summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python/netdev-times.py
diff options
context:
space:
mode:
authorThomas Reim <reimth@gmail.com>2011-07-29 14:29:00 +0000
committerDave Airlie <airlied@redhat.com>2011-08-04 14:40:43 +0100
commita81b31e9fc98e067b7e7f1244861c97e44268e2d (patch)
tree51381172d92fdc005e670964c57be4f6d4974c6b /tools/perf/scripts/python/netdev-times.py
parentd522d9cc5bdd41214084383fc3e6d882f6916a78 (diff)
drm/radeon: Extended DDC Probing for ECS A740GM-M DVI-D Connector
ECS A740GM-M with ATI RADEON 2100 sends data to i2c bus for a DVI connector that is not implemented/existent on the board. Fix by applying extented DDC probing for this connector. Requires [PATCH] drm/radeon: Extended DDC Probing for Connectors with Improperly Wired DDC Lines Tested for kernel 2.6.38 on Asus ECS A740GM-M board BugLink: http://bugs.launchpad.net/bugs/810926 Cc: <stable@kernel.org> Signed-off-by: Thomas Reim <reimth@gmail.com> Reviewed-by: Alex Deucher <alexdeucher@gmail.com> Acked-by: Stephen Michaels <Stephen.Micheals@gmail.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'tools/perf/scripts/python/netdev-times.py')
0 files changed, 0 insertions, 0 deletions
class='mode'>-rw-r--r--Documentation/Makefile160
-rw-r--r--Documentation/PCI/pci-error-recovery.rst15
-rw-r--r--Documentation/RCU/Design/Requirements/Requirements.rst33
-rw-r--r--Documentation/RCU/checklist.rst12
-rw-r--r--Documentation/RCU/whatisRCU.rst3
-rw-r--r--Documentation/accel/qaic/aic100.rst25
-rw-r--r--Documentation/accel/qaic/qaic.rst8
-rw-r--r--Documentation/accounting/taskstats.rst54
-rw-r--r--Documentation/admin-guide/LSM/Smack.rst16
-rw-r--r--Documentation/admin-guide/LSM/ipe.rst17
-rw-r--r--Documentation/admin-guide/RAS/main.rst142
-rw-r--r--Documentation/admin-guide/bcache.rst13
-rw-r--r--Documentation/admin-guide/blockdev/zoned_loop.rst61
-rw-r--r--Documentation/admin-guide/cgroup-v2.rst31
-rw-r--r--Documentation/admin-guide/efi-stub.rst3
-rw-r--r--Documentation/admin-guide/hw-vuln/l1d_flush.rst2
-rw-r--r--Documentation/admin-guide/hw-vuln/spectre.rst2
-rw-r--r--Documentation/admin-guide/kernel-parameters.rst97
-rw-r--r--Documentation/admin-guide/kernel-parameters.txt118
-rw-r--r--Documentation/admin-guide/md.rst10
-rw-r--r--Documentation/admin-guide/media/mali-c55-graph.dot19
-rw-r--r--Documentation/admin-guide/media/mali-c55.rst413
-rw-r--r--Documentation/admin-guide/media/platform-cardlist.rst2
-rw-r--r--Documentation/admin-guide/media/radio-cardlist.rst1
-rw-r--r--Documentation/admin-guide/media/rkcif-rk3568-vicap.dot8
-rw-r--r--Documentation/admin-guide/media/rkcif.rst79
-rw-r--r--Documentation/admin-guide/media/v4l-drivers.rst2
-rw-r--r--Documentation/admin-guide/pm/cpuidle.rst9
-rw-r--r--Documentation/admin-guide/pm/intel_pstate.rst133
-rw-r--r--Documentation/admin-guide/sysctl/net.rst29
-rw-r--r--Documentation/admin-guide/tainted-kernels.rst2
-rw-r--r--Documentation/admin-guide/thermal/index.rst1
-rw-r--r--Documentation/admin-guide/thermal/intel_thermal_throttle.rst91
-rw-r--r--Documentation/admin-guide/workload-tracing.rst10
-rw-r--r--Documentation/arch/arm64/booting.rst8
-rw-r--r--Documentation/arch/arm64/sve.rst5
-rw-r--r--Documentation/arch/s390/s390dbf.rst5
-rw-r--r--Documentation/arch/x86/boot.rst40
-rw-r--r--Documentation/bpf/libbpf/program_types.rst18
-rw-r--r--Documentation/bpf/map_array.rst5
-rw-r--r--Documentation/conf.py15
-rw-r--r--Documentation/core-api/assoc_array.rst196
-rw-r--r--Documentation/core-api/printk-formats.rst11
-rw-r--r--Documentation/crypto/index.rst1
-rw-r--r--Documentation/crypto/sha3.rst130
-rw-r--r--Documentation/crypto/userspace-if.rst7
-rw-r--r--Documentation/dev-tools/checkpatch.rst13
-rw-r--r--Documentation/dev-tools/kunit/run_manual.rst6
-rw-r--r--Documentation/devicetree/bindings/.yamllint2
-rw-r--r--Documentation/devicetree/bindings/Makefile3
-rw-r--r--Documentation/devicetree/bindings/arm/altera/socfpga-clk-manager.yaml20
-rw-r--r--Documentation/devicetree/bindings/arm/amd,seattle.yaml24
-rw-r--r--Documentation/devicetree/bindings/arm/apm.yaml28
-rw-r--r--Documentation/devicetree/bindings/arm/lge.yaml28
-rw-r--r--Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt146
-rw-r--r--Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt191
-rw-r--r--Documentation/devicetree/bindings/arm/psci.yaml1
-rw-r--r--Documentation/devicetree/bindings/ata/eswin,eic7700-ahci.yaml79
-rw-r--r--Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml4
-rw-r--r--Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml2
-rw-r--r--Documentation/devicetree/bindings/bus/cznic,moxtet.yaml94
-rw-r--r--Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml2
-rw-r--r--Documentation/devicetree/bindings/bus/moxtet.txt46
-rw-r--r--Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml2
-rw-r--r--Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml2
-rw-r--r--Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-gates-clk.yaml1
-rw-r--r--Documentation/devicetree/bindings/clock/marvell,ap80x-clock.yaml54
-rw-r--r--Documentation/devicetree/bindings/clock/marvell,cp110-clock.yaml70
-rw-r--r--Documentation/devicetree/bindings/clock/nvidia,tegra124-car.yaml8
-rw-r--r--Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml6
-rw-r--r--Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml2
-rw-r--r--Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml1
-rw-r--r--Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml1
-rw-r--r--Documentation/devicetree/bindings/crypto/amd,ccp-seattle-v1a.yaml3
-rw-r--r--Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml1
-rw-r--r--Documentation/devicetree/bindings/crypto/qcom,prng.yaml1
-rw-r--r--Documentation/devicetree/bindings/crypto/qcom-qce.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-frontend.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/allwinner,sun6i-a31-drc.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml76
-rw-r--r--Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml88
-rw-r--r--Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/bridge/fsl,imx8mp-hdmi-tx.yaml12
-rw-r--r--Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/bridge/parade,ps8622.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml54
-rw-r--r--Documentation/devicetree/bindings/display/bridge/sil,sii8620.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/ilitek,ili9486.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pai.yaml69
-rw-r--r--Documentation/devicetree/bindings/display/msm/dp-controller.yaml17
-rw-r--r--Documentation/devicetree/bindings/display/msm/gmu.yaml60
-rw-r--r--Documentation/devicetree/bindings/display/msm/gpu.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/msm/qcom,glymur-mdss.yaml264
-rw-r--r--Documentation/devicetree/bindings/display/msm/qcom,qcs8300-mdss.yaml286
-rw-r--r--Documentation/devicetree/bindings/display/msm/qcom,sm6150-mdss.yaml40
-rw-r--r--Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml16
-rw-r--r--Documentation/devicetree/bindings/display/msm/qcom,sm8750-mdss.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml68
-rw-r--r--Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml3
-rw-r--r--Documentation/devicetree/bindings/display/panel/lg,ld070wx3-sl01.yaml60
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-lvds.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml30
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-simple.yaml6
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-timing.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml14
-rw-r--r--Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/panel/samsung,s6e3fc2x01.yaml81
-rw-r--r--Documentation/devicetree/bindings/display/panel/samsung,sofef00.yaml79
-rw-r--r--Documentation/devicetree/bindings/display/panel/sharp,lq079l1sx01.yaml99
-rw-r--r--Documentation/devicetree/bindings/display/panel/synaptics,td4300-panel.yaml89
-rw-r--r--Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml3
-rw-r--r--Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml11
-rw-r--r--Documentation/devicetree/bindings/display/simple-framebuffer.yaml1
-rw-r--r--Documentation/devicetree/bindings/dma/apm,xgene-storm-dma.yaml59
-rw-r--r--Documentation/devicetree/bindings/dma/apm-xgene-dma.txt47
-rw-r--r--Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml1
-rw-r--r--Documentation/devicetree/bindings/dma/stericsson,dma40.yaml1
-rw-r--r--Documentation/devicetree/bindings/dma/stm32/st,stm32-dma.yaml1
-rw-r--r--Documentation/devicetree/bindings/dts-coding-style.rst5
-rw-r--r--Documentation/devicetree/bindings/edac/altr,socfpga-ecc-manager.yaml2
-rw-r--r--Documentation/devicetree/bindings/edac/apm,xgene-edac.yaml1
-rw-r--r--Documentation/devicetree/bindings/eeprom/at25.yaml1
-rw-r--r--Documentation/devicetree/bindings/firmware/qemu,fw-cfg-mmio.yaml1
-rw-r--r--Documentation/devicetree/bindings/fpga/fpga-region.yaml5
-rw-r--r--Documentation/devicetree/bindings/fpga/lattice,ice40-fpga-mgr.yaml59
-rw-r--r--Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt21
-rw-r--r--Documentation/devicetree/bindings/gnss/gnss-common.yaml3
-rw-r--r--Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml10
-rw-r--r--Documentation/devicetree/bindings/gpio/brcm,xgs-iproc-gpio.yaml1
-rw-r--r--Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml1
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-mxs.yaml13
-rw-r--r--Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml5
-rw-r--r--Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml4
-rw-r--r--Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml2
-rw-r--r--Documentation/devicetree/bindings/gpio/trivial-gpio.yaml2
-rw-r--r--Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml1
-rw-r--r--Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml42
-rw-r--r--Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml65
-rw-r--r--Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml1
-rw-r--r--Documentation/devicetree/bindings/hwmon/adi,max31827.yaml1
-rw-r--r--Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml30
-rw-r--r--Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt14
-rw-r--r--Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml7
-rw-r--r--Documentation/devicetree/bindings/hwmon/max31785.txt22
-rw-r--r--Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml22
-rw-r--r--Documentation/devicetree/bindings/hwmon/national,lm90.yaml1
-rw-r--r--Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml1
-rw-r--r--Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml52
-rw-r--r--Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml63
-rw-r--r--Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml1
-rw-r--r--Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml1
-rw-r--r--Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.yaml1
-rw-r--r--Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml2
-rw-r--r--Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml1
-rw-r--r--Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml2
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/cosmic,10001-adc.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/afe/voltage-divider.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/frequency/adi,admv4420.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/pressure/murata,zpa2326.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/proximity/semtech,sx9324.yaml1
-rw-r--r--Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml21
-rw-r--r--Documentation/devicetree/bindings/input/cypress,cyapa.yaml2
-rw-r--r--Documentation/devicetree/bindings/input/ti,drv266x.yaml1
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml2
-rw-r--r--Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml2
-rw-r--r--Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.yaml3
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2700-intc.yaml14
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.yaml2
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml2
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/loongson,liointc.yaml1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/mediatek,mtk-cirq.yaml1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.yaml1
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml5
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-mswi.yaml17
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml4
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/ti,omap4-wugen-mpu.yaml2
-rw-r--r--Documentation/devicetree/bindings/iommu/arm,smmu.yaml8
-rw-r--r--Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml8
-rw-r--r--Documentation/devicetree/bindings/iommu/qcom,iommu.yaml4
-rw-r--r--Documentation/devicetree/bindings/leds/backlight/arc,arc2c0608.yaml108
-rw-r--r--Documentation/devicetree/bindings/leds/backlight/arcxcnn_bl.txt33
-rw-r--r--Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml101
-rw-r--r--Documentation/devicetree/bindings/leds/common.yaml6
-rw-r--r--Documentation/devicetree/bindings/leds/leds-pwm.yaml7
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml5
-rw-r--r--Documentation/devicetree/bindings/leds/qcom,pm8058-led.yaml2
-rw-r--r--Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml1
-rw-r--r--Documentation/devicetree/bindings/mailbox/apm,xgene-slimpro-mbox.yaml62
-rw-r--r--Documentation/devicetree/bindings/mailbox/arm,mhu.yaml1
-rw-r--r--Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml1
-rw-r--r--Documentation/devicetree/bindings/mailbox/mtk,adsp-mbox.yaml1
-rw-r--r--Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml16
-rw-r--r--Documentation/devicetree/bindings/mailbox/xgene-slimpro-mailbox.txt35
-rw-r--r--Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/amphion,vpu.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/arm,mali-c55.yaml86
-rw-r--r--Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/dongwoon,dw9719.yaml89
-rw-r--r--Documentation/devicetree/bindings/media/i2c/nxp,tda19971.yaml162
-rw-r--r--Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt178
-rw-r--r--Documentation/devicetree/bindings/media/i2c/samsung,s5k5baf.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/samsung,s5k6a3.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml105
-rw-r--r--Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml6
-rw-r--r--Documentation/devicetree/bindings/media/i2c/techwell,tw9900.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt157
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ti,tvp5150.yaml133
-rw-r--r--Documentation/devicetree/bindings/media/mediatek,mt8173-mdp.yaml169
-rw-r--r--Documentation/devicetree/bindings/media/mediatek-mdp.txt96
-rw-r--r--Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml13
-rw-r--r--Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/qcom,msm8939-camss.yaml254
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sm8650-camss.yaml375
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sm8750-iris.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml103
-rw-r--r--Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml124
-rw-r--r--Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml172
-rw-r--r--Documentation/devicetree/bindings/media/rockchip,vdec.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/samsung,exynos4210-csis.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/samsung,exynos4210-fimc.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-is.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-lite.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/samsung,fimc.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/samsung,s5c73m3.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/samsung,s5pv210-jpeg.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml1
-rw-r--r--Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt88
-rw-r--r--Documentation/devicetree/bindings/media/video-interface-devices.yaml4
-rw-r--r--Documentation/devicetree/bindings/media/video-interfaces.yaml4
-rw-r--r--Documentation/devicetree/bindings/memory-controllers/qcom,ebi2-peripheral-props.yaml1
-rw-r--r--Documentation/devicetree/bindings/mfd/aspeed-lpc.yaml16
-rw-r--r--Documentation/devicetree/bindings/mfd/da9052-i2c.txt67
-rw-r--r--Documentation/devicetree/bindings/mfd/dlg,da9052.yaml89
-rw-r--r--Documentation/devicetree/bindings/mfd/dlg,da9063.yaml2
-rw-r--r--Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml74
-rw-r--r--Documentation/devicetree/bindings/mfd/maxim,max77705.yaml14
-rw-r--r--Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml161
-rw-r--r--Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml2
-rw-r--r--Documentation/devicetree/bindings/mfd/renesas,r2a11302ft.yaml58
-rw-r--r--Documentation/devicetree/bindings/mfd/silergy,sy7636a.yaml11
-rw-r--r--Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml1
-rw-r--r--Documentation/devicetree/bindings/mfd/syscon.yaml226
-rw-r--r--Documentation/devicetree/bindings/mfd/ti,tps65910.yaml3
-rw-r--r--Documentation/devicetree/bindings/mfd/ti,twl.yaml44
-rw-r--r--Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml2
-rw-r--r--Documentation/devicetree/bindings/mmc/davinci_mmc.txt32
-rw-r--r--Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml1
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-am654.yaml3
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-milbeaut.txt30
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci-msm.yaml2
-rw-r--r--Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml57
-rw-r--r--Documentation/devicetree/bindings/mmc/socionext,milbeaut-m10v-sdhci-3.0.yaml79
-rw-r--r--Documentation/devicetree/bindings/mmc/ti,da830-mmc.yaml61
-rw-r--r--Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml41
-rw-r--r--Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml1
-rw-r--r--Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml1
-rw-r--r--Documentation/devicetree/bindings/mtd/mtd-physmap.yaml10
-rw-r--r--Documentation/devicetree/bindings/mux/mux-controller.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml35
-rw-r--r--Documentation/devicetree/bindings/net/airoha,en7581-npu.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml2
-rw-r--r--Documentation/devicetree/bindings/net/amd,xgbe-seattle-v1a.yaml147
-rw-r--r--Documentation/devicetree/bindings/net/amd-xgbe.txt76
-rw-r--r--Documentation/devicetree/bindings/net/aspeed,ast2600-mdio.yaml7
-rw-r--r--Documentation/devicetree/bindings/net/bluetooth/marvell,sd8897-bt.yaml79
-rw-r--r--Documentation/devicetree/bindings/net/brcm,bcmgenet.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/brcm,mdio-mux-iproc.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/btusb.txt2
-rw-r--r--Documentation/devicetree/bindings/net/can/bosch,m_can.yaml25
-rw-r--r--Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml5
-rw-r--r--Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml5
-rw-r--r--Documentation/devicetree/bindings/net/cdns,macb.yaml23
-rw-r--r--Documentation/devicetree/bindings/net/cortina,gemini-ethernet.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/dsa/lantiq,gswip.yaml164
-rw-r--r--Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml167
-rw-r--r--Documentation/devicetree/bindings/net/dsa/nxp,sja1105.yaml3
-rw-r--r--Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml129
-rw-r--r--Documentation/devicetree/bindings/net/ethernet-phy.yaml10
-rw-r--r--Documentation/devicetree/bindings/net/ethernet-switch.yaml16
-rw-r--r--Documentation/devicetree/bindings/net/fsl,enetc.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/fsl,gianfar.yaml2
-rw-r--r--Documentation/devicetree/bindings/net/marvell-bt-8xxx.txt83
-rw-r--r--Documentation/devicetree/bindings/net/mdio-mux-multiplexer.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/mediatek,net.yaml26
-rw-r--r--Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml4
-rw-r--r--Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt73
-rw-r--r--Documentation/devicetree/bindings/net/mscc-phy-vsc8531.yaml131
-rw-r--r--Documentation/devicetree/bindings/net/nxp,netc-blk-ctrl.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/qcom,ethqos.yaml8
-rw-r--r--Documentation/devicetree/bindings/net/qcom,ipa.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/rockchip-dwmac.yaml3
-rw-r--r--Documentation/devicetree/bindings/net/snps,dwmac.yaml6
-rw-r--r--Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml19
-rw-r--r--Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml1
-rw-r--r--Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml66
-rw-r--r--Documentation/devicetree/bindings/net/wireless/ti,wlcore.yaml1
-rw-r--r--Documentation/devicetree/bindings/npu/arm,ethos.yaml79
-rw-r--r--Documentation/devicetree/bindings/nvmem/brcm,ocotp.txt17
-rw-r--r--Documentation/devicetree/bindings/nvmem/brcm,ocotp.yaml39
-rw-r--r--Documentation/devicetree/bindings/pci/altr,pcie-root-port.yaml1
-rw-r--r--Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml23
-rw-r--r--Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml83
-rw-r--r--Documentation/devicetree/bindings/pci/loongson.yaml1
-rw-r--r--Documentation/devicetree/bindings/pci/mediatek-pcie-mt7623.yaml164
-rw-r--r--Documentation/devicetree/bindings/pci/mediatek-pcie.txt289
-rw-r--r--Documentation/devicetree/bindings/pci/mediatek-pcie.yaml438
-rw-r--r--Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml130
-rw-r--r--Documentation/devicetree/bindings/pci/pci-ep.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml5
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sc8280xp.yaml5
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml8
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml249
-rw-r--r--Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml4
-rw-r--r--Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml6
-rw-r--r--Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml157
-rw-r--r--Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml1
-rw-r--r--Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml179
-rw-r--r--Documentation/devicetree/bindings/pci/versatile.yaml1
-rw-r--r--Documentation/devicetree/bindings/perf/fsl-imx-ddr.yaml29
-rw-r--r--Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml4
-rw-r--r--Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml4
-rw-r--r--Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml4
-rw-r--r--Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml1
-rw-r--r--Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml4
-rw-r--r--Documentation/devicetree/bindings/pinctrl/brcm,bcm21664-pinctrl.yaml1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/fsl,imx9-pinctrl.yaml1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/marvell,ap806-pinctrl.yaml61
-rw-r--r--Documentation/devicetree/bindings/pinctrl/marvell,armada-7k-pinctrl.yaml72
-rw-r--r--Documentation/devicetree/bindings/pinctrl/microchip,sparx5-sgpio.yaml12
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,ipq5018-tlmm.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,ipq5332-tlmm.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,ipq8074-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,ipq9574-tlmm.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8660-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8916-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8976-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8994-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml10
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml6
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,qcs404-pinctrl.yaml3
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sc7180-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sdm630-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sm6115-lpass-lpi-pinctrl.yaml1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sm6125-tlmm.yaml1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sm8150-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sm8350-lpass-lpi-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sm8550-lpass-lpi-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/renesas,rza1-ports.yaml5
-rw-r--r--Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml2
-rw-r--r--Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/toshiba,visconti-pinctrl.yaml26
-rw-r--r--Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml1
-rw-r--r--Documentation/devicetree/bindings/power/actions,owl-sps.txt21
-rw-r--r--Documentation/devicetree/bindings/power/actions,s500-sps.yaml39
-rw-r--r--Documentation/devicetree/bindings/power/mediatek,mt8196-gpufreq.yaml117
-rw-r--r--Documentation/devicetree/bindings/power/mediatek,power-controller.yaml4
-rw-r--r--Documentation/devicetree/bindings/power/qcom,rpmpd.yaml1
-rw-r--r--Documentation/devicetree/bindings/power/renesas,sysc-rmobile.yaml4
-rw-r--r--Documentation/devicetree/bindings/power/rockchip,power-controller.yaml2
-rw-r--r--Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml1
-rw-r--r--Documentation/devicetree/bindings/power/supply/richtek,rt9756.yaml72
-rw-r--r--Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml1
-rw-r--r--Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml1
-rw-r--r--Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml48
-rw-r--r--Documentation/devicetree/bindings/regulator/da9211.txt205
-rw-r--r--Documentation/devicetree/bindings/regulator/dlg,da9211.yaml103
-rw-r--r--Documentation/devicetree/bindings/regulator/fitipower,fp9931.yaml110
-rw-r--r--Documentation/devicetree/bindings/regulator/mediatek,mt6316b-regulator.yaml76
-rw-r--r--Documentation/devicetree/bindings/regulator/mediatek,mt6316c-regulator.yaml76
-rw-r--r--Documentation/devicetree/bindings/regulator/mediatek,mt6316d-regulator.yaml75
-rw-r--r--Documentation/devicetree/bindings/regulator/mediatek,mt6363-regulator.yaml146
-rw-r--r--Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml39
-rw-r--r--Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml55
-rw-r--r--Documentation/devicetree/bindings/regulator/richtek,rt6245-regulator.yaml1
-rw-r--r--Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml2
-rw-r--r--Documentation/devicetree/bindings/reset/ti,sci-reset.yaml1
-rw-r--r--Documentation/devicetree/bindings/rng/inside-secure,safexcel-eip76.yaml2
-rw-r--r--Documentation/devicetree/bindings/rng/microchip,pic32-rng.txt17
-rw-r--r--Documentation/devicetree/bindings/rng/microchip,pic32-rng.yaml40
-rw-r--r--Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml2
-rw-r--r--Documentation/devicetree/bindings/serial/qcom,msm-uartdm.yaml2
-rw-r--r--Documentation/devicetree/bindings/serial/renesas,scif.yaml1
-rw-r--r--Documentation/devicetree/bindings/slimbus/qcom,slim-ngd.yaml2
-rw-r--r--Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml38
-rw-r--r--Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-muram.yaml1
-rw-r--r--Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml1
-rw-r--r--Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml9
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml2
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml2
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml2
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml2
-rw-r--r--Documentation/devicetree/bindings/soc/rockchip/grf.yaml1
-rw-r--r--Documentation/devicetree/bindings/soc/tegra/nvidia,tegra20-pmc.yaml12
-rw-r--r--Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml3
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau1372.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau7002.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau7118.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/adi,max98363.yaml60
-rw-r--r--Documentation/devicetree/bindings/sound/adi,ssm2602.txt19
-rw-r--r--Documentation/devicetree/bindings/sound/adi,ssm3515.yaml49
-rw-r--r--Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml44
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs4271.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml8
-rw-r--r--Documentation/devicetree/bindings/sound/cs4265.txt29
-rw-r--r--Documentation/devicetree/bindings/sound/cs4341.txt22
-rw-r--r--Documentation/devicetree/bindings/sound/cs4349.txt19
-rw-r--r--Documentation/devicetree/bindings/sound/da9055.txt22
-rw-r--r--Documentation/devicetree/bindings/sound/maxim,max98090.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/maxim,max98095.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/maxim,max98504.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8189-afe-pcm.yaml178
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8189-nau8825.yaml101
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml45
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml106
-rw-r--r--Documentation/devicetree/bindings/sound/nxp,tfa9879.yaml44
-rw-r--r--Documentation/devicetree/bindings/sound/nxp,uda1342.yaml42
-rw-r--r--Documentation/devicetree/bindings/sound/pcm1789.txt22
-rw-r--r--Documentation/devicetree/bindings/sound/pcm179x.txt27
-rw-r--r--Documentation/devicetree/bindings/sound/pcm186x.txt42
-rw-r--r--Documentation/devicetree/bindings/sound/pcm5102a.txt13
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml19
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml50
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6adm-routing.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6adm.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6afe.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6apm.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6asm-dais.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6asm.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6core.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6prm.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,sm8250.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,tm2.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml87
-rw-r--r--Documentation/devicetree/bindings/sound/ti,pcm1862.yaml76
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tas2781.yaml58
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/trivial-codec.yaml79
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8510.yaml41
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8523.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8580.yaml42
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8711.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8728.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8737.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8750.yaml42
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8753.yaml62
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8776.yaml41
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8903.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8961.yaml43
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8974.yaml41
-rw-r--r--Documentation/devicetree/bindings/sound/wlf,wm8994.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/wm8770.txt16
-rw-r--r--Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml7
-rw-r--r--Documentation/devicetree/bindings/spi/aspeed,ast2600-fmc.yaml4
-rw-r--r--Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml21
-rw-r--r--Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml70
-rw-r--r--Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt36
-rw-r--r--Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.yaml72
-rw-r--r--Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml2
-rw-r--r--Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml2
-rw-r--r--Documentation/devicetree/bindings/spi/qcom,spi-qup.yaml2
-rw-r--r--Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml65
-rw-r--r--Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml2
-rw-r--r--Documentation/devicetree/bindings/spi/spi-cadence.yaml12
-rw-r--r--Documentation/devicetree/bindings/spi/spi-controller.yaml2
-rw-r--r--Documentation/devicetree/bindings/spi/spi-rockchip.yaml1
-rw-r--r--Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt33
-rw-r--r--Documentation/devicetree/bindings/thermal/amazon,al-thermal.yaml50
-rw-r--r--Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt105
-rw-r--r--Documentation/devicetree/bindings/thermal/brcm,sr-thermal.yaml121
-rw-r--r--Documentation/devicetree/bindings/thermal/db8500-thermal.txt44
-rw-r--r--Documentation/devicetree/bindings/thermal/fsl,imx91-tmu.yaml87
-rw-r--r--Documentation/devicetree/bindings/thermal/qcom-tsens.yaml9
-rw-r--r--Documentation/devicetree/bindings/thermal/renesas,r9a09g047-tsu.yaml6
-rw-r--r--Documentation/devicetree/bindings/timer/nvidia,tegra-timer.yaml1
-rw-r--r--Documentation/devicetree/bindings/timer/nvidia,tegra186-timer.yaml1
-rw-r--r--Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml47
-rw-r--r--Documentation/devicetree/bindings/trivial-devices.yaml14
-rw-r--r--Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml2
-rw-r--r--Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml10
-rw-r--r--Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/nxp,ptn36502.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/parade,ps8830.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml3
-rw-r--r--Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/ti,tusb1046.yaml1
-rw-r--r--Documentation/devicetree/bindings/usb/usb-switch-ports.yaml68
-rw-r--r--Documentation/devicetree/bindings/usb/usb-switch.yaml52
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.yaml12
-rw-r--r--Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml2
-rw-r--r--Documentation/doc-guide/checktransupdate.rst6
-rw-r--r--Documentation/doc-guide/contributing.rst2
-rw-r--r--Documentation/doc-guide/kernel-doc.rst33
-rw-r--r--Documentation/doc-guide/parse-headers.rst189
-rw-r--r--Documentation/doc-guide/sphinx.rst6
-rw-r--r--Documentation/driver-api/cxl/allocation/page-allocator.rst31
-rw-r--r--Documentation/driver-api/dpll.rst36
-rw-r--r--Documentation/driver-api/firmware/efi/index.rst11
-rw-r--r--Documentation/driver-api/generic_pt.rst137
-rw-r--r--Documentation/driver-api/gpio/index.rst1
-rw-r--r--Documentation/driver-api/gpio/pca953x.rst552
-rw-r--r--Documentation/driver-api/index.rst1
-rw-r--r--Documentation/driver-api/media/v4l2-core.rst1
-rw-r--r--Documentation/driver-api/media/v4l2-isp.rst49
-rw-r--r--Documentation/driver-api/parport-lowlevel.rst5
-rw-r--r--Documentation/driver-api/pci/p2pdma.rst97
-rw-r--r--Documentation/driver-api/pci/pci.rst3
-rw-r--r--Documentation/driver-api/pldmfw/index.rst1
-rw-r--r--Documentation/driver-api/thermal/intel_dptf.rst23
-rw-r--r--Documentation/driver-api/usb/writing_musb_glue_layer.rst2
-rw-r--r--Documentation/filesystems/ext4/inodes.rst2
-rw-r--r--Documentation/filesystems/ext4/super.rst4
-rw-r--r--Documentation/filesystems/fscrypt.rst2
-rw-r--r--Documentation/filesystems/gfs2/glocks.rst (renamed from Documentation/filesystems/gfs2-glocks.rst)0
-rw-r--r--Documentation/filesystems/gfs2/index.rst (renamed from Documentation/filesystems/gfs2.rst)12
-rw-r--r--Documentation/filesystems/gfs2/uevents.rst (renamed from Documentation/filesystems/gfs2-uevents.rst)0
-rw-r--r--Documentation/filesystems/index.rst4
-rw-r--r--Documentation/filesystems/iomap/operations.rst50
-rw-r--r--Documentation/filesystems/porting.rst15
-rw-r--r--Documentation/filesystems/ramfs-rootfs-initramfs.rst12
-rw-r--r--Documentation/filesystems/resctrl.rst134
-rw-r--r--Documentation/filesystems/xfs/xfs-online-fsck-design.rst238
-rw-r--r--Documentation/firmware-guide/acpi/i2c-muxes.rst8
-rw-r--r--Documentation/gpu/drm-kms-helpers.rst12
-rw-r--r--Documentation/gpu/drm-kms.rst15
-rw-r--r--Documentation/gpu/nova/core/todo.rst30
-rw-r--r--Documentation/gpu/rfc/color_pipeline.rst378
-rw-r--r--Documentation/gpu/rfc/index.rst3
-rw-r--r--Documentation/gpu/todo.rst37
-rw-r--r--Documentation/gpu/vkms.rst119
-rw-r--r--Documentation/gpu/xe/index.rst1
-rw-r--r--Documentation/gpu/xe/xe_exec_queue.rst20
-rw-r--r--Documentation/gpu/xe/xe_gt_freq.rst3
-rw-r--r--Documentation/hid/hid-alps.rst8
-rw-r--r--Documentation/hwmon/aht10.rst10
-rw-r--r--Documentation/hwmon/asus_ec_sensors.rst4
-rw-r--r--Documentation/hwmon/ds1621.rst10
-rw-r--r--Documentation/hwmon/index.rst5
-rw-r--r--Documentation/hwmon/isl68137.rst10
-rw-r--r--Documentation/hwmon/jc42.rst2
-rw-r--r--Documentation/hwmon/lm75.rst13
-rw-r--r--Documentation/hwmon/lm90.rst127
-rw-r--r--Documentation/hwmon/macsmc-hwmon.rst71
-rw-r--r--Documentation/hwmon/max127.rst2
-rw-r--r--Documentation/hwmon/max15301.rst2
-rw-r--r--Documentation/hwmon/max16064.rst2
-rw-r--r--Documentation/hwmon/max16065.rst8
-rw-r--r--Documentation/hwmon/max1619.rst4
-rw-r--r--Documentation/hwmon/max16601.rst2
-rw-r--r--Documentation/hwmon/max1668.rst2
-rw-r--r--Documentation/hwmon/max17616.rst62
-rw-r--r--Documentation/hwmon/max197.rst4
-rw-r--r--Documentation/hwmon/max20730.rst8
-rw-r--r--Documentation/hwmon/max31722.rst4
-rw-r--r--Documentation/hwmon/max31730.rst2
-rw-r--r--Documentation/hwmon/max31785.rst2
-rw-r--r--Documentation/hwmon/max31790.rst2
-rw-r--r--Documentation/hwmon/max31827.rst6
-rw-r--r--Documentation/hwmon/max34440.rst37
-rw-r--r--Documentation/hwmon/max6639.rst2
-rw-r--r--Documentation/hwmon/max6650.rst4
-rw-r--r--Documentation/hwmon/max6697.rst20
-rw-r--r--Documentation/hwmon/max77705.rst4
-rw-r--r--Documentation/hwmon/max8688.rst2
-rw-r--r--Documentation/hwmon/mp2925.rst151
-rw-r--r--Documentation/hwmon/mp9945.rst117
-rw-r--r--Documentation/hwmon/pmbus.rst2
-rw-r--r--Documentation/hwmon/sy7636a-hwmon.rst4
-rw-r--r--Documentation/hwmon/tsc1641.rst87
-rw-r--r--Documentation/hwmon/zl6100.rst16
-rw-r--r--Documentation/input/event-codes.rst25
-rw-r--r--Documentation/kbuild/kbuild.rst10
-rw-r--r--Documentation/leds/leds-lp5521.rst2
-rw-r--r--Documentation/leds/leds-lp5523.rst2
-rw-r--r--Documentation/locking/seqlock.rst9
-rw-r--r--Documentation/memory-barriers.txt6
-rw-r--r--Documentation/misc-devices/amd-sbi.rst6
-rw-r--r--Documentation/misc-devices/mrvl_cn10k_dpi.rst4
-rw-r--r--Documentation/misc-devices/tps6594-pfsm.rst12
-rw-r--r--Documentation/misc-devices/uacce.rst7
-rw-r--r--Documentation/mm/active_mm.rst2
-rw-r--r--Documentation/netlink/genetlink-c.yaml2
-rw-r--r--Documentation/netlink/genetlink.yaml2
-rw-r--r--Documentation/netlink/netlink-raw.yaml2
-rw-r--r--Documentation/netlink/specs/conntrack.yaml2
-rw-r--r--Documentation/netlink/specs/devlink.yaml11
-rw-r--r--Documentation/netlink/specs/dpll.yaml9
-rw-r--r--Documentation/netlink/specs/em.yaml113
-rw-r--r--Documentation/netlink/specs/ethtool.yaml88
-rw-r--r--Documentation/netlink/specs/netdev.yaml28
-rw-r--r--Documentation/netlink/specs/nftables.yaml2
-rw-r--r--Documentation/netlink/specs/psp.yaml95
-rw-r--r--Documentation/netlink/specs/rt-addr.yaml7
-rw-r--r--Documentation/netlink/specs/rt-link.yaml50
-rw-r--r--Documentation/netlink/specs/rt-neigh.yaml2
-rw-r--r--Documentation/netlink/specs/rt-route.yaml8
-rw-r--r--Documentation/netlink/specs/rt-rule.yaml6
-rw-r--r--Documentation/netlink/specs/wireguard.yaml298
-rw-r--r--Documentation/networking/6pack.rst2
-rw-r--r--Documentation/networking/arcnet-hardware.rst22
-rw-r--r--Documentation/networking/arcnet.rst48
-rw-r--r--Documentation/networking/ax25.rst7
-rw-r--r--Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst22
-rw-r--r--Documentation/networking/device_drivers/ethernet/index.rst1
-rw-r--r--Documentation/networking/device_drivers/ethernet/mucse/rnpgbe.rst17
-rw-r--r--Documentation/networking/devlink/devlink-eswitch-attr.rst13
-rw-r--r--Documentation/networking/devlink/devlink-params.rst14
-rw-r--r--Documentation/networking/devlink/i40e.rst34
-rw-r--r--Documentation/networking/devlink/index.rst1
-rw-r--r--Documentation/networking/devlink/mlx5.rst14
-rw-r--r--Documentation/networking/devlink/stmmac.rst40
-rw-r--r--Documentation/networking/dsa/dsa.rst17
-rw-r--r--Documentation/networking/ethtool-netlink.rst64
-rw-r--r--Documentation/networking/index.rst5
-rw-r--r--Documentation/networking/ip-sysctl.rst60
-rw-r--r--Documentation/networking/napi.rst50
-rw-r--r--Documentation/networking/net_cachelines/inet_connection_sock.rst2
-rw-r--r--Documentation/networking/net_cachelines/inet_sock.rst79
-rw-r--r--Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst3
-rw-r--r--Documentation/networking/net_failover.rst6
-rw-r--r--Documentation/networking/netconsole.rst5
-rw-r--r--Documentation/networking/nfc.rst6
-rw-r--r--Documentation/networking/smc-sysctl.rst40
-rw-r--r--Documentation/networking/statistics.rst4
-rw-r--r--Documentation/networking/tls.rst20
-rw-r--r--Documentation/networking/xfrm/index.rst13
-rw-r--r--Documentation/networking/xfrm/xfrm_device.rst (renamed from Documentation/networking/xfrm_device.rst)20
-rw-r--r--Documentation/networking/xfrm/xfrm_proc.rst (renamed from Documentation/networking/xfrm_proc.rst)0
-rw-r--r--Documentation/networking/xfrm/xfrm_sync.rst (renamed from Documentation/networking/xfrm_sync.rst)97
-rw-r--r--Documentation/networking/xfrm/xfrm_sysctl.rst (renamed from Documentation/networking/xfrm_sysctl.rst)4
-rw-r--r--Documentation/power/index.rst1
-rw-r--r--Documentation/power/pm_qos_interface.rst9
-rw-r--r--Documentation/power/power_supply_class.rst84
-rw-r--r--Documentation/power/runtime_pm.rst10
-rw-r--r--Documentation/power/shutdown-debugging.rst53
-rw-r--r--Documentation/process/2.Process.rst47
-rw-r--r--Documentation/process/coding-style.rst2
-rw-r--r--Documentation/process/submitting-patches.rst5
-rw-r--r--Documentation/rust/quick-start.rst4
-rw-r--r--Documentation/security/keys/trusted-encrypted.rst88
-rw-r--r--Documentation/sound/codecs/cs35l56.rst9
-rw-r--r--Documentation/sphinx/kernel_abi.py6
-rw-r--r--Documentation/sphinx/kernel_feat.py26
-rwxr-xr-xDocumentation/sphinx/kernel_include.py112
-rw-r--r--Documentation/sphinx/kerneldoc-preamble.sty2
-rw-r--r--Documentation/sphinx/kerneldoc.py6
-rw-r--r--Documentation/sphinx/load_config.py60
-rw-r--r--Documentation/sphinx/parallel-wrapper.sh33
-rw-r--r--Documentation/tools/rtla/common_appendix.txt (renamed from Documentation/tools/rtla/common_appendix.rst)0
-rw-r--r--Documentation/tools/rtla/common_hist_options.txt (renamed from Documentation/tools/rtla/common_hist_options.rst)0
-rw-r--r--Documentation/tools/rtla/common_options.txt (renamed from Documentation/tools/rtla/common_options.rst)16
-rw-r--r--Documentation/tools/rtla/common_osnoise_description.txt (renamed from Documentation/tools/rtla/common_osnoise_description.rst)0
-rw-r--r--Documentation/tools/rtla/common_osnoise_options.txt (renamed from Documentation/tools/rtla/common_osnoise_options.rst)0
-rw-r--r--Documentation/tools/rtla/common_timerlat_aa.txt (renamed from Documentation/tools/rtla/common_timerlat_aa.rst)0
-rw-r--r--Documentation/tools/rtla/common_timerlat_description.txt (renamed from Documentation/tools/rtla/common_timerlat_description.rst)0
-rw-r--r--Documentation/tools/rtla/common_timerlat_options.txt (renamed from Documentation/tools/rtla/common_timerlat_options.rst)4
-rw-r--r--Documentation/tools/rtla/common_top_options.txt (renamed from Documentation/tools/rtla/common_top_options.rst)0
-rw-r--r--Documentation/tools/rtla/rtla-hwnoise.rst8
-rw-r--r--Documentation/tools/rtla/rtla-osnoise-hist.rst10
-rw-r--r--Documentation/tools/rtla/rtla-osnoise-top.rst10
-rw-r--r--Documentation/tools/rtla/rtla-osnoise.rst4
-rw-r--r--Documentation/tools/rtla/rtla-timerlat-hist.rst12
-rw-r--r--Documentation/tools/rtla/rtla-timerlat-top.rst14
-rw-r--r--Documentation/tools/rtla/rtla-timerlat.rst4
-rw-r--r--Documentation/tools/rtla/rtla.rst2
-rw-r--r--Documentation/trace/timerlat-tracer.rst12
-rw-r--r--Documentation/translations/it_IT/doc-guide/parse-headers.rst8
-rw-r--r--Documentation/translations/it_IT/doc-guide/sphinx.rst4
-rw-r--r--Documentation/translations/ja_JP/SubmittingPatches28
-rw-r--r--Documentation/translations/zh_CN/admin-guide/README.rst2
-rw-r--r--Documentation/translations/zh_CN/block/blk-mq.rst130
-rw-r--r--Documentation/translations/zh_CN/block/data-integrity.rst192
-rw-r--r--Documentation/translations/zh_CN/block/index.rst35
-rw-r--r--Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst2
-rw-r--r--Documentation/translations/zh_CN/doc-guide/checktransupdate.rst6
-rw-r--r--Documentation/translations/zh_CN/doc-guide/contributing.rst2
-rw-r--r--Documentation/translations/zh_CN/doc-guide/parse-headers.rst8
-rw-r--r--Documentation/translations/zh_CN/doc-guide/sphinx.rst4
-rw-r--r--Documentation/translations/zh_CN/filesystems/dnotify.rst67
-rw-r--r--Documentation/translations/zh_CN/filesystems/gfs2-glocks.rst211
-rw-r--r--Documentation/translations/zh_CN/filesystems/gfs2-uevents.rst97
-rw-r--r--Documentation/translations/zh_CN/filesystems/gfs2.rst57
-rw-r--r--Documentation/translations/zh_CN/filesystems/index.rst17
-rw-r--r--Documentation/translations/zh_CN/filesystems/inotify.rst80
-rw-r--r--Documentation/translations/zh_CN/filesystems/ubifs-authentication.rst354
-rw-r--r--Documentation/translations/zh_CN/filesystems/ubifs.rst114
-rw-r--r--Documentation/translations/zh_CN/how-to.rst4
-rw-r--r--Documentation/translations/zh_CN/kbuild/kbuild.rst27
-rw-r--r--Documentation/translations/zh_CN/mm/active_mm.rst2
-rw-r--r--Documentation/translations/zh_CN/networking/generic-hdlc.rst176
-rw-r--r--Documentation/translations/zh_CN/networking/index.rst7
-rw-r--r--Documentation/translations/zh_CN/networking/mptcp-sysctl.rst139
-rw-r--r--Documentation/translations/zh_CN/networking/timestamping.rst674
-rw-r--r--Documentation/translations/zh_CN/rust/general-information.rst1
-rw-r--r--Documentation/translations/zh_CN/rust/index.rst33
-rw-r--r--Documentation/translations/zh_CN/rust/testing.rst215
-rw-r--r--Documentation/translations/zh_CN/scsi/index.rst92
-rw-r--r--Documentation/translations/zh_CN/scsi/libsas.rst425
-rw-r--r--Documentation/translations/zh_CN/scsi/link_power_management_policy.rst32
-rw-r--r--Documentation/translations/zh_CN/scsi/scsi-parameters.rst118
-rw-r--r--Documentation/translations/zh_CN/scsi/scsi.rst48
-rw-r--r--Documentation/translations/zh_CN/scsi/scsi_eh.rst482
-rw-r--r--Documentation/translations/zh_CN/scsi/scsi_mid_low_api.rst1174
-rw-r--r--Documentation/translations/zh_CN/scsi/sd-parameters.rst38
-rw-r--r--Documentation/translations/zh_CN/scsi/wd719x.rst35
-rw-r--r--Documentation/translations/zh_CN/security/SCTP.rst317
-rw-r--r--Documentation/translations/zh_CN/security/index.rst4
-rw-r--r--Documentation/translations/zh_CN/security/ipe.rst398
-rw-r--r--Documentation/translations/zh_CN/security/lsm-development.rst19
-rw-r--r--Documentation/translations/zh_CN/security/secrets/coco.rst96
-rw-r--r--Documentation/translations/zh_CN/security/secrets/index.rst9
-rw-r--r--Documentation/translations/zh_CN/subsystem-apis.rst3
-rw-r--r--Documentation/translations/zh_TW/admin-guide/README.rst2
-rw-r--r--Documentation/translations/zh_TW/dev-tools/gdb-kernel-debugging.rst2
-rw-r--r--Documentation/userspace-api/dma-buf-heaps.rst59
-rw-r--r--Documentation/userspace-api/media/cec/cec-header.rst8
-rw-r--r--Documentation/userspace-api/media/cec/cec.h.rst.exceptions3
-rw-r--r--Documentation/userspace-api/media/drivers/index.rst1
-rw-r--r--Documentation/userspace-api/media/drivers/mali-c55.rst55
-rw-r--r--Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions86
-rw-r--r--Documentation/userspace-api/media/dvb/dmx_types.rst1
-rw-r--r--Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions5
-rw-r--r--Documentation/userspace-api/media/dvb/headers.rst31
-rw-r--r--Documentation/userspace-api/media/mediactl/media-header.rst8
-rw-r--r--Documentation/userspace-api/media/mediactl/media.h.rst.exceptions3
-rw-r--r--Documentation/userspace-api/media/rc/lirc-header.rst14
-rw-r--r--Documentation/userspace-api/media/v4l/app-pri.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/audio.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/biblio.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/buffer.rst2
-rw-r--r--Documentation/userspace-api/media/v4l/capture-example.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/capture.c.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/colorspaces-defs.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/colorspaces-details.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/colorspaces.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/common-defs.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/common.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/compat.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/control.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/crop.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/depth-formats.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-decoder.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-encoder.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-event.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-mem2mem.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-meta.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-osd.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-overlay.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-radio.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-sdr.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-subdev.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dev-touch.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/devices.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/dv-timings.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-camera.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-detect.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-dv.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-jpeg.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-rf-tuner.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/extended-controls.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/field-order.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/fourcc.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/hsv-formats.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/libv4l.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/meta-formats.rst3
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-arm-mali-c55.rst84
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-d4xx.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-generic.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-uvc.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-vivid.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-vsp1-hgo.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/metafmt-vsp1-hgt.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-bayer.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-cnf4.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-compressed.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-indexed.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-intro.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-inzi.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-m420.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-packed-hsv.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-reserved.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-rgb.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-cs08.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-cs14le.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-cu08.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-cu16le.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu16be.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu18be.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu20be.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-sdr-ru12le.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb10.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb10alaw8.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb10dpcm8.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb10p.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb12.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb14.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb16.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-srggb8.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-tch-td08.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-tch-td16.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-tch-tu08.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-tch-tu16.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-uv8.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-v4l2-mplane.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-y12i.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-y16i.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-y8i.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt-z16.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/pixfmt.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/planar-apis.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/querycap.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/sdr-formats.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/selection-api-configuration.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/selection-api-examples.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/selection-api-intro.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/selection-api-targets.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/selection-api-vs-crop-api.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/selection-api.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/selections-common.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/standard.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/subdev-formats.rst421
-rw-r--r--Documentation/userspace-api/media/v4l/tch-formats.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/tuner.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/user-func.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/v4l2-isp.rst67
-rw-r--r--Documentation/userspace-api/media/v4l/v4l2-selection-flags.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/v4l2-selection-targets.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/v4l2.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/v4l2grab-example.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/v4l2grab.c.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/video.rst1
-rw-r--r--Documentation/userspace-api/media/v4l/videodev.rst9
-rw-r--r--Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions288
-rw-r--r--Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst8
-rw-r--r--Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst2
-rw-r--r--Documentation/userspace-api/media/v4l/yuv-formats.rst1
-rw-r--r--Documentation/userspace-api/netlink/intro-specs.rst4
-rw-r--r--Documentation/userspace-api/spec_ctrl.rst6
-rw-r--r--Documentation/w1/w1-netlink.rst2
-rw-r--r--Documentation/wmi/driver-development-guide.rst1
-rw-r--r--MAINTAINERS476
-rw-r--r--Makefile34
-rw-r--r--README160
-rw-r--r--arch/Kconfig7
-rw-r--r--arch/alpha/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-fuji-data64.dts14
-rw-r--r--arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi8
-rw-r--r--arch/arm/boot/dts/broadcom/bcm2835-rpi-common.dtsi9
-rw-r--r--arch/arm/boot/dts/broadcom/bcm47189-luxul-xap-1440.dts4
-rw-r--r--arch/arm/boot/dts/nxp/imx/imx51-zii-rdu1.dts4
-rw-r--r--arch/arm/boot/dts/nxp/imx/imx6ul.dtsi2
-rw-r--r--arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts2
-rw-r--r--arch/arm/boot/dts/st/stih410.dtsi326
-rw-r--r--arch/arm/configs/axm55xx_defconfig1
-rw-r--r--arch/arm/configs/clps711x_defconfig1
-rw-r--r--arch/arm/configs/dove_defconfig1
-rw-r--r--arch/arm/configs/ep93xx_defconfig1
-rw-r--r--arch/arm/configs/jornada720_defconfig1
-rw-r--r--arch/arm/configs/keystone_defconfig1
-rw-r--r--arch/arm/configs/lpc32xx_defconfig1
-rw-r--r--arch/arm/configs/mmp2_defconfig1
-rw-r--r--arch/arm/configs/mv78xx0_defconfig1
-rw-r--r--arch/arm/configs/omap1_defconfig1
-rw-r--r--arch/arm/configs/orion5x_defconfig1
-rw-r--r--arch/arm/configs/pxa168_defconfig1
-rw-r--r--arch/arm/configs/pxa3xx_defconfig1
-rw-r--r--arch/arm/configs/pxa910_defconfig1
-rw-r--r--arch/arm/configs/spitz_defconfig1
-rw-r--r--arch/arm/crypto/Kconfig16
-rw-r--r--arch/arm/crypto/Makefile2
-rw-r--r--arch/arm/crypto/blake2b-neon-glue.c104
-rw-r--r--arch/arm/include/asm/simd.h7
-rw-r--r--arch/arm/include/asm/uaccess.h26
-rw-r--r--arch/arm/tools/syscall.tbl1
-rw-r--r--arch/arm64/Kconfig26
-rw-r--r--arch/arm64/Kconfig.platforms1
-rw-r--r--arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts10
-rw-r--r--arch/arm64/boot/dts/broadcom/bcm2712.dtsi2
-rw-r--r--arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi2
-rw-r--r--arch/arm64/boot/dts/freescale/imx8dxl-ss-conn.dtsi4
-rw-r--r--arch/arm64/boot/dts/freescale/imx8dxl-ss-hsio.dtsi5
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts24
-rw-r--r--arch/arm64/boot/dts/freescale/imx8qm-mek.dts4
-rw-r--r--arch/arm64/boot/dts/freescale/imx95.dtsi40
-rw-r--r--arch/arm64/boot/dts/nvidia/tegra194-p3668.dtsi1
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3328.dtsi1
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-op1.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso10
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi6
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3568-odroid-m1.dts2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3576.dtsi14
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588-opp.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi4
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588j.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts4
-rw-r--r--arch/arm64/configs/defconfig5
-rw-r--r--arch/arm64/crypto/Kconfig21
-rw-r--r--arch/arm64/crypto/Makefile6
-rw-r--r--arch/arm64/crypto/aes-ce-ccm-glue.c116
-rw-r--r--arch/arm64/crypto/aes-ce-glue.c87
-rw-r--r--arch/arm64/crypto/aes-glue.c139
-rw-r--r--arch/arm64/crypto/aes-neonbs-glue.c150
-rw-r--r--arch/arm64/crypto/ghash-ce-glue.c27
-rw-r--r--arch/arm64/crypto/nhpoly1305-neon-glue.c5
-rw-r--r--arch/arm64/crypto/polyval-ce-glue.c158
-rw-r--r--arch/arm64/crypto/sha3-ce-glue.c151
-rw-r--r--arch/arm64/crypto/sm3-ce-glue.c15
-rw-r--r--arch/arm64/crypto/sm3-neon-glue.c16
-rw-r--r--arch/arm64/crypto/sm4-ce-ccm-glue.c49
-rw-r--r--arch/arm64/crypto/sm4-ce-cipher-glue.c10
-rw-r--r--arch/arm64/crypto/sm4-ce-gcm-glue.c62
-rw-r--r--arch/arm64/crypto/sm4-ce-glue.c214
-rw-r--r--arch/arm64/crypto/sm4-neon-glue.c25
-rw-r--r--arch/arm64/include/asm/alternative-macros.h8
-rw-r--r--arch/arm64/include/asm/alternative.h11
-rw-r--r--arch/arm64/include/asm/arch_gicv3.h4
-rw-r--r--arch/arm64/include/asm/asm-extable.h6
-rw-r--r--arch/arm64/include/asm/assembler.h12
-rw-r--r--arch/arm64/include/asm/atomic_lse.h20
-rw-r--r--arch/arm64/include/asm/barrier.h4
-rw-r--r--arch/arm64/include/asm/bug.h2
-rw-r--r--arch/arm64/include/asm/cache.h4
-rw-r--r--arch/arm64/include/asm/cpucaps.h4
-rw-r--r--arch/arm64/include/asm/cpufeature.h8
-rw-r--r--arch/arm64/include/asm/cputype.h6
-rw-r--r--arch/arm64/include/asm/current.h4
-rw-r--r--arch/arm64/include/asm/debug-monitors.h4
-rw-r--r--arch/arm64/include/asm/efi.h13
-rw-r--r--arch/arm64/include/asm/el2_setup.h4
-rw-r--r--arch/arm64/include/asm/elf.h4
-rw-r--r--arch/arm64/include/asm/esr.h4
-rw-r--r--arch/arm64/include/asm/fixmap.h4
-rw-r--r--arch/arm64/include/asm/fpsimd.h2
-rw-r--r--arch/arm64/include/asm/fpu.h16
-rw-r--r--arch/arm64/include/asm/ftrace.h6
-rw-r--r--arch/arm64/include/asm/gpr-num.h6
-rw-r--r--arch/arm64/include/asm/hwcap.h2
-rw-r--r--arch/arm64/include/asm/image.h4
-rw-r--r--arch/arm64/include/asm/insn.h4
-rw-r--r--arch/arm64/include/asm/jump_label.h4
-rw-r--r--arch/arm64/include/asm/kasan.h2
-rw-r--r--arch/arm64/include/asm/kexec.h4
-rw-r--r--arch/arm64/include/asm/kfence.h3
-rw-r--r--arch/arm64/include/asm/kgdb.h4
-rw-r--r--arch/arm64/include/asm/kvm_asm.h4
-rw-r--r--arch/arm64/include/asm/kvm_mmu.h4
-rw-r--r--arch/arm64/include/asm/kvm_mte.h4
-rw-r--r--arch/arm64/include/asm/kvm_ptrauth.h6
-rw-r--r--arch/arm64/include/asm/linkage.h2
-rw-r--r--arch/arm64/include/asm/memory.h5
-rw-r--r--arch/arm64/include/asm/mmu.h4
-rw-r--r--arch/arm64/include/asm/mmu_context.h20
-rw-r--r--arch/arm64/include/asm/mte-kasan.h4
-rw-r--r--arch/arm64/include/asm/mte.h4
-rw-r--r--arch/arm64/include/asm/neon.h4
-rw-r--r--arch/arm64/include/asm/page.h8
-rw-r--r--arch/arm64/include/asm/percpu.h15
-rw-r--r--arch/arm64/include/asm/pgtable-hwdef.h143
-rw-r--r--arch/arm64/include/asm/pgtable-prot.h6
-rw-r--r--arch/arm64/include/asm/pgtable.h25
-rw-r--r--arch/arm64/include/asm/proc-fns.h4
-rw-r--r--arch/arm64/include/asm/processor.h11
-rw-r--r--arch/arm64/include/asm/ptrace.h4
-rw-r--r--arch/arm64/include/asm/rsi_smc.h4
-rw-r--r--arch/arm64/include/asm/rwonce.h4
-rw-r--r--arch/arm64/include/asm/scs.h6
-rw-r--r--arch/arm64/include/asm/sdei.h4
-rw-r--r--arch/arm64/include/asm/simd.h12
-rw-r--r--arch/arm64/include/asm/smp.h4
-rw-r--r--arch/arm64/include/asm/spectre.h5
-rw-r--r--arch/arm64/include/asm/stacktrace/frame.h4
-rw-r--r--arch/arm64/include/asm/suspend.h2
-rw-r--r--arch/arm64/include/asm/sysreg.h12
-rw-r--r--arch/arm64/include/asm/system_misc.h4
-rw-r--r--arch/arm64/include/asm/thread_info.h2
-rw-r--r--arch/arm64/include/asm/tlbflush.h85
-rw-r--r--arch/arm64/include/asm/uaccess.h4
-rw-r--r--arch/arm64/include/asm/vdso.h4
-rw-r--r--arch/arm64/include/asm/vdso/compat_barrier.h4
-rw-r--r--arch/arm64/include/asm/vdso/compat_gettimeofday.h4
-rw-r--r--arch/arm64/include/asm/vdso/getrandom.h4
-rw-r--r--arch/arm64/include/asm/vdso/gettimeofday.h4
-rw-r--r--arch/arm64/include/asm/vdso/processor.h4
-rw-r--r--arch/arm64/include/asm/vdso/vsyscall.h4
-rw-r--r--arch/arm64/include/asm/virt.h4
-rw-r--r--arch/arm64/include/asm/vmap_stack.h4
-rw-r--r--arch/arm64/include/asm/xor.h22
-rw-r--r--arch/arm64/include/uapi/asm/kvm.h2
-rw-r--r--arch/arm64/include/uapi/asm/ptrace.h4
-rw-r--r--arch/arm64/include/uapi/asm/sigcontext.h4
-rw-r--r--arch/arm64/kernel/acpi.c22
-rw-r--r--arch/arm64/kernel/alternative.c19
-rw-r--r--arch/arm64/kernel/cpufeature.c12
-rw-r--r--arch/arm64/kernel/efi.c46
-rw-r--r--arch/arm64/kernel/entry-common.c30
-rw-r--r--arch/arm64/kernel/entry-ftrace.S2
-rw-r--r--arch/arm64/kernel/fpsimd.c83
-rw-r--r--arch/arm64/kernel/ftrace.c2
-rw-r--r--arch/arm64/kernel/irq.c2
-rw-r--r--arch/arm64/kernel/machine_kexec.c2
-rw-r--r--arch/arm64/kernel/module.c21
-rw-r--r--arch/arm64/kernel/mte.c3
-rw-r--r--arch/arm64/kernel/pi/map_kernel.c10
-rw-r--r--arch/arm64/kernel/pi/patch-scs.c10
-rw-r--r--arch/arm64/kernel/pi/pi.h2
-rw-r--r--arch/arm64/kernel/probes/kprobes.c5
-rw-r--r--arch/arm64/kernel/probes/uprobes.c2
-rw-r--r--arch/arm64/kernel/proton-pack.c35
-rw-r--r--arch/arm64/kernel/ptrace.c40
-rw-r--r--arch/arm64/kernel/sdei.c6
-rw-r--r--arch/arm64/kernel/smp.c6
-rw-r--r--arch/arm64/kernel/syscall.c2
-rw-r--r--arch/arm64/kernel/traps.c2
-rw-r--r--arch/arm64/kernel/vdso32/Makefile3
-rw-r--r--arch/arm64/kernel/vmcore_info.c2
-rw-r--r--arch/arm64/kvm/arch_timer.c2
-rw-r--r--arch/arm64/kvm/arm.c4
-rw-r--r--arch/arm64/kvm/hyp/nvhe/ffa.c11
-rw-r--r--arch/arm64/kvm/hyp/nvhe/mem_protect.c28
-rw-r--r--arch/arm64/kvm/mmu.c2
-rw-r--r--arch/arm64/kvm/nested.c2
-rw-r--r--arch/arm64/kvm/sys_regs.c75
-rw-r--r--arch/arm64/kvm/vgic/vgic-debug.c16
-rw-r--r--arch/arm64/kvm/vgic/vgic-init.c16
-rw-r--r--arch/arm64/kvm/vgic/vgic-its.c18
-rw-r--r--arch/arm64/kvm/vgic/vgic-v3.c3
-rw-r--r--arch/arm64/kvm/vgic/vgic.c23
-rw-r--r--arch/arm64/mm/contpte.c3
-rw-r--r--arch/arm64/mm/copypage.c11
-rw-r--r--arch/arm64/mm/fault.c29
-rw-r--r--arch/arm64/mm/mmu.c331
-rw-r--r--arch/arm64/mm/pageattr.c12
-rw-r--r--arch/arm64/mm/pgd.c2
-rw-r--r--arch/arm64/mm/proc.S36
-rw-r--r--arch/arm64/net/bpf_jit_comp.c32
-rwxr-xr-xarch/arm64/tools/gen-sysreg.awk146
-rw-r--r--arch/arm64/tools/syscall_32.tbl1
-rw-r--r--arch/arm64/tools/sysreg21
-rw-r--r--arch/csky/abiv2/cacheflush.c2
-rw-r--r--arch/csky/abiv2/inc/abi/cacheflush.h4
-rw-r--r--arch/hexagon/configs/comet_defconfig1
-rw-r--r--arch/loongarch/Makefile2
-rw-r--r--arch/loongarch/configs/loongson3_defconfig1
-rw-r--r--arch/loongarch/include/asm/bug.h27
-rw-r--r--arch/loongarch/include/asm/cpu-features.h2
-rw-r--r--arch/loongarch/include/asm/cpu.h27
-rw-r--r--arch/loongarch/include/asm/hw_breakpoint.h4
-rw-r--r--arch/loongarch/include/asm/io.h5
-rw-r--r--arch/loongarch/include/asm/loongarch.h2
-rw-r--r--arch/loongarch/include/asm/pgalloc.h2
-rw-r--r--arch/loongarch/include/asm/pgtable.h11
-rw-r--r--arch/loongarch/include/uapi/asm/ptrace.h40
-rw-r--r--arch/loongarch/kernel/cpu-probe.c38
-rw-r--r--arch/loongarch/kernel/kexec_efi.c2
-rw-r--r--arch/loongarch/kernel/kexec_elf.c2
-rw-r--r--arch/loongarch/kernel/machine_kexec.c24
-rw-r--r--arch/loongarch/kernel/machine_kexec_file.c2
-rw-r--r--arch/loongarch/kernel/mem.c7
-rw-r--r--arch/loongarch/kernel/numa.c83
-rw-r--r--arch/loongarch/kernel/perf_event.c7
-rw-r--r--arch/loongarch/kernel/proc.c2
-rw-r--r--arch/loongarch/kernel/setup.c5
-rw-r--r--arch/loongarch/kernel/traps.c4
-rw-r--r--arch/loongarch/kvm/intc/eiointc.c2
-rw-r--r--arch/loongarch/kvm/mmu.c2
-rw-r--r--arch/loongarch/kvm/timer.c2
-rw-r--r--arch/loongarch/kvm/vcpu.c19
-rw-r--r--arch/loongarch/mm/init.c2
-rw-r--r--arch/loongarch/mm/ioremap.c2
-rw-r--r--arch/loongarch/net/bpf_jit.c12
-rw-r--r--arch/loongarch/pci/pci.c8
-rw-r--r--arch/loongarch/vdso/Makefile2
-rw-r--r--arch/m68k/coldfire/m5272.c15
-rw-r--r--arch/m68k/configs/amcore_defconfig1
-rw-r--r--arch/m68k/configs/amiga_defconfig4
-rw-r--r--arch/m68k/configs/apollo_defconfig4
-rw-r--r--arch/m68k/configs/atari_defconfig4
-rw-r--r--arch/m68k/configs/bvme6000_defconfig4
-rw-r--r--arch/m68k/configs/hp300_defconfig4
-rw-r--r--arch/m68k/configs/mac_defconfig4
-rw-r--r--arch/m68k/configs/multi_defconfig4
-rw-r--r--arch/m68k/configs/mvme147_defconfig4
-rw-r--r--arch/m68k/configs/mvme16x_defconfig4
-rw-r--r--arch/m68k/configs/q40_defconfig4
-rw-r--r--arch/m68k/configs/stmark2_defconfig1
-rw-r--r--arch/m68k/configs/sun3_defconfig4
-rw-r--r--arch/m68k/configs/sun3x_defconfig4
-rw-r--r--arch/m68k/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/microblaze/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/mips/bcm47xx/setup.c7
-rw-r--r--arch/mips/boot/dts/econet/en751221.dtsi2
-rw-r--r--arch/mips/configs/decstation_64_defconfig1
-rw-r--r--arch/mips/configs/decstation_defconfig1
-rw-r--r--arch/mips/configs/decstation_r4k_defconfig1
-rw-r--r--arch/mips/kernel/process.c2
-rw-r--r--arch/mips/kernel/syscalls/syscall_n32.tbl1
-rw-r--r--arch/mips/kernel/syscalls/syscall_n64.tbl1
-rw-r--r--arch/mips/kernel/syscalls/syscall_o32.tbl1
-rw-r--r--arch/mips/mm/tlb-r4k.c116
-rw-r--r--arch/mips/mti-malta/malta-init.c20
-rw-r--r--arch/mips/mti-malta/malta-setup.c4
-rw-r--r--arch/mips/pci/pci-malta.c3
-rw-r--r--arch/parisc/boot/compressed/Makefile2
-rw-r--r--arch/parisc/include/asm/bug.h6
-rw-r--r--arch/parisc/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/parisc/kernel/unwind.c13
-rw-r--r--arch/powerpc/Kconfig1
-rw-r--r--arch/powerpc/boot/Makefile3
-rw-r--r--arch/powerpc/include/asm/bug.h12
-rw-r--r--arch/powerpc/include/asm/mem_encrypt.h3
-rw-r--r--arch/powerpc/include/asm/uaccess.h8
-rw-r--r--arch/powerpc/kernel/iommu.c5
-rw-r--r--arch/powerpc/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/powerpc/net/bpf_jit_comp.c10
-rw-r--r--arch/powerpc/platforms/Kconfig.cputype1
-rw-r--r--arch/powerpc/platforms/cell/spufs/inode.c42
-rw-r--r--arch/powerpc/platforms/pseries/papr-hvpipe.c39
-rw-r--r--arch/powerpc/platforms/pseries/papr-platform-dump.c30
-rw-r--r--arch/powerpc/platforms/pseries/papr-rtas-common.c27
-rw-r--r--arch/riscv/Kconfig2
-rw-r--r--arch/riscv/Makefile17
-rw-r--r--arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi2
-rw-r--r--arch/riscv/include/asm/asm.h14
-rw-r--r--arch/riscv/include/asm/bug.h10
-rw-r--r--arch/riscv/include/asm/cpufeature.h2
-rw-r--r--arch/riscv/include/asm/hwprobe.h7
-rw-r--r--arch/riscv/include/asm/insn-def.h8
-rw-r--r--arch/riscv/include/asm/pgtable-64.h2
-rw-r--r--arch/riscv/include/asm/pgtable.h2
-rw-r--r--arch/riscv/include/asm/uaccess.h8
-rw-r--r--arch/riscv/include/asm/vdso/arch_data.h6
-rw-r--r--arch/riscv/include/asm/vendor_extensions/mips.h6
-rw-r--r--arch/riscv/include/asm/vendorid_list.h2
-rw-r--r--arch/riscv/kernel/cpu.c4
-rw-r--r--arch/riscv/kernel/cpufeature.c4
-rw-r--r--arch/riscv/kernel/kgdb.c4
-rw-r--r--arch/riscv/kernel/module-sections.c8
-rw-r--r--arch/riscv/kernel/sbi.c6
-rw-r--r--arch/riscv/kernel/smp.c24
-rw-r--r--arch/riscv/kernel/stacktrace.c21
-rw-r--r--arch/riscv/kernel/sys_hwprobe.c76
-rw-r--r--arch/riscv/kernel/tests/Kconfig.debug2
-rw-r--r--arch/riscv/kernel/tests/kprobes/Makefile4
-rw-r--r--arch/riscv/kernel/tests/kprobes/test-kprobes.c5
-rw-r--r--arch/riscv/kernel/unaligned_access_speed.c9
-rw-r--r--arch/riscv/kernel/vdso/hwprobe.c2
-rw-r--r--arch/riscv/kvm/aia_imsic.c16
-rw-r--r--arch/riscv/kvm/mmu.c25
-rw-r--r--arch/riscv/kvm/vcpu.c2
-rw-r--r--arch/riscv/mm/ptdump.c2
-rw-r--r--arch/riscv/net/bpf_jit_comp64.c11
-rw-r--r--arch/s390/Kconfig22
-rw-r--r--arch/s390/Makefile17
-rw-r--r--arch/s390/appldata/appldata_base.c3
-rw-r--r--arch/s390/appldata/appldata_os.c3
-rw-r--r--arch/s390/boot/Makefile1
-rw-r--r--arch/s390/boot/boot.h4
-rw-r--r--arch/s390/boot/ipl_data.c3
-rw-r--r--arch/s390/boot/ipl_parm.c6
-rw-r--r--arch/s390/boot/stackprotector.c6
-rw-r--r--arch/s390/boot/startup.c8
-rw-r--r--arch/s390/configs/compat.config3
-rw-r--r--arch/s390/configs/debug_defconfig18
-rw-r--r--arch/s390/configs/defconfig18
-rw-r--r--arch/s390/configs/zfcpdump_defconfig1
-rw-r--r--arch/s390/crypto/Kconfig20
-rw-r--r--arch/s390/crypto/Makefile2
-rw-r--r--arch/s390/crypto/aes_s390.c3
-rw-r--r--arch/s390/crypto/hmac_s390.c3
-rw-r--r--arch/s390/crypto/paes_s390.c3
-rw-r--r--arch/s390/crypto/phmac_s390.c55
-rw-r--r--arch/s390/crypto/prng.c3
-rw-r--r--arch/s390/crypto/sha.h51
-rw-r--r--arch/s390/crypto/sha3_256_s390.c157
-rw-r--r--arch/s390/crypto/sha3_512_s390.c157
-rw-r--r--arch/s390/crypto/sha_common.c117
-rw-r--r--arch/s390/hypfs/hypfs_diag.c3
-rw-r--r--arch/s390/hypfs/hypfs_diag_fs.c3
-rw-r--r--arch/s390/hypfs/hypfs_sprp.c6
-rw-r--r--arch/s390/hypfs/inode.c3
-rw-r--r--arch/s390/include/asm/ap.h30
-rw-r--r--arch/s390/include/asm/arch-stackprotector.h25
-rw-r--r--arch/s390/include/asm/bug.h102
-rw-r--r--arch/s390/include/asm/cio.h2
-rw-r--r--arch/s390/include/asm/compat.h140
-rw-r--r--arch/s390/include/asm/cpufeature.h1
-rw-r--r--arch/s390/include/asm/elf.h47
-rw-r--r--arch/s390/include/asm/fpu-insn.h3
-rw-r--r--arch/s390/include/asm/ftrace.h19
-rw-r--r--arch/s390/include/asm/idals.h76
-rw-r--r--arch/s390/include/asm/lowcore.h3
-rw-r--r--arch/s390/include/asm/nospec-insn.h2
-rw-r--r--arch/s390/include/asm/pai.h1
-rw-r--r--arch/s390/include/asm/pci.h1
-rw-r--r--arch/s390/include/asm/percpu.h8
-rw-r--r--arch/s390/include/asm/pgtable.h31
-rw-r--r--arch/s390/include/asm/processor.h22
-rw-r--r--arch/s390/include/asm/ptrace.h5
-rw-r--r--arch/s390/include/asm/seccomp.h5
-rw-r--r--arch/s390/include/asm/smp.h2
-rw-r--r--arch/s390/include/asm/stackprotector.h16
-rw-r--r--arch/s390/include/asm/syscall.h19
-rw-r--r--arch/s390/include/asm/syscall_wrapper.h95
-rw-r--r--arch/s390/include/asm/thread_info.h2
-rw-r--r--arch/s390/include/asm/tlbflush.h13
-rw-r--r--arch/s390/include/asm/trace/ap.h87
-rw-r--r--arch/s390/include/asm/trace/zcrypt.h44
-rw-r--r--arch/s390/include/asm/uaccess.h4
-rw-r--r--arch/s390/include/asm/unistd.h8
-rw-r--r--arch/s390/include/asm/vdso-symbols.h12
-rw-r--r--arch/s390/include/uapi/asm/bitsperlong.h4
-rw-r--r--arch/s390/include/uapi/asm/ipcbuf.h3
-rw-r--r--arch/s390/include/uapi/asm/posix_types.h13
-rw-r--r--arch/s390/include/uapi/asm/ptrace.h124
-rw-r--r--arch/s390/include/uapi/asm/sigcontext.h15
-rw-r--r--arch/s390/include/uapi/asm/stat.h70
-rw-r--r--arch/s390/include/uapi/asm/unistd.h4
-rw-r--r--arch/s390/kernel/Makefile12
-rw-r--r--arch/s390/kernel/asm-offsets.c4
-rw-r--r--arch/s390/kernel/audit.c16
-rw-r--r--arch/s390/kernel/audit.h16
-rw-r--r--arch/s390/kernel/compat_audit.c48
-rw-r--r--arch/s390/kernel/compat_linux.c289
-rw-r--r--arch/s390/kernel/compat_linux.h101
-rw-r--r--arch/s390/kernel/compat_ptrace.h64
-rw-r--r--arch/s390/kernel/compat_signal.c420
-rw-r--r--arch/s390/kernel/cpacf.c3
-rw-r--r--arch/s390/kernel/cpcmd.c3
-rw-r--r--arch/s390/kernel/debug.c3
-rw-r--r--arch/s390/kernel/dis.c17
-rw-r--r--arch/s390/kernel/dumpstack.c8
-rw-r--r--arch/s390/kernel/early.c21
-rw-r--r--arch/s390/kernel/entry.S25
-rw-r--r--arch/s390/kernel/head.S (renamed from arch/s390/kernel/head64.S)0
-rw-r--r--arch/s390/kernel/hiperdispatch.c5
-rw-r--r--arch/s390/kernel/module.c21
-rw-r--r--arch/s390/kernel/nmi.c3
-rw-r--r--arch/s390/kernel/os_info.c3
-rw-r--r--arch/s390/kernel/perf_cpum_cf.c6
-rw-r--r--arch/s390/kernel/perf_cpum_sf.c7
-rw-r--r--arch/s390/kernel/perf_event.c4
-rw-r--r--arch/s390/kernel/perf_pai.c1230
-rw-r--r--arch/s390/kernel/perf_pai_crypto.c843
-rw-r--r--arch/s390/kernel/perf_pai_ext.c756
-rw-r--r--arch/s390/kernel/perf_regs.c3
-rw-r--r--arch/s390/kernel/process.c9
-rw-r--r--arch/s390/kernel/processor.c3
-rw-r--r--arch/s390/kernel/ptrace.c524
-rw-r--r--arch/s390/kernel/setup.c6
-rw-r--r--arch/s390/kernel/signal.c27
-rw-r--r--arch/s390/kernel/smp.c15
-rw-r--r--arch/s390/kernel/stackprotector.c156
-rw-r--r--arch/s390/kernel/stacktrace.c3
-rw-r--r--arch/s390/kernel/sthyi.c2
-rw-r--r--arch/s390/kernel/syscall.c12
-rw-r--r--arch/s390/kernel/syscalls/Makefile58
-rw-r--r--arch/s390/kernel/syscalls/syscall.tbl857
-rwxr-xr-xarch/s390/kernel/syscalls/syscalltbl232
-rw-r--r--arch/s390/kernel/sysinfo.c2
-rw-r--r--arch/s390/kernel/time.c3
-rw-r--r--arch/s390/kernel/topology.c3
-rw-r--r--arch/s390/kernel/uprobes.c13
-rw-r--r--arch/s390/kernel/uv.c3
-rw-r--r--arch/s390/kernel/vdso.c36
-rw-r--r--arch/s390/kernel/vdso/.gitignore (renamed from tools/bpf/runqslower/.gitignore)2
-rw-r--r--arch/s390/kernel/vdso/Makefile76
-rwxr-xr-xarch/s390/kernel/vdso/gen_vdso_offsets.sh (renamed from arch/s390/kernel/vdso64/gen_vdso_offsets.sh)2
-rw-r--r--arch/s390/kernel/vdso/getcpu.c (renamed from arch/s390/kernel/vdso64/getcpu.c)0
-rw-r--r--arch/s390/kernel/vdso/note.S (renamed from arch/s390/kernel/vdso32/note.S)0
-rw-r--r--arch/s390/kernel/vdso/vdso.h (renamed from arch/s390/kernel/vdso64/vdso.h)6
-rw-r--r--arch/s390/kernel/vdso/vdso.lds.S (renamed from arch/s390/kernel/vdso64/vdso64.lds.S)43
-rw-r--r--arch/s390/kernel/vdso/vdso_generic.c (renamed from arch/s390/kernel/vdso64/vdso64_generic.c)0
-rw-r--r--arch/s390/kernel/vdso/vdso_user_wrapper.S (renamed from arch/s390/kernel/vdso64/vdso_user_wrapper.S)0
-rw-r--r--arch/s390/kernel/vdso/vdso_wrapper.S (renamed from arch/s390/kernel/vdso32/vdso32_wrapper.S)8
-rw-r--r--arch/s390/kernel/vdso/vgetrandom-chacha.S (renamed from arch/s390/kernel/vdso64/vgetrandom-chacha.S)0
-rw-r--r--arch/s390/kernel/vdso/vgetrandom.c (renamed from arch/s390/kernel/vdso64/vgetrandom.c)0
-rw-r--r--arch/s390/kernel/vdso32/Makefile64
-rwxr-xr-xarch/s390/kernel/vdso32/gen_vdso_offsets.sh15
-rw-r--r--arch/s390/kernel/vdso32/vdso32.lds.S140
-rw-r--r--arch/s390/kernel/vdso32/vdso_user_wrapper.S22
-rw-r--r--arch/s390/kernel/vdso64/.gitignore2
-rw-r--r--arch/s390/kernel/vdso64/Makefile79
-rw-r--r--arch/s390/kernel/vdso64/note.S13
-rw-r--r--arch/s390/kernel/vdso64/vdso64_wrapper.S15
-rw-r--r--arch/s390/kernel/vmlinux.lds.S15
-rw-r--r--arch/s390/kvm/interrupt.c3
-rw-r--r--arch/s390/kvm/kvm-s390.c3
-rw-r--r--arch/s390/kvm/priv.c2
-rw-r--r--arch/s390/mm/cmm.c4
-rw-r--r--arch/s390/mm/dump_pagetables.c23
-rw-r--r--arch/s390/mm/extmem.c17
-rw-r--r--arch/s390/mm/fault.c29
-rw-r--r--arch/s390/mm/gmap.c28
-rw-r--r--arch/s390/mm/hugetlbpage.c3
-rw-r--r--arch/s390/mm/mmap.c1
-rw-r--r--arch/s390/mm/pageattr.c4
-rw-r--r--arch/s390/mm/pfault.c3
-rw-r--r--arch/s390/mm/pgalloc.c2
-rw-r--r--arch/s390/mm/pgtable.c18
-rw-r--r--arch/s390/mm/vmem.c21
-rw-r--r--arch/s390/net/bpf_jit_comp.c10
-rw-r--r--arch/s390/pci/pci.c3
-rw-r--r--arch/s390/pci/pci_bus.c7
-rw-r--r--arch/s390/pci/pci_clp.c7
-rw-r--r--arch/s390/pci/pci_debug.c3
-rw-r--r--arch/s390/pci/pci_event.c7
-rw-r--r--arch/s390/pci/pci_iov.c3
-rw-r--r--arch/s390/pci/pci_irq.c12
-rw-r--r--arch/s390/pci/pci_report.c3
-rw-r--r--arch/s390/pci/pci_sysfs.c3
-rw-r--r--arch/s390/purgatory/Makefile3
-rw-r--r--arch/s390/tools/gen_facilities.c1
-rw-r--r--arch/sh/configs/ap325rxa_defconfig1
-rw-r--r--arch/sh/configs/apsh4a3a_defconfig1
-rw-r--r--arch/sh/configs/apsh4ad0a_defconfig1
-rw-r--r--arch/sh/configs/dreamcast_defconfig1
-rw-r--r--arch/sh/configs/ecovec24_defconfig1
-rw-r--r--arch/sh/configs/edosk7760_defconfig1
-rw-r--r--arch/sh/configs/espt_defconfig1
-rw-r--r--arch/sh/configs/hp6xx_defconfig1
-rw-r--r--arch/sh/configs/landisk_defconfig1
-rw-r--r--arch/sh/configs/lboxre2_defconfig1
-rw-r--r--arch/sh/configs/migor_defconfig1
-rw-r--r--arch/sh/configs/r7780mp_defconfig1
-rw-r--r--arch/sh/configs/r7785rp_defconfig1
-rw-r--r--arch/sh/configs/rts7751r2d1_defconfig1
-rw-r--r--arch/sh/configs/rts7751r2dplus_defconfig1
-rw-r--r--arch/sh/configs/sdk7780_defconfig1
-rw-r--r--arch/sh/configs/sdk7786_defconfig1
-rw-r--r--arch/sh/configs/se7206_defconfig1
-rw-r--r--arch/sh/configs/se7343_defconfig1
-rw-r--r--arch/sh/configs/se7705_defconfig1
-rw-r--r--arch/sh/configs/se7712_defconfig1
-rw-r--r--arch/sh/configs/se7721_defconfig1
-rw-r--r--arch/sh/configs/se7722_defconfig1
-rw-r--r--arch/sh/configs/se7724_defconfig1
-rw-r--r--arch/sh/configs/se7750_defconfig1
-rw-r--r--arch/sh/configs/se7751_defconfig1
-rw-r--r--arch/sh/configs/se7780_defconfig1
-rw-r--r--arch/sh/configs/sh03_defconfig1
-rw-r--r--arch/sh/configs/sh2007_defconfig1
-rw-r--r--arch/sh/configs/sh7710voipgw_defconfig1
-rw-r--r--arch/sh/configs/sh7757lcr_defconfig1
-rw-r--r--arch/sh/configs/sh7763rdp_defconfig1
-rw-r--r--arch/sh/configs/sh7785lcr_32bit_defconfig1
-rw-r--r--arch/sh/configs/sh7785lcr_defconfig1
-rw-r--r--arch/sh/configs/shmin_defconfig1
-rw-r--r--arch/sh/configs/shx3_defconfig1
-rw-r--r--arch/sh/configs/titan_defconfig1
-rw-r--r--arch/sh/configs/ul2_defconfig1
-rw-r--r--arch/sh/configs/urquell_defconfig1
-rw-r--r--arch/sh/include/asm/bug.h4
-rw-r--r--arch/sh/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/sparc/configs/sparc32_defconfig1
-rw-r--r--arch/sparc/configs/sparc64_defconfig1
-rw-r--r--arch/sparc/kernel/syscalls/syscall.tbl1
-rw-r--r--arch/um/kernel/kmsg_dump.c2
-rw-r--r--arch/x86/Kconfig5
-rw-r--r--arch/x86/Kconfig.cpufeatures4
-rw-r--r--arch/x86/Makefile8
-rw-r--r--arch/x86/boot/a20.c10
-rw-r--r--arch/x86/boot/boot.h2
-rw-r--r--arch/x86/boot/compressed/Makefile7
-rw-r--r--arch/x86/boot/compressed/misc.h11
-rw-r--r--arch/x86/boot/compressed/pgtable_64.c11
-rw-r--r--arch/x86/boot/compressed/sev-handle-vc.c3
-rw-r--r--arch/x86/boot/compressed/sev.c7
-rw-r--r--arch/x86/boot/compressed/sev.h6
-rw-r--r--arch/x86/boot/cpucheck.c16
-rw-r--r--arch/x86/boot/msr.h26
-rw-r--r--arch/x86/boot/startup/Makefile2
-rw-r--r--arch/x86/boot/startup/sev-shared.c2
-rw-r--r--arch/x86/coco/sev/vc-handle.c1
-rw-r--r--arch/x86/coco/sev/vc-shared.c11
-rw-r--r--arch/x86/crypto/Kconfig10
-rw-r--r--arch/x86/crypto/Makefile8
-rw-r--r--arch/x86/crypto/aes-gcm-aesni-x86_64.S12
-rw-r--r--arch/x86/crypto/aes-gcm-vaes-avx2.S1146
-rw-r--r--arch/x86/crypto/aes-gcm-vaes-avx512.S (renamed from arch/x86/crypto/aes-gcm-avx10-x86_64.S)722
-rw-r--r--arch/x86/crypto/aesni-intel_glue.c267
-rw-r--r--arch/x86/crypto/polyval-clmulni_glue.c180
-rw-r--r--arch/x86/entry/entry.S15
-rw-r--r--arch/x86/entry/entry_64.S3
-rw-r--r--arch/x86/entry/entry_64_fred.S3
-rw-r--r--arch/x86/entry/entry_fred.c4
-rw-r--r--arch/x86/entry/syscall_32.c3
-rw-r--r--arch/x86/entry/syscalls/syscall_32.tbl1
-rw-r--r--arch/x86/entry/syscalls/syscall_64.tbl1
-rw-r--r--arch/x86/events/amd/core.c12
-rw-r--r--arch/x86/events/core.c83
-rw-r--r--arch/x86/events/intel/core.c445
-rw-r--r--arch/x86/events/intel/cstate.c18
-rw-r--r--arch/x86/events/intel/ds.c604
-rw-r--r--arch/x86/events/intel/lbr.c3
-rw-r--r--arch/x86/events/intel/pt.c7
-rw-r--r--arch/x86/events/intel/uncore.c3
-rw-r--r--arch/x86/events/perf_event.h41
-rw-r--r--arch/x86/include/asm/alternative.h4
-rw-r--r--arch/x86/include/asm/amd/node.h1
-rw-r--r--arch/x86/include/asm/asm.h5
-rw-r--r--arch/x86/include/asm/bug.h147
-rw-r--r--arch/x86/include/asm/cpufeature.h3
-rw-r--r--arch/x86/include/asm/cpufeatures.h11
-rw-r--r--arch/x86/include/asm/fred.h2
-rw-r--r--arch/x86/include/asm/ftrace.h5
-rw-r--r--arch/x86/include/asm/futex.h75
-rw-r--r--arch/x86/include/asm/idtentry.h4
-rw-r--r--arch/x86/include/asm/insn-eval.h2
-rw-r--r--arch/x86/include/asm/insn.h5
-rw-r--r--arch/x86/include/asm/intel-family.h6
-rw-r--r--arch/x86/include/asm/intel_ds.h10
-rw-r--r--arch/x86/include/asm/jump_label.h1
-rw-r--r--arch/x86/include/asm/kvm_types.h5
-rw-r--r--arch/x86/include/asm/mce.h14
-rw-r--r--arch/x86/include/asm/msr-index.h30
-rw-r--r--arch/x86/include/asm/page_64.h17
-rw-r--r--arch/x86/include/asm/percpu.h5
-rw-r--r--arch/x86/include/asm/perf_event.h116
-rw-r--r--arch/x86/include/asm/ptrace.h24
-rw-r--r--arch/x86/include/asm/runtime-const.h4
-rw-r--r--arch/x86/include/asm/sgx.h97
-rw-r--r--arch/x86/include/asm/shared/msr.h15
-rw-r--r--arch/x86/include/asm/smap.h41
-rw-r--r--arch/x86/include/asm/smp.h2
-rw-r--r--arch/x86/include/asm/string.h26
-rw-r--r--arch/x86/include/asm/svm.h1
-rw-r--r--arch/x86/include/asm/topology.h14
-rw-r--r--arch/x86/include/asm/uaccess.h12
-rw-r--r--arch/x86/include/asm/uaccess_64.h10
-rw-r--r--arch/x86/include/asm/unwind_user.h41
-rw-r--r--arch/x86/include/asm/uprobes.h9
-rw-r--r--arch/x86/include/uapi/asm/processor-flags.h2
-rw-r--r--arch/x86/include/uapi/asm/sgx.h10
-rw-r--r--arch/x86/include/uapi/asm/vmx.h1
-rw-r--r--arch/x86/kernel/acpi/apei.c2
-rw-r--r--arch/x86/kernel/acpi/cppc.c2
-rw-r--r--arch/x86/kernel/alternative.c96
-rw-r--r--arch/x86/kernel/amd_node.c150
-rw-r--r--arch/x86/kernel/apic/apic.c18
-rw-r--r--arch/x86/kernel/apic/apic_common.c3
-rw-r--r--arch/x86/kernel/apic/io_apic.c2
-rw-r--r--arch/x86/kernel/cpu/amd.c24
-rw-r--r--arch/x86/kernel/cpu/bugs.c262
-rw-r--r--arch/x86/kernel/cpu/bus_lock.c3
-rw-r--r--arch/x86/kernel/cpu/common.c45
-rw-r--r--arch/x86/kernel/cpu/cpu.h9
-rw-r--r--arch/x86/kernel/cpu/cpuid-deps.c3
-rw-r--r--arch/x86/kernel/cpu/mce/amd.c356
-rw-r--r--arch/x86/kernel/cpu/mce/core.c31
-rw-r--r--arch/x86/kernel/cpu/mce/internal.h4
-rw-r--r--arch/x86/kernel/cpu/mce/threshold.c19
-rw-r--r--arch/x86/kernel/cpu/microcode/amd.c130
-rw-r--r--arch/x86/kernel/cpu/microcode/core.c13
-rw-r--r--arch/x86/kernel/cpu/microcode/intel.c362
-rw-r--r--arch/x86/kernel/cpu/microcode/internal.h4
-rw-r--r--arch/x86/kernel/cpu/mtrr/generic.c1
-rw-r--r--arch/x86/kernel/cpu/mtrr/mtrr.h4
-rw-r--r--arch/x86/kernel/cpu/resctrl/core.c9
-rw-r--r--arch/x86/kernel/cpu/resctrl/ctrlmondata.c40
-rw-r--r--arch/x86/kernel/cpu/resctrl/internal.h5
-rw-r--r--arch/x86/kernel/cpu/resctrl/monitor.c12
-rw-r--r--arch/x86/kernel/cpu/scattered.c5
-rw-r--r--arch/x86/kernel/cpu/sgx/driver.c19
-rw-r--r--arch/x86/kernel/cpu/sgx/encl.c1
-rw-r--r--arch/x86/kernel/cpu/sgx/encls.h5
-rw-r--r--arch/x86/kernel/cpu/sgx/main.c104
-rw-r--r--arch/x86/kernel/cpu/sgx/sgx.h3
-rw-r--r--arch/x86/kernel/cpu/sgx/virt.c25
-rw-r--r--arch/x86/kernel/cpu/topology.c4
-rw-r--r--arch/x86/kernel/cpu/topology_common.c3
-rw-r--r--arch/x86/kernel/cpu/tsx.c58
-rw-r--r--arch/x86/kernel/dumpstack.c23
-rw-r--r--arch/x86/kernel/e820.c3
-rw-r--r--arch/x86/kernel/fpu/core.c24
-rw-r--r--arch/x86/kernel/fpu/xstate.c7
-rw-r--r--arch/x86/kernel/ftrace.c7
-rw-r--r--arch/x86/kernel/ftrace_64.S20
-rw-r--r--arch/x86/kernel/hw_breakpoint.c3
-rw-r--r--arch/x86/kernel/irq.c3
-rw-r--r--arch/x86/kernel/kprobes/core.c3
-rw-r--r--arch/x86/kernel/kprobes/opt.c4
-rw-r--r--arch/x86/kernel/kvm.c5
-rw-r--r--arch/x86/kernel/module.c15
-rw-r--r--arch/x86/kernel/msr.c2
-rw-r--r--arch/x86/kernel/nmi.c5
-rw-r--r--arch/x86/kernel/process_64.c5
-rw-r--r--arch/x86/kernel/reboot.c5
-rw-r--r--arch/x86/kernel/relocate_kernel_64.S7
-rw-r--r--arch/x86/kernel/smpboot.c81
-rw-r--r--arch/x86/kernel/static_call.c13
-rw-r--r--arch/x86/kernel/traps.c163
-rw-r--r--arch/x86/kernel/tsc.c1
-rw-r--r--arch/x86/kernel/uprobes.c70
-rw-r--r--arch/x86/kvm/reverse_cpuid.h1
-rw-r--r--arch/x86/kvm/svm/avic.c24
-rw-r--r--arch/x86/kvm/svm/nested.c20
-rw-r--r--arch/x86/kvm/svm/svm.c95
-rw-r--r--arch/x86/kvm/svm/svm.h5
-rw-r--r--arch/x86/kvm/vmx/common.h2
-rw-r--r--arch/x86/kvm/vmx/nested.c8
-rw-r--r--arch/x86/kvm/vmx/vmx.c8
-rw-r--r--arch/x86/kvm/x86.c50
-rw-r--r--arch/x86/lib/cache-smp.c9
-rw-r--r--arch/x86/lib/insn-eval.c151
-rw-r--r--arch/x86/lib/kaslr.c2
-rw-r--r--arch/x86/lib/msr.c5
-rw-r--r--arch/x86/math-emu/poly.h2
-rw-r--r--arch/x86/mm/dump_pagetables.c1
-rw-r--r--arch/x86/mm/init_64.c3
-rw-r--r--arch/x86/mm/pat/memtype.c3
-rw-r--r--arch/x86/mm/physaddr.c11
-rw-r--r--arch/x86/mm/tlb.c5
-rw-r--r--arch/x86/net/bpf_jit_comp.c99
-rw-r--r--arch/x86/virt/vmx/tdx/tdx.c69
-rw-r--r--arch/xtensa/configs/audio_kc705_defconfig1
-rw-r--r--arch/xtensa/configs/generic_kc705_defconfig1
-rw-r--r--arch/xtensa/configs/iss_defconfig1
-rw-r--r--arch/xtensa/configs/nommu_kc705_defconfig1
-rw-r--r--arch/xtensa/configs/smp_lx200_defconfig1
-rw-r--r--arch/xtensa/configs/virt_defconfig1
-rw-r--r--arch/xtensa/configs/xip_kc705_defconfig1
-rw-r--r--arch/xtensa/kernel/syscalls/syscall.tbl1
-rw-r--r--block/bdev.c25
-rw-r--r--block/bio-integrity-auto.c26
-rw-r--r--block/bio-integrity.c48
-rw-r--r--block/bio.c1
-rw-r--r--block/blk-core.c12
-rw-r--r--block/blk-crypto.c2
-rw-r--r--block/blk-iocost.c6
-rw-r--r--block/blk-lib.c6
-rw-r--r--block/blk-map.c3
-rw-r--r--block/blk-merge.c44
-rw-r--r--block/blk-mq-dma.c31
-rw-r--r--block/blk-mq-sched.c120
-rw-r--r--block/blk-mq-sched.h40
-rw-r--r--block/blk-mq-tag.c2
-rw-r--r--block/blk-mq.c152
-rw-r--r--block/blk-mq.h2
-rw-r--r--block/blk-settings.c37
-rw-r--r--block/blk-sysfs.c26
-rw-r--r--block/blk-throttle.c45
-rw-r--r--block/blk-zoned.c928
-rw-r--r--block/blk.h23
-rw-r--r--block/elevator.c80
-rw-r--r--block/elevator.h27
-rw-r--r--block/fops.c5
-rw-r--r--block/genhd.c8
-rw-r--r--block/ioctl.c8
-rw-r--r--block/kyber-iosched.c30
-rw-r--r--block/mq-deadline.c129
-rw-r--r--block/partitions/efi.c3
-rw-r--r--crypto/Kconfig35
-rw-r--r--crypto/Makefile9
-rw-r--r--crypto/aead.c20
-rw-r--r--crypto/aegis128-neon.c33
-rw-r--r--crypto/af_alg.c7
-rw-r--r--crypto/ahash.c18
-rw-r--r--crypto/algif_hash.c3
-rw-r--r--crypto/algif_rng.c3
-rw-r--r--crypto/ansi_cprng.c474
-rw-r--r--crypto/asymmetric_keys/asymmetric_type.c14
-rw-r--r--crypto/asymmetric_keys/restrict.c7
-rw-r--r--crypto/asymmetric_keys/x509_cert_parser.c2
-rw-r--r--crypto/asymmetric_keys/x509_public_key.c2
-rw-r--r--crypto/authenc.c75
-rw-r--r--crypto/blake2b.c111
-rw-r--r--crypto/blake2b_generic.c192
-rw-r--r--crypto/deflate.c3
-rw-r--r--crypto/df_sp80090a.c232
-rw-r--r--crypto/drbg.c266
-rw-r--r--crypto/fips.c5
-rw-r--r--crypto/hctr2.c226
-rw-r--r--crypto/jitterentropy-kcapi.c12
-rw-r--r--crypto/polyval-generic.c205
-rw-r--r--crypto/scatterwalk.c345
-rw-r--r--crypto/sha3.c166
-rw-r--r--crypto/sha3_generic.c290
-rw-r--r--crypto/skcipher.c261
-rw-r--r--crypto/tcrypt.c12
-rw-r--r--crypto/tcrypt.h18
-rw-r--r--crypto/testmgr.c118
-rw-r--r--crypto/testmgr.h397
-rw-r--r--crypto/zstd.c17
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/accel/Kconfig1
-rw-r--r--drivers/accel/Makefile1
-rw-r--r--drivers/accel/amdxdna/Makefile1
-rw-r--r--drivers/accel/amdxdna/TODO1
-rw-r--r--drivers/accel/amdxdna/aie2_ctx.c195
-rw-r--r--drivers/accel/amdxdna/aie2_error.c95
-rw-r--r--drivers/accel/amdxdna/aie2_message.c647
-rw-r--r--drivers/accel/amdxdna/aie2_msg_priv.h88
-rw-r--r--drivers/accel/amdxdna/aie2_pci.c269
-rw-r--r--drivers/accel/amdxdna/aie2_pci.h54
-rw-r--r--drivers/accel/amdxdna/aie2_smu.c49
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.c104
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.h45
-rw-r--r--drivers/accel/amdxdna/amdxdna_error.h59
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.c51
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.h6
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox.c14
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox_helper.h6
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.c63
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.h3
-rw-r--r--drivers/accel/amdxdna/amdxdna_pm.c94
-rw-r--r--drivers/accel/amdxdna/amdxdna_pm.h18
-rw-r--r--drivers/accel/amdxdna/npu1_regs.c8
-rw-r--r--drivers/accel/amdxdna/npu2_regs.c2
-rw-r--r--drivers/accel/amdxdna/npu4_regs.c12
-rw-r--r--drivers/accel/amdxdna/npu5_regs.c2
-rw-r--r--drivers/accel/amdxdna/npu6_regs.c2
-rw-r--r--drivers/accel/ethosu/Kconfig11
-rw-r--r--drivers/accel/ethosu/Makefile4
-rw-r--r--drivers/accel/ethosu/ethosu_device.h197
-rw-r--r--drivers/accel/ethosu/ethosu_drv.c403
-rw-r--r--drivers/accel/ethosu/ethosu_drv.h15
-rw-r--r--drivers/accel/ethosu/ethosu_gem.c704
-rw-r--r--drivers/accel/ethosu/ethosu_gem.h46
-rw-r--r--drivers/accel/ethosu/ethosu_job.c497
-rw-r--r--drivers/accel/ethosu/ethosu_job.h40
-rw-r--r--drivers/accel/ivpu/Makefile1
-rw-r--r--drivers/accel/ivpu/ivpu_debugfs.c38
-rw-r--r--drivers/accel/ivpu/ivpu_drv.c18
-rw-r--r--drivers/accel/ivpu/ivpu_drv.h5
-rw-r--r--drivers/accel/ivpu/ivpu_fw.c229
-rw-r--r--drivers/accel/ivpu/ivpu_fw.h14
-rw-r--r--drivers/accel/ivpu/ivpu_gem.c161
-rw-r--r--drivers/accel/ivpu/ivpu_gem.h22
-rw-r--r--drivers/accel/ivpu/ivpu_gem_userptr.c213
-rw-r--r--drivers/accel/ivpu/ivpu_hw.c59
-rw-r--r--drivers/accel/ivpu/ivpu_hw.h10
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs.c20
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs.h2
-rw-r--r--drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h3
-rw-r--r--drivers/accel/ivpu/ivpu_hw_ip.c10
-rw-r--r--drivers/accel/ivpu/ivpu_ipc.c2
-rw-r--r--drivers/accel/ivpu/ivpu_job.c257
-rw-r--r--drivers/accel/ivpu/ivpu_job.h49
-rw-r--r--drivers/accel/ivpu/ivpu_mmu.c2
-rw-r--r--drivers/accel/ivpu/ivpu_mmu_context.c9
-rw-r--r--drivers/accel/ivpu/ivpu_mmu_context.h2
-rw-r--r--drivers/accel/ivpu/ivpu_ms.c25
-rw-r--r--drivers/accel/ivpu/ivpu_pm.c18
-rw-r--r--drivers/accel/ivpu/ivpu_sysfs.c3
-rw-r--r--drivers/accel/ivpu/vpu_jsm_api.h653
-rw-r--r--drivers/accel/qaic/Kconfig1
-rw-r--r--drivers/accel/qaic/Makefile2
-rw-r--r--drivers/accel/qaic/qaic.h40
-rw-r--r--drivers/accel/qaic/qaic_control.c25
-rw-r--r--drivers/accel/qaic/qaic_data.c164
-rw-r--r--drivers/accel/qaic/qaic_drv.c116
-rw-r--r--drivers/accel/qaic/qaic_ras.c6
-rw-r--r--drivers/accel/qaic/qaic_ssr.c815
-rw-r--r--drivers/accel/qaic/qaic_ssr.h17
-rw-r--r--drivers/accel/qaic/qaic_sysfs.c109
-rw-r--r--drivers/accel/qaic/qaic_timesync.c9
-rw-r--r--drivers/accel/qaic/qaic_timesync.h3
-rw-r--r--drivers/accel/qaic/sahara.c164
-rw-r--r--drivers/accel/rocket/rocket_gem.c1
-rw-r--r--drivers/acpi/acpi_mrrm.c46
-rw-r--r--drivers/acpi/acpi_tad.c73
-rw-r--r--drivers/acpi/acpi_video.c4
-rw-r--r--drivers/acpi/acpica/nswalk.c9
-rw-r--r--drivers/acpi/acpica/tbprint.c6
-rw-r--r--drivers/acpi/apei/einj-core.c64
-rw-r--r--drivers/acpi/apei/ghes.c27
-rw-r--r--drivers/acpi/arm64/Kconfig3
-rw-r--r--drivers/acpi/arm64/Makefile1
-rw-r--r--drivers/acpi/arm64/gtdt.c38
-rw-r--r--drivers/acpi/arm64/mpam.c411
-rw-r--r--drivers/acpi/battery.c12
-rw-r--r--drivers/acpi/button.c4
-rw-r--r--drivers/acpi/cppc_acpi.c8
-rw-r--r--drivers/acpi/dptf/Makefile1
-rw-r--r--drivers/acpi/dptf/dptf_pch_fivr.c2
-rw-r--r--drivers/acpi/dptf/dptf_power.c2
-rw-r--r--drivers/acpi/dptf/int340x_thermal.c94
-rw-r--r--drivers/acpi/ec.c3
-rw-r--r--drivers/acpi/fan.h48
-rw-r--r--drivers/acpi/fan_attr.c2
-rw-r--r--drivers/acpi/fan_core.c259
-rw-r--r--drivers/acpi/fan_hwmon.c32
-rw-r--r--drivers/acpi/internal.h1
-rw-r--r--drivers/acpi/irq.c19
-rw-r--r--drivers/acpi/numa/hmat.c57
-rw-r--r--drivers/acpi/numa/srat.c2
-rw-r--r--drivers/acpi/osl.c6
-rw-r--r--drivers/acpi/pptt.c280
-rw-r--r--drivers/acpi/prmt.c6
-rw-r--r--drivers/acpi/processor_core.c2
-rw-r--r--drivers/acpi/processor_driver.c6
-rw-r--r--drivers/acpi/processor_idle.c157
-rw-r--r--drivers/acpi/property.c31
-rw-r--r--drivers/acpi/riscv/rimt.c122
-rw-r--r--drivers/acpi/sbs.c2
-rw-r--r--drivers/acpi/scan.c3
-rw-r--r--drivers/acpi/sleep.c2
-rw-r--r--drivers/acpi/sleep.h3
-rw-r--r--drivers/acpi/spcr.c2
-rw-r--r--drivers/acpi/tables.c2
-rw-r--r--drivers/acpi/thermal.c3
-rw-r--r--drivers/acpi/x86/lpss.c2
-rw-r--r--drivers/acpi/x86/s2idle.c65
-rw-r--r--drivers/amba/Kconfig2
-rw-r--r--drivers/amba/bus.c9
-rw-r--r--drivers/android/binder.c38
-rw-r--r--drivers/android/binder/error.rs5
-rw-r--r--drivers/android/binder/freeze.rs22
-rw-r--r--drivers/android/binder/node.rs2
-rw-r--r--drivers/android/binder/process.rs54
-rw-r--r--drivers/android/binder/range_alloc/tree.rs2
-rw-r--r--drivers/android/binder/stats.rs6
-rw-r--r--drivers/android/binder/transaction.rs6
-rw-r--r--drivers/android/binder_netlink.c1
-rw-r--r--drivers/android/binder_netlink.h1
-rw-r--r--drivers/ata/libata-core.c14
-rw-r--r--drivers/ata/libata-scsi.c11
-rw-r--r--drivers/ata/libata-sff.c3
-rw-r--r--drivers/ata/pata_it821x.c5
-rw-r--r--drivers/ata/pata_pcmcia.c1
-rw-r--r--drivers/atm/fore200e.c2
-rw-r--r--drivers/auxdisplay/line-display.c240
-rw-r--r--drivers/auxdisplay/line-display.h4
-rw-r--r--drivers/base/arch_topology.c2
-rw-r--r--drivers/base/core.c2
-rw-r--r--drivers/base/devcoredump.c136
-rw-r--r--drivers/base/devtmpfs.c6
-rw-r--r--drivers/base/firmware_loader/main.c59
-rw-r--r--drivers/base/memory.c23
-rw-r--r--drivers/base/platform.c71
-rw-r--r--drivers/base/power/generic_ops.c85
-rw-r--r--drivers/base/power/main.c41
-rw-r--r--drivers/base/power/runtime.c23
-rw-r--r--drivers/base/power/trace.c4
-rw-r--r--drivers/base/power/wakeup.c24
-rw-r--r--drivers/base/regmap/internal.h2
-rw-r--r--drivers/base/regmap/regcache-flat.c107
-rw-r--r--drivers/base/regmap/regcache-maple.c47
-rw-r--r--drivers/base/regmap/regcache-rbtree.c31
-rw-r--r--drivers/base/regmap/regcache.c17
-rw-r--r--drivers/base/regmap/regmap-i3c.c5
-rw-r--r--drivers/base/regmap/regmap-kunit.c22
-rw-r--r--drivers/base/regmap/regmap-sdw-mbq.c26
-rw-r--r--drivers/base/regmap/regmap-slimbus.c6
-rw-r--r--drivers/base/swnode.c30
-rw-r--r--drivers/bcma/main.c6
-rw-r--r--drivers/block/drbd/drbd_bitmap.c10
-rw-r--r--drivers/block/drbd/drbd_receiver.c14
-rw-r--r--drivers/block/floppy.c2
-rw-r--r--drivers/block/loop.c4
-rw-r--r--drivers/block/nbd.c44
-rw-r--r--drivers/block/null_blk/main.c81
-rw-r--r--drivers/block/null_blk/null_blk.h3
-rw-r--r--drivers/block/null_blk/zoned.c6
-rw-r--r--drivers/block/ps3disk.c4
-rw-r--r--drivers/block/rnbd/rnbd-proto.h15
-rw-r--r--drivers/block/rnull/configfs.rs9
-rw-r--r--drivers/block/rnull/rnull.rs3
-rw-r--r--drivers/block/ublk_drv.c407
-rw-r--r--drivers/block/virtio_blk.c24
-rw-r--r--drivers/block/zloop.c160
-rw-r--r--drivers/bluetooth/Kconfig1
-rw-r--r--drivers/bluetooth/bpa10x.c4
-rw-r--r--drivers/bluetooth/btbcm.c4
-rw-r--r--drivers/bluetooth/btintel_pcie.c186
-rw-r--r--drivers/bluetooth/btintel_pcie.h4
-rw-r--r--drivers/bluetooth/btmtksdio.c13
-rw-r--r--drivers/bluetooth/btmtkuart.c4
-rw-r--r--drivers/bluetooth/btnxpuart.c4
-rw-r--r--drivers/bluetooth/btrtl.c44
-rw-r--r--drivers/bluetooth/btusb.c99
-rw-r--r--drivers/bluetooth/hci_ag6xx.c2
-rw-r--r--drivers/bluetooth/hci_aml.c2
-rw-r--r--drivers/bluetooth/hci_ath.c2
-rw-r--r--drivers/bluetooth/hci_bcm.c8
-rw-r--r--drivers/bluetooth/hci_h4.c6
-rw-r--r--drivers/bluetooth/hci_h5.c53
-rw-r--r--drivers/bluetooth/hci_intel.c5
-rw-r--r--drivers/bluetooth/hci_ll.c2
-rw-r--r--drivers/bluetooth/hci_mrvl.c6
-rw-r--r--drivers/bluetooth/hci_nokia.c4
-rw-r--r--drivers/bluetooth/hci_qca.c2
-rw-r--r--drivers/bluetooth/hci_uart.h2
-rw-r--r--drivers/bus/fsl-mc/mc-sys.c2
-rw-r--r--drivers/char/hw_random/bcm2835-rng.c11
-rw-r--r--drivers/char/hw_random/core.c11
-rw-r--r--drivers/char/hw_random/s390-trng.c3
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c3
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c6
-rw-r--r--drivers/char/random.c44
-rw-r--r--drivers/char/tpm/tpm-chip.c37
-rw-r--r--drivers/char/tpm/tpm-dev-common.c3
-rw-r--r--drivers/char/tpm/tpm-interface.c20
-rw-r--r--drivers/char/tpm/tpm.h1
-rw-r--r--drivers/char/tpm/tpm1-cmd.c5
-rw-r--r--drivers/char/tpm/tpm2-cmd.c22
-rw-r--r--drivers/char/tpm/tpm_crb.c4
-rw-r--r--drivers/char/tpm/tpm_tis_core.c3
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c4
-rw-r--r--drivers/clk/sunxi-ng/ccu-sun55i-a523.c2
-rw-r--r--drivers/clocksource/Kconfig11
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/arm_arch_timer_mmio.c2
-rw-r--r--drivers/clocksource/sh_cmt.c36
-rw-r--r--drivers/clocksource/timer-nxp-pit.c3
-rw-r--r--drivers/clocksource/timer-nxp-stm.c23
-rw-r--r--drivers/clocksource/timer-ralink.c11
-rw-r--r--drivers/clocksource/timer-rda.c9
-rw-r--r--drivers/clocksource/timer-realtek.c150
-rw-r--r--drivers/clocksource/timer-sp804.c24
-rw-r--r--drivers/clocksource/timer-sprd.c24
-rw-r--r--drivers/clocksource/timer-stm32-lp.c1
-rw-r--r--drivers/comedi/comedi_buf.c2
-rw-r--r--drivers/counter/microchip-tcb-capture.c2
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c2
-rw-r--r--drivers/cpufreq/amd-pstate.c41
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c17
-rw-r--r--drivers/cpufreq/cpufreq-dt-platdev.c16
-rw-r--r--drivers/cpufreq/cpufreq-nforce2.c3
-rw-r--r--drivers/cpufreq/cpufreq.c11
-rw-r--r--drivers/cpufreq/intel_pstate.c228
-rw-r--r--drivers/cpufreq/mediatek-cpufreq.c12
-rw-r--r--drivers/cpufreq/qcom-cpufreq-nvmem.c35
-rw-r--r--drivers/cpufreq/s5pv210-cpufreq.c6
-rw-r--r--drivers/cpufreq/sun50i-cpufreq-nvmem.c11
-rw-r--r--drivers/cpufreq/tegra186-cpufreq.c150
-rw-r--r--drivers/cpufreq/tegra194-cpufreq.c3
-rw-r--r--drivers/cpuidle/cpuidle-big_little.c11
-rw-r--r--drivers/cpuidle/cpuidle-psci.c4
-rw-r--r--drivers/cpuidle/cpuidle-riscv-sbi.c5
-rw-r--r--drivers/cpuidle/cpuidle.c12
-rw-r--r--drivers/cpuidle/driver.c10
-rw-r--r--drivers/cpuidle/governor.c4
-rw-r--r--drivers/cpuidle/governors/menu.c31
-rw-r--r--drivers/cpuidle/governors/teo.c159
-rw-r--r--drivers/cpuidle/poll_state.c4
-rw-r--r--drivers/crypto/Kconfig1
-rw-r--r--drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c2
-rw-r--r--drivers/crypto/aspeed/aspeed-acry.c2
-rw-r--r--drivers/crypto/atmel-i2c.c2
-rw-r--r--drivers/crypto/axis/artpec6_crypto.c9
-rw-r--r--drivers/crypto/caam/blob_gen.c86
-rw-r--r--drivers/crypto/caam/caamalg.c128
-rw-r--r--drivers/crypto/caam/caamalg_desc.c87
-rw-r--r--drivers/crypto/caam/caamalg_desc.h13
-rw-r--r--drivers/crypto/caam/caamrng.c4
-rw-r--r--drivers/crypto/caam/desc.h9
-rw-r--r--drivers/crypto/caam/desc_constr.h8
-rw-r--r--drivers/crypto/cavium/nitrox/nitrox_mbx.c2
-rw-r--r--drivers/crypto/ccp/ccp-dev.c2
-rw-r--r--drivers/crypto/ccp/sev-dev.c17
-rw-r--r--drivers/crypto/ccp/sp-dev.h2
-rw-r--r--drivers/crypto/ccp/sp-pci.c19
-rw-r--r--drivers/crypto/ccp/sp-platform.c17
-rw-r--r--drivers/crypto/ccree/cc_buffer_mgr.c6
-rw-r--r--drivers/crypto/hifn_795x.c7
-rw-r--r--drivers/crypto/hisilicon/qm.c84
-rw-r--r--drivers/crypto/hisilicon/sgl.c5
-rw-r--r--drivers/crypto/intel/iaa/iaa_crypto_main.c2
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_aer.c6
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_isr.c3
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_sriov.c3
-rw-r--r--drivers/crypto/intel/qat/qat_common/adf_vf_isr.c3
-rw-r--r--drivers/crypto/intel/qat/qat_common/qat_uclo.c18
-rw-r--r--drivers/crypto/marvell/cesa/cesa.c7
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c6
-rw-r--r--drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c5
-rw-r--r--drivers/crypto/qce/core.c3
-rw-r--r--drivers/crypto/qce/dma.c6
-rw-r--r--drivers/crypto/rockchip/rk3288_crypto_skcipher.c3
-rw-r--r--drivers/crypto/starfive/jh7110-hash.c6
-rw-r--r--drivers/crypto/ti/Kconfig1
-rw-r--r--drivers/crypto/ti/dthev2-aes.c137
-rw-r--r--drivers/crypto/ti/dthev2-common.h10
-rw-r--r--drivers/crypto/xilinx/xilinx-trng.c39
-rw-r--r--drivers/cxl/acpi.c73
-rw-r--r--drivers/cxl/core/cdat.c4
-rw-r--r--drivers/cxl/core/hdm.c3
-rw-r--r--drivers/cxl/core/pci.c87
-rw-r--r--drivers/cxl/core/port.c1
-rw-r--r--drivers/cxl/core/region.c313
-rw-r--r--drivers/cxl/cxl.h29
-rw-r--r--drivers/cxl/cxlpci.h1
-rw-r--r--drivers/cxl/pci.c2
-rw-r--r--drivers/dax/super.c2
-rw-r--r--drivers/devfreq/devfreq.c2
-rw-r--r--drivers/devfreq/governor_passive.c27
-rw-r--r--drivers/devfreq/governor_performance.c2
-rw-r--r--drivers/devfreq/governor_powersave.c2
-rw-r--r--drivers/devfreq/governor_simpleondemand.c6
-rw-r--r--drivers/devfreq/governor_userspace.c2
-rw-r--r--drivers/devfreq/hisi_uncore_freq.c6
-rw-r--r--drivers/devfreq/tegra30-devfreq.c15
-rw-r--r--drivers/dibs/dibs_main.c8
-rw-r--r--drivers/dma-buf/Makefile2
-rw-r--r--drivers/dma-buf/dma-buf-mapping.c248
-rw-r--r--drivers/dma-buf/dma-buf.c10
-rw-r--r--drivers/dma-buf/dma-fence.c54
-rw-r--r--drivers/dma-buf/heaps/Kconfig10
-rw-r--r--drivers/dma-buf/heaps/cma_heap.c47
-rw-r--r--drivers/dma-buf/heaps/system_heap.c33
-rw-r--r--drivers/dma-buf/sw_sync.c4
-rw-r--r--drivers/dma-buf/sync_debug.c2
-rw-r--r--drivers/dma/ioat/init.c1
-rw-r--r--drivers/dpll/dpll_netlink.c48
-rw-r--r--drivers/dpll/dpll_nl.c1
-rw-r--r--drivers/dpll/dpll_nl.h1
-rw-r--r--drivers/dpll/zl3073x/Makefile3
-rw-r--r--drivers/dpll/zl3073x/core.c243
-rw-r--r--drivers/dpll/zl3073x/core.h184
-rw-r--r--drivers/dpll/zl3073x/dpll.c820
-rw-r--r--drivers/dpll/zl3073x/fw.c6
-rw-r--r--drivers/dpll/zl3073x/out.c157
-rw-r--r--drivers/dpll/zl3073x/out.h93
-rw-r--r--drivers/dpll/zl3073x/prop.c19
-rw-r--r--drivers/dpll/zl3073x/ref.c204
-rw-r--r--drivers/dpll/zl3073x/ref.h134
-rw-r--r--drivers/dpll/zl3073x/synth.c87
-rw-r--r--drivers/dpll/zl3073x/synth.h72
-rw-r--r--drivers/edac/Kconfig20
-rw-r--r--drivers/edac/Makefile3
-rw-r--r--drivers/edac/altera_edac.c22
-rw-r--r--drivers/edac/amd64_edac.c61
-rw-r--r--drivers/edac/amd64_edac.h7
-rw-r--r--drivers/edac/edac_mc_sysfs.c404
-rw-r--r--drivers/edac/ghes_edac.c7
-rw-r--r--drivers/edac/i10nm_base.c3
-rw-r--r--drivers/edac/ie31200_edac.c2
-rw-r--r--drivers/edac/igen6_edac.c2
-rw-r--r--drivers/edac/imh_base.c602
-rw-r--r--drivers/edac/skx_base.c4
-rw-r--r--drivers/edac/skx_common.c33
-rw-r--r--drivers/edac/skx_common.h98
-rw-r--r--drivers/edac/versalnet_edac.c26
-rw-r--r--drivers/firewire/core-card.c27
-rw-r--r--drivers/firewire/core-device.c194
-rw-r--r--drivers/firewire/core-topology.c3
-rw-r--r--drivers/firewire/core-transaction.c90
-rw-r--r--drivers/firewire/core.h5
-rw-r--r--drivers/firewire/init_ohci1394_dma.c10
-rw-r--r--drivers/firewire/ohci.c78
-rw-r--r--drivers/firmware/arm_ffa/driver.c37
-rw-r--r--drivers/firmware/arm_scmi/common.h32
-rw-r--r--drivers/firmware/arm_scmi/driver.c59
-rw-r--r--drivers/firmware/cirrus/cs_dsp.c175
-rw-r--r--drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c1
-rw-r--r--drivers/firmware/efi/cper-arm.c52
-rw-r--r--drivers/firmware/efi/cper.c62
-rw-r--r--drivers/firmware/efi/efi.c3
-rw-r--r--drivers/firmware/efi/libstub/Makefile4
-rw-r--r--drivers/firmware/efi/libstub/efi-stub.c2
-rw-r--r--drivers/firmware/efi/libstub/efistub.h31
-rw-r--r--drivers/firmware/efi/libstub/gop.c137
-rw-r--r--drivers/firmware/efi/libstub/x86-5lvl.c4
-rw-r--r--drivers/firmware/efi/libstub/x86-stub.c104
-rw-r--r--drivers/firmware/efi/memattr.c7
-rw-r--r--drivers/firmware/efi/riscv-runtime.c10
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c17
-rw-r--r--drivers/firmware/efi/stmm/mm_communication.h6
-rw-r--r--drivers/firmware/qcom/qcom_scm.c17
-rw-r--r--drivers/firmware/stratix10-svc.c7
-rw-r--r--drivers/gnss/ubx.c8
-rw-r--r--drivers/gpio/Kconfig35
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/TODO11
-rw-r--r--drivers/gpio/gpio-104-idio-16.c1
-rw-r--r--drivers/gpio/gpio-aggregator.c1
-rw-r--r--drivers/gpio/gpio-aspeed.c7
-rw-r--r--drivers/gpio/gpio-brcmstb.c12
-rw-r--r--drivers/gpio/gpio-bt8xx.c30
-rw-r--r--drivers/gpio/gpio-dwapb.c18
-rw-r--r--drivers/gpio/gpio-elkhartlake.c36
-rw-r--r--drivers/gpio/gpio-fxl6408.c13
-rw-r--r--drivers/gpio/gpio-grgpio.c24
-rw-r--r--drivers/gpio/gpio-htc-egpio.c21
-rw-r--r--drivers/gpio/gpio-idio-16.c5
-rw-r--r--drivers/gpio/gpio-latch.c2
-rw-r--r--drivers/gpio/gpio-ljca.c14
-rw-r--r--drivers/gpio/gpio-loongson-64bit.c11
-rw-r--r--drivers/gpio/gpio-menz127.c26
-rw-r--r--drivers/gpio/gpio-ml-ioh.c12
-rw-r--r--drivers/gpio/gpio-mlxbf2.c8
-rw-r--r--drivers/gpio/gpio-mm-lantiq.c57
-rw-r--r--drivers/gpio/gpio-mmio.c323
-rw-r--r--drivers/gpio/gpio-mpsse.c229
-rw-r--r--drivers/gpio/gpio-msc313.c8
-rw-r--r--drivers/gpio/gpio-mvebu.c3
-rw-r--r--drivers/gpio/gpio-omap.c15
-rw-r--r--drivers/gpio/gpio-pca953x.c13
-rw-r--r--drivers/gpio/gpio-pch.c12
-rw-r--r--drivers/gpio/gpio-pci-idio-16.c1
-rw-r--r--drivers/gpio/gpio-pl061.c17
-rw-r--r--drivers/gpio/gpio-qixis-fpga.c111
-rw-r--r--drivers/gpio/gpio-regmap.c44
-rw-r--r--drivers/gpio/gpio-shared-proxy.c334
-rw-r--r--drivers/gpio/gpio-tb10x.c19
-rw-r--r--drivers/gpio/gpio-tegra186.c174
-rw-r--r--drivers/gpio/gpio-tqmx86.c9
-rw-r--r--drivers/gpio/gpio-uniphier.c9
-rw-r--r--drivers/gpio/gpio-virtuser.c8
-rw-r--r--drivers/gpio/gpio-xgene.c8
-rw-r--r--drivers/gpio/gpio-xilinx.c15
-rw-r--r--drivers/gpio/gpio-zynq.c15
-rw-r--r--drivers/gpio/gpiolib-acpi-core.c33
-rw-r--r--drivers/gpio/gpiolib-cdev.c93
-rw-r--r--drivers/gpio/gpiolib-legacy.c44
-rw-r--r--drivers/gpio/gpiolib-of.c79
-rw-r--r--drivers/gpio/gpiolib-shared.c656
-rw-r--r--drivers/gpio/gpiolib-shared.h71
-rw-r--r--drivers/gpio/gpiolib-swnode.c5
-rw-r--r--drivers/gpio/gpiolib-sysfs.c16
-rw-r--r--drivers/gpio/gpiolib.c225
-rw-r--r--drivers/gpio/gpiolib.h49
-rw-r--r--drivers/gpu/drm/Makefile11
-rw-r--r--drivers/gpu/drm/adp/adp_drv.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/Kconfig24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/Makefile10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/aldebaran.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c19
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c34
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c443
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c244
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.c62
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_display.h7
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c214
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c145
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c72
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c79
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c36
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c58
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_isp.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c69
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c16
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.h1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c388
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h38
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c422
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h30
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c79
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h9
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_securedisplay.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c124
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c147
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c640
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c174
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c343
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h27
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c45
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c34
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c37
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h92
-rw-r--r--drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik_ih.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cz_ih.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c31
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/iceland_ih.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_userqueue.c60
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v11_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v12_0.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/psp_v11_0.c26
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si.c22
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si_ih.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/sid.h40
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc15.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/tonga_ih.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/umc_v12_0.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v1_0.c839
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v1_0.h32
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v2_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v3_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vce_v4_0.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c12
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c12
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_events.c11
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c7
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_migrate.c1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_migrate.h1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process.c12
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_queue.c12
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c22
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.h1
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_topology.c4
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/Makefile3
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c494
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h7
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c768
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c209
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h36
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c47
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c8
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c18
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c10
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c17
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c59
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c39
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/Makefile2
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/bios_parser.c95
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table.c286
-rw-r--r--drivers/gpu/drm/amd/display/dc/bios/command_table.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c87
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c42
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc.c1123
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c2853
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_resource.c129
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_state.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_stream.c67
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h581
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_bios_types.h9
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c132
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h43
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_dp_types.h36
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_spl_translate.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_stream.h11
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_types.h24
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.h64
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c123
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c26
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_abm.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_audio.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c85
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h16
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c14
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c33
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c20
-rw-r--r--drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2/Makefile141
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/Makefile140
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/cmntypes.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/cmntypes.h)18
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.c)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core_structs.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/display_mode_core_structs.h)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_lib_defines.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/display_mode_lib_defines.h)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/display_mode_util.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/display_mode_util.h)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_utils.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_utils.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.c)4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn4_soc_bb.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/bounding_boxes/dcn4_soc_bb.h)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml2_external_lib_deps.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml2_external_lib_deps.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_dchub_registers.h)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_display_cfg_types.h)23
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_policy_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_policy_types.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_soc_parameter_types.h)11
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_types.h)7
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.c)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c)123
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.c)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_shared_types.h)45
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_utils.c)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_utils.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.h)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c)477
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.h)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_interfaces.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_legacy.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_legacy.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_debug.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/inc/dml2_debug.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml21/src/inc/dml2_internal_shared_types.h)76
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_types.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_internal_types.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_internal_types.h)2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.c)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c)3
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_utils.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_utils.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_assert.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml_assert.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_depedencies.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml_depedencies.h)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml_display_rq_dlg_calc.c)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml_display_rq_dlg_calc.h)0
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml_logging.h (renamed from drivers/gpu/drm/amd/display/dc/dml2/dml_logging.h)1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c19
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c39
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn32/dcn32_dpp.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn35/dcn35_dpp.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c43
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c32
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dsc/dsc.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c33
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c53
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h136
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c69
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c147
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c74
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c28
-rw-r--r--drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c90
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c15
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c76
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c144
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h8
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c1412
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h104
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c28
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h1446
-rw-r--r--drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h36
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/core_types.h12
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/cursor_reg_cache.h28
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h121
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h50
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h17
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h7
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h57
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h27
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/opp.h13
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h130
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/link_service.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/inc/resource.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c19
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.c173
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.c14
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_factory.c66
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c64
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c24
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c143
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c16
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c14
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c13
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.h6
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.c13
-rw-r--r--drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h38
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c131
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c1
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c38
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c9
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c9
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c29
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c34
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c35
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c36
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c7
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c13
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c8
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h3
-rw-r--r--drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h2
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c15
-rw-r--r--drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c7
-rw-r--r--drivers/gpu/drm/amd/display/dmub/dmub_srv.h32
-rw-r--r--drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h719
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c1
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c50
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c40
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.h2
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c17
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c33
-rw-r--r--drivers/gpu/drm/amd/display/include/bios_parser_types.h11
-rw-r--r--drivers/gpu/drm/amd/display/include/dpcd_defs.h16
-rw-r--r--drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h1
-rw-r--r--drivers/gpu/drm/amd/display/include/grph_object_id.h7
-rw-r--r--drivers/gpu/drm/amd/display/include/signal_types.h12
-rw-r--r--drivers/gpu/drm/amd/display/modules/freesync/freesync.c11
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c6
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h3
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c13
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c87
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c61
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c2
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c122
-rw-r--r--drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h6
-rw-r--r--drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h148
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_helpers.c33
-rw-r--r--drivers/gpu/drm/amd/display/modules/power/power_helpers.h5
-rw-r--r--drivers/gpu/drm/amd/include/amd_cper.h2
-rw-r--r--drivers/gpu/drm/amd/include/amd_shared.h1
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_d.h5
-rw-r--r--drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_sh_mask.h10
-rw-r--r--drivers/gpu/drm/amd/include/ivsrcid/vcn/irqsrcs_vcn_5_0.h2
-rw-r--r--drivers/gpu/drm/amd/include/kgd_pp_interface.h125
-rw-r--r--drivers/gpu/drm/amd/include/mes_v11_api_def.h3
-rw-r--r--drivers/gpu/drm/amd/include/mes_v12_api_def.h3
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm.c40
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c37
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h5
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c28
-rw-r--r--drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.h557
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c10
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c4
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c101
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h21
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h9
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h20
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h14
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c17
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c26
-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_0_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c249
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c326
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h165
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c16
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h67
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_internal.h1
-rw-r--r--drivers/gpu/drm/amd/ras/Makefile34
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/Makefile33
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c285
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.h54
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c182
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.h27
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c648
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h83
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c94
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.h30
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.c125
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.h30
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.c190
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.h41
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c279
-rw-r--r--drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h110
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/Makefile44
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras.h370
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca.c672
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca.h164
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c379
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.h71
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cmd.c522
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cmd.h426
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_core.c603
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cper.c315
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_cper.h304
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c1339
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h197
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx.c70
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx.h43
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.c426
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.h259
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_log_ring.c317
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_log_ring.h93
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1.c81
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1.h50
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c105
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.h30
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio.c96
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio.h46
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.c123
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.h31
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_process.c322
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_process.h53
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp.c750
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp.h145
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.c46
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.h31
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_ta_if.h231
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.c707
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc.h166
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c511
-rw-r--r--drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h314
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_crtc.c31
-rw-r--r--drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c1
-rw-r--r--drivers/gpu/drm/arm/hdlcd_crtc.c1
-rw-r--r--drivers/gpu/drm/arm/hdlcd_drv.c1
-rw-r--r--drivers/gpu/drm/arm/malidp_drv.c1
-rw-r--r--drivers/gpu/drm/arm/malidp_mw.c1
-rw-r--r--drivers/gpu/drm/arm/malidp_planes.c2
-rw-r--r--drivers/gpu/drm/armada/armada_crtc.c1
-rw-r--r--drivers/gpu/drm/armada/armada_debugfs.c1
-rw-r--r--drivers/gpu/drm/armada/armada_fb.c1
-rw-r--r--drivers/gpu/drm/armada/armada_fbdev.c15
-rw-r--r--drivers/gpu/drm/armada/armada_gem.c1
-rw-r--r--drivers/gpu/drm/armada/armada_overlay.c1
-rw-r--r--drivers/gpu/drm/armada/armada_plane.c8
-rw-r--r--drivers/gpu/drm/ast/Makefile3
-rw-r--r--drivers/gpu/drm/ast/ast_2000.c108
-rw-r--r--drivers/gpu/drm/ast/ast_2100.c92
-rw-r--r--drivers/gpu/drm/ast/ast_2200.c92
-rw-r--r--drivers/gpu/drm/ast/ast_2300.c135
-rw-r--r--drivers/gpu/drm/ast/ast_2400.c100
-rw-r--r--drivers/gpu/drm/ast/ast_2500.c106
-rw-r--r--drivers/gpu/drm/ast/ast_2600.c72
-rw-r--r--drivers/gpu/drm/ast/ast_drv.c69
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h108
-rw-r--r--drivers/gpu/drm/ast/ast_main.c268
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c58
-rw-r--r--drivers/gpu/drm/ast/ast_tables.h60
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c21
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c15
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h3
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c3
-rw-r--r--drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c53
-rw-r--r--drivers/gpu/drm/bridge/imx/Kconfig11
-rw-r--r--drivers/gpu/drm/bridge/imx/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c158
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c65
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c7
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c68
-rw-r--r--drivers/gpu/drm/bridge/sii902x.c20
-rw-r--r--drivers/gpu/drm/bridge/simple-bridge.c10
-rw-r--r--drivers/gpu/drm/bridge/synopsys/Kconfig8
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-dp.c2
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c5
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c235
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h14
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c18
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi86.c112
-rw-r--r--drivers/gpu/drm/ci/gitlab-ci.yml2
-rw-r--r--drivers/gpu/drm/clients/drm_client_setup.c4
-rw-r--r--drivers/gpu/drm/clients/drm_fbdev_client.c37
-rw-r--r--drivers/gpu/drm/clients/drm_log.c43
-rw-r--r--drivers/gpu/drm/display/drm_bridge_connector.c69
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper.c232
-rw-r--r--drivers/gpu/drm/drm_atomic.c225
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c24
-rw-r--r--drivers/gpu/drm/drm_atomic_state_helper.c5
-rw-r--r--drivers/gpu/drm/drm_atomic_uapi.c160
-rw-r--r--drivers/gpu/drm/drm_bridge.c67
-rw-r--r--drivers/gpu/drm/drm_buddy.c395
-rw-r--r--drivers/gpu/drm/drm_client.c198
-rw-r--r--drivers/gpu/drm/drm_client_event.c29
-rw-r--r--drivers/gpu/drm/drm_client_modeset.c44
-rw-r--r--drivers/gpu/drm/drm_client_sysrq.c65
-rw-r--r--drivers/gpu/drm/drm_color_mgmt.c43
-rw-r--r--drivers/gpu/drm/drm_colorop.c599
-rw-r--r--drivers/gpu/drm/drm_connector.c1
-rw-r--r--drivers/gpu/drm/drm_crtc.c35
-rw-r--r--drivers/gpu/drm/drm_crtc_internal.h1
-rw-r--r--drivers/gpu/drm/drm_displayid.c58
-rw-r--r--drivers/gpu/drm/drm_displayid_internal.h2
-rw-r--r--drivers/gpu/drm/drm_drv.c3
-rw-r--r--drivers/gpu/drm/drm_dumb_buffers.c171
-rw-r--r--drivers/gpu/drm/drm_edid.c3
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c152
-rw-r--r--drivers/gpu/drm/drm_fbdev_dma.c25
-rw-r--r--drivers/gpu/drm/drm_fbdev_shmem.c21
-rw-r--r--drivers/gpu/drm/drm_fbdev_ttm.c24
-rw-r--r--drivers/gpu/drm/drm_file.c2
-rw-r--r--drivers/gpu/drm/drm_format_helper.c91
-rw-r--r--drivers/gpu/drm/drm_framebuffer.c2
-rw-r--r--drivers/gpu/drm/drm_gem.c28
-rw-r--r--drivers/gpu/drm/drm_gem_atomic_helper.c10
-rw-r--r--drivers/gpu/drm/drm_gem_dma_helper.c10
-rw-r--r--drivers/gpu/drm/drm_gem_framebuffer_helper.c1
-rw-r--r--drivers/gpu/drm/drm_gem_shmem_helper.c114
-rw-r--r--drivers/gpu/drm/drm_gem_ttm_helper.c1
-rw-r--r--drivers/gpu/drm/drm_gem_vram_helper.c11
-rw-r--r--drivers/gpu/drm/drm_gpusvm.c6
-rw-r--r--drivers/gpu/drm/drm_gpuvm.c191
-rw-r--r--drivers/gpu/drm/drm_internal.h11
-rw-r--r--drivers/gpu/drm/drm_ioctl.c7
-rw-r--r--drivers/gpu/drm/drm_mipi_dbi.c3
-rw-r--r--drivers/gpu/drm/drm_mm.c1
-rw-r--r--drivers/gpu/drm/drm_mode_config.c7
-rw-r--r--drivers/gpu/drm/drm_mode_object.c18
-rw-r--r--drivers/gpu/drm/drm_modeset_helper.c6
-rw-r--r--drivers/gpu/drm/drm_panic.c60
-rw-r--r--drivers/gpu/drm/drm_plane.c63
-rw-r--r--drivers/gpu/drm/drm_prime.c1
-rw-r--r--drivers/gpu/drm/drm_vblank.c180
-rw-r--r--drivers/gpu/drm/drm_vblank_helper.c176
-rw-r--r--drivers/gpu/drm/drm_vblank_work.c2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_buffer.c3
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_drv.c1
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem.c1
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c1
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_gpu.c2
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_hwdb.c32
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_mmu.c2
-rw-r--r--drivers/gpu/drm/exynos/exynos5433_drm_decon.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos7_drm_decon.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c12
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_g2d.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c9
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_ipp.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c3
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c1
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c1
-rw-r--r--drivers/gpu/drm/gma500/backlight.c2
-rw-r--r--drivers/gpu/drm/gma500/cdv_device.c1
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_display.c1
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c1
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_hdmi.c1
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_lvds.c1
-rw-r--r--drivers/gpu/drm/gma500/fbdev.c58
-rw-r--r--drivers/gpu/drm/gma500/gem.c1
-rw-r--r--drivers/gpu/drm/gma500/intel_bios.c1
-rw-r--r--drivers/gpu/drm/gma500/intel_gmbus.c2
-rw-r--r--drivers/gpu/drm/gma500/mid_bios.c1
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_crtc.c1
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi.c1
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c3
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds.c1
-rw-r--r--drivers/gpu/drm/gma500/opregion.c3
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c1
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_display.c1
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c1
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_sdvo.c1
-rw-r--r--drivers/gpu/drm/gma500/psb_irq.c37
-rw-r--r--drivers/gpu/drm/gud/gud_connector.c8
-rw-r--r--drivers/gpu/drm/gud/gud_drv.c45
-rw-r--r--drivers/gpu/drm/gud/gud_pipe.c12
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c1
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c1
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_drv.c1
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_modeset.c12
-rw-r--r--drivers/gpu/drm/i915/Makefile17
-rw-r--r--drivers/gpu/drm/i915/display/g4x_dp.c2
-rw-r--r--drivers/gpu/drm/i915/display/hsw_ips.c63
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_plane.c34
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_plane.h5
-rw-r--r--drivers/gpu/drm/i915/display/i9xx_wm.c9
-rw-r--r--drivers/gpu/drm/i915/display/icl_dsi.c39
-rw-r--r--drivers/gpu/drm/i915/display/intel_acpi.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_alpm.c91
-rw-r--r--drivers/gpu/drm/i915/display/intel_alpm.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_backlight.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_bios.c9
-rw-r--r--drivers/gpu/drm/i915/display/intel_bo.c40
-rw-r--r--drivers/gpu/drm/i915/display/intel_bo.h8
-rw-r--r--drivers/gpu/drm/i915/display/intel_bw.c355
-rw-r--r--drivers/gpu/drm/i915/display/intel_bw.h6
-rw-r--r--drivers/gpu/drm/i915/display/intel_casf.c290
-rw-r--r--drivers/gpu/drm/i915/display/intel_casf.h21
-rw-r--r--drivers/gpu/drm/i915/display/intel_casf_regs.h33
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c512
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.h16
-rw-r--r--drivers/gpu/drm/i915/display/intel_color.c352
-rw-r--r--drivers/gpu/drm/i915/display/intel_color.h8
-rw-r--r--drivers/gpu/drm/i915/display/intel_color_pipeline.c99
-rw-r--r--drivers/gpu/drm/i915/display/intel_color_pipeline.h14
-rw-r--r--drivers/gpu/drm/i915/display/intel_color_regs.h29
-rw-r--r--drivers/gpu/drm/i915/display/intel_colorop.c35
-rw-r--r--drivers/gpu/drm/i915/display/intel_colorop.h15
-rw-r--r--drivers/gpu/drm/i915/display/intel_combo_phy.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_connector.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_crt.c16
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc.c99
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc.h11
-rw-r--r--drivers/gpu/drm/i915/display/intel_crtc_state_dump.c16
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.c32
-rw-r--r--drivers/gpu/drm/i915/display/intel_cursor.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy.c263
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy.h21
-rw-r--r--drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h32
-rw-r--r--drivers/gpu/drm/i915/display/intel_dbuf_bw.c295
-rw-r--r--drivers/gpu/drm/i915/display/intel_dbuf_bw.h37
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi.c77
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c83
-rw-r--r--drivers/gpu/drm/i915/display/intel_ddi_buf_trans.h9
-rw-r--r--drivers/gpu/drm/i915/display/intel_de.h107
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c375
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.h16
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_conversion.c20
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_core.h34
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_debugfs.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_device.c19
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_device.h13
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_driver.c23
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_irq.c129
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_irq.h8
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_jiffies.h43
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_limits.h9
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power.c21
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_map.c26
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_power_well.c58
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_reset.c1
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_rpm.c33
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_types.h82
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_utils.c32
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_utils.h31
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_wa.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_display_wa.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc.c123
-rw-r--r--drivers/gpu/drm/i915/display/intel_dmc_wl.c25
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c263
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.h12
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_aux.c8
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_hdcp.c14
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_link_training.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c49
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpio_phy.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll.c35
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll_mgr.c29
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpll_mgr.h11
-rw-r--r--drivers/gpu/drm/i915/display/intel_dpt.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb.c57
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsb.h4
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_vbt.c37
-rw-r--r--drivers/gpu/drm/i915/display/intel_dvo.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb.c66
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb_bo.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb_bo.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_fb_pin.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbc.c203
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbc.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev.c72
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev_fb.c58
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev_fb.h14
-rw-r--r--drivers/gpu/drm/i915/display/intel_fdi.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_flipq.c14
-rw-r--r--drivers/gpu/drm/i915/display/intel_frontbuffer.c152
-rw-r--r--drivers/gpu/drm/i915/display/intel_frontbuffer.h18
-rw-r--r--drivers/gpu/drm/i915/display/intel_gmbus.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.c51
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp_gsc.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.c20
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdmi.h1
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_hotplug_irq.c5
-rw-r--r--drivers/gpu/drm/i915/display/intel_link_bw.c17
-rw-r--r--drivers/gpu/drm/i915/display/intel_link_bw.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_lspcon.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy.c2327
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy.h47
-rw-r--r--drivers/gpu/drm/i915/display/intel_lt_phy_regs.h90
-rw-r--r--drivers/gpu/drm/i915/display/intel_lvds.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_setup.c14
-rw-r--r--drivers/gpu/drm/i915/display/intel_modeset_verify.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_overlay.c7
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch.c4
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch_display.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_pch_refclk.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_pfit.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_pipe_crc.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.c120
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_plane_initial.c13
-rw-r--r--drivers/gpu/drm/i915/display/intel_pmdemand.c21
-rw-r--r--drivers/gpu/drm/i915/display/intel_pps.c3
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.c452
-rw-r--r--drivers/gpu/drm/i915/display/intel_psr.h5
-rw-r--r--drivers/gpu/drm/i915/display/intel_qp_tables.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_sbi.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_snps_phy.c10
-rw-r--r--drivers/gpu/drm/i915/display/intel_sprite.c12
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc.c23
-rw-r--r--drivers/gpu/drm/i915/display/intel_tc.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_vblank.c16
-rw-r--r--drivers/gpu/drm/i915/display/intel_vblank.h2
-rw-r--r--drivers/gpu/drm/i915/display/intel_vdsc.c26
-rw-r--r--drivers/gpu/drm/i915/display/intel_vdsc.h3
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.c498
-rw-r--r--drivers/gpu/drm/i915/display/intel_vrr.h5
-rw-r--r--drivers/gpu/drm/i915/display/skl_prefill.c157
-rw-r--r--drivers/gpu/drm/i915/display/skl_prefill.h46
-rw-r--r--drivers/gpu/drm/i915/display/skl_scaler.c234
-rw-r--r--drivers/gpu/drm/i915/display/skl_scaler.h17
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c214
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane_regs.h139
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.c381
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.h3
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark_regs.h52
-rw-r--r--drivers/gpu/drm/i915/display/vlv_clock.c88
-rw-r--r--drivers/gpu/drm/i915/display/vlv_clock.h38
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi.c56
-rw-r--r--drivers/gpu/drm/i915/display/vlv_dsi_pll.c8
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_context.c1
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_create.c5
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_mman.c5
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.c17
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object.h1
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.c103
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h54
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_object_types.h2
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_pages.c4
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_phys.c1
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_shmem.c15
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_shrinker.c2
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_stolen.c105
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_stolen.h34
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_tiling.c5
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm.c8
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c1
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_userptr.c2
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_wait.c7
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gemfs.c2
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c2
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c3
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c2
-rw-r--r--drivers/gpu/drm/i915/gt/gen2_engine_cs.c8
-rw-r--r--drivers/gpu/drm/i915/gt/gen8_engine_cs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_breadcrumbs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c3
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_user.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_execlists_submission.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ggtt.c1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_ggtt_gmch.c1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_clock_utils.c4
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_lrc.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_mocs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rc6.c5
-rw-r--r--drivers/gpu/drm/i915/gt/intel_region_lmem.c26
-rw-r--r--drivers/gpu/drm/i915/gt/intel_renderstate.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_rps.c18
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sa_media.c1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sseu.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_timeline.c1
-rw-r--r--drivers/gpu/drm/i915/gt/intel_tlb.h2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_wopcm.c2
-rw-r--r--drivers/gpu/drm/i915/gt/intel_workarounds.c9
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_context.c2
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_execlists.c3
-rw-r--r--drivers/gpu/drm/i915/gt/selftest_migrate.c9
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/aperture_gm.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/cfg_space.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/cmd_parser.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/display.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/dmabuf.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/edid.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/handlers.c1
-rw-r--r--drivers/gpu/drm/i915/gvt/interrupt.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/kvmgt.c291
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio.c7
-rw-r--r--drivers/gpu/drm/i915/gvt/mmio_context.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/scheduler.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/vgpu.c2
-rw-r--r--drivers/gpu/drm/i915/i915_cmd_parser.c1
-rw-r--r--drivers/gpu/drm/i915/i915_config.c2
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c1
-rw-r--r--drivers/gpu/drm/i915/i915_driver.c155
-rw-r--r--drivers/gpu/drm/i915/i915_driver.h2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h23
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c3
-rw-r--r--drivers/gpu/drm/i915/i915_getparam.c2
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c108
-rw-r--r--drivers/gpu/drm/i915/i915_jiffies.h16
-rw-r--r--drivers/gpu/drm/i915/i915_mmio_range.c18
-rw-r--r--drivers/gpu/drm/i915/i915_mmio_range.h19
-rw-r--r--drivers/gpu/drm/i915/i915_module.c1
-rw-r--r--drivers/gpu/drm/i915/i915_perf.c67
-rw-r--r--drivers/gpu/drm/i915/i915_pmu.c6
-rw-r--r--drivers/gpu/drm/i915/i915_query.c2
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h1
-rw-r--r--drivers/gpu/drm/i915/i915_reg_defs.h10
-rw-r--r--drivers/gpu/drm/i915/i915_request.c2
-rw-r--r--drivers/gpu/drm/i915/i915_switcheroo.c2
-rw-r--r--drivers/gpu/drm/i915/i915_sysfs.c2
-rw-r--r--drivers/gpu/drm/i915/i915_ttm_buddy_manager.c4
-rw-r--r--drivers/gpu/drm/i915/i915_utils.c1
-rw-r--r--drivers/gpu/drm/i915/i915_utils.h41
-rw-r--r--drivers/gpu/drm/i915/i915_vgpu.c2
-rw-r--r--drivers/gpu/drm/i915/i915_vma.c26
-rw-r--r--drivers/gpu/drm/i915/intel_clock_gating.c2
-rw-r--r--drivers/gpu/drm/i915/intel_gvt.c2
-rw-r--r--drivers/gpu/drm/i915/intel_memory_region.c1
-rw-r--r--drivers/gpu/drm/i915/intel_pcode.c2
-rw-r--r--drivers/gpu/drm/i915/intel_region_ttm.c2
-rw-r--r--drivers/gpu/drm/i915/intel_runtime_pm.c77
-rw-r--r--drivers/gpu/drm/i915/intel_runtime_pm.h3
-rw-r--r--drivers/gpu/drm/i915/intel_step.c2
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.c16
-rw-r--r--drivers/gpu/drm/i915/intel_uncore.h8
-rw-r--r--drivers/gpu/drm/i915/intel_wakeref.c2
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp.c2
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c2
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_huc.c2
-rw-r--r--drivers/gpu/drm/i915/pxp/intel_pxp_session.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_active.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_gtt.c4
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_request.c2
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_selftest.c1
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_uncore.c4
-rw-r--r--drivers/gpu/drm/i915/selftests/mock_gem_device.c4
-rw-r--r--drivers/gpu/drm/i915/soc/intel_dram.c15
-rw-r--r--drivers/gpu/drm/i915/soc/intel_dram.h1
-rw-r--r--drivers/gpu/drm/i915/soc/intel_gmch.c1
-rw-r--r--drivers/gpu/drm/i915/soc/intel_rom.c7
-rw-r--r--drivers/gpu/drm/i915/soc/intel_rom.h6
-rw-r--r--drivers/gpu/drm/i915/vlv_iosf_sb.c2
-rw-r--r--drivers/gpu/drm/imagination/Kconfig1
-rw-r--r--drivers/gpu/drm/imagination/pvr_ccb.c1
-rw-r--r--drivers/gpu/drm/imagination/pvr_device.c2
-rw-r--r--drivers/gpu/drm/imagination/pvr_device.h8
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw.c1
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_meta.c2
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_trace.c1
-rw-r--r--drivers/gpu/drm/imagination/pvr_power.c1
-rw-r--r--drivers/gpu/drm/imagination/pvr_vm.c1
-rw-r--r--drivers/gpu/drm/imx/dc/dc-ed.c8
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fg.c4
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fu.c10
-rw-r--r--drivers/gpu/drm/imx/dc/dc-fu.h4
-rw-r--r--drivers/gpu/drm/imx/dc/dc-lb.c28
-rw-r--r--drivers/gpu/drm/imx/dc/dc-plane.c2
-rw-r--r--drivers/gpu/drm/imx/dcss/dcss-plane.c5
-rw-r--r--drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c1
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-drm-core.c31
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-ldb.c1
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-tve.c18
-rw-r--r--drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c4
-rw-r--r--drivers/gpu/drm/imx/ipuv3/parallel-display.c23
-rw-r--r--drivers/gpu/drm/imx/lcdc/imx-lcdc.c1
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-drm-drv.c13
-rw-r--r--drivers/gpu/drm/ingenic/ingenic-ipu.c4
-rw-r--r--drivers/gpu/drm/kmb/kmb_drv.c1
-rw-r--r--drivers/gpu/drm/kmb/kmb_plane.c4
-rw-r--r--drivers/gpu/drm/lima/lima_sched.c2
-rw-r--r--drivers/gpu/drm/logicvc/logicvc_layer.c4
-rw-r--r--drivers/gpu/drm/loongson/lsdc_benchmark.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_crtc.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_debugfs.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_drv.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_gem.c32
-rw-r--r--drivers/gpu/drm/loongson/lsdc_i2c.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_irq.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_output_7a1000.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_output_7a2000.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_pixpll.c1
-rw-r--r--drivers/gpu/drm/loongson/lsdc_plane.c3
-rw-r--r--drivers/gpu/drm/loongson/lsdc_ttm.c4
-rw-r--r--drivers/gpu/drm/mcde/mcde_clk_div.c13
-rw-r--r--drivers/gpu/drm/mcde/mcde_display.c1
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig23
-rw-r--r--drivers/gpu/drm/mediatek/Makefile3
-rw-r--r--drivers/gpu/drm/mediatek/mtk_crtc.c8
-rw-r--r--drivers/gpu/drm/mediatek/mtk_ddp_comp.c33
-rw-r--r--drivers/gpu/drm/mediatek/mtk_ddp_comp.h2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ccorr.c23
-rw-r--r--drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c12
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dp.c1
-rw-r--r--drivers/gpu/drm/mediatek/mtk_drm_drv.c14
-rw-r--r--drivers/gpu/drm/mediatek/mtk_gem.c1
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi.c539
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.c456
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_common.h198
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c396
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h263
-rw-r--r--drivers/gpu/drm/mediatek/mtk_hdmi_v2.c1521
-rw-r--r--drivers/gpu/drm/mediatek/mtk_plane.c28
-rw-r--r--drivers/gpu/drm/meson/meson_overlay.c1
-rw-r--r--drivers/gpu/drm/meson/meson_plane.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_drv.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh3.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200eh5.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200er.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200ev.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200ew3.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200se.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200wb.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_vga.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_vga_bmc.c1
-rw-r--r--drivers/gpu/drm/msm/Makefile2
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx_catalog.c7
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx_gpu.c52
-rw-r--r--drivers/gpu/drm/msm/adreno/a2xx_gpu.h2
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_catalog.c13
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.c52
-rw-r--r--drivers/gpu/drm/msm/adreno/a3xx_gpu.h2
-rw-r--r--drivers/gpu/drm/msm/adreno/a4xx_catalog.c7
-rw-r--r--drivers/gpu/drm/msm/adreno/a4xx_gpu.c54
-rw-r--r--drivers/gpu/drm/msm/adreno/a4xx_gpu.h2
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_catalog.c17
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c61
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.h1
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_catalog.c385
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gmu.c335
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gmu.h25
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu.c438
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu.h31
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c5
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h74
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_hfi.c74
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_hfi.h17
-rw-r--r--drivers/gpu/drm/msm/adreno/a8xx_gpu.c1201
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_device.c4
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gen7_0_0_snapshot.h420
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gen7_2_0_snapshot.h332
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gen7_9_0_snapshot.h470
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c19
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h56
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h541
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c118
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h8
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c47
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h2
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c5
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h3
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h6
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h8
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c1
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c442
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h12
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c8
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h10
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c3
-rw-r--r--drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c3
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c3
-rw-r--r--drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c7
-rw-r--r--drivers/gpu/drm/msm/disp/msm_disp_snapshot.h13
-rw-r--r--drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c3
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.c10
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.c9
-rw-r--r--drivers/gpu/drm/msm/dp/dp_link.c117
-rw-r--r--drivers/gpu/drm/msm/dp/dp_link.h5
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.c78
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.h3
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy.h1
-rw-r--r--drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c18
-rw-r--r--drivers/gpu/drm/msm/msm_fbdev.c11
-rw-r--r--drivers/gpu/drm/msm/msm_gem.c40
-rw-r--r--drivers/gpu/drm/msm/msm_gem_submit.c9
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c36
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c24
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h11
-rw-r--r--drivers/gpu/drm/msm/msm_iommu.c5
-rw-r--r--drivers/gpu/drm/msm/msm_mdss.c2
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx.xml2199
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml2
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml283
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a7xx_enums.xml7
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml121
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/a8xx_enums.xml299
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/adreno_common.xml12
-rw-r--r--drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml361
-rw-r--r--drivers/gpu/drm/msm/registers/gen_header.py19
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_kms.c1
-rw-r--r--drivers/gpu/drm/mxsfb/mxsfb_kms.c1
-rw-r--r--drivers/gpu/drm/nouveau/Kconfig1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c4
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.h1
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndw.c24
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c33
-rw-r--r--drivers/gpu/drm/nouveau/include/nvfw/hs.h4
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h2
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h5
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_platform.c20
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sched.c14
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_uvmm.c102
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_uvmm.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/fw.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c320
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c185
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h18
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb100.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb202.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gh100.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c69
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c4
-rw-r--r--drivers/gpu/drm/nova/Kconfig2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_crtc.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_debugfs.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_dmm_tiler.c2
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_encoder.c4
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fb.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_fbdev.c12
-rw-r--r--drivers/gpu/drm/omapdrm/omap_gem.c16
-rw-r--r--drivers/gpu/drm/omapdrm/omap_irq.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_overlay.c1
-rw-r--r--drivers/gpu/drm/omapdrm/omap_plane.c3
-rw-r--r--drivers/gpu/drm/panel/Kconfig59
-rw-r--r--drivers/gpu/drm/panel/Makefile4
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c10
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9881c.c1327
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9882t.c69
-rw-r--r--drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c21
-rw-r--r--drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c2
-rw-r--r--drivers/gpu/drm/panel/panel-lg-ld070wx3.c184
-rw-r--r--drivers/gpu/drm/panel/panel-newvision-nv3052c.c408
-rw-r--r--drivers/gpu/drm/panel/panel-ronbo-rb070d30.c8
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c385
-rw-r--r--drivers/gpu/drm/panel/panel-samsung-sofef00.c105
-rw-r--r--drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c225
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c127
-rw-r--r--drivers/gpu/drm/panel/panel-sitronix-st7789v.c7
-rw-r--r--drivers/gpu/drm/panel/panel-synaptics-tddi.c277
-rw-r--r--drivers/gpu/drm/panel/panel-visionox-rm69299.c71
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_devfreq.c6
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c68
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.h24
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c243
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_dump.c8
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.c9
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c4
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gpu.c66
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.c336
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.h38
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.c115
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.h3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_perfcnt.c26
-rw-r--r--drivers/gpu/drm/panthor/Makefile1
-rw-r--r--drivers/gpu/drm/panthor/panthor_devfreq.c64
-rw-r--r--drivers/gpu/drm/panthor/panthor_devfreq.h2
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.c43
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.h25
-rw-r--r--drivers/gpu/drm/panthor/panthor_drv.c13
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c134
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.h32
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c39
-rw-r--r--drivers/gpu/drm/panthor/panthor_gpu.c38
-rw-r--r--drivers/gpu/drm/panthor/panthor_gpu.h1
-rw-r--r--drivers/gpu/drm/panthor/panthor_heap.c1
-rw-r--r--drivers/gpu/drm/panthor/panthor_hw.c109
-rw-r--r--drivers/gpu/drm/panthor/panthor_hw.h47
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c156
-rw-r--r--drivers/gpu/drm/panthor/panthor_pwr.c549
-rw-r--r--drivers/gpu/drm/panthor/panthor_pwr.h23
-rw-r--r--drivers/gpu/drm/panthor/panthor_regs.h83
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.c364
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.h3
-rw-r--r--drivers/gpu/drm/pl111/pl111_display.c14
-rw-r--r--drivers/gpu/drm/qxl/qxl_cmd.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_debugfs.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_display.c30
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_gem.c3
-rw-r--r--drivers/gpu/drm/qxl/qxl_image.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_ioctl.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_irq.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_kms.c1
-rw-r--r--drivers/gpu/drm/qxl/qxl_release.c2
-rw-r--r--drivers/gpu/drm/qxl/qxl_ttm.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon.h1
-rw-r--r--drivers/gpu/drm/radeon/radeon_acpi.c1
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c20
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c113
-rw-r--r--drivers/gpu/drm/radeon/radeon_fbdev.c17
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c6
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c3
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c1
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c7
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c50
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h387
-rw-r--r--drivers/gpu/drm/renesas/rz-du/Kconfig2
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c1
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c42
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-core.c1
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-reg.c2
-rw-r--r--drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c21
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c2
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c195
-rw-r--r--drivers/gpu/drm/rockchip/inno_hdmi.c1
-rw-r--r--drivers/gpu/drm/rockchip/rk3066_hdmi.c1
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_drv.c4
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c13
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop.c7
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_vop2.c142
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_lvds.c1
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_rgb.c1
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop2_reg.c49
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_vop_reg.c1
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c40
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c20
-rw-r--r--drivers/gpu/drm/scheduler/tests/sched_tests.h3
-rw-r--r--drivers/gpu/drm/sitronix/st7571-i2c.c3
-rw-r--r--drivers/gpu/drm/sitronix/st7586.c1
-rw-r--r--drivers/gpu/drm/sitronix/st7735r.c1
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c87
-rw-r--r--drivers/gpu/drm/sti/sti_cursor.c1
-rw-r--r--drivers/gpu/drm/sti/sti_drv.c19
-rw-r--r--drivers/gpu/drm/sti/sti_gdp.c1
-rw-r--r--drivers/gpu/drm/sti/sti_hda.c5
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi.c2
-rw-r--r--drivers/gpu/drm/sti/sti_hqvdp.c1
-rw-r--r--drivers/gpu/drm/sti/sti_plane.c1
-rw-r--r--drivers/gpu/drm/sti/sti_vtg.c7
-rw-r--r--drivers/gpu/drm/stm/drv.c1
-rw-r--r--drivers/gpu/drm/stm/dw_mipi_dsi-stm.c14
-rw-r--r--drivers/gpu/drm/stm/ltdc.c1
-rw-r--r--drivers/gpu/drm/stm/lvds.c12
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.c1
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_drv.c1
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_frontend.c1
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c12
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c18
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_csc.c113
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_csc.h16
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.c218
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_mixer.h65
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_layer.c187
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_layer.h7
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_scaler.c44
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_ui_scaler.h4
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_layer.c248
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_layer.h7
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_scaler.c51
-rw-r--r--drivers/gpu/drm/sun4i/sun8i_vi_scaler.h6
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_helper.h34
-rw-r--r--drivers/gpu/drm/sysfb/drm_sysfb_modeset.c153
-rw-r--r--drivers/gpu/drm/sysfb/efidrm.c1
-rw-r--r--drivers/gpu/drm/sysfb/ofdrm.c1
-rw-r--r--drivers/gpu/drm/sysfb/simpledrm.c4
-rw-r--r--drivers/gpu/drm/sysfb/vesadrm.c4
-rw-r--r--drivers/gpu/drm/tegra/Makefile1
-rw-r--r--drivers/gpu/drm/tegra/dc.c4
-rw-r--r--drivers/gpu/drm/tegra/drm.c3
-rw-r--r--drivers/gpu/drm/tegra/drm.h1
-rw-r--r--drivers/gpu/drm/tegra/dsi.c65
-rw-r--r--drivers/gpu/drm/tegra/fb.c1
-rw-r--r--drivers/gpu/drm/tegra/fbdev.c11
-rw-r--r--drivers/gpu/drm/tegra/gem.c8
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c5
-rw-r--r--drivers/gpu/drm/tegra/hub.c1
-rw-r--r--drivers/gpu/drm/tegra/nvjpg.c330
-rw-r--r--drivers/gpu/drm/tegra/sor.c5
-rw-r--r--drivers/gpu/drm/tegra/uapi.c7
-rw-r--r--drivers/gpu/drm/tests/.kunitconfig2
-rw-r--r--drivers/gpu/drm/tests/Makefile3
-rw-r--r--drivers/gpu/drm/tests/drm_buddy_test.c105
-rw-r--r--drivers/gpu/drm/tests/drm_fixp_test.c71
-rw-r--r--drivers/gpu/drm/tests/drm_mm_test.c1
-rw-r--r--drivers/gpu/drm/tidss/tidss_crtc.c42
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc.c126
-rw-r--r--drivers/gpu/drm/tidss/tidss_dispc.h6
-rw-r--r--drivers/gpu/drm/tidss/tidss_drv.c16
-rw-r--r--drivers/gpu/drm/tidss/tidss_drv.h2
-rw-r--r--drivers/gpu/drm/tidss/tidss_kms.c4
-rw-r--r--drivers/gpu/drm/tidss/tidss_oldi.c22
-rw-r--r--drivers/gpu/drm/tidss/tidss_plane.c8
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_crtc.c9
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_plane.c3
-rw-r--r--drivers/gpu/drm/tiny/Kconfig1
-rw-r--r--drivers/gpu/drm/tiny/bochs.c11
-rw-r--r--drivers/gpu/drm/tiny/cirrus-qemu.c12
-rw-r--r--drivers/gpu/drm/tiny/gm12u320.c1
-rw-r--r--drivers/gpu/drm/tiny/hx8357d.c1
-rw-r--r--drivers/gpu/drm/tiny/ili9163.c1
-rw-r--r--drivers/gpu/drm/tiny/ili9225.c1
-rw-r--r--drivers/gpu/drm/tiny/ili9341.c1
-rw-r--r--drivers/gpu/drm/tiny/ili9486.c1
-rw-r--r--drivers/gpu/drm/tiny/mi0283qt.c1
-rw-r--r--drivers/gpu/drm/tiny/panel-mipi-dbi.c1
-rw-r--r--drivers/gpu/drm/tiny/pixpaper.c1
-rw-r--r--drivers/gpu/drm/tiny/repaper.c1
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_bo_test.c28
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c73
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_device_test.c33
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c22
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.h7
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_mock_manager.c1
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_pool_test.c24
-rw-r--r--drivers/gpu/drm/ttm/tests/ttm_resource_test.c5
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c67
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_internal.h2
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c38
-rw-r--r--drivers/gpu/drm/ttm/ttm_device.c9
-rw-r--r--drivers/gpu/drm/ttm/ttm_module.c3
-rw-r--r--drivers/gpu/drm/ttm/ttm_pool.c45
-rw-r--r--drivers/gpu/drm/ttm/ttm_pool_internal.h25
-rw-r--r--drivers/gpu/drm/ttm/ttm_resource.c37
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c11
-rw-r--r--drivers/gpu/drm/tve200/tve200_display.c1
-rw-r--r--drivers/gpu/drm/udl/udl_edid.c1
-rw-r--r--drivers/gpu/drm/v3d/v3d_bo.c2
-rw-r--r--drivers/gpu/drm/v3d/v3d_debugfs.c1
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.c1
-rw-r--r--drivers/gpu/drm/v3d/v3d_gem.c1
-rw-r--r--drivers/gpu/drm/v3d/v3d_gemfs.c2
-rw-r--r--drivers/gpu/drm/v3d/v3d_irq.c2
-rw-r--r--drivers/gpu/drm/v3d/v3d_sched.c1
-rw-r--r--drivers/gpu/drm/v3d/v3d_submit.c1
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_irq.c1
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_main.c1
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_mode.c9
-rw-r--r--drivers/gpu/drm/vboxvideo/vbox_ttm.c1
-rw-r--r--drivers/gpu/drm/vc4/Kconfig1
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_debugfs.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_dpi.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_dsi.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c138
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.h1
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_irq.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_kms.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_perfmon.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_plane.c7
-rw-r--r--drivers/gpu/drm/vc4/vc4_render_cl.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_txp.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_v3d.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate_shaders.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_vec.c1
-rw-r--r--drivers/gpu/drm/vgem/vgem_fence.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_debugfs.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_display.c37
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_kms.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_object.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_plane.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_vq.c1
-rw-r--r--drivers/gpu/drm/vkms/Kconfig1
-rw-r--r--drivers/gpu/drm/vkms/Makefile5
-rw-r--r--drivers/gpu/drm/vkms/tests/Makefile3
-rw-r--r--drivers/gpu/drm/vkms/tests/vkms_color_test.c414
-rw-r--r--drivers/gpu/drm/vkms/tests/vkms_config_test.c71
-rw-r--r--drivers/gpu/drm/vkms/vkms_colorop.c120
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.c136
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.h28
-rw-r--r--drivers/gpu/drm/vkms/vkms_config.c15
-rw-r--r--drivers/gpu/drm/vkms/vkms_config.h54
-rw-r--r--drivers/gpu/drm/vkms/vkms_configfs.c843
-rw-r--r--drivers/gpu/drm/vkms/vkms_configfs.h8
-rw-r--r--drivers/gpu/drm/vkms/vkms_connector.c35
-rw-r--r--drivers/gpu/drm/vkms/vkms_connector.h9
-rw-r--r--drivers/gpu/drm/vkms/vkms_crtc.c88
-rw-r--r--drivers/gpu/drm/vkms/vkms_drv.c27
-rw-r--r--drivers/gpu/drm/vkms/vkms_drv.h34
-rw-r--r--drivers/gpu/drm/vkms/vkms_luts.c811
-rw-r--r--drivers/gpu/drm/vkms/vkms_luts.h12
-rw-r--r--drivers/gpu/drm/vkms/vkms_output.c7
-rw-r--r--drivers/gpu/drm/vkms/vkms_plane.c10
-rw-r--r--drivers/gpu/drm/vkms/vkms_writeback.c1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c16
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h1
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c5
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_gem.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c3
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c12
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_surface.c21
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c6
-rw-r--r--drivers/gpu/drm/xe/Kconfig1
-rw-r--r--drivers/gpu/drm/xe/Kconfig.debug16
-rw-r--r--drivers/gpu/drm/xe/Makefile26
-rw-r--r--drivers/gpu/drm/xe/abi/guc_actions_abi.h8
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h4
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h105
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h4
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_scheduler_types.h13
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h9
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h2
-rw-r--r--drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h31
-rw-r--r--drivers/gpu/drm/xe/display/ext/i915_utils.c27
-rw-r--r--drivers/gpu/drm/xe/display/intel_bo.c60
-rw-r--r--drivers/gpu/drm/xe/display/intel_fb_bo.c3
-rw-r--r--drivers/gpu/drm/xe/display/intel_fbdev_fb.c70
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.c23
-rw-r--r--drivers/gpu/drm/xe/display/xe_display.h4
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_rpm.c61
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_rpm.h11
-rw-r--r--drivers/gpu/drm/xe/display/xe_display_wa.c3
-rw-r--r--drivers/gpu/drm/xe/display/xe_panic.c50
-rw-r--r--drivers/gpu/drm/xe/display/xe_plane_initial.c4
-rw-r--r--drivers/gpu/drm/xe/display/xe_stolen.c123
-rw-r--r--drivers/gpu/drm/xe/instructions/xe_gpu_commands.h6
-rw-r--r--drivers/gpu/drm/xe/regs/xe_engine_regs.h4
-rw-r--r--drivers/gpu/drm/xe/regs/xe_gt_regs.h31
-rw-r--r--drivers/gpu/drm/xe/regs/xe_i2c_regs.h3
-rw-r--r--drivers/gpu/drm/xe/regs/xe_irq_regs.h8
-rw-r--r--drivers/gpu/drm/xe/regs/xe_pmt.h1
-rw-r--r--drivers/gpu/drm/xe/regs/xe_regs.h2
-rw-r--r--drivers/gpu/drm/xe/tests/xe_dma_buf.c17
-rw-r--r--drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_config_kunit.c208
-rw-r--r--drivers/gpu/drm/xe/tests/xe_mocs.c2
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci.c6
-rw-r--r--drivers/gpu/drm/xe/tests/xe_pci_test.c16
-rw-r--r--drivers/gpu/drm/xe/tests/xe_rtp_test.c6
-rw-r--r--drivers/gpu/drm/xe/xe_bo.c117
-rw-r--r--drivers/gpu/drm/xe/xe_bo.h4
-rw-r--r--drivers/gpu/drm/xe/xe_bo_doc.h8
-rw-r--r--drivers/gpu/drm/xe/xe_bo_evict.c9
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.c283
-rw-r--r--drivers/gpu/drm/xe/xe_configfs.h8
-rw-r--r--drivers/gpu/drm/xe/xe_debugfs.c16
-rw-r--r--drivers/gpu/drm/xe/xe_devcoredump.c4
-rw-r--r--drivers/gpu/drm/xe/xe_device.c95
-rw-r--r--drivers/gpu/drm/xe/xe_device_sysfs.c10
-rw-r--r--drivers/gpu/drm/xe/xe_device_types.h55
-rw-r--r--drivers/gpu/drm/xe/xe_device_wa_oob.rules3
-rw-r--r--drivers/gpu/drm/xe/xe_dma_buf.c41
-rw-r--r--drivers/gpu/drm/xe/xe_eu_stall.c36
-rw-r--r--drivers/gpu/drm/xe/xe_exec.c31
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.c240
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue.h28
-rw-r--r--drivers/gpu/drm/xe/xe_exec_queue_types.h20
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c2
-rw-r--r--drivers/gpu/drm/xe/xe_force_wake_types.h26
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.c177
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt.h3
-rw-r--r--drivers/gpu/drm/xe/xe_ggtt_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.c27
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.h30
-rw-r--r--drivers/gpu/drm/xe/xe_gsc.c4
-rw-r--r--drivers/gpu/drm/xe/xe_gt.c99
-rw-r--r--drivers/gpu/drm/xe/xe_gt.h19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_clock.c26
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.c159
-rw-r--r--drivers/gpu/drm/xe/xe_gt_debugfs.h1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_freq.c37
-rw-r--r--drivers/gpu/drm/xe/xe_gt_mcr.c80
-rw-r--r--drivers/gpu/drm/xe/xe_gt_pagefault.c679
-rw-r--r--drivers/gpu/drm/xe/xe_gt_pagefault.h19
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf.c36
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c349
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h16
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c750
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h12
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h36
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c461
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h1
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c1022
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.h48
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_migration_types.h34
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c21
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h5
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_printk.h7
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.c465
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf.h11
-rw-r--r--drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h34
-rw-r--r--drivers/gpu/drm/xe/xe_gt_throttle.c355
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.c24
-rw-r--r--drivers/gpu/drm/xe/xe_gt_topology.h4
-rw-r--r--drivers/gpu/drm/xe/xe_gt_types.h79
-rw-r--r--drivers/gpu/drm/xe/xe_guard.h119
-rw-r--r--drivers/gpu/drm/xe/xe_guc.c328
-rw-r--r--drivers/gpu/drm/xe/xe_guc.h1
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads.c8
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ads_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_buf.c57
-rw-r--r--drivers/gpu/drm/xe/xe_guc_buf.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_capture.c29
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.c395
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct.h13
-rw-r--r--drivers/gpu/drm/xe/xe_guc_ct_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_exec_queue_types.h15
-rw-r--r--drivers/gpu/drm/xe/xe_guc_fwif.h1
-rw-r--r--drivers/gpu/drm/xe/xe_guc_log_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pagefault.c95
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pagefault.h15
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc.c147
-rw-r--r--drivers/gpu/drm/xe/xe_guc_pc_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay.c17
-rw-r--r--drivers/gpu/drm/xe/xe_guc_relay_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.c619
-rw-r--r--drivers/gpu/drm/xe/xe_guc_submit.h7
-rw-r--r--drivers/gpu/drm/xe/xe_guc_tlb_inval.c2
-rw-r--r--drivers/gpu/drm/xe/xe_heci_gsc.c2
-rw-r--r--drivers/gpu/drm/xe/xe_huc.c10
-rw-r--r--drivers/gpu/drm/xe/xe_hw_engine.c62
-rw-r--r--drivers/gpu/drm/xe/xe_hwmon.c8
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.c28
-rw-r--r--drivers/gpu/drm/xe/xe_i2c.h4
-rw-r--r--drivers/gpu/drm/xe/xe_irq.c156
-rw-r--r--drivers/gpu/drm/xe/xe_lmtt.c11
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.c20
-rw-r--r--drivers/gpu/drm/xe/xe_lrc.h16
-rw-r--r--drivers/gpu/drm/xe/xe_map.h22
-rw-r--r--drivers/gpu/drm/xe/xe_memirq.c57
-rw-r--r--drivers/gpu/drm/xe/xe_memirq.h2
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.c369
-rw-r--r--drivers/gpu/drm/xe/xe_migrate.h16
-rw-r--r--drivers/gpu/drm/xe/xe_migrate_doc.h2
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.c29
-rw-r--r--drivers/gpu/drm/xe/xe_mmio.h4
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.c42
-rw-r--r--drivers/gpu/drm/xe/xe_mocs.h8
-rw-r--r--drivers/gpu/drm/xe/xe_oa.c65
-rw-r--r--drivers/gpu/drm/xe/xe_oa_types.h11
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault.c444
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault.h19
-rw-r--r--drivers/gpu/drm/xe/xe_pagefault_types.h136
-rw-r--r--drivers/gpu/drm/xe/xe_pat.c145
-rw-r--r--drivers/gpu/drm/xe/xe_pat.h12
-rw-r--r--drivers/gpu/drm/xe/xe_pci.c302
-rw-r--r--drivers/gpu/drm/xe/xe_pci.h3
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.c115
-rw-r--r--drivers/gpu/drm/xe/xe_pci_sriov.h1
-rw-r--r--drivers/gpu/drm/xe/xe_pci_types.h11
-rw-r--r--drivers/gpu/drm/xe/xe_pcode.c40
-rw-r--r--drivers/gpu/drm/xe/xe_pcode_api.h6
-rw-r--r--drivers/gpu/drm/xe/xe_platform_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_pm.c87
-rw-r--r--drivers/gpu/drm/xe/xe_pm.h19
-rw-r--r--drivers/gpu/drm/xe/xe_pmu.c11
-rw-r--r--drivers/gpu/drm/xe/xe_preempt_fence.c11
-rw-r--r--drivers/gpu/drm/xe/xe_preempt_fence_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_psmi.c4
-rw-r--r--drivers/gpu/drm/xe/xe_pt.c114
-rw-r--r--drivers/gpu/drm/xe/xe_query.c2
-rw-r--r--drivers/gpu/drm/xe/xe_range_fence.h4
-rw-r--r--drivers/gpu/drm/xe/xe_reg_whitelist.c10
-rw-r--r--drivers/gpu/drm/xe/xe_ring_ops.c23
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.c31
-rw-r--r--drivers/gpu/drm/xe/xe_rtp.h30
-rw-r--r--drivers/gpu/drm/xe/xe_rtp_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_sa.c21
-rw-r--r--drivers/gpu/drm/xe/xe_sa.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.c25
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job.h13
-rw-r--r--drivers/gpu/drm/xe/xe_sched_job_types.h11
-rw-r--r--drivers/gpu/drm/xe/xe_sriov.c2
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_packet.c520
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_packet.h30
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_packet_types.h75
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf.c175
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf.h22
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_control.c279
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_control.h22
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c395
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h18
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_helpers.h27
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_migration.c365
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_migration.h30
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_migration_types.h37
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision.c438
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision.h45
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h36
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c647
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_sysfs.h16
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_pf_types.h25
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_printk.h12
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf.c286
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf.h2
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.c74
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_ccs.h1
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vf_types.h10
-rw-r--r--drivers/gpu/drm/xe/xe_sriov_vfio.c80
-rw-r--r--drivers/gpu/drm/xe/xe_svm.c10
-rw-r--r--drivers/gpu/drm/xe/xe_sync.c91
-rw-r--r--drivers/gpu/drm/xe/xe_sync.h3
-rw-r--r--drivers/gpu/drm/xe/xe_sync_types.h3
-rw-r--r--drivers/gpu/drm/xe/xe_tile.c14
-rw-r--r--drivers/gpu/drm/xe/xe_tile_debugfs.c19
-rw-r--r--drivers/gpu/drm/xe/xe_tile_debugfs.h3
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c253
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h15
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_printk.h33
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf.c112
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf.h9
-rw-r--r--drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h23
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval.h2
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.c31
-rw-r--r--drivers/gpu/drm/xe/xe_tlb_inval_job.h5
-rw-r--r--drivers/gpu/drm/xe/xe_trace.h23
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c8
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_sys_mgr.c6
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr.c6
-rw-r--r--drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h4
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.c27
-rw-r--r--drivers/gpu/drm/xe/xe_tuning.h2
-rw-r--r--drivers/gpu/drm/xe/xe_uc_fw_types.h6
-rw-r--r--drivers/gpu/drm/xe/xe_uc_types.h2
-rw-r--r--drivers/gpu/drm/xe/xe_userptr.c4
-rw-r--r--drivers/gpu/drm/xe/xe_validation.h14
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c244
-rw-r--r--drivers/gpu/drm/xe/xe_vm_doc.h8
-rw-r--r--drivers/gpu/drm/xe/xe_vm_types.h20
-rw-r--r--drivers/gpu/drm/xe/xe_vram.c96
-rw-r--r--drivers/gpu/drm/xe/xe_wa.c38
-rw-r--r--drivers/gpu/drm/xe/xe_wa.h2
-rw-r--r--drivers/gpu/drm/xe/xe_wa_oob.rules17
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front.c1
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front_gem.c1
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front_kms.c1
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_kms.c7
-rw-r--r--drivers/gpu/host1x/bus.c12
-rw-r--r--drivers/gpu/host1x/dev.c20
-rw-r--r--drivers/gpu/host1x/dev.h3
-rw-r--r--drivers/gpu/host1x/hw/channel_hw.c106
-rw-r--r--drivers/gpu/host1x/hw/intr_hw.c56
-rw-r--r--drivers/gpu/host1x/syncpt.c4
-rw-r--r--drivers/gpu/nova-core/bitfield.rs330
-rw-r--r--drivers/gpu/nova-core/dma.rs36
-rw-r--r--drivers/gpu/nova-core/driver.rs27
-rw-r--r--drivers/gpu/nova-core/falcon.rs281
-rw-r--r--drivers/gpu/nova-core/falcon/gsp.rs29
-rw-r--r--drivers/gpu/nova-core/falcon/hal.rs14
-rw-r--r--drivers/gpu/nova-core/falcon/hal/ga102.rs44
-rw-r--r--drivers/gpu/nova-core/falcon/sec2.rs10
-rw-r--r--drivers/gpu/nova-core/fb.rs102
-rw-r--r--drivers/gpu/nova-core/fb/hal.rs6
-rw-r--r--drivers/gpu/nova-core/fb/hal/ga100.rs16
-rw-r--r--drivers/gpu/nova-core/fb/hal/ga102.rs8
-rw-r--r--drivers/gpu/nova-core/fb/hal/tu102.rs25
-rw-r--r--drivers/gpu/nova-core/firmware.rs33
-rw-r--r--drivers/gpu/nova-core/firmware/booter.rs82
-rw-r--r--drivers/gpu/nova-core/firmware/fwsec.rs188
-rw-r--r--drivers/gpu/nova-core/firmware/gsp.rs53
-rw-r--r--drivers/gpu/nova-core/firmware/riscv.rs34
-rw-r--r--drivers/gpu/nova-core/gfw.rs48
-rw-r--r--drivers/gpu/nova-core/gpu.rs115
-rw-r--r--drivers/gpu/nova-core/gsp.rs151
-rw-r--r--drivers/gpu/nova-core/gsp/boot.rs153
-rw-r--r--drivers/gpu/nova-core/gsp/cmdq.rs679
-rw-r--r--drivers/gpu/nova-core/gsp/commands.rs227
-rw-r--r--drivers/gpu/nova-core/gsp/fw.rs923
-rw-r--r--drivers/gpu/nova-core/gsp/fw/commands.rs128
-rw-r--r--drivers/gpu/nova-core/gsp/fw/r570_144.rs6
-rw-r--r--drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs950
-rw-r--r--drivers/gpu/nova-core/gsp/sequencer.rs407
-rw-r--r--drivers/gpu/nova-core/nova_core.rs5
-rw-r--r--drivers/gpu/nova-core/num.rs217
-rw-r--r--drivers/gpu/nova-core/regs.rs91
-rw-r--r--drivers/gpu/nova-core/regs/macros.rs287
-rw-r--r--drivers/gpu/nova-core/sbuffer.rs227
-rw-r--r--drivers/gpu/nova-core/util.rs33
-rw-r--r--drivers/gpu/nova-core/vbios.rs423
-rw-r--r--drivers/hid/Kconfig3
-rw-r--r--drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c2
-rw-r--r--drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c563
-rw-r--r--drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c29
-rw-r--r--drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c6
-rw-r--r--drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c1395
-rw-r--r--drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c724
-rw-r--r--drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c86
-rw-r--r--drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c321
-rw-r--r--drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c305
-rw-r--r--drivers/hid/bpf/progs/XPPen__Deco02.bpf.c359
-rw-r--r--drivers/hid/bpf/progs/hid_report_helpers.h10
-rw-r--r--drivers/hid/hid-apple.c1
-rw-r--r--drivers/hid/hid-corsair-void.c5
-rw-r--r--drivers/hid/hid-elecom.c6
-rw-r--r--drivers/hid/hid-evision.c21
-rw-r--r--drivers/hid/hid-generic.c9
-rw-r--r--drivers/hid/hid-haptic.c2
-rw-r--r--drivers/hid/hid-ids.h13
-rw-r--r--drivers/hid/hid-input.c30
-rw-r--r--drivers/hid/hid-lenovo.c17
-rw-r--r--drivers/hid/hid-lg-g15.c483
-rw-r--r--drivers/hid/hid-logitech-dj.c192
-rw-r--r--drivers/hid/hid-logitech-hidpp.c12
-rw-r--r--drivers/hid/hid-nintendo.c5
-rw-r--r--drivers/hid/hid-ntrig.c7
-rw-r--r--drivers/hid/hid-playstation.c2
-rw-r--r--drivers/hid/hid-quirks.c16
-rw-r--r--drivers/hid/hid-uclogic-core.c19
-rw-r--r--drivers/hid/hid-uclogic-params.c51
-rw-r--r--drivers/hid/hid-uclogic-params.h5
-rw-r--r--drivers/hid/hid-uclogic-rdesc.c125
-rw-r--r--drivers/hid/hid-uclogic-rdesc.h8
-rw-r--r--drivers/hid/hid-winwing.c171
-rw-r--r--drivers/hid/intel-ish-hid/ipc/ipc.c101
-rw-r--r--drivers/hid/intel-ish-hid/ipc/pci-ish.c31
-rw-r--r--drivers/hid/intel-ish-hid/ishtp-hid-client.c15
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/bus.c18
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/client.c6
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/hbm.c4
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h3
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c2
-rw-r--r--drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c1
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c2
-rw-r--r--drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c1
-rw-r--r--drivers/hid/usbhid/hid-pidff.c4
-rw-r--r--drivers/hv/mshv_root_main.c33
-rw-r--r--drivers/hwmon/Kconfig36
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/adm1026.c16
-rw-r--r--drivers/hwmon/adm1029.c3
-rw-r--r--drivers/hwmon/adm9240.c17
-rw-r--r--drivers/hwmon/adt7410.c11
-rw-r--r--drivers/hwmon/adt7411.c59
-rw-r--r--drivers/hwmon/adt7x10.c27
-rw-r--r--drivers/hwmon/aht10.c43
-rw-r--r--drivers/hwmon/aquacomputer_d5next.c37
-rw-r--r--drivers/hwmon/aspeed-g6-pwm-tach.c3
-rw-r--r--drivers/hwmon/asus-ec-sensors.c67
-rw-r--r--drivers/hwmon/asus_rog_ryujin.c48
-rw-r--r--drivers/hwmon/cgbc-hwmon.c3
-rw-r--r--drivers/hwmon/chipcap2.c7
-rw-r--r--drivers/hwmon/corsair-cpro.c8
-rw-r--r--drivers/hwmon/corsair-psu.c13
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c9
-rw-r--r--drivers/hwmon/drivetemp.c5
-rw-r--r--drivers/hwmon/emc1403.c46
-rw-r--r--drivers/hwmon/emc2103.c4
-rw-r--r--drivers/hwmon/ftsteutates.c84
-rw-r--r--drivers/hwmon/gpd-fan.c120
-rw-r--r--drivers/hwmon/hs3001.c10
-rw-r--r--drivers/hwmon/i5500_temp.c3
-rw-r--r--drivers/hwmon/ina238.c26
-rw-r--r--drivers/hwmon/ina2xx.c28
-rw-r--r--drivers/hwmon/ina3221.c19
-rw-r--r--drivers/hwmon/jc42.c11
-rw-r--r--drivers/hwmon/k10temp.c12
-rw-r--r--drivers/hwmon/lm78.c5
-rw-r--r--drivers/hwmon/lm87.c16
-rw-r--r--drivers/hwmon/lm90.c25
-rw-r--r--drivers/hwmon/lm92.c11
-rw-r--r--drivers/hwmon/lm95234.c12
-rw-r--r--drivers/hwmon/lm95241.c16
-rw-r--r--drivers/hwmon/lm95245.c16
-rw-r--r--drivers/hwmon/lochnagar-hwmon.c18
-rw-r--r--drivers/hwmon/ltc2947-core.c92
-rw-r--r--drivers/hwmon/ltc4245.c8
-rw-r--r--drivers/hwmon/ltc4282.c68
-rw-r--r--drivers/hwmon/macsmc-hwmon.c851
-rw-r--r--drivers/hwmon/max127.c23
-rw-r--r--drivers/hwmon/max16065.c7
-rw-r--r--drivers/hwmon/max31790.c48
-rw-r--r--drivers/hwmon/max31827.c60
-rw-r--r--drivers/hwmon/max6620.c43
-rw-r--r--drivers/hwmon/max6639.c23
-rw-r--r--drivers/hwmon/max6697.c11
-rw-r--r--drivers/hwmon/mr75203.c1
-rw-r--r--drivers/hwmon/nct6775-platform.c1
-rw-r--r--drivers/hwmon/nct7363.c2
-rw-r--r--drivers/hwmon/nct7904.c63
-rw-r--r--drivers/hwmon/npcm750-pwm-fan.c11
-rw-r--r--drivers/hwmon/ntc_thermistor.c43
-rw-r--r--drivers/hwmon/peci/common.h3
-rw-r--r--drivers/hwmon/peci/cputemp.c90
-rw-r--r--drivers/hwmon/peci/dimmtemp.c36
-rw-r--r--drivers/hwmon/pmbus/Kconfig28
-rw-r--r--drivers/hwmon/pmbus/Makefile3
-rw-r--r--drivers/hwmon/pmbus/isl68137.c17
-rw-r--r--drivers/hwmon/pmbus/max17616.c73
-rw-r--r--drivers/hwmon/pmbus/max34440.c56
-rw-r--r--drivers/hwmon/pmbus/mp2925.c316
-rw-r--r--drivers/hwmon/pmbus/mp9945.c243
-rw-r--r--drivers/hwmon/powr1220.c17
-rw-r--r--drivers/hwmon/sbtsi_temp.c17
-rw-r--r--drivers/hwmon/scmi-hwmon.c9
-rw-r--r--drivers/hwmon/sfctemp.c36
-rw-r--r--drivers/hwmon/sht3x.c27
-rw-r--r--drivers/hwmon/sht4x.c40
-rw-r--r--drivers/hwmon/sy7636a-hwmon.c7
-rw-r--r--drivers/hwmon/tmp102.c2
-rw-r--r--drivers/hwmon/tmp103.c3
-rw-r--r--drivers/hwmon/tmp108.c1
-rw-r--r--drivers/hwmon/tmp401.c8
-rw-r--r--drivers/hwmon/tmp421.c28
-rw-r--r--drivers/hwmon/tmp464.c13
-rw-r--r--drivers/hwmon/tsc1641.c748
-rw-r--r--drivers/hwmon/vt1211.c53
-rw-r--r--drivers/hwmon/vt8231.c18
-rw-r--r--drivers/hwmon/w83781d.c5
-rw-r--r--drivers/hwmon/w83l786ng.c26
-rw-r--r--drivers/hwtracing/coresight/coresight-trbe.c9
-rw-r--r--drivers/i2c/muxes/i2c-mux-pca954x.c50
-rw-r--r--drivers/iio/accel/adxl355_core.c44
-rw-r--r--drivers/iio/accel/bmc150-accel-core.c5
-rw-r--r--drivers/iio/accel/bmc150-accel.h1
-rw-r--r--drivers/iio/adc/ad4030.c2
-rw-r--r--drivers/iio/adc/ad7124.c12
-rw-r--r--drivers/iio/adc/ad7280a.c2
-rw-r--r--drivers/iio/adc/ad7380.c8
-rw-r--r--drivers/iio/adc/rtq6056.c2
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c5
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dma.c6
-rw-r--r--drivers/iio/buffer/industrialio-buffer-dmaengine.c2
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_dev.c4
-rw-r--r--drivers/iio/humidity/hdc3020.c73
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h40
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c19
-rw-r--r--drivers/iio/industrialio-buffer.c21
-rw-r--r--drivers/iio/pressure/bmp280-core.c15
-rw-r--r--drivers/infiniband/Kconfig1
-rw-r--r--drivers/infiniband/core/cm.c9
-rw-r--r--drivers/infiniband/core/cma.c2
-rw-r--r--drivers/infiniband/core/device.c4
-rw-r--r--drivers/infiniband/core/restrack.c4
-rw-r--r--drivers/infiniband/core/ucma.c2
-rw-r--r--drivers/infiniband/core/umem.c8
-rw-r--r--drivers/infiniband/core/uverbs_std_types_cq.c1
-rw-r--r--drivers/infiniband/core/verbs.c3
-rw-r--r--drivers/infiniband/hw/Makefile1
-rw-r--r--drivers/infiniband/hw/bng_re/Kconfig10
-rw-r--r--drivers/infiniband/hw/bng_re/Makefile8
-rw-r--r--drivers/infiniband/hw/bng_re/bng_debugfs.c39
-rw-r--r--drivers/infiniband/hw/bng_re/bng_debugfs.h12
-rw-r--r--drivers/infiniband/hw/bng_re/bng_dev.c534
-rw-r--r--drivers/infiniband/hw/bng_re/bng_fw.c767
-rw-r--r--drivers/infiniband/hw/bng_re/bng_fw.h211
-rw-r--r--drivers/infiniband/hw/bng_re/bng_re.h85
-rw-r--r--drivers/infiniband/hw/bng_re/bng_res.c279
-rw-r--r--drivers/infiniband/hw/bng_re/bng_res.h215
-rw-r--r--drivers/infiniband/hw/bng_re/bng_sp.c131
-rw-r--r--drivers/infiniband/hw/bng_re/bng_sp.h47
-rw-r--r--drivers/infiniband/hw/bng_re/bng_tlv.h128
-rw-r--r--drivers/infiniband/hw/bnxt_re/bnxt_re.h2
-rw-r--r--drivers/infiniband/hw/bnxt_re/debugfs.c128
-rw-r--r--drivers/infiniband/hw/bnxt_re/debugfs.h19
-rw-r--r--drivers/infiniband/hw/bnxt_re/ib_verbs.c19
-rw-r--r--drivers/infiniband/hw/bnxt_re/main.c1
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_fp.c3
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_fp.h1
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_sp.c8
-rw-r--r--drivers/infiniband/hw/bnxt_re/qplib_sp.h2
-rw-r--r--drivers/infiniband/hw/cxgb4/mem.c2
-rw-r--r--drivers/infiniband/hw/efa/efa_verbs.c16
-rw-r--r--drivers/infiniband/hw/erdma/erdma_cm.c6
-rw-r--r--drivers/infiniband/hw/hfi1/init.c4
-rw-r--r--drivers/infiniband/hw/hfi1/opfn.c4
-rw-r--r--drivers/infiniband/hw/hns/Makefile4
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_ah.c1
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_bond.c1012
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_bond.h95
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_cq.c58
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_device.h20
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.c153
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_hw_v2.h20
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_main.c189
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_pd.c1
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_qp.c7
-rw-r--r--drivers/infiniband/hw/hns/hns_roce_srq.c1
-rw-r--r--drivers/infiniband/hw/irdma/cm.c2
-rw-r--r--drivers/infiniband/hw/irdma/ctrl.c107
-rw-r--r--drivers/infiniband/hw/irdma/hw.c3
-rw-r--r--drivers/infiniband/hw/irdma/icrdma_if.c6
-rw-r--r--drivers/infiniband/hw/irdma/ig3rdma_if.c4
-rw-r--r--drivers/infiniband/hw/irdma/main.h3
-rw-r--r--drivers/infiniband/hw/irdma/pble.c8
-rw-r--r--drivers/infiniband/hw/irdma/puda.c20
-rw-r--r--drivers/infiniband/hw/irdma/type.h7
-rw-r--r--drivers/infiniband/hw/irdma/uk.c67
-rw-r--r--drivers/infiniband/hw/irdma/user.h6
-rw-r--r--drivers/infiniband/hw/irdma/utils.c58
-rw-r--r--drivers/infiniband/hw/irdma/verbs.c50
-rw-r--r--drivers/infiniband/hw/irdma/verbs.h5
-rw-r--r--drivers/infiniband/hw/mlx4/cm.c2
-rw-r--r--drivers/infiniband/hw/mlx5/cq.c11
-rw-r--r--drivers/infiniband/hw/mlx5/devx.c14
-rw-r--r--drivers/infiniband/hw/mlx5/fs.c65
-rw-r--r--drivers/infiniband/hw/mlx5/ib_rep.c74
-rw-r--r--drivers/infiniband/hw/mlx5/main.c6
-rw-r--r--drivers/infiniband/hw/mlx5/odp.c93
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c5
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h4
-rw-r--r--drivers/infiniband/sw/rdmavt/cq.c3
-rw-r--r--drivers/infiniband/sw/rxe/rxe_mr.c1
-rw-r--r--drivers/infiniband/sw/rxe/rxe_net.c49
-rw-r--r--drivers/infiniband/sw/rxe/rxe_odp.c1
-rw-r--r--drivers/infiniband/sw/rxe/rxe_qp.c49
-rw-r--r--drivers/infiniband/sw/rxe/rxe_srq.c7
-rw-r--r--drivers/infiniband/sw/siw/siw_cm.c59
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c29
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c2
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c2
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-srv.c2
-rw-r--r--drivers/input/keyboard/cros_ec_keyb.c6
-rw-r--r--drivers/input/keyboard/imx_sc_key.c2
-rw-r--r--drivers/input/misc/Kconfig11
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/arizona-haptics.c14
-rw-r--r--drivers/input/misc/pf1550-onkey.c197
-rw-r--r--drivers/input/tablet/pegasus_notetaker.c9
-rw-r--r--drivers/input/touchscreen/goodix.c28
-rw-r--r--drivers/input/touchscreen/goodix.h1
-rw-r--r--drivers/iommu/Kconfig15
-rw-r--r--drivers/iommu/Makefile2
-rw-r--r--drivers/iommu/amd/Kconfig5
-rw-r--r--drivers/iommu/amd/Makefile2
-rw-r--r--drivers/iommu/amd/amd_iommu.h1
-rw-r--r--drivers/iommu/amd/amd_iommu_types.h114
-rw-r--r--drivers/iommu/amd/debugfs.c2
-rw-r--r--drivers/iommu/amd/init.c15
-rw-r--r--drivers/iommu/amd/io_pgtable.c577
-rw-r--r--drivers/iommu/amd/io_pgtable_v2.c370
-rw-r--r--drivers/iommu/amd/iommu.c572
-rw-r--r--drivers/iommu/apple-dart.c11
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c18
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c33
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c28
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu.c9
-rw-r--r--drivers/iommu/arm/arm-smmu/qcom_iommu.c21
-rw-r--r--drivers/iommu/dma-iommu.c9
-rw-r--r--drivers/iommu/exynos-iommu.c20
-rw-r--r--drivers/iommu/fsl_pamu_domain.c12
-rw-r--r--drivers/iommu/generic_pt/.kunitconfig14
-rw-r--r--drivers/iommu/generic_pt/Kconfig79
-rw-r--r--drivers/iommu/generic_pt/fmt/Makefile28
-rw-r--r--drivers/iommu/generic_pt/fmt/amdv1.h411
-rw-r--r--drivers/iommu/generic_pt/fmt/defs_amdv1.h21
-rw-r--r--drivers/iommu/generic_pt/fmt/defs_vtdss.h21
-rw-r--r--drivers/iommu/generic_pt/fmt/defs_x86_64.h21
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_amdv1.c15
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_mock.c10
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_template.h48
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_vtdss.c10
-rw-r--r--drivers/iommu/generic_pt/fmt/iommu_x86_64.c11
-rw-r--r--drivers/iommu/generic_pt/fmt/vtdss.h285
-rw-r--r--drivers/iommu/generic_pt/fmt/x86_64.h279
-rw-r--r--drivers/iommu/generic_pt/iommu_pt.h1289
-rw-r--r--drivers/iommu/generic_pt/kunit_generic_pt.h823
-rw-r--r--drivers/iommu/generic_pt/kunit_iommu.h184
-rw-r--r--drivers/iommu/generic_pt/kunit_iommu_pt.h487
-rw-r--r--drivers/iommu/generic_pt/pt_common.h389
-rw-r--r--drivers/iommu/generic_pt/pt_defs.h332
-rw-r--r--drivers/iommu/generic_pt/pt_fmt_defaults.h295
-rw-r--r--drivers/iommu/generic_pt/pt_iter.h636
-rw-r--r--drivers/iommu/generic_pt/pt_log2.h122
-rw-r--r--drivers/iommu/intel/Kconfig6
-rw-r--r--drivers/iommu/intel/iommu.c931
-rw-r--r--drivers/iommu/intel/iommu.h99
-rw-r--r--drivers/iommu/intel/nested.c7
-rw-r--r--drivers/iommu/intel/pasid.c44
-rw-r--r--drivers/iommu/intel/pasid.h1
-rw-r--r--drivers/iommu/intel/svm.c1
-rw-r--r--drivers/iommu/io-pgtable-arm-selftests.c214
-rw-r--r--drivers/iommu/io-pgtable-arm.c203
-rw-r--r--drivers/iommu/io-pgtable.c4
-rw-r--r--drivers/iommu/iommu-pages.c136
-rw-r--r--drivers/iommu/iommu-pages.h51
-rw-r--r--drivers/iommu/iommu.c44
-rw-r--r--drivers/iommu/iommufd/Kconfig1
-rw-r--r--drivers/iommu/iommufd/driver.c2
-rw-r--r--drivers/iommu/iommufd/io_pagetable.c90
-rw-r--r--drivers/iommu/iommufd/io_pagetable.h54
-rw-r--r--drivers/iommu/iommufd/ioas.c12
-rw-r--r--drivers/iommu/iommufd/iommufd_private.h18
-rw-r--r--drivers/iommu/iommufd/iommufd_test.h21
-rw-r--r--drivers/iommu/iommufd/iova_bitmap.c5
-rw-r--r--drivers/iommu/iommufd/main.c10
-rw-r--r--drivers/iommu/iommufd/pages.c414
-rw-r--r--drivers/iommu/iommufd/selftest.c569
-rw-r--r--drivers/iommu/ipmmu-vmsa.c12
-rw-r--r--drivers/iommu/msm_iommu.c11
-rw-r--r--drivers/iommu/mtk_iommu.c174
-rw-r--r--drivers/iommu/mtk_iommu_v1.c35
-rw-r--r--drivers/iommu/omap-iommu.c19
-rw-r--r--drivers/iommu/omap-iommu.h2
-rw-r--r--drivers/iommu/riscv/iommu.c9
-rw-r--r--drivers/iommu/rockchip-iommu.c20
-rw-r--r--drivers/iommu/s390-iommu.c13
-rw-r--r--drivers/iommu/sprd-iommu.c3
-rw-r--r--drivers/iommu/sun50i-iommu.c10
-rw-r--r--drivers/iommu/tegra-smmu.c15
-rw-r--r--drivers/iommu/virtio-iommu.c6
-rw-r--r--drivers/irqchip/Kconfig10
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-aclint-sswi.c3
-rw-r--r--drivers/irqchip/irq-apple-aic.c62
-rw-r--r--drivers/irqchip/irq-atmel-aic-common.c15
-rw-r--r--drivers/irqchip/irq-bcm2712-mip.c11
-rw-r--r--drivers/irqchip/irq-bcm7038-l1.c17
-rw-r--r--drivers/irqchip/irq-bcm7120-l2.c31
-rw-r--r--drivers/irqchip/irq-brcmstb-l2.c25
-rw-r--r--drivers/irqchip/irq-gic-its-msi-parent.c91
-rw-r--r--drivers/irqchip/irq-gic-v3.c224
-rw-r--r--drivers/irqchip/irq-imx-mu-msi.c28
-rw-r--r--drivers/irqchip/irq-mchp-eic.c5
-rw-r--r--drivers/irqchip/irq-meson-gpio.c17
-rw-r--r--drivers/irqchip/irq-mvebu-pic.c2
-rw-r--r--drivers/irqchip/irq-partition-percpu.c241
-rw-r--r--drivers/irqchip/irq-qcom-mpm.c6
-rw-r--r--drivers/irqchip/irq-renesas-rzg2l.c37
-rw-r--r--drivers/irqchip/irq-renesas-rzv2h.c32
-rw-r--r--drivers/irqchip/irq-riscv-imsic-early.c11
-rw-r--r--drivers/irqchip/irq-riscv-imsic-platform.c4
-rw-r--r--drivers/irqchip/irq-riscv-imsic-state.c20
-rw-r--r--drivers/irqchip/irq-riscv-imsic-state.h4
-rw-r--r--drivers/irqchip/irq-riscv-intc.c3
-rw-r--r--drivers/irqchip/irq-sifive-plic.c149
-rw-r--r--drivers/irqchip/irq-starfive-jh8100-intc.c6
-rw-r--r--drivers/irqchip/irq-ts4800.c1
-rw-r--r--drivers/irqchip/irqchip.c10
-rw-r--r--drivers/irqchip/qcom-irq-combiner.c6
-rw-r--r--drivers/irqchip/qcom-pdc.c5
-rw-r--r--drivers/isdn/capi/kcapi.c2
-rw-r--r--drivers/isdn/hardware/mISDN/hfcsusb.c18
-rw-r--r--drivers/isdn/mISDN/l1oip_core.c2
-rw-r--r--drivers/isdn/mISDN/socket.c4
-rw-r--r--drivers/leds/Kconfig8
-rw-r--r--drivers/leds/flash/leds-rt4505.c2
-rw-r--r--drivers/leds/flash/leds-rt8515.c2
-rw-r--r--drivers/leds/flash/leds-sgm3140.c3
-rw-r--r--drivers/leds/flash/leds-tps6131x.c2
-rw-r--r--drivers/leds/led-class.c6
-rw-r--r--drivers/leds/leds-cros_ec.c5
-rw-r--r--drivers/leds/leds-lp50xx.c67
-rw-r--r--drivers/leds/leds-max5970.c2
-rw-r--r--drivers/leds/leds-max77705.c2
-rw-r--r--drivers/leds/leds-netxbig.c36
-rw-r--r--drivers/leds/leds-pwm.c27
-rw-r--r--drivers/leds/leds-upboard.c2
-rw-r--r--drivers/leds/rgb/leds-ktd202x.c4
-rw-r--r--drivers/leds/rgb/leds-ncp5623.c2
-rw-r--r--drivers/leds/rgb/leds-qcom-lpg.c10
-rw-r--r--drivers/leds/trigger/ledtrig-input-events.c2
-rw-r--r--drivers/mailbox/mailbox-test.c2
-rw-r--r--drivers/mailbox/mailbox-th1520.c4
-rw-r--r--drivers/mailbox/mtk-cmdq-mailbox.c45
-rw-r--r--drivers/mailbox/mtk-gpueb-mailbox.c2
-rw-r--r--drivers/mailbox/omap-mailbox.c35
-rw-r--r--drivers/mailbox/pcc.c8
-rw-r--r--drivers/md/bcache/alloc.c25
-rw-r--r--drivers/md/bcache/bcache.h6
-rw-r--r--drivers/md/bcache/bset.h8
-rw-r--r--drivers/md/bcache/btree.c53
-rw-r--r--drivers/md/bcache/journal.c93
-rw-r--r--drivers/md/bcache/journal.h13
-rw-r--r--drivers/md/bcache/super.c33
-rw-r--r--drivers/md/bcache/sysfs.c15
-rw-r--r--drivers/md/bcache/writeback.c5
-rw-r--r--drivers/md/dm-pcache/Makefile2
-rw-r--r--drivers/md/dm-pcache/cache.c4
-rw-r--r--drivers/md/dm-pcache/cache.h2
-rw-r--r--drivers/md/dm-pcache/cache_req.c6
-rw-r--r--drivers/md/dm-pcache/pcache_internal.h2
-rw-r--r--drivers/md/dm-vdo/logger.c2
-rw-r--r--drivers/md/dm-verity-fec.c6
-rw-r--r--drivers/md/dm-zone.c63
-rw-r--r--drivers/md/dm.c2
-rw-r--r--drivers/md/dm.h3
-rw-r--r--drivers/md/md-linear.c2
-rw-r--r--drivers/md/md-llbitmap.c2
-rw-r--r--drivers/md/md.c259
-rw-r--r--drivers/md/md.h10
-rw-r--r--drivers/md/raid0.c20
-rw-r--r--drivers/md/raid1.c1
-rw-r--r--drivers/md/raid10.c1
-rw-r--r--drivers/md/raid5-cache.c2
-rw-r--r--drivers/md/raid5.c7
-rw-r--r--drivers/media/cec/core/cec-core.c1
-rw-r--r--drivers/media/common/saa7146/saa7146_fops.c4
-rw-r--r--drivers/media/common/siano/smsir.c2
-rw-r--r--drivers/media/common/videobuf2/videobuf2-dma-contig.c1
-rw-r--r--drivers/media/common/videobuf2/videobuf2-v4l2.c5
-rw-r--r--drivers/media/dvb-core/dmxdev.c4
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c2
-rw-r--r--drivers/media/dvb-core/dvb_demux.c28
-rw-r--r--drivers/media/dvb-core/dvb_ringbuffer.c36
-rw-r--r--drivers/media/dvb-core/dvbdev.c4
-rw-r--r--drivers/media/dvb-frontends/cxd2841er.c3
-rw-r--r--drivers/media/dvb-frontends/drx39xyj/drxj.c2
-rw-r--r--drivers/media/dvb-frontends/drxk_hard.c3
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c4
-rw-r--r--drivers/media/dvb-frontends/mn88443x.c7
-rw-r--r--drivers/media/i2c/Kconfig12
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/adv7604.c4
-rw-r--r--drivers/media/i2c/adv7842.c15
-rw-r--r--drivers/media/i2c/ar0521.c4
-rw-r--r--drivers/media/i2c/ccs/ccs-core.c8
-rw-r--r--drivers/media/i2c/ds90ub913.c2
-rw-r--r--drivers/media/i2c/ds90ub953.c14
-rw-r--r--drivers/media/i2c/dw9719.c128
-rw-r--r--drivers/media/i2c/imx111.c1610
-rw-r--r--drivers/media/i2c/imx214.c15
-rw-r--r--drivers/media/i2c/imx219.c99
-rw-r--r--drivers/media/i2c/imx274.c3
-rw-r--r--drivers/media/i2c/imx335.c513
-rw-r--r--drivers/media/i2c/imx412.c4
-rw-r--r--drivers/media/i2c/max9286.c4
-rw-r--r--drivers/media/i2c/max96717.c18
-rw-r--r--drivers/media/i2c/msp3400-kthreads.c2
-rw-r--r--drivers/media/i2c/mt9m111.c4
-rw-r--r--drivers/media/i2c/mt9v111.c12
-rw-r--r--drivers/media/i2c/ov02c10.c27
-rw-r--r--drivers/media/i2c/ov13b10.c1
-rw-r--r--drivers/media/i2c/ov5675.c4
-rw-r--r--drivers/media/i2c/ov5693.c4
-rw-r--r--drivers/media/i2c/ov9282.c4
-rw-r--r--drivers/media/i2c/rj54n1cb0c.c8
-rw-r--r--drivers/media/i2c/st-mipid02.c4
-rw-r--r--drivers/media/i2c/tc358746.c12
-rw-r--r--drivers/media/i2c/tda1997x.c1
-rw-r--r--drivers/media/i2c/vd55g1.c234
-rw-r--r--drivers/media/mc/mc-request.c34
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c9
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c30
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.h8
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c8
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c4
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c8
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c34
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.c14
-rw-r--r--drivers/media/pci/intel/ivsc/mei_ace.c4
-rw-r--r--drivers/media/pci/ivtv/ivtv-driver.c11
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c22
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.h6
-rw-r--r--drivers/media/pci/mgb4/mgb4_trigger.c5
-rw-r--r--drivers/media/pci/mgb4/mgb4_vin.c4
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.c4
-rw-r--r--drivers/media/pci/pt1/pt1.c2
-rw-r--r--drivers/media/platform/Kconfig1
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-core.c118
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Kconfig1
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-params.c166
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/ge2d.c5
-rw-r--r--drivers/media/platform/amphion/vdec.c4
-rw-r--r--drivers/media/platform/amphion/venc.c4
-rw-r--r--drivers/media/platform/amphion/vpu_core.c40
-rw-r--r--drivers/media/platform/amphion/vpu_drv.c26
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c23
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c16
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.h10
-rw-r--r--drivers/media/platform/arm/Kconfig5
-rw-r--r--drivers/media/platform/arm/Makefile (renamed from arch/s390/kernel/vdso32/.gitignore)2
-rw-r--r--drivers/media/platform/arm/mali-c55/Kconfig18
-rw-r--r--drivers/media/platform/arm/mali-c55/Makefile11
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-capture.c959
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-common.h310
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-core.c917
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-isp.c665
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-params.c819
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-registers.h449
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-resizer.c1156
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-stats.c323
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-tpg.c437
-rw-r--r--drivers/media/platform/chips-media/coda/coda-bit.c2
-rw-r--r--drivers/media/platform/chips-media/coda/coda-common.c4
-rw-r--r--drivers/media/platform/chips-media/coda/coda-jpeg.c4
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.c6
-rw-r--r--drivers/media/platform/m2m-deinterlace.c7
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c11
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c4
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c4
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c14
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c14
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c7
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c12
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c14
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c5
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c8
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c5
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c14
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c12
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h2
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c5
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/h264.c2
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c9
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c6
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c22
-rw-r--r--drivers/media/platform/nxp/imx-pxp.c7
-rw-r--r--drivers/media/platform/nxp/imx7-media-csi.c1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c50
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c22
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c4
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c5
-rw-r--r--drivers/media/platform/nxp/mx2_emmaprp.c7
-rw-r--r--drivers/media/platform/qcom/camss/Makefile1
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c102
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c1
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c8
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c12
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-vbif.c31
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-vbif.h19
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c17
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h3
-rw-r--r--drivers/media/platform/qcom/camss/camss.c483
-rw-r--r--drivers/media/platform/qcom/camss/camss.h3
-rw-r--r--drivers/media/platform/qcom/iris/Makefile2
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c17
-rw-r--r--drivers/media/platform/qcom/iris/iris_common.c7
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c18
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c18
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c15
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c21
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h7
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h18
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen1.c (renamed from drivers/media/platform/qcom/iris/iris_platform_sm8250.c)63
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c26
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h535
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sc7280.h26
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c4
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.c3
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c8
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c63
-rw-r--r--drivers/media/platform/qcom/iris/iris_venc.c61
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu2.c6
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c34
-rw-r--r--drivers/media/platform/qcom/venus/core.c1
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c19
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c8
-rw-r--r--drivers/media/platform/qcom/venus/venc.c8
-rw-r--r--drivers/media/platform/renesas/Kconfig1
-rw-r--r--drivers/media/platform/renesas/Makefile1
-rw-r--r--drivers/media/platform/renesas/rcar_drif.c1
-rw-r--r--drivers/media/platform/renesas/rcar_fdp1.c6
-rw-r--r--drivers/media/platform/renesas/rcar_jpu.c16
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c8
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/Kconfig18
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/Makefile5
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c251
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c376
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c531
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h130
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c3
-rw-r--r--drivers/media/platform/rockchip/Kconfig1
-rw-r--r--drivers/media/platform/rockchip/Makefile1
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c6
-rw-r--r--drivers/media/platform/rockchip/rkcif/Kconfig18
-rw-r--r--drivers/media/platform/rockchip/rkcif/Makefile8
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c865
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h25
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c777
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h23
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-common.h250
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-dev.c303
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.c442
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.h31
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-regs.h153
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.c636
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-stream.h32
-rw-r--r--drivers/media/platform/rockchip/rkisp1/Kconfig1
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.h1
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c4
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c31
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c151
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Makefile2
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c1848
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c820
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h4
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c4
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.c200
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.h17
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.c14
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d.c4
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c7
-rw-r--r--drivers/media/platform/st/Makefile1
-rw-r--r--drivers/media/platform/st/sti/Kconfig1
-rw-r--r--drivers/media/platform/st/sti/Makefile1
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Kconfig28
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Makefile11
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c262
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h60
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c1158
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h287
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c244
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h23
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c235
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h17
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.c7
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/sun8i-di.c2
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c2
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c2
-rw-r--r--drivers/media/platform/ti/cal/cal.c3
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c4
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.c4
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.c10
-rw-r--r--drivers/media/platform/ti/vpe/vpe.c7
-rw-r--r--drivers/media/platform/verisilicon/hantro_drv.c2
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2.c88
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c17
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_regs.h13
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c2
-rw-r--r--drivers/media/platform/verisilicon/hantro_hw.h1
-rw-r--r--drivers/media/platform/verisilicon/imx8m_vpu_hw.c2
-rw-r--r--drivers/media/rc/ir-hix5hd2.c1
-rw-r--r--drivers/media/rc/st_rc.c2
-rw-r--r--drivers/media/test-drivers/vicodec/vicodec-core.c11
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_channel.c3
-rw-r--r--drivers/media/test-drivers/vim2m.c14
-rw-r--r--drivers/media/test-drivers/visl/visl-dec.c2
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.c6
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c4
-rw-r--r--drivers/media/tuners/xc2028.c9
-rw-r--r--drivers/media/usb/dvb-usb/dtv5100.c5
-rw-r--r--drivers/media/usb/dvb-usb/pctv452e.c7
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c2
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c15
-rw-r--r--drivers/media/v4l2-core/Kconfig4
-rw-r--r--drivers/media/v4l2-core/Makefile1
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c29
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c131
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-isp.c132
-rw-r--r--drivers/media/v4l2-core/v4l2-mem2mem.c21
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c2
-rw-r--r--drivers/memory/tegra/tegra210.c4
-rw-r--r--drivers/mfd/Kconfig32
-rw-r--r--drivers/mfd/Makefile3
-rw-r--r--drivers/mfd/altera-sysmgr.c2
-rw-r--r--drivers/mfd/bcm2835-pm.c1
-rw-r--r--drivers/mfd/da9055-core.c2
-rw-r--r--drivers/mfd/da9063-i2c.c3
-rw-r--r--drivers/mfd/ls2k-bmc-core.c2
-rw-r--r--drivers/mfd/macsmc.c6
-rw-r--r--drivers/mfd/max77620.c15
-rw-r--r--drivers/mfd/mt6358-irq.c1
-rw-r--r--drivers/mfd/mt6397-irq.c1
-rw-r--r--drivers/mfd/pf1550.c367
-rw-r--r--drivers/mfd/qnap-mcu.c80
-rw-r--r--drivers/mfd/rohm-bd718x7.c9
-rw-r--r--drivers/mfd/sec-acpm.c23
-rw-r--r--drivers/mfd/sec-irq.c73
-rw-r--r--drivers/mfd/simple-mfd-i2c.c18
-rw-r--r--drivers/mfd/syscon.c2
-rw-r--r--drivers/mfd/tqmx86.c8
-rw-r--r--drivers/mfd/wl1273-core.c262
-rw-r--r--drivers/misc/amd-sbi/Kconfig2
-rw-r--r--drivers/misc/fastrpc.c2
-rw-r--r--drivers/misc/mei/hw-me-regs.h2
-rw-r--r--drivers/misc/mei/mei_lb.c3
-rw-r--r--drivers/misc/mei/pci-me.c15
-rw-r--r--drivers/misc/mei/pci-txe.c17
-rw-r--r--drivers/misc/mei/platform-vsc.c11
-rw-r--r--drivers/misc/ntsync.c21
-rw-r--r--drivers/misc/vmw_balloon.c8
-rw-r--r--drivers/mmc/core/block.c14
-rw-r--r--drivers/mmc/core/bus.h2
-rw-r--r--drivers/mmc/core/debugfs.c10
-rw-r--r--drivers/mmc/core/mmc.c4
-rw-r--r--drivers/mmc/core/mmc_test.c24
-rw-r--r--drivers/mmc/core/sd.c9
-rw-r--r--drivers/mmc/host/Kconfig3
-rw-r--r--drivers/mmc/host/atmel-mci.c10
-rw-r--r--drivers/mmc/host/cqhci.h1
-rw-r--r--drivers/mmc/host/davinci_mmc.c6
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c10
-rw-r--r--drivers/mmc/host/dw_mmc.c15
-rw-r--r--drivers/mmc/host/meson-mx-sdio.c339
-rw-r--r--drivers/mmc/host/mtk-sd.c4
-rw-r--r--drivers/mmc/host/omap.c6
-rw-r--r--drivers/mmc/host/omap_hsmmc.c4
-rw-r--r--drivers/mmc/host/pxamci.c56
-rw-r--r--drivers/mmc/host/renesas_sdhi.h3
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c39
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c15
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c3
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c154
-rw-r--r--drivers/mmc/host/sdhci-msm.c27
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c2
-rw-r--r--drivers/mmc/host/sdhci-of-dwcmshc.c635
-rw-r--r--drivers/mmc/host/tmio_mmc.h2
-rw-r--r--drivers/most/most_usb.c27
-rw-r--r--drivers/mtd/devices/docg3.h2
-rw-r--r--drivers/mtd/devices/mtd_intel_dg.c74
-rw-r--r--drivers/mtd/lpddr/lpddr_cmds.c8
-rw-r--r--drivers/mtd/maps/pcmciamtd.c1
-rw-r--r--drivers/mtd/mtdchar.c6
-rw-r--r--drivers/mtd/mtdpart.c7
-rw-r--r--drivers/mtd/nand/Kconfig2
-rw-r--r--drivers/mtd/nand/ecc-realtek.c6
-rw-r--r--drivers/mtd/nand/onenand/onenand_samsung.c2
-rw-r--r--drivers/mtd/nand/raw/cadence-nand-controller.c276
-rw-r--r--drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c3
-rw-r--r--drivers/mtd/nand/raw/lpc32xx_slc.c2
-rw-r--r--drivers/mtd/nand/raw/marvell_nand.c13
-rw-r--r--drivers/mtd/nand/raw/nand_base.c13
-rw-r--r--drivers/mtd/nand/raw/renesas-nand-controller.c5
-rw-r--r--drivers/mtd/nand/raw/sunxi_nand.c409
-rw-r--r--drivers/mtd/nand/spi/core.c1
-rw-r--r--drivers/mtd/nand/spi/esmt.c24
-rw-r--r--drivers/mtd/nand/spi/fmsh.c74
-rw-r--r--drivers/mtd/sm_ftl.c5
-rw-r--r--drivers/mtd/spi-nor/core.c10
-rw-r--r--drivers/mtd/spi-nor/core.h6
-rw-r--r--drivers/mtd/spi-nor/micron-st.c101
-rw-r--r--drivers/mtd/spi-nor/sfdp.c30
-rw-r--r--drivers/mtd/spi-nor/spansion.c38
-rw-r--r--drivers/mtd/spi-nor/winbond.c24
-rw-r--r--drivers/net/bonding/bond_3ad.c9
-rw-r--r--drivers/net/bonding/bond_main.c151
-rw-r--r--drivers/net/bonding/bond_options.c9
-rw-r--r--drivers/net/can/Kconfig17
-rw-r--r--drivers/net/can/Makefile1
-rw-r--r--drivers/net/can/at91_can.c1
-rw-r--r--drivers/net/can/bxcan.c5
-rw-r--r--drivers/net/can/c_can/c_can_main.c1
-rw-r--r--drivers/net/can/can327.c1
-rw-r--r--drivers/net/can/cc770/cc770.c1
-rw-r--r--drivers/net/can/ctucanfd/ctucanfd_base.c1
-rw-r--r--drivers/net/can/dev/bittiming.c63
-rw-r--r--drivers/net/can/dev/calc_bittiming.c114
-rw-r--r--drivers/net/can/dev/dev.c125
-rw-r--r--drivers/net/can/dev/netlink.c325
-rw-r--r--drivers/net/can/dummy_can.c285
-rw-r--r--drivers/net/can/esd/esd_402_pci-core.c4
-rw-r--r--drivers/net/can/esd/esdacc.c2
-rw-r--r--drivers/net/can/flexcan/flexcan-core.c1
-rw-r--r--drivers/net/can/grcan.c1
-rw-r--r--drivers/net/can/ifi_canfd/ifi_canfd.c1
-rw-r--r--drivers/net/can/janz-ican3.c1
-rw-r--r--drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c4
-rw-r--r--drivers/net/can/m_can/m_can.c256
-rw-r--r--drivers/net/can/m_can/m_can.h5
-rw-r--r--drivers/net/can/m_can/m_can_pci.c4
-rw-r--r--drivers/net/can/m_can/m_can_platform.c4
-rw-r--r--drivers/net/can/m_can/tcan4x5x-core.c4
-rw-r--r--drivers/net/can/mscan/mscan.c1
-rw-r--r--drivers/net/can/peak_canfd/peak_canfd.c36
-rw-r--r--drivers/net/can/rcar/rcar_can.c1
-rw-r--r--drivers/net/can/rcar/rcar_canfd.c300
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd-core.c1
-rw-r--r--drivers/net/can/rockchip/rockchip_canfd-tx.c2
-rw-r--r--drivers/net/can/sja1000/sja1000.c5
-rw-r--r--drivers/net/can/slcan/slcan-core.c1
-rw-r--r--drivers/net/can/softing/softing_main.c1
-rw-r--r--drivers/net/can/spi/hi311x.c1
-rw-r--r--drivers/net/can/spi/mcp251x.c32
-rw-r--r--drivers/net/can/spi/mcp251xfd/Kconfig1
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c277
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c114
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd.h8
-rw-r--r--drivers/net/can/sun4i_can.c5
-rw-r--r--drivers/net/can/ti_hecc.c1
-rw-r--r--drivers/net/can/usb/ems_usb.c1
-rw-r--r--drivers/net/can/usb/esd_usb.c1
-rw-r--r--drivers/net/can/usb/etas_es58x/es58x_core.c4
-rw-r--r--drivers/net/can/usb/f81604.c1
-rw-r--r--drivers/net/can/usb/gs_usb.c121
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c4
-rw-r--r--drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c4
-rw-r--r--drivers/net/can/usb/mcba_usb.c1
-rw-r--r--drivers/net/can/usb/nct6694_canfd.c1
-rw-r--r--drivers/net/can/usb/peak_usb/pcan_usb_core.c40
-rw-r--r--drivers/net/can/usb/ucan.c1
-rw-r--r--drivers/net/can/usb/usb_8dev.c1
-rw-r--r--drivers/net/can/xilinx_can.c1
-rw-r--r--drivers/net/dsa/Kconfig7
-rw-r--r--drivers/net/dsa/Makefile1
-rw-r--r--drivers/net/dsa/b53/b53_common.c384
-rw-r--r--drivers/net/dsa/b53/b53_priv.h111
-rw-r--r--drivers/net/dsa/b53/b53_regs.h48
-rw-r--r--drivers/net/dsa/dsa_loop.c7
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek.c2
-rw-r--r--drivers/net/dsa/hirschmann/hellcreek_ptp.c14
-rw-r--r--drivers/net/dsa/ks8995.c6
-rw-r--r--drivers/net/dsa/lantiq/Kconfig17
-rw-r--r--drivers/net/dsa/lantiq/Makefile2
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip.c1688
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip.h33
-rw-r--r--drivers/net/dsa/lantiq/lantiq_gswip_common.c1739
-rw-r--r--drivers/net/dsa/lantiq/mxl-gsw1xx.c733
-rw-r--r--drivers/net/dsa/lantiq/mxl-gsw1xx.h126
-rw-r--r--drivers/net/dsa/lantiq/mxl-gsw1xx_pce.h154
-rw-r--r--drivers/net/dsa/microchip/ksz9477.c100
-rw-r--r--drivers/net/dsa/microchip/ksz9477_reg.h3
-rw-r--r--drivers/net/dsa/microchip/ksz_common.c35
-rw-r--r--drivers/net/dsa/microchip/ksz_common.h2
-rw-r--r--drivers/net/dsa/microchip/ksz_ptp.c22
-rw-r--r--drivers/net/dsa/microchip/lan937x_main.c1
-rw-r--r--drivers/net/dsa/mt7530.c5
-rw-r--r--drivers/net/dsa/mt7530.h1
-rw-r--r--drivers/net/dsa/mv88e6060.c2
-rw-r--r--drivers/net/dsa/ocelot/felix.c70
-rw-r--r--drivers/net/dsa/realtek/rtl8365mb.c2
-rw-r--r--drivers/net/dsa/realtek/rtl8366rb.c2
-rw-r--r--drivers/net/dsa/rzn1_a5psw.c2
-rw-r--r--drivers/net/dsa/sja1105/sja1105_main.c7
-rw-r--r--drivers/net/dsa/sja1105/sja1105_tas.c8
-rw-r--r--drivers/net/dsa/xrs700x/xrs700x.c11
-rw-r--r--drivers/net/dsa/yt921x.c3006
-rw-r--r--drivers/net/dsa/yt921x.h567
-rw-r--r--drivers/net/ethernet/3com/3c515.c4
-rw-r--r--drivers/net/ethernet/Kconfig1
-rw-r--r--drivers/net/ethernet/Makefile1
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.c438
-rw-r--r--drivers/net/ethernet/airoha/airoha_eth.h72
-rw-r--r--drivers/net/ethernet/airoha/airoha_npu.c93
-rw-r--r--drivers/net/ethernet/airoha/airoha_ppe.c261
-rw-r--r--drivers/net/ethernet/airoha/airoha_ppe_debugfs.c3
-rw-r--r--drivers/net/ethernet/airoha/airoha_regs.h115
-rw-r--r--drivers/net/ethernet/altera/altera_tse.h3
-rw-r--r--drivers/net/ethernet/altera/altera_tse_main.c47
-rw-r--r--drivers/net/ethernet/amd/Kconfig1
-rw-r--r--drivers/net/ethernet/amd/pds_core/core.h3
-rw-r--r--drivers/net/ethernet/amd/pds_core/devlink.c3
-rw-r--r--drivers/net/ethernet/amd/xgbe/Makefile2
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c19
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c109
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c7
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-hwtstamp.c28
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c3
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-selftest.c346
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h22
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c22
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h1
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_main.c66
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ptp.c6
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ptp.h8
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/aq_ring.c5
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c19
-rw-r--r--drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c2
-rw-r--r--drivers/net/ethernet/broadcom/Kconfig1
-rw-r--r--drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c34
-rw-r--r--drivers/net/ethernet/broadcom/b44.c37
-rw-r--r--drivers/net/ethernet/broadcom/bnge/Makefile3
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge.h10
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_auxr.c258
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_auxr.h84
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_core.c18
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c40
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_resc.c12
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_resc.h1
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c16
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c71
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c40
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h6
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_coredump.c5
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c8
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c31
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c7
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c55
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c7
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h1
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmgenet.c31
-rw-r--r--drivers/net/ethernet/broadcom/genet/bcmmii.c75
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c25
-rw-r--r--drivers/net/ethernet/cadence/macb.h77
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c355
-rw-r--r--drivers/net/ethernet/cadence/macb_ptp.c16
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_main.c50
-rw-r--r--drivers/net/ethernet/cavium/liquidio/lio_vf_main.c48
-rw-r--r--drivers/net/ethernet/cavium/octeon/octeon_mgmt.c62
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c16
-rw-r--r--drivers/net/ethernet/cavium/thunder/nicvf_main.c45
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c1
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4.h2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c158
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c40
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c4
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sched.c44
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/sched.h12
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c7
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c4
-rw-r--r--drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c8
-rw-r--r--drivers/net/ethernet/dlink/dl2k.c10
-rw-r--r--drivers/net/ethernet/dlink/dl2k.h2
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c7
-rw-r--r--drivers/net/ethernet/engleder/tsnep.h8
-rw-r--r--drivers/net/ethernet/engleder/tsnep_main.c14
-rw-r--r--drivers/net/ethernet/engleder/tsnep_ptp.c88
-rw-r--r--drivers/net/ethernet/fealnx.c4
-rw-r--r--drivers/net/ethernet/freescale/Kconfig1
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c45
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c3
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c11
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.c53
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.h10
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_hw.h36
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc4_pf.c15
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_ethtool.c99
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_hw.h1
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_pf_common.c19
-rw-r--r--drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c400
-rw-r--r--drivers/net/ethernet/freescale/fec.h31
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c142
-rw-r--r--drivers/net/ethernet/freescale/fec_ptp.c64
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.c91
-rw-r--r--drivers/net/ethernet/freescale/fman/mac.h14
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c11
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth.h4
-rw-r--r--drivers/net/ethernet/fungible/funeth/funeth_main.c40
-rw-r--r--drivers/net/ethernet/google/gve/gve.h22
-rw-r--r--drivers/net/ethernet/google/gve/gve_adminq.c4
-rw-r--r--drivers/net/ethernet/google/gve/gve_dqo.h1
-rw-r--r--drivers/net/ethernet/google/gve/gve_ethtool.c97
-rw-r--r--drivers/net/ethernet/google/gve/gve_main.c97
-rw-r--r--drivers/net/ethernet/google/gve/gve_ptp.c27
-rw-r--r--drivers/net/ethernet/google/gve/gve_rx_dqo.c73
-rw-r--r--drivers/net/ethernet/google/gve/gve_tx.c2
-rw-r--r--drivers/net/ethernet/google/gve/gve_tx_dqo.c6
-rw-r--r--drivers/net/ethernet/hisilicon/Kconfig2
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/Makefile1
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h9
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c11
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c3
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c1
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c17
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c1
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h4
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_trace.h84
-rw-r--r--drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c217
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hnae3.h5
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.c31
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c16
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c9
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h2
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c32
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.h9
-rw-r--r--drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c2
-rw-r--r--drivers/net/ethernet/intel/Kconfig5
-rw-r--r--drivers/net/ethernet/intel/e1000e/e1000.h1
-rw-r--r--drivers/net/ethernet/intel/e1000e/ethtool.c51
-rw-r--r--drivers/net/ethernet/intel/e1000e/ich8lan.c41
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c4
-rw-r--r--drivers/net/ethernet/intel/e1000e/ptp.c7
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c17
-rw-r--r--drivers/net/ethernet/intel/fm10k/fm10k_pci.c6
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e.h4
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_devlink.c55
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_ethtool.c19
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_main.c1
-rw-r--r--drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c43
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_adv_rss.c119
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_adv_rss.h31
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_ethtool.c107
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_ptp.c7
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_virtchnl.c12
-rw-r--r--drivers/net/ethernet/intel/ice/devlink/devlink.c35
-rw-r--r--drivers/net/ethernet/intel/ice/ice.h8
-rw-r--r--drivers/net/ethernet/intel/ice/ice_base.c170
-rw-r--r--drivers/net/ethernet/intel/ice/ice_common.c39
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ethtool.c200
-rw-r--r--drivers/net/ethernet/intel/ice/ice_fdir.c2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flex_pipe.c101
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flex_type.h1
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flow.c269
-rw-r--r--drivers/net/ethernet/intel/ice/ice_flow.h94
-rw-r--r--drivers/net/ethernet/intel/ice/ice_fw_update.c2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lag.c3
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h3
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lib.c5
-rw-r--r--drivers/net/ethernet/intel/ice/ice_main.c205
-rw-r--r--drivers/net/ethernet/intel/ice/ice_protocol_type.h20
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.c37
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ptp.h2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sbq_cmd.h1
-rw-r--r--drivers/net/ethernet/intel/ice/ice_sriov.c3
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx.c710
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx.h132
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx_lib.c65
-rw-r--r--drivers/net/ethernet/intel/ice/ice_txrx_lib.h9
-rw-r--r--drivers/net/ethernet/intel/ice/ice_type.h1
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_lib.h48
-rw-r--r--drivers/net/ethernet/intel/ice/ice_xsk.c146
-rw-r--r--drivers/net/ethernet/intel/ice/ice_xsk.h6
-rw-r--r--drivers/net/ethernet/intel/ice/virt/queues.c6
-rw-r--r--drivers/net/ethernet/intel/ice/virt/rss.c1313
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf.h14
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_ethtool.c35
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_lib.c24
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_main.c107
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c2
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_txrx.c12
-rw-r--r--drivers/net/ethernet/intel/idpf/idpf_virtchnl.c4
-rw-r--r--drivers/net/ethernet/intel/idpf/xdp.c2
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ethtool.c14
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c2
-rw-r--r--drivers/net/ethernet/intel/igb/igb_ptp.c7
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c2
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ethtool.c16
-rw-r--r--drivers/net/ethernet/intel/igc/igc_main.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe.h2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c15
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c7
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ethtool.c14
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf.h18
-rw-r--r--drivers/net/ethernet/marvell/mvneta.c14
-rw-r--r--drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c11
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/Makefile3
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cgx.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c218
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.h28
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/nix.c20
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/npa.c21
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cn20k/struct.h340
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mbox.h73
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu.h15
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c42
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c15
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c76
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c29
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h31
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c10
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c220
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c20
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h19
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c6
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c56
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_ethtool.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/en_netdev.c62
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/mlx4_en.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c55
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cq.c23
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/dev.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en.h21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/mapping.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/mapping.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/params.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rss.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rss.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/int_port.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tir.c29
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tir.h3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/trap.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/trap.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c25
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c41
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c233
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h16
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_common.c52
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c46
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c58
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c169
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rep.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c133
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_stats.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.c32
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tx.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c15
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/legacy.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c36
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c226
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c15
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c31
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.c82
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fs_core.h19
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c41
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c21
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c53
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c116
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c238
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/st.c29
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c99
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/port.c36
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c48
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h11
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c90
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c61
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h20
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c69
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/hws/send.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_domain.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_send.c28
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/vport.c43
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/wc.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/core_linecards.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c6
-rw-r--r--drivers/net/ethernet/meta/Kconfig3
-rw-r--r--drivers/net/ethernet/meta/fbnic/Makefile1
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic.h15
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_csr.h2
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c9
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_fw.c8
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_irq.c34
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mac.c81
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mac.h41
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_mdio.c195
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_netdev.c11
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_netdev.h8
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_pci.c16
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_phylink.c187
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_time.c2
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_tlv.h2
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_txrx.c32
-rw-r--r--drivers/net/ethernet/meta/fbnic/fbnic_txrx.h1
-rw-r--r--drivers/net/ethernet/microchip/lan743x_main.c1
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c18
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_main.c2
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_main.h4
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c5
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c8
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main.c2
-rw-r--r--drivers/net/ethernet/microsoft/mana/gdma_main.c183
-rw-r--r--drivers/net/ethernet/microsoft/mana/hw_channel.c12
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana_en.c204
-rw-r--r--drivers/net/ethernet/microsoft/mana/mana_ethtool.c87
-rw-r--r--drivers/net/ethernet/mucse/Kconfig33
-rw-r--r--drivers/net/ethernet/mucse/Makefile7
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/Makefile11
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h71
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c143
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h17
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c320
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c406
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h20
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c191
-rw-r--r--drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h88
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c4
-rw-r--r--drivers/net/ethernet/neterion/s2io.c1
-rw-r--r--drivers/net/ethernet/netronome/nfp/devlink_param.c3
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c6
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c38
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_lif.c17
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_lif.h18
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_phc.c61
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_txrx.c34
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_devlink.c3
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_fp.c5
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_main.c22
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ptp.c76
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_ptp.h6
-rw-r--r--drivers/net/ethernet/realtek/r8169_main.c111
-rw-r--r--drivers/net/ethernet/renesas/ravb.h16
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c167
-rw-r--r--drivers/net/ethernet/renesas/rcar_gen4_ptp.h13
-rw-r--r--drivers/net/ethernet/renesas/rswitch.h3
-rw-r--r--drivers/net/ethernet/renesas/rswitch_main.c88
-rw-r--r--drivers/net/ethernet/renesas/rtsn.c47
-rw-r--r--drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c4
-rw-r--r--drivers/net/ethernet/sfc/mae.c4
-rw-r--r--drivers/net/ethernet/spacemit/k1_emac.c3
-rw-r--r--drivers/net/ethernet/spacemit/k1_emac.h8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Kconfig21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/Makefile4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/chain_mode.c9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/common.h42
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c34
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c235
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c134
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c165
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c53
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c73
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c30
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c44
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c77
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c30
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c269
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c372
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c26
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c168
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c24
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c54
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c44
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c21
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c6
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c26
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000.h7
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c96
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c35
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4.h3
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c100
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c30
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h11
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h16
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c39
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c33
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.c251
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/hwif.h19
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/ring_mode.c9
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac.h27
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_est.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c112
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c3
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.c48
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.h12
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c617
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c88
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c85
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c67
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h25
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c112
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c2
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-nuss.c47
-rw-r--r--drivers/net/ethernet/ti/am65-cpsw-qos.c51
-rw-r--r--drivers/net/ethernet/ti/am65-cpts.c63
-rw-r--r--drivers/net/ethernet/ti/cpsw_new.c6
-rw-r--r--drivers/net/ethernet/ti/davinci_mdio.c21
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_common.c516
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_config.c7
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.c401
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth.h31
-rw-r--r--drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c7
-rw-r--r--drivers/net/ethernet/ti/netcp.h5
-rw-r--r--drivers/net/ethernet/ti/netcp_core.c68
-rw-r--r--drivers/net/ethernet/ti/netcp_ethss.c72
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.c58
-rw-r--r--drivers/net/ethernet/toshiba/ps3_gelic_net.h1
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_ethtool.c73
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_hw.c72
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_lib.c143
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_sriov.c4
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_type.h55
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf.h4
-rw-r--r--drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c12
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c298
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h5
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c38
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c10
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_main.c28
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c2
-rw-r--r--drivers/net/ethernet/wangxun/txgbe/txgbe_type.h39
-rw-r--r--drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c12
-rw-r--r--drivers/net/gtp.c2
-rw-r--r--drivers/net/hyperv/netvsc_drv.c15
-rw-r--r--drivers/net/ipa/ipa_interrupt.c1
-rw-r--r--drivers/net/ipa/ipa_main.c1
-rw-r--r--drivers/net/ipa/ipa_modem.c4
-rw-r--r--drivers/net/ipa/ipa_smp2p.c2
-rw-r--r--drivers/net/ipa/ipa_uc.c2
-rw-r--r--drivers/net/ipvlan/ipvlan_core.c4
-rw-r--r--drivers/net/mctp/mctp-usb.c8
-rw-r--r--drivers/net/mdio/fwnode_mdio.c5
-rw-r--r--drivers/net/mdio/mdio-airoha.c2
-rw-r--r--drivers/net/mdio/of_mdio.c5
-rw-r--r--drivers/net/netconsole.c395
-rw-r--r--drivers/net/netdevsim/dev.c56
-rw-r--r--drivers/net/netdevsim/ipsec.c1
-rw-r--r--drivers/net/netdevsim/netdev.c26
-rw-r--r--drivers/net/netdevsim/netdevsim.h6
-rw-r--r--drivers/net/netdevsim/psp.c27
-rw-r--r--drivers/net/netkit.c6
-rw-r--r--drivers/net/ovpn/netlink-gen.c1
-rw-r--r--drivers/net/ovpn/netlink-gen.h1
-rw-r--r--drivers/net/ovpn/tcp.c26
-rw-r--r--drivers/net/pcs/pcs-lynx.c77
-rw-r--r--drivers/net/pcs/pcs-xpcs-plat.c5
-rw-r--r--drivers/net/pcs/pcs-xpcs.c136
-rw-r--r--drivers/net/phy/Kconfig2
-rw-r--r--drivers/net/phy/adin1100.c7
-rw-r--r--drivers/net/phy/aquantia/aquantia_firmware.c2
-rw-r--r--drivers/net/phy/bcm-phy-ptp.c21
-rw-r--r--drivers/net/phy/dp83640.c29
-rw-r--r--drivers/net/phy/dp83867.c42
-rw-r--r--drivers/net/phy/dp83869.c4
-rw-r--r--drivers/net/phy/dp83td510.c62
-rw-r--r--drivers/net/phy/fixed_phy.c51
-rw-r--r--drivers/net/phy/mdio-open-alliance.h49
-rw-r--r--drivers/net/phy/mdio-private.h11
-rw-r--r--drivers/net/phy/mdio_bus.c93
-rw-r--r--drivers/net/phy/mdio_bus_provider.c13
-rw-r--r--drivers/net/phy/mdio_device.c60
-rw-r--r--drivers/net/phy/micrel.c379
-rw-r--r--drivers/net/phy/microchip_rds_ptp.c8
-rw-r--r--drivers/net/phy/microchip_t1s.c100
-rw-r--r--drivers/net/phy/motorcomm.c3
-rw-r--r--drivers/net/phy/mscc/mscc.h12
-rw-r--r--drivers/net/phy/mscc/mscc_main.c466
-rw-r--r--drivers/net/phy/mscc/mscc_ptp.c21
-rw-r--r--drivers/net/phy/mxl-gpy.c135
-rw-r--r--drivers/net/phy/nxp-c45-tja11xx.c22
-rw-r--r--drivers/net/phy/phy-c45.c287
-rw-r--r--drivers/net/phy/phy-caps.h1
-rw-r--r--drivers/net/phy/phy-core.c47
-rw-r--r--drivers/net/phy/phy.c14
-rw-r--r--drivers/net/phy/phy_caps.c2
-rw-r--r--drivers/net/phy/phy_device.c46
-rw-r--r--drivers/net/phy/phylink.c95
-rw-r--r--drivers/net/phy/qt2025.rs10
-rw-r--r--drivers/net/phy/realtek/realtek_main.c418
-rw-r--r--drivers/net/ppp/pppoe.c4
-rw-r--r--drivers/net/ppp/pptp.c8
-rw-r--r--drivers/net/pse-pd/pd692x0.c155
-rw-r--r--drivers/net/pse-pd/tps23881.c69
-rw-r--r--drivers/net/sungem_phy.c2
-rw-r--r--drivers/net/team/team_core.c109
-rw-r--r--drivers/net/team/team_nl.c1
-rw-r--r--drivers/net/team/team_nl.h1
-rw-r--r--drivers/net/tun_vnet.h2
-rw-r--r--drivers/net/usb/asix_devices.c12
-rw-r--r--drivers/net/usb/qmi_wwan.c6
-rw-r--r--drivers/net/usb/r8152.c1
-rw-r--r--drivers/net/usb/rtl8150.c11
-rw-r--r--drivers/net/usb/usbnet.c295
-rw-r--r--drivers/net/veth.c45
-rw-r--r--drivers/net/virtio_net.c116
-rw-r--r--drivers/net/vmxnet3/vmxnet3_ethtool.c18
-rw-r--r--drivers/net/vxlan/vxlan_core.c18
-rw-r--r--drivers/net/vxlan/vxlan_private.h2
-rw-r--r--drivers/net/wan/framer/pef2256/pef2256.c7
-rw-r--r--drivers/net/wan/hdlc_ppp.c4
-rw-r--r--drivers/net/wireguard/Makefile2
-rw-r--r--drivers/net/wireguard/cookie.c18
-rw-r--r--drivers/net/wireguard/generated/netlink.c73
-rw-r--r--drivers/net/wireguard/generated/netlink.h30
-rw-r--r--drivers/net/wireguard/netlink.c68
-rw-r--r--drivers/net/wireguard/noise.c32
-rw-r--r--drivers/net/wireless/ath/ath10k/core.c28
-rw-r--r--drivers/net/wireless/ath/ath10k/core.h6
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/qmi.c2
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode.c253
-rw-r--r--drivers/net/wireless/ath/ath10k/testmode_i.h15
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.c40
-rw-r--r--drivers/net/wireless/ath/ath10k/wmi.h19
-rw-r--r--drivers/net/wireless/ath/ath11k/core.c54
-rw-r--r--drivers/net/wireless/ath/ath11k/hal.h38
-rw-r--r--drivers/net/wireless/ath/ath11k/mac.c465
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.c20
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.h18
-rw-r--r--drivers/net/wireless/ath/ath11k/qmi.c2
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.c23
-rw-r--r--drivers/net/wireless/ath/ath11k/wmi.h18
-rw-r--r--drivers/net/wireless/ath/ath12k/core.c24
-rw-r--r--drivers/net/wireless/ath/ath12k/core.h4
-rw-r--r--drivers/net/wireless/ath/ath12k/debugfs.c14
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_mon.c19
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_rx.c74
-rw-r--r--drivers/net/wireless/ath/ath12k/hal_rx.c10
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c926
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.h14
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.c24
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.c13
-rw-r--r--drivers/net/wireless/ath/ath12k/qmi.h5
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.c98
-rw-r--r--drivers/net/wireless/ath/ath12k/wmi.h55
-rw-r--r--drivers/net/wireless/ath/ath12k/wow.c1
-rw-r--r--drivers/net/wireless/ath/wcn36xx/hal.h74
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.c60
-rw-r--r--drivers/net/wireless/ath/wcn36xx/smd.h1
-rw-r--r--drivers/net/wireless/ath/wil6210/pm.c1
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c3
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c14
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c28
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h3
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2100.c6
-rw-r--r--drivers/net/wireless/intel/ipw2x00/ipw2200.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/22000.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/8000.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/9000.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/ax210.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/bz.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/dr.c3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/cfg/sc.c3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/alive.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/coex.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/commands.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/debug.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/location.h8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h134
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/power.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/rx.h286
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/scan.h78
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/sta.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/stats.h39
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/tx.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/error-dump.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/file.h74
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/img.h12
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/regulatory.c26
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/runtime.h22
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-config.h11
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.c29
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-drv.h9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-modparams.h4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h17
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.c8
-rw-r--r--drivers/net/wireless/intel/iwlwifi/iwl-trans.h6
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/constants.h2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/d3.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/fw.c14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/iface.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/link.c28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mac80211.c103
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.c1
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mld.h25
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/mlo.c100
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/notif.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/roc.c4
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.c1691
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/rx.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mld/sta.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/fw.c15
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c13
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/mvm.h5
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c24
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.c164
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rs.h3
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/rx.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/time-event.c14
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/utils.c176
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/drv.c10
-rw-r--r--drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c9
-rw-r--r--drivers/net/wireless/intel/iwlwifi/tests/devinfo.c29
-rw-r--r--drivers/net/wireless/marvell/mwl8k.c71
-rw-r--r--drivers/net/wireless/mediatek/mt76/Kconfig6
-rw-r--r--drivers/net/wireless/mediatek/mt76/Makefile3
-rw-r--r--drivers/net/wireless/mediatek/mt76/agg-rx.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/channel.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/debugfs.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.c75
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.h69
-rw-r--r--drivers/net/wireless/mediatek/mt76/eeprom.c77
-rw-r--r--drivers/net/wireless/mediatek/mt76/mac80211.c10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mmio.c14
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76.h159
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/Kconfig2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/beacon.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/core.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/dma.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/main.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7603/soc.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/Kconfig2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/dma.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/main.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.c6
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mmio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/sdio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/soc.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/testmode.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/usb.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c21
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_dma.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_phy.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_phy.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_trace.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x02_util.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/phy.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/Kconfig2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/coredump.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/coredump.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c76
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/dma.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/init.c9
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mac.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/main.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.c174
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mcu.h8
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mmio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/soc.c23
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/testmode.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/testmode.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/Kconfig2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/Makefile2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/init.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/main.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mcu.c4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/testmode.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7921/usb.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/Kconfig2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/Makefile4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/init.c152
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mac.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/main.c40
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.c99
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.h10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h11
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci.c5
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regd.c265
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regd.h19
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/testmode.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/usb.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x.h4
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_core.c3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_dma.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_mac.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_trace.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt792x_usb.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Kconfig9
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Makefile3
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/coredump.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/coredump.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c74
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/dma.c33
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/init.c34
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.c62
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mac.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/main.c153
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.c74
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mcu.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mmio.c16
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h38
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/npu.c352
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/pci.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/regs.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/npu.c501
-rw-r--r--drivers/net/wireless/mediatek/mt76/pci.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/scan.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/sdio.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/sdio.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/sdio_txrx.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/testmode.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/testmode.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/trace.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c8
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb_trace.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/usb_trace.h2
-rw-r--r--drivers/net/wireless/mediatek/mt76/util.c2
-rw-r--r--drivers/net/wireless/mediatek/mt76/util.h3
-rw-r--r--drivers/net/wireless/mediatek/mt76/wed.c12
-rw-r--r--drivers/net/wireless/quantenna/qtnfmac/core.c3
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.c35
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800lib.h2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800pci.c3
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2800soc.c6
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00.h2
-rw-r--r--drivers/net/wireless/ralink/rt2x00/rt2x00dev.c10
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c9
-rw-r--r--drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c27
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8192c.c80
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/8723a.c115
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/core.c188
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/regs.h1
-rw-r--r--drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h1
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/base.c2
-rw-r--r--drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/bf.c8
-rw-r--r--drivers/net/wireless/realtek/rtw88/bf.h7
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822bu.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/rtw8822cu.c2
-rw-r--r--drivers/net/wireless/realtek/rtw88/usb.c3
-rw-r--r--drivers/net/wireless/realtek/rtw89/Kconfig22
-rw-r--r--drivers/net/wireless/realtek/rtw89/Makefile6
-rw-r--r--drivers/net/wireless/realtek/rtw89/cam.c173
-rw-r--r--drivers/net/wireless/realtek/rtw89/cam.h446
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.c231
-rw-r--r--drivers/net/wireless/realtek/rtw89/core.h104
-rw-r--r--drivers/net/wireless/realtek/rtw89/debug.c299
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.c176
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.h67
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.c200
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac.h114
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac80211.c89
-rw-r--r--drivers/net/wireless/realtek/rtw89/mac_be.c9
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.c18
-rw-r--r--drivers/net/wireless/realtek/rtw89/pci.h4
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy.c65
-rw-r--r--drivers/net/wireless/realtek/rtw89/phy_be.c4
-rw-r--r--drivers/net/wireless/realtek/rtw89/ps.c23
-rw-r--r--drivers/net/wireless/realtek/rtw89/reg.h24
-rw-r--r--drivers/net/wireless/realtek/rtw89/regd.c22
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b.c5
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c8
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8851bu.c24
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a.c85
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c16
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852au.c79
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b.c5
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_common.c6
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c6
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bt.c5
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852bu.c24
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c.c170
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c.h2
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c69
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8852cu.c69
-rw-r--r--drivers/net/wireless/realtek/rtw89/rtw8922a.c17
-rw-r--r--drivers/net/wireless/realtek/rtw89/txrx.h7
-rw-r--r--drivers/net/wireless/realtek/rtw89/usb.c115
-rw-r--r--drivers/net/wireless/realtek/rtw89/usb.h12
-rw-r--r--drivers/net/wireless/realtek/rtw89/wow.c8
-rw-r--r--drivers/net/wireless/silabs/wfx/main.c2
-rw-r--r--drivers/net/wireless/st/cw1200/bh.c11
-rw-r--r--drivers/net/wireless/ti/wl18xx/debugfs.c3
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.c1
-rw-r--r--drivers/net/wireless/ti/wlcore/debugfs.c11
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c36
-rw-r--r--drivers/net/wireless/ti/wlcore/scan.c1
-rw-r--r--drivers/net/wireless/ti/wlcore/sysfs.c1
-rw-r--r--drivers/net/wireless/ti/wlcore/testmode.c2
-rw-r--r--drivers/net/wireless/ti/wlcore/tx.c1
-rw-r--r--drivers/net/wireless/ti/wlcore/vendor_cmd.c3
-rw-r--r--drivers/net/wireless/virtual/mac80211_hwsim.c22
-rw-r--r--drivers/net/wireless/zydas/zd1211rw/zd_usb.c1
-rw-r--r--drivers/net/wwan/iosm/iosm_ipc_devlink.c3
-rw-r--r--drivers/net/wwan/mhi_wwan_mbim.c19
-rw-r--r--drivers/net/wwan/qcom_bam_dmux.c2
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_cldma.c5
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_cldma.h2
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c2
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c2
-rw-r--r--drivers/net/xen-netfront.c5
-rw-r--r--drivers/nfc/mei_phy.h4
-rw-r--r--drivers/nvme/host/apple.c1
-rw-r--r--drivers/nvme/host/core.c18
-rw-r--r--drivers/nvme/host/fabrics.h6
-rw-r--r--drivers/nvme/host/fc.c16
-rw-r--r--drivers/nvme/host/ioctl.c7
-rw-r--r--drivers/nvme/host/multipath.c6
-rw-r--r--drivers/nvme/host/nvme.h9
-rw-r--r--drivers/nvme/host/pci.c131
-rw-r--r--drivers/nvme/host/rdma.c1
-rw-r--r--drivers/nvme/host/tcp.c5
-rw-r--r--drivers/nvme/host/zns.c10
-rw-r--r--drivers/nvme/target/auth.c3
-rw-r--r--drivers/nvme/target/fabrics-cmd-auth.c1
-rw-r--r--drivers/nvme/target/loop.c1
-rw-r--r--drivers/nvme/target/nvmet.h1
-rw-r--r--drivers/nvme/target/tcp.c2
-rw-r--r--drivers/nvmem/layouts.c2
-rw-r--r--drivers/nvmem/rcar-efuse.c1
-rw-r--r--drivers/of/address.c4
-rw-r--r--drivers/of/base.c47
-rw-r--r--drivers/of/fdt.c101
-rw-r--r--drivers/of/irq.c68
-rw-r--r--drivers/of/of_kunit_helpers.c5
-rw-r--r--drivers/of/of_reserved_mem.c69
-rw-r--r--drivers/of/overlay.c3
-rw-r--r--drivers/opp/core.c69
-rw-r--r--drivers/opp/cpu.c16
-rw-r--r--drivers/opp/of.c125
-rw-r--r--drivers/pci/Makefile2
-rw-r--r--drivers/pci/bus.c3
-rw-r--r--drivers/pci/controller/Kconfig18
-rw-r--r--drivers/pci/controller/Makefile1
-rw-r--r--drivers/pci/controller/cadence/Kconfig21
-rw-r--r--drivers/pci/controller/cadence/Makefile11
-rw-r--r--drivers/pci/controller/cadence/pci-j721e.c33
-rw-r--r--drivers/pci/controller/cadence/pci-sky1.c238
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-common.c288
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-common.h46
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-hpa.c368
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host.c278
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h193
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-hpa.c167
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-lga-regs.h230
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-plat.c9
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.c12
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.h409
-rw-r--r--drivers/pci/controller/cadence/pcie-sg2042.c3
-rw-r--r--drivers/pci/controller/dwc/Kconfig38
-rw-r--r--drivers/pci/controller/dwc/Makefile5
-rw-r--r--drivers/pci/controller/dwc/pci-keystone.c80
-rw-r--r--drivers/pci/controller/dwc/pci-meson.c18
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-ep.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-host.c40
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.c36
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h21
-rw-r--r--drivers/pci/controller/dwc/pcie-dw-rockchip.c63
-rw-r--r--drivers/pci/controller/dwc/pcie-nxp-s32g.c406
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c132
-rw-r--r--drivers/pci/controller/dwc/pcie-spacemit-k1.c357
-rw-r--r--drivers/pci/controller/dwc/pcie-stm32-ep.c43
-rw-r--r--drivers/pci/controller/dwc/pcie-stm32.c14
-rw-r--r--drivers/pci/controller/dwc/pcie-stm32.h3
-rw-r--r--drivers/pci/controller/dwc/pcie-tegra194.c48
-rw-r--r--drivers/pci/controller/pci-host-common.c13
-rw-r--r--drivers/pci/controller/pci-host-common.h1
-rw-r--r--drivers/pci/controller/pci-hyperv.c62
-rw-r--r--drivers/pci/controller/pci-ixp4xx.c6
-rw-r--r--drivers/pci/controller/pcie-apple.c43
-rw-r--r--drivers/pci/controller/pcie-brcmstb.c209
-rw-r--r--drivers/pci/controller/pcie-iproc.c22
-rw-r--r--drivers/pci/controller/pcie-mediatek.c113
-rw-r--r--drivers/pci/controller/pcie-rzg3s-host.c1761
-rw-r--r--drivers/pci/controller/vmd.c40
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-test.c10
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-vntb.c153
-rw-r--r--drivers/pci/endpoint/pci-epf-core.c159
-rw-r--r--drivers/pci/host-bridge.c1
-rw-r--r--drivers/pci/hotplug/s390_pci_hpc.c3
-rw-r--r--drivers/pci/iov.c25
-rw-r--r--drivers/pci/msi/irqdomain.c90
-rw-r--r--drivers/pci/p2pdma.c186
-rw-r--r--drivers/pci/pci-driver.c6
-rw-r--r--drivers/pci/pci-sysfs.c23
-rw-r--r--drivers/pci/pci.c172
-rw-r--r--drivers/pci/pci.h16
-rw-r--r--drivers/pci/pcie/aspm.c59
-rw-r--r--drivers/pci/pcie/portdrv.c1
-rw-r--r--drivers/pci/pcie/ptm.c23
-rw-r--r--drivers/pci/probe.c20
-rw-r--r--drivers/pci/pwrctrl/Kconfig15
-rw-r--r--drivers/pci/pwrctrl/Makefile2
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c648
-rw-r--r--drivers/pci/quirks.c42
-rw-r--r--drivers/pci/rebar.c328
-rw-r--r--drivers/pci/setup-bus.c128
-rw-r--r--drivers/pci/setup-res.c78
-rw-r--r--drivers/pci/tph.c16
-rw-r--r--drivers/peci/cpu.c4
-rw-r--r--drivers/perf/arm-ni.c97
-rw-r--r--drivers/perf/arm_cspmu/arm_cspmu.c52
-rw-r--r--drivers/perf/arm_cspmu/arm_cspmu.h39
-rw-r--r--drivers/perf/arm_cspmu/nvidia_cspmu.c194
-rw-r--r--drivers/perf/arm_pmu.c55
-rw-r--r--drivers/perf/arm_pmu_acpi.c2
-rw-r--r--drivers/perf/arm_pmu_platform.c20
-rw-r--r--drivers/perf/arm_pmuv3.c26
-rw-r--r--drivers/perf/arm_spe_pmu.c50
-rw-r--r--drivers/perf/fsl_imx8_ddr_perf.c93
-rw-r--r--drivers/perf/riscv_pmu_sbi.c2
-rw-r--r--drivers/pinctrl/cirrus/pinctrl-cs42l43.c23
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8189.c4
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8196.c6
-rw-r--r--drivers/pinctrl/nxp/pinctrl-s32cc.c3
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm.c2
-rw-r--r--drivers/pinctrl/realtek/Kconfig1
-rw-r--r--drivers/platform/arm64/lenovo-thinkpad-t14s.c16
-rw-r--r--drivers/platform/chrome/cros_ec_ishtp.c1
-rw-r--r--drivers/platform/chrome/cros_ec_lightbar.c16
-rw-r--r--drivers/platform/chrome/cros_ec_sensorhub_ring.c11
-rw-r--r--drivers/platform/chrome/cros_usbpd_notify.c17
-rw-r--r--drivers/platform/mellanox/mlxbf-pmc.c1
-rw-r--r--drivers/platform/surface/surface_aggregator_registry.c13
-rw-r--r--drivers/platform/x86/Kconfig3
-rw-r--r--drivers/platform/x86/acer-wmi.c4
-rw-r--r--drivers/platform/x86/amd/pmc/pmc-quirks.c25
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.c3
-rw-r--r--drivers/platform/x86/amd/pmc/pmc.h1
-rw-r--r--drivers/platform/x86/dell/alienware-wmi-wmax.c102
-rw-r--r--drivers/platform/x86/dell/dell-wmi-base.c12
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c6
-rw-r--r--drivers/platform/x86/huawei-wmi.c4
-rw-r--r--drivers/platform/x86/intel/Kconfig13
-rw-r--r--drivers/platform/x86/intel/Makefile1
-rw-r--r--drivers/platform/x86/intel/chtwc_int33fe.c29
-rw-r--r--drivers/platform/x86/intel/ehl_pse_io.c86
-rw-r--r--drivers/platform/x86/intel/hid.c1
-rw-r--r--drivers/platform/x86/intel/int3472/clk_and_regulator.c5
-rw-r--r--drivers/platform/x86/intel/int3472/led.c2
-rw-r--r--drivers/platform/x86/intel/punit_ipc.c2
-rw-r--r--drivers/platform/x86/intel/speed_select_if/isst_if_mmio.c4
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h9
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c4
-rw-r--r--drivers/platform/x86/msi-wmi-platform.c43
-rw-r--r--drivers/pmdomain/arm/scmi_pm_domain.c13
-rw-r--r--drivers/pmdomain/bcm/bcm2835-power.c17
-rw-r--r--drivers/pmdomain/core.c16
-rw-r--r--drivers/pmdomain/governor.c49
-rw-r--r--drivers/pmdomain/imx/gpc.c2
-rw-r--r--drivers/pmdomain/mediatek/Kconfig17
-rw-r--r--drivers/pmdomain/mediatek/Makefile1
-rw-r--r--drivers/pmdomain/mediatek/mt8196-pm-domains.h625
-rw-r--r--drivers/pmdomain/mediatek/mtk-mfg-pmdomain.c1044
-rw-r--r--drivers/pmdomain/mediatek/mtk-pm-domains.c324
-rw-r--r--drivers/pmdomain/mediatek/mtk-pm-domains.h49
-rw-r--r--drivers/pmdomain/qcom/rpmhpd.c28
-rw-r--r--drivers/pmdomain/rockchip/pm-domains.c41
-rw-r--r--drivers/pmdomain/samsung/exynos-pm-domains.c29
-rw-r--r--drivers/pmdomain/tegra/powergate-bpmp.c1
-rw-r--r--drivers/pnp/driver.c19
-rw-r--r--drivers/power/reset/Kconfig9
-rw-r--r--drivers/power/reset/Makefile1
-rw-r--r--drivers/power/reset/spacemit-p1-reboot.c88
-rw-r--r--drivers/power/supply/Kconfig35
-rw-r--r--drivers/power/supply/Makefile3
-rw-r--r--drivers/power/supply/apm_power.c3
-rw-r--r--drivers/power/supply/bd71828-power.c1049
-rw-r--r--drivers/power/supply/cw2015_battery.c8
-rw-r--r--drivers/power/supply/intel_dc_ti_battery.c10
-rw-r--r--drivers/power/supply/max17040_battery.c6
-rw-r--r--drivers/power/supply/max77705_charger.c56
-rw-r--r--drivers/power/supply/pf1550-charger.c641
-rw-r--r--drivers/power/supply/qcom_battmgr.c14
-rw-r--r--drivers/power/supply/rt5033_charger.c2
-rw-r--r--drivers/power/supply/rt9467-charger.c6
-rw-r--r--drivers/power/supply/rt9756.c955
-rw-r--r--drivers/power/supply/wm831x_power.c10
-rw-r--r--drivers/powercap/dtpm.c16
-rw-r--r--drivers/powercap/intel_rapl_common.c39
-rw-r--r--drivers/powercap/intel_rapl_msr.c43
-rw-r--r--drivers/powercap/intel_rapl_tpmi.c2
-rw-r--r--drivers/pps/generators/pps_gen_parport.c3
-rw-r--r--drivers/pps/kapi.c3
-rw-r--r--drivers/ptp/ptp_chardev.c4
-rw-r--r--drivers/ptp/ptp_clock.c4
-rw-r--r--drivers/ptp/ptp_ines.c31
-rw-r--r--drivers/ptp/ptp_ocp.c67
-rw-r--r--drivers/pwm/Kconfig33
-rw-r--r--drivers/pwm/Makefile2
-rw-r--r--drivers/pwm/core.c8
-rw-r--r--drivers/pwm/pwm-adp5585.c4
-rw-r--r--drivers/pwm/pwm-airoha.c622
-rw-r--r--drivers/pwm/pwm-bcm2835.c28
-rw-r--r--drivers/pwm/pwm-max7360.c2
-rw-r--r--drivers/pwm/pwm-mediatek.c285
-rw-r--r--drivers/pwm/pwm-rzg2l-gpt.c15
-rw-r--r--drivers/pwm/pwm_th1520.rs387
-rw-r--r--drivers/ras/amd/atl/core.c7
-rw-r--r--drivers/ras/amd/atl/internal.h6
-rw-r--r--drivers/ras/amd/atl/prm.c4
-rw-r--r--drivers/ras/amd/atl/system.c30
-rw-r--r--drivers/ras/amd/atl/umc.c23
-rw-r--r--drivers/ras/cec.c2
-rw-r--r--drivers/ras/ras.c40
-rw-r--r--drivers/regulator/Kconfig41
-rw-r--r--drivers/regulator/Makefile4
-rw-r--r--drivers/regulator/arizona-micsupp.c8
-rw-r--r--drivers/regulator/bd71815-regulator.c8
-rw-r--r--drivers/regulator/bd71828-regulator.c4
-rw-r--r--drivers/regulator/bd718x7-regulator.c6
-rw-r--r--drivers/regulator/bd96801-regulator.c10
-rw-r--r--drivers/regulator/core.c169
-rw-r--r--drivers/regulator/fixed.c1
-rw-r--r--drivers/regulator/fp9931.c551
-rw-r--r--drivers/regulator/hi6421-regulator.c10
-rw-r--r--drivers/regulator/hi6421v530-regulator.c4
-rw-r--r--drivers/regulator/hi6421v600-regulator.c6
-rw-r--r--drivers/regulator/irq_helpers.c2
-rw-r--r--drivers/regulator/max77650-regulator.c6
-rw-r--r--drivers/regulator/mt6315-regulator.c6
-rw-r--r--drivers/regulator/mt6316-regulator.c345
-rw-r--r--drivers/regulator/mt6358-regulator.c2
-rw-r--r--drivers/regulator/mt6363-regulator.c938
-rw-r--r--drivers/regulator/of_regulator.c6
-rw-r--r--drivers/regulator/pca9450-regulator.c203
-rw-r--r--drivers/regulator/pf1550-regulator.c429
-rw-r--r--drivers/regulator/pf9453-regulator.c42
-rw-r--r--drivers/regulator/qcom-labibb-regulator.c4
-rw-r--r--drivers/regulator/qcom-rpmh-regulator.c1338
-rw-r--r--drivers/regulator/renesas-usb-vbus-regulator.c2
-rw-r--r--drivers/regulator/rtq2208-regulator.c6
-rw-r--r--drivers/regulator/sy7636a-regulator.c27
-rw-r--r--drivers/resctrl/Kconfig24
-rw-r--r--drivers/resctrl/Makefile4
-rw-r--r--drivers/resctrl/mpam_devices.c2723
-rw-r--r--drivers/resctrl/mpam_internal.h658
-rw-r--r--drivers/resctrl/test_mpam_devices.c389
-rw-r--r--drivers/reset/Kconfig1
-rw-r--r--drivers/reset/core.c138
-rw-r--r--drivers/reset/reset-gpio.c19
-rw-r--r--drivers/reset/reset-imx8mp-audiomix.c4
-rw-r--r--drivers/rtc/rtc-cpcap.c1
-rw-r--r--drivers/rtc/rtc-rx8025.c2
-rw-r--r--drivers/rtc/rtc-tps6586x.c1
-rw-r--r--drivers/s390/block/dasd.c68
-rw-r--r--drivers/s390/block/dasd_devmap.c3
-rw-r--r--drivers/s390/block/dasd_eckd.c19
-rw-r--r--drivers/s390/block/dasd_fba.c1
-rw-r--r--drivers/s390/block/dasd_genhd.c80
-rw-r--r--drivers/s390/block/dasd_ioctl.c6
-rw-r--r--drivers/s390/block/dcssblk.c7
-rw-r--r--drivers/s390/block/scm_blk.c3
-rw-r--r--drivers/s390/block/scm_drv.c3
-rw-r--r--drivers/s390/char/con3270.c21
-rw-r--r--drivers/s390/char/diag_ftp.c3
-rw-r--r--drivers/s390/char/fs3270.c7
-rw-r--r--drivers/s390/char/hmcdrv_cache.c3
-rw-r--r--drivers/s390/char/hmcdrv_dev.c3
-rw-r--r--drivers/s390/char/hmcdrv_ftp.c3
-rw-r--r--drivers/s390/char/hmcdrv_mod.c3
-rw-r--r--drivers/s390/char/monreader.c3
-rw-r--r--drivers/s390/char/monwriter.c3
-rw-r--r--drivers/s390/char/sclp_ap.c3
-rw-r--r--drivers/s390/char/sclp_cmd.c3
-rw-r--r--drivers/s390/char/sclp_config.c3
-rw-r--r--drivers/s390/char/sclp_cpi_sys.c3
-rw-r--r--drivers/s390/char/sclp_ctl.c12
-rw-r--r--drivers/s390/char/sclp_early.c3
-rw-r--r--drivers/s390/char/sclp_ftp.c3
-rw-r--r--drivers/s390/char/sclp_mem.c292
-rw-r--r--drivers/s390/char/sclp_ocf.c3
-rw-r--r--drivers/s390/char/sclp_pci.c3
-rw-r--r--drivers/s390/char/sclp_sd.c3
-rw-r--r--drivers/s390/char/sclp_sdias.c3
-rw-r--r--drivers/s390/char/tape.h21
-rw-r--r--drivers/s390/char/tape_34xx.c31
-rw-r--r--drivers/s390/char/tape_3590.c92
-rw-r--r--drivers/s390/char/tape_char.c142
-rw-r--r--drivers/s390/char/tape_class.c3
-rw-r--r--drivers/s390/char/tape_core.c38
-rw-r--r--drivers/s390/char/tape_proc.c3
-rw-r--r--drivers/s390/char/tape_std.c83
-rw-r--r--drivers/s390/char/tape_std.h9
-rw-r--r--drivers/s390/char/vmcp.c7
-rw-r--r--drivers/s390/char/vmlogrdr.c3
-rw-r--r--drivers/s390/char/vmur.c3
-rw-r--r--drivers/s390/char/zcore.c3
-rw-r--r--drivers/s390/cio/blacklist.c3
-rw-r--r--drivers/s390/cio/ccwgroup.c6
-rw-r--r--drivers/s390/cio/ccwreq.c3
-rw-r--r--drivers/s390/cio/chp.c5
-rw-r--r--drivers/s390/cio/chsc.c13
-rw-r--r--drivers/s390/cio/chsc_sch.c7
-rw-r--r--drivers/s390/cio/cio.c5
-rw-r--r--drivers/s390/cio/cio_inject.c3
-rw-r--r--drivers/s390/cio/cmf.c3
-rw-r--r--drivers/s390/cio/css.c3
-rw-r--r--drivers/s390/cio/device.c3
-rw-r--r--drivers/s390/cio/device_status.c2
-rw-r--r--drivers/s390/cio/vfio_ccw_ops.c47
-rw-r--r--drivers/s390/crypto/ap_bus.c196
-rw-r--r--drivers/s390/crypto/ap_bus.h5
-rw-r--r--drivers/s390/crypto/ap_card.c3
-rw-r--r--drivers/s390/crypto/ap_queue.c75
-rw-r--r--drivers/s390/crypto/pkey_api.c3
-rw-r--r--drivers/s390/crypto/pkey_base.c3
-rw-r--r--drivers/s390/crypto/pkey_cca.c3
-rw-r--r--drivers/s390/crypto/pkey_ep11.c3
-rw-r--r--drivers/s390/crypto/pkey_pckmo.c3
-rw-r--r--drivers/s390/crypto/pkey_sysfs.c3
-rw-r--r--drivers/s390/crypto/pkey_uv.c3
-rw-r--r--drivers/s390/crypto/vfio_ap_ops.c14
-rw-r--r--drivers/s390/crypto/zcrypt_api.c257
-rw-r--r--drivers/s390/crypto/zcrypt_card.c1
-rw-r--r--drivers/s390/crypto/zcrypt_ccamisc.c3
-rw-r--r--drivers/s390/crypto/zcrypt_ep11misc.c3
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c3
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c3
-rw-r--r--drivers/s390/crypto/zcrypt_queue.c1
-rw-r--r--drivers/s390/net/ctcm_fsms.c17
-rw-r--r--drivers/s390/net/ctcm_main.c3
-rw-r--r--drivers/s390/net/ctcm_mpc.c4
-rw-r--r--drivers/s390/net/ctcm_sysfs.c3
-rw-r--r--drivers/s390/net/ism_drv.c3
-rw-r--r--drivers/s390/net/qeth_core_main.c9
-rw-r--r--drivers/s390/net/qeth_core_mpc.c247
-rw-r--r--drivers/s390/net/qeth_core_mpc.h20
-rw-r--r--drivers/s390/net/qeth_core_sys.c3
-rw-r--r--drivers/s390/net/qeth_ethtool.c3
-rw-r--r--drivers/s390/net/qeth_l2_main.c3
-rw-r--r--drivers/s390/net/qeth_l3_main.c3
-rw-r--r--drivers/s390/net/smsgiucv_app.c12
-rw-r--r--drivers/s390/scsi/zfcp_aux.c3
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c3
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c3
-rw-r--r--drivers/s390/scsi/zfcp_erp.c3
-rw-r--r--drivers/s390/scsi/zfcp_fc.c3
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c3
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c3
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c3
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c3
-rw-r--r--drivers/scsi/bfa/bfad.c1
-rw-r--r--drivers/scsi/csiostor/csio_init.c1
-rw-r--r--drivers/scsi/fnic/fnic_trace.c57
-rw-r--r--drivers/scsi/hosts.c5
-rw-r--r--drivers/scsi/ipr.c1
-rw-r--r--drivers/scsi/libfc/fc_fcp.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c6
-rw-r--r--drivers/scsi/mesh.c1
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c5
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c13
-rw-r--r--drivers/scsi/scsi_error.c4
-rw-r--r--drivers/scsi/sd.h2
-rw-r--r--drivers/scsi/sd_zbc.c20
-rw-r--r--drivers/scsi/sg.c10
-rw-r--r--drivers/scsi/snic/snic_debugfs.c10
-rw-r--r--drivers/scsi/snic/snic_trc.c5
-rw-r--r--drivers/scsi/stex.c1
-rw-r--r--drivers/scsi/storvsc_drv.c96
-rw-r--r--drivers/slimbus/qcom-ngd-ctrl.c3
-rw-r--r--drivers/soc/qcom/ubwc_config.c14
-rw-r--r--drivers/soc/tegra/common.c12
-rw-r--r--drivers/soc/ti/knav_dma.c14
-rw-r--r--drivers/spi/Kconfig35
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/spi-airoha-snfi.c538
-rw-r--r--drivers/spi/spi-amlogic-spifc-a1.c4
-rw-r--r--drivers/spi/spi-amlogic-spifc-a4.c4
-rw-r--r--drivers/spi/spi-aspeed-smc.c747
-rw-r--r--drivers/spi/spi-bcm63xx.c18
-rw-r--r--drivers/spi/spi-cadence-quadspi.c17
-rw-r--r--drivers/spi/spi-cadence.c106
-rw-r--r--drivers/spi/spi-ch341.c2
-rw-r--r--drivers/spi/spi-cs42l43.c40
-rw-r--r--drivers/spi/spi-davinci.c64
-rw-r--r--drivers/spi/spi-dw-bt1.c4
-rw-r--r--drivers/spi/spi-dw-core.c188
-rw-r--r--drivers/spi/spi-dw-dma.c22
-rw-r--r--drivers/spi/spi-dw-mmio.c13
-rw-r--r--drivers/spi/spi-dw-pci.c8
-rw-r--r--drivers/spi/spi-dw.h12
-rw-r--r--drivers/spi/spi-fsl-lpspi.c8
-rw-r--r--drivers/spi/spi-fsl-qspi.c88
-rw-r--r--drivers/spi/spi-imx.c73
-rw-r--r--drivers/spi/spi-intel-pci.c3
-rw-r--r--drivers/spi/spi-intel.c6
-rw-r--r--drivers/spi/spi-mem.c5
-rw-r--r--drivers/spi/spi-microchip-core-spi.c429
-rw-r--r--drivers/spi/spi-mpfs.c (renamed from drivers/spi/spi-microchip-core.c)207
-rw-r--r--drivers/spi/spi-nxp-fspi.c42
-rw-r--r--drivers/spi/spi-offload-trigger-pwm.c3
-rw-r--r--drivers/spi/spi-qpic-snand.c2
-rw-r--r--drivers/spi/spi-rockchip-sfc.c12
-rw-r--r--drivers/spi/spi-rzv2h-rspi.c303
-rw-r--r--drivers/spi/spi-sg2044-nor.c4
-rw-r--r--drivers/spi/spi-tegra210-quad.c174
-rw-r--r--drivers/spi/spi-tle62x0.c2
-rw-r--r--drivers/spi/spi-xilinx.c2
-rw-r--r--drivers/spi/spi.c12
-rw-r--r--drivers/spi/spidev.c2
-rw-r--r--drivers/staging/gpib/agilent_82350b/agilent_82350b.c12
-rw-r--r--drivers/staging/gpib/fmh_gpib/fmh_gpib.c5
-rw-r--r--drivers/staging/gpib/ni_usb/ni_usb_gpib.c13
-rw-r--r--drivers/staging/greybus/audio_codec.c16
-rw-r--r--drivers/staging/greybus/audio_helper.c9
-rw-r--r--drivers/staging/greybus/audio_topology.c24
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c4
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2722.c6
-rw-r--r--drivers/staging/media/av7110/av7110.c2
-rw-r--r--drivers/staging/media/av7110/av7110_ca.c2
-rw-r--r--drivers/staging/media/av7110/av7110_v4l.c4
-rw-r--r--drivers/staging/media/imx/imx-media-csc-scaler.c2
-rw-r--r--drivers/staging/media/ipu3/ipu3.c3
-rw-r--r--drivers/staging/media/ipu3/ipu3.h1
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi-phy.c4
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2.c4
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-video.c7
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_dec.c2
-rw-r--r--drivers/staging/media/tegra-video/tegra20.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c2
-rw-r--r--drivers/target/loopback/tcm_loop.c3
-rw-r--r--drivers/target/target_core_configfs.c14
-rw-r--r--drivers/tee/qcomtee/Kconfig1
-rw-r--r--drivers/tee/qcomtee/call.c4
-rw-r--r--drivers/tee/qcomtee/core.c4
-rw-r--r--drivers/thermal/Kconfig10
-rw-r--r--drivers/thermal/Makefile1
-rw-r--r--drivers/thermal/imx91_thermal.c384
-rw-r--r--drivers/thermal/intel/Kconfig3
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c13
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3403_thermal.c1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.h2
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c10
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rapl.c2
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c15
-rw-r--r--drivers/thermal/renesas/rcar_gen3_thermal.c10
-rw-r--r--drivers/thermal/renesas/rcar_thermal.c8
-rw-r--r--drivers/thunderbolt/nhi.c2
-rw-r--r--drivers/thunderbolt/nhi.h1
-rw-r--r--drivers/tty/amiserial.c14
-rw-r--r--drivers/tty/pty.c51
-rw-r--r--drivers/tty/serial/8250/8250.h4
-rw-r--r--drivers/tty/serial/8250/8250_dw.c4
-rw-r--r--drivers/tty/serial/8250/8250_exar.c11
-rw-r--r--drivers/tty/serial/8250/8250_mtk.c6
-rw-r--r--drivers/tty/serial/8250/8250_pci.c1
-rw-r--r--drivers/tty/serial/8250/8250_platform.c2
-rw-r--r--drivers/tty/serial/8250/8250_rsa.c26
-rw-r--r--drivers/tty/serial/8250/Makefile2
-rw-r--r--drivers/tty/serial/amba-pl011.c2
-rw-r--r--drivers/tty/serial/icom.c8
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c1
-rw-r--r--drivers/tty/serial/kgdboc.c1
-rw-r--r--drivers/tty/serial/sc16is7xx.c7
-rw-r--r--drivers/tty/serial/sh-sci.c14
-rw-r--r--drivers/tty/synclink_gt.c20
-rw-r--r--drivers/ufs/core/ufs-sysfs.c2
-rw-r--r--drivers/ufs/core/ufs-sysfs.h1
-rw-r--r--drivers/ufs/core/ufshcd.c45
-rw-r--r--drivers/ufs/host/ufs-qcom.c15
-rw-r--r--drivers/ufs/host/ufshcd-pci.c70
-rw-r--r--drivers/usb/cdns3/cdns3-pci-wrap.c5
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c11
-rw-r--r--drivers/usb/chipidea/core.c3
-rw-r--r--drivers/usb/core/quirks.c2
-rw-r--r--drivers/usb/dwc3/core.c3
-rw-r--r--drivers/usb/dwc3/dwc3-generic-plat.c3
-rw-r--r--drivers/usb/dwc3/dwc3-imx8mp.c9
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c80
-rw-r--r--drivers/usb/dwc3/ep0.c1
-rw-r--r--drivers/usb/dwc3/gadget.c7
-rw-r--r--drivers/usb/gadget/function/f_eem.c7
-rw-r--r--drivers/usb/gadget/legacy/raw_gadget.c2
-rw-r--r--drivers/usb/gadget/udc/core.c17
-rw-r--r--drivers/usb/gadget/udc/renesas_usbf.c4
-rw-r--r--drivers/usb/host/sl811-hcd.c1
-rw-r--r--drivers/usb/host/xhci-dbgcap.c15
-rw-r--r--drivers/usb/host/xhci-dbgcap.h1
-rw-r--r--drivers/usb/host/xhci-dbgtty.c23
-rw-r--r--drivers/usb/host/xhci-pci.c3
-rw-r--r--drivers/usb/host/xhci-ring.c15
-rw-r--r--drivers/usb/host/xhci-sideband.c102
-rw-r--r--drivers/usb/host/xhci.c1
-rw-r--r--drivers/usb/misc/Kconfig1
-rw-r--r--drivers/usb/renesas_usbhs/common.c14
-rw-r--r--drivers/usb/serial/ftdi_sio.c1
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h1
-rw-r--r--drivers/usb/serial/option.c20
-rw-r--r--drivers/usb/storage/sddr55.c6
-rw-r--r--drivers/usb/storage/transport.c16
-rw-r--r--drivers/usb/storage/uas.c5
-rw-r--r--drivers/usb/storage/unusual_devs.h2
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c4
-rw-r--r--drivers/usb/typec/ucsi/psy.c5
-rw-r--r--drivers/vdpa/mlx5/net/mlx5_vnet.c8
-rw-r--r--drivers/vdpa/octeon_ep/octep_vdpa_main.c1
-rw-r--r--drivers/vdpa/pds/vdpa_dev.c2
-rw-r--r--drivers/vdpa/vdpa_user/vduse_dev.c3
-rw-r--r--drivers/vfio/cdx/main.c29
-rw-r--r--drivers/vfio/device_cdev.c2
-rw-r--r--drivers/vfio/fsl-mc/vfio_fsl_mc.c43
-rw-r--r--drivers/vfio/group.c28
-rw-r--r--drivers/vfio/pci/Kconfig5
-rw-r--r--drivers/vfio/pci/Makefile3
-rw-r--r--drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.c171
-rw-r--r--drivers/vfio/pci/hisilicon/hisi_acc_vfio_pci.h23
-rw-r--r--drivers/vfio/pci/mlx5/main.c1
-rw-r--r--drivers/vfio/pci/nvgrace-gpu/main.c342
-rw-r--r--drivers/vfio/pci/pds/vfio_dev.c1
-rw-r--r--drivers/vfio/pci/qat/main.c1
-rw-r--r--drivers/vfio/pci/vfio_pci.c6
-rw-r--r--drivers/vfio/pci/vfio_pci_config.c23
-rw-r--r--drivers/vfio/pci/vfio_pci_core.c300
-rw-r--r--drivers/vfio/pci/vfio_pci_dmabuf.c350
-rw-r--r--drivers/vfio/pci/vfio_pci_intrs.c52
-rw-r--r--drivers/vfio/pci/vfio_pci_priv.h28
-rw-r--r--drivers/vfio/pci/virtio/common.h5
-rw-r--r--drivers/vfio/pci/virtio/legacy_io.c38
-rw-r--r--drivers/vfio/pci/virtio/main.c5
-rw-r--r--drivers/vfio/pci/xe/Kconfig12
-rw-r--r--drivers/vfio/pci/xe/Makefile3
-rw-r--r--drivers/vfio/pci/xe/main.c573
-rw-r--r--drivers/vfio/platform/vfio_amba.c1
-rw-r--r--drivers/vfio/platform/vfio_platform.c1
-rw-r--r--drivers/vfio/platform/vfio_platform_common.c40
-rw-r--r--drivers/vfio/platform/vfio_platform_private.h3
-rw-r--r--drivers/vfio/vfio_iommu_type1.c173
-rw-r--r--drivers/vfio/vfio_main.c51
-rw-r--r--drivers/vhost/net.c82
-rw-r--r--drivers/vhost/scsi.c9
-rw-r--r--drivers/vhost/test.c10
-rw-r--r--drivers/vhost/vhost.c80
-rw-r--r--drivers/vhost/vhost.h52
-rw-r--r--drivers/vhost/vsock.c10
-rw-r--r--drivers/video/backlight/Kconfig9
-rw-r--r--drivers/video/backlight/Makefile1
-rw-r--r--drivers/video/backlight/aw99706.c471
-rw-r--r--drivers/video/backlight/led_bl.c13
-rw-r--r--drivers/video/fbdev/Kconfig8
-rw-r--r--drivers/video/fbdev/aty/atyfb_base.c8
-rw-r--r--drivers/video/fbdev/core/Kconfig2
-rw-r--r--drivers/video/fbdev/core/bitblit.c138
-rw-r--r--drivers/video/fbdev/core/fbcon.c487
-rw-r--r--drivers/video/fbdev/core/fbcon.h17
-rw-r--r--drivers/video/fbdev/core/fbcon_ccw.c151
-rw-r--r--drivers/video/fbdev/core/fbcon_cw.c151
-rw-r--r--drivers/video/fbdev/core/fbcon_rotate.c47
-rw-r--r--drivers/video/fbdev/core/fbcon_rotate.h18
-rw-r--r--drivers/video/fbdev/core/fbcon_ud.c167
-rw-r--r--drivers/video/fbdev/core/fbmem.c1
-rw-r--r--drivers/video/fbdev/core/softcursor.c18
-rw-r--r--drivers/video/fbdev/core/tileblit.c32
-rw-r--r--drivers/video/fbdev/pvr2fb.c2
-rw-r--r--drivers/video/fbdev/simplefb.c6
-rw-r--r--drivers/video/fbdev/valkyriefb.c2
-rw-r--r--drivers/virtio/virtio.c12
-rw-r--r--drivers/virtio/virtio_balloon.c3
-rw-r--r--drivers/virtio/virtio_debug.c10
-rw-r--r--drivers/virtio/virtio_pci_modern_dev.c6
-rw-r--r--drivers/virtio/virtio_ring.c7
-rw-r--r--drivers/virtio/virtio_vdpa.c2
-rw-r--r--drivers/watchdog/diag288_wdt.c3
-rw-r--r--drivers/xen/pvcalls-back.c4
-rw-r--r--drivers/xen/xenbus/xenbus_xs.c4
-rw-r--r--fs/9p/acl.c1
-rw-r--r--fs/9p/vfs_dentry.c10
-rw-r--r--fs/9p/vfs_file.c17
-rw-r--r--fs/9p/vfs_inode.c10
-rw-r--r--fs/9p/vfs_inode_dotl.c10
-rw-r--r--fs/Makefile2
-rw-r--r--fs/affs/inode.c2
-rw-r--r--fs/afs/cell.c121
-rw-r--r--fs/afs/dir.c4
-rw-r--r--fs/afs/dynroot.c9
-rw-r--r--fs/afs/inode.c8
-rw-r--r--fs/afs/internal.h13
-rw-r--r--fs/afs/mntpt.c3
-rw-r--r--fs/afs/proc.c3
-rw-r--r--fs/afs/rxrpc.c6
-rw-r--r--fs/afs/security.c49
-rw-r--r--fs/afs/super.c2
-rw-r--r--fs/afs/vl_alias.c3
-rw-r--r--fs/aio.c6
-rw-r--r--fs/anon_inodes.c23
-rw-r--r--fs/attr.c2
-rw-r--r--fs/autofs/autofs_i.h5
-rw-r--r--fs/autofs/dev-ioctl.c31
-rw-r--r--fs/autofs/inode.c1
-rw-r--r--fs/autofs/root.c8
-rw-r--r--fs/backing-file.c153
-rw-r--r--fs/befs/linuxvfs.c2
-rw-r--r--fs/bfs/inode.c21
-rw-r--r--fs/binfmt_elf.c2
-rw-r--r--fs/binfmt_misc.c11
-rw-r--r--fs/btrfs/accessors.h1
-rw-r--r--fs/btrfs/acl.c25
-rw-r--r--fs/btrfs/backref.c37
-rw-r--r--fs/btrfs/backref.h7
-rw-r--r--fs/btrfs/bio.c290
-rw-r--r--fs/btrfs/bio.h39
-rw-r--r--fs/btrfs/block-group.c89
-rw-r--r--fs/btrfs/block-group.h2
-rw-r--r--fs/btrfs/block-rsv.c14
-rw-r--r--fs/btrfs/btrfs_inode.h20
-rw-r--r--fs/btrfs/compression.c47
-rw-r--r--fs/btrfs/compression.h19
-rw-r--r--fs/btrfs/ctree.c238
-rw-r--r--fs/btrfs/ctree.h18
-rw-r--r--fs/btrfs/defrag.c19
-rw-r--r--fs/btrfs/delalloc-space.c4
-rw-r--r--fs/btrfs/delayed-inode.c28
-rw-r--r--fs/btrfs/delayed-inode.h7
-rw-r--r--fs/btrfs/delayed-ref.c45
-rw-r--r--fs/btrfs/dev-replace.c4
-rw-r--r--fs/btrfs/dir-item.c4
-rw-r--r--fs/btrfs/direct-io.c10
-rw-r--r--fs/btrfs/disk-io.c66
-rw-r--r--fs/btrfs/disk-io.h3
-rw-r--r--fs/btrfs/extent-tree.c172
-rw-r--r--fs/btrfs/extent-tree.h27
-rw-r--r--fs/btrfs/extent_io.c86
-rw-r--r--fs/btrfs/extent_io.h1
-rw-r--r--fs/btrfs/extent_map.h3
-rw-r--r--fs/btrfs/file-item.c85
-rw-r--r--fs/btrfs/file-item.h4
-rw-r--r--fs/btrfs/file.c48
-rw-r--r--fs/btrfs/free-space-cache.c24
-rw-r--r--fs/btrfs/free-space-tree.c55
-rw-r--r--fs/btrfs/fs.h36
-rw-r--r--fs/btrfs/inode-item.c5
-rw-r--r--fs/btrfs/inode.c281
-rw-r--r--fs/btrfs/ioctl.c219
-rw-r--r--fs/btrfs/messages.c1
-rw-r--r--fs/btrfs/messages.h3
-rw-r--r--fs/btrfs/misc.h12
-rw-r--r--fs/btrfs/ordered-data.c76
-rw-r--r--fs/btrfs/print-tree.c16
-rw-r--r--fs/btrfs/qgroup.c186
-rw-r--r--fs/btrfs/raid-stripe-tree.c18
-rw-r--r--fs/btrfs/raid56.c839
-rw-r--r--fs/btrfs/raid56.h103
-rw-r--r--fs/btrfs/ref-verify.c2
-rw-r--r--fs/btrfs/reflink.c15
-rw-r--r--fs/btrfs/relocation.c85
-rw-r--r--fs/btrfs/root-tree.c4
-rw-r--r--fs/btrfs/scrub.c268
-rw-r--r--fs/btrfs/send.c208
-rw-r--r--fs/btrfs/space-info.c464
-rw-r--r--fs/btrfs/space-info.h43
-rw-r--r--fs/btrfs/subpage.c72
-rw-r--r--fs/btrfs/subpage.h1
-rw-r--r--fs/btrfs/super.c85
-rw-r--r--fs/btrfs/sysfs.c58
-rw-r--r--fs/btrfs/sysfs.h3
-rw-r--r--fs/btrfs/tests/extent-io-tests.c3
-rw-r--r--fs/btrfs/tests/extent-map-tests.c6
-rw-r--r--fs/btrfs/tests/qgroup-tests.c16
-rw-r--r--fs/btrfs/transaction.c48
-rw-r--r--fs/btrfs/transaction.h4
-rw-r--r--fs/btrfs/tree-checker.c23
-rw-r--r--fs/btrfs/tree-log.c188
-rw-r--r--fs/btrfs/tree-log.h8
-rw-r--r--fs/btrfs/uuid-tree.c120
-rw-r--r--fs/btrfs/verity.c32
-rw-r--r--fs/btrfs/volumes.c219
-rw-r--r--fs/btrfs/volumes.h10
-rw-r--r--fs/btrfs/xattr.c41
-rw-r--r--fs/btrfs/zoned.c124
-rw-r--r--fs/btrfs/zoned.h7
-rw-r--r--fs/buffer.c6
-rw-r--r--fs/cachefiles/interface.c11
-rw-r--r--fs/cachefiles/namei.c98
-rw-r--r--fs/cachefiles/volume.c9
-rw-r--r--fs/ceph/addr.c6
-rw-r--r--fs/ceph/cache.c2
-rw-r--r--fs/ceph/crypto.c4
-rw-r--r--fs/ceph/dir.c5
-rw-r--r--fs/ceph/file.c4
-rw-r--r--fs/ceph/inode.c77
-rw-r--r--fs/ceph/super.c2
-rw-r--r--fs/ceph/xattr.c6
-rw-r--r--fs/coda/cnode.c4
-rw-r--r--fs/configfs/dir.c2
-rw-r--r--fs/configfs/file.c2
-rw-r--r--fs/coredump.c144
-rw-r--r--fs/cramfs/inode.c2
-rw-r--r--fs/crypto/inline_crypt.c3
-rw-r--r--fs/crypto/keyring.c2
-rw-r--r--fs/crypto/keysetup.c2
-rw-r--r--fs/dax.c30
-rw-r--r--fs/dcache.c35
-rw-r--r--fs/debugfs/inode.c74
-rw-r--r--fs/dlm/lowcomms.c8
-rw-r--r--fs/drop_caches.c2
-rw-r--r--fs/ecryptfs/Kconfig2
-rw-r--r--fs/ecryptfs/crypto.c90
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h13
-rw-r--r--fs/ecryptfs/inode.c169
-rw-r--r--fs/ecryptfs/keystore.c65
-rw-r--r--fs/ecryptfs/main.c7
-rw-r--r--fs/ecryptfs/super.c5
-rw-r--r--fs/efivarfs/super.c1
-rw-r--r--fs/efs/inode.c2
-rw-r--r--fs/erofs/compress.h12
-rw-r--r--fs/erofs/data.c5
-rw-r--r--fs/erofs/decompressor.c149
-rw-r--r--fs/erofs/decompressor_crypto.c7
-rw-r--r--fs/erofs/decompressor_deflate.c37
-rw-r--r--fs/erofs/decompressor_lzma.c26
-rw-r--r--fs/erofs/decompressor_zstd.c35
-rw-r--r--fs/erofs/erofs_fs.h2
-rw-r--r--fs/erofs/fileio.c8
-rw-r--r--fs/erofs/fscache.c4
-rw-r--r--fs/erofs/inode.c2
-rw-r--r--fs/erofs/super.c38
-rw-r--r--fs/erofs/zdata.c21
-rw-r--r--fs/erofs/zmap.c59
-rw-r--r--fs/eventfd.c31
-rw-r--r--fs/eventpoll.c32
-rw-r--r--fs/exec.c5
-rw-r--r--fs/exfat/super.c5
-rw-r--r--fs/ext2/inode.c2
-rw-r--r--fs/ext4/balloc.c2
-rw-r--r--fs/ext4/dir.c8
-rw-r--r--fs/ext4/ext4.h50
-rw-r--r--fs/ext4/ext4_jbd2.c3
-rw-r--r--fs/ext4/extents.c28
-rw-r--r--fs/ext4/extents_status.c31
-rw-r--r--fs/ext4/extents_status.h2
-rw-r--r--fs/ext4/hash.c2
-rw-r--r--fs/ext4/ialloc.c1
-rw-r--r--fs/ext4/inline.c14
-rw-r--r--fs/ext4/inode.c193
-rw-r--r--fs/ext4/ioctl.c14
-rw-r--r--fs/ext4/mballoc.c188
-rw-r--r--fs/ext4/mmp.c8
-rw-r--r--fs/ext4/move_extent.c786
-rw-r--r--fs/ext4/namei.c18
-rw-r--r--fs/ext4/orphan.c8
-rw-r--r--fs/ext4/readpage.c7
-rw-r--r--fs/ext4/super.c72
-rw-r--r--fs/ext4/sysfs.c6
-rw-r--r--fs/ext4/verity.c2
-rw-r--r--fs/ext4/xattr.c6
-rw-r--r--fs/f2fs/acl.c1
-rw-r--r--fs/f2fs/compress.c2
-rw-r--r--fs/f2fs/data.c7
-rw-r--r--fs/f2fs/inode.c2
-rw-r--r--fs/f2fs/namei.c4
-rw-r--r--fs/f2fs/super.c2
-rw-r--r--fs/fat/inode.c7
-rw-r--r--fs/fcntl.c13
-rw-r--r--fs/fhandle.c30
-rw-r--r--fs/file.c54
-rw-r--r--fs/file_attr.c4
-rw-r--r--fs/freevxfs/vxfs_inode.c2
-rw-r--r--fs/fs-writeback.c187
-rw-r--r--fs/fs_dirent.c (renamed from fs/fs_types.c)2
-rw-r--r--fs/fs_struct.c6
-rw-r--r--fs/fuse/dev_uring.c7
-rw-r--r--fs/fuse/dir.c22
-rw-r--r--fs/fuse/file.c286
-rw-r--r--fs/fuse/fuse_i.h8
-rw-r--r--fs/fuse/inode.c17
-rw-r--r--fs/fuse/virtio_fs.c2
-rw-r--r--fs/gfs2/aops.c16
-rw-r--r--fs/gfs2/file.c4
-rw-r--r--fs/gfs2/glock.c229
-rw-r--r--fs/gfs2/glock.h12
-rw-r--r--fs/gfs2/glops.c100
-rw-r--r--fs/gfs2/incore.h22
-rw-r--r--fs/gfs2/inode.c19
-rw-r--r--fs/gfs2/inode.h1
-rw-r--r--fs/gfs2/lock_dlm.c57
-rw-r--r--fs/gfs2/log.c59
-rw-r--r--fs/gfs2/lops.c12
-rw-r--r--fs/gfs2/meta_io.c13
-rw-r--r--fs/gfs2/ops_fstype.c39
-rw-r--r--fs/gfs2/quota.c66
-rw-r--r--fs/gfs2/recovery.c8
-rw-r--r--fs/gfs2/super.c35
-rw-r--r--fs/gfs2/super.h1
-rw-r--r--fs/gfs2/sys.c64
-rw-r--r--fs/gfs2/trace_gfs2.h1
-rw-r--r--fs/gfs2/trans.c30
-rw-r--r--fs/gfs2/util.c328
-rw-r--r--fs/gfs2/util.h56
-rw-r--r--fs/hfs/.kunitconfig7
-rw-r--r--fs/hfs/Kconfig15
-rw-r--r--fs/hfs/Makefile2
-rw-r--r--fs/hfs/bfind.c2
-rw-r--r--fs/hfs/bnode.c52
-rw-r--r--fs/hfs/brec.c2
-rw-r--r--fs/hfs/btree.c4
-rw-r--r--fs/hfs/btree.h113
-rw-r--r--fs/hfs/catalog.c2
-rw-r--r--fs/hfs/hfs.h269
-rw-r--r--fs/hfs/hfs_fs.h89
-rw-r--r--fs/hfs/inode.c5
-rw-r--r--fs/hfs/string.c5
-rw-r--r--fs/hfs/string_test.c133
-rw-r--r--fs/hfsplus/.kunitconfig8
-rw-r--r--fs/hfsplus/Kconfig15
-rw-r--r--fs/hfsplus/Makefile3
-rw-r--r--fs/hfsplus/bfind.c2
-rw-r--r--fs/hfsplus/bnode.c64
-rw-r--r--fs/hfsplus/brec.c2
-rw-r--r--fs/hfsplus/btree.c2
-rw-r--r--fs/hfsplus/dir.c7
-rw-r--r--fs/hfsplus/hfsplus_fs.h41
-rw-r--r--fs/hfsplus/hfsplus_raw.h394
-rw-r--r--fs/hfsplus/inode.c41
-rw-r--r--fs/hfsplus/options.c1
-rw-r--r--fs/hfsplus/super.c89
-rw-r--r--fs/hfsplus/unicode.c16
-rw-r--r--fs/hfsplus/unicode_test.c1579
-rw-r--r--fs/hfsplus/xattr.c22
-rw-r--r--fs/hostfs/hostfs_kern.c31
-rw-r--r--fs/hpfs/dir.c2
-rw-r--r--fs/hpfs/inode.c2
-rw-r--r--fs/hpfs/super.c1
-rw-r--r--fs/hugetlbfs/inode.c9
-rw-r--r--fs/init.c6
-rw-r--r--fs/inode.c317
-rw-r--r--fs/internal.h3
-rw-r--r--fs/iomap/Makefile3
-rw-r--r--fs/iomap/bio.c88
-rw-r--r--fs/iomap/buffered-io.c646
-rw-r--r--fs/iomap/direct-io.c268
-rw-r--r--fs/iomap/internal.h12
-rw-r--r--fs/iomap/ioend.c2
-rw-r--r--fs/iomap/iter.c20
-rw-r--r--fs/iomap/seek.c8
-rw-r--r--fs/iomap/trace.h7
-rw-r--r--fs/isofs/inode.c7
-rw-r--r--fs/jbd2/checkpoint.c2
-rw-r--r--fs/jbd2/journal.c35
-rw-r--r--fs/jbd2/transaction.c26
-rw-r--r--fs/jffs2/fs.c4
-rw-r--r--fs/jfs/file.c4
-rw-r--r--fs/jfs/inode.c2
-rw-r--r--fs/jfs/jfs_incore.h6
-rw-r--r--fs/jfs/jfs_txnmgr.c2
-rw-r--r--fs/kernfs/inode.c2
-rw-r--r--fs/libfs.c43
-rw-r--r--fs/lockd/netlink.c1
-rw-r--r--fs/lockd/netlink.h1
-rw-r--r--fs/locks.c103
-rw-r--r--fs/minix/inode.c18
-rw-r--r--fs/minix/minix.h9
-rw-r--r--fs/minix/namei.c39
-rw-r--r--fs/mount.h3
-rw-r--r--fs/namei.c1059
-rw-r--r--fs/namespace.c206
-rw-r--r--fs/netfs/buffered_write.c2
-rw-r--r--fs/netfs/misc.c10
-rw-r--r--fs/netfs/read_single.c6
-rw-r--r--fs/nfs/client.c8
-rw-r--r--fs/nfs/dir.c7
-rw-r--r--fs/nfs/inode.c20
-rw-r--r--fs/nfs/localio.c273
-rw-r--r--fs/nfs/nfs3client.c14
-rw-r--r--fs/nfs/nfs4client.c14
-rw-r--r--fs/nfs/nfs4file.c2
-rw-r--r--fs/nfs/nfs4idmap.c7
-rw-r--r--fs/nfs/nfs4proc.c9
-rw-r--r--fs/nfs/pnfs.c2
-rw-r--r--fs/nfs/pnfs_nfs.c66
-rw-r--r--fs/nfs/sysfs.c1
-rw-r--r--fs/nfsd/filecache.c57
-rw-r--r--fs/nfsd/filecache.h2
-rw-r--r--fs/nfsd/netlink.c1
-rw-r--r--fs/nfsd/netlink.h1
-rw-r--r--fs/nfsd/nfs3proc.c16
-rw-r--r--fs/nfsd/nfs4proc.c57
-rw-r--r--fs/nfsd/nfs4recover.c40
-rw-r--r--fs/nfsd/nfs4state.c172
-rw-r--r--fs/nfsd/nfs4xdr.c26
-rw-r--r--fs/nfsd/nfsd.h4
-rw-r--r--fs/nfsd/nfsfh.c6
-rw-r--r--fs/nfsd/nfsproc.c14
-rw-r--r--fs/nfsd/state.h5
-rw-r--r--fs/nfsd/vfs.c175
-rw-r--r--fs/nfsd/vfs.h2
-rw-r--r--fs/nfsd/xdr4.h4
-rw-r--r--fs/nilfs2/cpfile.c2
-rw-r--r--fs/nilfs2/dat.c2
-rw-r--r--fs/nilfs2/ifile.c2
-rw-r--r--fs/nilfs2/inode.c10
-rw-r--r--fs/nilfs2/nilfs.h1
-rw-r--r--fs/nilfs2/segment.c7
-rw-r--r--fs/nilfs2/sufile.c2
-rw-r--r--fs/notify/fanotify/fanotify_user.c60
-rw-r--r--fs/notify/fdinfo.c6
-rw-r--r--fs/notify/fsnotify.c2
-rw-r--r--fs/nsfs.c148
-rw-r--r--fs/ntfs3/attrib.c88
-rw-r--r--fs/ntfs3/dir.c3
-rw-r--r--fs/ntfs3/file.c109
-rw-r--r--fs/ntfs3/frecord.c219
-rw-r--r--fs/ntfs3/fsntfs.c132
-rw-r--r--fs/ntfs3/index.c3
-rw-r--r--fs/ntfs3/inode.c29
-rw-r--r--fs/ntfs3/namei.c6
-rw-r--r--fs/ntfs3/ntfs_fs.h40
-rw-r--r--fs/ntfs3/record.c2
-rw-r--r--fs/ntfs3/run.c17
-rw-r--r--fs/ntfs3/super.c89
-rw-r--r--fs/ntfs3/xattr.c18
-rw-r--r--fs/ocfs2/acl.c1
-rw-r--r--fs/ocfs2/alloc.c2
-rw-r--r--fs/ocfs2/cluster/tcp.c6
-rw-r--r--fs/ocfs2/dlmglue.c2
-rw-r--r--fs/ocfs2/inode.c27
-rw-r--r--fs/ocfs2/inode.h1
-rw-r--r--fs/ocfs2/journal.c11
-rw-r--r--fs/ocfs2/move_extents.c5
-rw-r--r--fs/ocfs2/ocfs2_trace.h2
-rw-r--r--fs/ocfs2/super.c2
-rw-r--r--fs/omfs/inode.c3
-rw-r--r--fs/open.c44
-rw-r--r--fs/openpromfs/inode.c2
-rw-r--r--fs/orangefs/inode.c6
-rw-r--r--fs/orangefs/orangefs-utils.c6
-rw-r--r--fs/overlayfs/copy_up.c143
-rw-r--r--fs/overlayfs/dir.c587
-rw-r--r--fs/overlayfs/file.c97
-rw-r--r--fs/overlayfs/inode.c124
-rw-r--r--fs/overlayfs/namei.c402
-rw-r--r--fs/overlayfs/overlayfs.h63
-rw-r--r--fs/overlayfs/readdir.c110
-rw-r--r--fs/overlayfs/super.c138
-rw-r--r--fs/overlayfs/util.c43
-rw-r--r--fs/overlayfs/xattrs.c35
-rw-r--r--fs/pidfs.c189
-rw-r--r--fs/pipe.c2
-rw-r--r--fs/posix_acl.c8
-rw-r--r--fs/proc/array.c9
-rw-r--r--fs/proc/base.c13
-rw-r--r--fs/proc/generic.c12
-rw-r--r--fs/qnx4/inode.c2
-rw-r--r--fs/qnx6/inode.c2
-rw-r--r--fs/quota/dquot.c2
-rw-r--r--fs/resctrl/ctrlmondata.c309
-rw-r--r--fs/resctrl/internal.h17
-rw-r--r--fs/resctrl/monitor.c16
-rw-r--r--fs/resctrl/rdtgroup.c82
-rw-r--r--fs/romfs/super.c2
-rw-r--r--fs/select.c12
-rw-r--r--fs/signalfd.c29
-rw-r--r--fs/smb/client/cached_dir.c51
-rw-r--r--fs/smb/client/cifs_debug.c23
-rw-r--r--fs/smb/client/cifs_spnego.c6
-rw-r--r--fs/smb/client/cifsfs.c7
-rw-r--r--fs/smb/client/cifsglob.h35
-rw-r--r--fs/smb/client/cifspdu.h601
-rw-r--r--fs/smb/client/cifsproto.h3
-rw-r--r--fs/smb/client/cifssmb.c48
-rw-r--r--fs/smb/client/cifstransport.c8
-rw-r--r--fs/smb/client/connect.c57
-rw-r--r--fs/smb/client/dfs_cache.c55
-rw-r--r--fs/smb/client/file.c1
-rw-r--r--fs/smb/client/fs_context.c8
-rw-r--r--fs/smb/client/inode.c24
-rw-r--r--fs/smb/client/misc.c2
-rw-r--r--fs/smb/client/ntlmssp.h8
-rw-r--r--fs/smb/client/readdir.c12
-rw-r--r--fs/smb/client/reparse.h8
-rw-r--r--fs/smb/client/rfc1002pdu.h8
-rw-r--r--fs/smb/client/smb1ops.c3
-rw-r--r--fs/smb/client/smb2inode.c4
-rw-r--r--fs/smb/client/smb2ops.c98
-rw-r--r--fs/smb/client/smb2pdu.c52
-rw-r--r--fs/smb/client/smb2pdu.h112
-rw-r--r--fs/smb/client/smb2proto.h6
-rw-r--r--fs/smb/client/smb2transport.c18
-rw-r--r--fs/smb/client/smbdirect.c134
-rw-r--r--fs/smb/client/trace.c1
-rw-r--r--fs/smb/client/transport.c2
-rw-r--r--fs/smb/common/cifsglob.h30
-rw-r--r--fs/smb/common/fscc.h174
-rw-r--r--fs/smb/common/smb2pdu.h279
-rw-r--r--fs/smb/common/smbacl.h8
-rw-r--r--fs/smb/common/smbdirect/smbdirect_socket.h64
-rw-r--r--fs/smb/common/smbglob.h72
-rw-r--r--fs/smb/server/Kconfig6
-rw-r--r--fs/smb/server/auth.c390
-rw-r--r--fs/smb/server/auth.h10
-rw-r--r--fs/smb/server/crypto_ctx.c24
-rw-r--r--fs/smb/server/crypto_ctx.h15
-rw-r--r--fs/smb/server/mgmt/tree_connect.c18
-rw-r--r--fs/smb/server/mgmt/tree_connect.h1
-rw-r--r--fs/smb/server/misc.c15
-rw-r--r--fs/smb/server/oplock.c8
-rw-r--r--fs/smb/server/server.c4
-rw-r--r--fs/smb/server/smb2misc.c2
-rw-r--r--fs/smb/server/smb2ops.c38
-rw-r--r--fs/smb/server/smb2pdu.c223
-rw-r--r--fs/smb/server/smb2pdu.h107
-rw-r--r--fs/smb/server/smb_common.h276
-rw-r--r--fs/smb/server/transport_ipc.c15
-rw-r--r--fs/smb/server/transport_rdma.c469
-rw-r--r--fs/smb/server/transport_tcp.c50
-rw-r--r--fs/smb/server/vfs.c130
-rw-r--r--fs/smb/server/vfs.h8
-rw-r--r--fs/smb/server/vfs_cache.c88
-rw-r--r--fs/splice.c2
-rw-r--r--fs/squashfs/inode.c2
-rw-r--r--fs/super.c14
-rw-r--r--fs/sync.c19
-rw-r--r--fs/sysfs/group.c26
-rw-r--r--fs/timerfd.c29
-rw-r--r--fs/ubifs/file.c2
-rw-r--r--fs/ubifs/super.c2
-rw-r--r--fs/udf/inode.c2
-rw-r--r--fs/ufs/inode.c2
-rw-r--r--fs/userfaultfd.c30
-rw-r--r--fs/utimes.c5
-rw-r--r--fs/xattr.c12
-rw-r--r--fs/xfs/Kconfig11
-rw-r--r--fs/xfs/libxfs/xfs_errortag.h6
-rw-r--r--fs/xfs/libxfs/xfs_group.h9
-rw-r--r--fs/xfs/libxfs/xfs_log_format.h38
-rw-r--r--fs/xfs/libxfs/xfs_ondisk.h6
-rw-r--r--fs/xfs/libxfs/xfs_quota_defs.h4
-rw-r--r--fs/xfs/libxfs/xfs_rtgroup.h20
-rw-r--r--fs/xfs/libxfs/xfs_zones.c1
-rw-r--r--fs/xfs/scrub/common.c2
-rw-r--r--fs/xfs/scrub/inode_repair.c2
-rw-r--r--fs/xfs/scrub/nlinks.c34
-rw-r--r--fs/xfs/scrub/orphanage.c13
-rw-r--r--fs/xfs/scrub/parent.c2
-rw-r--r--fs/xfs/scrub/quota.c8
-rw-r--r--fs/xfs/scrub/quota_repair.c18
-rw-r--r--fs/xfs/scrub/quotacheck.c11
-rw-r--r--fs/xfs/scrub/quotacheck_repair.c21
-rw-r--r--fs/xfs/scrub/symlink_repair.c2
-rw-r--r--fs/xfs/scrub/xfarray.c2
-rw-r--r--fs/xfs/xfs_aops.c7
-rw-r--r--fs/xfs/xfs_bmap_util.c2
-rw-r--r--fs/xfs/xfs_buf.c2
-rw-r--r--fs/xfs/xfs_buf.h1
-rw-r--r--fs/xfs/xfs_discard.c4
-rw-r--r--fs/xfs/xfs_dquot.c143
-rw-r--r--fs/xfs/xfs_dquot.h22
-rw-r--r--fs/xfs/xfs_dquot_item.c6
-rw-r--r--fs/xfs/xfs_file.c50
-rw-r--r--fs/xfs/xfs_handle.c56
-rw-r--r--fs/xfs/xfs_health.c4
-rw-r--r--fs/xfs/xfs_icache.c37
-rw-r--r--fs/xfs/xfs_inode.c6
-rw-r--r--fs/xfs/xfs_inode_item.c4
-rw-r--r--fs/xfs/xfs_ioctl.c6
-rw-r--r--fs/xfs/xfs_iomap.c120
-rw-r--r--fs/xfs/xfs_iops.c2
-rw-r--r--fs/xfs/xfs_log.c206
-rw-r--r--fs/xfs/xfs_log_cil.c6
-rw-r--r--fs/xfs/xfs_log_priv.h33
-rw-r--r--fs/xfs/xfs_log_recover.c45
-rw-r--r--fs/xfs/xfs_mount.h1
-rw-r--r--fs/xfs/xfs_qm.c154
-rw-r--r--fs/xfs/xfs_qm.h2
-rw-r--r--fs/xfs/xfs_qm_bhv.c4
-rw-r--r--fs/xfs/xfs_qm_syscalls.c10
-rw-r--r--fs/xfs/xfs_quotaops.c2
-rw-r--r--fs/xfs/xfs_reflink.h2
-rw-r--r--fs/xfs/xfs_super.c58
-rw-r--r--fs/xfs/xfs_trace.h8
-rw-r--r--fs/xfs/xfs_trans_dquot.c18
-rw-r--r--fs/xfs/xfs_zone_alloc.c218
-rw-r--r--fs/xfs/xfs_zone_gc.c122
-rw-r--r--fs/xfs/xfs_zone_priv.h3
-rw-r--r--fs/xfs/xfs_zone_space_resv.c10
-rw-r--r--fs/zonefs/file.c5
-rw-r--r--fs/zonefs/super.c4
-rw-r--r--include/acpi/processor.h34
-rw-r--r--include/asm-generic/bug.h80
-rw-r--r--include/asm-generic/rqspinlock.h60
-rw-r--r--include/asm-generic/thread_info_tif.h3
-rw-r--r--include/asm-generic/vmlinux.lds.h81
-rw-r--r--include/crypto/aead.h87
-rw-r--r--include/crypto/algapi.h12
-rw-r--r--include/crypto/blake2b.h143
-rw-r--r--include/crypto/blake2s.h126
-rw-r--r--include/crypto/chacha.h12
-rw-r--r--include/crypto/chacha20poly1305.h19
-rw-r--r--include/crypto/curve25519.h24
-rw-r--r--include/crypto/df_sp80090a.h28
-rw-r--r--include/crypto/drbg.h25
-rw-r--r--include/crypto/internal/blake2b.h101
-rw-r--r--include/crypto/internal/drbg.h54
-rw-r--r--include/crypto/internal/skcipher.h48
-rw-r--r--include/crypto/md5.h11
-rw-r--r--include/crypto/poly1305.h2
-rw-r--r--include/crypto/polyval.h182
-rw-r--r--include/crypto/rng.h11
-rw-r--r--include/crypto/scatterwalk.h117
-rw-r--r--include/crypto/sha1.h12
-rw-r--r--include/crypto/sha2.h53
-rw-r--r--include/crypto/sha3.h320
-rw-r--r--include/drm/Makefile2
-rw-r--r--include/drm/bridge/dw_hdmi.h11
-rw-r--r--include/drm/bridge/dw_hdmi_qp.h6
-rw-r--r--include/drm/display/drm_dp.h6
-rw-r--r--include/drm/display/drm_dp_helper.h22
-rw-r--r--include/drm/drm_atomic.h255
-rw-r--r--include/drm/drm_atomic_uapi.h3
-rw-r--r--include/drm/drm_bridge.h61
-rw-r--r--include/drm/drm_buddy.h13
-rw-r--r--include/drm/drm_client.h53
-rw-r--r--include/drm/drm_client_event.h12
-rw-r--r--include/drm/drm_color_mgmt.h29
-rw-r--r--include/drm/drm_colorop.h464
-rw-r--r--include/drm/drm_crtc.h20
-rw-r--r--include/drm/drm_device.h8
-rw-r--r--include/drm/drm_dumb_buffers.h14
-rw-r--r--include/drm/drm_edid.h6
-rw-r--r--include/drm/drm_fb_helper.h20
-rw-r--r--include/drm/drm_file.h7
-rw-r--r--include/drm/drm_fixed.h17
-rw-r--r--include/drm/drm_format_helper.h4
-rw-r--r--include/drm/drm_gem_shmem_helper.h2
-rw-r--r--include/drm/drm_gpusvm.h4
-rw-r--r--include/drm/drm_gpuvm.h16
-rw-r--r--include/drm/drm_mm.h2
-rw-r--r--include/drm/drm_mode_config.h18
-rw-r--r--include/drm/drm_modeset_helper_vtables.h12
-rw-r--r--include/drm/drm_plane.h19
-rw-r--r--include/drm/drm_vblank.h32
-rw-r--r--include/drm/drm_vblank_helper.h56
-rw-r--r--include/drm/gpu_scheduler.h2
-rw-r--r--include/drm/intel/display_member.h42
-rw-r--r--include/drm/intel/display_parent_interface.h45
-rw-r--r--include/drm/intel/pciids.h25
-rw-r--r--include/drm/intel/xe_sriov_vfio.h143
-rw-r--r--include/drm/ttm/ttm_allocation.h12
-rw-r--r--include/drm/ttm/ttm_bo.h2
-rw-r--r--include/drm/ttm/ttm_device.h8
-rw-r--r--include/drm/ttm/ttm_pool.h8
-rw-r--r--include/drm/ttm/ttm_resource.h32
-rw-r--r--include/dt-bindings/media/c8sectpfe.h13
-rw-r--r--include/dt-bindings/media/video-interfaces.h4
-rw-r--r--include/dt-bindings/memory/mediatek,mt8189-memory-port.h283
-rw-r--r--include/dt-bindings/power/mediatek,mt8196-power.h58
-rw-r--r--include/dt-bindings/power/qcom,rpmhpd.h3
-rw-r--r--include/dt-bindings/power/rockchip,rv1126b-power-controller.h17
-rw-r--r--include/keys/asymmetric-type.h2
-rw-r--r--include/linux/acpi.h49
-rw-r--r--include/linux/annotate.h134
-rw-r--r--include/linux/arch_topology.h17
-rw-r--r--include/linux/arm_ffa.h21
-rw-r--r--include/linux/arm_mpam.h66
-rw-r--r--include/linux/ata.h1
-rw-r--r--include/linux/atomic/atomic-instrumented.h26
-rw-r--r--include/linux/avf/virtchnl.h50
-rw-r--r--include/linux/backing-dev-defs.h6
-rw-r--r--include/linux/backing-dev.h5
-rw-r--r--include/linux/backlight.h1
-rw-r--r--include/linux/bio-integrity.h7
-rw-r--r--include/linux/bio.h2
-rw-r--r--include/linux/bitmap.h15
-rw-r--r--include/linux/blk-integrity.h19
-rw-r--r--include/linux/blk-mq-dma.h28
-rw-r--r--include/linux/blk-mq.h30
-rw-r--r--include/linux/blk_types.h25
-rw-r--r--include/linux/blkdev.h62
-rw-r--r--include/linux/blktrace_api.h3
-rw-r--r--include/linux/bpf-cgroup.h17
-rw-r--r--include/linux/bpf.h102
-rw-r--r--include/linux/bpf_local_storage.h13
-rw-r--r--include/linux/bpf_types.h1
-rw-r--r--include/linux/bpf_verifier.h30
-rw-r--r--include/linux/bug.h8
-rw-r--r--include/linux/buildid.h25
-rw-r--r--include/linux/byteorder/generic.h16
-rw-r--r--include/linux/can/bittiming.h81
-rw-r--r--include/linux/can/dev.h82
-rw-r--r--include/linux/cc_platform.h2
-rw-r--r--include/linux/ceph/libceph.h3
-rw-r--r--include/linux/cgroup-defs.h2
-rw-r--r--include/linux/cgroup.h14
-rw-r--r--include/linux/cleanup.h57
-rw-r--r--include/linux/compiler.h8
-rw-r--r--include/linux/compiler_types.h26
-rw-r--r--include/linux/configfs.h4
-rw-r--r--include/linux/console.h68
-rw-r--r--include/linux/context_tracking_state.h44
-rw-r--r--include/linux/cper.h12
-rw-r--r--include/linux/cpuidle.h6
-rw-r--r--include/linux/cpumask.h28
-rw-r--r--include/linux/cpuset.h9
-rw-r--r--include/linux/cred.h22
-rw-r--r--include/linux/delay.h8
-rw-r--r--include/linux/devfreq-governor.h (renamed from drivers/devfreq/governor.h)33
-rw-r--r--include/linux/device-mapper.h10
-rw-r--r--include/linux/dma-buf-mapping.h17
-rw-r--r--include/linux/dma-buf.h11
-rw-r--r--include/linux/dma-buf/heaps/cma.h16
-rw-r--r--include/linux/dma-mapping.h2
-rw-r--r--include/linux/dpll.h1
-rw-r--r--include/linux/efi.h8
-rw-r--r--include/linux/ehl_pse_io_aux.h24
-rw-r--r--include/linux/elfnote.h13
-rw-r--r--include/linux/energy_model.h4
-rw-r--r--include/linux/entry-common.h38
-rw-r--r--include/linux/entry-virt.h2
-rw-r--r--include/linux/ethtool.h2
-rw-r--r--include/linux/exportfs.h7
-rw-r--r--include/linux/fault-inject.h8
-rw-r--r--include/linux/fbcon.h2
-rw-r--r--include/linux/file.h126
-rw-r--r--include/linux/filelock.h98
-rw-r--r--include/linux/filter.h43
-rw-r--r--include/linux/firewire.h17
-rw-r--r--include/linux/firmware/cirrus/cs_dsp.h6
-rw-r--r--include/linux/firmware/cirrus/cs_dsp_test_utils.h18
-rw-r--r--include/linux/freezer.h12
-rw-r--r--include/linux/fs.h727
-rw-r--r--include/linux/fs/super.h238
-rw-r--r--include/linux/fs/super_types.h336
-rw-r--r--include/linux/fs_dirent.h (renamed from include/linux/fs_types.h)11
-rw-r--r--include/linux/fs_struct.h6
-rw-r--r--include/linux/ftrace.h43
-rw-r--r--include/linux/generic_pt/common.h191
-rw-r--r--include/linux/generic_pt/iommu.h293
-rw-r--r--include/linux/gfp.h3
-rw-r--r--include/linux/gfp_types.h6
-rw-r--r--include/linux/gpio/consumer.h11
-rw-r--r--include/linux/gpio/legacy-of-mm-gpiochip.h36
-rw-r--r--include/linux/gpio/regmap.h9
-rw-r--r--include/linux/hfs_common.h633
-rw-r--r--include/linux/hid.h1
-rw-r--r--include/linux/highmem.h6
-rw-r--r--include/linux/hisi_acc_qm.h3
-rw-r--r--include/linux/huge_mm.h57
-rw-r--r--include/linux/hung_task.h8
-rw-r--r--include/linux/icmp.h32
-rw-r--r--include/linux/ieee80211-eht.h1182
-rw-r--r--include/linux/ieee80211-he.h825
-rw-r--r--include/linux/ieee80211-ht.h292
-rw-r--r--include/linux/ieee80211-mesh.h230
-rw-r--r--include/linux/ieee80211-nan.h35
-rw-r--r--include/linux/ieee80211-p2p.h71
-rw-r--r--include/linux/ieee80211-s1g.h575
-rw-r--r--include/linux/ieee80211-vht.h236
-rw-r--r--include/linux/ieee80211.h3326
-rw-r--r--include/linux/if_hsr.h9
-rw-r--r--include/linux/if_vlan.h13
-rw-r--r--include/linux/iio/buffer-dma.h1
-rw-r--r--include/linux/iio/buffer_impl.h2
-rw-r--r--include/linux/init.h3
-rw-r--r--include/linux/init_task.h1
-rw-r--r--include/linux/intel-ish-client-if.h3
-rw-r--r--include/linux/intel_rapl.h2
-rw-r--r--include/linux/interrupt.h25
-rw-r--r--include/linux/interval_tree.h4
-rw-r--r--include/linux/interval_tree_generic.h2
-rw-r--r--include/linux/io-pgtable.h2
-rw-r--r--include/linux/io_uring/cmd.h22
-rw-r--r--include/linux/io_uring_types.h12
-rw-r--r--include/linux/iomap.h86
-rw-r--r--include/linux/iommu.h3
-rw-r--r--include/linux/ipv6.h1
-rw-r--r--include/linux/irq-entry-common.h77
-rw-r--r--include/linux/irq.h5
-rw-r--r--include/linux/irq_work.h9
-rw-r--r--include/linux/irq_work_types.h14
-rw-r--r--include/linux/irqchip.h8
-rw-r--r--include/linux/irqchip/irq-partition-percpu.h53
-rw-r--r--include/linux/irqchip/riscv-imsic.h3
-rw-r--r--include/linux/irqdesc.h1
-rw-r--r--include/linux/irqdomain.h33
-rw-r--r--include/linux/jbd2.h6
-rw-r--r--include/linux/kdb.h16
-rw-r--r--include/linux/kernel_read_file.h1
-rw-r--r--include/linux/key-type.h9
-rw-r--r--include/linux/kfifo.h34
-rw-r--r--include/linux/kvm_types.h14
-rw-r--r--include/linux/livepatch.h25
-rw-r--r--include/linux/livepatch_external.h76
-rw-r--r--include/linux/livepatch_helpers.h77
-rw-r--r--include/linux/local_lock.h4
-rw-r--r--include/linux/local_lock_internal.h62
-rw-r--r--include/linux/lockdep.h2
-rw-r--r--include/linux/lsm_hooks.h73
-rw-r--r--include/linux/mailbox/mtk-cmdq-mailbox.h10
-rw-r--r--include/linux/map_benchmark.h1
-rw-r--r--include/linux/mdio.h13
-rw-r--r--include/linux/memfd.h2
-rw-r--r--include/linux/memory.h9
-rw-r--r--include/linux/memory_hotplug.h18
-rw-r--r--include/linux/mempool.h58
-rw-r--r--include/linux/memremap.h1
-rw-r--r--include/linux/mfd/macsmc.h7
-rw-r--r--include/linux/mfd/pf1550.h273
-rw-r--r--include/linux/mfd/samsung/irq.h6
-rw-r--r--include/linux/mfd/wl1273-core.h277
-rw-r--r--include/linux/mii_timestamper.h13
-rw-r--r--include/linux/misc_cgroup.h2
-rw-r--r--include/linux/mlx5/cq.h1
-rw-r--r--include/linux/mlx5/driver.h14
-rw-r--r--include/linux/mlx5/fs.h25
-rw-r--r--include/linux/mlx5/mlx5_ifc.h51
-rw-r--r--include/linux/mlx5/port.h1
-rw-r--r--include/linux/mlx5/vport.h3
-rw-r--r--include/linux/mm.h48
-rw-r--r--include/linux/mm_types.h128
-rw-r--r--include/linux/mmc/card.h1
-rw-r--r--include/linux/module.h3
-rw-r--r--include/linux/msi.h3
-rw-r--r--include/linux/mtd/spear_smi.h19
-rw-r--r--include/linux/mtd/spinand.h1
-rw-r--r--include/linux/mutex.h45
-rw-r--r--include/linux/namei.h83
-rw-r--r--include/linux/net.h9
-rw-r--r--include/linux/net/intel/libie/fwlog.h12
-rw-r--r--include/linux/netdev_features.h18
-rw-r--r--include/linux/netdevice.h30
-rw-r--r--include/linux/netdevice_xmit.h9
-rw-r--r--include/linux/notifier.h2
-rw-r--r--include/linux/ns/ns_common_types.h196
-rw-r--r--include/linux/ns/nstree_types.h55
-rw-r--r--include/linux/ns_common.h233
-rw-r--r--include/linux/nsfs.h3
-rw-r--r--include/linux/nsproxy.h9
-rw-r--r--include/linux/nstree.h52
-rw-r--r--include/linux/objtool.h96
-rw-r--r--include/linux/objtool_types.h2
-rw-r--r--include/linux/of.h13
-rw-r--r--include/linux/of_fdt.h9
-rw-r--r--include/linux/of_irq.h7
-rw-r--r--include/linux/page-flags.h16
-rw-r--r--include/linux/pagemap.h18
-rw-r--r--include/linux/pci-epf.h12
-rw-r--r--include/linux/pci-p2pdma.h120
-rw-r--r--include/linux/pci-tph.h1
-rw-r--r--include/linux/pci.h29
-rw-r--r--include/linux/pci_ids.h1
-rw-r--r--include/linux/pcs/pcs-xpcs.h4
-rw-r--r--include/linux/percpu-defs.h2
-rw-r--r--include/linux/perf/arm_pmu.h7
-rw-r--r--include/linux/perf_event.h2
-rw-r--r--include/linux/pgtable.h4
-rw-r--r--include/linux/phy.h281
-rw-r--r--include/linux/phy_fixed.h14
-rw-r--r--include/linux/phylink.h28
-rw-r--r--include/linux/pid_namespace.h3
-rw-r--r--include/linux/pipe_fs_i.h23
-rw-r--r--include/linux/platform_data/bcmgenet.h19
-rw-r--r--include/linux/platform_data/lp855x.h4
-rw-r--r--include/linux/platform_data/spi-davinci.h73
-rw-r--r--include/linux/platform_data/x86/int3472.h1
-rw-r--r--include/linux/platform_device.h3
-rw-r--r--include/linux/pm.h9
-rw-r--r--include/linux/pm_domain.h1
-rw-r--r--include/linux/pm_qos.h9
-rw-r--r--include/linux/pm_runtime.h32
-rw-r--r--include/linux/pm_wakeup.h17
-rw-r--r--include/linux/power/max77705_charger.h2
-rw-r--r--include/linux/prandom.h6
-rw-r--r--include/linux/preempt.h2
-rw-r--r--include/linux/prmt.h2
-rw-r--r--include/linux/property.h13
-rw-r--r--include/linux/pseudo_fs.h1
-rw-r--r--include/linux/pwm.h39
-rw-r--r--include/linux/random.h15
-rw-r--r--include/linux/ras.h16
-rw-r--r--include/linux/rculist_nulls.h65
-rw-r--r--include/linux/regmap.h40
-rw-r--r--include/linux/regulator/driver.h3
-rw-r--r--include/linux/regulator/mt6363-regulator.h330
-rw-r--r--include/linux/regulator/pca9450.h32
-rw-r--r--include/linux/resctrl.h24
-rw-r--r--include/linux/restart_block.h2
-rw-r--r--include/linux/resume_user_mode.h2
-rw-r--r--include/linux/rhashtable.h70
-rw-r--r--include/linux/rseq.h214
-rw-r--r--include/linux/rseq_entry.h616
-rw-r--r--include/linux/rseq_types.h164
-rw-r--r--include/linux/sbitmap.h6
-rw-r--r--include/linux/sched.h100
-rw-r--r--include/linux/sched/coredump.h2
-rw-r--r--include/linux/sched/ext.h27
-rw-r--r--include/linux/sched/topology.h3
-rw-r--r--include/linux/security.h3
-rw-r--r--include/linux/seqlock.h114
-rw-r--r--include/linux/shmem_fs.h2
-rw-r--r--include/linux/sizes.h1
-rw-r--r--include/linux/skbuff.h82
-rw-r--r--include/linux/smp.h5
-rw-r--r--include/linux/soc/airoha/airoha_offload.h1
-rw-r--r--include/linux/soc/mediatek/mtk_wed.h1
-rw-r--r--include/linux/socket.h29
-rw-r--r--include/linux/soundwire/sdw_registers.h2
-rw-r--r--include/linux/spi/offload/types.h9
-rw-r--r--include/linux/srcu.h147
-rw-r--r--include/linux/srcutiny.h31
-rw-r--r--include/linux/srcutree.h99
-rw-r--r--include/linux/stmmac.h23
-rw-r--r--include/linux/string.h19
-rw-r--r--include/linux/syscalls.h4
-rw-r--r--include/linux/thread_info.h5
-rw-r--r--include/linux/timer.h9
-rw-r--r--include/linux/tpm.h13
-rw-r--r--include/linux/types.h1
-rw-r--r--include/linux/uaccess.h314
-rw-r--r--include/linux/unwind_deferred.h52
-rw-r--r--include/linux/unwind_deferred_types.h18
-rw-r--r--include/linux/unwind_user_types.h2
-rw-r--r--include/linux/usb/chipidea.h1
-rw-r--r--include/linux/usb/gadget.h5
-rw-r--r--include/linux/usb/usbnet.h2
-rw-r--r--include/linux/user_namespace.h4
-rw-r--r--include/linux/vfio.h6
-rw-r--r--include/linux/vfio_pci_core.h73
-rw-r--r--include/linux/virtio.h2
-rw-r--r--include/linux/virtio_config.h24
-rw-r--r--include/linux/virtio_features.h29
-rw-r--r--include/linux/virtio_net.h12
-rw-r--r--include/linux/virtio_pci_modern.h8
-rw-r--r--include/linux/writeback.h15
-rw-r--r--include/linux/xattr.h4
-rw-r--r--include/media/drv-intf/saa7146_vv.h3
-rw-r--r--include/media/media-entity.h10
-rw-r--r--include/media/v4l2-common.h20
-rw-r--r--include/media/v4l2-dev.h6
-rw-r--r--include/media/v4l2-isp.h91
-rw-r--r--include/media/v4l2-mem2mem.h18
-rw-r--r--include/media/v4l2-subdev.h2
-rw-r--r--include/net/addrconf.h5
-rw-r--r--include/net/bluetooth/hci.h83
-rw-r--r--include/net/bluetooth/hci_core.h45
-rw-r--r--include/net/bluetooth/hci_sync.h3
-rw-r--r--include/net/bluetooth/l2cap.h4
-rw-r--r--include/net/bluetooth/mgmt.h6
-rw-r--r--include/net/cfg80211.h134
-rw-r--r--include/net/devlink.h49
-rw-r--r--include/net/dsa.h21
-rw-r--r--include/net/gro.h27
-rw-r--r--include/net/ieee80211_radiotap.h20
-rw-r--r--include/net/inet_common.h13
-rw-r--r--include/net/inet_connection_sock.h31
-rw-r--r--include/net/inet_sock.h9
-rw-r--r--include/net/ip.h4
-rw-r--r--include/net/ipv6.h10
-rw-r--r--include/net/ipv6_stubs.h2
-rw-r--r--include/net/libeth/xdp.h2
-rw-r--r--include/net/mac80211.h4
-rw-r--r--include/net/mana/gdma.h24
-rw-r--r--include/net/mana/hw_channel.h2
-rw-r--r--include/net/mana/mana.h24
-rw-r--r--include/net/neighbour.h17
-rw-r--r--include/net/netfilter/nf_conntrack_count.h17
-rw-r--r--include/net/netfilter/nf_conntrack_l4proto.h2
-rw-r--r--include/net/netfilter/nf_flow_table.h26
-rw-r--r--include/net/netmem.h70
-rw-r--r--include/net/netns/core.h2
-rw-r--r--include/net/netns/ipv4.h5
-rw-r--r--include/net/netns/ipv6.h1
-rw-r--r--include/net/netns/mpls.h1
-rw-r--r--include/net/netns/smc.h5
-rw-r--r--include/net/nl802154.h5
-rw-r--r--include/net/ping.h2
-rw-r--r--include/net/pkt_cls.h2
-rw-r--r--include/net/pkt_sched.h11
-rw-r--r--include/net/proto_memory.h3
-rw-r--r--include/net/psp/types.h32
-rw-r--r--include/net/request_sock.h1
-rw-r--r--include/net/sch_generic.h122
-rw-r--r--include/net/sctp/auth.h1
-rw-r--r--include/net/sctp/sctp.h5
-rw-r--r--include/net/sctp/stream_sched.h4
-rw-r--r--include/net/sctp/structs.h9
-rw-r--r--include/net/selftests.h45
-rw-r--r--include/net/smc.h53
-rw-r--r--include/net/sock.h99
-rw-r--r--include/net/tcp.h44
-rw-r--r--include/net/tls.h28
-rw-r--r--include/net/udp.h2
-rw-r--r--include/net/vsock_addr.h2
-rw-r--r--include/net/xdp_sock.h7
-rw-r--r--include/net/xdp_sock_drv.h4
-rw-r--r--include/net/xfrm.h3
-rw-r--r--include/net/xsk_buff_pool.h13
-rw-r--r--include/ras/ras_event.h49
-rw-r--r--include/rdma/ib_cm.h4
-rw-r--r--include/rdma/ib_verbs.h100
-rw-r--r--include/rdma/rdmavt_qp.h70
-rw-r--r--include/scsi/scsi_device.h10
-rw-r--r--include/soc/fsl/caam-blob.h26
-rw-r--r--include/sound/asoundef.h9
-rw-r--r--include/sound/cs-amp-lib.h25
-rw-r--r--include/sound/cs35l56.h50
-rw-r--r--include/sound/sdca.h19
-rw-r--r--include/sound/sdca_fdl.h105
-rw-r--r--include/sound/sdca_function.h126
-rw-r--r--include/sound/sdca_hid.h21
-rw-r--r--include/sound/sdca_interrupts.h19
-rw-r--r--include/sound/sdca_regmap.h2
-rw-r--r--include/sound/sdca_ump.h50
-rw-r--r--include/sound/soc-acpi-intel-match.h2
-rw-r--r--include/sound/soc-acpi.h8
-rw-r--r--include/sound/soc.h39
-rw-r--r--include/sound/soc_sdw_utils.h19
-rw-r--r--include/sound/tas2781.h8
-rw-r--r--include/trace/events/asoc.h4
-rw-r--r--include/trace/events/ext4.h99
-rw-r--r--include/trace/events/net.h37
-rw-r--r--include/trace/events/power.h3
-rw-r--r--include/trace/events/rseq.h4
-rw-r--r--include/trace/events/sched_ext.h39
-rw-r--r--include/trace/events/spi-mem.h106
-rw-r--r--include/trace/events/tcp.h9
-rw-r--r--include/trace/events/timer_migration.h4
-rw-r--r--include/trace/events/writeback.h8
-rw-r--r--include/uapi/asm-generic/posix_types.h1
-rw-r--r--include/uapi/asm-generic/unistd.h4
-rw-r--r--include/uapi/drm/amdgpu_drm.h9
-rw-r--r--include/uapi/drm/amdxdna_accel.h61
-rw-r--r--include/uapi/drm/drm.h15
-rw-r--r--include/uapi/drm/drm_fourcc.h25
-rw-r--r--include/uapi/drm/drm_mode.h184
-rw-r--r--include/uapi/drm/ethosu_accel.h261
-rw-r--r--include/uapi/drm/ivpu_accel.h63
-rw-r--r--include/uapi/drm/panfrost_drm.h150
-rw-r--r--include/uapi/drm/xe_drm.h21
-rw-r--r--include/uapi/linux/android/binder_netlink.h1
-rw-r--r--include/uapi/linux/blktrace_api.h55
-rw-r--r--include/uapi/linux/blkzoned.h46
-rw-r--r--include/uapi/linux/bpf.h35
-rw-r--r--include/uapi/linux/btrfs.h9
-rw-r--r--include/uapi/linux/can/netlink.h34
-rw-r--r--include/uapi/linux/devlink.h4
-rw-r--r--include/uapi/linux/dpll.h2
-rw-r--r--include/uapi/linux/energy_model.h62
-rw-r--r--include/uapi/linux/ethtool.h5
-rw-r--r--include/uapi/linux/ethtool_netlink_generated.h36
-rw-r--r--include/uapi/linux/fb.h2
-rw-r--r--include/uapi/linux/fcntl.h16
-rw-r--r--include/uapi/linux/fou.h1
-rw-r--r--include/uapi/linux/fs.h3
-rw-r--r--include/uapi/linux/handshake.h1
-rw-r--r--include/uapi/linux/if_ether.h4
-rw-r--r--include/uapi/linux/if_team.h1
-rw-r--r--include/uapi/linux/input-event-codes.h14
-rw-r--r--include/uapi/linux/io_uring.h49
-rw-r--r--include/uapi/linux/io_uring/query.h27
-rw-r--r--include/uapi/linux/iommufd.h10
-rw-r--r--include/uapi/linux/isst_if.h50
-rw-r--r--include/uapi/linux/lockd_netlink.h1
-rw-r--r--include/uapi/linux/mdio.h23
-rw-r--r--include/uapi/linux/media-bus-format.h9
-rw-r--r--include/uapi/linux/media/amlogic/c3-isp-config.h92
-rw-r--r--include/uapi/linux/media/arm/mali-c55-config.h794
-rw-r--r--include/uapi/linux/media/v4l2-isp.h102
-rw-r--r--include/uapi/linux/mount.h2
-rw-r--r--include/uapi/linux/mptcp.h3
-rw-r--r--include/uapi/linux/mptcp_pm.h1
-rw-r--r--include/uapi/linux/net_shaper.h1
-rw-r--r--include/uapi/linux/netdev.h2
-rw-r--r--include/uapi/linux/netfilter/nf_tables.h14
-rw-r--r--include/uapi/linux/netfilter_ipv6/ip6t_srh.h40
-rw-r--r--include/uapi/linux/nfsd_netlink.h1
-rw-r--r--include/uapi/linux/nl80211-vnd-intel.h1
-rw-r--r--include/uapi/linux/nsfs.h58
-rw-r--r--include/uapi/linux/ovpn.h1
-rw-r--r--include/uapi/linux/perf_event.h23
-rw-r--r--include/uapi/linux/pidfd.h11
-rw-r--r--include/uapi/linux/psp.h19
-rw-r--r--include/uapi/linux/raid/md_p.h3
-rw-r--r--include/uapi/linux/rkisp1-config.h107
-rw-r--r--include/uapi/linux/rseq.h21
-rw-r--r--include/uapi/linux/tee.h23
-rw-r--r--include/uapi/linux/tls.h2
-rw-r--r--include/uapi/linux/v4l2-controls.h6
-rw-r--r--include/uapi/linux/vfio.h28
-rw-r--r--include/uapi/linux/videodev2.h4
-rw-r--r--include/uapi/linux/virtio_net.h3
-rw-r--r--include/uapi/linux/virtio_pci.h2
-rw-r--r--include/uapi/linux/wireguard.h191
-rw-r--r--include/uapi/sound/intel/avs/tokens.h6
-rw-r--r--include/ufs/ufshcd.h7
-rw-r--r--init/Kconfig39
-rw-r--r--init/do_mounts.c3
-rw-r--r--init/do_mounts_rd.c3
-rw-r--r--init/init_task.c30
-rw-r--r--init/version-timestamp.c7
-rw-r--r--io_uring/cancel.c270
-rw-r--r--io_uring/cancel.h8
-rw-r--r--io_uring/cmd_net.c24
-rw-r--r--io_uring/fdinfo.c45
-rw-r--r--io_uring/filetable.c2
-rw-r--r--io_uring/futex.c57
-rw-r--r--io_uring/io_uring.c551
-rw-r--r--io_uring/io_uring.h63
-rw-r--r--io_uring/kbuf.c39
-rw-r--r--io_uring/kbuf.h5
-rw-r--r--io_uring/memmap.c59
-rw-r--r--io_uring/memmap.h24
-rw-r--r--io_uring/mock_file.c43
-rw-r--r--io_uring/msg_ring.c3
-rw-r--r--io_uring/net.c15
-rw-r--r--io_uring/notif.c7
-rw-r--r--io_uring/opdef.c26
-rw-r--r--io_uring/opdef.h2
-rw-r--r--io_uring/poll.c13
-rw-r--r--io_uring/poll.h2
-rw-r--r--io_uring/query.c57
-rw-r--r--io_uring/query.h2
-rw-r--r--io_uring/register.c106
-rw-r--r--io_uring/rsrc.c57
-rw-r--r--io_uring/rsrc.h6
-rw-r--r--io_uring/rw.c29
-rw-r--r--io_uring/rw.h2
-rw-r--r--io_uring/slist.h18
-rw-r--r--io_uring/sqpoll.c66
-rw-r--r--io_uring/sqpoll.h1
-rw-r--r--io_uring/timeout.c20
-rw-r--r--io_uring/uring_cmd.c34
-rw-r--r--io_uring/waitid.c50
-rw-r--r--io_uring/zcrx.c441
-rw-r--r--io_uring/zcrx.h17
-rw-r--r--ipc/mqueue.c83
-rw-r--r--ipc/msgutil.c7
-rw-r--r--ipc/namespace.c3
-rw-r--r--kernel/Kconfig.kexec9
-rw-r--r--kernel/Makefile1
-rw-r--r--kernel/acct.c29
-rw-r--r--kernel/audit.h2
-rw-r--r--kernel/auditfilter.c3
-rw-r--r--kernel/auditsc.c43
-rw-r--r--kernel/bpf/Makefile2
-rw-r--r--kernel/bpf/arraymap.c38
-rw-r--r--kernel/bpf/bpf_insn_array.c304
-rw-r--r--kernel/bpf/bpf_iter.c29
-rw-r--r--kernel/bpf/bpf_local_storage.c235
-rw-r--r--kernel/bpf/bpf_lsm.c1
-rw-r--r--kernel/bpf/bpf_struct_ops.c2
-rw-r--r--kernel/bpf/cgroup.c8
-rw-r--r--kernel/bpf/core.c26
-rw-r--r--kernel/bpf/disasm.c3
-rw-r--r--kernel/bpf/hashtab.c67
-rw-r--r--kernel/bpf/helpers.c330
-rw-r--r--kernel/bpf/liveness.c42
-rw-r--r--kernel/bpf/log.c3
-rw-r--r--kernel/bpf/range_tree.c21
-rw-r--r--kernel/bpf/ringbuf.c116
-rw-r--r--kernel/bpf/rqspinlock.c90
-rw-r--r--kernel/bpf/stackmap.c66
-rw-r--r--kernel/bpf/stream.c162
-rw-r--r--kernel/bpf/syscall.c91
-rw-r--r--kernel/bpf/token.c47
-rw-r--r--kernel/bpf/trampoline.c88
-rw-r--r--kernel/bpf/verifier.c1001
-rw-r--r--kernel/cgroup/cgroup.c114
-rw-r--r--kernel/cgroup/cpuset-internal.h13
-rw-r--r--kernel/cgroup/cpuset.c362
-rw-r--r--kernel/cgroup/legacy_freezer.c2
-rw-r--r--kernel/cgroup/namespace.c2
-rw-r--r--kernel/cpu.c19
-rw-r--r--kernel/crash_core.c2
-rw-r--r--kernel/cred.c33
-rw-r--r--kernel/debug/kdb/kdb_io.c47
-rw-r--r--kernel/dma/contiguous.c11
-rw-r--r--kernel/dma/debug.c5
-rw-r--r--kernel/dma/direct.c5
-rw-r--r--kernel/entry/common.c39
-rw-r--r--kernel/entry/syscall-common.c8
-rw-r--r--kernel/events/callchain.c14
-rw-r--r--kernel/events/core.c100
-rw-r--r--kernel/exit.c15
-rw-r--r--kernel/fork.c13
-rw-r--r--kernel/freezer.c2
-rw-r--r--kernel/futex/core.c16
-rw-r--r--kernel/futex/futex.h58
-rw-r--r--kernel/gcov/gcc_4_7.c4
-rw-r--r--kernel/irq/chip.c35
-rw-r--r--kernel/irq/handle.c10
-rw-r--r--kernel/irq/irqdesc.c24
-rw-r--r--kernel/irq/irqdomain.c32
-rw-r--r--kernel/irq/manage.c176
-rw-r--r--kernel/irq/msi.c2
-rw-r--r--kernel/irq/proc.c2
-rw-r--r--kernel/kallsyms.c5
-rw-r--r--kernel/kexec_handover.c95
-rw-r--r--kernel/kexec_handover_debug.c25
-rw-r--r--kernel/kexec_handover_internal.h20
-rw-r--r--kernel/kthread.c15
-rw-r--r--kernel/livepatch/Kconfig12
-rw-r--r--kernel/livepatch/core.c16
-rw-r--r--kernel/locking/locktorture.c8
-rw-r--r--kernel/locking/mutex-debug.c10
-rw-r--r--kernel/locking/mutex.c28
-rw-r--r--kernel/locking/mutex.h5
-rw-r--r--kernel/locking/rtmutex_api.c19
-rw-r--r--kernel/locking/spinlock_debug.c4
-rw-r--r--kernel/module/main.c17
-rw-r--r--kernel/nscommon.c246
-rw-r--r--kernel/nsproxy.c57
-rw-r--r--kernel/nstree.c782
-rw-r--r--kernel/padata.c12
-rw-r--r--kernel/panic.c16
-rw-r--r--kernel/pid.c12
-rw-r--r--kernel/pid_namespace.c2
-rw-r--r--kernel/power/Kconfig11
-rw-r--r--kernel/power/Makefile4
-rw-r--r--kernel/power/console.c8
-rw-r--r--kernel/power/em_netlink.c308
-rw-r--r--kernel/power/em_netlink.h39
-rw-r--r--kernel/power/em_netlink_autogen.c48
-rw-r--r--kernel/power/em_netlink_autogen.h23
-rw-r--r--kernel/power/energy_model.c90
-rw-r--r--kernel/power/hibernate.c19
-rw-r--r--kernel/power/main.c103
-rw-r--r--kernel/power/power.h1
-rw-r--r--kernel/power/process.c1
-rw-r--r--kernel/power/qos.c106
-rw-r--r--kernel/power/snapshot.c13
-rw-r--r--kernel/power/suspend.c16
-rw-r--r--kernel/power/swap.c278
-rw-r--r--kernel/power/user.c4
-rw-r--r--kernel/printk/internal.h53
-rw-r--r--kernel/printk/nbcon.c174
-rw-r--r--kernel/printk/printk.c296
-rw-r--r--kernel/printk/printk_ringbuffer.c67
-rw-r--r--kernel/ptrace.c6
-rw-r--r--kernel/rcu/Kconfig.debug15
-rw-r--r--kernel/rcu/rcutorture.c76
-rw-r--r--kernel/rcu/refscale.c379
-rw-r--r--kernel/rcu/srcutiny.c13
-rw-r--r--kernel/rcu/srcutree.c130
-rw-r--r--kernel/rcu/tiny.c8
-rw-r--r--kernel/rcu/tree.c16
-rw-r--r--kernel/rcu/tree_exp.h3
-rw-r--r--kernel/rcu/tree_plugin.h9
-rw-r--r--kernel/rcu/tree_stall.h3
-rw-r--r--kernel/rcu/update.c8
-rw-r--r--kernel/rseq.c655
-rw-r--r--kernel/sched/autogroup.c4
-rw-r--r--kernel/sched/core.c1236
-rw-r--r--kernel/sched/cpudeadline.c34
-rw-r--r--kernel/sched/cpudeadline.h4
-rw-r--r--kernel/sched/cputime.c20
-rw-r--r--kernel/sched/deadline.c390
-rw-r--r--kernel/sched/debug.c8
-rw-r--r--kernel/sched/ext.c1270
-rw-r--r--kernel/sched/ext_idle.c43
-rw-r--r--kernel/sched/ext_internal.h29
-rw-r--r--kernel/sched/fair.c627
-rw-r--r--kernel/sched/features.h7
-rw-r--r--kernel/sched/idle.c41
-rw-r--r--kernel/sched/isolation.c23
-rw-r--r--kernel/sched/membarrier.c8
-rw-r--r--kernel/sched/rt.c13
-rw-r--r--kernel/sched/sched.h670
-rw-r--r--kernel/sched/stats.h2
-rw-r--r--kernel/sched/stop_task.c13
-rw-r--r--kernel/sched/syscalls.c100
-rw-r--r--kernel/sched/topology.c114
-rw-r--r--kernel/signal.c2
-rw-r--r--kernel/smp.c22
-rw-r--r--kernel/task_work.c8
-rw-r--r--kernel/time/hrtimer.c4
-rw-r--r--kernel/time/namespace.c5
-rw-r--r--kernel/time/posix-cpu-timers.c4
-rw-r--r--kernel/time/posix-timers.c14
-rw-r--r--kernel/time/tick-oneshot.c20
-rw-r--r--kernel/time/tick-sched.c41
-rw-r--r--kernel/time/timekeeping.c27
-rw-r--r--kernel/time/timer.c9
-rw-r--r--kernel/time/timer_migration.c487
-rw-r--r--kernel/time/timer_migration.h2
-rw-r--r--kernel/trace/Kconfig12
-rw-r--r--kernel/trace/blktrace.c533
-rw-r--r--kernel/trace/bpf_trace.c48
-rw-r--r--kernel/trace/ftrace.c77
-rw-r--r--kernel/trace/ring_buffer.c4
-rw-r--r--kernel/trace/rv/monitors/pagefault/Kconfig1
-rw-r--r--kernel/trace/rv/rv.c12
-rw-r--r--kernel/trace/trace.c10
-rw-r--r--kernel/trace/trace_events_hist.c6
-rw-r--r--kernel/trace/trace_events_user.c22
-rw-r--r--kernel/trace/trace_fprobe.c7
-rw-r--r--kernel/trace/trace_output.c6
-rw-r--r--kernel/unwind/deferred.c44
-rw-r--r--kernel/unwind/user.c59
-rw-r--r--kernel/user.c7
-rw-r--r--kernel/watch_queue.c4
-rw-r--r--kernel/watchdog.c9
-rw-r--r--kernel/workqueue.c86
-rw-r--r--lib/Kconfig.debug13
-rw-r--r--lib/Kconfig.kmsan2
-rw-r--r--lib/bitmap.c6
-rw-r--r--lib/bug.c90
-rw-r--r--lib/buildid.c56
-rw-r--r--lib/crc/arm/crc-t10dif.h19
-rw-r--r--lib/crc/arm/crc32.h11
-rw-r--r--lib/crc/arm64/crc-t10dif.h19
-rw-r--r--lib/crc/arm64/crc32.h16
-rw-r--r--lib/crypto/Kconfig40
-rw-r--r--lib/crypto/Makefile32
-rw-r--r--lib/crypto/arm/blake2b-neon-core.S (renamed from arch/arm/crypto/blake2b-neon-core.S)29
-rw-r--r--lib/crypto/arm/blake2b.h40
-rw-r--r--lib/crypto/arm/blake2s-core.S22
-rw-r--r--lib/crypto/arm/blake2s.h4
-rw-r--r--lib/crypto/arm/chacha.h11
-rw-r--r--lib/crypto/arm/curve25519.h5
-rw-r--r--lib/crypto/arm/poly1305.h6
-rw-r--r--lib/crypto/arm/sha1-armv7-neon.S2
-rw-r--r--lib/crypto/arm/sha1-ce-core.S2
-rw-r--r--lib/crypto/arm/sha1.h13
-rw-r--r--lib/crypto/arm/sha256-ce.S2
-rw-r--r--lib/crypto/arm/sha256.h12
-rw-r--r--lib/crypto/arm/sha512.h5
-rw-r--r--lib/crypto/arm64/chacha.h11
-rw-r--r--lib/crypto/arm64/poly1305.h6
-rw-r--r--lib/crypto/arm64/polyval-ce-core.S (renamed from arch/arm64/crypto/polyval-ce-core.S)38
-rw-r--r--lib/crypto/arm64/polyval.h80
-rw-r--r--lib/crypto/arm64/sha1-ce-core.S2
-rw-r--r--lib/crypto/arm64/sha1.h7
-rw-r--r--lib/crypto/arm64/sha256-ce.S2
-rw-r--r--lib/crypto/arm64/sha256.h19
-rw-r--r--lib/crypto/arm64/sha3-ce-core.S (renamed from arch/arm64/crypto/sha3-ce-core.S)69
-rw-r--r--lib/crypto/arm64/sha3.h59
-rw-r--r--lib/crypto/arm64/sha512-ce-core.S2
-rw-r--r--lib/crypto/arm64/sha512.h8
-rw-r--r--lib/crypto/blake2b.c174
-rw-r--r--lib/crypto/blake2s.c66
-rw-r--r--lib/crypto/chacha20poly1305.c18
-rw-r--r--lib/crypto/fips.h45
-rw-r--r--lib/crypto/mpi/mpicoder.c2
-rw-r--r--lib/crypto/polyval.c307
-rw-r--r--lib/crypto/s390/sha3.h151
-rw-r--r--lib/crypto/sha1.c19
-rw-r--r--lib/crypto/sha256.c26
-rw-r--r--lib/crypto/sha3.c411
-rw-r--r--lib/crypto/sha512.c19
-rw-r--r--lib/crypto/tests/Kconfig29
-rw-r--r--lib/crypto/tests/Makefile3
-rw-r--r--lib/crypto/tests/blake2b-testvecs.h342
-rw-r--r--lib/crypto/tests/blake2b_kunit.c133
-rw-r--r--lib/crypto/tests/blake2s_kunit.c39
-rw-r--r--lib/crypto/tests/polyval-testvecs.h186
-rw-r--r--lib/crypto/tests/polyval_kunit.c223
-rw-r--r--lib/crypto/tests/sha256_kunit.c1
-rw-r--r--lib/crypto/tests/sha3-testvecs.h249
-rw-r--r--lib/crypto/tests/sha3_kunit.c422
-rw-r--r--lib/crypto/x86/blake2s-core.S275
-rw-r--r--lib/crypto/x86/blake2s.h22
-rw-r--r--lib/crypto/x86/polyval-pclmul-avx.S (renamed from arch/x86/crypto/polyval-clmulni_asm.S)40
-rw-r--r--lib/crypto/x86/polyval.h83
-rw-r--r--lib/debugobjects.c6
-rw-r--r--lib/interval_tree.c1
-rw-r--r--lib/iov_iter.c22
-rw-r--r--lib/kfifo.c8
-rw-r--r--lib/kunit/Kconfig24
-rw-r--r--lib/kunit/executor.c8
-rw-r--r--lib/kunit/kunit-test.c2
-rw-r--r--lib/kunit/test.c3
-rw-r--r--lib/locking-selftest.c4
-rw-r--r--lib/maple_tree.c30
-rw-r--r--lib/raid6/neon.c17
-rw-r--r--lib/raid6/recov_neon.c15
-rw-r--r--lib/strncpy_from_user.c2
-rw-r--r--lib/strnlen_user.c2
-rw-r--r--lib/test_kho.c3
-rw-r--r--lib/tests/printf_kunit.c4
-rw-r--r--lib/tests/string_kunit.c13
-rw-r--r--lib/vsprintf.c80
-rw-r--r--mm/Kconfig7
-rw-r--r--mm/backing-dev.c2
-rw-r--r--mm/damon/core.c7
-rw-r--r--mm/damon/stat.c9
-rw-r--r--mm/damon/sysfs.c17
-rw-r--r--mm/fadvise.c3
-rw-r--r--mm/filemap.c204
-rw-r--r--mm/hmm.c2
-rw-r--r--mm/huge_memory.c99
-rw-r--r--mm/hugetlb.c5
-rw-r--r--mm/internal.h2
-rw-r--r--mm/kasan/common.c12
-rw-r--r--mm/kfence/core.c14
-rw-r--r--mm/kmsan/core.c3
-rw-r--r--mm/kmsan/hooks.c6
-rw-r--r--mm/kmsan/shadow.c2
-rw-r--r--mm/ksm.c113
-rw-r--r--mm/memblock.c3
-rw-r--r--mm/memcontrol.c40
-rw-r--r--mm/memfd.c70
-rw-r--r--mm/memory.c82
-rw-r--r--mm/memory_hotplug.c17
-rw-r--r--mm/mempool.c441
-rw-r--r--mm/migrate.c3
-rw-r--r--mm/mm_init.c2
-rw-r--r--mm/mmap_lock.c1
-rw-r--r--mm/mremap.c17
-rw-r--r--mm/page-writeback.c6
-rw-r--r--mm/page_alloc.c24
-rw-r--r--mm/page_owner.c3
-rw-r--r--mm/secretmem.c22
-rw-r--r--mm/shmem.c32
-rw-r--r--mm/slab.h112
-rw-r--r--mm/slab_common.c29
-rw-r--r--mm/slub.c743
-rw-r--r--mm/sparse.c3
-rw-r--r--mm/swap_state.c13
-rw-r--r--mm/swapfile.c4
-rw-r--r--mm/truncate.c47
-rw-r--r--mm/usercopy.c24
-rw-r--r--mm/vmscan.c2
-rw-r--r--mm/workingset.c2
-rw-r--r--net/8021q/vlan.c2
-rw-r--r--net/9p/trans_fd.c8
-rw-r--r--net/Kconfig8
-rw-r--r--net/appletalk/ddp.c4
-rw-r--r--net/atm/clip.c4
-rw-r--r--net/atm/common.c4
-rw-r--r--net/atm/pvc.c4
-rw-r--r--net/atm/svc.c4
-rw-r--r--net/ax25/af_ax25.c4
-rw-r--r--net/batman-adv/Kconfig1
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c51
-rw-r--r--net/batman-adv/main.h2
-rw-r--r--net/batman-adv/originator.c14
-rw-r--r--net/batman-adv/types.h2
-rw-r--r--net/bluetooth/6lowpan.c105
-rw-r--r--net/bluetooth/hci_conn.c95
-rw-r--r--net/bluetooth/hci_core.c89
-rw-r--r--net/bluetooth/hci_event.c296
-rw-r--r--net/bluetooth/hci_sock.c4
-rw-r--r--net/bluetooth/hci_sync.c277
-rw-r--r--net/bluetooth/iso.c253
-rw-r--r--net/bluetooth/l2cap_core.c28
-rw-r--r--net/bluetooth/l2cap_sock.c4
-rw-r--r--net/bluetooth/mgmt.c193
-rw-r--r--net/bluetooth/rfcomm/core.c6
-rw-r--r--net/bluetooth/rfcomm/sock.c5
-rw-r--r--net/bluetooth/rfcomm/tty.c26
-rw-r--r--net/bluetooth/sco.c39
-rw-r--r--net/bluetooth/smp.c31
-rw-r--r--net/bpf/test_run.c148
-rw-r--r--net/bridge/br_forward.c2
-rw-r--r--net/bridge/br_if.c23
-rw-r--r--net/bridge/br_input.c4
-rw-r--r--net/bridge/br_mst.c10
-rw-r--r--net/bridge/br_multicast.c9
-rw-r--r--net/bridge/br_netlink.c2
-rw-r--r--net/bridge/br_private.h13
-rw-r--r--net/caif/caif_socket.c2
-rw-r--r--net/can/Kconfig1
-rw-r--r--net/can/bcm.c2
-rw-r--r--net/can/isotp.c2
-rw-r--r--net/can/j1939/socket.c4
-rw-r--r--net/can/raw.c56
-rw-r--r--net/ceph/auth_x.c2
-rw-r--r--net/ceph/ceph_common.c58
-rw-r--r--net/ceph/debugfs.c14
-rw-r--r--net/ceph/messenger.c2
-rw-r--r--net/ceph/messenger_v2.c17
-rw-r--r--net/ceph/osdmap.c18
-rw-r--r--net/compat.c4
-rw-r--r--net/core/bpf_sk_storage.c16
-rw-r--r--net/core/datagram.c44
-rw-r--r--net/core/dev.c379
-rw-r--r--net/core/dev.h4
-rw-r--r--net/core/dev_ioctl.c14
-rw-r--r--net/core/devmem.c33
-rw-r--r--net/core/devmem.h1
-rw-r--r--net/core/filter.c143
-rw-r--r--net/core/gro.c10
-rw-r--r--net/core/gro_cells.c9
-rw-r--r--net/core/hotdata.c2
-rw-r--r--net/core/neighbour.c131
-rw-r--r--net/core/net_namespace.c14
-rw-r--r--net/core/netdev-genl-gen.c3
-rw-r--r--net/core/netdev-genl-gen.h1
-rw-r--r--net/core/netmem_priv.h16
-rw-r--r--net/core/netpoll.c16
-rw-r--r--net/core/page_pool.c4
-rw-r--r--net/core/rtnetlink.c18
-rw-r--r--net/core/scm.c20
-rw-r--r--net/core/selftests.c48
-rw-r--r--net/core/skbuff.c117
-rw-r--r--net/core/sock.c120
-rw-r--r--net/core/sysctl_net_core.c16
-rw-r--r--net/devlink/netlink_gen.c8
-rw-r--r--net/devlink/netlink_gen.h1
-rw-r--r--net/devlink/param.c185
-rw-r--r--net/devlink/rate.c4
-rw-r--r--net/devlink/region.c2
-rw-r--r--net/dns_resolver/dns_query.c6
-rw-r--r--net/dsa/Kconfig14
-rw-r--r--net/dsa/Makefile2
-rw-r--r--net/dsa/conduit.c145
-rw-r--r--net/dsa/devlink.c3
-rw-r--r--net/dsa/dsa.c65
-rw-r--r--net/dsa/port.c3
-rw-r--r--net/dsa/tag.h18
-rw-r--r--net/dsa/tag_brcm.c24
-rw-r--r--net/dsa/tag_gswip.c6
-rw-r--r--net/dsa/tag_hellcreek.c3
-rw-r--r--net/dsa/tag_ksz.c20
-rw-r--r--net/dsa/tag_mtk.c3
-rw-r--r--net/dsa/tag_mxl-gsw1xx.c117
-rw-r--r--net/dsa/tag_ocelot.c6
-rw-r--r--net/dsa/tag_qca.c3
-rw-r--r--net/dsa/tag_rtl4_a.c2
-rw-r--r--net/dsa/tag_rtl8_4.c3
-rw-r--r--net/dsa/tag_rzn1_a5psw.c3
-rw-r--r--net/dsa/tag_trailer.c3
-rw-r--r--net/dsa/tag_xrs700x.c8
-rw-r--r--net/dsa/tag_yt921x.c139
-rw-r--r--net/ethernet/eth.c16
-rw-r--r--net/ethtool/Makefile2
-rw-r--r--net/ethtool/common.c8
-rw-r--r--net/ethtool/mse.c329
-rw-r--r--net/ethtool/netlink.c10
-rw-r--r--net/ethtool/netlink.h2
-rw-r--r--net/handshake/genl.c1
-rw-r--r--net/handshake/genl.h1
-rw-r--r--net/handshake/netlink.c38
-rw-r--r--net/handshake/tlshd.c1
-rw-r--r--net/hsr/hsr_device.c25
-rw-r--r--net/hsr/hsr_forward.c22
-rw-r--r--net/hsr/hsr_netlink.c16
-rw-r--r--net/hsr/hsr_slave.c7
-rw-r--r--net/ieee802154/socket.c12
-rw-r--r--net/ipv4/Kconfig4
-rw-r--r--net/ipv4/af_inet.c22
-rw-r--r--net/ipv4/arp.c6
-rw-r--r--net/ipv4/datagram.c4
-rw-r--r--net/ipv4/esp4_offload.c6
-rw-r--r--net/ipv4/fou_nl.c1
-rw-r--r--net/ipv4/fou_nl.h1
-rw-r--r--net/ipv4/icmp.c191
-rw-r--r--net/ipv4/inet_connection_sock.c56
-rw-r--r--net/ipv4/inet_diag.c8
-rw-r--r--net/ipv4/inet_hashtables.c8
-rw-r--r--net/ipv4/inet_timewait_sock.c35
-rw-r--r--net/ipv4/ip_input.c4
-rw-r--r--net/ipv4/ipconfig.c3
-rw-r--r--net/ipv4/ipip.c25
-rw-r--r--net/ipv4/ping.c8
-rw-r--r--net/ipv4/raw.c3
-rw-r--r--net/ipv4/route.c5
-rw-r--r--net/ipv4/sysctl_net_ipv4.c29
-rw-r--r--net/ipv4/tcp.c125
-rw-r--r--net/ipv4/tcp_input.c87
-rw-r--r--net/ipv4/tcp_ipv4.c154
-rw-r--r--net/ipv4/tcp_lp.c7
-rw-r--r--net/ipv4/tcp_minisocks.c8
-rw-r--r--net/ipv4/tcp_offload.c27
-rw-r--r--net/ipv4/tcp_output.c38
-rw-r--r--net/ipv4/tcp_timer.c26
-rw-r--r--net/ipv4/udp.c6
-rw-r--r--net/ipv4/udp_tunnel_core.c4
-rw-r--r--net/ipv6/addrconf.c2
-rw-r--r--net/ipv6/af_inet6.c7
-rw-r--r--net/ipv6/datagram.c8
-rw-r--r--net/ipv6/esp6_offload.c6
-rw-r--r--net/ipv6/icmp.c214
-rw-r--r--net/ipv6/ip6_fib.c4
-rw-r--r--net/ipv6/ip6_flowlabel.c44
-rw-r--r--net/ipv6/ip6_udp_tunnel.c4
-rw-r--r--net/ipv6/ndisc.c8
-rw-r--r--net/ipv6/ping.c2
-rw-r--r--net/ipv6/raw.c3
-rw-r--r--net/ipv6/tcp_ipv6.c147
-rw-r--r--net/ipv6/udp.c5
-rw-r--r--net/iucv/af_iucv.c16
-rw-r--r--net/iucv/iucv.c5
-rw-r--r--net/kcm/Kconfig4
-rw-r--r--net/kcm/kcmsock.c22
-rw-r--r--net/key/af_key.c2
-rw-r--r--net/l2tp/l2tp_core.c14
-rw-r--r--net/l2tp/l2tp_debugfs.c2
-rw-r--r--net/l2tp/l2tp_ip.c6
-rw-r--r--net/l2tp/l2tp_ip6.c5
-rw-r--r--net/l2tp/l2tp_ppp.c2
-rw-r--r--net/llc/af_llc.c4
-rw-r--r--net/mac80211/aes_cmac.c60
-rw-r--r--net/mac80211/aes_cmac.h7
-rw-r--r--net/mac80211/aes_gmac.c22
-rw-r--r--net/mac80211/aes_gmac.h1
-rw-r--r--net/mac80211/agg-rx.c7
-rw-r--r--net/mac80211/cfg.c50
-rw-r--r--net/mac80211/chan.c412
-rw-r--r--net/mac80211/driver-ops.c8
-rw-r--r--net/mac80211/he.c6
-rw-r--r--net/mac80211/ibss.c14
-rw-r--r--net/mac80211/ieee80211_i.h58
-rw-r--r--net/mac80211/iface.c60
-rw-r--r--net/mac80211/key.c11
-rw-r--r--net/mac80211/link.c9
-rw-r--r--net/mac80211/main.c3
-rw-r--r--net/mac80211/mesh.c26
-rw-r--r--net/mac80211/mesh_hwmp.c7
-rw-r--r--net/mac80211/mesh_plink.c7
-rw-r--r--net/mac80211/mlme.c142
-rw-r--r--net/mac80211/parse.c30
-rw-r--r--net/mac80211/rx.c188
-rw-r--r--net/mac80211/scan.c6
-rw-r--r--net/mac80211/tdls.c12
-rw-r--r--net/mac80211/tests/elems.c4
-rw-r--r--net/mac80211/tx.c6
-rw-r--r--net/mac80211/util.c35
-rw-r--r--net/mac80211/wpa.c148
-rw-r--r--net/mac80211/wpa.h10
-rw-r--r--net/mctp/af_mctp.c4
-rw-r--r--net/mctp/route.c1
-rw-r--r--net/mctp/test/route-test.c111
-rw-r--r--net/mctp/test/utils.c50
-rw-r--r--net/mctp/test/utils.h13
-rw-r--r--net/mpls/af_mpls.c321
-rw-r--r--net/mpls/internal.h19
-rw-r--r--net/mpls/mpls_iptunnel.c6
-rw-r--r--net/mptcp/fastopen.c4
-rw-r--r--net/mptcp/mib.c2
-rw-r--r--net/mptcp/mib.h2
-rw-r--r--net/mptcp/mptcp_diag.c3
-rw-r--r--net/mptcp/mptcp_pm_gen.c1
-rw-r--r--net/mptcp/mptcp_pm_gen.h1
-rw-r--r--net/mptcp/options.c54
-rw-r--r--net/mptcp/pm.c24
-rw-r--r--net/mptcp/pm_kernel.c50
-rw-r--r--net/mptcp/protocol.c643
-rw-r--r--net/mptcp/protocol.h57
-rw-r--r--net/mptcp/sockopt.c2
-rw-r--r--net/mptcp/subflow.c54
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/ipvs/ip_vs_app.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_conn.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_dh.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_est.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_fo.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_ftp.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_lblc.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_lblcr.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_lc.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_mh.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_nfct.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_nq.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_ovf.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_pe.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_pe_sip.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_proto.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_ah_esp.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_tcp.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_udp.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_rr.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sched.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sed.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sh.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sync.c9
-rw-r--r--net/netfilter/ipvs/ip_vs_twos.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_wlc.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_wrr.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c3
-rw-r--r--net/netfilter/nf_conncount.c211
-rw-r--r--net/netfilter/nf_conntrack_core.c2
-rw-r--r--net/netfilter/nf_conntrack_standalone.c4
-rw-r--r--net/netfilter/nf_flow_table_core.c5
-rw-r--r--net/netfilter/nf_flow_table_ip.c293
-rw-r--r--net/netfilter/nf_flow_table_offload.c2
-rw-r--r--net/netfilter/nf_flow_table_path.c330
-rw-r--r--net/netfilter/nf_tables_api.c34
-rw-r--r--net/netfilter/nft_connlimit.c56
-rw-r--r--net/netfilter/nft_ct.c30
-rw-r--r--net/netfilter/nft_flow_offload.c252
-rw-r--r--net/netfilter/nft_lookup.c13
-rw-r--r--net/netfilter/xt_connlimit.c14
-rw-r--r--net/netlink/af_netlink.c8
-rw-r--r--net/netrom/af_netrom.c6
-rw-r--r--net/nfc/llcp_sock.c6
-rw-r--r--net/nfc/rawsock.c2
-rw-r--r--net/openvswitch/actions.c68
-rw-r--r--net/openvswitch/conntrack.c16
-rw-r--r--net/openvswitch/flow_netlink.c64
-rw-r--r--net/openvswitch/flow_netlink.h2
-rw-r--r--net/packet/af_packet.c15
-rw-r--r--net/phonet/pep.c3
-rw-r--r--net/phonet/socket.c10
-rw-r--r--net/psp/psp-nl-gen.c20
-rw-r--r--net/psp/psp-nl-gen.h3
-rw-r--r--net/psp/psp_main.c3
-rw-r--r--net/psp/psp_nl.c93
-rw-r--r--net/psp/psp_sock.c4
-rw-r--r--net/qrtr/af_qrtr.c4
-rw-r--r--net/qrtr/ns.c2
-rw-r--r--net/rds/af_rds.c2
-rw-r--r--net/rds/bind.c2
-rw-r--r--net/rds/rds.h2
-rw-r--r--net/rds/tcp_connect.c4
-rw-r--r--net/rds/tcp_listen.c2
-rw-r--r--net/rose/af_rose.c5
-rw-r--r--net/rxrpc/af_rxrpc.c4
-rw-r--r--net/rxrpc/rxperf.c2
-rw-r--r--net/sched/act_bpf.c6
-rw-r--r--net/sched/act_connmark.c12
-rw-r--r--net/sched/act_ct.c8
-rw-r--r--net/sched/act_ife.c12
-rw-r--r--net/sched/act_mirred.c62
-rw-r--r--net/sched/cls_api.c8
-rw-r--r--net/sched/cls_bpf.c6
-rw-r--r--net/sched/cls_flower.c2
-rw-r--r--net/sched/em_canid.c3
-rw-r--r--net/sched/em_cmp.c5
-rw-r--r--net/sched/em_nbyte.c2
-rw-r--r--net/sched/em_text.c11
-rw-r--r--net/sched/sch_api.c5
-rw-r--r--net/sched/sch_cake.c77
-rw-r--r--net/sched/sch_codel.c4
-rw-r--r--net/sched/sch_dualpi2.c1
-rw-r--r--net/sched/sch_fq.c9
-rw-r--r--net/sched/sch_fq_codel.c5
-rw-r--r--net/sched/sch_generic.c24
-rw-r--r--net/sched/sch_netem.c1
-rw-r--r--net/sched/sch_qfq.c2
-rw-r--r--net/sched/sch_taprio.c1
-rw-r--r--net/sched/sch_tbf.c1
-rw-r--r--net/sctp/diag.c21
-rw-r--r--net/sctp/input.c2
-rw-r--r--net/sctp/inqueue.c13
-rw-r--r--net/sctp/ipv6.c49
-rw-r--r--net/sctp/protocol.c33
-rw-r--r--net/sctp/socket.c223
-rw-r--r--net/sctp/stream.c8
-rw-r--r--net/sctp/stream_sched.c16
-rw-r--r--net/sctp/stream_sched_fc.c4
-rw-r--r--net/sctp/stream_sched_prio.c2
-rw-r--r--net/sctp/stream_sched_rr.c2
-rw-r--r--net/sctp/transport.c34
-rw-r--r--net/shaper/shaper_nl_gen.c1
-rw-r--r--net/shaper/shaper_nl_gen.h1
-rw-r--r--net/smc/Kconfig10
-rw-r--r--net/smc/Makefile1
-rw-r--r--net/smc/af_smc.c34
-rw-r--r--net/smc/smc.h4
-rw-r--r--net/smc/smc_clc.c1
-rw-r--r--net/smc/smc_core.c34
-rw-r--r--net/smc/smc_core.h8
-rw-r--r--net/smc/smc_hs_bpf.c140
-rw-r--r--net/smc/smc_hs_bpf.h31
-rw-r--r--net/smc/smc_ib.c10
-rw-r--r--net/smc/smc_inet.c13
-rw-r--r--net/smc/smc_llc.c2
-rw-r--r--net/smc/smc_sysctl.c113
-rw-r--r--net/smc/smc_sysctl.h2
-rw-r--r--net/smc/smc_wr.c31
-rw-r--r--net/smc/smc_wr.h2
-rw-r--r--net/socket.c115
-rw-r--r--net/strparser/strparser.c4
-rw-r--r--net/sunrpc/Kconfig3
-rw-r--r--net/sunrpc/clnt.c6
-rw-r--r--net/sunrpc/svcsock.c2
-rw-r--r--net/sunrpc/xprtsock.c9
-rw-r--r--net/tipc/net.c2
-rw-r--r--net/tipc/socket.c10
-rw-r--r--net/tls/tls_device.c9
-rw-r--r--net/tls/tls_main.c64
-rw-r--r--net/tls/tls_sw.c2
-rw-r--r--net/unix/af_unix.c68
-rw-r--r--net/unix/af_unix.h4
-rw-r--r--net/unix/garbage.c102
-rw-r--r--net/vmw_vsock/af_vsock.c84
-rw-r--r--net/vmw_vsock/vsock_addr.c2
-rw-r--r--net/wireless/core.c88
-rw-r--r--net/wireless/core.h4
-rw-r--r--net/wireless/debugfs.c33
-rw-r--r--net/wireless/mlme.c19
-rw-r--r--net/wireless/nl80211.c13
-rw-r--r--net/wireless/scan.c20
-rw-r--r--net/wireless/sysfs.c2
-rw-r--r--net/wireless/trace.h21
-rw-r--r--net/wireless/util.c29
-rw-r--r--net/x25/af_x25.c4
-rw-r--r--net/xdp/xsk.c163
-rw-r--r--net/xdp/xsk_buff_pool.c21
-rw-r--r--net/xfrm/Kconfig11
-rw-r--r--net/xfrm/espintcp.c6
-rw-r--r--net/xfrm/xfrm_device.c2
-rw-r--r--net/xfrm/xfrm_input.c30
-rw-r--r--net/xfrm/xfrm_output.c8
-rw-r--r--net/xfrm/xfrm_state.c30
-rw-r--r--net/xfrm/xfrm_user.c8
-rw-r--r--rust/Makefile164
-rw-r--r--rust/bindings/bindings_helper.h1
-rw-r--r--rust/ffi.rs2
-rw-r--r--rust/helpers/helpers.c1
-rw-r--r--rust/helpers/pwm.c20
-rw-r--r--rust/kernel/acpi.rs4
-rw-r--r--rust/kernel/alloc/kvec/errors.rs14
-rw-r--r--rust/kernel/auxiliary.rs8
-rw-r--r--rust/kernel/block/mq.rs5
-rw-r--r--rust/kernel/block/mq/operations.rs4
-rw-r--r--rust/kernel/block/mq/request.rs8
-rw-r--r--rust/kernel/clk.rs4
-rw-r--r--rust/kernel/configfs.rs2
-rw-r--r--rust/kernel/debugfs.rs2
-rw-r--r--rust/kernel/debugfs/callback_adapters.rs7
-rw-r--r--rust/kernel/debugfs/entry.rs2
-rw-r--r--rust/kernel/debugfs/file_ops.rs6
-rw-r--r--rust/kernel/debugfs/traits.rs65
-rw-r--r--rust/kernel/device.rs5
-rw-r--r--rust/kernel/devres.rs2
-rw-r--r--rust/kernel/drm/gem/mod.rs53
-rw-r--r--rust/kernel/drm/ioctl.rs4
-rw-r--r--rust/kernel/error.rs2
-rw-r--r--rust/kernel/firmware.rs15
-rw-r--r--rust/kernel/fmt.rs87
-rw-r--r--rust/kernel/init.rs3
-rw-r--r--rust/kernel/lib.rs7
-rw-r--r--rust/kernel/num.rs79
-rw-r--r--rust/kernel/num/bounded.rs1058
-rw-r--r--rust/kernel/opp.rs120
-rw-r--r--rust/kernel/pci/id.rs3
-rw-r--r--rust/kernel/prelude.rs10
-rw-r--r--rust/kernel/ptr.rs1
-rw-r--r--rust/kernel/pwm.rs735
-rw-r--r--rust/kernel/rbtree.rs244
-rw-r--r--rust/kernel/regulator.rs13
-rw-r--r--rust/kernel/seq_file.rs2
-rw-r--r--rust/kernel/slice.rs49
-rw-r--r--rust/kernel/str.rs454
-rw-r--r--rust/kernel/sync.rs3
-rw-r--r--rust/kernel/sync/atomic.rs12
-rw-r--r--rust/kernel/sync/condvar.rs4
-rw-r--r--rust/kernel/sync/lock.rs43
-rw-r--r--rust/kernel/sync/lock/global.rs7
-rw-r--r--rust/kernel/transmute.rs63
-rw-r--r--rust/kernel/types.rs1
-rw-r--r--rust/macros/fmt.rs94
-rw-r--r--rust/macros/lib.rs19
-rw-r--r--rust/macros/module.rs10
-rw-r--r--rust/macros/quote.rs7
-rw-r--r--rust/pin-init/README.md2
-rw-r--r--rust/pin-init/src/macros.rs2
-rw-r--r--rust/proc-macro2/README.md13
-rw-r--r--rust/proc-macro2/detection.rs77
-rw-r--r--rust/proc-macro2/extra.rs153
-rw-r--r--rust/proc-macro2/fallback.rs1258
-rw-r--r--rust/proc-macro2/lib.rs1351
-rw-r--r--rust/proc-macro2/location.rs31
-rw-r--r--rust/proc-macro2/marker.rs19
-rw-r--r--rust/proc-macro2/parse.rs997
-rw-r--r--rust/proc-macro2/probe.rs12
-rw-r--r--rust/proc-macro2/probe/proc_macro_span.rs53
-rw-r--r--rust/proc-macro2/probe/proc_macro_span_file.rs16
-rw-r--r--rust/proc-macro2/probe/proc_macro_span_location.rs23
-rw-r--r--rust/proc-macro2/rcvec.rs148
-rw-r--r--rust/proc-macro2/wrapper.rs986
-rw-r--r--rust/quote/README.md12
-rw-r--r--rust/quote/ext.rs112
-rw-r--r--rust/quote/format.rs170
-rw-r--r--rust/quote/ident_fragment.rs90
-rw-r--r--rust/quote/lib.rs1456
-rw-r--r--rust/quote/runtime.rs494
-rw-r--r--rust/quote/spanned.rs52
-rw-r--r--rust/quote/to_tokens.rs273
-rw-r--r--rust/syn/README.md13
-rw-r--r--rust/syn/attr.rs838
-rw-r--r--rust/syn/bigint.rs68
-rw-r--r--rust/syn/buffer.rs436
-rw-r--r--rust/syn/classify.rs313
-rw-r--r--rust/syn/custom_keyword.rs262
-rw-r--r--rust/syn/custom_punctuation.rs306
-rw-r--r--rust/syn/data.rs426
-rw-r--r--rust/syn/derive.rs261
-rw-r--r--rust/syn/discouraged.rs227
-rw-r--r--rust/syn/drops.rs60
-rw-r--r--rust/syn/error.rs469
-rw-r--r--rust/syn/export.rs75
-rw-r--r--rust/syn/expr.rs4175
-rw-r--r--rust/syn/ext.rs138
-rw-r--r--rust/syn/file.rs127
-rw-r--r--rust/syn/fixup.rs775
-rw-r--r--rust/syn/gen/clone.rs2269
-rw-r--r--rust/syn/gen/debug.rs3240
-rw-r--r--rust/syn/gen/eq.rs2308
-rw-r--r--rust/syn/gen/fold.rs3904
-rw-r--r--rust/syn/gen/hash.rs2878
-rw-r--r--rust/syn/gen/visit.rs3943
-rw-r--r--rust/syn/gen/visit_mut.rs3761
-rw-r--r--rust/syn/generics.rs1479
-rw-r--r--rust/syn/group.rs293
-rw-r--r--rust/syn/ident.rs110
-rw-r--r--rust/syn/item.rs3492
-rw-r--r--rust/syn/lib.rs1013
-rw-r--r--rust/syn/lifetime.rs158
-rw-r--r--rust/syn/lit.rs1862
-rw-r--r--rust/syn/lookahead.rs334
-rw-r--r--rust/syn/mac.rs227
-rw-r--r--rust/syn/macros.rs184
-rw-r--r--rust/syn/meta.rs429
-rw-r--r--rust/syn/op.rs221
-rw-r--r--rust/syn/parse.rs1421
-rw-r--r--rust/syn/parse_macro_input.rs130
-rw-r--r--rust/syn/parse_quote.rs242
-rw-r--r--rust/syn/pat.rs957
-rw-r--r--rust/syn/path.rs968
-rw-r--r--rust/syn/precedence.rs212
-rw-r--r--rust/syn/print.rs18
-rw-r--r--rust/syn/punctuated.rs1157
-rw-r--r--rust/syn/restriction.rs180
-rw-r--r--rust/syn/scan_expr.rs267
-rw-r--r--rust/syn/sealed.rs6
-rw-r--r--rust/syn/span.rs65
-rw-r--r--rust/syn/spanned.rs120
-rw-r--r--rust/syn/stmt.rs486
-rw-r--r--rust/syn/thread.rs62
-rw-r--r--rust/syn/token.rs1098
-rw-r--r--rust/syn/tt.rs109
-rw-r--r--rust/syn/ty.rs1273
-rw-r--r--rust/syn/verbatim.rs35
-rw-r--r--rust/syn/whitespace.rs67
-rwxr-xr-xsamples/bpf/do_hbm_test.sh2
-rw-r--r--samples/bpf/hbm.c4
-rw-r--r--samples/bpf/tcp_cong_kern.c2
-rw-r--r--samples/bpf/tracex1.bpf.c2
-rw-r--r--samples/qmi/qmi_sample_client.c2
-rw-r--r--samples/rust/rust_debugfs.rs12
-rw-r--r--samples/rust/rust_debugfs_scoped.rs8
-rw-r--r--samples/rust/rust_driver_platform.rs4
-rw-r--r--samples/vfio-mdev/mbochs.c71
-rw-r--r--samples/vfio-mdev/mdpy.c34
-rw-r--r--samples/vfio-mdev/mtty.c35
-rw-r--r--samples/vfs/test-statx.c6
-rw-r--r--samples/watch_queue/watch_test.c6
-rw-r--r--scripts/Kconfig.include3
-rw-r--r--scripts/Makefile.build2
-rw-r--r--scripts/Makefile.dtbs9
-rw-r--r--scripts/Makefile.lib6
-rw-r--r--scripts/Makefile.modinst2
-rw-r--r--scripts/Makefile.package20
-rw-r--r--scripts/Makefile.vmlinux15
-rw-r--r--scripts/Makefile.vmlinux_o6
-rw-r--r--scripts/Makefile.warn (renamed from scripts/Makefile.extrawarn)4
-rwxr-xr-xscripts/atomic/gen-atomic-instrumented.sh11
-rwxr-xr-xscripts/cc-can-link.sh2
-rwxr-xr-xscripts/check-function-names.sh25
-rwxr-xr-xscripts/check-variable-fonts.sh115
-rwxr-xr-xscripts/clang-tools/gen_compile_commands.py135
-rwxr-xr-xscripts/crypto/gen-fips-testvecs.py36
-rwxr-xr-xscripts/crypto/gen-hash-testvecs.py101
-rwxr-xr-xscripts/decode_stacktrace.sh14
-rwxr-xr-xscripts/faddr2line19
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.c3
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.h2
-rw-r--r--scripts/gendwarfksyms/symbols.c4
-rwxr-xr-xscripts/generate_rust_analyzer.py25
-rwxr-xr-xscripts/get_feat.pl641
-rwxr-xr-xscripts/headers_install.sh2
-rwxr-xr-xscripts/jobserver-exec88
-rw-r--r--scripts/kconfig/mconf.c3
-rw-r--r--scripts/kconfig/nconf.c3
-rwxr-xr-xscripts/kernel-doc.pl2439
-rwxr-xr-xscripts/kernel-doc.py6
-rwxr-xr-xscripts/link-vmlinux.sh3
-rwxr-xr-xscripts/livepatch/fix-patch-lines79
-rw-r--r--scripts/livepatch/init.c108
-rwxr-xr-xscripts/livepatch/klp-build831
-rw-r--r--scripts/mod/modpost.c5
-rw-r--r--scripts/module.lds.S22
-rwxr-xr-xscripts/package/install-extmod-build2
-rw-r--r--scripts/rustdoc_test_gen.rs1
-rwxr-xr-xscripts/sphinx-build-wrapper719
-rwxr-xr-xscripts/split-man.pl28
-rw-r--r--scripts/syscall.tbl1
-rw-r--r--security/Makefile2
-rw-r--r--security/apparmor/apparmorfs.c12
-rw-r--r--security/apparmor/crypto.c3
-rw-r--r--security/apparmor/include/apparmorfs.h2
-rw-r--r--security/apparmor/include/crypto.h1
-rw-r--r--security/apparmor/lsm.c11
-rw-r--r--security/bpf/hooks.c2
-rw-r--r--security/commoncap.c36
-rw-r--r--security/device_cgroup.c56
-rw-r--r--security/inode.c46
-rw-r--r--security/integrity/evm/evm_main.c5
-rw-r--r--security/integrity/evm/evm_secfs.c11
-rw-r--r--security/integrity/iint.c14
-rw-r--r--security/integrity/ima/ima_fs.c11
-rw-r--r--security/integrity/ima/ima_main.c68
-rw-r--r--security/integrity/ima/ima_policy.c62
-rw-r--r--security/integrity/integrity.h2
-rw-r--r--security/ipe/audit.c1
-rw-r--r--security/ipe/fs.c4
-rw-r--r--security/ipe/hooks.c30
-rw-r--r--security/ipe/hooks.h3
-rw-r--r--security/ipe/ipe.c4
-rw-r--r--security/ipe/ipe.h2
-rw-r--r--security/keys/big_key.c2
-rw-r--r--security/keys/encrypted-keys/ecryptfs_format.c3
-rw-r--r--security/keys/encrypted-keys/encrypted.c4
-rw-r--r--security/keys/process_keys.c2
-rw-r--r--security/keys/trusted-keys/trusted_caam.c108
-rw-r--r--security/keys/trusted-keys/trusted_core.c4
-rw-r--r--security/keys/trusted-keys/trusted_tpm2.c55
-rw-r--r--security/keys/user_defined.c2
-rw-r--r--security/landlock/fs.c9
-rw-r--r--security/landlock/setup.c2
-rw-r--r--security/loadpin/loadpin.c13
-rw-r--r--security/lockdown/lockdown.c5
-rw-r--r--security/lsm.h58
-rw-r--r--security/lsm_init.c564
-rw-r--r--security/lsm_notifier.c31
-rw-r--r--security/lsm_syscalls.c2
-rw-r--r--security/min_addr.c5
-rw-r--r--security/safesetid/lsm.c3
-rw-r--r--security/safesetid/lsm.h2
-rw-r--r--security/safesetid/securityfs.c3
-rw-r--r--security/security.c623
-rw-r--r--security/selinux/Kconfig11
-rw-r--r--security/selinux/Makefile2
-rw-r--r--security/selinux/avc.c9
-rw-r--r--security/selinux/hooks.c289
-rw-r--r--security/selinux/ibpkey.c5
-rw-r--r--security/selinux/include/audit.h9
-rw-r--r--security/selinux/include/classmap.h2
-rw-r--r--security/selinux/include/hash.h47
-rw-r--r--security/selinux/include/initcalls.h19
-rw-r--r--security/selinux/include/objsec.h22
-rw-r--r--security/selinux/include/policycap.h1
-rw-r--r--security/selinux/include/policycap_names.h1
-rw-r--r--security/selinux/include/security.h5
-rw-r--r--security/selinux/initcalls.c52
-rw-r--r--security/selinux/netif.c5
-rw-r--r--security/selinux/netlink.c5
-rw-r--r--security/selinux/netnode.c5
-rw-r--r--security/selinux/netport.c5
-rw-r--r--security/selinux/selinuxfs.c20
-rw-r--r--security/selinux/ss/avtab.c39
-rw-r--r--security/selinux/ss/services.c26
-rw-r--r--security/smack/smack.h17
-rw-r--r--security/smack/smack_access.c96
-rw-r--r--security/smack/smack_lsm.c290
-rw-r--r--security/smack/smack_netfilter.c4
-rw-r--r--security/smack/smackfs.c4
-rw-r--r--security/tomoyo/common.h2
-rw-r--r--security/tomoyo/securityfs_if.c4
-rw-r--r--security/tomoyo/tomoyo.c3
-rw-r--r--security/yama/yama_lsm.c2
-rw-r--r--sound/ac97_bus.c13
-rw-r--r--sound/core/pcm_drm_eld.c2
-rw-r--r--sound/core/rawmidi.c5
-rw-r--r--sound/drivers/pcmtest.c4
-rw-r--r--sound/firewire/dice/Makefile2
-rw-r--r--sound/firewire/dice/dice-extension.c4
-rw-r--r--sound/firewire/dice/dice-teac.c43
-rw-r--r--sound/firewire/dice/dice.c13
-rw-r--r--sound/firewire/dice/dice.h1
-rw-r--r--sound/firewire/tascam/tascam-stream.c21
-rw-r--r--sound/hda/codecs/cirrus/cs420x.c1
-rw-r--r--sound/hda/codecs/hdmi/intelhdmi.c1
-rw-r--r--sound/hda/codecs/hdmi/nvhdmi-mcp.c4
-rw-r--r--sound/hda/codecs/realtek/alc269.c118
-rw-r--r--sound/hda/codecs/senarytech.c9
-rw-r--r--sound/hda/codecs/side-codecs/Kconfig15
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.c116
-rw-r--r--sound/hda/codecs/side-codecs/cs35l56_hda.h6
-rw-r--r--sound/hda/codecs/side-codecs/tas2781_hda_i2c.c44
-rw-r--r--sound/hda/controllers/intel.c2
-rw-r--r--sound/hda/core/intel-dsp-config.c8
-rw-r--r--sound/isa/gus/gus_reset.c3
-rw-r--r--sound/isa/wavefront/wavefront_midi.c2
-rw-r--r--sound/isa/wavefront/wavefront_synth.c4
-rw-r--r--sound/pci/au88x0/au88x0.c8
-rw-r--r--sound/pci/au88x0/au88x0_eq.c2
-rw-r--r--sound/pci/ctxfi/ctatc.c97
-rw-r--r--sound/pci/ctxfi/ctatc.h8
-rw-r--r--sound/pci/ctxfi/ctdaio.c18
-rw-r--r--sound/pci/ctxfi/ctdaio.h3
-rw-r--r--sound/pci/ctxfi/cthardware.h4
-rw-r--r--sound/pci/ctxfi/cthw20k1.c3
-rw-r--r--sound/pci/ctxfi/cthw20k2.c81
-rw-r--r--sound/pci/ctxfi/ctmixer.c73
-rw-r--r--sound/pci/maestro3.c6
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c40
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c23
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c12
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c10
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c157
-rw-r--r--sound/soc/amd/ps/acp63.h2
-rw-r--r--sound/soc/amd/ps/pci-ps.c9
-rw-r--r--sound/soc/amd/vangogh/acp5x-mach.c3
-rw-r--r--sound/soc/atmel/atmel-pdmic.c4
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c2
-rw-r--r--sound/soc/atmel/tse850-pcm5142.c32
-rw-r--r--sound/soc/codecs/88pm860x-codec.c11
-rw-r--r--sound/soc/codecs/Kconfig50
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/ab8500-codec.c18
-rw-r--r--sound/soc/codecs/ad1836.c2
-rw-r--r--sound/soc/codecs/ad193x.c4
-rw-r--r--sound/soc/codecs/adau1761.c9
-rw-r--r--sound/soc/codecs/adau1781.c2
-rw-r--r--sound/soc/codecs/adau17x1.c14
-rw-r--r--sound/soc/codecs/adau1977.c5
-rw-r--r--sound/soc/codecs/adau7118.c7
-rw-r--r--sound/soc/codecs/adav80x.c10
-rw-r--r--sound/soc/codecs/ak4458.c4
-rw-r--r--sound/soc/codecs/ak4619.c4
-rw-r--r--sound/soc/codecs/ak4641.c7
-rw-r--r--sound/soc/codecs/alc5623.c2
-rw-r--r--sound/soc/codecs/arizona-jack.c8
-rw-r--r--sound/soc/codecs/arizona.c22
-rw-r--r--sound/soc/codecs/audio-iio-aux.c2
-rw-r--r--sound/soc/codecs/aw87390.c14
-rw-r--r--sound/soc/codecs/aw88081.c24
-rw-r--r--sound/soc/codecs/aw88166.c159
-rw-r--r--sound/soc/codecs/aw88166.h5
-rw-r--r--sound/soc/codecs/aw88261.c34
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c30
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.c39
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h8
-rw-r--r--sound/soc/codecs/aw88399.c253
-rw-r--r--sound/soc/codecs/aw88399.h5
-rw-r--r--sound/soc/codecs/bd28623.c6
-rw-r--r--sound/soc/codecs/cpcap.c21
-rw-r--r--sound/soc/codecs/cros_ec_codec.c10
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c1523
-rw-r--r--sound/soc/codecs/cs-amp-lib.c372
-rw-r--r--sound/soc/codecs/cs35l33.c4
-rw-r--r--sound/soc/codecs/cs35l36.c6
-rw-r--r--sound/soc/codecs/cs35l41.c2
-rw-r--r--sound/soc/codecs/cs35l45.c12
-rw-r--r--sound/soc/codecs/cs35l56-shared.c439
-rw-r--r--sound/soc/codecs/cs35l56.c252
-rw-r--r--sound/soc/codecs/cs35l56.h6
-rw-r--r--sound/soc/codecs/cs4234.c7
-rw-r--r--sound/soc/codecs/cs4270.c2
-rw-r--r--sound/soc/codecs/cs4271.c38
-rw-r--r--sound/soc/codecs/cs42l42.c2
-rw-r--r--sound/soc/codecs/cs42l43-jack.c14
-rw-r--r--sound/soc/codecs/cs42l43.c38
-rw-r--r--sound/soc/codecs/cs42l51.c6
-rw-r--r--sound/soc/codecs/cs42l52.c5
-rw-r--r--sound/soc/codecs/cs42l56.c5
-rw-r--r--sound/soc/codecs/cs42l73.c3
-rw-r--r--sound/soc/codecs/cs42l84.c4
-rw-r--r--sound/soc/codecs/cs42xx8.c2
-rw-r--r--sound/soc/codecs/cs43130.c2
-rw-r--r--sound/soc/codecs/cs47l15.c11
-rw-r--r--sound/soc/codecs/cs47l24.c4
-rw-r--r--sound/soc/codecs/cs47l35.c5
-rw-r--r--sound/soc/codecs/cs47l85.c5
-rw-r--r--sound/soc/codecs/cs47l90.c5
-rw-r--r--sound/soc/codecs/cs47l92.c11
-rw-r--r--sound/soc/codecs/cs48l32.c40
-rw-r--r--sound/soc/codecs/cs530x-i2c.c24
-rw-r--r--sound/soc/codecs/cs530x-spi.c92
-rw-r--r--sound/soc/codecs/cs530x.c528
-rw-r--r--sound/soc/codecs/cs530x.h90
-rw-r--r--sound/soc/codecs/cs53l30.c9
-rw-r--r--sound/soc/codecs/cx20442.c5
-rw-r--r--sound/soc/codecs/cx2072x.c6
-rw-r--r--sound/soc/codecs/da7210.c4
-rw-r--r--sound/soc/codecs/da7213.c90
-rw-r--r--sound/soc/codecs/da7213.h1
-rw-r--r--sound/soc/codecs/da7218.c21
-rw-r--r--sound/soc/codecs/da7219-aad.c10
-rw-r--r--sound/soc/codecs/da7219.c29
-rw-r--r--sound/soc/codecs/da732x.c7
-rw-r--r--sound/soc/codecs/da9055.c6
-rw-r--r--sound/soc/codecs/es7134.c2
-rw-r--r--sound/soc/codecs/es8311.c3
-rw-r--r--sound/soc/codecs/es8316.c4
-rw-r--r--sound/soc/codecs/es8326.c16
-rw-r--r--sound/soc/codecs/es8328.c8
-rw-r--r--sound/soc/codecs/es8389.c4
-rw-r--r--sound/soc/codecs/fs210x.c2
-rw-r--r--sound/soc/codecs/hda.c2
-rw-r--r--sound/soc/codecs/hdac_hda.c4
-rw-r--r--sound/soc/codecs/hdac_hdmi.c35
-rw-r--r--sound/soc/codecs/hdmi-codec.c2
-rw-r--r--sound/soc/codecs/idt821034.c8
-rw-r--r--sound/soc/codecs/jz4740.c3
-rw-r--r--sound/soc/codecs/jz4760.c7
-rw-r--r--sound/soc/codecs/jz4770.c8
-rw-r--r--sound/soc/codecs/lm49453.c3
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c116
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c19
-rw-r--r--sound/soc/codecs/lpass-va-macro.c106
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c169
-rw-r--r--sound/soc/codecs/madera.c48
-rw-r--r--sound/soc/codecs/max9759.c8
-rw-r--r--sound/soc/codecs/max9768.c4
-rw-r--r--sound/soc/codecs/max98088.c18
-rw-r--r--sound/soc/codecs/max98090.c60
-rw-r--r--sound/soc/codecs/max98095.c27
-rw-r--r--sound/soc/codecs/max98373.c3
-rw-r--r--sound/soc/codecs/max98390.c22
-rw-r--r--sound/soc/codecs/max98396.c11
-rw-r--r--sound/soc/codecs/max9850.c3
-rw-r--r--sound/soc/codecs/max9867.c15
-rw-r--r--sound/soc/codecs/max98925.c2
-rw-r--r--sound/soc/codecs/ml26124.c3
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c6
-rw-r--r--sound/soc/codecs/mt6357.c2
-rw-r--r--sound/soc/codecs/mt6358.c25
-rw-r--r--sound/soc/codecs/mt6359.c18
-rw-r--r--sound/soc/codecs/mt6660.c3
-rw-r--r--sound/soc/codecs/nau8325.c5
-rw-r--r--sound/soc/codecs/nau8810.c7
-rw-r--r--sound/soc/codecs/nau8821.c28
-rw-r--r--sound/soc/codecs/nau8822.c20
-rw-r--r--sound/soc/codecs/nau8824.c10
-rw-r--r--sound/soc/codecs/nau8825.c6
-rw-r--r--sound/soc/codecs/ntp8835.c4
-rw-r--r--sound/soc/codecs/pcm1681.c4
-rw-r--r--sound/soc/codecs/pcm186x.c6
-rw-r--r--sound/soc/codecs/pcm512x.c25
-rw-r--r--sound/soc/codecs/pcm6240.c9
-rw-r--r--sound/soc/codecs/peb2466.c6
-rw-r--r--sound/soc/codecs/pm4125-sdw.c64
-rw-r--r--sound/soc/codecs/pm4125.c125
-rw-r--r--sound/soc/codecs/pm4125.h18
-rw-r--r--sound/soc/codecs/rk3308_codec.c3
-rw-r--r--sound/soc/codecs/rt1011.c36
-rw-r--r--sound/soc/codecs/rt1015.c17
-rw-r--r--sound/soc/codecs/rt1318.c4
-rw-r--r--sound/soc/codecs/rt274.c5
-rw-r--r--sound/soc/codecs/rt286.c8
-rw-r--r--sound/soc/codecs/rt298.c9
-rw-r--r--sound/soc/codecs/rt5514.c8
-rw-r--r--sound/soc/codecs/rt5616.c5
-rw-r--r--sound/soc/codecs/rt5631.c10
-rw-r--r--sound/soc/codecs/rt5640.c19
-rw-r--r--sound/soc/codecs/rt5645.c15
-rw-r--r--sound/soc/codecs/rt5651.c13
-rw-r--r--sound/soc/codecs/rt5659.c13
-rw-r--r--sound/soc/codecs/rt5660.c5
-rw-r--r--sound/soc/codecs/rt5663.c10
-rw-r--r--sound/soc/codecs/rt5665.c6
-rw-r--r--sound/soc/codecs/rt5668.c3
-rw-r--r--sound/soc/codecs/rt5670.c43
-rw-r--r--sound/soc/codecs/rt5677.c14
-rw-r--r--sound/soc/codecs/rt5682.c10
-rw-r--r--sound/soc/codecs/rt5682s.c5
-rw-r--r--sound/soc/codecs/rt700.c28
-rw-r--r--sound/soc/codecs/rt711-sdca.c13
-rw-r--r--sound/soc/codecs/rt711.c28
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c9
-rw-r--r--sound/soc/codecs/rt712-sdca.c20
-rw-r--r--sound/soc/codecs/rt715-sdca.c9
-rw-r--r--sound/soc/codecs/rt715.c35
-rw-r--r--sound/soc/codecs/rt721-sdca.c13
-rw-r--r--sound/soc/codecs/rt721-sdca.h1
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c4
-rw-r--r--sound/soc/codecs/rt9123.c6
-rw-r--r--sound/soc/codecs/sgtl5000.c8
-rw-r--r--sound/soc/codecs/simple-mux.c7
-rw-r--r--sound/soc/codecs/sma1303.c31
-rw-r--r--sound/soc/codecs/sma1307.c95
-rw-r--r--sound/soc/codecs/ssm2518.c3
-rw-r--r--sound/soc/codecs/ssm2602.c4
-rw-r--r--sound/soc/codecs/ssm4567.c3
-rw-r--r--sound/soc/codecs/sta32x.c10
-rw-r--r--sound/soc/codecs/sta350.c10
-rw-r--r--sound/soc/codecs/sta529.c3
-rw-r--r--sound/soc/codecs/tas2562.c4
-rw-r--r--sound/soc/codecs/tas2781-comlib-i2c.c2
-rw-r--r--sound/soc/codecs/tas2781-i2c.c115
-rw-r--r--sound/soc/codecs/tas2783-sdw.c24
-rw-r--r--sound/soc/codecs/tas5086.c4
-rw-r--r--sound/soc/codecs/tas571x.c7
-rw-r--r--sound/soc/codecs/tas5720.c4
-rw-r--r--sound/soc/codecs/tas5805m.c6
-rw-r--r--sound/soc/codecs/tas6424.c4
-rw-r--r--sound/soc/codecs/tfa989x.c2
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c8
-rw-r--r--sound/soc/codecs/tlv320adcx140.c6
-rw-r--r--sound/soc/codecs/tlv320aic23.c4
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c14
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c3
-rw-r--r--sound/soc/codecs/tlv320aic3x.c11
-rw-r--r--sound/soc/codecs/tlv320dac33.c9
-rw-r--r--sound/soc/codecs/tscs42xx.c6
-rw-r--r--sound/soc/codecs/tscs454.c6
-rw-r--r--sound/soc/codecs/twl4030.c14
-rw-r--r--sound/soc/codecs/twl6040.c15
-rw-r--r--sound/soc/codecs/uda1334.c4
-rw-r--r--sound/soc/codecs/uda1380.c3
-rw-r--r--sound/soc/codecs/wcd9335.c34
-rw-r--r--sound/soc/codecs/wcd934x.c44
-rw-r--r--sound/soc/codecs/wcd937x.c63
-rw-r--r--sound/soc/codecs/wcd938x.c27
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c8
-rw-r--r--sound/soc/codecs/wcd939x.c24
-rw-r--r--sound/soc/codecs/wm0010.c5
-rw-r--r--sound/soc/codecs/wm2000.c8
-rw-r--r--sound/soc/codecs/wm5100.c4
-rw-r--r--sound/soc/codecs/wm5102.c12
-rw-r--r--sound/soc/codecs/wm5110.c16
-rw-r--r--sound/soc/codecs/wm8350.c7
-rw-r--r--sound/soc/codecs/wm8400.c14
-rw-r--r--sound/soc/codecs/wm8510.c3
-rw-r--r--sound/soc/codecs/wm8523.c3
-rw-r--r--sound/soc/codecs/wm8580.c8
-rw-r--r--sound/soc/codecs/wm8711.c3
-rw-r--r--sound/soc/codecs/wm8728.c3
-rw-r--r--sound/soc/codecs/wm8731.c9
-rw-r--r--sound/soc/codecs/wm8737.c6
-rw-r--r--sound/soc/codecs/wm8750.c3
-rw-r--r--sound/soc/codecs/wm8753.c7
-rw-r--r--sound/soc/codecs/wm8770.c3
-rw-r--r--sound/soc/codecs/wm8776.c3
-rw-r--r--sound/soc/codecs/wm8804.c4
-rw-r--r--sound/soc/codecs/wm8900.c12
-rw-r--r--sound/soc/codecs/wm8903.c10
-rw-r--r--sound/soc/codecs/wm8904.c23
-rw-r--r--sound/soc/codecs/wm8940.c6
-rw-r--r--sound/soc/codecs/wm8955.c10
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c32
-rw-r--r--sound/soc/codecs/wm8960.c16
-rw-r--r--sound/soc/codecs/wm8961.c5
-rw-r--r--sound/soc/codecs/wm8962.c30
-rw-r--r--sound/soc/codecs/wm8971.c3
-rw-r--r--sound/soc/codecs/wm8974.c3
-rw-r--r--sound/soc/codecs/wm8978.c9
-rw-r--r--sound/soc/codecs/wm8983.c7
-rw-r--r--sound/soc/codecs/wm8985.c9
-rw-r--r--sound/soc/codecs/wm8988.c3
-rw-r--r--sound/soc/codecs/wm8990.c9
-rw-r--r--sound/soc/codecs/wm8991.c5
-rw-r--r--sound/soc/codecs/wm8993.c13
-rw-r--r--sound/soc/codecs/wm8994.c46
-rw-r--r--sound/soc/codecs/wm8995.c7
-rw-r--r--sound/soc/codecs/wm8996.c13
-rw-r--r--sound/soc/codecs/wm8997.c4
-rw-r--r--sound/soc/codecs/wm8998.c8
-rw-r--r--sound/soc/codecs/wm9081.c7
-rw-r--r--sound/soc/codecs/wm9090.c5
-rw-r--r--sound/soc/codecs/wm9712.c7
-rw-r--r--sound/soc/codecs/wm9713.c7
-rw-r--r--sound/soc/codecs/wm_adsp.c21
-rw-r--r--sound/soc/codecs/wm_hubs.c10
-rw-r--r--sound/soc/codecs/wsa881x.c9
-rw-r--r--sound/soc/codecs/wsa883x.c19
-rw-r--r--sound/soc/codecs/wsa884x.c8
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c4
-rw-r--r--sound/soc/fsl/fsl_aud2htx.h3
-rw-r--r--sound/soc/fsl/fsl_micfil.c140
-rw-r--r--sound/soc/fsl/fsl_sai.c11
-rw-r--r--sound/soc/fsl/fsl_spdif.c16
-rw-r--r--sound/soc/fsl/fsl_xcvr.c88
-rw-r--r--sound/soc/fsl/imx-rpmsg.c2
-rw-r--r--sound/soc/generic/audio-graph-card.c4
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c15
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c3
-rw-r--r--sound/soc/intel/avs/boards/da7219.c3
-rw-r--r--sound/soc/intel/avs/boards/es8336.c5
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c3
-rw-r--r--sound/soc/intel/avs/boards/rt274.c6
-rw-r--r--sound/soc/intel/avs/boards/rt5514.c3
-rw-r--r--sound/soc/intel/avs/boards/rt5640.c3
-rw-r--r--sound/soc/intel/avs/control.c7
-rw-r--r--sound/soc/intel/avs/debugfs.c9
-rw-r--r--sound/soc/intel/avs/path.c13
-rw-r--r--sound/soc/intel/avs/pcm.c5
-rw-r--r--sound/soc/intel/avs/probes.c18
-rw-r--r--sound/soc/intel/avs/topology.c113
-rw-r--r--sound/soc/intel/avs/topology.h8
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c9
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c3
-rw-r--r--sound/soc/intel/boards/bytcht_es8316.c9
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c25
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c18
-rw-r--r--sound/soc/intel/boards/bytcr_wm5102.c14
-rw-r--r--sound/soc/intel/boards/cht_bsw_max98090_ti.c5
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c12
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c8
-rw-r--r--sound/soc/intel/boards/hda_dsp_common.c15
-rw-r--r--sound/soc/intel/boards/sof_board_helpers.c10
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.c5
-rw-r--r--sound/soc/intel/boards/sof_da7219.c5
-rw-r--r--sound/soc/intel/boards/sof_es8336.c14
-rw-r--r--sound/soc/intel/boards/sof_maxim_common.c22
-rw-r--r--sound/soc/intel/boards/sof_nau8825.c2
-rw-r--r--sound/soc/intel/boards/sof_nuvoton_common.c5
-rw-r--r--sound/soc/intel/boards/sof_pcm512x.c5
-rw-r--r--sound/soc/intel/boards/sof_realtek_common.c34
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c17
-rw-r--r--sound/soc/intel/boards/sof_sdw.c80
-rw-r--r--sound/soc/intel/catpt/device.c26
-rw-r--r--sound/soc/intel/catpt/loader.c18
-rw-r--r--sound/soc/intel/catpt/pcm.c31
-rw-r--r--sound/soc/intel/catpt/sysfs.c2
-rw-r--r--sound/soc/intel/common/Makefile1
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-nvl-match.c90
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c131
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.c8
-rw-r--r--sound/soc/mediatek/Kconfig31
-rw-r--r--sound/soc/mediatek/Makefile1
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c7
-rw-r--r--sound/soc/mediatek/common/mtk-btcvsd.c24
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.c5
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-adda.c4
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-i2s.c4
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c12
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-adda.c4
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-i2s.c4
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-tdm.c4
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-misc-control.c12
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-common.c2
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366.c19
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c10
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c4
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-dmic.c2
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-etdm.c3
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c17
-rw-r--r--sound/soc/mediatek/mt8189/Makefile18
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-clk.c750
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-clk.h76
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-common.h240
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-pcm.c2615
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-adda.c1228
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-i2s.c1463
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-pcm.c332
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-tdm.c672
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-interconnection.h97
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-nau8825.c1178
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-reg.h10773
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-adda.c12
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-i2s.c4
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c7
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-adda.c8
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-etdm.c3
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c21
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-afe-pcm.c4
-rw-r--r--sound/soc/meson/aiu-acodec-ctrl.c6
-rw-r--r--sound/soc/meson/aiu-codec-ctrl.c6
-rw-r--r--sound/soc/meson/axg-spdifout.c4
-rw-r--r--sound/soc/meson/axg-tdm-interface.c4
-rw-r--r--sound/soc/meson/g12a-toacodec.c6
-rw-r--r--sound/soc/meson/g12a-tohdmitx.c12
-rw-r--r--sound/soc/meson/t9015.c4
-rw-r--r--sound/soc/mxs/mxs-saif.c123
-rw-r--r--sound/soc/pxa/spitz.c9
-rw-r--r--sound/soc/qcom/qdsp6/audioreach.c234
-rw-r--r--sound/soc/qcom/qdsp6/q6adm.c179
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c49
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c25
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c57
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c87
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.c205
-rw-r--r--sound/soc/qcom/qdsp6/q6asm.h1
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c27
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.c6
-rw-r--r--sound/soc/qcom/qdsp6/q6usb.c3
-rw-r--r--sound/soc/qcom/qdsp6/topology.c12
-rw-r--r--sound/soc/qcom/sc7180.c10
-rw-r--r--sound/soc/qcom/sc7280.c67
-rw-r--r--sound/soc/qcom/sc8280xp.c34
-rw-r--r--sound/soc/qcom/sdm845.c53
-rw-r--r--sound/soc/qcom/sdw.c128
-rw-r--r--sound/soc/qcom/sdw.h7
-rw-r--r--sound/soc/qcom/sm8250.c34
-rw-r--r--sound/soc/qcom/x1e80100.c33
-rw-r--r--sound/soc/renesas/fsi.c8
-rw-r--r--sound/soc/renesas/rcar/ssiu.c3
-rw-r--r--sound/soc/renesas/rz-ssi.c89
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c3
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c3
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c2
-rw-r--r--sound/soc/rockchip/rockchip_sai.c8
-rw-r--r--sound/soc/samsung/aries_wm8994.c6
-rw-r--r--sound/soc/samsung/bells.c6
-rw-r--r--sound/soc/samsung/littlemill.c8
-rw-r--r--sound/soc/samsung/lowland.c3
-rw-r--r--sound/soc/samsung/midas_wm1811.c8
-rw-r--r--sound/soc/samsung/smdk_wm8994.c30
-rw-r--r--sound/soc/samsung/speyside.c24
-rw-r--r--sound/soc/samsung/tm2_wm5110.c7
-rw-r--r--sound/soc/samsung/tobermory.c6
-rw-r--r--sound/soc/sdca/Kconfig28
-rw-r--r--sound/soc/sdca/Makefile10
-rw-r--r--sound/soc/sdca/sdca_asoc.c89
-rw-r--r--sound/soc/sdca/sdca_class.c304
-rw-r--r--sound/soc/sdca/sdca_class.h37
-rw-r--r--sound/soc/sdca/sdca_class_function.c460
-rw-r--r--sound/soc/sdca/sdca_device.c20
-rw-r--r--sound/soc/sdca/sdca_fdl.c504
-rw-r--r--sound/soc/sdca/sdca_function_device.c117
-rw-r--r--sound/soc/sdca/sdca_function_device.h15
-rw-r--r--sound/soc/sdca/sdca_functions.c313
-rw-r--r--sound/soc/sdca/sdca_hid.c59
-rw-r--r--sound/soc/sdca/sdca_interrupts.c280
-rw-r--r--sound/soc/sdca/sdca_regmap.c98
-rw-r--r--sound/soc/sdca/sdca_ump.c262
-rw-r--r--sound/soc/sdw_utils/Makefile1
-rw-r--r--sound/soc/sdw_utils/soc_sdw_bridge_cs35l56.c5
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l42.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l43.c9
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs42l45.c80
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs_amp.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_dmic.c5
-rw-r--r--sound/soc/sdw_utils/soc_sdw_maxim.c8
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt5682.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt700.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt711.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp.c5
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c11
-rw-r--r--sound/soc/sdw_utils/soc_sdw_ti_amp.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c179
-rw-r--r--sound/soc/soc-core.c42
-rw-r--r--sound/soc/soc-dai.c5
-rw-r--r--sound/soc/soc-dapm.c196
-rw-r--r--sound/soc/soc-jack.c2
-rw-r--r--sound/soc/soc-ops.c62
-rw-r--r--sound/soc/soc-pcm.c38
-rw-r--r--sound/soc/soc-topology.c10
-rw-r--r--sound/soc/sof/fw-file-profile.c13
-rw-r--r--sound/soc/sof/imx/Kconfig1
-rw-r--r--sound/soc/sof/imx/imx9.c36
-rw-r--r--sound/soc/sof/intel/Kconfig18
-rw-r--r--sound/soc/sof/intel/Makefile2
-rw-r--r--sound/soc/sof/intel/apl.c1
-rw-r--r--sound/soc/sof/intel/cnl.c4
-rw-r--r--sound/soc/sof/intel/hda-dsp.c1
-rw-r--r--sound/soc/sof/intel/hda-ipc.c2
-rw-r--r--sound/soc/sof/intel/hda.c213
-rw-r--r--sound/soc/sof/intel/hda.h1
-rw-r--r--sound/soc/sof/intel/icl.c1
-rw-r--r--sound/soc/sof/intel/lnl.c1
-rw-r--r--sound/soc/sof/intel/mtl.c2
-rw-r--r--sound/soc/sof/intel/nvl.c55
-rw-r--r--sound/soc/sof/intel/nvl.h14
-rw-r--r--sound/soc/sof/intel/pci-nvl.c82
-rw-r--r--sound/soc/sof/intel/ptl.c2
-rw-r--r--sound/soc/sof/intel/shim.h2
-rw-r--r--sound/soc/sof/intel/skl.c1
-rw-r--r--sound/soc/sof/intel/tgl.c4
-rw-r--r--sound/soc/sof/pcm.c18
-rw-r--r--sound/soc/sof/sof-client-probes-ipc4.c2
-rw-r--r--sound/soc/sof/sof-client-probes.c11
-rw-r--r--sound/soc/sof/topology.c7
-rw-r--r--sound/soc/spacemit/Kconfig15
-rw-r--r--sound/soc/spacemit/Makefile5
-rw-r--r--sound/soc/spacemit/k1_i2s.c461
-rw-r--r--sound/soc/stm/stm32_adfsdm.c2
-rw-r--r--sound/soc/stm/stm32_sai.c14
-rw-r--r--sound/soc/stm/stm32_sai_sub.c57
-rw-r--r--sound/soc/sunxi/sun4i-codec.c3
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c26
-rw-r--r--sound/soc/sunxi/sun50i-codec-analog.c2
-rw-r--r--sound/soc/sunxi/sun8i-codec-analog.c14
-rw-r--r--sound/soc/sunxi/sun8i-codec.c4
-rw-r--r--sound/soc/tegra/Kconfig2
-rw-r--r--sound/soc/tegra/tegra186_asrc.c24
-rw-r--r--sound/soc/tegra/tegra186_dspk.c24
-rw-r--r--sound/soc/tegra/tegra210_admaif.c16
-rw-r--r--sound/soc/tegra/tegra210_adx.c4
-rw-r--r--sound/soc/tegra/tegra210_ahub.c6
-rw-r--r--sound/soc/tegra/tegra210_amx.c4
-rw-r--r--sound/soc/tegra/tegra210_dmic.c24
-rw-r--r--sound/soc/tegra/tegra210_i2s.c32
-rw-r--r--sound/soc/tegra/tegra210_mbdrc.c24
-rw-r--r--sound/soc/tegra/tegra210_mixer.c4
-rw-r--r--sound/soc/tegra/tegra210_mvc.c12
-rw-r--r--sound/soc/tegra/tegra210_ope.c4
-rw-r--r--sound/soc/tegra/tegra210_peq.c8
-rw-r--r--sound/soc/tegra/tegra210_sfc.c16
-rw-r--r--sound/soc/tegra/tegra_asoc_machine.c11
-rw-r--r--sound/soc/tegra/tegra_wm8903.c3
-rw-r--r--sound/soc/ti/ams-delta.c8
-rw-r--r--sound/soc/ti/davinci-evm.c11
-rw-r--r--sound/soc/ti/j721e-evm.c2
-rw-r--r--sound/soc/ti/n810.c12
-rw-r--r--sound/soc/ti/omap-abe-twl6040.c2
-rw-r--r--sound/soc/ti/omap-twl4030.c2
-rw-r--r--sound/soc/ti/omap3pandora.c36
-rw-r--r--sound/soc/ti/rx51.c17
-rw-r--r--sound/soc/uniphier/aio-cpu.c4
-rw-r--r--sound/soc/uniphier/evea.c12
-rw-r--r--sound/soc/ux500/mop500_ab8500.c2
-rw-r--r--sound/usb/endpoint.c5
-rw-r--r--sound/usb/line6/podhd.c16
-rw-r--r--sound/usb/mixer.c4
-rw-r--r--sound/usb/mixer_quirks.c122
-rw-r--r--sound/usb/mixer_s1810c.c35
-rw-r--r--sound/usb/quirks.c11
-rw-r--r--sound/usb/stream.c6
-rw-r--r--tools/arch/arm64/include/asm/cputype.h4
-rw-r--r--tools/arch/arm64/include/asm/esr.h4
-rw-r--r--tools/arch/arm64/include/asm/gpr-num.h6
-rw-r--r--tools/arch/arm64/include/asm/sysreg.h12
-rw-r--r--tools/arch/arm64/include/uapi/asm/kvm.h2
-rw-r--r--tools/arch/riscv/include/asm/csr.h5
-rw-r--r--tools/arch/s390/include/uapi/asm/bitsperlong.h4
-rw-r--r--tools/arch/x86/include/asm/cpufeatures.h12
-rw-r--r--tools/arch/x86/include/asm/insn.h5
-rw-r--r--tools/arch/x86/include/asm/msr-index.h20
-rw-r--r--tools/arch/x86/include/uapi/asm/kvm.h34
-rw-r--r--tools/arch/x86/include/uapi/asm/svm.h4
-rw-r--r--tools/arch/x86/include/uapi/asm/vmx.h7
-rw-r--r--tools/arch/x86/tools/gen-cpu-feature-names-x86.awk34
-rw-r--r--tools/bpf/Makefile13
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-map.rst3
-rw-r--r--tools/bpf/bpftool/Documentation/bpftool-prog.rst2
-rw-r--r--tools/bpf/bpftool/btf_dumper.c2
-rw-r--r--tools/bpf/bpftool/map.c3
-rw-r--r--tools/bpf/bpftool/sign.c6
-rw-r--r--tools/bpf/runqslower/Makefile91
-rw-r--r--tools/bpf/runqslower/runqslower.bpf.c106
-rw-r--r--tools/bpf/runqslower/runqslower.c171
-rw-r--r--tools/bpf/runqslower/runqslower.h13
-rw-r--r--tools/build/Build2
-rw-r--r--tools/build/Makefile21
-rw-r--r--tools/build/Makefile.feature4
-rw-r--r--tools/build/feature/Makefile4
-rwxr-xr-xtools/docs/check-variable-fonts.py37
-rwxr-xr-xtools/docs/checktransupdate.py (renamed from scripts/checktransupdate.py)8
-rwxr-xr-xtools/docs/documentation-file-ref-check (renamed from scripts/documentation-file-ref-check)2
-rwxr-xr-xtools/docs/features-refresh.sh (renamed from Documentation/features/scripts/features-refresh.sh)0
-rwxr-xr-xtools/docs/find-unused-docs.sh (renamed from scripts/find-unused-docs.sh)6
-rwxr-xr-xtools/docs/get_abi.py (renamed from scripts/get_abi.py)10
-rwxr-xr-xtools/docs/get_feat.py225
-rwxr-xr-xtools/docs/list-arch.sh (renamed from Documentation/features/list-arch.sh)2
-rwxr-xr-xtools/docs/parse-headers.py14
-rwxr-xr-xtools/docs/sphinx-build-wrapper864
-rwxr-xr-xtools/docs/sphinx-pre-install (renamed from scripts/sphinx-pre-install)140
-rwxr-xr-xtools/docs/test_doc_build.py (renamed from scripts/test_doc_build.py)0
-rw-r--r--tools/include/asm-generic/bitops/__fls.h2
-rw-r--r--tools/include/asm-generic/bitops/fls.h2
-rw-r--r--tools/include/asm-generic/bitops/fls64.h4
-rw-r--r--tools/include/linux/interval_tree_generic.h10
-rw-r--r--tools/include/linux/livepatch_external.h76
-rw-r--r--tools/include/linux/objtool_types.h2
-rw-r--r--tools/include/linux/string.h14
-rw-r--r--tools/include/nolibc/Makefile22
-rw-r--r--tools/include/nolibc/arch-arm.h2
-rw-r--r--tools/include/nolibc/arch-arm64.h2
-rw-r--r--tools/include/nolibc/arch-loongarch.h2
-rw-r--r--tools/include/nolibc/arch-m68k.h2
-rw-r--r--tools/include/nolibc/arch-mips.h2
-rw-r--r--tools/include/nolibc/arch-powerpc.h2
-rw-r--r--tools/include/nolibc/arch-riscv.h2
-rw-r--r--tools/include/nolibc/arch-s390.h7
-rw-r--r--tools/include/nolibc/arch-sh.h2
-rw-r--r--tools/include/nolibc/arch-sparc.h2
-rw-r--r--tools/include/nolibc/arch-x86.h10
-rw-r--r--tools/include/nolibc/arch.h11
-rw-r--r--tools/include/nolibc/compiler.h4
-rw-r--r--tools/include/nolibc/crt.h3
-rw-r--r--tools/include/nolibc/dirent.h6
-rw-r--r--tools/include/nolibc/getopt.h2
-rw-r--r--tools/include/nolibc/inttypes.h3
-rw-r--r--tools/include/nolibc/nolibc.h2
-rw-r--r--tools/include/nolibc/stackprotector.h2
-rw-r--r--tools/include/nolibc/std.h4
-rw-r--r--tools/include/nolibc/stdio.h10
-rw-r--r--tools/include/nolibc/stdlib.h2
-rw-r--r--tools/include/nolibc/string.h15
-rw-r--r--tools/include/nolibc/sys.h74
-rw-r--r--tools/include/nolibc/sys/auxv.h3
-rw-r--r--tools/include/nolibc/sys/mman.h5
-rw-r--r--tools/include/nolibc/sys/reboot.h2
-rw-r--r--tools/include/nolibc/sys/select.h103
-rw-r--r--tools/include/nolibc/sys/uio.h49
-rw-r--r--tools/include/nolibc/sys/wait.h18
-rw-r--r--tools/include/nolibc/time.h16
-rw-r--r--tools/include/nolibc/types.h47
-rw-r--r--tools/include/nolibc/unistd.h6
-rw-r--r--tools/include/uapi/drm/drm.h63
-rw-r--r--tools/include/uapi/linux/bpf.h34
-rw-r--r--tools/include/uapi/linux/kvm.h3
-rw-r--r--tools/include/uapi/linux/netdev.h2
-rw-r--r--tools/include/uapi/linux/nsfs.h70
-rw-r--r--tools/include/uapi/linux/perf_event.h21
-rw-r--r--tools/lib/bpf/bpf.c2
-rw-r--r--tools/lib/bpf/bpf_helpers.h28
-rw-r--r--tools/lib/bpf/bpf_tracing.h2
-rw-r--r--tools/lib/bpf/btf.c75
-rw-r--r--tools/lib/bpf/btf.h8
-rw-r--r--tools/lib/bpf/libbpf.c300
-rw-r--r--tools/lib/bpf/libbpf.h27
-rw-r--r--tools/lib/bpf/libbpf_internal.h2
-rw-r--r--tools/lib/bpf/libbpf_probes.c4
-rw-r--r--tools/lib/bpf/linker.c3
-rw-r--r--tools/lib/bpf/usdt.c2
-rw-r--r--tools/lib/python/__init__.py (renamed from tools/docs/lib/__init__.py)0
-rw-r--r--tools/lib/python/abi/__init__.py0
-rw-r--r--tools/lib/python/abi/abi_parser.py (renamed from scripts/lib/abi/abi_parser.py)2
-rw-r--r--tools/lib/python/abi/abi_regex.py (renamed from scripts/lib/abi/abi_regex.py)4
-rw-r--r--tools/lib/python/abi/helpers.py (renamed from scripts/lib/abi/helpers.py)0
-rw-r--r--tools/lib/python/abi/system_symbols.py (renamed from scripts/lib/abi/system_symbols.py)2
-rwxr-xr-xtools/lib/python/feat/parse_features.py494
-rwxr-xr-xtools/lib/python/jobserver.py149
-rw-r--r--tools/lib/python/kdoc/__init__.py0
-rw-r--r--tools/lib/python/kdoc/enrich_formatter.py (renamed from tools/docs/lib/enrich_formatter.py)0
-rw-r--r--tools/lib/python/kdoc/kdoc_files.py (renamed from scripts/lib/kdoc/kdoc_files.py)15
-rw-r--r--tools/lib/python/kdoc/kdoc_item.py (renamed from scripts/lib/kdoc/kdoc_item.py)3
-rw-r--r--tools/lib/python/kdoc/kdoc_output.py (renamed from scripts/lib/kdoc/kdoc_output.py)97
-rw-r--r--tools/lib/python/kdoc/kdoc_parser.py (renamed from scripts/lib/kdoc/kdoc_parser.py)61
-rw-r--r--tools/lib/python/kdoc/kdoc_re.py (renamed from scripts/lib/kdoc/kdoc_re.py)24
-rwxr-xr-xtools/lib/python/kdoc/latex_fonts.py167
-rwxr-xr-xtools/lib/python/kdoc/parse_data_structs.py (renamed from tools/docs/lib/parse_data_structs.py)230
-rw-r--r--tools/lib/python/kdoc/python_version.py178
-rw-r--r--tools/net/ynl/Makefile29
-rw-r--r--tools/net/ynl/lib/ynl-priv.h4
-rwxr-xr-xtools/net/ynl/pyynl/cli.py100
-rwxr-xr-xtools/net/ynl/pyynl/ethtool.py3
-rw-r--r--tools/net/ynl/pyynl/lib/ynl.py48
-rwxr-xr-xtools/net/ynl/pyynl/ynl_gen_c.py38
-rw-r--r--tools/net/ynl/samples/.gitignore1
-rw-r--r--tools/net/ynl/samples/Makefile1
-rw-r--r--tools/net/ynl/samples/page-pool.c149
-rw-r--r--tools/net/ynl/samples/tc-filter-add.c335
-rw-r--r--tools/net/ynl/tests/Makefile32
-rw-r--r--tools/net/ynl/tests/config6
-rwxr-xr-xtools/net/ynl/tests/test_ynl_cli.sh327
-rwxr-xr-xtools/net/ynl/tests/test_ynl_ethtool.sh222
-rw-r--r--tools/net/ynl/ynltool/.gitignore2
-rw-r--r--tools/net/ynl/ynltool/Makefile55
-rw-r--r--tools/net/ynl/ynltool/json_writer.c288
-rw-r--r--tools/net/ynl/ynltool/json_writer.h75
-rw-r--r--tools/net/ynl/ynltool/main.c242
-rw-r--r--tools/net/ynl/ynltool/main.h66
-rw-r--r--tools/net/ynl/ynltool/page-pool.c461
-rw-r--r--tools/net/ynl/ynltool/qstats.c621
-rw-r--r--tools/objtool/.gitignore3
-rw-r--r--tools/objtool/Build7
-rw-r--r--tools/objtool/Makefile70
-rw-r--r--tools/objtool/arch/loongarch/decode.c29
-rw-r--r--tools/objtool/arch/loongarch/orc.c1
-rw-r--r--tools/objtool/arch/loongarch/special.c5
-rw-r--r--tools/objtool/arch/powerpc/decode.c31
-rw-r--r--tools/objtool/arch/powerpc/special.c5
-rw-r--r--tools/objtool/arch/x86/Build13
-rw-r--r--tools/objtool/arch/x86/decode.c111
-rw-r--r--tools/objtool/arch/x86/orc.c1
-rw-r--r--tools/objtool/arch/x86/special.c12
-rw-r--r--tools/objtool/builtin-check.c102
-rw-r--r--tools/objtool/builtin-klp.c53
-rw-r--r--tools/objtool/check.c1490
-rw-r--r--tools/objtool/disas.c1248
-rw-r--r--tools/objtool/elf.c822
-rw-r--r--tools/objtool/include/objtool/arch.h16
-rw-r--r--tools/objtool/include/objtool/builtin.h14
-rw-r--r--tools/objtool/include/objtool/check.h39
-rw-r--r--tools/objtool/include/objtool/checksum.h43
-rw-r--r--tools/objtool/include/objtool/checksum_types.h25
-rw-r--r--tools/objtool/include/objtool/disas.h81
-rw-r--r--tools/objtool/include/objtool/elf.h198
-rw-r--r--tools/objtool/include/objtool/endianness.h9
-rw-r--r--tools/objtool/include/objtool/klp.h35
-rw-r--r--tools/objtool/include/objtool/objtool.h4
-rw-r--r--tools/objtool/include/objtool/special.h4
-rw-r--r--tools/objtool/include/objtool/trace.h141
-rw-r--r--tools/objtool/include/objtool/util.h19
-rw-r--r--tools/objtool/include/objtool/warn.h66
-rw-r--r--tools/objtool/klp-diff.c1723
-rw-r--r--tools/objtool/klp-post-link.c168
-rw-r--r--tools/objtool/noreturns.h1
-rw-r--r--tools/objtool/objtool.c42
-rw-r--r--tools/objtool/orc_dump.c1
-rw-r--r--tools/objtool/orc_gen.c9
-rw-r--r--tools/objtool/special.c16
-rwxr-xr-xtools/objtool/sync-check.sh2
-rw-r--r--tools/objtool/trace.c203
-rw-r--r--tools/objtool/weak.c7
-rw-r--r--tools/perf/Makefile.config5
-rw-r--r--tools/perf/Makefile.perf6
-rw-r--r--tools/perf/arch/x86/entry/syscalls/syscall_64.tbl1
-rw-r--r--tools/perf/builtin-lock.c2
-rwxr-xr-xtools/perf/tests/shell/lock_contention.sh14
-rw-r--r--tools/perf/trace/beauty/include/linux/socket.h5
-rw-r--r--tools/perf/trace/beauty/include/uapi/linux/fcntl.h1
-rw-r--r--tools/perf/trace/beauty/include/uapi/linux/fs.h5
-rw-r--r--tools/perf/trace/beauty/include/uapi/linux/prctl.h10
-rw-r--r--tools/perf/util/header.c10
-rw-r--r--tools/perf/util/libbfd.c38
-rw-r--r--tools/perf/util/mutex.c14
-rw-r--r--tools/perf/util/mutex.h2
-rw-r--r--tools/perf/util/symbol.c6
-rw-r--r--tools/power/acpi/tools/pfrut/pfrut.c7
-rw-r--r--tools/power/cpupower/Makefile32
-rw-r--r--tools/sched_ext/Makefile4
-rw-r--r--tools/sched_ext/include/scx/common.bpf.h15
-rw-r--r--tools/sched_ext/include/scx/compat.bpf.h314
-rw-r--r--tools/sched_ext/include/scx/compat.h14
-rw-r--r--tools/sched_ext/scx_cpu0.bpf.c88
-rw-r--r--tools/sched_ext/scx_cpu0.c106
-rw-r--r--tools/sched_ext/scx_flatcg.bpf.c10
-rw-r--r--tools/sched_ext/scx_qmap.bpf.c52
-rw-r--r--tools/testing/cxl/Kbuild3
-rw-r--r--tools/testing/cxl/test/Kbuild1
-rw-r--r--tools/testing/cxl/test/cxl.c86
-rw-r--r--tools/testing/cxl/test/cxl_translate.c445
-rw-r--r--tools/testing/cxl/test/mem.c11
-rw-r--r--tools/testing/cxl/test/mock.c52
-rw-r--r--tools/testing/cxl/test/mock.h4
-rw-r--r--tools/testing/selftests/alsa/conf.c2
-rw-r--r--tools/testing/selftests/arm64/fp/fp-ptrace.c5
-rw-r--r--tools/testing/selftests/arm64/fp/sve-ptrace.c61
-rw-r--r--tools/testing/selftests/arm64/fp/zt-test.S2
-rw-r--r--tools/testing/selftests/bpf/.gitignore2
-rw-r--r--tools/testing/selftests/bpf/Makefile44
-rw-r--r--tools/testing/selftests/bpf/benchs/bench_ringbufs.c65
-rw-r--r--tools/testing/selftests/bpf/benchs/bench_trigger.c4
-rwxr-xr-xtools/testing/selftests/bpf/benchs/run_bench_ringbufs.sh4
-rw-r--r--tools/testing/selftests/bpf/bpf_arena_list.h6
-rw-r--r--tools/testing/selftests/bpf/bpf_arena_strsearch.h128
-rw-r--r--tools/testing/selftests/bpf/bpf_kfuncs.h12
-rw-r--r--tools/testing/selftests/bpf/config8
-rw-r--r--tools/testing/selftests/bpf/network_helpers.c52
-rw-r--r--tools/testing/selftests/bpf/network_helpers.h16
-rw-r--r--tools/testing/selftests/bpf/prog_tests/arena_strsearch.c30
-rw-r--r--tools/testing/selftests/bpf/prog_tests/bpf_gotox.c292
-rw-r--r--tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c504
-rw-r--r--tools/testing/selftests/bpf/prog_tests/btf.c65
-rw-r--r--tools/testing/selftests/bpf/prog_tests/btf_split.c87
-rw-r--r--tools/testing/selftests/bpf/prog_tests/check_mtu.c23
-rw-r--r--tools/testing/selftests/bpf/prog_tests/cls_redirect.c122
-rw-r--r--tools/testing/selftests/bpf/prog_tests/file_reader.c117
-rw-r--r--tools/testing/selftests/bpf/prog_tests/htab_update.c37
-rw-r--r--tools/testing/selftests/bpf/prog_tests/kmem_cache_iter.c3
-rw-r--r--tools/testing/selftests/bpf/prog_tests/livepatch_trampoline.c107
-rw-r--r--tools/testing/selftests/bpf/prog_tests/mptcp.c140
-rw-r--r--tools/testing/selftests/bpf/prog_tests/perf_branches.c22
-rw-r--r--tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c56
-rw-r--r--tools/testing/selftests/bpf/prog_tests/res_spin_lock.c8
-rw-r--r--tools/testing/selftests/bpf/prog_tests/ringbuf.c65
-rw-r--r--tools/testing/selftests/bpf/prog_tests/select_reuseport.c67
-rw-r--r--tools/testing/selftests/bpf/prog_tests/send_signal.c5
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sk_bypass_prot_mem.c292
-rw-r--r--tools/testing/selftests/bpf/prog_tests/stacktrace_ips.c150
-rw-r--r--tools/testing/selftests/bpf/prog_tests/string_kfuncs.c2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_bpf_smc.c390
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_lsm.c2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_tc_edt.c145
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_tc_tunnel.c714
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_tunnel.c107
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_xsk.c2596
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_xsk.h298
-rw-r--r--tools/testing/selftests/bpf/prog_tests/verifier.c4
-rw-r--r--tools/testing/selftests/bpf/prog_tests/wq.c56
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c129
-rw-r--r--tools/testing/selftests/bpf/prog_tests/xsk.c151
-rw-r--r--tools/testing/selftests/bpf/progs/arena_strsearch.c146
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_cc_cubic.c9
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_cubic.c7
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_dctcp.c6
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_gotox.c448
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_iter_setsockopt.c17
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_iter_tcp4.c8
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_iter_tcp6.c8
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_misc.h4
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_smc.c117
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_tracing_net.h14
-rw-r--r--tools/testing/selftests/bpf/progs/connect4_prog.c21
-rw-r--r--tools/testing/selftests/bpf/progs/dynptr_success.c12
-rw-r--r--tools/testing/selftests/bpf/progs/file_reader.c145
-rw-r--r--tools/testing/selftests/bpf/progs/file_reader_fail.c52
-rw-r--r--tools/testing/selftests/bpf/progs/htab_update.c19
-rw-r--r--tools/testing/selftests/bpf/progs/ip_check_defrag.c5
-rw-r--r--tools/testing/selftests/bpf/progs/iters_looping.c53
-rw-r--r--tools/testing/selftests/bpf/progs/livepatch_trampoline.c30
-rw-r--r--tools/testing/selftests/bpf/progs/lsm.c8
-rw-r--r--tools/testing/selftests/bpf/progs/lsm_tailcall.c8
-rw-r--r--tools/testing/selftests/bpf/progs/mptcp_sockmap.c43
-rw-r--r--tools/testing/selftests/bpf/progs/rcu_read_lock.c40
-rw-r--r--tools/testing/selftests/bpf/progs/refcounted_kptr.c60
-rw-r--r--tools/testing/selftests/bpf/progs/ringbuf_bench.c11
-rw-r--r--tools/testing/selftests/bpf/progs/sk_bypass_prot_mem.c104
-rw-r--r--tools/testing/selftests/bpf/progs/stacktrace_ips.c49
-rw-r--r--tools/testing/selftests/bpf/progs/stream_fail.c6
-rw-r--r--tools/testing/selftests/bpf/progs/string_kfuncs_failure1.c12
-rw-r--r--tools/testing/selftests/bpf/progs/string_kfuncs_failure2.c2
-rw-r--r--tools/testing/selftests/bpf/progs/string_kfuncs_success.c10
-rw-r--r--tools/testing/selftests/bpf/progs/strobemeta.h6
-rw-r--r--tools/testing/selftests/bpf/progs/task_work.c6
-rw-r--r--tools/testing/selftests/bpf/progs/task_work_fail.c8
-rw-r--r--tools/testing/selftests/bpf/progs/task_work_stress.c4
-rw-r--r--tools/testing/selftests/bpf/progs/tcp_ca_write_sk_pacing.c2
-rw-r--r--tools/testing/selftests/bpf/progs/test_check_mtu.c12
-rw-r--r--tools/testing/selftests/bpf/progs/test_perf_branches.c3
-rw-r--r--tools/testing/selftests/bpf/progs/test_ringbuf_overwrite.c98
-rw-r--r--tools/testing/selftests/bpf/progs/test_tc_edt.c11
-rw-r--r--tools/testing/selftests/bpf/progs/test_tc_tunnel.c95
-rw-r--r--tools/testing/selftests/bpf/progs/test_xdp_meta.c386
-rw-r--r--tools/testing/selftests/bpf/progs/trigger_bench.c6
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_async_cb_context.c181
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_bounds.c154
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_direct_packet_access.c59
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_gotox.c389
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_live_stack.c50
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_lsm.c4
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_netfilter_ctx.c5
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_sock.c39
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_subprog_precision.c53
-rw-r--r--tools/testing/selftests/bpf/progs/wq.c17
-rw-r--r--tools/testing/selftests/bpf/progs/wq_failures.c23
-rwxr-xr-xtools/testing/selftests/bpf/test_bpftool_build.sh4
-rw-r--r--tools/testing/selftests/bpf/test_kmods/bpf_test_rqspinlock.c236
-rw-r--r--tools/testing/selftests/bpf/test_kmods/bpf_testmod.c30
-rw-r--r--tools/testing/selftests/bpf/test_loader.c29
-rw-r--r--tools/testing/selftests/bpf/test_maps.c3
-rw-r--r--tools/testing/selftests/bpf/test_tag.c2
-rwxr-xr-xtools/testing/selftests/bpf/test_tc_edt.sh100
-rwxr-xr-xtools/testing/selftests/bpf/test_tc_tunnel.sh320
-rw-r--r--tools/testing/selftests/bpf/xskxceiver.c2512
-rw-r--r--tools/testing/selftests/bpf/xskxceiver.h156
-rw-r--r--tools/testing/selftests/cachestat/.gitignore1
-rw-r--r--tools/testing/selftests/cachestat/test_cachestat.c4
-rw-r--r--tools/testing/selftests/cgroup/lib/include/cgroup_util.h20
-rw-r--r--tools/testing/selftests/cgroup/test_core.c7
-rw-r--r--tools/testing/selftests/cgroup/test_cpu.c25
-rw-r--r--tools/testing/selftests/cgroup/test_cpuset.c7
-rw-r--r--tools/testing/selftests/cgroup/test_freezer.c7
-rw-r--r--tools/testing/selftests/cgroup/test_kill.c7
-rw-r--r--tools/testing/selftests/cgroup/test_kmem.c7
-rw-r--r--tools/testing/selftests/cgroup/test_memcontrol.c7
-rw-r--r--tools/testing/selftests/cgroup/test_zswap.c7
-rw-r--r--tools/testing/selftests/coredump/.gitignore4
-rw-r--r--tools/testing/selftests/coredump/Makefile8
-rw-r--r--tools/testing/selftests/coredump/coredump_socket_protocol_test.c1568
-rw-r--r--tools/testing/selftests/coredump/coredump_socket_test.c742
-rw-r--r--tools/testing/selftests/coredump/coredump_test.h59
-rw-r--r--tools/testing/selftests/coredump/coredump_test_helpers.c383
-rw-r--r--tools/testing/selftests/coredump/stackdump_test.c1662
-rw-r--r--tools/testing/selftests/dma/dma_map_benchmark.c2
-rw-r--r--tools/testing/selftests/drivers/net/.gitignore1
-rw-r--r--tools/testing/selftests/drivers/net/Makefile4
-rw-r--r--tools/testing/selftests/drivers/net/bonding/Makefile2
-rwxr-xr-xtools/testing/selftests/drivers/net/bonding/bond_macvlan_ipvlan.sh1
-rw-r--r--tools/testing/selftests/drivers/net/bonding/config4
-rwxr-xr-xtools/testing/selftests/drivers/net/bonding/netcons_over_bonding.sh361
-rw-r--r--tools/testing/selftests/drivers/net/gro.c (renamed from tools/testing/selftests/net/gro.c)17
-rwxr-xr-xtools/testing/selftests/drivers/net/gro.py164
-rw-r--r--tools/testing/selftests/drivers/net/hw/.gitignore1
-rw-r--r--tools/testing/selftests/drivers/net/hw/Makefile26
-rwxr-xr-xtools/testing/selftests/drivers/net/hw/devlink_rate_tc_bw.py174
-rw-r--r--tools/testing/selftests/drivers/net/hw/lib/py/__init__.py9
-rw-r--r--tools/testing/selftests/drivers/net/hw/toeplitz.c (renamed from tools/testing/selftests/net/toeplitz.c)72
-rwxr-xr-xtools/testing/selftests/drivers/net/hw/toeplitz.py211
-rw-r--r--tools/testing/selftests/drivers/net/lib/py/__init__.py9
-rw-r--r--tools/testing/selftests/drivers/net/lib/py/env.py2
-rw-r--r--tools/testing/selftests/drivers/net/lib/py/load.py84
-rw-r--r--tools/testing/selftests/drivers/net/lib/sh/lib_netcons.sh80
-rwxr-xr-xtools/testing/selftests/drivers/net/netcons_basic.sh5
-rwxr-xr-xtools/testing/selftests/drivers/net/netcons_overflow.sh2
-rwxr-xr-xtools/testing/selftests/drivers/net/netcons_torture.sh130
-rw-r--r--tools/testing/selftests/drivers/net/netdevsim/Makefile5
-rwxr-xr-xtools/testing/selftests/drivers/net/netdevsim/devlink.sh116
-rwxr-xr-xtools/testing/selftests/drivers/net/netdevsim/ethtool-ring.sh85
-rwxr-xr-xtools/testing/selftests/drivers/net/psp.py13
-rwxr-xr-xtools/testing/selftests/drivers/net/ring_reconfig.py167
-rwxr-xr-xtools/testing/selftests/drivers/net/stats.py5
-rwxr-xr-xtools/testing/selftests/drivers/net/xdp.py57
-rw-r--r--tools/testing/selftests/filesystems/utils.c2
-rw-r--r--tools/testing/selftests/ftrace/test.d/00basic/trace_marker_raw.tc107
-rw-r--r--tools/testing/selftests/ftrace/test.d/dynevent/add_remove_fprobe.tc18
-rw-r--r--tools/testing/selftests/ftrace/test.d/dynevent/enable_disable_tprobe.tc40
-rw-r--r--tools/testing/selftests/ftrace/test.d/filter/event-filter-function.tc4
-rw-r--r--tools/testing/selftests/hid/tests/test_tablet.py71
-rw-r--r--tools/testing/selftests/iommu/iommufd.c105
-rw-r--r--tools/testing/selftests/iommu/iommufd_utils.h60
-rw-r--r--tools/testing/selftests/kselftest/runner.sh14
-rw-r--r--tools/testing/selftests/kvm/arm64/get-reg-list.c3
-rw-r--r--tools/testing/selftests/kvm/arm64/set_id_regs.c10
-rw-r--r--tools/testing/selftests/kvm/lib/arm64/gic_v3_its.c9
-rw-r--r--tools/testing/selftests/livepatch/functions.sh6
-rw-r--r--tools/testing/selftests/mm/uffd-unit-tests.c15
-rw-r--r--tools/testing/selftests/namespaces/.gitignore9
-rw-r--r--tools/testing/selftests/namespaces/Makefile24
-rw-r--r--tools/testing/selftests/namespaces/cred_change_test.c814
-rw-r--r--tools/testing/selftests/namespaces/listns_efault_test.c530
-rw-r--r--tools/testing/selftests/namespaces/listns_pagination_bug.c138
-rw-r--r--tools/testing/selftests/namespaces/listns_permissions_test.c759
-rw-r--r--tools/testing/selftests/namespaces/listns_test.c679
-rw-r--r--tools/testing/selftests/namespaces/ns_active_ref_test.c2672
-rw-r--r--tools/testing/selftests/namespaces/nsid_test.c107
-rw-r--r--tools/testing/selftests/namespaces/regression_pidfd_setns_test.c113
-rw-r--r--tools/testing/selftests/namespaces/siocgskns_test.c1824
-rw-r--r--tools/testing/selftests/namespaces/stress_test.c626
-rw-r--r--tools/testing/selftests/namespaces/wrappers.h35
-rw-r--r--tools/testing/selftests/net/.gitignore8
-rw-r--r--tools/testing/selftests/net/Makefile7
-rw-r--r--tools/testing/selftests/net/af_unix/.gitignore8
-rw-r--r--tools/testing/selftests/net/af_unix/Makefile2
-rw-r--r--tools/testing/selftests/net/af_unix/so_peek_off.c162
-rw-r--r--tools/testing/selftests/net/af_unix/unix_connreset.c180
-rwxr-xr-xtools/testing/selftests/net/arp_ndisc_evict_nocarrier.sh2
-rwxr-xr-xtools/testing/selftests/net/bareudp.sh2
-rwxr-xr-xtools/testing/selftests/net/busy_poll_test.sh24
-rw-r--r--tools/testing/selftests/net/busy_poller.c16
-rwxr-xr-xtools/testing/selftests/net/fib_tests.sh66
-rwxr-xr-xtools/testing/selftests/net/forwarding/bridge_mdb.sh100
-rwxr-xr-xtools/testing/selftests/net/forwarding/lib_sh_test.sh7
-rwxr-xr-xtools/testing/selftests/net/forwarding/local_termination.sh2
-rwxr-xr-xtools/testing/selftests/net/gro.sh105
-rw-r--r--tools/testing/selftests/net/io_uring_zerocopy_tx.c24
-rw-r--r--tools/testing/selftests/net/lib.sh2
-rw-r--r--tools/testing/selftests/net/lib/Makefile1
-rwxr-xr-xtools/testing/selftests/net/lib/ksft_setup_loopback.sh111
-rw-r--r--tools/testing/selftests/net/lib/py/__init__.py5
-rw-r--r--tools/testing/selftests/net/lib/py/ksft.py105
-rw-r--r--tools/testing/selftests/net/lib/py/nsim.py2
-rw-r--r--tools/testing/selftests/net/lib/py/utils.py20
-rw-r--r--tools/testing/selftests/net/lib/xdp_native.bpf.c5
-rw-r--r--tools/testing/selftests/net/mptcp/mptcp_connect.c28
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_connect.sh146
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_join.sh361
-rw-r--r--tools/testing/selftests/net/mptcp/mptcp_lib.sh79
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_sockopt.sh45
-rwxr-xr-xtools/testing/selftests/net/mptcp/simult_flows.sh46
-rwxr-xr-xtools/testing/selftests/net/mptcp/userspace_pm.sh3
-rwxr-xr-xtools/testing/selftests/net/netfilter/nft_flowtable.sh126
-rw-r--r--tools/testing/selftests/net/netfilter/sctp_collision.c3
-rw-r--r--tools/testing/selftests/net/netlink-dumps.c1
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_rto_synack_rto_max.pkt54
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_syscall_bad_arg_sendmsg-empty-iov.pkt4
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_user_timeout_user-timeout-probe.pkt6
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_basic.pkt2
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_batch.pkt2
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_client.pkt2
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_closed.pkt2
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_epoll_edge.pkt3
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_epoll_exclusive.pkt3
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_epoll_oneshot.pkt3
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_fastopen-client.pkt2
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_fastopen-server.pkt2
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_maxfrags.pkt2
-rw-r--r--tools/testing/selftests/net/packetdrill/tcp_zerocopy_small.pkt2
-rwxr-xr-xtools/testing/selftests/net/rtnetlink.sh20
-rw-r--r--tools/testing/selftests/net/sctp_hello.c17
-rwxr-xr-xtools/testing/selftests/net/sctp_vrf.sh73
-rw-r--r--tools/testing/selftests/net/setup_loopback.sh120
-rw-r--r--tools/testing/selftests/net/setup_veth.sh45
-rw-r--r--tools/testing/selftests/net/so_txtime.c2
-rw-r--r--tools/testing/selftests/net/tls.c141
-rwxr-xr-xtools/testing/selftests/net/toeplitz.sh199
-rwxr-xr-xtools/testing/selftests/net/toeplitz_client.sh28
-rwxr-xr-xtools/testing/selftests/net/traceroute.sh313
-rw-r--r--tools/testing/selftests/net/txtimestamp.c2
-rw-r--r--tools/testing/selftests/nolibc/Makefile.nolibc6
-rw-r--r--tools/testing/selftests/nolibc/nolibc-test.c13
-rwxr-xr-xtools/testing/selftests/nolibc/run-tests.sh8
-rw-r--r--tools/testing/selftests/pidfd/pidfd.h15
-rw-r--r--tools/testing/selftests/pidfd/pidfd_info_test.c73
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-again.sh56
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm-series.sh116
-rwxr-xr-xtools/testing/selftests/rcutorture/bin/kvm.sh2
-rw-r--r--tools/testing/selftests/rcutorture/configs/rcu/TREE041
-rw-r--r--tools/testing/selftests/rseq/rseq-s390.h39
-rwxr-xr-xtools/testing/selftests/run_kselftest.sh14
-rw-r--r--tools/testing/selftests/sched_ext/Makefile1
-rw-r--r--tools/testing/selftests/sched_ext/peek_dsq.bpf.c251
-rw-r--r--tools/testing/selftests/sched_ext/peek_dsq.c224
-rw-r--r--tools/testing/selftests/tc-testing/tc-tests/infra/qdiscs.json72
-rw-r--r--tools/testing/selftests/timers/nanosleep.c55
-rw-r--r--tools/testing/selftests/timers/posix_timers.c32
-rw-r--r--tools/testing/selftests/tpm2/tpm2.py4
-rw-r--r--tools/testing/selftests/ublk/kublk.c70
-rw-r--r--tools/testing/selftests/ublk/kublk.h9
-rw-r--r--tools/testing/selftests/user_events/perf_test.c2
-rw-r--r--tools/testing/selftests/vDSO/vdso_config.h4
-rw-r--r--tools/testing/selftests/vfio/Makefile10
-rw-r--r--tools/testing/selftests/vfio/lib/drivers/dsa/dsa.c36
-rw-r--r--tools/testing/selftests/vfio/lib/drivers/ioat/ioat.c18
-rw-r--r--tools/testing/selftests/vfio/lib/include/libvfio.h26
-rw-r--r--tools/testing/selftests/vfio/lib/include/libvfio/assert.h54
-rw-r--r--tools/testing/selftests/vfio/lib/include/libvfio/iommu.h76
-rw-r--r--tools/testing/selftests/vfio/lib/include/libvfio/iova_allocator.h23
-rw-r--r--tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h125
-rw-r--r--tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_driver.h97
-rw-r--r--tools/testing/selftests/vfio/lib/include/vfio_util.h295
-rw-r--r--tools/testing/selftests/vfio/lib/iommu.c465
-rw-r--r--tools/testing/selftests/vfio/lib/iova_allocator.c94
-rw-r--r--tools/testing/selftests/vfio/lib/libvfio.c78
-rw-r--r--tools/testing/selftests/vfio/lib/libvfio.mk23
-rw-r--r--tools/testing/selftests/vfio/lib/vfio_pci_device.c260
-rw-r--r--tools/testing/selftests/vfio/lib/vfio_pci_driver.c16
-rwxr-xr-xtools/testing/selftests/vfio/run.sh109
-rwxr-xr-xtools/testing/selftests/vfio/scripts/cleanup.sh41
-rwxr-xr-xtools/testing/selftests/vfio/scripts/lib.sh42
-rwxr-xr-xtools/testing/selftests/vfio/scripts/run.sh16
-rwxr-xr-xtools/testing/selftests/vfio/scripts/setup.sh48
-rw-r--r--tools/testing/selftests/vfio/vfio_dma_mapping_test.c127
-rw-r--r--tools/testing/selftests/vfio/vfio_iommufd_setup_test.c2
-rw-r--r--tools/testing/selftests/vfio/vfio_pci_device_init_perf_test.c168
-rw-r--r--tools/testing/selftests/vfio/vfio_pci_device_test.c12
-rw-r--r--tools/testing/selftests/vfio/vfio_pci_driver_test.c57
-rwxr-xr-xtools/testing/selftests/vsock/vmtest.sh354
-rw-r--r--tools/testing/selftests/x86/test_vsyscall.c21
-rw-r--r--tools/testing/vsock/vsock_test.c7
-rw-r--r--tools/thermal/thermal-engine/thermal-engine.c2
-rw-r--r--tools/tracing/latency/latency-collector.c2
-rw-r--r--usr/Makefile4
-rw-r--r--usr/include/Makefile11
-rwxr-xr-xusr/include/headers_check.pl63
-rw-r--r--virt/kvm/guest_memfd.c45
-rw-r--r--virt/kvm/kvm_main.c7
9189 files changed, 442054 insertions, 129369 deletions
diff --git a/.clang-format b/.clang-format
index f371a13b4d19..2fadacbd3b8e 100644
--- a/.clang-format
+++ b/.clang-format
@@ -167,7 +167,7 @@ ForEachMacros:
- 'drm_connector_for_each_possible_encoder'
- 'drm_exec_for_each_locked_object'
- 'drm_exec_for_each_locked_object_reverse'
- - 'drm_for_each_bridge_in_chain'
+ - 'drm_for_each_bridge_in_chain_scoped'
- 'drm_for_each_connector_iter'
- 'drm_for_each_crtc'
- 'drm_for_each_crtc_reverse'
@@ -415,6 +415,7 @@ ForEachMacros:
- 'for_each_prop_dlc_cpus'
- 'for_each_prop_dlc_platforms'
- 'for_each_property_of_node'
+ - 'for_each_pt_level_entry'
- 'for_each_rdt_resource'
- 'for_each_reg'
- 'for_each_reg_filtered'
diff --git a/.gitignore b/.gitignore
index 86a1ba0d9035..3a7241c941f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,6 +41,7 @@
*.o.*
*.patch
*.pyc
+*.rlib
*.rmeta
*.rpm
*.rsi
diff --git a/.mailmap b/.mailmap
index c371ba3fb845..6b22c085b39d 100644
--- a/.mailmap
+++ b/.mailmap
@@ -27,6 +27,7 @@ Alan Cox <alan@lxorguk.ukuu.org.uk>
Alan Cox <root@hraefn.swansea.linux.org.uk>
Aleksandar Markovic <aleksandar.markovic@mips.com> <aleksandar.markovic@imgtec.com>
Aleksey Gorelov <aleksey_gorelov@phoenix.com>
+Alex Williamson <alex@shazbot.org> <alex.williamson@redhat.com>
Alexander Lobakin <alobakin@pm.me> <alobakin@dlink.ru>
Alexander Lobakin <alobakin@pm.me> <alobakin@marvell.com>
Alexander Lobakin <alobakin@pm.me> <bloodyreaper@yandex.ru>
@@ -173,6 +174,7 @@ Carlos Bilbao <carlos.bilbao@kernel.org> <bilbao@vt.edu>
Changbin Du <changbin.du@intel.com> <changbin.du@gmail.com>
Chao Yu <chao@kernel.org> <chao2.yu@samsung.com>
Chao Yu <chao@kernel.org> <yuchao0@huawei.com>
+Chen-Yu Tsai <wens@kernel.org> <wens@csie.org>
Chester Lin <chester62515@gmail.com> <clin@suse.com>
Chris Chiu <chris.chiu@canonical.com> <chiu@endlessm.com>
Chris Chiu <chris.chiu@canonical.com> <chiu@endlessos.org>
@@ -205,6 +207,7 @@ Danilo Krummrich <dakr@kernel.org> <dakr@redhat.com>
David Brownell <david-b@pacbell.net>
David Collins <quic_collinsd@quicinc.com> <collinsd@codeaurora.org>
David Heidelberg <david@ixit.cz> <d.okias@gmail.com>
+David Hildenbrand <david@kernel.org> <david@redhat.com>
David Rheinsberg <david@readahead.eu> <dh.herrmann@gmail.com>
David Rheinsberg <david@readahead.eu> <dh.herrmann@googlemail.com>
David Rheinsberg <david@readahead.eu> <david.rheinsberg@gmail.com>
@@ -342,7 +345,8 @@ Jayachandran C <c.jayachandran@gmail.com> <jayachandranc@netlogicmicro.com>
Jayachandran C <c.jayachandran@gmail.com> <jchandra@broadcom.com>
Jayachandran C <c.jayachandran@gmail.com> <jchandra@digeo.com>
Jayachandran C <c.jayachandran@gmail.com> <jnair@caviumnetworks.com>
-<jean-philippe@linaro.org> <jean-philippe.brucker@arm.com>
+Jean-Philippe Brucker <jpb@kernel.org> <jean-philippe.brucker@arm.com>
+Jean-Philippe Brucker <jpb@kernel.org> <jean-philippe@linaro.org>
Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org> <jeanmichel.hautbois@ideasonboard.com>
Jean Tourrilhes <jt@hpl.hp.com>
Jeevan Shriram <quic_jshriram@quicinc.com> <jshriram@codeaurora.org>
@@ -425,7 +429,7 @@ Kenneth W Chen <kenneth.w.chen@intel.com>
Kenneth Westfield <quic_kwestfie@quicinc.com> <kwestfie@codeaurora.org>
Kiran Gunda <quic_kgunda@quicinc.com> <kgunda@codeaurora.org>
Kirill Tkhai <tkhai@ya.ru> <ktkhai@virtuozzo.com>
-Kirill A. Shutemov <kas@kernel.org> <kirill.shutemov@linux.intel.com>
+Kiryl Shutsemau <kas@kernel.org> <kirill.shutemov@linux.intel.com>
Kishon Vijay Abraham I <kishon@kernel.org> <kishon@ti.com>
Konrad Dybcio <konradybcio@kernel.org> <konrad.dybcio@linaro.org>
Konrad Dybcio <konradybcio@kernel.org> <konrad.dybcio@somainline.org>
@@ -436,6 +440,7 @@ Krishna Manikandan <quic_mkrishn@quicinc.com> <mkrishn@codeaurora.org>
Krzysztof Kozlowski <krzk@kernel.org> <k.kozlowski.k@gmail.com>
Krzysztof Kozlowski <krzk@kernel.org> <k.kozlowski@samsung.com>
Krzysztof Kozlowski <krzk@kernel.org> <krzysztof.kozlowski@canonical.com>
+Krzysztof Kozlowski <krzk@kernel.org> <krzysztof.kozlowski@linaro.org>
Krzysztof Wilczyński <kwilczynski@kernel.org> <krzysztof.wilczynski@linux.com>
Krzysztof Wilczyński <kwilczynski@kernel.org> <kw@linux.com>
Kshitiz Godara <quic_kgodara@quicinc.com> <kgodara@codeaurora.org>
@@ -586,8 +591,8 @@ Nicolas Pitre <nico@fluxnic.net> <nicolas.pitre@linaro.org>
Nicolas Pitre <nico@fluxnic.net> <nico@linaro.org>
Nicolas Saenz Julienne <nsaenz@kernel.org> <nsaenzjulienne@suse.de>
Nicolas Saenz Julienne <nsaenz@kernel.org> <nsaenzjulienne@suse.com>
-Nicolas Schier <nicolas.schier@linux.dev> <n.schier@avm.de>
-Nicolas Schier <nicolas.schier@linux.dev> <nicolas@fjasle.eu>
+Nicolas Schier <nsc@kernel.org> <n.schier@avm.de>
+Nicolas Schier <nsc@kernel.org> <nicolas@fjasle.eu>
Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se>
Nikolay Aleksandrov <razor@blackwall.org> <naleksan@redhat.com>
Nikolay Aleksandrov <razor@blackwall.org> <nikolay@redhat.com>
@@ -604,7 +609,8 @@ Oleksij Rempel <o.rempel@pengutronix.de>
Oleksij Rempel <o.rempel@pengutronix.de> <ore@pengutronix.de>
Oliver Hartkopp <socketcan@hartkopp.net> <oliver.hartkopp@volkswagen.de>
Oliver Hartkopp <socketcan@hartkopp.net> <oliver@hartkopp.net>
-Oliver Upton <oliver.upton@linux.dev> <oupton@google.com>
+Oliver Upton <oupton@kernel.org> <oupton@google.com>
+Oliver Upton <oupton@kernel.org> <oliver.upton@linux.dev>
Ondřej Jirman <megi@xff.cz> <megous@megous.com>
Oza Pawandeep <quic_poza@quicinc.com> <poza@codeaurora.org>
Pali Rohár <pali@kernel.org> <pali.rohar@gmail.com>
@@ -633,6 +639,7 @@ Peter Oruba <peter.oruba@amd.com>
Peter Oruba <peter@oruba.de>
Pierre-Louis Bossart <pierre-louis.bossart@linux.dev> <pierre-louis.bossart@linux.intel.com>
Pratyush Anand <pratyush.anand@gmail.com> <pratyush.anand@st.com>
+Pratyush Yadav <pratyush@kernel.org> <ptyadav@amazon.de>
Praveen BP <praveenbp@ti.com>
Pradeep Kumar Chitrapu <quic_pradeepc@quicinc.com> <pradeepc@codeaurora.org>
Prasad Sodagudi <quic_psodagud@quicinc.com> <psodagud@codeaurora.org>
@@ -643,6 +650,7 @@ Qais Yousef <qyousef@layalina.io> <qais.yousef@arm.com>
Quentin Monnet <qmo@kernel.org> <quentin.monnet@netronome.com>
Quentin Monnet <qmo@kernel.org> <quentin@isovalent.com>
Quentin Perret <qperret@qperret.net> <quentin.perret@arm.com>
+Rae Moar <raemoar63@gmail.com> <rmoar@google.com>
Rafael J. Wysocki <rjw@rjwysocki.net> <rjw@sisk.pl>
Rajeev Nandan <quic_rajeevny@quicinc.com> <rajeevny@codeaurora.org>
Rajendra Nayak <quic_rjendra@quicinc.com> <rnayak@codeaurora.org>
@@ -686,7 +694,10 @@ Sachin Mokashi <sachin.mokashi@intel.com> <sachinx.mokashi@intel.com>
Sachin P Sant <ssant@in.ibm.com>
Sai Prakash Ranjan <quic_saipraka@quicinc.com> <saiprakash.ranjan@codeaurora.org>
Sakari Ailus <sakari.ailus@linux.intel.com> <sakari.ailus@iki.fi>
+Sam Protsenko <semen.protsenko@linaro.org>
+Sam Protsenko <semen.protsenko@linaro.org> <semen.protsenko@globallogic.com>
Sam Ravnborg <sam@mars.ravnborg.org>
+Samuel Kayode <samkay014@gmail.com> <samuel.kayode@savoirfairelinux.com>
Sankeerth Billakanti <quic_sbillaka@quicinc.com> <sbillaka@codeaurora.org>
Santosh Shilimkar <santosh.shilimkar@oracle.org>
Santosh Shilimkar <ssantosh@kernel.org>
diff --git a/.pylintrc b/.pylintrc
index 89eaf2100edd..8c6fc2b628b3 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -1,2 +1,2 @@
[MASTER]
-init-hook='import sys; sys.path += ["scripts/lib/kdoc", "scripts/lib/abi", "tools/docs/lib"]'
+init-hook='import sys; sys.path += ["tools/lib/python"]'
diff --git a/CREDITS b/CREDITS
index 903ea238e64f..fa5397f4ebcd 100644
--- a/CREDITS
+++ b/CREDITS
@@ -2036,6 +2036,10 @@ S: Botanicka' 68a
S: 602 00 Brno
S: Czech Republic
+N: Karsten Keil
+E: isdn@linux-pingi.de
+D: ISDN subsystem maintainer
+
N: Jakob Kemi
E: jakob.kemi@telia.com
D: V4L W9966 Webcam driver
diff --git a/Documentation/ABI/stable/sysfs-driver-qaic b/Documentation/ABI/stable/sysfs-driver-qaic
new file mode 100644
index 000000000000..c767a93342b3
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-driver-qaic
@@ -0,0 +1,19 @@
+What: /sys/bus/pci/drivers/qaic/XXXX:XX:XX.X/accel/accel<minor_nr>/dbc<N>_state
+Date: October 2025
+KernelVersion: 6.19
+Contact: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
+Description: Represents the current state of DMA Bridge channel (DBC). Below are the possible
+ states:
+
+ =================== ==========================================================
+ IDLE (0) DBC is free and can be activated
+ ASSIGNED (1) DBC is activated and a workload is running on device
+ BEFORE_SHUTDOWN (2) Sub-system associated with this workload has crashed and
+ it will shutdown soon
+ AFTER_SHUTDOWN (3) Sub-system associated with this workload has crashed and
+ it has shutdown
+ BEFORE_POWER_UP (4) Sub-system associated with this workload is shutdown and
+ it will be powered up soon
+ AFTER_POWER_UP (5) Sub-system associated with this workload is now powered up
+ =================== ==========================================================
+Users: Any userspace application or clients interested in DBC state.
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index c2385183826c..d4b3696a9efb 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -20,9 +20,10 @@ Description:
rule format: action [condition ...]
action: measure | dont_measure | appraise | dont_appraise |
- audit | hash | dont_hash
+ audit | dont_audit | hash | dont_hash
condition:= base | lsm [option]
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [fsname=]
+ [fs_subtype=]
[uid=] [euid=] [gid=] [egid=]
[fowner=] [fgroup=]]
lsm: [[subj_user=] [subj_role=] [subj_type=]
diff --git a/Documentation/ABI/testing/sysfs-auxdisplay-linedisp b/Documentation/ABI/testing/sysfs-auxdisplay-linedisp
new file mode 100644
index 000000000000..55f1b559e84e
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-auxdisplay-linedisp
@@ -0,0 +1,90 @@
+What: /sys/.../message
+Date: October 2021
+KernelVersion: 5.16
+Description:
+ Controls the text message displayed on character line displays.
+
+ Reading returns the current message with a trailing newline.
+ Writing updates the displayed message. Messages longer than the
+ display width will automatically scroll. Trailing newlines in
+ input are automatically trimmed.
+
+ Writing an empty string clears the display.
+
+ Example:
+ echo "Hello World" > message
+ cat message # Returns "Hello World\n"
+
+What: /sys/.../num_chars
+Date: November 2025
+KernelVersion: 6.18
+Contact: Jean-François Lessard <jefflessard3@gmail.com>
+Description:
+ Read-only attribute showing the character width capacity of
+ the line display device. Messages longer than this will scroll.
+
+ Example:
+ cat num_chars # Returns "16\n" for 16-char display
+
+What: /sys/.../scroll_step_ms
+Date: October 2021
+KernelVersion: 5.16
+Description:
+ Controls the scrolling speed for messages longer than the display
+ width, specified in milliseconds per scroll step.
+
+ Setting to 0 disables scrolling. Default is 500ms.
+
+ Example:
+ echo "250" > scroll_step_ms # 4Hz scrolling
+ cat scroll_step_ms # Returns "250\n"
+
+What: /sys/.../map_seg7
+Date: January 2024
+KernelVersion: 6.9
+Description:
+ Read/write binary blob representing the ASCII-to-7-segment
+ display conversion table used by the linedisp driver, as defined
+ by struct seg7_conversion_map in <linux/map_to_7segment.h>.
+
+ Only visible on displays with 7-segment capability.
+
+ This attribute is not human-readable. Writes must match the
+ struct size exactly, else -EINVAL is returned; reads return the
+ entire mapping as a binary blob.
+
+ This interface and its implementation match existing conventions
+ used in segment-mapped display drivers since 2005.
+
+ ABI note: This style of binary sysfs attribute *is an exception*
+ to current "one value per file, text only" sysfs rules, for
+ historical compatibility and driver uniformity. New drivers are
+ discouraged from introducing additional binary sysfs ABIs.
+
+ Reference interface guidance:
+ - include/uapi/linux/map_to_7segment.h
+
+What: /sys/.../map_seg14
+Date: January 2024
+KernelVersion: 6.9
+Description:
+ Read/write binary blob representing the ASCII-to-14-segment
+ display conversion table used by the linedisp driver, as defined
+ by struct seg14_conversion_map in <linux/map_to_14segment.h>.
+
+ Only visible on displays with 14-segment capability.
+
+ This attribute is not human-readable. Writes must match the
+ struct size exactly, else -EINVAL is returned; reads return the
+ entire mapping as a binary blob.
+
+ This interface and its implementation match existing conventions
+ used by segment-mapped display drivers since 2005.
+
+ ABI note: This style of binary sysfs attribute *is an exception*
+ to current "one value per file, text only" sysfs rules, for
+ historical compatibility and driver uniformity. New drivers are
+ discouraged from introducing additional binary sysfs ABIs.
+
+ Reference interface guidance:
+ - include/uapi/linux/map_to_14segment.h
diff --git a/Documentation/ABI/testing/sysfs-block-bcache b/Documentation/ABI/testing/sysfs-block-bcache
index 9e4bbc5d51fd..9344a657ca70 100644
--- a/Documentation/ABI/testing/sysfs-block-bcache
+++ b/Documentation/ABI/testing/sysfs-block-bcache
@@ -106,13 +106,6 @@ Description:
will be discarded from the cache. Should not be turned off with
writeback caching enabled.
-What: /sys/block/<disk>/bcache/discard
-Date: November 2010
-Contact: Kent Overstreet <kent.overstreet@gmail.com>
-Description:
- For a cache, a boolean allowing discard/TRIM to be turned off
- or back on if the device supports it.
-
What: /sys/block/<disk>/bcache/bucket_size
Date: November 2010
Contact: Kent Overstreet <kent.overstreet@gmail.com>
diff --git a/Documentation/ABI/testing/sysfs-bus-cxl b/Documentation/ABI/testing/sysfs-bus-cxl
index 6b4e8c7a963d..c80a1b5a03db 100644
--- a/Documentation/ABI/testing/sysfs-bus-cxl
+++ b/Documentation/ABI/testing/sysfs-bus-cxl
@@ -496,8 +496,17 @@ Description:
changed, only freed by writing 0. The kernel makes no guarantees
that data is maintained over an address space freeing event, and
there is no guarantee that a free followed by an allocate
- results in the same address being allocated.
+ results in the same address being allocated. If extended linear
+ cache is present, the size indicates extended linear cache size
+ plus the CXL region size.
+What: /sys/bus/cxl/devices/regionZ/extended_linear_cache_size
+Date: October, 2025
+KernelVersion: v6.19
+Contact: linux-cxl@vger.kernel.org
+Description:
+ (RO) The size of extended linear cache, if there is an extended
+ linear cache. Otherwise the attribute will not be visible.
What: /sys/bus/cxl/devices/regionZ/mode
Date: January, 2023
diff --git a/Documentation/ABI/testing/sysfs-class-power-rt9756 b/Documentation/ABI/testing/sysfs-class-power-rt9756
new file mode 100644
index 000000000000..c4d6c2b4715d
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-power-rt9756
@@ -0,0 +1,30 @@
+What: /sys/class/power_supply/rt9756-*/watchdog_timer
+Date: Dec 2025
+KernelVersion: 6.19
+Contact: ChiYuan Huang <cy_huang@richtek.com>
+Description:
+ This entry shows and sets the watchdog timer when rt9756 charger
+ operates in charging mode. When the timer expires, the device
+ will disable the charging. To prevent the timer expires, any
+ host communication can make the timer restarted.
+
+ Access: Read, Write
+
+ Valid values:
+ - 500, 1000, 5000, 30000, 40000, 80000, 128000 or 255000 (milliseconds),
+ - 0: disabled
+
+What: /sys/class/power_supply/rt9756-*/operation_mode
+Date: Dec 2025
+KernelVersion: 6.19
+Contact: ChiYuan Huang <cy_huang@richtek.com>
+Description:
+ This entry shows and set the operation mode when rt9756 charger
+ operates in charging phase. If 'bypass' mode is used, internal
+ path will connect vbus directly to vbat. Else, default 'div2'
+ mode for the switch-cap charging.
+
+ Access: Read, Write
+
+ Valid values:
+ - 'bypass' or 'div2'
diff --git a/Documentation/ABI/testing/sysfs-driver-intel-xe-sriov b/Documentation/ABI/testing/sysfs-driver-intel-xe-sriov
new file mode 100644
index 000000000000..2fd7e9b7bacc
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-intel-xe-sriov
@@ -0,0 +1,159 @@
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/
+Date: October 2025
+KernelVersion: 6.19
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ This directory appears for the particular Intel Xe device when:
+
+ - device supports SR-IOV, and
+ - device is a Physical Function (PF), and
+ - driver support for the SR-IOV PF is enabled on given device.
+
+ This directory is used as a root for all attributes required to
+ manage both Physical Function (PF) and Virtual Functions (VFs).
+
+
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/
+Date: October 2025
+KernelVersion: 6.19
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ This directory holds attributes related to the SR-IOV Physical
+ Function (PF).
+
+
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf1/
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf2/
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<N>/
+Date: October 2025
+KernelVersion: 6.19
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ These directories hold attributes related to the SR-IOV Virtual
+ Functions (VFs).
+
+ Note that the VF number <N> is 1-based as described in PCI SR-IOV
+ specification as the Xe driver follows that naming schema.
+
+ There could be "vf1", "vf2" and so on, up to "vf<N>", where <N>
+ matches the value of the "sriov_totalvfs" attribute.
+
+
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/profile/exec_quantum_ms
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/profile/preempt_timeout_us
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/profile/sched_priority
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/profile/exec_quantum_ms
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/profile/preempt_timeout_us
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/profile/sched_priority
+Date: October 2025
+KernelVersion: 6.19
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ These files expose scheduling parameters for the PF and its VFs, and
+ are visible only on Intel Xe platforms that use time-sliced GPU sharing.
+ They can be changed even if VFs are enabled and running and reflect the
+ settings of all tiles/GTs assigned to the given function.
+
+ exec_quantum_ms: (RW) unsigned integer
+ The GT execution quantum (EQ) in [ms] for the given function.
+ Actual quantum value might be aligned per HW/FW requirements.
+
+ Default is 0 (unlimited).
+
+ preempt_timeout_us: (RW) unsigned integer
+ The GT preemption timeout in [us] of the given function.
+ Actual timeout value might be aligned per HW/FW requirements.
+
+ Default is 0 (unlimited).
+
+ sched_priority: (RW/RO) string
+ The GT scheduling priority of the given function.
+
+ "low" - function will be scheduled on the GPU for its EQ/PT
+ only if function has any work already submitted.
+
+ "normal" - functions will be scheduled on the GPU for its EQ/PT
+ irrespective of whether it has submitted a work or not.
+
+ "high" - function will be scheduled on the GPU for its EQ/PT
+ in the next time-slice after the current one completes
+ and function has a work submitted.
+
+ Default is "low".
+
+ When read, this file will display the current and available
+ scheduling priorities. The currently active priority level will
+ be enclosed in square brackets, like:
+
+ [low] normal high
+
+ This file can be read-only if changing the priority is not
+ supported.
+
+ Writes to these attributes may fail with errors like:
+ -EINVAL if provided input is malformed or not recognized,
+ -EPERM if change is not applicable on given HW/FW,
+ -EIO if FW refuses to change the provisioning.
+
+ Reads from these attributes may fail with:
+ -EUCLEAN if value is not consistent across all tiles/GTs.
+
+
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/.bulk_profile/exec_quantum_ms
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/.bulk_profile/preempt_timeout_us
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/.bulk_profile/sched_priority
+Date: October 2025
+KernelVersion: 6.19
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ These files allows bulk reconfiguration of the scheduling parameters
+ of the PF or VFs and are available only for Intel Xe platforms with
+ GPU sharing based on the time-slice basis. These scheduling parameters
+ can be changed even if VFs are enabled and running.
+
+ exec_quantum_ms: (WO) unsigned integer
+ The GT execution quantum (EQ) in [ms] to be applied to all functions.
+ See sriov_admin/{pf,vf<N>}/profile/exec_quantum_ms for more details.
+
+ preempt_timeout_us: (WO) unsigned integer
+ The GT preemption timeout (PT) in [us] to be applied to all functions.
+ See sriov_admin/{pf,vf<N>}/profile/preempt_timeout_us for more details.
+
+ sched_priority: (RW/RO) string
+ The GT scheduling priority to be applied for all functions.
+ See sriov_admin/{pf,vf<N>}/profile/sched_priority for more details.
+
+ Writes to these attributes may fail with errors like:
+ -EINVAL if provided input is malformed or not recognized,
+ -EPERM if change is not applicable on given HW/FW,
+ -EIO if FW refuses to change the provisioning.
+
+
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/stop
+Date: October 2025
+KernelVersion: 6.19
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ This file allows to control scheduling of the VF on the Intel Xe GPU
+ platforms. It allows to implement custom policy mechanism in case VFs
+ are misbehaving or triggering adverse events above defined thresholds.
+
+ stop: (WO) bool
+ All GT executions of given function shall be immediately stopped.
+ To allow scheduling this VF again, the VF FLR must be triggered.
+
+ Writes to this attribute may fail with errors like:
+ -EINVAL if provided input is malformed or not recognized,
+ -EPERM if change is not applicable on given HW/FW,
+ -EIO if FW refuses to change the scheduling.
+
+
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/device
+What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/device
+Date: October 2025
+KernelVersion: 6.19
+Contact: intel-xe@lists.freedesktop.org
+Description:
+ These are symlinks to the underlying PCI device entry representing
+ given Xe SR-IOV function. For the PF, this link is always present.
+ For VFs, this link is present only for currently enabled VFs.
diff --git a/Documentation/ABI/testing/sysfs-module b/Documentation/ABI/testing/sysfs-module
index 62addab47d0c..6bc9af6229f0 100644
--- a/Documentation/ABI/testing/sysfs-module
+++ b/Documentation/ABI/testing/sysfs-module
@@ -59,6 +59,8 @@ Description: Module taint flags:
F force-loaded module
C staging driver module
E unsigned module
+ K livepatch module
+ N in-kernel test module
== =====================
What: /sys/module/grant_table/parameters/free_per_iteration
diff --git a/Documentation/ABI/testing/sysfs-power b/Documentation/ABI/testing/sysfs-power
index 4d8e1ad020f0..d38da077905a 100644
--- a/Documentation/ABI/testing/sysfs-power
+++ b/Documentation/ABI/testing/sysfs-power
@@ -454,3 +454,19 @@ Description:
disables it. Reads from the file return the current value.
The default is "1" if the build-time "SUSPEND_SKIP_SYNC" config
flag is unset, or "0" otherwise.
+
+What: /sys/power/hibernate_compression_threads
+Date: October 2025
+Contact: <luoxueqin@kylinos.cn>
+Description:
+ Controls the number of threads used for compression
+ and decompression of hibernation images.
+
+ The value can be adjusted at runtime to balance
+ performance and CPU utilization.
+
+ The change takes effect on the next hibernation or
+ resume operation.
+
+ Minimum value: 1
+ Default value: 3
diff --git a/Documentation/Kconfig b/Documentation/Kconfig
index 3a0e7ac0c4e3..8b6c4b84b218 100644
--- a/Documentation/Kconfig
+++ b/Documentation/Kconfig
@@ -19,7 +19,7 @@ config WARN_ABI_ERRORS
described at Documentation/ABI/README. Yet, as they're manually
written, it would be possible that some of those files would
have errors that would break them for being parsed by
- scripts/get_abi.pl. Add a check to verify them.
+ tools/docs/get_abi.py. Add a check to verify them.
If unsure, select 'N'.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 3609cb86137b..e96ac6dcac4f 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -8,12 +8,12 @@ subdir- := devicetree/bindings
ifneq ($(MAKECMDGOALS),cleandocs)
# Check for broken documentation file references
ifeq ($(CONFIG_WARN_MISSING_DOCUMENTS),y)
-$(shell $(srctree)/scripts/documentation-file-ref-check --warn)
+$(shell $(srctree)/tools/docs/documentation-file-ref-check --warn)
endif
# Check for broken ABI files
ifeq ($(CONFIG_WARN_ABI_ERRORS),y)
-$(shell $(srctree)/scripts/get_abi.py --dir $(srctree)/Documentation/ABI validate)
+$(shell $(srctree)/tools/docs/get_abi.py --dir $(srctree)/Documentation/ABI validate)
endif
endif
@@ -23,21 +23,22 @@ SPHINXOPTS =
SPHINXDIRS = .
DOCS_THEME =
DOCS_CSS =
-_SPHINXDIRS = $(sort $(patsubst $(srctree)/Documentation/%/index.rst,%,$(wildcard $(srctree)/Documentation/*/index.rst)))
-SPHINX_CONF = conf.py
+RUSTDOC =
PAPER =
BUILDDIR = $(obj)/output
PDFLATEX = xelatex
LATEXOPTS = -interaction=batchmode -no-shell-escape
+PYTHONPYCACHEPREFIX ?= $(abspath $(BUILDDIR)/__pycache__)
+
+# Wrapper for sphinx-build
+
+BUILD_WRAPPER = $(srctree)/tools/docs/sphinx-build-wrapper
+
# For denylisting "variable font" files
# Can be overridden by setting as an env variable
FONTS_CONF_DENY_VF ?= $(HOME)/deny-vf
-ifeq ($(findstring 1, $(KBUILD_VERBOSE)),)
-SPHINXOPTS += "-q"
-endif
-
# User-friendly check for sphinx-build
HAVE_SPHINX := $(shell if which $(SPHINXBUILD) >/dev/null 2>&1; then echo 1; else echo 0; fi)
@@ -46,141 +47,46 @@ ifeq ($(HAVE_SPHINX),0)
.DEFAULT:
$(warning The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed and in PATH, or set the SPHINXBUILD make variable to point to the full path of the '$(SPHINXBUILD)' executable.)
@echo
- @$(srctree)/scripts/sphinx-pre-install
+ @$(srctree)/tools/docs/sphinx-pre-install
@echo " SKIP Sphinx $@ target."
else # HAVE_SPHINX
-# User-friendly check for pdflatex and latexmk
-HAVE_PDFLATEX := $(shell if which $(PDFLATEX) >/dev/null 2>&1; then echo 1; else echo 0; fi)
-HAVE_LATEXMK := $(shell if which latexmk >/dev/null 2>&1; then echo 1; else echo 0; fi)
-
-ifeq ($(HAVE_LATEXMK),1)
- PDFLATEX := latexmk -$(PDFLATEX)
-endif #HAVE_LATEXMK
-
-# Internal variables.
-PAPEROPT_a4 = -D latex_elements.papersize=a4paper
-PAPEROPT_letter = -D latex_elements.papersize=letterpaper
-ALLSPHINXOPTS = -D kerneldoc_srctree=$(srctree) -D kerneldoc_bin=$(KERNELDOC)
-ALLSPHINXOPTS += $(PAPEROPT_$(PAPER)) $(SPHINXOPTS)
-ifneq ($(wildcard $(srctree)/.config),)
-ifeq ($(CONFIG_RUST),y)
- # Let Sphinx know we will include rustdoc
- ALLSPHINXOPTS += -t rustdoc
-endif
-endif
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# Common documentation targets
+htmldocs mandocs infodocs texinfodocs latexdocs epubdocs xmldocs pdfdocs linkcheckdocs:
+ $(Q)PYTHONPYCACHEPREFIX="$(PYTHONPYCACHEPREFIX)" \
+ $(srctree)/tools/docs/sphinx-pre-install --version-check
+ +$(Q)PYTHONPYCACHEPREFIX="$(PYTHONPYCACHEPREFIX)" \
+ $(PYTHON3) $(BUILD_WRAPPER) $@ \
+ --sphinxdirs="$(SPHINXDIRS)" $(RUSTDOC) \
+ --builddir="$(BUILDDIR)" --deny-vf=$(FONTS_CONF_DENY_VF) \
+ --theme=$(DOCS_THEME) --css=$(DOCS_CSS) --paper=$(PAPER)
-# commands; the 'cmd' from scripts/Kbuild.include is not *loopable*
-loop_cmd = $(echo-cmd) $(cmd_$(1)) || exit;
-# $2 sphinx builder e.g. "html"
-# $3 name of the build subfolder / e.g. "userspace-api/media", used as:
-# * dest folder relative to $(BUILDDIR) and
-# * cache folder relative to $(BUILDDIR)/.doctrees
-# $4 dest subfolder e.g. "man" for man pages at userspace-api/media/man
-# $5 reST source folder relative to $(src),
-# e.g. "userspace-api/media" for the linux-tv book-set at ./Documentation/userspace-api/media
-
-PYTHONPYCACHEPREFIX ?= $(abspath $(BUILDDIR)/__pycache__)
-
-quiet_cmd_sphinx = SPHINX $@ --> file://$(abspath $(BUILDDIR)/$3/$4)
- cmd_sphinx = \
- PYTHONPYCACHEPREFIX="$(PYTHONPYCACHEPREFIX)" \
- BUILDDIR=$(abspath $(BUILDDIR)) SPHINX_CONF=$(abspath $(src)/$5/$(SPHINX_CONF)) \
- $(PYTHON3) $(srctree)/scripts/jobserver-exec \
- $(CONFIG_SHELL) $(srctree)/Documentation/sphinx/parallel-wrapper.sh \
- $(SPHINXBUILD) \
- -b $2 \
- -c $(abspath $(src)) \
- -d $(abspath $(BUILDDIR)/.doctrees/$3) \
- -D version=$(KERNELVERSION) -D release=$(KERNELRELEASE) \
- $(ALLSPHINXOPTS) \
- $(abspath $(src)/$5) \
- $(abspath $(BUILDDIR)/$3/$4) && \
- if [ "x$(DOCS_CSS)" != "x" ]; then \
- cp $(if $(patsubst /%,,$(DOCS_CSS)),$(abspath $(srctree)/$(DOCS_CSS)),$(DOCS_CSS)) $(BUILDDIR)/$3/_static/; \
- fi
-
-htmldocs:
- @$(srctree)/scripts/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,html,$(var),,$(var)))
-
-htmldocs-redirects: $(srctree)/Documentation/.renames.txt
- @tools/docs/gen-redirects.py --output $(BUILDDIR) < $<
-
-# If Rust support is available and .config exists, add rustdoc generated contents.
-# If there are any, the errors from this make rustdoc will be displayed but
-# won't stop the execution of htmldocs
-
-ifneq ($(wildcard $(srctree)/.config),)
-ifeq ($(CONFIG_RUST),y)
- $(Q)$(MAKE) rustdoc || true
-endif
endif
-texinfodocs:
- @$(srctree)/scripts/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,texinfo,$(var),texinfo,$(var)))
-
-# Note: the 'info' Make target is generated by sphinx itself when
-# running the texinfodocs target define above.
-infodocs: texinfodocs
- $(MAKE) -C $(BUILDDIR)/texinfo info
-
-linkcheckdocs:
- @$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,linkcheck,$(var),,$(var)))
-
-latexdocs:
- @$(srctree)/scripts/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,latex,$(var),latex,$(var)))
-
-ifeq ($(HAVE_PDFLATEX),0)
-
-pdfdocs:
- $(warning The '$(PDFLATEX)' command was not found. Make sure you have it installed and in PATH to produce PDF output.)
- @echo " SKIP Sphinx $@ target."
-
-else # HAVE_PDFLATEX
-
-pdfdocs: DENY_VF = XDG_CONFIG_HOME=$(FONTS_CONF_DENY_VF)
-pdfdocs: latexdocs
- @$(srctree)/scripts/sphinx-pre-install --version-check
- $(foreach var,$(SPHINXDIRS), \
- $(MAKE) PDFLATEX="$(PDFLATEX)" LATEXOPTS="$(LATEXOPTS)" $(DENY_VF) -C $(BUILDDIR)/$(var)/latex || sh $(srctree)/scripts/check-variable-fonts.sh || exit; \
- mkdir -p $(BUILDDIR)/$(var)/pdf; \
- mv $(subst .tex,.pdf,$(wildcard $(BUILDDIR)/$(var)/latex/*.tex)) $(BUILDDIR)/$(var)/pdf/; \
- )
-
-endif # HAVE_PDFLATEX
-
-epubdocs:
- @$(srctree)/scripts/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,epub,$(var),epub,$(var)))
-
-xmldocs:
- @$(srctree)/scripts/sphinx-pre-install --version-check
- @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,xml,$(var),xml,$(var)))
-
-endif # HAVE_SPHINX
-
# The following targets are independent of HAVE_SPHINX, and the rules should
# work or silently pass without Sphinx.
+htmldocs-redirects: $(srctree)/Documentation/.renames.txt
+ @tools/docs/gen-redirects.py --output $(BUILDDIR) < $<
+
refcheckdocs:
- $(Q)cd $(srctree);scripts/documentation-file-ref-check
+ $(Q)cd $(srctree); tools/docs/documentation-file-ref-check
cleandocs:
$(Q)rm -rf $(BUILDDIR)
+# Used only on help
+_SPHINXDIRS = $(shell printf "%s\n" $(patsubst $(srctree)/Documentation/%/index.rst,%,$(wildcard $(srctree)/Documentation/*/index.rst)) | sort -f)
+
dochelp:
@echo ' Linux kernel internal documentation in different formats from ReST:'
@echo ' htmldocs - HTML'
@echo ' htmldocs-redirects - generate HTML redirects for moved pages'
@echo ' texinfodocs - Texinfo'
@echo ' infodocs - Info'
+ @echo ' mandocs - Man pages'
@echo ' latexdocs - LaTeX'
@echo ' pdfdocs - PDF'
@echo ' epubdocs - EPUB'
@@ -192,13 +98,17 @@ dochelp:
@echo ' cleandocs - clean all generated files'
@echo
@echo ' make SPHINXDIRS="s1 s2" [target] Generate only docs of folder s1, s2'
- @echo ' valid values for SPHINXDIRS are: $(_SPHINXDIRS)'
- @echo
- @echo ' make SPHINX_CONF={conf-file} [target] use *additional* sphinx-build'
- @echo ' configuration. This is e.g. useful to build with nit-picking config.'
+ @echo ' top level values for SPHINXDIRS are: $(_SPHINXDIRS)'
+ @echo ' you may also use a subdirectory like SPHINXDIRS=userspace-api/media,'
+ @echo ' provided that there is an index.rst file at the subdirectory.'
@echo
@echo ' make DOCS_THEME={sphinx-theme} selects a different Sphinx theme.'
@echo
@echo ' make DOCS_CSS={a .css file} adds a DOCS_CSS override file for html/epub output.'
@echo
+ @echo ' make PAPER={a4|letter} Specifies the paper size used for LaTeX/PDF output.'
+ @echo
+ @echo ' make FONTS_CONF_DENY_VF={path} sets a deny list to block variable Noto CJK fonts'
+ @echo ' for PDF build. See tools/lib/python/kdoc/latex_fonts.py for more details'
+ @echo
@echo ' Default location for the generated documents is Documentation/output'
diff --git a/Documentation/PCI/pci-error-recovery.rst b/Documentation/PCI/pci-error-recovery.rst
index 5df481ac6193..43bc4e3665b4 100644
--- a/Documentation/PCI/pci-error-recovery.rst
+++ b/Documentation/PCI/pci-error-recovery.rst
@@ -326,6 +326,21 @@ be recovered, there is nothing more that can be done; the platform
will typically report a "permanent failure" in such a case. The
device will be considered "dead" in this case.
+Drivers typically need to call pci_restore_state() after reset to
+re-initialize the device's config space registers and thereby
+bring it from D0\ :sub:`uninitialized` into D0\ :sub:`active` state
+(PCIe r7.0 sec 5.3.1.1). The PCI core invokes pci_save_state()
+on enumeration after initializing config space to ensure that a
+saved state is available for subsequent error recovery.
+Drivers which modify config space on probe may need to invoke
+pci_save_state() afterwards to record those changes for later
+error recovery. When going into system suspend, pci_save_state()
+is called for every PCI device and that state will be restored
+not only on resume, but also on any subsequent error recovery.
+In the unlikely event that the saved state recorded on suspend
+is unsuitable for error recovery, drivers should call
+pci_save_state() on resume.
+
Drivers for multi-function cards will need to coordinate among
themselves as to which driver instance will perform any "one-shot"
or global device initialization. For example, the Symbios sym53cxx2
diff --git a/Documentation/RCU/Design/Requirements/Requirements.rst b/Documentation/RCU/Design/Requirements/Requirements.rst
index f24b3c0b9b0d..ba417a08b93d 100644
--- a/Documentation/RCU/Design/Requirements/Requirements.rst
+++ b/Documentation/RCU/Design/Requirements/Requirements.rst
@@ -2637,15 +2637,16 @@ synchronize_srcu() for some other domain ``ss1``, and if an
that was held across as ``ss``-domain synchronize_srcu(), deadlock
would again be possible. Such a deadlock cycle could extend across an
arbitrarily large number of different SRCU domains. Again, with great
-power comes great responsibility.
+power comes great responsibility, though lockdep is now able to detect
+this sort of deadlock.
-Unlike the other RCU flavors, SRCU read-side critical sections can run
-on idle and even offline CPUs. This ability requires that
-srcu_read_lock() and srcu_read_unlock() contain memory barriers,
-which means that SRCU readers will run a bit slower than would RCU
-readers. It also motivates the smp_mb__after_srcu_read_unlock() API,
-which, in combination with srcu_read_unlock(), guarantees a full
-memory barrier.
+Unlike the other RCU flavors, SRCU read-side critical sections can run on
+idle and even offline CPUs, with the exception of srcu_read_lock_fast()
+and friends. This ability requires that srcu_read_lock() and
+srcu_read_unlock() contain memory barriers, which means that SRCU
+readers will run a bit slower than would RCU readers. It also motivates
+the smp_mb__after_srcu_read_unlock() API, which, in combination with
+srcu_read_unlock(), guarantees a full memory barrier.
Also unlike other RCU flavors, synchronize_srcu() may **not** be
invoked from CPU-hotplug notifiers, due to the fact that SRCU grace
@@ -2681,15 +2682,15 @@ run some tests first. SRCU just might need a few adjustment to deal with
that sort of load. Of course, your mileage may vary based on the speed
of your CPUs and the size of your memory.
-The `SRCU
-API <https://lwn.net/Articles/609973/#RCU%20Per-Flavor%20API%20Table>`__
+The `SRCU API
+<https://lwn.net/Articles/609973/#RCU%20Per-Flavor%20API%20Table>`__
includes srcu_read_lock(), srcu_read_unlock(),
-srcu_dereference(), srcu_dereference_check(),
-synchronize_srcu(), synchronize_srcu_expedited(),
-call_srcu(), srcu_barrier(), and srcu_read_lock_held(). It
-also includes DEFINE_SRCU(), DEFINE_STATIC_SRCU(), and
-init_srcu_struct() APIs for defining and initializing
-``srcu_struct`` structures.
+srcu_dereference(), srcu_dereference_check(), synchronize_srcu(),
+synchronize_srcu_expedited(), call_srcu(), srcu_barrier(),
+and srcu_read_lock_held(). It also includes DEFINE_SRCU(),
+DEFINE_STATIC_SRCU(), DEFINE_SRCU_FAST(), DEFINE_STATIC_SRCU_FAST(),
+init_srcu_struct(), and init_srcu_struct_fast() APIs for defining and
+initializing ``srcu_struct`` structures.
More recently, the SRCU API has added polling interfaces:
diff --git a/Documentation/RCU/checklist.rst b/Documentation/RCU/checklist.rst
index c9bfb2b218e5..4b30f701225f 100644
--- a/Documentation/RCU/checklist.rst
+++ b/Documentation/RCU/checklist.rst
@@ -417,11 +417,13 @@ over a rather long period of time, but improvements are always welcome!
you should be using RCU rather than SRCU, because RCU is almost
always faster and easier to use than is SRCU.
- Also unlike other forms of RCU, explicit initialization and
- cleanup is required either at build time via DEFINE_SRCU()
- or DEFINE_STATIC_SRCU() or at runtime via init_srcu_struct()
- and cleanup_srcu_struct(). These last two are passed a
- "struct srcu_struct" that defines the scope of a given
+ Also unlike other forms of RCU, explicit initialization
+ and cleanup is required either at build time via
+ DEFINE_SRCU(), DEFINE_STATIC_SRCU(), DEFINE_SRCU_FAST(),
+ or DEFINE_STATIC_SRCU_FAST() or at runtime via either
+ init_srcu_struct() or init_srcu_struct_fast() and
+ cleanup_srcu_struct(). These last three are passed a
+ `struct srcu_struct` that defines the scope of a given
SRCU domain. Once initialized, the srcu_struct is passed
to srcu_read_lock(), srcu_read_unlock() synchronize_srcu(),
synchronize_srcu_expedited(), and call_srcu(). A given
diff --git a/Documentation/RCU/whatisRCU.rst b/Documentation/RCU/whatisRCU.rst
index cf0b0ac9f463..a1582bd653d1 100644
--- a/Documentation/RCU/whatisRCU.rst
+++ b/Documentation/RCU/whatisRCU.rst
@@ -1227,7 +1227,10 @@ SRCU: Initialization/cleanup/ordering::
DEFINE_SRCU
DEFINE_STATIC_SRCU
+ DEFINE_SRCU_FAST // for srcu_read_lock_fast() and friends
+ DEFINE_STATIC_SRCU_FAST // for srcu_read_lock_fast() and friends
init_srcu_struct
+ init_srcu_struct_fast
cleanup_srcu_struct
smp_mb__after_srcu_read_unlock
diff --git a/Documentation/accel/qaic/aic100.rst b/Documentation/accel/qaic/aic100.rst
index 273da6192fb3..41331cf580b1 100644
--- a/Documentation/accel/qaic/aic100.rst
+++ b/Documentation/accel/qaic/aic100.rst
@@ -487,8 +487,8 @@ one user crashes, the fallout of that should be limited to that workload and not
impact other workloads. SSR accomplishes this.
If a particular workload crashes, QSM notifies the host via the QAIC_SSR MHI
-channel. This notification identifies the workload by it's assigned DBC. A
-multi-stage recovery process is then used to cleanup both sides, and get the
+channel. This notification identifies the workload by its assigned DBC. A
+multi-stage recovery process is then used to cleanup both sides, and gets the
DBC/NSPs into a working state.
When SSR occurs, any state in the workload is lost. Any inputs that were in
@@ -496,6 +496,27 @@ process, or queued by not yet serviced, are lost. The loaded artifacts will
remain in on-card DDR, but the host will need to re-activate the workload if
it desires to recover the workload.
+When SSR occurs for a specific NSP, the assigned DBC goes through the
+following state transactions in order:
+
+DBC_STATE_BEFORE_SHUTDOWN
+ Indicates that the affected NSP was found in an unrecoverable error
+ condition.
+DBC_STATE_AFTER_SHUTDOWN
+ Indicates that the NSP is under reset.
+DBC_STATE_BEFORE_POWER_UP
+ Indicates that the NSP's debug information has been collected, and is
+ ready to be collected by the host (if desired). At that stage the NSP
+ is restarted by QSM.
+DBC_STATE_AFTER_POWER_UP
+ Indicates that the NSP has been restarted, fully operational and is
+ in idle state.
+
+SSR also has an optional crashdump collection feature. If enabled, the host can
+collect the memory dump for the crashed NSP and dump it to the user space via
+the dev_coredump subsystem. The host can also decline the crashdump collection
+request from the device.
+
Reliability, Accessibility, Serviceability (RAS)
================================================
diff --git a/Documentation/accel/qaic/qaic.rst b/Documentation/accel/qaic/qaic.rst
index 018d6cc173d7..ef27e262cb91 100644
--- a/Documentation/accel/qaic/qaic.rst
+++ b/Documentation/accel/qaic/qaic.rst
@@ -36,7 +36,7 @@ polling mode and reenables the IRQ line.
This mitigation in QAIC is very effective. The same lprnet usecase that
generates 100k IRQs per second (per /proc/interrupts) is reduced to roughly 64
IRQs over 5 minutes while keeping the host system stable, and having the same
-workload throughput performance (within run to run noise variation).
+workload throughput performance (within run-to-run noise variation).
Single MSI Mode
---------------
@@ -49,7 +49,7 @@ useful to be able to fall back to a single MSI when needed.
To support this fallback, we allow the case where only one MSI is able to be
allocated, and share that one MSI between MHI and the DBCs. The device detects
when only one MSI has been configured and directs the interrupts for the DBCs
-to the interrupt normally used for MHI. Unfortunately this means that the
+to the interrupt normally used for MHI. Unfortunately, this means that the
interrupt handlers for every DBC and MHI wake up for every interrupt that
arrives; however, the DBC threaded irq handlers only are started when work to be
done is detected (MHI will always start its threaded handler).
@@ -62,9 +62,9 @@ never disabled, allowing each new entry to the FIFO to trigger a new interrupt.
Neural Network Control (NNC) Protocol
=====================================
-The implementation of NNC is split between the KMD (QAIC) and UMD. In general
+The implementation of NNC is split between the KMD (QAIC) and UMD. In general,
QAIC understands how to encode/decode NNC wire protocol, and elements of the
-protocol which require kernel space knowledge to process (for example, mapping
+protocol which requires kernel space knowledge to process (for example, mapping
host memory to device IOVAs). QAIC understands the structure of a message, and
all of the transactions. QAIC does not understand commands (the payload of a
passthrough transaction).
diff --git a/Documentation/accounting/taskstats.rst b/Documentation/accounting/taskstats.rst
index 2a28b7f55c10..173c1e7bf5ef 100644
--- a/Documentation/accounting/taskstats.rst
+++ b/Documentation/accounting/taskstats.rst
@@ -76,41 +76,43 @@ The messages are in the format::
The taskstats payload is one of the following three kinds:
1. Commands: Sent from user to kernel. Commands to get data on
-a pid/tgid consist of one attribute, of type TASKSTATS_CMD_ATTR_PID/TGID,
-containing a u32 pid or tgid in the attribute payload. The pid/tgid denotes
-the task/process for which userspace wants statistics.
-
-Commands to register/deregister interest in exit data from a set of cpus
-consist of one attribute, of type
-TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK and contain a cpumask in the
-attribute payload. The cpumask is specified as an ascii string of
-comma-separated cpu ranges e.g. to listen to exit data from cpus 1,2,3,5,7,8
-the cpumask would be "1-3,5,7-8". If userspace forgets to deregister interest
-in cpus before closing the listening socket, the kernel cleans up its interest
-set over time. However, for the sake of efficiency, an explicit deregistration
-is advisable.
+ a pid/tgid consist of one attribute, of type TASKSTATS_CMD_ATTR_PID/TGID,
+ containing a u32 pid or tgid in the attribute payload. The pid/tgid denotes
+ the task/process for which userspace wants statistics.
+
+ Commands to register/deregister interest in exit data from a set of cpus
+ consist of one attribute, of type
+ TASKSTATS_CMD_ATTR_REGISTER/DEREGISTER_CPUMASK and contain a cpumask in the
+ attribute payload. The cpumask is specified as an ascii string of
+ comma-separated cpu ranges e.g. to listen to exit data from cpus 1,2,3,5,7,8
+ the cpumask would be "1-3,5,7-8". If userspace forgets to deregister
+ interest in cpus before closing the listening socket, the kernel cleans up
+ its interest set over time. However, for the sake of efficiency, an explicit
+ deregistration is advisable.
2. Response for a command: sent from the kernel in response to a userspace
-command. The payload is a series of three attributes of type:
+ command. The payload is a series of three attributes of type:
-a) TASKSTATS_TYPE_AGGR_PID/TGID : attribute containing no payload but indicates
-a pid/tgid will be followed by some stats.
+ a) TASKSTATS_TYPE_AGGR_PID/TGID: attribute containing no payload but
+ indicates a pid/tgid will be followed by some stats.
-b) TASKSTATS_TYPE_PID/TGID: attribute whose payload is the pid/tgid whose stats
-are being returned.
+ b) TASKSTATS_TYPE_PID/TGID: attribute whose payload is the pid/tgid whose
+ stats are being returned.
-c) TASKSTATS_TYPE_STATS: attribute with a struct taskstats as payload. The
-same structure is used for both per-pid and per-tgid stats.
+ c) TASKSTATS_TYPE_STATS: attribute with a struct taskstats as payload. The
+ same structure is used for both per-pid and per-tgid stats.
3. New message sent by kernel whenever a task exits. The payload consists of a
series of attributes of the following type:
-a) TASKSTATS_TYPE_AGGR_PID: indicates next two attributes will be pid+stats
-b) TASKSTATS_TYPE_PID: contains exiting task's pid
-c) TASKSTATS_TYPE_STATS: contains the exiting task's per-pid stats
-d) TASKSTATS_TYPE_AGGR_TGID: indicates next two attributes will be tgid+stats
-e) TASKSTATS_TYPE_TGID: contains tgid of process to which task belongs
-f) TASKSTATS_TYPE_STATS: contains the per-tgid stats for exiting task's process
+ a) TASKSTATS_TYPE_AGGR_PID: indicates next two attributes will be pid+stats
+ b) TASKSTATS_TYPE_PID: contains exiting task's pid
+ c) TASKSTATS_TYPE_STATS: contains the exiting task's per-pid stats
+ d) TASKSTATS_TYPE_AGGR_TGID: indicates next two attributes will be
+ tgid+stats
+ e) TASKSTATS_TYPE_TGID: contains tgid of process to which task belongs
+ f) TASKSTATS_TYPE_STATS: contains the per-tgid stats for exiting task's
+ process
per-tgid stats
diff --git a/Documentation/admin-guide/LSM/Smack.rst b/Documentation/admin-guide/LSM/Smack.rst
index 6d44f4fdbf59..c5ed775f2d10 100644
--- a/Documentation/admin-guide/LSM/Smack.rst
+++ b/Documentation/admin-guide/LSM/Smack.rst
@@ -601,10 +601,15 @@ specification.
Task Attribute
~~~~~~~~~~~~~~
-The Smack label of a process can be read from /proc/<pid>/attr/current. A
-process can read its own Smack label from /proc/self/attr/current. A
+The Smack label of a process can be read from ``/proc/<pid>/attr/current``. A
+process can read its own Smack label from ``/proc/self/attr/current``. A
privileged process can change its own Smack label by writing to
-/proc/self/attr/current but not the label of another process.
+``/proc/self/attr/current`` but not the label of another process.
+
+Format of writing is : only the label or the label followed by one of the
+3 trailers: ``\n`` (by common agreement for ``/proc/...`` interfaces),
+``\0`` (because some applications incorrectly include it),
+``\n\0`` (because we think some applications may incorrectly include it).
File Attribute
~~~~~~~~~~~~~~
@@ -696,6 +701,11 @@ sockets.
A privileged program may set this to match the label of another
task with which it hopes to communicate.
+UNIX domain socket (UDS) with a BSD address functions both as a file in a
+filesystem and as a socket. As a file, it carries the SMACK64 attribute. This
+attribute is not involved in Smack security enforcement and is immutably
+assigned the label "*".
+
Smack Netlabel Exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Documentation/admin-guide/LSM/ipe.rst b/Documentation/admin-guide/LSM/ipe.rst
index dc7088451f9d..a756d8158531 100644
--- a/Documentation/admin-guide/LSM/ipe.rst
+++ b/Documentation/admin-guide/LSM/ipe.rst
@@ -95,7 +95,20 @@ languages when these scripts are invoked by passing these program files
to the interpreter. This is because the way interpreters execute these
files; the scripts themselves are not evaluated as executable code
through one of IPE's hooks, but they are merely text files that are read
-(as opposed to compiled executables) [#interpreters]_.
+(as opposed to compiled executables). However, with the introduction of the
+``AT_EXECVE_CHECK`` flag (:doc:`AT_EXECVE_CHECK </userspace-api/check_exec>`),
+interpreters can use it to signal the kernel that a script file will be executed,
+and request the kernel to perform LSM security checks on it.
+
+IPE's EXECUTE operation enforcement differs between compiled executables and
+interpreted scripts: For compiled executables, enforcement is triggered
+automatically by the kernel during ``execve()``, ``execveat()``, ``mmap()``
+and ``mprotect()`` syscalls when loading executable content. For interpreted
+scripts, enforcement requires explicit interpreter integration using
+``execveat()`` with ``AT_EXECVE_CHECK`` flag. Unlike exec syscalls that IPE
+intercepts during the execution process, this mechanism needs the interpreter
+to take the initiative, and existing interpreters won't be automatically
+supported unless the signal call is added.
Threat Model
------------
@@ -806,8 +819,6 @@ A:
.. [#digest_cache_lsm] https://lore.kernel.org/lkml/20240415142436.2545003-1-roberto.sassu@huaweicloud.com/
-.. [#interpreters] There is `some interest in solving this issue <https://lore.kernel.org/lkml/20220321161557.495388-1-mic@digikod.net/>`_.
-
.. [#devdoc] Please see :doc:`the design docs </security/ipe>` for more on
this topic.
diff --git a/Documentation/admin-guide/RAS/main.rst b/Documentation/admin-guide/RAS/main.rst
index 447bfde509fb..5a45db32c49b 100644
--- a/Documentation/admin-guide/RAS/main.rst
+++ b/Documentation/admin-guide/RAS/main.rst
@@ -406,24 +406,8 @@ index of the MC::
|->mc2
....
-Under each ``mcX`` directory each ``csrowX`` is again represented by a
-``csrowX``, where ``X`` is the csrow index::
-
- .../mc/mc0/
- |
- |->csrow0
- |->csrow2
- |->csrow3
- ....
-
-Notice that there is no csrow1, which indicates that csrow0 is composed
-of a single ranked DIMMs. This should also apply in both Channels, in
-order to have dual-channel mode be operational. Since both csrow2 and
-csrow3 are populated, this indicates a dual ranked set of DIMMs for
-channels 0 and 1.
-
-Within each of the ``mcX`` and ``csrowX`` directories are several EDAC
-control and attribute files.
+Within each of the ``mcX`` directory are several EDAC control and
+attribute files.
``mcX`` directories
-------------------
@@ -569,7 +553,7 @@ this ``X`` memory module:
- Unbuffered-DDR
.. [#f5] On some systems, the memory controller doesn't have any logic
- to identify the memory module. On such systems, the directory is called ``rankX`` and works on a similar way as the ``csrowX`` directories.
+ to identify the memory module. On such systems, the directory is called ``rankX``.
On modern Intel memory controllers, the memory controller identifies the
memory modules directly. On such systems, the directory is called ``dimmX``.
@@ -577,126 +561,6 @@ this ``X`` memory module:
symlinks inside the sysfs mapping that are automatically created by
the sysfs subsystem. Currently, they serve no purpose.
-``csrowX`` directories
-----------------------
-
-When CONFIG_EDAC_LEGACY_SYSFS is enabled, sysfs will contain the ``csrowX``
-directories. As this API doesn't work properly for Rambus, FB-DIMMs and
-modern Intel Memory Controllers, this is being deprecated in favor of
-``dimmX`` directories.
-
-In the ``csrowX`` directories are EDAC control and attribute files for
-this ``X`` instance of csrow:
-
-
-- ``ue_count`` - Total Uncorrectable Errors count attribute file
-
- This attribute file displays the total count of uncorrectable
- errors that have occurred on this csrow. If panic_on_ue is set
- this counter will not have a chance to increment, since EDAC
- will panic the system.
-
-
-- ``ce_count`` - Total Correctable Errors count attribute file
-
- This attribute file displays the total count of correctable
- errors that have occurred on this csrow. This count is very
- important to examine. CEs provide early indications that a
- DIMM is beginning to fail. This count field should be
- monitored for non-zero values and report such information
- to the system administrator.
-
-
-- ``size_mb`` - Total memory managed by this csrow attribute file
-
- This attribute file displays, in count of megabytes, the memory
- that this csrow contains.
-
-
-- ``mem_type`` - Memory Type attribute file
-
- This attribute file will display what type of memory is currently
- on this csrow. Normally, either buffered or unbuffered memory.
- Examples:
-
- - Registered-DDR
- - Unbuffered-DDR
-
-
-- ``edac_mode`` - EDAC Mode of operation attribute file
-
- This attribute file will display what type of Error detection
- and correction is being utilized.
-
-
-- ``dev_type`` - Device type attribute file
-
- This attribute file will display what type of DRAM device is
- being utilized on this DIMM.
- Examples:
-
- - x1
- - x2
- - x4
- - x8
-
-
-- ``ch0_ce_count`` - Channel 0 CE Count attribute file
-
- This attribute file will display the count of CEs on this
- DIMM located in channel 0.
-
-
-- ``ch0_ue_count`` - Channel 0 UE Count attribute file
-
- This attribute file will display the count of UEs on this
- DIMM located in channel 0.
-
-
-- ``ch0_dimm_label`` - Channel 0 DIMM Label control file
-
-
- This control file allows this DIMM to have a label assigned
- to it. With this label in the module, when errors occur
- the output can provide the DIMM label in the system log.
- This becomes vital for panic events to isolate the
- cause of the UE event.
-
- DIMM Labels must be assigned after booting, with information
- that correctly identifies the physical slot with its
- silk screen label. This information is currently very
- motherboard specific and determination of this information
- must occur in userland at this time.
-
-
-- ``ch1_ce_count`` - Channel 1 CE Count attribute file
-
-
- This attribute file will display the count of CEs on this
- DIMM located in channel 1.
-
-
-- ``ch1_ue_count`` - Channel 1 UE Count attribute file
-
-
- This attribute file will display the count of UEs on this
- DIMM located in channel 0.
-
-
-- ``ch1_dimm_label`` - Channel 1 DIMM Label control file
-
- This control file allows this DIMM to have a label assigned
- to it. With this label in the module, when errors occur
- the output can provide the DIMM label in the system log.
- This becomes vital for panic events to isolate the
- cause of the UE event.
-
- DIMM Labels must be assigned after booting, with information
- that correctly identifies the physical slot with its
- silk screen label. This information is currently very
- motherboard specific and determination of this information
- must occur in userland at this time.
-
System Logging
--------------
diff --git a/Documentation/admin-guide/bcache.rst b/Documentation/admin-guide/bcache.rst
index 6fdb495ac466..f71f349553e4 100644
--- a/Documentation/admin-guide/bcache.rst
+++ b/Documentation/admin-guide/bcache.rst
@@ -17,8 +17,7 @@ The latest bcache kernel code can be found from mainline Linux kernel:
It's designed around the performance characteristics of SSDs - it only allocates
in erase block sized buckets, and it uses a hybrid btree/log to track cached
extents (which can be anywhere from a single sector to the bucket size). It's
-designed to avoid random writes at all costs; it fills up an erase block
-sequentially, then issues a discard before reusing it.
+designed to avoid random writes at all costs.
Both writethrough and writeback caching are supported. Writeback defaults to
off, but can be switched on and off arbitrarily at runtime. Bcache goes to
@@ -618,19 +617,11 @@ bucket_size
cache_replacement_policy
One of either lru, fifo or random.
-discard
- Boolean; if on a discard/TRIM will be issued to each bucket before it is
- reused. Defaults to off, since SATA TRIM is an unqueued command (and thus
- slow).
-
freelist_percent
Size of the freelist as a percentage of nbuckets. Can be written to to
increase the number of buckets kept on the freelist, which lets you
artificially reduce the size of the cache at runtime. Mostly for testing
- purposes (i.e. testing how different size caches affect your hit rate), but
- since buckets are discarded when they move on to the freelist will also make
- the SSD's garbage collection easier by effectively giving it more reserved
- space.
+ purposes (i.e. testing how different size caches affect your hit rate).
io_errors
Number of errors that have occurred, decayed by io_error_halflife.
diff --git a/Documentation/admin-guide/blockdev/zoned_loop.rst b/Documentation/admin-guide/blockdev/zoned_loop.rst
index 64dcfde7450a..806adde664db 100644
--- a/Documentation/admin-guide/blockdev/zoned_loop.rst
+++ b/Documentation/admin-guide/blockdev/zoned_loop.rst
@@ -68,30 +68,43 @@ The options available for the add command can be listed by reading the
In more details, the options that can be used with the "add" command are as
follows.
-================ ===========================================================
-id Device number (the X in /dev/zloopX).
- Default: automatically assigned.
-capacity_mb Device total capacity in MiB. This is always rounded up to
- the nearest higher multiple of the zone size.
- Default: 16384 MiB (16 GiB).
-zone_size_mb Device zone size in MiB. Default: 256 MiB.
-zone_capacity_mb Device zone capacity (must always be equal to or lower than
- the zone size. Default: zone size.
-conv_zones Total number of conventioanl zones starting from sector 0.
- Default: 8.
-base_dir Path to the base directory where to create the directory
- containing the zone files of the device.
- Default=/var/local/zloop.
- The device directory containing the zone files is always
- named with the device ID. E.g. the default zone file
- directory for /dev/zloop0 is /var/local/zloop/0.
-nr_queues Number of I/O queues of the zoned block device. This value is
- always capped by the number of online CPUs
- Default: 1
-queue_depth Maximum I/O queue depth per I/O queue.
- Default: 64
-buffered_io Do buffered IOs instead of direct IOs (default: false)
-================ ===========================================================
+=================== =========================================================
+id Device number (the X in /dev/zloopX).
+ Default: automatically assigned.
+capacity_mb Device total capacity in MiB. This is always rounded up
+ to the nearest higher multiple of the zone size.
+ Default: 16384 MiB (16 GiB).
+zone_size_mb Device zone size in MiB. Default: 256 MiB.
+zone_capacity_mb Device zone capacity (must always be equal to or lower
+ than the zone size. Default: zone size.
+conv_zones Total number of conventioanl zones starting from
+ sector 0
+ Default: 8
+base_dir Path to the base directory where to create the directory
+ containing the zone files of the device.
+ Default=/var/local/zloop.
+ The device directory containing the zone files is always
+ named with the device ID. E.g. the default zone file
+ directory for /dev/zloop0 is /var/local/zloop/0.
+nr_queues Number of I/O queues of the zoned block device. This
+ value is always capped by the number of online CPUs
+ Default: 1
+queue_depth Maximum I/O queue depth per I/O queue.
+ Default: 64
+buffered_io Do buffered IOs instead of direct IOs (default: false)
+zone_append Enable or disable a zloop device native zone append
+ support.
+ Default: 1 (enabled).
+ If native zone append support is disabled, the block layer
+ will emulate this operation using regular write
+ operations.
+ordered_zone_append Enable zloop mitigation of zone append reordering.
+ Default: disabled.
+ This is useful for testing file systems file data mapping
+ (extents), as when enabled, this can significantly reduce
+ the number of data extents needed to for a file data
+ mapping.
+=================== =========================================================
3) Deleting a Zoned Device
--------------------------
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index 0e6c67ac585a..4c072e85acdf 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -53,7 +53,8 @@ v1 is available under :ref:`Documentation/admin-guide/cgroup-v1/index.rst <cgrou
5-2. Memory
5-2-1. Memory Interface Files
5-2-2. Usage Guidelines
- 5-2-3. Memory Ownership
+ 5-2-3. Reclaim Protection
+ 5-2-4. Memory Ownership
5-3. IO
5-3-1. IO Interface Files
5-3-2. Writeback
@@ -1317,7 +1318,7 @@ PAGE_SIZE multiple when read back.
smaller overages.
Effective min boundary is limited by memory.min values of
- all ancestor cgroups. If there is memory.min overcommitment
+ ancestor cgroups. If there is memory.min overcommitment
(child cgroup or cgroups are requiring more protected memory
than parent will allow), then each child cgroup will get
the part of parent's protection proportional to its
@@ -1326,9 +1327,6 @@ PAGE_SIZE multiple when read back.
Putting more memory than generally available under this
protection is discouraged and may lead to constant OOMs.
- If a memory cgroup is not populated with processes,
- its memory.min is ignored.
-
memory.low
A read-write single value file which exists on non-root
cgroups. The default is "0".
@@ -1343,7 +1341,7 @@ PAGE_SIZE multiple when read back.
smaller overages.
Effective low boundary is limited by memory.low values of
- all ancestor cgroups. If there is memory.low overcommitment
+ ancestor cgroups. If there is memory.low overcommitment
(child cgroup or cgroups are requiring more protected memory
than parent will allow), then each child cgroup will get
the part of parent's protection proportional to its
@@ -1934,6 +1932,27 @@ memory - is necessary to determine whether a workload needs more
memory; unfortunately, memory pressure monitoring mechanism isn't
implemented yet.
+Reclaim Protection
+~~~~~~~~~~~~~~~~~~
+
+The protection configured with "memory.low" or "memory.min" applies relatively
+to the target of the reclaim (i.e. any of memory cgroup limits, proactive
+memory.reclaim or global reclaim apparently located in the root cgroup).
+The protection value configured for B applies unchanged to the reclaim
+targeting A (i.e. caused by competition with the sibling E)::
+
+ root - ... - A - B - C
+ \ ` D
+ ` E
+
+When the reclaim targets ancestors of A, the effective protection of B is
+capped by the protection value configured for A (and any other intermediate
+ancestors between A and the target).
+
+To express indifference about relative sibling protection, it is suggested to
+use memory_recursiveprot. Configuring all descendants of a parent with finite
+protection to "max" works but it may unnecessarily skew memory.events:low
+field.
Memory Ownership
~~~~~~~~~~~~~~~~
diff --git a/Documentation/admin-guide/efi-stub.rst b/Documentation/admin-guide/efi-stub.rst
index 090f3a185e18..f8e7407698bd 100644
--- a/Documentation/admin-guide/efi-stub.rst
+++ b/Documentation/admin-guide/efi-stub.rst
@@ -79,6 +79,9 @@ because the image we're executing is interpreted by the EFI shell,
which understands relative paths, whereas the rest of the command line
is passed to bzImage.efi.
+.. hint::
+ It is also possible to provide an initrd using a Linux-specific UEFI
+ protocol at boot time. See :ref:`pe-coff-entry-point` for details.
The "dtb=" option
-----------------
diff --git a/Documentation/admin-guide/hw-vuln/l1d_flush.rst b/Documentation/admin-guide/hw-vuln/l1d_flush.rst
index 210020bc3f56..35dc25159b28 100644
--- a/Documentation/admin-guide/hw-vuln/l1d_flush.rst
+++ b/Documentation/admin-guide/hw-vuln/l1d_flush.rst
@@ -31,7 +31,7 @@ specifically opt into the feature to enable it.
Mitigation
----------
-When PR_SET_L1D_FLUSH is enabled for a task a flush of the L1D cache is
+When PR_SPEC_L1D_FLUSH is enabled for a task a flush of the L1D cache is
performed when the task is scheduled out and the incoming task belongs to a
different process and therefore to a different address space.
diff --git a/Documentation/admin-guide/hw-vuln/spectre.rst b/Documentation/admin-guide/hw-vuln/spectre.rst
index 991f12adef8d..4bb8549bee82 100644
--- a/Documentation/admin-guide/hw-vuln/spectre.rst
+++ b/Documentation/admin-guide/hw-vuln/spectre.rst
@@ -406,7 +406,7 @@ The possible values in this file are:
- Single threaded indirect branch prediction (STIBP) status for protection
between different hyper threads. This feature can be controlled through
- prctl per process, or through kernel command line options. This is x86
+ prctl per process, or through kernel command line options. This is an x86
only feature. For more details see below.
==================== ========================================================
diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst
index 7bf8cc7df6b5..02a725536cc5 100644
--- a/Documentation/admin-guide/kernel-parameters.rst
+++ b/Documentation/admin-guide/kernel-parameters.rst
@@ -110,102 +110,7 @@ The parameters listed below are only valid if certain kernel build options
were enabled and if respective hardware is present. This list should be kept
in alphabetical order. The text in square brackets at the beginning
of each description states the restrictions within which a parameter
-is applicable::
-
- ACPI ACPI support is enabled.
- AGP AGP (Accelerated Graphics Port) is enabled.
- ALSA ALSA sound support is enabled.
- APIC APIC support is enabled.
- APM Advanced Power Management support is enabled.
- APPARMOR AppArmor support is enabled.
- ARM ARM architecture is enabled.
- ARM64 ARM64 architecture is enabled.
- AX25 Appropriate AX.25 support is enabled.
- CLK Common clock infrastructure is enabled.
- CMA Contiguous Memory Area support is enabled.
- DRM Direct Rendering Management support is enabled.
- DYNAMIC_DEBUG Build in debug messages and enable them at runtime
- EARLY Parameter processed too early to be embedded in initrd.
- EDD BIOS Enhanced Disk Drive Services (EDD) is enabled
- EFI EFI Partitioning (GPT) is enabled
- EVM Extended Verification Module
- FB The frame buffer device is enabled.
- FTRACE Function tracing enabled.
- GCOV GCOV profiling is enabled.
- HIBERNATION HIBERNATION is enabled.
- HW Appropriate hardware is enabled.
- HYPER_V HYPERV support is enabled.
- IMA Integrity measurement architecture is enabled.
- IP_PNP IP DHCP, BOOTP, or RARP is enabled.
- IPV6 IPv6 support is enabled.
- ISAPNP ISA PnP code is enabled.
- ISDN Appropriate ISDN support is enabled.
- ISOL CPU Isolation is enabled.
- JOY Appropriate joystick support is enabled.
- KGDB Kernel debugger support is enabled.
- KVM Kernel Virtual Machine support is enabled.
- LIBATA Libata driver is enabled
- LOONGARCH LoongArch architecture is enabled.
- LOOP Loopback device support is enabled.
- LP Printer support is enabled.
- M68k M68k architecture is enabled.
- These options have more detailed description inside of
- Documentation/arch/m68k/kernel-options.rst.
- MDA MDA console support is enabled.
- MIPS MIPS architecture is enabled.
- MOUSE Appropriate mouse support is enabled.
- MSI Message Signaled Interrupts (PCI).
- MTD MTD (Memory Technology Device) support is enabled.
- NET Appropriate network support is enabled.
- NFS Appropriate NFS support is enabled.
- NUMA NUMA support is enabled.
- OF Devicetree is enabled.
- PARISC The PA-RISC architecture is enabled.
- PCI PCI bus support is enabled.
- PCIE PCI Express support is enabled.
- PCMCIA The PCMCIA subsystem is enabled.
- PNP Plug & Play support is enabled.
- PPC PowerPC architecture is enabled.
- PPT Parallel port support is enabled.
- PS2 Appropriate PS/2 support is enabled.
- PV_OPS A paravirtualized kernel is enabled.
- RAM RAM disk support is enabled.
- RDT Intel Resource Director Technology.
- RISCV RISCV architecture is enabled.
- S390 S390 architecture is enabled.
- SCSI Appropriate SCSI support is enabled.
- A lot of drivers have their options described inside
- the Documentation/scsi/ sub-directory.
- SDW SoundWire support is enabled.
- SECURITY Different security models are enabled.
- SELINUX SELinux support is enabled.
- SERIAL Serial support is enabled.
- SH SuperH architecture is enabled.
- SMP The kernel is an SMP kernel.
- SPARC Sparc architecture is enabled.
- SUSPEND System suspend states are enabled.
- SWSUSP Software suspend (hibernation) is enabled.
- TPM TPM drivers are enabled.
- UMS USB Mass Storage support is enabled.
- USB USB support is enabled.
- USBHID USB Human Interface Device support is enabled.
- V4L Video For Linux support is enabled.
- VGA The VGA console has been enabled.
- VMMIO Driver for memory mapped virtio devices is enabled.
- VT Virtual terminal support is enabled.
- WDT Watchdog support is enabled.
- X86-32 X86-32, aka i386 architecture is enabled.
- X86-64 X86-64 architecture is enabled.
- X86 Either 32-bit or 64-bit x86 (same as X86-32+X86-64)
- X86_UV SGI UV support is enabled.
- XEN Xen support is enabled
- XTENSA xtensa architecture is enabled.
-
-In addition, the following text indicates that the option::
-
- BOOT Is a boot loader parameter.
- BUGS= Relates to possible processor bugs on the said processor.
- KNL Is a kernel start-up parameter.
+is applicable.
Parameters denoted with BOOT are actually interpreted by the boot
loader, and have no meaning to the kernel directly.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 6c42061ca20e..1e89d122f084 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1,3 +1,101 @@
+ ACPI ACPI support is enabled.
+ AGP AGP (Accelerated Graphics Port) is enabled.
+ ALSA ALSA sound support is enabled.
+ APIC APIC support is enabled.
+ APM Advanced Power Management support is enabled.
+ APPARMOR AppArmor support is enabled.
+ ARM ARM architecture is enabled.
+ ARM64 ARM64 architecture is enabled.
+ AX25 Appropriate AX.25 support is enabled.
+ CLK Common clock infrastructure is enabled.
+ CMA Contiguous Memory Area support is enabled.
+ DRM Direct Rendering Management support is enabled.
+ DYNAMIC_DEBUG Build in debug messages and enable them at runtime
+ EARLY Parameter processed too early to be embedded in initrd.
+ EDD BIOS Enhanced Disk Drive Services (EDD) is enabled
+ EFI EFI Partitioning (GPT) is enabled
+ EVM Extended Verification Module
+ FB The frame buffer device is enabled.
+ FTRACE Function tracing enabled.
+ GCOV GCOV profiling is enabled.
+ HIBERNATION HIBERNATION is enabled.
+ HW Appropriate hardware is enabled.
+ HYPER_V HYPERV support is enabled.
+ IMA Integrity measurement architecture is enabled.
+ IP_PNP IP DHCP, BOOTP, or RARP is enabled.
+ IPV6 IPv6 support is enabled.
+ ISAPNP ISA PnP code is enabled.
+ ISDN Appropriate ISDN support is enabled.
+ ISOL CPU Isolation is enabled.
+ JOY Appropriate joystick support is enabled.
+ KGDB Kernel debugger support is enabled.
+ KVM Kernel Virtual Machine support is enabled.
+ LIBATA Libata driver is enabled
+ LOONGARCH LoongArch architecture is enabled.
+ LOOP Loopback device support is enabled.
+ LP Printer support is enabled.
+ M68k M68k architecture is enabled.
+ These options have more detailed description inside of
+ Documentation/arch/m68k/kernel-options.rst.
+ MDA MDA console support is enabled.
+ MIPS MIPS architecture is enabled.
+ MOUSE Appropriate mouse support is enabled.
+ MSI Message Signaled Interrupts (PCI).
+ MTD MTD (Memory Technology Device) support is enabled.
+ NET Appropriate network support is enabled.
+ NFS Appropriate NFS support is enabled.
+ NUMA NUMA support is enabled.
+ OF Devicetree is enabled.
+ PARISC The PA-RISC architecture is enabled.
+ PCI PCI bus support is enabled.
+ PCIE PCI Express support is enabled.
+ PCMCIA The PCMCIA subsystem is enabled.
+ PNP Plug & Play support is enabled.
+ PPC PowerPC architecture is enabled.
+ PPT Parallel port support is enabled.
+ PS2 Appropriate PS/2 support is enabled.
+ PV_OPS A paravirtualized kernel is enabled.
+ RAM RAM disk support is enabled.
+ RDT Intel Resource Director Technology.
+ RISCV RISCV architecture is enabled.
+ S390 S390 architecture is enabled.
+ SCSI Appropriate SCSI support is enabled.
+ A lot of drivers have their options described inside
+ the Documentation/scsi/ sub-directory.
+ SDW SoundWire support is enabled.
+ SECURITY Different security models are enabled.
+ SELINUX SELinux support is enabled.
+ SERIAL Serial support is enabled.
+ SH SuperH architecture is enabled.
+ SMP The kernel is an SMP kernel.
+ SPARC Sparc architecture is enabled.
+ SUSPEND System suspend states are enabled.
+ SWSUSP Software suspend (hibernation) is enabled.
+ TPM TPM drivers are enabled.
+ UMS USB Mass Storage support is enabled.
+ USB USB support is enabled.
+ USBHID USB Human Interface Device support is enabled.
+ V4L Video For Linux support is enabled.
+ VGA The VGA console has been enabled.
+ VMMIO Driver for memory mapped virtio devices is enabled.
+ VT Virtual terminal support is enabled.
+ WDT Watchdog support is enabled.
+ X86-32 X86-32, aka i386 architecture is enabled.
+ X86-64 X86-64 architecture is enabled.
+ X86 Either 32-bit or 64-bit x86 (same as X86-32+X86-64)
+ X86_UV SGI UV support is enabled.
+ XEN Xen support is enabled
+ XTENSA xtensa architecture is enabled.
+
+In addition, the following text indicates that the option
+
+ BOOT Is a boot loader parameter.
+ BUGS= Relates to possible processor bugs on the said processor.
+ KNL Is a kernel start-up parameter.
+
+
+Kernel parameters
+
accept_memory= [MM]
Format: { eager | lazy }
default: lazy
@@ -1907,6 +2005,16 @@
/sys/power/pm_test). Only available when CONFIG_PM_DEBUG
is set. Default value is 5.
+ hibernate_compression_threads=
+ [HIBERNATION]
+ Set the number of threads used for compressing or decompressing
+ hibernation images.
+
+ Format: <integer>
+ Default: 3
+ Minimum: 1
+ Example: hibernate_compression_threads=4
+
highmem=nn[KMG] [KNL,BOOT,EARLY] forces the highmem zone to have an exact
size of <nn>. This works even on boxes that have no
highmem otherwise. This also works to reduce highmem
@@ -6207,7 +6315,7 @@
rdt= [HW,X86,RDT]
Turn on/off individual RDT features. List is:
cmt, mbmtotal, mbmlocal, l3cat, l3cdp, l2cat, l2cdp,
- mba, smba, bmec, abmc.
+ mba, smba, bmec, abmc, sdciae.
E.g. to turn on cmt and turn off mba use:
rdt=cmt,!mba
@@ -6404,7 +6512,7 @@
that don't.
off - no mitigation
- auto - automatically select a migitation
+ auto - automatically select a mitigation
auto,nosmt - automatically select a mitigation,
disabling SMT if necessary for
the full mitigation (only on Zen1
@@ -6500,6 +6608,10 @@
Memory area to be used by remote processor image,
managed by CMA.
+ rseq_debug= [KNL] Enable or disable restartable sequence
+ debug mode. Defaults to CONFIG_RSEQ_DEBUG_DEFAULT_ENABLE.
+ Format: <bool>
+
rt_group_sched= [KNL] Enable or disable SCHED_RR/FIFO group scheduling
when CONFIG_RT_GROUP_SCHED=y. Defaults to
!CONFIG_RT_GROUP_SCHED_DEFAULT_DISABLED.
@@ -7150,7 +7262,7 @@
limit. Default value is 8191 pools.
stacktrace [FTRACE]
- Enabled the stack tracer on boot up.
+ Enable the stack tracer on boot up.
stacktrace_filter=[function-list]
[FTRACE] Limit the functions that the stack tracer
diff --git a/Documentation/admin-guide/md.rst b/Documentation/admin-guide/md.rst
index deed823eab01..dc7eab191caa 100644
--- a/Documentation/admin-guide/md.rst
+++ b/Documentation/admin-guide/md.rst
@@ -238,6 +238,16 @@ All md devices contain:
the number of devices in a raid4/5/6, or to support external
metadata formats which mandate such clipping.
+ logical_block_size
+ Configure the array's logical block size in bytes. This attribute
+ is only supported for 1.x meta. Write the value before starting
+ array. The final array LBS uses the maximum between this
+ configuration and LBS of all combined devices. Note that
+ LBS cannot exceed PAGE_SIZE before RAID supports folio.
+ WARNING: Arrays created on new kernel cannot be assembled at old
+ kernel due to padding check, Set module parameter 'check_new_feature'
+ to false to bypass, but data loss may occur.
+
reshape_position
This is either ``none`` or a sector number within the devices of
the array where ``reshape`` is up to. If this is set, the three
diff --git a/Documentation/admin-guide/media/mali-c55-graph.dot b/Documentation/admin-guide/media/mali-c55-graph.dot
new file mode 100644
index 000000000000..0775ba42bf4c
--- /dev/null
+++ b/Documentation/admin-guide/media/mali-c55-graph.dot
@@ -0,0 +1,19 @@
+digraph board {
+ rankdir=TB
+ n00000001 [label="{{} | mali-c55 tpg\n/dev/v4l-subdev0 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000001:port0 -> n00000003:port0 [style=dashed]
+ n00000003 [label="{{<port0> 0} | mali-c55 isp\n/dev/v4l-subdev1 | {<port1> 1 | <port2> 2}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000003:port1 -> n00000007:port0 [style=bold]
+ n00000003:port2 -> n00000007:port2 [style=bold]
+ n00000003:port1 -> n0000000b:port0 [style=bold]
+ n00000007 [label="{{<port0> 0 | <port2> 2} | mali-c55 resizer fr\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000007:port1 -> n0000000e [style=bold]
+ n0000000b [label="{{<port0> 0} | mali-c55 resizer ds\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n0000000b:port1 -> n00000012 [style=bold]
+ n0000000e [label="mali-c55 fr\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
+ n00000012 [label="mali-c55 ds\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
+ n00000022 [label="{{<port0> 0} | csi2-rx\n/dev/v4l-subdev4 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000022:port1 -> n00000003:port0
+ n00000027 [label="{{} | imx415 1-001a\n/dev/v4l-subdev5 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000027:port0 -> n00000022:port0 [style=bold]
+} \ No newline at end of file
diff --git a/Documentation/admin-guide/media/mali-c55.rst b/Documentation/admin-guide/media/mali-c55.rst
new file mode 100644
index 000000000000..315f982000c4
--- /dev/null
+++ b/Documentation/admin-guide/media/mali-c55.rst
@@ -0,0 +1,413 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==========================================
+ARM Mali-C55 Image Signal Processor driver
+==========================================
+
+Introduction
+============
+
+This file documents the driver for ARM's Mali-C55 Image Signal Processor. The
+driver is located under drivers/media/platform/arm/mali-c55.
+
+The Mali-C55 ISP receives data in either raw Bayer format or RGB/YUV format from
+sensors through either a parallel interface or a memory bus before processing it
+and outputting it through an internal DMA engine. Two output pipelines are
+possible (though one may not be fitted, depending on the implementation). These
+are referred to as "Full resolution" and "Downscale", but the naming is historic
+and both pipes are capable of cropping/scaling operations. The full resolution
+pipe is also capable of outputting RAW data, bypassing much of the ISP's
+processing. The downscale pipe cannot output RAW data. An integrated test
+pattern generator can be used to drive the ISP and produce image data in the
+absence of a connected camera sensor. The driver module is named mali_c55, and
+is enabled through the CONFIG_VIDEO_MALI_C55 config option.
+
+The driver implements V4L2, Media Controller and V4L2 Subdevice interfaces and
+expects camera sensors connected to the ISP to have V4L2 subdevice interfaces.
+
+Mali-C55 ISP hardware
+=====================
+
+A high level functional view of the Mali-C55 ISP is presented below. The ISP
+takes input from either a live source or through a DMA engine for memory input,
+depending on the SoC integration.::
+
+ +---------+ +----------+ +--------+
+ | Sensor |--->| CSI-2 Rx | "Full Resolution" | DMA |
+ +---------+ +----------+ |\ Output +--->| Writer |
+ | | \ | +--------+
+ | | \ +----------+ +------+---> Streaming I/O
+ +------------+ +------->| | | | |
+ | | | |-->| Mali-C55 |--+
+ | DMA Reader |--------------->| | | ISP | |
+ | | | / | | | +---> Streaming I/O
+ +------------+ | / +----------+ | |
+ |/ +------+
+ | +--------+
+ +--->| DMA |
+ "Downscaled" | Writer |
+ Output +--------+
+
+Media Controller Topology
+=========================
+
+An example of the ISP's topology (as implemented in a system with an IMX415
+camera sensor and generic CSI-2 receiver) is below:
+
+
+.. kernel-figure:: mali-c55-graph.dot
+ :alt: mali-c55-graph.dot
+ :align: center
+
+The driver has 4 V4L2 subdevices:
+
+- `mali_c55 isp`: Responsible for configuring input crop and color space
+ conversion
+- `mali_c55 tpg`: The test pattern generator, emulating a camera sensor.
+- `mali_c55 resizer fr`: The Full-Resolution pipe resizer
+- `mali_c55 resizer ds`: The Downscale pipe resizer
+
+The driver has 3 V4L2 video devices:
+
+- `mali-c55 fr`: The full-resolution pipe's capture device
+- `mali-c55 ds`: The downscale pipe's capture device
+- `mali-c55 3a stats`: The 3A statistics capture device
+
+Frame sequences are synchronised across to two capture devices, meaning if one
+pipe is started later than the other the sequence numbers returned in its
+buffers will match those of the other pipe rather than starting from zero.
+
+Idiosyncrasies
+--------------
+
+**mali-c55 isp**
+The `mali-c55 isp` subdevice has a single sink pad to which all sources of data
+should be connected. The active source is selected by enabling the appropriate
+media link and disabling all others. The ISP has two source pads, reflecting the
+different paths through which it can internally route data. Tap points within
+the ISP allow users to divert data to avoid processing by some or all of the
+hardware's processing steps. The diagram below is intended only to highlight how
+the bypassing works and is not a true reflection of those processing steps; for
+a high-level functional block diagram see ARM's developer page for the
+ISP [3]_::
+
+ +--------------------------------------------------------------+
+ | Possible Internal ISP Data Routes |
+ | +------------+ +----------+ +------------+ |
+ +---+ | | | | | Colour | +---+
+ | 0 |--+-->| Processing |->| Demosaic |->| Space |--->| 1 |
+ +---+ | | | | | | Conversion | +---+
+ | | +------------+ +----------+ +------------+ |
+ | | +---+
+ | +---------------------------------------------------| 2 |
+ | +---+
+ | |
+ +--------------------------------------------------------------+
+
+
+.. flat-table::
+ :header-rows: 1
+
+ * - Pad
+ - Direction
+ - Purpose
+
+ * - 0
+ - sink
+ - Data input, connected to the TPG and camera sensors
+
+ * - 1
+ - source
+ - RGB/YUV data, connected to the FR and DS V4L2 subdevices
+
+ * - 2
+ - source
+ - RAW bayer data, connected to the FR V4L2 subdevices
+
+The ISP is limited to both input and output resolutions between 640x480 and
+8192x8192, and this is reflected in the ISP and resizer subdevice's .set_fmt()
+operations.
+
+**mali-c55 resizer fr**
+The `mali-c55 resizer fr` subdevice has two _sink_ pads to reflect the different
+insertion points in the hardware (either RAW or demosaiced data):
+
+.. flat-table::
+ :header-rows: 1
+
+ * - Pad
+ - Direction
+ - Purpose
+
+ * - 0
+ - sink
+ - Data input connected to the ISP's demosaiced stream.
+
+ * - 1
+ - source
+ - Data output connected to the capture video device
+
+ * - 2
+ - sink
+ - Data input connected to the ISP's raw data stream
+
+The data source in use is selected through the routing API; two routes each of a
+single stream are available:
+
+.. flat-table::
+ :header-rows: 1
+
+ * - Sink Pad
+ - Source Pad
+ - Purpose
+
+ * - 0
+ - 1
+ - Demosaiced data route
+
+ * - 2
+ - 1
+ - Raw data route
+
+
+If the demosaiced route is active then the FR pipe is only capable of output
+in RGB/YUV formats. If the raw route is active then the output reflects the
+input (which may be either Bayer or RGB/YUV data).
+
+Using the driver to capture video
+=================================
+
+Using the media controller APIs we can configure the input source and ISP to
+capture images in a variety of formats. In the examples below, configuring the
+media graph is done with the v4l-utils [1]_ package's media-ctl utility.
+Capturing the images is done with yavta [2]_.
+
+Configuring the input source
+----------------------------
+
+The first step is to set the input source that we wish by enabling the correct
+media link. Using the example topology above, we can select the TPG as follows:
+
+.. code-block:: none
+
+ media-ctl -l "'lte-csi2-rx':1->'mali-c55 isp':0[0]"
+ media-ctl -l "'mali-c55 tpg':0->'mali-c55 isp':0[1]"
+
+Configuring which video devices will stream data
+------------------------------------------------
+
+The driver will wait for all video devices to have their VIDIOC_STREAMON ioctl
+called before it tells the sensor to start streaming. To facilitate this we need
+to enable links to the video devices that we want to use. In the example below
+we enable the links to both of the image capture video devices
+
+.. code-block:: none
+
+ media-ctl -l "'mali-c55 resizer fr':1->'mali-c55 fr':0[1]"
+ media-ctl -l "'mali-c55 resizer ds':1->'mali-c55 ds':0[1]"
+
+Capturing bayer data from the source and processing to RGB/YUV
+--------------------------------------------------------------
+
+To capture 1920x1080 bayer data from the source and push it through the ISP's
+full processing pipeline, we configure the data formats appropriately on the
+source, ISP and resizer subdevices and set the FR resizer's routing to select
+processed data. The media bus format on the resizer's source pad will be either
+RGB121212_1X36 or YUV10_1X30, depending on whether you want to capture RGB or
+YUV. The ISP's debayering block outputs RGB data natively, setting the source
+pad format to YUV10_1X30 enables the colour space conversion block.
+
+In this example we target RGB565 output, so select RGB121212_1X36 as the resizer
+source pad's format:
+
+.. code-block:: none
+
+ # Set formats on the TPG and ISP
+ media-ctl -V "'mali-c55 tpg':0[fmt:SRGGB20_1X20/1920x1080]"
+ media-ctl -V "'mali-c55 isp':0[fmt:SRGGB20_1X20/1920x1080]"
+ media-ctl -V "'mali-c55 isp':1[fmt:SRGGB20_1X20/1920x1080]"
+
+ # Set routing on the FR resizer
+ media-ctl -R "'mali-c55 resizer fr'[0/0->1/0[1],2/0->1/0[0]]"
+
+ # Set format on the resizer, must be done AFTER the routing.
+ media-ctl -V "'mali-c55 resizer fr':1[fmt:RGB121212_1X36/1920x1080]"
+
+The downscale output can also be used to stream data at the same time. In this
+case since only processed data can be captured through the downscale output no
+routing need be set:
+
+.. code-block:: none
+
+ # Set format on the resizer
+ media-ctl -V "'mali-c55 resizer ds':1[fmt:RGB121212_1X36/1920x1080]"
+
+Following which images can be captured from both the FR and DS output's video
+devices (simultaneously, if desired):
+
+.. code-block:: none
+
+ yavta -f RGB565 -s 1920x1080 -c10 /dev/video0
+ yavta -f RGB565 -s 1920x1080 -c10 /dev/video1
+
+Cropping the image
+~~~~~~~~~~~~~~~~~~
+
+Both the full resolution and downscale pipes can crop to a minimum resolution of
+640x480. To crop the image simply configure the resizer's sink pad's crop and
+compose rectangles and set the format on the video device:
+
+.. code-block:: none
+
+ media-ctl -V "'mali-c55 resizer fr':0[fmt:RGB121212_1X36/1920x1080 crop:(480,270)/640x480 compose:(0,0)/640x480]"
+ media-ctl -V "'mali-c55 resizer fr':1[fmt:RGB121212_1X36/640x480]"
+ yavta -f RGB565 -s 640x480 -c10 /dev/video0
+
+Downscaling the image
+~~~~~~~~~~~~~~~~~~~~~
+
+Both the full resolution and downscale pipes can downscale the image by up to 8x
+provided the minimum 640x480 output resolution is adhered to. For the best image
+result the scaling ratio for each direction should be the same. To configure
+scaling we use the compose rectangle on the resizer's sink pad:
+
+.. code-block:: none
+
+ media-ctl -V "'mali-c55 resizer fr':0[fmt:RGB121212_1X36/1920x1080 crop:(0,0)/1920x1080 compose:(0,0)/640x480]"
+ media-ctl -V "'mali-c55 resizer fr':1[fmt:RGB121212_1X36/640x480]"
+ yavta -f RGB565 -s 640x480 -c10 /dev/video0
+
+Capturing images in YUV formats
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If we need to output YUV data rather than RGB the color space conversion block
+needs to be active, which is achieved by setting MEDIA_BUS_FMT_YUV10_1X30 on the
+resizer's source pad. We can then configure a capture format like NV12 (here in
+its multi-planar variant)
+
+.. code-block:: none
+
+ media-ctl -V "'mali-c55 resizer fr':1[fmt:YUV10_1X30/1920x1080]"
+ yavta -f NV12M -s 1920x1080 -c10 /dev/video0
+
+Capturing RGB data from the source and processing it with the resizers
+----------------------------------------------------------------------
+
+The Mali-C55 ISP can work with sensors capable of outputting RGB data. In this
+case although none of the image quality blocks would be used it can still
+crop/scale the data in the usual way. For this reason RGB data input to the ISP
+still goes through the ISP subdevice's pad 1 to the resizer.
+
+To achieve this, the ISP's sink pad's format is set to
+MEDIA_BUS_FMT_RGB202020_1X60 - this reflects the format that data must be in to
+work with the ISP. Converting the camera sensor's output to that format is the
+responsibility of external hardware.
+
+In this example we ask the test pattern generator to give us RGB data instead of
+bayer.
+
+.. code-block:: none
+
+ media-ctl -V "'mali-c55 tpg':0[fmt:RGB202020_1X60/1920x1080]"
+ media-ctl -V "'mali-c55 isp':0[fmt:RGB202020_1X60/1920x1080]"
+
+Cropping or scaling the data can be done in exactly the same way as outlined
+earlier.
+
+Capturing raw data from the source and outputting it unmodified
+-----------------------------------------------------------------
+
+The ISP can additionally capture raw data from the source and output it on the
+full resolution pipe only, completely unmodified. In this case the downscale
+pipe can still process the data normally and be used at the same time.
+
+To configure raw bypass the FR resizer's subdevice's routing table needs to be
+configured, followed by formats in the appropriate places:
+
+.. code-block:: none
+
+ media-ctl -R "'mali-c55 resizer fr'[0/0->1/0[0],2/0->1/0[1]]"
+ media-ctl -V "'mali-c55 isp':0[fmt:RGB202020_1X60/1920x1080]"
+ media-ctl -V "'mali-c55 resizer fr':2[fmt:RGB202020_1X60/1920x1080]"
+ media-ctl -V "'mali-c55 resizer fr':1[fmt:RGB202020_1X60/1920x1080]"
+
+ # Set format on the video device and stream
+ yavta -f RGB565 -s 1920x1080 -c10 /dev/video0
+
+.. _mali-c55-3a-stats:
+
+Capturing ISP Statistics
+========================
+
+The ISP is capable of producing statistics for consumption by image processing
+algorithms running in userspace. These statistics can be captured by queueing
+buffers to the `mali-c55 3a stats` V4L2 Device whilst the ISP is streaming. Only
+the :ref:`V4L2_META_FMT_MALI_C55_STATS <v4l2-meta-fmt-mali-c55-stats>`
+format is supported, so no format-setting need be done:
+
+.. code-block:: none
+
+ # We assume the media graph has been configured to support RGB565 capture
+ # from the mali-c55 fr V4L2 Device, which is at /dev/video0. The statistics
+ # V4L2 device is at /dev/video3
+
+ yavta -f RGB565 -s 1920x1080 -c32 /dev/video0 && \
+ yavta -c10 -F /dev/video3
+
+The layout of the buffer is described by :c:type:`mali_c55_stats_buffer`,
+but broadly statistics are generated to support three image processing
+algorithms; AEXP (Auto-Exposure), AWB (Auto-White Balance) and AF (Auto-Focus).
+These stats can be drawn from various places in the Mali C55 ISP pipeline, known
+as "tap points". This high-level block diagram is intended to explain where in
+the processing flow the statistics can be drawn from::
+
+ +--> AEXP-2 +----> AEXP-1 +--> AF-0
+ | +----> AF-1 |
+ | | |
+ +---------+ | +--------------+ | +--------------+ |
+ | Input +-+-->+ Digital Gain +---+-->+ Black Level +---+---+
+ +---------+ +--------------+ +--------------+ |
+ +-----------------------------------------------------------------+
+ |
+ | +--------------+ +---------+ +----------------+
+ +-->| Sinter Noise +-+ White +--+--->| Lens Shading +--+---------------+
+ | Reduction | | Balance | | | | | |
+ +--------------+ +---------+ | +----------------+ | |
+ +---> AEXP-0 (A) +--> AEXP-0 (B) |
+ +--------------------------------------------------------------------------+
+ |
+ | +----------------+ +--------------+ +----------------+
+ +-->| Tone mapping +-+--->| Demosaicing +->+ Purple Fringe +-+-----------+
+ | | | +--------------+ | Correction | | |
+ +----------------+ +-> AEXP-IRIDIX +----------------+ +---> AWB-0 |
+ +----------------------------------------------------------------------------+
+ | +-------------+ +-------------+
+ +------------------->| Colour +---+--->| Output |
+ | Correction | | | Pipelines |
+ +-------------+ | +-------------+
+ +--> AWB-1
+
+By default all statistics are drawn from the 0th tap point for each algorithm;
+I.E. AEXP statistics from AEXP-0 (A), AWB statistics from AWB-0 and AF
+statistics from AF-0. This is configurable for AEXP and AWB statsistics through
+programming the ISP's parameters.
+
+.. _mali-c55-3a-params:
+
+Programming ISP Parameters
+==========================
+
+The ISP can be programmed with various parameters from userspace to apply to the
+hardware before and during video stream. This allows userspace to dynamically
+change values such as black level, white balance and lens shading gains and so
+on.
+
+The buffer format and how to populate it are described by the
+:ref:`V4L2_META_FMT_MALI_C55_PARAMS <v4l2-meta-fmt-mali-c55-params>` format,
+which should be set as the data format for the `mali-c55 3a params` video node.
+
+References
+==========
+.. [1] https://git.linuxtv.org/v4l-utils.git/
+.. [2] https://git.ideasonboard.org/yavta.git
+.. [3] https://developer.arm.com/Processors/Mali-C55
diff --git a/Documentation/admin-guide/media/platform-cardlist.rst b/Documentation/admin-guide/media/platform-cardlist.rst
index 1230ae4037ad..63f4b19c3628 100644
--- a/Documentation/admin-guide/media/platform-cardlist.rst
+++ b/Documentation/admin-guide/media/platform-cardlist.rst
@@ -18,8 +18,6 @@ am437x-vpfe TI AM437x VPFE
aspeed-video Aspeed AST2400 and AST2500
atmel-isc ATMEL Image Sensor Controller (ISC)
atmel-isi ATMEL Image Sensor Interface (ISI)
-c8sectpfe SDR platform devices
-c8sectpfe SDR platform devices
cafe_ccic Marvell 88ALP01 (Cafe) CMOS Camera Controller
cdns-csi2rx Cadence MIPI-CSI2 RX Controller
cdns-csi2tx Cadence MIPI-CSI2 TX Controller
diff --git a/Documentation/admin-guide/media/radio-cardlist.rst b/Documentation/admin-guide/media/radio-cardlist.rst
index a82a146bf912..cec724256812 100644
--- a/Documentation/admin-guide/media/radio-cardlist.rst
+++ b/Documentation/admin-guide/media/radio-cardlist.rst
@@ -30,7 +30,6 @@ radio-terratec TerraTec ActiveRadio ISA Standalone
radio-timb Enable the Timberdale radio driver
radio-trust Trust FM radio card
radio-typhoon Typhoon Radio (a.k.a. EcoRadio)
-radio-wl1273 Texas Instruments WL1273 I2C FM Radio
fm_drv ISA radio devices
fm_drv ISA radio devices
radio-zoltrix Zoltrix Radio
diff --git a/Documentation/admin-guide/media/rkcif-rk3568-vicap.dot b/Documentation/admin-guide/media/rkcif-rk3568-vicap.dot
new file mode 100644
index 000000000000..3fac59335459
--- /dev/null
+++ b/Documentation/admin-guide/media/rkcif-rk3568-vicap.dot
@@ -0,0 +1,8 @@
+digraph board {
+ rankdir=TB
+ n00000001 [label="{{<port0> 0} | rkcif-dvp0\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000001:port1 -> n00000004
+ n00000004 [label="rkcif-dvp0-id0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
+ n00000025 [label="{{} | it6801 2-0048\n/dev/v4l-subdev1 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000025:port0 -> n00000001:port0
+}
diff --git a/Documentation/admin-guide/media/rkcif.rst b/Documentation/admin-guide/media/rkcif.rst
new file mode 100644
index 000000000000..2558c121abc4
--- /dev/null
+++ b/Documentation/admin-guide/media/rkcif.rst
@@ -0,0 +1,79 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================
+Rockchip Camera Interface (CIF)
+=========================================
+
+Introduction
+============
+
+The Rockchip Camera Interface (CIF) is featured in many Rockchip SoCs in
+different variants.
+The different variants are combinations of common building blocks, such as
+
+* INTERFACE blocks of different types, namely
+
+ * the Digital Video Port (DVP, a parallel data interface)
+ * the interface block for the MIPI CSI-2 receiver
+
+* CROP units
+
+* MIPI CSI-2 receiver (not available on all variants): This unit is referred
+ to as MIPI CSI HOST in the Rockchip documentation.
+ Technically, it is a separate hardware block, but it is strongly coupled to
+ the CIF and therefore included here.
+
+* MUX units (not available on all variants) that pass the video data to an
+ image signal processor (ISP)
+
+* SCALE units (not available on all variants)
+
+* DMA engines that transfer video data into system memory using a
+ double-buffering mechanism called ping-pong mode
+
+* Support for four streams per INTERFACE block (not available on all
+ variants), e.g., for MIPI CSI-2 Virtual Channels (VCs)
+
+This document describes the different variants of the CIF, their hardware
+layout, as well as their representation in the media controller centric rkcif
+device driver, which is located under drivers/media/platform/rockchip/rkcif.
+
+Variants
+========
+
+Rockchip PX30 Video Input Processor (VIP)
+-----------------------------------------
+
+The PX30 Video Input Processor (VIP) features a digital video port that accepts
+parallel video data or BT.656.
+Since these protocols do not feature multiple streams, the VIP has one DMA
+engine that transfers the input video data into system memory.
+
+The rkcif driver represents this hardware variant by exposing one V4L2 subdevice
+(the DVP INTERFACE/CROP block) and one V4L2 device (the DVP DMA engine).
+
+Rockchip RK3568 Video Capture (VICAP)
+-------------------------------------
+
+The RK3568 Video Capture (VICAP) unit features a digital video port and a MIPI
+CSI-2 receiver that can receive video data independently.
+The DVP accepts parallel video data, BT.656 and BT.1120.
+Since the BT.1120 protocol may feature more than one stream, the RK3568 VICAP
+DVP features four DMA engines that can capture different streams.
+Similarly, the RK3568 VICAP MIPI CSI-2 receiver features four DMA engines to
+handle different Virtual Channels (VCs).
+
+The rkcif driver represents this hardware variant by exposing up the following
+V4L2 subdevices:
+
+* rkcif-dvp0: INTERFACE/CROP block for the DVP
+
+and the following video devices:
+
+* rkcif-dvp0-id0: The support for multiple streams on the DVP is not yet
+ implemented, as it is hard to find test hardware. Thus, this video device
+ represents the first DMA engine of the RK3568 DVP.
+
+.. kernel-figure:: rkcif-rk3568-vicap.dot
+ :alt: Topology of the RK3568 Video Capture (VICAP) unit
+ :align: center
diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst
index 3bac5165b134..393f83e8dc4d 100644
--- a/Documentation/admin-guide/media/v4l-drivers.rst
+++ b/Documentation/admin-guide/media/v4l-drivers.rst
@@ -19,12 +19,14 @@ Video4Linux (V4L) driver-specific documentation
ipu3
ipu6-isys
ivtv
+ mali-c55
mgb4
omap3isp
philips
qcom_camss
raspberrypi-pisp-be
rcar-fdp1
+ rkcif
rkisp1
raspberrypi-rp1-cfe
saa7134
diff --git a/Documentation/admin-guide/pm/cpuidle.rst b/Documentation/admin-guide/pm/cpuidle.rst
index 0c090b076224..be4c1120e3f0 100644
--- a/Documentation/admin-guide/pm/cpuidle.rst
+++ b/Documentation/admin-guide/pm/cpuidle.rst
@@ -580,6 +580,15 @@ the given CPU as the upper limit for the exit latency of the idle states that
they are allowed to select for that CPU. They should never select any idle
states with exit latency beyond that limit.
+While the above CPU QoS constraints apply to CPU idle time management, user
+space may also request a CPU system wakeup latency QoS limit, via the
+`cpu_wakeup_latency` file. This QoS constraint is respected when selecting a
+suitable idle state for the CPUs, while entering the system-wide suspend-to-idle
+sleep state, but also to the regular CPU idle time management.
+
+Note that, the management of the `cpu_wakeup_latency` file works according to
+the 'cpu_dma_latency' file from user space point of view. Moreover, the unit
+is also microseconds.
Idle States Control Via Kernel Command Line
===========================================
diff --git a/Documentation/admin-guide/pm/intel_pstate.rst b/Documentation/admin-guide/pm/intel_pstate.rst
index 26e702c7016e..fde967b0c2e0 100644
--- a/Documentation/admin-guide/pm/intel_pstate.rst
+++ b/Documentation/admin-guide/pm/intel_pstate.rst
@@ -48,8 +48,9 @@ only way to pass early-configuration-time parameters to it is via the kernel
command line. However, its configuration can be adjusted via ``sysfs`` to a
great extent. In some configurations it even is possible to unregister it via
``sysfs`` which allows another ``CPUFreq`` scaling driver to be loaded and
-registered (see `below <status_attr_>`_).
+registered (see :ref:`below <status_attr>`).
+.. _operation_modes:
Operation Modes
===============
@@ -62,6 +63,8 @@ a certain performance scaling algorithm. Which of them will be in effect
depends on what kernel command line options are used and on the capabilities of
the processor.
+.. _active_mode:
+
Active Mode
-----------
@@ -94,6 +97,8 @@ Which of the P-state selection algorithms is used by default depends on the
Namely, if that option is set, the ``performance`` algorithm will be used by
default, and the other one will be used by default if it is not set.
+.. _active_mode_hwp:
+
Active Mode With HWP
~~~~~~~~~~~~~~~~~~~~
@@ -123,7 +128,7 @@ Energy-Performance Bias (EPB) knob (otherwise), which means that the processor's
internal P-state selection logic is expected to focus entirely on performance.
This will override the EPP/EPB setting coming from the ``sysfs`` interface
-(see `Energy vs Performance Hints`_ below). Moreover, any attempts to change
+(see :ref:`energy_performance_hints` below). Moreover, any attempts to change
the EPP/EPB to a value different from 0 ("performance") via ``sysfs`` in this
configuration will be rejected.
@@ -192,6 +197,8 @@ This is the default P-state selection algorithm if the
:c:macro:`CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE` kernel configuration option
is not set.
+.. _passive_mode:
+
Passive Mode
------------
@@ -289,12 +296,12 @@ Unlike ``_PSS`` objects in the ACPI tables, ``intel_pstate`` always exposes
the entire range of available P-states, including the whole turbo range, to the
``CPUFreq`` core and (in the passive mode) to generic scaling governors. This
generally causes turbo P-states to be set more often when ``intel_pstate`` is
-used relative to ACPI-based CPU performance scaling (see `below <acpi-cpufreq_>`_
-for more information).
+used relative to ACPI-based CPU performance scaling (see
+:ref:`below <acpi-cpufreq>` for more information).
Moreover, since ``intel_pstate`` always knows what the real turbo threshold is
(even if the Configurable TDP feature is enabled in the processor), its
-``no_turbo`` attribute in ``sysfs`` (described `below <no_turbo_attr_>`_) should
+``no_turbo`` attribute in ``sysfs`` (described :ref:`below <no_turbo_attr>`) should
work as expected in all cases (that is, if set to disable turbo P-states, it
always should prevent ``intel_pstate`` from using them).
@@ -307,12 +314,12 @@ pieces of information on it to be known, including:
* The minimum supported P-state.
- * The maximum supported `non-turbo P-state <turbo_>`_.
+ * The maximum supported :ref:`non-turbo P-state <turbo>`.
* Whether or not turbo P-states are supported at all.
- * The maximum supported `one-core turbo P-state <turbo_>`_ (if turbo P-states
- are supported).
+ * The maximum supported :ref:`one-core turbo P-state <turbo>` (if turbo
+ P-states are supported).
* The scaling formula to translate the driver's internal representation
of P-states into frequencies and the other way around.
@@ -400,10 +407,10 @@ Energy-Aware Scheduling Support
If ``CONFIG_ENERGY_MODEL`` has been set during kernel configuration and
``intel_pstate`` runs on a hybrid processor without SMT, in addition to enabling
-`CAS <CAS_>`_ it registers an Energy Model for the processor. This allows the
+:ref:`CAS` it registers an Energy Model for the processor. This allows the
Energy-Aware Scheduling (EAS) support to be enabled in the CPU scheduler if
``schedutil`` is used as the ``CPUFreq`` governor which requires ``intel_pstate``
-to operate in the `passive mode <Passive Mode_>`_.
+to operate in the :ref:`passive mode <passive_mode>`.
The Energy Model registered by ``intel_pstate`` is artificial (that is, it is
based on abstract cost values and it does not include any real power numbers)
@@ -432,6 +439,8 @@ the ``energy_model`` directory in ``debugfs`` (typlically mounted on
User Space Interface in ``sysfs``
=================================
+.. _global_attributes:
+
Global Attributes
-----------------
@@ -444,8 +453,8 @@ argument is passed to the kernel in the command line.
``max_perf_pct``
Maximum P-state the driver is allowed to set in percent of the
- maximum supported performance level (the highest supported `turbo
- P-state <turbo_>`_).
+ maximum supported performance level (the highest supported :ref:`turbo
+ P-state <turbo>`).
This attribute will not be exposed if the
``intel_pstate=per_cpu_perf_limits`` argument is present in the kernel
@@ -453,8 +462,8 @@ argument is passed to the kernel in the command line.
``min_perf_pct``
Minimum P-state the driver is allowed to set in percent of the
- maximum supported performance level (the highest supported `turbo
- P-state <turbo_>`_).
+ maximum supported performance level (the highest supported :ref:`turbo
+ P-state <turbo>`).
This attribute will not be exposed if the
``intel_pstate=per_cpu_perf_limits`` argument is present in the kernel
@@ -463,18 +472,18 @@ argument is passed to the kernel in the command line.
``num_pstates``
Number of P-states supported by the processor (between 0 and 255
inclusive) including both turbo and non-turbo P-states (see
- `Turbo P-states Support`_).
+ :ref:`turbo`).
This attribute is present only if the value exposed by it is the same
for all of the CPUs in the system.
The value of this attribute is not affected by the ``no_turbo``
- setting described `below <no_turbo_attr_>`_.
+ setting described :ref:`below <no_turbo_attr>`.
This attribute is read-only.
``turbo_pct``
- Ratio of the `turbo range <turbo_>`_ size to the size of the entire
+ Ratio of the :ref:`turbo range <turbo>` size to the size of the entire
range of supported P-states, in percent.
This attribute is present only if the value exposed by it is the same
@@ -486,7 +495,7 @@ argument is passed to the kernel in the command line.
``no_turbo``
If set (equal to 1), the driver is not allowed to set any turbo P-states
- (see `Turbo P-states Support`_). If unset (equal to 0, which is the
+ (see :ref:`turbo`). If unset (equal to 0, which is the
default), turbo P-states can be set by the driver.
[Note that ``intel_pstate`` does not support the general ``boost``
attribute (supported by some other scaling drivers) which is replaced
@@ -495,11 +504,11 @@ argument is passed to the kernel in the command line.
This attribute does not affect the maximum supported frequency value
supplied to the ``CPUFreq`` core and exposed via the policy interface,
but it affects the maximum possible value of per-policy P-state limits
- (see `Interpretation of Policy Attributes`_ below for details).
+ (see :ref:`policy_attributes_interpretation` below for details).
``hwp_dynamic_boost``
This attribute is only present if ``intel_pstate`` works in the
- `active mode with the HWP feature enabled <Active Mode With HWP_>`_ in
+ :ref:`active mode with the HWP feature enabled <active_mode_hwp>` in
the processor. If set (equal to 1), it causes the minimum P-state limit
to be increased dynamically for a short time whenever a task previously
waiting on I/O is selected to run on a given logical CPU (the purpose
@@ -514,12 +523,12 @@ argument is passed to the kernel in the command line.
Operation mode of the driver: "active", "passive" or "off".
"active"
- The driver is functional and in the `active mode
- <Active Mode_>`_.
+ The driver is functional and in the :ref:`active mode
+ <active_mode>`.
"passive"
- The driver is functional and in the `passive mode
- <Passive Mode_>`_.
+ The driver is functional and in the :ref:`passive mode
+ <passive_mode>`.
"off"
The driver is not functional (it is not registered as a scaling
@@ -547,13 +556,15 @@ argument is passed to the kernel in the command line.
attribute to "1" enables the energy-efficiency optimizations and setting
to "0" disables them.
+.. _policy_attributes_interpretation:
+
Interpretation of Policy Attributes
-----------------------------------
The interpretation of some ``CPUFreq`` policy attributes described in
Documentation/admin-guide/pm/cpufreq.rst is special with ``intel_pstate``
as the current scaling driver and it generally depends on the driver's
-`operation mode <Operation Modes_>`_.
+:ref:`operation mode <operation_modes>`.
First of all, the values of the ``cpuinfo_max_freq``, ``cpuinfo_min_freq`` and
``scaling_cur_freq`` attributes are produced by applying a processor-specific
@@ -562,9 +573,10 @@ Also, the values of the ``scaling_max_freq`` and ``scaling_min_freq``
attributes are capped by the frequency corresponding to the maximum P-state that
the driver is allowed to set.
-If the ``no_turbo`` `global attribute <no_turbo_attr_>`_ is set, the driver is
-not allowed to use turbo P-states, so the maximum value of ``scaling_max_freq``
-and ``scaling_min_freq`` is limited to the maximum non-turbo P-state frequency.
+If the ``no_turbo`` :ref:`global attribute <no_turbo_attr>` is set, the driver
+is not allowed to use turbo P-states, so the maximum value of
+``scaling_max_freq`` and ``scaling_min_freq`` is limited to the maximum
+non-turbo P-state frequency.
Accordingly, setting ``no_turbo`` causes ``scaling_max_freq`` and
``scaling_min_freq`` to go down to that value if they were above it before.
However, the old values of ``scaling_max_freq`` and ``scaling_min_freq`` will be
@@ -576,7 +588,7 @@ and ``scaling_min_freq`` corresponds to the maximum supported turbo P-state,
which also is the value of ``cpuinfo_max_freq`` in either case.
Next, the following policy attributes have special meaning if
-``intel_pstate`` works in the `active mode <Active Mode_>`_:
+``intel_pstate`` works in the :ref:`active mode <active_mode>`:
``scaling_available_governors``
List of P-state selection algorithms provided by ``intel_pstate``.
@@ -597,20 +609,22 @@ processor:
Shows the base frequency of the CPU. Any frequency above this will be
in the turbo frequency range.
-The meaning of these attributes in the `passive mode <Passive Mode_>`_ is the
+The meaning of these attributes in the :ref:`passive mode <passive_mode>` is the
same as for other scaling drivers.
Additionally, the value of the ``scaling_driver`` attribute for ``intel_pstate``
depends on the operation mode of the driver. Namely, it is either
-"intel_pstate" (in the `active mode <Active Mode_>`_) or "intel_cpufreq" (in the
-`passive mode <Passive Mode_>`_).
+"intel_pstate" (in the :ref:`active mode <active_mode>`) or "intel_cpufreq"
+(in the :ref:`passive mode <passive_mode>`).
+
+.. _pstate_limits_coordination:
Coordination of P-State Limits
------------------------------
``intel_pstate`` allows P-state limits to be set in two ways: with the help of
-the ``max_perf_pct`` and ``min_perf_pct`` `global attributes
-<Global Attributes_>`_ or via the ``scaling_max_freq`` and ``scaling_min_freq``
+the ``max_perf_pct`` and ``min_perf_pct`` :ref:`global attributes
+<global_attributes>` or via the ``scaling_max_freq`` and ``scaling_min_freq``
``CPUFreq`` policy attributes. The coordination between those limits is based
on the following rules, regardless of the current operation mode of the driver:
@@ -632,17 +646,18 @@ on the following rules, regardless of the current operation mode of the driver:
3. The global and per-policy limits can be set independently.
-In the `active mode with the HWP feature enabled <Active Mode With HWP_>`_, the
+In the :ref:`active mode with the HWP feature enabled <active_mode_hwp>`, the
resulting effective values are written into hardware registers whenever the
limits change in order to request its internal P-state selection logic to always
set P-states within these limits. Otherwise, the limits are taken into account
-by scaling governors (in the `passive mode <Passive Mode_>`_) and by the driver
-every time before setting a new P-state for a CPU.
+by scaling governors (in the :ref:`passive mode <passive_mode>`) and by the
+driver every time before setting a new P-state for a CPU.
Additionally, if the ``intel_pstate=per_cpu_perf_limits`` command line argument
is passed to the kernel, ``max_perf_pct`` and ``min_perf_pct`` are not exposed
at all and the only way to set the limits is by using the policy attributes.
+.. _energy_performance_hints:
Energy vs Performance Hints
---------------------------
@@ -702,9 +717,9 @@ output.
On those systems each ``_PSS`` object returns a list of P-states supported by
the corresponding CPU which basically is a subset of the P-states range that can
be used by ``intel_pstate`` on the same system, with one exception: the whole
-`turbo range <turbo_>`_ is represented by one item in it (the topmost one). By
-convention, the frequency returned by ``_PSS`` for that item is greater by 1 MHz
-than the frequency of the highest non-turbo P-state listed by it, but the
+:ref:`turbo range <turbo>` is represented by one item in it (the topmost one).
+By convention, the frequency returned by ``_PSS`` for that item is greater by
+1 MHz than the frequency of the highest non-turbo P-state listed by it, but the
corresponding P-state representation (following the hardware specification)
returned for it matches the maximum supported turbo P-state (or is the
special value 255 meaning essentially "go as high as you can get").
@@ -730,18 +745,18 @@ benefit from running at turbo frequencies will be given non-turbo P-states
instead.
One more issue related to that may appear on systems supporting the
-`Configurable TDP feature <turbo_>`_ allowing the platform firmware to set the
-turbo threshold. Namely, if that is not coordinated with the lists of P-states
-returned by ``_PSS`` properly, there may be more than one item corresponding to
-a turbo P-state in those lists and there may be a problem with avoiding the
-turbo range (if desirable or necessary). Usually, to avoid using turbo
-P-states overall, ``acpi-cpufreq`` simply avoids using the topmost state listed
-by ``_PSS``, but that is not sufficient when there are other turbo P-states in
-the list returned by it.
+:ref:`Configurable TDP feature <turbo>` allowing the platform firmware to set
+the turbo threshold. Namely, if that is not coordinated with the lists of
+P-states returned by ``_PSS`` properly, there may be more than one item
+corresponding to a turbo P-state in those lists and there may be a problem with
+avoiding the turbo range (if desirable or necessary). Usually, to avoid using
+turbo P-states overall, ``acpi-cpufreq`` simply avoids using the topmost state
+listed by ``_PSS``, but that is not sufficient when there are other turbo
+P-states in the list returned by it.
Apart from the above, ``acpi-cpufreq`` works like ``intel_pstate`` in the
-`passive mode <Passive Mode_>`_, except that the number of P-states it can set
-is limited to the ones listed by the ACPI ``_PSS`` objects.
+:ref:`passive mode <passive_mode>`, except that the number of P-states it can
+set is limited to the ones listed by the ACPI ``_PSS`` objects.
Kernel Command Line Options for ``intel_pstate``
@@ -756,11 +771,11 @@ of them have to be prepended with the ``intel_pstate=`` prefix.
processor is supported by it.
``active``
- Register ``intel_pstate`` in the `active mode <Active Mode_>`_ to start
- with.
+ Register ``intel_pstate`` in the :ref:`active mode <active_mode>` to
+ start with.
``passive``
- Register ``intel_pstate`` in the `passive mode <Passive Mode_>`_ to
+ Register ``intel_pstate`` in the :ref:`passive mode <passive_mode>` to
start with.
``force``
@@ -793,12 +808,12 @@ of them have to be prepended with the ``intel_pstate=`` prefix.
and this option has no effect.
``per_cpu_perf_limits``
- Use per-logical-CPU P-State limits (see `Coordination of P-state
- Limits`_ for details).
+ Use per-logical-CPU P-State limits (see
+ :ref:`pstate_limits_coordination` for details).
``no_cas``
- Do not enable `capacity-aware scheduling <CAS_>`_ which is enabled by
- default on hybrid systems without SMT.
+ Do not enable :ref:`capacity-aware scheduling <CAS>` which is enabled
+ by default on hybrid systems without SMT.
Diagnostics and Tuning
======================
@@ -810,7 +825,7 @@ There are two static trace events that can be used for ``intel_pstate``
diagnostics. One of them is the ``cpu_frequency`` trace event generally used
by ``CPUFreq``, and the other one is the ``pstate_sample`` trace event specific
to ``intel_pstate``. Both of them are triggered by ``intel_pstate`` only if
-it works in the `active mode <Active Mode_>`_.
+it works in the :ref:`active mode <active_mode>`.
The following sequence of shell commands can be used to enable them and see
their output (if the kernel is generally configured to support event tracing)::
@@ -822,7 +837,7 @@ their output (if the kernel is generally configured to support event tracing)::
gnome-terminal--4510 [001] ..s. 1177.680733: pstate_sample: core_busy=107 scaled=94 from=26 to=26 mperf=1143818 aperf=1230607 tsc=29838618 freq=2474476
cat-5235 [002] ..s. 1177.681723: cpu_frequency: state=2900000 cpu_id=2
-If ``intel_pstate`` works in the `passive mode <Passive Mode_>`_, the
+If ``intel_pstate`` works in the :ref:`passive mode <passive_mode>`, the
``cpu_frequency`` trace event will be triggered either by the ``schedutil``
scaling governor (for the policies it is attached to), or by the ``CPUFreq``
core (for the policies with other scaling governors).
diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst
index 2ef50828aff1..369a738a6819 100644
--- a/Documentation/admin-guide/sysctl/net.rst
+++ b/Documentation/admin-guide/sysctl/net.rst
@@ -212,6 +212,14 @@ mem_pcpu_rsv
Per-cpu reserved forward alloc cache size in page units. Default 1MB per CPU.
+bypass_prot_mem
+---------------
+
+Skip charging socket buffers to the global per-protocol memory
+accounting controlled by net.ipv4.tcp_mem, net.ipv4.udp_mem, etc.
+
+Default: 0 (off)
+
rmem_default
------------
@@ -347,9 +355,9 @@ skb_defer_max
-------------
Max size (in skbs) of the per-cpu list of skbs being freed
-by the cpu which allocated them. Used by TCP stack so far.
+by the cpu which allocated them.
-Default: 64
+Default: 128
optmem_max
----------
@@ -406,6 +414,23 @@ to SOCK_TXREHASH_DEFAULT (i. e. not overridden by setsockopt).
If set to 1 (default), hash rethink is performed on listening socket.
If set to 0, hash rethink is not performed.
+txq_reselection_ms
+------------------
+
+Controls how often (in ms) a busy connected flow can select another tx queue.
+
+A resection is desirable when/if user thread has migrated and XPS
+would select a different queue. Same can occur without XPS
+if the flow hash has changed.
+
+But switching txq can introduce reorders, especially if the
+old queue is under high pressure. Modern TCP stacks deal
+well with reorders if they happen not too often.
+
+To disable this feature, set the value to 0.
+
+Default : 1000
+
gro_normal_batch
----------------
diff --git a/Documentation/admin-guide/tainted-kernels.rst b/Documentation/admin-guide/tainted-kernels.rst
index a0cc017e4424..ed1f8f1e86c5 100644
--- a/Documentation/admin-guide/tainted-kernels.rst
+++ b/Documentation/admin-guide/tainted-kernels.rst
@@ -186,6 +186,6 @@ More detailed explanation for tainting
18) ``N`` if an in-kernel test, such as a KUnit test, has been run.
- 19) ``J`` if userpace opened /dev/fwctl/* and performed a FWTCL_RPC_DEBUG_WRITE
+ 19) ``J`` if userspace opened /dev/fwctl/* and performed a FWTCL_RPC_DEBUG_WRITE
to use the devices debugging features. Device debugging features could
cause the device to malfunction in undefined ways.
diff --git a/Documentation/admin-guide/thermal/index.rst b/Documentation/admin-guide/thermal/index.rst
index 193b7b01a87d..e48bc0a1951b 100644
--- a/Documentation/admin-guide/thermal/index.rst
+++ b/Documentation/admin-guide/thermal/index.rst
@@ -6,3 +6,4 @@ Thermal Subsystem
:maxdepth: 1
intel_powerclamp
+ intel_thermal_throttle
diff --git a/Documentation/admin-guide/thermal/intel_thermal_throttle.rst b/Documentation/admin-guide/thermal/intel_thermal_throttle.rst
new file mode 100644
index 000000000000..f4fbf9d5a4ec
--- /dev/null
+++ b/Documentation/admin-guide/thermal/intel_thermal_throttle.rst
@@ -0,0 +1,91 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: <isonum.txt>
+
+=======================================
+Intel thermal throttle events reporting
+=======================================
+
+:Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+
+Introduction
+------------
+
+Intel processors have built in automatic and adaptive thermal monitoring
+mechanisms that force the processor to reduce its power consumption in order
+to operate within predetermined temperature limits.
+
+Refer to section "THERMAL MONITORING AND PROTECTION" in the "Intel® 64 and
+IA-32 Architectures Software Developer’s Manual Volume 3 (3A, 3B, 3C, & 3D):
+System Programming Guide" for more details.
+
+In general, there are two mechanisms to control the core temperature of the
+processor. They are called "Thermal Monitor 1 (TM1) and Thermal Monitor 2 (TM2)".
+
+The status of the temperature sensor that triggers the thermal monitor (TM1/TM2)
+is indicated through the "thermal status flag" and "thermal status log flag" in
+MSR_IA32_THERM_STATUS for core level and MSR_IA32_PACKAGE_THERM_STATUS for
+package level.
+
+Thermal Status flag, bit 0 — When set, indicates that the processor core
+temperature is currently at the trip temperature of the thermal monitor and that
+the processor power consumption is being reduced via either TM1 or TM2, depending
+on which is enabled. When clear, the flag indicates that the core temperature is
+below the thermal monitor trip temperature. This flag is read only.
+
+Thermal Status Log flag, bit 1 — When set, indicates that the thermal sensor has
+tripped since the last power-up or reset or since the last time that software
+cleared this flag. This flag is a sticky bit; once set it remains set until
+cleared by software or until a power-up or reset of the processor. The default
+state is clear.
+
+It is possible that when user reads MSR_IA32_THERM_STATUS or
+MSR_IA32_PACKAGE_THERM_STATUS, TM1/TM2 is not active. In this case,
+"Thermal Status flag" will read "0" and the "Thermal Status Log flag" will be set
+to show any previous "TM1/TM2" activation. But since it needs to be cleared by
+the software, it can't show the number of occurrences of "TM1/TM2" activations.
+
+Hence, Linux provides counters of how many times the "Thermal Status flag" was
+set. Also presents how long the "Thermal Status flag" was active in milliseconds.
+Using these counters, users can check if the performance was limited because of
+thermal events. It is recommended to read from sysfs instead of directly reading
+MSRs as the "Thermal Status Log flag" is reset by the driver to implement rate
+control.
+
+Sysfs Interface
+---------------
+
+Thermal throttling events are presented for each CPU under
+"/sys/devices/system/cpu/cpuX/thermal_throttle/", where "X" is the CPU number.
+
+All these counters are read-only. They can't be reset to 0. So, they can potentially
+overflow after reaching the maximum 64 bit unsigned integer.
+
+``core_throttle_count``
+ Shows the number of times "Thermal Status flag" changed from 0 to 1 for this
+ CPU since OS boot and thermal vector is initialized. This is a 64 bit counter.
+
+``package_throttle_count``
+ Shows the number of times "Thermal Status flag" changed from 0 to 1 for the
+ package containing this CPU since OS boot and thermal vector is initialized.
+ Package status is broadcast to all CPUs; all CPUs in the package increment
+ this count. This is a 64-bit counter.
+
+``core_throttle_max_time_ms``
+ Shows the maximum amount of time for which "Thermal Status flag" has been
+ set to 1 for this CPU at the core level since OS boot and thermal vector
+ is initialized.
+
+``package_throttle_max_time_ms``
+ Shows the maximum amount of time for which "Thermal Status flag" has been
+ set to 1 for the package containing this CPU since OS boot and thermal
+ vector is initialized.
+
+``core_throttle_total_time_ms``
+ Shows the cumulative time for which "Thermal Status flag" has been
+ set to 1 for this CPU for core level since OS boot and thermal vector
+ is initialized.
+
+``package_throttle_total_time_ms``
+ Shows the cumulative time for which "Thermal Status flag" has been set
+ to 1 for the package containing this CPU since OS boot and thermal vector
+ is initialized.
diff --git a/Documentation/admin-guide/workload-tracing.rst b/Documentation/admin-guide/workload-tracing.rst
index d6313890ee41..35963491b9f1 100644
--- a/Documentation/admin-guide/workload-tracing.rst
+++ b/Documentation/admin-guide/workload-tracing.rst
@@ -196,11 +196,11 @@ Let’s checkout the latest Linux repository and build cscope database::
cscope -R -p10 # builds cscope.out database before starting browse session
cscope -d -p10 # starts browse session on cscope.out database
-Note: Run "cscope -R -p10" to build the database and c"scope -d -p10" to
-enter into the browsing session. cscope by default cscope.out database.
-To get out of this mode press ctrl+d. -p option is used to specify the
-number of file path components to display. -p10 is optimal for browsing
-kernel sources.
+Note: Run "cscope -R -p10" to build the database and "cscope -d -p10" to
+enter into the browsing session. cscope by default uses the cscope.out
+database. To get out of this mode press ctrl+d. -p option is used to
+specify the number of file path components to display. -p10 is optimal
+for browsing kernel sources.
What is perf and how do we use it?
==================================
diff --git a/Documentation/arch/arm64/booting.rst b/Documentation/arch/arm64/booting.rst
index e4f953839f71..26efca09aef3 100644
--- a/Documentation/arch/arm64/booting.rst
+++ b/Documentation/arch/arm64/booting.rst
@@ -391,13 +391,13 @@ Before jumping into the kernel, the following conditions must be met:
- SMCR_EL2.LEN must be initialised to the same value for all CPUs the
kernel will execute on.
- - HWFGRTR_EL2.nTPIDR2_EL0 (bit 55) must be initialised to 0b01.
+ - HFGRTR_EL2.nTPIDR2_EL0 (bit 55) must be initialised to 0b01.
- - HWFGWTR_EL2.nTPIDR2_EL0 (bit 55) must be initialised to 0b01.
+ - HFGWTR_EL2.nTPIDR2_EL0 (bit 55) must be initialised to 0b01.
- - HWFGRTR_EL2.nSMPRI_EL1 (bit 54) must be initialised to 0b01.
+ - HFGRTR_EL2.nSMPRI_EL1 (bit 54) must be initialised to 0b01.
- - HWFGWTR_EL2.nSMPRI_EL1 (bit 54) must be initialised to 0b01.
+ - HFGWTR_EL2.nSMPRI_EL1 (bit 54) must be initialised to 0b01.
For CPUs with the Scalable Matrix Extension FA64 feature (FEAT_SME_FA64):
diff --git a/Documentation/arch/arm64/sve.rst b/Documentation/arch/arm64/sve.rst
index 28152492c29c..a61c9d0efe4d 100644
--- a/Documentation/arch/arm64/sve.rst
+++ b/Documentation/arch/arm64/sve.rst
@@ -402,6 +402,11 @@ The regset data starts with struct user_sve_header, containing:
streaming mode and any SETREGSET of NT_ARM_SSVE will enter streaming mode
if the target was not in streaming mode.
+* On systems that do not support SVE it is permitted to use SETREGSET to
+ write SVE_PT_REGS_FPSIMD formatted data via NT_ARM_SVE, in this case the
+ vector length should be specified as 0. This allows streaming mode to be
+ disabled on systems with SME but not SVE.
+
* If any register data is provided along with SVE_PT_VL_ONEXEC then the
registers data will be interpreted with the current vector length, not
the vector length configured for use on exec.
diff --git a/Documentation/arch/s390/s390dbf.rst b/Documentation/arch/s390/s390dbf.rst
index af8bdc3629e7..aad6d88974fe 100644
--- a/Documentation/arch/s390/s390dbf.rst
+++ b/Documentation/arch/s390/s390dbf.rst
@@ -243,9 +243,8 @@ Examples:
Changing the size of debug areas
------------------------------------
-It is possible the change the size of debug areas through piping
-the number of pages to the debugfs file "pages". The resize request will
-also flush the debug areas.
+To resize a debug area, write the desired page count to the "pages" file.
+Existing data is preserved if it fits; otherwise, oldest entries are dropped.
Example:
diff --git a/Documentation/arch/x86/boot.rst b/Documentation/arch/x86/boot.rst
index 77e6163288db..32eea3d2807e 100644
--- a/Documentation/arch/x86/boot.rst
+++ b/Documentation/arch/x86/boot.rst
@@ -1431,12 +1431,34 @@ The boot loader *must* fill out the following fields in bp::
All other fields should be zero.
.. note::
- The EFI Handover Protocol is deprecated in favour of the ordinary PE/COFF
- entry point, combined with the LINUX_EFI_INITRD_MEDIA_GUID based initrd
- loading protocol (refer to [0] for an example of the bootloader side of
- this), which removes the need for any knowledge on the part of the EFI
- bootloader regarding the internal representation of boot_params or any
- requirements/limitations regarding the placement of the command line
- and ramdisk in memory, or the placement of the kernel image itself.
-
-[0] https://github.com/u-boot/u-boot/commit/ec80b4735a593961fe701cc3a5d717d4739b0fd0
+ The EFI Handover Protocol is deprecated in favour of the ordinary PE/COFF
+ entry point described below.
+
+.. _pe-coff-entry-point:
+
+PE/COFF entry point
+===================
+
+When compiled with ``CONFIG_EFI_STUB=y``, the kernel can be executed as a
+regular PE/COFF binary. See Documentation/admin-guide/efi-stub.rst for
+implementation details.
+
+The stub loader can request the initrd via a UEFI protocol. For this to work,
+the firmware or bootloader needs to register a handle which carries
+implementations of the ``EFI_LOAD_FILE2`` protocol and the device path
+protocol exposing the ``LINUX_EFI_INITRD_MEDIA_GUID`` vendor media device path.
+In this case, a kernel booting via the EFI stub will invoke
+``LoadFile2::LoadFile()`` method on the registered protocol to instruct the
+firmware to load the initrd into a memory location chosen by the kernel/EFI
+stub.
+
+This approach removes the need for any knowledge on the part of the EFI
+bootloader regarding the internal representation of boot_params or any
+requirements/limitations regarding the placement of the command line and
+ramdisk in memory, or the placement of the kernel image itself.
+
+For sample implementations, refer to `the original u-boot implementation`_ or
+`the OVMF implementation`_.
+
+.. _the original u-boot implementation: https://github.com/u-boot/u-boot/commit/ec80b4735a593961fe701cc3a5d717d4739b0fd0
+.. _the OVMF implementation: https://github.com/tianocore/edk2/blob/1780373897f12c25075f8883e073144506441168/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c
diff --git a/Documentation/bpf/libbpf/program_types.rst b/Documentation/bpf/libbpf/program_types.rst
index 218b020a2f81..3b837522834b 100644
--- a/Documentation/bpf/libbpf/program_types.rst
+++ b/Documentation/bpf/libbpf/program_types.rst
@@ -100,10 +100,26 @@ described in more detail in the footnotes.
| | | ``uretprobe.s+`` [#uprobe]_ | Yes |
+ + +----------------------------------+-----------+
| | | ``usdt+`` [#usdt]_ | |
++ + +----------------------------------+-----------+
+| | | ``usdt.s+`` [#usdt]_ | Yes |
+ +----------------------------------------+----------------------------------+-----------+
| | ``BPF_TRACE_KPROBE_MULTI`` | ``kprobe.multi+`` [#kpmulti]_ | |
+ + +----------------------------------+-----------+
| | | ``kretprobe.multi+`` [#kpmulti]_ | |
++ +----------------------------------------+----------------------------------+-----------+
+| | ``BPF_TRACE_KPROBE_SESSION`` | ``kprobe.session+`` [#kpmulti]_ | |
++ +----------------------------------------+----------------------------------+-----------+
+| | ``BPF_TRACE_UPROBE_MULTI`` | ``uprobe.multi+`` [#upmul]_ | |
++ + +----------------------------------+-----------+
+| | | ``uprobe.multi.s+`` [#upmul]_ | Yes |
++ + +----------------------------------+-----------+
+| | | ``uretprobe.multi+`` [#upmul]_ | |
++ + +----------------------------------+-----------+
+| | | ``uretprobe.multi.s+`` [#upmul]_ | Yes |
++ +----------------------------------------+----------------------------------+-----------+
+| | ``BPF_TRACE_UPROBE_SESSION`` | ``uprobe.session+`` [#upmul]_ | |
++ + +----------------------------------+-----------+
+| | | ``uprobe.session.s+`` [#upmul]_ | Yes |
+-------------------------------------------+----------------------------------------+----------------------------------+-----------+
| ``BPF_PROG_TYPE_LIRC_MODE2`` | ``BPF_LIRC_MODE2`` | ``lirc_mode2`` | |
+-------------------------------------------+----------------------------------------+----------------------------------+-----------+
@@ -219,6 +235,8 @@ described in more detail in the footnotes.
non-negative integer.
.. [#ksyscall] The ``ksyscall`` attach format is ``ksyscall/<syscall>``.
.. [#uprobe] The ``uprobe`` attach format is ``uprobe[.s]/<path>:<function>[+<offset>]``.
+.. [#upmul] The ``uprobe.multi`` attach format is ``uprobe.multi[.s]/<path>:<function-pattern>``
+ where ``function-pattern`` supports ``*`` and ``?`` wildcards.
.. [#usdt] The ``usdt`` attach format is ``usdt/<path>:<provider>:<name>``.
.. [#kpmulti] The ``kprobe.multi`` attach format is ``kprobe.multi/<pattern>`` where ``pattern``
supports ``*`` and ``?`` wildcards. Valid characters for pattern are
diff --git a/Documentation/bpf/map_array.rst b/Documentation/bpf/map_array.rst
index f2f51a53e8ae..fa56ff75190c 100644
--- a/Documentation/bpf/map_array.rst
+++ b/Documentation/bpf/map_array.rst
@@ -15,8 +15,9 @@ of constant size. The size of the array is defined in ``max_entries`` at
creation time. All array elements are pre-allocated and zero initialized when
created. ``BPF_MAP_TYPE_PERCPU_ARRAY`` uses a different memory region for each
CPU whereas ``BPF_MAP_TYPE_ARRAY`` uses the same memory region. The value
-stored can be of any size, however, all array elements are aligned to 8
-bytes.
+stored can be of any size for ``BPF_MAP_TYPE_ARRAY`` and not more than
+``PCPU_MIN_UNIT_SIZE`` (32 kB) for ``BPF_MAP_TYPE_PERCPU_ARRAY``. All
+array elements are aligned to 8 bytes.
Since kernel 5.5, memory mapping may be enabled for ``BPF_MAP_TYPE_ARRAY`` by
setting the flag ``BPF_F_MMAPABLE``. The map definition is page-aligned and
diff --git a/Documentation/conf.py b/Documentation/conf.py
index 574896cca198..1ea2ae5c6276 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -18,8 +18,6 @@ import sphinx
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath("sphinx"))
-from load_config import loadConfig # pylint: disable=C0413,E0401
-
# Minimal supported version
needs_sphinx = "3.4.3"
@@ -93,8 +91,12 @@ def config_init(app, config):
# LaTeX and PDF output require a list of documents with are dependent
# of the app.srcdir. Add them here
- # When SPHINXDIRS is used, we just need to get index.rst, if it exists
+ # Handle the case where SPHINXDIRS is used
if not os.path.samefile(doctree, app.srcdir):
+ # Add a tag to mark that the build is actually a subproject
+ tags.add("subproject")
+
+ # get index.rst, if it exists
doc = os.path.basename(app.srcdir)
fname = "index"
if os.path.exists(os.path.join(app.srcdir, fname + ".rst")):
@@ -583,13 +585,6 @@ pdf_documents = [
kerneldoc_bin = "../scripts/kernel-doc.py"
kerneldoc_srctree = ".."
-# ------------------------------------------------------------------------------
-# Since loadConfig overwrites settings from the global namespace, it has to be
-# the last statement in the conf.py file
-# ------------------------------------------------------------------------------
-loadConfig(globals())
-
-
def setup(app):
"""Patterns need to be updated at init time on older Sphinx versions"""
diff --git a/Documentation/core-api/assoc_array.rst b/Documentation/core-api/assoc_array.rst
index 792bbf9939e1..19d89f92bf8d 100644
--- a/Documentation/core-api/assoc_array.rst
+++ b/Documentation/core-api/assoc_array.rst
@@ -92,18 +92,18 @@ There are two functions for dealing with the script:
void assoc_array_apply_edit(struct assoc_array_edit *edit);
-This will perform the edit functions, interpolating various write barriers
-to permit accesses under the RCU read lock to continue. The edit script
-will then be passed to ``call_rcu()`` to free it and any dead stuff it points
-to.
+ This will perform the edit functions, interpolating various write barriers
+ to permit accesses under the RCU read lock to continue. The edit script
+ will then be passed to ``call_rcu()`` to free it and any dead stuff it
+ points to.
2. Cancel an edit script::
void assoc_array_cancel_edit(struct assoc_array_edit *edit);
-This frees the edit script and all preallocated memory immediately. If
-this was for insertion, the new object is _not_ released by this function,
-but must rather be released by the caller.
+ This frees the edit script and all preallocated memory immediately. If
+ this was for insertion, the new object is *not* released by this function,
+ but must rather be released by the caller.
These functions are guaranteed not to fail.
@@ -123,43 +123,43 @@ This points to a number of methods, all of which need to be provided:
unsigned long (*get_key_chunk)(const void *index_key, int level);
-This should return a chunk of caller-supplied index key starting at the
-*bit* position given by the level argument. The level argument will be a
-multiple of ``ASSOC_ARRAY_KEY_CHUNK_SIZE`` and the function should return
-``ASSOC_ARRAY_KEY_CHUNK_SIZE bits``. No error is possible.
+ This should return a chunk of caller-supplied index key starting at the
+ *bit* position given by the level argument. The level argument will be a
+ multiple of ``ASSOC_ARRAY_KEY_CHUNK_SIZE`` and the function should return
+ ``ASSOC_ARRAY_KEY_CHUNK_SIZE bits``. No error is possible.
2. Get a chunk of an object's index key::
unsigned long (*get_object_key_chunk)(const void *object, int level);
-As the previous function, but gets its data from an object in the array
-rather than from a caller-supplied index key.
+ As the previous function, but gets its data from an object in the array
+ rather than from a caller-supplied index key.
3. See if this is the object we're looking for::
bool (*compare_object)(const void *object, const void *index_key);
-Compare the object against an index key and return ``true`` if it matches and
-``false`` if it doesn't.
+ Compare the object against an index key and return ``true`` if it matches
+ and ``false`` if it doesn't.
4. Diff the index keys of two objects::
int (*diff_objects)(const void *object, const void *index_key);
-Return the bit position at which the index key of the specified object
-differs from the given index key or -1 if they are the same.
+ Return the bit position at which the index key of the specified object
+ differs from the given index key or -1 if they are the same.
5. Free an object::
void (*free_object)(void *object);
-Free the specified object. Note that this may be called an RCU grace period
-after ``assoc_array_apply_edit()`` was called, so ``synchronize_rcu()`` may be
-necessary on module unloading.
+ Free the specified object. Note that this may be called an RCU grace period
+ after ``assoc_array_apply_edit()`` was called, so ``synchronize_rcu()`` may
+ be necessary on module unloading.
Manipulation Functions
@@ -171,7 +171,7 @@ There are a number of functions for manipulating an associative array:
void assoc_array_init(struct assoc_array *array);
-This initialises the base structure for an associative array. It can't fail.
+ This initialises the base structure for an associative array. It can't fail.
2. Insert/replace an object in an associative array::
@@ -182,21 +182,21 @@ This initialises the base structure for an associative array. It can't fail.
const void *index_key,
void *object);
-This inserts the given object into the array. Note that the least
-significant bit of the pointer must be zero as it's used to type-mark
-pointers internally.
+ This inserts the given object into the array. Note that the least
+ significant bit of the pointer must be zero as it's used to type-mark
+ pointers internally.
-If an object already exists for that key then it will be replaced with the
-new object and the old one will be freed automatically.
+ If an object already exists for that key then it will be replaced with the
+ new object and the old one will be freed automatically.
-The ``index_key`` argument should hold index key information and is
-passed to the methods in the ops table when they are called.
+ The ``index_key`` argument should hold index key information and is
+ passed to the methods in the ops table when they are called.
-This function makes no alteration to the array itself, but rather returns
-an edit script that must be applied. ``-ENOMEM`` is returned in the case of
-an out-of-memory error.
+ This function makes no alteration to the array itself, but rather returns
+ an edit script that must be applied. ``-ENOMEM`` is returned in the case of
+ an out-of-memory error.
-The caller should lock exclusively against other modifiers of the array.
+ The caller should lock exclusively against other modifiers of the array.
3. Delete an object from an associative array::
@@ -206,15 +206,15 @@ The caller should lock exclusively against other modifiers of the array.
const struct assoc_array_ops *ops,
const void *index_key);
-This deletes an object that matches the specified data from the array.
+ This deletes an object that matches the specified data from the array.
-The ``index_key`` argument should hold index key information and is
-passed to the methods in the ops table when they are called.
+ The ``index_key`` argument should hold index key information and is
+ passed to the methods in the ops table when they are called.
-This function makes no alteration to the array itself, but rather returns
-an edit script that must be applied. ``-ENOMEM`` is returned in the case of
-an out-of-memory error. ``NULL`` will be returned if the specified object is
-not found within the array.
+ This function makes no alteration to the array itself, but rather returns
+ an edit script that must be applied. ``-ENOMEM`` is returned in the case of
+ an out-of-memory error. ``NULL`` will be returned if the specified object
+ is not found within the array.
The caller should lock exclusively against other modifiers of the array.
@@ -225,14 +225,14 @@ The caller should lock exclusively against other modifiers of the array.
assoc_array_clear(struct assoc_array *array,
const struct assoc_array_ops *ops);
-This deletes all the objects from an associative array and leaves it
-completely empty.
+ This deletes all the objects from an associative array and leaves it
+ completely empty.
-This function makes no alteration to the array itself, but rather returns
-an edit script that must be applied. ``-ENOMEM`` is returned in the case of
-an out-of-memory error.
+ This function makes no alteration to the array itself, but rather returns
+ an edit script that must be applied. ``-ENOMEM`` is returned in the case of
+ an out-of-memory error.
-The caller should lock exclusively against other modifiers of the array.
+ The caller should lock exclusively against other modifiers of the array.
5. Destroy an associative array, deleting all objects::
@@ -240,14 +240,14 @@ The caller should lock exclusively against other modifiers of the array.
void assoc_array_destroy(struct assoc_array *array,
const struct assoc_array_ops *ops);
-This destroys the contents of the associative array and leaves it
-completely empty. It is not permitted for another thread to be traversing
-the array under the RCU read lock at the same time as this function is
-destroying it as no RCU deferral is performed on memory release -
-something that would require memory to be allocated.
+ This destroys the contents of the associative array and leaves it
+ completely empty. It is not permitted for another thread to be traversing
+ the array under the RCU read lock at the same time as this function is
+ destroying it as no RCU deferral is performed on memory release -
+ something that would require memory to be allocated.
-The caller should lock exclusively against other modifiers and accessors
-of the array.
+ The caller should lock exclusively against other modifiers and accessors
+ of the array.
6. Garbage collect an associative array::
@@ -257,24 +257,24 @@ of the array.
bool (*iterator)(void *object, void *iterator_data),
void *iterator_data);
-This iterates over the objects in an associative array and passes each one to
-``iterator()``. If ``iterator()`` returns ``true``, the object is kept. If it
-returns ``false``, the object will be freed. If the ``iterator()`` function
-returns ``true``, it must perform any appropriate refcount incrementing on the
-object before returning.
+ This iterates over the objects in an associative array and passes each one
+ to ``iterator()``. If ``iterator()`` returns ``true``, the object is kept.
+ If it returns ``false``, the object will be freed. If the ``iterator()``
+ function returns ``true``, it must perform any appropriate refcount
+ incrementing on the object before returning.
-The internal tree will be packed down if possible as part of the iteration
-to reduce the number of nodes in it.
+ The internal tree will be packed down if possible as part of the iteration
+ to reduce the number of nodes in it.
-The ``iterator_data`` is passed directly to ``iterator()`` and is otherwise
-ignored by the function.
+ The ``iterator_data`` is passed directly to ``iterator()`` and is otherwise
+ ignored by the function.
-The function will return ``0`` if successful and ``-ENOMEM`` if there wasn't
-enough memory.
+ The function will return ``0`` if successful and ``-ENOMEM`` if there wasn't
+ enough memory.
-It is possible for other threads to iterate over or search the array under
-the RCU read lock while this function is in progress. The caller should
-lock exclusively against other modifiers of the array.
+ It is possible for other threads to iterate over or search the array under
+ the RCU read lock while this function is in progress. The caller should
+ lock exclusively against other modifiers of the array.
Access Functions
@@ -289,19 +289,19 @@ There are two functions for accessing an associative array:
void *iterator_data),
void *iterator_data);
-This passes each object in the array to the iterator callback function.
-``iterator_data`` is private data for that function.
+ This passes each object in the array to the iterator callback function.
+ ``iterator_data`` is private data for that function.
-This may be used on an array at the same time as the array is being
-modified, provided the RCU read lock is held. Under such circumstances,
-it is possible for the iteration function to see some objects twice. If
-this is a problem, then modification should be locked against. The
-iteration algorithm should not, however, miss any objects.
+ This may be used on an array at the same time as the array is being
+ modified, provided the RCU read lock is held. Under such circumstances,
+ it is possible for the iteration function to see some objects twice. If
+ this is a problem, then modification should be locked against. The
+ iteration algorithm should not, however, miss any objects.
-The function will return ``0`` if no objects were in the array or else it will
-return the result of the last iterator function called. Iteration stops
-immediately if any call to the iteration function results in a non-zero
-return.
+ The function will return ``0`` if no objects were in the array or else it
+ will return the result of the last iterator function called. Iteration
+ stops immediately if any call to the iteration function results in a
+ non-zero return.
2. Find an object in an associative array::
@@ -310,14 +310,14 @@ return.
const struct assoc_array_ops *ops,
const void *index_key);
-This walks through the array's internal tree directly to the object
-specified by the index key..
+ This walks through the array's internal tree directly to the object
+ specified by the index key.
-This may be used on an array at the same time as the array is being
-modified, provided the RCU read lock is held.
+ This may be used on an array at the same time as the array is being
+ modified, provided the RCU read lock is held.
-The function will return the object if found (and set ``*_type`` to the object
-type) or will return ``NULL`` if the object was not found.
+ The function will return the object if found (and set ``*_type`` to the
+ object type) or will return ``NULL`` if the object was not found.
Index Key Form
@@ -399,10 +399,11 @@ fixed levels. For example::
In the above example, there are 7 nodes (A-G), each with 16 slots (0-f).
Assuming no other meta data nodes in the tree, the key space is divided
-thusly::
+thusly:
+ =========== ====
KEY PREFIX NODE
- ========== ====
+ =========== ====
137* D
138* E
13[0-69-f]* C
@@ -410,10 +411,12 @@ thusly::
e6* G
e[0-57-f]* F
[02-df]* A
+ =========== ====
So, for instance, keys with the following example index keys will be found in
-the appropriate nodes::
+the appropriate nodes:
+ =============== ======= ====
INDEX KEY PREFIX NODE
=============== ======= ====
13694892892489 13 C
@@ -422,12 +425,13 @@ the appropriate nodes::
138bbb89003093 138 E
1394879524789 12 C
1458952489 1 B
- 9431809de993ba - A
- b4542910809cd - A
+ 9431809de993ba \- A
+ b4542910809cd \- A
e5284310def98 e F
e68428974237 e6 G
e7fffcbd443 e F
- f3842239082 - A
+ f3842239082 \- A
+ =============== ======= ====
To save memory, if a node can hold all the leaves in its portion of keyspace,
then the node will have all those leaves in it and will not have any metadata
@@ -441,8 +445,9 @@ metadata pointer. If the metadata pointer is there, any leaf whose key matches
the metadata key prefix must be in the subtree that the metadata pointer points
to.
-In the above example list of index keys, node A will contain::
+In the above example list of index keys, node A will contain:
+ ==== =============== ==================
SLOT CONTENT INDEX KEY (PREFIX)
==== =============== ==================
1 PTR TO NODE B 1*
@@ -450,11 +455,16 @@ In the above example list of index keys, node A will contain::
any LEAF b4542910809cd
e PTR TO NODE F e*
any LEAF f3842239082
+ ==== =============== ==================
-and node B::
+and node B:
- 3 PTR TO NODE C 13*
- any LEAF 1458952489
+ ==== =============== ==================
+ SLOT CONTENT INDEX KEY (PREFIX)
+ ==== =============== ==================
+ 3 PTR TO NODE C 13*
+ any LEAF 1458952489
+ ==== =============== ==================
Shortcuts
diff --git a/Documentation/core-api/printk-formats.rst b/Documentation/core-api/printk-formats.rst
index 7f2f11b48286..c0b1b6089307 100644
--- a/Documentation/core-api/printk-formats.rst
+++ b/Documentation/core-api/printk-formats.rst
@@ -547,11 +547,13 @@ Time and date
%pt[RT]s YYYY-mm-dd HH:MM:SS
%pt[RT]d YYYY-mm-dd
%pt[RT]t HH:MM:SS
- %pt[RT][dt][r][s]
+ %ptSp <seconds>.<nanoseconds>
+ %pt[RST][dt][r][s]
For printing date and time as represented by::
- R struct rtc_time structure
+ R content of struct rtc_time
+ S content of struct timespec64
T time64_t type
in human readable format.
@@ -563,6 +565,11 @@ The %pt[RT]s (space) will override ISO 8601 separator by using ' ' (space)
instead of 'T' (Capital T) between date and time. It won't have any effect
when date or time is omitted.
+The %ptSp is equivalent to %lld.%09ld for the content of the struct timespec64.
+When the other specifiers are given, it becomes the respective equivalent of
+%ptT[dt][r][s].%09ld. In other words, the seconds are being printed in
+the human readable format followed by a dot and nanoseconds.
+
Passed by reference.
struct clk
diff --git a/Documentation/crypto/index.rst b/Documentation/crypto/index.rst
index 100b47d049c0..4ee667c446f9 100644
--- a/Documentation/crypto/index.rst
+++ b/Documentation/crypto/index.rst
@@ -27,3 +27,4 @@ for cryptographic use cases, as well as programming examples.
descore-readme
device_drivers/index
krb5
+ sha3
diff --git a/Documentation/crypto/sha3.rst b/Documentation/crypto/sha3.rst
new file mode 100644
index 000000000000..37640f295118
--- /dev/null
+++ b/Documentation/crypto/sha3.rst
@@ -0,0 +1,130 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+==========================
+SHA-3 Algorithm Collection
+==========================
+
+.. contents::
+
+Overview
+========
+
+The SHA-3 family of algorithms, as specified in NIST FIPS-202 [1]_, contains six
+algorithms based on the Keccak sponge function. The differences between them
+are: the "rate" (how much of the state buffer gets updated with new data between
+invocations of the Keccak function and analogous to the "block size"), what
+domain separation suffix gets appended to the input data, and how much output
+data is extracted at the end. The Keccak sponge function is designed such that
+arbitrary amounts of output can be obtained for certain algorithms.
+
+Four digest algorithms are provided:
+
+ - SHA3-224
+ - SHA3-256
+ - SHA3-384
+ - SHA3-512
+
+Additionally, two Extendable-Output Functions (XOFs) are provided:
+
+ - SHAKE128
+ - SHAKE256
+
+The SHA-3 library API supports all six of these algorithms. The four digest
+algorithms are also supported by the crypto_shash and crypto_ahash APIs.
+
+This document describes the SHA-3 library API.
+
+
+Digests
+=======
+
+The following functions compute SHA-3 digests::
+
+ void sha3_224(const u8 *in, size_t in_len, u8 out[SHA3_224_DIGEST_SIZE]);
+ void sha3_256(const u8 *in, size_t in_len, u8 out[SHA3_256_DIGEST_SIZE]);
+ void sha3_384(const u8 *in, size_t in_len, u8 out[SHA3_384_DIGEST_SIZE]);
+ void sha3_512(const u8 *in, size_t in_len, u8 out[SHA3_512_DIGEST_SIZE]);
+
+For users that need to pass in data incrementally, an incremental API is also
+provided. The incremental API uses the following struct::
+
+ struct sha3_ctx { ... };
+
+Initialization is done with one of::
+
+ void sha3_224_init(struct sha3_ctx *ctx);
+ void sha3_256_init(struct sha3_ctx *ctx);
+ void sha3_384_init(struct sha3_ctx *ctx);
+ void sha3_512_init(struct sha3_ctx *ctx);
+
+Input data is then added with any number of calls to::
+
+ void sha3_update(struct sha3_ctx *ctx, const u8 *in, size_t in_len);
+
+Finally, the digest is generated using::
+
+ void sha3_final(struct sha3_ctx *ctx, u8 *out);
+
+which also zeroizes the context. The length of the digest is determined by the
+initialization function that was called.
+
+
+Extendable-Output Functions
+===========================
+
+The following functions compute the SHA-3 extendable-output functions (XOFs)::
+
+ void shake128(const u8 *in, size_t in_len, u8 *out, size_t out_len);
+ void shake256(const u8 *in, size_t in_len, u8 *out, size_t out_len);
+
+For users that need to provide the input data incrementally and/or receive the
+output data incrementally, an incremental API is also provided. The incremental
+API uses the following struct::
+
+ struct shake_ctx { ... };
+
+Initialization is done with one of::
+
+ void shake128_init(struct shake_ctx *ctx);
+ void shake256_init(struct shake_ctx *ctx);
+
+Input data is then added with any number of calls to::
+
+ void shake_update(struct shake_ctx *ctx, const u8 *in, size_t in_len);
+
+Finally, the output data is extracted with any number of calls to::
+
+ void shake_squeeze(struct shake_ctx *ctx, u8 *out, size_t out_len);
+
+and telling it how much data should be extracted. Note that performing multiple
+squeezes, with the output laid consecutively in a buffer, gets exactly the same
+output as doing a single squeeze for the combined amount over the same buffer.
+
+More input data cannot be added after squeezing has started.
+
+Once all the desired output has been extracted, zeroize the context::
+
+ void shake_zeroize_ctx(struct shake_ctx *ctx);
+
+
+Testing
+=======
+
+To test the SHA-3 code, use sha3_kunit (CONFIG_CRYPTO_LIB_SHA3_KUNIT_TEST).
+
+Since the SHA-3 algorithms are FIPS-approved, when the kernel is booted in FIPS
+mode the SHA-3 library also performs a simple self-test. This is purely to meet
+a FIPS requirement. Normal testing done by kernel developers and integrators
+should use the much more comprehensive KUnit test suite instead.
+
+
+References
+==========
+
+.. [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
+
+
+API Function Reference
+======================
+
+.. kernel-doc:: include/crypto/sha3.h
diff --git a/Documentation/crypto/userspace-if.rst b/Documentation/crypto/userspace-if.rst
index f80f243e227e..8158b363cd98 100644
--- a/Documentation/crypto/userspace-if.rst
+++ b/Documentation/crypto/userspace-if.rst
@@ -302,10 +302,9 @@ follows:
Depending on the RNG type, the RNG must be seeded. The seed is provided
-using the setsockopt interface to set the key. For example, the
-ansi_cprng requires a seed. The DRBGs do not require a seed, but may be
-seeded. The seed is also known as a *Personalization String* in NIST SP 800-90A
-standard.
+using the setsockopt interface to set the key. The SP800-90A DRBGs do
+not require a seed, but may be seeded. The seed is also known as a
+*Personalization String* in NIST SP 800-90A standard.
Using the read()/recvmsg() system calls, random numbers can be obtained.
The kernel generates at most 128 bytes in one call. If user space
diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst
index d5c47e560324..dfaad0a279ff 100644
--- a/Documentation/dev-tools/checkpatch.rst
+++ b/Documentation/dev-tools/checkpatch.rst
@@ -461,16 +461,9 @@ Comments
line comments is::
/*
- * This is the preferred style
- * for multi line comments.
- */
-
- The networking comment style is a bit different, with the first line
- not empty like the former::
-
- /* This is the preferred comment style
- * for files in net/ and drivers/net/
- */
+ * This is the preferred style
+ * for multi line comments.
+ */
See: https://www.kernel.org/doc/html/latest/process/coding-style.html#commenting
diff --git a/Documentation/dev-tools/kunit/run_manual.rst b/Documentation/dev-tools/kunit/run_manual.rst
index 699d92885075..98e8d5b28808 100644
--- a/Documentation/dev-tools/kunit/run_manual.rst
+++ b/Documentation/dev-tools/kunit/run_manual.rst
@@ -35,6 +35,12 @@ or be built into the kernel.
a good way of quickly testing everything applicable to the current
config.
+ KUnit can be enabled or disabled at boot time, and this behavior is
+ controlled by the kunit.enable kernel parameter.
+ By default, kunit.enable is set to 1 because KUNIT_DEFAULT_ENABLED is
+ enabled by default. To ensure that tests are executed as expected,
+ verify that kunit.enable=1 at boot time.
+
Once we have built our kernel (and/or modules), it is simple to run
the tests. If the tests are built-in, they will run automatically on the
kernel boot. The results will be written to the kernel log (``dmesg``)
diff --git a/Documentation/devicetree/bindings/.yamllint b/Documentation/devicetree/bindings/.yamllint
index 532799501800..8f9dd18dfe04 100644
--- a/Documentation/devicetree/bindings/.yamllint
+++ b/Documentation/devicetree/bindings/.yamllint
@@ -30,7 +30,7 @@ rules:
document-start:
present: true
empty-lines:
- max: 3
+ max: 1
max-end: 1
empty-values:
forbid-in-block-mappings: true
diff --git a/Documentation/devicetree/bindings/Makefile b/Documentation/devicetree/bindings/Makefile
index 8390d6c00030..8d6f85f4455d 100644
--- a/Documentation/devicetree/bindings/Makefile
+++ b/Documentation/devicetree/bindings/Makefile
@@ -32,7 +32,8 @@ find_cmd = $(find_all_cmd) | \
sed 's|^$(srctree)/||' | \
grep -F -e "$(subst :," -e ",$(DT_SCHEMA_FILES))" | \
sed 's|^|$(srctree)/|'
-CHK_DT_EXAMPLES := $(patsubst $(srctree)/%.yaml,%.example.dtb, $(shell $(find_cmd)))
+CHK_DT_EXAMPLES := $(patsubst $(srctree)/%.yaml,%.example.dtb, \
+ $(shell $(find_cmd) | xargs grep -l '^examples:'))
quiet_cmd_yamllint = LINT $(src)
cmd_yamllint = ($(find_cmd) | \
diff --git a/Documentation/devicetree/bindings/arm/altera/socfpga-clk-manager.yaml b/Documentation/devicetree/bindings/arm/altera/socfpga-clk-manager.yaml
index a758f4bb2bb3..4683bd1293fa 100644
--- a/Documentation/devicetree/bindings/arm/altera/socfpga-clk-manager.yaml
+++ b/Documentation/devicetree/bindings/arm/altera/socfpga-clk-manager.yaml
@@ -27,17 +27,17 @@ properties:
additionalProperties: false
properties:
- "#address-cells":
+ '#address-cells':
const: 1
- "#size-cells":
+ '#size-cells':
const: 0
patternProperties:
- "^osc[0-9]$":
+ '^osc[0-9]$':
type: object
- "^[a-z0-9,_]+(clk|pll|clk_gate|clk_divided)(@[a-f0-9]+)?$":
+ '^[a-z0-9,_]+(clk|pll|clk_gate|clk_divided)(@[a-f0-9]+)?$':
type: object
$ref: '#/$defs/clock-props'
unevaluatedProperties: false
@@ -58,14 +58,14 @@ properties:
minItems: 1
maxItems: 5
- "#address-cells":
+ '#address-cells':
const: 1
- "#size-cells":
+ '#size-cells':
const: 0
patternProperties:
- "^[a-z0-9,_]+(clk|pll)(@[a-f0-9]+)?$":
+ '^[a-z0-9,_]+(clk|pll)(@[a-f0-9]+)?$':
type: object
$ref: '#/$defs/clock-props'
unevaluatedProperties: false
@@ -86,11 +86,11 @@ properties:
required:
- compatible
- clocks
- - "#clock-cells"
+ - '#clock-cells'
required:
- compatible
- - "#clock-cells"
+ - '#clock-cells'
required:
- compatible
@@ -104,7 +104,7 @@ $defs:
reg:
maxItems: 1
- "#clock-cells":
+ '#clock-cells':
const: 0
clk-gate:
diff --git a/Documentation/devicetree/bindings/arm/amd,seattle.yaml b/Documentation/devicetree/bindings/arm/amd,seattle.yaml
new file mode 100644
index 000000000000..7a3fc05b19eb
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/amd,seattle.yaml
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/amd,seattle.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AMD Seattle SoC Platforms
+
+maintainers:
+ - Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
+ - Tom Lendacky <thomas.lendacky@amd.com>
+
+properties:
+ $nodename:
+ const: "/"
+ compatible:
+ oneOf:
+ - description: Boards with AMD Seattle SoC
+ items:
+ - const: amd,seattle-overdrive
+ - const: amd,seattle
+
+additionalProperties: true
+...
diff --git a/Documentation/devicetree/bindings/arm/apm.yaml b/Documentation/devicetree/bindings/arm/apm.yaml
new file mode 100644
index 000000000000..ea0d362cea3a
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/apm.yaml
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/apm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: APM X-Gene SoC Platforms
+
+maintainers:
+ - Khuong Dinh <khuong@os.amperecomputing.com>
+
+properties:
+ $nodename:
+ const: "/"
+ compatible:
+ oneOf:
+ - description: Boards with X-Gene1 Soc
+ items:
+ - const: apm,mustang
+ - const: apm,xgene-storm
+
+ - description: Boards with X-Gene2 SoC
+ items:
+ - const: apm,merlin
+ - const: apm,xgene-shadowcat
+
+additionalProperties: true
+...
diff --git a/Documentation/devicetree/bindings/arm/lge.yaml b/Documentation/devicetree/bindings/arm/lge.yaml
new file mode 100644
index 000000000000..d983ef7fcbd6
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/lge.yaml
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/arm/lge.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LG Electronics SoC Platforms
+
+maintainers:
+ - Chanho Min <chanho.min@lge.com>
+
+properties:
+ $nodename:
+ const: "/"
+ compatible:
+ oneOf:
+ - description: Boards with LG1312 Soc
+ items:
+ - const: lge,lg1312-ref
+ - const: lge,lg1312
+
+ - description: Boards with LG1313 SoC
+ items:
+ - const: lge,lg1313-ref
+ - const: lge,lg1313
+
+additionalProperties: true
+...
diff --git a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt
deleted file mode 100644
index 72de11bd2ef0..000000000000
--- a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt
+++ /dev/null
@@ -1,146 +0,0 @@
-Marvell Armada AP80x System Controller
-======================================
-
-The AP806/AP807 is one of the two core HW blocks of the Marvell Armada
-7K/8K/931x SoCs. It contains system controllers, which provide several
-registers giving access to numerous features: clocks, pin-muxing and
-many other SoC configuration items. This DT binding allows to describe
-these system controllers.
-
-For the top level node:
- - compatible: must be: "syscon", "simple-mfd";
- - reg: register area of the AP80x system controller
-
-SYSTEM CONTROLLER 0
-===================
-
-Clocks:
--------
-
-
-The Device Tree node representing the AP806/AP807 system controller
-provides a number of clocks:
-
- - 0: reference clock of CPU cluster 0
- - 1: reference clock of CPU cluster 1
- - 2: fixed PLL at 1200 Mhz
- - 3: MSS clock, derived from the fixed PLL
-
-Required properties:
-
- - compatible: must be one of:
- * "marvell,ap806-clock"
- * "marvell,ap807-clock"
- - #clock-cells: must be set to 1
-
-Pinctrl:
---------
-
-For common binding part and usage, refer to
-Documentation/devicetree/bindings/pinctrl/marvell,mvebu-pinctrl.txt.
-
-Required properties:
-- compatible must be "marvell,ap806-pinctrl",
-
-Available mpp pins/groups and functions:
-Note: brackets (x) are not part of the mpp name for marvell,function and given
-only for more detailed description in this document.
-
-name pins functions
-================================================================================
-mpp0 0 gpio, sdio(clk), spi0(clk)
-mpp1 1 gpio, sdio(cmd), spi0(miso)
-mpp2 2 gpio, sdio(d0), spi0(mosi)
-mpp3 3 gpio, sdio(d1), spi0(cs0n)
-mpp4 4 gpio, sdio(d2), i2c0(sda)
-mpp5 5 gpio, sdio(d3), i2c0(sdk)
-mpp6 6 gpio, sdio(ds)
-mpp7 7 gpio, sdio(d4), uart1(rxd)
-mpp8 8 gpio, sdio(d5), uart1(txd)
-mpp9 9 gpio, sdio(d6), spi0(cs1n)
-mpp10 10 gpio, sdio(d7)
-mpp11 11 gpio, uart0(txd)
-mpp12 12 gpio, sdio(pw_off), sdio(hw_rst)
-mpp13 13 gpio
-mpp14 14 gpio
-mpp15 15 gpio
-mpp16 16 gpio
-mpp17 17 gpio
-mpp18 18 gpio
-mpp19 19 gpio, uart0(rxd), sdio(pw_off)
-
-GPIO:
------
-For common binding part and usage, refer to
-Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml.
-
-Required properties:
-
-- compatible: "marvell,armada-8k-gpio"
-
-- offset: offset address inside the syscon block
-
-Optional properties:
-
-- marvell,pwm-offset: offset address of PWM duration control registers inside
- the syscon block
-
-Example:
-ap_syscon: system-controller@6f4000 {
- compatible = "syscon", "simple-mfd";
- reg = <0x6f4000 0x1000>;
-
- ap_clk: clock {
- compatible = "marvell,ap806-clock";
- #clock-cells = <1>;
- };
-
- ap_pinctrl: pinctrl {
- compatible = "marvell,ap806-pinctrl";
- };
-
- ap_gpio: gpio {
- compatible = "marvell,armada-8k-gpio";
- offset = <0x1040>;
- ngpios = <19>;
- gpio-controller;
- #gpio-cells = <2>;
- gpio-ranges = <&ap_pinctrl 0 0 19>;
- marvell,pwm-offset = <0x10c0>;
- #pwm-cells = <2>;
- clocks = <&ap_clk 3>;
- };
-};
-
-SYSTEM CONTROLLER 1
-===================
-
-Cluster clocks:
----------------
-
-Device Tree Clock bindings for cluster clock of Marvell
-AP806/AP807. Each cluster contain up to 2 CPUs running at the same
-frequency.
-
-Required properties:
- - compatible: must be one of:
- * "marvell,ap806-cpu-clock"
- * "marvell,ap807-cpu-clock"
-- #clock-cells : should be set to 1.
-
-- clocks : shall be the input parent clock(s) phandle for the clock
- (one per cluster)
-
-- reg: register range associated with the cluster clocks
-
-ap_syscon1: system-controller@6f8000 {
- compatible = "marvell,armada-ap806-syscon1", "syscon", "simple-mfd";
- reg = <0x6f8000 0x1000>;
-
- cpu_clk: clock-cpu@278 {
- compatible = "marvell,ap806-cpu-clock";
- clocks = <&ap_clk 0>, <&ap_clk 1>;
- #clock-cells = <1>;
- reg = <0x278 0xa30>;
- };
-};
diff --git a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt
deleted file mode 100644
index 54ff9f218328..000000000000
--- a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt
+++ /dev/null
@@ -1,191 +0,0 @@
-Marvell Armada CP110 System Controller
-======================================
-
-The CP110 is one of the two core HW blocks of the Marvell Armada 7K/8K
-SoCs. It contains system controllers, which provide several registers
-giving access to numerous features: clocks, pin-muxing and many other
-SoC configuration items. This DT binding allows to describe these
-system controllers.
-
-For the top level node:
- - compatible: must be: "syscon", "simple-mfd";
- - reg: register area of the CP110 system controller
-
-SYSTEM CONTROLLER 0
-===================
-
-Clocks:
--------
-
-The Device Tree node representing this System Controller 0 provides a
-number of clocks:
-
- - a set of core clocks
- - a set of gateable clocks
-
-Those clocks can be referenced by other Device Tree nodes using two
-cells:
- - The first cell must be 0 or 1. 0 for the core clocks and 1 for the
- gateable clocks.
- - The second cell identifies the particular core clock or gateable
- clocks.
-
-The following clocks are available:
- - Core clocks
- - 0 0 APLL
- - 0 1 PPv2 core
- - 0 2 EIP
- - 0 3 Core
- - 0 4 NAND core
- - 0 5 SDIO core
- - Gateable clocks
- - 1 0 Audio
- - 1 1 Comm Unit
- - 1 2 NAND
- - 1 3 PPv2
- - 1 4 SDIO
- - 1 5 MG Domain
- - 1 6 MG Core
- - 1 7 XOR1
- - 1 8 XOR0
- - 1 9 GOP DP
- - 1 11 PCIe x1 0
- - 1 12 PCIe x1 1
- - 1 13 PCIe x4
- - 1 14 PCIe / XOR
- - 1 15 SATA
- - 1 16 SATA USB
- - 1 17 Main
- - 1 18 SD/MMC/GOP
- - 1 21 Slow IO (SPI, NOR, BootROM, I2C, UART)
- - 1 22 USB3H0
- - 1 23 USB3H1
- - 1 24 USB3 Device
- - 1 25 EIP150
- - 1 26 EIP197
-
-Required properties:
-
- - compatible: must be:
- "marvell,cp110-clock"
- - #clock-cells: must be set to 2
-
-Pinctrl:
---------
-
-For common binding part and usage, refer to the file
-Documentation/devicetree/bindings/pinctrl/marvell,mvebu-pinctrl.txt.
-
-Required properties:
-
-- compatible: "marvell,armada-7k-pinctrl", "marvell,armada-8k-cpm-pinctrl",
- "marvell,armada-8k-cps-pinctrl" or "marvell,cp115-standalone-pinctrl"
- depending on the specific variant of the SoC being used.
-
-Available mpp pins/groups and functions:
-Note: brackets (x) are not part of the mpp name for marvell,function and given
-only for more detailed description in this document.
-
-name pins functions
-================================================================================
-mpp0 0 gpio, dev(ale1), au(i2smclk), ge0(rxd3), tdm(pclk), ptp(pulse), mss_i2c(sda), uart0(rxd), sata0(present_act), ge(mdio)
-mpp1 1 gpio, dev(ale0), au(i2sdo_spdifo), ge0(rxd2), tdm(drx), ptp(clk), mss_i2c(sck), uart0(txd), sata1(present_act), ge(mdc)
-mpp2 2 gpio, dev(ad15), au(i2sextclk), ge0(rxd1), tdm(dtx), mss_uart(rxd), ptp(pclk_out), i2c1(sck), uart1(rxd), sata0(present_act), xg(mdc)
-mpp3 3 gpio, dev(ad14), au(i2slrclk), ge0(rxd0), tdm(fsync), mss_uart(txd), pcie(rstoutn), i2c1(sda), uart1(txd), sata1(present_act), xg(mdio)
-mpp4 4 gpio, dev(ad13), au(i2sbclk), ge0(rxctl), tdm(rstn), mss_uart(rxd), uart1(cts), pcie0(clkreq), uart3(rxd), ge(mdc)
-mpp5 5 gpio, dev(ad12), au(i2sdi), ge0(rxclk), tdm(intn), mss_uart(txd), uart1(rts), pcie1(clkreq), uart3(txd), ge(mdio)
-mpp6 6 gpio, dev(ad11), ge0(txd3), spi0(csn2), au(i2sextclk), sata1(present_act), pcie2(clkreq), uart0(rxd), ptp(pulse)
-mpp7 7 gpio, dev(ad10), ge0(txd2), spi0(csn1), spi1(csn1), sata0(present_act), led(data), uart0(txd), ptp(clk)
-mpp8 8 gpio, dev(ad9), ge0(txd1), spi0(csn0), spi1(csn0), uart0(cts), led(stb), uart2(rxd), ptp(pclk_out), synce1(clk)
-mpp9 9 gpio, dev(ad8), ge0(txd0), spi0(mosi), spi1(mosi), pcie(rstoutn), synce2(clk)
-mpp10 10 gpio, dev(readyn), ge0(txctl), spi0(miso), spi1(miso), uart0(cts), sata1(present_act)
-mpp11 11 gpio, dev(wen1), ge0(txclkout), spi0(clk), spi1(clk), uart0(rts), led(clk), uart2(txd), sata0(present_act)
-mpp12 12 gpio, dev(clk_out), nf(rbn1), spi1(csn1), ge0(rxclk)
-mpp13 13 gpio, dev(burstn), nf(rbn0), spi1(miso), ge0(rxctl), mss_spi(miso)
-mpp14 14 gpio, dev(bootcsn), dev(csn0), spi1(csn0), spi0(csn3), au(i2sextclk), spi0(miso), sata0(present_act), mss_spi(csn)
-mpp15 15 gpio, dev(ad7), spi1(mosi), spi0(mosi), mss_spi(mosi), ptp(pulse_cp2cp)
-mpp16 16 gpio, dev(ad6), spi1(clk), mss_spi(clk)
-mpp17 17 gpio, dev(ad5), ge0(txd3)
-mpp18 18 gpio, dev(ad4), ge0(txd2), ptp(clk_cp2cp)
-mpp19 19 gpio, dev(ad3), ge0(txd1), wakeup(out_cp2cp)
-mpp20 20 gpio, dev(ad2), ge0(txd0)
-mpp21 21 gpio, dev(ad1), ge0(txctl), sei(in_cp2cp)
-mpp22 22 gpio, dev(ad0), ge0(txclkout), wakeup(in_cp2cp)
-mpp23 23 gpio, dev(a1), au(i2smclk), link(rd_in_cp2cp)
-mpp24 24 gpio, dev(a0), au(i2slrclk)
-mpp25 25 gpio, dev(oen), au(i2sdo_spdifo)
-mpp26 26 gpio, dev(wen0), au(i2sbclk)
-mpp27 27 gpio, dev(csn0), spi1(miso), mss_gpio4, ge0(rxd3), spi0(csn4), ge(mdio), sata0(present_act), uart0(rts), rei(in_cp2cp)
-mpp28 28 gpio, dev(csn1), spi1(csn0), mss_gpio5, ge0(rxd2), spi0(csn5), pcie2(clkreq), ptp(pulse), ge(mdc), sata1(present_act), uart0(cts), led(data)
-mpp29 29 gpio, dev(csn2), spi1(mosi), mss_gpio6, ge0(rxd1), spi0(csn6), pcie1(clkreq), ptp(clk), mss_i2c(sda), sata0(present_act), uart0(rxd), led(stb)
-mpp30 30 gpio, dev(csn3), spi1(clk), mss_gpio7, ge0(rxd0), spi0(csn7), pcie0(clkreq), ptp(pclk_out), mss_i2c(sck), sata1(present_act), uart0(txd), led(clk)
-mpp31 31 gpio, dev(a2), mss_gpio4, pcie(rstoutn), ge(mdc)
-mpp32 32 gpio, mii(col), mii(txerr), mss_spi(miso), tdm(drx), au(i2sextclk), au(i2sdi), ge(mdio), sdio(v18_en), pcie1(clkreq), mss_gpio0
-mpp33 33 gpio, mii(txclk), sdio(pwr10), mss_spi(csn), tdm(fsync), au(i2smclk), sdio(bus_pwr), xg(mdio), pcie2(clkreq), mss_gpio1
-mpp34 34 gpio, mii(rxerr), sdio(pwr11), mss_spi(mosi), tdm(dtx), au(i2slrclk), sdio(wr_protect), ge(mdc), pcie0(clkreq), mss_gpio2
-mpp35 35 gpio, sata1(present_act), i2c1(sda), mss_spi(clk), tdm(pclk), au(i2sdo_spdifo), sdio(card_detect), xg(mdio), ge(mdio), pcie(rstoutn), mss_gpio3
-mpp36 36 gpio, synce2(clk), i2c1(sck), ptp(clk), synce1(clk), au(i2sbclk), sata0(present_act), xg(mdc), ge(mdc), pcie2(clkreq), mss_gpio5
-mpp37 37 gpio, uart2(rxd), i2c0(sck), ptp(pclk_out), tdm(intn), mss_i2c(sck), sata1(present_act), ge(mdc), xg(mdc), pcie1(clkreq), mss_gpio6, link(rd_out_cp2cp)
-mpp38 38 gpio, uart2(txd), i2c0(sda), ptp(pulse), tdm(rstn), mss_i2c(sda), sata0(present_act), ge(mdio), xg(mdio), au(i2sextclk), mss_gpio7, ptp(pulse_cp2cp)
-mpp39 39 gpio, sdio(wr_protect), au(i2sbclk), ptp(clk), spi0(csn1), sata1(present_act), mss_gpio0
-mpp40 40 gpio, sdio(pwr11), synce1(clk), mss_i2c(sda), au(i2sdo_spdifo), ptp(pclk_out), spi0(clk), uart1(txd), ge(mdio), sata0(present_act), mss_gpio1
-mpp41 41 gpio, sdio(pwr10), sdio(bus_pwr), mss_i2c(sck), au(i2slrclk), ptp(pulse), spi0(mosi), uart1(rxd), ge(mdc), sata1(present_act), mss_gpio2, rei(out_cp2cp)
-mpp42 42 gpio, sdio(v18_en), sdio(wr_protect), synce2(clk), au(i2smclk), mss_uart(txd), spi0(miso), uart1(cts), xg(mdc), sata0(present_act), mss_gpio4
-mpp43 43 gpio, sdio(card_detect), synce1(clk), au(i2sextclk), mss_uart(rxd), spi0(csn0), uart1(rts), xg(mdio), sata1(present_act), mss_gpio5, wakeup(out_cp2cp)
-mpp44 44 gpio, ge1(txd2), uart0(rts), ptp(clk_cp2cp)
-mpp45 45 gpio, ge1(txd3), uart0(txd), pcie(rstoutn)
-mpp46 46 gpio, ge1(txd1), uart1(rts)
-mpp47 47 gpio, ge1(txd0), spi1(clk), uart1(txd), ge(mdc)
-mpp48 48 gpio, ge1(txctl_txen), spi1(mosi), xg(mdc), wakeup(in_cp2cp)
-mpp49 49 gpio, ge1(txclkout), mii(crs), spi1(miso), uart1(rxd), ge(mdio), pcie0(clkreq), sdio(v18_en), sei(out_cp2cp)
-mpp50 50 gpio, ge1(rxclk), mss_i2c(sda), spi1(csn0), uart2(txd), uart0(rxd), xg(mdio), sdio(pwr11)
-mpp51 51 gpio, ge1(rxd0), mss_i2c(sck), spi1(csn1), uart2(rxd), uart0(cts), sdio(pwr10)
-mpp52 52 gpio, ge1(rxd1), synce1(clk), synce2(clk), spi1(csn2), uart1(cts), led(clk), pcie(rstoutn), pcie0(clkreq)
-mpp53 53 gpio, ge1(rxd2), ptp(clk), spi1(csn3), uart1(rxd), led(stb), sdio(led)
-mpp54 54 gpio, ge1(rxd3), synce2(clk), ptp(pclk_out), synce1(clk), led(data), sdio(hw_rst), sdio_wp(wr_protect)
-mpp55 55 gpio, ge1(rxctl_rxdv), ptp(pulse), sdio(led), sdio_cd(card_detect)
-mpp56 56 gpio, tdm(drx), au(i2sdo_spdifo), spi0(clk), uart1(rxd), sata1(present_act), sdio(clk)
-mpp57 57 gpio, mss_i2c(sda), ptp(pclk_out), tdm(intn), au(i2sbclk), spi0(mosi), uart1(txd), sata0(present_act), sdio(cmd)
-mpp58 58 gpio, mss_i2c(sck), ptp(clk), tdm(rstn), au(i2sdi), spi0(miso), uart1(cts), led(clk), sdio(d0)
-mpp59 59 gpio, mss_gpio7, synce2(clk), tdm(fsync), au(i2slrclk), spi0(csn0), uart0(cts), led(stb), uart1(txd), sdio(d1)
-mpp60 60 gpio, mss_gpio6, ptp(pulse), tdm(dtx), au(i2smclk), spi0(csn1), uart0(rts), led(data), uart1(rxd), sdio(d2)
-mpp61 61 gpio, mss_gpio5, ptp(clk), tdm(pclk), au(i2sextclk), spi0(csn2), uart0(txd), uart2(txd), sata1(present_act), ge(mdio), sdio(d3)
-mpp62 62 gpio, mss_gpio4, synce1(clk), ptp(pclk_out), sata1(present_act), spi0(csn3), uart0(rxd), uart2(rxd), sata0(present_act), ge(mdc)
-
-GPIO:
------
-
-For common binding part and usage, refer to
-Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml.
-
-Required properties:
-
-- compatible: "marvell,armada-8k-gpio"
-
-- offset: offset address inside the syscon block
-
-Example:
-
-CP110_LABEL(syscon0): system-controller@440000 {
- compatible = "syscon", "simple-mfd";
- reg = <0x440000 0x1000>;
-
- CP110_LABEL(clk): clock {
- compatible = "marvell,cp110-clock";
- #clock-cells = <2>;
- };
-
- CP110_LABEL(pinctrl): pinctrl {
- compatible = "marvell,armada-8k-cpm-pinctrl";
- };
-
- CP110_LABEL(gpio1): gpio@100 {
- compatible = "marvell,armada-8k-gpio";
- offset = <0x100>;
- ngpios = <32>;
- gpio-controller;
- #gpio-cells = <2>;
- gpio-ranges = <&CP110_LABEL(pinctrl) 0 0 32>;
- };
-
-};
diff --git a/Documentation/devicetree/bindings/arm/psci.yaml b/Documentation/devicetree/bindings/arm/psci.yaml
index 7360a2849b5b..6e2e0c551841 100644
--- a/Documentation/devicetree/bindings/arm/psci.yaml
+++ b/Documentation/devicetree/bindings/arm/psci.yaml
@@ -163,7 +163,6 @@ examples:
method = "smc";
};
-
- |+
// Case 3: PSCI v0.2 and PSCI v0.1.
diff --git a/Documentation/devicetree/bindings/ata/eswin,eic7700-ahci.yaml b/Documentation/devicetree/bindings/ata/eswin,eic7700-ahci.yaml
new file mode 100644
index 000000000000..6554e30018b3
--- /dev/null
+++ b/Documentation/devicetree/bindings/ata/eswin,eic7700-ahci.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/ata/eswin,eic7700-ahci.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Eswin EIC7700 SoC SATA Controller
+
+maintainers:
+ - Yulin Lu <luyulin@eswincomputing.com>
+ - Huan He <hehuan1@eswincomputing.com>
+
+description:
+ AHCI SATA controller embedded into the EIC7700 SoC is based on the DWC AHCI
+ SATA v5.00a IP core.
+
+select:
+ properties:
+ compatible:
+ const: eswin,eic7700-ahci
+ required:
+ - compatible
+
+allOf:
+ - $ref: snps,dwc-ahci-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: eswin,eic7700-ahci
+ - const: snps,dwc-ahci
+
+ clocks:
+ minItems: 2
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: aclk
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ const: arst
+
+ ports-implemented:
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - phys
+ - phy-names
+ - ports-implemented
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ sata@50420000 {
+ compatible = "eswin,eic7700-ahci", "snps,dwc-ahci";
+ reg = <0x50420000 0x10000>;
+ interrupt-parent = <&plic>;
+ interrupts = <58>;
+ clocks = <&clock 171>, <&clock 186>;
+ clock-names = "pclk", "aclk";
+ phys = <&sata_phy>;
+ phy-names = "sata-phy";
+ ports-implemented = <0x1>;
+ resets = <&reset 96>;
+ reset-names = "arst";
+ };
diff --git a/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml b/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml
index 4c848fcb5a5d..7707cbed2260 100644
--- a/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml
+++ b/Documentation/devicetree/bindings/ata/snps,dwc-ahci.yaml
@@ -33,6 +33,10 @@ properties:
- description: SPEAr1340 AHCI SATA device
const: snps,spear-ahci
+ iommus:
+ minItems: 1
+ maxItems: 3
+
patternProperties:
"^sata-port@[0-9a-e]$":
$ref: /schemas/ata/snps,dwc-ahci-common.yaml#/$defs/dwc-ahci-port
diff --git a/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml b/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml
index 24c939f59091..cd5c2a532a92 100644
--- a/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml
+++ b/Documentation/devicetree/bindings/bus/allwinner,sun8i-a23-rsb.yaml
@@ -43,7 +43,7 @@ properties:
maximum: 20000000
patternProperties:
- "^.*@[0-9a-fA-F]+$":
+ "@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
diff --git a/Documentation/devicetree/bindings/bus/cznic,moxtet.yaml b/Documentation/devicetree/bindings/bus/cznic,moxtet.yaml
new file mode 100644
index 000000000000..d340899ca5f1
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/cznic,moxtet.yaml
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bus/cznic,moxtet.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Turris Moxtet SPI bus
+
+maintainers:
+ - Marek Behún <kabel@kernel.org>
+
+description: >
+ Turris Mox module status and configuration bus (over SPI)
+
+ The driver finds the devices connected to the bus by itself, but it may be
+ needed to reference some of them from other parts of the device tree. In that
+ case the devices can be defined as subnodes of the moxtet node.
+
+properties:
+ compatible:
+ const: cznic,moxtet
+
+ reg:
+ maxItems: 1
+
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
+ spi-cpol: true
+
+ spi-cpha: true
+
+ spi-max-frequency: true
+
+ interrupt-controller: true
+
+ "#interrupt-cells":
+ const: 1
+
+ interrupts:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - "#address-cells"
+ - "#size-cells"
+ - spi-cpol
+ - spi-cpha
+ - interrupts
+ - interrupt-controller
+ - "#interrupt-cells"
+
+additionalProperties:
+ type: object
+
+ required:
+ - reg
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ moxtet@1 {
+ compatible = "cznic,moxtet";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+ spi-max-frequency = <10000000>;
+ spi-cpol;
+ spi-cpha;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ interrupt-parent = <&gpiosb>;
+ interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
+
+ gpio@0 {
+ compatible = "cznic,moxtet-gpio";
+ gpio-controller;
+ #gpio-cells = <2>;
+ reg = <0>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml b/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml
index 4adbb7afa889..6645352c7f6b 100644
--- a/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml
+++ b/Documentation/devicetree/bindings/bus/fsl,imx8qxp-pixel-link-msi-bus.yaml
@@ -70,7 +70,7 @@ properties:
- const: ahb
patternProperties:
- "^.*@[0-9a-f]+$":
+ "@[0-9a-f]+$":
description: Devices attached to the bus
type: object
diff --git a/Documentation/devicetree/bindings/bus/moxtet.txt b/Documentation/devicetree/bindings/bus/moxtet.txt
deleted file mode 100644
index fb50fc865336..000000000000
--- a/Documentation/devicetree/bindings/bus/moxtet.txt
+++ /dev/null
@@ -1,46 +0,0 @@
-Turris Mox module status and configuration bus (over SPI)
-
-Required properties:
- - compatible : Should be "cznic,moxtet"
- - #address-cells : Has to be 1
- - #size-cells : Has to be 0
- - spi-cpol : Required inverted clock polarity
- - spi-cpha : Required shifted clock phase
- - interrupts : Must contain reference to the shared interrupt line
- - interrupt-controller : Required
- - #interrupt-cells : Has to be 1
-
-For other required and optional properties of SPI slave nodes please refer to
-../spi/spi-bus.txt.
-
-Required properties of subnodes:
- - reg : Should be position on the Moxtet bus (how many Moxtet
- modules are between this module and CPU module, so
- either 0 or a positive integer)
-
-The driver finds the devices connected to the bus by itself, but it may be
-needed to reference some of them from other parts of the device tree. In that
-case the devices can be defined as subnodes of the moxtet node.
-
-Example:
-
- moxtet@1 {
- compatible = "cznic,moxtet";
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <1>;
- spi-max-frequency = <10000000>;
- spi-cpol;
- spi-cpha;
- interrupt-controller;
- #interrupt-cells = <1>;
- interrupt-parent = <&gpiosb>;
- interrupts = <5 IRQ_TYPE_EDGE_FALLING>;
-
- moxtet_sfp: gpio@0 {
- compatible = "cznic,moxtet-gpio";
- gpio-controller;
- #gpio-cells = <2>;
- reg = <0>;
- }
- };
diff --git a/Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml b/Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml
index d12b62a3a5a8..bf0af3424c9a 100644
--- a/Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml
+++ b/Documentation/devicetree/bindings/bus/st,stm32-etzpc.yaml
@@ -44,7 +44,7 @@ properties:
Contains the firewall ID associated to the peripheral.
patternProperties:
- "^.*@[0-9a-f]+$":
+ "@[0-9a-f]+$":
description: Peripherals
type: object
diff --git a/Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml b/Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml
index 20acd1a6b173..9eb0b48b4f51 100644
--- a/Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml
+++ b/Documentation/devicetree/bindings/bus/st,stm32mp25-rifsc.yaml
@@ -60,7 +60,7 @@ properties:
Contains the firewall ID associated to the peripheral.
patternProperties:
- "^.*@[0-9a-f]+$":
+ "@[0-9a-f]+$":
description: Peripherals
type: object
diff --git a/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-gates-clk.yaml b/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-gates-clk.yaml
index c4714d0fbe07..e588a7e8f260 100644
--- a/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-gates-clk.yaml
+++ b/Documentation/devicetree/bindings/clock/allwinner,sun4i-a10-gates-clk.yaml
@@ -132,7 +132,6 @@ examples:
"ahb_mp", "ahb_mali400";
};
-
- |
clk@1c20068 {
#clock-cells = <1>;
diff --git a/Documentation/devicetree/bindings/clock/marvell,ap80x-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,ap80x-clock.yaml
new file mode 100644
index 000000000000..43b0631ba167
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/marvell,ap80x-clock.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/marvell,ap80x-clock.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell Armada AP80x System Controller Clocks
+
+maintainers:
+ - Gregory Clement <gregory.clement@bootlin.com>
+ - Miquel Raynal <miquel.raynal@bootlin.com>
+
+description: >
+ The AP806/AP807 is one of the two core HW blocks of the Marvell Armada
+ 7K/8K/931x SoCs. It contains system controllers, which provide several
+ registers giving access to numerous features: clocks, pin-muxing and many
+ other SoC configuration items.
+
+properties:
+ compatible:
+ enum:
+ - marvell,ap806-clock
+ - marvell,ap806-cpu-clock
+ - marvell,ap807-clock
+ - marvell,ap807-cpu-clock
+
+ reg:
+ maxItems: 1
+
+ "#clock-cells":
+ const: 1
+
+ clocks:
+ items:
+ - description: cluster 0 parent clock phandle
+ - description: cluster 1 parent clock phandle
+
+required:
+ - compatible
+ - "#clock-cells"
+
+additionalProperties: false
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - marvell,ap806-cpu-clock
+ - marvell,ap807-cpu-clock
+ then:
+ required:
+ - clocks
diff --git a/Documentation/devicetree/bindings/clock/marvell,cp110-clock.yaml b/Documentation/devicetree/bindings/clock/marvell,cp110-clock.yaml
new file mode 100644
index 000000000000..ad0bc79b24c6
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/marvell,cp110-clock.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/clock/marvell,cp110-clock.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell Armada CP110 System Controller Clocks
+
+maintainers:
+ - Gregory Clement <gregory.clement@bootlin.com>
+ - Miquel Raynal <miquel.raynal@bootlin.com>
+
+description: >
+ The CP110 is one of the two core HW blocks of the Marvell Armada 7K/8K/931x
+ SoCs. It contains system controllers, which provide several registers giving
+ access to numerous features: clocks, pin-muxing and many other SoC
+ configuration items.
+
+properties:
+ compatible:
+ const: marvell,cp110-clock
+
+ "#clock-cells":
+ const: 2
+ description: >
+ The first cell must be 0 or 1. 0 for the core clocks and 1 for the
+ gateable clocks. The second cell identifies the particular core clock or
+ gateable clocks.
+
+ The following clocks are available:
+
+ - Core clocks
+ - 0 0 APLL
+ - 0 1 PPv2 core
+ - 0 2 EIP
+ - 0 3 Core
+ - 0 4 NAND core
+ - 0 5 SDIO core
+
+ - Gateable clocks
+ - 1 0 Audio
+ - 1 1 Comm Unit
+ - 1 2 NAND
+ - 1 3 PPv2
+ - 1 4 SDIO
+ - 1 5 MG Domain
+ - 1 6 MG Core
+ - 1 7 XOR1
+ - 1 8 XOR0
+ - 1 9 GOP DP
+ - 1 11 PCIe x1 0
+ - 1 12 PCIe x1 1
+ - 1 13 PCIe x4
+ - 1 14 PCIe / XOR
+ - 1 15 SATA
+ - 1 16 SATA USB
+ - 1 17 Main
+ - 1 18 SD/MMC/GOP
+ - 1 21 Slow IO (SPI, NOR, BootROM, I2C, UART)
+ - 1 22 USB3H0
+ - 1 23 USB3H1
+ - 1 24 USB3 Device
+ - 1 25 EIP150
+ - 1 26 EIP197
+
+required:
+ - compatible
+ - "#clock-cells"
+
+additionalProperties: false
diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.yaml b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.yaml
index a9ba21144a56..13bb616249a1 100644
--- a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.yaml
+++ b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.yaml
@@ -37,7 +37,7 @@ properties:
'#clock-cells':
const: 1
- "#reset-cells":
+ '#reset-cells':
const: 1
nvidia,external-memory-controller:
@@ -46,7 +46,7 @@ properties:
phandle of the external memory controller node
patternProperties:
- "^emc-timings-[0-9]+$":
+ '^emc-timings-[0-9]+$':
type: object
properties:
nvidia,ram-code:
@@ -56,7 +56,7 @@ patternProperties:
this timing set is used for
patternProperties:
- "^timing-[0-9]+$":
+ '^timing-[0-9]+$':
type: object
properties:
clock-frequency:
@@ -94,7 +94,7 @@ required:
- compatible
- reg
- '#clock-cells'
- - "#reset-cells"
+ - '#reset-cells'
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml b/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml
index bee2dd4b29bf..73cccc0df424 100644
--- a/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml
+++ b/Documentation/devicetree/bindings/clock/nvidia,tegra20-car.yaml
@@ -39,11 +39,11 @@ properties:
'#clock-cells':
const: 1
- "#reset-cells":
+ '#reset-cells':
const: 1
patternProperties:
- "^(sclk)|(pll-[cem])$":
+ '^(sclk)|(pll-[cem])$':
type: object
properties:
compatible:
@@ -76,7 +76,7 @@ required:
- compatible
- reg
- '#clock-cells'
- - "#reset-cells"
+ - '#reset-cells'
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml b/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml
index 90cd3feab5fa..ab97d4b7dba8 100644
--- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml
@@ -8,7 +8,7 @@ title: Qualcomm RPM Clock Controller
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
The clock enumerators are defined in <dt-bindings/clock/qcom,rpmcc.h> and
diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml
index bc2fd3761328..655154534c0f 100644
--- a/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml
+++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mssr.yaml
@@ -99,7 +99,6 @@ properties:
the datasheet.
const: 1
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml b/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml
index b44a76a958f4..b497c28e8094 100644
--- a/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml
+++ b/Documentation/devicetree/bindings/clock/xlnx,clocking-wizard.yaml
@@ -22,7 +22,6 @@ properties:
- xlnx,clocking-wizard-v6.0
- xlnx,versal-clk-wizard
-
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/crypto/amd,ccp-seattle-v1a.yaml b/Documentation/devicetree/bindings/crypto/amd,ccp-seattle-v1a.yaml
index 32bf3a1c3b42..5fb708471059 100644
--- a/Documentation/devicetree/bindings/crypto/amd,ccp-seattle-v1a.yaml
+++ b/Documentation/devicetree/bindings/crypto/amd,ccp-seattle-v1a.yaml
@@ -21,6 +21,9 @@ properties:
dma-coherent: true
+ iommus:
+ maxItems: 4
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
index 08fe6a707a37..c3408dcf5d20 100644
--- a/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
+++ b/Documentation/devicetree/bindings/crypto/qcom,inline-crypto-engine.yaml
@@ -13,6 +13,7 @@ properties:
compatible:
items:
- enum:
+ - qcom,kaanapali-inline-crypto-engine
- qcom,qcs8300-inline-crypto-engine
- qcom,sa8775p-inline-crypto-engine
- qcom,sc7180-inline-crypto-engine
diff --git a/Documentation/devicetree/bindings/crypto/qcom,prng.yaml b/Documentation/devicetree/bindings/crypto/qcom,prng.yaml
index ed7e16bd11d3..597441d94cf1 100644
--- a/Documentation/devicetree/bindings/crypto/qcom,prng.yaml
+++ b/Documentation/devicetree/bindings/crypto/qcom,prng.yaml
@@ -20,6 +20,7 @@ properties:
- qcom,ipq5332-trng
- qcom,ipq5424-trng
- qcom,ipq9574-trng
+ - qcom,kaanapali-trng
- qcom,qcs615-trng
- qcom,qcs8300-trng
- qcom,sa8255p-trng
diff --git a/Documentation/devicetree/bindings/crypto/qcom-qce.yaml b/Documentation/devicetree/bindings/crypto/qcom-qce.yaml
index e009cb712fb8..79d5be2548bc 100644
--- a/Documentation/devicetree/bindings/crypto/qcom-qce.yaml
+++ b/Documentation/devicetree/bindings/crypto/qcom-qce.yaml
@@ -45,6 +45,7 @@ properties:
- items:
- enum:
+ - qcom,kaanapali-qce
- qcom,qcs615-qce
- qcom,qcs8300-qce
- qcom,sa8775p-qce
diff --git a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-frontend.yaml b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-frontend.yaml
index 98e8240a05bd..995b3ef408b7 100644
--- a/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-frontend.yaml
+++ b/Documentation/devicetree/bindings/display/allwinner,sun4i-a10-display-frontend.yaml
@@ -121,5 +121,4 @@ examples:
};
};
-
...
diff --git a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-drc.yaml b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-drc.yaml
index 895506d93f4c..85a6086cc10e 100644
--- a/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-drc.yaml
+++ b/Documentation/devicetree/bindings/display/allwinner,sun6i-a31-drc.yaml
@@ -121,5 +121,4 @@ examples:
};
};
-
...
diff --git a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml
index 60fd927b5a06..c43b02ec884f 100644
--- a/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/allwinner,sun8i-a83t-dw-hdmi.yaml
@@ -142,7 +142,6 @@ then:
reset-names:
minItems: 2
-
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml
index cb0a90f02321..3ae45db85ea7 100644
--- a/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml
+++ b/Documentation/devicetree/bindings/display/amlogic,meson-vpu.yaml
@@ -25,7 +25,6 @@ description: |
M |-------|______|----|____________| |________________| | |
___|__________________________________________________________|_______________|
-
VIU: Video Input Unit
---------------------
diff --git a/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml b/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
index 6d11f5955b51..c1cefd547391 100644
--- a/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
+++ b/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
@@ -56,22 +56,12 @@ properties:
- const: cec
interrupts:
- items:
- - description: CEC TX interrupt
- - description: CEC RX interrupt
- - description: CEC stuck at low interrupt
- - description: Wake-up interrupt
- - description: Hotplug connected interrupt
- - description: Hotplug removed interrupt
+ minItems: 5
+ maxItems: 6
interrupt-names:
- items:
- - const: cec-tx
- - const: cec-rx
- - const: cec-low
- - const: wakeup
- - const: hpd-connected
- - const: hpd-removed
+ minItems: 5
+ maxItems: 6
ddc:
$ref: /schemas/types.yaml#/definitions/phandle
@@ -112,6 +102,61 @@ required:
additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - brcm,bcm2711-hdmi0
+ - brcm,bcm2711-hdmi1
+ then:
+ properties:
+ interrupts:
+ items:
+ - description: CEC TX interrupt
+ - description: CEC RX interrupt
+ - description: CEC stuck at low interrupt
+ - description: Wake-up interrupt
+ - description: Hotplug connected interrupt
+ - description: Hotplug removed interrupt
+ interrupt-names:
+ items:
+ - const: cec-tx
+ - const: cec-rx
+ - const: cec-low
+ - const: wakeup
+ - const: hpd-connected
+ - const: hpd-removed
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - brcm,bcm2712-hdmi0
+ - brcm,bcm2712-hdmi1
+ then:
+ properties:
+ interrupts:
+ items:
+ - description: CEC TX interrupt
+ - description: CEC RX interrupt
+ - description: CEC stuck at low interrupt
+ - description: Hotplug connected interrupt
+ - description: Hotplug removed interrupt
+ interrupts-names:
+ items:
+ - const: cec-tx
+ - const: cec-rx
+ - const: cec-low
+ - const: hpd-connected
+ - const: hpd-removed
+
+ required:
+ - interrupts
+ - interrupt-names
+
examples:
- |
hdmi0: hdmi@7ef00700 {
@@ -136,6 +181,9 @@ examples:
"hd";
clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
clock-names = "hdmi", "bvb", "audio", "cec";
+ interrupts = <0>, <1>, <2>, <3>, <4>, <5>;
+ interrupt-names = "cec-tx", "cec-rx", "cec-low", "wakeup",
+ "hpd-connected", "hpd-removed";
resets = <&dvp 0>;
ddc = <&ddc0>;
};
diff --git a/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml b/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
index f91c9dce2a44..9aca38a58a16 100644
--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
+++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
@@ -20,11 +20,20 @@ properties:
maxItems: 1
interrupts:
- maxItems: 1
+ minItems: 1
+ maxItems: 3
+
+ interrupt-names:
+ minItems: 1
+ maxItems: 3
clocks:
- maxItems: 1
- description: Core Clock
+ minItems: 1
+ maxItems: 2
+
+ clock-names:
+ minItems: 1
+ maxItems: 2
required:
- compatible
@@ -33,17 +42,68 @@ required:
additionalProperties: false
-if:
- properties:
- compatible:
- contains:
- enum:
- - brcm,bcm2711-hvs
- - brcm,bcm2712-hvs
-
-then:
- required:
- - clocks
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: brcm,bcm2711-hvs
+
+ then:
+ properties:
+ clocks:
+ items:
+ - description: Core Clock
+ interrupts:
+ maxItems: 1
+ clock-names: false
+ interrupt-names: false
+
+ required:
+ - clocks
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: brcm,bcm2712-hvs
+
+ then:
+ properties:
+ clocks:
+ minItems: 2
+ maxItems: 2
+ clock-names:
+ items:
+ - const: core
+ - const: disp
+ interrupts:
+ items:
+ - description: Channel 0 End of frame
+ - description: Channel 1 End of frame
+ - description: Channel 2 End of frame
+ interrupt-names:
+ items:
+ - const: ch0-eof
+ - const: ch1-eof
+ - const: ch2-eof
+ required:
+ - clocks
+ - clock-names
+ - interrupt-names
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: brcm,bcm2835-hvs
+
+ then:
+ properties:
+ interrupts:
+ maxItems: 1
+ clock-names: false
+ interrupt-names: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml
index 5bbe81862c8f..d29a0d06187e 100644
--- a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml
@@ -156,7 +156,6 @@ else:
adi,input-style: false
adi,input-justification: false
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,imx8mp-hdmi-tx.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,imx8mp-hdmi-tx.yaml
index 05442d437755..6211ab8bbb0e 100644
--- a/Documentation/devicetree/bindings/display/bridge/fsl,imx8mp-hdmi-tx.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/fsl,imx8mp-hdmi-tx.yaml
@@ -49,6 +49,10 @@ properties:
$ref: /schemas/graph.yaml#/properties/port
description: HDMI output port
+ port@2:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Parallel audio input port
+
required:
- port@0
- port@1
@@ -98,5 +102,13 @@ examples:
remote-endpoint = <&hdmi0_con>;
};
};
+
+ port@2 {
+ reg = <2>;
+
+ endpoint {
+ remote-endpoint = <&pai_to_hdmi_tx>;
+ };
+ };
};
};
diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
index ba644c30dcf4..17d1f97ce8c2 100644
--- a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
@@ -19,6 +19,7 @@ properties:
compatible:
enum:
- ite,it66121
+ - ite,it66122
- ite,it6610
reg:
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
index 0487bbffd7f7..4f7d3e9cf0c2 100644
--- a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
@@ -131,7 +131,6 @@ required:
additionalProperties: false
-
examples:
- |
lvds-encoder {
diff --git a/Documentation/devicetree/bindings/display/bridge/parade,ps8622.yaml b/Documentation/devicetree/bindings/display/bridge/parade,ps8622.yaml
index e6397ac2048b..235018a81e85 100644
--- a/Documentation/devicetree/bindings/display/bridge/parade,ps8622.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/parade,ps8622.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Parade PS8622/PS8625 DisplayPort to LVDS Converter
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml b/Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
index c167795c63f6..b95f10edd3a2 100644
--- a/Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
@@ -14,6 +14,9 @@ description: |
R-Car Gen4 SoCs. The encoder can operate in either DSI or CSI-2 mode, with up
to four data lanes.
+allOf:
+ - $ref: /schemas/display/dsi-controller.yaml#
+
properties:
compatible:
enum:
@@ -80,14 +83,14 @@ required:
- resets
- ports
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/r8a779a0-cpg-mssr.h>
#include <dt-bindings/power/r8a779a0-sysc.h>
- dsi0: dsi-encoder@fed80000 {
+ dsi@fed80000 {
compatible = "renesas,r8a779a0-dsi-csi2-tx";
reg = <0xfed80000 0x10000>;
power-domains = <&sysc R8A779A0_PD_ALWAYS_ON>;
@@ -117,4 +120,51 @@ examples:
};
};
};
+
+ - |
+ #include <dt-bindings/clock/r8a779g0-cpg-mssr.h>
+ #include <dt-bindings/power/r8a779g0-sysc.h>
+
+ dsi@fed80000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "renesas,r8a779g0-dsi-csi2-tx";
+ reg = <0xfed80000 0x10000>;
+ clocks = <&cpg CPG_MOD 415>,
+ <&cpg CPG_CORE R8A779G0_CLK_DSIEXT>,
+ <&cpg CPG_CORE R8A779G0_CLK_DSIREF>;
+ clock-names = "fck", "dsi", "pll";
+ power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
+ resets = <&cpg 415>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ };
+
+ port@1 {
+ reg = <1>;
+
+ dsi0port1_out: endpoint {
+ remote-endpoint = <&panel_in>;
+ data-lanes = <1 2>;
+ };
+ };
+ };
+
+ panel@0 {
+ reg = <0>;
+ compatible = "raspberrypi,dsi-7inch", "ilitek,ili9881c";
+ power-supply = <&vcc_lcd_reg>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&dsi0port1_out>;
+ };
+ };
+ };
+ };
...
diff --git a/Documentation/devicetree/bindings/display/bridge/sil,sii8620.yaml b/Documentation/devicetree/bindings/display/bridge/sil,sii8620.yaml
index 6d1a36b76fcb..a5fe46de3535 100644
--- a/Documentation/devicetree/bindings/display/bridge/sil,sii8620.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/sil,sii8620.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Silicon Image SiI8620 HDMI/MHL bridge
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
index 9ef587d46506..20c7e0a77802 100644
--- a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
@@ -27,7 +27,9 @@ properties:
- const: adi,adv7123
- enum:
- adi,adv7123
+ - asl-tek,cs5263
- dumb-vga-dac
+ - parade,ps185hdm
- radxa,ra620
- realtek,rtd2171
- ti,opa362
diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml
index b78f64c9c5f4..70f229dc4e0c 100644
--- a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml
@@ -123,7 +123,6 @@ properties:
- required:
- port@1
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/display/ilitek,ili9486.yaml b/Documentation/devicetree/bindings/display/ilitek,ili9486.yaml
index 9cc1fd0751cd..7d78edc403dc 100644
--- a/Documentation/devicetree/bindings/display/ilitek,ili9486.yaml
+++ b/Documentation/devicetree/bindings/display/ilitek,ili9486.yaml
@@ -54,7 +54,6 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
-
display@0{
compatible = "waveshare,rpi-lcd-35", "ilitek,ili9486";
reg = <0>;
diff --git a/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pai.yaml b/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pai.yaml
new file mode 100644
index 000000000000..4f99682a308d
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/imx/fsl,imx8mp-hdmi-pai.yaml
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/imx/fsl,imx8mp-hdmi-pai.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8MP HDMI Parallel Audio Interface
+
+maintainers:
+ - Shengjiu Wang <shengjiu.wang@nxp.com>
+
+description:
+ The HDMI TX Parallel Audio Interface (HTX_PAI) is a bridge between the
+ Audio Subsystem to the HDMI TX Controller.
+
+properties:
+ compatible:
+ const: fsl,imx8mp-hdmi-pai
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: apb
+
+ power-domains:
+ maxItems: 1
+
+ port:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Output to the HDMI TX controller.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - power-domains
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/imx8mp-clock.h>
+ #include <dt-bindings/power/imx8mp-power.h>
+
+ audio-bridge@32fc4800 {
+ compatible = "fsl,imx8mp-hdmi-pai";
+ reg = <0x32fc4800 0x800>;
+ interrupt-parent = <&irqsteer_hdmi>;
+ interrupts = <14>;
+ clocks = <&clk IMX8MP_CLK_HDMI_APB>;
+ clock-names = "apb";
+ power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_PAI>;
+
+ port {
+ pai_to_hdmi_tx: endpoint {
+ remote-endpoint = <&hdmi_tx_from_pai>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/display/msm/dp-controller.yaml b/Documentation/devicetree/bindings/display/msm/dp-controller.yaml
index aeb4e4f36044..ebda78db87a6 100644
--- a/Documentation/devicetree/bindings/display/msm/dp-controller.yaml
+++ b/Documentation/devicetree/bindings/display/msm/dp-controller.yaml
@@ -18,6 +18,7 @@ properties:
compatible:
oneOf:
- enum:
+ - qcom,glymur-dp
- qcom,sa8775p-dp
- qcom,sc7180-dp
- qcom,sc7280-dp
@@ -33,6 +34,11 @@ properties:
- items:
- enum:
+ - qcom,qcs8300-dp
+ - const: qcom,sa8775p-dp
+
+ - items:
+ - enum:
- qcom,sm6350-dp
- const: qcom,sc7180-dp
@@ -55,6 +61,12 @@ properties:
- items:
- enum:
+ - qcom,sm6150-dp
+ - const: qcom,sm8150-dp
+ - const: qcom,sm8350-dp
+
+ - items:
+ - enum:
- qcom,sm8750-dp
- const: qcom,sm8650-dp
@@ -195,9 +207,11 @@ allOf:
compatible:
contains:
enum:
+ - qcom,glymur-dp
- qcom,sa8775p-dp
- qcom,x1e80100-dp
then:
+ $ref: /schemas/sound/dai-common.yaml#
oneOf:
- required:
- aux-bus
@@ -239,6 +253,7 @@ allOf:
enum:
# these platforms support 2 streams MST on some interfaces,
# others are SST only
+ - qcom,glymur-dp
- qcom,sc8280xp-dp
- qcom,x1e80100-dp
then:
@@ -295,7 +310,7 @@ allOf:
minItems: 6
maxItems: 8
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/display/msm/gmu.yaml b/Documentation/devicetree/bindings/display/msm/gmu.yaml
index afc187935744..e32056ae0f5d 100644
--- a/Documentation/devicetree/bindings/display/msm/gmu.yaml
+++ b/Documentation/devicetree/bindings/display/msm/gmu.yaml
@@ -21,7 +21,7 @@ properties:
compatible:
oneOf:
- items:
- - pattern: '^qcom,adreno-gmu-[67][0-9][0-9]\.[0-9]$'
+ - pattern: '^qcom,adreno-gmu-[6-8][0-9][0-9]\.[0-9]$'
- const: qcom,adreno-gmu
- items:
- pattern: '^qcom,adreno-gmu-x[1-9][0-9][0-9]\.[0-9]$'
@@ -303,6 +303,64 @@ allOf:
properties:
compatible:
contains:
+ const: qcom,adreno-gmu-840.1
+ then:
+ properties:
+ reg:
+ items:
+ - description: Core GMU registers
+ reg-names:
+ items:
+ - const: gmu
+ clocks:
+ items:
+ - description: GPU AHB clock
+ - description: GMU clock
+ - description: GPU CX clock
+ - description: GPU MEMNOC clock
+ - description: GMU HUB clock
+ clock-names:
+ items:
+ - const: ahb
+ - const: gmu
+ - const: cxo
+ - const: memnoc
+ - const: hub
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,adreno-gmu-x285.1
+ then:
+ properties:
+ reg:
+ items:
+ - description: Core GMU registers
+ reg-names:
+ items:
+ - const: gmu
+ clocks:
+ items:
+ - description: GPU AHB clock
+ - description: GMU clock
+ - description: GPU CX clock
+ - description: GPU MEMNOC clock
+ - description: GMU HUB clock
+ - description: GMU RSCC HUB clock
+ clock-names:
+ items:
+ - const: ahb
+ - const: gmu
+ - const: cxo
+ - const: memnoc
+ - const: hub
+ - const: rscc
+
+ - if:
+ properties:
+ compatible:
+ contains:
const: qcom,adreno-gmu-wrapper
then:
properties:
diff --git a/Documentation/devicetree/bindings/display/msm/gpu.yaml b/Documentation/devicetree/bindings/display/msm/gpu.yaml
index 3696b083e353..826aafdcc20b 100644
--- a/Documentation/devicetree/bindings/display/msm/gpu.yaml
+++ b/Documentation/devicetree/bindings/display/msm/gpu.yaml
@@ -133,7 +133,6 @@ properties:
For GMU attached devices a phandle to the GMU device that will
control the power for the GPU.
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/display/msm/qcom,glymur-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,glymur-mdss.yaml
new file mode 100644
index 000000000000..2329ed96e6cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/msm/qcom,glymur-mdss.yaml
@@ -0,0 +1,264 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/msm/qcom,glymur-mdss.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Glymur Display MDSS
+
+maintainers:
+ - Abel Vesa <abel.vesa@linaro.org>
+
+description:
+ Glymur MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
+ DPU display controller, DP interfaces, etc.
+
+$ref: /schemas/display/msm/mdss-common.yaml#
+
+properties:
+ compatible:
+ const: qcom,glymur-mdss
+
+ clocks:
+ items:
+ - description: Display AHB
+ - description: Display hf AXI
+ - description: Display core
+
+ iommus:
+ maxItems: 1
+
+ interconnects:
+ items:
+ - description: Interconnect path from mdp0 port to the data bus
+ - description: Interconnect path from CPU to the reg bus
+
+ interconnect-names:
+ items:
+ - const: mdp0-mem
+ - const: cpu-cfg
+
+patternProperties:
+ "^display-controller@[0-9a-f]+$":
+ type: object
+ additionalProperties: true
+ properties:
+ compatible:
+ const: qcom,glymur-dpu
+
+ "^displayport-controller@[0-9a-f]+$":
+ type: object
+ additionalProperties: true
+ properties:
+ compatible:
+ const: qcom,glymur-dp
+
+ "^phy@[0-9a-f]+$":
+ type: object
+ additionalProperties: true
+ properties:
+ compatible:
+ const: qcom,glymur-dp-phy
+
+required:
+ - compatible
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,rpmh.h>
+ #include <dt-bindings/interconnect/qcom,icc.h>
+ #include <dt-bindings/interconnect/qcom,glymur-rpmh.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/phy/phy-qcom-qmp.h>
+ #include <dt-bindings/power/qcom,rpmhpd.h>
+
+ display-subsystem@ae00000 {
+ compatible = "qcom,glymur-mdss";
+ reg = <0x0ae00000 0x1000>;
+ reg-names = "mdss";
+
+ interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&dispcc_ahb_clk>,
+ <&gcc_disp_hf_axi_clk>,
+ <&dispcc_mdp_clk>;
+ clock-names = "bus", "nrt_bus", "core";
+
+ interconnects = <&mmss_noc MASTER_MDP QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
+ <&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
+ &config_noc SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
+ interconnect-names = "mdp0-mem",
+ "cpu-cfg";
+
+ resets = <&disp_cc_mdss_core_bcr>;
+
+ power-domains = <&mdss_gdsc>;
+
+ iommus = <&apps_smmu 0x1c00 0x2>;
+
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ display-controller@ae01000 {
+ compatible = "qcom,glymur-dpu";
+ reg = <0x0ae01000 0x8f000>,
+ <0x0aeb0000 0x2008>;
+ reg-names = "mdp", "vbif";
+
+ clocks = <&gcc_axi_clk>,
+ <&dispcc_ahb_clk>,
+ <&dispcc_mdp_lut_clk>,
+ <&dispcc_mdp_clk>,
+ <&dispcc_mdp_vsync_clk>;
+ clock-names = "nrt_bus",
+ "iface",
+ "lut",
+ "core",
+ "vsync";
+
+ assigned-clocks = <&dispcc_mdp_vsync_clk>;
+ assigned-clock-rates = <19200000>;
+
+ operating-points-v2 = <&mdp_opp_table>;
+ power-domains = <&rpmhpd RPMHPD_MMCX>;
+
+ interrupt-parent = <&mdss>;
+ interrupts = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ dpu_intf1_out: endpoint {
+ remote-endpoint = <&dsi0_in>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ dpu_intf2_out: endpoint {
+ remote-endpoint = <&dsi1_in>;
+ };
+ };
+ };
+
+ mdp_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-200000000 {
+ opp-hz = /bits/ 64 <200000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-325000000 {
+ opp-hz = /bits/ 64 <325000000>;
+ required-opps = <&rpmhpd_opp_svs>;
+ };
+
+ opp-375000000 {
+ opp-hz = /bits/ 64 <375000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>;
+ };
+
+ opp-514000000 {
+ opp-hz = /bits/ 64 <514000000>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+ };
+ };
+
+ displayport-controller@ae90000 {
+ compatible = "qcom,glymur-dp";
+ reg = <0xae90000 0x200>,
+ <0xae90200 0x200>,
+ <0xae90400 0x600>,
+ <0xae91000 0x400>,
+ <0xae91400 0x400>;
+
+ interrupt-parent = <&mdss>;
+ interrupts = <12>;
+
+ clocks = <&dispcc_mdss_ahb_clk>,
+ <&dispcc_dptx0_aux_clk>,
+ <&dispcc_dptx0_link_clk>,
+ <&dispcc_dptx0_link_intf_clk>,
+ <&dispcc_dptx0_pixel0_clk>,
+ <&dispcc_dptx0_pixel1_clk>;
+ clock-names = "core_iface",
+ "core_aux",
+ "ctrl_link",
+ "ctrl_link_iface",
+ "stream_pixel",
+ "stream_1_pixel";
+
+ assigned-clocks = <&dispcc_mdss_dptx0_link_clk_src>,
+ <&dispcc_mdss_dptx0_pixel0_clk_src>,
+ <&dispcc_mdss_dptx0_pixel1_clk_src>;
+ assigned-clock-parents = <&usb_1_ss0_qmpphy QMP_USB43DP_DP_LINK_CLK>,
+ <&usb_1_ss0_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>,
+ <&usb_1_ss0_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>;
+
+ operating-points-v2 = <&mdss_dp0_opp_table>;
+
+ power-domains = <&rpmhpd RPMHPD_MMCX>;
+
+ phys = <&usb_1_ss0_qmpphy QMP_USB43DP_DP_PHY>;
+ phy-names = "dp";
+
+ #sound-dai-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ mdss_dp0_in: endpoint {
+ remote-endpoint = <&mdss_intf0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ mdss_dp0_out: endpoint {
+ };
+ };
+ };
+
+ mdss_dp0_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-160000000 {
+ opp-hz = /bits/ 64 <160000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-270000000 {
+ opp-hz = /bits/ 64 <270000000>;
+ required-opps = <&rpmhpd_opp_svs>;
+ };
+
+ opp-540000000 {
+ opp-hz = /bits/ 64 <540000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>;
+ };
+
+ opp-810000000 {
+ opp-hz = /bits/ 64 <810000000>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcs8300-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcs8300-mdss.yaml
new file mode 100644
index 000000000000..e96baaae9ba9
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/msm/qcom,qcs8300-mdss.yaml
@@ -0,0 +1,286 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/msm/qcom,qcs8300-mdss.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm Technologies, Inc. QCS8300 Display MDSS
+
+maintainers:
+ - Yongxing Mou <yongxing.mou@oss.qualcomm.com>
+
+description:
+ QCS8300 MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
+ DPU display controller, DP interfaces and EDP etc.
+
+$ref: /schemas/display/msm/mdss-common.yaml#
+
+properties:
+ compatible:
+ const: qcom,qcs8300-mdss
+
+ clocks:
+ items:
+ - description: Display AHB
+ - description: Display hf AXI
+ - description: Display core
+
+ iommus:
+ maxItems: 1
+
+ interconnects:
+ maxItems: 3
+
+ interconnect-names:
+ maxItems: 3
+
+patternProperties:
+ "^display-controller@[0-9a-f]+$":
+ type: object
+ additionalProperties: true
+
+ properties:
+ compatible:
+ contains:
+ const: qcom,qcs8300-dpu
+
+ "^displayport-controller@[0-9a-f]+$":
+ type: object
+ additionalProperties: true
+
+ properties:
+ compatible:
+ contains:
+ const: qcom,qcs8300-dp
+
+ "^phy@[0-9a-f]+$":
+ type: object
+ additionalProperties: true
+ properties:
+ compatible:
+ contains:
+ const: qcom,qcs8300-edp-phy
+
+required:
+ - compatible
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interconnect/qcom,icc.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,qcs8300-gcc.h>
+ #include <dt-bindings/clock/qcom,sa8775p-dispcc.h>
+ #include <dt-bindings/interconnect/qcom,qcs8300-rpmh.h>
+ #include <dt-bindings/power/qcom,rpmhpd.h>
+ #include <dt-bindings/power/qcom-rpmpd.h>
+
+ mdss: display-subsystem@ae00000 {
+ compatible = "qcom,qcs8300-mdss";
+ reg = <0x0ae00000 0x1000>;
+ reg-names = "mdss";
+
+ interconnects = <&mmss_noc MASTER_MDP0 QCOM_ICC_TAG_ACTIVE_ONLY
+ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>,
+ <&mmss_noc MASTER_MDP1 QCOM_ICC_TAG_ACTIVE_ONLY
+ &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>,
+ <&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
+ &config_noc SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
+ interconnect-names = "mdp0-mem",
+ "mdp1-mem",
+ "cpu-cfg";
+
+ resets = <&dispcc_core_bcr>;
+ power-domains = <&dispcc_gdsc>;
+
+ clocks = <&dispcc_ahb_clk>,
+ <&gcc GCC_DISP_HF_AXI_CLK>,
+ <&dispcc_mdp_clk>;
+
+ interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ iommus = <&apps_smmu 0x1000 0x402>;
+
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ display-controller@ae01000 {
+ compatible = "qcom,qcs8300-dpu", "qcom,sa8775p-dpu";
+ reg = <0x0ae01000 0x8f000>,
+ <0x0aeb0000 0x2008>;
+ reg-names = "mdp", "vbif";
+
+ clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_MDP_LUT_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_MDP_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_VSYNC_CLK>;
+ clock-names = "nrt_bus",
+ "iface",
+ "lut",
+ "core",
+ "vsync";
+
+ assigned-clocks = <&dispcc0 MDSS_DISP_CC_MDSS_VSYNC_CLK>;
+ assigned-clock-rates = <19200000>;
+ operating-points-v2 = <&mdp_opp_table>;
+ power-domains = <&rpmhpd RPMHPD_MMCX>;
+
+ interrupt-parent = <&mdss>;
+ interrupts = <0>;
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+
+ dpu_intf0_out: endpoint {
+ remote-endpoint = <&mdss_dp0_in>;
+ };
+ };
+ };
+
+ mdp_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-375000000 {
+ opp-hz = /bits/ 64 <375000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>;
+ };
+
+ opp-500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+
+ opp-575000000 {
+ opp-hz = /bits/ 64 <575000000>;
+ required-opps = <&rpmhpd_opp_turbo>;
+ };
+
+ opp-650000000 {
+ opp-hz = /bits/ 64 <650000000>;
+ required-opps = <&rpmhpd_opp_turbo_l1>;
+ };
+ };
+ };
+
+ mdss_dp0_phy: phy@aec2a00 {
+ compatible = "qcom,qcs8300-edp-phy", "qcom,sa8775p-edp-phy";
+
+ reg = <0x0aec2a00 0x200>,
+ <0x0aec2200 0xd0>,
+ <0x0aec2600 0xd0>,
+ <0x0aec2000 0x1c8>;
+
+ clocks = <&dispcc MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>,
+ <&dispcc MDSS_DISP_CC_MDSS_AHB_CLK>;
+ clock-names = "aux",
+ "cfg_ahb";
+
+ #clock-cells = <1>;
+ #phy-cells = <0>;
+
+ vdda-phy-supply = <&vreg_l1c>;
+ vdda-pll-supply = <&vreg_l4a>;
+ };
+
+ displayport-controller@af54000 {
+ compatible = "qcom,qcs8300-dp", "qcom,sa8775p-dp";
+
+ pinctrl-0 = <&dp_hot_plug_det>;
+ pinctrl-names = "default";
+
+ reg = <0xaf54000 0x104>,
+ <0xaf54200 0x0c0>,
+ <0xaf55000 0x770>,
+ <0xaf56000 0x09c>,
+ <0xaf57000 0x09c>,
+ <0xaf58000 0x09c>,
+ <0xaf59000 0x09c>,
+ <0xaf5a000 0x23c>,
+ <0xaf5b000 0x23c>;
+
+ interrupt-parent = <&mdss>;
+ interrupts = <12>;
+ clocks = <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_LINK_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_LINK_INTF_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL0_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL1_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL2_CLK>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL3_CLK>;
+ clock-names = "core_iface",
+ "core_aux",
+ "ctrl_link",
+ "ctrl_link_iface",
+ "stream_pixel",
+ "stream_1_pixel",
+ "stream_2_pixel",
+ "stream_3_pixel";
+ assigned-clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_LINK_CLK_SRC>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL0_CLK_SRC>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL1_CLK_SRC>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL2_CLK_SRC>,
+ <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL3_CLK_SRC>;
+ assigned-clock-parents = <&mdss_dp0_phy 0>,
+ <&mdss_dp0_phy 1>,
+ <&mdss_dp0_phy 1>,
+ <&mdss_dp0_phy 1>;
+ phys = <&mdss_dp0_phy>;
+ phy-names = "dp";
+ operating-points-v2 = <&dp_opp_table>;
+ power-domains = <&rpmhpd RPMHPD_MMCX>;
+
+ #sound-dai-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ mdss_dp0_in: endpoint {
+ remote-endpoint = <&dpu_intf0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ mdss_dp_out: endpoint { };
+ };
+ };
+
+ dp_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-160000000 {
+ opp-hz = /bits/ 64 <160000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
+ };
+
+ opp-270000000 {
+ opp-hz = /bits/ 64 <270000000>;
+ required-opps = <&rpmhpd_opp_svs>;
+ };
+
+ opp-540000000 {
+ opp-hz = /bits/ 64 <540000000>;
+ required-opps = <&rpmhpd_opp_svs_l1>;
+ };
+
+ opp-810000000 {
+ opp-hz = /bits/ 64 <810000000>;
+ required-opps = <&rpmhpd_opp_nom>;
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm6150-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm6150-mdss.yaml
index 9ac24f99d3ad..46e9335f849f 100644
--- a/Documentation/devicetree/bindings/display/msm/qcom,sm6150-mdss.yaml
+++ b/Documentation/devicetree/bindings/display/msm/qcom,sm6150-mdss.yaml
@@ -51,6 +51,14 @@ patternProperties:
compatible:
const: qcom,sm6150-dpu
+ "^displayport-controller@[0-9a-f]+$":
+ type: object
+ additionalProperties: true
+ properties:
+ compatible:
+ contains:
+ const: qcom,sm6150-dp
+
"^dsi@[0-9a-f]+$":
type: object
additionalProperties: true
@@ -130,35 +138,37 @@ examples:
#size-cells = <0>;
port@0 {
- reg = <0>;
- dpu_intf0_out: endpoint {
- };
+ reg = <0>;
+
+ dpu_intf0_out: endpoint {
+ };
};
port@1 {
- reg = <1>;
- dpu_intf1_out: endpoint {
- remote-endpoint = <&mdss_dsi0_in>;
- };
+ reg = <1>;
+
+ dpu_intf1_out: endpoint {
+ remote-endpoint = <&mdss_dsi0_in>;
+ };
};
};
mdp_opp_table: opp-table {
compatible = "operating-points-v2";
- opp-19200000 {
- opp-hz = /bits/ 64 <19200000>;
- required-opps = <&rpmhpd_opp_low_svs>;
+ opp-192000000 {
+ opp-hz = /bits/ 64 <192000000>;
+ required-opps = <&rpmhpd_opp_low_svs>;
};
- opp-25600000 {
- opp-hz = /bits/ 64 <25600000>;
- required-opps = <&rpmhpd_opp_svs>;
+ opp-256000000 {
+ opp-hz = /bits/ 64 <256000000>;
+ required-opps = <&rpmhpd_opp_svs>;
};
opp-307200000 {
- opp-hz = /bits/ 64 <307200000>;
- required-opps = <&rpmhpd_opp_nom>;
+ opp-hz = /bits/ 64 <307200000>;
+ required-opps = <&rpmhpd_opp_nom>;
};
};
};
diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml
index 0a46120dd868..fe296e3186d0 100644
--- a/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml
+++ b/Documentation/devicetree/bindings/display/msm/qcom,sm8650-dpu.yaml
@@ -13,11 +13,17 @@ $ref: /schemas/display/msm/dpu-common.yaml#
properties:
compatible:
- enum:
- - qcom,sa8775p-dpu
- - qcom,sm8650-dpu
- - qcom,sm8750-dpu
- - qcom,x1e80100-dpu
+ oneOf:
+ - enum:
+ - qcom,glymur-dpu
+ - qcom,sa8775p-dpu
+ - qcom,sm8650-dpu
+ - qcom,sm8750-dpu
+ - qcom,x1e80100-dpu
+ - items:
+ - enum:
+ - qcom,qcs8300-dpu
+ - const: qcom,sa8775p-dpu
reg:
items:
diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm8750-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm8750-mdss.yaml
index 4151f475f3bc..d55fda9a523e 100644
--- a/Documentation/devicetree/bindings/display/msm/qcom,sm8750-mdss.yaml
+++ b/Documentation/devicetree/bindings/display/msm/qcom,sm8750-mdss.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8750 Display MDSS
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
SM8650 MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml
new file mode 100644
index 000000000000..02f7fb1f16dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/ilitek,il79900a.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/ilitek,il79900a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ilitek IL79900a based MIPI-DSI panels
+
+maintainers:
+ - Langyan Ye <yelangyan@huaqin.corp-partner.google.com>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - tianma,tl121bvms07-00
+ - const: ilitek,il79900a
+
+ reg:
+ maxItems: 1
+ description: DSI virtual channel used by the panel
+
+ enable-gpios:
+ maxItems: 1
+ description: GPIO specifier for the enable pin
+
+ avdd-supply:
+ description: Positive analog voltage supply (AVDD)
+
+ avee-supply:
+ description: Negative analog voltage supply (AVEE)
+
+ pp1800-supply:
+ description: 1.8V logic voltage supply
+
+ backlight: true
+
+required:
+ - compatible
+ - reg
+ - enable-gpios
+ - avdd-supply
+ - avee-supply
+ - pp1800-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "tianma,tl121bvms07-00", "ilitek,il79900a";
+ reg = <0>;
+ enable-gpios = <&pio 25 0>;
+ avdd-supply = <&reg_avdd>;
+ avee-supply = <&reg_avee>;
+ pp1800-supply = <&reg_pp1800>;
+ backlight = <&backlight>;
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml
index 434cc6af9c95..d979701a00a8 100644
--- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml
+++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml
@@ -20,9 +20,11 @@ properties:
- bananapi,lhr050h41
- bestar,bsd1218-a101kl68
- feixin,k101-im2byl02
+ - raspberrypi,dsi-5inch
- raspberrypi,dsi-7inch
- startek,kd050hdfia020
- tdo,tl050hdv35
+ - wanchanglong,w552946aaa
- wanchanglong,w552946aba
- const: ilitek,ili9881c
@@ -30,6 +32,7 @@ properties:
maxItems: 1
backlight: true
+ port: true
power-supply: true
reset-gpios: true
rotation: true
diff --git a/Documentation/devicetree/bindings/display/panel/lg,ld070wx3-sl01.yaml b/Documentation/devicetree/bindings/display/panel/lg,ld070wx3-sl01.yaml
new file mode 100644
index 000000000000..0f0b9079f199
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/lg,ld070wx3-sl01.yaml
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/lg,ld070wx3-sl01.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: LG Corporation 7" WXGA TFT LCD panel
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: lg,ld070wx3-sl01
+
+ reg:
+ maxItems: 1
+
+ vdd-supply: true
+ vcc-supply: true
+
+ backlight: true
+ port: true
+
+required:
+ - compatible
+ - vdd-supply
+ - vcc-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "lg,ld070wx3-sl01";
+ reg = <0>;
+
+ vdd-supply = <&vdd_3v3_lcd>;
+ vcc-supply = <&vcc_1v8_lcd>;
+
+ backlight = <&backlight>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
index 4388d5375851..dbc01e640895 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
@@ -59,6 +59,8 @@ properties:
# Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel
- jenson,bl-jt60050-01a
- tbs,a711-panel
+ # Winstar WF70A8SYJHLNGA 7" WSVGA (1024x600) color TFT LCD LVDS panel
+ - winstar,wf70a8syjhlnga
- const: panel-lvds
diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml
index 9b92a05791cc..8d668979b62d 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-simple-dsi.yaml
@@ -19,6 +19,9 @@ description: |
If the panel is more advanced a dedicated binding file is required.
+allOf:
+ - $ref: panel-common.yaml#
+
properties:
compatible:
@@ -42,8 +45,6 @@ properties:
- kingdisplay,kd097d04
# LG ACX467AKM-7 4.95" 1080×1920 LCD Panel
- lg,acx467akm-7
- # LG Corporation 7" WXGA TFT LCD panel
- - lg,ld070wx3-sl01
# LG Corporation 5" HD TFT LCD panel
- lg,lh500wx1-sd03
# Lincoln LCD197 5" 1080x1920 LCD panel
@@ -56,10 +57,6 @@ properties:
- panasonic,vvx10f034n00
# Samsung s6e3fa7 1080x2220 based AMS559NK06 AMOLED panel
- samsung,s6e3fa7-ams559nk06
- # Samsung s6e3fc2x01 1080x2340 AMOLED panel
- - samsung,s6e3fc2x01
- # Samsung sofef00 1080x2280 AMOLED panel
- - samsung,sofef00
# Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel
- tdo,tl070wsh30
@@ -72,31 +69,12 @@ properties:
reset-gpios: true
port: true
power-supply: true
- vddio-supply: true
-
-allOf:
- - $ref: panel-common.yaml#
- - if:
- properties:
- compatible:
- enum:
- - samsung,s6e3fc2x01
- - samsung,sofef00
- then:
- properties:
- power-supply: false
- required:
- - vddio-supply
- else:
- properties:
- vddio-supply: false
- required:
- - power-supply
additionalProperties: false
required:
- compatible
+ - power-supply
- reg
examples:
diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
index 2017428d8828..24e277b19094 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
@@ -184,6 +184,8 @@ properties:
- innolux,n156bge-l21
# Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel
- innolux,zj070na-01p
+ # JuTouch Technology Co.. 10" JT101TM023 WXGA (1280 x 800) LVDS panel
+ - jutouch,jt101tm023
# Kaohsiung Opto-Electronics Inc. 5.7" QVGA (320 x 240) TFT LCD panel
- koe,tx14d24vm1bpa
# Kaohsiung Opto-Electronics. TX31D200VM0BAA 12.3" HSXGA LVDS panel
@@ -268,6 +270,8 @@ properties:
- qiaodian,qd43003c0-40
# Shenzhen QiShenglong Industrialist Co., Ltd. Gopher 2b 4.3" 480(RGB)x272 TFT LCD panel
- qishenglong,gopher2b-lcd
+ # Raystar Optronics, Inc. RFF500F-AWH-DNN 5.0" TFT 840x480
+ - raystar,rff500f-awh-dnn
# Rocktech Displays Ltd. RK101II01D-CT 10.1" TFT 1280x800
- rocktech,rk101ii01d-ct
# Rocktech Display Ltd. RK070ER9427 800(RGB)x480 TFT LCD panel
@@ -276,6 +280,8 @@ properties:
- rocktech,rk043fn48h
# Samsung Electronics 10.1" WXGA (1280x800) TFT LCD panel
- samsung,ltl101al01
+ # Samsung Electronics 10.6" FWXGA (1366x768) TFT LCD panel
+ - samsung,ltl106al01
# Samsung Electronics 10.1" WSVGA TFT LCD panel
- samsung,ltn101nt05
# Satoz SAT050AT40H12R2 5.0" WVGA TFT LCD panel
diff --git a/Documentation/devicetree/bindings/display/panel/panel-timing.yaml b/Documentation/devicetree/bindings/display/panel/panel-timing.yaml
index aea69b84ca5d..8c9774458777 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-timing.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-timing.yaml
@@ -41,7 +41,6 @@ description: |
| | | v | |
+-------+----------+-------------------------------------+----------+
-
The following is the panel timings shown with time on the x-axis.
This matches the timing diagrams often found in data sheets.
diff --git a/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml b/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml
index 04f86e0cbac9..694037301583 100644
--- a/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml
+++ b/Documentation/devicetree/bindings/display/panel/ronbo,rb070d30.yaml
@@ -9,6 +9,9 @@ title: Ronbo RB070D30 DSI Display Panel
maintainers:
- Maxime Ripard <mripard@kernel.org>
+allOf:
+ - $ref: panel-common.yaml#
+
properties:
compatible:
const: ronbo,rb070d30
@@ -20,10 +23,6 @@ properties:
description: GPIO used for the power pin
maxItems: 1
- reset-gpios:
- description: GPIO used for the reset pin
- maxItems: 1
-
shlr-gpios:
description: GPIO used for the shlr pin (horizontal flip)
maxItems: 1
@@ -35,10 +34,6 @@ properties:
vcc-lcd-supply:
description: Power regulator
- backlight:
- description: Backlight used by the panel
- $ref: /schemas/types.yaml#/definitions/phandle
-
required:
- compatible
- power-gpios
@@ -47,5 +42,6 @@ required:
- shlr-gpios
- updn-gpios
- vcc-lcd-supply
+ - port
-additionalProperties: false
+unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml
index ccb574caed28..f1723e910252 100644
--- a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml
+++ b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml
@@ -33,6 +33,8 @@ properties:
- samsung,atna45dc02
# Samsung 15.6" 3K (2880x1620 pixels) eDP AMOLED panel
- samsung,atna56ac03
+ # Samsung 16.0" 3K (2880x1800 pixels) eDP AMOLED panel
+ - samsung,atna60cl08
- const: samsung,atna33xc20
enable-gpios: true
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,s6e3fc2x01.yaml b/Documentation/devicetree/bindings/display/panel/samsung,s6e3fc2x01.yaml
new file mode 100644
index 000000000000..d48354fb52ea
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/samsung,s6e3fc2x01.yaml
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/samsung,s6e3fc2x01.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung S6E3FC2X01 AMOLED DDIC
+
+description: The S6E3FC2X01 is display driver IC with connected panel.
+
+maintainers:
+ - David Heidelberg <david@ixit.cz>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - enum:
+ # Samsung 6.41 inch, 1080x2340 pixels, 19.5:9 ratio
+ - samsung,s6e3fc2x01-ams641rw
+ - const: samsung,s6e3fc2x01
+
+ reg:
+ maxItems: 1
+
+ reset-gpios: true
+
+ port: true
+
+ vddio-supply:
+ description: VDD regulator
+
+ vci-supply:
+ description: VCI regulator
+
+ poc-supply:
+ description: POC regulator
+
+required:
+ - compatible
+ - reset-gpios
+ - vddio-supply
+ - vci-supply
+ - poc-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "samsung,s6e3fc2x01-ams641rw", "samsung,s6e3fc2x01";
+ reg = <0>;
+
+ vddio-supply = <&vreg_l14a_1p88>;
+ vci-supply = <&s2dos05_buck1>;
+ poc-supply = <&s2dos05_ldo1>;
+
+ te-gpios = <&tlmm 10 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
+
+ pinctrl-0 = <&sde_dsi_active &sde_te_active_sleep>;
+ pinctrl-1 = <&sde_dsi_suspend &sde_te_active_sleep>;
+ pinctrl-names = "default", "sleep";
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&mdss_dsi0_out>;
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,sofef00.yaml b/Documentation/devicetree/bindings/display/panel/samsung,sofef00.yaml
new file mode 100644
index 000000000000..eeee3cac72e3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/samsung,sofef00.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/samsung,sofef00.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung SOFEF00 AMOLED DDIC
+
+description: The SOFEF00 is display driver IC with connected panel.
+
+maintainers:
+ - David Heidelberg <david@ixit.cz>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ items:
+ - enum:
+ # Samsung 6.01 inch, 1080x2160 pixels, 18:9 ratio
+ - samsung,sofef00-ams601nt22
+ # Samsung 6.28 inch, 1080x2280 pixels, 19:9 ratio
+ - samsung,sofef00-ams628nw01
+ - const: samsung,sofef00
+
+ reg:
+ maxItems: 1
+
+ poc-supply:
+ description: POC regulator
+
+ vci-supply:
+ description: VCI regulator
+
+ vddio-supply:
+ description: VDD regulator
+
+required:
+ - compatible
+ - reset-gpios
+ - poc-supply
+ - vci-supply
+ - vddio-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "samsung,sofef00-ams628nw01", "samsung,sofef00";
+ reg = <0>;
+
+ vddio-supply = <&vreg_l14a_1p88>;
+ vci-supply = <&s2dos05_buck1>;
+ poc-supply = <&s2dos05_ldo1>;
+
+ te-gpios = <&tlmm 10 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
+
+ pinctrl-0 = <&panel_active>;
+ pinctrl-1 = <&panel_suspend>;
+ pinctrl-names = "default", "sleep";
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&mdss_dsi0_out>;
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/panel/sharp,lq079l1sx01.yaml b/Documentation/devicetree/bindings/display/panel/sharp,lq079l1sx01.yaml
new file mode 100644
index 000000000000..08a35ebbbb3c
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/sharp,lq079l1sx01.yaml
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/sharp,lq079l1sx01.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sharp Microelectronics 7.9" WQXGA TFT LCD panel
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description: >
+ This panel requires a dual-channel DSI host to operate and it supports
+ only left-right split mode, where each channel drives the left or right
+ half of the screen and only video mode.
+
+ Each of the DSI channels controls a separate DSI peripheral.
+ The peripheral driven by the first link (DSI-LINK1), left one, is
+ considered the primary peripheral and controls the device.
+
+allOf:
+ - $ref: panel-common-dual.yaml#
+
+properties:
+ compatible:
+ const: sharp,lq079l1sx01
+
+ reg:
+ maxItems: 1
+
+ avdd-supply:
+ description: regulator that supplies the analog voltage
+
+ vddio-supply:
+ description: regulator that supplies the I/O voltage
+
+ vsp-supply:
+ description: positive boost supply regulator
+
+ vsn-supply:
+ description: negative boost supply regulator
+
+ reset-gpios:
+ maxItems: 1
+
+ backlight: true
+ ports: true
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - vddio-supply
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "sharp,lq079l1sx01";
+ reg = <0>;
+
+ reset-gpios = <&gpio 59 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&avdd_lcd>;
+ vddio-supply = <&vdd_lcd_io>;
+ vsp-supply = <&vsp_5v5_lcd>;
+ vsn-supply = <&vsn_5v5_lcd>;
+
+ backlight = <&backlight>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ panel_in0: endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ panel_in1: endpoint {
+ remote-endpoint = <&dsi1_out>;
+ };
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/display/panel/synaptics,td4300-panel.yaml b/Documentation/devicetree/bindings/display/panel/synaptics,td4300-panel.yaml
new file mode 100644
index 000000000000..152d94367130
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/synaptics,td4300-panel.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/synaptics,td4300-panel.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Synaptics TDDI Display Panel Controller
+
+maintainers:
+ - Kaustabh Chakraborty <kauschluss@disroot.org>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - syna,td4101-panel
+ - syna,td4300-panel
+
+ reg:
+ maxItems: 1
+
+ vio-supply:
+ description: core I/O voltage supply
+
+ vsn-supply:
+ description: negative voltage supply for analog circuits
+
+ vsp-supply:
+ description: positive voltage supply for analog circuits
+
+ backlight-gpios:
+ maxItems: 1
+ description: backlight enable GPIO
+
+ reset-gpios: true
+ width-mm: true
+ height-mm: true
+ panel-timing: true
+
+required:
+ - compatible
+ - reg
+ - width-mm
+ - height-mm
+ - panel-timing
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "syna,td4300-panel";
+ reg = <0>;
+
+ vio-supply = <&panel_vio_reg>;
+ vsn-supply = <&panel_vsn_reg>;
+ vsp-supply = <&panel_vsp_reg>;
+
+ backlight-gpios = <&gpd3 5 GPIO_ACTIVE_LOW>;
+ reset-gpios = <&gpd3 4 GPIO_ACTIVE_LOW>;
+
+ width-mm = <68>;
+ height-mm = <121>;
+
+ panel-timing {
+ clock-frequency = <144389520>;
+
+ hactive = <1080>;
+ hsync-len = <4>;
+ hfront-porch = <120>;
+ hback-porch = <32>;
+
+ vactive = <1920>;
+ vsync-len = <2>;
+ vfront-porch = <21>;
+ vback-porch = <4>;
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml b/Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml
index 59a373728e62..99db268eb9b3 100644
--- a/Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml
+++ b/Documentation/devicetree/bindings/display/panel/tpo,tpg110.yaml
@@ -38,7 +38,6 @@ description: |+
The serial protocol has line names that resemble I2C but the
protocol is not I2C but 3WIRE SPI.
-
allOf:
- $ref: panel-common.yaml#
- $ref: /schemas/spi/spi-peripheral-props.yaml#
diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
index 1e32d14b6edb..2cc66dcef870 100644
--- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
+++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
@@ -25,6 +25,9 @@ properties:
- enum:
- renesas,r9a07g054-du # RZ/V2L
- const: renesas,r9a07g044-du # RZ/G2L fallback
+ - items:
+ - const: renesas,r9a09g056-du # RZ/V2N
+ - const: renesas,r9a09g057-du # RZ/V2H(P) fallback
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
index a8a008717997..6345f0132d43 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-dp.yaml
@@ -125,7 +125,6 @@ examples:
power-domains = <&power RK3588_PD_VO0>;
#sound-dai-cells = <0>;
-
ports {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml
index c59df3c1a3f7..632b48bfabb9 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,dw-mipi-dsi.yaml
@@ -17,6 +17,7 @@ properties:
- rockchip,px30-mipi-dsi
- rockchip,rk3128-mipi-dsi
- rockchip,rk3288-mipi-dsi
+ - rockchip,rk3368-mipi-dsi
- rockchip,rk3399-mipi-dsi
- rockchip,rk3568-mipi-dsi
- rockchip,rv1126-mipi-dsi
@@ -73,6 +74,7 @@ allOf:
enum:
- rockchip,px30-mipi-dsi
- rockchip,rk3128-mipi-dsi
+ - rockchip,rk3368-mipi-dsi
- rockchip,rk3568-mipi-dsi
- rockchip,rv1126-mipi-dsi
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml
index 96b4b088eebe..d649808c59da 100644
--- a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml
@@ -113,6 +113,14 @@ properties:
description:
Additional HDMI QP related data is accessed through VO GRF regs.
+ frl-enable-gpios:
+ description:
+ Optional GPIO line to be asserted when operating in HDMI 2.1 FRL mode and
+ deasserted for HDMI 1.4/2.0 TMDS. It can be used to control external
+ voltage bias for HDMI data lines. When not present the HDMI encoder will
+ operate in TMDS mode only.
+ maxItems: 1
+
required:
- compatible
- reg
@@ -132,8 +140,10 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
+ #include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/power/rk3588-power.h>
#include <dt-bindings/reset/rockchip,rk3588-cru.h>
@@ -164,6 +174,7 @@ examples:
rockchip,grf = <&sys_grf>;
rockchip,vo-grf = <&vo1_grf>;
#sound-dai-cells = <0>;
+ frl-enable-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_LOW>;
ports {
#address-cells = <1>;
diff --git a/Documentation/devicetree/bindings/display/simple-framebuffer.yaml b/Documentation/devicetree/bindings/display/simple-framebuffer.yaml
index 296500f9da05..45ffdebc9d86 100644
--- a/Documentation/devicetree/bindings/display/simple-framebuffer.yaml
+++ b/Documentation/devicetree/bindings/display/simple-framebuffer.yaml
@@ -181,7 +181,6 @@ allOf:
required:
- amlogic,pipeline
-
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/dma/apm,xgene-storm-dma.yaml b/Documentation/devicetree/bindings/dma/apm,xgene-storm-dma.yaml
new file mode 100644
index 000000000000..9ca5f7848785
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/apm,xgene-storm-dma.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/apm,xgene-storm-dma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: APM X-Gene Storm SoC DMA
+
+maintainers:
+ - Khuong Dinh <khuong@os.amperecomputing.com>
+
+properties:
+ compatible:
+ const: apm,xgene-storm-dma
+
+ reg:
+ items:
+ - description: DMA control and status registers
+ - description: Descriptor ring control and status registers
+ - description: Descriptor ring command registers
+ - description: SoC efuse registers
+
+ interrupts:
+ items:
+ - description: DMA error reporting interrupt
+ - description: DMA channel 0 completion interrupt
+ - description: DMA channel 1 completion interrupt
+ - description: DMA channel 2 completion interrupt
+ - description: DMA channel 3 completion interrupt
+
+ clocks:
+ maxItems: 1
+
+ dma-coherent: true
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ dma@1f270000 {
+ compatible = "apm,xgene-storm-dma";
+ reg = <0x1f270000 0x10000>,
+ <0x1f200000 0x10000>,
+ <0x1b000000 0x400000>,
+ <0x1054a000 0x100>;
+ interrupts = <0x0 0x82 0x4>,
+ <0x0 0xb8 0x4>,
+ <0x0 0xb9 0x4>,
+ <0x0 0xba 0x4>,
+ <0x0 0xbb 0x4>;
+ dma-coherent;
+ clocks = <&dmaclk 0>;
+ };
diff --git a/Documentation/devicetree/bindings/dma/apm-xgene-dma.txt b/Documentation/devicetree/bindings/dma/apm-xgene-dma.txt
deleted file mode 100644
index c53e0b08032f..000000000000
--- a/Documentation/devicetree/bindings/dma/apm-xgene-dma.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-Applied Micro X-Gene SoC DMA nodes
-
-DMA nodes are defined to describe on-chip DMA interfaces in
-APM X-Gene SoC.
-
-Required properties for DMA interfaces:
-- compatible: Should be "apm,xgene-dma".
-- device_type: set to "dma".
-- reg: Address and length of the register set for the device.
- It contains the information of registers in the following order:
- 1st - DMA control and status register address space.
- 2nd - Descriptor ring control and status register address space.
- 3rd - Descriptor ring command register address space.
- 4th - Soc efuse register address space.
-- interrupts: DMA has 5 interrupts sources. 1st interrupt is
- DMA error reporting interrupt. 2nd, 3rd, 4th and 5th interrupts
- are completion interrupts for each DMA channels.
-- clocks: Reference to the clock entry.
-
-Optional properties:
-- dma-coherent : Present if dma operations are coherent
-
-Example:
- dmaclk: dmaclk@1f27c000 {
- compatible = "apm,xgene-device-clock";
- #clock-cells = <1>;
- clocks = <&socplldiv2 0>;
- reg = <0x0 0x1f27c000 0x0 0x1000>;
- reg-names = "csr-reg";
- clock-output-names = "dmaclk";
- };
-
- dma: dma@1f270000 {
- compatible = "apm,xgene-storm-dma";
- device_type = "dma";
- reg = <0x0 0x1f270000 0x0 0x10000>,
- <0x0 0x1f200000 0x0 0x10000>,
- <0x0 0x1b000000 0x0 0x400000>,
- <0x0 0x1054a000 0x0 0x100>;
- interrupts = <0x0 0x82 0x4>,
- <0x0 0xb8 0x4>,
- <0x0 0xb9 0x4>,
- <0x0 0xba 0x4>,
- <0x0 0xbb 0x4>;
- dma-coherent;
- clocks = <&dmaclk 0>;
- };
diff --git a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml
index c21a4f073f6c..18c0a7c18bc8 100644
--- a/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml
+++ b/Documentation/devicetree/bindings/dma/snps,dma-spear1340.yaml
@@ -22,7 +22,6 @@ properties:
- renesas,r9a06g032-dma
- const: renesas,rzn1-dma
-
"#dma-cells":
minimum: 3
maximum: 4
diff --git a/Documentation/devicetree/bindings/dma/stericsson,dma40.yaml b/Documentation/devicetree/bindings/dma/stericsson,dma40.yaml
index 7b94d24d5ef4..8b42d9880400 100644
--- a/Documentation/devicetree/bindings/dma/stericsson,dma40.yaml
+++ b/Documentation/devicetree/bindings/dma/stericsson,dma40.yaml
@@ -120,7 +120,6 @@ properties:
- description: LCPA memory base, deprecated, use eSRAM pool instead
deprecated: true
-
reg-names:
oneOf:
- items:
diff --git a/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma.yaml b/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma.yaml
index 11a289f1d505..598903354196 100644
--- a/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma.yaml
+++ b/Documentation/devicetree/bindings/dma/stm32/st,stm32-dma.yaml
@@ -48,7 +48,6 @@ description: |
by transfer completion. This must only be used on channels
managing transfers for STM32 USART/UART.
-
maintainers:
- Amelie Delaunay <amelie.delaunay@foss.st.com>
diff --git a/Documentation/devicetree/bindings/dts-coding-style.rst b/Documentation/devicetree/bindings/dts-coding-style.rst
index 202acac0507a..4a02ea60cbbe 100644
--- a/Documentation/devicetree/bindings/dts-coding-style.rst
+++ b/Documentation/devicetree/bindings/dts-coding-style.rst
@@ -120,7 +120,8 @@ The following order of properties in device nodes is preferred:
4. Standard/common properties (defined by common bindings, e.g. without
vendor-prefixes)
5. Vendor-specific properties
-6. "status" (if applicable)
+6. "status" (if applicable), preceded by a blank line if there is content
+ before the property
7. Child nodes, where each node is preceded with a blank line
The "status" property is by default "okay", thus it can be omitted.
@@ -150,6 +151,7 @@ Example::
#address-cells = <1>;
#size-cells = <1>;
vendor,custom-property = <2>;
+
status = "disabled";
child_node: child-class@100 {
@@ -165,6 +167,7 @@ Example::
vdd-1v8-supply = <&board_vreg4>;
vdd-3v3-supply = <&board_vreg2>;
vdd-12v-supply = <&board_vreg3>;
+
status = "okay";
}
diff --git a/Documentation/devicetree/bindings/edac/altr,socfpga-ecc-manager.yaml b/Documentation/devicetree/bindings/edac/altr,socfpga-ecc-manager.yaml
index 3d787dea0f14..136e8fccd429 100644
--- a/Documentation/devicetree/bindings/edac/altr,socfpga-ecc-manager.yaml
+++ b/Documentation/devicetree/bindings/edac/altr,socfpga-ecc-manager.yaml
@@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Altera SoCFPGA ECC Manager
maintainers:
- - Matthew Gerlach <matthew.gerlach@altera.com>
+ - Niravkumar L Rabara <niravkumarlaxmidas.rabara@altera.com>
description:
This binding describes the device tree nodes required for the Altera SoCFPGA
diff --git a/Documentation/devicetree/bindings/edac/apm,xgene-edac.yaml b/Documentation/devicetree/bindings/edac/apm,xgene-edac.yaml
index 9afc78254cc0..9637df7af3c8 100644
--- a/Documentation/devicetree/bindings/edac/apm,xgene-edac.yaml
+++ b/Documentation/devicetree/bindings/edac/apm,xgene-edac.yaml
@@ -97,7 +97,6 @@ patternProperties:
- reg
- memory-controller
-
'^edacpmd@':
description: PMD subnode
type: object
diff --git a/Documentation/devicetree/bindings/eeprom/at25.yaml b/Documentation/devicetree/bindings/eeprom/at25.yaml
index 00e0f07b44f8..e1599ce10916 100644
--- a/Documentation/devicetree/bindings/eeprom/at25.yaml
+++ b/Documentation/devicetree/bindings/eeprom/at25.yaml
@@ -25,6 +25,7 @@ properties:
oneOf:
- items:
- enum:
+ - anvo,anv32c81w
- anvo,anv32e61w
- atmel,at25256B
- fujitsu,mb85rs1mt
diff --git a/Documentation/devicetree/bindings/firmware/qemu,fw-cfg-mmio.yaml b/Documentation/devicetree/bindings/firmware/qemu,fw-cfg-mmio.yaml
index 3faae3236665..c6fc1d6e25da 100644
--- a/Documentation/devicetree/bindings/firmware/qemu,fw-cfg-mmio.yaml
+++ b/Documentation/devicetree/bindings/firmware/qemu,fw-cfg-mmio.yaml
@@ -23,7 +23,6 @@ description: |
The authoritative guest-side hardware interface documentation to the fw_cfg
device can be found in "docs/specs/fw_cfg.txt" in the QEMU source tree.
-
properties:
compatible:
const: qemu,fw-cfg-mmio
diff --git a/Documentation/devicetree/bindings/fpga/fpga-region.yaml b/Documentation/devicetree/bindings/fpga/fpga-region.yaml
index 7d2d3b7aa4b7..4c61461d6247 100644
--- a/Documentation/devicetree/bindings/fpga/fpga-region.yaml
+++ b/Documentation/devicetree/bindings/fpga/fpga-region.yaml
@@ -18,7 +18,6 @@ description: |
- Supported Use Models
- Constraints
-
Introduction
============
@@ -31,7 +30,6 @@ description: |
document isn't a replacement for any manufacturers specifications for FPGA
usage.
-
Terminology
===========
@@ -108,7 +106,6 @@ description: |
a soft logic bridge (Bridge0-2) in the FPGA. The contents of each PRR can be
reprogrammed independently while the rest of the system continues to function.
-
Sequence
========
@@ -124,7 +121,6 @@ description: |
When the overlay is removed, the child nodes will be removed and the FPGA Region
will disable the bridges.
-
FPGA Region
===========
@@ -170,7 +166,6 @@ description: |
hardware bridges remain enabled. The PR regions' bridges will be FPGA bridges
within the static image of the FPGA.
-
Supported Use Models
====================
diff --git a/Documentation/devicetree/bindings/fpga/lattice,ice40-fpga-mgr.yaml b/Documentation/devicetree/bindings/fpga/lattice,ice40-fpga-mgr.yaml
new file mode 100644
index 000000000000..5121c6120785
--- /dev/null
+++ b/Documentation/devicetree/bindings/fpga/lattice,ice40-fpga-mgr.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/fpga/lattice,ice40-fpga-mgr.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Lattice iCE40 FPGA Manager
+
+maintainers:
+ - Joel Holdsworth <joel@airwebreathe.org.uk>
+
+properties:
+ compatible:
+ const: lattice,ice40-fpga-mgr
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ minimum: 1000000
+ maximum: 25000000
+
+ cdone-gpios:
+ maxItems: 1
+ description: GPIO input connected to CDONE pin
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ Active-low GPIO output connected to CRESET_B pin. Note that unless the
+ GPIO is held low during startup, the FPGA will enter Master SPI mode and
+ drive SCK with a clock signal potentially jamming other devices on the bus
+ until the firmware is loaded.
+
+required:
+ - compatible
+ - reg
+ - spi-max-frequency
+ - cdone-gpios
+ - reset-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fpga@0 {
+ compatible = "lattice,ice40-fpga-mgr";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ cdone-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt b/Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt
deleted file mode 100644
index 4dc412437b08..000000000000
--- a/Documentation/devicetree/bindings/fpga/lattice-ice40-fpga-mgr.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-Lattice iCE40 FPGA Manager
-
-Required properties:
-- compatible: Should contain "lattice,ice40-fpga-mgr"
-- reg: SPI chip select
-- spi-max-frequency: Maximum SPI frequency (>=1000000, <=25000000)
-- cdone-gpios: GPIO input connected to CDONE pin
-- reset-gpios: Active-low GPIO output connected to CRESET_B pin. Note
- that unless the GPIO is held low during startup, the
- FPGA will enter Master SPI mode and drive SCK with a
- clock signal potentially jamming other devices on the
- bus until the firmware is loaded.
-
-Example:
- fpga: fpga@0 {
- compatible = "lattice,ice40-fpga-mgr";
- reg = <0>;
- spi-max-frequency = <1000000>;
- cdone-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
- reset-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
- };
diff --git a/Documentation/devicetree/bindings/gnss/gnss-common.yaml b/Documentation/devicetree/bindings/gnss/gnss-common.yaml
index d4430d2d6855..354c0524089c 100644
--- a/Documentation/devicetree/bindings/gnss/gnss-common.yaml
+++ b/Documentation/devicetree/bindings/gnss/gnss-common.yaml
@@ -31,8 +31,7 @@ properties:
maxItems: 1
timepulse-gpios:
- description: When a timepulse is provided to the GNSS device using a
- GPIO line, this is used.
+ description: Timepulse signal
maxItems: 1
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml b/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml
index c0c2bfaa606f..b349b7bc0412 100644
--- a/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml
+++ b/Documentation/devicetree/bindings/gnss/u-blox,neo-6m.yaml
@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/gnss/u-blox,neo-6m.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: U-blox GNSS Receiver
+title: u-blox GNSS receiver
allOf:
- $ref: gnss-common.yaml#
@@ -14,7 +14,7 @@ maintainers:
- Johan Hovold <johan@kernel.org>
description: >
- The U-blox GNSS receivers can use UART, DDC (I2C), SPI and USB interfaces.
+ The u-blox GNSS receivers can use UART, DDC (I2C), SPI and USB interfaces.
properties:
compatible:
@@ -36,6 +36,9 @@ properties:
reset-gpios:
maxItems: 1
+ safeboot-gpios:
+ maxItems: 1
+
vcc-supply:
description: >
Main voltage regulator
@@ -64,6 +67,7 @@ examples:
compatible = "u-blox,neo-8";
v-bckp-supply = <&gnss_v_bckp_reg>;
vcc-supply = <&gnss_vcc_reg>;
- reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
+ reset-gpios = <&gpio 1 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>;
+ safeboot-gpios = <&gpio 2 (GPIO_ACTIVE_LOW | GPIO_OPEN_DRAIN)>;
};
};
diff --git a/Documentation/devicetree/bindings/gpio/brcm,xgs-iproc-gpio.yaml b/Documentation/devicetree/bindings/gpio/brcm,xgs-iproc-gpio.yaml
index c213cb9ddb9f..5cfefbbea6ca 100644
--- a/Documentation/devicetree/bindings/gpio/brcm,xgs-iproc-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/brcm,xgs-iproc-gpio.yaml
@@ -66,5 +66,4 @@ examples:
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>;
};
-
...
diff --git a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml
index ab35bcf98101..23410aeca300 100644
--- a/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml
+++ b/Documentation/devicetree/bindings/gpio/fairchild,74hc595.yaml
@@ -22,7 +22,6 @@ description: |
___ ________
chip select# |___________________|
-
maintainers:
- Maxime Ripard <mripard@kernel.org>
diff --git a/Documentation/devicetree/bindings/gpio/gpio-mxs.yaml b/Documentation/devicetree/bindings/gpio/gpio-mxs.yaml
index aaf97124803f..fed1b06495ad 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-mxs.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-mxs.yaml
@@ -28,6 +28,7 @@ properties:
'#address-cells':
const: 1
+
'#size-cells':
const: 0
@@ -35,7 +36,7 @@ properties:
maxItems: 1
patternProperties:
- "^(?!gpio@)[^@]+@[0-9]+$":
+ '^(?!gpio@)[^@]+@[0-9]+$':
type: object
properties:
fsl,pinmux-ids:
@@ -93,7 +94,7 @@ patternProperties:
additionalProperties: false
- "^gpio@[0-9]+$":
+ '^gpio@[0-9]+$':
type: object
properties:
compatible:
@@ -110,10 +111,10 @@ patternProperties:
interrupt-controller: true
- "#interrupt-cells":
+ '#interrupt-cells':
const: 2
- "#gpio-cells":
+ '#gpio-cells':
const: 2
gpio-controller: true
@@ -123,8 +124,8 @@ patternProperties:
- reg
- interrupts
- interrupt-controller
- - "#interrupt-cells"
- - "#gpio-cells"
+ - '#interrupt-cells'
+ - '#gpio-cells'
- gpio-controller
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
index d78da7dd2a56..184432d24ea1 100644
--- a/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/microchip,mpfs-gpio.yaml
@@ -11,7 +11,10 @@ maintainers:
properties:
compatible:
- items:
+ oneOf:
+ - items:
+ - const: microchip,pic64gx-gpio
+ - const: microchip,mpfs-gpio
- enum:
- microchip,mpfs-gpio
- microchip,coregpio-rtl-v3
diff --git a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml
index ab2afc0e4153..bba6f5b6606f 100644
--- a/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/snps,dw-apb-gpio.yaml
@@ -111,8 +111,8 @@ additionalProperties: false
required:
- compatible
- reg
- - "#address-cells"
- - "#size-cells"
+ - '#address-cells'
+ - '#size-cells'
examples:
- |
diff --git a/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml b/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml
index 5e3e199fd9a4..96d50d14c071 100644
--- a/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/ti,twl4030-gpio.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
-$id: http://devicetree.org/schemas/ti,twl4030-gpio.yaml#
+$id: http://devicetree.org/schemas/gpio/ti,twl4030-gpio.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI TWL4030 GPIO controller
diff --git a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml
index c994177de940..3f4bbd57fc52 100644
--- a/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml
+++ b/Documentation/devicetree/bindings/gpio/trivial-gpio.yaml
@@ -22,6 +22,8 @@ properties:
- cznic,moxtet-gpio
- dlg,slg7xl45106
- fcs,fxl6408
+ - fsl,ls1046aqds-fpga-gpio-stat-pres2
+ - fsl,lx2160ardb-fpga-gpio-sfp
- gateworks,pld-gpio
- ibm,ppc4xx-gpio
- loongson,ls1x-gpio
diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
index be198182dbfe..db49b8ff8c74 100644
--- a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
@@ -22,6 +22,7 @@ properties:
- mediatek,mt8183-mali
- mediatek,mt8183b-mali
- mediatek,mt8186-mali
+ - mediatek,mt8365-mali
- realtek,rtd1619-mali
- renesas,r9a07g044-mali
- renesas,r9a07g054-mali
diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
index a5b4e0021758..bee9faf1d3f8 100644
--- a/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-valhall-csf.yaml
@@ -18,6 +18,8 @@ properties:
oneOf:
- items:
- enum:
+ - mediatek,mt8196-mali
+ - nxp,imx95-mali # G310
- rockchip,rk3588-mali
- const: arm,mali-valhall-csf # Mali Valhall GPU model/revision is fully discoverable
@@ -44,7 +46,9 @@ properties:
minItems: 1
items:
- const: core
- - const: coregroup
+ - enum:
+ - coregroup
+ - stacks
- const: stacks
mali-supply: true
@@ -91,7 +95,6 @@ required:
- interrupts
- interrupt-names
- clocks
- - mali-supply
additionalProperties: false
@@ -108,6 +111,29 @@ allOf:
power-domains:
maxItems: 1
power-domain-names: false
+ required:
+ - mali-supply
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt8196-mali
+ then:
+ properties:
+ mali-supply: false
+ sram-supply: false
+ operating-points-v2: false
+ power-domains:
+ maxItems: 1
+ power-domain-names: false
+ clocks:
+ maxItems: 2
+ clock-names:
+ items:
+ - const: core
+ - const: stacks
+ required:
+ - power-domains
examples:
- |
@@ -143,5 +169,17 @@ examples:
};
};
};
+ - |
+ gpu@48000000 {
+ compatible = "mediatek,mt8196-mali", "arm,mali-valhall-csf";
+ reg = <0x48000000 0x480000>;
+ clocks = <&gpufreq 0>, <&gpufreq 1>;
+ clock-names = "core", "stacks";
+ interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "job", "mmu", "gpu";
+ power-domains = <&gpufreq>;
+ };
...
diff --git a/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml b/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml
index c87d7bece0ec..225a6e1b7fcd 100644
--- a/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml
+++ b/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml
@@ -15,6 +15,16 @@ properties:
oneOf:
- items:
- enum:
+ - renesas,r8a7796-gpu
+ - renesas,r8a77961-gpu
+ - const: img,img-gx6250
+ - const: img,img-rogue
+ - items:
+ - const: renesas,r8a77965-gpu
+ - const: img,img-ge7800
+ - const: img,img-rogue
+ - items:
+ - enum:
- ti,am62-gpu
- const: img,img-axe-1-16m
# This deprecated element must be kept around to allow old kernels to
@@ -86,48 +96,56 @@ allOf:
properties:
compatible:
contains:
- const: img,img-axe-1-16m
+ enum:
+ - ti,am62-gpu
+ - ti,j721s2-gpu
then:
properties:
- power-domains:
- items:
- - description: Power domain A
- power-domain-names:
+ clocks:
maxItems: 1
- required:
- - power-domains
- - power-domain-names
- if:
properties:
compatible:
contains:
- const: thead,th1520-gpu
+ enum:
+ - img,img-ge7800
+ - img,img-gx6250
+ - thead,th1520-gpu
then:
properties:
clocks:
minItems: 3
clock-names:
minItems: 3
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: img,img-axe-1-16m
+ then:
+ properties:
power-domains:
- items:
- - description: The single, unified power domain for the GPU on the
- TH1520 SoC, integrating all internal IP power domains.
- power-domain-names: false
+ maxItems: 1
+ power-domain-names:
+ maxItems: 1
required:
- power-domains
+ - power-domain-names
- if:
properties:
compatible:
contains:
- const: img,img-bxs-4-64
+ enum:
+ - img,img-bxs-4-64
+ - img,img-ge7800
+ - img,img-gx6250
then:
properties:
power-domains:
- items:
- - description: Power domain A
- - description: Power domain B
+ minItems: 2
power-domain-names:
minItems: 2
required:
@@ -138,13 +156,16 @@ allOf:
properties:
compatible:
contains:
- enum:
- - ti,am62-gpu
- - ti,j721s2-gpu
+ const: thead,th1520-gpu
then:
properties:
- clocks:
- maxItems: 1
+ power-domains:
+ items:
+ - description: The single, unified power domain for the GPU on the
+ TH1520 SoC, integrating all internal IP power domains.
+ power-domain-names: false
+ required:
+ - power-domains
examples:
- |
diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml
index 152935334c76..3e3f49cf2f52 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2947.yaml
@@ -81,7 +81,6 @@ required:
- compatible
- reg
-
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
index f60e06ab7d0a..c2f7c6ee1a37 100644
--- a/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
+++ b/Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
@@ -93,7 +93,6 @@ allOf:
adi,fault-q:
default: 4
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml
new file mode 100644
index 000000000000..58c51626a9ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/apm,xgene-slimpro-hwmon.yaml
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/apm,xgene-slimpro-hwmon.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: APM X-Gene SLIMpro hwmon
+
+maintainers:
+ - Khuong Dinh <khuong@os.amperecomputing.com>
+
+properties:
+ compatible:
+ const: apm,xgene-slimpro-hwmon
+
+ mboxes:
+ maxItems: 1
+
+required:
+ - compatible
+ - mboxes
+
+additionalProperties: false
+
+examples:
+ - |
+ hwmon {
+ compatible = "apm,xgene-slimpro-hwmon";
+ mboxes = <&mailbox 7>;
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt b/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt
deleted file mode 100644
index 59b38557f1bb..000000000000
--- a/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-APM X-Gene hwmon driver
-
-APM X-Gene SOC sensors are accessed over the "SLIMpro" mailbox.
-
-Required properties :
- - compatible : should be "apm,xgene-slimpro-hwmon"
- - mboxes : use the label reference for the mailbox as the first parameter.
- The second parameter is the channel number.
-
-Example :
- hwmonslimpro {
- compatible = "apm,xgene-slimpro-hwmon";
- mboxes = <&mailbox 7>;
- };
diff --git a/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml b/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml
index 9e5ed901ae54..851fb16ec7fa 100644
--- a/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml
+++ b/Documentation/devicetree/bindings/hwmon/aspeed,g6-pwm-tach.yaml
@@ -18,8 +18,11 @@ description: |
properties:
compatible:
- enum:
- - aspeed,ast2600-pwm-tach
+ oneOf:
+ - items:
+ - const: aspeed,ast2700-pwm-tach
+ - const: aspeed,ast2600-pwm-tach
+ - const: aspeed,ast2600-pwm-tach
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/hwmon/max31785.txt b/Documentation/devicetree/bindings/hwmon/max31785.txt
deleted file mode 100644
index 106e08c56aaa..000000000000
--- a/Documentation/devicetree/bindings/hwmon/max31785.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Bindings for the Maxim MAX31785 Intelligent Fan Controller
-==========================================================
-
-Reference:
-
-https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
-
-The Maxim MAX31785 is a PMBus device providing closed-loop, multi-channel fan
-management with temperature and remote voltage sensing. Various fan control
-features are provided, including PWM frequency control, temperature hysteresis,
-dual tachometer measurements, and fan health monitoring.
-
-Required properties:
-- compatible : One of "maxim,max31785" or "maxim,max31785a"
-- reg : I2C address, one of 0x52, 0x53, 0x54, 0x55.
-
-Example:
-
- fans@52 {
- compatible = "maxim,max31785";
- reg = <0x52>;
- };
diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml
index b1ff496f87f9..558cbd251b0f 100644
--- a/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml
+++ b/Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml
@@ -20,7 +20,11 @@ description: >
properties:
compatible:
- const: maxim,max31790
+ enum:
+ - maxim,max31785
+ - maxim,max31785a
+ - maxim,max31785b
+ - maxim,max31790
reg:
maxItems: 1
@@ -31,11 +35,17 @@ properties:
resets:
maxItems: 1
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
"#pwm-cells":
const: 1
patternProperties:
- "^fan-[0-9]+$":
+ "^fan@[0-9]+$":
$ref: fan-common.yaml#
unevaluatedProperties: false
@@ -56,13 +66,17 @@ examples:
reg = <0x20>;
clocks = <&sys_clk>;
resets = <&reset 0>;
+ #address-cells = <1>;
#pwm-cells = <1>;
+ #size-cells = <0>;
- fan-0 {
+ fan@0 {
+ reg = <0x0>;
pwms = <&pwm_provider 1>;
};
- fan-1 {
+ fan@1 {
+ reg = <0x1>;
pwms = <&pwm_provider 2>;
};
};
diff --git a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml
index 1b871f166e79..164068ba069d 100644
--- a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml
+++ b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml
@@ -45,7 +45,6 @@ properties:
- ti,tmp461
- winbond,w83l771
-
interrupts:
items:
- description: |
diff --git a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml
index b8e500e6cd9f..dc8bc4c6df34 100644
--- a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml
+++ b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml
@@ -75,6 +75,7 @@ properties:
- const: murata,ncp15wl333
- const: murata,ncp03wf104
- const: murata,ncp15xh103
+ - const: murata,ncp18wm474
- const: samsung,1404-001221
# Deprecated "ntc," compatible strings
- const: ntc,ncp15wb473
diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml
new file mode 100644
index 000000000000..fa48af81e083
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max17616.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices MAX17616/MAX17616A Current-Limiter with PMBus Interface
+
+maintainers:
+ - Kim Seer Paller <kimseer.paller@analog.com>
+
+description: |
+ The MAX17616/MAX17616A is a 3V to 80V, 7A current-limiter with overvoltage,
+ surge, undervoltage, reverse polarity, and loss of ground protection. It allows
+ monitoring of input/output voltage, output current and temperature through the
+ PMBus serial interface.
+ Datasheet:
+ https://www.analog.com/en/products/max17616.html
+
+properties:
+ compatible:
+ const: adi,max17616
+
+ reg:
+ maxItems: 1
+
+ vcc-supply: true
+
+ interrupts:
+ description: Fault condition signal provided on SMBALERT pin.
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - vcc-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hwmon@16 {
+ compatible = "adi,max17616";
+ reg = <0x16>;
+ vcc-supply = <&vcc>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml b/Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
new file mode 100644
index 000000000000..aaf244790663
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/hwmon/st,tsc1641.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ST Microelectronics TSC1641 I2C power monitor
+
+maintainers:
+ - Igor Reznichenko <igor@reznichenko.net>
+
+description: |
+ TSC1641 is a 60 V, 16-bit high-precision power monitor with I2C and
+ MIPI I3C interface
+
+ Datasheets:
+ https://www.st.com/resource/en/datasheet/tsc1641.pdf
+
+properties:
+ compatible:
+ const: st,tsc1641
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ description: Optional alert interrupt.
+ maxItems: 1
+
+ shunt-resistor-micro-ohms:
+ description: Shunt resistor value in micro-ohms. Since device has internal
+ 16-bit RSHUNT register with 10 uOhm LSB, the maximum value is capped at
+ 655.35 mOhm.
+ minimum: 100
+ default: 1000
+ maximum: 655350
+
+ st,alert-polarity-active-high:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description: Default value is 0 which configures the normal polarity of the
+ ALERT pin, being active low open-drain. Setting this to 1 configures the
+ polarity of the ALERT pin to be inverted and active high open-drain.
+ Specify this property to set the alert polarity to active-high.
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ power-sensor@40 {
+ compatible = "st,tsc1641";
+ reg = <0x40>;
+ shunt-resistor-micro-ohms = <1000>;
+ st,alert-polarity-active-high;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml b/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml
index cba5b4a1b81f..0fe6ea190f60 100644
--- a/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml
+++ b/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml
@@ -20,7 +20,6 @@ description: |
https://www.ti.com/lit/gpn/tmp513
https://www.ti.com/lit/gpn/tmp512
-
properties:
compatible:
enum:
diff --git a/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
index ee7de53e1918..d57e4bf8f65f 100644
--- a/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
+++ b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
@@ -15,7 +15,6 @@ description: |
Datasheets:
https://www.ti.com/lit/gpn/tps23861
-
properties:
compatible:
enum:
diff --git a/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.yaml b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.yaml
index b6af924dee2e..d8610daa10cd 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.yaml
+++ b/Documentation/devicetree/bindings/i2c/i2c-mux-gpmux.yaml
@@ -27,7 +27,6 @@ description: |+
| '------' | | dev | | dev | | dev |
'------------' '-----' '-----' '-----'
-
allOf:
- $ref: /schemas/i2c/i2c-mux.yaml#
diff --git a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml
index 758d8f6321e1..06a04db3eda2 100644
--- a/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml
+++ b/Documentation/devicetree/bindings/i2c/qcom,i2c-qup.yaml
@@ -9,7 +9,7 @@ title: Qualcomm Universal Peripheral (QUP) I2C controller
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
allOf:
- $ref: /schemas/i2c/i2c-controller.yaml#
diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
index 17ce39c19ab1..f9a449fee2b0 100644
--- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
@@ -64,7 +64,6 @@ patternProperties:
required:
- reg
-
allOf:
- if:
properties:
diff --git a/Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml b/Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml
index 28139b676661..19cfffb39296 100644
--- a/Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml
+++ b/Documentation/devicetree/bindings/i2c/tsd,mule-i2c-mux.yaml
@@ -16,7 +16,6 @@ description: |
can be selected by writing the appropriate device number to an I2C config
register.
-
+--------------------------------------------------+
| Mule |
0x18| +---------------+ |
@@ -34,7 +33,6 @@ description: |
| |__/ +--------+ |
+--------------------------------------------------+
-
allOf:
- $ref: /schemas/i2c/i2c-mux.yaml#
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
index 8dae89ecb64d..b91bfb16ed6b 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
@@ -30,7 +30,6 @@ description: |
* https://www.analog.com/en/products/adaq4380-4.html
* https://www.analog.com/en/products/adaq4381-4.html
-
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
index 1180d2ffbf84..73c8e9c532f3 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
@@ -166,7 +166,6 @@ properties:
An example of backend can be found at
http://analogdevicesinc.github.io/hdl/library/axi_ad7606x/index.html
-
patternProperties:
"^channel@[1-8]$":
type: object
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml
index 9ee4d977c5ed..238a8c9c4143 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml
@@ -48,7 +48,6 @@ properties:
enum: [2500000, 4096000]
default: 4096000
-
'#io-channel-cells':
const: 1
diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
index bd429552d568..f22eba0250ee 100644
--- a/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/adi,ade9000.yaml
@@ -57,7 +57,6 @@ properties:
description: External clock source when not using crystal
maxItems: 1
-
"#clock-cells":
description:
ADE9000 can provide clock output via CLKOUT pin with external buffer.
diff --git a/Documentation/devicetree/bindings/iio/adc/cosmic,10001-adc.yaml b/Documentation/devicetree/bindings/iio/adc/cosmic,10001-adc.yaml
index 4e695b97d015..9ea44ce63f25 100644
--- a/Documentation/devicetree/bindings/iio/adc/cosmic,10001-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/cosmic,10001-adc.yaml
@@ -36,7 +36,6 @@ properties:
"#io-channel-cells":
const: 1
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
index 17bb60e18a1c..c4c4575d3fa9 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
@@ -456,7 +456,6 @@ patternProperties:
items:
minimum: 40
-
- if:
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
index 1caa896fce82..de91cb03fdc6 100644
--- a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
+++ b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml
@@ -57,7 +57,6 @@ description: |
4 | batt_dischrg_i
5 | ts_v
-
properties:
compatible:
oneOf:
diff --git a/Documentation/devicetree/bindings/iio/afe/voltage-divider.yaml b/Documentation/devicetree/bindings/iio/afe/voltage-divider.yaml
index 4151f99b42aa..9752d1450064 100644
--- a/Documentation/devicetree/bindings/iio/afe/voltage-divider.yaml
+++ b/Documentation/devicetree/bindings/iio/afe/voltage-divider.yaml
@@ -29,7 +29,6 @@ description: |
|
GND
-
properties:
compatible:
const: voltage-divider
diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,admv4420.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,admv4420.yaml
index 64f2352aac3d..ca40359a3944 100644
--- a/Documentation/devicetree/bindings/iio/frequency/adi,admv4420.yaml
+++ b/Documentation/devicetree/bindings/iio/frequency/adi,admv4420.yaml
@@ -37,7 +37,6 @@ required:
- compatible
- reg
-
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
diff --git a/Documentation/devicetree/bindings/iio/pressure/murata,zpa2326.yaml b/Documentation/devicetree/bindings/iio/pressure/murata,zpa2326.yaml
index c33640ddde58..886f4129c301 100644
--- a/Documentation/devicetree/bindings/iio/pressure/murata,zpa2326.yaml
+++ b/Documentation/devicetree/bindings/iio/pressure/murata,zpa2326.yaml
@@ -12,7 +12,6 @@ maintainers:
description: |
Pressure sensor from Murata with SPI and I2C bus interfaces.
-
properties:
compatible:
const: murata,zpa2326
diff --git a/Documentation/devicetree/bindings/iio/proximity/semtech,sx9324.yaml b/Documentation/devicetree/bindings/iio/proximity/semtech,sx9324.yaml
index 48f221463166..8fed45ee557b 100644
--- a/Documentation/devicetree/bindings/iio/proximity/semtech,sx9324.yaml
+++ b/Documentation/devicetree/bindings/iio/proximity/semtech,sx9324.yaml
@@ -78,7 +78,6 @@ properties:
minItems: 3
maxItems: 3
-
semtech,ph01-resolution:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [8, 16, 32, 64, 128, 256, 512, 1024]
diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
index 312febeeb3bb..a22725f7619b 100644
--- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
+++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
@@ -39,7 +39,6 @@ $defs:
- reg
- adi,sensor-type
-
properties:
compatible:
oneOf:
@@ -88,7 +87,7 @@ properties:
const: 0
patternProperties:
- "^thermocouple@":
+ '^thermocouple@':
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
@@ -146,7 +145,7 @@ patternProperties:
required:
- adi,custom-thermocouple
- "^diode@":
+ '^diode@':
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
@@ -191,7 +190,7 @@ patternProperties:
$ref: /schemas/types.yaml#/definitions/uint32
default: 0
- "^rtd@":
+ '^rtd@':
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: RTD sensor.
@@ -280,7 +279,7 @@ patternProperties:
type: boolean
dependencies:
- adi,current-rotate: [ "adi,rsense-share" ]
+ adi,current-rotate: [ 'adi,rsense-share' ]
- if:
properties:
@@ -290,7 +289,7 @@ patternProperties:
required:
- adi,custom-rtd
- "^thermistor@":
+ '^thermistor@':
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Thermistor sensor.
@@ -364,7 +363,7 @@ patternProperties:
- adi,rsense-handle
dependencies:
- adi,current-rotate: [ "adi,rsense-share" ]
+ adi,current-rotate: [ 'adi,rsense-share' ]
allOf:
- if:
@@ -392,7 +391,7 @@ patternProperties:
required:
- adi,custom-thermistor
- "^adc@":
+ '^adc@':
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Direct ADC sensor.
@@ -407,7 +406,7 @@ patternProperties:
description: Whether the sensor is single-ended.
type: boolean
- "^temp@":
+ '^temp@':
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Active analog temperature sensor.
@@ -437,7 +436,7 @@ patternProperties:
required:
- adi,custom-temp
- "^rsense@":
+ '^rsense@':
$ref: '#/$defs/sensor-node'
unevaluatedProperties: false
description: Sense resistor sensor.
@@ -476,7 +475,7 @@ allOf:
- adi,ltc2984
then:
patternProperties:
- "^temp@": false
+ '^temp@': false
examples:
- |
diff --git a/Documentation/devicetree/bindings/input/cypress,cyapa.yaml b/Documentation/devicetree/bindings/input/cypress,cyapa.yaml
index 29515151abe9..da629d511da1 100644
--- a/Documentation/devicetree/bindings/input/cypress,cyapa.yaml
+++ b/Documentation/devicetree/bindings/input/cypress,cyapa.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cypress All Points Addressable (APA) I2C Touchpad / Trackpad
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/input/ti,drv266x.yaml b/Documentation/devicetree/bindings/input/ti,drv266x.yaml
index da1818824373..1bce389d0e5c 100644
--- a/Documentation/devicetree/bindings/input/ti,drv266x.yaml
+++ b/Documentation/devicetree/bindings/input/ti,drv266x.yaml
@@ -37,7 +37,6 @@ examples:
- |
#include <dt-bindings/gpio/gpio.h>
-
i2c {
#address-cells = <1>;
#size-cells = <0>;
diff --git a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
index c593ae63d0ec..12256ae7df90 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/st,stmfts.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: ST-Microelectronics FingerTip touchscreen controller
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The ST-Microelectronics FingerTip device provides a basic touchscreen
diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml
index 256de140c03d..afa4d3539f5c 100644
--- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml
+++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Interconnect Bandwidth Monitor
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
Bandwidth Monitor measures current throughput on buses between various NoC
diff --git a/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml
index dad3ad2fd93b..da16d8e9bdc5 100644
--- a/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml
+++ b/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml
@@ -122,7 +122,6 @@ allOf:
required:
- reg
-
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.yaml
index 3d60d9e9e208..d0fad930de9d 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/amlogic,meson-gpio-intc.yaml
@@ -39,6 +39,9 @@ properties:
- amlogic,a4-gpio-ao-intc
- amlogic,a5-gpio-intc
- amlogic,c3-gpio-intc
+ - amlogic,s6-gpio-intc
+ - amlogic,s7-gpio-intc
+ - amlogic,s7d-gpio-intc
- amlogic,t7-gpio-intc
- const: amlogic,meson-gpio-intc
diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
index f3247a47f9ee..bfd30aae682b 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
@@ -305,7 +305,6 @@ examples:
};
};
-
device@0 {
reg = <0 4>;
interrupts = <1 1 4 &part0>;
diff --git a/Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2700-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2700-intc.yaml
index 55636d06a674..258d21fe6e35 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2700-intc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2700-intc.yaml
@@ -25,13 +25,14 @@ properties:
interrupt-controller: true
'#interrupt-cells':
- const: 2
+ const: 1
description:
The first cell is the IRQ number, the second cell is the trigger
type as defined in interrupt.txt in this directory.
interrupts:
- maxItems: 6
+ minItems: 1
+ maxItems: 10
description: |
Depend to which INTC0 or INTC1 used.
INTC0 and INTC1 are two kinds of interrupt controller with enable and raw
@@ -53,7 +54,6 @@ properties:
| |---...
+---------+---module31
-
required:
- compatible
- reg
@@ -74,13 +74,17 @@ examples:
interrupt-controller@12101b00 {
compatible = "aspeed,ast2700-intc-ic";
reg = <0 0x12101b00 0 0x10>;
- #interrupt-cells = <2>;
+ #interrupt-cells = <1>;
interrupt-controller;
interrupts = <GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 193 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 194 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 195 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 196 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>;
+ <GIC_SPI 197 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 199 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 200 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 201 IRQ_TYPE_LEVEL_HIGH>;
};
};
diff --git a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.yaml
index 5fda626c80ce..2ff390c1705b 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm2836-l1-intc.yaml
@@ -34,8 +34,6 @@ properties:
required:
- compatible
- reg
- - interrupt-controller
- - '#interrupt-cells'
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml
index c49688be1058..5c768c1e159c 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,irqsteer.yaml
@@ -20,6 +20,7 @@ properties:
- fsl,imx8qm-irqsteer
- fsl,imx8qxp-irqsteer
- fsl,imx94-irqsteer
+ - fsl,imx95-irqsteer
- const: fsl,imx-irqsteer
reg:
@@ -87,6 +88,7 @@ allOf:
- fsl,imx8mp-irqsteer
- fsl,imx8qm-irqsteer
- fsl,imx8qxp-irqsteer
+ - fsl,imx95-irqsteer
then:
required:
- power-domains
diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml
index fdc254f8d013..55b1ae863b91 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,vf610-mscm-ir.yaml
@@ -14,7 +14,6 @@ description:
Vybrid SoC's but is only really useful in dual core configurations (VF6xx
which comes with a Cortex-A5/Cortex-M4 combination).
-
maintainers:
- Frank Li <Frank.Li@nxp.com>
diff --git a/Documentation/devicetree/bindings/interrupt-controller/loongson,liointc.yaml b/Documentation/devicetree/bindings/interrupt-controller/loongson,liointc.yaml
index 60441f0c5d72..f63b23f48d8e 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/loongson,liointc.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/loongson,liointc.yaml
@@ -78,7 +78,6 @@ required:
- '#interrupt-cells'
- loongson,parent_int_map
-
unevaluatedProperties: false
if:
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,mtk-cirq.yaml b/Documentation/devicetree/bindings/interrupt-controller/mediatek,mtk-cirq.yaml
index fdcb4d8db818..20dfffb34f0c 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/mediatek,mtk-cirq.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,mtk-cirq.yaml
@@ -18,7 +18,6 @@ description:
flush command is executed. With CIRQ, MCUSYS can be completely turned off
to improve the system power consumption without losing interrupts.
-
properties:
compatible:
items:
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.yaml b/Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.yaml
index 4ff609faba32..d943ea820cdd 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/mscc,ocelot-icpu-intr.yaml
@@ -26,7 +26,6 @@ properties:
- mscc,ocelot-icpu-intr
- mscc,serval-icpu-intr
-
'#interrupt-cells':
const: 1
diff --git a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml
index f683d696909b..e80820cc55a7 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml
@@ -58,8 +58,10 @@ properties:
- const: andestech,nceplic100
- items:
- enum:
+ - anlogic,dr1v90-plic
- canaan,k210-plic
- eswin,eic7700-plic
+ - microchip,pic64gx-plic
- sifive,fu540-c000-plic
- spacemit,k1-plic
- starfive,jh7100-plic
@@ -76,6 +78,9 @@ properties:
- thead,th1520-plic
- const: thead,c900-plic
- items:
+ - const: ultrarisc,dp1000-plic
+ - const: ultrarisc,cp100-plic
+ - items:
- const: sifive,plic-1.0.0
- const: riscv,plic0
deprecated: true
diff --git a/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-mswi.yaml b/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-mswi.yaml
index d6fb08a54167..62fd220e126e 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-mswi.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-mswi.yaml
@@ -4,18 +4,23 @@
$id: http://devicetree.org/schemas/interrupt-controller/thead,c900-aclint-mswi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Sophgo sg2042 CLINT Machine-level Software Interrupt Device
+title: ACLINT Machine-level Software Interrupt Device
maintainers:
- Inochi Amaoto <inochiama@outlook.com>
properties:
compatible:
- items:
- - enum:
- - sophgo,sg2042-aclint-mswi
- - sophgo,sg2044-aclint-mswi
- - const: thead,c900-aclint-mswi
+ oneOf:
+ - items:
+ - enum:
+ - sophgo,sg2042-aclint-mswi
+ - sophgo,sg2044-aclint-mswi
+ - const: thead,c900-aclint-mswi
+ - items:
+ - enum:
+ - anlogic,dr1v90-aclint-mswi
+ - const: nuclei,ux900-aclint-mswi
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml b/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml
index c1ab865fcd64..d02c6886283a 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/thead,c900-aclint-sswi.yaml
@@ -30,6 +30,10 @@ properties:
- const: thead,c900-aclint-sswi
- items:
- const: mips,p8700-aclint-sswi
+ - items:
+ - enum:
+ - anlogic,dr1v90-aclint-sswi
+ - const: nuclei,ux900-aclint-sswi
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/interrupt-controller/ti,omap4-wugen-mpu.yaml b/Documentation/devicetree/bindings/interrupt-controller/ti,omap4-wugen-mpu.yaml
index 6e3d6e6d9e07..61b30a7732ec 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/ti,omap4-wugen-mpu.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/ti,omap4-wugen-mpu.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI OMAP4 Wake-up Generator
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: >
All TI OMAP4/5 (and their derivatives) are interrupt controllers that route
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
index 89495f094d52..cdbd23b5c08c 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.yaml
@@ -35,6 +35,8 @@ properties:
- description: Qcom SoCs implementing "qcom,smmu-500" and "arm,mmu-500"
items:
- enum:
+ - qcom,glymur-smmu-500
+ - qcom,kaanapali-smmu-500
- qcom,milos-smmu-500
- qcom,qcm2290-smmu-500
- qcom,qcs615-smmu-500
@@ -89,6 +91,8 @@ properties:
- description: Qcom Adreno GPUs implementing "qcom,smmu-500" and "arm,mmu-500"
items:
- enum:
+ - qcom,glymur-smmu-500
+ - qcom,kaanapali-smmu-500
- qcom,milos-smmu-500
- qcom,qcm2290-smmu-500
- qcom,qcs615-smmu-500
@@ -638,7 +642,6 @@ examples:
<&smmu1 7>;
};
-
/* SMMU with stream matching */
smmu2: iommu@ba5f0000 {
compatible = "arm,smmu-v1";
@@ -664,7 +667,6 @@ examples:
iommus = <&smmu2 1 0x30>;
};
-
/* ARM MMU-500 with 10-bit stream ID input configuration */
smmu3: iommu@ba600000 {
compatible = "arm,mmu-500", "arm,smmu-v2";
@@ -685,8 +687,6 @@ examples:
/* bus whose child devices emit one unique 10-bit stream
ID each, but may master through multiple SMMU TBUs */
iommu-map = <0 &smmu3 0 0x400>;
-
-
};
- |+
diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
index f49ed8ac4776..79c573c47b08 100644
--- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
@@ -82,6 +82,9 @@ properties:
- mediatek,mt8188-iommu-vdo # generation two
- mediatek,mt8188-iommu-vpp # generation two
- mediatek,mt8188-iommu-infra # generation two
+ - mediatek,mt8189-iommu-apu # generation two
+ - mediatek,mt8189-iommu-infra # generation two
+ - mediatek,mt8189-iommu-mm # generation two
- mediatek,mt8192-m4u # generation two
- mediatek,mt8195-iommu-vdo # generation two
- mediatek,mt8195-iommu-vpp # generation two
@@ -128,6 +131,7 @@ properties:
This is the mtk_m4u_id according to the HW. Specifies the mtk_m4u_id as
defined in
dt-binding/memory/mediatek,mt8188-memory-port.h for mt8188,
+ dt-binding/memory/mediatek,mt8189-memory-port.h for mt8189,
dt-binding/memory/mt2701-larb-port.h for mt2701 and mt7623,
dt-binding/memory/mt2712-larb-port.h for mt2712,
dt-binding/memory/mt6779-larb-port.h for mt6779,
@@ -164,6 +168,7 @@ allOf:
- mediatek,mt8186-iommu-mm
- mediatek,mt8188-iommu-vdo
- mediatek,mt8188-iommu-vpp
+ - mediatek,mt8189-iommu-mm
- mediatek,mt8192-m4u
- mediatek,mt8195-iommu-vdo
- mediatek,mt8195-iommu-vpp
@@ -180,6 +185,7 @@ allOf:
- mediatek,mt8186-iommu-mm
- mediatek,mt8188-iommu-vdo
- mediatek,mt8188-iommu-vpp
+ - mediatek,mt8189-iommu-mm
- mediatek,mt8192-m4u
- mediatek,mt8195-iommu-vdo
- mediatek,mt8195-iommu-vpp
@@ -208,6 +214,8 @@ allOf:
contains:
enum:
- mediatek,mt8188-iommu-infra
+ - mediatek,mt8189-iommu-apu
+ - mediatek,mt8189-iommu-infra
- mediatek,mt8195-iommu-infra
then:
diff --git a/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml b/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml
index 3e5623edd207..93a489025317 100644
--- a/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml
+++ b/Documentation/devicetree/bindings/iommu/qcom,iommu.yaml
@@ -32,14 +32,18 @@ properties:
- const: qcom,msm-iommu-v2
clocks:
+ minItems: 2
items:
- description: Clock required for IOMMU register group access
- description: Clock required for underlying bus access
+ - description: Clock required for Translation Buffer Unit access
clock-names:
+ minItems: 2
items:
- const: iface
- const: bus
+ - const: tbu
power-domains:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/leds/backlight/arc,arc2c0608.yaml b/Documentation/devicetree/bindings/leds/backlight/arc,arc2c0608.yaml
new file mode 100644
index 000000000000..786beced5590
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/backlight/arc,arc2c0608.yaml
@@ -0,0 +1,108 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/backlight/arc,arc2c0608.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ArcticSand arc2c0608 LED driver
+
+description: |
+ The ArcticSand arc2c0608 LED driver provides ultra
+ efficient notebook backlighting. Optional properties not
+ specified will default to values in IC EPROM.
+
+ Datasheet:
+ https://www.murata.com/-/media/webrenewal/products/power/power-semiconductor/overview/lineup/led-boost/arc2/arc2c0608.ashx.
+
+maintainers:
+ - Brian Dodge <bdodge@arcticsand.com>
+
+allOf:
+ - $ref: /schemas/leds/common.yaml
+
+properties:
+ compatible:
+ const: arc,arc2c0608
+
+ reg:
+ maxItems: 1
+
+ default-brightness:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 4095
+
+ led-sources:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description: List of enabled channels
+ items:
+ enum: [0, 1, 2, 3, 4, 5]
+ minItems: 1
+ uniqueItems: true
+
+ arc,led-config-0:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Fading speed (period between intensity
+ steps)
+
+ arc,led-config-1:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: If set, sets ILED_CONFIG register. Used for
+ fine tuning the maximum LED current.
+
+ arc,dim-freq:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: PWM mode frequency setting (bits [3:0] used)
+
+ arc,comp-config:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Setting for register CONFIG_COMP which
+ controls internal resitances, feed forward freqs,
+ and initial VOUT at startup. Consult the datasheet.
+
+ arc,filter-config:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: RC and PWM Filter settings.
+ Bit Assignment
+ 7654 3 2 1 0
+ xxxx RCF1 RCF0 PWM1 PWM0
+ RCF statuses PWM Filter Statues
+ 00 = OFF (default) 00 = OFF (default)
+ 01 = LOW 01 = 2 STEPS
+ 10 - MEDIUM 10 = 4 STEPS
+ 11 = HIGH 11 = 8 STEPS
+
+ arc,trim-config:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Sets percentage increase of Maximum LED
+ Current.
+ 0x00 = 0% increase.
+ 0x20 = 20.2%.
+ 0x3F = 41.5%
+
+ label: true
+
+ linux,default-trigger: true
+
+additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led-controller@30 {
+ compatible = "arc,arc2c0608";
+ reg = <0x30>;
+ default-brightness = <500>;
+ label = "lcd-backlight";
+ linux,default-trigger = "backlight";
+ led-sources = <0 1 2 5>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/leds/backlight/arcxcnn_bl.txt b/Documentation/devicetree/bindings/leds/backlight/arcxcnn_bl.txt
deleted file mode 100644
index 230abdefd6e7..000000000000
--- a/Documentation/devicetree/bindings/leds/backlight/arcxcnn_bl.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-Binding for ArcticSand arc2c0608 LED driver
-
-Required properties:
-- compatible: should be "arc,arc2c0608"
-- reg: slave address
-
-Optional properties:
-- default-brightness: brightness value on boot, value from: 0-4095
-- label: The name of the backlight device
- See Documentation/devicetree/bindings/leds/common.txt
-- led-sources: List of enabled channels from 0 to 5.
- See Documentation/devicetree/bindings/leds/common.txt
-
-- arc,led-config-0: setting for register ILED_CONFIG_0
-- arc,led-config-1: setting for register ILED_CONFIG_1
-- arc,dim-freq: PWM mode frequence setting (bits [3:0] used)
-- arc,comp-config: setting for register CONFIG_COMP
-- arc,filter-config: setting for register FILTER_CONFIG
-- arc,trim-config: setting for register IMAXTUNE
-
-Note: Optional properties not specified will default to values in IC EPROM
-
-Example:
-
-arc2c0608@30 {
- compatible = "arc,arc2c0608";
- reg = <0x30>;
- default-brightness = <500>;
- label = "lcd-backlight";
- linux,default-trigger = "backlight";
- led-sources = <0 1 2 5>;
-};
-
diff --git a/Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml b/Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
new file mode 100644
index 000000000000..f48ce7a3434d
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/leds/backlight/awinic,aw99706.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Awinic AW99706 6-channel WLED Backlight Driver
+
+maintainers:
+ - Junjie Cao <caojunjie650@gmail.com>
+
+allOf:
+ - $ref: common.yaml#
+
+properties:
+ compatible:
+ const: awinic,aw99706
+
+ reg:
+ maxItems: 1
+
+ enable-gpios:
+ description: GPIO to use to enable/disable the backlight (HWEN pin).
+ maxItems: 1
+
+ awinic,dim-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ Select dimming mode of the device.
+ 0 = Bypass mode.
+ 1 = DC mode.
+ 2 = MIX mode(PWM at low brightness and DC at high brightness).
+ 3 = MIX-26k mode(MIX mode with different PWM frequency).
+ enum: [ 0, 1, 2, 3 ]
+ default: 1
+
+ awinic,sw-freq-hz:
+ description: Boost switching frequency in Hz.
+ enum: [ 300000, 400000, 500000, 600000, 660000, 750000, 850000, 1000000,
+ 1200000, 1330000, 1500000, 1700000 ]
+ default: 750000
+
+ awinic,sw-ilmt-microamp:
+ description: Switching current limitation in uA.
+ enum: [ 1500000, 2000000, 2500000, 3000000 ]
+ default: 3000000
+
+ awinic,iled-max-microamp:
+ description: Maximum LED current setting in uA.
+ minimum: 5000
+ maximum: 50000
+ multipleOf: 500
+ default: 20000
+
+ awinic,uvlo-thres-microvolt:
+ description: UVLO(Under Voltage Lock Out) in uV.
+ enum: [ 2200000, 5000000 ]
+ default: 2200000
+
+ awinic,ramp-ctl:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ Select ramp control and filter of the device.
+ 0 = Fade in/fade out.
+ 1 = Light filter.
+ 2 = Medium filter.
+ 3 = Heavy filter.
+ enum: [ 0, 1, 2, 3 ]
+ default: 2
+
+required:
+ - compatible
+ - reg
+ - enable-gpios
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ backlight@76 {
+ compatible = "awinic,aw99706";
+ reg = <0x76>;
+ enable-gpios = <&tlmm 88 GPIO_ACTIVE_HIGH>;
+ default-brightness = <2047>;
+ max-brightness = <4095>;
+ awinic,dim-mode = <1>;
+ awinic,sw-freq-hz = <750000>;
+ awinic,sw-ilmt-microamp = <3000000>;
+ awinic,uvlo-thres-microvolt = <2200000>;
+ awinic,iled-max-microamp = <20000>;
+ awinic,ramp-ctl = <2>;
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/leds/common.yaml b/Documentation/devicetree/bindings/leds/common.yaml
index 274f83288a92..f4e44b33f56d 100644
--- a/Documentation/devicetree/bindings/leds/common.yaml
+++ b/Documentation/devicetree/bindings/leds/common.yaml
@@ -173,6 +173,12 @@ properties:
led-max-microamp.
$ref: /schemas/types.yaml#/definitions/uint32
+ default-brightness:
+ description:
+ Brightness to be set if LED's default state is on. Used only during
+ initialization. If the option is not set then max brightness is used.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
panic-indicator:
description:
This property specifies that the LED should be used, if at all possible,
diff --git a/Documentation/devicetree/bindings/leds/leds-pwm.yaml b/Documentation/devicetree/bindings/leds/leds-pwm.yaml
index 61b97e8bc36d..6c4fcefbe25f 100644
--- a/Documentation/devicetree/bindings/leds/leds-pwm.yaml
+++ b/Documentation/devicetree/bindings/leds/leds-pwm.yaml
@@ -40,6 +40,13 @@ patternProperties:
initialization. If the option is not set then max brightness is used.
$ref: /schemas/types.yaml#/definitions/uint32
+ enable-gpios:
+ description:
+ GPIO for LED hardware enable control. Set active when brightness is
+ non-zero and inactive when brightness is zero.
+ The GPIO default state follows the "default-state" property.
+ maxItems: 1
+
required:
- pwms
- max-brightness
diff --git a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
index 841a0229c472..c4b7e57b2518 100644
--- a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
+++ b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml
@@ -13,6 +13,11 @@ description: >
The Qualcomm Light Pulse Generator consists of three different hardware blocks;
a ramp generator with lookup table (LUT), the light pulse generator and a three
channel current sink. These blocks are found in a wide range of Qualcomm PMICs.
+ The light pulse generator (LPG) can also be used independently to output PWM
+ signal for standard PWM applications. In this scenario, the LPG output should
+ be routed to a specific PMIC GPIO by setting the GPIO pin mux to the special
+ functions indicated in the datasheet, the TRILED driver for the channel will
+ not be enabled in this configuration.
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/leds/qcom,pm8058-led.yaml b/Documentation/devicetree/bindings/leds/qcom,pm8058-led.yaml
index fa03e73622d4..b409b2a8b5c5 100644
--- a/Documentation/devicetree/bindings/leds/qcom,pm8058-led.yaml
+++ b/Documentation/devicetree/bindings/leds/qcom,pm8058-led.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm PM8058 PMIC LED
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
The Qualcomm PM8058 contains an LED block for up to six LEDs:: three normal
diff --git a/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml b/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
index bcf0ad4ea57e..05250aefd385 100644
--- a/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
+++ b/Documentation/devicetree/bindings/leds/qcom,spmi-flash-led.yaml
@@ -24,6 +24,7 @@ properties:
- enum:
- qcom,pm6150l-flash-led
- qcom,pm660l-flash-led
+ - qcom,pm7550-flash-led
- qcom,pm8150c-flash-led
- qcom,pm8150l-flash-led
- qcom,pm8350c-flash-led
diff --git a/Documentation/devicetree/bindings/mailbox/apm,xgene-slimpro-mbox.yaml b/Documentation/devicetree/bindings/mailbox/apm,xgene-slimpro-mbox.yaml
new file mode 100644
index 000000000000..815f08d61de8
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/apm,xgene-slimpro-mbox.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/apm,xgene-slimpro-mbox.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: APM X-Gene SLIMpro mailbox
+
+maintainers:
+ - Khuong Dinh <khuong@os.amperecomputing.com>
+
+description:
+ The APM X-Gene SLIMpro mailbox is used to communicate messages between
+ the ARM64 processors and the Cortex M3 (dubbed SLIMpro). It uses a simple
+ interrupt based door bell mechanism and can exchange simple messages using the
+ internal registers.
+
+properties:
+ compatible:
+ const: apm,xgene-slimpro-mbox
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: mailbox channel 0 doorbell
+ - description: mailbox channel 1 doorbell
+ - description: mailbox channel 2 doorbell
+ - description: mailbox channel 3 doorbell
+ - description: mailbox channel 4 doorbell
+ - description: mailbox channel 5 doorbell
+ - description: mailbox channel 6 doorbell
+ - description: mailbox channel 7 doorbell
+
+ '#mbox-cells':
+ description: Number of mailbox channel.
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - '#mbox-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ mailbox@10540000 {
+ compatible = "apm,xgene-slimpro-mbox";
+ reg = <0x10540000 0xa000>;
+ #mbox-cells = <1>;
+ interrupts = <0x0 0x0 0x4>,
+ <0x0 0x1 0x4>,
+ <0x0 0x2 0x4>,
+ <0x0 0x3 0x4>,
+ <0x0 0x4 0x4>,
+ <0x0 0x5 0x4>,
+ <0x0 0x6 0x4>,
+ <0x0 0x7 0x4>;
+ };
diff --git a/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml b/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml
index d9a4f4a02d7c..e45b661e8b41 100644
--- a/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml
+++ b/Documentation/devicetree/bindings/mailbox/arm,mhu.yaml
@@ -52,7 +52,6 @@ properties:
- const: arm,mhu-doorbell
- const: arm,primecell
-
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml b/Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml
index 02f06314d85f..3828d77f6316 100644
--- a/Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml
+++ b/Documentation/devicetree/bindings/mailbox/arm,mhuv2.yaml
@@ -127,7 +127,6 @@ properties:
- minimum: 0
maximum: 124
-
'#mbox-cells':
description: |
It is always set to 2. The first argument in the consumers 'mboxes'
diff --git a/Documentation/devicetree/bindings/mailbox/mtk,adsp-mbox.yaml b/Documentation/devicetree/bindings/mailbox/mtk,adsp-mbox.yaml
index 8a1369df4ecb..4ca0d5e49c79 100644
--- a/Documentation/devicetree/bindings/mailbox/mtk,adsp-mbox.yaml
+++ b/Documentation/devicetree/bindings/mailbox/mtk,adsp-mbox.yaml
@@ -26,7 +26,6 @@ properties:
- mediatek,mt8188-adsp-mbox
- const: mediatek,mt8186-adsp-mbox
-
"#mbox-cells":
const: 0
diff --git a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml
index 615ed103b7e6..f40dc9048327 100644
--- a/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml
+++ b/Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml
@@ -187,10 +187,10 @@ allOf:
enum:
- qcom,msm8916-apcs-kpss-global
then:
- $ref: "#/$defs/msm8916-apcs-clock-controller"
+ $ref: '#/$defs/msm8916-apcs-clock-controller'
properties:
clock-controller:
- $ref: "#/$defs/msm8916-apcs-clock-controller"
+ $ref: '#/$defs/msm8916-apcs-clock-controller'
- if:
properties:
@@ -199,10 +199,10 @@ allOf:
enum:
- qcom,msm8939-apcs-kpss-global
then:
- $ref: "#/$defs/msm8939-apcs-clock-controller"
+ $ref: '#/$defs/msm8939-apcs-clock-controller'
properties:
clock-controller:
- $ref: "#/$defs/msm8939-apcs-clock-controller"
+ $ref: '#/$defs/msm8939-apcs-clock-controller'
- if:
properties:
@@ -211,10 +211,10 @@ allOf:
enum:
- qcom,sdx55-apcs-gcc
then:
- $ref: "#/$defs/sdx55-apcs-clock-controller"
+ $ref: '#/$defs/sdx55-apcs-clock-controller'
properties:
clock-controller:
- $ref: "#/$defs/sdx55-apcs-clock-controller"
+ $ref: '#/$defs/sdx55-apcs-clock-controller'
- if:
properties:
@@ -223,10 +223,10 @@ allOf:
enum:
- qcom,ipq6018-apcs-apps-global
then:
- $ref: "#/$defs/ipq6018-apcs-clock-controller"
+ $ref: '#/$defs/ipq6018-apcs-clock-controller'
properties:
clock-controller:
- $ref: "#/$defs/ipq6018-apcs-clock-controller"
+ $ref: '#/$defs/ipq6018-apcs-clock-controller'
- if:
properties:
diff --git a/Documentation/devicetree/bindings/mailbox/xgene-slimpro-mailbox.txt b/Documentation/devicetree/bindings/mailbox/xgene-slimpro-mailbox.txt
deleted file mode 100644
index e46451bb242f..000000000000
--- a/Documentation/devicetree/bindings/mailbox/xgene-slimpro-mailbox.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-The APM X-Gene SLIMpro mailbox is used to communicate messages between
-the ARM64 processors and the Cortex M3 (dubbed SLIMpro). It uses a simple
-interrupt based door bell mechanism and can exchange simple messages using the
-internal registers.
-
-There are total of 8 interrupts in this mailbox. Each used for an individual
-door bell (or mailbox channel).
-
-Required properties:
-- compatible: Should be as "apm,xgene-slimpro-mbox".
-
-- reg: Contains the mailbox register address range.
-
-- interrupts: 8 interrupts must be from 0 to 7, interrupt 0 define the
- the interrupt for mailbox channel 0 and interrupt 1 for
- mailbox channel 1 and so likewise for the reminder.
-
-- #mbox-cells: only one to specify the mailbox channel number.
-
-Example:
-
-Mailbox Node:
- mailbox: mailbox@10540000 {
- compatible = "apm,xgene-slimpro-mbox";
- reg = <0x0 0x10540000 0x0 0xa000>;
- #mbox-cells = <1>;
- interrupts = <0x0 0x0 0x4>,
- <0x0 0x1 0x4>,
- <0x0 0x2 0x4>,
- <0x0 0x3 0x4>,
- <0x0 0x4 0x4>,
- <0x0 0x5 0x4>,
- <0x0 0x6 0x4>,
- <0x0 0x7 0x4>,
- };
diff --git a/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.yaml b/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.yaml
index fe83b5cb1278..04d6473d666f 100644
--- a/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.yaml
+++ b/Documentation/devicetree/bindings/mailbox/xlnx,zynqmp-ipi-mailbox.yaml
@@ -142,7 +142,7 @@ patternProperties:
- compatible
- reg
- reg-names
- - "#mbox-cells"
+ - '#mbox-cells'
- xlnx,ipi-id
required:
diff --git a/Documentation/devicetree/bindings/media/amphion,vpu.yaml b/Documentation/devicetree/bindings/media/amphion,vpu.yaml
index 5a920d9e78c7..fa18013d705d 100644
--- a/Documentation/devicetree/bindings/media/amphion,vpu.yaml
+++ b/Documentation/devicetree/bindings/media/amphion,vpu.yaml
@@ -45,7 +45,6 @@ patternProperties:
between driver and firmware. Implement via mailbox on driver.
$ref: /schemas/mailbox/fsl,mu.yaml#
-
"^vpu-core@[0-9a-f]+$":
description:
Each core correspond a decoder or encoder, need to configure them
diff --git a/Documentation/devicetree/bindings/media/arm,mali-c55.yaml b/Documentation/devicetree/bindings/media/arm,mali-c55.yaml
new file mode 100644
index 000000000000..fc4fcd19922a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/arm,mali-c55.yaml
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/arm,mali-c55.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ARM Mali-C55 Image Signal Processor
+
+maintainers:
+ - Daniel Scally <dan.scally@ideasonboard.com>
+ - Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+properties:
+ compatible:
+ const: arm,mali-c55
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: ISP Video Clock
+ - description: ISP AXI clock
+ - description: ISP AHB-lite clock
+
+ clock-names:
+ items:
+ - const: vclk
+ - const: aclk
+ - const: hclk
+
+ resets:
+ items:
+ - description: vclk domain reset
+ - description: aclk domain reset
+ - description: hclk domain reset
+
+ reset-names:
+ items:
+ - const: vresetn
+ - const: aresetn
+ - const: hresetn
+
+ port:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Input parallel video bus
+
+ properties:
+ endpoint:
+ $ref: /schemas/graph.yaml#/properties/endpoint
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ isp@400000 {
+ compatible = "arm,mali-c55";
+ reg = <0x400000 0x200000>;
+ clocks = <&clk 0>, <&clk 1>, <&clk 2>;
+ clock-names = "vclk", "aclk", "hclk";
+ resets = <&resets 0>, <&resets 1>, <&resets 2>;
+ reset-names = "vresetn", "aresetn", "hresetn";
+ interrupts = <GIC_SPI 861 IRQ_TYPE_EDGE_RISING>;
+
+ port {
+ isp_in: endpoint {
+ remote-endpoint = <&csi2_rx_out>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml b/Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml
index 511ac0d67a7f..988a5b3a62bd 100644
--- a/Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml
+++ b/Documentation/devicetree/bindings/media/fsl,imx6q-vdoa.yaml
@@ -16,7 +16,7 @@ maintainers:
properties:
compatible:
- const: "fsl,imx6q-vdoa"
+ const: fsl,imx6q-vdoa
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml b/Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml
index 2dc2829d42a0..f8d9889dbc21 100644
--- a/Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/adi,adv7604.yaml
@@ -154,7 +154,5 @@ examples:
};
};
};
-
-
};
};
diff --git a/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9719.yaml b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9719.yaml
new file mode 100644
index 000000000000..8e8d62436e0d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/dongwoon,dw9719.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/dongwoon,dw9719.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Dongwoon Anatech DW9719 Voice Coil Motor (VCM) Controller
+
+maintainers:
+ - André Apitzsch <git@apitzsch.eu>
+
+description:
+ The Dongwoon DW9718S/9719/9761 is a single 10-bit digital-to-analog converter
+ with 100 mA output current sink capability, designed for linear control of
+ voice coil motors (VCM) in camera lenses. This chip provides a Smart Actuator
+ Control (SAC) mode intended for driving voice coil lenses in camera modules.
+
+properties:
+ compatible:
+ enum:
+ - dongwoon,dw9718s
+ - dongwoon,dw9719
+ - dongwoon,dw9761
+ - dongwoon,dw9800k
+
+ reg:
+ maxItems: 1
+
+ vdd-supply:
+ description: VDD power supply
+
+ dongwoon,sac-mode:
+ description: |
+ Slew Rate Control mode to use: direct, LSC (Linear Slope Control) or
+ SAC1-SAC6 (Smart Actuator Control).
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum:
+ - 0 # Direct mode
+ - 1 # LSC mode
+ - 2 # SAC1 mode (operation time# 0.32 x Tvib)
+ - 3 # SAC2 mode (operation time# 0.48 x Tvib)
+ - 4 # SAC3 mode (operation time# 0.72 x Tvib)
+ - 5 # SAC4 mode (operation time# 1.20 x Tvib)
+ - 6 # SAC5 mode (operation time# 1.64 x Tvib)
+ - 7 # SAC6 mode (operation time# 1.88 x Tvib)
+ default: 4
+
+ dongwoon,vcm-prescale:
+ description:
+ Indication of VCM switching frequency dividing rate select.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: dongwoon,dw9718s
+ then:
+ properties:
+ dongwoon,vcm-prescale:
+ description:
+ The final frequency is 10 MHz divided by (value + 2).
+ maximum: 15
+ default: 0
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ actuator@c {
+ compatible = "dongwoon,dw9718s";
+ reg = <0x0c>;
+
+ vdd-supply = <&pm8937_l17>;
+
+ dongwoon,sac-mode = <4>;
+ dongwoon,vcm-prescale = <0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/nxp,tda19971.yaml b/Documentation/devicetree/bindings/media/i2c/nxp,tda19971.yaml
new file mode 100644
index 000000000000..477e59316dfa
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/nxp,tda19971.yaml
@@ -0,0 +1,162 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/nxp,tda19971.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP TDA1997x HDMI receiver
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+description: |
+ The TDA19971/73 are HDMI video receivers.
+
+ The TDA19971 Video port output pins can be used as follows:
+ - RGB 8bit per color (24 bits total): R[11:4] B[11:4] G[11:4]
+ - YUV444 8bit per color (24 bits total): Y[11:4] Cr[11:4] Cb[11:4]
+ - YUV422 semi-planar 8bit per component (16 bits total): Y[11:4] CbCr[11:4]
+ - YUV422 semi-planar 10bit per component (20 bits total): Y[11:2] CbCr[11:2]
+ - YUV422 semi-planar 12bit per component (24 bits total): - Y[11:0] CbCr[11:0]
+ - YUV422 BT656 8bit per component (8 bits total): YCbCr[11:4] (2-cycles)
+ - YUV422 BT656 10bit per component (10 bits total): YCbCr[11:2] (2-cycles)
+ - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles)
+
+ The TDA19973 Video port output pins can be used as follows:
+ - RGB 12bit per color (36 bits total): R[11:0] B[11:0] G[11:0]
+ - YUV444 12bit per color (36 bits total): Y[11:0] Cb[11:0] Cr[11:0]
+ - YUV422 semi-planar 12bit per component (24 bits total): Y[11:0] CbCr[11:0]
+ - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles)
+
+ The Video port output pins are mapped via 4-bit 'pin groups' allowing
+ for a variety of connection possibilities including swapping pin order within
+ pin groups. The video_portcfg device-tree property consists of register mapping
+ pairs which map a chip-specific VP output register to a 4-bit pin group. If
+ the pin group needs to be bit-swapped you can use the *_S pin-group defines.
+
+properties:
+ compatible:
+ enum:
+ - nxp,tda19971
+ - nxp,tda19973
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ DOVDD-supply: true
+
+ DVDD-supply: true
+
+ AVDD-supply: true
+
+ '#sound-dai-cells':
+ const: 0
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ nxp,vidout-portcfg:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ minItems: 1
+ maxItems: 4
+ items:
+ items:
+ - description: Video Port control registers index.
+ maximum: 8
+ minimum: 0
+ - description: pin(pinswapped) groups
+
+ description:
+ array of pairs mapping VP output pins to pin groups.
+
+ nxp,audout-format:
+ enum:
+ - i2s
+ - spdif
+
+ nxp,audout-width:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [8, 16, 24, 32]
+ description:
+ width of audio output data bus.
+
+ nxp,audout-layout:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+ description:
+ data layout (0=AP0 used, 1=AP0/AP1/AP2/AP3 used).
+
+ nxp,audout-mclk-fs:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Multiplication factor between stream rate and codec mclk.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - DOVDD-supply
+ - AVDD-supply
+ - DVDD-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/media/tda1997x.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hdmi-receiver@48 {
+ compatible = "nxp,tda19971";
+ reg = <0x48>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_tda1997x>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
+ DOVDD-supply = <&reg_3p3v>;
+ AVDD-supply = <&reg_1p8v>;
+ DVDD-supply = <&reg_1p8v>;
+ /* audio */
+ #sound-dai-cells = <0>;
+ nxp,audout-format = "i2s";
+ nxp,audout-layout = <0>;
+ nxp,audout-width = <16>;
+ nxp,audout-mclk-fs = <128>;
+ /*
+ * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4]
+ * and Y[11:4] across 16bits in the same pixclk cycle.
+ */
+ nxp,vidout-portcfg =
+ /* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */
+ < TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >,
+ /* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */
+ < TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >,
+ /* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */
+ < TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >,
+ /* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */
+ < TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >;
+
+ port {
+ endpoint {
+ remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
+ bus-width = <16>;
+ hsync-active = <1>;
+ vsync-active = <1>;
+ data-active = <1>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt b/Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt
deleted file mode 100644
index e76167999d76..000000000000
--- a/Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt
+++ /dev/null
@@ -1,178 +0,0 @@
-Device-Tree bindings for the NXP TDA1997x HDMI receiver
-
-The TDA19971/73 are HDMI video receivers.
-
-The TDA19971 Video port output pins can be used as follows:
- - RGB 8bit per color (24 bits total): R[11:4] B[11:4] G[11:4]
- - YUV444 8bit per color (24 bits total): Y[11:4] Cr[11:4] Cb[11:4]
- - YUV422 semi-planar 8bit per component (16 bits total): Y[11:4] CbCr[11:4]
- - YUV422 semi-planar 10bit per component (20 bits total): Y[11:2] CbCr[11:2]
- - YUV422 semi-planar 12bit per component (24 bits total): - Y[11:0] CbCr[11:0]
- - YUV422 BT656 8bit per component (8 bits total): YCbCr[11:4] (2-cycles)
- - YUV422 BT656 10bit per component (10 bits total): YCbCr[11:2] (2-cycles)
- - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles)
-
-The TDA19973 Video port output pins can be used as follows:
- - RGB 12bit per color (36 bits total): R[11:0] B[11:0] G[11:0]
- - YUV444 12bit per color (36 bits total): Y[11:0] Cb[11:0] Cr[11:0]
- - YUV422 semi-planar 12bit per component (24 bits total): Y[11:0] CbCr[11:0]
- - YUV422 BT656 12bit per component (12 bits total): YCbCr[11:0] (2-cycles)
-
-The Video port output pins are mapped via 4-bit 'pin groups' allowing
-for a variety of connection possibilities including swapping pin order within
-pin groups. The video_portcfg device-tree property consists of register mapping
-pairs which map a chip-specific VP output register to a 4-bit pin group. If
-the pin group needs to be bit-swapped you can use the *_S pin-group defines.
-
-Required Properties:
- - compatible :
- - "nxp,tda19971" for the TDA19971
- - "nxp,tda19973" for the TDA19973
- - reg : I2C slave address
- - interrupts : The interrupt number
- - DOVDD-supply : Digital I/O supply
- - DVDD-supply : Digital Core supply
- - AVDD-supply : Analog supply
- - nxp,vidout-portcfg : array of pairs mapping VP output pins to pin groups.
-
-Optional Properties:
- - nxp,audout-format : DAI bus format: "i2s" or "spdif".
- - nxp,audout-width : width of audio output data bus (1-4).
- - nxp,audout-layout : data layout (0=AP0 used, 1=AP0/AP1/AP2/AP3 used).
- - nxp,audout-mclk-fs : Multiplication factor between stream rate and codec
- mclk.
-
-The port node shall contain one endpoint child node for its digital
-output video port, in accordance with the video interface bindings defined in
-Documentation/devicetree/bindings/media/video-interfaces.txt.
-
-Optional Endpoint Properties:
- The following three properties are defined in video-interfaces.txt and
- are valid for the output parallel bus endpoint:
- - hsync-active: Horizontal synchronization polarity. Defaults to active high.
- - vsync-active: Vertical synchronization polarity. Defaults to active high.
- - data-active: Data polarity. Defaults to active high.
-
-Examples:
- - VP[15:0] connected to IMX6 CSI_DATA[19:4] for 16bit YUV422
- 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins)
- hdmi-receiver@48 {
- compatible = "nxp,tda19971";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_tda1997x>;
- reg = <0x48>;
- interrupt-parent = <&gpio1>;
- interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
- DOVDD-supply = <&reg_3p3v>;
- AVDD-supply = <&reg_1p8v>;
- DVDD-supply = <&reg_1p8v>;
- /* audio */
- #sound-dai-cells = <0>;
- nxp,audout-format = "i2s";
- nxp,audout-layout = <0>;
- nxp,audout-width = <16>;
- nxp,audout-mclk-fs = <128>;
- /*
- * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4]
- * and Y[11:4] across 16bits in the same pixclk cycle.
- */
- nxp,vidout-portcfg =
- /* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */
- < TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >,
- /* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */
- < TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >,
- /* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */
- < TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >,
- /* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */
- < TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >;
-
- port {
- tda1997x_to_ipu1_csi0_mux: endpoint {
- remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
- bus-width = <16>;
- hsync-active = <1>;
- vsync-active = <1>;
- data-active = <1>;
- };
- };
- };
- - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656
- 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins)
- hdmi-receiver@48 {
- compatible = "nxp,tda19971";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_tda1997x>;
- reg = <0x48>;
- interrupt-parent = <&gpio1>;
- interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
- DOVDD-supply = <&reg_3p3v>;
- AVDD-supply = <&reg_1p8v>;
- DVDD-supply = <&reg_1p8v>;
- /* audio */
- #sound-dai-cells = <0>;
- nxp,audout-format = "i2s";
- nxp,audout-layout = <0>;
- nxp,audout-width = <16>;
- nxp,audout-mclk-fs = <128>;
- /*
- * The 8bpp YUV422 semi-planar mode outputs CbCr[11:4]
- * and Y[11:4] across 16bits in the same pixclk cycle.
- */
- nxp,vidout-portcfg =
- /* Y[11:8]<->VP[15:12]<->CSI_DATA[19:16] */
- < TDA1997X_VP24_V15_12 TDA1997X_G_Y_11_8 >,
- /* Y[7:4]<->VP[11:08]<->CSI_DATA[15:12] */
- < TDA1997X_VP24_V11_08 TDA1997X_G_Y_7_4 >,
- /* CbCc[11:8]<->VP[07:04]<->CSI_DATA[11:8] */
- < TDA1997X_VP24_V07_04 TDA1997X_R_CR_CBCR_11_8 >,
- /* CbCr[7:4]<->VP[03:00]<->CSI_DATA[7:4] */
- < TDA1997X_VP24_V03_00 TDA1997X_R_CR_CBCR_7_4 >;
-
- port {
- tda1997x_to_ipu1_csi0_mux: endpoint {
- remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
- bus-width = <16>;
- hsync-active = <1>;
- vsync-active = <1>;
- data-active = <1>;
- };
- };
- };
- - VP[15:8] connected to IMX6 CSI_DATA[19:12] for 8bit BT656
- 16bit I2S layout0 with a 128*fs clock (A_WS, AP0, A_CLK pins)
- hdmi-receiver@48 {
- compatible = "nxp,tda19971";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_tda1997x>;
- reg = <0x48>;
- interrupt-parent = <&gpio1>;
- interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
- DOVDD-supply = <&reg_3p3v>;
- AVDD-supply = <&reg_1p8v>;
- DVDD-supply = <&reg_1p8v>;
- /* audio */
- #sound-dai-cells = <0>;
- nxp,audout-format = "i2s";
- nxp,audout-layout = <0>;
- nxp,audout-width = <16>;
- nxp,audout-mclk-fs = <128>;
- /*
- * The 8bpp BT656 mode outputs YCbCr[11:4] across 8bits over
- * 2 pixclk cycles.
- */
- nxp,vidout-portcfg =
- /* YCbCr[11:8]<->VP[15:12]<->CSI_DATA[19:16] */
- < TDA1997X_VP24_V15_12 TDA1997X_R_CR_CBCR_11_8 >,
- /* YCbCr[7:4]<->VP[11:08]<->CSI_DATA[15:12] */
- < TDA1997X_VP24_V11_08 TDA1997X_R_CR_CBCR_7_4 >,
-
- port {
- tda1997x_to_ipu1_csi0_mux: endpoint {
- remote-endpoint = <&ipu1_csi0_mux_from_parallel_sensor>;
- bus-width = <16>;
- hsync-active = <1>;
- vsync-active = <1>;
- data-active = <1>;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/media/i2c/samsung,s5k5baf.yaml b/Documentation/devicetree/bindings/media/i2c/samsung,s5k5baf.yaml
index ebd95a8d9b2f..4cb0f5aa1301 100644
--- a/Documentation/devicetree/bindings/media/i2c/samsung,s5k5baf.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/samsung,s5k5baf.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5K5BAF UXGA 1/5" 2M CMOS Image Sensor with embedded SoC ISP
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/media/i2c/samsung,s5k6a3.yaml b/Documentation/devicetree/bindings/media/i2c/samsung,s5k6a3.yaml
index e563e35920c4..9df1e0f872f2 100644
--- a/Documentation/devicetree/bindings/media/i2c/samsung,s5k6a3.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/samsung,s5k6a3.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5K6A3(YX) raw image sensor
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
S5K6A3(YX) is a raw image sensor with MIPI CSI-2 and CCP2 image data
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
new file mode 100644
index 000000000000..20f48d5e9b2d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/sony,imx111.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony IMX111 8MP CMOS Digital Image Sensor
+
+maintainers:
+ - Svyatoslav Ryhel <clamor95@gmail.com>
+
+description:
+ IMX111 sensor is a Sony CMOS active pixel digital image sensor with an active
+ array size of 2464H x 3280V. It is programmable through I2C interface. Image
+ data is sent through MIPI CSI-2, through 1 or 2 lanes.
+
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+ - $ref: /schemas/nvmem/nvmem-consumer.yaml#
+
+properties:
+ compatible:
+ const: sony,imx111
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ description: EXTCLK with possible frequency from 6 to 54 MHz
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ iovdd-supply:
+ description: Digital IO power supply (1.8V)
+
+ dvdd-supply:
+ description: Digital power supply (1.2V)
+
+ avdd-supply:
+ description: Analog power supply (2.7V)
+
+ port:
+ additionalProperties: false
+ $ref: /schemas/graph.yaml#/$defs/port-base
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ required:
+ - data-lanes
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - port
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interfaces.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera@10 {
+ compatible = "sony,imx111";
+ reg = <0x10>;
+
+ clocks = <&imx111_clk>;
+
+ iovdd-supply = <&camera_vddio_1v8>;
+ dvdd-supply = <&camera_vddd_1v2>;
+ avdd-supply = <&camera_vdda_2v7>;
+
+ orientation = <1>;
+ rotation = <90>;
+
+ nvmem = <&eeprom>;
+ flash-leds = <&led>;
+ lens-focus = <&vcm>;
+
+ reset-gpios = <&gpio 84 GPIO_ACTIVE_LOW>;
+
+ port {
+ imx111_output: endpoint {
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <542400000>;
+ remote-endpoint = <&csi_input>;
+ };
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
index 3c071e6fbea6..060ac6829b66 100644
--- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
@@ -25,7 +25,11 @@ allOf:
properties:
compatible:
- const: st,vd55g1
+ enum:
+ - st,vd55g1
+ - st,vd65g4
+ description:
+ VD55G1 is the monochrome variant, while VD65G4 is the color one.
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/media/i2c/techwell,tw9900.yaml b/Documentation/devicetree/bindings/media/i2c/techwell,tw9900.yaml
index c9673391afdb..0592d0b9af92 100644
--- a/Documentation/devicetree/bindings/media/i2c/techwell,tw9900.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/techwell,tw9900.yaml
@@ -70,7 +70,6 @@ properties:
$ref: /schemas/graph.yaml#/properties/port
description: Video port for the decoder output.
-
required:
- port@0
- port@1
diff --git a/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt b/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt
deleted file mode 100644
index 94b908ace53c..000000000000
--- a/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.txt
+++ /dev/null
@@ -1,157 +0,0 @@
-* Texas Instruments TVP5150 and TVP5151 video decoders
-
-The TVP5150 and TVP5151 are video decoders that convert baseband NTSC and PAL
-(and also SECAM in the TVP5151 case) video signals to either 8-bit 4:2:2 YUV
-with discrete syncs or 8-bit ITU-R BT.656 with embedded syncs output formats.
-
-Required Properties:
-====================
-- compatible: Value must be "ti,tvp5150".
-- reg: I2C slave address.
-
-Optional Properties:
-====================
-- pdn-gpios: Phandle for the GPIO connected to the PDN pin, if any.
-- reset-gpios: Phandle for the GPIO connected to the RESETB pin, if any.
-
-The device node must contain one 'port' child node per device physical input
-and output port, in accordance with the video interface bindings defined in
-Documentation/devicetree/bindings/media/video-interfaces.txt. The port nodes
-are numbered as follows
-
- Name Type Port
- --------------------------------------
- AIP1A sink 0
- AIP1B sink 1
- Y-OUT src 2
-
-The device node must contain at least one sink port and the src port. Each input
-port must be linked to an endpoint defined in [1]. The port/connector layout is
-as follows
-
-tvp-5150 port@0 (AIP1A)
- endpoint@0 -----------> Comp0-Con port
- endpoint@1 ------+----> Svideo-Con port
-tvp-5150 port@1 (AIP1B) |
- endpoint@1 ------+
- endpoint@0 -----------> Comp1-Con port
-tvp-5150 port@2
- endpoint (video bitstream output at YOUT[0-7] parallel bus)
-
-Required Endpoint Properties for parallel synchronization on output port:
-=========================================================================
-
-- hsync-active: Active state of the HSYNC signal. Must be <1> (HIGH).
-- vsync-active: Active state of the VSYNC signal. Must be <1> (HIGH).
-- field-even-active: Field signal level during the even field data
- transmission. Must be <0>.
-
-Note: Do not specify any of these properties if you want to use the embedded
- BT.656 synchronization.
-
-Optional Connector Properties:
-==============================
-
-- sdtv-standards: Set the possible signals to which the hardware tries to lock
- instead of using the autodetection mechanism. Please look at
- [1] for more information.
-
-[1] Documentation/devicetree/bindings/display/connector/analog-tv-connector.yaml.
-
-Example - three input sources:
-#include <dt-bindings/display/sdtv-standards.h>
-
-comp_connector_0 {
- compatible = "composite-video-connector";
- label = "Composite0";
- sdtv-standards = <SDTV_STD_PAL_M>; /* limit to pal-m signals */
-
- port {
- composite0_to_tvp5150: endpoint {
- remote-endpoint = <&tvp5150_to_composite0>;
- };
- };
-};
-
-comp_connector_1 {
- compatible = "composite-video-connector";
- label = "Composite1";
- sdtv-standards = <SDTV_STD_NTSC_M>; /* limit to ntsc-m signals */
-
- port {
- composite1_to_tvp5150: endpoint {
- remote-endpoint = <&tvp5150_to_composite1>;
- };
- };
-};
-
-svideo_connector {
- compatible = "svideo-connector";
- label = "S-Video";
-
- port {
- #address-cells = <1>;
- #size-cells = <0>;
-
- svideo_luma_to_tvp5150: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&tvp5150_to_svideo_luma>;
- };
-
- svideo_chroma_to_tvp5150: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&tvp5150_to_svideo_chroma>;
- };
- };
-};
-
-&i2c2 {
- tvp5150@5c {
- compatible = "ti,tvp5150";
- reg = <0x5c>;
- pdn-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>;
- reset-gpios = <&gpio6 7 GPIO_ACTIVE_LOW>;
- #address-cells = <1>;
- #size-cells = <0>;
-
- port@0 {
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <0>;
-
- tvp5150_to_composite0: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&composite0_to_tvp5150>;
- };
-
- tvp5150_to_svideo_luma: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&svideo_luma_to_tvp5150>;
- };
- };
-
- port@1 {
- #address-cells = <1>;
- #size-cells = <0>;
- reg = <1>;
-
- tvp5150_to_composite1: endpoint@0 {
- reg = <0>;
- remote-endpoint = <&composite1_to_tvp5150>;
- };
-
- tvp5150_to_svideo_chroma: endpoint@1 {
- reg = <1>;
- remote-endpoint = <&svideo_chroma_to_tvp5150>;
- };
- };
-
- port@2 {
- reg = <2>;
-
- tvp5150_1: endpoint {
- remote-endpoint = <&ccdc_ep>;
- };
- };
- };
-};
diff --git a/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.yaml b/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.yaml
new file mode 100644
index 000000000000..382a29652a05
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ti,tvp5150.yaml
@@ -0,0 +1,133 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ti,tvp5150.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TVP5150 and TVP5151 video decoders
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+description:
+ The TVP5150 and TVP5151 are video decoders that convert baseband NTSC and PAL
+ (and also SECAM in the TVP5151 case) video signals to either 8-bit 4:2:2 YUV
+ with discrete syncs or 8-bit ITU-R BT.656 with embedded syncs output formats.
+
+properties:
+ compatible:
+ const: ti,tvp5150
+
+ reg:
+ maxItems: 1
+
+ pdn-gpios:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ sink port node, AIP1A
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@1:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ sink port node, AIP1B
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@2:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description:
+ source port node, Y-OUT
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - port@2
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/display/sdtv-standards.h>
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bridge@5c {
+ compatible = "ti,tvp5150";
+ reg = <0x5c>;
+ pdn-gpios = <&gpio4 30 GPIO_ACTIVE_LOW>;
+ reset-gpios = <&gpio6 7 GPIO_ACTIVE_LOW>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>;
+
+ endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&composite0_to_tvp5150>;
+ };
+
+ endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&svideo_luma_to_tvp5150>;
+ };
+ };
+
+ port@1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>;
+
+ endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&composite1_to_tvp5150>;
+ };
+
+ endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&svideo_chroma_to_tvp5150>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ endpoint {
+ remote-endpoint = <&ccdc_ep>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/mediatek,mt8173-mdp.yaml b/Documentation/devicetree/bindings/media/mediatek,mt8173-mdp.yaml
new file mode 100644
index 000000000000..8ca33a733c47
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/mediatek,mt8173-mdp.yaml
@@ -0,0 +1,169 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/mediatek,mt8173-mdp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT8173 Media Data Path
+
+maintainers:
+ - Ariel D'Alessandro <ariel.dalessandro@collabora.com>
+
+description:
+ Media Data Path is used for scaling and color space conversion.
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - mediatek,mt8173-mdp-rdma
+ - mediatek,mt8173-mdp-rsz
+ - mediatek,mt8173-mdp-wdma
+ - mediatek,mt8173-mdp-wrot
+ - items:
+ - const: mediatek,mt8173-mdp-rdma
+ - const: mediatek,mt8173-mdp
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ minItems: 1
+ maxItems: 2
+
+ power-domains:
+ maxItems: 1
+
+ iommus:
+ maxItems: 1
+
+ mediatek,vpu:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ phandle to Mediatek Video Processor Unit for HW Codec encode/decode and
+ image processing.
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - power-domains
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt8173-mdp-rdma
+ then:
+ properties:
+ clocks:
+ items:
+ - description: Main clock
+ - description: Mutex clock
+ else:
+ properties:
+ clocks:
+ items:
+ - description: Main clock
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - mediatek,mt8173-mdp-rdma
+ - mediatek,mt8173-mdp-wdma
+ - mediatek,mt8173-mdp-wrot
+ then:
+ required:
+ - iommus
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mediatek,mt8173-mdp
+ then:
+ required:
+ - mediatek,vpu
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/mt8173-clk.h>
+ #include <dt-bindings/memory/mt8173-larb-port.h>
+ #include <dt-bindings/power/mt8173-power.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ mdp_rdma0: rdma@14001000 {
+ compatible = "mediatek,mt8173-mdp-rdma",
+ "mediatek,mt8173-mdp";
+ reg = <0 0x14001000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_RDMA0>,
+ <&mmsys CLK_MM_MUTEX_32K>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ iommus = <&iommu M4U_PORT_MDP_RDMA0>;
+ mediatek,vpu = <&vpu>;
+ };
+
+ mdp_rdma1: rdma@14002000 {
+ compatible = "mediatek,mt8173-mdp-rdma";
+ reg = <0 0x14002000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_RDMA1>,
+ <&mmsys CLK_MM_MUTEX_32K>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ iommus = <&iommu M4U_PORT_MDP_RDMA1>;
+ };
+
+ mdp_rsz0: rsz@14003000 {
+ compatible = "mediatek,mt8173-mdp-rsz";
+ reg = <0 0x14003000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_RSZ0>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ };
+
+ mdp_rsz1: rsz@14004000 {
+ compatible = "mediatek,mt8173-mdp-rsz";
+ reg = <0 0x14004000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_RSZ1>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ };
+
+ mdp_rsz2: rsz@14005000 {
+ compatible = "mediatek,mt8173-mdp-rsz";
+ reg = <0 0x14005000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_RSZ2>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ };
+
+ mdp_wdma0: wdma@14006000 {
+ compatible = "mediatek,mt8173-mdp-wdma";
+ reg = <0 0x14006000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_WDMA>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ iommus = <&iommu M4U_PORT_MDP_WDMA>;
+ };
+
+ mdp_wrot0: wrot@14007000 {
+ compatible = "mediatek,mt8173-mdp-wrot";
+ reg = <0 0x14007000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_WROT0>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ iommus = <&iommu M4U_PORT_MDP_WROT0>;
+ };
+
+ mdp_wrot1: wrot@14008000 {
+ compatible = "mediatek,mt8173-mdp-wrot";
+ reg = <0 0x14008000 0 0x1000>;
+ clocks = <&mmsys CLK_MM_MDP_WROT1>;
+ power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
+ iommus = <&iommu M4U_PORT_MDP_WROT1>;
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/media/mediatek-mdp.txt b/Documentation/devicetree/bindings/media/mediatek-mdp.txt
deleted file mode 100644
index 253a93eabb5e..000000000000
--- a/Documentation/devicetree/bindings/media/mediatek-mdp.txt
+++ /dev/null
@@ -1,96 +0,0 @@
-* Mediatek Media Data Path
-
-Media Data Path is used for scaling and color space conversion.
-
-Required properties (controller node):
-- compatible: "mediatek,mt8173-mdp"
-- mediatek,vpu: the node of video processor unit, see
- Documentation/devicetree/bindings/media/mediatek,mt8173-vpu.yaml for
- details.
-
-Required properties (all function blocks, child node):
-- compatible: Should be one of
- "mediatek,mt8173-mdp-rdma" - read DMA
- "mediatek,mt8173-mdp-rsz" - resizer
- "mediatek,mt8173-mdp-wdma" - write DMA
- "mediatek,mt8173-mdp-wrot" - write DMA with rotation
-- reg: Physical base address and length of the function block register space
-- clocks: device clocks, see
- Documentation/devicetree/bindings/clock/clock-bindings.txt for details.
-- power-domains: a phandle to the power domain, see
- Documentation/devicetree/bindings/power/power_domain.txt for details.
-
-Required properties (DMA function blocks, child node):
-- compatible: Should be one of
- "mediatek,mt8173-mdp-rdma"
- "mediatek,mt8173-mdp-wdma"
- "mediatek,mt8173-mdp-wrot"
-- iommus: should point to the respective IOMMU block with master port as
- argument, see Documentation/devicetree/bindings/iommu/mediatek,iommu.yaml
- for details.
-
-Example:
- mdp_rdma0: rdma@14001000 {
- compatible = "mediatek,mt8173-mdp-rdma";
- "mediatek,mt8173-mdp";
- reg = <0 0x14001000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_RDMA0>,
- <&mmsys CLK_MM_MUTEX_32K>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- iommus = <&iommu M4U_PORT_MDP_RDMA0>;
- mediatek,vpu = <&vpu>;
- };
-
- mdp_rdma1: rdma@14002000 {
- compatible = "mediatek,mt8173-mdp-rdma";
- reg = <0 0x14002000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_RDMA1>,
- <&mmsys CLK_MM_MUTEX_32K>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- iommus = <&iommu M4U_PORT_MDP_RDMA1>;
- };
-
- mdp_rsz0: rsz@14003000 {
- compatible = "mediatek,mt8173-mdp-rsz";
- reg = <0 0x14003000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_RSZ0>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- };
-
- mdp_rsz1: rsz@14004000 {
- compatible = "mediatek,mt8173-mdp-rsz";
- reg = <0 0x14004000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_RSZ1>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- };
-
- mdp_rsz2: rsz@14005000 {
- compatible = "mediatek,mt8173-mdp-rsz";
- reg = <0 0x14005000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_RSZ2>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- };
-
- mdp_wdma0: wdma@14006000 {
- compatible = "mediatek,mt8173-mdp-wdma";
- reg = <0 0x14006000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_WDMA>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- iommus = <&iommu M4U_PORT_MDP_WDMA>;
- };
-
- mdp_wrot0: wrot@14007000 {
- compatible = "mediatek,mt8173-mdp-wrot";
- reg = <0 0x14007000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_WROT0>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- iommus = <&iommu M4U_PORT_MDP_WROT0>;
- };
-
- mdp_wrot1: wrot@14008000 {
- compatible = "mediatek,mt8173-mdp-wrot";
- reg = <0 0x14008000 0 0x1000>;
- clocks = <&mmsys CLK_MM_MDP_WROT1>;
- power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
- iommus = <&iommu M4U_PORT_MDP_WROT1>;
- };
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
index f43b91984f01..001a0d9b71e0 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8-isi.yaml
@@ -22,6 +22,7 @@ properties:
- fsl,imx8mn-isi
- fsl,imx8mp-isi
- fsl,imx8ulp-isi
+ - fsl,imx91-isi
- fsl,imx93-isi
reg:
@@ -66,7 +67,6 @@ required:
- interrupts
- clocks
- clock-names
- - fsl,blk-ctrl
- ports
allOf:
@@ -77,6 +77,7 @@ allOf:
enum:
- fsl,imx8mn-isi
- fsl,imx8ulp-isi
+ - fsl,imx91-isi
- fsl,imx93-isi
then:
properties:
@@ -109,6 +110,16 @@ allOf:
- port@0
- port@1
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ const: fsl,imx91-isi
+ then:
+ required:
+ - fsl,blk-ctrl
+
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
index 4cba42ba7cf7..b5aca3d2cc5c 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
@@ -79,7 +79,6 @@ allOf:
power-domains:
minItems: 2 # Wrapper and 1 slot
-
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/media/qcom,msm8939-camss.yaml b/Documentation/devicetree/bindings/media/qcom,msm8939-camss.yaml
new file mode 100644
index 000000000000..77b389d76a43
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,msm8939-camss.yaml
@@ -0,0 +1,254 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/qcom,msm8939-camss.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm MSM8939 Camera Subsystem (CAMSS)
+
+maintainers:
+ - Vincent Knecht <vincent.knecht@mailoo.org>
+
+description:
+ The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms
+
+properties:
+ compatible:
+ const: qcom,msm8939-camss
+
+ reg:
+ maxItems: 11
+
+ reg-names:
+ items:
+ - const: csiphy0
+ - const: csiphy0_clk_mux
+ - const: csiphy1
+ - const: csiphy1_clk_mux
+ - const: csid0
+ - const: csid1
+ - const: ispif
+ - const: csi_clk_mux
+ - const: vfe0
+ - const: csid2
+ - const: vfe0_vbif
+
+ clocks:
+ maxItems: 24
+
+ clock-names:
+ items:
+ - const: top_ahb
+ - const: ispif_ahb
+ - const: csiphy0_timer
+ - const: csiphy1_timer
+ - const: csi0_ahb
+ - const: csi0
+ - const: csi0_phy
+ - const: csi0_pix
+ - const: csi0_rdi
+ - const: csi1_ahb
+ - const: csi1
+ - const: csi1_phy
+ - const: csi1_pix
+ - const: csi1_rdi
+ - const: ahb
+ - const: vfe0
+ - const: csi_vfe0
+ - const: vfe_ahb
+ - const: vfe_axi
+ - const: csi2_ahb
+ - const: csi2
+ - const: csi2_phy
+ - const: csi2_pix
+ - const: csi2_rdi
+
+ interrupts:
+ maxItems: 7
+
+ interrupt-names:
+ items:
+ - const: csiphy0
+ - const: csiphy1
+ - const: csid0
+ - const: csid1
+ - const: ispif
+ - const: vfe0
+ - const: csid2
+
+ iommus:
+ maxItems: 1
+
+ power-domains:
+ items:
+ - description: VFE GDSC - Video Front End, Global Distributed Switch
+ Controller.
+
+ vdda-supply:
+ description:
+ Definition of the regulator used as 1.2V analog power supply.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ description:
+ CSI input ports.
+
+ patternProperties:
+ "^port@[0-1]$":
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+
+ description:
+ Input port for receiving CSI data.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ bus-type:
+ enum:
+ - 4 # MEDIA_BUS_TYPE_CSI2_DPHY
+
+ required:
+ - data-lanes
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - interrupts
+ - interrupt-names
+ - iommus
+ - power-domains
+ - vdda-supply
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,gcc-msm8939.h>
+
+ isp@1b0ac00 {
+ compatible = "qcom,msm8939-camss";
+
+ reg = <0x01b0ac00 0x200>,
+ <0x01b00030 0x4>,
+ <0x01b0b000 0x200>,
+ <0x01b00038 0x4>,
+ <0x01b08000 0x100>,
+ <0x01b08400 0x100>,
+ <0x01b0a000 0x500>,
+ <0x01b00020 0x10>,
+ <0x01b10000 0x1000>,
+ <0x01b08800 0x100>,
+ <0x01b40000 0x200>;
+
+ reg-names = "csiphy0",
+ "csiphy0_clk_mux",
+ "csiphy1",
+ "csiphy1_clk_mux",
+ "csid0",
+ "csid1",
+ "ispif",
+ "csi_clk_mux",
+ "vfe0",
+ "csid2",
+ "vfe0_vbif";
+
+ clocks = <&gcc GCC_CAMSS_TOP_AHB_CLK>,
+ <&gcc GCC_CAMSS_ISPIF_AHB_CLK>,
+ <&gcc GCC_CAMSS_CSI0PHYTIMER_CLK>,
+ <&gcc GCC_CAMSS_CSI1PHYTIMER_CLK>,
+ <&gcc GCC_CAMSS_CSI0_AHB_CLK>,
+ <&gcc GCC_CAMSS_CSI0_CLK>,
+ <&gcc GCC_CAMSS_CSI0PHY_CLK>,
+ <&gcc GCC_CAMSS_CSI0PIX_CLK>,
+ <&gcc GCC_CAMSS_CSI0RDI_CLK>,
+ <&gcc GCC_CAMSS_CSI1_AHB_CLK>,
+ <&gcc GCC_CAMSS_CSI1_CLK>,
+ <&gcc GCC_CAMSS_CSI1PHY_CLK>,
+ <&gcc GCC_CAMSS_CSI1PIX_CLK>,
+ <&gcc GCC_CAMSS_CSI1RDI_CLK>,
+ <&gcc GCC_CAMSS_AHB_CLK>,
+ <&gcc GCC_CAMSS_VFE0_CLK>,
+ <&gcc GCC_CAMSS_CSI_VFE0_CLK>,
+ <&gcc GCC_CAMSS_VFE_AHB_CLK>,
+ <&gcc GCC_CAMSS_VFE_AXI_CLK>,
+ <&gcc GCC_CAMSS_CSI2_AHB_CLK>,
+ <&gcc GCC_CAMSS_CSI2_CLK>,
+ <&gcc GCC_CAMSS_CSI2PHY_CLK>,
+ <&gcc GCC_CAMSS_CSI2PIX_CLK>,
+ <&gcc GCC_CAMSS_CSI2RDI_CLK>;
+
+ clock-names = "top_ahb",
+ "ispif_ahb",
+ "csiphy0_timer",
+ "csiphy1_timer",
+ "csi0_ahb",
+ "csi0",
+ "csi0_phy",
+ "csi0_pix",
+ "csi0_rdi",
+ "csi1_ahb",
+ "csi1",
+ "csi1_phy",
+ "csi1_pix",
+ "csi1_rdi",
+ "ahb",
+ "vfe0",
+ "csi_vfe0",
+ "vfe_ahb",
+ "vfe_axi",
+ "csi2_ahb",
+ "csi2",
+ "csi2_phy",
+ "csi2_pix",
+ "csi2_rdi";
+
+ interrupts = <GIC_SPI 78 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 79 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 51 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 52 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 55 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 57 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 153 IRQ_TYPE_EDGE_RISING>;
+
+ interrupt-names = "csiphy0",
+ "csiphy1",
+ "csid0",
+ "csid1",
+ "ispif",
+ "vfe0",
+ "csid2";
+
+ iommus = <&apps_iommu 3>;
+
+ power-domains = <&gcc VFE_GDSC>;
+
+ vdda-supply = <&reg_1v2>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@1 {
+ reg = <1>;
+
+ csiphy1_ep: endpoint {
+ data-lanes = <0 2>;
+ remote-endpoint = <&sensor_ep>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml
index d195f1bfb23d..c99fe4106eee 100644
--- a/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sc8280xp-camss.yaml
@@ -484,7 +484,6 @@ examples:
"gcc_axi_hf",
"gcc_axi_sf";
-
iommus = <&apps_smmu 0x2000 0x4e0>,
<&apps_smmu 0x2020 0x4e0>,
<&apps_smmu 0x2040 0x4e0>,
diff --git a/Documentation/devicetree/bindings/media/qcom,sm8650-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm8650-camss.yaml
new file mode 100644
index 000000000000..9c8de722601e
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,sm8650-camss.yaml
@@ -0,0 +1,375 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/qcom,sm8650-camss.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SM8650 Camera Subsystem (CAMSS)
+
+maintainers:
+ - Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
+
+description:
+ The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms.
+
+properties:
+ compatible:
+ const: qcom,sm8650-camss
+
+ reg:
+ maxItems: 17
+
+ reg-names:
+ items:
+ - const: csid_wrapper
+ - const: csid0
+ - const: csid1
+ - const: csid2
+ - const: csid_lite0
+ - const: csid_lite1
+ - const: csiphy0
+ - const: csiphy1
+ - const: csiphy2
+ - const: csiphy3
+ - const: csiphy4
+ - const: csiphy5
+ - const: vfe0
+ - const: vfe1
+ - const: vfe2
+ - const: vfe_lite0
+ - const: vfe_lite1
+
+ clocks:
+ maxItems: 33
+
+ clock-names:
+ items:
+ - const: camnoc_axi
+ - const: cpas_ahb
+ - const: cpas_fast_ahb
+ - const: cpas_vfe0
+ - const: cpas_vfe1
+ - const: cpas_vfe2
+ - const: cpas_vfe_lite
+ - const: csid
+ - const: csiphy0
+ - const: csiphy0_timer
+ - const: csiphy1
+ - const: csiphy1_timer
+ - const: csiphy2
+ - const: csiphy2_timer
+ - const: csiphy3
+ - const: csiphy3_timer
+ - const: csiphy4
+ - const: csiphy4_timer
+ - const: csiphy5
+ - const: csiphy5_timer
+ - const: csiphy_rx
+ - const: gcc_axi_hf
+ - const: qdss_debug_xo
+ - const: vfe0
+ - const: vfe0_fast_ahb
+ - const: vfe1
+ - const: vfe1_fast_ahb
+ - const: vfe2
+ - const: vfe2_fast_ahb
+ - const: vfe_lite
+ - const: vfe_lite_ahb
+ - const: vfe_lite_cphy_rx
+ - const: vfe_lite_csid
+
+ interrupts:
+ maxItems: 16
+
+ interrupt-names:
+ items:
+ - const: csid0
+ - const: csid1
+ - const: csid2
+ - const: csid_lite0
+ - const: csid_lite1
+ - const: csiphy0
+ - const: csiphy1
+ - const: csiphy2
+ - const: csiphy3
+ - const: csiphy4
+ - const: csiphy5
+ - const: vfe0
+ - const: vfe1
+ - const: vfe2
+ - const: vfe_lite0
+ - const: vfe_lite1
+
+ interconnects:
+ maxItems: 2
+
+ interconnect-names:
+ items:
+ - const: ahb
+ - const: hf_mnoc
+
+ iommus:
+ maxItems: 3
+
+ power-domains:
+ items:
+ - description: IFE0 GDSC - Image Front End, Global Distributed Switch Controller.
+ - description: IFE1 GDSC - Image Front End, Global Distributed Switch Controller.
+ - description: IFE2 GDSC - Image Front End, Global Distributed Switch Controller.
+ - description: Titan GDSC - Titan ISP Block, Global Distributed Switch Controller.
+
+ power-domain-names:
+ items:
+ - const: ife0
+ - const: ife1
+ - const: ife2
+ - const: top
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ description:
+ CSI input ports.
+
+ patternProperties:
+ "^port@[0-5]$":
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+
+ description:
+ Input port for receiving CSI data from a CSIPHY.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ bus-type:
+ enum:
+ - 1 # MEDIA_BUS_TYPE_CSI2_CPHY
+ - 4 # MEDIA_BUS_TYPE_CSI2_DPHY
+
+ required:
+ - data-lanes
+
+ vdd-csiphy01-0p9-supply:
+ description:
+ Phandle to a 0.9V regulator supply to CSIPHY0 and CSIPHY1 IP blocks.
+
+ vdd-csiphy01-1p2-supply:
+ description:
+ Phandle to a 1.2V regulator supply to CSIPHY0 and CSIPHY1 IP blocks.
+
+ vdd-csiphy24-0p9-supply:
+ description:
+ Phandle to a 0.9V regulator supply to CSIPHY2 and CSIPHY4 IP blocks.
+
+ vdd-csiphy24-1p2-supply:
+ description:
+ Phandle to a 1.2V regulator supply to CSIPHY2 and CSIPHY4 IP blocks.
+
+ vdd-csiphy35-0p9-supply:
+ description:
+ Phandle to a 0.9V regulator supply to CSIPHY3 and CSIPHY5 IP blocks.
+
+ vdd-csiphy35-1p2-supply:
+ description:
+ Phandle to a 1.2V regulator supply to CSIPHY3 and CSIPHY5 IP blocks.
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - interconnects
+ - interconnect-names
+ - interrupts
+ - interrupt-names
+ - iommus
+ - power-domains
+ - power-domain-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,sm8650-camcc.h>
+ #include <dt-bindings/clock/qcom,sm8650-gcc.h>
+ #include <dt-bindings/interconnect/qcom,sm8650-rpmh.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ isp@acb6000 {
+ compatible = "qcom,sm8650-camss";
+ reg = <0 0x0acb6000 0 0x1000>,
+ <0 0x0acb8000 0 0x1000>,
+ <0 0x0acba000 0 0x1000>,
+ <0 0x0acbc000 0 0x1000>,
+ <0 0x0accb000 0 0x1000>,
+ <0 0x0acd0000 0 0x1000>,
+ <0 0x0ace4000 0 0x2000>,
+ <0 0x0ace6000 0 0x2000>,
+ <0 0x0ace8000 0 0x2000>,
+ <0 0x0acea000 0 0x2000>,
+ <0 0x0acec000 0 0x2000>,
+ <0 0x0acee000 0 0x2000>,
+ <0 0x0ac62000 0 0xf000>,
+ <0 0x0ac71000 0 0xf000>,
+ <0 0x0ac80000 0 0xf000>,
+ <0 0x0accc000 0 0x2000>,
+ <0 0x0acd1000 0 0x2000>;
+ reg-names = "csid_wrapper",
+ "csid0",
+ "csid1",
+ "csid2",
+ "csid_lite0",
+ "csid_lite1",
+ "csiphy0",
+ "csiphy1",
+ "csiphy2",
+ "csiphy3",
+ "csiphy4",
+ "csiphy5",
+ "vfe0",
+ "vfe1",
+ "vfe2",
+ "vfe_lite0",
+ "vfe_lite1";
+ clocks = <&camcc CAM_CC_CAMNOC_AXI_RT_CLK>,
+ <&camcc CAM_CC_CPAS_AHB_CLK>,
+ <&camcc CAM_CC_CPAS_FAST_AHB_CLK>,
+ <&camcc CAM_CC_CPAS_IFE_0_CLK>,
+ <&camcc CAM_CC_CPAS_IFE_1_CLK>,
+ <&camcc CAM_CC_CPAS_IFE_2_CLK>,
+ <&camcc CAM_CC_CPAS_IFE_LITE_CLK>,
+ <&camcc CAM_CC_CSID_CLK>,
+ <&camcc CAM_CC_CSIPHY0_CLK>,
+ <&camcc CAM_CC_CSI0PHYTIMER_CLK>,
+ <&camcc CAM_CC_CSI1PHYTIMER_CLK>,
+ <&camcc CAM_CC_CSIPHY1_CLK>,
+ <&camcc CAM_CC_CSI2PHYTIMER_CLK>,
+ <&camcc CAM_CC_CSIPHY2_CLK>,
+ <&camcc CAM_CC_CSI3PHYTIMER_CLK>,
+ <&camcc CAM_CC_CSIPHY3_CLK>,
+ <&camcc CAM_CC_CSI4PHYTIMER_CLK>,
+ <&camcc CAM_CC_CSIPHY4_CLK>,
+ <&camcc CAM_CC_CSI5PHYTIMER_CLK>,
+ <&camcc CAM_CC_CSIPHY5_CLK>,
+ <&camcc CAM_CC_CSID_CSIPHY_RX_CLK>,
+ <&gcc GCC_CAMERA_HF_AXI_CLK>,
+ <&camcc CAM_CC_QDSS_DEBUG_XO_CLK>,
+ <&camcc CAM_CC_IFE_0_CLK>,
+ <&camcc CAM_CC_IFE_0_FAST_AHB_CLK>,
+ <&camcc CAM_CC_IFE_1_CLK>,
+ <&camcc CAM_CC_IFE_1_FAST_AHB_CLK>,
+ <&camcc CAM_CC_IFE_2_CLK>,
+ <&camcc CAM_CC_IFE_2_FAST_AHB_CLK>,
+ <&camcc CAM_CC_IFE_LITE_CLK>,
+ <&camcc CAM_CC_IFE_LITE_AHB_CLK>,
+ <&camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>,
+ <&camcc CAM_CC_IFE_LITE_CSID_CLK>;
+ clock-names = "camnoc_axi",
+ "cpas_ahb",
+ "cpas_fast_ahb",
+ "cpas_vfe0",
+ "cpas_vfe1",
+ "cpas_vfe2",
+ "cpas_vfe_lite",
+ "csid",
+ "csiphy0",
+ "csiphy0_timer",
+ "csiphy1",
+ "csiphy1_timer",
+ "csiphy2",
+ "csiphy2_timer",
+ "csiphy3",
+ "csiphy3_timer",
+ "csiphy4",
+ "csiphy4_timer",
+ "csiphy5",
+ "csiphy5_timer",
+ "csiphy_rx",
+ "gcc_axi_hf",
+ "qdss_debug_xo",
+ "vfe0",
+ "vfe0_fast_ahb",
+ "vfe1",
+ "vfe1_fast_ahb",
+ "vfe2",
+ "vfe2_fast_ahb",
+ "vfe_lite",
+ "vfe_lite_ahb",
+ "vfe_lite_cphy_rx",
+ "vfe_lite_csid";
+ interrupts = <GIC_SPI 601 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 603 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 431 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 605 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 376 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 478 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 479 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 448 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 122 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 89 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 602 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 604 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 688 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 606 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 377 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "csid0",
+ "csid1",
+ "csid2",
+ "csid_lite0",
+ "csid_lite1",
+ "csiphy0",
+ "csiphy1",
+ "csiphy2",
+ "csiphy3",
+ "csiphy4",
+ "csiphy5",
+ "vfe0",
+ "vfe1",
+ "vfe2",
+ "vfe_lite0",
+ "vfe_lite1";
+ interconnects = <&gem_noc MASTER_APPSS_PROC 0
+ &config_noc SLAVE_CAMERA_CFG 0>,
+ <&mmss_noc MASTER_CAMNOC_HF 0
+ &mc_virt SLAVE_EBI1 0>;
+ interconnect-names = "ahb", "hf_mnoc";
+ iommus = <&apps_smmu 0x800 0x20>,
+ <&apps_smmu 0x18a0 0x40>,
+ <&apps_smmu 0x1860 0x00>;
+ power-domains = <&camcc CAM_CC_IFE_0_GDSC>,
+ <&camcc CAM_CC_IFE_1_GDSC>,
+ <&camcc CAM_CC_IFE_2_GDSC>,
+ <&camcc CAM_CC_TITAN_TOP_GDSC>;
+ power-domain-names = "ife0", "ife1", "ife2", "top";
+ vdd-csiphy01-0p9-supply = <&vreg_0p9>;
+ vdd-csiphy01-1p2-supply = <&vreg_1p2>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@1 {
+ reg = <1>;
+
+ csiphy1_ep: endpoint {
+ data-lanes = <0 1>;
+ remote-endpoint = <&camera_sensor>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/qcom,sm8750-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8750-iris.yaml
index c9a0fcafe53f..c42d3470bdac 100644
--- a/Documentation/devicetree/bindings/media/qcom,sm8750-iris.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sm8750-iris.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8750 SoC Iris video encoder and decoder
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The Iris video processing unit on Qualcomm SM8750 SoC is a video encode and
diff --git a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml
index b075341caafc..b87a13479a4b 100644
--- a/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,x1e80100-camss.yaml
@@ -124,7 +124,7 @@ properties:
vdd-csiphy-1p2-supply:
description:
- Phandle to 1.8V regulator supply to a PHY.
+ Phandle to 1.2V regulator supply to a PHY.
ports:
$ref: /schemas/graph.yaml#/properties/ports
diff --git a/Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml b/Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml
new file mode 100644
index 000000000000..c09cbd8c9e35
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/renesas,r9a09g057-ivc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas RZ/V2H(P) Input Video Control Block
+
+maintainers:
+ - Daniel Scally <dan.scally@ideasonboard.com>
+
+description:
+ The IVC block is a module that takes video frames from memory and feeds them
+ to the Image Signal Processor for processing.
+
+properties:
+ compatible:
+ const: renesas,r9a09g057-ivc # RZ/V2H(P)
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Input Video Control block register access clock
+ - description: Video input data AXI bus clock
+ - description: ISP system clock
+
+ clock-names:
+ items:
+ - const: reg
+ - const: axi
+ - const: isp
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ items:
+ - description: Input Video Control block register access reset
+ - description: Video input data AXI bus reset
+ - description: ISP core reset
+
+ reset-names:
+ items:
+ - const: reg
+ - const: axi
+ - const: isp
+
+ port:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Output parallel video bus
+
+ properties:
+ endpoint:
+ $ref: /schemas/graph.yaml#/properties/endpoint
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - power-domains
+ - resets
+ - reset-names
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/renesas,r9a09g057-cpg.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ isp-input@16040000 {
+ compatible = "renesas,r9a09g057-ivc";
+ reg = <0x16040000 0x230>;
+
+ clocks = <&cpg CPG_MOD 0xe3>,
+ <&cpg CPG_MOD 0xe4>,
+ <&cpg CPG_MOD 0xe5>;
+ clock-names = "reg", "axi", "isp";
+
+ power-domains = <&cpg>;
+
+ resets = <&cpg 0xd4>,
+ <&cpg 0xd1>,
+ <&cpg 0xd3>;
+ reset-names = "reg", "axi", "isp";
+
+ interrupts = <GIC_SPI 861 IRQ_TYPE_EDGE_RISING>;
+
+ port {
+ ivc_out: endpoint {
+ remote-endpoint = <&isp_in>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml b/Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml
new file mode 100644
index 000000000000..cc08ce94bef7
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml
@@ -0,0 +1,124 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/rockchip,px30-vip.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip PX30 Video Input Processor (VIP)
+
+maintainers:
+ - Mehdi Djait <mehdi.djait@linux.intel.com>
+ - Michael Riesch <michael.riesch@collabora.com>
+
+description:
+ The Rockchip PX30 Video Input Processor (VIP) receives the data from a camera
+ sensor or CCIR656 encoder and transfers it into system main memory by AXI bus.
+
+properties:
+ compatible:
+ const: rockchip,px30-vip
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: ACLK
+ - description: HCLK
+ - description: PCLK
+
+ clock-names:
+ items:
+ - const: aclk
+ - const: hclk
+ - const: pclk
+
+ resets:
+ items:
+ - description: AXI
+ - description: AHB
+ - description: PCLK IN
+
+ reset-names:
+ items:
+ - const: axi
+ - const: ahb
+ - const: pclkin
+
+ power-domains:
+ maxItems: 1
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: input port on the parallel interface
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ bus-type:
+ enum:
+ - 5 # MEDIA_BUS_TYPE_PARALLEL
+ - 6 # MEDIA_BUS_TYPE_BT656
+
+ required:
+ - bus-type
+
+ required:
+ - port@0
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/px30-cru.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/media/video-interfaces.h>
+ #include <dt-bindings/power/px30-power.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ video-capture@ff490000 {
+ compatible = "rockchip,px30-vip";
+ reg = <0x0 0xff490000 0x0 0x200>;
+ interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_CIF>, <&cru HCLK_CIF>, <&cru PCLK_CIF>;
+ clock-names = "aclk", "hclk", "pclk";
+ power-domains = <&power PX30_PD_VI>;
+ resets = <&cru SRST_CIF_A>, <&cru SRST_CIF_H>, <&cru SRST_CIF_PCLKIN>;
+ reset-names = "axi", "ahb", "pclkin";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ cif_in: endpoint {
+ remote-endpoint = <&tw9900_out>;
+ bus-type = <MEDIA_BUS_TYPE_BT656>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
new file mode 100644
index 000000000000..18cd0a5a5318
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
@@ -0,0 +1,172 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/rockchip,rk3568-vicap.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip RK3568 Video Capture (VICAP)
+
+maintainers:
+ - Michael Riesch <michael.riesch@collabora.com>
+
+description:
+ The Rockchip RK3568 Video Capture (VICAP) block features a digital video
+ port (DVP, a parallel video interface) and a MIPI CSI-2 port. It receives
+ the data from camera sensors, video decoders, or other companion ICs and
+ transfers it into system main memory by AXI bus.
+
+properties:
+ compatible:
+ const: rockchip,rk3568-vicap
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: ACLK
+ - description: HCLK
+ - description: DCLK
+ - description: ICLK
+
+ clock-names:
+ items:
+ - const: aclk
+ - const: hclk
+ - const: dclk
+ - const: iclk
+
+ iommus:
+ maxItems: 1
+
+ resets:
+ items:
+ - description: ARST
+ - description: HRST
+ - description: DRST
+ - description: PRST
+ - description: IRST
+
+ reset-names:
+ items:
+ - const: arst
+ - const: hrst
+ - const: drst
+ - const: prst
+ - const: irst
+
+ rockchip,grf:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: Phandle to general register file used for video input block control.
+
+ power-domains:
+ maxItems: 1
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: The digital video port (DVP, a parallel video interface).
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ bus-type:
+ enum:
+ - 5 # MEDIA_BUS_TYPE_PARALLEL
+ - 6 # MEDIA_BUS_TYPE_BT656
+
+ rockchip,dvp-clk-delay:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 0
+ minimum: 0
+ maximum: 127
+ description:
+ Delay the DVP path clock input to align the sampling phase,
+ only valid in dual edge sampling mode. Delay is zero by
+ default and can be adjusted optionally.
+
+ required:
+ - bus-type
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the MIPI CSI-2 receiver output.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/rk3568-cru.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/power/rk3568-power.h>
+ #include <dt-bindings/media/video-interfaces.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ vicap: video-capture@fdfe0000 {
+ compatible = "rockchip,rk3568-vicap";
+ reg = <0x0 0xfdfe0000 0x0 0x200>;
+ interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
+ assigned-clocks = <&cru DCLK_VICAP>;
+ assigned-clock-rates = <300000000>;
+ clocks = <&cru ACLK_VICAP>, <&cru HCLK_VICAP>,
+ <&cru DCLK_VICAP>, <&cru ICLK_VICAP_G>;
+ clock-names = "aclk", "hclk", "dclk", "iclk";
+ iommus = <&vicap_mmu>;
+ power-domains = <&power RK3568_PD_VI>;
+ resets = <&cru SRST_A_VICAP>, <&cru SRST_H_VICAP>,
+ <&cru SRST_D_VICAP>, <&cru SRST_P_VICAP>,
+ <&cru SRST_I_VICAP>;
+ reset-names = "arst", "hrst", "drst", "prst", "irst";
+ rockchip,grf = <&grf>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ vicap_dvp: port@0 {
+ reg = <0>;
+
+ vicap_dvp_input: endpoint {
+ bus-type = <MEDIA_BUS_TYPE_BT656>;
+ bus-width = <16>;
+ pclk-sample = <MEDIA_PCLK_SAMPLE_DUAL_EDGE>;
+ remote-endpoint = <&it6801_output>;
+ };
+ };
+
+ vicap_mipi: port@1 {
+ reg = <1>;
+
+ vicap_mipi_input: endpoint {
+ remote-endpoint = <&csi_output>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
index 96b6c8938768..809fda45b3bd 100644
--- a/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip,vdec.yaml
@@ -16,6 +16,7 @@ description: |-
properties:
compatible:
oneOf:
+ - const: rockchip,rk3288-vdec
- const: rockchip,rk3399-vdec
- const: rockchip,rk3576-vdec
- const: rockchip,rk3588-vdec
diff --git a/Documentation/devicetree/bindings/media/samsung,exynos4210-csis.yaml b/Documentation/devicetree/bindings/media/samsung,exynos4210-csis.yaml
index dd6cc7ac1f7c..2ddca4167b0b 100644
--- a/Documentation/devicetree/bindings/media/samsung,exynos4210-csis.yaml
+++ b/Documentation/devicetree/bindings/media/samsung,exynos4210-csis.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5P/Exynos SoC series MIPI CSI-2 receiver (MIPI CSIS)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
properties:
diff --git a/Documentation/devicetree/bindings/media/samsung,exynos4210-fimc.yaml b/Documentation/devicetree/bindings/media/samsung,exynos4210-fimc.yaml
index 2ba27b230559..17ece4eb300c 100644
--- a/Documentation/devicetree/bindings/media/samsung,exynos4210-fimc.yaml
+++ b/Documentation/devicetree/bindings/media/samsung,exynos4210-fimc.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5P/Exynos SoC Fully Integrated Mobile Camera
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
description:
diff --git a/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-is.yaml b/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-is.yaml
index 71d63bb9abb5..c8894358c46c 100644
--- a/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-is.yaml
+++ b/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-is.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos4212/4412 SoC Imaging Subsystem (FIMC-IS)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
description:
@@ -111,7 +111,6 @@ patternProperties:
reg:
maxItems: 1
-
clocks:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-lite.yaml b/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-lite.yaml
index f80eca0a4f41..bda724897293 100644
--- a/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-lite.yaml
+++ b/Documentation/devicetree/bindings/media/samsung,exynos4212-fimc-lite.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung Exynos SoC series camera host interface (FIMC-LITE)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
description:
diff --git a/Documentation/devicetree/bindings/media/samsung,fimc.yaml b/Documentation/devicetree/bindings/media/samsung,fimc.yaml
index 2a54379d9509..1bfba84f8854 100644
--- a/Documentation/devicetree/bindings/media/samsung,fimc.yaml
+++ b/Documentation/devicetree/bindings/media/samsung,fimc.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5P/Exynos SoC Camera Subsystem (FIMC)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
description: |
diff --git a/Documentation/devicetree/bindings/media/samsung,s5c73m3.yaml b/Documentation/devicetree/bindings/media/samsung,s5c73m3.yaml
index 1b75390fdaac..1af5d7ac382c 100644
--- a/Documentation/devicetree/bindings/media/samsung,s5c73m3.yaml
+++ b/Documentation/devicetree/bindings/media/samsung,s5c73m3.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Samsung S5C73M3 8Mp camera ISP
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Sylwester Nawrocki <s.nawrocki@samsung.com>
description:
diff --git a/Documentation/devicetree/bindings/media/samsung,s5pv210-jpeg.yaml b/Documentation/devicetree/bindings/media/samsung,s5pv210-jpeg.yaml
index e28d6ec56c0b..5c969e764d4f 100644
--- a/Documentation/devicetree/bindings/media/samsung,s5pv210-jpeg.yaml
+++ b/Documentation/devicetree/bindings/media/samsung,s5pv210-jpeg.yaml
@@ -42,7 +42,6 @@ properties:
reg:
maxItems: 1
-
required:
- compatible
- clocks
diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
index 510e94e9ca3a..b7f6c87d0e06 100644
--- a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
+++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
@@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Synopsys DesignWare HDMI RX Controller
maintainers:
- - Shreeya Patel <shreeya.patel@collabora.com>
+ - Dmitry Osipenko <dmitry.osipenko@collabora.com>
description:
Synopsys DesignWare HDMI Input Controller preset on RK3588 SoCs
diff --git a/Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml b/Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
index 4afa4a24b868..b9f7d84f38c2 100644
--- a/Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
+++ b/Documentation/devicetree/bindings/media/st,stm32-dma2d.yaml
@@ -21,7 +21,6 @@ description:
format and copy the result into a part or the whole of a destination image
with a different color format. (TODO)
-
maintainers:
- Dillon Min <dillon.minfei@gmail.com>
diff --git a/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt b/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
deleted file mode 100644
index 880d4d70c9fd..000000000000
--- a/Documentation/devicetree/bindings/media/stih407-c8sectpfe.txt
+++ /dev/null
@@ -1,88 +0,0 @@
-STMicroelectronics STi c8sectpfe binding
-============================================
-
-This document describes the c8sectpfe device bindings that is used to get transport
-stream data into the SoC on the TS pins, and into DDR for further processing.
-
-It is typically used in conjunction with one or more demodulator and tuner devices
-which converts from the RF to digital domain. Demodulators and tuners are usually
-located on an external DVB frontend card connected to SoC TS input pins.
-
-Currently 7 TS input (tsin) channels are supported on the stih407 family SoC.
-
-Required properties (controller (parent) node):
-- compatible : Should be "stih407-c8sectpfe"
-
-- reg : Address and length of register sets for each device in
- "reg-names"
-
-- reg-names : The names of the register addresses corresponding to the
- registers filled in "reg":
- - c8sectpfe: c8sectpfe registers
- - c8sectpfe-ram: c8sectpfe internal sram
-
-- clocks : phandle list of c8sectpfe clocks
-- clock-names : should be "c8sectpfe"
-See: Documentation/devicetree/bindings/clock/clock-bindings.txt
-
-- pinctrl-names : a pinctrl state named tsin%d-serial or tsin%d-parallel (where %d is tsin-num)
- must be defined for each tsin child node.
-- pinctrl-0 : phandle referencing pin configuration for this tsin configuration
-See: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
-
-
-Required properties (tsin (child) node):
-
-- tsin-num : tsin id of the InputBlock (must be between 0 to 6)
-- i2c-bus : phandle to the I2C bus DT node which the demodulators & tuners on this tsin channel are connected.
-- reset-gpios : reset gpio for this tsin channel.
-
-Optional properties (tsin (child) node):
-
-- invert-ts-clk : Bool property to control sense of ts input clock (data stored on falling edge of clk).
-- serial-not-parallel : Bool property to configure input bus width (serial on ts_data<7>).
-- async-not-sync : Bool property to control if data is received in asynchronous mode
- (all bits/bytes with ts_valid or ts_packet asserted are valid).
-
-- dvb-card : Describes the NIM card connected to this tsin channel.
-
-Example:
-
-/* stih410 SoC b2120 + b2004a + stv0367-pll(NIMB) + stv0367-tda18212 (NIMA) DT example) */
-
- c8sectpfe@8a20000 {
- compatible = "st,stih407-c8sectpfe";
- reg = <0x08a20000 0x10000>, <0x08a00000 0x4000>;
- reg-names = "stfe", "stfe-ram";
- interrupts = <GIC_SPI 34 IRQ_TYPE_NONE>, <GIC_SPI 35 IRQ_TYPE_NONE>;
- interrupt-names = "stfe-error-irq", "stfe-idle-irq";
- pinctrl-0 = <&pinctrl_tsin0_serial>;
- pinctrl-1 = <&pinctrl_tsin0_parallel>;
- pinctrl-2 = <&pinctrl_tsin3_serial>;
- pinctrl-3 = <&pinctrl_tsin4_serial_alt3>;
- pinctrl-4 = <&pinctrl_tsin5_serial_alt1>;
- pinctrl-names = "tsin0-serial",
- "tsin0-parallel",
- "tsin3-serial",
- "tsin4-serial",
- "tsin5-serial";
- clocks = <&clk_s_c0_flexgen CLK_PROC_STFE>;
- clock-names = "c8sectpfe";
-
- /* tsin0 is TSA on NIMA */
- tsin0: port@0 {
- tsin-num = <0>;
- serial-not-parallel;
- i2c-bus = <&ssc2>;
- reset-gpios = <&pio15 4 GPIO_ACTIVE_HIGH>;
- dvb-card = <STV0367_TDA18212_NIMA_1>;
- };
-
- tsin3: port@3 {
- tsin-num = <3>;
- serial-not-parallel;
- i2c-bus = <&ssc3>;
- reset-gpios = <&pio15 7 GPIO_ACTIVE_HIGH>;
- dvb-card = <STV0367_TDA18212_NIMB_1>;
- };
- };
diff --git a/Documentation/devicetree/bindings/media/video-interface-devices.yaml b/Documentation/devicetree/bindings/media/video-interface-devices.yaml
index 3ad1590b0496..a81d2a155fe6 100644
--- a/Documentation/devicetree/bindings/media/video-interface-devices.yaml
+++ b/Documentation/devicetree/bindings/media/video-interface-devices.yaml
@@ -128,7 +128,6 @@ properties:
0 degrees camera rotation:
-
Y-Rp
^
Y-Rc !
@@ -145,7 +144,6 @@ properties:
0 +------------------------------------->
0 X-Rc
-
X-Rc 0
<------------------------------------+ 0
X-Rp 0 !
@@ -228,7 +226,6 @@ properties:
V
X-Rc
-
Example one - Webcam
A camera module installed on the user facing part of a laptop screen
@@ -273,7 +270,6 @@ properties:
optical inversion, the two reference systems will not be aligned, with
'Rp' being rotated 180 degrees relatively to 'Rc':
-
X-Rc 0
<------------------------------------+ 0
!
diff --git a/Documentation/devicetree/bindings/media/video-interfaces.yaml b/Documentation/devicetree/bindings/media/video-interfaces.yaml
index 038e85b45bef..6ed4695cacf7 100644
--- a/Documentation/devicetree/bindings/media/video-interfaces.yaml
+++ b/Documentation/devicetree/bindings/media/video-interfaces.yaml
@@ -95,7 +95,7 @@ properties:
- 6 # BT.656
- 7 # DPI
description:
- Data bus type.
+ Data bus type. See include/dt-bindings/media/video-interfaces.h.
bus-width:
$ref: /schemas/types.yaml#/definitions/uint32
@@ -229,7 +229,7 @@ properties:
Imaging. The length of the array must be the same length as the
data-lanes property. If the line-orders property is omitted, the value
shall be interpreted as 0 (ABC). This property is valid for CSI-2 C-PHY
- busses only.
+ busses only. See include/dt-bindings/media/video-interfaces.h.
strobe:
$ref: /schemas/types.yaml#/definitions/uint32
diff --git a/Documentation/devicetree/bindings/memory-controllers/qcom,ebi2-peripheral-props.yaml b/Documentation/devicetree/bindings/memory-controllers/qcom,ebi2-peripheral-props.yaml
index 29f8c30e8a88..aec88cd2df76 100644
--- a/Documentation/devicetree/bindings/memory-controllers/qcom,ebi2-peripheral-props.yaml
+++ b/Documentation/devicetree/bindings/memory-controllers/qcom,ebi2-peripheral-props.yaml
@@ -62,7 +62,6 @@ properties:
minimum: 0
maximum: 15
-
# FAST chip selects
qcom,xmem-address-hold-enable:
$ref: /schemas/types.yaml#/definitions/uint32
diff --git a/Documentation/devicetree/bindings/mfd/aspeed-lpc.yaml b/Documentation/devicetree/bindings/mfd/aspeed-lpc.yaml
index f329223cec07..cbc3a2485a2f 100644
--- a/Documentation/devicetree/bindings/mfd/aspeed-lpc.yaml
+++ b/Documentation/devicetree/bindings/mfd/aspeed-lpc.yaml
@@ -48,16 +48,16 @@ properties:
reg:
maxItems: 1
- "#address-cells":
+ '#address-cells':
const: 1
- "#size-cells":
+ '#size-cells':
const: 1
ranges: true
patternProperties:
- "^lpc-ctrl@[0-9a-f]+$":
+ '^lpc-ctrl@[0-9a-f]+$':
type: object
additionalProperties: false
@@ -92,7 +92,7 @@ patternProperties:
- compatible
- clocks
- "^reset-controller@[0-9a-f]+$":
+ '^reset-controller@[0-9a-f]+$':
type: object
additionalProperties: false
@@ -118,7 +118,7 @@ patternProperties:
- compatible
- '#reset-cells'
- "^lpc-snoop@[0-9a-f]+$":
+ '^lpc-snoop@[0-9a-f]+$':
type: object
additionalProperties: false
@@ -152,15 +152,15 @@ patternProperties:
- interrupts
- snoop-ports
- "^uart-routing@[0-9a-f]+$":
+ '^uart-routing@[0-9a-f]+$':
$ref: /schemas/soc/aspeed/uart-routing.yaml#
description: The UART routing control under LPC register space
required:
- compatible
- reg
- - "#address-cells"
- - "#size-cells"
+ - '#address-cells'
+ - '#size-cells'
- ranges
additionalProperties:
diff --git a/Documentation/devicetree/bindings/mfd/da9052-i2c.txt b/Documentation/devicetree/bindings/mfd/da9052-i2c.txt
deleted file mode 100644
index 07c69c0c6624..000000000000
--- a/Documentation/devicetree/bindings/mfd/da9052-i2c.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-* Dialog DA9052/53 Power Management Integrated Circuit (PMIC)
-
-Required properties:
-- compatible : Should be "dlg,da9052", "dlg,da9053-aa",
- "dlg,da9053-ab", or "dlg,da9053-bb"
-
-Optional properties:
-- dlg,tsi-as-adc : Boolean, if set the X+, X-, Y+, Y- touchscreen
- input lines are used as general purpose analogue
- input.
-- tsiref-supply: Phandle to the regulator, which provides the reference
- voltage for the TSIREF pin. Must be provided when the
- touchscreen pins are used for ADC purposes.
-
-Sub-nodes:
-- regulators : Contain the regulator nodes. The DA9052/53 regulators are
- bound using their names as listed below:
-
- buck1 : regulator BUCK CORE
- buck2 : regulator BUCK PRO
- buck3 : regulator BUCK MEM
- buck4 : regulator BUCK PERI
- ldo1 : regulator LDO1
- ldo2 : regulator LDO2
- ldo3 : regulator LDO3
- ldo4 : regulator LDO4
- ldo5 : regulator LDO5
- ldo6 : regulator LDO6
- ldo7 : regulator LDO7
- ldo8 : regulator LDO8
- ldo9 : regulator LDO9
- ldo10 : regulator LDO10
-
- The bindings details of individual regulator device can be found in:
- Documentation/devicetree/bindings/regulator/regulator.txt
-
-Examples:
-
-i2c@63fc8000 { /* I2C1 */
-
- pmic: dialog@48 {
- compatible = "dlg,da9053-aa";
- reg = <0x48>;
-
- regulators {
- buck1 {
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <2075000>;
- };
-
- buck2 {
- regulator-min-microvolt = <500000>;
- regulator-max-microvolt = <2075000>;
- };
-
- buck3 {
- regulator-min-microvolt = <925000>;
- regulator-max-microvolt = <2500000>;
- };
-
- buck4 {
- regulator-min-microvolt = <925000>;
- regulator-max-microvolt = <2500000>;
- };
- };
- };
-};
diff --git a/Documentation/devicetree/bindings/mfd/dlg,da9052.yaml b/Documentation/devicetree/bindings/mfd/dlg,da9052.yaml
new file mode 100644
index 000000000000..1103a8cc5cea
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/dlg,da9052.yaml
@@ -0,0 +1,89 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/dlg,da9052.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Dialog DA9052/53 Power Management Integrated Circuit (PMIC)
+
+maintainers:
+ - Frank Li <Frank.Li@nxp.com>
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - dlg,da9053-aa
+ - dlg,da9053-ab
+ - dlg,da9053-bb
+ - dlg,da9053-bc
+ - dlg,da9052
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ dlg,tsi-as-adc:
+ type: boolean
+ description:
+ if set the X+, X-, Y+, Y- touchscreen input lines are used as general
+ purpose analogue input.
+
+ tsiref-supply:
+ description: The reference voltage for the TSIREF pin.
+
+ regulators:
+ type: object
+ additionalProperties: false
+
+ patternProperties:
+ "^(ldo([1-9]|10)|buck[1-4])$":
+ type: object
+ $ref: /schemas/regulator/regulator.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - regulators
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pmic@48 {
+ compatible = "dlg,da9053-aa";
+ reg = <0x48>;
+
+ regulators {
+ buck1 {
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2075000>;
+ };
+
+ buck2 {
+ regulator-min-microvolt = <500000>;
+ regulator-max-microvolt = <2075000>;
+ };
+
+ buck3 {
+ regulator-min-microvolt = <925000>;
+ regulator-max-microvolt = <2500000>;
+ };
+
+ buck4 {
+ regulator-min-microvolt = <925000>;
+ regulator-max-microvolt = <2500000>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/dlg,da9063.yaml b/Documentation/devicetree/bindings/mfd/dlg,da9063.yaml
index 51612dc22748..4f08e9ac7e56 100644
--- a/Documentation/devicetree/bindings/mfd/dlg,da9063.yaml
+++ b/Documentation/devicetree/bindings/mfd/dlg,da9063.yaml
@@ -81,6 +81,8 @@ properties:
watchdog:
$ref: /schemas/watchdog/dlg,da9062-watchdog.yaml
+ wakeup-source: true
+
patternProperties:
"^(.+-hog(-[0-9]+)?)$":
type: object
diff --git a/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml b/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml
index d2886f2686a8..cfa69f1f380a 100644
--- a/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml
+++ b/Documentation/devicetree/bindings/mfd/fsl,mc13xxx.yaml
@@ -93,38 +93,14 @@ properties:
leds:
type: object
- $ref: /schemas/leds/common.yaml#
+ additionalProperties: false
properties:
- reg:
- description: |
- One of
- MC13783 LED IDs
- 0: Main display
- 1: AUX display
- 2: Keypad
- 3: Red 1
- 4: Green 1
- 5: Blue 1
- 6: Red 2
- 7: Green 2
- 8: Blue 2
- 9: Red 3
- 10: Green 3
- 11: Blue 3
-
- MC13892 LED IDs
- 0: Main display
- 1: AUX display
- 2: Keypad
- 3: Red
- 4: Green
- 5: Blue
-
- MC34708 LED IDs
- 0: Charger Red
- 1: Charger Green
- maxItems: 1
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
led-control:
$ref: /schemas/types.yaml#/definitions/uint32-array
@@ -132,6 +108,42 @@ properties:
Setting for LED-Control register array length depends on model,
mc13783: 6, mc13892: 4, mc34708: 1
+ patternProperties:
+ '^led@[0-9a-b]$':
+ $ref: /schemas/leds/common.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ reg:
+ description: |
+ One of
+ MC13783 LED IDs
+ 0: Main display
+ 1: AUX display
+ 2: Keypad
+ 3: Red 1
+ 4: Green 1
+ 5: Blue 1
+ 6: Red 2
+ 7: Green 2
+ 8: Blue 2
+ 9: Red 3
+ 10: Green 3
+ 11: Blue 3
+
+ MC13892 LED IDs
+ 0: Main display
+ 1: AUX display
+ 2: Keypad
+ 3: Red
+ 4: Green
+ 5: Blue
+
+ MC34708 LED IDs
+ 0: Charger Red
+ 1: Charger Green
+ maxItems: 1
+
regulators:
type: object
@@ -262,7 +274,7 @@ examples:
#size-cells = <0>;
led-control = <0x000 0x000 0x0e0 0x000>;
- sysled@3 {
+ led@3 {
reg = <3>;
label = "system:red:live";
linux,default-trigger = "heartbeat";
diff --git a/Documentation/devicetree/bindings/mfd/maxim,max77705.yaml b/Documentation/devicetree/bindings/mfd/maxim,max77705.yaml
index 0ec89f0adc64..8b62aadb4213 100644
--- a/Documentation/devicetree/bindings/mfd/maxim,max77705.yaml
+++ b/Documentation/devicetree/bindings/mfd/maxim,max77705.yaml
@@ -26,6 +26,18 @@ properties:
interrupts:
maxItems: 1
+ interrupt-controller:
+ description:
+ The driver implements an interrupt controller for the sub devices.
+ The interrupt number mapping is as follows
+ 0 - charger
+ 1 - topsys
+ 2 - fuelgauge
+ 3 - usb type-c management block.
+
+ '#interrupt-cells':
+ const: 1
+
haptic:
type: object
additionalProperties: false
@@ -118,8 +130,10 @@ examples:
pmic@66 {
compatible = "maxim,max77705";
reg = <0x66>;
+ #interrupt-cells = <1>;
interrupt-parent = <&pm8998_gpios>;
interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
pinctrl-0 = <&chg_int_default>;
pinctrl-names = "default";
diff --git a/Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml b/Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml
new file mode 100644
index 000000000000..e50dc44252c6
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml
@@ -0,0 +1,161 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/nxp,pf1550.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP PF1550 Power Management IC
+
+maintainers:
+ - Samuel Kayode <samuel.kayode@savoirfairelinux.com>
+
+description:
+ PF1550 PMIC provides battery charging and power supply for low power IoT and
+ wearable applications. This device consists of an i2c controlled MFD that
+ includes regulators, battery charging and an onkey/power button.
+
+$ref: /schemas/power/supply/power-supply.yaml
+
+properties:
+ compatible:
+ const: nxp,pf1550
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ wakeup-source: true
+
+ regulators:
+ type: object
+ additionalProperties: false
+
+ patternProperties:
+ "^(ldo[1-3]|sw[1-3]|vrefddr)$":
+ type: object
+ $ref: /schemas/regulator/regulator.yaml
+ description:
+ regulator configuration for ldo1-3, buck converters(sw1-3)
+ and DDR termination reference voltage (vrefddr)
+ unevaluatedProperties: false
+
+ monitored-battery:
+ description: |
+ A phandle to a monitored battery node that contains a valid value
+ for:
+ constant-charge-voltage-max-microvolt.
+
+ nxp,thermal-regulation-celsius:
+ description:
+ Temperature threshold for thermal regulation of charger in celsius.
+ enum: [ 80, 95, 110, 125 ]
+
+ nxp,min-system-microvolt:
+ description:
+ System specific lower limit voltage.
+ enum: [ 3500000, 3700000, 4300000 ]
+
+ nxp,disable-key-power:
+ type: boolean
+ description:
+ Disable power-down using a long key-press. The onkey driver will remove
+ support for the KEY_POWER key press when triggered using a long press of
+ the onkey.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/input/linux-event-codes.h>
+
+ battery: battery-cell {
+ compatible = "simple-battery";
+ constant-charge-voltage-max-microvolt = <4400000>;
+ };
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pmic@8 {
+ compatible = "nxp,pf1550";
+ reg = <0x8>;
+
+ interrupt-parent = <&gpio1>;
+ interrupts = <2 IRQ_TYPE_LEVEL_LOW>;
+ wakeup-source;
+ monitored-battery = <&battery>;
+ nxp,min-system-microvolt = <4300000>;
+ nxp,thermal-regulation-celsius = <80>;
+
+ regulators {
+ sw1_reg: sw1 {
+ regulator-name = "sw1";
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1387500>;
+ regulator-always-on;
+ regulator-ramp-delay = <6250>;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ regulator-suspend-min-microvolt = <1270000>;
+ };
+ };
+
+ sw2_reg: sw2 {
+ regulator-name = "sw2";
+ regulator-min-microvolt = <600000>;
+ regulator-max-microvolt = <1387500>;
+ regulator-always-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ sw3_reg: sw3 {
+ regulator-name = "sw3";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+
+ regulator-state-mem {
+ regulator-on-in-suspend;
+ };
+ };
+
+ vldo1_reg: ldo1 {
+ regulator-name = "ldo1";
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+
+ regulator-state-mem {
+ regulator-off-in-suspend;
+ };
+ };
+
+ vldo2_reg: ldo2 {
+ regulator-name = "ldo2";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ vldo3_reg: ldo3 {
+ regulator-name = "ldo3";
+ regulator-min-microvolt = <750000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
index 078a6886f8b1..65c80e3b4500 100644
--- a/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
+++ b/Documentation/devicetree/bindings/mfd/qcom,spmi-pmic.yaml
@@ -43,6 +43,7 @@ properties:
- qcom,pm7250b
- qcom,pm7550ba
- qcom,pm7325
+ - qcom,pm7550
- qcom,pm8004
- qcom,pm8005
- qcom,pm8009
@@ -84,6 +85,7 @@ properties:
- qcom,pmi8994
- qcom,pmi8998
- qcom,pmih0108
+ - qcom,pmiv0104
- qcom,pmk8002
- qcom,pmk8350
- qcom,pmk8550
diff --git a/Documentation/devicetree/bindings/mfd/renesas,r2a11302ft.yaml b/Documentation/devicetree/bindings/mfd/renesas,r2a11302ft.yaml
new file mode 100644
index 000000000000..7b96619ebd8c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/renesas,r2a11302ft.yaml
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/renesas,r2a11302ft.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas R2A11302FT Power Supply ICs for R-Car
+
+maintainers:
+ - Wolfram Sang <wsa+renesas@sang-engineering.com>
+
+description: |
+ The Renesas R2A11302FT PMIC is used with Renesas R-Car Gen1/Gen2
+ based SoCs.
+
+ FIXME: The binding is incomplete and resembles the information gathered
+ so far.
+
+properties:
+ compatible:
+ const: renesas,r2a11302ft
+
+ reg:
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 6000000
+
+ spi-cpol: true
+
+ spi-cpha: true
+
+required:
+ - compatible
+ - reg
+ - spi-cpol
+ - spi-cpha
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pmic@0 {
+ compatible = "renesas,r2a11302ft";
+ reg = <0>;
+ spi-max-frequency = <6000000>;
+ spi-cpol;
+ spi-cpha;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/mfd/silergy,sy7636a.yaml b/Documentation/devicetree/bindings/mfd/silergy,sy7636a.yaml
index ee0be32ac020..4f829fe75d41 100644
--- a/Documentation/devicetree/bindings/mfd/silergy,sy7636a.yaml
+++ b/Documentation/devicetree/bindings/mfd/silergy,sy7636a.yaml
@@ -32,6 +32,17 @@ properties:
Specifying the power good GPIOs.
maxItems: 1
+ enable-gpios:
+ maxItems: 1
+
+ vcom-en-gpios:
+ maxItems: 1
+
+ vin-supply:
+ description:
+ Supply for the whole chip. Some vendor kernels and devicetrees
+ declare this as a non-existing GPIO named "pwrall".
+
regulators:
type: object
diff --git a/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml b/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml
index b2cfa4120b8a..ce5e845ab5c5 100644
--- a/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml
+++ b/Documentation/devicetree/bindings/mfd/stericsson,ab8500.yaml
@@ -444,7 +444,6 @@ properties:
additionalProperties: false
-
regulator-external:
description: Node describing the AB8500 external regulators. This
concerns the autonomous regulators VSMPS1, VSMPS2 and VSMPS3
diff --git a/Documentation/devicetree/bindings/mfd/syscon.yaml b/Documentation/devicetree/bindings/mfd/syscon.yaml
index 657c38175fba..55efb83b1495 100644
--- a/Documentation/devicetree/bindings/mfd/syscon.yaml
+++ b/Documentation/devicetree/bindings/mfd/syscon.yaml
@@ -85,6 +85,7 @@ select:
- mediatek,mt2701-pctl-a-syscfg
- mediatek,mt2712-pctl-a-syscfg
- mediatek,mt6397-pctl-pmic-syscfg
+ - mediatek,mt7981-topmisc
- mediatek,mt7988-topmisc
- mediatek,mt8135-pctl-a-syscfg
- mediatek,mt8135-pctl-b-syscfg
@@ -133,111 +134,126 @@ select:
properties:
compatible:
- items:
- - enum:
- - airoha,en7581-pbus-csr
- - al,alpine-sysfabric-service
- - allwinner,sun8i-a83t-system-controller
- - allwinner,sun8i-h3-system-controller
- - allwinner,sun8i-v3s-system-controller
- - allwinner,sun50i-a64-system-controller
- - altr,l3regs
- - altr,sdr-ctl
- - amd,pensando-elba-syscon
- - amlogic,meson-mx-assist
- - amlogic,meson-mx-bootrom
- - amlogic,meson8-analog-top
- - amlogic,meson8b-analog-top
- - amlogic,meson8-pmu
- - amlogic,meson8b-pmu
- - apm,merlin-poweroff-mailbox
- - apm,mustang-poweroff-mailbox
- - apm,xgene-csw
- - apm,xgene-efuse
- - apm,xgene-mcb
- - apm,xgene-rb
- - apm,xgene-scu
- - atmel,sama5d2-sfrbu
- - atmel,sama5d3-nfc-io
- - atmel,sama5d3-sfrbu
- - atmel,sama5d4-sfrbu
- - axis,artpec6-syscon
- - brcm,cru-clkset
- - brcm,sr-cdru
- - brcm,sr-mhb
- - cirrus,ep7209-syscon1
- - cirrus,ep7209-syscon2
- - cirrus,ep7209-syscon3
- - cnxt,cx92755-uc
- - freecom,fsg-cs2-system-controller
- - fsl,imx93-aonmix-ns-syscfg
- - fsl,imx93-wakeupmix-syscfg
- - fsl,ls1088a-reset
- - fsl,vf610-anatop
- - fsl,vf610-mscm-cpucfg
- - hisilicon,dsa-subctrl
- - hisilicon,hi6220-sramctrl
- - hisilicon,hip04-ppe
- - hisilicon,pcie-sas-subctrl
- - hisilicon,peri-subctrl
- - hpe,gxp-sysreg
- - loongson,ls1b-syscon
- - loongson,ls1c-syscon
- - lsi,axxia-syscon
- - marvell,armada-3700-cpu-misc
- - marvell,armada-3700-nb-pm
- - marvell,armada-3700-avs
- - marvell,armada-3700-usb2-host-device-misc
- - marvell,armada-3700-usb2-host-misc
- - marvell,dove-global-config
- - mediatek,mt2701-pctl-a-syscfg
- - mediatek,mt2712-pctl-a-syscfg
- - mediatek,mt6397-pctl-pmic-syscfg
- - mediatek,mt7988-topmisc
- - mediatek,mt8135-pctl-a-syscfg
- - mediatek,mt8135-pctl-b-syscfg
- - mediatek,mt8173-pctl-a-syscfg
- - mediatek,mt8365-infracfg-nao
- - mediatek,mt8365-syscfg
- - microchip,lan966x-cpu-syscon
- - microchip,mpfs-control-scb
- - microchip,mpfs-sysreg-scb
- - microchip,sam9x60-sfr
- - microchip,sama7d65-ddr3phy
- - microchip,sama7d65-sfrbu
- - microchip,sama7g5-ddr3phy
- - mscc,ocelot-cpu-syscon
- - mstar,msc313-pmsleep
- - nuvoton,ma35d1-sys
- - nuvoton,wpcm450-shm
- - qcom,apq8064-mmss-sfpb
- - qcom,apq8064-sps-sic
- - rockchip,px30-qos
- - rockchip,rk3036-qos
- - rockchip,rk3066-qos
- - rockchip,rk3128-qos
- - rockchip,rk3228-qos
- - rockchip,rk3288-qos
- - rockchip,rk3368-qos
- - rockchip,rk3399-qos
- - rockchip,rk3528-qos
- - rockchip,rk3562-qos
- - rockchip,rk3568-qos
- - rockchip,rk3576-qos
- - rockchip,rk3588-qos
- - rockchip,rv1126-qos
- - st,spear1340-misc
- - stericsson,nomadik-pmu
- - starfive,jh7100-sysmain
- - ti,am62-opp-efuse-table
- - ti,am62-usb-phy-ctrl
- - ti,am625-dss-oldi-io-ctrl
- - ti,am62p-cpsw-mac-efuse
- - ti,am654-dss-oldi-io-ctrl
- - ti,j784s4-acspcie-proxy-ctrl
- - ti,j784s4-pcie-ctrl
- - ti,keystone-pllctrl
- - const: syscon
+ oneOf:
+ - items:
+ - enum:
+ - airoha,en7581-pbus-csr
+ - al,alpine-sysfabric-service
+ - allwinner,sun8i-a83t-system-controller
+ - allwinner,sun8i-h3-system-controller
+ - allwinner,sun8i-v3s-system-controller
+ - allwinner,sun50i-a64-system-controller
+ - altr,l3regs
+ - altr,sdr-ctl
+ - amd,pensando-elba-syscon
+ - amlogic,meson-mx-assist
+ - amlogic,meson-mx-bootrom
+ - amlogic,meson8-analog-top
+ - amlogic,meson8b-analog-top
+ - amlogic,meson8-pmu
+ - amlogic,meson8b-pmu
+ - apm,merlin-poweroff-mailbox
+ - apm,mustang-poweroff-mailbox
+ - apm,xgene-csw
+ - apm,xgene-efuse
+ - apm,xgene-mcb
+ - apm,xgene-rb
+ - apm,xgene-scu
+ - atmel,sama5d2-sfrbu
+ - atmel,sama5d3-nfc-io
+ - atmel,sama5d3-sfrbu
+ - atmel,sama5d4-sfrbu
+ - axis,artpec6-syscon
+ - brcm,cru-clkset
+ - brcm,sr-cdru
+ - brcm,sr-mhb
+ - cirrus,ep7209-syscon1
+ - cirrus,ep7209-syscon2
+ - cirrus,ep7209-syscon3
+ - cnxt,cx92755-uc
+ - freecom,fsg-cs2-system-controller
+ - fsl,imx93-aonmix-ns-syscfg
+ - fsl,imx93-wakeupmix-syscfg
+ - fsl,ls1088a-reset
+ - fsl,vf610-anatop
+ - fsl,vf610-mscm-cpucfg
+ - hisilicon,dsa-subctrl
+ - hisilicon,hi6220-sramctrl
+ - hisilicon,hip04-ppe
+ - hisilicon,pcie-sas-subctrl
+ - hisilicon,peri-subctrl
+ - hpe,gxp-sysreg
+ - loongson,ls1b-syscon
+ - loongson,ls1c-syscon
+ - lsi,axxia-syscon
+ - marvell,armada-3700-cpu-misc
+ - marvell,armada-3700-nb-pm
+ - marvell,armada-3700-avs
+ - marvell,armada-3700-usb2-host-device-misc
+ - marvell,armada-3700-usb2-host-misc
+ - marvell,dove-global-config
+ - mediatek,mt2701-pctl-a-syscfg
+ - mediatek,mt2712-pctl-a-syscfg
+ - mediatek,mt6397-pctl-pmic-syscfg
+ - mediatek,mt7988-topmisc
+ - mediatek,mt8135-pctl-a-syscfg
+ - mediatek,mt8135-pctl-b-syscfg
+ - mediatek,mt8173-pctl-a-syscfg
+ - mediatek,mt8365-infracfg-nao
+ - mediatek,mt8365-syscfg
+ - microchip,lan966x-cpu-syscon
+ - microchip,mpfs-control-scb
+ - microchip,mpfs-sysreg-scb
+ - microchip,sam9x60-sfr
+ - microchip,sama7d65-ddr3phy
+ - microchip,sama7d65-sfrbu
+ - microchip,sama7g5-ddr3phy
+ - mscc,ocelot-cpu-syscon
+ - mstar,msc313-pmsleep
+ - nuvoton,ma35d1-sys
+ - nuvoton,wpcm450-shm
+ - qcom,apq8064-mmss-sfpb
+ - qcom,apq8064-sps-sic
+ - rockchip,px30-qos
+ - rockchip,rk3036-qos
+ - rockchip,rk3066-qos
+ - rockchip,rk3128-qos
+ - rockchip,rk3228-qos
+ - rockchip,rk3288-qos
+ - rockchip,rk3368-qos
+ - rockchip,rk3399-qos
+ - rockchip,rk3528-qos
+ - rockchip,rk3562-qos
+ - rockchip,rk3568-qos
+ - rockchip,rk3576-qos
+ - rockchip,rk3588-qos
+ - rockchip,rv1126-qos
+ - st,spear1340-misc
+ - stericsson,nomadik-pmu
+ - starfive,jh7100-sysmain
+ - ti,am62-opp-efuse-table
+ - ti,am62-usb-phy-ctrl
+ - ti,am625-dss-oldi-io-ctrl
+ - ti,am62p-cpsw-mac-efuse
+ - ti,am654-dss-oldi-io-ctrl
+ - ti,j784s4-acspcie-proxy-ctrl
+ - ti,j784s4-pcie-ctrl
+ - ti,keystone-pllctrl
+ - const: syscon
+ - items:
+ - enum:
+ - microchip,sama7g5-sfrbu
+ - microchip,sama7d65-sfrbu
+ - const: atmel,sama5d2-sfrbu
+ - const: syscon
+ - items:
+ - const: microchip,pic64gx-control-scb
+ - const: microchip,mpfs-control-scb
+ - const: syscon
+ - items:
+ - const: microchip,pic64gx-sysreg-scb
+ - const: microchip,mpfs-sysreg-scb
+ - const: syscon
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml b/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml
index a2668fc30a7b..f1a76f88fc0c 100644
--- a/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml
+++ b/Documentation/devicetree/bindings/mfd/ti,tps65910.yaml
@@ -166,9 +166,6 @@ patternProperties:
required:
- compatible
- reg
- - interrupts
- - interrupt-controller
- - '#interrupt-cells'
- gpio-controller
- '#gpio-cells'
- regulators
diff --git a/Documentation/devicetree/bindings/mfd/ti,twl.yaml b/Documentation/devicetree/bindings/mfd/ti,twl.yaml
index 776b04e182cb..9cc3e4721612 100644
--- a/Documentation/devicetree/bindings/mfd/ti,twl.yaml
+++ b/Documentation/devicetree/bindings/mfd/ti,twl.yaml
@@ -55,6 +55,15 @@ allOf:
gpadc: false
+ pwrbutton:
+ properties:
+ compatible:
+ const: ti,twl4030-pwrbutton
+ interrupts:
+ items:
+ - items:
+ const: 8
+
usb-comparator: false
- if:
@@ -95,7 +104,14 @@ allOf:
compatible:
const: ti,twl6030-gpadc
- pwrbutton: false
+ pwrbutton:
+ properties:
+ compatible:
+ const: ti,twl6030-pwrbutton
+ interrupts:
+ items:
+ - items:
+ const: 0
madc: false
@@ -146,7 +162,14 @@ allOf:
compatible:
const: ti,twl6032-gpadc
- pwrbutton: false
+ pwrbutton:
+ properties:
+ compatible:
+ const: ti,twl6030-pwrbutton
+ interrupts:
+ items:
+ - items:
+ const: 0
madc: false
@@ -226,11 +249,11 @@ properties:
properties:
compatible:
- const: ti,twl4030-pwrbutton
+ enum:
+ - ti,twl4030-pwrbutton
+ - ti,twl6030-pwrbutton
interrupts:
- items:
- - items:
- const: 8
+ maxItems: 1
watchdog:
type: object
@@ -400,7 +423,7 @@ properties:
- '#pwm-cells'
patternProperties:
- "^regulator-":
+ '^regulator-':
type: object
unevaluatedProperties: false
$ref: /schemas/regulator/regulator.yaml
@@ -429,7 +452,7 @@ required:
- reg
- interrupts
- interrupt-controller
- - "#interrupt-cells"
+ - '#interrupt-cells'
examples:
- |
@@ -459,6 +482,11 @@ examples:
#io-channel-cells = <1>;
};
+ pwrbutton {
+ compatible = "ti,twl6030-pwrbutton";
+ interrupts = <0>;
+ };
+
rtc {
compatible = "ti,twl4030-rtc";
interrupts = <8>;
diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml
index 493655a38b37..0936bfef8c75 100644
--- a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml
+++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml
@@ -21,9 +21,11 @@ properties:
- items:
- enum:
- brcm,bcm2712-sdhci
+ - brcm,bcm72116-sdhci
- brcm,bcm74165b0-sdhci
- brcm,bcm7445-sdhci
- brcm,bcm7425-sdhci
+ - brcm,bcm74371-sdhci
- const: brcm,sdhci-brcmstb
reg:
diff --git a/Documentation/devicetree/bindings/mmc/davinci_mmc.txt b/Documentation/devicetree/bindings/mmc/davinci_mmc.txt
deleted file mode 100644
index 516fb0143d4c..000000000000
--- a/Documentation/devicetree/bindings/mmc/davinci_mmc.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-* TI Highspeed MMC host controller for DaVinci
-
-The Highspeed MMC Host Controller on TI DaVinci family
-provides an interface for MMC, SD and SDIO types of memory cards.
-
-This file documents the properties used by the davinci_mmc driver.
-
-Required properties:
-- compatible:
- Should be "ti,da830-mmc": for da830, da850, dm365
- Should be "ti,dm355-mmc": for dm355, dm644x
-
-Optional properties:
-- bus-width: Number of data lines, can be <1>, <4>, or <8>, default <1>
-- max-frequency: Maximum operating clock frequency, default 25MHz.
-- dmas: List of DMA specifiers with the controller specific format
- as described in the generic DMA client binding. A tx and rx
- specifier is required.
-- dma-names: RX and TX DMA request names. These strings correspond
- 1:1 with the DMA specifiers listed in dmas.
-
-Example:
-mmc0: mmc@1c40000 {
- compatible = "ti,da830-mmc",
- reg = <0x40000 0x1000>;
- interrupts = <16>;
- bus-width = <4>;
- max-frequency = <50000000>;
- dmas = <&edma 16
- &edma 17>;
- dma-names = "rx", "tx";
-};
diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
index bf273115235b..acb9fb9a92cd 100644
--- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
+++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
@@ -38,6 +38,7 @@ properties:
- rockchip,rk3328-dw-mshc
- rockchip,rk3368-dw-mshc
- rockchip,rk3399-dw-mshc
+ - rockchip,rk3506-dw-mshc
- rockchip,rk3528-dw-mshc
- rockchip,rk3562-dw-mshc
- rockchip,rk3568-dw-mshc
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml
index 676a74695389..242a3c6b925c 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml
+++ b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml
@@ -50,8 +50,7 @@ properties:
- const: clk_ahb
- const: clk_xin
- dma-coherent:
- type: boolean
+ dma-coherent: true
# PHY output tap delays:
# Used to delay the data valid window and align it to the sampling clock.
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-milbeaut.txt b/Documentation/devicetree/bindings/mmc/sdhci-milbeaut.txt
deleted file mode 100644
index 627ee89c125b..000000000000
--- a/Documentation/devicetree/bindings/mmc/sdhci-milbeaut.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-* SOCIONEXT Milbeaut SDHCI controller
-
-This file documents differences between the core properties in mmc.txt
-and the properties used by the sdhci_milbeaut driver.
-
-Required properties:
-- compatible: "socionext,milbeaut-m10v-sdhci-3.0"
-- clocks: Must contain an entry for each entry in clock-names. It is a
- list of phandles and clock-specifier pairs.
- See ../clocks/clock-bindings.txt for details.
-- clock-names: Should contain the following two entries:
- "iface" - clock used for sdhci interface
- "core" - core clock for sdhci controller
-
-Optional properties:
-- fujitsu,cmd-dat-delay-select: boolean property indicating that this host
- requires the CMD_DAT_DELAY control to be enabled.
-
-Example:
- sdhci3: mmc@1b010000 {
- compatible = "socionext,milbeaut-m10v-sdhci-3.0";
- reg = <0x1b010000 0x10000>;
- interrupts = <0 265 0x4>;
- voltage-ranges = <3300 3300>;
- bus-width = <4>;
- clocks = <&clk 7>, <&ahb_clk>;
- clock-names = "core", "iface";
- cap-sdio-irq;
- fujitsu,cmd-dat-delay-select;
- };
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
index 594bd174ff21..938be8228d66 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml
@@ -42,6 +42,7 @@ properties:
- qcom,ipq5424-sdhci
- qcom,ipq6018-sdhci
- qcom,ipq9574-sdhci
+ - qcom,kaanapali-sdhci
- qcom,milos-sdhci
- qcom,qcm2290-sdhci
- qcom,qcs404-sdhci
@@ -70,6 +71,7 @@ properties:
- qcom,sm8450-sdhci
- qcom,sm8550-sdhci
- qcom,sm8650-sdhci
+ - qcom,sm8750-sdhci
- qcom,x1e80100-sdhci
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0
diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
index f882219a0a26..7e7c55dc2440 100644
--- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
+++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
@@ -30,6 +30,7 @@ properties:
- sophgo,sg2002-dwcmshc
- sophgo,sg2042-dwcmshc
- thead,th1520-dwcmshc
+ - eswin,eic7700-dwcmshc
reg:
maxItems: 1
@@ -52,17 +53,30 @@ properties:
maxItems: 5
reset-names:
- items:
- - const: core
- - const: bus
- - const: axi
- - const: block
- - const: timer
+ maxItems: 5
rockchip,txclk-tapnum:
description: Specify the number of delay for tx sampling.
$ref: /schemas/types.yaml#/definitions/uint8
+ eswin,hsp-sp-csr:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: Phandle to HSP(High-Speed Peripheral) device
+ - description: Offset of the stability status register for internal
+ clock.
+ - description: Offset of the stability register for host regulator
+ voltage.
+ description:
+ HSP CSR is to control and get status of different high-speed peripherals
+ (such as Ethernet, USB, SATA, etc.) via register, which can tune
+ board-level's parameters of PHY, etc.
+
+ eswin,drive-impedance-ohms:
+ description: Specifies the drive impedance in Ohm.
+ enum: [33, 40, 50, 66, 100]
+
required:
- compatible
- reg
@@ -114,6 +128,37 @@ allOf:
properties:
compatible:
contains:
+ const: eswin,eic7700-dwcmshc
+ then:
+ properties:
+ resets:
+ minItems: 4
+ maxItems: 4
+ reset-names:
+ items:
+ - const: axi
+ - const: phy
+ - const: prstn
+ - const: txrx
+ required:
+ - eswin,hsp-sp-csr
+ - eswin,drive-impedance-ohms
+ else:
+ properties:
+ resets:
+ maxItems: 5
+ reset-names:
+ items:
+ - const: core
+ - const: bus
+ - const: axi
+ - const: block
+ - const: timer
+
+ - if:
+ properties:
+ compatible:
+ contains:
const: rockchip,rk3576-dwcmshc
then:
diff --git a/Documentation/devicetree/bindings/mmc/socionext,milbeaut-m10v-sdhci-3.0.yaml b/Documentation/devicetree/bindings/mmc/socionext,milbeaut-m10v-sdhci-3.0.yaml
new file mode 100644
index 000000000000..2ba53626a959
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/socionext,milbeaut-m10v-sdhci-3.0.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/socionext,milbeaut-m10v-sdhci-3.0.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SOCIONEXT Milbeaut SDHCI controller
+
+maintainers:
+ - Taichi Sugaya <sugaya.taichi@socionext.com>
+ - Takao Orito <orito.takao@socionext.com>
+
+description:
+ The SOCIONEXT Milbeaut SDHCI controller is a specialized SD Host
+ Controller found in some of Socionext's Milbeaut image processing SoCs.
+ It features a dedicated "bridge controller." This bridge controller
+ implements special functions like reset control, clock management for
+ various SDR modes (SDR12, SDR25, SDR50) and physical pin property settings.
+
+allOf:
+ - $ref: sdhci-common.yaml#
+
+properties:
+ compatible:
+ const: socionext,milbeaut-m10v-sdhci-3.0
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: core
+ - const: iface
+
+ fujitsu,cmd-dat-delay-select:
+ description:
+ Its presence indicates that the controller requires a specific command
+ and data line delay selection mechanism for proper operation, particularly
+ when dealing with high-speed SD/eMMC modes.
+ type: boolean
+
+ voltage-ranges:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ items:
+ items:
+ - description: minimum slot voltage (mV).
+ - description: maximum slot voltage (mV).
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ mmc@1b010000 {
+ compatible = "socionext,milbeaut-m10v-sdhci-3.0";
+ reg = <0x1b010000 0x10000>;
+ interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;
+ voltage-ranges = <3300 3300>;
+ bus-width = <4>;
+ clocks = <&clk 7>, <&ahb_clk>;
+ clock-names = "core", "iface";
+ cap-sdio-irq;
+ fujitsu,cmd-dat-delay-select;
+ };
+...
diff --git a/Documentation/devicetree/bindings/mmc/ti,da830-mmc.yaml b/Documentation/devicetree/bindings/mmc/ti,da830-mmc.yaml
new file mode 100644
index 000000000000..36b33dde086b
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/ti,da830-mmc.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mmc/ti,da830-mmc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI Highspeed MMC host controller for DaVinci
+
+description:
+ The Highspeed MMC Host Controller on TI DaVinci family
+ provides an interface for MMC, SD and SDIO types of memory cards.
+
+allOf:
+ - $ref: mmc-controller.yaml
+
+maintainers:
+ - Kishon Vijay Abraham I <kishon@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - ti,da830-mmc
+ - ti,dm355-mmc
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 2
+
+ dmas:
+ maxItems: 2
+
+ dma-names:
+ items:
+ - const: rx
+ - const: tx
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ mmc@1c40000 {
+ compatible = "ti,da830-mmc";
+ reg = <0x40000 0x1000>;
+ interrupts = <16 IRQ_TYPE_LEVEL_HIGH>,
+ <17 IRQ_TYPE_LEVEL_HIGH>;
+ bus-width = <4>;
+ max-frequency = <50000000>;
+ dmas = <&edma 16>, <&edma 17>;
+ dma-names = "rx", "tx";
+ };
+...
diff --git a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
index 054b6b8bf9b9..9d061e2216cb 100644
--- a/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
+++ b/Documentation/devicetree/bindings/mtd/allwinner,sun4i-a10-nand.yaml
@@ -6,9 +6,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Allwinner A10 NAND Controller
-allOf:
- - $ref: nand-controller.yaml
-
maintainers:
- Chen-Yu Tsai <wens@csie.org>
- Maxime Ripard <mripard@kernel.org>
@@ -18,6 +15,8 @@ properties:
enum:
- allwinner,sun4i-a10-nand
- allwinner,sun8i-a23-nand-controller
+ - allwinner,sun50i-h616-nand-controller
+
reg:
maxItems: 1
@@ -25,14 +24,20 @@ properties:
maxItems: 1
clocks:
+ minItems: 2
items:
- description: Bus Clock
- description: Module Clock
+ - description: ECC Clock
+ - description: MBus Clock
clock-names:
+ minItems: 2
items:
- const: ahb
- const: mod
+ - const: ecc
+ - const: mbus
resets:
maxItems: 1
@@ -85,6 +90,36 @@ required:
unevaluatedProperties: false
+allOf:
+ - $ref: nand-controller.yaml
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - allwinner,sun4i-a10-nand
+ - allwinner,sun8i-a23-nand-controller
+ then:
+ properties:
+ clocks:
+ maxItems: 2
+ clock-names:
+ maxItems: 2
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - allwinner,sun50i-h616-nand-controller
+ then:
+ properties:
+ clocks:
+ minItems: 4
+ clock-names:
+ minItems: 4
+
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
diff --git a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml
index 284f0f882c32..fa2aa29be794 100644
--- a/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml
+++ b/Documentation/devicetree/bindings/mtd/amlogic,meson-nand.yaml
@@ -88,7 +88,6 @@ patternProperties:
amlogic,boot-pages: [nand-is-boot-medium, "amlogic,boot-page-step"]
amlogic,boot-page-step: [nand-is-boot-medium, "amlogic,boot-pages"]
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml b/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml
index 1ecea848e8b9..bc89cbf8193a 100644
--- a/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml
+++ b/Documentation/devicetree/bindings/mtd/marvell,nand-controller.yaml
@@ -145,7 +145,6 @@ allOf:
clock-names:
minItems: 1
-
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml b/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml
index 1b375dee83b0..a9ec3ca002c7 100644
--- a/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml
+++ b/Documentation/devicetree/bindings/mtd/mtd-physmap.yaml
@@ -69,6 +69,16 @@ properties:
minItems: 1
maxItems: 8
+ clocks:
+ description: |
+ Chips may need clocks to be enabled for themselves or for transparent
+ bridges.
+
+ power-domains:
+ description: |
+ Chips may need power domains to be enabled for themselves or for
+ transparent bridges.
+
bank-width:
description: Width (in bytes) of the bank. Equal to the device width times
the number of interleaved chips.
diff --git a/Documentation/devicetree/bindings/mux/mux-controller.yaml b/Documentation/devicetree/bindings/mux/mux-controller.yaml
index 571ad9e13ecf..78340bbe4df6 100644
--- a/Documentation/devicetree/bindings/mux/mux-controller.yaml
+++ b/Documentation/devicetree/bindings/mux/mux-controller.yaml
@@ -20,7 +20,6 @@ description: |
space is a simple zero-based enumeration. I.e. 0-1 for a 2-way multiplexer,
0-7 for an 8-way multiplexer, etc.
-
Mux controller nodes
--------------------
diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
index 6d22131ac2f9..fbe2ddcdd909 100644
--- a/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
+++ b/Documentation/devicetree/bindings/net/airoha,en7581-eth.yaml
@@ -17,6 +17,7 @@ properties:
compatible:
enum:
- airoha,en7581-eth
+ - airoha,an7583-eth
reg:
items:
@@ -44,6 +45,7 @@ properties:
- description: PDMA irq
resets:
+ minItems: 7
maxItems: 8
reset-names:
@@ -54,8 +56,9 @@ properties:
- const: xsi-mac
- const: hsi0-mac
- const: hsi1-mac
- - const: hsi-mac
+ - enum: [ hsi-mac, xfp-mac ]
- const: xfp-mac
+ minItems: 7
memory-region:
items:
@@ -81,6 +84,36 @@ properties:
interface to implement hardware flow offloading programming Packet
Processor Engine (PPE) flow table.
+allOf:
+ - $ref: ethernet-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - airoha,en7581-eth
+ then:
+ properties:
+ resets:
+ minItems: 8
+
+ reset-names:
+ minItems: 8
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - airoha,an7583-eth
+ then:
+ properties:
+ resets:
+ maxItems: 7
+
+ reset-names:
+ maxItems: 7
+
patternProperties:
"^ethernet@[1-4]$":
type: object
diff --git a/Documentation/devicetree/bindings/net/airoha,en7581-npu.yaml b/Documentation/devicetree/bindings/net/airoha,en7581-npu.yaml
index c7644e6586d3..59c57f58116b 100644
--- a/Documentation/devicetree/bindings/net/airoha,en7581-npu.yaml
+++ b/Documentation/devicetree/bindings/net/airoha,en7581-npu.yaml
@@ -18,6 +18,7 @@ properties:
compatible:
enum:
- airoha,en7581-npu
+ - airoha,an7583-npu
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml
index fc62fb2a68ac..323a669fa982 100644
--- a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml
+++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml
@@ -201,7 +201,6 @@ allOf:
- clocks
- resets
-
mdio@2:
$ref: mdio.yaml#
unevaluatedProperties: false
@@ -251,7 +250,6 @@ allOf:
maxItems: 1
power-domains: false
-
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/net/amd,xgbe-seattle-v1a.yaml b/Documentation/devicetree/bindings/net/amd,xgbe-seattle-v1a.yaml
new file mode 100644
index 000000000000..006add8b6410
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/amd,xgbe-seattle-v1a.yaml
@@ -0,0 +1,147 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/amd,xgbe-seattle-v1a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: AMD XGBE Seattle v1a
+
+maintainers:
+ - Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
+
+allOf:
+ - $ref: /schemas/net/ethernet-controller.yaml#
+
+properties:
+ compatible:
+ const: amd,xgbe-seattle-v1a
+
+ reg:
+ items:
+ - description: MAC registers
+ - description: PCS registers
+ - description: SerDes Rx/Tx registers
+ - description: SerDes integration registers (1/2)
+ - description: SerDes integration registers (2/2)
+
+ interrupts:
+ description: Device interrupts. The first entry is the general device
+ interrupt. If amd,per-channel-interrupt is specified, each DMA channel
+ interrupt must be specified. The last entry is the PCS auto-negotiation
+ interrupt.
+ minItems: 2
+ maxItems: 6
+
+ clocks:
+ items:
+ - description: DMA clock for the device
+ - description: PTP clock for the device
+
+ clock-names:
+ items:
+ - const: dma_clk
+ - const: ptp_clk
+
+ iommus:
+ maxItems: 1
+
+ phy-mode: true
+
+ dma-coherent: true
+
+ amd,per-channel-interrupt:
+ description: Indicates that Rx and Tx complete will generate a unique
+ interrupt for each DMA channel.
+ type: boolean
+
+ amd,speed-set:
+ description: >
+ Speed capabilities of the device.
+ 0 = 1GbE and 10GbE
+ 1 = 2.5GbE and 10GbE
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+
+ amd,serdes-blwc:
+ description: Baseline wandering correction enablement for each speed.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 3
+ maxItems: 3
+ items:
+ enum: [0, 1]
+
+ amd,serdes-cdr-rate:
+ description: CDR rate speed selection for each speed.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - description: CDR rate for 1GbE
+ - description: CDR rate for 2.5GbE
+ - description: CDR rate for 10GbE
+
+ amd,serdes-pq-skew:
+ description: PQ data sampling skew for each speed.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - description: PQ skew for 1GbE
+ - description: PQ skew for 2.5GbE
+ - description: PQ skew for 10GbE
+
+ amd,serdes-tx-amp:
+ description: TX amplitude boost for each speed.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - description: TX amplitude for 1GbE
+ - description: TX amplitude for 2.5GbE
+ - description: TX amplitude for 10GbE
+
+ amd,serdes-dfe-tap-config:
+ description: DFE taps available to run for each speed.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - description: DFE taps available for 1GbE
+ - description: DFE taps available for 2.5GbE
+ - description: DFE taps available for 10GbE
+
+ amd,serdes-dfe-tap-enable:
+ description: DFE taps to enable for each speed.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - description: DFE taps to enable for 1GbE
+ - description: DFE taps to enable for 2.5GbE
+ - description: DFE taps to enable for 10GbE
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - phy-mode
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ ethernet@e0700000 {
+ compatible = "amd,xgbe-seattle-v1a";
+ reg = <0xe0700000 0x80000>,
+ <0xe0780000 0x80000>,
+ <0xe1240800 0x00400>,
+ <0xe1250000 0x00060>,
+ <0xe1250080 0x00004>;
+ interrupts = <0 325 4>,
+ <0 326 1>, <0 327 1>, <0 328 1>, <0 329 1>,
+ <0 323 4>;
+ amd,per-channel-interrupt;
+ clocks = <&xgbe_dma_clk>, <&xgbe_ptp_clk>;
+ clock-names = "dma_clk", "ptp_clk";
+ phy-mode = "xgmii";
+ mac-address = [ 02 a1 a2 a3 a4 a5 ];
+ amd,speed-set = <0>;
+ amd,serdes-blwc = <1>, <1>, <0>;
+ amd,serdes-cdr-rate = <2>, <2>, <7>;
+ amd,serdes-pq-skew = <10>, <10>, <30>;
+ amd,serdes-tx-amp = <15>, <15>, <10>;
+ amd,serdes-dfe-tap-config = <3>, <3>, <1>;
+ amd,serdes-dfe-tap-enable = <0>, <0>, <127>;
+ };
diff --git a/Documentation/devicetree/bindings/net/amd-xgbe.txt b/Documentation/devicetree/bindings/net/amd-xgbe.txt
deleted file mode 100644
index 9c27dfcd1133..000000000000
--- a/Documentation/devicetree/bindings/net/amd-xgbe.txt
+++ /dev/null
@@ -1,76 +0,0 @@
-* AMD 10GbE driver (amd-xgbe)
-
-Required properties:
-- compatible: Should be "amd,xgbe-seattle-v1a"
-- reg: Address and length of the register sets for the device
- - MAC registers
- - PCS registers
- - SerDes Rx/Tx registers
- - SerDes integration registers (1/2)
- - SerDes integration registers (2/2)
-- interrupts: Should contain the amd-xgbe interrupt(s). The first interrupt
- listed is required and is the general device interrupt. If the optional
- amd,per-channel-interrupt property is specified, then one additional
- interrupt for each DMA channel supported by the device should be specified.
- The last interrupt listed should be the PCS auto-negotiation interrupt.
-- clocks:
- - DMA clock for the amd-xgbe device (used for calculating the
- correct Rx interrupt watchdog timer value on a DMA channel
- for coalescing)
- - PTP clock for the amd-xgbe device
-- clock-names: Should be the names of the clocks
- - "dma_clk" for the DMA clock
- - "ptp_clk" for the PTP clock
-- phy-mode: See ethernet.txt file in the same directory
-
-Optional properties:
-- dma-coherent: Present if dma operations are coherent
-- amd,per-channel-interrupt: Indicates that Rx and Tx complete will generate
- a unique interrupt for each DMA channel - this requires an additional
- interrupt be configured for each DMA channel
-- amd,speed-set: Speed capabilities of the device
- 0 - 1GbE and 10GbE (default)
- 1 - 2.5GbE and 10GbE
-
-The MAC address will be determined using the optional properties defined in
-ethernet.txt.
-
-The following optional properties are represented by an array with each
-value corresponding to a particular speed. The first array value represents
-the setting for the 1GbE speed, the second value for the 2.5GbE speed and
-the third value for the 10GbE speed. All three values are required if the
-property is used.
-- amd,serdes-blwc: Baseline wandering correction enablement
- 0 - Off
- 1 - On
-- amd,serdes-cdr-rate: CDR rate speed selection
-- amd,serdes-pq-skew: PQ (data sampling) skew
-- amd,serdes-tx-amp: TX amplitude boost
-- amd,serdes-dfe-tap-config: DFE taps available to run
-- amd,serdes-dfe-tap-enable: DFE taps to enable
-
-Example:
- xgbe@e0700000 {
- compatible = "amd,xgbe-seattle-v1a";
- reg = <0 0xe0700000 0 0x80000>,
- <0 0xe0780000 0 0x80000>,
- <0 0xe1240800 0 0x00400>,
- <0 0xe1250000 0 0x00060>,
- <0 0xe1250080 0 0x00004>;
- interrupt-parent = <&gic>;
- interrupts = <0 325 4>,
- <0 326 1>, <0 327 1>, <0 328 1>, <0 329 1>,
- <0 323 4>;
- amd,per-channel-interrupt;
- clocks = <&xgbe_dma_clk>, <&xgbe_ptp_clk>;
- clock-names = "dma_clk", "ptp_clk";
- phy-mode = "xgmii";
- mac-address = [ 02 a1 a2 a3 a4 a5 ];
- amd,speed-set = <0>;
- amd,serdes-blwc = <1>, <1>, <0>;
- amd,serdes-cdr-rate = <2>, <2>, <7>;
- amd,serdes-pq-skew = <10>, <10>, <30>;
- amd,serdes-tx-amp = <15>, <15>, <10>;
- amd,serdes-dfe-tap-config = <3>, <3>, <1>;
- amd,serdes-dfe-tap-enable = <0>, <0>, <127>;
- };
diff --git a/Documentation/devicetree/bindings/net/aspeed,ast2600-mdio.yaml b/Documentation/devicetree/bindings/net/aspeed,ast2600-mdio.yaml
index d6ef468495c5..a105dc07ed12 100644
--- a/Documentation/devicetree/bindings/net/aspeed,ast2600-mdio.yaml
+++ b/Documentation/devicetree/bindings/net/aspeed,ast2600-mdio.yaml
@@ -19,7 +19,12 @@ allOf:
properties:
compatible:
- const: aspeed,ast2600-mdio
+ oneOf:
+ - const: aspeed,ast2600-mdio
+ - items:
+ - enum:
+ - aspeed,ast2700-mdio
+ - const: aspeed,ast2600-mdio
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/net/bluetooth/marvell,sd8897-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/marvell,sd8897-bt.yaml
new file mode 100644
index 000000000000..a307c64cfa4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/marvell,sd8897-bt.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/bluetooth/marvell,sd8897-bt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell 8897/8997 (sd8897/sd8997) bluetooth devices (SDIO)
+
+maintainers:
+ - Ariel D'Alessandro <ariel.dalessandro@collabora.com>
+
+allOf:
+ - $ref: /schemas/net/bluetooth/bluetooth-controller.yaml#
+
+properties:
+ compatible:
+ enum:
+ - marvell,sd8897-bt
+ - marvell,sd8997-bt
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ marvell,cal-data:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ description:
+ Calibration data downloaded to the device during initialization.
+ maxItems: 28
+
+ marvell,wakeup-pin:
+ $ref: /schemas/types.yaml#/definitions/uint16
+ description:
+ Wakeup pin number of the bluetooth chip. Used by firmware to wakeup host
+ system.
+
+ marvell,wakeup-gap-ms:
+ $ref: /schemas/types.yaml#/definitions/uint16
+ description:
+ Wakeup latency of the host platform. Required by the chip sleep feature.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ mmc {
+ vmmc-supply = <&wlan_en_reg>;
+ bus-width = <4>;
+ cap-power-off-card;
+ keep-power-in-suspend;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bluetooth@2 {
+ compatible = "marvell,sd8897-bt";
+ reg = <2>;
+ interrupt-parent = <&pio>;
+ interrupts = <119 IRQ_TYPE_LEVEL_LOW>;
+
+ marvell,cal-data = /bits/ 8 <
+ 0x37 0x01 0x1c 0x00 0xff 0xff 0xff 0xff 0x01 0x7f 0x04 0x02
+ 0x00 0x00 0xba 0xce 0xc0 0xc6 0x2d 0x00 0x00 0x00 0x00 0x00
+ 0x00 0x00 0xf0 0x00>;
+ marvell,wakeup-pin = /bits/ 16 <0x0d>;
+ marvell,wakeup-gap-ms = /bits/ 16 <0x64>;
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/net/brcm,bcmgenet.yaml b/Documentation/devicetree/bindings/net/brcm,bcmgenet.yaml
index 0e3fb4e42e3f..a1119c47e29b 100644
--- a/Documentation/devicetree/bindings/net/brcm,bcmgenet.yaml
+++ b/Documentation/devicetree/bindings/net/brcm,bcmgenet.yaml
@@ -31,7 +31,6 @@ properties:
- description: RX and TX rings interrupt line
- description: Wake-on-LAN interrupt line
-
clocks:
minItems: 1
items:
diff --git a/Documentation/devicetree/bindings/net/brcm,mdio-mux-iproc.yaml b/Documentation/devicetree/bindings/net/brcm,mdio-mux-iproc.yaml
index 3f27746d9a56..d544f785e6b9 100644
--- a/Documentation/devicetree/bindings/net/brcm,mdio-mux-iproc.yaml
+++ b/Documentation/devicetree/bindings/net/brcm,mdio-mux-iproc.yaml
@@ -29,7 +29,6 @@ properties:
maxItems: 1
description: core clock driving the MDIO block
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/net/btusb.txt b/Documentation/devicetree/bindings/net/btusb.txt
index f546b1f7dd6d..a68022a57c51 100644
--- a/Documentation/devicetree/bindings/net/btusb.txt
+++ b/Documentation/devicetree/bindings/net/btusb.txt
@@ -14,7 +14,7 @@ Required properties:
Also, vendors that use btusb may have device additional properties, e.g:
-Documentation/devicetree/bindings/net/marvell-bt-8xxx.txt
+Documentation/devicetree/bindings/net/bluetooth/marvell,sd8897-bt.yaml
Optional properties:
diff --git a/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml b/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml
index 61ef60d8f1c7..2c9d37975bed 100644
--- a/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml
+++ b/Documentation/devicetree/bindings/net/can/bosch,m_can.yaml
@@ -109,6 +109,26 @@ properties:
maximum: 32
minItems: 1
+ pinctrl-0:
+ description: Default pinctrl state
+
+ pinctrl-1:
+ description: Can be "sleep" or "wakeup" pinctrl state
+
+ pinctrl-2:
+ description: Can be "sleep" or "wakeup" pinctrl state
+
+ pinctrl-names:
+ description:
+ When present should contain at least "default" describing the default pin
+ states. Other states are "sleep" which describes the pinstate when
+ sleeping and "wakeup" describing the pins if wakeup is enabled.
+ minItems: 1
+ items:
+ - const: default
+ - enum: [ sleep, wakeup ]
+ - const: wakeup
+
power-domains:
description:
Power domain provider node and an args specifier containing
@@ -125,6 +145,11 @@ properties:
minItems: 1
maxItems: 2
+ wakeup-source:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ List of phandles to system idle states in which mcan can wakeup the system.
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml
index c155c9c6db39..2d13638ebc6a 100644
--- a/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml
+++ b/Documentation/devicetree/bindings/net/can/microchip,mcp251xfd.yaml
@@ -49,6 +49,11 @@ properties:
Must be half or less of "clocks" frequency.
maximum: 20000000
+ gpio-controller: true
+
+ "#gpio-cells":
+ const: 2
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml b/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml
index 1219c5cb601f..519a11fbe972 100644
--- a/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml
+++ b/Documentation/devicetree/bindings/net/can/microchip,mpfs-can.yaml
@@ -32,11 +32,15 @@ properties:
- description: AHB peripheral clock
- description: CAN bus clock
+ resets:
+ maxItems: 1
+
required:
- compatible
- reg
- interrupts
- clocks
+ - resets
additionalProperties: false
@@ -46,6 +50,7 @@ examples:
compatible = "microchip,mpfs-can";
reg = <0x2010c000 0x1000>;
clocks = <&clkcfg 17>, <&clkcfg 37>;
+ resets = <&clkcfg 17>;
interrupt-parent = <&plic>;
interrupts = <56>;
};
diff --git a/Documentation/devicetree/bindings/net/cdns,macb.yaml b/Documentation/devicetree/bindings/net/cdns,macb.yaml
index 1029786a855c..cb14c35ba996 100644
--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml
+++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml
@@ -38,7 +38,10 @@ properties:
- cdns,sam9x60-macb # Microchip sam9x60 SoC
- microchip,mpfs-macb # Microchip PolarFire SoC
- const: cdns,macb # Generic
-
+ - items:
+ - const: microchip,pic64gx-macb # Microchip PIC64GX SoC
+ - const: microchip,mpfs-macb # Microchip PolarFire SoC
+ - const: cdns,macb # Generic
- items:
- enum:
- atmel,sama5d3-macb # 10/100Mbit IP on Atmel sama5d3 SoCs
@@ -47,18 +50,19 @@ properties:
- const: cdns,macb # Generic
- enum:
- - atmel,sama5d29-gem # GEM XL IP (10/100) on Atmel sama5d29 SoCs
- atmel,sama5d2-gem # GEM IP (10/100) on Atmel sama5d2 SoCs
+ - atmel,sama5d29-gem # GEM XL IP (10/100) on Atmel sama5d29 SoCs
- atmel,sama5d3-gem # Gigabit IP on Atmel sama5d3 SoCs
- atmel,sama5d4-gem # GEM IP (10/100) on Atmel sama5d4 SoCs
+ - cdns,emac # Generic
+ - cdns,gem # Generic
+ - cdns,macb # Generic
- cdns,np4-macb # NP4 SoC devices
- microchip,sama7g5-emac # Microchip SAMA7G5 ethernet interface
- microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface
+ - mobileye,eyeq5-gem # Mobileye EyeQ5 SoCs
- raspberrypi,rp1-gem # Raspberry Pi RP1 gigabit ethernet interface
- sifive,fu540-c000-gem # SiFive FU540-C000 SoC
- - cdns,emac # Generic
- - cdns,gem # Generic
- - cdns,macb # Generic
- items:
- enum:
@@ -183,6 +187,15 @@ allOf:
reg:
maxItems: 1
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: mobileye,eyeq5-gem
+ then:
+ required:
+ - phys
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.yaml b/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.yaml
index 44fd23a5fa2b..a930358f6a66 100644
--- a/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.yaml
+++ b/Documentation/devicetree/bindings/net/cortina,gemini-ethernet.yaml
@@ -100,7 +100,6 @@ examples:
};
};
-
ethernet@60000000 {
compatible = "cortina,gemini-ethernet";
reg = <0x60000000 0x4000>, /* Global registers, queue */
diff --git a/Documentation/devicetree/bindings/net/dsa/lantiq,gswip.yaml b/Documentation/devicetree/bindings/net/dsa/lantiq,gswip.yaml
index f3154b19af78..205b683849a5 100644
--- a/Documentation/devicetree/bindings/net/dsa/lantiq,gswip.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/lantiq,gswip.yaml
@@ -4,10 +4,14 @@
$id: http://devicetree.org/schemas/net/dsa/lantiq,gswip.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Lantiq GSWIP Ethernet switches
+title: Lantiq GSWIP and MaxLinear GSW1xx Ethernet switches
-allOf:
- - $ref: dsa.yaml#/$defs/ethernet-ports
+description:
+ Lantiq GSWIP and MaxLinear GSW1xx switches share the same hardware IP.
+ Lantiq switches are embedded in SoCs and accessed via memory-mapped I/O,
+ while MaxLinear switches are standalone ICs connected via MDIO.
+
+$ref: dsa.yaml#
maintainers:
- Hauke Mehrtens <hauke@hauke-m.de>
@@ -18,9 +22,14 @@ properties:
- lantiq,xrx200-gswip
- lantiq,xrx300-gswip
- lantiq,xrx330-gswip
+ - maxlinear,gsw120
+ - maxlinear,gsw125
+ - maxlinear,gsw140
+ - maxlinear,gsw141
+ - maxlinear,gsw145
reg:
- minItems: 3
+ minItems: 1
maxItems: 3
reg-names:
@@ -37,9 +46,6 @@ properties:
compatible:
const: lantiq,xrx200-mdio
- required:
- - compatible
-
gphy-fw:
type: object
properties:
@@ -91,10 +97,63 @@ properties:
additionalProperties: false
+patternProperties:
+ "^(ethernet-)?ports$":
+ type: object
+ patternProperties:
+ "^(ethernet-)?port@[0-6]$":
+ $ref: dsa-port.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ maxlinear,rmii-refclk-out:
+ type: boolean
+ description:
+ Configure the RMII reference clock to be a clock output
+ rather than an input. Only applicable for RMII mode.
+ tx-internal-delay-ps:
+ enum: [0, 500, 1000, 1500, 2000, 2500, 3000, 3500]
+ description:
+ RGMII TX Clock Delay defined in pico seconds.
+ The delay lines adjust the MII clock vs. data timing.
+ If this property is not present the delay is determined by
+ the interface mode.
+ rx-internal-delay-ps:
+ enum: [0, 500, 1000, 1500, 2000, 2500, 3000, 3500]
+ description:
+ RGMII RX Clock Delay defined in pico seconds.
+ The delay lines adjust the MII clock vs. data timing.
+ If this property is not present the delay is determined by
+ the interface mode.
+
required:
- compatible
- reg
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - lantiq,xrx200-gswip
+ - lantiq,xrx300-gswip
+ - lantiq,xrx330-gswip
+ then:
+ properties:
+ reg:
+ minItems: 3
+ maxItems: 3
+ mdio:
+ required:
+ - compatible
+ else:
+ properties:
+ reg:
+ maxItems: 1
+ reg-names: false
+ gphy-fw: false
+
unevaluatedProperties: false
examples:
@@ -113,8 +172,10 @@ examples:
port@0 {
reg = <0>;
label = "lan3";
- phy-mode = "rgmii";
+ phy-mode = "rgmii-id";
phy-handle = <&phy0>;
+ tx-internal-delay-ps = <2000>;
+ rx-internal-delay-ps = <2000>;
};
port@1 {
@@ -200,3 +261,90 @@ examples:
};
};
};
+
+ - |
+ #include <dt-bindings/leds/common.h>
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ switch@1f {
+ compatible = "maxlinear,gsw125";
+ reg = <0x1f>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ label = "lan0";
+ phy-handle = <&switchphy0>;
+ phy-mode = "internal";
+ };
+
+ port@1 {
+ reg = <1>;
+ label = "lan1";
+ phy-handle = <&switchphy1>;
+ phy-mode = "internal";
+ };
+
+ port@4 {
+ reg = <4>;
+ label = "wan";
+ phy-mode = "1000base-x";
+ managed = "in-band-status";
+ };
+
+ port@5 {
+ reg = <5>;
+ phy-mode = "rgmii-id";
+ tx-internal-delay-ps = <2000>;
+ rx-internal-delay-ps = <2000>;
+ ethernet = <&eth0>;
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+ };
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ switchphy0: switchphy@0 {
+ reg = <0>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@0 {
+ reg = <0>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ };
+ };
+ };
+
+ switchphy1: switchphy@1 {
+ reg = <1>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@0 {
+ reg = <0>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ };
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
new file mode 100644
index 000000000000..33a6552e46fc
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
@@ -0,0 +1,167 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/motorcomm,yt921x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Motorcomm YT921x Ethernet switch family
+
+maintainers:
+ - David Yang <mmyangfl@gmail.com>
+
+description: |
+ The Motorcomm YT921x series is a family of Ethernet switches with up to 8
+ internal GbE PHYs and up to 2 GMACs, including:
+
+ - YT9215S / YT9215RB / YT9215SC: 5 GbE PHYs (Port 0-4) + 2 GMACs (Port 8-9)
+ - YT9213NB: 2 GbE PHYs (Port 1/3) + 1 GMAC (Port 9)
+ - YT9214NB: 2 GbE PHYs (Port 1/3) + 2 GMACs (Port 8-9)
+ - YT9218N: 8 GbE PHYs (Port 0-7)
+ - YT9218MB: 8 GbE PHYs (Port 0-7) + 2 GMACs (Port 8-9)
+
+ Any port can be used as the CPU port.
+
+properties:
+ compatible:
+ const: motorcomm,yt9215
+
+ reg:
+ enum: [0x0, 0x1d]
+
+ reset-gpios:
+ maxItems: 1
+
+ mdio:
+ $ref: /schemas/net/mdio.yaml#
+ unevaluatedProperties: false
+ description:
+ Internal MDIO bus for the internal GbE PHYs. PHY 0-7 are used for Port
+ 0-7 respectively.
+
+ mdio-external:
+ $ref: /schemas/net/mdio.yaml#
+ unevaluatedProperties: false
+ description:
+ External MDIO bus to access external components. External PHYs for GMACs
+ (Port 8-9) are expected to be connected to the external MDIO bus in
+ vendor's reference design, but that is not a hard limitation from the
+ chip.
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: dsa.yaml#/$defs/ethernet-ports
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ switch@1d {
+ compatible = "motorcomm,yt9215";
+ /* default 0x1d, alternate 0x0 */
+ reg = <0x1d>;
+ reset-gpios = <&tlmm 39 GPIO_ACTIVE_LOW>;
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sw_phy0: phy@0 {
+ reg = <0x0>;
+ };
+
+ sw_phy1: phy@1 {
+ reg = <0x1>;
+ };
+
+ sw_phy2: phy@2 {
+ reg = <0x2>;
+ };
+
+ sw_phy3: phy@3 {
+ reg = <0x3>;
+ };
+
+ sw_phy4: phy@4 {
+ reg = <0x4>;
+ };
+ };
+
+ mdio-external {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ phy1: phy@b {
+ reg = <0xb>;
+ };
+ };
+
+ ethernet-ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-port@0 {
+ reg = <0>;
+ label = "lan1";
+ phy-mode = "internal";
+ phy-handle = <&sw_phy0>;
+ };
+
+ ethernet-port@1 {
+ reg = <1>;
+ label = "lan2";
+ phy-mode = "internal";
+ phy-handle = <&sw_phy1>;
+ };
+
+ ethernet-port@2 {
+ reg = <2>;
+ label = "lan3";
+ phy-mode = "internal";
+ phy-handle = <&sw_phy2>;
+ };
+
+ ethernet-port@3 {
+ reg = <3>;
+ label = "lan4";
+ phy-mode = "internal";
+ phy-handle = <&sw_phy3>;
+ };
+
+ ethernet-port@4 {
+ reg = <4>;
+ label = "lan5";
+ phy-mode = "internal";
+ phy-handle = <&sw_phy4>;
+ };
+
+ /* CPU port */
+ ethernet-port@8 {
+ reg = <8>;
+ phy-mode = "2500base-x";
+ ethernet = <&eth0>;
+
+ fixed-link {
+ speed = <2500>;
+ full-duplex;
+ };
+ };
+
+ /* if external phy is connected to a MAC */
+ ethernet-port@9 {
+ reg = <9>;
+ label = "wan";
+ phy-mode = "rgmii-id";
+ phy-handle = <&phy1>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/dsa/nxp,sja1105.yaml b/Documentation/devicetree/bindings/net/dsa/nxp,sja1105.yaml
index e9dd914b0734..607b7fe8d28e 100644
--- a/Documentation/devicetree/bindings/net/dsa/nxp,sja1105.yaml
+++ b/Documentation/devicetree/bindings/net/dsa/nxp,sja1105.yaml
@@ -41,6 +41,9 @@ properties:
therefore discouraged.
maxItems: 1
+ clocks:
+ maxItems: 1
+
spi-cpha: true
spi-cpol: true
diff --git a/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml
new file mode 100644
index 000000000000..91e8cd1db67b
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/eswin,eic7700-eth.yaml
@@ -0,0 +1,129 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/eswin,eic7700-eth.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Eswin EIC7700 SOC Eth Controller
+
+maintainers:
+ - Shuang Liang <liangshuang@eswincomputing.com>
+ - Zhi Li <lizhi2@eswincomputing.com>
+ - Shangjuan Wei <weishangjuan@eswincomputing.com>
+
+description:
+ Platform glue layer implementation for STMMAC Ethernet driver.
+
+select:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - eswin,eic7700-qos-eth
+ required:
+ - compatible
+
+allOf:
+ - $ref: snps,dwmac.yaml#
+
+properties:
+ compatible:
+ items:
+ - const: eswin,eic7700-qos-eth
+ - const: snps,dwmac-5.20
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ const: macirq
+
+ clocks:
+ items:
+ - description: AXI clock
+ - description: Configuration clock
+ - description: GMAC main clock
+ - description: Tx clock
+
+ clock-names:
+ items:
+ - const: axi
+ - const: cfg
+ - const: stmmaceth
+ - const: tx
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ items:
+ - const: stmmaceth
+
+ rx-internal-delay-ps:
+ enum: [0, 200, 600, 1200, 1600, 1800, 2000, 2200, 2400]
+
+ tx-internal-delay-ps:
+ enum: [0, 200, 600, 1200, 1600, 1800, 2000, 2200, 2400]
+
+ eswin,hsp-sp-csr:
+ description:
+ HSP CSR is to control and get status of different high-speed peripherals
+ (such as Ethernet, USB, SATA, etc.) via register, which can tune
+ board-level's parameters of PHY, etc.
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: Phandle to HSP(High-Speed Peripheral) device
+ - description: Offset of phy control register for internal
+ or external clock selection
+ - description: Offset of AXI clock controller Low-Power request
+ register
+ - description: Offset of register controlling TX/RX clock delay
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+ - interrupt-names
+ - phy-mode
+ - resets
+ - reset-names
+ - rx-internal-delay-ps
+ - tx-internal-delay-ps
+ - eswin,hsp-sp-csr
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ ethernet@50400000 {
+ compatible = "eswin,eic7700-qos-eth", "snps,dwmac-5.20";
+ reg = <0x50400000 0x10000>;
+ clocks = <&d0_clock 186>, <&d0_clock 171>, <&d0_clock 40>,
+ <&d0_clock 193>;
+ clock-names = "axi", "cfg", "stmmaceth", "tx";
+ interrupt-parent = <&plic>;
+ interrupts = <61>;
+ interrupt-names = "macirq";
+ phy-mode = "rgmii-id";
+ phy-handle = <&phy0>;
+ resets = <&reset 95>;
+ reset-names = "stmmaceth";
+ rx-internal-delay-ps = <200>;
+ tx-internal-delay-ps = <200>;
+ eswin,hsp-sp-csr = <&hsp_sp_csr 0x100 0x108 0x118>;
+ snps,axi-config = <&stmmac_axi_setup>;
+ snps,aal;
+ snps,fixed-burst;
+ snps,tso;
+ stmmac_axi_setup: stmmac-axi-config {
+ snps,blen = <0 0 0 0 16 8 4>;
+ snps,rd_osr_lmt = <2>;
+ snps,wr_osr_lmt = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml
index 2ec2d9fda7e3..bb4c49fc5fd8 100644
--- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml
@@ -35,9 +35,13 @@ properties:
description: PHYs that implement IEEE802.3 clause 45
- pattern: "^ethernet-phy-id[a-f0-9]{4}\\.[a-f0-9]{4}$"
description:
- If the PHY reports an incorrect ID (or none at all) then the
- compatible list may contain an entry with the correct PHY ID
- in the above form.
+ PHYs contain identification registers. These will be read to
+ identify the PHY. If the PHY reports an incorrect ID, or the
+ PHY requires a specific initialization sequence (like a
+ particular order of clocks, resets, power supplies), in
+ order to be able to read the ID registers, then the
+ compatible list must contain an entry with the correct PHY
+ ID in the above form.
The first group of digits is the 16 bit Phy Identifier 1
register, this is the chip vendor OUI bits 3:18. The
second group of digits is the Phy Identifier 2 register,
diff --git a/Documentation/devicetree/bindings/net/ethernet-switch.yaml b/Documentation/devicetree/bindings/net/ethernet-switch.yaml
index b3b7e1a1b127..6bb68f7dbc7f 100644
--- a/Documentation/devicetree/bindings/net/ethernet-switch.yaml
+++ b/Documentation/devicetree/bindings/net/ethernet-switch.yaml
@@ -35,14 +35,14 @@ allOf:
then:
properties:
$nodename:
- pattern: "switch[0-3]@[0-3]+$"
+ pattern: 'switch[0-3]@[0-3]+$'
else:
properties:
$nodename:
- pattern: "^(ethernet-)?switch(@.*)?$"
+ pattern: '^(ethernet-)?switch(@.*)?$'
patternProperties:
- "^(ethernet-)?ports$":
+ '^(ethernet-)?ports$':
type: object
unevaluatedProperties: false
@@ -53,13 +53,13 @@ patternProperties:
const: 0
patternProperties:
- "^(ethernet-)?port@[0-9a-f]+$":
+ '^(ethernet-)?port@[0-9a-f]+$':
type: object
description: Ethernet switch ports
required:
- - "#address-cells"
- - "#size-cells"
+ - '#address-cells'
+ - '#size-cells'
oneOf:
- required:
@@ -75,9 +75,9 @@ $defs:
$ref: '#'
patternProperties:
- "^(ethernet-)?ports$":
+ '^(ethernet-)?ports$':
patternProperties:
- "^(ethernet-)?port@[0-9a-f]+$":
+ '^(ethernet-)?port@[0-9a-f]+$':
description: Ethernet switch ports
$ref: ethernet-switch-port.yaml#
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/net/fsl,enetc.yaml b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
index ca70f0050171..aac20ab72ace 100644
--- a/Documentation/devicetree/bindings/net/fsl,enetc.yaml
+++ b/Documentation/devicetree/bindings/net/fsl,enetc.yaml
@@ -27,6 +27,7 @@ properties:
- const: fsl,enetc
- enum:
- pci1131,e101
+ - pci1131,e110
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/net/fsl,gianfar.yaml b/Documentation/devicetree/bindings/net/fsl,gianfar.yaml
index f92f284aa05b..0d8909770ccb 100644
--- a/Documentation/devicetree/bindings/net/fsl,gianfar.yaml
+++ b/Documentation/devicetree/bindings/net/fsl,gianfar.yaml
@@ -167,8 +167,6 @@ allOf:
- description: Receive interrupt
- description: Error interrupt
-
-
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/net/marvell-bt-8xxx.txt b/Documentation/devicetree/bindings/net/marvell-bt-8xxx.txt
deleted file mode 100644
index 957e5e5c2927..000000000000
--- a/Documentation/devicetree/bindings/net/marvell-bt-8xxx.txt
+++ /dev/null
@@ -1,83 +0,0 @@
-Marvell 8897/8997 (sd8897/sd8997) bluetooth devices (SDIO or USB based)
-------
-The 8997 devices supports multiple interfaces. When used on SDIO interfaces,
-the btmrvl driver is used and when used on USB interface, the btusb driver is
-used.
-
-Required properties:
-
- - compatible : should be one of the following:
- * "marvell,sd8897-bt" (for SDIO)
- * "marvell,sd8997-bt" (for SDIO)
- * "usb1286,204e" (for USB)
-
-Optional properties:
-
- - marvell,cal-data: Calibration data downloaded to the device during
- initialization. This is an array of 28 values(u8).
- This is only applicable to SDIO devices.
-
- - marvell,wakeup-pin: It represents wakeup pin number of the bluetooth chip.
- firmware will use the pin to wakeup host system (u16).
- - marvell,wakeup-gap-ms: wakeup gap represents wakeup latency of the host
- platform. The value will be configured to firmware. This
- is needed to work chip's sleep feature as expected (u16).
- - interrupt-names: Used only for USB based devices (See below)
- - interrupts : specifies the interrupt pin number to the cpu. For SDIO, the
- driver will use the first interrupt specified in the interrupt
- array. For USB based devices, the driver will use the interrupt
- named "wakeup" from the interrupt-names and interrupt arrays.
- The driver will request an irq based on this interrupt number.
- During system suspend, the irq will be enabled so that the
- bluetooth chip can wakeup host platform under certain
- conditions. During system resume, the irq will be disabled
- to make sure unnecessary interrupt is not received.
-
-Example:
-
-IRQ pin 119 is used as system wakeup source interrupt.
-wakeup pin 13 and gap 100ms are configured so that firmware can wakeup host
-using this device side pin and wakeup latency.
-
-Example for SDIO device follows (calibration data is also available in
-below example).
-
-&mmc3 {
- vmmc-supply = <&wlan_en_reg>;
- bus-width = <4>;
- cap-power-off-card;
- keep-power-in-suspend;
-
- #address-cells = <1>;
- #size-cells = <0>;
- btmrvl: bluetooth@2 {
- compatible = "marvell,sd8897-bt";
- reg = <2>;
- interrupt-parent = <&pio>;
- interrupts = <119 IRQ_TYPE_LEVEL_LOW>;
-
- marvell,cal-data = /bits/ 8 <
- 0x37 0x01 0x1c 0x00 0xff 0xff 0xff 0xff 0x01 0x7f 0x04 0x02
- 0x00 0x00 0xba 0xce 0xc0 0xc6 0x2d 0x00 0x00 0x00 0x00 0x00
- 0x00 0x00 0xf0 0x00>;
- marvell,wakeup-pin = /bits/ 16 <0x0d>;
- marvell,wakeup-gap-ms = /bits/ 16 <0x64>;
- };
-};
-
-Example for USB device:
-
-&usb_host1_ohci {
- #address-cells = <1>;
- #size-cells = <0>;
-
- mvl_bt1: bt@1 {
- compatible = "usb1286,204e";
- reg = <1>;
- interrupt-parent = <&gpio0>;
- interrupt-names = "wakeup";
- interrupts = <119 IRQ_TYPE_LEVEL_LOW>;
- marvell,wakeup-pin = /bits/ 16 <0x0d>;
- marvell,wakeup-gap-ms = /bits/ 16 <0x64>;
- };
-};
diff --git a/Documentation/devicetree/bindings/net/mdio-mux-multiplexer.yaml b/Documentation/devicetree/bindings/net/mdio-mux-multiplexer.yaml
index 282987074ee4..23947ba6aeaf 100644
--- a/Documentation/devicetree/bindings/net/mdio-mux-multiplexer.yaml
+++ b/Documentation/devicetree/bindings/net/mdio-mux-multiplexer.yaml
@@ -14,7 +14,6 @@ description: |+
of a mux producer device. The mux producer can be of any type like mmio mux
producer, gpio mux producer or generic register based mux producer.
-
allOf:
- $ref: /schemas/net/mdio-mux.yaml#
diff --git a/Documentation/devicetree/bindings/net/mediatek,net.yaml b/Documentation/devicetree/bindings/net/mediatek,net.yaml
index b45f67f92e80..cc346946291a 100644
--- a/Documentation/devicetree/bindings/net/mediatek,net.yaml
+++ b/Documentation/devicetree/bindings/net/mediatek,net.yaml
@@ -112,7 +112,7 @@ properties:
mediatek,wed:
$ref: /schemas/types.yaml#/definitions/phandle-array
- minItems: 2
+ minItems: 1
maxItems: 2
items:
maxItems: 1
@@ -249,6 +249,9 @@ allOf:
minItems: 1
maxItems: 1
+ mediatek,wed:
+ minItems: 2
+
mediatek,wed-pcie: false
else:
properties:
@@ -338,12 +341,13 @@ allOf:
- const: netsys0
- const: netsys1
- mediatek,infracfg: false
-
mediatek,sgmiisys:
minItems: 2
maxItems: 2
+ mediatek,wed:
+ maxItems: 1
+
- if:
properties:
compatible:
@@ -385,6 +389,9 @@ allOf:
minItems: 2
maxItems: 2
+ mediatek,wed:
+ minItems: 2
+
- if:
properties:
compatible:
@@ -429,6 +436,19 @@ allOf:
- const: xgp2
- const: xgp3
+ mediatek,wed:
+ minItems: 2
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ralink,rt5350-eth
+ then:
+ properties:
+ mediatek,wed:
+ minItems: 2
+
patternProperties:
"^mac@[0-2]$":
type: object
diff --git a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
index 5caa3779660d..5491d0775ede 100644
--- a/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
+++ b/Documentation/devicetree/bindings/net/microchip,sparx5-switch.yaml
@@ -180,9 +180,9 @@ allOf:
then:
properties:
reg:
- minItems: 2
+ maxItems: 2
reg-names:
- minItems: 2
+ maxItems: 2
else:
properties:
reg:
diff --git a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
deleted file mode 100644
index 0a3647fe331b..000000000000
--- a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-* Microsemi - vsc8531 Giga bit ethernet phy
-
-Optional properties:
-- vsc8531,vddmac : The vddmac in mV. Allowed values is listed
- in the first row of Table 1 (below).
- This property is only used in combination
- with the 'edge-slowdown' property.
- Default value is 3300.
-- vsc8531,edge-slowdown : % the edge should be slowed down relative to
- the fastest possible edge time.
- Edge rate sets the drive strength of the MAC
- interface output signals. Changing the
- drive strength will affect the edge rate of
- the output signal. The goal of this setting
- is to help reduce electrical emission (EMI)
- by being able to reprogram drive strength
- and in effect slow down the edge rate if
- desired.
- To adjust the edge-slowdown, the 'vddmac'
- must be specified. Table 1 lists the
- supported edge-slowdown values for a given
- 'vddmac'.
- Default value is 0%.
- Ref: Table:1 - Edge rate change (below).
-- vsc8531,led-[N]-mode : LED mode. Specify how the LED[N] should behave.
- N depends on the number of LEDs supported by a
- PHY.
- Allowed values are defined in
- "include/dt-bindings/net/mscc-phy-vsc8531.h".
- Default values are VSC8531_LINK_1000_ACTIVITY (1),
- VSC8531_LINK_100_ACTIVITY (2),
- VSC8531_LINK_ACTIVITY (0) and
- VSC8531_DUPLEX_COLLISION (8).
-- load-save-gpios : GPIO used for the load/save operation of the PTP
- hardware clock (PHC).
-
-
-Table: 1 - Edge rate change
-----------------------------------------------------------------|
-| Edge Rate Change (VDDMAC) |
-| |
-| 3300 mV 2500 mV 1800 mV 1500 mV |
-|---------------------------------------------------------------|
-| 0% 0% 0% 0% |
-| (Fastest) (recommended) (recommended) |
-|---------------------------------------------------------------|
-| 2% 3% 5% 6% |
-|---------------------------------------------------------------|
-| 4% 6% 9% 14% |
-|---------------------------------------------------------------|
-| 7% 10% 16% 21% |
-|(recommended) (recommended) |
-|---------------------------------------------------------------|
-| 10% 14% 23% 29% |
-|---------------------------------------------------------------|
-| 17% 23% 35% 42% |
-|---------------------------------------------------------------|
-| 29% 37% 52% 58% |
-|---------------------------------------------------------------|
-| 53% 63% 76% 77% |
-| (slowest) |
-|---------------------------------------------------------------|
-
-Example:
-
- vsc8531_0: ethernet-phy@0 {
- compatible = "ethernet-phy-id0007.0570";
- vsc8531,vddmac = <3300>;
- vsc8531,edge-slowdown = <7>;
- vsc8531,led-0-mode = <VSC8531_LINK_1000_ACTIVITY>;
- vsc8531,led-1-mode = <VSC8531_LINK_100_ACTIVITY>;
- load-save-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>;
- };
diff --git a/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.yaml b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.yaml
new file mode 100644
index 000000000000..0afbd0ff126f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/mscc-phy-vsc8531.yaml
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/mscc-phy-vsc8531.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microsemi VSC8531 Gigabit Ethernet PHY
+
+maintainers:
+ - Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+
+description:
+ The VSC8531 is a Gigabit Ethernet PHY with configurable MAC interface
+ drive strength and LED modes.
+
+allOf:
+ - $ref: ethernet-phy.yaml#
+
+select:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - ethernet-phy-id0007.0570 # VSC8531
+ - ethernet-phy-id0007.0772 # VSC8541
+ required:
+ - compatible
+
+properties:
+ compatible:
+ items:
+ - enum:
+ - ethernet-phy-id0007.0570 # VSC8531
+ - ethernet-phy-id0007.0772 # VSC8541
+ - const: ethernet-phy-ieee802.3-c22
+
+ vsc8531,vddmac:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ The VDDMAC voltage in millivolts. This property is used in combination
+ with the edge-slowdown property to control the drive strength of the
+ MAC interface output signals.
+ enum: [3300, 2500, 1800, 1500]
+ default: 3300
+
+ vsc8531,edge-slowdown:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: >
+ Percentage by which the edge rate should be slowed down relative to
+ the fastest possible edge time. This setting helps reduce electromagnetic
+ interference (EMI) by adjusting the drive strength of the MAC interface
+ output signals. Valid values depend on the vddmac voltage setting
+ according to the edge rate change table in the datasheet.
+
+ - When vsc8531,vddmac = 3300 mV: allowed values are 0, 2, 4, 7, 10, 17, 29, and 53.
+ (Recommended: 7)
+ - When vsc8531,vddmac = 2500 mV: allowed values are 0, 3, 6, 10, 14, 23, 37, and 63.
+ (Recommended: 10)
+ - When vsc8531,vddmac = 1800 mV: allowed values are 0, 5, 9, 16, 23, 35, 52, and 76.
+ (Recommended: 0)
+ - When vsc8531,vddmac = 1500 mV: allowed values are 0, 6, 14, 21, 29, 42, 58, and 77.
+ (Recommended: 0)
+ enum: [0, 2, 3, 4, 5, 6, 7, 9, 10, 14, 16, 17, 21, 23, 29, 35, 37, 42, 52, 53, 58, 63, 76, 77]
+ default: 0
+
+ vsc8531,led-0-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: LED[0] behavior mode. See include/dt-bindings/net/mscc-phy-vsc8531.h
+ for available modes.
+ minimum: 0
+ maximum: 15
+ default: 1
+
+ vsc8531,led-1-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: LED[1] behavior mode. See include/dt-bindings/net/mscc-phy-vsc8531.h
+ for available modes.
+ minimum: 0
+ maximum: 15
+ default: 2
+
+ vsc8531,led-2-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: LED[2] behavior mode. See include/dt-bindings/net/mscc-phy-vsc8531.h
+ for available modes.
+ minimum: 0
+ maximum: 15
+ default: 0
+
+ vsc8531,led-3-mode:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: LED[3] behavior mode. See include/dt-bindings/net/mscc-phy-vsc8531.h
+ for available modes.
+ minimum: 0
+ maximum: 15
+ default: 8
+
+ load-save-gpios:
+ description: GPIO phandle used for the load/save operation of the PTP hardware
+ clock (PHC).
+ maxItems: 1
+
+dependencies:
+ vsc8531,edge-slowdown:
+ - vsc8531,vddmac
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/net/mscc-phy-vsc8531.h>
+
+ mdio {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-phy@0 {
+ compatible = "ethernet-phy-id0007.0772", "ethernet-phy-ieee802.3-c22";
+ reg = <0>;
+ vsc8531,vddmac = <3300>;
+ vsc8531,edge-slowdown = <7>;
+ vsc8531,led-0-mode = <VSC8531_LINK_1000_ACTIVITY>;
+ vsc8531,led-1-mode = <VSC8531_LINK_100_ACTIVITY>;
+ load-save-gpios = <&gpio 10 GPIO_ACTIVE_HIGH>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/net/nxp,netc-blk-ctrl.yaml b/Documentation/devicetree/bindings/net/nxp,netc-blk-ctrl.yaml
index 97389fd5dbbf..deea4fd73d76 100644
--- a/Documentation/devicetree/bindings/net/nxp,netc-blk-ctrl.yaml
+++ b/Documentation/devicetree/bindings/net/nxp,netc-blk-ctrl.yaml
@@ -21,6 +21,7 @@ maintainers:
properties:
compatible:
enum:
+ - nxp,imx94-netc-blk-ctrl
- nxp,imx95-netc-blk-ctrl
reg:
diff --git a/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml b/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml
index bb1ee3398655..0b3803f647b7 100644
--- a/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml
+++ b/Documentation/devicetree/bindings/net/pse-pd/ti,tps23881.yaml
@@ -16,6 +16,7 @@ properties:
compatible:
enum:
- ti,tps23881
+ - ti,tps23881b
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/net/qcom,ethqos.yaml b/Documentation/devicetree/bindings/net/qcom,ethqos.yaml
index e7ee0d9efed8..423959cb928d 100644
--- a/Documentation/devicetree/bindings/net/qcom,ethqos.yaml
+++ b/Documentation/devicetree/bindings/net/qcom,ethqos.yaml
@@ -73,6 +73,14 @@ properties:
dma-coherent: true
+ interconnects:
+ maxItems: 2
+
+ interconnect-names:
+ items:
+ - const: cpu-mac
+ - const: mac-mem
+
phys: true
phy-names:
diff --git a/Documentation/devicetree/bindings/net/qcom,ipa.yaml b/Documentation/devicetree/bindings/net/qcom,ipa.yaml
index b4a79912d473..c7f5f2ef7452 100644
--- a/Documentation/devicetree/bindings/net/qcom,ipa.yaml
+++ b/Documentation/devicetree/bindings/net/qcom,ipa.yaml
@@ -24,7 +24,6 @@ description:
iommu/iommu.txt and iommu/arm,smmu.yaml for more information about SMMU
bindings.
-
- |
-------- ---------
| | | |
diff --git a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml
index 0ac7c4b47d6b..d17112527dab 100644
--- a/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml
+++ b/Documentation/devicetree/bindings/net/rockchip-dwmac.yaml
@@ -24,6 +24,7 @@ select:
- rockchip,rk3366-gmac
- rockchip,rk3368-gmac
- rockchip,rk3399-gmac
+ - rockchip,rk3506-gmac
- rockchip,rk3528-gmac
- rockchip,rk3568-gmac
- rockchip,rk3576-gmac
@@ -50,6 +51,7 @@ properties:
- rockchip,rv1108-gmac
- items:
- enum:
+ - rockchip,rk3506-gmac
- rockchip,rk3528-gmac
- rockchip,rk3568-gmac
- rockchip,rk3576-gmac
@@ -148,6 +150,7 @@ allOf:
compatible:
contains:
enum:
+ - rockchip,rk3506-gmac
- rockchip,rk3528-gmac
then:
properties:
diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
index 658c004e6a5c..dd3c72e8363e 100644
--- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml
+++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml
@@ -86,10 +86,14 @@ properties:
- rockchip,rk3328-gmac
- rockchip,rk3366-gmac
- rockchip,rk3368-gmac
+ - rockchip,rk3399-gmac
+ - rockchip,rk3506-gmac
+ - rockchip,rk3528-gmac
+ - rockchip,rk3568-gmac
- rockchip,rk3576-gmac
- rockchip,rk3588-gmac
- - rockchip,rk3399-gmac
- rockchip,rv1108-gmac
+ - rockchip,rv1126-gmac
- snps,dwmac
- snps,dwmac-3.40a
- snps,dwmac-3.50a
diff --git a/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml b/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml
index ce21979a2d9a..e8d3814db0e9 100644
--- a/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml
+++ b/Documentation/devicetree/bindings/net/sophgo,sg2044-dwmac.yaml
@@ -70,6 +70,25 @@ required:
allOf:
- $ref: snps,dwmac.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: sophgo,sg2042-dwmac
+ then:
+ properties:
+ phy-mode:
+ enum:
+ - rgmii-rxid
+ - rgmii-id
+ else:
+ properties:
+ phy-mode:
+ enum:
+ - rgmii
+ - rgmii-rxid
+ - rgmii-txid
+ - rgmii-id
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml b/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml
index d14ca81f70e0..8b5da602a2e8 100644
--- a/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml
+++ b/Documentation/devicetree/bindings/net/ti,cpsw-switch.yaml
@@ -156,7 +156,6 @@ patternProperties:
CPSW MDIO bus.
$ref: ti,davinci-mdio.yaml#
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
index eabceb849537..ae6b97cdc44b 100644
--- a/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/mediatek,mt76.yaml
@@ -151,6 +151,12 @@ properties:
- ETSI
- JP
+ country:
+ $ref: /schemas/types.yaml#/definitions/string
+ pattern: '^[A-Z]{2}$'
+ description:
+ ISO 3166-1 alpha-2 country code for power limits
+
patternProperties:
"^txpower-[256]g$":
type: object
@@ -210,6 +216,66 @@ properties:
minItems: 13
maxItems: 13
+ paths-cck:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 4
+ maxItems: 4
+ description:
+ 4 half-dBm backoff values (1 - 4 antennas, single spacial
+ stream)
+
+ paths-ofdm:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 4
+ maxItems: 4
+ description:
+ 4 half-dBm backoff values (1 - 4 antennas, single spacial
+ stream)
+
+ paths-ofdm-bf:
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 4
+ maxItems: 4
+ description:
+ 4 half-dBm backoff values for beamforming
+ (1 - 4 antennas, single spacial stream)
+
+ paths-ru:
+ $ref: /schemas/types.yaml#/definitions/uint8-matrix
+ description:
+ Sets of half-dBm backoff values for 802.11ax rates for
+ 1T1ss (aka 1 transmitting antenna with 1 spacial stream),
+ 2T1ss, 3T1ss, 4T1ss, 2T2ss, 3T2ss, 4T2ss, 3T3ss, 4T3ss
+ and 4T4ss.
+ Each set starts with the number of channel bandwidth or
+ resource unit settings for which the rate set applies,
+ followed by 10 power limit values. The order of the
+ channel resource unit settings is RU26, RU52, RU106,
+ RU242/SU20, RU484/SU40, RU996/SU80 and RU2x996/SU160.
+ minItems: 1
+ maxItems: 7
+ items:
+ minItems: 11
+ maxItems: 11
+
+ paths-ru-bf:
+ $ref: /schemas/types.yaml#/definitions/uint8-matrix
+ description:
+ Sets of half-dBm backoff (beamforming) values for 802.11ax
+ rates for 1T1ss (aka 1 transmitting antenna with 1 spacial
+ stream), 2T1ss, 3T1ss, 4T1ss, 2T2ss, 3T2ss, 4T2ss, 3T3ss,
+ 4T3ss and 4T4ss.
+ Each set starts with the number of channel bandwidth or
+ resource unit settings for which the rate set applies,
+ followed by 10 power limit values. The order of the
+ channel resource unit settings is RU26, RU52, RU106,
+ RU242/SU20, RU484/SU40, RU996/SU80 and RU2x996/SU160.
+ minItems: 1
+ maxItems: 7
+ items:
+ minItems: 11
+ maxItems: 11
+
txs-delta:
$ref: /schemas/types.yaml#/definitions/uint32-array
description:
diff --git a/Documentation/devicetree/bindings/net/wireless/ti,wlcore.yaml b/Documentation/devicetree/bindings/net/wireless/ti,wlcore.yaml
index 75c9489f319b..9de5fdefcbcc 100644
--- a/Documentation/devicetree/bindings/net/wireless/ti,wlcore.yaml
+++ b/Documentation/devicetree/bindings/net/wireless/ti,wlcore.yaml
@@ -50,7 +50,6 @@ properties:
Points to the node of the regulator that powers/enable the wl12xx/wl18xx
chip. This is required when connected via SPI.
-
ref-clock-frequency:
$ref: /schemas/types.yaml#/definitions/uint32
description: Reference clock frequency.
diff --git a/Documentation/devicetree/bindings/npu/arm,ethos.yaml b/Documentation/devicetree/bindings/npu/arm,ethos.yaml
new file mode 100644
index 000000000000..716c4997f976
--- /dev/null
+++ b/Documentation/devicetree/bindings/npu/arm,ethos.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/npu/arm,ethos.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Arm Ethos U65/U85
+
+maintainers:
+ - Rob Herring <robh@kernel.org>
+
+description: >
+ The Arm Ethos-U NPUs are designed for IoT inference applications. The NPUs
+ can accelerate 8-bit and 16-bit integer quantized networks:
+
+ Transformer networks (U85 only)
+ Convolutional Neural Networks (CNN)
+ Recurrent Neural Networks (RNN)
+
+ Further documentation is available here:
+
+ U65 TRM: https://developer.arm.com/documentation/102023/
+ U85 TRM: https://developer.arm.com/documentation/102685/
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - fsl,imx93-npu
+ - const: arm,ethos-u65
+ - items:
+ - {}
+ - const: arm,ethos-u85
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: core
+ - const: apb
+
+ power-domains:
+ maxItems: 1
+
+ sram:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/imx93-clock.h>
+
+ npu@4a900000 {
+ compatible = "fsl,imx93-npu", "arm,ethos-u65";
+ reg = <0x4a900000 0x1000>;
+ interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>;
+ power-domains = <&mlmix>;
+ clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>;
+ clock-names = "core", "apb";
+ sram = <&sram>;
+ };
+...
diff --git a/Documentation/devicetree/bindings/nvmem/brcm,ocotp.txt b/Documentation/devicetree/bindings/nvmem/brcm,ocotp.txt
deleted file mode 100644
index 0415265c215a..000000000000
--- a/Documentation/devicetree/bindings/nvmem/brcm,ocotp.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-Broadcom OTP memory controller
-
-Required Properties:
-- compatible: "brcm,ocotp" for the first generation Broadcom OTPC which is used
- in Cygnus and supports 32 bit read/write. Use "brcm,ocotp-v2" for the second
- generation Broadcom OTPC which is used in SoC's such as Stingray and supports
- 64-bit read/write.
-- reg: Base address of the OTP controller.
-- brcm,ocotp-size: Amount of memory available, in 32 bit words
-
-Example:
-
-otp: otp@301c800 {
- compatible = "brcm,ocotp";
- reg = <0x0301c800 0x2c>;
- brcm,ocotp-size = <2048>;
-};
diff --git a/Documentation/devicetree/bindings/nvmem/brcm,ocotp.yaml b/Documentation/devicetree/bindings/nvmem/brcm,ocotp.yaml
new file mode 100644
index 000000000000..ffad28417488
--- /dev/null
+++ b/Documentation/devicetree/bindings/nvmem/brcm,ocotp.yaml
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/nvmem/brcm,ocotp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom OTP memory controller
+
+maintainers:
+ - Ray Jui <rjui@broadcom.com>
+ - Scott Branden <sbranden@broadcom.com>
+
+properties:
+ compatible:
+ enum:
+ - brcm,ocotp
+ - brcm,ocotp-v2
+
+ reg:
+ maxItems: 1
+
+ brcm,ocotp-size:
+ description: Amount of memory available, in 32-bit words
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+required:
+ - compatible
+ - reg
+ - brcm,ocotp-size
+
+additionalProperties: false
+
+examples:
+ - |
+ otp@301c800 {
+ compatible = "brcm,ocotp";
+ reg = <0x0301c800 0x2c>;
+ brcm,ocotp-size = <2048>;
+ };
diff --git a/Documentation/devicetree/bindings/pci/altr,pcie-root-port.yaml b/Documentation/devicetree/bindings/pci/altr,pcie-root-port.yaml
index 5d3f48a001b7..f516db47ab20 100644
--- a/Documentation/devicetree/bindings/pci/altr,pcie-root-port.yaml
+++ b/Documentation/devicetree/bindings/pci/altr,pcie-root-port.yaml
@@ -93,7 +93,6 @@ allOf:
reg-names:
minItems: 3
-
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml b/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml
index 79a21ba0f9fd..d67cb7a850a3 100644
--- a/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/amlogic,axg-pcie.yaml
@@ -20,9 +20,10 @@ allOf:
select:
properties:
compatible:
- enum:
- - amlogic,axg-pcie
- - amlogic,g12a-pcie
+ contains:
+ enum:
+ - amlogic,axg-pcie
+ - amlogic,g12a-pcie
required:
- compatible
@@ -36,13 +37,13 @@ properties:
reg:
items:
- - description: External local bus interface registers
+ - description: Data Bus Interface registers
- description: Meson designed configuration registers
- description: PCIe configuration space
reg-names:
items:
- - const: elbi
+ - const: dbi
- const: cfg
- const: config
@@ -51,15 +52,15 @@ properties:
clocks:
items:
+ - description: PCIe PHY clock
- description: PCIe GEN 100M PLL clock
- description: PCIe RC clock gate
- - description: PCIe PHY clock
clock-names:
items:
+ - const: general
- const: pclk
- const: port
- - const: general
phys:
maxItems: 1
@@ -88,7 +89,7 @@ required:
- reg
- reg-names
- interrupts
- - clock
+ - clocks
- clock-names
- "#address-cells"
- "#size-cells"
@@ -113,10 +114,10 @@ examples:
pcie: pcie@f9800000 {
compatible = "amlogic,axg-pcie", "snps,dw-pcie";
reg = <0xf9800000 0x400000>, <0xff646000 0x2000>, <0xf9f00000 0x100000>;
- reg-names = "elbi", "cfg", "config";
+ reg-names = "dbi", "cfg", "config";
interrupts = <GIC_SPI 177 IRQ_TYPE_EDGE_RISING>;
- clocks = <&pclk>, <&clk_port>, <&clk_phy>;
- clock-names = "pclk", "port", "general";
+ clocks = <&clk_phy>, <&pclk>, <&clk_port>;
+ clock-names = "general", "pclk", "port";
resets = <&reset_pcie_port>, <&reset_pcie_apb>;
reset-names = "port", "apb";
phys = <&pcie_phy>;
diff --git a/Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml b/Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml
new file mode 100644
index 000000000000..b910a42e0843
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/cix,sky1-pcie-host.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/cix,sky1-pcie-host.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: CIX Sky1 PCIe Root Complex
+
+maintainers:
+ - Hans Zhang <hans.zhang@cixtech.com>
+
+description:
+ PCIe root complex controller based on the Cadence PCIe core.
+
+allOf:
+ - $ref: /schemas/pci/pci-host-bridge.yaml#
+
+properties:
+ compatible:
+ const: cix,sky1-pcie-host
+
+ reg:
+ items:
+ - description: PCIe controller registers.
+ - description: ECAM registers.
+ - description: Remote CIX System Unit strap registers.
+ - description: Remote CIX System Unit status registers.
+ - description: Region for sending messages registers.
+
+ reg-names:
+ items:
+ - const: reg
+ - const: cfg
+ - const: rcsu_strap
+ - const: rcsu_status
+ - const: msg
+
+ ranges:
+ maxItems: 3
+
+required:
+ - compatible
+ - ranges
+ - bus-range
+ - device_type
+ - interrupt-map
+ - interrupt-map-mask
+ - msi-map
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@a010000 {
+ compatible = "cix,sky1-pcie-host";
+ reg = <0x00 0x0a010000 0x00 0x10000>,
+ <0x00 0x2c000000 0x00 0x4000000>,
+ <0x00 0x0a000300 0x00 0x100>,
+ <0x00 0x0a000400 0x00 0x100>,
+ <0x00 0x60000000 0x00 0x00100000>;
+ reg-names = "reg", "cfg", "rcsu_strap", "rcsu_status", "msg";
+ ranges = <0x01000000 0x00 0x60100000 0x00 0x60100000 0x00 0x00100000>,
+ <0x02000000 0x00 0x60200000 0x00 0x60200000 0x00 0x1fe00000>,
+ <0x43000000 0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ bus-range = <0xc0 0xff>;
+ device_type = "pci";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH 0>,
+ <0 0 0 2 &gic 0 0 GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH 0>,
+ <0 0 0 3 &gic 0 0 GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH 0>,
+ <0 0 0 4 &gic 0 0 GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH 0>;
+ msi-map = <0xc000 &gic_its 0xc000 0x4000>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/loongson.yaml b/Documentation/devicetree/bindings/pci/loongson.yaml
index 1988465e73a1..e5bba63aa947 100644
--- a/Documentation/devicetree/bindings/pci/loongson.yaml
+++ b/Documentation/devicetree/bindings/pci/loongson.yaml
@@ -32,7 +32,6 @@ properties:
minItems: 1
maxItems: 3
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-mt7623.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-mt7623.yaml
new file mode 100644
index 000000000000..e33bcc216e30
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-mt7623.yaml
@@ -0,0 +1,164 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/mediatek-pcie-mt7623.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: PCIe controller on MediaTek SoCs
+
+maintainers:
+ - Christian Marangi <ansuelsmth@gmail.com>
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt2701-pcie
+ - mediatek,mt7623-pcie
+
+ reg:
+ minItems: 4
+ maxItems: 4
+
+ reg-names:
+ items:
+ - const: subsys
+ - const: port0
+ - const: port1
+ - const: port2
+
+ clocks:
+ minItems: 4
+ maxItems: 4
+
+ clock-names:
+ items:
+ - const: free_ck
+ - const: sys_ck0
+ - const: sys_ck1
+ - const: sys_ck2
+
+ resets:
+ minItems: 3
+ maxItems: 3
+
+ reset-names:
+ items:
+ - const: pcie-rst0
+ - const: pcie-rst1
+ - const: pcie-rst2
+
+ phys:
+ minItems: 3
+ maxItems: 3
+
+ phy-names:
+ items:
+ - const: pcie-phy0
+ - const: pcie-phy1
+ - const: pcie-phy2
+
+ power-domains:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - ranges
+ - clocks
+ - clock-names
+ - '#interrupt-cells'
+ - resets
+ - reset-names
+ - phys
+ - phy-names
+ - power-domains
+ - pcie@0,0
+ - pcie@1,0
+ - pcie@2,0
+
+allOf:
+ - $ref: /schemas/pci/pci-host-bridge.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ # MT7623
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/clock/mt2701-clk.h>
+ #include <dt-bindings/reset/mt2701-resets.h>
+ #include <dt-bindings/phy/phy.h>
+ #include <dt-bindings/power/mt2701-power.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@1a140000 {
+ compatible = "mediatek,mt7623-pcie";
+ device_type = "pci";
+ reg = <0 0x1a140000 0 0x1000>, /* PCIe shared registers */
+ <0 0x1a142000 0 0x1000>, /* Port0 registers */
+ <0 0x1a143000 0 0x1000>, /* Port1 registers */
+ <0 0x1a144000 0 0x1000>; /* Port2 registers */
+ reg-names = "subsys", "port0", "port1", "port2";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0xf800 0 0 0>;
+ interrupt-map = <0x0000 0 0 0 &sysirq GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>,
+ <0x0800 0 0 0 &sysirq GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>,
+ <0x1000 0 0 0 &sysirq GIC_SPI 195 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
+ <&hifsys CLK_HIFSYS_PCIE0>,
+ <&hifsys CLK_HIFSYS_PCIE1>,
+ <&hifsys CLK_HIFSYS_PCIE2>;
+ clock-names = "free_ck", "sys_ck0", "sys_ck1", "sys_ck2";
+ resets = <&hifsys MT2701_HIFSYS_PCIE0_RST>,
+ <&hifsys MT2701_HIFSYS_PCIE1_RST>,
+ <&hifsys MT2701_HIFSYS_PCIE2_RST>;
+ reset-names = "pcie-rst0", "pcie-rst1", "pcie-rst2";
+ phys = <&pcie0_phy PHY_TYPE_PCIE>, <&pcie1_phy PHY_TYPE_PCIE>,
+ <&pcie2_phy PHY_TYPE_PCIE>;
+ phy-names = "pcie-phy0", "pcie-phy1", "pcie-phy2";
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
+ bus-range = <0x00 0xff>;
+ ranges = <0x81000000 0 0x1a160000 0 0x1a160000 0 0x00010000>, /* I/O space */
+ <0x83000000 0 0x60000000 0 0x60000000 0 0x10000000>; /* memory space */
+
+ pcie@0,0 {
+ device_type = "pci";
+ reg = <0x0000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &sysirq GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>;
+ ranges;
+ };
+
+ pcie@1,0 {
+ device_type = "pci";
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &sysirq GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
+ ranges;
+ };
+
+ pcie@2,0 {
+ device_type = "pci";
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &sysirq GIC_SPI 195 IRQ_TYPE_LEVEL_LOW>;
+ ranges;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.txt b/Documentation/devicetree/bindings/pci/mediatek-pcie.txt
deleted file mode 100644
index 684227522267..000000000000
--- a/Documentation/devicetree/bindings/pci/mediatek-pcie.txt
+++ /dev/null
@@ -1,289 +0,0 @@
-MediaTek Gen2 PCIe controller
-
-Required properties:
-- compatible: Should contain one of the following strings:
- "mediatek,mt2701-pcie"
- "mediatek,mt2712-pcie"
- "mediatek,mt7622-pcie"
- "mediatek,mt7623-pcie"
- "mediatek,mt7629-pcie"
- "airoha,en7523-pcie"
-- device_type: Must be "pci"
-- reg: Base addresses and lengths of the root ports.
-- reg-names: Names of the above areas to use during resource lookup.
-- #address-cells: Address representation for root ports (must be 3)
-- #size-cells: Size representation for root ports (must be 2)
-- clocks: Must contain an entry for each entry in clock-names.
- See ../clocks/clock-bindings.txt for details.
-- clock-names:
- Mandatory entries:
- - sys_ckN :transaction layer and data link layer clock
- Required entries for MT2701/MT7623:
- - free_ck :for reference clock of PCIe subsys
- Required entries for MT2712/MT7622:
- - ahb_ckN :AHB slave interface operating clock for CSR access and RC
- initiated MMIO access
- Required entries for MT7622:
- - axi_ckN :application layer MMIO channel operating clock
- - aux_ckN :pe2_mac_bridge and pe2_mac_core operating clock when
- pcie_mac_ck/pcie_pipe_ck is turned off
- - obff_ckN :OBFF functional block operating clock
- - pipe_ckN :LTSSM and PHY/MAC layer operating clock
- where N starting from 0 to one less than the number of root ports.
-- phys: List of PHY specifiers (used by generic PHY framework).
-- phy-names : Must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
- number of PHYs as specified in *phys* property.
-- power-domains: A phandle and power domain specifier pair to the power domain
- which is responsible for collapsing and restoring power to the peripheral.
-- bus-range: Range of bus numbers associated with this controller.
-- ranges: Ranges for the PCI memory and I/O regions.
-
-Required properties for MT7623/MT2701:
-- #interrupt-cells: Size representation for interrupts (must be 1)
-- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
- Please refer to the standard PCI bus binding document for a more detailed
- explanation.
-- resets: Must contain an entry for each entry in reset-names.
- See ../reset/reset.txt for details.
-- reset-names: Must be "pcie-rst0", "pcie-rst1", "pcie-rstN".. based on the
- number of root ports.
-
-Required properties for MT2712/MT7622/MT7629:
--interrupts: A list of interrupt outputs of the controller, must have one
- entry for each PCIe port
-- interrupt-names: Must include the following entries:
- - "pcie_irq": The interrupt that is asserted when an MSI/INTX is received
-- linux,pci-domain: PCI domain ID. Should be unique for each host controller
-
-In addition, the device tree node must have sub-nodes describing each
-PCIe port interface, having the following mandatory properties:
-
-Required properties:
-- device_type: Must be "pci"
-- reg: Only the first four bytes are used to refer to the correct bus number
- and device number.
-- #address-cells: Must be 3
-- #size-cells: Must be 2
-- #interrupt-cells: Must be 1
-- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
- Please refer to the standard PCI bus binding document for a more detailed
- explanation.
-- ranges: Sub-ranges distributed from the PCIe controller node. An empty
- property is sufficient.
-
-Examples for MT7623:
-
- hifsys: syscon@1a000000 {
- compatible = "mediatek,mt7623-hifsys",
- "mediatek,mt2701-hifsys",
- "syscon";
- reg = <0 0x1a000000 0 0x1000>;
- #clock-cells = <1>;
- #reset-cells = <1>;
- };
-
- pcie: pcie@1a140000 {
- compatible = "mediatek,mt7623-pcie";
- device_type = "pci";
- reg = <0 0x1a140000 0 0x1000>, /* PCIe shared registers */
- <0 0x1a142000 0 0x1000>, /* Port0 registers */
- <0 0x1a143000 0 0x1000>, /* Port1 registers */
- <0 0x1a144000 0 0x1000>; /* Port2 registers */
- reg-names = "subsys", "port0", "port1", "port2";
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0xf800 0 0 0>;
- interrupt-map = <0x0000 0 0 0 &sysirq GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>,
- <0x0800 0 0 0 &sysirq GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>,
- <0x1000 0 0 0 &sysirq GIC_SPI 195 IRQ_TYPE_LEVEL_LOW>;
- clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
- <&hifsys CLK_HIFSYS_PCIE0>,
- <&hifsys CLK_HIFSYS_PCIE1>,
- <&hifsys CLK_HIFSYS_PCIE2>;
- clock-names = "free_ck", "sys_ck0", "sys_ck1", "sys_ck2";
- resets = <&hifsys MT2701_HIFSYS_PCIE0_RST>,
- <&hifsys MT2701_HIFSYS_PCIE1_RST>,
- <&hifsys MT2701_HIFSYS_PCIE2_RST>;
- reset-names = "pcie-rst0", "pcie-rst1", "pcie-rst2";
- phys = <&pcie0_phy PHY_TYPE_PCIE>, <&pcie1_phy PHY_TYPE_PCIE>,
- <&pcie2_phy PHY_TYPE_PCIE>;
- phy-names = "pcie-phy0", "pcie-phy1", "pcie-phy2";
- power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
- bus-range = <0x00 0xff>;
- ranges = <0x81000000 0 0x1a160000 0 0x1a160000 0 0x00010000 /* I/O space */
- 0x83000000 0 0x60000000 0 0x60000000 0 0x10000000>; /* memory space */
-
- pcie@0,0 {
- reg = <0x0000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &sysirq GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>;
- ranges;
- };
-
- pcie@1,0 {
- reg = <0x0800 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &sysirq GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
- ranges;
- };
-
- pcie@2,0 {
- reg = <0x1000 0 0 0 0>;
- #address-cells = <3>;
- #size-cells = <2>;
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 0>;
- interrupt-map = <0 0 0 0 &sysirq GIC_SPI 195 IRQ_TYPE_LEVEL_LOW>;
- ranges;
- };
- };
-
-Examples for MT2712:
-
- pcie1: pcie@112ff000 {
- compatible = "mediatek,mt2712-pcie";
- device_type = "pci";
- reg = <0 0x112ff000 0 0x1000>;
- reg-names = "port1";
- linux,pci-domain = <1>;
- #address-cells = <3>;
- #size-cells = <2>;
- interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "pcie_irq";
- clocks = <&topckgen CLK_TOP_PE2_MAC_P1_SEL>,
- <&pericfg CLK_PERI_PCIE1>;
- clock-names = "sys_ck1", "ahb_ck1";
- phys = <&u3port1 PHY_TYPE_PCIE>;
- phy-names = "pcie-phy1";
- bus-range = <0x00 0xff>;
- ranges = <0x82000000 0 0x11400000 0x0 0x11400000 0 0x300000>;
- status = "disabled";
-
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 7>;
- interrupt-map = <0 0 0 1 &pcie_intc1 0>,
- <0 0 0 2 &pcie_intc1 1>,
- <0 0 0 3 &pcie_intc1 2>,
- <0 0 0 4 &pcie_intc1 3>;
- pcie_intc1: interrupt-controller {
- interrupt-controller;
- #address-cells = <0>;
- #interrupt-cells = <1>;
- };
- };
-
- pcie0: pcie@11700000 {
- compatible = "mediatek,mt2712-pcie";
- device_type = "pci";
- reg = <0 0x11700000 0 0x1000>;
- reg-names = "port0";
- linux,pci-domain = <0>;
- #address-cells = <3>;
- #size-cells = <2>;
- interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "pcie_irq";
- clocks = <&topckgen CLK_TOP_PE2_MAC_P0_SEL>,
- <&pericfg CLK_PERI_PCIE0>;
- clock-names = "sys_ck0", "ahb_ck0";
- phys = <&u3port0 PHY_TYPE_PCIE>;
- phy-names = "pcie-phy0";
- bus-range = <0x00 0xff>;
- ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>;
- status = "disabled";
-
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 7>;
- interrupt-map = <0 0 0 1 &pcie_intc0 0>,
- <0 0 0 2 &pcie_intc0 1>,
- <0 0 0 3 &pcie_intc0 2>,
- <0 0 0 4 &pcie_intc0 3>;
- pcie_intc0: interrupt-controller {
- interrupt-controller;
- #address-cells = <0>;
- #interrupt-cells = <1>;
- };
- };
-
-Examples for MT7622:
-
- pcie0: pcie@1a143000 {
- compatible = "mediatek,mt7622-pcie";
- device_type = "pci";
- reg = <0 0x1a143000 0 0x1000>;
- reg-names = "port0";
- linux,pci-domain = <0>;
- #address-cells = <3>;
- #size-cells = <2>;
- interrupts = <GIC_SPI 228 IRQ_TYPE_LEVEL_LOW>;
- interrupt-names = "pcie_irq";
- clocks = <&pciesys CLK_PCIE_P0_MAC_EN>,
- <&pciesys CLK_PCIE_P0_AHB_EN>,
- <&pciesys CLK_PCIE_P0_AUX_EN>,
- <&pciesys CLK_PCIE_P0_AXI_EN>,
- <&pciesys CLK_PCIE_P0_OBFF_EN>,
- <&pciesys CLK_PCIE_P0_PIPE_EN>;
- clock-names = "sys_ck0", "ahb_ck0", "aux_ck0",
- "axi_ck0", "obff_ck0", "pipe_ck0";
-
- power-domains = <&scpsys MT7622_POWER_DOMAIN_HIF0>;
- bus-range = <0x00 0xff>;
- ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x8000000>;
- status = "disabled";
-
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 7>;
- interrupt-map = <0 0 0 1 &pcie_intc0 0>,
- <0 0 0 2 &pcie_intc0 1>,
- <0 0 0 3 &pcie_intc0 2>,
- <0 0 0 4 &pcie_intc0 3>;
- pcie_intc0: interrupt-controller {
- interrupt-controller;
- #address-cells = <0>;
- #interrupt-cells = <1>;
- };
- };
-
- pcie1: pcie@1a145000 {
- compatible = "mediatek,mt7622-pcie";
- device_type = "pci";
- reg = <0 0x1a145000 0 0x1000>;
- reg-names = "port1";
- linux,pci-domain = <1>;
- #address-cells = <3>;
- #size-cells = <2>;
- interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_LOW>;
- interrupt-names = "pcie_irq";
- clocks = <&pciesys CLK_PCIE_P1_MAC_EN>,
- /* designer has connect RC1 with p0_ahb clock */
- <&pciesys CLK_PCIE_P0_AHB_EN>,
- <&pciesys CLK_PCIE_P1_AUX_EN>,
- <&pciesys CLK_PCIE_P1_AXI_EN>,
- <&pciesys CLK_PCIE_P1_OBFF_EN>,
- <&pciesys CLK_PCIE_P1_PIPE_EN>;
- clock-names = "sys_ck1", "ahb_ck1", "aux_ck1",
- "axi_ck1", "obff_ck1", "pipe_ck1";
-
- power-domains = <&scpsys MT7622_POWER_DOMAIN_HIF0>;
- bus-range = <0x00 0xff>;
- ranges = <0x82000000 0 0x28000000 0x0 0x28000000 0 0x8000000>;
- status = "disabled";
-
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 7>;
- interrupt-map = <0 0 0 1 &pcie_intc1 0>,
- <0 0 0 2 &pcie_intc1 1>,
- <0 0 0 3 &pcie_intc1 2>,
- <0 0 0 4 &pcie_intc1 3>;
- pcie_intc1: interrupt-controller {
- interrupt-controller;
- #address-cells = <0>;
- #interrupt-cells = <1>;
- };
- };
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml
new file mode 100644
index 000000000000..0b8c78ec4f91
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie.yaml
@@ -0,0 +1,438 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/mediatek-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: PCIe controller on MediaTek SoCs
+
+maintainers:
+ - Christian Marangi <ansuelsmth@gmail.com>
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - airoha,an7583-pcie
+ - mediatek,mt2712-pcie
+ - mediatek,mt7622-pcie
+ - mediatek,mt7629-pcie
+ - items:
+ - const: airoha,en7523-pcie
+ - const: mediatek,mt7622-pcie
+
+ reg:
+ maxItems: 1
+
+ reg-names:
+ enum: [ port0, port1 ]
+
+ clocks:
+ minItems: 1
+ maxItems: 6
+
+ clock-names:
+ minItems: 1
+ items:
+ - enum: [ sys_ck0, sys_ck1 ]
+ - enum: [ ahb_ck0, ahb_ck1 ]
+ - enum: [ aux_ck0, aux_ck1 ]
+ - enum: [ axi_ck0, axi_ck1 ]
+ - enum: [ obff_ck0, obff_ck1 ]
+ - enum: [ pipe_ck0, pipe_ck1 ]
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ const: pcie-rst1
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ const: pcie_irq
+
+ phys:
+ maxItems: 1
+
+ phy-names:
+ enum: [ pcie-phy0, pcie-phy1 ]
+
+ power-domains:
+ maxItems: 1
+
+ mediatek,pbus-csr:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: phandle to pbus-csr syscon
+ - description: offset of pbus-csr base address register
+ - description: offset of pbus-csr base address mask register
+ description:
+ Phandle with two arguments to the syscon node used to detect if
+ a given address is accessible on PCIe controller.
+
+ '#interrupt-cells':
+ const: 1
+
+ interrupt-controller:
+ description: Interrupt controller node for handling legacy PCI interrupts.
+ type: object
+ properties:
+ '#address-cells':
+ const: 0
+ '#interrupt-cells':
+ const: 1
+ interrupt-controller: true
+
+ required:
+ - '#address-cells'
+ - '#interrupt-cells'
+ - interrupt-controller
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - ranges
+ - clocks
+ - clock-names
+ - '#interrupt-cells'
+ - interrupts
+ - interrupt-names
+ - interrupt-controller
+
+allOf:
+ - $ref: /schemas/pci/pci-host-bridge.yaml#
+
+ - if:
+ properties:
+ compatible:
+ const: airoha,an7583-pcie
+ then:
+ properties:
+ reg-names:
+ const: port1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: sys_ck1
+
+ phy-names:
+ const: pcie-phy1
+
+ power-domain: false
+
+ required:
+ - resets
+ - reset-names
+ - phys
+ - phy-names
+ - mediatek,pbus-csr
+
+ - if:
+ properties:
+ compatible:
+ const: mediatek,mt2712-pcie
+ then:
+ properties:
+ clocks:
+ minItems: 2
+ maxItems: 2
+
+ clock-names:
+ minItems: 2
+ maxItems: 2
+
+ reset: false
+
+ reset-names: false
+
+ power-domains: false
+
+ mediatek,pbus-csr: false
+
+ required:
+ - phys
+ - phy-names
+
+ - if:
+ properties:
+ compatible:
+ const: mediatek,mt7622-pcie
+ then:
+ properties:
+ clocks:
+ minItems: 6
+
+ reset: false
+
+ reset-names: false
+
+ phys: false
+
+ phy-names: false
+
+ mediatek,pbus-csr: false
+
+ required:
+ - power-domains
+
+ - if:
+ properties:
+ compatible:
+ const: mediatek,mt7629-pcie
+ then:
+ properties:
+ clocks:
+ minItems: 6
+
+ reset: false
+
+ reset-names: false
+
+ mediatek,pbus-csr: false
+
+ required:
+ - power-domains
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: airoha,en7523-pcie
+ then:
+ properties:
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ maxItems: 1
+
+ reset: false
+
+ reset-names: false
+
+ phys: false
+
+ phy-names: false
+
+ power-domain: false
+
+ mediatek,pbus-csr: false
+
+unevaluatedProperties: false
+
+examples:
+ # MT2712
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/phy/phy.h>
+
+ soc_1 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@112ff000 {
+ compatible = "mediatek,mt2712-pcie";
+ device_type = "pci";
+ reg = <0 0x112ff000 0 0x1000>;
+ reg-names = "port1";
+ linux,pci-domain = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "pcie_irq";
+ clocks = <&topckgen>, /* CLK_TOP_PE2_MAC_P1_SEL */
+ <&pericfg>; /* CLK_PERI_PCIE1 */
+ clock-names = "sys_ck1", "ahb_ck1";
+ phys = <&u3port1 PHY_TYPE_PCIE>;
+ phy-names = "pcie-phy1";
+ bus-range = <0x00 0xff>;
+ ranges = <0x82000000 0 0x11400000 0x0 0x11400000 0 0x300000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc1 0>,
+ <0 0 0 2 &pcie_intc1 1>,
+ <0 0 0 3 &pcie_intc1 2>,
+ <0 0 0 4 &pcie_intc1 3>;
+ pcie_intc1: interrupt-controller {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ };
+ };
+
+ pcie@11700000 {
+ compatible = "mediatek,mt2712-pcie";
+ device_type = "pci";
+ reg = <0 0x11700000 0 0x1000>;
+ reg-names = "port0";
+ linux,pci-domain = <0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "pcie_irq";
+ clocks = <&topckgen>, /* CLK_TOP_PE2_MAC_P0_SEL */
+ <&pericfg>; /* CLK_PERI_PCIE0 */
+ clock-names = "sys_ck0", "ahb_ck0";
+ phys = <&u3port0 PHY_TYPE_PCIE>;
+ phy-names = "pcie-phy0";
+ bus-range = <0x00 0xff>;
+ ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc0 0>,
+ <0 0 0 2 &pcie_intc0 1>,
+ <0 0 0 3 &pcie_intc0 2>,
+ <0 0 0 4 &pcie_intc0 3>;
+ pcie_intc0: interrupt-controller {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ };
+ };
+ };
+
+ # MT7622
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/power/mt7622-power.h>
+
+ soc_2 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@1a143000 {
+ compatible = "mediatek,mt7622-pcie";
+ device_type = "pci";
+ reg = <0 0x1a143000 0 0x1000>;
+ reg-names = "port0";
+ linux,pci-domain = <0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ interrupts = <GIC_SPI 228 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "pcie_irq";
+ clocks = <&pciesys>, /* CLK_PCIE_P0_MAC_EN */
+ <&pciesys>, /* CLK_PCIE_P0_AHB_EN */
+ <&pciesys>, /* CLK_PCIE_P0_AUX_EN */
+ <&pciesys>, /* CLK_PCIE_P0_AXI_EN */
+ <&pciesys>, /* CLK_PCIE_P0_OBFF_EN */
+ <&pciesys>; /* CLK_PCIE_P0_PIPE_EN */
+ clock-names = "sys_ck0", "ahb_ck0", "aux_ck0",
+ "axi_ck0", "obff_ck0", "pipe_ck0";
+
+ power-domains = <&scpsys MT7622_POWER_DOMAIN_HIF0>;
+ bus-range = <0x00 0xff>;
+ ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x8000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc0_1 0>,
+ <0 0 0 2 &pcie_intc0_1 1>,
+ <0 0 0 3 &pcie_intc0_1 2>,
+ <0 0 0 4 &pcie_intc0_1 3>;
+ pcie_intc0_1: interrupt-controller {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ };
+ };
+
+ pcie@1a145000 {
+ compatible = "mediatek,mt7622-pcie";
+ device_type = "pci";
+ reg = <0 0x1a145000 0 0x1000>;
+ reg-names = "port1";
+ linux,pci-domain = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ interrupts = <GIC_SPI 229 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-names = "pcie_irq";
+ clocks = <&pciesys>, /* CLK_PCIE_P1_MAC_EN */
+ /* designer has connect RC1 with p0_ahb clock */
+ <&pciesys>, /* CLK_PCIE_P0_AHB_EN */
+ <&pciesys>, /* CLK_PCIE_P1_AUX_EN */
+ <&pciesys>, /* CLK_PCIE_P1_AXI_EN */
+ <&pciesys>, /* CLK_PCIE_P1_OBFF_EN */
+ <&pciesys>; /* CLK_PCIE_P1_PIPE_EN */
+ clock-names = "sys_ck1", "ahb_ck1", "aux_ck1",
+ "axi_ck1", "obff_ck1", "pipe_ck1";
+
+ power-domains = <&scpsys MT7622_POWER_DOMAIN_HIF0>;
+ bus-range = <0x00 0xff>;
+ ranges = <0x82000000 0 0x28000000 0x0 0x28000000 0 0x8000000>;
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc1_1 0>,
+ <0 0 0 2 &pcie_intc1_1 1>,
+ <0 0 0 3 &pcie_intc1_1 2>,
+ <0 0 0 4 &pcie_intc1_1 3>;
+ pcie_intc1_1: interrupt-controller {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ };
+ };
+ };
+
+ # AN7583
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/en7523-clk.h>
+
+ soc_3 {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@1fa92000 {
+ compatible = "airoha,an7583-pcie";
+ device_type = "pci";
+ linux,pci-domain = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ reg = <0x0 0x1fa92000 0x0 0x1670>;
+ reg-names = "port1";
+
+ clocks = <&scuclk EN7523_CLK_PCIE>;
+ clock-names = "sys_ck1";
+
+ phys = <&pciephy>;
+ phy-names = "pcie-phy1";
+
+ ranges = <0x02000000 0 0x24000000 0x0 0x24000000 0 0x4000000>;
+
+ resets = <&scuclk>; /* AN7583_PCIE1_RST */
+ reset-names = "pcie-rst1";
+
+ mediatek,pbus-csr = <&pbus_csr 0x8 0xc>;
+
+ interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "pcie_irq";
+ bus-range = <0x00 0xff>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie_intc1 0>,
+ <0 0 0 2 &pcie_intc1 1>,
+ <0 0 0 3 &pcie_intc1 2>,
+ <0 0 0 4 &pcie_intc1 3>;
+
+ pcie_intc1_4: interrupt-controller {
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml
new file mode 100644
index 000000000000..66a050028278
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/nxp,s32g-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP S32G2xxx/S32G3xxx PCIe Root Complex controller
+
+maintainers:
+ - Bogdan Hamciuc <bogdan.hamciuc@nxp.com>
+ - Ionut Vicovan <ionut.vicovan@nxp.com>
+
+description:
+ This PCIe controller is based on the Synopsys DesignWare PCIe IP.
+ The S32G SoC family has two PCIe controllers, which can be configured as
+ either Root Complex or Endpoint.
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - nxp,s32g2-pcie
+ - items:
+ - const: nxp,s32g3-pcie
+ - const: nxp,s32g2-pcie
+
+ reg:
+ maxItems: 6
+
+ reg-names:
+ items:
+ - const: dbi
+ - const: dbi2
+ - const: atu
+ - const: dma
+ - const: ctrl
+ - const: config
+
+ interrupts:
+ minItems: 1
+ maxItems: 2
+
+ interrupt-names:
+ items:
+ - const: msi
+ - const: dma
+ minItems: 1
+
+ pcie@0:
+ description:
+ Describe the S32G Root Port.
+ type: object
+ $ref: /schemas/pci/pci-pci-bridge.yaml#
+
+ properties:
+ reg:
+ maxItems: 1
+
+ phys:
+ maxItems: 1
+
+ required:
+ - reg
+ - phys
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - interrupts
+ - interrupt-names
+ - ranges
+ - pcie@0
+
+allOf:
+ - $ref: /schemas/pci/snps,dw-pcie.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/phy/phy.h>
+
+ bus {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@40400000 {
+ compatible = "nxp,s32g3-pcie", "nxp,s32g2-pcie";
+ reg = <0x00 0x40400000 0x0 0x00001000>, /* dbi registers */
+ <0x00 0x40420000 0x0 0x00001000>, /* dbi2 registers */
+ <0x00 0x40460000 0x0 0x00001000>, /* atu registers */
+ <0x00 0x40470000 0x0 0x00001000>, /* dma registers */
+ <0x00 0x40481000 0x0 0x000000f8>, /* ctrl registers */
+ <0x5f 0xffffe000 0x0 0x00002000>; /* config space */
+ reg-names = "dbi", "dbi2", "atu", "dma", "ctrl", "config";
+ dma-coherent;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ ranges =
+ <0x01000000 0x0 0x00000000 0x5f 0xfffe0000 0x0 0x00010000>,
+ <0x02000000 0x0 0x00000000 0x58 0x00000000 0x0 0x80000000>,
+ <0x02000000 0x1 0x00000000 0x59 0x00000000 0x6 0xfffe0000>;
+
+ bus-range = <0x0 0xff>;
+ interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi", "dma";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &gic 0 0 GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &gic 0 0 GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &gic 0 0 GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
+
+ pcie@0 {
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+
+ device_type = "pci";
+ phys = <&serdes0 PHY_TYPE_PCIE 0 0>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/pci-ep.yaml b/Documentation/devicetree/bindings/pci/pci-ep.yaml
index 1868a10d5b10..baeb583e0bcd 100644
--- a/Documentation/devicetree/bindings/pci/pci-ep.yaml
+++ b/Documentation/devicetree/bindings/pci/pci-ep.yaml
@@ -11,7 +11,7 @@ description: |
maintainers:
- Kishon Vijay Abraham I <kishon@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
properties:
$nodename:
diff --git a/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml
index 039eecdbd6aa..fe2e8beb5bab 100644
--- a/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml
+++ b/Documentation/devicetree/bindings/pci/plda,xpressrich3-axi-common.yaml
@@ -72,7 +72,7 @@ required:
- reg-names
- interrupts
- msi-controller
- - "#interrupt-cells"
+ - '#interrupt-cells'
- interrupt-map-mask
- interrupt-map
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml
index ab2509ec1c4b..77f8faf54737 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-common.yaml
@@ -8,7 +8,7 @@ title: Qualcomm PCI Express Root Complex Common Properties
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
properties:
reg:
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml
index ac3414203d38..bed9a40b186b 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm PCIe Endpoint Controller
maintainers:
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml
index bdddd4f499d1..1f2d098b8638 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8255p.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SA8255p based firmware managed and ECAM compliant PCIe Root Comp
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SA8255p SoC PCIe root complex controller is based on the Synopsys
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml
index 19afe2a03409..63630a814f28 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sa8775p.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SA8775p PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SA8775p SoC PCIe root complex controller is based on the Synopsys
@@ -78,6 +78,9 @@ properties:
required:
- interconnects
- interconnect-names
+ - power-domains
+ - resets
+ - reset-names
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml
index 4d0a91556603..1f942b3075f1 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sc7280.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SC7280 PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SC7280 SoC PCIe root complex controller is based on the Synopsys
@@ -76,6 +76,11 @@ properties:
items:
- const: pci
+required:
+ - power-domains
+ - resets
+ - reset-names
+
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml
index 34a4d7b2c845..6a7c410c9fc3 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SC8180x PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SC8180x SoC PCIe root complex controller is based on the Synopsys
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8280xp.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8280xp.yaml
index 15ba2385eb73..bc0e71dc06a3 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8280xp.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8280xp.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SC8280XP PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SC8280XP SoC PCIe root complex controller is based on the Synopsys
@@ -61,6 +61,9 @@ properties:
required:
- interconnects
- interconnect-names
+ - power-domains
+ - resets
+ - reset-names
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml
index 26b247a41785..6a5421e4f19d 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SM8150 PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SM8150 SoC PCIe root complex controller is based on the Synopsys
@@ -74,6 +74,11 @@ properties:
items:
- const: pci
+required:
+ - power-domains
+ - resets
+ - reset-names
+
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.yaml
index af4dae68d508..adbeaa8f2c13 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8250.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SM8250 PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SM8250 SoC PCIe root complex controller is based on the Synopsys
@@ -83,6 +83,11 @@ properties:
items:
- const: pci
+required:
+ - power-domains
+ - resets
+ - reset-names
+
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.yaml
index dde3079adbb3..5744d5e969fb 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8350.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SM8350 PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SM8350 SoC PCIe root complex controller is based on the Synopsys
@@ -73,6 +73,11 @@ properties:
items:
- const: pci
+required:
+ - power-domains
+ - resets
+ - reset-names
+
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml
index 6e0a6d8f0ed0..28b8ffb74124 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8450.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SM8450 PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SM8450 SoC PCIe root complex controller is based on the Synopsys
@@ -77,6 +77,11 @@ properties:
items:
- const: pci
+required:
+ - power-domains
+ - resets
+ - reset-names
+
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml
index 38b561e23c1f..3a94a9c1bb15 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8550.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SM8550 PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm SM8550 SoC (and compatible) PCIe root complex controller is based on
@@ -20,6 +20,7 @@ properties:
- const: qcom,pcie-sm8550
- items:
- enum:
+ - qcom,kaanapali-pcie
- qcom,sar2130p-pcie
- qcom,pcie-sm8650
- qcom,pcie-sm8750
@@ -83,6 +84,11 @@ properties:
- const: pci # PCIe core reset
- const: link_down # PCIe link down reset
+required:
+ - power-domains
+ - resets
+ - reset-names
+
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml
index 61581ffbfb24..62c674ca0cf7 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml
@@ -8,7 +8,7 @@ title: Qualcomm X1E80100 PCI Express Root Complex
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description:
Qualcomm X1E80100 SoC (and compatible) PCIe root complex controller is based on
@@ -73,6 +73,11 @@ properties:
- const: pci # PCIe core reset
- const: link_down # PCIe link down reset
+required:
+ - power-domains
+ - resets
+ - reset-names
+
allOf:
- $ref: qcom,pcie-common.yaml#
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
index 0e1808105a81..c61930441be0 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
@@ -8,7 +8,7 @@ title: Qualcomm PCI express root complex
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
description: |
Qualcomm PCIe root complex controller is based on the Synopsys DesignWare
diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
new file mode 100644
index 000000000000..d668782546a2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
@@ -0,0 +1,249 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/renesas,r9a08g045-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas RZ/G3S PCIe host controller
+
+maintainers:
+ - Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+
+description:
+ Renesas RZ/G3S PCIe host controller complies with PCIe Base Specification
+ 4.0 and supports up to 5 GT/s (Gen2).
+
+properties:
+ compatible:
+ const: renesas,r9a08g045-pcie # RZ/G3S
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: System error interrupt
+ - description: System error on correctable error interrupt
+ - description: System error on non-fatal error interrupt
+ - description: System error on fatal error interrupt
+ - description: AXI error interrupt
+ - description: INTA interrupt
+ - description: INTB interrupt
+ - description: INTC interrupt
+ - description: INTD interrupt
+ - description: MSI interrupt
+ - description: Link bandwidth interrupt
+ - description: PME interrupt
+ - description: DMA interrupt
+ - description: PCIe event interrupt
+ - description: Message interrupt
+ - description: All interrupts
+
+ interrupt-names:
+ items:
+ - description: serr
+ - description: ser_cor
+ - description: serr_nonfatal
+ - description: serr_fatal
+ - description: axi_err
+ - description: inta
+ - description: intb
+ - description: intc
+ - description: intd
+ - description: msi
+ - description: link_bandwidth
+ - description: pm_pme
+ - description: dma
+ - description: pcie_evt
+ - description: msg
+ - description: all
+
+ interrupt-controller: true
+
+ clocks:
+ items:
+ - description: System clock
+ - description: PM control clock
+
+ clock-names:
+ items:
+ - description: aclk
+ - description: pm
+
+ resets:
+ items:
+ - description: AXI2PCIe Bridge reset
+ - description: Data link layer/transaction layer reset
+ - description: Transaction layer (ACLK domain) reset
+ - description: Transaction layer (PCLK domain) reset
+ - description: Physical layer reset
+ - description: Configuration register reset
+ - description: Configuration register reset
+
+ reset-names:
+ items:
+ - description: aresetn
+ - description: rst_b
+ - description: rst_gp_b
+ - description: rst_ps_b
+ - description: rst_rsm_b
+ - description: rst_cfg_b
+ - description: rst_load_b
+
+ power-domains:
+ maxItems: 1
+
+ dma-ranges:
+ description:
+ A single range for the inbound memory region.
+ maxItems: 1
+
+ renesas,sysc:
+ description: |
+ System controller registers control and monitor various PCIe
+ functionalities.
+
+ Control:
+ - transition to L1 state
+ - receiver termination settings
+ - RST_RSM_B signal
+
+ Monitor:
+ - clkl1pm clock request state
+ - power off information in L2 state
+ - errors (fatal, non-fatal, correctable)
+ $ref: /schemas/types.yaml#/definitions/phandle
+
+patternProperties:
+ "^pcie@0,[0-0]$":
+ type: object
+ allOf:
+ - $ref: /schemas/pci/pci-pci-bridge.yaml#
+
+ properties:
+ reg:
+ maxItems: 1
+
+ vendor-id:
+ const: 0x1912
+
+ device-id:
+ const: 0x0033
+
+ clocks:
+ items:
+ - description: Reference clock
+
+ clock-names:
+ items:
+ - const: ref
+
+ required:
+ - device_type
+ - vendor-id
+ - device-id
+ - clocks
+ - clock-names
+
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - interrupts
+ - interrupt-names
+ - interrupt-map
+ - interrupt-map-mask
+ - interrupt-controller
+ - power-domains
+ - "#address-cells"
+ - "#size-cells"
+ - "#interrupt-cells"
+ - renesas,sysc
+
+allOf:
+ - $ref: /schemas/pci/pci-host-bridge.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/r9a08g045-cpg.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ bus {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@11e40000 {
+ compatible = "renesas,r9a08g045-pcie";
+ reg = <0 0x11e40000 0 0x10000>;
+ ranges = <0x02000000 0 0x30000000 0 0x30000000 0 0x08000000>;
+ /* Map all possible DRAM ranges (4 GB). */
+ dma-ranges = <0x42000000 0 0x40000000 0 0x40000000 1 0x00000000>;
+ bus-range = <0x0 0xff>;
+ interrupts = <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "serr", "serr_cor", "serr_nonfatal",
+ "serr_fatal", "axi_err", "inta",
+ "intb", "intc", "intd", "msi",
+ "link_bandwidth", "pm_pme", "dma",
+ "pcie_evt", "msg", "all";
+ #interrupt-cells = <1>;
+ interrupt-controller;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie 0 0 0 0>, /* INTA */
+ <0 0 0 2 &pcie 0 0 0 1>, /* INTB */
+ <0 0 0 3 &pcie 0 0 0 2>, /* INTC */
+ <0 0 0 4 &pcie 0 0 0 3>; /* INTD */
+ clocks = <&cpg CPG_MOD R9A08G045_PCI_ACLK>,
+ <&cpg CPG_MOD R9A08G045_PCI_CLKL1PM>;
+ clock-names = "aclk", "pm";
+ resets = <&cpg R9A08G045_PCI_ARESETN>,
+ <&cpg R9A08G045_PCI_RST_B>,
+ <&cpg R9A08G045_PCI_RST_GP_B>,
+ <&cpg R9A08G045_PCI_RST_PS_B>,
+ <&cpg R9A08G045_PCI_RST_RSM_B>,
+ <&cpg R9A08G045_PCI_RST_CFG_B>,
+ <&cpg R9A08G045_PCI_RST_LOAD_B>;
+ reset-names = "aresetn", "rst_b", "rst_gp_b", "rst_ps_b",
+ "rst_rsm_b", "rst_cfg_b", "rst_load_b";
+ power-domains = <&cpg>;
+ device_type = "pci";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ renesas,sysc = <&sysc>;
+
+ pcie@0,0 {
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ ranges;
+ clocks = <&versa3 5>;
+ clock-names = "ref";
+ device_type = "pci";
+ vendor-id = <0x1912>;
+ device-id = <0x0033>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml b/Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml
index 6c6d828ce964..355c4a46bd31 100644
--- a/Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/rockchip-dw-pcie.yaml
@@ -22,6 +22,7 @@ properties:
- const: rockchip,rk3568-pcie
- items:
- enum:
+ - rockchip,rk3528-pcie
- rockchip,rk3562-pcie
- rockchip,rk3576-pcie
- rockchip,rk3588-pcie
@@ -78,6 +79,7 @@ allOf:
compatible:
contains:
enum:
+ - rockchip,rk3528-pcie
- rockchip,rk3562-pcie
- rockchip,rk3576-pcie
then:
@@ -89,6 +91,7 @@ allOf:
compatible:
contains:
enum:
+ - rockchip,rk3528-pcie
- rockchip,rk3562-pcie
- rockchip,rk3576-pcie
then:
@@ -121,7 +124,6 @@ allOf:
- const: dma2
- const: dma3
-
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
index 34594972d8db..6339a76499b2 100644
--- a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
+++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
@@ -115,11 +115,11 @@ properties:
above for new bindings.
oneOf:
- description: See native 'dbi' clock for details
- enum: [ pcie, pcie_apb_sys, aclk_dbi, reg ]
+ enum: [ pcie, pcie_apb_sys, aclk_dbi, reg, port ]
- description: See native 'mstr/slv' clock for details
enum: [ pcie_bus, pcie_inbound_axi, pcie_aclk, aclk_mst, aclk_slv ]
- description: See native 'pipe' clock for details
- enum: [ pcie_phy, pcie_phy_ref, link ]
+ enum: [ pcie_phy, pcie_phy_ref, link, general ]
- description: See native 'aux' clock for details
enum: [ pcie_aux ]
- description: See native 'ref' clock for details.
@@ -176,7 +176,7 @@ properties:
- description: See native 'phy' reset for details
enum: [ pciephy, link ]
- description: See native 'pwr' reset for details
- enum: [ turnoff ]
+ enum: [ turnoff, port ]
phys:
description:
diff --git a/Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml b/Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml
new file mode 100644
index 000000000000..c4c00b5fcdc0
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml
@@ -0,0 +1,157 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/spacemit,k1-pcie-host.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: SpacemiT K1 PCI Express Host Controller
+
+maintainers:
+ - Alex Elder <elder@riscstar.com>
+
+description: >
+ The SpacemiT K1 SoC PCIe host controller is based on the Synopsys DesignWare
+ PCIe IP. The controller uses the DesignWare built-in MSI interrupt
+ controller, and supports 256 MSIs.
+
+allOf:
+ - $ref: /schemas/pci/snps,dw-pcie.yaml#
+
+properties:
+ compatible:
+ const: spacemit,k1-pcie
+
+ reg:
+ items:
+ - description: DesignWare PCIe registers
+ - description: ATU address space
+ - description: PCIe configuration space
+ - description: Link control registers
+
+ reg-names:
+ items:
+ - const: dbi
+ - const: atu
+ - const: config
+ - const: link
+
+ clocks:
+ items:
+ - description: DWC PCIe Data Bus Interface (DBI) clock
+ - description: DWC PCIe application AXI-bus master interface clock
+ - description: DWC PCIe application AXI-bus slave interface clock
+
+ clock-names:
+ items:
+ - const: dbi
+ - const: mstr
+ - const: slv
+
+ resets:
+ items:
+ - description: DWC PCIe Data Bus Interface (DBI) reset
+ - description: DWC PCIe application AXI-bus master interface reset
+ - description: DWC PCIe application AXI-bus slave interface reset
+
+ reset-names:
+ items:
+ - const: dbi
+ - const: mstr
+ - const: slv
+
+ interrupts:
+ items:
+ - description: Interrupt used for MSIs
+
+ interrupt-names:
+ const: msi
+
+ spacemit,apmu:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ A phandle that refers to the APMU system controller, whose regmap is
+ used in managing resets and link state, along with and offset of its
+ reset control register.
+ items:
+ - items:
+ - description: phandle to APMU system controller
+ - description: register offset
+
+patternProperties:
+ '^pcie@':
+ type: object
+ $ref: /schemas/pci/pci-pci-bridge.yaml#
+
+ properties:
+ phys:
+ maxItems: 1
+
+ vpcie3v3-supply:
+ description:
+ A phandle for 3.3v regulator to use for PCIe
+
+ required:
+ - phys
+ - vpcie3v3-supply
+
+ unevaluatedProperties: false
+
+required:
+ - clocks
+ - clock-names
+ - resets
+ - reset-names
+ - interrupts
+ - interrupt-names
+ - spacemit,apmu
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/spacemit,k1-syscon.h>
+ pcie@ca400000 {
+ device_type = "pci";
+ compatible = "spacemit,k1-pcie";
+ reg = <0xca400000 0x00001000>,
+ <0xca700000 0x0001ff24>,
+ <0x9f000000 0x00002000>,
+ <0xc0c20000 0x00001000>;
+ reg-names = "dbi",
+ "atu",
+ "config",
+ "link";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x01000000 0x0 0x00000000 0x9f002000 0x0 0x00100000>,
+ <0x02000000 0x0 0x90000000 0x90000000 0x0 0x0f000000>;
+ interrupts = <142>;
+ interrupt-names = "msi";
+ clocks = <&syscon_apmu CLK_PCIE1_DBI>,
+ <&syscon_apmu CLK_PCIE1_MASTER>,
+ <&syscon_apmu CLK_PCIE1_SLAVE>;
+ clock-names = "dbi",
+ "mstr",
+ "slv";
+ resets = <&syscon_apmu RESET_PCIE1_DBI>,
+ <&syscon_apmu RESET_PCIE1_MASTER>,
+ <&syscon_apmu RESET_PCIE1_SLAVE>;
+ reset-names = "dbi",
+ "mstr",
+ "slv";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pcie1_3_cfg>;
+ spacemit,apmu = <&syscon_apmu 0x3d4>;
+
+ pcie@0 {
+ device_type = "pci";
+ compatible = "pciclass,0604";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ phys = <&pcie1_phy>;
+ vpcie3v3-supply = <&pcie_vcc_3v3>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
index 5f432452c815..33c80626e8ec 100644
--- a/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml
@@ -16,7 +16,6 @@ properties:
compatible:
const: starfive,jh7110-pcie
-
reg:
maxItems: 2
diff --git a/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
new file mode 100644
index 000000000000..fae466064780
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
@@ -0,0 +1,179 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/toshiba,tc9563.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Toshiba TC9563 PCIe switch
+
+maintainers:
+ - Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
+
+description: |
+ Toshiba TC9563 PCIe switch has one upstream and three downstream ports.
+ The 3rd downstream port has integrated endpoint device of Ethernet MAC.
+ Other two downstream ports are supposed to connect to external device.
+
+ The TC9563 PCIe switch can be configured through I2C interface before
+ PCIe link is established to change FTS, ASPM related entry delays,
+ tx amplitude etc for better power efficiency and functionality.
+
+properties:
+ compatible:
+ enum:
+ - pci1179,0623
+
+ reg:
+ maxItems: 1
+
+ resx-gpios:
+ maxItems: 1
+ description:
+ GPIO controlling the RESX# pin.
+
+ vdd18-supply: true
+
+ vdd09-supply: true
+
+ vddc-supply: true
+
+ vddio1-supply: true
+
+ vddio2-supply: true
+
+ vddio18-supply: true
+
+ i2c-parent:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description:
+ A phandle to the parent I2C node and the slave address of the device
+ used to configure tc9563 to change FTS, tx amplitude etc.
+ items:
+ - description: Phandle to the I2C controller node
+ - description: I2C slave address
+
+patternProperties:
+ "^pcie@[1-3],0$":
+ description:
+ child nodes describing the internal downstream ports of
+ the tc9563 switch.
+ type: object
+ allOf:
+ - $ref: "#/$defs/tc9563-node"
+ - $ref: /schemas/pci/pci-pci-bridge.yaml#
+ unevaluatedProperties: false
+
+$defs:
+ tc9563-node:
+ type: object
+
+ properties:
+ toshiba,tx-amplitude-microvolt:
+ description:
+ Change Tx Margin setting for low power consumption.
+
+ toshiba,no-dfe-support:
+ type: boolean
+ description:
+ Disable DFE (Decision Feedback Equalizer), which mitigates
+ intersymbol interference and some reflections caused by
+ impedance mismatches.
+
+required:
+ - resx-gpios
+ - vdd18-supply
+ - vdd09-supply
+ - vddc-supply
+ - vddio1-supply
+ - vddio2-supply
+ - vddio18-supply
+ - i2c-parent
+
+allOf:
+ - $ref: "#/$defs/tc9563-node"
+ - $ref: /schemas/pci/pci-bus-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ pcie {
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ bus-range = <0x01 0xff>;
+
+ pcie@0,0 {
+ compatible = "pci1179,0623";
+
+ reg = <0x10000 0x0 0x0 0x0 0x0>;
+ device_type = "pci";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ bus-range = <0x02 0xff>;
+
+ i2c-parent = <&qup_i2c 0x77>;
+
+ vdd18-supply = <&vdd>;
+ vdd09-supply = <&vdd>;
+ vddc-supply = <&vdd>;
+ vddio1-supply = <&vdd>;
+ vddio2-supply = <&vdd>;
+ vddio18-supply = <&vdd>;
+
+ resx-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
+
+ pcie@1,0 {
+ compatible = "pciclass,0604";
+ reg = <0x20800 0x0 0x0 0x0 0x0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ ranges;
+ bus-range = <0x03 0xff>;
+
+ toshiba,no-dfe-support;
+ };
+
+ pcie@2,0 {
+ compatible = "pciclass,0604";
+ reg = <0x21000 0x0 0x0 0x0 0x0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ ranges;
+ bus-range = <0x04 0xff>;
+ };
+
+ pcie@3,0 {
+ compatible = "pciclass,0604";
+ reg = <0x21800 0x0 0x0 0x0 0x0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ ranges;
+ bus-range = <0x05 0xff>;
+
+ toshiba,tx-amplitude-microvolt = <10>;
+
+ ethernet@0,0 {
+ reg = <0x50000 0x0 0x0 0x0 0x0>;
+ };
+
+ ethernet@0,1 {
+ reg = <0x50100 0x0 0x0 0x0 0x0>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/versatile.yaml b/Documentation/devicetree/bindings/pci/versatile.yaml
index 294c7cd84b37..d30b8849db91 100644
--- a/Documentation/devicetree/bindings/pci/versatile.yaml
+++ b/Documentation/devicetree/bindings/pci/versatile.yaml
@@ -90,5 +90,4 @@ examples:
<0x0000 0 0 4 &sic 28>;
};
-
...
diff --git a/Documentation/devicetree/bindings/perf/fsl-imx-ddr.yaml b/Documentation/devicetree/bindings/perf/fsl-imx-ddr.yaml
index d2e578d6b83b..103e4aec2439 100644
--- a/Documentation/devicetree/bindings/perf/fsl-imx-ddr.yaml
+++ b/Documentation/devicetree/bindings/perf/fsl-imx-ddr.yaml
@@ -14,6 +14,7 @@ properties:
oneOf:
- enum:
- fsl,imx8-ddr-pmu
+ - fsl,imx8dxl-db-pmu
- fsl,imx8m-ddr-pmu
- fsl,imx8mq-ddr-pmu
- fsl,imx8mm-ddr-pmu
@@ -28,7 +29,10 @@ properties:
- fsl,imx8mp-ddr-pmu
- const: fsl,imx8m-ddr-pmu
- items:
- - const: fsl,imx8dxl-ddr-pmu
+ - enum:
+ - fsl,imx8dxl-ddr-pmu
+ - fsl,imx8qm-ddr-pmu
+ - fsl,imx8qxp-ddr-pmu
- const: fsl,imx8-ddr-pmu
- items:
- enum:
@@ -43,6 +47,14 @@ properties:
interrupts:
maxItems: 1
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: ipg
+ - const: cnt
+
required:
- compatible
- reg
@@ -50,6 +62,21 @@ required:
additionalProperties: false
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: fsl,imx8dxl-db-pmu
+ then:
+ required:
+ - clocks
+ - clock-names
+ else:
+ properties:
+ clocks: false
+ clock-names: false
+
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
diff --git a/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml b/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml
index 6a47e08e0e97..f9cffbb2df07 100644
--- a/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/fsl,imx8mq-usb-phy.yaml
@@ -142,7 +142,9 @@ allOf:
required:
- orientation-switch
then:
- $ref: /schemas/usb/usb-switch.yaml#
+ allOf:
+ - $ref: /schemas/usb/usb-switch.yaml#
+ - $ref: /schemas/usb/usb-switch-ports.yaml#
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
index 0febd04a61f4..dd345cbd0a0b 100644
--- a/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/motorola,cpcap-usb-phy.yaml
@@ -67,8 +67,8 @@ properties:
mode-gpios:
description: Optional GPIOs for configuring alternate modes
items:
- - description: "mode selection GPIO #0"
- - description: "mode selection GPIO #1"
+ - description: mode selection GPIO#0
+ - description: mode selection GPIO#1
required:
- compatible
diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
index a58370a6a5d3..fba7b2549dde 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-ufs-phy.yaml
@@ -24,6 +24,10 @@ properties:
- enum:
- qcom,qcs8300-qmp-ufs-phy
- const: qcom,sa8775p-qmp-ufs-phy
+ - items:
+ - enum:
+ - qcom,kaanapali-qmp-ufs-phy
+ - const: qcom,sm8750-qmp-ufs-phy
- enum:
- qcom,msm8996-qmp-ufs-phy
- qcom,msm8998-qmp-ufs-phy
diff --git a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml
index a1b55168e050..863a1a446739 100644
--- a/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,sc8280xp-qmp-usb3-uni-phy.yaml
@@ -35,7 +35,6 @@ properties:
- qcom,sm8350-qmp-usb3-uni-phy
- qcom,x1e80100-qmp-usb3-uni-phy
-
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml b/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml
index e906403208c0..ea1135c91fb7 100644
--- a/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/samsung,usb3-drd-phy.yaml
@@ -125,7 +125,9 @@ allOf:
contains:
const: google,gs101-usb31drd-phy
then:
- $ref: /schemas/usb/usb-switch.yaml#
+ allOf:
+ - $ref: /schemas/usb/usb-switch.yaml#
+ - $ref: /schemas/usb/usb-switch-ports.yaml#
properties:
clocks:
diff --git a/Documentation/devicetree/bindings/pinctrl/brcm,bcm21664-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/brcm,bcm21664-pinctrl.yaml
index 1283a588416d..a2e609b066ee 100644
--- a/Documentation/devicetree/bindings/pinctrl/brcm,bcm21664-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/brcm,bcm21664-pinctrl.yaml
@@ -116,7 +116,6 @@ patternProperties:
input-schmitt-enable: false
input-schmitt-disable: false
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/pinctrl/fsl,imx9-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/fsl,imx9-pinctrl.yaml
index a438db8884f2..96e7b6995273 100644
--- a/Documentation/devicetree/bindings/pinctrl/fsl,imx9-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/fsl,imx9-pinctrl.yaml
@@ -58,7 +58,6 @@ patternProperties:
- description: |
"pad_setting" indicates the pad configuration value to be applied.
-
required:
- fsl,pins
diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,ap806-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/marvell,ap806-pinctrl.yaml
new file mode 100644
index 000000000000..00a7e358a8c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/marvell,ap806-pinctrl.yaml
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/marvell,ap806-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell AP806 pin controller
+
+maintainers:
+ - Gregory Clement <gregory.clement@bootlin.com>
+ - Miquel Raynal <miquel.raynal@bootlin.com>
+
+properties:
+ compatible:
+ const: marvell,ap806-pinctrl
+
+ reg:
+ maxItems: 1
+
+patternProperties:
+ '-pins$':
+ type: object
+ additionalProperties: false
+
+ properties:
+ marvell,function:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ Indicates the function to select.
+ enum: [ gpio, i2c0, sdio, spi0, uart0, uart1 ]
+
+ marvell,pins:
+ $ref: /schemas/types.yaml#/definitions/string-array
+ description:
+ Array of MPP pins to be used for the given function.
+ minItems: 1
+ maxItems: 20
+ items:
+ enum: [
+ mpp0, mpp1, mpp2, mpp3, mpp4, mpp5, mpp6, mpp7, mpp8, mpp9, mpp10,
+ mpp11, mpp12, mpp13, mpp14, mpp15, mpp16, mpp17, mpp18, mpp19
+ ]
+
+allOf:
+ - $ref: pinctrl.yaml#
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ pinctrl {
+ compatible = "marvell,ap806-pinctrl";
+
+ uart0_pins: uart0-pins {
+ marvell,pins = "mpp11", "mpp19";
+ marvell,function = "uart0";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,armada-7k-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/marvell,armada-7k-pinctrl.yaml
new file mode 100644
index 000000000000..88910ad170e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/marvell,armada-7k-pinctrl.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/marvell,armada-7k-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell Armada 7K/8K pin controller
+
+maintainers:
+ - Gregory Clement <gregory.clement@bootlin.com>
+ - Miquel Raynal <miquel.raynal@bootlin.com>
+
+properties:
+ compatible:
+ enum:
+ - marvell,armada-7k-pinctrl
+ - marvell,armada-8k-cpm-pinctrl
+ - marvell,armada-8k-cps-pinctrl
+ - marvell,cp115-standalone-pinctrl
+
+ reg:
+ maxItems: 1
+
+patternProperties:
+ '-pins(-.+)?$':
+ type: object
+ additionalProperties: false
+
+ properties:
+ marvell,function:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ Indicates the function to select.
+ enum: [
+ au, dev, ge, ge0, ge1, gpio, i2c0, i2c1, led, link, mii, mss_gpio0,
+ mss_gpio1, mss_gpio2, mss_gpio3, mss_gpio4, mss_gpio5, mss_gpio6,
+ mss_gpio7, mss_i2c, mss_spi, mss_uart, nf, pcie, pcie0, pcie1, pcie2,
+ ptp, rei, sata0, sata1, sdio, sdio_cd, sdio_wp, sei, spi0, spi1,
+ synce1, synce2, tdm, uart0, uart1, uart2, uart3, wakeup, xg
+ ]
+
+ marvell,pins:
+ $ref: /schemas/types.yaml#/definitions/string-array
+ description:
+ Array of MPP pins to be used for the given function.
+ minItems: 1
+ maxItems: 63
+ items:
+ pattern: '^mpp([1-5]?[0-9]|6[0-2])$'
+
+allOf:
+ - $ref: pinctrl.yaml#
+
+required:
+ - compatible
+
+additionalProperties: false
+
+examples:
+ - |
+ pinctrl {
+ compatible = "marvell,armada-7k-pinctrl";
+
+ nand_pins: nand-pins {
+ marvell,pins =
+ "mpp15", "mpp16", "mpp17", "mpp18",
+ "mpp19", "mpp20", "mpp21", "mpp22",
+ "mpp23", "mpp24", "mpp25", "mpp26",
+ "mpp27";
+ marvell,function = "dev";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/microchip,sparx5-sgpio.yaml b/Documentation/devicetree/bindings/pinctrl/microchip,sparx5-sgpio.yaml
index 0df4e114fdd6..fa47732d7cef 100644
--- a/Documentation/devicetree/bindings/pinctrl/microchip,sparx5-sgpio.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/microchip,sparx5-sgpio.yaml
@@ -18,7 +18,7 @@ description: |
properties:
$nodename:
- pattern: "^gpio@[0-9a-f]+$"
+ pattern: '^gpio@[0-9a-f]+$'
compatible:
enum:
@@ -26,10 +26,10 @@ properties:
- mscc,ocelot-sgpio
- mscc,luton-sgpio
- "#address-cells":
+ '#address-cells':
const: 1
- "#size-cells":
+ '#size-cells':
const: 0
reg:
@@ -76,7 +76,7 @@ properties:
- const: switch
patternProperties:
- "^gpio@[0-1]$":
+ '^gpio@[0-1]$':
type: object
properties:
compatible:
@@ -132,8 +132,8 @@ required:
- reg
- clocks
- microchip,sgpio-port-ranges
- - "#address-cells"
- - "#size-cells"
+ - '#address-cells'
+ - '#size-cells'
examples:
- |
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq5018-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5018-tlmm.yaml
index 23300606547c..96635b2f6a27 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,ipq5018-tlmm.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5018-tlmm.yaml
@@ -8,7 +8,7 @@ title: Qualcomm IPQ5018 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm IPQ5018 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq5332-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5332-tlmm.yaml
index e571cd64418f..22685c479983 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,ipq5332-tlmm.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq5332-tlmm.yaml
@@ -8,7 +8,7 @@ title: Qualcomm IPQ5332 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
Top Level Mode Multiplexer pin controller in Qualcomm IPQ5332 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq8074-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8074-pinctrl.yaml
index 6f90dbbdbdcc..40def3ac3bf7 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,ipq8074-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq8074-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm IPQ8074 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm IPQ8074 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,ipq9574-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,ipq9574-tlmm.yaml
index bca903b5da6d..7afec315b63e 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,ipq9574-tlmm.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,ipq9574-tlmm.yaml
@@ -8,7 +8,7 @@ title: Qualcomm Technologies, Inc. IPQ9574 TLMM block
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm IPQ9574 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml
index 3b5045730471..619341dd637c 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,lpass-lpi-common.yaml
@@ -9,7 +9,7 @@ title: Qualcomm SoC LPASS LPI TLMM Common Properties
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Common properties for the Top Level Mode Multiplexer pin controllers in the
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8660-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8660-pinctrl.yaml
index 61f5be21f30c..203ad69e99e8 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8660-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8660-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8660 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8660 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8916-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8916-pinctrl.yaml
index 904af87f9eaf..9bf098cf18ee 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8916-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8916-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8916 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8916 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.yaml
index 46618740bd31..435f0dc7a82e 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8960-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8960 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8960 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.yaml
index 840fdaabde12..a9aff442824c 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8974-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8974 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8974 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8976-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8976-pinctrl.yaml
index d4391c194ff7..501329bff905 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8976-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8976-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8976 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8976 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8994-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8994-pinctrl.yaml
index fa90981db40b..2ec10908d556 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8994-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8994-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8994 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8994 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.yaml
index c5010c175b23..496f38009c7d 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8996-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8996 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8996 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.yaml
index bcaa231adaf7..3b098a226a67 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8998-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM8998 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm MSM8998 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml
index 5e6dfcc3fe9b..6632bcd037ba 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-gpio.yaml
@@ -424,13 +424,13 @@ allOf:
patternProperties:
'-state$':
oneOf:
- - $ref: "#/$defs/qcom-pmic-gpio-state"
+ - $ref: '#/$defs/qcom-pmic-gpio-state'
- patternProperties:
- "(pinconf|-pins)$":
- $ref: "#/$defs/qcom-pmic-gpio-state"
+ '(pinconf|-pins)$':
+ $ref: '#/$defs/qcom-pmic-gpio-state'
additionalProperties: false
- "-hog(-[0-9]+)?$":
+ '-hog(-[0-9]+)?$':
type: object
required:
- gpio-hog
@@ -503,7 +503,7 @@ $defs:
- gpio1-gpio12 for pmxr2230
items:
- pattern: "^gpio([0-9]+)$"
+ pattern: '^gpio([0-9]+)$'
function:
items:
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml
index 9364ae05f3e6..daf4c1c03712 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,pmic-mpp.yaml
@@ -74,10 +74,10 @@ required:
patternProperties:
'-state$':
oneOf:
- - $ref: "#/$defs/qcom-pmic-mpp-state"
+ - $ref: '#/$defs/qcom-pmic-mpp-state'
- patternProperties:
'-pins$':
- $ref: "#/$defs/qcom-pmic-mpp-state"
+ $ref: '#/$defs/qcom-pmic-mpp-state'
additionalProperties: false
$defs:
@@ -100,7 +100,7 @@ $defs:
- mpp1-mpp4 for pma8084
items:
- pattern: "^mpp([0-9]+)$"
+ pattern: '^mpp([0-9]+)$'
function:
items:
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,qcs404-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,qcs404-pinctrl.yaml
index 4009501b3414..91b8dcec3f08 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,qcs404-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,qcs404-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm QCS404 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm QCS404 SoC.
@@ -142,7 +142,6 @@ examples:
interrupt-controller;
#interrupt-cells = <2>;
-
blsp1-i2c1-default-state {
pins = "gpio24", "gpio25";
function = "blsp_i2c1";
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sc7180-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sc7180-pinctrl.yaml
index 5606f2136ad1..ec0bf4fdfa4f 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sc7180-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sc7180-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SC7180 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm SC7180 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdm630-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sdm630-pinctrl.yaml
index a00cb43df144..80627a1ad663 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sdm630-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdm630-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SDM630 and SDM660 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm SDM630 and SDM660 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml
index 0f331844608c..4fcac2e55b55 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SDM845 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm SDM845 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm6115-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm6115-lpass-lpi-pinctrl.yaml
index f4cf2ce86fcd..d3e4926034a7 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sm6115-lpass-lpi-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm6115-lpass-lpi-pinctrl.yaml
@@ -66,7 +66,6 @@ $defs:
Specify the alternative function to be configured for the specified
pins.
-
allOf:
- $ref: qcom,lpass-lpi-common.yaml#
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm6125-tlmm.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm6125-tlmm.yaml
index ddeaeaa9a450..5a57a59cc1e5 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sm6125-tlmm.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm6125-tlmm.yaml
@@ -88,7 +88,6 @@ $defs:
uim2_present, uim2_reset, unused1, unused2, usb_phy, vfr_1, vsense_trigger,
wlan1_adc0, wlan1_adc1, wlan2_adc0, wlan2_adc1, wsa_clk, wsa_data ]
-
required:
- pins
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8150-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8150-pinctrl.yaml
index bdb7ed4be026..c4542e2d7108 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8150-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8150-pinctrl.yaml
@@ -8,7 +8,7 @@ title: Qualcomm SM8150 TLMM pin controller
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Top Level Mode Multiplexer pin controller in Qualcomm SM8150 SoC.
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8350-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8350-lpass-lpi-pinctrl.yaml
index 9d782f910b31..46aec0713775 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8350-lpass-lpi-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8350-lpass-lpi-pinctrl.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8350 SoC LPASS LPI TLMM
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description:
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8550-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8550-lpass-lpi-pinctrl.yaml
index bf4a72facae1..89821871c606 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8550-lpass-lpi-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8550-lpass-lpi-pinctrl.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8550 SoC LPASS LPI TLMM
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description:
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
index e90a5274647d..74df912e60ad 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sm8650-lpass-lpi-pinctrl.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SM8650 SoC LPASS LPI TLMM
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description:
diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml
index cfe004573366..075f3abdfbec 100644
--- a/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/renesas,pfc.yaml
@@ -129,7 +129,7 @@ additionalProperties:
- type: object
additionalProperties:
- $ref: "#/additionalProperties/anyOf/0"
+ $ref: '#/additionalProperties/anyOf/0'
examples:
- |
diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,rza1-ports.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,rza1-ports.yaml
index 2bd7d47d0fdb..8203c3c46cc7 100644
--- a/Documentation/devicetree/bindings/pinctrl/renesas,rza1-ports.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/renesas,rza1-ports.yaml
@@ -65,7 +65,6 @@ patternProperties:
- '#gpio-cells'
- gpio-ranges
-
additionalProperties:
anyOf:
- type: object
@@ -118,7 +117,7 @@ additionalProperties:
- type: object
additionalProperties:
- $ref: "#/additionalProperties/anyOf/0"
+ $ref: '#/additionalProperties/anyOf/0'
examples:
- |
@@ -150,7 +149,6 @@ examples:
pinmux = <RZA1_PINMUX(3, 0, 6)>, <RZA1_PINMUX(3, 2, 4)>;
};
-
/*
* I2c master: both SDA and SCL pins need bi-directional operations
* Pin #4 on port #1 is configured as alternate function #1.
@@ -162,7 +160,6 @@ examples:
pinmux = <RZA1_PINMUX(1, 4, 1)>, <RZA1_PINMUX(1, 5, 1)>;
};
-
/*
* Multi-function timer input and output compare pins.
*/
diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml
index 5156d54b240b..00c05243b9a4 100644
--- a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml
@@ -135,7 +135,7 @@ additionalProperties:
- type: object
additionalProperties:
- $ref: "#/additionalProperties/anyOf/0"
+ $ref: '#/additionalProperties/anyOf/0'
allOf:
- $ref: pinctrl.yaml#
diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml
index 5fa5d31f8866..88b2fa5e684d 100644
--- a/Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/renesas,rzv2m-pinctrl.yaml
@@ -88,7 +88,7 @@ additionalProperties:
- type: object
additionalProperties:
- $ref: "#/additionalProperties/anyOf/0"
+ $ref: '#/additionalProperties/anyOf/0'
allOf:
- $ref: pinctrl.yaml#
diff --git a/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml
index f3258f2fd3a4..3f14eab01c54 100644
--- a/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/starfive,jh7100-pinctrl.yaml
@@ -32,7 +32,6 @@ description: |
| | | | | | -------
UART0 UART1 --
-
The big MUX in the diagram only has 7 different ways of mapping peripherals
on the left to pins on the right. StarFive calls the 7 configurations "signal
groups".
diff --git a/Documentation/devicetree/bindings/pinctrl/toshiba,visconti-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/toshiba,visconti-pinctrl.yaml
index 19d47fd414bc..ce04d2eadec9 100644
--- a/Documentation/devicetree/bindings/pinctrl/toshiba,visconti-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/toshiba,visconti-pinctrl.yaml
@@ -50,18 +50,20 @@ patternProperties:
groups:
description:
Name of the pin group to use for the functions.
- $ref: /schemas/types.yaml#/definitions/string
- enum: [i2c0_grp, i2c1_grp, i2c2_grp, i2c3_grp, i2c4_grp,
- i2c5_grp, i2c6_grp, i2c7_grp, i2c8_grp,
- spi0_grp, spi0_cs0_grp, spi0_cs1_grp, spi0_cs2_grp,
- spi1_grp, spi2_grp, spi3_grp, spi4_grp, spi5_grp, spi6_grp,
- uart0_grp, uart1_grp, uart2_grp, uart3_grp,
- pwm0_gpio4_grp, pwm0_gpio8_grp, pwm0_gpio12_grp,
- pwm0_gpio16_grp, pwm1_gpio5_grp, pwm1_gpio9_grp,
- pwm1_gpio13_grp, pwm1_gpio17_grp, pwm2_gpio6_grp,
- pwm2_gpio10_grp, pwm2_gpio14_grp, pwm2_gpio18_grp,
- pwm3_gpio7_grp, pwm3_gpio11_grp, pwm3_gpio15_grp,
- pwm3_gpio19_grp, pcmif_out_grp, pcmif_in_grp]
+ items:
+ enum: [i2c0_grp, i2c1_grp, i2c2_grp, i2c3_grp, i2c4_grp,
+ i2c5_grp, i2c6_grp, i2c7_grp, i2c8_grp,
+ spi0_grp, spi0_cs0_grp, spi0_cs1_grp, spi0_cs2_grp,
+ spi1_grp, spi2_grp, spi3_grp, spi4_grp, spi5_grp, spi6_grp,
+ uart0_grp, uart1_grp, uart2_grp, uart3_grp,
+ pwm0_gpio4_grp, pwm0_gpio8_grp, pwm0_gpio12_grp,
+ pwm0_gpio16_grp, pwm1_gpio5_grp, pwm1_gpio9_grp,
+ pwm1_gpio13_grp, pwm1_gpio17_grp, pwm2_gpio6_grp,
+ pwm2_gpio10_grp, pwm2_gpio14_grp, pwm2_gpio18_grp,
+ pwm3_gpio7_grp, pwm3_gpio11_grp, pwm3_gpio15_grp,
+ pwm3_gpio19_grp, pcmif_out_grp, pcmif_in_grp]
+ minItems: 1
+ maxItems: 8
drive-strength:
enum: [2, 4, 6, 8, 16, 24, 32]
diff --git a/Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml
index 55ece6a8be5e..81e2164ea98f 100644
--- a/Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/xlnx,versal-pinctrl.yaml
@@ -74,6 +74,7 @@ patternProperties:
'^conf':
type: object
+ unevaluatedProperties: false
description:
Pinctrl node's client devices use subnodes for pin configurations,
which in turn use the standard properties below.
diff --git a/Documentation/devicetree/bindings/power/actions,owl-sps.txt b/Documentation/devicetree/bindings/power/actions,owl-sps.txt
deleted file mode 100644
index a3571937b019..000000000000
--- a/Documentation/devicetree/bindings/power/actions,owl-sps.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-Actions Semi Owl Smart Power System (SPS)
-
-Required properties:
-- compatible : "actions,s500-sps" for S500
- "actions,s700-sps" for S700
- "actions,s900-sps" for S900
-- reg : Offset and length of the register set for the device.
-- #power-domain-cells : Must be 1.
- See macros in:
- include/dt-bindings/power/owl-s500-powergate.h for S500
- include/dt-bindings/power/owl-s700-powergate.h for S700
- include/dt-bindings/power/owl-s900-powergate.h for S900
-
-
-Example:
-
- sps: power-controller@b01b0100 {
- compatible = "actions,s500-sps";
- reg = <0xb01b0100 0x100>;
- #power-domain-cells = <1>;
- };
diff --git a/Documentation/devicetree/bindings/power/actions,s500-sps.yaml b/Documentation/devicetree/bindings/power/actions,s500-sps.yaml
new file mode 100644
index 000000000000..bb942817b3db
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/actions,s500-sps.yaml
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/actions,s500-sps.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Actions Semi Owl Smart Power System (SPS)
+
+maintainers:
+ - Andreas Färber <afaerber@suse.de>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - actions,s500-sps
+ - actions,s700-sps
+ - actions,s900-sps
+
+ reg:
+ maxItems: 1
+
+ '#power-domain-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - '#power-domain-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ power-controller@b01b0100 {
+ compatible = "actions,s500-sps";
+ reg = <0xb01b0100 0x100>;
+ #power-domain-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/power/mediatek,mt8196-gpufreq.yaml b/Documentation/devicetree/bindings/power/mediatek,mt8196-gpufreq.yaml
new file mode 100644
index 000000000000..b9e43abaf8a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/mediatek,mt8196-gpufreq.yaml
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/mediatek,mt8196-gpufreq.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MFlexGraphics Power and Frequency Controller
+
+maintainers:
+ - Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
+
+description:
+ A special-purpose embedded MCU to control power and frequency of GPU devices
+ using MediaTek Flexible Graphics integration hardware.
+
+properties:
+ $nodename:
+ pattern: '^power-controller@[a-f0-9]+$'
+
+ compatible:
+ enum:
+ - mediatek,mt8196-gpufreq
+
+ reg:
+ items:
+ - description: GPR memory area
+ - description: RPC memory area
+ - description: SoC variant ID register
+
+ reg-names:
+ items:
+ - const: gpr
+ - const: rpc
+ - const: hw-revision
+
+ clocks:
+ items:
+ - description: main clock of the embedded controller (EB)
+ - description: core PLL
+ - description: stack 0 PLL
+ - description: stack 1 PLL
+
+ clock-names:
+ items:
+ - const: eb
+ - const: core
+ - const: stack0
+ - const: stack1
+
+ mboxes:
+ items:
+ - description: FastDVFS events
+ - description: frequency control
+ - description: sleep control
+ - description: timer control
+ - description: frequency hopping control
+ - description: hardware voter control
+ - description: FastDVFS control
+
+ mbox-names:
+ items:
+ - const: fast-dvfs-event
+ - const: gpufreq
+ - const: sleep
+ - const: timer
+ - const: fhctl
+ - const: ccf
+ - const: fast-dvfs
+
+ memory-region:
+ items:
+ - description: phandle to the GPUEB shared memory
+
+ "#clock-cells":
+ const: 1
+
+ "#power-domain-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - mboxes
+ - mbox-names
+ - memory-region
+ - "#clock-cells"
+ - "#power-domain-cells"
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/mediatek,mt8196-clock.h>
+
+ power-controller@4b09fd00 {
+ compatible = "mediatek,mt8196-gpufreq";
+ reg = <0x4b09fd00 0x80>,
+ <0x4b800000 0x1000>,
+ <0x4b860128 0x4>;
+ reg-names = "gpr", "rpc", "hw-revision";
+ clocks = <&topckgen CLK_TOP_MFG_EB>,
+ <&mfgpll CLK_MFG_AO_MFGPLL>,
+ <&mfgpll_sc0 CLK_MFGSC0_AO_MFGPLL_SC0>,
+ <&mfgpll_sc1 CLK_MFGSC1_AO_MFGPLL_SC1>;
+ clock-names = "eb", "core", "stack0", "stack1";
+ mboxes = <&gpueb_mbox 0>, <&gpueb_mbox 1>, <&gpueb_mbox 2>,
+ <&gpueb_mbox 3>, <&gpueb_mbox 4>, <&gpueb_mbox 5>,
+ <&gpueb_mbox 7>;
+ mbox-names = "fast-dvfs-event", "gpufreq", "sleep", "timer", "fhctl",
+ "ccf", "fast-dvfs";
+ memory-region = <&gpueb_shared_memory>;
+ #clock-cells = <1>;
+ #power-domain-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
index 500d98921581..f8a13928f615 100644
--- a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
+++ b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
@@ -33,6 +33,9 @@ properties:
- mediatek,mt8188-power-controller
- mediatek,mt8192-power-controller
- mediatek,mt8195-power-controller
+ - mediatek,mt8196-hwv-hfrp-power-controller
+ - mediatek,mt8196-hwv-scp-power-controller
+ - mediatek,mt8196-power-controller
- mediatek,mt8365-power-controller
'#power-domain-cells':
@@ -157,6 +160,7 @@ allOf:
contains:
enum:
- mediatek,mt8183-power-controller
+ - mediatek,mt8196-power-controller
then:
properties:
access-controllers:
diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
index af5fef872529..27af5b8aa134 100644
--- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
+++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml
@@ -18,6 +18,7 @@ properties:
oneOf:
- enum:
- qcom,glymur-rpmhpd
+ - qcom,kaanapali-rpmhpd
- qcom,mdm9607-rpmpd
- qcom,milos-rpmhpd
- qcom,msm8226-rpmpd
diff --git a/Documentation/devicetree/bindings/power/renesas,sysc-rmobile.yaml b/Documentation/devicetree/bindings/power/renesas,sysc-rmobile.yaml
index fba6914ec40d..948a9da111df 100644
--- a/Documentation/devicetree/bindings/power/renesas,sysc-rmobile.yaml
+++ b/Documentation/devicetree/bindings/power/renesas,sysc-rmobile.yaml
@@ -45,7 +45,7 @@ properties:
const: 0
additionalProperties:
- $ref: "#/$defs/pd-node"
+ $ref: '#/$defs/pd-node'
required:
- compatible
@@ -83,7 +83,7 @@ $defs:
- '#power-domain-cells'
additionalProperties:
- $ref: "#/$defs/pd-node"
+ $ref: '#/$defs/pd-node'
examples:
- |
diff --git a/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml b/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml
index a884e49c995f..b41db576f95d 100644
--- a/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml
+++ b/Documentation/devicetree/bindings/power/rockchip,power-controller.yaml
@@ -46,6 +46,7 @@ properties:
- rockchip,rk3576-power-controller
- rockchip,rk3588-power-controller
- rockchip,rv1126-power-controller
+ - rockchip,rv1126b-power-controller
"#power-domain-cells":
const: 1
@@ -126,6 +127,7 @@ $defs:
"include/dt-bindings/power/rk3568-power.h"
"include/dt-bindings/power/rk3588-power.h"
"include/dt-bindings/power/rockchip,rv1126-power.h"
+ "include/dt-bindings/power/rockchip,rv1126b-power-controller.h"
clocks:
minItems: 1
diff --git a/Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml b/Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml
index 4c74cc78729e..3e8689019251 100644
--- a/Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml
+++ b/Documentation/devicetree/bindings/power/supply/mt6360_charger.yaml
@@ -21,7 +21,6 @@ properties:
description: Maximum CHGIN regulation voltage in uV.
enum: [ 5500000, 6500000, 11000000, 14500000 ]
-
usb-otg-vbus-regulator:
type: object
description: OTG boost regulator.
diff --git a/Documentation/devicetree/bindings/power/supply/richtek,rt9756.yaml b/Documentation/devicetree/bindings/power/supply/richtek,rt9756.yaml
new file mode 100644
index 000000000000..a88bf6cd1927
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/richtek,rt9756.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/power/supply/richtek,rt9756.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Richtek RT9756 Smart Cap Divider Charger
+
+maintainers:
+ - ChiYuan Huang <cy_huang@richtek.com>
+
+description: |
+ The RT9756/RT9757 is a high efficiency and high charge current charger.
+
+ The efficiency is up to 98.2% when VBAT = 4V, IBAT = 2A in DIV2 mode and 99.1%
+ when VBAT=4V, IBAT=1A in bypass mode. The maximum charger current is up to 8A
+ in DIV2 mode and 5A in bypass mode. The device integrates smart cap divider
+ topology, direct charging mode, external over-voltage protection control, an
+ input reverse blocking NFET and 2-way regulation, a dual phase charge pump
+ core, 8-Channel high speed ADCs and USB BC 1.2 detection.
+
+ RT9770 is almost the same with RT9756/57, only BC 1.2 detection function is
+ removed to shrink the die size.
+
+allOf:
+ - $ref: power-supply.yaml#
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - richtek,rt9756
+ - richtek,rt9770
+ - items:
+ - enum:
+ - richtek,rt9757
+ - const: richtek,rt9756
+
+ reg:
+ maxItems: 1
+
+ wakeup-source: true
+
+ interrupts:
+ maxItems: 1
+
+ shunt-resistor-micro-ohms:
+ description: Battery current sense resistor mounted.
+ default: 2000
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ charger@6f {
+ compatible = "richtek,rt9756";
+ reg = <0x6f>;
+ wakeup-source;
+ interrupts-extended = <&gpio_intc 32 IRQ_TYPE_EDGE_FALLING>;
+ shunt-resistor-micro-ohms = <5000>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml
index 994fac12c8da..4f19744844e9 100644
--- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml
+++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml
@@ -65,7 +65,6 @@ properties:
- const: vbus_v
- const: usb_charger_c
-
required:
- compatible
- monitored-battery
diff --git a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
index 1b192e197b11..1197858e431f 100644
--- a/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
+++ b/Documentation/devicetree/bindings/pwm/allwinner,sun4i-a10-pwm.yaml
@@ -55,7 +55,6 @@ properties:
resets:
maxItems: 1
-
allOf:
- $ref: pwm.yaml#
diff --git a/Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml b/Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml
new file mode 100644
index 000000000000..855aec59ac53
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/thead,th1520-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: T-HEAD TH1520 PWM controller
+
+maintainers:
+ - Michal Wilczynski <m.wilczynski@samsung.com>
+
+allOf:
+ - $ref: pwm.yaml#
+
+properties:
+ compatible:
+ const: thead,th1520-pwm
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: SoC PWM clock
+
+ "#pwm-cells":
+ const: 3
+
+required:
+ - compatible
+ - reg
+ - clocks
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/thead,th1520-clk-ap.h>
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ pwm@ffec01c000 {
+ compatible = "thead,th1520-pwm";
+ reg = <0xff 0xec01c000 0x0 0x4000>;
+ clocks = <&clk CLK_PWM>;
+ #pwm-cells = <3>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/da9211.txt b/Documentation/devicetree/bindings/regulator/da9211.txt
deleted file mode 100644
index eb871447d508..000000000000
--- a/Documentation/devicetree/bindings/regulator/da9211.txt
+++ /dev/null
@@ -1,205 +0,0 @@
-* Dialog Semiconductor DA9211/DA9212/DA9213/DA9223/DA9214/DA9224/DA9215/DA9225
- Voltage Regulator
-
-Required properties:
-- compatible: "dlg,da9211" or "dlg,da9212" or "dlg,da9213" or "dlg,da9223"
- or "dlg,da9214" or "dlg,da9224" or "dlg,da9215" or "dlg,da9225"
-- reg: I2C slave address, usually 0x68.
-- interrupts: the interrupt outputs of the controller
-- regulators: A node that houses a sub-node for each regulator within the
- device. Each sub-node is identified using the node's name, with valid
- values listed below. The content of each sub-node is defined by the
- standard binding for regulators; see regulator.txt.
- BUCKA and BUCKB.
-
-Optional properties:
-- enable-gpios: platform gpio for control of BUCKA/BUCKB.
-- Any optional property defined in regulator.txt
- - regulator-initial-mode and regulator-allowed-modes may be specified using
- mode values from dt-bindings/regulator/dlg,da9211-regulator.h
-
-Example 1) DA9211
- pmic: da9211@68 {
- compatible = "dlg,da9211";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <2000000>;
- regulator-max-microamp = <5000000>;
- enable-gpios = <&gpio 27 0>;
- regulator-allowed-modes = <DA9211_BUCK_MODE_SYNC
- DA9211_BUCK_MODE_AUTO>;
- };
- };
- };
-
-Example 2) DA9212
- pmic: da9212@68 {
- compatible = "dlg,da9212";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <2000000>;
- regulator-max-microamp = <5000000>;
- enable-gpios = <&gpio 27 0>;
- };
- BUCKB {
- regulator-name = "VBUCKB";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <2000000>;
- regulator-max-microamp = <5000000>;
- enable-gpios = <&gpio 17 0>;
- };
- };
- };
-
-Example 3) DA9213
- pmic: da9213@68 {
- compatible = "dlg,da9213";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <3000000>;
- regulator-max-microamp = <6000000>;
- enable-gpios = <&gpio 27 0>;
- };
- };
- };
-
-Example 4) DA9223
- pmic: da9223@68 {
- compatible = "dlg,da9223";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <3000000>;
- regulator-max-microamp = <6000000>;
- enable-gpios = <&gpio 27 0>;
- };
- };
- };
-
-Example 5) DA9214
- pmic: da9214@68 {
- compatible = "dlg,da9214";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <3000000>;
- regulator-max-microamp = <6000000>;
- enable-gpios = <&gpio 27 0>;
- };
- BUCKB {
- regulator-name = "VBUCKB";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <3000000>;
- regulator-max-microamp = <6000000>;
- enable-gpios = <&gpio 17 0>;
- };
- };
- };
-
-Example 6) DA9224
- pmic: da9224@68 {
- compatible = "dlg,da9224";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <3000000>;
- regulator-max-microamp = <6000000>;
- enable-gpios = <&gpio 27 0>;
- };
- BUCKB {
- regulator-name = "VBUCKB";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <3000000>;
- regulator-max-microamp = <6000000>;
- enable-gpios = <&gpio 17 0>;
- };
- };
- };
-
-Example 7) DA9215
- pmic: da9215@68 {
- compatible = "dlg,da9215";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <4000000>;
- regulator-max-microamp = <7000000>;
- enable-gpios = <&gpio 27 0>;
- };
- BUCKB {
- regulator-name = "VBUCKB";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <4000000>;
- regulator-max-microamp = <7000000>;
- enable-gpios = <&gpio 17 0>;
- };
- };
- };
-
-Example 8) DA9225
- pmic: da9225@68 {
- compatible = "dlg,da9225";
- reg = <0x68>;
- interrupts = <3 27>;
-
- regulators {
- BUCKA {
- regulator-name = "VBUCKA";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <4000000>;
- regulator-max-microamp = <7000000>;
- enable-gpios = <&gpio 27 0>;
- };
- BUCKB {
- regulator-name = "VBUCKB";
- regulator-min-microvolt = < 300000>;
- regulator-max-microvolt = <1570000>;
- regulator-min-microamp = <4000000>;
- regulator-max-microamp = <7000000>;
- enable-gpios = <&gpio 17 0>;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/regulator/dlg,da9211.yaml b/Documentation/devicetree/bindings/regulator/dlg,da9211.yaml
new file mode 100644
index 000000000000..4d7e495a6f59
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/dlg,da9211.yaml
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/dlg,da9211.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title:
+ Dialog Semiconductor DA9211-9215, DA9223-9225 Voltage Regulators
+
+maintainers:
+ - Ariel D'Alessandro <ariel.dalessandro@collabora.com>
+
+properties:
+ compatible:
+ enum:
+ - dlg,da9211
+ - dlg,da9212
+ - dlg,da9213
+ - dlg,da9214
+ - dlg,da9215
+ - dlg,da9223
+ - dlg,da9224
+ - dlg,da9225
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ regulators:
+ type: object
+ additionalProperties: false
+ description:
+ List of regulators provided by the device
+
+ patternProperties:
+ "^BUCK([AB])$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for a single BUCK regulator
+
+ properties:
+ regulator-initial-mode:
+ items:
+ enum: [ 1, 2, 3 ]
+ description:
+ Defined in include/dt-bindings/regulator/dlg,da9211-regulator.h
+
+ regulator-allowed-modes:
+ items:
+ enum: [ 1, 2, 3 ]
+ description:
+ Defined in include/dt-bindings/regulator/dlg,da9211-regulator.h
+
+ enable-gpios:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - regulators
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/regulator/dlg,da9211-regulator.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ regulator@68 {
+ compatible = "dlg,da9212";
+ reg = <0x68>;
+ interrupts = <3 27>;
+
+ regulators {
+ BUCKA {
+ regulator-name = "VBUCKA";
+ regulator-min-microvolt = < 300000>;
+ regulator-max-microvolt = <1570000>;
+ regulator-min-microamp = <2000000>;
+ regulator-max-microamp = <5000000>;
+ enable-gpios = <&gpio 27 0>;
+ };
+ BUCKB {
+ regulator-name = "VBUCKB";
+ regulator-min-microvolt = < 300000>;
+ regulator-max-microvolt = <1570000>;
+ regulator-min-microamp = <2000000>;
+ regulator-max-microamp = <5000000>;
+ enable-gpios = <&gpio 17 0>;
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/regulator/fitipower,fp9931.yaml b/Documentation/devicetree/bindings/regulator/fitipower,fp9931.yaml
new file mode 100644
index 000000000000..c6585e3bacbe
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/fitipower,fp9931.yaml
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/fitipower,fp9931.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: FitiPower FP9931/JD9930 Power Management Integrated Circuit
+
+maintainers:
+ - Andreas Kemnade <andreas@kemnade.info>
+
+description:
+ FP9931 is a Power Management IC to provide Power for EPDs with one 3.3V
+ switch, 2 symmetric LDOs behind 2 DC/DC converters, and one unsymmetric
+ regulator for a compensation voltage.
+ JD9930 has in addition some kind of night mode.
+
+properties:
+ compatible:
+ oneOf:
+ - const: fitipower,fp9931
+
+ - items:
+ - const: fitipower,jd9930
+ - const: fitipower,fp9931
+
+ reg:
+ maxItems: 1
+
+ enable-gpios:
+ maxItems: 1
+
+ pg-gpios:
+ maxItems: 1
+
+ en-ts-gpios:
+ maxItems: 1
+
+ xon-gpios:
+ maxItems: 1
+
+ vin-supply:
+ description:
+ Supply for the whole chip. Some vendor kernels and devicetrees
+ declare this as a non-existing GPIO named "pwrall".
+
+ fitipower,tdly-ms:
+ description:
+ Power up soft start delay settings tDLY1-4 bitfields in the
+ POWERON_DELAY register
+ items:
+ - enum: [0, 1, 2, 4]
+ - enum: [0, 1, 2, 4]
+ - enum: [0, 1, 2, 4]
+ - enum: [0, 1, 2, 4]
+
+ regulators:
+ type: object
+ additionalProperties: false
+ patternProperties:
+ "^(vcom|vposneg|v3p3)$":
+ unevaluatedProperties: false
+ type: object
+ $ref: /schemas/regulator/regulator.yaml
+
+required:
+ - compatible
+ - reg
+ - pg-gpios
+ - enable-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pmic@18 {
+ compatible = "fitipower,fp9931";
+ reg = <0x18>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fp9931_gpio>;
+ vin-supply = <&epd_pmic_supply>;
+ pg-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>;
+ en-ts-gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>;
+ fitipower,tdly-ms = <2 2 4 4>;
+
+ regulators {
+ vcom {
+ regulator-name = "vcom";
+ regulator-min-microvolt = <2352840>;
+ regulator-max-microvolt = <2352840>;
+ };
+
+ vposneg {
+ regulator-name = "vposneg";
+ regulator-min-microvolt = <15060000>;
+ regulator-max-microvolt = <15060000>;
+ };
+
+ v3p3 {
+ regulator-name = "v3p3";
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6316b-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6316b-regulator.yaml
new file mode 100644
index 000000000000..ea595935f4c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6316b-regulator.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/mediatek,mt6316b-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT6316 BP/VP SPMI PMIC Regulators
+
+maintainers:
+ - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+ The MediaTek MT6316BP/VP PMICs are fully controlled by SPMI interface, both
+ feature four step-down DC/DC (buck) converters, and provides 2+2 Phases,
+ joining Buck 1+2 for the first phase, and Buck 3+4 for the second phase.
+
+properties:
+ compatible:
+ const: mediatek,mt6316b-regulator
+
+ reg:
+ maxItems: 1
+
+patternProperties:
+ "^vbuck(12|34)$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ properties:
+ regulator-allowed-modes:
+ description: |
+ Allowed Buck regulator operating modes allowed. Valid values below.
+ 0 - Normal mode with automatic power saving, reducing the switching
+ frequency when light load conditions are detected
+ 1 - Forced Continuous Conduction mode (FCCM) for improved voltage
+ regulation accuracy with constant switching frequency but lower
+ regulator efficiency
+ 2 - Forced Low Power mode for improved regulator efficiency, used
+ when no heavy load is expected, will shut down unnecessary IP
+ blocks and secondary phases to reduce quiescent current.
+ This mode does not limit the maximum output current but unless
+ only a light load is applied, there will be regulation accuracy
+ and efficiency losses.
+ minItems: 1
+ maxItems: 3
+ items:
+ enum: [ 0, 1, 2 ]
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/spmi/spmi.h>
+
+ spmi {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pmic@8 {
+ compatible = "mediatek,mt6316b-regulator";
+ reg = <0x8 SPMI_USID>;
+
+ vbuck12 {
+ regulator-name = "dvdd_core";
+ regulator-min-microvolt = <450000>;
+ regulator-max-microvolt = <965000>;
+ regulator-allowed-modes = <0 1 2>;
+ regulator-enable-ramp-delay = <256>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6316c-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6316c-regulator.yaml
new file mode 100644
index 000000000000..186dcd3f11ed
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6316c-regulator.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/mediatek,mt6316c-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT6316 CP/HP/KP SPMI PMIC Regulators
+
+maintainers:
+ - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+ The MediaTek MT6316CP/HP/KP PMICs are fully controlled by SPMI interface,
+ features four step-down DC/DC (buck) converters, and provides 3+1 Phases,
+ joining Buck 1+2+4 for the first phase, and uses Buck 3 for the second.
+
+properties:
+ compatible:
+ const: mediatek,mt6316c-regulator
+
+ reg:
+ maxItems: 1
+
+patternProperties:
+ "^vbuck(124|3)$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ properties:
+ regulator-allowed-modes:
+ description: |
+ Allowed Buck regulator operating modes allowed. Valid values below.
+ 0 - Normal mode with automatic power saving, reducing the switching
+ frequency when light load conditions are detected
+ 1 - Forced Continuous Conduction mode (FCCM) for improved voltage
+ regulation accuracy with constant switching frequency but lower
+ regulator efficiency
+ 2 - Forced Low Power mode for improved regulator efficiency, used
+ when no heavy load is expected, will shut down unnecessary IP
+ blocks and secondary phases to reduce quiescent current.
+ This mode does not limit the maximum output current but unless
+ only a light load is applied, there will be regulation accuracy
+ and efficiency losses.
+ minItems: 1
+ maxItems: 3
+ items:
+ enum: [ 0, 1, 2 ]
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/spmi/spmi.h>
+
+ spmi {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pmic@6 {
+ compatible = "mediatek,mt6316c-regulator";
+ reg = <0x6 SPMI_USID>;
+
+ vbuck124 {
+ regulator-name = "dvdd_proc_m";
+ regulator-min-microvolt = <450000>;
+ regulator-max-microvolt = <1277500>;
+ regulator-allowed-modes = <0 1 2>;
+ regulator-enable-ramp-delay = <256>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6316d-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6316d-regulator.yaml
new file mode 100644
index 000000000000..aa9e9ef3b52d
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6316d-regulator.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/mediatek,mt6316d-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT6316 DP/TP SPMI PMIC Regulators
+
+maintainers:
+ - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+ The MediaTek MT6316DP/TP PMICs are fully controlled by SPMI interface, both
+ feature four step-down DC/DC (buck) converters, and provides a single Phase,
+ joining Buck 1+2+3+4.
+
+properties:
+ compatible:
+ const: mediatek,mt6316d-regulator
+
+ reg:
+ maxItems: 1
+
+ vbuck1234:
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ properties:
+ regulator-allowed-modes:
+ description: |
+ Allowed Buck regulator operating modes allowed. Valid values below.
+ 0 - Normal mode with automatic power saving, reducing the switching
+ frequency when light load conditions are detected
+ 1 - Forced Continuous Conduction mode (FCCM) for improved voltage
+ regulation accuracy with constant switching frequency but lower
+ regulator efficiency
+ 2 - Forced Low Power mode for improved regulator efficiency, used
+ when no heavy load is expected, will shut down unnecessary IP
+ blocks and secondary phases to reduce quiescent current.
+ This mode does not limit the maximum output current but unless
+ only a light load is applied, there will be regulation accuracy
+ and efficiency losses.
+ minItems: 1
+ maxItems: 3
+ items:
+ enum: [ 0, 1, 2 ]
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/spmi/spmi.h>
+
+ spmi {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ pmic@7 {
+ compatible = "mediatek,mt6316d-regulator";
+ reg = <0x7 SPMI_USID>;
+
+ vbuck1234 {
+ regulator-name = "dvdd_gpustack";
+ regulator-min-microvolt = <400000>;
+ regulator-max-microvolt = <1277500>;
+ regulator-allowed-modes = <0 1 2>;
+ regulator-enable-ramp-delay = <256>;
+ };
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6363-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6363-regulator.yaml
new file mode 100644
index 000000000000..4f79d4f81d49
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6363-regulator.yaml
@@ -0,0 +1,146 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/mediatek,mt6363-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT6363 PMIC Regulators
+
+maintainers:
+ - AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+
+description:
+ The MT6363 SPMI PMIC provides 10 BUCK and 25 LDO (Low DropOut) regulators
+ and can optionally provide overcurrent warnings with one ocp interrupt
+ for each voltage regulator.
+
+properties:
+ compatible:
+ const: mediatek,mt6363-regulator
+
+ reg:
+ maxItems: 1
+
+ vsys-vbuck1-supply:
+ description: Input supply for vbuck1
+
+ vsys-vbuck2-supply:
+ description: Input supply for vbuck2
+
+ vsys-vbuck3-supply:
+ description: Input supply for vbuck3
+
+ vsys-vbuck4-supply:
+ description: Input supply for vbuck4
+
+ vsys-vbuck5-supply:
+ description: Input supply for vbuck5
+
+ vsys-vbuck6-supply:
+ description: Input supply for vbuck6
+
+ vsys-vbuck7-supply:
+ description: Input supply for vbuck7
+
+ vsys-vs1-supply:
+ description: Input supply for vs1
+
+ vsys-vs2-supply:
+ description: Input supply for vs2
+
+ vsys-vs3-supply:
+ description: Input supply for vs3
+
+ vs1-ldo1-supply:
+ description: Input supply for va15, vio0p75, vm18, vrf18, vrf-io18
+
+ vs1-ldo2-supply:
+ description: Input supply for vcn15, vio18, vufs18
+
+ vs2-ldo1-supply:
+ description: Input supply for vsram-cpub, vsram-cpum, vrf12, vrf13, vufs12
+
+ vs2-ldo2-supply:
+ description: Input supply for va12-1, va12-2, vcn13, vsram-cpul
+
+ vs3-ldo1-supply:
+ description: Input supply for vsram-apu, vsram-digrf, vsram-mdfe
+
+ vs3-ldo2-supply:
+ description: Input supply for vsram-modem, vrf0p9
+
+ vsys-ldo1-supply:
+ description: Input supply for vaux18, vemc, vtref18
+
+patternProperties:
+ "^v(buck[1-7]|s[1-3])$":
+ description: Buck regulators
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ properties:
+ regulator-allowed-modes:
+ description: |
+ Allowed Buck regulator operating modes allowed. Valid values below.
+ 0 - Normal mode with automatic power saving, reducing the switching
+ frequency when light load conditions are detected
+ 1 - Forced Continuous Conduction mode (FCCM) for improved voltage
+ regulation accuracy with constant switching frequency but lower
+ regulator efficiency
+ 2 - Forced Low Power mode for improved regulator efficiency, used
+ when no heavy load is expected, does not limit the maximum out
+ current but unless only a light load is applied, there will be
+ regulation accuracy and efficiency losses.
+ 3 - Forced Ultra Low Power mode for ultra low load, this greatly
+ reduces the maximum output power, makes the regulator to be
+ efficient only for ultra light load, and greatly reduces the
+ quiescent current (Iq) of the buck.
+ maxItems: 3
+ items:
+ enum: [ 0, 1, 2, 3 ]
+
+ "^va(12-1|12-2|15)$":
+ $ref: "#/$defs/ldo-common"
+
+ "^v(aux|m|rf-io|tref)18$":
+ $ref: "#/$defs/ldo-common"
+
+ "^v(cn13|cn15|emc)$":
+ $ref: "#/$defs/ldo-common"
+
+ "^vio(0p75|18)$":
+ $ref: "#/$defs/ldo-common"
+
+ "^vrf(0p9|12|13|18)$":
+ $ref: "#/$defs/ldo-common"
+
+ "^vsram-(apu|cpub|cpum|cpul|digrf|mdfe|modem)$":
+ $ref: "#/$defs/ldo-common"
+
+ "^vufs(12|18)$":
+ $ref: "#/$defs/ldo-common"
+
+$defs:
+ ldo-common:
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ properties:
+ regulator-allowed-modes:
+ description: |
+ Allowed LDO regulator operating modes allowed. Valid values below.
+ 0 - Normal mode with automatic power saving, reducing the switching
+ frequency when light load conditions are detected
+ 2 - Forced Low Power mode for improved regulator efficiency, used
+ when no heavy load is expected, does not limit the maximum out
+ current but unless only a light load is applied, there will be
+ regulation accuracy and efficiency losses.
+ maxItems: 2
+ items:
+ enum: [ 0, 2 ]
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
diff --git a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml
index a5486c36830f..ec04adfb9d1c 100644
--- a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml
@@ -41,6 +41,21 @@ properties:
interrupts:
maxItems: 1
+ inl1-supply:
+ description: Regulator supply for the INL1 pin group, powering LDOx
+
+ inb13-supply:
+ description:
+ Regulator supply for the INB13 pin group, powering BUCK1 and BUCK3.
+
+ inb26-supply:
+ description:
+ Regulator supply for the INB26 pin group, powering BUCK2 and BUCK6.
+
+ inb45-supply:
+ description:
+ Regulator supply for the INB45 pin group, powering BUCK4 and BUCK5.
+
regulators:
type: object
description: |
@@ -124,6 +139,30 @@ properties:
When WDOG_B signal is asserted a warm reset will be done instead of cold
reset.
+ nxp,pmic-on-req-on-debounce-us:
+ enum: [ 120, 20000, 100000, 750000 ]
+ description: Debounce time for PMIC_ON_REQ high.
+
+ nxp,pmic-on-req-off-debounce-us:
+ enum: [ 120, 2000 ]
+ description: Debounce time for PMIC_ON_REQ is asserted low
+
+ nxp,power-on-step-ms:
+ enum: [ 1, 2, 4, 8]
+ description: Time step configuration during power on sequence
+
+ nxp,power-down-step-ms:
+ enum: [ 2, 4, 8, 16 ]
+ description: Time step configuration during power down sequence
+
+ nxp,restart-ms:
+ enum: [ 250, 500 ]
+ description: Time to stay off regulators during Cold reset
+
+ npx,pmic-rst-b-debounce-ms:
+ enum: [ 10, 50, 100, 500, 1000, 2000, 4000, 8000 ]
+ description: PMIC_RST_B debounce time
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml
index 4c5b0629aa3e..58bb0ad5dda4 100644
--- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml
@@ -8,7 +8,7 @@ title: Qualcomm Technologies, Inc. RPMh Regulators
maintainers:
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
rpmh-regulator devices support PMIC regulator management via the Voltage
@@ -51,10 +51,15 @@ description: |
For PM8450, smps1 - smps6, ldo1 - ldo4
For PM8550, smps1 - smps6, ldo1 - ldo17, bob1 - bob2
For PM8998, smps1 - smps13, ldo1 - ldo28, lvs1 - lvs2
+ For PMH0101, ldo1 - ldo18, bob1 - bob2
+ For PMH0104, smps1 - smps4
+ For PMH0110, smps1 - smps10, ldo1 - ldo4
For PMI8998, bob
For PMC8380, smps1 - smps8, ldo1 - lodo3
+ For PMCX0102, smps1 - smps10, ldo1 - ldo4
For PMR735A, smps1 - smps3, ldo1 - ldo7
For PMR735B, ldo1 - ldo12
+ For PMR735D, ldo1 - ldo7
For PMX55, smps1 - smps7, ldo1 - ldo16
For PMX65, smps1 - smps8, ldo1 - ldo21
For PMX75, smps1 - smps10, ldo1 - ldo21
@@ -85,12 +90,17 @@ properties:
- qcom,pmc8180-rpmh-regulators
- qcom,pmc8180c-rpmh-regulators
- qcom,pmc8380-rpmh-regulators
+ - qcom,pmcx0102-rpmh-regulators
- qcom,pmg1110-rpmh-regulators
+ - qcom,pmh0101-rpmh-regulators
+ - qcom,pmh0104-rpmh-regulators
+ - qcom,pmh0110-rpmh-regulators
- qcom,pmi8998-rpmh-regulators
- qcom,pmm8155au-rpmh-regulators
- qcom,pmm8654au-rpmh-regulators
- qcom,pmr735a-rpmh-regulators
- qcom,pmr735b-rpmh-regulators
+ - qcom,pmr735d-rpmh-regulators
- qcom,pmx55-rpmh-regulators
- qcom,pmx65-rpmh-regulators
- qcom,pmx75-rpmh-regulators
@@ -100,7 +110,7 @@ properties:
RPMh resource name suffix used for the regulators found
on this PMIC.
$ref: /schemas/types.yaml#/definitions/string
- enum: [a, b, c, d, e, f, g, h, i, j, k, l, m, n]
+ pattern: "^[a-n]|[A-N]_E[0-3]+$"
qcom,always-wait-for-ack:
description: |
@@ -246,6 +256,7 @@ allOf:
compatible:
enum:
- qcom,pm8005-rpmh-regulators
+ - qcom,pmh0104-rpmh-regulators
then:
patternProperties:
"^vdd-s[1-4]-supply$": true
@@ -426,6 +437,34 @@ allOf:
properties:
compatible:
enum:
+ - qcom,pmh0101-rpmh-regulators
+ then:
+ properties:
+ vdd-l1-l4-l10-supply: true
+ vdd-l2-l13-l14-supply: true
+ vdd-l3-l11-supply: true
+ vdd-l5-l16-supply: true
+ vdd-l6-l7-supply: true
+ vdd-l8-l9-supply: true
+ patternProperties:
+ "^vdd-l(1[2578])-supply$": true
+ "^vdd-bob[1-2]-supply$": true
+
+ - if:
+ properties:
+ compatible:
+ enum:
+ - qcom,pmcx0102-rpmh-regulators
+ - qcom,pmh0110-rpmh-regulators
+ then:
+ patternProperties:
+ "^vdd-l[1-4]-supply$": true
+ "^vdd-s([1-9]|10)-supply$": true
+
+ - if:
+ properties:
+ compatible:
+ enum:
- qcom,pmi8998-rpmh-regulators
then:
properties:
@@ -463,6 +502,18 @@ allOf:
properties:
compatible:
enum:
+ - qcom,pmr735d-rpmh-regulators
+ then:
+ properties:
+ vdd-l1-l2-l5-supply: true
+ vdd-l3-l4-supply: true
+ patternProperties:
+ "^vdd-l[6-7]-supply$": true
+
+ - if:
+ properties:
+ compatible:
+ enum:
- qcom,pmx55-rpmh-regulators
then:
properties:
diff --git a/Documentation/devicetree/bindings/regulator/richtek,rt6245-regulator.yaml b/Documentation/devicetree/bindings/regulator/richtek,rt6245-regulator.yaml
index b73762e151bb..84546fec3b18 100644
--- a/Documentation/devicetree/bindings/regulator/richtek,rt6245-regulator.yaml
+++ b/Documentation/devicetree/bindings/regulator/richtek,rt6245-regulator.yaml
@@ -55,7 +55,6 @@ properties:
delay time 0us, 10us, 20us, 40us. If this property is missing then keep
in chip default.
-
richtek,switch-freq-select:
$ref: /schemas/types.yaml#/definitions/uint8
enum: [0, 1, 2]
diff --git a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml
index a492f74a8608..a927551356e6 100644
--- a/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml
+++ b/Documentation/devicetree/bindings/remoteproc/ti,k3-r5f-rproc.yaml
@@ -79,7 +79,6 @@ properties:
It should be set as 3 (Single-Core mode) which is also the default if
omitted.
-
# R5F Processor Child Nodes:
# ==========================
@@ -167,7 +166,6 @@ patternProperties:
- description: region reserved for firmware image sections
additionalItems: true
-
# Optional properties:
# --------------------
# The following properties are optional properties for each of the R5F cores:
diff --git a/Documentation/devicetree/bindings/reset/ti,sci-reset.yaml b/Documentation/devicetree/bindings/reset/ti,sci-reset.yaml
index 1db08ce9ae27..68640abacd95 100644
--- a/Documentation/devicetree/bindings/reset/ti,sci-reset.yaml
+++ b/Documentation/devicetree/bindings/reset/ti,sci-reset.yaml
@@ -40,7 +40,6 @@ properties:
Please see https://software-dl.ti.com/tisci/esd/latest/index.html for
protocol documentation for the values to be used for different devices.
-
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/rng/inside-secure,safexcel-eip76.yaml b/Documentation/devicetree/bindings/rng/inside-secure,safexcel-eip76.yaml
index 0877eb44f9ed..f501fc7691c6 100644
--- a/Documentation/devicetree/bindings/rng/inside-secure,safexcel-eip76.yaml
+++ b/Documentation/devicetree/bindings/rng/inside-secure,safexcel-eip76.yaml
@@ -44,7 +44,6 @@ properties:
- const: core
- const: reg
-
allOf:
- if:
properties:
@@ -58,7 +57,6 @@ allOf:
required:
- interrupts
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/rng/microchip,pic32-rng.txt b/Documentation/devicetree/bindings/rng/microchip,pic32-rng.txt
deleted file mode 100644
index c6d1003befb7..000000000000
--- a/Documentation/devicetree/bindings/rng/microchip,pic32-rng.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-* Microchip PIC32 Random Number Generator
-
-The PIC32 RNG provides a pseudo random number generator which can be seeded by
-another true random number generator.
-
-Required properties:
-- compatible : should be "microchip,pic32mzda-rng"
-- reg : Specifies base physical address and size of the registers.
-- clocks: clock phandle.
-
-Example:
-
- rng: rng@1f8e6000 {
- compatible = "microchip,pic32mzda-rng";
- reg = <0x1f8e6000 0x1000>;
- clocks = <&PBCLK5>;
- };
diff --git a/Documentation/devicetree/bindings/rng/microchip,pic32-rng.yaml b/Documentation/devicetree/bindings/rng/microchip,pic32-rng.yaml
new file mode 100644
index 000000000000..1f6f6fb81ddc
--- /dev/null
+++ b/Documentation/devicetree/bindings/rng/microchip,pic32-rng.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/rng/microchip,pic32-rng.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip PIC32 Random Number Generator
+
+description: |
+ The PIC32 RNG provides a pseudo random number generator which can be seeded
+ by another true random number generator.
+
+maintainers:
+ - Joshua Henderson <joshua.henderson@microchip.com>
+
+properties:
+ compatible:
+ enum:
+ - microchip,pic32mzda-rng
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ rng: rng@1f8e6000 {
+ compatible = "microchip,pic32mzda-rng";
+ reg = <0x1f8e6000 0x1000>;
+ clocks = <&PBCLK5>;
+ };
diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml b/Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml
index ea6abfe2d95e..bc2e48754805 100644
--- a/Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml
+++ b/Documentation/devicetree/bindings/serial/qcom,msm-uart.yaml
@@ -8,7 +8,7 @@ title: Qualcomm MSM SoC Serial UART
maintainers:
- Bjorn Andersson <andersson@kernel.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The MSM serial UART hardware is designed for low-speed use cases where a
diff --git a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.yaml b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.yaml
index e0fa363ad7e2..788ef5c1c446 100644
--- a/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.yaml
+++ b/Documentation/devicetree/bindings/serial/qcom,msm-uartdm.yaml
@@ -9,7 +9,7 @@ title: Qualcomm MSM Serial UARTDM
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
The MSM serial UARTDM hardware is designed for high-speed use cases where the
diff --git a/Documentation/devicetree/bindings/serial/renesas,scif.yaml b/Documentation/devicetree/bindings/serial/renesas,scif.yaml
index e925cd4c3ac8..72483bc3274d 100644
--- a/Documentation/devicetree/bindings/serial/renesas,scif.yaml
+++ b/Documentation/devicetree/bindings/serial/renesas,scif.yaml
@@ -197,6 +197,7 @@ allOf:
- renesas,rcar-gen2-scif
- renesas,rcar-gen3-scif
- renesas,rcar-gen4-scif
+ - renesas,rcar-gen5-scif
then:
properties:
interrupts:
diff --git a/Documentation/devicetree/bindings/slimbus/qcom,slim-ngd.yaml b/Documentation/devicetree/bindings/slimbus/qcom,slim-ngd.yaml
index abf61c15246e..27a92b79c724 100644
--- a/Documentation/devicetree/bindings/slimbus/qcom,slim-ngd.yaml
+++ b/Documentation/devicetree/bindings/slimbus/qcom,slim-ngd.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SoC SLIMBus Non Generic Device (NGD) Controller
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description:
diff --git a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml
index e28ef198a801..039c8e4a4c51 100644
--- a/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml
+++ b/Documentation/devicetree/bindings/soc/bcm/brcm,bcm2835-pm.yaml
@@ -13,23 +13,21 @@ description: |
maintainers:
- Nicolas Saenz Julienne <nsaenz@kernel.org>
-allOf:
- - $ref: /schemas/watchdog/watchdog.yaml#
-
properties:
compatible:
items:
- enum:
- brcm,bcm2835-pm
- brcm,bcm2711-pm
+ - brcm,bcm2712-pm
- const: brcm,bcm2835-pm-wdt
reg:
- minItems: 2
+ minItems: 1
maxItems: 3
reg-names:
- minItems: 2
+ minItems: 1
items:
- const: pm
- const: asb
@@ -62,7 +60,35 @@ required:
- reg
- "#power-domain-cells"
- "#reset-cells"
- - clocks
+
+allOf:
+ - $ref: /schemas/watchdog/watchdog.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - brcm,bcm2835-pm
+ - brcm,bcm2711-pm
+ then:
+ required:
+ - clocks
+
+ properties:
+ reg:
+ minItems: 2
+
+ reg-names:
+ minItems: 2
+
+ else:
+ properties:
+ reg:
+ maxItems: 1
+
+ reg-names:
+ maxItems: 1
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-muram.yaml b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-muram.yaml
index cf0f38dbbe0d..2c06d869fdb5 100644
--- a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-muram.yaml
+++ b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/fsl,qe-muram.yaml
@@ -30,7 +30,6 @@ properties:
$ref: /schemas/types.yaml#/definitions/string
enum: [host, slave]
-
patternProperties:
'^data\-only@[a-f0-9]+$':
type: object
diff --git a/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml
index a10326a9683d..5267cfe92572 100644
--- a/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml
+++ b/Documentation/devicetree/bindings/soc/mediatek/mediatek,mutex.yaml
@@ -91,7 +91,6 @@ allOf:
required:
- clocks
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml b/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml
index 2c7275c4503b..668b943db173 100644
--- a/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml
+++ b/Documentation/devicetree/bindings/soc/microchip/atmel,at91rm9200-tcb.yaml
@@ -57,7 +57,7 @@ properties:
const: 0
patternProperties:
- "^timer@[0-2]$":
+ '^timer@[0-2]$':
description: The timer block channels that are used as timers or counters.
type: object
additionalProperties: false
@@ -80,7 +80,7 @@ patternProperties:
- compatible
- reg
- "^pwm@[0-2]$":
+ '^pwm@[0-2]$':
description: The timer block channels that are used as PWMs.
$ref: /schemas/pwm/pwm.yaml#
type: object
@@ -92,7 +92,7 @@ patternProperties:
TCB channel to use for this PWM.
enum: [ 0, 1, 2 ]
- "#pwm-cells":
+ '#pwm-cells':
description:
The only third cell flag supported by this binding is
PWM_POLARITY_INVERTED.
@@ -101,11 +101,10 @@ patternProperties:
required:
- compatible
- reg
- - "#pwm-cells"
+ - '#pwm-cells'
additionalProperties: false
-
allOf:
- if:
properties:
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml
index c33704333e49..d9f6d34a61c6 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,gsbi.yaml
@@ -9,7 +9,7 @@ title: Qualcomm General Serial Bus Interface (GSBI)
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The GSBI controller is modeled as a node with zero or more child nodes, each
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml
index d9fabefc8147..b667f4afdb55 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd.yaml
@@ -9,7 +9,7 @@ title: Qualcomm Shared Memory Driver
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The Qualcomm Shared Memory Driver is a FIFO based communication channel for
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml
index 1ba1d419e83b..f91276822858 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smp2p.yaml
@@ -9,7 +9,7 @@ title: Qualcomm Shared Memory Point 2 Point
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The Shared Memory Point to Point (SMP2P) protocol facilitates communication
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml
index 4900215f26af..67d4a7cb9eeb 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smsm.yaml
@@ -9,7 +9,7 @@ title: Qualcomm Shared Memory State Machine
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The Shared Memory State Machine facilitates broadcasting of single bit state
diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml
index dca5e27b8233..0b8e3294c83e 100644
--- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml
+++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml
@@ -317,7 +317,6 @@ allOf:
properties:
clocks: false
-
examples:
- |
#include <dt-bindings/clock/rk3399-cru.h>
diff --git a/Documentation/devicetree/bindings/soc/tegra/nvidia,tegra20-pmc.yaml b/Documentation/devicetree/bindings/soc/tegra/nvidia,tegra20-pmc.yaml
index 7140c312d898..f516960dbbef 100644
--- a/Documentation/devicetree/bindings/soc/tegra/nvidia,tegra20-pmc.yaml
+++ b/Documentation/devicetree/bindings/soc/tegra/nvidia,tegra20-pmc.yaml
@@ -133,12 +133,12 @@ properties:
property. The supported-hw is a bitfield indicating SoC speedo or
process ID mask.
- "#power-domain-cells":
+ '#power-domain-cells':
const: 0
required:
- operating-points-v2
- - "#power-domain-cells"
+ - '#power-domain-cells'
i2c-thermtrip:
type: object
@@ -220,7 +220,7 @@ properties:
xusbc USB Partition C Tegra114/124/210
patternProperties:
- "^[a-z0-9]+$":
+ '^[a-z0-9]+$':
type: object
additionalProperties: false
properties:
@@ -365,9 +365,9 @@ allOf:
additionalProperties: false
dependencies:
- nvidia,suspend-mode: ["nvidia,core-pwr-off-time", "nvidia,cpu-pwr-off-time"]
- nvidia,core-pwr-off-time: ["nvidia,core-pwr-good-time"]
- nvidia,cpu-pwr-off-time: ["nvidia,cpu-pwr-good-time"]
+ nvidia,suspend-mode: ['nvidia,core-pwr-off-time', 'nvidia,cpu-pwr-off-time']
+ nvidia,core-pwr-off-time: ['nvidia,core-pwr-good-time']
+ nvidia,cpu-pwr-off-time: ['nvidia,cpu-pwr-good-time']
examples:
- |
diff --git a/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml b/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml
index b5336bcbfb01..d97e88433d2f 100644
--- a/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml
+++ b/Documentation/devicetree/bindings/soc/ti/ti,pruss.yaml
@@ -11,7 +11,6 @@ maintainers:
- Suman Anna <s-anna@ti.com>
description: |+
-
The Programmable Real-Time Unit and Industrial Communication Subsystem
(PRU-ICSS a.k.a. PRUSS) is present on various TI SoCs such as AM335x, AM437x,
Keystone 66AK2G, OMAP-L138/DA850 etc. A PRUSS consists of dual 32-bit RISC
@@ -44,7 +43,6 @@ description: |+
integration within the IP and the SoC. These nodes are described in the
following sections.
-
PRU-ICSS Node
==============
Each PRU-ICSS instance is represented as its own node with the individual PRU
@@ -54,7 +52,6 @@ description: |+
See ../../mfd/syscon.yaml for generic SysCon binding details.
-
properties:
$nodename:
pattern: "^(pruss|icssg)@[0-9a-f]+$"
diff --git a/Documentation/devicetree/bindings/sound/adi,adau1372.yaml b/Documentation/devicetree/bindings/sound/adi,adau1372.yaml
index ea62e51aba90..9a7ff50a0a22 100644
--- a/Documentation/devicetree/bindings/sound/adi,adau1372.yaml
+++ b/Documentation/devicetree/bindings/sound/adi,adau1372.yaml
@@ -4,7 +4,6 @@
$id: http://devicetree.org/schemas/sound/adi,adau1372.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-
title: Analog Devices ADAU1372 CODEC
maintainers:
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7002.yaml b/Documentation/devicetree/bindings/sound/adi,adau7002.yaml
index fcca0fde7d86..7858f3f8ec2f 100644
--- a/Documentation/devicetree/bindings/sound/adi,adau7002.yaml
+++ b/Documentation/devicetree/bindings/sound/adi,adau7002.yaml
@@ -7,7 +7,7 @@ $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>
+ - Krzysztof Kozlowski <krzk@kernel.org>
allOf:
- $ref: dai-common.yaml#
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7118.yaml b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
index 12f60507aed7..11f59c29b575 100644
--- a/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
+++ b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
@@ -4,7 +4,6 @@
$id: http://devicetree.org/schemas/sound/adi,adau7118.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-
title: Analog Devices ADAU7118 8 Channel PDM to I2S/TDM Converter
maintainers:
diff --git a/Documentation/devicetree/bindings/sound/adi,max98363.yaml b/Documentation/devicetree/bindings/sound/adi,max98363.yaml
deleted file mode 100644
index c388cda56011..000000000000
--- a/Documentation/devicetree/bindings/sound/adi,max98363.yaml
+++ /dev/null
@@ -1,60 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/adi,max98363.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Analog Devices MAX98363 SoundWire Amplifier
-
-maintainers:
- - Ryan Lee <ryans.lee@analog.com>
-
-description:
- The MAX98363 is a SoundWire input Class D mono amplifier that
- supports MIPI SoundWire v1.2-compatible digital interface for
- audio and control data.
- SoundWire peripheral device ID of MAX98363 is 0x3*019f836300
- where * is the peripheral device unique ID decoded from pin.
- It supports up to 10 peripheral devices(0x0 to 0x9).
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: sdw3019f836300
-
- reg:
- maxItems: 1
-
- '#sound-dai-cells':
- const: 0
-
-required:
- - compatible
- - reg
- - "#sound-dai-cells"
-
-unevaluatedProperties: false
-
-examples:
- - |
- soundwire@3250000 {
- #address-cells = <2>;
- #size-cells = <0>;
- reg = <0x3250000 0x2000>;
-
- speaker@0,0 {
- compatible = "sdw3019f836300";
- reg = <0 0>;
- #sound-dai-cells = <0>;
- sound-name-prefix = "Speaker Left";
- };
-
- speaker@0,1 {
- compatible = "sdw3019f836300";
- reg = <0 1>;
- #sound-dai-cells = <0>;
- sound-name-prefix = "Speaker Right";
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/adi,ssm2602.txt b/Documentation/devicetree/bindings/sound/adi,ssm2602.txt
deleted file mode 100644
index 3b3302fe399b..000000000000
--- a/Documentation/devicetree/bindings/sound/adi,ssm2602.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Analog Devices SSM2602, SSM2603 and SSM2604 I2S audio CODEC devices
-
-SSM2602 support both I2C and SPI as the configuration interface,
-the selection is made by the MODE strap-in pin.
-SSM2603 and SSM2604 only support I2C as the configuration interface.
-
-Required properties:
-
- - compatible : One of "adi,ssm2602", "adi,ssm2603" or "adi,ssm2604"
-
- - reg : the I2C address of the device for I2C, the chip select
- number for SPI.
-
- Example:
-
- ssm2602: ssm2602@1a {
- compatible = "adi,ssm2602";
- reg = <0x1a>;
- };
diff --git a/Documentation/devicetree/bindings/sound/adi,ssm3515.yaml b/Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
deleted file mode 100644
index 144450df5869..000000000000
--- a/Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
+++ /dev/null
@@ -1,49 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/adi,ssm3515.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Analog Devices SSM3515 Audio Amplifier
-
-maintainers:
- - Martin Povišer <povik+lin@cutebit.org>
-
-description: |
- SSM3515 is a mono Class-D audio amplifier with digital input.
-
- https://www.analog.com/media/en/technical-documentation/data-sheets/SSM3515.pdf
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- enum:
- - adi,ssm3515
-
- reg:
- maxItems: 1
-
- '#sound-dai-cells':
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec@14 {
- compatible = "adi,ssm3515";
- reg = <0x14>;
- #sound-dai-cells = <0>;
- sound-name-prefix = "Left Tweeter";
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml
index 739114fb6549..ae86cb5f0a74 100644
--- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-i2s.yaml
@@ -33,7 +33,9 @@ properties:
- const: allwinner,sun50i-h6-i2s
- const: allwinner,sun50i-r329-i2s
- items:
- - const: allwinner,sun20i-d1-i2s
+ - enum:
+ - allwinner,sun20i-d1-i2s
+ - allwinner,sun55i-a523-i2s
- const: allwinner,sun50i-r329-i2s
reg:
diff --git a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml
index aa32dc950e72..1d089ba70f45 100644
--- a/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml
+++ b/Documentation/devicetree/bindings/sound/allwinner,sun4i-a10-spdif.yaml
@@ -23,6 +23,7 @@ properties:
- const: allwinner,sun8i-h3-spdif
- const: allwinner,sun50i-h6-spdif
- const: allwinner,sun50i-h616-spdif
+ - const: allwinner,sun55i-a523-spdif
- items:
- const: allwinner,sun8i-a83t-spdif
- const: allwinner,sun8i-h3-spdif
@@ -37,14 +38,12 @@ properties:
maxItems: 1
clocks:
- items:
- - description: Bus Clock
- - description: Module Clock
+ minItems: 2
+ maxItems: 3
clock-names:
- items:
- - const: apb
- - const: spdif
+ minItems: 2
+ maxItems: 3
# Even though it only applies to subschemas under the conditionals,
# not listing them here will trigger a warning because of the
@@ -65,6 +64,7 @@ allOf:
- allwinner,sun8i-h3-spdif
- allwinner,sun50i-h6-spdif
- allwinner,sun50i-h616-spdif
+ - allwinner,sun55i-a523-spdif
then:
required:
@@ -98,6 +98,38 @@ allOf:
- const: rx
- const: tx
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - allwinner,sun55i-a523-spdif
+
+ then:
+ properties:
+ clocks:
+ items:
+ - description: Bus Clock
+ - description: TX Clock
+ - description: RX Clock
+
+ clock-names:
+ items:
+ - const: apb
+ - const: tx
+ - const: rx
+ else:
+ properties:
+ clocks:
+ items:
+ - description: Bus Clock
+ - description: Module Clock
+
+ clock-names:
+ items:
+ - const: apb
+ - const: spdif
+
required:
- "#sound-dai-cells"
- compatible
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs4271.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs4271.yaml
index 68fbf5cc208f..d286eb169915 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs4271.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs4271.yaml
@@ -25,6 +25,16 @@ properties:
reg:
maxItems: 1
+ clocks:
+ items:
+ - description:
+ Master clock connected to the MCLK pin if MCLK is an input (i.e. no
+ crystal used).
+
+ clock-names:
+ items:
+ - const: mclk
+
spi-cpha: true
spi-cpol: true
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml
index 9582eb8eb418..7600fff0e3b7 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs530x.yaml
@@ -15,10 +15,15 @@ description:
allOf:
- $ref: dai-common.yaml#
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
+ - cirrus,cs4282
+ - cirrus,cs4302
+ - cirrus,cs4304
+ - cirrus,cs4308
- cirrus,cs5302
- cirrus,cs5304
- cirrus,cs5308
@@ -26,6 +31,9 @@ properties:
reg:
maxItems: 1
+ spi-max-frequency:
+ maximum: 24000000
+
'#sound-dai-cells':
const: 1
diff --git a/Documentation/devicetree/bindings/sound/cs4265.txt b/Documentation/devicetree/bindings/sound/cs4265.txt
deleted file mode 100644
index 380fff8e4e83..000000000000
--- a/Documentation/devicetree/bindings/sound/cs4265.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-CS4265 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
- - compatible : "cirrus,cs4265"
-
- - reg : the I2C address of the device for I2C. The I2C address depends on
- the state of the AD0 pin. If AD0 is high, the i2c address is 0x4f.
- If it is low, the i2c address is 0x4e.
-
-Optional properties:
-
- - reset-gpios : a GPIO spec for the reset pin. If specified, it will be
- deasserted before communication to the codec starts.
-
-Examples:
-
-codec_ad0_high: cs4265@4f { /* AD0 Pin is high */
- compatible = "cirrus,cs4265";
- reg = <0x4f>;
-};
-
-
-codec_ad0_low: cs4265@4e { /* AD0 Pin is low */
- compatible = "cirrus,cs4265";
- reg = <0x4e>;
-};
diff --git a/Documentation/devicetree/bindings/sound/cs4341.txt b/Documentation/devicetree/bindings/sound/cs4341.txt
deleted file mode 100644
index c1d5c8ad1a36..000000000000
--- a/Documentation/devicetree/bindings/sound/cs4341.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Cirrus Logic CS4341 audio DAC
-
-This device supports both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
- - compatible: "cirrus,cs4341a"
- - reg : the I2C address of the device for I2C, the chip select
- number for SPI.
-
-For required properties on I2C-bus, please consult
-dtschema schemas/i2c/i2c-controller.yaml
-For required properties on SPI-bus, please consult
-Documentation/devicetree/bindings/spi/spi-bus.txt
-
-Example:
- codec: cs4341@0 {
- #sound-dai-cells = <0>;
- compatible = "cirrus,cs4341a";
- reg = <0>;
- spi-max-frequency = <6000000>;
- };
diff --git a/Documentation/devicetree/bindings/sound/cs4349.txt b/Documentation/devicetree/bindings/sound/cs4349.txt
deleted file mode 100644
index 54c117b59dba..000000000000
--- a/Documentation/devicetree/bindings/sound/cs4349.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-CS4349 audio CODEC
-
-Required properties:
-
- - compatible : "cirrus,cs4349"
-
- - reg : the I2C address of the device for I2C
-
-Optional properties:
-
- - reset-gpios : a GPIO spec for the reset pin.
-
-Example:
-
-codec: cs4349@48 {
- compatible = "cirrus,cs4349";
- reg = <0x48>;
- reset-gpios = <&gpio 54 0>;
-};
diff --git a/Documentation/devicetree/bindings/sound/da9055.txt b/Documentation/devicetree/bindings/sound/da9055.txt
deleted file mode 100644
index 75c6338b6ae2..000000000000
--- a/Documentation/devicetree/bindings/sound/da9055.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-* Dialog DA9055 Audio CODEC
-
-DA9055 provides Audio CODEC support (I2C only).
-
-The Audio CODEC device in DA9055 has its own I2C address which is configurable,
-so the device is instantiated separately from the PMIC (MFD) device.
-
-For details on accompanying PMIC I2C device, see the following:
-Documentation/devicetree/bindings/mfd/da9055.txt
-
-Required properties:
-
- - compatible: "dlg,da9055-codec"
- - reg: Specifies the I2C slave address
-
-
-Example:
-
- codec: da9055-codec@1a {
- compatible = "dlg,da9055-codec";
- reg = <0x1a>;
- };
diff --git a/Documentation/devicetree/bindings/sound/maxim,max98090.yaml b/Documentation/devicetree/bindings/sound/maxim,max98090.yaml
index 65e4c516912f..9df1296aacb7 100644
--- a/Documentation/devicetree/bindings/sound/maxim,max98090.yaml
+++ b/Documentation/devicetree/bindings/sound/maxim,max98090.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim Integrated MAX98090/MAX98091 audio codecs
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description: |
Pins on the device (for linking into audio routes):
diff --git a/Documentation/devicetree/bindings/sound/maxim,max98095.yaml b/Documentation/devicetree/bindings/sound/maxim,max98095.yaml
index 77544a9e1587..76ea4fe711de 100644
--- a/Documentation/devicetree/bindings/sound/maxim,max98095.yaml
+++ b/Documentation/devicetree/bindings/sound/maxim,max98095.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim Integrated MAX98095 audio codec
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
allOf:
- $ref: dai-common.yaml#
diff --git a/Documentation/devicetree/bindings/sound/maxim,max98504.yaml b/Documentation/devicetree/bindings/sound/maxim,max98504.yaml
index 23f19a9d2c06..6d33bb4a98ae 100644
--- a/Documentation/devicetree/bindings/sound/maxim,max98504.yaml
+++ b/Documentation/devicetree/bindings/sound/maxim,max98504.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim Integrated MAX98504 class D mono speaker amplifier
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
Maxim Integrated MAX98504 speaker amplifier supports I2C control interface
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8189-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8189-afe-pcm.yaml
new file mode 100644
index 000000000000..9c9f21652af9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8189-afe-pcm.yaml
@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/mediatek,mt8189-afe-pcm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek Audio Front End PCM controller for MT8189
+
+maintainers:
+ - Darren Ye <darren.ye@mediatek.com>
+ - Cyril Chao <cyril.chao@mediatek.com>
+
+properties:
+ compatible:
+ const: mediatek,mt8189-afe-pcm
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ memory-region:
+ maxItems: 1
+
+ mediatek,apmixedsys:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: To set up the apll12 tuner
+
+ power-domains:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: mux for audio intbus
+ - description: mux for audio engen1
+ - description: mux for audio engen2
+ - description: mux for audio h
+ - description: audio apll1 clock
+ - description: audio apll2 clock
+ - description: audio apll1 divide4
+ - description: audio apll2 divide4
+ - description: audio apll12 divide for i2sin0
+ - description: audio apll12 divide for i2sin1
+ - description: audio apll12 divide for i2sout0
+ - description: audio apll12 divide for i2sout1
+ - description: audio apll12 divide for fmi2s
+ - description: audio apll12 divide for tdmout mck
+ - description: audio apll12 divide for tdmout bck
+ - description: mux for audio apll1
+ - description: mux for audio apll2
+ - description: mux for i2sin0 mck
+ - description: mux for i2sin1 mck
+ - description: mux for i2sout0 mck
+ - description: mux for i2sout1 mck
+ - description: mux for fmi2s mck
+ - description: mux for tdmout mck
+ - description: 26m clock
+ - description: audio slv clock
+ - description: audio mst clock
+ - description: audio intbus clock
+
+ clock-names:
+ items:
+ - const: top_aud_intbus
+ - const: top_aud_eng1
+ - const: top_aud_eng2
+ - const: top_aud_h
+ - const: apll1
+ - const: apll2
+ - const: apll1_d4
+ - const: apll2_d4
+ - const: apll12_div_i2sin0
+ - const: apll12_div_i2sin1
+ - const: apll12_div_i2sout0
+ - const: apll12_div_i2sout1
+ - const: apll12_div_fmi2s
+ - const: apll12_div_tdmout_m
+ - const: apll12_div_tdmout_b
+ - const: top_apll1
+ - const: top_apll2
+ - const: top_i2sin0
+ - const: top_i2sin1
+ - const: top_i2sout0
+ - const: top_i2sout1
+ - const: top_fmi2s
+ - const: top_dptx
+ - const: clk26m
+ - const: aud_slv_ck_peri
+ - const: aud_mst_ck_peri
+ - const: aud_intbus_ck_peri
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - memory-region
+ - power-domains
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ afe@11050000 {
+ compatible = "mediatek,mt8189-afe-pcm";
+ reg = <0 0x11050000 0 0x10000>;
+ interrupts = <GIC_SPI 392 IRQ_TYPE_LEVEL_HIGH 0>;
+ memory-region = <&afe_dma_mem_reserved>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&aud_pins_default>;
+ power-domains = <&scpsys 1>; //MT8189_POWER_DOMAIN_AUDIO
+ clocks = <&topckgen_clk 23>, //CLK_TOP_AUD_INTBUS_SEL
+ <&topckgen_clk 39>, //CLK_TOP_AUD_ENGEN1_SEL
+ <&topckgen_clk 40>, //CLK_TOP_AUD_ENGEN2_SEL
+ <&topckgen_clk 49>, //CLK_TOP_AUDIO_H_SEL
+ <&topckgen_clk 146>, //CLK_TOP_APLL1
+ <&topckgen_clk 151>, //CLK_TOP_APLL2
+ <&topckgen_clk 148>, //CLK_TOP_APLL1_D4
+ <&topckgen_clk 153>, //CLK_TOP_APLL2_D4
+ <&topckgen_clk 93>, //CLK_TOP_APLL12_CK_DIV_I2SIN0
+ <&topckgen_clk 94>, //CLK_TOP_APLL12_CK_DIV_I2SIN1
+ <&topckgen_clk 95>, //CLK_TOP_APLL12_CK_DIV_I2SOUT0
+ <&topckgen_clk 96>, //CLK_TOP_APLL12_CK_DIV_I2SOUT1
+ <&topckgen_clk 97>, //CLK_TOP_APLL12_CK_DIV_FMI2S
+ <&topckgen_clk 98>, //CLK_TOP_APLL12_CK_DIV_TDMOUT_M
+ <&topckgen_clk 99>, //CLK_TOP_APLL12_CK_DIV_TDMOUT_B
+ <&topckgen_clk 44>, //CLK_TOP_AUD_1_SEL
+ <&topckgen_clk 45>, //CLK_TOP_AUD_2_SEL
+ <&topckgen_clk 78>, //CLK_TOP_APLL_I2SIN0_MCK_SEL
+ <&topckgen_clk 79>, //CLK_TOP_APLL_I2SIN1_MCK_SEL
+ <&topckgen_clk 84>, //CLK_TOP_APLL_I2SOUT0_MCK_SEL
+ <&topckgen_clk 85>, //CLK_TOP_APLL_I2SOUT1_MCK_SEL
+ <&topckgen_clk 90>, //CLK_TOP_APLL_FMI2S_MCK_SEL
+ <&topckgen_clk 91>, //CLK_TOP_APLL_TDMOUT_MCK_SEL
+ <&topckgen_clk 191>, //CLK_TOP_TCK_26M_MX9
+ <&pericfg_ao_clk 77>, //CLK_PERAO_AUDIO0
+ <&pericfg_ao_clk 78>, //CLK_PERAO_AUDIO1
+ <&pericfg_ao_clk 79>; //CLK_PERAO_AUDIO2
+ clock-names = "top_aud_intbus",
+ "top_aud_eng1",
+ "top_aud_eng2",
+ "top_aud_h",
+ "apll1",
+ "apll2",
+ "apll1_d4",
+ "apll2_d4",
+ "apll12_div_i2sin0",
+ "apll12_div_i2sin1",
+ "apll12_div_i2sout0",
+ "apll12_div_i2sout1",
+ "apll12_div_fmi2s",
+ "apll12_div_tdmout_m",
+ "apll12_div_tdmout_b",
+ "top_apll1",
+ "top_apll2",
+ "top_i2sin0",
+ "top_i2sin1",
+ "top_i2sout0",
+ "top_i2sout1",
+ "top_fmi2s",
+ "top_dptx",
+ "clk26m",
+ "aud_slv_ck_peri",
+ "aud_mst_ck_peri",
+ "aud_intbus_ck_peri";
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8189-nau8825.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8189-nau8825.yaml
new file mode 100644
index 000000000000..dd9ee0a3b292
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8189-nau8825.yaml
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/mediatek,mt8189-nau8825.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT8189 ASoC sound card
+
+maintainers:
+ - Darren Ye <darren.ye@mediatek.com>
+ - Cyril Chao <cyril.chao@mediatek.com>
+
+allOf:
+ - $ref: sound-card-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - mediatek,mt8189-nau8825
+ - mediatek,mt8189-rt5650
+ - mediatek,mt8189-rt5682s
+ - mediatek,mt8189-rt5682i
+ - mediatek,mt8189-es8326
+
+ mediatek,platform:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of MT8189 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.
+ enum:
+ - TDM_DPTX_BE
+ - I2SOUT0_BE
+ - I2SIN0_BE
+ - I2SOUT1_BE
+
+ codec:
+ description: Holds subnode which indicates codec dai.
+ type: object
+ additionalProperties: false
+
+ properties:
+ sound-dai:
+ minItems: 1
+ maxItems: 2
+ required:
+ - sound-dai
+
+ dai-format:
+ description: audio format.
+ enum:
+ - i2s
+ - right_j
+ - left_j
+ - dsp_a
+ - dsp_b
+
+ mediatek,clk-provider:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: Indicates dai-link clock master.
+ enum:
+ - cpu
+ - codec
+
+ additionalProperties: false
+
+ required:
+ - link-name
+
+required:
+ - compatible
+ - mediatek,platform
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "mediatek,mt8189-nau8825";
+ model = "mt8189_rt9123_8825";
+ mediatek,platform = <&afe>;
+ dai-link-0 {
+ link-name = "I2SOUT1_BE";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&nau8825>;
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml
deleted file mode 100644
index 7ccfbb8d8b04..000000000000
--- a/Documentation/devicetree/bindings/sound/nuvoton,nau8540.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/nuvoton,nau8540.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Nuvoton Technology Corporation NAU85L40 Audio CODEC
-
-maintainers:
- - John Hsu <KCHSU0@nuvoton.com>
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: nuvoton,nau8540
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1c {
- compatible = "nuvoton,nau8540";
- reg = <0x1c>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml
deleted file mode 100644
index d9696f6c75ed..000000000000
--- a/Documentation/devicetree/bindings/sound/nuvoton,nau8810.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/nuvoton,nau8810.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: NAU8810/NAU8812/NAU8814 audio CODEC
-
-maintainers:
- - David Lin <CTLIN0@nuvoton.com>
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- enum:
- - nuvoton,nau8810
- - nuvoton,nau8812
- - nuvoton,nau8814
-
- reg:
- maxItems: 1
-
- '#sound-dai-cells':
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec@1a {
- #sound-dai-cells = <0>;
- compatible = "nuvoton,nau8810";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
index b32f33214ba6..2ce4049f94ac 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
@@ -67,46 +67,72 @@ properties:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
-if:
- properties:
- compatible:
- contains:
- const: nvidia,tegra210-admaif
-
-then:
- properties:
- dmas:
- description:
- DMA channel specifiers, equally divided for Tx and Rx.
- minItems: 1
- maxItems: 20
- dma-names:
- items:
- pattern: "^[rt]x(10|[1-9])$"
- description:
- Should be "rx1", "rx2" ... "rx10" for DMA Rx channel
- Should be "tx1", "tx2" ... "tx10" for DMA Tx channel
- minItems: 1
- maxItems: 20
- interconnects: false
- interconnect-names: false
- iommus: false
-
-else:
- properties:
- dmas:
- description:
- DMA channel specifiers, equally divided for Tx and Rx.
- minItems: 1
- maxItems: 40
- dma-names:
- items:
- pattern: "^[rt]x(1[0-9]|[1-9]|20)$"
- description:
- Should be "rx1", "rx2" ... "rx20" for DMA Rx channel
- Should be "tx1", "tx2" ... "tx20" for DMA Tx channel
- minItems: 1
- maxItems: 40
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nvidia,tegra210-admaif
+ then:
+ properties:
+ dmas:
+ description:
+ DMA channel specifiers, equally divided for Tx and Rx.
+ minItems: 1
+ maxItems: 20
+ dma-names:
+ items:
+ pattern: "^[rt]x(10|[1-9])$"
+ description:
+ Should be "rx1", "rx2" ... "rx10" for DMA Rx channel
+ Should be "tx1", "tx2" ... "tx10" for DMA Tx channel
+ minItems: 1
+ maxItems: 20
+ interconnects: false
+ interconnect-names: false
+ iommus: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nvidia,tegra186-admaif
+ then:
+ properties:
+ dmas:
+ description:
+ DMA channel specifiers, equally divided for Tx and Rx.
+ minItems: 1
+ maxItems: 40
+ dma-names:
+ items:
+ pattern: "^[rt]x(1[0-9]|[1-9]|20)$"
+ description:
+ Should be "rx1", "rx2" ... "rx20" for DMA Rx channel
+ Should be "tx1", "tx2" ... "tx20" for DMA Tx channel
+ minItems: 1
+ maxItems: 40
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nvidia,tegra264-admaif
+ then:
+ properties:
+ dmas:
+ description:
+ DMA channel specifiers, equally divided for Tx and Rx.
+ minItems: 1
+ maxItems: 64
+ dma-names:
+ items:
+ pattern: "^[rt]x(3[0-2]|[1-2][0-9]|[1-9])$"
+ description:
+ Should be "rx1", "rx2" ... "rx32" for DMA Rx channel
+ Should be "tx1", "tx2" ... "tx32" for DMA Tx channel
+ minItems: 1
+ maxItems: 64
required:
- compatible
diff --git a/Documentation/devicetree/bindings/sound/nxp,tfa9879.yaml b/Documentation/devicetree/bindings/sound/nxp,tfa9879.yaml
deleted file mode 100644
index df26248573ad..000000000000
--- a/Documentation/devicetree/bindings/sound/nxp,tfa9879.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/nxp,tfa9879.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: NXP TFA9879 class-D audio amplifier
-
-maintainers:
- - Peter Rosin <peda@axentia.se>
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: nxp,tfa9879
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
- - '#sound-dai-cells'
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c1 {
- #address-cells = <1>;
- #size-cells = <0>;
- amplifier@6c {
- compatible = "nxp,tfa9879";
- reg = <0x6c>;
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_i2c1>;
- #sound-dai-cells = <0>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/nxp,uda1342.yaml b/Documentation/devicetree/bindings/sound/nxp,uda1342.yaml
deleted file mode 100644
index 71c6a5a2f5bc..000000000000
--- a/Documentation/devicetree/bindings/sound/nxp,uda1342.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/nxp,uda1342.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: NXP uda1342 audio CODECs
-
-maintainers:
- - Binbin Zhou <zhoubinbin@loongson.cn>
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: nxp,uda1342
-
- reg:
- maxItems: 1
-
- '#sound-dai-cells':
- const: 0
-
-required:
- - compatible
- - reg
- - '#sound-dai-cells'
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1a {
- compatible = "nxp,uda1342";
- reg = <0x1a>;
- #sound-dai-cells = <0>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/pcm1789.txt b/Documentation/devicetree/bindings/sound/pcm1789.txt
deleted file mode 100644
index 3c74ed220ac2..000000000000
--- a/Documentation/devicetree/bindings/sound/pcm1789.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Texas Instruments pcm1789 DT bindings
-
-PCM1789 is a simple audio codec that can be connected via
-I2C or SPI. Currently, only I2C bus is supported.
-
-Required properties:
-
- - compatible: "ti,pcm1789"
-
-Required properties on I2C:
-
- - reg: the I2C address
- - reset-gpios: GPIO to control the RESET pin
-
-Examples:
-
- audio-codec@4c {
- compatible = "ti,pcm1789";
- reg = <0x4c>;
- reset-gpios = <&gpio2 14 GPIO_ACTIVE_LOW>;
- #sound-dai-cells = <0>;
- };
diff --git a/Documentation/devicetree/bindings/sound/pcm179x.txt b/Documentation/devicetree/bindings/sound/pcm179x.txt
deleted file mode 100644
index 436c2b247693..000000000000
--- a/Documentation/devicetree/bindings/sound/pcm179x.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-Texas Instruments pcm179x DT bindings
-
-This driver supports both the I2C and SPI bus.
-
-Required properties:
-
- - compatible: "ti,pcm1792a"
-
-For required properties on SPI, please consult
-Documentation/devicetree/bindings/spi/spi-bus.txt
-
-Required properties on I2C:
-
- - reg: the I2C address
-
-
-Examples:
-
- codec_spi: 1792a@0 {
- compatible = "ti,pcm1792a";
- spi-max-frequency = <600000>;
- };
-
- codec_i2c: 1792a@4c {
- compatible = "ti,pcm1792a";
- reg = <0x4c>;
- };
diff --git a/Documentation/devicetree/bindings/sound/pcm186x.txt b/Documentation/devicetree/bindings/sound/pcm186x.txt
deleted file mode 100644
index 1087f4855980..000000000000
--- a/Documentation/devicetree/bindings/sound/pcm186x.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-Texas Instruments PCM186x Universal Audio ADC
-
-These devices support both I2C and SPI (configured with pin strapping
-on the board).
-
-Required properties:
-
- - compatible : "ti,pcm1862",
- "ti,pcm1863",
- "ti,pcm1864",
- "ti,pcm1865"
-
- - reg : The I2C address of the device for I2C, the chip select
- number for SPI.
-
- - avdd-supply: Analog core power supply (3.3v)
- - dvdd-supply: Digital core power supply
- - iovdd-supply: Digital IO power supply
- See regulator/regulator.txt for more information
-
-CODEC input pins:
- * VINL1
- * VINR1
- * VINL2
- * VINR2
- * VINL3
- * VINR3
- * VINL4
- * VINR4
-
-The pins can be used in referring sound node's audio-routing property.
-
-Example:
-
- pcm186x: audio-codec@4a {
- compatible = "ti,pcm1865";
- reg = <0x4a>;
-
- avdd-supply = <&reg_3v3_analog>;
- dvdd-supply = <&reg_3v3>;
- iovdd-supply = <&reg_1v8>;
- };
diff --git a/Documentation/devicetree/bindings/sound/pcm5102a.txt b/Documentation/devicetree/bindings/sound/pcm5102a.txt
deleted file mode 100644
index c63ab0b6ee19..000000000000
--- a/Documentation/devicetree/bindings/sound/pcm5102a.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-PCM5102a audio CODECs
-
-These devices does not use I2C or SPI.
-
-Required properties:
-
- - compatible : set as "ti,pcm5102a"
-
-Examples:
-
- pcm5102a: pcm5102a {
- compatible = "ti,pcm5102a";
- };
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
index 92f95eb74b19..2eed2277511f 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-rx-macro.yaml
@@ -14,12 +14,14 @@ properties:
oneOf:
- enum:
- qcom,sc7280-lpass-rx-macro
+ - qcom,sm6115-lpass-rx-macro
- qcom,sm8250-lpass-rx-macro
- qcom,sm8450-lpass-rx-macro
- qcom,sm8550-lpass-rx-macro
- qcom,sc8280xp-lpass-rx-macro
- items:
- enum:
+ - qcom,kaanapali-lpass-rx-macro
- qcom,sm8650-lpass-rx-macro
- qcom,sm8750-lpass-rx-macro
- qcom,x1e80100-lpass-rx-macro
@@ -84,6 +86,23 @@ allOf:
properties:
compatible:
enum:
+ - qcom,sm6115-lpass-rx-macro
+ then:
+ properties:
+ clocks:
+ minItems: 4
+ maxItems: 4
+ clock-names:
+ items:
+ - const: mclk
+ - const: npl
+ - const: dcodec
+ - const: fsgen
+
+ - if:
+ properties:
+ compatible:
+ enum:
- qcom,sc8280xp-lpass-rx-macro
- qcom,sm8250-lpass-rx-macro
- qcom,sm8450-lpass-rx-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
index 914798a89878..e5e65e226a02 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-tx-macro.yaml
@@ -21,6 +21,7 @@ properties:
- qcom,sc8280xp-lpass-tx-macro
- items:
- enum:
+ - qcom,kaanapali-lpass-tx-macro
- qcom,sm8650-lpass-tx-macro
- qcom,sm8750-lpass-tx-macro
- qcom,x1e80100-lpass-tx-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
index 1c0d78af3c05..5c42b2b323ee 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
@@ -14,6 +14,7 @@ properties:
oneOf:
- enum:
- qcom,sc7280-lpass-va-macro
+ - qcom,sm6115-lpass-va-macro
- qcom,sm8250-lpass-va-macro
- qcom,sm8450-lpass-va-macro
- qcom,sm8550-lpass-va-macro
@@ -21,6 +22,7 @@ properties:
- items:
- enum:
- qcom,glymur-lpass-va-macro
+ - qcom,kaanapali-lpass-va-macro
- qcom,sm8650-lpass-va-macro
- qcom,sm8750-lpass-va-macro
- qcom,x1e80100-lpass-va-macro
@@ -41,11 +43,7 @@ properties:
clock-names:
minItems: 1
- items:
- - const: mclk
- - const: macro
- - const: dcodec
- - const: npl
+ maxItems: 4
clock-output-names:
maxItems: 1
@@ -90,29 +88,48 @@ allOf:
clocks:
maxItems: 1
clock-names:
- maxItems: 1
+ items:
+ - const: mclk
else:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
- minItems: 3
- maxItems: 3
-
+ items:
+ - const: mclk
+ - const: macro
+ - const: dcodec
- if:
properties:
compatible:
contains:
- const: qcom,sm8250-lpass-va-macro
+ const: qcom,sm6115-lpass-va-macro
then:
properties:
clocks:
minItems: 3
maxItems: 3
clock-names:
+ items:
+ - const: mclk
+ - const: dcodec
+ - const: npl
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: qcom,sm8250-lpass-va-macro
+ then:
+ properties:
+ clocks:
minItems: 3
maxItems: 3
+ clock-names:
+ items:
+ - const: mclk
+ - const: macro
+ - const: dcodec
- if:
properties:
@@ -127,8 +144,11 @@ allOf:
minItems: 4
maxItems: 4
clock-names:
- minItems: 4
- maxItems: 4
+ items:
+ - const: mclk
+ - const: macro
+ - const: dcodec
+ - const: npl
- if:
properties:
@@ -142,8 +162,10 @@ allOf:
minItems: 3
maxItems: 3
clock-names:
- minItems: 3
- maxItems: 3
+ items:
+ - const: mclk
+ - const: macro
+ - const: dcodec
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
index b6f5ba5d1320..d5f22b5cf021 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-wsa-macro.yaml
@@ -21,6 +21,7 @@ properties:
- items:
- enum:
- qcom,glymur-lpass-wsa-macro
+ - qcom,kaanapali-lpass-wsa-macro
- qcom,sm8650-lpass-wsa-macro
- qcom,sm8750-lpass-wsa-macro
- qcom,x1e80100-lpass-wsa-macro
diff --git a/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
index 23624f32ac30..769e4cb5b99b 100644
--- a/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,pm4125-sdw.yaml
@@ -32,7 +32,7 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
- maxItems: 2
+ maxItems: 4
items:
enum: [1, 2, 3, 4]
@@ -48,7 +48,7 @@ properties:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
- maxItems: 2
+ maxItems: 5
items:
enum: [1, 2, 3, 4, 5]
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6adm-routing.yaml b/Documentation/devicetree/bindings/sound/qcom,q6adm-routing.yaml
index 3f11d2e183e1..26fe8cc66b3c 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6adm-routing.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6adm-routing.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio Device Manager (Q6ADM) routing
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6adm.yaml b/Documentation/devicetree/bindings/sound/qcom,q6adm.yaml
index fe14a97ea616..3c32c5b0fad8 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6adm.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6adm.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio Device Manager (Q6ADM)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml b/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml
index 268f7073d797..4624b3d461d5 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6afe.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio FrontEnd (Q6AFE)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml
index 894e653d37d7..2fb95544db8b 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm DSP LPASS (Low Power Audio SubSystem) Audio Ports
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm.yaml
index ef1965aca254..ec06769a2b63 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6apm.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6apm.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio Process Manager (Q6APM)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6asm-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6asm-dais.yaml
index ce811942a9f1..47a105a97ecf 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6asm-dais.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6asm-dais.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio Stream Manager (Q6ASM)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6asm.yaml b/Documentation/devicetree/bindings/sound/qcom,q6asm.yaml
index cb49f9667cca..a6f88ce92299 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6asm.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6asm.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio Stream Manager (Q6ASM)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6core.yaml b/Documentation/devicetree/bindings/sound/qcom,q6core.yaml
index e240712de9ca..8642ef9f9142 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6core.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6core.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Audio Core (Q6Core)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6prm.yaml b/Documentation/devicetree/bindings/sound/qcom,q6prm.yaml
index f6dbb1267bfe..3eafe189e699 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6prm.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6prm.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm Proxy Resource Manager (Q6PRM)
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
allOf:
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index b49a920af704..15f38622b98b 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -23,6 +23,7 @@ properties:
- const: qcom,sdm845-sndcard
- items:
- enum:
+ - qcom,kaanapali-sndcard
- qcom,sm8550-sndcard
- qcom,sm8650-sndcard
- qcom,sm8750-sndcard
@@ -38,6 +39,7 @@ properties:
- qcom,qcs8275-sndcard
- qcom,qcs9075-sndcard
- qcom,qcs9100-sndcard
+ - qcom,qrb2210-sndcard
- qcom,qrb4210-rb2-sndcard
- qcom,qrb5165-rb5-sndcard
- qcom,sc7180-qdsp6-sndcard
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
index a65b1d1d5fdd..3a7334e41fd6 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
@@ -132,7 +132,7 @@ properties:
$ref: /schemas/gpio/qcom,wcd934x-gpio.yaml#
patternProperties:
- "^.*@[0-9a-f]+$":
+ "@[0-9a-f]+$":
type: object
additionalProperties: true
description: |
diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml
index 83e0360301e1..866c5e780fb0 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wsa8840.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm WSA8840/WSA8845/WSA8845H smart speaker amplifier
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
description:
diff --git a/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.yaml b/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.yaml
index 7bb6c5dff786..9435f395403a 100644
--- a/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.yaml
+++ b/Documentation/devicetree/bindings/sound/rockchip,i2s-tdm.yaml
@@ -135,7 +135,6 @@ properties:
the direction (input/output) needs to be dynamically adjusted.
type: boolean
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.yaml b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.yaml
index 5cdb8bcc687b..52e3f1f900c4 100644
--- a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.yaml
+++ b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.yaml
@@ -8,10 +8,10 @@ title: Rockchip rk3328 internal codec
maintainers:
- Heiko Stuebner <heiko@sntech.de>
+
allOf:
- $ref: dai-common.yaml#
-
properties:
compatible:
const: rockchip,rk3328-codec
diff --git a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml
index cbc7ba37362a..67586ba3e0a0 100644
--- a/Documentation/devicetree/bindings/sound/samsung,tm2.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,tm2.yaml
@@ -30,7 +30,6 @@ properties:
- items:
- description: Phandle to the HDMI transmitter node.
-
samsung,audio-routing:
description: |
List of the connections between audio components; each entry is
diff --git a/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml
new file mode 100644
index 000000000000..55bd0b307d22
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/spacemit,k1-i2s.yaml
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/spacemit,k1-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: K1 I2S controller
+
+description:
+ The I2S bus (Inter-IC sound bus) is a serial link for digital
+ audio data transfer between devices in the system.
+
+maintainers:
+ - Troy Mitchell <troy.mitchell@linux.spacemit.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: spacemit,k1-i2s
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: clock for I2S sysclk
+ - description: clock for I2S bclk
+ - description: clock for I2S bus
+ - description: clock for I2S controller
+
+ clock-names:
+ items:
+ - const: sysclk
+ - const: bclk
+ - const: bus
+ - const: func
+
+ dmas:
+ minItems: 1
+ maxItems: 2
+
+ dma-names:
+ minItems: 1
+ items:
+ - const: tx
+ - const: rx
+
+ resets:
+ maxItems: 1
+
+ port:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - dmas
+ - dma-names
+ - resets
+ - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/spacemit,k1-syscon.h>
+ i2s@d4026000 {
+ compatible = "spacemit,k1-i2s";
+ reg = <0xd4026000 0x30>;
+ clocks = <&syscon_mpmu CLK_I2S_SYSCLK>,
+ <&syscon_mpmu CLK_I2S_BCLK>,
+ <&syscon_apbc CLK_SSPA0_BUS>,
+ <&syscon_apbc CLK_SSPA0>;
+ clock-names = "sysclk", "bclk", "bus", "func";
+ dmas = <&pdma0 21>, <&pdma0 22>;
+ dma-names = "tx", "rx";
+ resets = <&syscon_apbc RESET_SSPA0>;
+ #sound-dai-cells = <0>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm1862.yaml b/Documentation/devicetree/bindings/sound/ti,pcm1862.yaml
new file mode 100644
index 000000000000..0f0e254a2420
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,pcm1862.yaml
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,pcm1862.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments PCM186x Universal Audio ADC
+
+maintainers:
+ - Ranganath V N <vnranganath.20@gmail.com>
+
+description: |
+ The Texas Instruments PCM186x family are multi-channel audio ADCs
+ that support both I2C and SPI control interfaces, selected by
+ pin strapping. These devices include on-chip programmable gain
+ amplifiers and support differential or single-ended analog inputs.
+
+ CODEC input pins:
+ * VINL1
+ * VINR1
+ * VINL2
+ * VINR2
+ * VINL3
+ * VINR3
+ * VINL4
+ * VINR4
+
+ The pins can be used in referring sound node's audio-routing property.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - ti,pcm1862
+ - ti,pcm1863
+ - ti,pcm1864
+ - ti,pcm1865
+
+ reg:
+ maxItems: 1
+
+ avdd-supply: true
+
+ dvdd-supply: true
+
+ iovdd-supply: true
+
+ '#sound-dai-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - dvdd-supply
+ - iovdd-supply
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@4a {
+ compatible = "ti,pcm1865";
+ reg = <0x4a>;
+
+ avdd-supply = <&reg_3v3_analog>;
+ dvdd-supply = <&reg_3v3>;
+ iovdd-supply = <&reg_1v8>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
index 7f84f506013c..f3a5638f4239 100644
--- a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
@@ -24,21 +24,26 @@ description: |
Instruments Smart Amp speaker protection algorithm. The
integrated speaker voltage and current sense provides for real time
monitoring of loudspeaker behavior.
- The TAS5802/TAS5815/TAS5825/TAS5827/TAS5828 is a stereo, digital input
- Class-D audio amplifier optimized for efficiently driving high peak
- power into small loudspeakers. An integrated on-chip DSP supports
- Texas Instruments Smart Amp speaker protection algorithm.
+ The TAS5802/TAS5815/TAS5822/TAS5825/TAS5827/TAS5828 is a stereo,
+ digital input Class-D audio amplifier optimized for efficiently driving
+ high peak power into small loudspeakers. An integrated on-chip DSP
+ supports Texas Instruments Smart Amp speaker protection algorithm.
Specifications about the audio amplifier can be found at:
https://www.ti.com/lit/gpn/tas2120
https://www.ti.com/lit/gpn/tas2320
https://www.ti.com/lit/gpn/tas2563
https://www.ti.com/lit/gpn/tas2572
+ https://www.ti.com/lit/gpn/tas2574
https://www.ti.com/lit/gpn/tas2781
+ https://www.ti.com/lit/gpn/tas5806m
+ https://www.ti.com/lit/gpn/tas5806md
https://www.ti.com/lit/gpn/tas5815
+ https://www.ti.com/lit/gpn/tas5822m
https://www.ti.com/lit/gpn/tas5825m
https://www.ti.com/lit/gpn/tas5827
https://www.ti.com/lit/gpn/tas5828m
+ https://www.ti.com/lit/gpn/tas5830
properties:
compatible:
@@ -57,12 +62,18 @@ properties:
ti,tas2563: 6.1-W Boosted Class-D Audio Amplifier With Integrated
DSP and IV Sense, 16/20/24/32bit stereo I2S or multichannel TDM.
+ ti,tas2568: 5.3-W Digital Input Smart Amp with I/V Sense and Integrated
+ 10.75-V Class-H Boost
+
ti,tas2570: 5.8-W Digital Input smart amp with I/V sense and integrated
11-V Class-H Boost
ti,tas2572: 6.6-W Digital Input smart amp with I/V sense and integrated
13-V Class-H Boost
+ ti,tas2574: 8.5-W Digital Input smart amp with I/V sense and integrated
+ 15-V Class-H Boost
+
ti,tas2781: 24-V Class-D Amplifier with Real Time Integrated Speaker
Protection and Audio Processing, 16/20/24/32bit stereo I2S or
multichannel TDM.
@@ -71,9 +82,20 @@ properties:
Audio Amplifier with 96-Khz Extended Processing and Low Idle Power
Dissipation.
+ ti,tas5806m: 23-W, Inductor-Less, Digital Input, Stereo, Closed-Loop
+ Class-D Audio Amplifier with Enhanced Processing and Low Power
+ Dissipation.
+
+ ti,tas5806md: 23-W, Inductor-Less, Digital Input, Stereo, Closed-Loop
+ Class-D Audio Amplifier with Enhanced Processing and DirectPath(TM)
+ HP Driver
+
ti,tas5815: 30-W, Digital Input, Stereo, Closed-loop Class-D Audio
Amplifier with 96 kHz Enhanced Processing
+ ti,tas5822: 35-W, Digital Input, Stereo, Closed-Loop Class-D Audio
+ Amplifier with 96 kHz Enhanced Processing
+
ti,tas5825: 38-W Stereo, Inductor-Less, Digital Input, Closed-Loop 4.5V
to 26.4V Class-D Audio Amplifier with 192-kHz Extended Audio Processing.
@@ -82,6 +104,9 @@ properties:
ti,tas5828: 50-W Stereo, Digital Input, High Efficiency Closed-Loop
Class-D Amplifier with Hybrid-Pro Algorithm
+
+ ti,tas5830: 65-W Stereo, Digital Input, High Efficiency Closed-Loop
+ Class-D Amplifier with Class-H Algorithm
oneOf:
- items:
- enum:
@@ -90,13 +115,19 @@ properties:
- ti,tas2120
- ti,tas2320
- ti,tas2563
+ - ti,tas2568
- ti,tas2570
- ti,tas2572
+ - ti,tas2574
- ti,tas5802
+ - ti,tas5806m
+ - ti,tas5806md
- ti,tas5815
+ - ti,tas5822
- ti,tas5825
- ti,tas5827
- ti,tas5828
+ - ti,tas5830
- const: ti,tas2781
- enum:
- ti,tas2781
@@ -132,6 +163,8 @@ allOf:
- ti,tas2118
- ti,tas2120
- ti,tas2320
+ - ti,tas2568
+ - ti,tas2574
then:
properties:
reg:
@@ -212,8 +245,25 @@ allOf:
compatible:
contains:
enum:
+ - ti,tas5806m
+ - ti,tas5806md
+ - ti,tas5822
+ then:
+ properties:
+ reg:
+ maxItems: 4
+ items:
+ minimum: 0x2c
+ maximum: 0x2f
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- ti,tas5827
- ti,tas5828
+ - ti,tas5830
then:
properties:
reg:
diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml
index 85e937e34962..10299064cbc6 100644
--- a/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml
@@ -84,7 +84,6 @@ properties:
description: gpio pin number used for codec reset
deprecated: true
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/sound/trivial-codec.yaml b/Documentation/devicetree/bindings/sound/trivial-codec.yaml
new file mode 100644
index 000000000000..9a35dfb17349
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/trivial-codec.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/trivial-codec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Trivial Audio Codec
+
+maintainers:
+ - Rob Herring <robh@kernel.org>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ # Analog Devices SSM2602 I2S audio CODEC devices
+ - adi,ssm2602
+ - adi,ssm2603
+ - adi,ssm2604
+ - adi,ssm3515
+ # Cirrus Logic CS4265 audio DAC
+ - cirrus,cs4265
+ - cirrus,cs4341a
+ - cirrus,cs4349
+ - dlg,da9055-codec
+ # Nuvoton Technology Corporation NAU85L40 Audio CODEC
+ - nuvoton,nau8540
+ - nuvoton,nau8810
+ - nuvoton,nau8812
+ - nuvoton,nau8814
+ # NXP TFA9879 class-D audio amplifier
+ - nxp,tfa9879
+ - nxp,uda1342
+ - sdw3019f836300
+ - ti,pcm1789
+ - ti,pcm1792a
+ - ti,pcm5102a
+ - wlf,wm8510
+ - wlf,wm8523
+ - wlf,wm8580
+ - wlf,wm8581
+ - wlf,wm8711
+ - wlf,wm8728
+ - wlf,wm8737
+ - wlf,wm8750
+ - wlf,wm8753
+ - wlf,wm8770
+ - wlf,wm8776
+ - wlf,wm8961
+ - wlf,wm8974
+ - wlf,wm8987
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ reset-gpios:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec@1a {
+ compatible = "wlf,wm8523";
+ reg = <0x1a>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8510.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8510.yaml
deleted file mode 100644
index 6d12b0ac37e2..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8510.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8510.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8510 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8510
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec@1a {
- compatible = "wlf,wm8510";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8523.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8523.yaml
deleted file mode 100644
index decc395bb873..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8523.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8523.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8523 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8523
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1a {
- compatible = "wlf,wm8523";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8580.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8580.yaml
deleted file mode 100644
index 2f27852cdc20..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8580.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8580.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8580 and WM8581 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- enum:
- - wlf,wm8580
- - wlf,wm8581
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1a {
- compatible = "wlf,wm8580";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8711.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8711.yaml
deleted file mode 100644
index ecaac2818b44..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8711.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8711.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8711 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8711
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1a {
- compatible = "wlf,wm8711";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8728.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8728.yaml
deleted file mode 100644
index fc89475a051e..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8728.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8728.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8728 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8728
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1a {
- compatible = "wlf,wm8728";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8737.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8737.yaml
deleted file mode 100644
index 12d8765726d8..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8737.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8737.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8737 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8737
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1a {
- compatible = "wlf,wm8737";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml
deleted file mode 100644
index 96859e38315b..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8750.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8750.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8750 and WM8987 audio CODECs
-
-description: |
- These devices support both I2C and SPI (configured with pin strapping
- on the board).
-
-maintainers:
- - Mark Brown <broonie@kernel.org>
-
-properties:
- compatible:
- enum:
- - wlf,wm8750
- - wlf,wm8987
-
- reg:
- description:
- The I2C address of the device for I2C, the chip select number for SPI
- maxItems: 1
-
-additionalProperties: false
-
-required:
- - reg
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec@1a {
- compatible = "wlf,wm8750";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8753.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8753.yaml
deleted file mode 100644
index 9eebe7d7f0b7..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8753.yaml
+++ /dev/null
@@ -1,62 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8753.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8753 audio CODEC
-
-description: |
- Pins on the device (for linking into audio routes):
- * LOUT1
- * LOUT2
- * ROUT1
- * ROUT2
- * MONO1
- * MONO2
- * OUT3
- * OUT4
- * LINE1
- * LINE2
- * RXP
- * RXN
- * ACIN
- * ACOP
- * MIC1N
- * MIC1
- * MIC2N
- * MIC2
- * Mic Bias
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8753
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
- codec@1a {
- compatible = "wlf,wm8753";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8776.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8776.yaml
deleted file mode 100644
index 7bbc96ee81be..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8776.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8776.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8776 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8776
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec@1a {
- compatible = "wlf,wm8776";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml
index 4cfa66f62681..089b67384797 100644
--- a/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml
+++ b/Documentation/devicetree/bindings/sound/wlf,wm8903.yaml
@@ -75,7 +75,6 @@ properties:
DCVDD-supply:
description: Digital core supply regulator for the DCVDD pin.
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8961.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8961.yaml
deleted file mode 100644
index f58078545569..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8961.yaml
+++ /dev/null
@@ -1,43 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8961.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Wolfson WM8961 Ultra-Low Power Stereo CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8961
-
- reg:
- maxItems: 1
-
- '#sound-dai-cells':
- const: 0
-
-required:
- - compatible
- - reg
- - '#sound-dai-cells'
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- wm8961: codec@4a {
- compatible = "wlf,wm8961";
- reg = <0x4a>;
- #sound-dai-cells = <0>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8974.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8974.yaml
deleted file mode 100644
index d27300207c67..000000000000
--- a/Documentation/devicetree/bindings/sound/wlf,wm8974.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/sound/wlf,wm8974.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: WM8974 audio CODEC
-
-maintainers:
- - patches@opensource.cirrus.com
-
-allOf:
- - $ref: dai-common.yaml#
-
-properties:
- compatible:
- const: wlf,wm8974
-
- reg:
- maxItems: 1
-
- "#sound-dai-cells":
- const: 0
-
-required:
- - compatible
- - reg
-
-unevaluatedProperties: false
-
-examples:
- - |
- i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec@1a {
- compatible = "wlf,wm8974";
- reg = <0x1a>;
- };
- };
diff --git a/Documentation/devicetree/bindings/sound/wlf,wm8994.yaml b/Documentation/devicetree/bindings/sound/wlf,wm8994.yaml
index 8f045de02850..0db04a90ac6b 100644
--- a/Documentation/devicetree/bindings/sound/wlf,wm8994.yaml
+++ b/Documentation/devicetree/bindings/sound/wlf,wm8994.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Wolfson WM1811/WM8994/WM8958 audio codecs
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
- patches@opensource.cirrus.com
description: |
diff --git a/Documentation/devicetree/bindings/sound/wm8770.txt b/Documentation/devicetree/bindings/sound/wm8770.txt
deleted file mode 100644
index cac762a1105d..000000000000
--- a/Documentation/devicetree/bindings/sound/wm8770.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-WM8770 audio CODEC
-
-This device supports SPI.
-
-Required properties:
-
- - compatible : "wlf,wm8770"
-
- - reg : the chip select number.
-
-Example:
-
-wm8770: codec@1 {
- compatible = "wlf,wm8770";
- reg = <1>;
-};
diff --git a/Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml b/Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml
index b820c5613dcc..855aa08995b9 100644
--- a/Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml
+++ b/Documentation/devicetree/bindings/spi/airoha,en7581-snand.yaml
@@ -14,7 +14,12 @@ allOf:
properties:
compatible:
- const: airoha,en7581-snand
+ oneOf:
+ - const: airoha,en7581-snand
+ - items:
+ - enum:
+ - airoha,en7523-snand
+ - const: airoha,en7581-snand
reg:
items:
diff --git a/Documentation/devicetree/bindings/spi/aspeed,ast2600-fmc.yaml b/Documentation/devicetree/bindings/spi/aspeed,ast2600-fmc.yaml
index 57d932af4506..80e542624cc6 100644
--- a/Documentation/devicetree/bindings/spi/aspeed,ast2600-fmc.yaml
+++ b/Documentation/devicetree/bindings/spi/aspeed,ast2600-fmc.yaml
@@ -12,7 +12,7 @@ maintainers:
description: |
This binding describes the Aspeed Static Memory Controllers (FMC and
- SPI) of the AST2400, AST2500 and AST2600 SOCs.
+ SPI) of the AST2400, AST2500, AST2600 and AST2700 SOCs.
allOf:
- $ref: spi-controller.yaml#
@@ -20,6 +20,8 @@ allOf:
properties:
compatible:
enum:
+ - aspeed,ast2700-fmc
+ - aspeed,ast2700-spi
- aspeed,ast2600-fmc
- aspeed,ast2600-spi
- aspeed,ast2500-fmc
diff --git a/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml b/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml
index f2dd20370dbb..1d10cfbad86c 100644
--- a/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml
+++ b/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml
@@ -9,9 +9,6 @@ title: Freescale Quad Serial Peripheral Interface (QuadSPI)
maintainers:
- Han Xu <han.xu@nxp.com>
-allOf:
- - $ref: spi-controller.yaml#
-
properties:
compatible:
oneOf:
@@ -22,6 +19,7 @@ properties:
- fsl,imx6ul-qspi
- fsl,ls1021a-qspi
- fsl,ls2080a-qspi
+ - spacemit,k1-qspi
- items:
- enum:
- fsl,ls1043a-qspi
@@ -54,6 +52,11 @@ properties:
- const: qspi_en
- const: qspi
+ resets:
+ items:
+ - description: SoC QSPI reset
+ - description: SoC QSPI bus reset
+
required:
- compatible
- reg
@@ -62,6 +65,18 @@ required:
- clocks
- clock-names
+allOf:
+ - $ref: spi-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ not:
+ contains:
+ const: spacemit,k1-qspi
+ then:
+ properties:
+ resets: false
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml b/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml
index 62a568bdbfa0..636338d24bdf 100644
--- a/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/microchip,mpfs-spi.yaml
@@ -21,11 +21,13 @@ properties:
- microchip,mpfs-qspi
- microchip,pic64gx-qspi
- const: microchip,coreqspi-rtl-v2
- - const: microchip,coreqspi-rtl-v2 # FPGA QSPI
+ - enum:
+ - microchip,coreqspi-rtl-v2 # FPGA QSPI
+ - microchip,corespi-rtl-v5 # FPGA CoreSPI
+ - microchip,mpfs-spi
- items:
- const: microchip,pic64gx-spi
- const: microchip,mpfs-spi
- - const: microchip,mpfs-spi
reg:
maxItems: 1
@@ -39,6 +41,45 @@ properties:
clocks:
maxItems: 1
+ microchip,apb-datawidth:
+ description: APB bus data width in bits.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [8, 16, 32]
+ default: 8
+
+ microchip,frame-size:
+ description: |
+ Number of bits per SPI frame, as configured in Libero.
+ In Motorola and TI modes, this corresponds directly
+ to the requested frame size. For NSC mode this is set
+ to 9 + the required data frame size.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 4
+ maximum: 32
+ default: 8
+
+ microchip,protocol-configuration:
+ description: CoreSPI protocol selection. Determines operating mode
+ $ref: /schemas/types.yaml#/definitions/string
+ enum:
+ - motorola
+ - ti
+ - nsc
+ default: motorola
+
+ microchip,motorola-mode:
+ description: Motorola SPI mode selection
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1, 2, 3]
+ default: 3
+
+ microchip,ssel-active:
+ description: |
+ Keep SSEL asserted between frames when using the Motorola protocol.
+ When present, the controller keeps SSEL active across contiguous
+ transfers and deasserts only when the overall transfer completes.
+ type: boolean
+
required:
- compatible
- reg
@@ -71,6 +112,31 @@ allOf:
num-cs:
maximum: 1
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: microchip,corespi-rtl-v5
+ then:
+ properties:
+ num-cs:
+ minimum: 1
+ maximum: 8
+ default: 8
+
+ fifo-depth:
+ minimum: 1
+ maximum: 32
+ default: 4
+
+ else:
+ properties:
+ microchip,apb-datawidth: false
+ microchip,frame-size: false
+ microchip,protocol-configuration: false
+ microchip,motorola-mode: false
+ microchip,ssel-active: false
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt b/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt
deleted file mode 100644
index a4e72e52af59..000000000000
--- a/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Nuvoton NPCM Peripheral Serial Peripheral Interface(PSPI) controller driver
-
-Nuvoton NPCM7xx SOC support two PSPI channels.
-
-Required properties:
- - compatible : "nuvoton,npcm750-pspi" for Poleg NPCM7XX.
- "nuvoton,npcm845-pspi" for Arbel NPCM8XX.
- - #address-cells : should be 1. see spi-bus.txt
- - #size-cells : should be 0. see spi-bus.txt
- - specifies physical base address and size of the register.
- - interrupts : contain PSPI interrupt.
- - clocks : phandle of PSPI reference clock.
- - clock-names: Should be "clk_apb5".
- - pinctrl-names : a pinctrl state named "default" must be defined.
- - pinctrl-0 : phandle referencing pin configuration of the device.
- - resets : phandle to the reset control for this device.
- - cs-gpios: Specifies the gpio pins to be used for chipselects.
- See: Documentation/devicetree/bindings/spi/spi-bus.txt
-
-Optional properties:
-- clock-frequency : Input clock frequency to the PSPI block in Hz.
- Default is 25000000 Hz.
-
-spi0: spi@f0200000 {
- compatible = "nuvoton,npcm750-pspi";
- reg = <0xf0200000 0x1000>;
- pinctrl-names = "default";
- pinctrl-0 = <&pspi1_pins>;
- #address-cells = <1>;
- #size-cells = <0>;
- interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clk NPCM7XX_CLK_APB5>;
- clock-names = "clk_apb5";
- resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>
- cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
-};
diff --git a/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.yaml b/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.yaml
new file mode 100644
index 000000000000..db0fb872020a
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/nuvoton,npcm-pspi.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/nuvoton,npcm-pspi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NPCM Peripheral SPI (PSPI) Controller
+
+maintainers:
+ - Tomer Maimon <tmaimon77@gmail.com>
+
+allOf:
+ - $ref: spi-controller.yaml#
+
+description:
+ Nuvoton NPCM Peripheral Serial Peripheral Interface (PSPI) controller.
+ Nuvoton NPCM7xx SOC supports two PSPI channels.
+ Nuvoton NPCM8xx SOC support one PSPI channel.
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,npcm750-pspi # Poleg NPCM7XX
+ - nuvoton,npcm845-pspi # Arbel NPCM8XX
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+ description: PSPI reference clock.
+
+ clock-names:
+ items:
+ - const: clk_apb5
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+ - resets
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/nuvoton,npcm7xx-clock.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/nuvoton,npcm7xx-reset.h>
+ #include "dt-bindings/gpio/gpio.h"
+ spi0: spi@f0200000 {
+ compatible = "nuvoton,npcm750-pspi";
+ reg = <0xf0200000 0x1000>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pspi1_pins>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk NPCM7XX_CLK_APB5>;
+ clock-names = "clk_apb5";
+ resets = <&rstc NPCM7XX_RESET_IPSRST2 NPCM7XX_RESET_PSPI1>;
+ cs-gpios = <&gpio6 11 GPIO_ACTIVE_LOW>;
+ };
+
diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
index d12c5a060ed0..edf399681d7a 100644
--- a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.yaml
@@ -9,7 +9,7 @@ title: GENI based Qualcomm Universal Peripheral (QUP) Serial Peripheral Interfac
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The QUP v3 core is a GENI based AHB slave that provides a common data path
diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml
index cb1f15224b45..7d0571feb46d 100644
--- a/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qpic-snand.yaml
@@ -25,6 +25,8 @@ properties:
- items:
- enum:
- qcom,ipq5018-snand
+ - qcom,ipq5332-snand
+ - qcom,ipq5424-snand
- const: qcom,ipq9574-snand
- const: qcom,ipq9574-snand
diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-qup.yaml b/Documentation/devicetree/bindings/spi/qcom,spi-qup.yaml
index 88be13268962..7df21b15a0d4 100644
--- a/Documentation/devicetree/bindings/spi/qcom,spi-qup.yaml
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-qup.yaml
@@ -9,7 +9,7 @@ title: Qualcomm Universal Peripheral (QUP) Serial Peripheral Interface (SPI)
maintainers:
- Andy Gross <agross@kernel.org>
- Bjorn Andersson <bjorn.andersson@linaro.org>
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
description:
The QUP core is an AHB slave that provides a common data path (an output FIFO
diff --git a/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml
index ab27fefc3c3a..069557a587b5 100644
--- a/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml
+++ b/Documentation/devicetree/bindings/spi/renesas,rzv2h-rspi.yaml
@@ -9,12 +9,18 @@ title: Renesas RZ/V2H(P) Renesas Serial Peripheral Interface (RSPI)
maintainers:
- Fabrizio Castro <fabrizio.castro.jz@renesas.com>
-allOf:
- - $ref: spi-controller.yaml#
-
properties:
compatible:
- const: renesas,r9a09g057-rspi # RZ/V2H(P)
+ oneOf:
+ - enum:
+ - renesas,r9a09g057-rspi # RZ/V2H(P)
+ - renesas,r9a09g077-rspi # RZ/T2H
+ - items:
+ - const: renesas,r9a09g056-rspi # RZ/V2N
+ - const: renesas,r9a09g057-rspi
+ - items:
+ - const: renesas,r9a09g087-rspi # RZ/N2H
+ - const: renesas,r9a09g077-rspi # RZ/T2H
reg:
maxItems: 1
@@ -36,13 +42,12 @@ properties:
- const: tx
clocks:
+ minItems: 2
maxItems: 3
clock-names:
- items:
- - const: pclk
- - const: pclk_sfr
- - const: tclk
+ minItems: 2
+ maxItems: 3
resets:
maxItems: 2
@@ -62,12 +67,52 @@ required:
- interrupt-names
- clocks
- clock-names
- - resets
- - reset-names
- power-domains
- '#address-cells'
- '#size-cells'
+allOf:
+ - $ref: spi-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - renesas,r9a09g057-rspi
+ then:
+ properties:
+ clocks:
+ minItems: 3
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: pclk_sfr
+ - const: tclk
+
+ required:
+ - resets
+ - reset-names
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - renesas,r9a09g077-rspi
+ then:
+ properties:
+ clocks:
+ maxItems: 2
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: pclkspi
+
+ resets: false
+ reset-names: false
+
unevaluatedProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
index 0543c526b783..5c87fc8a845d 100644
--- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
+++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml
@@ -153,7 +153,7 @@ properties:
provides an interface to override the native DWC SSI CS control.
patternProperties:
- "^.*@[0-9a-f]+$":
+ "@[0-9a-f]+$":
type: object
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/spi/spi-cadence.yaml b/Documentation/devicetree/bindings/spi/spi-cadence.yaml
index 8de96abe9da1..347bed0c4956 100644
--- a/Documentation/devicetree/bindings/spi/spi-cadence.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-cadence.yaml
@@ -14,9 +14,15 @@ allOf:
properties:
compatible:
- enum:
- - cdns,spi-r1p6
- - xlnx,zynq-spi-r1p6
+ oneOf:
+ - enum:
+ - xlnx,zynq-spi-r1p6
+ - items:
+ - enum:
+ - xlnx,zynqmp-spi-r1p6
+ - xlnx,versal-net-spi-r1p6
+ - cix,sky1-spi-r1p6
+ - const: cdns,spi-r1p6
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/spi/spi-controller.yaml b/Documentation/devicetree/bindings/spi/spi-controller.yaml
index 82d051f7bd6e..3b8e990e30c4 100644
--- a/Documentation/devicetree/bindings/spi/spi-controller.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-controller.yaml
@@ -111,7 +111,7 @@ properties:
- compatible
patternProperties:
- "^.*@[0-9a-f]+$":
+ "@[0-9a-f]+$":
type: object
$ref: spi-peripheral-props.yaml
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/spi/spi-rockchip.yaml b/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
index 748faf7f7081..ce6762c92fda 100644
--- a/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
@@ -34,6 +34,7 @@ properties:
- rockchip,rk3328-spi
- rockchip,rk3368-spi
- rockchip,rk3399-spi
+ - rockchip,rk3506-spi
- rockchip,rk3528-spi
- rockchip,rk3562-spi
- rockchip,rk3568-spi
diff --git a/Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt b/Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt
deleted file mode 100644
index 12fc4ef04837..000000000000
--- a/Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt
+++ /dev/null
@@ -1,33 +0,0 @@
-Amazon's Annapurna Labs Thermal Sensor
-
-Simple thermal device that allows temperature reading by a single MMIO
-transaction.
-
-Required properties:
-- compatible: "amazon,al-thermal".
-- reg: The physical base address and length of the sensor's registers.
-- #thermal-sensor-cells: Must be 1. See Documentation/devicetree/bindings/thermal/thermal-sensor.yaml for a description.
-
-Example:
- thermal: thermal {
- compatible = "amazon,al-thermal";
- reg = <0x0 0x05002860 0x0 0x1>;
- #thermal-sensor-cells = <0x1>;
- };
-
- thermal-zones {
- thermal-z0 {
- polling-delay-passive = <250>;
- polling-delay = <1000>;
- thermal-sensors = <&thermal 0>;
- trips {
- critical {
- temperature = <105000>;
- hysteresis = <2000>;
- type = "critical";
- };
- };
-
- };
- };
-
diff --git a/Documentation/devicetree/bindings/thermal/amazon,al-thermal.yaml b/Documentation/devicetree/bindings/thermal/amazon,al-thermal.yaml
new file mode 100644
index 000000000000..6b5884d74dd6
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/amazon,al-thermal.yaml
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/amazon,al-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amazon Annapurna Labs Thermal Sensor
+
+maintainers:
+ - Talel Shenhar <talel@amazon.com>
+
+description:
+ Simple thermal device that allows temperature reading by a single MMIO
+ transaction.
+
+properties:
+ compatible:
+ items:
+ - const: amazon,al-thermal
+
+ reg:
+ maxItems: 1
+
+ '#thermal-sensor-cells':
+ const: 1
+
+additionalProperties: false
+
+examples:
+ - |
+ thermal: thermal@5002860 {
+ compatible = "amazon,al-thermal";
+ reg = <0x05002860 0x1>;
+ #thermal-sensor-cells = <0x1>;
+ };
+
+ thermal-zones {
+ z0-thermal {
+ polling-delay-passive = <250>;
+ polling-delay = <1000>;
+ thermal-sensors = <&thermal 0>;
+ trips {
+ critical {
+ temperature = <105000>;
+ hysteresis = <2000>;
+ type = "critical";
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt b/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt
deleted file mode 100644
index 3ab330219d45..000000000000
--- a/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.txt
+++ /dev/null
@@ -1,105 +0,0 @@
-* Broadcom Stingray Thermal
-
-This binding describes thermal sensors that is part of Stingray SoCs.
-
-Required properties:
-- compatible : Must be "brcm,sr-thermal"
-- reg : Memory where tmon data will be available.
-- brcm,tmon-mask: A one cell bit mask of valid TMON sources.
- Each bit represents single TMON source.
-- #thermal-sensor-cells : Thermal sensor phandler
-- polling-delay: Max number of milliseconds to wait between polls.
-- thermal-sensors: A list of thermal sensor phandles and specifier.
- specifier value is tmon ID and it should be
- in correspond with brcm,tmon-mask.
-- temperature: trip temperature threshold in millicelsius.
-
-Example:
- tmons {
- compatible = "simple-bus";
- #address-cells = <1>;
- #size-cells = <1>;
- ranges = <0x0 0x0 0x8f100000 0x100>;
-
- tmon: tmon@0 {
- compatible = "brcm,sr-thermal";
- reg = <0x0 0x40>;
- brcm,tmon-mask = <0x3f>;
- #thermal-sensor-cells = <1>;
- };
- };
-
- thermal-zones {
- ihost0_thermal: ihost0-thermal {
- polling-delay-passive = <0>;
- polling-delay = <1000>;
- thermal-sensors = <&tmon 0>;
- trips {
- cpu-crit {
- temperature = <105000>;
- hysteresis = <0>;
- type = "critical";
- };
- };
- };
- ihost1_thermal: ihost1-thermal {
- polling-delay-passive = <0>;
- polling-delay = <1000>;
- thermal-sensors = <&tmon 1>;
- trips {
- cpu-crit {
- temperature = <105000>;
- hysteresis = <0>;
- type = "critical";
- };
- };
- };
- ihost2_thermal: ihost2-thermal {
- polling-delay-passive = <0>;
- polling-delay = <1000>;
- thermal-sensors = <&tmon 2>;
- trips {
- cpu-crit {
- temperature = <105000>;
- hysteresis = <0>;
- type = "critical";
- };
- };
- };
- ihost3_thermal: ihost3-thermal {
- polling-delay-passive = <0>;
- polling-delay = <1000>;
- thermal-sensors = <&tmon 3>;
- trips {
- cpu-crit {
- temperature = <105000>;
- hysteresis = <0>;
- type = "critical";
- };
- };
- };
- crmu_thermal: crmu-thermal {
- polling-delay-passive = <0>;
- polling-delay = <1000>;
- thermal-sensors = <&tmon 4>;
- trips {
- cpu-crit {
- temperature = <105000>;
- hysteresis = <0>;
- type = "critical";
- };
- };
- };
- nitro_thermal: nitro-thermal {
- polling-delay-passive = <0>;
- polling-delay = <1000>;
- thermal-sensors = <&tmon 5>;
- trips {
- cpu-crit {
- temperature = <105000>;
- hysteresis = <0>;
- type = "critical";
- };
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.yaml b/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.yaml
new file mode 100644
index 000000000000..576a627cd599
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/brcm,sr-thermal.yaml
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/brcm,sr-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom Stingray Thermal Sensors
+
+maintainers:
+ - Ray Jui <rjui@broadcom.com>
+ - Scott Branden <sbranden@broadcom.com>
+
+allOf:
+ - $ref: thermal-sensor.yaml#
+
+properties:
+ compatible:
+ const: brcm,sr-thermal
+
+ reg:
+ maxItems: 1
+
+ brcm,tmon-mask:
+ description:
+ A one-cell bit mask of valid TMON sources. Each bit represents a single
+ TMON source.
+ $ref: /schemas/types.yaml#/definitions/uint32
+
+ '#thermal-sensor-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - brcm,tmon-mask
+
+additionalProperties: false
+
+examples:
+ - |
+ tmon: thermal-sensor@0 {
+ compatible = "brcm,sr-thermal";
+ reg = <0x0 0x40>;
+ brcm,tmon-mask = <0x3f>;
+ #thermal-sensor-cells = <1>;
+ };
+
+ thermal-zones {
+ ihost0_thermal: ihost0-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&tmon 0>;
+ trips {
+ cpu-crit {
+ temperature = <105000>;
+ hysteresis = <0>;
+ type = "critical";
+ };
+ };
+ };
+ ihost1_thermal: ihost1-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&tmon 1>;
+ trips {
+ cpu-crit {
+ temperature = <105000>;
+ hysteresis = <0>;
+ type = "critical";
+ };
+ };
+ };
+ ihost2_thermal: ihost2-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&tmon 2>;
+ trips {
+ cpu-crit {
+ temperature = <105000>;
+ hysteresis = <0>;
+ type = "critical";
+ };
+ };
+ };
+ ihost3_thermal: ihost3-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&tmon 3>;
+ trips {
+ cpu-crit {
+ temperature = <105000>;
+ hysteresis = <0>;
+ type = "critical";
+ };
+ };
+ };
+ crmu_thermal: crmu-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&tmon 4>;
+ trips {
+ cpu-crit {
+ temperature = <105000>;
+ hysteresis = <0>;
+ type = "critical";
+ };
+ };
+ };
+ nitro_thermal: nitro-thermal {
+ polling-delay-passive = <0>;
+ polling-delay = <1000>;
+ thermal-sensors = <&tmon 5>;
+ trips {
+ cpu-crit {
+ temperature = <105000>;
+ hysteresis = <0>;
+ type = "critical";
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/thermal/db8500-thermal.txt b/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
deleted file mode 100644
index 2e1c06fad81f..000000000000
--- a/Documentation/devicetree/bindings/thermal/db8500-thermal.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-* ST-Ericsson DB8500 Thermal
-
-** Thermal node properties:
-
-- compatible : "stericsson,db8500-thermal";
-- reg : address range of the thermal sensor registers;
-- interrupts : interrupts generated from PRCMU;
-- interrupt-names : "IRQ_HOTMON_LOW" and "IRQ_HOTMON_HIGH";
-- num-trips : number of total trip points, this is required, set it 0 if none,
- if greater than 0, the following properties must be defined;
-- tripN-temp : temperature of trip point N, should be in ascending order;
-- tripN-type : type of trip point N, should be one of "active" "passive" "hot"
- "critical";
-- tripN-cdev-num : number of the cooling devices which can be bound to trip
- point N, this is required if trip point N is defined, set it 0 if none,
- otherwise the following cooling device names must be defined;
-- tripN-cdev-nameM : name of the No. M cooling device of trip point N;
-
-Usually the num-trips and tripN-*** are separated in board related dts files.
-
-Example:
-thermal@801573c0 {
- compatible = "stericsson,db8500-thermal";
- reg = <0x801573c0 0x40>;
- interrupts = <21 0x4>, <22 0x4>;
- interrupt-names = "IRQ_HOTMON_LOW", "IRQ_HOTMON_HIGH";
-
- num-trips = <3>;
-
- trip0-temp = <75000>;
- trip0-type = "active";
- trip0-cdev-num = <1>;
- trip0-cdev-name0 = "thermal-cpufreq-0";
-
- trip1-temp = <80000>;
- trip1-type = "active";
- trip1-cdev-num = <2>;
- trip1-cdev-name0 = "thermal-cpufreq-0";
- trip1-cdev-name1 = "thermal-fan";
-
- trip2-temp = <85000>;
- trip2-type = "critical";
- trip2-cdev-num = <0>;
-}
diff --git a/Documentation/devicetree/bindings/thermal/fsl,imx91-tmu.yaml b/Documentation/devicetree/bindings/thermal/fsl,imx91-tmu.yaml
new file mode 100644
index 000000000000..7fd1a86d7287
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/fsl,imx91-tmu.yaml
@@ -0,0 +1,87 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/fsl,imx91-tmu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP i.MX91 Thermal
+
+maintainers:
+ - Pengfei Li <pengfei.li_1@nxp.com>
+
+description:
+ i.MX91 features a new temperature sensor. It includes programmable
+ temperature threshold comparators for both normal and privileged
+ accesses and allows a programmable measurement frequency for the
+ Periodic One-Shot Measurement mode. Additionally, it provides
+ status registers for indicating the end of measurement and threshold
+ violation events.
+
+properties:
+ compatible:
+ items:
+ - const: fsl,imx91-tmu
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ interrupts:
+ items:
+ - description: Comparator 1 irq
+ - description: Comparator 2 irq
+ - description: Data ready irq
+
+ interrupt-names:
+ items:
+ - const: thr1
+ - const: thr2
+ - const: ready
+
+ nvmem-cells:
+ items:
+ - description: Phandle to the trim control 1 provided by ocotp
+ - description: Phandle to the trim control 2 provided by ocotp
+
+ nvmem-cell-names:
+ items:
+ - const: trim1
+ - const: trim2
+
+ "#thermal-sensor-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - interrupts
+ - interrupt-names
+
+allOf:
+ - $ref: thermal-sensor.yaml
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/imx93-clock.h>
+
+ thermal-sensor@44482000 {
+ compatible = "fsl,imx91-tmu";
+ reg = <0x44482000 0x1000>;
+ #thermal-sensor-cells = <0>;
+ clocks = <&clk IMX93_CLK_TMC_GATE>;
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "thr1", "thr2", "ready";
+ nvmem-cells = <&tmu_trim1>, <&tmu_trim2>;
+ nvmem-cell-names = "trim1", "trim2";
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
index 78e2f6573b96..3c5256b0cd9f 100644
--- a/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
+++ b/Documentation/devicetree/bindings/thermal/qcom-tsens.yaml
@@ -36,10 +36,15 @@ properties:
- qcom,msm8974-tsens
- const: qcom,tsens-v0_1
+ - description:
+ v1 of TSENS without RPM which requires to be explicitly reset
+ and enabled in the driver.
+ enum:
+ - qcom,ipq5018-tsens
+
- description: v1 of TSENS
items:
- enum:
- - qcom,ipq5018-tsens
- qcom,msm8937-tsens
- qcom,msm8956-tsens
- qcom,msm8976-tsens
@@ -50,11 +55,13 @@ properties:
items:
- enum:
- qcom,glymur-tsens
+ - qcom,kaanapali-tsens
- qcom,milos-tsens
- qcom,msm8953-tsens
- qcom,msm8996-tsens
- qcom,msm8998-tsens
- qcom,qcm2290-tsens
+ - qcom,qcs8300-tsens
- qcom,qcs615-tsens
- qcom,sa8255p-tsens
- qcom,sa8775p-tsens
diff --git a/Documentation/devicetree/bindings/thermal/renesas,r9a09g047-tsu.yaml b/Documentation/devicetree/bindings/thermal/renesas,r9a09g047-tsu.yaml
index 8d3f3c24f0f2..befdc8b7a082 100644
--- a/Documentation/devicetree/bindings/thermal/renesas,r9a09g047-tsu.yaml
+++ b/Documentation/devicetree/bindings/thermal/renesas,r9a09g047-tsu.yaml
@@ -16,7 +16,11 @@ description:
properties:
compatible:
- const: renesas,r9a09g047-tsu
+ oneOf:
+ - const: renesas,r9a09g047-tsu # RZ/G3E
+ - items:
+ - const: renesas,r9a09g057-tsu # RZ/V2H
+ - const: renesas,r9a09g047-tsu # RZ/G3E
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/timer/nvidia,tegra-timer.yaml b/Documentation/devicetree/bindings/timer/nvidia,tegra-timer.yaml
index 9ea2ea3a7599..adf208b7a5b9 100644
--- a/Documentation/devicetree/bindings/timer/nvidia,tegra-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/nvidia,tegra-timer.yaml
@@ -100,7 +100,6 @@ properties:
items:
- const: timer
-
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/timer/nvidia,tegra186-timer.yaml b/Documentation/devicetree/bindings/timer/nvidia,tegra186-timer.yaml
index 76516e18e042..1d0bd36907ed 100644
--- a/Documentation/devicetree/bindings/timer/nvidia,tegra186-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/nvidia,tegra186-timer.yaml
@@ -15,7 +15,6 @@ description: >
reference generated by USEC, TSC or either clk_m or OSC. Each TMR can be
programmed to generate one-shot, periodic, or watchdog interrupts.
-
properties:
compatible:
oneOf:
diff --git a/Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml b/Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml
new file mode 100644
index 000000000000..e08d3d2d306b
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/realtek,rtd1625-systimer.yaml
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/timer/realtek,rtd1625-systimer.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek System Timer
+
+maintainers:
+ - Hao-Wen Ting <haowen.ting@realtek.com>
+
+description:
+ The Realtek SYSTIMER (System Timer) is a 64-bit global hardware counter operating
+ at a fixed 1MHz frequency. Thanks to its compare match interrupt capability,
+ the timer natively supports oneshot mode for tick broadcast functionality.
+
+properties:
+ compatible:
+ oneOf:
+ - const: realtek,rtd1625-systimer
+ - items:
+ - const: realtek,rtd1635-systimer
+ - const: realtek,rtd1625-systimer
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ timer@89420 {
+ compatible = "realtek,rtd1635-systimer",
+ "realtek,rtd1625-systimer";
+ reg = <0x89420 0x18>;
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml
index 58ff948d93c9..057b32048f53 100644
--- a/Documentation/devicetree/bindings/trivial-devices.yaml
+++ b/Documentation/devicetree/bindings/trivial-devices.yaml
@@ -43,8 +43,14 @@ properties:
- adi,ad5110
# Temperature sensor with integrated fan control
- adi,adm1027
+ # Analog Devices ADT7410 High Accuracy Digital Temperature Sensor
+ - adi,adt7410
# Analog Devices ADT7411 Temperature Sensor and 8-channel ADC
- adi,adt7411
+ # Analog Devices ADT7420 High Accuracy Digital Temperature Sensor
+ - adi,adt7420
+ # Analog Devices ADT7422 High Accuracy Digital Temperature Sensor
+ - adi,adt7422
# Temperature sensor with integrated fan control
- adi,adt7463
# Temperature sensor with integrated fan control
@@ -53,6 +59,8 @@ properties:
- adi,lt7182s
# AMS iAQ-Core VOC Sensor
- ams,iaq-core
+ # Arduino microcontroller interface over SPI on UnoQ board
+ - arduino,unoq-mcu
# Temperature monitoring of Astera Labs PT5161L PCIe retimer
- asteralabs,pt5161l
# i2c h/w elliptic curve crypto module
@@ -297,6 +305,10 @@ properties:
- mps,mp2888
# Monolithic Power Systems Inc. multi-phase controller mp2891
- mps,mp2891
+ # Monolithic Power Systems Inc. multi-phase controller mp2925
+ - mps,mp2925
+ # Monolithic Power Systems Inc. multi-phase controller mp2929
+ - mps,mp2929
# Monolithic Power Systems Inc. multi-phase controller mp29502
- mps,mp29502
# Monolithic Power Systems Inc. multi-phase controller mp29608
@@ -317,6 +329,8 @@ properties:
- mps,mp5998
# Monolithic Power Systems Inc. digital step-down converter mp9941
- mps,mp9941
+ # Monolithic Power Systems Inc. digital step-down converter mp9945
+ - mps,mp9945
# Temperature sensor with integrated fan control
- national,lm63
# Temperature sensor with integrated fan control
diff --git a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
index aaa0bbb5bfe1..cea84ab2204f 100644
--- a/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
+++ b/Documentation/devicetree/bindings/ufs/qcom,sm8650-ufshc.yaml
@@ -15,6 +15,7 @@ select:
compatible:
contains:
enum:
+ - qcom,kaanapali-ufshc
- qcom,sm8650-ufshc
- qcom,sm8750-ufshc
required:
@@ -24,6 +25,7 @@ properties:
compatible:
items:
- enum:
+ - qcom,kaanapali-ufshc
- qcom,sm8650-ufshc
- qcom,sm8750-ufshc
- const: qcom,ufshc
diff --git a/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml b/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml
index e3a7df91f7f1..89b1fb90aeeb 100644
--- a/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml
+++ b/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml
@@ -76,6 +76,7 @@ required:
allOf:
- $ref: usb-switch.yaml#
+ - $ref: usb-switch-ports.yaml#
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml
index baf130669c38..73e7a60a0060 100644
--- a/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml
+++ b/Documentation/devicetree/bindings/usb/fsl,imx8mp-dwc3.yaml
@@ -89,13 +89,21 @@ required:
- reg
- "#address-cells"
- "#size-cells"
- - dma-ranges
- ranges
- clocks
- clock-names
- interrupts
- power-domains
+allOf:
+ - if:
+ properties:
+ compatible:
+ const: fsl,imx8mp-dwc3
+ then:
+ required:
+ - dma-ranges
+
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml b/Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml
index e588514fab2d..793662f6f3bf 100644
--- a/Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml
+++ b/Documentation/devicetree/bindings/usb/gpio-sbu-mux.yaml
@@ -52,6 +52,7 @@ required:
allOf:
- $ref: usb-switch.yaml#
+ - $ref: usb-switch-ports.yaml#
- if:
required:
- mode-switch
diff --git a/Documentation/devicetree/bindings/usb/nxp,ptn36502.yaml b/Documentation/devicetree/bindings/usb/nxp,ptn36502.yaml
index d805dde80796..4d2fcaa71870 100644
--- a/Documentation/devicetree/bindings/usb/nxp,ptn36502.yaml
+++ b/Documentation/devicetree/bindings/usb/nxp,ptn36502.yaml
@@ -46,6 +46,7 @@ required:
allOf:
- $ref: usb-switch.yaml#
+ - $ref: usb-switch-ports.yaml#
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml b/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml
index 589914d22bf2..25fab5fdc2cd 100644
--- a/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml
+++ b/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml
@@ -91,6 +91,7 @@ required:
allOf:
- $ref: usb-switch.yaml#
+ - $ref: usb-switch-ports.yaml#
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/usb/parade,ps8830.yaml b/Documentation/devicetree/bindings/usb/parade,ps8830.yaml
index aeb33667818e..eaeab1c01a59 100644
--- a/Documentation/devicetree/bindings/usb/parade,ps8830.yaml
+++ b/Documentation/devicetree/bindings/usb/parade,ps8830.yaml
@@ -81,6 +81,7 @@ required:
allOf:
- $ref: usb-switch.yaml#
+ - $ref: usb-switch-ports.yaml#
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml
index 6d3ef364672e..6d3fa2bc9cee 100644
--- a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml
+++ b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml
@@ -28,7 +28,6 @@ properties:
- qcom,pm4125-typec
- const: qcom,pmi632-typec
-
connector:
type: object
$ref: /schemas/connector/usb-connector.yaml#
diff --git a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml
index dfd084ed9024..d49a58d5478f 100644
--- a/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml
+++ b/Documentation/devicetree/bindings/usb/qcom,snps-dwc3.yaml
@@ -68,6 +68,7 @@ properties:
- qcom,sm8550-dwc3
- qcom,sm8650-dwc3
- qcom,x1e80100-dwc3
+ - qcom,x1e80100-dwc3-mp
- const: qcom,snps-dwc3
reg:
@@ -460,8 +461,10 @@ allOf:
then:
properties:
interrupts:
+ minItems: 4
maxItems: 5
interrupt-names:
+ minItems: 4
items:
- const: dwc_usb3
- const: pwr_event
diff --git a/Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml b/Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml
index 96346723f3e9..96dcec9b7620 100644
--- a/Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml
+++ b/Documentation/devicetree/bindings/usb/qcom,wcd939x-usbss.yaml
@@ -60,6 +60,7 @@ required:
allOf:
- $ref: usb-switch.yaml#
+ - $ref: usb-switch-ports.yaml#
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml b/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml
index f713cac4a8ac..e1501ea6b50b 100644
--- a/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml
+++ b/Documentation/devicetree/bindings/usb/ti,tusb1046.yaml
@@ -11,6 +11,7 @@ maintainers:
allOf:
- $ref: usb-switch.yaml#
+ - $ref: usb-switch-ports.yaml#
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/usb/usb-switch-ports.yaml b/Documentation/devicetree/bindings/usb/usb-switch-ports.yaml
new file mode 100644
index 000000000000..6bf0c97e30ae
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/usb-switch-ports.yaml
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/usb-switch-ports.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: USB Orientation and Mode Switches Ports Graph Properties
+
+maintainers:
+ - Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+description:
+ Ports Graph properties for devices handling USB mode and orientation switching.
+
+properties:
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ description:
+ A port node to link the device to a TypeC controller for the purpose of
+ handling altmode muxing and orientation switching.
+
+ properties:
+ endpoint:
+ $ref: /schemas/graph.yaml#/$defs/endpoint-base
+ unevaluatedProperties: false
+ properties:
+ data-lanes:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 8
+ uniqueItems: true
+ items:
+ maximum: 8
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Super Speed (SS) Output endpoint to the Type-C connector
+
+ port@1:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ description:
+ Super Speed (SS) Input endpoint from the Super-Speed PHY
+ unevaluatedProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/graph.yaml#/$defs/endpoint-base
+ unevaluatedProperties: false
+ properties:
+ data-lanes:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 8
+ uniqueItems: true
+ items:
+ maximum: 8
+
+oneOf:
+ - required:
+ - port
+ - required:
+ - ports
+
+additionalProperties: true
diff --git a/Documentation/devicetree/bindings/usb/usb-switch.yaml b/Documentation/devicetree/bindings/usb/usb-switch.yaml
index 896201912630..f77731493dc4 100644
--- a/Documentation/devicetree/bindings/usb/usb-switch.yaml
+++ b/Documentation/devicetree/bindings/usb/usb-switch.yaml
@@ -25,56 +25,4 @@ properties:
description: Possible handler of SuperSpeed signals retiming
type: boolean
- port:
- $ref: /schemas/graph.yaml#/$defs/port-base
- description:
- A port node to link the device to a TypeC controller for the purpose of
- handling altmode muxing and orientation switching.
-
- properties:
- endpoint:
- $ref: /schemas/graph.yaml#/$defs/endpoint-base
- unevaluatedProperties: false
- properties:
- data-lanes:
- $ref: /schemas/types.yaml#/definitions/uint32-array
- minItems: 1
- maxItems: 8
- uniqueItems: true
- items:
- maximum: 8
-
- ports:
- $ref: /schemas/graph.yaml#/properties/ports
- properties:
- port@0:
- $ref: /schemas/graph.yaml#/properties/port
- description:
- Super Speed (SS) Output endpoint to the Type-C connector
-
- port@1:
- $ref: /schemas/graph.yaml#/$defs/port-base
- description:
- Super Speed (SS) Input endpoint from the Super-Speed PHY
- unevaluatedProperties: false
-
- properties:
- endpoint:
- $ref: /schemas/graph.yaml#/$defs/endpoint-base
- unevaluatedProperties: false
- properties:
- data-lanes:
- $ref: /schemas/types.yaml#/definitions/uint32-array
- minItems: 1
- maxItems: 8
- uniqueItems: true
- items:
- maximum: 8
-
-oneOf:
- - required:
- - port
- - required:
- - ports
-
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index f1d1882009ba..2bac488d2a9a 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -20,7 +20,7 @@ patternProperties:
"^(keypad|m25p|max8952|max8997|max8998|mpmc),.*": true
"^(pciclass|pinctrl-single|#pinctrl-single|PowerPC),.*": true
"^(pl022|pxa-mmc|rcar_sound|rotary-encoder|s5m8767|sdhci),.*": true
- "^(simple-audio-card|st-plgpio|st-spics|ts),.*": true
+ "^(simple-audio-card|st-plgpio|st-spics|ts|vsc8531),.*": true
"^pool[0-3],.*": true
# Keep list in alphabetical order.
@@ -176,6 +176,8 @@ patternProperties:
description: All Sensors Corporation
"^asix,.*":
description: ASIX Electronics Corporation
+ "^asl-tek,.*":
+ description: ASL Xiamen Technology Co., Ltd.
"^aspeed,.*":
description: ASPEED Technology Inc.
"^asrock,.*":
@@ -570,6 +572,8 @@ patternProperties:
description: Foxconn Industrial Internet
"^firefly,.*":
description: Firefly
+ "^fitipower,.*":
+ description: Fitipower Integrated Technology Inc.
"^flipkart,.*":
description: Flipkart Inc.
"^focaltech,.*":
@@ -835,6 +839,8 @@ patternProperties:
description: JOZ BV
"^jty,.*":
description: JTY
+ "^jutouch,.*":
+ description: JuTouch Technology Co., Ltd.
"^kam,.*":
description: Kamstrup A/S
"^karo,.*":
@@ -1323,6 +1329,8 @@ patternProperties:
description: Raumfeld GmbH
"^raydium,.*":
description: Raydium Semiconductor Corp.
+ "^raystar,.*":
+ description: Raystar Optronics, Inc.
"^rda,.*":
description: Unisoc Communications, Inc.
"^realtek,.*":
@@ -1705,6 +1713,8 @@ patternProperties:
description: Universal Scientific Industrial Co., Ltd.
"^usr,.*":
description: U.S. Robotics Corporation
+ "^ultrarisc,.*":
+ description: UltraRISC Technology Co., Ltd.
"^ultratronik,.*":
description: Ultratronik GmbH
"^utoo,.*":
diff --git a/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml
index dc6af204e8af..a519422c371c 100644
--- a/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml
+++ b/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm PM8916 watchdog timer controller
maintainers:
- - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+ - Krzysztof Kozlowski <krzk@kernel.org>
allOf:
- $ref: watchdog.yaml#
diff --git a/Documentation/doc-guide/checktransupdate.rst b/Documentation/doc-guide/checktransupdate.rst
index dfaf9d373747..7b25375cc6d9 100644
--- a/Documentation/doc-guide/checktransupdate.rst
+++ b/Documentation/doc-guide/checktransupdate.rst
@@ -27,15 +27,15 @@ Usage
::
- ./scripts/checktransupdate.py --help
+ tools/docs/checktransupdate.py --help
Please refer to the output of argument parser for usage details.
Samples
-- ``./scripts/checktransupdate.py -l zh_CN``
+- ``tools/docs/checktransupdate.py -l zh_CN``
This will print all the files that need to be updated in the zh_CN locale.
-- ``./scripts/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst``
+- ``tools/docs/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst``
This will only print the status of the specified file.
Then the output is something like:
diff --git a/Documentation/doc-guide/contributing.rst b/Documentation/doc-guide/contributing.rst
index 662c7a840cd5..f8047e633113 100644
--- a/Documentation/doc-guide/contributing.rst
+++ b/Documentation/doc-guide/contributing.rst
@@ -152,7 +152,7 @@ generate links to that documentation. Adding ``kernel-doc`` directives to
the documentation to bring those comments in can help the community derive
the full value of the work that has gone into creating them.
-The ``scripts/find-unused-docs.sh`` tool can be used to find these
+The ``tools/docs/find-unused-docs.sh`` tool can be used to find these
overlooked comments.
Note that the most value comes from pulling in the documentation for
diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst
index af9697e60165..fd89a6d56ea9 100644
--- a/Documentation/doc-guide/kernel-doc.rst
+++ b/Documentation/doc-guide/kernel-doc.rst
@@ -405,6 +405,10 @@ Domain`_ references.
``%CONST``
Name of a constant. (No cross-referencing, just formatting.)
+ Examples::
+
+ %0 %NULL %-1 %-EFAULT %-EINVAL %-ENOMEM
+
````literal````
A literal block that should be handled as-is. The output will use a
``monospaced font``.
@@ -579,20 +583,23 @@ source.
How to use kernel-doc to generate man pages
-------------------------------------------
-If you just want to use kernel-doc to generate man pages you can do this
-from the kernel git tree::
+To generate man pages for all files that contain kernel-doc markups, run::
+
+ $ make mandocs
- $ scripts/kernel-doc -man \
- $(git grep -l '/\*\*' -- :^Documentation :^tools) \
- | scripts/split-man.pl /tmp/man
+Or calling ``script-build-wrapper`` directly::
-Some older versions of git do not support some of the variants of syntax for
-path exclusion. One of the following commands may work for those versions::
+ $ ./tools/docs/sphinx-build-wrapper mandocs
- $ scripts/kernel-doc -man \
- $(git grep -l '/\*\*' -- . ':!Documentation' ':!tools') \
- | scripts/split-man.pl /tmp/man
+The output will be at ``/man`` directory inside the output directory
+(by default: ``Documentation/output``).
+
+Optionally, it is possible to generate a partial set of man pages by
+using SPHINXDIRS:
+
+ $ make SPHINXDIRS=driver-api/media mandocs
+
+.. note::
- $ scripts/kernel-doc -man \
- $(git grep -l '/\*\*' -- . ":(exclude)Documentation" ":(exclude)tools") \
- | scripts/split-man.pl /tmp/man
+ When SPHINXDIRS={subdir} is used, it will only generate man pages for
+ the files explicitly inside a ``Documentation/{subdir}/.../*.rst`` file.
diff --git a/Documentation/doc-guide/parse-headers.rst b/Documentation/doc-guide/parse-headers.rst
index 204b025f1349..a7bb01ff04eb 100644
--- a/Documentation/doc-guide/parse-headers.rst
+++ b/Documentation/doc-guide/parse-headers.rst
@@ -5,173 +5,168 @@ Including uAPI header files
Sometimes, it is useful to include header files and C example codes in
order to describe the userspace API and to generate cross-references
between the code and the documentation. Adding cross-references for
-userspace API files has an additional vantage: Sphinx will generate warnings
+userspace API files has an additional advantage: Sphinx will generate warnings
if a symbol is not found at the documentation. That helps to keep the
uAPI documentation in sync with the Kernel changes.
-The :ref:`parse_headers.pl <parse_headers>` provide a way to generate such
+The :ref:`parse_headers.py <parse_headers>` provides a way to generate such
cross-references. It has to be called via Makefile, while building the
documentation. Please see ``Documentation/userspace-api/media/Makefile`` for an example
about how to use it inside the Kernel tree.
.. _parse_headers:
-parse_headers.pl
-^^^^^^^^^^^^^^^^
+tools/docs/parse_headers.py
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
NAME
****
-
-parse_headers.pl - parse a C file, in order to identify functions, structs,
+parse_headers.py - parse a C file, in order to identify functions, structs,
enums and defines and create cross-references to a Sphinx book.
+USAGE
+*****
+
+parse-headers.py [-h] [-d] [-t] ``FILE_IN`` ``FILE_OUT`` ``FILE_RULES``
SYNOPSIS
********
-
-\ **parse_headers.pl**\ [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>]
-
-Where <options> can be: --debug, --help or --usage.
-
-
-OPTIONS
-*******
-
-
-
-\ **--debug**\
-
- Put the script in verbose mode, useful for debugging.
-
-
-
-\ **--usage**\
-
- Prints a brief help message and exits.
-
-
-
-\ **--help**\
-
- Prints a more detailed help message and exits.
-
-
-DESCRIPTION
-***********
-
-
-Convert a C header or source file (C_FILE), into a reStructuredText
+Converts a C header or source file ``FILE_IN`` into a ReStructured Text
included via ..parsed-literal block with cross-references for the
documentation files that describe the API. It accepts an optional
-EXCEPTIONS_FILE with describes what elements will be either ignored or
-be pointed to a non-default reference.
-
-The output is written at the (OUT_FILE).
+``FILE_RULES`` file to describe what elements will be either ignored or
+be pointed to a non-default reference type/name.
-It is capable of identifying defines, functions, structs, typedefs,
-enums and enum symbols and create cross-references for all of them.
-It is also capable of distinguish #define used for specifying a Linux
-ioctl.
+The output is written at ``FILE_OUT``.
-The EXCEPTIONS_FILE contain two types of statements: \ **ignore**\ or \ **replace**\ .
+It is capable of identifying ``define``, ``struct``, ``typedef``, ``enum``
+and enum ``symbol``, creating cross-references for all of them.
-The syntax for the ignore tag is:
+It is also capable of distinguishing ``#define`` used for specifying
+Linux-specific macros used to define ``ioctl``.
+The optional ``FILE_RULES`` contains a set of rules like::
-ignore \ **type**\ \ **name**\
+ ignore ioctl VIDIOC_ENUM_FMT
+ replace ioctl VIDIOC_DQBUF vidioc_qbuf
+ replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`v4l2_event_motion_det`
-The \ **ignore**\ means that it won't generate cross references for a
-\ **name**\ symbol of type \ **type**\ .
+POSITIONAL ARGUMENTS
+********************
-The syntax for the replace tag is:
+ ``FILE_IN``
+ Input C file
+ ``FILE_OUT``
+ Output RST file
-replace \ **type**\ \ **name**\ \ **new_value**\
+ ``FILE_RULES``
+ Exceptions file (optional)
-The \ **replace**\ means that it will generate cross references for a
-\ **name**\ symbol of type \ **type**\ , but, instead of using the default
-replacement rule, it will use \ **new_value**\ .
-
-For both statements, \ **type**\ can be either one of the following:
+OPTIONS
+*******
+ ``-h``, ``--help``
+ show a help message and exit
+ ``-d``, ``--debug``
+ Increase debug level. Can be used multiple times
+ ``-t``, ``--toc``
+ instead of a literal block, outputs a TOC table at the RST file
-\ **ioctl**\
- The ignore or replace statement will apply to ioctl definitions like:
+DESCRIPTION
+***********
- #define VIDIOC_DBG_S_REGISTER _IOW('V', 79, struct v4l2_dbg_register)
+Creates an enriched version of a Kernel header file with cross-links
+to each C data structure type, from ``FILE_IN``, formatting it with
+reStructuredText notation, either as-is or as a table of contents.
+It accepts an optional ``FILE_RULES`` which describes what elements will be
+either ignored or be pointed to a non-default reference, and optionally
+defines the C namespace to be used.
+It is meant to allow having more comprehensive documentation, where
+uAPI headers will create cross-reference links to the code.
-\ **define**\
+The output is written at the ``FILE_OUT``.
- The ignore or replace statement will apply to any other #define found
- at C_FILE.
+The ``FILE_RULES`` may contain contain three types of statements:
+**ignore**, **replace** and **namespace**.
+By default, it create rules for all symbols and defines, but it also
+allows parsing an exception file. Such file contains a set of rules
+using the syntax below:
+1. Ignore rules:
-\ **typedef**\
+ ignore *type* *symbol*
- The ignore or replace statement will apply to typedef statements at C_FILE.
+Removes the symbol from reference generation.
+2. Replace rules:
+ replace *type* *old_symbol* *new_reference*
-\ **struct**\
+ Replaces *old_symbol* with a *new_reference*.
+ The *new_reference* can be:
- The ignore or replace statement will apply to the name of struct statements
- at C_FILE.
+ - A simple symbol name;
+ - A full Sphinx reference.
+3. Namespace rules
+ namespace *namespace*
-\ **enum**\
+ Sets C *namespace* to be used during cross-reference generation. Can
+ be overridden by replace rules.
- The ignore or replace statement will apply to the name of enum statements
- at C_FILE.
+On ignore and replace rules, *type* can be:
+ - ioctl:
+ for defines of the form ``_IO*``, e.g., ioctl definitions
+ - define:
+ for other defines
-\ **symbol**\
+ - symbol:
+ for symbols defined within enums;
- The ignore or replace statement will apply to the name of enum value
- at C_FILE.
+ - typedef:
+ for typedefs;
- For replace statements, \ **new_value**\ will automatically use :c:type:
- references for \ **typedef**\ , \ **enum**\ and \ **struct**\ types. It will use :ref:
- for \ **ioctl**\ , \ **define**\ and \ **symbol**\ types. The type of reference can
- also be explicitly defined at the replace statement.
+ - enum:
+ for the name of a non-anonymous enum;
+ - struct:
+ for structs.
EXAMPLES
********
+- Ignore a define ``_VIDEODEV2_H`` at ``FILE_IN``::
-ignore define _VIDEODEV2_H
-
-
-Ignore a #define _VIDEODEV2_H at the C_FILE.
-
-ignore symbol PRIVATE
-
+ ignore define _VIDEODEV2_H
-On a struct like:
+- On an data structure like this enum::
-enum foo { BAR1, BAR2, PRIVATE };
+ enum foo { BAR1, BAR2, PRIVATE };
-It won't generate cross-references for \ **PRIVATE**\ .
+ It won't generate cross-references for ``PRIVATE``::
-replace symbol BAR1 :c:type:\`foo\`
-replace symbol BAR2 :c:type:\`foo\`
+ ignore symbol PRIVATE
+ At the same struct, instead of creating one cross reference per symbol,
+ make them all point to the ``enum foo`` C type::
-On a struct like:
+ replace symbol BAR1 :c:type:\`foo\`
+ replace symbol BAR2 :c:type:\`foo\`
-enum foo { BAR1, BAR2, PRIVATE };
-It will make the BAR1 and BAR2 enum symbols to cross reference the foo
-symbol at the C domain.
+- Use C namespace ``MC`` for all symbols at ``FILE_IN``::
+ namespace MC
BUGS
****
@@ -184,7 +179,7 @@ COPYRIGHT
*********
-Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab+samsung@kernel.org>.
+Copyright (c) 2016, 2025 by Mauro Carvalho Chehab <mchehab+huawei@kernel.org>.
License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl.html>.
diff --git a/Documentation/doc-guide/sphinx.rst b/Documentation/doc-guide/sphinx.rst
index 607589592bfb..51c370260f3b 100644
--- a/Documentation/doc-guide/sphinx.rst
+++ b/Documentation/doc-guide/sphinx.rst
@@ -106,7 +106,7 @@ There's a script that automatically checks for Sphinx dependencies. If it can
recognize your distribution, it will also give a hint about the install
command line options for your distro::
- $ ./scripts/sphinx-pre-install
+ $ ./tools/docs/sphinx-pre-install
Checking if the needed tools for Fedora release 26 (Twenty Six) are available
Warning: better to also install "texlive-luatex85".
You should run:
@@ -116,7 +116,7 @@ command line options for your distro::
. sphinx_2.4.4/bin/activate
pip install -r Documentation/sphinx/requirements.txt
- Can't build as 1 mandatory dependency is missing at ./scripts/sphinx-pre-install line 468.
+ Can't build as 1 mandatory dependency is missing at ./tools/docs/sphinx-pre-install line 468.
By default, it checks all the requirements for both html and PDF, including
the requirements for images, math expressions and LaTeX build, and assumes
@@ -149,7 +149,7 @@ a venv with it with, and install minimal requirements with::
A more comprehensive test can be done by using:
- scripts/test_doc_build.py
+ tools/docs/test_doc_build.py
Such script create one Python venv per supported version,
optionally building documentation for a range of Sphinx versions.
diff --git a/Documentation/driver-api/cxl/allocation/page-allocator.rst b/Documentation/driver-api/cxl/allocation/page-allocator.rst
index 7b8fe1b8d5bb..3fa584a248bd 100644
--- a/Documentation/driver-api/cxl/allocation/page-allocator.rst
+++ b/Documentation/driver-api/cxl/allocation/page-allocator.rst
@@ -41,37 +41,6 @@ To simplify this, the page allocator will prefer :code:`ZONE_MOVABLE` over
will fallback to allocate from :code:`ZONE_NORMAL`.
-Zone and Node Quirks
-====================
-Let's consider a configuration where the local DRAM capacity is largely onlined
-into :code:`ZONE_NORMAL`, with no :code:`ZONE_MOVABLE` capacity present. The
-CXL capacity has the opposite configuration - all onlined in
-:code:`ZONE_MOVABLE`.
-
-Under the default allocation policy, the page allocator will completely skip
-:code:`ZONE_MOVABLE` as a valid allocation target. This is because, as of
-Linux v6.15, the page allocator does (approximately) the following: ::
-
- for (each zone in local_node):
-
- for (each node in fallback_order):
-
- attempt_allocation(gfp_flags);
-
-Because the local node does not have :code:`ZONE_MOVABLE`, the CXL node is
-functionally unreachable for direct allocation. As a result, the only way
-for CXL capacity to be used is via `demotion` in the reclaim path.
-
-This configuration also means that if the DRAM ndoe has :code:`ZONE_MOVABLE`
-capacity - when that capacity is depleted, the page allocator will actually
-prefer CXL :code:`ZONE_MOVABLE` pages over DRAM :code:`ZONE_NORMAL` pages.
-
-We may wish to invert this priority in future Linux versions.
-
-If `demotion` and `swap` are disabled, Linux will begin to cause OOM crashes
-when the DRAM nodes are depleted. See the reclaim section for more details.
-
-
CGroups and CPUSets
===================
Finally, assuming CXL memory is reachable via the page allocation (i.e. onlined
diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
index be1fc643b645..83118c728ed9 100644
--- a/Documentation/driver-api/dpll.rst
+++ b/Documentation/driver-api/dpll.rst
@@ -198,26 +198,28 @@ be requested with the same attribute with ``DPLL_CMD_DEVICE_SET`` command.
================================== ======================================
Device may also provide ability to adjust a signal phase on a pin.
-If pin phase adjustment is supported, minimal and maximal values that pin
-handle shall be provide to the user on ``DPLL_CMD_PIN_GET`` respond
-with ``DPLL_A_PIN_PHASE_ADJUST_MIN`` and ``DPLL_A_PIN_PHASE_ADJUST_MAX``
+If pin phase adjustment is supported, minimal and maximal values and
+granularity that pin handle shall be provided to the user on
+``DPLL_CMD_PIN_GET`` respond with ``DPLL_A_PIN_PHASE_ADJUST_MIN``,
+``DPLL_A_PIN_PHASE_ADJUST_MAX`` and ``DPLL_A_PIN_PHASE_ADJUST_GRAN``
attributes. Configured phase adjust value is provided with
``DPLL_A_PIN_PHASE_ADJUST`` attribute of a pin, and value change can be
requested with the same attribute with ``DPLL_CMD_PIN_SET`` command.
- =============================== ======================================
- ``DPLL_A_PIN_ID`` configured pin id
- ``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase adjustment
- ``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase adjustment
- ``DPLL_A_PIN_PHASE_ADJUST`` attr configured value of phase
- adjustment on parent dpll device
- ``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting
- configuration on given parent dpll
- device
- ``DPLL_A_PIN_PARENT_ID`` parent dpll device id
- ``DPLL_A_PIN_PHASE_OFFSET`` attr measured phase difference
- between a pin and parent dpll device
- =============================== ======================================
+ ================================ ==========================================
+ ``DPLL_A_PIN_ID`` configured pin id
+ ``DPLL_A_PIN_PHASE_ADJUST_GRAN`` attr granularity of phase adjustment value
+ ``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase adjustment
+ ``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase adjustment
+ ``DPLL_A_PIN_PHASE_ADJUST`` attr configured value of phase
+ adjustment on parent dpll device
+ ``DPLL_A_PIN_PARENT_DEVICE`` nested attribute for requesting
+ configuration on given parent dpll
+ device
+ ``DPLL_A_PIN_PARENT_ID`` parent dpll device id
+ ``DPLL_A_PIN_PHASE_OFFSET`` attr measured phase difference
+ between a pin and parent dpll device
+ ================================ ==========================================
All phase related values are provided in pico seconds, which represents
time difference between signals phase. The negative value means that
@@ -384,6 +386,8 @@ according to attribute purpose.
frequencies
``DPLL_A_PIN_ANY_FREQUENCY_MIN`` attr minimum value of frequency
``DPLL_A_PIN_ANY_FREQUENCY_MAX`` attr maximum value of frequency
+ ``DPLL_A_PIN_PHASE_ADJUST_GRAN`` attr granularity of phase
+ adjustment value
``DPLL_A_PIN_PHASE_ADJUST_MIN`` attr minimum value of phase
adjustment
``DPLL_A_PIN_PHASE_ADJUST_MAX`` attr maximum value of phase
diff --git a/Documentation/driver-api/firmware/efi/index.rst b/Documentation/driver-api/firmware/efi/index.rst
index 4fe8abba9fc6..5a6b6229592c 100644
--- a/Documentation/driver-api/firmware/efi/index.rst
+++ b/Documentation/driver-api/firmware/efi/index.rst
@@ -1,11 +1,16 @@
.. SPDX-License-Identifier: GPL-2.0
-============
-UEFI Support
-============
+====================================================
+Unified Extensible Firmware Interface (UEFI) Support
+====================================================
UEFI stub library functions
===========================
.. kernel-doc:: drivers/firmware/efi/libstub/mem.c
:internal:
+
+UEFI Common Platform Error Record (CPER) functions
+==================================================
+
+.. kernel-doc:: drivers/firmware/efi/cper.c
diff --git a/Documentation/driver-api/generic_pt.rst b/Documentation/driver-api/generic_pt.rst
new file mode 100644
index 000000000000..fd29d1b525e5
--- /dev/null
+++ b/Documentation/driver-api/generic_pt.rst
@@ -0,0 +1,137 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================
+Generic Radix Page Table
+========================
+
+.. kernel-doc:: include/linux/generic_pt/common.h
+ :doc: Generic Radix Page Table
+
+.. kernel-doc:: drivers/iommu/generic_pt/pt_defs.h
+ :doc: Generic Page Table Language
+
+Usage
+=====
+
+Generic PT is structured as a multi-compilation system. Since each format
+provides an API using a common set of names there can be only one format active
+within a compilation unit. This design avoids function pointers around the low
+level API.
+
+Instead the function pointers can end up at the higher level API (i.e.
+map/unmap, etc.) and the per-format code can be directly inlined into the
+per-format compilation unit. For something like IOMMU each format will be
+compiled into a per-format IOMMU operations kernel module.
+
+For this to work the .c file for each compilation unit will include both the
+format headers and the generic code for the implementation. For instance in an
+implementation compilation unit the headers would normally be included as
+follows:
+
+generic_pt/fmt/iommu_amdv1.c::
+
+ #include <linux/generic_pt/common.h>
+ #include "defs_amdv1.h"
+ #include "../pt_defs.h"
+ #include "amdv1.h"
+ #include "../pt_common.h"
+ #include "../pt_iter.h"
+ #include "../iommu_pt.h" /* The IOMMU implementation */
+
+iommu_pt.h includes definitions that will generate the operations functions for
+map/unmap/etc. using the definitions provided by AMDv1. The resulting module
+will have exported symbols named like pt_iommu_amdv1_init().
+
+Refer to drivers/iommu/generic_pt/fmt/iommu_template.h for an example of how the
+IOMMU implementation uses multi-compilation to generate per-format ops structs
+pointers.
+
+The format code is written so that the common names arise from #defines to
+distinct format specific names. This is intended to aid debuggability by
+avoiding symbol clashes across all the different formats.
+
+Exported symbols and other global names are mangled using a per-format string
+via the NS() helper macro.
+
+The format uses struct pt_common as the top-level struct for the table,
+and each format will have its own struct pt_xxx which embeds it to store
+format-specific information.
+
+The implementation will further wrap struct pt_common in its own top-level
+struct, such as struct pt_iommu_amdv1.
+
+Format functions at the struct pt_common level
+----------------------------------------------
+
+.. kernel-doc:: include/linux/generic_pt/common.h
+ :identifiers:
+.. kernel-doc:: drivers/iommu/generic_pt/pt_common.h
+
+Iteration Helpers
+-----------------
+
+.. kernel-doc:: drivers/iommu/generic_pt/pt_iter.h
+
+Writing a Format
+----------------
+
+It is best to start from a simple format that is similar to the target. x86_64
+is usually a good reference for something simple, and AMDv1 is something fairly
+complete.
+
+The required inline functions need to be implemented in the format header.
+These should all follow the standard pattern of::
+
+ static inline pt_oaddr_t amdv1pt_entry_oa(const struct pt_state *pts)
+ {
+ [..]
+ }
+ #define pt_entry_oa amdv1pt_entry_oa
+
+where a uniquely named per-format inline function provides the implementation
+and a define maps it to the generic name. This is intended to make debug symbols
+work better. inline functions should always be used as the prototypes in
+pt_common.h will cause the compiler to validate the function signature to
+prevent errors.
+
+Review pt_fmt_defaults.h to understand some of the optional inlines.
+
+Once the format compiles then it should be run through the generic page table
+kunit test in kunit_generic_pt.h using kunit. For example::
+
+ $ tools/testing/kunit/kunit.py run --build_dir build_kunit_x86_64 --arch x86_64 --kunitconfig ./drivers/iommu/generic_pt/.kunitconfig amdv1_fmt_test.*
+ [...]
+ [11:15:08] Testing complete. Ran 9 tests: passed: 9
+ [11:15:09] Elapsed time: 3.137s total, 0.001s configuring, 2.368s building, 0.311s running
+
+The generic tests are intended to prove out the format functions and give
+clearer failures to speed up finding the problems. Once those pass then the
+entire kunit suite should be run.
+
+IOMMU Invalidation Features
+---------------------------
+
+Invalidation is how the page table algorithms synchronize with a HW cache of the
+page table memory, typically called the TLB (or IOTLB for IOMMU cases).
+
+The TLB can store present PTEs, non-present PTEs and table pointers, depending
+on its design. Every HW has its own approach on how to describe what has changed
+to have changed items removed from the TLB.
+
+PT_FEAT_FLUSH_RANGE
+~~~~~~~~~~~~~~~~~~~
+
+PT_FEAT_FLUSH_RANGE is the easiest scheme to understand. It tries to generate a
+single range invalidation for each operation, over-invalidating if there are
+gaps of VA that don't need invalidation. This trades off impacted VA for number
+of invalidation operations. It does not keep track of what is being invalidated;
+however, if pages have to be freed then page table pointers have to be cleaned
+from the walk cache. The range can start/end at any page boundary.
+
+PT_FEAT_FLUSH_RANGE_NO_GAPS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+PT_FEAT_FLUSH_RANGE_NO_GAPS is similar to PT_FEAT_FLUSH_RANGE; however, it tries
+to minimize the amount of impacted VA by issuing extra flush operations. This is
+useful if the cost of processing VA is very high, for instance because a
+hypervisor is processing the page table with a shadowing algorithm.
diff --git a/Documentation/driver-api/gpio/index.rst b/Documentation/driver-api/gpio/index.rst
index 87929840e85a..bee58f709b9a 100644
--- a/Documentation/driver-api/gpio/index.rst
+++ b/Documentation/driver-api/gpio/index.rst
@@ -15,6 +15,7 @@ Contents:
legacy-boards
drivers-on-gpio
bt8xxgpio
+ pca953x
Core
====
diff --git a/Documentation/driver-api/gpio/pca953x.rst b/Documentation/driver-api/gpio/pca953x.rst
new file mode 100644
index 000000000000..4bd7cf1120cb
--- /dev/null
+++ b/Documentation/driver-api/gpio/pca953x.rst
@@ -0,0 +1,552 @@
+============================================
+PCA953x I²C GPIO expander compatibility list
+============================================
+
+:Author: Levente Révész <levente.revesz@eilabs.com>
+
+I went through all the datasheets and created this note listing
+chip functions and register layouts.
+
+Overview of chips
+=================
+
+Chips with the basic 4 registers
+--------------------------------
+
+These chips have 4 register banks: input, output, invert and direction.
+Each of these banks contains (lines/8) registers, one for each GPIO port.
+
+Banks offset is always a power of 2:
+
+- 4 lines -> bank offset is 1
+- 8 lines -> bank offset is 1
+- 16 lines -> bank offset is 2
+- 24 lines -> bank offset is 4
+- 32 lines -> bank offset is 4
+- 40 lines -> bank offset is 8
+
+For example, register layout of GPIO expander with 24 lines:
+
++------+-----------------+--------+
+| addr | function | bank |
++======+=================+========+
+| 00 | input port0 | |
++------+-----------------+ |
+| 01 | input port1 | bank 0 |
++------+-----------------+ |
+| 02 | input port2 | |
++------+-----------------+--------+
+| 03 | n/a | |
++------+-----------------+--------+
+| 04 | output port0 | |
++------+-----------------+ |
+| 05 | output port1 | bank 1 |
++------+-----------------+ |
+| 06 | output port2 | |
++------+-----------------+--------+
+| 07 | n/a | |
++------+-----------------+--------+
+| 08 | invert port0 | |
++------+-----------------+ |
+| 09 | invert port1 | bank 2 |
++------+-----------------+ |
+| 0A | invert port2 | |
++------+-----------------+--------+
+| 0B | n/a | |
++------+-----------------+--------+
+| 0C | direction port0 | |
++------+-----------------+ |
+| 0D | direction port1 | bank 3 |
++------+-----------------+ |
+| 0E | direction port2 | |
++------+-----------------+--------+
+| 0F | n/a | |
++------+-----------------+--------+
+
+.. note::
+ This is followed by all supported chips, except by pcal6534.
+
+The table below shows the offsets for each of the compatible chips:
+
+========== ===== ========= ===== ====== ====== =========
+compatible lines interrupt input output invert direction
+========== ===== ========= ===== ====== ====== =========
+pca9536 4 no 00 01 02 03
+pca9537 4 yes 00 01 02 03
+pca6408 8 yes 00 01 02 03
+tca6408 8 yes 00 01 02 03
+pca9534 8 yes 00 01 02 03
+pca9538 8 yes 00 01 02 03
+pca9554 8 yes 00 01 02 03
+tca9554 8 yes 00 01 02 03
+pca9556 8 no 00 01 02 03
+pca9557 8 no 00 01 02 03
+pca6107 8 yes 00 01 02 03
+pca6416 16 yes 00 02 04 06
+tca6416 16 yes 00 02 04 06
+pca9535 16 yes 00 02 04 06
+pca9539 16 yes 00 02 04 06
+tca9539 16 yes 00 02 04 06
+pca9555 16 yes 00 02 04 06
+max7318 16 yes 00 02 04 06
+tca6424 24 yes 00 04 08 0C
+========== ===== ========= ===== ====== ====== =========
+
+Chips with additional timeout_en register
+-----------------------------------------
+
+These Maxim chips have a bus timeout function which can be enabled in
+the timeout_en register. This is present in only two chips. Defaults to
+timeout disabled.
+
+========== ===== ========= ===== ====== ====== ========= ==========
+compatible lines interrupt input output invert direction timeout_en
+========== ===== ========= ===== ====== ====== ========= ==========
+max7310 8 no 00 01 02 03 04
+max7312 16 yes 00 02 04 06 08
+========== ===== ========= ===== ====== ====== ========= ==========
+
+Chips with additional int_mask register
+---------------------------------------
+
+These chips have an interrupt mask register in addition to the 4 basic
+registers. The interrupt masks default to all interrupts disabled. To
+use interrupts with these chips, the driver has to set the int_mask
+register.
+
+========== ===== ========= ===== ====== ====== ========= ========
+compatible lines interrupt input output invert direction int_mask
+========== ===== ========= ===== ====== ====== ========= ========
+pca9505 40 yes 00 08 10 18 20
+pca9506 40 yes 00 08 10 18 20
+========== ===== ========= ===== ====== ====== ========= ========
+
+Chips with additional int_mask and out_conf registers
+-----------------------------------------------------
+
+This chip has an interrupt mask register, and an output port
+configuration register, which can select between push-pull and
+open-drain modes. Each bit controls two lines. Both of these registers
+are present in PCAL chips as well, albeit the out_conf works
+differently.
+
+========== ===== ========= ===== ====== ====== ========= ======== ========
+compatible lines interrupt input output invert direction int_mask out_conf
+========== ===== ========= ===== ====== ====== ========= ======== ========
+pca9698 40 yes 00 08 10 18 20 28
+========== ===== ========= ===== ====== ====== ========= ======== ========
+
+pca9698 also has a "master output" register for setting all outputs per
+port to the same value simultaneously, and a chip specific mode register
+for various additional chip settings.
+
+========== ============= ====
+compatible master_output mode
+========== ============= ====
+pca9698 29 2A
+========== ============= ====
+
+Chips with LED blink and intensity control
+------------------------------------------
+
+These Maxim chips have no invert register.
+
+They have two sets of output registers (output0 and output1). An internal
+timer alternates the effective output between the values set in these
+registers, if blink mode is enabled in the blink register. The
+master_intensity register and the intensity registers together define
+the PWM intensity value for each pair of outputs.
+
+These chips can be used as simple GPIO expanders if the driver handles the
+input, output0 and direction registers.
+
+========== ===== ========= ===== ======= ========= ======= ================ ===== =========
+compatible lines interrupt input output0 direction output1 master_intensity blink intensity
+========== ===== ========= ===== ======= ========= ======= ================ ===== =========
+max7315 8 yes 00 01 03 09 0E 0F 10
+max7313 16 yes 00 02 06 0A 0E 0F 10
+========== ===== ========= ===== ======= ========= ======= ================ ===== =========
+
+Basic PCAL chips
+----------------
+
+========== ===== ========= ===== ====== ====== =========
+compatible lines interrupt input output invert direction
+========== ===== ========= ===== ====== ====== =========
+pcal6408 8 yes 00 01 02 03
+pcal9554b 8 yes 00 01 02 03
+pcal6416 16 yes 00 02 04 06
+pcal9535 16 yes 00 02 04 06
+pcal9555a 16 yes 00 02 04 06
+========== ===== ========= ===== ====== ====== =========
+
+These chips have several additional features:
+
+ 1. output drive strength setting (out_strength)
+ 2. input latch (in_latch)
+ 3. pull-up/pull-down (pull_in, pull_sel)
+ 4. push-pull/open-drain outputs (out_conf)
+ 5. interrupt mask and interrupt status (int_mask, int_status)
+
+========== ============ ======== ======= ======== ======== ========== ========
+compatible out_strength in_latch pull_en pull_sel int_mask int_status out_conf
+========== ============ ======== ======= ======== ======== ========== ========
+pcal6408 40 42 43 44 45 46 4F
+pcal9554b 40 42 43 44 45 46 4F
+pcal6416 40 44 46 48 4A 4C 4F
+pcal9535 40 44 46 48 4A 4C 4F
+pcal9555a 40 44 46 48 4A 4C 4F
+========== ============ ======== ======= ======== ======== ========== ========
+
+Currently the driver has support for the input latch, pull-up/pull-down
+and uses int_mask and int_status for interrupts.
+
+PCAL chips with extended interrupt and output configuration functions
+---------------------------------------------------------------------
+
+========== ===== ========= ===== ====== ====== =========
+compatible lines interrupt input output invert direction
+========== ===== ========= ===== ====== ====== =========
+pcal6524 24 yes 00 04 08 0C
+pcal6534 34 yes 00 05 0A 0F
+========== ===== ========= ===== ====== ====== =========
+
+These chips have the full PCAL register set, plus the following functions:
+
+ 1. interrupt event selection: level, rising, falling, any edge
+ 2. clear interrupt status per line
+ 3. read input without clearing interrupt status
+ 4. individual output config (push-pull/open-drain) per output line
+ 5. debounce inputs
+
+========== ============ ======== ======= ======== ======== ========== ========
+compatible out_strength in_latch pull_en pull_sel int_mask int_status out_conf
+========== ============ ======== ======= ======== ======== ========== ========
+pcal6524 40 48 4C 50 54 58 5C
+pcal6534 30 3A 3F 44 49 4E 53
+========== ============ ======== ======= ======== ======== ========== ========
+
+========== ======== ========= ============ ============== ======== ==============
+compatible int_edge int_clear input_status indiv_out_conf debounce debounce_count
+========== ======== ========= ============ ============== ======== ==============
+pcal6524 60 68 6C 70 74 76
+pcal6534 54 5E 63 68 6D 6F
+========== ======== ========= ============ ============== ======== ==============
+
+As can be seen in the table above, pcal6534 does not follow the usual
+bank spacing rule. Its banks are closely packed instead.
+
+PCA957X chips with a completely different register layout
+---------------------------------------------------------
+
+These chips have the basic 4 registers, but at unusual addresses.
+
+Additionally, they have:
+
+ 1. pull-up/pull-down (pull_sel)
+ 2. a global pull enable, defaults to disabled (config)
+ 3. interrupt mask, interrupt status (int_mask, int_status)
+
+========== ===== ========= ===== ====== ====== ======== ========= ====== ======== ==========
+compatible lines interrupt input invert config pull_sel direction output int_mask int_status
+========== ===== ========= ===== ====== ====== ======== ========= ====== ======== ==========
+pca9574 8 yes 00 01 02 03 04 05 06 07
+pca9575 16 yes 00 02 04 06 08 0A 0C 0E
+========== ===== ========= ===== ====== ====== ======== ========= ====== ======== ==========
+
+Currently the driver supports none of the advanced features.
+
+XRA1202
+-------
+
+Basic 4 registers, plus advanced features:
+
+ 1. interrupt mask, defaults to interrupts disabled
+ 2. interrupt status
+ 3. interrupt event selection, level, rising, falling, any edge
+ (int_mask, rising_mask, falling_mask)
+ 4. pull-up (no pull-down)
+ 5. tri-state
+ 6. debounce
+
+========== ===== ========= ===== ====== ====== ========= =========
+compatible lines interrupt input output invert direction pullup_en
+========== ===== ========= ===== ====== ====== ========= =========
+xra1202 8 yes 00 01 02 03 04
+========== ===== ========= ===== ====== ====== ========= =========
+
+========== ======== ======== ========== =========== ============ ========
+compatible int_mask tristate int_status rising_mask falling_mask debounce
+========== ======== ======== ========== =========== ============ ========
+xra1202 05 06 07 08 09 0A
+========== ======== ======== ========== =========== ============ ========
+
+Overview of functions
+=====================
+
+This section lists chip functions that are supported by the driver
+already, or are at least common in multiple chips.
+
+Input, Output, Invert, Direction
+--------------------------------
+
+The basic 4 GPIO functions are present in all but one chip category, i.e.
+`Chips with LED blink and intensity control`_ are missing the invert
+register.
+
+3 different layouts are used for these registers:
+
+ 1. banks 0, 1, 2, 3 with bank offsets of 2^n
+ - all other chips
+
+ 2. banks 0, 1, 2, 3 with closely packed banks
+ - pcal6534
+
+ 3. banks 0, 5, 1, 4 with bank offsets of 2^n
+ - pca9574
+ - pca9575
+
+Interrupts
+----------
+
+Only an interrupt mask register
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The same layout is used for all of these:
+
+ 1. bank 5 with bank offsets of 2^n
+ - pca9505
+ - pca9506
+ - pca9698
+
+Interrupt mask and interrupt status registers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+These work the same way in all of the chips: mask and status have
+one bit per line, 1 in the mask means interrupt enabled.
+
+Layouts:
+
+ 1. base offset 0x40, bank 5 and bank 6, bank offsets of 2^n
+ - pcal6408
+ - pcal6416
+ - pcal9535
+ - pcal9554b
+ - pcal9555a
+ - pcal6524
+
+ 2. base offset 0x30, bank 5 and 6, closely packed banks
+ - pcal6534
+
+ 3. bank 6 and 7, bank offsets of 2^n
+ - pca9574
+ - pca9575
+
+ 4. bank 5 and 7, bank offsets of 2^n
+ - xra1202
+
+Interrupt on specific edges
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+`PCAL chips with extended interrupt and output configuration functions`_
+have an int_edge register. This contains 2 bits per line, one of 4 events
+can be selected for each line:
+
+ 0: level, 1: rising edge, 2: falling edge, 3: any edge
+
+Layouts:
+
+ 1. base offset 0x40, bank 7, bank offsets of 2^n
+
+ - pcal6524
+
+ 2. base offset 0x30, bank 7 + offset 0x01, closely packed banks
+ (out_conf is 1 byte, not (lines/8) bytes, hence the 0x01 offset)
+
+ - pcal6534
+
+`XRA1202`_ chips have a different mechanism for the same thing: they have
+a rising mask and a falling mask, with one bit per line.
+
+Layout:
+
+ 1. bank 5, bank offsets of 2^n
+
+Input latch
+-----------
+
+Only `Basic PCAL chips`_ and
+`PCAL chips with extended interrupt and output configuration functions`_
+have this function. When the latch is enabled, the interrupt is not cleared
+until the input port is read. When the latch is disabled, the interrupt
+is cleared even if the input register is not read, if the input pin returns
+to the logic value it had before generating the interrupt. Defaults to latch
+disabled.
+
+Currently the driver enables the latch for each line with interrupt
+enabled.
+
+ 1. base offset 0x40, bank 2, bank offsets of 2^n
+ - pcal6408
+ - pcal6416
+ - pcal9535
+ - pcal9554b
+ - pcal9555a
+ - pcal6524
+
+ 2. base offset 0x30, bank 2, closely packed banks
+ - pcal6534
+
+Pull-up and pull-down
+---------------------
+
+`Basic PCAL chips`_ and
+`PCAL chips with extended interrupt and output configuration functions`_
+use the same mechanism: their pull_en register enables the pull-up or pull-down
+function, and their pull_sel register chooses the direction. They all use one
+bit per line.
+
+ 0: pull-down, 1: pull-up
+
+Layouts:
+
+ 1. base offset 0x40, bank 3 (en) and 4 (sel), bank offsets of 2^n
+ - pcal6408
+ - pcal6416
+ - pcal9535
+ - pcal9554b
+ - pcal9555a
+ - pcal6524
+
+ 2. base offset 0x30, bank 3 (en) and 4 (sel), closely packed banks
+ - pcal6534
+
+`PCA957X chips with a completely different register layout`_ have a pull_sel
+register with one bit per line, and a global pull_en bit in their config
+register.
+
+Layout:
+
+ 1. bank 2 (config), bank 3 (sel), bank offsets of 2^n
+ - pca9574
+ - pca9575
+
+`XRA1202`_ chips can only pull-up. They have a pullup_en register.
+
+Layout:
+
+ 1. bank 4, bank offsets of 2^n
+ - xra1202
+
+Push-pull and open-drain
+------------------------
+
+`Chips with additional int_mask and out_conf registers`_ have this function,
+but only for select IO ports. Register has 1 bit per 2 lines. In pca9698,
+only port0 and port1 have this function.
+
+ 0: open-drain, 1: push-pull
+
+Layout:
+
+ 1. base offset 5*bankoffset
+ - pca9698
+
+`Basic PCAL chips`_ have 1 bit per port in one single out_conf register.
+Only whole ports can be configured.
+
+ 0: push-pull, 1: open-drain
+
+Layout:
+
+ 1. base offset 0x4F
+ - pcal6408
+ - pcal6416
+ - pcal9535
+ - pcal9554b
+ - pcal9555a
+
+`PCAL chips with extended interrupt and output configuration functions`_
+can set this for each line individually. They have the same per-port out_conf
+register as `Basic PCAL chips`_, but they also have an indiv_out_conf register
+with one bit per line, which inverts the effect of the port-wise setting.
+
+ 0: push-pull, 1: open-drain
+
+Layouts:
+
+ 1. base offset 0x40 + 7*bankoffset (out_conf),
+ base offset 0x60, bank 4 (indiv_out_conf) with bank offset of 2^n
+
+ - pcal6524
+
+ 2. base offset 0x30 + 7*banksize (out_conf),
+ base offset 0x54, bank 4 (indiv_out_conf), closely packed banks
+
+ - pcal6534
+
+This function is currently not supported by the driver.
+
+Output drive strength
+---------------------
+
+Only PCAL chips have this function. 2 bits per line.
+
+==== ==============
+bits drive strength
+==== ==============
+ 00 0.25x
+ 01 0.50x
+ 10 0.75x
+ 11 1.00x
+==== ==============
+
+ 1. base offset 0x40, bank 0 and 1, bank offsets of 2^n
+ - pcal6408
+ - pcal6416
+ - pcal9535
+ - pcal9554b
+ - pcal9555a
+ - pcal6524
+
+ 2. base offset 0x30, bank 0 and 1, closely packed banks
+ - pcal6534
+
+Currently not supported by the driver.
+
+Datasheets
+==========
+
+- MAX7310: https://datasheets.maximintegrated.com/en/ds/MAX7310.pdf
+- MAX7312: https://datasheets.maximintegrated.com/en/ds/MAX7312.pdf
+- MAX7313: https://datasheets.maximintegrated.com/en/ds/MAX7313.pdf
+- MAX7315: https://datasheets.maximintegrated.com/en/ds/MAX7315.pdf
+- MAX7318: https://datasheets.maximintegrated.com/en/ds/MAX7318.pdf
+- PCA6107: https://pdf1.alldatasheet.com/datasheet-pdf/view/161780/TI/PCA6107.html
+- PCA6408A: https://www.nxp.com/docs/en/data-sheet/PCA6408A.pdf
+- PCA6416A: https://www.nxp.com/docs/en/data-sheet/PCA6416A.pdf
+- PCA9505: https://www.nxp.com/docs/en/data-sheet/PCA9505_9506.pdf
+- PCA9505: https://www.nxp.com/docs/en/data-sheet/PCA9505_9506.pdf
+- PCA9534: https://www.nxp.com/docs/en/data-sheet/PCA9534.pdf
+- PCA9535: https://www.nxp.com/docs/en/data-sheet/PCA9535_PCA9535C.pdf
+- PCA9536: https://www.nxp.com/docs/en/data-sheet/PCA9536.pdf
+- PCA9537: https://www.nxp.com/docs/en/data-sheet/PCA9537.pdf
+- PCA9538: https://www.nxp.com/docs/en/data-sheet/PCA9538.pdf
+- PCA9539: https://www.nxp.com/docs/en/data-sheet/PCA9539_PCA9539R.pdf
+- PCA9554: https://www.nxp.com/docs/en/data-sheet/PCA9554_9554A.pdf
+- PCA9555: https://www.nxp.com/docs/en/data-sheet/PCA9555.pdf
+- PCA9556: https://www.nxp.com/docs/en/data-sheet/PCA9556.pdf
+- PCA9557: https://www.nxp.com/docs/en/data-sheet/PCA9557.pdf
+- PCA9574: https://www.nxp.com/docs/en/data-sheet/PCA9574.pdf
+- PCA9575: https://www.nxp.com/docs/en/data-sheet/PCA9575.pdf
+- PCA9698: https://www.nxp.com/docs/en/data-sheet/PCA9698.pdf
+- PCAL6408A: https://www.nxp.com/docs/en/data-sheet/PCAL6408A.pdf
+- PCAL6416A: https://www.nxp.com/docs/en/data-sheet/PCAL6416A.pdf
+- PCAL6524: https://www.nxp.com/docs/en/data-sheet/PCAL6524.pdf
+- PCAL6534: https://www.nxp.com/docs/en/data-sheet/PCAL6534.pdf
+- PCAL9535A: https://www.nxp.com/docs/en/data-sheet/PCAL9535A.pdf
+- PCAL9554B: https://www.nxp.com/docs/en/data-sheet/PCAL9554B_PCAL9554C.pdf
+- PCAL9555A: https://www.nxp.com/docs/en/data-sheet/PCAL9555A.pdf
+- TCA6408A: https://www.ti.com/lit/gpn/tca6408a
+- TCA6416: https://www.ti.com/lit/gpn/tca6416
+- TCA6424: https://www.ti.com/lit/gpn/tca6424
+- TCA9539: https://www.ti.com/lit/gpn/tca9539
+- TCA9554: https://www.ti.com/lit/gpn/tca9554
+- XRA1202: https://assets.maxlinear.com/web/documents/xra1202_1202p_101_042213.pdf
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index 3e2a270bd828..baff96b5cf0b 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -93,6 +93,7 @@ Subsystem-specific APIs
frame-buffer
aperture
generic-counter
+ generic_pt
gpio/index
hsi
hte/index
diff --git a/Documentation/driver-api/media/v4l2-core.rst b/Documentation/driver-api/media/v4l2-core.rst
index ad987c34ad2a..a5f5102c64cc 100644
--- a/Documentation/driver-api/media/v4l2-core.rst
+++ b/Documentation/driver-api/media/v4l2-core.rst
@@ -27,3 +27,4 @@ Video4Linux devices
v4l2-common
v4l2-tveeprom
v4l2-jpeg
+ v4l2-isp
diff --git a/Documentation/driver-api/media/v4l2-isp.rst b/Documentation/driver-api/media/v4l2-isp.rst
new file mode 100644
index 000000000000..618ae614ff79
--- /dev/null
+++ b/Documentation/driver-api/media/v4l2-isp.rst
@@ -0,0 +1,49 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+V4L2 generic ISP parameters and statistics support
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Design rationale
+================
+
+ISP configuration parameters and statistics are processed and collected by
+drivers and exchanged with userspace through data types that usually
+reflect the ISP peripheral registers layout.
+
+Each ISP driver defines its own metadata output format for parameters and
+a metadata capture format for statistics. The buffer layout is realized by a
+set of C structures that reflects the registers layout. The number and types
+of C structures is fixed by the format definition and becomes part of the Linux
+kernel uAPI/uABI interface.
+
+Because of the hard requirement of backward compatibility when extending the
+user API/ABI interface, modifying an ISP driver capture or output metadata
+format after it has been accepted by mainline is very hard if not impossible.
+
+It generally happens, in fact, that after the first accepted revision of an ISP
+driver the buffers layout need to be modified, either to support new hardware
+blocks, to fix bugs or to support different revisions of the hardware.
+
+Each of these situations would require defining a new metadata format, making it
+really hard to maintain and extend drivers and requiring userspace to use
+the correct format depending on the kernel revision in use.
+
+V4L2 ISP configuration parameters
+=================================
+
+For these reasons, Video4Linux2 defines generic types for ISP configuration
+parameters and statistics. Drivers are still expected to define their own
+formats for their metadata output and capture nodes, but the buffers layout can
+be defined using the extensible and versioned types defined by
+include/uapi/linux/media/v4l2-isp.h.
+
+Drivers are expected to provide the definitions of their supported ISP blocks
+and the expected maximum size of a buffer.
+
+For driver developers a set of helper functions to assist them with validation
+of the buffer received from userspace is available in
+drivers/media/v4l2-core/v4l2-isp.c
+
+V4L2 ISP support driver documentation
+=====================================
+.. kernel-doc:: include/media/v4l2-isp.h
diff --git a/Documentation/driver-api/parport-lowlevel.rst b/Documentation/driver-api/parport-lowlevel.rst
index 0633d70ffda7..a907e279f509 100644
--- a/Documentation/driver-api/parport-lowlevel.rst
+++ b/Documentation/driver-api/parport-lowlevel.rst
@@ -7,6 +7,7 @@ PARPORT interface documentation
Described here are the following functions:
Global functions::
+
parport_register_driver
parport_unregister_driver
parport_enumerate
@@ -34,6 +35,7 @@ Global functions::
Port functions (can be overridden by low-level drivers):
SPP::
+
port->ops->read_data
port->ops->write_data
port->ops->read_status
@@ -46,17 +48,20 @@ Port functions (can be overridden by low-level drivers):
port->ops->data_reverse
EPP::
+
port->ops->epp_write_data
port->ops->epp_read_data
port->ops->epp_write_addr
port->ops->epp_read_addr
ECP::
+
port->ops->ecp_write_data
port->ops->ecp_read_data
port->ops->ecp_write_addr
Other::
+
port->ops->nibble_read_data
port->ops->byte_read_data
port->ops->compat_write_data
diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver-api/pci/p2pdma.rst
index d0b241628cf1..280673b50350 100644
--- a/Documentation/driver-api/pci/p2pdma.rst
+++ b/Documentation/driver-api/pci/p2pdma.rst
@@ -9,22 +9,48 @@ between two devices on the bus. This type of transaction is henceforth
called Peer-to-Peer (or P2P). However, there are a number of issues that
make P2P transactions tricky to do in a perfectly safe way.
-One of the biggest issues is that PCI doesn't require forwarding
-transactions between hierarchy domains, and in PCIe, each Root Port
-defines a separate hierarchy domain. To make things worse, there is no
-simple way to determine if a given Root Complex supports this or not.
-(See PCIe r4.0, sec 1.3.1). Therefore, as of this writing, the kernel
-only supports doing P2P when the endpoints involved are all behind the
-same PCI bridge, as such devices are all in the same PCI hierarchy
-domain, and the spec guarantees that all transactions within the
-hierarchy will be routable, but it does not require routing
-between hierarchies.
-
-The second issue is that to make use of existing interfaces in Linux,
-memory that is used for P2P transactions needs to be backed by struct
-pages. However, PCI BARs are not typically cache coherent so there are
-a few corner case gotchas with these pages so developers need to
-be careful about what they do with them.
+For PCIe the routing of Transaction Layer Packets (TLPs) is well-defined up
+until they reach a host bridge or root port. If the path includes PCIe switches
+then based on the ACS settings the transaction can route entirely within
+the PCIe hierarchy and never reach the root port. The kernel will evaluate
+the PCIe topology and always permit P2P in these well-defined cases.
+
+However, if the P2P transaction reaches the host bridge then it might have to
+hairpin back out the same root port, be routed inside the CPU SOC to another
+PCIe root port, or routed internally to the SOC.
+
+The PCIe specification doesn't define the forwarding of transactions between
+hierarchy domains and kernel defaults to blocking such routing. There is an
+allow list to allow detecting known-good HW, in which case P2P between any
+two PCIe devices will be permitted.
+
+Since P2P inherently is doing transactions between two devices it requires two
+drivers to be co-operating inside the kernel. The providing driver has to convey
+its MMIO to the consuming driver. To meet the driver model lifecycle rules the
+MMIO must have all DMA mapping removed, all CPU accesses prevented, all page
+table mappings undone before the providing driver completes remove().
+
+This requires the providing and consuming driver to actively work together to
+guarantee that the consuming driver has stopped using the MMIO during a removal
+cycle. This is done by either a synchronous invalidation shutdown or waiting
+for all usage refcounts to reach zero.
+
+At the lowest level the P2P subsystem offers a naked struct p2p_provider that
+delegates lifecycle management to the providing driver. It is expected that
+drivers using this option will wrap their MMIO memory in DMABUF and use DMABUF
+to provide an invalidation shutdown. These MMIO addresess have no struct page, and
+if used with mmap() must create special PTEs. As such there are very few
+kernel uAPIs that can accept pointers to them; in particular they cannot be used
+with read()/write(), including O_DIRECT.
+
+Building on this, the subsystem offers a layer to wrap the MMIO in a ZONE_DEVICE
+pgmap of MEMORY_DEVICE_PCI_P2PDMA to create struct pages. The lifecycle of
+pgmap ensures that when the pgmap is destroyed all other drivers have stopped
+using the MMIO. This option works with O_DIRECT flows, in some cases, if the
+underlying subsystem supports handling MEMORY_DEVICE_PCI_P2PDMA through
+FOLL_PCI_P2PDMA. The use of FOLL_LONGTERM is prevented. As this relies on pgmap
+it also relies on architecture support along with alignment and minimum size
+limitations.
Driver Writer's Guide
@@ -114,14 +140,39 @@ allocating scatter-gather lists with P2P memory.
Struct Page Caveats
-------------------
-Driver writers should be very careful about not passing these special
-struct pages to code that isn't prepared for it. At this time, the kernel
-interfaces do not have any checks for ensuring this. This obviously
-precludes passing these pages to userspace.
+While the MEMORY_DEVICE_PCI_P2PDMA pages can be installed in VMAs,
+pin_user_pages() and related will not return them unless FOLL_PCI_P2PDMA is set.
-P2P memory is also technically IO memory but should never have any side
-effects behind it. Thus, the order of loads and stores should not be important
-and ioreadX(), iowriteX() and friends should not be necessary.
+The MEMORY_DEVICE_PCI_P2PDMA pages require care to support in the kernel. The
+KVA is still MMIO and must still be accessed through the normal
+readX()/writeX()/etc helpers. Direct CPU access (e.g. memcpy) is forbidden, just
+like any other MMIO mapping. While this will actually work on some
+architectures, others will experience corruption or just crash in the kernel.
+Supporting FOLL_PCI_P2PDMA in a subsystem requires scrubbing it to ensure no CPU
+access happens.
+
+
+Usage With DMABUF
+=================
+
+DMABUF provides an alternative to the above struct page-based
+client/provider/orchestrator system and should be used when struct page
+doesn't exist. In this mode the exporting driver will wrap
+some of its MMIO in a DMABUF and give the DMABUF FD to userspace.
+
+Userspace can then pass the FD to an importing driver which will ask the
+exporting driver to map it to the importer.
+
+In this case the initiator and target pci_devices are known and the P2P subsystem
+is used to determine the mapping type. The phys_addr_t-based DMA API is used to
+establish the dma_addr_t.
+
+Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants
+to remove() it must deliver an invalidation shutdown to all DMABUF importing
+drivers through move_notify() and synchronously DMA unmap all the MMIO.
+
+No importing driver can continue to have a DMA map to the MMIO after the
+exporting driver has destroyed its p2p_provider.
P2P DMA Support Library
diff --git a/Documentation/driver-api/pci/pci.rst b/Documentation/driver-api/pci/pci.rst
index 59d86e827198..99a1bbaaec5d 100644
--- a/Documentation/driver-api/pci/pci.rst
+++ b/Documentation/driver-api/pci/pci.rst
@@ -37,6 +37,9 @@ PCI Support Library
.. kernel-doc:: drivers/pci/slot.c
:export:
+.. kernel-doc:: drivers/pci/rebar.c
+ :export:
+
.. kernel-doc:: drivers/pci/rom.c
:export:
diff --git a/Documentation/driver-api/pldmfw/index.rst b/Documentation/driver-api/pldmfw/index.rst
index fd871b83f34f..e59beca374c1 100644
--- a/Documentation/driver-api/pldmfw/index.rst
+++ b/Documentation/driver-api/pldmfw/index.rst
@@ -14,7 +14,6 @@ the PLDM for Firmware Update standard
file-format
driver-ops
-==================================
Overview of the ``pldmfw`` library
==================================
diff --git a/Documentation/driver-api/thermal/intel_dptf.rst b/Documentation/driver-api/thermal/intel_dptf.rst
index c51ac793dc06..916bf0f36a03 100644
--- a/Documentation/driver-api/thermal/intel_dptf.rst
+++ b/Documentation/driver-api/thermal/intel_dptf.rst
@@ -409,3 +409,26 @@ based on the processor generation.
Limit 1 from being exhausted.
4 – Unknown: Can't classify.
+
+ On processors starting from Panther Lake additional hints are provided.
+ The hardware analyzes workload residencies over an extended period to
+ determine whether the workload classification tends toward idle/battery
+ life states or sustained/performance states. Based on this long-term
+ analysis, it classifies:
+
+ Power Classification: If the workload exhibits more idle or battery life
+ residencies, it is classified as "power".
+
+ Performance Classification: If the workload exhibits more sustained or
+ performance residencies, it is classified as "performance".
+
+ This approach enables applications to ignore short-term workload
+ fluctuations and instead respond to longer-term power vs. performance
+ trends.
+
+ Residency thresholds for this classification are CPU generation-specific.
+ Classification is reported via bit 4 of the workload_type_index:
+
+ Bit 4 = 1: Power classification
+
+ Bit 4 = 0: Performance classification
diff --git a/Documentation/driver-api/usb/writing_musb_glue_layer.rst b/Documentation/driver-api/usb/writing_musb_glue_layer.rst
index 0bb96ecdf527..b748b9fb1965 100644
--- a/Documentation/driver-api/usb/writing_musb_glue_layer.rst
+++ b/Documentation/driver-api/usb/writing_musb_glue_layer.rst
@@ -709,7 +709,7 @@ Resources
USB Home Page: https://www.usb.org
-linux-usb Mailing List Archives: https://marc.info/?l=linux-usb
+linux-usb Mailing List Archives: https://lore.kernel.org/linux-usb
USB On-the-Go Basics:
https://www.maximintegrated.com/app-notes/index.mvp/id/1822
diff --git a/Documentation/filesystems/ext4/inodes.rst b/Documentation/filesystems/ext4/inodes.rst
index cfc6c1659931..55cd5c380e92 100644
--- a/Documentation/filesystems/ext4/inodes.rst
+++ b/Documentation/filesystems/ext4/inodes.rst
@@ -297,6 +297,8 @@ The ``i_flags`` field is a combination of these values:
- Inode has inline data (EXT4_INLINE_DATA_FL).
* - 0x20000000
- Create children with the same project ID (EXT4_PROJINHERIT_FL).
+ * - 0x40000000
+ - Use case-insensitive lookups for directory contents (EXT4_CASEFOLD_FL).
* - 0x80000000
- Reserved for ext4 library (EXT4_RESERVED_FL).
* -
diff --git a/Documentation/filesystems/ext4/super.rst b/Documentation/filesystems/ext4/super.rst
index 1b240661bfa3..9a59cded9bd7 100644
--- a/Documentation/filesystems/ext4/super.rst
+++ b/Documentation/filesystems/ext4/super.rst
@@ -671,7 +671,9 @@ following:
* - 0x8000
- Data in inode (INCOMPAT_INLINE_DATA).
* - 0x10000
- - Encrypted inodes are present on the filesystem. (INCOMPAT_ENCRYPT).
+ - Encrypted inodes can be present. (INCOMPAT_ENCRYPT).
+ * - 0x20000
+ - Directories can be marked case-insensitive. (INCOMPAT_CASEFOLD).
.. _super_rocompat:
diff --git a/Documentation/filesystems/fscrypt.rst b/Documentation/filesystems/fscrypt.rst
index 696a5844bfa3..70af896822e1 100644
--- a/Documentation/filesystems/fscrypt.rst
+++ b/Documentation/filesystems/fscrypt.rst
@@ -450,9 +450,7 @@ API, but the filenames mode still does.
- CONFIG_CRYPTO_HCTR2
- Recommended:
- arm64: CONFIG_CRYPTO_AES_ARM64_CE_BLK
- - arm64: CONFIG_CRYPTO_POLYVAL_ARM64_CE
- x86: CONFIG_CRYPTO_AES_NI_INTEL
- - x86: CONFIG_CRYPTO_POLYVAL_CLMUL_NI
- Adiantum
- Mandatory:
diff --git a/Documentation/filesystems/gfs2-glocks.rst b/Documentation/filesystems/gfs2/glocks.rst
index ce5ff08cbd59..ce5ff08cbd59 100644
--- a/Documentation/filesystems/gfs2-glocks.rst
+++ b/Documentation/filesystems/gfs2/glocks.rst
diff --git a/Documentation/filesystems/gfs2.rst b/Documentation/filesystems/gfs2/index.rst
index 1bc48a13430c..e5e195403561 100644
--- a/Documentation/filesystems/gfs2.rst
+++ b/Documentation/filesystems/gfs2/index.rst
@@ -4,6 +4,9 @@
Global File System 2
====================
+Overview
+========
+
GFS2 is a cluster file system. It allows a cluster of computers to
simultaneously use a block device that is shared between them (with FC,
iSCSI, NBD, etc). GFS2 reads and writes to the block device like a local
@@ -50,3 +53,12 @@ The following man pages are available from gfs2-utils:
gfs2_convert to convert a gfs filesystem to GFS2 in-place
mkfs.gfs2 to make a filesystem
============ =============================================
+
+Implementation Notes
+====================
+
+.. toctree::
+ :maxdepth: 1
+
+ glocks
+ uevents
diff --git a/Documentation/filesystems/gfs2-uevents.rst b/Documentation/filesystems/gfs2/uevents.rst
index f162a2c76c69..f162a2c76c69 100644
--- a/Documentation/filesystems/gfs2-uevents.rst
+++ b/Documentation/filesystems/gfs2/uevents.rst
diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst
index af516e528ded..f4873197587d 100644
--- a/Documentation/filesystems/index.rst
+++ b/Documentation/filesystems/index.rst
@@ -89,9 +89,7 @@ Documentation for filesystem implementations.
ext3
ext4/index
f2fs
- gfs2
- gfs2-uevents
- gfs2-glocks
+ gfs2/index
hfs
hfsplus
hpfs
diff --git a/Documentation/filesystems/iomap/operations.rst b/Documentation/filesystems/iomap/operations.rst
index 387fd9cc72ca..da982ca7e413 100644
--- a/Documentation/filesystems/iomap/operations.rst
+++ b/Documentation/filesystems/iomap/operations.rst
@@ -135,6 +135,27 @@ These ``struct kiocb`` flags are significant for buffered I/O with iomap:
* ``IOCB_DONTCACHE``: Turns on ``IOMAP_DONTCACHE``.
+``struct iomap_read_ops``
+--------------------------
+
+.. code-block:: c
+
+ struct iomap_read_ops {
+ int (*read_folio_range)(const struct iomap_iter *iter,
+ struct iomap_read_folio_ctx *ctx, size_t len);
+ void (*submit_read)(struct iomap_read_folio_ctx *ctx);
+ };
+
+iomap calls these functions:
+
+ - ``read_folio_range``: Called to read in the range. This must be provided
+ by the caller. If this succeeds, iomap_finish_folio_read() must be called
+ after the range is read in, regardless of whether the read succeeded or
+ failed.
+
+ - ``submit_read``: Submit any pending read requests. This function is
+ optional.
+
Internal per-Folio State
------------------------
@@ -182,6 +203,28 @@ The ``flags`` argument to ``->iomap_begin`` will be set to zero.
The pagecache takes whatever locks it needs before calling the
filesystem.
+Both ``iomap_readahead`` and ``iomap_read_folio`` pass in a ``struct
+iomap_read_folio_ctx``:
+
+.. code-block:: c
+
+ struct iomap_read_folio_ctx {
+ const struct iomap_read_ops *ops;
+ struct folio *cur_folio;
+ struct readahead_control *rac;
+ void *read_ctx;
+ };
+
+``iomap_readahead`` must set:
+ * ``ops->read_folio_range()`` and ``rac``
+
+``iomap_read_folio`` must set:
+ * ``ops->read_folio_range()`` and ``cur_folio``
+
+``ops->submit_read()`` and ``read_ctx`` are optional. ``read_ctx`` is used to
+pass in any custom data the caller needs accessible in the ops callbacks for
+fulfilling reads.
+
Buffered Writes
---------------
@@ -317,6 +360,9 @@ The fields are as follows:
delalloc reservations to avoid having delalloc reservations for
clean pagecache.
This function must be supplied by the filesystem.
+ If this succeeds, iomap_finish_folio_write() must be called once writeback
+ completes for the range, regardless of whether the writeback succeeded or
+ failed.
- ``writeback_submit``: Submit the previous built writeback context.
Block based file systems should use the iomap_ioend_writeback_submit
@@ -444,10 +490,6 @@ These ``struct kiocb`` flags are significant for direct I/O with iomap:
Only meaningful for asynchronous I/O, and only if the entire I/O can
be issued as a single ``struct bio``.
- * ``IOCB_DIO_CALLER_COMP``: Try to run I/O completion from the caller's
- process context.
- See ``linux/fs.h`` for more details.
-
Filesystems should call ``iomap_dio_rw`` from ``->read_iter`` and
``->write_iter``, and set ``FMODE_CAN_ODIRECT`` in the ``->open``
function for the file.
diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst
index 7233b04668fc..d33429294252 100644
--- a/Documentation/filesystems/porting.rst
+++ b/Documentation/filesystems/porting.rst
@@ -211,7 +211,7 @@ test and set for you.
e.g.::
inode = iget_locked(sb, ino);
- if (inode->i_state & I_NEW) {
+ if (inode_state_read_once(inode) & I_NEW) {
err = read_inode_from_disk(inode);
if (err < 0) {
iget_failed(inode);
@@ -1309,3 +1309,16 @@ a different length, use
vfs_parse_fs_qstr(fc, key, &QSTR_LEN(value, len))
instead.
+
+---
+
+**mandatory**
+
+vfs_mkdir() now returns a dentry - the one returned by ->mkdir(). If
+that dentry is different from the dentry passed in, including if it is
+an IS_ERR() dentry pointer, the original dentry is dput().
+
+When vfs_mkdir() returns an error, and so both dputs() the original
+dentry and doesn't provide a replacement, it also unlocks the parent.
+Consequently the return value from vfs_mkdir() can be passed to
+end_creating() and the parent will be unlocked precisely when necessary.
diff --git a/Documentation/filesystems/ramfs-rootfs-initramfs.rst b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
index fa4f81099cb4..a9d271e171c3 100644
--- a/Documentation/filesystems/ramfs-rootfs-initramfs.rst
+++ b/Documentation/filesystems/ramfs-rootfs-initramfs.rst
@@ -290,11 +290,11 @@ Why cpio rather than tar?
This decision was made back in December, 2001. The discussion started here:
- http://www.uwsg.iu.edu/hypermail/linux/kernel/0112.2/1538.html
+- https://lore.kernel.org/lkml/a03cke$640$1@cesium.transmeta.com/
And spawned a second thread (specifically on tar vs cpio), starting here:
- http://www.uwsg.iu.edu/hypermail/linux/kernel/0112.2/1587.html
+- https://lore.kernel.org/lkml/3C25A06D.7030408@zytor.com/
The quick and dirty summary version (which is no substitute for reading
the above threads) is:
@@ -310,7 +310,7 @@ the above threads) is:
either way about the archive format, and there are alternative tools,
such as:
- http://freecode.com/projects/afio
+ https://linux.die.net/man/1/afio
2) The cpio archive format chosen by the kernel is simpler and cleaner (and
thus easier to create and parse) than any of the (literally dozens of)
@@ -331,12 +331,12 @@ the above threads) is:
5) Al Viro made the decision (quote: "tar is ugly as hell and not going to be
supported on the kernel side"):
- http://www.uwsg.iu.edu/hypermail/linux/kernel/0112.2/1540.html
+ - https://lore.kernel.org/lkml/Pine.GSO.4.21.0112222109050.21702-100000@weyl.math.psu.edu/
explained his reasoning:
- - http://www.uwsg.iu.edu/hypermail/linux/kernel/0112.2/1550.html
- - http://www.uwsg.iu.edu/hypermail/linux/kernel/0112.2/1638.html
+ - https://lore.kernel.org/lkml/Pine.GSO.4.21.0112222240530.21702-100000@weyl.math.psu.edu/
+ - https://lore.kernel.org/lkml/Pine.GSO.4.21.0112230849550.23300-100000@weyl.math.psu.edu/
and, most importantly, designed and implemented the initramfs code.
diff --git a/Documentation/filesystems/resctrl.rst b/Documentation/filesystems/resctrl.rst
index b7f35b07876a..8c8ce678148a 100644
--- a/Documentation/filesystems/resctrl.rst
+++ b/Documentation/filesystems/resctrl.rst
@@ -17,17 +17,18 @@ AMD refers to this feature as AMD Platform Quality of Service(AMD QoS).
This feature is enabled by the CONFIG_X86_CPU_RESCTRL and the x86 /proc/cpuinfo
flag bits:
-=============================================== ================================
-RDT (Resource Director Technology) Allocation "rdt_a"
-CAT (Cache Allocation Technology) "cat_l3", "cat_l2"
-CDP (Code and Data Prioritization) "cdp_l3", "cdp_l2"
-CQM (Cache QoS Monitoring) "cqm_llc", "cqm_occup_llc"
-MBM (Memory Bandwidth Monitoring) "cqm_mbm_total", "cqm_mbm_local"
-MBA (Memory Bandwidth Allocation) "mba"
-SMBA (Slow Memory Bandwidth Allocation) ""
-BMEC (Bandwidth Monitoring Event Configuration) ""
-ABMC (Assignable Bandwidth Monitoring Counters) ""
-=============================================== ================================
+=============================================================== ================================
+RDT (Resource Director Technology) Allocation "rdt_a"
+CAT (Cache Allocation Technology) "cat_l3", "cat_l2"
+CDP (Code and Data Prioritization) "cdp_l3", "cdp_l2"
+CQM (Cache QoS Monitoring) "cqm_llc", "cqm_occup_llc"
+MBM (Memory Bandwidth Monitoring) "cqm_mbm_total", "cqm_mbm_local"
+MBA (Memory Bandwidth Allocation) "mba"
+SMBA (Slow Memory Bandwidth Allocation) ""
+BMEC (Bandwidth Monitoring Event Configuration) ""
+ABMC (Assignable Bandwidth Monitoring Counters) ""
+SDCIAE (Smart Data Cache Injection Allocation Enforcement) ""
+=============================================================== ================================
Historically, new features were made visible by default in /proc/cpuinfo. This
resulted in the feature flags becoming hard to parse by humans. Adding a new
@@ -72,6 +73,11 @@ The 'info' directory contains information about the enabled
resources. Each resource has its own subdirectory. The subdirectory
names reflect the resource names.
+Most of the files in the resource's subdirectory are read-only, and
+describe properties of the resource. Resources that support global
+configuration options also include writable files that can be used
+to modify those settings.
+
Each subdirectory contains the following files with respect to
allocation:
@@ -90,12 +96,19 @@ related to allocation:
must be set when writing a mask.
"shareable_bits":
- Bitmask of shareable resource with other executing
- entities (e.g. I/O). User can use this when
- setting up exclusive cache partitions. Note that
- some platforms support devices that have their
- own settings for cache use which can over-ride
- these bits.
+ Bitmask of shareable resource with other executing entities
+ (e.g. I/O). Applies to all instances of this resource. User
+ can use this when setting up exclusive cache partitions.
+ Note that some platforms support devices that have their
+ own settings for cache use which can over-ride these bits.
+
+ When "io_alloc" is enabled, a portion of each cache instance can
+ be configured for shared use between hardware and software.
+ "bit_usage" should be used to see which portions of each cache
+ instance is configured for hardware use via "io_alloc" feature
+ because every cache instance can have its "io_alloc" bitmask
+ configured independently via "io_alloc_cbm".
+
"bit_usage":
Annotated capacity bitmasks showing how all
instances of the resource are used. The legend is:
@@ -109,16 +122,16 @@ related to allocation:
"H":
Corresponding region is used by hardware only
but available for software use. If a resource
- has bits set in "shareable_bits" but not all
- of these bits appear in the resource groups'
- schematas then the bits appearing in
- "shareable_bits" but no resource group will
- be marked as "H".
+ has bits set in "shareable_bits" or "io_alloc_cbm"
+ but not all of these bits appear in the resource
+ groups' schemata then the bits appearing in
+ "shareable_bits" or "io_alloc_cbm" but no
+ resource group will be marked as "H".
"X":
Corresponding region is available for sharing and
- used by hardware and software. These are the
- bits that appear in "shareable_bits" as
- well as a resource group's allocation.
+ used by hardware and software. These are the bits
+ that appear in "shareable_bits" or "io_alloc_cbm"
+ as well as a resource group's allocation.
"S":
Corresponding region is used by software
and available for sharing.
@@ -136,6 +149,77 @@ related to allocation:
"1":
Non-contiguous 1s value in CBM is supported.
+"io_alloc":
+ "io_alloc" enables system software to configure the portion of
+ the cache allocated for I/O traffic. File may only exist if the
+ system supports this feature on some of its cache resources.
+
+ "disabled":
+ Resource supports "io_alloc" but the feature is disabled.
+ Portions of cache used for allocation of I/O traffic cannot
+ be configured.
+ "enabled":
+ Portions of cache used for allocation of I/O traffic
+ can be configured using "io_alloc_cbm".
+ "not supported":
+ Support not available for this resource.
+
+ The feature can be modified by writing to the interface, for example:
+
+ To enable::
+
+ # echo 1 > /sys/fs/resctrl/info/L3/io_alloc
+
+ To disable::
+
+ # echo 0 > /sys/fs/resctrl/info/L3/io_alloc
+
+ The underlying implementation may reduce resources available to
+ general (CPU) cache allocation. See architecture specific notes
+ below. Depending on usage requirements the feature can be enabled
+ or disabled.
+
+ On AMD systems, io_alloc feature is supported by the L3 Smart
+ Data Cache Injection Allocation Enforcement (SDCIAE). The CLOSID for
+ io_alloc is the highest CLOSID supported by the resource. When
+ io_alloc is enabled, the highest CLOSID is dedicated to io_alloc and
+ no longer available for general (CPU) cache allocation. When CDP is
+ enabled, io_alloc routes I/O traffic using the highest CLOSID allocated
+ for the instruction cache (CDP_CODE), making this CLOSID no longer
+ available for general (CPU) cache allocation for both the CDP_CODE
+ and CDP_DATA resources.
+
+"io_alloc_cbm":
+ Capacity bitmasks that describe the portions of cache instances to
+ which I/O traffic from supported I/O devices are routed when "io_alloc"
+ is enabled.
+
+ CBMs are displayed in the following format:
+
+ <cache_id0>=<cbm>;<cache_id1>=<cbm>;...
+
+ Example::
+
+ # cat /sys/fs/resctrl/info/L3/io_alloc_cbm
+ 0=ffff;1=ffff
+
+ CBMs can be configured by writing to the interface.
+
+ Example::
+
+ # echo 1=ff > /sys/fs/resctrl/info/L3/io_alloc_cbm
+ # cat /sys/fs/resctrl/info/L3/io_alloc_cbm
+ 0=ffff;1=00ff
+
+ # echo "0=ff;1=f" > /sys/fs/resctrl/info/L3/io_alloc_cbm
+ # cat /sys/fs/resctrl/info/L3/io_alloc_cbm
+ 0=00ff;1=000f
+
+ When CDP is enabled "io_alloc_cbm" associated with the CDP_DATA and CDP_CODE
+ resources may reflect the same values. For example, values read from and
+ written to /sys/fs/resctrl/info/L3DATA/io_alloc_cbm may be reflected by
+ /sys/fs/resctrl/info/L3CODE/io_alloc_cbm and vice versa.
+
Memory bandwidth(MB) subdirectory contains the following files
with respect to allocation:
diff --git a/Documentation/filesystems/xfs/xfs-online-fsck-design.rst b/Documentation/filesystems/xfs/xfs-online-fsck-design.rst
index 8cbcd3c26434..3d9233f403db 100644
--- a/Documentation/filesystems/xfs/xfs-online-fsck-design.rst
+++ b/Documentation/filesystems/xfs/xfs-online-fsck-design.rst
@@ -105,10 +105,8 @@ occur; this capability aids both strategies.
TLDR; Show Me the Code!
-----------------------
-Code is posted to the kernel.org git trees as follows:
-`kernel changes <https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-symlink>`_,
-`userspace changes <https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-media-scan-service>`_, and
-`QA test changes <https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=repair-dirs>`_.
+Kernel and userspace code has been fully merged as of October 2025.
+
Each kernel patchset adding an online repair function will use the same branch
name across the kernel, xfsprogs, and fstests git repos.
@@ -249,7 +247,7 @@ sharing and lock acquisition rules as the regular filesystem.
This means that scrub cannot take *any* shortcuts to save time, because doing
so could lead to concurrency problems.
In other words, online fsck is not a complete replacement for offline fsck, and
-a complete run of online fsck may take longer than online fsck.
+a complete run of online fsck may take longer than offline fsck.
However, both of these limitations are acceptable tradeoffs to satisfy the
different motivations of online fsck, which are to **minimize system downtime**
and to **increase predictability of operation**.
@@ -764,12 +762,8 @@ allow the online fsck developers to compare online fsck against offline fsck,
and they enable XFS developers to find deficiencies in the code base.
Proposed patchsets include
-`general fuzzer improvements
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=fuzzer-improvements>`_,
`fuzzing baselines
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=fuzz-baseline>`_,
-and `improvements in fuzz testing comprehensiveness
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=more-fuzz-testing>`_.
+<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=fuzz-baseline>`_.
Stress Testing
--------------
@@ -801,11 +795,6 @@ Success is defined by the ability to run all of these tests without observing
any unexpected filesystem shutdowns due to corrupted metadata, kernel hang
check warnings, or any other sort of mischief.
-Proposed patchsets include `general stress testing
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=race-scrub-and-mount-state-changes>`_
-and the `evolution of existing per-function stress testing
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=refactor-scrub-stress>`_.
-
4. User Interface
=================
@@ -886,10 +875,6 @@ apply as nice of a priority to IO and CPU scheduling as possible.
This measure was taken to minimize delays in the rest of the filesystem.
No such hardening has been performed for the cron job.
-Proposed patchset:
-`Enabling the xfs_scrub background service
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-media-scan-service>`_.
-
Health Reporting
----------------
@@ -912,13 +897,6 @@ notifications and initiate a repair?
*Answer*: These questions remain unanswered, but should be a part of the
conversation with early adopters and potential downstream users of XFS.
-Proposed patchsets include
-`wiring up health reports to correction returns
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=corruption-health-reports>`_
-and
-`preservation of sickness info during memory reclaim
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=indirect-health-reporting>`_.
-
5. Kernel Algorithms and Data Structures
========================================
@@ -1310,21 +1288,6 @@ Space allocation records are cross-referenced as follows:
are there the same number of reverse mapping records for each block as the
reference count record claims?
-Proposed patchsets are the series to find gaps in
-`refcount btree
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-detect-refcount-gaps>`_,
-`inode btree
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-detect-inobt-gaps>`_, and
-`rmap btree
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-detect-rmapbt-gaps>`_ records;
-to find
-`mergeable records
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-detect-mergeable-records>`_;
-and to
-`improve cross referencing with rmap
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-strengthen-rmap-checking>`_
-before starting a repair.
-
Checking Extended Attributes
````````````````````````````
@@ -1756,10 +1719,6 @@ For scrub, the drain works as follows:
To avoid polling in step 4, the drain provides a waitqueue for scrub threads to
be woken up whenever the intent count drops to zero.
-The proposed patchset is the
-`scrub intent drain series
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-drain-intents>`_.
-
.. _jump_labels:
Static Keys (aka Jump Label Patching)
@@ -2036,10 +1995,6 @@ The ``xfarray_store_anywhere`` function is used to insert a record in any
null record slot in the bag; and the ``xfarray_unset`` function removes a
record from the bag.
-The proposed patchset is the
-`big in-memory array
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=big-array>`_.
-
Iterating Array Elements
^^^^^^^^^^^^^^^^^^^^^^^^
@@ -2172,10 +2127,6 @@ However, it should be noted that these repair functions only use blob storage
to cache a small number of entries before adding them to a temporary ondisk
file, which is why compaction is not required.
-The proposed patchset is at the start of the
-`extended attribute repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-xattrs>`_ series.
-
.. _xfbtree:
In-Memory B+Trees
@@ -2214,11 +2165,6 @@ xfiles enables reuse of the entire btree library.
Btrees built atop an xfile are collectively known as ``xfbtrees``.
The next few sections describe how they actually work.
-The proposed patchset is the
-`in-memory btree
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=in-memory-btrees>`_
-series.
-
Using xfiles as a Buffer Cache Target
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -2459,14 +2405,6 @@ This enables the log to release the old EFI to keep the log moving forwards.
EFIs have a role to play during the commit and reaping phases; please see the
next section and the section about :ref:`reaping<reaping>` for more details.
-Proposed patchsets are the
-`bitmap rework
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-bitmap-rework>`_
-and the
-`preparation for bulk loading btrees
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-prep-for-bulk-loading>`_.
-
-
Writing the New Tree
````````````````````
@@ -2623,11 +2561,6 @@ The number of records for the inode btree is the number of xfarray records,
but the record count for the free inode btree has to be computed as inode chunk
records are stored in the xfarray.
-The proposed patchset is the
-`AG btree repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-ag-btrees>`_
-series.
-
Case Study: Rebuilding the Space Reference Counts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -2716,11 +2649,6 @@ Reverse mappings are added to the bag using ``xfarray_store_anywhere`` and
removed via ``xfarray_unset``.
Bag members are examined through ``xfarray_iter`` loops.
-The proposed patchset is the
-`AG btree repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-ag-btrees>`_
-series.
-
Case Study: Rebuilding File Fork Mapping Indices
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -2757,11 +2685,6 @@ EXTENTS format instead of BMBT, which may require a conversion.
Third, the incore extent map must be reloaded carefully to avoid disturbing
any delayed allocation extents.
-The proposed patchset is the
-`file mapping repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-file-mappings>`_
-series.
-
.. _reaping:
Reaping Old Metadata Blocks
@@ -2843,11 +2766,6 @@ blocks.
As stated earlier, online repair functions use very large transactions to
minimize the chances of this occurring.
-The proposed patchset is the
-`preparation for bulk loading btrees
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-prep-for-bulk-loading>`_
-series.
-
Case Study: Reaping After a Regular Btree Repair
````````````````````````````````````````````````
@@ -2943,11 +2861,6 @@ When the walk is complete, the bitmap disunion operation ``(ag_owner_bitmap &
btrees.
These blocks can then be reaped using the methods outlined above.
-The proposed patchset is the
-`AG btree repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-ag-btrees>`_
-series.
-
.. _rmap_reap:
Case Study: Reaping After Repairing Reverse Mapping Btrees
@@ -2972,11 +2885,6 @@ methods outlined above.
The rest of the process of rebuildng the reverse mapping btree is discussed
in a separate :ref:`case study<rmap_repair>`.
-The proposed patchset is the
-`AG btree repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-ag-btrees>`_
-series.
-
Case Study: Rebuilding the AGFL
```````````````````````````````
@@ -3024,11 +2932,6 @@ more complicated, because computing the correct value requires traversing the
forks, or if that fails, leaving the fields invalid and waiting for the fork
fsck functions to run.
-The proposed patchset is the
-`inode
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-inodes>`_
-repair series.
-
Quota Record Repairs
--------------------
@@ -3045,11 +2948,6 @@ checking are obviously bad limits and timer values.
Quota usage counters are checked, repaired, and discussed separately in the
section about :ref:`live quotacheck <quotacheck>`.
-The proposed patchset is the
-`quota
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-quota>`_
-repair series.
-
.. _fscounters:
Freezing to Fix Summary Counters
@@ -3145,11 +3043,6 @@ long enough to check and correct the summary counters.
| This bug was fixed in Linux 5.17. |
+--------------------------------------------------------------------------+
-The proposed patchset is the
-`summary counter cleanup
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-fscounters>`_
-series.
-
Full Filesystem Scans
---------------------
@@ -3277,15 +3170,6 @@ Second, if the incore inode is stuck in some intermediate state, the scan
coordinator must release the AGI and push the main filesystem to get the inode
back into a loadable state.
-The proposed patches are the
-`inode scanner
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-iscan>`_
-series.
-The first user of the new functionality is the
-`online quotacheck
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-quotacheck>`_
-series.
-
Inode Management
````````````````
@@ -3381,12 +3265,6 @@ To capture these nuances, the online fsck code has a separate ``xchk_irele``
function to set or clear the ``DONTCACHE`` flag to get the required release
behavior.
-Proposed patchsets include fixing
-`scrub iget usage
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-iget-fixes>`_ and
-`dir iget usage
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-dir-iget-fixes>`_.
-
.. _ilocking:
Locking Inodes
@@ -3443,11 +3321,6 @@ If the dotdot entry changes while the directory is unlocked, then a move or
rename operation must have changed the child's parentage, and the scan can
exit early.
-The proposed patchset is the
-`directory repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-dirs>`_
-series.
-
.. _fshooks:
Filesystem Hooks
@@ -3594,11 +3467,6 @@ The inode scan APIs are pretty simple:
- ``xchk_iscan_teardown`` to finish the scan
-This functionality is also a part of the
-`inode scanner
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-iscan>`_
-series.
-
.. _quotacheck:
Case Study: Quota Counter Checking
@@ -3686,11 +3554,6 @@ needing to hold any locks for a long duration.
If repairs are desired, the real and shadow dquots are locked and their
resource counts are set to the values in the shadow dquot.
-The proposed patchset is the
-`online quotacheck
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-quotacheck>`_
-series.
-
.. _nlinks:
Case Study: File Link Count Checking
@@ -3744,11 +3607,6 @@ shadow information.
If no parents are found, the file must be :ref:`reparented <orphanage>` to the
orphanage to prevent the file from being lost forever.
-The proposed patchset is the
-`file link count repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-nlinks>`_
-series.
-
.. _rmap_repair:
Case Study: Rebuilding Reverse Mapping Records
@@ -3828,11 +3686,6 @@ scan for reverse mapping records.
12. Free the xfbtree now that it not needed.
-The proposed patchset is the
-`rmap repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-rmap-btree>`_
-series.
-
Staging Repairs with Temporary Files on Disk
--------------------------------------------
@@ -3971,11 +3824,6 @@ Once a good copy of a data file has been constructed in a temporary file, it
must be conveyed to the file being repaired, which is the topic of the next
section.
-The proposed patches are in the
-`repair temporary files
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-tempfiles>`_
-series.
-
Logged File Content Exchanges
-----------------------------
@@ -4025,11 +3873,6 @@ The new ``XFS_SB_FEAT_INCOMPAT_EXCHRANGE`` incompatible feature flag
in the superblock protects these new log item records from being replayed on
old kernels.
-The proposed patchset is the
-`file contents exchange
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=atomic-file-updates>`_
-series.
-
+--------------------------------------------------------------------------+
| **Sidebar: Using Log-Incompatible Feature Flags** |
+--------------------------------------------------------------------------+
@@ -4323,11 +4166,6 @@ To repair the summary file, write the xfile contents into the temporary file
and use atomic mapping exchange to commit the new contents.
The temporary file is then reaped.
-The proposed patchset is the
-`realtime summary repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-rtsummary>`_
-series.
-
Case Study: Salvaging Extended Attributes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -4369,11 +4207,6 @@ Salvaging extended attributes is done as follows:
4. Reap the temporary file.
-The proposed patchset is the
-`extended attribute repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-xattrs>`_
-series.
-
Fixing Directories
------------------
@@ -4448,11 +4281,6 @@ Unfortunately, the current dentry cache design doesn't provide a means to walk
every child dentry of a specific directory, which makes this a hard problem.
There is no known solution.
-The proposed patchset is the
-`directory repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-dirs>`_
-series.
-
Parent Pointers
```````````````
@@ -4612,11 +4440,6 @@ a :ref:`directory entry live update hook <liveupdate>` as follows:
7. Reap the temporary directory.
-The proposed patchset is the
-`parent pointers directory repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-fsck>`_
-series.
-
Case Study: Repairing Parent Pointers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -4662,11 +4485,6 @@ directory reconstruction:
8. Reap the temporary file.
-The proposed patchset is the
-`parent pointers repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-fsck>`_
-series.
-
Digression: Offline Checking of Parent Pointers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -4755,11 +4573,6 @@ connectivity checks:
4. Move on to examining link counts, as we do today.
-The proposed patchset is the
-`offline parent pointers repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-fsck>`_
-series.
-
Rebuilding directories from parent pointers in offline repair would be very
challenging because xfs_repair currently uses two single-pass scans of the
filesystem during phases 3 and 4 to decide which files are corrupt enough to be
@@ -4903,12 +4716,6 @@ Repairing the directory tree works as follows:
6. If the subdirectory has zero paths, attach it to the lost and found.
-The proposed patches are in the
-`directory tree repair
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=scrub-directory-tree>`_
-series.
-
-
.. _orphanage:
The Orphanage
@@ -4973,11 +4780,6 @@ Orphaned files are adopted by the orphanage as follows:
7. If a runtime error happens, call ``xrep_adoption_cancel`` to release all
resources.
-The proposed patches are in the
-`orphanage adoption
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=repair-orphanage>`_
-series.
-
6. Userspace Algorithms and Data Structures
===========================================
@@ -5091,14 +4893,6 @@ first workqueue's workers until the backlog eases.
This doesn't completely solve the balancing problem, but reduces it enough to
move on to more pressing issues.
-The proposed patchsets are the scrub
-`performance tweaks
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-performance-tweaks>`_
-and the
-`inode scan rebalance
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-iscan-rebalance>`_
-series.
-
.. _scrubrepair:
Scheduling Repairs
@@ -5179,20 +4973,6 @@ immediately.
Corrupt file data blocks reported by phase 6 cannot be recovered by the
filesystem.
-The proposed patchsets are the
-`repair warning improvements
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-better-repair-warnings>`_,
-refactoring of the
-`repair data dependency
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-repair-data-deps>`_
-and
-`object tracking
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-object-tracking>`_,
-and the
-`repair scheduling
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=scrub-repair-scheduling>`_
-improvement series.
-
Checking Names for Confusable Unicode Sequences
-----------------------------------------------
@@ -5372,6 +5152,8 @@ The extra flexibility enables several new use cases:
This emulates an atomic device write in software, and can support arbitrary
scattered writes.
+(This functionality was merged into mainline as of 2025)
+
Vectorized Scrub
----------------
@@ -5393,13 +5175,7 @@ It is hoped that ``io_uring`` will pick up enough of this functionality that
online fsck can use that instead of adding a separate vectored scrub system
call to XFS.
-The relevant patchsets are the
-`kernel vectorized scrub
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=vectorized-scrub>`_
-and
-`userspace vectorized scrub
-<https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=vectorized-scrub>`_
-series.
+(This functionality was merged into mainline as of 2025)
Quality of Service Targets for Scrub
------------------------------------
diff --git a/Documentation/firmware-guide/acpi/i2c-muxes.rst b/Documentation/firmware-guide/acpi/i2c-muxes.rst
index f366539acd79..96ef4012d78f 100644
--- a/Documentation/firmware-guide/acpi/i2c-muxes.rst
+++ b/Documentation/firmware-guide/acpi/i2c-muxes.rst
@@ -37,8 +37,8 @@ which corresponds to the following ASL (in the scope of \_SB)::
Name (_HID, ...)
Name (_CRS, ResourceTemplate () {
I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED,
- AddressingMode7Bit, "\\_SB.SMB1.CH00", 0x00,
- ResourceConsumer,,)
+ AddressingMode7Bit, "\\_SB.SMB1.MUX0.CH00",
+ 0x00, ResourceConsumer,,)
}
}
}
@@ -52,8 +52,8 @@ which corresponds to the following ASL (in the scope of \_SB)::
Name (_HID, ...)
Name (_CRS, ResourceTemplate () {
I2cSerialBus (0x50, ControllerInitiated, I2C_SPEED,
- AddressingMode7Bit, "\\_SB.SMB1.CH01", 0x00,
- ResourceConsumer,,)
+ AddressingMode7Bit, "\\_SB.SMB1.MUX0.CH01",
+ 0x00, ResourceConsumer,,)
}
}
}
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index 5139705089f2..781129f78b06 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -92,6 +92,18 @@ GEM Atomic Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_gem_atomic_helper.c
:export:
+VBLANK Helper Reference
+-----------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c
+ :doc: overview
+
+.. kernel-doc:: include/drm/drm_vblank_helper.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c
+ :export:
+
Simple KMS Helper Reference
===========================
diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index abfe220764e1..2292e65f044c 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -413,6 +413,21 @@ Plane Panic Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_panic.c
:export:
+Colorop Abstraction
+===================
+
+.. kernel-doc:: drivers/gpu/drm/drm_colorop.c
+ :doc: overview
+
+Colorop Functions Reference
+---------------------------
+
+.. kernel-doc:: include/drm/drm_colorop.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_colorop.c
+ :export:
+
Display Modes Function Reference
================================
diff --git a/Documentation/gpu/nova/core/todo.rst b/Documentation/gpu/nova/core/todo.rst
index 0972cb905f7a..35cc7c31d423 100644
--- a/Documentation/gpu/nova/core/todo.rst
+++ b/Documentation/gpu/nova/core/todo.rst
@@ -44,25 +44,6 @@ automatically generates the corresponding mappings between a value and a number.
| Complexity: Beginner
| Link: https://docs.rs/num/latest/num/trait.FromPrimitive.html
-Conversion from byte slices for types implementing FromBytes [TRSM]
--------------------------------------------------------------------
-
-We retrieve several structures from byte streams coming from the BIOS or loaded
-firmware. At the moment converting the bytes slice into the proper type require
-an inelegant `unsafe` operation; this will go away once `FromBytes` implements
-a proper `from_bytes` method.
-
-| Complexity: Beginner
-
-CoherentAllocation improvements [COHA]
---------------------------------------
-
-`CoherentAllocation` needs a safe way to write into the allocation, and to
-obtain slices within the allocation.
-
-| Complexity: Beginner
-| Contact: Abdiel Janulgue
-
Generic register abstraction [REGA]
-----------------------------------
@@ -153,17 +134,6 @@ A `num` core kernel module is being designed to provide these operations.
| Complexity: Intermediate
| Contact: Alexandre Courbot
-Delay / Sleep abstractions [DLAY]
----------------------------------
-
-Rust abstractions for the kernel's delay() and sleep() functions.
-
-FUJITA Tomonori plans to work on abstractions for read_poll_timeout_atomic()
-(and friends) [1].
-
-| Complexity: Beginner
-| Link: https://lore.kernel.org/netdev/20250228.080550.354359820929821928.fujita.tomonori@gmail.com/ [1]
-
IRQ abstractions
----------------
diff --git a/Documentation/gpu/rfc/color_pipeline.rst b/Documentation/gpu/rfc/color_pipeline.rst
new file mode 100644
index 000000000000..cd1cc2d0f988
--- /dev/null
+++ b/Documentation/gpu/rfc/color_pipeline.rst
@@ -0,0 +1,378 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================
+Linux Color Pipeline API
+========================
+
+What problem are we solving?
+============================
+
+We would like to support pre-, and post-blending complex color
+transformations in display controller hardware in order to allow for
+HW-supported HDR use-cases, as well as to provide support to
+color-managed applications, such as video or image editors.
+
+It is possible to support an HDR output on HW supporting the Colorspace
+and HDR Metadata drm_connector properties, but that requires the
+compositor or application to render and compose the content into one
+final buffer intended for display. Doing so is costly.
+
+Most modern display HW offers various 1D LUTs, 3D LUTs, matrices, and other
+operations to support color transformations. These operations are often
+implemented in fixed-function HW and therefore much more power efficient than
+performing similar operations via shaders or CPU.
+
+We would like to make use of this HW functionality to support complex color
+transformations with no, or minimal CPU or shader load. The switch between HW
+fixed-function blocks and shaders/CPU must be seamless with no visible
+difference when fallback to shaders/CPU is neceesary at any time.
+
+
+How are other OSes solving this problem?
+========================================
+
+The most widely supported use-cases regard HDR content, whether video or
+gaming.
+
+Most OSes will specify the source content format (color gamut, encoding transfer
+function, and other metadata, such as max and average light levels) to a driver.
+Drivers will then program their fixed-function HW accordingly to map from a
+source content buffer's space to a display's space.
+
+When fixed-function HW is not available the compositor will assemble a shader to
+ask the GPU to perform the transformation from the source content format to the
+display's format.
+
+A compositor's mapping function and a driver's mapping function are usually
+entirely separate concepts. On OSes where a HW vendor has no insight into
+closed-source compositor code such a vendor will tune their color management
+code to visually match the compositor's. On other OSes, where both mapping
+functions are open to an implementer they will ensure both mappings match.
+
+This results in mapping algorithm lock-in, meaning that no-one alone can
+experiment with or introduce new mapping algorithms and achieve
+consistent results regardless of which implementation path is taken.
+
+Why is Linux different?
+=======================
+
+Unlike other OSes, where there is one compositor for one or more drivers, on
+Linux we have a many-to-many relationship. Many compositors; many drivers.
+In addition each compositor vendor or community has their own view of how
+color management should be done. This is what makes Linux so beautiful.
+
+This means that a HW vendor can now no longer tune their driver to one
+compositor, as tuning it to one could make it look fairly different from
+another compositor's color mapping.
+
+We need a better solution.
+
+
+Descriptive API
+===============
+
+An API that describes the source and destination colorspaces is a descriptive
+API. It describes the input and output color spaces but does not describe
+how precisely they should be mapped. Such a mapping includes many minute
+design decision that can greatly affect the look of the final result.
+
+It is not feasible to describe such mapping with enough detail to ensure the
+same result from each implementation. In fact, these mappings are a very active
+research area.
+
+
+Prescriptive API
+================
+
+A prescriptive API describes not the source and destination colorspaces. It
+instead prescribes a recipe for how to manipulate pixel values to arrive at the
+desired outcome.
+
+This recipe is generally an ordered list of straight-forward operations,
+with clear mathematical definitions, such as 1D LUTs, 3D LUTs, matrices,
+or other operations that can be described in a precise manner.
+
+
+The Color Pipeline API
+======================
+
+HW color management pipelines can significantly differ between HW
+vendors in terms of availability, ordering, and capabilities of HW
+blocks. This makes a common definition of color management blocks and
+their ordering nigh impossible. Instead we are defining an API that
+allows user space to discover the HW capabilities in a generic manner,
+agnostic of specific drivers and hardware.
+
+
+drm_colorop Object
+==================
+
+To support the definition of color pipelines we define the DRM core
+object type drm_colorop. Individual drm_colorop objects will be chained
+via the NEXT property of a drm_colorop to constitute a color pipeline.
+Each drm_colorop object is unique, i.e., even if multiple color
+pipelines have the same operation they won't share the same drm_colorop
+object to describe that operation.
+
+Note that drivers are not expected to map drm_colorop objects statically
+to specific HW blocks. The mapping of drm_colorop objects is entirely a
+driver-internal detail and can be as dynamic or static as a driver needs
+it to be. See more in the Driver Implementation Guide section below.
+
+Each drm_colorop has three core properties:
+
+TYPE: An enumeration property, defining the type of transformation, such as
+* enumerated curve
+* custom (uniform) 1D LUT
+* 3x3 matrix
+* 3x4 matrix
+* 3D LUT
+* etc.
+
+Depending on the type of transformation other properties will describe
+more details.
+
+BYPASS: A boolean property that can be used to easily put a block into
+bypass mode. The BYPASS property is not mandatory for a colorop, as long
+as the entire pipeline can get bypassed by setting the COLOR_PIPELINE on
+a plane to '0'.
+
+NEXT: The ID of the next drm_colorop in a color pipeline, or 0 if this
+drm_colorop is the last in the chain.
+
+An example of a drm_colorop object might look like one of these::
+
+ /* 1D enumerated curve */
+ Color operation 42
+ ├─ "TYPE": immutable enum {1D enumerated curve, 1D LUT, 3x3 matrix, 3x4 matrix, 3D LUT, etc.} = 1D enumerated curve
+ ├─ "BYPASS": bool {true, false}
+ ├─ "CURVE_1D_TYPE": enum {sRGB EOTF, sRGB inverse EOTF, PQ EOTF, PQ inverse EOTF, …}
+ └─ "NEXT": immutable color operation ID = 43
+
+ /* custom 4k entry 1D LUT */
+ Color operation 52
+ ├─ "TYPE": immutable enum {1D enumerated curve, 1D LUT, 3x3 matrix, 3x4 matrix, 3D LUT, etc.} = 1D LUT
+ ├─ "BYPASS": bool {true, false}
+ ├─ "SIZE": immutable range = 4096
+ ├─ "DATA": blob
+ └─ "NEXT": immutable color operation ID = 0
+
+ /* 17^3 3D LUT */
+ Color operation 72
+ ├─ "TYPE": immutable enum {1D enumerated curve, 1D LUT, 3x3 matrix, 3x4 matrix, 3D LUT, etc.} = 3D LUT
+ ├─ "BYPASS": bool {true, false}
+ ├─ "SIZE": immutable range = 17
+ ├─ "DATA": blob
+ └─ "NEXT": immutable color operation ID = 73
+
+drm_colorop extensibility
+-------------------------
+
+Unlike existing DRM core objects, like &drm_plane, drm_colorop is not
+extensible. This simplifies implementations and keeps all functionality
+for managing &drm_colorop objects in the DRM core.
+
+If there is a need one may introduce a simple &drm_colorop_funcs
+function table in the future, for example to support an IN_FORMATS
+property on a &drm_colorop.
+
+If a driver requires the ability to create a driver-specific colorop
+object they will need to add &drm_colorop func table support with
+support for the usual functions, like destroy, atomic_duplicate_state,
+and atomic_destroy_state.
+
+
+COLOR_PIPELINE Plane Property
+=============================
+
+Color Pipelines are created by a driver and advertised via a new
+COLOR_PIPELINE enum property on each plane. Values of the property
+always include object id 0, which is the default and means all color
+processing is disabled. Additional values will be the object IDs of the
+first drm_colorop in a pipeline. A driver can create and advertise none,
+one, or more possible color pipelines. A DRM client will select a color
+pipeline by setting the COLOR PIPELINE to the respective value.
+
+NOTE: Many DRM clients will set enumeration properties via the string
+value, often hard-coding it. Since this enumeration is generated based
+on the colorop object IDs it is important to perform the Color Pipeline
+Discovery, described below, instead of hard-coding color pipeline
+assignment. Drivers might generate the enum strings dynamically.
+Hard-coded strings might only work for specific drivers on a specific
+pieces of HW. Color Pipeline Discovery can work universally, as long as
+drivers implement the required color operations.
+
+The COLOR_PIPELINE property is only exposed when the
+DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set. Drivers shall ignore any
+existing pre-blend color operations when this cap is set, such as
+COLOR_RANGE and COLOR_ENCODING. If drivers want to support COLOR_RANGE
+or COLOR_ENCODING functionality when the color pipeline client cap is
+set, they are expected to expose colorops in the pipeline to allow for
+the appropriate color transformation.
+
+Setting of the COLOR_PIPELINE plane property or drm_colorop properties
+is only allowed for userspace that sets this client cap.
+
+An example of a COLOR_PIPELINE property on a plane might look like this::
+
+ Plane 10
+ ├─ "TYPE": immutable enum {Overlay, Primary, Cursor} = Primary
+ ├─ …
+ └─ "COLOR_PIPELINE": enum {0, 42, 52} = 0
+
+
+Color Pipeline Discovery
+========================
+
+A DRM client wanting color management on a drm_plane will:
+
+1. Get the COLOR_PIPELINE property of the plane
+2. iterate all COLOR_PIPELINE enum values
+3. for each enum value walk the color pipeline (via the NEXT pointers)
+ and see if the available color operations are suitable for the
+ desired color management operations
+
+If userspace encounters an unknown or unsuitable color operation during
+discovery it does not need to reject the entire color pipeline outright,
+as long as the unknown or unsuitable colorop has a "BYPASS" property.
+Drivers will ensure that a bypassed block does not have any effect.
+
+An example of chained properties to define an AMD pre-blending color
+pipeline might look like this::
+
+ Plane 10
+ ├─ "TYPE" (immutable) = Primary
+ └─ "COLOR_PIPELINE": enum {0, 44} = 0
+
+ Color operation 44
+ ├─ "TYPE" (immutable) = 1D enumerated curve
+ ├─ "BYPASS": bool
+ ├─ "CURVE_1D_TYPE": enum {sRGB EOTF, PQ EOTF} = sRGB EOTF
+ └─ "NEXT" (immutable) = 45
+
+ Color operation 45
+ ├─ "TYPE" (immutable) = 3x4 Matrix
+ ├─ "BYPASS": bool
+ ├─ "DATA": blob
+ └─ "NEXT" (immutable) = 46
+
+ Color operation 46
+ ├─ "TYPE" (immutable) = 1D enumerated curve
+ ├─ "BYPASS": bool
+ ├─ "CURVE_1D_TYPE": enum {sRGB Inverse EOTF, PQ Inverse EOTF} = sRGB EOTF
+ └─ "NEXT" (immutable) = 47
+
+ Color operation 47
+ ├─ "TYPE" (immutable) = 1D LUT
+ ├─ "SIZE": immutable range = 4096
+ ├─ "DATA": blob
+ └─ "NEXT" (immutable) = 48
+
+ Color operation 48
+ ├─ "TYPE" (immutable) = 3D LUT
+ ├─ "DATA": blob
+ └─ "NEXT" (immutable) = 49
+
+ Color operation 49
+ ├─ "TYPE" (immutable) = 1D enumerated curve
+ ├─ "BYPASS": bool
+ ├─ "CURVE_1D_TYPE": enum {sRGB EOTF, PQ EOTF} = sRGB EOTF
+ └─ "NEXT" (immutable) = 0
+
+
+Color Pipeline Programming
+==========================
+
+Once a DRM client has found a suitable pipeline it will:
+
+1. Set the COLOR_PIPELINE enum value to the one pointing at the first
+ drm_colorop object of the desired pipeline
+2. Set the properties for all drm_colorop objects in the pipeline to the
+ desired values, setting BYPASS to true for unused drm_colorop blocks,
+ and false for enabled drm_colorop blocks
+3. Perform (TEST_ONLY or not) atomic commit with all the other KMS
+ states it wishes to change
+
+To configure the pipeline for an HDR10 PQ plane and blending in linear
+space, a compositor might perform an atomic commit with the following
+property values::
+
+ Plane 10
+ └─ "COLOR_PIPELINE" = 42
+
+ Color operation 42
+ └─ "BYPASS" = true
+
+ Color operation 44
+ └─ "BYPASS" = true
+
+ Color operation 45
+ └─ "BYPASS" = true
+
+ Color operation 46
+ └─ "BYPASS" = true
+
+ Color operation 47
+ ├─ "DATA" = Gamut mapping + tone mapping + night mode
+ └─ "BYPASS" = false
+
+ Color operation 48
+ ├─ "CURVE_1D_TYPE" = PQ EOTF
+ └─ "BYPASS" = false
+
+
+Driver Implementer's Guide
+==========================
+
+What does this all mean for driver implementations? As noted above the
+colorops can map to HW directly but don't need to do so. Here are some
+suggestions on how to think about creating your color pipelines:
+
+- Try to expose pipelines that use already defined colorops, even if
+ your hardware pipeline is split differently. This allows existing
+ userspace to immediately take advantage of the hardware.
+
+- Additionally, try to expose your actual hardware blocks as colorops.
+ Define new colorop types where you believe it can offer significant
+ benefits if userspace learns to program them.
+
+- Avoid defining new colorops for compound operations with very narrow
+ scope. If you have a hardware block for a special operation that
+ cannot be split further, you can expose that as a new colorop type.
+ However, try to not define colorops for "use cases", especially if
+ they require you to combine multiple hardware blocks.
+
+- Design new colorops as prescriptive, not descriptive; by the
+ mathematical formula, not by the assumed input and output.
+
+A defined colorop type must be deterministic. The exact behavior of the
+colorop must be documented entirely, whether via a mathematical formula
+or some other description. Its operation can depend only on its
+properties and input and nothing else, allowed error tolerance
+notwithstanding.
+
+
+Driver Forward/Backward Compatibility
+=====================================
+
+As this is uAPI drivers can't regress color pipelines that have been
+introduced for a given HW generation. New HW generations are free to
+abandon color pipelines advertised for previous generations.
+Nevertheless, it can be beneficial to carry support for existing color
+pipelines forward as those will likely already have support in DRM
+clients.
+
+Introducing new colorops to a pipeline is fine, as long as they can be
+bypassed or are purely informational. DRM clients implementing support
+for the pipeline can always skip unknown properties as long as they can
+be confident that doing so will not cause unexpected results.
+
+If a new colorop doesn't fall into one of the above categories
+(bypassable or informational) the modified pipeline would be unusable
+for user space. In this case a new pipeline should be defined.
+
+
+References
+==========
+
+1. https://lore.kernel.org/dri-devel/QMers3awXvNCQlyhWdTtsPwkp5ie9bze_hD5nAccFW7a_RXlWjYB7MoUW_8CKLT2bSQwIXVi5H6VULYIxCdgvryZoAoJnC5lZgyK1QWn488=@emersion.fr/ \ No newline at end of file
diff --git a/Documentation/gpu/rfc/index.rst b/Documentation/gpu/rfc/index.rst
index 396e535377fb..ef19b0ba2a3e 100644
--- a/Documentation/gpu/rfc/index.rst
+++ b/Documentation/gpu/rfc/index.rst
@@ -35,3 +35,6 @@ host such documentation:
.. toctree::
i915_vm_bind.rst
+
+.. toctree::
+ color_pipeline.rst \ No newline at end of file
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index b5f58b4274b1..9013ced318cb 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -623,6 +623,43 @@ Contact: Thomas Zimmermann <tzimmermann@suse.de>, Simona Vetter
Level: Advanced
+Implement a new DUMB_CREATE2 ioctl
+----------------------------------
+
+The current DUMB_CREATE ioctl is not well defined. Instead of a pixel and
+framebuffer format, it only accepts a color mode of vague semantics. Assuming
+a linear framebuffer, the color mode gives an idea of the supported pixel
+format. But userspace effectively has to guess the correct values. It really
+only works reliably with framebuffers in XRGB8888. Userspace has begun to
+workaround these limitations by computing arbitrary format's buffer sizes and
+calculating their sizes in terms of XRGB8888 pixels.
+
+One possible solution is a new ioctl DUMB_CREATE2. It should accept a DRM
+format and a format modifier to resolve the color mode's ambiguity. As
+framebuffers can be multi-planar, the new ioctl has to return the buffer size,
+pitch and GEM handle for each individual color plane.
+
+In the first step, the new ioctl can be limited to the current features of
+the existing DUMB_CREATE. Individual drivers can then be extended to support
+multi-planar formats. Rockchip might require this and would be a good candidate.
+
+It might also be helpful to userspace to query information about the size of
+a potential buffer, if allocated. Userspace would supply geometry and format;
+the kernel would return minimal allocation sizes and scanline pitch. There is
+interest to allocate that memory from another device and provide it to the
+DRM driver (say via dma-buf).
+
+Another requested feature is the ability to allocate a buffer by size, without
+format. Accelators use this for their buffer allocation and it could likely be
+generalized.
+
+In addition to the kernel implementation, there must be user-space support
+for the new ioctl. There's code in Mesa that might be able to use the new
+call.
+
+Contact: Thomas Zimmermann <tzimmermann@suse.de>
+
+Level: Advanced
Better Testing
==============
diff --git a/Documentation/gpu/vkms.rst b/Documentation/gpu/vkms.rst
index 8a8b1002931f..1e79e62a6bc4 100644
--- a/Documentation/gpu/vkms.rst
+++ b/Documentation/gpu/vkms.rst
@@ -51,6 +51,97 @@ To disable the driver, use ::
sudo modprobe -r vkms
+Configuring With Configfs
+=========================
+
+It is possible to create and configure multiple VKMS instances via configfs.
+
+Start by mounting configfs and loading VKMS::
+
+ sudo mount -t configfs none /config
+ sudo modprobe vkms
+
+Once VKMS is loaded, ``/config/vkms`` is created automatically. Each directory
+under ``/config/vkms`` represents a VKMS instance, create a new one::
+
+ sudo mkdir /config/vkms/my-vkms
+
+By default, the instance is disabled::
+
+ cat /config/vkms/my-vkms/enabled
+ 0
+
+And directories are created for each configurable item of the display pipeline::
+
+ tree /config/vkms/my-vkms
+ ├── connectors
+ ├── crtcs
+ ├── enabled
+ ├── encoders
+ └── planes
+
+To add items to the display pipeline, create one or more directories under the
+available paths.
+
+Start by creating one or more planes::
+
+ sudo mkdir /config/vkms/my-vkms/planes/plane0
+
+Planes have 1 configurable attribute:
+
+- type: Plane type: 0 overlay, 1 primary, 2 cursor (same values as those
+ exposed by the "type" property of a plane)
+
+Continue by creating one or more CRTCs::
+
+ sudo mkdir /config/vkms/my-vkms/crtcs/crtc0
+
+CRTCs have 1 configurable attribute:
+
+- writeback: Enable or disable writeback connector support by writing 1 or 0
+
+Next, create one or more encoders::
+
+ sudo mkdir /config/vkms/my-vkms/encoders/encoder0
+
+Last but not least, create one or more connectors::
+
+ sudo mkdir /config/vkms/my-vkms/connectors/connector0
+
+Connectors have 1 configurable attribute:
+
+- status: Connection status: 1 connected, 2 disconnected, 3 unknown (same values
+ as those exposed by the "status" property of a connector)
+
+To finish the configuration, link the different pipeline items::
+
+ sudo ln -s /config/vkms/my-vkms/crtcs/crtc0 /config/vkms/my-vkms/planes/plane0/possible_crtcs
+ sudo ln -s /config/vkms/my-vkms/crtcs/crtc0 /config/vkms/my-vkms/encoders/encoder0/possible_crtcs
+ sudo ln -s /config/vkms/my-vkms/encoders/encoder0 /config/vkms/my-vkms/connectors/connector0/possible_encoders
+
+Since at least one primary plane is required, make sure to set the right type::
+
+ echo "1" | sudo tee /config/vkms/my-vkms/planes/plane0/type
+
+Once you are done configuring the VKMS instance, enable it::
+
+ echo "1" | sudo tee /config/vkms/my-vkms/enabled
+
+Finally, you can remove the VKMS instance disabling it::
+
+ echo "0" | sudo tee /config/vkms/my-vkms/enabled
+
+And removing the top level directory and its subdirectories::
+
+ sudo rm /config/vkms/my-vkms/planes/*/possible_crtcs/*
+ sudo rm /config/vkms/my-vkms/encoders/*/possible_crtcs/*
+ sudo rm /config/vkms/my-vkms/connectors/*/possible_encoders/*
+ sudo rmdir /config/vkms/my-vkms/planes/*
+ sudo rmdir /config/vkms/my-vkms/crtcs/*
+ sudo rmdir /config/vkms/my-vkms/encoders/*
+ sudo rmdir /config/vkms/my-vkms/connectors/*
+ sudo rmdir /config/vkms/my-vkms
+
Testing With IGT
================
@@ -68,26 +159,23 @@ To return to graphical mode, do::
sudo systemctl isolate graphical.target
-Once you are in text only mode, you can run tests using the --device switch
-or IGT_DEVICE variable to specify the device filter for the driver we want
-to test. IGT_DEVICE can also be used with the run-test.sh script to run the
+Once you are in text only mode, you can run tests using the IGT_FORCE_DRIVER
+variable to specify the device filter for the driver we want to test.
+IGT_FORCE_DRIVER can also be used with the run-tests.sh script to run the
tests for a specific driver::
- sudo ./build/tests/<name of test> --device "sys:/sys/devices/platform/vkms"
- sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./build/tests/<name of test>
- sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./scripts/run-tests.sh -t <name of test>
+ sudo IGT_FORCE_DRIVER="vkms" ./build/tests/<name of test>
+ sudo IGT_FORCE_DRIVER="vkms" ./scripts/run-tests.sh -t <name of test>
For example, to test the functionality of the writeback library,
we can run the kms_writeback test::
- sudo ./build/tests/kms_writeback --device "sys:/sys/devices/platform/vkms"
- sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./build/tests/kms_writeback
- sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./scripts/run-tests.sh -t kms_writeback
+ sudo IGT_FORCE_DRIVER="vkms" ./build/tests/kms_writeback
+ sudo IGT_FORCE_DRIVER="vkms" ./scripts/run-tests.sh -t kms_writeback
You can also run subtests if you do not want to run the entire test::
- sudo ./build/tests/kms_flip --run-subtest basic-plain-flip --device "sys:/sys/devices/platform/vkms"
- sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./build/tests/kms_flip --run-subtest basic-plain-flip
+ sudo IGT_FORCE_DRIVER="vkms" ./build/tests/kms_flip --run-subtest basic-plain-flip
Testing With KUnit
==================
@@ -147,21 +235,14 @@ Runtime Configuration
---------------------
We want to be able to reconfigure vkms instance without having to reload the
-module. Use/Test-cases:
+module through configfs. Use/Test-cases:
- Hotplug/hotremove connectors on the fly (to be able to test DP MST handling
of compositors).
-- Configure planes/crtcs/connectors (we'd need some code to have more than 1 of
- them first).
-
- Change output configuration: Plug/unplug screens, change EDID, allow changing
the refresh rate.
-The currently proposed solution is to expose vkms configuration through
-configfs. All existing module options should be supported through configfs
-too.
-
Writeback support
-----------------
diff --git a/Documentation/gpu/xe/index.rst b/Documentation/gpu/xe/index.rst
index 88b22fad880e..bc432c95d1a3 100644
--- a/Documentation/gpu/xe/index.rst
+++ b/Documentation/gpu/xe/index.rst
@@ -14,6 +14,7 @@ DG2, etc is provided to prototype the driver.
xe_mm
xe_map
xe_migrate
+ xe_exec_queue
xe_cs
xe_pm
xe_gt_freq
diff --git a/Documentation/gpu/xe/xe_exec_queue.rst b/Documentation/gpu/xe/xe_exec_queue.rst
new file mode 100644
index 000000000000..6076569e311c
--- /dev/null
+++ b/Documentation/gpu/xe/xe_exec_queue.rst
@@ -0,0 +1,20 @@
+.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+===============
+Execution Queue
+===============
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
+ :doc: Execution Queue
+
+Internal API
+============
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue_types.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
+ :internal:
diff --git a/Documentation/gpu/xe/xe_gt_freq.rst b/Documentation/gpu/xe/xe_gt_freq.rst
index c0811200e327..182d6aabeee1 100644
--- a/Documentation/gpu/xe/xe_gt_freq.rst
+++ b/Documentation/gpu/xe/xe_gt_freq.rst
@@ -7,6 +7,9 @@ Xe GT Frequency Management
.. kernel-doc:: drivers/gpu/drm/xe/xe_gt_freq.c
:doc: Xe GT Frequency Management
+.. kernel-doc:: drivers/gpu/drm/xe/xe_gt_throttle.c
+ :doc: Xe GT Throttle
+
Internal API
============
diff --git a/Documentation/hid/hid-alps.rst b/Documentation/hid/hid-alps.rst
index 94382bb0ada4..4a22a357f00c 100644
--- a/Documentation/hid/hid-alps.rst
+++ b/Documentation/hid/hid-alps.rst
@@ -69,6 +69,7 @@ To read/write to RAM, need to send a command to the device.
The command format is as below.
DataByte(SET_REPORT)
+~~~~~~~~~~~~~~~~~~~~
===== ======================
Byte1 Command Byte
@@ -89,6 +90,7 @@ Value Byte is writing data when you send the write commands.
When you read RAM, there is no meaning.
DataByte(GET_REPORT)
+~~~~~~~~~~~~~~~~~~~~
===== ======================
Byte1 Response Byte
@@ -104,8 +106,10 @@ Read value is stored in Value Byte.
Packet Format
+-------------
+
Touchpad data byte
-------------------
+~~~~~~~~~~~~~~~~~~
======= ======= ======= ======= ======= ======= ======= ======= =====
@@ -156,7 +160,7 @@ Zsn_6-0(7bit):
StickPointer data byte
-----------------------
+~~~~~~~~~~~~~~~~~~~~~~
======= ======= ======= ======= ======= ======= ======= ======= =====
- b7 b6 b5 b4 b3 b2 b1 b0
diff --git a/Documentation/hwmon/aht10.rst b/Documentation/hwmon/aht10.rst
index 213644b4ecba..7903b6434326 100644
--- a/Documentation/hwmon/aht10.rst
+++ b/Documentation/hwmon/aht10.rst
@@ -20,6 +20,14 @@ Supported chips:
English: http://www.aosong.com/userfiles/files/media/Data%20Sheet%20AHT20.pdf
+ * Aosong DHT20
+
+ Prefix: 'dht20'
+
+ Addresses scanned: None
+
+ Datasheet: https://www.digikey.co.nz/en/htmldatasheets/production/9184855/0/0/1/101020932
+
Author: Johannes Cornelis Draaijer <jcdra1@gmail.com>
@@ -33,7 +41,7 @@ The address of this i2c device may only be 0x38
Special Features
----------------
-AHT20 has additional CRC8 support which is sent as the last byte of the sensor
+AHT20, DHT20 has additional CRC8 support which is sent as the last byte of the sensor
values.
Usage Notes
diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst
index a5a58c00c322..232885f24430 100644
--- a/Documentation/hwmon/asus_ec_sensors.rst
+++ b/Documentation/hwmon/asus_ec_sensors.rst
@@ -9,6 +9,7 @@ Supported boards:
* PRIME X570-PRO
* PRIME X670E-PRO WIFI
* PRIME Z270-A
+ * Pro WS TRX50-SAGE WIFI
* Pro WS X570-ACE
* Pro WS WRX90E-SAGE SE
* ProArt X570-CREATOR WIFI
@@ -29,14 +30,17 @@ Supported boards:
* ROG STRIX B550-I GAMING
* ROG STRIX B650E-I GAMING WIFI
* ROG STRIX B850-I GAMING WIFI
+ * ROG STRIX X470-I GAMING
* ROG STRIX X570-E GAMING
* ROG STRIX X570-E GAMING WIFI II
* ROG STRIX X570-F GAMING
* ROG STRIX X570-I GAMING
* ROG STRIX X670E-E GAMING WIFI
* ROG STRIX X670E-I GAMING WIFI
+ * ROG STRIX X870-F GAMING WIFI
* ROG STRIX X870-I GAMING WIFI
* ROG STRIX X870E-E GAMING WIFI
+ * ROG STRIX X870E-H GAMING WIFI7
* ROG STRIX Z390-F GAMING
* ROG STRIX Z490-F GAMING
* ROG STRIX Z690-A GAMING WIFI D4
diff --git a/Documentation/hwmon/ds1621.rst b/Documentation/hwmon/ds1621.rst
index 552b37e9dd34..d0808720aa07 100644
--- a/Documentation/hwmon/ds1621.rst
+++ b/Documentation/hwmon/ds1621.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: none
- Datasheet: Publicly available from www.maximintegrated.com
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1621.pdf
* Dallas Semiconductor DS1625
@@ -17,7 +17,7 @@ Supported chips:
Addresses scanned: none
- Datasheet: Publicly available from www.datasheetarchive.com
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1620.pdf
* Maxim Integrated DS1631
@@ -25,7 +25,7 @@ Supported chips:
Addresses scanned: none
- Datasheet: Publicly available from www.maximintegrated.com
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1631-DS1731.pdf
* Maxim Integrated DS1721
@@ -33,7 +33,7 @@ Supported chips:
Addresses scanned: none
- Datasheet: Publicly available from www.maximintegrated.com
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1721.pdf
* Maxim Integrated DS1731
@@ -41,7 +41,7 @@ Supported chips:
Addresses scanned: none
- Datasheet: Publicly available from www.maximintegrated.com
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS1631-DS1731.pdf
Authors:
- Christian W. Zuckschwerdt <zany@triq.net>
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 51a5bdf75b08..85d7a686883e 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -144,6 +144,7 @@ Hardware Monitoring Kernel Drivers
ltc4261
ltc4282
ltc4286
+ macsmc-hwmon
max127
max15301
max16064
@@ -151,6 +152,7 @@ Hardware Monitoring Kernel Drivers
max1619
max16601
max1668
+ max17616
max197
max20730
max20751
@@ -177,6 +179,7 @@ Hardware Monitoring Kernel Drivers
mp2869
mp2888
mp2891
+ mp2925
mp29502
mp2975
mp2993
@@ -184,6 +187,7 @@ Hardware Monitoring Kernel Drivers
mp5920
mp5990
mp9941
+ mp9945
mpq8785
nct6683
nct6775
@@ -253,6 +257,7 @@ Hardware Monitoring Kernel Drivers
tps40422
tps53679
tps546d24
+ tsc1641
twl4030-madc-hwmon
ucd9000
ucd9200
diff --git a/Documentation/hwmon/isl68137.rst b/Documentation/hwmon/isl68137.rst
index 5bc029c98383..e77f582c2850 100644
--- a/Documentation/hwmon/isl68137.rst
+++ b/Documentation/hwmon/isl68137.rst
@@ -414,6 +414,16 @@ Supported chips:
Publicly available (after August 2020 launch) at the Renesas website
+ * Renesas RAA229141
+
+ Prefix: 'raa229141'
+
+ Addresses scanned: -
+
+ Datasheet:
+
+ Provided by Renesas upon request and NDA
+
Authors:
- Maxim Sloyko <maxims@google.com>
- Robert Lippert <rlippert@google.com>
diff --git a/Documentation/hwmon/jc42.rst b/Documentation/hwmon/jc42.rst
index 19d10512f6c0..3736e63db2a8 100644
--- a/Documentation/hwmon/jc42.rst
+++ b/Documentation/hwmon/jc42.rst
@@ -33,7 +33,7 @@ Supported chips:
Datasheets:
- http://datasheets.maxim-ic.com/en/ds/MAX6604.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6604.pdf
* Microchip MCP9804, MCP9805, MCP9808, MCP98242, MCP98243, MCP98244, MCP9843
diff --git a/Documentation/hwmon/lm75.rst b/Documentation/hwmon/lm75.rst
index 908b3a9df06e..4269da04508e 100644
--- a/Documentation/hwmon/lm75.rst
+++ b/Documentation/hwmon/lm75.rst
@@ -23,15 +23,17 @@ Supported chips:
http://www.national.com/
- * Dallas Semiconductor (now Maxim) DS75, DS1775, DS7505
+ * Dallas Semiconductor (now Analog Devices) DS75, DS1775, DS7505
Prefixes: 'ds75', 'ds1775', 'ds7505'
Addresses scanned: none
- Datasheet: Publicly available at the Maxim website
+ Datasheets:
- https://www.maximintegrated.com/
+ https://www.analog.com/media/en/technical-documentation/data-sheets/DS75.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/DS1775.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/DS7505.pdf
* Maxim MAX6625, MAX6626, MAX31725, MAX31726
@@ -39,9 +41,10 @@ Supported chips:
Addresses scanned: none
- Datasheet: Publicly available at the Maxim website
+ Datasheets:
- http://www.maxim-ic.com/
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6625-MAX6626.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31725-MAX31726.pdf
* Microchip (TelCom) TCN75
diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst
index 98452eed16d5..79c023521d39 100644
--- a/Documentation/hwmon/lm90.rst
+++ b/Documentation/hwmon/lm90.rst
@@ -9,7 +9,9 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheet: Publicly available at the National Semiconductor website
+ Datasheet: Publicly available at the TI website
+
+ https://www.ti.com/lit/ds/symlink/lm84.pdf
* National Semiconductor LM90
@@ -17,9 +19,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: Publicly available at the National Semiconductor website
+ Datasheet: Publicly available at the TI website
- http://www.national.com/pf/LM/LM90.html
+ https://www.ti.com/lit/ds/symlink/lm90.pdf
* National Semiconductor LM89
@@ -27,9 +29,9 @@ Supported chips:
Addresses scanned: I2C 0x4c and 0x4d
- Datasheet: Publicly available at the National Semiconductor website
+ Datasheet: Publicly available at the TI website
- http://www.national.com/mpf/LM/LM89.html
+ https://www.ti.com/lit/ds/symlink/lm89.pdf
* National Semiconductor LM99
@@ -37,9 +39,9 @@ Supported chips:
Addresses scanned: I2C 0x4c and 0x4d
- Datasheet: Publicly available at the National Semiconductor website
+ Datasheet: Publicly available at the TI website
- http://www.national.com/pf/LM/LM99.html
+ https://www.ti.com/lit/ds/symlink/lm99.pdf
* National Semiconductor LM86
@@ -47,9 +49,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: Publicly available at the National Semiconductor website
+ Datasheet: Publicly available at the TI website
- http://www.national.com/mpf/LM/LM86.html
+ https://www.ti.com/lit/ds/symlink/lm86.pdf
* Analog Devices ADM1020
@@ -57,7 +59,9 @@ Supported chips:
Addresses scanned: I2C 0x4c - 0x4e
- Datasheet: Publicly available at the Analog Devices website
+ Datasheet: Publicly available at the DigiKey website
+
+ https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1020.pdf
* Analog Devices ADM1021
@@ -65,7 +69,9 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheet: Publicly available at the Analog Devices website
+ Datasheet: Publicly available at the DigiKey website
+
+ https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1021.pdf
* Analog Devices ADM1021A/ADM1023
@@ -75,15 +81,18 @@ Supported chips:
Datasheet: Publicly available at the Analog Devices website
+ https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1021A.pdf
+ https://media.digikey.com/pdf/Data%20Sheets/Analog%20Devices%20PDFs/ADM1023.pdf
+
* Analog Devices ADM1032
Prefix: 'adm1032'
Addresses scanned: I2C 0x4c and 0x4d
- Datasheet: Publicly available at the ON Semiconductor website
+ Datasheet: Publicly available at the DigiKey website
- https://www.onsemi.com/PowerSolutions/product.do?id=ADM1032
+ https://www.digikey.com/htmldatasheets/production/53140/0/0/1/ADM1032.pdf
* Analog Devices ADT7461
@@ -111,9 +120,9 @@ Supported chips:
Addresses scanned: I2C 0x4b and 0x4c
- Datasheet: Publicly available at the ON Semiconductor website
+ Datasheet: Publicly available at the DigiKey website
- https://www.onsemi.com/PowerSolutions/product.do?id=ADT7481
+ https://www.digikey.com/htmldatasheets/production/234607/0/0/1/ADT7481.pdf
* Analog Devices ADT7482
@@ -191,7 +200,9 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1617.pdf
* Maxim MAX1617A
@@ -199,7 +210,9 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1617A.pdf
* Maxim MAX6642
@@ -207,9 +220,9 @@ Supported chips:
Addresses scanned: I2C 0x48-0x4f
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6642.pdf
* Maxim MAX6646
@@ -217,9 +230,9 @@ Supported chips:
Addresses scanned: I2C 0x4d
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6646-MAX6649.pdf
* Maxim MAX6647
@@ -227,9 +240,9 @@ Supported chips:
Addresses scanned: I2C 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6646-MAX6649.pdf
* Maxim MAX6648
@@ -237,9 +250,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3500
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6648-MAX6692.pdf
* Maxim MAX6649
@@ -247,9 +260,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3497
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1617.pdf
* Maxim MAX6654
@@ -259,9 +272,9 @@ Supported chips:
0x4c, 0x4d and 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- https://www.maximintegrated.com/en/products/sensors/MAX6654.html
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6654.pdf
* Maxim MAX6657
@@ -269,9 +282,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6657-MAX6659.pdf
* Maxim MAX6658
@@ -279,9 +292,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6657-MAX6659.pdf
* Maxim MAX6659
@@ -289,9 +302,9 @@ Supported chips:
Addresses scanned: I2C 0x4c, 0x4d, 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6657-MAX6659.pdf
* Maxim MAX6680
@@ -301,9 +314,9 @@ Supported chips:
0x4c, 0x4d and 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6680-MAX6681.pdf
* Maxim MAX6681
@@ -313,9 +326,9 @@ Supported chips:
0x4c, 0x4d and 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3370
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6680-MAX6681.pdf
* Maxim MAX6692
@@ -323,9 +336,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3500
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6648-MAX6692.pdf
* Maxim MAX6695
@@ -333,9 +346,9 @@ Supported chips:
Addresses scanned: I2C 0x18
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/datasheet/index.mvp/id/4199
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6695-MAX6696.pdf
* Maxim MAX6696
@@ -345,9 +358,9 @@ Supported chips:
0x4c, 0x4d and 0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet: Publicly available at the Analog Devices website
- http://www.maxim-ic.com/datasheet/index.mvp/id/4199
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6695-MAX6696.pdf
* Winbond/Nuvoton W83L771W/G
@@ -355,7 +368,9 @@ Supported chips:
Addresses scanned: I2C 0x4c
- Datasheet: No longer available
+ Datasheet: Publicly available at the DigiKey website
+
+ https://mm.digikey.com/Volume0/opasdata/d220001/medias/docus/1128/W83L771W%2CW83L771G.pdf
* Winbond/Nuvoton W83L771AWG/ASG
@@ -381,7 +396,7 @@ Supported chips:
Datasheet: Publicly available at Nuvoton website
- https://www.nuvoton.com/resource-files/Nuvoton_NCT7717U_Datasheet_V111.pdf
+ https://www.nuvoton.com/resource-files/Nuvoton_NCT7717U_Datasheet_V111.pdf
* Nuvoton NCT7718
@@ -391,7 +406,7 @@ Supported chips:
Datasheet: Publicly available at Nuvoton website
- https://www.nuvoton.com/resource-files/Nuvoton_NCT7718W_Datasheet_V11.pdf
+ https://www.nuvoton.com/resource-files/Nuvoton_NCT7718W_Datasheet_V11.pdf
* Philips/NXP SA56004X
@@ -401,7 +416,7 @@ Supported chips:
Datasheet: Publicly available at NXP website
- http://ics.nxp.com/products/interface/datasheet/sa56004x.pdf
+ https://www.nxp.com/docs/en/data-sheet/SA56004X.pdf
* GMT G781
@@ -437,7 +452,9 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheets: Publicly available at the Philips website
+ Datasheets: Publicly available at the DigiKey website
+
+ https://www.digikey.com/htmldatasheets/production/97606/0/0/1/ne1617.pdf
* Philips NE1618
@@ -445,7 +462,9 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheets: Publicly available at the Philips website
+ Datasheets: Publicly available at the DigiKey website
+
+ https://media.digikey.com/pdf/Data%20Sheets/NXP%20PDFs/NE1618.pdf
* Genesys Logic GL523SM
@@ -453,7 +472,7 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheet:
+ Datasheet: No longer available at Genesys Logic website
* TI THMC10
@@ -461,7 +480,7 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheet: Publicly available at the TI website
+ Datasheet: No longer available at the TI website
* Onsemi MC1066
@@ -469,7 +488,7 @@ Supported chips:
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
- Datasheet: Publicly available at the Onsemi website
+ Datasheet: No longer available at the Onsemi website
Author: Jean Delvare <jdelvare@suse.de>
diff --git a/Documentation/hwmon/macsmc-hwmon.rst b/Documentation/hwmon/macsmc-hwmon.rst
new file mode 100644
index 000000000000..6903f76df62b
--- /dev/null
+++ b/Documentation/hwmon/macsmc-hwmon.rst
@@ -0,0 +1,71 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver macsmc-hwmon
+==========================
+
+Supported hardware
+
+ * Apple Silicon Macs (M1 and up)
+
+Author: James Calligeros <jcalligeros99@gmail.com>
+
+Description
+-----------
+
+macsmc-hwmon exposes the Apple System Management controller's
+temperature, voltage, current and power sensors, as well as
+fan speed and control capabilities, via hwmon.
+
+Because each Apple Silicon Mac exposes a different set of sensors
+(e.g. the MacBooks expose battery telemetry that is not present on
+the desktop Macs), sensors present on any given machine are described
+via Devicetree. The driver picks these up and registers them with
+hwmon when probed.
+
+Manual fan speed is supported via the fan_control module parameter. This
+is disabled by default and marked as unsafe, as it cannot be proven that
+the system will fail safe if overheating due to manual fan control being
+used.
+
+sysfs interface
+---------------
+
+currX_input
+ Ammeter value
+
+currX_label
+ Ammeter label
+
+fanX_input
+ Current fan speed
+
+fanX_label
+ Fan label
+
+fanX_min
+ Minimum possible fan speed
+
+fanX_max
+ Maximum possible fan speed
+
+fanX_target
+ Current fan setpoint
+
+inX_input
+ Voltmeter value
+
+inX_label
+ Voltmeter label
+
+powerX_input
+ Power meter value
+
+powerX_label
+ Power meter label
+
+tempX_input
+ Temperature sensor value
+
+tempX_label
+ Temperature sensor label
+
diff --git a/Documentation/hwmon/max127.rst b/Documentation/hwmon/max127.rst
index dc192dd9c37c..09204b45f27b 100644
--- a/Documentation/hwmon/max127.rst
+++ b/Documentation/hwmon/max127.rst
@@ -13,7 +13,7 @@ Supported chips:
Prefix: 'max127'
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX127-MAX128.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max127-max128.pdf
Description
-----------
diff --git a/Documentation/hwmon/max15301.rst b/Documentation/hwmon/max15301.rst
index e2222e98304f..a0a993195cd1 100644
--- a/Documentation/hwmon/max15301.rst
+++ b/Documentation/hwmon/max15301.rst
@@ -11,7 +11,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX15301.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max15301.pdf
* Maxim MAX15303
diff --git a/Documentation/hwmon/max16064.rst b/Documentation/hwmon/max16064.rst
index c06249292557..2a8a76d7b230 100644
--- a/Documentation/hwmon/max16064.rst
+++ b/Documentation/hwmon/max16064.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX16064.pdf
+ Datasheet: https://www.digikey.com/en/htmldatasheets/production/701833/0/0/1/max16064
Author: Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/max16065.rst b/Documentation/hwmon/max16065.rst
index 45f69f334f25..ac3dc6f023dd 100644
--- a/Documentation/hwmon/max16065.rst
+++ b/Documentation/hwmon/max16065.rst
@@ -12,7 +12,7 @@ Supported chips:
Datasheet:
- http://datasheets.maxim-ic.com/en/ds/MAX16065-MAX16066.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/max16065-max16066.pdf
* Maxim MAX16067
@@ -22,7 +22,7 @@ Supported chips:
Datasheet:
- http://datasheets.maxim-ic.com/en/ds/MAX16067.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/max16067.pdf
* Maxim MAX16068
@@ -32,7 +32,7 @@ Supported chips:
Datasheet:
- http://datasheets.maxim-ic.com/en/ds/MAX16068.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/max16068.pdf
* Maxim MAX16070/MAX16071
@@ -42,7 +42,7 @@ Supported chips:
Datasheet:
- http://datasheets.maxim-ic.com/en/ds/MAX16070-MAX16071.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/max16070-max16071.pdf
Author: Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/max1619.rst b/Documentation/hwmon/max1619.rst
index b5fc175ae18d..f134d0fa9bfd 100644
--- a/Documentation/hwmon/max1619.rst
+++ b/Documentation/hwmon/max1619.rst
@@ -9,9 +9,9 @@ Supported chips:
Addresses scanned: I2C 0x18-0x1a, 0x29-0x2b, 0x4c-0x4e
- Datasheet: Publicly available at the Maxim website
+ Datasheet:
- http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1619.pdf
Authors:
- Oleksij Rempel <bug-track@fisher-privat.net>,
diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst
index c8c63a053e40..3b1392bf547e 100644
--- a/Documentation/hwmon/max16601.rst
+++ b/Documentation/hwmon/max16601.rst
@@ -35,7 +35,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX16602.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max16602.pdf
Author: Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/max1668.rst b/Documentation/hwmon/max1668.rst
index 417f17d750e6..e2b8a4056abe 100644
--- a/Documentation/hwmon/max1668.rst
+++ b/Documentation/hwmon/max1668.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX1668-MAX1989.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX1668-MAX1989.pdf
Author:
diff --git a/Documentation/hwmon/max17616.rst b/Documentation/hwmon/max17616.rst
new file mode 100644
index 000000000000..a3dc429048ae
--- /dev/null
+++ b/Documentation/hwmon/max17616.rst
@@ -0,0 +1,62 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver max17616
+======================
+
+Supported chips:
+
+ * Analog Devices MAX17616/MAX17616A
+
+ Prefix: 'max17616'
+
+ Addresses scanned: -
+
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max17616-max17616a.pdf
+
+Author:
+
+ - Kim Seer Paller <kimseer.paller@analog.com>
+
+
+Description
+-----------
+
+This driver supports hardware monitoring for Analog Devices MAX17616/MAX17616A
+Current-Limiter with OV/Surge, UV, Reverse Polarity, Loss of Ground Protection
+with PMBus Interface.
+
+The MAX17616/MAX17616A is a 3V to 80V, 7A current-limiter with overvoltage,
+surge, undervoltage, reverse polarity, and loss of ground protection. Through
+the PMBus interface, the device can monitor input/output voltages, output current
+and temperature.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate
+the devices explicitly. Please see Documentation/i2c/instantiating-devices.rst
+for details.
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+Sysfs entries
+-------------
+
+================= ========================================
+in1_label "vin"
+in1_input Measured input voltage
+in1_alarm Input voltage alarm
+in2_label "vout1"
+in2_input Measured output voltage
+curr1_label "iout1"
+curr1_input Measured output current.
+curr1_alarm Output current alarm
+temp1_input Measured temperature
+temp1_alarm Chip temperature alarm
+================= ========================================
diff --git a/Documentation/hwmon/max197.rst b/Documentation/hwmon/max197.rst
index 02fe19bc3428..00e16056823f 100644
--- a/Documentation/hwmon/max197.rst
+++ b/Documentation/hwmon/max197.rst
@@ -11,13 +11,13 @@ Supported chips:
Prefix: 'max197'
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX197.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX197.pdf
* Maxim MAX199
Prefix: 'max199'
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX199.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX199.pdf
Description
-----------
diff --git a/Documentation/hwmon/max20730.rst b/Documentation/hwmon/max20730.rst
index cb0c95b2b1f6..0ce473bca889 100644
--- a/Documentation/hwmon/max20730.rst
+++ b/Documentation/hwmon/max20730.rst
@@ -11,7 +11,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20710.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20710.pdf
* Maxim MAX20730
@@ -19,7 +19,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20730.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20730.pdf
* Maxim MAX20734
@@ -27,7 +27,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20734.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20734.pdf
* Maxim MAX20743
@@ -35,7 +35,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20743.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20743.pdf
Author: Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/max31722.rst b/Documentation/hwmon/max31722.rst
index 0ab15c00b226..b9d176ee7a69 100644
--- a/Documentation/hwmon/max31722.rst
+++ b/Documentation/hwmon/max31722.rst
@@ -11,7 +11,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31722-max31723.pdf
* Maxim Integrated MAX31723
@@ -21,7 +21,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31722-max31723.pdf
Author: Tiberiu Breana <tiberiu.a.breana@intel.com>
diff --git a/Documentation/hwmon/max31730.rst b/Documentation/hwmon/max31730.rst
index def0de19dbd2..1c5a32b64187 100644
--- a/Documentation/hwmon/max31730.rst
+++ b/Documentation/hwmon/max31730.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: 0x1c, 0x1d, 0x1e, 0x1f, 0x4c, 0x4d, 0x4e, 0x4f
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31730.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31730.pdf
Author: Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/max31785.rst b/Documentation/hwmon/max31785.rst
index c8c6756d0ee1..92817436759e 100644
--- a/Documentation/hwmon/max31785.rst
+++ b/Documentation/hwmon/max31785.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31785.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max31785.pdf
Author: Andrew Jeffery <andrew@aj.id.au>
diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst
index 33c5c7330efc..b8af2d907b6e 100644
--- a/Documentation/hwmon/max31790.rst
+++ b/Documentation/hwmon/max31790.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://pdfserv.maximintegrated.com/en/ds/MAX31790.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31790.pdf
Author: Il Han <corone.il.han@gmail.com>
diff --git a/Documentation/hwmon/max31827.rst b/Documentation/hwmon/max31827.rst
index 6cc5088b26b7..ddd039529077 100644
--- a/Documentation/hwmon/max31827.rst
+++ b/Documentation/hwmon/max31827.rst
@@ -11,7 +11,7 @@ Supported chips:
Addresses scanned: I2C 0x40 - 0x5f
- Datasheet: Publicly available at the Analog Devices website
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31827-MAX31829.pdf
* Maxim MAX31828
@@ -19,7 +19,7 @@ Supported chips:
Addresses scanned: I2C 0x40 - 0x5f
- Datasheet: Publicly available at the Analog Devices website
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31827-MAX31829.pdf
* Maxim MAX31829
@@ -27,7 +27,7 @@ Supported chips:
Addresses scanned: I2C 0x40 - 0x5f
- Datasheet: Publicly available at the Analog Devices website
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31827-MAX31829.pdf
Authors:
diff --git a/Documentation/hwmon/max34440.rst b/Documentation/hwmon/max34440.rst
index 8591a7152ce5..d6d4fbc863d9 100644
--- a/Documentation/hwmon/max34440.rst
+++ b/Documentation/hwmon/max34440.rst
@@ -11,13 +11,21 @@ Supported chips:
Datasheet: -
+ * ADI ADPM12200
+
+ Prefixes: 'adpm12200'
+
+ Addresses scanned: -
+
+ Datasheet: -
+
* Maxim MAX34440
Prefixes: 'max34440'
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34440.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34440.pdf
* Maxim MAX34441
@@ -27,7 +35,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34441.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34441.pdf
* Maxim MAX34446
@@ -37,7 +45,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34446.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34446.pdf
* Maxim MAX34451
@@ -47,7 +55,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34451.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34451.pdf
* Maxim MAX34460
@@ -57,7 +65,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34460.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34460.pdf
* Maxim MAX34461
@@ -67,7 +75,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX34461.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max34461.pdf
Author: Guenter Roeck <linux@roeck-us.net>
@@ -79,10 +87,11 @@ This driver supports multiple devices: hardware monitoring for Maxim MAX34440
PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply
Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data
Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34460, and
-MAX34461; PMBus DC/DC Power Module ADPM12160. The MAX34451 supports monitoring
-voltage or current of 12 channels based on GIN pins. The MAX34460 supports 12
-voltage channels, and the MAX34461 supports 16 voltage channels. The ADPM1260
-also monitors both input and output of voltage and current.
+MAX34461; PMBus DC/DC Power Module ADPM12160, and ADPM12200. The MAX34451
+supports monitoring voltage or current of 12 channels based on GIN pins. The
+MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 voltage
+channels. The ADPM12160, and ADPM12200 also monitors both input and output
+of voltage and current.
The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
@@ -140,7 +149,8 @@ in[1-6]_reset_history Write any value to reset history.
.. note::
- MAX34446 only supports in[1-4].
- - ADPM12160 only supports in[1-2]. Label is "vin1" and "vout1" respectively.
+ - ADPM12160, and ADPM12200 only supports in[1-2]. Label is "vin1"
+ and "vout1" respectively.
Curr
~~~~
@@ -162,7 +172,8 @@ curr[1-6]_reset_history Write any value to reset history.
- in6 and curr6 attributes only exist for MAX34440.
- MAX34446 only supports curr[1-4].
- - For ADPM12160, curr[1] is "iin1" and curr[2-6] are "iout[1-5].
+ - For ADPM12160, and ADPM12200, curr[1] is "iin1" and curr[2-6]
+ are "iout[1-5]".
Power
~~~~~
@@ -198,7 +209,7 @@ temp[1-8]_reset_history Write any value to reset history.
.. note::
- temp7 and temp8 attributes only exist for MAX34440.
- MAX34446 only supports temp[1-3].
- - ADPM12160 only supports temp[1].
+ - ADPM12160, and ADPM12200 only supports temp[1].
.. note::
diff --git a/Documentation/hwmon/max6639.rst b/Documentation/hwmon/max6639.rst
index c85d285a3489..492c13a5880d 100644
--- a/Documentation/hwmon/max6639.rst
+++ b/Documentation/hwmon/max6639.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: I2C 0x2c, 0x2e, 0x2f
- Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX6639-MAX6639F.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6639-max6639f.pdf
Authors:
- He Changqing <hechangqing@semptian.com>
diff --git a/Documentation/hwmon/max6650.rst b/Documentation/hwmon/max6650.rst
index 7952b6ecaa2d..427f353c5e9c 100644
--- a/Documentation/hwmon/max6650.rst
+++ b/Documentation/hwmon/max6650.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: none
- Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6650-MAX6651.pdf
* Maxim MAX6651
@@ -17,7 +17,7 @@ Supported chips:
Addresses scanned: none
- Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6650-MAX6651.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6650-MAX6651.pdf
Authors:
- Hans J. Koch <hjk@hansjkoch.de>
diff --git a/Documentation/hwmon/max6697.rst b/Documentation/hwmon/max6697.rst
index 90ca224c446a..5b37ff08ff44 100644
--- a/Documentation/hwmon/max6697.rst
+++ b/Documentation/hwmon/max6697.rst
@@ -7,61 +7,61 @@ Supported chips:
Prefix: 'max6581'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6581.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6581.pdf
* Maxim MAX6602
Prefix: 'max6602'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6602.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6602.pdf
* Maxim MAX6622
Prefix: 'max6622'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6622.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6622.pdf
* Maxim MAX6636
Prefix: 'max6636'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6636.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6636.pdf
* Maxim MAX6689
Prefix: 'max6689'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6689.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6689.pdf
* Maxim MAX6693
Prefix: 'max6693'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6693.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6693.pdf
* Maxim MAX6694
Prefix: 'max6694'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6694.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6694.pdf
* Maxim MAX6697
Prefix: 'max6697'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6697.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6697.pdf
* Maxim MAX6698
Prefix: 'max6698'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6698.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6698.pdf
* Maxim MAX6699
Prefix: 'max6699'
- Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX6699.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6699.pdf
Author:
diff --git a/Documentation/hwmon/max77705.rst b/Documentation/hwmon/max77705.rst
index 4a7680a340e1..5202de614647 100644
--- a/Documentation/hwmon/max77705.rst
+++ b/Documentation/hwmon/max77705.rst
@@ -11,7 +11,9 @@ Supported chips:
Addresses scanned: none
- Datasheet: Not available
+ Datasheet:
+
+ https://www.analog.com/media/en/technical-documentation/data-sheets/max77505.pdf
Authors:
- Dzmitry Sankouski <dsankouski@gmail.com>
diff --git a/Documentation/hwmon/max8688.rst b/Documentation/hwmon/max8688.rst
index 71e7f2cbe2e2..71b6b9ee90ab 100644
--- a/Documentation/hwmon/max8688.rst
+++ b/Documentation/hwmon/max8688.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX8688.pdf
+ Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max8688.pdf
Author: Guenter Roeck <linux@roeck-us.net>
diff --git a/Documentation/hwmon/mp2925.rst b/Documentation/hwmon/mp2925.rst
new file mode 100644
index 000000000000..63eda215b6cb
--- /dev/null
+++ b/Documentation/hwmon/mp2925.rst
@@ -0,0 +1,151 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver mp2925
+====================
+
+Supported chips:
+
+ * MPS mp2925
+
+ Prefix: 'mp2925'
+
+ * MPS mp2929
+
+ Prefix: 'mp2929'
+
+Author:
+
+ Wensheng Wang <wenswang@yeah.net>
+
+Description
+-----------
+
+This driver implements support for Monolithic Power Systems, Inc. (MPS)
+MP2925 Dual Loop Digital Multi-phase Controller.
+
+Device compliant with:
+
+- PMBus rev 1.3 interface.
+
+The driver exports the following attributes via the 'sysfs' files
+for input voltage:
+
+**in1_input**
+
+**in1_label**
+
+**in1_crit**
+
+**in1_crit_alarm**
+
+**in1_lcrit**
+
+**in1_lcrit_alarm**
+
+**in1_max**
+
+**in1_max_alarm**
+
+**in1_min**
+
+**in1_min_alarm**
+
+The driver provides the following attributes for output voltage:
+
+**in2_input**
+
+**in2_label**
+
+**in2_crit**
+
+**in2_crit_alarm**
+
+**in2_lcrit**
+
+**in2_lcrit_alarm**
+
+**in3_input**
+
+**in3_label**
+
+**in3_crit**
+
+**in3_crit_alarm**
+
+**in3_lcrit**
+
+**in3_lcrit_alarm**
+
+The driver provides the following attributes for input current:
+
+**curr1_input**
+
+**curr1_label**
+
+The driver provides the following attributes for output current:
+
+**curr2_input**
+
+**curr2_label**
+
+**curr2_crit**
+
+**curr2_crit_alarm**
+
+**curr2_max**
+
+**curr2_max_alarm**
+
+**curr3_input**
+
+**curr3_label**
+
+**curr3_crit**
+
+**curr3_crit_alarm**
+
+**curr3_max**
+
+**curr3_max_alarm**
+
+The driver provides the following attributes for input power:
+
+**power1_input**
+
+**power1_label**
+
+**power2_input**
+
+**power2_label**
+
+The driver provides the following attributes for output power:
+
+**power3_input**
+
+**power3_label**
+
+**power4_input**
+
+**power4_label**
+
+The driver provides the following attributes for temperature:
+
+**temp1_input**
+
+**temp1_crit**
+
+**temp1_crit_alarm**
+
+**temp1_max**
+
+**temp1_max_alarm**
+
+**temp2_input**
+
+**temp2_crit**
+
+**temp2_crit_alarm**
+
+**temp2_max**
+
+**temp2_max_alarm**
diff --git a/Documentation/hwmon/mp9945.rst b/Documentation/hwmon/mp9945.rst
new file mode 100644
index 000000000000..f406f96efcf9
--- /dev/null
+++ b/Documentation/hwmon/mp9945.rst
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver mp9945
+=====================
+
+Supported chips:
+
+ * MPS mp9945
+
+ Prefix: 'mp9945'
+
+Author:
+
+ Cosmo Chou <chou.cosmo@gmail.com>
+
+Description
+-----------
+
+This driver implements support for Monolithic Power Systems, Inc. (MPS)
+MP9945 Digital Single-phase Controller.
+
+Device compliant with:
+
+- PMBus rev 1.3 interface.
+
+The driver exports the following attributes via the 'sysfs' files
+for input voltage:
+
+**in1_input**
+
+**in1_label**
+
+**in1_crit**
+
+**in1_crit_alarm**
+
+**in1_lcrit**
+
+**in1_lcrit_alarm**
+
+**in1_max**
+
+**in1_max_alarm**
+
+**in1_min**
+
+**in1_min_alarm**
+
+The driver provides the following attributes for output voltage:
+
+**in2_input**
+
+**in2_label**
+
+**in2_crit**
+
+**in2_crit_alarm**
+
+**in2_lcrit**
+
+**in2_lcrit_alarm**
+
+**in2_min**
+
+**in2_min_alarm**
+
+The driver provides the following attributes for input current:
+
+**curr1_input**
+
+**curr1_label**
+
+**curr1_max**
+
+**curr1_max_alarm**
+
+The driver provides the following attributes for output current:
+
+**curr2_input**
+
+**curr2_label**
+
+**curr2_crit**
+
+**curr2_crit_alarm**
+
+**curr2_max**
+
+**curr2_max_alarm**
+
+The driver provides the following attributes for input power:
+
+**power1_input**
+
+**power1_label**
+
+The driver provides the following attributes for output power:
+
+**power2_input**
+
+**power2_label**
+
+**power2_max**
+
+**power2_max_alarm**
+
+The driver provides the following attributes for temperature:
+
+**temp1_input**
+
+**temp1_crit**
+
+**temp1_crit_alarm**
+
+**temp1_max**
+
+**temp1_max_alarm**
diff --git a/Documentation/hwmon/pmbus.rst b/Documentation/hwmon/pmbus.rst
index d477124cf67f..a8e01a5b96da 100644
--- a/Documentation/hwmon/pmbus.rst
+++ b/Documentation/hwmon/pmbus.rst
@@ -74,7 +74,7 @@ Supported chips:
Datasheet:
- Not published
+ https://www.analog.com/media/en/technical-documentation/data-sheets/MAX20796.pdf
* Generic PMBus devices
diff --git a/Documentation/hwmon/sy7636a-hwmon.rst b/Documentation/hwmon/sy7636a-hwmon.rst
index c85db7b32941..0143ce0e5db7 100644
--- a/Documentation/hwmon/sy7636a-hwmon.rst
+++ b/Documentation/hwmon/sy7636a-hwmon.rst
@@ -17,10 +17,10 @@ the Silergy SY7636A PMIC.
The following sensors are supported
* Temperature
- - SoC on-die temperature in milli-degree C
+ - Temperature of external NTC in milli-degree C
sysfs-Interface
---------------
temp0_input
- - SoC on-die temperature (milli-degree C)
+ - Temperature of external NTC (milli-degree C)
diff --git a/Documentation/hwmon/tsc1641.rst b/Documentation/hwmon/tsc1641.rst
new file mode 100644
index 000000000000..425e25f7a7d1
--- /dev/null
+++ b/Documentation/hwmon/tsc1641.rst
@@ -0,0 +1,87 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver tsc1641
+=====================
+
+Supported chips:
+
+ * ST TSC1641
+
+ Prefix: 'tsc1641'
+
+ Addresses scanned: -
+
+ Datasheet:
+ https://www.st.com/resource/en/datasheet/tsc1641.pdf
+
+Author:
+ - Igor Reznichenko <igor@reznichenko.net>
+
+
+Description
+-----------
+
+The TSC1641 is a high-precision current, voltage, power, and temperature
+monitoring analog front-end (AFE). It monitors bidirectional current into a
+shunt resistor and load voltage up to 60 V in a synchronized way. Digital bus
+interface is I2C/SMbus. The TSC1641 allows the assertion of several alerts
+regarding the voltage, current, power and temperature.
+
+Usage Notes
+-----------
+
+The TSC1641 driver requires the value of the external shunt resistor to
+correctly compute current and power measurements. The resistor value, in
+micro-ohms, should be provided either through the device tree property
+"shunt-resistor-micro-ohms" or via writable sysfs attribute "shunt_resistor".
+Please refer to the Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
+for bindings if the device tree is used.
+
+Supported range of shunt resistor values is from 100 uOhm to 655.35 mOhm, in
+10 uOhm steps.
+When selecting the value keep in mind device maximum DC power measurement is
+1600W. See datasheet p.22 for ST recommendations on selecting shunt value.
+
+If the shunt resistor value is not specified in the device tree, the driver
+initializes it to 1000 uOhm by default. Users may configure the correct shunt
+resistor value at runtime by writing to the "shunt_resistor" sysfs attribute.
+
+The driver only supports continuous operating mode.
+Measurement ranges:
+
+================ ===============================================================
+Current Bidirectional, dependent on shunt
+Bus voltage 0-60V
+Maximum DC power 1600W
+Temperature -40C to +125C
+================ ===============================================================
+
+Sysfs entries
+-------------
+
+==================== ===========================================================
+in0_input bus voltage (mV)
+in0_max bus voltage max alarm limit (mV)
+in0_max_alarm bus voltage max alarm limit exceeded
+in0_min bus voltage min alarm limit (mV)
+in0_min_alarm bus voltage min alarm limit exceeded
+
+curr1_input current measurement (mA)
+curr1_max current max alarm limit (mA)
+curr1_max_alarm current max alarm limit exceeded
+curr1_min current min alarm limit (mA)
+curr1_min_alarm current min alarm limit exceeded
+
+power1_input power measurement (uW)
+power1_max power max alarm limit (uW)
+power1_max_alarm power max alarm limit exceeded
+
+shunt_resistor shunt resistor value (uOhms)
+
+temp1_input temperature measurement (mdegC)
+temp1_max temperature max alarm limit (mdegC)
+temp1_max_alarm temperature max alarm limit exceeded
+
+update_interval data conversion time (1 - 33ms), longer conversion time
+ corresponds to higher effective resolution in bits
+==================== =========================================================== \ No newline at end of file
diff --git a/Documentation/hwmon/zl6100.rst b/Documentation/hwmon/zl6100.rst
index d42ed9d3ac69..1513c9d2d461 100644
--- a/Documentation/hwmon/zl6100.rst
+++ b/Documentation/hwmon/zl6100.rst
@@ -9,7 +9,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl2004-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl2004-datasheet
* Renesas / Intersil / Zilker Labs ZL2005
@@ -17,7 +17,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl2005-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl2005-datasheet
* Renesas / Intersil / Zilker Labs ZL2006
@@ -25,7 +25,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl2006-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl2006-datasheet
* Renesas / Intersil / Zilker Labs ZL2008
@@ -33,7 +33,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl2008-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl2008-datasheet
* Renesas / Intersil / Zilker Labs ZL2105
@@ -41,7 +41,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl2105-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl2105-datasheet
* Renesas / Intersil / Zilker Labs ZL2106
@@ -49,7 +49,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl2106-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl2106-datasheet
* Renesas / Intersil / Zilker Labs ZL6100
@@ -57,7 +57,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl6100-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl6100-datasheet
* Renesas / Intersil / Zilker Labs ZL6105
@@ -65,7 +65,7 @@ Supported chips:
Addresses scanned: -
- Datasheet: https://www.renesas.com/us/en/document/dst/zl6105-datasheet.pdf
+ Datasheet: https://www.renesas.com/us/en/document/dst/zl6105-datasheet
* Renesas / Intersil / Zilker Labs ZL8802
diff --git a/Documentation/input/event-codes.rst b/Documentation/input/event-codes.rst
index 1ead9bb8d9c6..4424cbff251f 100644
--- a/Documentation/input/event-codes.rst
+++ b/Documentation/input/event-codes.rst
@@ -400,19 +400,30 @@ can report through the rotational axes (absolute and/or relative rx, ry, rz).
All other axes retain their meaning. A device must not mix
regular directional axes and accelerometer axes on the same event node.
-INPUT_PROP_HAPTIC_TOUCHPAD
---------------------------
+INPUT_PROP_PRESSUREPAD
+----------------------
+
+The INPUT_PROP_PRESSUREPAD property indicates that the device provides
+simulated haptic feedback (e.g. a vibrator motor situated below the surface)
+instead of physical haptic feedback (e.g. a hinge). This property is only set
+if the device:
-The INPUT_PROP_HAPTIC_TOUCHPAD property indicates that device:
-- supports simple haptic auto and manual triggering
- can differentiate between at least 5 fingers
- uses correct resolution for the X/Y (units and value)
-- reports correct force per touch, and correct units for them (newtons or grams)
- follows the MT protocol type B
+If the simulated haptic feedback is controllable by userspace the device must:
+
+- support simple haptic auto and manual triggering, and
+- report correct force per touch, and correct units for them (newtons or grams), and
+- provide the EV_FF FF_HAPTIC force feedback effect.
+
Summing up, such devices follow the MS spec for input devices in
-Win8 and Win8.1, and in addition support the Simple haptic controller HID table,
-and report correct units for the pressure.
+Win8 and Win8.1, and in addition may support the Simple haptic controller HID
+table, and report correct units for the pressure.
+
+Where applicable, this property is set in addition to INPUT_PROP_BUTTONPAD, it
+does not replace that property.
Guidelines
==========
diff --git a/Documentation/kbuild/kbuild.rst b/Documentation/kbuild/kbuild.rst
index 3388a10f2dcc..82826b0332df 100644
--- a/Documentation/kbuild/kbuild.rst
+++ b/Documentation/kbuild/kbuild.rst
@@ -328,8 +328,14 @@ KBUILD_BUILD_TIMESTAMP
----------------------
Setting this to a date string overrides the timestamp used in the
UTS_VERSION definition (uname -v in the running kernel). The value has to
-be a string that can be passed to date -d. The default value
-is the output of the date command at one point during build.
+be a string that can be passed to date -d. E.g.::
+
+ $ KBUILD_BUILD_TIMESTAMP="Mon Oct 13 00:00:00 UTC 2025" make
+
+The default value is the output of the date command at one point during
+build. If provided, this timestamp will also be used for mtime fields
+within any initramfs archive. Initramfs mtimes are 32-bit, so dates before
+the 1970 Unix epoch, or after 2106-02-07 06:28:15 UTC will fail.
KBUILD_BUILD_USER, KBUILD_BUILD_HOST
------------------------------------
diff --git a/Documentation/leds/leds-lp5521.rst b/Documentation/leds/leds-lp5521.rst
index 0432615b083d..4c838c88820e 100644
--- a/Documentation/leds/leds-lp5521.rst
+++ b/Documentation/leds/leds-lp5521.rst
@@ -22,7 +22,7 @@ More details of the instructions can be found from the public data sheet.
LP5521 has the internal program memory for running various LED patterns.
There are two ways to run LED patterns.
-1) Legacy interface - enginex_mode and enginex_load
+1) sysfs interface - enginex_mode and enginex_load
Control interface for the engines:
x is 1 .. 3
diff --git a/Documentation/leds/leds-lp5523.rst b/Documentation/leds/leds-lp5523.rst
index 7d7362a1dd57..f5a87b07514a 100644
--- a/Documentation/leds/leds-lp5523.rst
+++ b/Documentation/leds/leds-lp5523.rst
@@ -35,7 +35,7 @@ If both fields are NULL, 'lp5523' is used by default.
LP5523 has the internal program memory for running various LED patterns.
There are two ways to run LED patterns.
-1) Legacy interface - enginex_mode, enginex_load and enginex_leds
+1) sysfs interface - enginex_mode, enginex_load and enginex_leds
Control interface for the engines:
diff --git a/Documentation/locking/seqlock.rst b/Documentation/locking/seqlock.rst
index 3fb7ea3ab22a..9899871d3d9a 100644
--- a/Documentation/locking/seqlock.rst
+++ b/Documentation/locking/seqlock.rst
@@ -220,13 +220,14 @@ Read path, three categories:
according to a passed marker. This is used to avoid lockless readers
starvation (too much retry loops) in case of a sharp spike in write
activity. First, a lockless read is tried (even marker passed). If
- that trial fails (odd sequence counter is returned, which is used as
- the next iteration marker), the lockless read is transformed to a
- full locking read and no retry loop is necessary::
+ that trial fails (sequence counter doesn't match), make the marker
+ odd for the next iteration, the lockless read is transformed to a
+ full locking read and no retry loop is necessary, for example::
/* marker; even initialization */
- int seq = 0;
+ int seq = 1;
do {
+ seq++; /* 2 on the 1st/lockless path, otherwise odd */
read_seqbegin_or_lock(&foo_seqlock, &seq);
/* ... [[read-side critical section]] ... */
diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt
index 1d164e005776..61b7317bcf2e 100644
--- a/Documentation/memory-barriers.txt
+++ b/Documentation/memory-barriers.txt
@@ -2182,9 +2182,11 @@ set_current_state() may be wrapped by:
which therefore also imply a general memory barrier after setting the state.
The whole sequence above is available in various canned forms, all of which
-interpolate the memory barrier in the right place:
+interpolate the memory barrier in the right place, for example:
wait_event();
+ wait_event_cmd();
+ wait_event_exclusive_cmd();
wait_event_interruptible();
wait_event_interruptible_exclusive();
wait_event_interruptible_timeout();
@@ -2192,8 +2194,6 @@ interpolate the memory barrier in the right place:
wait_event_timeout();
wait_on_bit();
wait_on_bit_lock();
- wait_event_cmd();
- wait_event_exclusive_cmd();
Secondly, code that performs a wake up normally follows something like this:
diff --git a/Documentation/misc-devices/amd-sbi.rst b/Documentation/misc-devices/amd-sbi.rst
index 5648fc6ec572..07ceb44fbe5e 100644
--- a/Documentation/misc-devices/amd-sbi.rst
+++ b/Documentation/misc-devices/amd-sbi.rst
@@ -28,8 +28,10 @@ MCAMSR and register xfer commands.
Register sets is common across APML protocols. IOCTL is providing synchronization
among protocols as transactions may create race condition.
-$ ls -al /dev/sbrmi-3c
-crw------- 1 root root 10, 53 Jul 10 11:13 /dev/sbrmi-3c
+.. code-block:: bash
+
+ $ ls -al /dev/sbrmi-3c
+ crw------- 1 root root 10, 53 Jul 10 11:13 /dev/sbrmi-3c
apml_sbrmi driver registers hwmon sensors for monitoring power_cap_max,
current power consumption and managing power_cap.
diff --git a/Documentation/misc-devices/mrvl_cn10k_dpi.rst b/Documentation/misc-devices/mrvl_cn10k_dpi.rst
index a75e372723d8..fa9b8cd6806f 100644
--- a/Documentation/misc-devices/mrvl_cn10k_dpi.rst
+++ b/Documentation/misc-devices/mrvl_cn10k_dpi.rst
@@ -33,12 +33,12 @@ drivers/misc/mrvl_cn10k_dpi.c
Driver IOCTLs
=============
-:c:macro::`DPI_MPS_MRRS_CFG`
+:c:macro:`DPI_MPS_MRRS_CFG`
ioctl that sets max payload size & max read request size parameters of
a pem port to which DMA engines are wired.
-:c:macro::`DPI_ENGINE_CFG`
+:c:macro:`DPI_ENGINE_CFG`
ioctl that sets DMA engine's fifo sizes & max outstanding load request
thresholds.
diff --git a/Documentation/misc-devices/tps6594-pfsm.rst b/Documentation/misc-devices/tps6594-pfsm.rst
index 4ada37ccdcba..5f17a4fd9579 100644
--- a/Documentation/misc-devices/tps6594-pfsm.rst
+++ b/Documentation/misc-devices/tps6594-pfsm.rst
@@ -39,28 +39,28 @@ include/uapi/linux/tps6594_pfsm.h
Driver IOCTLs
=============
-:c:macro::`PMIC_GOTO_STANDBY`
+:c:macro:`PMIC_GOTO_STANDBY`
All device resources are powered down. The processor is off, and
no voltage domains are energized.
-:c:macro::`PMIC_GOTO_LP_STANDBY`
+:c:macro:`PMIC_GOTO_LP_STANDBY`
The digital and analog functions of the PMIC, which are not
required to be always-on, are turned off (low-power).
-:c:macro::`PMIC_UPDATE_PGM`
+:c:macro:`PMIC_UPDATE_PGM`
Triggers a firmware update.
-:c:macro::`PMIC_SET_ACTIVE_STATE`
+:c:macro:`PMIC_SET_ACTIVE_STATE`
One of the operational modes.
The PMICs are fully functional and supply power to all PDN loads.
All voltage domains are energized in both MCU and Main processor
sections.
-:c:macro::`PMIC_SET_MCU_ONLY_STATE`
+:c:macro:`PMIC_SET_MCU_ONLY_STATE`
One of the operational modes.
Only the power resources assigned to the MCU Safety Island are on.
-:c:macro::`PMIC_SET_RETENTION_STATE`
+:c:macro:`PMIC_SET_RETENTION_STATE`
One of the operational modes.
Depending on the triggers set, some DDR/GPIO voltage domains can
remain energized, while all other domains are off to minimize
diff --git a/Documentation/misc-devices/uacce.rst b/Documentation/misc-devices/uacce.rst
index 1db412e9b1a3..5f78d413e379 100644
--- a/Documentation/misc-devices/uacce.rst
+++ b/Documentation/misc-devices/uacce.rst
@@ -1,7 +1,10 @@
.. SPDX-License-Identifier: GPL-2.0
-Introduction of Uacce
----------------------
+Uacce (Unified/User-space-access-intended Accelerator Framework)
+================================================================
+
+Introduction
+------------
Uacce (Unified/User-space-access-intended Accelerator Framework) targets to
provide Shared Virtual Addressing (SVA) between accelerators and processes.
diff --git a/Documentation/mm/active_mm.rst b/Documentation/mm/active_mm.rst
index d096fc091e23..60d819d7d043 100644
--- a/Documentation/mm/active_mm.rst
+++ b/Documentation/mm/active_mm.rst
@@ -92,4 +92,4 @@ helpers, which abstract this config option.
and register state is separate, the alpha PALcode joins the two, and you
need to switch both together).
- (From http://marc.info/?l=linux-kernel&m=93337278602211&w=2)
+ (From https://lore.kernel.org/lkml/Pine.LNX.4.10.9907301410280.752-100000@penguin.transmeta.com/)
diff --git a/Documentation/netlink/genetlink-c.yaml b/Documentation/netlink/genetlink-c.yaml
index 5a234e9b5fa2..57f59fe23e3f 100644
--- a/Documentation/netlink/genetlink-c.yaml
+++ b/Documentation/netlink/genetlink-c.yaml
@@ -227,7 +227,7 @@ properties:
Optional format indicator that is intended only for choosing
the right formatting mechanism when displaying values of this
type.
- enum: [ hex, mac, fddi, ipv4, ipv6, uuid ]
+ enum: [ hex, mac, fddi, ipv4, ipv6, ipv4-or-v6, uuid ]
# Start genetlink-c
name-prefix:
type: string
diff --git a/Documentation/netlink/genetlink.yaml b/Documentation/netlink/genetlink.yaml
index 7b1ec153e834..b020a537d8ac 100644
--- a/Documentation/netlink/genetlink.yaml
+++ b/Documentation/netlink/genetlink.yaml
@@ -185,7 +185,7 @@ properties:
Optional format indicator that is intended only for choosing
the right formatting mechanism when displaying values of this
type.
- enum: [ hex, mac, fddi, ipv4, ipv6, uuid ]
+ enum: [ hex, mac, fddi, ipv4, ipv6, ipv4-or-v6, uuid ]
# Make sure name-prefix does not appear in subsets (subsets inherit naming)
dependencies:
diff --git a/Documentation/netlink/netlink-raw.yaml b/Documentation/netlink/netlink-raw.yaml
index 246fa07bccf6..0166a7e4afbb 100644
--- a/Documentation/netlink/netlink-raw.yaml
+++ b/Documentation/netlink/netlink-raw.yaml
@@ -157,7 +157,7 @@ properties:
Optional format indicator that is intended only for choosing
the right formatting mechanism when displaying values of this
type.
- enum: [ hex, mac, fddi, ipv4, ipv6, uuid ]
+ enum: [ hex, mac, fddi, ipv4, ipv6, ipv4-or-v6, uuid ]
struct:
description: Name of the nested struct type.
type: string
diff --git a/Documentation/netlink/specs/conntrack.yaml b/Documentation/netlink/specs/conntrack.yaml
index bef528633b17..db7cddcda50a 100644
--- a/Documentation/netlink/specs/conntrack.yaml
+++ b/Documentation/netlink/specs/conntrack.yaml
@@ -457,7 +457,7 @@ attribute-sets:
name: labels
type: binary
-
- name: labels mask
+ name: labels-mask
type: binary
-
name: synproxy
diff --git a/Documentation/netlink/specs/devlink.yaml b/Documentation/netlink/specs/devlink.yaml
index 3db59c965869..837112da6738 100644
--- a/Documentation/netlink/specs/devlink.yaml
+++ b/Documentation/netlink/specs/devlink.yaml
@@ -99,6 +99,8 @@ definitions:
name: legacy
-
name: switchdev
+ -
+ name: switchdev-inactive
-
type: enum
name: eswitch-inline-mode
@@ -857,6 +859,14 @@ attribute-sets:
name: health-reporter-burst-period
type: u64
doc: Time (in msec) for recoveries before starting the grace period.
+
+ # TODO: fill in the attributes in between
+
+ -
+ name: param-reset-default
+ type: flag
+ doc: Request restoring parameter to its default value.
+ value: 183
-
name: dl-dev-stats
subset-of: devlink
@@ -1791,6 +1801,7 @@ operations:
- param-type
# param-value-data is missing here as the type is variable
- param-value-cmode
+ - param-reset-default
-
name: region-get
diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
index cafb4ec20447..78d0724d7e12 100644
--- a/Documentation/netlink/specs/dpll.yaml
+++ b/Documentation/netlink/specs/dpll.yaml
@@ -440,6 +440,12 @@ attribute-sets:
doc: |
Capable pin provides list of pins that can be bound to create a
reference-sync pin pair.
+ -
+ name: phase-adjust-gran
+ type: u32
+ doc: |
+ Granularity of phase adjustment, in picoseconds. The value of
+ phase adjustment must be a multiple of this granularity.
-
name: pin-parent-device
@@ -605,6 +611,8 @@ operations:
reply: &pin-attrs
attributes:
- id
+ - module-name
+ - clock-id
- board-label
- panel-label
- package-label
@@ -614,6 +622,7 @@ operations:
- capabilities
- parent-device
- parent-pin
+ - phase-adjust-gran
- phase-adjust-min
- phase-adjust-max
- phase-adjust
diff --git a/Documentation/netlink/specs/em.yaml b/Documentation/netlink/specs/em.yaml
new file mode 100644
index 000000000000..9905ca482325
--- /dev/null
+++ b/Documentation/netlink/specs/em.yaml
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+
+name: em
+
+doc: |
+ Energy model netlink interface to notify its changes.
+
+protocol: genetlink
+
+uapi-header: linux/energy_model.h
+
+attribute-sets:
+ -
+ name: pds
+ attributes:
+ -
+ name: pd
+ type: nest
+ nested-attributes: pd
+ multi-attr: true
+ -
+ name: pd
+ attributes:
+ -
+ name: pad
+ type: pad
+ -
+ name: pd-id
+ type: u32
+ -
+ name: flags
+ type: u64
+ -
+ name: cpus
+ type: string
+ -
+ name: pd-table
+ attributes:
+ -
+ name: pd-id
+ type: u32
+ -
+ name: ps
+ type: nest
+ nested-attributes: ps
+ multi-attr: true
+ -
+ name: ps
+ attributes:
+ -
+ name: pad
+ type: pad
+ -
+ name: performance
+ type: u64
+ -
+ name: frequency
+ type: u64
+ -
+ name: power
+ type: u64
+ -
+ name: cost
+ type: u64
+ -
+ name: flags
+ type: u64
+
+operations:
+ list:
+ -
+ name: get-pds
+ attribute-set: pds
+ doc: Get the list of information for all performance domains.
+ do:
+ reply:
+ attributes:
+ - pd
+ -
+ name: get-pd-table
+ attribute-set: pd-table
+ doc: Get the energy model table of a performance domain.
+ do:
+ request:
+ attributes:
+ - pd-id
+ reply:
+ attributes:
+ - pd-id
+ - ps
+ -
+ name: pd-created
+ doc: A performance domain is created.
+ notify: get-pd-table
+ mcgrp: event
+ -
+ name: pd-updated
+ doc: A performance domain is updated.
+ notify: get-pd-table
+ mcgrp: event
+ -
+ name: pd-deleted
+ doc: A performance domain is deleted.
+ attribute-set: pd-table
+ event:
+ attributes:
+ - pd-id
+ mcgrp: event
+
+mcast-groups:
+ list:
+ -
+ name: event
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 6a0fb1974513..0a2d2343f79a 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -1269,7 +1269,7 @@ attribute-sets:
-
name: hist
type: nest
- multi-attr: True
+ multi-attr: true
nested-attributes: fec-hist
-
name: fec
@@ -1823,6 +1823,73 @@ attribute-sets:
type: uint
enum: pse-event
doc: List of events reported by the PSE controller
+ -
+ name: mse-capabilities
+ doc: MSE capabilities attribute set
+ attr-cnt-name: --ethtool-a-mse-capabilities-cnt
+ attributes:
+ -
+ name: max-average-mse
+ type: uint
+ -
+ name: max-peak-mse
+ type: uint
+ -
+ name: refresh-rate-ps
+ type: uint
+ -
+ name: num-symbols
+ type: uint
+ -
+ name: mse-snapshot
+ doc: MSE snapshot attribute set
+ attr-cnt-name: --ethtool-a-mse-snapshot-cnt
+ attributes:
+ -
+ name: average-mse
+ type: uint
+ -
+ name: peak-mse
+ type: uint
+ -
+ name: worst-peak-mse
+ type: uint
+ -
+ name: mse
+ attr-cnt-name: --ethtool-a-mse-cnt
+ attributes:
+ -
+ name: header
+ type: nest
+ nested-attributes: header
+ -
+ name: capabilities
+ type: nest
+ nested-attributes: mse-capabilities
+ -
+ name: channel-a
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: channel-b
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: channel-c
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: channel-d
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: worst-channel
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: link
+ type: nest
+ nested-attributes: mse-snapshot
operations:
enum-model: directional
@@ -2756,6 +2823,25 @@ operations:
attributes:
- header
- context
+ -
+ name: mse-get
+ doc: Get PHY MSE measurement data and capabilities.
+ attribute-set: mse
+ do: &mse-get-op
+ request:
+ attributes:
+ - header
+ reply:
+ attributes:
+ - header
+ - capabilities
+ - channel-a
+ - channel-b
+ - channel-c
+ - channel-d
+ - worst-channel
+ - link
+ dump: *mse-get-op
mcast-groups:
list:
diff --git a/Documentation/netlink/specs/netdev.yaml b/Documentation/netlink/specs/netdev.yaml
index e00d3fa1c152..82bf5cb2617d 100644
--- a/Documentation/netlink/specs/netdev.yaml
+++ b/Documentation/netlink/specs/netdev.yaml
@@ -88,7 +88,7 @@ definitions:
-
name: napi-threaded
type: enum
- entries: [disabled, enabled]
+ entries: [disabled, enabled, busy-poll]
attribute-sets:
-
@@ -291,7 +291,8 @@ attribute-sets:
name: threaded
doc: Whether the NAPI is configured to operate in threaded polling
mode. If this is set to enabled then the NAPI context operates
- in threaded polling mode.
+ in threaded polling mode. If this is set to busy-poll, then the
+ threaded polling mode also busy polls.
type: u32
enum: napi-threaded
-
@@ -732,6 +733,29 @@ operations:
- rx-bytes
- tx-packets
- tx-bytes
+ - rx-alloc-fail
+ - rx-hw-drops
+ - rx-hw-drop-overruns
+ - rx-csum-complete
+ - rx-csum-unnecessary
+ - rx-csum-none
+ - rx-csum-bad
+ - rx-hw-gro-packets
+ - rx-hw-gro-bytes
+ - rx-hw-gro-wire-packets
+ - rx-hw-gro-wire-bytes
+ - rx-hw-drop-ratelimits
+ - tx-hw-drops
+ - tx-hw-drop-errors
+ - tx-csum-none
+ - tx-needs-csum
+ - tx-hw-gso-packets
+ - tx-hw-gso-bytes
+ - tx-hw-gso-wire-packets
+ - tx-hw-gso-wire-bytes
+ - tx-hw-drop-ratelimits
+ - tx-stop
+ - tx-wake
-
name: bind-rx
doc: Bind dmabuf to netdev
diff --git a/Documentation/netlink/specs/nftables.yaml b/Documentation/netlink/specs/nftables.yaml
index cce88819ba71..17ad707fa0d5 100644
--- a/Documentation/netlink/specs/nftables.yaml
+++ b/Documentation/netlink/specs/nftables.yaml
@@ -915,7 +915,7 @@ attribute-sets:
type: string
doc: Name of set to use
-
- name: set id
+ name: set-id
type: u32
byte-order: big-endian
doc: ID of set to use
diff --git a/Documentation/netlink/specs/psp.yaml b/Documentation/netlink/specs/psp.yaml
index 944429e5c9a8..f3a57782d2cf 100644
--- a/Documentation/netlink/specs/psp.yaml
+++ b/Documentation/netlink/specs/psp.yaml
@@ -76,6 +76,83 @@ attribute-sets:
name: spi
doc: Security Parameters Index (SPI) of the association.
type: u32
+ -
+ name: stats
+ attributes:
+ -
+ name: dev-id
+ doc: PSP device ID.
+ type: u32
+ checks:
+ min: 1
+ -
+ name: key-rotations
+ type: uint
+ doc: |
+ Number of key rotations during the lifetime of the device.
+ Kernel statistic.
+ -
+ name: stale-events
+ type: uint
+ doc: |
+ Number of times a socket's Rx got shut down due to using
+ a key which went stale (fully rotated out).
+ Kernel statistic.
+ -
+ name: rx-packets
+ type: uint
+ doc: |
+ Number of successfully processed and authenticated PSP packets.
+ Device statistic (from the PSP spec).
+ -
+ name: rx-bytes
+ type: uint
+ doc: |
+ Number of successfully authenticated PSP bytes received, counting from
+ the first byte after the IV through the last byte of payload.
+ The fixed initial portion of the PSP header (16 bytes)
+ and the PSP trailer/ICV (16 bytes) are not included in this count.
+ Device statistic (from the PSP spec).
+ -
+ name: rx-auth-fail
+ type: uint
+ doc: |
+ Number of received PSP packets with unsuccessful authentication.
+ Device statistic (from the PSP spec).
+ -
+ name: rx-error
+ type: uint
+ doc: |
+ Number of received PSP packets with length/framing errors.
+ Device statistic (from the PSP spec).
+ -
+ name: rx-bad
+ type: uint
+ doc: |
+ Number of received PSP packets with miscellaneous errors
+ (invalid master key indicated by SPI, unsupported version, etc.)
+ Device statistic (from the PSP spec).
+ -
+ name: tx-packets
+ type: uint
+ doc: |
+ Number of successfully processed PSP packets for transmission.
+ Device statistic (from the PSP spec).
+ -
+ name: tx-bytes
+ type: uint
+ doc: |
+ Number of successfully processed PSP bytes for transmit, counting from
+ the first byte after the IV through the last byte of payload.
+ The fixed initial portion of the PSP header (16 bytes)
+ and the PSP trailer/ICV (16 bytes) are not included in this count.
+ Device statistic (from the PSP spec).
+ -
+ name: tx-error
+ type: uint
+ doc: |
+ Number of PSP packets for transmission with errors.
+ Device statistic (from the PSP spec).
operations:
list:
@@ -177,6 +254,24 @@ operations:
pre: psp-assoc-device-get-locked
post: psp-device-unlock
+ -
+ name: get-stats
+ doc: Get device statistics.
+ attribute-set: stats
+ do:
+ request:
+ attributes:
+ - dev-id
+ reply: &stats-all
+ attributes:
+ - dev-id
+ - key-rotations
+ - stale-events
+ pre: psp-device-get-locked
+ post: psp-device-unlock
+ dump:
+ reply: *stats-all
+
mcast-groups:
list:
-
diff --git a/Documentation/netlink/specs/rt-addr.yaml b/Documentation/netlink/specs/rt-addr.yaml
index 3a582eac1629..163a106c41bb 100644
--- a/Documentation/netlink/specs/rt-addr.yaml
+++ b/Documentation/netlink/specs/rt-addr.yaml
@@ -86,17 +86,18 @@ attribute-sets:
-
name: address
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: local
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: label
type: string
-
name: broadcast
- type: binary
+ type: u32
+ byte-order: big-endian
display-hint: ipv4
-
name: anycast
diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml
index 2a23e9699c0b..6beeb6ee5adf 100644
--- a/Documentation/netlink/specs/rt-link.yaml
+++ b/Documentation/netlink/specs/rt-link.yaml
@@ -1707,11 +1707,11 @@ attribute-sets:
-
name: local
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: remote
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: ttl
type: u8
@@ -1833,11 +1833,11 @@ attribute-sets:
-
name: local
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: remote
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: fwmark
type: u32
@@ -1868,7 +1868,8 @@ attribute-sets:
type: u32
-
name: remote
- type: binary
+ type: u32
+ byte-order: big-endian
display-hint: ipv4
-
name: ttl
@@ -1914,6 +1915,35 @@ attribute-sets:
type: binary
struct: ifla-geneve-port-range
-
+ name: linkinfo-hsr-attrs
+ name-prefix: ifla-hsr-
+ attributes:
+ -
+ name: slave1
+ type: u32
+ -
+ name: slave2
+ type: u32
+ -
+ name: multicast-spec
+ type: u8
+ -
+ name: supervision-addr
+ type: binary
+ display-hint: mac
+ -
+ name: seq-nr
+ type: u16
+ -
+ name: version
+ type: u8
+ -
+ name: protocol
+ type: u8
+ -
+ name: interlink
+ type: u32
+ -
name: linkinfo-iptun-attrs
name-prefix: ifla-iptun-
attributes:
@@ -1923,11 +1953,11 @@ attribute-sets:
-
name: local
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: remote
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: ttl
type: u8
@@ -1957,7 +1987,8 @@ attribute-sets:
display-hint: ipv6
-
name: 6rd-relay-prefix
- type: binary
+ type: u32
+ byte-order: big-endian
display-hint: ipv4
-
name: 6rd-prefixlen
@@ -2300,6 +2331,9 @@ sub-messages:
value: geneve
attribute-set: linkinfo-geneve-attrs
-
+ value: hsr
+ attribute-set: linkinfo-hsr-attrs
+ -
value: ipip
attribute-set: linkinfo-iptun-attrs
-
diff --git a/Documentation/netlink/specs/rt-neigh.yaml b/Documentation/netlink/specs/rt-neigh.yaml
index 2f568a6231c9..0f46ef313590 100644
--- a/Documentation/netlink/specs/rt-neigh.yaml
+++ b/Documentation/netlink/specs/rt-neigh.yaml
@@ -194,7 +194,7 @@ attribute-sets:
-
name: dst
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: lladdr
type: binary
diff --git a/Documentation/netlink/specs/rt-route.yaml b/Documentation/netlink/specs/rt-route.yaml
index 1ecb3fadc067..33195db96746 100644
--- a/Documentation/netlink/specs/rt-route.yaml
+++ b/Documentation/netlink/specs/rt-route.yaml
@@ -87,11 +87,11 @@ attribute-sets:
-
name: dst
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: src
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: iif
type: u32
@@ -101,14 +101,14 @@ attribute-sets:
-
name: gateway
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: priority
type: u32
-
name: prefsrc
type: binary
- display-hint: ipv4
+ display-hint: ipv4-or-v6
-
name: metrics
type: nest
diff --git a/Documentation/netlink/specs/rt-rule.yaml b/Documentation/netlink/specs/rt-rule.yaml
index bebee452a950..7f03a44ab036 100644
--- a/Documentation/netlink/specs/rt-rule.yaml
+++ b/Documentation/netlink/specs/rt-rule.yaml
@@ -96,10 +96,12 @@ attribute-sets:
attributes:
-
name: dst
- type: u32
+ type: binary
+ display-hint: ipv4-or-v6
-
name: src
- type: u32
+ type: binary
+ display-hint: ipv4-or-v6
-
name: iifname
type: string
diff --git a/Documentation/netlink/specs/wireguard.yaml b/Documentation/netlink/specs/wireguard.yaml
new file mode 100644
index 000000000000..30479fc6bb69
--- /dev/null
+++ b/Documentation/netlink/specs/wireguard.yaml
@@ -0,0 +1,298 @@
+# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+---
+name: wireguard
+protocol: genetlink-legacy
+
+doc: |
+ **Netlink protocol to control WireGuard network devices.**
+
+ The below enums and macros are for interfacing with WireGuard, using generic
+ netlink, with family ``WG_GENL_NAME`` and version ``WG_GENL_VERSION``. It
+ defines two commands: get and set. Note that while they share many common
+ attributes, these two commands actually accept a slightly different set of
+ inputs and outputs. These differences are noted under the individual
+ attributes.
+c-family-name: wg-genl-name
+c-version-name: wg-genl-version
+max-by-define: true
+
+definitions:
+ -
+ name-prefix: wg-
+ name: key-len
+ type: const
+ value: 32
+ -
+ name: --kernel-timespec
+ type: struct
+ header: linux/time_types.h
+ members:
+ -
+ name: sec
+ type: u64
+ doc: Number of seconds, since UNIX epoch.
+ -
+ name: nsec
+ type: u64
+ doc: Number of nanoseconds, after the second began.
+ -
+ name: wgdevice-flags
+ name-prefix: wgdevice-f-
+ enum-name: wgdevice-flag
+ type: flags
+ entries:
+ - replace-peers
+ -
+ name: wgpeer-flags
+ name-prefix: wgpeer-f-
+ enum-name: wgpeer-flag
+ type: flags
+ entries:
+ - remove-me
+ - replace-allowedips
+ - update-only
+ -
+ name: wgallowedip-flags
+ name-prefix: wgallowedip-f-
+ enum-name: wgallowedip-flag
+ type: flags
+ entries:
+ - remove-me
+
+attribute-sets:
+ -
+ name: wgdevice
+ enum-name: wgdevice-attribute
+ name-prefix: wgdevice-a-
+ attr-cnt-name: --wgdevice-a-last
+ attributes:
+ -
+ name: unspec
+ type: unused
+ value: 0
+ -
+ name: ifindex
+ type: u32
+ -
+ name: ifname
+ type: string
+ checks:
+ max-len: 15
+ -
+ name: private-key
+ type: binary
+ doc: Set to all zeros to remove.
+ display-hint: hex
+ checks:
+ exact-len: wg-key-len
+ -
+ name: public-key
+ type: binary
+ display-hint: hex
+ checks:
+ exact-len: wg-key-len
+ -
+ name: flags
+ type: u32
+ doc: |
+ ``0`` or ``WGDEVICE_F_REPLACE_PEERS`` if all current peers should be
+ removed prior to adding the list below.
+ enum: wgdevice-flags
+ -
+ name: listen-port
+ type: u16
+ doc: Set as ``0`` to choose randomly.
+ -
+ name: fwmark
+ type: u32
+ doc: Set as ``0`` to disable.
+ -
+ name: peers
+ type: indexed-array
+ sub-type: nest
+ nested-attributes: wgpeer
+ doc: |
+ The index/type parameter is unused on ``SET_DEVICE`` operations and is
+ zero on ``GET_DEVICE`` operations.
+ -
+ name: wgpeer
+ enum-name: wgpeer-attribute
+ name-prefix: wgpeer-a-
+ attr-cnt-name: --wgpeer-a-last
+ attributes:
+ -
+ name: unspec
+ type: unused
+ value: 0
+ -
+ name: public-key
+ type: binary
+ display-hint: hex
+ checks:
+ exact-len: wg-key-len
+ -
+ name: preshared-key
+ type: binary
+ doc: Set as all zeros to remove.
+ display-hint: hex
+ checks:
+ exact-len: wg-key-len
+ -
+ name: flags
+ type: u32
+ doc: |
+ ``0`` and/or ``WGPEER_F_REMOVE_ME`` if the specified peer should not
+ exist at the end of the operation, rather than added/updated and/or
+ ``WGPEER_F_REPLACE_ALLOWEDIPS`` if all current allowed IPs of this
+ peer should be removed prior to adding the list below and/or
+ ``WGPEER_F_UPDATE_ONLY`` if the peer should only be set if it already
+ exists.
+ enum: wgpeer-flags
+ -
+ name: endpoint
+ type: binary
+ doc: struct sockaddr_in or struct sockaddr_in6
+ checks:
+ min-len: 16
+ -
+ name: persistent-keepalive-interval
+ type: u16
+ doc: Set as ``0`` to disable.
+ -
+ name: last-handshake-time
+ type: binary
+ struct: --kernel-timespec
+ checks:
+ exact-len: 16
+ -
+ name: rx-bytes
+ type: u64
+ -
+ name: tx-bytes
+ type: u64
+ -
+ name: allowedips
+ type: indexed-array
+ sub-type: nest
+ nested-attributes: wgallowedip
+ doc: |
+ The index/type parameter is unused on ``SET_DEVICE`` operations and is
+ zero on ``GET_DEVICE`` operations.
+ -
+ name: protocol-version
+ type: u32
+ doc: |
+ Should not be set or used at all by most users of this API, as the
+ most recent protocol will be used when this is unset. Otherwise,
+ must be set to ``1``.
+ -
+ name: wgallowedip
+ enum-name: wgallowedip-attribute
+ name-prefix: wgallowedip-a-
+ attr-cnt-name: --wgallowedip-a-last
+ attributes:
+ -
+ name: unspec
+ type: unused
+ value: 0
+ -
+ name: family
+ type: u16
+ doc: IP family, either ``AF_INET`` or ``AF_INET6``.
+ -
+ name: ipaddr
+ type: binary
+ doc: Either ``struct in_addr`` or ``struct in6_addr``.
+ display-hint: ipv4-or-v6
+ checks:
+ min-len: 4
+ -
+ name: cidr-mask
+ type: u8
+ -
+ name: flags
+ type: u32
+ doc: |
+ ``WGALLOWEDIP_F_REMOVE_ME`` if the specified IP should be removed;
+ otherwise, this IP will be added if it is not already present.
+ enum: wgallowedip-flags
+
+operations:
+ enum-name: wg-cmd
+ name-prefix: wg-cmd-
+ list:
+ -
+ name: get-device
+ value: 0
+ doc: |
+ Retrieve WireGuard device
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ The command should be called with one but not both of:
+
+ - ``WGDEVICE_A_IFINDEX``
+ - ``WGDEVICE_A_IFNAME``
+
+ The kernel will then return several messages (``NLM_F_MULTI``). It is
+ possible that all of the allowed IPs of a single peer will not fit
+ within a single netlink message. In that case, the same peer will be
+ written in the following message, except it will only contain
+ ``WGPEER_A_PUBLIC_KEY`` and ``WGPEER_A_ALLOWEDIPS``. This may occur
+ several times in a row for the same peer. It is then up to the receiver
+ to coalesce adjacent peers. Likewise, it is possible that all peers will
+ not fit within a single message. So, subsequent peers will be sent in
+ following messages, except those will only contain ``WGDEVICE_A_IFNAME``
+ and ``WGDEVICE_A_PEERS``. It is then up to the receiver to coalesce
+ these messages to form the complete list of peers.
+
+ Since this is an ``NLA_F_DUMP`` command, the final message will always
+ be ``NLMSG_DONE``, even if an error occurs. However, this ``NLMSG_DONE``
+ message contains an integer error code. It is either zero or a negative
+ error code corresponding to the errno.
+ attribute-set: wgdevice
+ flags: [uns-admin-perm]
+
+ dump:
+ pre: wg-get-device-start
+ post: wg-get-device-done
+ request:
+ attributes:
+ - ifindex
+ - ifname
+ reply: &all-attrs
+ attributes:
+ - ifindex
+ - ifname
+ - private-key
+ - public-key
+ - flags
+ - listen-port
+ - fwmark
+ - peers
+ -
+ name: set-device
+ value: 1
+ doc: |
+ Set WireGuard device
+ ~~~~~~~~~~~~~~~~~~~~
+
+ This command should be called with a wgdevice set, containing one but
+ not both of ``WGDEVICE_A_IFINDEX`` and ``WGDEVICE_A_IFNAME``.
+
+ It is possible that the amount of configuration data exceeds that of the
+ maximum message length accepted by the kernel. In that case, several
+ messages should be sent one after another, with each successive one
+ filling in information not contained in the prior. Note that if
+ ``WGDEVICE_F_REPLACE_PEERS`` is specified in the first message, it
+ probably should not be specified in fragments that come after, so that
+ the list of peers is only cleared the first time but appended after.
+ Likewise for peers, if ``WGPEER_F_REPLACE_ALLOWEDIPS`` is specified in
+ the first message of a peer, it likely should not be specified in
+ subsequent fragments.
+
+ If an error occurs, ``NLMSG_ERROR`` will reply containing an errno.
+ attribute-set: wgdevice
+ flags: [uns-admin-perm]
+
+ do:
+ request: *all-attrs
diff --git a/Documentation/networking/6pack.rst b/Documentation/networking/6pack.rst
index bc5bf1f1a98f..66d5fd4fc821 100644
--- a/Documentation/networking/6pack.rst
+++ b/Documentation/networking/6pack.rst
@@ -94,7 +94,7 @@ kernels may lead to a compilation error because the interface to a kernel
function has been changed in the 2.1.8x kernels.
How to turn on 6pack support:
-=============================
+-----------------------------
- In the linux kernel configuration program, select the code maturity level
options menu and turn on the prompting for development drivers.
diff --git a/Documentation/networking/arcnet-hardware.rst b/Documentation/networking/arcnet-hardware.rst
index 3bf7f99cd7bb..20e5075d0d0e 100644
--- a/Documentation/networking/arcnet-hardware.rst
+++ b/Documentation/networking/arcnet-hardware.rst
@@ -4,18 +4,20 @@
ARCnet Hardware
===============
+:Author: Avery Pennarun <apenwarr@worldvisions.ca>
+
.. note::
- 1) This file is a supplement to arcnet.txt. Please read that for general
+ 1) This file is a supplement to arcnet.rst. Please read that for general
driver configuration help.
2) This file is no longer Linux-specific. It should probably be moved out
of the kernel sources. Ideas?
Because so many people (myself included) seem to have obtained ARCnet cards
without manuals, this file contains a quick introduction to ARCnet hardware,
-some cabling tips, and a listing of all jumper settings I can find. Please
-e-mail apenwarr@worldvisions.ca with any settings for your particular card,
-or any other information you have!
+some cabling tips, and a listing of all jumper settings I can find. If you
+have any settings for your particular card, and/or any other information you
+have, do not hesitate to :ref:`email to netdev <arcnet-netdev>`.
Introduction to ARCnet
@@ -72,11 +74,10 @@ level of encapsulation is defined by RFC1201, which I call "packet
splitting," that allows "virtual packets" to grow as large as 64K each,
although they are generally kept down to the Ethernet-style 1500 bytes.
-For more information on the advantages and disadvantages (mostly the
-advantages) of ARCnet networks, you might try the "ARCnet Trade Association"
-WWW page:
+For more information on ARCnet networks, visit the "ARCNET Resource Center"
+WWW page at:
- http://www.arcnet.com
+ https://www.arcnet.cc
Cabling ARCnet Networks
@@ -3226,9 +3227,6 @@ Settings for IRQ Selection (Lower Jumper Line)
Other Cards
===========
-I have no information on other models of ARCnet cards at the moment. Please
-send any and all info to:
-
- apenwarr@worldvisions.ca
+I have no information on other models of ARCnet cards at the moment.
Thanks.
diff --git a/Documentation/networking/arcnet.rst b/Documentation/networking/arcnet.rst
index 82fce606c0f0..cd43a18ad149 100644
--- a/Documentation/networking/arcnet.rst
+++ b/Documentation/networking/arcnet.rst
@@ -4,6 +4,8 @@
ARCnet
======
+:Author: Avery Pennarun <apenwarr@worldvisions.ca>
+
.. note::
See also arcnet-hardware.txt in this directory for jumper-setting
@@ -30,18 +32,7 @@ Come on, be a sport! Send me a success report!
(hey, that was even better than my original poem... this is getting bad!)
-
-.. warning::
-
- If you don't e-mail me about your success/failure soon, I may be forced to
- start SINGING. And we don't want that, do we?
-
- (You know, it might be argued that I'm pushing this point a little too much.
- If you think so, why not flame me in a quick little e-mail? Please also
- include the type of card(s) you're using, software, size of network, and
- whether it's working or not.)
-
- My e-mail address is: apenwarr@worldvisions.ca
+----
These are the ARCnet drivers for Linux.
@@ -59,23 +50,14 @@ ARCnet 2.10 ALPHA, Tomasz's all-new-and-improved RFC1051 support has been
included and seems to be working fine!
+.. _arcnet-netdev:
+
Where do I discuss these drivers?
---------------------------------
-Tomasz has been so kind as to set up a new and improved mailing list.
-Subscribe by sending a message with the BODY "subscribe linux-arcnet YOUR
-REAL NAME" to listserv@tichy.ch.uj.edu.pl. Then, to submit messages to the
-list, mail to linux-arcnet@tichy.ch.uj.edu.pl.
-
-There are archives of the mailing list at:
-
- http://epistolary.org/mailman/listinfo.cgi/arcnet
-
-The people on linux-net@vger.kernel.org (now defunct, replaced by
-netdev@vger.kernel.org) have also been known to be very helpful, especially
-when we're talking about ALPHA Linux kernels that may or may not work right
-in the first place.
-
+ARCnet discussions take place on netdev. Simply send your email to
+netdev@vger.kernel.org and make sure to Cc: maintainer listed in
+"ARCNET NETWORK LAYER" heading of Documentation/process/maintainers.rst.
Other Drivers and Info
----------------------
@@ -523,17 +505,9 @@ can set up your network then:
It works: what now?
-------------------
-Send mail describing your setup, preferably including driver version, kernel
-version, ARCnet card model, CPU type, number of systems on your network, and
-list of software in use to me at the following address:
-
- apenwarr@worldvisions.ca
-
-I do send (sometimes automated) replies to all messages I receive. My email
-can be weird (and also usually gets forwarded all over the place along the
-way to me), so if you don't get a reply within a reasonable time, please
-resend.
-
+Send mail following :ref:`arcnet-netdev`. Describe your setup, preferably
+including driver version, kernel version, ARCnet card model, CPU type, number
+of systems on your network, and list of software in use.
It doesn't work: what now?
--------------------------
diff --git a/Documentation/networking/ax25.rst b/Documentation/networking/ax25.rst
index 605e72c6c877..89c79dd6c6f9 100644
--- a/Documentation/networking/ax25.rst
+++ b/Documentation/networking/ax25.rst
@@ -11,6 +11,7 @@ found on https://linux-ax25.in-berlin.de.
There is a mailing list for discussing Linux amateur radio matters
called linux-hams@vger.kernel.org. To subscribe to it, send a message to
-majordomo@vger.kernel.org with the words "subscribe linux-hams" in the body
-of the message, the subject field is ignored. You don't need to be
-subscribed to post but of course that means you might miss an answer.
+linux-hams+subscribe@vger.kernel.org or use the web interface at
+https://vger.kernel.org. The subject and body of the message are
+ignored. You don't need to be subscribed to post but of course that
+means you might miss an answer.
diff --git a/Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst b/Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst
index 289c146a8291..5aedbabb7382 100644
--- a/Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst
+++ b/Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst
@@ -28,6 +28,7 @@ these MAP frames and send them to appropriate PDN's.
================
a. MAP packet v1 (data / control)
+---------------------------------
MAP header fields are in big endian format.
@@ -54,6 +55,7 @@ Payload length includes the padding length but does not include MAP header
length.
b. Map packet v4 (data / control)
+---------------------------------
MAP header fields are in big endian format.
@@ -107,6 +109,7 @@ over which checksum is computed.
Checksum value, indicates the checksum computed.
c. MAP packet v5 (data / control)
+---------------------------------
MAP header fields are in big endian format.
@@ -134,19 +137,24 @@ Payload length includes the padding length but does not include MAP header
length.
d. Checksum offload header v5
+-----------------------------
Checksum offload header fields are in big endian format.
+Packet format::
+
Bit 0 - 6 7 8-15 16-31
Function Header Type Next Header Checksum Valid Reserved
Header Type is to indicate the type of header, this usually is set to CHECKSUM
Header types
-= ==========================================
+
+= ===============
0 Reserved
1 Reserved
2 checksum header
+= ===============
Checksum Valid is to indicate whether the header checksum is valid. Value of 1
implies that checksum is calculated on this packet and is valid, value of 0
@@ -154,7 +162,10 @@ indicates that the calculated packet checksum is invalid.
Reserved bits must be zero when sent and ignored when received.
-e. MAP packet v1/v5 (command specific)::
+e. MAP packet v1/v5 (command specific)
+--------------------------------------
+
+Packet format::
Bit 0 1 2-7 8 - 15 16 - 31
Function Command Reserved Pad Multiplexer ID Payload length
@@ -177,15 +188,18 @@ Command types
= ==========================================
f. Aggregation
+--------------
Aggregation is multiple MAP packets (can be data or command) delivered to
rmnet in a single linear skb. rmnet will process the individual
packets and either ACK the MAP command or deliver the IP packet to the
network stack as needed
-MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
+Packet format::
+
+ MAP header|IP Packet|Optional padding|MAP header|IP Packet|Optional padding....
-MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
+ MAP header|IP Packet|Optional padding|MAP header|Command Packet|Optional pad...
3. Userspace configuration
==========================
diff --git a/Documentation/networking/device_drivers/ethernet/index.rst b/Documentation/networking/device_drivers/ethernet/index.rst
index 7cfcd183054f..bcc02355f828 100644
--- a/Documentation/networking/device_drivers/ethernet/index.rst
+++ b/Documentation/networking/device_drivers/ethernet/index.rst
@@ -47,6 +47,7 @@ Contents:
mellanox/mlx5/index
meta/fbnic
microsoft/netvsc
+ mucse/rnpgbe
neterion/s2io
netronome/nfp
pensando/ionic
diff --git a/Documentation/networking/device_drivers/ethernet/mucse/rnpgbe.rst b/Documentation/networking/device_drivers/ethernet/mucse/rnpgbe.rst
new file mode 100644
index 000000000000..d35cf8a46b6c
--- /dev/null
+++ b/Documentation/networking/device_drivers/ethernet/mucse/rnpgbe.rst
@@ -0,0 +1,17 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================================================
+Linux Base Driver for MUCSE(R) Gigabit PCI Express Adapters
+===========================================================
+
+Contents
+========
+
+- Identifying Your Adapter
+
+Identifying Your Adapter
+========================
+The driver is compatible with devices based on the following:
+
+ * MUCSE(R) Ethernet Controller N210 series
+ * MUCSE(R) Ethernet Controller N500 series
diff --git a/Documentation/networking/devlink/devlink-eswitch-attr.rst b/Documentation/networking/devlink/devlink-eswitch-attr.rst
index 08bb39ab1528..eafe09abc40c 100644
--- a/Documentation/networking/devlink/devlink-eswitch-attr.rst
+++ b/Documentation/networking/devlink/devlink-eswitch-attr.rst
@@ -39,6 +39,10 @@ The following is a list of E-Switch attributes.
rules.
* ``switchdev`` allows for more advanced offloading capabilities of
the E-Switch to hardware.
+ * ``switchdev_inactive`` switchdev mode but starts inactive, doesn't allow traffic
+ until explicitly activated. This mode is useful for orchestrators that
+ want to prepare the device in switchdev mode but only activate it when
+ all configurations are done.
* - ``inline-mode``
- enum
- Some HWs need the VF driver to put part of the packet
@@ -74,3 +78,12 @@ Example Usage
# enable encap-mode with legacy mode
$ devlink dev eswitch set pci/0000:08:00.0 mode legacy inline-mode none encap-mode basic
+
+ # start switchdev mode in inactive state
+ $ devlink dev eswitch set pci/0000:08:00.0 mode switchdev_inactive
+
+ # setup switchdev configurations, representors, FDB entries, etc..
+ ...
+
+ # activate switchdev mode to allow traffic
+ $ devlink dev eswitch set pci/0000:08:00.0 mode switchdev
diff --git a/Documentation/networking/devlink/devlink-params.rst b/Documentation/networking/devlink/devlink-params.rst
index 0a9c20d70122..ea17756dcda6 100644
--- a/Documentation/networking/devlink/devlink-params.rst
+++ b/Documentation/networking/devlink/devlink-params.rst
@@ -41,6 +41,16 @@ In order for ``driverinit`` parameters to take effect, the driver must
support reloading via the ``devlink-reload`` command. This command will
request a reload of the device driver.
+Default parameter values
+=========================
+
+Drivers may optionally export default values for parameters of cmode
+``runtime`` and ``permanent``. For ``driverinit`` parameters, the last
+value set by the driver will be used as the default value. Drivers can
+also support resetting params with cmode ``runtime`` and ``permanent``
+to their default values. Resetting ``driverinit`` params is supported
+by devlink core without additional driver support needed.
+
.. _devlink_params_generic:
Generic configuration parameters
@@ -151,3 +161,7 @@ own name.
* - ``num_doorbells``
- u32
- Controls the number of doorbells used by the device.
+ * - ``max_mac_per_vf``
+ - u32
+ - Controls the maximum number of MAC address filters that can be assigned
+ to a Virtual Function (VF).
diff --git a/Documentation/networking/devlink/i40e.rst b/Documentation/networking/devlink/i40e.rst
index d3cb5bb5197e..51c887f0dc83 100644
--- a/Documentation/networking/devlink/i40e.rst
+++ b/Documentation/networking/devlink/i40e.rst
@@ -7,6 +7,40 @@ i40e devlink support
This document describes the devlink features implemented by the ``i40e``
device driver.
+Parameters
+==========
+
+.. list-table:: Generic parameters implemented
+ :widths: 5 5 90
+
+ * - Name
+ - Mode
+ - Notes
+ * - ``max_mac_per_vf``
+ - runtime
+ - Controls the maximum number of MAC addresses a VF can use
+ on i40e devices.
+
+ By default (``0``), the driver enforces its internally calculated per-VF
+ MAC filter limit, which is based on the number of allocated VFS.
+
+ If set to a non-zero value, this parameter acts as a strict cap:
+ the driver will use the user-provided value instead of its internal
+ calculation.
+
+ **Important notes:**
+
+ - This value **must be set before enabling SR-IOV**.
+ Attempting to change it while SR-IOV is enabled will return an error.
+ - MAC filters are a **shared hardware resource** across all VFs.
+ Setting a high value may cause other VFs to be starved of filters.
+ - This value is a **Administrative policy**. The hardware may return
+ errors when its absolute limit is reached, regardless of the value
+ set here.
+
+ The default value is ``0`` (internal calculation is used).
+
+
Info versions
=============
diff --git a/Documentation/networking/devlink/index.rst b/Documentation/networking/devlink/index.rst
index 0c58e5c729d9..35b12a2bfeba 100644
--- a/Documentation/networking/devlink/index.rst
+++ b/Documentation/networking/devlink/index.rst
@@ -99,5 +99,6 @@ parameters, info versions, and other features it supports.
prestera
qed
sfc
+ stmmac
ti-cpsw-switch
zl3073x
diff --git a/Documentation/networking/devlink/mlx5.rst b/Documentation/networking/devlink/mlx5.rst
index 0e5f9c76e514..4bba4d780a4a 100644
--- a/Documentation/networking/devlink/mlx5.rst
+++ b/Documentation/networking/devlink/mlx5.rst
@@ -218,6 +218,20 @@ parameters.
* ``balanced`` : Merges fewer CQEs, resulting in a moderate compression ratio but maintaining a balance between bandwidth savings and performance
* ``aggressive`` : Merges more CQEs into a single entry, achieving a higher compression rate and maximizing performance, particularly under high traffic loads
+ * - ``swp_l4_csum_mode``
+ - string
+ - permanent
+ - Configure how the L4 checksum is calculated by the device when using
+ Software Parser (SWP) hints for header locations.
+
+ * ``default`` : Use the device's default checksum calculation
+ mode. The driver will discover during init whether or
+ full_csum or l4_only is in use. Setting this value explicitly
+ from userspace is not allowed, but some firmware versions may
+ return this value on param read.
+ * ``full_csum`` : Calculate full checksum including the pseudo-header
+ * ``l4_only`` : Calculate L4-only checksum, excluding the pseudo-header
+
The ``mlx5`` driver supports reloading via ``DEVLINK_CMD_RELOAD``
Info versions
diff --git a/Documentation/networking/devlink/stmmac.rst b/Documentation/networking/devlink/stmmac.rst
new file mode 100644
index 000000000000..47e3ff10bc08
--- /dev/null
+++ b/Documentation/networking/devlink/stmmac.rst
@@ -0,0 +1,40 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
+stmmac (synopsys dwmac) devlink support
+=======================================
+
+This document describes the devlink features implemented by the ``stmmac``
+device driver.
+
+Parameters
+==========
+
+The ``stmmac`` driver implements the following driver-specific parameters.
+
+.. list-table:: Driver-specific parameters implemented
+ :widths: 5 5 5 85
+
+ * - Name
+ - Type
+ - Mode
+ - Description
+ * - ``phc_coarse_adj``
+ - Boolean
+ - runtime
+ - Enable the Coarse timestamping mode, as defined in the DWMAC TRM.
+ A detailed explanation of this timestamping mode can be found in the
+ Socfpga Functionnal Description [1].
+
+ In Coarse mode, the ptp clock is expected to be fed by a high-precision
+ clock that is externally adjusted, and the subsecond increment used for
+ timestamping is set to 1/ptp_clock_rate.
+
+ In Fine mode (i.e. Coarse mode == false), the ptp clock frequency is
+ continuously adjusted, but the subsecond increment is set to
+ 2/ptp_clock_rate.
+
+ Coarse mode is suitable for PTP Grand Master operation. If unsure, leave
+ the parameter to False.
+
+ [1] https://www.intel.com/content/www/us/en/docs/programmable/683126/21-2/functional-description-of-the-emac.html
diff --git a/Documentation/networking/dsa/dsa.rst b/Documentation/networking/dsa/dsa.rst
index 7b2e69cd7ef0..5c79740a533b 100644
--- a/Documentation/networking/dsa/dsa.rst
+++ b/Documentation/networking/dsa/dsa.rst
@@ -1104,12 +1104,11 @@ health of the network and for discovery of other nodes.
In Linux, both HSR and PRP are implemented in the hsr driver, which
instantiates a virtual, stackable network interface with two member ports.
The driver only implements the basic roles of DANH (Doubly Attached Node
-implementing HSR) and DANP (Doubly Attached Node implementing PRP); the roles
-of RedBox and QuadBox are not implemented (therefore, bridging a hsr network
-interface with a physical switch port does not produce the expected result).
+implementing HSR), DANP (Doubly Attached Node implementing PRP) and RedBox
+(allows non-HSR devices to connect to the ring via Interlink ports).
-A driver which is able of offloading certain functions of a DANP or DANH should
-declare the corresponding netdev features as indicated by the documentation at
+A driver which is able of offloading certain functions should declare the
+corresponding netdev features as indicated by the documentation at
``Documentation/networking/netdev-features.rst``. Additionally, the following
methods must be implemented:
@@ -1120,6 +1119,14 @@ methods must be implemented:
- ``port_hsr_leave``: function invoked when a given switch port leaves a
DANP/DANH and returns to normal operation as a standalone port.
+Note that the ``NETIF_F_HW_HSR_DUP`` feature relies on transmission towards
+multiple ports, which is generally available whenever the tagging protocol uses
+the ``dsa_xmit_port_mask()`` helper function. If the helper is used, the HSR
+offload feature should also be set. The ``dsa_port_simple_hsr_join()`` and
+``dsa_port_simple_hsr_leave()`` methods can be used as generic implementations
+of ``port_hsr_join`` and ``port_hsr_leave``, if this is the only supported
+offload feature.
+
TODO
====
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index b270886c5f5d..af56c304cef4 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -242,6 +242,7 @@ Userspace to kernel:
``ETHTOOL_MSG_RSS_SET`` set RSS settings
``ETHTOOL_MSG_RSS_CREATE_ACT`` create an additional RSS context
``ETHTOOL_MSG_RSS_DELETE_ACT`` delete an additional RSS context
+ ``ETHTOOL_MSG_MSE_GET`` get MSE diagnostic data
===================================== =================================
Kernel to userspace:
@@ -299,6 +300,7 @@ Kernel to userspace:
``ETHTOOL_MSG_RSS_CREATE_ACT_REPLY`` create an additional RSS context
``ETHTOOL_MSG_RSS_CREATE_NTF`` additional RSS context created
``ETHTOOL_MSG_RSS_DELETE_NTF`` additional RSS context deleted
+ ``ETHTOOL_MSG_MSE_GET_REPLY`` MSE diagnostic data
======================================== =================================
``GET`` requests are sent by userspace applications to retrieve device
@@ -2458,6 +2460,68 @@ Kernel response contents:
For a description of each attribute, see ``TSCONFIG_GET``.
+MSE_GET
+=======
+
+Retrieves detailed Mean Square Error (MSE) diagnostic information from the PHY.
+
+Request Contents:
+
+ ==================================== ====== ============================
+ ``ETHTOOL_A_MSE_HEADER`` nested request header
+ ==================================== ====== ============================
+
+Kernel Response Contents:
+
+ ==================================== ====== ================================
+ ``ETHTOOL_A_MSE_HEADER`` nested reply header
+ ``ETHTOOL_A_MSE_CAPABILITIES`` nested capability/scale info for MSE
+ measurements
+ ``ETHTOOL_A_MSE_CHANNEL_A`` nested snapshot for Channel A
+ ``ETHTOOL_A_MSE_CHANNEL_B`` nested snapshot for Channel B
+ ``ETHTOOL_A_MSE_CHANNEL_C`` nested snapshot for Channel C
+ ``ETHTOOL_A_MSE_CHANNEL_D`` nested snapshot for Channel D
+ ``ETHTOOL_A_MSE_WORST_CHANNEL`` nested snapshot for worst channel
+ ``ETHTOOL_A_MSE_LINK`` nested snapshot for link-wide aggregate
+ ==================================== ====== ================================
+
+MSE Capabilities
+----------------
+
+This nested attribute reports the capability / scaling properties used to
+interpret snapshot values.
+
+ ============================================== ====== =========================
+ ``ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE`` uint max avg_mse scale
+ ``ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE`` uint max peak_mse scale
+ ``ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS`` uint sample rate (picoseconds)
+ ``ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS`` uint symbols per HW sample
+ ============================================== ====== =========================
+
+The max-average/peak fields are included only if the corresponding metric
+is supported by the PHY. Their absence indicates that the metric is not
+available.
+
+See ``struct phy_mse_capability`` kernel documentation in
+``include/linux/phy.h``.
+
+MSE Snapshot
+------------
+
+Each per-channel nest contains an atomic snapshot of MSE values for that
+selector (channel A/B/C/D, worst channel, or link).
+
+ ========================================== ====== ===================
+ ``ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE`` uint average MSE value
+ ``ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE`` uint current peak MSE
+ ``ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE`` uint worst-case peak MSE
+ ========================================== ====== ===================
+
+Within each channel nest, only the metrics supported by the PHY will be present.
+
+See ``struct phy_mse_snapshot`` kernel documentation in
+``include/linux/phy.h``.
+
Request translation
===================
diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index c775cababc8c..75db2251649b 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -131,10 +131,7 @@ Contents:
vxlan
x25
x25-iface
- xfrm_device
- xfrm_proc
- xfrm_sync
- xfrm_sysctl
+ xfrm/index
xdp-rx-metadata
xsk-tx-metadata
diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index a06cb99d66dc..bc9a01606daf 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -673,6 +673,16 @@ tcp_moderate_rcvbuf - BOOLEAN
Default: 1 (enabled)
+tcp_rcvbuf_low_rtt - INTEGER
+ rcvbuf autotuning can over estimate final socket rcvbuf, which
+ can lead to cache trashing for high throughput flows.
+
+ For small RTT flows (below tcp_rcvbuf_low_rtt usecs), we can relax
+ rcvbuf growth: Few additional ms to reach the final (and smaller)
+ rcvbuf is a good tradeoff.
+
+ Default : 1000 (1 ms)
+
tcp_mtu_probing - INTEGER
Controls TCP Packetization-Layer Path MTU Discovery. Takes three
values:
@@ -854,9 +864,18 @@ tcp_sack - BOOLEAN
Default: 1 (enabled)
+tcp_comp_sack_rtt_percent - INTEGER
+ Percentage of SRTT used for the compressed SACK feature.
+ See tcp_comp_sack_nr, tcp_comp_sack_delay_ns, tcp_comp_sack_slack_ns.
+
+ Possible values : 1 - 1000
+
+ Default : 33 %
+
tcp_comp_sack_delay_ns - LONG INTEGER
- TCP tries to reduce number of SACK sent, using a timer
- based on 5% of SRTT, capped by this sysctl, in nano seconds.
+ TCP tries to reduce number of SACK sent, using a timer based
+ on tcp_comp_sack_rtt_percent of SRTT, capped by this sysctl
+ in nano seconds.
The default is 1ms, based on TSO autosizing period.
Default : 1,000,000 ns (1 ms)
@@ -866,8 +885,9 @@ tcp_comp_sack_slack_ns - LONG INTEGER
timer used by SACK compression. This gives extra time
for small RTT flows, and reduces system overhead by allowing
opportunistic reduction of timer interrupts.
+ Too big values might reduce goodput.
- Default : 100,000 ns (100 us)
+ Default : 10,000 ns (10 us)
tcp_comp_sack_nr - INTEGER
Max number of SACK that can be compressed.
@@ -1796,6 +1816,23 @@ icmp_errors_use_inbound_ifaddr - BOOLEAN
Default: 0 (disabled)
+icmp_errors_extension_mask - UNSIGNED INTEGER
+ Bitmask of ICMP extensions to append to ICMPv4 error messages
+ ("Destination Unreachable", "Time Exceeded" and "Parameter Problem").
+ The original datagram is trimmed / padded to 128 bytes in order to be
+ compatible with applications that do not comply with RFC 4884.
+
+ Possible extensions are:
+
+ ==== ==============================================================
+ 0x01 Incoming IP interface information according to RFC 5837.
+ Extension will include the index, IPv4 address (if present),
+ name and MTU of the IP interface that received the datagram
+ which elicited the ICMP error.
+ ==== ==============================================================
+
+ Default: 0x00 (no extensions)
+
igmp_max_memberships - INTEGER
Change the maximum number of multicast groups we can subscribe to.
Default: 20
@@ -3262,6 +3299,23 @@ error_anycast_as_unicast - BOOLEAN
Default: 0 (disabled)
+errors_extension_mask - UNSIGNED INTEGER
+ Bitmask of ICMP extensions to append to ICMPv6 error messages
+ ("Destination Unreachable" and "Time Exceeded"). The original datagram
+ is trimmed / padded to 128 bytes in order to be compatible with
+ applications that do not comply with RFC 4884.
+
+ Possible extensions are:
+
+ ==== ==============================================================
+ 0x01 Incoming IP interface information according to RFC 5837.
+ Extension will include the index, IPv6 address (if present),
+ name and MTU of the IP interface that received the datagram
+ which elicited the ICMP error.
+ ==== ==============================================================
+
+ Default: 0x00 (no extensions)
+
xfrm6_gc_thresh - INTEGER
(Obsolete since linux-4.14)
The threshold at which we will start garbage collecting for IPv6
diff --git a/Documentation/networking/napi.rst b/Documentation/networking/napi.rst
index 7dd60366f4ff..4e008efebb35 100644
--- a/Documentation/networking/napi.rst
+++ b/Documentation/networking/napi.rst
@@ -263,7 +263,9 @@ are not well known).
Busy polling is enabled by either setting ``SO_BUSY_POLL`` on
selected sockets or using the global ``net.core.busy_poll`` and
``net.core.busy_read`` sysctls. An io_uring API for NAPI busy polling
-also exists.
+also exists. Threaded polling of NAPI also has a mode to busy poll for
+packets (:ref:`threaded busy polling<threaded_busy_poll>`) using the NAPI
+processing kthread.
epoll-based busy polling
------------------------
@@ -426,6 +428,52 @@ Therefore, setting ``gro_flush_timeout`` and ``napi_defer_hard_irqs`` is
the recommended usage, because otherwise setting ``irq-suspend-timeout``
might not have any discernible effect.
+.. _threaded_busy_poll:
+
+Threaded NAPI busy polling
+--------------------------
+
+Threaded NAPI busy polling extends threaded NAPI and adds support to do
+continuous busy polling of the NAPI. This can be useful for forwarding or
+AF_XDP applications.
+
+Threaded NAPI busy polling can be enabled on per NIC queue basis using Netlink.
+
+For example, using the following script:
+
+.. code-block:: bash
+
+ $ ynl --family netdev --do napi-set \
+ --json='{"id": 66, "threaded": "busy-poll"}'
+
+The kernel will create a kthread that busy polls on this NAPI.
+
+The user may elect to set the CPU affinity of this kthread to an unused CPU
+core to improve how often the NAPI is polled at the expense of wasted CPU
+cycles. Note that this will keep the CPU core busy with 100% usage.
+
+Once threaded busy polling is enabled for a NAPI, PID of the kthread can be
+retrieved using Netlink so the affinity of the kthread can be set up.
+
+For example, the following script can be used to fetch the PID:
+
+.. code-block:: bash
+
+ $ ynl --family netdev --do napi-get --json='{"id": 66}'
+
+This will output something like following, the pid `258` is the PID of the
+kthread that is polling this NAPI.
+
+.. code-block:: bash
+
+ $ {'defer-hard-irqs': 0,
+ 'gro-flush-timeout': 0,
+ 'id': 66,
+ 'ifindex': 2,
+ 'irq-suspend-timeout': 0,
+ 'pid': 258,
+ 'threaded': 'busy-poll'}
+
.. _threaded:
Threaded NAPI
diff --git a/Documentation/networking/net_cachelines/inet_connection_sock.rst b/Documentation/networking/net_cachelines/inet_connection_sock.rst
index 8fae85ebb773..cc2000f55c29 100644
--- a/Documentation/networking/net_cachelines/inet_connection_sock.rst
+++ b/Documentation/networking/net_cachelines/inet_connection_sock.rst
@@ -12,8 +12,8 @@ struct inet_sock icsk_inet read_mostly r
struct request_sock_queue icsk_accept_queue
struct inet_bind_bucket icsk_bind_hash read_mostly tcp_set_state
struct inet_bind2_bucket icsk_bind2_hash read_mostly tcp_set_state,inet_put_port
-struct timer_list icsk_retransmit_timer read_write inet_csk_reset_xmit_timer,tcp_connect
struct timer_list icsk_delack_timer read_mostly inet_csk_reset_xmit_timer,tcp_connect
+struct timer_list icsk_keepalive_timer
u32 icsk_rto read_write tcp_cwnd_validate,tcp_schedule_loss_probe,tcp_connect_init,tcp_connect,tcp_write_xmit,tcp_push_one
u32 icsk_rto_min
u32 icsk_rto_max read_mostly tcp_reset_xmit_timer
diff --git a/Documentation/networking/net_cachelines/inet_sock.rst b/Documentation/networking/net_cachelines/inet_sock.rst
index b11bf48fa2b3..4c72a28a7012 100644
--- a/Documentation/networking/net_cachelines/inet_sock.rst
+++ b/Documentation/networking/net_cachelines/inet_sock.rst
@@ -5,42 +5,43 @@
inet_sock struct fast path usage breakdown
==========================================
-======================= ===================== =================== =================== ======================================================================================================
-Type Name fastpath_tx_access fastpath_rx_access comment
-======================= ===================== =================== =================== ======================================================================================================
-struct sock sk read_mostly read_mostly tcp_init_buffer_space,tcp_init_transfer,tcp_finish_connect,tcp_connect,tcp_send_rcvq,tcp_send_syn_data
-struct ipv6_pinfo* pinet6
-be16 inet_sport read_mostly __tcp_transmit_skb
-be32 inet_daddr read_mostly ip_select_ident_segs
-be32 inet_rcv_saddr
-be16 inet_dport read_mostly __tcp_transmit_skb
-u16 inet_num
-be32 inet_saddr
-s16 uc_ttl read_mostly __ip_queue_xmit/ip_select_ttl
-u16 cmsg_flags
-struct ip_options_rcu* inet_opt read_mostly __ip_queue_xmit
-u16 inet_id read_mostly ip_select_ident_segs
-u8 tos read_mostly ip_queue_xmit
-u8 min_ttl
-u8 mc_ttl
-u8 pmtudisc
-u8:1 recverr
-u8:1 is_icsk
-u8:1 freebind
-u8:1 hdrincl
-u8:1 mc_loop
-u8:1 transparent
-u8:1 mc_all
-u8:1 nodefrag
-u8:1 bind_address_no_port
-u8:1 recverr_rfc4884
-u8:1 defer_connect read_mostly tcp_sendmsg_fastopen
-u8 rcv_tos
-u8 convert_csum
-int uc_index
-int mc_index
-be32 mc_addr
-struct ip_mc_socklist* mc_list
-struct inet_cork_full cork read_mostly __tcp_transmit_skb
-struct local_port_range
-======================= ===================== =================== =================== ======================================================================================================
+======================== ===================== =================== =================== ======================================================================================================
+Type Name fastpath_tx_access fastpath_rx_access comment
+======================== ===================== =================== =================== ======================================================================================================
+struct sock sk read_mostly read_mostly tcp_init_buffer_space,tcp_init_transfer,tcp_finish_connect,tcp_connect,tcp_send_rcvq,tcp_send_syn_data
+struct ipv6_pinfo* pinet6
+struct ipv6_fl_socklist* ipv6_fl_list read_mostly tcp_v6_connect,__ip6_datagram_connect,udpv6_sendmsg,rawv6_sendmsg
+be16 inet_sport read_mostly __tcp_transmit_skb
+be32 inet_daddr read_mostly ip_select_ident_segs
+be32 inet_rcv_saddr
+be16 inet_dport read_mostly __tcp_transmit_skb
+u16 inet_num
+be32 inet_saddr
+s16 uc_ttl read_mostly __ip_queue_xmit/ip_select_ttl
+u16 cmsg_flags
+struct ip_options_rcu* inet_opt read_mostly __ip_queue_xmit
+u16 inet_id read_mostly ip_select_ident_segs
+u8 tos read_mostly ip_queue_xmit
+u8 min_ttl
+u8 mc_ttl
+u8 pmtudisc
+u8:1 recverr
+u8:1 is_icsk
+u8:1 freebind
+u8:1 hdrincl
+u8:1 mc_loop
+u8:1 transparent
+u8:1 mc_all
+u8:1 nodefrag
+u8:1 bind_address_no_port
+u8:1 recverr_rfc4884
+u8:1 defer_connect read_mostly tcp_sendmsg_fastopen
+u8 rcv_tos
+u8 convert_csum
+int uc_index
+int mc_index
+be32 mc_addr
+struct ip_mc_socklist* mc_list
+struct inet_cork_full cork read_mostly __tcp_transmit_skb
+struct local_port_range
+======================== ===================== =================== =================== ======================================================================================================
diff --git a/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst b/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst
index 6e7b20afd2d4..beaf1880a19b 100644
--- a/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst
+++ b/Documentation/networking/net_cachelines/netns_ipv4_sysctl.rst
@@ -102,7 +102,8 @@ u8 sysctl_tcp_app_win
u8 sysctl_tcp_frto tcp_enter_loss
u8 sysctl_tcp_nometrics_save TCP_LAST_ACK/tcp_update_metrics
u8 sysctl_tcp_no_ssthresh_metrics_save TCP_LAST_ACK/tcp_(update/init)_metrics
-u8 sysctl_tcp_moderate_rcvbuf read_mostly read_mostly tcp_tso_should_defer(tx);tcp_rcv_space_adjust(rx)
+u8 sysctl_tcp_moderate_rcvbuf read_mostly tcp_rcvbuf_grow()
+u32 sysctl_tcp_rcvbuf_low_rtt read_mostly tcp_rcvbuf_grow()
u8 sysctl_tcp_tso_win_divisor read_mostly tcp_tso_should_defer(tcp_write_xmit)
u8 sysctl_tcp_workaround_signed_windows tcp_select_window
int sysctl_tcp_limit_output_bytes read_mostly tcp_small_queue_check(tcp_write_xmit)
diff --git a/Documentation/networking/net_failover.rst b/Documentation/networking/net_failover.rst
index f4e1b4e07adc..2f776e90d318 100644
--- a/Documentation/networking/net_failover.rst
+++ b/Documentation/networking/net_failover.rst
@@ -96,9 +96,8 @@ needed to these network configuration daemons to make sure that an IP is
received only on the 'failover' device.
Below is the patch snippet used with 'cloud-ifupdown-helper' script found on
-Debian cloud images:
+Debian cloud images::
-::
@@ -27,6 +27,8 @@ do_setup() {
local working="$cfgdir/.$INTERFACE"
local final="$cfgdir/$INTERFACE"
@@ -172,9 +171,8 @@ appropriate FDB entry is added.
The following script is executed on the destination hypervisor once migration
completes, and it reattaches the VF to the VM and brings down the virtio-net
-interface.
+interface::
-::
# reattach-vf.sh
#!/bin/bash
diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
index 59cb9982afe6..4ab5d7b05cf1 100644
--- a/Documentation/networking/netconsole.rst
+++ b/Documentation/networking/netconsole.rst
@@ -19,9 +19,6 @@ Userdata append support by Matthew Wood <thepacketgeek@gmail.com>, Jan 22 2024
Sysdata append support by Breno Leitao <leitao@debian.org>, Jan 15 2025
-Please send bug reports to Matt Mackall <mpm@selenic.com>
-Satyam Sharma <satyam.sharma@gmail.com>, and Cong Wang <xiyou.wangcong@gmail.com>
-
Introduction:
=============
@@ -91,7 +88,7 @@ for example:
nc -u -l -p <port>' / 'nc -u -l <port>
- or::
+ or::
netcat -u -l -p <port>' / 'netcat -u -l <port>
diff --git a/Documentation/networking/nfc.rst b/Documentation/networking/nfc.rst
index 9aab3a88c9b2..401735006143 100644
--- a/Documentation/networking/nfc.rst
+++ b/Documentation/networking/nfc.rst
@@ -71,7 +71,8 @@ Userspace interface
The userspace interface is divided in control operations and low-level data
exchange operation.
-CONTROL OPERATIONS:
+Control operations
+------------------
Generic netlink is used to implement the interface to the control operations.
The operations are composed by commands and events, all listed below:
@@ -100,7 +101,8 @@ relevant information such as the supported NFC protocols.
All polling operations requested through one netlink socket are stopped when
it's closed.
-LOW-LEVEL DATA EXCHANGE:
+Low-level data exchange
+-----------------------
The userspace must use PF_NFC sockets to perform any data communication with
targets. All NFC sockets use AF_NFC::
diff --git a/Documentation/networking/smc-sysctl.rst b/Documentation/networking/smc-sysctl.rst
index a874d007f2db..904a910f198e 100644
--- a/Documentation/networking/smc-sysctl.rst
+++ b/Documentation/networking/smc-sysctl.rst
@@ -71,3 +71,43 @@ smcr_max_conns_per_lgr - INTEGER
acceptable value ranges from 16 to 255. Only for SMC-R v2.1 and later.
Default: 255
+
+smcr_max_send_wr - INTEGER
+ So-called work request buffers are SMCR link (and RDMA queue pair) level
+ resources necessary for performing RDMA operations. Since up to 255
+ connections can share a link group and thus also a link and the number
+ of the work request buffers is decided when the link is allocated,
+ depending on the workload it can be a bottleneck in a sense that threads
+ have to wait for work request buffers to become available. Before the
+ introduction of this control the maximal number of work request buffers
+ available on the send path used to be hard coded to 16. With this control
+ it becomes configurable. The acceptable range is between 2 and 2048.
+
+ Please be aware that all the buffers need to be allocated as a physically
+ continuous array in which each element is a single buffer and has the size
+ of SMC_WR_BUF_SIZE (48) bytes. If the allocation fails, we keep retrying
+ with half of the buffer count until it is ether successful or (unlikely)
+ we dip below the old hard coded value which is 16 where we give up much
+ like before having this control.
+
+ Default: 16
+
+smcr_max_recv_wr - INTEGER
+ So-called work request buffers are SMCR link (and RDMA queue pair) level
+ resources necessary for performing RDMA operations. Since up to 255
+ connections can share a link group and thus also a link and the number
+ of the work request buffers is decided when the link is allocated,
+ depending on the workload it can be a bottleneck in a sense that threads
+ have to wait for work request buffers to become available. Before the
+ introduction of this control the maximal number of work request buffers
+ available on the receive path used to be hard coded to 16. With this control
+ it becomes configurable. The acceptable range is between 2 and 2048.
+
+ Please be aware that all the buffers need to be allocated as a physically
+ continuous array in which each element is a single buffer and has the size
+ of SMC_WR_BUF_SIZE (48) bytes. If the allocation fails, we keep retrying
+ with half of the buffer count until it is ether successful or (unlikely)
+ we dip below the old hard coded value which is 16 where we give up much
+ like before having this control.
+
+ Default: 48
diff --git a/Documentation/networking/statistics.rst b/Documentation/networking/statistics.rst
index 518284e287b0..66b0ef941457 100644
--- a/Documentation/networking/statistics.rst
+++ b/Documentation/networking/statistics.rst
@@ -184,9 +184,11 @@ Protocol-related statistics can be requested in get commands by setting
the `ETHTOOL_FLAG_STATS` flag in `ETHTOOL_A_HEADER_FLAGS`. Currently
statistics are supported in the following commands:
- - `ETHTOOL_MSG_PAUSE_GET`
- `ETHTOOL_MSG_FEC_GET`
+ - `ETHTOOL_MSG_LINKSTATE_GET`
- `ETHTOOL_MSG_MM_GET`
+ - `ETHTOOL_MSG_PAUSE_GET`
+ - `ETHTOOL_MSG_TSINFO_GET`
debugfs
-------
diff --git a/Documentation/networking/tls.rst b/Documentation/networking/tls.rst
index 36cc7afc2527..980c442d7161 100644
--- a/Documentation/networking/tls.rst
+++ b/Documentation/networking/tls.rst
@@ -280,6 +280,26 @@ If the record decrypted turns out to had been padded or is not a data
record it will be decrypted again into a kernel buffer without zero copy.
Such events are counted in the ``TlsDecryptRetry`` statistic.
+TLS_TX_MAX_PAYLOAD_LEN
+~~~~~~~~~~~~~~~~~~~~~~
+
+Specifies the maximum size of the plaintext payload for transmitted TLS records.
+
+When this option is set, the kernel enforces the specified limit on all outgoing
+TLS records. No plaintext fragment will exceed this size. This option can be used
+to implement the TLS Record Size Limit extension [1].
+
+* For TLS 1.2, the value corresponds directly to the record size limit.
+* For TLS 1.3, the value should be set to record_size_limit - 1, since
+ the record size limit includes one additional byte for the ContentType
+ field.
+
+The valid range for this option is 64 to 16384 bytes for TLS 1.2, and 63 to
+16384 bytes for TLS 1.3. The lower minimum for TLS 1.3 accounts for the
+extra byte used by the ContentType field.
+
+[1] https://datatracker.ietf.org/doc/html/rfc8449
+
Statistics
==========
diff --git a/Documentation/networking/xfrm/index.rst b/Documentation/networking/xfrm/index.rst
new file mode 100644
index 000000000000..7d866da836fe
--- /dev/null
+++ b/Documentation/networking/xfrm/index.rst
@@ -0,0 +1,13 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==============
+XFRM Framework
+==============
+
+.. toctree::
+ :maxdepth: 2
+
+ xfrm_device
+ xfrm_proc
+ xfrm_sync
+ xfrm_sysctl
diff --git a/Documentation/networking/xfrm_device.rst b/Documentation/networking/xfrm/xfrm_device.rst
index 122204da0fff..b0d85a5f57d1 100644
--- a/Documentation/networking/xfrm_device.rst
+++ b/Documentation/networking/xfrm/xfrm_device.rst
@@ -20,11 +20,15 @@ can radically increase throughput and decrease CPU utilization. The XFRM
Device interface allows NIC drivers to offer to the stack access to the
hardware offload.
-Right now, there are two types of hardware offload that kernel supports.
+Right now, there are two types of hardware offload that kernel supports:
+
* IPsec crypto offload:
+
* NIC performs encrypt/decrypt
* Kernel does everything else
+
* IPsec packet offload:
+
* NIC performs encrypt/decrypt
* NIC does encapsulation
* Kernel and NIC have SA and policy in-sync
@@ -34,7 +38,7 @@ Right now, there are two types of hardware offload that kernel supports.
Userland access to the offload is typically through a system such as
libreswan or KAME/raccoon, but the iproute2 'ip xfrm' command set can
be handy when experimenting. An example command might look something
-like this for crypto offload:
+like this for crypto offload::
ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport \
reqid 0x07 replay-window 32 \
@@ -42,7 +46,7 @@ like this for crypto offload:
sel src 14.0.0.52/24 dst 14.0.0.70/24 proto tcp \
offload dev eth4 dir in
-and for packet offload
+and for packet offload::
ip x s add proto esp dst 14.0.0.70 src 14.0.0.52 spi 0x07 mode transport \
reqid 0x07 replay-window 32 \
@@ -153,26 +157,26 @@ the packet's skb. At this point the data should be decrypted but the
IPsec headers are still in the packet data; they are removed later up
the stack in xfrm_input().
- find and hold the SA that was used to the Rx skb::
+1. Find and hold the SA that was used to the Rx skb::
- get spi, protocol, and destination IP from packet headers
+ /* get spi, protocol, and destination IP from packet headers */
xs = find xs from (spi, protocol, dest_IP)
xfrm_state_hold(xs);
- store the state information into the skb::
+2. Store the state information into the skb::
sp = secpath_set(skb);
if (!sp) return;
sp->xvec[sp->len++] = xs;
sp->olen++;
- indicate the success and/or error status of the offload::
+3. Indicate the success and/or error status of the offload::
xo = xfrm_offload(skb);
xo->flags = CRYPTO_DONE;
xo->status = crypto_status;
- hand the packet to napi_gro_receive() as usual
+4. Hand the packet to napi_gro_receive() as usual.
In ESN mode, xdo_dev_state_advance_esn() is called from
xfrm_replay_advance_esn() for RX, and xfrm_replay_overflow_offload_esn for TX.
diff --git a/Documentation/networking/xfrm_proc.rst b/Documentation/networking/xfrm/xfrm_proc.rst
index 973d1571acac..973d1571acac 100644
--- a/Documentation/networking/xfrm_proc.rst
+++ b/Documentation/networking/xfrm/xfrm_proc.rst
diff --git a/Documentation/networking/xfrm_sync.rst b/Documentation/networking/xfrm/xfrm_sync.rst
index 6246503ceab2..dfc2ec0df380 100644
--- a/Documentation/networking/xfrm_sync.rst
+++ b/Documentation/networking/xfrm/xfrm_sync.rst
@@ -1,8 +1,8 @@
.. SPDX-License-Identifier: GPL-2.0
-====
-XFRM
-====
+=========
+XFRM sync
+=========
The sync patches work is based on initial patches from
Krisztian <hidden@balabit.hu> and others and additional patches
@@ -36,7 +36,7 @@ is not driven by packet arrival.
- the replay sequence for both inbound and outbound
1) Message Structure
-----------------------
+--------------------
nlmsghdr:aevent_id:optional-TLVs.
@@ -83,31 +83,31 @@ when going from kernel to user space)
A program needs to subscribe to multicast group XFRMNLGRP_AEVENTS
to get notified of these events.
-2) TLVS reflect the different parameters:
------------------------------------------
+2) TLVS reflect the different parameters
+----------------------------------------
a) byte value (XFRMA_LTIME_VAL)
-This TLV carries the running/current counter for byte lifetime since
-last event.
+ This TLV carries the running/current counter for byte lifetime since
+ last event.
-b)replay value (XFRMA_REPLAY_VAL)
+b) replay value (XFRMA_REPLAY_VAL)
-This TLV carries the running/current counter for replay sequence since
-last event.
+ This TLV carries the running/current counter for replay sequence since
+ last event.
-c)replay threshold (XFRMA_REPLAY_THRESH)
+c) replay threshold (XFRMA_REPLAY_THRESH)
-This TLV carries the threshold being used by the kernel to trigger events
-when the replay sequence is exceeded.
+ This TLV carries the threshold being used by the kernel to trigger events
+ when the replay sequence is exceeded.
d) expiry timer (XFRMA_ETIMER_THRESH)
-This is a timer value in milliseconds which is used as the nagle
-value to rate limit the events.
+ This is a timer value in milliseconds which is used as the nagle
+ value to rate limit the events.
-3) Default configurations for the parameters:
----------------------------------------------
+3) Default configurations for the parameters
+--------------------------------------------
By default these events should be turned off unless there is
at least one listener registered to listen to the multicast
@@ -121,12 +121,14 @@ in case they are not specified.
the two sysctls/proc entries are:
a) /proc/sys/net/core/sysctl_xfrm_aevent_etime
-used to provide default values for the XFRMA_ETIMER_THRESH in incremental
-units of time of 100ms. The default is 10 (1 second)
+
+ Used to provide default values for the XFRMA_ETIMER_THRESH in incremental
+ units of time of 100ms. The default is 10 (1 second)
b) /proc/sys/net/core/sysctl_xfrm_aevent_rseqth
-used to provide default values for XFRMA_REPLAY_THRESH parameter
-in incremental packet count. The default is two packets.
+
+ Used to provide default values for XFRMA_REPLAY_THRESH parameter
+ in incremental packet count. The default is two packets.
4) Message types
----------------
@@ -134,50 +136,51 @@ in incremental packet count. The default is two packets.
a) XFRM_MSG_GETAE issued by user-->kernel.
XFRM_MSG_GETAE does not carry any TLVs.
-The response is a XFRM_MSG_NEWAE which is formatted based on what
-XFRM_MSG_GETAE queried for.
+ The response is a XFRM_MSG_NEWAE which is formatted based on what
+ XFRM_MSG_GETAE queried for.
+
+ The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-* if XFRM_AE_RTHR flag is set, then XFRMA_REPLAY_THRESH is also retrieved
-* if XFRM_AE_ETHR flag is set, then XFRMA_ETIMER_THRESH is also retrieved
+ * if XFRM_AE_RTHR flag is set, then XFRMA_REPLAY_THRESH is also retrieved
+ * if XFRM_AE_ETHR flag is set, then XFRMA_ETIMER_THRESH is also retrieved
b) XFRM_MSG_NEWAE is issued by either user space to configure
or kernel to announce events or respond to a XFRM_MSG_GETAE.
-i) user --> kernel to configure a specific SA.
+ i) user --> kernel to configure a specific SA.
-any of the values or threshold parameters can be updated by passing the
-appropriate TLV.
+ any of the values or threshold parameters can be updated by passing the
+ appropriate TLV.
-A response is issued back to the sender in user space to indicate success
-or failure.
+ A response is issued back to the sender in user space to indicate success
+ or failure.
-In the case of success, additionally an event with
-XFRM_MSG_NEWAE is also issued to any listeners as described in iii).
+ In the case of success, additionally an event with
+ XFRM_MSG_NEWAE is also issued to any listeners as described in iii).
-ii) kernel->user direction as a response to XFRM_MSG_GETAE
+ ii) kernel->user direction as a response to XFRM_MSG_GETAE
-The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
+ The response will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-The threshold TLVs will be included if explicitly requested in
-the XFRM_MSG_GETAE message.
+ The threshold TLVs will be included if explicitly requested in
+ the XFRM_MSG_GETAE message.
-iii) kernel->user to report as event if someone sets any values or
- thresholds for an SA using XFRM_MSG_NEWAE (as described in #i above).
- In such a case XFRM_AE_CU flag is set to inform the user that
- the change happened as a result of an update.
- The message will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
+ iii) kernel->user to report as event if someone sets any values or
+ thresholds for an SA using XFRM_MSG_NEWAE (as described in #i above).
+ In such a case XFRM_AE_CU flag is set to inform the user that
+ the change happened as a result of an update.
+ The message will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-iv) kernel->user to report event when replay threshold or a timeout
- is exceeded.
+ iv) kernel->user to report event when replay threshold or a timeout
+ is exceeded.
In such a case either XFRM_AE_CR (replay exceeded) or XFRM_AE_CE (timeout
happened) is set to inform the user what happened.
Note the two flags are mutually exclusive.
The message will always have XFRMA_LTIME_VAL and XFRMA_REPLAY_VAL TLVs.
-Exceptions to threshold settings
---------------------------------
+5) Exceptions to threshold settings
+-----------------------------------
If you have an SA that is getting hit by traffic in bursts such that
there is a period where the timer threshold expires with no packets
diff --git a/Documentation/networking/xfrm_sysctl.rst b/Documentation/networking/xfrm/xfrm_sysctl.rst
index 47b9bbdd0179..7d0c4b17c0bd 100644
--- a/Documentation/networking/xfrm_sysctl.rst
+++ b/Documentation/networking/xfrm/xfrm_sysctl.rst
@@ -4,8 +4,8 @@
XFRM Syscall
============
-/proc/sys/net/core/xfrm_* Variables:
-====================================
+/proc/sys/net/core/xfrm_* Variables
+===================================
xfrm_acq_expires - INTEGER
default 30 - hard timeout in seconds for acquire requests
diff --git a/Documentation/power/index.rst b/Documentation/power/index.rst
index a0f5244fb427..ea70633d9ce6 100644
--- a/Documentation/power/index.rst
+++ b/Documentation/power/index.rst
@@ -19,6 +19,7 @@ Power Management
power_supply_class
runtime_pm
s2ram
+ shutdown-debugging
suspend-and-cpuhotplug
suspend-and-interrupts
swsusp-and-swap-files
diff --git a/Documentation/power/pm_qos_interface.rst b/Documentation/power/pm_qos_interface.rst
index 5019c79c7710..4c008e2202f0 100644
--- a/Documentation/power/pm_qos_interface.rst
+++ b/Documentation/power/pm_qos_interface.rst
@@ -55,7 +55,8 @@ int cpu_latency_qos_request_active(handle):
From user space:
-The infrastructure exposes one device node, /dev/cpu_dma_latency, for the CPU
+The infrastructure exposes two separate device nodes, /dev/cpu_dma_latency for
+the CPU latency QoS and /dev/cpu_wakeup_latency for the CPU system wakeup
latency QoS.
Only processes can register a PM QoS request. To provide for automatic
@@ -63,15 +64,15 @@ cleanup of a process, the interface requires the process to register its
parameter requests as follows.
To register the default PM QoS target for the CPU latency QoS, the process must
-open /dev/cpu_dma_latency.
+open /dev/cpu_dma_latency. To register a CPU system wakeup QoS limit, the
+process must open /dev/cpu_wakeup_latency.
As long as the device node is held open that process has a registered
request on the parameter.
To change the requested target value, the process needs to write an s32 value to
the open device node. Alternatively, it can write a hex string for the value
-using the 10 char long format e.g. "0x12345678". This translates to a
-cpu_latency_qos_update_request() call.
+using the 10 char long format e.g. "0x12345678".
To remove the user mode request for a target value simply close the device
node.
diff --git a/Documentation/power/power_supply_class.rst b/Documentation/power/power_supply_class.rst
index da8e275a14ff..6d11f8c594a0 100644
--- a/Documentation/power/power_supply_class.rst
+++ b/Documentation/power/power_supply_class.rst
@@ -7,35 +7,35 @@ Synopsis
Power supply class used to represent battery, UPS, AC or DC power supply
properties to user-space.
-It defines core set of attributes, which should be applicable to (almost)
+It defines a core set of attributes which should be applicable to (almost)
every power supply out there. Attributes are available via sysfs and uevent
interfaces.
-Each attribute has well defined meaning, up to unit of measure used. While
+Each attribute has a well-defined meaning, up to the unit of measure used. While
the attributes provided are believed to be universally applicable to any
power supply, specific monitoring hardware may not be able to provide them
all, so any of them may be skipped.
-Power supply class is extensible, and allows to define drivers own attributes.
-The core attribute set is subject to the standard Linux evolution (i.e.
-if it will be found that some attribute is applicable to many power supply
-types or their drivers, it can be added to the core set).
+The power supply class is extensible and allows drivers to define their own
+attributes. The core attribute set is subject to the standard Linux evolution
+(i.e., if some attribute is found to be applicable to many power
+supply types or their drivers, it can be added to the core set).
-It also integrates with LED framework, for the purpose of providing
+It also integrates with the LED framework, for the purpose of providing
typically expected feedback of battery charging/fully charged status and
AC/USB power supply online status. (Note that specific details of the
indication (including whether to use it at all) are fully controllable by
-user and/or specific machine defaults, per design principles of LED
-framework).
+user and/or specific machine defaults, per design principles of the LED
+framework.)
Attributes/properties
~~~~~~~~~~~~~~~~~~~~~
-Power supply class has predefined set of attributes, this eliminates code
-duplication across drivers. Power supply class insist on reusing its
+The power supply class has a predefined set of attributes. This eliminates code
+duplication across drivers. The power supply class insists on reusing its
predefined attributes *and* their units.
-So, userspace gets predictable set of attributes and their units for any
+So, userspace gets a predictable set of attributes and their units for any
kind of power supply, and can process/present them to a user in consistent
manner. Results for different power supplies and machines are also directly
comparable.
@@ -61,7 +61,7 @@ Attributes/properties detailed
| **Charge/Energy/Capacity - how to not confuse** |
+--------------------------------------------------------------------------+
| **Because both "charge" (µAh) and "energy" (µWh) represents "capacity" |
-| of battery, this class distinguish these terms. Don't mix them!** |
+| of battery, this class distinguishes these terms. Don't mix them!** |
| |
| - `CHARGE_*` |
| attributes represents capacity in µAh only. |
@@ -81,7 +81,7 @@ _NOW
STATUS
this attribute represents operating status (charging, full,
- discharging (i.e. powering a load), etc.). This corresponds to
+ discharging (i.e., powering a load), etc.). This corresponds to
`BATTERY_STATUS_*` values, as defined in battery.h.
CHARGE_TYPE
@@ -92,10 +92,10 @@ CHARGE_TYPE
AUTHENTIC
indicates the power supply (battery or charger) connected
- to the platform is authentic(1) or non authentic(0).
+ to the platform is authentic(1) or non-authentic(0).
HEALTH
- represents health of the battery, values corresponds to
+ represents health of the battery. Values corresponds to
POWER_SUPPLY_HEALTH_*, defined in battery.h.
VOLTAGE_OCV
@@ -103,11 +103,11 @@ VOLTAGE_OCV
VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN
design values for maximal and minimal power supply voltages.
- Maximal/minimal means values of voltages when battery considered
+ Maximal/minimal means values of voltages when battery is considered
"full"/"empty" at normal conditions. Yes, there is no direct relation
between voltage and battery capacity, but some dumb
batteries use voltage for very approximated calculation of capacity.
- Battery driver also can use this attribute just to inform userspace
+ A battery driver also can use this attribute just to inform userspace
about maximal and minimal voltage thresholds of a given battery.
VOLTAGE_MAX, VOLTAGE_MIN
@@ -122,16 +122,16 @@ CURRENT_BOOT
Reports the current measured during boot
CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN
- design charge values, when battery considered full/empty.
+ design charge values, when battery is considered full/empty.
ENERGY_FULL_DESIGN, ENERGY_EMPTY_DESIGN
same as above but for energy.
CHARGE_FULL, CHARGE_EMPTY
- These attributes means "last remembered value of charge when battery
- became full/empty". It also could mean "value of charge when battery
+ These attributes mean "last remembered value of charge when battery
+ became full/empty". They also could mean "value of charge when battery is
considered full/empty at given conditions (temperature, age)".
- I.e. these attributes represents real thresholds, not design values.
+ I.e., these attributes represents real thresholds, not design values.
ENERGY_FULL, ENERGY_EMPTY
same as above but for energy.
@@ -153,12 +153,12 @@ CHARGE_TERM_CURRENT
CONSTANT_CHARGE_CURRENT
constant charge current programmed by charger.
-
CONSTANT_CHARGE_CURRENT_MAX
maximum charge current supported by the power supply object.
CONSTANT_CHARGE_VOLTAGE
constant charge voltage programmed by charger.
+
CONSTANT_CHARGE_VOLTAGE_MAX
maximum charge voltage supported by the power supply object.
@@ -208,10 +208,10 @@ TEMP_MAX
TIME_TO_EMPTY
seconds left for battery to be considered empty
- (i.e. while battery powers a load)
+ (i.e., while battery powers a load)
TIME_TO_FULL
seconds left for battery to be considered full
- (i.e. while battery is charging)
+ (i.e., while battery is charging)
Battery <-> external power supply interaction
@@ -220,13 +220,13 @@ Often power supplies are acting as supplies and supplicants at the same
time. Batteries are good example. So, batteries usually care if they're
externally powered or not.
-For that case, power supply class implements notification mechanism for
+For that case, the power supply class implements a notification mechanism for
batteries.
-External power supply (AC) lists supplicants (batteries) names in
+An external power supply (AC) lists supplicants (batteries) names in
"supplied_to" struct member, and each power_supply_changed() call
-issued by external power supply will notify supplicants via
-external_power_changed callback.
+issued by an external power supply will notify supplicants via
+the external_power_changed callback.
Devicetree battery characteristics
@@ -241,14 +241,14 @@ battery node have names corresponding to elements in enum power_supply_property,
for naming consistency between sysfs attributes and battery node properties.
-QA
-~~
+Q&A
+~~~
Q:
Where is POWER_SUPPLY_PROP_XYZ attribute?
A:
- If you cannot find attribute suitable for your driver needs, feel free
- to add it and send patch along with your driver.
+ If you cannot find an attribute suitable for your driver needs, feel free
+ to add it and send a patch along with your driver.
The attributes available currently are the ones currently provided by the
drivers written.
@@ -258,18 +258,18 @@ A:
Q:
- I have some very specific attribute (e.g. battery color), should I add
+ I have some very specific attribute (e.g., battery color). Should I add
this attribute to standard ones?
A:
Most likely, no. Such attribute can be placed in the driver itself, if
- it is useful. Of course, if the attribute in question applicable to
- large set of batteries, provided by many drivers, and/or comes from
+ it is useful. Of course, if the attribute in question is applicable to
+ a large set of batteries, provided by many drivers, and/or comes from
some general battery specification/standard, it may be a candidate to
be added to the core attribute set.
Q:
- Suppose, my battery monitoring chip/firmware does not provides capacity
+ Suppose my battery monitoring chip/firmware does not provide capacity
in percents, but provides charge_{now,full,empty}. Should I calculate
percentage capacity manually, inside the driver, and register CAPACITY
attribute? The same question about time_to_empty/time_to_full.
@@ -278,11 +278,11 @@ A:
directly measurable by the specific hardware available.
Inferring not available properties using some heuristics or mathematical
- model is not subject of work for a battery driver. Such functionality
+ model is not a subject of work for a battery driver. Such functionality
should be factored out, and in fact, apm_power, the driver to serve
- legacy APM API on top of power supply class, uses a simple heuristic of
+ legacy APM API on top of the power supply class, uses a simple heuristic of
approximating remaining battery capacity based on its charge, current,
- voltage and so on. But full-fledged battery model is likely not subject
- for kernel at all, as it would require floating point calculation to deal
- with things like differential equations and Kalman filters. This is
+ voltage and so on. But a full-fledged battery model is likely not a subject
+ for the kernel at all, as it would require floating point calculations to
+ deal with things like differential equations and Kalman filters. This is
better be handled by batteryd/libbattery, yet to be written.
diff --git a/Documentation/power/runtime_pm.rst b/Documentation/power/runtime_pm.rst
index c8dbdb8595e5..8246df3cecd7 100644
--- a/Documentation/power/runtime_pm.rst
+++ b/Documentation/power/runtime_pm.rst
@@ -480,16 +480,6 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
`bool pm_runtime_status_suspended(struct device *dev);`
- return true if the device's runtime PM status is 'suspended'
- `void pm_runtime_allow(struct device *dev);`
- - set the power.runtime_auto flag for the device and decrease its usage
- counter (used by the /sys/devices/.../power/control interface to
- effectively allow the device to be power managed at run time)
-
- `void pm_runtime_forbid(struct device *dev);`
- - unset the power.runtime_auto flag for the device and increase its usage
- counter (used by the /sys/devices/.../power/control interface to
- effectively prevent the device from being power managed at run time)
-
`void pm_runtime_no_callbacks(struct device *dev);`
- set the power.no_callbacks flag for the device and remove the runtime
PM attributes from /sys/devices/.../power (or prevent them from being
diff --git a/Documentation/power/shutdown-debugging.rst b/Documentation/power/shutdown-debugging.rst
new file mode 100644
index 000000000000..c510122e0bbc
--- /dev/null
+++ b/Documentation/power/shutdown-debugging.rst
@@ -0,0 +1,53 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Debugging Kernel Shutdown Hangs with pstore
++++++++++++++++++++++++++++++++++++++++++++
+
+Overview
+========
+If the system hangs while shutting down, the kernel logs may need to be
+retrieved to debug the issue.
+
+On systems that have a UART available, it is best to configure the kernel to use
+this UART for kernel console output.
+
+If a UART isn't available, the ``pstore`` subsystem provides a mechanism to
+persist this data across a system reset, allowing it to be retrieved on the next
+boot.
+
+Kernel Configuration
+====================
+To enable ``pstore`` and enable saving kernel ring buffer logs, set the
+following kernel configuration options:
+
+* ``CONFIG_PSTORE=y``
+* ``CONFIG_PSTORE_CONSOLE=y``
+
+Additionally, enable a backend to store the data. Depending upon your platform
+some potential options include:
+
+* ``CONFIG_EFI_VARS_PSTORE=y``
+* ``CONFIG_PSTORE_RAM=y``
+* ``CONFIG_CHROMEOS_PSTORE=y``
+* ``CONFIG_PSTORE_BLK=y``
+
+Kernel Command-line Parameters
+==============================
+Add these parameters to your kernel command line:
+
+* ``printk.always_kmsg_dump=Y``
+ * Forces the kernel to dump the entire message buffer to pstore during
+ shutdown
+* ``efi_pstore.pstore_disable=N``
+ * For EFI-based systems, ensures the EFI backend is active
+
+Userspace Interaction and Log Retrieval
+=======================================
+On the next boot after a hang, pstore logs will be available in the pstore
+filesystem (``/sys/fs/pstore``) and can be retrieved by userspace.
+
+On systemd systems, the ``systemd-pstore`` service will help do the following:
+
+#. Locate pstore data in ``/sys/fs/pstore``
+#. Read and save it to ``/var/lib/systemd/pstore``
+#. Clear pstore data for the next event
diff --git a/Documentation/process/2.Process.rst b/Documentation/process/2.Process.rst
index ef3b116492df..7bd41838a546 100644
--- a/Documentation/process/2.Process.rst
+++ b/Documentation/process/2.Process.rst
@@ -13,24 +13,19 @@ how the process works is required in order to be an effective part of it.
The big picture
---------------
-The kernel developers use a loosely time-based release process, with a new
-major kernel release happening every two or three months. The recent
-release history looks like this:
-
- ====== =================
- 5.0 March 3, 2019
- 5.1 May 5, 2019
- 5.2 July 7, 2019
- 5.3 September 15, 2019
- 5.4 November 24, 2019
- 5.5 January 6, 2020
- ====== =================
-
-Every 5.x release is a major kernel release with new features, internal
-API changes, and more. A typical release can contain about 13,000
-changesets with changes to several hundred thousand lines of code. 5.x is
-the leading edge of Linux kernel development; the kernel uses a
-rolling development model which is continually integrating major changes.
+The Linux kernel uses a loosely time-based, rolling release development
+model. A new major kernel release (which we will call, as an example, 9.x)
+[1]_ happens every two or three months, which comes with new features,
+internal API changes, and more. A typical release can contain about 13,000
+changesets with changes to several hundred thousand lines of code. Recent
+releases, along with their dates, can be found at `Wikipedia
+<https://en.wikipedia.org/wiki/Linux_kernel_version_history>`_.
+
+.. [1] Strictly speaking, the Linux kernel does not use semantic versioning
+ number scheme, but rather the 9.x pair identifies major release
+ version as a whole number. For each release, x is incremented,
+ but 9 is incremented only if x is deemed large enough (e.g.
+ Linux 5.0 is released following Linux 4.20).
A relatively straightforward discipline is followed with regard to the
merging of patches for each release. At the beginning of each development
@@ -48,9 +43,9 @@ detail later on).
The merge window lasts for approximately two weeks. At the end of this
time, Linus Torvalds will declare that the window is closed and release the
-first of the "rc" kernels. For the kernel which is destined to be 5.6,
+first of the "rc" kernels. For the kernel which is destined to be 9.x,
for example, the release which happens at the end of the merge window will
-be called 5.6-rc1. The -rc1 release is the signal that the time to
+be called 9.x-rc1. The -rc1 release is the signal that the time to
merge new features has passed, and that the time to stabilize the next
kernel has begun.
@@ -99,13 +94,15 @@ release is made. In the real world, this kind of perfection is hard to
achieve; there are just too many variables in a project of this size.
There comes a point where delaying the final release just makes the problem
worse; the pile of changes waiting for the next merge window will grow
-larger, creating even more regressions the next time around. So most 5.x
-kernels go out with a handful of known regressions though, hopefully, none
-of them are serious.
+larger, creating even more regressions the next time around. So most kernels
+go out with a handful of known regressions, though, hopefully, none of them
+are serious.
Once a stable release is made, its ongoing maintenance is passed off to the
-"stable team," currently Greg Kroah-Hartman. The stable team will release
-occasional updates to the stable release using the 5.x.y numbering scheme.
+"stable team," currently consists of Greg Kroah-Hartman and Sasha Levin. The
+stable team will release occasional updates to the stable release using the
+9.x.y numbering scheme.
+
To be considered for an update release, a patch must (1) fix a significant
bug, and (2) already be merged into the mainline for the next development
kernel. Kernels will typically receive stable updates for a little more
diff --git a/Documentation/process/coding-style.rst b/Documentation/process/coding-style.rst
index d1a8e5465ed9..2969ca378dbb 100644
--- a/Documentation/process/coding-style.rst
+++ b/Documentation/process/coding-style.rst
@@ -76,7 +76,7 @@ Don't use commas to avoid using braces:
if (condition)
do_this(), do_that();
-Always uses braces for multiple statements:
+Always use braces for multiple statements:
.. code-block:: c
diff --git a/Documentation/process/submitting-patches.rst b/Documentation/process/submitting-patches.rst
index 910e8fc9e3c8..9a509f1a6873 100644
--- a/Documentation/process/submitting-patches.rst
+++ b/Documentation/process/submitting-patches.rst
@@ -592,8 +592,9 @@ Both Tested-by and Reviewed-by tags, once received on mailing list from tester
or reviewer, should be added by author to the applicable patches when sending
next versions. However if the patch has changed substantially in following
version, these tags might not be applicable anymore and thus should be removed.
-Usually removal of someone's Tested-by or Reviewed-by tags should be mentioned
-in the patch changelog (after the '---' separator).
+Usually removal of someone's Acked-by, Tested-by or Reviewed-by tags should be
+mentioned in the patch changelog with an explanation (after the '---'
+separator).
A Suggested-by: tag indicates that the patch idea is suggested by the person
named and ensures credit to the person for the idea: if we diligently credit
diff --git a/Documentation/rust/quick-start.rst b/Documentation/rust/quick-start.rst
index 155f7107329a..152289f0bed2 100644
--- a/Documentation/rust/quick-start.rst
+++ b/Documentation/rust/quick-start.rst
@@ -39,8 +39,8 @@ of the box, e.g.::
Debian
******
-Debian Testing and Debian Unstable (Sid), outside of the freeze period, provide
-recent Rust releases and thus they should generally work out of the box, e.g.::
+Debian 13 (Trixie), as well as Testing and Debian Unstable (Sid) provide recent
+Rust releases and thus they should generally work out of the box, e.g.::
apt install rustc rust-src bindgen rustfmt rust-clippy
diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index f4d7e162d5e4..eae6a36b1c9a 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -10,6 +10,37 @@ of a Trust Source for greater security, while Encrypted Keys can be used on any
system. All user level blobs, are displayed and loaded in hex ASCII for
convenience, and are integrity verified.
+Trusted Keys as Protected key
+=============================
+It is the secure way of keeping the keys in the kernel key-ring as Trusted-Key,
+such that:
+
+- Key-blob, an encrypted key-data, created to be stored, loaded and seen by
+ userspace.
+- Key-data, the plain-key text in the system memory, to be used by
+ kernel space only.
+
+Though key-data is not accessible to the user-space in plain-text, but it is in
+plain-text in system memory, when used in kernel space. Even though kernel-space
+attracts small surface attack, but with compromised kernel or side-channel
+attack accessing the system memory can lead to a chance of the key getting
+compromised/leaked.
+
+In order to protect the key in kernel space, the concept of "protected-keys" is
+introduced which will act as an added layer of protection. The key-data of the
+protected keys is encrypted with Key-Encryption-Key(KEK), and decrypted inside
+the trust source boundary. The plain-key text never available out-side in the
+system memory. Thus, any crypto operation that is to be executed using the
+protected key, can only be done by the trust source, which generated the
+key blob.
+
+Hence, if the protected-key is leaked or compromised, it is of no use to the
+hacker.
+
+Trusted keys as protected keys, with trust source having the capability of
+generating:
+
+- Key-Blob, to be loaded, stored and seen by user-space.
Trust Source
============
@@ -252,7 +283,7 @@ in bytes. Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
Trusted Keys usage: CAAM
------------------------
-Usage::
+Trusted Keys Usage::
keyctl add trusted name "new keylen" ring
keyctl add trusted name "load hex_blob" ring
@@ -262,6 +293,21 @@ Usage::
CAAM-specific format. The key length for new keys is always in bytes.
Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
+Trusted Keys as Protected Keys Usage::
+
+ keyctl add trusted name "new keylen pk [options]" ring
+ keyctl add trusted name "load hex_blob [options]" ring
+ keyctl print keyid
+
+ where, 'pk' is used to direct trust source to generate protected key.
+
+ options:
+ key_enc_algo = For CAAM, supported enc algo are ECB(2), CCM(1).
+
+"keyctl print" returns an ASCII hex copy of the sealed key, which is in a
+CAAM-specific format. The key length for new keys is always in bytes.
+Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
+
Trusted Keys usage: DCP
-----------------------
@@ -343,6 +389,46 @@ Load a trusted key from the saved blob::
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba
+Create and save a trusted key as protected key named "kmk" of length 32 bytes.
+
+::
+
+ $ keyctl add trusted kmk "new 32 pk key_enc_algo=1" @u
+ 440502848
+
+ $ keyctl show
+ Session Keyring
+ -3 --alswrv 500 500 keyring: _ses
+ 97833714 --alswrv 500 -1 \_ keyring: _uid.500
+ 440502848 --alswrv 500 500 \_ trusted: kmk
+
+ $ keyctl print 440502848
+ 0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
+ 3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
+ 27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
+ a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
+ d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
+ dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
+ f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
+ e4a8aea2b607ec96931e6f4d4fe563ba
+
+ $ keyctl pipe 440502848 > kmk.blob
+
+Load a trusted key from the saved blob::
+
+ $ keyctl add trusted kmk "load `cat kmk.blob` key_enc_algo=1" @u
+ 268728824
+
+ $ keyctl print 268728824
+ 0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
+ 3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
+ 27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
+ a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
+ d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
+ dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
+ f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
+ e4a8aea2b607ec96931e6f4d4fe563ba
+
Reseal (TPM specific) a trusted key under new PCR values::
$ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
diff --git a/Documentation/sound/codecs/cs35l56.rst b/Documentation/sound/codecs/cs35l56.rst
index 57d1964453e1..d5363b08f515 100644
--- a/Documentation/sound/codecs/cs35l56.rst
+++ b/Documentation/sound/codecs/cs35l56.rst
@@ -105,10 +105,10 @@ In this example the SSID is 10280c63.
The format of the firmware file names is:
-SoundWire (except CS35L56 Rev B0):
+SoundWire:
cs35lxx-b0-dsp1-misc-SSID[-spkidX]-l?u?
-SoundWire CS35L56 Rev B0:
+SoundWire CS35L56 Rev B0 firmware released before kernel version 6.16:
cs35lxx-b0-dsp1-misc-SSID[-spkidX]-ampN
Non-SoundWire (HDA and I2S):
@@ -127,9 +127,8 @@ Where:
* spkidX is an optional part, used for laptops that have firmware
configurations for different makes and models of internal speakers.
-The CS35L56 Rev B0 continues to use the old filename scheme because a
-large number of firmware files have already been published with these
-names.
+Early firmware for CS35L56 Rev B0 used the ALSA prefix (ampN) as the
+filename qualifier. Support for the l?u? qualifier was added in kernel 6.16.
Sound Open Firmware and ALSA topology files
-------------------------------------------
diff --git a/Documentation/sphinx/kernel_abi.py b/Documentation/sphinx/kernel_abi.py
index 4c4375201b9e..5667f207d175 100644
--- a/Documentation/sphinx/kernel_abi.py
+++ b/Documentation/sphinx/kernel_abi.py
@@ -14,7 +14,7 @@
:license: GPL Version 2, June 1991 see Linux/COPYING for details.
The ``kernel-abi`` (:py:class:`KernelCmd`) directive calls the
- scripts/get_abi.py script to parse the Kernel ABI files.
+ AbiParser class to parse the Kernel ABI files.
Overview of directive's argument and options.
@@ -43,9 +43,9 @@ from sphinx.util.docutils import switch_source_input
from sphinx.util import logging
srctree = os.path.abspath(os.environ["srctree"])
-sys.path.insert(0, os.path.join(srctree, "scripts/lib/abi"))
+sys.path.insert(0, os.path.join(srctree, "tools/lib/python"))
-from abi_parser import AbiParser
+from abi.abi_parser import AbiParser
__version__ = "1.0"
diff --git a/Documentation/sphinx/kernel_feat.py b/Documentation/sphinx/kernel_feat.py
index aaac76892ceb..bdc0fef5c87f 100644
--- a/Documentation/sphinx/kernel_feat.py
+++ b/Documentation/sphinx/kernel_feat.py
@@ -13,7 +13,7 @@
:license: GPL Version 2, June 1991 see Linux/COPYING for details.
The ``kernel-feat`` (:py:class:`KernelFeat`) directive calls the
- scripts/get_feat.pl script to parse the Kernel ABI files.
+ tools/docs/get_feat.pl script to parse the Kernel ABI files.
Overview of directive's argument and options.
@@ -34,7 +34,6 @@
import codecs
import os
import re
-import subprocess
import sys
from docutils import nodes, statemachine
@@ -42,6 +41,11 @@ from docutils.statemachine import ViewList
from docutils.parsers.rst import directives, Directive
from sphinx.util.docutils import switch_source_input
+srctree = os.path.abspath(os.environ["srctree"])
+sys.path.insert(0, os.path.join(srctree, "tools/lib/python"))
+
+from feat.parse_features import ParseFeature # pylint: disable=C0413
+
def ErrorString(exc): # Shamelessly stolen from docutils
return f'{exc.__class__.__name}: {exc}'
@@ -84,18 +88,16 @@ class KernelFeat(Directive):
srctree = os.path.abspath(os.environ["srctree"])
- args = [
- os.path.join(srctree, 'scripts/get_feat.pl'),
- 'rest',
- '--enable-fname',
- '--dir',
- os.path.join(srctree, 'Documentation', self.arguments[0]),
- ]
+ feature_dir = os.path.join(srctree, 'Documentation', self.arguments[0])
- if len(self.arguments) > 1:
- args.extend(['--arch', self.arguments[1]])
+ feat = ParseFeature(feature_dir, False, True)
+ feat.parse()
- lines = subprocess.check_output(args, cwd=os.path.dirname(doc.current_source)).decode('utf-8')
+ if len(self.arguments) > 1:
+ arch = self.arguments[1]
+ lines = feat.output_arch_table(arch)
+ else:
+ lines = feat.output_matrix()
line_regex = re.compile(r"^\.\. FILE (\S+)$")
diff --git a/Documentation/sphinx/kernel_include.py b/Documentation/sphinx/kernel_include.py
index f94412cd17c9..626762ff6af3 100755
--- a/Documentation/sphinx/kernel_include.py
+++ b/Documentation/sphinx/kernel_include.py
@@ -87,6 +87,8 @@ import os.path
import re
import sys
+from difflib import get_close_matches
+
from docutils import io, nodes, statemachine
from docutils.statemachine import ViewList
from docutils.parsers.rst import Directive, directives
@@ -95,15 +97,17 @@ from docutils.parsers.rst.directives.body import CodeBlock, NumberLines
from sphinx.util import logging
srctree = os.path.abspath(os.environ["srctree"])
-sys.path.insert(0, os.path.join(srctree, "tools/docs/lib"))
+sys.path.insert(0, os.path.join(srctree, "tools/lib/python"))
-from parse_data_structs import ParseDataStructs
+from kdoc.parse_data_structs import ParseDataStructs
__version__ = "1.0"
logger = logging.getLogger(__name__)
RE_DOMAIN_REF = re.compile(r'\\ :(ref|c:type|c:func):`([^<`]+)(?:<([^>]+)>)?`\\')
RE_SIMPLE_REF = re.compile(r'`([^`]+)`')
+RE_LINENO_REF = re.compile(r'^\s*-\s+LINENO_(\d+):\s+(.*)')
+RE_SPLIT_DOMAIN = re.compile(r"(.*)\.(.*)")
def ErrorString(exc): # Shamelessly stolen from docutils
return f'{exc.__class__.__name}: {exc}'
@@ -212,14 +216,16 @@ class KernelInclude(Directive):
- a TOC table containing cross references.
"""
parser = ParseDataStructs()
- parser.parse_file(path)
if 'exception-file' in self.options:
source_dir = os.path.dirname(os.path.abspath(
self.state_machine.input_lines.source(
self.lineno - self.state_machine.input_offset - 1)))
exceptions_file = os.path.join(source_dir, self.options['exception-file'])
- parser.process_exceptions(exceptions_file)
+ else:
+ exceptions_file = None
+
+ parser.parse_file(path, exceptions_file)
# Store references on a symbol dict to be used at check time
if 'warn-broken' in self.options:
@@ -242,23 +248,32 @@ class KernelInclude(Directive):
# TOC output is a ReST file, not a literal. So, we can add line
# numbers
- rawtext = parser.gen_toc()
+ startline = self.options.get('start-line', None)
+ endline = self.options.get('end-line', None)
- include_lines = statemachine.string2lines(rawtext, tab_width,
- convert_whitespace=True)
+ relpath = os.path.relpath(path, srctree)
- # Append line numbers data
+ result = ViewList()
+ for line in parser.gen_toc().split("\n"):
+ match = RE_LINENO_REF.match(line)
+ if not match:
+ result.append(line, path)
+ continue
- startline = self.options.get('start-line', None)
+ ln, ref = match.groups()
+ ln = int(ln)
- result = ViewList()
- if startline and startline > 0:
- offset = startline - 1
- else:
- offset = 0
+ # Filter line range if needed
+ if startline and (ln < startline):
+ continue
+
+ if endline and (ln > endline):
+ continue
- for ln, line in enumerate(include_lines, start=offset):
- result.append(line, path, ln)
+ # Sphinx numerates starting with zero, but text editors
+ # and other tools start from one
+ realln = ln + 1
+ result.append(f"- {ref}: {relpath}#{realln}", path, ln)
self.state_machine.insert_input(result, path)
@@ -388,6 +403,63 @@ class KernelInclude(Directive):
# ==============================================================================
reported = set()
+DOMAIN_INFO = {}
+all_refs = {}
+
+def fill_domain_info(env):
+ """
+ Get supported reference types for each Sphinx domain and C namespaces
+ """
+ if DOMAIN_INFO:
+ return
+
+ for domain_name, domain_instance in env.domains.items():
+ try:
+ object_types = list(domain_instance.object_types.keys())
+ DOMAIN_INFO[domain_name] = object_types
+ except AttributeError:
+ # Ignore domains that we can't retrieve object types, if any
+ pass
+
+ for domain in DOMAIN_INFO.keys():
+ domain_obj = env.get_domain(domain)
+ for name, dispname, objtype, docname, anchor, priority in domain_obj.get_objects():
+ ref_name = name.lower()
+
+ if domain == "c":
+ if '.' in ref_name:
+ ref_name = ref_name.split(".")[-1]
+
+ if not ref_name in all_refs:
+ all_refs[ref_name] = []
+
+ all_refs[ref_name].append(f"\t{domain}:{objtype}:`{name}` (from {docname})")
+
+def get_suggestions(app, env, node,
+ original_target, original_domain, original_reftype):
+ """Check if target exists in the other domain or with different reftypes."""
+ original_target = original_target.lower()
+
+ # Remove namespace if present
+ if original_domain == "c":
+ if '.' in original_target:
+ original_target = original_target.split(".")[-1]
+
+ suggestions = []
+
+ # If name exists, propose exact name match on different domains
+ if original_target in all_refs:
+ return all_refs[original_target]
+
+ # If not found, get a close match, using difflib.
+ # Such method is based on Ratcliff-Obershelp Algorithm, which seeks
+ # for a close match within a certain distance. We're using the defaults
+ # here, e.g. cutoff=0.6, proposing 3 alternatives
+ matches = get_close_matches(original_target, all_refs.keys())
+ for match in matches:
+ suggestions += all_refs[match]
+
+ return suggestions
def check_missing_refs(app, env, node, contnode):
"""Check broken refs for the files it creates xrefs"""
@@ -404,11 +476,13 @@ def check_missing_refs(app, env, node, contnode):
if node.source not in xref_files:
return None
+ fill_domain_info(env)
+
target = node.get('reftarget', '')
domain = node.get('refdomain', 'std')
reftype = node.get('reftype', '')
- msg = f"can't link to: {domain}:{reftype}:: {target}"
+ msg = f"Invalid xref: {domain}:{reftype}:`{target}`"
# Don't duplicate warnings
data = (node.source, msg)
@@ -416,6 +490,10 @@ def check_missing_refs(app, env, node, contnode):
return None
reported.add(data)
+ suggestions = get_suggestions(app, env, node, target, domain, reftype)
+ if suggestions:
+ msg += ". Possible alternatives:\n" + '\n'.join(suggestions)
+
logger.warning(msg, location=node, type='ref', subtype='missing')
return None
diff --git a/Documentation/sphinx/kerneldoc-preamble.sty b/Documentation/sphinx/kerneldoc-preamble.sty
index 5d68395539fe..16d9ff46fdf6 100644
--- a/Documentation/sphinx/kerneldoc-preamble.sty
+++ b/Documentation/sphinx/kerneldoc-preamble.sty
@@ -220,7 +220,7 @@
If you want them, please install non-variable ``Noto Sans CJK''
font families along with the texlive-xecjk package by following
instructions from
- \sphinxcode{./scripts/sphinx-pre-install}.
+ \sphinxcode{./tools/docs/sphinx-pre-install}.
Having optional non-variable ``Noto Serif CJK'' font families will
improve the looks of those translations.
\end{sphinxadmonition}}
diff --git a/Documentation/sphinx/kerneldoc.py b/Documentation/sphinx/kerneldoc.py
index 2586b4d4e494..d8cdf068ef35 100644
--- a/Documentation/sphinx/kerneldoc.py
+++ b/Documentation/sphinx/kerneldoc.py
@@ -42,10 +42,10 @@ from sphinx.util import logging
from pprint import pformat
srctree = os.path.abspath(os.environ["srctree"])
-sys.path.insert(0, os.path.join(srctree, "scripts/lib/kdoc"))
+sys.path.insert(0, os.path.join(srctree, "tools/lib/python"))
-from kdoc_files import KernelFiles
-from kdoc_output import RestFormat
+from kdoc.kdoc_files import KernelFiles
+from kdoc.kdoc_output import RestFormat
__version__ = '1.0'
kfiles = None
diff --git a/Documentation/sphinx/load_config.py b/Documentation/sphinx/load_config.py
deleted file mode 100644
index 1afb0c97f06b..000000000000
--- a/Documentation/sphinx/load_config.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8; mode: python -*-
-# SPDX-License-Identifier: GPL-2.0
-# pylint: disable=R0903, C0330, R0914, R0912, E0401
-
-import os
-import sys
-from sphinx.util.osutil import fs_encoding
-
-# ------------------------------------------------------------------------------
-def loadConfig(namespace):
-# ------------------------------------------------------------------------------
-
- """Load an additional configuration file into *namespace*.
-
- The name of the configuration file is taken from the environment
- ``SPHINX_CONF``. The external configuration file extends (or overwrites) the
- configuration values from the origin ``conf.py``. With this you are able to
- maintain *build themes*. """
-
- config_file = os.environ.get("SPHINX_CONF", None)
- if (config_file is not None
- and os.path.normpath(namespace["__file__"]) != os.path.normpath(config_file) ):
- config_file = os.path.abspath(config_file)
-
- # Let's avoid one conf.py file just due to latex_documents
- start = config_file.find('Documentation/')
- if start >= 0:
- start = config_file.find('/', start + 1)
-
- end = config_file.rfind('/')
- if start >= 0 and end > 0:
- dir = config_file[start + 1:end]
-
- print("source directory: %s" % dir)
- new_latex_docs = []
- latex_documents = namespace['latex_documents']
-
- for l in latex_documents:
- if l[0].find(dir + '/') == 0:
- has = True
- fn = l[0][len(dir) + 1:]
- new_latex_docs.append((fn, l[1], l[2], l[3], l[4]))
- break
-
- namespace['latex_documents'] = new_latex_docs
-
- # If there is an extra conf.py file, load it
- if os.path.isfile(config_file):
- sys.stdout.write("load additional sphinx-config: %s\n" % config_file)
- config = namespace.copy()
- config['__file__'] = config_file
- with open(config_file, 'rb') as f:
- code = compile(f.read(), fs_encoding, 'exec')
- exec(code, config)
- del config['__file__']
- namespace.update(config)
- else:
- config = namespace.copy()
- config['tags'].add("subproject")
- namespace.update(config)
diff --git a/Documentation/sphinx/parallel-wrapper.sh b/Documentation/sphinx/parallel-wrapper.sh
deleted file mode 100644
index e54c44ce117d..000000000000
--- a/Documentation/sphinx/parallel-wrapper.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0+
-#
-# Figure out if we should follow a specific parallelism from the make
-# environment (as exported by scripts/jobserver-exec), or fall back to
-# the "auto" parallelism when "-jN" is not specified at the top-level
-# "make" invocation.
-
-sphinx="$1"
-shift || true
-
-parallel="$PARALLELISM"
-if [ -z "$parallel" ] ; then
- # If no parallelism is specified at the top-level make, then
- # fall back to the expected "-jauto" mode that the "htmldocs"
- # target has had.
- auto=$(perl -e 'open IN,"'"$sphinx"' --version 2>&1 |";
- while (<IN>) {
- if (m/([\d\.]+)/) {
- print "auto" if ($1 >= "1.7")
- }
- }
- close IN')
- if [ -n "$auto" ] ; then
- parallel="$auto"
- fi
-fi
-# Only if some parallelism has been determined do we add the -jN option.
-if [ -n "$parallel" ] ; then
- parallel="-j$parallel"
-fi
-
-exec "$sphinx" $parallel "$@"
diff --git a/Documentation/tools/rtla/common_appendix.rst b/Documentation/tools/rtla/common_appendix.txt
index 53cae7537537..53cae7537537 100644
--- a/Documentation/tools/rtla/common_appendix.rst
+++ b/Documentation/tools/rtla/common_appendix.txt
diff --git a/Documentation/tools/rtla/common_hist_options.rst b/Documentation/tools/rtla/common_hist_options.txt
index df53ff835bfb..df53ff835bfb 100644
--- a/Documentation/tools/rtla/common_hist_options.rst
+++ b/Documentation/tools/rtla/common_hist_options.txt
diff --git a/Documentation/tools/rtla/common_options.rst b/Documentation/tools/rtla/common_options.txt
index 77ef35d3f831..1c4f3e663cf5 100644
--- a/Documentation/tools/rtla/common_options.rst
+++ b/Documentation/tools/rtla/common_options.txt
@@ -1,11 +1,15 @@
**-c**, **--cpus** *cpu-list*
- Set the osnoise tracer to run the sample threads in the cpu-list.
+ Set the |tool| tracer to run the sample threads in the cpu-list.
+
+ By default, the |tool| tracer runs the sample threads on all CPUs.
**-H**, **--house-keeping** *cpu-list*
Run rtla control threads only on the given cpu-list.
+ If omitted, rtla will attempt to auto-migrate its main thread to any CPU that is not running any workload threads.
+
**-d**, **--duration** *time[s|m|h|d]*
Set the duration of the session.
@@ -35,17 +39,21 @@
**-P**, **--priority** *o:prio|r:prio|f:prio|d:runtime:period*
- Set scheduling parameters to the osnoise tracer threads, the format to set the priority are:
+ Set scheduling parameters to the |tool| tracer threads, the format to set the priority are:
- *o:prio* - use SCHED_OTHER with *prio*;
- *r:prio* - use SCHED_RR with *prio*;
- *f:prio* - use SCHED_FIFO with *prio*;
- *d:runtime[us|ms|s]:period[us|ms|s]* - use SCHED_DEADLINE with *runtime* and *period* in nanoseconds.
+ If not set, tracer threads keep their default priority. For rtla user threads, it is set to SCHED_FIFO with priority 95. For kernel threads, see *osnoise* and *timerlat* tracer documentation for the running kernel version.
+
**-C**, **--cgroup**\[*=cgroup*]
Set a *cgroup* to the tracer's threads. If the **-C** option is passed without arguments, the tracer's thread will inherit **rtla**'s *cgroup*. Otherwise, the threads will be placed on the *cgroup* passed to the option.
+ If not set, the behavior differs between workload types. User workloads created by rtla will inherit rtla's cgroup. Kernel workloads are assigned the root cgroup.
+
**--warm-up** *s*
After starting the workload, let it run for *s* seconds before starting collecting the data, allowing the system to warm-up. Statistical data generated during warm-up is discarded.
@@ -53,6 +61,8 @@
**--trace-buffer-size** *kB*
Set the per-cpu trace buffer size in kB for the tracing output.
+ If not set, the default tracefs buffer size is used.
+
**--on-threshold** *action*
Defines an action to be executed when tracing is stopped on a latency threshold
@@ -67,7 +77,7 @@
- *trace[,file=<filename>]*
Saves trace output, optionally taking a filename. Alternative to -t/--trace.
- Note that nlike -t/--trace, specifying this multiple times will result in
+ Note that unlike -t/--trace, specifying this multiple times will result in
the trace being saved multiple times.
- *signal,num=<sig>,pid=<pid>*
diff --git a/Documentation/tools/rtla/common_osnoise_description.rst b/Documentation/tools/rtla/common_osnoise_description.txt
index d5d61615b967..d5d61615b967 100644
--- a/Documentation/tools/rtla/common_osnoise_description.rst
+++ b/Documentation/tools/rtla/common_osnoise_description.txt
diff --git a/Documentation/tools/rtla/common_osnoise_options.rst b/Documentation/tools/rtla/common_osnoise_options.txt
index bd3c4f499193..bd3c4f499193 100644
--- a/Documentation/tools/rtla/common_osnoise_options.rst
+++ b/Documentation/tools/rtla/common_osnoise_options.txt
diff --git a/Documentation/tools/rtla/common_timerlat_aa.rst b/Documentation/tools/rtla/common_timerlat_aa.txt
index 077029e6b289..077029e6b289 100644
--- a/Documentation/tools/rtla/common_timerlat_aa.rst
+++ b/Documentation/tools/rtla/common_timerlat_aa.txt
diff --git a/Documentation/tools/rtla/common_timerlat_description.rst b/Documentation/tools/rtla/common_timerlat_description.txt
index 49fcae3ffdec..49fcae3ffdec 100644
--- a/Documentation/tools/rtla/common_timerlat_description.rst
+++ b/Documentation/tools/rtla/common_timerlat_description.txt
diff --git a/Documentation/tools/rtla/common_timerlat_options.rst b/Documentation/tools/rtla/common_timerlat_options.txt
index 1f5d024b53aa..33070b264cae 100644
--- a/Documentation/tools/rtla/common_timerlat_options.rst
+++ b/Documentation/tools/rtla/common_timerlat_options.txt
@@ -13,7 +13,7 @@
Set the automatic trace mode. This mode sets some commonly used options
while debugging the system. It is equivalent to use **-T** *us* **-s** *us*
**-t**. By default, *timerlat* tracer uses FIFO:95 for *timerlat* threads,
- thus equilavent to **-P** *f:95*.
+ thus equivalent to **-P** *f:95*.
**-p**, **--period** *us*
@@ -56,7 +56,7 @@
**-u**, **--user-threads**
Set timerlat to run without a workload, and then dispatches user-space workloads
- to wait on the timerlat_fd. Once the workload is awakes, it goes to sleep again
+ to wait on the timerlat_fd. Once the workload is awakened, it goes to sleep again
adding so the measurement for the kernel-to-user and user-to-kernel to the tracer
output. **--user-threads** will be used unless the user specify **-k**.
diff --git a/Documentation/tools/rtla/common_top_options.rst b/Documentation/tools/rtla/common_top_options.txt
index f48878938f84..f48878938f84 100644
--- a/Documentation/tools/rtla/common_top_options.rst
+++ b/Documentation/tools/rtla/common_top_options.txt
diff --git a/Documentation/tools/rtla/rtla-hwnoise.rst b/Documentation/tools/rtla/rtla-hwnoise.rst
index 3a7163c02ac8..26512b15fe7b 100644
--- a/Documentation/tools/rtla/rtla-hwnoise.rst
+++ b/Documentation/tools/rtla/rtla-hwnoise.rst
@@ -29,11 +29,11 @@ collection of the tracer output.
OPTIONS
=======
-.. include:: common_osnoise_options.rst
+.. include:: common_osnoise_options.txt
-.. include:: common_top_options.rst
+.. include:: common_top_options.txt
-.. include:: common_options.rst
+.. include:: common_options.txt
EXAMPLE
=======
@@ -106,4 +106,4 @@ AUTHOR
======
Written by Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/tools/rtla/rtla-osnoise-hist.rst b/Documentation/tools/rtla/rtla-osnoise-hist.rst
index 1fc60ef26106..007521c865d9 100644
--- a/Documentation/tools/rtla/rtla-osnoise-hist.rst
+++ b/Documentation/tools/rtla/rtla-osnoise-hist.rst
@@ -15,7 +15,7 @@ SYNOPSIS
DESCRIPTION
===========
-.. include:: common_osnoise_description.rst
+.. include:: common_osnoise_description.txt
The **rtla osnoise hist** tool collects all **osnoise:sample_threshold**
occurrence in a histogram, displaying the results in a user-friendly way.
@@ -24,11 +24,11 @@ collection of the tracer output.
OPTIONS
=======
-.. include:: common_osnoise_options.rst
+.. include:: common_osnoise_options.txt
-.. include:: common_hist_options.rst
+.. include:: common_hist_options.txt
-.. include:: common_options.rst
+.. include:: common_options.txt
EXAMPLE
=======
@@ -65,4 +65,4 @@ AUTHOR
======
Written by Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/tools/rtla/rtla-osnoise-top.rst b/Documentation/tools/rtla/rtla-osnoise-top.rst
index b1cbd7bcd4ae..6ccadae38945 100644
--- a/Documentation/tools/rtla/rtla-osnoise-top.rst
+++ b/Documentation/tools/rtla/rtla-osnoise-top.rst
@@ -15,7 +15,7 @@ SYNOPSIS
DESCRIPTION
===========
-.. include:: common_osnoise_description.rst
+.. include:: common_osnoise_description.txt
**rtla osnoise top** collects the periodic summary from the *osnoise* tracer,
including the counters of the occurrence of the interference source,
@@ -26,11 +26,11 @@ collection of the tracer output.
OPTIONS
=======
-.. include:: common_osnoise_options.rst
+.. include:: common_osnoise_options.txt
-.. include:: common_top_options.rst
+.. include:: common_top_options.txt
-.. include:: common_options.rst
+.. include:: common_options.txt
EXAMPLE
=======
@@ -60,4 +60,4 @@ AUTHOR
======
Written by Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/tools/rtla/rtla-osnoise.rst b/Documentation/tools/rtla/rtla-osnoise.rst
index c129b206ce34..540d2bf6c152 100644
--- a/Documentation/tools/rtla/rtla-osnoise.rst
+++ b/Documentation/tools/rtla/rtla-osnoise.rst
@@ -14,7 +14,7 @@ SYNOPSIS
DESCRIPTION
===========
-.. include:: common_osnoise_description.rst
+.. include:: common_osnoise_description.txt
The *osnoise* tracer outputs information in two ways. It periodically prints
a summary of the noise of the operating system, including the counters of
@@ -56,4 +56,4 @@ AUTHOR
======
Written by Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/tools/rtla/rtla-timerlat-hist.rst b/Documentation/tools/rtla/rtla-timerlat-hist.rst
index 4923a362129b..f56fe546411b 100644
--- a/Documentation/tools/rtla/rtla-timerlat-hist.rst
+++ b/Documentation/tools/rtla/rtla-timerlat-hist.rst
@@ -16,7 +16,7 @@ SYNOPSIS
DESCRIPTION
===========
-.. include:: common_timerlat_description.rst
+.. include:: common_timerlat_description.txt
The **rtla timerlat hist** displays a histogram of each tracer event
occurrence. This tool uses the periodic information, and the
@@ -25,13 +25,13 @@ occurrence. This tool uses the periodic information, and the
OPTIONS
=======
-.. include:: common_timerlat_options.rst
+.. include:: common_timerlat_options.txt
-.. include:: common_hist_options.rst
+.. include:: common_hist_options.txt
-.. include:: common_options.rst
+.. include:: common_options.txt
-.. include:: common_timerlat_aa.rst
+.. include:: common_timerlat_aa.txt
EXAMPLE
=======
@@ -110,4 +110,4 @@ AUTHOR
======
Written by Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/tools/rtla/rtla-timerlat-top.rst b/Documentation/tools/rtla/rtla-timerlat-top.rst
index 50968cdd2095..72d85e36c193 100644
--- a/Documentation/tools/rtla/rtla-timerlat-top.rst
+++ b/Documentation/tools/rtla/rtla-timerlat-top.rst
@@ -16,23 +16,23 @@ SYNOPSIS
DESCRIPTION
===========
-.. include:: common_timerlat_description.rst
+.. include:: common_timerlat_description.txt
The **rtla timerlat top** displays a summary of the periodic output
from the *timerlat* tracer. It also provides information for each
operating system noise via the **osnoise:** tracepoints that can be
-seem with the option **-T**.
+seen with the option **-T**.
OPTIONS
=======
-.. include:: common_timerlat_options.rst
+.. include:: common_timerlat_options.txt
-.. include:: common_top_options.rst
+.. include:: common_top_options.txt
-.. include:: common_options.rst
+.. include:: common_options.txt
-.. include:: common_timerlat_aa.rst
+.. include:: common_timerlat_aa.txt
**--aa-only** *us*
@@ -133,4 +133,4 @@ AUTHOR
------
Written by Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/tools/rtla/rtla-timerlat.rst b/Documentation/tools/rtla/rtla-timerlat.rst
index 20e2d259467f..ce9f57e038c3 100644
--- a/Documentation/tools/rtla/rtla-timerlat.rst
+++ b/Documentation/tools/rtla/rtla-timerlat.rst
@@ -14,7 +14,7 @@ SYNOPSIS
DESCRIPTION
===========
-.. include:: common_timerlat_description.rst
+.. include:: common_timerlat_description.txt
The **rtla timerlat top** mode displays a summary of the periodic output
from the *timerlat* tracer. The **rtla timerlat hist** mode displays
@@ -51,4 +51,4 @@ AUTHOR
======
Written by Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/tools/rtla/rtla.rst b/Documentation/tools/rtla/rtla.rst
index fc0d233efcd5..2a5fb7004ad4 100644
--- a/Documentation/tools/rtla/rtla.rst
+++ b/Documentation/tools/rtla/rtla.rst
@@ -45,4 +45,4 @@ AUTHOR
======
Daniel Bristot de Oliveira <bristot@kernel.org>
-.. include:: common_appendix.rst
+.. include:: common_appendix.txt
diff --git a/Documentation/trace/timerlat-tracer.rst b/Documentation/trace/timerlat-tracer.rst
index 53a56823e903..68d429d454a5 100644
--- a/Documentation/trace/timerlat-tracer.rst
+++ b/Documentation/trace/timerlat-tracer.rst
@@ -43,12 +43,12 @@ It is possible to follow the trace by reading the trace file::
<...>-868 [001] .... 54.030347: #2 context thread timer_latency 4351 ns
-The tracer creates a per-cpu kernel thread with real-time priority that
-prints two lines at every activation. The first is the *timer latency*
-observed at the *hardirq* context before the activation of the thread.
-The second is the *timer latency* observed by the thread. The ACTIVATION
-ID field serves to relate the *irq* execution to its respective *thread*
-execution.
+The tracer creates a per-cpu kernel thread with real-time priority
+SCHED_FIFO:95 that prints two lines at every activation. The first is
+the *timer latency* observed at the *hardirq* context before the activation
+of the thread. The second is the *timer latency* observed by the thread.
+The ACTIVATION ID field serves to relate the *irq* execution to its
+respective *thread* execution.
The *irq*/*thread* splitting is important to clarify in which context
the unexpected high value is coming from. The *irq* context can be
diff --git a/Documentation/translations/it_IT/doc-guide/parse-headers.rst b/Documentation/translations/it_IT/doc-guide/parse-headers.rst
index 026a23e49767..b0caa40fe1e9 100644
--- a/Documentation/translations/it_IT/doc-guide/parse-headers.rst
+++ b/Documentation/translations/it_IT/doc-guide/parse-headers.rst
@@ -13,28 +13,28 @@ dello spazio utente ha ulteriori vantaggi: Sphinx genererà dei messaggi
d'avviso se un simbolo non viene trovato nella documentazione. Questo permette
di mantenere allineate la documentazione della uAPI (API spazio utente)
con le modifiche del kernel.
-Il programma :ref:`parse_headers.pl <it_parse_headers>` genera questi riferimenti.
+Il programma :ref:`parse_headers.py <it_parse_headers>` genera questi riferimenti.
Esso dev'essere invocato attraverso un Makefile, mentre si genera la
documentazione. Per avere un esempio su come utilizzarlo all'interno del kernel
consultate ``Documentation/userspace-api/media/Makefile``.
.. _it_parse_headers:
-parse_headers.pl
+parse_headers.py
^^^^^^^^^^^^^^^^
NOME
****
-parse_headers.pl - analizza i file C al fine di identificare funzioni,
+parse_headers.py - analizza i file C al fine di identificare funzioni,
strutture, enumerati e definizioni, e creare riferimenti per Sphinx
SINTASSI
********
-\ **parse_headers.pl**\ [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>]
+\ **parse_headers.py**\ [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>]
Dove <options> può essere: --debug, --usage o --help.
diff --git a/Documentation/translations/it_IT/doc-guide/sphinx.rst b/Documentation/translations/it_IT/doc-guide/sphinx.rst
index 1f513bc33618..a5c5d935febf 100644
--- a/Documentation/translations/it_IT/doc-guide/sphinx.rst
+++ b/Documentation/translations/it_IT/doc-guide/sphinx.rst
@@ -109,7 +109,7 @@ Sphinx. Se lo script riesce a riconoscere la vostra distribuzione, allora
sarà in grado di darvi dei suggerimenti su come procedere per completare
l'installazione::
- $ ./scripts/sphinx-pre-install
+ $ ./tools/docs/sphinx-pre-install
Checking if the needed tools for Fedora release 26 (Twenty Six) are available
Warning: better to also install "texlive-luatex85".
You should run:
@@ -119,7 +119,7 @@ l'installazione::
. sphinx_2.4.4/bin/activate
pip install -r Documentation/sphinx/requirements.txt
- Can't build as 1 mandatory dependency is missing at ./scripts/sphinx-pre-install line 468.
+ Can't build as 1 mandatory dependency is missing at ./tools/docs/sphinx-pre-install line 468.
L'impostazione predefinita prevede il controllo dei requisiti per la generazione
di documenti html e PDF, includendo anche il supporto per le immagini, le
diff --git a/Documentation/translations/ja_JP/SubmittingPatches b/Documentation/translations/ja_JP/SubmittingPatches
index 5334db471744..b950347b5993 100644
--- a/Documentation/translations/ja_JP/SubmittingPatches
+++ b/Documentation/translations/ja_JP/SubmittingPatches
@@ -132,6 +132,25 @@ http://savannah.nongnu.org/projects/quilt
platform_set_drvdata(), but left the variable "dev" unused,
delete it.
+特定ã®ã‚³ãƒŸãƒƒãƒˆã§å°Žå…¥ã•れãŸä¸å…·åˆã‚’修正ã™ã‚‹å ´åˆï¼ˆä¾‹ãˆã° ``git bisect`` ã§åŽŸå› ã¨ãªã£ãŸ
+コミットを特定ã—ãŸã¨ããªã©ï¼‰ã¯ã€ã‚³ãƒŸãƒƒãƒˆã® SHA-1 ã®å…ˆé ­12文字ã¨1行ã®è¦ç´„ã‚’æ·»ãˆãŸ
+「Fixes:ã€ã‚¿ã‚°ã‚’付ã‘ã¦ãã ã•ã„。ã“ã®è¡Œã¯75文字を超ãˆã¦ã‚‚æ§‹ã„ã¾ã›ã‚“ãŒã€é€”中ã§
+改行ã›ãšã€å¿…ãš1行ã§è¨˜è¿°ã—ã¦ãã ã•ã„。
+例:
+ Fixes: 54a4f0239f2e ("KVM: MMU: make kvm_mmu_zap_page() return the number of pages it actually freed")
+
+以下㮠git ã®è¨­å®šã‚’使ã†ã¨ã€git log ã‚„ git show ã§ä¸Šè¨˜å½¢å¼ã‚’出力ã™ã‚‹ãŸã‚ã®
+専用ã®å‡ºåЛ形å¼ã‚’追加ã§ãã¾ã™::
+
+ [core]
+ abbrev = 12
+ [pretty]
+ fixes = Fixes: %h (\"%s\")
+
+使用例::
+
+ $ git log -1 --pretty=fixes 54a4f0239f2e
+ Fixes: 54a4f0239f2e ("KVM: MMU: make kvm_mmu_zap_page() return the number of pages it actually freed")
3) パッãƒã®åˆ†å‰²
@@ -409,7 +428,7 @@ Acked-by: ãŒå¿…ãšã—もパッãƒå…¨ä½“ã®æ‰¿èªã‚’示ã—ã¦ã„ã‚‹ã‚ã‘ã§ã¯ã
ã“ã®ã‚¿ã‚°ã¯ãƒ‘ッãƒã«é–¢å¿ƒãŒã‚ã‚‹ã¨æ€ã‚れる人é”ãŒãã®ãƒ‘ッãƒã®è­°è«–ã«å«ã¾ã‚Œã¦ã„ãŸã“ã¨
を明文化ã—ã¾ã™ã€‚
-14) Reported-by:, Tested-by:, Reviewed-by: ãŠã‚ˆã³ Suggested-by: ã®åˆ©ç”¨
+14) Reported-by:, Tested-by:, Reviewed-by:, Suggested-by: ãŠã‚ˆã³ Fixes: ã®åˆ©ç”¨
ä»–ã®èª°ã‹ã«ã‚ˆã£ã¦å ±å‘Šã•れãŸå•題を修正ã™ã‚‹ãƒ‘ッãƒã§ã‚れã°ã€å•題報告者ã¨ã„ã†å¯„与を
クレジットã™ã‚‹ãŸã‚ã«ã€Reported-by: タグを追加ã™ã‚‹ã“ã¨ã‚’検討ã—ã¦ãã ã•ã„。
@@ -465,6 +484,13 @@ Suggested-by: ã‚¿ã‚°ã¯ã€ãƒ‘ッãƒã®ã‚¢ã‚¤ãƒ‡ã‚¢ãŒãã®äººã‹ã‚‰ã®ææ¡ˆã«
クレジットã—ã¦ã„ã‘ã°ã€æœ›ã‚€ã‚‰ãã¯ãã®äººãŸã¡ãŒå°†æ¥åˆ¥ã®æ©Ÿä¼šã«å†åº¦åŠ›ã‚’è²¸ã™æ°—ã«
ãªã£ã¦ãれるã‹ã‚‚ã—れã¾ã›ã‚“。
+Fixes: ã‚¿ã‚°ã¯ã€ãã®ãƒ‘ッãƒãŒä»¥å‰ã®ã‚³ãƒŸãƒƒãƒˆã«ã‚ã£ãŸå•題を修正ã™ã‚‹ã“ã¨ã‚’示ã—ã¾ã™ã€‚
+ã“れã¯ã€ãƒã‚°ãŒã©ã“ã§ç™ºç”Ÿã—ãŸã‹ã‚’特定ã—ã‚„ã™ãã—ã€ãƒã‚°ä¿®æ­£ã®ãƒ¬ãƒ“ューã«å½¹ç«‹ã¡ã¾ã™ã€‚
+ã¾ãŸã€ã“ã®ã‚¿ã‚°ã¯stableカーãƒãƒ«ãƒãƒ¼ãƒ ãŒã€ã‚ãªãŸã®ä¿®æ­£ã‚’ã©ã®stableカーãƒãƒ«
+ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã«é©ç”¨ã™ã¹ãã‹åˆ¤æ–­ã™ã‚‹æ‰‹åŠ©ã‘ã«ã‚‚ãªã‚Šã¾ã™ã€‚パッãƒã«ã‚ˆã£ã¦ä¿®æ­£ã•れãŸ
+ãƒã‚°ã‚’示ã™ã«ã¯ã€ã“ã®æ–¹æ³•ãŒæŽ¨å¥¨ã•れã¾ã™ã€‚å‰è¿°ã®ã€ã€Œ2) パッãƒã«å¯¾ã™ã‚‹èª¬æ˜Žã€ã®
+セクションをå‚ç…§ã—ã¦ãã ã•ã„。
+
15) 標準的ãªãƒ‘ッãƒã®ãƒ•ォーマット
標準的ãªãƒ‘ッãƒã®ã‚µãƒ–ジェクトã¯ä»¥ä¸‹ã®ã¨ãŠã‚Šã§ã™ã€‚
diff --git a/Documentation/translations/zh_CN/admin-guide/README.rst b/Documentation/translations/zh_CN/admin-guide/README.rst
index 82e628b77efd..7c2ffe7e87c7 100644
--- a/Documentation/translations/zh_CN/admin-guide/README.rst
+++ b/Documentation/translations/zh_CN/admin-guide/README.rst
@@ -288,4 +288,4 @@ Documentation/translations/zh_CN/admin-guide/bug-hunting.rst 。
更多用GDB调试内核的信æ¯ï¼Œè¯·å‚阅:
Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst
-和 Documentation/dev-tools/kgdb.rst 。
+和 Documentation/process/debugging/kgdb.rst 。
diff --git a/Documentation/translations/zh_CN/block/blk-mq.rst b/Documentation/translations/zh_CN/block/blk-mq.rst
new file mode 100644
index 000000000000..ccc08f76ff97
--- /dev/null
+++ b/Documentation/translations/zh_CN/block/blk-mq.rst
@@ -0,0 +1,130 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/block/blk-mq.rst
+
+:翻译:
+
+ æŸ¯å­æ° kezijie <kezijie@leap-io-kernel.com>
+
+:校译:
+
+
+
+================================================
+多队列å—设备 I/O 排队机制 (blk-mq)
+================================================
+
+多队列å—设备 I/O 排队机制æä¾›äº†ä¸€ç»„ APIï¼Œä½¿é«˜é€Ÿå­˜å‚¨è®¾å¤‡èƒ½å¤ŸåŒæ—¶åœ¨å¤šä¸ªé˜Ÿåˆ—中
+处ç†å¹¶å‘çš„ I/O 请求并将其æäº¤åˆ°å—设备,从而实现æžé«˜çš„æ¯ç§’è¾“å…¥/输出æ“作次数
+(IOPS)ï¼Œå……åˆ†å‘æŒ¥çŽ°ä»£å­˜å‚¨è®¾å¤‡çš„å¹¶è¡Œèƒ½åŠ›ã€‚
+
+介ç»
+====
+
+背景
+----
+
+ç£ç›˜ä»Ž Linux 内核开å‘åˆæœŸå°±å·²æˆä¸ºäº‹å®žä¸Šçš„æ ‡å‡†ã€‚å— I/O å­ç³»ç»Ÿçš„目标是尽å¯èƒ½
+为此类设备æä¾›æœ€ä½³æ€§èƒ½ï¼Œå› ä¸ºå®ƒä»¬åœ¨è¿›è¡Œéšæœºè®¿é—®æ—¶ä»£ä»·æžé«˜ï¼Œæ€§èƒ½ç“¶é¢ˆä¸»è¦åœ¨æœºæ¢°
+è¿åŠ¨éƒ¨ä»¶ä¸Šï¼Œå…¶é€Ÿåº¦è¿œä½ŽäºŽå­˜å‚¨æ ˆä¸­å…¶ä»–ä»»ä½•å±‚ã€‚å…¶ä¸­ä¸€ä¸ªè½¯ä»¶ä¼˜åŒ–ä¾‹å­æ˜¯æ ¹æ®ç¡¬ç›˜ç£
+头当å‰çš„ä½ç½®é‡æ–°æŽ’åºè¯»/写请求。
+
+然而,éšç€å›ºæ€ç¡¬ç›˜å’Œéžæ˜“失性存储的å‘展,它们没有机械部件,也ä¸å­˜åœ¨éšæœºè®¿é—®ä»£
+ç ï¼Œå¹¶èƒ½å¤Ÿè¿›è¡Œé«˜é€Ÿå¹¶è¡Œè®¿é—®ï¼Œå­˜å‚¨æ ˆçš„瓶颈从存储设备转移到了æ“作系统。为了充分
+利用这些设备设计中的并行性,引入了多队列机制。
+
+原æ¥çš„è®¾è®¡åªæœ‰ä¸€ä¸ªé˜Ÿåˆ—æ¥å­˜å‚¨å—设备 I/O 请求,并且åªä½¿ç”¨ä¸€ä¸ªé”。由于缓存中的
+è„æ•°æ®å’Œå¤šå¤„ç†å™¨å…±äº«å•é”的瓶颈,这ç§è®¾è®¡åœ¨ SMP 系统中扩展性ä¸ä½³ã€‚当ä¸åŒè¿›ç¨‹
+(或åŒä¸€è¿›ç¨‹åœ¨ä¸åŒ CPU ä¸Šï¼‰åŒæ—¶æ‰§è¡Œå—设备 I/O 时,该å•队列模型还会出现严é‡
+的拥塞问题。为了解决这些问题,blk-mq API 引入了多个队列,æ¯ä¸ªé˜Ÿåˆ—在本地 CPU
+上拥有独立的入å£ç‚¹ï¼Œä»Žè€Œæ¶ˆé™¤äº†å¯¹å…¨å±€é”的需求。关于其具体工作机制的更深入说明,
+请å‚è§ä¸‹ä¸€èŠ‚ï¼ˆ `工作原ç†`_ )。
+
+工作原ç†
+--------
+
+当用户空间执行对å—设备的 I/O(例如读写文件)时,blk-mq 便会介入:它将存储和
+管ç†å‘é€åˆ°å—设备的 I/O 请求,充当用户空间(文件系统,如果存在的è¯ï¼‰ä¸Žå—设备驱
+动之间的中间层。
+
+blk-mq 由两组队列组æˆï¼šè½¯ä»¶æš‚存队列和硬件派å‘队列。当请求到达å—层时,它会å°
+试最短路径:直接å‘é€åˆ°ç¡¬ä»¶é˜Ÿåˆ—ã€‚ç„¶è€Œï¼Œæœ‰ä¸¤ç§æƒ…况下å¯èƒ½ä¸ä¼šè¿™æ ·åšï¼šå¦‚果该层有
+IO 调度器或者是希望åˆå¹¶è¯·æ±‚ã€‚åœ¨è¿™ä¸¤ç§æƒ…况下,请求将被å‘é€åˆ°è½¯ä»¶é˜Ÿåˆ—。
+
+éšåŽï¼Œåœ¨è½¯ä»¶é˜Ÿåˆ—中的请求被处ç†åŽï¼Œè¯·æ±‚会被放置到硬件队列。硬件队列是第二阶段
+的队列,硬件å¯ä»¥ç›´æŽ¥è®¿é—®å¹¶å¤„ç†è¿™äº›è¯·æ±‚ã€‚ç„¶è€Œï¼Œå¦‚æžœç¡¬ä»¶æ²¡æœ‰è¶³å¤Ÿçš„èµ„æºæ¥æŽ¥å—æ›´
+多请求,blk-mq 会将请求放置在临时队列中,待硬件资æºå……足时å†å‘é€ã€‚
+
+软件暂存队列
+~~~~~~~~~~~~
+
+在这些请求未直接å‘é€åˆ°é©±åŠ¨æ—¶ï¼Œå—设备 I/O å­ç³»ç»Ÿä¼šå°†è¯·æ±‚添加到软件暂存队列中
+(由 struct blk_mq_ctx 表示)。一个请求å¯èƒ½åŒ…å«ä¸€ä¸ªæˆ–多个 BIO。它们通过 struct bio
+æ•°æ®ç»“构到达å—层。å—层éšåŽä¼šåŸºäºŽè¿™äº› BIO 构建新的结构体 struct request,用于
+与设备驱动通信。æ¯ä¸ªé˜Ÿåˆ—都有自己的é”,队列数é‡ç”±æ¯ä¸ª CPU å’Œæ¯ä¸ª node 为基础
+æ¥å†³å®šã€‚
+
+暂存队列å¯ç”¨äºŽåˆå¹¶ç›¸é‚»æ‰‡åŒºçš„请求。例如,对扇区3-6ã€6-7ã€7-9的请求å¯ä»¥åˆå¹¶
+为对扇区3-9的一个请求。å³ä¾¿ SSD 或 NVM çš„éšæœºè®¿é—®å’Œé¡ºåºè®¿é—®å“应时间相åŒï¼Œ
+åˆå¹¶é¡ºåºè®¿é—®çš„请求ä»å¯å‡å°‘å•独请求的数é‡ã€‚è¿™ç§åˆå¹¶è¯·æ±‚的技术称为 plugging。
+
+此外,I/O 调度器还å¯ä»¥å¯¹è¯·æ±‚è¿›è¡Œé‡æ–°æŽ’åºä»¥ç¡®ä¿ç³»ç»Ÿèµ„æºçš„公平性(例如防止æŸ
+个应用出现“饥饿â€çŽ°è±¡ï¼‰æˆ–æ˜¯æé«˜ I/O 性能。
+
+I/O 调度器
+^^^^^^^^^^
+
+å—层实现了多ç§è°ƒåº¦å™¨ï¼Œæ¯ç§è°ƒåº¦å™¨éƒ½éµå¾ªä¸€å®šå¯å‘å¼è§„则以æé«˜ I/O 性能。它们是
+â€œå¯æ’æ‹”â€çš„(plug and play),å¯åœ¨è¿è¡Œæ—¶é€šè¿‡ sysfs 选择。你å¯ä»¥åœ¨è¿™é‡Œé˜…读更
+多关于 Linux IO 调度器知识 `here
+<https://www.kernel.org/doc/html/latest/block/index.html>`_。调度åªå‘
+生在åŒä¸€é˜Ÿåˆ—内的请求之间,因此无法åˆå¹¶ä¸åŒé˜Ÿåˆ—的请求,å¦åˆ™ä¼šé€ æˆç¼“存冲çªå¹¶éœ€
+è¦ä¸ºæ¯ä¸ªé˜Ÿåˆ—加é”。调度åŽï¼Œè¯·æ±‚å³å¯å‘é€åˆ°ç¡¬ä»¶ã€‚å¯èƒ½é€‰æ‹©çš„调度器之一是 NONE è°ƒ
+度器,这是最直接的调度器:它åªå°†è¯·æ±‚放到进程所在的软件队列,ä¸è¿›è¡Œé‡æ–°æŽ’åºã€‚
+当设备开始处ç†ç¡¬ä»¶é˜Ÿåˆ—中的请求时(è¿è¡Œç¡¬ä»¶é˜Ÿåˆ—),映射到该硬件队列的软件队列
+会按映射顺åºä¾æ¬¡æ¸…空。
+
+硬件派å‘队列
+~~~~~~~~~~~~~
+
+硬件队列(由 struct blk_mq_hw_ctx è¡¨ç¤ºï¼‰æ˜¯è®¾å¤‡é©±åŠ¨ç”¨æ¥æ˜ å°„设备æäº¤é˜Ÿåˆ—
+(或设备 DMA 环缓存)的结构体,它是å—层æäº¤è·¯å¾„在底层设备驱动接管请求之å‰çš„
+最åŽä¸€ä¸ªé˜¶æ®µã€‚è¿è¡Œæ­¤é˜Ÿåˆ—时,å—层会从相关软件队列中å–出请求,并å°è¯•æ´¾å‘到硬件。
+
+如果请求无法直接å‘é€åˆ°ç¡¬ä»¶ï¼Œå®ƒä»¬ä¼šè¢«åŠ å…¥åˆ°è¯·æ±‚çš„é“¾è¡¨(``hctx->dispatch``) 中。
+éšåŽï¼Œå½“å—层下次è¿è¡Œè¯¥é˜Ÿåˆ—时,会优先å‘é€ä½äºŽ ``dispatch`` 链表中的请求,
+以确ä¿é‚£äº›æœ€æ—©å‡†å¤‡å¥½å‘é€çš„请求能够得到公平调度。硬件队列的数é‡å–决于硬件åŠ
+其设备驱动所支æŒçš„硬件上下文数,但ä¸ä¼šè¶…过系统的CPU核心数。在这个阶段ä¸
+会å‘ç”Ÿé‡æ–°æŽ’åºï¼Œæ¯ä¸ªè½¯ä»¶é˜Ÿåˆ—都有一组硬件队列æ¥ç”¨äºŽæäº¤è¯·æ±‚。
+
+.. note::
+
+ å—层和设备å议都ä¸ä¿è¯è¯·æ±‚完æˆé¡ºåºã€‚此问题需由更高层处ç†ï¼Œä¾‹å¦‚文件系统。
+
+åŸºäºŽæ ‡è¯†çš„å®Œæˆæœºåˆ¶
+~~~~~~~~~~~~~~~~~~~
+
+为了指示哪一个请求已ç»å®Œæˆï¼Œæ¯ä¸ªè¯·æ±‚都会被分é…一个整数标识,该标识的å–值范围
+是从0到分å‘队列的大å°ã€‚这个标识由å—层生æˆï¼Œå¹¶åœ¨ä¹‹åŽç”±è®¾å¤‡é©±åŠ¨ä½¿ç”¨ï¼Œä»Žè€Œé¿
+å…了为æ¯ä¸ªè¯·æ±‚å†å•ç‹¬åˆ›å»ºå†—ä½™çš„æ ‡è¯†ç¬¦ã€‚å½“è¯·æ±‚åœ¨é©±åŠ¨ä¸­å®Œæˆæ—¶ï¼Œé©±åŠ¨ä¼šå°†è¯¥æ ‡è¯†è¿”
+回给å—层,以通知该请求已完æˆã€‚这样,å—层就无需å†è¿›è¡Œçº¿æ€§æœç´¢æ¥ç¡®å®šæ˜¯å“ªä¸€ä¸ª
+I/O 请求完æˆäº†ã€‚
+
+更多阅读
+--------
+
+- `Linux å— I/O:多队列 SSD å¹¶å‘访问简介 <http://kernel.dk/blk-mq.pdf>`_
+
+- `NOOP 调度器 <https://en.wikipedia.org/wiki/Noop_scheduler>`_
+
+- `Null å—è®¾å¤‡é©±åŠ¨ç¨‹åº <https://www.kernel.org/doc/html/latest/block/null_blk.html>`_
+
+æºä»£ç 
+======
+
+该API在以下内核代ç ä¸­:
+
+include/linux/blk-mq.h
+
+block/blk-mq.c \ No newline at end of file
diff --git a/Documentation/translations/zh_CN/block/data-integrity.rst b/Documentation/translations/zh_CN/block/data-integrity.rst
new file mode 100644
index 000000000000..b31aa9ef8954
--- /dev/null
+++ b/Documentation/translations/zh_CN/block/data-integrity.rst
@@ -0,0 +1,192 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/block/data-integrity.rst
+
+:翻译:
+
+ æŸ¯å­æ° kezijie <kezijie@leap-io-kernel.com>
+
+:校译:
+
+==========
+æ•°æ®å®Œæ•´æ€§
+==========
+
+1. 引言
+=======
+
+现代文件系统对数æ®å’Œå…ƒæ•°æ®éƒ½è¿›è¡Œäº†æ ¡éªŒå’Œä¿æŠ¤ä»¥é˜²æ­¢æ•°æ®æŸåã€‚ç„¶è€Œï¼Œè¿™ç§æŸåçš„
+æ£€æµ‹æ˜¯åœ¨è¯»å–æ—¶æ‰è¿›è¡Œï¼Œè¿™å¯èƒ½å‘生在数æ®å†™å…¥æ•°æœˆä¹‹åŽã€‚到那时,应用程åºå°è¯•写入
+的原始数æ®å¾ˆå¯èƒ½å·²ç»ä¸¢å¤±ã€‚
+
+解决方案是确ä¿ç£ç›˜å®žé™…å­˜å‚¨çš„å†…å®¹å°±æ˜¯åº”ç”¨ç¨‹åºæƒ³å­˜å‚¨çš„。SCSI åè®®æ—(如 SBC
+æ•°æ®å®Œæ•´æ€§å­—段ã€SCC ä¿æŠ¤ææ¡ˆï¼‰ä»¥åŠ SATA/T13ï¼ˆå¤–éƒ¨è·¯å¾„ä¿æŠ¤ï¼‰æœ€è¿‘æ–°å¢žçš„åŠŸèƒ½ï¼Œ
+通过在 I/O 中附加完整性元数æ®çš„æ–¹å¼ï¼Œè¯•图解决这一问题。完整性元数æ®ï¼ˆåœ¨
+SCSI æœ¯è¯­ä¸­ç§°ä¸ºä¿æŠ¤ä¿¡æ¯ï¼‰åŒ…括æ¯ä¸ªæ‰‡åŒºçš„æ ¡éªŒå’Œï¼Œä»¥åŠä¸€ä¸ªé€’增计数器,用于确ä¿
+儿‰‡åŒºæŒ‰æ­£ç¡®é¡ºåºè¢«å†™å…¥ç›˜ã€‚在æŸäº›ä¿æŠ¤æ–¹æ¡ˆä¸­ï¼Œè¿˜èƒ½ä¿è¯ I/O 写入ç£ç›˜çš„æ­£ç¡®ä½ç½®ã€‚
+
+当å‰çš„存储控制器和设备实现了多ç§ä¿æŠ¤æŽªæ–½ï¼Œä¾‹å¦‚æ ¡éªŒå’Œå’Œæ•°æ®æ¸…ç†ã€‚但这些技术通
+常åªåœ¨å„自的独立域内工作,或最多仅在 I/O è·¯å¾„çš„ç›¸é‚»èŠ‚ç‚¹ä¹‹é—´å‘æŒ¥ä½œç”¨ã€‚DIF åŠ
+其它数æ®å®Œæ•´æ€§æ‹“å±•æœ‰æ„æ€çš„ç‚¹åœ¨äºŽä¿æŠ¤æ ¼å¼å®šä¹‰æ˜Žç¡®ï¼ŒI/O 路径上的æ¯ä¸ªèŠ‚ç‚¹éƒ½å¯ä»¥
+éªŒè¯ I/O 的完整性,如检测到æŸåå¯ç›´æŽ¥æ‹’ç»ã€‚è¿™ä¸ä»…å¯ä»¥é˜²æ­¢æ•°æ®æŸå,还能够隔
+离故障点。
+
+2. æ•°æ®å®Œæ•´æ€§æ‹“展
+=================
+
+如上所述,这些å议扩展åªä¿æŠ¤æŽ§åˆ¶å™¨ä¸Žå­˜å‚¨è®¾å¤‡ä¹‹é—´çš„路径。然而,许多控制器实际
+上å…许æ“作系统与完整性元数æ®(IMD)交互。我们一直与多家 FC/SAS HBA 厂商åˆä½œï¼Œ
+ä½¿ä¿æŠ¤ä¿¡æ¯èƒ½å¤Ÿåœ¨å…¶æŽ§åˆ¶å™¨ä¸Žæ“作系统之间传输。
+
+SCSI æ•°æ®å®Œæ•´æ€§å­—段通过在æ¯ä¸ªæ‰‡åŒºåŽé™„加8å­—èŠ‚çš„ä¿æŠ¤ä¿¡æ¯æ¥å®žçŽ°ã€‚æ•°æ® + 完整
+性元数æ®å­˜å‚¨åœ¨ç£ç›˜çš„520å­—èŠ‚æ‰‡åŒºä¸­ã€‚æ•°æ® + IMD 在控制器与目标设备之间传输
+时是交错组åˆåœ¨ä¸€èµ·çš„。T13 ææ¡ˆçš„æ–¹å¼ç±»ä¼¼ã€‚
+
+由于æ“作系统处ç†520字节(甚至 4104 字节)扇区éžå¸¸ä¸ä¾¿ï¼Œæˆ‘们è”系了多家 HBA
+厂商,并鼓励它们分离数æ®ä¸Žå®Œæ•´æ€§å…ƒæ•°æ®çš„ scatter-gather lists。
+
+控制器在写入时会将数æ®ç¼“冲区和完整性元数æ®ç¼“冲区的数æ®äº¤é”™åœ¨ä¸€èµ·ï¼Œå¹¶åœ¨è¯»å–æ—¶
+会拆分它们。这样,Linux 就能直接通过 DMA 将数æ®ç¼“冲区传输到主机内存或从主机
+内存读å–,而无需修改页缓存。
+
+此外,SCSI 与 SATA è§„èŒƒè¦æ±‚çš„16ä½ CRC 校验在软件中计算代价较高。基准测试å‘
+现,计算此校验在高负载情形下显著影å“系统性能。一些控制器å…许在æ“作系统接å£å¤„
+使用轻é‡çº§æ ¡éªŒã€‚例如 Emulex æ”¯æŒ TCP/IP 校验。æ“作系统æä¾›çš„ IP 校验在写入
+时会转æ¢ä¸º16ä½ CRCï¼Œè¯»å–æ—¶åˆ™ç›¸å。这å…许 Linux 或应用程åºä»¥æžä½Žçš„开销生æˆ
+完整性元数æ®ï¼ˆä¸Žè½¯ä»¶ RAID5 相当)。
+
+IP 校验在检测ä½é”™è¯¯æ–¹é¢æ¯” CRC 弱,但关键在于数æ®ç¼“冲区与完整性元数æ®ç¼“冲区
+çš„åˆ†ç¦»ã€‚åªæœ‰è¿™ä¸¤ä¸ªä¸åŒçš„缓冲区匹é…,I/O æ‰èƒ½å®Œæˆã€‚
+
+æ•°æ®ä¸Žå®Œæ•´æ€§å…ƒæ•°æ®ç¼“å†²åŒºçš„åˆ†ç¦»ä»¥åŠæ ¡éªŒé€‰æ‹©è¢«ç§°ä¸ºæ•°æ®å®Œæ•´æ€§æ‰©å±•。由于这些扩展
+超出了å议主体(T10ã€T13)的范围,Oracle åŠå…¶åˆä½œä¼™ä¼´æ­£å°è¯•在存储网络行业å
+会内对其进行标准化。
+
+3. å†…æ ¸å˜æ›´
+===========
+
+Linux 中的数æ®å®Œæ•´æ€§æ¡†æž¶å…è®¸å°†ä¿æŠ¤ä¿¡æ¯å›ºå®šåˆ° I/O 上,并在支æŒè¯¥åŠŸèƒ½çš„æŽ§åˆ¶å™¨
+之间å‘é€å’ŒæŽ¥æ”¶ã€‚
+
+SCSI å’Œ SATA ä¸­å®Œæ•´æ€§æ‰©å±•çš„ä¼˜åŠ¿åœ¨äºŽï¼Œå®ƒä»¬èƒ½å¤Ÿä¿æŠ¤ä»Žåº”ç”¨ç¨‹åºåˆ°å­˜å‚¨è®¾å¤‡çš„æ•´ä¸ª
+è·¯å¾„ã€‚ç„¶è€Œï¼Œè¿™åŒæ—¶ä¹Ÿæ˜¯æœ€å¤§çš„劣势。这æ„味ç€ä¿æŠ¤ä¿¡æ¯å¿…须采用ç£ç›˜å¯ä»¥ç†è§£çš„æ ¼å¼ã€‚
+
+通常,Linux/POSIX 应用程åºå¹¶ä¸å…³å¿ƒæ‰€è®¿é—®å­˜å‚¨è®¾å¤‡çš„具体细节。虚拟文件系统层
+å’Œå—层会让硬件扇区大å°å’Œä¼ è¾“å议对应用程åºå®Œå…¨é€æ˜Žã€‚
+
+然而,在准备å‘é€åˆ°ç£ç›˜çš„ä¿æŠ¤ä¿¡æ¯æ—¶ï¼Œå°±éœ€è¦è¿™ç§ç»†èŠ‚ã€‚å› æ­¤ï¼Œç«¯åˆ°ç«¯ä¿æŠ¤æ–¹æ¡ˆçš„æ¦‚
+念实际上è¿å了层次结构。应用程åºå®Œå…¨ä¸åº”该知é“它访问的是 SCSI 还是 SATA ç£ç›˜ã€‚
+
+Linux 中实现的数æ®å®Œæ•´æ€§æ”¯æŒå°è¯•将这些细节对应用程åºéšè—。就应用程åºï¼ˆä»¥åŠåœ¨
+æŸç§ç¨‹åº¦ä¸Šå†…æ ¸ï¼‰è€Œè¨€ï¼Œå®Œæ•´æ€§å…ƒæ•°æ®æ˜¯é™„加在 I/O 上的ä¸é€æ˜Žä¿¡æ¯ã€‚
+
+当å‰å®žçްå…许å—层自动为任何 I/O 生æˆä¿æŠ¤ä¿¡æ¯ã€‚最终目标是将用户数æ®çš„完整性元
+æ•°æ®è®¡ç®—移至用户空间。内核中产生的元数æ®å’Œå…¶ä»– I/O ä»å°†ä½¿ç”¨è‡ªåŠ¨ç”ŸæˆæŽ¥å£ã€‚
+
+一些存储设备å…许为æ¯ä¸ªç¡¬ä»¶æ‰‡åŒºé™„加一个16ä½çš„æ ‡è¯†å€¼ã€‚这个标识空间的所有者是
+å—设备的所有者,也就是在多数情况下由文件系统掌控。文件系统å¯ä»¥åˆ©ç”¨è¿™é¢å¤–空间
+按需为扇区附加标识。由于标识空间有é™ï¼Œå—接å£å…许通过交错方å¼å¯¹æ›´å¤§çš„æ•°æ®å—æ ‡
+识。这样,8*16ä½çš„ä¿¡æ¯å¯ä»¥é™„加到典型的 4KB 文件系统å—上。
+
+这也æ„味ç€è¯¸å¦‚ fsck å’Œ mkfs 等应用程åºéœ€è¦èƒ½å¤Ÿä»Žç”¨æˆ·ç©ºé—´è®¿é—®å¹¶æ“作这些标记。
+为此,正在开å‘一个é€ä¼ æŽ¥å£ã€‚
+
+4. å—层实现细节
+===============
+
+4.1 Bio
+--------
+
+当å¯ç”¨ CONFIG_BLK_DEV_INTEGRITY 时,数æ®å®Œæ•´æ€§è¡¥ä¸ä¼šåœ¨ struct bio 中添加
+一个新字段。调用 bio_integrity(bio) ä¼šè¿”å›žä¸€ä¸ªæŒ‡å‘ struct bip 的指针,该
+结构体包å«äº†è¯¥ bio 的完整性负载。本质上,bip 是一个精简版的 struct bio,其
+中包å«ä¸€ä¸ª bio_vec,用于ä¿å­˜å®Œæ•´æ€§å…ƒæ•°æ®ä»¥åŠæ‰€éœ€çš„维护信æ¯ï¼ˆbvec æ± ã€å‘é‡è®¡
+数等)。
+
+内核å­ç³»ç»Ÿå¯ä»¥é€šè¿‡è°ƒç”¨ bio_integrity_alloc(bio) æ¥ä¸ºæŸä¸ª bio å¯ç”¨æ•°æ®å®Œæ•´
+æ€§ä¿æŠ¤ã€‚è¯¥å‡½æ•°ä¼šåˆ†é…并附加一个 bip 到该 bio 上。
+
+éšåŽä½¿ç”¨ bio_integrity_add_page() 将包å«å®Œæ•´æ€§å…ƒæ•°æ®çš„å•独页é¢é™„加到该 bio。
+
+调用 bio_free() 会自动释放bip。
+
+4.2 å—设备
+-----------
+
+å—设备å¯ä»¥åœ¨ queue_limits 结构中的 integrity å­ç»“构中设置完整性信æ¯ã€‚
+
+对于分层å—设备,需è¦é€‰æ‹©ä¸€ä¸ªé€‚用于所有å­è®¾å¤‡çš„完整性é…置文件。å¯ä»¥ä½¿ç”¨
+queue_limits_stack_integrity() æ¥å助完æˆè¯¥æ“作。目å‰ï¼ŒDM å’Œ MD linearã€
+RAID0 å’Œ RAID1 已嗿”¯æŒã€‚而RAID4/5/6因涉åŠåº”用标签ä»éœ€é¢å¤–的开å‘工作。
+
+5.0 å—层完整性API
+==================
+
+5.1 普通文件系统
+-----------------
+
+ 普通文件系统并ä¸çŸ¥é“其下层å—设备具备å‘逿ˆ–接收完整性元数æ®çš„能力。
+ 在执行写æ“作时,å—层会在调用 submit_bio() 时自动生æˆå®Œæ•´æ€§å…ƒæ•°æ®ã€‚
+ 在执行读æ“作时,I/O 完æˆåŽä¼šè§¦å‘完整性验è¯ã€‚
+
+ IMD 的生æˆä¸ŽéªŒè¯è¡Œä¸ºå¯ä»¥é€šè¿‡ä»¥ä¸‹å¼€å…³æŽ§åˆ¶::
+
+ /sys/block/<bdev>/integrity/write_generate
+
+ and::
+
+ /sys/block/<bdev>/integrity/read_verify
+
+ flags.
+
+5.2 具备完整性感知的文件系统
+----------------------------
+
+ 具备完整性感知能力的文件系统å¯ä»¥åœ¨å‡†å¤‡ I/O 时附加完整性元数æ®ï¼Œ
+ 并且如果底层å—设备支æŒåº”用标签空间,也å¯ä»¥åŠ ä»¥åˆ©ç”¨ã€‚
+
+
+ `bool bio_integrity_prep(bio);`
+
+ è¦ä¸ºå†™æ“作生æˆå®Œæ•´æ€§å…ƒæ•°æ®æˆ–为读æ“作设置缓冲区,文件系统必须调用
+ bio_integrity_prep(bio)。
+
+ 在调用此函数之å‰ï¼Œå¿…须先设置好 bio çš„æ•°æ®æ–¹å‘和起始扇区,并确
+ ä¿è¯¥ bio å·²ç»æ·»åŠ å®Œæ‰€æœ‰çš„æ•°æ®é¡µã€‚调用者需è¦è‡ªè¡Œä¿è¯ï¼Œåœ¨ I/O 进行
+ 期间 bio ä¸ä¼šè¢«ä¿®æ”¹ã€‚如果由于æŸç§åŽŸå› å‡†å¤‡å¤±è´¥ï¼Œåˆ™åº”å½“ä»¥é”™è¯¯çŠ¶æ€
+ 完æˆè¯¥ bio。
+
+5.3 传递已有的完整性元数æ®
+--------------------------
+
+ 能够自行生æˆå®Œæ•´æ€§å…ƒæ•°æ®æˆ–å¯ä»¥ä»Žç”¨æˆ·ç©ºé—´ä¼ è¾“完整性元数æ®çš„æ–‡ä»¶ç³»ç»Ÿï¼Œ
+ å¯ä»¥ä½¿ç”¨å¦‚下接å£ï¼š
+
+
+ `struct bip * bio_integrity_alloc(bio, gfp_mask, nr_pages);`
+
+ 为 bio 分é…完整性负载并挂载到 bio 上。nr_pages 表示需è¦åœ¨
+ integrity bio_vec list ä¸­å­˜å‚¨å¤šå°‘é¡µä¿æŠ¤æ•°æ®ï¼ˆç±»ä¼¼ bio_alloc)。
+
+ 完整性负载将在 bio_free() 被调用时释放。
+
+
+ `int bio_integrity_add_page(bio, page, len, offset);`
+
+ 将包å«å®Œæ•´æ€§å…ƒæ•°æ®çš„一页附加到已有的 bio 上。该 bio 必须已有 bip,
+ å³å¿…须先调用 bio_integrity_alloc()。对于写æ“作,页中的完整
+ 性元数æ®å¿…须采用目标设备å¯è¯†åˆ«çš„æ ¼å¼ï¼Œä½†æœ‰ä¸€ä¸ªä¾‹å¤–,当请求在 I/O æ ˆ
+ 中传递时,扇区å·ä¼šè¢«é‡æ–°æ˜ å°„。这æ„味ç€é€šè¿‡æ­¤æŽ¥å£æ·»åŠ çš„é¡µåœ¨ I/O 过程
+ 中å¯èƒ½ä¼šè¢«ä¿®æ”¹ï¼å®Œæ•´æ€§å…ƒæ•°æ®ä¸­çš„第一个引用标签必须等于 bip->bip_sector。
+
+ åªè¦ bip bio_vec array(nr_pages)有空间,就å¯ä»¥ç»§ç»­é€šè¿‡
+ bio_integrity_add_page()添加页。
+
+ 当读æ“作完æˆåŽï¼Œé™„加的页将包å«ä»Žå­˜å‚¨è®¾å¤‡æŽ¥æ”¶åˆ°çš„完整性元数æ®ã€‚
+ 接收方需è¦å¤„ç†è¿™äº›å…ƒæ•°æ®ï¼Œå¹¶åœ¨æ“ä½œå®Œæˆæ—¶éªŒè¯æ•°æ®å®Œæ•´æ€§
+
+
+----------------------------------------------------------------------
+
+2007-12-24 Martin K. Petersen <martin.petersen@oracle.com> \ No newline at end of file
diff --git a/Documentation/translations/zh_CN/block/index.rst b/Documentation/translations/zh_CN/block/index.rst
new file mode 100644
index 000000000000..f2ae5096ed68
--- /dev/null
+++ b/Documentation/translations/zh_CN/block/index.rst
@@ -0,0 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/block/index.rst
+
+:翻译:
+
+ æŸ¯å­æ° ke zijie <kezijie@leap-io-kernel.com>
+
+:校译:
+
+=====
+Block
+=====
+
+.. toctree::
+ :maxdepth: 1
+
+ blk-mq
+ data-integrity
+
+TODOList:
+* bfq-iosched
+* biovecs
+* cmdline-partition
+* deadline-iosched
+* inline-encryption
+* ioprio
+* kyber-iosched
+* null_blk
+* pr
+* stat
+* switching-sched
+* writeback_cache_control
+* ublk
diff --git a/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst b/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst
index 282aacd33442..0b382a32b3fe 100644
--- a/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst
+++ b/Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst
@@ -2,7 +2,7 @@
.. include:: ../disclaimer-zh_CN.rst
-:Original: Documentation/dev-tools/gdb-kernel-debugging.rst
+:Original: Documentation/process/debugging/gdb-kernel-debugging.rst
:Translator: 高超 gao chao <gaochao49@huawei.com>
通过gdb调试内核和模å—
diff --git a/Documentation/translations/zh_CN/doc-guide/checktransupdate.rst b/Documentation/translations/zh_CN/doc-guide/checktransupdate.rst
index d20b4ce66b9f..dbfd65398077 100644
--- a/Documentation/translations/zh_CN/doc-guide/checktransupdate.rst
+++ b/Documentation/translations/zh_CN/doc-guide/checktransupdate.rst
@@ -28,15 +28,15 @@
::
- ./scripts/checktransupdate.py --help
+ tools/docs/checktransupdate.py --help
具体用法请å‚è€ƒå‚æ•°è§£æžå™¨çš„输出
示例
-- ``./scripts/checktransupdate.py -l zh_CN``
+- ``tools/docs/checktransupdate.py -l zh_CN``
è¿™å°†æ‰“å° zh_CN è¯­è¨€ä¸­éœ€è¦æ›´æ–°çš„æ‰€æœ‰æ–‡ä»¶ã€‚
-- ``./scripts/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst``
+- ``tools/docs/checktransupdate.py Documentation/translations/zh_CN/dev-tools/testing-overview.rst``
è¿™å°†åªæ‰“å°æŒ‡å®šæ–‡ä»¶çš„状æ€ã€‚
ç„¶åŽè¾“出类似如下的内容:
diff --git a/Documentation/translations/zh_CN/doc-guide/contributing.rst b/Documentation/translations/zh_CN/doc-guide/contributing.rst
index 394a13b438b0..b0c8ba782b16 100644
--- a/Documentation/translations/zh_CN/doc-guide/contributing.rst
+++ b/Documentation/translations/zh_CN/doc-guide/contributing.rst
@@ -124,7 +124,7 @@ C代ç ç¼–译器å‘出的警告常常会被视为误报,从而导致出现了æ
è¿™ä½¿å¾—è¿™äº›ä¿¡æ¯æ›´é𾿉¾åˆ°ï¼Œä¾‹å¦‚使Sphinxæ— æ³•ç”ŸæˆæŒ‡å‘该文档的链接。将 ``kernel-doc``
指令添加到文档中以引入这些注释å¯ä»¥å¸®åŠ©ç¤¾åŒºèŽ·å¾—ä¸ºç¼–å†™æ³¨é‡Šæ‰€åšå·¥ä½œçš„全部价值。
-``scripts/find-unused-docs.sh`` 工具å¯ä»¥ç”¨æ¥æ‰¾åˆ°è¿™äº›è¢«å¿½ç•¥çš„评论。
+``tools/docs/find-unused-docs.sh`` 工具å¯ä»¥ç”¨æ¥æ‰¾åˆ°è¿™äº›è¢«å¿½ç•¥çš„评论。
请注æ„,将导出的函数和数æ®ç»“构引入文档是最有价值的。许多å­ç³»ç»Ÿè¿˜å…·æœ‰ä¾›å†…部
使用的kernel-doc注释;除éžè¿™äº›æ³¨é‡Šæ”¾åœ¨ä¸“门针对相关å­ç³»ç»Ÿå¼€å‘人员的文档中,
diff --git a/Documentation/translations/zh_CN/doc-guide/parse-headers.rst b/Documentation/translations/zh_CN/doc-guide/parse-headers.rst
index a08819e904ed..65d9dc5143ff 100644
--- a/Documentation/translations/zh_CN/doc-guide/parse-headers.rst
+++ b/Documentation/translations/zh_CN/doc-guide/parse-headers.rst
@@ -13,20 +13,20 @@
有时,为了æè¿°ç”¨æˆ·ç©ºé—´API并在代ç å’Œæ–‡æ¡£ä¹‹é—´ç”Ÿæˆäº¤å‰å¼•用,需è¦åŒ…å«å¤´æ–‡ä»¶å’Œç¤ºä¾‹
C代ç ã€‚为用户空间API文件添加交å‰å¼•用还有一个好处:如果在文档中找ä¸åˆ°ç›¸åº”符å·ï¼Œ
Sphinx将生æˆè­¦å‘Šã€‚è¿™æœ‰åŠ©äºŽä¿æŒç”¨æˆ·ç©ºé—´APIæ–‡æ¡£ä¸Žå†…æ ¸æ›´æ”¹åŒæ­¥ã€‚
-:ref:`parse_headers.pl <parse_headers_zh>` æä¾›äº†ç”Ÿæˆæ­¤ç±»äº¤å‰å¼•ç”¨çš„ä¸€ç§æ–¹æ³•。
+:ref:`parse_headers.py <parse_headers_zh>` æä¾›äº†ç”Ÿæˆæ­¤ç±»äº¤å‰å¼•ç”¨çš„ä¸€ç§æ–¹æ³•。
在构建文档时,必须通过Makefile调用它。有关如何在内核树中使用它的示例,请å‚阅
``Documentation/userspace-api/media/Makefile`` 。
.. _parse_headers_zh:
-parse_headers.pl
+parse_headers.py
----------------
脚本åç§°
~~~~~~~~
-parse_headers.pl——解æžä¸€ä¸ªC文件,识别函数ã€ç»“æž„ä½“ã€æžšä¸¾ã€å®šä¹‰å¹¶å¯¹Sphinx文档
+parse_headers.py——解æžä¸€ä¸ªC文件,识别函数ã€ç»“æž„ä½“ã€æžšä¸¾ã€å®šä¹‰å¹¶å¯¹Sphinx文档
创建交å‰å¼•用。
@@ -34,7 +34,7 @@ parse_headers.pl——解æžä¸€ä¸ªC文件,识别函数ã€ç»“æž„ä½“ã€æžšä¸¾ã€
~~~~~~~~
-\ **parse_headers.pl**\ [<选项>] <C文件> <输出文件> [<例外文件>]
+\ **parse_headers.py**\ [<选项>] <C文件> <输出文件> [<例外文件>]
<选项> å¯ä»¥æ˜¯ï¼š --debug, --help 或 --usage 。
diff --git a/Documentation/translations/zh_CN/doc-guide/sphinx.rst b/Documentation/translations/zh_CN/doc-guide/sphinx.rst
index 23eac67fbc30..3375c6f3a811 100644
--- a/Documentation/translations/zh_CN/doc-guide/sphinx.rst
+++ b/Documentation/translations/zh_CN/doc-guide/sphinx.rst
@@ -84,7 +84,7 @@ PDF和LaTeX构建
这有一个脚本å¯ä»¥è‡ªåŠ¨æ£€æŸ¥Sphinxä¾èµ–项。如果它认得您的å‘行版,还会æç¤ºæ‚¨æ‰€ç”¨å‘行
版的安装命令::
- $ ./scripts/sphinx-pre-install
+ $ ./tools/docs/sphinx-pre-install
Checking if the needed tools for Fedora release 26 (Twenty Six) are available
Warning: better to also install "texlive-luatex85".
You should run:
@@ -94,7 +94,7 @@ PDF和LaTeX构建
. sphinx_2.4.4/bin/activate
pip install -r Documentation/sphinx/requirements.txt
- Can't build as 1 mandatory dependency is missing at ./scripts/sphinx-pre-install line 468.
+ Can't build as 1 mandatory dependency is missing at ./tools/docs/sphinx-pre-install line 468.
默认情况下,它会检查htmlå’ŒPDF的所有ä¾èµ–项,包括图åƒã€æ•°å­¦è¡¨è¾¾å¼å’ŒLaTeX构建的
需求,并å‡è®¾å°†ä½¿ç”¨è™šæ‹ŸPython环境。html构建所需的ä¾èµ–项被认为是必需的,其他ä¾
diff --git a/Documentation/translations/zh_CN/filesystems/dnotify.rst b/Documentation/translations/zh_CN/filesystems/dnotify.rst
new file mode 100644
index 000000000000..5ab109b9424c
--- /dev/null
+++ b/Documentation/translations/zh_CN/filesystems/dnotify.rst
@@ -0,0 +1,67 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/filesystems/dnotify.rst
+
+:翻译:
+
+ çŽ‹é¾™æ° Wang Longjie <wang.longjie1@zte.com.cn>
+
+==============
+Linux 目录通知
+==============
+
+ Stephen Rothwell <sfr@canb.auug.org.au>
+
+目录通知的目的是使用户应用程åºèƒ½å¤Ÿåœ¨ç›®å½•或目录中的任何文件å‘ç”Ÿå˜æ›´æ—¶æ”¶åˆ°é€šçŸ¥ã€‚基本机制包括应用程åº
+通过 fcntl(2) 调用在目录上注册通知,通知本身则通过信å·ä¼ é€’。
+
+应用程åºå¯ä»¥å†³å®šå¸Œæœ›æ”¶åˆ°å“ªäº› “事件†的通知。当å‰å·²å®šä¹‰çš„事件如下:
+
+ ========= =====================================
+ DN_ACCESS 目录中的文件被访问(read)
+ DN_MODIFY 目录中的文件被修改(write,truncate)
+ DN_CREATE 目录中创建了文件
+ DN_DELETE ç›®å½•ä¸­çš„æ–‡ä»¶è¢«å–æ¶ˆé“¾æŽ¥
+ DN_RENAME 目录中的文件被é‡å‘½å
+ DN_ATTRIB 目录中的文件属性被更改(chmod,chown)
+ ========= =====================================
+
+通常,应用程åºå¿…é¡»åœ¨æ¯æ¬¡é€šçŸ¥åŽé‡æ–°æ³¨å†Œï¼Œä½†å¦‚果将 DN_MULTISHOT 与事件掩ç è¿›è¡Œæˆ–è¿ç®—,则注册
+å°†ä¸€ç›´ä¿æŒæœ‰æ•ˆï¼Œç›´åˆ°è¢«æ˜¾å¼ç§»é™¤ï¼ˆé€šè¿‡æ³¨å†Œä¸ºä¸æŽ¥æ”¶ä»»ä½•事件)。
+
+默认情况下,SIGIO ä¿¡å·å°†è¢«ä¼ é€’给进程,且ä¸é™„带其他有用的信æ¯ã€‚但是,如果使用 F_SETSIG fcntl(2)
+调用让内核知é“è¦ä¼ é€’哪个信å·ï¼Œä¸€ä¸ª siginfo 结构体将被传递给信å·å¤„ç†ç¨‹åºï¼Œè¯¥ç»“构体的 si_fd æˆå‘˜å°†
+包å«ä¸Žå‘生事件的目录相关è”的文件æè¿°ç¬¦ã€‚
+
+åº”ç”¨ç¨‹åºæœ€å¥½é€‰æ‹©ä¸€ä¸ªå®žæ—¶ä¿¡å·ï¼ˆSIGRTMIN + <n>),以便通知å¯ä»¥è¢«æŽ’队。如果指定了 DN_MULTISHOT,
+这一点尤为é‡è¦ã€‚注æ„,SIGRTMIN 通常是被阻塞的,因此最好使用(至少)SIGRTMIN + 1。
+
+实现预期(特性与缺陷 :-))
+--------------------------
+
+对于文件的任何本地访问,通知都应能正常工作,å³ä½¿å®žé™…文件系统ä½äºŽè¿œç¨‹æœåŠ¡å™¨ä¸Šã€‚è¿™æ„味ç€ï¼Œå¯¹æœ¬åœ°ç”¨æˆ·
+æ¨¡å¼æœåС噍æä¾›çš„æ–‡ä»¶çš„远程访问应能触å‘é€šçŸ¥ã€‚åŒæ ·çš„,对本地内核 NFS æœåС噍æä¾›çš„æ–‡ä»¶çš„远程访问
+也应能触å‘通知。
+
+为了尽å¯èƒ½å‡å°å¯¹æ–‡ä»¶ç³»ç»Ÿä»£ç çš„å½±å“,文件硬链接的问题已被忽略。因此,如果一个文件(x)存在于两个
+目录(a å’Œ b)中,通过åç§°â€a/xâ€å¯¹è¯¥æ–‡ä»¶è¿›è¡Œçš„æ›´æ”¹åº”通知给期望接收目录“aâ€é€šçŸ¥çš„程åºï¼Œä½†ä¸ä¼š
+通知给期望接收目录“bâ€é€šçŸ¥çš„程åºã€‚
+
+æ­¤å¤–ï¼Œå–æ¶ˆé“¾æŽ¥çš„æ–‡ä»¶ä»ä¼šåœ¨å®ƒä»¬é“¾æŽ¥åˆ°çš„æœ€åŽä¸€ä¸ªç›®å½•中触å‘通知。
+
+é…ç½®
+----
+
+Dnotify ç”± CONFIG_DNOTIFY é…置选项控制。ç¦ç”¨è¯¥é€‰é¡¹æ—¶ï¼Œfcntl(fd, F_NOTIFY, ...) 将返
+回 -EINVAL。
+
+示例
+----
+具体示例å¯å‚è§ tools/testing/selftests/filesystems/dnotify_test.c。
+
+注æ„
+----
+从 Linux 2.6.13 开始,dnotify 已被 inotify å–代。有关 inotify 的更多信æ¯ï¼Œè¯·å‚è§
+Documentation/filesystems/inotify.rst。
diff --git a/Documentation/translations/zh_CN/filesystems/gfs2-glocks.rst b/Documentation/translations/zh_CN/filesystems/gfs2-glocks.rst
new file mode 100644
index 000000000000..abfd2f2f94e9
--- /dev/null
+++ b/Documentation/translations/zh_CN/filesystems/gfs2-glocks.rst
@@ -0,0 +1,211 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/filesystems/gfs2-glocks.rst
+
+:翻译:
+
+ 邵明寅 Shao Mingyin <shao.mingyin@zte.com.cn>
+
+:校译:
+
+ æ¨æ¶› yang tao <yang.tao172@zte.com.cn>
+
+==================
+Glock 内部加é”规则
+==================
+
+本文档é˜è¿° glock çŠ¶æ€æœºå†…部è¿ä½œçš„基本原ç†ã€‚æ¯ä¸ª glock(å³
+fs/gfs2/incore.h 中的 struct gfs2_glock)包å«ä¸¤æŠŠä¸»è¦çš„内部é”:
+
+ 1. 自旋é”(gl_lockref.lockï¼‰ï¼šç”¨äºŽä¿æŠ¤å†…éƒ¨çŠ¶æ€ï¼ˆå¦‚
+ gl_stateã€gl_targetï¼‰å’ŒæŒæœ‰è€…列表(gl_holders)
+ 2. éžé˜»å¡žçš„ä½é”(GLF_LOCKï¼‰ï¼šç”¨äºŽé˜²æ­¢å…¶ä»–çº¿ç¨‹åŒæ—¶è°ƒç”¨
+ DLM ç­‰æ“作。若æŸçº¿ç¨‹èŽ·å–æ­¤é”,则在释放时必须调用
+ run_queueï¼ˆé€šå¸¸é€šè¿‡å·¥ä½œé˜Ÿåˆ—ï¼‰ï¼Œä»¥ç¡®ä¿æ‰€æœ‰å¾…处ç†ä»»åŠ¡
+ 得以完æˆã€‚
+
+gl_holders 列表包å«ä¸Žè¯¥ glock å…³è”的所有排队é”请求(ä¸
+ä»…æ˜¯æŒæœ‰è€…ï¼‰ã€‚è‹¥å­˜åœ¨å·²æŒæœ‰çš„é”,它们将ä½äºŽåˆ—表开头的连
+ç»­æ¡ç›®ä¸­ã€‚é”的授予严格éµå¾ªæŽ’队顺åºã€‚
+
+glock 层用户å¯è¯·æ±‚三ç§é”状æ€ï¼šå…±äº«ï¼ˆSH)ã€å»¶è¿Ÿï¼ˆDF)和
+排他(EX)。它们对应以下 DLM 锿¨¡å¼ï¼š
+
+========== ====== =====================================================
+Glock æ¨¡å¼ DLM 锿¨¡å¼
+========== ====== =====================================================
+ UN IV/NL 未加é”(无关è”çš„ DLM é”)或 NL
+ SH PR å—ä¿æŠ¤è¯»ï¼ˆProtected read)
+ DF CW å¹¶å‘写(Concurrent write)
+ EX EX 排他(Exclusive)
+========== ====== =====================================================
+
+因此,DF 本质上是一ç§ä¸Žâ€œå¸¸è§„â€å…±äº«é”模å¼ï¼ˆSH)互斥的共
+享模å¼ã€‚在 GFS2 中,DF 模å¼ä¸“用于直接 I/O æ“作。Glock
+本质上是é”加缓存管ç†ä¾‹ç¨‹çš„组åˆï¼Œå…¶ç¼“存规则如下:
+
+========== ============== ========== ========== ==============
+Glock æ¨¡å¼ ç¼“å­˜å…ƒæ•°æ® ç¼“å­˜æ•°æ® è„æ•°æ® è„元数æ®
+========== ============== ========== ========== ==============
+ UN å¦ å¦ å¦ å¦
+ DF 是 å¦ å¦ å¦
+ SH 是 是 å¦ å¦
+ EX 是 是 是 是
+========== ============== ========== ========== ==============
+
+这些规则通过为æ¯ç§ glock 定义的æ“ä½œå‡½æ•°å®žçŽ°ã€‚å¹¶éžæ‰€æœ‰
+glock 类型都使用全部的模å¼ï¼Œä¾‹å¦‚ä»… inode glock 使用 DF 模
+å¼ã€‚
+
+glock æ“作函数åŠç±»åž‹å¸¸é‡è¯´æ˜Žè¡¨ï¼š
+
+============== ========================================================
+字段 用途
+============== ========================================================
+go_sync 远程状æ€å˜æ›´å‰è°ƒç”¨ï¼ˆå¦‚åŒæ­¥è„æ•°æ®ï¼‰
+go_xmote_bh 远程状æ€å˜æ›´åŽè°ƒç”¨ï¼ˆå¦‚刷新缓存)
+go_inval 远程状æ€å˜æ›´éœ€ä½¿ç¼“存失效时调用
+go_instantiate èŽ·å– glock 时调用
+go_held æ¯æ¬¡èŽ·å– glock æŒæœ‰è€…时调用
+go_dump 为 debugfs 文件打å°å¯¹è±¡å†…容,或出错时将 glock 转储至日志
+go_callback è‹¥ DLM å‘é€å›žè°ƒä»¥é‡Šæ”¾æ­¤é”时调用
+go_unlocked 当 glock è§£é”æ—¶è°ƒç”¨ï¼ˆdlm_unlock())
+go_type glock 类型,``LM_TYPE_*``
+go_flags è‹¥ glock å…³è”地å€ç©ºé—´ï¼Œåˆ™è®¾ç½®GLOF_ASPACE 标志
+============== ========================================================
+
+æ¯ç§é”çš„æœ€çŸ­æŒæœ‰æ—¶é—´æ˜¯æŒ‡åœ¨è¿œç¨‹é”授予åŽå¿½ç•¥è¿œç¨‹é™çº§è¯·æ±‚
+的时间段。此举旨在防止é”在集群节点间æŒç»­å¼¹è·³è€Œæ— å®žè´¨è¿›
+展的情况,此现象常è§äºŽå¤šèŠ‚ç‚¹å†™å…¥çš„å…±äº«å†…å­˜æ˜ å°„æ–‡ä»¶ã€‚é€š
+过延迟å“应远程回调的é™çº§æ“作,为用户空间程åºäº‰å–页é¢å–
+消映射å‰çš„å¤„ç†æ—¶é—´ã€‚
+
+未æ¥è®¡åˆ’å°† glock çš„ "EX" 模å¼è®¾ä¸ºæœ¬åœ°å…±äº«ï¼Œä½¿æœ¬åœ°é”通
+过 i_mutex å®žçŽ°è€Œéž glock。
+
+glock æ“作函数的加é”规则:
+
+============== ====================== =============================
+æ“作 GLF_LOCK ä½é”æŒæœ‰ gl_lockref.lock è‡ªæ—‹é”æŒæœ‰
+============== ====================== =============================
+go_sync 是 å¦
+go_xmote_bh 是 å¦
+go_inval 是 å¦
+go_instantiate å¦ å¦
+go_held å¦ å¦
+go_dump 有时 是
+go_callback 有时(N/A) 是
+go_unlocked 是 å¦
+============== ====================== =============================
+
+.. Note::
+
+ 若入å£å¤„æŒæœ‰é”则æ“作期间ä¸å¾—释放ä½é”或自旋é”。
+ go_dump å’Œ do_demote_ok 严ç¦é˜»å¡žã€‚
+ 仅当 glock çŠ¶æ€æŒ‡ç¤ºå…¶ç¼“å­˜æœ€æ–°æ•°æ®æ—¶æ‰ä¼šè°ƒç”¨ go_dump。
+
+GFS2 内部的 glock 加é”顺åºï¼š
+
+ 1. i_rwsem(如需è¦ï¼‰
+ 2. é‡å‘½å glock(仅用于é‡å‘½å)
+ 3. Inode glock
+ (父级优先于å­çº§ï¼ŒåŒçº§ inode 按é”ç¼–å·æŽ’åºï¼‰
+ 4. Rgrp glock(用于(åï¼‰åˆ†é…æ“作)
+ 5. 事务 glock(通过 gfs2_trans_begin,éžè¯»æ“作)
+ 6. i_rw_mutex(如需è¦ï¼‰
+ 7. 页é”(始终最åŽï¼Œè‡³å…³é‡è¦ï¼ï¼‰
+
+æ¯ä¸ª inode 对应两把 glockï¼šä¸€æŠŠç®¡ç† inode 本身(加é”顺
+åºå¦‚上),å¦ä¸€æŠŠï¼ˆç§°ä¸º iopen glockï¼‰ç»“åˆ inode çš„
+i_nlink 字段决定 inode 生命周期。inode 加é”基于å•个
+inode,rgrp 加é”基于å•个 rgrpã€‚é€šå¸¸ä¼˜å…ˆèŽ·å–æœ¬åœ°é”å†èŽ·
+å–集群é”。
+
+Glock 统计
+----------
+
+统计分为两类:超级å—相关统计和å•个 glock 相关统计。超级
+å—ç»Ÿè®¡æŒ‰æ¯ CPU 执行以å‡å°‘收集开销,并进一步按 glock ç±»
+型细分。所有时间å•ä½ä¸ºçº³ç§’。
+
+超级å—å’Œ glock 统计收集相åŒä¿¡æ¯ã€‚è¶…çº§å—æ—¶åºç»Ÿè®¡ä¸º glock
+æ—¶åºç»Ÿè®¡æä¾›é»˜è®¤å€¼ï¼Œä½¿æ–°å»º glock 具有åˆç†çš„åˆå§‹å€¼ã€‚æ¯ä¸ª
+glock 的计数器在创建时åˆå§‹åŒ–为零,当 glock 从内存移除时
+统计丢失。
+
+统计包å«ä¸‰ç»„å‡å€¼/方差对åŠä¸¤ä¸ªè®¡æ•°å™¨ã€‚å‡å€¼/方差对为平滑
+指数估计,算法与网络代ç ä¸­çš„往返时间计算类似(å‚è§ã€Š
+TCP/IP详解 å·1》第21.3节åŠã€Šå·2》第25.10节)。与 TCP/IP
+案例ä¸åŒï¼Œæ­¤å¤„å‡å€¼/方差未缩放且å•ä½ä¸ºæ•´æ•°çº³ç§’。
+
+三组å‡å€¼/方差对测é‡ä»¥ä¸‹å†…容:
+
+ 1. DLM 锿—¶é—´ï¼ˆéžé˜»å¡žè¯·æ±‚)
+ 2. DLM 锿—¶é—´ï¼ˆé˜»å¡žè¯·æ±‚)
+ 3. è¯·æ±‚é—´éš”æ—¶é—´ï¼ˆæŒ‡å‘ DLM)
+
+éžé˜»å¡žè¯·æ±‚指无论目标 DLM é”处于何ç§çжæ€å‡èƒ½ç«‹å³å®Œæˆçš„请求。
+当剿»¡è¶³æ¡ä»¶çš„请求包括:(a)é”当å‰çжæ€ä¸ºäº’斥(如é”é™çº§ï¼‰ã€
+(b)请求状æ€ä¸ºç©ºç½®æˆ–è§£é”ï¼ˆåŒæ ·å¦‚é”é™çº§ï¼‰ã€æˆ–(c)设置"try lock"
+标志的请求。其余é”请求å‡å±žé˜»å¡žè¯·æ±‚。
+
+两个计数器分别统计:
+ 1. é”请求总数(决定å‡å€¼/方差计算的数æ®é‡ï¼‰
+ 2. glock 代ç é¡¶å±‚çš„æŒæœ‰è€…排队数(通常远大于 DLM é”请求数)
+
+为什么收集这些统计数æ®ï¼Ÿæˆ‘ä»¬éœ€æ·±å…¥åˆ†æžæ—¶åºå‚数的动因如下:
+
+1. 更精准设置 glock "æœ€çŸ­æŒæœ‰æ—¶é—´"
+2. 快速识别性能问题
+3. 改进资æºç»„分é…算法(基于é”等待时间而éžç›²ç›® "try lock")
+
+因平滑更新的特性,采样é‡çš„阶跃å˜åŒ–éœ€ç» 8 次采样(方差需
+4 次)æ‰èƒ½å®Œå…¨ä½“现,解æžç»“果时需审慎考虑。
+
+通过é”è¯·æ±‚å®Œæˆæ—¶é—´å’Œ glock å¹³å‡é”请求间隔时间,å¯è®¡ç®—节
+点使用 glock æ—¶é•¿ä¸Žé›†ç¾¤å…±äº«æ—¶é•¿çš„å æ¯”ï¼Œå¯¹è®¾ç½®é”æœ€çŸ­æŒæœ‰
+时间至关é‡è¦ã€‚
+
+我们已采å–严谨措施,力求精准测é‡ç›®æ ‡é‡å€¼ã€‚任何测é‡ç³»ç»Ÿå‡
+å­˜åœ¨è¯¯å·®ï¼Œä½†æˆ‘æœŸæœ›å½“å‰æ–¹æ¡ˆå·²è¾¾åˆ°åˆç†ç²¾åº¦æžé™ã€‚
+
+超级å—状æ€ç»Ÿè®¡è·¯å¾„::
+
+ /sys/kernel/debug/gfs2/<fsname>/sbstats
+
+Glock 状æ€ç»Ÿè®¡è·¯å¾„::
+
+ /sys/kernel/debug/gfs2/<fsname>/glstats
+
+(å‡è®¾ debugfs 挂载于 /sys/kernel/debug,且 <fsname> 替
+æ¢ä¸ºå¯¹åº” GFS2 文件系统å)
+
+输出缩写说明:
+
+========= ============================================
+srtt éžé˜»å¡ž DLM 请求的平滑往返时间
+srttvar srtt 的方差估计
+srttb (潜在)阻塞 DLM 请求的平滑往返时间
+srttvarb srttb 的方差估计
+sirt DLM 请求的平滑请求间隔时间
+sirtvar sirt 的方差估计
+dlm DLM 请求数(glstats 文件中的 dcnt)
+queue 排队的 glock 请求数(glstats 文件中的 qcnt)
+========= ============================================
+
+sbstats文件按glock类型(æ¯ç§ç±»åž‹8行)和CPU核心(æ¯CPU一列)
+记录统计数æ®é›†ã€‚glstats文件则为æ¯ä¸ªglockæä¾›ç»Ÿè®¡é›†ï¼Œå…¶æ ¼å¼
+与glocks文件类似,但所有时åºç»Ÿè®¡é‡å‡é‡‡ç”¨å‡å€¼/方差格å¼å­˜å‚¨ã€‚
+
+gfs2_glock_lock_time 跟踪点实时输出目标 glock 的当å‰ç»Ÿè®¡
+å€¼ï¼Œå¹¶é™„å¸¦æ¯æ¬¡æŽ¥æ”¶åˆ°çš„dlmå“应附加信æ¯ï¼š
+
+====== ============
+status DLM 请求状æ€
+flags DLM 请求标志
+tdiff 该请求的耗时
+====== ============
+
+(其余字段åŒä¸Šè¡¨ï¼‰
diff --git a/Documentation/translations/zh_CN/filesystems/gfs2-uevents.rst b/Documentation/translations/zh_CN/filesystems/gfs2-uevents.rst
new file mode 100644
index 000000000000..3975c4544118
--- /dev/null
+++ b/Documentation/translations/zh_CN/filesystems/gfs2-uevents.rst
@@ -0,0 +1,97 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/filesystems/gfs2-uevents.rst
+
+:翻译:
+
+ 邵明寅 Shao Mingyin <shao.mingyin@zte.com.cn>
+
+:校译:
+
+ æ¨æ¶› yang tao <yang.tao172@zte.com.cn>
+
+===============
+uevents 与 GFS2
+===============
+
+在 GFS2 文件系统的挂载生命周期内,会生æˆå¤šä¸ª uevent。
+本文档解释了这些事件的å«ä¹‰åŠå…¶ç”¨é€”(被 gfs2-utils 中的 gfs_controld 使用)。
+
+GFS2 uevents 列表
+=================
+
+1. ADD
+------
+
+ADD 事件å‘生在挂载时。它始终是新建文件系统生æˆçš„第一个 uevent。如果挂载æˆ
+功,éšåŽä¼šç”Ÿæˆ ONLINE uevent。如果挂载失败,则éšåŽä¼šç”Ÿæˆ REMOVE uevent。
+
+ADD uevent 包å«ä¸¤ä¸ªçŽ¯å¢ƒå˜é‡ï¼šSPECTATOR=[0|1] å’Œ RDONLY=[0|1],分别用
+于指定文件系统的观察者状æ€ï¼ˆä¸€ç§æœªåˆ†é…日志的åªè¯»æŒ‚载)和åªè¯»çжæ€ï¼ˆå·²åˆ†é…日志)。
+
+2. ONLINE
+---------
+
+ONLINE uevent 在æˆåŠŸæŒ‚è½½æˆ–é‡æ–°æŒ‚è½½åŽç”Ÿæˆã€‚它具有与 ADD uevent 相åŒçš„环
+境å˜é‡ã€‚ONLINE uevent åŠå…¶ç”¨äºŽæ ‡è¯†è§‚察者和 RDONLY 状æ€çš„两个环境å˜é‡æ˜¯è¾ƒ
+新版本内核引入的功能(2.6.32-rc+ åŠä»¥ä¸Šï¼‰ï¼Œæ—§ç‰ˆæœ¬å†…æ ¸ä¸ä¼šç”Ÿæˆæ­¤äº‹ä»¶ã€‚
+
+3. CHANGE
+---------
+
+CHANGE uevent 在两ç§åœºæ™¯ä¸‹ä½¿ç”¨ã€‚一是报告第一个节点æˆåŠŸæŒ‚è½½æ–‡ä»¶ç³»ç»Ÿæ—¶
+(FIRSTMOUNT=Done)。这作为信å·å‘ŠçŸ¥ gfs_controld,此时集群中其他节点å¯ä»¥
+安全挂载该文件系统。
+
+å¦ä¸€ä¸ª CHANGE uevent 用于通知文件系统æŸä¸ªæ—¥å¿—的日志æ¢å¤å·²å®Œæˆã€‚它包å«ä¸¤ä¸ª
+环境å˜é‡ï¼šJID= 指定刚æ¢å¤çš„æ—¥å¿— ID,RECOVERY=[Done|Failed] 表示æ“作æˆ
+功与å¦ã€‚这些 uevent ä¼šåœ¨æ¯æ¬¡æ—¥å¿—æ¢å¤æ—¶ç”Ÿæˆï¼Œæ— è®ºæ˜¯åœ¨åˆå§‹æŒ‚载过程中,还是
+gfs_controld 通过 /sys/fs/gfs2/<fsname>/lock_module/recovery 文件
+请求特定日志æ¢å¤çš„结果。
+
+由于早期版本的 gfs_controld 使用 CHANGE uevent 时未检查环境å˜é‡ä»¥ç¡®å®šçж
+æ€ï¼Œè‹¥ä¸ºå…¶æ·»åŠ æ–°åŠŸèƒ½ï¼Œå­˜åœ¨ç”¨æˆ·å·¥å…·ç‰ˆæœ¬è¿‡æ—§å¯¼è‡´é›†ç¾¤æ•…éšœçš„é£Žé™©ã€‚å› æ­¤ï¼Œåœ¨æ–°å¢žç”¨
+于标识æˆåŠŸæŒ‚è½½æˆ–é‡æ–°æŒ‚载的 uevent 时,选择了使用 ONLINE uevent。
+
+4. OFFLINE
+----------
+
+OFFLINE uevent 仅在文件系统å‘生错误时生æˆï¼Œæ˜¯ "withdraw" 机制的一部分。
+当å‰è¯¥äº‹ä»¶æœªæä¾›å…·ä½“错误信æ¯ï¼Œæ­¤é—®é¢˜æœ‰å¾…ä¿®å¤ã€‚
+
+5. REMOVE
+---------
+
+REMOVE uevent åœ¨æŒ‚è½½å¤±è´¥ç»“æŸæˆ–å¸è½½æ–‡ä»¶ç³»ç»Ÿæ—¶ç”Ÿæˆã€‚所有 REMOVE uevent
+之å‰éƒ½è‡³å°‘存在åŒä¸€æ–‡ä»¶ç³»ç»Ÿçš„ ADD uevent。与其他 uevent ä¸åŒï¼Œå®ƒç”±å†…核的
+kobject å­ç³»ç»Ÿè‡ªåŠ¨ç”Ÿæˆã€‚
+
+
+所有 GFS2 uevents 的通用信æ¯ï¼ˆuevent 环境å˜é‡ï¼‰
+===============================================
+
+1. LOCKTABLE=
+--------------
+
+LOCKTABLE æ˜¯ä¸€ä¸ªå­—ç¬¦ä¸²ï¼Œå…¶å€¼æ¥æºäºŽæŒ‚载命令行(locktable=)或 fstab 文件。
+它用作文件系统标签,并为 lock_dlm 类型的挂载æä¾›åŠ å…¥é›†ç¾¤æ‰€éœ€çš„ä¿¡æ¯ã€‚
+
+2. LOCKPROTO=
+-------------
+
+LOCKPROTO 是一个字符串,其值å–决于挂载命令行或 fstab 中的设置。其值将是
+lock_nolock 或 lock_dlm。未æ¥å¯èƒ½æ”¯æŒå…¶ä»–é”管ç†å™¨ã€‚
+
+3. JOURNALID=
+-------------
+
+如果文件系统正在使用日志(观察者挂载ä¸åˆ†é…日志),则所有 GFS2 uevent 中都
+ä¼šåŒ…å«æ­¤å˜é‡ï¼Œå…¶å€¼ä¸ºæ•°å­—å½¢å¼çš„æ—¥å¿— ID。
+
+4. UUID=
+--------
+
+在较新版本的 gfs2-utils 中,mkfs.gfs2 ä¼šå‘æ–‡ä»¶ç³»ç»Ÿè¶…级å—写入 UUID。若存
+在 UUID,所有与该文件系统相关的 uevent 中å‡ä¼šåŒ…嫿­¤ä¿¡æ¯ã€‚
diff --git a/Documentation/translations/zh_CN/filesystems/gfs2.rst b/Documentation/translations/zh_CN/filesystems/gfs2.rst
new file mode 100644
index 000000000000..ffa62b12b019
--- /dev/null
+++ b/Documentation/translations/zh_CN/filesystems/gfs2.rst
@@ -0,0 +1,57 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/filesystems/gfs2.rst
+
+:翻译:
+
+ 邵明寅 Shao Mingyin <shao.mingyin@zte.com.cn>
+
+:校译:
+
+ æ¨æ¶› yang tao <yang.tao172@zte.com.cn>
+
+=====================================
+全局文件系统 2 (Global File System 2)
+=====================================
+
+GFS2 是一个集群文件系统。它å…è®¸ä¸€ç»„è®¡ç®—æœºåŒæ—¶ä½¿ç”¨åœ¨å®ƒä»¬ä¹‹é—´å…±äº«çš„å—设备(通
+过 FCã€iSCSIã€NBD 等)。GFS2 åƒæœ¬åœ°æ–‡ä»¶ç³»ç»Ÿä¸€æ ·è¯»å†™å—设备,但也使用一个é”
+æ¨¡å—æ¥è®©è®¡ç®—机å调它们的 I/O æ“作,从而维护文件系统的一致性。GFS2 的出色特
+æ€§ä¹‹ä¸€æ˜¯å®Œç¾Žä¸€è‡´æ€§â€”â€”åœ¨ä¸€å°æœºå™¨ä¸Šå¯¹æ–‡ä»¶ç³»ç»Ÿæ‰€åšçš„æ›´æ”¹ä¼šç«‹å³æ˜¾ç¤ºåœ¨é›†ç¾¤ä¸­çš„æ‰€
+有其他机器上。
+
+GFS2 使用å¯äº’æ¢çš„节点间é”å®šæœºåˆ¶ï¼Œå½“å‰æ”¯æŒçš„æœºåˆ¶æœ‰ï¼š
+
+ lock_nolock
+ - å…许将 GFS2 用作本地文件系统
+
+ lock_dlm
+ - 使用分布å¼é”管ç†å™¨ (dlm) 进行节点间é”定。
+ 该 dlm ä½äºŽ linux/fs/dlm/
+
+lock_dlm ä¾èµ–于在上述 URL 中找到的用户空间集群管ç†ç³»ç»Ÿã€‚
+
+è‹¥è¦å°† GFS2 用作本地文件系统,则ä¸éœ€è¦å¤–部集群系统,åªéœ€ï¼š:
+
+ $ mkfs -t gfs2 -p lock_nolock -j 1 /dev/block_device
+ $ mount -t gfs2 /dev/block_device /dir
+
+在所有集群节点上都需è¦å®‰è£… gfs2-utils 软件包;对于 lock_dlmï¼Œæ‚¨è¿˜éœ€è¦æŒ‰
+照文档é…ç½® dlm å’Œ corosync 用户空间工具。
+
+gfs2-utils å¯åœ¨ https://pagure.io/gfs2-utils 找到。
+
+GFS2 在ç£ç›˜æ ¼å¼ä¸Šä¸Žæ—©æœŸç‰ˆæœ¬çš„ GFS ä¸å…¼å®¹ï¼Œä½†å®ƒå·²ç›¸å½“接近。
+
+以下手册页 (man pages) å¯åœ¨ gfs2-utils 中找到:
+
+ ============ =============================================
+ fsck.gfs2 ç”¨äºŽä¿®å¤æ–‡ä»¶ç³»ç»Ÿ
+ gfs2_grow 用于在线扩展文件系统
+ gfs2_jadd ç”¨äºŽåœ¨çº¿å‘æ–‡ä»¶ç³»ç»Ÿæ·»åŠ æ—¥å¿—
+ tunegfs2 用于æ“ä½œã€æ£€æŸ¥å’Œè°ƒä¼˜æ–‡ä»¶ç³»ç»Ÿ
+ gfs2_convert 用于将 gfs 文件系统原地转æ¢ä¸º GFS2
+ mkfs.gfs2 用于创建文件系统
+ ============ =============================================
diff --git a/Documentation/translations/zh_CN/filesystems/index.rst b/Documentation/translations/zh_CN/filesystems/index.rst
index 9f2a8b003778..fcc79ff9fdad 100644
--- a/Documentation/translations/zh_CN/filesystems/index.rst
+++ b/Documentation/translations/zh_CN/filesystems/index.rst
@@ -15,6 +15,16 @@ Linux Kernel中的文件系统
文件系统(VFS)层以åŠåŸºäºŽå…¶ä¸Šçš„å„ç§æ–‡ä»¶ç³»ç»Ÿå¦‚何工作呈现给大家。当å‰\
å¯ä»¥çœ‹åˆ°ä¸‹é¢çš„内容。
+核心 VFS 文档
+=============
+
+有关 VFS 层本身以åŠå…¶ç®—法工作方å¼çš„æ–‡æ¡£ï¼Œè¯·å‚阅这些手册。
+
+.. toctree::
+ :maxdepth: 1
+
+ dnotify
+
文件系统
========
@@ -26,4 +36,9 @@ Linux Kernel中的文件系统
virtiofs
debugfs
tmpfs
-
+ ubifs
+ ubifs-authentication
+ gfs2
+ gfs2-uevents
+ gfs2-glocks
+ inotify
diff --git a/Documentation/translations/zh_CN/filesystems/inotify.rst b/Documentation/translations/zh_CN/filesystems/inotify.rst
new file mode 100644
index 000000000000..b4d740aca946
--- /dev/null
+++ b/Documentation/translations/zh_CN/filesystems/inotify.rst
@@ -0,0 +1,80 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/filesystems/inotify.rst
+
+:翻译:
+
+ çŽ‹é¾™æ° Wang Longjie <wang.longjie1@zte.com.cn>
+
+==========================================
+Inotify - 一个强大且简å•çš„æ–‡ä»¶å˜æ›´é€šçŸ¥ç³»ç»Ÿ
+==========================================
+
+
+
+文档由 Robert Love <rml@novell.com> 于 2005 年 3 月 15 日开始撰写
+
+文档由 Zhang Zhen <zhenzhang.zhang@huawei.com> 于 2015 年 1 月 4 日更新
+
+ - 删除了已废弃的接å£ï¼Œå…³äºŽç”¨æˆ·æŽ¥å£è¯·å‚考手册页。
+
+(i) 基本原ç†
+
+问:
+ ä¸å°†ç›‘控项与被监控对象打开的文件æè¿°ç¬¦ï¼ˆfd)绑定,这背åŽçš„设计决策是什么?
+
+答:
+ 监控项会与打开的 inotify 设备相关è”,而éžä¸Žæ‰“开的文件相关è”。这解决了 dnotify 的主è¦é—®é¢˜ï¼š
+ ä¿æŒæ–‡ä»¶æ‰“开会é”定文件,更糟的是,还会é”定挂载点。因此,dnotify 在带有å¯ç§»åŠ¨ä»‹è´¨çš„æ¡Œé¢ç³»ç»Ÿ
+ 上难以使用,因为介质将无法被å¸è½½ã€‚监控文件ä¸åº”è¦æ±‚文件处于打开状æ€ã€‚
+
+问:
+ 与æ¯ä¸ªç›‘控项一个文件æè¿°ç¬¦çš„æ–¹å¼ç›¸æ¯”,采用æ¯ä¸ªå®žä¾‹ä¸€ä¸ªæ–‡ä»¶æè¿°ç¬¦çš„设计决策是出于什么
+ 考虑?
+
+答:
+ æ¯ä¸ªç›‘控项一个文件æè¿°ç¬¦ä¼šå¾ˆå¿«çš„æ¶ˆè€—掉超出å…许数é‡çš„æ–‡ä»¶æè¿°ç¬¦ï¼Œå…¶æ•°é‡ä¼šè¶…出实际å¯ç®¡ç†çš„范
+ 围,也会超出 select() 能高效处ç†çš„范围。诚然,root 用户å¯ä»¥æé«˜æ¯ä¸ªè¿›ç¨‹çš„æ–‡ä»¶æè¿°ç¬¦é™åˆ¶ï¼Œ
+ 用户也å¯ä»¥ä½¿ç”¨ epollï¼Œä½†åŒæ—¶è¦æ±‚这两者是ä¸åˆç†ä¸”多余的。一个监控项所消耗的内存比一个打开的文
+ ä»¶è¦å°‘,因此将这两个数é‡ç©ºé—´åˆ†å¼€æ˜¯åˆç†çš„。当å‰çš„设计正是用户空间开å‘者所期望的:用户åªéœ€åˆå§‹
+ 化一次 inotifyï¼Œç„¶åŽæ·»åŠ  n 个监控项,而这åªéœ€è¦ä¸€ä¸ªæ–‡ä»¶æè¿°ç¬¦ï¼Œæ— éœ€è°ƒæ•´æ–‡ä»¶æè¿°ç¬¦é™åˆ¶ã€‚åˆ
+ 始化 inotify 实例åˆå§‹åŒ–ä¸¤åƒæ¬¡æ˜¯å¾ˆè’谬的。如果我们能够简æ´åœ°å®žçŽ°ç”¨æˆ·ç©ºé—´çš„å好——而且我们
+ 确实å¯ä»¥ï¼Œidr 层让这类事情å˜å¾—轻而易举——那么我们就应该这么åšã€‚
+
+ 还有其他åˆç†çš„ç†ç”±ã€‚å¦‚æžœåªæœ‰ä¸€ä¸ªæ–‡ä»¶æè¿°ç¬¦ï¼Œé‚£å°±åªéœ€è¦åœ¨è¯¥æè¿°ç¬¦ä¸Šé˜»å¡žï¼Œå®ƒå¯¹åº”ç€ä¸€ä¸ªäº‹ä»¶é˜Ÿåˆ—。
+ 这个å•一文件æè¿°ç¬¦ä¼šè¿”回所有的监控事件以åŠä»»ä½•å¯èƒ½çš„带外数æ®ã€‚而如果æ¯ä¸ªæ–‡ä»¶æè¿°ç¬¦éƒ½æ˜¯ä¸€ä¸ªç‹¬
+ 立的监控项,
+
+ - 将无法知晓事件的顺åºã€‚文件 foo 和文件 bar 上的事件会触å‘两个文件æè¿°ç¬¦ä¸Šçš„ poll(),
+ 但无法判断哪个事件先å‘生。而用å•个队列就å¯ä»¥å¾ˆå®¹æ˜“çš„æä¾›äº‹ä»¶çš„顺åºã€‚è¿™ç§é¡ºåºå¯¹çŽ°æœ‰çš„åº”ç”¨ç¨‹
+ åºï¼ˆå¦‚ Beagle)至关é‡è¦ã€‚想象一下,如果“mv a b ; mv b aâ€è¿™æ ·çš„事件没有顺åºä¼šæ˜¯ä»€ä¹ˆ
+ 情况。
+
+ - 我们将ä¸å¾—ä¸ç»´æŠ¤ n 个文件æè¿°ç¬¦å’Œ n 个带有状æ€çš„å†…éƒ¨é˜Ÿåˆ—ï¼Œè€Œä¸æ˜¯ä»…仅一个。这在 kernel 中
+ 会混乱得多。å•个线性队列是åˆç†çš„æ•°æ®ç»“构。
+
+ - 用户空间开å‘者更é’ç当å‰çš„ API。例如,Beagle 的开å‘者们就很喜欢它。相信我,我问过他们。
+ 这并ä¸å¥‡æ€ªï¼šè°ä¼šæƒ³é€šè¿‡ select æ¥ç®¡ç†ä»¥åŠé˜»å¡žåœ¨ 1000 个文件æè¿°ç¬¦ä¸Šå‘¢ï¼Ÿ
+
+ - 无法获å–带外数æ®ã€‚
+
+ - 1024 这个数é‡ä»ç„¶å¤ªå°‘。 ;-)
+
+ 当è¦è®¾è®¡ä¸€ä¸ªå¯æ‰©å±•到数åƒä¸ªç›®å½•çš„æ–‡ä»¶å˜æ›´é€šçŸ¥ç³»ç»Ÿæ—¶ï¼Œå¤„ç†æ•°åƒä¸ªæ–‡ä»¶æè¿°ç¬¦ä¼¼ä¹Žå¹¶ä¸æ˜¯åˆé€‚的接å£ã€‚
+ 这太ç¹ç了。
+
+ 此外,创建多个实例ã€å¤„ç†å¤šä¸ªé˜Ÿåˆ—以åŠç›¸åº”的多个文件æè¿°ç¬¦æ˜¯å¯è¡Œçš„。ä¸å¿…是æ¯ä¸ªè¿›ç¨‹å¯¹åº”一个文件æ
+ 述符;而是æ¯ä¸ªé˜Ÿåˆ—对应一个文件æè¿°ç¬¦ï¼Œä¸€ä¸ªè¿›ç¨‹å®Œå…¨å¯èƒ½éœ€è¦å¤šä¸ªé˜Ÿåˆ—。
+
+问:
+ 为什么采用系统调用的方å¼ï¼Ÿ
+
+答:
+ ç³Ÿç³•çš„ç”¨æˆ·ç©ºé—´æŽ¥å£æ˜¯ dnotify 的第二大问题。信å·å¯¹äºŽæ–‡ä»¶é€šçŸ¥æ¥è¯´æ˜¯ä¸€ç§éžå¸¸ç³Ÿç³•的接å£ã€‚其实对
+ 于其他任何事情,信å·ä¹Ÿéƒ½ä¸æ˜¯å¥½çš„æŽ¥å£ã€‚从å„个角度æ¥çœ‹ï¼Œç†æƒ³çš„解决方案是基于文件æè¿°ç¬¦çš„,它å…许
+ 基本的文件 I/O æ“ä½œä»¥åŠ poll/select æ“ä½œã€‚èŽ·å–æ–‡ä»¶æè¿°ç¬¦å’Œç®¡ç†ç›‘控项既å¯ä»¥é€šè¿‡è®¾å¤‡æ–‡ä»¶æ¥
+ 实现,也å¯ä»¥é€šè¿‡ä¸€ç³»åˆ—新的系统调用æ¥å®žçŽ°ã€‚æˆ‘ä»¬å†³å®šé‡‡ç”¨ä¸€ç³»åˆ—ç³»ç»Ÿè°ƒç”¨ï¼Œå› ä¸ºè¿™æ˜¯æä¾›æ–°çš„内核接å£
+ 的首选方法。两者之间唯一真正的区别在于,我们是想使用 open(2) 和 ioctl(2),还是想使用几
+ 个新的系统调用。系统调用比 ioctl 更有优势。
diff --git a/Documentation/translations/zh_CN/filesystems/ubifs-authentication.rst b/Documentation/translations/zh_CN/filesystems/ubifs-authentication.rst
new file mode 100644
index 000000000000..0e7cf7707e26
--- /dev/null
+++ b/Documentation/translations/zh_CN/filesystems/ubifs-authentication.rst
@@ -0,0 +1,354 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/filesystems/ubifs-authentication.rst
+
+:翻译:
+
+ 邵明寅 Shao Mingyin <shao.mingyin@zte.com.cn>
+
+:校译:
+
+ æ¨æ¶› yang tao <yang.tao172@zte.com.cn>
+
+=============
+UBIFSè®¤è¯æ”¯æŒ
+=============
+
+引言
+====
+UBIFS 利用 fscrypt æ¡†æž¶ä¸ºæ–‡ä»¶å†…å®¹åŠæ–‡ä»¶åæä¾›ä¿å¯†æ€§ã€‚这能防止攻击者在å•一
+æ—¶é—´ç‚¹è¯»å–æ–‡ä»¶ç³»ç»Ÿå†…容的攻击行为。典型案例是智能手机丢失时,攻击者若没有文件
+系统解密密钥则无法读å–设备上的个人数æ®ã€‚
+
+在现阶段,UBIFS 加密尚ä¸èƒ½é˜²æ­¢æ”»å‡»è€…篡改文件系统内容åŽç”¨æˆ·ç»§ç»­ä½¿ç”¨è®¾å¤‡çš„æ”»
+å‡»åœºæ™¯ã€‚è¿™ç§æƒ…况下,攻击者å¯ä»»æ„修改文件系统内容而ä¸è¢«ç”¨æˆ·å¯Ÿè§‰ã€‚例如修改二
+è¿›åˆ¶æ–‡ä»¶ä½¿å…¶æ‰§è¡Œæ—¶è§¦å‘æ¶æ„行为 [DMC-CBC-ATTACK]。由于 UBIFS 大部分文件
+系统元数æ®ä»¥æ˜Žæ–‡å­˜å‚¨ï¼Œä½¿å¾—文件替æ¢å’Œå†…容篡改å˜å¾—相当容易。
+
+其他全盘加密系统(如 dm-crypt)å¯ä»¥è¦†ç›–所有文件系统元数æ®ï¼Œè¿™ç±»ç³»ç»Ÿè™½ç„¶èƒ½
+å¢žåŠ è¿™ç§æ”»å‡»çš„难度,但特别是当攻击者能多次访问设备时,也有å¯èƒ½å®žçŽ°æ”»å‡»ã€‚å¯¹äºŽ
+基于 Linux å— IO 层的 dm-crypt 等文件系统,å¯é€šè¿‡ dm-integrity 或
+dm-verity å­ç³»ç»Ÿ[DM-INTEGRITY, DM-VERITY]在å—层实现完整数æ®è®¤è¯ï¼Œè¿™äº›
+功能也å¯ä¸Ž dm-crypt 结åˆä½¿ç”¨[CRYPTSETUP2]。
+
+本文æè¿°ä¸€ç§ä¸º UBIFS 实现文件内容认è¯å’Œå®Œæ•´å…ƒæ•°æ®è®¤è¯çš„æ–¹æ³•。由于 UBIFS
+使用 fscrypt 进行文件内容和文件å加密,认è¯ç³»ç»Ÿå¯ä¸Ž fscrypt 集æˆä»¥åˆ©ç”¨å¯†
+é’¥æ´¾ç”Ÿç­‰çŽ°æœ‰åŠŸèƒ½ã€‚ä½†ç³»ç»ŸåŒæ—¶ä¹Ÿåº”支æŒåœ¨ä¸å¯ç”¨åŠ å¯†çš„æƒ…å†µä¸‹ä½¿ç”¨ UBIFS 认è¯ã€‚
+
+
+MTD, UBI & UBIFS
+----------------
+在 Linux 中,MTD(内存技术设备)å­ç³»ç»Ÿæä¾›è®¿é—®è£¸é—ªå­˜è®¾å¤‡çš„统一接å£ã€‚è¿è¡ŒäºŽ
+MTD 之上的é‡è¦å­ç³»ç»Ÿæ˜¯ UBI(无åºå—映åƒï¼‰ï¼Œå®ƒä¸ºé—ªå­˜è®¾å¤‡æä¾›å·ç®¡ç†åŠŸèƒ½ï¼Œç±»ä¼¼
+于å—设备的 LVM。此外,UBI 还处ç†é—ªå­˜ç‰¹æœ‰çš„磨æŸå‡è¡¡å’Œé€æ˜Ž I/O 错误处ç†ã€‚
+UBI å‘上层æä¾›é€»è¾‘擦除å—(LEB)ï¼Œå¹¶é€æ˜Žåœ°æ˜ å°„åˆ°é—ªå­˜çš„ç‰©ç†æ“¦é™¤å—(PEB)。
+
+UBIFS 是è¿è¡ŒäºŽ UBI 之上的裸闪存文件系统。因此 UBI 处ç†ç£¨æŸå‡è¡¡å’Œéƒ¨åˆ†é—ªå­˜
+特性,而 UBIFSä¸“æ³¨äºŽå¯æ‰©å±•æ€§ã€æ€§èƒ½å’Œå¯æ¢å¤æ€§ã€‚
+
+::
+
+ +------------+ +*******+ +-----------+ +-----+
+ | | * UBIFS * | UBI-BLOCK | | ... |
+ | JFFS/JFFS2 | +*******+ +-----------+ +-----+
+ | | +-----------------------------+ +-----------+ +-----+
+ | | | UBI | | MTD-BLOCK | | ... |
+ +------------+ +-----------------------------+ +-----------+ +-----+
+ +------------------------------------------------------------------+
+ | MEMORY TECHNOLOGY DEVICES (MTD) |
+ +------------------------------------------------------------------+
+ +-----------------------------+ +--------------------------+ +-----+
+ | NAND DRIVERS | | NOR DRIVERS | | ... |
+ +-----------------------------+ +--------------------------+ +-----+
+
+ 图1:处ç†è£¸é—ªå­˜çš„ Linux 内核å­ç³»ç»Ÿ
+
+
+
+UBIFS 内部维护多个æŒä¹…化在闪存上的数æ®ç»“构:
+
+- *索引*:存储在闪存上的 B+ 树,å¶èŠ‚ç‚¹åŒ…å«æ–‡ä»¶ç³»ç»Ÿæ•°æ®
+- *日志*ï¼šåœ¨æ›´æ–°é—ªå­˜ç´¢å¼•å‰æ”¶é›†æ–‡ä»¶ç³»ç»Ÿå˜æ›´çš„辅助数æ®ç»“构,å¯å‡å°‘闪存磨æŸ
+- *树节点缓存(TNC)*ï¼šåæ˜ å½“剿–‡ä»¶ç³»ç»Ÿçжæ€çš„内存 B+ 树,é¿å…频ç¹è¯»å–闪存。
+ 本质上是索引的内存表示,但包å«é¢å¤–属性
+- *LEB属性树(LPT)*:用于统计æ¯ä¸ª UBI LEB 空闲空间的闪存B+æ ‘
+
+本节åŽç»­å°†è¯¦ç»†è®¨è®ºUBIFS的闪存数æ®ç»“构。因为 TNC ä¸ç›´æŽ¥æŒä¹…化到闪存,其在此
+处的é‡è¦æ€§è¾ƒä½Žã€‚更多 UBIFS 细节详è§[UBIFS-WP]。
+
+
+UBIFS 索引与树节点缓存
+~~~~~~~~~~~~~~~~~~~~~~
+
+UBIFS 在闪存上的基础实体称为 *节点* ,包å«å¤šç§ç±»åž‹ã€‚如存储文件内容å—的数æ®
+节点
+( ``struct ubifs_data_node`` ),或表示 VFS 索引节点的 inode 节点
+( ``struct ubifs_ino_node`` )。几乎所有节点共享包å«èŠ‚ç‚¹ç±»åž‹ã€é•¿åº¦ã€åºåˆ—
+å·ç­‰åŸºç¡€ä¿¡æ¯çš„通用头
+( ``ubifs_ch`` )(è§å†…æ ¸æºç  ``fs/ubifs/ubifs-media.h`` )。LPTæ¡ç›®
+和填充节点(用于填充 LEB
+尾部ä¸å¯ç”¨ç©ºé—´ï¼‰ç­‰æ¬¡è¦èŠ‚ç‚¹ç±»åž‹é™¤å¤–ã€‚
+
+为é¿å…æ¯æ¬¡å˜æ›´é‡å†™æ•´ä¸ª B+ 树,UBIFS 采用 *wandering tree* 实现:仅é‡å†™
+å˜æ›´èŠ‚ç‚¹ï¼Œæ—§ç‰ˆæœ¬è¢«æ ‡è®°åºŸå¼ƒè€Œéžç«‹å³æ“¦é™¤ã€‚因此索引ä¸å›ºå®šå­˜å‚¨äºŽé—ªå­˜æŸå¤„,而是在
+闪存上 *wanders* ,在 LEB 被 UBIFS é‡ç”¨å‰ï¼Œé—ªå­˜ä¸Šä¼šå­˜åœ¨åºŸå¼ƒéƒ¨åˆ†ã€‚为定ä½
+最新索引,UBIFS 在 UBI LEB 1 存储称为 *主节点* çš„ç‰¹æ®ŠèŠ‚ç‚¹ï¼Œå§‹ç»ˆæŒ‡å‘æœ€æ–°
+UBIFS ç´¢å¼•æ ¹èŠ‚ç‚¹ã€‚ä¸ºå¢žå¼ºå¯æ¢å¤æ€§ï¼Œä¸»èŠ‚ç‚¹è¿˜å¤‡ä»½åˆ° LEB 2。因此挂载 UBIFS åª
+éœ€è¯»å– LEB 1 å’Œ 2 获å–当å‰ä¸»èŠ‚ç‚¹ï¼Œè¿›è€Œå®šä½æœ€æ–°é—ªå­˜ç´¢å¼•。
+
+TNC æ˜¯é—ªå­˜ç´¢å¼•çš„å†…å­˜è¡¨ç¤ºï¼ŒåŒ…å«æœªæŒä¹…化的è¿è¡Œæ—¶å±žæ€§ï¼ˆå¦‚è„æ ‡è®°ï¼‰ã€‚TNC 作为回
+写å¼ç¼“存,所有闪存索引修改都通过 TNC 完æˆã€‚与其他缓存类似,TNC 无需将完整
+ç´¢å¼•å…¨éƒ¨åŠ è½½åˆ°å†…å­˜ä¸­ï¼Œéœ€è¦æ—¶ä»Žé—ªå­˜è¯»å–部分内容。 *æäº¤* 是更新闪存文件系统
+结构(如索引)的 UBIFS æ“ä½œã€‚æ¯æ¬¡æäº¤æ—¶ï¼Œæ ‡è®°ä¸ºè„çš„ TNC 节点被写入闪存以更
+æ–°æŒä¹…化索引。
+
+
+日志
+~~~~
+
+为é¿å…闪存磨æŸï¼Œç´¢å¼•仅在满足特定æ¡ä»¶ï¼ˆå¦‚ ``fsync(2)`` ï¼‰æ—¶æ‰æŒä¹…化(æäº¤ï¼‰ã€‚
+日志用于记录索引æäº¤ä¹‹é—´çš„æ‰€æœ‰å˜æ›´ï¼ˆä»¥ inode èŠ‚ç‚¹ã€æ•°æ®èŠ‚ç‚¹ç­‰å½¢å¼ï¼‰ã€‚挂载时
+ä»Žé—ªå­˜è¯»å–æ—¥å¿—并釿”¾åˆ° TNC(此时 TNC 按需从闪存索引创建)。
+
+UBIFS ä¿ç•™ä¸€ç»„专用于日志的 LEB(称为 *日志区* )。日志区 LEB æ•°é‡åœ¨æ–‡ä»¶ç³»
+统创建时é…置(使用 ``mkfs.ubifs`` )并存储于超级å—节点。日志区仅å«ä¸¤ç±»èŠ‚
+点: *引用节点* å’Œ *æäº¤èµ·å§‹èŠ‚ç‚¹* 。执行索引æäº¤æ—¶å†™å…¥æäº¤èµ·å§‹èŠ‚ç‚¹ï¼Œæ¯æ¬¡æ—¥
+志更新时写入引用节点。æ¯ä¸ªå¼•ç”¨èŠ‚ç‚¹æŒ‡å‘æž„æˆæ—¥å¿—æ¡ç›®çš„其他节点( inode 节点ã€
+æ•°æ®èŠ‚ç‚¹ç­‰ï¼‰åœ¨é—ªå­˜ä¸Šçš„ä½ç½®ï¼Œè¿™äº›èŠ‚ç‚¹ç§°ä¸º *bud* ,æè¿°åŒ…嫿•°æ®çš„实际文件系
+ç»Ÿå˜æ›´ã€‚
+
+æ—¥å¿—åŒºä»¥çŽ¯å½¢ç¼“å†²åŒºç»´æŠ¤ã€‚å½“æ—¥å¿—å°†æ»¡æ—¶è§¦å‘æäº¤æ“ä½œï¼ŒåŒæ—¶å†™å…¥æäº¤èµ·å§‹èŠ‚ç‚¹ã€‚å› æ­¤
+挂载时 UBIFS 查找最新æäº¤èµ·å§‹èŠ‚ç‚¹ï¼Œä»…é‡æ”¾å…¶åŽçš„引用节点。æäº¤èµ·å§‹èŠ‚ç‚¹å‰çš„引
+用节点将被忽略(因其已属于闪存索引)。
+
+写入日志æ¡ç›®æ—¶ï¼ŒUBIFS é¦–å…ˆç¡®ä¿æœ‰è¶³å¤Ÿç©ºé—´å†™å…¥å¼•用节点和该æ¡ç›®çš„ bud。然åŽå…ˆ
+写引用节点,å†å†™æè¿°æ–‡ä»¶å˜æ›´çš„ budã€‚åœ¨æ—¥å¿—é‡æ”¾é˜¶æ®µï¼ŒUBIFS 会记录æ¯ä¸ªå‚考节
+点,并检查其引用的 LEBä½ç½®ä»¥å®šä½ budsã€‚è‹¥è¿™äº›æ•°æ®æŸå或丢失,UBIFS 会å°è¯•
+é€šè¿‡é‡æ–°è¯»å– LEB æ¥æ¢å¤ï¼Œä½†ä»…针对日志中最åŽå¼•用的 LEBï¼Œå› ä¸ºåªæœ‰å®ƒå¯èƒ½å› æ–­
+电而æŸå。若æ¢å¤å¤±è´¥ï¼ŒUBIFS å°†æ‹’ç»æŒ‚载。对于其他 LEB 的错误,UBIFS 会直接
+终止挂载æ“作。
+
+::
+
+ | ---- LOG AREA ---- | ---------- MAIN AREA ------------ |
+
+ -----+------+-----+--------+---- ------+-----+-----+---------------
+ \ | | | | / / | | | \
+ / CS | REF | REF | | \ \ DENT | INO | INO | /
+ \ | | | | / / | | | \
+ ----+------+-----+--------+--- -------+-----+-----+----------------
+ | | ^ ^
+ | | | |
+ +------------------------+ |
+ | |
+ +-------------------------------+
+
+
+ 图2ï¼šåŒ…å«æäº¤èµ·å§‹èŠ‚ç‚¹(CS)和引用节点(REF)的日志区闪存布局,引用节点指å‘å«
+ bud 的主区
+
+
+LEB属性树/表
+~~~~~~~~~~~~
+
+LEB 属性树用于存储æ¯ä¸ª LEB 的信æ¯ï¼ŒåŒ…括 LEB 类型ã€LEB 上的空闲空间和
+*è„空间* (旧空间,废弃内容) [1]_ 的数é‡ã€‚因为 UBIFS 从ä¸åœ¨å•个 LEB æ··
+åˆå­˜å‚¨ç´¢å¼•节点和数æ®èŠ‚ç‚¹ï¼Œæ‰€ä»¥ LEB 的类型至关é‡è¦ï¼Œæ¯ä¸ª LEB 都有特定用途,
+这对空闲空间计算éžå¸¸æœ‰å¸®åŠ©ã€‚è¯¦è§[UBIFS-WP]。
+
+LEB 属性树也是 B+ 树,但远å°äºŽç´¢å¼•。因为其体积å°ï¼Œæ‰€ä»¥æ¯æ¬¡æäº¤æ—¶éƒ½æ•´å—写入,
+ä¿å­˜ LPT æ˜¯åŽŸå­æ“作。
+
+
+.. [1] 由于LEBåªèƒ½è¿½åР写入ä¸èƒ½è¦†ç›–ï¼Œç©ºé—²ç©ºé—´ï¼ˆå³ LEB 剩余å¯å†™ç©ºé—´ï¼‰ä¸ŽåºŸå¼ƒ
+ 内容(先å‰å†™å…¥ä½†æœªæ“¦é™¤å‰ä¸èƒ½è¦†ç›–)存在区别。
+
+
+UBIFS认è¯
+=========
+
+本章介ç»UBIFS认è¯ï¼Œä½¿UBIFS能验è¯é—ªå­˜ä¸Šå…ƒæ•°æ®å’Œæ–‡ä»¶å†…容的真实性与完整性。
+
+
+å¨èƒæ¨¡åž‹
+--------
+
+UBIFS 认è¯å¯æ£€æµ‹ç¦»çº¿æ•°æ®ç¯¡æ”¹ã€‚虽然ä¸èƒ½é˜²æ­¢ç¯¡æ”¹ï¼Œä½†æ˜¯èƒ½è®©ï¼ˆå¯ä¿¡ï¼‰ä»£ç æ£€æŸ¥é—ª
+存文件内容和文件系统元数æ®çš„完整性与真实性,也能检查文件内容被替æ¢çš„æ”»å‡»ã€‚
+
+UBIFS 认è¯ä¸é˜²æŠ¤å…¨é—ªå­˜å†…容回滚(攻击者å¯è½¬å‚¨é—ªå­˜å†…å®¹å¹¶åœ¨åŽæœŸè¿˜åŽŸï¼‰ã€‚ä¹Ÿä¸é˜²æŠ¤
+å•个索引æäº¤çš„éƒ¨åˆ†å›žæ»šï¼ˆæ”»å‡»è€…èƒ½éƒ¨åˆ†æ’¤é”€å˜æ›´ï¼‰ã€‚这是因为 UBIFS ä¸ç«‹å³è¦†ç›–ç´¢
+引树或日志的旧版本,而是标记为废弃,ç¨åŽç”±åžƒåœ¾å›žæ”¶æ“¦é™¤ã€‚æ”»å‡»è€…å¯æ“¦é™¤å½“剿 ‘部
+åˆ†å†…å®¹å¹¶è¿˜åŽŸé—ªå­˜ä¸Šå°šæœªæ“¦é™¤çš„æ—§ç‰ˆæœ¬ã€‚å› æ¯æ¬¡æäº¤æ€»ä¼šå†™å…¥ç´¢å¼•根节点和主节点的新
+版本而ä¸è¦†ç›–旧版本,UBI 的磨æŸå‡è¡¡æ“ä½œï¼ˆå°†å†…å®¹ä»Žç‰©ç†æ“¦é™¤å—å¤åˆ¶åˆ°å¦ä¸€æ“¦é™¤å—
+且éžåŽŸå­æ“¦é™¤åŽŸå—)进一步助长此问题。
+
+UBIFS 认è¯ä¸è¦†ç›–认è¯å¯†é’¥æä¾›åŽæ”»å‡»è€…在设备执行代ç çš„æ”»å‡»ï¼Œéœ€ç»“åˆå®‰å…¨å¯åŠ¨å’Œ
+å¯ä¿¡å¯åŠ¨ç­‰æŽªæ–½ç¡®ä¿è®¾å¤‡ä»…执行å¯ä¿¡ä»£ç ã€‚
+
+
+认è¯
+----
+
+为完全信任从闪存读å–的数æ®ï¼Œæ‰€æœ‰å­˜å‚¨åœ¨é—ªå­˜çš„ UBIFS æ•°æ®ç»“æž„å‡éœ€è®¤è¯ï¼š
+- åŒ…å«æ–‡ä»¶å†…å®¹ã€æ‰©å±•å±žæ€§ã€æ–‡ä»¶é•¿åº¦ç­‰å…ƒæ•°æ®çš„索引
+- é€šè¿‡è®°å½•æ–‡ä»¶ç³»ç»Ÿå˜æ›´æ¥åŒ…嫿–‡ä»¶å†…容和元数æ®çš„æ—¥å¿—
+- 存储 UBIFS 用于空闲空间统计的 UBI LEB 元数æ®çš„ LPT
+
+
+索引认è¯
+~~~~~~~~
+
+借助 *wandering tree* 概念,UBIFS ä»…æ›´æ–°å’ŒæŒä¹…化从å¶èŠ‚ç‚¹åˆ°æ ¹èŠ‚ç‚¹çš„å˜æ›´
+部分。这å…许用å­èŠ‚ç‚¹å“ˆå¸Œå¢žå¼ºç´¢å¼•æ ‘èŠ‚ç‚¹ã€‚æœ€ç»ˆç´¢å¼•åŸºæœ¬æˆä¸º Merkle 树:因索引
+å¶èŠ‚ç‚¹å«å®žé™…文件系统数æ®ï¼Œå…¶çˆ¶ç´¢å¼•节点的哈希覆盖所有文件内容和元数æ®ã€‚文件
+å˜æ›´æ—¶ï¼ŒUBIFS 索引从å¶èŠ‚ç‚¹åˆ°æ ¹èŠ‚ç‚¹ï¼ˆå«ä¸»èŠ‚ç‚¹ï¼‰ç›¸åº”æ›´æ–°ï¼Œæ­¤è¿‡ç¨‹å¯æŒ‚é’©ä»¥åŒæ­¥
+釿–°è®¡ç®—å„å˜æ›´èŠ‚ç‚¹çš„å“ˆå¸Œã€‚è¯»å–æ–‡ä»¶æ—¶ï¼ŒUBIFS å¯ä»Žå¶èŠ‚ç‚¹åˆ°æ ¹èŠ‚ç‚¹é€çº§éªŒè¯å“ˆå¸Œ
+ç¡®ä¿èŠ‚ç‚¹å®Œæ•´æ€§ã€‚
+
+ä¸ºç¡®ä¿æ•´ä¸ªç´¢å¼•真实性,UBIFS 主节点存储基于密钥的哈希(HMAC),覆盖自身内容åŠ
+ç´¢å¼•æ ‘æ ¹èŠ‚ç‚¹å“ˆå¸Œã€‚å¦‚å‰æ‰€è¿°ï¼Œä¸»èŠ‚ç‚¹åœ¨ç´¢å¼•æŒä¹…化时(å³ç´¢å¼•æäº¤æ—¶ï¼‰æ€»ä¼šå†™å…¥é—ªå­˜ã€‚
+
+此方法仅修改 UBIFS 索引节点和主节点以包å«å“ˆå¸Œï¼Œå…¶ä»–ç±»åž‹èŠ‚ç‚¹ä¿æŒä¸å˜ï¼Œå‡å°‘了
+对 UBIFS 用户(如嵌入å¼è®¾å¤‡ï¼‰å®è´µçš„存储开销。
+
+::
+
+ +---------------+
+ | Master Node |
+ | (hash) |
+ +---------------+
+ |
+ v
+ +-------------------+
+ | Index Node #1 |
+ | |
+ | branch0 branchn |
+ | (hash) (hash) |
+ +-------------------+
+ | ... | (fanout: 8)
+ | |
+ +-------+ +------+
+ | |
+ v v
+ +-------------------+ +-------------------+
+ | Index Node #2 | | Index Node #3 |
+ | | | |
+ | branch0 branchn | | branch0 branchn |
+ | (hash) (hash) | | (hash) (hash) |
+ +-------------------+ +-------------------+
+ | ... | ... |
+ v v v
+ +-----------+ +----------+ +-----------+
+ | Data Node | | INO Node | | DENT Node |
+ +-----------+ +----------+ +-----------+
+
+
+ 图3:索引节点哈希与主节点 HMAC 的覆盖范围
+
+
+
+å¥å£®æ€§æ€§å’Œæ–­ç”µå®‰å…¨æ€§çš„å…³é”®åœ¨äºŽä»¥åŽŸå­æ“作æŒä¹…化哈希值与文件内容。UBIFS 现有
+çš„å˜æ›´èŠ‚ç‚¹æŒä¹…åŒ–æœºåˆ¶ä¸“ä¸ºæ­¤è®¾è®¡ï¼Œèƒ½å¤Ÿç¡®ä¿æ–­ç”µæ—¶å®‰å…¨æ¢å¤ã€‚为索引节点添加哈希值
+ä¸ä¼šæ”¹å˜è¯¥æœºåˆ¶ï¼Œå› ä¸ºæ¯ä¸ªå“ˆå¸Œå€¼éƒ½ä¸Žå…¶å¯¹åº”èŠ‚ç‚¹ä»¥åŽŸå­æ“ä½œåŒæ­¥æŒä¹…化。
+
+
+日志认è¯
+~~~~~~~~
+
+日志也需è¦è®¤è¯ã€‚因为日志æŒç»­å†™å…¥ï¼Œå¿…须频ç¹åœ°æ·»åŠ è®¤è¯ä¿¡æ¯ä»¥ç¡®ä¿æ–­ç”µæ—¶æœªè®¤è¯æ•°
+æ®é‡å¯æŽ§ã€‚方法是从æäº¤èµ·å§‹èŠ‚ç‚¹å¼€å§‹ï¼Œå¯¹å…ˆå‰å¼•用节点ã€å½“å‰å¼•用节点和 bud 节点
+创建连续哈希链。适时地在bud节点间æ’入认è¯èŠ‚ç‚¹ï¼Œè¿™ç§æ–°èŠ‚ç‚¹ç±»åž‹åŒ…å«å“ˆå¸Œé“¾å½“å‰
+状æ€çš„ HMAC。因此日志å¯è®¤è¯è‡³æœ€åŽä¸€ä¸ªè®¤è¯èŠ‚ç‚¹ã€‚æ—¥å¿—å°¾éƒ¨æ— è®¤è¯èŠ‚ç‚¹çš„éƒ¨åˆ†æ— æ³•
+认è¯ï¼Œåœ¨æ—¥å¿—釿”¾æ—¶è·³è¿‡ã€‚
+
+日志认è¯ç¤ºæ„图如下::
+
+ ,,,,,,,,
+ ,......,...........................................
+ ,. CS , hash1.----. hash2.----.
+ ,. | , . |hmac . |hmac
+ ,. v , . v . v
+ ,.REF#0,-> bud -> bud -> bud.-> auth -> bud -> bud.-> auth ...
+ ,..|...,...........................................
+ , | ,
+ , | ,,,,,,,,,,,,,,,
+ . | hash3,----.
+ , | , |hmac
+ , v , v
+ , REF#1 -> bud -> bud,-> auth ...
+ ,,,|,,,,,,,,,,,,,,,,,,
+ v
+ REF#2 -> ...
+ |
+ V
+ ...
+
+因为哈希值包å«å¼•ç”¨èŠ‚ç‚¹ï¼Œæ”»å‡»è€…æ— æ³•é‡æŽ’æˆ–è·³è¿‡æ—¥å¿—å¤´é‡æ”¾ï¼Œä»…能移除日志尾部的
+bud 节点或引用节点,最大é™åº¦å°†æ–‡ä»¶ç³»ç»Ÿå›žé€€è‡³ä¸Šæ¬¡æäº¤ã€‚
+
+日志区ä½ç½®å­˜å‚¨äºŽä¸»èŠ‚ç‚¹ã€‚å› ä¸ºä¸»èŠ‚ç‚¹é€šè¿‡ HMAC 认è¯ï¼Œæ‰€ä»¥æœªç»æ£€æµ‹æ— æ³•篡改。日
+志区大å°åœ¨æ–‡ä»¶ç³»ç»Ÿåˆ›å»ºæ—¶ç”± `mkfs.ubifs` 指定并存储于超级å—节点。为é¿å…篡
+改此值åŠå…¶ä»–傿•°ï¼Œè¶…级å—结构添加 HMAC。超级å—节点存储在 LEB 0,仅在功能标
+å¿—ç­‰å˜æ›´æ—¶ä¿®æ”¹ï¼Œæ–‡ä»¶å˜æ›´æ—¶ä¸ä¿®æ”¹ã€‚
+
+
+LPT认è¯
+~~~~~~~
+
+LPT 根节点在闪存上的ä½ç½®å­˜å‚¨äºŽ UBIFS 主节点。因为 LPT æ¯æ¬¡æäº¤æ—¶éƒ½ä»¥åŽŸå­
+æ“作写入和读å–,无需å•ç‹¬è®¤è¯æ ‘节点。通过主节点存储的简å•å“ˆå¸Œä¿æŠ¤å®Œæ•´ LPT
+å³å¯ã€‚因为主节点自身已认è¯ï¼Œé€šè¿‡éªŒè¯ä¸»èŠ‚ç‚¹çœŸå®žæ€§å¹¶æ¯”å¯¹å­˜å‚¨çš„ LTP 哈希与读
+å–的闪存 LPT 计算哈希值,å³å¯éªŒè¯ LPT 真实性。
+
+
+密钥管ç†
+--------
+
+为了简化实现,UBIFS 认è¯ä½¿ç”¨å•一密钥计算超级å—ã€ä¸»èŠ‚ç‚¹ã€æäº¤èµ·å§‹èŠ‚ç‚¹å’Œå¼•ç”¨
+节点的 HMAC。创建文件系统(`mkfs.ubifs`) 时需æä¾›æ­¤å¯†é’¥ä»¥è®¤è¯è¶…级å—节点。
+挂载文件系统时也需此密钥验è¯è®¤è¯èŠ‚ç‚¹å¹¶ä¸ºå˜æ›´ç”Ÿæˆæ–° HMAC。
+
+UBIFS è®¤è¯æ—¨åœ¨ä¸Ž UBIFS 加密(fscrypt)ååŒå·¥ä½œä»¥æä¾›ä¿å¯†æ€§å’ŒçœŸå®žæ€§ã€‚因为
+UBIFS 加密采用基于目录的差异化加密策略,å¯èƒ½å­˜åœ¨å¤šä¸ª fscrypt 主密钥甚至未
+加密目录。而 UBIFS 认è¯é‡‡ç”¨å…¨æœ‰æˆ–全无方å¼ï¼Œè¦ä¹ˆè®¤è¯æ•´ä¸ªæ–‡ä»¶ç³»ç»Ÿè¦ä¹ˆå®Œå…¨ä¸
+认è¯ã€‚基于此特性,且为确ä¿è®¤è¯æœºåˆ¶å¯ç‹¬ç«‹äºŽåŠ å¯†åŠŸèƒ½ä½¿ç”¨ï¼ŒUBIFS 认è¯ä¸ä¸Ž
+fscrypt 共享主密钥,而是维护独立的认è¯ä¸“用密钥。
+
+æä¾›è®¤è¯å¯†é’¥çš„API尚未定义,但å¯é€šè¿‡ç±»ä¼¼ fscrypt 的用户空间密钥环æä¾›ã€‚需注
+æ„å½“å‰ fscrypt 方案存在缺陷,用户空间 API ç»ˆå°†å˜æ›´[FSCRYPT-POLICY2]。
+
+用户ä»å¯é€šè¿‡ç”¨æˆ·ç©ºé—´æä¾›å•一å£ä»¤æˆ–密钥覆盖 UBIFS 认è¯ä¸ŽåŠ å¯†ã€‚ç›¸åº”ç”¨æˆ·ç©ºé—´å·¥
+å…·å¯è§£å†³æ­¤é—®é¢˜ï¼šé™¤æ´¾ç”Ÿçš„ fscrypt 加密主密钥外,é¢å¤–派生认è¯å¯†é’¥ã€‚
+
+为检查挂载时密钥å¯ç”¨æ€§ï¼ŒUBIFS 超级å—节点将é¢å¤–存储认è¯å¯†é’¥çš„哈希。此方法类
+ä¼¼ fscrypt 加密策略 v2 æå‡ºçš„æ–¹æ³•[FSCRYPT-POLICY2]。
+
+
+æœªæ¥æ‰©å±•
+========
+
+特定场景下,若供应商需è¦å‘客户æä¾›è®¤è¯æ–‡ä»¶ç³»ç»Ÿé•œåƒï¼Œåº”该能在ä¸å…±äº« UBIFS 认
+è¯å¯†é’¥çš„å‰æä¸‹å®žçŽ°ã€‚æ–¹æ³•æ˜¯åœ¨æ¯ä¸ª HMAC 外é¢å¤–存储数字签åï¼Œä¾›åº”å•†éšæ–‡ä»¶ç³»ç»Ÿ
+镜åƒåˆ†å‘公钥。若该文件系统åŽç»­éœ€è¦ä¿®æ”¹ï¼Œè‹¥åŽç»­éœ€ä¿®æ”¹è¯¥æ–‡ä»¶ç³»ç»Ÿï¼ŒUBIFS å¯åœ¨
+é¦–æ¬¡æŒ‚è½½æ—¶å°†å…¨éƒ¨æ•°å­—ç­¾åæ›¿æ¢ä¸º HMAC,其处ç†é€»è¾‘与 IMA/EVM å­ç³»ç»Ÿåº”对此类情
+况的方å¼ç±»ä¼¼ã€‚此时,HMAC 密钥需按常规方å¼é¢„å…ˆæä¾›ã€‚
+
+
+å‚考
+====
+
+[CRYPTSETUP2] https://www.saout.de/pipermail/dm-crypt/2017-November/005745.html
+
+[DMC-CBC-ATTACK] https://www.jakoblell.com/blog/2013/12/22/practical-malleability-attack-against-cbc-en
+crypted-luks-partitions/
+
+[DM-INTEGRITY] https://www.kernel.org/doc/Documentation/device-mapper/dm-integrity.rst
+
+[DM-VERITY] https://www.kernel.org/doc/Documentation/device-mapper/verity.rst
+
+[FSCRYPT-POLICY2] https://www.spinics.net/lists/linux-ext4/msg58710.html
+
+[UBIFS-WP] http://www.linux-mtd.infradead.org/doc/ubifs_whitepaper.pdf
diff --git a/Documentation/translations/zh_CN/filesystems/ubifs.rst b/Documentation/translations/zh_CN/filesystems/ubifs.rst
new file mode 100644
index 000000000000..d1873fc6a67c
--- /dev/null
+++ b/Documentation/translations/zh_CN/filesystems/ubifs.rst
@@ -0,0 +1,114 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/filesystems/ubifs.rst
+
+:翻译:
+
+ 邵明寅 Shao Mingyin <shao.mingyin@zte.com.cn>
+
+:校译:
+
+ æ¨æ¶› yang tao <yang.tao172@zte.com.cn>
+
+============
+UBI 文件系统
+============
+
+简介
+====
+
+UBIFS 文件系统全称为 UBI 文件系统(UBI File System)。UBI 代表无åºå—镜
+åƒï¼ˆUnsorted Block Images)。UBIFS 是一ç§é—ªå­˜æ–‡ä»¶ç³»ç»Ÿï¼Œè¿™æ„味ç€å®ƒä¸“为闪
+存设备设计。需è¦ç†è§£çš„æ˜¯ï¼ŒUBIFS与 Linux 中任何传统文件系统(如 Ext2ã€
+XFSã€JFS 等)完全ä¸åŒã€‚UBIFS 代表一类特殊的文件系统,它们工作在 MTD 设备
+而éžå—设备上。该类别的å¦ä¸€ä¸ª Linux 文件系统是 JFFS2。
+
+为更清晰说明,以下是 MTD 设备与å—è®¾å¤‡çš„ç®€è¦æ¯”较:
+
+1. MTD 设备代表闪存设备,由较大尺寸的擦除å—组æˆï¼Œé€šå¸¸çº¦ 128KiB。å—设备由
+ å°å—组æˆï¼Œé€šå¸¸ 512 字节。
+2. MTD è®¾å¤‡æ”¯æŒ 3 ç§ä¸»è¦æ“作:在擦除å—内åç§»ä½ç½®è¯»å–ã€åœ¨æ“¦é™¤å—内åç§»ä½ç½®å†™
+ å…¥ã€ä»¥åŠæ“¦é™¤æ•´ä¸ªæ“¦é™¤å—。å—è®¾å¤‡æ”¯æŒ 2 ç§ä¸»è¦æ“ä½œï¼šè¯»å–æ•´ä¸ªå—和写入整个å—。
+3. 整个擦除å—必须先擦除æ‰èƒ½é‡å†™å†…容。å—å¯ç›´æŽ¥é‡å†™ã€‚
+4. 擦除å—在ç»åŽ†ä¸€å®šæ¬¡æ•°çš„æ“¦å†™å‘¨æœŸåŽä¼šç£¨æŸï¼Œé€šå¸¸ SLC NAND å’Œ NOR 闪存为
+ 100K-1G 次,MLC NAND 闪存为 1K-10K 次。å—设备ä¸å…·å¤‡ç£¨æŸç‰¹æ€§ã€‚
+5. 擦除å—å¯èƒ½æŸåï¼ˆä»…é™ NAND é—ªå­˜ï¼‰ï¼Œè½¯ä»¶éœ€å¤„ç†æ­¤é—®é¢˜ã€‚硬盘上的å—通常ä¸ä¼šæŸ
+ å,因为硬件有åå—æ›¿æ¢æœºåˆ¶ï¼ˆè‡³å°‘现代 LBA 硬盘如此)。
+
+这充分说明了 UBIFS 与传统文件系统的本质差异。
+
+UBIFS 工作在 UBI 层之上。UBI 是一个独立的软件层(ä½äºŽ drivers/mtd/ubi),
+本质上是å·ç®¡ç†å’Œç£¨æŸå‡è¡¡å±‚。它æä¾›ç§°ä¸º UBI å·çš„高级抽象,比 MTD 设备更上层。
+UBI 设备的编程模型与 MTD 设备éžå¸¸ç›¸ä¼¼ï¼Œä»ç”±å¤§å®¹é‡æ“¦é™¤å—组æˆï¼Œæ”¯æŒè¯»/写/擦
+除æ“作,但 UBI 设备消除了磨æŸå’Œåå—é™åˆ¶ï¼ˆä¸Šè¿°åˆ—表的第 4 和第 5 项)。
+
+æŸç§æ„义上,UBIFS 是 JFFS2 文件系统的下一代产å“,但它与 JFFS2 差异巨大且
+ä¸å…¼å®¹ã€‚主è¦åŒºåˆ«å¦‚下:
+
+* JFFS2 工作在 MTD 设备之上,UBIFS ä¾èµ–于 UBI 并工作在 UBI å·ä¹‹ä¸Šã€‚
+* JFFS2 æ²¡æœ‰ä»‹è´¨ç´¢å¼•ï¼Œéœ€åœ¨æŒ‚è½½æ—¶æž„å»ºç´¢å¼•ï¼Œè¿™è¦æ±‚全介质扫æã€‚UBIFS 在闪存
+ 介质上维护文件系统索引信æ¯ï¼Œæ— éœ€å…¨ä»‹è´¨æ‰«æï¼Œå› æ­¤æŒ‚载速度远快于 JFFS2。
+* JFFS2 是直写(write-through)文件系统,而 UBIFS 支æŒå›žå†™
+ (write-back),这使得 UBIFS 写入速度快得多。
+
+与 JFFS2 类似,UBIFS 支æŒå®žæ—¶åŽ‹ç¼©ï¼Œå¯å°†å¤§é‡æ•°æ®å­˜å…¥é—ªå­˜ã€‚
+
+与 JFFS2 类似,UBIFS 能容å¿å¼‚常é‡å¯å’Œæ–­ç”µã€‚它ä¸éœ€è¦ç±»ä¼¼ fsck.ext2 的工
+具。UBIFS ä¼šè‡ªåŠ¨é‡æ”¾æ—¥å¿—并从崩溃中æ¢å¤ï¼Œç¡®ä¿é—ªå­˜æ•°æ®ç»“构的一致性。
+
+UBIFS 具有对数级扩展性(其使用的数æ®ç»“构多为树形),因此挂载时间和内存消耗ä¸
+åƒ JFFS2 那样线性ä¾èµ–于闪存容é‡ã€‚这是因为 UBIFS 在闪存介质上维护文件系统
+索引。但 UBIFS ä¾èµ–于线性扩展的 UBI 层,因此整体 UBI/UBIFS æ ˆä»æ˜¯çº¿æ€§æ‰©
+展。尽管如此,UBIFS/UBI çš„æ‰©å±•æ€§ä»æ˜¾è‘—优于 JFFS2。
+
+UBIFS å¼€å‘者认为,未æ¥å¯å¼€å‘åŒæ ·å…·å¤‡å¯¹æ•°çº§æ‰©å±•性的 UBI2。UBI2 将支æŒä¸Ž
+UBI 相åŒçš„ API,但二进制ä¸å…¼å®¹ã€‚å› æ­¤ UBIFS 无需修改å³å¯ä½¿ç”¨ UBI2。
+
+挂载选项
+========
+
+(*) 表示默认选项。
+
+==================== =======================================================
+bulk_read 批é‡è¯»å–以利用闪存介质的顺åºè¯»å–加速特性
+no_bulk_read (*) ç¦ç”¨æ‰¹é‡è¯»å–
+no_chk_data_crc (*) 跳过数æ®èŠ‚ç‚¹çš„ CRC 校验以æé«˜è¯»å–性能。 仅在闪存
+ 介质高度å¯é æ—¶ä½¿ç”¨æ­¤é€‰é¡¹ã€‚ 此选项å¯èƒ½å¯¼è‡´æ–‡ä»¶å†…容æŸå无法被
+ 察觉。
+chk_data_crc 强制校验数æ®èŠ‚ç‚¹çš„ CRC
+compr=none 覆盖默认压缩器,设置为"none"
+compr=lzo 覆盖默认压缩器,设置为"LZO"
+compr=zlib 覆盖默认压缩器,设置为"zlib"
+auth_key= 指定用于文件系统身份验è¯çš„密钥。
+ 使用此选项将强制å¯ç”¨èº«ä»½éªŒè¯ã€‚
+ 传入的密钥必须存在于内核密钥环中, 且类型必须是'logon'
+auth_hash_name= 用于身份验è¯çš„å“ˆå¸Œç®—æ³•ã€‚åŒæ—¶ç”¨äºŽå“ˆå¸Œè®¡ç®—å’Œ HMAC
+ 生æˆã€‚典型值包括"sha256"或"sha512"
+==================== =======================================================
+
+快速使用指å—
+============
+
+挂载的 UBI å·é€šè¿‡ "ubiX_Y" 或 "ubiX:NAME" 语法指定,其中 "X" 是 UBI
+设备编å·ï¼Œ"Y" 是 UBI å·ç¼–å·ï¼Œ"NAME" 是 UBI å·å称。
+
+å°† UBI 设备 0 çš„å· 0 挂载到 /mnt/ubifs::
+
+ $ mount -t ubifs ubi0_0 /mnt/ubifs
+
+å°† UBI 设备 0 çš„ "rootfs" å·æŒ‚载到 /mnt/ubifs("rootfs" 是å·å)::
+
+ $ mount -t ubifs ubi0:rootfs /mnt/ubifs
+
+以下是内核å¯åЍ傿•°çš„示例,用于将 mtd0 附加到 UBI 并挂载 "rootfs" å·ï¼š
+ubi.mtd=0 root=ubi0:rootfs rootfstype=ubifs
+
+å‚考资料
+========
+
+UBIFS 文档åŠå¸¸è§é—®é¢˜è§£ç­”/æ“作指å—请访问 MTD 官网:
+
+- http://www.linux-mtd.infradead.org/doc/ubifs.html
+- http://www.linux-mtd.infradead.org/faq/ubifs.html
diff --git a/Documentation/translations/zh_CN/how-to.rst b/Documentation/translations/zh_CN/how-to.rst
index ddd99c0f9b4d..7ae5d8765888 100644
--- a/Documentation/translations/zh_CN/how-to.rst
+++ b/Documentation/translations/zh_CN/how-to.rst
@@ -64,7 +64,7 @@ Linux å‘行版和简å•地使用 Linux 命令行,那么å¯ä»¥è¿…速开始了ã
::
cd linux
- ./scripts/sphinx-pre-install
+ ./tools/docs/sphinx-pre-install
以 Fedora 为例,它的输出是这样的::
@@ -437,7 +437,7 @@ git email 默认会抄é€ç»™æ‚¨ä¸€ä»½ï¼Œæ‰€ä»¥æ‚¨å¯ä»¥åˆ‡æ¢ä¸ºå®¡é˜…者的角è
对于首次å‚与 Linux 内核中文文档翻译的新手,建议您在 linux 目录中è¿è¡Œä»¥ä¸‹å‘½ä»¤ï¼š
::
- ./script/checktransupdate.py -l zh_CN``
+ tools/docs/checktransupdate.py -l zh_CN``
该命令会列出需è¦ç¿»è¯‘æˆ–æ›´æ–°çš„è‹±æ–‡æ–‡æ¡£ï¼Œç»“æžœåŒæ—¶ä¿å­˜åœ¨ checktransupdate.log 中。
diff --git a/Documentation/translations/zh_CN/kbuild/kbuild.rst b/Documentation/translations/zh_CN/kbuild/kbuild.rst
index e5e2aebe1ebc..57f5cf5b2cdd 100644
--- a/Documentation/translations/zh_CN/kbuild/kbuild.rst
+++ b/Documentation/translations/zh_CN/kbuild/kbuild.rst
@@ -93,6 +93,16 @@ HOSTRUSTFLAGS
-------------
åœ¨æž„å»ºä¸»æœºç¨‹åºæ—¶ä¼ é€’ç»™ $(HOSTRUSTC) çš„é¢å¤–标志。
+PROCMACROLDFLAGS
+----------------
+用于链接 Rust 过程å®çš„æ ‡å¿—ã€‚ç”±äºŽè¿‡ç¨‹å®æ˜¯ç”± rustc 在构建时加载的,
+因此必须以与当å‰ä½¿ç”¨çš„ rustc 工具链兼容的方å¼è¿›è¡Œé“¾æŽ¥ã€‚
+
+例如,当 rustc 使用的 C 库与用户希望用于主机程åºçš„ C 库ä¸åŒæ—¶ï¼Œ
+此设置会éžå¸¸æœ‰ç”¨ã€‚
+
+å¦‚æžœæœªè®¾ç½®ï¼Œåˆ™é»˜è®¤ä½¿ç”¨é“¾æŽ¥ä¸»æœºç¨‹åºæ—¶ä¼ é€’的标志。
+
HOSTLDFLAGS
-----------
é“¾æŽ¥ä¸»æœºç¨‹åºæ—¶ä¼ é€’çš„é¢å¤–选项。
@@ -135,12 +145,18 @@ KBUILD_OUTPUT
指定内核构建的输出目录。
在å•ç‹¬çš„æž„å»ºç›®å½•ä¸­ä¸ºé¢„æž„å»ºå†…æ ¸æž„å»ºå¤–éƒ¨æ¨¡å—æ—¶ï¼Œè¿™ä¸ªå˜é‡ä¹Ÿå¯ä»¥æŒ‡å‘内核输出目录。请注æ„,
-è¿™å¹¶ä¸æŒ‡å®šå¤–éƒ¨æ¨¡å—æœ¬èº«çš„输出目录。
+è¿™å¹¶ä¸æŒ‡å®šå¤–éƒ¨æ¨¡å—æœ¬èº«çš„输出目录(使用 KBUILD_EXTMOD_OUTPUT æ¥è¾¾åˆ°è¿™ä¸ªç›®çš„)。
输出目录也å¯ä»¥ä½¿ç”¨ "O=..." 指定。
设置 "O=..." 优先于 KBUILD_OUTPUT。
+KBUILD_EXTMOD_OUTPUT
+--------------------
+指定外部模å—的输出目录
+
+设置 "MO=..." 优先于 KBUILD_EXTMOD_OUTPUT.
+
KBUILD_EXTRA_WARN
-----------------
指定é¢å¤–的构建检查。也å¯ä»¥é€šè¿‡åœ¨å‘½ä»¤è¡Œä¼ é€’ "W=..." æ¥è®¾ç½®ç›¸åŒçš„值。
@@ -290,8 +306,13 @@ IGNORE_DIRS
KBUILD_BUILD_TIMESTAMP
----------------------
将该环境å˜é‡è®¾ç½®ä¸ºæ—¥æœŸå­—符串,å¯ä»¥è¦†ç›–在 UTS_VERSION 定义中使用的时间戳
-(è¿è¡Œå†…核时的 uname -v)。该值必须是一个å¯ä»¥ä¼ é€’ç»™ date -d 的字符串。默认值是
-内核构建æŸä¸ªæ—¶åˆ»çš„ date 命令输出。
+(è¿è¡Œå†…核时的 uname -v) 。该值必须是一个å¯ä»¥ä¼ é€’ç»™ date -d 的字符串。例如::
+
+ $ KBUILD_BUILD_TIMESTAMP="Mon Oct 13 00:00:00 UTC 2025" make
+
+默认值是内核构建æŸä¸ªæ—¶åˆ»çš„ date 命令输出。如果æä¾›è¯¥æ—¶æˆ³ï¼Œå®ƒè¿˜ç”¨äºŽä»»ä½• initramfs å½’
+档文件中的 mtime 字段。 Initramfs mtimes 是 32 ä½çš„,因此早于 Unix 纪元 1970 年,或
+晚于å调世界时 (UTC) 2106 å¹´ 2 月 7 æ—¥ 6 æ—¶ 28 分 15 秒的日期是无效的。
KBUILD_BUILD_USER, KBUILD_BUILD_HOST
------------------------------------
diff --git a/Documentation/translations/zh_CN/mm/active_mm.rst b/Documentation/translations/zh_CN/mm/active_mm.rst
index b3352668c4c8..9496a0bb7d07 100644
--- a/Documentation/translations/zh_CN/mm/active_mm.rst
+++ b/Documentation/translations/zh_CN/mm/active_mm.rst
@@ -87,4 +87,4 @@ Active MM
最丑陋的之一--ä¸åƒå…¶ä»–架构的MMå’Œå¯„å­˜å™¨çŠ¶æ€æ˜¯åˆ†å¼€çš„,alphaçš„PALcode将两者
连接起æ¥ï¼Œä½ éœ€è¦åŒæ—¶åˆ‡æ¢ä¸¤è€…)。
- (æ–‡æ¡£æ¥æº http://marc.info/?l=linux-kernel&m=93337278602211&w=2)
+ (æ–‡æ¡£æ¥æº https://lore.kernel.org/lkml/Pine.LNX.4.10.9907301410280.752-100000@penguin.transmeta.com/)
diff --git a/Documentation/translations/zh_CN/networking/generic-hdlc.rst b/Documentation/translations/zh_CN/networking/generic-hdlc.rst
new file mode 100644
index 000000000000..9e493dc9721e
--- /dev/null
+++ b/Documentation/translations/zh_CN/networking/generic-hdlc.rst
@@ -0,0 +1,176 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/networking/generic-hdlc.rst
+
+:翻译:
+
+ 孙渔喜 Sun yuxi <sun.yuxi@zte.com.cn>
+
+==========
+通用HDLC层
+==========
+
+Krzysztof Halasa <khc@pm.waw.pl>
+
+
+通用HDLC层当剿”¯æŒä»¥ä¸‹åè®®:
+
+1. 帧中继(支æŒANSIã€CCITTã€CiscoåŠæ— LMI模å¼ï¼‰
+
+ - 常规(路由)接å£å’Œä»¥å¤ªç½‘桥接(以太网设备仿真)接å£
+ å¯å…±äº«åŒä¸€æ¡PVC。
+ - 支æŒARPï¼ˆå†…æ ¸æš‚ä¸æ”¯æŒInARP,但å¯é€šè¿‡å®žéªŒæ€§ç”¨æˆ·ç©ºé—´å®ˆæŠ¤ç¨‹åºå®žçŽ°ï¼Œ
+ 下载地å€ï¼šhttp://www.kernel.org/pub/linux/utils/net/hdlc/)。
+
+2. 原始HDLC —— 支æŒIP(IPv4ï¼‰æŽ¥å£æˆ–以太网设备仿真
+3. Cisco HDLC
+4. PPP
+5. X.25(使用X.25å议栈)
+
+通用HDLC仅作为å议驱动 - å¿…é¡»é…åˆå…·ä½“硬件的底层驱动
+æ‰èƒ½è¿è¡Œã€‚
+
+以太网设备仿真(使用HDLC或帧中继PVC)兼容IEEE 802.1Q(VLAN)和
+802.1D(以太网桥接)。
+
+
+请确ä¿å·²åŠ è½½ hdlc.o 和硬件驱动程åºã€‚系统将为æ¯ä¸ªWAN端å£åˆ›å»ºä¸€ä¸ª
+"hdlc"网络设备(如hdlc0等)。您需è¦ä½¿ç”¨"sethdlc"工具,å¯ä»Žä»¥ä¸‹
+地å€èŽ·å–:
+
+ http://www.kernel.org/pub/linux/utils/net/hdlc/
+
+编译 sethdlc.c 工具::
+
+ gcc -O2 -Wall -o sethdlc sethdlc.c
+
+请确ä¿ä½¿ç”¨ä¸Žæ‚¨å†…核版本匹é…çš„ sethdlc 工具。
+
+使用 sethdlc å·¥å…·è®¾ç½®ç‰©ç†æŽ¥å£ã€æ—¶é’Ÿé¢‘率ã€HDLC 模å¼ï¼Œ
+若使用帧中继还需添加所需的 PVC。
+é€šå¸¸æ‚¨éœ€è¦æ‰§è¡Œç±»ä¼¼ä»¥ä¸‹å‘½ä»¤::
+
+ sethdlc hdlc0 clock int rate 128000
+ sethdlc hdlc0 cisco interval 10 timeout 25
+
+或::
+
+ sethdlc hdlc0 rs232 clock ext
+ sethdlc hdlc0 fr lmi ansi
+ sethdlc hdlc0 create 99
+ ifconfig hdlc0 up
+ ifconfig pvc0 localIP pointopoint remoteIP
+
+在帧中继模å¼ä¸‹ï¼Œè¯·å…ˆå¯ç”¨ä¸»hdlc设备(ä¸åˆ†é…IP地å€ï¼‰ï¼Œå†
+使用pvc设备。
+
+
+接å£è®¾ç½®é€‰é¡¹ï¼š
+
+* v35 | rs232 | x21 | t1 | e1
+ - å½“ç½‘å¡æ”¯æŒè½¯ä»¶å¯é€‰æŽ¥å£æ—¶ï¼Œå¯ä¸ºæŒ‡å®šç«¯å£è®¾ç½®ç‰©ç†æŽ¥å£
+ loopback
+ - å¯ç”¨ç¡¬ä»¶çŽ¯å›žï¼ˆä»…ç”¨äºŽæµ‹è¯•ï¼‰
+* clock ext
+ - RX与TXæ—¶é’Ÿå‡ä½¿ç”¨å¤–部时钟æº
+* clock int
+ - RX与TXæ—¶é’Ÿå‡ä½¿ç”¨å†…部时钟æº
+* clock txint
+ - RX时钟使用外部时钟æºï¼ŒTX时钟使用内部时钟æº
+* clock txfromrx
+ - RX时钟使用外部时钟æºï¼ŒTX时钟从RX时钟派生
+* rate
+ - 设置时钟速率(仅适用于"int"或"txint"时钟模å¼ï¼‰
+
+
+设置å议选项:
+
+* hdlc - 设置原始HDLC模å¼ï¼ˆä»…支æŒIPå议)
+
+ nrz / nrzi / fm-mark / fm-space / manchester - 传输编ç é€‰é¡¹
+
+ no-parity / crc16 / crc16-pr0 (预设零值的CRC16) / crc32-itu
+
+ crc16-itu (使用ITU-T多项å¼çš„CRC16) / crc16-itu-pr0 - 校验方å¼é€‰é¡¹
+
+* hdlc-eth - 使用HDLC进行以太网设备仿真. æ ¡éªŒå’Œç¼–ç æ–¹å¼åŒä¸Š
+ as above.
+
+* cisco - 设置Cisco HDLC模å¼ï¼ˆæ”¯æŒIPã€IPv6å’ŒIPXå议)
+
+ interval - ä¿æ´»æ•°æ®åŒ…å‘é€é—´éš”(秒)
+
+ timeout - æœªæ”¶åˆ°ä¿æ´»æ•°æ®åŒ…的超时时间(秒),超过此时长将判定
+ 链路断开
+
+* ppp - è®¾ç½®åŒæ­¥PPP模å¼
+
+* x25 - 设置X.25模å¼
+
+* fr - 帧中继模å¼
+
+ lmi ansi / ccitt / cisco / none - LMI(链路管ç†)类型
+
+ dce - 将帧中继设置为DCE(网络侧)LMI模å¼ï¼ˆé»˜è®¤ä¸ºDTE用户侧)。
+
+ 此设置与时钟无关ï¼
+
+ - t391 - 链路完整性验è¯è½®è¯¢å®šæ—¶å™¨ï¼ˆç§’)- 用户侧
+ - t392 - 轮询验è¯å®šæ—¶å™¨ï¼ˆç§’)- 网络侧
+ - n391 - 全状æ€è½®è¯¢è®¡æ•°å™¨ - 用户侧
+ - n392 - 错误阈值 - 用户侧和网络侧共用
+ - n393 - 监控事件计数 - 用户侧和网络侧共用
+
+帧中继专用命令:
+
+* create n | delete n - 添加/删除DLCIç¼–å·ä¸ºnçš„PVC接å£ã€‚
+ 新创建的接å£å°†å‘½å为pvc0ã€pvc1等。
+
+* create ether n | delete ether n - 添加/删除用于以太网
+ 桥接帧的设备设备将命å为pvceth0ã€pvceth1等。
+
+
+
+
+æ¿å¡ç‰¹å®šé—®é¢˜
+------------
+
+n2.o å’Œ c101.o 驱动模å—需è¦å‚æ•°æ‰èƒ½å·¥ä½œ::
+
+ insmod n2 hw=io,irq,ram,ports[:io,irq,...]
+
+示例::
+
+ insmod n2 hw=0x300,10,0xD0000,01
+
+或::
+
+ insmod c101 hw=irq,ram[:irq,...]
+
+示例::
+
+ insmod c101 hw=9,0xdc000
+
+若直接编译进内核,这些驱动需è¦é€šè¿‡å†…æ ¸(命令行)傿•°é…ç½®::
+
+ n2.hw=io,irq,ram,ports:...
+
+或::
+
+ c101.hw=irq,ram:...
+
+
+
+若您的N2ã€C101或PLX200SYNæ¿å¡å‡ºçŽ°é—®é¢˜ï¼Œå¯é€šè¿‡"private"
+å‘½ä»¤æŸ¥çœ‹ç«¯å£æ•°æ®åŒ…æè¿°ç¬¦çŽ¯ï¼ˆæ˜¾ç¤ºåœ¨å†…æ ¸æ—¥å¿—ä¸­ï¼‰
+
+ sethdlc hdlc0 private
+
+硬件驱动需使用#define DEBUG_RINGS编译选项构建。
+在æäº¤é”™è¯¯æŠ¥å‘Šæ—¶é™„上这些信æ¯å°†å¾ˆæœ‰å¸®åŠ©ã€‚å¦‚åœ¨ä½¿ç”¨è¿‡ç¨‹ä¸­é‡
+åˆ°ä»»ä½•é—®é¢˜ï¼Œè¯·éšæ—¶å‘ŠçŸ¥ã€‚
+
+获å–è¡¥ä¸å’Œå…¶ä»–ä¿¡æ¯ï¼Œè¯·è®¿é—®ï¼š
+<http://www.kernel.org/pub/linux/utils/net/hdlc/>. \ No newline at end of file
diff --git a/Documentation/translations/zh_CN/networking/index.rst b/Documentation/translations/zh_CN/networking/index.rst
index bb0edcffd144..c276c0993c51 100644
--- a/Documentation/translations/zh_CN/networking/index.rst
+++ b/Documentation/translations/zh_CN/networking/index.rst
@@ -27,6 +27,9 @@
xfrm_proc
netmem
alias
+ mptcp-sysctl
+ generic-hdlc
+ timestamping
Todolist:
@@ -76,7 +79,6 @@ Todolist:
* eql
* fib_trie
* filter
-* generic-hdlc
* generic_netlink
* netlink_spec/index
* gen_stats
@@ -96,7 +98,6 @@ Todolist:
* mctp
* mpls-sysctl
* mptcp
-* mptcp-sysctl
* multiqueue
* multi-pf-netdev
* net_cachelines/index
@@ -126,7 +127,6 @@ Todolist:
* sctp
* secid
* seg6-sysctl
-* skbuff
* smc-sysctl
* sriov
* statistics
@@ -138,7 +138,6 @@ Todolist:
* tcp_ao
* tcp-thin
* team
-* timestamping
* tipc
* tproxy
* tuntap
diff --git a/Documentation/translations/zh_CN/networking/mptcp-sysctl.rst b/Documentation/translations/zh_CN/networking/mptcp-sysctl.rst
new file mode 100644
index 000000000000..0b1b9ed7c647
--- /dev/null
+++ b/Documentation/translations/zh_CN/networking/mptcp-sysctl.rst
@@ -0,0 +1,139 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/networking/mptcp-sysctl.rst
+
+:翻译:
+
+ 孙渔喜 Sun yuxi <sun.yuxi@zte.com.cn>
+
+================
+MPTCP Sysfs å˜é‡
+================
+
+/proc/sys/net/mptcp/* Variables
+===============================
+
+add_addr_timeout - INTEGER (ç§’)
+ 设置ADD_ADDR控制消æ¯çš„é‡ä¼ è¶…时时间。当MPTCP对端未确认
+ å…ˆå‰çš„ADD_ADDRæ¶ˆæ¯æ—¶ï¼Œå°†åœ¨è¯¥è¶…æ—¶æ—¶é—´åŽé‡æ–°å‘é€ã€‚
+
+ 默认值与TCP_RTO_MAX相åŒã€‚此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:120
+
+allow_join_initial_addr_port - BOOLEAN
+ 控制是å¦å…许对端å‘åˆå§‹å­æµä½¿ç”¨çš„IP地å€å’Œç«¯å£å·å‘é€åŠ å…¥
+ 请求(1表示å…è®¸ï¼‰ã€‚æ­¤å‚æ•°ä¼šè®¾ç½®è¿žæŽ¥æ—¶å‘é€ç»™å¯¹ç«¯çš„æ ‡å¿—ä½ï¼Œ
+ å¹¶å†³å®šæ˜¯å¦æŽ¥å—æ­¤ç±»åŠ å…¥è¯·æ±‚ã€‚
+
+ 通过ADD_ADDR通告的地å€ä¸å—æ­¤å‚æ•°å½±å“。
+
+ 此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:1
+
+available_path_managers - STRING
+ 显示已注册的å¯ç”¨è·¯å¾„管ç†å™¨é€‰é¡¹ã€‚å¯èƒ½æœ‰æ›´å¤šè·¯å¾„管ç†å™¨å¯ç”¨
+ 但尚未加载。
+
+available_schedulers - STRING
+ 显示已注册的å¯ç”¨è°ƒåº¦å™¨é€‰é¡¹ã€‚å¯èƒ½æœ‰æ›´å¤šæ•°æ®åŒ…调度器å¯ç”¨
+ 但尚未加载。
+
+blackhole_timeout - INTEGER (ç§’)
+ 当å‘生MPTCP防ç«å¢™é»‘洞问题时,åˆå§‹ç¦ç”¨æ´»è·ƒMPTCP套接字上MPTCP
+ åŠŸèƒ½çš„æ—¶é—´ï¼ˆç§’ï¼‰ã€‚å¦‚æžœåœ¨é‡æ–°å¯ç”¨MPTCPåŽç«‹å³æ£€æµ‹åˆ°æ›´å¤šé»‘洞问题,
+ 此时间段将呈指数增长;当黑洞问题消失时,将é‡ç½®ä¸ºåˆå§‹å€¼ã€‚
+
+ 设置为0å¯ç¦ç”¨é»‘洞检测功能。此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:3600
+
+checksum_enabled - BOOLEAN
+ 控制是å¦å¯ç”¨DSS校验和功能。
+
+ 当值为éžé›¶æ—¶å¯å¯ç”¨DSS校验和。此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:0
+
+close_timeout - INTEGER (seconds)
+ 设置"先断åŽè¿ž"超时时间:在未调用close或shutdown系统调用时,
+ MPTCP套接字将在最åŽä¸€ä¸ªå­æµç§»é™¤åŽä¿æŒå½“å‰çжæ€è¾¾åˆ°è¯¥æ—¶é•¿ï¼Œæ‰
+ 会转为TCP_CLOSE状æ€ã€‚
+
+ 默认值与TCP_TIMEWAIT_LEN相åŒã€‚此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:60
+
+enabled - BOOLEAN
+ 控制是å¦å…许创建MPTCP套接字。
+
+ 当值为1æ—¶å…许创建MPTCP套接字。此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:1(å¯ç”¨ï¼‰
+
+path_manager - STRING
+ 设置用于æ¯ä¸ªæ–°MPTCP套接字的默认路径管ç†å™¨å称。内核路径管ç†å°†
+ æ ¹æ®é€šè¿‡MPTCP netlink APIé…置的æ¯ä¸ªå‘½åç©ºé—´å€¼æ¥æŽ§åˆ¶å­æµè¿žæŽ¥
+ 和地å€é€šå‘Šã€‚用户空间路径管ç†å°†æ¯ä¸ªMPTCPè¿žæŽ¥çš„å­æµè¿žæŽ¥å†³ç­–和地
+ å€é€šå‘Šäº¤ç”±ç‰¹æƒç”¨æˆ·ç©ºé—´ç¨‹åºæŽ§åˆ¶ï¼Œä»£ä»·æ˜¯éœ€è¦æ›´å¤šnetlinkæµé‡æ¥
+ 传播所有相关事件和命令。
+
+ 此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ * "kernel" - 内核路径管ç†å™¨
+ * "userspace" - 用户空间路径管ç†å™¨
+
+ 默认值:"kernel"
+
+pm_type - INTEGER
+ 设置用于æ¯ä¸ªæ–°MPTCP套接字的默认路径管ç†å™¨ç±»åž‹ã€‚内核路径管ç†å°†
+ æ ¹æ®é€šè¿‡MPTCP netlink APIé…置的æ¯ä¸ªå‘½åç©ºé—´å€¼æ¥æŽ§åˆ¶å­æµè¿žæŽ¥
+ 和地å€é€šå‘Šã€‚用户空间路径管ç†å°†æ¯ä¸ªMPTCPè¿žæŽ¥çš„å­æµè¿žæŽ¥å†³ç­–和地
+ å€é€šå‘Šäº¤ç”±ç‰¹æƒç”¨æˆ·ç©ºé—´ç¨‹åºæŽ§åˆ¶ï¼Œä»£ä»·æ˜¯éœ€è¦æ›´å¤šnetlinkæµé‡æ¥
+ 传播所有相关事件和命令。
+
+ 此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 自v6.15起已弃用,请改用path_manager傿•°ã€‚
+
+ * 0 - 内核路径管ç†å™¨
+ * 1 - 用户空间路径管ç†å™¨
+
+ 默认值:0
+
+scheduler - STRING
+ 选择所需的调度器类型。
+
+ 支æŒé€‰æ‹©ä¸åŒçš„æ•°æ®åŒ…调度器。此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:"default"
+
+stale_loss_cnt - INTEGER
+ ç”¨äºŽåˆ¤å®šå­æµå¤±æ•ˆï¼ˆstale)的MPTCP层é‡ä¼ é—´é𔿬¡æ•°é˜ˆå€¼ã€‚当指定
+ å­æµåœ¨è¿žç»­å¤šä¸ªé‡ä¼ é—´éš”内既无数æ®ä¼ è¾“åˆæœ‰å¾…å¤„ç†æ•°æ®æ—¶ï¼Œå°†è¢«æ ‡
+ 记为失效状æ€ã€‚å¤±æ•ˆå­æµå°†è¢«æ•°æ®åŒ…调度器忽略。
+ 设置较低的stale_loss_cnt值å¯å®žçŽ°å¿«é€Ÿä¸»å¤‡åˆ‡æ¢ï¼Œè¾ƒé«˜çš„值则能
+ 最大化边缘场景(如高误ç çŽ‡é“¾è·¯æˆ–å¯¹ç«¯æš‚åœæ•°æ®å¤„ç†ç­‰å¼‚常情况)
+ 的链路利用率。
+
+ 此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:4
+
+syn_retrans_before_tcp_fallback - INTEGER
+ 在回退到 TCP(å³ä¸¢å¼ƒ MPTCP 选项)之å‰ï¼ŒSYN + MP_CAPABLE
+ 报文的é‡ä¼ æ¬¡æ•°ã€‚æ¢å¥è¯è¯´ï¼Œå¦‚果所有报文在传输过程中都被丢弃,
+ 那么将会:
+
+ * 首次SYNæºå¸¦MPTCP支æŒé€‰é¡¹
+ * æŒ‰æœ¬å‚æ•°å€¼é‡ä¼ æºå¸¦MPTCP选项的SYN包
+ * åŽç»­é‡ä¼ å°†ä¸å†æºå¸¦MPTCP支æŒé€‰é¡¹
+
+ 0 表示首次é‡ä¼ å³ä¸¢å¼ƒMPTCP选项。
+ >=128 表示所有SYNé‡ä¼ å‡ä¿ç•™MPTCP选项设置过低的值å¯èƒ½å¢žåŠ 
+ MPTCP黑洞误判几率。此为æ¯ä¸ªå‘½å空间的sysctl傿•°ã€‚
+
+ 默认值:2
diff --git a/Documentation/translations/zh_CN/networking/timestamping.rst b/Documentation/translations/zh_CN/networking/timestamping.rst
new file mode 100644
index 000000000000..4593f53ad09a
--- /dev/null
+++ b/Documentation/translations/zh_CN/networking/timestamping.rst
@@ -0,0 +1,674 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/networking/timestamping.rst
+
+:翻译:
+
+ 王亚鑫 Wang Yaxin <wang.yaxin@zte.com.cn>
+
+======
+时间戳
+======
+
+
+1. 控制接å£
+===========
+
+接收网络数æ®åŒ…时间戳的接å£åŒ…括:
+
+SO_TIMESTAMP
+ 为æ¯ä¸ªä¼ å…¥æ•°æ®åŒ…生æˆï¼ˆä¸ä¸€å®šæ˜¯å•调的)系统时间时间戳。通过 recvmsg()
+ 在控制消æ¯ä¸­ä»¥å¾®ç§’分辨率报告时间戳。
+ SO_TIMESTAMP æ ¹æ®æž¶æž„类型和 libc çš„ lib 中的 time_t 表示方å¼å®šä¹‰ä¸º
+ SO_TIMESTAMP_NEW 或 SO_TIMESTAMP_OLD。
+ SO_TIMESTAMP_OLD å’Œ SO_TIMESTAMP_NEW çš„æŽ§åˆ¶æ¶ˆæ¯æ ¼å¼åˆ†åˆ«ä¸º
+ struct __kernel_old_timeval 和 struct __kernel_sock_timeval。
+
+SO_TIMESTAMPNS
+ 与 SO_TIMESTAMP 相åŒçš„æ—¶é—´æˆ³æœºåˆ¶ï¼Œä½†ä»¥ struct timespec æ ¼å¼æŠ¥å‘Šæ—¶é—´æˆ³ï¼Œ
+ 纳秒分辨率。
+ SO_TIMESTAMPNS æ ¹æ®æž¶æž„类型和 libc çš„ time_t 表示方å¼å®šä¹‰ä¸º
+ SO_TIMESTAMPNS_NEW 或 SO_TIMESTAMPNS_OLD。
+ æŽ§åˆ¶æ¶ˆæ¯æ ¼å¼å¯¹äºŽ SO_TIMESTAMPNS_OLD 为 struct timespec,
+ 对于 SO_TIMESTAMPNS_NEW 为 struct __kernel_timespec。
+
+IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
+ 仅用于多播:通过读å–回环数æ®åŒ…接收时间戳,获得近似的传输时间戳。
+
+SO_TIMESTAMPING
+ 在接收ã€ä¼ è¾“æˆ–ä¸¤è€…æ—¶ç”Ÿæˆæ—¶é—´æˆ³ã€‚支æŒå¤šä¸ªæ—¶é—´æˆ³æºï¼ŒåŒ…括硬件。
+ 支æŒä¸ºæµå¥—æŽ¥å­—ç”Ÿæˆæ—¶é—´æˆ³ã€‚
+
+
+1.1 SO_TIMESTAMP(也包括 SO_TIMESTAMP_OLD 和 SO_TIMESTAMP_NEW)
+---------------------------------------------------------------
+
+此套接字选项在接收路径上å¯ç”¨æ•°æ®æŠ¥çš„æ—¶é—´æˆ³ã€‚由于目标套接字(如果有)
+在网络栈早期未知,因此必须为所有数æ®åŒ…å¯ç”¨æ­¤åŠŸèƒ½ã€‚æ‰€æœ‰æ—©æœŸæŽ¥æ”¶çš„æ—¶é—´
+戳选项也是如此。
+
+有关接å£è¯¦ç»†ä¿¡æ¯ï¼Œè¯·å‚阅 `man 7 socket`。
+
+始终使用 SO_TIMESTAMP_NEW 时间戳以获得 struct __kernel_sock_timeval
+æ ¼å¼çš„æ—¶é—´æˆ³ã€‚
+
+如果时间在 2038 å¹´åŽï¼ŒSO_TIMESTAMP_OLD 在 32 使œºå™¨ä¸Šå°†è¿”回错误的时间戳。
+
+1.2 SO_TIMESTAMPNS(也包括 SO_TIMESTAMPNS_OLD 和 SO_TIMESTAMPNS_NEW)
+---------------------------------------------------------------------
+
+此选项与 SO_TIMESTAMP 相åŒï¼Œä½†è¿”回数æ®ç±»åž‹æœ‰æ‰€ä¸åŒã€‚å…¶ struct timespec
+能达到比 SO_TIMESTAMP 的 timeval(毫秒)更高的分辨率(纳秒)时间戳。
+
+始终使用 SO_TIMESTAMPNS_NEW 时间戳获得 struct __kernel_timespec æ ¼å¼
+的时间戳。
+
+如果时间在 2038 å¹´åŽï¼ŒSO_TIMESTAMPNS_OLD 在 32 使œºå™¨ä¸Šå°†è¿”回错误的时间戳。
+
+1.3 SO_TIMESTAMPING(也包括 SO_TIMESTAMPING_OLD 和 SO_TIMESTAMPING_NEW)
+------------------------------------------------------------------------
+
+支æŒå¤šç§ç±»åž‹çš„æ—¶é—´æˆ³è¯·æ±‚ã€‚å› æ­¤ï¼Œæ­¤å¥—æŽ¥å­—é€‰é¡¹æŽ¥å—æ ‡å¿—ä½å›¾ï¼Œè€Œä¸æ˜¯å¸ƒå°”值。在::
+
+ err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
+
+val 是一个整数,设置了以下任何ä½ã€‚设置其他ä½å°†è¿”回 EINVAL 䏔䏿›´æ”¹å½“å‰çжæ€ã€‚
+
+这个套接字选项é…置以下几个方é¢çš„æ—¶é—´æˆ³ç”Ÿæˆï¼š
+为å•个 sk_buff ç»“æž„ä½“ç”Ÿæˆæ—¶é—´æˆ³ï¼ˆ1.3.1);
+将时间戳报告到套接字的错误队列(1.3.2);
+é…置相关选项(1.3.3);
+也å¯ä»¥é€šè¿‡ cmsg 为å•个 sendmsg 调用å¯ç”¨æ—¶é—´æˆ³ç”Ÿæˆï¼ˆ1.3.4)。
+
+1.3.1 时间戳生æˆ
+^^^^^^^^^^^^^^^^
+
+æŸäº›ä½æ˜¯å‘å议栈请求å°è¯•ç”Ÿæˆæ—¶é—´æˆ³ã€‚它们的任何组åˆéƒ½æ˜¯æœ‰æ•ˆçš„。对这些ä½çš„æ›´æ”¹é€‚
+用于新创建的数æ®åŒ…ï¼Œè€Œä¸æ˜¯å·²ç»åœ¨å议栈中的数æ®åŒ…。因此,å¯ä»¥é€šè¿‡åœ¨ä¸¤ä¸ª setsockopt
+调用之间嵌入 send() 调用æ¥é€‰æ‹©æ€§åœ°ä¸ºæ•°æ®åŒ…å­é›†è¯·æ±‚时间戳(例如,用于采样),
+一个用于å¯ç”¨æ—¶é—´æˆ³ç”Ÿæˆï¼Œä¸€ä¸ªç”¨äºŽç¦ç”¨å®ƒã€‚时间戳也å¯èƒ½ç”±äºŽç‰¹å®šå¥—接字请求之外的原
+因而生æˆï¼Œä¾‹å¦‚在当系统范围内å¯ç”¨æŽ¥æ”¶æ—¶é—´æˆ³æ—¶ï¼Œå¦‚剿‰€è¿°ã€‚
+
+SOF_TIMESTAMPING_RX_HARDWARE:
+ 请求由网络适é…器生æˆçš„æŽ¥æ”¶æ—¶é—´æˆ³ã€‚
+
+SOF_TIMESTAMPING_RX_SOFTWARE:
+ 当数æ®è¿›å…¥å†…核时请求接收时间戳。这些时间戳在设备驱动程åºå°†æ•°æ®åŒ…交给内核接收
+ å议栈åŽç”Ÿæˆã€‚
+
+SOF_TIMESTAMPING_TX_HARDWARE:
+ 请求由网络适é…器生æˆçš„传输时间戳。此标志å¯ä»¥é€šè¿‡å¥—接字选项和控制消æ¯å¯ç”¨ã€‚
+
+SOF_TIMESTAMPING_TX_SOFTWARE:
+ 当数æ®ç¦»å¼€å†…核时请求传输(TX)时间戳。这些时间戳由设备驱动程åºç”Ÿæˆï¼Œå¹¶ä¸”å°½
+ å¯èƒ½è´´è¿‘网络接å£å‘é€ç‚¹ï¼Œä½†å§‹ç»ˆåœ¨å†…核将数æ®åŒ…传递给网络接å£ä¹‹å‰ç”Ÿæˆã€‚因此,
+ 它们需è¦é©±åŠ¨ç¨‹åºæ”¯æŒï¼Œä¸”å¯èƒ½å¹¶éžæ‰€æœ‰è®¾å¤‡éƒ½å¯ç”¨ã€‚此标志å¯é€šè¿‡å¥—接字选项和
+ 控制消æ¯ä¸¤ç§æ–¹å¼å¯ç”¨ã€‚
+
+SOF_TIMESTAMPING_TX_SCHED:
+ 在进入数æ®åŒ…调度器之å‰è¯·æ±‚传输时间戳。内核传输延迟(如果很长)通常由排队
+ 延迟主导。此时间戳与在 SOF_TIMESTAMPING_TX_SOFTWARE 处获å–的时间戳之
+ 间的差异将暴露此延迟,并且与åè®®å¤„ç†æ— å…³ã€‚å议处ç†ä¸­äº§ç”Ÿçš„延迟(如果有)
+ å¯ä»¥é€šè¿‡ä»Ž send() 之å‰ç«‹å³èŽ·å–的用户空间时间戳中å‡åŽ»æ­¤æ—¶é—´æˆ³æ¥è®¡ç®—。在
+ 具有虚拟设备的机器上,传输的数æ®åŒ…通过多个设备和多个数æ®åŒ…调度器,在æ¯å±‚
+ ç”Ÿæˆæ—¶é—´æˆ³ã€‚è¿™å…许对排队延迟进行细粒度测é‡ã€‚此标志å¯ä»¥é€šè¿‡å¥—接字选项和控
+ 制消æ¯å¯ç”¨ã€‚
+
+SOF_TIMESTAMPING_TX_ACK:
+ 请求在å‘é€ç¼“冲区中的所有数æ®éƒ½å·²å¾—到确认时生æˆä¼ è¾“(TX)时间戳。此选项
+ 仅适用于å¯é å议,目å‰ä»…在TCPå议中实现。对于该å议,它å¯èƒ½ä¼šè¿‡åº¦æŠ¥å‘Š
+ 测é‡ç»“果,因为时间戳是在send()调用时缓冲区中的所有数æ®ï¼ˆåŒ…括该缓冲区)
+ 都被确认时生æˆçš„,å³ç´¯ç§¯ç¡®è®¤ã€‚该机制会忽略选择确认(SACK)和å‰å‘确认
+ (FACK)。此标志å¯é€šè¿‡å¥—接字选项和控制消æ¯ä¸¤ç§æ–¹å¼å¯ç”¨ã€‚
+
+SOF_TIMESTAMPING_TX_COMPLETION:
+ 在数æ®åŒ…ä¼ è¾“å®Œæˆæ—¶è¯·æ±‚ä¼ è¾“æ—¶é—´æˆ³ã€‚å®Œæˆæ—¶é—´æˆ³ç”±å†…核在从硬件接收数æ®åŒ…完æˆ
+ 报告时生æˆã€‚硬件å¯èƒ½ä¸€æ¬¡æŠ¥å‘Šå¤šä¸ªæ•°æ®åŒ…ï¼Œå®Œæˆæ—¶é—´æˆ³å映报告的时åºè€Œä¸æ˜¯å®ž
+ 际传输时间。此标志å¯ä»¥é€šè¿‡å¥—接字选项和控制消æ¯å¯ç”¨ã€‚
+
+
+1.3.2 时间戳报告
+^^^^^^^^^^^^^^^^
+
+å…¶ä»–ä¸‰ä¸ªä½æŽ§åˆ¶å°†åœ¨ç”Ÿæˆçš„æŽ§åˆ¶æ¶ˆæ¯ä¸­æŠ¥å‘Šå“ªäº›æ—¶é—´æˆ³ã€‚对这些ä½çš„æ›´æ”¹åœ¨å议栈中
+的时间戳报告ä½ç½®ç«‹å³ç”Ÿæ•ˆã€‚仅当数æ®åŒ…设置了相关的时间戳生æˆè¯·æ±‚时,æ‰ä¼šæŠ¥å‘Š
+其时间戳。
+
+SOF_TIMESTAMPING_SOFTWARE:
+ 在å¯ç”¨æ—¶æŠ¥å‘Šä»»ä½•软件时间戳。
+
+SOF_TIMESTAMPING_SYS_HARDWARE:
+ 此选项已被弃用和忽略。
+
+SOF_TIMESTAMPING_RAW_HARDWARE:
+ 在å¯ç”¨æ—¶æŠ¥å‘Šç”± SOF_TIMESTAMPING_TX_HARDWARE 或 SOF_TIMESTAMPING_RX_HARDWARE
+ 生æˆçš„硬件时间戳。
+
+
+1.3.3 时间戳选项
+^^^^^^^^^^^^^^^^
+
+æŽ¥å£æ”¯æŒä»¥ä¸‹é€‰é¡¹
+
+SOF_TIMESTAMPING_OPT_ID:
+ æ¯ä¸ªæ•°æ®åŒ…生æˆä¸€ä¸ªå”¯ä¸€æ ‡è¯†ç¬¦ã€‚一个进程å¯ä»¥åŒæ—¶å­˜åœ¨å¤šä¸ªæœªå®Œæˆçš„æ—¶é—´æˆ³è¯·æ±‚。
+ æ•°æ®åŒ…在传输路径中å¯èƒ½ä¼šå‘ç”Ÿé‡æŽ’åºï¼ˆä¾‹å¦‚在数æ®åŒ…è°ƒåº¦å™¨ä¸­ï¼‰ã€‚åœ¨è¿™ç§æƒ…况下,
+ 时间戳会以与原始send()调用ä¸åŒçš„é¡ºåºæŽ’é˜Ÿåˆ°é”™è¯¯é˜Ÿåˆ—ä¸­ã€‚å¦‚æ­¤ä¸€æ¥ï¼Œä»…æ ¹æ®
+ æ—¶é—´æˆ³é¡ºåºæˆ– payload(有效载è·ï¼‰æ£€æŸ¥ï¼Œå¹¶ä¸æ€»èƒ½å°†æ—¶é—´æˆ³ä¸ŽåŽŸå§‹send()调用
+ 唯一匹é…。
+
+ 此选项在 send() æ—¶å°†æ¯ä¸ªæ•°æ®åŒ…与唯一标识符关è”,并与时间戳一起返回。
+ 标识符æºè‡ªæ¯ä¸ªå¥—接字的 u32 è®¡æ•°å™¨ï¼ˆä¼šå›žç»•ï¼‰ã€‚å¯¹äºŽæ•°æ®æŠ¥å¥—æŽ¥å­—ï¼Œè®¡æ•°å™¨
+ éšæ¯ä¸ªå‘é€çš„æ•°æ®åŒ…递增。对于æµå¥—æŽ¥å­—ï¼Œå®ƒéšæ¯ä¸ªå­—节递增。对于æµå¥—接字,
+ 还è¦è®¾ç½® SOF_TIMESTAMPING_OPT_ID_TCP,请å‚阅下é¢çš„部分。
+
+ 计数器从零开始。在首次å¯ç”¨å¥—接字选项时åˆå§‹åŒ–。在ç¦ç”¨åŽå†é‡æ–°å¯ç”¨é€‰é¡¹æ—¶
+ é‡ç½®ã€‚é‡ç½®è®¡æ•°å™¨ä¸ä¼šæ›´æ”¹ç³»ç»Ÿä¸­çŽ°æœ‰æ•°æ®åŒ…的标识符。
+
+ æ­¤é€‰é¡¹ä»…é’ˆå¯¹ä¼ è¾“æ—¶é—´æˆ³å®žçŽ°ã€‚åœ¨è¿™ç§æƒ…况下,时间戳总是与sock_extended_err
+ 结构体一起回环。该选项会修改ee_dataå­—æ®µï¼Œä»¥ä¼ é€’ä¸€ä¸ªåœ¨è¯¥å¥—æŽ¥å­—æ‰€æœ‰åŒæ—¶
+ å­˜åœ¨çš„æœªå®Œæˆæ—¶é—´æˆ³è¯·æ±‚中唯一的 ID。
+
+ 进程å¯ä»¥é€šè¿‡æŽ§åˆ¶æ¶ˆæ¯SCM_TS_OPT_ID(TCP å¥—æŽ¥å­—ä¸æ”¯æŒï¼‰ä¼ é€’特定 ID,
+ 从而选择性地覆盖默认生æˆçš„ ID,示例如下::
+
+ struct msghdr *msg;
+ ...
+ cmsg = CMSG_FIRSTHDR(msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_TS_OPT_ID;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
+ *((__u32 *) CMSG_DATA(cmsg)) = opt_id;
+ err = sendmsg(fd, msg, 0);
+
+
+SOF_TIMESTAMPING_OPT_ID_TCP:
+ 与 SOF_TIMESTAMPING_OPT_ID 一起传递给新的 TCP 时间戳应用程åºã€‚
+ SOF_TIMESTAMPING_OPT_ID 定义了æµå¥—接字计数器的增é‡ï¼Œä½†å…¶èµ·å§‹ç‚¹
+ å¹¶ä¸å®Œå…¨æ˜¾è€Œæ˜“è§ã€‚此选项修å¤äº†è¿™ä¸€ç‚¹ã€‚
+
+ 对于æµå¥—接字,如果设置了 SOF_TIMESTAMPING_OPT_ID,则此选项应始终
+ è®¾ç½®ã€‚åœ¨æ•°æ®æŠ¥å¥—æŽ¥å­—ä¸Šï¼Œé€‰é¡¹æ²¡æœ‰æ•ˆæžœã€‚
+
+ 一个åˆç†çš„æœŸæœ›æ˜¯ç³»ç»Ÿè°ƒç”¨åŽè®¡æ•°å™¨é‡ç½®ä¸ºé›¶ï¼Œå› æ­¤åŽç»­å†™å…¥ N 字节将生æˆ
+ 计数器为 N-1 的时间戳。SOF_TIMESTAMPING_OPT_ID_TCP 在所有æ¡ä»¶ä¸‹
+ 都实现了此行为。
+
+ SOF_TIMESTAMPING_OPT_ID ä¸å¸¦ä¿®é¥°ç¬¦æ—¶é€šå¸¸æŠ¥å‘Šç›¸åŒï¼Œç‰¹åˆ«æ˜¯åœ¨å¥—接字选项
+ 在无数æ®ä¼ è¾“时设置时。如果正在传输数æ®ï¼Œå®ƒå¯èƒ½ä¸Žè¾“出队列的长度(SIOCOUTQ)
+ å差。
+
+ 差异是由于基于 snd_una 与 write_seq 的。snd_una 是 peer 确认的 stream
+ çš„åç§»é‡ã€‚è¿™å–决于外部因素,例如网络 RTT。write_seq 是进程写入的最åŽä¸€ä¸ª
+ 字节。此åç§»é‡ä¸å—外部输入影å“。
+
+ 差异细微,在套接字选项åˆå§‹åŒ–æ—¶é…ç½®æ—¶ä¸æ˜“察觉,但 SOF_TIMESTAMPING_OPT_ID_TCP
+ 行为在任何时候都更稳å¥ã€‚
+
+SOF_TIMESTAMPING_OPT_CMSG:
+ æ”¯æŒæ‰€æœ‰æ—¶é—´æˆ³æ•°æ®åŒ…çš„ recv() cmsg。控制消æ¯å·²æ— æ¡ä»¶åœ°åœ¨æ‰€æœ‰æŽ¥æ”¶æ—¶é—´æˆ³æ•°æ®åŒ…
+ å’Œ IPv6 æ•°æ®åŒ…上支æŒï¼Œä»¥åŠåœ¨å‘逿—¶é—´æˆ³æ•°æ®åŒ…çš„ IPv4 æ•°æ®åŒ…上支æŒã€‚此选项扩展
+ 了它们以在å‘逿—¶é—´æˆ³æ•°æ®åŒ…çš„ IPv4 æ•°æ®åŒ…上支æŒã€‚一个用例是å¯ç”¨ socket 选项
+ IP_PKTINFO ä»¥å…³è”æ•°æ®åŒ…与其出å£è®¾å¤‡ï¼Œé€šè¿‡å¯ç”¨ socket 选项 IP_PKTINFO åŒæ—¶ã€‚
+
+
+SOF_TIMESTAMPING_OPT_TSONLY:
+ 仅适用于传输时间戳。使内核返回一个 cmsg 与一个空数æ®åŒ…ä¸€èµ·ï¼Œè€Œä¸æ˜¯ä¸ŽåŽŸ
+ å§‹æ•°æ®åŒ…一起。这å‡å°‘了套接字接收预算(SO_RCVBUF)中收å–的内存é‡ï¼Œå¹¶å³ä½¿
+ 在 sysctl net.core.tstamp_allow_data 为 0 时也æä¾›æ—¶é—´æˆ³ã€‚此选项ç¦ç”¨
+ SOF_TIMESTAMPING_OPT_CMSG。
+
+SOF_TIMESTAMPING_OPT_STATS:
+ 与传输时间戳一起获å–的选项性统计信æ¯ã€‚它必须与 SOF_TIMESTAMPING_OPT_TSONLY
+ 一起使用。当传输时间戳å¯ç”¨æ—¶ï¼Œç»Ÿè®¡ä¿¡æ¯å¯åœ¨ç±»åž‹ä¸º SCM_TIMESTAMPING_OPT_STATS
+ çš„å•独控制消æ¯ä¸­èŽ·å–,作为 TLV(struct nlattr)类型的列表。这些统计信æ¯å…许应
+ 用程åºå°†å„ç§ä¼ è¾“层统计信æ¯ä¸Žä¼ è¾“时间戳关è”,例如æŸä¸ªæ•°æ®å—被 peer 的接收窗å£é™
+ 制了多长时间。
+
+SOF_TIMESTAMPING_OPT_PKTINFO:
+ å¯ç”¨ SCM_TIMESTAMPING_PKTINFO 控制消æ¯ä»¥æŽ¥æ”¶å¸¦æœ‰ç¡¬ä»¶æ—¶é—´æˆ³çš„æ•°æ®åŒ…。
+ 消æ¯åŒ…å« struct scm_ts_pktinfo,它æä¾›æŽ¥æ”¶æ•°æ®åŒ…的实际接å£ç´¢å¼•和层 2 长度。
+ åªæœ‰åœ¨ CONFIG_NET_RX_BUSY_POLL å¯ç”¨ä¸”驱动程åºä½¿ç”¨ NAPI 时,æ‰ä¼šè¿”回éžé›¶çš„
+ 有效接å£ç´¢å¼•。该结构还包å«å¦å¤–两个字段,但它们是ä¿ç•™å­—段且未定义。
+
+SOF_TIMESTAMPING_OPT_TX_SWHW:
+ 请求在 SOF_TIMESTAMPING_TX_HARDWARE 和 SOF_TIMESTAMPING_TX_SOFTWARE
+ åŒæ—¶å¯ç”¨æ—¶ï¼Œä¸ºä¼ å‡ºæ•°æ®åŒ…生æˆç¡¬ä»¶å’Œè½¯ä»¶æ—¶é—´æˆ³ã€‚å¦‚æžœåŒæ—¶ç”Ÿæˆä¸¤ä¸ªæ—¶é—´æˆ³ï¼Œä¸¤ä¸ªå•
+ 独的消æ¯å°†å›žçŽ¯åˆ°å¥—æŽ¥å­—çš„é”™è¯¯é˜Ÿåˆ—ï¼Œæ¯ä¸ªæ¶ˆæ¯ä»…包å«ä¸€ä¸ªæ—¶é—´æˆ³ã€‚
+
+SOF_TIMESTAMPING_OPT_RX_FILTER:
+ è¿‡æ»¤æŽ‰è™šå‡æŽ¥æ”¶æ—¶é—´æˆ³ï¼šä»…å½“åŒ¹é…çš„æ—¶é—´æˆ³ç”Ÿæˆæ ‡å¿—å·²å¯ç”¨æ—¶æ‰æŠ¥å‘ŠæŽ¥æ”¶æ—¶é—´æˆ³ã€‚
+
+ 接收时间戳在入å£è·¯å¾„中生æˆè¾ƒæ—©ï¼Œåœ¨æ•°æ®åŒ…的目的套接字确定之å‰ã€‚如果任何套接
+ å­—å¯ç”¨æŽ¥æ”¶æ—¶é—´æˆ³ï¼Œæ‰€æœ‰å¥—接字的数æ®åŒ…将接收时间戳数æ®åŒ…。包括那些请求时间戳
+ 报告与 SOF_TIMESTAMPING_SOFTWARE 和/或 SOF_TIMESTAMPING_RAW_HARDWARE,
+ 但未请求接收时间戳生æˆã€‚è¿™å¯èƒ½å‘生在仅请求å‘逿—¶é—´æˆ³æ—¶ã€‚
+
+ æŽ¥æ”¶è™šå‡æ—¶é—´æˆ³é€šå¸¸æ˜¯æ— å®³çš„。进程å¯ä»¥å¿½ç•¥æ„外的éžé›¶å€¼ã€‚但它使行为在其他套接
+ 字上微妙地ä¾èµ–。此标志隔离套接字以获得更确定的行为。
+
+新应用程åºé¼“励传递 SOF_TIMESTAMPING_OPT_ID 以区分时间戳并传递
+SOF_TIMESTAMPING_OPT_TSONLY 以æ“作,而ä¸ç®¡ sysctl net.core.tstamp_allow_data
+的设置。
+
+例外情况是当进程需è¦é¢å¤–çš„ cmsg æ•°æ®æ—¶ï¼Œä¾‹å¦‚ SOL_IP/IP_PKTINFO 以检测出
+å£ç½‘络接å£ã€‚ç„¶åŽä¼ é€’选项 SOF_TIMESTAMPING_OPT_CMSG。此选项ä¾èµ–于访问原
+å§‹æ•°æ®åŒ…的内容,因此ä¸èƒ½ä¸Ž SOF_TIMESTAMPING_OPT_TSONLY 组åˆã€‚
+
+
+1.3.4. 通过控制消æ¯å¯ç”¨æ—¶é—´æˆ³
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+除了套接字选项外,时间戳生æˆè¿˜å¯ä»¥é€šè¿‡ cmsg 按写入请求,仅适用于
+SOF_TIMESTAMPING_TX_*(è§ç¬¬ 1.3.1 节)。使用此功能,应用程åºå¯ä»¥æ— éœ€å¯ç”¨å’Œ
+ç¦ç”¨æ—¶é—´æˆ³å³å¯é‡‡æ ·æ¯ä¸ª sendmsg() 的时间戳::
+
+ struct msghdr *msg;
+ ...
+ cmsg = CMSG_FIRSTHDR(msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SO_TIMESTAMPING;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
+ *((__u32 *) CMSG_DATA(cmsg)) = SOF_TIMESTAMPING_TX_SCHED |
+ SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_TX_ACK;
+ err = sendmsg(fd, msg, 0);
+
+通过 cmsg 设置的 SOF_TIMESTAMPING_TX_* 标志将覆盖通过 setsockopt 设置的
+SOF_TIMESTAMPING_TX_* 标志。
+
+此外,应用程åºä»ç„¶éœ€è¦é€šè¿‡ setsockopt å¯ç”¨æ—¶é—´æˆ³æŠ¥å‘Šä»¥æŽ¥æ”¶æ—¶é—´æˆ³::
+
+ __u32 val = SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_OPT_ID /* 或任何其他标志 */;
+ err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, &val, sizeof(val));
+
+
+1.4 å­—èŠ‚æµæ—¶é—´æˆ³
+----------------
+
+SO_TIMESTAMPING æŽ¥å£æ”¯æŒå­—节æµçš„æ—¶é—´æˆ³ã€‚æ¯ä¸ªè¯·æ±‚解释为请求当整个缓冲区内容
+通过时间戳点时。也就是说,对于æµé€‰é¡¹ SOF_TIMESTAMPING_TX_SOFTWARE 将记录
+å½“æ‰€æœ‰å­—èŠ‚éƒ½åˆ°è¾¾è®¾å¤‡é©±åŠ¨ç¨‹åºæ—¶ï¼Œæ— è®ºæ•°æ®è¢«è½¬æ¢æˆå¤šå°‘个数æ®åŒ…。
+
+一般æ¥è¯´ï¼Œå­—èŠ‚æµæ²¡æœ‰è‡ªç„¶åˆ†éš”符,因此将时间戳与数æ®ç›¸å…³è”是éžå¹³å‡¡çš„。字节范围
+å¯èƒ½è·¨æ®µï¼Œä»»ä½•段å¯èƒ½åˆå¹¶ï¼ˆå¯èƒ½åˆå¹¶å…ˆå‰åˆ†æ®µç¼“冲区关è”的独立 send() 调用)。段
+å¯ä»¥é‡æ–°æŽ’åºï¼ŒåŒä¸€å­—节范围å¯ä»¥åœ¨å¤šä¸ªæ®µä¸­å¹¶å­˜ï¼Œå¯¹äºŽå®žçްé‡ä¼ çš„å议。
+
+所有时间戳必须实现相åŒçš„语义,å¦åˆ™å®ƒä»¬æ˜¯ä¸å¯æ¯”较的。以ä¸åŒäºŽç®€å•情况(缓冲区
+到 skb çš„ 1:1 映射)的方å¼å¤„ç†â€œç½•è§â€è§’è½æƒ…况是ä¸å¤Ÿçš„,因为性能调试通常需è¦
+关注这些异常。
+
+在实践中,时间戳å¯ä»¥ä¸Žå­—èŠ‚æµæ®µä¸€è‡´åœ°å…³è”ï¼Œå¦‚æžœæ—¶é—´æˆ³è¯­ä¹‰å’Œæµ‹é‡æ—¶åºçš„选择正确。
+此挑战与决定 IP 分片策略没有ä¸åŒã€‚在那里,定义是仅对第一个分片进行时间戳。对
+于字节æµï¼Œæˆ‘们选择仅在所有字节通过æŸä¸ªç‚¹æ—¶ç”Ÿæˆæ—¶é—´æˆ³ã€‚SOF_TIMESTAMPING_TX_ACK
+å®šä¹‰çš„å®žçŽ°å’ŒæŽ¨ç†æ˜¯å®¹æ˜“的。一个需è¦è€ƒè™‘ SACK çš„å®žçŽ°ä¼šæ›´å¤æ‚,因为å¯èƒ½å­˜åœ¨ä¼ è¾“
+空洞和乱åºåˆ°è¾¾ã€‚
+
+在主机上,TCP 也å¯ä»¥é€šè¿‡ Nagleã€corkã€autocorkã€åˆ†æ®µå’Œ GSO 打破简å•çš„ 1:1
+缓冲区到 skbuff 映射。实现确ä¿åœ¨æ‰€æœ‰æƒ…况下都正确,通过跟踪æ¯ä¸ª send() 传递
+ç»™send() 的最åŽä¸€ä¸ªå­—节,å³ä½¿å®ƒåœ¨ skbuff 扩展或åˆå¹¶æ“作åŽä¸å†æ˜¯æœ€åŽä¸€ä¸ªå­—
+节。它存储相关的åºåˆ—å·åœ¨ skb_shinfo(skb)->tskey。因为一个 skbuff åªæœ‰ä¸€
+个这样的字段,所以åªèƒ½ç”Ÿæˆä¸€ä¸ªæ—¶é—´æˆ³ã€‚
+
+åœ¨ç½•è§æƒ…况下,如果两个请求折å åˆ°åŒä¸€ä¸ª skb,则时间戳请求å¯èƒ½ä¼šè¢«é”™è¿‡ã€‚进程å¯
+以通过始终在请求之间刷新 TCP æ ˆæ¥æ£€æµ‹æ­¤æƒ…况,例如å¯ç”¨ TCP_NODELAY å’Œç¦ç”¨
+TCP_CORKå’Œ autocork。在 linux-4.7 之åŽï¼Œæ›´å¥½çš„预防åˆå¹¶æ–¹æ³•是使用 MSG_EOR
+标志在sendmsg()时。
+
+è¿™äº›é¢„é˜²æŽªæ–½ç¡®ä¿æ—¶é—´æˆ³ä»…在所有字节通过时间戳点时生æˆï¼Œå‡è®¾ç½‘络栈本身ä¸ä¼šé‡æ–°
+æŽ’åºæ®µã€‚栈确实试图é¿å…釿–°æŽ’åºã€‚唯一的例外是管ç†å‘˜æŽ§åˆ¶ï¼šå¯ä»¥æž„造一个数æ®åŒ…è°ƒ
+度器é…置,将æ¥è‡ªåŒä¸€æµçš„ä¸åŒæ®µå»¶è¿Ÿä¸åŒã€‚è¿™ç§è®¾ç½®é€šå¸¸ä¸å¸¸è§ã€‚
+
+
+2 æ•°æ®æŽ¥å£
+==========
+
+时间戳通过 recvmsg() 的辅助数æ®åŠŸèƒ½è¯»å–。请å‚阅 `man 3 cmsg` 了解此接å£çš„
+详细信æ¯ã€‚å¥—æŽ¥å­—æ‰‹å†Œé¡µé¢ (`man 7 socket`) æè¿°äº†å¦‚何检索SO_TIMESTAMP å’Œ
+SO_TIMESTAMPNS 生æˆçš„æ•°æ®åŒ…时间戳。
+
+
+2.1 SCM_TIMESTAMPING 记录
+-------------------------
+
+这些时间戳在 cmsg_level SOL_SOCKETã€cmsg_type SCM_TIMESTAMPING 和类型为
+
+对于 SO_TIMESTAMPING_OLD::
+
+ struct scm_timestamping {
+ struct timespec ts[3];
+ };
+
+对于 SO_TIMESTAMPING_NEW::
+
+ struct scm_timestamping64 {
+ struct __kernel_timespec ts[3];
+
+始终使用 SO_TIMESTAMPING_NEW 时间戳以始终获得 struct scm_timestamping64
+æ ¼å¼çš„æ—¶é—´æˆ³ã€‚
+
+SO_TIMESTAMPING_OLD 在 32 使œºå™¨ä¸Š 2038 å¹´åŽè¿”回错误的时间戳。
+
+该结构å¯ä»¥è¿”回最多三个时间戳。这是一个é—留功能。任何时候至少有一个字
+段ä¸ä¸ºé›¶ã€‚大多数时间戳都通过 ts[0] 传递。硬件时间戳通过 ts[2] 传递。
+
+ts[1] 以å‰ç”¨äºŽå­˜å‚¨ç¡¬ä»¶æ—¶é—´æˆ³è½¬æ¢ä¸ºç³»ç»Ÿæ—¶é—´ã€‚相å,将硬件时钟设备直接
+暴露为HW PTPæ—¶é’Ÿæºï¼Œä»¥å…许用户空间进行时间转æ¢ï¼Œå¹¶å¯é€‰åœ°ä¸Žç”¨æˆ·ç©ºé—´
+PTP 堆栈(如linuxptpï¼‰åŒæ­¥ç³»ç»Ÿæ—¶é—´ã€‚对于 PTP æ—¶é’Ÿ API,请å‚阅
+Documentation/driver-api/ptp.rst。
+
+注æ„ï¼Œå¦‚æžœåŒæ—¶å¯ç”¨äº† SO_TIMESTAMP 或 SO_TIMESTAMPNS 与
+SO_TIMESTAMPING 使用 SOF_TIMESTAMPING_SOFTWARE,在 recvmsg()
+调用时会生æˆä¸€ä¸ªè™šå‡çš„软件时间戳,并传递给 ts[0] 当真实软件时间戳缺
+失时。这也å‘生在硬件传输时间戳上。
+
+2.1.1 传输时间戳与 MSG_ERRQUEUE
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+对于传输时间戳,传出数æ®åŒ…回环到套接字的错误队列,并附加å‘逿—¶é—´æˆ³ï¼ˆs)。
+进程通过调用带有 MSG_ERRQUEUE 标志的 recvmsg() 接收时间戳,并传递
+一个足够大的 msg_control缓冲区以接收相关的元数æ®ç»“构。recvmsg 调用
+返回原始传出数æ®åŒ…,并附加两个辅助消æ¯ã€‚
+
+一个 cm_level SOL_IP(V6) 和 cm_type IP(V6)_RECVERR 嵌入一个
+struct sock_extended_err这定义了错误类型。对于时间戳,ee_errno
+字段是 ENOMSG。å¦ä¸€ä¸ªè¾…助消æ¯å°†å…·æœ‰ cm_level SOL_SOCKET å’Œ cm_type
+SCM_TIMESTAMPING。这嵌入了 struct scm_timestamping。
+
+
+2.1.1.2 时间戳类型
+~~~~~~~~~~~~~~~~~~
+
+三个 struct timespec 的语义由 struct sock_extended_err 中的
+ee_info 字段定义。它包å«ä¸€ä¸ªç±»åž‹ SCM_TSTAMP_* æ¥å®šä¹‰å®žé™…传递给
+scm_timestamping 的时间戳。
+
+SCM_TSTAMP_* 类型与之å‰è®¨è®ºçš„ SOF_TIMESTAMPING_* 控制字段完全
+匹é…ï¼Œåªæœ‰ä¸€ä¸ªä¾‹å¤–对于é—留原因,SCM_TSTAMP_SND 等于零,å¯ä»¥è®¾ç½®ä¸º
+SOF_TIMESTAMPING_TX_HARDWARE 和 SOF_TIMESTAMPING_TX_SOFTWARE。
+它是第一个,如果 ts[2] ä¸ä¸ºé›¶ï¼Œå¦åˆ™æ˜¯ç¬¬äºŒä¸ªï¼Œåœ¨è¿™ç§æƒ…况下,时间戳存
+储在ts[0] 中。
+
+
+2.1.1.3 分片
+~~~~~~~~~~~~
+
+ä¼ å‡ºæ•°æ®æŠ¥åˆ†ç‰‡å¾ˆå°‘è§ï¼Œä½†å¯èƒ½å‘生,例如通过显å¼ç¦ç”¨ PMTU å‘现。如果
+传出数æ®åŒ…被分片,则仅对第一个分片进行时间戳,并返回给å‘é€å¥—接字。
+
+
+2.1.1.4 æ•°æ®åŒ…è´Ÿè½½
+~~~~~~~~~~~~~~~~~~
+
+调用应用程åºé€šå¸¸ä¸å…³å¿ƒæŽ¥æ”¶å®ƒä¼ é€’给堆栈的整个数æ®åŒ…负载:套接字错误队
+列机制仅是一ç§å°†æ—¶é—´æˆ³é™„åŠ åˆ°å…¶ä¸Šçš„æ–¹æ³•ã€‚åœ¨è¿™ç§æƒ…况下,应用程åºå¯ä»¥é€‰
+择读å–较å°çš„æ•°æ®æŠ¥ï¼Œç”šè‡³é•¿åº¦ä¸º 0。负载相应地被截断。直到进程调用
+recvmsg() 到错误队列,然而,整个数æ®åŒ…ä»åœ¨é˜Ÿåˆ—中,å ç”¨ SO_RCVBUF 预算。
+
+
+2.1.1.5 阻塞读å–
+~~~~~~~~~~~~~~~~
+
+从错误队列读å–始终是éžé˜»å¡žæ“作。è¦é˜»å¡žç­‰å¾…时间戳,请使用 poll 或
+select。poll() 将在 pollfd.revents 中返回 POLLERR,如果错误队列
+中有数æ®ã€‚没有必è¦åœ¨ pollfd.events中传递此标志。此标志在请求时被忽
+略。å¦è¯·å‚阅 `man 2 poll`。
+
+
+2.1.2 接收时间戳
+^^^^^^^^^^^^^^^^
+
+在接收时,没有ç†ç”±ä»Žå¥—接字错误队列读å–。SCM_TIMESTAMPING 辅助数æ®ä¸Ž
+æ•°æ®åŒ…æ•°æ®ä¸€èµ·é€šè¿‡æ­£å¸¸ recvmsg() å‘é€ã€‚ç”±äºŽè¿™ä¸æ˜¯å¥—接字错误,它ä¸ä¼´
+éšæ¶ˆæ¯ SOL_IP(V6)/IP(V6)_RECVERRORã€‚åœ¨è¿™ç§æƒ…况下,struct
+scm_timestamping 中的三个字段å«ä¹‰éšå¼å®šä¹‰ã€‚ts[0] 在设置时包å«è½¯ä»¶
+时间戳,ts[1] 冿¬¡è¢«å¼ƒç”¨ï¼Œts[2] 在设置时包å«ç¡¬ä»¶æ—¶é—´æˆ³ã€‚
+
+
+3. 硬件时间戳é…置:ETHTOOL_MSG_TSCONFIG_SET/GET
+===============================================
+
+硬件时间戳也必须为æ¯ä¸ªè®¾å¤‡é©±åŠ¨ç¨‹åºåˆå§‹åŒ–,该驱动程åºé¢„期执行硬件时间戳。
+傿•°åœ¨ include/uapi/linux/net_tstamp.h 中定义为::
+
+ struct hwtstamp_config {
+ int flags; /* ç›®å‰æ²¡æœ‰å®šä¹‰çš„æ ‡å¿—,必须为零 */
+ int tx_type; /* HWTSTAMP_TX_* */
+ int rx_filter; /* HWTSTAMP_FILTER_* */
+ };
+
+期望的行为通过 tsconfig netlink 套接字 ``ETHTOOL_MSG_TSCONFIG_SET``
+传递到内核,并通过 ``ETHTOOL_A_TSCONFIG_TX_TYPES``ã€
+``ETHTOOL_A_TSCONFIG_RX_FILTERS`` 和 ``ETHTOOL_A_TSCONFIG_HWTSTAMP_FLAGS``
+netlink 属性设置 struct hwtstamp_config 相应地。
+
+``ETHTOOL_A_TSCONFIG_HWTSTAMP_PROVIDER`` netlink 嵌套属性用于选择
+ç¡¬ä»¶æ—¶é—´æˆ³çš„æ¥æºã€‚它由设备æºçš„索引和时间戳类型é™å®šç¬¦ç»„æˆã€‚
+
+驱动程åºå¯ä»¥è‡ªç”±ä½¿ç”¨æ¯”请求更宽æ¾çš„é…置。预期驱动程åºåº”仅实现å¯ä»¥ç›´æŽ¥æ”¯æŒçš„
+最通用模å¼ã€‚例如,如果硬件å¯ä»¥æ”¯æŒ HWTSTAMP_FILTER_PTP_V2_EVENT,则它
+通常应始终å‡çº§HWTSTAMP_FILTER_PTP_V2_L2_SYNCï¼Œä¾æ­¤ç±»æŽ¨ï¼Œå› ä¸º
+HWTSTAMP_FILTER_PTP_V2_EVENT 更通用(更实用)。
+
+支æŒç¡¬ä»¶æ—¶é—´æˆ³çš„驱动程åºåº”æ›´æ–° struct,并å¯èƒ½è¿”回更宽æ¾çš„实际é…置。如果
+请求的数æ®åŒ…无法进行时间戳,则ä¸åº”更改任何内容,并返回 ERANGE(与 EINVAL
+相å,这表明 SIOCSHWTSTAMP æ ¹æœ¬ä¸æ”¯æŒï¼‰ã€‚
+
+åªæœ‰å…·æœ‰ç®¡ç†æƒé™çš„进程æ‰èƒ½æ›´æ”¹é…置。用户空间负责确ä¿å¤šä¸ªè¿›ç¨‹ä¸ä¼šç›¸äº’干扰,
+å¹¶ç¡®ä¿è®¾ç½®è¢«é‡ç½®ã€‚
+
+任何进程都å¯ä»¥é€šè¿‡è¯·æ±‚ tsconfig netlink 套接字 ``ETHTOOL_MSG_TSCONFIG_GET``
+读å–实际é…置。
+
+é—ç•™é…置是使用 ioctl(SIOCSHWTSTAMP) ä¸ŽæŒ‡å‘ struct ifreq 的指针,其
+ifr_dataæŒ‡å‘ struct hwtstamp_config。tx_type å’Œ rx_filter 是驱动
+ç¨‹åºæœŸæœ›æ‰§è¡Œçš„æç¤ºã€‚如果请求的细粒度过滤对传入数æ®åŒ…䏿”¯æŒï¼Œé©±åŠ¨ç¨‹åºå¯èƒ½
+会对请求的数æ®åŒ…进行时间戳。ioctl(SIOCGHWTSTAMP) 以与
+ioctl(SIOCSHWTSTAMP) 相åŒçš„æ–¹å¼ä½¿ç”¨ã€‚然而,å¹¶éžæ‰€æœ‰é©±åŠ¨ç¨‹åºéƒ½å®žçŽ°äº†è¿™ä¸€ç‚¹ã€‚
+
+::
+
+ /* å¯èƒ½çš„ hwtstamp_config->tx_type 值 */
+ enum {
+ /*
+ * ä¸ä¼šéœ€è¦ç¡¬ä»¶æ—¶é—´æˆ³çš„传出数æ®åŒ…ï¼›
+ * 如果数æ®åŒ…到达并请求它,则ä¸ä¼šè¿›è¡Œç¡¬ä»¶æ—¶é—´æˆ³
+ */
+ HWTSTAMP_TX_OFF,
+
+ /*
+ * å¯ç”¨ä¼ å‡ºæ•°æ®åŒ…的硬件时间戳;
+ * æ•°æ®åŒ…çš„å‘é€è€…决定哪些数æ®åŒ…éœ€è¦æ—¶é—´æˆ³ï¼Œ
+ * 在å‘逿•°æ®åŒ…之å‰è®¾ç½® SOF_TIMESTAMPING_TX_SOFTWARE
+ */
+ HWTSTAMP_TX_ON,
+ };
+
+ /* å¯èƒ½çš„ hwtstamp_config->rx_filter 值 */
+ enum {
+ /* 时间戳ä¸ä¼ å…¥ä»»ä½•æ•°æ®åŒ… */
+ HWTSTAMP_FILTER_NONE,
+
+ /* 时间戳任何传入数æ®åŒ… */
+ HWTSTAMP_FILTER_ALL,
+
+ /* 返回值:时间戳所有请求的数æ®åŒ…加上一些其他数æ®åŒ… */
+ HWTSTAMP_FILTER_SOME,
+
+ /* PTP v1,UDP,任何事件数æ®åŒ… */
+ HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
+
+ /* 有关完整值列表,请检查
+ * 文件 include/uapi/linux/net_tstamp.h
+ */
+ };
+
+3.1 硬件时间戳实现:设备驱动程åº
+--------------------------------
+
+支æŒç¡¬ä»¶æ—¶é—´æˆ³çš„驱动程åºå¿…é¡»æ”¯æŒ ndo_hwtstamp_set NDO 或é—ç•™ SIOCSHWTSTAMP
+ioctl å¹¶æ›´æ–°æä¾›çš„ struct hwtstamp_config 与实际值,如 SIOCSHWTSTAMP 部分
+æ‰€è¿°ã€‚å®ƒè¿˜åº”æ”¯æŒ ndo_hwtstamp_get 或é—ç•™ SIOCGHWTSTAMP。
+
+接收数æ®åŒ…的时间戳必须存储在 skb 中。è¦èŽ·å– skb 的共享时间戳结构,请调用
+skb_hwtstamps()。然åŽè®¾ç½®ç»“构中的时间戳::
+
+ struct skb_shared_hwtstamps {
+ /* 硬件时间戳转æ¢ä¸ºè‡ªä»»æ„时间点的æŒç»­æ—¶é—´
+ * 自定义点
+ */
+ ktime_t hwtstamp;
+ };
+
+传出数æ®åŒ…的时间戳应按如下方å¼ç”Ÿæˆï¼š
+
+- 在 hard_start_xmit() 中,检查 (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)
+ 是å¦ä¸ä¸ºé›¶ã€‚å¦‚æžœæ˜¯ï¼Œåˆ™é©±åŠ¨ç¨‹åºæœŸæœ›æ‰§è¡Œç¡¬ä»¶æ—¶é—´æˆ³ã€‚
+- 如果此 skb 和请求都å¯èƒ½ï¼Œåˆ™å£°æ˜Žé©±åŠ¨ç¨‹åºæ­£åœ¨æ‰§è¡Œæ—¶é—´æˆ³ï¼Œé€šè¿‡è®¾ç½® skb_shinfo(skb)->tx_flags
+ 中的标志SKBTX_IN_PROGRESS,例如::
+
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+ 您å¯èƒ½å¸Œæœ›ä¿ç•™ä¸Ž skb å…³è”çš„æŒ‡é’ˆï¼Œè€Œä¸æ˜¯é‡Šæ”¾ skbã€‚ä¸æ”¯æŒç¡¬ä»¶æ—¶é—´æˆ³çš„驱
+ 动程åºä¸ä¼šè¿™æ ·åšã€‚驱动程åºç»ä¸èƒ½è§¦åŠ sk_buff::tstampï¼å®ƒç”¨äºŽå­˜å‚¨ç½‘络
+ å­ç³»ç»Ÿç”Ÿæˆçš„软件时间戳。
+- 驱动程åºåº”在尽å¯èƒ½æŽ¥è¿‘å°† sk_buff 传递给硬件时调用 skb_tx_timestamp()。
+ skb_tx_timestamp()æä¾›è½¯ä»¶æ—¶é—´æˆ³ï¼ˆå¦‚果请求),并且硬件时间戳ä¸å¯ç”¨
+ (SKBTX_IN_PROGRESS 未设置)。
+- 一旦驱动程åºå‘逿•°æ®åŒ…å¹¶/或获å–硬件时间戳,它就会通过 skb_tstamp_tx()
+ 传递时间戳,原始 skb,原始硬件时间戳。skb_tstamp_tx() 克隆原始 skb 并
+ 添加时间戳,因此原始 skb 现在必须释放。如果获å–硬件时间戳失败,则驱动程åº
+ ä¸åº”回退到软件时间戳。ç†ç”±æ˜¯ï¼Œè¿™ä¼šåœ¨å¤„ç†ç®¡é“中的ç¨åŽæ—¶é—´å‘ç”Ÿï¼Œè€Œä¸æ˜¯å…¶ä»–软
+ 件时间戳,因此å¯èƒ½å¯¼è‡´æ—¶é—´æˆ³ä¹‹é—´çš„差异。
+
+3.2 å †å  PTP 硬件时钟的特殊考虑
+-------------------------------
+
+在数æ®åŒ…的路径中å¯èƒ½å­˜åœ¨å¤šä¸ª PHC(PTP 硬件时钟)。内核没有明确的机制å…许用
+户选择用于时间戳以太网帧的 PHC。相å,å‡è®¾æœ€å¤–层的 PHC 始终是最优的,并且
+内核驱动程åºåä½œä»¥å®žçŽ°è¿™ä¸€ç›®æ ‡ã€‚ç›®å‰æœ‰ 3 ç§å †å  PHC 的情况,如下所示:
+
+3.2.1 DSA(分布å¼äº¤æ¢æž¶æž„ï¼‰äº¤æ¢æœº
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+这些是具有一个端å£è¿žæŽ¥åˆ°ï¼ˆå®Œå…¨ä¸çŸ¥æƒ…的)主机以太网接å£çš„ä»¥å¤ªç½‘äº¤æ¢æœºï¼Œå¹¶ä¸”
+执行端å£å¤šè·¯å¤ç”¨æˆ–å¯é€‰è½¬å‘加速功能。æ¯ä¸ª DSA äº¤æ¢æœºç«¯å£åœ¨ç”¨æˆ·çœ‹æ¥éƒ½æ˜¯ç‹¬ç«‹çš„
+(虚拟)网络接å£ï¼Œå…¶ç½‘络 I/O 在底层通过主机接å£ï¼ˆåœ¨ TX 上é‡å®šå‘到主机端å£ï¼Œ
+在 RX 上拦截帧)执行。
+
+当 DSA äº¤æ¢æœºè¿žæŽ¥åˆ°ä¸»æœºç«¯å£æ—¶ï¼ŒPTP åŒæ­¥å¿…é¡»å—到é™åˆ¶ï¼Œå› ä¸ºäº¤æ¢æœºçš„å¯å˜æŽ’队
+延迟引入了主机端å£ä¸Žå…¶ PTP 伙伴之间的路径延迟抖动。因此,一些 DSA äº¤æ¢æœº
+包å«è‡ªå·±çš„æ—¶é—´æˆ³æ—¶é’Ÿï¼Œå¹¶å…·æœ‰åœ¨è‡ªèº« MAC上执行网络时间戳的能力,因此路径延迟
+仅测é‡çº¿ç¼†å’Œ PHY ä¼ æ’­å»¶è¿Ÿã€‚æ”¯æŒ Linux çš„ DSA äº¤æ¢æœºæš´éœ²äº†ä¸Žä»»ä½•其他网络
+接å£ç›¸åŒçš„ ABI(除了 DSA 接å£åœ¨ç½‘络 I/O æ–¹é¢å®žé™…上是虚拟的,它们确实有自
+己的PHCï¼‰ã€‚å…¸åž‹åœ°ï¼Œä½†ä¸æ˜¯å¼ºåˆ¶æ€§åœ°ï¼Œæ‰€æœ‰DSA äº¤æ¢æœºæŽ¥å£å…±äº«ç›¸åŒçš„ PHC。
+
+通过设计,DSA äº¤æ¢æœºå¯¹è¿žæŽ¥åˆ°å…¶ä¸»æœºç«¯å£çš„ PTP 时间戳ä¸éœ€è¦ä»»ä½•特殊的驱动程
+åºå¤„ç†ã€‚然而,当主机端å£ä¹Ÿæ”¯æŒ PTP 时间戳时,DSA 将负责拦截
+``.ndo_eth_ioctl`` 调用,并阻止å°è¯•在主机端å£ä¸Šå¯ç”¨ç¡¬ä»¶æ—¶é—´æˆ³ã€‚这是因为
+SO_TIMESTAMPING API ä¸å…许为åŒä¸€æ•°æ®åŒ…传递多个硬件时间戳,因此除了 DSA
+äº¤æ¢æœºç«¯å£ä¹‹å¤–的任何人都ä¸åº”阻止这样åšã€‚
+
+在通用层,DSA æä¾›äº†ä»¥ä¸‹åŸºç¡€è®¾æ–½ç”¨äºŽ PTP 时间戳:
+
+- ``.port_txtstamp()``:在用户空间从用户空间请求带有硬件 TX 时间戳请求
+ 的数æ®åŒ…之å‰è°ƒç”¨çš„é’©å­ã€‚这是必需的,因为硬件时间戳在实际 MAC ä¼ è¾“åŽæ‰å¯
+ 用,因此驱动程åºå¿…须准备将时间戳与原始数æ®åŒ…相关è”,以便它å¯ä»¥é‡æ–°å…¥é˜Ÿæ•°
+ æ®åŒ…到套接字的错误队列。为了ä¿å­˜å¯èƒ½åœ¨æ—¶é—´æˆ³å¯ç”¨æ—¶éœ€è¦çš„æ•°æ®åŒ…,驱动程åº
+ å¯ä»¥è°ƒç”¨ ``skb_clone_sk``,在 skb->cb 中ä¿å­˜å…‹éš†æŒ‡é’ˆï¼Œå¹¶å…¥é˜Ÿä¸€ä¸ª tx
+ skb é˜Ÿåˆ—ã€‚é€šå¸¸ï¼Œäº¤æ¢æœºä¼šæœ‰ä¸€ä¸ªPTP TX 时间戳寄存器(或有时是一个 FIFO),
+ 其中时间戳å¯ç”¨ã€‚在 FIFO 的情况下,硬件å¯èƒ½ä¼šå­˜å‚¨PTP åºåˆ— ID/消æ¯ç±»åž‹/
+ 域å·å’Œå®žé™…时间戳的键值对。为了在等待时间戳的数æ®åŒ…队列和实际时间戳之间正
+ 确关è”,驱动程åºå¯ä»¥ä½¿ç”¨ BPF 分类器(``ptp_classify_raw``) æ¥è¯†åˆ« PTP
+ 传输类型,并使用 ``ptp_parse_header`` 解释 PTP 头字段。å¯èƒ½å­˜åœ¨ä¸€ä¸ª IRQ,
+ 当此时间戳å¯ç”¨æ—¶è§¦å‘,或者驱动程åºå¯èƒ½éœ€è¦è½®è¯¢ï¼Œåœ¨è°ƒç”¨ ``dev_queue_xmit()``
+ 到主机接å£ä¹‹åŽã€‚啿­¥ TX 时间戳ä¸éœ€è¦æ•°æ®åŒ…克隆,因为 PTP åè®®ä¸éœ€è¦åŽç»­æ¶ˆ
+ æ¯ï¼ˆå› ä¸ºTX 时间戳已嵌入到数æ®åŒ…ä¸­ï¼‰ï¼Œå› æ­¤ç”¨æˆ·ç©ºé—´ä¸æœŸæœ›æ•°æ®åŒ…带有 TX 时间戳
+ è¢«é‡æ–°å…¥é˜Ÿåˆ°å…¶å¥—接字的错误队列。
+
+- ``.port_rxtstamp()``:在 RX 上,DSA è¿è¡Œ BPF 分类器以识别 PTP 事件消æ¯
+ (任何其他数æ®åŒ…,包括 PTP 通用消æ¯ï¼Œä¸è¿›è¡Œæ—¶é—´æˆ³ï¼‰ã€‚é©±åŠ¨ç¨‹åºæä¾›åŽŸå§‹ï¼ˆä¹Ÿæ˜¯å”¯ä¸€ï¼‰
+ 时间戳数æ®åŒ…,以便它å¯ä»¥æ ‡è®°å®ƒï¼Œå¦‚果它是立å³å¯ç”¨çš„,或者延迟。在接收时,时间
+ 戳å¯èƒ½è¦ä¹ˆåœ¨é¢‘带内(通过DSA 头中的元数æ®ï¼Œæˆ–以其他方å¼é™„加到数æ®åŒ…),è¦ä¹ˆåœ¨é¢‘
+ 带外(通过å¦ä¸€ä¸ª RX 时间戳FIFO)。在 RX 上延迟通常是必è¦çš„,当检索时间戳需è¦
+ å¯ç¡çœ ä¸Šä¸‹æ–‡æ—¶ã€‚åœ¨è¿™ç§æƒ…况下,DSAé©±åŠ¨ç¨‹åºæœ‰è´£ä»»è°ƒç”¨ ``netif_rx()`` 在新鲜时
+ 间戳的 skb 上。
+
+3.2.2 以太网 PHYs
+^^^^^^^^^^^^^^^^^
+
+这些是通常在网络栈中履行第 1 层角色的设备,因此它们在 DSA äº¤æ¢æœºä¸­æ²¡æœ‰ç½‘络接
+å£çš„表示。然而,PHYå¯èƒ½èƒ½å¤Ÿæ£€æµ‹å’Œæ—¶é—´æˆ³ PTP æ•°æ®åŒ…,出于性能原因:在尽å¯èƒ½æŽ¥
+近导线的地方获å–çš„æ—¶é—´æˆ³å…·æœ‰æ›´ç¨³å®šçš„åŒæ­¥æ€§å’Œæ›´ç²¾ç¡®çš„精度。
+
+æ”¯æŒ PTP 时间戳的 PHY 驱动程åºå¿…须创建 ``struct mii_timestamper`` 并添加
+指å‘它的指针在 ``phydev->mii_ts`` 中。 ``phydev->mii_ts`` 的存在将由网络
+堆栈检查。
+
+由于 PHY 没有网络接å£è¡¨ç¤ºï¼ŒPHY 的时间戳和 ethtool ioctl æ“作需è¦é€šè¿‡å…¶å„自
+çš„ MAC驱动程åºè¿›è¡Œä¸­ä»‹ã€‚因此,与 DSA äº¤æ¢æœºä¸åŒï¼Œéœ€è¦å¯¹æ¯ä¸ªå•独的 MAC 驱动
+程åºè¿›è¡Œ PHY时间戳支æŒçš„修改。这包括:
+
+- 在 ``.ndo_eth_ioctl`` ä¸­æ£€æŸ¥ï¼Œæ˜¯å¦ ``phy_has_hwtstamp(netdev->phydev)``
+ 为真或å‡ã€‚如果是,则 MAC 驱动程åºä¸åº”å¤„ç†æ­¤è¯·æ±‚,而应将其传递给 PHY 使用
+ ``phy_mii_ioctl()``。
+
+- 在 RX 上,特殊干预å¯èƒ½æˆ–å¯èƒ½ä¸éœ€è¦ï¼Œå…·ä½“å–决于将 skb 传递到网络堆栈的函数。
+ 在 plain ``netif_rx()`` 和类似情况下,MAC 驱动程åºå¿…须检查是å¦
+ ``skb_defer_rx_timestamp(skb)`` 是必è¦çš„,如果是,则ä¸è°ƒç”¨ ``netif_rx()``。
+ 如果 ``CONFIG_NETWORK_PHY_TIMESTAMPING`` å¯ç”¨ï¼Œå¹¶ä¸”
+ ``skb->dev->phydev->mii_ts`` 存在,它的 ``.rxtstamp()`` é’©å­çŽ°åœ¨å°†è¢«è°ƒ
+ 用,以使用与 DSA 类似的逻辑确定 RX 时间戳延迟是å¦å¿…è¦ã€‚åŒæ ·åƒ DSA,它æˆä¸º
+ PHY 驱动程åºçš„责任,在时间戳å¯ç”¨æ—¶å‘逿•°æ®åŒ…到堆栈。
+
+ 对于其他 skb 接收函数,例如 ``napi_gro_receive`` 和 ``netif_receive_skb``,
+ å †æ ˆä¼šè‡ªåŠ¨æ£€æŸ¥æ˜¯å¦ ``skb_defer_rx_timestamp()`` 是必è¦çš„,因此此检查ä¸
+ 需è¦åœ¨é©±åŠ¨ç¨‹åºå†…部。
+
+- 在 TX ä¸Šï¼ŒåŒæ ·ï¼Œç‰¹æ®Šå¹²é¢„å¯èƒ½æˆ–å¯èƒ½ä¸éœ€è¦ã€‚调用 ``mii_ts->txtstamp()``é’©
+ å­çš„函数å为``skb_clone_tx_timestamp()``。此函数å¯ä»¥ç›´æŽ¥è°ƒç”¨ï¼ˆåœ¨è¿™ç§æƒ…
+ å†µä¸‹ï¼Œç¡®å®žéœ€è¦æ˜¾å¼ MAC é©±åŠ¨ç¨‹åºæ”¯æŒï¼‰ï¼Œä½†å‡½æ•°ä¹Ÿ piggybacks 从
+ ``skb_tx_timestamp()`` 调用,许多 MAC 驱动程åºå·²ç»ä¸ºè½¯ä»¶æ—¶é—´æˆ³ç›®çš„æ‰§è¡Œã€‚
+ 因此,如果 MAC 支æŒè½¯ä»¶æ—¶é—´æˆ³ï¼Œåˆ™å®ƒä¸éœ€è¦åœ¨æ­¤é˜¶æ®µæ‰§è¡Œä»»ä½•å…¶ä»–æ“作。
+
+3.2.3 MII 总线嗅探设备
+^^^^^^^^^^^^^^^^^^^^^^
+
+这些执行与时间戳以太网 PHY 相åŒçš„角色,除了它们是离散设备,因此å¯ä»¥ä¸Žä»»ä½• PHY
+组åˆï¼Œå³ä½¿å®ƒä¸æ”¯æŒæ—¶é—´æˆ³ã€‚在 Linux 中,它们是å¯å‘现的,å¯ä»¥é€šè¿‡ Device Tree
+附加到 ``struct phy_device``,对于其余部分,它们使用与那些相åŒçš„ mii_ts 基
+础设施。请å‚阅 Documentation/devicetree/bindings/ptp/timestamper.txt 了
+解更多详细信æ¯ã€‚
+
+3.2.4 MAC 驱动程åºçš„其他注æ„事项
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+å †å  PHC å¯èƒ½ä¼šæš´éœ² MAC 驱动程åºçš„é”™è¯¯ï¼Œè¿™äº›é”™è¯¯åœ¨æœªå †å  PHC 时无法触å‘。一个
+例孿¶‰åŠæ­¤è¡Œä»£ç ï¼Œå·²ç»åœ¨å‰é¢çš„部分中介ç»è¿‡::
+
+ skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
+
+任何 TX 时间戳逻辑,无论是普通的 MAC 驱动程åºã€DSA äº¤æ¢æœºé©±åŠ¨ç¨‹åºã€PHY 驱动程
+åºè¿˜æ˜¯ MII 总线嗅探设备驱动程åºï¼Œéƒ½åº”该设置此标志。但一个未æ„识到 PHC å †å çš„
+MAC 驱动程åºå¯èƒ½ä¼šè¢«å…¶ä»–䏿˜¯å®ƒè‡ªå·±çš„实体设置此标志,并传递一个é‡å¤çš„æ—¶é—´æˆ³ã€‚例
+如,典型的 TX 时间戳逻辑å¯èƒ½æ˜¯å°†ä¼ è¾“部分分为 2 个部分:
+
+1. "TX":检查是å¦é€šè¿‡ ``.ndo_eth_ioctl``("``priv->hwtstamp_tx_enabled
+ == true``"ï¼‰å’Œå½“å‰ skb 是å¦éœ€è¦ TX 时间戳("``skb_shinfo(skb)->tx_flags
+ & SKBTX_HW_TSTAMP``")。如果为真,则设置 "``skb_shinfo(skb)->tx_flags
+ |= SKBTX_IN_PROGRESS``" 标志。注æ„ï¼šå¦‚ä¸Šæ‰€è¿°ï¼Œåœ¨å †å  PHC 系统中,此æ¡ä»¶
+ ä¸åº”触å‘,因为此 MAC è‚¯å®šä¸æ˜¯æœ€å¤–层的 PHC。但这是典型的错误所在。传输继续
+ 使用此数æ®åŒ…。
+
+2. "TX 确认":传输完æˆã€‚é©±åŠ¨ç¨‹åºæ£€æŸ¥æ˜¯å¦éœ€è¦æ”¶é›†ä»»ä½• TX 时间戳。这里通常是典
+ 型的错误所在:驱动程åºé‡‡å–æ·å¾„ï¼Œåªæ£€æŸ¥ "``skb_shinfo(skb)->tx_flags &
+ SKBTX_IN_PROGRESS``" 是å¦è®¾ç½®ã€‚åœ¨å †å  PHC 系统中,这是错误的,因为此 MAC
+ 驱动程åºä¸æ˜¯å”¯ä¸€åœ¨ TX æ•°æ®è·¯å¾„中å¯ç”¨ SKBTX_IN_PROGRESS 的实体。
+
+此问题的正确解决方案是 MAC 驱动程åºåœ¨å…¶ "TX 确认" 部分中有一个å¤åˆæ£€æŸ¥ï¼Œä¸ä»…
+针对 "``skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS``",还针对
+"``priv->hwtstamp_tx_enabled == true``"ã€‚å› ä¸ºç³»ç»Ÿç¡®ä¿ PTP 时间戳仅对最
+外层 PHC å¯ç”¨ï¼Œæ­¤å¢žå¼ºæ£€æŸ¥å°†é¿å…å‘用户空间传递é‡å¤çš„ TX 时间戳。
diff --git a/Documentation/translations/zh_CN/rust/general-information.rst b/Documentation/translations/zh_CN/rust/general-information.rst
index 251f6ee2bb44..9b5e37e13f38 100644
--- a/Documentation/translations/zh_CN/rust/general-information.rst
+++ b/Documentation/translations/zh_CN/rust/general-information.rst
@@ -13,6 +13,7 @@
本文档包å«äº†åœ¨å†…核中使用Rustæ”¯æŒæ—¶éœ€è¦äº†è§£çš„æœ‰ç”¨ä¿¡æ¯ã€‚
+.. _rust_code_documentation_zh_cn:
ä»£ç æ–‡æ¡£
--------
diff --git a/Documentation/translations/zh_CN/rust/index.rst b/Documentation/translations/zh_CN/rust/index.rst
index b01f887e7167..5347d4729588 100644
--- a/Documentation/translations/zh_CN/rust/index.rst
+++ b/Documentation/translations/zh_CN/rust/index.rst
@@ -10,7 +10,35 @@
Rust
====
-与内核中的Rust有关的文档。若è¦å¼€å§‹åœ¨å†…核中使用Rust,请阅读quick-start.rst指å—。
+与内核中的Rust有关的文档。若è¦å¼€å§‹åœ¨å†…核中使用Rust,请阅读 quick-start.rst 指å—。
+
+Rust 实验
+---------
+Rust 支æŒåœ¨ v6.1 版本中åˆå¹¶åˆ°ä¸»çº¿ï¼Œä»¥å¸®åŠ©ç¡®å®š Rust 作为一ç§è¯­è¨€æ˜¯å¦é€‚åˆå†…核,
+峿˜¯å¦å€¼å¾—进行æƒè¡¡ã€‚
+
+ç›®å‰ï¼ŒRust 支æŒä¸»è¦é¢å‘对 Rust æ”¯æŒæ„Ÿå…´è¶£çš„内核开å‘人员和维护者,
+以便他们å¯ä»¥å¼€å§‹å¤„ç†æŠ½è±¡å’Œé©±åŠ¨ç¨‹åºï¼Œå¹¶å¸®åЩ开å‘基础设施和工具。
+
+如果您是终端用户,请注æ„ï¼Œç›®å‰æ²¡æœ‰é€‚åˆæˆ–æ—¨åœ¨ç”Ÿäº§ä½¿ç”¨çš„å†…ç½®é©±åŠ¨ç¨‹åºæˆ–模å—,
+并且 Rust 支æŒä»å¤„于开å‘/实验阶段,尤其是对于特定内核é…置。
+
+ä»£ç æ–‡æ¡£
+--------
+
+给定一个内核é…置,内核å¯èƒ½ä¼šç”Ÿæˆ Rust ä»£ç æ–‡æ¡£ï¼Œå³ç”± ``rustdoc`` 工具呈现的 HTML。
+
+.. only:: rustdoc and html
+
+ 该内核文档使用 `Rust ä»£ç æ–‡æ¡£ <rustdoc/kernel/index.html>`_ 构建。
+
+.. only:: not rustdoc and html
+
+ 该内核文档ä¸ä½¿ç”¨ Rust ä»£ç æ–‡æ¡£æž„建。
+
+预生æˆç‰ˆæœ¬æä¾›åœ¨ï¼šhttps://rust.docs.kernel.org。
+
+请å‚阅 :ref:`ä»£ç æ–‡æ¡£ <rust_code_documentation_zh_cn>` éƒ¨åˆ†ä»¥èŽ·å–æ›´å¤šè¯¦ç»†ä¿¡æ¯ã€‚
.. toctree::
:maxdepth: 1
@@ -19,6 +47,9 @@ Rust
general-information
coding-guidelines
arch-support
+ testing
+
+你还å¯ä»¥åœ¨ :doc:`../../../process/kernel-docs` 中找到 Rust çš„å­¦ä¹ ææ–™ã€‚
.. only:: subproject and html
diff --git a/Documentation/translations/zh_CN/rust/testing.rst b/Documentation/translations/zh_CN/rust/testing.rst
new file mode 100644
index 000000000000..ca81f1cef6eb
--- /dev/null
+++ b/Documentation/translations/zh_CN/rust/testing.rst
@@ -0,0 +1,215 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/rust/testing.rst
+
+:翻译:
+
+ éƒ­æ° Ben Guo <benx.guo@gmail.com>
+
+测试
+====
+
+本文介ç»äº†å¦‚何在内核中测试 Rust 代ç ã€‚
+
+æœ‰ä¸‰ç§æµ‹è¯•类型:
+
+- KUnit 测试
+- ``#[test]`` 测试
+- Kselftests
+
+KUnit 测试
+----------
+
+这些测试æ¥è‡ª Rust 文档中的示例。它们会被转æ¢ä¸º KUnit 测试。
+
+使用
+****
+
+这些测试å¯ä»¥é€šè¿‡ KUnit è¿è¡Œã€‚例如,在命令行中使用 ``kunit_tool`` ( ``kunit.py`` )::
+
+ ./tools/testing/kunit/kunit.py run --make_options LLVM=1 --arch x86_64 --kconfig_add CONFIG_RUST=y
+
+或者,KUnit 也å¯ä»¥åœ¨å†…æ ¸å¯åŠ¨æ—¶ä»¥å†…ç½®æ–¹å¼è¿è¡Œã€‚èŽ·å–æ›´å¤š KUnit ä¿¡æ¯ï¼Œè¯·å‚阅
+Documentation/dev-tools/kunit/index.rst。
+关于内核内置与命令行测试的详细信æ¯ï¼Œè¯·å‚阅 Documentation/dev-tools/kunit/architecture.rst。
+
+è¦ä½¿ç”¨è¿™äº› KUnit 文档测试,需è¦åœ¨å†…æ ¸é…置中å¯ç”¨ä»¥ä¸‹é€‰é¡¹::
+
+ CONFIG_KUNIT
+ Kernel hacking -> Kernel Testing and Coverage -> KUnit - Enable support for unit tests
+ CONFIG_RUST_KERNEL_DOCTESTS
+ Kernel hacking -> Rust hacking -> Doctests for the `kernel` crate
+
+KUnit æµ‹è¯•å³æ–‡æ¡£æµ‹è¯•
+********************
+
+文档测试( *doctests* )一般用于展示函数ã€ç»“构体或模å—等的使用方法。
+
+它们éžå¸¸æ–¹ä¾¿ï¼Œå› ä¸ºå®ƒä»¬å°±å†™åœ¨æ–‡æ¡£æ—边。例如:
+
+.. code-block:: rust
+
+ /// 求和两个数字。
+ ///
+ /// ```
+ /// assert_eq!(mymod::f(10, 20), 30);
+ /// ```
+ pub fn f(a: i32, b: i32) -> i32 {
+ a + b
+ }
+
+在用户空间中,这些测试由 ``rustdoc`` 负责收集并è¿è¡Œã€‚å•独使用这个工具已ç»å¾ˆæœ‰ä»·å€¼ï¼Œ
+因为它å¯ä»¥éªŒè¯ç¤ºä¾‹èƒ½å¦æˆåŠŸç¼–è¯‘ï¼ˆç¡®ä¿å’Œä»£ç ä¿æŒåŒæ­¥ï¼‰ï¼Œ
+åŒæ—¶è¿˜å¯ä»¥è¿è¡Œé‚£äº›ä¸ä¾èµ–内核 API 的示例。
+
+ç„¶è€Œï¼Œåœ¨å†…æ ¸ä¸­ï¼Œè¿™äº›æµ‹è¯•ä¼šè½¬æ¢æˆ KUnit 测试套件。
+è¿™æ„å‘³ç€æ–‡æ¡£æµ‹è¯•ä¼šè¢«ç¼–è¯‘æˆ Rust 内核对象,从而å¯ä»¥åœ¨æž„建的内核环境中è¿è¡Œã€‚
+
+通过与 KUnit 集æˆï¼ŒRust 的文档测试å¯ä»¥å¤ç”¨å†…核现有的测试设施。
+例如,内核日志会显示::
+
+ KTAP version 1
+ 1..1
+ KTAP version 1
+ # Subtest: rust_doctests_kernel
+ 1..59
+ # rust_doctest_kernel_build_assert_rs_0.location: rust/kernel/build_assert.rs:13
+ ok 1 rust_doctest_kernel_build_assert_rs_0
+ # rust_doctest_kernel_build_assert_rs_1.location: rust/kernel/build_assert.rs:56
+ ok 2 rust_doctest_kernel_build_assert_rs_1
+ # rust_doctest_kernel_init_rs_0.location: rust/kernel/init.rs:122
+ ok 3 rust_doctest_kernel_init_rs_0
+ ...
+ # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150
+ ok 59 rust_doctest_kernel_types_rs_2
+ # rust_doctests_kernel: pass:59 fail:0 skip:0 total:59
+ # Totals: pass:59 fail:0 skip:0 total:59
+ ok 1 rust_doctests_kernel
+
+文档测试中,也å¯ä»¥æ­£å¸¸ä½¿ç”¨ `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_ è¿ç®—符,例如:
+
+.. code-block:: rust
+
+ /// ```
+ /// # use kernel::{spawn_work_item, workqueue};
+ /// spawn_work_item!(workqueue::system(), || pr_info!("x\n"))?;
+ /// # Ok::<(), Error>(())
+ /// ```
+
+这些测试和普通代ç ä¸€æ ·ï¼Œä¹Ÿå¯ä»¥åœ¨ ``CLIPPY=1`` æ¡ä»¶ä¸‹é€šè¿‡ Clippy 进行编译,
+å› æ­¤å¯ä»¥ä»Žé¢å¤–çš„ lint 检查中获益。
+
+为了便于开å‘è€…å®šä½æ–‡æ¡£æµ‹è¯•出错的具体行å·ï¼Œæ—¥å¿—ä¼šè¾“å‡ºä¸€æ¡ KTAP 诊断信æ¯ã€‚
+其中标明了原始测试的文件和行å·ï¼ˆä¸æ˜¯ ``rustdoc`` 生æˆçš„临时 Rust 文件ä½ç½®ï¼‰::
+
+ # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150
+
+Rust æµ‹è¯•ä¸­å¸¸ç”¨çš„æ–­è¨€å®æ˜¯æ¥è‡ª Rust 标准库( ``core`` )中的 ``assert!`` å’Œ ``assert_eq!`` å®ã€‚
+内核æä¾›äº†ä¸€ä¸ªå®šåˆ¶ç‰ˆæœ¬ï¼Œè¿™äº›å®çš„调用会被转å‘到 KUnit。
+å’Œ KUnit 测试ä¸åŒçš„æ˜¯ï¼Œè¿™äº›å®ä¸éœ€è¦ä¼ é€’ä¸Šä¸‹æ–‡å‚æ•°ï¼ˆ ``struct kunit *`` )。
+è¿™ä½¿å¾—å®ƒä»¬æ›´æ˜“äºŽä½¿ç”¨ï¼ŒåŒæ—¶æ–‡æ¡£çš„读者无需关心底层用的是什么测试框架。
+æ­¤å¤–ï¼Œè¿™ç§æ–¹å¼æœªæ¥ä¹Ÿè®¸å¯ä»¥è®©æˆ‘们更容易测试第三方代ç ã€‚
+
+当剿œ‰ä¸€ä¸ªé™åˆ¶ï¼šKUnit 䏿”¯æŒåœ¨å…¶ä»–任务中执行断言。
+å› æ­¤ï¼Œå¦‚æžœæ–­è¨€çœŸçš„å¤±è´¥äº†ï¼Œæˆ‘ä»¬åªæ˜¯ç®€å•地把错误打å°åˆ°å†…核日志里。
+å¦å¤–,文档测试ä¸é€‚用于éžå…¬å¼€çš„函数。
+
+ä½œä¸ºæ–‡æ¡£ä¸­çš„æµ‹è¯•ç¤ºä¾‹ï¼Œåº”å½“åƒ â€œå®žé™…ä»£ç â€ 一样编写。
+例如:ä¸è¦ä½¿ç”¨ ``unwrap()`` 或 ``expect()``,请使用 `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_ è¿ç®—符。
+更多背景信æ¯ï¼Œè¯·å‚阅:
+
+ https://rust.docs.kernel.org/kernel/error/type.Result.html#error-codes-in-c-and-rust
+
+``#[test]`` 测试
+----------------
+
+此外,还有 ``#[test]`` 测试。与文档测试类似,这些测试与用户空间中的测试方å¼ä¹Ÿéžå¸¸ç›¸è¿‘ï¼Œå¹¶ä¸”åŒæ ·ä¼šæ˜ å°„到 KUnit。
+
+这些测试通过 ``kunit_tests`` 过程å®å¼•入,该å®å°†æµ‹è¯•套件的åç§°ä½œä¸ºå‚æ•°ã€‚
+
+例如,å‡è®¾æƒ³è¦æµ‹è¯•å‰é¢æ–‡æ¡£æµ‹è¯•示例中的函数 ``f``,我们å¯ä»¥åœ¨å®šä¹‰è¯¥å‡½æ•°çš„åŒä¸€æ–‡ä»¶ä¸­ç¼–写:
+
+.. code-block:: rust
+
+ #[kunit_tests(rust_kernel_mymod)]
+ mod tests {
+ use super::*;
+
+ #[test]
+ fn test_f() {
+ assert_eq!(f(10, 20), 30);
+ }
+ }
+
+如果我们执行这段代ç ï¼Œå†…核日志会显示::
+
+ KTAP version 1
+ # Subtest: rust_kernel_mymod
+ # speed: normal
+ 1..1
+ # test_f.speed: normal
+ ok 1 test_f
+ ok 1 rust_kernel_mymod
+
+与文档测试类似, ``assert!`` å’Œ ``assert_eq!`` å®è¢«æ˜ å°„回 KUnit 并且ä¸ä¼šå‘生 panic。
+åŒæ ·ï¼Œæ”¯æŒ `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_ è¿ç®—符,
+测试函数å¯ä»¥ä»€ä¹ˆéƒ½ä¸è¿”回(å•元类型 ``()``)或 ``Result`` (任何 ``Result<T, E>``)。例如:
+
+.. code-block:: rust
+
+ #[kunit_tests(rust_kernel_mymod)]
+ mod tests {
+ use super::*;
+
+ #[test]
+ fn test_g() -> Result {
+ let x = g()?;
+ assert_eq!(x, 30);
+ Ok(())
+ }
+ }
+
+如果我们è¿è¡Œæµ‹è¯•并且调用 ``g`` 失败,那么内核日志会显示::
+
+ KTAP version 1
+ # Subtest: rust_kernel_mymod
+ # speed: normal
+ 1..1
+ # test_g: ASSERTION FAILED at rust/kernel/lib.rs:335
+ Expected is_test_result_ok(test_g()) to be true, but is false
+ # test_g.speed: normal
+ not ok 1 test_g
+ not ok 1 rust_kernel_mymod
+
+如果 ``#[test]`` 测试å¯ä»¥å¯¹ç”¨æˆ·èµ·åˆ°ç¤ºä¾‹ä½œç”¨ï¼Œé‚£å°±åº”该改用文档测试。
+å³ä½¿æ˜¯ API çš„è¾¹ç•Œæƒ…å†µï¼Œä¾‹å¦‚é”™è¯¯æˆ–è¾¹ç•Œé—®é¢˜ï¼Œæ”¾åœ¨ç¤ºä¾‹ä¸­å±•ç¤ºä¹ŸåŒæ ·æœ‰ä»·å€¼ã€‚
+
+``rusttest`` 宿主机测试
+-----------------------
+
+这类测试è¿è¡Œåœ¨ç”¨æˆ·ç©ºé—´ï¼Œå¯ä»¥é€šè¿‡ ``rusttest`` 目标在构建内核的宿主机中编译并è¿è¡Œ::
+
+ make LLVM=1 rusttest
+
+当剿“作需è¦å†…æ ¸ ``.config``。
+
+ç›®å‰ï¼Œå®ƒä»¬ä¸»è¦ç”¨äºŽæµ‹è¯• ``macros`` crate 的示例。
+
+Kselftests
+----------
+
+Kselftests å¯ä»¥åœ¨ ``tools/testing/selftests/rust`` 文件夹中找到。
+
+测试所需的内核é…置选项列在 ``tools/testing/selftests/rust/config`` 文件中,
+å¯ä»¥å€ŸåŠ© ``merge_config.sh`` 脚本åˆå¹¶åˆ°çŽ°æœ‰é…置中::
+
+ ./scripts/kconfig/merge_config.sh .config tools/testing/selftests/rust/config
+
+Kselftests 会在内核æºç æ ‘中构建,以便在è¿è¡Œç›¸åŒç‰ˆæœ¬å†…核的系统上执行测试。
+
+一旦安装并å¯åŠ¨äº†ä¸Žæºç æ ‘匹é…的内核,测试å³å¯é€šè¿‡ä»¥ä¸‹å‘½ä»¤ç¼–译并执行::
+
+ make TARGETS="rust" kselftest
+
+请å‚阅 Documentation/dev-tools/kselftest.rst æ–‡æ¡£ä»¥èŽ·å–æ›´å¤šä¿¡æ¯ã€‚
diff --git a/Documentation/translations/zh_CN/scsi/index.rst b/Documentation/translations/zh_CN/scsi/index.rst
new file mode 100644
index 000000000000..5f1803e2706c
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/index.rst
@@ -0,0 +1,92 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/index.rst
+
+:翻译:
+
+ éƒæ ‹æ ‹ doubled <doubled@leap-io-kernel.com>
+
+:校译:
+
+
+
+==========
+SCSIå­ç³»ç»Ÿ
+==========
+
+.. toctree::
+ :maxdepth: 1
+
+简介
+====
+
+.. toctree::
+ :maxdepth: 1
+
+ scsi
+
+SCSI驱动接å£
+============
+
+.. toctree::
+ :maxdepth: 1
+
+ scsi_mid_low_api
+ scsi_eh
+
+SCSIé©±åŠ¨å‚æ•°
+============
+
+.. toctree::
+ :maxdepth: 1
+
+ scsi-parameters
+ link_power_management_policy
+
+SCSI主机适é…器驱动
+==================
+
+.. toctree::
+ :maxdepth: 1
+
+ libsas
+ sd-parameters
+ wd719x
+
+Todolist:
+
+* 53c700
+* aacraid
+* advansys
+* aha152x
+* aic79xx
+* aic7xxx
+* arcmsr_spec
+* bfa
+* bnx2fc
+* BusLogic
+* cxgb3i
+* dc395x
+* dpti
+* FlashPoint
+* g_NCR5380
+* hpsa
+* hptiop
+* lpfc
+* megaraid
+* ncr53c8xx
+* NinjaSCSI
+* ppa
+* qlogicfas
+* scsi-changer
+* scsi_fc_transport
+* scsi-generic
+* smartpqi
+* st
+* sym53c500_cs
+* sym53c8xx_2
+* tcm_qla2xxx
+* ufs
+
+* scsi_transport_srp/figures
diff --git a/Documentation/translations/zh_CN/scsi/libsas.rst b/Documentation/translations/zh_CN/scsi/libsas.rst
new file mode 100644
index 000000000000..15fa71cdd821
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/libsas.rst
@@ -0,0 +1,425 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/libsas.rst
+
+:翻译:
+
+ å¼ é’°æ° Yujie Zhang <yjzhang@leap-io-kernel.com>
+
+:校译:
+
+======
+SAS 层
+======
+
+SAS 层是一个管ç†åŸºç¡€æž¶æž„ï¼Œç”¨äºŽç®¡ç† SAS LLDD。它ä½äºŽ SCSI Core
+与 SAS LLDD 之间。 体系结构如下: SCSI Core 关注的是 SAM/SPC 相
+关的问题;SAS LLDD åŠå…¶åºåˆ—控制器负责 PHY 层ã€OOB ä¿¡å·ä»¥åŠé“¾è·¯
+管ç†ï¼›è€Œ SAS 层则负责以下任务::
+
+ * SAS Phyã€Port 和主机适é…器(HA)事件管ç†ï¼ˆäº‹ä»¶ç”± LLDD
+ 生æˆï¼Œç”± SAS 层处ç†ï¼‰ï¼›
+ * SAS 端å£çš„管ç†ï¼ˆåˆ›å»ºä¸Žé”€æ¯ï¼‰ï¼›
+ * SAS 域的å‘çŽ°ä¸Žé‡æ–°éªŒè¯ï¼›
+ * SAS 域内设备的管ç†ï¼›
+ * SCSI 主机的注册与注销;
+ * 将设备注册到 SCSI Core(SAS 设备)或 libata(SATA 设备);
+ * 扩展器的管ç†ï¼Œå¹¶å‘用户空间导出扩展器控制接å£ã€‚
+
+SAS LLDD æ˜¯ä¸€ç§ PCI 设备驱动程åºã€‚它负责 PHY 层和 OOB(带外)
+ä¿¡å·çš„管ç†ã€åŽ‚å•†ç‰¹å®šçš„ä»»åŠ¡ï¼Œå¹¶å‘ SAS 层上报事件。
+
+SAS 层实现了 SAS 1.1 规范中定义的大部分 SAS 功能。
+
+sas_ha_struct ç»“æž„ä½“ç”¨äºŽå‘ SAS 层æè¿°ä¸€ä¸ª SAS LLDD。该结构的
+大部分字段由 SAS 层使用,但其中少数字段需è¦ç”± LLDD 进行åˆå§‹åŒ–。
+
+在完æˆç¡¬ä»¶åˆå§‹åŒ–之åŽï¼Œåº”当在驱动的 probe() 函数中调用
+sas_register_ha()。该函数会将 LLDD 注册到 SCSI å­ç³»ç»Ÿä¸­ï¼Œåˆ›
+建一个对应的 SCSI 主机,并将你的 SAS é©±åŠ¨ç¨‹åºæ³¨å†Œåˆ°å…¶åœ¨ sysfs
+下创建的 SAS 设备树中。éšåŽè¯¥å‡½æ•°å°†è¿”回。接ç€ï¼Œä½ éœ€è¦ä½¿èƒ½ PHY,
+以å¯åŠ¨å®žé™…çš„ OOB(带外)过程;此时驱动将开始调用 notify_* ç³»
+列事件回调函数。
+
+结构体说明
+==========
+
+``struct sas_phy``
+------------------
+
+é€šå¸¸æƒ…å†µä¸‹ï¼Œè¯¥ç»“æž„ä½“ä¼šè¢«é™æ€åœ°åµŒå…¥åˆ°é©±åŠ¨è‡ªèº«å®šä¹‰çš„ PHY 结构体中,
+例如::
+
+ struct my_phy {
+ blah;
+ struct sas_phy sas_phy;
+ bleh;
+ }
+
+éšåŽï¼Œåœ¨ä¸»æœºé€‚é…器(HA)的结构体中,所有的 PHY 通常以 my_phy
+数组的形å¼å­˜åœ¨ï¼ˆå¦‚下文所示)。
+
+在åˆå§‹åŒ–å„个 PHY 时,除了åˆå§‹åŒ–驱动自定义的 PHY 结构体外,还
+需è¦åŒæ—¶åˆå§‹åŒ–其中的 sas_phy 结构体。
+
+一般æ¥è¯´ï¼ŒPHY 的管ç†ç”± LLDD 负责,而端å£ï¼ˆport)的管ç†ç”± SAS
+层负责。因此,PHY çš„åˆå§‹åŒ–与更新由 LLDD 完æˆï¼Œè€Œç«¯å£çš„åˆå§‹åŒ–与
+更新则由 SAS 层完æˆã€‚系统设计中规定,æŸäº›å­—段å¯ç”± LLDD 进行读
+写,而 SAS 层åªèƒ½è¯»å–这些字段;å之亦然。其设计目的是为了é¿å…ä¸
+å¿…è¦çš„锿“作。
+
+在该设计中,æŸäº›å­—段å¯ç”± LLDD 进行读写(RW),而 SAS 层仅å¯è¯»
+å–这些字段;å之亦然。这样设计的目的在于é¿å…ä¸å¿…è¦çš„锿“作。
+
+enabled
+ - 必须设置(0/1)
+
+id
+ - 必须设置[0,MAX_PHYS)]
+
+class, proto, type, role, oob_mode, linkrate
+ - 必须设置。
+
+oob_mode
+ - 当 OOB(带外信å·ï¼‰å®ŒæˆåŽï¼Œè®¾ç½®æ­¤å­—段,然åŽé€šçŸ¥ SAS 层。
+
+sas_addr
+ - 通常指å‘一个ä¿å­˜è¯¥ PHY çš„ SAS 地å€çš„æ•°ç»„,该数组å¯èƒ½ä½äºŽ
+ 驱动自定义的 my_phy 结构体中。
+
+attached_sas_addr
+ - 当 LLDD 接收到 IDENTIFY 帧或 FIS 帧时,应在通知 SAS 层
+ 之å‰è®¾ç½®è¯¥å­—段。其设计æ„图在于:有时 LLDD å¯èƒ½éœ€è¦ä¼ªé€ æˆ–
+ æä¾›ä¸€ä¸ªä¸Žå®žé™…ä¸åŒçš„ SAS 地å€ç”¨äºŽè¯¥ PHY/端å£ï¼Œè€Œè¯¥æœºåˆ¶å…许
+ LLDD 这样åšã€‚ç†æƒ³æƒ…况下,应将 SAS 地å€ä»Ž IDENTIFY 帧中
+ å¤åˆ¶è¿‡æ¥ï¼›å¯¹äºŽç›´æŽ¥è¿žæŽ¥çš„ SATA 设备,也å¯ä»¥ç”± LLDD 生æˆä¸€
+ 个 SAS 地å€ã€‚åŽç»­çš„å‘现过程å¯èƒ½ä¼šä¿®æ”¹æ­¤å­—段。
+
+frame_rcvd
+ - 当接收到 IDENTIFY 或 FIS 帧时,将该帧å¤åˆ¶åˆ°æ­¤å¤„。正确的
+ æ“作æµç¨‹æ˜¯èŽ·å–é” â†’ å¤åˆ¶æ•°æ® → 设置 frame_rcvd_size → 释
+ æ”¾é” â†’ 调用事件通知。该字段是一个指针,因为驱动无法精确确
+ 定硬件帧的大å°ï¼›å› æ­¤ï¼Œå®žé™…çš„å¸§æ•°æ®æ•°ç»„应定义在驱动自定义的
+ PHY 结构体中,然åŽè®©æ­¤æŒ‡é’ˆæŒ‡å‘该数组。在æŒé”状æ€ä¸‹ï¼Œå°†å¸§ä»Ž
+ DMA å¯è®¿é—®å†…存区域å¤åˆ¶åˆ°è¯¥æ•°ç»„中。
+
+sas_prim
+ - 用于存放接收到的原语(primitive)。å‚è§ sas.h。æ“作æµç¨‹åŒ
+ 样是:获å–é” â†’ 设置 primitive → é‡Šæ”¾é” â†’ 通知事件。
+
+port
+ - 如果该 PHY 属于æŸä¸ªç«¯å£ï¼ˆport),此字段指å‘对应的 sas_port
+ 结构体。LLDD ä»…å¯è¯»å–此字段。它由 SAS 层设置,用于指å‘当å‰
+ PHY 所属的 sas_port。
+
+ha
+ - å¯ä»¥ç”± LLDD 设置;但无论是å¦è®¾ç½®ï¼ŒSAS å±‚éƒ½ä¼šå†æ¬¡å¯¹å…¶è¿›è¡Œèµ‹å€¼ã€‚
+
+lldd_phy
+ - LLDD 应将此字段设置为指å‘自身定义的 PHY 结构体,这样当 SAS
+ 层调用æŸä¸ªå›žè°ƒå¹¶ä¼ å…¥ sas_phy 时,驱动å¯ä»¥å¿«é€Ÿå®šä½è‡ªèº«çš„ PHY
+ 结构体。如果 sas_phy æ˜¯åµŒå…¥å¼æˆå‘˜ï¼Œä¹Ÿå¯ä»¥ä½¿ç”¨ container_of()
+ å®è¿›è¡Œè®¿é—®â€”â€”ä¸¤ç§æ–¹å¼å‡å¯ã€‚
+
+``struct sas_port``
+-------------------
+
+LLDD ä¸åº”修改该结构体中的任何字段——它åªèƒ½è¯»å–这些字段。这些字段的
+å«ä¹‰åº”当是ä¸è¨€è‡ªæ˜Žçš„。
+
+phy_mask 为 32 ä½ï¼Œç›®å‰è¿™ä¸€é•¿åº¦å·²è¶³å¤Ÿä½¿ç”¨ï¼Œå› ä¸ºå°šæœªå¬è¯´æœ‰ä¸»æœºé€‚é…
+器拥有超过8 个 PHY。
+
+lldd_port
+ - ç›®å‰å°šæ— æ˜Žç¡®ç”¨é€”。ä¸è¿‡ï¼Œå¯¹äºŽé‚£äº›å¸Œæœ›åœ¨ LLDD 内部维护自身端
+ å£è¡¨ç¤ºçš„驱动,实现时å¯ä»¥åˆ©ç”¨è¯¥å­—段。
+
+``struct sas_ha_struct``
+------------------------
+
+å®ƒé€šå¸¸é™æ€å£°æ˜Žåœ¨ä½ è‡ªå·±çš„ LLDD 结构中,用于æè¿°æ‚¨çš„适é…器::
+
+ struct my_sas_ha {
+ blah;
+ struct sas_ha_struct sas_ha;
+ struct my_phy phys[MAX_PHYS];
+ struct sas_port sas_ports[MAX_PHYS]; /* (1) */
+ bleh;
+ };
+
+ (1) 如果你的 LLDD 没有自己的端å£è¡¨ç¤º
+
+需è¦åˆå§‹åŒ–(示例函数如下所示)。
+
+pcidev
+^^^^^^
+
+sas_addr
+ - 由于 SAS 层䏿ƒ³å¼„乱内存分é…ç­‰, 因此这指å‘陿€åˆ†é…的数
+ 组中的æŸä¸ªä½ç½®ï¼ˆä¾‹å¦‚,在您的主机适é…器结构中),并ä¿å­˜æ‚¨æˆ–
+ 制造商等给出的主机适é…器的 SAS 地å€ã€‚
+
+sas_port
+^^^^^^^^
+
+sas_phy
+ - 指å‘结构体的指针数组(å‚è§ä¸Šæ–‡å…³äºŽ sas_addr 的说明)。
+ 这些指针必须设置。更多细节è§ä¸‹æ–‡è¯´æ˜Žã€‚
+
+num_phys
+ - 表示 sas_phy 数组中 PHY 的数é‡ï¼ŒåŒæ—¶ä¹Ÿè¡¨ç¤º sas_port
+ æ•°ç»„ä¸­çš„ç«¯å£æ•°é‡ã€‚ä¸€ä¸ªç«¯å£æœ€å¤šå¯¹åº”一个 PHYï¼Œå› æ­¤æœ€å¤§ç«¯å£æ•°
+ 等于 num_phys。因此,结构中ä¸å†å•独使用 num_ports 字段,
+ 而仅使用 num_phys。
+
+事件接å£::
+
+ /* LLDD 调用以下函数æ¥é€šçŸ¥ SAS 类层å‘生事件 */
+ void sas_notify_port_event(struct sas_phy *, enum port_event, gfp_t);
+ void sas_notify_phy_event(struct sas_phy *, enum phy_event, gfp_t);
+
+端å£äº‹ä»¶é€šçŸ¥::
+
+ /* SAS 类层调用以下回调æ¥é€šçŸ¥ LLDD 端å£äº‹ä»¶ */
+ void (*lldd_port_formed)(struct sas_phy *);
+ void (*lldd_port_deformed)(struct sas_phy *);
+
+如果 LLDD 希望在端å£å½¢æˆæˆ–解散时接收通知,则应将上述回调指针设
+置为符åˆå‡½æ•°ç±»åž‹å®šä¹‰çš„处ç†å‡½æ•°ã€‚
+
+SAS LLDD 还应至少实现 SCSI å议中定义的一ç§ä»»åŠ¡ç®¡ç†å‡½æ•°ï¼ˆTMFs)::
+
+ /* 任务管ç†å‡½æ•°. 必须在进程上下文中调用 */
+ int (*lldd_abort_task)(struct sas_task *);
+ int (*lldd_abort_task_set)(struct domain_device *, u8 *lun);
+ int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
+ int (*lldd_I_T_nexus_reset)(struct domain_device *);
+ int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
+ int (*lldd_query_task)(struct sas_task *);
+
+如需更多信æ¯ï¼Œè¯·å‚考 T10.org。
+
+端å£ä¸Žé€‚é…器管ç†::
+
+ /* 端å£ä¸Žé€‚é…å™¨ç®¡ç† */
+ int (*lldd_clear_nexus_port)(struct sas_port *);
+ int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
+
+SAS LLDD 至少应实现上述函数中的一个。
+
+PHY 管ç†::
+
+ /* PHY ç®¡ç† */
+ int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
+
+lldd_ha
+ - 应设置为指å‘驱动的主机适é…器(HA)结构体的指针。如果 sas_ha_struct
+ 被嵌入到更大的结构体中,也å¯ä»¥é€šè¿‡ container_of() 宿¥èŽ·å–。
+
+一个示例的åˆå§‹åŒ–与注册函数å¯ä»¥å¦‚下所示:(该函数应在 probe()
+函数的最åŽè°ƒç”¨ï¼‰ä½†å¿…须在使能 PHY 执行 OOB 之å‰è°ƒç”¨::
+
+ static int register_sas_ha(struct my_sas_ha *my_ha)
+ {
+ int i;
+ static struct sas_phy *sas_phys[MAX_PHYS];
+ static struct sas_port *sas_ports[MAX_PHYS];
+
+ my_ha->sas_ha.sas_addr = &my_ha->sas_addr[0];
+
+ for (i = 0; i < MAX_PHYS; i++) {
+ sas_phys[i] = &my_ha->phys[i].sas_phy;
+ sas_ports[i] = &my_ha->sas_ports[i];
+ }
+
+ my_ha->sas_ha.sas_phy = sas_phys;
+ my_ha->sas_ha.sas_port = sas_ports;
+ my_ha->sas_ha.num_phys = MAX_PHYS;
+
+ my_ha->sas_ha.lldd_port_formed = my_port_formed;
+
+ my_ha->sas_ha.lldd_dev_found = my_dev_found;
+ my_ha->sas_ha.lldd_dev_gone = my_dev_gone;
+
+ my_ha->sas_ha.lldd_execute_task = my_execute_task;
+
+ my_ha->sas_ha.lldd_abort_task = my_abort_task;
+ my_ha->sas_ha.lldd_abort_task_set = my_abort_task_set;
+ my_ha->sas_ha.lldd_clear_task_set = my_clear_task_set;
+ my_ha->sas_ha.lldd_I_T_nexus_reset= NULL; (2)
+ my_ha->sas_ha.lldd_lu_reset = my_lu_reset;
+ my_ha->sas_ha.lldd_query_task = my_query_task;
+
+ my_ha->sas_ha.lldd_clear_nexus_port = my_clear_nexus_port;
+ my_ha->sas_ha.lldd_clear_nexus_ha = my_clear_nexus_ha;
+
+ my_ha->sas_ha.lldd_control_phy = my_control_phy;
+
+ return sas_register_ha(&my_ha->sas_ha);
+ }
+
+(2) SAS 1.1 未定义 I_T Nexus Reset TMF(任务管ç†åŠŸèƒ½ï¼‰ã€‚
+
+事件
+====
+
+事件是 SAS LLDD 唯一的通知 SAS 层å‘生任何情况的方å¼ã€‚
+LLDD 没有其他方法å¯ä»¥å‘ŠçŸ¥ SAS 层其内部或 SAS 域中å‘生的事件。
+
+Phy 事件::
+
+ PHYE_LOSS_OF_SIGNAL, (C)
+ PHYE_OOB_DONE,
+ PHYE_OOB_ERROR, (C)
+ PHYE_SPINUP_HOLD.
+
+端å£äº‹ä»¶ï¼Œé€šè¿‡ _phy_ 传递::
+
+ PORTE_BYTES_DMAED, (M)
+ PORTE_BROADCAST_RCVD, (E)
+ PORTE_LINK_RESET_ERR, (C)
+ PORTE_TIMER_EVENT, (C)
+ PORTE_HARD_RESET.
+
+主机适é…器事件:
+ HAE_RESET
+
+SAS LLDD 应能够生æˆä»¥ä¸‹äº‹ä»¶::
+
+ - æ¥è‡ª C 组的至少一个事件(å¯é€‰ï¼‰ï¼Œ
+ - 标记为 M(必需)的事件为必需事件(至少一ç§ï¼‰ï¼›
+ - 若希望 SAS 层处ç†åŸŸé‡æ–°éªŒè¯ï¼ˆdomain revalidation),则
+ åº”ç”Ÿæˆæ ‡è®°ä¸º E(扩展器)的事件(仅需一ç§ï¼‰ï¼›
+ - 未标记的事件为å¯é€‰äº‹ä»¶ã€‚
+
+å«ä¹‰
+
+HAE_RESET
+ - 当 HA å‘生内部错误并被å¤ä½æ—¶ã€‚
+
+PORTE_BYTES_DMAED
+ - 在接收到 IDENTIFY/FIS 帧时。
+
+PORTE_BROADCAST_RCVD
+ - 在接收到一个原语时。
+
+PORTE_LINK_RESET_ERR
+ - 定时器超时ã€ä¿¡å·ä¸¢å¤±ã€ä¸¢å¤± DWS 等情况。 [1]_
+
+PORTE_TIMER_EVENT
+ - DWS å¤ä½è¶…时定时器到期时。[1]_
+
+PORTE_HARD_RESET
+ - 收到 Hard Reset 原语。
+
+PHYE_LOSS_OF_SIGNAL
+ - 设备已断开连接。 [1]_
+
+PHYE_OOB_DONE
+ - OOB 过程æˆåŠŸå®Œæˆï¼Œoob_mode 有效。
+
+PHYE_OOB_ERROR
+ - 执行 OOB 过程中出现错误,设备å¯èƒ½å·²æ–­å¼€ã€‚ [1]_
+
+PHYE_SPINUP_HOLD
+ - 检测到 SATA 设备,但未å‘é€ COMWAKE ä¿¡å·ã€‚
+
+.. [1] 应设置或清除 phy 中相应的字段,或者从 tasklet 中调用
+ 内è”函数 sas_phy_disconnected()ï¼Œè¯¥å‡½æ•°åªæ˜¯ä¸€ä¸ªè¾…助函数。
+
+执行命令 SCSI RPC::
+
+ int (*lldd_execute_task)(struct sas_task *, gfp_t gfp_flags);
+
+用于将任务排队æäº¤ç»™ SAS LLDD,@task ä¸ºè¦æ‰§è¡Œçš„任务,@gfp_mask
+为定义调用者上下文的 gfp 掩ç ã€‚
+
+此函数应实现 执行 SCSI RPC 命令。
+
+也就是说,当调用 lldd_execute_task() 时,命令应当立å³åœ¨ä¼ è¾“
+层å‘出。SAS LLDD 中在任何层级上都ä¸åº”å†è¿›è¡Œé˜Ÿåˆ—排放。
+
+返回值::
+
+ * 返回 -SAS_QUEUE_FULL 或 -ENOMEM 表示未排入队列;
+ * 返回 0 表示任务已æˆåŠŸæŽ’å…¥é˜Ÿåˆ—ã€‚
+
+::
+
+ struct sas_task {
+ dev —— 此任务目标设备;
+ task_proto —— å议类型,为 enum sas_proto 中的一ç§ï¼›
+ scatter —— æŒ‡å‘æ•£å¸ƒ/èšé›†ï¼ˆSG)列表数组的指针;
+ num_scatter —— SG 列表元素数é‡ï¼›
+ total_xfer_len —— 预计传输的总字节数;
+ data_dir —— æ•°æ®ä¼ è¾“æ–¹å‘(PCI_DMA_*)ï¼›
+ task_done —— ä»»åŠ¡æ‰§è¡Œå®Œæˆæ—¶çš„回调函数。
+ };
+
+å‘现
+====
+
+sysfs 树有以下用途::
+
+ a) å®ƒæ˜¾ç¤ºå½“å‰æ—¶åˆ» SAS 域的物ç†å¸ƒå±€ï¼Œå³å±•示当å‰ç‰©ç†ä¸–界中
+ 域的实际结构。
+ b) 显示æŸäº›è®¾å¤‡çš„傿•°ã€‚ _at_discovery_time_.
+
+䏋颿˜¯ä¸€ä¸ªæŒ‡å‘ tree(1) 程åºçš„链接,该工具在查看 SAS 域时éžå¸¸
+有用:
+ftp://mama.indstate.edu/linux/tree/
+
+æˆ‘æœŸæœ›ç”¨æˆ·ç©ºé—´çš„åº”ç”¨ç¨‹åºæœ€ç»ˆèƒ½å¤Ÿä¸ºæ­¤åˆ›å»ºä¸€ä¸ªå›¾å½¢ç•Œé¢ã€‚
+
+也就是说,sysfs 域树ä¸ä¼šæ˜¾ç¤ºæˆ–ä¿å­˜æŸäº›çжæ€å˜åŒ–,例如,如果你更
+改了 READY LED å«ä¹‰çš„设置,sysfs æ ‘ä¸ä¼šå映这ç§çжæ€å˜åŒ–;但它
+确实会显示域设备的当å‰è¿žæŽ¥çжæ€ã€‚
+
+维护内部设备状æ€å˜åŒ–çš„èŒè´£ç”±ä¸Šå±‚(命令集驱动)和用户空间负责。
+
+当æŸä¸ªè®¾å¤‡æˆ–多个设备从域中拔出时,这一å˜åŒ–会立å³å映在 sysfs
+树中,并且这些设备会从系统中移除。
+
+结构体 domain_device æè¿°äº† SAS 域中的任æ„设备。它完全由 SAS
+层管ç†ã€‚ä¸€ä¸ªä»»åŠ¡ä¼šæŒ‡å‘æŸä¸ªåŸŸè®¾å¤‡ï¼ŒSAS LLDD å°±æ˜¯é€šè¿‡è¿™ç§æ–¹å¼çŸ¥
+é“任务应å‘é€åˆ°ä½•处。SAS LLDD åªè¯»å– domain_device 结构的内容,
+但ä¸ä¼šåˆ›å»ºæˆ–销æ¯å®ƒã€‚
+
+用户空间中的扩展器管ç†
+======================
+
+在 sysfs 中的æ¯ä¸ªæ‰©å±•器目录下,都有一个å为 "smp_portal" çš„
+文件。这是一个二进制的 sysfs 属性文件,它实现了一个 SMP å…¥å£
+(注æ„ï¼šè¿™å¹¶ä¸æ˜¯ä¸€ä¸ª SMP 端å£ï¼‰ï¼Œç”¨æˆ·ç©ºé—´ç¨‹åºå¯ä»¥é€šè¿‡å®ƒå‘é€
+SMP 请求并接收 SMP å“应。
+
+该功能的实现方å¼çœ‹èµ·æ¥éžå¸¸ç®€å•:
+
+1. 构建è¦å‘é€çš„ SMP 帧。其格å¼å’Œå¸ƒå±€åœ¨ SAS è§„èŒƒä¸­æœ‰è¯´æ˜Žã€‚ä¿æŒ
+ CRC 字段为 0。
+
+open(2)
+
+2. ä»¥è¯»å†™æ¨¡å¼æ‰“开该扩展器的 SMP portal sysfs 文件。
+
+write(2)
+
+3. 将第 1 步中构建的帧写入文件。
+
+read(2)
+
+4. 读å–与所构建帧预期返回长度相åŒçš„æ•°æ®é‡ã€‚如果读å–的数æ®é‡ä¸Ž
+ 预期ä¸ç¬¦ï¼Œåˆ™è¡¨ç¤ºå‘生了æŸç§é”™è¯¯ã€‚
+
+close(2)
+
+整个过程在 "expander_conf.c" 文件中的函数 do_smp_func()
+åŠå…¶è°ƒç”¨è€…中有详细展示。
+
+对应的内核实现ä½äºŽ "sas_expander.c" 文件中。
+
+ç¨‹åº "expander_conf.c" å®žçŽ°äº†ä¸Šè¿°é€»è¾‘ã€‚å®ƒæŽ¥æ”¶ä¸€ä¸ªå‚æ•°â€”—扩展器
+SMP portal çš„ sysfs 文件å,并输出扩展器的信æ¯ï¼ŒåŒ…括路由表内容。
+
+SMP portal 赋予了你对扩展器的完全控制æƒï¼Œå› æ­¤è¯·è°¨æ…Žæ“作。
diff --git a/Documentation/translations/zh_CN/scsi/link_power_management_policy.rst b/Documentation/translations/zh_CN/scsi/link_power_management_policy.rst
new file mode 100644
index 000000000000..f2ab8fdf4aa8
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/link_power_management_policy.rst
@@ -0,0 +1,32 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/link_power_management_policy.rst
+
+:翻译:
+
+ éƒæ ‹æ ‹ doubled <doubled@leap-io-kernel.com>
+
+:校译:
+
+
+
+================
+链路电æºç®¡ç†ç­–ç•¥
+================
+
+è¯¥å‚æ•°å…许用户设置链路(接å£ï¼‰çš„电æºç®¡ç†æ¨¡å¼ã€‚
+共计三类å¯é€‰é¡¹ï¼š
+
+===================== =====================================================
+选项 作用
+===================== =====================================================
+min_power 指示控制器在å¯èƒ½çš„æƒ…况下尽é‡ä½¿é“¾è·¯å¤„于最低功耗。
+ è¿™å¯èƒ½ä¼šç‰ºç‰²ä¸€å®šçš„æ€§èƒ½ï¼Œå› ä¸ºä»Žä½ŽåŠŸè€—çŠ¶æ€æ¢å¤æ—¶ä¼šå¢žåŠ å»¶è¿Ÿã€‚
+
+max_performance 通常,这æ„味ç€ä¸è¿›è¡Œç”µæºç®¡ç†ã€‚指示
+ 控制器优先考虑性能而éžç”µæºç®¡ç†ã€‚
+
+medium_power 指示控制器在å¯èƒ½çš„æƒ…况下进入较低功耗状æ€ï¼Œ
+ è€Œéžæœ€ä½ŽåŠŸè€—çŠ¶æ€ï¼Œä»Žè€Œæ”¹å–„min_power模å¼ä¸‹çš„延迟。
+===================== =====================================================
diff --git a/Documentation/translations/zh_CN/scsi/scsi-parameters.rst b/Documentation/translations/zh_CN/scsi/scsi-parameters.rst
new file mode 100644
index 000000000000..ace777e070ea
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/scsi-parameters.rst
@@ -0,0 +1,118 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/scsi-parameters.rst
+
+:翻译:
+
+ éƒæ ‹æ ‹ doubled <doubled@leap-io-kernel.com>
+
+:校译:
+
+
+
+============
+SCSIå†…æ ¸å‚æ•°
+============
+
+请查阅Documentation/admin-guide/kernel-parameters.rst以获å–
+指定模å—傿•°ç›¸å…³çš„通用信æ¯ã€‚
+
+当剿–‡æ¡£å¯èƒ½ä¸å®Œå…¨æ˜¯æœ€æ–°å’Œå…¨é¢çš„。命令 ``modinfo -p ${modulename}``
+显示了å¯åŠ è½½æ¨¡å—çš„å‚æ•°åˆ—表。å¯åŠ è½½æ¨¡å—被加载到内核中åŽï¼Œä¹Ÿä¼šåœ¨
+/sys/module/${modulename}/parameters/ ç›®å½•ä¸‹æ˜¾ç¤ºå…¶å‚æ•°ã€‚å…¶
+中æŸäº›å‚æ•°å¯ä»¥é€šè¿‡å‘½ä»¤
+``echo -n ${value} > /sys/module/${modulename}/parameters/${parm}``
+在è¿è¡Œæ—¶ä¿®æ”¹ã€‚
+
+::
+
+ advansys= [HW,SCSI]
+ 请查阅 drivers/scsi/advansys.c 文件头部。
+
+ aha152x= [HW,SCSI]
+ 请查阅 Documentation/scsi/aha152x.rst。
+
+ aha1542= [HW,SCSI]
+ æ ¼å¼ï¼š<portbase>[,<buson>,<busoff>[,<dmaspeed>]]
+
+ aic7xxx= [HW,SCSI]
+ 请查阅 Documentation/scsi/aic7xxx.rst。
+
+ aic79xx= [HW,SCSI]
+ 请查阅 Documentation/scsi/aic79xx.rst。
+
+ atascsi= [HW,SCSI]
+ 请查阅 drivers/scsi/atari_scsi.c。
+
+ BusLogic= [HW,SCSI]
+ 请查阅 drivers/scsi/BusLogic.c 文件中
+ BusLogic_ParseDriverOptions()函数å‰çš„æ³¨é‡Šã€‚
+
+ gvp11= [HW,SCSI]
+
+ ips= [HW,SCSI] Adaptec / IBM ServeRAID 控制器
+ 请查阅 drivers/scsi/ips.c 文件头部。
+
+ mac5380= [HW,SCSI]
+ 请查阅 drivers/scsi/mac_scsi.c。
+
+ scsi_mod.max_luns=
+ [SCSI] æœ€å¤§å¯æŽ¢æµ‹LUN数。
+ å–值范围为 1 到 2^32-1。
+
+ scsi_mod.max_report_luns=
+ [SCSI] 接收到的最大LUN数。
+ å–值范围为 1 到 16384。
+
+ NCR_D700= [HW,SCSI]
+ 请查阅 drivers/scsi/NCR_D700.c 文件头部。
+
+ ncr5380= [HW,SCSI]
+ 请查阅 Documentation/scsi/g_NCR5380.rst。
+
+ ncr53c400= [HW,SCSI]
+ 请查阅 Documentation/scsi/g_NCR5380.rst。
+
+ ncr53c400a= [HW,SCSI]
+ 请查阅 Documentation/scsi/g_NCR5380.rst。
+
+ ncr53c8xx= [HW,SCSI]
+
+ osst= [HW,SCSI] SCSIç£å¸¦é©±åЍ
+ æ ¼å¼ï¼š<buffer_size>,<write_threshold>
+ å¦è¯·æŸ¥é˜… Documentation/scsi/st.rst。
+
+ scsi_debug_*= [SCSI]
+ 请查阅 drivers/scsi/scsi_debug.c。
+
+ scsi_mod.default_dev_flags=
+ [SCSI] SCSI默认设备标志
+ æ ¼å¼ï¼š<integer>
+
+ scsi_mod.dev_flags=
+ [SCSI] 厂商和型å·çš„黑/白å啿¡ç›®
+ æ ¼å¼ï¼š<vendor>:<model>:<flags>
+ (flags 为整数值)
+
+ scsi_mod.scsi_logging_level=
+ [SCSI] æ—¥å¿—çº§åˆ«çš„ä½æŽ©ç 
+ ä½çš„定义请查阅 drivers/scsi/scsi_logging.h。
+ æ­¤å‚æ•°ä¹Ÿå¯ä»¥é€šè¿‡sysctl对dev.scsi.logging_level
+ 进行设置(/proc/sys/dev/scsi/logging_level)。
+ 此外,S390-tools软件包æä¾›äº†ä¸€ä¸ªä¾¿æ·çš„
+ ‘scsi_logging_level’ 脚本,å¯ä»¥ä»Žä»¥ä¸‹åœ°å€ä¸‹è½½ï¼š
+ https://github.com/ibm-s390-linux/s390-tools/blob/master/scripts/scsi_logging_level
+
+ scsi_mod.scan= [SCSI] sync(默认)在å‘现SCSI总线过程中
+ åŒæ­¥æ‰«æã€‚async在内核线程中异步扫æï¼Œå…许系统继续
+ å¯åЍæµç¨‹ã€‚none忽略扫æï¼Œé¢„æœŸç”±ç”¨æˆ·ç©ºé—´å®Œæˆæ‰«æã€‚
+
+ sim710= [SCSI,HW]
+ 请查阅 drivers/scsi/sim710.c 文件头部。
+
+ st= [HW,SCSI] SCSIç£å¸¦å‚数(缓冲区大å°ç­‰ï¼‰
+ 请查阅 Documentation/scsi/st.rst。
+
+ wd33c93= [HW,SCSI]
+ 请查阅 drivers/scsi/wd33c93.c 文件头部。
diff --git a/Documentation/translations/zh_CN/scsi/scsi.rst b/Documentation/translations/zh_CN/scsi/scsi.rst
new file mode 100644
index 000000000000..5d6e39c7cbb5
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/scsi.rst
@@ -0,0 +1,48 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/scsi.rst
+
+:翻译:
+
+ éƒæ ‹æ ‹ doubled <doubled@leap-io-kernel.com>
+
+:校译:
+
+
+
+==============
+SCSIå­ç³»ç»Ÿæ–‡æ¡£
+==============
+
+Linux文档项目(LDP)维护了一份æè¿°Linux内核(lk) 2.4中SCSI
+å­ç³»ç»Ÿçš„æ–‡æ¡£ã€‚请å‚考:
+https://www.tldp.org/HOWTO/SCSI-2.4-HOWTO 。LDPæä¾›å•页和
+多页的HTML版本,以åŠPostScript与PDFæ ¼å¼çš„æ–‡æ¡£ã€‚
+
+在SCSIå­ç³»ç»Ÿä¸­ä½¿ç”¨æ¨¡å—的注æ„事项
+================================
+Linux内核中的SCSI支æŒå¯ä»¥æ ¹æ®ç»ˆç«¯ç”¨æˆ·çš„需求以ä¸åŒçš„æ–¹å¼æ¨¡å—
+化。为了ç†è§£ä½ çš„选择,我们首先需è¦å®šä¹‰ä¸€äº›æœ¯è¯­ã€‚
+
+scsi-core(也被称为“中间层â€ï¼‰åŒ…å«SCSI支æŒçš„æ ¸å¿ƒã€‚没有他你将
+无法使用任何其他SCSI驱动程åºã€‚SCSI核心支æŒå¯ä»¥æ˜¯ä¸€ä¸ªæ¨¡å—(
+scsi_mod.o),也å¯ä»¥ç¼–译进内核。如果SCSI核心是一个模å—,那么
+他必须是第一个被加载的SCSI模å—,如果你将å¸è½½è¯¥æ¨¡å—,那么他必
+须是最åŽä¸€ä¸ªè¢«å¸è½½çš„æ¨¡å—。实际上,modprobeå’Œrmmod命令将确ä¿
+SCSIå­ç³»ç»Ÿä¸­æ¨¡å—加载与å¸è½½çš„æ­£ç¡®é¡ºåºã€‚
+
+一旦SCSI核心存在于内核中(无论是编译进内核还是作为模å—加载),
+独立的上层驱动和底层驱动å¯ä»¥æŒ‰ç…§ä»»æ„顺åºåŠ è½½ã€‚ç£ç›˜é©±åŠ¨ç¨‹åº
+(sd_mod.o)ã€å…‰ç›˜é©±åŠ¨ç¨‹åºï¼ˆsr_mod.o)ã€ç£å¸¦é©±åŠ¨ç¨‹åº [1]_
+(st.o)以åŠSCSI通用驱动程åºï¼ˆsg.o)代表了上层驱动,用于控制
+相应的å„ç§è®¾å¤‡ã€‚例如,你å¯ä»¥åŠ è½½ç£å¸¦é©±åŠ¨ç¨‹åºæ¥ä½¿ç”¨ç£å¸¦é©±åŠ¨å™¨ï¼Œ
+ç„¶åŽåœ¨ä¸éœ€è¦è¯¥é©±åŠ¨ç¨‹åºæ—¶å¸è½½ä»–(并释放相关内存)。
+
+底层驱动程åºç”¨äºŽæ”¯æŒæ‚¨æ‰€è¿è¡Œç¡¬ä»¶å¹³å°æ”¯æŒçš„ä¸åŒä¸»æœºå¡ã€‚这些ä¸åŒ
+的主机å¡é€šå¸¸è¢«ç§°ä¸ºä¸»æœºæ€»çº¿é€‚é…器(HBAs)。例如,aic7xxx.o驱动
+程åºè¢«ç”¨äºŽæŽ§åˆ¶Adaptec所属的所有最新的SCSI控制器。几乎所有的底
+层驱动都å¯ä»¥è¢«ç¼–è¯‘ä¸ºæ¨¡å—æˆ–直接编译进内核。
+
+.. [1] ç£å¸¦é©±åŠ¨ç¨‹åºæœ‰ä¸€ä¸ªå˜ç§ç”¨äºŽæŽ§åˆ¶OnStreamç£å¸¦è®¾å¤‡ã€‚其模å—
+ å称为osst.o 。
diff --git a/Documentation/translations/zh_CN/scsi/scsi_eh.rst b/Documentation/translations/zh_CN/scsi/scsi_eh.rst
new file mode 100644
index 000000000000..26e0f30f0949
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/scsi_eh.rst
@@ -0,0 +1,482 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/scsi_eh.rst
+
+:翻译:
+
+ éƒæ ‹æ ‹ doubled <doubled@leap-io-kernel.com>
+
+:校译:
+
+
+===================
+SCSI 中间层错误处ç†
+===================
+
+本文档æè¿°äº†SCSI中间层(mid layer)的错误处ç†åŸºç¡€æž¶æž„。
+关于SCSI中间层的更多信æ¯ï¼Œè¯·å‚阅:
+Documentation/scsi/scsi_mid_low_api.rst。
+
+.. 目录
+
+ [1] SCSI 命令如何通过中间层传递并进入错误处ç†ï¼ˆEH)
+ [1-1] scsi_cmnd(SCSI命令)结构体
+ [1-2] scmd(SCSI 命令)是如何完æˆçš„?
+ [1-2-1] 通过scsi_done完æˆscmd
+ [1-2-2] 通过超时机制完æˆscmd
+ [1-3] é”™è¯¯å¤„ç†æ¨¡å—如何接管æµç¨‹
+ [2] SCSIé”™è¯¯å¤„ç†æœºåˆ¶å·¥ä½œåŽŸç†
+ [2-1] 基于细粒度回调的错误处ç†
+ [2-1-1] 概览
+ [2-1-2] scmdåœ¨é”™è¯¯å¤„ç†æµç¨‹ä¸­çš„传递路径
+ [2-1-3] 控制æµåˆ†æž
+ [2-2] 通过transportt->eh_strategy_handler()实现的错误处ç†
+ [2-2-1] transportt->eh_strategy_handler()调用å‰çš„中间层状æ€
+ [2-2-2] transportt->eh_strategy_handler()调用åŽçš„中间层状æ€
+ [2-2-3] 注æ„事项
+
+
+1. SCSI命令在中间层åŠé”™è¯¯å¤„ç†ä¸­çš„传递æµç¨‹
+=========================================
+
+1.1 scsi_cmnd结构体
+-------------------
+
+æ¯ä¸ªSCSI命令都由struct scsi_cmnd(简称scmd)结构体
+表示。scmd包å«ä¸¤ä¸ªlist_head类型的链表节点:scmd->list
+与scmd->eh_entry。其中scmd->list是用于空闲链表或设备
+专属的scmd分é…链表,与错误处ç†è®¨è®ºå…³è”ä¸å¤§ã€‚而
+scmd->eh_entry则是专用于命令完æˆå’Œé”™è¯¯å¤„ç†é“¾è¡¨ï¼Œé™¤éž
+特别说明,本文讨论中所有scmd的链表æ“作å‡é€šè¿‡
+scmd->eh_entry实现。
+
+
+1.2 scmd是如何完æˆçš„?
+----------------------
+
+底层设备驱动(LLDD)在获å–SCSI命令(scmd)åŽï¼Œå­˜åœ¨ä¸¤ç§
+完æˆè·¯å¾„:底层驱动å¯é€šè¿‡è°ƒç”¨hostt->queuecommand()时从
+中间层传递的scsi_done回调函数主动完æˆå‘½ä»¤ï¼Œæˆ–者当命令未
+åŠæ—¶å®Œæˆæ—¶ç”±å—层(block layer)触å‘è¶…æ—¶å¤„ç†æœºåˆ¶ã€‚
+
+
+1.2.1 通过scsi_done回调完æˆSCSI命令
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+对于所有éžé”™è¯¯å¤„ç†ï¼ˆEH)命令,scsi_done()是其完æˆå›žè°ƒ
+函数。它åªè°ƒç”¨blk_mq_complete_request()æ¥åˆ é™¤å—层的
+定时器并触å‘å—设备软中断(BLOCK_SOFTIRQ)。
+
+BLOCK_SOFTIRQ会间接调用scsi_complete(),进而调用
+scsi_decide_disposition()æ¥å†³å®šå¦‚何处ç†è¯¥å‘½ä»¤ã€‚
+scsi_decide_disposition()会查看scmd->result值和感
+åº”ç æ•°æ®æ¥å†³å®šå¦‚何处ç†å‘½ä»¤ã€‚
+
+ - SUCCESS
+
+ 调用scsi_finish_command()æ¥å¤„ç†è¯¥å‘½ä»¤ã€‚该函数会
+ 执行一些维护æ“作,然åŽè°ƒç”¨scsi_io_completion()æ¥
+ 完æˆI/Oæ“作。scsi_io_completion()会通过调用
+ blk_end_requeståŠå…¶ç›¸å…³å‡½æ•°æ¥é€šçŸ¥å—层该请求已完æˆï¼Œ
+ 如果å‘生错误,还会判断如何处ç†å‰©ä½™çš„æ•°æ®ã€‚
+
+ - NEEDS_RETRY
+
+ - ADD_TO_MLQUEUE
+
+ scmdè¢«é‡æ–°åŠ å…¥åˆ°å—设备队列中。
+
+ - otherwise
+
+ 调用scsi_eh_scmd_add(scmd)æ¥å¤„ç†è¯¥å‘½ä»¤ã€‚
+ 关于此函数的详细信æ¯ï¼Œè¯·å‚è§ [1-3]。
+
+
+1.2.2 scmdè¶…æ—¶å®Œæˆæœºåˆ¶
+^^^^^^^^^^^^^^^^^^^^^^
+
+SCSIå‘½ä»¤è¶…æ—¶å¤„ç†æœºåˆ¶ç”±scsi_timeout()函数实现。
+当å‘生超时事件时,该函数
+
+ 1. 首先调用å¯é€‰çš„hostt->eh_timed_out()回调函数。
+ 返回值å¯èƒ½æ˜¯ä»¥ä¸‹3ç§æƒ…况之一:
+
+ - ``SCSI_EH_RESET_TIMER``
+ 表示需è¦å»¶é•¿å‘½ä»¤æ‰§è¡Œæ—¶é—´å¹¶é‡å¯è®¡æ—¶å™¨ã€‚
+
+ - ``SCSI_EH_NOT_HANDLED``
+ 表示eh_timed_out()未处ç†è¯¥å‘½ä»¤ã€‚
+ 此时将执行第2æ­¥çš„å¤„ç†æµç¨‹ã€‚
+
+ - ``SCSI_EH_DONE``
+ 表示eh_timed_out()已完æˆè¯¥å‘½ä»¤ã€‚
+
+ 2. 若未通过回调函数解决,系统将调用
+ scsi_abort_command()å‘起异步中止æ“作,该æ“作最多
+ 坿‰§è¡Œscmd->allowed + 1次。但存在三ç§ä¾‹å¤–情况会跳
+ 过异步中止而直接进入第3步处ç†ï¼šå½“检测到
+ SCSI_EH_ABORT_SCHEDULED标志ä½å·²ç½®ä½ï¼ˆè¡¨æ˜Žè¯¥å‘½ä»¤å…ˆ
+ å‰å·²è¢«ä¸­æ­¢è¿‡ä¸€æ¬¡ä¸”当å‰é‡è¯•ä»å¤±è´¥ï¼‰ã€å½“é‡è¯•次数已达上
+ é™ã€æˆ–å½“é”™è¯¯å¤„ç†æ—¶é™å·²åˆ°æœŸæ—¶ã€‚在这些情况下,系统将跳
+ 过异步中止æµç¨‹è€Œç›´æŽ¥æ‰§è¡Œç¬¬3æ­¥å¤„ç†æ–¹æ¡ˆã€‚
+
+ 3. 最终未解决的命令会通过scsi_eh_scmd_add(scmd)移交给
+ 错误处ç†å­ç³»ç»Ÿï¼Œå…·ä½“æµç¨‹è¯¦è§[1-4]章节说明。
+
+1.3 异步命令中止机制
+--------------------
+
+当命令超时触å‘åŽï¼Œç³»ç»Ÿä¼šé€šè¿‡scsi_abort_command()调度异
+步中止æ“作。若中止æ“作执行æˆåŠŸï¼Œåˆ™æ ¹æ®é‡è¯•次数决定åŽç»­å¤„
+ç†ï¼šè‹¥æœªè¾¾æœ€å¤§é‡è¯•é™åˆ¶ï¼Œå‘½ä»¤å°†é‡æ–°ä¸‹å‘执行;若é‡è¯•次数已
+耗尽,则命令最终以DID_TIME_OUT状æ€ç»ˆæ­¢ã€‚当中止æ“作失败
+时,系统会调用scsi_eh_scmd_add()将该命令移交错误处ç†å­
+ç³»ç»Ÿï¼Œå…·ä½“å¤„ç†æµç¨‹è¯¦è§[1-4]。
+
+1.4 错误处ç†(EH)接管机制
+------------------------
+
+SCSI命令通过scsi_eh_scmd_add()å‡½æ•°è¿›å…¥é”™è¯¯å¤„ç†æµç¨‹ï¼Œè¯¥å‡½
+数执行以下æ“作:
+
+ 1. 将scmd->eh_entry链接到shost->eh_cmd_q
+
+ 2. 在shost->shost_state中设置SHOST_RECOVERY状æ€ä½
+
+ 3. 递增shost->host_failed失败计数器
+
+ 4. 当检测到shost->host_busy == shost->host_failed
+ æ—¶ï¼ˆå³æ‰€æœ‰è¿›è¡Œä¸­å‘½ä»¤å‡å·²å¤±è´¥ï¼‰ç«‹å³å”¤é†’SCSI错误处ç†
+ 线程。
+
+如上所述,当任一scmd被加入到shost->eh_cmd_q队列时,系统
+会立å³ç½®ä½shost_state中的SHOST_RECOVERYçŠ¶æ€æ ‡å¿—ä½ï¼Œè¯¥æ“
+作将阻止å—层å‘对应主机控制器下å‘任何新的SCSI命令。在此状
+æ€ä¸‹ï¼Œä¸»æœºæŽ§åˆ¶å™¨ä¸Šæ‰€æœ‰æ­£åœ¨å¤„ç†çš„scmd最终会进入以下三ç§çж
+æ€ä¹‹ä¸€ï¼šæ­£å¸¸å®Œæˆã€å¤±è´¥åŽè¢«ç§»å…¥åˆ°eh_cmd_qé˜Ÿåˆ—ã€æˆ–因超时被
+添加到shost->eh_cmd_q队列。
+
+如果所有的SCSI命令都已ç»å®Œæˆæˆ–失败,系统中正在执行的命令
+æ•°é‡ä¸Žå¤±è´¥å‘½ä»¤æ•°é‡ç›¸ç­‰ï¼ˆ
+å³shost->host_busy == shost->host_failed),此时将唤
+醒SCSI错误处ç†çº¿ç¨‹ã€‚SCSI错误处ç†çº¿ç¨‹ä¸€æ—¦è¢«å”¤é†’,就å¯ä»¥ç¡®
+ä¿æ‰€æœ‰æœªå®Œæˆå‘½ä»¤å‡å·²æ ‡è®°ä¸ºå¤±è´¥çжæ€ï¼Œå¹¶ä¸”å·²ç»è¢«é“¾æŽ¥åˆ°
+shost->eh_cmd_q队列中。
+
+需è¦ç‰¹åˆ«è¯´æ˜Žçš„æ˜¯ï¼Œè¿™å¹¶ä¸æ„味ç€åº•å±‚å¤„ç†æµç¨‹å®Œå…¨é™æ­¢ã€‚当底层
+驱动以错误状æ€å®ŒæˆæŸä¸ªscmd时,底层驱动åŠå…¶ä¸‹å±‚组件会立刻é—
+忘该命令的所有关è”状æ€ã€‚但对于超时命令,除éž
+hostt->eh_timed_out()å›žè°ƒå‡½æ•°å·²ç»æ˜Žç¡®é€šçŸ¥åº•层驱动丢弃该
+å‘½ä»¤ï¼ˆå½“å‰æ‰€æœ‰åº•å±‚é©±åŠ¨å‡æœªå®žçŽ°æ­¤åŠŸèƒ½ï¼‰ï¼Œå¦åˆ™ä»Žåº•层驱动视角
+看该命令ä»å¤„于活跃状æ€ï¼Œç†è®ºä¸Šä»å¯èƒ½åœ¨æŸæ—¶åˆ»å®Œæˆã€‚当然,由
+于超时计时器早已触å‘,所有此类延迟完æˆéƒ½å°†è¢«ç³»ç»Ÿç›´æŽ¥å¿½ç•¥ã€‚
+
+我们将在åŽç»­ç« èŠ‚è¯¦ç»†è®¨è®ºå…³äºŽSCSI错误处ç†å¦‚何执行中止æ“作(
+å³å¼ºåˆ¶åº•层驱动丢弃已超时SCSI命令)。
+
+
+2. SCSIé”™è¯¯å¤„ç†æœºåˆ¶è¯¦è§£
+=======================
+
+SCSI底层驱动å¯ä»¥é€šè¿‡ä»¥ä¸‹ä¸¤ç§æ–¹å¼ä¹‹ä¸€æ¥å®žçްSCSI错误处ç†ã€‚
+
+ - 细粒度的错误处ç†å›žè°ƒæœºåˆ¶
+ 底层驱动å¯é€‰æ‹©å®žçŽ°ç»†ç²’åº¦çš„é”™è¯¯å¤„ç†å›žè°ƒå‡½æ•°ï¼Œç”±SCSI中间层
+ 主导错误æ¢å¤æµç¨‹å¹¶è‡ªåŠ¨è°ƒç”¨å¯¹åº”çš„å›žè°ƒå‡½æ•°ã€‚æ­¤å®žçŽ°æ¨¡å¼çš„详
+ 细设计规范在[2-1]节中展开讨论。
+
+ - eh_strategy_handler()回调函数
+ 该回调函数作为统一的错误处ç†å…¥å£ï¼Œéœ€è¦å®Œæ•´å®žçŽ°æ‰€æœ‰çš„æ¢å¤
+ æ“作。具体而言,它必须涵盖SCSI中间层在常规æ¢å¤è¿‡ç¨‹ä¸­æ‰§è¡Œ
+ çš„å…¨éƒ¨å¤„ç†æµç¨‹ï¼Œç›¸å…³å®žçŽ°å°†åœ¨[2-2]节中详细æè¿°ã€‚
+
+当错误æ¢å¤æµç¨‹å®ŒæˆåŽï¼ŒSCSI错误处ç†ç³»ç»Ÿé€šè¿‡è°ƒç”¨
+scsi_restart_operations()函数æ¢å¤æ­£å¸¸è¿è¡Œï¼Œè¯¥å‡½æ•°æŒ‰é¡ºåºæ‰§è¡Œ
+以下æ“作:
+
+ 1. éªŒè¯æ˜¯å¦éœ€è¦æ‰§è¡Œé©±åЍ噍安免闍é”定机制
+
+ 2. 清除shost_state中的SHOST_RECOVERYçŠ¶æ€æ ‡å¿—ä½
+
+ 3. 唤醒所有在shost->host_wait上等待的任务。如果有人调用了
+ scsi_block_when_processing_errors()则会å‘ç”Ÿè¿™ç§æƒ…况。
+ ï¼ˆç–‘é—®ï¼šç”±äºŽé”™è¯¯å¤„ç†æœŸé—´å—层队列已被阻塞,为何ä»éœ€æ˜¾å¼
+ 唤醒?)
+
+ 4. 强制激活该主机控制器下所有设备的I/O队列
+
+
+2.1 åŸºäºŽç»†ç²’åº¦å›žè°ƒçš„é”™è¯¯å¤„ç†æœºåˆ¶
+--------------------------------
+
+2.1.1 概述
+^^^^^^^^^^^
+
+如果ä¸å­˜åœ¨eh_strategy_handler(),SCSI中间层将负责驱动的
+错误处ç†ã€‚错误处ç†ï¼ˆEH)的目标有两个:一是让底层驱动程åºã€
+主机和设备ä¸å†ç»´æŠ¤å·²è¶…æ—¶çš„SCSI命令(scmd);二是使他们准备
+好接收新命令。当一个SCSI命令(scmd)被底层é—忘且底层已准备
+好冿¬¡å¤„ç†æˆ–æ‹’ç»è¯¥å‘½ä»¤æ—¶ï¼Œå³å¯è®¤ä¸ºè¯¥scmdå·²æ¢å¤ã€‚
+
+为实现这些目标,错误处ç†ï¼ˆEHï¼‰ä¼šé€æ­¥æ‰§è¡Œä¸¥é‡æ€§é€’增的æ¢å¤
+æ“作。部分æ“作通过下å‘SCSI命令完æˆï¼Œè€Œå…¶ä»–æ“作则通过调用
+以下细粒度的错误处ç†å›žè°ƒå‡½æ•°å®žçŽ°ã€‚è¿™äº›å›žè°ƒå‡½æ•°å¯ä»¥çœç•¥ï¼Œ
+若被çœç•¥åˆ™é»˜è®¤å§‹ç»ˆè§†ä¸ºæ‰§è¡Œå¤±è´¥ã€‚
+
+::
+
+ int (* eh_abort_handler)(struct scsi_cmnd *);
+ int (* eh_device_reset_handler)(struct scsi_cmnd *);
+ int (* eh_bus_reset_handler)(struct scsi_cmnd *);
+ int (* eh_host_reset_handler)(struct scsi_cmnd *);
+
+åªæœ‰åœ¨ä½Žçº§åˆ«çš„错误æ¢å¤æ“作无法æ¢å¤éƒ¨åˆ†å¤±è´¥çš„SCSI命令
+(scmd)时,æ‰ä¼šé‡‡å–更高级别的æ¢å¤æ“作。如果最高级别的错误
+处ç†å¤±è´¥ï¼Œå°±æ„å‘³ç€æ•´ä¸ªé”™è¯¯æ¢å¤ï¼ˆEH)过程失败,所有未能æ¢å¤
+的设备被强制下线。
+
+在æ¢å¤è¿‡ç¨‹ä¸­ï¼Œéœ€éµå¾ªä»¥ä¸‹è§„则:
+
+ - 错误æ¢å¤æ“作针对待处ç†åˆ—表eh_work_q中的失败的scmds执
+ 行。如果æŸä¸ªæ¢å¤æ“作æˆåŠŸæ¢å¤äº†ä¸€ä¸ªscmd,那么该scmd会
+ 从eh_work_q链表中移除。
+
+ éœ€è¦æ³¨æ„的是,对æŸä¸ªscmd执行的å•个æ¢å¤æ“作å¯èƒ½ä¼šæ¢å¤
+ 多个scmd。例如,对æŸä¸ªè®¾å¤‡æ‰§è¡Œå¤ä½æ“作å¯èƒ½ä¼šæ¢å¤è¯¥è®¾
+ 备上所有失败的scmd。
+
+ - 仅当低级别的æ¢å¤æ“作完æˆä¸”eh_work_qä»ç„¶éžç©ºæ—¶ï¼Œæ‰ä¼š
+ è§¦å‘æ›´é«˜çº§åˆ«çš„æ“ä½œ
+
+ - SCSI错误æ¢å¤æœºåˆ¶ä¼šé‡ç”¨å¤±è´¥çš„scmdæ¥å‘逿¢å¤å‘½ä»¤ã€‚对于
+ è¶…æ—¶çš„scmd,SCSIé”™è¯¯å¤„ç†æœºåˆ¶ä¼šç¡®ä¿åº•层驱动在é‡ç”¨scmd
+ å‰å·²ä¸å†ç»´æŠ¤è¯¥å‘½ä»¤ã€‚
+
+当一个SCSI命令(scmd)被æˆåŠŸæ¢å¤åŽï¼Œé”™è¯¯å¤„ç†é€»è¾‘会通过
+scsi_eh_finish_cmd()将其从待处ç†é˜Ÿåˆ—(eh_work_q)移
+至错误处ç†çš„æœ¬åœ°å®Œæˆé˜Ÿåˆ—(eh_done_q)。当所有scmd凿¢
+å¤å®Œæˆï¼ˆå³eh_work_q为空时),错误处ç†é€»è¾‘会调用
+scsi_eh_flush_done_q()对这些已æ¢å¤çš„scmd进行处ç†ï¼Œå³
+釿–°å°è¯•或错误总终止(å‘上层通知失败)。
+
+SCSI命令仅在满足以下全部æ¡ä»¶æ—¶æ‰ä¼šè¢«é‡è¯•:对应的SCSI设
+备ä»å¤„于在线状æ€ï¼Œæœªè®¾ç½®REQ_FAILFAST标志或递增åŽçš„
+scmd->retries值ä»å°äºŽscmd->allowed。
+
+2.1.2 SCSI命令在错误处ç†è¿‡ç¨‹ä¸­çš„æµè½¬è·¯å¾„
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ 1. 错误完æˆ/è¶…æ—¶
+
+ :处ç†: 调用scsi_eh_scmd_add()处ç†scmd
+
+ - 将scmd添加到shost->eh_cmd_q
+ - 设置SHOST_RECOVERY标记ä½
+ - shost->host_failed++
+
+ :é”è¦æ±‚: shost->host_lock
+
+ 2. å¯åŠ¨é”™è¯¯å¤„ç†ï¼ˆEH)
+
+ :æ“作: 将所有scmd移动到EH本地eh_work_q队列,并
+ 清空 shost->eh_cmd_q。
+
+ :é”è¦æ±‚: shost->host_lock(éžä¸¥æ ¼å¿…éœ€ï¼Œä»…ä¸ºä¿æŒä¸€è‡´æ€§ï¼‰
+
+ 3. scmdæ¢å¤
+
+ :æ“作: 调用scsi_eh_finish_cmd()完æˆscmdçš„EH
+
+ - 将scmd从本地eh_work_q队列移至本地eh_done_q队列
+
+ :é”è¦æ±‚: æ— 
+
+ :å¹¶å‘æŽ§åˆ¶: æ¯ä¸ªç‹¬ç«‹çš„eh_work_qè‡³å¤šä¸€ä¸ªçº¿ç¨‹ï¼Œç¡®ä¿æ— é”
+ 队列的访问
+
+ 4. EH完æˆ
+
+ :æ“作: 调用scsi_eh_flush_done_q()é‡è¯•scmd或通知上层处ç†
+ 失败。此函数å¯ä»¥è¢«å¹¶å‘调用,但æ¯ä¸ªç‹¬ç«‹çš„eh_work_q队
+ åˆ—è‡³å¤šä¸€ä¸ªçº¿ç¨‹ï¼Œä»¥ç¡®ä¿æ— é”队列的访问。
+
+ - 从eh_done_q队列中移除scmd,清除scmd->eh_entry
+ - 如果需è¦é‡è¯•,调用scsi_queue_insert()釿–°å…¥é˜Ÿscmd
+ - å¦åˆ™ï¼Œè°ƒç”¨scsi_finish_command()完æˆscmd
+ - 将shost->host_failed置为零
+
+ :é”è¦æ±‚: 队列或完æˆå‡½æ•°ä¼šæ‰§è¡Œé€‚å½“çš„åŠ é”æ“作
+
+
+2.1.3 控制æµ
+^^^^^^^^^^^^
+
+ 通过细粒度回调机制执行的SCSI错误处ç†ï¼ˆEH)是从
+ scsi_unjam_host()函数开始的
+
+``scsi_unjam_host``
+
+ 1. æŒæœ‰shost->host_locké”,将shost->eh_cmd_q中的命令移动
+ 到本地的eh_work_q队里中,并释放host_locké”。注æ„,这一步
+ 会清空shost->eh_cmd_q。
+
+ 2. 调用scsi_eh_get_sense函数。
+
+ ``scsi_eh_get_sense``
+
+ 该æ“作针对没有有效感知数æ®çš„错误完æˆå‘½ä»¤ã€‚大部分SCSI传输åè®®
+ æˆ–åº•å±‚é©±åŠ¨åœ¨å‘½ä»¤å¤±è´¥æ—¶ä¼šè‡ªåŠ¨èŽ·å–æ„ŸçŸ¥æ•°æ®ï¼ˆè‡ªåŠ¨æ„ŸçŸ¥ï¼‰ã€‚å‡ºäºŽæ€§
+ 能原因,建议使用自动感知,推è使用自动感知机制,因为它ä¸ä»…有
+ 助于æå‡æ€§èƒ½ï¼Œè¿˜èƒ½é¿å…从å‘生CHECK CONDITION到执行本æ“作之间,
+ 感知信æ¯å‡ºçްä¸åŒæ­¥çš„问题。
+
+ 注æ„ï¼Œå¦‚æžœä¸æ”¯æŒè‡ªåŠ¨æ„ŸçŸ¥ï¼Œé‚£ä¹ˆåœ¨ä½¿ç”¨scsi_done()以错误状æ€å®Œæˆ
+ scmd 时,scmd->sense_bufferå°†åŒ…å«æ— æ•ˆæ„ŸçŸ¥æ•°æ®ã€‚åœ¨è¿™ç§æƒ…况下,
+ scsi_decide_disposition()总是返回FAILED从而触å‘SCSI错误处ç†
+ (EH)。当该scmdæ‰§è¡Œåˆ°è¿™é‡Œæ—¶ï¼Œä¼šé‡æ–°èŽ·å–æ„ŸçŸ¥æ•°æ®ï¼Œå¹¶å†æ¬¡è°ƒç”¨
+ scsi_decide_disposition()进行处ç†ã€‚
+
+ 1. 调用scsi_request_sense()å‘é€REQUEST_SENSE命令。如果失败,
+ 则ä¸é‡‡å–任何æ“作。请注æ„,ä¸é‡‡å–任何æ“作会导致对该scmd执行
+ 更高级别的æ¢å¤æ“作。
+
+ 2. 调用scsi_decide_disposition()处ç†scmd
+
+ - SUCCESS
+ scmd->retries被设置为scmd->allowed以防止
+ scsi_eh_flush_done_q()é‡è¯•该scmd,并调用
+ scsi_eh_finish_cmd()。
+
+ - NEEDS_RETRY
+ 调用scsi_eh_finish_cmd()
+
+ - 其他情况
+ æ— æ“作。
+
+ 4. 如果!list_empty(&eh_work_q),则调用scsi_eh_ready_devs()。
+
+ ``scsi_eh_ready_devs``
+
+ 该函数采å–å››ç§é€æ­¥å¢žå¼ºçš„æŽªæ–½ï¼Œä½¿å¤±è´¥çš„è®¾å¤‡å‡†å¤‡å¥½å¤„ç†æ–°çš„命令。
+
+ 1. 调用scsi_eh_stu()
+
+ ``scsi_eh_stu``
+
+ 对于æ¯ä¸ªå…·æœ‰æœ‰æ•ˆæ„ŸçŸ¥æ•°æ®ä¸”scsi_check_sense()判断为失败的
+ scmdå‘é€START STOP UNIT(STU)命令且将startç½®1。注æ„,由
+ 于我们明确选择错误完æˆçš„scmd,å¯ä»¥ç¡®å®šåº•层驱动已ä¸å†ç»´æŠ¤è¯¥
+ scmd,我们å¯ä»¥é‡ç”¨å®ƒè¿›è¡ŒSTU。
+
+ 如果STUæ“作æˆåŠŸä¸”sdev处于离线或就绪状æ€ï¼Œæ‰€æœ‰åœ¨sdev上失败的
+ scmd都会通过scsi_eh_finish_cmd()完æˆã€‚
+
+ *注æ„* 如果hostt->eh_abort_handler()未实现或返回失败,å¯èƒ½
+ æ­¤æ—¶ä»æœ‰è¶…æ—¶çš„scmd,此时STUä¸ä¼šå¯¼è‡´åº•层驱动ä¸å†ç»´æŠ¤scmd。但
+ 是,如果STU执行æˆåŠŸï¼Œè¯¥å‡½æ•°ä¼šé€šè¿‡scsi_eh_finish_cmd()æ¥å®Œæˆ
+ sdev上的所有scmd,这会导致底层驱动处于ä¸ä¸€è‡´çš„状æ€ã€‚看æ¥STU
+ æ“作应仅在sdevä¸åŒ…å«è¶…æ—¶scmd时进行。
+
+ 2. 如果!list_empty(&eh_work_q),调用scsi_eh_bus_device_reset()。
+
+ ``scsi_eh_bus_device_reset``
+
+ æ­¤æ“作与scsi_eh_stu()éžå¸¸ç›¸ä¼¼ï¼ŒåŒºåˆ«åœ¨äºŽä½¿ç”¨
+ hostt->eh_device_reset_handler()替代STU命令。此外,由于我们
+ 没有å‘é€SCSI命令且é‡ç½®ä¼šæ¸…空该sdev上所有的scmd,所以无需筛选错
+ 误完æˆçš„scmd。
+
+ 3. 如果!list_empty(&eh_work_q),调用scsi_eh_bus_reset()。
+
+ ``scsi_eh_bus_reset``
+
+ 对于æ¯ä¸ªåŒ…å«å¤±è´¥scmdçš„SCSI通é“调用
+ hostt->eh_bus_reset_handler()。如果总线é‡ç½®æˆåŠŸï¼Œé‚£ä¹ˆè¯¥é€šé“上
+ 所有准备就绪或离线状æ€sdev上的失败scmd都会被处ç†å¤„ç†å®Œæˆã€‚
+
+ 4. 如果!list_empty(&eh_work_q),调用scsi_eh_host_reset()。
+
+ ``scsi_eh_host_reset``
+
+ 调用hostt->eh_host_reset_handler()是最终的手段。如果SCSI主机
+ é‡ç½®æˆåŠŸï¼Œä¸»æœºä¸Šæ‰€æœ‰å°±ç»ªæˆ–ç¦»çº¿sdev上的失败scmd都会通过错误处ç†
+ 完æˆã€‚
+
+ 5. 如果!list_empty(&eh_work_q),调用scsi_eh_offline_sdevs()。
+
+ ``scsi_eh_offline_sdevs``
+
+ ç¦»çº¿æ‰€æœ‰åŒ…å«æœªæ¢å¤scmd的所有sdev,并通过
+ scsi_eh_finish_cmd()完æˆè¿™äº›scmd。
+
+ 5. 调用scsi_eh_flush_done_q()。
+
+ ``scsi_eh_flush_done_q``
+
+ 此时所有的scmdéƒ½å·²ç»æ¢å¤ï¼ˆæˆ–放弃),并通过
+ scsi_eh_finish_cmd()函数加入eh_done_q队列。该函数通过
+ é‡è¯•或显示通知上层scmd的失败æ¥åˆ·æ–°eh_done_q。
+
+
+2.2 基于transportt->eh_strategy_handler()çš„é”™è¯¯å¤„ç†æœºåˆ¶
+-------------------------------------------------------------
+
+在该机制中,transportt->eh_strategy_handler()替代
+scsi_unjam_host()的被调用,并负责整个错误æ¢å¤è¿‡ç¨‹ã€‚该处ç†
+函数完æˆåŽåº”该确ä¿åº•层驱动ä¸å†ç»´æŠ¤ä»»ä½•失败的scmd并且将设备
+设置为就绪(准备接收新命令)或离线状æ€ã€‚此外,该函数还应该
+执行SCSI错误处ç†çš„维护任务,以维护SCSI中间层的数æ®å®Œæ•´æ€§ã€‚
+æ¢å¥è¯è¯´ï¼Œeh_strategy_handler()必须实现[2-1-2]中除第1æ­¥
+外的所有步骤。
+
+
+2.2.1 transportt->eh_strategy_handler()调用å‰çš„SCSI中间层状æ€
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ 进入该处ç†å‡½æ•°æ—¶ï¼Œä»¥ä¸‹æ¡ä»¶æˆç«‹ã€‚
+
+ - æ¯ä¸ªå¤±è´¥çš„scmdçš„eh_flags字段已正确设置。
+
+ - æ¯ä¸ªå¤±è´¥çš„scmd通过scmd->eh_entry链接到scmd->eh_cmd_q队列。
+
+ - 已设置SHOST_RECOVERY标志。
+
+ - `shost->host_failed == shost->host_busy`。
+
+2.2.2 transportt->eh_strategy_handler()调用åŽçš„SCSI中间层状æ€
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ 从该处ç†å‡½æ•°é€€å‡ºæ—¶ï¼Œä»¥ä¸‹æ¡ä»¶æˆç«‹ã€‚
+
+ - shost->host_failed为零。
+
+ - shost->eh_cmd_q被清空。
+
+ - æ¯ä¸ªscmd->eh_entry被清空。
+
+ - 对æ¯ä¸ªscmd必须调用scsi_queue_insert()或scsi_finish_command()。
+ 注æ„,该处ç†ç¨‹åºå¯ä»¥ä½¿ç”¨scmd->retries(剩余é‡è¯•次数)和
+ scmd->allowed(å…许é‡è¯•次数)é™åˆ¶é‡è¯•次数。
+
+
+2.2.3 注æ„事项
+^^^^^^^^^^^^^^
+
+ - 需明确已超时的scmd在底层ä»å¤„于活跃状æ€ï¼Œå› æ­¤åœ¨æ“作这些
+ scmdå‰ï¼Œå¿…须确ä¿åº•层已彻底ä¸å†ç»´æŠ¤ã€‚
+
+ - 访问或修改shostæ•°æ®ç»“æž„æ—¶ï¼Œå¿…é¡»æŒæœ‰shost->host_locké”
+ ä»¥ç»´æŒæ•°æ®ä¸€è‡´æ€§ã€‚
+
+ - 错误处ç†å®ŒæˆåŽï¼Œæ¯ä¸ªæ•…障设备必须彻底清除所有活跃SCSI命
+ 令(scmd)的关è”状æ€ã€‚
+
+ - 错误处ç†å®ŒæˆåŽï¼Œæ¯ä¸ªæ•…障设备必须被设置为就绪(准备接收
+ 新命令)或离线状æ€ã€‚
+
+
+Tejun Heo
+htejun@gmail.com
+
+11th September 2005
diff --git a/Documentation/translations/zh_CN/scsi/scsi_mid_low_api.rst b/Documentation/translations/zh_CN/scsi/scsi_mid_low_api.rst
new file mode 100644
index 000000000000..f701945a1b1c
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/scsi_mid_low_api.rst
@@ -0,0 +1,1174 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/scsi_mid_low_api.rst
+
+:翻译:
+
+ éƒæ ‹æ ‹ doubled <doubled@leap-io-kernel.com>
+
+:校译:
+
+
+
+=========================
+SCSI中间层 — 底层驱动接å£
+=========================
+
+简介
+====
+本文档概述了Linux SCSI中间层与SCSI底层驱动之间的接å£ã€‚底层
+驱动(LLD)通常被称为主机总线适é…器(HBA)驱动或主机驱动
+(HDï¼‰ã€‚åœ¨è¯¥ä¸Šä¸‹æ–‡ä¸­ï¼Œâ€œä¸»æœºâ€æŒ‡çš„æ˜¯è®¡ç®—机IO总线(例如:PCI总
+线或ISA总线)与SCSI传输层中å•个SCSIå¯åŠ¨å™¨ç«¯å£ä¹‹é—´çš„æ¡¥æ¢ã€‚
+“å¯åЍ噍â€ç«¯å£ï¼ˆSCSI术语,å‚考SAM-3:http://www.t10.org)å‘
+“目标â€SCSI端å£ï¼ˆä¾‹å¦‚:ç£ç›˜ï¼‰å‘é€SCSI命令。在一个è¿è¡Œçš„系统
+中存在多ç§åº•层驱动(LLDs),但æ¯ç§ç¡¬ä»¶ç±»åž‹ä»…对应一ç§åº•层驱动
+(LLD)。大多数底层驱动å¯ä»¥æŽ§åˆ¶ä¸€ä¸ªæˆ–多个SCSI HBA。部分HBA
+内部集æˆå¤šä¸ªä¸»æœºæŽ§åˆ¶å™¨ã€‚
+
+在æŸäº›æƒ…况下,SCSI传输层本身是已存在于Linux中的外部总线å­ç³»
+统(例如:USBå’Œieee1394)。在此类场景下,SCSIå­ç³»ç»Ÿçš„底层驱
+动将作为与其他驱动å­ç³»ç»Ÿçš„软件桥接层。典型示例包括
+usb-storage驱动(ä½äºŽdrivers/usb/storage目录)以
+åŠieee1394/sbp2驱动(ä½äºŽ drivers/ieee1394 目录)。
+
+例如,aic7xxx底层驱动负责控制基于Adaptecå…¬å¸7xxx芯片系列的
+SCSI并行接å£ï¼ˆSPI)控制器。aic7xxx底层驱动å¯ä»¥å†…建到内核中
+或作为模å—加载。一个Linux系统中åªèƒ½è¿è¡Œä¸€ä¸ªaic7xxx底层驱动
+程åºï¼Œä½†ä»–å¯èƒ½æŽ§åˆ¶å¤šä¸ªä¸»æœºæ€»çº¿é€‚é…器(HBA)。这些HBAå¯èƒ½ä½äºŽ
+PCIæ‰©å±•å¡æˆ–内置于主æ¿ä¸­ï¼ˆæˆ–两者兼有)。æŸäº›åŸºäºŽaic7xxxçš„HBA
+é‡‡ç”¨åŒæŽ§åˆ¶å™¨è®¾è®¡ï¼Œå› æ­¤ä¼šå‘ˆçŽ°ä¸ºä¸¤ä¸ªSCSI主机适é…器。与大多数现
+代HBA相åŒï¼Œæ¯ä¸ªaic7xxx控制器都拥有其独立的PCI设备地å€ã€‚[SCSI
+主机与PCI设备之间一一对应虽然常è§ï¼Œä½†å¹¶éžå¼ºåˆ¶è¦æ±‚(例如ISA适
+é…器就ä¸é€‚用此规则)。]
+
+SCSI中间层将SCSI底层驱动(LLD)与其他层(例如SCSI上层驱动以
+åŠå—层)隔离开æ¥ã€‚
+
+本文档的版本大致与Linux内核2.6.8相匹é…。
+
+文档
+====
+内核æºç æ ‘中设有专用的SCSI文档目录,通常ä½äºŽ
+Documentation/scsi目录下。大多数文档采用
+reStructuredTextæ ¼å¼ã€‚本文档å为
+scsi_mid_low_api.rst,å¯åœ¨è¯¥ç›®å½•中找到。该文档的最新版本å¯
+以访问 https://docs.kernel.org/scsi/scsi_mid_low_api.html
+查阅。许多底层驱动(LLD)的文档也ä½äºŽDocumentation/scsi目录
+下(例如aic7xxx.rst)。SCSI中间层的简è¦è¯´æ˜Žè§scsi.rst文件,
+è¯¥æ–‡æ¡£åŒ…å«æŒ‡å‘Linux Kernel 2.4系列SCSIå­ç³»ç»Ÿçš„æ–‡æ¡£é“¾æŽ¥ã€‚æ­¤
+外还收录了两份SCSI上层驱动文档:st.rst(SCSIç£å¸¦é©±åŠ¨ï¼‰ä¸Ž
+scsi-generic.rst(用通用SCSI(sg)驱动)。
+
+部分底层驱动的文档(或相关URL)å¯èƒ½åµŒåœ¨Cæºä»£ç æ–‡ä»¶æˆ–与其
+æºç åŒä½äºŽåŒä¸€ç›®å½•下。例如,USB大容é‡å­˜å‚¨é©±åŠ¨çš„æ–‡æ¡£é“¾æŽ¥å¯ä»¥åœ¨
+目录/usr/src/linux/drivers/usb/storage下找到。
+
+驱动程åºç»“æž„
+============
+传统上,SCSIå­ç³»ç»Ÿçš„底层驱动(LLD)至少包å«drivers/scsi
+目录下的两个文件。例如,一个å为“xyzâ€çš„驱动会包å«ä¸€ä¸ªå¤´æ–‡ä»¶
+xyz.hå’Œä¸€ä¸ªæºæ–‡ä»¶xyz.c。[实际上所有代ç å®Œå…¨å¯ä»¥åˆå¹¶ä¸ºå•个
+文件,头文件并éžå¿…需的。] 部分需è¦è·¨æ“作系统移æ¤çš„底层驱动会
+é‡‡ç”¨æ›´å¤æ‚的文件结构。例如,aic7xxx驱动,就为通用代ç ä¸Žæ“作
+系统专用代ç ï¼ˆå¦‚FreeBSDå’ŒLinux)分别创建了独立的文件。此类
+驱动通常会在drivers/scsi目录下拥有自己å•独的å­ç›®å½•。
+
+当需è¦å‘Linux内核添加新的底层驱动(LLD)时,必须留æ„
+drivers/scsi目录下的两个文件:Makefile以åŠKconfig。建议å‚
+考现有底层驱动的代ç ç»„织方å¼ã€‚
+
+éšç€Linux内核2.5å¼€å‘å†…æ ¸é€æ­¥æ¼”进为2.6系列的生产版本,该接å£
+也引入了一些å˜åŒ–。以驱动åˆå§‹åŒ–代ç ä¸ºä¾‹ï¼ŒçŽ°æœ‰ä¸¤ç§æ¨¡åž‹å¯ç”¨ã€‚å…¶
+中旧模型与Linux内核2.4的实现相似,他基于在加载HBA驱动时检测
+到的主机,被称为“被动(passive)â€åˆå§‹åŒ–模型。而新的模型å…许
+在底层驱动(LLDï¼‰çš„ç”Ÿå‘½å‘¨æœŸå†…åŠ¨æ€æ‹”æ’HBAï¼Œè¿™ç§æ–¹å¼è¢«ç§°ä¸ºâ€œçƒ­
+æ’æ‹”(hotplug)â€åˆå§‹åŒ–模型。推è使用新的模型,因为他既能处ç†
+传统的永久连接SCSI设备,也能处ç†çŽ°ä»£æ”¯æŒçƒ­æ’拔的类SCSI设备
+(例如通过USB或IEEE 1394连接的数ç ç›¸æœºï¼‰ã€‚这两ç§åˆå§‹åŒ–模型将
+在åŽç»­çš„章节中分别讨论。
+
+SCSI底层驱动(LLD)通过以下3ç§æ–¹å¼ä¸ŽSCSIå­ç³»ç»Ÿè¿›è¡Œäº¤äº’:
+
+ a) 直接调用由SCSI中间层æä¾›çš„æŽ¥å£å‡½æ•°
+ b) 将一组函数指针传递给中间层æä¾›çš„æ³¨å†Œå‡½æ•°ï¼Œä¸­é—´å±‚将在
+ åŽç»­è¿è¡Œçš„æŸä¸ªæ—¶åˆ»è°ƒç”¨è¿™äº›å‡½æ•°ã€‚è¿™äº›å‡½æ•°ç”±LLD实现。
+ c) 直接访问中间层维护的核心数æ®ç»“æž„
+
+a)组中所涉åŠçš„æ‰€æœ‰å‡½æ•°ï¼Œå‡åˆ—于下文“中间层æä¾›çš„函数â€ç« èŠ‚ä¸­ã€‚
+
+b)组中涉åŠçš„æ‰€æœ‰å‡½æ•°å‡åˆ—于下文å为“接å£å‡½æ•°â€çš„章节中。这些
+函数指针ä½äºŽç»“构体struct scsi_host_template中,该结构体实
+例会被传递给scsi_host_alloc()。对于LLD未实现的接å£å‡½æ•°ï¼Œåº”
+对struct scsi_host_template中的对应æˆå‘˜èµ‹NULL。如果在文件
+作用域定义一个struct scsi_host_template的实例,没有显å¼åˆ
+始化的函数指针æˆå‘˜å°†è‡ªåŠ¨è®¾ç½®ä¸ºNULL。
+
+c)组中æåˆ°çš„ç”¨æ³•åœ¨â€œçƒ­æ’æ‹”â€çŽ¯å¢ƒä¸­å°¤å…¶éœ€è¦è°¨æ…Žå¤„ç†ã€‚LLDå¿…é¡»
+明确知晓这些与中间层åŠå…¶ä»–层级共享的数æ®ç»“构的生命周期。
+
+LLD中定义的所有函数以åŠåœ¨æ–‡ä»¶ä½œç”¨åŸŸå†…定义的所有数æ®éƒ½åº”声明
+为static。例如,在一个å为“xxxâ€çš„LLD中的sdev_init()函数定
+义如下:
+``static int xxx_sdev_init(struct scsi_device * sdev) { /* code */ }``
+
+çƒ­æ’æ‹”åˆå§‹åŒ–模型
+================
+在该模型中,底层驱动(LLD)控制SCSI主机适é…器在å­ç³»ç»Ÿä¸­çš„æ³¨
+册与注销时机。主机最早å¯ä»¥åœ¨é©±åЍåˆå§‹åŒ–阶段被注册,最晚å¯ä»¥åœ¨
+驱动å¸è½½æ—¶è¢«ç§»é™¤ã€‚通常,驱动会å“应æ¥è‡ªsysfs probe()的回调,
+表示已检测到一个主机总线适é…器(HBA)。在确认该新设备是LLDçš„
+目标设备åŽï¼ŒLLDåˆå§‹åŒ–HBA,并将一个新的SCSI主机适é…器注册到
+SCSI中间层。
+
+在LLDåˆå§‹åŒ–过程中,驱动应当å‘其期望å‘现HBAçš„IO总线(例如PCI
+总线)进行注册。该æ“作通常å¯ä»¥é€šè¿‡sysfs完æˆã€‚ä»»ä½•é©±åŠ¨å‚æ•°ï¼ˆ
+特别是那些在驱动加载åŽä»å¯ä¿®æ”¹çš„傿•°ï¼‰ä¹Ÿå¯ä»¥åœ¨æ­¤æ—¶é€šè¿‡sysfs
+注册。当LLD注册其首个HBA时,SCSI中间层首次感å—到该LLD的存在。
+
+在ç¨åŽçš„æŸä¸ªæ—¶é—´ç‚¹ï¼Œå½“LLD检测到新的HBA时,接下æ¥åœ¨LLD与SCSI
+中间层之间会å‘生一系列典型的调用过程。该示例展示了中间层如何
+æ‰«ææ–°å¼•入的HBA,在该过程中å‘现了3个SCSIè®¾å¤‡ï¼Œå…¶ä¸­åªæœ‰å‰ä¸¤ä¸ª
+设备有å“应::
+
+ HBA探测:å‡è®¾åœ¨æ‰«æä¸­å‘现2个SCSI设备
+ 底层驱动 中间层 底层驱动
+ =======---------------======---------------=======
+ scsi_host_alloc() -->
+ scsi_add_host() ---->
+ scsi_scan_host() -------+
+ |
+ sdev_init()
+ sdev_configure() --> scsi_change_queue_depth()
+ |
+ sdev_init()
+ sdev_configure()
+ |
+ sdev_init() ***
+ sdev_destroy() ***
+
+
+ *** 对于SCSI中间层å°è¯•扫æä½†æœªå“应的SCSI设备,系统调用
+ sdev_init()和sdev_destroy()函数对。
+
+如果LLD期望调整默认队列设置,å¯ä»¥åœ¨å…¶sdev_configure()例程
+中调用scsi_change_queue_depth()。
+
+当移除一个HBA时,å¯èƒ½æ˜¯ç”±äºŽå¸è½½LLD模å—相关的有åºå…³é—­ï¼ˆä¾‹å¦‚通
+过rmmod命令),也å¯èƒ½æ˜¯ç”±äºŽsysfsçš„remove()回调而触å‘的“热拔
+æ’â€äº‹ä»¶ã€‚æ— è®ºå“ªç§æƒ…况,其执行顺åºéƒ½æ˜¯ç›¸åŒçš„::
+
+ HBA移除:å‡è®¾è¿žæŽ¥äº†2个SCSI设备
+ 底层驱动 中间层 底层驱动
+ =======---------------------======-----------------=======
+ scsi_remove_host() ---------+
+ |
+ sdev_destroy()
+ sdev_destroy()
+ scsi_host_put()
+
+LLD用于跟踪struct Scsi_Host的实例å¯èƒ½ä¼šéžå¸¸æœ‰ç”¨
+(scsi_host_alloc()返回的指针)。这些实例由中间层“拥有â€ã€‚
+当引用计数为零时,struct Scsi_Host实例会被
+scsi_host_put()释放。
+
+HBAçš„çƒ­æ’æ‹”是一个特殊的场景,特别是当HBA下的ç£ç›˜æ­£åœ¨å¤„ç†å·²æŒ‚
+载文件系统上的SCSI命令时。为了应对其中的诸多问题,中间层引入
+了引用计数逻辑。具体内容å‚考下文关于引用计数的章节。
+
+çƒ­æ’æ‹”æ¦‚å¿µåŒæ ·é€‚用于SCSI设备。目å‰ï¼Œå½“添加HBA时,
+scsi_scan_host() 函数会扫æè¯¥HBA所属SCSI传输通é“上的设备。在
+æ–°åž‹SCSI传输å议中,HBAå¯èƒ½åœ¨æ‰«æå®ŒæˆåŽæ‰æ£€æµ‹åˆ°æ–°çš„SCSI设备。
+LLDå¯é€šè¿‡ä»¥ä¸‹æ­¥éª¤é€šçŸ¥ä¸­é—´å±‚æ–°SCSI设备的存在::
+
+ SCSIè®¾å¤‡çƒ­æ’æ‹”
+ 底层驱动 中间层 底层驱动
+ =======-------------------======-----------------=======
+ scsi_add_device() ------+
+ |
+ sdev_init()
+ sdev_configure() [--> scsi_change_queue_depth()]
+
+类似的,LLDå¯èƒ½ä¼šæ„ŸçŸ¥åˆ°æŸä¸ªSCSI设备已ç»è¢«ç§»é™¤ï¼ˆæ‹”出)或与他的连
+接已中断。æŸäº›çŽ°æœ‰çš„SCSI传输å议(例如SPI)å¯èƒ½ç›´åˆ°åŽç»­SCSI命令
+执行失败时æ‰ä¼šå‘现设备已ç»è¢«ç§»é™¤ï¼Œä¸­é—´å±‚会将该设备设置为离线状æ€ã€‚
+è‹¥LLD检测到SCSI设备已ç»è¢«ç§»é™¤ï¼Œå¯é€šè¿‡ä»¥ä¸‹æµç¨‹è§¦å‘上层对该设备的
+移除æ“作::
+
+ SCSI设备热拔æ’
+ 底层驱动 中间层 底层驱动
+ =======-------------------======-----------------=======
+ scsi_remove_device() -------+
+ |
+ sdev_destroy()
+
+对于LLD而言,跟踪struct scsi_device实例å¯èƒ½ä¼šéžå¸¸æœ‰ç”¨ï¼ˆè¯¥ç»“æž„
+çš„æŒ‡é’ˆä¼šä½œä¸ºå‚æ•°ä¼ é€’ç»™sdev_init()å’Œsdev_configure()回调函数)。
+这些实例的所有æƒå½’属于中间层(mid-level)。struct scsi_device
+实例在sdev_destroy()执行åŽé‡Šæ”¾ã€‚
+
+引用计数
+========
+Scsi_Host结构体已引入引用计数机制。该机制将struct Scsi_Host
+实例的所有æƒåˆ†æ•£åˆ°ä½¿ç”¨ä»–çš„å„SCSI层,而此å‰è¿™ç±»å®žä¾‹å®Œå…¨ç”±ä¸­é—´
+层独å ç®¡ç†ã€‚底层驱动(LLD)通常无需直接æ“作这些引用计数,仅在
+æŸäº›ç‰¹å®šåœºæ™¯ä¸‹å¯èƒ½éœ€è¦ä»‹å…¥ã€‚
+
+与struct Scsi_Hostç›¸å…³çš„å¼•ç”¨è®¡æ•°å‡½æ•°ä¸»è¦æœ‰ä»¥ä¸‹3ç§ï¼š
+
+ - scsi_host_alloc():
+ è¿”å›žæŒ‡å‘æ–°å®žä¾‹çš„æŒ‡é’ˆï¼Œè¯¥å®žä¾‹çš„引用计数被设置为1。
+
+ - scsi_host_get():
+ 给定实例的引用计数加1。
+
+ - scsi_host_put():
+ 给定实例的引用计数å‡1。如果引用计数å‡å°‘到0,则释放该实例。
+
+scsi_device结构体现已引入引用计数机制。该机制将
+struct scsi_device实例的所有æƒåˆ†æ•£åˆ°ä½¿ç”¨ä»–çš„å„SCSI层,而此
+å‰è¿™ç±»å®žä¾‹å®Œå…¨ç”±ä¸­é—´å±‚独å ç®¡ç†ã€‚相关访问函数声明详è§
+include/scsi/scsi_device.h文件末尾部分。若LLD需è¦ä¿ç•™
+scsi_device实例的指针副本,则应调用scsi_device_get()增加其
+引用计数;ä¸å†éœ€è¦è¯¥æŒ‡é’ˆæ—¶ï¼Œå¯é€šè¿‡scsi_device_put()递å‡å¼•用
+计数(该æ“作å¯èƒ½ä¼šå¯¼è‡´è¯¥å®žä¾‹è¢«é‡Šæ”¾ï¼‰ã€‚
+
+.. Note::
+
+ struct Scsi_Host实际上包å«ä¸¤ä¸ªå¹¶è¡Œç»´æŠ¤çš„引用计数器,该引
+ ç”¨è®¡æ•°ç”±è¿™äº›å‡½æ•°å…±åŒæ“作。
+
+ç¼–ç è§„范
+========
+
+首先,Linus Torvalds关于C语言编ç é£Žæ ¼çš„观点å¯ä»¥åœ¨
+Documentation/process/coding-style.rst文件中找到。
+
+此外,在相关gcc编译器支æŒçš„å‰æä¸‹ï¼Œé¼“åŠ±ä½¿ç”¨å¤§å¤šæ•°C99标准的增强
+特性。因此,在适当的情况下鼓励使用C99风格的结构体和数组åˆå§‹åŒ–
+æ–¹å¼ã€‚但ä¸è¦è¿‡åº¦ä½¿ç”¨ï¼Œç›®å‰å¯¹å¯å˜é•¿åº¦æ•°ç»„(VLA)的支æŒè¿˜å¾…完善。
+一个例外是 ``//`` 风格的注释;在Linux中倾å‘于使
+用 ``/*...*/`` 注释格å¼ã€‚
+
+对于编写良好ã€ç»è¿‡å……分测试且有完整文档的代ç ä¸éœ€è¦é‡æ–°æ ¼å¼åŒ–
+以符åˆä¸Šè¿°è§„范。例如,aic7xxx驱动是从FreeBSDå’ŒAdaptec代ç åº“
+ç§»æ¤åˆ°Linux的。毫无疑问,FreeBSDå’ŒAdaptecéµå¾ªå…¶åŽŸæœ‰çš„ç¼–ç è§„
+范。
+
+
+中间层æä¾›çš„函数
+================
+这些函数由SCSI中间层æä¾›ï¼Œä¾›åº•层驱动(LLD)调用。这些函数的å
+称(å³å…¥å£ç‚¹ï¼‰å‡å·²å¯¼å‡ºï¼Œå› æ­¤ä½œä¸ºæ¨¡å—加载的LLDå¯ä»¥è®¿é—®ä»–们。内
+核会确ä¿åœ¨ä»»ä½•LLDåˆå§‹åŒ–之å‰ï¼ŒSCSI中间层已先行加载并完æˆåˆå§‹åŒ–。
+下文按字æ¯é¡ºåºåˆ—出这些函数,其åç§°å‡ä»¥ ``scsi_`` 开头。
+
+摘è¦ï¼š
+
+ - scsi_add_device - 创建新的SCSI逻辑å•元(LU)设备实例
+ - scsi_add_host - 执行sysfs注册并设置传输类
+ - scsi_change_queue_depth - 调整SCSI设备队列深度
+ - scsi_bios_ptable - 返回å—设备分区表的副本
+ - scsi_block_requests - é˜»æ­¢å‘æŒ‡å®šä¸»æœºæäº¤æ–°å‘½ä»¤
+ - scsi_host_alloc - 分é…引用计数为1的新SCSI主机适é…器实例scsi_host
+ - scsi_host_get - 增加SCSI主机适é…器实例的引用计数
+ - scsi_host_put - å‡å°‘SCSI主机适é…器的引用计数(归零时释放)
+ - scsi_remove_device - å¸è½½å¹¶ç§»é™¤SCSI设备
+ - scsi_remove_host - å¸è½½å¹¶ç§»é™¤ä¸»æœºæŽ§åˆ¶å™¨ä¸‹çš„æ‰€æœ‰SCSI设备
+ - scsi_report_bus_reset - 报告检测到的SCSI总线å¤ä½äº‹ä»¶
+ - scsi_scan_host - 执行SCSI总线扫æ
+ - scsi_track_queue_full - 跟踪连续出现的队列满事件
+ - scsi_unblock_requests - æ¢å¤å‘指定主机æäº¤å‘½ä»¤
+
+详细信æ¯::
+
+ /**
+ * scsi_add_device - 创建新的SCSI逻辑å•元(LU)设备实例
+ * @shost: 指å‘SCSI主机适é…器实例的指针
+ * @channel: 通é“å·ï¼ˆé€šå¸¸ä¸º0)
+ * @id: 目标IDå·
+ * @lun: 逻辑å•å…ƒå·ï¼ˆLUN)
+ *
+ * è¿”å›žæŒ‡å‘æ–°çš„struct scsi_device实例的指针,
+ * å¦‚æžœå‡ºçŽ°å¼‚å¸¸ï¼ˆä¾‹å¦‚åœ¨ç»™å®šåœ°å€æ²¡æœ‰è®¾å¤‡å“应),则返
+ * 回ERR_PTR(-ENODEV)
+ *
+ * 是å¦é˜»å¡žï¼šæ˜¯
+ *
+ * 注æ„事项:本函数通常在添加HBAçš„SCSI总线扫æè¿‡ç¨‹
+ * 中由系统内部调用(å³scsi_scan_host()执行期间)。因此,
+ * 仅应在以下情况调用:HBA在scsi_scan_host()å®Œæˆæ‰«æåŽï¼Œ
+ * åˆæ£€æµ‹åˆ°æ–°çš„SCSI设备(逻辑å•元)。若æˆåŠŸæ‰§è¡Œï¼Œæœ¬æ¬¡è°ƒç”¨
+ * å¯èƒ½ä¼šè§¦å‘LLD的以下回调函数:sdev_init()以åŠ
+ * sdev_configure()
+ *
+ * 函数定义:drivers/scsi/scsi_scan.c
+ **/
+ struct scsi_device * scsi_add_device(struct Scsi_Host *shost,
+ unsigned int channel,
+ unsigned int id, unsigned int lun)
+
+
+ /**
+ * scsi_add_host - 执行sysfs注册并设置传输类
+ * @shost: 指å‘SCSI主机适é…器实例的指针
+ * @dev: 指å‘scsi类设备结构体(struct device)的指针
+ *
+ * æˆåŠŸè¿”å›ž0,失败返回负的errno(例如:-ENOMEM)
+ *
+ * 是å¦é˜»å¡žï¼šå¦
+ *
+ * 注æ„äº‹é¡¹ï¼šä»…åœ¨â€œçƒ­æ’æ‹”åˆå§‹åŒ–模型â€ä¸­éœ€è¦è°ƒç”¨ï¼Œä¸”必须在
+ * scsi_host_alloc()æˆåŠŸæ‰§è¡ŒåŽè°ƒç”¨ã€‚该函数ä¸ä¼šæ‰«ææ€»çº¿ï¼›
+ * 总线扫æå¯é€šè¿‡è°ƒç”¨scsi_scan_host()或其他传输层特定的
+ * 方法完æˆã€‚在调用该函数之å‰ï¼ŒLLD必须先设置好传输模æ¿ï¼Œ
+ * 并且åªèƒ½åœ¨è°ƒç”¨è¯¥å‡½æ•°ä¹‹åŽæ‰èƒ½è®¿é—®ä¼ è¾“ç±»
+ * (transport class)相关的数æ®ç»“构。
+ *
+ * 函数定义:drivers/scsi/hosts.c
+ **/
+ int scsi_add_host(struct Scsi_Host *shost, struct device * dev)
+
+
+ /**
+ * scsi_change_queue_depth - 调整SCSI设备队列深度
+ * @sdev: 指å‘è¦æ›´æ”¹é˜Ÿåˆ—深度的SCSI设备的指针
+ * @tags 如果å¯ç”¨äº†æ ‡è®°é˜Ÿåˆ—,则表示å…许的标记数,
+ * æˆ–è€…åœ¨éžæ ‡è®°æ¨¡å¼ä¸‹ï¼ŒLLDå¯ä»¥æŽ’队的命令
+ * 数(如 cmd_per_lun)。
+ *
+ * 无返回
+ *
+ * 是å¦é˜»å¡žï¼šå¦
+ *
+ * 注æ„事项:å¯ä»¥åœ¨ä»»ä½•时刻调用该函数,åªè¦è¯¥SCSI设备å—该LLD控
+ * 制。[具体æ¥è¯´ï¼Œå¯ä»¥åœ¨sdev_configure()执行期间或之åŽï¼Œä¸”在
+ * sdev_destroy()执行之å‰è°ƒç”¨ã€‚] 该函数å¯å®‰å…¨åœ°åœ¨ä¸­æ–­ä¸Šä¸‹æ–‡ä¸­
+ * 调用。
+ *
+ * 函数定义:drivers/scsi/scsi.c [更多注释请å‚考æºä»£ç ]
+ **/
+ int scsi_change_queue_depth(struct scsi_device *sdev, int tags)
+
+
+ /**
+ * scsi_bios_ptable - 返回å—设备分区表的副本
+ * @dev: 指å‘å—设备的指针
+ *
+ * 返回指å‘分区表的指针,失败返回NULL
+ *
+ * 是å¦é˜»å¡žï¼šæ˜¯
+ *
+ * 注æ„事项:调用方负责释放返回的内存(通过 kfree() 释放)
+ *
+ * 函数定义:drivers/scsi/scsicam.c
+ **/
+ unsigned char *scsi_bios_ptable(struct block_device *dev)
+
+
+ /**
+ * scsi_block_requests - é˜»æ­¢å‘æŒ‡å®šä¸»æœºæäº¤æ–°å‘½ä»¤
+ *
+ * @shost: 指å‘特定主机的指针,用于阻止命令的å‘é€
+ *
+ * 无返回
+ *
+ * 是å¦é˜»å¡žï¼šå¦
+ *
+ * 注æ„事项:没有定时器或其他任何机制å¯ä»¥è§£é™¤é˜»å¡žï¼Œå”¯ä¸€çš„æ–¹å¼
+ * 是由LLD调用scsi_unblock_requests()æ–¹å¯æ¢å¤ã€‚
+ *
+ * 函数定义:drivers/scsi/scsi_lib.c
+ **/
+ void scsi_block_requests(struct Scsi_Host * shost)
+
+
+ /**
+ * scsi_host_alloc - 创建SCSI主机适é…器实例并执行基础åˆå§‹åŒ–
+ * @sht: 指å‘SCSI主机模æ¿çš„æŒ‡é’ˆ
+ * @privsize: 在hostdata数组中分é…çš„é¢å¤–字节数(该数组是返
+ * 回的Scsi_Host实例的最åŽä¸€ä¸ªæˆå‘˜ï¼‰
+ *
+ * è¿”å›žæŒ‡å‘æ–°çš„Scsi_Host实例的指针,失败返回NULL
+ *
+ * 是å¦é˜»å¡žï¼šæ˜¯
+ *
+ * 注æ„事项:当此调用返回给LLD时,该主机适é…器上的
+ * SCSI总线扫æå°šæœªè¿›è¡Œã€‚hostdata数组(默认长度为
+ * 零)是LLD专属的æ¯ä¸»æœºç§æœ‰åŒºåŸŸï¼Œä¾›LLD独å ä½¿ç”¨ã€‚
+ * 两个相关的引用计数都被设置为1。完整的注册(ä½äºŽ
+ * sysfs)与总线扫æç”±scsi_add_host()å’Œ
+ * scsi_scan_host()ç¨åŽæ‰§è¡Œã€‚
+ * 函数定义:drivers/scsi/hosts.c
+ **/
+ struct Scsi_Host * scsi_host_alloc(const struct scsi_host_template * sht,
+ int privsize)
+
+
+ /**
+ * scsi_host_get - 增加SCSI主机适é…器实例的引用计数
+ * @shost: 指å‘Scsi_Host实例的指针
+ *
+ * 无返回
+ *
+ * 是å¦é˜»å¡žï¼šç›®å‰å¯èƒ½ä¼šé˜»å¡žï¼Œä½†å¯èƒ½è¿­ä»£ä¸ºä¸é˜»å¡ž
+ *
+ * 注æ„äº‹é¡¹ï¼šä¼šåŒæ—¶å¢žåŠ struct Scsi_Host中两个å­å¯¹
+ * 象的引用计数
+ *
+ * 函数定义:drivers/scsi/hosts.c
+ **/
+ void scsi_host_get(struct Scsi_Host *shost)
+
+
+ /**
+ * scsi_host_put - å‡å°‘SCSI主机适é…器实例的引用计数
+ * (归零时释放)
+ * @shost: 指å‘Scsi_Host实例的指针
+ *
+ * 无返回
+ *
+ * 是å¦é˜»å¡žï¼šå½“å‰å¯èƒ½ä¼šé˜»å¡žï¼Œä½†å¯èƒ½ä¼šæ”¹ä¸ºä¸é˜»å¡ž
+ *
+ * 注æ„事项:实际会递å‡ä¸¤ä¸ªå­å¯¹è±¡ä¸­çš„计数。当åŽä¸€ä¸ªå¼•用
+ * 计数归零时系统会自动释放Scsi_Host实例。
+ * LLD 无需关注Scsi_Host实例的具体释放时机,åªè¦åœ¨å¹³è¡¡
+ * 引用计数使用åŽä¸å†è®¿é—®è¯¥å®žä¾‹å³å¯ã€‚
+ * 函数定义:drivers/scsi/hosts.c
+ **/
+ void scsi_host_put(struct Scsi_Host *shost)
+
+
+ /**
+ * scsi_remove_device - å¸è½½å¹¶ç§»é™¤SCSI设备
+ * @sdev: 指å‘SCSI设备实例的指针
+ *
+ * 返回值:æˆåŠŸè¿”å›ž0,若设备未连接,则返回-EINVAL
+ *
+ * 是å¦é˜»å¡žï¼šæ˜¯
+ *
+ * 如果LLDå‘现æŸä¸ªSCSI设备(逻辑å•元,lu)已ç»è¢«ç§»é™¤ï¼Œ
+ * 但其主机适é…å™¨å®žä¾‹ä¾æ—§å­˜åœ¨ï¼Œåˆ™å¯ä»¥è¯·æ±‚移除该SCSI设备。
+ * 如果该调用æˆåŠŸå°†è§¦å‘sdev_destroy()回调函数的执行。调
+ * 用完æˆåŽï¼Œsdevå°†å˜æˆä¸€ä¸ªæ— æ•ˆçš„æŒ‡é’ˆã€‚
+ *
+ * 函数定义:drivers/scsi/scsi_sysfs.c
+ **/
+ int scsi_remove_device(struct scsi_device *sdev)
+
+
+ /**
+ * scsi_remove_host - å¸è½½å¹¶ç§»é™¤ä¸»æœºæŽ§åˆ¶å™¨ä¸‹çš„æ‰€æœ‰SCSI设备
+ * @shost: 指å‘SCSI主机适é…器实例的指针
+ *
+ * 返回值:æˆåŠŸè¿”å›ž0,失败返回1(例如:LLD正忙??)
+ *
+ * 是å¦é˜»å¡žï¼šæ˜¯
+ *
+ * 注æ„äº‹é¡¹ï¼šä»…åœ¨ä½¿ç”¨â€œçƒ­æ’æ‹”åˆå§‹åŒ–æ¨¡åž‹â€æ—¶è°ƒç”¨ã€‚应在调用
+ * scsi_host_put()å‰è°ƒç”¨ã€‚
+ *
+ * 函数定义:drivers/scsi/hosts.c
+ **/
+ int scsi_remove_host(struct Scsi_Host *shost)
+
+
+ /**
+ * scsi_report_bus_reset - 报告检测到的SCSI总线å¤ä½äº‹ä»¶
+ * @shost: 指å‘å…³è”çš„SCSI主机适é…器的指针
+ * @channel: å‘生SCSI总线å¤ä½çš„通é“å·
+ *
+ * 返回值:无
+ *
+ * 是å¦é˜»å¡žï¼šå¦
+ *
+ * 注æ„事项:仅当å¤ä½æ¥è‡ªæœªçŸ¥æ¥æºæ—¶æ‰éœ€è°ƒç”¨æ­¤å‡½æ•°ã€‚
+ * ç”±SCSI中间层å‘èµ·çš„å¤ä½æ— éœ€è°ƒç”¨ï¼Œä½†è°ƒç”¨ä¹Ÿä¸ä¼šå¯¼
+ * 致副作用。此函数的主è¦ä½œç”¨æ˜¯ç¡®ä¿ç³»ç»Ÿèƒ½æ­£ç¡®å¤„ç†
+ * CHECK_CONDITION状æ€ã€‚
+ *
+ * 函数定义:drivers/scsi/scsi_error.c
+ **/
+ void scsi_report_bus_reset(struct Scsi_Host * shost, int channel)
+
+
+ /**
+ * scsi_scan_host - 执行SCSI总线扫æ
+ * @shost: 指å‘SCSI主机适é…器实例的指针
+ *
+ * 是å¦é˜»å¡žï¼šæ˜¯
+ *
+ * 注æ„事项:应在调用scsi_add_host()åŽè°ƒç”¨
+ *
+ * 函数定义:drivers/scsi/scsi_scan.c
+ **/
+ void scsi_scan_host(struct Scsi_Host *shost)
+
+
+ /**
+ * scsi_track_queue_full - 跟踪指定设备上连续的QUEUE_FULL
+ * 事件,以判断是å¦éœ€è¦åŠä½•时调整
+ * 该设备的队列深度。
+ * @sdev: 指å‘SCSI设备实例的指针
+ * @depth: 当å‰è¯¥è®¾å¤‡ä¸Šæœªå®Œæˆçš„SCSI命令数é‡ï¼ˆä¸åŒ…括返回
+ * QUEUE_FULL的命令)
+ *
+ * 返回值:0 - 当å‰é˜Ÿåˆ—深度无需调整
+ * >0 - 需è¦å°†é˜Ÿåˆ—深度调整为此返回值指定的新深度
+ * -1 - 需è¦å›žé€€åˆ°éžæ ‡è®°æ“作模å¼ï¼Œå¹¶ä½¿ç”¨
+ * host->cmd_per_lunä½œä¸ºéžæ ‡è®°å‘½ä»¤é˜Ÿåˆ—çš„
+ * 深度é™åˆ¶
+ *
+ * 是å¦é˜»å¡žï¼šå¦
+ *
+ * 注æ„事项:LLDå¯ä»¥åœ¨ä»»æ„时刻调用该函数。系统将自动执行“正确
+ * çš„å¤„ç†æµç¨‹â€ï¼›è¯¥å‡½æ•°æ”¯æŒåœ¨ä¸­æ–­ä¸Šä¸‹æ–‡ä¸­å®‰å…¨åœ°è°ƒç”¨
+ *
+ * 函数定义:drivers/scsi/scsi.c
+ **/
+ int scsi_track_queue_full(struct scsi_device *sdev, int depth)
+
+
+ /**
+ * scsi_unblock_requests - æ¢å¤å‘指定主机适é…器æäº¤å‘½ä»¤
+ *
+ * @shost: 指å‘è¦è§£é™¤é˜»å¡žçš„主机适é…器的指针
+ *
+ * 返回值:无
+ *
+ * 是å¦é˜»å¡žï¼šå¦
+ *
+ * 函数定义:drivers/scsi/scsi_lib.c
+ **/
+ void scsi_unblock_requests(struct Scsi_Host * shost)
+
+
+
+接å£å‡½æ•°
+========
+接å£å‡½æ•°ç”±åº•层驱动(LLD)定义实现,其函数指针ä¿å­˜åœ¨
+struct scsi_host_template实例中,并将该实例传递给
+scsi_host_alloc()。
+部分接å£å‡½æ•°ä¸ºå¿…选实现项。所有
+接å£å‡½æ•°éƒ½åº”声明为static,约定俗æˆçš„命å规则如下,
+驱动“xyzâ€åº”将其sdev_configure()函数声明为::
+
+ static int xyz_sdev_configure(struct scsi_device * sdev);
+
+其余接å£å‡½æ•°çš„命å规范å‡ä¾æ­¤ç±»æŽ¨ã€‚
+
+需将该函数指针赋值给“struct scsi_host_templateâ€å®žä¾‹
+的‘sdev_configure’æˆå‘˜å˜é‡ä¸­ï¼Œå¹¶å°†è¯¥ç»“构体实例指针传
+递到中间层的scsi_host_alloc()函数。
+
+å„个接å£å‡½æ•°çš„详细说明å¯å‚考include/scsi/scsi_host.h
+文件,具体æè¿°ä½äºŽâ€œstruct scsi_host_templateâ€ç»“构体
+å„个æˆå‘˜çš„上方。在æŸäº›æƒ…况下,scsi_host.h头文件中的æ
+述比本文æä¾›çš„æ›´ä¸ºè¯¦å°½ã€‚
+
+以下按字æ¯é¡ºåºåˆ—出所有接å£å‡½æ•°åŠå…¶è¯´æ˜Žã€‚
+
+摘è¦ï¼š
+
+ - bios_param - 获å–ç£ç›˜çš„ç£å¤´/扇区/柱é¢å‚æ•°
+ - eh_timed_out - SCSI命令超时回调
+ - eh_abort_handler - 中止指定的SCSI命令
+ - eh_bus_reset_handler - 触å‘SCSI总线å¤ä½
+ - eh_device_reset_handler - 执行SCSI设备å¤ä½
+ - eh_host_reset_handler - å¤ä½ä¸»æœºï¼ˆä¸»æœºæ€»çº¿é€‚é…器)
+ - info - æä¾›æŒ‡å®šä¸»æœºé€‚é…器的相关信æ¯
+ - ioctl - 驱动å¯å“应ioctl控制命令
+ - proc_info - 支æŒ/proc/scsi/{驱动å}/{主机å·}文件节点的读写æ“作
+ - queuecommand - å°†SCSI命令æäº¤åˆ°ä¸»æœºæŽ§åˆ¶å™¨ï¼Œå‘½ä»¤æ‰§è¡Œå®ŒæˆåŽè°ƒç”¨â€˜done’回调
+ - sdev_init - åœ¨å‘æ–°è®¾å¤‡å‘é€SCSI命令å‰çš„åˆå§‹åŒ–
+ - sdev_configure - 设备挂载åŽçš„精细化微调
+ - sdev_destroy - 设备å³å°†è¢«ç§»é™¤å‰çš„æ¸…ç†
+
+
+详细信æ¯::
+
+ /**
+ * bios_param - 获å–ç£ç›˜çš„ç£å¤´/扇区/柱é¢å‚æ•°
+ * @sdev: 指å‘SCSI设备实例的指针(定义于
+ * include/scsi/scsi_device.h中)
+ * @bdev: 指å‘å—设备实例的指针(定义于fs.h中)
+ * @capacity: 设备容é‡ï¼ˆä»¥512字节扇区为å•ä½ï¼‰
+ * @params: 三元数组用于ä¿å­˜è¾“出结果:
+ * params[0]:ç£å¤´æ•°é‡ï¼ˆæœ€å¤§255)
+ * params[1]:扇区数é‡ï¼ˆæœ€å¤§63)
+ * params[2]ï¼šæŸ±é¢æ•°é‡
+ *
+ * 返回值:被忽略
+ *
+ * å¹¶å‘安全声明: æ— é”
+ *
+ * 调用上下文说明: 进程上下文(sd)
+ *
+ * 注æ„事项: 若未æä¾›æ­¤å‡½æ•°ï¼Œç³»ç»Ÿå°†åŸºäºŽREAD CAPACITY
+ * ä½¿ç”¨é»˜è®¤å‡ ä½•å‚æ•°ã€‚params数组已预åˆå§‹åŒ–伪值,防止函
+ * 数无输出。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int bios_param(struct scsi_device * sdev, struct block_device *bdev,
+ sector_t capacity, int params[3])
+
+
+ /**
+ * eh_timed_out - SCSI命令超时回调
+ * @scp: 标识超时的命令
+ *
+ * 返回值:
+ *
+ * EH_HANDLED: 我已修å¤è¯¥é”™è¯¯ï¼Œè¯·ç»§ç»­å®Œæˆè¯¥å‘½ä»¤
+ * EH_RESET_TIMER: æˆ‘éœ€è¦æ›´å¤šæ—¶é—´ï¼Œè¯·é‡ç½®å®šæ—¶å™¨å¹¶é‡æ–°å¼€å§‹è®¡æ—¶
+ * EH_NOT_HANDLED 开始正常的错误æ¢å¤æµç¨‹
+ *
+ * å¹¶å‘安全声明: æ— é”
+ *
+ * 调用上下文说明: 中断上下文
+ *
+ * 注æ„事项: 该回调函数为LLDæä¾›ä¸€ä¸ªæœºä¼šè¿›è¡Œæœ¬åœ°
+ * 错误æ¢å¤å¤„ç†ã€‚此处的æ¢å¤ä»…é™äºŽåˆ¤æ–­è¯¥æœªå®Œæˆçš„命
+ * 令是å¦è¿˜æœ‰å¯èƒ½å®Œæˆã€‚此回调中ä¸å…è®¸ä¸­æ­¢æˆ–é‡æ–°å¯
+ * 动该命令。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int eh_timed_out(struct scsi_cmnd * scp)
+
+
+ /**
+ * eh_abort_handler - 中止指定的SCSI命令
+ * @scp: 标识è¦ä¸­æ­¢çš„命令
+ *
+ * 返回值:如果命令æˆåŠŸä¸­æ­¢ï¼Œåˆ™è¿”å›žSUCCESS,å¦åˆ™è¿”回FAILED
+ *
+ * å¹¶å‘安全声明: æ— é”
+ *
+ * 调用上下文说明: 内核线程
+ *
+ * 注æ„事项: 该函数仅在命令超时时æ‰è¢«è°ƒç”¨ã€‚
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int eh_abort_handler(struct scsi_cmnd * scp)
+
+
+ /**
+ * eh_bus_reset_handler - å‘èµ·SCSI总线å¤ä½
+ * @scp: 包å«è¯¥è®¾å¤‡çš„SCSI总线应进行é‡ç½®
+ *
+ * 返回值:é‡ç½®æˆåŠŸè¿”å›žSUCCESSï¼›å¦åˆ™è¿”回FAILED
+ *
+ * å¹¶å‘安全声明: æ— é”
+ *
+ * 调用上下文说明: 内核线程
+ *
+ * 注æ„事项: ç”±SCSI错误处ç†çº¿ç¨‹ï¼ˆscsi_eh)调用。
+ * åœ¨é”™è¯¯å¤„ç†æœŸé—´ï¼Œå½“å‰ä¸»æœºé€‚é…器的所有IO请求å‡
+ * 被阻塞。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int eh_bus_reset_handler(struct scsi_cmnd * scp)
+
+
+ /**
+ * eh_device_reset_handler - å‘èµ·SCSI设备å¤ä½
+ * @scp: 指定将被é‡ç½®çš„SCSI设备
+ *
+ * 返回值:如果命令æˆåŠŸä¸­æ­¢è¿”å›žSUCCESS,å¦åˆ™è¿”回FAILED
+ *
+ * å¹¶å‘安全声明: æ— é”
+ *
+ * 调用上下文说明: 内核线程
+ *
+ * 注æ„事项: ç”±SCSI错误处ç†çº¿ç¨‹ï¼ˆscsi_eh)调用。
+ * åœ¨é”™è¯¯å¤„ç†æœŸé—´ï¼Œå½“å‰ä¸»æœºé€‚é…器的所有IO请求å‡
+ * 被阻塞。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int eh_device_reset_handler(struct scsi_cmnd * scp)
+
+
+ /**
+ * eh_host_reset_handler - å¤ä½ä¸»æœºï¼ˆä¸»æœºæ€»çº¿é€‚é…器)
+ * @scp: 管ç†è¯¥è®¾å¤‡çš„SCSI主机适é…器应该被é‡ç½®
+ *
+ * 返回值:如果命令æˆåŠŸä¸­æ­¢è¿”å›žSUCCESS,å¦åˆ™è¿”回FAILED
+ *
+ * å¹¶å‘安全声明: æ— é”
+ *
+ * 调用上下文说明: 内核线程
+ *
+ * 注æ„事项: ç”±SCSI错误处ç†çº¿ç¨‹ï¼ˆscsi_eh)调用。
+ * åœ¨é”™è¯¯å¤„ç†æœŸé—´ï¼Œå½“å‰ä¸»æœºé€‚é…器的所有IO请求å‡
+ * 被阻塞。当使用默认的eh_strategy策略时,如果
+ * _abort_ã€_device_reset_ã€_bus_reset_和该处
+ * ç†å‡½æ•°å‡æœªå®šä¹‰ï¼ˆæˆ–全部返回FAILED),系统强制
+ * 该故障设备处于离线状æ€
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int eh_host_reset_handler(struct scsi_cmnd * scp)
+
+
+ /**
+ * info - æä¾›ç»™å®šä¸»æœºé€‚é…器的详细信æ¯ï¼šé©±åŠ¨ç¨‹åºåç§°
+ * 以åŠç”¨äºŽåŒºåˆ†ä¸åŒä¸»æœºé€‚é…器的数æ®ç»“æž„
+ * @shp: 指å‘目标主机的struct Scsi_Host实例
+ *
+ * 返回值:返回以NULL结尾的ASCII字符串。[驱动
+ * 负责管ç†è¿”回的字符串所在内存并确ä¿å…¶åœ¨æ•´ä¸ª
+ * 主机适é…器生命周期内有效。]
+ *
+ * å¹¶å‘安全声明: æ— é”
+ *
+ * 调用上下文说明: 进程上下文
+ *
+ * 注æ„事项: 通常æä¾›è¯¸å¦‚I/Oåœ°å€æˆ–中断å·
+ * ç­‰PCI或ISAä¿¡æ¯ã€‚如果未实现该函数,则
+ * 默认使用struct Scsi_Host::name 字段。
+ * 返回的字符串应为å•行(å³ä¸åŒ…嫿¢è¡Œç¬¦ï¼‰ã€‚
+ * 通过SCSI_IOCTL_PROBE_HOST ioctlå¯èŽ·
+ * å–该函数返回的字符串,如果该函数ä¸å¯ç”¨ï¼Œ
+ * 则ioctl返回struct Scsi_Host::name中
+ * 的字符串。
+
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ const char * info(struct Scsi_Host * shp)
+
+
+ /**
+ * ioctl - 驱动å¯å“应ioctl控制命令
+ * @sdp: ioctlæ“作针对的SCSI设备
+ * @cmd: ioctl命令å·
+ * @arg: 指å‘用户空间读写数æ®çš„æŒ‡é’ˆã€‚由于他指å‘用
+ * 户空间,必须使用适当的内核函数
+ * (如 copy_from_user())。按照Unix的风
+ * æ ¼ï¼Œè¯¥å‚æ•°ä¹Ÿå¯ä»¥è§†ä¸ºunsigned long 类型。
+ *
+ * 返回值:如果出错则返回负的“errnoâ€å€¼ã€‚返回0或正值表
+ * 示æˆåŠŸï¼Œå¹¶å°†è¿”å›žå€¼ä¼ é€’ç»™ç”¨æˆ·ç©ºé—´ã€‚
+ *
+ * å¹¶å‘安全声明:无é”
+ *
+ * 调用上下文说明:进程上下文
+ *
+ * 注æ„事项:SCSIå­ç³»ç»Ÿä½¿ç”¨â€œé€å±‚下传
+ * (trickle down)â€çš„ioctl模型。
+ * 用户层会对上层驱动设备节点
+ * (例如/dev/sdc)å‘èµ·ioctl()调用,
+ * 如果上层驱动无法识别该命令,则将其
+ * 传递给SCSI中间层,若中间层也无法识
+ * 别,则å†ä¼ é€’给控制该设备的LLD。
+ * æ ¹æ®æœ€æ–°çš„Unixæ ‡å‡†ï¼Œå¯¹äºŽä¸æ”¯æŒçš„
+ * ioctl()命令,应返回-ENOTTY。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int ioctl(struct scsi_device *sdp, int cmd, void *arg)
+
+
+ /**
+ * proc_info - 支æŒ/proc/scsi/{驱动å}/{主机å·}文件节点的读写æ“作
+ * @buffer: 输入或出的缓冲区锚点(writeto1_read0==0表示å‘buffer写
+ * 入,writeto1_read0==1表示由buffer读å–)
+ * @start: 当writeto1_read0==0时,用于指定驱动实际填充的起始ä½ç½®ï¼›
+ * 当writeto1_read0==1时被忽略。
+ * @offset: 当writeto1_read0==0时,表示用户关注的数æ®åœ¨ç¼“冲区中的
+ * å移。当writeto1_read0==1时忽略。
+ * @length: 缓冲区的最大(或实际使用)长度
+ * @host_no: 目标SCSI Host的编å·ï¼ˆstruct Scsi_Host::host_no)
+ * @writeto1_read0: 1 -> 表示数æ®ä»Žç”¨æˆ·ç©ºé—´å†™å…¥é©±åЍ
+ * (例如,“echo some_string > /proc/scsi/xyz/2â€ï¼‰
+ * 0 -> è¡¨ç¤ºç”¨æˆ·ä»Žé©±åŠ¨è¯»å–æ•°æ®
+ * (例如,“cat /proc/scsi/xyz/2â€ï¼‰
+ *
+ * 返回值:当writeto1_read0==1时返回写入长度。å¦åˆ™ï¼Œ
+ * 返回从offsetå移开始输出到buffer的字符数。
+ *
+ * å¹¶å‘安全声明:无é”
+ *
+ * 调用上下文说明:进程上下文
+ *
+ * 注æ„事项:该函数由scsi_proc.c驱动,与proc_fs交互。
+ * 当å‰SCSIå­ç³»ç»Ÿå¯ç§»é™¤å¯¹proc_fs的支æŒï¼Œç›¸å…³é…置选。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int proc_info(char * buffer, char ** start, off_t offset,
+ int length, int host_no, int writeto1_read0)
+
+
+ /**
+ * queuecommand - å°†SCSI命令æäº¤åˆ°ä¸»æœºæŽ§åˆ¶å™¨ï¼Œå‘½ä»¤æ‰§è¡Œå®ŒæˆåŽè°ƒç”¨scp->scsi_done回调函数
+ * @shost: 指å‘目标SCSI主机控制器
+ * @scp: 指å‘待处ç†çš„SCSI命令
+ *
+ * 返回值:æˆåŠŸè¿”å›ž0。
+ *
+ * 如果å‘生错误,则返回:
+ *
+ * SCSI_MLQUEUE_DEVICE_BUSY表示设备队列满,
+ * SCSI_MLQUEUE_HOST_BUSY表示整个主机队列满
+ *
+ * åœ¨è¿™ä¸¤ç§æƒ…å†µä¸‹ï¼Œä¸­é—´å±‚å°†è‡ªåŠ¨é‡æ–°æäº¤è¯¥I/O请求
+ *
+ * - 若返回SCSI_MLQUEUE_DEVICE_BUSY,则仅暂åœè¯¥
+ * 特定设备的命令处ç†ï¼Œå½“该设备的æŸä¸ªå‘½ä»¤å®Œæˆè¿”回
+ * 时(或在短暂延迟åŽå¦‚果没有其他未完æˆå‘½ä»¤ï¼‰å°†æ¢
+ * å¤å…¶å¤„ç†ã€‚å…¶ä»–è®¾å¤‡çš„å‘½ä»¤ä»æ­£å¸¸ç»§ç»­å¤„ç†ã€‚
+ *
+ * - 若返回SCSI_MLQUEUE_HOST_BUSY,将暂åœè¯¥ä¸»æœº
+ * 的所有I/Oæ“作,当任æ„命令从该主机返回时(或在
+ * 短暂延迟åŽå¦‚果没有其他未完æˆå‘½ä»¤ï¼‰å°†æ¢å¤å¤„ç†ã€‚
+ *
+ * 为了与早期的queuecommand兼容,任何其他返回值
+ * 都被视作SCSI_MLQUEUE_HOST_BUSY。
+ *
+ * 对于其他å¯ç«‹å³æ£€æµ‹åˆ°çš„错误,å¯é€šè¿‡ä»¥ä¸‹æµç¨‹å¤„
+ * ç†ï¼šè®¾ç½®scp->result为适当错误值,调用scp->scsi_done
+ * 回调函数,然åŽè¯¥å‡½æ•°è¿”回0ã€‚è‹¥è¯¥å‘½ä»¤æœªç«‹å³æ‰§è¡Œï¼ˆLLD
+ * 正在å¯åŠ¨æˆ–å°†è¦å¯åŠ¨è¯¥å‘½ä»¤ï¼‰ï¼Œåˆ™åº”å°†scp->resultç½®0å¹¶
+ * 返回0。
+ *
+ * 命令所有æƒè¯´æ˜Žï¼šè‹¥é©±åŠ¨è¿”å›ž0,则表示驱动获得该命令的
+ * 所有æƒï¼Œ
+ * å¹¶å¿…é¡»ç¡®ä¿æœ€ç»ˆæ‰§è¡Œscp->scsi_done回调函数。注æ„:驱动
+ * å¯ä»¥åœ¨è¿”回0之å‰è°ƒç”¨scp->scsi_done,但一旦调用该回
+ * 调函数åŽï¼Œå°±åªèƒ½è¿”回0。若驱动返回éžé›¶å€¼ï¼Œåˆ™ç¦æ­¢åœ¨ä»»ä½•æ—¶
+ * 刻执行该命令的scsi_done回调函数。
+ *
+ * å¹¶å‘安全声明:在2.6.36åŠæ›´æ—©çš„å†…æ ¸ç‰ˆæœ¬ä¸­ï¼Œè°ƒç”¨è¯¥å‡½æ•°æ—¶æŒæœ‰
+ * struct Scsi_Host::host_locké”(通过“irqsaveâ€èŽ·å–中断安全的自旋é”),
+ * 并且返回时ä»éœ€ä¿æŒè¯¥é”;从Linux 2.6.37开始,queuecommand
+ * 将在无é”状æ€ä¸‹è¢«è°ƒç”¨ã€‚
+ *
+ * 调用上下文说明:在中断(软中断)或进程上下文中
+ *
+ * 注æ„事项:该函数执行应当éžå¸¸å¿«é€Ÿï¼Œé€šå¸¸ä¸ä¼šç­‰å¾…I/O
+ * 完æˆã€‚å› æ­¤scp->scsi_done回调函数通常会在该函数返
+ * 回åŽçš„æŸä¸ªæ—¶åˆ»è¢«è°ƒç”¨ï¼ˆç»å¸¸ç›´æŽ¥ä»Žä¸­æ–­æœåŠ¡ä¾‹ç¨‹ä¸­è°ƒç”¨ï¼‰ã€‚
+ * æŸäº›æƒ…况下(如模拟SCSI INQUIRYå“应的伪适é…器驱动),
+ * scp->scsi_done回调å¯èƒ½åœ¨è¯¥å‡½æ•°è¿”回å‰å°±è¢«è°ƒç”¨ã€‚
+ * è‹¥scp->scsi_done回调函数未在指定时é™å†…被调用,SCSI中
+ * 间层将å¯åŠ¨é”™è¯¯å¤„ç†æµç¨‹ã€‚当调用scp->scsi_done回调函数
+ * 时,若“resultâ€å­—段被设置为CHECK CONDITION,
+ * 则LLD应执行自动感知并填充
+ * struct scsi_cmnd::sense_buffer数组。在中间层将
+ * 命令加入LLD队列之å‰å‰ï¼Œscsi_cmnd::sense_buffer数组
+ * 会被清零。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šLLD必须实现
+ **/
+ int queuecommand(struct Scsi_Host *shost, struct scsi_cmnd * scp)
+
+
+ /**
+ * sdev_init - åœ¨å‘æ–°è®¾å¤‡å‘é€ä»»ä½•SCSI命令å‰ï¼ˆå³å¼€å§‹æ‰«æ
+ * 之å‰ï¼‰è°ƒç”¨è¯¥å‡½æ•°
+ * @sdp: 指å‘å³å°†è¢«æ‰«æçš„æ–°è®¾å¤‡çš„æŒ‡é’ˆ
+ *
+ * 返回值:返回0表示正常。返回其他值表示出错,
+ * 该设备将被忽略。
+ *
+ * å¹¶å‘安全声明:无é”
+ *
+ * 调用上下文说明:进程上下文
+ *
+ * 注æ„事项:该函数å…许LLD在设备首次扫æå‰åˆ†é…所需的资æºã€‚
+ * 对应的SCSI设备å¯èƒ½å°šæœªçœŸæ­£å­˜åœ¨ï¼Œä½†SCSI中间层å³å°†å¯¹å…¶è¿›
+ * 行扫æï¼ˆä¾‹å¦‚å‘é€INQUIRY命令等)。如果设备存在,将调用
+ * sdev_configure()进行é…置;如果设备ä¸å­˜åœ¨ï¼Œåˆ™è°ƒç”¨
+ * sdev_destroy()销æ¯ã€‚更多细节请å‚考
+ * include/scsi/scsi_host.h文件。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int sdev_init(struct scsi_device *sdp)
+
+
+ /**
+ * sdev_configure - åœ¨è®¾å¤‡é¦–æ¬¡å®Œæˆæ‰«æï¼ˆå³å·²æˆåŠŸå“应INQUIRY
+ * 命令)之åŽï¼ŒLDDå¯è°ƒç”¨è¯¥å‡½æ•°å¯¹è®¾å¤‡è¿›è¡Œè¿›ä¸€æ­¥é…ç½®
+ * @sdp: 已连接的设备
+ *
+ * 返回值:返回0表示æˆåŠŸã€‚ä»»ä½•å…¶ä»–è¿”å›žå€¼éƒ½è¢«è§†ä¸ºé”™è¯¯ï¼Œæ­¤æ—¶
+ * 设备将被标记为离线。[被标记离线的设备ä¸ä¼šè°ƒç”¨sdev_destroy(),
+ * 因此需è¦LLD主动清ç†èµ„æºã€‚]
+ *
+ * å¹¶å‘安全声明:无é”
+ *
+ * 调用上下文说明:进程上下文
+ *
+ * 注æ„事项:该接å£å…许LLD查看设备扫æä»£ç æ‰€å‘出的åˆå§‹INQUIRY
+ * 命令的å“应,并采å–对应æ“作。具体实现细节请å‚阅
+ * include/scsi/scsi_host.h文件。
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ int sdev_configure(struct scsi_device *sdp)
+
+
+ /**
+ * sdev_destroy - 当指定设备å³å°†è¢«å…³é—­æ—¶è°ƒç”¨ã€‚此时该设备
+ * 上的所有I/O活动å‡å·²åœæ­¢ã€‚
+ * @sdp: å³å°†å…³é—­çš„设备
+ *
+ * 返回值:无
+ *
+ * å¹¶å‘安全声明:无é”
+ *
+ * 调用上下文说明:进程上下文
+ *
+ * 注æ„事项:该设备的中间层数æ®ç»“æž„ä»ç„¶å­˜åœ¨
+ * 但å³å°†è¢«é”€æ¯ã€‚é©±åŠ¨ç¨‹åºæ­¤æ—¶åº”当释放为该设
+ * 备分é…的所有专属资æºã€‚系统将ä¸å†å‘æ­¤sdp
+ * 实例å‘é€ä»»ä½•命令。[但该设备å¯èƒ½åœ¨æœªæ¥è¢«
+ * 釿–°è¿žæŽ¥ï¼Œå±Šæ—¶å°†é€šè¿‡æ–°çš„struct scsi_device
+ * 实例,并触å‘åŽç»­çš„sdev_init()å’Œ
+ * sdev_configure()调用过程。]
+ *
+ * å¯é€‰å®žçŽ°è¯´æ˜Žï¼šç”±LLD选择性定义
+ **/
+ void sdev_destroy(struct scsi_device *sdp)
+
+
+
+æ•°æ®ç»“æž„
+========
+struct scsi_host_template
+-------------------------
+æ¯ä¸ªLLD对应一个“struct scsi_host_templateâ€
+实例 [#]_。该结构体通常被åˆå§‹åŒ–为驱动头文件中的é™
+æ€å…¨å±€å˜é‡ï¼Œæ­¤æ–¹å¼å¯ç¡®ä¿æœªæ˜¾å¼åˆå§‹åŒ–çš„æˆå‘˜è‡ªåŠ¨ç½®é›¶
+(0或NULL)。关键æˆå‘˜å˜é‡è¯´æ˜Žå¦‚下:
+
+ name
+ - 驱动程åºçš„å称(å¯ä»¥åŒ…å«ç©ºæ ¼ï¼Œè¯·é™åˆ¶åœ¨80个字符以内)
+
+ proc_name
+ - 在“/proc/scsi/<proc_name>/<host_no>â€
+ å’Œsysfs的“driversâ€ç›®å½•中使用的å称。因此
+ “proc_nameâ€åº”仅包å«Unix文件å䏭坿ޥå—
+ 的字符。
+
+ ``(*queuecommand)()``
+ - 中间层使用的主è¦å›žè°ƒå‡½æ•°ï¼Œç”¨äºŽå°†SCSI命令
+ æäº¤åˆ°LLD。
+
+ vendor_id
+ - 该字段是一个唯一标识值,用于确认æä¾›
+ Scsi_Host LLD的供应商,最常用于
+ 验è¯ä¾›åº”商特定的消æ¯è¯·æ±‚。该值由标识符类型
+ 和供应商特定值组æˆï¼Œæœ‰æ•ˆæ ¼å¼æè¿°è¯·å‚阅
+ scsi_netlink.h头文件。
+
+该结构体的完整定义åŠè¯¦ç»†æ³¨é‡Šè¯·å‚阅 ``include/scsi/scsi_host.h``。
+
+.. [#] 在æžç«¯æƒ…况下,å•ä¸ªé©±åŠ¨éœ€è¦æŽ§åˆ¶å¤šç§ä¸åŒç±»åž‹çš„硬件时,驱动å¯
+ 能包å«å¤šä¸ªå®žä¾‹ï¼Œï¼ˆä¾‹å¦‚æŸä¸ªLLDé©±åŠ¨åŒæ—¶å¤„ç†ISAå’ŒPCI两ç§ç±»åž‹
+ 的适é…å¡ï¼Œå¹¶ä¸ºæ¯ç§ç¡¬ä»¶ç±»åž‹ç»´æŠ¤ç‹¬ç«‹çš„
+ struct scsi_host_template实例)。
+
+struct Scsi_Host
+----------------
+æ¯ä¸ªç”±LLD控制的主机适é…器对应一个struct Scsi_Host实例。
+该结构体与struct scsi_host_templateå…·æœ‰å¤šä¸ªç›¸åŒæˆå‘˜ã€‚
+当创建struct Scsi_Host实例时(通过hosts.c中的
+scsi_host_alloc()函数),这些通用æˆå‘˜ä¼šä»ŽLLDçš„
+struct scsi_host_template实例åˆå§‹åŒ–而æ¥ã€‚关键æˆå‘˜è¯´æ˜Ž
+如下:
+
+ host_no
+ - 系统范围内唯一的主机标识å·ï¼ŒæŒ‰å‡åºä»Ž0开始分é…
+ can_queue
+ - 必须大于0,表示适é…器å¯å¤„ç†çš„æœ€å¤§å¹¶å‘命令数,ç¦
+ æ­¢å‘适é…器å‘é€è¶…过此数值的命令数
+ this_id
+ - 主机适é…器的SCSI ID(SCSIå¯åŠ¨å™¨æ ‡è¯†ï¼‰ï¼Œè‹¥æœªçŸ¥åˆ™
+ 设置为-1
+ sg_tablesize
+ - 主机适é…器支æŒçš„æœ€å¤§æ•£åˆ—表(scatter-gather)元素
+ 数。设置为SG_ALL或更å°çš„值å¯é¿å…使用链å¼SG列表,
+ 且最å°å€¼å¿…须为1
+ max_sectors
+ - å•个SCSI命令中å…许的最大扇区数(通常为512字节/
+ 扇区)。默认值为0,此时会使用
+ SCSI_DEFAULT_MAX_SECTORS(在scsi_host.h中定义),
+ 当å‰è¯¥å€¼ä¸º1024。因此,如果未定义max_sectors,则ç£ç›˜çš„
+ 最大传输大å°ä¸º512KB。注æ„:这个大å°å¯èƒ½ä¸è¶³ä»¥æ”¯æŒ
+ ç£ç›˜å›ºä»¶ä¸Šä¼ ã€‚
+ cmd_per_lun
+ - 主机适é…器的设备上,æ¯ä¸ªLUNå¯æŽ’é˜Ÿçš„æœ€å¤§å‘½ä»¤æ•°ã€‚
+ 此值å¯é€šè¿‡LLD调用scsi_change_queue_depth()进行
+ 调整。
+ hostt
+ - 指å‘LLD struct scsi_host_template实例的指针,
+ 当å‰struct Scsi_Host实例正是由此模æ¿ç”Ÿæˆã€‚
+ hostt->proc_name
+ - LLDçš„å称,sysfs使用的驱动å。
+ transportt
+ - 指å‘LLD struct scsi_transport_template实例的指
+ é’ˆï¼ˆå¦‚æžœå­˜åœ¨ï¼‰ã€‚å½“å‰æ”¯æŒFC与SPI传输å议。
+ hostdata[0]
+ - 为LLD在struct Scsi_Host结构体末尾预留的区域,大å°ç”±
+ scsi_host_alloc()çš„ç¬¬äºŒä¸ªå‚æ•°(privsize)决定。
+
+scsi_host结构体的完整定义详è§include/scsi/scsi_host.h。
+
+struct scsi_device
+------------------
+通常而言,æ¯ä¸ªSCSI逻辑å•元(Logical Unit)对应一个该结构
+的实例。连接到主机适é…器的SCSI设备通过三个è¦ç´ å”¯ä¸€æ ‡è¯†ï¼šé€š
+é“å·ï¼ˆChannel Number)ã€ç›®æ ‡ID(Target ID)和逻辑å•å…ƒå·
+(LUN)。
+该结构体完整定义于include/scsi/scsi_device.h。
+
+struct scsi_cmnd
+----------------
+该结构体实例用于在LLD与SCSI中间层之间传递SCSI命令
+åŠå…¶å“应。SCSI中间层会确ä¿ï¼šæäº¤åˆ°LLD的命令数ä¸è¶…过
+scsi_change_queue_depth()(或struct Scsi_Host::cmd_per_lun)
+设定的上é™ï¼Œä¸”æ¯ä¸ªSCSI设备至少分é…一个struct scsi_cmnd实例。
+关键æˆå‘˜è¯´æ˜Žå¦‚下:
+
+ cmnd
+ - 包å«SCSI命令的数组
+ cmd_len
+ - SCSI命令的长度(字节为å•ä½ï¼‰
+ sc_data_direction
+ - æ•°æ®çš„传输方å‘。请å‚考
+ include/linux/dma-mapping.h中的
+ “enum dma_data_directionâ€ã€‚
+ result
+ - LLD在调用“doneâ€ä¹‹å‰è®¾ç½®è¯¥å€¼ã€‚值为0表示命令æˆåŠŸ
+ 完æˆï¼ˆå¹¶ä¸”所有数æ®ï¼ˆå¦‚果有)已æˆåŠŸåœ¨ä¸»æœºä¸ŽSCSI
+ 目标设备之间完æˆä¼ è¾“)。“resultâ€æ˜¯ä¸€ä¸ª32使— ç¬¦
+ å·æ•´æ•°ï¼Œå¯ä»¥è§†ä¸º2个相关字节。SCSI状æ€å€¼ä½äºŽæœ€
+ 低有效ä½ã€‚请å‚考include/scsi/scsi.h中的
+ status_byte()与host_byte()å®ä»¥åŠå…¶ç›¸å…³å¸¸é‡ã€‚
+ sense_buffer
+ - 这是一个数组(最大长度为SCSI_SENSE_BUFFERSIZE
+ 字节),当SCSI状æ€ï¼ˆâ€œresultâ€çš„æœ€ä½Žæœ‰æ•ˆä½ï¼‰è®¾ä¸º
+ CHECK_CONDITION(2)时,该数组由LLD填写。若
+ CHECK_CONDITION被置ä½ï¼Œä¸”sense_buffer[0]的高
+ åŠå­—节值为7,则中间层会认为sense_buffer数组
+ åŒ…å«æœ‰æ•ˆçš„SCSI感知数æ®ï¼›å¦åˆ™ï¼Œä¸­é—´å±‚会å‘é€
+ REQUEST_SENSE SCSI命令æ¥èŽ·å–æ„ŸçŸ¥æ•°æ®ã€‚由于命令
+ 排队的存在,åŽä¸€ç§æ–¹å¼å®¹æ˜“出错,因此建议LLD始终
+ 支æŒâ€œè‡ªåŠ¨æ„ŸçŸ¥â€ã€‚
+ device
+ - 指å‘与该命令关è”çš„scsi_device对象的指针。
+ resid_len (通过调用scsi_set_resid() / scsi_get_resid()访问)
+ - LLDåº”å°†æ­¤æ— ç¬¦å·æ•´æ•°è®¾ç½®ä¸ºè¯·æ±‚的传输长度(å³
+ “request_bufflenâ€ï¼‰å‡åŽ»å®žé™…ä¼ è¾“çš„å­—èŠ‚æ•°ã€‚â€œresid_lenâ€
+ 默认设置为0,因此如果LLDæ— æ³•æ£€æµ‹åˆ°æ•°æ®æ¬ è½½ï¼ˆä¸èƒ½æŠ¥å‘Šæº¢å‡ºï¼‰ï¼Œ
+ 则å¯ä»¥å¿½ç•¥å®ƒã€‚LLD应在调用“doneâ€ä¹‹å‰è®¾ç½®
+ “resid_lenâ€ã€‚
+ underflow
+ - 如果实际传输的字节数å°äºŽè¯¥å­—段值,LLD应将
+ DID_ERROR << 16赋值给“resultâ€ã€‚å¹¶éžæ‰€æœ‰
+ LLD都实现此项检查,部分LLD仅将错误信æ¯è¾“出
+ 到日志,而未真正报告DID_ERROR。更推è
+ çš„åšæ³•是由LLD实现“resid_lenâ€çš„æ”¯æŒã€‚
+
+建议LLD在从SCSI目标设备进行数æ®ä¼ è¾“时设置“resid_lenâ€å­—段
+(例如READæ“作)。当这些数æ®ä¼ è¾“çš„æ„ŸçŸ¥ç æ˜¯MEDIUM ERROR或
+HARDWARE ERROR(有时也包括RECOVERED ERROR)时设置
+resid_len尤为é‡è¦ã€‚åœ¨è¿™ç§æƒ…况下,如果LLD无法确定接收了多
+å°‘æ•°æ®ï¼Œé‚£ä¹ˆæœ€å®‰å…¨çš„åšæ³•是表示没有接收到任何数æ®ã€‚例如:
+为了表明没有接收到任何有效数æ®ï¼ŒLLDå¯ä»¥ä½¿ç”¨å¦‚下辅助函数::
+
+ scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));
+
+其中SCpnt是一个指å‘scsi_cmnd对象的指针。如果表示仅接收到
+三个512字节的数æ®å—,å¯ä»¥è¿™æ ·è®¾ç½®resid_len::
+
+ scsi_set_resid(SCpnt, scsi_bufflen(SCpnt) - (3 * 512));
+
+scsi_cmnd结构体定义在 include/scsi/scsi_cmnd.h文件中。
+
+
+é”
+===
+æ¯ä¸ªstruct Scsi_Host实例都有一个å为default_lock
+的自旋é”(spin_lock),它在scsi_host_alloc()函数
+中åˆå§‹åŒ–(该函数定义在hosts.c文件中)。在åŒä¸€ä¸ªå‡½æ•°
+中,struct Scsi_Host::host_lock指针会被åˆå§‹åŒ–为指
+å‘default_lock。此åŽï¼ŒSCSI中间层执行的加
+é”å’Œè§£é”æ“作都会使用host_lock指针。过去,驱动程åºå¯
+以é‡å†™host_lock指针,但现在ä¸å†å…许这样åšã€‚
+
+
+自动感知
+========
+自动感知(Autosense或auto-sense)在SAM-2规范中被定
+义为:当SCSI命令完æˆçжæ€ä¸ºCHECK CONDITION时,“自动
+将感知数æ®ï¼ˆsense data)返回给应用程åºå®¢æˆ·ç«¯â€ã€‚底层
+驱动(LLD)应当执行自动感知。当LLD检测到
+CHECK CONDITIONçŠ¶æ€æ—¶ï¼Œå¯é€šè¿‡ä»¥ä¸‹ä»»ä¸€æ–¹å¼å®Œæˆï¼š
+
+ a) è¦æ±‚SCSIå议(例如SCSI并行接å£ï¼ˆSPI))在此
+ ç±»å“应中执行一次é¢å¤–的数æ®ä¼ è¾“
+
+ b) 或由LLD主动å‘èµ·REQUEST SENSEå‘½ä»¤èŽ·å–æ„ŸçŸ¥æ•°æ®
+
+æ— è®ºé‡‡ç”¨å“ªç§æ–¹å¼ï¼Œå½“检测到CHECK CONDITIONçŠ¶æ€æ—¶ï¼Œä¸­
+间层通过检查结构体scsi_cmnd::sense_buffer[0]的值æ¥
+判断LLD是å¦å·²æ‰§è¡Œè‡ªåŠ¨æ„ŸçŸ¥ã€‚è‹¥è¯¥å­—èŠ‚çš„é«˜åŠå­—节为7
+(或 0xf),则认为已执行自动感知;若该字节为其他值
+ï¼ˆä¸”æ­¤å­—èŠ‚åœ¨æ¯æ¡å‘½ä»¤æ‰§è¡Œå‰ä¼šè¢«åˆå§‹åŒ–为0),则中间层将
+主动å‘èµ·REQUEST SENSE命令。
+
+在存在命令队列的场景下,ä¿å­˜å¤±è´¥å‘½ä»¤æ„ŸçŸ¥æ•°æ®çš„“nexusâ€
+å¯èƒ½ä¼šåœ¨ç­‰å¾…REQUEST SENSE命令期间å˜å¾—ä¸åŒæ­¥ã€‚因此,
+最佳实践是由LLD执行自动感知。
+
+
+自Linux内核2.4以æ¥çš„å˜æ›´
+========================
+io_request_lockå·²è¢«å¤šä¸ªç»†ç²’åº¦é”æ›¿ä»£ã€‚与底层驱动
+(LLDï¼‰ç›¸å…³çš„é”æ˜¯struct Scsi_Host::host_lock,且æ¯
+个SCSI主机都独立拥有一个该é”。
+
+æ—§çš„é”™è¯¯å¤„ç†æœºåˆ¶å·²ç»è¢«ç§»é™¤ã€‚è¿™æ„味ç€LLD的接å£å‡½æ•°
+abort()与reset()å·²ç»è¢«åˆ é™¤ã€‚
+struct scsi_host_template::use_new_eh_code标志
+也已ç»è¢«ç§»é™¤ã€‚
+
+在Linux内核2.4中,SCSIå­ç³»ç»Ÿçš„é…ç½®æè¿°ä¸Žå…¶ä»–Linuxå­ç³»
+统的é…ç½®æè¿°é›†ä¸­å­˜æ”¾åœ¨Documentation/Configure.help
+文件中。在Linux内核2.6中,SCSIå­ç³»ç»Ÿæ‹¥æœ‰ç‹¬ç«‹çš„é…置文
+ä»¶drivers/scsi/Kconfig(体积更å°ï¼‰ï¼ŒåŒæ—¶åŒ…å«é…置信æ¯
+与帮助信æ¯ã€‚
+
+struct SHTå·²é‡å‘½å为struct scsi_host_template。
+
+æ–°å¢žâ€œçƒ­æ’æ‹”åˆå§‹åŒ–模型â€ä»¥åŠè®¸å¤šç”¨äºŽæ”¯æŒè¯¥åŠŸèƒ½çš„é¢å¤–函数。
+
+
+致谢
+====
+以下人员对本文档åšå‡ºäº†è´¡çŒ®ï¼š
+
+ - Mike Anderson <andmike at us dot ibm dot com>
+ - James Bottomley <James dot Bottomley at hansenpartnership dot com>
+ - Patrick Mansfield <patmans at us dot ibm dot com>
+ - Christoph Hellwig <hch at infradead dot org>
+ - Doug Ledford <dledford at redhat dot com>
+ - Andries Brouwer <Andries dot Brouwer at cwi dot nl>
+ - Randy Dunlap <rdunlap at xenotime dot net>
+ - Alan Stern <stern at rowland dot harvard dot edu>
+
+
+Douglas Gilbert
+dgilbert at interlog dot com
+
+2004年9月21日
diff --git a/Documentation/translations/zh_CN/scsi/sd-parameters.rst b/Documentation/translations/zh_CN/scsi/sd-parameters.rst
new file mode 100644
index 000000000000..b3d0445dc9f3
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/sd-parameters.rst
@@ -0,0 +1,38 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/sd-parameters.rst
+
+:翻译:
+
+ éƒæ ‹æ ‹ doubled <doubled@leap-io-kernel.com>
+
+:校译:
+
+
+
+============================
+Linux SCSIç£ç›˜é©±åŠ¨ï¼ˆsdï¼‰å‚æ•°
+============================
+
+缓存类型(读/写)
+------------------
+å¯ç”¨/ç¦ç”¨é©±åŠ¨å™¨è¯»å†™ç¼“å­˜ã€‚
+
+=========================== ===== ===== ======= =======
+ 缓存类型字符串 WCE RCD 写缓存 读缓存
+=========================== ===== ===== ======= =======
+ write through 0 0 关闭 å¼€å¯
+ none 0 1 关闭 关闭
+ write back 1 0 å¼€å¯ å¼€å¯
+ write back, no read (daft) 1 1 å¼€å¯ å…³é—­
+=========================== ===== ===== ======= =======
+
+将缓存类型设置为“write backâ€å¹¶å°†è¯¥è®¾ç½®ä¿å­˜åˆ°é©±åЍ噍::
+
+ # echo "write back" > cache_type
+
+如果è¦ä¿®æ”¹ç¼“存模å¼ä½†ä¸ä½¿æ›´æ”¹æŒä¹…化,å¯åœ¨ç¼“存类型字符串å‰
+添加“temporary â€ã€‚例如::
+
+ # echo "temporary write back" > cache_type
diff --git a/Documentation/translations/zh_CN/scsi/wd719x.rst b/Documentation/translations/zh_CN/scsi/wd719x.rst
new file mode 100644
index 000000000000..79757c42032b
--- /dev/null
+++ b/Documentation/translations/zh_CN/scsi/wd719x.rst
@@ -0,0 +1,35 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/scsi/libsas.rst
+
+:翻译:
+
+ å¼ é’°æ° Yujie Zhang <yjzhang@leap-io-kernel.com>
+
+:校译:
+
+====================================================
+Western Digital WD7193, WD7197 å’Œ WD7296 SCSI å¡é©±åЍ
+====================================================
+
+这些å¡éœ€è¦åŠ è½½å›ºä»¶ã€‚å›ºä»¶å¯ä»Ž WD æä¾›ä¸‹è½½çš„ Windows NT 驱动程
+åºä¸­æå–。地å€å¦‚下:
+
+http://support.wdc.com/product/download.asp?groupid=801&sid=27&lang=en
+
+该文件或网页上都未包å«ä»»ä½•许å¯å£°æ˜Žï¼Œå› æ­¤è¯¥å›ºä»¶å¯èƒ½æ— æ³•被收录到
+linux-firmware 项目中。
+
+æä¾›çš„脚本å¯ç”¨äºŽä¸‹è½½å¹¶æå–å›ºä»¶ï¼Œç”Ÿæˆ wd719x-risc.bin å’Œ
+wd719x-wcs.bin 文件。请将它们放置在 /lib/firmware/ 目录下。
+脚本内容如下:
+
+ #!/bin/sh
+ wget http://support.wdc.com/download/archive/pciscsi.exe
+ lha xi pciscsi.exe pci-scsi.exe
+ lha xi pci-scsi.exe nt/wd7296a.sys
+ rm pci-scsi.exe
+ dd if=wd7296a.sys of=wd719x-risc.bin bs=1 skip=5760 count=14336
+ dd if=wd7296a.sys of=wd719x-wcs.bin bs=1 skip=20096 count=514
+ rm wd7296a.sys
diff --git a/Documentation/translations/zh_CN/security/SCTP.rst b/Documentation/translations/zh_CN/security/SCTP.rst
new file mode 100644
index 000000000000..f2774b0d66b5
--- /dev/null
+++ b/Documentation/translations/zh_CN/security/SCTP.rst
@@ -0,0 +1,317 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/security/SCTP.rst
+
+:翻译:
+ 赵硕 Shuo Zhao <zhaoshuo@cqsoftware.com.cn>
+
+====
+SCTP
+====
+
+SCTPçš„LSM支æŒ
+=============
+
+安全钩å­
+--------
+
+å¯¹äºŽå®‰å…¨æ¨¡å—æ”¯æŒï¼Œå·²ç»å®žçŽ°äº†ä¸‰ä¸ªç‰¹å®šäºŽSCTP的钩å­::
+
+ security_sctp_assoc_request()
+ security_sctp_bind_connect()
+ security_sctp_sk_clone()
+ security_sctp_assoc_established()
+
+这些钩å­çš„用法在下é¢çš„ `SCTPçš„SELinux支æŒ`_ 一章中æè¿°SELinux的实现。
+
+
+security_sctp_assoc_request()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+将关è”INITæ•°æ®åŒ…çš„ ``@asoc`` å’Œ ``@chunk->skb`` 传递给安全模å—。
+æˆåŠŸæ—¶è¿”å›ž 0,失败时返回错误。
+::
+
+ @asoc - 指å‘sctpå…³è”结构的指针。
+ @skb - 指å‘包å«å…³è”æ•°æ®åŒ…skbuff的指针。
+
+
+security_sctp_bind_connect()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+将一个或多个IPv4/IPv6地å€ä¼ é€’给安全模å—进行基于 ``@optname`` 的验è¯ï¼Œ
+这将导致是绑定还是连接æœåŠ¡ï¼Œå¦‚ä¸‹é¢çš„æƒé™æ£€æŸ¥è¡¨æ‰€ç¤ºã€‚æˆåŠŸæ—¶è¿”å›ž 0,失败
+时返回错误。
+::
+
+ @sk - 指å‘sock结构的指针。
+ @optname - 需è¦éªŒè¯çš„选项å称。
+ @address - 一个或多个IPv4 / IPv6地å€ã€‚
+ @addrlen - 地å€çš„æ€»é•¿åº¦ã€‚使用sizeof(struct sockaddr_in)或
+ sizeof(struct sockaddr_in6)æ¥è®¡ç®—æ¯ä¸ªipv4或ipv6地å€ã€‚
+
+ ------------------------------------------------------------------
+ | BIND 类型检查 |
+ | @optname | @address contains |
+ |----------------------------|-----------------------------------|
+ | SCTP_SOCKOPT_BINDX_ADD | 一个或多个 ipv4 / ipv6 åœ°å€ |
+ | SCTP_PRIMARY_ADDR | å•个 ipv4 or ipv6 åœ°å€ |
+ | SCTP_SET_PEER_PRIMARY_ADDR | å•个 ipv4 or ipv6 åœ°å€ |
+ ------------------------------------------------------------------
+
+ ------------------------------------------------------------------
+ | CONNECT 类型检查 |
+ | @optname | @address contains |
+ |----------------------------|-----------------------------------|
+ | SCTP_SOCKOPT_CONNECTX | 一个或多个 ipv4 / ipv6 åœ°å€ |
+ | SCTP_PARAM_ADD_IP | 一个或多个 ipv4 / ipv6 åœ°å€ |
+ | SCTP_SENDMSG_CONNECT | å•个 ipv4 or ipv6 åœ°å€ |
+ | SCTP_PARAM_SET_PRIMARY | å•个 ipv4 or ipv6 åœ°å€ |
+ ------------------------------------------------------------------
+
+æ¡ç›® ``@optname`` 的摘è¦å¦‚下::
+
+ SCTP_SOCKOPT_BINDX_ADD - å…许在(å¯é€‰åœ°ï¼‰è°ƒç”¨ bind(3) åŽï¼Œå…³è”é¢å¤–
+ 的绑定地å€ã€‚
+ sctp_bindx(3) 用于在套接字上添加一组绑定地å€ã€‚
+
+ SCTP_SOCKOPT_CONNECTX - å…许分é…多个地å€ä»¥è¿žæŽ¥åˆ°å¯¹ç«¯ï¼ˆå¤šå®¿ä¸»ï¼‰ã€‚
+ sctp_connectx(3) 使用多个目标地å€åœ¨SCTP
+ 套接字上å‘起连接。
+
+ SCTP_SENDMSG_CONNECT - 通过sendmsg(2)或sctp_sendmsg(3)在新关è”上
+ å‘起连接。
+
+ SCTP_PRIMARY_ADDR - 设置本地主地å€ã€‚
+
+ SCTP_SET_PEER_PRIMARY_ADDR - 请求远程对端将æŸä¸ªåœ°å€è®¾ç½®ä¸ºå…¶ä¸»åœ°å€ã€‚
+
+ SCTP_PARAM_ADD_IP - 在å¯ç”¨åЍæ€åœ°å€é‡é…置时使用。
+ SCTP_PARAM_SET_PRIMARY - 如下所述,å¯ç”¨é‡æ–°é…置功能。
+
+
+为了支æŒåЍæ€åœ°å€é‡æ–°é…置,必须在两个端点上å¯ç”¨ä»¥ä¸‹
+傿•°ï¼ˆæˆ–使用适当的 **setsockopt**\(2))::
+
+ /proc/sys/net/sctp/addip_enable
+ /proc/sys/net/sctp/addip_noauth_enable
+
+当相应的 ``@optname`` 存在时,以下的 *_PARAM_* 傿•°ä¼š
+通过ASCONFå—å‘é€åˆ°å¯¹ç«¯::
+
+ @optname ASCONF Parameter
+ ---------- ------------------
+ SCTP_SOCKOPT_BINDX_ADD -> SCTP_PARAM_ADD_IP
+ SCTP_SET_PEER_PRIMARY_ADDR -> SCTP_PARAM_SET_PRIMARY
+
+
+security_sctp_sk_clone()
+~~~~~~~~~~~~~~~~~~~~~~~~
+æ¯å½“通过 **accept**\(2)创建一个新的套接字(å³TCP类型的套接字),或者当
+一个套接字被‘剥离’时如用户空间调用 **sctp_peeloff**\(3),会调用此函数。
+::
+
+ @asoc - 指å‘当å‰sctpå…³è”结构的指针。
+ @sk - 指å‘当å‰å¥—接字结构的指针。
+ @newsk - æŒ‡å‘æ–°çš„套接字结构的指针。
+
+
+security_sctp_assoc_established()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+当收到COOKIE ACK时调用,对于客户端,对端的secid将被ä¿å­˜
+到 ``@asoc->peer_secid`` 中::
+
+ @asoc - 指å‘sctpå…³è”结构的指针。
+ @skb - 指å‘COOKIE ACKæ•°æ®åŒ…çš„skbuff指针。
+
+
+用于关è”建立的安全钩å­
+----------------------
+
+ä¸‹å›¾å±•ç¤ºäº†åœ¨å»ºç«‹å…³è”æ—¶ ``security_sctp_bind_connect()``〠``security_sctp_assoc_request()``
+和 ``security_sctp_assoc_established()`` 的使用。
+::
+
+ SCTP 端点 "A" SCTP 端点 "Z"
+ ============= =============
+ sctp_sf_do_prm_asoc()
+ å…³è”的设置å¯ä»¥é€šè¿‡connect(2),
+ sctp_connectx(3),sendmsg(2)
+ or sctp_sendmsg(3)æ¥å‘起。
+ 这将导致调用security_sctp_bind_connect()
+ å‘起与SCTP对端端点"Z"的关è”。
+ INIT --------------------------------------------->
+ sctp_sf_do_5_1B_init()
+ å“应一个INITæ•°æ®å—。
+ SCTP对端端点"A"正在请求一个临时关è”。
+ 如果是首次关è”,调用security_sctp_assoc_request()
+ æ¥è®¾ç½®å¯¹ç­‰æ–¹æ ‡ç­¾ã€‚
+ å¦‚æžœä¸æ˜¯é¦–次关è”,检查是å¦è¢«å…许。
+ 如果å…许,则å‘é€:
+ <----------------------------------------------- INIT ACK
+ |
+ | å¦åˆ™ï¼Œç”Ÿæˆå®¡è®¡äº‹ä»¶å¹¶é»˜é»˜ä¸¢å¼ƒè¯¥æ•°æ®åŒ…。
+ |
+ COOKIE ECHO ------------------------------------------>
+ sctp_sf_do_5_1D_ce()
+ å“应一个COOKIE ECHOæ•°æ®å—。
+ 确认该cookie并创建一个永久关è”。
+ 调用security_sctp_assoc_request()
+ 执行与INITæ•°æ®å—å“应相åŒçš„æ“ä½œã€‚
+ <------------------------------------------- COOKIE ACK
+ | |
+ sctp_sf_do_5_1E_ca |
+ 调用security_sctp_assoc_established() |
+ æ¥è®¾ç½®å¯¹æ–¹æ ‡ç­¾ |
+ | |
+ | 如果是SCTP_SOCKET_TCP或是剥离的套接
+ | 字,会调用 security_sctp_sk_clone()
+ | æ¥å…‹éš†æ–°çš„套接字。
+ | |
+ 建立 建立
+ | |
+ ------------------------------------------------------------------
+ | å…³è”建立 |
+ ------------------------------------------------------------------
+
+
+SCTPçš„SELinux支æŒ
+=================
+
+安全钩å­
+--------
+
+上é¢çš„ `SCTPçš„LSM支æŒ`_ 章节æè¿°äº†ä»¥ä¸‹SCTP安全钩å­ï¼ŒSELinux的细节
+说明如下::
+
+ security_sctp_assoc_request()
+ security_sctp_bind_connect()
+ security_sctp_sk_clone()
+ security_sctp_assoc_established()
+
+
+security_sctp_assoc_request()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+将关è”INITæ•°æ®åŒ…çš„ ``@asoc`` å’Œ ``@chunk->skb`` 传递给安全模å—。
+æˆåŠŸæ—¶è¿”å›ž 0,失败时返回错误。
+::
+
+ @asoc - 指å‘sctpå…³è”结构的指针。
+ @skb - 指å‘å…³è”æ•°æ®åŒ…skbuff的指针。
+
+å®‰å…¨æ¨¡å—æ‰§è¡Œä»¥ä¸‹æ“作:
+ 如果这是 ``@asoc->base.sk`` 上的首次关è”,则将对端的sid设置
+ 为 ``@skb`` 中的值。这将确ä¿åªæœ‰ä¸€ä¸ªå¯¹ç«¯sid分é…ç»™å¯èƒ½æ”¯æŒå¤šä¸ª
+ å…³è”çš„ ``@asoc->base.sk``。
+
+ å¦åˆ™éªŒè¯ ``@asoc->base.sk peer sid`` 是å¦ä¸Ž ``@skb peer sid``
+ 匹é…ï¼Œä»¥ç¡®å®šè¯¥å…³è”æ˜¯å¦åº”被å…许或拒ç»ã€‚
+
+ å°†sctpçš„ ``@asoc sid`` 设置为套接字的sid(æ¥è‡ª ``asoc->base.sk``)
+ 并从 ``@skb peer sid`` 中æå–MLS部分。这将在SCTPçš„TCP类型套接字åŠ
+ 剥离连接中使用,因为它们会导致生æˆä¸€ä¸ªæ–°çš„套接字。
+
+ 如果é…置了IP安全选项(CIPSO/CALIPSO),则会在套接字上设置IP选项。
+
+
+security_sctp_bind_connect()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+æ ¹æ® ``@optname`` 检查ipv4/ipv6åœ°å€æ‰€éœ€çš„æƒé™ï¼Œå…·ä½“如下::
+
+ ------------------------------------------------------------------
+ | BIND æƒé™æ£€æŸ¥ |
+ | @optname | @address contains |
+ |----------------------------|-----------------------------------|
+ | SCTP_SOCKOPT_BINDX_ADD | 一个或多个 ipv4 / ipv6 åœ°å€ |
+ | SCTP_PRIMARY_ADDR | å•个 ipv4 or ipv6 åœ°å€ |
+ | SCTP_SET_PEER_PRIMARY_ADDR | å•个 ipv4 or ipv6 åœ°å€ |
+ ------------------------------------------------------------------
+
+ ------------------------------------------------------------------
+ | CONNECT æƒé™æ£€æŸ¥ |
+ | @optname | @address contains |
+ |----------------------------|-----------------------------------|
+ | SCTP_SOCKOPT_CONNECTX | 一个或多个 ipv4 / ipv6 åœ°å€ |
+ | SCTP_PARAM_ADD_IP | 一个或多个 ipv4 / ipv6 åœ°å€ |
+ | SCTP_SENDMSG_CONNECT | å•个 ipv4 or ipv6 åœ°å€ |
+ | SCTP_PARAM_SET_PRIMARY | å•个 ipv4 or ipv6 åœ°å€ |
+ ------------------------------------------------------------------
+
+
+`SCTPçš„LSM支æŒ`_ æä¾›äº† ``@optname`` 摘è¦ï¼Œå¹¶ä¸”还æè¿°äº†å½“å¯ç”¨åЍæ€åœ°å€é‡æ–°
+é…置时,ASCONFå—的处ç†è¿‡ç¨‹ã€‚
+
+
+security_sctp_sk_clone()
+~~~~~~~~~~~~~~~~~~~~~~~~
+æ¯å½“通过 **accept**\(2)(å³TCP类型的套接字)创建一个新的套接字,或者
+当一个套接字被“剥离â€å¦‚用户空间调用 **sctp_peeloff**\(3)时,
+``security_sctp_sk_clone()`` 将会分别将新套接字的sid和对端sid设置为
+``@asoc sid`` å’Œ ``@asoc peer sid`` 中包å«çš„值。
+::
+
+ @asoc - 指å‘当å‰sctpå…³è”结构的指针。
+ @sk - 指å‘当å‰sock结构的指针。
+ @newsk - æŒ‡å‘æ–°sock结构的指针。
+
+
+security_sctp_assoc_established()
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+当接收到COOKIE ACK时调用,它将连接的对端sid设置为 ``@skb`` 中的值::
+
+ @asoc - 指å‘sctpå…³è”结构的指针。
+ @skb - 指å‘COOKIE ACK包skbuff的指针。
+
+
+策略声明
+--------
+以下支æŒSCTP的类和æƒé™åœ¨å†…核中是å¯ç”¨çš„::
+
+ class sctp_socket inherits socket { node_bind }
+
+当å¯ç”¨ä»¥ä¸‹ç­–略功能时::
+
+ policycap extended_socket_class;
+
+SELinux对SCTPçš„æ”¯æŒæ·»åŠ äº†ç”¨äºŽè¿žæŽ¥ç‰¹å®šç«¯å£ç±»åž‹ ``name_connect`` æƒé™
+以åŠåœ¨ä¸‹é¢çš„章节中进行解释的 ``association`` æƒé™ã€‚
+
+如果用户空间工具已更新,SCTP将支æŒå¦‚下所示的 ``portcon`` 声明::
+
+ portcon sctp 1024-1036 system_u:object_r:sctp_ports_t:s0
+
+
+SCTP对端标签
+------------
+æ¯ä¸ªSCTP套接字仅分é…ä¸€ä¸ªå¯¹ç«¯æ ‡ç­¾ã€‚è¿™ä¸ªæ ‡ç­¾å°†åœ¨å»ºç«‹ç¬¬ä¸€ä¸ªå…³è”æ—¶åˆ†é…。
+任何åŽç»­åœ¨è¯¥å¥—接字上的关è”都会将它们的数æ®åŒ…对端标签与套接字的对端标
+ç­¾è¿›è¡Œæ¯”è¾ƒï¼Œåªæœ‰åœ¨å®ƒä»¬ä¸åŒçš„æƒ…况下 ``association`` æƒé™æ‰ä¼šè¢«éªŒè¯ã€‚
+这是通过检查套接字的对端sid与接收到的数æ®åŒ…中的对端sidæ¥éªŒè¯çš„,以决
+定是å¦å…许或拒ç»è¯¥å…³è”。
+
+注:
+ 1) 如果对端标签未å¯ç”¨ï¼Œåˆ™å¯¹ç«¯ä¸Šä¸‹æ–‡å°†å§‹ç»ˆæ˜¯ ``SECINITSID_UNLABELED``
+ (在策略声明中为 ``unlabeled_t`` )。
+
+ 2) 由于SCTPå¯ä»¥åœ¨å•ä¸ªå¥—æŽ¥å­—ä¸Šæ”¯æŒæ¯ä¸ªç«¯ç‚¹ï¼ˆå¤šå®¿ä¸»ï¼‰çš„多个传输地å€ï¼Œå› æ­¤
+ å¯ä»¥é…置策略和NetLabel为æ¯ä¸ªç«¯ç‚¹æä¾›ä¸åŒçš„对端标签。由于套接字的对端
+ 标签是由第一个关è”的传输地å€å†³å®šçš„ï¼Œå› æ­¤å»ºè®®æ‰€æœ‰çš„å¯¹ç«¯æ ‡ç­¾ä¿æŒä¸€è‡´ã€‚
+
+ 3) 用户空间å¯ä»¥ä½¿ç”¨ **getpeercon**\(3) æ¥æ£€ç´¢å¥—接字的对端上下文。
+
+ 4) è™½ç„¶è¿™ä¸æ˜¯SCTP特有的,但在使用NetLabelæ—¶è¦æ³¨æ„,如果标签分é…给特定的接
+ å£ï¼Œè€Œè¯¥æŽ¥å£â€˜goes down’,则NetLabelæœåŠ¡ä¼šç§»é™¤è¯¥æ¡ç›®ã€‚因此,请确ä¿ç½‘络å¯
+ 动脚本调用 **netlabelctl**\(8) æ¥è®¾ç½®æ‰€éœ€çš„æ ‡ç­¾ï¼ˆè¯¦ç»†ä¿¡æ¯ï¼Œ
+ 请å‚阅 **netlabel-config**\(8) 辅助脚本)。
+
+ 5) NetLabel SCTP对端标签规则应用如下所述标签为“netlabelâ€çš„一组帖å­ï¼š
+ https://www.paul-moore.com/blog/t.
+
+ 6) CIPSO仅支æŒIPv4地å€ï¼š ``socket(AF_INET, ...)``
+ CALIPSO仅支æŒIPv6地å€ï¼š ``socket(AF_INET6, ...)``
+
+ 测试CIPSO/CALIPSO时请注æ„以下事项:
+ a) 如果SCTPæ•°æ®åŒ…由于无效标签无法é€è¾¾ï¼ŒCIPSO会å‘é€ä¸€ä¸ªICMP包。
+ b) CALIPSOä¸ä¼šå‘é€ICMP包,åªä¼šé»˜é»˜ä¸¢å¼ƒæ•°æ®åŒ…。
+
+ 7) RFC 3554䏿”¯æŒIPSEC —— SCTP/IPSEC支æŒå°šæœªåœ¨ç”¨æˆ·ç©ºé—´å®žçް(**racoon**\(8)
+ 或 **ipsec_pluto**\(8))ï¼Œå°½ç®¡å†…æ ¸æ”¯æŒ SCTP/IPSEC。
diff --git a/Documentation/translations/zh_CN/security/index.rst b/Documentation/translations/zh_CN/security/index.rst
index 78d9d4b36dca..d33b107405c7 100644
--- a/Documentation/translations/zh_CN/security/index.rst
+++ b/Documentation/translations/zh_CN/security/index.rst
@@ -18,7 +18,9 @@
credentials
snp-tdx-threat-model
lsm
+ lsm-development
sak
+ SCTP
self-protection
siphash
tpm/index
@@ -28,7 +30,5 @@
TODOLIST:
* IMA-templates
* keys/index
-* lsm-development
-* SCTP
* secrets/index
* ipe
diff --git a/Documentation/translations/zh_CN/security/ipe.rst b/Documentation/translations/zh_CN/security/ipe.rst
new file mode 100644
index 000000000000..55968f0c7ae3
--- /dev/null
+++ b/Documentation/translations/zh_CN/security/ipe.rst
@@ -0,0 +1,398 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/security/sak.rst
+
+:翻译:
+ 赵硕 Shuo Zhao <zhaoshuo@cqsoftware.com.cn>
+
+完整性策略执行(IPE)-内核文档
+==============================
+
+.. NOTE::
+
+ 这是针对开å‘äººå‘˜è€Œä¸æ˜¯ç®¡ç†å‘˜çš„æ–‡æ¡£ã€‚如果您正在
+ 寻找有关IPE使用的文档,请å‚阅 :doc:`IPE admin
+ guide </admin-guide/LSM/ipe>`。
+
+历å²èƒŒæ™¯
+--------
+
+最åˆä¿ƒä½¿IPE实施的原因,是需è¦åˆ›å»ºä¸€ä¸ªé”定å¼ç³»ç»Ÿã€‚该系统将
+ä»Žä¸€å¼€å§‹å°±å…·å¤‡å®‰å…¨æ€§ï¼Œå¹¶ä¸”åœ¨å¯æ‰§è¡Œä»£ç å’Œç³»ç»ŸåŠŸèƒ½å…³é”®çš„ç‰¹å®š
+æ•°æ®æ–‡ä»¶ä¸Šï¼Œæä¾›å¼ºæœ‰åŠ›çš„å®Œæ•´æ€§ä¿éšœã€‚åªæœ‰å½“è¿™äº›ç‰¹å®šæ•°æ®æ–‡ä»¶
+符åˆå®Œæ•´æ€§ç­–略时,它们æ‰å¯ä»¥è¢«è¯»å–。系统中还将存在强制访问
+控制机制,因此扩展属性(xattrs)也必须å—åˆ°ä¿æŠ¤ã€‚è¿™å°±å¼•å‡ºäº†
+需è¦é€‰æ‹©èƒ½å¤Ÿæä¾›å®Œæ•´æ€§ä¿è¯çš„æœºåˆ¶ã€‚当时,有两ç§ä¸»è¦æœºåˆ¶è¢«è€ƒ
+è™‘ï¼Œç”¨ä»¥åœ¨æ»¡è¶³è¿™äº›è¦æ±‚çš„å‰æä¸‹ä¿è¯ç³»ç»Ÿå®Œæ•´æ€§ï¼š
+
+ 1. IMA + EVM Signatures
+ 2. DM-Verity
+
+这两个选项都ç»è¿‡äº†ä»”细考虑,然而在原始的IPE使用场景
+中,最终选择DM-Verity而éžIMA+EVM作为完整性机制,主
+è¦æœ‰ä¸‰ä¸ªåŽŸå› ï¼š
+
+ 1. 防护é¢å¤–的攻击途径
+
+ * 使用IMA+EVM时,如果没有加密解决方案,系统很容易å—到
+ ç¦»çº¿æ”»å‡»ï¼Œç‰¹åˆ«æ˜¯é’ˆå¯¹ä¸Šè¿°ç‰¹å®šæ•°æ®æ–‡ä»¶çš„æ”»å‡»ã€‚
+
+ ä¸Žå¯æ‰§è¡Œæ–‡ä»¶ä¸åŒï¼Œè¯»å–æ“作(如对å—ä¿æŠ¤æ•°æ®æ–‡ä»¶çš„读
+ å–æ“作)无法强制性进行全局完整性验è¯ã€‚è¿™æ„味ç€å¿…é¡»
+ 有一ç§é€‰æ‹©æœºåˆ¶æ¥å†³å®šæ˜¯å¦åº”对æŸä¸ªè¯»å–æ“作实施完整性
+ 策略。
+
+ 在当时,这是通过强制访问控制标签æ¥å®žçŽ°çš„ï¼ŒIMA策略会
+ 指定哪些标签需è¦è¿›è¡Œå®Œæ•´æ€§éªŒè¯ï¼Œè¿™å¸¦æ¥äº†ä¸€ä¸ªé—®é¢˜ï¼š
+ EVM虽然å¯ä»¥ä¿æŠ¤æ ‡ç­¾ï¼Œä½†å¦‚果攻击者离线修改文件系统,
+ 那么攻击者就å¯ä»¥æ¸…除所有的扩展属性(xattrs)——包括
+ 用于确定文件是å¦åº”å—完整性策略约æŸçš„SELinux标签。
+
+ 使用DM-Verity,由于xattrs被ä¿å­˜ä¸ºMerkel树的一部分,
+ 如果对由dm-verityä¿æŠ¤çš„æ–‡ä»¶ç³»ç»Ÿè¿›è¡Œäº†ç¦»çº¿æŒ‚è½½ï¼Œæ ¡éªŒ
+ 和将ä¸åœ¨åŒ¹é…,文件将无法读å–。
+
+ * 由于用户空间的二进制文件在Linux中是分页加载的,dm-
+ verityåŒæ ·æä¾›äº†å¯¹æŠ—æ¶æ„å—设备的é¢å¤–ä¿æŠ¤ã€‚åœ¨è¿™æ ·çš„
+ 攻击中,å—è®¾å¤‡æœ€åˆæŠ¥å‘Šé€‚å½“çš„å†…å®¹ä»¥ä¾›IMA哈希计算,通
+ 过所需的完整性检查。然åŽï¼Œåœ¨è®¿é—®çœŸå®žæ•°æ®æ—¶å‘生的页é¢
+ 错误将报告攻击者的有效载è·ã€‚由于dm-verity会在页é¢é”™
+ 误å‘生时检查数æ®ï¼ˆä»¥åŠç£ç›˜è®¿é—®ï¼‰ï¼Œå› æ­¤è¿™ç§æ”»å‡»å¾—到了
+ 缓解。
+
+ 2. 性能:
+
+ * dm-verity在å—è¢«è¯»å–æ—¶æŒ‰éœ€æä¾›å®Œæ•´æ€§éªŒè¯ï¼Œè€Œä¸éœ€è¦å°†æ•´
+ 个文件读入内存进行验è¯ã€‚
+
+ 3. ç­¾å的简化性:
+
+ * ä¸éœ€è¦ä¸¤ä¸ªç­¾å(IMA ç„¶åŽæ˜¯ EVM):一个签åå¯ä»¥è¦†ç›–整个
+ å—设备。
+ * ç­¾åå¯ä»¥å­˜å‚¨åœ¨æ–‡ä»¶ç³»ç»Ÿå…ƒæ•°æ®ä¹‹å¤–。
+ * è¯¥ç­¾åæ”¯æŒåŸºäºŽ x.509 的签å基础设施。
+
+ä¸‹ä¸€æ­¥æ˜¯é€‰æ‹©ä¸€ä¸ªç­–ç•¥æ¥æ‰§è¡Œå®Œæ•´æ€§éªŒè¯æœºåˆ¶ï¼Œè¯¥ç­–略的最低
+è¦æ±‚是:
+
+ 1. 策略本身必须ç»è¿‡å®Œæ•´æ€§éªŒè¯ï¼ˆé˜²æ­¢é’ˆå¯¹å®ƒçš„ç®€å•æ”»å‡»ï¼‰ã€‚
+ 2. 策略本身必须抵抗回滚攻击。
+ 3. ç­–ç•¥æ‰§è¡Œå¿…é¡»å…·æœ‰ç±»ä¼¼å®½æ¾æ¨¡å¼çš„功能。
+ 4. 策略必须能够在ä¸é‡å¯çš„æƒ…况下,完整地进行更新。
+ 5. ç­–ç•¥æ›´æ–°å¿…é¡»æ˜¯åŽŸå­æ€§çš„。
+ 6. ç­–ç•¥å¿…é¡»æ”¯æŒæ’¤é”€å…ˆå‰åˆ›å»ºçš„组件。
+ 7. 策略必须在任何时间点都能进行审计。
+
+当时,IMA作为唯一的完整性策略机制,被用æ¥ä¸Žè¿™äº›è¦æ±‚进行对比,
+ä½†æœªèƒ½æ»¡è¶³æ‰€æœ‰æœ€ä½Žè¦æ±‚。尽管考虑过扩展IMAä»¥æ¶µç›–è¿™äº›è¦æ±‚,但
+最终因两个原因被放弃:
+
+ 1. å›žå½’é£Žé™©ï¼›è¿™å…¶ä¸­è®¸å¤šå˜æ›´å°†å¯¼è‡´å¯¹å·²ç»å­˜åœ¨äºŽå†…核的IMA进行
+ é‡å¤§ä»£ç æ›´æ”¹ï¼Œå› æ­¤å¯èƒ½ä¼šå½±å“用户。
+
+ 2. IMA在该系统中用于测é‡å’Œè¯æ˜Žï¼›å°†æµ‹é‡ç­–略与本地完整性策略
+ 的执行分离被认为是有利的。
+
+由于这些原因,决定创建一个新的LSM,其èŒè´£æ˜¯ä»…é™äºŽæœ¬åœ°å®Œæ•´æ€§
+策略的执行。
+
+èŒè´£å’ŒèŒƒå›´
+----------
+
+IPEé¡¾åæ€ä¹‰ï¼Œæœ¬è´¨ä¸Šæ˜¯ä¸€ç§å®Œæ•´æ€§ç­–略执行解决方案;IPEå¹¶ä¸å¼ºåˆ¶è§„定
+如何æä¾›å®Œæ•´æ€§ä¿éšœï¼Œè€Œæ˜¯å°†è¿™ä¸€å†³ç­–æƒç•™ç»™ç³»ç»Ÿç®¡ç†å‘˜ï¼Œç®¡ç†å‘˜æ ¹æ®è‡ªèº«
+需求,选择符åˆçš„æœºåˆ¶æ¥è®¾å®šå®‰å…¨æ ‡å‡†ã€‚存在几ç§ä¸åŒçš„完整性解决方案,
+它们æä¾›äº†ä¸åŒç¨‹åº¦çš„安全ä¿éšœï¼›è€ŒIPEå…许系统管ç†å‘˜ç†è®ºä¸Šä¸ºæ‰€æœ‰è¿™äº›
+解决方案制定策略。
+
+IPE自身没有内置确ä¿å®Œæ•´æ€§çš„固有机制。相å,在构建具备完整性ä¿éšœèƒ½åŠ›
+的系统时,存在更高效的分层方案å¯ä¾›ä½¿ç”¨ã€‚需è¦é‡ç‚¹æ³¨æ„çš„æ˜¯ï¼Œç”¨äºŽè¯æ˜Žå®Œ
+整性的机制,与用于执行完整性声明的策略是相互独立的。
+
+因此,IPE便®ä»¥ä¸‹æ–¹é¢è¿›è¡Œè®¾è®¡ï¼š
+
+ 1. 便于与完整性æä¾›æœºåˆ¶é›†æˆã€‚
+ 2. 便于平å°ç®¡ç†å‘˜/系统管ç†å‘˜ä½¿ç”¨ã€‚
+
+设计ç†ç”±:
+---------
+
+IPE是在评估其他æ“作系统和环境中的现有完整性策略解决方案åŽè®¾è®¡çš„。
+在对其他实现的调查中,å‘现了一些缺陷:
+
+ 1. ç­–ç•¥ä¸æ˜“为人们读å–,通常需è¦äºŒè¿›åˆ¶ä¸­é—´æ ¼å¼ã€‚
+ 2. 默认情况下会éšå¼é‡‡å–å•一的ã€ä¸å¯å®šåˆ¶çš„æ“ä½œã€‚
+ 3. è°ƒè¯•ç­–ç•¥éœ€è¦æ‰‹åЍæ¥ç¡®å®šè¿å了哪个规则。
+ 4. 编写策略需è¦å¯¹æ›´å¤§ç³»ç»Ÿæˆ–æ“作系统有深入的了解。
+
+IPEå°è¯•é¿å…所有这些缺陷。
+
+ç­–ç•¥
+~~~~
+
+纯文本
+^^^^^^
+
+IPE的策略是纯文本格å¼çš„。相较于其他Linux安全模å—(LSM),
+策略文件体积略大,但能解决其他平å°ä¸Šéƒ¨åˆ†å®Œæ•´æ€§ç­–略方案存在
+的两个核心问题。
+
+第一个问题是代ç ç»´æŠ¤å’Œå†—余的问题。为了编写策略,策略必须是
+以æŸç§å½¢å¼çš„字符串形å¼å‘ˆçŽ°ï¼ˆæ— è®ºæ˜¯ XMLã€JSONã€YAML 等结构化
+æ ¼å¼ï¼Œè¿˜æ˜¯å…¶ä»–å½¢å¼ï¼‰ï¼Œä»¥ä¾¿ç­–略编写者能够ç†è§£æ‰€å†™å†…容。在å‡è®¾
+的二进制策略设计中,需è¦ä¸€ä¸ªåºåˆ—化器将策略将å¯è¯»çš„å½¢å¼è½¬æ¢ä¸º
+二进制形å¼ï¼ŒåŒæ—¶è¿˜éœ€è¦ä¸€ä¸ªååºåˆ—化器æ¥å°†äºŒè¿›åˆ¶å½¢å¼è½¬æ¢ä¸ºå†…æ ¸
+中的数æ®ç»“构。
+
+最终,还需è¦å¦ä¸€ä¸ªååºåˆ—化器将是必è¦çš„,用于将二进制形å¼è½¬æ¢
+为人类å¯è¯»çš„å½¢å¼ï¼Œå¹¶å°½å¯èƒ½ä¿å­˜æ‰€æœ‰ä¿¡æ¯ï¼Œè¿™æ˜¯å› ä¸ºä½¿ç”¨æ­¤è®¿é—®æŽ§
+制系统的用户必须维护一个校验表和原始文件,æ‰èƒ½ç†è§£å“ªäº›ç­–略已
+ç»éƒ¨ç½²åœ¨è¯¥ç³»ç»Ÿä¸Šï¼Œå“ªäº›æ²¡æœ‰ã€‚对于å•个用户æ¥è¯´ï¼Œè¿™å¯èƒ½æ²¡é—®é¢˜ï¼Œ
+因为旧的策略å¯ä»¥åœ¨æ›´æ–°ç”Ÿæ•ˆåŽå¾ˆå¿«è¢«ä¸¢å¼ƒã€‚ä½†å¯¹äºŽç®¡ç†æˆåƒä¸Šä¸‡ã€
+甚至数å万å°è®¡ç®—机的用户,且这些计算机有ä¸åŒçš„æ“ä½œç³»ç»Ÿå’Œä¸åŒ
+çš„æ“作需求,这很快就æˆäº†ä¸€ä¸ªé—®é¢˜ï¼Œå› ä¸ºæ•°å¹´å‰çš„过时策略å¯èƒ½ä»ç„¶
+存在,从而导致需è¦å¿«é€Ÿæ¢å¤ç­–略或投资大é‡åŸºç¡€è®¾æ–½æ¥è·Ÿè¸ªæ¯ä¸ªç­–ç•¥
+的内容。
+
+有了这三个独立的åºåˆ—化器/ååºåˆ—åŒ–å™¨ï¼Œç»´æŠ¤æˆæœ¬éžå¸¸æ˜‚贵。如果策略
+é¿å…使用二进制格å¼ï¼Œåˆ™åªéœ€è¦ä¸€ä¸ªåºåˆ—化器;将人类å¯è¯»çš„å½¢å¼è½¬æ¢
+为内核中的数æ®ç»“构。从而节çœäº†ä»£ç ç»´æŠ¤æˆæœ¬ï¼Œå¹¶ä¿æŒäº†å¯æ“作性。
+
+第二个关于二进制格å¼çš„é—®é¢˜æ˜¯é€æ˜Žæ€§ï¼Œç”±äºŽIPEæ ¹æ®ç³»ç»Ÿèµ„æºçš„å¯ä¿¡åº¦
+æ¥æŽ§åˆ¶è®¿é—®ï¼Œå› æ­¤å…¶ç­–ç•¥ä¹Ÿå¿…é¡»å¯ä¿¡ï¼Œä»¥ä¾¿å¯ä»¥è¢«æ›´æ”¹ã€‚è¿™æ˜¯é€šè¿‡ç­¾åæ¥
+完æˆçš„,这就需è¦ç­¾å过程。签å过程通常具有很高的安全标准,因为
+任何被签å的内容都å¯ä»¥è¢«ç”¨æ¥æ”»å‡»å®Œæ•´æ€§æ‰§è¡Œç³»ç»Ÿã€‚签署时,签署者
+必须知é“他们在签署什么,二进制策略å¯èƒ½ä¼šå¯¼è‡´è¿™ä¸€ç‚¹çš„æ¨¡ç³ŠåŒ–;签署
+è€…çœ‹åˆ°çš„åªæ˜¯ä¸€ä¸ªä¸é€æ˜Žçš„二进制数æ®å—。å¦ä¸€æ–¹é¢ï¼Œå¯¹äºŽçº¯æ–‡æœ¬ç­–略中,
+签署者看到的则是实际æäº¤çš„策略。
+
+å¯åŠ¨ç­–ç•¥
+~~~~~~~~
+
+如果é…置得当,IPE能够在内核å¯åŠ¨å¹¶è¿›å…¥ç”¨æˆ·æ¨¡å¼æ—¶ç«‹å³æ‰§è¡Œç­–略。
+è¿™æ„味ç€éœ€è¦åœ¨ç”¨æˆ·æ¨¡å¼å¼€å§‹çš„那一刻就存储一定的策略。通常,这ç§
+存储å¯ä»¥é€šè¿‡ä¸€ä¸‹ä¸‰ç§æ–¹å¼ä¹‹ä¸€æ¥å¤„ç†ï¼š
+
+ 1. 策略文件存储在ç£ç›˜ä¸Šï¼Œå†…核在进入å¯èƒ½éœ€è¦åšå‡ºæ‰§è¡Œå†³ç­–的代ç 
+ 路径之å‰ï¼Œå…ˆåŠ è½½è¯¥ç­–ç•¥ã€‚
+ 2. 策略文件由引导加载程åºä¼ é€’给内核,内核解æžè¿™äº›ç­–略。
+ 3. 将一个策略文件编译到内核中,内核在åˆå§‹åŒ–过程中对其进行解æžå¹¶
+ 执行。
+
+ç¬¬ä¸€ç§æ–¹å¼å­˜åœ¨é—®é¢˜ï¼šå†…æ ¸ä»Žç”¨æˆ·ç©ºé—´è¯»å–æ–‡ä»¶é€šå¸¸æ˜¯ä¸æŽ¨è的,并且在
+内核中æžä¸ºç½•è§ã€‚
+
+第二ç§é€‰é¡¹åŒæ ·å­˜åœ¨é—®é¢˜ï¼šLinux在其整个生æ€ç³»ç»Ÿä¸­æ”¯æŒå¤šç§å¼•导加载程åºï¼Œ
+所有引导加载程åºéƒ½å¿…须支æŒè¿™ç§æ–°æ–¹æ³•ï¼Œæˆ–è€…éœ€è¦æœ‰ä¸€ä¸ªç‹¬ç«‹çš„æ¥æºï¼Œè¿™
+å¯èƒ½ä¼šå¯¼è‡´å†…æ ¸å¯åŠ¨è¿‡ç¨‹å‘生ä¸å¿…è¦çš„é‡å¤§å˜åŒ–。
+
+第三ç§é€‰é¡¹æ˜¯æœ€ä½³é€‰æ‹©ï¼Œä½†éœ€è¦æ³¨æ„的是,编译进内核的策略会å ç”¨ç£ç›˜ç©ºé—´ã€‚
+é‡è¦çš„æ˜¯è¦ä½¿è¿™ä¸€ç­–ç•¥è¶³å¤Ÿé€šç”¨ï¼Œä»¥ä¾¿ç”¨æˆ·ç©ºé—´èƒ½å¤ŸåŠ è½½æ–°çš„ã€æ›´å¤æ‚的策略,
+åŒæ—¶ä¹Ÿè¦è¶³å¤Ÿä¸¥æ ¼ï¼Œä»¥é˜²æ­¢è¿‡åº¦æŽˆæƒå¹¶é¿å…引å‘安全问题。
+
+initramfsæä¾›äº†ä¸€ç§å»ºç«‹æ­¤å¯åŠ¨è·¯å¾„çš„æ–¹æ³•ã€‚å†…æ ¸å¯åŠ¨æ—¶ä»¥æœ€å°åŒ–的策略å¯åŠ¨ï¼Œ
+该策略仅信任initramfs。在initramfs内,当真实的根文件系统已挂载且尚未
+åˆ‡æ¢æ—¶ï¼Œå®ƒä¼šéƒ¨ç½²å¹¶æ¿€æ´»ä¸€ä¸ªä¿¡ä»»æ–°æ ¹æ–‡ä»¶ç³»ç»Ÿçš„ç­–ç•¥ã€‚è¿™ç§æ–¹æ³•防止了在任何
+步骤中出现过度授æƒï¼Œå¹¶ä¿æŒå†…核策略的最å°åŒ–。
+
+å¯åЍ
+^^^^
+
+ç„¶è€Œï¼Œå¹¶ä¸æ˜¯æ¯ä¸ªç³»ç»Ÿéƒ½ä»¥initramfså¯åŠ¨ï¼Œå› æ­¤ç¼–è¯‘è¿›å†…æ ¸çš„å¯åŠ¨ç­–ç•¥éœ€è¦å…·å¤‡
+ä¸€å®šçš„çµæ´»æ€§ï¼Œä»¥æ˜Žç¡®å¦‚何为å¯åŠ¨çš„ä¸‹ä¸€ä¸ªé˜¶æ®µå»ºç«‹ä¿¡ä»»ã€‚ä¸ºæ­¤ï¼Œå¦‚æžœæˆ‘ä»¬å°†ç¼–è¯‘
+进内核的策略设计为一个完整的IPE策略,这样系统构建者便能åˆç†å®šä¹‰ç¬¬ä¸€é˜¶æ®µå¯
+动的需求。
+
+坿›´æ–°ã€æ— éœ€é‡å¯çš„ç­–ç•¥
+~~~~~~~~~~~~~~~~~~~~~~
+
+éšç€æ—¶é—´çš„æŽ¨ç§»ï¼Œç³»ç»Ÿéœ€æ±‚å‘生å˜åŒ–(例如,之å‰ä¿¡ä»»çš„应用程åºä¸­å‘çŽ°æ¼æ´žã€ç§˜é’¥
+è½®æ¢ç­‰ï¼‰ã€‚更新内核以满足这些安全目标并éžå§‹ç»ˆæ˜¯ä¸€ä¸ªåˆé€‚的选择,因为内核更新并
+éžå®Œå…¨æ— é£Žé™©çš„,而æç½®å®‰å…¨æ›´æ–°ä¼šä½¿ç³»ç»Ÿå¤„于脆弱状æ€ã€‚è¿™æ„味ç€IPE需è¦ä¸€ä¸ªå¯ä»¥
+完全更新的策略(å…è®¸æ’¤é”€çŽ°æœ‰çš„ç­–ç•¥ï¼‰ï¼Œå¹¶ä¸”è¿™ä¸ªæ›´æ–°æ¥æºå¿…须是内核外部的(å…许
+å†ä¸æ›´æ–°å†…核的情况下更新策略)。
+
+此外,由于内核在调用之间是无状æ€çš„,并且从内核空间读å–ç£ç›˜ä¸Šçš„ç­–ç•¥æ–‡ä»¶ä¸æ˜¯ä¸€
+个好主æ„,因此策略更新必须能够在ä¸é‡å¯çš„æƒ…况下完æˆã€‚
+
+为了å…è®¸ä»Žå¤–éƒ¨æ¥æºè¿›è¡Œæ›´æ–°ï¼Œè€ƒè™‘åˆ°å¤–éƒ¨æ¥æºå¯èƒ½æ˜¯æ¶æ„的,因此该策略需è¦å…·å¤‡å¯è¢«
+识别为å¯ä¿¡çš„æœºåˆ¶ã€‚这一机制通过签å链实现:策略的签å需与内核中的æŸä¸ªä¿¡ä»»æºç›¸
+å…³è”ã€‚é€šå¸¸ï¼Œè¿™ä¸ªä¿¡ä»»æºæ˜¯ ``SYSTEM_TRUSTED_KEYRING`` ,这是一个在内核编译时就被
+åˆå§‹åŒ–填充的密钥环,因为这符åˆä¸Šè¿°ç¼–译进æ¥ç­–略的制作者与能够部署策略更新的实体
+相åŒçš„预期。
+
+防回滚 / é˜²é‡æ”¾
+~~~~~~~~~~~~~~~
+
+éšç€æ—¶é—´çš„æŽ¨ç§»ï¼Œç³»ç»Ÿå¯èƒ½ä¼šå‘çŽ°æ¼æ´žï¼Œæ›¾ç»å—信任的资æºå¯èƒ½ä¸å†å¯ä¿¡ï¼ŒIPEçš„
+策略也ä¸ä¾‹å¤–。å¯èƒ½ä¼šå‡ºçŽ°çš„æƒ…å†µæ˜¯ï¼Œç­–ç•¥åˆ¶ä½œè€…è¯¯éƒ¨ç½²äº†ä¸€ä¸ªä¸å®‰å…¨çš„策略,
+éšåŽå†ç”¨ä¸€ä¸ªå®‰å…¨çš„策略进行修正。
+
+å‡è®¾ä¸€æ—¦ä¸å®‰å…¨çš„策略被部署,攻击者获å–了这个ä¸å®‰å…¨çš„策略,IPEéœ€è¦æœ‰ä¸€ç§
+æ–¹å¼æ¥é˜²æ­¢ä»Žå®‰å…¨çš„策略更新回滚到ä¸å®‰å…¨çš„策略。
+
+最åˆï¼ŒIPE的策略å¯ä»¥åŒ…å«ä¸€ä¸ªpolicy_versionå­—æ®µï¼Œå£°æ˜Žç³»ç»Ÿä¸Šæ‰€æœ‰å¯æ¿€æ´»ç­–ç•¥
+所需的最低版本å·ã€‚这将在系统è¿è¡ŒæœŸé—´é˜²æ­¢å›žæ»šã€‚
+
+.. WARNING::
+
+ ç„¶è€Œï¼Œç”±äºŽå†…æ ¸æ¯æ¬¡å¯åŠ¨éƒ½æ˜¯æ— çŠ¶æ€çš„,因此该策略版本将在下次
+ å¯åŠ¨æ—¶è¢«é‡ç½®ä¸º0.0.0ã€‚ç³»ç»Ÿæž„å»ºè€…éœ€è¦æ„识到这一点,并确ä¿åœ¨å¯
+ 动åŽå°½å¿«éƒ¨ç½²æ–°çš„å®‰å…¨ç­–ç•¥ï¼Œä»¥ç¡®ä¿æ”»å‡»è€…部署ä¸å®‰å…¨çš„策略的几
+ 率最å°åŒ–。
+
+éšå¼æ“作:
+~~~~~~~~~
+
+éšå¼æ“ä½œçš„é—®é¢˜åªæœ‰åœ¨è€ƒè™‘系统中多个æ“作具有ä¸åŒçº§åˆ«æ—¶æ‰ä¼šæ˜¾çŽ°å‡ºæ¥ã€‚
+ä¾‹å¦‚ï¼Œè€ƒè™‘ä¸€ä¸ªç³»ç»Ÿï¼Œè¯¥ç³»ç»Ÿå¯¹å¯æ‰§è¡Œä»£ç å’Œç³»ç»Ÿä¸­å¯¹å…¶åŠŸèƒ½è‡³å…³é‡è¦çš„
+ç‰¹å®šæ•°æ®æä¾›å¼ºå¤§çš„å®Œæ•´æ€§ä¿éšœã€‚在这个系统中,å¯èƒ½å­˜åœ¨ä¸‰ç§ç±»åž‹çš„
+策略:
+
+ 1. 一ç§ç­–略,在这ç§ç­–略中,如果æ“作未能匹é…到任何规则,则该æ“
+ 作将被拒ç»ã€‚
+ 2. 一ç§ç­–略,在这ç§ç­–略中,如果æ“作未能匹é…到任何规则,则该æ“
+ 作将被å…许。
+ 3. 一ç§ç­–略,在这ç§ç­–略中,如果æ“作未能匹é…到任何规则,则执行
+ æ“作由策略作者指定。
+
+第一ç§ç±»åž‹çš„策略示例如下::
+
+ op=EXECUTE integrity_verified=YES action=ALLOW
+
+åœ¨ç¤ºä¾‹ç³»ç»Ÿä¸­ï¼Œè¿™å¯¹äºŽå¯æ‰§è¡Œæ–‡ä»¶æ¥è¯´æ•ˆæžœå¾ˆå¥½ï¼Œå› ä¸ºæ‰€æœ‰å¯æ‰§è¡Œæ–‡ä»¶
+都应该拥有完整性ä¿éšœã€‚ä½†é—®é¢˜å‡ºçŽ°åœ¨ç¬¬äºŒä¸ªè¦æ±‚上,å³å…³äºŽç‰¹å®šæ•°æ®
+æ–‡ä»¶çš„è¦æ±‚。这将导致如下策略(å‡è®¾ç­–ç•¥æŒ‰è¡Œä¾æ¬¡æ‰§è¡Œï¼‰::
+
+ op=EXECUTE integrity_verified=YES action=ALLOW
+
+ op=READ integrity_verified=NO label=critical_t action=DENY
+ op=READ action=ALLOW
+
+è‹¥é˜…è¯»è¿‡æ–‡æ¡£ï¼Œäº†è§£ç­–ç•¥æŒ‰é¡ºåºæ‰§è¡Œä¸”默认动作是拒ç»ï¼Œé‚£ä¹ˆè¿™ä¸ªç­–略的
+逻辑还算清晰;但最åŽä¸€è¡Œè§„åˆ™å®žé™…ä¸Šå°†è¯»å–æ“作的默认动作改æˆäº†å…许。
+è¿™ç§è®¾è®¡æ˜¯å¿…è¦çš„,因为在实际系统中,存在一些无需验è¯çš„è¯»å–æ“作(例
+如呿—¥å¿—æ–‡ä»¶è¿½åŠ å†…å®¹æ—¶çš„è¯»å–æ“作)。
+
+第二ç§ç­–略类型(未匹é…任何规则时默认å…è®¸ï¼‰åœ¨ç®¡æŽ§ç‰¹å®šæ•°æ®æ–‡ä»¶æ—¶é€»è¾‘
+更清晰,其策略å¯ç®€åŒ–为::
+
+ op=READ integrity_verified=NO label=critical_t action=DENY
+
+但与第一ç§ç­–略类似,这ç§é»˜è®¤å…许的策略在管控执行æ“作时会存在缺陷,
+å› æ­¤ä»éœ€æ˜¾å¼è¦†ç›–默认动作::
+
+ op=EXECUTE integrity_verified=YES action=ALLOW
+ op=EXECUTE action=DENY
+
+ op=READ integrity_verified=NO label=critical_t action=DENY
+
+这就引出了第三ç§ç­–略类型(自定义默认动作)。该类型无需让用户绞尽脑æ±
+通过空规则覆盖默认动作,而是强制用户根æ®è‡ªèº«åœºæ™¯æ€è€ƒåˆé€‚的默认动作是
+什么,并显å¼å£°æ˜Ž::
+
+ DEFAULT op=EXECUTE action=DENY
+ op=EXECUTE integrity_verified=YES action=ALLOW
+
+ DEFAULT op=READ action=ALLOW
+ op=READ integrity_verified=NO label=critical_t action=DENY
+
+策略调试:
+~~~~~~~~~
+
+在开å‘策略时,知é“ç­–ç•¥è¿å了哪一行有助于å‡å°‘è°ƒè¯•æˆæœ¬ï¼›å¯ä»¥
+将调查的范围缩å°åˆ°å¯¼è‡´è¯¥è¡Œä¸ºçš„确切行。有些完整性策略系统并
+ä¸æä¾›è¿™ä¸€ä¿¡æ¯ï¼Œè€Œæ˜¯æä¾›è¯„估过程中使用的信æ¯ã€‚è¿™éšåŽéœ€è¦å°†
+这些信æ¯å’Œç­–略进行关è”,以分æžå“ªé‡Œäº†é—®é¢˜ã€‚
+
+相å,IPEåªä¼šè¾“出匹é…到的规则。这将调查范围é™åˆ¶åˆ°ç¡®åˆ‡åˆ°ç­–略行
+(在特定规则的情况下)或部分(在DEFAULT规则的情况下)。当在
+评估策略时观察到策略失败时,这å¯ä»¥å‡å°‘迭代和调查的时间。
+
+IPE的策略引擎还被设计æˆè®©äººç±»å®¹æ˜“ç†è§£å¦‚何调查策略失败。æ¯ä¸€
+行都会按编写顺åºè¿›è¡Œè¯„估,因此算法éžå¸¸ç®€å•,便于人类é‡çŽ°æ­¥
+骤并找出å¯èƒ½å¯¼è‡´å¤±è´¥çš„原因。而在调查其他的系统中,加载策略
+时会进行优化(例如对规则排åºï¼‰ã€‚在这些系统中,调试需è¦å¤šä¸ª
+步骤,而且没有先阅读代ç çš„æƒ…况下,终端用户å¯èƒ½æ— æ³•完全ç†è§£
+该算法的原ç†ã€‚
+
+简化策略:
+~~~~~~~~~
+
+最åŽï¼ŒIPE的策略是为系统管ç†å‘˜è®¾è®¡çš„ï¼Œè€Œä¸æ˜¯å†…核开å‘人员。
+IPE䏿¶‰åŠå•独的LSMé’©å­ï¼ˆæˆ–系统调用),而是涵盖æ“作。这
+æ„味ç€ï¼Œç³»ç»Ÿç®¡ç†å‘˜ä¸éœ€è¦çŸ¥é“åƒ ``mmap`` 〠``mprotect`` ã€
+``execve`` å’Œ ``uselib`` è¿™äº›ç³»ç»Ÿè°ƒç”¨å¿…é¡»æœ‰è§„åˆ™è¿›è¡Œä¿æŠ¤ï¼Œ
+而åªéœ€è¦çŸ¥é“他们想è¦é™åˆ¶ä»£ç æ‰§è¡Œã€‚è¿™å‡å°‘了由于缺ä¹å¯¹åº•层
+系统的了解而å¯èƒ½å¯¼è‡´çš„绕过情况;而IPE的维护者作为内核开å‘
+人员,å¯ä»¥åšå‡ºæ­£ç¡®çš„选择,确定æŸäº›æ“作是å¦ä¸Žè¿™äº›æ“作匹é…,
+以åŠåœ¨ä»€ä¹ˆæ¡ä»¶ä¸‹åŒ¹é…。
+
+实现说明
+--------
+
+匿å内存
+~~~~~~~~
+
+在IPE中,匿åå†…å­˜çš„å¤„ç†æ–¹å¼ä¸Žå…¶ä»–任何类型的访问没有区别。当匿
+å内存使用 ``+X`` 映射时,它ä»ç„¶ä¼šè¿›å…¥ ``file_mmp`` 或
+``file_mprotect`` é’©å­ï¼Œä½†æ­¤æ—¶ä¼šå¸¦æœ‰ä¸€ä¸ª ``NULL`` 文件对象
+这会åƒå…¶ä»–文件一样æäº¤è¿›è¡Œè¯„估。然而,所有当å‰çš„信任属性都会
+评估为å‡ï¼Œå› ä¸ºå®ƒä»¬éƒ½æ˜¯åŸºäºŽæ–‡ä»¶çš„,而此次æ“作并ä¸ä¸Žä»»ä½•文件相关è”。
+
+.. WARNING::
+
+ 这也适用于 ``kernel_load_data`` é’©å­ï¼Œå½“内核从一个没有文件
+ 支æŒçš„ç”¨æˆ·ç©ºé—´ç¼“å†²åŒºåŠ è½½æ•°æ®æ—¶ã€‚åœ¨è¿™ç§æƒ…况下,所有当å‰çš„ä¿¡ä»»
+ 属性也将评估为false。
+
+Securityfs接å£
+~~~~~~~~~~~~~~
+
+æ¯ä¸ªç­–略的对应的securityfs树是有些独特的。例如,对于一个标准的
+securityfs策略树::
+
+ MyPolicy
+ |- active
+ |- delete
+ |- name
+ |- pkcs7
+ |- policy
+ |- update
+ |- version
+
+策略存储在MyPolicy对应节点的 ``->i_private`` æ•°æ®ä¸­ã€‚
+
+测试
+----
+
+IPE为策略解æžå™¨æä¾›äº†KUnit测试。推èkunitconfig::
+
+ CONFIG_KUNIT=y
+ CONFIG_SECURITY=y
+ CONFIG_SECURITYFS=y
+ CONFIG_PKCS7_MESSAGE_PARSER=y
+ CONFIG_SYSTEM_DATA_VERIFICATION=y
+ CONFIG_FS_VERITY=y
+ CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y
+ CONFIG_BLOCK=y
+ CONFIG_MD=y
+ CONFIG_BLK_DEV_DM=y
+ CONFIG_DM_VERITY=y
+ CONFIG_DM_VERITY_VERIFY_ROOTHASH_SIG=y
+ CONFIG_NET=y
+ CONFIG_AUDIT=y
+ CONFIG_AUDITSYSCALL=y
+ CONFIG_BLK_DEV_INITRD=y
+
+ CONFIG_SECURITY_IPE=y
+ CONFIG_IPE_PROP_DM_VERITY=y
+ CONFIG_IPE_PROP_DM_VERITY_SIGNATURE=y
+ CONFIG_IPE_PROP_FS_VERITY=y
+ CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG=y
+ CONFIG_SECURITY_IPE_KUNIT_TEST=y
+
+此外,IPE 具有一个基于 Python 的集æˆ
+`测试套件 <https://github.com/microsoft/ipe/tree/test-suite>`_
+å¯ä»¥æµ‹è¯•用户界é¢å’Œå¼ºåˆ¶æ‰§è¡ŒåŠŸèƒ½ã€‚
diff --git a/Documentation/translations/zh_CN/security/lsm-development.rst b/Documentation/translations/zh_CN/security/lsm-development.rst
new file mode 100644
index 000000000000..7ed3719a9d07
--- /dev/null
+++ b/Documentation/translations/zh_CN/security/lsm-development.rst
@@ -0,0 +1,19 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../disclaimer-zh_CN.rst
+
+:Original: Documentation/security/lsm-development.rst
+
+:翻译:
+ 赵硕 Shuo Zhao <zhaoshuo@cqsoftware.com.cn>
+
+=================
+Linux安全模å—å¼€å‘
+=================
+
+基于https://lore.kernel.org/r/20071026073721.618b4778@laptopd505.fenrus.org,
+å½“ä¸€ç§æ–°çš„LSMçš„æ„图(它试图防范什么,以åŠåœ¨å“ªäº›æƒ…况下人们会期望使用它)在
+``Documentation/admin-guide/LSM/`` 中适当记录下æ¥åŽï¼Œå°±ä¼šè¢«æŽ¥å—进入内核。
+这使得LSM的代ç å¯ä»¥å¾ˆè½»æ¾çš„与其目标进行对比,从而让最终用户和å‘行版å¯ä»¥æ›´
+明智地决定那些LSM适åˆä»–们的需求。
+
+有关å¯ç”¨çš„ LSM 钩孿ޥå£çš„详细文档,请å‚阅 ``security/security.c`` åŠç›¸å…³ç»“构。
diff --git a/Documentation/translations/zh_CN/security/secrets/coco.rst b/Documentation/translations/zh_CN/security/secrets/coco.rst
new file mode 100644
index 000000000000..a27bc1acdb7c
--- /dev/null
+++ b/Documentation/translations/zh_CN/security/secrets/coco.rst
@@ -0,0 +1,96 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. include:: ../../disclaimer-zh_CN.rst
+
+:Original: Documentation/security/secrets/coco.rst
+
+:翻译:
+
+ 赵硕 Shuo Zhao <zhaoshuo@cqsoftware.com.cn>
+
+============
+机密计算密钥
+============
+
+本文档介ç»äº†åœ¨EFI驱动程åºå’Œefi_secret内核模å—中,机密计算密钥从固件
+到æ“ä½œç³»ç»Ÿçš„æ³¨å…¥å¤„ç†æµç¨‹ã€‚
+
+简介
+====
+
+机密计算硬件(如AMD SEV,Secure Encrypted Virtualization)å…许虚拟机
+所有者将密钥注入虚拟机(VM)内存,且主机/è™šæ‹Ÿæœºç›‘æŽ§ç¨‹åºæ— æ³•读å–这些密
+钥。在SEV中,密钥注入需在虚拟机å¯åЍæµç¨‹çš„æ—©æœŸé˜¶æ®µï¼ˆå®¢æˆ·æœºå¼€å§‹è¿è¡Œå‰ï¼‰
+执行。
+
+efi_secret内核模å—å…许用户空间应用程åºé€šè¿‡securityfs(安全文件系统)访
+问这些密钥。
+
+å¯†é’¥æ•°æ®æµ
+==========
+
+客户机固件å¯èƒ½ä¼šä¸ºå¯†é’¥æ³¨å…¥é¢„ç•™ä¸€å—æŒ‡å®šçš„内存区域,并将该区域的ä½ç½®ï¼ˆåŸºå‡†
+客户机物ç†åœ°å€GPA和长度)在EFIé…置表中,通过 ``LINUX_EFI_COCO_SECRET_AREA_GUID``
+æ¡ç›®ï¼ˆå¯¹åº”çš„GUID值为 ``adf956ad-e98c-484c-ae11-b51c7d336447`` )的形å¼å‘布。
+固件应将此内存区域标记为 ``EFI_RESERVED_TYPE`` ,因此内核ä¸åº”将其用于自身用途。
+
+虚拟机å¯åŠ¨è¿‡ç¨‹ä¸­ï¼Œè™šæ‹Ÿæœºç®¡ç†å™¨å¯å‘该区域注入密钥。在AMD SEVå’ŒSEV-ES中,此
+æ“作通过 ``KVM_SEV_LAUNCH_SECRET`` 命令执行(å‚è§ [sev_CN]_ )。注入的“客户机
+所有者密钥数æ®â€åº”采用带GUID的密钥值表结构,其二进制格å¼åœ¨ ``drivers/virt/
+coco/efi_secret/efi_secret.c`` 文件的EFI密钥区域结构部分中有详细æè¿°ã€‚
+
+内核å¯åŠ¨æ—¶ï¼Œå†…æ ¸çš„EFI驱动程åºå°†ä¿å­˜å¯†é’¥åŒºåŸŸä½ç½®ï¼ˆæ¥è‡ªEFIé…置表)到 ``efi.coco_secret``
+字段。éšåŽï¼Œå®ƒä¼šæ£€æŸ¥å¯†é’¥åŒºåŸŸæ˜¯å¦å·²å¡«å……:映射该区域并检查其内容是å¦ä»¥
+``EFI_SECRET_TABLE_HEADER_GUID`` (对应的GUID为 ``1e74f542-71dd-4d66-963e-ef4287ff173b`` )
+开头。如果密钥区域已填充,EFI驱动程åºå°†è‡ªåŠ¨åŠ è½½efi_secret内核模å—,并通过securityfs将密钥
+暴露给用户空间应用程åºã€‚efi_secret文件系统接å£çš„详细信æ¯è¯·å‚考 [secrets-coco-abi_CN]_ 。
+
+
+应用使用示例
+============
+
+å‡è®¾å®¢æˆ·æœºéœ€è¦å¯¹åŠ å¯†æ–‡ä»¶è¿›è¡Œè®¡ç®—å¤„ç†ã€‚客户机所有者通过密钥注入机制æä¾›è§£å¯†å¯†é’¥
+(å³å¯†é’¥ï¼‰ã€‚客户机应用程åºä»Žefi_secret文件系统读å–该密钥,然åŽå°†æ–‡ä»¶è§£å¯†åˆ°å†…存中,
+接ç€å¯¹å†…容进行需è¦çš„计算。
+
+在此示例中,主机无法从ç£ç›˜é•œåƒä¸­è¯»å–文件,因为文件是加密的;主机无法读å–解密密钥,
+因为它是通过密钥注入机制(å³å®‰å…¨é€šé“)传递的;主机也无法读å–内存中的解密内容,因为
+这是一个机密型(内存加密)客户机。
+
+以下是一个简å•的示例,展示了在客户机中使用efi_secret模å—的过程,在å¯åŠ¨æ—¶æ³¨å…¥äº†
+一个包å«4个密钥的EFI密钥区域::
+
+ # ls -la /sys/kernel/security/secrets/coco
+ total 0
+ drwxr-xr-x 2 root root 0 Jun 28 11:54 .
+ drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
+ -r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
+ -r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
+ -r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
+ -r--r----- 1 root root 0 Jun 28 11:54 e6f5a162-d67f-4750-a67c-5d065f2a9910
+
+ # hd /sys/kernel/security/secrets/coco/e6f5a162-d67f-4750-a67c-5d065f2a9910
+ 00000000 74 68 65 73 65 2d 61 72 65 2d 74 68 65 2d 6b 61 |these-are-the-ka|
+ 00000010 74 61 2d 73 65 63 72 65 74 73 00 01 02 03 04 05 |ta-secrets......|
+ 00000020 06 07 |..|
+ 00000022
+
+ # rm /sys/kernel/security/secrets/coco/e6f5a162-d67f-4750-a67c-5d065f2a9910
+
+ # ls -la /sys/kernel/security/secrets/coco
+ total 0
+ drwxr-xr-x 2 root root 0 Jun 28 11:55 .
+ drwxr-xr-x 3 root root 0 Jun 28 11:54 ..
+ -r--r----- 1 root root 0 Jun 28 11:54 736870e5-84f0-4973-92ec-06879ce3da0b
+ -r--r----- 1 root root 0 Jun 28 11:54 83c83f7f-1356-4975-8b7e-d3a0b54312c6
+ -r--r----- 1 root root 0 Jun 28 11:54 9553f55d-3da2-43ee-ab5d-ff17f78864d2
+
+
+å‚考文献
+========
+
+请å‚è§ [sev-api-spec_CN]_ ä»¥èŽ·å–æœ‰å…³SEV ``LAUNCH_SECRET`` æ“作的更多信æ¯ã€‚
+
+.. [sev_CN] Documentation/virt/kvm/x86/amd-memory-encryption.rst
+.. [secrets-coco-abi_CN] Documentation/ABI/testing/securityfs-secrets-coco
+.. [sev-api-spec_CN] https://www.amd.com/system/files/TechDocs/55766_SEV-KM_API_Specification.pdf
+
diff --git a/Documentation/translations/zh_CN/security/secrets/index.rst b/Documentation/translations/zh_CN/security/secrets/index.rst
index 5ea78713f10e..38464dcb2c3c 100644
--- a/Documentation/translations/zh_CN/security/secrets/index.rst
+++ b/Documentation/translations/zh_CN/security/secrets/index.rst
@@ -5,13 +5,10 @@
:翻译:
-=====================
+========
密钥文档
-=====================
+========
.. toctree::
-
-TODOLIST:
-
-* coco
+ coco
diff --git a/Documentation/translations/zh_CN/subsystem-apis.rst b/Documentation/translations/zh_CN/subsystem-apis.rst
index 8b646c1010be..830217140fb6 100644
--- a/Documentation/translations/zh_CN/subsystem-apis.rst
+++ b/Documentation/translations/zh_CN/subsystem-apis.rst
@@ -71,12 +71,11 @@ TODOList:
:maxdepth: 1
filesystems/index
+ scsi/index
TODOList:
-* block/index
* cdrom/index
-* scsi/index
* target/index
**Fixme**: è¿™é‡Œè¿˜éœ€è¦æ›´å¤šçš„分类组织工作。
diff --git a/Documentation/translations/zh_TW/admin-guide/README.rst b/Documentation/translations/zh_TW/admin-guide/README.rst
index 0b038074d9d1..c8b7ccfaa656 100644
--- a/Documentation/translations/zh_TW/admin-guide/README.rst
+++ b/Documentation/translations/zh_TW/admin-guide/README.rst
@@ -291,5 +291,5 @@ Documentation/translations/zh_CN/admin-guide/bug-hunting.rst 。
更多用GDB調試內核的信æ¯ï¼Œè«‹åƒé–±ï¼š
Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst
-和 Documentation/dev-tools/kgdb.rst 。
+和 Documentation/process/debugging/kgdb.rst 。
diff --git a/Documentation/translations/zh_TW/dev-tools/gdb-kernel-debugging.rst b/Documentation/translations/zh_TW/dev-tools/gdb-kernel-debugging.rst
index b595af59ba78..4fd1757c3036 100644
--- a/Documentation/translations/zh_TW/dev-tools/gdb-kernel-debugging.rst
+++ b/Documentation/translations/zh_TW/dev-tools/gdb-kernel-debugging.rst
@@ -2,7 +2,7 @@
.. include:: ../disclaimer-zh_TW.rst
-:Original: Documentation/dev-tools/gdb-kernel-debugging.rst
+:Original: Documentation/process/debugging/gdb-kernel-debugging.rst
:Translator: 高超 gao chao <gaochao49@huawei.com>
通éŽgdb調試內核和模塊
diff --git a/Documentation/userspace-api/dma-buf-heaps.rst b/Documentation/userspace-api/dma-buf-heaps.rst
index 1dfe5e7acd5a..05445c83b79a 100644
--- a/Documentation/userspace-api/dma-buf-heaps.rst
+++ b/Documentation/userspace-api/dma-buf-heaps.rst
@@ -16,13 +16,52 @@ following heaps:
- The ``system`` heap allocates virtually contiguous, cacheable, buffers.
- - The ``cma`` heap allocates physically contiguous, cacheable,
- buffers. Only present if a CMA region is present. Such a region is
- usually created either through the kernel commandline through the
- ``cma`` parameter, a memory region Device-Tree node with the
- ``linux,cma-default`` property set, or through the ``CMA_SIZE_MBYTES`` or
- ``CMA_SIZE_PERCENTAGE`` Kconfig options. The heap's name in devtmpfs is
- ``default_cma_region``. For backwards compatibility, when the
- ``DMABUF_HEAPS_CMA_LEGACY`` Kconfig option is set, a duplicate node is
- created following legacy naming conventions; the legacy name might be
- ``reserved``, ``linux,cma``, or ``default-pool``.
+ - The ``default_cma_region`` heap allocates physically contiguous,
+ cacheable, buffers. Only present if a CMA region is present. Such a
+ region is usually created either through the kernel commandline
+ through the ``cma`` parameter, a memory region Device-Tree node with
+ the ``linux,cma-default`` property set, or through the
+ ``CMA_SIZE_MBYTES`` or ``CMA_SIZE_PERCENTAGE`` Kconfig options. Prior
+ to Linux 6.17, its name wasn't stable and could be called
+ ``reserved``, ``linux,cma``, or ``default-pool``, depending on the
+ platform.
+
+ - A heap will be created for each reusable region in the device tree
+ with the ``shared-dma-pool`` compatible, using the full device tree
+ node name as its name. The buffer semantics are identical to
+ ``default-cma-region``.
+
+Naming Convention
+=================
+
+``dma-buf`` heaps name should meet a number of constraints:
+
+- The name must be stable, and must not change from one version to the other.
+ Userspace identifies heaps by their name, so if the names ever change, we
+ would be likely to introduce regressions.
+
+- The name must describe the memory region the heap will allocate from, and
+ must uniquely identify it in a given platform. Since userspace applications
+ use the heap name as the discriminant, it must be able to tell which heap it
+ wants to use reliably if there's multiple heaps.
+
+- The name must not mention implementation details, such as the allocator. The
+ heap driver will change over time, and implementation details when it was
+ introduced might not be relevant in the future.
+
+- The name should describe properties of the buffers that would be allocated.
+ Doing so will make heap identification easier for userspace. Such properties
+ are:
+
+ - ``contiguous`` for physically contiguous buffers;
+
+ - ``protected`` for encrypted buffers not accessible the OS;
+
+- The name may describe intended usage. Doing so will make heap identification
+ easier for userspace applications and users.
+
+For example, assuming a platform with a reserved memory region located
+at the RAM address 0x42000000, intended to allocate video framebuffers,
+physically contiguous, and backed by the CMA kernel allocator, good
+names would be ``memory@42000000-contiguous`` or ``video@42000000``, but
+``cma-video`` wouldn't.
diff --git a/Documentation/userspace-api/media/cec/cec-header.rst b/Documentation/userspace-api/media/cec/cec-header.rst
index f67003bb8740..648498bc7d6f 100644
--- a/Documentation/userspace-api/media/cec/cec-header.rst
+++ b/Documentation/userspace-api/media/cec/cec-header.rst
@@ -2,10 +2,12 @@
.. _cec_header:
-***************
-CEC Header File
-***************
+****************
+CEC uAPI Symbols
+****************
.. kernel-include:: include/uapi/linux/cec.h
:generate-cross-refs:
:exception-file: cec.h.rst.exceptions
+ :toc:
+ :warn-broken:
diff --git a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions
index 15fa1752d4ef..65e8be062bdb 100644
--- a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions
+++ b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions
@@ -1,5 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
+# All symbols belong to CEC namespace
+namespace CEC
+
# Ignore header name
ignore define _CEC_UAPI_H
diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst
index d706cb47b112..02967c9b18d6 100644
--- a/Documentation/userspace-api/media/drivers/index.rst
+++ b/Documentation/userspace-api/media/drivers/index.rst
@@ -32,6 +32,7 @@ For more details see the file COPYING in the source distribution of Linux.
cx2341x-uapi
dw100
imx-uapi
+ mali-c55
max2175
npcm-video
omap3isp-uapi
diff --git a/Documentation/userspace-api/media/drivers/mali-c55.rst b/Documentation/userspace-api/media/drivers/mali-c55.rst
new file mode 100644
index 000000000000..21148b187856
--- /dev/null
+++ b/Documentation/userspace-api/media/drivers/mali-c55.rst
@@ -0,0 +1,55 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Arm Mali-C55 ISP driver
+=======================
+
+The Arm Mali-C55 ISP driver implements a single driver-specific control:
+
+``V4L2_CID_MALI_C55_CAPABILITIES (bitmask)``
+ Detail the capabilities of the ISP by giving detail about the fitted blocks.
+
+ .. flat-table:: Bitmask meaning definitions
+ :header-rows: 1
+ :widths: 2 4 8
+
+ * - Bit
+ - Macro
+ - Meaning
+ * - 0
+ - MALI_C55_PONG
+ - Pong configuration space is fitted in the ISP
+ * - 1
+ - MALI_C55_WDR
+ - WDR Framestitch, offset and gain is fitted in the ISP
+ * - 2
+ - MALI_C55_COMPRESSION
+ - Temper compression is fitted in the ISP
+ * - 3
+ - MALI_C55_TEMPER
+ - Temper is fitted in the ISP
+ * - 4
+ - MALI_C55_SINTER_LITE
+ - Sinter Lite is fitted in the ISP instead of the full Sinter version
+ * - 5
+ - MALI_C55_SINTER
+ - Sinter is fitted in the ISP
+ * - 6
+ - MALI_C55_IRIDIX_LTM
+ - Iridix local tone mappine is fitted in the ISP
+ * - 7
+ - MALI_C55_IRIDIX_GTM
+ - Iridix global tone mapping is fitted in the ISP
+ * - 8
+ - MALI_C55_CNR
+ - Colour noise reduction is fitted in the ISP
+ * - 9
+ - MALI_C55_FRSCALER
+ - The full resolution pipe scaler is fitted in the ISP
+ * - 10
+ - MALI_C55_DS_PIPE
+ - The downscale pipe is fitted in the ISP
+
+ The Mali-C55 ISP can be configured in a number of ways to include or exclude
+ blocks which may not be necessary. This control provides a way for the
+ driver to communicate to userspace which of the blocks are fitted in the
+ design. \ No newline at end of file
diff --git a/Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions b/Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions
index afc14d384b83..2c219388d123 100644
--- a/Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions
+++ b/Documentation/userspace-api/media/dvb/dmx.h.rst.exceptions
@@ -1,34 +1,36 @@
# SPDX-License-Identifier: GPL-2.0
+# All symbols belone to this namespace
+namespace DTV.dmx
+
# Ignore header name
ignore define _UAPI_DVBDMX_H_
# Ignore limit constants
ignore define DMX_FILTER_SIZE
-# dmx_pes_type_t enum symbols
-replace enum dmx_ts_pes :c:type:`dmx_pes_type`
-replace symbol DMX_PES_AUDIO0 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_VIDEO0 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_TELETEXT0 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_SUBTITLE0 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_PCR0 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_AUDIO1 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_VIDEO1 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_TELETEXT1 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_SUBTITLE1 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_PCR1 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_AUDIO2 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_VIDEO2 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_TELETEXT2 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_SUBTITLE2 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_PCR2 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_AUDIO3 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_VIDEO3 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_TELETEXT3 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_SUBTITLE3 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_PCR3 :c:type:`dmx_pes_type`
-replace symbol DMX_PES_OTHER :c:type:`dmx_pes_type`
+# dmx_ts_pes_type_t enum symbols
+replace symbol DMX_PES_AUDIO0 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_VIDEO0 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_TELETEXT0 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_SUBTITLE0 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_PCR0 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_AUDIO1 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_VIDEO1 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_TELETEXT1 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_SUBTITLE1 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_PCR1 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_AUDIO2 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_VIDEO2 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_TELETEXT2 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_SUBTITLE2 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_PCR2 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_AUDIO3 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_VIDEO3 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_TELETEXT3 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_SUBTITLE3 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_PCR3 :c:type:`DTV.dmx.dmx_ts_pes`
+replace symbol DMX_PES_OTHER :c:type:`DTV.dmx.dmx_ts_pes`
# Ignore obsolete symbols
ignore define DMX_PES_AUDIO
@@ -38,29 +40,23 @@ ignore define DMX_PES_SUBTITLE
ignore define DMX_PES_PCR
# dmx_input_t symbols
-replace enum dmx_input :c:type:`dmx_input`
-replace symbol DMX_IN_FRONTEND :c:type:`dmx_input`
-replace symbol DMX_IN_DVR :c:type:`dmx_input`
+replace symbol DMX_IN_FRONTEND :c:enum:`DTV.dmx.dmx_input`
+replace symbol DMX_IN_DVR :c:enum:`DTV.dmx.dmx_input`
# Flags for struct dmx_sct_filter_params
-replace define DMX_CHECK_CRC :c:type:`dmx_sct_filter_params`
-replace define DMX_ONESHOT :c:type:`dmx_sct_filter_params`
-replace define DMX_IMMEDIATE_START :c:type:`dmx_sct_filter_params`
-
-# some typedefs should point to struct/enums
-replace typedef dmx_filter_t :c:type:`dmx_filter`
-replace typedef dmx_pes_type_t :c:type:`dmx_pes_type`
-replace typedef dmx_input_t :c:type:`dmx_input`
-
-replace symbol DMX_BUFFER_FLAG_HAD_CRC32_DISCARD :c:type:`dmx_buffer_flags`
-replace symbol DMX_BUFFER_FLAG_TEI :c:type:`dmx_buffer_flags`
-replace symbol DMX_BUFFER_PKT_COUNTER_MISMATCH :c:type:`dmx_buffer_flags`
-replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED :c:type:`dmx_buffer_flags`
-replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR :c:type:`dmx_buffer_flags`
-
-replace symbol DMX_OUT_DECODER :c:type:`dmx_output`
-replace symbol DMX_OUT_TAP :c:type:`dmx_output`
-replace symbol DMX_OUT_TS_TAP :c:type:`dmx_output`
-replace symbol DMX_OUT_TSDEMUX_TAP :c:type:`dmx_output`
+replace define DMX_CHECK_CRC :c:type:`DTV.dmx.dmx_sct_filter_params`
+replace define DMX_ONESHOT :c:type:`DTV.dmx.dmx_sct_filter_params`
+replace define DMX_IMMEDIATE_START :c:type:`DTV.dmx.dmx_sct_filter_params`
+
+replace symbol DMX_BUFFER_FLAG_HAD_CRC32_DISCARD :c:type:`DTV.dmx.dmx_buffer_flags`
+replace symbol DMX_BUFFER_FLAG_TEI :c:type:`DTV.dmx.dmx_buffer_flags`
+replace symbol DMX_BUFFER_PKT_COUNTER_MISMATCH :c:type:`DTV.dmx.dmx_buffer_flags`
+replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_DETECTED :c:type:`DTV.dmx.dmx_buffer_flags`
+replace symbol DMX_BUFFER_FLAG_DISCONTINUITY_INDICATOR :c:type:`DTV.dmx.dmx_buffer_flags`
+
+replace symbol DMX_OUT_DECODER :c:type:`DTV.dmx.dmx_output`
+replace symbol DMX_OUT_TAP :c:type:`DTV.dmx.dmx_output`
+replace symbol DMX_OUT_TS_TAP :c:type:`DTV.dmx.dmx_output`
+replace symbol DMX_OUT_TSDEMUX_TAP :c:type:`DTV.dmx.dmx_output`
replace ioctl DMX_DQBUF dmx_qbuf
diff --git a/Documentation/userspace-api/media/dvb/dmx_types.rst b/Documentation/userspace-api/media/dvb/dmx_types.rst
index 33458fbb84ab..dd76010696c8 100644
--- a/Documentation/userspace-api/media/dvb/dmx_types.rst
+++ b/Documentation/userspace-api/media/dvb/dmx_types.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: DTV.dmx
.. _dmx_types:
diff --git a/Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions b/Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions
index dcaf5740de7e..cecd4087d4be 100644
--- a/Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions
+++ b/Documentation/userspace-api/media/dvb/frontend.h.rst.exceptions
@@ -28,13 +28,14 @@ ignore define MAX_DTV_STATS
ignore define DTV_IOCTL_MAX_MSGS
# the same reference is used for both get and set ioctls
-replace ioctl FE_SET_PROPERTY :c:type:`FE_GET_PROPERTY`
+replace ioctl FE_SET_PROPERTY :ref:`FE_GET_PROPERTY`
+replace ioctl FE_GET_PROPERTY :ref:`FE_GET_PROPERTY`
# Typedefs that use the enum reference
replace typedef fe_sec_voltage_t :c:type:`fe_sec_voltage`
# Replaces for flag constants
-replace define FE_TUNE_MODE_ONESHOT :c:func:`FE_SET_FRONTEND_TUNE_MODE`
+replace define FE_TUNE_MODE_ONESHOT :ref:`FE_SET_FRONTEND_TUNE_MODE`
replace define LNA_AUTO dtv-lna
replace define NO_STREAM_ID_FILTER dtv-stream-id
diff --git a/Documentation/userspace-api/media/dvb/headers.rst b/Documentation/userspace-api/media/dvb/headers.rst
index c75f64cf21d5..6d69622bf1e0 100644
--- a/Documentation/userspace-api/media/dvb/headers.rst
+++ b/Documentation/userspace-api/media/dvb/headers.rst
@@ -1,25 +1,46 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
-****************************
-Digital TV uAPI header files
-****************************
-
-Digital TV uAPI headers
***********************
+Digital TV uAPI symbols
+***********************
+
+.. contents:: Table of Contents
+ :depth: 2
+ :local:
+
+Frontend
+========
.. kernel-include:: include/uapi/linux/dvb/frontend.h
:generate-cross-refs:
:exception-file: frontend.h.rst.exceptions
+ :toc:
+ :warn-broken:
+
+Demux
+=====
.. kernel-include:: include/uapi/linux/dvb/dmx.h
:generate-cross-refs:
:exception-file: dmx.h.rst.exceptions
+ :toc:
+ :warn-broken:
+
+Conditional Access
+==================
.. kernel-include:: include/uapi/linux/dvb/ca.h
:generate-cross-refs:
:exception-file: ca.h.rst.exceptions
+ :toc:
+ :warn-broken:
+
+Network
+=======
.. kernel-include:: include/uapi/linux/dvb/net.h
:generate-cross-refs:
:exception-file: net.h.rst.exceptions
+ :toc:
+ :warn-broken:
diff --git a/Documentation/userspace-api/media/mediactl/media-header.rst b/Documentation/userspace-api/media/mediactl/media-header.rst
index d561d2845f3d..a47ac5b2e99b 100644
--- a/Documentation/userspace-api/media/mediactl/media-header.rst
+++ b/Documentation/userspace-api/media/mediactl/media-header.rst
@@ -2,10 +2,12 @@
.. _media_header:
-****************************
-Media Controller Header File
-****************************
+*****************************
+Media controller uAPI symbols
+*****************************
.. kernel-include:: include/uapi/linux/media.h
:generate-cross-refs:
:exception-file: media.h.rst.exceptions
+ :toc:
+ :warn-broken:
diff --git a/Documentation/userspace-api/media/mediactl/media.h.rst.exceptions b/Documentation/userspace-api/media/mediactl/media.h.rst.exceptions
index 9b4c26502d95..09aaec2b4718 100644
--- a/Documentation/userspace-api/media/mediactl/media.h.rst.exceptions
+++ b/Documentation/userspace-api/media/mediactl/media.h.rst.exceptions
@@ -1,5 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
+# All symbols are mapped inside MC C domain namespace
+namespace MC
+
# Ignore header name
ignore define __LINUX_MEDIA_H
diff --git a/Documentation/userspace-api/media/rc/lirc-header.rst b/Documentation/userspace-api/media/rc/lirc-header.rst
index a53328327847..ba4992625684 100644
--- a/Documentation/userspace-api/media/rc/lirc-header.rst
+++ b/Documentation/userspace-api/media/rc/lirc-header.rst
@@ -2,11 +2,19 @@
.. _lirc_header:
-****************
-LIRC Header File
-****************
+*****************
+LIRC uAPI symbols
+*****************
+
+.. contents:: Table of Contents
+ :depth: 2
+ :local:
+
+
.. kernel-include:: include/uapi/linux/lirc.h
:generate-cross-refs:
:exception-file: lirc.h.rst.exceptions
+ :toc:
+ :warn-broken:
diff --git a/Documentation/userspace-api/media/v4l/app-pri.rst b/Documentation/userspace-api/media/v4l/app-pri.rst
index 626a42f2e138..47d96cd64525 100644
--- a/Documentation/userspace-api/media/v4l/app-pri.rst
+++ b/Documentation/userspace-api/media/v4l/app-pri.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _app-pri:
diff --git a/Documentation/userspace-api/media/v4l/audio.rst b/Documentation/userspace-api/media/v4l/audio.rst
index 17f0b1c89908..3b424440e478 100644
--- a/Documentation/userspace-api/media/v4l/audio.rst
+++ b/Documentation/userspace-api/media/v4l/audio.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _audio:
diff --git a/Documentation/userspace-api/media/v4l/biblio.rst b/Documentation/userspace-api/media/v4l/biblio.rst
index 856acf6a890c..9721247e66ef 100644
--- a/Documentation/userspace-api/media/v4l/biblio.rst
+++ b/Documentation/userspace-api/media/v4l/biblio.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
**********
References
diff --git a/Documentation/userspace-api/media/v4l/buffer.rst b/Documentation/userspace-api/media/v4l/buffer.rst
index 856874341882..94dc2719e907 100644
--- a/Documentation/userspace-api/media/v4l/buffer.rst
+++ b/Documentation/userspace-api/media/v4l/buffer.rst
@@ -667,6 +667,8 @@ Buffer Flags
exposure of the frame has begun. This is only valid for the
``V4L2_BUF_TYPE_VIDEO_CAPTURE`` buffer type.
+.. c:enum:: v4l2_memory
+
.. raw:: latex
\normalsize
diff --git a/Documentation/userspace-api/media/v4l/capture-example.rst b/Documentation/userspace-api/media/v4l/capture-example.rst
index 25891320b7ad..f018a654f59c 100644
--- a/Documentation/userspace-api/media/v4l/capture-example.rst
+++ b/Documentation/userspace-api/media/v4l/capture-example.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _capture-example:
diff --git a/Documentation/userspace-api/media/v4l/capture.c.rst b/Documentation/userspace-api/media/v4l/capture.c.rst
index 349541b1dac0..aae17e1e844a 100644
--- a/Documentation/userspace-api/media/v4l/capture.c.rst
+++ b/Documentation/userspace-api/media/v4l/capture.c.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
file: media/v4l/capture.c
=========================
diff --git a/Documentation/userspace-api/media/v4l/colorspaces-defs.rst b/Documentation/userspace-api/media/v4l/colorspaces-defs.rst
index fe9f8aa8ab9d..0b40e735f3bf 100644
--- a/Documentation/userspace-api/media/v4l/colorspaces-defs.rst
+++ b/Documentation/userspace-api/media/v4l/colorspaces-defs.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
****************************
Defining Colorspaces in V4L2
diff --git a/Documentation/userspace-api/media/v4l/colorspaces-details.rst b/Documentation/userspace-api/media/v4l/colorspaces-details.rst
index 26a4ace42ca5..f29b0e3978bc 100644
--- a/Documentation/userspace-api/media/v4l/colorspaces-details.rst
+++ b/Documentation/userspace-api/media/v4l/colorspaces-details.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
********************************
Detailed Colorspace Descriptions
diff --git a/Documentation/userspace-api/media/v4l/colorspaces.rst b/Documentation/userspace-api/media/v4l/colorspaces.rst
index 2aa0dda4fd01..11954d7cf999 100644
--- a/Documentation/userspace-api/media/v4l/colorspaces.rst
+++ b/Documentation/userspace-api/media/v4l/colorspaces.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _colorspaces:
diff --git a/Documentation/userspace-api/media/v4l/common-defs.rst b/Documentation/userspace-api/media/v4l/common-defs.rst
index 6ae42ac7ddb7..329ba6ec760b 100644
--- a/Documentation/userspace-api/media/v4l/common-defs.rst
+++ b/Documentation/userspace-api/media/v4l/common-defs.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _common-defs:
diff --git a/Documentation/userspace-api/media/v4l/common.rst b/Documentation/userspace-api/media/v4l/common.rst
index ea0435182e44..507c35fec8ce 100644
--- a/Documentation/userspace-api/media/v4l/common.rst
+++ b/Documentation/userspace-api/media/v4l/common.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _common:
diff --git a/Documentation/userspace-api/media/v4l/compat.rst b/Documentation/userspace-api/media/v4l/compat.rst
index b63b8392dec6..f766ea89f9ff 100644
--- a/Documentation/userspace-api/media/v4l/compat.rst
+++ b/Documentation/userspace-api/media/v4l/compat.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _compat:
diff --git a/Documentation/userspace-api/media/v4l/control.rst b/Documentation/userspace-api/media/v4l/control.rst
index 9253cc946f02..19372bb32c4b 100644
--- a/Documentation/userspace-api/media/v4l/control.rst
+++ b/Documentation/userspace-api/media/v4l/control.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _control:
diff --git a/Documentation/userspace-api/media/v4l/crop.rst b/Documentation/userspace-api/media/v4l/crop.rst
index 3fe185e25ccf..c5f389aca275 100644
--- a/Documentation/userspace-api/media/v4l/crop.rst
+++ b/Documentation/userspace-api/media/v4l/crop.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _crop:
diff --git a/Documentation/userspace-api/media/v4l/depth-formats.rst b/Documentation/userspace-api/media/v4l/depth-formats.rst
index b4f3fc229c85..bd61064d51d3 100644
--- a/Documentation/userspace-api/media/v4l/depth-formats.rst
+++ b/Documentation/userspace-api/media/v4l/depth-formats.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _depth-formats:
diff --git a/Documentation/userspace-api/media/v4l/dev-decoder.rst b/Documentation/userspace-api/media/v4l/dev-decoder.rst
index ef8e8cf31f90..eb662ced0ab4 100644
--- a/Documentation/userspace-api/media/v4l/dev-decoder.rst
+++ b/Documentation/userspace-api/media/v4l/dev-decoder.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
+.. c:namespace:: V4L
.. _decoder:
diff --git a/Documentation/userspace-api/media/v4l/dev-encoder.rst b/Documentation/userspace-api/media/v4l/dev-encoder.rst
index 6c523c69bdce..cdad276d00bc 100644
--- a/Documentation/userspace-api/media/v4l/dev-encoder.rst
+++ b/Documentation/userspace-api/media/v4l/dev-encoder.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _encoder:
diff --git a/Documentation/userspace-api/media/v4l/dev-event.rst b/Documentation/userspace-api/media/v4l/dev-event.rst
index f34f9cf6ce6c..f61d774f0153 100644
--- a/Documentation/userspace-api/media/v4l/dev-event.rst
+++ b/Documentation/userspace-api/media/v4l/dev-event.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _event:
diff --git a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst
index 7041bb3d5b8d..6058eeaefca4 100644
--- a/Documentation/userspace-api/media/v4l/dev-mem2mem.rst
+++ b/Documentation/userspace-api/media/v4l/dev-mem2mem.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _mem2mem:
diff --git a/Documentation/userspace-api/media/v4l/dev-meta.rst b/Documentation/userspace-api/media/v4l/dev-meta.rst
index 5eee9ab60395..da706d01b808 100644
--- a/Documentation/userspace-api/media/v4l/dev-meta.rst
+++ b/Documentation/userspace-api/media/v4l/dev-meta.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _metadata:
diff --git a/Documentation/userspace-api/media/v4l/dev-osd.rst b/Documentation/userspace-api/media/v4l/dev-osd.rst
index 8e4be9129e75..f37450b1b631 100644
--- a/Documentation/userspace-api/media/v4l/dev-osd.rst
+++ b/Documentation/userspace-api/media/v4l/dev-osd.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _osd:
diff --git a/Documentation/userspace-api/media/v4l/dev-overlay.rst b/Documentation/userspace-api/media/v4l/dev-overlay.rst
index d52977120b41..b7f4302eee6e 100644
--- a/Documentation/userspace-api/media/v4l/dev-overlay.rst
+++ b/Documentation/userspace-api/media/v4l/dev-overlay.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _overlay:
diff --git a/Documentation/userspace-api/media/v4l/dev-radio.rst b/Documentation/userspace-api/media/v4l/dev-radio.rst
index 284ce96a1637..843a2151f470 100644
--- a/Documentation/userspace-api/media/v4l/dev-radio.rst
+++ b/Documentation/userspace-api/media/v4l/dev-radio.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _radio:
diff --git a/Documentation/userspace-api/media/v4l/dev-sdr.rst b/Documentation/userspace-api/media/v4l/dev-sdr.rst
index dfdeddbca41f..99907adc0628 100644
--- a/Documentation/userspace-api/media/v4l/dev-sdr.rst
+++ b/Documentation/userspace-api/media/v4l/dev-sdr.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _sdr:
diff --git a/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst b/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst
index 35ed05f2695e..e311f0c13272 100644
--- a/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst
+++ b/Documentation/userspace-api/media/v4l/dev-stateless-decoder.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
+.. c:namespace:: V4L
.. _stateless_decoder:
diff --git a/Documentation/userspace-api/media/v4l/dev-subdev.rst b/Documentation/userspace-api/media/v4l/dev-subdev.rst
index 225a45fef994..2530170a56ae 100644
--- a/Documentation/userspace-api/media/v4l/dev-subdev.rst
+++ b/Documentation/userspace-api/media/v4l/dev-subdev.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _subdev:
diff --git a/Documentation/userspace-api/media/v4l/dev-touch.rst b/Documentation/userspace-api/media/v4l/dev-touch.rst
index a71b9def5d58..808957cd9afc 100644
--- a/Documentation/userspace-api/media/v4l/dev-touch.rst
+++ b/Documentation/userspace-api/media/v4l/dev-touch.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _touch:
diff --git a/Documentation/userspace-api/media/v4l/devices.rst b/Documentation/userspace-api/media/v4l/devices.rst
index 8bfbad65a9d4..d4790b9ef81a 100644
--- a/Documentation/userspace-api/media/v4l/devices.rst
+++ b/Documentation/userspace-api/media/v4l/devices.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _devices:
diff --git a/Documentation/userspace-api/media/v4l/dv-timings.rst b/Documentation/userspace-api/media/v4l/dv-timings.rst
index 4b19bcb4bd80..9f117c82df1b 100644
--- a/Documentation/userspace-api/media/v4l/dv-timings.rst
+++ b/Documentation/userspace-api/media/v4l/dv-timings.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _dv-timings:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-camera.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-camera.rst
index cdc515c60468..b4daa7e28dc0 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-camera.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-camera.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _camera-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst
index 0da635691fdc..497ae74379f6 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _codec-stateless-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst
index 4a379bd9e3fb..c8890cb5e00a 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _codec-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst
index 1e7265155715..38a4136d7220 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-colorimetry.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _colorimetry-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-detect.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-detect.rst
index 312c4fa94dc3..ee2b7e37c1d9 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-detect.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-detect.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _detect-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-dv.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-dv.rst
index d2794e03ac6d..5918dde83efb 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-dv.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-dv.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _dv-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst
index d22c5efb806a..bd024ab461a4 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-flash.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _flash-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst
index ccd439e9e0e3..b7284768f7ea 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-rx.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _fm-rx-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst
index cb40cf4cc3ec..7143a4c08f78 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-fm-tx.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _fm-tx-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst
index 27803dca8d3e..6d516f041ca2 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _image-process-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst
index 71f23f131f97..f9c0b7ad3b4e 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-source.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _image-source-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-jpeg.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-jpeg.rst
index 522095c08469..b114650bca5b 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-jpeg.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-jpeg.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _jpeg-controls:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-rf-tuner.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-rf-tuner.rst
index 8a6f9f0373ff..f50802a1c4d4 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-rf-tuner.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-rf-tuner.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _rf-tuner-controls:
diff --git a/Documentation/userspace-api/media/v4l/extended-controls.rst b/Documentation/userspace-api/media/v4l/extended-controls.rst
index 44fcd67f20bf..5fe71da6afc0 100644
--- a/Documentation/userspace-api/media/v4l/extended-controls.rst
+++ b/Documentation/userspace-api/media/v4l/extended-controls.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _extended-controls:
diff --git a/Documentation/userspace-api/media/v4l/field-order.rst b/Documentation/userspace-api/media/v4l/field-order.rst
index 9a0ed8fc550f..2a01852513b3 100644
--- a/Documentation/userspace-api/media/v4l/field-order.rst
+++ b/Documentation/userspace-api/media/v4l/field-order.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _field-order:
diff --git a/Documentation/userspace-api/media/v4l/fourcc.rst b/Documentation/userspace-api/media/v4l/fourcc.rst
index d3482c40da62..5cea7008814f 100644
--- a/Documentation/userspace-api/media/v4l/fourcc.rst
+++ b/Documentation/userspace-api/media/v4l/fourcc.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
+.. c:namespace:: V4L
Guidelines for Video4Linux pixel format 4CCs
============================================
diff --git a/Documentation/userspace-api/media/v4l/hsv-formats.rst b/Documentation/userspace-api/media/v4l/hsv-formats.rst
index d810c914b673..f0731de6f038 100644
--- a/Documentation/userspace-api/media/v4l/hsv-formats.rst
+++ b/Documentation/userspace-api/media/v4l/hsv-formats.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _hsv-formats:
diff --git a/Documentation/userspace-api/media/v4l/libv4l.rst b/Documentation/userspace-api/media/v4l/libv4l.rst
index f446dd2d01ac..ce7a0400891c 100644
--- a/Documentation/userspace-api/media/v4l/libv4l.rst
+++ b/Documentation/userspace-api/media/v4l/libv4l.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _libv4l:
diff --git a/Documentation/userspace-api/media/v4l/meta-formats.rst b/Documentation/userspace-api/media/v4l/meta-formats.rst
index 0de80328c36b..3e0cab153f0a 100644
--- a/Documentation/userspace-api/media/v4l/meta-formats.rst
+++ b/Documentation/userspace-api/media/v4l/meta-formats.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _meta-formats:
@@ -12,6 +13,7 @@ These formats are used for the :ref:`metadata` interface only.
.. toctree::
:maxdepth: 1
+ metafmt-arm-mali-c55
metafmt-c3-isp
metafmt-d4xx
metafmt-generic
@@ -24,3 +26,4 @@ These formats are used for the :ref:`metadata` interface only.
metafmt-vivid
metafmt-vsp1-hgo
metafmt-vsp1-hgt
+ v4l2-isp
diff --git a/Documentation/userspace-api/media/v4l/metafmt-arm-mali-c55.rst b/Documentation/userspace-api/media/v4l/metafmt-arm-mali-c55.rst
new file mode 100644
index 000000000000..696e0a645a7e
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/metafmt-arm-mali-c55.rst
@@ -0,0 +1,84 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _v4l2-meta-fmt-mali-c55-params:
+.. _v4l2-meta-fmt-mali-c55-stats:
+
+*****************************************************************************
+V4L2_META_FMT_MALI_C55_STATS ('C55S'), V4L2_META_FMT_MALI_C55_PARAMS ('C55P')
+*****************************************************************************
+
+3A Statistics
+=============
+
+The ISP device collects different statistics over an input bayer frame. Those
+statistics can be obtained by userspace from the
+:ref:`mali-c55 3a stats <mali-c55-3a-stats>` metadata capture video node, using
+the :c:type:`v4l2_meta_format` interface. The buffer contains a single instance
+of the C structure :c:type:`mali_c55_stats_buffer` defined in
+``mali-c55-config.h``, so the structure can be obtained from the buffer by:
+
+.. code-block:: C
+
+ struct mali_c55_stats_buffer *stats =
+ (struct mali_c55_stats_buffer *)buf;
+
+For details of the statistics see :c:type:`mali_c55_stats_buffer`.
+
+Configuration Parameters
+========================
+
+The configuration parameters are passed to the :ref:`mali-c55 3a params
+<mali-c55-3a-params>` metadata output video node, using the
+:c:type:`v4l2_meta_format` interface. Rather than a single struct containing
+sub-structs for each configurable area of the ISP, parameters for the Mali-C55
+use the v4l2-isp parameters system, through which groups of parameters are
+defined as distinct structs or "blocks" which may be added to the data member of
+:c:type:`v4l2_isp_params_buffer`. Userspace is responsible for populating the
+data member with the blocks that need to be configured by the driver. Each
+block-specific struct embeds :c:type:`v4l2_isp_params_block_header` as its first
+member and userspace must populate the type member with a value from
+:c:type:`mali_c55_param_block_type`.
+
+.. code-block:: c
+
+ struct v4l2_isp_params_buffer *params =
+ (struct v4l2_isp_params_buffer *)buffer;
+
+ params->version = MALI_C55_PARAM_BUFFER_V1;
+ params->data_size = 0;
+
+ void *data = (void *)params->data;
+
+ struct mali_c55_params_awb_gains *gains =
+ (struct mali_c55_params_awb_gains *)data;
+
+ gains->header.type = MALI_C55_PARAM_BLOCK_AWB_GAINS;
+ gains->header.flags |= V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+ gains->header.size = sizeof(struct mali_c55_params_awb_gains);
+
+ gains->gain00 = 256;
+ gains->gain00 = 256;
+ gains->gain00 = 256;
+ gains->gain00 = 256;
+
+ data += sizeof(struct mali_c55_params_awb_gains);
+ params->data_size += sizeof(struct mali_c55_params_awb_gains);
+
+ struct mali_c55_params_sensor_off_preshading *blc =
+ (struct mali_c55_params_sensor_off_preshading *)data;
+
+ blc->header.type = MALI_C55_PARAM_BLOCK_SENSOR_OFFS;
+ blc->header.flags |= V4L2_ISP_PARAMS_FL_BLOCK_ENABLE;
+ blc->header.size = sizeof(struct mali_c55_params_sensor_off_preshading);
+
+ blc->chan00 = 51200;
+ blc->chan01 = 51200;
+ blc->chan10 = 51200;
+ blc->chan11 = 51200;
+
+ params->data_size += sizeof(struct mali_c55_params_sensor_off_preshading);
+
+Arm Mali-C55 uAPI data types
+============================
+
+.. kernel-doc:: include/uapi/linux/media/arm/mali-c55-config.h
diff --git a/Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst b/Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst
index 449b45c2ec24..24359601ae25 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-c3-isp.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-c3isp-stats:
.. _v4l2-meta-fmt-c3isp-params:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-d4xx.rst b/Documentation/userspace-api/media/v4l/metafmt-d4xx.rst
index 0686413b16b2..d716170bb795 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-d4xx.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-d4xx.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-d4xx:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-generic.rst b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
index 39d529c910a8..23f69e1a1afa 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-generic.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-generic.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
********************************************************************************************************************************************************************************************************************************************************************************
V4L2_META_FMT_GENERIC_8 ('MET8'), V4L2_META_FMT_GENERIC_CSI2_10 ('MC1A'), V4L2_META_FMT_GENERIC_CSI2_12 ('MC1C'), V4L2_META_FMT_GENERIC_CSI2_14 ('MC1E'), V4L2_META_FMT_GENERIC_CSI2_16 ('MC1G'), V4L2_META_FMT_GENERIC_CSI2_20 ('MC1K'), V4L2_META_FMT_GENERIC_CSI2_24 ('MC1O')
diff --git a/Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst b/Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
index 84d81dd7a7b5..c11d17e5a286 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-intel-ipu3.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-params:
.. _v4l2-meta-fmt-stat-3a:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst b/Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst
index 3281fe366c86..e230177910f4 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-pisp-be.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-rpi-be-cfg:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst b/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst
index fddeada83e4a..a2aa5c4c2920 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-pisp-fe.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-rpi-fe-cfg:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst b/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst
index 959f6bde8695..d3c899b0150b 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-rkisp1.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-rk-isp1-stat-3a:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-uvc.rst b/Documentation/userspace-api/media/v4l/metafmt-uvc.rst
index 4c05e9e54683..c4ae2f13e7f7 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-uvc.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-uvc.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-uvc:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-vivid.rst b/Documentation/userspace-api/media/v4l/metafmt-vivid.rst
index 7173e2c3e245..94f28736dc80 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-vivid.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-vivid.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0 OR GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-vivid:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgo.rst b/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgo.rst
index 8d886feb180c..70357899a5f0 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgo.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgo.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-vsp1-hgo:
diff --git a/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgt.rst b/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgt.rst
index d8830ff605de..4a1575a9e728 100644
--- a/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgt.rst
+++ b/Documentation/userspace-api/media/v4l/metafmt-vsp1-hgt.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-meta-fmt-vsp1-hgt:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst b/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst
index b5ca501842b0..8b4e413177f5 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-bayer.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _pixfmt-bayer:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-cnf4.rst b/Documentation/userspace-api/media/v4l/pixfmt-cnf4.rst
index 8f469290c304..4e3e9c5f4387 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-cnf4.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-cnf4.rst
@@ -1,4 +1,5 @@
.. -*- coding: utf-8; mode: rst -*-
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-CNF4:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst b/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst
index 806ed73ac474..c7efb0465db6 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-compressed.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
******************
Compressed Formats
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-indexed.rst b/Documentation/userspace-api/media/v4l/pixfmt-indexed.rst
index 5bd4a47c5854..08d698ebdd64 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-indexed.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-indexed.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _pixfmt-indexed:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-intro.rst b/Documentation/userspace-api/media/v4l/pixfmt-intro.rst
index 14239ee826bf..00c1b7b0e907 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-intro.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-intro.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
**********************
Standard Image Formats
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-inzi.rst b/Documentation/userspace-api/media/v4l/pixfmt-inzi.rst
index 3115c8f6a842..0af3a303a7f0 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-inzi.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-inzi.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-INZI:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-m420.rst b/Documentation/userspace-api/media/v4l/pixfmt-m420.rst
index c01a949e7c11..f44a6c5eaddf 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-m420.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-m420.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-M420:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-packed-hsv.rst b/Documentation/userspace-api/media/v4l/pixfmt-packed-hsv.rst
index dd89860f50e0..d1cab3c632f1 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-packed-hsv.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-packed-hsv.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _packed-hsv:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst b/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst
index 9f111ed594d2..ae7d88ae1c3d 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-packed-yuv.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _packed-yuv:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
index ac52485252d9..1cf6c59f4bc4 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-reserved.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _pixfmt-reserved:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst b/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst
index 5ed4d62df909..cf6760bb6109 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-rgb.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _pixfmt-rgb:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs08.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs08.rst
index bd6ee6111de4..b2dda90409d9 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs08.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs08.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-sdr-fmt-cs8:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs14le.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs14le.rst
index ea21b288d357..df8b4b22ebce 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs14le.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cs14le.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-SDR-FMT-CS14LE:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu08.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu08.rst
index 45fce09d85ff..86accef8f9f4 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu08.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu08.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-sdr-fmt-cu8:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu16le.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu16le.rst
index 7f4242f8da6f..13d8c86e9b1d 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu16le.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-cu16le.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-SDR-FMT-CU16LE:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu16be.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu16be.rst
index a4d4b70ece63..9c4908d57a25 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu16be.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu16be.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-SDR-FMT-PCU16BE:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu18be.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu18be.rst
index 3db690bd683a..56c3f2aee0a4 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu18be.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu18be.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-SDR-FMT-PCU18BE:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu20be.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu20be.rst
index 485343cdf150..1992ee5dd2bc 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu20be.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-pcu20be.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-SDR-FMT-PCU20BE:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-sdr-ru12le.rst b/Documentation/userspace-api/media/v4l/pixfmt-sdr-ru12le.rst
index 2ad4706bfc7a..3b2a94e64bcd 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-sdr-ru12le.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-sdr-ru12le.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-SDR-FMT-RU12LE:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst
index 3322b0600f1d..de4720dba48a 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb10-ipu3.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-pix-fmt-ipu3-sbggr10:
.. _v4l2-pix-fmt-ipu3-sgbrg10:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb10.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb10.rst
index a66414ab4291..6b4950ad54e0 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb10.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb10.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB10:
.. _v4l2-pix-fmt-sbggr10:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb10alaw8.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb10alaw8.rst
index a5ae1f099e68..42176c437ebe 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb10alaw8.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb10alaw8.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SBGGR10ALAW8:
.. _v4l2-pix-fmt-sgbrg10alaw8:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb10dpcm8.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb10dpcm8.rst
index f0544c6f4580..dac580181562 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb10dpcm8.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb10dpcm8.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SBGGR10DPCM8:
.. _v4l2-pix-fmt-sgbrg10dpcm8:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb10p.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb10p.rst
index fd5feb415531..af91f12c24e8 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb10p.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb10p.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB10P:
.. _v4l2-pix-fmt-sbggr10p:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb12.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb12.rst
index 15c34e1e4835..2b3212ef732f 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb12.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb12.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB12:
.. _v4l2-pix-fmt-sbggr12:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst
index 8c03aedcc00e..cffb3dfc338a 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb12p.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB12P:
.. _v4l2-pix-fmt-sbggr12p:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb14.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb14.rst
index 4f5120a6c678..7a3552c045a3 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb14.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb14.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB14:
.. _v4l2-pix-fmt-sbggr14:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst
index f4f53d7dbdeb..330197aded11 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb14p.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB14P:
.. _v4l2-pix-fmt-sbggr14p:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb16.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb16.rst
index 2f2f1ef430d9..dab222f49522 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb16.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb16.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB16:
.. _v4l2-pix-fmt-sbggr16:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst
index 5a82a15559d6..7a55a3c7c9f6 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb8-pisp-comp.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-pix-fmt-pisp-comp1-rggb:
.. _v4l2-pix-fmt-pisp-comp1-grbg:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-srggb8.rst b/Documentation/userspace-api/media/v4l/pixfmt-srggb8.rst
index 02061c5a9778..4cb263a6ea26 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-srggb8.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-srggb8.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-SRGGB8:
.. _v4l2-pix-fmt-sbggr8:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-tch-td08.rst b/Documentation/userspace-api/media/v4l/pixfmt-tch-td08.rst
index ec89f43c60ec..37b03da07cf4 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-tch-td08.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-tch-td08.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-TCH-FMT-DELTA-TD08:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-tch-td16.rst b/Documentation/userspace-api/media/v4l/pixfmt-tch-td16.rst
index 7b59a6424243..66d3d7041550 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-tch-td16.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-tch-td16.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-TCH-FMT-DELTA-TD16:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-tch-tu08.rst b/Documentation/userspace-api/media/v4l/pixfmt-tch-tu08.rst
index 63c5264b8668..b10bb7bad025 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-tch-tu08.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-tch-tu08.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-TCH-FMT-TU08:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-tch-tu16.rst b/Documentation/userspace-api/media/v4l/pixfmt-tch-tu16.rst
index ade618a037a8..889298d010a1 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-tch-tu16.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-tch-tu16.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-TCH-FMT-TU16:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-uv8.rst b/Documentation/userspace-api/media/v4l/pixfmt-uv8.rst
index ff1d73ef5dba..8fa97b57ad75 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-uv8.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-uv8.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-UV8:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-v4l2-mplane.rst b/Documentation/userspace-api/media/v4l/pixfmt-v4l2-mplane.rst
index ad4da988c3a3..7069b2a6b0b1 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-v4l2-mplane.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-v4l2-mplane.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
******************************
Multi-planar format structures
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst b/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst
index 9c423ffe02f9..995267741a26 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-v4l2.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
******************************
Single-planar format structure
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y12i.rst b/Documentation/userspace-api/media/v4l/pixfmt-y12i.rst
index d9b539381d74..b4223ce3506c 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-y12i.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-y12i.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-Y12I:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y16i.rst b/Documentation/userspace-api/media/v4l/pixfmt-y16i.rst
index 74ba9e910a38..51f216f24e41 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-y16i.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-y16i.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-Y16I:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-y8i.rst b/Documentation/userspace-api/media/v4l/pixfmt-y8i.rst
index 770ed4749c14..c5a3648d37e5 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-y8i.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-y8i.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-Y8I:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst
index 74df19be91f6..99bcc6d385b7 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-luma.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _yuv-luma-only:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst
index 6e4f399f1f88..0631919bd667 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-yuv-planar.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. planar-yuv:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt-z16.rst b/Documentation/userspace-api/media/v4l/pixfmt-z16.rst
index 54a8cd723d1a..3ab8844d2e6f 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt-z16.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt-z16.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _V4L2-PIX-FMT-Z16:
diff --git a/Documentation/userspace-api/media/v4l/pixfmt.rst b/Documentation/userspace-api/media/v4l/pixfmt.rst
index 11dab4a90630..71b29267488f 100644
--- a/Documentation/userspace-api/media/v4l/pixfmt.rst
+++ b/Documentation/userspace-api/media/v4l/pixfmt.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _pixfmt:
diff --git a/Documentation/userspace-api/media/v4l/planar-apis.rst b/Documentation/userspace-api/media/v4l/planar-apis.rst
index 9207ce4283df..075754f0e423 100644
--- a/Documentation/userspace-api/media/v4l/planar-apis.rst
+++ b/Documentation/userspace-api/media/v4l/planar-apis.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _planar-apis:
diff --git a/Documentation/userspace-api/media/v4l/querycap.rst b/Documentation/userspace-api/media/v4l/querycap.rst
index 15a90271af45..c96f7654c870 100644
--- a/Documentation/userspace-api/media/v4l/querycap.rst
+++ b/Documentation/userspace-api/media/v4l/querycap.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _querycap:
diff --git a/Documentation/userspace-api/media/v4l/sdr-formats.rst b/Documentation/userspace-api/media/v4l/sdr-formats.rst
index d8bdfdb56911..df76b7d2aaf2 100644
--- a/Documentation/userspace-api/media/v4l/sdr-formats.rst
+++ b/Documentation/userspace-api/media/v4l/sdr-formats.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _sdr-formats:
diff --git a/Documentation/userspace-api/media/v4l/selection-api-configuration.rst b/Documentation/userspace-api/media/v4l/selection-api-configuration.rst
index fee49bf1a1c0..978c401f4252 100644
--- a/Documentation/userspace-api/media/v4l/selection-api-configuration.rst
+++ b/Documentation/userspace-api/media/v4l/selection-api-configuration.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
*************
Configuration
diff --git a/Documentation/userspace-api/media/v4l/selection-api-examples.rst b/Documentation/userspace-api/media/v4l/selection-api-examples.rst
index 5f8e8a1f59d7..3cf7c067f20f 100644
--- a/Documentation/userspace-api/media/v4l/selection-api-examples.rst
+++ b/Documentation/userspace-api/media/v4l/selection-api-examples.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
********
Examples
diff --git a/Documentation/userspace-api/media/v4l/selection-api-intro.rst b/Documentation/userspace-api/media/v4l/selection-api-intro.rst
index 6534854ae9f7..817e660ce016 100644
--- a/Documentation/userspace-api/media/v4l/selection-api-intro.rst
+++ b/Documentation/userspace-api/media/v4l/selection-api-intro.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
************
Introduction
diff --git a/Documentation/userspace-api/media/v4l/selection-api-targets.rst b/Documentation/userspace-api/media/v4l/selection-api-targets.rst
index 50fdadd5b307..e1aaaa3a7123 100644
--- a/Documentation/userspace-api/media/v4l/selection-api-targets.rst
+++ b/Documentation/userspace-api/media/v4l/selection-api-targets.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
*****************
Selection targets
diff --git a/Documentation/userspace-api/media/v4l/selection-api-vs-crop-api.rst b/Documentation/userspace-api/media/v4l/selection-api-vs-crop-api.rst
index f57b9180012c..0c0d66a0cfe6 100644
--- a/Documentation/userspace-api/media/v4l/selection-api-vs-crop-api.rst
+++ b/Documentation/userspace-api/media/v4l/selection-api-vs-crop-api.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _selection-vs-crop:
diff --git a/Documentation/userspace-api/media/v4l/selection-api.rst b/Documentation/userspace-api/media/v4l/selection-api.rst
index 0360743746dc..1320eb632272 100644
--- a/Documentation/userspace-api/media/v4l/selection-api.rst
+++ b/Documentation/userspace-api/media/v4l/selection-api.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _selection-api:
diff --git a/Documentation/userspace-api/media/v4l/selections-common.rst b/Documentation/userspace-api/media/v4l/selections-common.rst
index 322b39cf0eba..e08da7a9b599 100644
--- a/Documentation/userspace-api/media/v4l/selections-common.rst
+++ b/Documentation/userspace-api/media/v4l/selections-common.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-selections-common:
diff --git a/Documentation/userspace-api/media/v4l/standard.rst b/Documentation/userspace-api/media/v4l/standard.rst
index 1f6678325da9..53818e37db5f 100644
--- a/Documentation/userspace-api/media/v4l/standard.rst
+++ b/Documentation/userspace-api/media/v4l/standard.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _standard:
diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst
index 2a94371448dc..cf970750dd4c 100644
--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
+++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-mbus-format:
@@ -2224,6 +2225,174 @@ The following table list existing packed 48bit wide RGB formats.
\endgroup
+The following table list existing packed 60bit wide RGB formats.
+
+.. tabularcolumns:: |p{4.0cm}|p{0.7cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|
+
+.. _v4l2-mbus-pixelcode-rgb-60:
+
+.. raw:: latex
+
+ \begingroup
+ \tiny
+ \setlength{\tabcolsep}{2pt}
+
+.. flat-table:: 60bit RGB formats
+ :header-rows: 3
+ :stub-columns: 0
+ :widths: 36 7 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
+
+ * - Identifier
+ - Code
+ -
+ - :cspan:`31` Data organization
+ * -
+ -
+ - Bit
+ -
+ -
+ -
+ -
+ - 59
+ - 58
+ - 57
+ - 56
+ - 55
+ - 54
+ - 53
+ - 52
+ - 51
+ - 50
+ - 49
+ - 48
+ - 47
+ - 46
+ - 45
+ - 44
+ - 43
+ - 42
+ - 41
+ - 40
+ - 39
+ - 38
+ - 37
+ - 36
+ - 35
+ - 34
+ - 33
+ - 32
+ * -
+ -
+ -
+ - 31
+ - 30
+ - 29
+ - 28
+ - 27
+ - 26
+ - 25
+ - 24
+ - 23
+ - 22
+ - 21
+ - 20
+ - 19
+ - 18
+ - 17
+ - 16
+ - 15
+ - 14
+ - 13
+ - 12
+ - 11
+ - 10
+ - 9
+ - 8
+ - 7
+ - 6
+ - 5
+ - 4
+ - 3
+ - 2
+ - 1
+ - 0
+ * .. _MEDIA-BUS-FMT-RGB202020-1X60:
+
+ - MEDIA_BUS_FMT_RGB202020_1X60
+ - 0x1026
+ -
+ -
+ -
+ -
+ -
+ - r\ :sub:`19`
+ - r\ :sub:`18`
+ - r\ :sub:`17`
+ - r\ :sub:`16`
+ - r\ :sub:`15`
+ - r\ :sub:`14`
+ - r\ :sub:`13`
+ - r\ :sub:`12`
+ - r\ :sub:`11`
+ - r\ :sub:`10`
+ - r\ :sub:`9`
+ - r\ :sub:`8`
+ - r\ :sub:`7`
+ - r\ :sub:`6`
+ - r\ :sub:`5`
+ - r\ :sub:`4`
+ - r\ :sub:`3`
+ - r\ :sub:`2`
+ - r\ :sub:`1`
+ - r\ :sub:`0`
+ - g\ :sub:`19`
+ - g\ :sub:`18`
+ - g\ :sub:`17`
+ - g\ :sub:`16`
+ - g\ :sub:`15`
+ - g\ :sub:`14`
+ - g\ :sub:`13`
+ - g\ :sub:`12`
+ * -
+ -
+ -
+ - g\ :sub:`11`
+ - g\ :sub:`10`
+ - g\ :sub:`9`
+ - g\ :sub:`8`
+ - g\ :sub:`7`
+ - g\ :sub:`6`
+ - g\ :sub:`5`
+ - g\ :sub:`4`
+ - g\ :sub:`3`
+ - g\ :sub:`2`
+ - g\ :sub:`1`
+ - g\ :sub:`0`
+ - b\ :sub:`19`
+ - b\ :sub:`18`
+ - b\ :sub:`17`
+ - b\ :sub:`16`
+ - b\ :sub:`15`
+ - b\ :sub:`14`
+ - b\ :sub:`13`
+ - b\ :sub:`12`
+ - b\ :sub:`11`
+ - b\ :sub:`10`
+ - b\ :sub:`9`
+ - b\ :sub:`8`
+ - b\ :sub:`7`
+ - b\ :sub:`6`
+ - b\ :sub:`5`
+ - b\ :sub:`4`
+ - b\ :sub:`3`
+ - b\ :sub:`2`
+ - b\ :sub:`1`
+ - b\ :sub:`0`
+
+.. raw:: latex
+
+ \endgroup
+
On LVDS buses, usually each sample is transferred serialized in seven
time slots per pixel clock, on three (18-bit) or four (24-bit) or five (30-bit)
differential data pairs at the same time. The remaining bits are used
@@ -2648,7 +2817,7 @@ organization is given as an example for the first pixel only.
\tiny
\setlength{\tabcolsep}{2pt}
-.. tabularcolumns:: |p{6.0cm}|p{0.7cm}|p{0.3cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|
+.. tabularcolumns:: |p{6.0cm}|p{0.7cm}|p{0.3cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|
.. _v4l2-mbus-pixelcode-bayer:
@@ -2661,10 +2830,14 @@ organization is given as an example for the first pixel only.
* - Identifier
- Code
-
- - :cspan:`15` Data organization
+ - :cspan:`19` Data organization
* -
-
- Bit
+ - 19
+ - 18
+ - 17
+ - 16
- 15
- 14
- 13
@@ -2694,6 +2867,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`7`
- b\ :sub:`6`
- b\ :sub:`5`
@@ -2715,6 +2892,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`7`
- g\ :sub:`6`
- g\ :sub:`5`
@@ -2736,6 +2917,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`7`
- g\ :sub:`6`
- g\ :sub:`5`
@@ -2757,6 +2942,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- r\ :sub:`7`
- r\ :sub:`6`
- r\ :sub:`5`
@@ -2778,6 +2967,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`7`
- b\ :sub:`6`
- b\ :sub:`5`
@@ -2799,6 +2992,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`7`
- g\ :sub:`6`
- g\ :sub:`5`
@@ -2820,6 +3017,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`7`
- g\ :sub:`6`
- g\ :sub:`5`
@@ -2841,6 +3042,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- r\ :sub:`7`
- r\ :sub:`6`
- r\ :sub:`5`
@@ -2862,6 +3067,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`7`
- b\ :sub:`6`
- b\ :sub:`5`
@@ -2883,6 +3092,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`7`
- g\ :sub:`6`
- g\ :sub:`5`
@@ -2904,6 +3117,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`7`
- g\ :sub:`6`
- g\ :sub:`5`
@@ -2925,6 +3142,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- r\ :sub:`7`
- r\ :sub:`6`
- r\ :sub:`5`
@@ -2946,6 +3167,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- 0
- 0
- 0
@@ -2965,6 +3190,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`7`
- b\ :sub:`6`
- b\ :sub:`5`
@@ -2986,6 +3215,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`7`
- b\ :sub:`6`
- b\ :sub:`5`
@@ -3005,6 +3238,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- 0
- 0
- 0
@@ -3026,6 +3263,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`9`
- b\ :sub:`8`
- b\ :sub:`7`
@@ -3045,6 +3286,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`1`
- b\ :sub:`0`
- 0
@@ -3066,6 +3311,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`1`
- b\ :sub:`0`
- 0
@@ -3085,6 +3334,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`9`
- b\ :sub:`8`
- b\ :sub:`7`
@@ -3104,6 +3357,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`9`
- b\ :sub:`8`
- b\ :sub:`7`
@@ -3125,6 +3382,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`9`
- g\ :sub:`8`
- g\ :sub:`7`
@@ -3146,6 +3407,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`9`
- g\ :sub:`8`
- g\ :sub:`7`
@@ -3167,6 +3432,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- r\ :sub:`9`
- r\ :sub:`8`
- r\ :sub:`7`
@@ -3186,6 +3455,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`11`
- b\ :sub:`10`
- b\ :sub:`9`
@@ -3207,6 +3480,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`11`
- g\ :sub:`10`
- g\ :sub:`9`
@@ -3228,6 +3505,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`11`
- g\ :sub:`10`
- g\ :sub:`9`
@@ -3249,6 +3530,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- r\ :sub:`11`
- r\ :sub:`10`
- r\ :sub:`9`
@@ -3268,6 +3553,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- b\ :sub:`13`
- b\ :sub:`12`
- b\ :sub:`11`
@@ -3289,6 +3578,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`13`
- g\ :sub:`12`
- g\ :sub:`11`
@@ -3310,6 +3603,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- g\ :sub:`13`
- g\ :sub:`12`
- g\ :sub:`11`
@@ -3331,6 +3628,10 @@ organization is given as an example for the first pixel only.
-
-
-
+ -
+ -
+ -
+ -
- r\ :sub:`13`
- r\ :sub:`12`
- r\ :sub:`11`
@@ -3350,6 +3651,10 @@ organization is given as an example for the first pixel only.
- MEDIA_BUS_FMT_SBGGR16_1X16
- 0x301d
-
+ -
+ -
+ -
+ -
- b\ :sub:`15`
- b\ :sub:`14`
- b\ :sub:`13`
@@ -3371,6 +3676,10 @@ organization is given as an example for the first pixel only.
- MEDIA_BUS_FMT_SGBRG16_1X16
- 0x301e
-
+ -
+ -
+ -
+ -
- g\ :sub:`15`
- g\ :sub:`14`
- g\ :sub:`13`
@@ -3392,6 +3701,10 @@ organization is given as an example for the first pixel only.
- MEDIA_BUS_FMT_SGRBG16_1X16
- 0x301f
-
+ -
+ -
+ -
+ -
- g\ :sub:`15`
- g\ :sub:`14`
- g\ :sub:`13`
@@ -3413,6 +3726,110 @@ organization is given as an example for the first pixel only.
- MEDIA_BUS_FMT_SRGGB16_1X16
- 0x3020
-
+ -
+ -
+ -
+ -
+ - r\ :sub:`15`
+ - r\ :sub:`14`
+ - r\ :sub:`13`
+ - r\ :sub:`12`
+ - r\ :sub:`11`
+ - r\ :sub:`10`
+ - r\ :sub:`9`
+ - r\ :sub:`8`
+ - r\ :sub:`7`
+ - r\ :sub:`6`
+ - r\ :sub:`5`
+ - r\ :sub:`4`
+ - r\ :sub:`3`
+ - r\ :sub:`2`
+ - r\ :sub:`1`
+ - r\ :sub:`0`
+ * .. _MEDIA-BUS-FMT-SBGGR20-1X20:
+
+ - MEDIA_BUS_FMT_SBGGR20_1X20
+ - 0x3021
+ -
+ - b\ :sub:`19`
+ - b\ :sub:`18`
+ - b\ :sub:`17`
+ - b\ :sub:`16`
+ - b\ :sub:`15`
+ - b\ :sub:`14`
+ - b\ :sub:`13`
+ - b\ :sub:`12`
+ - b\ :sub:`11`
+ - b\ :sub:`10`
+ - b\ :sub:`9`
+ - b\ :sub:`8`
+ - b\ :sub:`7`
+ - b\ :sub:`6`
+ - b\ :sub:`5`
+ - b\ :sub:`4`
+ - b\ :sub:`3`
+ - b\ :sub:`2`
+ - b\ :sub:`1`
+ - b\ :sub:`0`
+ * .. _MEDIA-BUS-FMT-SGBRG20-1X20:
+
+ - MEDIA_BUS_FMT_SGBRG20_1X20
+ - 0x3022
+ -
+ - g\ :sub:`19`
+ - g\ :sub:`18`
+ - g\ :sub:`17`
+ - g\ :sub:`16`
+ - g\ :sub:`15`
+ - g\ :sub:`14`
+ - g\ :sub:`13`
+ - g\ :sub:`12`
+ - g\ :sub:`11`
+ - g\ :sub:`10`
+ - g\ :sub:`9`
+ - g\ :sub:`8`
+ - g\ :sub:`7`
+ - g\ :sub:`6`
+ - g\ :sub:`5`
+ - g\ :sub:`4`
+ - g\ :sub:`3`
+ - g\ :sub:`2`
+ - g\ :sub:`1`
+ - g\ :sub:`0`
+ * .. _MEDIA-BUS-FMT-SGRBG20-1X20:
+
+ - MEDIA_BUS_FMT_SGRBG20_1X20
+ - 0x3023
+ -
+ - g\ :sub:`19`
+ - g\ :sub:`18`
+ - g\ :sub:`17`
+ - g\ :sub:`16`
+ - g\ :sub:`15`
+ - g\ :sub:`14`
+ - g\ :sub:`13`
+ - g\ :sub:`12`
+ - g\ :sub:`11`
+ - g\ :sub:`10`
+ - g\ :sub:`9`
+ - g\ :sub:`8`
+ - g\ :sub:`7`
+ - g\ :sub:`6`
+ - g\ :sub:`5`
+ - g\ :sub:`4`
+ - g\ :sub:`3`
+ - g\ :sub:`2`
+ - g\ :sub:`1`
+ - g\ :sub:`0`
+ * .. _MEDIA-BUS-FMT-SRGGB20-1X20:
+
+ - MEDIA_BUS_FMT_SRGGB20_1X20
+ - 0x3024
+ -
+ - r\ :sub:`19`
+ - r\ :sub:`18`
+ - r\ :sub:`17`
+ - r\ :sub:`16`
- r\ :sub:`15`
- r\ :sub:`14`
- r\ :sub:`13`
diff --git a/Documentation/userspace-api/media/v4l/tch-formats.rst b/Documentation/userspace-api/media/v4l/tch-formats.rst
index 8c941ff9e200..a382d1c20eb3 100644
--- a/Documentation/userspace-api/media/v4l/tch-formats.rst
+++ b/Documentation/userspace-api/media/v4l/tch-formats.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _tch-formats:
diff --git a/Documentation/userspace-api/media/v4l/tuner.rst b/Documentation/userspace-api/media/v4l/tuner.rst
index e2c53c3abdc6..c82f68d2f900 100644
--- a/Documentation/userspace-api/media/v4l/tuner.rst
+++ b/Documentation/userspace-api/media/v4l/tuner.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _tuner:
diff --git a/Documentation/userspace-api/media/v4l/user-func.rst b/Documentation/userspace-api/media/v4l/user-func.rst
index 6f661138801c..5fc95c792408 100644
--- a/Documentation/userspace-api/media/v4l/user-func.rst
+++ b/Documentation/userspace-api/media/v4l/user-func.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _user-func:
diff --git a/Documentation/userspace-api/media/v4l/v4l2-isp.rst b/Documentation/userspace-api/media/v4l/v4l2-isp.rst
new file mode 100644
index 000000000000..facf6dba1ca7
--- /dev/null
+++ b/Documentation/userspace-api/media/v4l/v4l2-isp.rst
@@ -0,0 +1,67 @@
+.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+
+.. _v4l2-isp:
+
+************************
+Generic V4L2 ISP formats
+************************
+
+Generic ISP formats are metadata formats that define a mechanism to pass ISP
+parameters and statistics between userspace and drivers in V4L2 buffers. They
+are designed to allow extending them in a backward-compatible way.
+
+ISP parameters
+==============
+
+The generic ISP configuration parameters format is realized by a defining a
+single C structure that contains a header, followed by a binary buffer where
+userspace programs a variable number of ISP configuration data block, one for
+each supported ISP feature.
+
+The :c:type:`v4l2_isp_params_buffer` structure defines the buffer header which
+is followed by a binary buffer of ISP configuration data. Userspace shall
+correctly populate the buffer header with the generic parameters format version
+and with the size (in bytes) of the binary data buffer where it will store the
+ISP blocks configuration.
+
+Each *ISP configuration block* is preceded by an header implemented by the
+:c:type:`v4l2_isp_params_block_header` structure, followed by the configuration
+parameters for that specific block, defined by the ISP driver specific data
+types.
+
+Userspace applications are responsible for correctly populating each block's
+header fields (type, flags and size) and the block-specific parameters.
+
+ISP block enabling, disabling and configuration
+-----------------------------------------------
+
+When userspace wants to configure and enable an ISP block it shall fully
+populate the block configuration and set the V4L2_ISP_PARAMS_FL_BLOCK_ENABLE
+bit in the block header's `flags` field.
+
+When userspace simply wants to disable an ISP block the
+V4L2_ISP_PARAMS_FL_BLOCK_DISABLE bit should be set in block header's `flags`
+field. Drivers accept a configuration parameters block with no additional
+data after the header in this case.
+
+If the configuration of an already active ISP block has to be updated,
+userspace shall fully populate the ISP block parameters and omit setting the
+V4L2_ISP_PARAMS_FL_BLOCK_ENABLE and V4L2_ISP_PARAMS_FL_BLOCK_DISABLE bits in the
+header's `flags` field.
+
+Setting both the V4L2_ISP_PARAMS_FL_BLOCK_ENABLE and
+V4L2_ISP_PARAMS_FL_BLOCK_DISABLE bits in the flags field is not allowed and
+returns an error.
+
+Extension to the parameters format can be implemented by adding new blocks
+definition without invalidating the existing ones.
+
+ISP statistics
+==============
+
+Support for generic statistics format is not yet implemented in Video4Linux2.
+
+V4L2 ISP uAPI data types
+========================
+
+.. kernel-doc:: include/uapi/linux/media/v4l2-isp.h
diff --git a/Documentation/userspace-api/media/v4l/v4l2-selection-flags.rst b/Documentation/userspace-api/media/v4l/v4l2-selection-flags.rst
index 1cb1531c1e52..6aa00b613148 100644
--- a/Documentation/userspace-api/media/v4l/v4l2-selection-flags.rst
+++ b/Documentation/userspace-api/media/v4l/v4l2-selection-flags.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-selection-flags:
diff --git a/Documentation/userspace-api/media/v4l/v4l2-selection-targets.rst b/Documentation/userspace-api/media/v4l/v4l2-selection-targets.rst
index b46bae984f35..e9fd4b4bad42 100644
--- a/Documentation/userspace-api/media/v4l/v4l2-selection-targets.rst
+++ b/Documentation/userspace-api/media/v4l/v4l2-selection-targets.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2-selection-targets:
diff --git a/Documentation/userspace-api/media/v4l/v4l2.rst b/Documentation/userspace-api/media/v4l/v4l2.rst
index 64fb264fb6c4..be07b717ebe0 100644
--- a/Documentation/userspace-api/media/v4l/v4l2.rst
+++ b/Documentation/userspace-api/media/v4l/v4l2.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. include:: <isonum.txt>
.. _v4l2spec:
diff --git a/Documentation/userspace-api/media/v4l/v4l2grab-example.rst b/Documentation/userspace-api/media/v4l/v4l2grab-example.rst
index b323be42c580..b2472d4b0e3f 100644
--- a/Documentation/userspace-api/media/v4l/v4l2grab-example.rst
+++ b/Documentation/userspace-api/media/v4l/v4l2grab-example.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _v4l2grab-example:
diff --git a/Documentation/userspace-api/media/v4l/v4l2grab.c.rst b/Documentation/userspace-api/media/v4l/v4l2grab.c.rst
index 1a55e3617ea8..c958db1e0211 100644
--- a/Documentation/userspace-api/media/v4l/v4l2grab.c.rst
+++ b/Documentation/userspace-api/media/v4l/v4l2grab.c.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
file: media/v4l/v4l2grab.c
==========================
diff --git a/Documentation/userspace-api/media/v4l/video.rst b/Documentation/userspace-api/media/v4l/video.rst
index f8f69a57602c..25cb854c1101 100644
--- a/Documentation/userspace-api/media/v4l/video.rst
+++ b/Documentation/userspace-api/media/v4l/video.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _video:
diff --git a/Documentation/userspace-api/media/v4l/videodev.rst b/Documentation/userspace-api/media/v4l/videodev.rst
index cde485bc9a5f..0b71a60928fb 100644
--- a/Documentation/userspace-api/media/v4l/videodev.rst
+++ b/Documentation/userspace-api/media/v4l/videodev.rst
@@ -1,11 +1,14 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _videodev:
-*******************************
-Video For Linux Two Header File
-*******************************
+***************************************
+Video For Linux Two Header uAPI Symbols
+***************************************
.. kernel-include:: include/uapi/linux/videodev2.h
:generate-cross-refs:
:exception-file: videodev2.h.rst.exceptions
+ :toc:
+ :warn-broken:
diff --git a/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions b/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions
index 35d3456cc812..c41693115db6 100644
--- a/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions
+++ b/Documentation/userspace-api/media/v4l/videodev2.h.rst.exceptions
@@ -1,5 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
+# All symbols are mapped inside V4L C domain namespace
+namespace V4L
+
# Ignore header name
ignore define _UAPI__LINUX_VIDEODEV2_H
@@ -15,157 +18,158 @@ ignore define _UAPI__LINUX_VIDEODEV2_H
ignore symbol V4L2_BUF_TYPE_PRIVATE
ignore symbol V4L2_TUNER_DIGITAL_TV
ignore symbol V4L2_COLORSPACE_BT878
+ignore struct __kernel_v4l2_timeval
# Documented enum v4l2_field
-replace symbol V4L2_FIELD_ALTERNATE :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_ANY :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_BOTTOM :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_INTERLACED :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_INTERLACED_BT :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_INTERLACED_TB :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_NONE :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_SEQ_BT :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_SEQ_TB :c:type:`v4l2_field`
-replace symbol V4L2_FIELD_TOP :c:type:`v4l2_field`
+replace symbol V4L2_FIELD_ALTERNATE :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_ANY :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_BOTTOM :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_INTERLACED :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_INTERLACED_BT :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_INTERLACED_TB :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_NONE :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_SEQ_BT :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_SEQ_TB :c:type:`V4L.v4l2_field`
+replace symbol V4L2_FIELD_TOP :c:type:`V4L.v4l2_field`
# Documented enum v4l2_buf_type
-replace symbol V4L2_BUF_TYPE_META_CAPTURE :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_META_OUTPUT :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_SDR_CAPTURE :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_SDR_OUTPUT :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_SLICED_VBI_CAPTURE :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_SLICED_VBI_OUTPUT :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VBI_CAPTURE :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VBI_OUTPUT :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY :c:type:`v4l2_buf_type`
-replace symbol V4L2_BUF_TYPE_VIDEO_OVERLAY :c:type:`v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_META_CAPTURE :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_META_OUTPUT :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_SDR_CAPTURE :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_SDR_OUTPUT :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_SLICED_VBI_CAPTURE :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_SLICED_VBI_OUTPUT :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VBI_CAPTURE :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VBI_OUTPUT :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY :c:type:`V4L.v4l2_buf_type`
+replace symbol V4L2_BUF_TYPE_VIDEO_OVERLAY :c:type:`V4L.v4l2_buf_type`
# Documented enum v4l2_tuner_type
-replace symbol V4L2_TUNER_ANALOG_TV :c:type:`v4l2_tuner_type`
-replace symbol V4L2_TUNER_RADIO :c:type:`v4l2_tuner_type`
-replace symbol V4L2_TUNER_RF :c:type:`v4l2_tuner_type`
-replace symbol V4L2_TUNER_SDR :c:type:`v4l2_tuner_type`
+replace symbol V4L2_TUNER_ANALOG_TV :c:type:`V4L.v4l2_tuner_type`
+replace symbol V4L2_TUNER_RADIO :c:type:`V4L.v4l2_tuner_type`
+replace symbol V4L2_TUNER_RF :c:type:`V4L.v4l2_tuner_type`
+replace symbol V4L2_TUNER_SDR :c:type:`V4L.v4l2_tuner_type`
# Documented enum v4l2_memory
-replace symbol V4L2_MEMORY_DMABUF :c:type:`v4l2_memory`
-replace symbol V4L2_MEMORY_MMAP :c:type:`v4l2_memory`
-replace symbol V4L2_MEMORY_OVERLAY :c:type:`v4l2_memory`
-replace symbol V4L2_MEMORY_USERPTR :c:type:`v4l2_memory`
+replace symbol V4L2_MEMORY_DMABUF :c:type:`V4L.v4l2_memory`
+replace symbol V4L2_MEMORY_MMAP :c:type:`V4L.v4l2_memory`
+replace symbol V4L2_MEMORY_OVERLAY :c:type:`V4L.v4l2_memory`
+replace symbol V4L2_MEMORY_USERPTR :c:type:`V4L.v4l2_memory`
# Documented enum v4l2_colorspace
-replace symbol V4L2_COLORSPACE_470_SYSTEM_BG :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_470_SYSTEM_M :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_OPRGB :c:type:`v4l2_colorspace`
-replace define V4L2_COLORSPACE_ADOBERGB :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_BT2020 :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_DCI_P3 :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_DEFAULT :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_JPEG :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_RAW :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_REC709 :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_SMPTE170M :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_SMPTE240M :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_SRGB :c:type:`v4l2_colorspace`
-replace symbol V4L2_COLORSPACE_LAST :c:type:`v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_470_SYSTEM_BG :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_470_SYSTEM_M :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_OPRGB :c:type:`V4L.v4l2_colorspace`
+replace define V4L2_COLORSPACE_ADOBERGB :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_BT2020 :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_DCI_P3 :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_DEFAULT :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_JPEG :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_RAW :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_REC709 :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_SMPTE170M :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_SMPTE240M :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_SRGB :c:type:`V4L.v4l2_colorspace`
+replace symbol V4L2_COLORSPACE_LAST :c:type:`V4L.v4l2_colorspace`
# Documented enum v4l2_xfer_func
-replace symbol V4L2_XFER_FUNC_709 :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_OPRGB :c:type:`v4l2_xfer_func`
-replace define V4L2_XFER_FUNC_ADOBERGB :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_DCI_P3 :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_DEFAULT :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_NONE :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_SMPTE2084 :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_SMPTE240M :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_SRGB :c:type:`v4l2_xfer_func`
-replace symbol V4L2_XFER_FUNC_LAST :c:type:`v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_709 :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_OPRGB :c:type:`V4L.v4l2_xfer_func`
+replace define V4L2_XFER_FUNC_ADOBERGB :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_DCI_P3 :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_DEFAULT :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_NONE :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_SMPTE2084 :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_SMPTE240M :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_SRGB :c:type:`V4L.v4l2_xfer_func`
+replace symbol V4L2_XFER_FUNC_LAST :c:type:`V4L.v4l2_xfer_func`
# Documented enum v4l2_ycbcr_encoding
-replace symbol V4L2_YCBCR_ENC_601 :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_709 :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_BT2020 :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_BT2020_CONST_LUM :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_DEFAULT :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_SYCC :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_XV601 :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_XV709 :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_SMPTE240M :c:type:`v4l2_ycbcr_encoding`
-replace symbol V4L2_YCBCR_ENC_LAST :c:type:`v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_601 :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_709 :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_BT2020 :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_BT2020_CONST_LUM :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_DEFAULT :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_SYCC :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_XV601 :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_XV709 :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_SMPTE240M :c:type:`V4L.v4l2_ycbcr_encoding`
+replace symbol V4L2_YCBCR_ENC_LAST :c:type:`V4L.v4l2_ycbcr_encoding`
# Documented enum v4l2_hsv_encoding
-replace symbol V4L2_HSV_ENC_180 :c:type:`v4l2_hsv_encoding`
-replace symbol V4L2_HSV_ENC_256 :c:type:`v4l2_hsv_encoding`
+replace symbol V4L2_HSV_ENC_180 :c:type:`V4L.v4l2_hsv_encoding`
+replace symbol V4L2_HSV_ENC_256 :c:type:`V4L.v4l2_hsv_encoding`
# Documented enum v4l2_quantization
-replace symbol V4L2_QUANTIZATION_DEFAULT :c:type:`v4l2_quantization`
-replace symbol V4L2_QUANTIZATION_FULL_RANGE :c:type:`v4l2_quantization`
-replace symbol V4L2_QUANTIZATION_LIM_RANGE :c:type:`v4l2_quantization`
+replace symbol V4L2_QUANTIZATION_DEFAULT :c:type:`V4L.v4l2_quantization`
+replace symbol V4L2_QUANTIZATION_FULL_RANGE :c:type:`V4L.v4l2_quantization`
+replace symbol V4L2_QUANTIZATION_LIM_RANGE :c:type:`V4L.v4l2_quantization`
# Documented enum v4l2_priority
-replace symbol V4L2_PRIORITY_BACKGROUND :c:type:`v4l2_priority`
-replace symbol V4L2_PRIORITY_DEFAULT :c:type:`v4l2_priority`
-replace symbol V4L2_PRIORITY_INTERACTIVE :c:type:`v4l2_priority`
-replace symbol V4L2_PRIORITY_RECORD :c:type:`v4l2_priority`
-replace symbol V4L2_PRIORITY_UNSET :c:type:`v4l2_priority`
+replace symbol V4L2_PRIORITY_BACKGROUND :c:type:`V4L.v4l2_priority`
+replace symbol V4L2_PRIORITY_DEFAULT :c:type:`V4L.v4l2_priority`
+replace symbol V4L2_PRIORITY_INTERACTIVE :c:type:`V4L.v4l2_priority`
+replace symbol V4L2_PRIORITY_RECORD :c:type:`V4L.v4l2_priority`
+replace symbol V4L2_PRIORITY_UNSET :c:type:`V4L.v4l2_priority`
# Documented enum v4l2_frmsizetypes
-replace symbol V4L2_FRMSIZE_TYPE_CONTINUOUS :c:type:`v4l2_frmsizetypes`
-replace symbol V4L2_FRMSIZE_TYPE_DISCRETE :c:type:`v4l2_frmsizetypes`
-replace symbol V4L2_FRMSIZE_TYPE_STEPWISE :c:type:`v4l2_frmsizetypes`
+replace symbol V4L2_FRMSIZE_TYPE_CONTINUOUS :c:type:`V4L.v4l2_frmsizetypes`
+replace symbol V4L2_FRMSIZE_TYPE_DISCRETE :c:type:`V4L.v4l2_frmsizetypes`
+replace symbol V4L2_FRMSIZE_TYPE_STEPWISE :c:type:`V4L.v4l2_frmsizetypes`
# Documented enum frmivaltypes
-replace symbol V4L2_FRMIVAL_TYPE_CONTINUOUS :c:type:`v4l2_frmivaltypes`
-replace symbol V4L2_FRMIVAL_TYPE_DISCRETE :c:type:`v4l2_frmivaltypes`
-replace symbol V4L2_FRMIVAL_TYPE_STEPWISE :c:type:`v4l2_frmivaltypes`
+replace symbol V4L2_FRMIVAL_TYPE_CONTINUOUS :c:type:`V4L.v4l2_frmivaltypes`
+replace symbol V4L2_FRMIVAL_TYPE_DISCRETE :c:type:`V4L.v4l2_frmivaltypes`
+replace symbol V4L2_FRMIVAL_TYPE_STEPWISE :c:type:`V4L.v4l2_frmivaltypes`
-# Documented enum :c:type:`v4l2_ctrl_type`
+# Documented enum :c:type:`V4L.v4l2_ctrl_type`
replace symbol V4L2_CTRL_COMPOUND_TYPES vidioc_queryctrl
-replace symbol V4L2_CTRL_TYPE_BITMASK :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_BOOLEAN :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_BUTTON :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_CTRL_CLASS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_INTEGER :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_INTEGER64 :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_INTEGER_MENU :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_MENU :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_STRING :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_U16 :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_U32 :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_U8 :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_MPEG2_SEQUENCE :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_MPEG2_PICTURE :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_MPEG2_QUANTISATION :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_H264_SPS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_H264_PPS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_H264_PRED_WEIGHTS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_AREA :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_RECT :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_VP9_FRAME :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HDR10_CLL_INFO :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_AV1_SEQUENCE :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_AV1_FRAME :c:type:`v4l2_ctrl_type`
-replace symbol V4L2_CTRL_TYPE_AV1_FILM_GRAIN :c:type:`v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_BITMASK :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_BOOLEAN :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_BUTTON :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_CTRL_CLASS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_INTEGER :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_INTEGER64 :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_INTEGER_MENU :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_MENU :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_STRING :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_U16 :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_U32 :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_U8 :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_MPEG2_SEQUENCE :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_MPEG2_PICTURE :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_MPEG2_QUANTISATION :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_H264_SPS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_H264_PPS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_H264_SCALING_MATRIX :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_H264_PRED_WEIGHTS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_H264_SLICE_PARAMS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_H264_DECODE_PARAMS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_AREA :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_RECT :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_FWHT_PARAMS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_VP8_FRAME :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_VP9_COMPRESSED_HDR :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_VP9_FRAME :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HDR10_CLL_INFO :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_SPS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_PPS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_SCALING_MATRIX :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_HEVC_DECODE_PARAMS :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_AV1_SEQUENCE :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_AV1_TILE_GROUP_ENTRY :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_AV1_FRAME :c:type:`V4L.v4l2_ctrl_type`
+replace symbol V4L2_CTRL_TYPE_AV1_FILM_GRAIN :c:type:`V4L.v4l2_ctrl_type`
# V4L2 capability defines
replace define V4L2_CAP_VIDEO_CAPTURE device-capabilities
@@ -201,10 +205,10 @@ replace define V4L2_CAP_IO_MC device-capabilities
replace define V4L2_CAP_EDID device-capabilities
# V4L2 pix flags
-replace define V4L2_PIX_FMT_PRIV_MAGIC :c:type:`v4l2_pix_format`
+replace define V4L2_PIX_FMT_PRIV_MAGIC :c:type:`V4L.v4l2_pix_format`
replace define V4L2_PIX_FMT_FLAG_PREMUL_ALPHA format-flags
-replace define V4L2_PIX_FMT_HM12 :c:type:`v4l2_pix_format`
-replace define V4L2_PIX_FMT_SUNXI_TILED_NV12 :c:type:`v4l2_pix_format`
+replace define V4L2_PIX_FMT_HM12 :c:type:`V4L.v4l2_pix_format`
+replace define V4L2_PIX_FMT_SUNXI_TILED_NV12 :c:type:`V4L.v4l2_pix_format`
# V4L2 format flags
replace define V4L2_FMT_FLAG_COMPRESSED fmtdesc-flags
@@ -263,7 +267,7 @@ replace define V4L2_FBUF_FLAG_SRC_CHROMAKEY framebuffer-flags
# Used on VIDIOC_G_PARM
replace define V4L2_MODE_HIGHQUALITY parm-flags
-replace define V4L2_CAP_TIMEPERFRAME :c:type:`v4l2_captureparm`
+replace define V4L2_CAP_TIMEPERFRAME :c:type:`V4L.v4l2_captureparm`
# The V4L2_STD_foo are all defined at v4l2_std_id table
@@ -316,11 +320,11 @@ replace define V4L2_STD_ALL v4l2-std-id
# V4L2 DT BT timings definitions
-replace define V4L2_DV_PROGRESSIVE :c:type:`v4l2_bt_timings`
-replace define V4L2_DV_INTERLACED :c:type:`v4l2_bt_timings`
+replace define V4L2_DV_PROGRESSIVE :c:type:`V4L.v4l2_bt_timings`
+replace define V4L2_DV_INTERLACED :c:type:`V4L.v4l2_bt_timings`
-replace define V4L2_DV_VSYNC_POS_POL :c:type:`v4l2_bt_timings`
-replace define V4L2_DV_HSYNC_POS_POL :c:type:`v4l2_bt_timings`
+replace define V4L2_DV_VSYNC_POS_POL :c:type:`V4L.v4l2_bt_timings`
+replace define V4L2_DV_HSYNC_POS_POL :c:type:`V4L.v4l2_bt_timings`
replace define V4L2_DV_BT_STD_CEA861 dv-bt-standards
replace define V4L2_DV_BT_STD_DMT dv-bt-standards
@@ -456,11 +460,11 @@ replace define V4L2_AUDMODE_AVL audio-mode
# MPEG
-replace define V4L2_ENC_IDX_FRAME_I :c:type:`v4l2_enc_idx`
-replace define V4L2_ENC_IDX_FRAME_P :c:type:`v4l2_enc_idx`
-replace define V4L2_ENC_IDX_FRAME_B :c:type:`v4l2_enc_idx`
-replace define V4L2_ENC_IDX_FRAME_MASK :c:type:`v4l2_enc_idx`
-replace define V4L2_ENC_IDX_ENTRIES :c:type:`v4l2_enc_idx`
+replace define V4L2_ENC_IDX_FRAME_I :c:type:`V4L.v4l2_enc_idx`
+replace define V4L2_ENC_IDX_FRAME_P :c:type:`V4L.v4l2_enc_idx`
+replace define V4L2_ENC_IDX_FRAME_B :c:type:`V4L.v4l2_enc_idx`
+replace define V4L2_ENC_IDX_FRAME_MASK :c:type:`V4L.v4l2_enc_idx`
+replace define V4L2_ENC_IDX_ENTRIES :c:type:`V4L.v4l2_enc_idx`
replace define V4L2_ENC_CMD_START encoder-cmds
replace define V4L2_ENC_CMD_STOP encoder-cmds
@@ -488,10 +492,10 @@ replace define V4L2_DEC_START_FMT_GOP decoder-cmds
replace define V4L2_VBI_UNSYNC vbifmt-flags
replace define V4L2_VBI_INTERLACED vbifmt-flags
-replace define V4L2_VBI_ITU_525_F1_START :c:type:`v4l2_vbi_format`
-replace define V4L2_VBI_ITU_525_F2_START :c:type:`v4l2_vbi_format`
-replace define V4L2_VBI_ITU_625_F1_START :c:type:`v4l2_vbi_format`
-replace define V4L2_VBI_ITU_625_F2_START :c:type:`v4l2_vbi_format`
+replace define V4L2_VBI_ITU_525_F1_START :c:type:`V4L.v4l2_vbi_format`
+replace define V4L2_VBI_ITU_525_F2_START :c:type:`V4L.v4l2_vbi_format`
+replace define V4L2_VBI_ITU_625_F1_START :c:type:`V4L.v4l2_vbi_format`
+replace define V4L2_VBI_ITU_625_F2_START :c:type:`V4L.v4l2_vbi_format`
replace define V4L2_SLICED_TELETEXT_B vbi-services
@@ -527,7 +531,7 @@ replace define V4L2_EVENT_CTRL_CH_DIMENSIONS ctrl-changes-flags
replace define V4L2_EVENT_SRC_CH_RESOLUTION src-changes-flags
-replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`v4l2_event_motion_det`
+replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`V4L.v4l2_event_motion_det`
replace define V4L2_EVENT_SUB_FL_SEND_INITIAL event-flags
replace define V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK event-flags
diff --git a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
index 3549417c7feb..c8baa9430c14 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-queryctrl.rst
@@ -15,6 +15,8 @@ VIDIOC_QUERYCTRL - VIDIOC_QUERY_EXT_CTRL - VIDIOC_QUERYMENU - Enumerate controls
Synopsis
========
+.. c:macro:: VIDIOC_QUERY_CTRL
+
``int ioctl(int fd, int VIDIOC_QUERYCTRL, struct v4l2_queryctrl *argp)``
.. c:macro:: VIDIOC_QUERY_EXT_CTRL
@@ -98,6 +100,8 @@ See also the examples in :ref:`control`.
.. _v4l2-queryctrl:
+.. c:struct:: v4l2_queryctrl
+
.. cssclass:: longtable
.. flat-table:: struct v4l2_queryctrl
@@ -178,6 +182,8 @@ See also the examples in :ref:`control`.
.. cssclass:: longtable
+.. c:struct:: v4l2_query_ext_ctrl
+
.. flat-table:: struct v4l2_query_ext_ctrl
:header-rows: 0
:stub-columns: 0
@@ -276,6 +282,8 @@ See also the examples in :ref:`control`.
.. _v4l2-querymenu:
+.. c:struct:: v4l2_querymenu
+
.. flat-table:: struct v4l2_querymenu
:header-rows: 0
:stub-columns: 0
diff --git a/Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst b/Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst
index 1995b39af9ba..b498d60752d7 100644
--- a/Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst
+++ b/Documentation/userspace-api/media/v4l/vidioc-remove-bufs.rst
@@ -54,7 +54,7 @@ are invoked.
- ``count``
- The number of buffers to be removed with indices 'index' until 'index + count - 1'.
All buffers in this range must be valid and in DEQUEUED state.
- :ref:`VIDIOC_REMOVE_BUFS` will always check the validity of ``type`, if it is
+ :ref:`VIDIOC_REMOVE_BUFS` will always check the validity of ``type``, if it is
invalid it returns ``EINVAL`` error code.
If count is set to 0 :ref:`VIDIOC_REMOVE_BUFS` will do nothing and return 0.
* - __u32
diff --git a/Documentation/userspace-api/media/v4l/yuv-formats.rst b/Documentation/userspace-api/media/v4l/yuv-formats.rst
index 78ee406d7647..c5ef408470a5 100644
--- a/Documentation/userspace-api/media/v4l/yuv-formats.rst
+++ b/Documentation/userspace-api/media/v4l/yuv-formats.rst
@@ -1,4 +1,5 @@
.. SPDX-License-Identifier: GFDL-1.1-no-invariants-or-later
+.. c:namespace:: V4L
.. _yuv-formats:
diff --git a/Documentation/userspace-api/netlink/intro-specs.rst b/Documentation/userspace-api/netlink/intro-specs.rst
index a4435ae4628d..e5ebc617754a 100644
--- a/Documentation/userspace-api/netlink/intro-specs.rst
+++ b/Documentation/userspace-api/netlink/intro-specs.rst
@@ -13,10 +13,10 @@ Simple CLI
Kernel comes with a simple CLI tool which should be useful when
developing Netlink related code. The tool is implemented in Python
and can use a YAML specification to issue Netlink requests
-to the kernel. Only Generic Netlink is supported.
+to the kernel.
The tool is located at ``tools/net/ynl/pyynl/cli.py``. It accepts
-a handul of arguments, the most important ones are:
+a handful of arguments, the most important ones are:
- ``--spec`` - point to the spec file
- ``--do $name`` / ``--dump $name`` - issue request ``$name``
diff --git a/Documentation/userspace-api/spec_ctrl.rst b/Documentation/userspace-api/spec_ctrl.rst
index 5e8ed9eef9aa..ca89151fc0a8 100644
--- a/Documentation/userspace-api/spec_ctrl.rst
+++ b/Documentation/userspace-api/spec_ctrl.rst
@@ -26,7 +26,8 @@ PR_GET_SPECULATION_CTRL
PR_GET_SPECULATION_CTRL returns the state of the speculation misfeature
which is selected with arg2 of prctl(2). The return value uses bits 0-3 with
-the following meaning:
+the following meaning (with the caveat that PR_SPEC_L1D_FLUSH has less obvious
+semantics, see documentation for that specific control below):
==== ====================== ==================================================
Bit Define Description
@@ -110,6 +111,9 @@ Speculation misfeature controls
- PR_SPEC_L1D_FLUSH: Flush L1D Cache on context switch out of the task
(works only when tasks run on non SMT cores)
+For this control, PR_SPEC_ENABLE means that the **mitigation** is enabled (L1D
+is flushed), PR_SPEC_DISABLE means it is disabled.
+
Invocations:
* prctl(PR_GET_SPECULATION_CTRL, PR_SPEC_L1D_FLUSH, 0, 0, 0);
* prctl(PR_SET_SPECULATION_CTRL, PR_SPEC_L1D_FLUSH, PR_SPEC_ENABLE, 0, 0);
diff --git a/Documentation/w1/w1-netlink.rst b/Documentation/w1/w1-netlink.rst
index be4f7b82dcb4..ff281713e626 100644
--- a/Documentation/w1/w1-netlink.rst
+++ b/Documentation/w1/w1-netlink.rst
@@ -196,7 +196,7 @@ Additional documentation, source code examples
==============================================
1. Documentation/driver-api/connector.rst
-2. http://www.ioremap.net/archive/w1
+2. https://github.com/bioothod/w1
This archive includes userspace application w1d.c which uses
read/write/search commands for all master/slave devices found on the bus.
diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst
index 99ef21fc1c1e..5680303ae314 100644
--- a/Documentation/wmi/driver-development-guide.rst
+++ b/Documentation/wmi/driver-development-guide.rst
@@ -54,6 +54,7 @@ to matching WMI devices using a struct wmi_device_id table:
::
static const struct wmi_device_id foo_id_table[] = {
+ /* Only use uppercase letters! */
{ "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL },
{ }
};
diff --git a/MAINTAINERS b/MAINTAINERS
index 545a4776795e..3206dc852d7e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -388,7 +388,7 @@ B: https://bugzilla.kernel.org
F: drivers/acpi/*thermal*
ACPI VIOT DRIVER
-M: Jean-Philippe Brucker <jean-philippe@linaro.org>
+M: Jean-Philippe Brucker <jpb@kernel.org>
L: linux-acpi@vger.kernel.org
L: iommu@lists.linux.dev
S: Maintained
@@ -915,6 +915,7 @@ F: drivers/staging/media/sunxi/cedrus/
ALPHA PORT
M: Richard Henderson <richard.henderson@linaro.org>
M: Matt Turner <mattst88@gmail.com>
+M: Magnus Lindholm <linmag7@gmail.com>
L: linux-alpha@vger.kernel.org
S: Odd Fixes
F: arch/alpha/
@@ -973,7 +974,7 @@ F: drivers/edac/al_mc_edac.c
AMAZON ANNAPURNA LABS THERMAL MMIO DRIVER
M: Talel Shenhar <talel@amazon.com>
S: Maintained
-F: Documentation/devicetree/bindings/thermal/amazon,al-thermal.txt
+F: Documentation/devicetree/bindings/thermal/amazon,al-thermal.yaml
F: drivers/thermal/thermal_mmio.c
AMAZON ETHERNET DRIVERS
@@ -1080,7 +1081,7 @@ M: Austin Zheng <austin.zheng@amd.com>
M: Jun Lei <jun.lei@amd.com>
S: Supported
F: drivers/gpu/drm/amd/display/dc/dml/
-F: drivers/gpu/drm/amd/display/dc/dml2/
+F: drivers/gpu/drm/amd/display/dc/dml2_0/
AMD FAM15H PROCESSOR POWER MONITORING DRIVER
M: Huang Rui <ray.huang@amd.com>
@@ -1757,6 +1758,7 @@ S: Supported
W: http://wiki.analog.com/
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/sound/adi,*
+F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: sound/soc/codecs/ad1*
F: sound/soc/codecs/ad7*
F: sound/soc/codecs/adau*
@@ -1997,6 +1999,10 @@ F: include/uapi/linux/if_arcnet.h
ARM AND ARM64 SoC SUB-ARCHITECTURES (COMMON PARTS)
M: Arnd Bergmann <arnd@arndb.de>
+M: Krzysztof Kozlowski <krzk@kernel.org>
+M: Alexandre Belloni <alexandre.belloni@bootlin.com>
+M: Linus Walleij <linus.walleij@linaro.org>
+R: Drew Fustini <fustini@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: soc@lists.linux.dev
S: Maintained
@@ -2017,6 +2023,15 @@ F: arch/arm64/include/asm/arch_timer.h
F: drivers/clocksource/arm_arch_timer.c
F: drivers/clocksource/arm_arch_timer_mmio.c
+ARM ETHOS-U NPU DRIVER
+M: Rob Herring (Arm) <robh@kernel.org>
+M: Tomeu Vizoso <tomeu@tomeuvizoso.net>
+L: dri-devel@lists.freedesktop.org
+S: Supported
+T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
+F: drivers/accel/ethosu/
+F: include/uapi/drm/ethosu_accel.h
+
ARM GENERIC INTERRUPT CONTROLLER DRIVERS
M: Marc Zyngier <maz@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -2092,7 +2107,8 @@ F: drivers/gpu/drm/arm/display/komeda/
ARM MALI PANFROST DRM DRIVER
M: Boris Brezillon <boris.brezillon@collabora.com>
M: Rob Herring <robh@kernel.org>
-R: Steven Price <steven.price@arm.com>
+M: Steven Price <steven.price@arm.com>
+M: Adrián Larumbe <adrian.larumbe@collabora.com>
L: dri-devel@lists.freedesktop.org
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
@@ -2101,6 +2117,20 @@ F: drivers/gpu/drm/ci/xfails/panfrost*
F: drivers/gpu/drm/panfrost/
F: include/uapi/drm/panfrost_drm.h
+ARM MALI-C55 ISP DRIVER
+M: Daniel Scally <dan.scally@ideasonboard.com>
+M: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media_tree.git
+F: Documentation/admin-guide/media/mali-c55-graph.dot
+F: Documentation/admin-guide/media/mali-c55.rst
+F: Documentation/devicetree/bindings/media/arm,mali-c55.yaml
+F: Documentation/userspace-api/media/drivers/mali-c55.rst
+F: Documentation/userspace-api/media/v4l/metafmt-arm-mali-c55.rst
+F: drivers/media/platform/arm/mali-c55/
+F: include/uapi/linux/media/arm/mali-c55-config.h
+
ARM MALI PANTHOR DRM DRIVER
M: Boris Brezillon <boris.brezillon@collabora.com>
M: Steven Price <steven.price@arm.com>
@@ -2239,7 +2269,7 @@ F: drivers/iommu/arm/
F: drivers/iommu/io-pgtable-arm*
ARM SMMU SVA SUPPORT
-R: Jean-Philippe Brucker <jean-philippe@linaro.org>
+R: Jean-Philippe Brucker <jpb@kernel.org>
F: drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-sva.c
ARM SUB-ARCHITECTURES
@@ -2264,7 +2294,7 @@ F: Documentation/devicetree/bindings/interrupt-controller/actions,owl-sirq.yaml
F: Documentation/devicetree/bindings/mmc/owl-mmc.yaml
F: Documentation/devicetree/bindings/net/actions,owl-emac.yaml
F: Documentation/devicetree/bindings/pinctrl/actions,*
-F: Documentation/devicetree/bindings/power/actions,owl-sps.txt
+F: Documentation/devicetree/bindings/power/actions,s500-sps.yaml
F: Documentation/devicetree/bindings/timer/actions,owl-timer.yaml
F: arch/arm/boot/dts/actions/
F: arch/arm/mach-actions/
@@ -2298,7 +2328,7 @@ S: Maintained
F: drivers/clk/sunxi/
ARM/Allwinner sunXi SoC support
-M: Chen-Yu Tsai <wens@csie.org>
+M: Chen-Yu Tsai <wens@kernel.org>
M: Jernej Skrabec <jernej.skrabec@gmail.com>
M: Samuel Holland <samuel@sholland.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -2392,9 +2422,9 @@ M: Martin Povišer <povik+lin@cutebit.org>
L: asahi@lists.linux.dev
L: linux-sound@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
-F: Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
F: Documentation/devicetree/bindings/sound/apple,*
+F: Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
+F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: sound/soc/apple/*
F: sound/soc/codecs/cs42l83-i2c.c
F: sound/soc/codecs/cs42l84.*
@@ -2438,12 +2468,14 @@ F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml
F: Documentation/devicetree/bindings/spi/apple,spi.yaml
F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml
F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml
+F: Documentation/hwmon/macsmc-hwmon.rst
F: arch/arm64/boot/dts/apple/
F: drivers/bluetooth/hci_bcm4377.c
F: drivers/clk/clk-apple-nco.c
F: drivers/cpufreq/apple-soc-cpufreq.c
F: drivers/dma/apple-admac.c
F: drivers/gpio/gpio-macsmc.c
+F: drivers/hwmon/macsmc-hwmon.c
F: drivers/pmdomain/apple/
F: drivers/i2c/busses/i2c-pasemi-core.c
F: drivers/i2c/busses/i2c-pasemi-platform.c
@@ -2656,7 +2688,7 @@ F: Documentation/ABI/testing/debugfs-moxtet
F: Documentation/ABI/testing/sysfs-bus-i2c-devices-turris-omnia-mcu
F: Documentation/ABI/testing/sysfs-bus-moxtet-devices
F: Documentation/ABI/testing/sysfs-firmware-turris-mox-rwtm
-F: Documentation/devicetree/bindings/bus/moxtet.txt
+F: Documentation/devicetree/bindings/bus/cznic,moxtet.yaml
F: Documentation/devicetree/bindings/firmware/cznic,turris-mox-rwtm.txt
F: Documentation/devicetree/bindings/firmware/cznic,turris-omnia-mcu.yaml
F: Documentation/devicetree/bindings/interrupt-controller/marvell,mpic.yaml
@@ -3132,6 +3164,15 @@ F: arch/arm64/boot/dts/freescale/s32g*.dts*
F: drivers/pinctrl/nxp/
F: drivers/rtc/rtc-s32g.c
+ARM/NXP S32G PCIE CONTROLLER DRIVER
+M: Ciprian Marian Costea <ciprianmarian.costea@oss.nxp.com>
+R: NXP S32 Linux Team <s32@nxp.com>
+L: imx@lists.linux.dev
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/pci/nxp,s32g-pcie.yaml
+F: drivers/pci/controller/dwc/pcie-nxp-s32g*
+
ARM/NXP S32G/S32R DWMAC ETHERNET DRIVER
M: Jan Petrous <jan.petrous@oss.nxp.com>
R: s32@nxp.com
@@ -3296,6 +3337,7 @@ F: drivers/*/*/*rockchip*
F: drivers/*/*rockchip*
F: drivers/clk/rockchip/
F: drivers/i2c/busses/i2c-rk3x.c
+F: drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
F: sound/soc/rockchip/
N: rockchip
@@ -3402,7 +3444,7 @@ S: Maintained
F: drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
ARM/SOCFPGA EDAC BINDINGS
-M: Matthew Gerlach <matthew.gerlach@altera.com>
+M: Niravkumar L Rabara <niravkumarlaxmidas.rabara@altera.com>
S: Maintained
F: Documentation/devicetree/bindings/edac/altr,socfpga-ecc-manager.yaml
@@ -3437,7 +3479,6 @@ F: drivers/clocksource/clksrc_st_lpc.c
F: drivers/cpufreq/sti-cpufreq.c
F: drivers/dma/st_fdma*
F: drivers/i2c/busses/i2c-st.c
-F: drivers/media/platform/st/sti/c8sectpfe/
F: drivers/media/rc/st_rc.c
F: drivers/mmc/host/sdhci-st.c
F: drivers/phy/st/phy-miphy28lp.c
@@ -3841,6 +3882,7 @@ F: drivers/hwmon/asus-ec-sensors.c
ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
M: Corentin Chary <corentin.chary@gmail.com>
M: Luke D. Jones <luke@ljones.dev>
+M: Denis Benato <benato.denis96@gmail.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
W: https://asus-linux.org/
@@ -3920,7 +3962,7 @@ F: crypto/async_tx/
F: include/linux/async_tx.h
AT24 EEPROM DRIVER
-M: Bartosz Golaszewski <brgl@bgdev.pl>
+M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-i2c@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
@@ -4123,6 +4165,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml
F: drivers/iio/adc/hx711.c
+AWINIC AW99706 WLED BACKLIGHT DRIVER
+M: Junjie Cao <caojunjie650@gmail.com>
+S: Maintained
+F: Documentation/devicetree/bindings/leds/backlight/awinic,aw99706.yaml
+F: drivers/video/backlight/aw99706.c
+
AX.25 NETWORK LAYER
L: linux-hams@vger.kernel.org
S: Orphan
@@ -4300,7 +4348,7 @@ F: Documentation/filesystems/befs.rst
F: fs/befs/
BFQ I/O SCHEDULER
-M: Yu Kuai <yukuai3@huawei.com>
+M: Yu Kuai <yukuai@fnnas.com>
L: linux-block@vger.kernel.org
S: Odd Fixes
F: Documentation/block/bfq-iosched.rst
@@ -4393,13 +4441,15 @@ BLOCK LAYER
M: Jens Axboe <axboe@kernel.dk>
L: linux-block@vger.kernel.org
S: Maintained
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git
F: Documentation/ABI/stable/sysfs-block
F: Documentation/block/
F: block/
F: drivers/block/
F: include/linux/bio.h
F: include/linux/blk*
+F: include/uapi/linux/blk*
+F: include/uapi/linux/ioprio.h
F: kernel/trace/blktrace.c
F: lib/sbitmap.c
@@ -4648,6 +4698,7 @@ F: Documentation/userspace-api/ebpf/
F: arch/*/net/*
F: include/linux/bpf*
F: include/linux/btf*
+F: include/linux/buildid.h
F: include/linux/filter.h
F: include/trace/events/xdp.h
F: include/uapi/linux/bpf*
@@ -4813,6 +4864,7 @@ F: drivers/net/dsa/b53/*
F: drivers/net/dsa/bcm_sf2*
F: include/linux/dsa/brcm.h
F: include/linux/platform_data/b53.h
+F: net/dsa/tag_brcm.c
BROADCOM BCM2711/BCM2835 ARM ARCHITECTURE
M: Florian Fainelli <florian.fainelli@broadcom.com>
@@ -5124,7 +5176,6 @@ F: Documentation/devicetree/bindings/net/brcm,unimac-mdio.yaml
F: drivers/net/ethernet/broadcom/genet/
F: drivers/net/ethernet/broadcom/unimac.h
F: drivers/net/mdio/mdio-bcm-unimac.c
-F: include/linux/platform_data/bcmgenet.h
F: include/linux/platform_data/mdio-bcm-unimac.h
BROADCOM IPROC ARM ARCHITECTURE
@@ -5192,6 +5243,13 @@ W: http://www.broadcom.com
F: drivers/infiniband/hw/bnxt_re/
F: include/uapi/rdma/bnxt_re-abi.h
+BROADCOM 800 GIGABIT ROCE DRIVER
+M: Siva Reddy Kallam <siva.kallam@broadcom.com>
+L: linux-rdma@vger.kernel.org
+S: Supported
+W: http://www.broadcom.com
+F: drivers/infiniband/hw/bng_re/
+
BROADCOM NVRAM DRIVER
M: Rafał Miłecki <zajec5@gmail.com>
L: linux-mips@vger.kernel.org
@@ -6606,7 +6664,6 @@ CRYPTOGRAPHIC RANDOM NUMBER GENERATOR
M: Neil Horman <nhorman@tuxdriver.com>
L: linux-crypto@vger.kernel.org
S: Maintained
-F: crypto/ansi_cprng.c
F: crypto/rng.c
CS3308 MEDIA DRIVER
@@ -7190,7 +7247,6 @@ F: Documentation/devicetree/bindings/input/dlg,da72??.yaml
F: Documentation/devicetree/bindings/input/dlg,da9062-onkey.yaml
F: Documentation/devicetree/bindings/mfd/da90*.txt
F: Documentation/devicetree/bindings/mfd/dlg,da90*.yaml
-F: Documentation/devicetree/bindings/regulator/da92*.txt
F: Documentation/devicetree/bindings/regulator/dlg,da9*.yaml
F: Documentation/devicetree/bindings/regulator/dlg,slg51000.yaml
F: Documentation/devicetree/bindings/sound/da[79]*.txt
@@ -7309,6 +7365,7 @@ F: Documentation/userspace-api/dma-buf-alloc-exchange.rst
F: drivers/dma-buf/
F: include/linux/*fence.h
F: include/linux/dma-buf.h
+F: include/linux/dma-buf/
F: include/linux/dma-resv.h
K: \bdma_(?:buf|fence|resv)\b
@@ -7412,16 +7469,10 @@ S: Maintained
P: Documentation/doc-guide/maintainer-profile.rst
T: git git://git.lwn.net/linux.git docs-next
F: Documentation/
-F: scripts/check-variable-fonts.sh
-F: scripts/checktransupdate.py
-F: scripts/documentation-file-ref-check
-F: scripts/get_abi.py
F: scripts/kernel-doc*
-F: scripts/lib/abi/*
-F: scripts/lib/kdoc/*
-F: tools/docs/*
+F: tools/lib/python/*
+F: tools/docs/
F: tools/net/ynl/pyynl/lib/doc_generator.py
-F: scripts/sphinx-pre-install
X: Documentation/ABI/
X: Documentation/admin-guide/media/
X: Documentation/devicetree/
@@ -7454,9 +7505,10 @@ DOCUMENTATION SCRIPTS
M: Mauro Carvalho Chehab <mchehab@kernel.org>
L: linux-doc@vger.kernel.org
S: Maintained
-F: Documentation/sphinx/parse-headers.pl
-F: scripts/documentation-file-ref-check
-F: scripts/sphinx-pre-install
+F: Documentation/sphinx/
+F: scripts/kernel-doc*
+F: tools/lib/python/*
+F: tools/docs/
DOCUMENTATION/ITALIAN
M: Federico Vaga <federico.vaga@vaga.pv.it>
@@ -7479,7 +7531,7 @@ F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9714.yaml
F: drivers/media/i2c/dw9714.c
DONGWOON DW9719 LENS VOICE COIL DRIVER
-M: Daniel Scally <djrscally@gmail.com>
+M: Daniel Scally <dan.scally@ideasonboard.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@@ -7637,8 +7689,7 @@ F: drivers/accel/
F: include/drm/drm_accel.h
DRM DRIVER FOR ALLWINNER DE2 AND DE3 ENGINE
-M: Maxime Ripard <mripard@kernel.org>
-M: Chen-Yu Tsai <wens@csie.org>
+M: Chen-Yu Tsai <wens@kernel.org>
R: Jernej Skrabec <jernej.skrabec@gmail.com>
L: dri-devel@lists.freedesktop.org
S: Supported
@@ -7747,7 +7798,8 @@ F: Documentation/devicetree/bindings/display/panel/panel-edp.yaml
F: drivers/gpu/drm/panel/panel-edp.c
DRM DRIVER FOR GENERIC USB DISPLAY
-S: Orphan
+M: Ruben Wauters <rubenru09@aol.com>
+S: Maintained
W: https://github.com/notro/gud/wiki
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/gud/
@@ -7869,6 +7921,7 @@ DRM DRIVER for Qualcomm Adreno GPUs
M: Rob Clark <robin.clark@oss.qualcomm.com>
R: Sean Paul <sean@poorly.run>
R: Konrad Dybcio <konradybcio@kernel.org>
+R: Akhil P Oommen <akhilpo@oss.qualcomm.com>
L: linux-arm-msm@vger.kernel.org
L: dri-devel@lists.freedesktop.org
L: freedreno@lists.freedesktop.org
@@ -7888,7 +7941,7 @@ DRM DRIVER for Qualcomm display hardware
M: Rob Clark <robin.clark@oss.qualcomm.com>
M: Dmitry Baryshkov <lumag@kernel.org>
R: Abhinav Kumar <abhinav.kumar@linux.dev>
-R: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
+R: Jessica Zhang <jesszhan0024@gmail.com>
R: Sean Paul <sean@poorly.run>
R: Marijn Suijten <marijn.suijten@somainline.org>
L: linux-arm-msm@vger.kernel.org
@@ -8057,12 +8110,25 @@ S: Maintained
F: Documentation/devicetree/bindings/display/panel/samsung,s6d7aa0.yaml
F: drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c
+DRM DRIVER FOR SAMSUNG S6E3FC2X01 DDIC
+M: David Heidelberg <david@ixit.cz>
+S: Maintained
+F: Documentation/devicetree/bindings/display/panel/samsung,s6e3fc2x01.yaml
+F: drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c
+
DRM DRIVER FOR SAMSUNG S6E3HA8 PANELS
M: Dzmitry Sankouski <dsankouski@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/display/panel/samsung,s6e3ha8.yaml
F: drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
+DRM DRIVER FOR SAMSUNG SOFEF00 DDIC
+M: David Heidelberg <david@ixit.cz>
+M: Casey Connolly <casey.connolly@linaro.org>
+S: Maintained
+F: Documentation/devicetree/bindings/display/panel/samsung,sofef00.yaml
+F: drivers/gpu/drm/panel/panel-samsung-sofef00.c
+
DRM DRIVER FOR SHARP MEMORY LCD
M: Alex Lanzano <lanzano.alex@gmail.com>
S: Maintained
@@ -8247,12 +8313,12 @@ S: Supported
W: https://drm.pages.freedesktop.org/maintainer-tools/drm-rust.html
T: git https://gitlab.freedesktop.org/drm/rust/kernel.git
F: drivers/gpu/drm/nova/
+F: drivers/gpu/drm/tyr/
F: drivers/gpu/nova-core/
F: rust/kernel/drm/
DRM DRIVERS FOR ALLWINNER A10
-M: Maxime Ripard <mripard@kernel.org>
-M: Chen-Yu Tsai <wens@csie.org>
+M: Chen-Yu Tsai <wens@kernel.org>
L: dri-devel@lists.freedesktop.org
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
@@ -8579,6 +8645,7 @@ S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/scheduler/
F: include/drm/gpu_scheduler.h
+F: include/drm/spsc_queue.h
DRM GPUVM
M: Danilo Krummrich <dakr@kernel.org>
@@ -8601,7 +8668,7 @@ F: drivers/gpu/drm/clients/drm_log.c
DRM PANEL DRIVERS
M: Neil Armstrong <neil.armstrong@linaro.org>
-R: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
+R: Jessica Zhang <jesszhan0024@gmail.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
@@ -9181,6 +9248,9 @@ S: Maintained
F: kernel/power/energy_model.c
F: include/linux/energy_model.h
F: Documentation/power/energy-model.rst
+F: Documentation/netlink/specs/em.yaml
+F: include/uapi/linux/energy_model.h
+F: kernel/power/em_netlink*.*
EPAPR HYPERVISOR BYTE CHANNEL DEVICE DRIVER
M: Laurentiu Tudor <laurentiu.tudor@nxp.com>
@@ -9202,6 +9272,7 @@ R: Yue Hu <zbestahu@gmail.com>
R: Jeffle Xu <jefflexu@linux.alibaba.com>
R: Sandeep Dhavale <dhavale@google.com>
R: Hongbo Li <lihongbo22@huawei.com>
+R: Chunhai Guo <guochunhai@vivo.com>
L: linux-erofs@lists.ozlabs.org
S: Maintained
W: https://erofs.docs.kernel.org
@@ -9258,7 +9329,6 @@ M: Ido Schimmel <idosch@nvidia.com>
L: bridge@lists.linux.dev
L: netdev@vger.kernel.org
S: Maintained
-W: http://www.linuxfoundation.org/en/Net:Bridge
F: include/linux/if_bridge.h
F: include/uapi/linux/if_bridge.h
F: include/linux/netfilter_bridge/
@@ -10528,7 +10598,7 @@ L: gfs2@lists.linux.dev
S: Supported
B: https://bugzilla.kernel.org/enter_bug.cgi?product=File%20System&component=gfs2
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2.git
-F: Documentation/filesystems/gfs2*
+F: Documentation/filesystems/gfs2/
F: fs/gfs2/
F: include/uapi/linux/gfs2_ondisk.h
@@ -10671,7 +10741,7 @@ F: tools/gpio/gpio-sloppy-logic-analyzer.sh
GPIO SUBSYSTEM
M: Linus Walleij <linus.walleij@linaro.org>
-M: Bartosz Golaszewski <brgl@bgdev.pl>
+M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-gpio@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
@@ -10688,7 +10758,7 @@ K: GPIOD_FLAGS_BIT_NONEXCLUSIVE
K: devm_gpiod_unhinge
GPIO UAPI
-M: Bartosz Golaszewski <brgl@bgdev.pl>
+M: Bartosz Golaszewski <brgl@kernel.org>
R: Kent Gibson <warthog618@gmail.com>
L: linux-gpio@vger.kernel.org
S: Maintained
@@ -11520,7 +11590,7 @@ F: include/linux/platform_data/huawei-gaokun-ec.h
HUGETLB SUBSYSTEM
M: Muchun Song <muchun.song@linux.dev>
M: Oscar Salvador <osalvador@suse.de>
-R: David Hildenbrand <david@redhat.com>
+R: David Hildenbrand <david@kernel.org>
L: linux-mm@kvack.org
S: Maintained
F: Documentation/ABI/testing/sysfs-kernel-mm-hugepages
@@ -12500,6 +12570,13 @@ F: drivers/gpu/drm/xe/
F: include/drm/intel/
F: include/uapi/drm/xe_drm.h
+INTEL ELKHART LAKE PSE I/O DRIVER
+M: Raag Jadav <raag.jadav@intel.com>
+L: platform-driver-x86@vger.kernel.org
+S: Supported
+F: drivers/platform/x86/intel/ehl_pse_io.c
+F: include/linux/ehl_pse_io_aux.h
+
INTEL ETHERNET DRIVERS
M: Tony Nguyen <anthony.l.nguyen@intel.com>
M: Przemek Kitszel <przemyslaw.kitszel@intel.com>
@@ -12516,6 +12593,7 @@ F: include/linux/avf/virtchnl.h
F: include/linux/net/intel/*/
INTEL ETHERNET PROTOCOL DRIVER FOR RDMA
+M: Krzysztof Czurylo <krzysztof.czurylo@intel.com>
M: Tatyana Nikolova <tatyana.e.nikolova@intel.com>
L: linux-rdma@vger.kernel.org
S: Supported
@@ -12562,6 +12640,7 @@ F: drivers/dma/ioat*
INTEL IAA CRYPTO DRIVER
M: Kristen Accardi <kristen.c.accardi@intel.com>
M: Vinicius Costa Gomes <vinicius.gomes@intel.com>
+M: Kanchana P Sridhar <kanchana.p.sridhar@intel.com>
L: linux-crypto@vger.kernel.org
S: Supported
F: Documentation/driver-api/crypto/iaa/iaa-crypto.rst
@@ -12613,7 +12692,7 @@ INTEL IPU3 CSI-2 CIO2 DRIVER
M: Yong Zhi <yong.zhi@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
M: Bingbu Cao <bingbu.cao@intel.com>
-M: Dan Scally <djrscally@gmail.com>
+M: Dan Scally <dan.scally@ideasonboard.com>
R: Tianshu Qiu <tian.shu.qiu@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
@@ -12856,7 +12935,8 @@ F: tools/testing/selftests/sgx/*
K: \bSGX_
INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
-M: Daniel Scally <djrscally@gmail.com>
+M: Daniel Scally <dan.scally@ideasonboard.com>
+M: Sakari Ailus <sakari.ailus@linux.intel.com>
S: Maintained
F: drivers/platform/x86/intel/int3472/
F: include/linux/platform_data/x86/int3472.h
@@ -13111,6 +13191,15 @@ F: include/uapi/linux/io_uring.h
F: include/uapi/linux/io_uring/
F: io_uring/
+IO_URING ZCRX
+M: Pavel Begunkov <asml.silence@gmail.com>
+L: io-uring@vger.kernel.org
+L: netdev@vger.kernel.org
+T: git https://github.com/isilence/linux.git zcrx/for-next
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git
+S: Maintained
+F: io_uring/zcrx.*
+
IPMI SUBSYSTEM
M: Corey Minyard <corey@minyard.net>
L: openipmi-developer@lists.sourceforge.net (moderated for non-subscribers)
@@ -13246,10 +13335,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending.git mast
F: drivers/infiniband/ulp/isert
ISDN/CMTP OVER BLUETOOTH
-M: Karsten Keil <isdn@linux-pingi.de>
-L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
L: netdev@vger.kernel.org
-S: Odd Fixes
+S: Orphan
W: http://www.isdn4linux.de
F: Documentation/isdn/
F: drivers/isdn/capi/
@@ -13258,10 +13345,8 @@ F: include/uapi/linux/isdn/
F: net/bluetooth/cmtp/
ISDN/mISDN SUBSYSTEM
-M: Karsten Keil <isdn@linux-pingi.de>
-L: isdn4linux@listserv.isdn4linux.de (subscribers-only)
L: netdev@vger.kernel.org
-S: Maintained
+S: Orphan
W: http://www.isdn4linux.de
F: drivers/isdn/Kconfig
F: drivers/isdn/Makefile
@@ -13415,9 +13500,12 @@ F: mm/kasan/
F: scripts/Makefile.kasan
KCONFIG
+M: Nathan Chancellor <nathan@kernel.org>
+M: Nicolas Schier <nsc@kernel.org>
L: linux-kbuild@vger.kernel.org
-S: Orphan
+S: Odd Fixes
Q: https://patchwork.kernel.org/project/linux-kbuild/list/
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux.git
F: Documentation/kbuild/kconfig*
F: scripts/Kconfig.include
F: scripts/kconfig/
@@ -13483,7 +13571,7 @@ F: fs/autofs/
KERNEL BUILD + files below scripts/ (unless maintained elsewhere)
M: Nathan Chancellor <nathan@kernel.org>
-M: Nicolas Schier <nicolas@fjasle.eu>
+M: Nicolas Schier <nsc@kernel.org>
L: linux-kbuild@vger.kernel.org
S: Odd Fixes
Q: https://patchwork.kernel.org/project/linux-kbuild/list/
@@ -13602,7 +13690,7 @@ F: fs/smb/server/
KERNEL UNIT TESTING FRAMEWORK (KUnit)
M: Brendan Higgins <brendan.higgins@linux.dev>
M: David Gow <davidgow@google.com>
-R: Rae Moar <rmoar@google.com>
+R: Rae Moar <raemoar63@gmail.com>
L: linux-kselftest@vger.kernel.org
L: kunit-dev@googlegroups.com
S: Maintained
@@ -13643,7 +13731,7 @@ F: virt/kvm/*
KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64)
M: Marc Zyngier <maz@kernel.org>
-M: Oliver Upton <oliver.upton@linux.dev>
+M: Oliver Upton <oupton@kernel.org>
R: Joey Gouly <joey.gouly@arm.com>
R: Suzuki K Poulose <suzuki.poulose@arm.com>
R: Zenghui Yu <yuzenghui@huawei.com>
@@ -13717,7 +13805,7 @@ KERNEL VIRTUAL MACHINE for s390 (KVM/s390)
M: Christian Borntraeger <borntraeger@linux.ibm.com>
M: Janosch Frank <frankja@linux.ibm.com>
M: Claudio Imbrenda <imbrenda@linux.ibm.com>
-R: David Hildenbrand <david@redhat.com>
+R: David Hildenbrand <david@kernel.org>
L: kvm@vger.kernel.org
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
@@ -13782,6 +13870,7 @@ F: Documentation/admin-guide/mm/kho.rst
F: Documentation/core-api/kho/*
F: include/linux/kexec_handover.h
F: kernel/kexec_handover.c
+F: lib/test_kho.c
F: tools/testing/selftests/kho/
KEYS-ENCRYPTED
@@ -14037,7 +14126,7 @@ F: tools/testing/selftests/landlock/
K: landlock
K: LANDLOCK
-LANTIQ / INTEL Ethernet drivers
+LANTIQ / MAXLINEAR / INTEL Ethernet DSA drivers
M: Hauke Mehrtens <hauke@hauke-m.de>
L: netdev@vger.kernel.org
S: Maintained
@@ -14045,6 +14134,7 @@ F: Documentation/devicetree/bindings/net/dsa/lantiq,gswip.yaml
F: drivers/net/dsa/lantiq/*
F: drivers/net/ethernet/lantiq_xrx200.c
F: net/dsa/tag_gswip.c
+F: net/dsa/tag_mxl-gsw1xx.c
LANTIQ MIPS ARCHITECTURE
M: John Crispin <john@phrozen.org>
@@ -14394,6 +14484,7 @@ F: tools/memory-model/
LINUX-NEXT TREE
M: Stephen Rothwell <sfr@canb.auug.org.au>
+M: Mark Brown <broonie@kernel.org>
L: linux-next@vger.kernel.org
S: Supported
B: mailto:linux-next@vger.kernel.org and the appropriate development tree
@@ -14440,10 +14531,11 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching.g
F: Documentation/ABI/testing/sysfs-kernel-livepatch
F: Documentation/livepatch/
F: arch/powerpc/include/asm/livepatch.h
-F: include/linux/livepatch.h
+F: include/linux/livepatch*.h
F: kernel/livepatch/
F: kernel/module/livepatch.c
F: samples/livepatch/
+F: scripts/livepatch/
F: tools/testing/selftests/livepatch/
LLC (802.2)
@@ -14517,6 +14609,7 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core
F: Documentation/locking/
F: arch/*/include/asm/spinlock*.h
+F: include/linux/local_lock*.h
F: include/linux/lockdep*.h
F: include/linux/mutex*.h
F: include/linux/rwlock*.h
@@ -15166,6 +15259,15 @@ S: Maintained
F: Documentation/hwmon/max15301.rst
F: drivers/hwmon/pmbus/max15301.c
+MAX17616 HARDWARE MONITOR DRIVER
+M: Kim Seer Paller <kimseer.paller@analog.com>
+L: linux-hwmon@vger.kernel.org
+S: Supported
+W: https://ez.analog.com/linux-software-drivers
+F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml
+F: Documentation/hwmon/max17616.rst
+F: drivers/hwmon/pmbus/max17616.c
+
MAX2175 SDR TUNER DRIVER
M: Ramesh Shanmugasundaram <rashanmu@gmail.com>
L: linux-media@vger.kernel.org
@@ -15291,7 +15393,7 @@ F: drivers/pwm/pwm-max7360.c
F: include/linux/mfd/max7360.h
MAXIM MAX77650 PMIC MFD DRIVER
-M: Bartosz Golaszewski <brgl@bgdev.pl>
+M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/*/*max77650.yaml
@@ -15393,14 +15495,12 @@ S: Supported
F: drivers/net/phy/mxl-86110.c
F: drivers/net/phy/mxl-gpy.c
-MCAN MMIO DEVICE DRIVER
-M: Chandrasekar Ramakrishnan <rcsekar@samsung.com>
+MCAN DEVICE DRIVER
+M: Markus Schneider-Pargmann <msp@baylibre.com>
L: linux-can@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/net/can/bosch,m_can.yaml
-F: drivers/net/can/m_can/m_can.c
-F: drivers/net/can/m_can/m_can.h
-F: drivers/net/can/m_can/m_can_platform.c
+F: drivers/net/can/m_can/
MCBA MICROCHIP CAN BUS ANALYZER TOOL DRIVER
R: Yasushi SHOJI <yashi@spacecubics.com>
@@ -15524,6 +15624,8 @@ F: drivers/media/pci/ddbridge/*
MEDIA DRIVERS FOR FREESCALE IMX
M: Steve Longerbeam <slongerbeam@gmail.com>
M: Philipp Zabel <p.zabel@pengutronix.de>
+R: Frank Li <Frank.Li@nxp.com>
+L: imx@lists.linux.dev
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@@ -15538,6 +15640,8 @@ M: Rui Miguel Silva <rmfrfs@gmail.com>
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
M: Martin Kepplinger <martin.kepplinger@puri.sm>
R: Purism Kernel Team <kernel@puri.sm>
+R: Frank Li <Frank.Li@nxp.com>
+L: imx@lists.linux.dev
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@@ -16187,7 +16291,7 @@ MEMORY CONTROLLER DRIVERS
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
-B: mailto:krzysztof.kozlowski@linaro.org
+B: mailto:krzk@kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl.git
F: Documentation/devicetree/bindings/memory-controllers/
F: drivers/memory/
@@ -16203,7 +16307,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
F: drivers/devfreq/tegra30-devfreq.c
MEMORY HOT(UN)PLUG
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
M: Oscar Salvador <osalvador@suse.de>
L: linux-mm@kvack.org
S: Maintained
@@ -16228,7 +16332,7 @@ F: tools/mm/
MEMORY MANAGEMENT - CORE
M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
R: Vlastimil Babka <vbabka@suse.cz>
@@ -16284,7 +16388,7 @@ F: mm/execmem.c
MEMORY MANAGEMENT - GUP (GET USER PAGES)
M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
R: Jason Gunthorpe <jgg@nvidia.com>
R: John Hubbard <jhubbard@nvidia.com>
R: Peter Xu <peterx@redhat.com>
@@ -16300,7 +16404,7 @@ F: tools/testing/selftests/mm/gup_test.c
MEMORY MANAGEMENT - KSM (Kernel Samepage Merging)
M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
R: Xu Xin <xu.xin16@zte.com.cn>
R: Chengming Zhou <chengming.zhou@linux.dev>
L: linux-mm@kvack.org
@@ -16316,7 +16420,7 @@ F: mm/mm_slot.h
MEMORY MANAGEMENT - MEMORY POLICY AND MIGRATION
M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
R: Zi Yan <ziy@nvidia.com>
R: Matthew Brost <matthew.brost@intel.com>
R: Joshua Hahn <joshua.hahnjy@gmail.com>
@@ -16356,7 +16460,7 @@ F: mm/workingset.c
MEMORY MANAGEMENT - MISC
M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
R: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
R: Vlastimil Babka <vbabka@suse.cz>
@@ -16444,7 +16548,7 @@ F: mm/shuffle.h
MEMORY MANAGEMENT - RECLAIM
M: Andrew Morton <akpm@linux-foundation.org>
M: Johannes Weiner <hannes@cmpxchg.org>
-R: David Hildenbrand <david@redhat.com>
+R: David Hildenbrand <david@kernel.org>
R: Michal Hocko <mhocko@kernel.org>
R: Qi Zheng <zhengqi.arch@bytedance.com>
R: Shakeel Butt <shakeel.butt@linux.dev>
@@ -16457,7 +16561,7 @@ F: mm/workingset.c
MEMORY MANAGEMENT - RMAP (REVERSE MAPPING)
M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Rik van Riel <riel@surriel.com>
R: Liam R. Howlett <Liam.Howlett@oracle.com>
@@ -16481,12 +16585,12 @@ F: mm/secretmem.c
MEMORY MANAGEMENT - SWAP
M: Andrew Morton <akpm@linux-foundation.org>
+M: Chris Li <chrisl@kernel.org>
+M: Kairui Song <kasong@tencent.com>
R: Kemeng Shi <shikemeng@huaweicloud.com>
-R: Kairui Song <kasong@tencent.com>
R: Nhat Pham <nphamcs@gmail.com>
R: Baoquan He <bhe@redhat.com>
R: Barry Song <baohua@kernel.org>
-R: Chris Li <chrisl@kernel.org>
L: linux-mm@kvack.org
S: Maintained
F: Documentation/mm/swap-table.rst
@@ -16502,7 +16606,7 @@ F: mm/swapfile.c
MEMORY MANAGEMENT - THP (TRANSPARENT HUGE PAGE)
M: Andrew Morton <akpm@linux-foundation.org>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
R: Zi Yan <ziy@nvidia.com>
R: Baolin Wang <baolin.wang@linux.alibaba.com>
@@ -16604,7 +16708,7 @@ MEMORY MAPPING - MADVISE (MEMORY ADVICE)
M: Andrew Morton <akpm@linux-foundation.org>
M: Liam R. Howlett <Liam.Howlett@oracle.com>
M: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
R: Vlastimil Babka <vbabka@suse.cz>
R: Jann Horn <jannh@google.com>
L: linux-mm@kvack.org
@@ -17436,6 +17540,14 @@ S: Maintained
F: Documentation/devicetree/bindings/net/motorcomm,yt8xxx.yaml
F: drivers/net/phy/motorcomm.c
+MOTORCOMM YT921X ETHERNET SWITCH DRIVER
+M: David Yang <mmyangfl@gmail.com>
+L: netdev@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/net/dsa/motorcomm,yt921x.yaml
+F: drivers/net/dsa/yt921x.*
+F: net/dsa/tag_yt921x.c
+
MOXA SMARTIO/INDUSTIO/INTELLIO SERIAL CARD
M: Jiri Slaby <jirislaby@kernel.org>
S: Maintained
@@ -17449,6 +17561,16 @@ S: Maintained
F: Documentation/devicetree/bindings/leds/backlight/mps,mp3309c.yaml
F: drivers/video/backlight/mp3309c.c
+MPAM DRIVER
+M: James Morse <james.morse@arm.com>
+M: Ben Horgan <ben.horgan@arm.com>
+R: Reinette Chatre <reinette.chatre@intel.com>
+R: Fenghua Yu <fenghuay@nvidia.com>
+S: Maintained
+F: drivers/resctrl/mpam_*
+F: drivers/resctrl/test_mpam_*
+F: include/linux/arm_mpam.h
+
MPS MP2869 DRIVER
M: Wensheng Wang <wenswang@yeah.net>
L: linux-hwmon@vger.kernel.org
@@ -17463,6 +17585,13 @@ S: Maintained
F: Documentation/hwmon/mp2891.rst
F: drivers/hwmon/pmbus/mp2891.c
+MPS MP2925 DRIVER
+M: Noah Wang <wenswang@yeah.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/mp2925.rst
+F: drivers/hwmon/pmbus/mp2925.c
+
MPS MP29502 DRIVER
M: Wensheng Wang <wenswang@yeah.net>
L: linux-hwmon@vger.kernel.org
@@ -17484,6 +17613,13 @@ S: Maintained
F: Documentation/hwmon/mp9941.rst
F: drivers/hwmon/pmbus/mp9941.c
+MPS MP9945 DRIVER
+M: Cosmo Chou <chou.cosmo@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/hwmon/mp9945.rst
+F: drivers/hwmon/pmbus/mp9945.c
+
MR800 AVERMEDIA USB FM RADIO DRIVER
M: Alexey Klimov <alexey.klimov@linaro.org>
L: linux-media@vger.kernel.org
@@ -17591,6 +17727,14 @@ T: git git://linuxtv.org/media.git
F: Documentation/devicetree/bindings/media/i2c/aptina,mt9v111.yaml
F: drivers/media/i2c/mt9v111.c
+MUCSE ETHERNET DRIVER
+M: Yibo Dong <dong100@mucse.com>
+L: netdev@vger.kernel.org
+S: Maintained
+W: https://www.mucse.com/en/
+F: Documentation/networking/device_drivers/ethernet/mucse/
+F: drivers/net/ethernet/mucse/
+
MULTIFUNCTION DEVICES (MFD)
M: Lee Jones <lee@kernel.org>
S: Maintained
@@ -18032,6 +18176,7 @@ L: netdev@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec-next.git
+F: Documentation/networking/xfrm/
F: include/net/xfrm.h
F: include/uapi/linux/xfrm.h
F: net/ipv4/ah4.c
@@ -18618,6 +18763,17 @@ S: Maintained
F: Documentation/devicetree/bindings/regulator/nxp,pf5300.yaml
F: drivers/regulator/pf530x-regulator.c
+NXP PF1550 PMIC MFD DRIVER
+M: Samuel Kayode <samkay014@gmail.com>
+L: imx@lists.linux.dev
+S: Maintained
+F: Documentation/devicetree/bindings/mfd/nxp,pf1550.yaml
+F: drivers/input/misc/pf1550-onkey.c
+F: drivers/mfd/pf1550.c
+F: drivers/power/supply/pf1550-charger.c
+F: drivers/regulator/pf1550-regulator.c
+F: include/linux/mfd/pf1550.h
+
NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER
M: Jagan Teki <jagan@amarulasolutions.com>
S: Maintained
@@ -18658,7 +18814,7 @@ NXP TFA9879 DRIVER
M: Peter Rosin <peda@axentia.se>
L: linux-sound@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/sound/nxp,tfa9879.yaml
+F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: sound/soc/codecs/tfa9879*
NXP-NCI NFC DRIVER
@@ -18762,6 +18918,10 @@ S: Maintained
F: arch/arm/*omap*/*clock*
OMAP DEVICE TREE SUPPORT
+M: Aaro Koskinen <aaro.koskinen@iki.fi>
+M: Andreas Kemnade <andreas@kemnade.info>
+M: Kevin Hilman <khilman@baylibre.com>
+M: Roger Quadros <rogerq@kernel.org>
M: Tony Lindgren <tony@atomide.com>
L: linux-omap@vger.kernel.org
L: devicetree@vger.kernel.org
@@ -18982,10 +19142,9 @@ T: git git://linuxtv.org/media.git
F: drivers/media/i2c/ov08d10.c
OMNIVISION OV08X40 SENSOR DRIVER
-M: Jason Chen <jason.z.chen@intel.com>
+M: Jimmy Su <jimmy.su@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
-T: git git://linuxtv.org/media.git
F: drivers/media/i2c/ov08x40.c
F: Documentation/devicetree/bindings/media/i2c/ovti,ov08x40.yaml
@@ -19078,7 +19237,7 @@ F: Documentation/devicetree/bindings/media/i2c/ovti,ov5675.yaml
F: drivers/media/i2c/ov5675.c
OMNIVISION OV5693 SENSOR DRIVER
-M: Daniel Scally <djrscally@gmail.com>
+M: Daniel Scally <dan.scally@ideasonboard.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@@ -19632,6 +19791,13 @@ S: Orphan
F: Documentation/devicetree/bindings/pci/cdns,*
F: drivers/pci/controller/cadence/*cadence*
+PCI DRIVER FOR CIX Sky1
+M: Hans Zhang <hans.zhang@cixtech.com>
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/cix,sky1-pcie-*.yaml
+F: drivers/pci/controller/cadence/*sky1*
+
PCI DRIVER FOR FREESCALE LAYERSCAPE
M: Minghuan Lian <minghuan.Lian@nxp.com>
M: Mingkai Hu <mingkai.hu@nxp.com>
@@ -19881,7 +20047,8 @@ F: drivers/pci/p2pdma.c
F: include/linux/pci-p2pdma.h
PCI POWER CONTROL
-M: Bartosz Golaszewski <brgl@bgdev.pl>
+M: Bartosz Golaszewski <brgl@kernel.org>
+M: Manivannan Sadhasivam <mani@kernel.org>
L: linux-pci@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
@@ -20018,6 +20185,14 @@ S: Maintained
F: drivers/pci/controller/dwc/pcie-qcom-common.c
F: drivers/pci/controller/dwc/pcie-qcom.c
+PCIE DRIVER FOR RENESAS RZ/G3S SERIES
+M: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
+L: linux-pci@vger.kernel.org
+L: linux-renesas-soc@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
+F: drivers/pci/controller/pcie-rzg3s-host.c
+
PCIE DRIVER FOR ROCKCHIP
M: Shawn Lin <shawn.lin@rock-chips.com>
L: linux-pci@vger.kernel.org
@@ -20146,6 +20321,7 @@ R: Alexander Shishkin <alexander.shishkin@linux.intel.com>
R: Jiri Olsa <jolsa@kernel.org>
R: Ian Rogers <irogers@google.com>
R: Adrian Hunter <adrian.hunter@intel.com>
+R: James Clark <james.clark@linaro.org>
L: linux-perf-users@vger.kernel.org
L: linux-kernel@vger.kernel.org
S: Supported
@@ -20477,7 +20653,7 @@ F: include/linux/powercap.h
F: kernel/configs/nopm.config
POWER SEQUENCING
-M: Bartosz Golaszewski <brgl@bgdev.pl>
+M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-pm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
@@ -20574,6 +20750,7 @@ R: John Ogness <john.ogness@linutronix.de>
R: Sergey Senozhatsky <senozhatsky@chromium.org>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git
+F: Documentation/core-api/printk-basics.rst
F: include/linux/printk.h
F: kernel/printk/
@@ -20774,6 +20951,14 @@ F: include/linux/pwm.h
F: include/linux/pwm_backlight.h
K: pwm_(config|apply_might_sleep|apply_atomic|ops)
+PWM SUBSYSTEM BINDINGS [RUST]
+M: Michal Wilczynski <m.wilczynski@samsung.com>
+L: linux-pwm@vger.kernel.org
+L: rust-for-linux@vger.kernel.org
+S: Maintained
+F: rust/helpers/pwm.c
+F: rust/kernel/pwm.rs
+
PXA GPIO DRIVER
M: Robert Jarzmik <robert.jarzmik@free.fr>
L: linux-gpio@vger.kernel.org
@@ -21031,6 +21216,7 @@ F: Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
F: drivers/net/wwan/qcom_bam_dmux.c
QUALCOMM BLUETOOTH DRIVER
+M: Bartosz Golaszewski <brgl@bgdev.pl>
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/bluetooth/btqca.[ch]
@@ -21159,7 +21345,7 @@ F: Documentation/devicetree/bindings/i2c/qcom,i2c-cci.yaml
F: drivers/i2c/busses/i2c-qcom-cci.c
QUALCOMM INTERCONNECT BWMON DRIVER
-M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml
@@ -21280,7 +21466,7 @@ F: Documentation/tee/qtee.rst
F: drivers/tee/qcomtee/
QUALCOMM TRUST ZONE MEMORY ALLOCATOR
-M: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/firmware/qcom/qcom_tzmem.c
@@ -21317,6 +21503,7 @@ F: drivers/media/platform/qcom/venus/
QUALCOMM WCN36XX WIRELESS DRIVER
M: Loic Poulain <loic.poulain@oss.qualcomm.com>
L: wcn36xx@lists.infradead.org
+L: linux-wireless@vger.kernel.org
S: Supported
W: https://wireless.wiki.kernel.org/en/users/Drivers/wcn36xx
F: drivers/net/wireless/ath/wcn36xx/
@@ -21654,6 +21841,11 @@ S: Maintained
F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
F: drivers/spi/spi-realtek-rtl-snand.c
+REALTEK SYSTIMER DRIVER
+M: Hao-Wen Ting <haowen.ting@realtek.com>
+S: Maintained
+F: drivers/clocksource/timer-realtek.c
+
REALTEK WIRELESS DRIVER (rtlwifi family)
M: Ping-Ke Shih <pkshih@realtek.com>
L: linux-wireless@vger.kernel.org
@@ -21915,6 +22107,13 @@ S: Maintained
F: Documentation/devicetree/bindings/net/renesas,rzv2h-gbeth.yaml
F: drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
+RENESAS RZ/V2H(P) INPUT VIDEO CONTROL BLOCK DRIVER
+M: Daniel Scally <dan.scally@ideasonboard.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml
+F: drivers/media/platform/renesas/rzv2h-ivc/
+
RENESAS RZ/V2H(P) RSPI DRIVER
M: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
L: linux-spi@vger.kernel.org
@@ -22118,7 +22317,7 @@ F: drivers/reset/reset-mpfs.c
F: drivers/rtc/rtc-mpfs.c
F: drivers/soc/microchip/mpfs-sys-controller.c
F: drivers/spi/spi-microchip-core-qspi.c
-F: drivers/spi/spi-microchip-core.c
+F: drivers/spi/spi-mpfs.c
F: drivers/usb/musb/mpfs.c
F: include/soc/microchip/mpfs.h
@@ -22182,6 +22381,7 @@ F: Documentation/devicetree/bindings/firmware/thead,th1520-aon.yaml
F: Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml
F: Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml
F: Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml
+F: Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml
F: Documentation/devicetree/bindings/reset/thead,th1520-reset.yaml
F: arch/riscv/boot/dts/thead/
F: drivers/clk/thead/clk-th1520-ap.c
@@ -22192,6 +22392,7 @@ F: drivers/pinctrl/pinctrl-th1520.c
F: drivers/pmdomain/thead/
F: drivers/power/reset/th1520-aon-reboot.c
F: drivers/power/sequencing/pwrseq-thead-gpu.c
+F: drivers/pwm/pwm_th1520.rs
F: drivers/reset/reset-th1520.c
F: include/dt-bindings/clock/thead,th1520-clk-ap.h
F: include/dt-bindings/power/thead,th1520-power.h
@@ -22221,6 +22422,16 @@ S: Maintained
F: Documentation/devicetree/bindings/net/can/rockchip,rk3568v2-canfd.yaml
F: drivers/net/can/rockchip/
+ROCKCHIP CAMERA INTERFACE (RKCIF) DRIVER
+M: Mehdi Djait <mehdi.djait@linux.intel.com>
+M: Michael Riesch <michael.riesch@collabora.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/admin-guide/media/rkcif*
+F: Documentation/devicetree/bindings/media/rockchip,px30-vip.yaml
+F: Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
+F: drivers/media/platform/rockchip/rkcif/
+
ROCKCHIP CRYPTO DRIVERS
M: Corentin Labbe <clabbe@baylibre.com>
L: linux-crypto@vger.kernel.org
@@ -22300,6 +22511,12 @@ L: linux-serial@vger.kernel.org
S: Odd Fixes
F: drivers/tty/serial/rp2.*
+ROHM BD71828 CHARGER
+M: Andreas Kemnade <andreas@kemnade.info>
+M: Matti Vaittinen <mazziesaccount@gmail.com>
+S: Maintained
+F: drivers/power/supply/bd71828-charger.c
+
ROHM BD79703 DAC
M: Matti Vaittinen <mazziesaccount@gmail.com>
S: Supported
@@ -22483,7 +22700,6 @@ F: tools/verification/
RUST
M: Miguel Ojeda <ojeda@kernel.org>
-M: Alex Gaynor <alex.gaynor@gmail.com>
R: Boqun Feng <boqun.feng@gmail.com>
R: Gary Guo <gary@garyguo.net>
R: Björn Roy Baron <bjorn3_gh@protonmail.com>
@@ -22520,6 +22736,14 @@ T: git https://github.com/Rust-for-Linux/linux.git alloc-next
F: rust/kernel/alloc.rs
F: rust/kernel/alloc/
+RUST [NUM]
+M: Alexandre Courbot <acourbot@nvidia.com>
+R: Yury Norov <yury.norov@gmail.com>
+L: rust-for-linux@vger.kernel.org
+S: Maintained
+F: rust/kernel/num.rs
+F: rust/kernel/num/
+
RUST [PIN-INIT]
M: Benno Lossin <lossin@kernel.org>
L: rust-for-linux@vger.kernel.org
@@ -22630,7 +22854,7 @@ F: arch/s390/mm
S390 NETWORK DRIVERS
M: Alexandra Winter <wintera@linux.ibm.com>
-R: Aswin Karuvally <aswin@linux.ibm.com>
+M: Aswin Karuvally <aswin@linux.ibm.com>
L: linux-s390@vger.kernel.org
L: netdev@vger.kernel.org
S: Supported
@@ -23842,7 +24066,7 @@ F: include/linux/property.h
SOFTWARE RAID (Multiple Disks) SUPPORT
M: Song Liu <song@kernel.org>
-M: Yu Kuai <yukuai3@huawei.com>
+M: Yu Kuai <yukuai@fnnas.com>
L: linux-raid@vger.kernel.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-raid/list/
@@ -24237,6 +24461,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git
F: Documentation/devicetree/bindings/spi/
F: Documentation/spi/
F: drivers/spi/
+F: include/trace/events/spi*
F: include/linux/spi/
F: include/uapi/linux/spi/
F: tools/spi/
@@ -24344,6 +24569,14 @@ S: Maintained
F: Documentation/hwmon/stpddc60.rst
F: drivers/hwmon/pmbus/stpddc60.c
+ST TSC1641 DRIVER
+M: Igor Reznichenko <igor@reznichenko.net>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/hwmon/st,tsc1641.yaml
+F: Documentation/hwmon/tsc1641.rst
+F: drivers/hwmon/tsc1641.c
+
ST VD55G1 DRIVER
M: Benjamin Mugnier <benjamin.mugnier@foss.st.com>
M: Sylvain Petinot <sylvain.petinot@foss.st.com>
@@ -24905,7 +25138,7 @@ F: drivers/net/pcs/pcs-xpcs.h
F: include/linux/pcs/pcs-xpcs.h
SYNOPSYS DESIGNWARE HDMI RX CONTROLLER DRIVER
-M: Shreeya Patel <shreeya.patel@collabora.com>
+M: Dmitry Osipenko <dmitry.osipenko@collabora.com>
L: linux-media@vger.kernel.org
L: kernel@collabora.com
S: Maintained
@@ -24930,6 +25163,7 @@ F: include/linux/soc/amd/isp4_misc.h
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
M: Jaehoon Chung <jh80.chung@samsung.com>
+M: Shawn Lin <shawn.lin@rock-chips.com>
L: linux-mmc@vger.kernel.org
S: Maintained
F: drivers/mmc/host/dw_mmc*
@@ -25098,7 +25332,7 @@ L: linux-media@vger.kernel.org
S: Maintained
W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
-F: Documentation/devicetree/bindings/media/i2c/nxp,tda1997x.txt
+F: Documentation/devicetree/bindings/media/i2c/nxp,tda19971.yaml
F: drivers/media/i2c/tda1997x.*
TDA827x MEDIA DRIVER
@@ -25647,7 +25881,7 @@ F: Documentation/devicetree/bindings/crypto/ti,am62l-dthev2.yaml
F: drivers/crypto/ti/
TI DAVINCI MACHINE SUPPORT
-M: Bartosz Golaszewski <brgl@bgdev.pl>
+M: Bartosz Golaszewski <brgl@kernel.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git
@@ -26031,6 +26265,8 @@ S: Supported
W: https://www.tq-group.com/en/products/tq-embedded/
F: arch/arm/boot/dts/nxp/imx/*mba*.dts*
F: arch/arm/boot/dts/nxp/imx/*tqma*.dts*
+F: arch/arm/boot/dts/ti/omap/*mba*.dts*
+F: arch/arm/boot/dts/ti/omap/*tqma*.dts*
F: arch/arm64/boot/dts/freescale/fsl-*tqml*.dts*
F: arch/arm64/boot/dts/freescale/imx*mba*.dts*
F: arch/arm64/boot/dts/freescale/imx*tqma*.dts*
@@ -26864,6 +27100,16 @@ F: drivers/media/i2c/vd55g1.c
F: drivers/media/i2c/vd56g3.c
F: drivers/media/i2c/vgxy61.c
+V4L2 GENERIC ISP PARAMETERS AND STATISTIC FORMATS
+M: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/driver-api/media/v4l2-isp.rst
+F: Documentation/userspace-api/media/v4l/v4l2-isp.rst
+F: drivers/media/v4l2-core/v4l2-isp.c
+F: include/media/v4l2-isp.h
+F: include/uapi/linux/media/v4l2-isp.h
+
VF610 NAND DRIVER
M: Stefan Agner <stefan@agner.ch>
L: linux-mtd@lists.infradead.org
@@ -26885,7 +27131,7 @@ S: Maintained
F: drivers/vfio/cdx/*
VFIO DRIVER
-M: Alex Williamson <alex.williamson@redhat.com>
+M: Alex Williamson <alex@shazbot.org>
L: kvm@vger.kernel.org
S: Maintained
T: git https://github.com/awilliam/linux-vfio.git
@@ -26975,6 +27221,13 @@ L: virtualization@lists.linux.dev
S: Maintained
F: drivers/vfio/pci/virtio
+VFIO XE PCI DRIVER
+M: Michał Winiarski <michal.winiarski@intel.com>
+L: kvm@vger.kernel.org
+L: intel-xe@lists.freedesktop.org
+S: Supported
+F: drivers/vfio/pci/xe
+
VGA_SWITCHEROO
R: Lukas Wunner <lukas@wunner.de>
S: Maintained
@@ -27048,7 +27301,7 @@ T: git git://linuxtv.org/media.git
F: drivers/media/test-drivers/vimc/*
VIRT LIB
-M: Alex Williamson <alex.williamson@redhat.com>
+M: Alex Williamson <alex@shazbot.org>
M: Paolo Bonzini <pbonzini@redhat.com>
L: kvm@vger.kernel.org
S: Supported
@@ -27069,7 +27322,7 @@ F: net/vmw_vsock/virtio_transport_common.c
VIRTIO BALLOON
M: "Michael S. Tsirkin" <mst@redhat.com>
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
L: virtualization@lists.linux.dev
S: Maintained
F: drivers/virtio/virtio_balloon.c
@@ -27097,7 +27350,7 @@ S: Maintained
F: drivers/char/virtio_console.c
F: include/uapi/linux/virtio_console.h
-VIRTIO CORE AND NET DRIVERS
+VIRTIO CORE
M: "Michael S. Tsirkin" <mst@redhat.com>
M: Jason Wang <jasowang@redhat.com>
R: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
@@ -27110,7 +27363,6 @@ F: Documentation/devicetree/bindings/virtio/
F: Documentation/driver-api/virtio/
F: drivers/block/virtio_blk.c
F: drivers/crypto/virtio/
-F: drivers/net/virtio_net.c
F: drivers/vdpa/
F: drivers/virtio/
F: include/linux/vdpa.h
@@ -27119,7 +27371,6 @@ F: include/linux/vringh.h
F: include/uapi/linux/virtio_*.h
F: net/vmw_vsock/virtio*
F: tools/virtio/
-F: tools/testing/selftests/drivers/net/virtio_net/
VIRTIO CRYPTO DRIVER
M: Gonglei <arei.gonglei@huawei.com>
@@ -27141,6 +27392,7 @@ F: arch/s390/include/uapi/asm/virtio-ccw.h
F: drivers/s390/virtio/
VIRTIO FILE SYSTEM
+M: German Maglione <gmaglione@redhat.com>
M: Vivek Goyal <vgoyal@redhat.com>
M: Stefan Hajnoczi <stefanha@redhat.com>
M: Miklos Szeredi <miklos@szeredi.hu>
@@ -27217,20 +27469,33 @@ F: drivers/virtio/virtio_input.c
F: include/uapi/linux/virtio_input.h
VIRTIO IOMMU DRIVER
-M: Jean-Philippe Brucker <jean-philippe@linaro.org>
+M: Jean-Philippe Brucker <jpb@kernel.org>
L: virtualization@lists.linux.dev
S: Maintained
F: drivers/iommu/virtio-iommu.c
F: include/uapi/linux/virtio_iommu.h
VIRTIO MEM DRIVER
-M: David Hildenbrand <david@redhat.com>
+M: David Hildenbrand <david@kernel.org>
L: virtualization@lists.linux.dev
S: Maintained
W: https://virtio-mem.gitlab.io/
F: drivers/virtio/virtio_mem.c
F: include/uapi/linux/virtio_mem.h
+VIRTIO NET DRIVER
+M: "Michael S. Tsirkin" <mst@redhat.com>
+M: Jason Wang <jasowang@redhat.com>
+R: Xuan Zhuo <xuanzhuo@linux.alibaba.com>
+R: Eugenio Pérez <eperezma@redhat.com>
+L: netdev@vger.kernel.org
+L: virtualization@lists.linux.dev
+S: Maintained
+F: drivers/net/virtio_net.c
+F: include/linux/virtio_net.h
+F: include/uapi/linux/virtio_net.h
+F: tools/testing/selftests/drivers/net/virtio_net/
+
VIRTIO PMEM DRIVER
M: Pankaj Gupta <pankaj.gupta.linux@gmail.com>
L: virtualization@lists.linux.dev
@@ -27621,6 +27886,7 @@ M: Jason A. Donenfeld <Jason@zx2c4.com>
L: wireguard@lists.zx2c4.com
L: netdev@vger.kernel.org
S: Maintained
+F: Documentation/netlink/specs/wireguard.yaml
F: drivers/net/wireguard/
F: tools/testing/selftests/wireguard/
@@ -27647,6 +27913,7 @@ F: Documentation/devicetree/bindings/extcon/wlf,arizona.yaml
F: Documentation/devicetree/bindings/mfd/wlf,arizona.yaml
F: Documentation/devicetree/bindings/mfd/wm831x.txt
F: Documentation/devicetree/bindings/regulator/wlf,arizona.yaml
+F: Documentation/devicetree/bindings/sound/trivial-codec.yaml
F: Documentation/devicetree/bindings/sound/wlf,*.yaml
F: Documentation/devicetree/bindings/sound/wm*
F: Documentation/hwmon/wm83??.rst
@@ -27706,7 +27973,7 @@ F: drivers/acpi/pmic/intel_pmic_xpower.c
N: axp288
X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS
-M: Chen-Yu Tsai <wens@csie.org>
+M: Chen-Yu Tsai <wens@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
N: axp[128]
@@ -27830,7 +28097,7 @@ F: arch/x86/kernel/stacktrace.c
F: arch/x86/kernel/unwind_*.c
X86 TRUST DOMAIN EXTENSIONS (TDX)
-M: Kirill A. Shutemov <kas@kernel.org>
+M: Kiryl Shutsemau <kas@kernel.org>
R: Dave Hansen <dave.hansen@linux.intel.com>
R: Rick Edgecombe <rick.p.edgecombe@intel.com>
L: x86@kernel.org
@@ -28290,6 +28557,13 @@ L: linux-kernel@vger.kernel.org
S: Maintained
F: arch/x86/kernel/cpu/zhaoxin.c
+ZONED BLOCK DEVICE (BLOCK LAYER)
+M: Damien Le Moal <dlemoal@kernel.org>
+L: linux-block@vger.kernel.org
+S: Maintained
+F: block/blk-zoned.c
+F: include/uapi/linux/blkzoned.h
+
ZONED LOOP DEVICE
M: Damien Le Moal <dlemoal@kernel.org>
R: Christoph Hellwig <hch@lst.de>
diff --git a/Makefile b/Makefile
index d14824792227..e61f1300fea0 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 6
PATCHLEVEL = 18
SUBLEVEL = 0
-EXTRAVERSION = -rc2
+EXTRAVERSION =
NAME = Baby Opossum Posse
# *DOCUMENTATION*
@@ -1061,6 +1061,9 @@ NOSTDINC_FLAGS += -nostdinc
# perform bounds checking.
KBUILD_CFLAGS += $(call cc-option, -fstrict-flex-arrays=3)
+# Allow including a tagged struct or union anonymously in another struct/union.
+KBUILD_CFLAGS += -fms-extensions
+
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += -fno-strict-overflow
@@ -1081,7 +1084,7 @@ KBUILD_CPPFLAGS += $(call cc-option,-fmacro-prefix-map=$(srcroot)/=)
endif
# include additional Makefiles when needed
-include-y := scripts/Makefile.extrawarn
+include-y := scripts/Makefile.warn
include-$(CONFIG_DEBUG_INFO) += scripts/Makefile.debug
include-$(CONFIG_DEBUG_INFO_BTF)+= scripts/Makefile.btf
include-$(CONFIG_KASAN) += scripts/Makefile.kasan
@@ -1134,8 +1137,17 @@ ifneq ($(CONFIG_ARCH_VMLINUX_NEEDS_RELOCS),)
LDFLAGS_vmlinux += --emit-relocs --discard-none
endif
-# Align the bit size of userspace programs with the kernel
-USERFLAGS_FROM_KERNEL := -m32 -m64 --target=%
+# Align the architecture of userspace programs with the kernel
+USERFLAGS_FROM_KERNEL := --target=%
+
+ifdef CONFIG_ARCH_USERFLAGS
+KBUILD_USERCFLAGS += $(CONFIG_ARCH_USERFLAGS)
+KBUILD_USERLDFLAGS += $(CONFIG_ARCH_USERFLAGS)
+else
+# If not overridden also inherit the bit size
+USERFLAGS_FROM_KERNEL += -m32 -m64
+endif
+
KBUILD_USERCFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
KBUILD_USERLDFLAGS += $(filter $(USERFLAGS_FROM_KERNEL), $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS))
@@ -1797,9 +1809,10 @@ $(help-board-dirs): help-%:
# Documentation targets
# ---------------------------------------------------------------------------
-DOC_TARGETS := xmldocs latexdocs pdfdocs htmldocs htmldocs-redirects \
- epubdocs cleandocs linkcheckdocs dochelp refcheckdocs \
- texinfodocs infodocs
+DOC_TARGETS := xmldocs latexdocs pdfdocs htmldocs epubdocs cleandocs \
+ linkcheckdocs dochelp refcheckdocs texinfodocs infodocs mandocs \
+ htmldocs-redirects
+
PHONY += $(DOC_TARGETS)
$(DOC_TARGETS):
$(Q)$(MAKE) $(build)=Documentation $@
@@ -1826,10 +1839,17 @@ rusttest: prepare
$(Q)$(MAKE) $(build)=rust $@
# Formatting targets
+#
+# Generated files as well as vendored crates are skipped.
PHONY += rustfmt rustfmtcheck
rustfmt:
$(Q)find $(srctree) $(RCS_FIND_IGNORE) \
+ \( \
+ -path $(srctree)/rust/proc-macro2 \
+ -o -path $(srctree)/rust/quote \
+ -o -path $(srctree)/rust/syn \
+ \) -prune -o \
-type f -a -name '*.rs' -a ! -name '*generated*' -print \
| xargs $(RUSTFMT) $(rustfmt_flags)
diff --git a/README b/README
index fd903645e6de..a9fc263ccd71 100644
--- a/README
+++ b/README
@@ -1,18 +1,156 @@
Linux kernel
============
-There are several guides for kernel developers and users. These guides can
-be rendered in a number of formats, like HTML and PDF. Please read
-Documentation/admin-guide/README.rst first.
+The Linux kernel is the core of any Linux operating system. It manages hardware,
+system resources, and provides the fundamental services for all other software.
-In order to build the documentation, use ``make htmldocs`` or
-``make pdfdocs``. The formatted documentation can also be read online at:
+Quick Start
+-----------
- https://www.kernel.org/doc/html/latest/
+* Report a bug: See Documentation/admin-guide/reporting-issues.rst
+* Get the latest kernel: https://kernel.org
+* Build the kernel: See Documentation/admin-guide/quickly-build-trimmed-linux.rst
+* Join the community: https://lore.kernel.org/
-There are various text files in the Documentation/ subdirectory,
-several of them using the reStructuredText markup notation.
+Essential Documentation
+-----------------------
-Please read the Documentation/process/changes.rst file, as it contains the
-requirements for building and running the kernel, and information about
-the problems which may result by upgrading your kernel.
+All users should be familiar with:
+
+* Building requirements: Documentation/process/changes.rst
+* Code of Conduct: Documentation/process/code-of-conduct.rst
+* License: See COPYING
+
+Documentation can be built with make htmldocs or viewed online at:
+https://www.kernel.org/doc/html/latest/
+
+
+Who Are You?
+============
+
+Find your role below:
+
+* New Kernel Developer - Getting started with kernel development
+* Academic Researcher - Studying kernel internals and architecture
+* Security Expert - Hardening and vulnerability analysis
+* Backport/Maintenance Engineer - Maintaining stable kernels
+* System Administrator - Configuring and troubleshooting
+* Maintainer - Leading subsystems and reviewing patches
+* Hardware Vendor - Writing drivers for new hardware
+* Distribution Maintainer - Packaging kernels for distros
+
+
+For Specific Users
+==================
+
+New Kernel Developer
+--------------------
+
+Welcome! Start your kernel development journey here:
+
+* Getting Started: Documentation/process/development-process.rst
+* Your First Patch: Documentation/process/submitting-patches.rst
+* Coding Style: Documentation/process/coding-style.rst
+* Build System: Documentation/kbuild/index.rst
+* Development Tools: Documentation/dev-tools/index.rst
+* Kernel Hacking Guide: Documentation/kernel-hacking/hacking.rst
+* Core APIs: Documentation/core-api/index.rst
+
+Academic Researcher
+-------------------
+
+Explore the kernel's architecture and internals:
+
+* Researcher Guidelines: Documentation/process/researcher-guidelines.rst
+* Memory Management: Documentation/mm/index.rst
+* Scheduler: Documentation/scheduler/index.rst
+* Networking Stack: Documentation/networking/index.rst
+* Filesystems: Documentation/filesystems/index.rst
+* RCU (Read-Copy Update): Documentation/RCU/index.rst
+* Locking Primitives: Documentation/locking/index.rst
+* Power Management: Documentation/power/index.rst
+
+Security Expert
+---------------
+
+Security documentation and hardening guides:
+
+* Security Documentation: Documentation/security/index.rst
+* LSM Development: Documentation/security/lsm-development.rst
+* Self Protection: Documentation/security/self-protection.rst
+* Reporting Vulnerabilities: Documentation/process/security-bugs.rst
+* CVE Procedures: Documentation/process/cve.rst
+* Embargoed Hardware Issues: Documentation/process/embargoed-hardware-issues.rst
+* Security Features: Documentation/userspace-api/seccomp_filter.rst
+
+Backport/Maintenance Engineer
+-----------------------------
+
+Maintain and stabilize kernel versions:
+
+* Stable Kernel Rules: Documentation/process/stable-kernel-rules.rst
+* Backporting Guide: Documentation/process/backporting.rst
+* Applying Patches: Documentation/process/applying-patches.rst
+* Subsystem Profile: Documentation/maintainer/maintainer-entry-profile.rst
+* Git for Maintainers: Documentation/maintainer/configure-git.rst
+
+System Administrator
+--------------------
+
+Configure, tune, and troubleshoot Linux systems:
+
+* Admin Guide: Documentation/admin-guide/index.rst
+* Kernel Parameters: Documentation/admin-guide/kernel-parameters.rst
+* Sysctl Tuning: Documentation/admin-guide/sysctl/index.rst
+* Tracing/Debugging: Documentation/trace/index.rst
+* Performance Security: Documentation/admin-guide/perf-security.rst
+* Hardware Monitoring: Documentation/hwmon/index.rst
+
+Maintainer
+----------
+
+Lead kernel subsystems and manage contributions:
+
+* Maintainer Handbook: Documentation/maintainer/index.rst
+* Pull Requests: Documentation/maintainer/pull-requests.rst
+* Managing Patches: Documentation/maintainer/modifying-patches.rst
+* Rebasing and Merging: Documentation/maintainer/rebasing-and-merging.rst
+* Development Process: Documentation/process/maintainer-handbooks.rst
+* Maintainer Entry Profile: Documentation/maintainer/maintainer-entry-profile.rst
+* Git Configuration: Documentation/maintainer/configure-git.rst
+
+Hardware Vendor
+---------------
+
+Write drivers and support new hardware:
+
+* Driver API Guide: Documentation/driver-api/index.rst
+* Driver Model: Documentation/driver-api/driver-model/driver.rst
+* Device Drivers: Documentation/driver-api/infrastructure.rst
+* Bus Types: Documentation/driver-api/driver-model/bus.rst
+* Device Tree Bindings: Documentation/devicetree/bindings/
+* Power Management: Documentation/driver-api/pm/index.rst
+* DMA API: Documentation/core-api/dma-api.rst
+
+Distribution Maintainer
+-----------------------
+
+Package and distribute the kernel:
+
+* Stable Kernel Rules: Documentation/process/stable-kernel-rules.rst
+* ABI Documentation: Documentation/ABI/README
+* Kernel Configuration: Documentation/kbuild/kconfig.rst
+* Module Signing: Documentation/admin-guide/module-signing.rst
+* Kernel Parameters: Documentation/admin-guide/kernel-parameters.rst
+* Tainted Kernels: Documentation/admin-guide/tainted-kernels.rst
+
+
+
+Communication and Support
+=========================
+
+* Mailing Lists: https://lore.kernel.org/
+* IRC: #kernelnewbies on irc.oftc.net
+* Bugzilla: https://bugzilla.kernel.org/
+* MAINTAINERS file: Lists subsystem maintainers and mailing lists
+* Email Clients: Documentation/process/email-clients.rst
diff --git a/arch/Kconfig b/arch/Kconfig
index 74ff01133532..61130b88964b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -917,6 +917,13 @@ config ARCH_USES_CFI_TRAPS
An architecture should select this option if it requires the
.kcfi_traps section for KCFI trap handling.
+config ARCH_USES_CFI_GENERIC_LLVM_PASS
+ bool
+ help
+ An architecture should select this option if it uses the generic
+ KCFIPass in LLVM to expand kCFI bundles instead of architecture-specific
+ lowering.
+
config CFI
bool "Use Kernel Control Flow Integrity (kCFI)"
default CFI_CLANG
diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl
index 16dca28ebf17..3fed97478058 100644
--- a/arch/alpha/kernel/syscalls/syscall.tbl
+++ b/arch/alpha/kernel/syscalls/syscall.tbl
@@ -509,3 +509,4 @@
577 common open_tree_attr sys_open_tree_attr
578 common file_getattr sys_file_getattr
579 common file_setattr sys_file_setattr
+580 common listns sys_listns
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 2e3f93b690f4..4fb985b76e97 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -44,6 +44,8 @@ config ARM
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_USE_MEMTEST
+ # https://github.com/llvm/llvm-project/commit/d130f402642fba3d065aacb506cb061c899558de
+ select ARCH_USES_CFI_GENERIC_LLVM_PASS if CLANG_VERSION < 220000
select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
select ARCH_WANT_GENERAL_HUGETLB
select ARCH_WANT_IPC_PARSE_VERSION
diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-fuji-data64.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-fuji-data64.dts
index aa9576d8ab56..48ca25f57ef6 100644
--- a/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-fuji-data64.dts
+++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-fuji-data64.dts
@@ -1254,3 +1254,17 @@
max-frequency = <25000000>;
bus-width = <4>;
};
+
+/*
+ * FIXME: rgmii delay is introduced by MAC (configured in u-boot now)
+ * instead of PCB on fuji board, so the "phy-mode" should be updated to
+ * "rgmii-[tx|rx]id" when the aspeed-mac driver can handle the delay
+ * properly.
+ */
+&mac3 {
+ status = "okay";
+ phy-mode = "rgmii";
+ phy-handle = <&ethphy3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_rgmii4_default>;
+};
diff --git a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
index c78ed064d166..1eb6406449d1 100644
--- a/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2711-rpi.dtsi
@@ -77,6 +77,14 @@
/delete-property/ pinctrl-0;
};
+&pm {
+ clocks = <&firmware_clocks 5>,
+ <&clocks BCM2835_CLOCK_PERI_IMAGE>,
+ <&clocks BCM2835_CLOCK_H264>,
+ <&clocks BCM2835_CLOCK_ISP>;
+ clock-names = "v3d", "peri_image", "h264", "isp";
+};
+
&rmem {
/*
* RPi4's co-processor will copy the board's bootloader configuration
diff --git a/arch/arm/boot/dts/broadcom/bcm2835-rpi-common.dtsi b/arch/arm/boot/dts/broadcom/bcm2835-rpi-common.dtsi
index 8b3c21d9f333..fa9d784c88b6 100644
--- a/arch/arm/boot/dts/broadcom/bcm2835-rpi-common.dtsi
+++ b/arch/arm/boot/dts/broadcom/bcm2835-rpi-common.dtsi
@@ -13,7 +13,16 @@
clock-names = "pixel", "hdmi";
};
+&pm {
+ clocks = <&firmware_clocks 5>,
+ <&clocks BCM2835_CLOCK_PERI_IMAGE>,
+ <&clocks BCM2835_CLOCK_H264>,
+ <&clocks BCM2835_CLOCK_ISP>;
+ clock-names = "v3d", "peri_image", "h264", "isp";
+};
+
&v3d {
+ clocks = <&firmware_clocks 5>;
power-domains = <&power RPI_POWER_DOMAIN_V3D>;
};
diff --git a/arch/arm/boot/dts/broadcom/bcm47189-luxul-xap-1440.dts b/arch/arm/boot/dts/broadcom/bcm47189-luxul-xap-1440.dts
index ac44c745bdf8..a39a021a3910 100644
--- a/arch/arm/boot/dts/broadcom/bcm47189-luxul-xap-1440.dts
+++ b/arch/arm/boot/dts/broadcom/bcm47189-luxul-xap-1440.dts
@@ -55,8 +55,8 @@
mdio {
/delete-node/ switch@1e;
- bcm54210e: ethernet-phy@0 {
- reg = <0>;
+ bcm54210e: ethernet-phy@25 {
+ reg = <25>;
};
};
};
diff --git a/arch/arm/boot/dts/nxp/imx/imx51-zii-rdu1.dts b/arch/arm/boot/dts/nxp/imx/imx51-zii-rdu1.dts
index 06545a6052f7..43ff5eafb2bb 100644
--- a/arch/arm/boot/dts/nxp/imx/imx51-zii-rdu1.dts
+++ b/arch/arm/boot/dts/nxp/imx/imx51-zii-rdu1.dts
@@ -259,7 +259,7 @@
pinctrl-0 = <&pinctrl_audmux>;
status = "okay";
- ssi2 {
+ mux-ssi2 {
fsl,audmux-port = <1>;
fsl,port-config = <
(IMX_AUDMUX_V2_PTCR_SYN |
@@ -271,7 +271,7 @@
>;
};
- aud3 {
+ mux-aud3 {
fsl,audmux-port = <2>;
fsl,port-config = <
IMX_AUDMUX_V2_PTCR_SYN
diff --git a/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi b/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi
index 6de224dd2bb9..6eb80f867f50 100644
--- a/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi
+++ b/arch/arm/boot/dts/nxp/imx/imx6ul.dtsi
@@ -339,7 +339,7 @@
#sound-dai-cells = <0>;
compatible = "fsl,imx6ul-sai", "fsl,imx6sx-sai";
reg = <0x02030000 0x4000>;
- interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_SAI3_IPG>,
<&clks IMX6UL_CLK_SAI3>,
<&clks IMX6UL_CLK_DUMMY>, <&clks IMX6UL_CLK_DUMMY>;
diff --git a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts
index 107b00b9a939..540642e99a41 100644
--- a/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts
+++ b/arch/arm/boot/dts/nxp/imx/imx6ull-engicam-microgea-rmm.dts
@@ -136,7 +136,7 @@
interrupt-parent = <&gpio2>;
interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio2 14 GPIO_ACTIVE_LOW>;
- report-rate-hz = <6>;
+ report-rate-hz = <60>;
/* settings valid only for Hycon touchscreen */
touchscreen-size-x = <1280>;
touchscreen-size-y = <800>;
diff --git a/arch/arm/boot/dts/st/stih410.dtsi b/arch/arm/boot/dts/st/stih410.dtsi
index d56343f44fda..07da9b48ccac 100644
--- a/arch/arm/boot/dts/st/stih410.dtsi
+++ b/arch/arm/boot/dts/st/stih410.dtsi
@@ -34,6 +34,41 @@
status = "disabled";
};
+ display-subsystem {
+ compatible = "st,sti-display-subsystem";
+ ports = <&compositor>, <&hqvdp>, <&tvout>, <&sti_hdmi>;
+
+ assigned-clocks = <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 1>,
+ <&clk_s_c0_pll1 0>,
+ <&clk_s_c0_flexgen CLK_COMPO_DVP>,
+ <&clk_s_c0_flexgen CLK_MAIN_DISP>,
+ <&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
+ <&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP1>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP2>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP3>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP4>;
+
+ assigned-clock-parents = <0>,
+ <0>,
+ <0>,
+ <&clk_s_c0_pll1 0>,
+ <&clk_s_c0_pll1 0>,
+ <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 1>,
+ <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 0>;
+
+ assigned-clock-rates = <297000000>,
+ <297000000>,
+ <0>,
+ <400000000>,
+ <400000000>;
+ };
+
soc {
ohci0: usb@9a03c00 {
compatible = "st,st-ohci-300x";
@@ -99,151 +134,174 @@
status = "disabled";
};
- sti-display-subsystem@0 {
- compatible = "st,sti-display-subsystem";
- #address-cells = <1>;
- #size-cells = <1>;
-
- reg = <0 0>;
- assigned-clocks = <&clk_s_d2_quadfs 0>,
- <&clk_s_d2_quadfs 1>,
- <&clk_s_c0_pll1 0>,
- <&clk_s_c0_flexgen CLK_COMPO_DVP>,
- <&clk_s_c0_flexgen CLK_MAIN_DISP>,
- <&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
- <&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
- <&clk_s_d2_flexgen CLK_PIX_GDP1>,
- <&clk_s_d2_flexgen CLK_PIX_GDP2>,
- <&clk_s_d2_flexgen CLK_PIX_GDP3>,
- <&clk_s_d2_flexgen CLK_PIX_GDP4>;
-
- assigned-clock-parents = <0>,
- <0>,
- <0>,
- <&clk_s_c0_pll1 0>,
- <&clk_s_c0_pll1 0>,
- <&clk_s_d2_quadfs 0>,
- <&clk_s_d2_quadfs 1>,
- <&clk_s_d2_quadfs 0>,
+ compositor: display-controller@9d11000 {
+ compatible = "st,stih407-compositor";
+ reg = <0x9d11000 0x1000>;
+
+ clock-names = "compo_main",
+ "compo_aux",
+ "pix_main",
+ "pix_aux",
+ "pix_gdp1",
+ "pix_gdp2",
+ "pix_gdp3",
+ "pix_gdp4",
+ "main_parent",
+ "aux_parent";
+
+ clocks = <&clk_s_c0_flexgen CLK_COMPO_DVP>,
+ <&clk_s_c0_flexgen CLK_COMPO_DVP>,
+ <&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
+ <&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP1>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP2>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP3>,
+ <&clk_s_d2_flexgen CLK_PIX_GDP4>,
+ <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 1>;
+
+ reset-names = "compo-main", "compo-aux";
+ resets = <&softreset STIH407_COMPO_SOFTRESET>,
+ <&softreset STIH407_COMPO_SOFTRESET>;
+ st,vtg = <&vtg_main>, <&vtg_aux>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ compo_main_out: endpoint {
+ remote-endpoint = <&tvout_in0>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ compo_aux_out: endpoint {
+ remote-endpoint = <&tvout_in1>;
+ };
+ };
+ };
+ };
+
+ tvout: encoder@8d08000 {
+ compatible = "st,stih407-tvout";
+ reg = <0x8d08000 0x1000>;
+ reg-names = "tvout-reg";
+ reset-names = "tvout";
+ resets = <&softreset STIH407_HDTVOUT_SOFTRESET>;
+ assigned-clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
+ <&clk_s_d2_flexgen CLK_TMDS_HDMI>,
+ <&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
+ <&clk_s_d0_flexgen CLK_PCM_0>,
+ <&clk_s_d2_flexgen CLK_PIX_HDDAC>,
+ <&clk_s_d2_flexgen CLK_HDDAC>;
+
+ assigned-clock-parents = <&clk_s_d2_quadfs 0>,
+ <&clk_tmdsout_hdmi>,
<&clk_s_d2_quadfs 0>,
+ <&clk_s_d0_quadfs 0>,
<&clk_s_d2_quadfs 0>,
<&clk_s_d2_quadfs 0>;
- assigned-clock-rates = <297000000>,
- <297000000>,
- <0>,
- <400000000>,
- <400000000>;
-
- ranges;
-
- sti-compositor@9d11000 {
- compatible = "st,stih407-compositor";
- reg = <0x9d11000 0x1000>;
-
- clock-names = "compo_main",
- "compo_aux",
- "pix_main",
- "pix_aux",
- "pix_gdp1",
- "pix_gdp2",
- "pix_gdp3",
- "pix_gdp4",
- "main_parent",
- "aux_parent";
-
- clocks = <&clk_s_c0_flexgen CLK_COMPO_DVP>,
- <&clk_s_c0_flexgen CLK_COMPO_DVP>,
- <&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
- <&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
- <&clk_s_d2_flexgen CLK_PIX_GDP1>,
- <&clk_s_d2_flexgen CLK_PIX_GDP2>,
- <&clk_s_d2_flexgen CLK_PIX_GDP3>,
- <&clk_s_d2_flexgen CLK_PIX_GDP4>,
- <&clk_s_d2_quadfs 0>,
- <&clk_s_d2_quadfs 1>;
-
- reset-names = "compo-main", "compo-aux";
- resets = <&softreset STIH407_COMPO_SOFTRESET>,
- <&softreset STIH407_COMPO_SOFTRESET>;
- st,vtg = <&vtg_main>, <&vtg_aux>;
- };
-
- sti-tvout@8d08000 {
- compatible = "st,stih407-tvout";
- reg = <0x8d08000 0x1000>;
- reg-names = "tvout-reg";
- reset-names = "tvout";
- resets = <&softreset STIH407_HDTVOUT_SOFTRESET>;
+ ports {
#address-cells = <1>;
- #size-cells = <1>;
- assigned-clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
- <&clk_s_d2_flexgen CLK_TMDS_HDMI>,
- <&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
- <&clk_s_d0_flexgen CLK_PCM_0>,
- <&clk_s_d2_flexgen CLK_PIX_HDDAC>,
- <&clk_s_d2_flexgen CLK_HDDAC>;
-
- assigned-clock-parents = <&clk_s_d2_quadfs 0>,
- <&clk_tmdsout_hdmi>,
- <&clk_s_d2_quadfs 0>,
- <&clk_s_d0_quadfs 0>,
- <&clk_s_d2_quadfs 0>,
- <&clk_s_d2_quadfs 0>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ tvout_in0: endpoint {
+ remote-endpoint = <&compo_main_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ tvout_in1: endpoint {
+ remote-endpoint = <&compo_aux_out>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ tvout_out0: endpoint {
+ remote-endpoint = <&hdmi_in>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+ tvout_out1: endpoint {
+ remote-endpoint = <&hda_in>;
+ };
+ };
};
+ };
- sti_hdmi: sti-hdmi@8d04000 {
- compatible = "st,stih407-hdmi";
- reg = <0x8d04000 0x1000>;
- reg-names = "hdmi-reg";
- #sound-dai-cells = <0>;
- interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "irq";
- clock-names = "pix",
- "tmds",
- "phy",
- "audio",
- "main_parent",
- "aux_parent";
-
- clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
- <&clk_s_d2_flexgen CLK_TMDS_HDMI>,
- <&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
- <&clk_s_d0_flexgen CLK_PCM_0>,
- <&clk_s_d2_quadfs 0>,
- <&clk_s_d2_quadfs 1>;
-
- hdmi,hpd-gpio = <&pio5 3 GPIO_ACTIVE_LOW>;
- reset-names = "hdmi";
- resets = <&softreset STIH407_HDMI_TX_PHY_SOFTRESET>;
- ddc = <&hdmiddc>;
+ sti_hdmi: hdmi@8d04000 {
+ compatible = "st,stih407-hdmi";
+ reg = <0x8d04000 0x1000>;
+ reg-names = "hdmi-reg";
+ #sound-dai-cells = <0>;
+ interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "irq";
+ clock-names = "pix",
+ "tmds",
+ "phy",
+ "audio",
+ "main_parent",
+ "aux_parent";
+
+ clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
+ <&clk_s_d2_flexgen CLK_TMDS_HDMI>,
+ <&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
+ <&clk_s_d0_flexgen CLK_PCM_0>,
+ <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 1>;
+
+ hdmi,hpd-gpio = <&pio5 3 GPIO_ACTIVE_LOW>;
+ reset-names = "hdmi";
+ resets = <&softreset STIH407_HDMI_TX_PHY_SOFTRESET>;
+ ddc = <&hdmiddc>;
+
+ port {
+ hdmi_in: endpoint {
+ remote-endpoint = <&tvout_out0>;
+ };
};
+ };
- sti-hda@8d02000 {
- compatible = "st,stih407-hda";
- status = "disabled";
- reg = <0x8d02000 0x400>, <0x92b0120 0x4>;
- reg-names = "hda-reg", "video-dacs-ctrl";
- clock-names = "pix",
- "hddac",
- "main_parent",
- "aux_parent";
- clocks = <&clk_s_d2_flexgen CLK_PIX_HDDAC>,
- <&clk_s_d2_flexgen CLK_HDDAC>,
- <&clk_s_d2_quadfs 0>,
- <&clk_s_d2_quadfs 1>;
+ analog@8d02000 {
+ compatible = "st,stih407-hda";
+ status = "disabled";
+ reg = <0x8d02000 0x400>, <0x92b0120 0x4>;
+ reg-names = "hda-reg", "video-dacs-ctrl";
+ clock-names = "pix",
+ "hddac",
+ "main_parent",
+ "aux_parent";
+ clocks = <&clk_s_d2_flexgen CLK_PIX_HDDAC>,
+ <&clk_s_d2_flexgen CLK_HDDAC>,
+ <&clk_s_d2_quadfs 0>,
+ <&clk_s_d2_quadfs 1>;
+
+ port {
+ hda_in: endpoint {
+ remote-endpoint = <&tvout_out1>;
+ };
};
+ };
- sti-hqvdp@9c00000 {
- compatible = "st,stih407-hqvdp";
- reg = <0x9C00000 0x100000>;
- clock-names = "hqvdp", "pix_main";
- clocks = <&clk_s_c0_flexgen CLK_MAIN_DISP>,
- <&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>;
- reset-names = "hqvdp";
- resets = <&softreset STIH407_HDQVDP_SOFTRESET>;
- st,vtg = <&vtg_main>;
- };
+ hqvdp: plane@9c00000 {
+ compatible = "st,stih407-hqvdp";
+ reg = <0x9C00000 0x100000>;
+ clock-names = "hqvdp", "pix_main";
+ clocks = <&clk_s_c0_flexgen CLK_MAIN_DISP>,
+ <&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>;
+ reset-names = "hqvdp";
+ resets = <&softreset STIH407_HDQVDP_SOFTRESET>;
+ st,vtg = <&vtg_main>;
};
bdisp0:bdisp@9f10000 {
diff --git a/arch/arm/configs/axm55xx_defconfig b/arch/arm/configs/axm55xx_defconfig
index 242a61208a0f..22b189090e15 100644
--- a/arch/arm/configs/axm55xx_defconfig
+++ b/arch/arm/configs/axm55xx_defconfig
@@ -232,4 +232,3 @@ CONFIG_RCU_CPU_STALL_TIMEOUT=60
CONFIG_DEBUG_USER=y
CONFIG_CRYPTO_GCM=y
CONFIG_CRYPTO_SHA256=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/clps711x_defconfig b/arch/arm/configs/clps711x_defconfig
index 6fa3477e6b02..f66d502ce2ef 100644
--- a/arch/arm/configs/clps711x_defconfig
+++ b/arch/arm/configs/clps711x_defconfig
@@ -75,5 +75,4 @@ CONFIG_MINIX_FS=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
CONFIG_EARLY_PRINTK=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/arm/configs/dove_defconfig b/arch/arm/configs/dove_defconfig
index bb6c4748bfc8..e98c35df675e 100644
--- a/arch/arm/configs/dove_defconfig
+++ b/arch/arm/configs/dove_defconfig
@@ -126,7 +126,6 @@ CONFIG_CRYPTO_SHA256=y
CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_DEV_MARVELL_CESA=y
CONFIG_PRINTK_TIME=y
# CONFIG_DEBUG_BUGVERBOSE is not set
diff --git a/arch/arm/configs/ep93xx_defconfig b/arch/arm/configs/ep93xx_defconfig
index 7f3756d8b086..9f3c7324d1cf 100644
--- a/arch/arm/configs/ep93xx_defconfig
+++ b/arch/arm/configs/ep93xx_defconfig
@@ -119,4 +119,3 @@ CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/jornada720_defconfig b/arch/arm/configs/jornada720_defconfig
index e6ec768f42e2..d57285cfefb2 100644
--- a/arch/arm/configs/jornada720_defconfig
+++ b/arch/arm/configs/jornada720_defconfig
@@ -92,4 +92,3 @@ CONFIG_NLS_UTF8=m
CONFIG_DEBUG_KERNEL=y
# CONFIG_FTRACE is not set
CONFIG_DEBUG_LL=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/keystone_defconfig b/arch/arm/configs/keystone_defconfig
index c1291ca290b2..b0cadd878152 100644
--- a/arch/arm/configs/keystone_defconfig
+++ b/arch/arm/configs/keystone_defconfig
@@ -228,7 +228,6 @@ CONFIG_CRYPTO_DES=y
CONFIG_CRYPTO_CBC=y
CONFIG_CRYPTO_CTR=y
CONFIG_CRYPTO_XCBC=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_USER_API_HASH=y
CONFIG_CRYPTO_USER_API_SKCIPHER=y
CONFIG_DMA_CMA=y
diff --git a/arch/arm/configs/lpc32xx_defconfig b/arch/arm/configs/lpc32xx_defconfig
index 9afccd76446b..2bddb0924a8c 100644
--- a/arch/arm/configs/lpc32xx_defconfig
+++ b/arch/arm/configs/lpc32xx_defconfig
@@ -177,7 +177,6 @@ CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_UTF8=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
# CONFIG_CRYPTO_HW is not set
CONFIG_PRINTK_TIME=y
CONFIG_DYNAMIC_DEBUG=y
diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index f67e9cda73e2..a9a212abfd69 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -78,4 +78,3 @@ CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
CONFIG_DEBUG_MMP_UART3=y
CONFIG_EARLY_PRINTK=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/mv78xx0_defconfig b/arch/arm/configs/mv78xx0_defconfig
index 55f4ab67a306..d3a26efe766c 100644
--- a/arch/arm/configs/mv78xx0_defconfig
+++ b/arch/arm/configs/mv78xx0_defconfig
@@ -121,4 +121,3 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_SCHEDSTATS=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig
index 24c54bf1e243..dee820474f44 100644
--- a/arch/arm/configs/omap1_defconfig
+++ b/arch/arm/configs/omap1_defconfig
@@ -220,7 +220,6 @@ CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_PCBC=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_FONTS=y
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y
diff --git a/arch/arm/configs/orion5x_defconfig b/arch/arm/configs/orion5x_defconfig
index c28426250ec3..002c9145026b 100644
--- a/arch/arm/configs/orion5x_defconfig
+++ b/arch/arm/configs/orion5x_defconfig
@@ -145,4 +145,3 @@ CONFIG_LATENCYTOP=y
# CONFIG_FTRACE is not set
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/pxa168_defconfig b/arch/arm/configs/pxa168_defconfig
index 4748c7d33cb8..8cbca84fe33a 100644
--- a/arch/arm/configs/pxa168_defconfig
+++ b/arch/arm/configs/pxa168_defconfig
@@ -48,4 +48,3 @@ CONFIG_MAGIC_SYSRQ=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/pxa3xx_defconfig b/arch/arm/configs/pxa3xx_defconfig
index 381356faf382..07d422f0ff34 100644
--- a/arch/arm/configs/pxa3xx_defconfig
+++ b/arch/arm/configs/pxa3xx_defconfig
@@ -106,5 +106,4 @@ CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_SPINLOCK_SLEEP=y
# CONFIG_FTRACE is not set
CONFIG_DEBUG_USER=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/arm/configs/pxa910_defconfig b/arch/arm/configs/pxa910_defconfig
index 49b59c600ae1..71ed0d73f8a9 100644
--- a/arch/arm/configs/pxa910_defconfig
+++ b/arch/arm/configs/pxa910_defconfig
@@ -59,4 +59,3 @@ CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
CONFIG_DEBUG_MMP_UART2=y
CONFIG_EARLY_PRINTK=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig
index 395df2f9dc8e..c130af6d44d4 100644
--- a/arch/arm/configs/spitz_defconfig
+++ b/arch/arm/configs/spitz_defconfig
@@ -228,7 +228,6 @@ CONFIG_CRYPTO_KHAZAD=m
CONFIG_CRYPTO_SERPENT=m
CONFIG_CRYPTO_TEA=m
CONFIG_CRYPTO_TWOFISH=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
diff --git a/arch/arm/crypto/Kconfig b/arch/arm/crypto/Kconfig
index c436eec22d86..f30d743df264 100644
--- a/arch/arm/crypto/Kconfig
+++ b/arch/arm/crypto/Kconfig
@@ -33,22 +33,6 @@ config CRYPTO_NHPOLY1305_NEON
Architecture: arm using:
- NEON (Advanced SIMD) extensions
-config CRYPTO_BLAKE2B_NEON
- tristate "Hash functions: BLAKE2b (NEON)"
- depends on KERNEL_MODE_NEON
- select CRYPTO_BLAKE2B
- help
- BLAKE2b cryptographic hash function (RFC 7693)
-
- Architecture: arm using
- - NEON (Advanced SIMD) extensions
-
- BLAKE2b digest algorithm optimized with ARM NEON instructions.
- On ARM processors that have NEON support but not the ARMv8
- Crypto Extensions, typically this BLAKE2b implementation is
- much faster than the SHA-2 family and slightly faster than
- SHA-1.
-
config CRYPTO_AES_ARM
tristate "Ciphers: AES"
select CRYPTO_ALGAPI
diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile
index 6346a73effc0..86dd43313dbf 100644
--- a/arch/arm/crypto/Makefile
+++ b/arch/arm/crypto/Makefile
@@ -5,7 +5,6 @@
obj-$(CONFIG_CRYPTO_AES_ARM) += aes-arm.o
obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o
-obj-$(CONFIG_CRYPTO_BLAKE2B_NEON) += blake2b-neon.o
obj-$(CONFIG_CRYPTO_NHPOLY1305_NEON) += nhpoly1305-neon.o
obj-$(CONFIG_CRYPTO_AES_ARM_CE) += aes-arm-ce.o
@@ -13,7 +12,6 @@ obj-$(CONFIG_CRYPTO_GHASH_ARM_CE) += ghash-arm-ce.o
aes-arm-y := aes-cipher-core.o aes-cipher-glue.o
aes-arm-bs-y := aes-neonbs-core.o aes-neonbs-glue.o
-blake2b-neon-y := blake2b-neon-core.o blake2b-neon-glue.o
aes-arm-ce-y := aes-ce-core.o aes-ce-glue.o
ghash-arm-ce-y := ghash-ce-core.o ghash-ce-glue.o
nhpoly1305-neon-y := nh-neon-core.o nhpoly1305-neon-glue.o
diff --git a/arch/arm/crypto/blake2b-neon-glue.c b/arch/arm/crypto/blake2b-neon-glue.c
deleted file mode 100644
index 2ff443a91724..000000000000
--- a/arch/arm/crypto/blake2b-neon-glue.c
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * BLAKE2b digest algorithm, NEON accelerated
- *
- * Copyright 2020 Google LLC
- */
-
-#include <crypto/internal/blake2b.h>
-#include <crypto/internal/hash.h>
-
-#include <linux/module.h>
-#include <linux/sizes.h>
-
-#include <asm/neon.h>
-#include <asm/simd.h>
-
-asmlinkage void blake2b_compress_neon(struct blake2b_state *state,
- const u8 *block, size_t nblocks, u32 inc);
-
-static void blake2b_compress_arch(struct blake2b_state *state,
- const u8 *block, size_t nblocks, u32 inc)
-{
- do {
- const size_t blocks = min_t(size_t, nblocks,
- SZ_4K / BLAKE2B_BLOCK_SIZE);
-
- kernel_neon_begin();
- blake2b_compress_neon(state, block, blocks, inc);
- kernel_neon_end();
-
- nblocks -= blocks;
- block += blocks * BLAKE2B_BLOCK_SIZE;
- } while (nblocks);
-}
-
-static int crypto_blake2b_update_neon(struct shash_desc *desc,
- const u8 *in, unsigned int inlen)
-{
- return crypto_blake2b_update_bo(desc, in, inlen, blake2b_compress_arch);
-}
-
-static int crypto_blake2b_finup_neon(struct shash_desc *desc, const u8 *in,
- unsigned int inlen, u8 *out)
-{
- return crypto_blake2b_finup(desc, in, inlen, out,
- blake2b_compress_arch);
-}
-
-#define BLAKE2B_ALG(name, driver_name, digest_size) \
- { \
- .base.cra_name = name, \
- .base.cra_driver_name = driver_name, \
- .base.cra_priority = 200, \
- .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY | \
- CRYPTO_AHASH_ALG_BLOCK_ONLY | \
- CRYPTO_AHASH_ALG_FINAL_NONZERO, \
- .base.cra_blocksize = BLAKE2B_BLOCK_SIZE, \
- .base.cra_ctxsize = sizeof(struct blake2b_tfm_ctx), \
- .base.cra_module = THIS_MODULE, \
- .digestsize = digest_size, \
- .setkey = crypto_blake2b_setkey, \
- .init = crypto_blake2b_init, \
- .update = crypto_blake2b_update_neon, \
- .finup = crypto_blake2b_finup_neon, \
- .descsize = sizeof(struct blake2b_state), \
- .statesize = BLAKE2B_STATE_SIZE, \
- }
-
-static struct shash_alg blake2b_neon_algs[] = {
- BLAKE2B_ALG("blake2b-160", "blake2b-160-neon", BLAKE2B_160_HASH_SIZE),
- BLAKE2B_ALG("blake2b-256", "blake2b-256-neon", BLAKE2B_256_HASH_SIZE),
- BLAKE2B_ALG("blake2b-384", "blake2b-384-neon", BLAKE2B_384_HASH_SIZE),
- BLAKE2B_ALG("blake2b-512", "blake2b-512-neon", BLAKE2B_512_HASH_SIZE),
-};
-
-static int __init blake2b_neon_mod_init(void)
-{
- if (!(elf_hwcap & HWCAP_NEON))
- return -ENODEV;
-
- return crypto_register_shashes(blake2b_neon_algs,
- ARRAY_SIZE(blake2b_neon_algs));
-}
-
-static void __exit blake2b_neon_mod_exit(void)
-{
- crypto_unregister_shashes(blake2b_neon_algs,
- ARRAY_SIZE(blake2b_neon_algs));
-}
-
-module_init(blake2b_neon_mod_init);
-module_exit(blake2b_neon_mod_exit);
-
-MODULE_DESCRIPTION("BLAKE2b digest algorithm, NEON accelerated");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Eric Biggers <ebiggers@google.com>");
-MODULE_ALIAS_CRYPTO("blake2b-160");
-MODULE_ALIAS_CRYPTO("blake2b-160-neon");
-MODULE_ALIAS_CRYPTO("blake2b-256");
-MODULE_ALIAS_CRYPTO("blake2b-256-neon");
-MODULE_ALIAS_CRYPTO("blake2b-384");
-MODULE_ALIAS_CRYPTO("blake2b-384-neon");
-MODULE_ALIAS_CRYPTO("blake2b-512");
-MODULE_ALIAS_CRYPTO("blake2b-512-neon");
diff --git a/arch/arm/include/asm/simd.h b/arch/arm/include/asm/simd.h
index be08a8da046f..8549fa8b7253 100644
--- a/arch/arm/include/asm/simd.h
+++ b/arch/arm/include/asm/simd.h
@@ -2,14 +2,21 @@
#ifndef _ASM_SIMD_H
#define _ASM_SIMD_H
+#include <linux/cleanup.h>
#include <linux/compiler_attributes.h>
#include <linux/preempt.h>
#include <linux/types.h>
+#include <asm/neon.h>
+
static __must_check inline bool may_use_simd(void)
{
return IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && !in_hardirq()
&& !irqs_disabled();
}
+DEFINE_LOCK_GUARD_0(ksimd, kernel_neon_begin(), kernel_neon_end())
+
+#define scoped_ksimd() scoped_guard(ksimd)
+
#endif /* _ASM_SIMD_H */
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index f90be312418e..d6ae80b5df36 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -283,10 +283,17 @@ extern int __put_user_8(void *, unsigned long long);
__gu_err; \
})
+/*
+ * This is a type: either unsigned long, if the argument fits into
+ * that type, or otherwise unsigned long long.
+ */
+#define __long_type(x) \
+ __typeof__(__builtin_choose_expr(sizeof(x) > sizeof(0UL), 0ULL, 0UL))
+
#define __get_user_err(x, ptr, err, __t) \
do { \
unsigned long __gu_addr = (unsigned long)(ptr); \
- unsigned long __gu_val; \
+ __long_type(x) __gu_val; \
unsigned int __ua_flags; \
__chk_user_ptr(ptr); \
might_fault(); \
@@ -295,6 +302,7 @@ do { \
case 1: __get_user_asm_byte(__gu_val, __gu_addr, err, __t); break; \
case 2: __get_user_asm_half(__gu_val, __gu_addr, err, __t); break; \
case 4: __get_user_asm_word(__gu_val, __gu_addr, err, __t); break; \
+ case 8: __get_user_asm_dword(__gu_val, __gu_addr, err, __t); break; \
default: (__gu_val) = __get_user_bad(); \
} \
uaccess_restore(__ua_flags); \
@@ -353,6 +361,22 @@ do { \
#define __get_user_asm_word(x, addr, err, __t) \
__get_user_asm(x, addr, err, "ldr" __t)
+#ifdef __ARMEB__
+#define __WORD0_OFFS 4
+#define __WORD1_OFFS 0
+#else
+#define __WORD0_OFFS 0
+#define __WORD1_OFFS 4
+#endif
+
+#define __get_user_asm_dword(x, addr, err, __t) \
+ ({ \
+ unsigned long __w0, __w1; \
+ __get_user_asm(__w0, addr + __WORD0_OFFS, err, "ldr" __t); \
+ __get_user_asm(__w1, addr + __WORD1_OFFS, err, "ldr" __t); \
+ (x) = ((u64)__w1 << 32) | (u64) __w0; \
+})
+
#define __put_user_switch(x, ptr, __err, __fn) \
do { \
const __typeof__(*(ptr)) __user *__pu_ptr = (ptr); \
diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl
index b07e699aaa3c..fd09afae72a2 100644
--- a/arch/arm/tools/syscall.tbl
+++ b/arch/arm/tools/syscall.tbl
@@ -484,3 +484,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 6663ffd23f25..65db12f66b8f 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -47,7 +47,6 @@ config ARM64
select ARCH_HAS_SETUP_DMA_OPS
select ARCH_HAS_SET_DIRECT_MAP
select ARCH_HAS_SET_MEMORY
- select ARCH_HAS_MEM_ENCRYPT
select ARCH_HAS_FORCE_DMA_UNENCRYPTED
select ARCH_STACKWALK
select ARCH_HAS_STRICT_KERNEL_RWX
@@ -2023,6 +2022,31 @@ config ARM64_TLB_RANGE
ARMv8.4-TLBI provides TLBI invalidation instruction that apply to a
range of input addresses.
+config ARM64_MPAM
+ bool "Enable support for MPAM"
+ select ARM64_MPAM_DRIVER if EXPERT # does nothing yet
+ select ACPI_MPAM if ACPI
+ help
+ Memory System Resource Partitioning and Monitoring (MPAM) is an
+ optional extension to the Arm architecture that allows each
+ transaction issued to the memory system to be labelled with a
+ Partition identifier (PARTID) and Performance Monitoring Group
+ identifier (PMG).
+
+ Memory system components, such as the caches, can be configured with
+ policies to control how much of various physical resources (such as
+ memory bandwidth or cache memory) the transactions labelled with each
+ PARTID can consume. Depending on the capabilities of the hardware,
+ the PARTID and PMG can also be used as filtering criteria to measure
+ the memory system resource consumption of different parts of a
+ workload.
+
+ Use of this extension requires CPU support, support in the
+ Memory System Components (MSC), and a description from firmware
+ of where the MSCs are in the address space.
+
+ MPAM is exposed to user-space via the resctrl pseudo filesystem.
+
endmenu # "ARMv8.4 architectural features"
menu "ARMv8.5 architectural features"
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 13173795c43d..3dbff0261f0a 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -316,6 +316,7 @@ config ARCH_QCOM
select GPIOLIB
select PINCTRL
select HAVE_PWRCTRL if PCI
+ select HAVE_SHARED_GPIOS
help
This enables support for the ARMv8 based Qualcomm chipsets.
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
index b8f256545022..3e0319fdb93f 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
@@ -18,11 +18,21 @@
#include "bcm2712-rpi-5-b-ovl-rp1.dts"
+/ {
+ aliases {
+ ethernet0 = &rp1_eth;
+ };
+};
+
&pcie2 {
#include "rp1-nexus.dtsi"
};
&rp1_eth {
+ assigned-clocks = <&rp1_clocks RP1_CLK_ETH_TSU>,
+ <&rp1_clocks RP1_CLK_ETH>;
+ assigned-clock-rates = <50000000>,
+ <125000000>;
status = "okay";
phy-mode = "rgmii-id";
phy-handle = <&phy1>;
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
index e77a66adc22a..205b87f557d6 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
@@ -326,6 +326,8 @@
<0x7fffe000 0x2000>;
interrupt-controller;
#address-cells = <0>;
+ interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) |
+ IRQ_TYPE_LEVEL_HIGH)>;
#interrupt-cells = <3>;
};
diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi
index 2cf0f7208350..a72b2f1c4a1b 100644
--- a/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8-ss-img.dtsi
@@ -67,7 +67,6 @@ img_subsys: bus@58000000 {
power-domains = <&pd IMX_SC_R_CSI_0>;
fsl,channel = <0>;
fsl,num-irqs = <32>;
- status = "disabled";
};
gpio0_mipi_csi0: gpio@58222000 {
@@ -144,7 +143,6 @@ img_subsys: bus@58000000 {
power-domains = <&pd IMX_SC_R_CSI_1>;
fsl,channel = <0>;
fsl,num-irqs = <32>;
- status = "disabled";
};
gpio0_mipi_csi1: gpio@58242000 {
diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-ss-conn.dtsi b/arch/arm64/boot/dts/freescale/imx8dxl-ss-conn.dtsi
index a66ba6d0a8c0..da33a35c6d46 100644
--- a/arch/arm64/boot/dts/freescale/imx8dxl-ss-conn.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8dxl-ss-conn.dtsi
@@ -29,8 +29,8 @@
compatible = "nxp,imx8dxl-dwmac-eqos", "snps,dwmac-5.10a";
reg = <0x5b050000 0x10000>;
interrupt-parent = <&gic>;
- interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>;
+ interrupts = <GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_wake_irq";
clocks = <&eqos_lpcg IMX_LPCG_CLK_4>,
<&eqos_lpcg IMX_LPCG_CLK_6>,
diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-ss-hsio.dtsi b/arch/arm64/boot/dts/freescale/imx8dxl-ss-hsio.dtsi
index ec466e4d7df5..5c0d09c5c086 100644
--- a/arch/arm64/boot/dts/freescale/imx8dxl-ss-hsio.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8dxl-ss-hsio.dtsi
@@ -54,3 +54,8 @@
interrupt-names = "dma";
};
};
+
+&pcieb_ep {
+ interrupts = <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "dma";
+};
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts
index 614b4ce330b1..0924ac50fd2d 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mp-kontron-bl-osm-s.dts
@@ -16,11 +16,20 @@
ethernet1 = &eqos;
};
- extcon_usbc: usbc {
- compatible = "linux,extcon-usb-gpio";
+ connector {
+ compatible = "gpio-usb-b-connector", "usb-b-connector";
+ id-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
+ label = "Type-C";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usb1_id>;
- id-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
+ type = "micro";
+ vbus-supply = <&reg_usb1_vbus>;
+
+ port {
+ usb_dr_connector: endpoint {
+ remote-endpoint = <&usb3_dwc>;
+ };
+ };
};
leds {
@@ -244,9 +253,15 @@
hnp-disable;
srp-disable;
dr_mode = "otg";
- extcon = <&extcon_usbc>;
usb-role-switch;
+ role-switch-default-mode = "peripheral";
status = "okay";
+
+ port {
+ usb3_dwc: endpoint {
+ remote-endpoint = <&usb_dr_connector>;
+ };
+ };
};
&usb_dwc3_1 {
@@ -273,7 +288,6 @@
};
&usb3_phy0 {
- vbus-supply = <&reg_usb1_vbus>;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
index 202d5c67ac40..9c0b6b8d6459 100644
--- a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
+++ b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
@@ -217,8 +217,8 @@
compatible = "nxp,cbdtu02043", "gpio-sbu-mux";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_typec_mux>;
- select-gpios = <&lsio_gpio4 6 GPIO_ACTIVE_LOW>;
- enable-gpios = <&lsio_gpio4 19 GPIO_ACTIVE_HIGH>;
+ select-gpios = <&lsio_gpio4 6 GPIO_ACTIVE_HIGH>;
+ enable-gpios = <&lsio_gpio4 19 GPIO_ACTIVE_LOW>;
orientation-switch;
port {
diff --git a/arch/arm64/boot/dts/freescale/imx95.dtsi b/arch/arm64/boot/dts/freescale/imx95.dtsi
index 1292677cbe4e..75b74077c10f 100644
--- a/arch/arm64/boot/dts/freescale/imx95.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx95.dtsi
@@ -250,6 +250,28 @@
clock-output-names = "dummy";
};
+ gpu_opp_table: opp-table {
+ compatible = "operating-points-v2";
+
+ opp-500000000 {
+ opp-hz = /bits/ 64 <500000000>;
+ opp-hz-real = /bits/ 64 <500000000>;
+ opp-microvolt = <920000>;
+ };
+
+ opp-800000000 {
+ opp-hz = /bits/ 64 <800000000>;
+ opp-hz-real = /bits/ 64 <800000000>;
+ opp-microvolt = <920000>;
+ };
+
+ opp-1000000000 {
+ opp-hz = /bits/ 64 <1000000000>;
+ opp-hz-real = /bits/ 64 <1000000000>;
+ opp-microvolt = <920000>;
+ };
+ };
+
clk_ext1: clock-ext1 {
compatible = "fixed-clock";
#clock-cells = <0>;
@@ -1886,7 +1908,7 @@
assigned-clock-rates = <3600000000>, <100000000>, <10000000>;
assigned-clock-parents = <0>, <0>,
<&scmi_clk IMX95_CLK_SYSPLL1_PFD1_DIV2>;
- msi-map = <0x0 &its 0x98 0x1>;
+ msi-map = <0x0 &its 0x10 0x1>;
power-domains = <&scmi_devpd IMX95_PD_HSIO_TOP>;
status = "disabled";
};
@@ -1963,6 +1985,7 @@
assigned-clock-rates = <3600000000>, <100000000>, <10000000>;
assigned-clock-parents = <0>, <0>,
<&scmi_clk IMX95_CLK_SYSPLL1_PFD1_DIV2>;
+ msi-map = <0x0 &its 0x98 0x1>;
power-domains = <&scmi_devpd IMX95_PD_HSIO_TOP>;
status = "disabled";
};
@@ -2138,6 +2161,21 @@
};
};
+ gpu: gpu@4d900000 {
+ compatible = "nxp,imx95-mali", "arm,mali-valhall-csf";
+ reg = <0 0x4d900000 0 0x480000>;
+ clocks = <&scmi_clk IMX95_CLK_GPU>, <&scmi_clk IMX95_CLK_GPUAPB>;
+ clock-names = "core", "coregroup";
+ interrupts = <GIC_SPI 289 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 290 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 288 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "job", "mmu", "gpu";
+ operating-points-v2 = <&gpu_opp_table>;
+ power-domains = <&scmi_devpd IMX95_PD_GPU>;
+ #cooling-cells = <2>;
+ dynamic-power-coefficient = <1013>;
+ };
+
ddr-pmu@4e090dc0 {
compatible = "fsl,imx95-ddr-pmu", "fsl,imx93-ddr-pmu";
reg = <0x0 0x4e090dc0 0x0 0x200>;
diff --git a/arch/arm64/boot/dts/nvidia/tegra194-p3668.dtsi b/arch/arm64/boot/dts/nvidia/tegra194-p3668.dtsi
index a410fc335fa3..c0f17f8189fa 100644
--- a/arch/arm64/boot/dts/nvidia/tegra194-p3668.dtsi
+++ b/arch/arm64/boot/dts/nvidia/tegra194-p3668.dtsi
@@ -42,6 +42,7 @@
interrupt-parent = <&gpio>;
interrupts = <TEGRA194_MAIN_GPIO(G, 4) IRQ_TYPE_LEVEL_LOW>;
#phy-cells = <0>;
+ wakeup-source;
};
};
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3328.dtsi b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
index 283d9cbc4368..03b7c4313750 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3328.dtsi
@@ -598,7 +598,6 @@
pinctrl-2 = <&otp_pin>;
resets = <&cru SRST_TSADC>;
reset-names = "tsadc-apb";
- rockchip,grf = <&grf>;
rockchip,hw-tshut-temp = <100000>;
#thermal-sensor-cells = <1>;
status = "disabled";
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-op1.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-op1.dtsi
index c4f4f1ff6117..9da6fd82e46b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-op1.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-op1.dtsi
@@ -3,7 +3,7 @@
* Copyright (c) 2016-2017 Fuzhou Rockchip Electronics Co., Ltd
*/
-#include "rk3399.dtsi"
+#include "rk3399-base.dtsi"
/ {
cluster0_opp: opp-table-0 {
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso
index 5e8f729c2cf2..141a921a06e4 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso
+++ b/arch/arm64/boot/dts/rockchip/rk3399-puma-haikou-video-demo.dtso
@@ -45,11 +45,11 @@
cam_dovdd_1v8: regulator-cam-dovdd-1v8 {
compatible = "regulator-fixed";
- gpio = <&pca9670 3 GPIO_ACTIVE_LOW>;
- regulator-max-microvolt = <1800000>;
- regulator-min-microvolt = <1800000>;
- regulator-name = "cam-dovdd-1v8";
- vin-supply = <&vcc1v8_video>;
+ gpio = <&pca9670 3 GPIO_ACTIVE_LOW>;
+ regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <1800000>;
+ regulator-name = "cam-dovdd-1v8";
+ vin-supply = <&vcc1v8_video>;
};
cam_dvdd_1v2: regulator-cam-dvdd-1v2 {
diff --git a/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi
index 7f578c50b4ad..b6cf03a7ba66 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3566-bigtreetech-cb2.dtsi
@@ -120,7 +120,7 @@
compatible = "regulator-fixed";
regulator-name = "vcc3v3_pcie";
enable-active-high;
- gpios = <&gpio0 RK_PB1 GPIO_ACTIVE_HIGH>;
+ gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&pcie_drv>;
regulator-always-on;
@@ -187,7 +187,7 @@
vcc5v0_usb2b: regulator-vcc5v0-usb2b {
compatible = "regulator-fixed";
enable-active-high;
- gpio = <&gpio0 RK_PC4 GPIO_ACTIVE_HIGH>;
+ gpio = <&gpio4 RK_PC4 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_usb2b_en>;
regulator-name = "vcc5v0_usb2b";
@@ -199,7 +199,7 @@
vcc5v0_usb2t: regulator-vcc5v0-usb2t {
compatible = "regulator-fixed";
enable-active-high;
- gpios = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
+ gpios = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&vcc5v0_usb2t_en>;
regulator-name = "vcc5v0_usb2t";
diff --git a/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi b/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi
index d0e38412d56a..08bf40de17ea 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3566-pinetab2.dtsi
@@ -789,7 +789,7 @@
vccio1-supply = <&vccio_acodec>;
vccio2-supply = <&vcc_1v8>;
vccio3-supply = <&vccio_sd>;
- vccio4-supply = <&vcc_1v8>;
+ vccio4-supply = <&vcca1v8_pmu>;
vccio5-supply = <&vcc_1v8>;
vccio6-supply = <&vcc1v8_dvp>;
vccio7-supply = <&vcc_3v3>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-odroid-m1.dts b/arch/arm64/boot/dts/rockchip/rk3568-odroid-m1.dts
index 0f844806ec54..442a2bc43ba8 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-odroid-m1.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-odroid-m1.dts
@@ -482,6 +482,8 @@
};
&i2s1_8ch {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s1m0_sclktx &i2s1m0_lrcktx &i2s1m0_sdi0 &i2s1m0_sdo0>;
rockchip,trcm-sync-tx-only;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3576.dtsi b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
index fc4e9e07f1cf..a86fc6b4e8c4 100644
--- a/arch/arm64/boot/dts/rockchip/rk3576.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3576.dtsi
@@ -276,12 +276,6 @@
opp-microvolt = <900000 900000 950000>;
clock-latency-ns = <40000>;
};
-
- opp-2208000000 {
- opp-hz = /bits/ 64 <2208000000>;
- opp-microvolt = <950000 950000 950000>;
- clock-latency-ns = <40000>;
- };
};
cluster1_opp_table: opp-table-cluster1 {
@@ -348,12 +342,6 @@
opp-microvolt = <925000 925000 950000>;
clock-latency-ns = <40000>;
};
-
- opp-2304000000 {
- opp-hz = /bits/ 64 <2304000000>;
- opp-microvolt = <950000 950000 950000>;
- clock-latency-ns = <40000>;
- };
};
gpu_opp_table: opp-table-gpu {
@@ -2561,8 +2549,6 @@
interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&i2c9m0_xfer>;
- resets = <&cru SRST_I2C9>, <&cru SRST_P_I2C9>;
- reset-names = "i2c", "apb";
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-opp.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-opp.dtsi
index 0f1a77697351..b5d630d2c879 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-opp.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-opp.dtsi
@@ -115,7 +115,7 @@
};
};
- gpu_opp_table: opp-table {
+ gpu_opp_table: opp-table-gpu {
compatible = "operating-points-v2";
opp-300000000 {
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi
index b44e89e1bb15..365c1d958f2d 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-tiger.dtsi
@@ -382,14 +382,12 @@
cap-mmc-highspeed;
mmc-ddr-1_8v;
mmc-hs200-1_8v;
- mmc-hs400-1_8v;
- mmc-hs400-enhanced-strobe;
mmc-pwrseq = <&emmc_pwrseq>;
no-sdio;
no-sd;
non-removable;
pinctrl-names = "default";
- pinctrl-0 = <&emmc_bus8 &emmc_cmd &emmc_clk &emmc_data_strobe>;
+ pinctrl-0 = <&emmc_bus8 &emmc_cmd &emmc_clk>;
vmmc-supply = <&vcc_3v3_s3>;
vqmmc-supply = <&vcc_1v8_s3>;
status = "okay";
diff --git a/arch/arm64/boot/dts/rockchip/rk3588j.dtsi b/arch/arm64/boot/dts/rockchip/rk3588j.dtsi
index 9884a5df47df..e1e0e3fc0ca7 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588j.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588j.dtsi
@@ -66,7 +66,7 @@
};
};
- gpu_opp_table: opp-table {
+ gpu_opp_table: opp-table-gpu {
compatible = "operating-points-v2";
opp-300000000 {
diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts
index ad6d04793b0a..83b9b6645a1e 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3588s-orangepi-5.dts
@@ -14,8 +14,8 @@
gpios = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>;
regulator-name = "vcc3v3_pcie20";
regulator-boot-on;
- regulator-min-microvolt = <1800000>;
- regulator-max-microvolt = <1800000>;
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
startup-delay-us = <50000>;
vin-supply = <&vcc5v0_sys>;
};
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index e3a2d37bd104..94d372e3b9bd 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1341,7 +1341,7 @@ CONFIG_COMMON_CLK_RS9_PCIE=y
CONFIG_COMMON_CLK_VC3=y
CONFIG_COMMON_CLK_VC5=y
CONFIG_COMMON_CLK_BD718XX=m
-CONFIG_CLK_RASPBERRYPI=m
+CONFIG_CLK_RASPBERRYPI=y
CONFIG_CLK_IMX8MM=y
CONFIG_CLK_IMX8MN=y
CONFIG_CLK_IMX8MP=y
@@ -1783,10 +1783,9 @@ CONFIG_CRYPTO_CHACHA20=m
CONFIG_CRYPTO_BENCHMARK=m
CONFIG_CRYPTO_ECHAINIV=y
CONFIG_CRYPTO_MICHAEL_MIC=m
-CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_SHA3=m
CONFIG_CRYPTO_USER_API_RNG=m
CONFIG_CRYPTO_GHASH_ARM64_CE=y
-CONFIG_CRYPTO_SHA3_ARM64=m
CONFIG_CRYPTO_SM3_ARM64_CE=m
CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
CONFIG_CRYPTO_AES_ARM64_BS=m
diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig
index 91f3093eee6a..bdd276a6e540 100644
--- a/arch/arm64/crypto/Kconfig
+++ b/arch/arm64/crypto/Kconfig
@@ -25,17 +25,6 @@ config CRYPTO_NHPOLY1305_NEON
Architecture: arm64 using:
- NEON (Advanced SIMD) extensions
-config CRYPTO_SHA3_ARM64
- tristate "Hash functions: SHA-3 (ARMv8.2 Crypto Extensions)"
- depends on KERNEL_MODE_NEON
- select CRYPTO_HASH
- select CRYPTO_SHA3
- help
- SHA-3 secure hash algorithms (FIPS 202)
-
- Architecture: arm64 using:
- - ARMv8.2 Crypto Extensions
-
config CRYPTO_SM3_NEON
tristate "Hash functions: SM3 (NEON)"
depends on KERNEL_MODE_NEON
@@ -58,16 +47,6 @@ config CRYPTO_SM3_ARM64_CE
Architecture: arm64 using:
- ARMv8.2 Crypto Extensions
-config CRYPTO_POLYVAL_ARM64_CE
- tristate "Hash functions: POLYVAL (ARMv8 Crypto Extensions)"
- depends on KERNEL_MODE_NEON
- select CRYPTO_POLYVAL
- help
- POLYVAL hash function for HCTR2
-
- Architecture: arm64 using:
- - ARMv8 Crypto Extensions
-
config CRYPTO_AES_ARM64
tristate "Ciphers: AES, modes: ECB, CBC, CTR, CTS, XCTR, XTS"
select CRYPTO_AES
diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile
index a8b2cdbe202c..1e330aa08d3f 100644
--- a/arch/arm64/crypto/Makefile
+++ b/arch/arm64/crypto/Makefile
@@ -5,9 +5,6 @@
# Copyright (C) 2014 Linaro Ltd <ard.biesheuvel@linaro.org>
#
-obj-$(CONFIG_CRYPTO_SHA3_ARM64) += sha3-ce.o
-sha3-ce-y := sha3-ce-glue.o sha3-ce-core.o
-
obj-$(CONFIG_CRYPTO_SM3_NEON) += sm3-neon.o
sm3-neon-y := sm3-neon-glue.o sm3-neon-core.o
@@ -32,9 +29,6 @@ sm4-neon-y := sm4-neon-glue.o sm4-neon-core.o
obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o
ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o
-obj-$(CONFIG_CRYPTO_POLYVAL_ARM64_CE) += polyval-ce.o
-polyval-ce-y := polyval-ce-glue.o polyval-ce-core.o
-
obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o
aes-ce-cipher-y := aes-ce-core.o aes-ce-glue.o
diff --git a/arch/arm64/crypto/aes-ce-ccm-glue.c b/arch/arm64/crypto/aes-ce-ccm-glue.c
index 2d791d51891b..c4fd648471f1 100644
--- a/arch/arm64/crypto/aes-ce-ccm-glue.c
+++ b/arch/arm64/crypto/aes-ce-ccm-glue.c
@@ -8,7 +8,6 @@
* Author: Ard Biesheuvel <ardb@kernel.org>
*/
-#include <asm/neon.h>
#include <linux/unaligned.h>
#include <crypto/aes.h>
#include <crypto/scatterwalk.h>
@@ -16,6 +15,8 @@
#include <crypto/internal/skcipher.h>
#include <linux/module.h>
+#include <asm/simd.h>
+
#include "aes-ce-setkey.h"
MODULE_IMPORT_NS("CRYPTO_INTERNAL");
@@ -114,11 +115,8 @@ static u32 ce_aes_ccm_auth_data(u8 mac[], u8 const in[], u32 abytes,
in += adv;
abytes -= adv;
- if (unlikely(rem)) {
- kernel_neon_end();
- kernel_neon_begin();
+ if (unlikely(rem))
macp = 0;
- }
} else {
u32 l = min(AES_BLOCK_SIZE - macp, abytes);
@@ -187,40 +185,38 @@ static int ccm_encrypt(struct aead_request *req)
if (unlikely(err))
return err;
- kernel_neon_begin();
+ scoped_ksimd() {
+ if (req->assoclen)
+ ccm_calculate_auth_mac(req, mac);
- if (req->assoclen)
- ccm_calculate_auth_mac(req, mac);
+ do {
+ u32 tail = walk.nbytes % AES_BLOCK_SIZE;
+ const u8 *src = walk.src.virt.addr;
+ u8 *dst = walk.dst.virt.addr;
+ u8 buf[AES_BLOCK_SIZE];
+ u8 *final_iv = NULL;
- do {
- u32 tail = walk.nbytes % AES_BLOCK_SIZE;
- const u8 *src = walk.src.virt.addr;
- u8 *dst = walk.dst.virt.addr;
- u8 buf[AES_BLOCK_SIZE];
- u8 *final_iv = NULL;
-
- if (walk.nbytes == walk.total) {
- tail = 0;
- final_iv = orig_iv;
- }
-
- if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
- src = dst = memcpy(&buf[sizeof(buf) - walk.nbytes],
- src, walk.nbytes);
+ if (walk.nbytes == walk.total) {
+ tail = 0;
+ final_iv = orig_iv;
+ }
- ce_aes_ccm_encrypt(dst, src, walk.nbytes - tail,
- ctx->key_enc, num_rounds(ctx),
- mac, walk.iv, final_iv);
+ if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
+ src = dst = memcpy(&buf[sizeof(buf) - walk.nbytes],
+ src, walk.nbytes);
- if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
- memcpy(walk.dst.virt.addr, dst, walk.nbytes);
+ ce_aes_ccm_encrypt(dst, src, walk.nbytes - tail,
+ ctx->key_enc, num_rounds(ctx),
+ mac, walk.iv, final_iv);
- if (walk.nbytes) {
- err = skcipher_walk_done(&walk, tail);
- }
- } while (walk.nbytes);
+ if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
+ memcpy(walk.dst.virt.addr, dst, walk.nbytes);
- kernel_neon_end();
+ if (walk.nbytes) {
+ err = skcipher_walk_done(&walk, tail);
+ }
+ } while (walk.nbytes);
+ }
if (unlikely(err))
return err;
@@ -254,40 +250,38 @@ static int ccm_decrypt(struct aead_request *req)
if (unlikely(err))
return err;
- kernel_neon_begin();
-
- if (req->assoclen)
- ccm_calculate_auth_mac(req, mac);
+ scoped_ksimd() {
+ if (req->assoclen)
+ ccm_calculate_auth_mac(req, mac);
- do {
- u32 tail = walk.nbytes % AES_BLOCK_SIZE;
- const u8 *src = walk.src.virt.addr;
- u8 *dst = walk.dst.virt.addr;
- u8 buf[AES_BLOCK_SIZE];
- u8 *final_iv = NULL;
-
- if (walk.nbytes == walk.total) {
- tail = 0;
- final_iv = orig_iv;
- }
+ do {
+ u32 tail = walk.nbytes % AES_BLOCK_SIZE;
+ const u8 *src = walk.src.virt.addr;
+ u8 *dst = walk.dst.virt.addr;
+ u8 buf[AES_BLOCK_SIZE];
+ u8 *final_iv = NULL;
- if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
- src = dst = memcpy(&buf[sizeof(buf) - walk.nbytes],
- src, walk.nbytes);
+ if (walk.nbytes == walk.total) {
+ tail = 0;
+ final_iv = orig_iv;
+ }
- ce_aes_ccm_decrypt(dst, src, walk.nbytes - tail,
- ctx->key_enc, num_rounds(ctx),
- mac, walk.iv, final_iv);
+ if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
+ src = dst = memcpy(&buf[sizeof(buf) - walk.nbytes],
+ src, walk.nbytes);
- if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
- memcpy(walk.dst.virt.addr, dst, walk.nbytes);
+ ce_aes_ccm_decrypt(dst, src, walk.nbytes - tail,
+ ctx->key_enc, num_rounds(ctx),
+ mac, walk.iv, final_iv);
- if (walk.nbytes) {
- err = skcipher_walk_done(&walk, tail);
- }
- } while (walk.nbytes);
+ if (unlikely(walk.nbytes < AES_BLOCK_SIZE))
+ memcpy(walk.dst.virt.addr, dst, walk.nbytes);
- kernel_neon_end();
+ if (walk.nbytes) {
+ err = skcipher_walk_done(&walk, tail);
+ }
+ } while (walk.nbytes);
+ }
if (unlikely(err))
return err;
diff --git a/arch/arm64/crypto/aes-ce-glue.c b/arch/arm64/crypto/aes-ce-glue.c
index 00b8749013c5..a4dad370991d 100644
--- a/arch/arm64/crypto/aes-ce-glue.c
+++ b/arch/arm64/crypto/aes-ce-glue.c
@@ -52,9 +52,8 @@ static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
return;
}
- kernel_neon_begin();
- __aes_ce_encrypt(ctx->key_enc, dst, src, num_rounds(ctx));
- kernel_neon_end();
+ scoped_ksimd()
+ __aes_ce_encrypt(ctx->key_enc, dst, src, num_rounds(ctx));
}
static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
@@ -66,9 +65,8 @@ static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[])
return;
}
- kernel_neon_begin();
- __aes_ce_decrypt(ctx->key_dec, dst, src, num_rounds(ctx));
- kernel_neon_end();
+ scoped_ksimd()
+ __aes_ce_decrypt(ctx->key_dec, dst, src, num_rounds(ctx));
}
int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
@@ -94,47 +92,48 @@ int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key,
for (i = 0; i < kwords; i++)
ctx->key_enc[i] = get_unaligned_le32(in_key + i * sizeof(u32));
- kernel_neon_begin();
- for (i = 0; i < sizeof(rcon); i++) {
- u32 *rki = ctx->key_enc + (i * kwords);
- u32 *rko = rki + kwords;
-
- rko[0] = ror32(__aes_ce_sub(rki[kwords - 1]), 8) ^ rcon[i] ^ rki[0];
- rko[1] = rko[0] ^ rki[1];
- rko[2] = rko[1] ^ rki[2];
- rko[3] = rko[2] ^ rki[3];
-
- if (key_len == AES_KEYSIZE_192) {
- if (i >= 7)
- break;
- rko[4] = rko[3] ^ rki[4];
- rko[5] = rko[4] ^ rki[5];
- } else if (key_len == AES_KEYSIZE_256) {
- if (i >= 6)
- break;
- rko[4] = __aes_ce_sub(rko[3]) ^ rki[4];
- rko[5] = rko[4] ^ rki[5];
- rko[6] = rko[5] ^ rki[6];
- rko[7] = rko[6] ^ rki[7];
+ scoped_ksimd() {
+ for (i = 0; i < sizeof(rcon); i++) {
+ u32 *rki = ctx->key_enc + (i * kwords);
+ u32 *rko = rki + kwords;
+
+ rko[0] = ror32(__aes_ce_sub(rki[kwords - 1]), 8) ^
+ rcon[i] ^ rki[0];
+ rko[1] = rko[0] ^ rki[1];
+ rko[2] = rko[1] ^ rki[2];
+ rko[3] = rko[2] ^ rki[3];
+
+ if (key_len == AES_KEYSIZE_192) {
+ if (i >= 7)
+ break;
+ rko[4] = rko[3] ^ rki[4];
+ rko[5] = rko[4] ^ rki[5];
+ } else if (key_len == AES_KEYSIZE_256) {
+ if (i >= 6)
+ break;
+ rko[4] = __aes_ce_sub(rko[3]) ^ rki[4];
+ rko[5] = rko[4] ^ rki[5];
+ rko[6] = rko[5] ^ rki[6];
+ rko[7] = rko[6] ^ rki[7];
+ }
}
- }
- /*
- * Generate the decryption keys for the Equivalent Inverse Cipher.
- * This involves reversing the order of the round keys, and applying
- * the Inverse Mix Columns transformation on all but the first and
- * the last one.
- */
- key_enc = (struct aes_block *)ctx->key_enc;
- key_dec = (struct aes_block *)ctx->key_dec;
- j = num_rounds(ctx);
-
- key_dec[0] = key_enc[j];
- for (i = 1, j--; j > 0; i++, j--)
- __aes_ce_invert(key_dec + i, key_enc + j);
- key_dec[i] = key_enc[0];
+ /*
+ * Generate the decryption keys for the Equivalent Inverse
+ * Cipher. This involves reversing the order of the round
+ * keys, and applying the Inverse Mix Columns transformation on
+ * all but the first and the last one.
+ */
+ key_enc = (struct aes_block *)ctx->key_enc;
+ key_dec = (struct aes_block *)ctx->key_dec;
+ j = num_rounds(ctx);
+
+ key_dec[0] = key_enc[j];
+ for (i = 1, j--; j > 0; i++, j--)
+ __aes_ce_invert(key_dec + i, key_enc + j);
+ key_dec[i] = key_enc[0];
+ }
- kernel_neon_end();
return 0;
}
EXPORT_SYMBOL(ce_aes_expandkey);
diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c
index 5e207ff34482..b087b900d279 100644
--- a/arch/arm64/crypto/aes-glue.c
+++ b/arch/arm64/crypto/aes-glue.c
@@ -5,8 +5,6 @@
* Copyright (C) 2013 - 2017 Linaro Ltd <ard.biesheuvel@linaro.org>
*/
-#include <asm/hwcap.h>
-#include <asm/neon.h>
#include <crypto/aes.h>
#include <crypto/ctr.h>
#include <crypto/internal/hash.h>
@@ -20,6 +18,9 @@
#include <linux/module.h>
#include <linux/string.h>
+#include <asm/hwcap.h>
+#include <asm/simd.h>
+
#include "aes-ce-setkey.h"
#ifdef USE_V8_CRYPTO_EXTENSIONS
@@ -186,10 +187,9 @@ static int __maybe_unused ecb_encrypt(struct skcipher_request *req)
err = skcipher_walk_virt(&walk, req, false);
while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) {
- kernel_neon_begin();
- aes_ecb_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key_enc, rounds, blocks);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_ecb_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key_enc, rounds, blocks);
err = skcipher_walk_done(&walk, walk.nbytes % AES_BLOCK_SIZE);
}
return err;
@@ -206,10 +206,9 @@ static int __maybe_unused ecb_decrypt(struct skcipher_request *req)
err = skcipher_walk_virt(&walk, req, false);
while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) {
- kernel_neon_begin();
- aes_ecb_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key_dec, rounds, blocks);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_ecb_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key_dec, rounds, blocks);
err = skcipher_walk_done(&walk, walk.nbytes % AES_BLOCK_SIZE);
}
return err;
@@ -224,10 +223,9 @@ static int cbc_encrypt_walk(struct skcipher_request *req,
unsigned int blocks;
while ((blocks = (walk->nbytes / AES_BLOCK_SIZE))) {
- kernel_neon_begin();
- aes_cbc_encrypt(walk->dst.virt.addr, walk->src.virt.addr,
- ctx->key_enc, rounds, blocks, walk->iv);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_cbc_encrypt(walk->dst.virt.addr, walk->src.virt.addr,
+ ctx->key_enc, rounds, blocks, walk->iv);
err = skcipher_walk_done(walk, walk->nbytes % AES_BLOCK_SIZE);
}
return err;
@@ -253,10 +251,9 @@ static int cbc_decrypt_walk(struct skcipher_request *req,
unsigned int blocks;
while ((blocks = (walk->nbytes / AES_BLOCK_SIZE))) {
- kernel_neon_begin();
- aes_cbc_decrypt(walk->dst.virt.addr, walk->src.virt.addr,
- ctx->key_dec, rounds, blocks, walk->iv);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_cbc_decrypt(walk->dst.virt.addr, walk->src.virt.addr,
+ ctx->key_dec, rounds, blocks, walk->iv);
err = skcipher_walk_done(walk, walk->nbytes % AES_BLOCK_SIZE);
}
return err;
@@ -322,10 +319,9 @@ static int cts_cbc_encrypt(struct skcipher_request *req)
if (err)
return err;
- kernel_neon_begin();
- aes_cbc_cts_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key_enc, rounds, walk.nbytes, walk.iv);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_cbc_cts_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key_enc, rounds, walk.nbytes, walk.iv);
return skcipher_walk_done(&walk, 0);
}
@@ -379,10 +375,9 @@ static int cts_cbc_decrypt(struct skcipher_request *req)
if (err)
return err;
- kernel_neon_begin();
- aes_cbc_cts_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key_dec, rounds, walk.nbytes, walk.iv);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_cbc_cts_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key_dec, rounds, walk.nbytes, walk.iv);
return skcipher_walk_done(&walk, 0);
}
@@ -399,11 +394,11 @@ static int __maybe_unused essiv_cbc_encrypt(struct skcipher_request *req)
blocks = walk.nbytes / AES_BLOCK_SIZE;
if (blocks) {
- kernel_neon_begin();
- aes_essiv_cbc_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key1.key_enc, rounds, blocks,
- req->iv, ctx->key2.key_enc);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_essiv_cbc_encrypt(walk.dst.virt.addr,
+ walk.src.virt.addr,
+ ctx->key1.key_enc, rounds, blocks,
+ req->iv, ctx->key2.key_enc);
err = skcipher_walk_done(&walk, walk.nbytes % AES_BLOCK_SIZE);
}
return err ?: cbc_encrypt_walk(req, &walk);
@@ -421,11 +416,11 @@ static int __maybe_unused essiv_cbc_decrypt(struct skcipher_request *req)
blocks = walk.nbytes / AES_BLOCK_SIZE;
if (blocks) {
- kernel_neon_begin();
- aes_essiv_cbc_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key1.key_dec, rounds, blocks,
- req->iv, ctx->key2.key_enc);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_essiv_cbc_decrypt(walk.dst.virt.addr,
+ walk.src.virt.addr,
+ ctx->key1.key_dec, rounds, blocks,
+ req->iv, ctx->key2.key_enc);
err = skcipher_walk_done(&walk, walk.nbytes % AES_BLOCK_SIZE);
}
return err ?: cbc_decrypt_walk(req, &walk);
@@ -461,10 +456,9 @@ static int __maybe_unused xctr_encrypt(struct skcipher_request *req)
else if (nbytes < walk.total)
nbytes &= ~(AES_BLOCK_SIZE - 1);
- kernel_neon_begin();
- aes_xctr_encrypt(dst, src, ctx->key_enc, rounds, nbytes,
- walk.iv, byte_ctr);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_xctr_encrypt(dst, src, ctx->key_enc, rounds, nbytes,
+ walk.iv, byte_ctr);
if (unlikely(nbytes < AES_BLOCK_SIZE))
memcpy(walk.dst.virt.addr,
@@ -506,10 +500,9 @@ static int __maybe_unused ctr_encrypt(struct skcipher_request *req)
else if (nbytes < walk.total)
nbytes &= ~(AES_BLOCK_SIZE - 1);
- kernel_neon_begin();
- aes_ctr_encrypt(dst, src, ctx->key_enc, rounds, nbytes,
- walk.iv);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_ctr_encrypt(dst, src, ctx->key_enc, rounds, nbytes,
+ walk.iv);
if (unlikely(nbytes < AES_BLOCK_SIZE))
memcpy(walk.dst.virt.addr,
@@ -562,11 +555,10 @@ static int __maybe_unused xts_encrypt(struct skcipher_request *req)
if (walk.nbytes < walk.total)
nbytes &= ~(AES_BLOCK_SIZE - 1);
- kernel_neon_begin();
- aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key1.key_enc, rounds, nbytes,
- ctx->key2.key_enc, walk.iv, first);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key1.key_enc, rounds, nbytes,
+ ctx->key2.key_enc, walk.iv, first);
err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
}
@@ -584,11 +576,10 @@ static int __maybe_unused xts_encrypt(struct skcipher_request *req)
if (err)
return err;
- kernel_neon_begin();
- aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key1.key_enc, rounds, walk.nbytes,
- ctx->key2.key_enc, walk.iv, first);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key1.key_enc, rounds, walk.nbytes,
+ ctx->key2.key_enc, walk.iv, first);
return skcipher_walk_done(&walk, 0);
}
@@ -634,11 +625,10 @@ static int __maybe_unused xts_decrypt(struct skcipher_request *req)
if (walk.nbytes < walk.total)
nbytes &= ~(AES_BLOCK_SIZE - 1);
- kernel_neon_begin();
- aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key1.key_dec, rounds, nbytes,
- ctx->key2.key_enc, walk.iv, first);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key1.key_dec, rounds, nbytes,
+ ctx->key2.key_enc, walk.iv, first);
err = skcipher_walk_done(&walk, walk.nbytes - nbytes);
}
@@ -657,11 +647,10 @@ static int __maybe_unused xts_decrypt(struct skcipher_request *req)
return err;
- kernel_neon_begin();
- aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key1.key_dec, rounds, walk.nbytes,
- ctx->key2.key_enc, walk.iv, first);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key1.key_dec, rounds, walk.nbytes,
+ ctx->key2.key_enc, walk.iv, first);
return skcipher_walk_done(&walk, 0);
}
@@ -808,10 +797,9 @@ static int cmac_setkey(struct crypto_shash *tfm, const u8 *in_key,
return err;
/* encrypt the zero vector */
- kernel_neon_begin();
- aes_ecb_encrypt(ctx->consts, (u8[AES_BLOCK_SIZE]){}, ctx->key.key_enc,
- rounds, 1);
- kernel_neon_end();
+ scoped_ksimd()
+ aes_ecb_encrypt(ctx->consts, (u8[AES_BLOCK_SIZE]){},
+ ctx->key.key_enc, rounds, 1);
cmac_gf128_mul_by_x(consts, consts);
cmac_gf128_mul_by_x(consts + 1, consts);
@@ -837,10 +825,10 @@ static int xcbc_setkey(struct crypto_shash *tfm, const u8 *in_key,
if (err)
return err;
- kernel_neon_begin();
- aes_ecb_encrypt(key, ks[0], ctx->key.key_enc, rounds, 1);
- aes_ecb_encrypt(ctx->consts, ks[1], ctx->key.key_enc, rounds, 2);
- kernel_neon_end();
+ scoped_ksimd() {
+ aes_ecb_encrypt(key, ks[0], ctx->key.key_enc, rounds, 1);
+ aes_ecb_encrypt(ctx->consts, ks[1], ctx->key.key_enc, rounds, 2);
+ }
return cbcmac_setkey(tfm, key, sizeof(key));
}
@@ -860,10 +848,9 @@ static void mac_do_update(struct crypto_aes_ctx *ctx, u8 const in[], int blocks,
int rem;
do {
- kernel_neon_begin();
- rem = aes_mac_update(in, ctx->key_enc, rounds, blocks,
- dg, enc_before, !enc_before);
- kernel_neon_end();
+ scoped_ksimd()
+ rem = aes_mac_update(in, ctx->key_enc, rounds, blocks,
+ dg, enc_before, !enc_before);
in += (blocks - rem) * AES_BLOCK_SIZE;
blocks = rem;
} while (blocks);
diff --git a/arch/arm64/crypto/aes-neonbs-glue.c b/arch/arm64/crypto/aes-neonbs-glue.c
index c4a623e86593..d496effb0a5b 100644
--- a/arch/arm64/crypto/aes-neonbs-glue.c
+++ b/arch/arm64/crypto/aes-neonbs-glue.c
@@ -85,9 +85,8 @@ static int aesbs_setkey(struct crypto_skcipher *tfm, const u8 *in_key,
ctx->rounds = 6 + key_len / 4;
- kernel_neon_begin();
- aesbs_convert_key(ctx->rk, rk.key_enc, ctx->rounds);
- kernel_neon_end();
+ scoped_ksimd()
+ aesbs_convert_key(ctx->rk, rk.key_enc, ctx->rounds);
return 0;
}
@@ -110,10 +109,9 @@ static int __ecb_crypt(struct skcipher_request *req,
blocks = round_down(blocks,
walk.stride / AES_BLOCK_SIZE);
- kernel_neon_begin();
- fn(walk.dst.virt.addr, walk.src.virt.addr, ctx->rk,
- ctx->rounds, blocks);
- kernel_neon_end();
+ scoped_ksimd()
+ fn(walk.dst.virt.addr, walk.src.virt.addr, ctx->rk,
+ ctx->rounds, blocks);
err = skcipher_walk_done(&walk,
walk.nbytes - blocks * AES_BLOCK_SIZE);
}
@@ -146,9 +144,8 @@ static int aesbs_cbc_ctr_setkey(struct crypto_skcipher *tfm, const u8 *in_key,
memcpy(ctx->enc, rk.key_enc, sizeof(ctx->enc));
- kernel_neon_begin();
- aesbs_convert_key(ctx->key.rk, rk.key_enc, ctx->key.rounds);
- kernel_neon_end();
+ scoped_ksimd()
+ aesbs_convert_key(ctx->key.rk, rk.key_enc, ctx->key.rounds);
memzero_explicit(&rk, sizeof(rk));
return 0;
@@ -167,11 +164,11 @@ static int cbc_encrypt(struct skcipher_request *req)
unsigned int blocks = walk.nbytes / AES_BLOCK_SIZE;
/* fall back to the non-bitsliced NEON implementation */
- kernel_neon_begin();
- neon_aes_cbc_encrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->enc, ctx->key.rounds, blocks,
- walk.iv);
- kernel_neon_end();
+ scoped_ksimd()
+ neon_aes_cbc_encrypt(walk.dst.virt.addr,
+ walk.src.virt.addr,
+ ctx->enc, ctx->key.rounds, blocks,
+ walk.iv);
err = skcipher_walk_done(&walk, walk.nbytes % AES_BLOCK_SIZE);
}
return err;
@@ -193,11 +190,10 @@ static int cbc_decrypt(struct skcipher_request *req)
blocks = round_down(blocks,
walk.stride / AES_BLOCK_SIZE);
- kernel_neon_begin();
- aesbs_cbc_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
- ctx->key.rk, ctx->key.rounds, blocks,
- walk.iv);
- kernel_neon_end();
+ scoped_ksimd()
+ aesbs_cbc_decrypt(walk.dst.virt.addr, walk.src.virt.addr,
+ ctx->key.rk, ctx->key.rounds, blocks,
+ walk.iv);
err = skcipher_walk_done(&walk,
walk.nbytes - blocks * AES_BLOCK_SIZE);
}
@@ -220,30 +216,32 @@ static int ctr_encrypt(struct skcipher_request *req)
const u8 *src = walk.src.virt.addr;
u8 *dst = walk.dst.virt.addr;
- kernel_neon_begin();
- if (blocks >= 8) {
- aesbs_ctr_encrypt(dst, src, ctx->key.rk, ctx->key.rounds,
- blocks, walk.iv);
- dst += blocks * AES_BLOCK_SIZE;
- src += blocks * AES_BLOCK_SIZE;
- }
- if (nbytes && walk.nbytes == walk.total) {
- u8 buf[AES_BLOCK_SIZE];
- u8 *d = dst;
-
- if (unlikely(nbytes < AES_BLOCK_SIZE))
- src = dst = memcpy(buf + sizeof(buf) - nbytes,
- src, nbytes);
-
- neon_aes_ctr_encrypt(dst, src, ctx->enc, ctx->key.rounds,
- nbytes, walk.iv);
+ scoped_ksimd() {
+ if (blocks >= 8) {
+ aesbs_ctr_encrypt(dst, src, ctx->key.rk,
+ ctx->key.rounds, blocks,
+ walk.iv);
+ dst += blocks * AES_BLOCK_SIZE;
+ src += blocks * AES_BLOCK_SIZE;
+ }
+ if (nbytes && walk.nbytes == walk.total) {
+ u8 buf[AES_BLOCK_SIZE];
+ u8 *d = dst;
+
+ if (unlikely(nbytes < AES_BLOCK_SIZE))
+ src = dst = memcpy(buf + sizeof(buf) -
+ nbytes, src, nbytes);
+
+ neon_aes_ctr_encrypt(dst, src, ctx->enc,
+ ctx->key.rounds, nbytes,
+ walk.iv);
- if (unlikely(nbytes < AES_BLOCK_SIZE))
- memcpy(d, dst, nbytes);
+ if (unlikely(nbytes < AES_BLOCK_SIZE))
+ memcpy(d, dst, nbytes);
- nbytes = 0;
+ nbytes = 0;
+ }
}
- kernel_neon_end();
err = skcipher_walk_done(&walk, nbytes);
}
return err;
@@ -320,33 +318,33 @@ static int __xts_crypt(struct skcipher_request *req, bool encrypt,
in = walk.src.virt.addr;
nbytes = walk.nbytes;
- kernel_neon_begin();
- if (blocks >= 8) {
- if (first == 1)
- neon_aes_ecb_encrypt(walk.iv, walk.iv,
- ctx->twkey,
- ctx->key.rounds, 1);
- first = 2;
-
- fn(out, in, ctx->key.rk, ctx->key.rounds, blocks,
- walk.iv);
-
- out += blocks * AES_BLOCK_SIZE;
- in += blocks * AES_BLOCK_SIZE;
- nbytes -= blocks * AES_BLOCK_SIZE;
+ scoped_ksimd() {
+ if (blocks >= 8) {
+ if (first == 1)
+ neon_aes_ecb_encrypt(walk.iv, walk.iv,
+ ctx->twkey,
+ ctx->key.rounds, 1);
+ first = 2;
+
+ fn(out, in, ctx->key.rk, ctx->key.rounds, blocks,
+ walk.iv);
+
+ out += blocks * AES_BLOCK_SIZE;
+ in += blocks * AES_BLOCK_SIZE;
+ nbytes -= blocks * AES_BLOCK_SIZE;
+ }
+ if (walk.nbytes == walk.total && nbytes > 0) {
+ if (encrypt)
+ neon_aes_xts_encrypt(out, in, ctx->cts.key_enc,
+ ctx->key.rounds, nbytes,
+ ctx->twkey, walk.iv, first);
+ else
+ neon_aes_xts_decrypt(out, in, ctx->cts.key_dec,
+ ctx->key.rounds, nbytes,
+ ctx->twkey, walk.iv, first);
+ nbytes = first = 0;
+ }
}
- if (walk.nbytes == walk.total && nbytes > 0) {
- if (encrypt)
- neon_aes_xts_encrypt(out, in, ctx->cts.key_enc,
- ctx->key.rounds, nbytes,
- ctx->twkey, walk.iv, first);
- else
- neon_aes_xts_decrypt(out, in, ctx->cts.key_dec,
- ctx->key.rounds, nbytes,
- ctx->twkey, walk.iv, first);
- nbytes = first = 0;
- }
- kernel_neon_end();
err = skcipher_walk_done(&walk, nbytes);
}
@@ -369,14 +367,16 @@ static int __xts_crypt(struct skcipher_request *req, bool encrypt,
in = walk.src.virt.addr;
nbytes = walk.nbytes;
- kernel_neon_begin();
- if (encrypt)
- neon_aes_xts_encrypt(out, in, ctx->cts.key_enc, ctx->key.rounds,
- nbytes, ctx->twkey, walk.iv, first);
- else
- neon_aes_xts_decrypt(out, in, ctx->cts.key_dec, ctx->key.rounds,
- nbytes, ctx->twkey, walk.iv, first);
- kernel_neon_end();
+ scoped_ksimd() {
+ if (encrypt)
+ neon_aes_xts_encrypt(out, in, ctx->cts.key_enc,
+ ctx->key.rounds, nbytes, ctx->twkey,
+ walk.iv, first);
+ else
+ neon_aes_xts_decrypt(out, in, ctx->cts.key_dec,
+ ctx->key.rounds, nbytes, ctx->twkey,
+ walk.iv, first);
+ }
return skcipher_walk_done(&walk, 0);
}
diff --git a/arch/arm64/crypto/ghash-ce-glue.c b/arch/arm64/crypto/ghash-ce-glue.c
index 4995b6e22335..7951557a285a 100644
--- a/arch/arm64/crypto/ghash-ce-glue.c
+++ b/arch/arm64/crypto/ghash-ce-glue.c
@@ -5,7 +5,6 @@
* Copyright (C) 2014 - 2018 Linaro Ltd. <ard.biesheuvel@linaro.org>
*/
-#include <asm/neon.h>
#include <crypto/aes.h>
#include <crypto/b128ops.h>
#include <crypto/gcm.h>
@@ -22,6 +21,8 @@
#include <linux/string.h>
#include <linux/unaligned.h>
+#include <asm/simd.h>
+
MODULE_DESCRIPTION("GHASH and AES-GCM using ARMv8 Crypto Extensions");
MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
MODULE_LICENSE("GPL v2");
@@ -74,9 +75,8 @@ void ghash_do_simd_update(int blocks, u64 dg[], const char *src,
u64 const h[][2],
const char *head))
{
- kernel_neon_begin();
- simd_update(blocks, dg, src, key->h, head);
- kernel_neon_end();
+ scoped_ksimd()
+ simd_update(blocks, dg, src, key->h, head);
}
/* avoid hogging the CPU for too long */
@@ -329,11 +329,10 @@ static int gcm_encrypt(struct aead_request *req, char *iv, int assoclen)
tag = NULL;
}
- kernel_neon_begin();
- pmull_gcm_encrypt(nbytes, dst, src, ctx->ghash_key.h,
- dg, iv, ctx->aes_key.key_enc, nrounds,
- tag);
- kernel_neon_end();
+ scoped_ksimd()
+ pmull_gcm_encrypt(nbytes, dst, src, ctx->ghash_key.h,
+ dg, iv, ctx->aes_key.key_enc, nrounds,
+ tag);
if (unlikely(!nbytes))
break;
@@ -399,11 +398,11 @@ static int gcm_decrypt(struct aead_request *req, char *iv, int assoclen)
tag = NULL;
}
- kernel_neon_begin();
- ret = pmull_gcm_decrypt(nbytes, dst, src, ctx->ghash_key.h,
- dg, iv, ctx->aes_key.key_enc,
- nrounds, tag, otag, authsize);
- kernel_neon_end();
+ scoped_ksimd()
+ ret = pmull_gcm_decrypt(nbytes, dst, src,
+ ctx->ghash_key.h,
+ dg, iv, ctx->aes_key.key_enc,
+ nrounds, tag, otag, authsize);
if (unlikely(!nbytes))
break;
diff --git a/arch/arm64/crypto/nhpoly1305-neon-glue.c b/arch/arm64/crypto/nhpoly1305-neon-glue.c
index e4a0b463f080..013de6ac569a 100644
--- a/arch/arm64/crypto/nhpoly1305-neon-glue.c
+++ b/arch/arm64/crypto/nhpoly1305-neon-glue.c
@@ -25,9 +25,8 @@ static int nhpoly1305_neon_update(struct shash_desc *desc,
do {
unsigned int n = min_t(unsigned int, srclen, SZ_4K);
- kernel_neon_begin();
- crypto_nhpoly1305_update_helper(desc, src, n, nh_neon);
- kernel_neon_end();
+ scoped_ksimd()
+ crypto_nhpoly1305_update_helper(desc, src, n, nh_neon);
src += n;
srclen -= n;
} while (srclen);
diff --git a/arch/arm64/crypto/polyval-ce-glue.c b/arch/arm64/crypto/polyval-ce-glue.c
deleted file mode 100644
index c4e653688ea0..000000000000
--- a/arch/arm64/crypto/polyval-ce-glue.c
+++ /dev/null
@@ -1,158 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Glue code for POLYVAL using ARMv8 Crypto Extensions
- *
- * Copyright (c) 2007 Nokia Siemens Networks - Mikko Herranen <mh1@iki.fi>
- * Copyright (c) 2009 Intel Corp.
- * Author: Huang Ying <ying.huang@intel.com>
- * Copyright 2021 Google LLC
- */
-
-/*
- * Glue code based on ghash-clmulni-intel_glue.c.
- *
- * This implementation of POLYVAL uses montgomery multiplication accelerated by
- * ARMv8 Crypto Extensions instructions to implement the finite field operations.
- */
-
-#include <asm/neon.h>
-#include <crypto/internal/hash.h>
-#include <crypto/polyval.h>
-#include <crypto/utils.h>
-#include <linux/cpufeature.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-
-#define NUM_KEY_POWERS 8
-
-struct polyval_tfm_ctx {
- /*
- * These powers must be in the order h^8, ..., h^1.
- */
- u8 key_powers[NUM_KEY_POWERS][POLYVAL_BLOCK_SIZE];
-};
-
-struct polyval_desc_ctx {
- u8 buffer[POLYVAL_BLOCK_SIZE];
-};
-
-asmlinkage void pmull_polyval_update(const struct polyval_tfm_ctx *keys,
- const u8 *in, size_t nblocks, u8 *accumulator);
-asmlinkage void pmull_polyval_mul(u8 *op1, const u8 *op2);
-
-static void internal_polyval_update(const struct polyval_tfm_ctx *keys,
- const u8 *in, size_t nblocks, u8 *accumulator)
-{
- kernel_neon_begin();
- pmull_polyval_update(keys, in, nblocks, accumulator);
- kernel_neon_end();
-}
-
-static void internal_polyval_mul(u8 *op1, const u8 *op2)
-{
- kernel_neon_begin();
- pmull_polyval_mul(op1, op2);
- kernel_neon_end();
-}
-
-static int polyval_arm64_setkey(struct crypto_shash *tfm,
- const u8 *key, unsigned int keylen)
-{
- struct polyval_tfm_ctx *tctx = crypto_shash_ctx(tfm);
- int i;
-
- if (keylen != POLYVAL_BLOCK_SIZE)
- return -EINVAL;
-
- memcpy(tctx->key_powers[NUM_KEY_POWERS-1], key, POLYVAL_BLOCK_SIZE);
-
- for (i = NUM_KEY_POWERS-2; i >= 0; i--) {
- memcpy(tctx->key_powers[i], key, POLYVAL_BLOCK_SIZE);
- internal_polyval_mul(tctx->key_powers[i],
- tctx->key_powers[i+1]);
- }
-
- return 0;
-}
-
-static int polyval_arm64_init(struct shash_desc *desc)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
-
- memset(dctx, 0, sizeof(*dctx));
-
- return 0;
-}
-
-static int polyval_arm64_update(struct shash_desc *desc,
- const u8 *src, unsigned int srclen)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
- const struct polyval_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
- unsigned int nblocks;
-
- do {
- /* allow rescheduling every 4K bytes */
- nblocks = min(srclen, 4096U) / POLYVAL_BLOCK_SIZE;
- internal_polyval_update(tctx, src, nblocks, dctx->buffer);
- srclen -= nblocks * POLYVAL_BLOCK_SIZE;
- src += nblocks * POLYVAL_BLOCK_SIZE;
- } while (srclen >= POLYVAL_BLOCK_SIZE);
-
- return srclen;
-}
-
-static int polyval_arm64_finup(struct shash_desc *desc, const u8 *src,
- unsigned int len, u8 *dst)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
- const struct polyval_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
-
- if (len) {
- crypto_xor(dctx->buffer, src, len);
- internal_polyval_mul(dctx->buffer,
- tctx->key_powers[NUM_KEY_POWERS-1]);
- }
-
- memcpy(dst, dctx->buffer, POLYVAL_BLOCK_SIZE);
-
- return 0;
-}
-
-static struct shash_alg polyval_alg = {
- .digestsize = POLYVAL_DIGEST_SIZE,
- .init = polyval_arm64_init,
- .update = polyval_arm64_update,
- .finup = polyval_arm64_finup,
- .setkey = polyval_arm64_setkey,
- .descsize = sizeof(struct polyval_desc_ctx),
- .base = {
- .cra_name = "polyval",
- .cra_driver_name = "polyval-ce",
- .cra_priority = 200,
- .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .cra_blocksize = POLYVAL_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct polyval_tfm_ctx),
- .cra_module = THIS_MODULE,
- },
-};
-
-static int __init polyval_ce_mod_init(void)
-{
- return crypto_register_shash(&polyval_alg);
-}
-
-static void __exit polyval_ce_mod_exit(void)
-{
- crypto_unregister_shash(&polyval_alg);
-}
-
-module_cpu_feature_match(PMULL, polyval_ce_mod_init)
-module_exit(polyval_ce_mod_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("POLYVAL hash function accelerated by ARMv8 Crypto Extensions");
-MODULE_ALIAS_CRYPTO("polyval");
-MODULE_ALIAS_CRYPTO("polyval-ce");
diff --git a/arch/arm64/crypto/sha3-ce-glue.c b/arch/arm64/crypto/sha3-ce-glue.c
deleted file mode 100644
index b4f1001046c9..000000000000
--- a/arch/arm64/crypto/sha3-ce-glue.c
+++ /dev/null
@@ -1,151 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * sha3-ce-glue.c - core SHA-3 transform using v8.2 Crypto Extensions
- *
- * Copyright (C) 2018 Linaro Ltd <ard.biesheuvel@linaro.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <asm/hwcap.h>
-#include <asm/neon.h>
-#include <asm/simd.h>
-#include <crypto/internal/hash.h>
-#include <crypto/sha3.h>
-#include <linux/cpufeature.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/unaligned.h>
-
-MODULE_DESCRIPTION("SHA3 secure hash using ARMv8 Crypto Extensions");
-MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS_CRYPTO("sha3-224");
-MODULE_ALIAS_CRYPTO("sha3-256");
-MODULE_ALIAS_CRYPTO("sha3-384");
-MODULE_ALIAS_CRYPTO("sha3-512");
-
-asmlinkage int sha3_ce_transform(u64 *st, const u8 *data, int blocks,
- int md_len);
-
-static int sha3_update(struct shash_desc *desc, const u8 *data,
- unsigned int len)
-{
- struct sha3_state *sctx = shash_desc_ctx(desc);
- struct crypto_shash *tfm = desc->tfm;
- unsigned int bs, ds;
- int blocks;
-
- ds = crypto_shash_digestsize(tfm);
- bs = crypto_shash_blocksize(tfm);
- blocks = len / bs;
- len -= blocks * bs;
- do {
- int rem;
-
- kernel_neon_begin();
- rem = sha3_ce_transform(sctx->st, data, blocks, ds);
- kernel_neon_end();
- data += (blocks - rem) * bs;
- blocks = rem;
- } while (blocks);
- return len;
-}
-
-static int sha3_finup(struct shash_desc *desc, const u8 *src, unsigned int len,
- u8 *out)
-{
- struct sha3_state *sctx = shash_desc_ctx(desc);
- struct crypto_shash *tfm = desc->tfm;
- __le64 *digest = (__le64 *)out;
- u8 block[SHA3_224_BLOCK_SIZE];
- unsigned int bs, ds;
- int i;
-
- ds = crypto_shash_digestsize(tfm);
- bs = crypto_shash_blocksize(tfm);
- memcpy(block, src, len);
-
- block[len++] = 0x06;
- memset(block + len, 0, bs - len);
- block[bs - 1] |= 0x80;
-
- kernel_neon_begin();
- sha3_ce_transform(sctx->st, block, 1, ds);
- kernel_neon_end();
- memzero_explicit(block , sizeof(block));
-
- for (i = 0; i < ds / 8; i++)
- put_unaligned_le64(sctx->st[i], digest++);
-
- if (ds & 4)
- put_unaligned_le32(sctx->st[i], (__le32 *)digest);
-
- return 0;
-}
-
-static struct shash_alg algs[] = { {
- .digestsize = SHA3_224_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = sha3_update,
- .finup = sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-224",
- .base.cra_driver_name = "sha3-224-ce",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_224_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
- .base.cra_priority = 200,
-}, {
- .digestsize = SHA3_256_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = sha3_update,
- .finup = sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-256",
- .base.cra_driver_name = "sha3-256-ce",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_256_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
- .base.cra_priority = 200,
-}, {
- .digestsize = SHA3_384_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = sha3_update,
- .finup = sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-384",
- .base.cra_driver_name = "sha3-384-ce",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_384_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
- .base.cra_priority = 200,
-}, {
- .digestsize = SHA3_512_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = sha3_update,
- .finup = sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-512",
- .base.cra_driver_name = "sha3-512-ce",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_512_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
- .base.cra_priority = 200,
-} };
-
-static int __init sha3_neon_mod_init(void)
-{
- return crypto_register_shashes(algs, ARRAY_SIZE(algs));
-}
-
-static void __exit sha3_neon_mod_fini(void)
-{
- crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
-}
-
-module_cpu_feature_match(SHA3, sha3_neon_mod_init);
-module_exit(sha3_neon_mod_fini);
diff --git a/arch/arm64/crypto/sm3-ce-glue.c b/arch/arm64/crypto/sm3-ce-glue.c
index eac6f5fa0abe..24c1fcfae072 100644
--- a/arch/arm64/crypto/sm3-ce-glue.c
+++ b/arch/arm64/crypto/sm3-ce-glue.c
@@ -5,7 +5,6 @@
* Copyright (C) 2018 Linaro Ltd <ard.biesheuvel@linaro.org>
*/
-#include <asm/neon.h>
#include <crypto/internal/hash.h>
#include <crypto/sm3.h>
#include <crypto/sm3_base.h>
@@ -13,6 +12,8 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <asm/simd.h>
+
MODULE_DESCRIPTION("SM3 secure hash using ARMv8 Crypto Extensions");
MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>");
MODULE_LICENSE("GPL v2");
@@ -25,18 +26,18 @@ static int sm3_ce_update(struct shash_desc *desc, const u8 *data,
{
int remain;
- kernel_neon_begin();
- remain = sm3_base_do_update_blocks(desc, data, len, sm3_ce_transform);
- kernel_neon_end();
+ scoped_ksimd() {
+ remain = sm3_base_do_update_blocks(desc, data, len, sm3_ce_transform);
+ }
return remain;
}
static int sm3_ce_finup(struct shash_desc *desc, const u8 *data,
unsigned int len, u8 *out)
{
- kernel_neon_begin();
- sm3_base_do_finup(desc, data, len, sm3_ce_transform);
- kernel_neon_end();
+ scoped_ksimd() {
+ sm3_base_do_finup(desc, data, len, sm3_ce_transform);
+ }
return sm3_base_finish(desc, out);
}
diff --git a/arch/arm64/crypto/sm3-neon-glue.c b/arch/arm64/crypto/sm3-neon-glue.c
index 6c4611a503a3..15f30cc24f32 100644
--- a/arch/arm64/crypto/sm3-neon-glue.c
+++ b/arch/arm64/crypto/sm3-neon-glue.c
@@ -5,7 +5,7 @@
* Copyright (C) 2022 Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
*/
-#include <asm/neon.h>
+#include <asm/simd.h>
#include <crypto/internal/hash.h>
#include <crypto/sm3.h>
#include <crypto/sm3_base.h>
@@ -20,20 +20,16 @@ asmlinkage void sm3_neon_transform(struct sm3_state *sst, u8 const *src,
static int sm3_neon_update(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
- int remain;
-
- kernel_neon_begin();
- remain = sm3_base_do_update_blocks(desc, data, len, sm3_neon_transform);
- kernel_neon_end();
- return remain;
+ scoped_ksimd()
+ return sm3_base_do_update_blocks(desc, data, len,
+ sm3_neon_transform);
}
static int sm3_neon_finup(struct shash_desc *desc, const u8 *data,
unsigned int len, u8 *out)
{
- kernel_neon_begin();
- sm3_base_do_finup(desc, data, len, sm3_neon_transform);
- kernel_neon_end();
+ scoped_ksimd()
+ sm3_base_do_finup(desc, data, len, sm3_neon_transform);
return sm3_base_finish(desc, out);
}
diff --git a/arch/arm64/crypto/sm4-ce-ccm-glue.c b/arch/arm64/crypto/sm4-ce-ccm-glue.c
index e9cc1c1364ec..332f02167a96 100644
--- a/arch/arm64/crypto/sm4-ce-ccm-glue.c
+++ b/arch/arm64/crypto/sm4-ce-ccm-glue.c
@@ -11,7 +11,7 @@
#include <linux/crypto.h>
#include <linux/kernel.h>
#include <linux/cpufeature.h>
-#include <asm/neon.h>
+#include <asm/simd.h>
#include <crypto/scatterwalk.h>
#include <crypto/internal/aead.h>
#include <crypto/internal/skcipher.h>
@@ -35,10 +35,9 @@ static int ccm_setkey(struct crypto_aead *tfm, const u8 *key,
if (key_len != SM4_KEY_SIZE)
return -EINVAL;
- kernel_neon_begin();
- sm4_ce_expand_key(key, ctx->rkey_enc, ctx->rkey_dec,
- crypto_sm4_fk, crypto_sm4_ck);
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_ce_expand_key(key, ctx->rkey_enc, ctx->rkey_dec,
+ crypto_sm4_fk, crypto_sm4_ck);
return 0;
}
@@ -167,39 +166,23 @@ static int ccm_crypt(struct aead_request *req, struct skcipher_walk *walk,
memcpy(ctr0, walk->iv, SM4_BLOCK_SIZE);
crypto_inc(walk->iv, SM4_BLOCK_SIZE);
- kernel_neon_begin();
+ scoped_ksimd() {
+ if (req->assoclen)
+ ccm_calculate_auth_mac(req, mac);
- if (req->assoclen)
- ccm_calculate_auth_mac(req, mac);
-
- while (walk->nbytes && walk->nbytes != walk->total) {
- unsigned int tail = walk->nbytes % SM4_BLOCK_SIZE;
-
- sm4_ce_ccm_crypt(rkey_enc, walk->dst.virt.addr,
- walk->src.virt.addr, walk->iv,
- walk->nbytes - tail, mac);
-
- kernel_neon_end();
-
- err = skcipher_walk_done(walk, tail);
-
- kernel_neon_begin();
- }
-
- if (walk->nbytes) {
- sm4_ce_ccm_crypt(rkey_enc, walk->dst.virt.addr,
- walk->src.virt.addr, walk->iv,
- walk->nbytes, mac);
+ while (walk->nbytes) {
+ unsigned int tail = walk->nbytes % SM4_BLOCK_SIZE;
- sm4_ce_ccm_final(rkey_enc, ctr0, mac);
+ if (walk->nbytes == walk->total)
+ tail = 0;
- kernel_neon_end();
+ sm4_ce_ccm_crypt(rkey_enc, walk->dst.virt.addr,
+ walk->src.virt.addr, walk->iv,
+ walk->nbytes - tail, mac);
- err = skcipher_walk_done(walk, 0);
- } else {
+ err = skcipher_walk_done(walk, tail);
+ }
sm4_ce_ccm_final(rkey_enc, ctr0, mac);
-
- kernel_neon_end();
}
return err;
diff --git a/arch/arm64/crypto/sm4-ce-cipher-glue.c b/arch/arm64/crypto/sm4-ce-cipher-glue.c
index c31d76fb5a17..bceec833ef4e 100644
--- a/arch/arm64/crypto/sm4-ce-cipher-glue.c
+++ b/arch/arm64/crypto/sm4-ce-cipher-glue.c
@@ -32,9 +32,8 @@ static void sm4_ce_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
if (!crypto_simd_usable()) {
sm4_crypt_block(ctx->rkey_enc, out, in);
} else {
- kernel_neon_begin();
- sm4_ce_do_crypt(ctx->rkey_enc, out, in);
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_ce_do_crypt(ctx->rkey_enc, out, in);
}
}
@@ -45,9 +44,8 @@ static void sm4_ce_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
if (!crypto_simd_usable()) {
sm4_crypt_block(ctx->rkey_dec, out, in);
} else {
- kernel_neon_begin();
- sm4_ce_do_crypt(ctx->rkey_dec, out, in);
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_ce_do_crypt(ctx->rkey_dec, out, in);
}
}
diff --git a/arch/arm64/crypto/sm4-ce-gcm-glue.c b/arch/arm64/crypto/sm4-ce-gcm-glue.c
index c2ea3d5f690b..ef06f4f768a1 100644
--- a/arch/arm64/crypto/sm4-ce-gcm-glue.c
+++ b/arch/arm64/crypto/sm4-ce-gcm-glue.c
@@ -11,7 +11,7 @@
#include <linux/crypto.h>
#include <linux/kernel.h>
#include <linux/cpufeature.h>
-#include <asm/neon.h>
+#include <asm/simd.h>
#include <crypto/b128ops.h>
#include <crypto/scatterwalk.h>
#include <crypto/internal/aead.h>
@@ -48,13 +48,11 @@ static int gcm_setkey(struct crypto_aead *tfm, const u8 *key,
if (key_len != SM4_KEY_SIZE)
return -EINVAL;
- kernel_neon_begin();
-
- sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
- crypto_sm4_fk, crypto_sm4_ck);
- sm4_ce_pmull_ghash_setup(ctx->key.rkey_enc, ctx->ghash_table);
-
- kernel_neon_end();
+ scoped_ksimd() {
+ sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
+ crypto_sm4_fk, crypto_sm4_ck);
+ sm4_ce_pmull_ghash_setup(ctx->key.rkey_enc, ctx->ghash_table);
+ }
return 0;
}
@@ -149,44 +147,28 @@ static int gcm_crypt(struct aead_request *req, struct skcipher_walk *walk,
memcpy(iv, req->iv, GCM_IV_SIZE);
put_unaligned_be32(2, iv + GCM_IV_SIZE);
- kernel_neon_begin();
+ scoped_ksimd() {
+ if (req->assoclen)
+ gcm_calculate_auth_mac(req, ghash);
- if (req->assoclen)
- gcm_calculate_auth_mac(req, ghash);
+ do {
+ unsigned int tail = walk->nbytes % SM4_BLOCK_SIZE;
+ const u8 *src = walk->src.virt.addr;
+ u8 *dst = walk->dst.virt.addr;
+ const u8 *l = NULL;
- while (walk->nbytes) {
- unsigned int tail = walk->nbytes % SM4_BLOCK_SIZE;
- const u8 *src = walk->src.virt.addr;
- u8 *dst = walk->dst.virt.addr;
+ if (walk->nbytes == walk->total) {
+ l = (const u8 *)&lengths;
+ tail = 0;
+ }
- if (walk->nbytes == walk->total) {
sm4_ce_pmull_gcm_crypt(ctx->key.rkey_enc, dst, src, iv,
- walk->nbytes, ghash,
- ctx->ghash_table,
- (const u8 *)&lengths);
-
- kernel_neon_end();
-
- return skcipher_walk_done(walk, 0);
- }
+ walk->nbytes - tail, ghash,
+ ctx->ghash_table, l);
- sm4_ce_pmull_gcm_crypt(ctx->key.rkey_enc, dst, src, iv,
- walk->nbytes - tail, ghash,
- ctx->ghash_table, NULL);
-
- kernel_neon_end();
-
- err = skcipher_walk_done(walk, tail);
-
- kernel_neon_begin();
+ err = skcipher_walk_done(walk, tail);
+ } while (walk->nbytes);
}
-
- sm4_ce_pmull_gcm_crypt(ctx->key.rkey_enc, NULL, NULL, iv,
- walk->nbytes, ghash, ctx->ghash_table,
- (const u8 *)&lengths);
-
- kernel_neon_end();
-
return err;
}
diff --git a/arch/arm64/crypto/sm4-ce-glue.c b/arch/arm64/crypto/sm4-ce-glue.c
index 7a60e7b559dc..5569cece5a0b 100644
--- a/arch/arm64/crypto/sm4-ce-glue.c
+++ b/arch/arm64/crypto/sm4-ce-glue.c
@@ -8,7 +8,7 @@
* Copyright (C) 2022 Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
*/
-#include <asm/neon.h>
+#include <asm/simd.h>
#include <crypto/b128ops.h>
#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
@@ -74,10 +74,9 @@ static int sm4_setkey(struct crypto_skcipher *tfm, const u8 *key,
if (key_len != SM4_KEY_SIZE)
return -EINVAL;
- kernel_neon_begin();
- sm4_ce_expand_key(key, ctx->rkey_enc, ctx->rkey_dec,
- crypto_sm4_fk, crypto_sm4_ck);
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_ce_expand_key(key, ctx->rkey_enc, ctx->rkey_dec,
+ crypto_sm4_fk, crypto_sm4_ck);
return 0;
}
@@ -94,12 +93,12 @@ static int sm4_xts_setkey(struct crypto_skcipher *tfm, const u8 *key,
if (ret)
return ret;
- kernel_neon_begin();
- sm4_ce_expand_key(key, ctx->key1.rkey_enc,
- ctx->key1.rkey_dec, crypto_sm4_fk, crypto_sm4_ck);
- sm4_ce_expand_key(&key[SM4_KEY_SIZE], ctx->key2.rkey_enc,
- ctx->key2.rkey_dec, crypto_sm4_fk, crypto_sm4_ck);
- kernel_neon_end();
+ scoped_ksimd() {
+ sm4_ce_expand_key(key, ctx->key1.rkey_enc,
+ ctx->key1.rkey_dec, crypto_sm4_fk, crypto_sm4_ck);
+ sm4_ce_expand_key(&key[SM4_KEY_SIZE], ctx->key2.rkey_enc,
+ ctx->key2.rkey_dec, crypto_sm4_fk, crypto_sm4_ck);
+ }
return 0;
}
@@ -117,16 +116,14 @@ static int sm4_ecb_do_crypt(struct skcipher_request *req, const u32 *rkey)
u8 *dst = walk.dst.virt.addr;
unsigned int nblks;
- kernel_neon_begin();
-
- nblks = BYTES2BLKS(nbytes);
- if (nblks) {
- sm4_ce_crypt(rkey, dst, src, nblks);
- nbytes -= nblks * SM4_BLOCK_SIZE;
+ scoped_ksimd() {
+ nblks = BYTES2BLKS(nbytes);
+ if (nblks) {
+ sm4_ce_crypt(rkey, dst, src, nblks);
+ nbytes -= nblks * SM4_BLOCK_SIZE;
+ }
}
- kernel_neon_end();
-
err = skcipher_walk_done(&walk, nbytes);
}
@@ -167,16 +164,14 @@ static int sm4_cbc_crypt(struct skcipher_request *req,
nblocks = nbytes / SM4_BLOCK_SIZE;
if (nblocks) {
- kernel_neon_begin();
-
- if (encrypt)
- sm4_ce_cbc_enc(ctx->rkey_enc, dst, src,
- walk.iv, nblocks);
- else
- sm4_ce_cbc_dec(ctx->rkey_dec, dst, src,
- walk.iv, nblocks);
-
- kernel_neon_end();
+ scoped_ksimd() {
+ if (encrypt)
+ sm4_ce_cbc_enc(ctx->rkey_enc, dst, src,
+ walk.iv, nblocks);
+ else
+ sm4_ce_cbc_dec(ctx->rkey_dec, dst, src,
+ walk.iv, nblocks);
+ }
}
err = skcipher_walk_done(&walk, nbytes % SM4_BLOCK_SIZE);
@@ -249,16 +244,14 @@ static int sm4_cbc_cts_crypt(struct skcipher_request *req, bool encrypt)
if (err)
return err;
- kernel_neon_begin();
-
- if (encrypt)
- sm4_ce_cbc_cts_enc(ctx->rkey_enc, walk.dst.virt.addr,
- walk.src.virt.addr, walk.iv, walk.nbytes);
- else
- sm4_ce_cbc_cts_dec(ctx->rkey_dec, walk.dst.virt.addr,
- walk.src.virt.addr, walk.iv, walk.nbytes);
-
- kernel_neon_end();
+ scoped_ksimd() {
+ if (encrypt)
+ sm4_ce_cbc_cts_enc(ctx->rkey_enc, walk.dst.virt.addr,
+ walk.src.virt.addr, walk.iv, walk.nbytes);
+ else
+ sm4_ce_cbc_cts_dec(ctx->rkey_dec, walk.dst.virt.addr,
+ walk.src.virt.addr, walk.iv, walk.nbytes);
+ }
return skcipher_walk_done(&walk, 0);
}
@@ -288,28 +281,26 @@ static int sm4_ctr_crypt(struct skcipher_request *req)
u8 *dst = walk.dst.virt.addr;
unsigned int nblks;
- kernel_neon_begin();
-
- nblks = BYTES2BLKS(nbytes);
- if (nblks) {
- sm4_ce_ctr_enc(ctx->rkey_enc, dst, src, walk.iv, nblks);
- dst += nblks * SM4_BLOCK_SIZE;
- src += nblks * SM4_BLOCK_SIZE;
- nbytes -= nblks * SM4_BLOCK_SIZE;
- }
-
- /* tail */
- if (walk.nbytes == walk.total && nbytes > 0) {
- u8 keystream[SM4_BLOCK_SIZE];
-
- sm4_ce_crypt_block(ctx->rkey_enc, keystream, walk.iv);
- crypto_inc(walk.iv, SM4_BLOCK_SIZE);
- crypto_xor_cpy(dst, src, keystream, nbytes);
- nbytes = 0;
+ scoped_ksimd() {
+ nblks = BYTES2BLKS(nbytes);
+ if (nblks) {
+ sm4_ce_ctr_enc(ctx->rkey_enc, dst, src, walk.iv, nblks);
+ dst += nblks * SM4_BLOCK_SIZE;
+ src += nblks * SM4_BLOCK_SIZE;
+ nbytes -= nblks * SM4_BLOCK_SIZE;
+ }
+
+ /* tail */
+ if (walk.nbytes == walk.total && nbytes > 0) {
+ u8 keystream[SM4_BLOCK_SIZE];
+
+ sm4_ce_crypt_block(ctx->rkey_enc, keystream, walk.iv);
+ crypto_inc(walk.iv, SM4_BLOCK_SIZE);
+ crypto_xor_cpy(dst, src, keystream, nbytes);
+ nbytes = 0;
+ }
}
- kernel_neon_end();
-
err = skcipher_walk_done(&walk, nbytes);
}
@@ -359,18 +350,16 @@ static int sm4_xts_crypt(struct skcipher_request *req, bool encrypt)
if (nbytes < walk.total)
nbytes &= ~(SM4_BLOCK_SIZE - 1);
- kernel_neon_begin();
-
- if (encrypt)
- sm4_ce_xts_enc(ctx->key1.rkey_enc, walk.dst.virt.addr,
- walk.src.virt.addr, walk.iv, nbytes,
- rkey2_enc);
- else
- sm4_ce_xts_dec(ctx->key1.rkey_dec, walk.dst.virt.addr,
- walk.src.virt.addr, walk.iv, nbytes,
- rkey2_enc);
-
- kernel_neon_end();
+ scoped_ksimd() {
+ if (encrypt)
+ sm4_ce_xts_enc(ctx->key1.rkey_enc, walk.dst.virt.addr,
+ walk.src.virt.addr, walk.iv, nbytes,
+ rkey2_enc);
+ else
+ sm4_ce_xts_dec(ctx->key1.rkey_dec, walk.dst.virt.addr,
+ walk.src.virt.addr, walk.iv, nbytes,
+ rkey2_enc);
+ }
rkey2_enc = NULL;
@@ -395,18 +384,16 @@ static int sm4_xts_crypt(struct skcipher_request *req, bool encrypt)
if (err)
return err;
- kernel_neon_begin();
-
- if (encrypt)
- sm4_ce_xts_enc(ctx->key1.rkey_enc, walk.dst.virt.addr,
- walk.src.virt.addr, walk.iv, walk.nbytes,
- rkey2_enc);
- else
- sm4_ce_xts_dec(ctx->key1.rkey_dec, walk.dst.virt.addr,
- walk.src.virt.addr, walk.iv, walk.nbytes,
- rkey2_enc);
-
- kernel_neon_end();
+ scoped_ksimd() {
+ if (encrypt)
+ sm4_ce_xts_enc(ctx->key1.rkey_enc, walk.dst.virt.addr,
+ walk.src.virt.addr, walk.iv, walk.nbytes,
+ rkey2_enc);
+ else
+ sm4_ce_xts_dec(ctx->key1.rkey_dec, walk.dst.virt.addr,
+ walk.src.virt.addr, walk.iv, walk.nbytes,
+ rkey2_enc);
+ }
return skcipher_walk_done(&walk, 0);
}
@@ -510,11 +497,9 @@ static int sm4_cbcmac_setkey(struct crypto_shash *tfm, const u8 *key,
if (key_len != SM4_KEY_SIZE)
return -EINVAL;
- kernel_neon_begin();
- sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
- crypto_sm4_fk, crypto_sm4_ck);
- kernel_neon_end();
-
+ scoped_ksimd()
+ sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
+ crypto_sm4_fk, crypto_sm4_ck);
return 0;
}
@@ -530,15 +515,13 @@ static int sm4_cmac_setkey(struct crypto_shash *tfm, const u8 *key,
memset(consts, 0, SM4_BLOCK_SIZE);
- kernel_neon_begin();
-
- sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
- crypto_sm4_fk, crypto_sm4_ck);
+ scoped_ksimd() {
+ sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
+ crypto_sm4_fk, crypto_sm4_ck);
- /* encrypt the zero block */
- sm4_ce_crypt_block(ctx->key.rkey_enc, (u8 *)consts, (const u8 *)consts);
-
- kernel_neon_end();
+ /* encrypt the zero block */
+ sm4_ce_crypt_block(ctx->key.rkey_enc, (u8 *)consts, (const u8 *)consts);
+ }
/* gf(2^128) multiply zero-ciphertext with u and u^2 */
a = be64_to_cpu(consts[0].a);
@@ -568,18 +551,16 @@ static int sm4_xcbc_setkey(struct crypto_shash *tfm, const u8 *key,
if (key_len != SM4_KEY_SIZE)
return -EINVAL;
- kernel_neon_begin();
-
- sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
- crypto_sm4_fk, crypto_sm4_ck);
+ scoped_ksimd() {
+ sm4_ce_expand_key(key, ctx->key.rkey_enc, ctx->key.rkey_dec,
+ crypto_sm4_fk, crypto_sm4_ck);
- sm4_ce_crypt_block(ctx->key.rkey_enc, key2, ks[0]);
- sm4_ce_crypt(ctx->key.rkey_enc, ctx->consts, ks[1], 2);
+ sm4_ce_crypt_block(ctx->key.rkey_enc, key2, ks[0]);
+ sm4_ce_crypt(ctx->key.rkey_enc, ctx->consts, ks[1], 2);
- sm4_ce_expand_key(key2, ctx->key.rkey_enc, ctx->key.rkey_dec,
- crypto_sm4_fk, crypto_sm4_ck);
-
- kernel_neon_end();
+ sm4_ce_expand_key(key2, ctx->key.rkey_enc, ctx->key.rkey_dec,
+ crypto_sm4_fk, crypto_sm4_ck);
+ }
return 0;
}
@@ -600,10 +581,9 @@ static int sm4_mac_update(struct shash_desc *desc, const u8 *p,
unsigned int nblocks = len / SM4_BLOCK_SIZE;
len %= SM4_BLOCK_SIZE;
- kernel_neon_begin();
- sm4_ce_mac_update(tctx->key.rkey_enc, ctx->digest, p,
- nblocks, false, true);
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_ce_mac_update(tctx->key.rkey_enc, ctx->digest, p,
+ nblocks, false, true);
return len;
}
@@ -619,10 +599,9 @@ static int sm4_cmac_finup(struct shash_desc *desc, const u8 *src,
ctx->digest[len] ^= 0x80;
consts += SM4_BLOCK_SIZE;
}
- kernel_neon_begin();
- sm4_ce_mac_update(tctx->key.rkey_enc, ctx->digest, consts, 1,
- false, true);
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_ce_mac_update(tctx->key.rkey_enc, ctx->digest, consts, 1,
+ false, true);
memcpy(out, ctx->digest, SM4_BLOCK_SIZE);
return 0;
}
@@ -635,10 +614,9 @@ static int sm4_cbcmac_finup(struct shash_desc *desc, const u8 *src,
if (len) {
crypto_xor(ctx->digest, src, len);
- kernel_neon_begin();
- sm4_ce_crypt_block(tctx->key.rkey_enc, ctx->digest,
- ctx->digest);
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_ce_crypt_block(tctx->key.rkey_enc, ctx->digest,
+ ctx->digest);
}
memcpy(out, ctx->digest, SM4_BLOCK_SIZE);
return 0;
diff --git a/arch/arm64/crypto/sm4-neon-glue.c b/arch/arm64/crypto/sm4-neon-glue.c
index e3500aca2d18..e944c2a2efb0 100644
--- a/arch/arm64/crypto/sm4-neon-glue.c
+++ b/arch/arm64/crypto/sm4-neon-glue.c
@@ -48,11 +48,8 @@ static int sm4_ecb_do_crypt(struct skcipher_request *req, const u32 *rkey)
nblocks = nbytes / SM4_BLOCK_SIZE;
if (nblocks) {
- kernel_neon_begin();
-
- sm4_neon_crypt(rkey, dst, src, nblocks);
-
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_neon_crypt(rkey, dst, src, nblocks);
}
err = skcipher_walk_done(&walk, nbytes % SM4_BLOCK_SIZE);
@@ -126,12 +123,9 @@ static int sm4_cbc_decrypt(struct skcipher_request *req)
nblocks = nbytes / SM4_BLOCK_SIZE;
if (nblocks) {
- kernel_neon_begin();
-
- sm4_neon_cbc_dec(ctx->rkey_dec, dst, src,
- walk.iv, nblocks);
-
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_neon_cbc_dec(ctx->rkey_dec, dst, src,
+ walk.iv, nblocks);
}
err = skcipher_walk_done(&walk, nbytes % SM4_BLOCK_SIZE);
@@ -157,12 +151,9 @@ static int sm4_ctr_crypt(struct skcipher_request *req)
nblocks = nbytes / SM4_BLOCK_SIZE;
if (nblocks) {
- kernel_neon_begin();
-
- sm4_neon_ctr_crypt(ctx->rkey_enc, dst, src,
- walk.iv, nblocks);
-
- kernel_neon_end();
+ scoped_ksimd()
+ sm4_neon_ctr_crypt(ctx->rkey_enc, dst, src,
+ walk.iv, nblocks);
dst += nblocks * SM4_BLOCK_SIZE;
src += nblocks * SM4_BLOCK_SIZE;
diff --git a/arch/arm64/include/asm/alternative-macros.h b/arch/arm64/include/asm/alternative-macros.h
index c8c77f9e36d6..862416624852 100644
--- a/arch/arm64/include/asm/alternative-macros.h
+++ b/arch/arm64/include/asm/alternative-macros.h
@@ -19,7 +19,7 @@
#error "cpucaps have overflown ARM64_CB_BIT"
#endif
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/stringify.h>
@@ -207,7 +207,7 @@ alternative_endif
#define _ALTERNATIVE_CFG(insn1, insn2, cap, cfg, ...) \
alternative_insn insn1, insn2, cap, IS_ENABLED(cfg)
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
/*
* Usage: asm(ALTERNATIVE(oldinstr, newinstr, cpucap));
@@ -219,7 +219,7 @@ alternative_endif
#define ALTERNATIVE(oldinstr, newinstr, ...) \
_ALTERNATIVE_CFG(oldinstr, newinstr, __VA_ARGS__, 1)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/types.h>
@@ -263,6 +263,6 @@ l_yes:
return true;
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_ALTERNATIVE_MACROS_H */
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index 00d97b8a757f..621aa8550174 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -4,7 +4,7 @@
#include <asm/alternative-macros.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/init.h>
#include <linux/types.h>
@@ -26,13 +26,16 @@ void __init apply_alternatives_all(void);
bool alternative_is_applied(u16 cpucap);
#ifdef CONFIG_MODULES
-void apply_alternatives_module(void *start, size_t length);
+int apply_alternatives_module(void *start, size_t length);
#else
-static inline void apply_alternatives_module(void *start, size_t length) { }
+static inline int apply_alternatives_module(void *start, size_t length)
+{
+ return 0;
+}
#endif
void alt_cb_patch_nops(struct alt_instr *alt, __le32 *origptr,
__le32 *updptr, int nr_inst);
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_ALTERNATIVE_H */
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
index 9e96f024b2f1..d20b03931a8d 100644
--- a/arch/arm64/include/asm/arch_gicv3.h
+++ b/arch/arm64/include/asm/arch_gicv3.h
@@ -9,7 +9,7 @@
#include <asm/sysreg.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/irqchip/arm-gic-common.h>
#include <linux/stringify.h>
@@ -188,5 +188,5 @@ static inline bool gic_has_relaxed_pmr_sync(void)
return cpus_have_cap(ARM64_HAS_GIC_PRIO_RELAXED_SYNC);
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_ARCH_GICV3_H */
diff --git a/arch/arm64/include/asm/asm-extable.h b/arch/arm64/include/asm/asm-extable.h
index 292f2687a12e..d67e2fdd1aee 100644
--- a/arch/arm64/include/asm/asm-extable.h
+++ b/arch/arm64/include/asm/asm-extable.h
@@ -27,7 +27,7 @@
/* Data fields for EX_TYPE_UACCESS_CPY */
#define EX_DATA_UACCESS_WRITE BIT(0)
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
.pushsection __ex_table, "a"; \
@@ -77,7 +77,7 @@
__ASM_EXTABLE_RAW(\insn, \fixup, EX_TYPE_UACCESS_CPY, \uaccess_is_write)
.endm
-#else /* __ASSEMBLY__ */
+#else /* __ASSEMBLER__ */
#include <linux/stringify.h>
@@ -132,6 +132,6 @@
EX_DATA_REG(ADDR, addr) \
")")
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_ASM_EXTABLE_H */
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 23be85d93348..f0ca7196f6fa 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -5,7 +5,7 @@
* Copyright (C) 1996-2000 Russell King
* Copyright (C) 2012 ARM Ltd.
*/
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#error "Only include this from assembly code"
#endif
@@ -325,14 +325,14 @@ alternative_cb_end
* tcr_set_t0sz - update TCR.T0SZ so that we can load the ID map
*/
.macro tcr_set_t0sz, valreg, t0sz
- bfi \valreg, \t0sz, #TCR_T0SZ_OFFSET, #TCR_TxSZ_WIDTH
+ bfi \valreg, \t0sz, #TCR_EL1_T0SZ_SHIFT, #TCR_EL1_T0SZ_WIDTH
.endm
/*
* tcr_set_t1sz - update TCR.T1SZ
*/
.macro tcr_set_t1sz, valreg, t1sz
- bfi \valreg, \t1sz, #TCR_T1SZ_OFFSET, #TCR_TxSZ_WIDTH
+ bfi \valreg, \t1sz, #TCR_EL1_T1SZ_SHIFT, #TCR_EL1_T1SZ_WIDTH
.endm
/*
@@ -371,7 +371,7 @@ alternative_endif
* [start, end) with dcache line size explicitly provided.
*
* op: operation passed to dc instruction
- * domain: domain used in dsb instruciton
+ * domain: domain used in dsb instruction
* start: starting virtual address of the region
* end: end virtual address of the region
* linesz: dcache line size
@@ -412,7 +412,7 @@ alternative_endif
* [start, end)
*
* op: operation passed to dc instruction
- * domain: domain used in dsb instruciton
+ * domain: domain used in dsb instruction
* start: starting virtual address of the region
* end: end virtual address of the region
* fixup: optional label to branch to on user fault
@@ -589,7 +589,7 @@ alternative_endif
.macro offset_ttbr1, ttbr, tmp
#if defined(CONFIG_ARM64_VA_BITS_52) && !defined(CONFIG_ARM64_LPA2)
mrs \tmp, tcr_el1
- and \tmp, \tmp, #TCR_T1SZ_MASK
+ and \tmp, \tmp, #TCR_EL1_T1SZ_MASK
cmp \tmp, #TCR_T1SZ(VA_BITS_MIN)
orr \tmp, \ttbr, #TTBR1_BADDR_4852_OFFSET
csel \ttbr, \tmp, \ttbr, eq
diff --git a/arch/arm64/include/asm/atomic_lse.h b/arch/arm64/include/asm/atomic_lse.h
index 87f568a94e55..afad1849c4cf 100644
--- a/arch/arm64/include/asm/atomic_lse.h
+++ b/arch/arm64/include/asm/atomic_lse.h
@@ -103,17 +103,17 @@ static __always_inline void __lse_atomic_and(int i, atomic_t *v)
return __lse_atomic_andnot(~i, v);
}
-#define ATOMIC_FETCH_OP_AND(name, mb, cl...) \
+#define ATOMIC_FETCH_OP_AND(name) \
static __always_inline int \
__lse_atomic_fetch_and##name(int i, atomic_t *v) \
{ \
return __lse_atomic_fetch_andnot##name(~i, v); \
}
-ATOMIC_FETCH_OP_AND(_relaxed, )
-ATOMIC_FETCH_OP_AND(_acquire, a, "memory")
-ATOMIC_FETCH_OP_AND(_release, l, "memory")
-ATOMIC_FETCH_OP_AND( , al, "memory")
+ATOMIC_FETCH_OP_AND(_relaxed)
+ATOMIC_FETCH_OP_AND(_acquire)
+ATOMIC_FETCH_OP_AND(_release)
+ATOMIC_FETCH_OP_AND( )
#undef ATOMIC_FETCH_OP_AND
@@ -210,17 +210,17 @@ static __always_inline void __lse_atomic64_and(s64 i, atomic64_t *v)
return __lse_atomic64_andnot(~i, v);
}
-#define ATOMIC64_FETCH_OP_AND(name, mb, cl...) \
+#define ATOMIC64_FETCH_OP_AND(name) \
static __always_inline long \
__lse_atomic64_fetch_and##name(s64 i, atomic64_t *v) \
{ \
return __lse_atomic64_fetch_andnot##name(~i, v); \
}
-ATOMIC64_FETCH_OP_AND(_relaxed, )
-ATOMIC64_FETCH_OP_AND(_acquire, a, "memory")
-ATOMIC64_FETCH_OP_AND(_release, l, "memory")
-ATOMIC64_FETCH_OP_AND( , al, "memory")
+ATOMIC64_FETCH_OP_AND(_relaxed)
+ATOMIC64_FETCH_OP_AND(_acquire)
+ATOMIC64_FETCH_OP_AND(_release)
+ATOMIC64_FETCH_OP_AND( )
#undef ATOMIC64_FETCH_OP_AND
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
index f5801b0ba9e9..9495c4441a46 100644
--- a/arch/arm64/include/asm/barrier.h
+++ b/arch/arm64/include/asm/barrier.h
@@ -7,7 +7,7 @@
#ifndef __ASM_BARRIER_H
#define __ASM_BARRIER_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/kasan-checks.h>
@@ -221,6 +221,6 @@ do { \
#include <asm-generic/barrier.h>
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_BARRIER_H */
diff --git a/arch/arm64/include/asm/bug.h b/arch/arm64/include/asm/bug.h
index 28be048db3f6..bceeaec21fb9 100644
--- a/arch/arm64/include/asm/bug.h
+++ b/arch/arm64/include/asm/bug.h
@@ -19,7 +19,7 @@
unreachable(); \
} while (0)
-#define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags))
+#define __WARN_FLAGS(cond_str, flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags))
#define HAVE_ARCH_BUG
diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h
index 09963004ceea..dd2c8586a725 100644
--- a/arch/arm64/include/asm/cache.h
+++ b/arch/arm64/include/asm/cache.h
@@ -35,7 +35,7 @@
#define ARCH_DMA_MINALIGN (128)
#define ARCH_KMALLOC_MINALIGN (8)
-#if !defined(__ASSEMBLY__) && !defined(BUILD_VDSO)
+#if !defined(__ASSEMBLER__) && !defined(BUILD_VDSO)
#include <linux/bitops.h>
#include <linux/kasan-enabled.h>
@@ -135,6 +135,6 @@ static inline u32 __attribute_const__ read_cpuid_effective_cachetype(void)
return ctr;
}
-#endif /* !defined(__ASSEMBLY__) && !defined(BUILD_VDSO) */
+#endif /* !defined(__ASSEMBLER__) && !defined(BUILD_VDSO) */
#endif
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 9d769291a306..2c8029472ad4 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -5,7 +5,7 @@
#include <asm/cpucap-defs.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/types.h>
/*
* Check whether a cpucap is possible at compiletime.
@@ -77,6 +77,6 @@ cpucap_is_possible(const unsigned int cap)
return true;
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index e223cbf350e4..4de51f8d92cb 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -19,7 +19,7 @@
#define ARM64_SW_FEATURE_OVERRIDE_HVHE 4
#define ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF 8
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/bug.h>
#include <linux/jump_label.h>
@@ -199,7 +199,7 @@ extern struct arm64_ftr_reg arm64_ftr_reg_ctrel0;
* registers (e.g, SCTLR, TCR etc.) or patching the kernel via
* alternatives. The kernel patching is batched and performed at later
* point. The actions are always initiated only after the capability
- * is finalised. This is usally denoted by "enabling" the capability.
+ * is finalised. This is usually denoted by "enabling" the capability.
* The actions are initiated as follows :
* a) Action is triggered on all online CPUs, after the capability is
* finalised, invoked within the stop_machine() context from
@@ -251,7 +251,7 @@ extern struct arm64_ftr_reg arm64_ftr_reg_ctrel0;
#define ARM64_CPUCAP_SCOPE_LOCAL_CPU ((u16)BIT(0))
#define ARM64_CPUCAP_SCOPE_SYSTEM ((u16)BIT(1))
/*
- * The capabilitiy is detected on the Boot CPU and is used by kernel
+ * The capability is detected on the Boot CPU and is used by kernel
* during early boot. i.e, the capability should be "detected" and
* "enabled" as early as possibly on all booting CPUs.
*/
@@ -1078,6 +1078,6 @@ static inline bool cpu_has_lpa2(void)
#endif
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 9b00b75acbf2..08860d482e60 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -247,9 +247,9 @@
/* Fujitsu Erratum 010001 affects A64FX 1.0 and 1.1, (v0r0 and v1r0) */
#define MIDR_FUJITSU_ERRATUM_010001 MIDR_FUJITSU_A64FX
#define MIDR_FUJITSU_ERRATUM_010001_MASK (~MIDR_CPU_VAR_REV(1, 0))
-#define TCR_CLEAR_FUJITSU_ERRATUM_010001 (TCR_NFD1 | TCR_NFD0)
+#define TCR_CLEAR_FUJITSU_ERRATUM_010001 (TCR_EL1_NFD1 | TCR_EL1_NFD0)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/sysreg.h>
@@ -328,6 +328,6 @@ static inline u32 __attribute_const__ read_cpuid_cachetype(void)
{
return read_cpuid(CTR_EL0);
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/current.h b/arch/arm64/include/asm/current.h
index 54ceae0874c7..c92912eaf186 100644
--- a/arch/arm64/include/asm/current.h
+++ b/arch/arm64/include/asm/current.h
@@ -4,7 +4,7 @@
#include <linux/compiler.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
struct task_struct;
@@ -23,7 +23,7 @@ static __always_inline struct task_struct *get_current(void)
#define current get_current()
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_CURRENT_H */
diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h
index f5e3ed2420ce..8d5f92418838 100644
--- a/arch/arm64/include/asm/debug-monitors.h
+++ b/arch/arm64/include/asm/debug-monitors.h
@@ -48,7 +48,7 @@
#define AARCH32_BREAK_THUMB2_LO 0xf7f0
#define AARCH32_BREAK_THUMB2_HI 0xa000
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
struct task_struct;
#define DBG_ARCH_ID_RESERVED 0 /* In case of ptrace ABI updates. */
@@ -88,5 +88,5 @@ static inline bool try_step_suspended_breakpoints(struct pt_regs *regs)
bool try_handle_aarch32_break(struct pt_regs *regs);
-#endif /* __ASSEMBLY */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_DEBUG_MONITORS_H */
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index bcd5622aa096..aa91165ca140 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -126,21 +126,14 @@ static inline void efi_set_pgd(struct mm_struct *mm)
if (mm != current->active_mm) {
/*
* Update the current thread's saved ttbr0 since it is
- * restored as part of a return from exception. Enable
- * access to the valid TTBR0_EL1 and invoke the errata
- * workaround directly since there is no return from
- * exception when invoking the EFI run-time services.
+ * restored as part of a return from exception.
*/
update_saved_ttbr0(current, mm);
- uaccess_ttbr0_enable();
- post_ttbr_update_workaround();
} else {
/*
- * Defer the switch to the current thread's TTBR0_EL1
- * until uaccess_enable(). Restore the current
- * thread's saved ttbr0 corresponding to its active_mm
+ * Restore the current thread's saved ttbr0
+ * corresponding to its active_mm
*/
- uaccess_ttbr0_disable();
update_saved_ttbr0(current, current->active_mm);
}
}
diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h
index 99a7c0235e6d..cacd20df1786 100644
--- a/arch/arm64/include/asm/el2_setup.h
+++ b/arch/arm64/include/asm/el2_setup.h
@@ -7,7 +7,7 @@
#ifndef __ARM_KVM_INIT_H__
#define __ARM_KVM_INIT_H__
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#error Assembly-only header
#endif
@@ -24,7 +24,7 @@
* ID_AA64MMFR4_EL1.E2H0 < 0. On such CPUs HCR_EL2.E2H is RES1, but it
* can reset into an UNKNOWN state and might not read as 1 until it has
* been initialized explicitly.
- * Initalize HCR_EL2.E2H so that later code can rely upon HCR_EL2.E2H
+ * Initialize HCR_EL2.E2H so that later code can rely upon HCR_EL2.E2H
* indicating whether the CPU is running in E2H mode.
*/
mrs_s x1, SYS_ID_AA64MMFR4_EL1
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index 3f93f4eef953..d2779d604c7b 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -133,7 +133,7 @@
#define ELF_ET_DYN_BASE (2 * DEFAULT_MAP_WINDOW_64 / 3)
#endif /* CONFIG_ARM64_FORCE_52BIT */
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <uapi/linux/elf.h>
#include <linux/bug.h>
@@ -293,6 +293,6 @@ static inline int arch_check_elf(void *ehdr, bool has_interp,
return 0;
}
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index e1deed824464..4975a92cbd17 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -431,7 +431,7 @@
#define ESR_ELx_IT_GCSPOPCX 6
#define ESR_ELx_IT_GCSPOPX 7
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/types.h>
static inline unsigned long esr_brk_comment(unsigned long esr)
@@ -534,6 +534,6 @@ static inline bool esr_iss_is_eretab(unsigned long esr)
}
const char *esr_get_class_string(unsigned long esr);
-#endif /* __ASSEMBLY */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_ESR_H */
diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h
index 635a43c4ec85..65555284446e 100644
--- a/arch/arm64/include/asm/fixmap.h
+++ b/arch/arm64/include/asm/fixmap.h
@@ -15,7 +15,7 @@
#ifndef _ASM_ARM64_FIXMAP_H
#define _ASM_ARM64_FIXMAP_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/kernel.h>
#include <linux/math.h>
#include <linux/sizes.h>
@@ -117,5 +117,5 @@ extern void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t pr
#include <asm-generic/fixmap.h>
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* _ASM_ARM64_FIXMAP_H */
diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index b8cf0ea43cc0..1d2e33559bd5 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -12,7 +12,7 @@
#include <asm/sigcontext.h>
#include <asm/sysreg.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/bitmap.h>
#include <linux/build_bug.h>
diff --git a/arch/arm64/include/asm/fpu.h b/arch/arm64/include/asm/fpu.h
index 2ae50bdce59b..751e88a96734 100644
--- a/arch/arm64/include/asm/fpu.h
+++ b/arch/arm64/include/asm/fpu.h
@@ -6,10 +6,22 @@
#ifndef __ASM_FPU_H
#define __ASM_FPU_H
+#include <linux/preempt.h>
#include <asm/neon.h>
#define kernel_fpu_available() cpu_has_neon()
-#define kernel_fpu_begin() kernel_neon_begin()
-#define kernel_fpu_end() kernel_neon_end()
+
+static inline void kernel_fpu_begin(void)
+{
+ BUG_ON(!in_task());
+ preempt_disable();
+ kernel_neon_begin(NULL);
+}
+
+static inline void kernel_fpu_end(void)
+{
+ kernel_neon_end(NULL);
+ preempt_enable();
+}
#endif /* ! __ASM_FPU_H */
diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h
index ba7cf7fec5e9..1621c84f44b3 100644
--- a/arch/arm64/include/asm/ftrace.h
+++ b/arch/arm64/include/asm/ftrace.h
@@ -37,7 +37,7 @@
*/
#define ARCH_FTRACE_SHIFT_STACK_TRACER 1
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/compat.h>
extern void _mcount(unsigned long);
@@ -217,9 +217,9 @@ static inline bool arch_syscall_match_sym_name(const char *sym,
*/
return !strcmp(sym + 8, name);
}
-#endif /* ifndef __ASSEMBLY__ */
+#endif /* ifndef __ASSEMBLER__ */
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent,
diff --git a/arch/arm64/include/asm/gpr-num.h b/arch/arm64/include/asm/gpr-num.h
index 05da4a7c5788..a114e4f8209b 100644
--- a/arch/arm64/include/asm/gpr-num.h
+++ b/arch/arm64/include/asm/gpr-num.h
@@ -2,7 +2,7 @@
#ifndef __ASM_GPR_NUM_H
#define __ASM_GPR_NUM_H
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
.irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
.equ .L__gpr_num_x\num, \num
@@ -11,7 +11,7 @@
.equ .L__gpr_num_xzr, 31
.equ .L__gpr_num_wzr, 31
-#else /* __ASSEMBLY__ */
+#else /* __ASSEMBLER__ */
#define __DEFINE_ASM_GPR_NUMS \
" .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n" \
@@ -21,6 +21,6 @@
" .equ .L__gpr_num_xzr, 31\n" \
" .equ .L__gpr_num_wzr, 31\n"
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_GPR_NUM_H */
diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h
index 6d567265467c..1f63814ae6c4 100644
--- a/arch/arm64/include/asm/hwcap.h
+++ b/arch/arm64/include/asm/hwcap.h
@@ -46,7 +46,7 @@
#define COMPAT_HWCAP2_SB (1 << 5)
#define COMPAT_HWCAP2_SSBS (1 << 6)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/log2.h>
/*
diff --git a/arch/arm64/include/asm/image.h b/arch/arm64/include/asm/image.h
index c09cf942dc92..9ba85173f857 100644
--- a/arch/arm64/include/asm/image.h
+++ b/arch/arm64/include/asm/image.h
@@ -20,7 +20,7 @@
#define ARM64_IMAGE_FLAG_PAGE_SIZE_64K 3
#define ARM64_IMAGE_FLAG_PHYS_BASE 1
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#define arm64_image_flag_field(flags, field) \
(((flags) >> field##_SHIFT) & field##_MASK)
@@ -54,6 +54,6 @@ struct arm64_image_header {
__le32 res5;
};
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_IMAGE_H */
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index 18c7811774d3..e1d30ba99d01 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -12,7 +12,7 @@
#include <asm/insn-def.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
enum aarch64_insn_hint_cr_op {
AARCH64_INSN_HINT_NOP = 0x0 << 5,
@@ -730,6 +730,6 @@ u32 aarch32_insn_mcr_extract_crm(u32 insn);
typedef bool (pstate_check_t)(unsigned long);
extern pstate_check_t * const aarch32_opcode_cond_checks[16];
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_INSN_H */
diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h
index 424ed421cd97..0cb211d3607d 100644
--- a/arch/arm64/include/asm/jump_label.h
+++ b/arch/arm64/include/asm/jump_label.h
@@ -8,7 +8,7 @@
#ifndef __ASM_JUMP_LABEL_H
#define __ASM_JUMP_LABEL_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/types.h>
#include <asm/insn.h>
@@ -58,5 +58,5 @@ l_yes:
return true;
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_JUMP_LABEL_H */
diff --git a/arch/arm64/include/asm/kasan.h b/arch/arm64/include/asm/kasan.h
index e1b57c13f8a4..b167e9d3da91 100644
--- a/arch/arm64/include/asm/kasan.h
+++ b/arch/arm64/include/asm/kasan.h
@@ -2,7 +2,7 @@
#ifndef __ASM_KASAN_H
#define __ASM_KASAN_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/linkage.h>
#include <asm/memory.h>
diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h
index 4d9cc7a76d9c..892e5bebda95 100644
--- a/arch/arm64/include/asm/kexec.h
+++ b/arch/arm64/include/asm/kexec.h
@@ -25,7 +25,7 @@
#define KEXEC_ARCH KEXEC_ARCH_AARCH64
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
/**
* crash_setup_regs() - save registers for the panic kernel
@@ -130,6 +130,6 @@ extern int load_other_segments(struct kimage *image,
char *cmdline);
#endif
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/kfence.h b/arch/arm64/include/asm/kfence.h
index a81937fae9f6..21dbc9dda747 100644
--- a/arch/arm64/include/asm/kfence.h
+++ b/arch/arm64/include/asm/kfence.h
@@ -10,8 +10,6 @@
#include <asm/set_memory.h>
-static inline bool arch_kfence_init_pool(void) { return true; }
-
static inline bool kfence_protect_page(unsigned long addr, bool protect)
{
set_memory_valid(addr, 1, !protect);
@@ -25,6 +23,7 @@ static inline bool arm64_kfence_can_set_direct_map(void)
{
return !kfence_early_init;
}
+bool arch_kfence_init_pool(void);
#else /* CONFIG_KFENCE */
static inline bool arm64_kfence_can_set_direct_map(void) { return false; }
#endif /* CONFIG_KFENCE */
diff --git a/arch/arm64/include/asm/kgdb.h b/arch/arm64/include/asm/kgdb.h
index 3184f5d1e3ae..67ef1c5532ae 100644
--- a/arch/arm64/include/asm/kgdb.h
+++ b/arch/arm64/include/asm/kgdb.h
@@ -14,7 +14,7 @@
#include <linux/ptrace.h>
#include <asm/debug-monitors.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
static inline void arch_kgdb_breakpoint(void)
{
@@ -36,7 +36,7 @@ static inline int kgdb_single_step_handler(struct pt_regs *regs,
}
#endif
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
/*
* gdb remote procotol (well most versions of it) expects the following
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index 9da54d4ee49e..4b34f7b7ed2f 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -46,7 +46,7 @@
#define __KVM_HOST_SMCCC_FUNC___kvm_hyp_init 0
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/mm.h>
@@ -303,7 +303,7 @@ void kvm_compute_final_ctr_el0(struct alt_instr *alt,
void __noreturn __cold nvhe_hyp_panic_handler(u64 esr, u64 spsr, u64 elr_virt,
u64 elr_phys, u64 par, uintptr_t vcpu, u64 far, u64 hpfar);
-#else /* __ASSEMBLY__ */
+#else /* __ASSEMBLER__ */
.macro get_host_ctxt reg, tmp
adr_this_cpu \reg, kvm_host_data, \tmp
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h
index e4069f2ce642..2dc5e6e742bb 100644
--- a/arch/arm64/include/asm/kvm_mmu.h
+++ b/arch/arm64/include/asm/kvm_mmu.h
@@ -49,7 +49,7 @@
* mappings, and none of this applies in that case.
*/
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#include <asm/alternative.h>
@@ -396,5 +396,5 @@ void kvm_s2_ptdump_create_debugfs(struct kvm *kvm);
static inline void kvm_s2_ptdump_create_debugfs(struct kvm *kvm) {}
#endif /* CONFIG_PTDUMP_STAGE2_DEBUGFS */
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ARM64_KVM_MMU_H__ */
diff --git a/arch/arm64/include/asm/kvm_mte.h b/arch/arm64/include/asm/kvm_mte.h
index de002636eb1f..3171963ad25c 100644
--- a/arch/arm64/include/asm/kvm_mte.h
+++ b/arch/arm64/include/asm/kvm_mte.h
@@ -5,7 +5,7 @@
#ifndef __ASM_KVM_MTE_H
#define __ASM_KVM_MTE_H
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#include <asm/sysreg.h>
@@ -62,5 +62,5 @@ alternative_else_nop_endif
.endm
#endif /* CONFIG_ARM64_MTE */
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_KVM_MTE_H */
diff --git a/arch/arm64/include/asm/kvm_ptrauth.h b/arch/arm64/include/asm/kvm_ptrauth.h
index 6199c9f7ec6e..e50987b32483 100644
--- a/arch/arm64/include/asm/kvm_ptrauth.h
+++ b/arch/arm64/include/asm/kvm_ptrauth.h
@@ -8,7 +8,7 @@
#ifndef __ASM_KVM_PTRAUTH_H
#define __ASM_KVM_PTRAUTH_H
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#include <asm/sysreg.h>
@@ -100,7 +100,7 @@ alternative_else_nop_endif
.endm
#endif /* CONFIG_ARM64_PTR_AUTH */
-#else /* !__ASSEMBLY */
+#else /* !__ASSEMBLER__ */
#define __ptrauth_save_key(ctxt, key) \
do { \
@@ -120,5 +120,5 @@ alternative_else_nop_endif
__ptrauth_save_key(ctxt, APGA); \
} while(0)
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_KVM_PTRAUTH_H */
diff --git a/arch/arm64/include/asm/linkage.h b/arch/arm64/include/asm/linkage.h
index d3acd9c87509..40bd17add539 100644
--- a/arch/arm64/include/asm/linkage.h
+++ b/arch/arm64/include/asm/linkage.h
@@ -1,7 +1,7 @@
#ifndef __ASM_LINKAGE_H
#define __ASM_LINKAGE_H
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#include <asm/assembler.h>
#endif
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index f1505c4acb38..9d54b2ea49d6 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -207,7 +207,7 @@
*/
#define TRAMP_SWAPPER_OFFSET (2 * PAGE_SIZE)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/bitops.h>
#include <linux/compiler.h>
@@ -392,7 +392,6 @@ static inline unsigned long virt_to_pfn(const void *kaddr)
* virt_to_page(x) convert a _valid_ virtual address to struct page *
* virt_addr_valid(x) indicates whether a virtual address is valid
*/
-#define ARCH_PFN_OFFSET ((unsigned long)PHYS_PFN_OFFSET)
#if defined(CONFIG_DEBUG_VIRTUAL)
#define page_to_virt(x) ({ \
@@ -422,7 +421,7 @@ static inline unsigned long virt_to_pfn(const void *kaddr)
})
void dump_mem_limit(void);
-#endif /* !ASSEMBLY */
+#endif /* !__ASSEMBLER__ */
/*
* Given that the GIC architecture permits ITS implementations that can only be
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 78a4dbf75e60..137a173df1ff 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -12,7 +12,7 @@
#define USER_ASID_FLAG (UL(1) << USER_ASID_BIT)
#define TTBR_ASID_MASK (UL(0xffff) << 48)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/refcount.h>
#include <asm/cpufeature.h>
@@ -112,5 +112,5 @@ void kpti_install_ng_mappings(void);
static inline void kpti_install_ng_mappings(void) {}
#endif
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 0dbe3b29049b..cc80af59c69e 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -8,7 +8,7 @@
#ifndef __ASM_MMU_CONTEXT_H
#define __ASM_MMU_CONTEXT_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/compiler.h>
#include <linux/sched.h>
@@ -62,29 +62,21 @@ static inline void cpu_switch_mm(pgd_t *pgd, struct mm_struct *mm)
}
/*
- * TCR.T0SZ value to use when the ID map is active.
- */
-#define idmap_t0sz TCR_T0SZ(IDMAP_VA_BITS)
-
-/*
* Ensure TCR.T0SZ is set to the provided value.
*/
static inline void __cpu_set_tcr_t0sz(unsigned long t0sz)
{
unsigned long tcr = read_sysreg(tcr_el1);
- if ((tcr & TCR_T0SZ_MASK) == t0sz)
+ if ((tcr & TCR_EL1_T0SZ_MASK) == t0sz)
return;
- tcr &= ~TCR_T0SZ_MASK;
+ tcr &= ~TCR_EL1_T0SZ_MASK;
tcr |= t0sz;
write_sysreg(tcr, tcr_el1);
isb();
}
-#define cpu_set_default_tcr_t0sz() __cpu_set_tcr_t0sz(TCR_T0SZ(vabits_actual))
-#define cpu_set_idmap_tcr_t0sz() __cpu_set_tcr_t0sz(idmap_t0sz)
-
/*
* Remove the idmap from TTBR0_EL1 and install the pgd of the active mm.
*
@@ -103,7 +95,7 @@ static inline void cpu_uninstall_idmap(void)
cpu_set_reserved_ttbr0();
local_flush_tlb_all();
- cpu_set_default_tcr_t0sz();
+ __cpu_set_tcr_t0sz(TCR_T0SZ(vabits_actual));
if (mm != &init_mm && !system_uses_ttbr0_pan())
cpu_switch_mm(mm->pgd, mm);
@@ -113,7 +105,7 @@ static inline void cpu_install_idmap(void)
{
cpu_set_reserved_ttbr0();
local_flush_tlb_all();
- cpu_set_idmap_tcr_t0sz();
+ __cpu_set_tcr_t0sz(TCR_T0SZ(IDMAP_VA_BITS));
cpu_switch_mm(lm_alias(idmap_pg_dir), &init_mm);
}
@@ -330,6 +322,6 @@ static inline void deactivate_mm(struct task_struct *tsk,
#include <asm-generic/mmu_context.h>
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* !__ASM_MMU_CONTEXT_H */
diff --git a/arch/arm64/include/asm/mte-kasan.h b/arch/arm64/include/asm/mte-kasan.h
index 0f9b08e8fb8d..352139271918 100644
--- a/arch/arm64/include/asm/mte-kasan.h
+++ b/arch/arm64/include/asm/mte-kasan.h
@@ -9,7 +9,7 @@
#include <asm/cputype.h>
#include <asm/mte-def.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/types.h>
@@ -259,6 +259,6 @@ static inline int mte_enable_kernel_store_only(void)
#endif /* CONFIG_ARM64_MTE */
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_MTE_KASAN_H */
diff --git a/arch/arm64/include/asm/mte.h b/arch/arm64/include/asm/mte.h
index 3b5069f4683d..6d4a78b9dc3e 100644
--- a/arch/arm64/include/asm/mte.h
+++ b/arch/arm64/include/asm/mte.h
@@ -8,7 +8,7 @@
#include <asm/compiler.h>
#include <asm/mte-def.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/bitfield.h>
#include <linux/kasan-enabled.h>
@@ -282,5 +282,5 @@ static inline void mte_check_tfsr_exit(void)
}
#endif /* CONFIG_KASAN_HW_TAGS */
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_MTE_H */
diff --git a/arch/arm64/include/asm/neon.h b/arch/arm64/include/asm/neon.h
index d4b1d172a79b..acebee4605b5 100644
--- a/arch/arm64/include/asm/neon.h
+++ b/arch/arm64/include/asm/neon.h
@@ -13,7 +13,7 @@
#define cpu_has_neon() system_supports_fpsimd()
-void kernel_neon_begin(void);
-void kernel_neon_end(void);
+void kernel_neon_begin(struct user_fpsimd_state *);
+void kernel_neon_end(struct user_fpsimd_state *);
#endif /* ! __ASM_NEON_H */
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
index 2312e6ee595f..00f117ff4f7a 100644
--- a/arch/arm64/include/asm/page.h
+++ b/arch/arm64/include/asm/page.h
@@ -10,7 +10,7 @@
#include <asm/page-def.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/personality.h> /* for READ_IMPLIES_EXEC */
#include <linux/types.h> /* for gfp_t */
@@ -33,8 +33,8 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
unsigned long vaddr);
#define vma_alloc_zeroed_movable_folio vma_alloc_zeroed_movable_folio
-void tag_clear_highpage(struct page *to);
-#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGE
+bool tag_clear_highpages(struct page *to, int numpages);
+#define __HAVE_ARCH_TAG_CLEAR_HIGHPAGES
#define clear_user_page(page, vaddr, pg) clear_page(page)
#define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
@@ -45,7 +45,7 @@ int pfn_is_map_memory(unsigned long pfn);
#include <asm/memory.h>
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#define VM_DATA_DEFAULT_FLAGS (VM_DATA_FLAGS_TSK_EXEC | VM_MTE_ALLOWED)
diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h
index 9abcc8ef3087..b57b2bb00967 100644
--- a/arch/arm64/include/asm/percpu.h
+++ b/arch/arm64/include/asm/percpu.h
@@ -77,7 +77,7 @@ __percpu_##name##_case_##sz(void *ptr, unsigned long val) \
" stxr" #sfx "\t%w[loop], %" #w "[tmp], %[ptr]\n" \
" cbnz %w[loop], 1b", \
/* LSE atomics */ \
- #op_lse "\t%" #w "[val], %[ptr]\n" \
+ #op_lse "\t%" #w "[val], %" #w "[tmp], %[ptr]\n" \
__nops(3)) \
: [loop] "=&r" (loop), [tmp] "=&r" (tmp), \
[ptr] "+Q"(*(u##sz *)ptr) \
@@ -124,9 +124,16 @@ PERCPU_RW_OPS(8)
PERCPU_RW_OPS(16)
PERCPU_RW_OPS(32)
PERCPU_RW_OPS(64)
-PERCPU_OP(add, add, stadd)
-PERCPU_OP(andnot, bic, stclr)
-PERCPU_OP(or, orr, stset)
+
+/*
+ * Use value-returning atomics for CPU-local ops as they are more likely
+ * to execute "near" to the CPU (e.g. in L1$).
+ *
+ * https://lore.kernel.org/r/e7d539ed-ced0-4b96-8ecd-048a5b803b85@paulmck-laptop
+ */
+PERCPU_OP(add, add, ldadd)
+PERCPU_OP(andnot, bic, ldclr)
+PERCPU_OP(or, orr, ldset)
PERCPU_RET_OP(add, add, ldadd)
#undef PERCPU_RW_OPS
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
index f3b77deedfa2..d49180bb7cb3 100644
--- a/arch/arm64/include/asm/pgtable-hwdef.h
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -228,102 +228,53 @@
/*
* TCR flags.
*/
-#define TCR_T0SZ_OFFSET 0
-#define TCR_T1SZ_OFFSET 16
-#define TCR_T0SZ(x) ((UL(64) - (x)) << TCR_T0SZ_OFFSET)
-#define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_T1SZ_OFFSET)
-#define TCR_TxSZ(x) (TCR_T0SZ(x) | TCR_T1SZ(x))
-#define TCR_TxSZ_WIDTH 6
-#define TCR_T0SZ_MASK (((UL(1) << TCR_TxSZ_WIDTH) - 1) << TCR_T0SZ_OFFSET)
-#define TCR_T1SZ_MASK (((UL(1) << TCR_TxSZ_WIDTH) - 1) << TCR_T1SZ_OFFSET)
-
-#define TCR_EPD0_SHIFT 7
-#define TCR_EPD0_MASK (UL(1) << TCR_EPD0_SHIFT)
-#define TCR_IRGN0_SHIFT 8
-#define TCR_IRGN0_MASK (UL(3) << TCR_IRGN0_SHIFT)
-#define TCR_IRGN0_NC (UL(0) << TCR_IRGN0_SHIFT)
-#define TCR_IRGN0_WBWA (UL(1) << TCR_IRGN0_SHIFT)
-#define TCR_IRGN0_WT (UL(2) << TCR_IRGN0_SHIFT)
-#define TCR_IRGN0_WBnWA (UL(3) << TCR_IRGN0_SHIFT)
-
-#define TCR_EPD1_SHIFT 23
-#define TCR_EPD1_MASK (UL(1) << TCR_EPD1_SHIFT)
-#define TCR_IRGN1_SHIFT 24
-#define TCR_IRGN1_MASK (UL(3) << TCR_IRGN1_SHIFT)
-#define TCR_IRGN1_NC (UL(0) << TCR_IRGN1_SHIFT)
-#define TCR_IRGN1_WBWA (UL(1) << TCR_IRGN1_SHIFT)
-#define TCR_IRGN1_WT (UL(2) << TCR_IRGN1_SHIFT)
-#define TCR_IRGN1_WBnWA (UL(3) << TCR_IRGN1_SHIFT)
-
-#define TCR_IRGN_NC (TCR_IRGN0_NC | TCR_IRGN1_NC)
-#define TCR_IRGN_WBWA (TCR_IRGN0_WBWA | TCR_IRGN1_WBWA)
-#define TCR_IRGN_WT (TCR_IRGN0_WT | TCR_IRGN1_WT)
-#define TCR_IRGN_WBnWA (TCR_IRGN0_WBnWA | TCR_IRGN1_WBnWA)
-#define TCR_IRGN_MASK (TCR_IRGN0_MASK | TCR_IRGN1_MASK)
-
-
-#define TCR_ORGN0_SHIFT 10
-#define TCR_ORGN0_MASK (UL(3) << TCR_ORGN0_SHIFT)
-#define TCR_ORGN0_NC (UL(0) << TCR_ORGN0_SHIFT)
-#define TCR_ORGN0_WBWA (UL(1) << TCR_ORGN0_SHIFT)
-#define TCR_ORGN0_WT (UL(2) << TCR_ORGN0_SHIFT)
-#define TCR_ORGN0_WBnWA (UL(3) << TCR_ORGN0_SHIFT)
-
-#define TCR_ORGN1_SHIFT 26
-#define TCR_ORGN1_MASK (UL(3) << TCR_ORGN1_SHIFT)
-#define TCR_ORGN1_NC (UL(0) << TCR_ORGN1_SHIFT)
-#define TCR_ORGN1_WBWA (UL(1) << TCR_ORGN1_SHIFT)
-#define TCR_ORGN1_WT (UL(2) << TCR_ORGN1_SHIFT)
-#define TCR_ORGN1_WBnWA (UL(3) << TCR_ORGN1_SHIFT)
-
-#define TCR_ORGN_NC (TCR_ORGN0_NC | TCR_ORGN1_NC)
-#define TCR_ORGN_WBWA (TCR_ORGN0_WBWA | TCR_ORGN1_WBWA)
-#define TCR_ORGN_WT (TCR_ORGN0_WT | TCR_ORGN1_WT)
-#define TCR_ORGN_WBnWA (TCR_ORGN0_WBnWA | TCR_ORGN1_WBnWA)
-#define TCR_ORGN_MASK (TCR_ORGN0_MASK | TCR_ORGN1_MASK)
-
-#define TCR_SH0_SHIFT 12
-#define TCR_SH0_MASK (UL(3) << TCR_SH0_SHIFT)
-#define TCR_SH0_INNER (UL(3) << TCR_SH0_SHIFT)
-
-#define TCR_SH1_SHIFT 28
-#define TCR_SH1_MASK (UL(3) << TCR_SH1_SHIFT)
-#define TCR_SH1_INNER (UL(3) << TCR_SH1_SHIFT)
-#define TCR_SHARED (TCR_SH0_INNER | TCR_SH1_INNER)
-
-#define TCR_TG0_SHIFT 14
-#define TCR_TG0_MASK (UL(3) << TCR_TG0_SHIFT)
-#define TCR_TG0_4K (UL(0) << TCR_TG0_SHIFT)
-#define TCR_TG0_64K (UL(1) << TCR_TG0_SHIFT)
-#define TCR_TG0_16K (UL(2) << TCR_TG0_SHIFT)
-
-#define TCR_TG1_SHIFT 30
-#define TCR_TG1_MASK (UL(3) << TCR_TG1_SHIFT)
-#define TCR_TG1_16K (UL(1) << TCR_TG1_SHIFT)
-#define TCR_TG1_4K (UL(2) << TCR_TG1_SHIFT)
-#define TCR_TG1_64K (UL(3) << TCR_TG1_SHIFT)
-
-#define TCR_IPS_SHIFT 32
-#define TCR_IPS_MASK (UL(7) << TCR_IPS_SHIFT)
-#define TCR_A1 (UL(1) << 22)
-#define TCR_ASID16 (UL(1) << 36)
-#define TCR_TBI0 (UL(1) << 37)
-#define TCR_TBI1 (UL(1) << 38)
-#define TCR_HA (UL(1) << 39)
-#define TCR_HD (UL(1) << 40)
-#define TCR_HPD0_SHIFT 41
-#define TCR_HPD0 (UL(1) << TCR_HPD0_SHIFT)
-#define TCR_HPD1_SHIFT 42
-#define TCR_HPD1 (UL(1) << TCR_HPD1_SHIFT)
-#define TCR_TBID0 (UL(1) << 51)
-#define TCR_TBID1 (UL(1) << 52)
-#define TCR_NFD0 (UL(1) << 53)
-#define TCR_NFD1 (UL(1) << 54)
-#define TCR_E0PD0 (UL(1) << 55)
-#define TCR_E0PD1 (UL(1) << 56)
-#define TCR_TCMA0 (UL(1) << 57)
-#define TCR_TCMA1 (UL(1) << 58)
-#define TCR_DS (UL(1) << 59)
+#define TCR_T0SZ(x) ((UL(64) - (x)) << TCR_EL1_T0SZ_SHIFT)
+#define TCR_T1SZ(x) ((UL(64) - (x)) << TCR_EL1_T1SZ_SHIFT)
+
+#define TCR_T0SZ_MASK TCR_EL1_T0SZ_MASK
+#define TCR_T1SZ_MASK TCR_EL1_T1SZ_MASK
+
+#define TCR_EPD0_MASK TCR_EL1_EPD0_MASK
+#define TCR_EPD1_MASK TCR_EL1_EPD1_MASK
+
+#define TCR_IRGN0_MASK TCR_EL1_IRGN0_MASK
+#define TCR_IRGN0_WBWA (TCR_EL1_IRGN0_WBWA << TCR_EL1_IRGN0_SHIFT)
+
+#define TCR_ORGN0_MASK TCR_EL1_ORGN0_MASK
+#define TCR_ORGN0_WBWA (TCR_EL1_ORGN0_WBWA << TCR_EL1_ORGN0_SHIFT)
+
+#define TCR_SH0_MASK TCR_EL1_SH0_MASK
+#define TCR_SH0_INNER (TCR_EL1_SH0_INNER << TCR_EL1_SH0_SHIFT)
+
+#define TCR_SH1_MASK TCR_EL1_SH1_MASK
+
+#define TCR_TG0_SHIFT TCR_EL1_TG0_SHIFT
+#define TCR_TG0_MASK TCR_EL1_TG0_MASK
+#define TCR_TG0_4K (TCR_EL1_TG0_4K << TCR_EL1_TG0_SHIFT)
+#define TCR_TG0_64K (TCR_EL1_TG0_64K << TCR_EL1_TG0_SHIFT)
+#define TCR_TG0_16K (TCR_EL1_TG0_16K << TCR_EL1_TG0_SHIFT)
+
+#define TCR_TG1_SHIFT TCR_EL1_TG1_SHIFT
+#define TCR_TG1_MASK TCR_EL1_TG1_MASK
+#define TCR_TG1_16K (TCR_EL1_TG1_16K << TCR_EL1_TG1_SHIFT)
+#define TCR_TG1_4K (TCR_EL1_TG1_4K << TCR_EL1_TG1_SHIFT)
+#define TCR_TG1_64K (TCR_EL1_TG1_64K << TCR_EL1_TG1_SHIFT)
+
+#define TCR_IPS_SHIFT TCR_EL1_IPS_SHIFT
+#define TCR_IPS_MASK TCR_EL1_IPS_MASK
+#define TCR_A1 TCR_EL1_A1
+#define TCR_ASID16 TCR_EL1_AS
+#define TCR_TBI0 TCR_EL1_TBI0
+#define TCR_TBI1 TCR_EL1_TBI1
+#define TCR_HA TCR_EL1_HA
+#define TCR_HD TCR_EL1_HD
+#define TCR_HPD0 TCR_EL1_HPD0
+#define TCR_HPD1 TCR_EL1_HPD1
+#define TCR_TBID0 TCR_EL1_TBID0
+#define TCR_TBID1 TCR_EL1_TBID1
+#define TCR_E0PD0 TCR_EL1_E0PD0
+#define TCR_E0PD1 TCR_EL1_E0PD1
+#define TCR_DS TCR_EL1_DS
/*
* TTBR.
diff --git a/arch/arm64/include/asm/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h
index 85dceb1c66f4..161e8660eddd 100644
--- a/arch/arm64/include/asm/pgtable-prot.h
+++ b/arch/arm64/include/asm/pgtable-prot.h
@@ -62,7 +62,7 @@
#define _PAGE_READONLY_EXEC (_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)
#define _PAGE_EXECONLY (_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/cpufeature.h>
#include <asm/pgtable-types.h>
@@ -84,7 +84,7 @@ extern unsigned long prot_ns_shared;
#else
static inline bool __pure lpa2_is_enabled(void)
{
- return read_tcr() & TCR_DS;
+ return read_tcr() & TCR_EL1_DS;
}
#define PTE_MAYBE_SHARED (lpa2_is_enabled() ? 0 : PTE_SHARED)
@@ -127,7 +127,7 @@ static inline bool __pure lpa2_is_enabled(void)
#define PAGE_READONLY_EXEC __pgprot(_PAGE_READONLY_EXEC)
#define PAGE_EXECONLY __pgprot(_PAGE_EXECONLY)
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#define pte_pi_index(pte) ( \
((pte & BIT(PTE_PI_IDX_3)) >> (PTE_PI_IDX_3 - 3)) | \
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index aa89c2e67ebc..64d5f1d9cce9 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -30,7 +30,7 @@
#define vmemmap ((struct page *)VMEMMAP_START - (memstart_addr >> PAGE_SHIFT))
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/cmpxchg.h>
#include <asm/fixmap.h>
@@ -130,12 +130,16 @@ static inline void arch_leave_lazy_mmu_mode(void)
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
/*
- * Outside of a few very special situations (e.g. hibernation), we always
- * use broadcast TLB invalidation instructions, therefore a spurious page
- * fault on one CPU which has been handled concurrently by another CPU
- * does not need to perform additional invalidation.
+ * We use local TLB invalidation instruction when reusing page in
+ * write protection fault handler to avoid TLBI broadcast in the hot
+ * path. This will cause spurious page faults if stale read-only TLB
+ * entries exist.
*/
-#define flush_tlb_fix_spurious_fault(vma, address, ptep) do { } while (0)
+#define flush_tlb_fix_spurious_fault(vma, address, ptep) \
+ local_flush_tlb_page_nonotify(vma, address)
+
+#define flush_tlb_fix_spurious_fault_pmd(vma, address, pmdp) \
+ local_flush_tlb_page_nonotify(vma, address)
/*
* ZERO_PAGE is a global shared page that is always zero: used
@@ -293,7 +297,8 @@ static inline pmd_t set_pmd_bit(pmd_t pmd, pgprot_t prot)
static inline pte_t pte_mkwrite_novma(pte_t pte)
{
pte = set_pte_bit(pte, __pgprot(PTE_WRITE));
- pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY));
+ if (pte_sw_dirty(pte))
+ pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY));
return pte;
}
@@ -432,7 +437,7 @@ bool pgattr_change_is_safe(pteval_t old, pteval_t new);
* 1 0 | 1 0 1
* 1 1 | 0 1 x
*
- * When hardware DBM is not present, the sofware PTE_DIRTY bit is updated via
+ * When hardware DBM is not present, the software PTE_DIRTY bit is updated via
* the page fault mechanism. Checking the dirty status of a pte becomes:
*
* PTE_DIRTY || (PTE_WRITE && !PTE_RDONLY)
@@ -598,7 +603,7 @@ static inline int pte_protnone(pte_t pte)
/*
* pte_present_invalid() tells us that the pte is invalid from HW
* perspective but present from SW perspective, so the fields are to be
- * interpretted as per the HW layout. The second 2 checks are the unique
+ * interpreted as per the HW layout. The second 2 checks are the unique
* encoding that we use for PROT_NONE. It is insufficient to only use
* the first check because we share the same encoding scheme with pmds
* which support pmd_mkinvalid(), so can be present-invalid without
@@ -1948,6 +1953,6 @@ static inline void clear_young_dirty_ptes(struct vm_area_struct *vma,
#endif /* CONFIG_ARM64_CONTPTE */
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* __ASM_PGTABLE_H */
diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
index 0d5d1f0525eb..ab78a78821a2 100644
--- a/arch/arm64/include/asm/proc-fns.h
+++ b/arch/arm64/include/asm/proc-fns.h
@@ -9,7 +9,7 @@
#ifndef __ASM_PROCFNS_H
#define __ASM_PROCFNS_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/page.h>
@@ -21,5 +21,5 @@ extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
#include <asm/memory.h>
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_PROCFNS_H */
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 61d62bfd5a7b..e30c4c8e3a7a 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -25,7 +25,7 @@
#define MTE_CTRL_STORE_ONLY (1UL << 19)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/build_bug.h>
#include <linux/cache.h>
@@ -172,7 +172,12 @@ struct thread_struct {
unsigned long fault_code; /* ESR_EL1 value */
struct debug_info debug; /* debugging */
- struct user_fpsimd_state kernel_fpsimd_state;
+ /*
+ * Set [cleared] by kernel_neon_begin() [kernel_neon_end()] to the
+ * address of a caller provided buffer that will be used to preserve a
+ * task's kernel mode FPSIMD state while it is scheduled out.
+ */
+ struct user_fpsimd_state *kernel_fpsimd_state;
unsigned int kernel_fpsimd_cpu;
#ifdef CONFIG_ARM64_PTR_AUTH
struct ptrauth_keys_user keys_user;
@@ -437,5 +442,5 @@ int set_tsc_mode(unsigned int val);
#define GET_TSC_CTL(adr) get_tsc_mode((adr))
#define SET_TSC_CTL(val) set_tsc_mode((val))
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index 65b053a24d82..39582511ad72 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -94,7 +94,7 @@
*/
#define NO_SYSCALL (-1)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/bug.h>
#include <linux/types.h>
@@ -361,5 +361,5 @@ static inline void procedure_link_pointer_set(struct pt_regs *regs,
extern unsigned long profile_pc(struct pt_regs *regs);
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/rsi_smc.h b/arch/arm64/include/asm/rsi_smc.h
index 6cb070eca9e9..e19253f96c94 100644
--- a/arch/arm64/include/asm/rsi_smc.h
+++ b/arch/arm64/include/asm/rsi_smc.h
@@ -122,7 +122,7 @@
*/
#define SMC_RSI_ATTESTATION_TOKEN_CONTINUE SMC_RSI_FID(0x195)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
struct realm_config {
union {
@@ -142,7 +142,7 @@ struct realm_config {
*/
} __aligned(0x1000);
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
/*
* Read configuration for the current Realm.
diff --git a/arch/arm64/include/asm/rwonce.h b/arch/arm64/include/asm/rwonce.h
index 97d9256d33c9..78beceec10cd 100644
--- a/arch/arm64/include/asm/rwonce.h
+++ b/arch/arm64/include/asm/rwonce.h
@@ -5,7 +5,7 @@
#ifndef __ASM_RWONCE_H
#define __ASM_RWONCE_H
-#if defined(CONFIG_LTO) && !defined(__ASSEMBLY__)
+#if defined(CONFIG_LTO) && !defined(__ASSEMBLER__)
#include <linux/compiler_types.h>
#include <asm/alternative-macros.h>
@@ -62,7 +62,7 @@
})
#endif /* !BUILD_VDSO */
-#endif /* CONFIG_LTO && !__ASSEMBLY__ */
+#endif /* CONFIG_LTO && !__ASSEMBLER__ */
#include <asm-generic/rwonce.h>
diff --git a/arch/arm64/include/asm/scs.h b/arch/arm64/include/asm/scs.h
index a76f9b387a26..0fbc2e7867d3 100644
--- a/arch/arm64/include/asm/scs.h
+++ b/arch/arm64/include/asm/scs.h
@@ -2,7 +2,7 @@
#ifndef _ASM_SCS_H
#define _ASM_SCS_H
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#include <asm/asm-offsets.h>
#include <asm/sysreg.h>
@@ -53,8 +53,8 @@ enum {
EDYNSCS_INVALID_CFA_OPCODE = 4,
};
-int __pi_scs_patch(const u8 eh_frame[], int size);
+int __pi_scs_patch(const u8 eh_frame[], int size, bool skip_dry_run);
-#endif /* __ASSEMBLY __ */
+#endif /* __ASSEMBLER__ */
#endif /* _ASM_SCS_H */
diff --git a/arch/arm64/include/asm/sdei.h b/arch/arm64/include/asm/sdei.h
index 484cb6972e99..b2248bd3cb58 100644
--- a/arch/arm64/include/asm/sdei.h
+++ b/arch/arm64/include/asm/sdei.h
@@ -9,7 +9,7 @@
#define SDEI_STACK_SIZE IRQ_STACK_SIZE
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/linkage.h>
#include <linux/preempt.h>
@@ -49,5 +49,5 @@ unsigned long do_sdei_event(struct pt_regs *regs,
unsigned long sdei_arch_get_entry_point(int conduit);
#define sdei_arch_get_entry_point(x) sdei_arch_get_entry_point(x)
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_SDEI_H */
diff --git a/arch/arm64/include/asm/simd.h b/arch/arm64/include/asm/simd.h
index 8e86c9e70e48..0941f6f58a14 100644
--- a/arch/arm64/include/asm/simd.h
+++ b/arch/arm64/include/asm/simd.h
@@ -6,12 +6,15 @@
#ifndef __ASM_SIMD_H
#define __ASM_SIMD_H
+#include <linux/cleanup.h>
#include <linux/compiler.h>
#include <linux/irqflags.h>
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/types.h>
+#include <asm/neon.h>
+
#ifdef CONFIG_KERNEL_MODE_NEON
/*
@@ -29,7 +32,7 @@ static __must_check inline bool may_use_simd(void)
*/
return !WARN_ON(!system_capabilities_finalized()) &&
system_supports_fpsimd() &&
- !in_hardirq() && !irqs_disabled() && !in_nmi();
+ !in_hardirq() && !in_nmi();
}
#else /* ! CONFIG_KERNEL_MODE_NEON */
@@ -40,4 +43,11 @@ static __must_check inline bool may_use_simd(void) {
#endif /* ! CONFIG_KERNEL_MODE_NEON */
+DEFINE_LOCK_GUARD_1(ksimd,
+ struct user_fpsimd_state,
+ kernel_neon_begin(_T->lock),
+ kernel_neon_end(_T->lock))
+
+#define scoped_ksimd() scoped_guard(ksimd, &(struct user_fpsimd_state){})
+
#endif
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
index d48ef6d5abcc..10ea4f543069 100644
--- a/arch/arm64/include/asm/smp.h
+++ b/arch/arm64/include/asm/smp.h
@@ -23,7 +23,7 @@
#define CPU_STUCK_REASON_52_BIT_VA (UL(1) << CPU_STUCK_REASON_SHIFT)
#define CPU_STUCK_REASON_NO_GRAN (UL(2) << CPU_STUCK_REASON_SHIFT)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/threads.h>
#include <linux/cpumask.h>
@@ -155,6 +155,6 @@ bool cpus_are_stuck_in_kernel(void);
extern void crash_smp_send_stop(void);
extern bool smp_crash_stop_failed(void);
-#endif /* ifndef __ASSEMBLY__ */
+#endif /* ifndef __ASSEMBLER__ */
#endif /* ifndef __ASM_SMP_H */
diff --git a/arch/arm64/include/asm/spectre.h b/arch/arm64/include/asm/spectre.h
index 8fef12626090..296ae3420bfd 100644
--- a/arch/arm64/include/asm/spectre.h
+++ b/arch/arm64/include/asm/spectre.h
@@ -12,7 +12,7 @@
#define BP_HARDEN_EL2_SLOTS 4
#define __BP_HARDEN_HYP_VECS_SZ ((BP_HARDEN_EL2_SLOTS - 1) * SZ_2K)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/smp.h>
#include <asm/percpu.h>
@@ -117,6 +117,7 @@ void spectre_bhb_patch_wa3(struct alt_instr *alt,
__le32 *origptr, __le32 *updptr, int nr_inst);
void spectre_bhb_patch_clearbhb(struct alt_instr *alt,
__le32 *origptr, __le32 *updptr, int nr_inst);
+void spectre_print_disabled_mitigations(void);
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_SPECTRE_H */
diff --git a/arch/arm64/include/asm/stacktrace/frame.h b/arch/arm64/include/asm/stacktrace/frame.h
index 0ee0f6ba0fd8..796797b8db7e 100644
--- a/arch/arm64/include/asm/stacktrace/frame.h
+++ b/arch/arm64/include/asm/stacktrace/frame.h
@@ -25,7 +25,7 @@
#define FRAME_META_TYPE_FINAL 1
#define FRAME_META_TYPE_PT_REGS 2
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
/*
* A standard AAPCS64 frame record.
*/
@@ -43,6 +43,6 @@ struct frame_record_meta {
struct frame_record record;
u64 type;
};
-#endif /* __ASSEMBLY */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_STACKTRACE_FRAME_H */
diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h
index 0cde2f473971..e65f33edf9d6 100644
--- a/arch/arm64/include/asm/suspend.h
+++ b/arch/arm64/include/asm/suspend.h
@@ -23,7 +23,7 @@ struct cpu_suspend_ctx {
* __cpu_suspend_enter()'s caller, and populated by __cpu_suspend_enter().
* This data must survive until cpu_resume() is called.
*
- * This struct desribes the size and the layout of the saved cpu state.
+ * This struct describes the size and the layout of the saved cpu state.
* The layout of the callee_saved_regs is defined by the implementation
* of __cpu_suspend_enter(), and cpu_resume(). This struct must be passed
* in by the caller as __cpu_suspend_enter()'s stack-frame is gone once it
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index c231d2a3e515..9df51accbb02 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -52,7 +52,7 @@
#ifndef CONFIG_BROKEN_GAS_INST
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
// The space separator is omitted so that __emit_inst(x) can be parsed as
// either an assembler directive or an assembler macro argument.
#define __emit_inst(x) .inst(x)
@@ -71,11 +71,11 @@
(((x) >> 24) & 0x000000ff))
#endif /* CONFIG_CPU_BIG_ENDIAN */
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
#define __emit_inst(x) .long __INSTR_BSWAP(x)
-#else /* __ASSEMBLY__ */
+#else /* __ASSEMBLER__ */
#define __emit_inst(x) ".long " __stringify(__INSTR_BSWAP(x)) "\n\t"
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* CONFIG_BROKEN_GAS_INST */
@@ -1129,9 +1129,7 @@
#define gicr_insn(insn) read_sysreg_s(GICV5_OP_GICR_##insn)
#define gic_insn(v, insn) write_sysreg_s(v, GICV5_OP_GIC_##insn)
-#define ARM64_FEATURE_FIELD_BITS 4
-
-#ifdef __ASSEMBLY__
+#ifdef __ASSEMBLER__
.macro mrs_s, rt, sreg
__emit_inst(0xd5200000|(\sreg)|(.L__gpr_num_\rt))
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
index 344b1c1a4bbb..d316a804eb38 100644
--- a/arch/arm64/include/asm/system_misc.h
+++ b/arch/arm64/include/asm/system_misc.h
@@ -7,7 +7,7 @@
#ifndef __ASM_SYSTEM_MISC_H
#define __ASM_SYSTEM_MISC_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/compiler.h>
#include <linux/linkage.h>
@@ -28,6 +28,6 @@ void arm64_notify_die(const char *str, struct pt_regs *regs,
struct mm_struct;
extern void __show_regs(struct pt_regs *);
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_SYSTEM_MISC_H */
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index f241b8601ebd..a803b887b0b4 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -10,7 +10,7 @@
#include <linux/compiler.h>
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
struct task_struct;
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index 18a5dc0c9a54..a2d65d7d6aae 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -8,7 +8,7 @@
#ifndef __ASM_TLBFLUSH_H
#define __ASM_TLBFLUSH_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/bitfield.h>
#include <linux/mm_types.h>
@@ -249,6 +249,19 @@ static inline unsigned long get_trans_granule(void)
* cannot be easily determined, the value TLBI_TTL_UNKNOWN will
* perform a non-hinted invalidation.
*
+ * local_flush_tlb_page(vma, addr)
+ * Local variant of flush_tlb_page(). Stale TLB entries may
+ * remain in remote CPUs.
+ *
+ * local_flush_tlb_page_nonotify(vma, addr)
+ * Same as local_flush_tlb_page() except MMU notifier will not be
+ * called.
+ *
+ * local_flush_tlb_contpte(vma, addr)
+ * Invalidate the virtual-address range
+ * '[addr, addr+CONT_PTE_SIZE)' mapped with contpte on local CPU
+ * for the user address space corresponding to 'vma->mm'. Stale
+ * TLB entries may remain in remote CPUs.
*
* Finally, take a look at asm/tlb.h to see how tlb_flush() is implemented
* on top of these routines, since that is our interface to the mmu_gather
@@ -282,6 +295,33 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
}
+static inline void __local_flush_tlb_page_nonotify_nosync(struct mm_struct *mm,
+ unsigned long uaddr)
+{
+ unsigned long addr;
+
+ dsb(nshst);
+ addr = __TLBI_VADDR(uaddr, ASID(mm));
+ __tlbi(vale1, addr);
+ __tlbi_user(vale1, addr);
+}
+
+static inline void local_flush_tlb_page_nonotify(struct vm_area_struct *vma,
+ unsigned long uaddr)
+{
+ __local_flush_tlb_page_nonotify_nosync(vma->vm_mm, uaddr);
+ dsb(nsh);
+}
+
+static inline void local_flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long uaddr)
+{
+ __local_flush_tlb_page_nonotify_nosync(vma->vm_mm, uaddr);
+ mmu_notifier_arch_invalidate_secondary_tlbs(vma->vm_mm, uaddr & PAGE_MASK,
+ (uaddr & PAGE_MASK) + PAGE_SIZE);
+ dsb(nsh);
+}
+
static inline void __flush_tlb_page_nosync(struct mm_struct *mm,
unsigned long uaddr)
{
@@ -472,6 +512,22 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
dsb(ish);
}
+static inline void local_flush_tlb_contpte(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ unsigned long asid;
+
+ addr = round_down(addr, CONT_PTE_SIZE);
+
+ dsb(nshst);
+ asid = ASID(vma->vm_mm);
+ __flush_tlb_range_op(vale1, addr, CONT_PTES, PAGE_SIZE, asid,
+ 3, true, lpa2_is_enabled());
+ mmu_notifier_arch_invalidate_secondary_tlbs(vma->vm_mm, addr,
+ addr + CONT_PTE_SIZE);
+ dsb(nsh);
+}
+
static inline void flush_tlb_range(struct vm_area_struct *vma,
unsigned long start, unsigned long end)
{
@@ -524,6 +580,33 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b
{
__flush_tlb_range_nosync(mm, start, end, PAGE_SIZE, true, 3);
}
+
+static inline bool __pte_flags_need_flush(ptdesc_t oldval, ptdesc_t newval)
+{
+ ptdesc_t diff = oldval ^ newval;
+
+ /* invalid to valid transition requires no flush */
+ if (!(oldval & PTE_VALID))
+ return false;
+
+ /* Transition in the SW bits requires no flush */
+ diff &= ~PTE_SWBITS_MASK;
+
+ return diff;
+}
+
+static inline bool pte_needs_flush(pte_t oldpte, pte_t newpte)
+{
+ return __pte_flags_need_flush(pte_val(oldpte), pte_val(newpte));
+}
+#define pte_needs_flush pte_needs_flush
+
+static inline bool huge_pmd_needs_flush(pmd_t oldpmd, pmd_t newpmd)
+{
+ return __pte_flags_need_flush(pmd_val(oldpmd), pmd_val(newpmd));
+}
+#define huge_pmd_needs_flush huge_pmd_needs_flush
+
#endif
#endif
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 1aa4ecb73429..6490930deef8 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -422,9 +422,9 @@ static __must_check __always_inline bool user_access_begin(const void __user *pt
}
#define user_access_begin(a,b) user_access_begin(a,b)
#define user_access_end() uaccess_ttbr0_disable()
-#define unsafe_put_user(x, ptr, label) \
+#define arch_unsafe_put_user(x, ptr, label) \
__raw_put_mem("sttr", x, uaccess_mask_ptr(ptr), label, U)
-#define unsafe_get_user(x, ptr, label) \
+#define arch_unsafe_get_user(x, ptr, label) \
__raw_get_mem("ldtr", x, uaccess_mask_ptr(ptr), label, U)
/*
diff --git a/arch/arm64/include/asm/vdso.h b/arch/arm64/include/asm/vdso.h
index 61679070f595..232b46969088 100644
--- a/arch/arm64/include/asm/vdso.h
+++ b/arch/arm64/include/asm/vdso.h
@@ -7,7 +7,7 @@
#define __VDSO_PAGES 4
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <generated/vdso-offsets.h>
@@ -19,6 +19,6 @@
extern char vdso_start[], vdso_end[];
extern char vdso32_start[], vdso32_end[];
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* __ASM_VDSO_H */
diff --git a/arch/arm64/include/asm/vdso/compat_barrier.h b/arch/arm64/include/asm/vdso/compat_barrier.h
index 6d75e03d3827..d7ebe7ceefa0 100644
--- a/arch/arm64/include/asm/vdso/compat_barrier.h
+++ b/arch/arm64/include/asm/vdso/compat_barrier.h
@@ -5,7 +5,7 @@
#ifndef __COMPAT_BARRIER_H
#define __COMPAT_BARRIER_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
/*
* Warning: This code is meant to be used from the compat vDSO only.
*/
@@ -31,6 +31,6 @@
#define smp_rmb() aarch32_smp_rmb()
#define smp_wmb() aarch32_smp_wmb()
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* __COMPAT_BARRIER_H */
diff --git a/arch/arm64/include/asm/vdso/compat_gettimeofday.h b/arch/arm64/include/asm/vdso/compat_gettimeofday.h
index 7d1a116549b1..0d513f924321 100644
--- a/arch/arm64/include/asm/vdso/compat_gettimeofday.h
+++ b/arch/arm64/include/asm/vdso/compat_gettimeofday.h
@@ -5,7 +5,7 @@
#ifndef __ASM_VDSO_COMPAT_GETTIMEOFDAY_H
#define __ASM_VDSO_COMPAT_GETTIMEOFDAY_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/barrier.h>
#include <asm/unistd_compat_32.h>
@@ -161,6 +161,6 @@ static inline bool vdso_clocksource_ok(const struct vdso_clock *vc)
}
#define vdso_clocksource_ok vdso_clocksource_ok
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* __ASM_VDSO_COMPAT_GETTIMEOFDAY_H */
diff --git a/arch/arm64/include/asm/vdso/getrandom.h b/arch/arm64/include/asm/vdso/getrandom.h
index a2197da1951b..da1d58bbfabe 100644
--- a/arch/arm64/include/asm/vdso/getrandom.h
+++ b/arch/arm64/include/asm/vdso/getrandom.h
@@ -3,7 +3,7 @@
#ifndef __ASM_VDSO_GETRANDOM_H
#define __ASM_VDSO_GETRANDOM_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/unistd.h>
#include <asm/vdso/vsyscall.h>
@@ -33,6 +33,6 @@ static __always_inline ssize_t getrandom_syscall(void *_buffer, size_t _len, uns
return ret;
}
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* __ASM_VDSO_GETRANDOM_H */
diff --git a/arch/arm64/include/asm/vdso/gettimeofday.h b/arch/arm64/include/asm/vdso/gettimeofday.h
index c59e84105b43..3658a757e255 100644
--- a/arch/arm64/include/asm/vdso/gettimeofday.h
+++ b/arch/arm64/include/asm/vdso/gettimeofday.h
@@ -7,7 +7,7 @@
#ifdef __aarch64__
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/alternative.h>
#include <asm/arch_timer.h>
@@ -96,7 +96,7 @@ static __always_inline const struct vdso_time_data *__arch_get_vdso_u_time_data(
#define __arch_get_vdso_u_time_data __arch_get_vdso_u_time_data
#endif /* IS_ENABLED(CONFIG_CC_IS_GCC) && IS_ENABLED(CONFIG_PAGE_SIZE_64KB) */
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#else /* !__aarch64__ */
diff --git a/arch/arm64/include/asm/vdso/processor.h b/arch/arm64/include/asm/vdso/processor.h
index ff830b766ad2..7abb0cc81cd6 100644
--- a/arch/arm64/include/asm/vdso/processor.h
+++ b/arch/arm64/include/asm/vdso/processor.h
@@ -5,13 +5,13 @@
#ifndef __ASM_VDSO_PROCESSOR_H
#define __ASM_VDSO_PROCESSOR_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
static inline void cpu_relax(void)
{
asm volatile("yield" ::: "memory");
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* __ASM_VDSO_PROCESSOR_H */
diff --git a/arch/arm64/include/asm/vdso/vsyscall.h b/arch/arm64/include/asm/vdso/vsyscall.h
index 417aae5763a8..3f3c8eb74e2e 100644
--- a/arch/arm64/include/asm/vdso/vsyscall.h
+++ b/arch/arm64/include/asm/vdso/vsyscall.h
@@ -2,7 +2,7 @@
#ifndef __ASM_VDSO_VSYSCALL_H
#define __ASM_VDSO_VSYSCALL_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <vdso/datapage.h>
@@ -22,6 +22,6 @@ void __arch_update_vdso_clock(struct vdso_clock *vc)
/* The asm-generic header needs to be included after the definitions above */
#include <asm-generic/vdso/vsyscall.h>
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#endif /* __ASM_VDSO_VSYSCALL_H */
diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h
index aa280f356b96..530af9620fdb 100644
--- a/arch/arm64/include/asm/virt.h
+++ b/arch/arm64/include/asm/virt.h
@@ -56,7 +56,7 @@
*/
#define BOOT_CPU_FLAG_E2H BIT_ULL(32)
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <asm/ptrace.h>
#include <asm/sections.h>
@@ -161,6 +161,6 @@ static inline bool is_hyp_nvhe(void)
return is_hyp_mode_available() && !is_kernel_in_hyp_mode();
}
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* ! __ASM__VIRT_H */
diff --git a/arch/arm64/include/asm/vmap_stack.h b/arch/arm64/include/asm/vmap_stack.h
index 20873099c035..75daee1a07e9 100644
--- a/arch/arm64/include/asm/vmap_stack.h
+++ b/arch/arm64/include/asm/vmap_stack.h
@@ -3,9 +3,7 @@
#ifndef __ASM_VMAP_STACK_H
#define __ASM_VMAP_STACK_H
-#include <linux/bug.h>
#include <linux/gfp.h>
-#include <linux/kconfig.h>
#include <linux/vmalloc.h>
#include <linux/pgtable.h>
#include <asm/memory.h>
@@ -19,8 +17,6 @@ static inline unsigned long *arch_alloc_vmap_stack(size_t stack_size, int node)
{
void *p;
- BUILD_BUG_ON(!IS_ENABLED(CONFIG_VMAP_STACK));
-
p = __vmalloc_node(stack_size, THREAD_ALIGN, THREADINFO_GFP, node,
__builtin_return_address(0));
return kasan_reset_tag(p);
diff --git a/arch/arm64/include/asm/xor.h b/arch/arm64/include/asm/xor.h
index befcd8a7abc9..c38e3d017a79 100644
--- a/arch/arm64/include/asm/xor.h
+++ b/arch/arm64/include/asm/xor.h
@@ -9,7 +9,7 @@
#include <linux/hardirq.h>
#include <asm-generic/xor.h>
#include <asm/hwcap.h>
-#include <asm/neon.h>
+#include <asm/simd.h>
#ifdef CONFIG_KERNEL_MODE_NEON
@@ -19,9 +19,8 @@ static void
xor_neon_2(unsigned long bytes, unsigned long * __restrict p1,
const unsigned long * __restrict p2)
{
- kernel_neon_begin();
- xor_block_inner_neon.do_2(bytes, p1, p2);
- kernel_neon_end();
+ scoped_ksimd()
+ xor_block_inner_neon.do_2(bytes, p1, p2);
}
static void
@@ -29,9 +28,8 @@ xor_neon_3(unsigned long bytes, unsigned long * __restrict p1,
const unsigned long * __restrict p2,
const unsigned long * __restrict p3)
{
- kernel_neon_begin();
- xor_block_inner_neon.do_3(bytes, p1, p2, p3);
- kernel_neon_end();
+ scoped_ksimd()
+ xor_block_inner_neon.do_3(bytes, p1, p2, p3);
}
static void
@@ -40,9 +38,8 @@ xor_neon_4(unsigned long bytes, unsigned long * __restrict p1,
const unsigned long * __restrict p3,
const unsigned long * __restrict p4)
{
- kernel_neon_begin();
- xor_block_inner_neon.do_4(bytes, p1, p2, p3, p4);
- kernel_neon_end();
+ scoped_ksimd()
+ xor_block_inner_neon.do_4(bytes, p1, p2, p3, p4);
}
static void
@@ -52,9 +49,8 @@ xor_neon_5(unsigned long bytes, unsigned long * __restrict p1,
const unsigned long * __restrict p4,
const unsigned long * __restrict p5)
{
- kernel_neon_begin();
- xor_block_inner_neon.do_5(bytes, p1, p2, p3, p4, p5);
- kernel_neon_end();
+ scoped_ksimd()
+ xor_block_inner_neon.do_5(bytes, p1, p2, p3, p4, p5);
}
static struct xor_block_template xor_block_arm64 = {
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index ed5f3892674c..a792a599b9d6 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -31,7 +31,7 @@
#define KVM_SPSR_FIQ 4
#define KVM_NR_SPSR 5
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/psci.h>
#include <linux/types.h>
#include <asm/ptrace.h>
diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h
index 0f39ba4f3efd..6fed93fb2536 100644
--- a/arch/arm64/include/uapi/asm/ptrace.h
+++ b/arch/arm64/include/uapi/asm/ptrace.h
@@ -80,7 +80,7 @@
#define PTRACE_PEEKMTETAGS 33
#define PTRACE_POKEMTETAGS 34
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
/*
* User structures for general purpose, floating point and debug registers.
@@ -332,6 +332,6 @@ struct user_gcs {
__u64 gcspr_el0;
};
-#endif /* __ASSEMBLY__ */
+#endif /* __ASSEMBLER__ */
#endif /* _UAPI__ASM_PTRACE_H */
diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h
index d42f7a92238b..e29bf3e2d0cc 100644
--- a/arch/arm64/include/uapi/asm/sigcontext.h
+++ b/arch/arm64/include/uapi/asm/sigcontext.h
@@ -17,7 +17,7 @@
#ifndef _UAPI__ASM_SIGCONTEXT_H
#define _UAPI__ASM_SIGCONTEXT_H
-#ifndef __ASSEMBLY__
+#ifndef __ASSEMBLER__
#include <linux/types.h>
@@ -192,7 +192,7 @@ struct gcs_context {
__u64 reserved;
};
-#endif /* !__ASSEMBLY__ */
+#endif /* !__ASSEMBLER__ */
#include <asm/sve_context.h>
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 7aca29e1d30b..af90128cfed5 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -133,7 +133,7 @@ static int __init acpi_fadt_sanity_check(void)
/*
* FADT is required on arm64; retrieve it to check its presence
- * and carry out revision and ACPI HW reduced compliancy tests
+ * and carry out revision and ACPI HW reduced compliance tests
*/
status = acpi_get_table(ACPI_SIG_FADT, 0, &table);
if (ACPI_FAILURE(status)) {
@@ -197,8 +197,6 @@ out:
*/
void __init acpi_boot_table_init(void)
{
- int ret;
-
/*
* Enable ACPI instead of device tree unless
* - ACPI has been disabled explicitly (acpi=off), or
@@ -252,12 +250,8 @@ done:
* behaviour, use acpi=nospcr to disable console in ACPI SPCR
* table as default serial console.
*/
- ret = acpi_parse_spcr(earlycon_acpi_spcr_enable,
+ acpi_parse_spcr(earlycon_acpi_spcr_enable,
!param_acpi_nospcr);
- if (!ret || param_acpi_nospcr || !IS_ENABLED(CONFIG_ACPI_SPCR_TABLE))
- pr_info("Use ACPI SPCR as default console: No\n");
- else
- pr_info("Use ACPI SPCR as default console: Yes\n");
if (IS_ENABLED(CONFIG_ACPI_BGRT))
acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt);
@@ -357,16 +351,6 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
* as long as we take care not to create a writable
* mapping for executable code.
*/
- fallthrough;
-
- case EFI_ACPI_MEMORY_NVS:
- /*
- * ACPI NVS marks an area reserved for use by the
- * firmware, even after exiting the boot service.
- * This may be used by the firmware for sharing dynamic
- * tables/data (e.g., ACPI CCEL) with the OS. Map it
- * as read-only.
- */
prot = PAGE_KERNEL_RO;
break;
@@ -439,7 +423,7 @@ int apei_claim_sea(struct pt_regs *regs)
irq_work_run();
__irq_exit();
} else {
- pr_warn_ratelimited("APEI work queued but not completed");
+ pr_warn_ratelimited("APEI work queued but not completed\n");
err = -EINPROGRESS;
}
}
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 8ff6610af496..f5ec7e7c1d3f 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -139,9 +139,9 @@ static noinstr void clean_dcache_range_nopatch(u64 start, u64 end)
} while (cur += d_size, cur < end);
}
-static void __apply_alternatives(const struct alt_region *region,
- bool is_module,
- unsigned long *cpucap_mask)
+static int __apply_alternatives(const struct alt_region *region,
+ bool is_module,
+ unsigned long *cpucap_mask)
{
struct alt_instr *alt;
__le32 *origptr, *updptr;
@@ -166,10 +166,13 @@ static void __apply_alternatives(const struct alt_region *region,
updptr = is_module ? origptr : lm_alias(origptr);
nr_inst = alt->orig_len / AARCH64_INSN_SIZE;
- if (ALT_HAS_CB(alt))
+ if (ALT_HAS_CB(alt)) {
alt_cb = ALT_REPL_PTR(alt);
- else
+ if (is_module && !core_kernel_text((unsigned long)alt_cb))
+ return -ENOEXEC;
+ } else {
alt_cb = patch_alternative;
+ }
alt_cb(alt, origptr, updptr, nr_inst);
@@ -193,6 +196,8 @@ static void __apply_alternatives(const struct alt_region *region,
bitmap_and(applied_alternatives, applied_alternatives,
system_cpucaps, ARM64_NCAPS);
}
+
+ return 0;
}
static void __init apply_alternatives_vdso(void)
@@ -277,7 +282,7 @@ void __init apply_boot_alternatives(void)
}
#ifdef CONFIG_MODULES
-void apply_alternatives_module(void *start, size_t length)
+int apply_alternatives_module(void *start, size_t length)
{
struct alt_region region = {
.begin = start,
@@ -287,7 +292,7 @@ void apply_alternatives_module(void *start, size_t length)
bitmap_fill(all_capabilities, ARM64_NCAPS);
- __apply_alternatives(&region, true, &all_capabilities[0]);
+ return __apply_alternatives(&region, true, &all_capabilities[0]);
}
#endif
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 5ed401ff79e3..42b182cfa404 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -95,6 +95,7 @@
#include <asm/vectors.h>
#include <asm/virt.h>
+#include <asm/spectre.h>
/* Kernel representation of AT_HWCAP and AT_HWCAP2 */
static DECLARE_BITMAP(elf_hwcap, MAX_CPU_FEATURES) __read_mostly;
@@ -1002,7 +1003,7 @@ static void __init sort_ftr_regs(void)
/*
* Initialise the CPU feature register from Boot CPU values.
- * Also initiliases the strict_mask for the register.
+ * Also initialises the strict_mask for the register.
* Any bits that are not covered by an arm64_ftr_bits entry are considered
* RES0 for the system-wide value, and must strictly match.
*/
@@ -1969,7 +1970,7 @@ static struct cpumask dbm_cpus __read_mostly;
static inline void __cpu_enable_hw_dbm(void)
{
- u64 tcr = read_sysreg(tcr_el1) | TCR_HD;
+ u64 tcr = read_sysreg(tcr_el1) | TCR_EL1_HD;
write_sysreg(tcr, tcr_el1);
isb();
@@ -2255,7 +2256,7 @@ static bool has_generic_auth(const struct arm64_cpu_capabilities *entry,
static void cpu_enable_e0pd(struct arm64_cpu_capabilities const *cap)
{
if (this_cpu_has_cap(ARM64_HAS_E0PD))
- sysreg_clear_set(tcr_el1, 0, TCR_E0PD1);
+ sysreg_clear_set(tcr_el1, 0, TCR_EL1_E0PD1);
}
#endif /* CONFIG_ARM64_E0PD */
@@ -3875,6 +3876,11 @@ static void __init setup_system_capabilities(void)
*/
if (system_uses_ttbr0_pan())
pr_info("emulated: Privileged Access Never (PAN) using TTBR0_EL1 switching\n");
+
+ /*
+ * Report Spectre mitigations status.
+ */
+ spectre_print_disabled_mitigations();
}
void __init setup_system_features(void)
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index 6c371b158b99..a81cb4aa4738 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -10,6 +10,7 @@
#include <linux/efi.h>
#include <linux/init.h>
#include <linux/kmemleak.h>
+#include <linux/kthread.h>
#include <linux/screen_info.h>
#include <linux/vmalloc.h>
@@ -165,20 +166,53 @@ asmlinkage efi_status_t efi_handle_corrupted_x18(efi_status_t s, const char *f)
return s;
}
-static DEFINE_RAW_SPINLOCK(efi_rt_lock);
-
void arch_efi_call_virt_setup(void)
{
- efi_virtmap_load();
- raw_spin_lock(&efi_rt_lock);
+ efi_runtime_assert_lock_held();
+
+ if (preemptible() && (current->flags & PF_KTHREAD)) {
+ /*
+ * Disable migration to ensure that a preempted EFI runtime
+ * service call will be resumed on the same CPU. This avoids
+ * potential issues with EFI runtime calls that are preempted
+ * while polling for an asynchronous completion of a secure
+ * firmware call, which may not permit the CPU to change.
+ */
+ migrate_disable();
+ kthread_use_mm(&efi_mm);
+ } else {
+ efi_virtmap_load();
+ }
+
+ /*
+ * Enable access to the valid TTBR0_EL1 and invoke the errata
+ * workaround directly since there is no return from exception when
+ * invoking the EFI run-time services.
+ */
+ uaccess_ttbr0_enable();
+ post_ttbr_update_workaround();
+
__efi_fpsimd_begin();
}
void arch_efi_call_virt_teardown(void)
{
__efi_fpsimd_end();
- raw_spin_unlock(&efi_rt_lock);
- efi_virtmap_unload();
+
+ /*
+ * Defer the switch to the current thread's TTBR0_EL1 until
+ * uaccess_enable(). Do so before efi_virtmap_unload() updates the
+ * saved TTBR0 value, so the userland page tables are not activated
+ * inadvertently over the back of an exception.
+ */
+ uaccess_ttbr0_disable();
+
+ if (preemptible() && (current->flags & PF_KTHREAD)) {
+ kthread_unuse_mm(&efi_mm);
+ migrate_enable();
+ } else {
+ efi_virtmap_unload();
+ }
}
asmlinkage u64 *efi_rt_stack_top __ro_after_init;
diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
index a9c81715ce59..3625797e9ee8 100644
--- a/arch/arm64/kernel/entry-common.c
+++ b/arch/arm64/kernel/entry-common.c
@@ -34,20 +34,12 @@
* Handle IRQ/context state management when entering from kernel mode.
* Before this function is called it is not safe to call regular kernel code,
* instrumentable code, or any code which may trigger an exception.
- *
- * This is intended to match the logic in irqentry_enter(), handling the kernel
- * mode transitions only.
*/
-static __always_inline irqentry_state_t __enter_from_kernel_mode(struct pt_regs *regs)
-{
- return irqentry_enter(regs);
-}
-
static noinstr irqentry_state_t enter_from_kernel_mode(struct pt_regs *regs)
{
irqentry_state_t state;
- state = __enter_from_kernel_mode(regs);
+ state = irqentry_enter(regs);
mte_check_tfsr_entry();
mte_disable_tco_entry(current);
@@ -58,21 +50,12 @@ static noinstr irqentry_state_t enter_from_kernel_mode(struct pt_regs *regs)
* Handle IRQ/context state management when exiting to kernel mode.
* After this function returns it is not safe to call regular kernel code,
* instrumentable code, or any code which may trigger an exception.
- *
- * This is intended to match the logic in irqentry_exit(), handling the kernel
- * mode transitions only, and with preemption handled elsewhere.
*/
-static __always_inline void __exit_to_kernel_mode(struct pt_regs *regs,
- irqentry_state_t state)
-{
- irqentry_exit(regs, state);
-}
-
static void noinstr exit_to_kernel_mode(struct pt_regs *regs,
irqentry_state_t state)
{
mte_check_tfsr_exit();
- __exit_to_kernel_mode(regs, state);
+ irqentry_exit(regs, state);
}
/*
@@ -80,17 +63,12 @@ static void noinstr exit_to_kernel_mode(struct pt_regs *regs,
* Before this function is called it is not safe to call regular kernel code,
* instrumentable code, or any code which may trigger an exception.
*/
-static __always_inline void __enter_from_user_mode(struct pt_regs *regs)
+static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
{
enter_from_user_mode(regs);
mte_disable_tco_entry(current);
}
-static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
-{
- __enter_from_user_mode(regs);
-}
-
/*
* Handle IRQ/context state management when exiting to user mode.
* After this function returns it is not safe to call regular kernel code,
@@ -100,7 +78,7 @@ static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
{
local_irq_disable();
- exit_to_user_mode_prepare(regs);
+ exit_to_user_mode_prepare_legacy(regs);
local_daif_mask();
mte_check_tfsr_exit();
exit_to_user_mode();
diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S
index 169ccf600066..025140caafe7 100644
--- a/arch/arm64/kernel/entry-ftrace.S
+++ b/arch/arm64/kernel/entry-ftrace.S
@@ -94,7 +94,7 @@ SYM_CODE_START(ftrace_caller)
stp x29, x30, [sp, #FREGS_SIZE]
add x29, sp, #FREGS_SIZE
- /* Prepare arguments for the the tracer func */
+ /* Prepare arguments for the tracer func */
sub x0, x30, #AARCH64_INSN_SIZE // ip (callsite's BL insn)
mov x1, x9 // parent_ip (callsite's LR)
mov x3, sp // regs
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index e3f8f51748bc..c154f72634e0 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -225,10 +225,21 @@ static void fpsimd_bind_task_to_cpu(void);
*/
static void get_cpu_fpsimd_context(void)
{
- if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_bh_disable();
- else
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+ /*
+ * The softirq subsystem lacks a true unmask/mask API, and
+ * re-enabling softirq processing using local_bh_enable() will
+ * not only unmask softirqs, it will also result in immediate
+ * delivery of any pending softirqs.
+ * This is undesirable when running with IRQs disabled, but in
+ * that case, there is no need to mask softirqs in the first
+ * place, so only bother doing so when IRQs are enabled.
+ */
+ if (!irqs_disabled())
+ local_bh_disable();
+ } else {
preempt_disable();
+ }
}
/*
@@ -240,10 +251,12 @@ static void get_cpu_fpsimd_context(void)
*/
static void put_cpu_fpsimd_context(void)
{
- if (!IS_ENABLED(CONFIG_PREEMPT_RT))
- local_bh_enable();
- else
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
+ if (!irqs_disabled())
+ local_bh_enable();
+ } else {
preempt_enable();
+ }
}
unsigned int task_get_vl(const struct task_struct *task, enum vec_type type)
@@ -1489,21 +1502,23 @@ static void fpsimd_load_kernel_state(struct task_struct *task)
* Elide the load if this CPU holds the most recent kernel mode
* FPSIMD context of the current task.
*/
- if (last->st == &task->thread.kernel_fpsimd_state &&
+ if (last->st == task->thread.kernel_fpsimd_state &&
task->thread.kernel_fpsimd_cpu == smp_processor_id())
return;
- fpsimd_load_state(&task->thread.kernel_fpsimd_state);
+ fpsimd_load_state(task->thread.kernel_fpsimd_state);
}
static void fpsimd_save_kernel_state(struct task_struct *task)
{
struct cpu_fp_state cpu_fp_state = {
- .st = &task->thread.kernel_fpsimd_state,
+ .st = task->thread.kernel_fpsimd_state,
.to_save = FP_STATE_FPSIMD,
};
- fpsimd_save_state(&task->thread.kernel_fpsimd_state);
+ BUG_ON(!cpu_fp_state.st);
+
+ fpsimd_save_state(task->thread.kernel_fpsimd_state);
fpsimd_bind_state_to_cpu(&cpu_fp_state);
task->thread.kernel_fpsimd_cpu = smp_processor_id();
@@ -1774,6 +1789,7 @@ void fpsimd_update_current_state(struct user_fpsimd_state const *state)
void fpsimd_flush_task_state(struct task_struct *t)
{
t->thread.fpsimd_cpu = NR_CPUS;
+ t->thread.kernel_fpsimd_state = NULL;
/*
* If we don't support fpsimd, bail out after we have
* reset the fpsimd_cpu for this task and clear the
@@ -1833,12 +1849,19 @@ void fpsimd_save_and_flush_cpu_state(void)
*
* The caller may freely use the FPSIMD registers until kernel_neon_end() is
* called.
+ *
+ * Unless called from non-preemptible task context, @state must point to a
+ * caller provided buffer that will be used to preserve the task's kernel mode
+ * FPSIMD context when it is scheduled out, or if it is interrupted by kernel
+ * mode FPSIMD occurring in softirq context. May be %NULL otherwise.
*/
-void kernel_neon_begin(void)
+void kernel_neon_begin(struct user_fpsimd_state *state)
{
if (WARN_ON(!system_supports_fpsimd()))
return;
+ WARN_ON((preemptible() || in_serving_softirq()) && !state);
+
BUG_ON(!may_use_simd());
get_cpu_fpsimd_context();
@@ -1846,7 +1869,7 @@ void kernel_neon_begin(void)
/* Save unsaved fpsimd state, if any: */
if (test_thread_flag(TIF_KERNEL_FPSTATE)) {
BUG_ON(IS_ENABLED(CONFIG_PREEMPT_RT) || !in_serving_softirq());
- fpsimd_save_kernel_state(current);
+ fpsimd_save_state(state);
} else {
fpsimd_save_user_state();
@@ -1867,8 +1890,16 @@ void kernel_neon_begin(void)
* mode in task context. So in this case, setting the flag here
* is always appropriate.
*/
- if (IS_ENABLED(CONFIG_PREEMPT_RT) || !in_serving_softirq())
+ if (IS_ENABLED(CONFIG_PREEMPT_RT) || !in_serving_softirq()) {
+ /*
+ * Record the caller provided buffer as the kernel mode
+ * FP/SIMD buffer for this task, so that the state can
+ * be preserved and restored on a context switch.
+ */
+ WARN_ON(current->thread.kernel_fpsimd_state != NULL);
+ current->thread.kernel_fpsimd_state = state;
set_thread_flag(TIF_KERNEL_FPSTATE);
+ }
}
/* Invalidate any task state remaining in the fpsimd regs: */
@@ -1886,22 +1917,30 @@ EXPORT_SYMBOL_GPL(kernel_neon_begin);
*
* The caller must not use the FPSIMD registers after this function is called,
* unless kernel_neon_begin() is called again in the meantime.
+ *
+ * The value of @state must match the value passed to the preceding call to
+ * kernel_neon_begin().
*/
-void kernel_neon_end(void)
+void kernel_neon_end(struct user_fpsimd_state *state)
{
if (!system_supports_fpsimd())
return;
+ if (!test_thread_flag(TIF_KERNEL_FPSTATE))
+ return;
+
/*
* If we are returning from a nested use of kernel mode FPSIMD, restore
* the task context kernel mode FPSIMD state. This can only happen when
* running in softirq context on non-PREEMPT_RT.
*/
- if (!IS_ENABLED(CONFIG_PREEMPT_RT) && in_serving_softirq() &&
- test_thread_flag(TIF_KERNEL_FPSTATE))
- fpsimd_load_kernel_state(current);
- else
+ if (!IS_ENABLED(CONFIG_PREEMPT_RT) && in_serving_softirq()) {
+ fpsimd_load_state(state);
+ } else {
clear_thread_flag(TIF_KERNEL_FPSTATE);
+ WARN_ON(current->thread.kernel_fpsimd_state != state);
+ current->thread.kernel_fpsimd_state = NULL;
+ }
}
EXPORT_SYMBOL_GPL(kernel_neon_end);
@@ -1934,11 +1973,11 @@ void __efi_fpsimd_begin(void)
if (!system_supports_fpsimd())
return;
- WARN_ON(preemptible());
-
if (may_use_simd()) {
- kernel_neon_begin();
+ kernel_neon_begin(&efi_fpsimd_state);
} else {
+ WARN_ON(preemptible());
+
/*
* If !efi_sve_state, SVE can't be in use yet and doesn't need
* preserving:
@@ -1986,7 +2025,7 @@ void __efi_fpsimd_end(void)
return;
if (!efi_fpsimd_state_used) {
- kernel_neon_end();
+ kernel_neon_end(&efi_fpsimd_state);
} else {
if (system_supports_sve() && efi_sve_state_used) {
bool ffr = true;
diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c
index 5adad37ab4fa..5a1554a44162 100644
--- a/arch/arm64/kernel/ftrace.c
+++ b/arch/arm64/kernel/ftrace.c
@@ -492,7 +492,7 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
return ret;
/*
- * When using mcount, callsites in modules may have been initalized to
+ * When using mcount, callsites in modules may have been initialized to
* call an arbitrary module PLT (which redirects to the _mcount stub)
* rather than the ftrace PLT we'll use at runtime (which redirects to
* the ftrace trampoline). We can ignore the old PLT when initializing
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c
index c0065a1d77cf..15dedb385b9e 100644
--- a/arch/arm64/kernel/irq.c
+++ b/arch/arm64/kernel/irq.c
@@ -62,7 +62,7 @@ static void __init init_irq_stacks(void)
}
}
-#ifndef CONFIG_PREEMPT_RT
+#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
static void ____do_softirq(struct pt_regs *regs)
{
__do_softirq();
diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c
index 6f121a0164a4..239c16e3d02f 100644
--- a/arch/arm64/kernel/machine_kexec.c
+++ b/arch/arm64/kernel/machine_kexec.c
@@ -251,7 +251,7 @@ void crash_post_resume(void)
* marked as Reserved as memory was allocated via memblock_reserve().
*
* In hibernation, the pages which are Reserved and yet "nosave" are excluded
- * from the hibernation iamge. crash_is_nosave() does thich check for crash
+ * from the hibernation image. crash_is_nosave() does thich check for crash
* dump kernel and will reduce the total size of hibernation image.
*/
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index d6d443c4a01a..24adb581af0e 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -489,16 +489,29 @@ int module_finalize(const Elf_Ehdr *hdr,
int ret;
s = find_section(hdr, sechdrs, ".altinstructions");
- if (s)
- apply_alternatives_module((void *)s->sh_addr, s->sh_size);
+ if (s) {
+ ret = apply_alternatives_module((void *)s->sh_addr, s->sh_size);
+ if (ret < 0) {
+ pr_err("module %s: error occurred when applying alternatives\n", me->name);
+ return ret;
+ }
+ }
if (scs_is_dynamic()) {
s = find_section(hdr, sechdrs, ".init.eh_frame");
if (s) {
- ret = __pi_scs_patch((void *)s->sh_addr, s->sh_size);
- if (ret)
+ /*
+ * Because we can reject modules that are malformed
+ * so SCS patching fails, skip dry run and try to patch
+ * it in place. If patching fails, the module would not
+ * be loaded anyway.
+ */
+ ret = __pi_scs_patch((void *)s->sh_addr, s->sh_size, true);
+ if (ret) {
pr_err("module %s: error occurred during dynamic SCS patching (%d)\n",
me->name, ret);
+ return -ENOEXEC;
+ }
}
}
diff --git a/arch/arm64/kernel/mte.c b/arch/arm64/kernel/mte.c
index 43f7a2f39403..32148bf09c1d 100644
--- a/arch/arm64/kernel/mte.c
+++ b/arch/arm64/kernel/mte.c
@@ -476,7 +476,8 @@ static int __access_remote_tags(struct mm_struct *mm, unsigned long addr,
folio = page_folio(page);
if (folio_test_hugetlb(folio))
- WARN_ON_ONCE(!folio_test_hugetlb_mte_tagged(folio));
+ WARN_ON_ONCE(!folio_test_hugetlb_mte_tagged(folio) &&
+ !is_huge_zero_folio(folio));
else
WARN_ON_ONCE(!page_mte_tagged(page) && !is_zero_page(page));
diff --git a/arch/arm64/kernel/pi/map_kernel.c b/arch/arm64/kernel/pi/map_kernel.c
index e8ddbde31a83..a852264958c3 100644
--- a/arch/arm64/kernel/pi/map_kernel.c
+++ b/arch/arm64/kernel/pi/map_kernel.c
@@ -104,7 +104,7 @@ static void __init map_kernel(u64 kaslr_offset, u64 va_offset, int root_level)
if (enable_scs) {
scs_patch(__eh_frame_start + va_offset,
- __eh_frame_end - __eh_frame_start);
+ __eh_frame_end - __eh_frame_start, false);
asm("ic ialluis");
dynamic_scs_is_enabled = true;
@@ -141,13 +141,13 @@ static void __init map_kernel(u64 kaslr_offset, u64 va_offset, int root_level)
static void noinline __section(".idmap.text") set_ttbr0_for_lpa2(phys_addr_t ttbr)
{
u64 sctlr = read_sysreg(sctlr_el1);
- u64 tcr = read_sysreg(tcr_el1) | TCR_DS;
+ u64 tcr = read_sysreg(tcr_el1) | TCR_EL1_DS;
u64 mmfr0 = read_sysreg(id_aa64mmfr0_el1);
u64 parange = cpuid_feature_extract_unsigned_field(mmfr0,
ID_AA64MMFR0_EL1_PARANGE_SHIFT);
- tcr &= ~TCR_IPS_MASK;
- tcr |= parange << TCR_IPS_SHIFT;
+ tcr &= ~TCR_EL1_IPS_MASK;
+ tcr |= parange << TCR_EL1_IPS_SHIFT;
asm(" msr sctlr_el1, %0 ;"
" isb ;"
@@ -263,7 +263,7 @@ asmlinkage void __init early_map_kernel(u64 boot_status, phys_addr_t fdt)
}
if (va_bits > VA_BITS_MIN)
- sysreg_clear_set(tcr_el1, TCR_T1SZ_MASK, TCR_T1SZ(va_bits));
+ sysreg_clear_set(tcr_el1, TCR_EL1_T1SZ_MASK, TCR_T1SZ(va_bits));
/*
* The virtual KASLR displacement modulo 2MiB is decided by the
diff --git a/arch/arm64/kernel/pi/patch-scs.c b/arch/arm64/kernel/pi/patch-scs.c
index 55d0cd64ef71..bbe7d30ed12b 100644
--- a/arch/arm64/kernel/pi/patch-scs.c
+++ b/arch/arm64/kernel/pi/patch-scs.c
@@ -225,7 +225,7 @@ static int scs_handle_fde_frame(const struct eh_frame *frame,
return 0;
}
-int scs_patch(const u8 eh_frame[], int size)
+int scs_patch(const u8 eh_frame[], int size, bool skip_dry_run)
{
int code_alignment_factor = 1;
bool fde_use_sdata8 = false;
@@ -277,11 +277,13 @@ int scs_patch(const u8 eh_frame[], int size)
}
} else {
ret = scs_handle_fde_frame(frame, code_alignment_factor,
- fde_use_sdata8, true);
+ fde_use_sdata8, !skip_dry_run);
if (ret)
return ret;
- scs_handle_fde_frame(frame, code_alignment_factor,
- fde_use_sdata8, false);
+
+ if (!skip_dry_run)
+ scs_handle_fde_frame(frame, code_alignment_factor,
+ fde_use_sdata8, false);
}
p += sizeof(frame->size) + frame->size;
diff --git a/arch/arm64/kernel/pi/pi.h b/arch/arm64/kernel/pi/pi.h
index 08ef9f80456b..aec3172d4003 100644
--- a/arch/arm64/kernel/pi/pi.h
+++ b/arch/arm64/kernel/pi/pi.h
@@ -27,7 +27,7 @@ extern pgd_t init_pg_dir[], init_pg_end[];
void init_feature_override(u64 boot_status, const void *fdt, int chosen);
u64 kaslr_early_init(void *fdt, int chosen);
void relocate_kernel(u64 offset);
-int scs_patch(const u8 eh_frame[], int size);
+int scs_patch(const u8 eh_frame[], int size, bool skip_dry_run);
void map_range(phys_addr_t *pte, u64 start, u64 end, phys_addr_t pa,
pgprot_t prot, int level, pte_t *tbl, bool may_use_cont,
diff --git a/arch/arm64/kernel/probes/kprobes.c b/arch/arm64/kernel/probes/kprobes.c
index 8ab6104a4883..43a0361a8bf0 100644
--- a/arch/arm64/kernel/probes/kprobes.c
+++ b/arch/arm64/kernel/probes/kprobes.c
@@ -49,7 +49,10 @@ void *alloc_insn_page(void)
addr = execmem_alloc(EXECMEM_KPROBES, PAGE_SIZE);
if (!addr)
return NULL;
- set_memory_rox((unsigned long)addr, 1);
+ if (set_memory_rox((unsigned long)addr, 1)) {
+ execmem_free(addr);
+ return NULL;
+ }
return addr;
}
diff --git a/arch/arm64/kernel/probes/uprobes.c b/arch/arm64/kernel/probes/uprobes.c
index 2799bdb2fb82..941668800aea 100644
--- a/arch/arm64/kernel/probes/uprobes.c
+++ b/arch/arm64/kernel/probes/uprobes.c
@@ -131,7 +131,7 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
struct uprobe_task *utask = current->utask;
/*
- * Task has received a fatal signal, so reset back to probbed
+ * Task has received a fatal signal, so reset back to probed
* address.
*/
instruction_pointer_set(regs, utask->vaddr);
diff --git a/arch/arm64/kernel/proton-pack.c b/arch/arm64/kernel/proton-pack.c
index f9a32dfde006..80a580e019c5 100644
--- a/arch/arm64/kernel/proton-pack.c
+++ b/arch/arm64/kernel/proton-pack.c
@@ -91,12 +91,7 @@ early_param("nospectre_v2", parse_spectre_v2_param);
static bool spectre_v2_mitigations_off(void)
{
- bool ret = __nospectre_v2 || cpu_mitigations_off();
-
- if (ret)
- pr_info_once("spectre-v2 mitigation disabled by command line option\n");
-
- return ret;
+ return __nospectre_v2 || cpu_mitigations_off();
}
static const char *get_bhb_affected_string(enum mitigation_state bhb_state)
@@ -421,13 +416,8 @@ early_param("ssbd", parse_spectre_v4_param);
*/
static bool spectre_v4_mitigations_off(void)
{
- bool ret = cpu_mitigations_off() ||
- __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_DISABLED;
-
- if (ret)
- pr_info_once("spectre-v4 mitigation disabled by command-line option\n");
-
- return ret;
+ return cpu_mitigations_off() ||
+ __spectre_v4_policy == SPECTRE_V4_POLICY_MITIGATION_DISABLED;
}
/* Do we need to toggle the mitigation state on entry to/exit from the kernel? */
@@ -1043,9 +1033,7 @@ void spectre_bhb_enable_mitigation(const struct arm64_cpu_capabilities *entry)
if (arm64_get_spectre_v2_state() == SPECTRE_VULNERABLE) {
/* No point mitigating Spectre-BHB alone. */
} else if (!IS_ENABLED(CONFIG_MITIGATE_SPECTRE_BRANCH_HISTORY)) {
- pr_info_once("spectre-bhb mitigation disabled by compile time option\n");
- } else if (cpu_mitigations_off() || __nospectre_bhb) {
- pr_info_once("spectre-bhb mitigation disabled by command line option\n");
+ /* Do nothing */
} else if (supports_ecbhb(SCOPE_LOCAL_CPU)) {
state = SPECTRE_MITIGATED;
set_bit(BHB_HW, &system_bhb_mitigations);
@@ -1199,3 +1187,18 @@ void unpriv_ebpf_notify(int new_state)
pr_err("WARNING: %s", EBPF_WARN);
}
#endif
+
+void spectre_print_disabled_mitigations(void)
+{
+ /* Keep a single copy of the common message suffix to avoid duplication. */
+ const char *spectre_disabled_suffix = "mitigation disabled by command-line option\n";
+
+ if (spectre_v2_mitigations_off())
+ pr_info("spectre-v2 %s", spectre_disabled_suffix);
+
+ if (spectre_v4_mitigations_off())
+ pr_info("spectre-v4 %s", spectre_disabled_suffix);
+
+ if (__nospectre_bhb || cpu_mitigations_off())
+ pr_info("spectre-bhb %s", spectre_disabled_suffix);
+}
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 4b001121c72d..b9bdd83fbbca 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -912,13 +912,39 @@ static int sve_set_common(struct task_struct *target,
return -EINVAL;
/*
- * Apart from SVE_PT_REGS_MASK, all SVE_PT_* flags are consumed by
- * vec_set_vector_length(), which will also validate them for us:
+ * On systems without SVE we accept FPSIMD format writes with
+ * a VL of 0 to allow exiting streaming mode, otherwise a VL
+ * is required.
*/
- ret = vec_set_vector_length(target, type, header.vl,
- ((unsigned long)header.flags & ~SVE_PT_REGS_MASK) << 16);
- if (ret)
- return ret;
+ if (header.vl) {
+ /*
+ * If the system does not support SVE we can't
+ * configure a SVE VL.
+ */
+ if (!system_supports_sve() && type == ARM64_VEC_SVE)
+ return -EINVAL;
+
+ /*
+ * Apart from SVE_PT_REGS_MASK, all SVE_PT_* flags are
+ * consumed by vec_set_vector_length(), which will
+ * also validate them for us:
+ */
+ ret = vec_set_vector_length(target, type, header.vl,
+ ((unsigned long)header.flags & ~SVE_PT_REGS_MASK) << 16);
+ if (ret)
+ return ret;
+ } else {
+ /* If the system supports SVE we require a VL. */
+ if (system_supports_sve())
+ return -EINVAL;
+
+ /*
+ * Only FPSIMD formatted data with no flags set is
+ * supported.
+ */
+ if (header.flags != SVE_PT_REGS_FPSIMD)
+ return -EINVAL;
+ }
/* Allocate SME storage if necessary, preserving any existing ZA/ZT state */
if (type == ARM64_VEC_SME) {
@@ -1016,7 +1042,7 @@ static int sve_set(struct task_struct *target,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
- if (!system_supports_sve())
+ if (!system_supports_sve() && !system_supports_sme())
return -EINVAL;
return sve_set_common(target, regset, pos, count, kbuf, ubuf,
diff --git a/arch/arm64/kernel/sdei.c b/arch/arm64/kernel/sdei.c
index 95169f7b6531..778f2a1faac8 100644
--- a/arch/arm64/kernel/sdei.c
+++ b/arch/arm64/kernel/sdei.c
@@ -63,8 +63,6 @@ static void free_sdei_stacks(void)
{
int cpu;
- BUILD_BUG_ON(!IS_ENABLED(CONFIG_VMAP_STACK));
-
for_each_possible_cpu(cpu) {
_free_sdei_stack(&sdei_stack_normal_ptr, cpu);
_free_sdei_stack(&sdei_stack_critical_ptr, cpu);
@@ -88,8 +86,6 @@ static int init_sdei_stacks(void)
int cpu;
int err = 0;
- BUILD_BUG_ON(!IS_ENABLED(CONFIG_VMAP_STACK));
-
for_each_possible_cpu(cpu) {
err = _init_sdei_stack(&sdei_stack_normal_ptr, cpu);
if (err)
@@ -202,7 +198,7 @@ out_err:
/*
* do_sdei_event() returns one of:
* SDEI_EV_HANDLED - success, return to the interrupted context.
- * SDEI_EV_FAILED - failure, return this error code to firmare.
+ * SDEI_EV_FAILED - failure, return this error code to firmware.
* virtual-address - success, return to this address.
*/
unsigned long __kprobes do_sdei_event(struct pt_regs *regs,
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 68cea3a4a35c..1aa324104afb 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -350,7 +350,7 @@ void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu)
/*
* Now that the dying CPU is beyond the point of no return w.r.t.
- * in-kernel synchronisation, try to get the firwmare to help us to
+ * in-kernel synchronisation, try to get the firmware to help us to
* verify that it has really left the kernel before we consider
* clobbering anything it might still be using.
*/
@@ -523,7 +523,7 @@ int arch_register_cpu(int cpu)
/*
* Availability of the acpi handle is sufficient to establish
- * that _STA has aleady been checked. No need to recheck here.
+ * that _STA has already been checked. No need to recheck here.
*/
c->hotpluggable = arch_cpu_is_hotpluggable(cpu);
@@ -1094,7 +1094,7 @@ static void ipi_setup_sgi(int ipi)
irq = ipi_irq_base + ipi;
if (ipi_should_be_nmi(ipi)) {
- err = request_percpu_nmi(irq, ipi_handler, "IPI", &irq_stat);
+ err = request_percpu_nmi(irq, ipi_handler, "IPI", NULL, &irq_stat);
WARN(err, "Could not request IRQ %d as NMI, err=%d\n", irq, err);
} else {
err = request_percpu_irq(irq, ipi_handler, "IPI", &irq_stat);
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index aba7ca6bca2d..c062badd1a56 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -96,7 +96,7 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
* (Similarly for HVC and SMC elsewhere.)
*/
- if (flags & _TIF_MTE_ASYNC_FAULT) {
+ if (unlikely(flags & _TIF_MTE_ASYNC_FAULT)) {
/*
* Process the asynchronous tag check fault before the actual
* syscall. do_notify_resume() will send a signal to userspace
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 681939ef5d16..914282016069 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -922,7 +922,7 @@ void __noreturn panic_bad_stack(struct pt_regs *regs, unsigned long esr, unsigne
__show_regs(regs);
/*
- * We use nmi_panic to limit the potential for recusive overflows, and
+ * We use nmi_panic to limit the potential for recursive overflows, and
* to get a better stack trace.
*/
nmi_panic(NULL, "kernel stack overflow");
diff --git a/arch/arm64/kernel/vdso32/Makefile b/arch/arm64/kernel/vdso32/Makefile
index ffa3536581f6..9d0efed91414 100644
--- a/arch/arm64/kernel/vdso32/Makefile
+++ b/arch/arm64/kernel/vdso32/Makefile
@@ -63,7 +63,7 @@ VDSO_CFLAGS += -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
$(filter -Werror,$(KBUILD_CPPFLAGS)) \
-Werror-implicit-function-declaration \
-Wno-format-security \
- -std=gnu11
+ -std=gnu11 -fms-extensions
VDSO_CFLAGS += -O2
# Some useful compiler-dependent flags from top-level Makefile
VDSO_CFLAGS += $(call cc32-option,-Wno-pointer-sign)
@@ -71,6 +71,7 @@ VDSO_CFLAGS += -fno-strict-overflow
VDSO_CFLAGS += $(call cc32-option,-Werror=strict-prototypes)
VDSO_CFLAGS += -Werror=date-time
VDSO_CFLAGS += $(call cc32-option,-Werror=incompatible-pointer-types)
+VDSO_CFLAGS += $(if $(CONFIG_CC_IS_CLANG),-Wno-microsoft-anon-tag)
# Compile as THUMB2 or ARM. Unwinding via frame-pointers in THUMB2 is
# unreliable.
diff --git a/arch/arm64/kernel/vmcore_info.c b/arch/arm64/kernel/vmcore_info.c
index b19d5d6cb8b3..9619ece66b79 100644
--- a/arch/arm64/kernel/vmcore_info.c
+++ b/arch/arm64/kernel/vmcore_info.c
@@ -14,7 +14,7 @@ static inline u64 get_tcr_el1_t1sz(void);
static inline u64 get_tcr_el1_t1sz(void)
{
- return (read_sysreg(tcr_el1) & TCR_T1SZ_MASK) >> TCR_T1SZ_OFFSET;
+ return (read_sysreg(tcr_el1) & TCR_EL1_T1SZ_MASK) >> TCR_EL1_T1SZ_SHIFT;
}
void arch_crash_save_vmcoreinfo(void)
diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c
index 3f675875abea..99a07972068d 100644
--- a/arch/arm64/kvm/arch_timer.c
+++ b/arch/arm64/kvm/arch_timer.c
@@ -806,7 +806,7 @@ static void timer_set_traps(struct kvm_vcpu *vcpu, struct timer_map *map)
tpt = tpc = true;
/*
- * For the poor sods that could not correctly substract one value
+ * For the poor sods that could not correctly subtract one value
* from another, trap the full virtual timer and counter.
*/
if (has_broken_cntvoff() && timer_get_offset(map->direct_vtimer))
diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 870953b4a8a7..12acc024f7a8 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -624,6 +624,7 @@ nommu:
kvm_timer_vcpu_load(vcpu);
kvm_vgic_load(vcpu);
kvm_vcpu_load_debug(vcpu);
+ kvm_vcpu_load_fgt(vcpu);
if (has_vhe())
kvm_vcpu_load_vhe(vcpu);
kvm_arch_vcpu_load_fp(vcpu);
@@ -642,7 +643,6 @@ nommu:
vcpu->arch.hcr_el2 |= HCR_TWI;
vcpu_set_pauth_traps(vcpu);
- kvm_vcpu_load_fgt(vcpu);
if (is_protected_kvm_enabled()) {
kvm_call_hyp_nvhe(__pkvm_vcpu_load,
@@ -2448,7 +2448,7 @@ static void kvm_hyp_init_symbols(void)
kvm_nvhe_sym(__icache_flags) = __icache_flags;
kvm_nvhe_sym(kvm_arm_vmid_bits) = kvm_arm_vmid_bits;
- /* Propagate the FGT state to the the nVHE side */
+ /* Propagate the FGT state to the nVHE side */
kvm_nvhe_sym(hfgrtr_masks) = hfgrtr_masks;
kvm_nvhe_sym(hfgwtr_masks) = hfgwtr_masks;
kvm_nvhe_sym(hfgitr_masks) = hfgitr_masks;
diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c
index 4e16f9b96f63..f731cc4c3f28 100644
--- a/arch/arm64/kvm/hyp/nvhe/ffa.c
+++ b/arch/arm64/kvm/hyp/nvhe/ffa.c
@@ -115,7 +115,7 @@ static void ffa_set_retval(struct kvm_cpu_context *ctxt,
*
* FFA-1.3 introduces 64-bit variants of the CPU cycle management
* interfaces. Moreover, FF-A 1.3 clarifies that SMC32 direct requests
- * complete with SMC32 direct reponses which *should* allow us use the
+ * complete with SMC32 direct responses which *should* allow us use the
* function ID sent by the caller to determine whether to return x8-x17.
*
* Note that we also cannot rely on function IDs in the response.
@@ -479,7 +479,7 @@ static void __do_ffa_mem_xfer(const u64 func_id,
struct ffa_mem_region_attributes *ep_mem_access;
struct ffa_composite_mem_region *reg;
struct ffa_mem_region *buf;
- u32 offset, nr_ranges;
+ u32 offset, nr_ranges, checked_offset;
int ret = 0;
if (addr_mbz || npages_mbz || fraglen > len ||
@@ -516,7 +516,12 @@ static void __do_ffa_mem_xfer(const u64 func_id,
goto out_unlock;
}
- if (fraglen < offset + sizeof(struct ffa_composite_mem_region)) {
+ if (check_add_overflow(offset, sizeof(struct ffa_composite_mem_region), &checked_offset)) {
+ ret = FFA_RET_INVALID_PARAMETERS;
+ goto out_unlock;
+ }
+
+ if (fraglen < checked_offset) {
ret = FFA_RET_INVALID_PARAMETERS;
goto out_unlock;
}
diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
index ddc8beb55eee..49db32f3ddf7 100644
--- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c
+++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c
@@ -367,6 +367,19 @@ static int host_stage2_unmap_dev_all(void)
return kvm_pgtable_stage2_unmap(pgt, addr, BIT(pgt->ia_bits) - addr);
}
+/*
+ * Ensure the PFN range is contained within PA-range.
+ *
+ * This check is also robust to overflows and is therefore a requirement before
+ * using a pfn/nr_pages pair from an untrusted source.
+ */
+static bool pfn_range_is_valid(u64 pfn, u64 nr_pages)
+{
+ u64 limit = BIT(kvm_phys_shift(&host_mmu.arch.mmu) - PAGE_SHIFT);
+
+ return pfn < limit && ((limit - pfn) >= nr_pages);
+}
+
struct kvm_mem_range {
u64 start;
u64 end;
@@ -776,6 +789,9 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages)
void *virt = __hyp_va(phys);
int ret;
+ if (!pfn_range_is_valid(pfn, nr_pages))
+ return -EINVAL;
+
host_lock_component();
hyp_lock_component();
@@ -804,6 +820,9 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages)
u64 virt = (u64)__hyp_va(phys);
int ret;
+ if (!pfn_range_is_valid(pfn, nr_pages))
+ return -EINVAL;
+
host_lock_component();
hyp_lock_component();
@@ -887,6 +906,9 @@ int __pkvm_host_share_ffa(u64 pfn, u64 nr_pages)
u64 size = PAGE_SIZE * nr_pages;
int ret;
+ if (!pfn_range_is_valid(pfn, nr_pages))
+ return -EINVAL;
+
host_lock_component();
ret = __host_check_page_state_range(phys, size, PKVM_PAGE_OWNED);
if (!ret)
@@ -902,6 +924,9 @@ int __pkvm_host_unshare_ffa(u64 pfn, u64 nr_pages)
u64 size = PAGE_SIZE * nr_pages;
int ret;
+ if (!pfn_range_is_valid(pfn, nr_pages))
+ return -EINVAL;
+
host_lock_component();
ret = __host_check_page_state_range(phys, size, PKVM_PAGE_SHARED_OWNED);
if (!ret)
@@ -945,6 +970,9 @@ int __pkvm_host_share_guest(u64 pfn, u64 gfn, u64 nr_pages, struct pkvm_hyp_vcpu
if (prot & ~KVM_PGTABLE_PROT_RWX)
return -EINVAL;
+ if (!pfn_range_is_valid(pfn, nr_pages))
+ return -EINVAL;
+
ret = __guest_check_transition_size(phys, ipa, nr_pages, &size);
if (ret)
return ret;
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 7cc964af8d30..9a6a80c3fbe5 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1755,7 +1755,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
/*
* Check if this is non-struct page memory PFN, and cannot support
- * CMOs. It could potentially be unsafe to access as cachable.
+ * CMOs. It could potentially be unsafe to access as cacheable.
*/
if (vm_flags & (VM_PFNMAP | VM_MIXEDMAP) && !pfn_is_map_memory(pfn)) {
if (is_vma_cacheable) {
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index f04cda40545b..be6bbd167770 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -85,7 +85,7 @@ int kvm_vcpu_init_nested(struct kvm_vcpu *vcpu)
/*
* Let's treat memory allocation failures as benign: If we fail to
* allocate anything, return an error and keep the allocated array
- * alive. Userspace may try to recover by intializing the vcpu
+ * alive. Userspace may try to recover by initializing the vcpu
* again, and there is no reason to affect the whole VM for this.
*/
num_mmus = atomic_read(&kvm->online_vcpus) * S2_MMU_PER_VCPU;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index e67eb39ddc11..ec3fbe0b8d52 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -2595,19 +2595,23 @@ static bool bad_redir_trap(struct kvm_vcpu *vcpu,
.val = 0, \
}
-/* sys_reg_desc initialiser for known cpufeature ID registers */
-#define AA32_ID_SANITISED(name) { \
- ID_DESC(name), \
- .visibility = aa32_id_visibility, \
- .val = 0, \
-}
-
/* sys_reg_desc initialiser for writable ID registers */
#define ID_WRITABLE(name, mask) { \
ID_DESC(name), \
.val = mask, \
}
+/*
+ * 32bit ID regs are fully writable when the guest is 32bit
+ * capable. Nothing in the KVM code should rely on 32bit features
+ * anyway, only 64bit, so let the VMM do its worse.
+ */
+#define AA32_ID_WRITABLE(name) { \
+ ID_DESC(name), \
+ .visibility = aa32_id_visibility, \
+ .val = GENMASK(31, 0), \
+}
+
/* sys_reg_desc initialiser for cpufeature ID registers that need filtering */
#define ID_FILTERED(sysreg, name, mask) { \
ID_DESC(sysreg), \
@@ -3128,40 +3132,39 @@ static const struct sys_reg_desc sys_reg_descs[] = {
/* AArch64 mappings of the AArch32 ID registers */
/* CRm=1 */
- AA32_ID_SANITISED(ID_PFR0_EL1),
- AA32_ID_SANITISED(ID_PFR1_EL1),
+ AA32_ID_WRITABLE(ID_PFR0_EL1),
+ AA32_ID_WRITABLE(ID_PFR1_EL1),
{ SYS_DESC(SYS_ID_DFR0_EL1),
.access = access_id_reg,
.get_user = get_id_reg,
.set_user = set_id_dfr0_el1,
.visibility = aa32_id_visibility,
.reset = read_sanitised_id_dfr0_el1,
- .val = ID_DFR0_EL1_PerfMon_MASK |
- ID_DFR0_EL1_CopDbg_MASK, },
+ .val = GENMASK(31, 0) },
ID_HIDDEN(ID_AFR0_EL1),
- AA32_ID_SANITISED(ID_MMFR0_EL1),
- AA32_ID_SANITISED(ID_MMFR1_EL1),
- AA32_ID_SANITISED(ID_MMFR2_EL1),
- AA32_ID_SANITISED(ID_MMFR3_EL1),
+ AA32_ID_WRITABLE(ID_MMFR0_EL1),
+ AA32_ID_WRITABLE(ID_MMFR1_EL1),
+ AA32_ID_WRITABLE(ID_MMFR2_EL1),
+ AA32_ID_WRITABLE(ID_MMFR3_EL1),
/* CRm=2 */
- AA32_ID_SANITISED(ID_ISAR0_EL1),
- AA32_ID_SANITISED(ID_ISAR1_EL1),
- AA32_ID_SANITISED(ID_ISAR2_EL1),
- AA32_ID_SANITISED(ID_ISAR3_EL1),
- AA32_ID_SANITISED(ID_ISAR4_EL1),
- AA32_ID_SANITISED(ID_ISAR5_EL1),
- AA32_ID_SANITISED(ID_MMFR4_EL1),
- AA32_ID_SANITISED(ID_ISAR6_EL1),
+ AA32_ID_WRITABLE(ID_ISAR0_EL1),
+ AA32_ID_WRITABLE(ID_ISAR1_EL1),
+ AA32_ID_WRITABLE(ID_ISAR2_EL1),
+ AA32_ID_WRITABLE(ID_ISAR3_EL1),
+ AA32_ID_WRITABLE(ID_ISAR4_EL1),
+ AA32_ID_WRITABLE(ID_ISAR5_EL1),
+ AA32_ID_WRITABLE(ID_MMFR4_EL1),
+ AA32_ID_WRITABLE(ID_ISAR6_EL1),
/* CRm=3 */
- AA32_ID_SANITISED(MVFR0_EL1),
- AA32_ID_SANITISED(MVFR1_EL1),
- AA32_ID_SANITISED(MVFR2_EL1),
+ AA32_ID_WRITABLE(MVFR0_EL1),
+ AA32_ID_WRITABLE(MVFR1_EL1),
+ AA32_ID_WRITABLE(MVFR2_EL1),
ID_UNALLOCATED(3,3),
- AA32_ID_SANITISED(ID_PFR2_EL1),
+ AA32_ID_WRITABLE(ID_PFR2_EL1),
ID_HIDDEN(ID_DFR1_EL1),
- AA32_ID_SANITISED(ID_MMFR5_EL1),
+ AA32_ID_WRITABLE(ID_MMFR5_EL1),
ID_UNALLOCATED(3,7),
/* AArch64 ID registers */
@@ -5606,11 +5609,17 @@ int kvm_finalize_sys_regs(struct kvm_vcpu *vcpu)
guard(mutex)(&kvm->arch.config_lock);
- if (!(static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif) &&
- irqchip_in_kernel(kvm) &&
- kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)) {
- kvm->arch.id_regs[IDREG_IDX(SYS_ID_AA64PFR0_EL1)] &= ~ID_AA64PFR0_EL1_GIC_MASK;
- kvm->arch.id_regs[IDREG_IDX(SYS_ID_PFR1_EL1)] &= ~ID_PFR1_EL1_GIC_MASK;
+ /*
+ * This hacks into the ID registers, so only perform it when the
+ * first vcpu runs, or the kvm_set_vm_id_reg() helper will scream.
+ */
+ if (!irqchip_in_kernel(kvm) && !kvm_vm_has_ran_once(kvm)) {
+ u64 val;
+
+ val = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC;
+ kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, val);
+ val = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC;
+ kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, val);
}
if (vcpu_has_nv(vcpu)) {
diff --git a/arch/arm64/kvm/vgic/vgic-debug.c b/arch/arm64/kvm/vgic/vgic-debug.c
index 4c1209261b65..bb92853d1fd3 100644
--- a/arch/arm64/kvm/vgic/vgic-debug.c
+++ b/arch/arm64/kvm/vgic/vgic-debug.c
@@ -64,29 +64,37 @@ static void iter_next(struct kvm *kvm, struct vgic_state_iter *iter)
static int iter_mark_lpis(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;
+ unsigned long intid, flags;
struct vgic_irq *irq;
- unsigned long intid;
int nr_lpis = 0;
+ xa_lock_irqsave(&dist->lpi_xa, flags);
+
xa_for_each(&dist->lpi_xa, intid, irq) {
if (!vgic_try_get_irq_ref(irq))
continue;
- xa_set_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER);
+ __xa_set_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER);
nr_lpis++;
}
+ xa_unlock_irqrestore(&dist->lpi_xa, flags);
+
return nr_lpis;
}
static void iter_unmark_lpis(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;
+ unsigned long intid, flags;
struct vgic_irq *irq;
- unsigned long intid;
xa_for_each_marked(&dist->lpi_xa, intid, irq, LPI_XA_MARK_DEBUG_ITER) {
- xa_clear_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER);
+ xa_lock_irqsave(&dist->lpi_xa, flags);
+ __xa_clear_mark(&dist->lpi_xa, intid, LPI_XA_MARK_DEBUG_ITER);
+ xa_unlock_irqrestore(&dist->lpi_xa, flags);
+
+ /* vgic_put_irq() expects to be called outside of the xa_lock */
vgic_put_irq(kvm, irq);
}
}
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 1796b1a22a72..da62edbc1205 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -53,7 +53,7 @@ void kvm_vgic_early_init(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;
- xa_init(&dist->lpi_xa);
+ xa_init_flags(&dist->lpi_xa, XA_FLAGS_LOCK_IRQ);
}
/* CREATION */
@@ -71,6 +71,7 @@ static int vgic_allocate_private_irqs_locked(struct kvm_vcpu *vcpu, u32 type);
int kvm_vgic_create(struct kvm *kvm, u32 type)
{
struct kvm_vcpu *vcpu;
+ u64 aa64pfr0, pfr1;
unsigned long i;
int ret;
@@ -161,10 +162,19 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
- if (type == KVM_DEV_TYPE_ARM_VGIC_V2)
+ aa64pfr0 = kvm_read_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1) & ~ID_AA64PFR0_EL1_GIC;
+ pfr1 = kvm_read_vm_id_reg(kvm, SYS_ID_PFR1_EL1) & ~ID_PFR1_EL1_GIC;
+
+ if (type == KVM_DEV_TYPE_ARM_VGIC_V2) {
kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
- else
+ } else {
INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
+ aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
+ pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3);
+ }
+
+ kvm_set_vm_id_reg(kvm, SYS_ID_AA64PFR0_EL1, aa64pfr0);
+ kvm_set_vm_id_reg(kvm, SYS_ID_PFR1_EL1, pfr1);
if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
kvm->arch.vgic.nassgicap = system_supports_direct_sgis();
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index ce3e3ed3f29f..3f1c4b10fed9 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -78,6 +78,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
{
struct vgic_dist *dist = &kvm->arch.vgic;
struct vgic_irq *irq = vgic_get_irq(kvm, intid), *oldirq;
+ unsigned long flags;
int ret;
/* In this case there is no put, since we keep the reference. */
@@ -88,7 +89,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
if (!irq)
return ERR_PTR(-ENOMEM);
- ret = xa_reserve(&dist->lpi_xa, intid, GFP_KERNEL_ACCOUNT);
+ ret = xa_reserve_irq(&dist->lpi_xa, intid, GFP_KERNEL_ACCOUNT);
if (ret) {
kfree(irq);
return ERR_PTR(ret);
@@ -103,7 +104,7 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
irq->target_vcpu = vcpu;
irq->group = 1;
- xa_lock(&dist->lpi_xa);
+ xa_lock_irqsave(&dist->lpi_xa, flags);
/*
* There could be a race with another vgic_add_lpi(), so we need to
@@ -114,21 +115,18 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
/* Someone was faster with adding this LPI, lets use that. */
kfree(irq);
irq = oldirq;
-
- goto out_unlock;
+ } else {
+ ret = xa_err(__xa_store(&dist->lpi_xa, intid, irq, 0));
}
- ret = xa_err(__xa_store(&dist->lpi_xa, intid, irq, 0));
+ xa_unlock_irqrestore(&dist->lpi_xa, flags);
+
if (ret) {
xa_release(&dist->lpi_xa, intid);
kfree(irq);
- }
-
-out_unlock:
- xa_unlock(&dist->lpi_xa);
- if (ret)
return ERR_PTR(ret);
+ }
/*
* We "cache" the configuration table entries in our struct vgic_irq's.
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 6fbb4b099855..2f75ef14d339 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -301,7 +301,8 @@ void vcpu_set_ich_hcr(struct kvm_vcpu *vcpu)
return;
/* Hide GICv3 sysreg if necessary */
- if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2) {
+ if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2 ||
+ !irqchip_in_kernel(vcpu->kvm)) {
vgic_v3->vgic_hcr |= (ICH_HCR_EL2_TALL0 | ICH_HCR_EL2_TALL1 |
ICH_HCR_EL2_TC);
return;
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 6dd5a10081e2..8d20c53faef0 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -28,7 +28,7 @@ struct vgic_global kvm_vgic_global_state __ro_after_init = {
* kvm->arch.config_lock (mutex)
* its->cmd_lock (mutex)
* its->its_lock (mutex)
- * vgic_dist->lpi_xa.xa_lock
+ * vgic_dist->lpi_xa.xa_lock must be taken with IRQs disabled
* vgic_cpu->ap_list_lock must be taken with IRQs disabled
* vgic_irq->irq_lock must be taken with IRQs disabled
*
@@ -141,32 +141,39 @@ static __must_check bool vgic_put_irq_norelease(struct kvm *kvm, struct vgic_irq
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
{
struct vgic_dist *dist = &kvm->arch.vgic;
+ unsigned long flags;
- if (irq->intid >= VGIC_MIN_LPI)
- might_lock(&dist->lpi_xa.xa_lock);
+ /*
+ * Normally the lock is only taken when the refcount drops to 0.
+ * Acquire/release it early on lockdep kernels to make locking issues
+ * in rare release paths a bit more obvious.
+ */
+ if (IS_ENABLED(CONFIG_LOCKDEP) && irq->intid >= VGIC_MIN_LPI) {
+ guard(spinlock_irqsave)(&dist->lpi_xa.xa_lock);
+ }
if (!__vgic_put_irq(kvm, irq))
return;
- xa_lock(&dist->lpi_xa);
+ xa_lock_irqsave(&dist->lpi_xa, flags);
vgic_release_lpi_locked(dist, irq);
- xa_unlock(&dist->lpi_xa);
+ xa_unlock_irqrestore(&dist->lpi_xa, flags);
}
static void vgic_release_deleted_lpis(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;
- unsigned long intid;
+ unsigned long flags, intid;
struct vgic_irq *irq;
- xa_lock(&dist->lpi_xa);
+ xa_lock_irqsave(&dist->lpi_xa, flags);
xa_for_each(&dist->lpi_xa, intid, irq) {
if (irq->pending_release)
vgic_release_lpi_locked(dist, irq);
}
- xa_unlock(&dist->lpi_xa);
+ xa_unlock_irqrestore(&dist->lpi_xa, flags);
}
void vgic_flush_pending_lpis(struct kvm_vcpu *vcpu)
diff --git a/arch/arm64/mm/contpte.c b/arch/arm64/mm/contpte.c
index c0557945939c..589bcf878938 100644
--- a/arch/arm64/mm/contpte.c
+++ b/arch/arm64/mm/contpte.c
@@ -622,8 +622,7 @@ int contpte_ptep_set_access_flags(struct vm_area_struct *vma,
__ptep_set_access_flags(vma, addr, ptep, entry, 0);
if (dirty)
- __flush_tlb_range(vma, start_addr, addr,
- PAGE_SIZE, true, 3);
+ local_flush_tlb_contpte(vma, start_addr);
} else {
__contpte_try_unfold(vma->vm_mm, addr, ptep, orig_pte);
__ptep_set_access_flags(vma, addr, ptep, entry, dirty);
diff --git a/arch/arm64/mm/copypage.c b/arch/arm64/mm/copypage.c
index a86c897017df..cd5912ba617b 100644
--- a/arch/arm64/mm/copypage.c
+++ b/arch/arm64/mm/copypage.c
@@ -35,7 +35,7 @@ void copy_highpage(struct page *to, struct page *from)
from != folio_page(src, 0))
return;
- WARN_ON_ONCE(!folio_try_hugetlb_mte_tagging(dst));
+ folio_try_hugetlb_mte_tagging(dst);
/*
* Populate tags for all subpages.
@@ -51,8 +51,13 @@ void copy_highpage(struct page *to, struct page *from)
}
folio_set_hugetlb_mte_tagged(dst);
} else if (page_mte_tagged(from)) {
- /* It's a new page, shouldn't have been tagged yet */
- WARN_ON_ONCE(!try_page_mte_tagging(to));
+ /*
+ * Most of the time it's a new page that shouldn't have been
+ * tagged yet. However, folio migration can end up reusing the
+ * same page without untagging it. Ignore the warning if the
+ * page is already tagged.
+ */
+ try_page_mte_tagging(to);
mte_copy_page_tags(kto, kfrom);
set_page_mte_tagged(to);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index d816ff44faff..be9dab2c7d6a 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -233,9 +233,13 @@ int __ptep_set_access_flags(struct vm_area_struct *vma,
pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval, pteval);
} while (pteval != old_pteval);
- /* Invalidate a stale read-only entry */
+ /*
+ * Invalidate the local stale read-only entry. Remote stale entries
+ * may still cause page faults and be invalidated via
+ * flush_tlb_fix_spurious_fault().
+ */
if (dirty)
- flush_tlb_page(vma, address);
+ local_flush_tlb_page(vma, address);
return 1;
}
@@ -967,10 +971,21 @@ struct folio *vma_alloc_zeroed_movable_folio(struct vm_area_struct *vma,
return vma_alloc_folio(flags, 0, vma, vaddr);
}
-void tag_clear_highpage(struct page *page)
+bool tag_clear_highpages(struct page *page, int numpages)
{
- /* Newly allocated page, shouldn't have been tagged yet */
- WARN_ON_ONCE(!try_page_mte_tagging(page));
- mte_zero_clear_page_tags(page_address(page));
- set_page_mte_tagged(page);
+ /*
+ * Check if MTE is supported and fall back to clear_highpage().
+ * get_huge_zero_folio() unconditionally passes __GFP_ZEROTAGS and
+ * post_alloc_hook() will invoke tag_clear_highpages().
+ */
+ if (!system_supports_mte())
+ return false;
+
+ /* Newly allocated pages, shouldn't have been tagged yet */
+ for (int i = 0; i < numpages; i++, page++) {
+ WARN_ON_ONCE(!try_page_mte_tagging(page));
+ mte_zero_clear_page_tags(page_address(page));
+ set_page_mte_tagged(page);
+ }
+ return true;
}
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index b8d37eb037fc..c8d24b7cdd62 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -49,6 +49,8 @@
#define NO_CONT_MAPPINGS BIT(1)
#define NO_EXEC_MAPPINGS BIT(2) /* assumes FEAT_HPDS is not used */
+#define INVALID_PHYS_ADDR (-1ULL)
+
DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
u64 kimage_voffset __ro_after_init;
@@ -194,11 +196,11 @@ static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end,
} while (ptep++, addr += PAGE_SIZE, addr != end);
}
-static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
- unsigned long end, phys_addr_t phys,
- pgprot_t prot,
- phys_addr_t (*pgtable_alloc)(enum pgtable_type),
- int flags)
+static int alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
+ unsigned long end, phys_addr_t phys,
+ pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type),
+ int flags)
{
unsigned long next;
pmd_t pmd = READ_ONCE(*pmdp);
@@ -213,6 +215,8 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
pmdval |= PMD_TABLE_PXN;
BUG_ON(!pgtable_alloc);
pte_phys = pgtable_alloc(TABLE_PTE);
+ if (pte_phys == INVALID_PHYS_ADDR)
+ return -ENOMEM;
ptep = pte_set_fixmap(pte_phys);
init_clear_pgtable(ptep);
ptep += pte_index(addr);
@@ -244,11 +248,13 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
* walker.
*/
pte_clear_fixmap();
+
+ return 0;
}
-static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
- phys_addr_t phys, pgprot_t prot,
- phys_addr_t (*pgtable_alloc)(enum pgtable_type), int flags)
+static int init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
+ phys_addr_t phys, pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type), int flags)
{
unsigned long next;
@@ -269,22 +275,29 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end,
BUG_ON(!pgattr_change_is_safe(pmd_val(old_pmd),
READ_ONCE(pmd_val(*pmdp))));
} else {
- alloc_init_cont_pte(pmdp, addr, next, phys, prot,
- pgtable_alloc, flags);
+ int ret;
+
+ ret = alloc_init_cont_pte(pmdp, addr, next, phys, prot,
+ pgtable_alloc, flags);
+ if (ret)
+ return ret;
BUG_ON(pmd_val(old_pmd) != 0 &&
pmd_val(old_pmd) != READ_ONCE(pmd_val(*pmdp)));
}
phys += next - addr;
} while (pmdp++, addr = next, addr != end);
+
+ return 0;
}
-static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
- unsigned long end, phys_addr_t phys,
- pgprot_t prot,
- phys_addr_t (*pgtable_alloc)(enum pgtable_type),
- int flags)
+static int alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
+ unsigned long end, phys_addr_t phys,
+ pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type),
+ int flags)
{
+ int ret;
unsigned long next;
pud_t pud = READ_ONCE(*pudp);
pmd_t *pmdp;
@@ -301,6 +314,8 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
pudval |= PUD_TABLE_PXN;
BUG_ON(!pgtable_alloc);
pmd_phys = pgtable_alloc(TABLE_PMD);
+ if (pmd_phys == INVALID_PHYS_ADDR)
+ return -ENOMEM;
pmdp = pmd_set_fixmap(pmd_phys);
init_clear_pgtable(pmdp);
pmdp += pmd_index(addr);
@@ -320,20 +335,26 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
(flags & NO_CONT_MAPPINGS) == 0)
__prot = __pgprot(pgprot_val(prot) | PTE_CONT);
- init_pmd(pmdp, addr, next, phys, __prot, pgtable_alloc, flags);
+ ret = init_pmd(pmdp, addr, next, phys, __prot, pgtable_alloc, flags);
+ if (ret)
+ goto out;
pmdp += pmd_index(next) - pmd_index(addr);
phys += next - addr;
} while (addr = next, addr != end);
+out:
pmd_clear_fixmap();
+
+ return ret;
}
-static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
- phys_addr_t phys, pgprot_t prot,
- phys_addr_t (*pgtable_alloc)(enum pgtable_type),
- int flags)
+static int alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
+ phys_addr_t phys, pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type),
+ int flags)
{
+ int ret = 0;
unsigned long next;
p4d_t p4d = READ_ONCE(*p4dp);
pud_t *pudp;
@@ -346,6 +367,8 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
p4dval |= P4D_TABLE_PXN;
BUG_ON(!pgtable_alloc);
pud_phys = pgtable_alloc(TABLE_PUD);
+ if (pud_phys == INVALID_PHYS_ADDR)
+ return -ENOMEM;
pudp = pud_set_fixmap(pud_phys);
init_clear_pgtable(pudp);
pudp += pud_index(addr);
@@ -375,8 +398,10 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
BUG_ON(!pgattr_change_is_safe(pud_val(old_pud),
READ_ONCE(pud_val(*pudp))));
} else {
- alloc_init_cont_pmd(pudp, addr, next, phys, prot,
- pgtable_alloc, flags);
+ ret = alloc_init_cont_pmd(pudp, addr, next, phys, prot,
+ pgtable_alloc, flags);
+ if (ret)
+ goto out;
BUG_ON(pud_val(old_pud) != 0 &&
pud_val(old_pud) != READ_ONCE(pud_val(*pudp)));
@@ -384,14 +409,18 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end,
phys += next - addr;
} while (pudp++, addr = next, addr != end);
+out:
pud_clear_fixmap();
+
+ return ret;
}
-static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end,
- phys_addr_t phys, pgprot_t prot,
- phys_addr_t (*pgtable_alloc)(enum pgtable_type),
- int flags)
+static int alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end,
+ phys_addr_t phys, pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type),
+ int flags)
{
+ int ret;
unsigned long next;
pgd_t pgd = READ_ONCE(*pgdp);
p4d_t *p4dp;
@@ -404,6 +433,8 @@ static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end,
pgdval |= PGD_TABLE_PXN;
BUG_ON(!pgtable_alloc);
p4d_phys = pgtable_alloc(TABLE_P4D);
+ if (p4d_phys == INVALID_PHYS_ADDR)
+ return -ENOMEM;
p4dp = p4d_set_fixmap(p4d_phys);
init_clear_pgtable(p4dp);
p4dp += p4d_index(addr);
@@ -418,8 +449,10 @@ static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end,
next = p4d_addr_end(addr, end);
- alloc_init_pud(p4dp, addr, next, phys, prot,
- pgtable_alloc, flags);
+ ret = alloc_init_pud(p4dp, addr, next, phys, prot,
+ pgtable_alloc, flags);
+ if (ret)
+ goto out;
BUG_ON(p4d_val(old_p4d) != 0 &&
p4d_val(old_p4d) != READ_ONCE(p4d_val(*p4dp)));
@@ -427,15 +460,19 @@ static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end,
phys += next - addr;
} while (p4dp++, addr = next, addr != end);
+out:
p4d_clear_fixmap();
+
+ return ret;
}
-static void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys,
- unsigned long virt, phys_addr_t size,
- pgprot_t prot,
- phys_addr_t (*pgtable_alloc)(enum pgtable_type),
- int flags)
+static int __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys,
+ unsigned long virt, phys_addr_t size,
+ pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type),
+ int flags)
{
+ int ret;
unsigned long addr, end, next;
pgd_t *pgdp = pgd_offset_pgd(pgdir, virt);
@@ -444,7 +481,7 @@ static void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys,
* within a page, we cannot map the region as the caller expects.
*/
if (WARN_ON((phys ^ virt) & ~PAGE_MASK))
- return;
+ return -EINVAL;
phys &= PAGE_MASK;
addr = virt & PAGE_MASK;
@@ -452,25 +489,45 @@ static void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys,
do {
next = pgd_addr_end(addr, end);
- alloc_init_p4d(pgdp, addr, next, phys, prot, pgtable_alloc,
- flags);
+ ret = alloc_init_p4d(pgdp, addr, next, phys, prot, pgtable_alloc,
+ flags);
+ if (ret)
+ return ret;
phys += next - addr;
} while (pgdp++, addr = next, addr != end);
+
+ return 0;
}
-static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
- unsigned long virt, phys_addr_t size,
- pgprot_t prot,
- phys_addr_t (*pgtable_alloc)(enum pgtable_type),
- int flags)
+static int __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
+ unsigned long virt, phys_addr_t size,
+ pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type),
+ int flags)
{
+ int ret;
+
mutex_lock(&fixmap_lock);
- __create_pgd_mapping_locked(pgdir, phys, virt, size, prot,
- pgtable_alloc, flags);
+ ret = __create_pgd_mapping_locked(pgdir, phys, virt, size, prot,
+ pgtable_alloc, flags);
mutex_unlock(&fixmap_lock);
+
+ return ret;
}
-#define INVALID_PHYS_ADDR (-1ULL)
+static void early_create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
+ unsigned long virt, phys_addr_t size,
+ pgprot_t prot,
+ phys_addr_t (*pgtable_alloc)(enum pgtable_type),
+ int flags)
+{
+ int ret;
+
+ ret = __create_pgd_mapping(pgdir, phys, virt, size, prot, pgtable_alloc,
+ flags);
+ if (ret)
+ panic("Failed to create page tables\n");
+}
static phys_addr_t __pgd_pgtable_alloc(struct mm_struct *mm, gfp_t gfp,
enum pgtable_type pgtable_type)
@@ -503,7 +560,7 @@ static phys_addr_t __pgd_pgtable_alloc(struct mm_struct *mm, gfp_t gfp,
}
static phys_addr_t
-try_pgd_pgtable_alloc_init_mm(enum pgtable_type pgtable_type, gfp_t gfp)
+pgd_pgtable_alloc_init_mm_gfp(enum pgtable_type pgtable_type, gfp_t gfp)
{
return __pgd_pgtable_alloc(&init_mm, gfp, pgtable_type);
}
@@ -511,21 +568,13 @@ try_pgd_pgtable_alloc_init_mm(enum pgtable_type pgtable_type, gfp_t gfp)
static phys_addr_t __maybe_unused
pgd_pgtable_alloc_init_mm(enum pgtable_type pgtable_type)
{
- phys_addr_t pa;
-
- pa = __pgd_pgtable_alloc(&init_mm, GFP_PGTABLE_KERNEL, pgtable_type);
- BUG_ON(pa == INVALID_PHYS_ADDR);
- return pa;
+ return pgd_pgtable_alloc_init_mm_gfp(pgtable_type, GFP_PGTABLE_KERNEL);
}
static phys_addr_t
pgd_pgtable_alloc_special_mm(enum pgtable_type pgtable_type)
{
- phys_addr_t pa;
-
- pa = __pgd_pgtable_alloc(NULL, GFP_PGTABLE_KERNEL, pgtable_type);
- BUG_ON(pa == INVALID_PHYS_ADDR);
- return pa;
+ return __pgd_pgtable_alloc(NULL, GFP_PGTABLE_KERNEL, pgtable_type);
}
static void split_contpte(pte_t *ptep)
@@ -546,7 +595,7 @@ static int split_pmd(pmd_t *pmdp, pmd_t pmd, gfp_t gfp, bool to_cont)
pte_t *ptep;
int i;
- pte_phys = try_pgd_pgtable_alloc_init_mm(TABLE_PTE, gfp);
+ pte_phys = pgd_pgtable_alloc_init_mm_gfp(TABLE_PTE, gfp);
if (pte_phys == INVALID_PHYS_ADDR)
return -ENOMEM;
ptep = (pte_t *)phys_to_virt(pte_phys);
@@ -591,7 +640,7 @@ static int split_pud(pud_t *pudp, pud_t pud, gfp_t gfp, bool to_cont)
pmd_t *pmdp;
int i;
- pmd_phys = try_pgd_pgtable_alloc_init_mm(TABLE_PMD, gfp);
+ pmd_phys = pgd_pgtable_alloc_init_mm_gfp(TABLE_PMD, gfp);
if (pmd_phys == INVALID_PHYS_ADDR)
return -ENOMEM;
pmdp = (pmd_t *)phys_to_virt(pmd_phys);
@@ -708,6 +757,30 @@ out:
return ret;
}
+static inline bool force_pte_mapping(void)
+{
+ const bool bbml2 = system_capabilities_finalized() ?
+ system_supports_bbml2_noabort() : cpu_supports_bbml2_noabort();
+
+ if (debug_pagealloc_enabled())
+ return true;
+ if (bbml2)
+ return false;
+ return rodata_full || arm64_kfence_can_set_direct_map() || is_realm_world();
+}
+
+static inline bool split_leaf_mapping_possible(void)
+{
+ /*
+ * !BBML2_NOABORT systems should never run into scenarios where we would
+ * have to split. So exit early and let calling code detect it and raise
+ * a warning.
+ */
+ if (!system_supports_bbml2_noabort())
+ return false;
+ return !force_pte_mapping();
+}
+
static DEFINE_MUTEX(pgtable_split_lock);
int split_kernel_leaf_mapping(unsigned long start, unsigned long end)
@@ -715,12 +788,11 @@ int split_kernel_leaf_mapping(unsigned long start, unsigned long end)
int ret;
/*
- * !BBML2_NOABORT systems should not be trying to change permissions on
- * anything that is not pte-mapped in the first place. Just return early
- * and let the permission change code raise a warning if not already
- * pte-mapped.
+ * Exit early if the region is within a pte-mapped area or if we can't
+ * split. For the latter case, the permission change code will raise a
+ * warning if not already pte-mapped.
*/
- if (!system_supports_bbml2_noabort())
+ if (!split_leaf_mapping_possible() || is_kfence_address((void *)start))
return 0;
/*
@@ -758,30 +830,30 @@ int split_kernel_leaf_mapping(unsigned long start, unsigned long end)
return ret;
}
-static int __init split_to_ptes_pud_entry(pud_t *pudp, unsigned long addr,
- unsigned long next,
- struct mm_walk *walk)
+static int split_to_ptes_pud_entry(pud_t *pudp, unsigned long addr,
+ unsigned long next, struct mm_walk *walk)
{
+ gfp_t gfp = *(gfp_t *)walk->private;
pud_t pud = pudp_get(pudp);
int ret = 0;
if (pud_leaf(pud))
- ret = split_pud(pudp, pud, GFP_ATOMIC, false);
+ ret = split_pud(pudp, pud, gfp, false);
return ret;
}
-static int __init split_to_ptes_pmd_entry(pmd_t *pmdp, unsigned long addr,
- unsigned long next,
- struct mm_walk *walk)
+static int split_to_ptes_pmd_entry(pmd_t *pmdp, unsigned long addr,
+ unsigned long next, struct mm_walk *walk)
{
+ gfp_t gfp = *(gfp_t *)walk->private;
pmd_t pmd = pmdp_get(pmdp);
int ret = 0;
if (pmd_leaf(pmd)) {
if (pmd_cont(pmd))
split_contpmd(pmdp);
- ret = split_pmd(pmdp, pmd, GFP_ATOMIC, false);
+ ret = split_pmd(pmdp, pmd, gfp, false);
/*
* We have split the pmd directly to ptes so there is no need to
@@ -793,9 +865,8 @@ static int __init split_to_ptes_pmd_entry(pmd_t *pmdp, unsigned long addr,
return ret;
}
-static int __init split_to_ptes_pte_entry(pte_t *ptep, unsigned long addr,
- unsigned long next,
- struct mm_walk *walk)
+static int split_to_ptes_pte_entry(pte_t *ptep, unsigned long addr,
+ unsigned long next, struct mm_walk *walk)
{
pte_t pte = __ptep_get(ptep);
@@ -805,12 +876,24 @@ static int __init split_to_ptes_pte_entry(pte_t *ptep, unsigned long addr,
return 0;
}
-static const struct mm_walk_ops split_to_ptes_ops __initconst = {
+static const struct mm_walk_ops split_to_ptes_ops = {
.pud_entry = split_to_ptes_pud_entry,
.pmd_entry = split_to_ptes_pmd_entry,
.pte_entry = split_to_ptes_pte_entry,
};
+static int range_split_to_ptes(unsigned long start, unsigned long end, gfp_t gfp)
+{
+ int ret;
+
+ arch_enter_lazy_mmu_mode();
+ ret = walk_kernel_page_table_range_lockless(start, end,
+ &split_to_ptes_ops, NULL, &gfp);
+ arch_leave_lazy_mmu_mode();
+
+ return ret;
+}
+
static bool linear_map_requires_bbml2 __initdata;
u32 idmap_kpti_bbml2_flag;
@@ -847,11 +930,9 @@ static int __init linear_map_split_to_ptes(void *__unused)
* PTE. The kernel alias remains static throughout runtime so
* can continue to be safely mapped with large mappings.
*/
- ret = walk_kernel_page_table_range_lockless(lstart, kstart,
- &split_to_ptes_ops, NULL, NULL);
+ ret = range_split_to_ptes(lstart, kstart, GFP_ATOMIC);
if (!ret)
- ret = walk_kernel_page_table_range_lockless(kend, lend,
- &split_to_ptes_ops, NULL, NULL);
+ ret = range_split_to_ptes(kend, lend, GFP_ATOMIC);
if (ret)
panic("Failed to split linear map\n");
flush_tlb_kernel_range(lstart, lend);
@@ -903,8 +984,8 @@ void __init create_mapping_noalloc(phys_addr_t phys, unsigned long virt,
&phys, virt);
return;
}
- __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
- NO_CONT_MAPPINGS);
+ early_create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
+ NO_CONT_MAPPINGS);
}
void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
@@ -918,8 +999,8 @@ void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
if (page_mappings_only)
flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
- __create_pgd_mapping(mm->pgd, phys, virt, size, prot,
- pgd_pgtable_alloc_special_mm, flags);
+ early_create_pgd_mapping(mm->pgd, phys, virt, size, prot,
+ pgd_pgtable_alloc_special_mm, flags);
}
static void update_mapping_prot(phys_addr_t phys, unsigned long virt,
@@ -931,8 +1012,8 @@ static void update_mapping_prot(phys_addr_t phys, unsigned long virt,
return;
}
- __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
- NO_CONT_MAPPINGS);
+ early_create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL,
+ NO_CONT_MAPPINGS);
/* flush the TLBs after updating live kernel mappings */
flush_tlb_kernel_range(virt, virt + size);
@@ -941,8 +1022,8 @@ static void update_mapping_prot(phys_addr_t phys, unsigned long virt,
static void __init __map_memblock(pgd_t *pgdp, phys_addr_t start,
phys_addr_t end, pgprot_t prot, int flags)
{
- __create_pgd_mapping(pgdp, start, __phys_to_virt(start), end - start,
- prot, early_pgtable_alloc, flags);
+ early_create_pgd_mapping(pgdp, start, __phys_to_virt(start), end - start,
+ prot, early_pgtable_alloc, flags);
}
void __init mark_linear_text_alias_ro(void)
@@ -1002,6 +1083,33 @@ static void __init arm64_kfence_map_pool(phys_addr_t kfence_pool, pgd_t *pgdp)
memblock_clear_nomap(kfence_pool, KFENCE_POOL_SIZE);
__kfence_pool = phys_to_virt(kfence_pool);
}
+
+bool arch_kfence_init_pool(void)
+{
+ unsigned long start = (unsigned long)__kfence_pool;
+ unsigned long end = start + KFENCE_POOL_SIZE;
+ int ret;
+
+ /* Exit early if we know the linear map is already pte-mapped. */
+ if (!split_leaf_mapping_possible())
+ return true;
+
+ /* Kfence pool is already pte-mapped for the early init case. */
+ if (kfence_early_init)
+ return true;
+
+ mutex_lock(&pgtable_split_lock);
+ ret = range_split_to_ptes(start, end, GFP_PGTABLE_KERNEL);
+ mutex_unlock(&pgtable_split_lock);
+
+ /*
+ * Since the system supports bbml2_noabort, tlb invalidation is not
+ * required here; the pgtable mappings have been split to pte but larger
+ * entries may safely linger in the TLB.
+ */
+
+ return !ret;
+}
#else /* CONFIG_KFENCE */
static inline phys_addr_t arm64_kfence_alloc_pool(void) { return 0; }
@@ -1009,16 +1117,6 @@ static inline void arm64_kfence_map_pool(phys_addr_t kfence_pool, pgd_t *pgdp) {
#endif /* CONFIG_KFENCE */
-static inline bool force_pte_mapping(void)
-{
- bool bbml2 = system_capabilities_finalized() ?
- system_supports_bbml2_noabort() : cpu_supports_bbml2_noabort();
-
- return (!bbml2 && (rodata_full || arm64_kfence_can_set_direct_map() ||
- is_realm_world())) ||
- debug_pagealloc_enabled();
-}
-
static void __init map_mem(pgd_t *pgdp)
{
static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);
@@ -1158,6 +1256,8 @@ static int __init __kpti_install_ng_mappings(void *__unused)
remap_fn = (void *)__pa_symbol(idmap_kpti_install_ng_mappings);
if (!cpu) {
+ int ret;
+
alloc = __get_free_pages(GFP_ATOMIC | __GFP_ZERO, order);
kpti_ng_temp_pgd = (pgd_t *)(alloc + (levels - 1) * PAGE_SIZE);
kpti_ng_temp_alloc = kpti_ng_temp_pgd_pa = __pa(kpti_ng_temp_pgd);
@@ -1178,9 +1278,11 @@ static int __init __kpti_install_ng_mappings(void *__unused)
// covers the PTE[] page itself, the remaining entries are free
// to be used as a ad-hoc fixmap.
//
- __create_pgd_mapping_locked(kpti_ng_temp_pgd, __pa(alloc),
- KPTI_NG_TEMP_VA, PAGE_SIZE, PAGE_KERNEL,
- kpti_ng_pgd_alloc, 0);
+ ret = __create_pgd_mapping_locked(kpti_ng_temp_pgd, __pa(alloc),
+ KPTI_NG_TEMP_VA, PAGE_SIZE, PAGE_KERNEL,
+ kpti_ng_pgd_alloc, 0);
+ if (ret)
+ panic("Failed to create page tables\n");
}
cpu_install_idmap();
@@ -1233,9 +1335,9 @@ static int __init map_entry_trampoline(void)
/* Map only the text into the trampoline page table */
memset(tramp_pg_dir, 0, PGD_SIZE);
- __create_pgd_mapping(tramp_pg_dir, pa_start, TRAMP_VALIAS,
- entry_tramp_text_size(), prot,
- pgd_pgtable_alloc_init_mm, NO_BLOCK_MAPPINGS);
+ early_create_pgd_mapping(tramp_pg_dir, pa_start, TRAMP_VALIAS,
+ entry_tramp_text_size(), prot,
+ pgd_pgtable_alloc_init_mm, NO_BLOCK_MAPPINGS);
/* Map both the text and data into the kernel page table */
for (i = 0; i < DIV_ROUND_UP(entry_tramp_text_size(), PAGE_SIZE); i++)
@@ -1877,23 +1979,28 @@ int arch_add_memory(int nid, u64 start, u64 size,
if (force_pte_mapping())
flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
- __create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start),
- size, params->pgprot, pgd_pgtable_alloc_init_mm,
- flags);
+ ret = __create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start),
+ size, params->pgprot, pgd_pgtable_alloc_init_mm,
+ flags);
+ if (ret)
+ goto err;
memblock_clear_nomap(start, size);
ret = __add_pages(nid, start >> PAGE_SHIFT, size >> PAGE_SHIFT,
params);
if (ret)
- __remove_pgd_mapping(swapper_pg_dir,
- __phys_to_virt(start), size);
- else {
- /* Address of hotplugged memory can be smaller */
- max_pfn = max(max_pfn, PFN_UP(start + size));
- max_low_pfn = max_pfn;
- }
+ goto err;
+
+ /* Address of hotplugged memory can be smaller */
+ max_pfn = max(max_pfn, PFN_UP(start + size));
+ max_low_pfn = max_pfn;
+
+ return 0;
+err:
+ __remove_pgd_mapping(swapper_pg_dir,
+ __phys_to_virt(start), size);
return ret;
}
diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
index 5135f2d66958..f0e784b963e6 100644
--- a/arch/arm64/mm/pageattr.c
+++ b/arch/arm64/mm/pageattr.c
@@ -148,7 +148,7 @@ static int change_memory_common(unsigned long addr, int numpages,
unsigned long size = PAGE_SIZE * numpages;
unsigned long end = start + size;
struct vm_struct *area;
- int i;
+ int ret;
if (!PAGE_ALIGNED(addr)) {
start &= PAGE_MASK;
@@ -184,9 +184,13 @@ static int change_memory_common(unsigned long addr, int numpages,
*/
if (rodata_full && (pgprot_val(set_mask) == PTE_RDONLY ||
pgprot_val(clear_mask) == PTE_RDONLY)) {
- for (i = 0; i < area->nr_pages; i++) {
- __change_memory_common((u64)page_address(area->pages[i]),
- PAGE_SIZE, set_mask, clear_mask);
+ unsigned long idx = (start - (unsigned long)kasan_reset_tag(area->addr))
+ >> PAGE_SHIFT;
+ for (; numpages; idx++, numpages--) {
+ ret = __change_memory_common((u64)page_address(area->pages[idx]),
+ PAGE_SIZE, set_mask, clear_mask);
+ if (ret)
+ return ret;
}
}
diff --git a/arch/arm64/mm/pgd.c b/arch/arm64/mm/pgd.c
index 8160cff35089..bf5110b91e2f 100644
--- a/arch/arm64/mm/pgd.c
+++ b/arch/arm64/mm/pgd.c
@@ -56,7 +56,7 @@ void __init pgtable_cache_init(void)
* With 52-bit physical addresses, the architecture requires the
* top-level table to be aligned to at least 64 bytes.
*/
- BUILD_BUG_ON(PGD_SIZE < 64);
+ BUILD_BUG_ON(!IS_ALIGNED(PGD_SIZE, 64));
#endif
/*
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index 86818511962b..01e868116448 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -23,15 +23,18 @@
#include <asm/sysreg.h>
#ifdef CONFIG_ARM64_64K_PAGES
-#define TCR_TG_FLAGS TCR_TG0_64K | TCR_TG1_64K
+#define TCR_TG_FLAGS ((TCR_EL1_TG0_64K << TCR_EL1_TG0_SHIFT) |\
+ (TCR_EL1_TG1_64K << TCR_EL1_TG1_SHIFT))
#elif defined(CONFIG_ARM64_16K_PAGES)
-#define TCR_TG_FLAGS TCR_TG0_16K | TCR_TG1_16K
+#define TCR_TG_FLAGS ((TCR_EL1_TG0_16K << TCR_EL1_TG0_SHIFT) |\
+ (TCR_EL1_TG1_16K << TCR_EL1_TG1_SHIFT))
#else /* CONFIG_ARM64_4K_PAGES */
-#define TCR_TG_FLAGS TCR_TG0_4K | TCR_TG1_4K
+#define TCR_TG_FLAGS ((TCR_EL1_TG0_4K << TCR_EL1_TG0_SHIFT) |\
+ (TCR_EL1_TG1_4K << TCR_EL1_TG1_SHIFT))
#endif
#ifdef CONFIG_RANDOMIZE_BASE
-#define TCR_KASLR_FLAGS TCR_NFD1
+#define TCR_KASLR_FLAGS TCR_EL1_NFD1
#else
#define TCR_KASLR_FLAGS 0
#endif
@@ -40,23 +43,30 @@
#define TCR_CACHE_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA
#ifdef CONFIG_KASAN_SW_TAGS
-#define TCR_KASAN_SW_FLAGS TCR_TBI1 | TCR_TBID1
+#define TCR_KASAN_SW_FLAGS TCR_EL1_TBI1 | TCR_EL1_TBID1
#else
#define TCR_KASAN_SW_FLAGS 0
#endif
#ifdef CONFIG_KASAN_HW_TAGS
-#define TCR_MTE_FLAGS TCR_TCMA1 | TCR_TBI1 | TCR_TBID1
+#define TCR_MTE_FLAGS TCR_EL1_TCMA1 | TCR_EL1_TBI1 | TCR_EL1_TBID1
#elif defined(CONFIG_ARM64_MTE)
/*
* The mte_zero_clear_page_tags() implementation uses DC GZVA, which relies on
* TBI being enabled at EL1.
*/
-#define TCR_MTE_FLAGS TCR_TBI1 | TCR_TBID1
+#define TCR_MTE_FLAGS TCR_EL1_TBI1 | TCR_EL1_TBID1
#else
#define TCR_MTE_FLAGS 0
#endif
+#define TCR_IRGN_WBWA ((TCR_EL1_IRGN0_WBWA << TCR_EL1_IRGN0_SHIFT) |\
+ (TCR_EL1_IRGN1_WBWA << TCR_EL1_IRGN1_SHIFT))
+#define TCR_ORGN_WBWA ((TCR_EL1_ORGN0_WBWA << TCR_EL1_ORGN0_SHIFT) |\
+ (TCR_EL1_ORGN1_WBWA << TCR_EL1_ORGN1_SHIFT))
+#define TCR_SHARED ((TCR_EL1_SH0_INNER << TCR_EL1_SH0_SHIFT) |\
+ (TCR_EL1_SH1_INNER << TCR_EL1_SH1_SHIFT))
+
/*
* Default MAIR_EL1. MT_NORMAL_TAGGED is initially mapped as Normal memory and
* changed during mte_cpu_setup to Normal Tagged if the system supports MTE.
@@ -129,7 +139,7 @@ SYM_FUNC_START(cpu_do_resume)
/* Don't change t0sz here, mask those bits when restoring */
mrs x7, tcr_el1
- bfi x8, x7, TCR_T0SZ_OFFSET, TCR_TxSZ_WIDTH
+ bfi x8, x7, TCR_EL1_T0SZ_SHIFT, TCR_EL1_T0SZ_WIDTH
msr tcr_el1, x8
msr vbar_el1, x9
@@ -481,8 +491,8 @@ SYM_FUNC_START(__cpu_setup)
tcr2 .req x15
mov_q mair, MAIR_EL1_SET
mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS_MIN) | TCR_CACHE_FLAGS | \
- TCR_SHARED | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \
- TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS
+ TCR_SHARED | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_EL1_AS | \
+ TCR_EL1_TBI0 | TCR_EL1_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS
mov tcr2, xzr
tcr_clear_errata_bits tcr, x9, x5
@@ -492,7 +502,7 @@ SYM_FUNC_START(__cpu_setup)
alternative_if ARM64_HAS_VA52
tcr_set_t1sz tcr, x9
#ifdef CONFIG_ARM64_LPA2
- orr tcr, tcr, #TCR_DS
+ orr tcr, tcr, #TCR_EL1_DS
#endif
alternative_else_nop_endif
#endif
@@ -500,7 +510,7 @@ alternative_else_nop_endif
/*
* Set the IPS bits in TCR_EL1.
*/
- tcr_compute_pa_size tcr, #TCR_IPS_SHIFT, x5, x6
+ tcr_compute_pa_size tcr, #TCR_EL1_IPS_SHIFT, x5, x6
#ifdef CONFIG_ARM64_HW_AFDBM
/*
* Enable hardware update of the Access Flags bit.
@@ -510,7 +520,7 @@ alternative_else_nop_endif
mrs x9, ID_AA64MMFR1_EL1
ubfx x9, x9, ID_AA64MMFR1_EL1_HAFDBS_SHIFT, #4
cbz x9, 1f
- orr tcr, tcr, #TCR_HA // hardware Access flag update
+ orr tcr, tcr, #TCR_EL1_HA // hardware Access flag update
#ifdef CONFIG_ARM64_HAFT
cmp x9, ID_AA64MMFR1_EL1_HAFDBS_HAFT
b.lt 1f
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index ab83089c3d8f..74dd29816f36 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -1213,6 +1213,7 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx,
u8 src = bpf2a64[insn->src_reg];
const u8 tmp = bpf2a64[TMP_REG_1];
const u8 tmp2 = bpf2a64[TMP_REG_2];
+ const u8 tmp3 = bpf2a64[TMP_REG_3];
const u8 fp = bpf2a64[BPF_REG_FP];
const u8 arena_vm_base = bpf2a64[ARENA_VM_START];
const u8 priv_sp = bpf2a64[PRIVATE_SP];
@@ -1451,6 +1452,10 @@ emit_bswap_uxt:
emit(A64_ASR(is64, dst, dst, imm), ctx);
break;
+ /* JUMP reg */
+ case BPF_JMP | BPF_JA | BPF_X:
+ emit(A64_BR(dst), ctx);
+ break;
/* JUMP off */
case BPF_JMP | BPF_JA:
case BPF_JMP32 | BPF_JA:
@@ -1757,8 +1762,8 @@ emit_cond_jmp:
case BPF_ST | BPF_PROBE_MEM32 | BPF_W:
case BPF_ST | BPF_PROBE_MEM32 | BPF_DW:
if (BPF_MODE(insn->code) == BPF_PROBE_MEM32) {
- emit(A64_ADD(1, tmp2, dst, arena_vm_base), ctx);
- dst = tmp2;
+ emit(A64_ADD(1, tmp3, dst, arena_vm_base), ctx);
+ dst = tmp3;
}
if (dst == fp) {
dst_adj = ctx->priv_sp_used ? priv_sp : A64_SP;
@@ -2230,6 +2235,13 @@ skip_init_ctx:
for (i = 0; i <= prog->len; i++)
ctx.offset[i] *= AARCH64_INSN_SIZE;
bpf_prog_fill_jited_linfo(prog, ctx.offset + 1);
+ /*
+ * The bpf_prog_update_insn_ptrs function expects offsets to
+ * point to the first byte of the jitted instruction (unlike
+ * the bpf_prog_fill_jited_linfo above, which, for historical
+ * reasons, expects to point to the next instruction)
+ */
+ bpf_prog_update_insn_ptrs(prog, ctx.offset, ctx.ro_image);
out_off:
if (!ro_header && priv_stack_ptr) {
free_percpu(priv_stack_ptr);
@@ -2922,8 +2934,9 @@ static int gen_branch_or_nop(enum aarch64_insn_branch_type type, void *ip,
* The dummy_tramp is used to prevent another CPU from jumping to unknown
* locations during the patching process, making the patching process easier.
*/
-int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
- void *old_addr, void *new_addr)
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
+ enum bpf_text_poke_type new_t, void *old_addr,
+ void *new_addr)
{
int ret;
u32 old_insn;
@@ -2967,14 +2980,13 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
!poking_bpf_entry))
return -EINVAL;
- if (poke_type == BPF_MOD_CALL)
- branch_type = AARCH64_INSN_BRANCH_LINK;
- else
- branch_type = AARCH64_INSN_BRANCH_NOLINK;
-
+ branch_type = old_t == BPF_MOD_CALL ? AARCH64_INSN_BRANCH_LINK :
+ AARCH64_INSN_BRANCH_NOLINK;
if (gen_branch_or_nop(branch_type, ip, old_addr, plt, &old_insn) < 0)
return -EFAULT;
+ branch_type = new_t == BPF_MOD_CALL ? AARCH64_INSN_BRANCH_LINK :
+ AARCH64_INSN_BRANCH_NOLINK;
if (gen_branch_or_nop(branch_type, ip, new_addr, plt, &new_insn) < 0)
return -EFAULT;
@@ -3053,7 +3065,7 @@ bool bpf_jit_supports_exceptions(void)
/* We unwind through both kernel frames starting from within bpf_throw
* call and BPF frames. Therefore we require FP unwinder to be enabled
* to walk kernel frames and reach BPF frames in the stack trace.
- * ARM64 kernel is aways compiled with CONFIG_FRAME_POINTER=y
+ * ARM64 kernel is always compiled with CONFIG_FRAME_POINTER=y
*/
return true;
}
diff --git a/arch/arm64/tools/gen-sysreg.awk b/arch/arm64/tools/gen-sysreg.awk
index bbbb812603e8..86860ab672dc 100755
--- a/arch/arm64/tools/gen-sysreg.awk
+++ b/arch/arm64/tools/gen-sysreg.awk
@@ -44,21 +44,38 @@ function expect_fields(nf) {
# Print a CPP macro definition, padded with spaces so that the macro bodies
# line up in a column
-function define(name, val) {
- printf "%-56s%s\n", "#define " name, val
+function define(prefix, name, val) {
+ printf "%-56s%s\n", "#define " prefix name, val
+}
+
+# Same as above, but without a prefix
+function define_reg(name, val) {
+ define(null, name, val)
}
# Print standard BITMASK/SHIFT/WIDTH CPP definitions for a field
-function define_field(reg, field, msb, lsb) {
- define(reg "_" field, "GENMASK(" msb ", " lsb ")")
- define(reg "_" field "_MASK", "GENMASK(" msb ", " lsb ")")
- define(reg "_" field "_SHIFT", lsb)
- define(reg "_" field "_WIDTH", msb - lsb + 1)
+function define_field(prefix, reg, field, msb, lsb) {
+ define(prefix, reg "_" field, "GENMASK(" msb ", " lsb ")")
+ define(prefix, reg "_" field "_MASK", "GENMASK(" msb ", " lsb ")")
+ define(prefix, reg "_" field "_SHIFT", lsb)
+ define(prefix, reg "_" field "_WIDTH", msb - lsb + 1)
}
# Print a field _SIGNED definition for a field
-function define_field_sign(reg, field, sign) {
- define(reg "_" field "_SIGNED", sign)
+function define_field_sign(prefix, reg, field, sign) {
+ define(prefix, reg "_" field "_SIGNED", sign)
+}
+
+# Print the Res0, Res1, Unkn masks
+function define_resx_unkn(prefix, reg, res0, res1, unkn) {
+ if (res0 != null)
+ define(prefix, reg "_RES0", "(" res0 ")")
+ if (res1 != null)
+ define(prefix, reg "_RES1", "(" res1 ")")
+ if (unkn != null)
+ define(prefix, reg "_UNKN", "(" unkn ")")
+ if (res0 != null || res1 != null || unkn != null)
+ print ""
}
# Parse a "<msb>[:<lsb>]" string into the global variables @msb and @lsb
@@ -128,18 +145,17 @@ $1 == "SysregFields" && block_current() == "Root" {
next_bit = 63
+ delete seen_prefixes
+
next
}
$1 == "EndSysregFields" && block_current() == "SysregFields" {
expect_fields(1)
- if (next_bit > 0)
+ if (next_bit >= 0)
fatal("Unspecified bits in " reg)
- define(reg "_RES0", "(" res0 ")")
- define(reg "_RES1", "(" res1 ")")
- define(reg "_UNKN", "(" unkn ")")
- print ""
+ define_resx_unkn(prefix, reg, res0, res1, unkn)
reg = null
res0 = null
@@ -170,35 +186,31 @@ $1 == "Sysreg" && block_current() == "Root" {
fatal("Duplicate Sysreg definition for " reg)
defined_regs[reg] = 1
- define("REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_" op2)
- define("SYS_" reg, "sys_reg(" op0 ", " op1 ", " crn ", " crm ", " op2 ")")
+ define_reg("REG_" reg, "S" op0 "_" op1 "_C" crn "_C" crm "_" op2)
+ define_reg("SYS_" reg, "sys_reg(" op0 ", " op1 ", " crn ", " crm ", " op2 ")")
- define("SYS_" reg "_Op0", op0)
- define("SYS_" reg "_Op1", op1)
- define("SYS_" reg "_CRn", crn)
- define("SYS_" reg "_CRm", crm)
- define("SYS_" reg "_Op2", op2)
+ define_reg("SYS_" reg "_Op0", op0)
+ define_reg("SYS_" reg "_Op1", op1)
+ define_reg("SYS_" reg "_CRn", crn)
+ define_reg("SYS_" reg "_CRm", crm)
+ define_reg("SYS_" reg "_Op2", op2)
print ""
+ prefix = null
next_bit = 63
+ delete seen_prefixes
+
next
}
$1 == "EndSysreg" && block_current() == "Sysreg" {
expect_fields(1)
- if (next_bit > 0)
+ if (next_bit >= 0)
fatal("Unspecified bits in " reg)
- if (res0 != null)
- define(reg "_RES0", "(" res0 ")")
- if (res1 != null)
- define(reg "_RES1", "(" res1 ")")
- if (unkn != null)
- define(reg "_UNKN", "(" unkn ")")
- if (res0 != null || res1 != null || unkn != null)
- print ""
+ define_resx_unkn(prefix, reg, res0, res1, unkn)
reg = null
op0 = null
@@ -209,6 +221,7 @@ $1 == "EndSysreg" && block_current() == "Sysreg" {
res0 = null
res1 = null
unkn = null
+ prefix = null
block_pop()
next
@@ -225,7 +238,7 @@ $1 == "EndSysreg" && block_current() == "Sysreg" {
print "/* For " reg " fields see " $2 " */"
print ""
- next_bit = 0
+ next_bit = -1
res0 = null
res1 = null
unkn = null
@@ -233,8 +246,7 @@ $1 == "EndSysreg" && block_current() == "Sysreg" {
next
}
-
-$1 == "Res0" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Res0" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
expect_fields(2)
parse_bitdef(reg, "RES0", $2)
field = "RES0_" msb "_" lsb
@@ -244,7 +256,7 @@ $1 == "Res0" && (block_current() == "Sysreg" || block_current() == "SysregFields
next
}
-$1 == "Res1" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Res1" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
expect_fields(2)
parse_bitdef(reg, "RES1", $2)
field = "RES1_" msb "_" lsb
@@ -254,7 +266,7 @@ $1 == "Res1" && (block_current() == "Sysreg" || block_current() == "SysregFields
next
}
-$1 == "Unkn" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Unkn" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
expect_fields(2)
parse_bitdef(reg, "UNKN", $2)
field = "UNKN_" msb "_" lsb
@@ -264,62 +276,62 @@ $1 == "Unkn" && (block_current() == "Sysreg" || block_current() == "SysregFields
next
}
-$1 == "Field" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Field" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
expect_fields(3)
field = $3
parse_bitdef(reg, field, $2)
- define_field(reg, field, msb, lsb)
+ define_field(prefix, reg, field, msb, lsb)
print ""
next
}
-$1 == "Raz" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Raz" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
expect_fields(2)
parse_bitdef(reg, field, $2)
next
}
-$1 == "SignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "SignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
block_push("Enum")
expect_fields(3)
field = $3
parse_bitdef(reg, field, $2)
- define_field(reg, field, msb, lsb)
- define_field_sign(reg, field, "true")
+ define_field(prefix, reg, field, msb, lsb)
+ define_field_sign(prefix, reg, field, "true")
delete seen_enum_vals
next
}
-$1 == "UnsignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "UnsignedEnum" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
block_push("Enum")
expect_fields(3)
field = $3
parse_bitdef(reg, field, $2)
- define_field(reg, field, msb, lsb)
- define_field_sign(reg, field, "false")
+ define_field(prefix, reg, field, msb, lsb)
+ define_field_sign(prefix, reg, field, "false")
delete seen_enum_vals
next
}
-$1 == "Enum" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+$1 == "Enum" && (block_current() == "Sysreg" || block_current() == "SysregFields" || block_current() == "Prefix") {
block_push("Enum")
expect_fields(3)
field = $3
parse_bitdef(reg, field, $2)
- define_field(reg, field, msb, lsb)
+ define_field(prefix, reg, field, msb, lsb)
delete seen_enum_vals
@@ -349,7 +361,47 @@ $1 == "EndEnum" && block_current() == "Enum" {
fatal("Duplicate Enum value " val " for " name)
seen_enum_vals[val] = 1
- define(reg "_" field "_" name, "UL(" val ")")
+ define(prefix, reg "_" field "_" name, "UL(" val ")")
+ next
+}
+
+$1 == "Prefix" && (block_current() == "Sysreg" || block_current() == "SysregFields") {
+ block_push("Prefix")
+
+ expect_fields(2)
+
+ if (next_bit < 63)
+ fatal("Prefixed fields must precede non-prefixed fields (" reg ")")
+
+ prefix = $2 "_"
+
+ if (prefix in seen_prefixes)
+ fatal("Duplicate prefix " prefix " for " reg)
+ seen_prefixes[prefix] = 1
+
+ res0 = "UL(0)"
+ res1 = "UL(0)"
+ unkn = "UL(0)"
+ next_bit = 63
+
+ next
+}
+
+$1 == "EndPrefix" && block_current() == "Prefix" {
+ expect_fields(1)
+ if (next_bit >= 0)
+ fatal("Unspecified bits in prefix " prefix " for " reg)
+
+ define_resx_unkn(prefix, reg, res0, res1, unkn)
+
+ prefix = null
+ res0 = "UL(0)"
+ res1 = "UL(0)"
+ unkn = "UL(0)"
+ next_bit = 63
+
+ block_pop()
+
next
}
diff --git a/arch/arm64/tools/syscall_32.tbl b/arch/arm64/tools/syscall_32.tbl
index 8d9088bc577d..8cdfe5d4dac9 100644
--- a/arch/arm64/tools/syscall_32.tbl
+++ b/arch/arm64/tools/syscall_32.tbl
@@ -481,3 +481,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index 1c6cdf9d54bb..8921b51866d6 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -4669,6 +4669,27 @@ Field 1 V3
Field 0 En
EndSysreg
+Sysreg ICH_VMCR_EL2 3 4 12 11 7
+Prefix FEAT_GCIE
+Res0 63:32
+Field 31:27 VPMR
+Res0 26:1
+Field 0 EN
+EndPrefix
+Res0 63:32
+Field 31:24 VPMR
+Field 23:21 VBPR0
+Field 20:18 VBPR1
+Res0 17:10
+Field 9 VEOIM
+Res0 8:5
+Field 4 VCBPR
+Field 3 VFIQEn
+Field 2 VAckCtl
+Field 1 VENG1
+Field 0 VENG0
+EndSysreg
+
Sysreg CONTEXTIDR_EL2 3 4 13 0 1
Fields CONTEXTIDR_ELx
EndSysreg
diff --git a/arch/csky/abiv2/cacheflush.c b/arch/csky/abiv2/cacheflush.c
index 876028b1083f..064b0f0f95ca 100644
--- a/arch/csky/abiv2/cacheflush.c
+++ b/arch/csky/abiv2/cacheflush.c
@@ -21,7 +21,7 @@ void update_mmu_cache_range(struct vm_fault *vmf, struct vm_area_struct *vma,
folio = page_folio(pfn_to_page(pfn));
- if (test_and_set_bit(PG_dcache_clean, &folio->flags))
+ if (test_and_set_bit(PG_dcache_clean, &folio->flags.f))
return;
icache_inv_range(address, address + nr*PAGE_SIZE);
diff --git a/arch/csky/abiv2/inc/abi/cacheflush.h b/arch/csky/abiv2/inc/abi/cacheflush.h
index 6513ac5d2578..da51a0f02391 100644
--- a/arch/csky/abiv2/inc/abi/cacheflush.h
+++ b/arch/csky/abiv2/inc/abi/cacheflush.h
@@ -20,8 +20,8 @@
static inline void flush_dcache_folio(struct folio *folio)
{
- if (test_bit(PG_dcache_clean, &folio->flags))
- clear_bit(PG_dcache_clean, &folio->flags);
+ if (test_bit(PG_dcache_clean, &folio->flags.f))
+ clear_bit(PG_dcache_clean, &folio->flags.f);
}
#define flush_dcache_folio flush_dcache_folio
diff --git a/arch/hexagon/configs/comet_defconfig b/arch/hexagon/configs/comet_defconfig
index 22d7f8ac58a3..83b7626550c8 100644
--- a/arch/hexagon/configs/comet_defconfig
+++ b/arch/hexagon/configs/comet_defconfig
@@ -69,7 +69,6 @@ CONFIG_INET=y
# CONFIG_INET_DIAG is not set
# CONFIG_IPV6 is not set
CONFIG_CRYPTO_MD5=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
CONFIG_FRAME_WARN=0
CONFIG_MAGIC_SYSRQ=y
diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
index dc5bd3f1b8d2..96ca1a688984 100644
--- a/arch/loongarch/Makefile
+++ b/arch/loongarch/Makefile
@@ -109,7 +109,7 @@ endif
ifdef CONFIG_RUSTC_HAS_ANNOTATE_TABLEJUMP
KBUILD_RUSTFLAGS += -Cllvm-args=--loongarch-annotate-tablejump
else
-KBUILD_RUSTFLAGS += -Zno-jump-tables # keep compatibility with older compilers
+KBUILD_RUSTFLAGS += $(if $(call rustc-min-version,109300),-Cjump-tables=n,-Zno-jump-tables) # keep compatibility with older compilers
endif
ifdef CONFIG_LTO_CLANG
# The annotate-tablejump option can not be passed to LLVM backend when LTO is enabled.
diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig
index 3e838c229cd5..50e1304e7a6f 100644
--- a/arch/loongarch/configs/loongson3_defconfig
+++ b/arch/loongarch/configs/loongson3_defconfig
@@ -917,7 +917,6 @@ CONFIG_MMC=y
CONFIG_MMC_LOONGSON2=m
CONFIG_INFINIBAND=m
CONFIG_EDAC=y
-# CONFIG_EDAC_LEGACY_SYSFS is not set
CONFIG_EDAC_LOONGSON=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_EFI=y
diff --git a/arch/loongarch/include/asm/bug.h b/arch/loongarch/include/asm/bug.h
index f6f254f2c5db..d090a5bec5eb 100644
--- a/arch/loongarch/include/asm/bug.h
+++ b/arch/loongarch/include/asm/bug.h
@@ -11,7 +11,7 @@
#else
#define __BUGVERBOSE_LOCATION(file, line) \
.pushsection .rodata.str, "aMS", @progbits, 1; \
- 10002: .string file; \
+ 10002: .ascii file "\0"; \
.popsection; \
\
.long 10002b - .; \
@@ -20,39 +20,38 @@
#endif
#ifndef CONFIG_GENERIC_BUG
-#define __BUG_ENTRY(flags)
+#define __BUG_ENTRY(cond_str, flags)
#else
-#define __BUG_ENTRY(flags) \
+#define __BUG_ENTRY(cond_str, flags) \
.pushsection __bug_table, "aw"; \
.align 2; \
10000: .long 10001f - .; \
- _BUGVERBOSE_LOCATION(__FILE__, __LINE__) \
- .short flags; \
+ _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
+ .short flags; \
.popsection; \
10001:
#endif
-#define ASM_BUG_FLAGS(flags) \
- __BUG_ENTRY(flags) \
+#define ASM_BUG_FLAGS(cond_str, flags) \
+ __BUG_ENTRY(cond_str, flags) \
break BRK_BUG;
-#define ASM_BUG() ASM_BUG_FLAGS(0)
+#define ASM_BUG() ASM_BUG_FLAGS("", 0)
-#define __BUG_FLAGS(flags, extra) \
- asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags)) \
- extra);
+#define __BUG_FLAGS(cond_str, flags, extra) \
+ asm_inline volatile (__stringify(ASM_BUG_FLAGS(cond_str, flags)) extra);
-#define __WARN_FLAGS(flags) \
+#define __WARN_FLAGS(cond_str, flags) \
do { \
instrumentation_begin(); \
- __BUG_FLAGS(BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\
+ __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\
instrumentation_end(); \
} while (0)
#define BUG() \
do { \
instrumentation_begin(); \
- __BUG_FLAGS(0, ""); \
+ __BUG_FLAGS("", 0, ""); \
unreachable(); \
} while (0)
diff --git a/arch/loongarch/include/asm/cpu-features.h b/arch/loongarch/include/asm/cpu-features.h
index fc83bb32f9f0..bd5f0457ad21 100644
--- a/arch/loongarch/include/asm/cpu-features.h
+++ b/arch/loongarch/include/asm/cpu-features.h
@@ -67,6 +67,8 @@
#define cpu_has_hypervisor cpu_opt(LOONGARCH_CPU_HYPERVISOR)
#define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
#define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
+#define cpu_has_msgint cpu_opt(LOONGARCH_CPU_MSGINT)
#define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
+#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
#endif /* __ASM_CPU_FEATURES_H */
diff --git a/arch/loongarch/include/asm/cpu.h b/arch/loongarch/include/asm/cpu.h
index dfb982fe8701..f3efb00b6141 100644
--- a/arch/loongarch/include/asm/cpu.h
+++ b/arch/loongarch/include/asm/cpu.h
@@ -55,6 +55,27 @@ enum cpu_type_enum {
CPU_LAST
};
+static inline char *id_to_core_name(unsigned int id)
+{
+ if ((id & PRID_COMP_MASK) != PRID_COMP_LOONGSON)
+ return "Unknown";
+
+ switch (id & PRID_SERIES_MASK) {
+ case PRID_SERIES_LA132:
+ return "LA132";
+ case PRID_SERIES_LA264:
+ return "LA264";
+ case PRID_SERIES_LA364:
+ return "LA364";
+ case PRID_SERIES_LA464:
+ return "LA464";
+ case PRID_SERIES_LA664:
+ return "LA664";
+ default:
+ return "Unknown";
+ }
+}
+
#endif /* !__ASSEMBLER__ */
/*
@@ -101,7 +122,9 @@ enum cpu_type_enum {
#define CPU_FEATURE_HYPERVISOR 26 /* CPU has hypervisor (running in VM) */
#define CPU_FEATURE_PTW 27 /* CPU has hardware page table walker */
#define CPU_FEATURE_LSPW 28 /* CPU has LSPW (lddir/ldpte instructions) */
-#define CPU_FEATURE_AVECINT 29 /* CPU has AVEC interrupt */
+#define CPU_FEATURE_MSGINT 29 /* CPU has MSG interrupt */
+#define CPU_FEATURE_AVECINT 30 /* CPU has AVEC interrupt */
+#define CPU_FEATURE_REDIRECTINT 31 /* CPU has interrupt remapping */
#define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
#define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
@@ -132,6 +155,8 @@ enum cpu_type_enum {
#define LOONGARCH_CPU_HYPERVISOR BIT_ULL(CPU_FEATURE_HYPERVISOR)
#define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
#define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
+#define LOONGARCH_CPU_MSGINT BIT_ULL(CPU_FEATURE_MSGINT)
#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
+#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
#endif /* _ASM_CPU_H */
diff --git a/arch/loongarch/include/asm/hw_breakpoint.h b/arch/loongarch/include/asm/hw_breakpoint.h
index 13b2462f3d8c..5faa97a87a9e 100644
--- a/arch/loongarch/include/asm/hw_breakpoint.h
+++ b/arch/loongarch/include/asm/hw_breakpoint.h
@@ -134,13 +134,13 @@ static inline void hw_breakpoint_thread_switch(struct task_struct *next)
/* Determine number of BRP registers available. */
static inline int get_num_brps(void)
{
- return csr_read64(LOONGARCH_CSR_FWPC) & CSR_FWPC_NUM;
+ return csr_read32(LOONGARCH_CSR_FWPC) & CSR_FWPC_NUM;
}
/* Determine number of WRP registers available. */
static inline int get_num_wrps(void)
{
- return csr_read64(LOONGARCH_CSR_MWPC) & CSR_MWPC_NUM;
+ return csr_read32(LOONGARCH_CSR_MWPC) & CSR_MWPC_NUM;
}
#endif /* __KERNEL__ */
diff --git a/arch/loongarch/include/asm/io.h b/arch/loongarch/include/asm/io.h
index eaff72b38dc8..0130185e0349 100644
--- a/arch/loongarch/include/asm/io.h
+++ b/arch/loongarch/include/asm/io.h
@@ -14,7 +14,7 @@
#include <asm/pgtable-bits.h>
#include <asm/string.h>
-extern void __init __iomem *early_ioremap(u64 phys_addr, unsigned long size);
+extern void __init __iomem *early_ioremap(phys_addr_t phys_addr, unsigned long size);
extern void __init early_iounmap(void __iomem *addr, unsigned long size);
#define early_memremap early_ioremap
@@ -25,6 +25,9 @@ extern void __init early_iounmap(void __iomem *addr, unsigned long size);
static inline void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size,
pgprot_t prot)
{
+ if (offset > TO_PHYS_MASK)
+ return NULL;
+
switch (pgprot_val(prot) & _CACHE_MASK) {
case _CACHE_CC:
return (void __iomem *)(unsigned long)(CACHE_BASE + offset);
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index 09dfd7eb406e..3de03cb864b2 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -128,6 +128,7 @@
#define CPUCFG6_PMNUM GENMASK(7, 4)
#define CPUCFG6_PMNUM_SHIFT 4
#define CPUCFG6_PMBITS GENMASK(13, 8)
+#define CPUCFG6_PMBITS_SHIFT 8
#define CPUCFG6_UPM BIT(14)
#define LOONGARCH_CPUCFG16 0x10
@@ -1137,6 +1138,7 @@
#define IOCSRF_FLATMODE BIT_ULL(10)
#define IOCSRF_VM BIT_ULL(11)
#define IOCSRF_AVEC BIT_ULL(15)
+#define IOCSRF_REDIRECT BIT_ULL(16)
#define LOONGARCH_IOCSR_VENDOR 0x10
diff --git a/arch/loongarch/include/asm/pgalloc.h b/arch/loongarch/include/asm/pgalloc.h
index 1c63a9d9a6d3..08dcc698ec18 100644
--- a/arch/loongarch/include/asm/pgalloc.h
+++ b/arch/loongarch/include/asm/pgalloc.h
@@ -88,7 +88,7 @@ static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address)
static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
{
pud_t *pud;
- struct ptdesc *ptdesc = pagetable_alloc(GFP_KERNEL & ~__GFP_HIGHMEM, 0);
+ struct ptdesc *ptdesc = pagetable_alloc(GFP_KERNEL, 0);
if (!ptdesc)
return NULL;
diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h
index bd128696e96d..03fb60432fde 100644
--- a/arch/loongarch/include/asm/pgtable.h
+++ b/arch/loongarch/include/asm/pgtable.h
@@ -424,6 +424,9 @@ static inline unsigned long pte_accessible(struct mm_struct *mm, pte_t a)
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
{
+ if (pte_val(pte) & _PAGE_DIRTY)
+ pte_val(pte) |= _PAGE_MODIFIED;
+
return __pte((pte_val(pte) & _PAGE_CHG_MASK) |
(pgprot_val(newprot) & ~_PAGE_CHG_MASK));
}
@@ -547,9 +550,11 @@ static inline struct page *pmd_page(pmd_t pmd)
static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
{
- pmd_val(pmd) = (pmd_val(pmd) & _HPAGE_CHG_MASK) |
- (pgprot_val(newprot) & ~_HPAGE_CHG_MASK);
- return pmd;
+ if (pmd_val(pmd) & _PAGE_DIRTY)
+ pmd_val(pmd) |= _PAGE_MODIFIED;
+
+ return __pmd((pmd_val(pmd) & _HPAGE_CHG_MASK) |
+ (pgprot_val(newprot) & ~_HPAGE_CHG_MASK));
}
static inline pmd_t pmd_mkinvalid(pmd_t pmd)
diff --git a/arch/loongarch/include/uapi/asm/ptrace.h b/arch/loongarch/include/uapi/asm/ptrace.h
index aafb3cd9e943..215e0f9e8aa3 100644
--- a/arch/loongarch/include/uapi/asm/ptrace.h
+++ b/arch/loongarch/include/uapi/asm/ptrace.h
@@ -10,10 +10,6 @@
#include <linux/types.h>
-#ifndef __KERNEL__
-#include <stdint.h>
-#endif
-
/*
* For PTRACE_{POKE,PEEK}USR. 0 - 31 are GPRs,
* 32 is syscall's original ARG0, 33 is PC, 34 is BADVADDR.
@@ -41,44 +37,44 @@ struct user_pt_regs {
} __attribute__((aligned(8)));
struct user_fp_state {
- uint64_t fpr[32];
- uint64_t fcc;
- uint32_t fcsr;
+ __u64 fpr[32];
+ __u64 fcc;
+ __u32 fcsr;
};
struct user_lsx_state {
/* 32 registers, 128 bits width per register. */
- uint64_t vregs[32*2];
+ __u64 vregs[32*2];
};
struct user_lasx_state {
/* 32 registers, 256 bits width per register. */
- uint64_t vregs[32*4];
+ __u64 vregs[32*4];
};
struct user_lbt_state {
- uint64_t scr[4];
- uint32_t eflags;
- uint32_t ftop;
+ __u64 scr[4];
+ __u32 eflags;
+ __u32 ftop;
};
struct user_watch_state {
- uint64_t dbg_info;
+ __u64 dbg_info;
struct {
- uint64_t addr;
- uint64_t mask;
- uint32_t ctrl;
- uint32_t pad;
+ __u64 addr;
+ __u64 mask;
+ __u32 ctrl;
+ __u32 pad;
} dbg_regs[8];
};
struct user_watch_state_v2 {
- uint64_t dbg_info;
+ __u64 dbg_info;
struct {
- uint64_t addr;
- uint64_t mask;
- uint32_t ctrl;
- uint32_t pad;
+ __u64 addr;
+ __u64 mask;
+ __u32 ctrl;
+ __u32 pad;
} dbg_regs[14];
};
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index cbfce2872d71..a2060a24b39f 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -157,6 +157,8 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
c->options |= LOONGARCH_CPU_TLB;
if (config & CPUCFG1_IOCSR)
c->options |= LOONGARCH_CPU_IOCSR;
+ if (config & CPUCFG1_MSGINT)
+ c->options |= LOONGARCH_CPU_MSGINT;
if (config & CPUCFG1_UAL) {
c->options |= LOONGARCH_CPU_UAL;
elf_hwcap |= HWCAP_LOONGARCH_UAL;
@@ -275,7 +277,7 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
uint32_t config;
uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]);
uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]);
- const char *core_name = "Unknown";
+ const char *core_name = id_to_core_name(c->processor_id);
switch (BIT(fls(c->isa_level) - 1)) {
case LOONGARCH_CPU_ISA_LA32R:
@@ -289,35 +291,23 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
break;
}
- switch (c->processor_id & PRID_SERIES_MASK) {
- case PRID_SERIES_LA132:
- core_name = "LA132";
- break;
- case PRID_SERIES_LA264:
- core_name = "LA264";
- break;
- case PRID_SERIES_LA364:
- core_name = "LA364";
- break;
- case PRID_SERIES_LA464:
- core_name = "LA464";
- break;
- case PRID_SERIES_LA664:
- core_name = "LA664";
- break;
- }
-
pr_info("%s Processor probed (%s Core)\n", __cpu_family[cpu], core_name);
- if (!cpu_has_iocsr)
+ if (!cpu_has_iocsr) {
+ __cpu_full_name[cpu] = "Unknown";
return;
-
- if (!__cpu_full_name[cpu])
- __cpu_full_name[cpu] = cpu_full_name;
+ }
*vendor = iocsr_read64(LOONGARCH_IOCSR_VENDOR);
*cpuname = iocsr_read64(LOONGARCH_IOCSR_CPUNAME);
+ if (!__cpu_full_name[cpu]) {
+ if (((char *)vendor)[0] == 0)
+ __cpu_full_name[cpu] = "Unknown";
+ else
+ __cpu_full_name[cpu] = cpu_full_name;
+ }
+
config = iocsr_read32(LOONGARCH_IOCSR_FEATURES);
if (config & IOCSRF_CSRIPI)
c->options |= LOONGARCH_CPU_CSRIPI;
@@ -331,6 +321,8 @@ static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int
c->options |= LOONGARCH_CPU_EIODECODE;
if (config & IOCSRF_AVEC)
c->options |= LOONGARCH_CPU_AVECINT;
+ if (config & IOCSRF_REDIRECT)
+ c->options |= LOONGARCH_CPU_REDIRECTINT;
if (config & IOCSRF_VM)
c->options |= LOONGARCH_CPU_HYPERVISOR;
}
diff --git a/arch/loongarch/kernel/kexec_efi.c b/arch/loongarch/kernel/kexec_efi.c
index 45121b914f8f..5ee78ebb1546 100644
--- a/arch/loongarch/kernel/kexec_efi.c
+++ b/arch/loongarch/kernel/kexec_efi.c
@@ -42,7 +42,7 @@ static void *efi_kexec_load(struct kimage *image,
{
int ret;
unsigned long text_offset, kernel_segment_number;
- struct kexec_buf kbuf;
+ struct kexec_buf kbuf = {};
struct kexec_segment *kernel_segment;
struct loongarch_image_header *h;
diff --git a/arch/loongarch/kernel/kexec_elf.c b/arch/loongarch/kernel/kexec_elf.c
index 97b2f049801a..1b6b64744c7f 100644
--- a/arch/loongarch/kernel/kexec_elf.c
+++ b/arch/loongarch/kernel/kexec_elf.c
@@ -59,7 +59,7 @@ static void *elf_kexec_load(struct kimage *image,
int ret;
unsigned long text_offset, kernel_segment_number;
struct elfhdr ehdr;
- struct kexec_buf kbuf;
+ struct kexec_buf kbuf = {};
struct kexec_elf_info elf_info;
struct kexec_segment *kernel_segment;
diff --git a/arch/loongarch/kernel/machine_kexec.c b/arch/loongarch/kernel/machine_kexec.c
index e4b2bbc47e62..d7fafda1d541 100644
--- a/arch/loongarch/kernel/machine_kexec.c
+++ b/arch/loongarch/kernel/machine_kexec.c
@@ -39,34 +39,12 @@ static unsigned long systable_ptr;
static unsigned long start_addr;
static unsigned long first_ind_entry;
-static void kexec_image_info(const struct kimage *kimage)
-{
- unsigned long i;
-
- pr_debug("kexec kimage info:\n");
- pr_debug("\ttype: %d\n", kimage->type);
- pr_debug("\tstart: %lx\n", kimage->start);
- pr_debug("\thead: %lx\n", kimage->head);
- pr_debug("\tnr_segments: %lu\n", kimage->nr_segments);
-
- for (i = 0; i < kimage->nr_segments; i++) {
- pr_debug("\t segment[%lu]: %016lx - %016lx", i,
- kimage->segment[i].mem,
- kimage->segment[i].mem + kimage->segment[i].memsz);
- pr_debug("\t\t0x%lx bytes, %lu pages\n",
- (unsigned long)kimage->segment[i].memsz,
- (unsigned long)kimage->segment[i].memsz / PAGE_SIZE);
- }
-}
-
int machine_kexec_prepare(struct kimage *kimage)
{
int i;
char *bootloader = "kexec";
void *cmdline_ptr = (void *)KEXEC_CMDLINE_ADDR;
- kexec_image_info(kimage);
-
kimage->arch.efi_boot = fw_arg0;
kimage->arch.systable_ptr = fw_arg2;
@@ -259,6 +237,7 @@ void machine_crash_shutdown(struct pt_regs *regs)
#ifdef CONFIG_SMP
crash_smp_send_stop();
#endif
+ machine_kexec_mask_interrupts();
cpumask_set_cpu(crashing_cpu, &cpus_in_crash);
pr_info("Starting crashdump kernel...\n");
@@ -296,6 +275,7 @@ void machine_kexec(struct kimage *image)
/* We do not want to be bothered. */
local_irq_disable();
+ machine_kexec_mask_interrupts();
pr_notice("EFI boot flag: 0x%lx\n", efi_boot);
pr_notice("Command line addr: 0x%lx\n", cmdline_ptr);
diff --git a/arch/loongarch/kernel/machine_kexec_file.c b/arch/loongarch/kernel/machine_kexec_file.c
index dda236b51a88..fb57026f5f25 100644
--- a/arch/loongarch/kernel/machine_kexec_file.c
+++ b/arch/loongarch/kernel/machine_kexec_file.c
@@ -143,7 +143,7 @@ int load_other_segments(struct kimage *image,
unsigned long initrd_load_addr = 0;
unsigned long orig_segments = image->nr_segments;
char *modified_cmdline = NULL;
- struct kexec_buf kbuf;
+ struct kexec_buf kbuf = {};
kbuf.image = image;
/* Don't allocate anything below the kernel */
diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c
index aed901c57fb4..8ab1ffedc52c 100644
--- a/arch/loongarch/kernel/mem.c
+++ b/arch/loongarch/kernel/mem.c
@@ -13,7 +13,7 @@
void __init memblock_init(void)
{
u32 mem_type;
- u64 mem_start, mem_end, mem_size;
+ u64 mem_start, mem_size;
efi_memory_desc_t *md;
/* Parse memory information */
@@ -21,7 +21,6 @@ void __init memblock_init(void)
mem_type = md->type;
mem_start = md->phys_addr;
mem_size = md->num_pages << EFI_PAGE_SHIFT;
- mem_end = mem_start + mem_size;
switch (mem_type) {
case EFI_LOADER_CODE:
@@ -31,8 +30,6 @@ void __init memblock_init(void)
case EFI_PERSISTENT_MEMORY:
case EFI_CONVENTIONAL_MEMORY:
memblock_add(mem_start, mem_size);
- if (max_low_pfn < (mem_end >> PAGE_SHIFT))
- max_low_pfn = mem_end >> PAGE_SHIFT;
break;
case EFI_PAL_CODE:
case EFI_UNUSABLE_MEMORY:
@@ -49,6 +46,8 @@ void __init memblock_init(void)
}
}
+ max_pfn = PFN_DOWN(memblock_end_of_DRAM());
+ max_low_pfn = min(PFN_DOWN(HIGHMEM_START), max_pfn);
memblock_set_current_limit(PFN_PHYS(max_low_pfn));
/* Reserve the first 2MB */
diff --git a/arch/loongarch/kernel/numa.c b/arch/loongarch/kernel/numa.c
index d6e73e8f9c0b..8b89898e20df 100644
--- a/arch/loongarch/kernel/numa.c
+++ b/arch/loongarch/kernel/numa.c
@@ -158,35 +158,9 @@ static void __init node_mem_init(unsigned int node)
#ifdef CONFIG_ACPI_NUMA
-/*
- * add_numamem_region
- *
- * Add a uasable memory region described by BIOS. The
- * routine gets each intersection between BIOS's region
- * and node's region, and adds them into node's memblock
- * pool.
- *
- */
-static void __init add_numamem_region(u64 start, u64 end, u32 type)
-{
- u32 node = pa_to_nid(start);
- u64 size = end - start;
- static unsigned long num_physpages;
-
- if (start >= end) {
- pr_debug("Invalid region: %016llx-%016llx\n", start, end);
- return;
- }
-
- num_physpages += (size >> PAGE_SHIFT);
- pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n",
- node, type, start, size);
- pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
- start >> PAGE_SHIFT, end >> PAGE_SHIFT, num_physpages);
- memblock_set_node(start, size, &memblock.memory, node);
-}
+static unsigned long num_physpages;
-static void __init init_node_memblock(void)
+static void __init info_node_memblock(void)
{
u32 mem_type;
u64 mem_end, mem_start, mem_size;
@@ -206,12 +180,20 @@ static void __init init_node_memblock(void)
case EFI_BOOT_SERVICES_DATA:
case EFI_PERSISTENT_MEMORY:
case EFI_CONVENTIONAL_MEMORY:
- add_numamem_region(mem_start, mem_end, mem_type);
+ num_physpages += (mem_size >> PAGE_SHIFT);
+ pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n",
+ (u32)pa_to_nid(mem_start), mem_type, mem_start, mem_size);
+ pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
+ mem_start >> PAGE_SHIFT, mem_end >> PAGE_SHIFT, num_physpages);
break;
case EFI_PAL_CODE:
case EFI_UNUSABLE_MEMORY:
case EFI_ACPI_RECLAIM_MEMORY:
- add_numamem_region(mem_start, mem_end, mem_type);
+ num_physpages += (mem_size >> PAGE_SHIFT);
+ pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n",
+ (u32)pa_to_nid(mem_start), mem_type, mem_start, mem_size);
+ pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
+ mem_start >> PAGE_SHIFT, mem_end >> PAGE_SHIFT, num_physpages);
fallthrough;
case EFI_RESERVED_TYPE:
case EFI_RUNTIME_SERVICES_CODE:
@@ -249,22 +231,16 @@ int __init init_numa_memory(void)
for (i = 0; i < NR_CPUS; i++)
set_cpuid_to_node(i, NUMA_NO_NODE);
- numa_reset_distance();
- nodes_clear(numa_nodes_parsed);
- nodes_clear(node_possible_map);
- nodes_clear(node_online_map);
- WARN_ON(memblock_clear_hotplug(0, PHYS_ADDR_MAX));
-
/* Parse SRAT and SLIT if provided by firmware. */
- ret = acpi_disabled ? fake_numa_init() : acpi_numa_init();
+ if (!acpi_disabled)
+ ret = numa_memblks_init(acpi_numa_init, false);
+ else
+ ret = numa_memblks_init(fake_numa_init, false);
+
if (ret < 0)
return ret;
- node_possible_map = numa_nodes_parsed;
- if (WARN_ON(nodes_empty(node_possible_map)))
- return -EINVAL;
-
- init_node_memblock();
+ info_node_memblock();
if (!memblock_validate_numa_coverage(SZ_1M))
return -EINVAL;
@@ -272,7 +248,8 @@ int __init init_numa_memory(void)
node_mem_init(node);
node_set_online(node);
}
- max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
+ max_pfn = PFN_DOWN(memblock_end_of_DRAM());
+ max_low_pfn = min(PFN_DOWN(HIGHMEM_START), max_pfn);
setup_nr_node_ids();
loongson_sysconf.nr_nodes = nr_node_ids;
@@ -283,26 +260,6 @@ int __init init_numa_memory(void)
#endif
-void __init paging_init(void)
-{
- unsigned int node;
- unsigned long zones_size[MAX_NR_ZONES] = {0, };
-
- for_each_online_node(node) {
- unsigned long start_pfn, end_pfn;
-
- get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
-
- if (end_pfn > max_low_pfn)
- max_low_pfn = end_pfn;
- }
-#ifdef CONFIG_ZONE_DMA32
- zones_size[ZONE_DMA32] = MAX_DMA32_PFN;
-#endif
- zones_size[ZONE_NORMAL] = max_low_pfn;
- free_area_init(zones_size);
-}
-
int pcibus_to_node(struct pci_bus *bus)
{
return dev_to_node(&bus->dev);
diff --git a/arch/loongarch/kernel/perf_event.c b/arch/loongarch/kernel/perf_event.c
index 8ad098703488..9d257c8519c9 100644
--- a/arch/loongarch/kernel/perf_event.c
+++ b/arch/loongarch/kernel/perf_event.c
@@ -845,13 +845,14 @@ static const struct loongarch_perf_event *loongarch_pmu_map_raw_event(u64 config
static int __init init_hw_perf_events(void)
{
- int counters;
+ int bits, counters;
if (!cpu_has_pmp)
return -ENODEV;
pr_info("Performance counters: ");
- counters = ((read_cpucfg(LOONGARCH_CPUCFG6) & CPUCFG6_PMNUM) >> 4) + 1;
+ bits = ((read_cpucfg(LOONGARCH_CPUCFG6) & CPUCFG6_PMBITS) >> CPUCFG6_PMBITS_SHIFT) + 1;
+ counters = ((read_cpucfg(LOONGARCH_CPUCFG6) & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT) + 1;
loongarch_pmu.num_counters = counters;
loongarch_pmu.max_period = (1ULL << 63) - 1;
@@ -867,7 +868,7 @@ static int __init init_hw_perf_events(void)
on_each_cpu(reset_counters, NULL, 1);
pr_cont("%s PMU enabled, %d %d-bit counters available to each CPU.\n",
- loongarch_pmu.name, counters, 64);
+ loongarch_pmu.name, counters, bits);
perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
diff --git a/arch/loongarch/kernel/proc.c b/arch/loongarch/kernel/proc.c
index cea30768ae92..63d2b7e7e844 100644
--- a/arch/loongarch/kernel/proc.c
+++ b/arch/loongarch/kernel/proc.c
@@ -17,6 +17,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
{
unsigned long n = (unsigned long) v - 1;
unsigned int isa = cpu_data[n].isa_level;
+ unsigned int prid = cpu_data[n].processor_id;
unsigned int version = cpu_data[n].processor_id & 0xff;
unsigned int fp_version = cpu_data[n].fpu_vers;
@@ -37,6 +38,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
seq_printf(m, "global_id\t\t: %d\n", cpu_data[n].global_id);
seq_printf(m, "CPU Family\t\t: %s\n", __cpu_family[n]);
seq_printf(m, "Model Name\t\t: %s\n", __cpu_full_name[n]);
+ seq_printf(m, "PRID\t\t\t: %s (%08x)\n", id_to_core_name(prid), prid);
seq_printf(m, "CPU Revision\t\t: 0x%02x\n", version);
seq_printf(m, "FPU Revision\t\t: 0x%02x\n", fp_version);
seq_printf(m, "CPU MHz\t\t\t: %llu.%02llu\n",
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index 69c17d162fff..25a87378e48e 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -294,8 +294,6 @@ static void __init fdt_setup(void)
early_init_dt_scan(fdt_pointer, __pa(fdt_pointer));
early_init_fdt_reserve_self();
-
- max_low_pfn = PFN_PHYS(memblock_end_of_DRAM());
#endif
}
@@ -390,7 +388,8 @@ static void __init check_kernel_sections_mem(void)
static void __init arch_mem_init(char **cmdline_p)
{
/* Recalculate max_low_pfn for "mem=xxx" */
- max_pfn = max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
+ max_pfn = PFN_DOWN(memblock_end_of_DRAM());
+ max_low_pfn = min(PFN_DOWN(HIGHMEM_START), max_pfn);
if (usermem)
pr_info("User-defined physical RAM map overwrite\n");
diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
index 3d9be6ca7ec5..da5926fead4a 100644
--- a/arch/loongarch/kernel/traps.c
+++ b/arch/loongarch/kernel/traps.c
@@ -1131,8 +1131,8 @@ static void configure_exception_vector(void)
tlbrentry = (unsigned long)exception_handlers + 80*VECSIZE;
csr_write64(eentry, LOONGARCH_CSR_EENTRY);
- csr_write64(eentry, LOONGARCH_CSR_MERRENTRY);
- csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY);
+ csr_write64(__pa(eentry), LOONGARCH_CSR_MERRENTRY);
+ csr_write64(__pa(tlbrentry), LOONGARCH_CSR_TLBRENTRY);
}
void per_cpu_trap_init(int cpu)
diff --git a/arch/loongarch/kvm/intc/eiointc.c b/arch/loongarch/kvm/intc/eiointc.c
index c32333695381..a1cc116b4dac 100644
--- a/arch/loongarch/kvm/intc/eiointc.c
+++ b/arch/loongarch/kvm/intc/eiointc.c
@@ -439,7 +439,7 @@ static int kvm_eiointc_ctrl_access(struct kvm_device *dev,
spin_lock_irqsave(&s->lock, flags);
switch (type) {
case KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU:
- if (val >= EIOINTC_ROUTE_MAX_VCPUS)
+ if (val > EIOINTC_ROUTE_MAX_VCPUS)
ret = -EINVAL;
else
s->num_cpu = val;
diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c
index 7c8143e79c12..a7fa458e3360 100644
--- a/arch/loongarch/kvm/mmu.c
+++ b/arch/loongarch/kvm/mmu.c
@@ -857,7 +857,7 @@ retry:
if (writeable) {
prot_bits = kvm_pte_mkwriteable(prot_bits);
- if (write)
+ if (write || !kvm_slot_dirty_track_enabled(memslot))
prot_bits = kvm_pte_mkdirty(prot_bits);
}
diff --git a/arch/loongarch/kvm/timer.c b/arch/loongarch/kvm/timer.c
index 32dc213374be..29c2aaba63c3 100644
--- a/arch/loongarch/kvm/timer.c
+++ b/arch/loongarch/kvm/timer.c
@@ -4,6 +4,7 @@
*/
#include <linux/kvm_host.h>
+#include <asm/delay.h>
#include <asm/kvm_csr.h>
#include <asm/kvm_vcpu.h>
@@ -95,6 +96,7 @@ void kvm_restore_timer(struct kvm_vcpu *vcpu)
* and set CSR TVAL with -1
*/
write_gcsr_timertick(0);
+ __delay(2); /* Wait cycles until timer interrupt injected */
/*
* Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 30e3b089a596..1245a6b35896 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -132,6 +132,9 @@ static void kvm_lose_pmu(struct kvm_vcpu *vcpu)
* Clear KVM_LARCH_PMU if the guest is not using PMU CSRs when
* exiting the guest, so that the next time trap into the guest.
* We don't need to deal with PMU CSRs contexts.
+ *
+ * Otherwise set the request bit KVM_REQ_PMU to restore guest PMU
+ * before entering guest VM
*/
val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_PERFCTRL0);
val |= kvm_read_sw_gcsr(csr, LOONGARCH_CSR_PERFCTRL1);
@@ -139,16 +142,12 @@ static void kvm_lose_pmu(struct kvm_vcpu *vcpu)
val |= kvm_read_sw_gcsr(csr, LOONGARCH_CSR_PERFCTRL3);
if (!(val & KVM_PMU_EVENT_ENABLED))
vcpu->arch.aux_inuse &= ~KVM_LARCH_PMU;
+ else
+ kvm_make_request(KVM_REQ_PMU, vcpu);
kvm_restore_host_pmu(vcpu);
}
-static void kvm_restore_pmu(struct kvm_vcpu *vcpu)
-{
- if ((vcpu->arch.aux_inuse & KVM_LARCH_PMU))
- kvm_make_request(KVM_REQ_PMU, vcpu);
-}
-
static void kvm_check_pmu(struct kvm_vcpu *vcpu)
{
if (kvm_check_request(KVM_REQ_PMU, vcpu)) {
@@ -299,7 +298,10 @@ static int kvm_pre_enter_guest(struct kvm_vcpu *vcpu)
vcpu->arch.aux_inuse &= ~KVM_LARCH_SWCSR_LATEST;
if (kvm_request_pending(vcpu) || xfer_to_guest_mode_work_pending()) {
- kvm_lose_pmu(vcpu);
+ if (vcpu->arch.aux_inuse & KVM_LARCH_PMU) {
+ kvm_lose_pmu(vcpu);
+ kvm_make_request(KVM_REQ_PMU, vcpu);
+ }
/* make sure the vcpu mode has been written */
smp_store_mb(vcpu->mode, OUTSIDE_GUEST_MODE);
local_irq_enable();
@@ -1604,9 +1606,6 @@ static int _kvm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
kvm_restore_timer(vcpu);
kvm_make_request(KVM_REQ_STEAL_UPDATE, vcpu);
- /* Restore hardware PMU CSRs */
- kvm_restore_pmu(vcpu);
-
/* Don't bother restoring registers multiple times unless necessary */
if (vcpu->arch.aux_inuse & KVM_LARCH_HWCSR_USABLE)
return 0;
diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c
index c3e4586a7975..6bfd4b8dad1b 100644
--- a/arch/loongarch/mm/init.c
+++ b/arch/loongarch/mm/init.c
@@ -60,7 +60,6 @@ int __ref page_is_ram(unsigned long pfn)
return memblock_is_memory(addr) && !memblock_is_reserved(addr);
}
-#ifndef CONFIG_NUMA
void __init paging_init(void)
{
unsigned long max_zone_pfns[MAX_NR_ZONES];
@@ -72,7 +71,6 @@ void __init paging_init(void)
free_area_init(max_zone_pfns);
}
-#endif /* !CONFIG_NUMA */
void __ref free_initmem(void)
{
diff --git a/arch/loongarch/mm/ioremap.c b/arch/loongarch/mm/ioremap.c
index df949a3d0f34..27c336959fe8 100644
--- a/arch/loongarch/mm/ioremap.c
+++ b/arch/loongarch/mm/ioremap.c
@@ -6,7 +6,7 @@
#include <asm/io.h>
#include <asm-generic/early_ioremap.h>
-void __init __iomem *early_ioremap(u64 phys_addr, unsigned long size)
+void __init __iomem *early_ioremap(phys_addr_t phys_addr, unsigned long size)
{
return ((void __iomem *)TO_CACHE(phys_addr));
}
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index cbe53d0b7fb0..8dc58781b8eb 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -1284,11 +1284,12 @@ void *bpf_arch_text_copy(void *dst, void *src, size_t len)
return ret ? ERR_PTR(-EINVAL) : dst;
}
-int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
- void *old_addr, void *new_addr)
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
+ enum bpf_text_poke_type new_t, void *old_addr,
+ void *new_addr)
{
int ret;
- bool is_call = (poke_type == BPF_MOD_CALL);
+ bool is_call;
u32 old_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
u32 new_insns[LOONGARCH_LONG_JUMP_NINSNS] = {[0 ... 4] = INSN_NOP};
@@ -1298,6 +1299,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
if (!is_bpf_text_address((unsigned long)ip))
return -ENOTSUPP;
+ is_call = old_t == BPF_MOD_CALL;
ret = emit_jump_or_nops(old_addr, ip, old_insns, is_call);
if (ret)
return ret;
@@ -1305,6 +1307,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
if (memcmp(ip, old_insns, LOONGARCH_LONG_JUMP_NBYTES))
return -EFAULT;
+ is_call = new_t == BPF_MOD_CALL;
ret = emit_jump_or_nops(new_addr, ip, new_insns, is_call);
if (ret)
return ret;
@@ -1624,6 +1627,9 @@ static int __arch_prepare_bpf_trampoline(struct jit_ctx *ctx, struct bpf_tramp_i
/* Direct jump skips 5 NOP instructions */
else if (is_bpf_text_address((unsigned long)orig_call))
orig_call += LOONGARCH_BPF_FENTRY_NBYTES;
+ /* Module tracing not supported - cause kernel lockups */
+ else if (is_module_text_address((unsigned long)orig_call))
+ return -ENOTSUPP;
if (flags & BPF_TRAMP_F_CALL_ORIG) {
move_addr(ctx, LOONGARCH_GPR_A0, (const u64)im);
diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c
index 5bc9627a6cf9..d9fc5d520b37 100644
--- a/arch/loongarch/pci/pci.c
+++ b/arch/loongarch/pci/pci.c
@@ -50,11 +50,11 @@ static int __init pcibios_init(void)
*/
lsize = cpu_last_level_cache_line_size();
- BUG_ON(!lsize);
+ if (lsize) {
+ pci_dfl_cache_line_size = lsize >> 2;
- pci_dfl_cache_line_size = lsize >> 2;
-
- pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize);
+ pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize);
+ }
return 0;
}
diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile
index d8316f993482..c0cc3ca5da9f 100644
--- a/arch/loongarch/vdso/Makefile
+++ b/arch/loongarch/vdso/Makefile
@@ -19,7 +19,7 @@ ccflags-vdso := \
cflags-vdso := $(ccflags-vdso) \
-isystem $(shell $(CC) -print-file-name=include) \
$(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \
- -std=gnu11 -O2 -g -fno-strict-aliasing -fno-common -fno-builtin \
+ -std=gnu11 -fms-extensions -O2 -g -fno-strict-aliasing -fno-common -fno-builtin \
-fno-stack-protector -fno-jump-tables -DDISABLE_BRANCH_PROFILING \
$(call cc-option, -fno-asynchronous-unwind-tables) \
$(call cc-option, -fno-stack-protector)
diff --git a/arch/m68k/coldfire/m5272.c b/arch/m68k/coldfire/m5272.c
index 918e2a3236c5..28b3ffa25ba0 100644
--- a/arch/m68k/coldfire/m5272.c
+++ b/arch/m68k/coldfire/m5272.c
@@ -16,7 +16,6 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/phy.h>
-#include <linux/phy_fixed.h>
#include <asm/machdep.h>
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
@@ -103,23 +102,9 @@ void __init config_BSP(char *commandp, int size)
/***************************************************************************/
-/*
- * Some 5272 based boards have the FEC ethernet directly connected to
- * an ethernet switch. In this case we need to use the fixed phy type,
- * and we need to declare it early in boot.
- */
-static const struct fixed_phy_status nettel_fixed_phy_status __initconst = {
- .link = 1,
- .speed = 100,
- .duplex = 0,
-};
-
-/***************************************************************************/
-
static int __init init_BSP(void)
{
m5272_uarts_init();
- fixed_phy_add(&nettel_fixed_phy_status);
clkdev_add_table(m5272_clk_lookup, ARRAY_SIZE(m5272_clk_lookup));
return 0;
}
diff --git a/arch/m68k/configs/amcore_defconfig b/arch/m68k/configs/amcore_defconfig
index 60767811e34a..88832e9cd7cb 100644
--- a/arch/m68k/configs/amcore_defconfig
+++ b/arch/m68k/configs/amcore_defconfig
@@ -86,5 +86,4 @@ CONFIG_PANIC_ON_OOPS=y
# CONFIG_SCHED_DEBUG is not set
# CONFIG_DEBUG_BUGVERBOSE is not set
# CONFIG_CRYPTO_ECHAINIV is not set
-CONFIG_CRYPTO_ANSI_CPRNG=y
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/m68k/configs/amiga_defconfig b/arch/m68k/configs/amiga_defconfig
index fba8089c9fb3..bfc1ee7c8158 100644
--- a/arch/m68k/configs/amiga_defconfig
+++ b/arch/m68k/configs/amiga_defconfig
@@ -265,7 +265,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -281,7 +280,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -583,6 +581,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -591,7 +590,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/apollo_defconfig b/arch/m68k/configs/apollo_defconfig
index 6af37716384c..d9d1f3c4c70d 100644
--- a/arch/m68k/configs/apollo_defconfig
+++ b/arch/m68k/configs/apollo_defconfig
@@ -261,7 +261,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -277,7 +276,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -540,6 +538,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -548,7 +547,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/atari_defconfig b/arch/m68k/configs/atari_defconfig
index 471f4ec3730d..523205adccc8 100644
--- a/arch/m68k/configs/atari_defconfig
+++ b/arch/m68k/configs/atari_defconfig
@@ -268,7 +268,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -284,7 +283,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -560,6 +558,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -568,7 +567,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/bvme6000_defconfig b/arch/m68k/configs/bvme6000_defconfig
index 28492ef51457..7b0a4ef0b010 100644
--- a/arch/m68k/configs/bvme6000_defconfig
+++ b/arch/m68k/configs/bvme6000_defconfig
@@ -258,7 +258,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -274,7 +273,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -532,6 +530,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -540,7 +539,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/hp300_defconfig b/arch/m68k/configs/hp300_defconfig
index 2fbefb16b72e..089c5c394c62 100644
--- a/arch/m68k/configs/hp300_defconfig
+++ b/arch/m68k/configs/hp300_defconfig
@@ -260,7 +260,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -276,7 +275,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -542,6 +540,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -550,7 +549,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/mac_defconfig b/arch/m68k/configs/mac_defconfig
index deec5df3f35a..5f2484c36733 100644
--- a/arch/m68k/configs/mac_defconfig
+++ b/arch/m68k/configs/mac_defconfig
@@ -259,7 +259,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -275,7 +274,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -559,6 +557,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -567,7 +566,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/multi_defconfig b/arch/m68k/configs/multi_defconfig
index 301a05c12577..74f0a1f6d871 100644
--- a/arch/m68k/configs/multi_defconfig
+++ b/arch/m68k/configs/multi_defconfig
@@ -279,7 +279,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -295,7 +294,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -646,6 +644,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -654,7 +653,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/mvme147_defconfig b/arch/m68k/configs/mvme147_defconfig
index 0d401db0e8f8..4bee18c820e4 100644
--- a/arch/m68k/configs/mvme147_defconfig
+++ b/arch/m68k/configs/mvme147_defconfig
@@ -257,7 +257,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -273,7 +272,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -532,6 +530,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -540,7 +539,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/mvme16x_defconfig b/arch/m68k/configs/mvme16x_defconfig
index 90fb5b6bcf83..322c17e55c9a 100644
--- a/arch/m68k/configs/mvme16x_defconfig
+++ b/arch/m68k/configs/mvme16x_defconfig
@@ -258,7 +258,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -274,7 +273,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -533,6 +531,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -541,7 +540,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/q40_defconfig b/arch/m68k/configs/q40_defconfig
index b89b0f7fe2da..82f9baab8fea 100644
--- a/arch/m68k/configs/q40_defconfig
+++ b/arch/m68k/configs/q40_defconfig
@@ -259,7 +259,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -275,7 +274,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -549,6 +547,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -557,7 +556,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/stmark2_defconfig b/arch/m68k/configs/stmark2_defconfig
index f3268fed02fc..f9ecb1dcc060 100644
--- a/arch/m68k/configs/stmark2_defconfig
+++ b/arch/m68k/configs/stmark2_defconfig
@@ -84,7 +84,6 @@ CONFIG_FSCACHE=y
CONFIG_CRAMFS=y
CONFIG_SQUASHFS=y
CONFIG_ROMFS_FS=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
# CONFIG_CRYPTO_HW is not set
CONFIG_PRINTK_TIME=y
# CONFIG_DEBUG_BUGVERBOSE is not set
diff --git a/arch/m68k/configs/sun3_defconfig b/arch/m68k/configs/sun3_defconfig
index 8cc372c4df72..f94ad226cb5b 100644
--- a/arch/m68k/configs/sun3_defconfig
+++ b/arch/m68k/configs/sun3_defconfig
@@ -254,7 +254,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -270,7 +269,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -530,6 +528,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -538,7 +537,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/configs/sun3x_defconfig b/arch/m68k/configs/sun3x_defconfig
index f4569f64c6e4..a5ecfc505ab2 100644
--- a/arch/m68k/configs/sun3x_defconfig
+++ b/arch/m68k/configs/sun3x_defconfig
@@ -255,7 +255,6 @@ CONFIG_BRIDGE_EBT_SNAT=m
CONFIG_BRIDGE_EBT_LOG=m
CONFIG_BRIDGE_EBT_NFLOG=m
CONFIG_IP_SCTP=m
-CONFIG_SCTP_COOKIE_HMAC_SHA1=y
CONFIG_RDS=m
CONFIG_RDS_TCP=m
CONFIG_L2TP=m
@@ -271,7 +270,6 @@ CONFIG_6LOWPAN_GHC_EXT_HDR_ROUTE=m
CONFIG_DNS_RESOLVER=y
CONFIG_BATMAN_ADV=m
# CONFIG_BATMAN_ADV_BATMAN_V is not set
-CONFIG_BATMAN_ADV_NC=y
CONFIG_NETLINK_DIAG=m
CONFIG_MPLS=y
CONFIG_NET_MPLS_GSO=m
@@ -530,6 +528,7 @@ CONFIG_CRYPTO_AEGIS128=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -538,7 +537,6 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
CONFIG_CRYPTO_USER_API_HASH=m
diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl
index f41d38dfbf13..871a5d67bf41 100644
--- a/arch/m68k/kernel/syscalls/syscall.tbl
+++ b/arch/m68k/kernel/syscalls/syscall.tbl
@@ -469,3 +469,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl
index 580af574fe73..022fc85d94b3 100644
--- a/arch/microblaze/kernel/syscalls/syscall.tbl
+++ b/arch/microblaze/kernel/syscalls/syscall.tbl
@@ -475,3 +475,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/mips/bcm47xx/setup.c b/arch/mips/bcm47xx/setup.c
index a93a4266dc1e..38ed61b4bd96 100644
--- a/arch/mips/bcm47xx/setup.c
+++ b/arch/mips/bcm47xx/setup.c
@@ -256,12 +256,6 @@ static int __init bcm47xx_cpu_fixes(void)
}
arch_initcall(bcm47xx_cpu_fixes);
-static const struct fixed_phy_status bcm47xx_fixed_phy_status __initconst = {
- .link = 1,
- .speed = SPEED_100,
- .duplex = DUPLEX_FULL,
-};
-
static int __init bcm47xx_register_bus_complete(void)
{
switch (bcm47xx_bus_type) {
@@ -282,7 +276,6 @@ static int __init bcm47xx_register_bus_complete(void)
bcm47xx_leds_register();
bcm47xx_workarounds();
- fixed_phy_add(&bcm47xx_fixed_phy_status);
return 0;
}
device_initcall(bcm47xx_register_bus_complete);
diff --git a/arch/mips/boot/dts/econet/en751221.dtsi b/arch/mips/boot/dts/econet/en751221.dtsi
index 66197e73d4f0..2abeef5b744a 100644
--- a/arch/mips/boot/dts/econet/en751221.dtsi
+++ b/arch/mips/boot/dts/econet/en751221.dtsi
@@ -18,7 +18,7 @@
cpu@0 {
device_type = "cpu";
- compatible = "mips,mips24KEc";
+ compatible = "mips,mips34Kc";
reg = <0>;
};
};
diff --git a/arch/mips/configs/decstation_64_defconfig b/arch/mips/configs/decstation_64_defconfig
index 52a63dd7aac7..dad98c575292 100644
--- a/arch/mips/configs/decstation_64_defconfig
+++ b/arch/mips/configs/decstation_64_defconfig
@@ -200,7 +200,6 @@ CONFIG_CRYPTO_LZO=m
CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/mips/configs/decstation_defconfig b/arch/mips/configs/decstation_defconfig
index 59fb7ee5eeb0..4e1b51a4ad90 100644
--- a/arch/mips/configs/decstation_defconfig
+++ b/arch/mips/configs/decstation_defconfig
@@ -195,7 +195,6 @@ CONFIG_CRYPTO_LZO=m
CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/mips/configs/decstation_r4k_defconfig b/arch/mips/configs/decstation_r4k_defconfig
index 8be1cb433e95..4e550dffc23d 100644
--- a/arch/mips/configs/decstation_r4k_defconfig
+++ b/arch/mips/configs/decstation_r4k_defconfig
@@ -195,7 +195,6 @@ CONFIG_CRYPTO_LZO=m
CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_DRBG_HASH=y
CONFIG_CRYPTO_DRBG_CTR=y
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 29191fa1801e..a3101f2268c6 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -692,7 +692,7 @@ unsigned long mips_stack_top(void)
/* Space for the VDSO, data page & GIC user page */
if (current->thread.abi) {
top -= PAGE_ALIGN(current->thread.abi->vdso->size);
- top -= PAGE_SIZE;
+ top -= VDSO_NR_PAGES * PAGE_SIZE;
top -= mips_gic_present() ? PAGE_SIZE : 0;
/* Space to randomize the VDSO base */
diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl
index d824ffe9a014..8cedc83c3266 100644
--- a/arch/mips/kernel/syscalls/syscall_n32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n32.tbl
@@ -408,3 +408,4 @@
467 n32 open_tree_attr sys_open_tree_attr
468 n32 file_getattr sys_file_getattr
469 n32 file_setattr sys_file_setattr
+470 n32 listns sys_listns
diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl
index 7a7049c2c307..9b92bddf06b5 100644
--- a/arch/mips/kernel/syscalls/syscall_n64.tbl
+++ b/arch/mips/kernel/syscalls/syscall_n64.tbl
@@ -384,3 +384,4 @@
467 n64 open_tree_attr sys_open_tree_attr
468 n64 file_getattr sys_file_getattr
469 n64 file_setattr sys_file_setattr
+470 n64 listns sys_listns
diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl
index d330274f0601..f810b8a55716 100644
--- a/arch/mips/kernel/syscalls/syscall_o32.tbl
+++ b/arch/mips/kernel/syscalls/syscall_o32.tbl
@@ -457,3 +457,4 @@
467 o32 open_tree_attr sys_open_tree_attr
468 o32 file_getattr sys_file_getattr
469 o32 file_setattr sys_file_setattr
+470 o32 listns sys_listns
diff --git a/arch/mips/mm/tlb-r4k.c b/arch/mips/mm/tlb-r4k.c
index 347126dc010d..44a662536148 100644
--- a/arch/mips/mm/tlb-r4k.c
+++ b/arch/mips/mm/tlb-r4k.c
@@ -12,9 +12,11 @@
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/smp.h>
+#include <linux/memblock.h>
#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <linux/export.h>
+#include <linux/sort.h>
#include <asm/cpu.h>
#include <asm/cpu-type.h>
@@ -508,58 +510,95 @@ static int __init set_ntlb(char *str)
__setup("ntlb=", set_ntlb);
-/* Initialise all TLB entries with unique values */
-static void r4k_tlb_uniquify(void)
+
+/* Comparison function for EntryHi VPN fields. */
+static int r4k_vpn_cmp(const void *a, const void *b)
{
- int entry = num_wired_entries();
+ long v = *(unsigned long *)a - *(unsigned long *)b;
+ int s = sizeof(long) > sizeof(int) ? sizeof(long) * 8 - 1: 0;
+ return s ? (v != 0) | v >> s : v;
+}
+
+/*
+ * Initialise all TLB entries with unique values that do not clash with
+ * what we have been handed over and what we'll be using ourselves.
+ */
+static void __ref r4k_tlb_uniquify(void)
+{
+ int tlbsize = current_cpu_data.tlbsize;
+ bool use_slab = slab_is_available();
+ int start = num_wired_entries();
+ phys_addr_t tlb_vpn_size;
+ unsigned long *tlb_vpns;
+ unsigned long vpn_mask;
+ int cnt, ent, idx, i;
+
+ vpn_mask = GENMASK(cpu_vmbits - 1, 13);
+ vpn_mask |= IS_ENABLED(CONFIG_64BIT) ? 3ULL << 62 : 1 << 31;
+
+ tlb_vpn_size = tlbsize * sizeof(*tlb_vpns);
+ tlb_vpns = (use_slab ?
+ kmalloc(tlb_vpn_size, GFP_KERNEL) :
+ memblock_alloc_raw(tlb_vpn_size, sizeof(*tlb_vpns)));
+ if (WARN_ON(!tlb_vpns))
+ return; /* Pray local_flush_tlb_all() is good enough. */
htw_stop();
+
+ for (i = start, cnt = 0; i < tlbsize; i++, cnt++) {
+ unsigned long vpn;
+
+ write_c0_index(i);
+ mtc0_tlbr_hazard();
+ tlb_read();
+ tlb_read_hazard();
+ vpn = read_c0_entryhi();
+ vpn &= vpn_mask & PAGE_MASK;
+ tlb_vpns[cnt] = vpn;
+
+ /* Prevent any large pages from overlapping regular ones. */
+ write_c0_pagemask(read_c0_pagemask() & PM_DEFAULT_MASK);
+ mtc0_tlbw_hazard();
+ tlb_write_indexed();
+ tlbw_use_hazard();
+ }
+
+ sort(tlb_vpns, cnt, sizeof(tlb_vpns[0]), r4k_vpn_cmp, NULL);
+
+ write_c0_pagemask(PM_DEFAULT_MASK);
write_c0_entrylo0(0);
write_c0_entrylo1(0);
- while (entry < current_cpu_data.tlbsize) {
- unsigned long asid_mask = cpu_asid_mask(&current_cpu_data);
- unsigned long asid = 0;
- int idx;
+ idx = 0;
+ ent = tlbsize;
+ for (i = start; i < tlbsize; i++)
+ while (1) {
+ unsigned long entryhi, vpn;
- /* Skip wired MMID to make ginvt_mmid work */
- if (cpu_has_mmid)
- asid = MMID_KERNEL_WIRED + 1;
+ entryhi = UNIQUE_ENTRYHI(ent);
+ vpn = entryhi & vpn_mask & PAGE_MASK;
- /* Check for match before using UNIQUE_ENTRYHI */
- do {
- if (cpu_has_mmid) {
- write_c0_memorymapid(asid);
- write_c0_entryhi(UNIQUE_ENTRYHI(entry));
+ if (idx >= cnt || vpn < tlb_vpns[idx]) {
+ write_c0_entryhi(entryhi);
+ write_c0_index(i);
+ mtc0_tlbw_hazard();
+ tlb_write_indexed();
+ ent++;
+ break;
+ } else if (vpn == tlb_vpns[idx]) {
+ ent++;
} else {
- write_c0_entryhi(UNIQUE_ENTRYHI(entry) | asid);
+ idx++;
}
- mtc0_tlbw_hazard();
- tlb_probe();
- tlb_probe_hazard();
- idx = read_c0_index();
- /* No match or match is on current entry */
- if (idx < 0 || idx == entry)
- break;
- /*
- * If we hit a match, we need to try again with
- * a different ASID.
- */
- asid++;
- } while (asid < asid_mask);
-
- if (idx >= 0 && idx != entry)
- panic("Unable to uniquify TLB entry %d", idx);
-
- write_c0_index(entry);
- mtc0_tlbw_hazard();
- tlb_write_indexed();
- entry++;
- }
+ }
tlbw_use_hazard();
htw_start();
flush_micro_tlb();
+ if (use_slab)
+ kfree(tlb_vpns);
+ else
+ memblock_free(tlb_vpns, tlb_vpn_size);
}
/*
@@ -602,6 +641,7 @@ static void r4k_tlb_configure(void)
/* From this point on the ARC firmware is dead. */
r4k_tlb_uniquify();
+ local_flush_tlb_all();
/* Did I tell you that ARC SUCKS? */
}
diff --git a/arch/mips/mti-malta/malta-init.c b/arch/mips/mti-malta/malta-init.c
index 000d6d50520a..82b0fd8576a2 100644
--- a/arch/mips/mti-malta/malta-init.c
+++ b/arch/mips/mti-malta/malta-init.c
@@ -241,16 +241,22 @@ mips_pci_controller:
#endif
/*
- * Setup the Malta max (2GB) memory for PCI DMA in host bridge
- * in transparent addressing mode.
+ * Set up memory mapping in host bridge for PCI DMA masters,
+ * in transparent addressing mode. For EVA use the Malta
+ * maximum of 2 GiB memory in the alias space at 0x80000000
+ * as per PHYS_OFFSET. Otherwise use 256 MiB of memory in
+ * the regular space, avoiding mapping the PCI MMIO window
+ * for DMA as it seems to confuse the system controller's
+ * logic, causing PCI MMIO to stop working.
*/
- mask = PHYS_OFFSET | PCI_BASE_ADDRESS_MEM_PREFETCH;
- MSC_WRITE(MSC01_PCI_BAR0, mask);
- MSC_WRITE(MSC01_PCI_HEAD4, mask);
+ mask = PHYS_OFFSET ? PHYS_OFFSET : 0xf0000000;
+ MSC_WRITE(MSC01_PCI_BAR0,
+ mask | PCI_BASE_ADDRESS_MEM_PREFETCH);
+ MSC_WRITE(MSC01_PCI_HEAD4,
+ PHYS_OFFSET | PCI_BASE_ADDRESS_MEM_PREFETCH);
- mask &= MSC01_PCI_BAR0_SIZE_MSK;
MSC_WRITE(MSC01_PCI_P2SCMSKL, mask);
- MSC_WRITE(MSC01_PCI_P2SCMAPL, mask);
+ MSC_WRITE(MSC01_PCI_P2SCMAPL, PHYS_OFFSET);
/* Don't handle target retries indefinitely. */
if ((data & MSC01_PCI_CFG_MAXRTRY_MSK) ==
diff --git a/arch/mips/mti-malta/malta-setup.c b/arch/mips/mti-malta/malta-setup.c
index 3a2836e9d856..816570514c37 100644
--- a/arch/mips/mti-malta/malta-setup.c
+++ b/arch/mips/mti-malta/malta-setup.c
@@ -47,7 +47,7 @@ static struct resource standard_io_resources[] = {
.name = "keyboard",
.start = 0x60,
.end = 0x6f,
- .flags = IORESOURCE_IO | IORESOURCE_BUSY
+ .flags = IORESOURCE_IO
},
{
.name = "dma page reg",
@@ -213,7 +213,7 @@ void __init plat_mem_setup(void)
/* Request I/O space for devices used on the Malta board. */
for (i = 0; i < ARRAY_SIZE(standard_io_resources); i++)
- request_resource(&ioport_resource, standard_io_resources+i);
+ insert_resource(&ioport_resource, standard_io_resources + i);
/*
* Enable DMA channel 4 (cascade channel) in the PIIX4 south bridge.
diff --git a/arch/mips/pci/pci-malta.c b/arch/mips/pci/pci-malta.c
index 6aefdf20ca05..2e35aeba45bc 100644
--- a/arch/mips/pci/pci-malta.c
+++ b/arch/mips/pci/pci-malta.c
@@ -230,8 +230,7 @@ void __init mips_pcibios_init(void)
}
/* PIIX4 ACPI starts at 0x1000 */
- if (controller->io_resource->start < 0x00001000UL)
- controller->io_resource->start = 0x00001000UL;
+ PCIBIOS_MIN_IO = 0x1000;
iomem_resource.end &= 0xfffffffffULL; /* 64 GB */
ioport_resource.end = controller->io_resource->end;
diff --git a/arch/parisc/boot/compressed/Makefile b/arch/parisc/boot/compressed/Makefile
index 17c42d718eb3..f8481e4e9d21 100644
--- a/arch/parisc/boot/compressed/Makefile
+++ b/arch/parisc/boot/compressed/Makefile
@@ -18,7 +18,7 @@ KBUILD_CFLAGS += -fno-PIE -mno-space-regs -mdisable-fpregs -Os
ifndef CONFIG_64BIT
KBUILD_CFLAGS += -mfast-indirect-calls
endif
-KBUILD_CFLAGS += -std=gnu11
+KBUILD_CFLAGS += -std=gnu11 -fms-extensions
LDFLAGS_vmlinux := -X -e startup --as-needed -T
$(obj)/vmlinux: $(obj)/vmlinux.lds $(addprefix $(obj)/, $(OBJECTS)) $(LIBGCC) FORCE
diff --git a/arch/parisc/include/asm/bug.h b/arch/parisc/include/asm/bug.h
index 833555f74ffa..5aa1623e4f2f 100644
--- a/arch/parisc/include/asm/bug.h
+++ b/arch/parisc/include/asm/bug.h
@@ -50,7 +50,7 @@
#endif
#ifdef CONFIG_DEBUG_BUGVERBOSE
-#define __WARN_FLAGS(flags) \
+#define __WARN_FLAGS(cond_str, flags) \
do { \
asm volatile("\n" \
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
@@ -61,12 +61,12 @@
"\t.short %1, %2\n" \
"\t.blockz %3-2*4-2*2\n" \
"\t.popsection" \
- : : "i" (__FILE__), "i" (__LINE__), \
+ : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
"i" (BUGFLAG_WARNING|(flags)), \
"i" (sizeof(struct bug_entry)) ); \
} while(0)
#else
-#define __WARN_FLAGS(flags) \
+#define __WARN_FLAGS(cond_str, flags) \
do { \
asm volatile("\n" \
"1:\t" PARISC_BUG_BREAK_ASM "\n" \
diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl
index 88a788a7b18d..39bdacaa530b 100644
--- a/arch/parisc/kernel/syscalls/syscall.tbl
+++ b/arch/parisc/kernel/syscalls/syscall.tbl
@@ -468,3 +468,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/parisc/kernel/unwind.c b/arch/parisc/kernel/unwind.c
index f7e0fee5ee55..7ac88ff13d3c 100644
--- a/arch/parisc/kernel/unwind.c
+++ b/arch/parisc/kernel/unwind.c
@@ -35,6 +35,8 @@
#define KERNEL_START (KERNEL_BINARY_TEXT_START)
+#define ALIGNMENT_OK(ptr, type) (((ptr) & (sizeof(type) - 1)) == 0)
+
extern struct unwind_table_entry __start___unwind[];
extern struct unwind_table_entry __stop___unwind[];
@@ -257,12 +259,15 @@ static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int
if (pc_is_kernel_fn(pc, _switch_to) ||
pc == (unsigned long)&_switch_to_ret) {
info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE;
- info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
+ if (ALIGNMENT_OK(info->prev_sp, long))
+ info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET);
+ else
+ info->prev_ip = info->prev_sp = 0;
return 1;
}
#ifdef CONFIG_IRQSTACKS
- if (pc == (unsigned long)&_call_on_stack) {
+ if (pc == (unsigned long)&_call_on_stack && ALIGNMENT_OK(info->sp, long)) {
info->prev_sp = *(unsigned long *)(info->sp - FRAME_SIZE - REG_SZ);
info->prev_ip = *(unsigned long *)(info->sp - FRAME_SIZE - RP_OFFSET);
return 1;
@@ -370,8 +375,10 @@ static void unwind_frame_regs(struct unwind_frame_info *info)
info->prev_sp = info->sp - frame_size;
if (e->Millicode)
info->rp = info->r31;
- else if (rpoffset)
+ else if (rpoffset && ALIGNMENT_OK(info->prev_sp, long))
info->rp = *(unsigned long *)(info->prev_sp - rpoffset);
+ else
+ info->rp = 0;
info->prev_ip = info->rp;
info->rp = 0;
}
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index e24f4d88885a..9537a61ebae0 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -137,6 +137,7 @@ config PPC
select ARCH_HAS_DMA_OPS if PPC64
select ARCH_HAS_FORTIFY_SOURCE
select ARCH_HAS_GCOV_PROFILE_ALL
+ select ARCH_HAS_GIGANTIC_PAGE if ARCH_SUPPORTS_HUGETLBFS
select ARCH_HAS_KCOV
select ARCH_HAS_KERNEL_FPU_SUPPORT if PPC64 && PPC_FPU
select ARCH_HAS_MEMBARRIER_CALLBACKS
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index c47b78c1d3e7..f1a4761ebd44 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -70,7 +70,7 @@ BOOTCPPFLAGS := -nostdinc $(LINUXINCLUDE)
BOOTCPPFLAGS += -isystem $(shell $(BOOTCC) -print-file-name=include)
BOOTCFLAGS := $(BOOTTARGETFLAGS) \
- -std=gnu11 \
+ -std=gnu11 -fms-extensions \
-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -O2 \
-msoft-float -mno-altivec -mno-vsx \
@@ -86,6 +86,7 @@ BOOTARFLAGS := -crD
ifdef CONFIG_CC_IS_CLANG
BOOTCFLAGS += $(CLANG_FLAGS)
+BOOTCFLAGS += -Wno-microsoft-anon-tag
BOOTAFLAGS += $(CLANG_FLAGS)
endif
diff --git a/arch/powerpc/include/asm/bug.h b/arch/powerpc/include/asm/bug.h
index bbaa7e81f821..0db48977c70c 100644
--- a/arch/powerpc/include/asm/bug.h
+++ b/arch/powerpc/include/asm/bug.h
@@ -51,11 +51,11 @@
".previous\n"
#endif
-#define BUG_ENTRY(insn, flags, ...) \
+#define BUG_ENTRY(cond_str, insn, flags, ...) \
__asm__ __volatile__( \
"1: " insn "\n" \
_EMIT_BUG_ENTRY \
- : : "i" (__FILE__), "i" (__LINE__), \
+ : : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
"i" (flags), \
"i" (sizeof(struct bug_entry)), \
##__VA_ARGS__)
@@ -67,12 +67,12 @@
*/
#define BUG() do { \
- BUG_ENTRY("twi 31, 0, 0", 0); \
+ BUG_ENTRY("", "twi 31, 0, 0", 0); \
unreachable(); \
} while (0)
#define HAVE_ARCH_BUG
-#define __WARN_FLAGS(flags) BUG_ENTRY("twi 31, 0, 0", BUGFLAG_WARNING | (flags))
+#define __WARN_FLAGS(cond_str, flags) BUG_ENTRY(cond_str, "twi 31, 0, 0", BUGFLAG_WARNING | (flags))
#ifdef CONFIG_PPC64
#define BUG_ON(x) do { \
@@ -80,7 +80,7 @@
if (x) \
BUG(); \
} else { \
- BUG_ENTRY(PPC_TLNEI " %4, 0", 0, "r" ((__force long)(x))); \
+ BUG_ENTRY(#x, PPC_TLNEI " %4, 0", 0, "r" ((__force long)(x))); \
} \
} while (0)
@@ -90,7 +90,7 @@
if (__ret_warn_on) \
__WARN(); \
} else { \
- BUG_ENTRY(PPC_TLNEI " %4, 0", \
+ BUG_ENTRY(#x, PPC_TLNEI " %4, 0", \
BUGFLAG_WARNING | BUGFLAG_TAINT(TAINT_WARN), \
"r" (__ret_warn_on)); \
} \
diff --git a/arch/powerpc/include/asm/mem_encrypt.h b/arch/powerpc/include/asm/mem_encrypt.h
index 2f26b8fc8d29..e355ca46fad9 100644
--- a/arch/powerpc/include/asm/mem_encrypt.h
+++ b/arch/powerpc/include/asm/mem_encrypt.h
@@ -9,6 +9,9 @@
#define _ASM_POWERPC_MEM_ENCRYPT_H
#include <asm/svm.h>
+#include <linux/types.h>
+
+struct device;
static inline bool force_dma_unencrypted(struct device *dev)
{
diff --git a/arch/powerpc/include/asm/uaccess.h b/arch/powerpc/include/asm/uaccess.h
index 4f5a46a77fa2..784a00e681fa 100644
--- a/arch/powerpc/include/asm/uaccess.h
+++ b/arch/powerpc/include/asm/uaccess.h
@@ -451,7 +451,7 @@ user_write_access_begin(const void __user *ptr, size_t len)
#define user_write_access_begin user_write_access_begin
#define user_write_access_end prevent_current_write_to_user
-#define unsafe_get_user(x, p, e) do { \
+#define arch_unsafe_get_user(x, p, e) do { \
__long_type(*(p)) __gu_val; \
__typeof__(*(p)) __user *__gu_addr = (p); \
\
@@ -459,7 +459,7 @@ user_write_access_begin(const void __user *ptr, size_t len)
(x) = (__typeof__(*(p)))__gu_val; \
} while (0)
-#define unsafe_put_user(x, p, e) \
+#define arch_unsafe_put_user(x, p, e) \
__put_user_size_goto((__typeof__(*(p)))(x), (p), sizeof(*(p)), e)
#define unsafe_copy_from_user(d, s, l, e) \
@@ -504,11 +504,11 @@ do { \
unsafe_put_user(*(u8*)(_src + _i), (u8 __user *)(_dst + _i), e); \
} while (0)
-#define __get_kernel_nofault(dst, src, type, err_label) \
+#define arch_get_kernel_nofault(dst, src, type, err_label) \
__get_user_size_goto(*((type *)(dst)), \
(__force type __user *)(src), sizeof(type), err_label)
-#define __put_kernel_nofault(dst, src, type, err_label) \
+#define arch_put_kernel_nofault(dst, src, type, err_label) \
__put_user_size_goto(*((type *)(src)), \
(__force type __user *)(dst), sizeof(type), err_label)
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index 244eb4857e7f..b7dcf07b2499 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -1156,7 +1156,8 @@ EXPORT_SYMBOL_GPL(iommu_add_device);
*/
static int
spapr_tce_platform_iommu_attach_dev(struct iommu_domain *platform_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct iommu_table_group *table_group;
@@ -1189,7 +1190,7 @@ static struct iommu_domain spapr_tce_platform_domain = {
static int
spapr_tce_blocked_iommu_attach_dev(struct iommu_domain *platform_domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct iommu_group *grp = iommu_group_get(dev);
struct iommu_table_group *table_group;
diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl
index b453e80dfc00..ec4458cdb97b 100644
--- a/arch/powerpc/kernel/syscalls/syscall.tbl
+++ b/arch/powerpc/kernel/syscalls/syscall.tbl
@@ -560,3 +560,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/powerpc/net/bpf_jit_comp.c b/arch/powerpc/net/bpf_jit_comp.c
index 88ad5ba7b87f..5e976730b2f5 100644
--- a/arch/powerpc/net/bpf_jit_comp.c
+++ b/arch/powerpc/net/bpf_jit_comp.c
@@ -1107,8 +1107,9 @@ static void do_isync(void *info __maybe_unused)
* execute isync (or some CSI) so that they don't go back into the
* trampoline again.
*/
-int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
- void *old_addr, void *new_addr)
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
+ enum bpf_text_poke_type new_t, void *old_addr,
+ void *new_addr)
{
unsigned long bpf_func, bpf_func_end, size, offset;
ppc_inst_t old_inst, new_inst;
@@ -1119,7 +1120,6 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
return -EOPNOTSUPP;
bpf_func = (unsigned long)ip;
- branch_flags = poke_type == BPF_MOD_CALL ? BRANCH_SET_LINK : 0;
/* We currently only support poking bpf programs */
if (!__bpf_address_lookup(bpf_func, &size, &offset, name)) {
@@ -1132,7 +1132,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
* an unconditional branch instruction at im->ip_after_call
*/
if (offset) {
- if (poke_type != BPF_MOD_JUMP) {
+ if (old_t == BPF_MOD_CALL || new_t == BPF_MOD_CALL) {
pr_err("%s (0x%lx): calls are not supported in bpf prog body\n", __func__,
bpf_func);
return -EOPNOTSUPP;
@@ -1166,6 +1166,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
}
old_inst = ppc_inst(PPC_RAW_NOP());
+ branch_flags = old_t == BPF_MOD_CALL ? BRANCH_SET_LINK : 0;
if (old_addr) {
if (is_offset_in_branch_range(ip - old_addr))
create_branch(&old_inst, ip, (unsigned long)old_addr, branch_flags);
@@ -1174,6 +1175,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
branch_flags);
}
new_inst = ppc_inst(PPC_RAW_NOP());
+ branch_flags = new_t == BPF_MOD_CALL ? BRANCH_SET_LINK : 0;
if (new_addr) {
if (is_offset_in_branch_range(ip - new_addr))
create_branch(&new_inst, ip, (unsigned long)new_addr, branch_flags);
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 7b527d18aa5e..4c321a8ea896 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -423,7 +423,6 @@ config PPC_64S_HASH_MMU
config PPC_RADIX_MMU
bool "Radix MMU Support"
depends on PPC_BOOK3S_64
- select ARCH_HAS_GIGANTIC_PAGE
default y
help
Enable support for the Power ISA 3.0 Radix style MMU. Currently this
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index 7ec60290abe6..78c4b6ce5f13 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -267,22 +267,11 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
static int spufs_context_open(const struct path *path)
{
- int ret;
- struct file *filp;
-
- ret = get_unused_fd_flags(0);
- if (ret < 0)
- return ret;
-
- filp = dentry_open(path, O_RDONLY, current_cred());
- if (IS_ERR(filp)) {
- put_unused_fd(ret);
- return PTR_ERR(filp);
- }
-
- filp->f_op = &spufs_context_fops;
- fd_install(ret, filp);
- return ret;
+ FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
+ if (fdf.err)
+ return fdf.err;
+ fd_prepare_file(fdf)->f_op = &spufs_context_fops;
+ return fd_publish(fdf);
}
static struct spu_context *
@@ -508,26 +497,15 @@ static const struct file_operations spufs_gang_fops = {
static int spufs_gang_open(const struct path *path)
{
- int ret;
- struct file *filp;
-
- ret = get_unused_fd_flags(0);
- if (ret < 0)
- return ret;
-
/*
* get references for dget and mntget, will be released
* in error path of *_open().
*/
- filp = dentry_open(path, O_RDONLY, current_cred());
- if (IS_ERR(filp)) {
- put_unused_fd(ret);
- return PTR_ERR(filp);
- }
-
- filp->f_op = &spufs_gang_fops;
- fd_install(ret, filp);
- return ret;
+ FD_PREPARE(fdf, 0, dentry_open(path, O_RDONLY, current_cred()));
+ if (fdf.err)
+ return fdf.err;
+ fd_prepare_file(fdf)->f_op = &spufs_gang_fops;
+ return fd_publish(fdf);
}
static int spufs_create_gang(struct inode *inode,
diff --git a/arch/powerpc/platforms/pseries/papr-hvpipe.c b/arch/powerpc/platforms/pseries/papr-hvpipe.c
index 21a2f447c43f..dd7b668799d9 100644
--- a/arch/powerpc/platforms/pseries/papr-hvpipe.c
+++ b/arch/powerpc/platforms/pseries/papr-hvpipe.c
@@ -479,10 +479,7 @@ static const struct file_operations papr_hvpipe_handle_ops = {
static int papr_hvpipe_dev_create_handle(u32 srcID)
{
- struct hvpipe_source_info *src_info;
- struct file *file;
- long err;
- int fd;
+ struct hvpipe_source_info *src_info __free(kfree) = NULL;
spin_lock(&hvpipe_src_list_lock);
/*
@@ -506,20 +503,13 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
src_info->tsk = current;
init_waitqueue_head(&src_info->recv_wqh);
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- err = fd;
- goto free_buf;
- }
-
- file = anon_inode_getfile("[papr-hvpipe]",
- &papr_hvpipe_handle_ops, (void *)src_info,
- O_RDWR);
- if (IS_ERR(file)) {
- err = PTR_ERR(file);
- goto free_fd;
- }
+ FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
+ anon_inode_getfile("[papr-hvpipe]", &papr_hvpipe_handle_ops,
+ (void *)src_info, O_RDWR));
+ if (fdf.err)
+ return fdf.err;
+ retain_and_null_ptr(src_info);
spin_lock(&hvpipe_src_list_lock);
/*
* If two processes are executing ioctl() for the same
@@ -528,22 +518,11 @@ static int papr_hvpipe_dev_create_handle(u32 srcID)
*/
if (hvpipe_find_source(srcID)) {
spin_unlock(&hvpipe_src_list_lock);
- err = -EALREADY;
- goto free_file;
+ return -EALREADY;
}
list_add(&src_info->list, &hvpipe_src_list);
spin_unlock(&hvpipe_src_list_lock);
-
- fd_install(fd, file);
- return fd;
-
-free_file:
- fput(file);
-free_fd:
- put_unused_fd(fd);
-free_buf:
- kfree(src_info);
- return err;
+ return fd_publish(fdf);
}
/*
diff --git a/arch/powerpc/platforms/pseries/papr-platform-dump.c b/arch/powerpc/platforms/pseries/papr-platform-dump.c
index f8d55eccdb6b..be633c9a0e75 100644
--- a/arch/powerpc/platforms/pseries/papr-platform-dump.c
+++ b/arch/powerpc/platforms/pseries/papr-platform-dump.c
@@ -303,8 +303,6 @@ static long papr_platform_dump_create_handle(u64 dump_tag)
{
struct ibm_platform_dump_params *params;
u64 param_dump_tag;
- struct file *file;
- long err;
int fd;
/*
@@ -334,34 +332,22 @@ static long papr_platform_dump_create_handle(u64 dump_tag)
params->dump_tag_lo = (u32)(dump_tag & 0x00000000ffffffffULL);
params->status = RTAS_IBM_PLATFORM_DUMP_START;
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
+ fd = FD_ADD(O_RDONLY | O_CLOEXEC,
+ anon_inode_getfile_fmode("[papr-platform-dump]",
+ &papr_platform_dump_handle_ops,
+ (void *)params, O_RDONLY,
+ FMODE_LSEEK | FMODE_PREAD));
if (fd < 0) {
- err = fd;
- goto free_area;
- }
-
- file = anon_inode_getfile_fmode("[papr-platform-dump]",
- &papr_platform_dump_handle_ops,
- (void *)params, O_RDONLY,
- FMODE_LSEEK | FMODE_PREAD);
- if (IS_ERR(file)) {
- err = PTR_ERR(file);
- goto put_fd;
+ rtas_work_area_free(params->work_area);
+ kfree(params);
+ return fd;
}
- fd_install(fd, file);
-
list_add(&params->list, &platform_dump_list);
pr_info("%s (%d) initiated platform dump for dump tag %llu\n",
current->comm, current->pid, dump_tag);
return fd;
-put_fd:
- put_unused_fd(fd);
-free_area:
- rtas_work_area_free(params->work_area);
- kfree(params);
- return err;
}
/*
diff --git a/arch/powerpc/platforms/pseries/papr-rtas-common.c b/arch/powerpc/platforms/pseries/papr-rtas-common.c
index 33c606e3378a..1630e0cd210c 100644
--- a/arch/powerpc/platforms/pseries/papr-rtas-common.c
+++ b/arch/powerpc/platforms/pseries/papr-rtas-common.c
@@ -205,35 +205,18 @@ long papr_rtas_setup_file_interface(struct papr_rtas_sequence *seq,
char *name)
{
const struct papr_rtas_blob *blob;
- struct file *file;
- long ret;
int fd;
blob = papr_rtas_retrieve(seq);
if (IS_ERR(blob))
return PTR_ERR(blob);
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto free_blob;
- }
-
- file = anon_inode_getfile_fmode(name, fops, (void *)blob,
- O_RDONLY, FMODE_LSEEK | FMODE_PREAD);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto put_fd;
- }
-
- fd_install(fd, file);
+ fd = FD_ADD(O_RDONLY | O_CLOEXEC,
+ anon_inode_getfile_fmode(name, fops, (void *)blob, O_RDONLY,
+ FMODE_LSEEK | FMODE_PREAD));
+ if (fd < 0)
+ papr_rtas_blob_free(blob);
return fd;
-
-put_fd:
- put_unused_fd(fd);
-free_blob:
- papr_rtas_blob_free(blob);
- return ret;
}
/*
diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index 22cda9c452d2..fadec20b87a8 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -367,7 +367,7 @@ config RISCV_NONSTANDARD_CACHE_OPS
systems to handle cache management.
config AS_HAS_INSN
- def_bool $(as-instr,.insn r 51$(comma) 0$(comma) 0$(comma) t0$(comma) t0$(comma) zero)
+ def_bool $(as-instr,.insn 0x100000f)
config AS_HAS_OPTION_ARCH
# https://github.com/llvm/llvm-project/commit/9e8ed3403c191ab9c4903e8eeb8f732ff8a43cb4
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index ecf2fcce2d92..4c6de57f65ef 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -134,21 +134,6 @@ endif
CHECKFLAGS += -D__riscv -D__riscv_xlen=$(BITS)
# Default target when executing plain make
-boot := arch/riscv/boot
-ifeq ($(CONFIG_XIP_KERNEL),y)
-KBUILD_IMAGE := $(boot)/xipImage
-else
-ifeq ($(CONFIG_RISCV_M_MODE)$(CONFIG_SOC_CANAAN_K210),yy)
-KBUILD_IMAGE := $(boot)/loader.bin
-else
-ifeq ($(CONFIG_EFI_ZBOOT),)
-KBUILD_IMAGE := $(boot)/Image.gz
-else
-KBUILD_IMAGE := $(boot)/vmlinuz.efi
-endif
-endif
-endif
-
boot := arch/riscv/boot
boot-image-y := Image
boot-image-$(CONFIG_KERNEL_BZIP2) := Image.bz2
@@ -159,7 +144,7 @@ boot-image-$(CONFIG_KERNEL_LZO) := Image.lzo
boot-image-$(CONFIG_KERNEL_ZSTD) := Image.zst
boot-image-$(CONFIG_KERNEL_XZ) := Image.xz
ifdef CONFIG_RISCV_M_MODE
-boot-image-$(CONFIG_ARCH_CANAAN) := loader.bin
+boot-image-$(CONFIG_SOC_CANAAN_K210) := loader.bin
endif
boot-image-$(CONFIG_EFI_ZBOOT) := vmlinuz.efi
boot-image-$(CONFIG_XIP_KERNEL) := xipImage
diff --git a/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi b/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi
index 6367112e614a..a7442a508433 100644
--- a/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi
+++ b/arch/riscv/boot/dts/allwinner/sun20i-d1s.dtsi
@@ -28,7 +28,7 @@
riscv,isa-base = "rv64i";
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr",
"zifencei", "zihpm", "xtheadvector";
- thead,vlenb = <128>;
+ thead,vlenb = <16>;
#cooling-cells = <2>;
cpu0_intc: interrupt-controller {
diff --git a/arch/riscv/include/asm/asm.h b/arch/riscv/include/asm/asm.h
index 8bd2a11382a3..e9e8ba83e632 100644
--- a/arch/riscv/include/asm/asm.h
+++ b/arch/riscv/include/asm/asm.h
@@ -12,6 +12,12 @@
#define __ASM_STR(x) #x
#endif
+#ifdef CONFIG_AS_HAS_INSN
+#define ASM_INSN_I(__x) ".insn " __x
+#else
+#define ASM_INSN_I(__x) ".4byte " __x
+#endif
+
#if __riscv_xlen == 64
#define __REG_SEL(a, b) __ASM_STR(a)
#elif __riscv_xlen == 32
@@ -84,15 +90,9 @@
.endm
#ifdef CONFIG_SMP
-#ifdef CONFIG_32BIT
-#define PER_CPU_OFFSET_SHIFT 2
-#else
-#define PER_CPU_OFFSET_SHIFT 3
-#endif
-
.macro asm_per_cpu dst sym tmp
lw \tmp, TASK_TI_CPU_NUM(tp)
- slli \tmp, \tmp, PER_CPU_OFFSET_SHIFT
+ slli \tmp, \tmp, RISCV_LGPTR
la \dst, __per_cpu_offset
add \dst, \dst, \tmp
REG_L \tmp, 0(\dst)
diff --git a/arch/riscv/include/asm/bug.h b/arch/riscv/include/asm/bug.h
index 4c03e20ad11f..6f581b84d8fc 100644
--- a/arch/riscv/include/asm/bug.h
+++ b/arch/riscv/include/asm/bug.h
@@ -60,28 +60,28 @@ typedef u32 bug_insn_t;
".org 2b + " size "\n\t" \
".popsection" \
-#define __BUG_FLAGS(flags) \
+#define __BUG_FLAGS(cond_str, flags) \
do { \
__asm__ __volatile__ ( \
ARCH_WARN_ASM("%0", "%1", "%2", "%3") \
: \
- : "i" (__FILE__), "i" (__LINE__), \
+ : "i" (WARN_CONDITION_STR(cond_str) __FILE__), "i" (__LINE__), \
"i" (flags), \
"i" (sizeof(struct bug_entry))); \
} while (0)
#else /* CONFIG_GENERIC_BUG */
-#define __BUG_FLAGS(flags) do { \
+#define __BUG_FLAGS(cond_str, flags) do { \
__asm__ __volatile__ ("ebreak\n"); \
} while (0)
#endif /* CONFIG_GENERIC_BUG */
#define BUG() do { \
- __BUG_FLAGS(0); \
+ __BUG_FLAGS("", 0); \
unreachable(); \
} while (0)
-#define __WARN_FLAGS(flags) __BUG_FLAGS(BUGFLAG_WARNING|(flags))
+#define __WARN_FLAGS(cond_str, flags) __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags))
#define ARCH_WARN_REACHABLE
diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
index fbd0e4306c93..62837fa981e8 100644
--- a/arch/riscv/include/asm/cpufeature.h
+++ b/arch/riscv/include/asm/cpufeature.h
@@ -31,6 +31,8 @@ struct riscv_isainfo {
DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
+extern const struct seq_operations cpuinfo_op;
+
/* Per-cpu ISA extensions. */
extern struct riscv_isainfo hart_isa[NR_CPUS];
diff --git a/arch/riscv/include/asm/hwprobe.h b/arch/riscv/include/asm/hwprobe.h
index 948d2b34e94e..58f8dda73259 100644
--- a/arch/riscv/include/asm/hwprobe.h
+++ b/arch/riscv/include/asm/hwprobe.h
@@ -42,4 +42,11 @@ static inline bool riscv_hwprobe_pair_cmp(struct riscv_hwprobe *pair,
return pair->value == other_pair->value;
}
+#ifdef CONFIG_MMU
+void riscv_hwprobe_register_async_probe(void);
+void riscv_hwprobe_complete_async_probe(void);
+#else
+static inline void riscv_hwprobe_register_async_probe(void) {}
+static inline void riscv_hwprobe_complete_async_probe(void) {}
+#endif
#endif
diff --git a/arch/riscv/include/asm/insn-def.h b/arch/riscv/include/asm/insn-def.h
index c9cfcea52cbb..d29da6ccd3dd 100644
--- a/arch/riscv/include/asm/insn-def.h
+++ b/arch/riscv/include/asm/insn-def.h
@@ -256,10 +256,10 @@
INSN_S(OPCODE_OP_IMM, FUNC3(6), __RS2(3), \
SIMM12((offset) & 0xfe0), RS1(base))
-#define RISCV_PAUSE ".4byte 0x100000f"
-#define ZAWRS_WRS_NTO ".4byte 0x00d00073"
-#define ZAWRS_WRS_STO ".4byte 0x01d00073"
-#define RISCV_NOP4 ".4byte 0x00000013"
+#define RISCV_PAUSE ASM_INSN_I("0x100000f")
+#define ZAWRS_WRS_NTO ASM_INSN_I("0x00d00073")
+#define ZAWRS_WRS_STO ASM_INSN_I("0x01d00073")
+#define RISCV_NOP4 ASM_INSN_I("0x00000013")
#define RISCV_INSN_NOP4 _AC(0x00000013, U)
diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h
index 1018d2216901..6e789fa58514 100644
--- a/arch/riscv/include/asm/pgtable-64.h
+++ b/arch/riscv/include/asm/pgtable-64.h
@@ -69,6 +69,8 @@ typedef struct {
#define PTRS_PER_PMD (PAGE_SIZE / sizeof(pmd_t))
+#define MAX_POSSIBLE_PHYSMEM_BITS 56
+
/*
* rv64 PTE format:
* | 63 | 62 61 | 60 54 | 53 10 | 9 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 29e994a9afb6..5a08eb5fe99f 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -654,6 +654,8 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
return __pgprot(prot);
}
+#define pgprot_dmacoherent pgprot_writecombine
+
/*
* Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
* default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h
index f5f4f7f85543..36bba6720c26 100644
--- a/arch/riscv/include/asm/uaccess.h
+++ b/arch/riscv/include/asm/uaccess.h
@@ -437,10 +437,10 @@ unsigned long __must_check clear_user(void __user *to, unsigned long n)
__clear_user(untagged_addr(to), n) : n;
}
-#define __get_kernel_nofault(dst, src, type, err_label) \
+#define arch_get_kernel_nofault(dst, src, type, err_label) \
__get_user_nocheck(*((type *)(dst)), (__force __user type *)(src), err_label)
-#define __put_kernel_nofault(dst, src, type, err_label) \
+#define arch_put_kernel_nofault(dst, src, type, err_label) \
__put_user_nocheck(*((type *)(src)), (__force __user type *)(dst), err_label)
static __must_check __always_inline bool user_access_begin(const void __user *ptr, size_t len)
@@ -460,10 +460,10 @@ static inline void user_access_restore(unsigned long enabled) { }
* We want the unsafe accessors to always be inlined and use
* the error labels - thus the macro games.
*/
-#define unsafe_put_user(x, ptr, label) \
+#define arch_unsafe_put_user(x, ptr, label) \
__put_user_nocheck(x, (ptr), label)
-#define unsafe_get_user(x, ptr, label) do { \
+#define arch_unsafe_get_user(x, ptr, label) do { \
__inttype(*(ptr)) __gu_val; \
__get_user_nocheck(__gu_val, (ptr), label); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
diff --git a/arch/riscv/include/asm/vdso/arch_data.h b/arch/riscv/include/asm/vdso/arch_data.h
index da57a3786f7a..88b37af55175 100644
--- a/arch/riscv/include/asm/vdso/arch_data.h
+++ b/arch/riscv/include/asm/vdso/arch_data.h
@@ -12,6 +12,12 @@ struct vdso_arch_data {
/* Boolean indicating all CPUs have the same static hwprobe values. */
__u8 homogeneous_cpus;
+
+ /*
+ * A gate to check and see if the hwprobe data is actually ready, as
+ * probing is deferred to avoid boot slowdowns.
+ */
+ __u8 ready;
};
#endif /* __RISCV_ASM_VDSO_ARCH_DATA_H */
diff --git a/arch/riscv/include/asm/vendor_extensions/mips.h b/arch/riscv/include/asm/vendor_extensions/mips.h
index ea8ca747d691..ffeb12dc17a3 100644
--- a/arch/riscv/include/asm/vendor_extensions/mips.h
+++ b/arch/riscv/include/asm/vendor_extensions/mips.h
@@ -30,8 +30,8 @@ extern struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_mips;
* allowing any subsequent instructions to fetch.
*/
-#define MIPS_PAUSE ".4byte 0x00501013\n\t"
-#define MIPS_EHB ".4byte 0x00301013\n\t"
-#define MIPS_IHB ".4byte 0x00101013\n\t"
+#define MIPS_PAUSE ASM_INSN_I("0x00501013\n\t")
+#define MIPS_EHB ASM_INSN_I("0x00301013\n\t")
+#define MIPS_IHB ASM_INSN_I("0x00101013\n\t")
#endif // _ASM_RISCV_VENDOR_EXTENSIONS_MIPS_H
diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h
index 3b09874d7a6d..7f5030ee1fcf 100644
--- a/arch/riscv/include/asm/vendorid_list.h
+++ b/arch/riscv/include/asm/vendorid_list.h
@@ -7,8 +7,8 @@
#define ANDES_VENDOR_ID 0x31e
#define MICROCHIP_VENDOR_ID 0x029
+#define MIPS_VENDOR_ID 0x127
#define SIFIVE_VENDOR_ID 0x489
#define THEAD_VENDOR_ID 0x5b7
-#define MIPS_VENDOR_ID 0x722
#endif
diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
index f6b13e9f5e6c..3dbc8cc557dd 100644
--- a/arch/riscv/kernel/cpu.c
+++ b/arch/riscv/kernel/cpu.c
@@ -62,10 +62,8 @@ int __init riscv_early_of_processor_hartid(struct device_node *node, unsigned lo
return -ENODEV;
}
- if (!of_device_is_available(node)) {
- pr_info("CPU with hartid=%lu is not available\n", *hart);
+ if (!of_device_is_available(node))
return -ENODEV;
- }
if (of_property_read_string(node, "riscv,isa-base", &isa))
goto old_interface;
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 67b59699357d..72ca768f4e91 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -932,9 +932,9 @@ static int has_thead_homogeneous_vlenb(void)
{
int cpu;
u32 prev_vlenb = 0;
- u32 vlenb;
+ u32 vlenb = 0;
- /* Ignore thead,vlenb property if xtheavector is not enabled in the kernel */
+ /* Ignore thead,vlenb property if xtheadvector is not enabled in the kernel */
if (!IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR))
return 0;
diff --git a/arch/riscv/kernel/kgdb.c b/arch/riscv/kernel/kgdb.c
index 9f3db3503dab..15fec5d1e6de 100644
--- a/arch/riscv/kernel/kgdb.c
+++ b/arch/riscv/kernel/kgdb.c
@@ -265,10 +265,10 @@ void kgdb_arch_handle_qxfer_pkt(char *remcom_in_buffer,
{
if (!strncmp(remcom_in_buffer, gdb_xfer_read_target,
sizeof(gdb_xfer_read_target)))
- strcpy(remcom_out_buffer, riscv_gdb_stub_target_desc);
+ strscpy(remcom_out_buffer, riscv_gdb_stub_target_desc, BUFMAX);
else if (!strncmp(remcom_in_buffer, gdb_xfer_read_cpuxml,
sizeof(gdb_xfer_read_cpuxml)))
- strcpy(remcom_out_buffer, riscv_gdb_stub_cpuxml);
+ strscpy(remcom_out_buffer, riscv_gdb_stub_cpuxml, BUFMAX);
}
static inline void kgdb_arch_update_addr(struct pt_regs *regs,
diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c
index 75551ac6504c..1675cbad8619 100644
--- a/arch/riscv/kernel/module-sections.c
+++ b/arch/riscv/kernel/module-sections.c
@@ -119,6 +119,7 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
unsigned int num_plts = 0;
unsigned int num_gots = 0;
Elf_Rela *scratch = NULL;
+ Elf_Rela *new_scratch;
size_t scratch_size = 0;
int i;
@@ -168,9 +169,12 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
scratch_size_needed = (num_scratch_relas + num_relas) * sizeof(*scratch);
if (scratch_size_needed > scratch_size) {
scratch_size = scratch_size_needed;
- scratch = kvrealloc(scratch, scratch_size, GFP_KERNEL);
- if (!scratch)
+ new_scratch = kvrealloc(scratch, scratch_size, GFP_KERNEL);
+ if (!new_scratch) {
+ kvfree(scratch);
return -ENOMEM;
+ }
+ scratch = new_scratch;
}
for (size_t j = 0; j < num_relas; j++)
diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
index 5e8cde055264..c443337056ab 100644
--- a/arch/riscv/kernel/sbi.c
+++ b/arch/riscv/kernel/sbi.c
@@ -648,9 +648,9 @@ int sbi_debug_console_read(char *bytes, unsigned int num_bytes)
void __init sbi_init(void)
{
+ bool srst_power_off = false;
int ret;
- sbi_set_power_off();
ret = sbi_get_spec_version();
if (ret > 0)
sbi_spec_version = ret;
@@ -683,6 +683,7 @@ void __init sbi_init(void)
sbi_probe_extension(SBI_EXT_SRST)) {
pr_info("SBI SRST extension detected\n");
register_platform_power_off(sbi_srst_power_off);
+ srst_power_off = true;
sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot;
sbi_srst_reboot_nb.priority = 192;
register_restart_handler(&sbi_srst_reboot_nb);
@@ -702,4 +703,7 @@ void __init sbi_init(void)
__sbi_send_ipi = __sbi_send_ipi_v01;
__sbi_rfence = __sbi_rfence_v01;
}
+
+ if (!srst_power_off)
+ sbi_set_power_off();
}
diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c
index e650dec44817..5ed5095320e6 100644
--- a/arch/riscv/kernel/smp.c
+++ b/arch/riscv/kernel/smp.c
@@ -40,6 +40,17 @@ enum ipi_message_type {
IPI_MAX
};
+static const char * const ipi_names[] = {
+ [IPI_RESCHEDULE] = "Rescheduling interrupts",
+ [IPI_CALL_FUNC] = "Function call interrupts",
+ [IPI_CPU_STOP] = "CPU stop interrupts",
+ [IPI_CPU_CRASH_STOP] = "CPU stop (for crash dump) interrupts",
+ [IPI_IRQ_WORK] = "IRQ work interrupts",
+ [IPI_TIMER] = "Timer broadcast interrupts",
+ [IPI_CPU_BACKTRACE] = "CPU backtrace interrupts",
+ [IPI_KGDB_ROUNDUP] = "KGDB roundup interrupts",
+};
+
unsigned long __cpuid_to_hartid_map[NR_CPUS] __ro_after_init = {
[0 ... NR_CPUS-1] = INVALID_HARTID
};
@@ -199,7 +210,7 @@ void riscv_ipi_set_virq_range(int virq, int nr)
/* Request IPIs */
for (i = 0; i < nr_ipi; i++) {
err = request_percpu_irq(ipi_virq_base + i, handle_IPI,
- "IPI", &ipi_dummy_dev);
+ ipi_names[i], &ipi_dummy_dev);
WARN_ON(err);
ipi_desc[i] = irq_to_desc(ipi_virq_base + i);
@@ -210,17 +221,6 @@ void riscv_ipi_set_virq_range(int virq, int nr)
riscv_ipi_enable();
}
-static const char * const ipi_names[] = {
- [IPI_RESCHEDULE] = "Rescheduling interrupts",
- [IPI_CALL_FUNC] = "Function call interrupts",
- [IPI_CPU_STOP] = "CPU stop interrupts",
- [IPI_CPU_CRASH_STOP] = "CPU stop (for crash dump) interrupts",
- [IPI_IRQ_WORK] = "IRQ work interrupts",
- [IPI_TIMER] = "Timer broadcast interrupts",
- [IPI_CPU_BACKTRACE] = "CPU backtrace interrupts",
- [IPI_KGDB_ROUNDUP] = "KGDB roundup interrupts",
-};
-
void show_ipi_stats(struct seq_file *p, int prec)
{
unsigned int cpu, i;
diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
index 3fe9e6edef8f..b41b6255751c 100644
--- a/arch/riscv/kernel/stacktrace.c
+++ b/arch/riscv/kernel/stacktrace.c
@@ -16,6 +16,22 @@
#ifdef CONFIG_FRAME_POINTER
+/*
+ * This disables KASAN checking when reading a value from another task's stack,
+ * since the other task could be running on another CPU and could have poisoned
+ * the stack in the meantime.
+ */
+#define READ_ONCE_TASK_STACK(task, x) \
+({ \
+ unsigned long val; \
+ unsigned long addr = x; \
+ if ((task) == current) \
+ val = READ_ONCE(addr); \
+ else \
+ val = READ_ONCE_NOCHECK(addr); \
+ val; \
+})
+
extern asmlinkage void handle_exception(void);
extern unsigned long ret_from_exception_end;
@@ -69,8 +85,9 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
fp = frame->ra;
pc = regs->ra;
} else {
- fp = frame->fp;
- pc = ftrace_graph_ret_addr(current, &graph_idx, frame->ra,
+ fp = READ_ONCE_TASK_STACK(task, frame->fp);
+ pc = READ_ONCE_TASK_STACK(task, frame->ra);
+ pc = ftrace_graph_ret_addr(current, &graph_idx, pc,
&frame->ra);
if (pc >= (unsigned long)handle_exception &&
pc < (unsigned long)&ret_from_exception_end) {
diff --git a/arch/riscv/kernel/sys_hwprobe.c b/arch/riscv/kernel/sys_hwprobe.c
index 000f4451a9d8..199d13f86f31 100644
--- a/arch/riscv/kernel/sys_hwprobe.c
+++ b/arch/riscv/kernel/sys_hwprobe.c
@@ -5,6 +5,9 @@
* more details.
*/
#include <linux/syscalls.h>
+#include <linux/completion.h>
+#include <linux/atomic.h>
+#include <linux/once.h>
#include <asm/cacheflush.h>
#include <asm/cpufeature.h>
#include <asm/hwprobe.h>
@@ -28,6 +31,11 @@ static void hwprobe_arch_id(struct riscv_hwprobe *pair,
bool first = true;
int cpu;
+ if (pair->key != RISCV_HWPROBE_KEY_MVENDORID &&
+ pair->key != RISCV_HWPROBE_KEY_MIMPID &&
+ pair->key != RISCV_HWPROBE_KEY_MARCHID)
+ goto out;
+
for_each_cpu(cpu, cpus) {
u64 cpu_id;
@@ -58,6 +66,7 @@ static void hwprobe_arch_id(struct riscv_hwprobe *pair,
}
}
+out:
pair->value = id;
}
@@ -454,28 +463,32 @@ static int hwprobe_get_cpus(struct riscv_hwprobe __user *pairs,
return 0;
}
-static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
- size_t pair_count, size_t cpusetsize,
- unsigned long __user *cpus_user,
- unsigned int flags)
-{
- if (flags & RISCV_HWPROBE_WHICH_CPUS)
- return hwprobe_get_cpus(pairs, pair_count, cpusetsize,
- cpus_user, flags);
+#ifdef CONFIG_MMU
- return hwprobe_get_values(pairs, pair_count, cpusetsize,
- cpus_user, flags);
+static DECLARE_COMPLETION(boot_probes_done);
+static atomic_t pending_boot_probes = ATOMIC_INIT(1);
+
+void riscv_hwprobe_register_async_probe(void)
+{
+ atomic_inc(&pending_boot_probes);
}
-#ifdef CONFIG_MMU
+void riscv_hwprobe_complete_async_probe(void)
+{
+ if (atomic_dec_and_test(&pending_boot_probes))
+ complete(&boot_probes_done);
+}
-static int __init init_hwprobe_vdso_data(void)
+static int complete_hwprobe_vdso_data(void)
{
struct vdso_arch_data *avd = vdso_k_arch_data;
u64 id_bitsmash = 0;
struct riscv_hwprobe pair;
int key;
+ if (unlikely(!atomic_dec_and_test(&pending_boot_probes)))
+ wait_for_completion(&boot_probes_done);
+
/*
* Initialize vDSO data with the answers for the "all CPUs" case, to
* save a syscall in the common case.
@@ -503,13 +516,52 @@ static int __init init_hwprobe_vdso_data(void)
* vDSO should defer to the kernel for exotic cpu masks.
*/
avd->homogeneous_cpus = id_bitsmash != 0 && id_bitsmash != -1;
+
+ /*
+ * Make sure all the VDSO values are visible before we look at them.
+ * This pairs with the implicit "no speculativly visible accesses"
+ * barrier in the VDSO hwprobe code.
+ */
+ smp_wmb();
+ avd->ready = true;
+ return 0;
+}
+
+static int __init init_hwprobe_vdso_data(void)
+{
+ struct vdso_arch_data *avd = vdso_k_arch_data;
+
+ /*
+ * Prevent the vDSO cached values from being used, as they're not ready
+ * yet.
+ */
+ avd->ready = false;
return 0;
}
arch_initcall_sync(init_hwprobe_vdso_data);
+#else
+
+static int complete_hwprobe_vdso_data(void) { return 0; }
+
#endif /* CONFIG_MMU */
+static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
+ size_t pair_count, size_t cpusetsize,
+ unsigned long __user *cpus_user,
+ unsigned int flags)
+{
+ DO_ONCE_SLEEPABLE(complete_hwprobe_vdso_data);
+
+ if (flags & RISCV_HWPROBE_WHICH_CPUS)
+ return hwprobe_get_cpus(pairs, pair_count, cpusetsize,
+ cpus_user, flags);
+
+ return hwprobe_get_values(pairs, pair_count, cpusetsize,
+ cpus_user, flags);
+}
+
SYSCALL_DEFINE5(riscv_hwprobe, struct riscv_hwprobe __user *, pairs,
size_t, pair_count, size_t, cpusetsize, unsigned long __user *,
cpus, unsigned int, flags)
diff --git a/arch/riscv/kernel/tests/Kconfig.debug b/arch/riscv/kernel/tests/Kconfig.debug
index 5db4df44279e..40f8dafffa0a 100644
--- a/arch/riscv/kernel/tests/Kconfig.debug
+++ b/arch/riscv/kernel/tests/Kconfig.debug
@@ -31,7 +31,7 @@ config RISCV_MODULE_LINKING_KUNIT
If unsure, say N.
config RISCV_KPROBES_KUNIT
- bool "KUnit test for riscv kprobes" if !KUNIT_ALL_TESTS
+ tristate "KUnit test for riscv kprobes" if !KUNIT_ALL_TESTS
depends on KUNIT
depends on KPROBES
default KUNIT_ALL_TESTS
diff --git a/arch/riscv/kernel/tests/kprobes/Makefile b/arch/riscv/kernel/tests/kprobes/Makefile
index 4cb6c66a98e8..df7256f62313 100644
--- a/arch/riscv/kernel/tests/kprobes/Makefile
+++ b/arch/riscv/kernel/tests/kprobes/Makefile
@@ -1 +1,3 @@
-obj-y += test-kprobes.o test-kprobes-asm.o
+obj-$(CONFIG_RISCV_KPROBES_KUNIT) += kprobes_riscv_kunit.o
+
+kprobes_riscv_kunit-objs := test-kprobes.o test-kprobes-asm.o
diff --git a/arch/riscv/kernel/tests/kprobes/test-kprobes.c b/arch/riscv/kernel/tests/kprobes/test-kprobes.c
index 6f6cdfbf5a95..664535ca0a98 100644
--- a/arch/riscv/kernel/tests/kprobes/test-kprobes.c
+++ b/arch/riscv/kernel/tests/kprobes/test-kprobes.c
@@ -49,8 +49,11 @@ static struct kunit_case kprobes_testcases[] = {
};
static struct kunit_suite kprobes_test_suite = {
- .name = "kprobes_test_riscv",
+ .name = "kprobes_riscv",
.test_cases = kprobes_testcases,
};
kunit_test_suites(&kprobes_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit test for riscv kprobes");
diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c
index ae2068425fbc..70b5e6927620 100644
--- a/arch/riscv/kernel/unaligned_access_speed.c
+++ b/arch/riscv/kernel/unaligned_access_speed.c
@@ -379,6 +379,7 @@ free:
static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused)
{
schedule_on_each_cpu(check_vector_unaligned_access);
+ riscv_hwprobe_complete_async_probe();
return 0;
}
@@ -473,8 +474,12 @@ static int __init check_unaligned_access_all_cpus(void)
per_cpu(vector_misaligned_access, cpu) = unaligned_vector_speed_param;
} else if (!check_vector_unaligned_access_emulated_all_cpus() &&
IS_ENABLED(CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS)) {
- kthread_run(vec_check_unaligned_access_speed_all_cpus,
- NULL, "vec_check_unaligned_access_speed_all_cpus");
+ riscv_hwprobe_register_async_probe();
+ if (IS_ERR(kthread_run(vec_check_unaligned_access_speed_all_cpus,
+ NULL, "vec_check_unaligned_access_speed_all_cpus"))) {
+ pr_warn("Failed to create vec_unalign_check kthread\n");
+ riscv_hwprobe_complete_async_probe();
+ }
}
/*
diff --git a/arch/riscv/kernel/vdso/hwprobe.c b/arch/riscv/kernel/vdso/hwprobe.c
index 2ddeba6c68dd..8f45500d0a6e 100644
--- a/arch/riscv/kernel/vdso/hwprobe.c
+++ b/arch/riscv/kernel/vdso/hwprobe.c
@@ -27,7 +27,7 @@ static int riscv_vdso_get_values(struct riscv_hwprobe *pairs, size_t pair_count,
* homogeneous, then this function can handle requests for arbitrary
* masks.
*/
- if ((flags != 0) || (!all_cpus && !avd->homogeneous_cpus))
+ if (flags != 0 || (!all_cpus && !avd->homogeneous_cpus) || unlikely(!avd->ready))
return riscv_hwprobe(pairs, pair_count, cpusetsize, cpus, flags);
/* This is something we can handle, fill out the pairs. */
diff --git a/arch/riscv/kvm/aia_imsic.c b/arch/riscv/kvm/aia_imsic.c
index fda0346f0ea1..11422cb95a64 100644
--- a/arch/riscv/kvm/aia_imsic.c
+++ b/arch/riscv/kvm/aia_imsic.c
@@ -689,8 +689,20 @@ bool kvm_riscv_vcpu_aia_imsic_has_interrupt(struct kvm_vcpu *vcpu)
*/
read_lock_irqsave(&imsic->vsfile_lock, flags);
- if (imsic->vsfile_cpu > -1)
- ret = !!(csr_read(CSR_HGEIP) & BIT(imsic->vsfile_hgei));
+ if (imsic->vsfile_cpu > -1) {
+ /*
+ * This function is typically called from kvm_vcpu_block() via
+ * kvm_arch_vcpu_runnable() upon WFI trap. The kvm_vcpu_block()
+ * can be preempted and the blocking VCPU might resume on a
+ * different CPU. This means it is possible that current CPU
+ * does not match the imsic->vsfile_cpu hence this function
+ * must check imsic->vsfile_cpu before accessing HGEIP CSR.
+ */
+ if (imsic->vsfile_cpu != vcpu->cpu)
+ ret = true;
+ else
+ ret = !!(csr_read(CSR_HGEIP) & BIT(imsic->vsfile_hgei));
+ }
read_unlock_irqrestore(&imsic->vsfile_lock, flags);
return ret;
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 525fb5a330c0..58f5f3536ffd 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -171,7 +171,6 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
enum kvm_mr_change change)
{
hva_t hva, reg_end, size;
- gpa_t base_gpa;
bool writable;
int ret = 0;
@@ -190,15 +189,13 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
hva = new->userspace_addr;
size = new->npages << PAGE_SHIFT;
reg_end = hva + size;
- base_gpa = new->base_gfn << PAGE_SHIFT;
writable = !(new->flags & KVM_MEM_READONLY);
mmap_read_lock(current->mm);
/*
* A memory region could potentially cover multiple VMAs, and
- * any holes between them, so iterate over all of them to find
- * out if we can map any of them right now.
+ * any holes between them, so iterate over all of them.
*
* +--------------------------------------------+
* +---------------+----------------+ +----------------+
@@ -209,7 +206,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
*/
do {
struct vm_area_struct *vma;
- hva_t vm_start, vm_end;
+ hva_t vm_end;
vma = find_vma_intersection(current->mm, hva, reg_end);
if (!vma)
@@ -225,36 +222,18 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
}
/* Take the intersection of this VMA with the memory region */
- vm_start = max(hva, vma->vm_start);
vm_end = min(reg_end, vma->vm_end);
if (vma->vm_flags & VM_PFNMAP) {
- gpa_t gpa = base_gpa + (vm_start - hva);
- phys_addr_t pa;
-
- pa = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
- pa += vm_start - vma->vm_start;
-
/* IO region dirty page logging not allowed */
if (new->flags & KVM_MEM_LOG_DIRTY_PAGES) {
ret = -EINVAL;
goto out;
}
-
- ret = kvm_riscv_mmu_ioremap(kvm, gpa, pa, vm_end - vm_start,
- writable, false);
- if (ret)
- break;
}
hva = vm_end;
} while (hva < reg_end);
- if (change == KVM_MR_FLAGS_ONLY)
- goto out;
-
- if (ret)
- kvm_riscv_mmu_iounmap(kvm, base_gpa, size);
-
out:
mmap_read_unlock(current->mm);
return ret;
diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c
index bccb919ca615..5ce35aba6069 100644
--- a/arch/riscv/kvm/vcpu.c
+++ b/arch/riscv/kvm/vcpu.c
@@ -212,7 +212,7 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
- return (kvm_riscv_vcpu_has_interrupts(vcpu, -1UL) &&
+ return (kvm_riscv_vcpu_has_interrupts(vcpu, -1ULL) &&
!kvm_riscv_vcpu_stopped(vcpu) && !vcpu->arch.pause);
}
diff --git a/arch/riscv/mm/ptdump.c b/arch/riscv/mm/ptdump.c
index 3b51690cc876..34299c2b231f 100644
--- a/arch/riscv/mm/ptdump.c
+++ b/arch/riscv/mm/ptdump.c
@@ -21,7 +21,7 @@
#define pt_dump_seq_puts(m, fmt) \
({ \
if (m) \
- seq_printf(m, fmt); \
+ seq_puts(m, fmt); \
})
/*
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 45cbc7c6fe49..5f9457e910e8 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -852,17 +852,19 @@ static int gen_jump_or_nops(void *target, void *ip, u32 *insns, bool is_call)
return emit_jump_and_link(is_call ? RV_REG_T0 : RV_REG_ZERO, rvoff, false, &ctx);
}
-int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
- void *old_addr, void *new_addr)
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
+ enum bpf_text_poke_type new_t, void *old_addr,
+ void *new_addr)
{
u32 old_insns[RV_FENTRY_NINSNS], new_insns[RV_FENTRY_NINSNS];
- bool is_call = poke_type == BPF_MOD_CALL;
+ bool is_call;
int ret;
if (!is_kernel_text((unsigned long)ip) &&
!is_bpf_text_address((unsigned long)ip))
return -ENOTSUPP;
+ is_call = old_t == BPF_MOD_CALL;
ret = gen_jump_or_nops(old_addr, ip, old_insns, is_call);
if (ret)
return ret;
@@ -870,6 +872,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type poke_type,
if (memcmp(ip, old_insns, RV_FENTRY_NBYTES))
return -EFAULT;
+ is_call = new_t == BPF_MOD_CALL;
ret = gen_jump_or_nops(new_addr, ip, new_insns, is_call);
if (ret)
return ret;
@@ -1131,7 +1134,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im,
store_args(nr_arg_slots, args_off, ctx);
/* skip to actual body of traced function */
- if (flags & BPF_TRAMP_F_SKIP_FRAME)
+ if (flags & BPF_TRAMP_F_ORIG_STACK)
orig_call += RV_FENTRY_NINSNS * 4;
if (flags & BPF_TRAMP_F_CALL_ORIG) {
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index c4145672ca34..938e5df75b2d 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -69,6 +69,9 @@ config CC_HAS_ASM_AOR_FORMAT_FLAGS
Clang versions before 19.1.0 do not support A,
O, and R inline assembly format flags.
+config CC_HAS_STACKPROTECTOR_GLOBAL
+ def_bool $(cc-option, -mstack-protector-guard=global -mstack-protector-guard-record)
+
config S390
def_bool y
#
@@ -140,7 +143,6 @@ config S390
select ARCH_INLINE_WRITE_UNLOCK_IRQ
select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE
- select ARCH_MODULE_NEEDS_WEAK_PER_CPU
select ARCH_STACKWALK
select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_DEBUG_PAGEALLOC
@@ -158,7 +160,6 @@ config S390
select ARCH_WANT_IRQS_OFF_ACTIVATE_MM
select ARCH_WANT_KERNEL_PMD_MKWRITE
select ARCH_WANT_LD_ORPHAN_WARN
- select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP
select ARCH_WANTS_THP_SWAP
select BUILDTIME_TABLE_SORT
select CLONE_BACKWARDS2
@@ -246,6 +247,7 @@ config S390
select HAVE_SAMPLE_FTRACE_DIRECT_MULTI
select HAVE_SETUP_PER_CPU_AREA
select HAVE_SOFTIRQ_ON_OWN_STACK
+ select HAVE_STACKPROTECTOR if CC_HAS_STACKPROTECTOR_GLOBAL
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_VIRT_CPU_ACCOUNTING
select HAVE_VIRT_CPU_ACCOUNTING_IDLE
@@ -505,22 +507,6 @@ config COMMAND_LINE_SIZE
This allows you to specify the maximum length of the kernel command
line.
-config COMPAT
- def_bool n
- prompt "Kernel support for 31 bit emulation"
- select ARCH_WANT_OLD_COMPAT_IPC
- select COMPAT_OLD_SIGACTION
- select HAVE_UID16
- depends on MULTIUSER
- depends on !CC_IS_CLANG && !LD_IS_LLD
- help
- Select this option if you want to enable your system kernel to
- handle system-calls from ELF binaries for 31 bit ESA. This option
- (and some other stuff like libraries and such) is needed for
- executing 31 bit applications.
-
- If unsure say N.
-
config SMP
def_bool y
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index b4769241332b..d78ad6885ca2 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -22,7 +22,7 @@ KBUILD_AFLAGS_DECOMPRESSOR := $(CLANG_FLAGS) -m64 -D__ASSEMBLY__
ifndef CONFIG_AS_IS_LLVM
KBUILD_AFLAGS_DECOMPRESSOR += $(if $(CONFIG_DEBUG_INFO),$(aflags_dwarf))
endif
-KBUILD_CFLAGS_DECOMPRESSOR := $(CLANG_FLAGS) -m64 -O2 -mpacked-stack -std=gnu11
+KBUILD_CFLAGS_DECOMPRESSOR := $(CLANG_FLAGS) -m64 -O2 -mpacked-stack -std=gnu11 -fms-extensions
KBUILD_CFLAGS_DECOMPRESSOR += -DDISABLE_BRANCH_PROFILING -D__NO_FORTIFY
KBUILD_CFLAGS_DECOMPRESSOR += -D__DECOMPRESSOR
KBUILD_CFLAGS_DECOMPRESSOR += -Wno-pointer-sign
@@ -35,6 +35,7 @@ KBUILD_CFLAGS_DECOMPRESSOR += $(call cc-disable-warning, address-of-packed-membe
KBUILD_CFLAGS_DECOMPRESSOR += $(if $(CONFIG_DEBUG_INFO),-g)
KBUILD_CFLAGS_DECOMPRESSOR += $(if $(CONFIG_DEBUG_INFO_DWARF4), $(call cc-option, -gdwarf-4,))
KBUILD_CFLAGS_DECOMPRESSOR += $(if $(CONFIG_CC_NO_ARRAY_BOUNDS),-Wno-array-bounds)
+KBUILD_CFLAGS_DECOMPRESSOR += $(if $(CONFIG_CC_IS_CLANG),-Wno-microsoft-anon-tag)
UTS_MACHINE := s390x
STACK_SIZE := $(if $(CONFIG_KASAN),65536,$(if $(CONFIG_KMSAN),65536,16384))
@@ -89,6 +90,10 @@ ifdef CONFIG_EXPOLINE
aflags-y += -DCC_USING_EXPOLINE
endif
+ifeq ($(CONFIG_STACKPROTECTOR),y)
+ KBUILD_CFLAGS += -mstack-protector-guard=global -mstack-protector-guard-record
+endif
+
ifdef CONFIG_FUNCTION_TRACER
ifeq ($(call cc-option,-mfentry -mnop-mcount),)
# make use of hotpatch feature if the compiler supports it
@@ -134,10 +139,9 @@ zfcpdump:
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
archheaders:
- $(Q)$(MAKE) $(build)=$(syscalls) uapi
+ $(Q)$(MAKE) $(build)=$(syscalls) all
archprepare:
- $(Q)$(MAKE) $(build)=$(syscalls) kapi
$(Q)$(MAKE) $(build)=$(tools) kapi $(extra_tools)
ifeq ($(KBUILD_EXTMOD),)
# We need to generate vdso-offsets.h before compiling certain files in kernel/.
@@ -148,12 +152,9 @@ ifeq ($(KBUILD_EXTMOD),)
# this hack.
prepare: vdso_prepare
vdso_prepare: prepare0
- $(Q)$(MAKE) $(build)=arch/s390/kernel/vdso64 include/generated/vdso64-offsets.h
- $(if $(CONFIG_COMPAT),$(Q)$(MAKE) \
- $(build)=arch/s390/kernel/vdso32 include/generated/vdso32-offsets.h)
+ $(Q)$(MAKE) $(build)=arch/s390/kernel/vdso include/generated/vdso-offsets.h
-vdso-install-y += arch/s390/kernel/vdso64/vdso64.so.dbg
-vdso-install-$(CONFIG_COMPAT) += arch/s390/kernel/vdso32/vdso32.so.dbg
+vdso-install-y += arch/s390/kernel/vdso/vdso.so.dbg
endif
diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c
index ad2b0baa527c..feb43db63f30 100644
--- a/arch/s390/appldata/appldata_base.c
+++ b/arch/s390/appldata/appldata_base.c
@@ -9,8 +9,7 @@
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
-#define KMSG_COMPONENT "appldata"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "appldata: " fmt
#include <linux/export.h>
#include <linux/module.h>
diff --git a/arch/s390/appldata/appldata_os.c b/arch/s390/appldata/appldata_os.c
index a363d30ce739..137d4e7e1e9a 100644
--- a/arch/s390/appldata/appldata_os.c
+++ b/arch/s390/appldata/appldata_os.c
@@ -8,8 +8,7 @@
* Author: Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
-#define KMSG_COMPONENT "appldata"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "appldata: " fmt
#include <linux/module.h>
#include <linux/init.h>
diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile
index 02f2cf082748..490167faba7a 100644
--- a/arch/s390/boot/Makefile
+++ b/arch/s390/boot/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
obj-y += $(if $(CONFIG_KERNEL_UNCOMPRESSED),,decompressor.o) info.o
obj-$(CONFIG_KERNEL_ZSTD) += clz_ctz.o
obj-$(CONFIG_KMSAN) += kmsan.o
+obj-$(CONFIG_STACKPROTECTOR) += stackprotector.o
obj-all := $(obj-y) piggy.o syms.o
targets := bzImage section_cmp.boot.data section_cmp.boot.preserved.data $(obj-y)
diff --git a/arch/s390/boot/boot.h b/arch/s390/boot/boot.h
index 37d5b097ede5..61a205b489fb 100644
--- a/arch/s390/boot/boot.h
+++ b/arch/s390/boot/boot.h
@@ -28,6 +28,10 @@ struct vmlinux_info {
unsigned long invalid_pg_dir_off;
unsigned long alt_instructions;
unsigned long alt_instructions_end;
+#ifdef CONFIG_STACKPROTECTOR
+ unsigned long stack_prot_start;
+ unsigned long stack_prot_end;
+#endif
#ifdef CONFIG_KASAN
unsigned long kasan_early_shadow_page_off;
unsigned long kasan_early_shadow_pte_off;
diff --git a/arch/s390/boot/ipl_data.c b/arch/s390/boot/ipl_data.c
index c4130a80b058..b0fd8a526b42 100644
--- a/arch/s390/boot/ipl_data.c
+++ b/arch/s390/boot/ipl_data.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-#include <linux/compat.h>
#include <linux/ptrace.h>
#include <asm/cio.h>
#include <asm/asm-offsets.h>
@@ -12,7 +11,7 @@
#define PSW_MASK_DISABLED (PSW_MASK_WAIT | PSW_MASK_EA | PSW_MASK_BA)
struct ipl_lowcore {
- psw_t32 ipl_psw; /* 0x0000 */
+ psw32_t ipl_psw; /* 0x0000 */
struct ccw0 ccwpgm[2]; /* 0x0008 */
u8 fill[56]; /* 0x0018 */
struct ccw0 ccwpgmcc[20]; /* 0x0050 */
diff --git a/arch/s390/boot/ipl_parm.c b/arch/s390/boot/ipl_parm.c
index f584d7da29cb..6bc950b92be7 100644
--- a/arch/s390/boot/ipl_parm.c
+++ b/arch/s390/boot/ipl_parm.c
@@ -3,6 +3,7 @@
#include <linux/init.h>
#include <linux/ctype.h>
#include <linux/pgtable.h>
+#include <asm/arch-stackprotector.h>
#include <asm/abs_lowcore.h>
#include <asm/page-states.h>
#include <asm/machine.h>
@@ -294,6 +295,11 @@ void parse_boot_command_line(void)
cmma_flag = 0;
}
+#ifdef CONFIG_STACKPROTECTOR
+ if (!strcmp(param, "debug_stackprotector"))
+ stack_protector_debug = 1;
+#endif
+
#if IS_ENABLED(CONFIG_KVM)
if (!strcmp(param, "prot_virt")) {
rc = kstrtobool(val, &enabled);
diff --git a/arch/s390/boot/stackprotector.c b/arch/s390/boot/stackprotector.c
new file mode 100644
index 000000000000..68494940c12a
--- /dev/null
+++ b/arch/s390/boot/stackprotector.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define boot_fmt(fmt) "stackprot: " fmt
+
+#include "boot.h"
+#include "../kernel/stackprotector.c"
diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c
index 3fbd25b9498f..f77067dfc2a8 100644
--- a/arch/s390/boot/startup.c
+++ b/arch/s390/boot/startup.c
@@ -20,6 +20,9 @@
#include <asm/uv.h>
#include <asm/abs_lowcore.h>
#include <asm/physmem_info.h>
+#include <asm/stacktrace.h>
+#include <asm/asm-offsets.h>
+#include <asm/arch-stackprotector.h>
#include "decompressor.h"
#include "boot.h"
#include "uv.h"
@@ -477,6 +480,10 @@ static void kaslr_adjust_vmlinux_info(long offset)
vmlinux.invalid_pg_dir_off += offset;
vmlinux.alt_instructions += offset;
vmlinux.alt_instructions_end += offset;
+#ifdef CONFIG_STACKPROTECTOR
+ vmlinux.stack_prot_start += offset;
+ vmlinux.stack_prot_end += offset;
+#endif
#ifdef CONFIG_KASAN
vmlinux.kasan_early_shadow_page_off += offset;
vmlinux.kasan_early_shadow_pte_off += offset;
@@ -622,6 +629,7 @@ void startup_kernel(void)
__apply_alternatives((struct alt_instr *)_vmlinux_info.alt_instructions,
(struct alt_instr *)_vmlinux_info.alt_instructions_end,
ALT_CTX_EARLY);
+ stack_protector_apply_early(text_lma);
/*
* Save KASLR offset for early dumps, before vmcore_info is set.
diff --git a/arch/s390/configs/compat.config b/arch/s390/configs/compat.config
deleted file mode 100644
index 6fd051453ae8..000000000000
--- a/arch/s390/configs/compat.config
+++ /dev/null
@@ -1,3 +0,0 @@
-# Help: Enable compat support
-CONFIG_COMPAT=y
-CONFIG_COMPAT_32BIT_TIME=y
diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig
index b31c1df90257..0713914b25b4 100644
--- a/arch/s390/configs/debug_defconfig
+++ b/arch/s390/configs/debug_defconfig
@@ -101,6 +101,7 @@ CONFIG_SLUB_STATS=y
CONFIG_MEMORY_HOTPLUG=y
CONFIG_MEMORY_HOTREMOVE=y
CONFIG_KSM=y
+CONFIG_PERSISTENT_HUGE_ZERO_FOLIO=y
CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_CMA_DEBUGFS=y
CONFIG_CMA_SYSFS=y
@@ -123,12 +124,12 @@ CONFIG_TLS_DEVICE=y
CONFIG_TLS_TOE=y
CONFIG_XFRM_USER=m
CONFIG_NET_KEY=m
-CONFIG_XDP_SOCKETS=y
-CONFIG_XDP_SOCKETS_DIAG=m
-CONFIG_DIBS=y
-CONFIG_DIBS_LO=y
CONFIG_SMC=m
CONFIG_SMC_DIAG=m
+CONFIG_DIBS=y
+CONFIG_DIBS_LO=y
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=m
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
@@ -472,6 +473,7 @@ CONFIG_SCSI_DH_EMC=m
CONFIG_SCSI_DH_ALUA=m
CONFIG_MD=y
CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LLBITMAP=y
# CONFIG_MD_BITMAP_FILE is not set
CONFIG_MD_LINEAR=m
CONFIG_MD_CLUSTER=m
@@ -654,9 +656,12 @@ CONFIG_JFS_POSIX_ACL=y
CONFIG_JFS_SECURITY=y
CONFIG_JFS_STATISTICS=y
CONFIG_XFS_FS=y
+CONFIG_XFS_SUPPORT_V4=y
+CONFIG_XFS_SUPPORT_ASCII_CI=y
CONFIG_XFS_QUOTA=y
CONFIG_XFS_POSIX_ACL=y
CONFIG_XFS_RT=y
+# CONFIG_XFS_ONLINE_SCRUB is not set
CONFIG_XFS_DEBUG=y
CONFIG_GFS2_FS=m
CONFIG_GFS2_FS_LOCKING_DLM=y
@@ -666,7 +671,6 @@ CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_BTRFS_DEBUG=y
CONFIG_BTRFS_ASSERT=y
CONFIG_NILFS2_FS=m
-CONFIG_FS_DAX=y
CONFIG_EXPORTFS_BLOCK_OPS=y
CONFIG_FS_ENCRYPTION=y
CONFIG_FS_VERITY=y
@@ -792,6 +796,7 @@ CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA3=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -800,13 +805,10 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_USER_API_HASH=m
CONFIG_CRYPTO_USER_API_SKCIPHER=m
CONFIG_CRYPTO_USER_API_RNG=m
CONFIG_CRYPTO_USER_API_AEAD=m
-CONFIG_CRYPTO_SHA3_256_S390=m
-CONFIG_CRYPTO_SHA3_512_S390=m
CONFIG_CRYPTO_GHASH_S390=m
CONFIG_CRYPTO_AES_S390=m
CONFIG_CRYPTO_DES_S390=m
diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig
index 161dad7ef211..c064e0cacc98 100644
--- a/arch/s390/configs/defconfig
+++ b/arch/s390/configs/defconfig
@@ -94,6 +94,7 @@ CONFIG_SLAB_BUCKETS=y
CONFIG_MEMORY_HOTPLUG=y
CONFIG_MEMORY_HOTREMOVE=y
CONFIG_KSM=y
+CONFIG_PERSISTENT_HUGE_ZERO_FOLIO=y
CONFIG_TRANSPARENT_HUGEPAGE=y
CONFIG_CMA_SYSFS=y
CONFIG_CMA_AREAS=7
@@ -114,12 +115,12 @@ CONFIG_TLS_DEVICE=y
CONFIG_TLS_TOE=y
CONFIG_XFRM_USER=m
CONFIG_NET_KEY=m
-CONFIG_XDP_SOCKETS=y
-CONFIG_XDP_SOCKETS_DIAG=m
-CONFIG_DIBS=y
-CONFIG_DIBS_LO=y
CONFIG_SMC=m
CONFIG_SMC_DIAG=m
+CONFIG_DIBS=y
+CONFIG_DIBS_LO=y
+CONFIG_XDP_SOCKETS=y
+CONFIG_XDP_SOCKETS_DIAG=m
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
@@ -462,6 +463,7 @@ CONFIG_SCSI_DH_EMC=m
CONFIG_SCSI_DH_ALUA=m
CONFIG_MD=y
CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LLBITMAP=y
# CONFIG_MD_BITMAP_FILE is not set
CONFIG_MD_LINEAR=m
CONFIG_MD_CLUSTER=m
@@ -644,16 +646,18 @@ CONFIG_JFS_POSIX_ACL=y
CONFIG_JFS_SECURITY=y
CONFIG_JFS_STATISTICS=y
CONFIG_XFS_FS=y
+CONFIG_XFS_SUPPORT_V4=y
+CONFIG_XFS_SUPPORT_ASCII_CI=y
CONFIG_XFS_QUOTA=y
CONFIG_XFS_POSIX_ACL=y
CONFIG_XFS_RT=y
+# CONFIG_XFS_ONLINE_SCRUB is not set
CONFIG_GFS2_FS=m
CONFIG_GFS2_FS_LOCKING_DLM=y
CONFIG_OCFS2_FS=m
CONFIG_BTRFS_FS=y
CONFIG_BTRFS_FS_POSIX_ACL=y
CONFIG_NILFS2_FS=m
-CONFIG_FS_DAX=y
CONFIG_EXPORTFS_BLOCK_OPS=y
CONFIG_FS_ENCRYPTION=y
CONFIG_FS_VERITY=y
@@ -776,6 +780,7 @@ CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_SHA3=m
CONFIG_CRYPTO_SM3_GENERIC=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_XCBC=m
@@ -784,14 +789,11 @@ CONFIG_CRYPTO_842=m
CONFIG_CRYPTO_LZ4=m
CONFIG_CRYPTO_LZ4HC=m
CONFIG_CRYPTO_ZSTD=m
-CONFIG_CRYPTO_ANSI_CPRNG=m
CONFIG_CRYPTO_JITTERENTROPY_OSR=1
CONFIG_CRYPTO_USER_API_HASH=m
CONFIG_CRYPTO_USER_API_SKCIPHER=m
CONFIG_CRYPTO_USER_API_RNG=m
CONFIG_CRYPTO_USER_API_AEAD=m
-CONFIG_CRYPTO_SHA3_256_S390=m
-CONFIG_CRYPTO_SHA3_512_S390=m
CONFIG_CRYPTO_GHASH_S390=m
CONFIG_CRYPTO_AES_S390=m
CONFIG_CRYPTO_DES_S390=m
diff --git a/arch/s390/configs/zfcpdump_defconfig b/arch/s390/configs/zfcpdump_defconfig
index ed0b137353ad..b5478267d6a7 100644
--- a/arch/s390/configs/zfcpdump_defconfig
+++ b/arch/s390/configs/zfcpdump_defconfig
@@ -33,7 +33,6 @@ CONFIG_NET=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_SAFE=y
CONFIG_BLK_DEV_RAM=y
-# CONFIG_DCSSBLK is not set
# CONFIG_DASD is not set
CONFIG_ENCLOSURE_SERVICES=y
CONFIG_SCSI=y
diff --git a/arch/s390/crypto/Kconfig b/arch/s390/crypto/Kconfig
index 03f73fbd38b6..f838ca055f6d 100644
--- a/arch/s390/crypto/Kconfig
+++ b/arch/s390/crypto/Kconfig
@@ -2,26 +2,6 @@
menu "Accelerated Cryptographic Algorithms for CPU (s390)"
-config CRYPTO_SHA3_256_S390
- tristate "Hash functions: SHA3-224 and SHA3-256"
- select CRYPTO_HASH
- help
- SHA3-224 and SHA3-256 secure hash algorithms (FIPS 202)
-
- Architecture: s390
-
- It is available as of z14.
-
-config CRYPTO_SHA3_512_S390
- tristate "Hash functions: SHA3-384 and SHA3-512"
- select CRYPTO_HASH
- help
- SHA3-384 and SHA3-512 secure hash algorithms (FIPS 202)
-
- Architecture: s390
-
- It is available as of z14.
-
config CRYPTO_GHASH_S390
tristate "Hash functions: GHASH"
select CRYPTO_HASH
diff --git a/arch/s390/crypto/Makefile b/arch/s390/crypto/Makefile
index 998f4b656b18..387a229e1038 100644
--- a/arch/s390/crypto/Makefile
+++ b/arch/s390/crypto/Makefile
@@ -3,8 +3,6 @@
# Cryptographic API
#
-obj-$(CONFIG_CRYPTO_SHA3_256_S390) += sha3_256_s390.o sha_common.o
-obj-$(CONFIG_CRYPTO_SHA3_512_S390) += sha3_512_s390.o sha_common.o
obj-$(CONFIG_CRYPTO_DES_S390) += des_s390.o
obj-$(CONFIG_CRYPTO_AES_S390) += aes_s390.o
obj-$(CONFIG_CRYPTO_PAES_S390) += paes_s390.o
diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c
index 5d36f4020dfa..d0a295435680 100644
--- a/arch/s390/crypto/aes_s390.c
+++ b/arch/s390/crypto/aes_s390.c
@@ -14,8 +14,7 @@
* Derived from "crypto/aes_generic.c"
*/
-#define KMSG_COMPONENT "aes_s390"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "aes_s390: " fmt
#include <crypto/aes.h>
#include <crypto/algapi.h>
diff --git a/arch/s390/crypto/hmac_s390.c b/arch/s390/crypto/hmac_s390.c
index 58444da9b004..f8cd09f341d4 100644
--- a/arch/s390/crypto/hmac_s390.c
+++ b/arch/s390/crypto/hmac_s390.c
@@ -5,8 +5,7 @@
* s390 specific HMAC support.
*/
-#define KMSG_COMPONENT "hmac_s390"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "hmac_s390: " fmt
#include <asm/cpacf.h>
#include <crypto/internal/hash.h>
diff --git a/arch/s390/crypto/paes_s390.c b/arch/s390/crypto/paes_s390.c
index a624a43a2b54..64aef7eb2030 100644
--- a/arch/s390/crypto/paes_s390.c
+++ b/arch/s390/crypto/paes_s390.c
@@ -10,8 +10,7 @@
* Harald Freudenberger <freude@de.ibm.com>
*/
-#define KMSG_COMPONENT "paes_s390"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "paes_s390: " fmt
#include <linux/atomic.h>
#include <linux/cpufeature.h>
diff --git a/arch/s390/crypto/phmac_s390.c b/arch/s390/crypto/phmac_s390.c
index 7ecfdc4fba2d..88342bd4c37a 100644
--- a/arch/s390/crypto/phmac_s390.c
+++ b/arch/s390/crypto/phmac_s390.c
@@ -5,8 +5,7 @@
* s390 specific HMAC support for protected keys.
*/
-#define KMSG_COMPONENT "phmac_s390"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "phmac_s390: " fmt
#include <asm/cpacf.h>
#include <asm/pkey.h>
@@ -169,11 +168,18 @@ struct kmac_sha2_ctx {
u64 buflen[2];
};
+enum async_op {
+ OP_NOP = 0,
+ OP_UPDATE,
+ OP_FINAL,
+ OP_FINUP,
+};
+
/* phmac request context */
struct phmac_req_ctx {
struct hash_walk_helper hwh;
struct kmac_sha2_ctx kmac_ctx;
- bool final;
+ enum async_op async_op;
};
/*
@@ -610,6 +616,7 @@ static int phmac_update(struct ahash_request *req)
* using engine to serialize requests.
*/
if (rc == 0 || rc == -EKEYEXPIRED) {
+ req_ctx->async_op = OP_UPDATE;
atomic_inc(&tfm_ctx->via_engine_ctr);
rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req);
if (rc != -EINPROGRESS)
@@ -647,8 +654,7 @@ static int phmac_final(struct ahash_request *req)
* using engine to serialize requests.
*/
if (rc == 0 || rc == -EKEYEXPIRED) {
- req->nbytes = 0;
- req_ctx->final = true;
+ req_ctx->async_op = OP_FINAL;
atomic_inc(&tfm_ctx->via_engine_ctr);
rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req);
if (rc != -EINPROGRESS)
@@ -676,13 +682,16 @@ static int phmac_finup(struct ahash_request *req)
if (rc)
goto out;
+ req_ctx->async_op = OP_FINUP;
+
/* Try synchronous operations if no active engine usage */
if (!atomic_read(&tfm_ctx->via_engine_ctr)) {
rc = phmac_kmac_update(req, false);
if (rc == 0)
- req->nbytes = 0;
+ req_ctx->async_op = OP_FINAL;
}
- if (!rc && !req->nbytes && !atomic_read(&tfm_ctx->via_engine_ctr)) {
+ if (!rc && req_ctx->async_op == OP_FINAL &&
+ !atomic_read(&tfm_ctx->via_engine_ctr)) {
rc = phmac_kmac_final(req, false);
if (rc == 0)
goto out;
@@ -694,7 +703,7 @@ static int phmac_finup(struct ahash_request *req)
* using engine to serialize requests.
*/
if (rc == 0 || rc == -EKEYEXPIRED) {
- req_ctx->final = true;
+ /* req->async_op has been set to either OP_FINUP or OP_FINAL */
atomic_inc(&tfm_ctx->via_engine_ctr);
rc = crypto_transfer_hash_request_to_engine(phmac_crypto_engine, req);
if (rc != -EINPROGRESS)
@@ -855,15 +864,16 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)
/*
* Three kinds of requests come in here:
- * update when req->nbytes > 0 and req_ctx->final is false
- * final when req->nbytes = 0 and req_ctx->final is true
- * finup when req->nbytes > 0 and req_ctx->final is true
- * For update and finup the hwh walk needs to be prepared and
- * up to date but the actual nr of bytes in req->nbytes may be
- * any non zero number. For final there is no hwh walk needed.
+ * 1. req->async_op == OP_UPDATE with req->nbytes > 0
+ * 2. req->async_op == OP_FINUP with req->nbytes > 0
+ * 3. req->async_op == OP_FINAL
+ * For update and finup the hwh walk has already been prepared
+ * by the caller. For final there is no hwh walk needed.
*/
- if (req->nbytes) {
+ switch (req_ctx->async_op) {
+ case OP_UPDATE:
+ case OP_FINUP:
rc = phmac_kmac_update(req, true);
if (rc == -EKEYEXPIRED) {
/*
@@ -880,10 +890,11 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)
hwh_advance(hwh, rc);
goto out;
}
- req->nbytes = 0;
- }
-
- if (req_ctx->final) {
+ if (req_ctx->async_op == OP_UPDATE)
+ break;
+ req_ctx->async_op = OP_FINAL;
+ fallthrough;
+ case OP_FINAL:
rc = phmac_kmac_final(req, true);
if (rc == -EKEYEXPIRED) {
/*
@@ -897,10 +908,14 @@ static int phmac_do_one_request(struct crypto_engine *engine, void *areq)
cond_resched();
return -ENOSPC;
}
+ break;
+ default:
+ /* unknown/unsupported/unimplemented asynch op */
+ return -EOPNOTSUPP;
}
out:
- if (rc || req_ctx->final)
+ if (rc || req_ctx->async_op == OP_FINAL)
memzero_explicit(kmac_ctx, sizeof(*kmac_ctx));
pr_debug("request complete with rc=%d\n", rc);
local_bh_disable();
diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c
index 2becd77df741..84e3d3c6ba09 100644
--- a/arch/s390/crypto/prng.c
+++ b/arch/s390/crypto/prng.c
@@ -6,8 +6,7 @@
* Driver for the s390 pseudo random number generator
*/
-#define KMSG_COMPONENT "prng"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "prng: " fmt
#include <linux/fs.h>
#include <linux/fips.h>
diff --git a/arch/s390/crypto/sha.h b/arch/s390/crypto/sha.h
deleted file mode 100644
index b9cd9572dd35..000000000000
--- a/arch/s390/crypto/sha.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
-/*
- * Cryptographic API.
- *
- * s390 generic implementation of the SHA Secure Hash Algorithms.
- *
- * Copyright IBM Corp. 2007
- * Author(s): Jan Glauber (jang@de.ibm.com)
- */
-#ifndef _CRYPTO_ARCH_S390_SHA_H
-#define _CRYPTO_ARCH_S390_SHA_H
-
-#include <crypto/hash.h>
-#include <crypto/sha2.h>
-#include <crypto/sha3.h>
-#include <linux/build_bug.h>
-#include <linux/types.h>
-
-/* must be big enough for the largest SHA variant */
-#define CPACF_MAX_PARMBLOCK_SIZE SHA3_STATE_SIZE
-#define SHA_MAX_BLOCK_SIZE SHA3_224_BLOCK_SIZE
-
-struct s390_sha_ctx {
- u64 count; /* message length in bytes */
- union {
- u32 state[CPACF_MAX_PARMBLOCK_SIZE / sizeof(u32)];
- struct {
- u64 state[SHA512_DIGEST_SIZE / sizeof(u64)];
- u64 count_hi;
- } sha512;
- struct {
- __le64 state[SHA3_STATE_SIZE / sizeof(u64)];
- } sha3;
- };
- int func; /* KIMD function to use */
- bool first_message_part;
-};
-
-struct shash_desc;
-
-int s390_sha_update_blocks(struct shash_desc *desc, const u8 *data,
- unsigned int len);
-int s390_sha_finup(struct shash_desc *desc, const u8 *src, unsigned int len,
- u8 *out);
-
-static inline void __check_s390_sha_ctx_size(void)
-{
- BUILD_BUG_ON(S390_SHA_CTX_SIZE != sizeof(struct s390_sha_ctx));
-}
-
-#endif
diff --git a/arch/s390/crypto/sha3_256_s390.c b/arch/s390/crypto/sha3_256_s390.c
deleted file mode 100644
index 03bb4f4bab70..000000000000
--- a/arch/s390/crypto/sha3_256_s390.c
+++ /dev/null
@@ -1,157 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Cryptographic API.
- *
- * s390 implementation of the SHA256 and SHA224 Secure Hash Algorithm.
- *
- * s390 Version:
- * Copyright IBM Corp. 2019
- * Author(s): Joerg Schmidbauer (jschmidb@de.ibm.com)
- */
-#include <asm/cpacf.h>
-#include <crypto/internal/hash.h>
-#include <crypto/sha3.h>
-#include <linux/cpufeature.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-
-#include "sha.h"
-
-static int sha3_256_init(struct shash_desc *desc)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
-
- sctx->first_message_part = test_facility(86);
- if (!sctx->first_message_part)
- memset(sctx->state, 0, sizeof(sctx->state));
- sctx->count = 0;
- sctx->func = CPACF_KIMD_SHA3_256;
-
- return 0;
-}
-
-static int sha3_256_export(struct shash_desc *desc, void *out)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
- union {
- u8 *u8;
- u64 *u64;
- } p = { .u8 = out };
- int i;
-
- if (sctx->first_message_part) {
- memset(out, 0, SHA3_STATE_SIZE);
- return 0;
- }
- for (i = 0; i < SHA3_STATE_SIZE / 8; i++)
- put_unaligned(le64_to_cpu(sctx->sha3.state[i]), p.u64++);
- return 0;
-}
-
-static int sha3_256_import(struct shash_desc *desc, const void *in)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
- union {
- const u8 *u8;
- const u64 *u64;
- } p = { .u8 = in };
- int i;
-
- for (i = 0; i < SHA3_STATE_SIZE / 8; i++)
- sctx->sha3.state[i] = cpu_to_le64(get_unaligned(p.u64++));
- sctx->count = 0;
- sctx->first_message_part = 0;
- sctx->func = CPACF_KIMD_SHA3_256;
-
- return 0;
-}
-
-static int sha3_224_import(struct shash_desc *desc, const void *in)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
-
- sha3_256_import(desc, in);
- sctx->func = CPACF_KIMD_SHA3_224;
- return 0;
-}
-
-static struct shash_alg sha3_256_alg = {
- .digestsize = SHA3_256_DIGEST_SIZE, /* = 32 */
- .init = sha3_256_init,
- .update = s390_sha_update_blocks,
- .finup = s390_sha_finup,
- .export = sha3_256_export,
- .import = sha3_256_import,
- .descsize = S390_SHA_CTX_SIZE,
- .statesize = SHA3_STATE_SIZE,
- .base = {
- .cra_name = "sha3-256",
- .cra_driver_name = "sha3-256-s390",
- .cra_priority = 300,
- .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .cra_blocksize = SHA3_256_BLOCK_SIZE,
- .cra_module = THIS_MODULE,
- }
-};
-
-static int sha3_224_init(struct shash_desc *desc)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
-
- sha3_256_init(desc);
- sctx->func = CPACF_KIMD_SHA3_224;
- return 0;
-}
-
-static struct shash_alg sha3_224_alg = {
- .digestsize = SHA3_224_DIGEST_SIZE,
- .init = sha3_224_init,
- .update = s390_sha_update_blocks,
- .finup = s390_sha_finup,
- .export = sha3_256_export, /* same as for 256 */
- .import = sha3_224_import, /* function code different! */
- .descsize = S390_SHA_CTX_SIZE,
- .statesize = SHA3_STATE_SIZE,
- .base = {
- .cra_name = "sha3-224",
- .cra_driver_name = "sha3-224-s390",
- .cra_priority = 300,
- .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .cra_blocksize = SHA3_224_BLOCK_SIZE,
- .cra_module = THIS_MODULE,
- }
-};
-
-static int __init sha3_256_s390_init(void)
-{
- int ret;
-
- if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA3_256))
- return -ENODEV;
-
- ret = crypto_register_shash(&sha3_256_alg);
- if (ret < 0)
- goto out;
-
- ret = crypto_register_shash(&sha3_224_alg);
- if (ret < 0)
- crypto_unregister_shash(&sha3_256_alg);
-out:
- return ret;
-}
-
-static void __exit sha3_256_s390_fini(void)
-{
- crypto_unregister_shash(&sha3_224_alg);
- crypto_unregister_shash(&sha3_256_alg);
-}
-
-module_cpu_feature_match(S390_CPU_FEATURE_MSA, sha3_256_s390_init);
-module_exit(sha3_256_s390_fini);
-
-MODULE_ALIAS_CRYPTO("sha3-256");
-MODULE_ALIAS_CRYPTO("sha3-224");
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SHA3-256 and SHA3-224 Secure Hash Algorithm");
diff --git a/arch/s390/crypto/sha3_512_s390.c b/arch/s390/crypto/sha3_512_s390.c
deleted file mode 100644
index a5c9690eecb1..000000000000
--- a/arch/s390/crypto/sha3_512_s390.c
+++ /dev/null
@@ -1,157 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Cryptographic API.
- *
- * s390 implementation of the SHA512 and SHA384 Secure Hash Algorithm.
- *
- * Copyright IBM Corp. 2019
- * Author(s): Joerg Schmidbauer (jschmidb@de.ibm.com)
- */
-#include <asm/cpacf.h>
-#include <crypto/internal/hash.h>
-#include <crypto/sha3.h>
-#include <linux/cpufeature.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-
-#include "sha.h"
-
-static int sha3_512_init(struct shash_desc *desc)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
-
- sctx->first_message_part = test_facility(86);
- if (!sctx->first_message_part)
- memset(sctx->state, 0, sizeof(sctx->state));
- sctx->count = 0;
- sctx->func = CPACF_KIMD_SHA3_512;
-
- return 0;
-}
-
-static int sha3_512_export(struct shash_desc *desc, void *out)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
- union {
- u8 *u8;
- u64 *u64;
- } p = { .u8 = out };
- int i;
-
- if (sctx->first_message_part) {
- memset(out, 0, SHA3_STATE_SIZE);
- return 0;
- }
- for (i = 0; i < SHA3_STATE_SIZE / 8; i++)
- put_unaligned(le64_to_cpu(sctx->sha3.state[i]), p.u64++);
- return 0;
-}
-
-static int sha3_512_import(struct shash_desc *desc, const void *in)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
- union {
- const u8 *u8;
- const u64 *u64;
- } p = { .u8 = in };
- int i;
-
- for (i = 0; i < SHA3_STATE_SIZE / 8; i++)
- sctx->sha3.state[i] = cpu_to_le64(get_unaligned(p.u64++));
- sctx->count = 0;
- sctx->first_message_part = 0;
- sctx->func = CPACF_KIMD_SHA3_512;
-
- return 0;
-}
-
-static int sha3_384_import(struct shash_desc *desc, const void *in)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
-
- sha3_512_import(desc, in);
- sctx->func = CPACF_KIMD_SHA3_384;
- return 0;
-}
-
-static struct shash_alg sha3_512_alg = {
- .digestsize = SHA3_512_DIGEST_SIZE,
- .init = sha3_512_init,
- .update = s390_sha_update_blocks,
- .finup = s390_sha_finup,
- .export = sha3_512_export,
- .import = sha3_512_import,
- .descsize = S390_SHA_CTX_SIZE,
- .statesize = SHA3_STATE_SIZE,
- .base = {
- .cra_name = "sha3-512",
- .cra_driver_name = "sha3-512-s390",
- .cra_priority = 300,
- .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .cra_blocksize = SHA3_512_BLOCK_SIZE,
- .cra_module = THIS_MODULE,
- }
-};
-
-MODULE_ALIAS_CRYPTO("sha3-512");
-
-static int sha3_384_init(struct shash_desc *desc)
-{
- struct s390_sha_ctx *sctx = shash_desc_ctx(desc);
-
- sha3_512_init(desc);
- sctx->func = CPACF_KIMD_SHA3_384;
- return 0;
-}
-
-static struct shash_alg sha3_384_alg = {
- .digestsize = SHA3_384_DIGEST_SIZE,
- .init = sha3_384_init,
- .update = s390_sha_update_blocks,
- .finup = s390_sha_finup,
- .export = sha3_512_export, /* same as for 512 */
- .import = sha3_384_import, /* function code different! */
- .descsize = S390_SHA_CTX_SIZE,
- .statesize = SHA3_STATE_SIZE,
- .base = {
- .cra_name = "sha3-384",
- .cra_driver_name = "sha3-384-s390",
- .cra_priority = 300,
- .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .cra_blocksize = SHA3_384_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct s390_sha_ctx),
- .cra_module = THIS_MODULE,
- }
-};
-
-MODULE_ALIAS_CRYPTO("sha3-384");
-
-static int __init init(void)
-{
- int ret;
-
- if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA3_512))
- return -ENODEV;
- ret = crypto_register_shash(&sha3_512_alg);
- if (ret < 0)
- goto out;
- ret = crypto_register_shash(&sha3_384_alg);
- if (ret < 0)
- crypto_unregister_shash(&sha3_512_alg);
-out:
- return ret;
-}
-
-static void __exit fini(void)
-{
- crypto_unregister_shash(&sha3_512_alg);
- crypto_unregister_shash(&sha3_384_alg);
-}
-
-module_cpu_feature_match(S390_CPU_FEATURE_MSA, init);
-module_exit(fini);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SHA3-512 and SHA3-384 Secure Hash Algorithm");
diff --git a/arch/s390/crypto/sha_common.c b/arch/s390/crypto/sha_common.c
deleted file mode 100644
index d6f839618794..000000000000
--- a/arch/s390/crypto/sha_common.c
+++ /dev/null
@@ -1,117 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Cryptographic API.
- *
- * s390 generic implementation of the SHA Secure Hash Algorithms.
- *
- * Copyright IBM Corp. 2007
- * Author(s): Jan Glauber (jang@de.ibm.com)
- */
-
-#include <crypto/internal/hash.h>
-#include <linux/export.h>
-#include <linux/module.h>
-#include <asm/cpacf.h>
-#include "sha.h"
-
-int s390_sha_update_blocks(struct shash_desc *desc, const u8 *data,
- unsigned int len)
-{
- unsigned int bsize = crypto_shash_blocksize(desc->tfm);
- struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
- unsigned int n;
- int fc;
-
- fc = ctx->func;
- if (ctx->first_message_part)
- fc |= CPACF_KIMD_NIP;
-
- /* process as many blocks as possible */
- n = (len / bsize) * bsize;
- ctx->count += n;
- switch (ctx->func) {
- case CPACF_KLMD_SHA_512:
- case CPACF_KLMD_SHA3_384:
- if (ctx->count < n)
- ctx->sha512.count_hi++;
- break;
- }
- cpacf_kimd(fc, ctx->state, data, n);
- ctx->first_message_part = 0;
- return len - n;
-}
-EXPORT_SYMBOL_GPL(s390_sha_update_blocks);
-
-static int s390_crypto_shash_parmsize(int func)
-{
- switch (func) {
- case CPACF_KLMD_SHA_1:
- return 20;
- case CPACF_KLMD_SHA_256:
- return 32;
- case CPACF_KLMD_SHA_512:
- return 64;
- case CPACF_KLMD_SHA3_224:
- case CPACF_KLMD_SHA3_256:
- case CPACF_KLMD_SHA3_384:
- case CPACF_KLMD_SHA3_512:
- return 200;
- default:
- return -EINVAL;
- }
-}
-
-int s390_sha_finup(struct shash_desc *desc, const u8 *src, unsigned int len,
- u8 *out)
-{
- struct s390_sha_ctx *ctx = shash_desc_ctx(desc);
- int mbl_offset, fc;
- u64 bits;
-
- ctx->count += len;
-
- bits = ctx->count * 8;
- mbl_offset = s390_crypto_shash_parmsize(ctx->func);
- if (mbl_offset < 0)
- return -EINVAL;
-
- mbl_offset = mbl_offset / sizeof(u32);
-
- /* set total msg bit length (mbl) in CPACF parmblock */
- switch (ctx->func) {
- case CPACF_KLMD_SHA_512:
- /* The SHA512 parmblock has a 128-bit mbl field. */
- if (ctx->count < len)
- ctx->sha512.count_hi++;
- ctx->sha512.count_hi <<= 3;
- ctx->sha512.count_hi |= ctx->count >> 61;
- mbl_offset += sizeof(u64) / sizeof(u32);
- fallthrough;
- case CPACF_KLMD_SHA_1:
- case CPACF_KLMD_SHA_256:
- memcpy(ctx->state + mbl_offset, &bits, sizeof(bits));
- break;
- case CPACF_KLMD_SHA3_224:
- case CPACF_KLMD_SHA3_256:
- case CPACF_KLMD_SHA3_384:
- case CPACF_KLMD_SHA3_512:
- break;
- default:
- return -EINVAL;
- }
-
- fc = ctx->func;
- fc |= test_facility(86) ? CPACF_KLMD_DUFOP : 0;
- if (ctx->first_message_part)
- fc |= CPACF_KLMD_NIP;
- cpacf_klmd(fc, ctx->state, src, len);
-
- /* copy digest to out */
- memcpy(out, ctx->state, crypto_shash_digestsize(desc->tfm));
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(s390_sha_finup);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("s390 SHA cipher common functions");
diff --git a/arch/s390/hypfs/hypfs_diag.c b/arch/s390/hypfs/hypfs_diag.c
index c8af67d20994..08777f57bd24 100644
--- a/arch/s390/hypfs/hypfs_diag.c
+++ b/arch/s390/hypfs/hypfs_diag.c
@@ -7,8 +7,7 @@
* Author(s): Michael Holzheu <holzheu@de.ibm.com>
*/
-#define KMSG_COMPONENT "hypfs"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "hypfs: " fmt
#include <linux/types.h>
#include <linux/errno.h>
diff --git a/arch/s390/hypfs/hypfs_diag_fs.c b/arch/s390/hypfs/hypfs_diag_fs.c
index ede951dc0085..013da4ff9802 100644
--- a/arch/s390/hypfs/hypfs_diag_fs.c
+++ b/arch/s390/hypfs/hypfs_diag_fs.c
@@ -7,8 +7,7 @@
* Author(s): Michael Holzheu <holzheu@de.ibm.com>
*/
-#define KMSG_COMPONENT "hypfs"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "hypfs: " fmt
#include <linux/types.h>
#include <linux/errno.h>
diff --git a/arch/s390/hypfs/hypfs_sprp.c b/arch/s390/hypfs/hypfs_sprp.c
index a2952ed5518b..a72576221cab 100644
--- a/arch/s390/hypfs/hypfs_sprp.c
+++ b/arch/s390/hypfs/hypfs_sprp.c
@@ -7,7 +7,6 @@
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
-#include <linux/compat.h>
#include <linux/errno.h>
#include <linux/gfp.h>
#include <linux/string.h>
@@ -116,10 +115,7 @@ static long hypfs_sprp_ioctl(struct file *file, unsigned int cmd,
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- if (is_compat_task())
- argp = compat_ptr(arg);
- else
- argp = (void __user *) arg;
+ argp = (void __user *)arg;
switch (cmd) {
case HYPFS_DIAG304:
return __hypfs_sprp_ioctl(argp);
diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c
index 96409573c75d..ee5cfa8f71a0 100644
--- a/arch/s390/hypfs/inode.c
+++ b/arch/s390/hypfs/inode.c
@@ -6,8 +6,7 @@
* Author(s): Michael Holzheu <holzheu@de.ibm.com>
*/
-#define KMSG_COMPONENT "hypfs"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "hypfs: " fmt
#include <linux/types.h>
#include <linux/errno.h>
diff --git a/arch/s390/include/asm/ap.h b/arch/s390/include/asm/ap.h
index 56817990c73d..b24459f692fa 100644
--- a/arch/s390/include/asm/ap.h
+++ b/arch/s390/include/asm/ap.h
@@ -38,16 +38,30 @@ typedef unsigned int ap_qid_t;
* The ap queue status word is returned by all three AP functions
* (PQAP, NQAP and DQAP). There's a set of flags in the first
* byte, followed by a 1 byte response code.
+ *
+ * For convenience the 'value' field is a 32 bit access of the
+ * whole status and the 'status_bits' and 'rc' fields comprise
+ * the leftmost 8 status bits and the response_code.
*/
struct ap_queue_status {
- unsigned int queue_empty : 1;
- unsigned int replies_waiting : 1;
- unsigned int queue_full : 1;
- unsigned int : 3;
- unsigned int async : 1;
- unsigned int irq_enabled : 1;
- unsigned int response_code : 8;
- unsigned int : 16;
+ union {
+ unsigned int value : 32;
+ struct {
+ unsigned int status_bits : 8;
+ unsigned int rc : 8;
+ unsigned int : 16;
+ };
+ struct {
+ unsigned int queue_empty : 1;
+ unsigned int replies_waiting : 1;
+ unsigned int queue_full : 1;
+ unsigned int : 3;
+ unsigned int async : 1;
+ unsigned int irq_enabled : 1;
+ unsigned int response_code : 8;
+ unsigned int : 16;
+ };
+ };
};
/*
diff --git a/arch/s390/include/asm/arch-stackprotector.h b/arch/s390/include/asm/arch-stackprotector.h
new file mode 100644
index 000000000000..953627259e91
--- /dev/null
+++ b/arch/s390/include/asm/arch-stackprotector.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _ASM_S390_ARCH_STACKPROTECTOR_H
+#define _ASM_S390_ARCH_STACKPROTECTOR_H
+
+extern unsigned long __stack_chk_guard;
+extern int stack_protector_debug;
+
+void __stack_protector_apply_early(unsigned long kernel_start);
+int __stack_protector_apply(unsigned long *start, unsigned long *end, unsigned long kernel_start);
+
+static inline void stack_protector_apply_early(unsigned long kernel_start)
+{
+ if (IS_ENABLED(CONFIG_STACKPROTECTOR))
+ __stack_protector_apply_early(kernel_start);
+}
+
+static inline int stack_protector_apply(unsigned long *start, unsigned long *end)
+{
+ if (IS_ENABLED(CONFIG_STACKPROTECTOR))
+ return __stack_protector_apply(start, end, 0);
+ return 0;
+}
+
+#endif /* _ASM_S390_ARCH_STACKPROTECTOR_H */
diff --git a/arch/s390/include/asm/bug.h b/arch/s390/include/asm/bug.h
index c500d45fb465..acb4b13d98c5 100644
--- a/arch/s390/include/asm/bug.h
+++ b/arch/s390/include/asm/bug.h
@@ -2,69 +2,55 @@
#ifndef _ASM_S390_BUG_H
#define _ASM_S390_BUG_H
-#include <linux/compiler.h>
-
-#ifdef CONFIG_BUG
-
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-
-#define __EMIT_BUG(x) do { \
- asm_inline volatile( \
- "0: mc 0,0\n" \
- ".section .rodata.str,\"aMS\",@progbits,1\n" \
- "1: .asciz \""__FILE__"\"\n" \
- ".previous\n" \
- ".section __bug_table,\"aw\"\n" \
- "2: .long 0b-.\n" \
- " .long 1b-.\n" \
- " .short %0,%1\n" \
- " .org 2b+%2\n" \
- ".previous\n" \
- : : "i" (__LINE__), \
- "i" (x), \
- "i" (sizeof(struct bug_entry))); \
-} while (0)
-
-#else /* CONFIG_DEBUG_BUGVERBOSE */
-
-#define __EMIT_BUG(x) do { \
- asm_inline volatile( \
- "0: mc 0,0\n" \
- ".section __bug_table,\"aw\"\n" \
- "1: .long 0b-.\n" \
- " .short %0\n" \
- " .org 1b+%1\n" \
- ".previous\n" \
- : : "i" (x), \
- "i" (sizeof(struct bug_entry))); \
+#include <linux/stringify.h>
+
+#ifndef CONFIG_DEBUG_BUGVERBOSE
+#define _BUGVERBOSE_LOCATION(file, line)
+#else
+#define __BUGVERBOSE_LOCATION(file, line) \
+ .pushsection .rodata.str, "aMS", @progbits, 1; \
+ 10002: .ascii file "\0"; \
+ .popsection; \
+ \
+ .long 10002b - .; \
+ .short line;
+#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
+#endif
+
+#ifndef CONFIG_GENERIC_BUG
+#define __BUG_ENTRY(cond_str, flags)
+#else
+#define __BUG_ENTRY(cond_str, flags) \
+ .pushsection __bug_table, "aw"; \
+ .align 4; \
+ 10000: .long 10001f - .; \
+ _BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
+ .short flags; \
+ .popsection; \
+ 10001:
+#endif
+
+#define ASM_BUG_FLAGS(cond_str, flags) \
+ __BUG_ENTRY(cond_str, flags) \
+ mc 0,0
+
+#define ASM_BUG() ASM_BUG_FLAGS("", 0)
+
+#define __BUG_FLAGS(cond_str, flags) \
+ asm_inline volatile(__stringify(ASM_BUG_FLAGS(cond_str, flags)));
+
+#define __WARN_FLAGS(cond_str, flags) \
+do { \
+ __BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags)); \
} while (0)
-#endif /* CONFIG_DEBUG_BUGVERBOSE */
-
-#define BUG() do { \
- __EMIT_BUG(0); \
- unreachable(); \
+#define BUG() \
+do { \
+ __BUG_FLAGS("", 0); \
+ unreachable(); \
} while (0)
-#define __WARN_FLAGS(flags) do { \
- __EMIT_BUG(BUGFLAG_WARNING|(flags)); \
-} while (0)
-
-#define WARN_ON(x) ({ \
- int __ret_warn_on = !!(x); \
- if (__builtin_constant_p(__ret_warn_on)) { \
- if (__ret_warn_on) \
- __WARN(); \
- } else { \
- if (unlikely(__ret_warn_on)) \
- __WARN(); \
- } \
- unlikely(__ret_warn_on); \
-})
-
#define HAVE_ARCH_BUG
-#define HAVE_ARCH_WARN_ON
-#endif /* CONFIG_BUG */
#include <asm-generic/bug.h>
diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h
index b6b619f340a5..0a82ae2300b6 100644
--- a/arch/s390/include/asm/cio.h
+++ b/arch/s390/include/asm/cio.h
@@ -18,6 +18,8 @@
#include <asm/scsw.h>
+#define CCW_MAX_BYTE_COUNT 65535
+
/**
* struct ccw1 - channel command word
* @cmd_code: command code
diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h
deleted file mode 100644
index 3cb9d813f022..000000000000
--- a/arch/s390/include/asm/compat.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_S390X_COMPAT_H
-#define _ASM_S390X_COMPAT_H
-/*
- * Architecture specific compatibility types
- */
-#include <linux/types.h>
-#include <linux/sched.h>
-#include <linux/sched/task_stack.h>
-#include <linux/thread_info.h>
-#include <asm/ptrace.h>
-
-#define compat_mode_t compat_mode_t
-typedef u16 compat_mode_t;
-
-#define __compat_uid_t __compat_uid_t
-typedef u16 __compat_uid_t;
-typedef u16 __compat_gid_t;
-
-#define compat_dev_t compat_dev_t
-typedef u16 compat_dev_t;
-
-#define compat_ipc_pid_t compat_ipc_pid_t
-typedef u16 compat_ipc_pid_t;
-
-#define compat_statfs compat_statfs
-
-#include <asm-generic/compat.h>
-
-#define __TYPE_IS_PTR(t) (!__builtin_types_compatible_p( \
- typeof(0?(__force t)0:0ULL), u64))
-
-#define __SC_DELOUSE(t,v) ({ \
- BUILD_BUG_ON(sizeof(t) > 4 && !__TYPE_IS_PTR(t)); \
- (__force t)(__TYPE_IS_PTR(t) ? ((v) & 0x7fffffff) : (v)); \
-})
-
-#define PSW32_MASK_USER 0x0000FF00UL
-
-#define PSW32_USER_BITS (PSW32_MASK_DAT | PSW32_MASK_IO | PSW32_MASK_EXT | \
- PSW32_DEFAULT_KEY | PSW32_MASK_BASE | \
- PSW32_MASK_MCHECK | PSW32_MASK_PSTATE | \
- PSW32_ASC_PRIMARY)
-
-#define COMPAT_UTS_MACHINE "s390\0\0\0\0"
-
-typedef u16 compat_nlink_t;
-
-typedef struct {
- u32 mask;
- u32 addr;
-} __aligned(8) psw_compat_t;
-
-typedef struct {
- psw_compat_t psw;
- u32 gprs[NUM_GPRS];
- u32 acrs[NUM_ACRS];
- u32 orig_gpr2;
-} s390_compat_regs;
-
-typedef struct {
- u32 gprs_high[NUM_GPRS];
-} s390_compat_regs_high;
-
-struct compat_stat {
- compat_dev_t st_dev;
- u16 __pad1;
- compat_ino_t st_ino;
- compat_mode_t st_mode;
- compat_nlink_t st_nlink;
- __compat_uid_t st_uid;
- __compat_gid_t st_gid;
- compat_dev_t st_rdev;
- u16 __pad2;
- u32 st_size;
- u32 st_blksize;
- u32 st_blocks;
- u32 st_atime;
- u32 st_atime_nsec;
- u32 st_mtime;
- u32 st_mtime_nsec;
- u32 st_ctime;
- u32 st_ctime_nsec;
- u32 __unused4;
- u32 __unused5;
-};
-
-struct compat_statfs {
- u32 f_type;
- u32 f_bsize;
- u32 f_blocks;
- u32 f_bfree;
- u32 f_bavail;
- u32 f_files;
- u32 f_ffree;
- compat_fsid_t f_fsid;
- u32 f_namelen;
- u32 f_frsize;
- u32 f_flags;
- u32 f_spare[4];
-};
-
-struct compat_statfs64 {
- u32 f_type;
- u32 f_bsize;
- u64 f_blocks;
- u64 f_bfree;
- u64 f_bavail;
- u64 f_files;
- u64 f_ffree;
- compat_fsid_t f_fsid;
- u32 f_namelen;
- u32 f_frsize;
- u32 f_flags;
- u32 f_spare[5];
-};
-
-/*
- * A pointer passed in from user mode. This should not
- * be used for syscall parameters, just declare them
- * as pointers because the syscall entry code will have
- * appropriately converted them already.
- */
-
-static inline void __user *compat_ptr(compat_uptr_t uptr)
-{
- return (void __user *)(unsigned long)(uptr & 0x7fffffffUL);
-}
-#define compat_ptr(uptr) compat_ptr(uptr)
-
-#ifdef CONFIG_COMPAT
-
-static inline int is_compat_task(void)
-{
- return test_thread_flag(TIF_31BIT);
-}
-
-#endif
-
-#endif /* _ASM_S390X_COMPAT_H */
diff --git a/arch/s390/include/asm/cpufeature.h b/arch/s390/include/asm/cpufeature.h
index 6c6a99660e78..d6fb999c8c6d 100644
--- a/arch/s390/include/asm/cpufeature.h
+++ b/arch/s390/include/asm/cpufeature.h
@@ -27,7 +27,6 @@ int cpu_have_feature(unsigned int nr);
#define cpu_has_edat1() test_facility(8)
#define cpu_has_edat2() test_facility(78)
#define cpu_has_gs() test_facility(133)
-#define cpu_has_idte() test_facility(3)
#define cpu_has_nx() test_facility(130)
#define cpu_has_rdp() test_facility(194)
#define cpu_has_seq_insn() test_facility(85)
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h
index a03df312081e..bb63fa4d20bb 100644
--- a/arch/s390/include/asm/elf.h
+++ b/arch/s390/include/asm/elf.h
@@ -162,8 +162,6 @@ enum {
* ELF register definitions..
*/
-#include <linux/compat.h>
-
#include <asm/ptrace.h>
#include <asm/syscall.h>
#include <asm/user.h>
@@ -171,9 +169,6 @@ enum {
typedef s390_fp_regs elf_fpregset_t;
typedef s390_regs elf_gregset_t;
-typedef s390_fp_regs compat_elf_fpregset_t;
-typedef s390_compat_regs compat_elf_gregset_t;
-
#include <linux/sched/mm.h> /* for task_struct */
#include <asm/mmu_context.h>
@@ -183,10 +178,6 @@ typedef s390_compat_regs compat_elf_gregset_t;
#define elf_check_arch(x) \
(((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
&& (x)->e_ident[EI_CLASS] == ELF_CLASS)
-#define compat_elf_check_arch(x) \
- (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
- && (x)->e_ident[EI_CLASS] == ELF_CLASS)
-#define compat_start_thread start_thread31
/* For SVR4/S390 the function pointer to be registered with `atexit` is
passed in R14. */
@@ -203,9 +194,7 @@ typedef s390_compat_regs compat_elf_gregset_t;
the loader. We need to make sure that it is out of the way of the program
that it will "exec", and that there is sufficient room for the brk. 64-bit
tasks are aligned to 4GB. */
-#define ELF_ET_DYN_BASE (is_compat_task() ? \
- (STACK_TOP / 3 * 2) : \
- (STACK_TOP / 3 * 2) & ~((1UL << 32) - 1))
+#define ELF_ET_DYN_BASE ((STACK_TOP / 3 * 2) & ~((1UL << 32) - 1))
/* This yields a mask that user programs can use to figure out what
instruction set this CPU supports. */
@@ -224,43 +213,21 @@ extern unsigned long elf_hwcap;
extern char elf_platform[];
#define ELF_PLATFORM (elf_platform)
-#ifndef CONFIG_COMPAT
#define SET_PERSONALITY(ex) \
do { \
set_personality(PER_LINUX | \
(current->personality & (~PER_MASK))); \
- current->thread.sys_call_table = sys_call_table; \
-} while (0)
-#else /* CONFIG_COMPAT */
-#define SET_PERSONALITY(ex) \
-do { \
- if (personality(current->personality) != PER_LINUX32) \
- set_personality(PER_LINUX | \
- (current->personality & ~PER_MASK)); \
- if ((ex).e_ident[EI_CLASS] == ELFCLASS32) { \
- set_thread_flag(TIF_31BIT); \
- current->thread.sys_call_table = \
- sys_call_table_emu; \
- } else { \
- clear_thread_flag(TIF_31BIT); \
- current->thread.sys_call_table = \
- sys_call_table; \
- } \
} while (0)
-#endif /* CONFIG_COMPAT */
/*
* Cache aliasing on the latest machines calls for a mapping granularity
- * of 512KB for the anonymous mapping base. For 64-bit processes use a
- * 512KB alignment and a randomization of up to 1GB. For 31-bit processes
- * the virtual address space is limited, use no alignment and limit the
- * randomization to 8MB.
- * For the additional randomization of the program break use 32MB for
- * 64-bit and 8MB for 31-bit.
+ * of 512KB for the anonymous mapping base. Use a 512KB alignment and a
+ * randomization of up to 1GB.
+ * For the additional randomization of the program break use 32MB.
*/
-#define BRK_RND_MASK (is_compat_task() ? 0x7ffUL : 0x1fffUL)
-#define MMAP_RND_MASK (is_compat_task() ? 0x7ffUL : 0x3ff80UL)
-#define MMAP_ALIGN_MASK (is_compat_task() ? 0 : 0x7fUL)
+#define BRK_RND_MASK (0x1fffUL)
+#define MMAP_RND_MASK (0x3ff80UL)
+#define MMAP_ALIGN_MASK (0x7fUL)
#define STACK_RND_MASK MMAP_RND_MASK
/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
diff --git a/arch/s390/include/asm/fpu-insn.h b/arch/s390/include/asm/fpu-insn.h
index e99f8bca8e08..96727f3bd0dc 100644
--- a/arch/s390/include/asm/fpu-insn.h
+++ b/arch/s390/include/asm/fpu-insn.h
@@ -12,6 +12,7 @@
#ifndef __ASSEMBLER__
#include <linux/instrumented.h>
+#include <linux/kmsan.h>
#include <asm/asm-extable.h>
asm(".include \"asm/fpu-insn-asm.h\"\n");
@@ -393,6 +394,7 @@ static __always_inline void fpu_vstl(u8 v1, u32 index, const void *vxr)
: [vxr] "=Q" (*(u8 *)vxr)
: [index] "d" (index), [v1] "I" (v1)
: "memory");
+ kmsan_unpoison_memory(vxr, size);
}
#else /* CONFIG_CC_HAS_ASM_AOR_FORMAT_FLAGS */
@@ -409,6 +411,7 @@ static __always_inline void fpu_vstl(u8 v1, u32 index, const void *vxr)
: [vxr] "=R" (*(u8 *)vxr)
: [index] "d" (index), [v1] "I" (v1)
: "memory", "1");
+ kmsan_unpoison_memory(vxr, size);
}
#endif /* CONFIG_CC_HAS_ASM_AOR_FORMAT_FLAGS */
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h
index bee2d16c2951..692c484ec163 100644
--- a/arch/s390/include/asm/ftrace.h
+++ b/arch/s390/include/asm/ftrace.h
@@ -105,28 +105,11 @@ static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs, unsi
}
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
-/*
- * Even though the system call numbers are identical for s390/s390x a
- * different system call table is used for compat tasks. This may lead
- * to e.g. incorrect or missing trace event sysfs files.
- * Therefore simply do not trace compat system calls at all.
- * See kernel/trace/trace_syscalls.c.
- */
-#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS
-static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs)
-{
- return is_compat_task();
-}
-
#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
static inline bool arch_syscall_match_sym_name(const char *sym,
const char *name)
{
- /*
- * Skip __s390_ and __s390x_ prefix - due to compat wrappers
- * and aliasing some symbols of 64 bit system call functions
- * may get the __s390_ prefix instead of the __s390x_ prefix.
- */
+ /* Skip the __s390x_ prefix. */
return !strcmp(sym + 7, name) || !strcmp(sym + 8, name);
}
diff --git a/arch/s390/include/asm/idals.h b/arch/s390/include/asm/idals.h
index ac68c657b28c..e5000ee6cdc6 100644
--- a/arch/s390/include/asm/idals.h
+++ b/arch/s390/include/asm/idals.h
@@ -181,6 +181,82 @@ static inline void idal_buffer_free(struct idal_buffer *ib)
}
/*
+ * Allocate an array of IDAL buffers to cover a total data size of @size. The
+ * resulting array is null-terminated.
+ *
+ * The amount of individual IDAL buffers is determined based on @size.
+ * Each IDAL buffer can have a maximum size of @CCW_MAX_BYTE_COUNT.
+ */
+static inline struct idal_buffer **idal_buffer_array_alloc(size_t size, int page_order)
+{
+ struct idal_buffer **ibs;
+ size_t ib_size; /* Size of a single idal buffer */
+ int count; /* Amount of individual idal buffers */
+ int i;
+
+ count = (size + CCW_MAX_BYTE_COUNT - 1) / CCW_MAX_BYTE_COUNT;
+ ibs = kmalloc_array(count + 1, sizeof(*ibs), GFP_KERNEL);
+ for (i = 0; i < count; i++) {
+ /* Determine size for the current idal buffer */
+ ib_size = min(size, CCW_MAX_BYTE_COUNT);
+ size -= ib_size;
+ ibs[i] = idal_buffer_alloc(ib_size, page_order);
+ if (IS_ERR(ibs[i])) {
+ while (i--)
+ idal_buffer_free(ibs[i]);
+ kfree(ibs);
+ ibs = NULL;
+ return ERR_PTR(-ENOMEM);
+ }
+ }
+ ibs[i] = NULL;
+ return ibs;
+}
+
+/*
+ * Free array of IDAL buffers
+ */
+static inline void idal_buffer_array_free(struct idal_buffer ***ibs)
+{
+ struct idal_buffer **p;
+
+ if (!ibs || !*ibs)
+ return;
+ for (p = *ibs; *p; p++)
+ idal_buffer_free(*p);
+ kfree(*ibs);
+ *ibs = NULL;
+}
+
+/*
+ * Determine size of IDAL buffer array
+ */
+static inline int idal_buffer_array_size(struct idal_buffer **ibs)
+{
+ int size = 0;
+
+ while (ibs && *ibs) {
+ size++;
+ ibs++;
+ }
+ return size;
+}
+
+/*
+ * Determine total data size covered by IDAL buffer array
+ */
+static inline size_t idal_buffer_array_datasize(struct idal_buffer **ibs)
+{
+ size_t size = 0;
+
+ while (ibs && *ibs) {
+ size += (*ibs)->size;
+ ibs++;
+ }
+ return size;
+}
+
+/*
* Test if a idal list is really needed.
*/
static inline bool __idal_buffer_is_needed(struct idal_buffer *ib)
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index d9c853db9a40..50ffe75adeb4 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -100,7 +100,8 @@ struct lowcore {
/* Save areas. */
__u64 save_area[8]; /* 0x0200 */
- __u8 pad_0x0240[0x0280-0x0240]; /* 0x0240 */
+ __u64 stack_canary; /* 0x0240 */
+ __u8 pad_0x0248[0x0280-0x0248]; /* 0x0248 */
__u64 save_area_restart[1]; /* 0x0280 */
__u64 pcpu; /* 0x0288 */
diff --git a/arch/s390/include/asm/nospec-insn.h b/arch/s390/include/asm/nospec-insn.h
index 6ce6b56e282b..46f92bb4c9e5 100644
--- a/arch/s390/include/asm/nospec-insn.h
+++ b/arch/s390/include/asm/nospec-insn.h
@@ -19,7 +19,7 @@
#ifdef CONFIG_EXPOLINE_EXTERN
SYM_CODE_START(\name)
#else
- .pushsection .text.\name,"axG",@progbits,\name,comdat
+ .pushsection .text..\name,"axG",@progbits,\name,comdat
.globl \name
.hidden \name
.type \name,@function
diff --git a/arch/s390/include/asm/pai.h b/arch/s390/include/asm/pai.h
index ebeabd0aaa51..534d0320e2aa 100644
--- a/arch/s390/include/asm/pai.h
+++ b/arch/s390/include/asm/pai.h
@@ -77,6 +77,7 @@ static __always_inline void pai_kernel_exit(struct pt_regs *regs)
#define PAI_SAVE_AREA(x) ((x)->hw.event_base)
#define PAI_CPU_MASK(x) ((x)->hw.addr_filters)
+#define PAI_PMU_IDX(x) ((x)->hw.last_tag)
#define PAI_SWLIST(x) (&(x)->hw.tp_list)
#endif
diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h
index 6890925d5587..a32f465ecf73 100644
--- a/arch/s390/include/asm/pci.h
+++ b/arch/s390/include/asm/pci.h
@@ -145,7 +145,6 @@ struct zpci_dev {
u8 has_resources : 1;
u8 is_physfn : 1;
u8 util_str_avail : 1;
- u8 irqs_registered : 1;
u8 tid_avail : 1;
u8 rtr_avail : 1; /* Relaxed translation allowed */
unsigned int devfn; /* DEVFN part of the RID*/
diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h
index 965886dfe954..5899f57f17d1 100644
--- a/arch/s390/include/asm/percpu.h
+++ b/arch/s390/include/asm/percpu.h
@@ -13,14 +13,6 @@
#define __my_cpu_offset get_lowcore()->percpu_offset
/*
- * For 64 bit module code, the module may be more than 4G above the
- * per cpu area, use weak definitions to force the compiler to
- * generate external references.
- * Therefore, we have enabled CONFIG_ARCH_MODULE_NEEDS_WEAK_PER_CPU
- * in the Kconfig.
- */
-
-/*
* We use a compare-and-swap loop since that uses less cpu cycles than
* disabling and enabling interrupts like the generic variant would do.
*/
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index b7100c6a4054..bca9b29778c3 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -648,18 +648,6 @@ static inline int mm_uses_skeys(struct mm_struct *mm)
return 0;
}
-static inline void csp(unsigned int *ptr, unsigned int old, unsigned int new)
-{
- union register_pair r1 = { .even = old, .odd = new, };
- unsigned long address = (unsigned long)ptr | 1;
-
- asm volatile(
- " csp %[r1],%[address]"
- : [r1] "+&d" (r1.pair), "+m" (*ptr)
- : [address] "d" (address)
- : "cc");
-}
-
/**
* cspg() - Compare and Swap and Purge (CSPG)
* @ptr: Pointer to the value to be exchanged
@@ -1154,17 +1142,15 @@ static inline pte_t pte_mkhuge(pte_t pte)
#define IPTE_NODAT 0x400
#define IPTE_GUEST_ASCE 0x800
-static __always_inline void __ptep_rdp(unsigned long addr, pte_t *ptep,
- unsigned long opt, unsigned long asce,
- int local)
+static __always_inline void __ptep_rdp(unsigned long addr, pte_t *ptep, int local)
{
unsigned long pto;
pto = __pa(ptep) & ~(PTRS_PER_PTE * sizeof(pte_t) - 1);
- asm volatile(".insn rrf,0xb98b0000,%[r1],%[r2],%[asce],%[m4]"
+ asm volatile(".insn rrf,0xb98b0000,%[r1],%[r2],%%r0,%[m4]"
: "+m" (*ptep)
- : [r1] "a" (pto), [r2] "a" ((addr & PAGE_MASK) | opt),
- [asce] "a" (asce), [m4] "i" (local));
+ : [r1] "a" (pto), [r2] "a" (addr & PAGE_MASK),
+ [m4] "i" (local));
}
static __always_inline void __ptep_ipte(unsigned long address, pte_t *ptep,
@@ -1348,7 +1334,7 @@ static inline void flush_tlb_fix_spurious_fault(struct vm_area_struct *vma,
* A local RDP can be used to do the flush.
*/
if (cpu_has_rdp() && !(pte_val(*ptep) & _PAGE_PROTECT))
- __ptep_rdp(address, ptep, 0, 0, 1);
+ __ptep_rdp(address, ptep, 1);
}
#define flush_tlb_fix_spurious_fault flush_tlb_fix_spurious_fault
@@ -1402,7 +1388,6 @@ int set_pgste_bits(struct mm_struct *mm, unsigned long addr,
int get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep);
int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc,
unsigned long *oldpte, unsigned long *oldpgste);
-void gmap_pmdp_csp(struct mm_struct *mm, unsigned long vmaddr);
void gmap_pmdp_invalidate(struct mm_struct *mm, unsigned long vmaddr);
void gmap_pmdp_idte_local(struct mm_struct *mm, unsigned long vmaddr);
void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr);
@@ -1692,10 +1677,10 @@ static inline pmd_t mk_pmd_phys(unsigned long physpage, pgprot_t pgprot)
#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_HUGETLB_PAGE */
-static inline void __pmdp_csp(pmd_t *pmdp)
+static inline void __pmdp_cspg(pmd_t *pmdp)
{
- csp((unsigned int *)pmdp + 1, pmd_val(*pmdp),
- pmd_val(*pmdp) | _SEGMENT_ENTRY_INVALID);
+ cspg((unsigned long *)pmdp, pmd_val(*pmdp),
+ pmd_val(*pmdp) | _SEGMENT_ENTRY_INVALID);
}
#define IDTE_GLOBAL 0
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 93e1034485d7..3affba95845b 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -119,18 +119,12 @@ extern void execve_tail(void);
unsigned long vdso_text_size(void);
unsigned long vdso_size(void);
-/*
- * User space process size: 2GB for 31 bit, 4TB or 8PT for 64 bit.
- */
-
-#define TASK_SIZE (test_thread_flag(TIF_31BIT) ? \
- _REGION3_SIZE : TASK_SIZE_MAX)
-#define TASK_UNMAPPED_BASE (test_thread_flag(TIF_31BIT) ? \
- (_REGION3_SIZE >> 1) : (_REGION2_SIZE >> 1))
+#define TASK_SIZE (TASK_SIZE_MAX)
+#define TASK_UNMAPPED_BASE (_REGION2_SIZE >> 1)
#define TASK_SIZE_MAX (-PAGE_SIZE)
#define VDSO_BASE (STACK_TOP + PAGE_SIZE)
-#define VDSO_LIMIT (test_thread_flag(TIF_31BIT) ? _REGION3_SIZE : _REGION2_SIZE)
+#define VDSO_LIMIT (_REGION2_SIZE)
#define STACK_TOP (VDSO_LIMIT - vdso_size() - PAGE_SIZE)
#define STACK_TOP_MAX (_REGION2_SIZE - vdso_size() - PAGE_SIZE)
@@ -181,7 +175,6 @@ struct thread_struct {
unsigned long system_timer; /* task cputime in kernel space */
unsigned long hardirq_timer; /* task cputime in hardirq context */
unsigned long softirq_timer; /* task cputime in softirq context */
- const sys_call_ptr_t *sys_call_table; /* system call table address */
union teid gmap_teid; /* address and flags of last gmap fault */
unsigned int gmap_int_code; /* int code of last gmap fault */
int ufpu_flags; /* user fpu flags */
@@ -379,14 +372,19 @@ static inline void local_mcck_enable(void)
/*
* Rewind PSW instruction address by specified number of bytes.
*/
-static inline unsigned long __rewind_psw(psw_t psw, unsigned long ilc)
+static inline unsigned long __rewind_psw(psw_t psw, long ilen)
{
unsigned long mask;
mask = (psw.mask & PSW_MASK_EA) ? -1UL :
(psw.mask & PSW_MASK_BA) ? (1UL << 31) - 1 :
(1UL << 24) - 1;
- return (psw.addr - ilc) & mask;
+ return (psw.addr - ilen) & mask;
+}
+
+static inline unsigned long __forward_psw(psw_t psw, long ilen)
+{
+ return __rewind_psw(psw, -ilen);
}
/*
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index dfa770b15fad..962cf042c66d 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -8,16 +8,19 @@
#define _S390_PTRACE_H
#include <linux/bits.h>
+#include <linux/typecheck.h>
#include <uapi/asm/ptrace.h>
#include <asm/thread_info.h>
#include <asm/tpi.h>
#define PIF_SYSCALL 0 /* inside a system call */
+#define PIF_PSW_ADDR_ADJUSTED 1 /* psw address has been adjusted */
#define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */
#define PIF_GUEST_FAULT 3 /* indicates program check in sie64a */
#define PIF_FTRACE_FULL_REGS 4 /* all register contents valid (ftrace) */
#define _PIF_SYSCALL BIT(PIF_SYSCALL)
+#define _PIF_ADDR_PSW_ADJUSTED BIT(PIF_PSW_ADDR_ADJUSTED)
#define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET)
#define _PIF_GUEST_FAULT BIT(PIF_GUEST_FAULT)
#define _PIF_FTRACE_FULL_REGS BIT(PIF_FTRACE_FULL_REGS)
@@ -99,7 +102,7 @@ enum {
typedef struct {
unsigned int mask;
unsigned int addr;
-} psw_t32 __aligned(8);
+} psw32_t __aligned(8);
#define PGM_INT_CODE_MASK 0x7f
#define PGM_INT_CODE_PER 0x80
diff --git a/arch/s390/include/asm/seccomp.h b/arch/s390/include/asm/seccomp.h
index 71d46f0ba97b..f904b674fee0 100644
--- a/arch/s390/include/asm/seccomp.h
+++ b/arch/s390/include/asm/seccomp.h
@@ -19,10 +19,5 @@
#define SECCOMP_ARCH_NATIVE AUDIT_ARCH_S390X
#define SECCOMP_ARCH_NATIVE_NR NR_syscalls
#define SECCOMP_ARCH_NATIVE_NAME "s390x"
-#ifdef CONFIG_COMPAT
-# define SECCOMP_ARCH_COMPAT AUDIT_ARCH_S390
-# define SECCOMP_ARCH_COMPAT_NR NR_syscalls
-# define SECCOMP_ARCH_COMPAT_NAME "s390"
-#endif
#endif /* _ASM_S390_SECCOMP_H */
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h
index 03f4d01664f8..fb2bdbf35da5 100644
--- a/arch/s390/include/asm/smp.h
+++ b/arch/s390/include/asm/smp.h
@@ -43,7 +43,7 @@ extern int __cpu_up(unsigned int cpu, struct task_struct *tidle);
extern void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
-extern void smp_call_ipl_cpu(void (*func)(void *), void *);
+extern void __noreturn smp_call_ipl_cpu(void (*func)(void *), void *data);
extern void smp_emergency_stop(void);
extern int smp_find_processor_id(u16 address);
diff --git a/arch/s390/include/asm/stackprotector.h b/arch/s390/include/asm/stackprotector.h
new file mode 100644
index 000000000000..0497850103dd
--- /dev/null
+++ b/arch/s390/include/asm/stackprotector.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _ASM_S390_STACKPROTECTOR_H
+#define _ASM_S390_STACKPROTECTOR_H
+
+#include <linux/sched.h>
+#include <asm/current.h>
+#include <asm/lowcore.h>
+
+static __always_inline void boot_init_stack_canary(void)
+{
+ current->stack_canary = get_random_canary();
+ get_lowcore()->stack_canary = current->stack_canary;
+}
+
+#endif /* _ASM_S390_STACKPROTECTOR_H */
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index 10ce5c4ccbd6..4271e4169f45 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -15,7 +15,6 @@
#include <asm/ptrace.h>
extern const sys_call_ptr_t sys_call_table[];
-extern const sys_call_ptr_t sys_call_table_emu[];
static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs)
@@ -46,15 +45,7 @@ static inline long syscall_get_error(struct task_struct *task,
struct pt_regs *regs)
{
unsigned long error = regs->gprs[2];
-#ifdef CONFIG_COMPAT
- if (test_tsk_thread_flag(task, TIF_31BIT)) {
- /*
- * Sign-extend the value so (int)-EFOO becomes (long)-EFOO
- * and will match correctly in comparisons.
- */
- error = (long)(int)error;
- }
-#endif
+
return IS_ERR_VALUE(error) ? error : 0;
}
@@ -78,10 +69,6 @@ static inline void syscall_get_arguments(struct task_struct *task,
{
unsigned long mask = -1UL;
-#ifdef CONFIG_COMPAT
- if (test_tsk_thread_flag(task, TIF_31BIT))
- mask = 0xffffffff;
-#endif
for (int i = 1; i < 6; i++)
args[i] = regs->gprs[2 + i] & mask;
@@ -99,10 +86,6 @@ static inline void syscall_set_arguments(struct task_struct *task,
static inline int syscall_get_arch(struct task_struct *task)
{
-#ifdef CONFIG_COMPAT
- if (test_tsk_thread_flag(task, TIF_31BIT))
- return AUDIT_ARCH_S390;
-#endif
return AUDIT_ARCH_S390X;
}
diff --git a/arch/s390/include/asm/syscall_wrapper.h b/arch/s390/include/asm/syscall_wrapper.h
index 35c1d1b860d8..9eb58d5348d8 100644
--- a/arch/s390/include/asm/syscall_wrapper.h
+++ b/arch/s390/include/asm/syscall_wrapper.h
@@ -13,101 +13,12 @@
,, regs->orig_gpr2,, regs->gprs[3],, regs->gprs[4] \
,, regs->gprs[5],, regs->gprs[6],, regs->gprs[7])
-#ifdef CONFIG_COMPAT
-
-#define __SC_COMPAT_CAST(t, a) \
-({ \
- long __ReS = a; \
- \
- BUILD_BUG_ON((sizeof(t) > 4) && !__TYPE_IS_L(t) && \
- !__TYPE_IS_UL(t) && !__TYPE_IS_PTR(t) && \
- !__TYPE_IS_LL(t)); \
- if (__TYPE_IS_L(t)) \
- __ReS = (s32)a; \
- if (__TYPE_IS_UL(t)) \
- __ReS = (u32)a; \
- if (__TYPE_IS_PTR(t)) \
- __ReS = a & 0x7fffffff; \
- if (__TYPE_IS_LL(t)) \
- return -ENOSYS; \
- (t)__ReS; \
-})
-
-/*
- * To keep the naming coherent, re-define SYSCALL_DEFINE0 to create an alias
- * named __s390x_sys_*()
- */
-#define COMPAT_SYSCALL_DEFINE0(sname) \
- long __s390_compat_sys_##sname(void); \
- ALLOW_ERROR_INJECTION(__s390_compat_sys_##sname, ERRNO); \
- long __s390_compat_sys_##sname(void)
-
-#define SYSCALL_DEFINE0(sname) \
- SYSCALL_METADATA(_##sname, 0); \
- long __s390_sys_##sname(void); \
- ALLOW_ERROR_INJECTION(__s390_sys_##sname, ERRNO); \
- long __s390x_sys_##sname(void); \
- ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \
- static inline long __do_sys_##sname(void); \
- long __s390_sys_##sname(void) \
- { \
- return __do_sys_##sname(); \
- } \
- long __s390x_sys_##sname(void) \
- { \
- return __do_sys_##sname(); \
- } \
- static inline long __do_sys_##sname(void)
-
-#define COND_SYSCALL(name) \
- cond_syscall(__s390x_sys_##name); \
- cond_syscall(__s390_sys_##name)
-
-#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \
- long __s390_compat_sys##name(struct pt_regs *regs); \
- ALLOW_ERROR_INJECTION(__s390_compat_sys##name, ERRNO); \
- static inline long __se_compat_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)); \
- static inline long __do_compat_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__)); \
- long __s390_compat_sys##name(struct pt_regs *regs) \
- { \
- return __se_compat_sys##name(SC_S390_REGS_TO_ARGS(x, __VA_ARGS__)); \
- } \
- static inline long __se_compat_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)) \
- { \
- __MAP(x, __SC_TEST, __VA_ARGS__); \
- return __do_compat_sys##name(__MAP(x, __SC_DELOUSE, __VA_ARGS__)); \
- } \
- static inline long __do_compat_sys##name(__MAP(x, __SC_DECL, __VA_ARGS__))
-
-/*
- * As some compat syscalls may not be implemented, we need to expand
- * COND_SYSCALL_COMPAT in kernel/sys_ni.c to cover this case as well.
- */
-#define COND_SYSCALL_COMPAT(name) \
- cond_syscall(__s390_compat_sys_##name)
-
-#define __S390_SYS_STUBx(x, name, ...) \
- long __s390_sys##name(struct pt_regs *regs); \
- ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \
- static inline long ___se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)); \
- long __s390_sys##name(struct pt_regs *regs) \
- { \
- return ___se_sys##name(SC_S390_REGS_TO_ARGS(x, __VA_ARGS__)); \
- } \
- static inline long ___se_sys##name(__MAP(x, __SC_LONG, __VA_ARGS__)) \
- { \
- __MAP(x, __SC_TEST, __VA_ARGS__); \
- return __do_sys##name(__MAP(x, __SC_COMPAT_CAST, __VA_ARGS__)); \
- }
-
-#else /* CONFIG_COMPAT */
-
#define SYSCALL_DEFINE0(sname) \
SYSCALL_METADATA(_##sname, 0); \
- long __s390x_sys_##sname(void); \
+ long __s390x_sys_##sname(struct pt_regs *__unused); \
ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \
static inline long __do_sys_##sname(void); \
- long __s390x_sys_##sname(void) \
+ long __s390x_sys_##sname(struct pt_regs *__unused) \
{ \
return __do_sys_##sname(); \
} \
@@ -118,8 +29,6 @@
#define __S390_SYS_STUBx(x, fullname, name, ...)
-#endif /* CONFIG_COMPAT */
-
#define __SYSCALL_DEFINEx(x, name, ...) \
long __s390x_sys##name(struct pt_regs *regs); \
ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \
diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h
index 7878e9bfbf07..6a548a819400 100644
--- a/arch/s390/include/asm/thread_info.h
+++ b/arch/s390/include/asm/thread_info.h
@@ -69,7 +69,6 @@ void arch_setup_new_exec(void);
#define TIF_GUARDED_STORAGE 17 /* load guarded storage control block */
#define TIF_ISOLATE_BP_GUEST 18 /* Run KVM guests with isolated BP */
#define TIF_PER_TRAP 19 /* Need to handle PER trap on exit to usermode */
-#define TIF_31BIT 20 /* 32bit process */
#define TIF_SINGLE_STEP 21 /* This task is single stepped */
#define TIF_BLOCK_STEP 22 /* This task is block stepped */
#define TIF_UPROBE_SINGLESTEP 23 /* This task is uprobe single stepped */
@@ -78,7 +77,6 @@ void arch_setup_new_exec(void);
#define _TIF_GUARDED_STORAGE BIT(TIF_GUARDED_STORAGE)
#define _TIF_ISOLATE_BP_GUEST BIT(TIF_ISOLATE_BP_GUEST)
#define _TIF_PER_TRAP BIT(TIF_PER_TRAP)
-#define _TIF_31BIT BIT(TIF_31BIT)
#define _TIF_SINGLE_STEP BIT(TIF_SINGLE_STEP)
#define _TIF_BLOCK_STEP BIT(TIF_BLOCK_STEP)
#define _TIF_UPROBE_SINGLESTEP BIT(TIF_UPROBE_SINGLESTEP)
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h
index 75491baa2197..163ccbbe8c47 100644
--- a/arch/s390/include/asm/tlbflush.h
+++ b/arch/s390/include/asm/tlbflush.h
@@ -35,9 +35,9 @@ static inline void __tlb_flush_idte(unsigned long asce)
*/
static inline void __tlb_flush_global(void)
{
- unsigned int dummy = 0;
+ unsigned long dummy = 0;
- csp(&dummy, 0, 0);
+ cspg(&dummy, 0, 0);
}
/*
@@ -54,7 +54,7 @@ static inline void __tlb_flush_mm(struct mm_struct *mm)
cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
barrier();
gmap_asce = READ_ONCE(mm->context.gmap_asce);
- if (cpu_has_idte() && gmap_asce != -1UL) {
+ if (gmap_asce != -1UL) {
if (gmap_asce)
__tlb_flush_idte(gmap_asce);
__tlb_flush_idte(mm->context.asce);
@@ -68,10 +68,7 @@ static inline void __tlb_flush_mm(struct mm_struct *mm)
static inline void __tlb_flush_kernel(void)
{
- if (cpu_has_idte())
- __tlb_flush_idte(init_mm.context.asce);
- else
- __tlb_flush_global();
+ __tlb_flush_idte(init_mm.context.asce);
}
static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
@@ -86,7 +83,6 @@ static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
/*
* TLB flushing:
- * flush_tlb() - flushes the current mm struct TLBs
* flush_tlb_all() - flushes all processes TLBs
* flush_tlb_mm(mm) - flushes the specified mm context TLB's
* flush_tlb_page(vma, vmaddr) - flushes one page
@@ -102,7 +98,6 @@ static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
* only one user. At the end of the update the flush_tlb_mm and
* flush_tlb_range functions need to do the flush.
*/
-#define flush_tlb() do { } while (0)
#define flush_tlb_all() do { } while (0)
#define flush_tlb_page(vma, addr) do { } while (0)
diff --git a/arch/s390/include/asm/trace/ap.h b/arch/s390/include/asm/trace/ap.h
new file mode 100644
index 000000000000..5c2e6c664b4d
--- /dev/null
+++ b/arch/s390/include/asm/trace/ap.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Tracepoint definitions for s390 ap bus related trace events
+ *
+ * There are two AP bus related tracepoint events defined here:
+ * There is a tracepoint s390_ap_nqap event immediately after a request
+ * has been pushed into the AP firmware queue with the NQAP AP command.
+ * The other tracepoint s390_ap_dqap event fires immediately after a
+ * reply has been pulled out of the AP firmware queue via DQAP AP command.
+ * The idea of these two trace events focuses on performance to measure
+ * the runtime of a crypto request/reply as close as possible at the
+ * firmware level. In combination with the two zcrypt tracepoints (see the
+ * zcrypt.h trace event definition file) this gives measurement data about
+ * the runtime of a request/reply within the zcrpyt and AP bus layer.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM s390
+
+#if !defined(_TRACE_S390_AP_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_S390_AP_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(s390_ap_nqapdqap_template,
+ TP_PROTO(u16 card, u16 dom, u32 status, u64 psmid),
+ TP_ARGS(card, dom, status, psmid),
+ TP_STRUCT__entry(
+ __field(u16, card)
+ __field(u16, dom)
+ __field(u32, status)
+ __field(u64, psmid)),
+ TP_fast_assign(
+ __entry->card = card;
+ __entry->dom = dom;
+ __entry->status = status;
+ __entry->psmid = psmid;),
+ TP_printk("card=%u dom=%u status=0x%08x psmid=0x%016lx",
+ (unsigned short)__entry->card,
+ (unsigned short)__entry->dom,
+ (unsigned int)__entry->status,
+ (unsigned long)__entry->psmid)
+);
+
+/**
+ * trace_s390_ap_nqap - ap msg nqap tracepoint function
+ * @card: Crypto card number addressed.
+ * @dom: Domain within the crypto card addressed.
+ * @status: AP queue status (GR1 on return of nqap).
+ * @psmid: Unique id identifying this request/reply.
+ *
+ * Called immediately after a request has been enqueued into
+ * the AP firmware queue with the NQAP command.
+ */
+DEFINE_EVENT(s390_ap_nqapdqap_template,
+ s390_ap_nqap,
+ TP_PROTO(u16 card, u16 dom, u32 status, u64 psmid),
+ TP_ARGS(card, dom, status, psmid)
+);
+
+/**
+ * trace_s390_ap_dqap - ap msg dqap tracepoint function
+ * @card: Crypto card number addressed.
+ * @dom: Domain within the crypto card addressed.
+ * @status: AP queue status (GR1 on return of dqap).
+ * @psmid: Unique id identifying this request/reply.
+ *
+ * Called immediately after a reply has been dequeued from
+ * the AP firmware queue with the DQAP command.
+ */
+DEFINE_EVENT(s390_ap_nqapdqap_template,
+ s390_ap_dqap,
+ TP_PROTO(u16 card, u16 dom, u32 status, u64 psmid),
+ TP_ARGS(card, dom, status, psmid)
+);
+
+#endif /* _TRACE_S390_AP_H */
+
+/* This part must be outside protection */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+
+#define TRACE_INCLUDE_PATH asm/trace
+#define TRACE_INCLUDE_FILE ap
+
+#include <trace/define_trace.h>
diff --git a/arch/s390/include/asm/trace/zcrypt.h b/arch/s390/include/asm/trace/zcrypt.h
index 457ddaa99e19..bfb01e335f31 100644
--- a/arch/s390/include/asm/trace/zcrypt.h
+++ b/arch/s390/include/asm/trace/zcrypt.h
@@ -2,7 +2,7 @@
/*
* Tracepoint definitions for the s390 zcrypt device driver
*
- * Copyright IBM Corp. 2016
+ * Copyright IBM Corp. 2016,2025
* Author(s): Harald Freudenberger <freude@de.ibm.com>
*
* Currently there are two tracepoint events defined here.
@@ -73,14 +73,15 @@ TRACE_EVENT(s390_zcrypt_req,
/**
* trace_s390_zcrypt_rep - zcrypt reply tracepoint function
- * @ptr: Address of the local buffer where the request from userspace
- * is stored. Can be used as a unique id to match together
- * request and reply.
- * @fc: Function code.
- * @rc: The bare returncode as returned by the device driver ioctl
- * function.
- * @dev: The adapter nr where this request was actually processed.
- * @dom: Domain id of the device where this request was processed.
+ * @ptr: Address of the local buffer where the request from userspace
+ * is stored. Can be used as a unique id to match together
+ * request and reply.
+ * @fc: Function code.
+ * @rc: The bare returncode as returned by the device driver ioctl
+ * function.
+ * @card: The adapter nr where this request was actually processed.
+ * @dom: Domain id of the device where this request was processed.
+ * @psmid: Unique id identifying this request/reply.
*
* Called upon recognising the reply from the crypto adapter. This
* message may act as the exit timestamp for the request but also
@@ -88,26 +89,29 @@ TRACE_EVENT(s390_zcrypt_req,
* and the returncode from the device driver.
*/
TRACE_EVENT(s390_zcrypt_rep,
- TP_PROTO(void *ptr, u32 fc, u32 rc, u16 dev, u16 dom),
- TP_ARGS(ptr, fc, rc, dev, dom),
+ TP_PROTO(void *ptr, u32 fc, u32 rc, u16 card, u16 dom, u64 psmid),
+ TP_ARGS(ptr, fc, rc, card, dom, psmid),
TP_STRUCT__entry(
__field(void *, ptr)
__field(u32, fc)
__field(u32, rc)
- __field(u16, device)
- __field(u16, domain)),
+ __field(u16, card)
+ __field(u16, dom)
+ __field(u64, psmid)),
TP_fast_assign(
__entry->ptr = ptr;
__entry->fc = fc;
__entry->rc = rc;
- __entry->device = dev;
- __entry->domain = dom;),
- TP_printk("ptr=%p fc=0x%04x rc=%d dev=0x%02hx domain=0x%04hx",
+ __entry->card = card;
+ __entry->dom = dom;
+ __entry->psmid = psmid;),
+ TP_printk("ptr=%p fc=0x%04x rc=%d card=%u dom=%u psmid=0x%016lx",
__entry->ptr,
- (unsigned int) __entry->fc,
- (int) __entry->rc,
- (unsigned short) __entry->device,
- (unsigned short) __entry->domain)
+ (unsigned int)__entry->fc,
+ (int)__entry->rc,
+ (unsigned short)__entry->card,
+ (unsigned short)__entry->dom,
+ (unsigned long)__entry->psmid)
);
#endif /* _TRACE_S390_ZCRYPT_H */
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index 3e5b8b677057..c5e02addcd67 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -468,8 +468,8 @@ do { \
#endif /* CONFIG_CC_HAS_ASM_GOTO_OUTPUT && CONFIG_CC_HAS_ASM_AOR_FORMAT_FLAGS */
-#define __get_kernel_nofault __mvc_kernel_nofault
-#define __put_kernel_nofault __mvc_kernel_nofault
+#define arch_get_kernel_nofault __mvc_kernel_nofault
+#define arch_put_kernel_nofault __mvc_kernel_nofault
void __cmpxchg_user_key_called_with_bad_pointer(void);
diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h
index 70fc671397da..921c3fb3586b 100644
--- a/arch/s390/include/asm/unistd.h
+++ b/arch/s390/include/asm/unistd.h
@@ -8,7 +8,8 @@
#define _ASM_S390_UNISTD_H_
#include <uapi/asm/unistd.h>
-#include <asm/unistd_nr.h>
+
+#define NR_syscalls (__NR_syscalls)
#define __ARCH_WANT_NEW_STAT
#define __ARCH_WANT_OLD_READDIR
@@ -27,11 +28,6 @@
#define __ARCH_WANT_SYS_OLDUMOUNT
#define __ARCH_WANT_SYS_SIGPENDING
#define __ARCH_WANT_SYS_SIGPROCMASK
-# ifdef CONFIG_COMPAT
-# define __ARCH_WANT_COMPAT_STAT
-# define __ARCH_WANT_SYS_TIME32
-# define __ARCH_WANT_SYS_UTIME32
-# endif
#define __ARCH_WANT_SYS_FORK
#define __ARCH_WANT_SYS_VFORK
#define __ARCH_WANT_SYS_CLONE
diff --git a/arch/s390/include/asm/vdso-symbols.h b/arch/s390/include/asm/vdso-symbols.h
index 0df17574d788..e3561e67c4e3 100644
--- a/arch/s390/include/asm/vdso-symbols.h
+++ b/arch/s390/include/asm/vdso-symbols.h
@@ -2,16 +2,8 @@
#ifndef __S390_VDSO_SYMBOLS_H__
#define __S390_VDSO_SYMBOLS_H__
-#include <generated/vdso64-offsets.h>
-#ifdef CONFIG_COMPAT
-#include <generated/vdso32-offsets.h>
-#endif
+#include <generated/vdso-offsets.h>
-#define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name))
-#ifdef CONFIG_COMPAT
-#define VDSO32_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso32_offset_##name))
-#else
-#define VDSO32_SYMBOL(tsk, name) (-1UL)
-#endif
+#define VDSO_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso_offset_##name))
#endif /* __S390_VDSO_SYMBOLS_H__ */
diff --git a/arch/s390/include/uapi/asm/bitsperlong.h b/arch/s390/include/uapi/asm/bitsperlong.h
index cceaf47b021a..7af27a985f25 100644
--- a/arch/s390/include/uapi/asm/bitsperlong.h
+++ b/arch/s390/include/uapi/asm/bitsperlong.h
@@ -2,11 +2,7 @@
#ifndef __ASM_S390_BITSPERLONG_H
#define __ASM_S390_BITSPERLONG_H
-#ifndef __s390x__
-#define __BITS_PER_LONG 32
-#else
#define __BITS_PER_LONG 64
-#endif
#include <asm-generic/bitsperlong.h>
diff --git a/arch/s390/include/uapi/asm/ipcbuf.h b/arch/s390/include/uapi/asm/ipcbuf.h
index 1030cd186899..9277e76d6d72 100644
--- a/arch/s390/include/uapi/asm/ipcbuf.h
+++ b/arch/s390/include/uapi/asm/ipcbuf.h
@@ -24,9 +24,6 @@ struct ipc64_perm
__kernel_mode_t mode;
unsigned short __pad1;
unsigned short seq;
-#ifndef __s390x__
- unsigned short __pad2;
-#endif /* ! __s390x__ */
unsigned long __unused1;
unsigned long __unused2;
};
diff --git a/arch/s390/include/uapi/asm/posix_types.h b/arch/s390/include/uapi/asm/posix_types.h
index 1913613e71b6..ad5ab940d192 100644
--- a/arch/s390/include/uapi/asm/posix_types.h
+++ b/arch/s390/include/uapi/asm/posix_types.h
@@ -26,17 +26,6 @@ typedef unsigned short __kernel_old_gid_t;
#define __kernel_old_uid_t __kernel_old_uid_t
#endif
-#ifndef __s390x__
-
-typedef unsigned long __kernel_ino_t;
-typedef unsigned short __kernel_mode_t;
-typedef unsigned short __kernel_ipc_pid_t;
-typedef unsigned short __kernel_uid_t;
-typedef unsigned short __kernel_gid_t;
-typedef int __kernel_ptrdiff_t;
-
-#else /* __s390x__ */
-
typedef unsigned int __kernel_ino_t;
typedef unsigned int __kernel_mode_t;
typedef int __kernel_ipc_pid_t;
@@ -45,8 +34,6 @@ typedef unsigned int __kernel_gid_t;
typedef long __kernel_ptrdiff_t;
typedef unsigned long __kernel_sigset_t; /* at least 32 bits */
-#endif /* __s390x__ */
-
#define __kernel_ino_t __kernel_ino_t
#define __kernel_mode_t __kernel_mode_t
#define __kernel_ipc_pid_t __kernel_ipc_pid_t
diff --git a/arch/s390/include/uapi/asm/ptrace.h b/arch/s390/include/uapi/asm/ptrace.h
index ea202072f1ad..ea29ba470e5a 100644
--- a/arch/s390/include/uapi/asm/ptrace.h
+++ b/arch/s390/include/uapi/asm/ptrace.h
@@ -14,94 +14,6 @@
* Offsets in the user_regs_struct. They are used for the ptrace
* system call and in entry.S
*/
-#ifndef __s390x__
-
-#define PT_PSWMASK 0x00
-#define PT_PSWADDR 0x04
-#define PT_GPR0 0x08
-#define PT_GPR1 0x0C
-#define PT_GPR2 0x10
-#define PT_GPR3 0x14
-#define PT_GPR4 0x18
-#define PT_GPR5 0x1C
-#define PT_GPR6 0x20
-#define PT_GPR7 0x24
-#define PT_GPR8 0x28
-#define PT_GPR9 0x2C
-#define PT_GPR10 0x30
-#define PT_GPR11 0x34
-#define PT_GPR12 0x38
-#define PT_GPR13 0x3C
-#define PT_GPR14 0x40
-#define PT_GPR15 0x44
-#define PT_ACR0 0x48
-#define PT_ACR1 0x4C
-#define PT_ACR2 0x50
-#define PT_ACR3 0x54
-#define PT_ACR4 0x58
-#define PT_ACR5 0x5C
-#define PT_ACR6 0x60
-#define PT_ACR7 0x64
-#define PT_ACR8 0x68
-#define PT_ACR9 0x6C
-#define PT_ACR10 0x70
-#define PT_ACR11 0x74
-#define PT_ACR12 0x78
-#define PT_ACR13 0x7C
-#define PT_ACR14 0x80
-#define PT_ACR15 0x84
-#define PT_ORIGGPR2 0x88
-#define PT_FPC 0x90
-/*
- * A nasty fact of life that the ptrace api
- * only supports passing of longs.
- */
-#define PT_FPR0_HI 0x98
-#define PT_FPR0_LO 0x9C
-#define PT_FPR1_HI 0xA0
-#define PT_FPR1_LO 0xA4
-#define PT_FPR2_HI 0xA8
-#define PT_FPR2_LO 0xAC
-#define PT_FPR3_HI 0xB0
-#define PT_FPR3_LO 0xB4
-#define PT_FPR4_HI 0xB8
-#define PT_FPR4_LO 0xBC
-#define PT_FPR5_HI 0xC0
-#define PT_FPR5_LO 0xC4
-#define PT_FPR6_HI 0xC8
-#define PT_FPR6_LO 0xCC
-#define PT_FPR7_HI 0xD0
-#define PT_FPR7_LO 0xD4
-#define PT_FPR8_HI 0xD8
-#define PT_FPR8_LO 0XDC
-#define PT_FPR9_HI 0xE0
-#define PT_FPR9_LO 0xE4
-#define PT_FPR10_HI 0xE8
-#define PT_FPR10_LO 0xEC
-#define PT_FPR11_HI 0xF0
-#define PT_FPR11_LO 0xF4
-#define PT_FPR12_HI 0xF8
-#define PT_FPR12_LO 0xFC
-#define PT_FPR13_HI 0x100
-#define PT_FPR13_LO 0x104
-#define PT_FPR14_HI 0x108
-#define PT_FPR14_LO 0x10C
-#define PT_FPR15_HI 0x110
-#define PT_FPR15_LO 0x114
-#define PT_CR_9 0x118
-#define PT_CR_10 0x11C
-#define PT_CR_11 0x120
-#define PT_IEEE_IP 0x13C
-#define PT_LASTOFF PT_IEEE_IP
-#define PT_ENDREGS 0x140-1
-
-#define GPR_SIZE 4
-#define CR_SIZE 4
-
-#define STACK_FRAME_OVERHEAD 96 /* size of minimum stack frame */
-
-#else /* __s390x__ */
-
#define PT_PSWMASK 0x00
#define PT_PSWADDR 0x08
#define PT_GPR0 0x10
@@ -166,38 +78,6 @@
#define STACK_FRAME_OVERHEAD 160 /* size of minimum stack frame */
-#endif /* __s390x__ */
-
-#ifndef __s390x__
-
-#define PSW_MASK_PER _AC(0x40000000, UL)
-#define PSW_MASK_DAT _AC(0x04000000, UL)
-#define PSW_MASK_IO _AC(0x02000000, UL)
-#define PSW_MASK_EXT _AC(0x01000000, UL)
-#define PSW_MASK_KEY _AC(0x00F00000, UL)
-#define PSW_MASK_BASE _AC(0x00080000, UL) /* always one */
-#define PSW_MASK_MCHECK _AC(0x00040000, UL)
-#define PSW_MASK_WAIT _AC(0x00020000, UL)
-#define PSW_MASK_PSTATE _AC(0x00010000, UL)
-#define PSW_MASK_ASC _AC(0x0000C000, UL)
-#define PSW_MASK_CC _AC(0x00003000, UL)
-#define PSW_MASK_PM _AC(0x00000F00, UL)
-#define PSW_MASK_RI _AC(0x00000000, UL)
-#define PSW_MASK_EA _AC(0x00000000, UL)
-#define PSW_MASK_BA _AC(0x00000000, UL)
-
-#define PSW_MASK_USER _AC(0x0000FF00, UL)
-
-#define PSW_ADDR_AMODE _AC(0x80000000, UL)
-#define PSW_ADDR_INSN _AC(0x7FFFFFFF, UL)
-
-#define PSW_ASC_PRIMARY _AC(0x00000000, UL)
-#define PSW_ASC_ACCREG _AC(0x00004000, UL)
-#define PSW_ASC_SECONDARY _AC(0x00008000, UL)
-#define PSW_ASC_HOME _AC(0x0000C000, UL)
-
-#else /* __s390x__ */
-
#define PSW_MASK_PER _AC(0x4000000000000000, UL)
#define PSW_MASK_DAT _AC(0x0400000000000000, UL)
#define PSW_MASK_IO _AC(0x0200000000000000, UL)
@@ -224,8 +104,6 @@
#define PSW_ASC_SECONDARY _AC(0x0000800000000000, UL)
#define PSW_ASC_HOME _AC(0x0000C00000000000, UL)
-#endif /* __s390x__ */
-
#define NUM_GPRS 16
#define NUM_FPRS 16
#define NUM_CRS 16
@@ -308,9 +186,7 @@ typedef struct {
#define PER_EM_MASK 0xE8000000UL
typedef struct {
-#ifdef __s390x__
unsigned : 32;
-#endif /* __s390x__ */
unsigned em_branching : 1;
unsigned em_instruction_fetch : 1;
/*
diff --git a/arch/s390/include/uapi/asm/sigcontext.h b/arch/s390/include/uapi/asm/sigcontext.h
index 8b35033334c4..7c90b30c50fd 100644
--- a/arch/s390/include/uapi/asm/sigcontext.h
+++ b/arch/s390/include/uapi/asm/sigcontext.h
@@ -17,24 +17,12 @@
#define __NUM_VXRS_LOW 16
#define __NUM_VXRS_HIGH 16
-#ifndef __s390x__
-
-/* Has to be at least _NSIG_WORDS from asm/signal.h */
-#define _SIGCONTEXT_NSIG 64
-#define _SIGCONTEXT_NSIG_BPW 32
-/* Size of stack frame allocated when calling signal handler. */
-#define __SIGNAL_FRAMESIZE 96
-
-#else /* __s390x__ */
-
/* Has to be at least _NSIG_WORDS from asm/signal.h */
#define _SIGCONTEXT_NSIG 64
#define _SIGCONTEXT_NSIG_BPW 64
/* Size of stack frame allocated when calling signal handler. */
#define __SIGNAL_FRAMESIZE 160
-#endif /* __s390x__ */
-
#define _SIGCONTEXT_NSIG_WORDS (_SIGCONTEXT_NSIG / _SIGCONTEXT_NSIG_BPW)
#define _SIGMASK_COPY_SIZE (sizeof(unsigned long)*_SIGCONTEXT_NSIG_WORDS)
@@ -66,9 +54,6 @@ typedef struct
typedef struct
{
-#ifndef __s390x__
- unsigned long gprs_high[__NUM_GPRS];
-#endif
unsigned long long vxrs_low[__NUM_VXRS_LOW];
__vector128 vxrs_high[__NUM_VXRS_HIGH];
unsigned char __reserved[128];
diff --git a/arch/s390/include/uapi/asm/stat.h b/arch/s390/include/uapi/asm/stat.h
index ac253d23606b..21599298c2f5 100644
--- a/arch/s390/include/uapi/asm/stat.h
+++ b/arch/s390/include/uapi/asm/stat.h
@@ -8,74 +8,6 @@
#ifndef _S390_STAT_H
#define _S390_STAT_H
-#ifndef __s390x__
-struct __old_kernel_stat {
- unsigned short st_dev;
- unsigned short st_ino;
- unsigned short st_mode;
- unsigned short st_nlink;
- unsigned short st_uid;
- unsigned short st_gid;
- unsigned short st_rdev;
- unsigned long st_size;
- unsigned long st_atime;
- unsigned long st_mtime;
- unsigned long st_ctime;
-};
-
-struct stat {
- unsigned short st_dev;
- unsigned short __pad1;
- unsigned long st_ino;
- unsigned short st_mode;
- unsigned short st_nlink;
- unsigned short st_uid;
- unsigned short st_gid;
- unsigned short st_rdev;
- unsigned short __pad2;
- unsigned long st_size;
- unsigned long st_blksize;
- unsigned long st_blocks;
- unsigned long st_atime;
- unsigned long st_atime_nsec;
- unsigned long st_mtime;
- unsigned long st_mtime_nsec;
- unsigned long st_ctime;
- unsigned long st_ctime_nsec;
- unsigned long __unused4;
- unsigned long __unused5;
-};
-
-/* This matches struct stat64 in glibc2.1, hence the absolutely
- * insane amounts of padding around dev_t's.
- */
-struct stat64 {
- unsigned long long st_dev;
- unsigned int __pad1;
-#define STAT64_HAS_BROKEN_ST_INO 1
- unsigned long __st_ino;
- unsigned int st_mode;
- unsigned int st_nlink;
- unsigned long st_uid;
- unsigned long st_gid;
- unsigned long long st_rdev;
- unsigned int __pad3;
- long long st_size;
- unsigned long st_blksize;
- unsigned char __pad4[4];
- unsigned long __pad5; /* future possible st_blocks high bits */
- unsigned long st_blocks; /* Number 512-byte blocks allocated. */
- unsigned long st_atime;
- unsigned long st_atime_nsec;
- unsigned long st_mtime;
- unsigned long st_mtime_nsec;
- unsigned long st_ctime;
- unsigned long st_ctime_nsec; /* will be high 32 bits of ctime someday */
- unsigned long long st_ino;
-};
-
-#else /* __s390x__ */
-
struct stat {
unsigned long st_dev;
unsigned long st_ino;
@@ -97,8 +29,6 @@ struct stat {
unsigned long __unused[3];
};
-#endif /* __s390x__ */
-
#define STAT_HAVE_NSEC 1
#endif
diff --git a/arch/s390/include/uapi/asm/unistd.h b/arch/s390/include/uapi/asm/unistd.h
index 01b5fe8b9db6..b0c5afe19db2 100644
--- a/arch/s390/include/uapi/asm/unistd.h
+++ b/arch/s390/include/uapi/asm/unistd.h
@@ -8,10 +8,6 @@
#ifndef _UAPI_ASM_S390_UNISTD_H_
#define _UAPI_ASM_S390_UNISTD_H_
-#ifdef __s390x__
#include <asm/unistd_64.h>
-#else
-#include <asm/unistd_32.h>
-#endif
#endif /* _UAPI_ASM_S390_UNISTD_H_ */
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index eb06ff888314..42c83d60d6fa 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -36,7 +36,7 @@ CFLAGS_stacktrace.o += -fno-optimize-sibling-calls
CFLAGS_dumpstack.o += -fno-optimize-sibling-calls
CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls
-obj-y := head64.o traps.o time.o process.o early.o setup.o idle.o vtime.o
+obj-y := head.o traps.o time.o process.o early.o setup.o idle.o vtime.o
obj-y += processor.o syscall.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
obj-y += debug.o irq.o ipl.o dis.o vdso.o cpufeature.o
obj-y += sysinfo.o lgr.o os_info.o ctlreg.o
@@ -56,9 +56,6 @@ obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_SCHED_TOPOLOGY) += topology.o hiperdispatch.o
obj-$(CONFIG_NUMA) += numa.o
obj-$(CONFIG_AUDIT) += audit.o
-compat-obj-$(CONFIG_AUDIT) += compat_audit.o
-obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o
-obj-$(CONFIG_COMPAT) += $(compat-obj-y)
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KPROBES) += mcount.o
@@ -70,7 +67,7 @@ obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_VMCORE_INFO) += vmcore_info.o
obj-$(CONFIG_UPROBES) += uprobes.o
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
-
+obj-$(CONFIG_STACKPROTECTOR) += stackprotector.o
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o
obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o
obj-$(CONFIG_CERT_STORE) += cert_store.o
@@ -79,10 +76,9 @@ obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o
-obj-$(CONFIG_PERF_EVENTS) += perf_pai_crypto.o perf_pai_ext.o
+obj-$(CONFIG_PERF_EVENTS) += perf_pai.o
obj-$(CONFIG_TRACEPOINTS) += trace.o
# vdso
-obj-y += vdso64/
-obj-$(CONFIG_COMPAT) += vdso32/
+obj-y += vdso/
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index a8915663e917..cfe27f6579e3 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -21,6 +21,9 @@ int main(void)
OFFSET(__TASK_stack, task_struct, stack);
OFFSET(__TASK_thread, task_struct, thread);
OFFSET(__TASK_pid, task_struct, pid);
+#ifdef CONFIG_STACKPROTECTOR
+ OFFSET(__TASK_stack_canary, task_struct, stack_canary);
+#endif
BLANK();
/* thread struct offsets */
OFFSET(__THREAD_ksp, thread_struct, ksp);
@@ -139,6 +142,7 @@ int main(void)
OFFSET(__LC_CURRENT_PID, lowcore, current_pid);
OFFSET(__LC_LAST_BREAK, lowcore, last_break);
/* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */
+ OFFSET(__LC_STACK_CANARY, lowcore, stack_canary);
OFFSET(__LC_DUMP_REIPL, lowcore, ipib);
OFFSET(__LC_VMCORE_INFO, lowcore, vmcore_info);
OFFSET(__LC_OS_INFO, lowcore, os_info);
diff --git a/arch/s390/kernel/audit.c b/arch/s390/kernel/audit.c
index 02051a596b87..7897d9411e13 100644
--- a/arch/s390/kernel/audit.c
+++ b/arch/s390/kernel/audit.c
@@ -3,7 +3,6 @@
#include <linux/types.h>
#include <linux/audit.h>
#include <asm/unistd.h>
-#include "audit.h"
static unsigned dir_class[] = {
#include <asm-generic/audit_dir_write.h>
@@ -32,19 +31,11 @@ static unsigned signal_class[] = {
int audit_classify_arch(int arch)
{
-#ifdef CONFIG_COMPAT
- if (arch == AUDIT_ARCH_S390)
- return 1;
-#endif
return 0;
}
int audit_classify_syscall(int abi, unsigned syscall)
{
-#ifdef CONFIG_COMPAT
- if (abi == AUDIT_ARCH_S390)
- return s390_classify_syscall(syscall);
-#endif
switch(syscall) {
case __NR_open:
return AUDITSC_OPEN;
@@ -63,13 +54,6 @@ int audit_classify_syscall(int abi, unsigned syscall)
static int __init audit_classes_init(void)
{
-#ifdef CONFIG_COMPAT
- audit_register_class(AUDIT_CLASS_WRITE_32, s390_write_class);
- audit_register_class(AUDIT_CLASS_READ_32, s390_read_class);
- audit_register_class(AUDIT_CLASS_DIR_WRITE_32, s390_dir_class);
- audit_register_class(AUDIT_CLASS_CHATTR_32, s390_chattr_class);
- audit_register_class(AUDIT_CLASS_SIGNAL_32, s390_signal_class);
-#endif
audit_register_class(AUDIT_CLASS_WRITE, write_class);
audit_register_class(AUDIT_CLASS_READ, read_class);
audit_register_class(AUDIT_CLASS_DIR_WRITE, dir_class);
diff --git a/arch/s390/kernel/audit.h b/arch/s390/kernel/audit.h
deleted file mode 100644
index 4d4b596412ec..000000000000
--- a/arch/s390/kernel/audit.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __ARCH_S390_KERNEL_AUDIT_H
-#define __ARCH_S390_KERNEL_AUDIT_H
-
-#include <linux/types.h>
-
-#ifdef CONFIG_COMPAT
-extern int s390_classify_syscall(unsigned);
-extern __u32 s390_dir_class[];
-extern __u32 s390_write_class[];
-extern __u32 s390_read_class[];
-extern __u32 s390_chattr_class[];
-extern __u32 s390_signal_class[];
-#endif /* CONFIG_COMPAT */
-
-#endif /* __ARCH_S390_KERNEL_AUDIT_H */
diff --git a/arch/s390/kernel/compat_audit.c b/arch/s390/kernel/compat_audit.c
deleted file mode 100644
index a7c46e8310f0..000000000000
--- a/arch/s390/kernel/compat_audit.c
+++ /dev/null
@@ -1,48 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#undef __s390x__
-#include <linux/audit_arch.h>
-#include <asm/unistd.h>
-#include "audit.h"
-
-unsigned s390_dir_class[] = {
-#include <asm-generic/audit_dir_write.h>
-~0U
-};
-
-unsigned s390_chattr_class[] = {
-#include <asm-generic/audit_change_attr.h>
-~0U
-};
-
-unsigned s390_write_class[] = {
-#include <asm-generic/audit_write.h>
-~0U
-};
-
-unsigned s390_read_class[] = {
-#include <asm-generic/audit_read.h>
-~0U
-};
-
-unsigned s390_signal_class[] = {
-#include <asm-generic/audit_signal.h>
-~0U
-};
-
-int s390_classify_syscall(unsigned syscall)
-{
- switch(syscall) {
- case __NR_open:
- return AUDITSC_OPEN;
- case __NR_openat:
- return AUDITSC_OPENAT;
- case __NR_socketcall:
- return AUDITSC_SOCKETCALL;
- case __NR_execve:
- return AUDITSC_EXECVE;
- case __NR_openat2:
- return AUDITSC_OPENAT2;
- default:
- return AUDITSC_COMPAT;
- }
-}
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
deleted file mode 100644
index f9d418d1b619..000000000000
--- a/arch/s390/kernel/compat_linux.c
+++ /dev/null
@@ -1,289 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * S390 version
- * Copyright IBM Corp. 2000
- * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
- * Gerhard Tonn (ton@de.ibm.com)
- * Thomas Spatzier (tspat@de.ibm.com)
- *
- * Conversion between 31bit and 64bit native syscalls.
- *
- * Heavily inspired by the 32-bit Sparc compat code which is
- * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
- *
- */
-
-
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/file.h>
-#include <linux/signal.h>
-#include <linux/resource.h>
-#include <linux/times.h>
-#include <linux/smp.h>
-#include <linux/sem.h>
-#include <linux/msg.h>
-#include <linux/shm.h>
-#include <linux/uio.h>
-#include <linux/quota.h>
-#include <linux/poll.h>
-#include <linux/personality.h>
-#include <linux/stat.h>
-#include <linux/filter.h>
-#include <linux/highmem.h>
-#include <linux/mman.h>
-#include <linux/ipv6.h>
-#include <linux/in.h>
-#include <linux/icmpv6.h>
-#include <linux/syscalls.h>
-#include <linux/sysctl.h>
-#include <linux/binfmts.h>
-#include <linux/capability.h>
-#include <linux/compat.h>
-#include <linux/vfs.h>
-#include <linux/ptrace.h>
-#include <linux/fadvise.h>
-#include <linux/ipc.h>
-#include <linux/slab.h>
-
-#include <asm/types.h>
-#include <linux/uaccess.h>
-
-#include <net/scm.h>
-#include <net/sock.h>
-
-#include "compat_linux.h"
-
-#ifdef CONFIG_SYSVIPC
-COMPAT_SYSCALL_DEFINE5(s390_ipc, uint, call, int, first, compat_ulong_t, second,
- compat_ulong_t, third, compat_uptr_t, ptr)
-{
- if (call >> 16) /* hack for backward compatibility */
- return -EINVAL;
- return compat_ksys_ipc(call, first, second, third, ptr, third);
-}
-#endif
-
-COMPAT_SYSCALL_DEFINE3(s390_truncate64, const char __user *, path, u32, high, u32, low)
-{
- return ksys_truncate(path, (unsigned long)high << 32 | low);
-}
-
-COMPAT_SYSCALL_DEFINE3(s390_ftruncate64, unsigned int, fd, u32, high, u32, low)
-{
- return ksys_ftruncate(fd, (unsigned long)high << 32 | low);
-}
-
-COMPAT_SYSCALL_DEFINE5(s390_pread64, unsigned int, fd, char __user *, ubuf,
- compat_size_t, count, u32, high, u32, low)
-{
- if ((compat_ssize_t) count < 0)
- return -EINVAL;
- return ksys_pread64(fd, ubuf, count, (unsigned long)high << 32 | low);
-}
-
-COMPAT_SYSCALL_DEFINE5(s390_pwrite64, unsigned int, fd, const char __user *, ubuf,
- compat_size_t, count, u32, high, u32, low)
-{
- if ((compat_ssize_t) count < 0)
- return -EINVAL;
- return ksys_pwrite64(fd, ubuf, count, (unsigned long)high << 32 | low);
-}
-
-COMPAT_SYSCALL_DEFINE4(s390_readahead, int, fd, u32, high, u32, low, s32, count)
-{
- return ksys_readahead(fd, (unsigned long)high << 32 | low, count);
-}
-
-struct stat64_emu31 {
- unsigned long long st_dev;
- unsigned int __pad1;
-#define STAT64_HAS_BROKEN_ST_INO 1
- u32 __st_ino;
- unsigned int st_mode;
- unsigned int st_nlink;
- u32 st_uid;
- u32 st_gid;
- unsigned long long st_rdev;
- unsigned int __pad3;
- long st_size;
- u32 st_blksize;
- unsigned char __pad4[4];
- u32 __pad5; /* future possible st_blocks high bits */
- u32 st_blocks; /* Number 512-byte blocks allocated. */
- u32 st_atime;
- u32 __pad6;
- u32 st_mtime;
- u32 __pad7;
- u32 st_ctime;
- u32 __pad8; /* will be high 32 bits of ctime someday */
- unsigned long st_ino;
-};
-
-static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat)
-{
- struct stat64_emu31 tmp;
-
- memset(&tmp, 0, sizeof(tmp));
-
- tmp.st_dev = huge_encode_dev(stat->dev);
- tmp.st_ino = stat->ino;
- tmp.__st_ino = (u32)stat->ino;
- tmp.st_mode = stat->mode;
- tmp.st_nlink = (unsigned int)stat->nlink;
- tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
- tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
- tmp.st_rdev = huge_encode_dev(stat->rdev);
- tmp.st_size = stat->size;
- tmp.st_blksize = (u32)stat->blksize;
- tmp.st_blocks = (u32)stat->blocks;
- tmp.st_atime = (u32)stat->atime.tv_sec;
- tmp.st_mtime = (u32)stat->mtime.tv_sec;
- tmp.st_ctime = (u32)stat->ctime.tv_sec;
-
- return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
-}
-
-COMPAT_SYSCALL_DEFINE2(s390_stat64, const char __user *, filename, struct stat64_emu31 __user *, statbuf)
-{
- struct kstat stat;
- int ret = vfs_stat(filename, &stat);
- if (!ret)
- ret = cp_stat64(statbuf, &stat);
- return ret;
-}
-
-COMPAT_SYSCALL_DEFINE2(s390_lstat64, const char __user *, filename, struct stat64_emu31 __user *, statbuf)
-{
- struct kstat stat;
- int ret = vfs_lstat(filename, &stat);
- if (!ret)
- ret = cp_stat64(statbuf, &stat);
- return ret;
-}
-
-COMPAT_SYSCALL_DEFINE2(s390_fstat64, unsigned int, fd, struct stat64_emu31 __user *, statbuf)
-{
- struct kstat stat;
- int ret = vfs_fstat(fd, &stat);
- if (!ret)
- ret = cp_stat64(statbuf, &stat);
- return ret;
-}
-
-COMPAT_SYSCALL_DEFINE4(s390_fstatat64, unsigned int, dfd, const char __user *, filename,
- struct stat64_emu31 __user *, statbuf, int, flag)
-{
- struct kstat stat;
- int error;
-
- error = vfs_fstatat(dfd, filename, &stat, flag);
- if (error)
- return error;
- return cp_stat64(statbuf, &stat);
-}
-
-/*
- * Linux/i386 didn't use to be able to handle more than
- * 4 system call parameters, so these system calls used a memory
- * block for parameter passing..
- */
-
-struct mmap_arg_struct_emu31 {
- compat_ulong_t addr;
- compat_ulong_t len;
- compat_ulong_t prot;
- compat_ulong_t flags;
- compat_ulong_t fd;
- compat_ulong_t offset;
-};
-
-COMPAT_SYSCALL_DEFINE1(s390_old_mmap, struct mmap_arg_struct_emu31 __user *, arg)
-{
- struct mmap_arg_struct_emu31 a;
-
- if (copy_from_user(&a, arg, sizeof(a)))
- return -EFAULT;
- if (a.offset & ~PAGE_MASK)
- return -EINVAL;
- return ksys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
- a.offset >> PAGE_SHIFT);
-}
-
-COMPAT_SYSCALL_DEFINE1(s390_mmap2, struct mmap_arg_struct_emu31 __user *, arg)
-{
- struct mmap_arg_struct_emu31 a;
-
- if (copy_from_user(&a, arg, sizeof(a)))
- return -EFAULT;
- return ksys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
-}
-
-COMPAT_SYSCALL_DEFINE3(s390_read, unsigned int, fd, char __user *, buf, compat_size_t, count)
-{
- if ((compat_ssize_t) count < 0)
- return -EINVAL;
-
- return ksys_read(fd, buf, count);
-}
-
-COMPAT_SYSCALL_DEFINE3(s390_write, unsigned int, fd, const char __user *, buf, compat_size_t, count)
-{
- if ((compat_ssize_t) count < 0)
- return -EINVAL;
-
- return ksys_write(fd, buf, count);
-}
-
-/*
- * 31 bit emulation wrapper functions for sys_fadvise64/fadvise64_64.
- * These need to rewrite the advise values for POSIX_FADV_{DONTNEED,NOREUSE}
- * because the 31 bit values differ from the 64 bit values.
- */
-
-COMPAT_SYSCALL_DEFINE5(s390_fadvise64, int, fd, u32, high, u32, low, compat_size_t, len, int, advise)
-{
- if (advise == 4)
- advise = POSIX_FADV_DONTNEED;
- else if (advise == 5)
- advise = POSIX_FADV_NOREUSE;
- return ksys_fadvise64_64(fd, (unsigned long)high << 32 | low, len,
- advise);
-}
-
-struct fadvise64_64_args {
- int fd;
- long long offset;
- long long len;
- int advice;
-};
-
-COMPAT_SYSCALL_DEFINE1(s390_fadvise64_64, struct fadvise64_64_args __user *, args)
-{
- struct fadvise64_64_args a;
-
- if ( copy_from_user(&a, args, sizeof(a)) )
- return -EFAULT;
- if (a.advice == 4)
- a.advice = POSIX_FADV_DONTNEED;
- else if (a.advice == 5)
- a.advice = POSIX_FADV_NOREUSE;
- return ksys_fadvise64_64(a.fd, a.offset, a.len, a.advice);
-}
-
-COMPAT_SYSCALL_DEFINE6(s390_sync_file_range, int, fd, u32, offhigh, u32, offlow,
- u32, nhigh, u32, nlow, unsigned int, flags)
-{
- return ksys_sync_file_range(fd, ((loff_t)offhigh << 32) + offlow,
- ((u64)nhigh << 32) + nlow, flags);
-}
-
-COMPAT_SYSCALL_DEFINE6(s390_fallocate, int, fd, int, mode, u32, offhigh, u32, offlow,
- u32, lenhigh, u32, lenlow)
-{
- return ksys_fallocate(fd, mode, ((loff_t)offhigh << 32) + offlow,
- ((u64)lenhigh << 32) + lenlow);
-}
diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h
deleted file mode 100644
index ef23739b277c..000000000000
--- a/arch/s390/kernel/compat_linux.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_S390X_S390_H
-#define _ASM_S390X_S390_H
-
-#include <linux/compat.h>
-#include <linux/socket.h>
-#include <linux/syscalls.h>
-#include <asm/ptrace.h>
-
-/*
- * Macro that masks the high order bit of a 32 bit pointer and
- * converts it to a 64 bit pointer.
- */
-#define A(__x) ((unsigned long)((__x) & 0x7FFFFFFFUL))
-#define AA(__x) ((unsigned long)(__x))
-
-/* Now 32bit compatibility types */
-struct ipc_kludge_32 {
- __u32 msgp; /* pointer */
- __s32 msgtyp;
-};
-
-/* asm/sigcontext.h */
-typedef union {
- __u64 d;
- __u32 f;
-} freg_t32;
-
-typedef struct {
- unsigned int fpc;
- unsigned int pad;
- freg_t32 fprs[__NUM_FPRS];
-} _s390_fp_regs32;
-
-typedef struct {
- psw_t32 psw;
- __u32 gprs[__NUM_GPRS];
- __u32 acrs[__NUM_ACRS];
-} _s390_regs_common32;
-
-typedef struct {
- _s390_regs_common32 regs;
- _s390_fp_regs32 fpregs;
-} _sigregs32;
-
-typedef struct {
- __u32 gprs_high[__NUM_GPRS];
- __u64 vxrs_low[__NUM_VXRS_LOW];
- __vector128 vxrs_high[__NUM_VXRS_HIGH];
- __u8 __reserved[128];
-} _sigregs_ext32;
-
-#define _SIGCONTEXT_NSIG32 64
-#define _SIGCONTEXT_NSIG_BPW32 32
-#define __SIGNAL_FRAMESIZE32 96
-#define _SIGMASK_COPY_SIZE32 (sizeof(u32) * 2)
-
-struct sigcontext32 {
- __u32 oldmask[_COMPAT_NSIG_WORDS];
- __u32 sregs; /* pointer */
-};
-
-/* asm/signal.h */
-
-/* asm/ucontext.h */
-struct ucontext32 {
- __u32 uc_flags;
- __u32 uc_link; /* pointer */
- compat_stack_t uc_stack;
- _sigregs32 uc_mcontext;
- compat_sigset_t uc_sigmask;
- /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */
- unsigned char __unused[128 - sizeof(compat_sigset_t)];
- _sigregs_ext32 uc_mcontext_ext;
-};
-
-struct stat64_emu31;
-struct mmap_arg_struct_emu31;
-struct fadvise64_64_args;
-
-long compat_sys_s390_truncate64(const char __user *path, u32 high, u32 low);
-long compat_sys_s390_ftruncate64(unsigned int fd, u32 high, u32 low);
-long compat_sys_s390_pread64(unsigned int fd, char __user *ubuf, compat_size_t count, u32 high, u32 low);
-long compat_sys_s390_pwrite64(unsigned int fd, const char __user *ubuf, compat_size_t count, u32 high, u32 low);
-long compat_sys_s390_readahead(int fd, u32 high, u32 low, s32 count);
-long compat_sys_s390_stat64(const char __user *filename, struct stat64_emu31 __user *statbuf);
-long compat_sys_s390_lstat64(const char __user *filename, struct stat64_emu31 __user *statbuf);
-long compat_sys_s390_fstat64(unsigned int fd, struct stat64_emu31 __user *statbuf);
-long compat_sys_s390_fstatat64(unsigned int dfd, const char __user *filename, struct stat64_emu31 __user *statbuf, int flag);
-long compat_sys_s390_old_mmap(struct mmap_arg_struct_emu31 __user *arg);
-long compat_sys_s390_mmap2(struct mmap_arg_struct_emu31 __user *arg);
-long compat_sys_s390_read(unsigned int fd, char __user *buf, compat_size_t count);
-long compat_sys_s390_write(unsigned int fd, const char __user *buf, compat_size_t count);
-long compat_sys_s390_fadvise64(int fd, u32 high, u32 low, compat_size_t len, int advise);
-long compat_sys_s390_fadvise64_64(struct fadvise64_64_args __user *args);
-long compat_sys_s390_sync_file_range(int fd, u32 offhigh, u32 offlow, u32 nhigh, u32 nlow, unsigned int flags);
-long compat_sys_s390_fallocate(int fd, int mode, u32 offhigh, u32 offlow, u32 lenhigh, u32 lenlow);
-long compat_sys_sigreturn(void);
-long compat_sys_rt_sigreturn(void);
-
-#endif /* _ASM_S390X_S390_H */
diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h
deleted file mode 100644
index 3c400fc7e987..000000000000
--- a/arch/s390/kernel/compat_ptrace.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _PTRACE32_H
-#define _PTRACE32_H
-
-#include <asm/ptrace.h> /* needed for NUM_CR_WORDS */
-#include "compat_linux.h" /* needed for psw_compat_t */
-
-struct compat_per_struct_kernel {
- __u32 cr9; /* PER control bits */
- __u32 cr10; /* PER starting address */
- __u32 cr11; /* PER ending address */
- __u32 bits; /* Obsolete software bits */
- __u32 starting_addr; /* User specified start address */
- __u32 ending_addr; /* User specified end address */
- __u16 perc_atmid; /* PER trap ATMID */
- __u32 address; /* PER trap instruction address */
- __u8 access_id; /* PER trap access identification */
-};
-
-struct compat_user_regs_struct
-{
- psw_compat_t psw;
- u32 gprs[NUM_GPRS];
- u32 acrs[NUM_ACRS];
- u32 orig_gpr2;
- /* nb: there's a 4-byte hole here */
- s390_fp_regs fp_regs;
- /*
- * These per registers are in here so that gdb can modify them
- * itself as there is no "official" ptrace interface for hardware
- * watchpoints. This is the way intel does it.
- */
- struct compat_per_struct_kernel per_info;
- u32 ieee_instruction_pointer; /* obsolete, always 0 */
-};
-
-struct compat_user {
- /* We start with the registers, to mimic the way that "memory"
- is returned from the ptrace(3,...) function. */
- struct compat_user_regs_struct regs;
- /* The rest of this junk is to help gdb figure out what goes where */
- u32 u_tsize; /* Text segment size (pages). */
- u32 u_dsize; /* Data segment size (pages). */
- u32 u_ssize; /* Stack segment size (pages). */
- u32 start_code; /* Starting virtual address of text. */
- u32 start_stack; /* Starting virtual address of stack area.
- This is actually the bottom of the stack,
- the top of the stack is always found in the
- esp register. */
- s32 signal; /* Signal that caused the core dump. */
- u32 u_ar0; /* Used by gdb to help find the values for */
- /* the registers. */
- u32 magic; /* To uniquely identify a core file */
- char u_comm[32]; /* User command that was responsible */
-};
-
-typedef struct
-{
- __u32 len;
- __u32 kernel_addr;
- __u32 process_addr;
-} compat_ptrace_area;
-
-#endif /* _PTRACE32_H */
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
deleted file mode 100644
index 5a86b9d1da71..000000000000
--- a/arch/s390/kernel/compat_signal.c
+++ /dev/null
@@ -1,420 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright IBM Corp. 2000, 2006
- * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- * Gerhard Tonn (ton@de.ibm.com)
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
- */
-
-#include <linux/compat.h>
-#include <linux/sched.h>
-#include <linux/sched/task_stack.h>
-#include <linux/mm.h>
-#include <linux/smp.h>
-#include <linux/kernel.h>
-#include <linux/signal.h>
-#include <linux/errno.h>
-#include <linux/wait.h>
-#include <linux/ptrace.h>
-#include <linux/unistd.h>
-#include <linux/stddef.h>
-#include <linux/tty.h>
-#include <linux/personality.h>
-#include <linux/binfmts.h>
-#include <asm/vdso-symbols.h>
-#include <asm/access-regs.h>
-#include <asm/ucontext.h>
-#include <linux/uaccess.h>
-#include <asm/lowcore.h>
-#include <asm/fpu.h>
-#include "compat_linux.h"
-#include "compat_ptrace.h"
-#include "entry.h"
-
-typedef struct
-{
- __u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
- struct sigcontext32 sc;
- _sigregs32 sregs;
- int signo;
- _sigregs_ext32 sregs_ext;
- __u16 svc_insn; /* Offset of svc_insn is NOT fixed! */
-} sigframe32;
-
-typedef struct
-{
- __u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
- __u16 svc_insn;
- compat_siginfo_t info;
- struct ucontext32 uc;
-} rt_sigframe32;
-
-/* Store registers needed to create the signal frame */
-static void store_sigregs(void)
-{
- save_access_regs(current->thread.acrs);
- save_user_fpu_regs();
-}
-
-/* Load registers after signal return */
-static void load_sigregs(void)
-{
- restore_access_regs(current->thread.acrs);
-}
-
-static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
-{
- _sigregs32 user_sregs;
- int i;
-
- user_sregs.regs.psw.mask = (__u32)(regs->psw.mask >> 32);
- user_sregs.regs.psw.mask &= PSW32_MASK_USER | PSW32_MASK_RI;
- user_sregs.regs.psw.mask |= PSW32_USER_BITS;
- user_sregs.regs.psw.addr = (__u32) regs->psw.addr |
- (__u32)(regs->psw.mask & PSW_MASK_BA);
- for (i = 0; i < NUM_GPRS; i++)
- user_sregs.regs.gprs[i] = (__u32) regs->gprs[i];
- memcpy(&user_sregs.regs.acrs, current->thread.acrs,
- sizeof(user_sregs.regs.acrs));
- fpregs_store((_s390_fp_regs *) &user_sregs.fpregs, &current->thread.ufpu);
- if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32)))
- return -EFAULT;
- return 0;
-}
-
-static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
-{
- _sigregs32 user_sregs;
- int i;
-
- /* Always make any pending restarted system call return -EINTR */
- current->restart_block.fn = do_no_restart_syscall;
-
- if (__copy_from_user(&user_sregs, &sregs->regs, sizeof(user_sregs)))
- return -EFAULT;
-
- if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW32_MASK_RI))
- return -EINVAL;
-
- /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
- regs->psw.mask = (regs->psw.mask & ~(PSW_MASK_USER | PSW_MASK_RI)) |
- (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_USER) << 32 |
- (__u64)(user_sregs.regs.psw.mask & PSW32_MASK_RI) << 32 |
- (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_AMODE);
- /* Check for invalid user address space control. */
- if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME)
- regs->psw.mask = PSW_ASC_PRIMARY |
- (regs->psw.mask & ~PSW_MASK_ASC);
- regs->psw.addr = (__u64)(user_sregs.regs.psw.addr & PSW32_ADDR_INSN);
- for (i = 0; i < NUM_GPRS; i++)
- regs->gprs[i] = (__u64) user_sregs.regs.gprs[i];
- memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
- sizeof(current->thread.acrs));
- fpregs_load((_s390_fp_regs *)&user_sregs.fpregs, &current->thread.ufpu);
-
- clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
- return 0;
-}
-
-static int save_sigregs_ext32(struct pt_regs *regs,
- _sigregs_ext32 __user *sregs_ext)
-{
- __u32 gprs_high[NUM_GPRS];
- __u64 vxrs[__NUM_VXRS_LOW];
- int i;
-
- /* Save high gprs to signal stack */
- for (i = 0; i < NUM_GPRS; i++)
- gprs_high[i] = regs->gprs[i] >> 32;
- if (__copy_to_user(&sregs_ext->gprs_high, &gprs_high,
- sizeof(sregs_ext->gprs_high)))
- return -EFAULT;
-
- /* Save vector registers to signal stack */
- if (cpu_has_vx()) {
- for (i = 0; i < __NUM_VXRS_LOW; i++)
- vxrs[i] = current->thread.ufpu.vxrs[i].low;
- if (__copy_to_user(&sregs_ext->vxrs_low, vxrs,
- sizeof(sregs_ext->vxrs_low)) ||
- __copy_to_user(&sregs_ext->vxrs_high,
- current->thread.ufpu.vxrs + __NUM_VXRS_LOW,
- sizeof(sregs_ext->vxrs_high)))
- return -EFAULT;
- }
- return 0;
-}
-
-static int restore_sigregs_ext32(struct pt_regs *regs,
- _sigregs_ext32 __user *sregs_ext)
-{
- __u32 gprs_high[NUM_GPRS];
- __u64 vxrs[__NUM_VXRS_LOW];
- int i;
-
- /* Restore high gprs from signal stack */
- if (__copy_from_user(&gprs_high, &sregs_ext->gprs_high,
- sizeof(sregs_ext->gprs_high)))
- return -EFAULT;
- for (i = 0; i < NUM_GPRS; i++)
- *(__u32 *)&regs->gprs[i] = gprs_high[i];
-
- /* Restore vector registers from signal stack */
- if (cpu_has_vx()) {
- if (__copy_from_user(vxrs, &sregs_ext->vxrs_low,
- sizeof(sregs_ext->vxrs_low)) ||
- __copy_from_user(current->thread.ufpu.vxrs + __NUM_VXRS_LOW,
- &sregs_ext->vxrs_high,
- sizeof(sregs_ext->vxrs_high)))
- return -EFAULT;
- for (i = 0; i < __NUM_VXRS_LOW; i++)
- current->thread.ufpu.vxrs[i].low = vxrs[i];
- }
- return 0;
-}
-
-COMPAT_SYSCALL_DEFINE0(sigreturn)
-{
- struct pt_regs *regs = task_pt_regs(current);
- sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15];
- sigset_t set;
-
- if (get_compat_sigset(&set, (compat_sigset_t __user *)frame->sc.oldmask))
- goto badframe;
- set_current_blocked(&set);
- save_user_fpu_regs();
- if (restore_sigregs32(regs, &frame->sregs))
- goto badframe;
- if (restore_sigregs_ext32(regs, &frame->sregs_ext))
- goto badframe;
- load_sigregs();
- return regs->gprs[2];
-badframe:
- force_sig(SIGSEGV);
- return 0;
-}
-
-COMPAT_SYSCALL_DEFINE0(rt_sigreturn)
-{
- struct pt_regs *regs = task_pt_regs(current);
- rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15];
- sigset_t set;
-
- if (get_compat_sigset(&set, &frame->uc.uc_sigmask))
- goto badframe;
- set_current_blocked(&set);
- if (compat_restore_altstack(&frame->uc.uc_stack))
- goto badframe;
- save_user_fpu_regs();
- if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
- goto badframe;
- if (restore_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext))
- goto badframe;
- load_sigregs();
- return regs->gprs[2];
-badframe:
- force_sig(SIGSEGV);
- return 0;
-}
-
-/*
- * Set up a signal frame.
- */
-
-
-/*
- * Determine which stack to use..
- */
-static inline void __user *
-get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
-{
- unsigned long sp;
-
- /* Default to using normal stack */
- sp = (unsigned long) A(regs->gprs[15]);
-
- /* Overflow on alternate signal stack gives SIGSEGV. */
- if (on_sig_stack(sp) && !on_sig_stack((sp - frame_size) & -8UL))
- return (void __user *) -1UL;
-
- /* This is the X/Open sanctioned signal stack switching. */
- if (ka->sa.sa_flags & SA_ONSTACK) {
- if (! sas_ss_flags(sp))
- sp = current->sas_ss_sp + current->sas_ss_size;
- }
-
- return (void __user *)((sp - frame_size) & -8ul);
-}
-
-static int setup_frame32(struct ksignal *ksig, sigset_t *set,
- struct pt_regs *regs)
-{
- int sig = ksig->sig;
- sigframe32 __user *frame;
- unsigned long restorer;
- size_t frame_size;
-
- /*
- * gprs_high are always present for 31-bit compat tasks.
- * The space for vector registers is only allocated if
- * the machine supports it
- */
- frame_size = sizeof(*frame) - sizeof(frame->sregs_ext.__reserved);
- if (!cpu_has_vx())
- frame_size -= sizeof(frame->sregs_ext.vxrs_low) +
- sizeof(frame->sregs_ext.vxrs_high);
- frame = get_sigframe(&ksig->ka, regs, frame_size);
- if (frame == (void __user *) -1UL)
- return -EFAULT;
-
- /* Set up backchain. */
- if (__put_user(regs->gprs[15], (unsigned int __user *) frame))
- return -EFAULT;
-
- /* Create struct sigcontext32 on the signal stack */
- if (put_compat_sigset((compat_sigset_t __user *)frame->sc.oldmask,
- set, sizeof(compat_sigset_t)))
- return -EFAULT;
- if (__put_user(ptr_to_compat(&frame->sregs), &frame->sc.sregs))
- return -EFAULT;
-
- /* Store registers needed to create the signal frame */
- store_sigregs();
-
- /* Create _sigregs32 on the signal stack */
- if (save_sigregs32(regs, &frame->sregs))
- return -EFAULT;
-
- /* Place signal number on stack to allow backtrace from handler. */
- if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo))
- return -EFAULT;
-
- /* Create _sigregs_ext32 on the signal stack */
- if (save_sigregs_ext32(regs, &frame->sregs_ext))
- return -EFAULT;
-
- /* Set up to return from userspace. If provided, use a stub
- already in userspace. */
- if (ksig->ka.sa.sa_flags & SA_RESTORER) {
- restorer = (unsigned long __force)
- ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE;
- } else {
- restorer = VDSO32_SYMBOL(current, sigreturn);
- }
-
- /* Set up registers for signal handler */
- regs->gprs[14] = restorer;
- regs->gprs[15] = (__force __u64) frame;
- /* Force 31 bit amode and default user address space control. */
- regs->psw.mask = PSW_MASK_BA |
- (PSW_USER_BITS & PSW_MASK_ASC) |
- (regs->psw.mask & ~PSW_MASK_ASC);
- regs->psw.addr = (__force __u64) ksig->ka.sa.sa_handler;
-
- regs->gprs[2] = sig;
- regs->gprs[3] = (__force __u64) &frame->sc;
-
- /* We forgot to include these in the sigcontext.
- To avoid breaking binary compatibility, they are passed as args. */
- if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
- sig == SIGTRAP || sig == SIGFPE) {
- /* set extra registers only for synchronous signals */
- regs->gprs[4] = regs->int_code & 127;
- regs->gprs[5] = regs->int_parm_long;
- regs->gprs[6] = current->thread.last_break;
- }
-
- return 0;
-}
-
-static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set,
- struct pt_regs *regs)
-{
- rt_sigframe32 __user *frame;
- unsigned long restorer;
- size_t frame_size;
- u32 uc_flags;
-
- frame_size = sizeof(*frame) -
- sizeof(frame->uc.uc_mcontext_ext.__reserved);
- /*
- * gprs_high are always present for 31-bit compat tasks.
- * The space for vector registers is only allocated if
- * the machine supports it
- */
- uc_flags = UC_GPRS_HIGH;
- if (cpu_has_vx()) {
- uc_flags |= UC_VXRS;
- } else {
- frame_size -= sizeof(frame->uc.uc_mcontext_ext.vxrs_low) +
- sizeof(frame->uc.uc_mcontext_ext.vxrs_high);
- }
- frame = get_sigframe(&ksig->ka, regs, frame_size);
- if (frame == (void __user *) -1UL)
- return -EFAULT;
-
- /* Set up backchain. */
- if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame))
- return -EFAULT;
-
- /* Set up to return from userspace. If provided, use a stub
- already in userspace. */
- if (ksig->ka.sa.sa_flags & SA_RESTORER) {
- restorer = (unsigned long __force)
- ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE;
- } else {
- restorer = VDSO32_SYMBOL(current, rt_sigreturn);
- }
-
- /* Create siginfo on the signal stack */
- if (copy_siginfo_to_user32(&frame->info, &ksig->info))
- return -EFAULT;
-
- /* Store registers needed to create the signal frame */
- store_sigregs();
-
- /* Create ucontext on the signal stack. */
- if (__put_user(uc_flags, &frame->uc.uc_flags) ||
- __put_user(0, &frame->uc.uc_link) ||
- __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]) ||
- save_sigregs32(regs, &frame->uc.uc_mcontext) ||
- put_compat_sigset(&frame->uc.uc_sigmask, set, sizeof(compat_sigset_t)) ||
- save_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext))
- return -EFAULT;
-
- /* Set up registers for signal handler */
- regs->gprs[14] = restorer;
- regs->gprs[15] = (__force __u64) frame;
- /* Force 31 bit amode and default user address space control. */
- regs->psw.mask = PSW_MASK_BA |
- (PSW_USER_BITS & PSW_MASK_ASC) |
- (regs->psw.mask & ~PSW_MASK_ASC);
- regs->psw.addr = (__u64 __force) ksig->ka.sa.sa_handler;
-
- regs->gprs[2] = ksig->sig;
- regs->gprs[3] = (__force __u64) &frame->info;
- regs->gprs[4] = (__force __u64) &frame->uc;
- regs->gprs[5] = current->thread.last_break;
- return 0;
-}
-
-/*
- * OK, we're invoking a handler
- */
-
-void handle_signal32(struct ksignal *ksig, sigset_t *oldset,
- struct pt_regs *regs)
-{
- int ret;
-
- /* Set up the stack frame */
- if (ksig->ka.sa.sa_flags & SA_SIGINFO)
- ret = setup_rt_frame32(ksig, oldset, regs);
- else
- ret = setup_frame32(ksig, oldset, regs);
-
- signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLE_STEP));
-}
-
diff --git a/arch/s390/kernel/cpacf.c b/arch/s390/kernel/cpacf.c
index 3bebc47beeab..9d85b4bc7036 100644
--- a/arch/s390/kernel/cpacf.c
+++ b/arch/s390/kernel/cpacf.c
@@ -3,8 +3,7 @@
* Copyright IBM Corp. 2024
*/
-#define KMSG_COMPONENT "cpacf"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "cpacf: " fmt
#include <linux/cpu.h>
#include <linux/device.h>
diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c
index 2f4174b961de..ab611764642a 100644
--- a/arch/s390/kernel/cpcmd.c
+++ b/arch/s390/kernel/cpcmd.c
@@ -6,8 +6,7 @@
* Christian Borntraeger (cborntra@de.ibm.com),
*/
-#define KMSG_COMPONENT "cpcmd"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "cpcmd: " fmt
#include <linux/kernel.h>
#include <linux/export.h>
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
index 6a26f202441d..71cdb6845dd7 100644
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -10,8 +10,7 @@
* Bugreports to: <Linux390@de.ibm.com>
*/
-#define KMSG_COMPONENT "s390dbf"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "s390dbf: " fmt
#include <linux/stddef.h>
#include <linux/kernel.h>
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
index 63a1d4226ff8..1cec93895b3a 100644
--- a/arch/s390/kernel/dis.c
+++ b/arch/s390/kernel/dis.c
@@ -503,24 +503,27 @@ static int copy_from_regs(struct pt_regs *regs, void *dst, void *src, int len)
void show_code(struct pt_regs *regs)
{
char *mode = user_mode(regs) ? "User" : "Krnl";
+ unsigned long addr, pswaddr;
unsigned char code[64];
char buffer[128], *ptr;
- unsigned long addr;
int start, end, opsize, hops, i;
+ pswaddr = regs->psw.addr;
+ if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
+ pswaddr = __forward_psw(regs->psw, regs->int_code >> 16);
/* Get a snapshot of the 64 bytes surrounding the fault address. */
- for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) {
- addr = regs->psw.addr - 34 + start;
+ for (start = 32; start && pswaddr >= 34 - start; start -= 2) {
+ addr = pswaddr - 34 + start;
if (copy_from_regs(regs, code + start - 2, (void *)addr, 2))
break;
}
for (end = 32; end < 64; end += 2) {
- addr = regs->psw.addr + end - 32;
+ addr = pswaddr + end - 32;
if (copy_from_regs(regs, code + end, (void *)addr, 2))
break;
}
/* Code snapshot usable ? */
- if ((regs->psw.addr & 1) || start >= end) {
+ if ((pswaddr & 1) || start >= end) {
printk("%s Code: Bad PSW.\n", mode);
return;
}
@@ -543,12 +546,12 @@ void show_code(struct pt_regs *regs)
while (start < end && hops < 8) {
opsize = insn_length(code[start]);
if (start + opsize == 32)
- *ptr++ = '#';
+ *ptr++ = '*';
else if (start == 32)
*ptr++ = '>';
else
*ptr++ = ' ';
- addr = regs->psw.addr + start - 32;
+ addr = pswaddr + start - 32;
ptr += sprintf(ptr, "%px: ", (void *)addr);
if (start + opsize >= end)
break;
diff --git a/arch/s390/kernel/dumpstack.c b/arch/s390/kernel/dumpstack.c
index dd410962ecbe..f9d52e05e01e 100644
--- a/arch/s390/kernel/dumpstack.c
+++ b/arch/s390/kernel/dumpstack.c
@@ -155,12 +155,16 @@ static void show_last_breaking_event(struct pt_regs *regs)
void show_registers(struct pt_regs *regs)
{
struct psw_bits *psw = &psw_bits(regs->psw);
+ unsigned long pswaddr;
char *mode;
+ pswaddr = regs->psw.addr;
+ if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
+ pswaddr = __forward_psw(regs->psw, regs->int_code >> 16);
mode = user_mode(regs) ? "User" : "Krnl";
- printk("%s PSW : %px %px", mode, (void *)regs->psw.mask, (void *)regs->psw.addr);
+ printk("%s PSW : %px %px", mode, (void *)regs->psw.mask, (void *)pswaddr);
if (!user_mode(regs))
- pr_cont(" (%pSR)", (void *)regs->psw.addr);
+ pr_cont(" (%pSR)", (void *)pswaddr);
pr_cont("\n");
printk(" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x "
"P:%x AS:%x CC:%x PM:%x", psw->per, psw->dat, psw->io, psw->ext,
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 544e5403dd91..b27239c03d79 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -4,8 +4,7 @@
* Author(s): Hongjie Yang <hongjie@us.ibm.com>,
*/
-#define KMSG_COMPONENT "setup"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "setup: " fmt
#include <linux/sched/debug.h>
#include <linux/cpufeature.h>
@@ -120,21 +119,21 @@ static noinline __init void setup_arch_string(void)
EBCASC(mach->type, sizeof(mach->type));
EBCASC(mach->model, sizeof(mach->model));
EBCASC(mach->model_capacity, sizeof(mach->model_capacity));
- sprintf(mstr, "%-16.16s %-4.4s %-16.16s %-16.16s",
- mach->manufacturer, mach->type,
- mach->model, mach->model_capacity);
+ scnprintf(mstr, sizeof(mstr), "%-16.16s %-4.4s %-16.16s %-16.16s",
+ mach->manufacturer, mach->type,
+ mach->model, mach->model_capacity);
strim_all(mstr);
if (stsi(vm, 3, 2, 2) == 0 && vm->count) {
EBCASC(vm->vm[0].cpi, sizeof(vm->vm[0].cpi));
- sprintf(hvstr, "%-16.16s", vm->vm[0].cpi);
+ scnprintf(hvstr, sizeof(hvstr), "%-16.16s", vm->vm[0].cpi);
strim_all(hvstr);
} else {
- sprintf(hvstr, "%s",
- machine_is_lpar() ? "LPAR" :
- machine_is_vm() ? "z/VM" :
- machine_is_kvm() ? "KVM" : "unknown");
+ scnprintf(hvstr, sizeof(hvstr), "%s",
+ machine_is_lpar() ? "LPAR" :
+ machine_is_vm() ? "z/VM" :
+ machine_is_kvm() ? "KVM" : "unknown");
}
- sprintf(arch_hw_string, "HW: %s (%s)", mstr, hvstr);
+ scnprintf(arch_hw_string, sizeof(arch_hw_string), "HW: %s (%s)", mstr, hvstr);
dump_stack_set_arch_desc("%s (%s)", mstr, hvstr);
}
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
index 75b0fbb236d0..c360087807d8 100644
--- a/arch/s390/kernel/entry.S
+++ b/arch/s390/kernel/entry.S
@@ -162,9 +162,13 @@ SYM_FUNC_START(__switch_to_asm)
stg %r3,__LC_CURRENT(%r13) # store task struct of next
stg %r15,__LC_KERNEL_STACK(%r13) # store end of kernel stack
lg %r15,__THREAD_ksp(%r1,%r3) # load kernel stack of next
- aghi %r3,__TASK_pid
- mvc __LC_CURRENT_PID(4,%r13),0(%r3) # store pid of next
+ lay %r4,__TASK_pid(%r3)
+ mvc __LC_CURRENT_PID(4,%r13),0(%r4) # store pid of next
ALTERNATIVE "nop", "lpp _LPP_OFFSET(%r13)", ALT_FACILITY(40)
+#ifdef CONFIG_STACKPROTECTOR
+ lg %r3,__TASK_stack_canary(%r3)
+ stg %r3,__LC_STACK_CANARY(%r13)
+#endif
lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
BR_EX %r14
SYM_FUNC_END(__switch_to_asm)
@@ -606,20 +610,3 @@ SYM_DATA_START_LOCAL(daton_psw)
.quad PSW_KERNEL_BITS
.quad .Ldaton
SYM_DATA_END(daton_psw)
-
- .section .rodata, "a"
- .balign 8
-#define SYSCALL(esame,emu) .quad __s390x_ ## esame
-SYM_DATA_START(sys_call_table)
-#include <asm/syscall_table.h>
-SYM_DATA_END(sys_call_table)
-#undef SYSCALL
-
-#ifdef CONFIG_COMPAT
-
-#define SYSCALL(esame,emu) .quad __s390_ ## emu
-SYM_DATA_START(sys_call_table_emu)
-#include <asm/syscall_table.h>
-SYM_DATA_END(sys_call_table_emu)
-#undef SYSCALL
-#endif
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head.S
index 7edb9ded199c..7edb9ded199c 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head.S
diff --git a/arch/s390/kernel/hiperdispatch.c b/arch/s390/kernel/hiperdispatch.c
index 2507bc3f7757..217206522266 100644
--- a/arch/s390/kernel/hiperdispatch.c
+++ b/arch/s390/kernel/hiperdispatch.c
@@ -3,8 +3,7 @@
* Copyright IBM Corp. 2024
*/
-#define KMSG_COMPONENT "hd"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "hd: " fmt
/*
* Hiperdispatch:
@@ -65,7 +64,7 @@
#define HD_DELAY_FACTOR (4)
#define HD_DELAY_INTERVAL (HZ / 4)
-#define HD_STEAL_THRESHOLD 30
+#define HD_STEAL_THRESHOLD 10
#define HD_STEAL_AVG_WEIGHT 16
static cpumask_t hd_vl_coremask; /* Mask containing all vertical low COREs */
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
index 91e207b50394..9d1f8a50f5a4 100644
--- a/arch/s390/kernel/module.c
+++ b/arch/s390/kernel/module.c
@@ -22,12 +22,14 @@
#include <linux/bug.h>
#include <linux/memory.h>
#include <linux/execmem.h>
+#include <asm/arch-stackprotector.h>
#include <asm/alternative.h>
#include <asm/nospec-branch.h>
#include <asm/facility.h>
#include <asm/ftrace.lds.h>
#include <asm/set_memory.h>
#include <asm/setup.h>
+#include <asm/asm-offsets.h>
#if 0
#define DEBUGP printk
@@ -495,9 +497,7 @@ int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *s;
char *secstrings, *secname;
void *aseg;
-#ifdef CONFIG_FUNCTION_TRACER
- int ret;
-#endif
+ int rc = 0;
if (IS_ENABLED(CONFIG_EXPOLINE) &&
!nospec_disable && me->arch.plt_size) {
@@ -527,14 +527,21 @@ int module_finalize(const Elf_Ehdr *hdr,
(str_has_prefix(secname, ".s390_return")))
nospec_revert(aseg, aseg + s->sh_size);
+ if (IS_ENABLED(CONFIG_STACKPROTECTOR) &&
+ (str_has_prefix(secname, "__stack_protector_loc"))) {
+ rc = stack_protector_apply(aseg, aseg + s->sh_size);
+ if (rc)
+ break;
+ }
+
#ifdef CONFIG_FUNCTION_TRACER
if (!strcmp(FTRACE_CALLSITE_SECTION, secname)) {
- ret = module_alloc_ftrace_hotpatch_trampolines(me, s);
- if (ret < 0)
- return ret;
+ rc = module_alloc_ftrace_hotpatch_trampolines(me, s);
+ if (rc)
+ break;
}
#endif /* CONFIG_FUNCTION_TRACER */
}
- return 0;
+ return rc;
}
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c
index 11f33243a23f..a55abbf65333 100644
--- a/arch/s390/kernel/nmi.c
+++ b/arch/s390/kernel/nmi.c
@@ -184,7 +184,7 @@ static notrace void nmi_print_info(void)
sclp_emergency_printk(message);
}
-static notrace void s390_handle_damage(void)
+static notrace void __noreturn s390_handle_damage(void)
{
struct lowcore *lc = get_lowcore();
union ctlreg0 cr0, cr0_new;
@@ -214,7 +214,6 @@ static notrace void s390_handle_damage(void)
lc->mcck_new_psw = psw_save;
local_ctl_load(0, &cr0.reg);
disabled_wait();
- while (1);
}
NOKPROBE_SYMBOL(s390_handle_damage);
diff --git a/arch/s390/kernel/os_info.c b/arch/s390/kernel/os_info.c
index c2a468986212..94fa44776d0c 100644
--- a/arch/s390/kernel/os_info.c
+++ b/arch/s390/kernel/os_info.c
@@ -6,8 +6,7 @@
* Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "os_info"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "os_info: " fmt
#include <linux/crash_dump.h>
#include <linux/kernel.h>
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
index 04457d88e589..408ab93112bf 100644
--- a/arch/s390/kernel/perf_cpum_cf.c
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -6,8 +6,7 @@
* Author(s): Hendrik Brueckner <brueckner@linux.ibm.com>
* Thomas Richter <tmricht@linux.ibm.com>
*/
-#define KMSG_COMPONENT "cpum_cf"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "cpum_cf: " fmt
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
@@ -1206,7 +1205,7 @@ static int __init cpumf_pmu_init(void)
}
/* Setup s390dbf facility */
- cf_dbg = debug_register(KMSG_COMPONENT, 2, 1, 128);
+ cf_dbg = debug_register("cpum_cf", 2, 1, 128);
if (!cf_dbg) {
pr_err("Registration of s390dbf(cpum_cf) failed\n");
rc = -ENOMEM;
@@ -1689,7 +1688,6 @@ static const struct file_operations cfset_fops = {
.open = cfset_open,
.release = cfset_release,
.unlocked_ioctl = cfset_ioctl,
- .compat_ioctl = cfset_ioctl,
};
static struct miscdevice cfset_dev = {
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index f432869f8921..459af23a47a5 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -5,8 +5,7 @@
* Copyright IBM Corp. 2013, 2018
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "cpum_sf"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "cpum_sf: " fmt
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
@@ -1093,7 +1092,7 @@ static void perf_event_count_update(struct perf_event *event, u64 count)
* combined-sampling data entry consists of a basic- and a diagnostic-sampling
* data entry. The sampling function is determined by the flags in the perf
* event hardware structure. The function always works with a combined-sampling
- * data entry but ignores the the diagnostic portion if it is not available.
+ * data entry but ignores the diagnostic portion if it is not available.
*
* Note that the implementation focuses on basic-sampling data entries and, if
* such an entry is not valid, the entire combined-sampling data entry is
@@ -2070,7 +2069,7 @@ static int __init init_cpum_sampling_pmu(void)
CPUMF_EVENT_PTR(SF, SF_CYCLES_BASIC_DIAG);
}
- sfdbg = debug_register(KMSG_COMPONENT, 2, 1, 80);
+ sfdbg = debug_register("cpum_sf", 2, 1, 80);
if (!sfdbg) {
pr_err("Registering for s390dbf failed\n");
return -ENOMEM;
diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c
index 91b8716c883a..606750bae508 100644
--- a/arch/s390/kernel/perf_event.c
+++ b/arch/s390/kernel/perf_event.c
@@ -5,8 +5,7 @@
* Copyright IBM Corp. 2012, 2013
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "perf"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "perf: " fmt
#include <linux/kernel.h>
#include <linux/perf_event.h>
@@ -15,7 +14,6 @@
#include <linux/seq_file.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
-#include <linux/compat.h>
#include <linux/sysfs.h>
#include <asm/stacktrace.h>
#include <asm/irq.h>
diff --git a/arch/s390/kernel/perf_pai.c b/arch/s390/kernel/perf_pai.c
new file mode 100644
index 000000000000..810f5b6c5e01
--- /dev/null
+++ b/arch/s390/kernel/perf_pai.c
@@ -0,0 +1,1230 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Performance event support - Processor Activity Instrumentation Facility
+ *
+ * Copyright IBM Corp. 2026
+ * Author(s): Thomas Richter <tmricht@linux.ibm.com>
+ */
+#define pr_fmt(fmt) "pai: " fmt
+
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/percpu.h>
+#include <linux/notifier.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/perf_event.h>
+#include <asm/ctlreg.h>
+#include <asm/pai.h>
+#include <asm/debug.h>
+
+static debug_info_t *paidbg;
+
+DEFINE_STATIC_KEY_FALSE(pai_key);
+
+enum {
+ PAI_PMU_CRYPTO, /* Index of PMU pai_crypto */
+ PAI_PMU_EXT, /* Index of PMU pai_ext */
+ PAI_PMU_MAX /* # of PAI PMUs */
+};
+
+enum {
+ PAIE1_CB_SZ = 0x200, /* Size of PAIE1 control block */
+ PAIE1_CTRBLOCK_SZ = 0x400 /* Size of PAIE1 counter blocks */
+};
+
+struct pai_userdata {
+ u16 num;
+ u64 value;
+} __packed;
+
+/* Create the PAI extension 1 control block area.
+ * The PAI extension control block 1 is pointed to by lowcore
+ * address 0x1508 for each CPU. This control block is 512 bytes in size
+ * and requires a 512 byte boundary alignment.
+ */
+struct paiext_cb { /* PAI extension 1 control block */
+ u64 header; /* Not used */
+ u64 reserved1;
+ u64 acc; /* Addr to analytics counter control block */
+ u8 reserved2[PAIE1_CTRBLOCK_SZ - 3 * sizeof(u64)];
+} __packed;
+
+struct pai_map {
+ unsigned long *area; /* Area for CPU to store counters */
+ struct pai_userdata *save; /* Page to store no-zero counters */
+ unsigned int active_events; /* # of PAI crypto users */
+ refcount_t refcnt; /* Reference count mapped buffers */
+ struct perf_event *event; /* Perf event for sampling */
+ struct list_head syswide_list; /* List system-wide sampling events */
+ struct paiext_cb *paiext_cb; /* PAI extension control block area */
+ bool fullpage; /* True: counter area is a full page */
+};
+
+struct pai_mapptr {
+ struct pai_map *mapptr;
+};
+
+static struct pai_root { /* Anchor to per CPU data */
+ refcount_t refcnt; /* Overall active events */
+ struct pai_mapptr __percpu *mapptr;
+} pai_root[PAI_PMU_MAX];
+
+/* This table defines the different parameters of the PAI PMUs. During
+ * initialization the machine dependent values are extracted and saved.
+ * However most of the values are static and do not change.
+ * There is one table entry per PAI PMU.
+ */
+struct pai_pmu { /* Define PAI PMU characteristics */
+ const char *pmuname; /* Name of PMU */
+ const int facility_nr; /* Facility number to check for support */
+ unsigned int num_avail; /* # Counters defined by hardware */
+ unsigned int num_named; /* # Counters known by name */
+ unsigned long base; /* Counter set base number */
+ unsigned long kernel_offset; /* Offset to kernel part in counter page */
+ unsigned long area_size; /* Size of counter area */
+ const char * const *names; /* List of counter names */
+ struct pmu *pmu; /* Ptr to supporting PMU */
+ int (*init)(struct pai_pmu *p); /* PMU support init function */
+ void (*exit)(struct pai_pmu *p); /* PMU support exit function */
+ struct attribute_group *event_group; /* Ptr to attribute of events */
+};
+
+static struct pai_pmu pai_pmu[]; /* Forward declaration */
+
+/* Free per CPU data when the last event is removed. */
+static void pai_root_free(int idx)
+{
+ if (refcount_dec_and_test(&pai_root[idx].refcnt)) {
+ free_percpu(pai_root[idx].mapptr);
+ pai_root[idx].mapptr = NULL;
+ }
+ debug_sprintf_event(paidbg, 5, "%s root[%d].refcount %d\n", __func__,
+ idx, refcount_read(&pai_root[idx].refcnt));
+}
+
+/*
+ * On initialization of first event also allocate per CPU data dynamically.
+ * Start with an array of pointers, the array size is the maximum number of
+ * CPUs possible, which might be larger than the number of CPUs currently
+ * online.
+ */
+static int pai_root_alloc(int idx)
+{
+ if (!refcount_inc_not_zero(&pai_root[idx].refcnt)) {
+ /* The memory is already zeroed. */
+ pai_root[idx].mapptr = alloc_percpu(struct pai_mapptr);
+ if (!pai_root[idx].mapptr)
+ return -ENOMEM;
+ refcount_set(&pai_root[idx].refcnt, 1);
+ }
+ return 0;
+}
+
+/* Release the PMU if event is the last perf event */
+static DEFINE_MUTEX(pai_reserve_mutex);
+
+/* Free all memory allocated for event counting/sampling setup */
+static void pai_free(struct pai_mapptr *mp)
+{
+ if (mp->mapptr->fullpage)
+ free_page((unsigned long)mp->mapptr->area);
+ else
+ kfree(mp->mapptr->area);
+ kfree(mp->mapptr->paiext_cb);
+ kvfree(mp->mapptr->save);
+ kfree(mp->mapptr);
+ mp->mapptr = NULL;
+}
+
+/* Adjust usage counters and remove allocated memory when all users are
+ * gone.
+ */
+static void pai_event_destroy_cpu(struct perf_event *event, int cpu)
+{
+ int idx = PAI_PMU_IDX(event);
+ struct pai_mapptr *mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
+ struct pai_map *cpump = mp->mapptr;
+
+ mutex_lock(&pai_reserve_mutex);
+ debug_sprintf_event(paidbg, 5, "%s event %#llx idx %d cpu %d users %d "
+ "refcnt %u\n", __func__, event->attr.config, idx,
+ event->cpu, cpump->active_events,
+ refcount_read(&cpump->refcnt));
+ if (refcount_dec_and_test(&cpump->refcnt))
+ pai_free(mp);
+ pai_root_free(idx);
+ mutex_unlock(&pai_reserve_mutex);
+}
+
+static void pai_event_destroy(struct perf_event *event)
+{
+ int cpu;
+
+ free_page(PAI_SAVE_AREA(event));
+ if (event->cpu == -1) {
+ struct cpumask *mask = PAI_CPU_MASK(event);
+
+ for_each_cpu(cpu, mask)
+ pai_event_destroy_cpu(event, cpu);
+ kfree(mask);
+ } else {
+ pai_event_destroy_cpu(event, event->cpu);
+ }
+}
+
+static void paicrypt_event_destroy(struct perf_event *event)
+{
+ static_branch_dec(&pai_key);
+ pai_event_destroy(event);
+}
+
+static u64 pai_getctr(unsigned long *page, int nr, unsigned long offset)
+{
+ if (offset)
+ nr += offset / sizeof(*page);
+ return page[nr];
+}
+
+/* Read the counter values. Return value from location in CMP. For base
+ * event xxx_ALL sum up all events. Returns counter value.
+ */
+static u64 pai_getdata(struct perf_event *event, bool kernel)
+{
+ int idx = PAI_PMU_IDX(event);
+ struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
+ struct pai_pmu *pp = &pai_pmu[idx];
+ struct pai_map *cpump = mp->mapptr;
+ unsigned int i;
+ u64 sum = 0;
+
+ if (event->attr.config != pp->base) {
+ return pai_getctr(cpump->area,
+ event->attr.config - pp->base,
+ kernel ? pp->kernel_offset : 0);
+ }
+
+ for (i = 1; i <= pp->num_avail; i++) {
+ u64 val = pai_getctr(cpump->area, i,
+ kernel ? pp->kernel_offset : 0);
+
+ if (!val)
+ continue;
+ sum += val;
+ }
+ return sum;
+}
+
+static u64 paicrypt_getall(struct perf_event *event)
+{
+ u64 sum = 0;
+
+ if (!event->attr.exclude_kernel)
+ sum += pai_getdata(event, true);
+ if (!event->attr.exclude_user)
+ sum += pai_getdata(event, false);
+
+ return sum;
+}
+
+/* Check concurrent access of counting and sampling for crypto events.
+ * This function is called in process context and it is save to block.
+ * When the event initialization functions fails, no other call back will
+ * be invoked.
+ *
+ * Allocate the memory for the event.
+ */
+static int pai_alloc_cpu(struct perf_event *event, int cpu)
+{
+ int rc, idx = PAI_PMU_IDX(event);
+ struct pai_map *cpump = NULL;
+ bool need_paiext_cb = false;
+ struct pai_mapptr *mp;
+
+ mutex_lock(&pai_reserve_mutex);
+ /* Allocate root node */
+ rc = pai_root_alloc(idx);
+ if (rc)
+ goto unlock;
+
+ /* Allocate node for this event */
+ mp = per_cpu_ptr(pai_root[idx].mapptr, cpu);
+ cpump = mp->mapptr;
+ if (!cpump) { /* Paicrypt_map allocated? */
+ rc = -ENOMEM;
+ cpump = kzalloc(sizeof(*cpump), GFP_KERNEL);
+ if (!cpump)
+ goto undo;
+ /* Allocate memory for counter page and counter extraction.
+ * Only the first counting event has to allocate a page.
+ */
+ mp->mapptr = cpump;
+ if (idx == PAI_PMU_CRYPTO) {
+ cpump->area = (unsigned long *)get_zeroed_page(GFP_KERNEL);
+ /* free_page() can handle 0x0 address */
+ cpump->fullpage = true;
+ } else { /* PAI_PMU_EXT */
+ /*
+ * Allocate memory for counter area and counter extraction.
+ * These are
+ * - a 512 byte block and requires 512 byte boundary
+ * alignment.
+ * - a 1KB byte block and requires 1KB boundary
+ * alignment.
+ * Only the first counting event has to allocate the area.
+ *
+ * Note: This works with commit 59bb47985c1d by default.
+ * Backporting this to kernels without this commit might
+ * needs adjustment.
+ */
+ cpump->area = kzalloc(pai_pmu[idx].area_size, GFP_KERNEL);
+ cpump->paiext_cb = kzalloc(PAIE1_CB_SZ, GFP_KERNEL);
+ need_paiext_cb = true;
+ }
+ cpump->save = kvmalloc_array(pai_pmu[idx].num_avail + 1,
+ sizeof(struct pai_userdata),
+ GFP_KERNEL);
+ if (!cpump->area || !cpump->save ||
+ (need_paiext_cb && !cpump->paiext_cb)) {
+ pai_free(mp);
+ goto undo;
+ }
+ INIT_LIST_HEAD(&cpump->syswide_list);
+ refcount_set(&cpump->refcnt, 1);
+ rc = 0;
+ } else {
+ refcount_inc(&cpump->refcnt);
+ }
+
+undo:
+ if (rc) {
+ /* Error in allocation of event, decrement anchor. Since
+ * the event in not created, its destroy() function is never
+ * invoked. Adjust the reference counter for the anchor.
+ */
+ pai_root_free(idx);
+ }
+unlock:
+ mutex_unlock(&pai_reserve_mutex);
+ /* If rc is non-zero, no increment of counter/sampler was done. */
+ return rc;
+}
+
+static int pai_alloc(struct perf_event *event)
+{
+ struct cpumask *maskptr;
+ int cpu, rc = -ENOMEM;
+
+ maskptr = kzalloc(sizeof(*maskptr), GFP_KERNEL);
+ if (!maskptr)
+ goto out;
+
+ for_each_online_cpu(cpu) {
+ rc = pai_alloc_cpu(event, cpu);
+ if (rc) {
+ for_each_cpu(cpu, maskptr)
+ pai_event_destroy_cpu(event, cpu);
+ kfree(maskptr);
+ goto out;
+ }
+ cpumask_set_cpu(cpu, maskptr);
+ }
+
+ /*
+ * On error all cpumask are freed and all events have been destroyed.
+ * Save of which CPUs data structures have been allocated for.
+ * Release them in pai_event_destroy call back function
+ * for this event.
+ */
+ PAI_CPU_MASK(event) = maskptr;
+ rc = 0;
+out:
+ return rc;
+}
+
+/* Validate event number and return error if event is not supported.
+ * On successful return, PAI_PMU_IDX(event) is set to the index of
+ * the supporting paing_support[] array element.
+ */
+static int pai_event_valid(struct perf_event *event, int idx)
+{
+ struct perf_event_attr *a = &event->attr;
+ struct pai_pmu *pp = &pai_pmu[idx];
+
+ /* PAI crypto PMU registered as PERF_TYPE_RAW, check event type */
+ if (a->type != PERF_TYPE_RAW && event->pmu->type != a->type)
+ return -ENOENT;
+ /* Allow only CRYPTO_ALL/NNPA_ALL for sampling */
+ if (a->sample_period && a->config != pp->base)
+ return -EINVAL;
+ /* PAI crypto event must be in valid range, try others if not */
+ if (a->config < pp->base || a->config > pp->base + pp->num_avail)
+ return -ENOENT;
+ if (idx == PAI_PMU_EXT && a->exclude_user)
+ return -EINVAL;
+ PAI_PMU_IDX(event) = idx;
+ return 0;
+}
+
+/* Might be called on different CPU than the one the event is intended for. */
+static int pai_event_init(struct perf_event *event, int idx)
+{
+ struct perf_event_attr *a = &event->attr;
+ int rc;
+
+ /* PAI event must be valid and in supported range */
+ rc = pai_event_valid(event, idx);
+ if (rc)
+ goto out;
+ /* Get a page to store last counter values for sampling */
+ if (a->sample_period) {
+ PAI_SAVE_AREA(event) = get_zeroed_page(GFP_KERNEL);
+ if (!PAI_SAVE_AREA(event)) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ }
+
+ if (event->cpu >= 0)
+ rc = pai_alloc_cpu(event, event->cpu);
+ else
+ rc = pai_alloc(event);
+ if (rc) {
+ free_page(PAI_SAVE_AREA(event));
+ goto out;
+ }
+
+ if (a->sample_period) {
+ a->sample_period = 1;
+ a->freq = 0;
+ /* Register for paicrypt_sched_task() to be called */
+ event->attach_state |= PERF_ATTACH_SCHED_CB;
+ /* Add raw data which contain the memory mapped counters */
+ a->sample_type |= PERF_SAMPLE_RAW;
+ /* Turn off inheritance */
+ a->inherit = 0;
+ }
+out:
+ return rc;
+}
+
+static int paicrypt_event_init(struct perf_event *event)
+{
+ int rc = pai_event_init(event, PAI_PMU_CRYPTO);
+
+ if (!rc) {
+ event->destroy = paicrypt_event_destroy;
+ static_branch_inc(&pai_key);
+ }
+ return rc;
+}
+
+static void pai_read(struct perf_event *event,
+ u64 (*fct)(struct perf_event *event))
+{
+ u64 prev, new, delta;
+
+ prev = local64_read(&event->hw.prev_count);
+ new = fct(event);
+ local64_set(&event->hw.prev_count, new);
+ delta = (prev <= new) ? new - prev : (-1ULL - prev) + new + 1;
+ local64_add(delta, &event->count);
+}
+
+static void paicrypt_read(struct perf_event *event)
+{
+ pai_read(event, paicrypt_getall);
+}
+
+static void pai_start(struct perf_event *event, int flags,
+ u64 (*fct)(struct perf_event *event))
+{
+ int idx = PAI_PMU_IDX(event);
+ struct pai_pmu *pp = &pai_pmu[idx];
+ struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
+ struct pai_map *cpump = mp->mapptr;
+ u64 sum;
+
+ if (!event->attr.sample_period) { /* Counting */
+ sum = fct(event); /* Get current value */
+ local64_set(&event->hw.prev_count, sum);
+ } else { /* Sampling */
+ memcpy((void *)PAI_SAVE_AREA(event), cpump->area, pp->area_size);
+ /* Enable context switch callback for system-wide sampling */
+ if (!(event->attach_state & PERF_ATTACH_TASK)) {
+ list_add_tail(PAI_SWLIST(event), &cpump->syswide_list);
+ perf_sched_cb_inc(event->pmu);
+ } else {
+ cpump->event = event;
+ }
+ }
+}
+
+static void paicrypt_start(struct perf_event *event, int flags)
+{
+ pai_start(event, flags, paicrypt_getall);
+}
+
+static int pai_add(struct perf_event *event, int flags)
+{
+ int idx = PAI_PMU_IDX(event);
+ struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
+ struct pai_map *cpump = mp->mapptr;
+ struct paiext_cb *pcb = cpump->paiext_cb;
+ unsigned long ccd;
+
+ if (++cpump->active_events == 1) {
+ if (!pcb) { /* PAI crypto */
+ ccd = virt_to_phys(cpump->area) | PAI_CRYPTO_KERNEL_OFFSET;
+ WRITE_ONCE(get_lowcore()->ccd, ccd);
+ local_ctl_set_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT);
+ } else { /* PAI extension 1 */
+ ccd = virt_to_phys(pcb);
+ WRITE_ONCE(get_lowcore()->aicd, ccd);
+ pcb->acc = virt_to_phys(cpump->area) | 0x1;
+ /* Enable CPU instruction lookup for PAIE1 control block */
+ local_ctl_set_bit(0, CR0_PAI_EXTENSION_BIT);
+ }
+ }
+ if (flags & PERF_EF_START)
+ pai_pmu[idx].pmu->start(event, PERF_EF_RELOAD);
+ event->hw.state = 0;
+ return 0;
+}
+
+static int paicrypt_add(struct perf_event *event, int flags)
+{
+ return pai_add(event, flags);
+}
+
+static void pai_have_sample(struct perf_event *, struct pai_map *);
+static void pai_stop(struct perf_event *event, int flags)
+{
+ int idx = PAI_PMU_IDX(event);
+ struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
+ struct pai_map *cpump = mp->mapptr;
+
+ if (!event->attr.sample_period) { /* Counting */
+ pai_pmu[idx].pmu->read(event);
+ } else { /* Sampling */
+ if (!(event->attach_state & PERF_ATTACH_TASK)) {
+ perf_sched_cb_dec(event->pmu);
+ list_del(PAI_SWLIST(event));
+ } else {
+ pai_have_sample(event, cpump);
+ cpump->event = NULL;
+ }
+ }
+ event->hw.state = PERF_HES_STOPPED;
+}
+
+static void paicrypt_stop(struct perf_event *event, int flags)
+{
+ pai_stop(event, flags);
+}
+
+static void pai_del(struct perf_event *event, int flags)
+{
+ int idx = PAI_PMU_IDX(event);
+ struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
+ struct pai_map *cpump = mp->mapptr;
+ struct paiext_cb *pcb = cpump->paiext_cb;
+
+ pai_pmu[idx].pmu->stop(event, PERF_EF_UPDATE);
+ if (--cpump->active_events == 0) {
+ if (!pcb) { /* PAI crypto */
+ local_ctl_clear_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT);
+ WRITE_ONCE(get_lowcore()->ccd, 0);
+ } else { /* PAI extension 1 */
+ /* Disable CPU instruction lookup for PAIE1 control block */
+ local_ctl_clear_bit(0, CR0_PAI_EXTENSION_BIT);
+ pcb->acc = 0;
+ WRITE_ONCE(get_lowcore()->aicd, 0);
+ }
+ }
+}
+
+static void paicrypt_del(struct perf_event *event, int flags)
+{
+ pai_del(event, flags);
+}
+
+/* Create raw data and save it in buffer. Calculate the delta for each
+ * counter between this invocation and the last invocation.
+ * Returns number of bytes copied.
+ * Saves only entries with positive counter difference of the form
+ * 2 bytes: Number of counter
+ * 8 bytes: Value of counter
+ */
+static size_t pai_copy(struct pai_userdata *userdata, unsigned long *page,
+ struct pai_pmu *pp, unsigned long *page_old,
+ bool exclude_user, bool exclude_kernel)
+{
+ int i, outidx = 0;
+
+ for (i = 1; i <= pp->num_avail; i++) {
+ u64 val = 0, val_old = 0;
+
+ if (!exclude_kernel) {
+ val += pai_getctr(page, i, pp->kernel_offset);
+ val_old += pai_getctr(page_old, i, pp->kernel_offset);
+ }
+ if (!exclude_user) {
+ val += pai_getctr(page, i, 0);
+ val_old += pai_getctr(page_old, i, 0);
+ }
+ if (val >= val_old)
+ val -= val_old;
+ else
+ val = (~0ULL - val_old) + val + 1;
+ if (val) {
+ userdata[outidx].num = i;
+ userdata[outidx].value = val;
+ outidx++;
+ }
+ }
+ return outidx * sizeof(*userdata);
+}
+
+/* Write sample when one or more counters values are nonzero.
+ *
+ * Note: The function paicrypt_sched_task() and pai_push_sample() are not
+ * invoked after function paicrypt_del() has been called because of function
+ * perf_sched_cb_dec(). Both functions are only
+ * called when sampling is active. Function perf_sched_cb_inc()
+ * has been invoked to install function paicrypt_sched_task() as call back
+ * to run at context switch time.
+ *
+ * This causes function perf_event_context_sched_out() and
+ * perf_event_context_sched_in() to check whether the PMU has installed an
+ * sched_task() callback. That callback is not active after paicrypt_del()
+ * returns and has deleted the event on that CPU.
+ */
+static int pai_push_sample(size_t rawsize, struct pai_map *cpump,
+ struct perf_event *event)
+{
+ int idx = PAI_PMU_IDX(event);
+ struct pai_pmu *pp = &pai_pmu[idx];
+ struct perf_sample_data data;
+ struct perf_raw_record raw;
+ struct pt_regs regs;
+ int overflow;
+
+ /* Setup perf sample */
+ memset(&regs, 0, sizeof(regs));
+ memset(&raw, 0, sizeof(raw));
+ memset(&data, 0, sizeof(data));
+ perf_sample_data_init(&data, 0, event->hw.last_period);
+ if (event->attr.sample_type & PERF_SAMPLE_TID) {
+ data.tid_entry.pid = task_tgid_nr(current);
+ data.tid_entry.tid = task_pid_nr(current);
+ }
+ if (event->attr.sample_type & PERF_SAMPLE_TIME)
+ data.time = event->clock();
+ if (event->attr.sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER))
+ data.id = event->id;
+ if (event->attr.sample_type & PERF_SAMPLE_CPU) {
+ data.cpu_entry.cpu = smp_processor_id();
+ data.cpu_entry.reserved = 0;
+ }
+ if (event->attr.sample_type & PERF_SAMPLE_RAW) {
+ raw.frag.size = rawsize;
+ raw.frag.data = cpump->save;
+ perf_sample_save_raw_data(&data, event, &raw);
+ }
+
+ overflow = perf_event_overflow(event, &data, &regs);
+ perf_event_update_userpage(event);
+ /* Save crypto counter lowcore page after reading event data. */
+ memcpy((void *)PAI_SAVE_AREA(event), cpump->area, pp->area_size);
+ return overflow;
+}
+
+/* Check if there is data to be saved on schedule out of a task. */
+static void pai_have_sample(struct perf_event *event, struct pai_map *cpump)
+{
+ struct pai_pmu *pp;
+ size_t rawsize;
+
+ if (!event) /* No event active */
+ return;
+ pp = &pai_pmu[PAI_PMU_IDX(event)];
+ rawsize = pai_copy(cpump->save, cpump->area, pp,
+ (unsigned long *)PAI_SAVE_AREA(event),
+ event->attr.exclude_user,
+ event->attr.exclude_kernel);
+ if (rawsize) /* No incremented counters */
+ pai_push_sample(rawsize, cpump, event);
+}
+
+/* Check if there is data to be saved on schedule out of a task. */
+static void pai_have_samples(int idx)
+{
+ struct pai_mapptr *mp = this_cpu_ptr(pai_root[idx].mapptr);
+ struct pai_map *cpump = mp->mapptr;
+ struct perf_event *event;
+
+ list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
+ pai_have_sample(event, cpump);
+}
+
+/* Called on schedule-in and schedule-out. No access to event structure,
+ * but for sampling only event CRYPTO_ALL is allowed.
+ */
+static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx,
+ struct task_struct *task, bool sched_in)
+{
+ /* We started with a clean page on event installation. So read out
+ * results on schedule_out and if page was dirty, save old values.
+ */
+ if (!sched_in)
+ pai_have_samples(PAI_PMU_CRYPTO);
+}
+
+/* ============================= paiext ====================================*/
+
+static void paiext_event_destroy(struct perf_event *event)
+{
+ pai_event_destroy(event);
+}
+
+/* Might be called on different CPU than the one the event is intended for. */
+static int paiext_event_init(struct perf_event *event)
+{
+ int rc = pai_event_init(event, PAI_PMU_EXT);
+
+ if (!rc) {
+ event->attr.exclude_kernel = true; /* No kernel space part */
+ event->destroy = paiext_event_destroy;
+ /* Offset of NNPA in paiext_cb */
+ event->hw.config_base = offsetof(struct paiext_cb, acc);
+ }
+ return rc;
+}
+
+static u64 paiext_getall(struct perf_event *event)
+{
+ return pai_getdata(event, false);
+}
+
+static void paiext_read(struct perf_event *event)
+{
+ pai_read(event, paiext_getall);
+}
+
+static void paiext_start(struct perf_event *event, int flags)
+{
+ pai_start(event, flags, paiext_getall);
+}
+
+static int paiext_add(struct perf_event *event, int flags)
+{
+ return pai_add(event, flags);
+}
+
+static void paiext_stop(struct perf_event *event, int flags)
+{
+ pai_stop(event, flags);
+}
+
+static void paiext_del(struct perf_event *event, int flags)
+{
+ pai_del(event, flags);
+}
+
+/* Called on schedule-in and schedule-out. No access to event structure,
+ * but for sampling only event NNPA_ALL is allowed.
+ */
+static void paiext_sched_task(struct perf_event_pmu_context *pmu_ctx,
+ struct task_struct *task, bool sched_in)
+{
+ /* We started with a clean page on event installation. So read out
+ * results on schedule_out and if page was dirty, save old values.
+ */
+ if (!sched_in)
+ pai_have_samples(PAI_PMU_EXT);
+}
+
+/* Attribute definitions for paicrypt interface. As with other CPU
+ * Measurement Facilities, there is one attribute per mapped counter.
+ * The number of mapped counters may vary per machine generation. Use
+ * the QUERY PROCESSOR ACTIVITY COUNTER INFORMATION (QPACI) instruction
+ * to determine the number of mapped counters. The instructions returns
+ * a positive number, which is the highest number of supported counters.
+ * All counters less than this number are also supported, there are no
+ * holes. A returned number of zero means no support for mapped counters.
+ *
+ * The identification of the counter is a unique number. The chosen range
+ * is 0x1000 + offset in mapped kernel page.
+ * All CPU Measurement Facility counters identifiers must be unique and
+ * the numbers from 0 to 496 are already used for the CPU Measurement
+ * Counter facility. Numbers 0xb0000, 0xbc000 and 0xbd000 are already
+ * used for the CPU Measurement Sampling facility.
+ */
+PMU_FORMAT_ATTR(event, "config:0-63");
+
+static struct attribute *paicrypt_format_attr[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static struct attribute_group paicrypt_events_group = {
+ .name = "events",
+ .attrs = NULL /* Filled in attr_event_init() */
+};
+
+static struct attribute_group paicrypt_format_group = {
+ .name = "format",
+ .attrs = paicrypt_format_attr,
+};
+
+static const struct attribute_group *paicrypt_attr_groups[] = {
+ &paicrypt_events_group,
+ &paicrypt_format_group,
+ NULL,
+};
+
+/* Performance monitoring unit for mapped counters */
+static struct pmu paicrypt = {
+ .task_ctx_nr = perf_hw_context,
+ .event_init = paicrypt_event_init,
+ .add = paicrypt_add,
+ .del = paicrypt_del,
+ .start = paicrypt_start,
+ .stop = paicrypt_stop,
+ .read = paicrypt_read,
+ .sched_task = paicrypt_sched_task,
+ .attr_groups = paicrypt_attr_groups
+};
+
+/* List of symbolic PAI counter names. */
+static const char * const paicrypt_ctrnames[] = {
+ [0] = "CRYPTO_ALL",
+ [1] = "KM_DEA",
+ [2] = "KM_TDEA_128",
+ [3] = "KM_TDEA_192",
+ [4] = "KM_ENCRYPTED_DEA",
+ [5] = "KM_ENCRYPTED_TDEA_128",
+ [6] = "KM_ENCRYPTED_TDEA_192",
+ [7] = "KM_AES_128",
+ [8] = "KM_AES_192",
+ [9] = "KM_AES_256",
+ [10] = "KM_ENCRYPTED_AES_128",
+ [11] = "KM_ENCRYPTED_AES_192",
+ [12] = "KM_ENCRYPTED_AES_256",
+ [13] = "KM_XTS_AES_128",
+ [14] = "KM_XTS_AES_256",
+ [15] = "KM_XTS_ENCRYPTED_AES_128",
+ [16] = "KM_XTS_ENCRYPTED_AES_256",
+ [17] = "KMC_DEA",
+ [18] = "KMC_TDEA_128",
+ [19] = "KMC_TDEA_192",
+ [20] = "KMC_ENCRYPTED_DEA",
+ [21] = "KMC_ENCRYPTED_TDEA_128",
+ [22] = "KMC_ENCRYPTED_TDEA_192",
+ [23] = "KMC_AES_128",
+ [24] = "KMC_AES_192",
+ [25] = "KMC_AES_256",
+ [26] = "KMC_ENCRYPTED_AES_128",
+ [27] = "KMC_ENCRYPTED_AES_192",
+ [28] = "KMC_ENCRYPTED_AES_256",
+ [29] = "KMC_PRNG",
+ [30] = "KMA_GCM_AES_128",
+ [31] = "KMA_GCM_AES_192",
+ [32] = "KMA_GCM_AES_256",
+ [33] = "KMA_GCM_ENCRYPTED_AES_128",
+ [34] = "KMA_GCM_ENCRYPTED_AES_192",
+ [35] = "KMA_GCM_ENCRYPTED_AES_256",
+ [36] = "KMF_DEA",
+ [37] = "KMF_TDEA_128",
+ [38] = "KMF_TDEA_192",
+ [39] = "KMF_ENCRYPTED_DEA",
+ [40] = "KMF_ENCRYPTED_TDEA_128",
+ [41] = "KMF_ENCRYPTED_TDEA_192",
+ [42] = "KMF_AES_128",
+ [43] = "KMF_AES_192",
+ [44] = "KMF_AES_256",
+ [45] = "KMF_ENCRYPTED_AES_128",
+ [46] = "KMF_ENCRYPTED_AES_192",
+ [47] = "KMF_ENCRYPTED_AES_256",
+ [48] = "KMCTR_DEA",
+ [49] = "KMCTR_TDEA_128",
+ [50] = "KMCTR_TDEA_192",
+ [51] = "KMCTR_ENCRYPTED_DEA",
+ [52] = "KMCTR_ENCRYPTED_TDEA_128",
+ [53] = "KMCTR_ENCRYPTED_TDEA_192",
+ [54] = "KMCTR_AES_128",
+ [55] = "KMCTR_AES_192",
+ [56] = "KMCTR_AES_256",
+ [57] = "KMCTR_ENCRYPTED_AES_128",
+ [58] = "KMCTR_ENCRYPTED_AES_192",
+ [59] = "KMCTR_ENCRYPTED_AES_256",
+ [60] = "KMO_DEA",
+ [61] = "KMO_TDEA_128",
+ [62] = "KMO_TDEA_192",
+ [63] = "KMO_ENCRYPTED_DEA",
+ [64] = "KMO_ENCRYPTED_TDEA_128",
+ [65] = "KMO_ENCRYPTED_TDEA_192",
+ [66] = "KMO_AES_128",
+ [67] = "KMO_AES_192",
+ [68] = "KMO_AES_256",
+ [69] = "KMO_ENCRYPTED_AES_128",
+ [70] = "KMO_ENCRYPTED_AES_192",
+ [71] = "KMO_ENCRYPTED_AES_256",
+ [72] = "KIMD_SHA_1",
+ [73] = "KIMD_SHA_256",
+ [74] = "KIMD_SHA_512",
+ [75] = "KIMD_SHA3_224",
+ [76] = "KIMD_SHA3_256",
+ [77] = "KIMD_SHA3_384",
+ [78] = "KIMD_SHA3_512",
+ [79] = "KIMD_SHAKE_128",
+ [80] = "KIMD_SHAKE_256",
+ [81] = "KIMD_GHASH",
+ [82] = "KLMD_SHA_1",
+ [83] = "KLMD_SHA_256",
+ [84] = "KLMD_SHA_512",
+ [85] = "KLMD_SHA3_224",
+ [86] = "KLMD_SHA3_256",
+ [87] = "KLMD_SHA3_384",
+ [88] = "KLMD_SHA3_512",
+ [89] = "KLMD_SHAKE_128",
+ [90] = "KLMD_SHAKE_256",
+ [91] = "KMAC_DEA",
+ [92] = "KMAC_TDEA_128",
+ [93] = "KMAC_TDEA_192",
+ [94] = "KMAC_ENCRYPTED_DEA",
+ [95] = "KMAC_ENCRYPTED_TDEA_128",
+ [96] = "KMAC_ENCRYPTED_TDEA_192",
+ [97] = "KMAC_AES_128",
+ [98] = "KMAC_AES_192",
+ [99] = "KMAC_AES_256",
+ [100] = "KMAC_ENCRYPTED_AES_128",
+ [101] = "KMAC_ENCRYPTED_AES_192",
+ [102] = "KMAC_ENCRYPTED_AES_256",
+ [103] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_DEA",
+ [104] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_TDEA_128",
+ [105] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_TDEA_192",
+ [106] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_DEA",
+ [107] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_128",
+ [108] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_192",
+ [109] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_128",
+ [110] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_192",
+ [111] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_256",
+ [112] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_128",
+ [113] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_192",
+ [114] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_256",
+ [115] = "PCC_COMPUTE_XTS_PARAMETER_USING_AES_128",
+ [116] = "PCC_COMPUTE_XTS_PARAMETER_USING_AES_256",
+ [117] = "PCC_COMPUTE_XTS_PARAMETER_USING_ENCRYPTED_AES_128",
+ [118] = "PCC_COMPUTE_XTS_PARAMETER_USING_ENCRYPTED_AES_256",
+ [119] = "PCC_SCALAR_MULTIPLY_P256",
+ [120] = "PCC_SCALAR_MULTIPLY_P384",
+ [121] = "PCC_SCALAR_MULTIPLY_P521",
+ [122] = "PCC_SCALAR_MULTIPLY_ED25519",
+ [123] = "PCC_SCALAR_MULTIPLY_ED448",
+ [124] = "PCC_SCALAR_MULTIPLY_X25519",
+ [125] = "PCC_SCALAR_MULTIPLY_X448",
+ [126] = "PRNO_SHA_512_DRNG",
+ [127] = "PRNO_TRNG_QUERY_RAW_TO_CONDITIONED_RATIO",
+ [128] = "PRNO_TRNG",
+ [129] = "KDSA_ECDSA_VERIFY_P256",
+ [130] = "KDSA_ECDSA_VERIFY_P384",
+ [131] = "KDSA_ECDSA_VERIFY_P521",
+ [132] = "KDSA_ECDSA_SIGN_P256",
+ [133] = "KDSA_ECDSA_SIGN_P384",
+ [134] = "KDSA_ECDSA_SIGN_P521",
+ [135] = "KDSA_ENCRYPTED_ECDSA_SIGN_P256",
+ [136] = "KDSA_ENCRYPTED_ECDSA_SIGN_P384",
+ [137] = "KDSA_ENCRYPTED_ECDSA_SIGN_P521",
+ [138] = "KDSA_EDDSA_VERIFY_ED25519",
+ [139] = "KDSA_EDDSA_VERIFY_ED448",
+ [140] = "KDSA_EDDSA_SIGN_ED25519",
+ [141] = "KDSA_EDDSA_SIGN_ED448",
+ [142] = "KDSA_ENCRYPTED_EDDSA_SIGN_ED25519",
+ [143] = "KDSA_ENCRYPTED_EDDSA_SIGN_ED448",
+ [144] = "PCKMO_ENCRYPT_DEA_KEY",
+ [145] = "PCKMO_ENCRYPT_TDEA_128_KEY",
+ [146] = "PCKMO_ENCRYPT_TDEA_192_KEY",
+ [147] = "PCKMO_ENCRYPT_AES_128_KEY",
+ [148] = "PCKMO_ENCRYPT_AES_192_KEY",
+ [149] = "PCKMO_ENCRYPT_AES_256_KEY",
+ [150] = "PCKMO_ENCRYPT_ECC_P256_KEY",
+ [151] = "PCKMO_ENCRYPT_ECC_P384_KEY",
+ [152] = "PCKMO_ENCRYPT_ECC_P521_KEY",
+ [153] = "PCKMO_ENCRYPT_ECC_ED25519_KEY",
+ [154] = "PCKMO_ENCRYPT_ECC_ED448_KEY",
+ [155] = "IBM_RESERVED_155",
+ [156] = "IBM_RESERVED_156",
+ [157] = "KM_FULL_XTS_AES_128",
+ [158] = "KM_FULL_XTS_AES_256",
+ [159] = "KM_FULL_XTS_ENCRYPTED_AES_128",
+ [160] = "KM_FULL_XTS_ENCRYPTED_AES_256",
+ [161] = "KMAC_HMAC_SHA_224",
+ [162] = "KMAC_HMAC_SHA_256",
+ [163] = "KMAC_HMAC_SHA_384",
+ [164] = "KMAC_HMAC_SHA_512",
+ [165] = "KMAC_HMAC_ENCRYPTED_SHA_224",
+ [166] = "KMAC_HMAC_ENCRYPTED_SHA_256",
+ [167] = "KMAC_HMAC_ENCRYPTED_SHA_384",
+ [168] = "KMAC_HMAC_ENCRYPTED_SHA_512",
+ [169] = "PCKMO_ENCRYPT_HMAC_512_KEY",
+ [170] = "PCKMO_ENCRYPT_HMAC_1024_KEY",
+ [171] = "PCKMO_ENCRYPT_AES_XTS_128",
+ [172] = "PCKMO_ENCRYPT_AES_XTS_256",
+};
+
+static struct attribute *paiext_format_attr[] = {
+ &format_attr_event.attr,
+ NULL,
+};
+
+static struct attribute_group paiext_events_group = {
+ .name = "events",
+ .attrs = NULL, /* Filled in attr_event_init() */
+};
+
+static struct attribute_group paiext_format_group = {
+ .name = "format",
+ .attrs = paiext_format_attr,
+};
+
+static const struct attribute_group *paiext_attr_groups[] = {
+ &paiext_events_group,
+ &paiext_format_group,
+ NULL,
+};
+
+/* Performance monitoring unit for mapped counters */
+static struct pmu paiext = {
+ .task_ctx_nr = perf_hw_context,
+ .event_init = paiext_event_init,
+ .add = paiext_add,
+ .del = paiext_del,
+ .start = paiext_start,
+ .stop = paiext_stop,
+ .read = paiext_read,
+ .sched_task = paiext_sched_task,
+ .attr_groups = paiext_attr_groups,
+};
+
+/* List of symbolic PAI extension 1 NNPA counter names. */
+static const char * const paiext_ctrnames[] = {
+ [0] = "NNPA_ALL",
+ [1] = "NNPA_ADD",
+ [2] = "NNPA_SUB",
+ [3] = "NNPA_MUL",
+ [4] = "NNPA_DIV",
+ [5] = "NNPA_MIN",
+ [6] = "NNPA_MAX",
+ [7] = "NNPA_LOG",
+ [8] = "NNPA_EXP",
+ [9] = "NNPA_IBM_RESERVED_9",
+ [10] = "NNPA_RELU",
+ [11] = "NNPA_TANH",
+ [12] = "NNPA_SIGMOID",
+ [13] = "NNPA_SOFTMAX",
+ [14] = "NNPA_BATCHNORM",
+ [15] = "NNPA_MAXPOOL2D",
+ [16] = "NNPA_AVGPOOL2D",
+ [17] = "NNPA_LSTMACT",
+ [18] = "NNPA_GRUACT",
+ [19] = "NNPA_CONVOLUTION",
+ [20] = "NNPA_MATMUL_OP",
+ [21] = "NNPA_MATMUL_OP_BCAST23",
+ [22] = "NNPA_SMALLBATCH",
+ [23] = "NNPA_LARGEDIM",
+ [24] = "NNPA_SMALLTENSOR",
+ [25] = "NNPA_1MFRAME",
+ [26] = "NNPA_2GFRAME",
+ [27] = "NNPA_ACCESSEXCEPT",
+ [28] = "NNPA_TRANSFORM",
+ [29] = "NNPA_GELU",
+ [30] = "NNPA_MOMENTS",
+ [31] = "NNPA_LAYERNORM",
+ [32] = "NNPA_MATMUL_OP_BCAST1",
+ [33] = "NNPA_SQRT",
+ [34] = "NNPA_INVSQRT",
+ [35] = "NNPA_NORM",
+ [36] = "NNPA_REDUCE",
+};
+
+static void __init attr_event_free(struct attribute **attrs)
+{
+ struct perf_pmu_events_attr *pa;
+ unsigned int i;
+
+ for (i = 0; attrs[i]; i++) {
+ struct device_attribute *dap;
+
+ dap = container_of(attrs[i], struct device_attribute, attr);
+ pa = container_of(dap, struct perf_pmu_events_attr, attr);
+ kfree(pa);
+ }
+ kfree(attrs);
+}
+
+static struct attribute * __init attr_event_init_one(int num,
+ unsigned long base,
+ const char *name)
+{
+ struct perf_pmu_events_attr *pa;
+
+ pa = kzalloc(sizeof(*pa), GFP_KERNEL);
+ if (!pa)
+ return NULL;
+
+ sysfs_attr_init(&pa->attr.attr);
+ pa->id = base + num;
+ pa->attr.attr.name = name;
+ pa->attr.attr.mode = 0444;
+ pa->attr.show = cpumf_events_sysfs_show;
+ pa->attr.store = NULL;
+ return &pa->attr.attr;
+}
+
+static struct attribute ** __init attr_event_init(struct pai_pmu *p)
+{
+ unsigned int min_attr = min_t(unsigned int, p->num_named, p->num_avail);
+ struct attribute **attrs;
+ unsigned int i;
+
+ attrs = kmalloc_array(min_attr + 1, sizeof(*attrs), GFP_KERNEL | __GFP_ZERO);
+ if (!attrs)
+ goto out;
+ for (i = 0; i < min_attr; i++) {
+ attrs[i] = attr_event_init_one(i, p->base, p->names[i]);
+ if (!attrs[i]) {
+ attr_event_free(attrs);
+ attrs = NULL;
+ goto out;
+ }
+ }
+ attrs[i] = NULL;
+out:
+ return attrs;
+}
+
+static void __init pai_pmu_exit(struct pai_pmu *p)
+{
+ attr_event_free(p->event_group->attrs);
+ p->event_group->attrs = NULL;
+}
+
+/* Add a PMU. Install its events and register the PMU device driver
+ * call back functions.
+ */
+static int __init pai_pmu_init(struct pai_pmu *p)
+{
+ int rc = -ENOMEM;
+
+
+ /* Export known PAI events */
+ p->event_group->attrs = attr_event_init(p);
+ if (!p->event_group->attrs) {
+ pr_err("Creation of PMU %s /sysfs failed\n", p->pmuname);
+ goto out;
+ }
+
+ rc = perf_pmu_register(p->pmu, p->pmuname, -1);
+ if (rc) {
+ pai_pmu_exit(p);
+ pr_err("Registering PMU %s failed with rc=%i\n", p->pmuname,
+ rc);
+ }
+out:
+ return rc;
+}
+
+/* PAI PMU characteristics table */
+static struct pai_pmu pai_pmu[] __refdata = {
+ [PAI_PMU_CRYPTO] = {
+ .pmuname = "pai_crypto",
+ .facility_nr = 196,
+ .num_named = ARRAY_SIZE(paicrypt_ctrnames),
+ .names = paicrypt_ctrnames,
+ .base = PAI_CRYPTO_BASE,
+ .kernel_offset = PAI_CRYPTO_KERNEL_OFFSET,
+ .area_size = PAGE_SIZE,
+ .init = pai_pmu_init,
+ .exit = pai_pmu_exit,
+ .pmu = &paicrypt,
+ .event_group = &paicrypt_events_group
+ },
+ [PAI_PMU_EXT] = {
+ .pmuname = "pai_ext",
+ .facility_nr = 197,
+ .num_named = ARRAY_SIZE(paiext_ctrnames),
+ .names = paiext_ctrnames,
+ .base = PAI_NNPA_BASE,
+ .kernel_offset = 0,
+ .area_size = PAIE1_CTRBLOCK_SZ,
+ .init = pai_pmu_init,
+ .exit = pai_pmu_exit,
+ .pmu = &paiext,
+ .event_group = &paiext_events_group
+ }
+};
+
+/*
+ * Check if the PMU (via facility) is supported by machine. Try all of the
+ * supported PAI PMUs.
+ * Return number of successfully installed PMUs.
+ */
+static int __init paipmu_setup(void)
+{
+ struct qpaci_info_block ib;
+ int install_ok = 0, rc;
+ struct pai_pmu *p;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(pai_pmu); ++i) {
+ p = &pai_pmu[i];
+
+ if (!test_facility(p->facility_nr))
+ continue;
+
+ qpaci(&ib);
+ switch (i) {
+ case PAI_PMU_CRYPTO:
+ p->num_avail = ib.num_cc;
+ if (p->num_avail >= PAI_CRYPTO_MAXCTR) {
+ pr_err("Too many PMU %s counters %d\n",
+ p->pmuname, p->num_avail);
+ continue;
+ }
+ break;
+ case PAI_PMU_EXT:
+ p->num_avail = ib.num_nnpa;
+ break;
+ }
+ p->num_avail += 1; /* Add xxx_ALL event */
+ if (p->init) {
+ rc = p->init(p);
+ if (!rc)
+ ++install_ok;
+ }
+ }
+ return install_ok;
+}
+
+static int __init pai_init(void)
+{
+ /* Setup s390dbf facility */
+ paidbg = debug_register("pai", 32, 256, 128);
+ if (!paidbg) {
+ pr_err("Registration of s390dbf pai failed\n");
+ return -ENOMEM;
+ }
+ debug_register_view(paidbg, &debug_sprintf_view);
+
+ if (!paipmu_setup()) {
+ /* No PMU registration, no need for debug buffer */
+ debug_unregister_view(paidbg, &debug_sprintf_view);
+ debug_unregister(paidbg);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+device_initcall(pai_init);
diff --git a/arch/s390/kernel/perf_pai_crypto.c b/arch/s390/kernel/perf_pai_crypto.c
deleted file mode 100644
index 62bf8a15bf32..000000000000
--- a/arch/s390/kernel/perf_pai_crypto.c
+++ /dev/null
@@ -1,843 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Performance event support - Processor Activity Instrumentation Facility
- *
- * Copyright IBM Corp. 2022
- * Author(s): Thomas Richter <tmricht@linux.ibm.com>
- */
-#define KMSG_COMPONENT "pai_crypto"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/kernel_stat.h>
-#include <linux/percpu.h>
-#include <linux/notifier.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/perf_event.h>
-#include <asm/ctlreg.h>
-#include <asm/pai.h>
-#include <asm/debug.h>
-
-static debug_info_t *cfm_dbg;
-static unsigned int paicrypt_cnt; /* Size of the mapped counter sets */
- /* extracted with QPACI instruction */
-
-DEFINE_STATIC_KEY_FALSE(pai_key);
-
-struct pai_userdata {
- u16 num;
- u64 value;
-} __packed;
-
-struct paicrypt_map {
- unsigned long *page; /* Page for CPU to store counters */
- struct pai_userdata *save; /* Page to store no-zero counters */
- unsigned int active_events; /* # of PAI crypto users */
- refcount_t refcnt; /* Reference count mapped buffers */
- struct perf_event *event; /* Perf event for sampling */
- struct list_head syswide_list; /* List system-wide sampling events */
-};
-
-struct paicrypt_mapptr {
- struct paicrypt_map *mapptr;
-};
-
-static struct paicrypt_root { /* Anchor to per CPU data */
- refcount_t refcnt; /* Overall active events */
- struct paicrypt_mapptr __percpu *mapptr;
-} paicrypt_root;
-
-/* Free per CPU data when the last event is removed. */
-static void paicrypt_root_free(void)
-{
- if (refcount_dec_and_test(&paicrypt_root.refcnt)) {
- free_percpu(paicrypt_root.mapptr);
- paicrypt_root.mapptr = NULL;
- }
- debug_sprintf_event(cfm_dbg, 5, "%s root.refcount %d\n", __func__,
- refcount_read(&paicrypt_root.refcnt));
-}
-
-/*
- * On initialization of first event also allocate per CPU data dynamically.
- * Start with an array of pointers, the array size is the maximum number of
- * CPUs possible, which might be larger than the number of CPUs currently
- * online.
- */
-static int paicrypt_root_alloc(void)
-{
- if (!refcount_inc_not_zero(&paicrypt_root.refcnt)) {
- /* The memory is already zeroed. */
- paicrypt_root.mapptr = alloc_percpu(struct paicrypt_mapptr);
- if (!paicrypt_root.mapptr)
- return -ENOMEM;
- refcount_set(&paicrypt_root.refcnt, 1);
- }
- return 0;
-}
-
-/* Release the PMU if event is the last perf event */
-static DEFINE_MUTEX(pai_reserve_mutex);
-
-/* Free all memory allocated for event counting/sampling setup */
-static void paicrypt_free(struct paicrypt_mapptr *mp)
-{
- free_page((unsigned long)mp->mapptr->page);
- kvfree(mp->mapptr->save);
- kfree(mp->mapptr);
- mp->mapptr = NULL;
-}
-
-/* Adjust usage counters and remove allocated memory when all users are
- * gone.
- */
-static void paicrypt_event_destroy_cpu(struct perf_event *event, int cpu)
-{
- struct paicrypt_mapptr *mp = per_cpu_ptr(paicrypt_root.mapptr, cpu);
- struct paicrypt_map *cpump = mp->mapptr;
-
- mutex_lock(&pai_reserve_mutex);
- debug_sprintf_event(cfm_dbg, 5, "%s event %#llx cpu %d users %d "
- "refcnt %u\n", __func__, event->attr.config,
- event->cpu, cpump->active_events,
- refcount_read(&cpump->refcnt));
- if (refcount_dec_and_test(&cpump->refcnt))
- paicrypt_free(mp);
- paicrypt_root_free();
- mutex_unlock(&pai_reserve_mutex);
-}
-
-static void paicrypt_event_destroy(struct perf_event *event)
-{
- int cpu;
-
- static_branch_dec(&pai_key);
- free_page(PAI_SAVE_AREA(event));
- if (event->cpu == -1) {
- struct cpumask *mask = PAI_CPU_MASK(event);
-
- for_each_cpu(cpu, mask)
- paicrypt_event_destroy_cpu(event, cpu);
- kfree(mask);
- } else {
- paicrypt_event_destroy_cpu(event, event->cpu);
- }
-}
-
-static u64 paicrypt_getctr(unsigned long *page, int nr, bool kernel)
-{
- if (kernel)
- nr += PAI_CRYPTO_MAXCTR;
- return page[nr];
-}
-
-/* Read the counter values. Return value from location in CMP. For event
- * CRYPTO_ALL sum up all events.
- */
-static u64 paicrypt_getdata(struct perf_event *event, bool kernel)
-{
- struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
- struct paicrypt_map *cpump = mp->mapptr;
- u64 sum = 0;
- int i;
-
- if (event->attr.config != PAI_CRYPTO_BASE) {
- return paicrypt_getctr(cpump->page,
- event->attr.config - PAI_CRYPTO_BASE,
- kernel);
- }
-
- for (i = 1; i <= paicrypt_cnt; i++) {
- u64 val = paicrypt_getctr(cpump->page, i, kernel);
-
- if (!val)
- continue;
- sum += val;
- }
- return sum;
-}
-
-static u64 paicrypt_getall(struct perf_event *event)
-{
- u64 sum = 0;
-
- if (!event->attr.exclude_kernel)
- sum += paicrypt_getdata(event, true);
- if (!event->attr.exclude_user)
- sum += paicrypt_getdata(event, false);
-
- return sum;
-}
-
-/* Check concurrent access of counting and sampling for crypto events.
- * This function is called in process context and it is save to block.
- * When the event initialization functions fails, no other call back will
- * be invoked.
- *
- * Allocate the memory for the event.
- */
-static int paicrypt_alloc_cpu(struct perf_event *event, int cpu)
-{
- struct paicrypt_map *cpump = NULL;
- struct paicrypt_mapptr *mp;
- int rc;
-
- mutex_lock(&pai_reserve_mutex);
- /* Allocate root node */
- rc = paicrypt_root_alloc();
- if (rc)
- goto unlock;
-
- /* Allocate node for this event */
- mp = per_cpu_ptr(paicrypt_root.mapptr, cpu);
- cpump = mp->mapptr;
- if (!cpump) { /* Paicrypt_map allocated? */
- rc = -ENOMEM;
- cpump = kzalloc(sizeof(*cpump), GFP_KERNEL);
- if (!cpump)
- goto undo;
- /* Allocate memory for counter page and counter extraction.
- * Only the first counting event has to allocate a page.
- */
- mp->mapptr = cpump;
- cpump->page = (unsigned long *)get_zeroed_page(GFP_KERNEL);
- cpump->save = kvmalloc_array(paicrypt_cnt + 1,
- sizeof(struct pai_userdata),
- GFP_KERNEL);
- if (!cpump->page || !cpump->save) {
- paicrypt_free(mp);
- goto undo;
- }
- INIT_LIST_HEAD(&cpump->syswide_list);
- refcount_set(&cpump->refcnt, 1);
- rc = 0;
- } else {
- refcount_inc(&cpump->refcnt);
- }
-
-undo:
- if (rc) {
- /* Error in allocation of event, decrement anchor. Since
- * the event in not created, its destroy() function is never
- * invoked. Adjust the reference counter for the anchor.
- */
- paicrypt_root_free();
- }
-unlock:
- mutex_unlock(&pai_reserve_mutex);
- return rc;
-}
-
-static int paicrypt_alloc(struct perf_event *event)
-{
- struct cpumask *maskptr;
- int cpu, rc = -ENOMEM;
-
- maskptr = kzalloc(sizeof(*maskptr), GFP_KERNEL);
- if (!maskptr)
- goto out;
-
- for_each_online_cpu(cpu) {
- rc = paicrypt_alloc_cpu(event, cpu);
- if (rc) {
- for_each_cpu(cpu, maskptr)
- paicrypt_event_destroy_cpu(event, cpu);
- kfree(maskptr);
- goto out;
- }
- cpumask_set_cpu(cpu, maskptr);
- }
-
- /*
- * On error all cpumask are freed and all events have been destroyed.
- * Save of which CPUs data structures have been allocated for.
- * Release them in paicrypt_event_destroy call back function
- * for this event.
- */
- PAI_CPU_MASK(event) = maskptr;
- rc = 0;
-out:
- return rc;
-}
-
-/* Might be called on different CPU than the one the event is intended for. */
-static int paicrypt_event_init(struct perf_event *event)
-{
- struct perf_event_attr *a = &event->attr;
- int rc = 0;
-
- /* PAI crypto PMU registered as PERF_TYPE_RAW, check event type */
- if (a->type != PERF_TYPE_RAW && event->pmu->type != a->type)
- return -ENOENT;
- /* PAI crypto event must be in valid range, try others if not */
- if (a->config < PAI_CRYPTO_BASE ||
- a->config > PAI_CRYPTO_BASE + paicrypt_cnt)
- return -ENOENT;
- /* Allow only CRYPTO_ALL for sampling */
- if (a->sample_period && a->config != PAI_CRYPTO_BASE)
- return -EINVAL;
- /* Get a page to store last counter values for sampling */
- if (a->sample_period) {
- PAI_SAVE_AREA(event) = get_zeroed_page(GFP_KERNEL);
- if (!PAI_SAVE_AREA(event)) {
- rc = -ENOMEM;
- goto out;
- }
- }
-
- if (event->cpu >= 0)
- rc = paicrypt_alloc_cpu(event, event->cpu);
- else
- rc = paicrypt_alloc(event);
- if (rc) {
- free_page(PAI_SAVE_AREA(event));
- goto out;
- }
- event->destroy = paicrypt_event_destroy;
-
- if (a->sample_period) {
- a->sample_period = 1;
- a->freq = 0;
- /* Register for paicrypt_sched_task() to be called */
- event->attach_state |= PERF_ATTACH_SCHED_CB;
- /* Add raw data which contain the memory mapped counters */
- a->sample_type |= PERF_SAMPLE_RAW;
- /* Turn off inheritance */
- a->inherit = 0;
- }
-
- static_branch_inc(&pai_key);
-out:
- return rc;
-}
-
-static void paicrypt_read(struct perf_event *event)
-{
- u64 prev, new, delta;
-
- prev = local64_read(&event->hw.prev_count);
- new = paicrypt_getall(event);
- local64_set(&event->hw.prev_count, new);
- delta = (prev <= new) ? new - prev
- : (-1ULL - prev) + new + 1; /* overflow */
- local64_add(delta, &event->count);
-}
-
-static void paicrypt_start(struct perf_event *event, int flags)
-{
- struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
- struct paicrypt_map *cpump = mp->mapptr;
- u64 sum;
-
- if (!event->attr.sample_period) { /* Counting */
- sum = paicrypt_getall(event); /* Get current value */
- local64_set(&event->hw.prev_count, sum);
- } else { /* Sampling */
- memcpy((void *)PAI_SAVE_AREA(event), cpump->page, PAGE_SIZE);
- /* Enable context switch callback for system-wide sampling */
- if (!(event->attach_state & PERF_ATTACH_TASK)) {
- list_add_tail(PAI_SWLIST(event), &cpump->syswide_list);
- perf_sched_cb_inc(event->pmu);
- } else {
- cpump->event = event;
- }
- }
-}
-
-static int paicrypt_add(struct perf_event *event, int flags)
-{
- struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
- struct paicrypt_map *cpump = mp->mapptr;
- unsigned long ccd;
-
- if (++cpump->active_events == 1) {
- ccd = virt_to_phys(cpump->page) | PAI_CRYPTO_KERNEL_OFFSET;
- WRITE_ONCE(get_lowcore()->ccd, ccd);
- local_ctl_set_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT);
- }
- if (flags & PERF_EF_START)
- paicrypt_start(event, PERF_EF_RELOAD);
- event->hw.state = 0;
- return 0;
-}
-
-static void paicrypt_have_sample(struct perf_event *, struct paicrypt_map *);
-static void paicrypt_stop(struct perf_event *event, int flags)
-{
- struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
- struct paicrypt_map *cpump = mp->mapptr;
-
- if (!event->attr.sample_period) { /* Counting */
- paicrypt_read(event);
- } else { /* Sampling */
- if (!(event->attach_state & PERF_ATTACH_TASK)) {
- perf_sched_cb_dec(event->pmu);
- list_del(PAI_SWLIST(event));
- } else {
- paicrypt_have_sample(event, cpump);
- cpump->event = NULL;
- }
- }
- event->hw.state = PERF_HES_STOPPED;
-}
-
-static void paicrypt_del(struct perf_event *event, int flags)
-{
- struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
- struct paicrypt_map *cpump = mp->mapptr;
-
- paicrypt_stop(event, PERF_EF_UPDATE);
- if (--cpump->active_events == 0) {
- local_ctl_clear_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT);
- WRITE_ONCE(get_lowcore()->ccd, 0);
- }
-}
-
-/* Create raw data and save it in buffer. Calculate the delta for each
- * counter between this invocation and the last invocation.
- * Returns number of bytes copied.
- * Saves only entries with positive counter difference of the form
- * 2 bytes: Number of counter
- * 8 bytes: Value of counter
- */
-static size_t paicrypt_copy(struct pai_userdata *userdata, unsigned long *page,
- unsigned long *page_old, bool exclude_user,
- bool exclude_kernel)
-{
- int i, outidx = 0;
-
- for (i = 1; i <= paicrypt_cnt; i++) {
- u64 val = 0, val_old = 0;
-
- if (!exclude_kernel) {
- val += paicrypt_getctr(page, i, true);
- val_old += paicrypt_getctr(page_old, i, true);
- }
- if (!exclude_user) {
- val += paicrypt_getctr(page, i, false);
- val_old += paicrypt_getctr(page_old, i, false);
- }
- if (val >= val_old)
- val -= val_old;
- else
- val = (~0ULL - val_old) + val + 1;
- if (val) {
- userdata[outidx].num = i;
- userdata[outidx].value = val;
- outidx++;
- }
- }
- return outidx * sizeof(struct pai_userdata);
-}
-
-static int paicrypt_push_sample(size_t rawsize, struct paicrypt_map *cpump,
- struct perf_event *event)
-{
- struct perf_sample_data data;
- struct perf_raw_record raw;
- struct pt_regs regs;
- int overflow;
-
- /* Setup perf sample */
- memset(&regs, 0, sizeof(regs));
- memset(&raw, 0, sizeof(raw));
- memset(&data, 0, sizeof(data));
- perf_sample_data_init(&data, 0, event->hw.last_period);
- if (event->attr.sample_type & PERF_SAMPLE_TID) {
- data.tid_entry.pid = task_tgid_nr(current);
- data.tid_entry.tid = task_pid_nr(current);
- }
- if (event->attr.sample_type & PERF_SAMPLE_TIME)
- data.time = event->clock();
- if (event->attr.sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER))
- data.id = event->id;
- if (event->attr.sample_type & PERF_SAMPLE_CPU) {
- data.cpu_entry.cpu = smp_processor_id();
- data.cpu_entry.reserved = 0;
- }
- if (event->attr.sample_type & PERF_SAMPLE_RAW) {
- raw.frag.size = rawsize;
- raw.frag.data = cpump->save;
- perf_sample_save_raw_data(&data, event, &raw);
- }
-
- overflow = perf_event_overflow(event, &data, &regs);
- perf_event_update_userpage(event);
- /* Save crypto counter lowcore page after reading event data. */
- memcpy((void *)PAI_SAVE_AREA(event), cpump->page, PAGE_SIZE);
- return overflow;
-}
-
-/* Check if there is data to be saved on schedule out of a task. */
-static void paicrypt_have_sample(struct perf_event *event,
- struct paicrypt_map *cpump)
-{
- size_t rawsize;
-
- if (!event) /* No event active */
- return;
- rawsize = paicrypt_copy(cpump->save, cpump->page,
- (unsigned long *)PAI_SAVE_AREA(event),
- event->attr.exclude_user,
- event->attr.exclude_kernel);
- if (rawsize) /* No incremented counters */
- paicrypt_push_sample(rawsize, cpump, event);
-}
-
-/* Check if there is data to be saved on schedule out of a task. */
-static void paicrypt_have_samples(void)
-{
- struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
- struct paicrypt_map *cpump = mp->mapptr;
- struct perf_event *event;
-
- list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
- paicrypt_have_sample(event, cpump);
-}
-
-/* Called on schedule-in and schedule-out. No access to event structure,
- * but for sampling only event CRYPTO_ALL is allowed.
- */
-static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx,
- struct task_struct *task, bool sched_in)
-{
- /* We started with a clean page on event installation. So read out
- * results on schedule_out and if page was dirty, save old values.
- */
- if (!sched_in)
- paicrypt_have_samples();
-}
-
-/* Attribute definitions for paicrypt interface. As with other CPU
- * Measurement Facilities, there is one attribute per mapped counter.
- * The number of mapped counters may vary per machine generation. Use
- * the QUERY PROCESSOR ACTIVITY COUNTER INFORMATION (QPACI) instruction
- * to determine the number of mapped counters. The instructions returns
- * a positive number, which is the highest number of supported counters.
- * All counters less than this number are also supported, there are no
- * holes. A returned number of zero means no support for mapped counters.
- *
- * The identification of the counter is a unique number. The chosen range
- * is 0x1000 + offset in mapped kernel page.
- * All CPU Measurement Facility counters identifiers must be unique and
- * the numbers from 0 to 496 are already used for the CPU Measurement
- * Counter facility. Numbers 0xb0000, 0xbc000 and 0xbd000 are already
- * used for the CPU Measurement Sampling facility.
- */
-PMU_FORMAT_ATTR(event, "config:0-63");
-
-static struct attribute *paicrypt_format_attr[] = {
- &format_attr_event.attr,
- NULL,
-};
-
-static struct attribute_group paicrypt_events_group = {
- .name = "events",
- .attrs = NULL /* Filled in attr_event_init() */
-};
-
-static struct attribute_group paicrypt_format_group = {
- .name = "format",
- .attrs = paicrypt_format_attr,
-};
-
-static const struct attribute_group *paicrypt_attr_groups[] = {
- &paicrypt_events_group,
- &paicrypt_format_group,
- NULL,
-};
-
-/* Performance monitoring unit for mapped counters */
-static struct pmu paicrypt = {
- .task_ctx_nr = perf_hw_context,
- .event_init = paicrypt_event_init,
- .add = paicrypt_add,
- .del = paicrypt_del,
- .start = paicrypt_start,
- .stop = paicrypt_stop,
- .read = paicrypt_read,
- .sched_task = paicrypt_sched_task,
- .attr_groups = paicrypt_attr_groups
-};
-
-/* List of symbolic PAI counter names. */
-static const char * const paicrypt_ctrnames[] = {
- [0] = "CRYPTO_ALL",
- [1] = "KM_DEA",
- [2] = "KM_TDEA_128",
- [3] = "KM_TDEA_192",
- [4] = "KM_ENCRYPTED_DEA",
- [5] = "KM_ENCRYPTED_TDEA_128",
- [6] = "KM_ENCRYPTED_TDEA_192",
- [7] = "KM_AES_128",
- [8] = "KM_AES_192",
- [9] = "KM_AES_256",
- [10] = "KM_ENCRYPTED_AES_128",
- [11] = "KM_ENCRYPTED_AES_192",
- [12] = "KM_ENCRYPTED_AES_256",
- [13] = "KM_XTS_AES_128",
- [14] = "KM_XTS_AES_256",
- [15] = "KM_XTS_ENCRYPTED_AES_128",
- [16] = "KM_XTS_ENCRYPTED_AES_256",
- [17] = "KMC_DEA",
- [18] = "KMC_TDEA_128",
- [19] = "KMC_TDEA_192",
- [20] = "KMC_ENCRYPTED_DEA",
- [21] = "KMC_ENCRYPTED_TDEA_128",
- [22] = "KMC_ENCRYPTED_TDEA_192",
- [23] = "KMC_AES_128",
- [24] = "KMC_AES_192",
- [25] = "KMC_AES_256",
- [26] = "KMC_ENCRYPTED_AES_128",
- [27] = "KMC_ENCRYPTED_AES_192",
- [28] = "KMC_ENCRYPTED_AES_256",
- [29] = "KMC_PRNG",
- [30] = "KMA_GCM_AES_128",
- [31] = "KMA_GCM_AES_192",
- [32] = "KMA_GCM_AES_256",
- [33] = "KMA_GCM_ENCRYPTED_AES_128",
- [34] = "KMA_GCM_ENCRYPTED_AES_192",
- [35] = "KMA_GCM_ENCRYPTED_AES_256",
- [36] = "KMF_DEA",
- [37] = "KMF_TDEA_128",
- [38] = "KMF_TDEA_192",
- [39] = "KMF_ENCRYPTED_DEA",
- [40] = "KMF_ENCRYPTED_TDEA_128",
- [41] = "KMF_ENCRYPTED_TDEA_192",
- [42] = "KMF_AES_128",
- [43] = "KMF_AES_192",
- [44] = "KMF_AES_256",
- [45] = "KMF_ENCRYPTED_AES_128",
- [46] = "KMF_ENCRYPTED_AES_192",
- [47] = "KMF_ENCRYPTED_AES_256",
- [48] = "KMCTR_DEA",
- [49] = "KMCTR_TDEA_128",
- [50] = "KMCTR_TDEA_192",
- [51] = "KMCTR_ENCRYPTED_DEA",
- [52] = "KMCTR_ENCRYPTED_TDEA_128",
- [53] = "KMCTR_ENCRYPTED_TDEA_192",
- [54] = "KMCTR_AES_128",
- [55] = "KMCTR_AES_192",
- [56] = "KMCTR_AES_256",
- [57] = "KMCTR_ENCRYPTED_AES_128",
- [58] = "KMCTR_ENCRYPTED_AES_192",
- [59] = "KMCTR_ENCRYPTED_AES_256",
- [60] = "KMO_DEA",
- [61] = "KMO_TDEA_128",
- [62] = "KMO_TDEA_192",
- [63] = "KMO_ENCRYPTED_DEA",
- [64] = "KMO_ENCRYPTED_TDEA_128",
- [65] = "KMO_ENCRYPTED_TDEA_192",
- [66] = "KMO_AES_128",
- [67] = "KMO_AES_192",
- [68] = "KMO_AES_256",
- [69] = "KMO_ENCRYPTED_AES_128",
- [70] = "KMO_ENCRYPTED_AES_192",
- [71] = "KMO_ENCRYPTED_AES_256",
- [72] = "KIMD_SHA_1",
- [73] = "KIMD_SHA_256",
- [74] = "KIMD_SHA_512",
- [75] = "KIMD_SHA3_224",
- [76] = "KIMD_SHA3_256",
- [77] = "KIMD_SHA3_384",
- [78] = "KIMD_SHA3_512",
- [79] = "KIMD_SHAKE_128",
- [80] = "KIMD_SHAKE_256",
- [81] = "KIMD_GHASH",
- [82] = "KLMD_SHA_1",
- [83] = "KLMD_SHA_256",
- [84] = "KLMD_SHA_512",
- [85] = "KLMD_SHA3_224",
- [86] = "KLMD_SHA3_256",
- [87] = "KLMD_SHA3_384",
- [88] = "KLMD_SHA3_512",
- [89] = "KLMD_SHAKE_128",
- [90] = "KLMD_SHAKE_256",
- [91] = "KMAC_DEA",
- [92] = "KMAC_TDEA_128",
- [93] = "KMAC_TDEA_192",
- [94] = "KMAC_ENCRYPTED_DEA",
- [95] = "KMAC_ENCRYPTED_TDEA_128",
- [96] = "KMAC_ENCRYPTED_TDEA_192",
- [97] = "KMAC_AES_128",
- [98] = "KMAC_AES_192",
- [99] = "KMAC_AES_256",
- [100] = "KMAC_ENCRYPTED_AES_128",
- [101] = "KMAC_ENCRYPTED_AES_192",
- [102] = "KMAC_ENCRYPTED_AES_256",
- [103] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_DEA",
- [104] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_TDEA_128",
- [105] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_TDEA_192",
- [106] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_DEA",
- [107] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_128",
- [108] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_TDEA_192",
- [109] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_128",
- [110] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_192",
- [111] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_AES_256",
- [112] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_128",
- [113] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_192",
- [114] = "PCC_COMPUTE_LAST_BLOCK_CMAC_USING_ENCRYPTED_AES_256",
- [115] = "PCC_COMPUTE_XTS_PARAMETER_USING_AES_128",
- [116] = "PCC_COMPUTE_XTS_PARAMETER_USING_AES_256",
- [117] = "PCC_COMPUTE_XTS_PARAMETER_USING_ENCRYPTED_AES_128",
- [118] = "PCC_COMPUTE_XTS_PARAMETER_USING_ENCRYPTED_AES_256",
- [119] = "PCC_SCALAR_MULTIPLY_P256",
- [120] = "PCC_SCALAR_MULTIPLY_P384",
- [121] = "PCC_SCALAR_MULTIPLY_P521",
- [122] = "PCC_SCALAR_MULTIPLY_ED25519",
- [123] = "PCC_SCALAR_MULTIPLY_ED448",
- [124] = "PCC_SCALAR_MULTIPLY_X25519",
- [125] = "PCC_SCALAR_MULTIPLY_X448",
- [126] = "PRNO_SHA_512_DRNG",
- [127] = "PRNO_TRNG_QUERY_RAW_TO_CONDITIONED_RATIO",
- [128] = "PRNO_TRNG",
- [129] = "KDSA_ECDSA_VERIFY_P256",
- [130] = "KDSA_ECDSA_VERIFY_P384",
- [131] = "KDSA_ECDSA_VERIFY_P521",
- [132] = "KDSA_ECDSA_SIGN_P256",
- [133] = "KDSA_ECDSA_SIGN_P384",
- [134] = "KDSA_ECDSA_SIGN_P521",
- [135] = "KDSA_ENCRYPTED_ECDSA_SIGN_P256",
- [136] = "KDSA_ENCRYPTED_ECDSA_SIGN_P384",
- [137] = "KDSA_ENCRYPTED_ECDSA_SIGN_P521",
- [138] = "KDSA_EDDSA_VERIFY_ED25519",
- [139] = "KDSA_EDDSA_VERIFY_ED448",
- [140] = "KDSA_EDDSA_SIGN_ED25519",
- [141] = "KDSA_EDDSA_SIGN_ED448",
- [142] = "KDSA_ENCRYPTED_EDDSA_SIGN_ED25519",
- [143] = "KDSA_ENCRYPTED_EDDSA_SIGN_ED448",
- [144] = "PCKMO_ENCRYPT_DEA_KEY",
- [145] = "PCKMO_ENCRYPT_TDEA_128_KEY",
- [146] = "PCKMO_ENCRYPT_TDEA_192_KEY",
- [147] = "PCKMO_ENCRYPT_AES_128_KEY",
- [148] = "PCKMO_ENCRYPT_AES_192_KEY",
- [149] = "PCKMO_ENCRYPT_AES_256_KEY",
- [150] = "PCKMO_ENCRYPT_ECC_P256_KEY",
- [151] = "PCKMO_ENCRYPT_ECC_P384_KEY",
- [152] = "PCKMO_ENCRYPT_ECC_P521_KEY",
- [153] = "PCKMO_ENCRYPT_ECC_ED25519_KEY",
- [154] = "PCKMO_ENCRYPT_ECC_ED448_KEY",
- [155] = "IBM_RESERVED_155",
- [156] = "IBM_RESERVED_156",
- [157] = "KM_FULL_XTS_AES_128",
- [158] = "KM_FULL_XTS_AES_256",
- [159] = "KM_FULL_XTS_ENCRYPTED_AES_128",
- [160] = "KM_FULL_XTS_ENCRYPTED_AES_256",
- [161] = "KMAC_HMAC_SHA_224",
- [162] = "KMAC_HMAC_SHA_256",
- [163] = "KMAC_HMAC_SHA_384",
- [164] = "KMAC_HMAC_SHA_512",
- [165] = "KMAC_HMAC_ENCRYPTED_SHA_224",
- [166] = "KMAC_HMAC_ENCRYPTED_SHA_256",
- [167] = "KMAC_HMAC_ENCRYPTED_SHA_384",
- [168] = "KMAC_HMAC_ENCRYPTED_SHA_512",
- [169] = "PCKMO_ENCRYPT_HMAC_512_KEY",
- [170] = "PCKMO_ENCRYPT_HMAC_1024_KEY",
- [171] = "PCKMO_ENCRYPT_AES_XTS_128",
- [172] = "PCKMO_ENCRYPT_AES_XTS_256",
-};
-
-static void __init attr_event_free(struct attribute **attrs, int num)
-{
- struct perf_pmu_events_attr *pa;
- int i;
-
- for (i = 0; i < num; i++) {
- struct device_attribute *dap;
-
- dap = container_of(attrs[i], struct device_attribute, attr);
- pa = container_of(dap, struct perf_pmu_events_attr, attr);
- kfree(pa);
- }
- kfree(attrs);
-}
-
-static int __init attr_event_init_one(struct attribute **attrs, int num)
-{
- struct perf_pmu_events_attr *pa;
-
- /* Index larger than array_size, no counter name available */
- if (num >= ARRAY_SIZE(paicrypt_ctrnames)) {
- attrs[num] = NULL;
- return 0;
- }
-
- pa = kzalloc(sizeof(*pa), GFP_KERNEL);
- if (!pa)
- return -ENOMEM;
-
- sysfs_attr_init(&pa->attr.attr);
- pa->id = PAI_CRYPTO_BASE + num;
- pa->attr.attr.name = paicrypt_ctrnames[num];
- pa->attr.attr.mode = 0444;
- pa->attr.show = cpumf_events_sysfs_show;
- pa->attr.store = NULL;
- attrs[num] = &pa->attr.attr;
- return 0;
-}
-
-/* Create PMU sysfs event attributes on the fly. */
-static int __init attr_event_init(void)
-{
- struct attribute **attrs;
- int ret, i;
-
- attrs = kmalloc_array(paicrypt_cnt + 2, sizeof(*attrs), GFP_KERNEL);
- if (!attrs)
- return -ENOMEM;
- for (i = 0; i <= paicrypt_cnt; i++) {
- ret = attr_event_init_one(attrs, i);
- if (ret) {
- attr_event_free(attrs, i);
- return ret;
- }
- }
- attrs[i] = NULL;
- paicrypt_events_group.attrs = attrs;
- return 0;
-}
-
-static int __init paicrypt_init(void)
-{
- struct qpaci_info_block ib;
- int rc;
-
- if (!test_facility(196))
- return 0;
-
- qpaci(&ib);
- paicrypt_cnt = ib.num_cc;
- if (paicrypt_cnt == 0)
- return 0;
- if (paicrypt_cnt >= PAI_CRYPTO_MAXCTR) {
- pr_err("Too many PMU pai_crypto counters %d\n", paicrypt_cnt);
- return -E2BIG;
- }
-
- rc = attr_event_init(); /* Export known PAI crypto events */
- if (rc) {
- pr_err("Creation of PMU pai_crypto /sysfs failed\n");
- return rc;
- }
-
- /* Setup s390dbf facility */
- cfm_dbg = debug_register(KMSG_COMPONENT, 2, 256, 128);
- if (!cfm_dbg) {
- pr_err("Registration of s390dbf pai_crypto failed\n");
- return -ENOMEM;
- }
- debug_register_view(cfm_dbg, &debug_sprintf_view);
-
- rc = perf_pmu_register(&paicrypt, "pai_crypto", -1);
- if (rc) {
- pr_err("Registering the pai_crypto PMU failed with rc=%i\n",
- rc);
- debug_unregister_view(cfm_dbg, &debug_sprintf_view);
- debug_unregister(cfm_dbg);
- return rc;
- }
- return 0;
-}
-
-device_initcall(paicrypt_init);
diff --git a/arch/s390/kernel/perf_pai_ext.c b/arch/s390/kernel/perf_pai_ext.c
deleted file mode 100644
index 7b32935273ce..000000000000
--- a/arch/s390/kernel/perf_pai_ext.c
+++ /dev/null
@@ -1,756 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Performance event support - Processor Activity Instrumentation Extension
- * Facility
- *
- * Copyright IBM Corp. 2022
- * Author(s): Thomas Richter <tmricht@linux.ibm.com>
- */
-#define KMSG_COMPONENT "pai_ext"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/kernel_stat.h>
-#include <linux/percpu.h>
-#include <linux/notifier.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/perf_event.h>
-#include <asm/ctlreg.h>
-#include <asm/pai.h>
-#include <asm/debug.h>
-
-#define PAIE1_CB_SZ 0x200 /* Size of PAIE1 control block */
-#define PAIE1_CTRBLOCK_SZ 0x400 /* Size of PAIE1 counter blocks */
-
-static debug_info_t *paiext_dbg;
-static unsigned int paiext_cnt; /* Extracted with QPACI instruction */
-
-struct pai_userdata {
- u16 num;
- u64 value;
-} __packed;
-
-/* Create the PAI extension 1 control block area.
- * The PAI extension control block 1 is pointed to by lowcore
- * address 0x1508 for each CPU. This control block is 512 bytes in size
- * and requires a 512 byte boundary alignment.
- */
-struct paiext_cb { /* PAI extension 1 control block */
- u64 header; /* Not used */
- u64 reserved1;
- u64 acc; /* Addr to analytics counter control block */
- u8 reserved2[488];
-} __packed;
-
-struct paiext_map {
- unsigned long *area; /* Area for CPU to store counters */
- struct pai_userdata *save; /* Area to store non-zero counters */
- unsigned int active_events; /* # of PAI Extension users */
- refcount_t refcnt;
- struct perf_event *event; /* Perf event for sampling */
- struct paiext_cb *paiext_cb; /* PAI extension control block area */
- struct list_head syswide_list; /* List system-wide sampling events */
-};
-
-struct paiext_mapptr {
- struct paiext_map *mapptr;
-};
-
-static struct paiext_root { /* Anchor to per CPU data */
- refcount_t refcnt; /* Overall active events */
- struct paiext_mapptr __percpu *mapptr;
-} paiext_root;
-
-/* Free per CPU data when the last event is removed. */
-static void paiext_root_free(void)
-{
- if (refcount_dec_and_test(&paiext_root.refcnt)) {
- free_percpu(paiext_root.mapptr);
- paiext_root.mapptr = NULL;
- }
- debug_sprintf_event(paiext_dbg, 5, "%s root.refcount %d\n", __func__,
- refcount_read(&paiext_root.refcnt));
-}
-
-/* On initialization of first event also allocate per CPU data dynamically.
- * Start with an array of pointers, the array size is the maximum number of
- * CPUs possible, which might be larger than the number of CPUs currently
- * online.
- */
-static int paiext_root_alloc(void)
-{
- if (!refcount_inc_not_zero(&paiext_root.refcnt)) {
- /* The memory is already zeroed. */
- paiext_root.mapptr = alloc_percpu(struct paiext_mapptr);
- if (!paiext_root.mapptr) {
- /* Returning without refcnt adjustment is ok. The
- * error code is handled by paiext_alloc() which
- * decrements refcnt when an event can not be
- * created.
- */
- return -ENOMEM;
- }
- refcount_set(&paiext_root.refcnt, 1);
- }
- return 0;
-}
-
-/* Protects against concurrent increment of sampler and counter member
- * increments at the same time and prohibits concurrent execution of
- * counting and sampling events.
- * Ensures that analytics counter block is deallocated only when the
- * sampling and counting on that cpu is zero.
- * For details see paiext_alloc().
- */
-static DEFINE_MUTEX(paiext_reserve_mutex);
-
-/* Free all memory allocated for event counting/sampling setup */
-static void paiext_free(struct paiext_mapptr *mp)
-{
- kfree(mp->mapptr->area);
- kfree(mp->mapptr->paiext_cb);
- kvfree(mp->mapptr->save);
- kfree(mp->mapptr);
- mp->mapptr = NULL;
-}
-
-/* Release the PMU if event is the last perf event */
-static void paiext_event_destroy_cpu(struct perf_event *event, int cpu)
-{
- struct paiext_mapptr *mp = per_cpu_ptr(paiext_root.mapptr, cpu);
- struct paiext_map *cpump = mp->mapptr;
-
- mutex_lock(&paiext_reserve_mutex);
- if (refcount_dec_and_test(&cpump->refcnt)) /* Last reference gone */
- paiext_free(mp);
- paiext_root_free();
- mutex_unlock(&paiext_reserve_mutex);
-}
-
-static void paiext_event_destroy(struct perf_event *event)
-{
- int cpu;
-
- free_page(PAI_SAVE_AREA(event));
- if (event->cpu == -1) {
- struct cpumask *mask = PAI_CPU_MASK(event);
-
- for_each_cpu(cpu, mask)
- paiext_event_destroy_cpu(event, cpu);
- kfree(mask);
- } else {
- paiext_event_destroy_cpu(event, event->cpu);
- }
- debug_sprintf_event(paiext_dbg, 4, "%s cpu %d\n", __func__,
- event->cpu);
-}
-
-/* Used to avoid races in checking concurrent access of counting and
- * sampling for pai_extension events.
- *
- * Only one instance of event pai_ext/NNPA_ALL/ for sampling is
- * allowed and when this event is running, no counting event is allowed.
- * Several counting events are allowed in parallel, but no sampling event
- * is allowed while one (or more) counting events are running.
- *
- * This function is called in process context and it is safe to block.
- * When the event initialization functions fails, no other call back will
- * be invoked.
- *
- * Allocate the memory for the event.
- */
-static int paiext_alloc_cpu(struct perf_event *event, int cpu)
-{
- struct paiext_mapptr *mp;
- struct paiext_map *cpump;
- int rc;
-
- mutex_lock(&paiext_reserve_mutex);
- rc = paiext_root_alloc();
- if (rc)
- goto unlock;
-
- mp = per_cpu_ptr(paiext_root.mapptr, cpu);
- cpump = mp->mapptr;
- if (!cpump) { /* Paiext_map allocated? */
- rc = -ENOMEM;
- cpump = kzalloc(sizeof(*cpump), GFP_KERNEL);
- if (!cpump)
- goto undo;
-
- /* Allocate memory for counter area and counter extraction.
- * These are
- * - a 512 byte block and requires 512 byte boundary alignment.
- * - a 1KB byte block and requires 1KB boundary alignment.
- * Only the first counting event has to allocate the area.
- *
- * Note: This works with commit 59bb47985c1d by default.
- * Backporting this to kernels without this commit might
- * need adjustment.
- */
- mp->mapptr = cpump;
- cpump->area = kzalloc(PAIE1_CTRBLOCK_SZ, GFP_KERNEL);
- cpump->paiext_cb = kzalloc(PAIE1_CB_SZ, GFP_KERNEL);
- cpump->save = kvmalloc_array(paiext_cnt + 1,
- sizeof(struct pai_userdata),
- GFP_KERNEL);
- if (!cpump->save || !cpump->area || !cpump->paiext_cb) {
- paiext_free(mp);
- goto undo;
- }
- INIT_LIST_HEAD(&cpump->syswide_list);
- refcount_set(&cpump->refcnt, 1);
- rc = 0;
- } else {
- refcount_inc(&cpump->refcnt);
- }
-
-undo:
- if (rc) {
- /* Error in allocation of event, decrement anchor. Since
- * the event in not created, its destroy() function is never
- * invoked. Adjust the reference counter for the anchor.
- */
- paiext_root_free();
- }
-unlock:
- mutex_unlock(&paiext_reserve_mutex);
- /* If rc is non-zero, no increment of counter/sampler was done. */
- return rc;
-}
-
-static int paiext_alloc(struct perf_event *event)
-{
- struct cpumask *maskptr;
- int cpu, rc = -ENOMEM;
-
- maskptr = kzalloc(sizeof(*maskptr), GFP_KERNEL);
- if (!maskptr)
- goto out;
-
- for_each_online_cpu(cpu) {
- rc = paiext_alloc_cpu(event, cpu);
- if (rc) {
- for_each_cpu(cpu, maskptr)
- paiext_event_destroy_cpu(event, cpu);
- kfree(maskptr);
- goto out;
- }
- cpumask_set_cpu(cpu, maskptr);
- }
-
- /*
- * On error all cpumask are freed and all events have been destroyed.
- * Save of which CPUs data structures have been allocated for.
- * Release them in paicrypt_event_destroy call back function
- * for this event.
- */
- PAI_CPU_MASK(event) = maskptr;
- rc = 0;
-out:
- return rc;
-}
-
-/* The PAI extension 1 control block supports up to 128 entries. Return
- * the index within PAIE1_CB given the event number. Also validate event
- * number.
- */
-static int paiext_event_valid(struct perf_event *event)
-{
- u64 cfg = event->attr.config;
-
- if (cfg >= PAI_NNPA_BASE && cfg <= PAI_NNPA_BASE + paiext_cnt) {
- /* Offset NNPA in paiext_cb */
- event->hw.config_base = offsetof(struct paiext_cb, acc);
- return 0;
- }
- return -ENOENT;
-}
-
-/* Might be called on different CPU than the one the event is intended for. */
-static int paiext_event_init(struct perf_event *event)
-{
- struct perf_event_attr *a = &event->attr;
- int rc;
-
- /* PMU pai_ext registered as PERF_TYPE_RAW, check event type */
- if (a->type != PERF_TYPE_RAW && event->pmu->type != a->type)
- return -ENOENT;
- /* PAI extension event must be valid and in supported range */
- rc = paiext_event_valid(event);
- if (rc)
- return rc;
- /* Allow only event NNPA_ALL for sampling. */
- if (a->sample_period && a->config != PAI_NNPA_BASE)
- return -EINVAL;
- /* Prohibit exclude_user event selection */
- if (a->exclude_user)
- return -EINVAL;
- /* Get a page to store last counter values for sampling */
- if (a->sample_period) {
- PAI_SAVE_AREA(event) = get_zeroed_page(GFP_KERNEL);
- if (!PAI_SAVE_AREA(event))
- return -ENOMEM;
- }
-
- if (event->cpu >= 0)
- rc = paiext_alloc_cpu(event, event->cpu);
- else
- rc = paiext_alloc(event);
- if (rc) {
- free_page(PAI_SAVE_AREA(event));
- return rc;
- }
- event->destroy = paiext_event_destroy;
-
- if (a->sample_period) {
- a->sample_period = 1;
- a->freq = 0;
- /* Register for paicrypt_sched_task() to be called */
- event->attach_state |= PERF_ATTACH_SCHED_CB;
- /* Add raw data which are the memory mapped counters */
- a->sample_type |= PERF_SAMPLE_RAW;
- /* Turn off inheritance */
- a->inherit = 0;
- }
-
- return 0;
-}
-
-static u64 paiext_getctr(unsigned long *area, int nr)
-{
- return area[nr];
-}
-
-/* Read the counter values. Return value from location in buffer. For event
- * NNPA_ALL sum up all events.
- */
-static u64 paiext_getdata(struct perf_event *event)
-{
- struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
- struct paiext_map *cpump = mp->mapptr;
- u64 sum = 0;
- int i;
-
- if (event->attr.config != PAI_NNPA_BASE)
- return paiext_getctr(cpump->area,
- event->attr.config - PAI_NNPA_BASE);
-
- for (i = 1; i <= paiext_cnt; i++)
- sum += paiext_getctr(cpump->area, i);
-
- return sum;
-}
-
-static u64 paiext_getall(struct perf_event *event)
-{
- return paiext_getdata(event);
-}
-
-static void paiext_read(struct perf_event *event)
-{
- u64 prev, new, delta;
-
- prev = local64_read(&event->hw.prev_count);
- new = paiext_getall(event);
- local64_set(&event->hw.prev_count, new);
- delta = new - prev;
- local64_add(delta, &event->count);
-}
-
-static void paiext_start(struct perf_event *event, int flags)
-{
- struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
- struct paiext_map *cpump = mp->mapptr;
- u64 sum;
-
- if (!event->attr.sample_period) { /* Counting */
- sum = paiext_getall(event); /* Get current value */
- local64_set(&event->hw.prev_count, sum);
- } else { /* Sampling */
- memcpy((void *)PAI_SAVE_AREA(event), cpump->area,
- PAIE1_CTRBLOCK_SZ);
- /* Enable context switch callback for system-wide sampling */
- if (!(event->attach_state & PERF_ATTACH_TASK)) {
- list_add_tail(PAI_SWLIST(event), &cpump->syswide_list);
- perf_sched_cb_inc(event->pmu);
- } else {
- cpump->event = event;
- }
- }
-}
-
-static int paiext_add(struct perf_event *event, int flags)
-{
- struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
- struct paiext_map *cpump = mp->mapptr;
- struct paiext_cb *pcb = cpump->paiext_cb;
-
- if (++cpump->active_events == 1) {
- get_lowcore()->aicd = virt_to_phys(cpump->paiext_cb);
- pcb->acc = virt_to_phys(cpump->area) | 0x1;
- /* Enable CPU instruction lookup for PAIE1 control block */
- local_ctl_set_bit(0, CR0_PAI_EXTENSION_BIT);
- }
- if (flags & PERF_EF_START)
- paiext_start(event, PERF_EF_RELOAD);
- event->hw.state = 0;
- return 0;
-}
-
-static void paiext_have_sample(struct perf_event *, struct paiext_map *);
-static void paiext_stop(struct perf_event *event, int flags)
-{
- struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
- struct paiext_map *cpump = mp->mapptr;
-
- if (!event->attr.sample_period) { /* Counting */
- paiext_read(event);
- } else { /* Sampling */
- if (!(event->attach_state & PERF_ATTACH_TASK)) {
- list_del(PAI_SWLIST(event));
- perf_sched_cb_dec(event->pmu);
- } else {
- paiext_have_sample(event, cpump);
- cpump->event = NULL;
- }
- }
- event->hw.state = PERF_HES_STOPPED;
-}
-
-static void paiext_del(struct perf_event *event, int flags)
-{
- struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
- struct paiext_map *cpump = mp->mapptr;
- struct paiext_cb *pcb = cpump->paiext_cb;
-
- paiext_stop(event, PERF_EF_UPDATE);
- if (--cpump->active_events == 0) {
- /* Disable CPU instruction lookup for PAIE1 control block */
- local_ctl_clear_bit(0, CR0_PAI_EXTENSION_BIT);
- pcb->acc = 0;
- get_lowcore()->aicd = 0;
- }
-}
-
-/* Create raw data and save it in buffer. Returns number of bytes copied.
- * Saves only positive counter entries of the form
- * 2 bytes: Number of counter
- * 8 bytes: Value of counter
- */
-static size_t paiext_copy(struct pai_userdata *userdata, unsigned long *area,
- unsigned long *area_old)
-{
- int i, outidx = 0;
-
- for (i = 1; i <= paiext_cnt; i++) {
- u64 val = paiext_getctr(area, i);
- u64 val_old = paiext_getctr(area_old, i);
-
- if (val >= val_old)
- val -= val_old;
- else
- val = (~0ULL - val_old) + val + 1;
- if (val) {
- userdata[outidx].num = i;
- userdata[outidx].value = val;
- outidx++;
- }
- }
- return outidx * sizeof(*userdata);
-}
-
-/* Write sample when one or more counters values are nonzero.
- *
- * Note: The function paiext_sched_task() and paiext_push_sample() are not
- * invoked after function paiext_del() has been called because of function
- * perf_sched_cb_dec().
- * The function paiext_sched_task() and paiext_push_sample() are only
- * called when sampling is active. Function perf_sched_cb_inc()
- * has been invoked to install function paiext_sched_task() as call back
- * to run at context switch time (see paiext_add()).
- *
- * This causes function perf_event_context_sched_out() and
- * perf_event_context_sched_in() to check whether the PMU has installed an
- * sched_task() callback. That callback is not active after paiext_del()
- * returns and has deleted the event on that CPU.
- */
-static int paiext_push_sample(size_t rawsize, struct paiext_map *cpump,
- struct perf_event *event)
-{
- struct perf_sample_data data;
- struct perf_raw_record raw;
- struct pt_regs regs;
- int overflow;
-
- /* Setup perf sample */
- memset(&regs, 0, sizeof(regs));
- memset(&raw, 0, sizeof(raw));
- memset(&data, 0, sizeof(data));
- perf_sample_data_init(&data, 0, event->hw.last_period);
- if (event->attr.sample_type & PERF_SAMPLE_TID) {
- data.tid_entry.pid = task_tgid_nr(current);
- data.tid_entry.tid = task_pid_nr(current);
- }
- if (event->attr.sample_type & PERF_SAMPLE_TIME)
- data.time = event->clock();
- if (event->attr.sample_type & (PERF_SAMPLE_ID | PERF_SAMPLE_IDENTIFIER))
- data.id = event->id;
- if (event->attr.sample_type & PERF_SAMPLE_CPU)
- data.cpu_entry.cpu = smp_processor_id();
- if (event->attr.sample_type & PERF_SAMPLE_RAW) {
- raw.frag.size = rawsize;
- raw.frag.data = cpump->save;
- perf_sample_save_raw_data(&data, event, &raw);
- }
-
- overflow = perf_event_overflow(event, &data, &regs);
- perf_event_update_userpage(event);
- /* Save NNPA lowcore area after read in event */
- memcpy((void *)PAI_SAVE_AREA(event), cpump->area,
- PAIE1_CTRBLOCK_SZ);
- return overflow;
-}
-
-/* Check if there is data to be saved on schedule out of a task. */
-static void paiext_have_sample(struct perf_event *event,
- struct paiext_map *cpump)
-{
- size_t rawsize;
-
- if (!event)
- return;
- rawsize = paiext_copy(cpump->save, cpump->area,
- (unsigned long *)PAI_SAVE_AREA(event));
- if (rawsize) /* Incremented counters */
- paiext_push_sample(rawsize, cpump, event);
-}
-
-/* Check if there is data to be saved on schedule out of a task. */
-static void paiext_have_samples(void)
-{
- struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
- struct paiext_map *cpump = mp->mapptr;
- struct perf_event *event;
-
- list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
- paiext_have_sample(event, cpump);
-}
-
-/* Called on schedule-in and schedule-out. No access to event structure,
- * but for sampling only event NNPA_ALL is allowed.
- */
-static void paiext_sched_task(struct perf_event_pmu_context *pmu_ctx,
- struct task_struct *task, bool sched_in)
-{
- /* We started with a clean page on event installation. So read out
- * results on schedule_out and if page was dirty, save old values.
- */
- if (!sched_in)
- paiext_have_samples();
-}
-
-/* Attribute definitions for pai extension1 interface. As with other CPU
- * Measurement Facilities, there is one attribute per mapped counter.
- * The number of mapped counters may vary per machine generation. Use
- * the QUERY PROCESSOR ACTIVITY COUNTER INFORMATION (QPACI) instruction
- * to determine the number of mapped counters. The instructions returns
- * a positive number, which is the highest number of supported counters.
- * All counters less than this number are also supported, there are no
- * holes. A returned number of zero means no support for mapped counters.
- *
- * The identification of the counter is a unique number. The chosen range
- * is 0x1800 + offset in mapped kernel page.
- * All CPU Measurement Facility counters identifiers must be unique and
- * the numbers from 0 to 496 are already used for the CPU Measurement
- * Counter facility. Number 0x1000 to 0x103e are used for PAI cryptography
- * counters.
- * Numbers 0xb0000, 0xbc000 and 0xbd000 are already
- * used for the CPU Measurement Sampling facility.
- */
-PMU_FORMAT_ATTR(event, "config:0-63");
-
-static struct attribute *paiext_format_attr[] = {
- &format_attr_event.attr,
- NULL,
-};
-
-static struct attribute_group paiext_events_group = {
- .name = "events",
- .attrs = NULL, /* Filled in attr_event_init() */
-};
-
-static struct attribute_group paiext_format_group = {
- .name = "format",
- .attrs = paiext_format_attr,
-};
-
-static const struct attribute_group *paiext_attr_groups[] = {
- &paiext_events_group,
- &paiext_format_group,
- NULL,
-};
-
-/* Performance monitoring unit for mapped counters */
-static struct pmu paiext = {
- .task_ctx_nr = perf_hw_context,
- .event_init = paiext_event_init,
- .add = paiext_add,
- .del = paiext_del,
- .start = paiext_start,
- .stop = paiext_stop,
- .read = paiext_read,
- .sched_task = paiext_sched_task,
- .attr_groups = paiext_attr_groups,
-};
-
-/* List of symbolic PAI extension 1 NNPA counter names. */
-static const char * const paiext_ctrnames[] = {
- [0] = "NNPA_ALL",
- [1] = "NNPA_ADD",
- [2] = "NNPA_SUB",
- [3] = "NNPA_MUL",
- [4] = "NNPA_DIV",
- [5] = "NNPA_MIN",
- [6] = "NNPA_MAX",
- [7] = "NNPA_LOG",
- [8] = "NNPA_EXP",
- [9] = "NNPA_IBM_RESERVED_9",
- [10] = "NNPA_RELU",
- [11] = "NNPA_TANH",
- [12] = "NNPA_SIGMOID",
- [13] = "NNPA_SOFTMAX",
- [14] = "NNPA_BATCHNORM",
- [15] = "NNPA_MAXPOOL2D",
- [16] = "NNPA_AVGPOOL2D",
- [17] = "NNPA_LSTMACT",
- [18] = "NNPA_GRUACT",
- [19] = "NNPA_CONVOLUTION",
- [20] = "NNPA_MATMUL_OP",
- [21] = "NNPA_MATMUL_OP_BCAST23",
- [22] = "NNPA_SMALLBATCH",
- [23] = "NNPA_LARGEDIM",
- [24] = "NNPA_SMALLTENSOR",
- [25] = "NNPA_1MFRAME",
- [26] = "NNPA_2GFRAME",
- [27] = "NNPA_ACCESSEXCEPT",
- [28] = "NNPA_TRANSFORM",
- [29] = "NNPA_GELU",
- [30] = "NNPA_MOMENTS",
- [31] = "NNPA_LAYERNORM",
- [32] = "NNPA_MATMUL_OP_BCAST1",
- [33] = "NNPA_SQRT",
- [34] = "NNPA_INVSQRT",
- [35] = "NNPA_NORM",
- [36] = "NNPA_REDUCE",
-};
-
-static void __init attr_event_free(struct attribute **attrs, int num)
-{
- struct perf_pmu_events_attr *pa;
- struct device_attribute *dap;
- int i;
-
- for (i = 0; i < num; i++) {
- dap = container_of(attrs[i], struct device_attribute, attr);
- pa = container_of(dap, struct perf_pmu_events_attr, attr);
- kfree(pa);
- }
- kfree(attrs);
-}
-
-static int __init attr_event_init_one(struct attribute **attrs, int num)
-{
- struct perf_pmu_events_attr *pa;
-
- /* Index larger than array_size, no counter name available */
- if (num >= ARRAY_SIZE(paiext_ctrnames)) {
- attrs[num] = NULL;
- return 0;
- }
-
- pa = kzalloc(sizeof(*pa), GFP_KERNEL);
- if (!pa)
- return -ENOMEM;
-
- sysfs_attr_init(&pa->attr.attr);
- pa->id = PAI_NNPA_BASE + num;
- pa->attr.attr.name = paiext_ctrnames[num];
- pa->attr.attr.mode = 0444;
- pa->attr.show = cpumf_events_sysfs_show;
- pa->attr.store = NULL;
- attrs[num] = &pa->attr.attr;
- return 0;
-}
-
-/* Create PMU sysfs event attributes on the fly. */
-static int __init attr_event_init(void)
-{
- struct attribute **attrs;
- int ret, i;
-
- attrs = kmalloc_array(paiext_cnt + 2, sizeof(*attrs), GFP_KERNEL);
- if (!attrs)
- return -ENOMEM;
- for (i = 0; i <= paiext_cnt; i++) {
- ret = attr_event_init_one(attrs, i);
- if (ret) {
- attr_event_free(attrs, i);
- return ret;
- }
- }
- attrs[i] = NULL;
- paiext_events_group.attrs = attrs;
- return 0;
-}
-
-static int __init paiext_init(void)
-{
- struct qpaci_info_block ib;
- int rc = -ENOMEM;
-
- if (!test_facility(197))
- return 0;
-
- qpaci(&ib);
- paiext_cnt = ib.num_nnpa;
- if (paiext_cnt >= PAI_NNPA_MAXCTR)
- paiext_cnt = PAI_NNPA_MAXCTR;
- if (!paiext_cnt)
- return 0;
-
- rc = attr_event_init();
- if (rc) {
- pr_err("Creation of PMU " KMSG_COMPONENT " /sysfs failed\n");
- return rc;
- }
-
- /* Setup s390dbf facility */
- paiext_dbg = debug_register(KMSG_COMPONENT, 2, 256, 128);
- if (!paiext_dbg) {
- pr_err("Registration of s390dbf " KMSG_COMPONENT " failed\n");
- rc = -ENOMEM;
- goto out_init;
- }
- debug_register_view(paiext_dbg, &debug_sprintf_view);
-
- rc = perf_pmu_register(&paiext, KMSG_COMPONENT, -1);
- if (rc) {
- pr_err("Registration of " KMSG_COMPONENT " PMU failed with "
- "rc=%i\n", rc);
- goto out_pmu;
- }
-
- return 0;
-
-out_pmu:
- debug_unregister_view(paiext_dbg, &debug_sprintf_view);
- debug_unregister(paiext_dbg);
-out_init:
- attr_event_free(paiext_events_group.attrs,
- ARRAY_SIZE(paiext_ctrnames) + 1);
- return rc;
-}
-
-device_initcall(paiext_init);
diff --git a/arch/s390/kernel/perf_regs.c b/arch/s390/kernel/perf_regs.c
index a6b058ee4a36..7b305f1456f8 100644
--- a/arch/s390/kernel/perf_regs.c
+++ b/arch/s390/kernel/perf_regs.c
@@ -44,9 +44,6 @@ int perf_reg_validate(u64 mask)
u64 perf_reg_abi(struct task_struct *task)
{
- if (test_tsk_thread_flag(task, TIF_31BIT))
- return PERF_SAMPLE_REGS_ABI_32;
-
return PERF_SAMPLE_REGS_ABI_64;
}
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index b107dbca4ed7..0df95dcb2101 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -24,7 +24,6 @@
#include <linux/tick.h>
#include <linux/personality.h>
#include <linux/syscalls.h>
-#include <linux/compat.h>
#include <linux/kprobes.h>
#include <linux/random.h>
#include <linux/init_task.h>
@@ -166,12 +165,8 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
/* Set a new TLS ? */
if (clone_flags & CLONE_SETTLS) {
- if (is_compat_task()) {
- p->thread.acrs[0] = (unsigned int)tls;
- } else {
- p->thread.acrs[0] = (unsigned int)(tls >> 32);
- p->thread.acrs[1] = (unsigned int)tls;
- }
+ p->thread.acrs[0] = (unsigned int)(tls >> 32);
+ p->thread.acrs[1] = (unsigned int)tls;
}
/*
* s390 stores the svc return address in arch_data when calling
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
index 11f70c1e2797..e33a3eccda56 100644
--- a/arch/s390/kernel/processor.c
+++ b/arch/s390/kernel/processor.c
@@ -4,8 +4,7 @@
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
-#define KMSG_COMPONENT "cpu"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "cpu: " fmt
#include <linux/stop_machine.h>
#include <linux/cpufeature.h>
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 494216c4b4f3..ceaa1726e328 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -22,7 +22,6 @@
#include <linux/elf.h>
#include <linux/regset.h>
#include <linux/seccomp.h>
-#include <linux/compat.h>
#include <trace/syscall.h>
#include <asm/guarded_storage.h>
#include <asm/access-regs.h>
@@ -38,10 +37,6 @@
#include "entry.h"
-#ifdef CONFIG_COMPAT
-#include "compat_ptrace.h"
-#endif
-
void update_cr_regs(struct task_struct *task)
{
struct pt_regs *regs = task_pt_regs(task);
@@ -507,308 +502,6 @@ long arch_ptrace(struct task_struct *child, long request,
}
}
-#ifdef CONFIG_COMPAT
-/*
- * Now the fun part starts... a 31 bit program running in the
- * 31 bit emulation tracing another program. PTRACE_PEEKTEXT,
- * PTRACE_PEEKDATA, PTRACE_POKETEXT and PTRACE_POKEDATA are easy
- * to handle, the difference to the 64 bit versions of the requests
- * is that the access is done in multiples of 4 byte instead of
- * 8 bytes (sizeof(unsigned long) on 31/64 bit).
- * The ugly part are PTRACE_PEEKUSR, PTRACE_PEEKUSR_AREA,
- * PTRACE_POKEUSR and PTRACE_POKEUSR_AREA. If the traced program
- * is a 31 bit program too, the content of struct user can be
- * emulated. A 31 bit program peeking into the struct user of
- * a 64 bit program is a no-no.
- */
-
-/*
- * Same as peek_user_per but for a 31 bit program.
- */
-static inline __u32 __peek_user_per_compat(struct task_struct *child,
- addr_t addr)
-{
- if (addr == offsetof(struct compat_per_struct_kernel, cr9))
- /* Control bits of the active per set. */
- return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
- PER_EVENT_IFETCH : child->thread.per_user.control;
- else if (addr == offsetof(struct compat_per_struct_kernel, cr10))
- /* Start address of the active per set. */
- return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
- 0 : child->thread.per_user.start;
- else if (addr == offsetof(struct compat_per_struct_kernel, cr11))
- /* End address of the active per set. */
- return test_thread_flag(TIF_SINGLE_STEP) ?
- PSW32_ADDR_INSN : child->thread.per_user.end;
- else if (addr == offsetof(struct compat_per_struct_kernel, bits))
- /* Single-step bit. */
- return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
- 0x80000000 : 0;
- else if (addr == offsetof(struct compat_per_struct_kernel, starting_addr))
- /* Start address of the user specified per set. */
- return (__u32) child->thread.per_user.start;
- else if (addr == offsetof(struct compat_per_struct_kernel, ending_addr))
- /* End address of the user specified per set. */
- return (__u32) child->thread.per_user.end;
- else if (addr == offsetof(struct compat_per_struct_kernel, perc_atmid))
- /* PER code, ATMID and AI of the last PER trap */
- return (__u32) child->thread.per_event.cause << 16;
- else if (addr == offsetof(struct compat_per_struct_kernel, address))
- /* Address of the last PER trap */
- return (__u32) child->thread.per_event.address;
- else if (addr == offsetof(struct compat_per_struct_kernel, access_id))
- /* Access id of the last PER trap */
- return (__u32) child->thread.per_event.paid << 24;
- return 0;
-}
-
-/*
- * Same as peek_user but for a 31 bit program.
- */
-static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
-{
- addr_t offset;
- __u32 tmp;
-
- if (addr < offsetof(struct compat_user, regs.acrs)) {
- struct pt_regs *regs = task_pt_regs(child);
- /*
- * psw and gprs are stored on the stack
- */
- if (addr == offsetof(struct compat_user, regs.psw.mask)) {
- /* Fake a 31 bit psw mask. */
- tmp = (__u32)(regs->psw.mask >> 32);
- tmp &= PSW32_MASK_USER | PSW32_MASK_RI;
- tmp |= PSW32_USER_BITS;
- } else if (addr == offsetof(struct compat_user, regs.psw.addr)) {
- /* Fake a 31 bit psw address. */
- tmp = (__u32) regs->psw.addr |
- (__u32)(regs->psw.mask & PSW_MASK_BA);
- } else {
- /* gpr 0-15 */
- tmp = *(__u32 *)((addr_t) &regs->psw + addr*2 + 4);
- }
- } else if (addr < offsetof(struct compat_user, regs.orig_gpr2)) {
- /*
- * access registers are stored in the thread structure
- */
- offset = addr - offsetof(struct compat_user, regs.acrs);
- tmp = *(__u32*)((addr_t) &child->thread.acrs + offset);
-
- } else if (addr == offsetof(struct compat_user, regs.orig_gpr2)) {
- /*
- * orig_gpr2 is stored on the kernel stack
- */
- tmp = *(__u32*)((addr_t) &task_pt_regs(child)->orig_gpr2 + 4);
-
- } else if (addr < offsetof(struct compat_user, regs.fp_regs)) {
- /*
- * prevent reads of padding hole between
- * orig_gpr2 and fp_regs on s390.
- */
- tmp = 0;
-
- } else if (addr == offsetof(struct compat_user, regs.fp_regs.fpc)) {
- /*
- * floating point control reg. is in the thread structure
- */
- tmp = child->thread.ufpu.fpc;
-
- } else if (addr < offsetof(struct compat_user, regs.fp_regs) + sizeof(s390_fp_regs)) {
- /*
- * floating point regs. are in the child->thread.ufpu.vxrs array
- */
- offset = addr - offsetof(struct compat_user, regs.fp_regs.fprs);
- tmp = *(__u32 *)((addr_t)child->thread.ufpu.vxrs + 2 * offset);
- } else if (addr < offsetof(struct compat_user, regs.per_info) + sizeof(struct compat_per_struct_kernel)) {
- /*
- * Handle access to the per_info structure.
- */
- addr -= offsetof(struct compat_user, regs.per_info);
- tmp = __peek_user_per_compat(child, addr);
-
- } else
- tmp = 0;
-
- return tmp;
-}
-
-static int peek_user_compat(struct task_struct *child,
- addr_t addr, addr_t data)
-{
- __u32 tmp;
-
- if (!is_compat_task() || (addr & 3) || addr > sizeof(struct user) - 3)
- return -EIO;
-
- tmp = __peek_user_compat(child, addr);
- return put_user(tmp, (__u32 __user *) data);
-}
-
-/*
- * Same as poke_user_per but for a 31 bit program.
- */
-static inline void __poke_user_per_compat(struct task_struct *child,
- addr_t addr, __u32 data)
-{
- if (addr == offsetof(struct compat_per_struct_kernel, cr9))
- /* PER event mask of the user specified per set. */
- child->thread.per_user.control =
- data & (PER_EVENT_MASK | PER_CONTROL_MASK);
- else if (addr == offsetof(struct compat_per_struct_kernel, starting_addr))
- /* Starting address of the user specified per set. */
- child->thread.per_user.start = data;
- else if (addr == offsetof(struct compat_per_struct_kernel, ending_addr))
- /* Ending address of the user specified per set. */
- child->thread.per_user.end = data;
-}
-
-/*
- * Same as poke_user but for a 31 bit program.
- */
-static int __poke_user_compat(struct task_struct *child,
- addr_t addr, addr_t data)
-{
- __u32 tmp = (__u32) data;
- addr_t offset;
-
- if (addr < offsetof(struct compat_user, regs.acrs)) {
- struct pt_regs *regs = task_pt_regs(child);
- /*
- * psw, gprs, acrs and orig_gpr2 are stored on the stack
- */
- if (addr == offsetof(struct compat_user, regs.psw.mask)) {
- __u32 mask = PSW32_MASK_USER;
-
- mask |= is_ri_task(child) ? PSW32_MASK_RI : 0;
- /* Build a 64 bit psw mask from 31 bit mask. */
- if ((tmp ^ PSW32_USER_BITS) & ~mask)
- /* Invalid psw mask. */
- return -EINVAL;
- if ((data & PSW32_MASK_ASC) == PSW32_ASC_HOME)
- /* Invalid address-space-control bits */
- return -EINVAL;
- regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
- (regs->psw.mask & PSW_MASK_BA) |
- (__u64)(tmp & mask) << 32;
- } else if (addr == offsetof(struct compat_user, regs.psw.addr)) {
- /* Build a 64 bit psw address from 31 bit address. */
- regs->psw.addr = (__u64) tmp & PSW32_ADDR_INSN;
- /* Transfer 31 bit amode bit to psw mask. */
- regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
- (__u64)(tmp & PSW32_ADDR_AMODE);
- } else {
- if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
- addr == offsetof(struct compat_user, regs.gprs[2])) {
- struct pt_regs *regs = task_pt_regs(child);
-
- regs->int_code = 0x20000 | (data & 0xffff);
- }
- /* gpr 0-15 */
- *(__u32*)((addr_t) &regs->psw + addr*2 + 4) = tmp;
- }
- } else if (addr < offsetof(struct compat_user, regs.orig_gpr2)) {
- /*
- * access registers are stored in the thread structure
- */
- offset = addr - offsetof(struct compat_user, regs.acrs);
- *(__u32*)((addr_t) &child->thread.acrs + offset) = tmp;
-
- } else if (addr == offsetof(struct compat_user, regs.orig_gpr2)) {
- /*
- * orig_gpr2 is stored on the kernel stack
- */
- *(__u32*)((addr_t) &task_pt_regs(child)->orig_gpr2 + 4) = tmp;
-
- } else if (addr < offsetof(struct compat_user, regs.fp_regs)) {
- /*
- * prevent writess of padding hole between
- * orig_gpr2 and fp_regs on s390.
- */
- return 0;
-
- } else if (addr == offsetof(struct compat_user, regs.fp_regs.fpc)) {
- /*
- * floating point control reg. is in the thread structure
- */
- child->thread.ufpu.fpc = data;
-
- } else if (addr < offsetof(struct compat_user, regs.fp_regs) + sizeof(s390_fp_regs)) {
- /*
- * floating point regs. are in the child->thread.ufpu.vxrs array
- */
- offset = addr - offsetof(struct compat_user, regs.fp_regs.fprs);
- *(__u32 *)((addr_t)child->thread.ufpu.vxrs + 2 * offset) = tmp;
- } else if (addr < offsetof(struct compat_user, regs.per_info) + sizeof(struct compat_per_struct_kernel)) {
- /*
- * Handle access to the per_info structure.
- */
- addr -= offsetof(struct compat_user, regs.per_info);
- __poke_user_per_compat(child, addr, data);
- }
-
- return 0;
-}
-
-static int poke_user_compat(struct task_struct *child,
- addr_t addr, addr_t data)
-{
- if (!is_compat_task() || (addr & 3) ||
- addr > sizeof(struct compat_user) - 3)
- return -EIO;
-
- return __poke_user_compat(child, addr, data);
-}
-
-long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
- compat_ulong_t caddr, compat_ulong_t cdata)
-{
- unsigned long addr = caddr;
- unsigned long data = cdata;
- compat_ptrace_area parea;
- int copied, ret;
-
- switch (request) {
- case PTRACE_PEEKUSR:
- /* read the word at location addr in the USER area. */
- return peek_user_compat(child, addr, data);
-
- case PTRACE_POKEUSR:
- /* write the word at location addr in the USER area */
- return poke_user_compat(child, addr, data);
-
- case PTRACE_PEEKUSR_AREA:
- case PTRACE_POKEUSR_AREA:
- if (copy_from_user(&parea, (void __force __user *) addr,
- sizeof(parea)))
- return -EFAULT;
- addr = parea.kernel_addr;
- data = parea.process_addr;
- copied = 0;
- while (copied < parea.len) {
- if (request == PTRACE_PEEKUSR_AREA)
- ret = peek_user_compat(child, addr, data);
- else {
- __u32 utmp;
- if (get_user(utmp,
- (__u32 __force __user *) data))
- return -EFAULT;
- ret = poke_user_compat(child, addr, utmp);
- }
- if (ret)
- return ret;
- addr += sizeof(unsigned int);
- data += sizeof(unsigned int);
- copied += sizeof(unsigned int);
- }
- return 0;
- case PTRACE_GET_LAST_BREAK:
- return put_user(child->thread.last_break, (unsigned int __user *)data);
- }
- return compat_ptrace_request(child, request, addr, data);
-}
-#endif
-
/*
* user_regset definitions.
*/
@@ -1297,225 +990,8 @@ static const struct user_regset_view user_s390_view = {
.n = ARRAY_SIZE(s390_regsets)
};
-#ifdef CONFIG_COMPAT
-static int s390_compat_regs_get(struct task_struct *target,
- const struct user_regset *regset,
- struct membuf to)
-{
- unsigned n;
-
- if (target == current)
- save_access_regs(target->thread.acrs);
-
- for (n = 0; n < sizeof(s390_compat_regs); n += sizeof(compat_ulong_t))
- membuf_store(&to, __peek_user_compat(target, n));
- return 0;
-}
-
-static int s390_compat_regs_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
-{
- int rc = 0;
-
- if (target == current)
- save_access_regs(target->thread.acrs);
-
- if (kbuf) {
- const compat_ulong_t *k = kbuf;
- while (count > 0 && !rc) {
- rc = __poke_user_compat(target, pos, *k++);
- count -= sizeof(*k);
- pos += sizeof(*k);
- }
- } else {
- const compat_ulong_t __user *u = ubuf;
- while (count > 0 && !rc) {
- compat_ulong_t word;
- rc = __get_user(word, u++);
- if (rc)
- break;
- rc = __poke_user_compat(target, pos, word);
- count -= sizeof(*u);
- pos += sizeof(*u);
- }
- }
-
- if (rc == 0 && target == current)
- restore_access_regs(target->thread.acrs);
-
- return rc;
-}
-
-static int s390_compat_regs_high_get(struct task_struct *target,
- const struct user_regset *regset,
- struct membuf to)
-{
- compat_ulong_t *gprs_high;
- int i;
-
- gprs_high = (compat_ulong_t *)task_pt_regs(target)->gprs;
- for (i = 0; i < NUM_GPRS; i++, gprs_high += 2)
- membuf_store(&to, *gprs_high);
- return 0;
-}
-
-static int s390_compat_regs_high_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
-{
- compat_ulong_t *gprs_high;
- int rc = 0;
-
- gprs_high = (compat_ulong_t *)
- &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)];
- if (kbuf) {
- const compat_ulong_t *k = kbuf;
- while (count > 0) {
- *gprs_high = *k++;
- *gprs_high += 2;
- count -= sizeof(*k);
- }
- } else {
- const compat_ulong_t __user *u = ubuf;
- while (count > 0 && !rc) {
- unsigned long word;
- rc = __get_user(word, u++);
- if (rc)
- break;
- *gprs_high = word;
- *gprs_high += 2;
- count -= sizeof(*u);
- }
- }
-
- return rc;
-}
-
-static int s390_compat_last_break_get(struct task_struct *target,
- const struct user_regset *regset,
- struct membuf to)
-{
- compat_ulong_t last_break = target->thread.last_break;
-
- return membuf_store(&to, (unsigned long)last_break);
-}
-
-static int s390_compat_last_break_set(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
-{
- return 0;
-}
-
-static const struct user_regset s390_compat_regsets[] = {
- {
- USER_REGSET_NOTE_TYPE(PRSTATUS),
- .n = sizeof(s390_compat_regs) / sizeof(compat_long_t),
- .size = sizeof(compat_long_t),
- .align = sizeof(compat_long_t),
- .regset_get = s390_compat_regs_get,
- .set = s390_compat_regs_set,
- },
- {
- USER_REGSET_NOTE_TYPE(PRFPREG),
- .n = sizeof(s390_fp_regs) / sizeof(compat_long_t),
- .size = sizeof(compat_long_t),
- .align = sizeof(compat_long_t),
- .regset_get = s390_fpregs_get,
- .set = s390_fpregs_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_SYSTEM_CALL),
- .n = 1,
- .size = sizeof(compat_uint_t),
- .align = sizeof(compat_uint_t),
- .regset_get = s390_system_call_get,
- .set = s390_system_call_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_LAST_BREAK),
- .n = 1,
- .size = sizeof(long),
- .align = sizeof(long),
- .regset_get = s390_compat_last_break_get,
- .set = s390_compat_last_break_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_TDB),
- .n = 1,
- .size = 256,
- .align = 1,
- .regset_get = s390_tdb_get,
- .set = s390_tdb_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_VXRS_LOW),
- .n = __NUM_VXRS_LOW,
- .size = sizeof(__u64),
- .align = sizeof(__u64),
- .regset_get = s390_vxrs_low_get,
- .set = s390_vxrs_low_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_VXRS_HIGH),
- .n = __NUM_VXRS_HIGH,
- .size = sizeof(__vector128),
- .align = sizeof(__vector128),
- .regset_get = s390_vxrs_high_get,
- .set = s390_vxrs_high_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_HIGH_GPRS),
- .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
- .size = sizeof(compat_long_t),
- .align = sizeof(compat_long_t),
- .regset_get = s390_compat_regs_high_get,
- .set = s390_compat_regs_high_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_GS_CB),
- .n = sizeof(struct gs_cb) / sizeof(__u64),
- .size = sizeof(__u64),
- .align = sizeof(__u64),
- .regset_get = s390_gs_cb_get,
- .set = s390_gs_cb_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_GS_BC),
- .n = sizeof(struct gs_cb) / sizeof(__u64),
- .size = sizeof(__u64),
- .align = sizeof(__u64),
- .regset_get = s390_gs_bc_get,
- .set = s390_gs_bc_set,
- },
- {
- USER_REGSET_NOTE_TYPE(S390_RI_CB),
- .n = sizeof(struct runtime_instr_cb) / sizeof(__u64),
- .size = sizeof(__u64),
- .align = sizeof(__u64),
- .regset_get = s390_runtime_instr_get,
- .set = s390_runtime_instr_set,
- },
-};
-
-static const struct user_regset_view user_s390_compat_view = {
- .name = "s390",
- .e_machine = EM_S390,
- .regsets = s390_compat_regsets,
- .n = ARRAY_SIZE(s390_compat_regsets)
-};
-#endif
-
const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
-#ifdef CONFIG_COMPAT
- if (test_tsk_thread_flag(task, TIF_31BIT))
- return &user_s390_compat_view;
-#endif
return &user_s390_view;
}
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 892fce2b7549..c1fe0b53c5ac 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -13,8 +13,7 @@
* This file handles the architecture-dependent parts of initialization
*/
-#define KMSG_COMPONENT "setup"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "setup: " fmt
#include <linux/errno.h>
#include <linux/export.h>
@@ -47,7 +46,6 @@
#include <linux/kexec.h>
#include <linux/crash_dump.h>
#include <linux/memory.h>
-#include <linux/compat.h>
#include <linux/start_kernel.h>
#include <linux/hugetlb.h>
#include <linux/kmemleak.h>
@@ -112,7 +110,7 @@ struct exception_table_entry __amode31_ref *__stop_amode31_ex_table = _stop_amod
* Because the AMODE31 sections are relocated below 2G at startup,
* the content of control registers CR2, CR5 and CR15 must be updated
* with new addresses after the relocation. The initial initialization of
- * control registers occurs in head64.S and then gets updated again after AMODE31
+ * control registers occurs in head.S and then gets updated again after AMODE31
* relocation. We must access the relevant AMODE31 tables indirectly via
* pointers placed in the .amode31.refs linker section. Those pointers get
* updated automatically during AMODE31 relocation and always contain a valid
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index e48013cd832c..4874de5edea0 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -27,7 +27,6 @@
#include <linux/personality.h>
#include <linux/binfmts.h>
#include <linux/syscalls.h>
-#include <linux/compat.h>
#include <asm/ucontext.h>
#include <linux/uaccess.h>
#include <asm/vdso-symbols.h>
@@ -290,12 +289,6 @@ static int setup_frame(int sig, struct k_sigaction *ka,
unsigned long restorer;
size_t frame_size;
- /*
- * gprs_high are only present for a 31-bit task running on
- * a 64-bit kernel (see compat_signal.c) but the space for
- * gprs_high need to be allocated if vector registers are
- * included in the signal frame on a 31-bit system.
- */
frame_size = sizeof(*frame) - sizeof(frame->sregs_ext);
if (cpu_has_vx())
frame_size += sizeof(frame->sregs_ext);
@@ -333,7 +326,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,
if (ka->sa.sa_flags & SA_RESTORER)
restorer = (unsigned long) ka->sa.sa_restorer;
else
- restorer = VDSO64_SYMBOL(current, sigreturn);
+ restorer = VDSO_SYMBOL(current, sigreturn);
/* Set up registers for signal handler */
regs->gprs[14] = restorer;
@@ -367,12 +360,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
size_t frame_size;
frame_size = sizeof(struct rt_sigframe) - sizeof(_sigregs_ext);
- /*
- * gprs_high are only present for a 31-bit task running on
- * a 64-bit kernel (see compat_signal.c) but the space for
- * gprs_high need to be allocated if vector registers are
- * included in the signal frame on a 31-bit system.
- */
uc_flags = 0;
if (cpu_has_vx()) {
frame_size += sizeof(_sigregs_ext);
@@ -391,7 +378,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = (unsigned long) ksig->ka.sa.sa_restorer;
else
- restorer = VDSO64_SYMBOL(current, rt_sigreturn);
+ restorer = VDSO_SYMBOL(current, rt_sigreturn);
/* Create siginfo on the signal stack */
if (copy_siginfo_to_user(&frame->info, &ksig->info))
@@ -490,10 +477,7 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
clear_pt_regs_flag(regs, PIF_SYSCALL);
rseq_signal_deliver(&ksig, regs);
- if (is_compat_task())
- handle_signal32(&ksig, oldset, regs);
- else
- handle_signal(&ksig, oldset, regs);
+ handle_signal(&ksig, oldset, regs);
return;
}
@@ -506,10 +490,7 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
/* Restart with sys_restart_syscall */
regs->gprs[2] = regs->orig_gpr2;
current->restart_block.arch_data = regs->psw.addr;
- if (is_compat_task())
- regs->psw.addr = VDSO32_SYMBOL(current, restart_syscall);
- else
- regs->psw.addr = VDSO64_SYMBOL(current, restart_syscall);
+ regs->psw.addr = VDSO_SYMBOL(current, restart_syscall);
if (test_thread_flag(TIF_SINGLE_STEP))
clear_thread_flag(TIF_PER_TRAP);
break;
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index da84c0dc6b7e..b7429f30afc1 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -15,8 +15,7 @@
* operates on physical cpu numbers needs to go into smp.c.
*/
-#define KMSG_COMPONENT "cpu"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "cpu: " fmt
#include <linux/cpufeature.h>
#include <linux/workqueue.h>
@@ -281,6 +280,9 @@ static void pcpu_attach_task(int cpu, struct task_struct *tsk)
lc->hardirq_timer = tsk->thread.hardirq_timer;
lc->softirq_timer = tsk->thread.softirq_timer;
lc->steal_timer = 0;
+#ifdef CONFIG_STACKPROTECTOR
+ lc->stack_canary = tsk->stack_canary;
+#endif
}
static void pcpu_start_fn(int cpu, void (*func)(void *), void *data)
@@ -305,9 +307,9 @@ static void __pcpu_delegate(pcpu_delegate_fn *func, void *data)
func(data); /* should not return */
}
-static void pcpu_delegate(struct pcpu *pcpu, int cpu,
- pcpu_delegate_fn *func,
- void *data, unsigned long stack)
+static void __noreturn pcpu_delegate(struct pcpu *pcpu, int cpu,
+ pcpu_delegate_fn *func,
+ void *data, unsigned long stack)
{
struct lowcore *lc, *abs_lc;
unsigned int source_cpu;
@@ -370,7 +372,7 @@ static int pcpu_set_smt(unsigned int mtid)
/*
* Call function on the ipl CPU.
*/
-void smp_call_ipl_cpu(void (*func)(void *), void *data)
+void __noreturn smp_call_ipl_cpu(void (*func)(void *), void *data)
{
struct lowcore *lc = lowcore_ptr[0];
@@ -697,6 +699,7 @@ static void __ref smp_get_core_info(struct sclp_core_info *info, int early)
continue;
info->core[info->configured].core_id =
address >> smp_cpu_mt_shift;
+ info->core[info->configured].type = boot_core_type;
info->configured++;
}
info->combined = info->configured;
diff --git a/arch/s390/kernel/stackprotector.c b/arch/s390/kernel/stackprotector.c
new file mode 100644
index 000000000000..d4e40483f008
--- /dev/null
+++ b/arch/s390/kernel/stackprotector.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) "stackprot: " fmt
+#endif
+
+#include <linux/export.h>
+#include <linux/uaccess.h>
+#include <linux/printk.h>
+#include <asm/abs_lowcore.h>
+#include <asm/sections.h>
+#include <asm/machine.h>
+#include <asm/asm-offsets.h>
+#include <asm/arch-stackprotector.h>
+
+#ifdef __DECOMPRESSOR
+
+#define DEBUGP boot_debug
+#define EMERGP boot_emerg
+#define PANIC boot_panic
+
+#else /* __DECOMPRESSOR */
+
+#define DEBUGP pr_debug
+#define EMERGP pr_emerg
+#define PANIC panic
+
+#endif /* __DECOMPRESSOR */
+
+int __bootdata_preserved(stack_protector_debug);
+
+unsigned long __stack_chk_guard;
+EXPORT_SYMBOL(__stack_chk_guard);
+
+struct insn_ril {
+ u8 opc1 : 8;
+ u8 r1 : 4;
+ u8 opc2 : 4;
+ u32 imm;
+} __packed;
+
+/*
+ * Convert a virtual instruction address to a real instruction address. The
+ * decompressor needs to patch instructions within the kernel image based on
+ * their virtual addresses, while dynamic address translation is still
+ * disabled. Therefore a translation from virtual kernel image addresses to
+ * the corresponding physical addresses is required.
+ *
+ * After dynamic address translation is enabled and when the kernel needs to
+ * patch instructions such a translation is not required since the addresses
+ * are identical.
+ */
+static struct insn_ril *vaddress_to_insn(unsigned long vaddress)
+{
+#ifdef __DECOMPRESSOR
+ return (struct insn_ril *)__kernel_pa(vaddress);
+#else
+ return (struct insn_ril *)vaddress;
+#endif
+}
+
+static unsigned long insn_to_vaddress(struct insn_ril *insn)
+{
+#ifdef __DECOMPRESSOR
+ return (unsigned long)__kernel_va(insn);
+#else
+ return (unsigned long)insn;
+#endif
+}
+
+#define INSN_RIL_STRING_SIZE (sizeof(struct insn_ril) * 2 + 1)
+
+static void insn_ril_to_string(char *str, struct insn_ril *insn)
+{
+ u8 *ptr = (u8 *)insn;
+ int i;
+
+ for (i = 0; i < sizeof(*insn); i++)
+ hex_byte_pack(&str[2 * i], ptr[i]);
+ str[2 * i] = 0;
+}
+
+static void stack_protector_dump(struct insn_ril *old, struct insn_ril *new)
+{
+ char ostr[INSN_RIL_STRING_SIZE];
+ char nstr[INSN_RIL_STRING_SIZE];
+
+ insn_ril_to_string(ostr, old);
+ insn_ril_to_string(nstr, new);
+ DEBUGP("%016lx: %s -> %s\n", insn_to_vaddress(old), ostr, nstr);
+}
+
+static int stack_protector_verify(struct insn_ril *insn, unsigned long kernel_start)
+{
+ char istr[INSN_RIL_STRING_SIZE];
+ unsigned long vaddress, offset;
+
+ /* larl */
+ if (insn->opc1 == 0xc0 && insn->opc2 == 0x0)
+ return 0;
+ /* lgrl */
+ if (insn->opc1 == 0xc4 && insn->opc2 == 0x8)
+ return 0;
+ insn_ril_to_string(istr, insn);
+ vaddress = insn_to_vaddress(insn);
+ if (__is_defined(__DECOMPRESSOR)) {
+ offset = (unsigned long)insn - kernel_start + TEXT_OFFSET;
+ EMERGP("Unexpected instruction at %016lx/%016lx: %s\n", vaddress, offset, istr);
+ PANIC("Stackprotector error\n");
+ } else {
+ EMERGP("Unexpected instruction at %016lx: %s\n", vaddress, istr);
+ }
+ return -EINVAL;
+}
+
+int __stack_protector_apply(unsigned long *start, unsigned long *end, unsigned long kernel_start)
+{
+ unsigned long canary, *loc;
+ struct insn_ril *insn, new;
+ int rc;
+
+ /*
+ * Convert LARL/LGRL instructions to LLILF so register R1 contains the
+ * address of the per-cpu / per-process stack canary:
+ *
+ * LARL/LGRL R1,__stack_chk_guard => LLILF R1,__lc_stack_canary
+ */
+ canary = __LC_STACK_CANARY;
+ if (machine_has_relocated_lowcore())
+ canary += LOWCORE_ALT_ADDRESS;
+ for (loc = start; loc < end; loc++) {
+ insn = vaddress_to_insn(*loc);
+ rc = stack_protector_verify(insn, kernel_start);
+ if (rc)
+ return rc;
+ new = *insn;
+ new.opc1 = 0xc0;
+ new.opc2 = 0xf;
+ new.imm = canary;
+ if (stack_protector_debug)
+ stack_protector_dump(insn, &new);
+ s390_kernel_write(insn, &new, sizeof(*insn));
+ }
+ return 0;
+}
+
+#ifdef __DECOMPRESSOR
+void __stack_protector_apply_early(unsigned long kernel_start)
+{
+ unsigned long *start, *end;
+
+ start = (unsigned long *)vmlinux.stack_prot_start;
+ end = (unsigned long *)vmlinux.stack_prot_end;
+ __stack_protector_apply(start, end, kernel_start);
+}
+#endif
diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c
index b153a395f46d..3aae7f70e6ab 100644
--- a/arch/s390/kernel/stacktrace.c
+++ b/arch/s390/kernel/stacktrace.c
@@ -8,7 +8,6 @@
#include <linux/perf_event.h>
#include <linux/stacktrace.h>
#include <linux/uaccess.h>
-#include <linux/compat.h>
#include <asm/asm-offsets.h>
#include <asm/stacktrace.h>
#include <asm/unwind.h>
@@ -107,8 +106,6 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo
unsigned long ip, sp;
bool first = true;
- if (is_compat_task())
- return;
if (!current->mm)
return;
ip = instruction_pointer(regs);
diff --git a/arch/s390/kernel/sthyi.c b/arch/s390/kernel/sthyi.c
index f4ccdbed4b89..5eae2e25997a 100644
--- a/arch/s390/kernel/sthyi.c
+++ b/arch/s390/kernel/sthyi.c
@@ -253,7 +253,7 @@ static void fill_diag_mac(struct sthyi_sctns *sctns,
sctns->mac.infmval1 |= MAC_CNT_VLD;
}
-/* Returns a pointer to the the next partition block. */
+/* Returns a pointer to the next partition block. */
static struct diag204_x_part_block *lpar_cpu_inf(struct lpar_cpu_inf *part_inf,
bool this_lpar,
void *diag224_buf,
diff --git a/arch/s390/kernel/syscall.c b/arch/s390/kernel/syscall.c
index 4fee74553ca2..795b6cca74c9 100644
--- a/arch/s390/kernel/syscall.c
+++ b/arch/s390/kernel/syscall.c
@@ -39,6 +39,16 @@
#include "entry.h"
+#define __SYSCALL(nr, sym) long __s390x_##sym(struct pt_regs *);
+#include <asm/syscall_table.h>
+#undef __SYSCALL
+
+#define __SYSCALL(nr, sym) [nr] = (__s390x_##sym),
+const sys_call_ptr_t sys_call_table[__NR_syscalls] = {
+#include <asm/syscall_table.h>
+};
+#undef __SYSCALL
+
#ifdef CONFIG_SYSVIPC
/*
* sys_ipc() is the de-multiplexer for the SysV IPC calls.
@@ -122,7 +132,7 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
goto out;
regs->gprs[2] = -ENOSYS;
if (likely(nr < NR_syscalls))
- regs->gprs[2] = current->thread.sys_call_table[nr](regs);
+ regs->gprs[2] = sys_call_table[nr](regs);
out:
syscall_exit_to_user_mode(regs);
}
diff --git a/arch/s390/kernel/syscalls/Makefile b/arch/s390/kernel/syscalls/Makefile
index c5d958a09ff4..d5fca0ca0890 100644
--- a/arch/s390/kernel/syscalls/Makefile
+++ b/arch/s390/kernel/syscalls/Makefile
@@ -1,48 +1,32 @@
# SPDX-License-Identifier: GPL-2.0
+kapi := arch/$(SRCARCH)/include/generated/asm
+uapi := arch/$(SRCARCH)/include/generated/uapi/asm
-gen := arch/$(ARCH)/include/generated
-kapi := $(gen)/asm
-uapi := $(gen)/uapi/asm
-
-syscall := $(src)/syscall.tbl
-systbl := $(src)/syscalltbl
-
-gen-y := $(kapi)/syscall_table.h
-kapi-hdrs-y := $(kapi)/unistd_nr.h
-uapi-hdrs-y := $(uapi)/unistd_32.h
-uapi-hdrs-y += $(uapi)/unistd_64.h
-
-targets += $(addprefix ../../../../,$(gen-y) $(kapi-hdrs-y) $(uapi-hdrs-y))
-
-PHONY += kapi uapi
-
-kapi: $(gen-y) $(kapi-hdrs-y)
-uapi: $(uapi-hdrs-y)
-
-
-# Create output directory if not already present
$(shell mkdir -p $(uapi) $(kapi))
-quiet_cmd_syshdr = SYSHDR $@
- cmd_syshdr = $(CONFIG_SHELL) '$(systbl)' -H -a $(syshdr_abi_$(basetarget)) -f "$@" < $< > $@
-
-quiet_cmd_sysnr = SYSNR $@
- cmd_sysnr = $(CONFIG_SHELL) '$(systbl)' -N -a $(sysnr_abi_$(basetarget)) < $< > $@
+syscall := $(src)/syscall.tbl
+syshdr := $(srctree)/scripts/syscallhdr.sh
+systbl := $(srctree)/scripts/syscalltbl.sh
-quiet_cmd_syscalls = SYSTBL $@
- cmd_syscalls = $(CONFIG_SHELL) '$(systbl)' -S < $< > $@
+quiet_cmd_syshdr = SYSHDR $@
+ cmd_syshdr = $(CONFIG_SHELL) $(syshdr) --emit-nr --abis common,$* $< $@
-syshdr_abi_unistd_32 := common,32
-$(uapi)/unistd_32.h: $(syscall) $(systbl) FORCE
- $(call if_changed,syshdr)
+quiet_cmd_systbl = SYSTBL $@
+ cmd_systbl = $(CONFIG_SHELL) $(systbl) --abis common,$* $< $@
-syshdr_abi_unistd_64 := common,64
-$(uapi)/unistd_64.h: $(syscall) $(systbl) FORCE
+$(uapi)/unistd_%.h: $(syscall) $(syshdr) FORCE
$(call if_changed,syshdr)
$(kapi)/syscall_table.h: $(syscall) $(systbl) FORCE
- $(call if_changed,syscalls)
+ $(call if_changed,systbl)
+
+uapisyshdr-y += unistd_64.h
+kapisyshdr-y += syscall_table.h
+
+uapisyshdr-y := $(addprefix $(uapi)/, $(uapisyshdr-y))
+kapisyshdr-y := $(addprefix $(kapi)/, $(kapisyshdr-y))
+targets += $(addprefix ../../../../, $(uapisyshdr-y) $(kapisyshdr-y))
-sysnr_abi_unistd_nr := common,32,64
-$(kapi)/unistd_nr.h: $(syscall) $(systbl) FORCE
- $(call if_changed,sysnr)
+PHONY += all
+all: $(uapisyshdr-y) $(kapisyshdr-y)
+ @:
diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl
index 8a6744d658db..417ed16b3c63 100644
--- a/arch/s390/kernel/syscalls/syscall.tbl
+++ b/arch/s390/kernel/syscalls/syscall.tbl
@@ -3,472 +3,397 @@
# System call table for s390
#
# Format:
+# <nr> <abi> <syscall> <entry>
#
-# <nr> <abi> <syscall> <entry-64bit> <compat-entry>
-#
-# where <abi> can be common, 64, or 32
+# <abi> is always common.
-1 common exit sys_exit sys_exit
-2 common fork sys_fork sys_fork
-3 common read sys_read compat_sys_s390_read
-4 common write sys_write compat_sys_s390_write
-5 common open sys_open compat_sys_open
-6 common close sys_close sys_close
-7 common restart_syscall sys_restart_syscall sys_restart_syscall
-8 common creat sys_creat sys_creat
-9 common link sys_link sys_link
-10 common unlink sys_unlink sys_unlink
-11 common execve sys_execve compat_sys_execve
-12 common chdir sys_chdir sys_chdir
-13 32 time - sys_time32
-14 common mknod sys_mknod sys_mknod
-15 common chmod sys_chmod sys_chmod
-16 32 lchown - sys_lchown16
-19 common lseek sys_lseek compat_sys_lseek
-20 common getpid sys_getpid sys_getpid
-21 common mount sys_mount sys_mount
-22 common umount sys_oldumount sys_oldumount
-23 32 setuid - sys_setuid16
-24 32 getuid - sys_getuid16
-25 32 stime - sys_stime32
-26 common ptrace sys_ptrace compat_sys_ptrace
-27 common alarm sys_alarm sys_alarm
-29 common pause sys_pause sys_pause
-30 common utime sys_utime sys_utime32
-33 common access sys_access sys_access
-34 common nice sys_nice sys_nice
-36 common sync sys_sync sys_sync
-37 common kill sys_kill sys_kill
-38 common rename sys_rename sys_rename
-39 common mkdir sys_mkdir sys_mkdir
-40 common rmdir sys_rmdir sys_rmdir
-41 common dup sys_dup sys_dup
-42 common pipe sys_pipe sys_pipe
-43 common times sys_times compat_sys_times
-45 common brk sys_brk sys_brk
-46 32 setgid - sys_setgid16
-47 32 getgid - sys_getgid16
-48 common signal sys_signal sys_signal
-49 32 geteuid - sys_geteuid16
-50 32 getegid - sys_getegid16
-51 common acct sys_acct sys_acct
-52 common umount2 sys_umount sys_umount
-54 common ioctl sys_ioctl compat_sys_ioctl
-55 common fcntl sys_fcntl compat_sys_fcntl
-57 common setpgid sys_setpgid sys_setpgid
-60 common umask sys_umask sys_umask
-61 common chroot sys_chroot sys_chroot
-62 common ustat sys_ustat compat_sys_ustat
-63 common dup2 sys_dup2 sys_dup2
-64 common getppid sys_getppid sys_getppid
-65 common getpgrp sys_getpgrp sys_getpgrp
-66 common setsid sys_setsid sys_setsid
-67 common sigaction sys_sigaction compat_sys_sigaction
-70 32 setreuid - sys_setreuid16
-71 32 setregid - sys_setregid16
-72 common sigsuspend sys_sigsuspend sys_sigsuspend
-73 common sigpending sys_sigpending compat_sys_sigpending
-74 common sethostname sys_sethostname sys_sethostname
-75 common setrlimit sys_setrlimit compat_sys_setrlimit
-76 32 getrlimit - compat_sys_old_getrlimit
-77 common getrusage sys_getrusage compat_sys_getrusage
-78 common gettimeofday sys_gettimeofday compat_sys_gettimeofday
-79 common settimeofday sys_settimeofday compat_sys_settimeofday
-80 32 getgroups - sys_getgroups16
-81 32 setgroups - sys_setgroups16
-83 common symlink sys_symlink sys_symlink
-85 common readlink sys_readlink sys_readlink
-86 common uselib sys_uselib sys_uselib
-87 common swapon sys_swapon sys_swapon
-88 common reboot sys_reboot sys_reboot
-89 common readdir - compat_sys_old_readdir
-90 common mmap sys_old_mmap compat_sys_s390_old_mmap
-91 common munmap sys_munmap sys_munmap
-92 common truncate sys_truncate compat_sys_truncate
-93 common ftruncate sys_ftruncate compat_sys_ftruncate
-94 common fchmod sys_fchmod sys_fchmod
-95 32 fchown - sys_fchown16
-96 common getpriority sys_getpriority sys_getpriority
-97 common setpriority sys_setpriority sys_setpriority
-99 common statfs sys_statfs compat_sys_statfs
-100 common fstatfs sys_fstatfs compat_sys_fstatfs
-101 32 ioperm - -
-102 common socketcall sys_socketcall compat_sys_socketcall
-103 common syslog sys_syslog sys_syslog
-104 common setitimer sys_setitimer compat_sys_setitimer
-105 common getitimer sys_getitimer compat_sys_getitimer
-106 common stat sys_newstat compat_sys_newstat
-107 common lstat sys_newlstat compat_sys_newlstat
-108 common fstat sys_newfstat compat_sys_newfstat
-110 common lookup_dcookie - -
-111 common vhangup sys_vhangup sys_vhangup
-112 common idle - -
-114 common wait4 sys_wait4 compat_sys_wait4
-115 common swapoff sys_swapoff sys_swapoff
-116 common sysinfo sys_sysinfo compat_sys_sysinfo
-117 common ipc sys_s390_ipc compat_sys_s390_ipc
-118 common fsync sys_fsync sys_fsync
-119 common sigreturn sys_sigreturn compat_sys_sigreturn
-120 common clone sys_clone sys_clone
-121 common setdomainname sys_setdomainname sys_setdomainname
-122 common uname sys_newuname sys_newuname
-124 common adjtimex sys_adjtimex sys_adjtimex_time32
-125 common mprotect sys_mprotect sys_mprotect
-126 common sigprocmask sys_sigprocmask compat_sys_sigprocmask
-127 common create_module - -
-128 common init_module sys_init_module sys_init_module
-129 common delete_module sys_delete_module sys_delete_module
-130 common get_kernel_syms - -
-131 common quotactl sys_quotactl sys_quotactl
-132 common getpgid sys_getpgid sys_getpgid
-133 common fchdir sys_fchdir sys_fchdir
-134 common bdflush sys_ni_syscall sys_ni_syscall
-135 common sysfs sys_sysfs sys_sysfs
-136 common personality sys_s390_personality sys_s390_personality
-137 common afs_syscall - -
-138 32 setfsuid - sys_setfsuid16
-139 32 setfsgid - sys_setfsgid16
-140 32 _llseek - sys_llseek
-141 common getdents sys_getdents compat_sys_getdents
-142 32 _newselect - compat_sys_select
-142 64 select sys_select -
-143 common flock sys_flock sys_flock
-144 common msync sys_msync sys_msync
-145 common readv sys_readv sys_readv
-146 common writev sys_writev sys_writev
-147 common getsid sys_getsid sys_getsid
-148 common fdatasync sys_fdatasync sys_fdatasync
-149 common _sysctl - -
-150 common mlock sys_mlock sys_mlock
-151 common munlock sys_munlock sys_munlock
-152 common mlockall sys_mlockall sys_mlockall
-153 common munlockall sys_munlockall sys_munlockall
-154 common sched_setparam sys_sched_setparam sys_sched_setparam
-155 common sched_getparam sys_sched_getparam sys_sched_getparam
-156 common sched_setscheduler sys_sched_setscheduler sys_sched_setscheduler
-157 common sched_getscheduler sys_sched_getscheduler sys_sched_getscheduler
-158 common sched_yield sys_sched_yield sys_sched_yield
-159 common sched_get_priority_max sys_sched_get_priority_max sys_sched_get_priority_max
-160 common sched_get_priority_min sys_sched_get_priority_min sys_sched_get_priority_min
-161 common sched_rr_get_interval sys_sched_rr_get_interval sys_sched_rr_get_interval_time32
-162 common nanosleep sys_nanosleep sys_nanosleep_time32
-163 common mremap sys_mremap sys_mremap
-164 32 setresuid - sys_setresuid16
-165 32 getresuid - sys_getresuid16
-167 common query_module - -
-168 common poll sys_poll sys_poll
-169 common nfsservctl - -
-170 32 setresgid - sys_setresgid16
-171 32 getresgid - sys_getresgid16
-172 common prctl sys_prctl sys_prctl
-173 common rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn
-174 common rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction
-175 common rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask
-176 common rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending
-177 common rt_sigtimedwait sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time32
-178 common rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo
-179 common rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend
-180 common pread64 sys_pread64 compat_sys_s390_pread64
-181 common pwrite64 sys_pwrite64 compat_sys_s390_pwrite64
-182 32 chown - sys_chown16
-183 common getcwd sys_getcwd sys_getcwd
-184 common capget sys_capget sys_capget
-185 common capset sys_capset sys_capset
-186 common sigaltstack sys_sigaltstack compat_sys_sigaltstack
-187 common sendfile sys_sendfile64 compat_sys_sendfile
-188 common getpmsg - -
-189 common putpmsg - -
-190 common vfork sys_vfork sys_vfork
-191 32 ugetrlimit - compat_sys_getrlimit
-191 64 getrlimit sys_getrlimit -
-192 32 mmap2 - compat_sys_s390_mmap2
-193 32 truncate64 - compat_sys_s390_truncate64
-194 32 ftruncate64 - compat_sys_s390_ftruncate64
-195 32 stat64 - compat_sys_s390_stat64
-196 32 lstat64 - compat_sys_s390_lstat64
-197 32 fstat64 - compat_sys_s390_fstat64
-198 32 lchown32 - sys_lchown
-198 64 lchown sys_lchown -
-199 32 getuid32 - sys_getuid
-199 64 getuid sys_getuid -
-200 32 getgid32 - sys_getgid
-200 64 getgid sys_getgid -
-201 32 geteuid32 - sys_geteuid
-201 64 geteuid sys_geteuid -
-202 32 getegid32 - sys_getegid
-202 64 getegid sys_getegid -
-203 32 setreuid32 - sys_setreuid
-203 64 setreuid sys_setreuid -
-204 32 setregid32 - sys_setregid
-204 64 setregid sys_setregid -
-205 32 getgroups32 - sys_getgroups
-205 64 getgroups sys_getgroups -
-206 32 setgroups32 - sys_setgroups
-206 64 setgroups sys_setgroups -
-207 32 fchown32 - sys_fchown
-207 64 fchown sys_fchown -
-208 32 setresuid32 - sys_setresuid
-208 64 setresuid sys_setresuid -
-209 32 getresuid32 - sys_getresuid
-209 64 getresuid sys_getresuid -
-210 32 setresgid32 - sys_setresgid
-210 64 setresgid sys_setresgid -
-211 32 getresgid32 - sys_getresgid
-211 64 getresgid sys_getresgid -
-212 32 chown32 - sys_chown
-212 64 chown sys_chown -
-213 32 setuid32 - sys_setuid
-213 64 setuid sys_setuid -
-214 32 setgid32 - sys_setgid
-214 64 setgid sys_setgid -
-215 32 setfsuid32 - sys_setfsuid
-215 64 setfsuid sys_setfsuid -
-216 32 setfsgid32 - sys_setfsgid
-216 64 setfsgid sys_setfsgid -
-217 common pivot_root sys_pivot_root sys_pivot_root
-218 common mincore sys_mincore sys_mincore
-219 common madvise sys_madvise sys_madvise
-220 common getdents64 sys_getdents64 sys_getdents64
-221 32 fcntl64 - compat_sys_fcntl64
-222 common readahead sys_readahead compat_sys_s390_readahead
-223 32 sendfile64 - compat_sys_sendfile64
-224 common setxattr sys_setxattr sys_setxattr
-225 common lsetxattr sys_lsetxattr sys_lsetxattr
-226 common fsetxattr sys_fsetxattr sys_fsetxattr
-227 common getxattr sys_getxattr sys_getxattr
-228 common lgetxattr sys_lgetxattr sys_lgetxattr
-229 common fgetxattr sys_fgetxattr sys_fgetxattr
-230 common listxattr sys_listxattr sys_listxattr
-231 common llistxattr sys_llistxattr sys_llistxattr
-232 common flistxattr sys_flistxattr sys_flistxattr
-233 common removexattr sys_removexattr sys_removexattr
-234 common lremovexattr sys_lremovexattr sys_lremovexattr
-235 common fremovexattr sys_fremovexattr sys_fremovexattr
-236 common gettid sys_gettid sys_gettid
-237 common tkill sys_tkill sys_tkill
-238 common futex sys_futex sys_futex_time32
-239 common sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity
-240 common sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity
-241 common tgkill sys_tgkill sys_tgkill
-243 common io_setup sys_io_setup compat_sys_io_setup
-244 common io_destroy sys_io_destroy sys_io_destroy
-245 common io_getevents sys_io_getevents sys_io_getevents_time32
-246 common io_submit sys_io_submit compat_sys_io_submit
-247 common io_cancel sys_io_cancel sys_io_cancel
-248 common exit_group sys_exit_group sys_exit_group
-249 common epoll_create sys_epoll_create sys_epoll_create
-250 common epoll_ctl sys_epoll_ctl sys_epoll_ctl
-251 common epoll_wait sys_epoll_wait sys_epoll_wait
-252 common set_tid_address sys_set_tid_address sys_set_tid_address
-253 common fadvise64 sys_fadvise64_64 compat_sys_s390_fadvise64
-254 common timer_create sys_timer_create compat_sys_timer_create
-255 common timer_settime sys_timer_settime sys_timer_settime32
-256 common timer_gettime sys_timer_gettime sys_timer_gettime32
-257 common timer_getoverrun sys_timer_getoverrun sys_timer_getoverrun
-258 common timer_delete sys_timer_delete sys_timer_delete
-259 common clock_settime sys_clock_settime sys_clock_settime32
-260 common clock_gettime sys_clock_gettime sys_clock_gettime32
-261 common clock_getres sys_clock_getres sys_clock_getres_time32
-262 common clock_nanosleep sys_clock_nanosleep sys_clock_nanosleep_time32
-264 32 fadvise64_64 - compat_sys_s390_fadvise64_64
-265 common statfs64 sys_statfs64 compat_sys_statfs64
-266 common fstatfs64 sys_fstatfs64 compat_sys_fstatfs64
-267 common remap_file_pages sys_remap_file_pages sys_remap_file_pages
-268 common mbind sys_mbind sys_mbind
-269 common get_mempolicy sys_get_mempolicy sys_get_mempolicy
-270 common set_mempolicy sys_set_mempolicy sys_set_mempolicy
-271 common mq_open sys_mq_open compat_sys_mq_open
-272 common mq_unlink sys_mq_unlink sys_mq_unlink
-273 common mq_timedsend sys_mq_timedsend sys_mq_timedsend_time32
-274 common mq_timedreceive sys_mq_timedreceive sys_mq_timedreceive_time32
-275 common mq_notify sys_mq_notify compat_sys_mq_notify
-276 common mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr
-277 common kexec_load sys_kexec_load compat_sys_kexec_load
-278 common add_key sys_add_key sys_add_key
-279 common request_key sys_request_key sys_request_key
-280 common keyctl sys_keyctl compat_sys_keyctl
-281 common waitid sys_waitid compat_sys_waitid
-282 common ioprio_set sys_ioprio_set sys_ioprio_set
-283 common ioprio_get sys_ioprio_get sys_ioprio_get
-284 common inotify_init sys_inotify_init sys_inotify_init
-285 common inotify_add_watch sys_inotify_add_watch sys_inotify_add_watch
-286 common inotify_rm_watch sys_inotify_rm_watch sys_inotify_rm_watch
-287 common migrate_pages sys_migrate_pages sys_migrate_pages
-288 common openat sys_openat compat_sys_openat
-289 common mkdirat sys_mkdirat sys_mkdirat
-290 common mknodat sys_mknodat sys_mknodat
-291 common fchownat sys_fchownat sys_fchownat
-292 common futimesat sys_futimesat sys_futimesat_time32
-293 32 fstatat64 - compat_sys_s390_fstatat64
-293 64 newfstatat sys_newfstatat -
-294 common unlinkat sys_unlinkat sys_unlinkat
-295 common renameat sys_renameat sys_renameat
-296 common linkat sys_linkat sys_linkat
-297 common symlinkat sys_symlinkat sys_symlinkat
-298 common readlinkat sys_readlinkat sys_readlinkat
-299 common fchmodat sys_fchmodat sys_fchmodat
-300 common faccessat sys_faccessat sys_faccessat
-301 common pselect6 sys_pselect6 compat_sys_pselect6_time32
-302 common ppoll sys_ppoll compat_sys_ppoll_time32
-303 common unshare sys_unshare sys_unshare
-304 common set_robust_list sys_set_robust_list compat_sys_set_robust_list
-305 common get_robust_list sys_get_robust_list compat_sys_get_robust_list
-306 common splice sys_splice sys_splice
-307 common sync_file_range sys_sync_file_range compat_sys_s390_sync_file_range
-308 common tee sys_tee sys_tee
-309 common vmsplice sys_vmsplice sys_vmsplice
-310 common move_pages sys_move_pages sys_move_pages
-311 common getcpu sys_getcpu sys_getcpu
-312 common epoll_pwait sys_epoll_pwait compat_sys_epoll_pwait
-313 common utimes sys_utimes sys_utimes_time32
-314 common fallocate sys_fallocate compat_sys_s390_fallocate
-315 common utimensat sys_utimensat sys_utimensat_time32
-316 common signalfd sys_signalfd compat_sys_signalfd
-317 common timerfd - -
-318 common eventfd sys_eventfd sys_eventfd
-319 common timerfd_create sys_timerfd_create sys_timerfd_create
-320 common timerfd_settime sys_timerfd_settime sys_timerfd_settime32
-321 common timerfd_gettime sys_timerfd_gettime sys_timerfd_gettime32
-322 common signalfd4 sys_signalfd4 compat_sys_signalfd4
-323 common eventfd2 sys_eventfd2 sys_eventfd2
-324 common inotify_init1 sys_inotify_init1 sys_inotify_init1
-325 common pipe2 sys_pipe2 sys_pipe2
-326 common dup3 sys_dup3 sys_dup3
-327 common epoll_create1 sys_epoll_create1 sys_epoll_create1
-328 common preadv sys_preadv compat_sys_preadv
-329 common pwritev sys_pwritev compat_sys_pwritev
-330 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo
-331 common perf_event_open sys_perf_event_open sys_perf_event_open
-332 common fanotify_init sys_fanotify_init sys_fanotify_init
-333 common fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark
-334 common prlimit64 sys_prlimit64 sys_prlimit64
-335 common name_to_handle_at sys_name_to_handle_at sys_name_to_handle_at
-336 common open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at
-337 common clock_adjtime sys_clock_adjtime sys_clock_adjtime32
-338 common syncfs sys_syncfs sys_syncfs
-339 common setns sys_setns sys_setns
-340 common process_vm_readv sys_process_vm_readv sys_process_vm_readv
-341 common process_vm_writev sys_process_vm_writev sys_process_vm_writev
-342 common s390_runtime_instr sys_s390_runtime_instr sys_s390_runtime_instr
-343 common kcmp sys_kcmp sys_kcmp
-344 common finit_module sys_finit_module sys_finit_module
-345 common sched_setattr sys_sched_setattr sys_sched_setattr
-346 common sched_getattr sys_sched_getattr sys_sched_getattr
-347 common renameat2 sys_renameat2 sys_renameat2
-348 common seccomp sys_seccomp sys_seccomp
-349 common getrandom sys_getrandom sys_getrandom
-350 common memfd_create sys_memfd_create sys_memfd_create
-351 common bpf sys_bpf sys_bpf
-352 common s390_pci_mmio_write sys_s390_pci_mmio_write sys_s390_pci_mmio_write
-353 common s390_pci_mmio_read sys_s390_pci_mmio_read sys_s390_pci_mmio_read
-354 common execveat sys_execveat compat_sys_execveat
-355 common userfaultfd sys_userfaultfd sys_userfaultfd
-356 common membarrier sys_membarrier sys_membarrier
-357 common recvmmsg sys_recvmmsg compat_sys_recvmmsg_time32
-358 common sendmmsg sys_sendmmsg compat_sys_sendmmsg
-359 common socket sys_socket sys_socket
-360 common socketpair sys_socketpair sys_socketpair
-361 common bind sys_bind sys_bind
-362 common connect sys_connect sys_connect
-363 common listen sys_listen sys_listen
-364 common accept4 sys_accept4 sys_accept4
-365 common getsockopt sys_getsockopt sys_getsockopt
-366 common setsockopt sys_setsockopt sys_setsockopt
-367 common getsockname sys_getsockname sys_getsockname
-368 common getpeername sys_getpeername sys_getpeername
-369 common sendto sys_sendto sys_sendto
-370 common sendmsg sys_sendmsg compat_sys_sendmsg
-371 common recvfrom sys_recvfrom compat_sys_recvfrom
-372 common recvmsg sys_recvmsg compat_sys_recvmsg
-373 common shutdown sys_shutdown sys_shutdown
-374 common mlock2 sys_mlock2 sys_mlock2
-375 common copy_file_range sys_copy_file_range sys_copy_file_range
-376 common preadv2 sys_preadv2 compat_sys_preadv2
-377 common pwritev2 sys_pwritev2 compat_sys_pwritev2
-378 common s390_guarded_storage sys_s390_guarded_storage sys_s390_guarded_storage
-379 common statx sys_statx sys_statx
-380 common s390_sthyi sys_s390_sthyi sys_s390_sthyi
-381 common kexec_file_load sys_kexec_file_load sys_kexec_file_load
-382 common io_pgetevents sys_io_pgetevents compat_sys_io_pgetevents
-383 common rseq sys_rseq sys_rseq
-384 common pkey_mprotect sys_pkey_mprotect sys_pkey_mprotect
-385 common pkey_alloc sys_pkey_alloc sys_pkey_alloc
-386 common pkey_free sys_pkey_free sys_pkey_free
+1 common exit sys_exit
+2 common fork sys_fork
+3 common read sys_read
+4 common write sys_write
+5 common open sys_open
+6 common close sys_close
+7 common restart_syscall sys_restart_syscall
+8 common creat sys_creat
+9 common link sys_link
+10 common unlink sys_unlink
+11 common execve sys_execve
+12 common chdir sys_chdir
+14 common mknod sys_mknod
+15 common chmod sys_chmod
+19 common lseek sys_lseek
+20 common getpid sys_getpid
+21 common mount sys_mount
+22 common umount sys_oldumount
+26 common ptrace sys_ptrace
+27 common alarm sys_alarm
+29 common pause sys_pause
+30 common utime sys_utime
+33 common access sys_access
+34 common nice sys_nice
+36 common sync sys_sync
+37 common kill sys_kill
+38 common rename sys_rename
+39 common mkdir sys_mkdir
+40 common rmdir sys_rmdir
+41 common dup sys_dup
+42 common pipe sys_pipe
+43 common times sys_times
+45 common brk sys_brk
+48 common signal sys_signal
+51 common acct sys_acct
+52 common umount2 sys_umount
+54 common ioctl sys_ioctl
+55 common fcntl sys_fcntl
+57 common setpgid sys_setpgid
+60 common umask sys_umask
+61 common chroot sys_chroot
+62 common ustat sys_ustat
+63 common dup2 sys_dup2
+64 common getppid sys_getppid
+65 common getpgrp sys_getpgrp
+66 common setsid sys_setsid
+67 common sigaction sys_sigaction
+72 common sigsuspend sys_sigsuspend
+73 common sigpending sys_sigpending
+74 common sethostname sys_sethostname
+75 common setrlimit sys_setrlimit
+77 common getrusage sys_getrusage
+78 common gettimeofday sys_gettimeofday
+79 common settimeofday sys_settimeofday
+83 common symlink sys_symlink
+85 common readlink sys_readlink
+86 common uselib sys_uselib
+87 common swapon sys_swapon
+88 common reboot sys_reboot
+89 common readdir sys_ni_syscall
+90 common mmap sys_old_mmap
+91 common munmap sys_munmap
+92 common truncate sys_truncate
+93 common ftruncate sys_ftruncate
+94 common fchmod sys_fchmod
+96 common getpriority sys_getpriority
+97 common setpriority sys_setpriority
+99 common statfs sys_statfs
+100 common fstatfs sys_fstatfs
+102 common socketcall sys_socketcall
+103 common syslog sys_syslog
+104 common setitimer sys_setitimer
+105 common getitimer sys_getitimer
+106 common stat sys_newstat
+107 common lstat sys_newlstat
+108 common fstat sys_newfstat
+110 common lookup_dcookie sys_ni_syscall
+111 common vhangup sys_vhangup
+112 common idle sys_ni_syscall
+114 common wait4 sys_wait4
+115 common swapoff sys_swapoff
+116 common sysinfo sys_sysinfo
+117 common ipc sys_s390_ipc
+118 common fsync sys_fsync
+119 common sigreturn sys_sigreturn
+120 common clone sys_clone
+121 common setdomainname sys_setdomainname
+122 common uname sys_newuname
+124 common adjtimex sys_adjtimex
+125 common mprotect sys_mprotect
+126 common sigprocmask sys_sigprocmask
+127 common create_module sys_ni_syscall
+128 common init_module sys_init_module
+129 common delete_module sys_delete_module
+130 common get_kernel_syms sys_ni_syscall
+131 common quotactl sys_quotactl
+132 common getpgid sys_getpgid
+133 common fchdir sys_fchdir
+134 common bdflush sys_ni_syscall
+135 common sysfs sys_sysfs
+136 common personality sys_s390_personality
+137 common afs_syscall sys_ni_syscall
+141 common getdents sys_getdents
+142 common select sys_select
+143 common flock sys_flock
+144 common msync sys_msync
+145 common readv sys_readv
+146 common writev sys_writev
+147 common getsid sys_getsid
+148 common fdatasync sys_fdatasync
+149 common _sysctl sys_ni_syscall
+150 common mlock sys_mlock
+151 common munlock sys_munlock
+152 common mlockall sys_mlockall
+153 common munlockall sys_munlockall
+154 common sched_setparam sys_sched_setparam
+155 common sched_getparam sys_sched_getparam
+156 common sched_setscheduler sys_sched_setscheduler
+157 common sched_getscheduler sys_sched_getscheduler
+158 common sched_yield sys_sched_yield
+159 common sched_get_priority_max sys_sched_get_priority_max
+160 common sched_get_priority_min sys_sched_get_priority_min
+161 common sched_rr_get_interval sys_sched_rr_get_interval
+162 common nanosleep sys_nanosleep
+163 common mremap sys_mremap
+167 common query_module sys_ni_syscall
+168 common poll sys_poll
+169 common nfsservctl sys_ni_syscall
+172 common prctl sys_prctl
+173 common rt_sigreturn sys_rt_sigreturn
+174 common rt_sigaction sys_rt_sigaction
+175 common rt_sigprocmask sys_rt_sigprocmask
+176 common rt_sigpending sys_rt_sigpending
+177 common rt_sigtimedwait sys_rt_sigtimedwait
+178 common rt_sigqueueinfo sys_rt_sigqueueinfo
+179 common rt_sigsuspend sys_rt_sigsuspend
+180 common pread64 sys_pread64
+181 common pwrite64 sys_pwrite64
+183 common getcwd sys_getcwd
+184 common capget sys_capget
+185 common capset sys_capset
+186 common sigaltstack sys_sigaltstack
+187 common sendfile sys_sendfile64
+188 common getpmsg sys_ni_syscall
+189 common putpmsg sys_ni_syscall
+190 common vfork sys_vfork
+191 common getrlimit sys_getrlimit
+198 common lchown sys_lchown
+199 common getuid sys_getuid
+200 common getgid sys_getgid
+201 common geteuid sys_geteuid
+202 common getegid sys_getegid
+203 common setreuid sys_setreuid
+204 common setregid sys_setregid
+205 common getgroups sys_getgroups
+206 common setgroups sys_setgroups
+207 common fchown sys_fchown
+208 common setresuid sys_setresuid
+209 common getresuid sys_getresuid
+210 common setresgid sys_setresgid
+211 common getresgid sys_getresgid
+212 common chown sys_chown
+213 common setuid sys_setuid
+214 common setgid sys_setgid
+215 common setfsuid sys_setfsuid
+216 common setfsgid sys_setfsgid
+217 common pivot_root sys_pivot_root
+218 common mincore sys_mincore
+219 common madvise sys_madvise
+220 common getdents64 sys_getdents64
+222 common readahead sys_readahead
+224 common setxattr sys_setxattr
+225 common lsetxattr sys_lsetxattr
+226 common fsetxattr sys_fsetxattr
+227 common getxattr sys_getxattr
+228 common lgetxattr sys_lgetxattr
+229 common fgetxattr sys_fgetxattr
+230 common listxattr sys_listxattr
+231 common llistxattr sys_llistxattr
+232 common flistxattr sys_flistxattr
+233 common removexattr sys_removexattr
+234 common lremovexattr sys_lremovexattr
+235 common fremovexattr sys_fremovexattr
+236 common gettid sys_gettid
+237 common tkill sys_tkill
+238 common futex sys_futex
+239 common sched_setaffinity sys_sched_setaffinity
+240 common sched_getaffinity sys_sched_getaffinity
+241 common tgkill sys_tgkill
+243 common io_setup sys_io_setup
+244 common io_destroy sys_io_destroy
+245 common io_getevents sys_io_getevents
+246 common io_submit sys_io_submit
+247 common io_cancel sys_io_cancel
+248 common exit_group sys_exit_group
+249 common epoll_create sys_epoll_create
+250 common epoll_ctl sys_epoll_ctl
+251 common epoll_wait sys_epoll_wait
+252 common set_tid_address sys_set_tid_address
+253 common fadvise64 sys_fadvise64_64
+254 common timer_create sys_timer_create
+255 common timer_settime sys_timer_settime
+256 common timer_gettime sys_timer_gettime
+257 common timer_getoverrun sys_timer_getoverrun
+258 common timer_delete sys_timer_delete
+259 common clock_settime sys_clock_settime
+260 common clock_gettime sys_clock_gettime
+261 common clock_getres sys_clock_getres
+262 common clock_nanosleep sys_clock_nanosleep
+265 common statfs64 sys_statfs64
+266 common fstatfs64 sys_fstatfs64
+267 common remap_file_pages sys_remap_file_pages
+268 common mbind sys_mbind
+269 common get_mempolicy sys_get_mempolicy
+270 common set_mempolicy sys_set_mempolicy
+271 common mq_open sys_mq_open
+272 common mq_unlink sys_mq_unlink
+273 common mq_timedsend sys_mq_timedsend
+274 common mq_timedreceive sys_mq_timedreceive
+275 common mq_notify sys_mq_notify
+276 common mq_getsetattr sys_mq_getsetattr
+277 common kexec_load sys_kexec_load
+278 common add_key sys_add_key
+279 common request_key sys_request_key
+280 common keyctl sys_keyctl
+281 common waitid sys_waitid
+282 common ioprio_set sys_ioprio_set
+283 common ioprio_get sys_ioprio_get
+284 common inotify_init sys_inotify_init
+285 common inotify_add_watch sys_inotify_add_watch
+286 common inotify_rm_watch sys_inotify_rm_watch
+287 common migrate_pages sys_migrate_pages
+288 common openat sys_openat
+289 common mkdirat sys_mkdirat
+290 common mknodat sys_mknodat
+291 common fchownat sys_fchownat
+292 common futimesat sys_futimesat
+293 common newfstatat sys_newfstatat
+294 common unlinkat sys_unlinkat
+295 common renameat sys_renameat
+296 common linkat sys_linkat
+297 common symlinkat sys_symlinkat
+298 common readlinkat sys_readlinkat
+299 common fchmodat sys_fchmodat
+300 common faccessat sys_faccessat
+301 common pselect6 sys_pselect6
+302 common ppoll sys_ppoll
+303 common unshare sys_unshare
+304 common set_robust_list sys_set_robust_list
+305 common get_robust_list sys_get_robust_list
+306 common splice sys_splice
+307 common sync_file_range sys_sync_file_range
+308 common tee sys_tee
+309 common vmsplice sys_vmsplice
+310 common move_pages sys_move_pages
+311 common getcpu sys_getcpu
+312 common epoll_pwait sys_epoll_pwait
+313 common utimes sys_utimes
+314 common fallocate sys_fallocate
+315 common utimensat sys_utimensat
+316 common signalfd sys_signalfd
+317 common timerfd sys_ni_syscall
+318 common eventfd sys_eventfd
+319 common timerfd_create sys_timerfd_create
+320 common timerfd_settime sys_timerfd_settime
+321 common timerfd_gettime sys_timerfd_gettime
+322 common signalfd4 sys_signalfd4
+323 common eventfd2 sys_eventfd2
+324 common inotify_init1 sys_inotify_init1
+325 common pipe2 sys_pipe2
+326 common dup3 sys_dup3
+327 common epoll_create1 sys_epoll_create1
+328 common preadv sys_preadv
+329 common pwritev sys_pwritev
+330 common rt_tgsigqueueinfo sys_rt_tgsigqueueinfo
+331 common perf_event_open sys_perf_event_open
+332 common fanotify_init sys_fanotify_init
+333 common fanotify_mark sys_fanotify_mark
+334 common prlimit64 sys_prlimit64
+335 common name_to_handle_at sys_name_to_handle_at
+336 common open_by_handle_at sys_open_by_handle_at
+337 common clock_adjtime sys_clock_adjtime
+338 common syncfs sys_syncfs
+339 common setns sys_setns
+340 common process_vm_readv sys_process_vm_readv
+341 common process_vm_writev sys_process_vm_writev
+342 common s390_runtime_instr sys_s390_runtime_instr
+343 common kcmp sys_kcmp
+344 common finit_module sys_finit_module
+345 common sched_setattr sys_sched_setattr
+346 common sched_getattr sys_sched_getattr
+347 common renameat2 sys_renameat2
+348 common seccomp sys_seccomp
+349 common getrandom sys_getrandom
+350 common memfd_create sys_memfd_create
+351 common bpf sys_bpf
+352 common s390_pci_mmio_write sys_s390_pci_mmio_write
+353 common s390_pci_mmio_read sys_s390_pci_mmio_read
+354 common execveat sys_execveat
+355 common userfaultfd sys_userfaultfd
+356 common membarrier sys_membarrier
+357 common recvmmsg sys_recvmmsg
+358 common sendmmsg sys_sendmmsg
+359 common socket sys_socket
+360 common socketpair sys_socketpair
+361 common bind sys_bind
+362 common connect sys_connect
+363 common listen sys_listen
+364 common accept4 sys_accept4
+365 common getsockopt sys_getsockopt
+366 common setsockopt sys_setsockopt
+367 common getsockname sys_getsockname
+368 common getpeername sys_getpeername
+369 common sendto sys_sendto
+370 common sendmsg sys_sendmsg
+371 common recvfrom sys_recvfrom
+372 common recvmsg sys_recvmsg
+373 common shutdown sys_shutdown
+374 common mlock2 sys_mlock2
+375 common copy_file_range sys_copy_file_range
+376 common preadv2 sys_preadv2
+377 common pwritev2 sys_pwritev2
+378 common s390_guarded_storage sys_s390_guarded_storage
+379 common statx sys_statx
+380 common s390_sthyi sys_s390_sthyi
+381 common kexec_file_load sys_kexec_file_load
+382 common io_pgetevents sys_io_pgetevents
+383 common rseq sys_rseq
+384 common pkey_mprotect sys_pkey_mprotect
+385 common pkey_alloc sys_pkey_alloc
+386 common pkey_free sys_pkey_free
# room for arch specific syscalls
-392 64 semtimedop sys_semtimedop -
-393 common semget sys_semget sys_semget
-394 common semctl sys_semctl compat_sys_semctl
-395 common shmget sys_shmget sys_shmget
-396 common shmctl sys_shmctl compat_sys_shmctl
-397 common shmat sys_shmat compat_sys_shmat
-398 common shmdt sys_shmdt sys_shmdt
-399 common msgget sys_msgget sys_msgget
-400 common msgsnd sys_msgsnd compat_sys_msgsnd
-401 common msgrcv sys_msgrcv compat_sys_msgrcv
-402 common msgctl sys_msgctl compat_sys_msgctl
-403 32 clock_gettime64 - sys_clock_gettime
-404 32 clock_settime64 - sys_clock_settime
-405 32 clock_adjtime64 - sys_clock_adjtime
-406 32 clock_getres_time64 - sys_clock_getres
-407 32 clock_nanosleep_time64 - sys_clock_nanosleep
-408 32 timer_gettime64 - sys_timer_gettime
-409 32 timer_settime64 - sys_timer_settime
-410 32 timerfd_gettime64 - sys_timerfd_gettime
-411 32 timerfd_settime64 - sys_timerfd_settime
-412 32 utimensat_time64 - sys_utimensat
-413 32 pselect6_time64 - compat_sys_pselect6_time64
-414 32 ppoll_time64 - compat_sys_ppoll_time64
-416 32 io_pgetevents_time64 - compat_sys_io_pgetevents_time64
-417 32 recvmmsg_time64 - compat_sys_recvmmsg_time64
-418 32 mq_timedsend_time64 - sys_mq_timedsend
-419 32 mq_timedreceive_time64 - sys_mq_timedreceive
-420 32 semtimedop_time64 - sys_semtimedop
-421 32 rt_sigtimedwait_time64 - compat_sys_rt_sigtimedwait_time64
-422 32 futex_time64 - sys_futex
-423 32 sched_rr_get_interval_time64 - sys_sched_rr_get_interval
-424 common pidfd_send_signal sys_pidfd_send_signal sys_pidfd_send_signal
-425 common io_uring_setup sys_io_uring_setup sys_io_uring_setup
-426 common io_uring_enter sys_io_uring_enter sys_io_uring_enter
-427 common io_uring_register sys_io_uring_register sys_io_uring_register
-428 common open_tree sys_open_tree sys_open_tree
-429 common move_mount sys_move_mount sys_move_mount
-430 common fsopen sys_fsopen sys_fsopen
-431 common fsconfig sys_fsconfig sys_fsconfig
-432 common fsmount sys_fsmount sys_fsmount
-433 common fspick sys_fspick sys_fspick
-434 common pidfd_open sys_pidfd_open sys_pidfd_open
-435 common clone3 sys_clone3 sys_clone3
-436 common close_range sys_close_range sys_close_range
-437 common openat2 sys_openat2 sys_openat2
-438 common pidfd_getfd sys_pidfd_getfd sys_pidfd_getfd
-439 common faccessat2 sys_faccessat2 sys_faccessat2
-440 common process_madvise sys_process_madvise sys_process_madvise
-441 common epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
-442 common mount_setattr sys_mount_setattr sys_mount_setattr
-443 common quotactl_fd sys_quotactl_fd sys_quotactl_fd
-444 common landlock_create_ruleset sys_landlock_create_ruleset sys_landlock_create_ruleset
-445 common landlock_add_rule sys_landlock_add_rule sys_landlock_add_rule
-446 common landlock_restrict_self sys_landlock_restrict_self sys_landlock_restrict_self
-447 common memfd_secret sys_memfd_secret sys_memfd_secret
-448 common process_mrelease sys_process_mrelease sys_process_mrelease
-449 common futex_waitv sys_futex_waitv sys_futex_waitv
-450 common set_mempolicy_home_node sys_set_mempolicy_home_node sys_set_mempolicy_home_node
-451 common cachestat sys_cachestat sys_cachestat
-452 common fchmodat2 sys_fchmodat2 sys_fchmodat2
-453 common map_shadow_stack sys_map_shadow_stack sys_map_shadow_stack
-454 common futex_wake sys_futex_wake sys_futex_wake
-455 common futex_wait sys_futex_wait sys_futex_wait
-456 common futex_requeue sys_futex_requeue sys_futex_requeue
-457 common statmount sys_statmount sys_statmount
-458 common listmount sys_listmount sys_listmount
-459 common lsm_get_self_attr sys_lsm_get_self_attr sys_lsm_get_self_attr
-460 common lsm_set_self_attr sys_lsm_set_self_attr sys_lsm_set_self_attr
-461 common lsm_list_modules sys_lsm_list_modules sys_lsm_list_modules
-462 common mseal sys_mseal sys_mseal
-463 common setxattrat sys_setxattrat sys_setxattrat
-464 common getxattrat sys_getxattrat sys_getxattrat
-465 common listxattrat sys_listxattrat sys_listxattrat
-466 common removexattrat sys_removexattrat sys_removexattrat
-467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr
-468 common file_getattr sys_file_getattr sys_file_getattr
-469 common file_setattr sys_file_setattr sys_file_setattr
+392 common semtimedop sys_semtimedop
+393 common semget sys_semget
+394 common semctl sys_semctl
+395 common shmget sys_shmget
+396 common shmctl sys_shmctl
+397 common shmat sys_shmat
+398 common shmdt sys_shmdt
+399 common msgget sys_msgget
+400 common msgsnd sys_msgsnd
+401 common msgrcv sys_msgrcv
+402 common msgctl sys_msgctl
+424 common pidfd_send_signal sys_pidfd_send_signal
+425 common io_uring_setup sys_io_uring_setup
+426 common io_uring_enter sys_io_uring_enter
+427 common io_uring_register sys_io_uring_register
+428 common open_tree sys_open_tree
+429 common move_mount sys_move_mount
+430 common fsopen sys_fsopen
+431 common fsconfig sys_fsconfig
+432 common fsmount sys_fsmount
+433 common fspick sys_fspick
+434 common pidfd_open sys_pidfd_open
+435 common clone3 sys_clone3
+436 common close_range sys_close_range
+437 common openat2 sys_openat2
+438 common pidfd_getfd sys_pidfd_getfd
+439 common faccessat2 sys_faccessat2
+440 common process_madvise sys_process_madvise
+441 common epoll_pwait2 sys_epoll_pwait2
+442 common mount_setattr sys_mount_setattr
+443 common quotactl_fd sys_quotactl_fd
+444 common landlock_create_ruleset sys_landlock_create_ruleset
+445 common landlock_add_rule sys_landlock_add_rule
+446 common landlock_restrict_self sys_landlock_restrict_self
+447 common memfd_secret sys_memfd_secret
+448 common process_mrelease sys_process_mrelease
+449 common futex_waitv sys_futex_waitv
+450 common set_mempolicy_home_node sys_set_mempolicy_home_node
+451 common cachestat sys_cachestat
+452 common fchmodat2 sys_fchmodat2
+453 common map_shadow_stack sys_map_shadow_stack
+454 common futex_wake sys_futex_wake
+455 common futex_wait sys_futex_wait
+456 common futex_requeue sys_futex_requeue
+457 common statmount sys_statmount
+458 common listmount sys_listmount
+459 common lsm_get_self_attr sys_lsm_get_self_attr
+460 common lsm_set_self_attr sys_lsm_set_self_attr
+461 common lsm_list_modules sys_lsm_list_modules
+462 common mseal sys_mseal
+463 common setxattrat sys_setxattrat
+464 common getxattrat sys_getxattrat
+465 common listxattrat sys_listxattrat
+466 common removexattrat sys_removexattrat
+467 common open_tree_attr sys_open_tree_attr
+468 common file_getattr sys_file_getattr
+469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/s390/kernel/syscalls/syscalltbl b/arch/s390/kernel/syscalls/syscalltbl
deleted file mode 100755
index fbac1732f874..000000000000
--- a/arch/s390/kernel/syscalls/syscalltbl
+++ /dev/null
@@ -1,232 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
-#
-# Generate system call table and header files
-#
-# Copyright IBM Corp. 2018
-# Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
-
-#
-# File path to the system call table definition.
-# You can set the path with the -i option. If omitted,
-# system call table definitions are read from standard input.
-#
-SYSCALL_TBL=""
-
-
-create_syscall_table_entries()
-{
- local nr abi name entry64 entry32 _ignore
- local temp=$(mktemp ${TMPDIR:-/tmp}/syscalltbl-common.XXXXXXXXX)
-
- (
- #
- # Initialize with 0 to create an NI_SYSCALL for 0
- #
- local prev_nr=0 prev_32=sys_ni_syscall prev_64=sys_ni_syscall
- while read nr abi name entry64 entry32 _ignore; do
- test x$entry32 = x- && entry32=sys_ni_syscall
- test x$entry64 = x- && entry64=sys_ni_syscall
-
- if test $prev_nr -eq $nr; then
- #
- # Same syscall but different ABI, just update
- # the respective entry point
- #
- case $abi in
- 32)
- prev_32=$entry32
- ;;
- 64)
- prev_64=$entry64
- ;;
- esac
- continue;
- else
- printf "%d\t%s\t%s\n" $prev_nr $prev_64 $prev_32
- fi
-
- prev_nr=$nr
- prev_64=$entry64
- prev_32=$entry32
- done
- printf "%d\t%s\t%s\n" $prev_nr $prev_64 $prev_32
- ) >> $temp
-
- #
- # Check for duplicate syscall numbers
- #
- if ! cat $temp |cut -f1 |uniq -d 2>&1; then
- echo "Error: generated system call table contains duplicate entries: $temp" >&2
- exit 1
- fi
-
- #
- # Generate syscall table
- #
- prev_nr=0
- while read nr entry64 entry32; do
- while test $prev_nr -lt $((nr - 1)); do
- printf "NI_SYSCALL\n"
- prev_nr=$((prev_nr + 1))
- done
- if test x$entry64 = xsys_ni_syscall &&
- test x$entry32 = xsys_ni_syscall; then
- printf "NI_SYSCALL\n"
- else
- printf "SYSCALL(%s,%s)\n" $entry64 $entry32
- fi
- prev_nr=$nr
- done < $temp
- rm $temp
-}
-
-generate_syscall_table()
-{
- cat <<-EoHEADER
- /* SPDX-License-Identifier: GPL-2.0 */
- /*
- * Definitions for sys_call_table, each line represents an
- * entry in the table in the form
- * SYSCALL(64 bit syscall, 31 bit emulated syscall)
- *
- * This file is meant to be included from entry.S.
- */
-
- #define NI_SYSCALL SYSCALL(sys_ni_syscall,sys_ni_syscall)
-
-EoHEADER
- grep -Ev '^(#|[[:blank:]]*$)' $SYSCALL_TBL \
- |sort -k1 -n \
- |create_syscall_table_entries
-}
-
-create_header_defines()
-{
- local nr abi name _ignore
-
- while read nr abi name _ignore; do
- printf "#define __NR_%s %d\n" $name $nr
- done
-}
-
-normalize_fileguard()
-{
- local fileguard="$1"
-
- echo "$1" |tr '[[:lower:]]' '[[:upper:]]' \
- |sed -e 's/[^A-Z0-9_]/_/g' -e 's/__/_/g'
-}
-
-generate_syscall_header()
-{
- local abis=$(echo "($1)" | tr ',' '|')
- local filename="$2"
- local fileguard suffix
-
- if test "$filename"; then
- fileguard=$(normalize_fileguard "__UAPI_ASM_S390_$2")
- else
- case "$abis" in
- *64*) suffix=64 ;;
- *32*) suffix=32 ;;
- esac
- fileguard=$(normalize_fileguard "__UAPI_ASM_S390_SYSCALLS_$suffix")
- fi
-
- cat <<-EoHEADER
- /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
- #ifndef ${fileguard}
- #define ${fileguard}
-
-EoHEADER
-
- grep -E "^[[:digit:]]+[[:space:]]+${abis}" $SYSCALL_TBL \
- |sort -k1 -n \
- |create_header_defines
-
- cat <<-EoFOOTER
-
- #endif /* ${fileguard} */
-EoFOOTER
-}
-
-__max_syscall_nr()
-{
- local abis=$(echo "($1)" | tr ',' '|')
-
- grep -E "^[[:digit:]]+[[:space:]]+${abis}" $SYSCALL_TBL \
- |sed -ne 's/^\([[:digit:]]*\)[[:space:]].*/\1/p' \
- |sort -n \
- |tail -1
-}
-
-
-generate_syscall_nr()
-{
- local abis="$1"
- local max_syscall_nr num_syscalls
-
- max_syscall_nr=$(__max_syscall_nr "$abis")
- num_syscalls=$((max_syscall_nr + 1))
-
- cat <<-EoHEADER
- /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
- #ifndef __ASM_S390_SYSCALLS_NR
- #define __ASM_S390_SYSCALLS_NR
-
- #define NR_syscalls ${num_syscalls}
-
- #endif /* __ASM_S390_SYSCALLS_NR */
-EoHEADER
-}
-
-
-#
-# Parse command line arguments
-#
-do_syscall_header=""
-do_syscall_table=""
-do_syscall_nr=""
-output_file=""
-abi_list="common,64"
-filename=""
-while getopts ":HNSXi:a:f:" arg; do
- case $arg in
- a)
- abi_list="$OPTARG"
- ;;
- i)
- SYSCALL_TBL="$OPTARG"
- ;;
- f)
- filename=${OPTARG##*/}
- ;;
- H)
- do_syscall_header=1
- ;;
- N)
- do_syscall_nr=1
- ;;
- S)
- do_syscall_table=1
- ;;
- X)
- set -x
- ;;
- :)
- echo "Missing argument for -$OPTARG" >&2
- exit 1
- ;;
- \?)
- echo "Invalid option specified" >&2
- exit 1
- ;;
- esac
-done
-
-test "$do_syscall_header" && generate_syscall_header "$abi_list" "$filename"
-test "$do_syscall_table" && generate_syscall_table
-test "$do_syscall_nr" && generate_syscall_nr "$abi_list"
-
-exit 0
diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c
index 1ea84e942bd4..33ca3e47a0e6 100644
--- a/arch/s390/kernel/sysinfo.c
+++ b/arch/s390/kernel/sysinfo.c
@@ -526,7 +526,7 @@ static __init int stsi_init_debugfs(void)
if (IS_ENABLED(CONFIG_SCHED_TOPOLOGY) && cpu_has_topology()) {
char link_to[10];
- sprintf(link_to, "15_1_%d", topology_mnest_limit());
+ snprintf(link_to, sizeof(link_to), "15_1_%d", topology_mnest_limit());
debugfs_create_symlink("topology", stsi_root, link_to);
}
return 0;
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 63517b85f4c9..bd0df61d1907 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -12,8 +12,7 @@
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
*/
-#define KMSG_COMPONENT "time"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "time: " fmt
#include <linux/kernel_stat.h>
#include <linux/errno.h>
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index 1594c80e9bc4..1913a5566ac2 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -3,8 +3,7 @@
* Copyright IBM Corp. 2007, 2011
*/
-#define KMSG_COMPONENT "cpu"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "cpu: " fmt
#include <linux/cpufeature.h>
#include <linux/workqueue.h>
diff --git a/arch/s390/kernel/uprobes.c b/arch/s390/kernel/uprobes.c
index 5b0633ea8d93..c624f3361e43 100644
--- a/arch/s390/kernel/uprobes.c
+++ b/arch/s390/kernel/uprobes.c
@@ -8,7 +8,6 @@
#include <linux/uaccess.h>
#include <linux/uprobes.h>
-#include <linux/compat.h>
#include <linux/kdebug.h>
#include <linux/sched/task_stack.h>
@@ -29,7 +28,7 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
{
if (psw_bits(regs->psw).eaba == PSW_BITS_AMODE_24BIT)
return -EINVAL;
- if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT)
+ if (psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT)
return -EINVAL;
clear_thread_flag(TIF_PER_TRAP);
auprobe->saved_per = psw_bits(regs->psw).per;
@@ -161,11 +160,6 @@ bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
/* Instruction Emulation */
-static void adjust_psw_addr(psw_t *psw, unsigned long len)
-{
- psw->addr = __rewind_psw(*psw, -len);
-}
-
#define EMU_ILLEGAL_OP 1
#define EMU_SPECIFICATION 2
#define EMU_ADDRESSING 3
@@ -353,7 +347,7 @@ static void handle_insn_ril(struct arch_uprobe *auprobe, struct pt_regs *regs)
}
break;
}
- adjust_psw_addr(&regs->psw, ilen);
+ regs->psw.addr = __forward_psw(regs->psw, ilen);
switch (rc) {
case EMU_ILLEGAL_OP:
regs->int_code = ilen << 16 | 0x0001;
@@ -373,8 +367,7 @@ static void handle_insn_ril(struct arch_uprobe *auprobe, struct pt_regs *regs)
bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
{
if ((psw_bits(regs->psw).eaba == PSW_BITS_AMODE_24BIT) ||
- ((psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT) &&
- !is_compat_task())) {
+ (psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT)) {
regs->psw.addr = __rewind_psw(regs->psw, UPROBE_SWBP_INSN_SIZE);
do_report_trap(regs, SIGILL, ILL_ILLADR, NULL);
return true;
diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c
index 5d17609bcfe1..ed46950be86f 100644
--- a/arch/s390/kernel/uv.c
+++ b/arch/s390/kernel/uv.c
@@ -4,8 +4,7 @@
*
* Copyright IBM Corp. 2019, 2024
*/
-#define KMSG_COMPONENT "prot_virt"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "prot_virt: " fmt
#include <linux/export.h>
#include <linux/kernel.h>
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
index 430feb1a5013..a27a90a199be 100644
--- a/arch/s390/kernel/vdso.c
+++ b/arch/s390/kernel/vdso.c
@@ -7,7 +7,6 @@
*/
#include <linux/binfmts.h>
-#include <linux/compat.h>
#include <linux/elf.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -23,8 +22,7 @@
#include <asm/alternative.h>
#include <asm/vdso.h>
-extern char vdso64_start[], vdso64_end[];
-extern char vdso32_start[], vdso32_end[];
+extern char vdso_start[], vdso_end[];
static int vdso_mremap(const struct vm_special_mapping *sm,
struct vm_area_struct *vma)
@@ -33,12 +31,7 @@ static int vdso_mremap(const struct vm_special_mapping *sm,
return 0;
}
-static struct vm_special_mapping vdso64_mapping = {
- .name = "[vdso]",
- .mremap = vdso_mremap,
-};
-
-static struct vm_special_mapping vdso32_mapping = {
+static struct vm_special_mapping vdso_mapping = {
.name = "[vdso]",
.mremap = vdso_mremap,
};
@@ -53,7 +46,6 @@ early_initcall(vdso_getcpu_init); /* Must be called before SMP init */
static int map_vdso(unsigned long addr, unsigned long vdso_mapping_len)
{
unsigned long vvar_start, vdso_text_start, vdso_text_len;
- struct vm_special_mapping *vdso_mapping;
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
int rc;
@@ -62,13 +54,7 @@ static int map_vdso(unsigned long addr, unsigned long vdso_mapping_len)
if (mmap_write_lock_killable(mm))
return -EINTR;
- if (is_compat_task()) {
- vdso_text_len = vdso32_end - vdso32_start;
- vdso_mapping = &vdso32_mapping;
- } else {
- vdso_text_len = vdso64_end - vdso64_start;
- vdso_mapping = &vdso64_mapping;
- }
+ vdso_text_len = vdso_end - vdso_start;
vvar_start = get_unmapped_area(NULL, addr, vdso_mapping_len, 0, 0);
rc = vvar_start;
if (IS_ERR_VALUE(vvar_start))
@@ -82,7 +68,7 @@ static int map_vdso(unsigned long addr, unsigned long vdso_mapping_len)
vma = _install_special_mapping(mm, vdso_text_start, vdso_text_len,
VM_READ|VM_EXEC|VM_SEALED_SYSMAP|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
- vdso_mapping);
+ &vdso_mapping);
if (IS_ERR(vma)) {
do_munmap(mm, vvar_start, PAGE_SIZE, NULL);
rc = PTR_ERR(vma);
@@ -122,13 +108,7 @@ static unsigned long vdso_addr(unsigned long start, unsigned long len)
unsigned long vdso_text_size(void)
{
- unsigned long size;
-
- if (is_compat_task())
- size = vdso32_end - vdso32_start;
- else
- size = vdso64_end - vdso64_start;
- return PAGE_ALIGN(size);
+ return PAGE_ALIGN(vdso_end - vdso_start);
}
unsigned long vdso_size(void)
@@ -166,7 +146,7 @@ static void vdso_apply_alternatives(void)
struct alt_instr *start, *end;
const struct elf64_hdr *hdr;
- hdr = (struct elf64_hdr *)vdso64_start;
+ hdr = (struct elf64_hdr *)vdso_start;
shdr = (void *)hdr + hdr->e_shoff;
alt = find_section(hdr, shdr, ".altinstructions");
if (!alt)
@@ -179,9 +159,7 @@ static void vdso_apply_alternatives(void)
static int __init vdso_init(void)
{
vdso_apply_alternatives();
- vdso64_mapping.pages = vdso_setup_pages(vdso64_start, vdso64_end);
- if (IS_ENABLED(CONFIG_COMPAT))
- vdso32_mapping.pages = vdso_setup_pages(vdso32_start, vdso32_end);
+ vdso_mapping.pages = vdso_setup_pages(vdso_start, vdso_end);
return 0;
}
arch_initcall(vdso_init);
diff --git a/tools/bpf/runqslower/.gitignore b/arch/s390/kernel/vdso/.gitignore
index ffdb70230c8b..652e31d82582 100644
--- a/tools/bpf/runqslower/.gitignore
+++ b/arch/s390/kernel/vdso/.gitignore
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
-/.output
+vdso.lds
diff --git a/arch/s390/kernel/vdso/Makefile b/arch/s390/kernel/vdso/Makefile
new file mode 100644
index 000000000000..2fa12d4ac106
--- /dev/null
+++ b/arch/s390/kernel/vdso/Makefile
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: GPL-2.0
+# List of files in the vdso
+
+# Include the generic Makefile to check the built vdso.
+include $(srctree)/lib/vdso/Makefile.include
+obj-vdso = vdso_user_wrapper.o note.o vgetrandom-chacha.o
+obj-cvdso = vdso_generic.o getcpu.o vgetrandom.o
+VDSO_CFLAGS_REMOVE := -pg $(CC_FLAGS_FTRACE) $(CC_FLAGS_EXPOLINE)
+CFLAGS_REMOVE_getcpu.o = $(VDSO_CFLAGS_REMOVE)
+CFLAGS_REMOVE_vgetrandom.o = $(VDSO_CFLAGS_REMOVE)
+CFLAGS_REMOVE_vdso_generic.o = $(VDSO_CFLAGS_REMOVE)
+
+ifneq ($(c-getrandom-y),)
+ CFLAGS_vgetrandom.o += -include $(c-getrandom-y)
+endif
+
+# Build rules
+
+targets := $(obj-vdso) $(obj-cvdso) vdso.so vdso.so.dbg
+obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
+obj-cvdso := $(addprefix $(obj)/, $(obj-cvdso))
+
+KBUILD_AFLAGS_VDSO := $(KBUILD_AFLAGS) -DBUILD_VDSO
+
+KBUILD_CFLAGS_VDSO := $(KBUILD_CFLAGS) -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING
+KBUILD_CFLAGS_VDSO := $(filter-out -mpacked-stack,$(KBUILD_CFLAGS_VDSO))
+KBUILD_CFLAGS_VDSO := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAGS_VDSO))
+KBUILD_CFLAGS_VDSO := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_VDSO))
+KBUILD_CFLAGS_VDSO := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_VDSO))
+KBUILD_CFLAGS_VDSO += -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables
+KBUILD_CFLAGS_VDSO += -fno-stack-protector
+ldflags-y := -shared -soname=linux-vdso.so.1 \
+ --hash-style=both --build-id=sha1 -T
+
+$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_VDSO)
+$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_VDSO)
+
+obj-y += vdso_wrapper.o
+targets += vdso.lds
+CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
+
+# Force dependency (incbin is bad)
+$(obj)/vdso_wrapper.o : $(obj)/vdso.so
+
+quiet_cmd_vdso_and_check = VDSO $@
+ cmd_vdso_and_check = $(cmd_ld); $(cmd_vdso_check)
+
+# link rule for the .so file, .lds has to be first
+$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) $(obj-cvdso) FORCE
+ $(call if_changed,vdso_and_check)
+
+# strip rule for the .so file
+$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: $(obj)/%.so.dbg FORCE
+ $(call if_changed,objcopy)
+
+# assembly rules for the .S files
+$(obj-vdso): %.o: %.S FORCE
+ $(call if_changed_dep,vdsoas)
+
+$(obj-cvdso): %.o: %.c FORCE
+ $(call if_changed_dep,vdsocc)
+
+# actual build commands
+quiet_cmd_vdsoas = VDSOA $@
+ cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<
+quiet_cmd_vdsocc = VDSOC $@
+ cmd_vdsocc = $(CC) $(c_flags) -c -o $@ $<
+
+# Generate VDSO offsets using helper script
+gen-vdsosym := $(src)/gen_vdso_offsets.sh
+quiet_cmd_vdsosym = VDSOSYM $@
+ cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
+
+include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE
+ $(call if_changed,vdsosym)
diff --git a/arch/s390/kernel/vdso64/gen_vdso_offsets.sh b/arch/s390/kernel/vdso/gen_vdso_offsets.sh
index 37f05cb38dad..359982fb002d 100755
--- a/arch/s390/kernel/vdso64/gen_vdso_offsets.sh
+++ b/arch/s390/kernel/vdso/gen_vdso_offsets.sh
@@ -12,4 +12,4 @@
#
LC_ALL=C
-sed -n 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso64_offset_\2\t0x\1/p'
+sed -n 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso_offset_\2\t0x\1/p'
diff --git a/arch/s390/kernel/vdso64/getcpu.c b/arch/s390/kernel/vdso/getcpu.c
index 5c5d4a848b76..5c5d4a848b76 100644
--- a/arch/s390/kernel/vdso64/getcpu.c
+++ b/arch/s390/kernel/vdso/getcpu.c
diff --git a/arch/s390/kernel/vdso32/note.S b/arch/s390/kernel/vdso/note.S
index db19d0680a0a..db19d0680a0a 100644
--- a/arch/s390/kernel/vdso32/note.S
+++ b/arch/s390/kernel/vdso/note.S
diff --git a/arch/s390/kernel/vdso64/vdso.h b/arch/s390/kernel/vdso/vdso.h
index 9e5397e7b590..8cff033dd854 100644
--- a/arch/s390/kernel/vdso64/vdso.h
+++ b/arch/s390/kernel/vdso/vdso.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __ARCH_S390_KERNEL_VDSO64_VDSO_H
-#define __ARCH_S390_KERNEL_VDSO64_VDSO_H
+#ifndef __ARCH_S390_KERNEL_VDSO_VDSO_H
+#define __ARCH_S390_KERNEL_VDSO_VDSO_H
#include <vdso/datapage.h>
@@ -12,4 +12,4 @@ int __s390_vdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts);
int __s390_vdso_clock_getres(clockid_t clock, struct __kernel_timespec *ts);
ssize_t __kernel_getrandom(void *buffer, size_t len, unsigned int flags, void *opaque_state, size_t opaque_len);
-#endif /* __ARCH_S390_KERNEL_VDSO64_VDSO_H */
+#endif /* __ARCH_S390_KERNEL_VDSO_VDSO_H */
diff --git a/arch/s390/kernel/vdso64/vdso64.lds.S b/arch/s390/kernel/vdso/vdso.lds.S
index e4f6551ae898..7bec4de0e8e0 100644
--- a/arch/s390/kernel/vdso64/vdso64.lds.S
+++ b/arch/s390/kernel/vdso/vdso.lds.S
@@ -7,6 +7,7 @@
#include <asm/vdso/vsyscall.h>
#include <asm/page.h>
#include <asm/vdso.h>
+#include <asm-generic/vmlinux.lds.h>
#include <vdso/datapage.h>
OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
@@ -59,47 +60,9 @@ SECTIONS
_end = .;
PROVIDE(end = .);
- /*
- * Stabs debugging sections are here too.
- */
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
+ STABS_DEBUG
+ DWARF_DEBUG
.comment 0 : { *(.comment) }
-
- /*
- * DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the
- * beginning of the section so we begin them at 0.
- */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
- /* DWARF 3 */
- .debug_pubtypes 0 : { *(.debug_pubtypes) }
- .debug_ranges 0 : { *(.debug_ranges) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : {
diff --git a/arch/s390/kernel/vdso64/vdso64_generic.c b/arch/s390/kernel/vdso/vdso_generic.c
index a9aa75643c08..a9aa75643c08 100644
--- a/arch/s390/kernel/vdso64/vdso64_generic.c
+++ b/arch/s390/kernel/vdso/vdso_generic.c
diff --git a/arch/s390/kernel/vdso64/vdso_user_wrapper.S b/arch/s390/kernel/vdso/vdso_user_wrapper.S
index aa06c85bcbd3..aa06c85bcbd3 100644
--- a/arch/s390/kernel/vdso64/vdso_user_wrapper.S
+++ b/arch/s390/kernel/vdso/vdso_user_wrapper.S
diff --git a/arch/s390/kernel/vdso32/vdso32_wrapper.S b/arch/s390/kernel/vdso/vdso_wrapper.S
index de2fb930471a..f69e62a14978 100644
--- a/arch/s390/kernel/vdso32/vdso32_wrapper.S
+++ b/arch/s390/kernel/vdso/vdso_wrapper.S
@@ -5,11 +5,11 @@
__PAGE_ALIGNED_DATA
- .globl vdso32_start, vdso32_end
+ .globl vdso_start, vdso_end
.balign PAGE_SIZE
-vdso32_start:
- .incbin "arch/s390/kernel/vdso32/vdso32.so"
+vdso_start:
+ .incbin "arch/s390/kernel/vdso/vdso.so"
.balign PAGE_SIZE
-vdso32_end:
+vdso_end:
.previous
diff --git a/arch/s390/kernel/vdso64/vgetrandom-chacha.S b/arch/s390/kernel/vdso/vgetrandom-chacha.S
index 09c034c2f853..09c034c2f853 100644
--- a/arch/s390/kernel/vdso64/vgetrandom-chacha.S
+++ b/arch/s390/kernel/vdso/vgetrandom-chacha.S
diff --git a/arch/s390/kernel/vdso64/vgetrandom.c b/arch/s390/kernel/vdso/vgetrandom.c
index b5268b507fb5..b5268b507fb5 100644
--- a/arch/s390/kernel/vdso64/vgetrandom.c
+++ b/arch/s390/kernel/vdso/vgetrandom.c
diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile
deleted file mode 100644
index 1e4ddd1a683f..000000000000
--- a/arch/s390/kernel/vdso32/Makefile
+++ /dev/null
@@ -1,64 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# List of files in the vdso
-
-# Include the generic Makefile to check the built vdso.
-include $(srctree)/lib/vdso/Makefile.include
-obj-vdso32 = vdso_user_wrapper-32.o note-32.o
-
-# Build rules
-
-targets := $(obj-vdso32) vdso32.so vdso32.so.dbg
-obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
-
-KBUILD_AFLAGS += -DBUILD_VDSO
-KBUILD_CFLAGS += -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING
-
-KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS))
-KBUILD_AFLAGS_32 += -m31 -s
-
-KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
-KBUILD_CFLAGS_32 := $(filter-out -mpacked-stack,$(KBUILD_CFLAGS))
-KBUILD_CFLAGS_32 := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAGS_32))
-KBUILD_CFLAGS_32 := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_32))
-KBUILD_CFLAGS_32 += -m31 -fPIC -shared -fno-common -fno-builtin -fasynchronous-unwind-tables
-
-LDFLAGS_vdso32.so.dbg += -shared -soname=linux-vdso32.so.1 \
- --hash-style=both --build-id=sha1 -melf_s390 -T
-
-$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
-$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
-
-obj-y += vdso32_wrapper.o
-targets += vdso32.lds
-CPPFLAGS_vdso32.lds += -P -C -U$(ARCH)
-
-# Force dependency (incbin is bad)
-$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so
-
-quiet_cmd_vdso_and_check = VDSO $@
- cmd_vdso_and_check = $(cmd_ld); $(cmd_vdso_check)
-
-$(obj)/vdso32.so.dbg: $(obj)/vdso32.lds $(obj-vdso32) FORCE
- $(call if_changed,vdso_and_check)
-
-# strip rule for the .so file
-$(obj)/%.so: OBJCOPYFLAGS := -S
-$(obj)/%.so: $(obj)/%.so.dbg FORCE
- $(call if_changed,objcopy)
-
-$(obj-vdso32): %-32.o: %.S FORCE
- $(call if_changed_dep,vdso32as)
-
-# actual build commands
-quiet_cmd_vdso32as = VDSO32A $@
- cmd_vdso32as = $(CC) $(a_flags) -c -o $@ $<
-quiet_cmd_vdso32cc = VDSO32C $@
- cmd_vdso32cc = $(CC) $(c_flags) -c -o $@ $<
-
-# Generate VDSO offsets using helper script
-gen-vdsosym := $(src)/gen_vdso_offsets.sh
-quiet_cmd_vdsosym = VDSOSYM $@
- cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
-
-include/generated/vdso32-offsets.h: $(obj)/vdso32.so.dbg FORCE
- $(call if_changed,vdsosym)
diff --git a/arch/s390/kernel/vdso32/gen_vdso_offsets.sh b/arch/s390/kernel/vdso32/gen_vdso_offsets.sh
deleted file mode 100755
index 9c4f951e227d..000000000000
--- a/arch/s390/kernel/vdso32/gen_vdso_offsets.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-# SPDX-License-Identifier: GPL-2.0
-
-#
-# Match symbols in the DSO that look like VDSO_*; produce a header file
-# of constant offsets into the shared object.
-#
-# Doing this inside the Makefile will break the $(filter-out) function,
-# causing Kbuild to rebuild the vdso-offsets header file every time.
-#
-# Inspired by arm64 version.
-#
-
-LC_ALL=C
-sed -n 's/\([0-9a-f]*\) . __kernel_compat_\(.*\)/\#define vdso32_offset_\2\t0x\1/p'
diff --git a/arch/s390/kernel/vdso32/vdso32.lds.S b/arch/s390/kernel/vdso32/vdso32.lds.S
deleted file mode 100644
index 9630d58c2080..000000000000
--- a/arch/s390/kernel/vdso32/vdso32.lds.S
+++ /dev/null
@@ -1,140 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * This is the infamous ld script for the 64 bits vdso
- * library
- */
-
-#include <asm/page.h>
-#include <asm/vdso.h>
-#include <vdso/datapage.h>
-
-OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
-OUTPUT_ARCH(s390:31-bit)
-
-SECTIONS
-{
- VDSO_VVAR_SYMS
-
- . = SIZEOF_HEADERS;
-
- .hash : { *(.hash) } :text
- .gnu.hash : { *(.gnu.hash) }
- .dynsym : { *(.dynsym) }
- .dynstr : { *(.dynstr) }
- .gnu.version : { *(.gnu.version) }
- .gnu.version_d : { *(.gnu.version_d) }
- .gnu.version_r : { *(.gnu.version_r) }
-
- .note : { *(.note.*) } :text :note
-
- . = ALIGN(16);
- .text : {
- *(.text .stub .text.* .gnu.linkonce.t.*)
- } :text
- PROVIDE(__etext = .);
- PROVIDE(_etext = .);
- PROVIDE(etext = .);
-
- /*
- * Other stuff is appended to the text segment:
- */
- .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
- .rodata1 : { *(.rodata1) }
-
- .dynamic : { *(.dynamic) } :text :dynamic
-
- .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
- .eh_frame : { KEEP (*(.eh_frame)) } :text
- .gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) }
-
- .rela.dyn ALIGN(8) : { *(.rela.dyn) }
- .got ALIGN(8) : { *(.got .toc) }
- .got.plt ALIGN(8) : { *(.got.plt) }
-
- _end = .;
- PROVIDE(end = .);
-
- /*
- * Stabs debugging sections are here too.
- */
- .stab 0 : { *(.stab) }
- .stabstr 0 : { *(.stabstr) }
- .stab.excl 0 : { *(.stab.excl) }
- .stab.exclstr 0 : { *(.stab.exclstr) }
- .stab.index 0 : { *(.stab.index) }
- .stab.indexstr 0 : { *(.stab.indexstr) }
- .comment 0 : { *(.comment) }
-
- /*
- * DWARF debug sections.
- * Symbols in the DWARF debugging sections are relative to the
- * beginning of the section so we begin them at 0.
- */
- /* DWARF 1 */
- .debug 0 : { *(.debug) }
- .line 0 : { *(.line) }
- /* GNU DWARF 1 extensions */
- .debug_srcinfo 0 : { *(.debug_srcinfo) }
- .debug_sfnames 0 : { *(.debug_sfnames) }
- /* DWARF 1.1 and DWARF 2 */
- .debug_aranges 0 : { *(.debug_aranges) }
- .debug_pubnames 0 : { *(.debug_pubnames) }
- /* DWARF 2 */
- .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
- .debug_abbrev 0 : { *(.debug_abbrev) }
- .debug_line 0 : { *(.debug_line) }
- .debug_frame 0 : { *(.debug_frame) }
- .debug_str 0 : { *(.debug_str) }
- .debug_loc 0 : { *(.debug_loc) }
- .debug_macinfo 0 : { *(.debug_macinfo) }
- /* SGI/MIPS DWARF 2 extensions */
- .debug_weaknames 0 : { *(.debug_weaknames) }
- .debug_funcnames 0 : { *(.debug_funcnames) }
- .debug_typenames 0 : { *(.debug_typenames) }
- .debug_varnames 0 : { *(.debug_varnames) }
- /* DWARF 3 */
- .debug_pubtypes 0 : { *(.debug_pubtypes) }
- .debug_ranges 0 : { *(.debug_ranges) }
- .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
-
- /DISCARD/ : {
- *(.note.GNU-stack)
- *(.branch_lt)
- *(.data .data.* .gnu.linkonce.d.* .sdata*)
- *(.bss .sbss .dynbss .dynsbss)
- }
-}
-
-/*
- * Very old versions of ld do not recognize this name token; use the constant.
- */
-#define PT_GNU_EH_FRAME 0x6474e550
-
-/*
- * We must supply the ELF program headers explicitly to get just one
- * PT_LOAD segment, and set the flags explicitly to make segments read-only.
- */
-PHDRS
-{
- text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */
- dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
- note PT_NOTE FLAGS(4); /* PF_R */
- eh_frame_hdr PT_GNU_EH_FRAME;
-}
-
-/*
- * This controls what symbols we export from the DSO.
- */
-VERSION
-{
- VDSO_VERSION_STRING {
- global:
- /*
- * Has to be there for the kernel to find
- */
- __kernel_compat_restart_syscall;
- __kernel_compat_rt_sigreturn;
- __kernel_compat_sigreturn;
- local: *;
- };
-}
diff --git a/arch/s390/kernel/vdso32/vdso_user_wrapper.S b/arch/s390/kernel/vdso32/vdso_user_wrapper.S
deleted file mode 100644
index 2e645003fdaf..000000000000
--- a/arch/s390/kernel/vdso32/vdso_user_wrapper.S
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-#include <linux/linkage.h>
-#include <asm/unistd.h>
-#include <asm/dwarf.h>
-
-.macro vdso_syscall func,syscall
- .globl __kernel_compat_\func
- .type __kernel_compat_\func,@function
- __ALIGN
-__kernel_compat_\func:
- CFI_STARTPROC
- svc \syscall
- /* Make sure we notice when a syscall returns, which shouldn't happen */
- .word 0
- CFI_ENDPROC
- .size __kernel_compat_\func,.-__kernel_compat_\func
-.endm
-
-vdso_syscall restart_syscall,__NR_restart_syscall
-vdso_syscall sigreturn,__NR_sigreturn
-vdso_syscall rt_sigreturn,__NR_rt_sigreturn
diff --git a/arch/s390/kernel/vdso64/.gitignore b/arch/s390/kernel/vdso64/.gitignore
deleted file mode 100644
index 4ec80685fecc..000000000000
--- a/arch/s390/kernel/vdso64/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-vdso64.lds
diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile
deleted file mode 100644
index d8f0df742809..000000000000
--- a/arch/s390/kernel/vdso64/Makefile
+++ /dev/null
@@ -1,79 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# List of files in the vdso
-
-# Include the generic Makefile to check the built vdso.
-include $(srctree)/lib/vdso/Makefile.include
-obj-vdso64 = vdso_user_wrapper.o note.o vgetrandom-chacha.o
-obj-cvdso64 = vdso64_generic.o getcpu.o vgetrandom.o
-VDSO_CFLAGS_REMOVE := -pg $(CC_FLAGS_FTRACE) $(CC_FLAGS_EXPOLINE)
-CFLAGS_REMOVE_getcpu.o = $(VDSO_CFLAGS_REMOVE)
-CFLAGS_REMOVE_vgetrandom.o = $(VDSO_CFLAGS_REMOVE)
-CFLAGS_REMOVE_vdso64_generic.o = $(VDSO_CFLAGS_REMOVE)
-
-ifneq ($(c-getrandom-y),)
- CFLAGS_vgetrandom.o += -include $(c-getrandom-y)
-endif
-
-# Build rules
-
-targets := $(obj-vdso64) $(obj-cvdso64) vdso64.so vdso64.so.dbg
-obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64))
-obj-cvdso64 := $(addprefix $(obj)/, $(obj-cvdso64))
-
-KBUILD_AFLAGS += -DBUILD_VDSO
-KBUILD_CFLAGS += -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING
-
-KBUILD_AFLAGS_64 := $(filter-out -m64,$(KBUILD_AFLAGS))
-KBUILD_AFLAGS_64 += -m64
-
-KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS))
-KBUILD_CFLAGS_64 := $(filter-out -mpacked-stack,$(KBUILD_CFLAGS_64))
-KBUILD_CFLAGS_64 := $(filter-out -mno-pic-data-is-text-relative,$(KBUILD_CFLAGS_64))
-KBUILD_CFLAGS_64 := $(filter-out -munaligned-symbols,$(KBUILD_CFLAGS_64))
-KBUILD_CFLAGS_64 := $(filter-out -fno-asynchronous-unwind-tables,$(KBUILD_CFLAGS_64))
-KBUILD_CFLAGS_64 += -m64 -fPIC -fno-common -fno-builtin -fasynchronous-unwind-tables
-ldflags-y := -shared -soname=linux-vdso64.so.1 \
- --hash-style=both --build-id=sha1 -T
-
-$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_64)
-$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_64)
-
-obj-y += vdso64_wrapper.o
-targets += vdso64.lds
-CPPFLAGS_vdso64.lds += -P -C -U$(ARCH)
-
-# Force dependency (incbin is bad)
-$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so
-
-quiet_cmd_vdso_and_check = VDSO $@
- cmd_vdso_and_check = $(cmd_ld); $(cmd_vdso_check)
-
-# link rule for the .so file, .lds has to be first
-$(obj)/vdso64.so.dbg: $(obj)/vdso64.lds $(obj-vdso64) $(obj-cvdso64) FORCE
- $(call if_changed,vdso_and_check)
-
-# strip rule for the .so file
-$(obj)/%.so: OBJCOPYFLAGS := -S
-$(obj)/%.so: $(obj)/%.so.dbg FORCE
- $(call if_changed,objcopy)
-
-# assembly rules for the .S files
-$(obj-vdso64): %.o: %.S FORCE
- $(call if_changed_dep,vdso64as)
-
-$(obj-cvdso64): %.o: %.c FORCE
- $(call if_changed_dep,vdso64cc)
-
-# actual build commands
-quiet_cmd_vdso64as = VDSO64A $@
- cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $<
-quiet_cmd_vdso64cc = VDSO64C $@
- cmd_vdso64cc = $(CC) $(c_flags) -c -o $@ $<
-
-# Generate VDSO offsets using helper script
-gen-vdsosym := $(src)/gen_vdso_offsets.sh
-quiet_cmd_vdsosym = VDSOSYM $@
- cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
-
-include/generated/vdso64-offsets.h: $(obj)/vdso64.so.dbg FORCE
- $(call if_changed,vdsosym)
diff --git a/arch/s390/kernel/vdso64/note.S b/arch/s390/kernel/vdso64/note.S
deleted file mode 100644
index db19d0680a0a..000000000000
--- a/arch/s390/kernel/vdso64/note.S
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
- * Here we can supply some information useful to userland.
- */
-
-#include <linux/uts.h>
-#include <linux/version.h>
-#include <linux/elfnote.h>
-
-ELFNOTE_START(Linux, 0, "a")
- .long LINUX_VERSION_CODE
-ELFNOTE_END
diff --git a/arch/s390/kernel/vdso64/vdso64_wrapper.S b/arch/s390/kernel/vdso64/vdso64_wrapper.S
deleted file mode 100644
index 672184998623..000000000000
--- a/arch/s390/kernel/vdso64/vdso64_wrapper.S
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/init.h>
-#include <linux/linkage.h>
-#include <asm/page.h>
-
- __PAGE_ALIGNED_DATA
-
- .globl vdso64_start, vdso64_end
- .balign PAGE_SIZE
-vdso64_start:
- .incbin "arch/s390/kernel/vdso64/vdso64.so"
- .balign PAGE_SIZE
-vdso64_end:
-
- .previous
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index d74d4c52ccd0..53bcbb91bb9b 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -51,7 +51,7 @@ SECTIONS
IRQENTRY_TEXT
SOFTIRQENTRY_TEXT
FTRACE_HOTPATCH_TRAMPOLINES_TEXT
- *(.text.*_indirect_*)
+ *(.text..*_indirect_*)
*(.gnu.warning)
. = ALIGN(PAGE_SIZE);
_etext = .; /* End of text section */
@@ -150,6 +150,15 @@ SECTIONS
*(.altinstr_replacement)
}
+#ifdef CONFIG_STACKPROTECTOR
+ . = ALIGN(8);
+ .stack_prot_table : {
+ __stack_prot_start = .;
+ KEEP(*(__stack_protector_loc))
+ __stack_prot_end = .;
+ }
+#endif
+
/*
* Table with the patch locations to undo expolines
*/
@@ -257,6 +266,10 @@ SECTIONS
QUAD(invalid_pg_dir)
QUAD(__alt_instructions)
QUAD(__alt_instructions_end)
+#ifdef CONFIG_STACKPROTECTOR
+ QUAD(__stack_prot_start)
+ QUAD(__stack_prot_end)
+#endif
#ifdef CONFIG_KASAN
QUAD(kasan_early_shadow_page)
QUAD(kasan_early_shadow_pte)
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index c62a868cf2b6..f55574af98cc 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -7,8 +7,7 @@
* Author(s): Carsten Otte <cotte@de.ibm.com>
*/
-#define KMSG_COMPONENT "kvm-s390"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "kvm-s390: " fmt
#include <linux/cpufeature.h>
#include <linux/interrupt.h>
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 16ba04062854..ff3a185f156c 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -10,8 +10,7 @@
* Jason J. Herne <jjherne@us.ibm.com>
*/
-#define KMSG_COMPONENT "kvm-s390"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "kvm-s390: " fmt
#include <linux/compiler.h>
#include <linux/export.h>
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 9a71b6e00948..0b14d894f38a 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -754,7 +754,7 @@ int is_valid_psw(psw_t *psw)
int kvm_s390_handle_lpsw(struct kvm_vcpu *vcpu)
{
psw_t *gpsw = &vcpu->arch.sie_block->gpsw;
- psw_compat_t new_psw;
+ psw32_t new_psw;
u64 addr;
int rc;
u8 ar;
diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c
index e2a6eb92420f..eb7ef63fab1e 100644
--- a/arch/s390/mm/cmm.c
+++ b/arch/s390/mm/cmm.c
@@ -321,8 +321,8 @@ static int cmm_timeout_handler(const struct ctl_table *ctl, int write,
cmm_set_timeout(nr, seconds);
*ppos += *lenp;
} else {
- len = sprintf(buf, "%ld %ld\n",
- cmm_timeout_pages, cmm_timeout_seconds);
+ len = scnprintf(buf, sizeof(buf), "%ld %ld\n",
+ cmm_timeout_pages, cmm_timeout_seconds);
if (len > *lenp)
len = *lenp;
memcpy(buffer, buf, len);
diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c
index 9af2aae0a515..89badbe72ae7 100644
--- a/arch/s390/mm/dump_pagetables.c
+++ b/arch/s390/mm/dump_pagetables.c
@@ -51,7 +51,7 @@ struct pg_state {
struct seq_file *__m = (m); \
\
if (__m) \
- seq_printf(__m, fmt); \
+ seq_puts(__m, fmt); \
})
static void print_prot(struct seq_file *m, unsigned int pr, int level)
@@ -291,16 +291,14 @@ static int ptdump_cmp(const void *a, const void *b)
static int add_marker(unsigned long start, unsigned long end, const char *name)
{
- size_t oldsize, newsize;
-
- oldsize = markers_cnt * sizeof(*markers);
- newsize = oldsize + 2 * sizeof(*markers);
- if (!oldsize)
- markers = kvmalloc(newsize, GFP_KERNEL);
- else
- markers = kvrealloc(markers, newsize, GFP_KERNEL);
- if (!markers)
- goto error;
+ struct addr_marker *new;
+ size_t newsize;
+
+ newsize = (markers_cnt + 2) * sizeof(*markers);
+ new = kvrealloc(markers, newsize, GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+ markers = new;
markers[markers_cnt].is_start = 1;
markers[markers_cnt].start_address = start;
markers[markers_cnt].size = end - start;
@@ -312,9 +310,6 @@ static int add_marker(unsigned long start, unsigned long end, const char *name)
markers[markers_cnt].name = name;
markers_cnt++;
return 0;
-error:
- markers_cnt = 0;
- return -ENOMEM;
}
static int pt_dump_init(void)
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c
index f7da53e212f5..6cc33c705de2 100644
--- a/arch/s390/mm/extmem.c
+++ b/arch/s390/mm/extmem.c
@@ -7,8 +7,7 @@
* Copyright IBM Corp. 2002, 2004
*/
-#define KMSG_COMPONENT "extmem"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "extmem: " fmt
#include <linux/kernel.h>
#include <linux/string.h>
@@ -598,14 +597,16 @@ segment_save(char *name)
goto out;
}
- sprintf(cmd1, "DEFSEG %s", name);
+ snprintf(cmd1, sizeof(cmd1), "DEFSEG %s", name);
for (i=0; i<seg->segcnt; i++) {
- sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
- seg->range[i].start >> PAGE_SHIFT,
- seg->range[i].end >> PAGE_SHIFT,
- segtype_string[seg->range[i].start & 0xff]);
+ size_t len = strlen(cmd1);
+
+ snprintf(cmd1 + len, sizeof(cmd1) - len, " %lX-%lX %s",
+ seg->range[i].start >> PAGE_SHIFT,
+ seg->range[i].end >> PAGE_SHIFT,
+ segtype_string[seg->range[i].start & 0xff]);
}
- sprintf(cmd2, "SAVESEG %s", name);
+ snprintf(cmd2, sizeof(cmd2), "SAVESEG %s", name);
response = 0;
cpcmd(cmd1, NULL, 0, &response);
if (response) {
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index e1ad05bfd28a..e2e13778c36a 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -23,7 +23,6 @@
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
-#include <linux/compat.h>
#include <linux/smp.h>
#include <linux/kdebug.h>
#include <linux/init.h>
@@ -133,8 +132,17 @@ static void dump_fault_info(struct pt_regs *regs)
union teid teid = { .val = regs->int_parm_long };
unsigned long asce;
- pr_alert("Failing address: %016lx TEID: %016lx\n",
+ pr_alert("Failing address: %016lx TEID: %016lx",
get_fault_address(regs), teid.val);
+ if (test_facility(131))
+ pr_cont(" ESOP-2");
+ else if (machine_has_esop())
+ pr_cont(" ESOP-1");
+ else
+ pr_cont(" SOP");
+ if (test_facility(75))
+ pr_cont(" FSI");
+ pr_cont("\n");
pr_alert("Fault in ");
switch (teid.as) {
case PSW_BITS_AS_HOME:
@@ -365,23 +373,20 @@ void do_protection_exception(struct pt_regs *regs)
* The exception to this rule are aborted transactions, for these
* the PSW already points to the correct location.
*/
- if (!(regs->int_code & 0x200))
+ if (!(regs->int_code & 0x200)) {
regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16);
+ set_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED);
+ }
/*
- * Check for low-address protection. This needs to be treated
- * as a special case because the translation exception code
- * field is not guaranteed to contain valid data in this case.
+ * If bit 61 if the TEID is not set, the remainder of the
+ * TEID is unpredictable. Special handling is required.
*/
if (unlikely(!teid.b61)) {
if (user_mode(regs)) {
- /* Low-address protection in user mode: cannot happen */
dump_fault_info(regs);
- die(regs, "Low-address protection");
+ die(regs, "Unexpected TEID");
}
- /*
- * Low-address protection in kernel mode means
- * NULL pointer write access in kernel mode.
- */
+ /* Assume low-address protection in kernel mode. */
return handle_fault_error_nolock(regs, 0);
}
if (unlikely(cpu_has_nx() && teid.b56)) {
diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c
index 8ff6bba107e8..603d9e5febb5 100644
--- a/arch/s390/mm/gmap.c
+++ b/arch/s390/mm/gmap.c
@@ -138,10 +138,7 @@ EXPORT_SYMBOL_GPL(gmap_create);
static void gmap_flush_tlb(struct gmap *gmap)
{
- if (cpu_has_idte())
- __tlb_flush_idte(gmap->asce);
- else
- __tlb_flush_global();
+ __tlb_flush_idte(gmap->asce);
}
static void gmap_radix_tree_free(struct radix_tree_root *root)
@@ -1988,10 +1985,8 @@ static void gmap_pmdp_xchg(struct gmap *gmap, pmd_t *pmdp, pmd_t new,
if (machine_has_tlb_guest())
__pmdp_idte(gaddr, (pmd_t *)pmdp, IDTE_GUEST_ASCE, gmap->asce,
IDTE_GLOBAL);
- else if (cpu_has_idte())
- __pmdp_idte(gaddr, (pmd_t *)pmdp, 0, 0, IDTE_GLOBAL);
else
- __pmdp_csp(pmdp);
+ __pmdp_idte(gaddr, (pmd_t *)pmdp, 0, 0, IDTE_GLOBAL);
set_pmd(pmdp, new);
}
@@ -2012,7 +2007,7 @@ static void gmap_pmdp_clear(struct mm_struct *mm, unsigned long vmaddr,
_SEGMENT_ENTRY_GMAP_UC |
_SEGMENT_ENTRY));
if (purge)
- __pmdp_csp(pmdp);
+ __pmdp_cspg(pmdp);
set_pmd(pmdp, __pmd(_SEGMENT_ENTRY_EMPTY));
}
spin_unlock(&gmap->guest_table_lock);
@@ -2033,17 +2028,6 @@ void gmap_pmdp_invalidate(struct mm_struct *mm, unsigned long vmaddr)
EXPORT_SYMBOL_GPL(gmap_pmdp_invalidate);
/**
- * gmap_pmdp_csp - csp all affected guest pmd entries
- * @mm: pointer to the process mm_struct
- * @vmaddr: virtual address in the process address space
- */
-void gmap_pmdp_csp(struct mm_struct *mm, unsigned long vmaddr)
-{
- gmap_pmdp_clear(mm, vmaddr, 1);
-}
-EXPORT_SYMBOL_GPL(gmap_pmdp_csp);
-
-/**
* gmap_pmdp_idte_local - invalidate and clear a guest pmd entry
* @mm: pointer to the process mm_struct
* @vmaddr: virtual address in the process address space
@@ -2066,7 +2050,7 @@ void gmap_pmdp_idte_local(struct mm_struct *mm, unsigned long vmaddr)
if (machine_has_tlb_guest())
__pmdp_idte(gaddr, pmdp, IDTE_GUEST_ASCE,
gmap->asce, IDTE_LOCAL);
- else if (cpu_has_idte())
+ else
__pmdp_idte(gaddr, pmdp, 0, 0, IDTE_LOCAL);
*pmdp = __pmd(_SEGMENT_ENTRY_EMPTY);
}
@@ -2099,10 +2083,8 @@ void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr)
if (machine_has_tlb_guest())
__pmdp_idte(gaddr, pmdp, IDTE_GUEST_ASCE,
gmap->asce, IDTE_GLOBAL);
- else if (cpu_has_idte())
- __pmdp_idte(gaddr, pmdp, 0, 0, IDTE_GLOBAL);
else
- __pmdp_csp(pmdp);
+ __pmdp_idte(gaddr, pmdp, 0, 0, IDTE_GLOBAL);
*pmdp = __pmd(_SEGMENT_ENTRY_EMPTY);
}
spin_unlock(&gmap->guest_table_lock);
diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c
index 72e8fa136af5..d42e61c7594e 100644
--- a/arch/s390/mm/hugetlbpage.c
+++ b/arch/s390/mm/hugetlbpage.c
@@ -6,8 +6,7 @@
* Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com>
*/
-#define KMSG_COMPONENT "hugetlb"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "hugetlb: " fmt
#include <linux/cpufeature.h>
#include <linux/mm.h>
diff --git a/arch/s390/mm/mmap.c b/arch/s390/mm/mmap.c
index 197c1d9497a7..2a222a7e14f4 100644
--- a/arch/s390/mm/mmap.c
+++ b/arch/s390/mm/mmap.c
@@ -15,7 +15,6 @@
#include <linux/sched/signal.h>
#include <linux/sched/mm.h>
#include <linux/random.h>
-#include <linux/compat.h>
#include <linux/security.h>
#include <linux/hugetlb.h>
#include <asm/elf.h>
diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c
index 348e759840e7..3042647c9dbf 100644
--- a/arch/s390/mm/pageattr.c
+++ b/arch/s390/mm/pageattr.c
@@ -78,10 +78,8 @@ static void pgt_set(unsigned long *old, unsigned long new, unsigned long addr,
}
table = (unsigned long *)((unsigned long)old & mask);
crdte(*old, new, table, dtt, addr, get_lowcore()->kernel_asce.val);
- } else if (cpu_has_idte()) {
- cspg(old, *old, new);
} else {
- csp((unsigned int *)old + 1, *old, new);
+ cspg(old, *old, new);
}
}
diff --git a/arch/s390/mm/pfault.c b/arch/s390/mm/pfault.c
index e6175d75e4b0..2f829448c719 100644
--- a/arch/s390/mm/pfault.c
+++ b/arch/s390/mm/pfault.c
@@ -199,8 +199,7 @@ block:
* return to userspace schedule() to block.
*/
__set_current_state(TASK_UNINTERRUPTIBLE);
- set_tsk_need_resched(tsk);
- set_preempt_need_resched();
+ set_need_resched_current();
}
}
out:
diff --git a/arch/s390/mm/pgalloc.c b/arch/s390/mm/pgalloc.c
index 626fca116cd7..7df23528c01b 100644
--- a/arch/s390/mm/pgalloc.c
+++ b/arch/s390/mm/pgalloc.c
@@ -164,6 +164,8 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
{
struct ptdesc *ptdesc = virt_to_ptdesc(table);
+ if (pagetable_is_reserved(ptdesc))
+ return free_reserved_ptdesc(ptdesc);
pagetable_dtor_free(ptdesc);
}
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 0fde20bbc50b..7ae77df276b5 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -274,9 +274,9 @@ void ptep_reset_dat_prot(struct mm_struct *mm, unsigned long addr, pte_t *ptep,
preempt_disable();
atomic_inc(&mm->context.flush_count);
if (cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id())))
- __ptep_rdp(addr, ptep, 0, 0, 1);
+ __ptep_rdp(addr, ptep, 1);
else
- __ptep_rdp(addr, ptep, 0, 0, 0);
+ __ptep_rdp(addr, ptep, 0);
/*
* PTE is not invalidated by RDP, only _PAGE_PROTECT is cleared. That
* means it is still valid and active, and must not be changed according
@@ -360,14 +360,10 @@ static inline void pmdp_idte_global(struct mm_struct *mm,
mm->context.asce, IDTE_GLOBAL);
if (mm_has_pgste(mm) && mm->context.allow_gmap_hpage_1m)
gmap_pmdp_idte_global(mm, addr);
- } else if (cpu_has_idte()) {
+ } else {
__pmdp_idte(addr, pmdp, 0, 0, IDTE_GLOBAL);
if (mm_has_pgste(mm) && mm->context.allow_gmap_hpage_1m)
gmap_pmdp_idte_global(mm, addr);
- } else {
- __pmdp_csp(pmdp);
- if (mm_has_pgste(mm) && mm->context.allow_gmap_hpage_1m)
- gmap_pmdp_csp(mm, addr);
}
}
@@ -487,14 +483,8 @@ static inline void pudp_idte_global(struct mm_struct *mm,
if (machine_has_tlb_guest())
__pudp_idte(addr, pudp, IDTE_NODAT | IDTE_GUEST_ASCE,
mm->context.asce, IDTE_GLOBAL);
- else if (cpu_has_idte())
- __pudp_idte(addr, pudp, 0, 0, IDTE_GLOBAL);
else
- /*
- * Invalid bit position is the same for pmd and pud, so we can
- * reuse _pmd_csp() here
- */
- __pmdp_csp((pmd_t *) pudp);
+ __pudp_idte(addr, pudp, 0, 0, IDTE_GLOBAL);
}
static inline pud_t pudp_flush_direct(struct mm_struct *mm,
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index f48ef361bc83..d96587b84e81 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -4,6 +4,7 @@
*/
#include <linux/memory_hotplug.h>
+#include <linux/bootmem_info.h>
#include <linux/cpufeature.h>
#include <linux/memblock.h>
#include <linux/pfn.h>
@@ -39,15 +40,21 @@ static void __ref *vmem_alloc_pages(unsigned int order)
static void vmem_free_pages(unsigned long addr, int order, struct vmem_altmap *altmap)
{
+ unsigned int nr_pages = 1 << order;
+ struct page *page;
+
if (altmap) {
vmem_altmap_free(altmap, 1 << order);
return;
}
- /* We don't expect boot memory to be removed ever. */
- if (!slab_is_available() ||
- WARN_ON_ONCE(PageReserved(virt_to_page((void *)addr))))
- return;
- free_pages(addr, order);
+ page = virt_to_page((void *)addr);
+ if (PageReserved(page)) {
+ /* allocated from memblock */
+ while (nr_pages--)
+ free_bootmem_page(page++);
+ } else {
+ free_pages(addr, order);
+ }
}
void *vmem_crst_alloc(unsigned long val)
@@ -79,10 +86,6 @@ pte_t __ref *vmem_pte_alloc(void)
static void vmem_pte_free(unsigned long *table)
{
- /* We don't expect boot memory to be removed ever. */
- if (!slab_is_available() ||
- WARN_ON_ONCE(PageReserved(virt_to_page(table))))
- return;
page_table_free(&init_mm, table);
}
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index cf461d76e9da..579461d471bb 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -15,8 +15,7 @@
* Michael Holzheu <holzheu@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "bpf_jit"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "bpf_jit: " fmt
#include <linux/netdevice.h>
#include <linux/filter.h>
@@ -2413,8 +2412,9 @@ bool bpf_jit_supports_far_kfunc_call(void)
return true;
}
-int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
- void *old_addr, void *new_addr)
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
+ enum bpf_text_poke_type new_t, void *old_addr,
+ void *new_addr)
{
struct bpf_plt expected_plt, current_plt, new_plt, *plt;
struct {
@@ -2431,7 +2431,7 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
if (insn.opc != (0xc004 | (old_addr ? 0xf0 : 0)))
return -EINVAL;
- if (t == BPF_MOD_JUMP &&
+ if ((new_t == BPF_MOD_JUMP || old_t == BPF_MOD_JUMP) &&
insn.disp == ((char *)new_addr - (char *)ip) >> 1) {
/*
* The branch already points to the destination,
diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c
index c82c577db2bc..93d2c9c780fc 100644
--- a/arch/s390/pci/pci.c
+++ b/arch/s390/pci/pci.c
@@ -16,8 +16,7 @@
* Thomas Klein
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
diff --git a/arch/s390/pci/pci_bus.c b/arch/s390/pci/pci_bus.c
index 45a1c36c5a54..72adc8f6e94f 100644
--- a/arch/s390/pci/pci_bus.c
+++ b/arch/s390/pci/pci_bus.c
@@ -7,8 +7,7 @@
*
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -45,8 +44,10 @@ static int zpci_bus_prepare_device(struct zpci_dev *zdev)
if (!zdev_enabled(zdev)) {
rc = zpci_enable_device(zdev);
- if (rc)
+ if (rc) {
+ pr_err("Enabling PCI function %08x failed\n", zdev->fid);
return rc;
+ }
}
if (!zdev->has_resources) {
diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c
index 241f7251c873..177aa0214547 100644
--- a/arch/s390/pci/pci_clp.c
+++ b/arch/s390/pci/pci_clp.c
@@ -6,10 +6,8 @@
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
-#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
@@ -651,7 +649,7 @@ static long clp_misc_ioctl(struct file *filp, unsigned int cmd,
if (cmd != CLP_SYNC)
return -EINVAL;
- argp = is_compat_task() ? compat_ptr(arg) : (void __user *) arg;
+ argp = (void __user *)arg;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
if (req.r != 0)
@@ -669,7 +667,6 @@ static const struct file_operations clp_misc_fops = {
.open = nonseekable_open,
.release = clp_misc_release,
.unlocked_ioctl = clp_misc_ioctl,
- .compat_ioctl = clp_misc_ioctl,
};
static struct miscdevice clp_misc_device = {
diff --git a/arch/s390/pci/pci_debug.c b/arch/s390/pci/pci_debug.c
index 38014206c16b..c7ed7bf254b5 100644
--- a/arch/s390/pci/pci_debug.c
+++ b/arch/s390/pci/pci_debug.c
@@ -6,8 +6,7 @@
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/seq_file.h>
diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c
index b95376041501..839bd91c056e 100644
--- a/arch/s390/pci/pci_event.c
+++ b/arch/s390/pci/pci_event.c
@@ -6,8 +6,7 @@
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/pci.h>
@@ -188,7 +187,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev)
* is unbound or probed and that userspace can't access its
* configuration space while we perform recovery.
*/
- pci_dev_lock(pdev);
+ device_lock(&pdev->dev);
if (pdev->error_state == pci_channel_io_perm_failure) {
ers_res = PCI_ERS_RESULT_DISCONNECT;
goto out_unlock;
@@ -257,7 +256,7 @@ static pci_ers_result_t zpci_event_attempt_error_recovery(struct pci_dev *pdev)
driver->err_handler->resume(pdev);
pci_uevent_ers(pdev, PCI_ERS_RESULT_RECOVERED);
out_unlock:
- pci_dev_unlock(pdev);
+ device_unlock(&pdev->dev);
zpci_report_status(zdev, "recovery", status_str);
return ers_res;
diff --git a/arch/s390/pci/pci_iov.c b/arch/s390/pci/pci_iov.c
index 191e56a623f6..13050ce5c3e9 100644
--- a/arch/s390/pci/pci_iov.c
+++ b/arch/s390/pci/pci_iov.c
@@ -7,8 +7,7 @@
*
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/pci.h>
diff --git a/arch/s390/pci/pci_irq.c b/arch/s390/pci/pci_irq.c
index 84482a921332..2a06df8c2498 100644
--- a/arch/s390/pci/pci_irq.c
+++ b/arch/s390/pci/pci_irq.c
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/irq.h>
@@ -107,9 +106,6 @@ static int zpci_set_irq(struct zpci_dev *zdev)
else
rc = zpci_set_airq(zdev);
- if (!rc)
- zdev->irqs_registered = 1;
-
return rc;
}
@@ -123,9 +119,6 @@ static int zpci_clear_irq(struct zpci_dev *zdev)
else
rc = zpci_clear_airq(zdev);
- if (!rc)
- zdev->irqs_registered = 0;
-
return rc;
}
@@ -427,8 +420,7 @@ bool arch_restore_msi_irqs(struct pci_dev *pdev)
{
struct zpci_dev *zdev = to_zpci(pdev);
- if (!zdev->irqs_registered)
- zpci_set_irq(zdev);
+ zpci_set_irq(zdev);
return true;
}
diff --git a/arch/s390/pci/pci_report.c b/arch/s390/pci/pci_report.c
index 1b494e5ecc4d..7030f7052926 100644
--- a/arch/s390/pci/pci_report.c
+++ b/arch/s390/pci/pci_report.c
@@ -7,8 +7,7 @@
*
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/sprintf.h>
diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c
index 12060870e2aa..c2444a23e26c 100644
--- a/arch/s390/pci/pci_sysfs.c
+++ b/arch/s390/pci/pci_sysfs.c
@@ -6,8 +6,7 @@
* Jan Glauber <jang@linux.vnet.ibm.com>
*/
-#define KMSG_COMPONENT "zpci"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "zpci: " fmt
#include <linux/kernel.h>
#include <linux/stat.h>
diff --git a/arch/s390/purgatory/Makefile b/arch/s390/purgatory/Makefile
index bd39b36e7bd6..0c196a5b194a 100644
--- a/arch/s390/purgatory/Makefile
+++ b/arch/s390/purgatory/Makefile
@@ -13,7 +13,7 @@ CFLAGS_sha256.o := -D__NO_FORTIFY
$(obj)/mem.o: $(srctree)/arch/s390/lib/mem.S FORCE
$(call if_changed_rule,as_o_S)
-KBUILD_CFLAGS := -std=gnu11 -fno-strict-aliasing -Wall -Wstrict-prototypes
+KBUILD_CFLAGS := -std=gnu11 -fms-extensions -fno-strict-aliasing -Wall -Wstrict-prototypes
KBUILD_CFLAGS += -Wno-pointer-sign -Wno-sign-compare
KBUILD_CFLAGS += -fno-zero-initialized-in-bss -fno-builtin -ffreestanding
KBUILD_CFLAGS += -Os -m64 -msoft-float -fno-common
@@ -21,6 +21,7 @@ KBUILD_CFLAGS += -fno-stack-protector
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
KBUILD_CFLAGS += -D__DISABLE_EXPORTS
KBUILD_CFLAGS += $(CLANG_FLAGS)
+KBUILD_CFLAGS += $(if $(CONFIG_CC_IS_CLANG),-Wno-microsoft-anon-tag)
KBUILD_CFLAGS += $(call cc-option,-fno-PIE)
KBUILD_AFLAGS := $(filter-out -DCC_USING_EXPOLINE,$(KBUILD_AFLAGS))
KBUILD_AFLAGS += -D__DISABLE_EXPORTS
diff --git a/arch/s390/tools/gen_facilities.c b/arch/s390/tools/gen_facilities.c
index d5c68ade71ab..2d28a569f793 100644
--- a/arch/s390/tools/gen_facilities.c
+++ b/arch/s390/tools/gen_facilities.c
@@ -29,6 +29,7 @@ static struct facility_def facility_defs[] = {
.bits = (int[]){
0, /* N3 instructions */
1, /* z/Arch mode installed */
+ 3, /* dat-enhancement 1 */
18, /* long displacement facility */
21, /* extended-immediate facility */
25, /* store clock fast */
diff --git a/arch/sh/configs/ap325rxa_defconfig b/arch/sh/configs/ap325rxa_defconfig
index 48b2e97114f9..b62be0d067f0 100644
--- a/arch/sh/configs/ap325rxa_defconfig
+++ b/arch/sh/configs/ap325rxa_defconfig
@@ -97,4 +97,3 @@ CONFIG_NLS_ISO8859_1=y
# CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_CRYPTO=y
CONFIG_CRYPTO_CBC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/apsh4a3a_defconfig b/arch/sh/configs/apsh4a3a_defconfig
index 85db9ce42d1a..4ecd1ab398fe 100644
--- a/arch/sh/configs/apsh4a3a_defconfig
+++ b/arch/sh/configs/apsh4a3a_defconfig
@@ -86,5 +86,4 @@ CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_BUGVERBOSE is not set
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
# CONFIG_FTRACE is not set
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/configs/apsh4ad0a_defconfig b/arch/sh/configs/apsh4ad0a_defconfig
index e8b3b720578b..8c6de64f65c8 100644
--- a/arch/sh/configs/apsh4ad0a_defconfig
+++ b/arch/sh/configs/apsh4ad0a_defconfig
@@ -116,4 +116,3 @@ CONFIG_DETECT_HUNG_TASK=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_DEBUG_VM=y
CONFIG_DWARF_UNWINDER=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/dreamcast_defconfig b/arch/sh/configs/dreamcast_defconfig
index 0c9f2030bb7c..4573d5d64989 100644
--- a/arch/sh/configs/dreamcast_defconfig
+++ b/arch/sh/configs/dreamcast_defconfig
@@ -66,6 +66,5 @@ CONFIG_LOGO=y
CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
CONFIG_HUGETLBFS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_GENERIC=y
diff --git a/arch/sh/configs/ecovec24_defconfig b/arch/sh/configs/ecovec24_defconfig
index fcca7cc5a75a..458115d83184 100644
--- a/arch/sh/configs/ecovec24_defconfig
+++ b/arch/sh/configs/ecovec24_defconfig
@@ -126,4 +126,3 @@ CONFIG_NLS_ISO8859_1=y
CONFIG_DEBUG_FS=y
CONFIG_CRYPTO=y
CONFIG_CRYPTO_CBC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/edosk7760_defconfig b/arch/sh/configs/edosk7760_defconfig
index 98f4611ba553..abeae220606a 100644
--- a/arch/sh/configs/edosk7760_defconfig
+++ b/arch/sh/configs/edosk7760_defconfig
@@ -110,4 +110,3 @@ CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_CRYPTO=y
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_DES=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/espt_defconfig b/arch/sh/configs/espt_defconfig
index e5d102cbff89..3968fe75f693 100644
--- a/arch/sh/configs/espt_defconfig
+++ b/arch/sh/configs/espt_defconfig
@@ -108,4 +108,3 @@ CONFIG_NLS_KOI8_U=y
CONFIG_NLS_UTF8=y
# CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_DEBUG_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/hp6xx_defconfig b/arch/sh/configs/hp6xx_defconfig
index 3582af15ad86..04a9fcb4342a 100644
--- a/arch/sh/configs/hp6xx_defconfig
+++ b/arch/sh/configs/hp6xx_defconfig
@@ -54,5 +54,4 @@ CONFIG_CRYPTO_CBC=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_PCBC=y
CONFIG_CRYPTO_MD5=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/configs/landisk_defconfig b/arch/sh/configs/landisk_defconfig
index 22177aa8f961..4c39a23e6e0d 100644
--- a/arch/sh/configs/landisk_defconfig
+++ b/arch/sh/configs/landisk_defconfig
@@ -109,4 +109,3 @@ CONFIG_SMB_FS=m
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_932=y
CONFIG_SH_STANDARD_BIOS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/lboxre2_defconfig b/arch/sh/configs/lboxre2_defconfig
index ff992301622b..9f53b514a8e7 100644
--- a/arch/sh/configs/lboxre2_defconfig
+++ b/arch/sh/configs/lboxre2_defconfig
@@ -56,4 +56,3 @@ CONFIG_TMPFS=y
CONFIG_ROMFS_FS=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_SH_STANDARD_BIOS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/migor_defconfig b/arch/sh/configs/migor_defconfig
index 31dbd8888aaa..7cdaa909ffd6 100644
--- a/arch/sh/configs/migor_defconfig
+++ b/arch/sh/configs/migor_defconfig
@@ -87,5 +87,4 @@ CONFIG_TMPFS=y
CONFIG_NFS_FS=y
CONFIG_ROOT_NFS=y
CONFIG_DEBUG_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/configs/r7780mp_defconfig b/arch/sh/configs/r7780mp_defconfig
index 58b792dacfec..af954f75444b 100644
--- a/arch/sh/configs/r7780mp_defconfig
+++ b/arch/sh/configs/r7780mp_defconfig
@@ -103,4 +103,3 @@ CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_CRYPTO_ECB=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_HMAC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/r7785rp_defconfig b/arch/sh/configs/r7785rp_defconfig
index 7edf18451158..a66dd6d74cf1 100644
--- a/arch/sh/configs/r7785rp_defconfig
+++ b/arch/sh/configs/r7785rp_defconfig
@@ -101,4 +101,3 @@ CONFIG_4KSTACKS=y
CONFIG_CRYPTO_ECB=m
CONFIG_CRYPTO_PCBC=m
CONFIG_CRYPTO_HMAC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/rts7751r2d1_defconfig b/arch/sh/configs/rts7751r2d1_defconfig
index 69568cc40396..0c54ab2b06e6 100644
--- a/arch/sh/configs/rts7751r2d1_defconfig
+++ b/arch/sh/configs/rts7751r2d1_defconfig
@@ -86,4 +86,3 @@ CONFIG_TMPFS=y
CONFIG_MINIX_FS=y
CONFIG_NLS_CODEPAGE_932=y
CONFIG_DEBUG_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/rts7751r2dplus_defconfig b/arch/sh/configs/rts7751r2dplus_defconfig
index ecb4bdb5bb58..3173b616b2cb 100644
--- a/arch/sh/configs/rts7751r2dplus_defconfig
+++ b/arch/sh/configs/rts7751r2dplus_defconfig
@@ -91,4 +91,3 @@ CONFIG_TMPFS=y
CONFIG_MINIX_FS=y
CONFIG_NLS_CODEPAGE_932=y
CONFIG_DEBUG_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/sdk7780_defconfig b/arch/sh/configs/sdk7780_defconfig
index 311817161afb..57ac7af157b8 100644
--- a/arch/sh/configs/sdk7780_defconfig
+++ b/arch/sh/configs/sdk7780_defconfig
@@ -134,4 +134,3 @@ CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_SH_STANDARD_BIOS=y
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_DES=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/sdk7786_defconfig b/arch/sh/configs/sdk7786_defconfig
index 2433aa5f44a8..d6fa3a422e24 100644
--- a/arch/sh/configs/sdk7786_defconfig
+++ b/arch/sh/configs/sdk7786_defconfig
@@ -211,4 +211,3 @@ CONFIG_FUNCTION_TRACER=y
CONFIG_DMA_API_DEBUG=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DWARF_UNWINDER=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7206_defconfig b/arch/sh/configs/se7206_defconfig
index 64f9308ee586..d67158f69c52 100644
--- a/arch/sh/configs/se7206_defconfig
+++ b/arch/sh/configs/se7206_defconfig
@@ -99,5 +99,4 @@ CONFIG_FRAME_POINTER=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/configs/se7343_defconfig b/arch/sh/configs/se7343_defconfig
index b0baa5771c26..2d4d1f974f14 100644
--- a/arch/sh/configs/se7343_defconfig
+++ b/arch/sh/configs/se7343_defconfig
@@ -91,4 +91,3 @@ CONFIG_CRAMFS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_NFSD=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7705_defconfig b/arch/sh/configs/se7705_defconfig
index 1752ddc2694a..ad55a4d9d57d 100644
--- a/arch/sh/configs/se7705_defconfig
+++ b/arch/sh/configs/se7705_defconfig
@@ -51,4 +51,3 @@ CONFIG_PROC_KCORE=y
CONFIG_JFFS2_FS=y
CONFIG_NFS_FS=y
CONFIG_ROOT_NFS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7712_defconfig b/arch/sh/configs/se7712_defconfig
index 1078c286a610..dee1d88f6a7d 100644
--- a/arch/sh/configs/se7712_defconfig
+++ b/arch/sh/configs/se7712_defconfig
@@ -94,4 +94,3 @@ CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_FRAME_POINTER=y
CONFIG_CRYPTO_ECB=m
CONFIG_CRYPTO_PCBC=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7721_defconfig b/arch/sh/configs/se7721_defconfig
index edb9e0d2dce5..a4551cadaf0b 100644
--- a/arch/sh/configs/se7721_defconfig
+++ b/arch/sh/configs/se7721_defconfig
@@ -120,4 +120,3 @@ CONFIG_NLS_ISO8859_1=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_FRAME_POINTER=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7722_defconfig b/arch/sh/configs/se7722_defconfig
index 33daa0a17a32..85b8eb013b79 100644
--- a/arch/sh/configs/se7722_defconfig
+++ b/arch/sh/configs/se7722_defconfig
@@ -53,4 +53,3 @@ CONFIG_PRINTK_TIME=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_FS=y
CONFIG_SH_STANDARD_BIOS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7724_defconfig b/arch/sh/configs/se7724_defconfig
index d572655f842d..9e3a54936f76 100644
--- a/arch/sh/configs/se7724_defconfig
+++ b/arch/sh/configs/se7724_defconfig
@@ -126,4 +126,3 @@ CONFIG_NLS_ISO8859_1=y
# CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_CRYPTO=y
CONFIG_CRYPTO_CBC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7750_defconfig b/arch/sh/configs/se7750_defconfig
index a1e25d7de8a6..83ae513e5462 100644
--- a/arch/sh/configs/se7750_defconfig
+++ b/arch/sh/configs/se7750_defconfig
@@ -52,4 +52,3 @@ CONFIG_ROOT_NFS=y
CONFIG_PARTITION_ADVANCED=y
# CONFIG_MSDOS_PARTITION is not set
# CONFIG_ENABLE_MUST_CHECK is not set
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7751_defconfig b/arch/sh/configs/se7751_defconfig
index 8b5fe4ec16bc..8f5ddab3c106 100644
--- a/arch/sh/configs/se7751_defconfig
+++ b/arch/sh/configs/se7751_defconfig
@@ -42,4 +42,3 @@ CONFIG_EXT2_FS=y
CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
CONFIG_JFFS2_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/se7780_defconfig b/arch/sh/configs/se7780_defconfig
index 21303304eda2..12463b766120 100644
--- a/arch/sh/configs/se7780_defconfig
+++ b/arch/sh/configs/se7780_defconfig
@@ -102,4 +102,3 @@ CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_ROOT_NFS=y
CONFIG_DEBUG_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/sh03_defconfig b/arch/sh/configs/sh03_defconfig
index 3d194d81c92b..9036431646f8 100644
--- a/arch/sh/configs/sh03_defconfig
+++ b/arch/sh/configs/sh03_defconfig
@@ -118,6 +118,5 @@ CONFIG_CRYPTO_ECB=m
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_SHA1=y
CONFIG_CRYPTO_DEFLATE=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_GENERIC=y
diff --git a/arch/sh/configs/sh2007_defconfig b/arch/sh/configs/sh2007_defconfig
index 889daa5d2faa..e32d2ce72699 100644
--- a/arch/sh/configs/sh2007_defconfig
+++ b/arch/sh/configs/sh2007_defconfig
@@ -191,5 +191,4 @@ CONFIG_CRYPTO_TEA=y
CONFIG_CRYPTO_TWOFISH=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/configs/sh7710voipgw_defconfig b/arch/sh/configs/sh7710voipgw_defconfig
index 5b151bb2bc43..c8625ba5be79 100644
--- a/arch/sh/configs/sh7710voipgw_defconfig
+++ b/arch/sh/configs/sh7710voipgw_defconfig
@@ -51,4 +51,3 @@ CONFIG_THERMAL=y
# CONFIG_DNOTIFY is not set
CONFIG_JFFS2_FS=y
CONFIG_DEBUG_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/sh7757lcr_defconfig b/arch/sh/configs/sh7757lcr_defconfig
index 25e9d22779b3..93066ae429ec 100644
--- a/arch/sh/configs/sh7757lcr_defconfig
+++ b/arch/sh/configs/sh7757lcr_defconfig
@@ -81,4 +81,3 @@ CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_BUGVERBOSE is not set
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
# CONFIG_FTRACE is not set
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/sh7763rdp_defconfig b/arch/sh/configs/sh7763rdp_defconfig
index e7b72ff377a8..5123eed9bbe0 100644
--- a/arch/sh/configs/sh7763rdp_defconfig
+++ b/arch/sh/configs/sh7763rdp_defconfig
@@ -110,4 +110,3 @@ CONFIG_NLS_KOI8_U=y
CONFIG_NLS_UTF8=y
# CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_DEBUG_FS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/sh7785lcr_32bit_defconfig b/arch/sh/configs/sh7785lcr_32bit_defconfig
index 17d2471d8e51..eb63aa61b046 100644
--- a/arch/sh/configs/sh7785lcr_32bit_defconfig
+++ b/arch/sh/configs/sh7785lcr_32bit_defconfig
@@ -144,5 +144,4 @@ CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_LATENCYTOP=y
# CONFIG_FTRACE is not set
CONFIG_CRYPTO_HMAC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/configs/sh7785lcr_defconfig b/arch/sh/configs/sh7785lcr_defconfig
index 34c8fe755add..2fcf50d8c820 100644
--- a/arch/sh/configs/sh7785lcr_defconfig
+++ b/arch/sh/configs/sh7785lcr_defconfig
@@ -112,5 +112,4 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_DETECT_HUNG_TASK=y
# CONFIG_DEBUG_BUGVERBOSE is not set
CONFIG_CRYPTO_HMAC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/configs/shmin_defconfig b/arch/sh/configs/shmin_defconfig
index bfeb004f130e..11ac2e66ec7e 100644
--- a/arch/sh/configs/shmin_defconfig
+++ b/arch/sh/configs/shmin_defconfig
@@ -49,4 +49,3 @@ CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_ROOT_NFS=y
CONFIG_SH_STANDARD_BIOS=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/shx3_defconfig b/arch/sh/configs/shx3_defconfig
index 52e7a42d66c7..0cb69a0b92cb 100644
--- a/arch/sh/configs/shx3_defconfig
+++ b/arch/sh/configs/shx3_defconfig
@@ -97,4 +97,3 @@ CONFIG_DEBUG_VM=y
CONFIG_FRAME_POINTER=y
CONFIG_SH_STANDARD_BIOS=y
CONFIG_DEBUG_STACK_USAGE=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/titan_defconfig b/arch/sh/configs/titan_defconfig
index 2c474645ec36..896e980d04e1 100644
--- a/arch/sh/configs/titan_defconfig
+++ b/arch/sh/configs/titan_defconfig
@@ -261,4 +261,3 @@ CONFIG_CRYPTO_KHAZAD=m
CONFIG_CRYPTO_SERPENT=m
CONFIG_CRYPTO_TEA=m
CONFIG_CRYPTO_TWOFISH=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/ul2_defconfig b/arch/sh/configs/ul2_defconfig
index b0c2ba478353..0d1c858754db 100644
--- a/arch/sh/configs/ul2_defconfig
+++ b/arch/sh/configs/ul2_defconfig
@@ -80,4 +80,3 @@ CONFIG_NLS_CODEPAGE_932=y
CONFIG_NLS_ISO8859_1=y
# CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_CRYPTO_MICHAEL_MIC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
diff --git a/arch/sh/configs/urquell_defconfig b/arch/sh/configs/urquell_defconfig
index e6d807f52253..f51ff6b1ec38 100644
--- a/arch/sh/configs/urquell_defconfig
+++ b/arch/sh/configs/urquell_defconfig
@@ -142,5 +142,4 @@ CONFIG_FRAME_POINTER=y
# CONFIG_FTRACE is not set
# CONFIG_DUMP_CODE is not set
CONFIG_CRYPTO_HMAC=y
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sh/include/asm/bug.h b/arch/sh/include/asm/bug.h
index 05a485c4fabc..891276687355 100644
--- a/arch/sh/include/asm/bug.h
+++ b/arch/sh/include/asm/bug.h
@@ -52,14 +52,14 @@ do { \
unreachable(); \
} while (0)
-#define __WARN_FLAGS(flags) \
+#define __WARN_FLAGS(cond_str, flags) \
do { \
__asm__ __volatile__ ( \
"1:\t.short %O0\n" \
_EMIT_BUG_ENTRY \
: \
: "n" (TRAPA_BUG_OPCODE), \
- "i" (__FILE__), \
+ "i" (WARN_CONDITION_STR(cond_str) __FILE__), \
"i" (__LINE__), \
"i" (BUGFLAG_WARNING|(flags)), \
"i" (sizeof(struct bug_entry))); \
diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl
index 5e9c9eff5539..969c11325ade 100644
--- a/arch/sh/kernel/syscalls/syscall.tbl
+++ b/arch/sh/kernel/syscalls/syscall.tbl
@@ -473,3 +473,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/sparc/configs/sparc32_defconfig b/arch/sparc/configs/sparc32_defconfig
index f6341b063b01..e021ecfb5a77 100644
--- a/arch/sparc/configs/sparc32_defconfig
+++ b/arch/sparc/configs/sparc32_defconfig
@@ -92,5 +92,4 @@ CONFIG_CRYPTO_CAST5=m
CONFIG_CRYPTO_CAST6=m
CONFIG_CRYPTO_SERPENT=m
CONFIG_CRYPTO_TWOFISH=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
diff --git a/arch/sparc/configs/sparc64_defconfig b/arch/sparc/configs/sparc64_defconfig
index 127940aafc39..9f3f41246ae6 100644
--- a/arch/sparc/configs/sparc64_defconfig
+++ b/arch/sparc/configs/sparc64_defconfig
@@ -227,7 +227,6 @@ CONFIG_CRYPTO_SEED=m
CONFIG_CRYPTO_SERPENT=m
CONFIG_CRYPTO_TEA=m
CONFIG_CRYPTO_TWOFISH=m
-# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_VCC=m
CONFIG_PATA_CMD64X=y
CONFIG_IP_PNP=y
diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl
index ebb7d06d1044..39aa26b6a50b 100644
--- a/arch/sparc/kernel/syscalls/syscall.tbl
+++ b/arch/sparc/kernel/syscalls/syscall.tbl
@@ -515,3 +515,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/arch/um/kernel/kmsg_dump.c b/arch/um/kernel/kmsg_dump.c
index 419021175272..fc0f543d1d8e 100644
--- a/arch/um/kernel/kmsg_dump.c
+++ b/arch/um/kernel/kmsg_dump.c
@@ -31,7 +31,7 @@ static void kmsg_dumper_stdout(struct kmsg_dumper *dumper,
* expected to output the crash information.
*/
if (strcmp(con->name, "ttynull") != 0 &&
- (console_srcu_read_flags(con) & CON_ENABLED)) {
+ console_is_usable(con, console_srcu_read_flags(con), true)) {
break;
}
}
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index fa3b616af03a..17a107cc5244 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -230,6 +230,7 @@ config X86
select HAVE_DYNAMIC_FTRACE_WITH_ARGS if X86_64
select HAVE_FTRACE_REGS_HAVING_PT_REGS if X86_64
select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
+ select HAVE_DYNAMIC_FTRACE_WITH_JMP if X86_64
select HAVE_SAMPLE_FTRACE_DIRECT if X86_64
select HAVE_SAMPLE_FTRACE_DIRECT_MULTI if X86_64
select HAVE_EBPF_JIT
@@ -261,6 +262,7 @@ config X86
select HAVE_FUNCTION_ERROR_INJECTION
select HAVE_KRETPROBES
select HAVE_RETHOOK
+ select HAVE_KLP_BUILD if X86_64
select HAVE_LIVEPATCH if X86_64
select HAVE_MIXED_BREAKPOINTS_REGS
select HAVE_MOD_ARCH_SPECIFIC
@@ -297,6 +299,7 @@ config X86
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_UACCESS_VALIDATION if HAVE_OBJTOOL
select HAVE_UNSTABLE_SCHED_CLOCK
+ select HAVE_UNWIND_USER_FP if X86_64
select HAVE_USER_RETURN_NOTIFIER
select HAVE_GENERIC_VDSO
select VDSO_GETRANDOM if X86_64
@@ -379,7 +382,7 @@ config GENERIC_CSUM
config GENERIC_BUG
def_bool y
depends on BUG
- select GENERIC_BUG_RELATIVE_POINTERS if X86_64
+ select GENERIC_BUG_RELATIVE_POINTERS
config GENERIC_BUG_RELATIVE_POINTERS
bool
diff --git a/arch/x86/Kconfig.cpufeatures b/arch/x86/Kconfig.cpufeatures
index 250c10627ab3..733d5aff2456 100644
--- a/arch/x86/Kconfig.cpufeatures
+++ b/arch/x86/Kconfig.cpufeatures
@@ -124,6 +124,10 @@ config X86_DISABLED_FEATURE_PCID
def_bool y
depends on !X86_64
+config X86_DISABLED_FEATURE_LASS
+ def_bool y
+ depends on X86_32
+
config X86_DISABLED_FEATURE_PKU
def_bool y
depends on !X86_INTEL_MEMORY_PROTECTION_KEYS
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 4db7e4bf69f5..1d403a3612ea 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -48,7 +48,8 @@ endif
# How to compile the 16-bit code. Note we always compile for -march=i386;
# that way we can complain to the user if the CPU is insufficient.
-REALMODE_CFLAGS := -std=gnu11 -m16 -g -Os -DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS \
+REALMODE_CFLAGS := -std=gnu11 -fms-extensions -m16 -g -Os \
+ -DDISABLE_BRANCH_PROFILING -D__DISABLE_EXPORTS \
-Wall -Wstrict-prototypes -march=i386 -mregparm=3 \
-fno-strict-aliasing -fomit-frame-pointer -fno-pic \
-mno-mmx -mno-sse $(call cc-option,-fcf-protection=none)
@@ -60,6 +61,7 @@ REALMODE_CFLAGS += $(cc_stack_align4)
REALMODE_CFLAGS += $(CLANG_FLAGS)
ifdef CONFIG_CC_IS_CLANG
REALMODE_CFLAGS += -Wno-gnu
+REALMODE_CFLAGS += -Wno-microsoft-anon-tag
endif
export REALMODE_CFLAGS
@@ -75,7 +77,7 @@ export BITS
#
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53383
#
-KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx
+KBUILD_CFLAGS += -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-sse4a
KBUILD_RUSTFLAGS += --target=$(objtree)/scripts/target.json
KBUILD_RUSTFLAGS += -Ctarget-feature=-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2
@@ -98,7 +100,7 @@ ifeq ($(CONFIG_X86_KERNEL_IBT),y)
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104816
#
KBUILD_CFLAGS += $(call cc-option,-fcf-protection=branch -fno-jump-tables)
-KBUILD_RUSTFLAGS += -Zcf-protection=branch -Zno-jump-tables
+KBUILD_RUSTFLAGS += -Zcf-protection=branch $(if $(call rustc-min-version,109300),-Cjump-tables=n,-Zno-jump-tables)
else
KBUILD_CFLAGS += $(call cc-option,-fcf-protection=none)
endif
diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c
index a2b6b428922a..bda042933a05 100644
--- a/arch/x86/boot/a20.c
+++ b/arch/x86/boot/a20.c
@@ -135,29 +135,29 @@ int enable_a20(void)
(legacy free, etc.) */
if (a20_test_short())
return 0;
-
+
/* Next, try the BIOS (INT 0x15, AX=0x2401) */
enable_a20_bios();
if (a20_test_short())
return 0;
-
+
/* Try enabling A20 through the keyboard controller */
kbc_err = empty_8042();
if (a20_test_short())
return 0; /* BIOS worked, but with delayed reaction */
-
+
if (!kbc_err) {
enable_a20_kbc();
if (a20_test_long())
return 0;
}
-
+
/* Finally, try enabling the "fast A20 gate" */
enable_a20_fast();
if (a20_test_long())
return 0;
}
-
+
return -1;
}
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index a3c58ebe3662..8e3eab34dff4 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -193,8 +193,6 @@ static inline bool heap_free(size_t n)
void copy_to_fs(addr_t dst, void *src, size_t len);
void *copy_from_fs(void *dst, addr_t src, size_t len);
-void copy_to_gs(addr_t dst, void *src, size_t len);
-void *copy_from_gs(void *dst, addr_t src, size_t len);
/* a20.c */
int enable_a20(void);
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 74657589264d..68f9d7a1683b 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -25,7 +25,7 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
# avoid errors with '-march=i386', and future flags may depend on the target to
# be valid.
KBUILD_CFLAGS := -m$(BITS) -O2 $(CLANG_FLAGS)
-KBUILD_CFLAGS += -std=gnu11
+KBUILD_CFLAGS += -std=gnu11 -fms-extensions
KBUILD_CFLAGS += -fno-strict-aliasing -fPIE
KBUILD_CFLAGS += -Wundef
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
@@ -36,7 +36,10 @@ KBUILD_CFLAGS += -mno-mmx -mno-sse
KBUILD_CFLAGS += -ffreestanding -fshort-wchar
KBUILD_CFLAGS += -fno-stack-protector
KBUILD_CFLAGS += $(call cc-disable-warning, address-of-packed-member)
-KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
+ifdef CONFIG_CC_IS_CLANG
+KBUILD_CFLAGS += -Wno-gnu
+KBUILD_CFLAGS += -Wno-microsoft-anon-tag
+endif
KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
KBUILD_CFLAGS += -D__DISABLE_EXPORTS
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index db1048621ea2..fd855e32c9b9 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -152,17 +152,6 @@ bool insn_has_rep_prefix(struct insn *insn);
void sev_insn_decode_init(void);
bool early_setup_ghcb(void);
#else
-static inline void sev_enable(struct boot_params *bp)
-{
- /*
- * bp->cc_blob_address should only be set by boot/compressed kernel.
- * Initialize it to 0 unconditionally (thus here in this stub too) to
- * ensure that uninitialized values from buggy bootloaders aren't
- * propagated.
- */
- 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/pgtable_64.c b/arch/x86/boot/compressed/pgtable_64.c
index bdd26050dff7..0e89e197e112 100644
--- a/arch/x86/boot/compressed/pgtable_64.c
+++ b/arch/x86/boot/compressed/pgtable_64.c
@@ -3,6 +3,7 @@
#include <asm/bootparam.h>
#include <asm/bootparam_utils.h>
#include <asm/e820/types.h>
+#include <asm/pgtable.h>
#include <asm/processor.h>
#include "../string.h"
#include "efi.h"
@@ -168,9 +169,10 @@ asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
* For 4- to 5-level paging transition, set up current CR3 as
* the first and the only entry in a new top-level page table.
*/
- *trampoline_32bit = __native_read_cr3() | _PAGE_TABLE_NOENC;
+ *trampoline_32bit = native_read_cr3_pa() | _PAGE_TABLE_NOENC;
} else {
- unsigned long src;
+ u64 *new_cr3;
+ pgd_t *pgdp;
/*
* For 5- to 4-level paging transition, copy page table pointed
@@ -180,8 +182,9 @@ asmlinkage void configure_5level_paging(struct boot_params *bp, void *pgtable)
* We cannot just point to the page table from trampoline as it
* may be above 4G.
*/
- src = *(unsigned long *)__native_read_cr3() & PAGE_MASK;
- memcpy(trampoline_32bit, (void *)src, PAGE_SIZE);
+ pgdp = (pgd_t *)native_read_cr3_pa();
+ new_cr3 = (u64 *)(native_pgd_val(pgdp[0]) & PTE_PFN_MASK);
+ memcpy(trampoline_32bit, new_cr3, PAGE_SIZE);
}
toggle_la57(trampoline_32bit);
diff --git a/arch/x86/boot/compressed/sev-handle-vc.c b/arch/x86/boot/compressed/sev-handle-vc.c
index 7530ad8b768b..030001b46554 100644
--- a/arch/x86/boot/compressed/sev-handle-vc.c
+++ b/arch/x86/boot/compressed/sev-handle-vc.c
@@ -29,11 +29,10 @@
bool insn_has_rep_prefix(struct insn *insn)
{
insn_byte_t p;
- int i;
insn_get_prefixes(insn);
- for_each_insn_prefix(insn, i, p) {
+ for_each_insn_prefix(insn, p) {
if (p == 0xf2 || p == 0xf3)
return true;
}
diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index 6e5c32a53d03..c8c1464b3a56 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -14,6 +14,7 @@
#include <asm/bootparam.h>
#include <asm/pgtable_types.h>
+#include <asm/shared/msr.h>
#include <asm/sev.h>
#include <asm/trapnr.h>
#include <asm/trap_pf.h>
@@ -397,7 +398,7 @@ void sev_enable(struct boot_params *bp)
}
/* Set the SME mask if this is an SEV guest. */
- boot_rdmsr(MSR_AMD64_SEV, &m);
+ raw_rdmsr(MSR_AMD64_SEV, &m);
sev_status = m.q;
if (!(sev_status & MSR_AMD64_SEV_ENABLED))
return;
@@ -446,7 +447,7 @@ u64 sev_get_status(void)
if (sev_check_cpu_support() < 0)
return 0;
- boot_rdmsr(MSR_AMD64_SEV, &m);
+ raw_rdmsr(MSR_AMD64_SEV, &m);
return m.q;
}
@@ -496,7 +497,7 @@ bool early_is_sevsnp_guest(void)
struct msr m;
/* Obtain the address of the calling area to use */
- boot_rdmsr(MSR_SVSM_CAA, &m);
+ raw_rdmsr(MSR_SVSM_CAA, &m);
boot_svsm_caa_pa = m.q;
/*
diff --git a/arch/x86/boot/compressed/sev.h b/arch/x86/boot/compressed/sev.h
index 92f79c21939c..22637b416b46 100644
--- a/arch/x86/boot/compressed/sev.h
+++ b/arch/x86/boot/compressed/sev.h
@@ -10,7 +10,7 @@
#ifdef CONFIG_AMD_MEM_ENCRYPT
-#include "../msr.h"
+#include <asm/shared/msr.h>
void snp_accept_memory(phys_addr_t start, phys_addr_t end);
u64 sev_get_status(void);
@@ -20,7 +20,7 @@ static inline u64 sev_es_rd_ghcb_msr(void)
{
struct msr m;
- boot_rdmsr(MSR_AMD64_SEV_ES_GHCB, &m);
+ raw_rdmsr(MSR_AMD64_SEV_ES_GHCB, &m);
return m.q;
}
@@ -30,7 +30,7 @@ static inline void sev_es_wr_ghcb_msr(u64 val)
struct msr m;
m.q = val;
- boot_wrmsr(MSR_AMD64_SEV_ES_GHCB, &m);
+ raw_wrmsr(MSR_AMD64_SEV_ES_GHCB, &m);
}
#else
diff --git a/arch/x86/boot/cpucheck.c b/arch/x86/boot/cpucheck.c
index f82de8de5dc6..2e1bb936cba2 100644
--- a/arch/x86/boot/cpucheck.c
+++ b/arch/x86/boot/cpucheck.c
@@ -26,9 +26,9 @@
#include <asm/intel-family.h>
#include <asm/processor-flags.h>
#include <asm/msr-index.h>
+#include <asm/shared/msr.h>
#include "string.h"
-#include "msr.h"
static u32 err_flags[NCAPINTS];
@@ -134,9 +134,9 @@ int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr)
struct msr m;
- boot_rdmsr(MSR_K7_HWCR, &m);
+ raw_rdmsr(MSR_K7_HWCR, &m);
m.l &= ~(1 << 15);
- boot_wrmsr(MSR_K7_HWCR, &m);
+ raw_wrmsr(MSR_K7_HWCR, &m);
get_cpuflags(); /* Make sure it really did something */
err = check_cpuflags();
@@ -148,9 +148,9 @@ int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr)
struct msr m;
- boot_rdmsr(MSR_VIA_FCR, &m);
+ raw_rdmsr(MSR_VIA_FCR, &m);
m.l |= (1 << 1) | (1 << 7);
- boot_wrmsr(MSR_VIA_FCR, &m);
+ raw_wrmsr(MSR_VIA_FCR, &m);
set_bit(X86_FEATURE_CX8, cpu.flags);
err = check_cpuflags();
@@ -160,14 +160,14 @@ int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr)
struct msr m, m_tmp;
u32 level = 1;
- boot_rdmsr(0x80860004, &m);
+ raw_rdmsr(0x80860004, &m);
m_tmp = m;
m_tmp.l = ~0;
- boot_wrmsr(0x80860004, &m_tmp);
+ raw_wrmsr(0x80860004, &m_tmp);
asm("cpuid"
: "+a" (level), "=d" (cpu.flags[0])
: : "ecx", "ebx");
- boot_wrmsr(0x80860004, &m);
+ raw_wrmsr(0x80860004, &m);
err = check_cpuflags();
} else if (err == 0x01 &&
diff --git a/arch/x86/boot/msr.h b/arch/x86/boot/msr.h
deleted file mode 100644
index aed66f7ae199..000000000000
--- a/arch/x86/boot/msr.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Helpers/definitions related to MSR access.
- */
-
-#ifndef BOOT_MSR_H
-#define BOOT_MSR_H
-
-#include <asm/shared/msr.h>
-
-/*
- * The kernel proper already defines rdmsr()/wrmsr(), but they are not for the
- * boot kernel since they rely on tracepoint/exception handling infrastructure
- * that's not available here.
- */
-static inline void boot_rdmsr(unsigned int reg, struct msr *m)
-{
- asm volatile("rdmsr" : "=a" (m->l), "=d" (m->h) : "c" (reg));
-}
-
-static inline void boot_wrmsr(unsigned int reg, const struct msr *m)
-{
- asm volatile("wrmsr" : : "c" (reg), "a"(m->l), "d" (m->h) : "memory");
-}
-
-#endif /* BOOT_MSR_H */
diff --git a/arch/x86/boot/startup/Makefile b/arch/x86/boot/startup/Makefile
index e8fdf020b422..5e499cfb29b5 100644
--- a/arch/x86/boot/startup/Makefile
+++ b/arch/x86/boot/startup/Makefile
@@ -36,7 +36,7 @@ $(patsubst %.o,$(obj)/%.o,$(lib-y)): OBJECT_FILES_NON_STANDARD := y
# relocations, even if other objtool actions are being deferred.
#
$(pi-objs): objtool-enabled = 1
-$(pi-objs): objtool-args = $(if $(delay-objtool),,$(objtool-args-y)) --noabs
+$(pi-objs): objtool-args = $(if $(delay-objtool),--dry-run,$(objtool-args-y)) --noabs
#
# Confine the startup code by prefixing all symbols with __pi_ (for position
diff --git a/arch/x86/boot/startup/sev-shared.c b/arch/x86/boot/startup/sev-shared.c
index 4e22ffd73516..a0fa8bb2b945 100644
--- a/arch/x86/boot/startup/sev-shared.c
+++ b/arch/x86/boot/startup/sev-shared.c
@@ -12,7 +12,7 @@
#include <asm/setup_data.h>
#ifndef __BOOT_COMPRESSED
-#define has_cpuflag(f) boot_cpu_has(f)
+#define has_cpuflag(f) cpu_feature_enabled(f)
#else
#undef WARN
#define WARN(condition, format...) (!!(condition))
diff --git a/arch/x86/coco/sev/vc-handle.c b/arch/x86/coco/sev/vc-handle.c
index 7fc136a35334..f08c7505ed82 100644
--- a/arch/x86/coco/sev/vc-handle.c
+++ b/arch/x86/coco/sev/vc-handle.c
@@ -352,7 +352,6 @@ fault:
#define sev_printk(fmt, ...) printk(fmt, ##__VA_ARGS__)
#define error(v)
-#define has_cpuflag(f) boot_cpu_has(f)
#include "vc-shared.c"
diff --git a/arch/x86/coco/sev/vc-shared.c b/arch/x86/coco/sev/vc-shared.c
index 9b01c9ad81be..58b2f985d546 100644
--- a/arch/x86/coco/sev/vc-shared.c
+++ b/arch/x86/coco/sev/vc-shared.c
@@ -1,5 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
+#ifndef __BOOT_COMPRESSED
+#define has_cpuflag(f) cpu_feature_enabled(f)
+#endif
+
static enum es_result vc_check_opcode_bytes(struct es_em_ctxt *ctxt,
unsigned long exit_code)
{
@@ -546,6 +550,13 @@ static enum es_result vc_handle_cpuid(struct ghcb *ghcb,
/* xgetbv will cause #GP - use reset value for xcr0 */
ghcb_set_xcr0(ghcb, 1);
+ if (has_cpuflag(X86_FEATURE_SHSTK) && regs->ax == 0xd && regs->cx == 1) {
+ struct msr m;
+
+ raw_rdmsr(MSR_IA32_XSS, &m);
+ ghcb_set_xss(ghcb, m.q);
+ }
+
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_CPUID, 0, 0);
if (ret != ES_OK)
return ret;
diff --git a/arch/x86/crypto/Kconfig b/arch/x86/crypto/Kconfig
index 48d3076b6053..3fd2423d3cf8 100644
--- a/arch/x86/crypto/Kconfig
+++ b/arch/x86/crypto/Kconfig
@@ -353,16 +353,6 @@ config CRYPTO_NHPOLY1305_AVX2
Architecture: x86_64 using:
- AVX2 (Advanced Vector Extensions 2)
-config CRYPTO_POLYVAL_CLMUL_NI
- tristate "Hash functions: POLYVAL (CLMUL-NI)"
- depends on 64BIT
- select CRYPTO_POLYVAL
- help
- POLYVAL hash function for HCTR2
-
- Architecture: x86_64 using:
- - CLMUL-NI (carry-less multiplication new instructions)
-
config CRYPTO_SM3_AVX_X86_64
tristate "Hash functions: SM3 (AVX)"
depends on 64BIT
diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile
index 2d30d5d36145..5f2fb4f148fe 100644
--- a/arch/x86/crypto/Makefile
+++ b/arch/x86/crypto/Makefile
@@ -46,15 +46,13 @@ obj-$(CONFIG_CRYPTO_AES_NI_INTEL) += aesni-intel.o
aesni-intel-y := aesni-intel_asm.o aesni-intel_glue.o
aesni-intel-$(CONFIG_64BIT) += aes-ctr-avx-x86_64.o \
aes-gcm-aesni-x86_64.o \
- aes-xts-avx-x86_64.o \
- aes-gcm-avx10-x86_64.o
+ aes-gcm-vaes-avx2.o \
+ aes-gcm-vaes-avx512.o \
+ aes-xts-avx-x86_64.o
obj-$(CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL) += ghash-clmulni-intel.o
ghash-clmulni-intel-y := ghash-clmulni-intel_asm.o ghash-clmulni-intel_glue.o
-obj-$(CONFIG_CRYPTO_POLYVAL_CLMUL_NI) += polyval-clmulni.o
-polyval-clmulni-y := polyval-clmulni_asm.o polyval-clmulni_glue.o
-
obj-$(CONFIG_CRYPTO_NHPOLY1305_SSE2) += nhpoly1305-sse2.o
nhpoly1305-sse2-y := nh-sse2-x86_64.o nhpoly1305-sse2-glue.o
obj-$(CONFIG_CRYPTO_NHPOLY1305_AVX2) += nhpoly1305-avx2.o
diff --git a/arch/x86/crypto/aes-gcm-aesni-x86_64.S b/arch/x86/crypto/aes-gcm-aesni-x86_64.S
index 45940e2883a0..7c8a8a32bd3c 100644
--- a/arch/x86/crypto/aes-gcm-aesni-x86_64.S
+++ b/arch/x86/crypto/aes-gcm-aesni-x86_64.S
@@ -61,15 +61,15 @@
// for the *_aesni functions or AVX for the *_aesni_avx ones. (But it seems
// there are no CPUs that support AES-NI without also PCLMULQDQ and SSE4.1.)
//
-// The design generally follows that of aes-gcm-avx10-x86_64.S, and that file is
+// The design generally follows that of aes-gcm-vaes-avx512.S, and that file is
// more thoroughly commented. This file has the following notable changes:
//
// - The vector length is fixed at 128-bit, i.e. xmm registers. This means
// there is only one AES block (and GHASH block) per register.
//
-// - Without AVX512 / AVX10, only 16 SIMD registers are available instead of
-// 32. We work around this by being much more careful about using
-// registers, relying heavily on loads to load values as they are needed.
+// - Without AVX512, only 16 SIMD registers are available instead of 32. We
+// work around this by being much more careful about using registers,
+// relying heavily on loads to load values as they are needed.
//
// - Masking is not available either. We work around this by implementing
// partial block loads and stores using overlapping scalar loads and stores
@@ -90,8 +90,8 @@
// multiplication instead of schoolbook multiplication. This saves one
// pclmulqdq instruction per block, at the cost of one 64-bit load, one
// pshufd, and 0.25 pxors per block. (This is without the three-argument
-// XOR support that would be provided by AVX512 / AVX10, which would be
-// more beneficial to schoolbook than Karatsuba.)
+// XOR support that would be provided by AVX512, which would be more
+// beneficial to schoolbook than Karatsuba.)
//
// As a rough approximation, we can assume that Karatsuba multiplication is
// faster than schoolbook multiplication in this context if one pshufd and
diff --git a/arch/x86/crypto/aes-gcm-vaes-avx2.S b/arch/x86/crypto/aes-gcm-vaes-avx2.S
new file mode 100644
index 000000000000..93c9504a488f
--- /dev/null
+++ b/arch/x86/crypto/aes-gcm-vaes-avx2.S
@@ -0,0 +1,1146 @@
+/* SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause */
+//
+// AES-GCM implementation for x86_64 CPUs that support the following CPU
+// features: VAES && VPCLMULQDQ && AVX2
+//
+// Copyright 2025 Google LLC
+//
+// Author: Eric Biggers <ebiggers@google.com>
+//
+//------------------------------------------------------------------------------
+//
+// This file is dual-licensed, meaning that you can use it under your choice of
+// either of the following two licenses:
+//
+// Licensed under the Apache License 2.0 (the "License"). You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// or
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// -----------------------------------------------------------------------------
+//
+// This is similar to aes-gcm-vaes-avx512.S, but it uses AVX2 instead of AVX512.
+// This means it can only use 16 vector registers instead of 32, the maximum
+// vector length is 32 bytes, and some instructions such as vpternlogd and
+// masked loads/stores are unavailable. However, it is able to run on CPUs that
+// have VAES without AVX512, namely AMD Zen 3 (including "Milan" server CPUs),
+// various Intel client CPUs such as Alder Lake, and Intel Sierra Forest.
+//
+// This implementation also uses Karatsuba multiplication instead of schoolbook
+// multiplication for GHASH in its main loop. This does not help much on Intel,
+// but it improves performance by ~5% on AMD Zen 3. Other factors weighing
+// slightly in favor of Karatsuba multiplication in this implementation are the
+// lower maximum vector length (which means there are fewer key powers, so we
+// can cache the halves of each key power XOR'd together and still use less
+// memory than the AVX512 implementation), and the unavailability of the
+// vpternlogd instruction (which helped schoolbook a bit more than Karatsuba).
+
+#include <linux/linkage.h>
+
+.section .rodata
+.p2align 4
+
+ // The below three 16-byte values must be in the order that they are, as
+ // they are really two 32-byte tables and a 16-byte value that overlap:
+ //
+ // - The first 32-byte table begins at .Lselect_high_bytes_table.
+ // For 0 <= len <= 16, the 16-byte value at
+ // '.Lselect_high_bytes_table + len' selects the high 'len' bytes of
+ // another 16-byte value when AND'ed with it.
+ //
+ // - The second 32-byte table begins at .Lrshift_and_bswap_table.
+ // For 0 <= len <= 16, the 16-byte value at
+ // '.Lrshift_and_bswap_table + len' is a vpshufb mask that does the
+ // following operation: right-shift by '16 - len' bytes (shifting in
+ // zeroes), then reflect all 16 bytes.
+ //
+ // - The 16-byte value at .Lbswap_mask is a vpshufb mask that reflects
+ // all 16 bytes.
+.Lselect_high_bytes_table:
+ .octa 0
+.Lrshift_and_bswap_table:
+ .octa 0xffffffffffffffffffffffffffffffff
+.Lbswap_mask:
+ .octa 0x000102030405060708090a0b0c0d0e0f
+
+ // Sixteen 0x0f bytes. By XOR'ing an entry of .Lrshift_and_bswap_table
+ // with this, we get a mask that left-shifts by '16 - len' bytes.
+.Lfifteens:
+ .octa 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f
+
+ // This is the GHASH reducing polynomial without its constant term, i.e.
+ // x^128 + x^7 + x^2 + x, represented using the backwards mapping
+ // between bits and polynomial coefficients.
+ //
+ // Alternatively, it can be interpreted as the naturally-ordered
+ // representation of the polynomial x^127 + x^126 + x^121 + 1, i.e. the
+ // "reversed" GHASH reducing polynomial without its x^128 term.
+.Lgfpoly:
+ .octa 0xc2000000000000000000000000000001
+
+ // Same as above, but with the (1 << 64) bit set.
+.Lgfpoly_and_internal_carrybit:
+ .octa 0xc2000000000000010000000000000001
+
+ // Values needed to prepare the initial vector of counter blocks.
+.Lctr_pattern:
+ .octa 0
+ .octa 1
+
+ // The number of AES blocks per vector, as a 128-bit value.
+.Linc_2blocks:
+ .octa 2
+
+// Offsets in struct aes_gcm_key_vaes_avx2
+#define OFFSETOF_AESKEYLEN 480
+#define OFFSETOF_H_POWERS 512
+#define NUM_H_POWERS 8
+#define OFFSETOFEND_H_POWERS (OFFSETOF_H_POWERS + (NUM_H_POWERS * 16))
+#define OFFSETOF_H_POWERS_XORED OFFSETOFEND_H_POWERS
+
+.text
+
+// Do one step of GHASH-multiplying the 128-bit lanes of \a by the 128-bit lanes
+// of \b and storing the reduced products in \dst. Uses schoolbook
+// multiplication.
+.macro _ghash_mul_step i, a, b, dst, gfpoly, t0, t1, t2
+.if \i == 0
+ vpclmulqdq $0x00, \a, \b, \t0 // LO = a_L * b_L
+ vpclmulqdq $0x01, \a, \b, \t1 // MI_0 = a_L * b_H
+.elseif \i == 1
+ vpclmulqdq $0x10, \a, \b, \t2 // MI_1 = a_H * b_L
+.elseif \i == 2
+ vpxor \t2, \t1, \t1 // MI = MI_0 + MI_1
+.elseif \i == 3
+ vpclmulqdq $0x01, \t0, \gfpoly, \t2 // LO_L*(x^63 + x^62 + x^57)
+.elseif \i == 4
+ vpshufd $0x4e, \t0, \t0 // Swap halves of LO
+.elseif \i == 5
+ vpxor \t0, \t1, \t1 // Fold LO into MI (part 1)
+ vpxor \t2, \t1, \t1 // Fold LO into MI (part 2)
+.elseif \i == 6
+ vpclmulqdq $0x11, \a, \b, \dst // HI = a_H * b_H
+.elseif \i == 7
+ vpclmulqdq $0x01, \t1, \gfpoly, \t0 // MI_L*(x^63 + x^62 + x^57)
+.elseif \i == 8
+ vpshufd $0x4e, \t1, \t1 // Swap halves of MI
+.elseif \i == 9
+ vpxor \t1, \dst, \dst // Fold MI into HI (part 1)
+ vpxor \t0, \dst, \dst // Fold MI into HI (part 2)
+.endif
+.endm
+
+// GHASH-multiply the 128-bit lanes of \a by the 128-bit lanes of \b and store
+// the reduced products in \dst. See _ghash_mul_step for full explanation.
+.macro _ghash_mul a, b, dst, gfpoly, t0, t1, t2
+.irp i, 0,1,2,3,4,5,6,7,8,9
+ _ghash_mul_step \i, \a, \b, \dst, \gfpoly, \t0, \t1, \t2
+.endr
+.endm
+
+// GHASH-multiply the 128-bit lanes of \a by the 128-bit lanes of \b and add the
+// *unreduced* products to \lo, \mi, and \hi.
+.macro _ghash_mul_noreduce a, b, lo, mi, hi, t0
+ vpclmulqdq $0x00, \a, \b, \t0 // a_L * b_L
+ vpxor \t0, \lo, \lo
+ vpclmulqdq $0x01, \a, \b, \t0 // a_L * b_H
+ vpxor \t0, \mi, \mi
+ vpclmulqdq $0x10, \a, \b, \t0 // a_H * b_L
+ vpxor \t0, \mi, \mi
+ vpclmulqdq $0x11, \a, \b, \t0 // a_H * b_H
+ vpxor \t0, \hi, \hi
+.endm
+
+// Reduce the unreduced products from \lo, \mi, and \hi and store the 128-bit
+// reduced products in \hi. See _ghash_mul_step for explanation of reduction.
+.macro _ghash_reduce lo, mi, hi, gfpoly, t0
+ vpclmulqdq $0x01, \lo, \gfpoly, \t0
+ vpshufd $0x4e, \lo, \lo
+ vpxor \lo, \mi, \mi
+ vpxor \t0, \mi, \mi
+ vpclmulqdq $0x01, \mi, \gfpoly, \t0
+ vpshufd $0x4e, \mi, \mi
+ vpxor \mi, \hi, \hi
+ vpxor \t0, \hi, \hi
+.endm
+
+// This is a specialized version of _ghash_mul that computes \a * \a, i.e. it
+// squares \a. It skips computing MI = (a_L * a_H) + (a_H * a_L) = 0.
+.macro _ghash_square a, dst, gfpoly, t0, t1
+ vpclmulqdq $0x00, \a, \a, \t0 // LO = a_L * a_L
+ vpclmulqdq $0x11, \a, \a, \dst // HI = a_H * a_H
+ vpclmulqdq $0x01, \t0, \gfpoly, \t1 // LO_L*(x^63 + x^62 + x^57)
+ vpshufd $0x4e, \t0, \t0 // Swap halves of LO
+ vpxor \t0, \t1, \t1 // Fold LO into MI
+ vpclmulqdq $0x01, \t1, \gfpoly, \t0 // MI_L*(x^63 + x^62 + x^57)
+ vpshufd $0x4e, \t1, \t1 // Swap halves of MI
+ vpxor \t1, \dst, \dst // Fold MI into HI (part 1)
+ vpxor \t0, \dst, \dst // Fold MI into HI (part 2)
+.endm
+
+// void aes_gcm_precompute_vaes_avx2(struct aes_gcm_key_vaes_avx2 *key);
+//
+// Given the expanded AES key |key->base.aes_key|, derive the GHASH subkey and
+// initialize |key->h_powers| and |key->h_powers_xored|.
+//
+// We use h_powers[0..7] to store H^8 through H^1, and h_powers_xored[0..7] to
+// store the 64-bit halves of the key powers XOR'd together (for Karatsuba
+// multiplication) in the order 8,6,7,5,4,2,3,1.
+SYM_FUNC_START(aes_gcm_precompute_vaes_avx2)
+
+ // Function arguments
+ .set KEY, %rdi
+
+ // Additional local variables
+ .set POWERS_PTR, %rsi
+ .set RNDKEYLAST_PTR, %rdx
+ .set TMP0, %ymm0
+ .set TMP0_XMM, %xmm0
+ .set TMP1, %ymm1
+ .set TMP1_XMM, %xmm1
+ .set TMP2, %ymm2
+ .set TMP2_XMM, %xmm2
+ .set H_CUR, %ymm3
+ .set H_CUR_XMM, %xmm3
+ .set H_CUR2, %ymm4
+ .set H_INC, %ymm5
+ .set H_INC_XMM, %xmm5
+ .set GFPOLY, %ymm6
+ .set GFPOLY_XMM, %xmm6
+
+ // Encrypt an all-zeroes block to get the raw hash subkey.
+ movl OFFSETOF_AESKEYLEN(KEY), %eax
+ lea 6*16(KEY,%rax,4), RNDKEYLAST_PTR
+ vmovdqu (KEY), H_CUR_XMM // Zero-th round key XOR all-zeroes block
+ lea 16(KEY), %rax
+1:
+ vaesenc (%rax), H_CUR_XMM, H_CUR_XMM
+ add $16, %rax
+ cmp %rax, RNDKEYLAST_PTR
+ jne 1b
+ vaesenclast (RNDKEYLAST_PTR), H_CUR_XMM, H_CUR_XMM
+
+ // Reflect the bytes of the raw hash subkey.
+ vpshufb .Lbswap_mask(%rip), H_CUR_XMM, H_CUR_XMM
+
+ // Finish preprocessing the byte-reflected hash subkey by multiplying it
+ // by x^-1 ("standard" interpretation of polynomial coefficients) or
+ // equivalently x^1 (natural interpretation). This gets the key into a
+ // format that avoids having to bit-reflect the data blocks later.
+ vpshufd $0xd3, H_CUR_XMM, TMP0_XMM
+ vpsrad $31, TMP0_XMM, TMP0_XMM
+ vpaddq H_CUR_XMM, H_CUR_XMM, H_CUR_XMM
+ vpand .Lgfpoly_and_internal_carrybit(%rip), TMP0_XMM, TMP0_XMM
+ vpxor TMP0_XMM, H_CUR_XMM, H_CUR_XMM
+
+ // Load the gfpoly constant.
+ vbroadcasti128 .Lgfpoly(%rip), GFPOLY
+
+ // Square H^1 to get H^2.
+ _ghash_square H_CUR_XMM, H_INC_XMM, GFPOLY_XMM, TMP0_XMM, TMP1_XMM
+
+ // Create H_CUR = [H^2, H^1] and H_INC = [H^2, H^2].
+ vinserti128 $1, H_CUR_XMM, H_INC, H_CUR
+ vinserti128 $1, H_INC_XMM, H_INC, H_INC
+
+ // Compute H_CUR2 = [H^4, H^3].
+ _ghash_mul H_INC, H_CUR, H_CUR2, GFPOLY, TMP0, TMP1, TMP2
+
+ // Store [H^2, H^1] and [H^4, H^3].
+ vmovdqu H_CUR, OFFSETOF_H_POWERS+3*32(KEY)
+ vmovdqu H_CUR2, OFFSETOF_H_POWERS+2*32(KEY)
+
+ // For Karatsuba multiplication: compute and store the two 64-bit halves
+ // of each key power XOR'd together. Order is 4,2,3,1.
+ vpunpcklqdq H_CUR, H_CUR2, TMP0
+ vpunpckhqdq H_CUR, H_CUR2, TMP1
+ vpxor TMP1, TMP0, TMP0
+ vmovdqu TMP0, OFFSETOF_H_POWERS_XORED+32(KEY)
+
+ // Compute and store H_CUR = [H^6, H^5] and H_CUR2 = [H^8, H^7].
+ _ghash_mul H_INC, H_CUR2, H_CUR, GFPOLY, TMP0, TMP1, TMP2
+ _ghash_mul H_INC, H_CUR, H_CUR2, GFPOLY, TMP0, TMP1, TMP2
+ vmovdqu H_CUR, OFFSETOF_H_POWERS+1*32(KEY)
+ vmovdqu H_CUR2, OFFSETOF_H_POWERS+0*32(KEY)
+
+ // Again, compute and store the two 64-bit halves of each key power
+ // XOR'd together. Order is 8,6,7,5.
+ vpunpcklqdq H_CUR, H_CUR2, TMP0
+ vpunpckhqdq H_CUR, H_CUR2, TMP1
+ vpxor TMP1, TMP0, TMP0
+ vmovdqu TMP0, OFFSETOF_H_POWERS_XORED(KEY)
+
+ vzeroupper
+ RET
+SYM_FUNC_END(aes_gcm_precompute_vaes_avx2)
+
+// Do one step of the GHASH update of four vectors of data blocks.
+// \i: the step to do, 0 through 9
+// \ghashdata_ptr: pointer to the data blocks (ciphertext or AAD)
+// KEY: pointer to struct aes_gcm_key_vaes_avx2
+// BSWAP_MASK: mask for reflecting the bytes of blocks
+// H_POW[2-1]_XORED: cached values from KEY->h_powers_xored
+// TMP[0-2]: temporary registers. TMP[1-2] must be preserved across steps.
+// LO, MI: working state for this macro that must be preserved across steps
+// GHASH_ACC: the GHASH accumulator (input/output)
+.macro _ghash_step_4x i, ghashdata_ptr
+ .set HI, GHASH_ACC # alias
+ .set HI_XMM, GHASH_ACC_XMM
+.if \i == 0
+ // First vector
+ vmovdqu 0*32(\ghashdata_ptr), TMP1
+ vpshufb BSWAP_MASK, TMP1, TMP1
+ vmovdqu OFFSETOF_H_POWERS+0*32(KEY), TMP2
+ vpxor GHASH_ACC, TMP1, TMP1
+ vpclmulqdq $0x00, TMP2, TMP1, LO
+ vpclmulqdq $0x11, TMP2, TMP1, HI
+ vpunpckhqdq TMP1, TMP1, TMP0
+ vpxor TMP1, TMP0, TMP0
+ vpclmulqdq $0x00, H_POW2_XORED, TMP0, MI
+.elseif \i == 1
+.elseif \i == 2
+ // Second vector
+ vmovdqu 1*32(\ghashdata_ptr), TMP1
+ vpshufb BSWAP_MASK, TMP1, TMP1
+ vmovdqu OFFSETOF_H_POWERS+1*32(KEY), TMP2
+ vpclmulqdq $0x00, TMP2, TMP1, TMP0
+ vpxor TMP0, LO, LO
+ vpclmulqdq $0x11, TMP2, TMP1, TMP0
+ vpxor TMP0, HI, HI
+ vpunpckhqdq TMP1, TMP1, TMP0
+ vpxor TMP1, TMP0, TMP0
+ vpclmulqdq $0x10, H_POW2_XORED, TMP0, TMP0
+ vpxor TMP0, MI, MI
+.elseif \i == 3
+ // Third vector
+ vmovdqu 2*32(\ghashdata_ptr), TMP1
+ vpshufb BSWAP_MASK, TMP1, TMP1
+ vmovdqu OFFSETOF_H_POWERS+2*32(KEY), TMP2
+.elseif \i == 4
+ vpclmulqdq $0x00, TMP2, TMP1, TMP0
+ vpxor TMP0, LO, LO
+ vpclmulqdq $0x11, TMP2, TMP1, TMP0
+ vpxor TMP0, HI, HI
+.elseif \i == 5
+ vpunpckhqdq TMP1, TMP1, TMP0
+ vpxor TMP1, TMP0, TMP0
+ vpclmulqdq $0x00, H_POW1_XORED, TMP0, TMP0
+ vpxor TMP0, MI, MI
+
+ // Fourth vector
+ vmovdqu 3*32(\ghashdata_ptr), TMP1
+ vpshufb BSWAP_MASK, TMP1, TMP1
+.elseif \i == 6
+ vmovdqu OFFSETOF_H_POWERS+3*32(KEY), TMP2
+ vpclmulqdq $0x00, TMP2, TMP1, TMP0
+ vpxor TMP0, LO, LO
+ vpclmulqdq $0x11, TMP2, TMP1, TMP0
+ vpxor TMP0, HI, HI
+ vpunpckhqdq TMP1, TMP1, TMP0
+ vpxor TMP1, TMP0, TMP0
+ vpclmulqdq $0x10, H_POW1_XORED, TMP0, TMP0
+ vpxor TMP0, MI, MI
+.elseif \i == 7
+ // Finalize 'mi' following Karatsuba multiplication.
+ vpxor LO, MI, MI
+ vpxor HI, MI, MI
+
+ // Fold lo into mi.
+ vbroadcasti128 .Lgfpoly(%rip), TMP2
+ vpclmulqdq $0x01, LO, TMP2, TMP0
+ vpshufd $0x4e, LO, LO
+ vpxor LO, MI, MI
+ vpxor TMP0, MI, MI
+.elseif \i == 8
+ // Fold mi into hi.
+ vpclmulqdq $0x01, MI, TMP2, TMP0
+ vpshufd $0x4e, MI, MI
+ vpxor MI, HI, HI
+ vpxor TMP0, HI, HI
+.elseif \i == 9
+ vextracti128 $1, HI, TMP0_XMM
+ vpxor TMP0_XMM, HI_XMM, GHASH_ACC_XMM
+.endif
+.endm
+
+// Update GHASH with four vectors of data blocks. See _ghash_step_4x for full
+// explanation.
+.macro _ghash_4x ghashdata_ptr
+.irp i, 0,1,2,3,4,5,6,7,8,9
+ _ghash_step_4x \i, \ghashdata_ptr
+.endr
+.endm
+
+// Load 1 <= %ecx <= 16 bytes from the pointer \src into the xmm register \dst
+// and zeroize any remaining bytes. Clobbers %rax, %rcx, and \tmp{64,32}.
+.macro _load_partial_block src, dst, tmp64, tmp32
+ sub $8, %ecx // LEN - 8
+ jle .Lle8\@
+
+ // Load 9 <= LEN <= 16 bytes.
+ vmovq (\src), \dst // Load first 8 bytes
+ mov (\src, %rcx), %rax // Load last 8 bytes
+ neg %ecx
+ shl $3, %ecx
+ shr %cl, %rax // Discard overlapping bytes
+ vpinsrq $1, %rax, \dst, \dst
+ jmp .Ldone\@
+
+.Lle8\@:
+ add $4, %ecx // LEN - 4
+ jl .Llt4\@
+
+ // Load 4 <= LEN <= 8 bytes.
+ mov (\src), %eax // Load first 4 bytes
+ mov (\src, %rcx), \tmp32 // Load last 4 bytes
+ jmp .Lcombine\@
+
+.Llt4\@:
+ // Load 1 <= LEN <= 3 bytes.
+ add $2, %ecx // LEN - 2
+ movzbl (\src), %eax // Load first byte
+ jl .Lmovq\@
+ movzwl (\src, %rcx), \tmp32 // Load last 2 bytes
+.Lcombine\@:
+ shl $3, %ecx
+ shl %cl, \tmp64
+ or \tmp64, %rax // Combine the two parts
+.Lmovq\@:
+ vmovq %rax, \dst
+.Ldone\@:
+.endm
+
+// Store 1 <= %ecx <= 16 bytes from the xmm register \src to the pointer \dst.
+// Clobbers %rax, %rcx, and \tmp{64,32}.
+.macro _store_partial_block src, dst, tmp64, tmp32
+ sub $8, %ecx // LEN - 8
+ jl .Llt8\@
+
+ // Store 8 <= LEN <= 16 bytes.
+ vpextrq $1, \src, %rax
+ mov %ecx, \tmp32
+ shl $3, %ecx
+ ror %cl, %rax
+ mov %rax, (\dst, \tmp64) // Store last LEN - 8 bytes
+ vmovq \src, (\dst) // Store first 8 bytes
+ jmp .Ldone\@
+
+.Llt8\@:
+ add $4, %ecx // LEN - 4
+ jl .Llt4\@
+
+ // Store 4 <= LEN <= 7 bytes.
+ vpextrd $1, \src, %eax
+ mov %ecx, \tmp32
+ shl $3, %ecx
+ ror %cl, %eax
+ mov %eax, (\dst, \tmp64) // Store last LEN - 4 bytes
+ vmovd \src, (\dst) // Store first 4 bytes
+ jmp .Ldone\@
+
+.Llt4\@:
+ // Store 1 <= LEN <= 3 bytes.
+ vpextrb $0, \src, 0(\dst)
+ cmp $-2, %ecx // LEN - 4 == -2, i.e. LEN == 2?
+ jl .Ldone\@
+ vpextrb $1, \src, 1(\dst)
+ je .Ldone\@
+ vpextrb $2, \src, 2(\dst)
+.Ldone\@:
+.endm
+
+// void aes_gcm_aad_update_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+// u8 ghash_acc[16],
+// const u8 *aad, int aadlen);
+//
+// This function processes the AAD (Additional Authenticated Data) in GCM.
+// Using the key |key|, it updates the GHASH accumulator |ghash_acc| with the
+// data given by |aad| and |aadlen|. On the first call, |ghash_acc| must be all
+// zeroes. |aadlen| must be a multiple of 16, except on the last call where it
+// can be any length. The caller must do any buffering needed to ensure this.
+//
+// This handles large amounts of AAD efficiently, while also keeping overhead
+// low for small amounts which is the common case. TLS and IPsec use less than
+// one block of AAD, but (uncommonly) other use cases may use much more.
+SYM_FUNC_START(aes_gcm_aad_update_vaes_avx2)
+
+ // Function arguments
+ .set KEY, %rdi
+ .set GHASH_ACC_PTR, %rsi
+ .set AAD, %rdx
+ .set AADLEN, %ecx // Must be %ecx for _load_partial_block
+ .set AADLEN64, %rcx // Zero-extend AADLEN before using!
+
+ // Additional local variables.
+ // %rax and %r8 are used as temporary registers.
+ .set TMP0, %ymm0
+ .set TMP0_XMM, %xmm0
+ .set TMP1, %ymm1
+ .set TMP1_XMM, %xmm1
+ .set TMP2, %ymm2
+ .set TMP2_XMM, %xmm2
+ .set LO, %ymm3
+ .set LO_XMM, %xmm3
+ .set MI, %ymm4
+ .set MI_XMM, %xmm4
+ .set GHASH_ACC, %ymm5
+ .set GHASH_ACC_XMM, %xmm5
+ .set BSWAP_MASK, %ymm6
+ .set BSWAP_MASK_XMM, %xmm6
+ .set GFPOLY, %ymm7
+ .set GFPOLY_XMM, %xmm7
+ .set H_POW2_XORED, %ymm8
+ .set H_POW1_XORED, %ymm9
+
+ // Load the bswap_mask and gfpoly constants. Since AADLEN is usually
+ // small, usually only 128-bit vectors will be used. So as an
+ // optimization, don't broadcast these constants to both 128-bit lanes
+ // quite yet.
+ vmovdqu .Lbswap_mask(%rip), BSWAP_MASK_XMM
+ vmovdqu .Lgfpoly(%rip), GFPOLY_XMM
+
+ // Load the GHASH accumulator.
+ vmovdqu (GHASH_ACC_PTR), GHASH_ACC_XMM
+
+ // Check for the common case of AADLEN <= 16, as well as AADLEN == 0.
+ test AADLEN, AADLEN
+ jz .Laad_done
+ cmp $16, AADLEN
+ jle .Laad_lastblock
+
+ // AADLEN > 16, so we'll operate on full vectors. Broadcast bswap_mask
+ // and gfpoly to both 128-bit lanes.
+ vinserti128 $1, BSWAP_MASK_XMM, BSWAP_MASK, BSWAP_MASK
+ vinserti128 $1, GFPOLY_XMM, GFPOLY, GFPOLY
+
+ // If AADLEN >= 128, update GHASH with 128 bytes of AAD at a time.
+ add $-128, AADLEN // 128 is 4 bytes, -128 is 1 byte
+ jl .Laad_loop_4x_done
+ vmovdqu OFFSETOF_H_POWERS_XORED(KEY), H_POW2_XORED
+ vmovdqu OFFSETOF_H_POWERS_XORED+32(KEY), H_POW1_XORED
+.Laad_loop_4x:
+ _ghash_4x AAD
+ sub $-128, AAD
+ add $-128, AADLEN
+ jge .Laad_loop_4x
+.Laad_loop_4x_done:
+
+ // If AADLEN >= 32, update GHASH with 32 bytes of AAD at a time.
+ add $96, AADLEN
+ jl .Laad_loop_1x_done
+.Laad_loop_1x:
+ vmovdqu (AAD), TMP0
+ vpshufb BSWAP_MASK, TMP0, TMP0
+ vpxor TMP0, GHASH_ACC, GHASH_ACC
+ vmovdqu OFFSETOFEND_H_POWERS-32(KEY), TMP0
+ _ghash_mul TMP0, GHASH_ACC, GHASH_ACC, GFPOLY, TMP1, TMP2, LO
+ vextracti128 $1, GHASH_ACC, TMP0_XMM
+ vpxor TMP0_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
+ add $32, AAD
+ sub $32, AADLEN
+ jge .Laad_loop_1x
+.Laad_loop_1x_done:
+ add $32, AADLEN
+ // Now 0 <= AADLEN < 32.
+
+ jz .Laad_done
+ cmp $16, AADLEN
+ jle .Laad_lastblock
+
+ // Update GHASH with the remaining 17 <= AADLEN <= 31 bytes of AAD.
+ mov AADLEN, AADLEN // Zero-extend AADLEN to AADLEN64.
+ vmovdqu (AAD), TMP0_XMM
+ vmovdqu -16(AAD, AADLEN64), TMP1_XMM
+ vpshufb BSWAP_MASK_XMM, TMP0_XMM, TMP0_XMM
+ vpxor TMP0_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
+ lea .Lrshift_and_bswap_table(%rip), %rax
+ vpshufb -16(%rax, AADLEN64), TMP1_XMM, TMP1_XMM
+ vinserti128 $1, TMP1_XMM, GHASH_ACC, GHASH_ACC
+ vmovdqu OFFSETOFEND_H_POWERS-32(KEY), TMP0
+ _ghash_mul TMP0, GHASH_ACC, GHASH_ACC, GFPOLY, TMP1, TMP2, LO
+ vextracti128 $1, GHASH_ACC, TMP0_XMM
+ vpxor TMP0_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
+ jmp .Laad_done
+
+.Laad_lastblock:
+ // Update GHASH with the remaining 1 <= AADLEN <= 16 bytes of AAD.
+ _load_partial_block AAD, TMP0_XMM, %r8, %r8d
+ vpshufb BSWAP_MASK_XMM, TMP0_XMM, TMP0_XMM
+ vpxor TMP0_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
+ vmovdqu OFFSETOFEND_H_POWERS-16(KEY), TMP0_XMM
+ _ghash_mul TMP0_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM, GFPOLY_XMM, \
+ TMP1_XMM, TMP2_XMM, LO_XMM
+
+.Laad_done:
+ // Store the updated GHASH accumulator back to memory.
+ vmovdqu GHASH_ACC_XMM, (GHASH_ACC_PTR)
+
+ vzeroupper
+ RET
+SYM_FUNC_END(aes_gcm_aad_update_vaes_avx2)
+
+// Do one non-last round of AES encryption on the blocks in the given AESDATA
+// vectors using the round key that has been broadcast to all 128-bit lanes of
+// \round_key.
+.macro _vaesenc round_key, vecs:vararg
+.irp i, \vecs
+ vaesenc \round_key, AESDATA\i, AESDATA\i
+.endr
+.endm
+
+// Generate counter blocks in the given AESDATA vectors, then do the zero-th AES
+// round on them. Clobbers TMP0.
+.macro _ctr_begin vecs:vararg
+ vbroadcasti128 .Linc_2blocks(%rip), TMP0
+.irp i, \vecs
+ vpshufb BSWAP_MASK, LE_CTR, AESDATA\i
+ vpaddd TMP0, LE_CTR, LE_CTR
+.endr
+.irp i, \vecs
+ vpxor RNDKEY0, AESDATA\i, AESDATA\i
+.endr
+.endm
+
+// Generate and encrypt counter blocks in the given AESDATA vectors, excluding
+// the last AES round. Clobbers %rax and TMP0.
+.macro _aesenc_loop vecs:vararg
+ _ctr_begin \vecs
+ lea 16(KEY), %rax
+.Laesenc_loop\@:
+ vbroadcasti128 (%rax), TMP0
+ _vaesenc TMP0, \vecs
+ add $16, %rax
+ cmp %rax, RNDKEYLAST_PTR
+ jne .Laesenc_loop\@
+.endm
+
+// Finalize the keystream blocks in the given AESDATA vectors by doing the last
+// AES round, then XOR those keystream blocks with the corresponding data.
+// Reduce latency by doing the XOR before the vaesenclast, utilizing the
+// property vaesenclast(key, a) ^ b == vaesenclast(key ^ b, a). Clobbers TMP0.
+.macro _aesenclast_and_xor vecs:vararg
+.irp i, \vecs
+ vpxor \i*32(SRC), RNDKEYLAST, TMP0
+ vaesenclast TMP0, AESDATA\i, AESDATA\i
+.endr
+.irp i, \vecs
+ vmovdqu AESDATA\i, \i*32(DST)
+.endr
+.endm
+
+// void aes_gcm_{enc,dec}_update_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+// const u32 le_ctr[4], u8 ghash_acc[16],
+// const u8 *src, u8 *dst, int datalen);
+//
+// This macro generates a GCM encryption or decryption update function with the
+// above prototype (with \enc selecting which one). The function computes the
+// next portion of the CTR keystream, XOR's it with |datalen| bytes from |src|,
+// and writes the resulting encrypted or decrypted data to |dst|. It also
+// updates the GHASH accumulator |ghash_acc| using the next |datalen| ciphertext
+// bytes.
+//
+// |datalen| must be a multiple of 16, except on the last call where it can be
+// any length. The caller must do any buffering needed to ensure this. Both
+// in-place and out-of-place en/decryption are supported.
+//
+// |le_ctr| must give the current counter in little-endian format. This
+// function loads the counter from |le_ctr| and increments the loaded counter as
+// needed, but it does *not* store the updated counter back to |le_ctr|. The
+// caller must update |le_ctr| if any more data segments follow. Internally,
+// only the low 32-bit word of the counter is incremented, following the GCM
+// standard.
+.macro _aes_gcm_update enc
+
+ // Function arguments
+ .set KEY, %rdi
+ .set LE_CTR_PTR, %rsi
+ .set LE_CTR_PTR32, %esi
+ .set GHASH_ACC_PTR, %rdx
+ .set SRC, %rcx // Assumed to be %rcx.
+ // See .Ltail_xor_and_ghash_1to16bytes
+ .set DST, %r8
+ .set DATALEN, %r9d
+ .set DATALEN64, %r9 // Zero-extend DATALEN before using!
+
+ // Additional local variables
+
+ // %rax is used as a temporary register. LE_CTR_PTR is also available
+ // as a temporary register after the counter is loaded.
+
+ // AES key length in bytes
+ .set AESKEYLEN, %r10d
+ .set AESKEYLEN64, %r10
+
+ // Pointer to the last AES round key for the chosen AES variant
+ .set RNDKEYLAST_PTR, %r11
+
+ // BSWAP_MASK is the shuffle mask for byte-reflecting 128-bit values
+ // using vpshufb, copied to all 128-bit lanes.
+ .set BSWAP_MASK, %ymm0
+ .set BSWAP_MASK_XMM, %xmm0
+
+ // GHASH_ACC is the accumulator variable for GHASH. When fully reduced,
+ // only the lowest 128-bit lane can be nonzero. When not fully reduced,
+ // more than one lane may be used, and they need to be XOR'd together.
+ .set GHASH_ACC, %ymm1
+ .set GHASH_ACC_XMM, %xmm1
+
+ // TMP[0-2] are temporary registers.
+ .set TMP0, %ymm2
+ .set TMP0_XMM, %xmm2
+ .set TMP1, %ymm3
+ .set TMP1_XMM, %xmm3
+ .set TMP2, %ymm4
+ .set TMP2_XMM, %xmm4
+
+ // LO and MI are used to accumulate unreduced GHASH products.
+ .set LO, %ymm5
+ .set LO_XMM, %xmm5
+ .set MI, %ymm6
+ .set MI_XMM, %xmm6
+
+ // H_POW[2-1]_XORED contain cached values from KEY->h_powers_xored. The
+ // descending numbering reflects the order of the key powers.
+ .set H_POW2_XORED, %ymm7
+ .set H_POW2_XORED_XMM, %xmm7
+ .set H_POW1_XORED, %ymm8
+
+ // RNDKEY0 caches the zero-th round key, and RNDKEYLAST the last one.
+ .set RNDKEY0, %ymm9
+ .set RNDKEYLAST, %ymm10
+
+ // LE_CTR contains the next set of little-endian counter blocks.
+ .set LE_CTR, %ymm11
+
+ // AESDATA[0-3] hold the counter blocks that are being encrypted by AES.
+ .set AESDATA0, %ymm12
+ .set AESDATA0_XMM, %xmm12
+ .set AESDATA1, %ymm13
+ .set AESDATA1_XMM, %xmm13
+ .set AESDATA2, %ymm14
+ .set AESDATA3, %ymm15
+
+.if \enc
+ .set GHASHDATA_PTR, DST
+.else
+ .set GHASHDATA_PTR, SRC
+.endif
+
+ vbroadcasti128 .Lbswap_mask(%rip), BSWAP_MASK
+
+ // Load the GHASH accumulator and the starting counter.
+ vmovdqu (GHASH_ACC_PTR), GHASH_ACC_XMM
+ vbroadcasti128 (LE_CTR_PTR), LE_CTR
+
+ // Load the AES key length in bytes.
+ movl OFFSETOF_AESKEYLEN(KEY), AESKEYLEN
+
+ // Make RNDKEYLAST_PTR point to the last AES round key. This is the
+ // round key with index 10, 12, or 14 for AES-128, AES-192, or AES-256
+ // respectively. Then load the zero-th and last round keys.
+ lea 6*16(KEY,AESKEYLEN64,4), RNDKEYLAST_PTR
+ vbroadcasti128 (KEY), RNDKEY0
+ vbroadcasti128 (RNDKEYLAST_PTR), RNDKEYLAST
+
+ // Finish initializing LE_CTR by adding 1 to the second block.
+ vpaddd .Lctr_pattern(%rip), LE_CTR, LE_CTR
+
+ // If there are at least 128 bytes of data, then continue into the loop
+ // that processes 128 bytes of data at a time. Otherwise skip it.
+ add $-128, DATALEN // 128 is 4 bytes, -128 is 1 byte
+ jl .Lcrypt_loop_4x_done\@
+
+ vmovdqu OFFSETOF_H_POWERS_XORED(KEY), H_POW2_XORED
+ vmovdqu OFFSETOF_H_POWERS_XORED+32(KEY), H_POW1_XORED
+
+ // Main loop: en/decrypt and hash 4 vectors (128 bytes) at a time.
+
+.if \enc
+ // Encrypt the first 4 vectors of plaintext blocks.
+ _aesenc_loop 0,1,2,3
+ _aesenclast_and_xor 0,1,2,3
+ sub $-128, SRC // 128 is 4 bytes, -128 is 1 byte
+ add $-128, DATALEN
+ jl .Lghash_last_ciphertext_4x\@
+.endif
+
+.align 16
+.Lcrypt_loop_4x\@:
+
+ // Start the AES encryption of the counter blocks.
+ _ctr_begin 0,1,2,3
+ cmp $24, AESKEYLEN
+ jl 128f // AES-128?
+ je 192f // AES-192?
+ // AES-256
+ vbroadcasti128 -13*16(RNDKEYLAST_PTR), TMP0
+ _vaesenc TMP0, 0,1,2,3
+ vbroadcasti128 -12*16(RNDKEYLAST_PTR), TMP0
+ _vaesenc TMP0, 0,1,2,3
+192:
+ vbroadcasti128 -11*16(RNDKEYLAST_PTR), TMP0
+ _vaesenc TMP0, 0,1,2,3
+ vbroadcasti128 -10*16(RNDKEYLAST_PTR), TMP0
+ _vaesenc TMP0, 0,1,2,3
+128:
+
+ // Finish the AES encryption of the counter blocks in AESDATA[0-3],
+ // interleaved with the GHASH update of the ciphertext blocks.
+.irp i, 9,8,7,6,5,4,3,2,1
+ _ghash_step_4x (9 - \i), GHASHDATA_PTR
+ vbroadcasti128 -\i*16(RNDKEYLAST_PTR), TMP0
+ _vaesenc TMP0, 0,1,2,3
+.endr
+ _ghash_step_4x 9, GHASHDATA_PTR
+.if \enc
+ sub $-128, DST // 128 is 4 bytes, -128 is 1 byte
+.endif
+ _aesenclast_and_xor 0,1,2,3
+ sub $-128, SRC
+.if !\enc
+ sub $-128, DST
+.endif
+ add $-128, DATALEN
+ jge .Lcrypt_loop_4x\@
+
+.if \enc
+.Lghash_last_ciphertext_4x\@:
+ // Update GHASH with the last set of ciphertext blocks.
+ _ghash_4x DST
+ sub $-128, DST
+.endif
+
+.Lcrypt_loop_4x_done\@:
+
+ // Undo the extra subtraction by 128 and check whether data remains.
+ sub $-128, DATALEN // 128 is 4 bytes, -128 is 1 byte
+ jz .Ldone\@
+
+ // The data length isn't a multiple of 128 bytes. Process the remaining
+ // data of length 1 <= DATALEN < 128.
+ //
+ // Since there are enough key powers available for all remaining data,
+ // there is no need to do a GHASH reduction after each iteration.
+ // Instead, multiply each remaining block by its own key power, and only
+ // do a GHASH reduction at the very end.
+
+ // Make POWERS_PTR point to the key powers [H^N, H^(N-1), ...] where N
+ // is the number of blocks that remain.
+ .set POWERS_PTR, LE_CTR_PTR // LE_CTR_PTR is free to be reused.
+ .set POWERS_PTR32, LE_CTR_PTR32
+ mov DATALEN, %eax
+ neg %rax
+ and $~15, %rax // -round_up(DATALEN, 16)
+ lea OFFSETOFEND_H_POWERS(KEY,%rax), POWERS_PTR
+
+ // Start collecting the unreduced GHASH intermediate value LO, MI, HI.
+ .set HI, H_POW2_XORED // H_POW2_XORED is free to be reused.
+ .set HI_XMM, H_POW2_XORED_XMM
+ vpxor LO_XMM, LO_XMM, LO_XMM
+ vpxor MI_XMM, MI_XMM, MI_XMM
+ vpxor HI_XMM, HI_XMM, HI_XMM
+
+ // 1 <= DATALEN < 128. Generate 2 or 4 more vectors of keystream blocks
+ // excluding the last AES round, depending on the remaining DATALEN.
+ cmp $64, DATALEN
+ jg .Ltail_gen_4_keystream_vecs\@
+ _aesenc_loop 0,1
+ cmp $32, DATALEN
+ jge .Ltail_xor_and_ghash_full_vec_loop\@
+ jmp .Ltail_xor_and_ghash_partial_vec\@
+.Ltail_gen_4_keystream_vecs\@:
+ _aesenc_loop 0,1,2,3
+
+ // XOR the remaining data and accumulate the unreduced GHASH products
+ // for DATALEN >= 32, starting with one full 32-byte vector at a time.
+.Ltail_xor_and_ghash_full_vec_loop\@:
+.if \enc
+ _aesenclast_and_xor 0
+ vpshufb BSWAP_MASK, AESDATA0, AESDATA0
+.else
+ vmovdqu (SRC), TMP1
+ vpxor TMP1, RNDKEYLAST, TMP0
+ vaesenclast TMP0, AESDATA0, AESDATA0
+ vmovdqu AESDATA0, (DST)
+ vpshufb BSWAP_MASK, TMP1, AESDATA0
+.endif
+ // The ciphertext blocks (i.e. GHASH input data) are now in AESDATA0.
+ vpxor GHASH_ACC, AESDATA0, AESDATA0
+ vmovdqu (POWERS_PTR), TMP2
+ _ghash_mul_noreduce TMP2, AESDATA0, LO, MI, HI, TMP0
+ vmovdqa AESDATA1, AESDATA0
+ vmovdqa AESDATA2, AESDATA1
+ vmovdqa AESDATA3, AESDATA2
+ vpxor GHASH_ACC_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
+ add $32, SRC
+ add $32, DST
+ add $32, POWERS_PTR
+ sub $32, DATALEN
+ cmp $32, DATALEN
+ jge .Ltail_xor_and_ghash_full_vec_loop\@
+ test DATALEN, DATALEN
+ jz .Ltail_ghash_reduce\@
+
+.Ltail_xor_and_ghash_partial_vec\@:
+ // XOR the remaining data and accumulate the unreduced GHASH products,
+ // for 1 <= DATALEN < 32.
+ vaesenclast RNDKEYLAST, AESDATA0, AESDATA0
+ cmp $16, DATALEN
+ jle .Ltail_xor_and_ghash_1to16bytes\@
+
+ // Handle 17 <= DATALEN < 32.
+
+ // Load a vpshufb mask that will right-shift by '32 - DATALEN' bytes
+ // (shifting in zeroes), then reflect all 16 bytes.
+ lea .Lrshift_and_bswap_table(%rip), %rax
+ vmovdqu -16(%rax, DATALEN64), TMP2_XMM
+
+ // Move the second keystream block to its own register and left-align it
+ vextracti128 $1, AESDATA0, AESDATA1_XMM
+ vpxor .Lfifteens(%rip), TMP2_XMM, TMP0_XMM
+ vpshufb TMP0_XMM, AESDATA1_XMM, AESDATA1_XMM
+
+ // Using overlapping loads and stores, XOR the source data with the
+ // keystream and write the destination data. Then prepare the GHASH
+ // input data: the full ciphertext block and the zero-padded partial
+ // ciphertext block, both byte-reflected, in AESDATA0.
+.if \enc
+ vpxor -16(SRC, DATALEN64), AESDATA1_XMM, AESDATA1_XMM
+ vpxor (SRC), AESDATA0_XMM, AESDATA0_XMM
+ vmovdqu AESDATA1_XMM, -16(DST, DATALEN64)
+ vmovdqu AESDATA0_XMM, (DST)
+ vpshufb TMP2_XMM, AESDATA1_XMM, AESDATA1_XMM
+ vpshufb BSWAP_MASK_XMM, AESDATA0_XMM, AESDATA0_XMM
+.else
+ vmovdqu -16(SRC, DATALEN64), TMP1_XMM
+ vmovdqu (SRC), TMP0_XMM
+ vpxor TMP1_XMM, AESDATA1_XMM, AESDATA1_XMM
+ vpxor TMP0_XMM, AESDATA0_XMM, AESDATA0_XMM
+ vmovdqu AESDATA1_XMM, -16(DST, DATALEN64)
+ vmovdqu AESDATA0_XMM, (DST)
+ vpshufb TMP2_XMM, TMP1_XMM, AESDATA1_XMM
+ vpshufb BSWAP_MASK_XMM, TMP0_XMM, AESDATA0_XMM
+.endif
+ vpxor GHASH_ACC_XMM, AESDATA0_XMM, AESDATA0_XMM
+ vinserti128 $1, AESDATA1_XMM, AESDATA0, AESDATA0
+ vmovdqu (POWERS_PTR), TMP2
+ jmp .Ltail_ghash_last_vec\@
+
+.Ltail_xor_and_ghash_1to16bytes\@:
+ // Handle 1 <= DATALEN <= 16. Carefully load and store the
+ // possibly-partial block, which we mustn't access out of bounds.
+ vmovdqu (POWERS_PTR), TMP2_XMM
+ mov SRC, KEY // Free up %rcx, assuming SRC == %rcx
+ mov DATALEN, %ecx
+ _load_partial_block KEY, TMP0_XMM, POWERS_PTR, POWERS_PTR32
+ vpxor TMP0_XMM, AESDATA0_XMM, AESDATA0_XMM
+ mov DATALEN, %ecx
+ _store_partial_block AESDATA0_XMM, DST, POWERS_PTR, POWERS_PTR32
+.if \enc
+ lea .Lselect_high_bytes_table(%rip), %rax
+ vpshufb BSWAP_MASK_XMM, AESDATA0_XMM, AESDATA0_XMM
+ vpand (%rax, DATALEN64), AESDATA0_XMM, AESDATA0_XMM
+.else
+ vpshufb BSWAP_MASK_XMM, TMP0_XMM, AESDATA0_XMM
+.endif
+ vpxor GHASH_ACC_XMM, AESDATA0_XMM, AESDATA0_XMM
+
+.Ltail_ghash_last_vec\@:
+ // Accumulate the unreduced GHASH products for the last 1-2 blocks. The
+ // GHASH input data is in AESDATA0. If only one block remains, then the
+ // second block in AESDATA0 is zero and does not affect the result.
+ _ghash_mul_noreduce TMP2, AESDATA0, LO, MI, HI, TMP0
+
+.Ltail_ghash_reduce\@:
+ // Finally, do the GHASH reduction.
+ vbroadcasti128 .Lgfpoly(%rip), TMP0
+ _ghash_reduce LO, MI, HI, TMP0, TMP1
+ vextracti128 $1, HI, GHASH_ACC_XMM
+ vpxor HI_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
+
+.Ldone\@:
+ // Store the updated GHASH accumulator back to memory.
+ vmovdqu GHASH_ACC_XMM, (GHASH_ACC_PTR)
+
+ vzeroupper
+ RET
+.endm
+
+// void aes_gcm_enc_final_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+// const u32 le_ctr[4], u8 ghash_acc[16],
+// u64 total_aadlen, u64 total_datalen);
+// bool aes_gcm_dec_final_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+// const u32 le_ctr[4], const u8 ghash_acc[16],
+// u64 total_aadlen, u64 total_datalen,
+// const u8 tag[16], int taglen);
+//
+// This macro generates one of the above two functions (with \enc selecting
+// which one). Both functions finish computing the GCM authentication tag by
+// updating GHASH with the lengths block and encrypting the GHASH accumulator.
+// |total_aadlen| and |total_datalen| must be the total length of the additional
+// authenticated data and the en/decrypted data in bytes, respectively.
+//
+// The encryption function then stores the full-length (16-byte) computed
+// authentication tag to |ghash_acc|. The decryption function instead loads the
+// expected authentication tag (the one that was transmitted) from the 16-byte
+// buffer |tag|, compares the first 4 <= |taglen| <= 16 bytes of it to the
+// computed tag in constant time, and returns true if and only if they match.
+.macro _aes_gcm_final enc
+
+ // Function arguments
+ .set KEY, %rdi
+ .set LE_CTR_PTR, %rsi
+ .set GHASH_ACC_PTR, %rdx
+ .set TOTAL_AADLEN, %rcx
+ .set TOTAL_DATALEN, %r8
+ .set TAG, %r9
+ .set TAGLEN, %r10d // Originally at 8(%rsp)
+ .set TAGLEN64, %r10
+
+ // Additional local variables.
+ // %rax and %xmm0-%xmm3 are used as temporary registers.
+ .set AESKEYLEN, %r11d
+ .set AESKEYLEN64, %r11
+ .set GFPOLY, %xmm4
+ .set BSWAP_MASK, %xmm5
+ .set LE_CTR, %xmm6
+ .set GHASH_ACC, %xmm7
+ .set H_POW1, %xmm8
+
+ // Load some constants.
+ vmovdqa .Lgfpoly(%rip), GFPOLY
+ vmovdqa .Lbswap_mask(%rip), BSWAP_MASK
+
+ // Load the AES key length in bytes.
+ movl OFFSETOF_AESKEYLEN(KEY), AESKEYLEN
+
+ // Set up a counter block with 1 in the low 32-bit word. This is the
+ // counter that produces the ciphertext needed to encrypt the auth tag.
+ // GFPOLY has 1 in the low word, so grab the 1 from there using a blend.
+ vpblendd $0xe, (LE_CTR_PTR), GFPOLY, LE_CTR
+
+ // Build the lengths block and XOR it with the GHASH accumulator.
+ // Although the lengths block is defined as the AAD length followed by
+ // the en/decrypted data length, both in big-endian byte order, a byte
+ // reflection of the full block is needed because of the way we compute
+ // GHASH (see _ghash_mul_step). By using little-endian values in the
+ // opposite order, we avoid having to reflect any bytes here.
+ vmovq TOTAL_DATALEN, %xmm0
+ vpinsrq $1, TOTAL_AADLEN, %xmm0, %xmm0
+ vpsllq $3, %xmm0, %xmm0 // Bytes to bits
+ vpxor (GHASH_ACC_PTR), %xmm0, GHASH_ACC
+
+ // Load the first hash key power (H^1), which is stored last.
+ vmovdqu OFFSETOFEND_H_POWERS-16(KEY), H_POW1
+
+ // Load TAGLEN if decrypting.
+.if !\enc
+ movl 8(%rsp), TAGLEN
+.endif
+
+ // Make %rax point to the last AES round key for the chosen AES variant.
+ lea 6*16(KEY,AESKEYLEN64,4), %rax
+
+ // Start the AES encryption of the counter block by swapping the counter
+ // block to big-endian and XOR-ing it with the zero-th AES round key.
+ vpshufb BSWAP_MASK, LE_CTR, %xmm0
+ vpxor (KEY), %xmm0, %xmm0
+
+ // Complete the AES encryption and multiply GHASH_ACC by H^1.
+ // Interleave the AES and GHASH instructions to improve performance.
+ cmp $24, AESKEYLEN
+ jl 128f // AES-128?
+ je 192f // AES-192?
+ // AES-256
+ vaesenc -13*16(%rax), %xmm0, %xmm0
+ vaesenc -12*16(%rax), %xmm0, %xmm0
+192:
+ vaesenc -11*16(%rax), %xmm0, %xmm0
+ vaesenc -10*16(%rax), %xmm0, %xmm0
+128:
+.irp i, 0,1,2,3,4,5,6,7,8
+ _ghash_mul_step \i, H_POW1, GHASH_ACC, GHASH_ACC, GFPOLY, \
+ %xmm1, %xmm2, %xmm3
+ vaesenc (\i-9)*16(%rax), %xmm0, %xmm0
+.endr
+ _ghash_mul_step 9, H_POW1, GHASH_ACC, GHASH_ACC, GFPOLY, \
+ %xmm1, %xmm2, %xmm3
+
+ // Undo the byte reflection of the GHASH accumulator.
+ vpshufb BSWAP_MASK, GHASH_ACC, GHASH_ACC
+
+ // Do the last AES round and XOR the resulting keystream block with the
+ // GHASH accumulator to produce the full computed authentication tag.
+ //
+ // Reduce latency by taking advantage of the property vaesenclast(key,
+ // a) ^ b == vaesenclast(key ^ b, a). I.e., XOR GHASH_ACC into the last
+ // round key, instead of XOR'ing the final AES output with GHASH_ACC.
+ //
+ // enc_final then returns the computed auth tag, while dec_final
+ // compares it with the transmitted one and returns a bool. To compare
+ // the tags, dec_final XORs them together and uses vptest to check
+ // whether the result is all-zeroes. This should be constant-time.
+ // dec_final applies the vaesenclast optimization to this additional
+ // value XOR'd too.
+.if \enc
+ vpxor (%rax), GHASH_ACC, %xmm1
+ vaesenclast %xmm1, %xmm0, GHASH_ACC
+ vmovdqu GHASH_ACC, (GHASH_ACC_PTR)
+.else
+ vpxor (TAG), GHASH_ACC, GHASH_ACC
+ vpxor (%rax), GHASH_ACC, GHASH_ACC
+ vaesenclast GHASH_ACC, %xmm0, %xmm0
+ lea .Lselect_high_bytes_table(%rip), %rax
+ vmovdqu (%rax, TAGLEN64), %xmm1
+ vpshufb BSWAP_MASK, %xmm1, %xmm1 // select low bytes, not high
+ xor %eax, %eax
+ vptest %xmm1, %xmm0
+ sete %al
+.endif
+ // No need for vzeroupper here, since only used xmm registers were used.
+ RET
+.endm
+
+SYM_FUNC_START(aes_gcm_enc_update_vaes_avx2)
+ _aes_gcm_update 1
+SYM_FUNC_END(aes_gcm_enc_update_vaes_avx2)
+SYM_FUNC_START(aes_gcm_dec_update_vaes_avx2)
+ _aes_gcm_update 0
+SYM_FUNC_END(aes_gcm_dec_update_vaes_avx2)
+
+SYM_FUNC_START(aes_gcm_enc_final_vaes_avx2)
+ _aes_gcm_final 1
+SYM_FUNC_END(aes_gcm_enc_final_vaes_avx2)
+SYM_FUNC_START(aes_gcm_dec_final_vaes_avx2)
+ _aes_gcm_final 0
+SYM_FUNC_END(aes_gcm_dec_final_vaes_avx2)
diff --git a/arch/x86/crypto/aes-gcm-avx10-x86_64.S b/arch/x86/crypto/aes-gcm-vaes-avx512.S
index 02ee11083d4f..06b71314d65c 100644
--- a/arch/x86/crypto/aes-gcm-avx10-x86_64.S
+++ b/arch/x86/crypto/aes-gcm-vaes-avx512.S
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause */
//
-// VAES and VPCLMULQDQ optimized AES-GCM for x86_64
+// AES-GCM implementation for x86_64 CPUs that support the following CPU
+// features: VAES && VPCLMULQDQ && AVX512BW && AVX512VL && BMI2
//
// Copyright 2024 Google LLC
//
@@ -45,41 +46,6 @@
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
-//
-//------------------------------------------------------------------------------
-//
-// This file implements AES-GCM (Galois/Counter Mode) for x86_64 CPUs that
-// support VAES (vector AES), VPCLMULQDQ (vector carryless multiplication), and
-// either AVX512 or AVX10. Some of the functions, notably the encryption and
-// decryption update functions which are the most performance-critical, are
-// provided in two variants generated from a macro: one using 256-bit vectors
-// (suffix: vaes_avx10_256) and one using 512-bit vectors (vaes_avx10_512). The
-// other, "shared" functions (vaes_avx10) use at most 256-bit vectors.
-//
-// The functions that use 512-bit vectors are intended for CPUs that support
-// 512-bit vectors *and* where using them doesn't cause significant
-// downclocking. They require the following CPU features:
-//
-// VAES && VPCLMULQDQ && BMI2 && ((AVX512BW && AVX512VL) || AVX10/512)
-//
-// The other functions require the following CPU features:
-//
-// VAES && VPCLMULQDQ && BMI2 && ((AVX512BW && AVX512VL) || AVX10/256)
-//
-// All functions use the "System V" ABI. The Windows ABI is not supported.
-//
-// Note that we use "avx10" in the names of the functions as a shorthand to
-// really mean "AVX10 or a certain set of AVX512 features". Due to Intel's
-// introduction of AVX512 and then its replacement by AVX10, there doesn't seem
-// to be a simple way to name things that makes sense on all CPUs.
-//
-// Note that the macros that support both 256-bit and 512-bit vectors could
-// fairly easily be changed to support 128-bit too. However, this would *not*
-// be sufficient to allow the code to run on CPUs without AVX512 or AVX10,
-// because the code heavily uses several features of these extensions other than
-// the vector length: the increase in the number of SIMD registers from 16 to
-// 32, masking support, and new instructions such as vpternlogd (which can do a
-// three-argument XOR). These features are very useful for AES-GCM.
#include <linux/linkage.h>
@@ -104,16 +70,14 @@
.Lgfpoly_and_internal_carrybit:
.octa 0xc2000000000000010000000000000001
- // The below constants are used for incrementing the counter blocks.
- // ctr_pattern points to the four 128-bit values [0, 1, 2, 3].
- // inc_2blocks and inc_4blocks point to the single 128-bit values 2 and
- // 4. Note that the same '2' is reused in ctr_pattern and inc_2blocks.
+ // Values needed to prepare the initial vector of counter blocks.
.Lctr_pattern:
.octa 0
.octa 1
-.Linc_2blocks:
.octa 2
.octa 3
+
+ // The number of AES blocks per vector, as a 128-bit value.
.Linc_4blocks:
.octa 4
@@ -130,29 +94,13 @@
// Offset to end of hash key powers array in the key struct.
//
// This is immediately followed by three zeroized padding blocks, which are
-// included so that partial vectors can be handled more easily. E.g. if VL=64
-// and two blocks remain, we load the 4 values [H^2, H^1, 0, 0]. The most
-// padding blocks needed is 3, which occurs if [H^1, 0, 0, 0] is loaded.
+// included so that partial vectors can be handled more easily. E.g. if two
+// blocks remain, we load the 4 values [H^2, H^1, 0, 0]. The most padding
+// blocks needed is 3, which occurs if [H^1, 0, 0, 0] is loaded.
#define OFFSETOFEND_H_POWERS (OFFSETOF_H_POWERS + (NUM_H_POWERS * 16))
.text
-// Set the vector length in bytes. This sets the VL variable and defines
-// register aliases V0-V31 that map to the ymm or zmm registers.
-.macro _set_veclen vl
- .set VL, \vl
-.irp i, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, \
- 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
-.if VL == 32
- .set V\i, %ymm\i
-.elseif VL == 64
- .set V\i, %zmm\i
-.else
- .error "Unsupported vector length"
-.endif
-.endr
-.endm
-
// The _ghash_mul_step macro does one step of GHASH multiplication of the
// 128-bit lanes of \a by the corresponding 128-bit lanes of \b and storing the
// reduced products in \dst. \t0, \t1, and \t2 are temporary registers of the
@@ -312,39 +260,44 @@
vpternlogd $0x96, \t0, \mi, \hi
.endm
-// void aes_gcm_precompute_##suffix(struct aes_gcm_key_avx10 *key);
-//
-// Given the expanded AES key |key->aes_key|, this function derives the GHASH
-// subkey and initializes |key->ghash_key_powers| with powers of it.
-//
-// The number of key powers initialized is NUM_H_POWERS, and they are stored in
-// the order H^NUM_H_POWERS to H^1. The zeroized padding blocks after the key
-// powers themselves are also initialized.
+// This is a specialized version of _ghash_mul that computes \a * \a, i.e. it
+// squares \a. It skips computing MI = (a_L * a_H) + (a_H * a_L) = 0.
+.macro _ghash_square a, dst, gfpoly, t0, t1
+ vpclmulqdq $0x00, \a, \a, \t0 // LO = a_L * a_L
+ vpclmulqdq $0x11, \a, \a, \dst // HI = a_H * a_H
+ vpclmulqdq $0x01, \t0, \gfpoly, \t1 // LO_L*(x^63 + x^62 + x^57)
+ vpshufd $0x4e, \t0, \t0 // Swap halves of LO
+ vpxord \t0, \t1, \t1 // Fold LO into MI
+ vpclmulqdq $0x01, \t1, \gfpoly, \t0 // MI_L*(x^63 + x^62 + x^57)
+ vpshufd $0x4e, \t1, \t1 // Swap halves of MI
+ vpternlogd $0x96, \t0, \t1, \dst // Fold MI into HI
+.endm
+
+// void aes_gcm_precompute_vaes_avx512(struct aes_gcm_key_vaes_avx512 *key);
//
-// This macro supports both VL=32 and VL=64. _set_veclen must have been invoked
-// with the desired length. In the VL=32 case, the function computes twice as
-// many key powers than are actually used by the VL=32 GCM update functions.
-// This is done to keep the key format the same regardless of vector length.
-.macro _aes_gcm_precompute
+// Given the expanded AES key |key->base.aes_key|, derive the GHASH subkey and
+// initialize |key->h_powers| and |key->padding|.
+SYM_FUNC_START(aes_gcm_precompute_vaes_avx512)
// Function arguments
.set KEY, %rdi
- // Additional local variables. V0-V2 and %rax are used as temporaries.
+ // Additional local variables.
+ // %zmm[0-2] and %rax are used as temporaries.
.set POWERS_PTR, %rsi
.set RNDKEYLAST_PTR, %rdx
- .set H_CUR, V3
+ .set H_CUR, %zmm3
.set H_CUR_YMM, %ymm3
.set H_CUR_XMM, %xmm3
- .set H_INC, V4
+ .set H_INC, %zmm4
.set H_INC_YMM, %ymm4
.set H_INC_XMM, %xmm4
- .set GFPOLY, V5
+ .set GFPOLY, %zmm5
.set GFPOLY_YMM, %ymm5
.set GFPOLY_XMM, %xmm5
// Get pointer to lowest set of key powers (located at end of array).
- lea OFFSETOFEND_H_POWERS-VL(KEY), POWERS_PTR
+ lea OFFSETOFEND_H_POWERS-64(KEY), POWERS_PTR
// Encrypt an all-zeroes block to get the raw hash subkey.
movl OFFSETOF_AESKEYLEN(KEY), %eax
@@ -363,8 +316,8 @@
// Zeroize the padding blocks.
vpxor %xmm0, %xmm0, %xmm0
- vmovdqu %ymm0, VL(POWERS_PTR)
- vmovdqu %xmm0, VL+2*16(POWERS_PTR)
+ vmovdqu %ymm0, 64(POWERS_PTR)
+ vmovdqu %xmm0, 64+2*16(POWERS_PTR)
// Finish preprocessing the first key power, H^1. Since this GHASH
// implementation operates directly on values with the backwards bit
@@ -397,54 +350,44 @@
// special needs to be done to make this happen, though: H^1 * H^1 would
// end up with two factors of x^-1, but the multiplication consumes one.
// So the product H^2 ends up with the desired one factor of x^-1.
- _ghash_mul H_CUR_XMM, H_CUR_XMM, H_INC_XMM, GFPOLY_XMM, \
- %xmm0, %xmm1, %xmm2
+ _ghash_square H_CUR_XMM, H_INC_XMM, GFPOLY_XMM, %xmm0, %xmm1
// Create H_CUR_YMM = [H^2, H^1] and H_INC_YMM = [H^2, H^2].
vinserti128 $1, H_CUR_XMM, H_INC_YMM, H_CUR_YMM
vinserti128 $1, H_INC_XMM, H_INC_YMM, H_INC_YMM
-.if VL == 64
// Create H_CUR = [H^4, H^3, H^2, H^1] and H_INC = [H^4, H^4, H^4, H^4].
_ghash_mul H_INC_YMM, H_CUR_YMM, H_INC_YMM, GFPOLY_YMM, \
%ymm0, %ymm1, %ymm2
vinserti64x4 $1, H_CUR_YMM, H_INC, H_CUR
vshufi64x2 $0, H_INC, H_INC, H_INC
-.endif
// Store the lowest set of key powers.
vmovdqu8 H_CUR, (POWERS_PTR)
- // Compute and store the remaining key powers. With VL=32, repeatedly
- // multiply [H^(i+1), H^i] by [H^2, H^2] to get [H^(i+3), H^(i+2)].
- // With VL=64, repeatedly multiply [H^(i+3), H^(i+2), H^(i+1), H^i] by
+ // Compute and store the remaining key powers.
+ // Repeatedly multiply [H^(i+3), H^(i+2), H^(i+1), H^i] by
// [H^4, H^4, H^4, H^4] to get [H^(i+7), H^(i+6), H^(i+5), H^(i+4)].
- mov $(NUM_H_POWERS*16/VL) - 1, %eax
-.Lprecompute_next\@:
- sub $VL, POWERS_PTR
- _ghash_mul H_INC, H_CUR, H_CUR, GFPOLY, V0, V1, V2
+ mov $3, %eax
+.Lprecompute_next:
+ sub $64, POWERS_PTR
+ _ghash_mul H_INC, H_CUR, H_CUR, GFPOLY, %zmm0, %zmm1, %zmm2
vmovdqu8 H_CUR, (POWERS_PTR)
dec %eax
- jnz .Lprecompute_next\@
+ jnz .Lprecompute_next
vzeroupper // This is needed after using ymm or zmm registers.
RET
-.endm
+SYM_FUNC_END(aes_gcm_precompute_vaes_avx512)
// XOR together the 128-bit lanes of \src (whose low lane is \src_xmm) and store
// the result in \dst_xmm. This implicitly zeroizes the other lanes of dst.
.macro _horizontal_xor src, src_xmm, dst_xmm, t0_xmm, t1_xmm, t2_xmm
vextracti32x4 $1, \src, \t0_xmm
-.if VL == 32
- vpxord \t0_xmm, \src_xmm, \dst_xmm
-.elseif VL == 64
vextracti32x4 $2, \src, \t1_xmm
vextracti32x4 $3, \src, \t2_xmm
vpxord \t0_xmm, \src_xmm, \dst_xmm
vpternlogd $0x96, \t1_xmm, \t2_xmm, \dst_xmm
-.else
- .error "Unsupported vector length"
-.endif
.endm
// Do one step of the GHASH update of the data blocks given in the vector
@@ -458,25 +401,21 @@
//
// The GHASH update does: GHASH_ACC = H_POW4*(GHASHDATA0 + GHASH_ACC) +
// H_POW3*GHASHDATA1 + H_POW2*GHASHDATA2 + H_POW1*GHASHDATA3, where the
-// operations are vectorized operations on vectors of 16-byte blocks. E.g.,
-// with VL=32 there are 2 blocks per vector and the vectorized terms correspond
-// to the following non-vectorized terms:
-//
-// H_POW4*(GHASHDATA0 + GHASH_ACC) => H^8*(blk0 + GHASH_ACC_XMM) and H^7*(blk1 + 0)
-// H_POW3*GHASHDATA1 => H^6*blk2 and H^5*blk3
-// H_POW2*GHASHDATA2 => H^4*blk4 and H^3*blk5
-// H_POW1*GHASHDATA3 => H^2*blk6 and H^1*blk7
+// operations are vectorized operations on 512-bit vectors of 128-bit blocks.
+// The vectorized terms correspond to the following non-vectorized terms:
//
-// With VL=64, we use 4 blocks/vector, H^16 through H^1, and blk0 through blk15.
+// H_POW4*(GHASHDATA0 + GHASH_ACC) => H^16*(blk0 + GHASH_ACC_XMM),
+// H^15*(blk1 + 0), H^14*(blk2 + 0), and H^13*(blk3 + 0)
+// H_POW3*GHASHDATA1 => H^12*blk4, H^11*blk5, H^10*blk6, and H^9*blk7
+// H_POW2*GHASHDATA2 => H^8*blk8, H^7*blk9, H^6*blk10, and H^5*blk11
+// H_POW1*GHASHDATA3 => H^4*blk12, H^3*blk13, H^2*blk14, and H^1*blk15
//
// More concretely, this code does:
// - Do vectorized "schoolbook" multiplications to compute the intermediate
// 256-bit product of each block and its corresponding hash key power.
-// There are 4*VL/16 of these intermediate products.
-// - Sum (XOR) the intermediate 256-bit products across vectors. This leaves
-// VL/16 256-bit intermediate values.
+// - Sum (XOR) the intermediate 256-bit products across vectors.
// - Do a vectorized reduction of these 256-bit intermediate values to
-// 128-bits each. This leaves VL/16 128-bit intermediate values.
+// 128-bits each.
// - Sum (XOR) these values and store the 128-bit result in GHASH_ACC_XMM.
//
// See _ghash_mul_step for the full explanation of the operations performed for
@@ -532,85 +471,224 @@
.endif
.endm
-// Do one non-last round of AES encryption on the counter blocks in V0-V3 using
-// the round key that has been broadcast to all 128-bit lanes of \round_key.
+// Update GHASH with four vectors of data blocks. See _ghash_step_4x for full
+// explanation.
+.macro _ghash_4x
+.irp i, 0,1,2,3,4,5,6,7,8,9
+ _ghash_step_4x \i
+.endr
+.endm
+
+// void aes_gcm_aad_update_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+// u8 ghash_acc[16],
+// const u8 *aad, int aadlen);
+//
+// This function processes the AAD (Additional Authenticated Data) in GCM.
+// Using the key |key|, it updates the GHASH accumulator |ghash_acc| with the
+// data given by |aad| and |aadlen|. On the first call, |ghash_acc| must be all
+// zeroes. |aadlen| must be a multiple of 16, except on the last call where it
+// can be any length. The caller must do any buffering needed to ensure this.
+//
+// This handles large amounts of AAD efficiently, while also keeping overhead
+// low for small amounts which is the common case. TLS and IPsec use less than
+// one block of AAD, but (uncommonly) other use cases may use much more.
+SYM_FUNC_START(aes_gcm_aad_update_vaes_avx512)
+
+ // Function arguments
+ .set KEY, %rdi
+ .set GHASH_ACC_PTR, %rsi
+ .set AAD, %rdx
+ .set AADLEN, %ecx
+ .set AADLEN64, %rcx // Zero-extend AADLEN before using!
+
+ // Additional local variables.
+ // %rax and %k1 are used as temporary registers.
+ .set GHASHDATA0, %zmm0
+ .set GHASHDATA0_XMM, %xmm0
+ .set GHASHDATA1, %zmm1
+ .set GHASHDATA1_XMM, %xmm1
+ .set GHASHDATA2, %zmm2
+ .set GHASHDATA2_XMM, %xmm2
+ .set GHASHDATA3, %zmm3
+ .set BSWAP_MASK, %zmm4
+ .set BSWAP_MASK_XMM, %xmm4
+ .set GHASH_ACC, %zmm5
+ .set GHASH_ACC_XMM, %xmm5
+ .set H_POW4, %zmm6
+ .set H_POW3, %zmm7
+ .set H_POW2, %zmm8
+ .set H_POW1, %zmm9
+ .set H_POW1_XMM, %xmm9
+ .set GFPOLY, %zmm10
+ .set GFPOLY_XMM, %xmm10
+ .set GHASHTMP0, %zmm11
+ .set GHASHTMP1, %zmm12
+ .set GHASHTMP2, %zmm13
+
+ // Load the GHASH accumulator.
+ vmovdqu (GHASH_ACC_PTR), GHASH_ACC_XMM
+
+ // Check for the common case of AADLEN <= 16, as well as AADLEN == 0.
+ cmp $16, AADLEN
+ jg .Laad_more_than_16bytes
+ test AADLEN, AADLEN
+ jz .Laad_done
+
+ // Fast path: update GHASH with 1 <= AADLEN <= 16 bytes of AAD.
+ vmovdqu .Lbswap_mask(%rip), BSWAP_MASK_XMM
+ vmovdqu .Lgfpoly(%rip), GFPOLY_XMM
+ mov $-1, %eax
+ bzhi AADLEN, %eax, %eax
+ kmovd %eax, %k1
+ vmovdqu8 (AAD), GHASHDATA0_XMM{%k1}{z}
+ vmovdqu OFFSETOFEND_H_POWERS-16(KEY), H_POW1_XMM
+ vpshufb BSWAP_MASK_XMM, GHASHDATA0_XMM, GHASHDATA0_XMM
+ vpxor GHASHDATA0_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
+ _ghash_mul H_POW1_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM, GFPOLY_XMM, \
+ GHASHDATA0_XMM, GHASHDATA1_XMM, GHASHDATA2_XMM
+ jmp .Laad_done
+
+.Laad_more_than_16bytes:
+ vbroadcasti32x4 .Lbswap_mask(%rip), BSWAP_MASK
+ vbroadcasti32x4 .Lgfpoly(%rip), GFPOLY
+
+ // If AADLEN >= 256, update GHASH with 256 bytes of AAD at a time.
+ sub $256, AADLEN
+ jl .Laad_loop_4x_done
+ vmovdqu8 OFFSETOFEND_H_POWERS-4*64(KEY), H_POW4
+ vmovdqu8 OFFSETOFEND_H_POWERS-3*64(KEY), H_POW3
+ vmovdqu8 OFFSETOFEND_H_POWERS-2*64(KEY), H_POW2
+ vmovdqu8 OFFSETOFEND_H_POWERS-1*64(KEY), H_POW1
+.Laad_loop_4x:
+ vmovdqu8 0*64(AAD), GHASHDATA0
+ vmovdqu8 1*64(AAD), GHASHDATA1
+ vmovdqu8 2*64(AAD), GHASHDATA2
+ vmovdqu8 3*64(AAD), GHASHDATA3
+ _ghash_4x
+ add $256, AAD
+ sub $256, AADLEN
+ jge .Laad_loop_4x
+.Laad_loop_4x_done:
+
+ // If AADLEN >= 64, update GHASH with 64 bytes of AAD at a time.
+ add $192, AADLEN
+ jl .Laad_loop_1x_done
+ vmovdqu8 OFFSETOFEND_H_POWERS-1*64(KEY), H_POW1
+.Laad_loop_1x:
+ vmovdqu8 (AAD), GHASHDATA0
+ vpshufb BSWAP_MASK, GHASHDATA0, GHASHDATA0
+ vpxord GHASHDATA0, GHASH_ACC, GHASH_ACC
+ _ghash_mul H_POW1, GHASH_ACC, GHASH_ACC, GFPOLY, \
+ GHASHDATA0, GHASHDATA1, GHASHDATA2
+ _horizontal_xor GHASH_ACC, GHASH_ACC_XMM, GHASH_ACC_XMM, \
+ GHASHDATA0_XMM, GHASHDATA1_XMM, GHASHDATA2_XMM
+ add $64, AAD
+ sub $64, AADLEN
+ jge .Laad_loop_1x
+.Laad_loop_1x_done:
+
+ // Update GHASH with the remaining 0 <= AADLEN < 64 bytes of AAD.
+ add $64, AADLEN
+ jz .Laad_done
+ mov $-1, %rax
+ bzhi AADLEN64, %rax, %rax
+ kmovq %rax, %k1
+ vmovdqu8 (AAD), GHASHDATA0{%k1}{z}
+ neg AADLEN64
+ and $~15, AADLEN64 // -round_up(AADLEN, 16)
+ vmovdqu8 OFFSETOFEND_H_POWERS(KEY,AADLEN64), H_POW1
+ vpshufb BSWAP_MASK, GHASHDATA0, GHASHDATA0
+ vpxord GHASHDATA0, GHASH_ACC, GHASH_ACC
+ _ghash_mul H_POW1, GHASH_ACC, GHASH_ACC, GFPOLY, \
+ GHASHDATA0, GHASHDATA1, GHASHDATA2
+ _horizontal_xor GHASH_ACC, GHASH_ACC_XMM, GHASH_ACC_XMM, \
+ GHASHDATA0_XMM, GHASHDATA1_XMM, GHASHDATA2_XMM
+
+.Laad_done:
+ // Store the updated GHASH accumulator back to memory.
+ vmovdqu GHASH_ACC_XMM, (GHASH_ACC_PTR)
+
+ vzeroupper // This is needed after using ymm or zmm registers.
+ RET
+SYM_FUNC_END(aes_gcm_aad_update_vaes_avx512)
+
+// Do one non-last round of AES encryption on the blocks in %zmm[0-3] using the
+// round key that has been broadcast to all 128-bit lanes of \round_key.
.macro _vaesenc_4x round_key
- vaesenc \round_key, V0, V0
- vaesenc \round_key, V1, V1
- vaesenc \round_key, V2, V2
- vaesenc \round_key, V3, V3
+ vaesenc \round_key, %zmm0, %zmm0
+ vaesenc \round_key, %zmm1, %zmm1
+ vaesenc \round_key, %zmm2, %zmm2
+ vaesenc \round_key, %zmm3, %zmm3
.endm
// Start the AES encryption of four vectors of counter blocks.
.macro _ctr_begin_4x
// Increment LE_CTR four times to generate four vectors of little-endian
- // counter blocks, swap each to big-endian, and store them in V0-V3.
- vpshufb BSWAP_MASK, LE_CTR, V0
+ // counter blocks, swap each to big-endian, and store them in %zmm[0-3].
+ vpshufb BSWAP_MASK, LE_CTR, %zmm0
vpaddd LE_CTR_INC, LE_CTR, LE_CTR
- vpshufb BSWAP_MASK, LE_CTR, V1
+ vpshufb BSWAP_MASK, LE_CTR, %zmm1
vpaddd LE_CTR_INC, LE_CTR, LE_CTR
- vpshufb BSWAP_MASK, LE_CTR, V2
+ vpshufb BSWAP_MASK, LE_CTR, %zmm2
vpaddd LE_CTR_INC, LE_CTR, LE_CTR
- vpshufb BSWAP_MASK, LE_CTR, V3
+ vpshufb BSWAP_MASK, LE_CTR, %zmm3
vpaddd LE_CTR_INC, LE_CTR, LE_CTR
// AES "round zero": XOR in the zero-th round key.
- vpxord RNDKEY0, V0, V0
- vpxord RNDKEY0, V1, V1
- vpxord RNDKEY0, V2, V2
- vpxord RNDKEY0, V3, V3
+ vpxord RNDKEY0, %zmm0, %zmm0
+ vpxord RNDKEY0, %zmm1, %zmm1
+ vpxord RNDKEY0, %zmm2, %zmm2
+ vpxord RNDKEY0, %zmm3, %zmm3
.endm
-// Do the last AES round for four vectors of counter blocks V0-V3, XOR source
-// data with the resulting keystream, and write the result to DST and
+// Do the last AES round for four vectors of counter blocks %zmm[0-3], XOR
+// source data with the resulting keystream, and write the result to DST and
// GHASHDATA[0-3]. (Implementation differs slightly, but has the same effect.)
.macro _aesenclast_and_xor_4x
// XOR the source data with the last round key, saving the result in
// GHASHDATA[0-3]. This reduces latency by taking advantage of the
// property vaesenclast(key, a) ^ b == vaesenclast(key ^ b, a).
- vpxord 0*VL(SRC), RNDKEYLAST, GHASHDATA0
- vpxord 1*VL(SRC), RNDKEYLAST, GHASHDATA1
- vpxord 2*VL(SRC), RNDKEYLAST, GHASHDATA2
- vpxord 3*VL(SRC), RNDKEYLAST, GHASHDATA3
+ vpxord 0*64(SRC), RNDKEYLAST, GHASHDATA0
+ vpxord 1*64(SRC), RNDKEYLAST, GHASHDATA1
+ vpxord 2*64(SRC), RNDKEYLAST, GHASHDATA2
+ vpxord 3*64(SRC), RNDKEYLAST, GHASHDATA3
// Do the last AES round. This handles the XOR with the source data
// too, as per the optimization described above.
- vaesenclast GHASHDATA0, V0, GHASHDATA0
- vaesenclast GHASHDATA1, V1, GHASHDATA1
- vaesenclast GHASHDATA2, V2, GHASHDATA2
- vaesenclast GHASHDATA3, V3, GHASHDATA3
+ vaesenclast GHASHDATA0, %zmm0, GHASHDATA0
+ vaesenclast GHASHDATA1, %zmm1, GHASHDATA1
+ vaesenclast GHASHDATA2, %zmm2, GHASHDATA2
+ vaesenclast GHASHDATA3, %zmm3, GHASHDATA3
// Store the en/decrypted data to DST.
- vmovdqu8 GHASHDATA0, 0*VL(DST)
- vmovdqu8 GHASHDATA1, 1*VL(DST)
- vmovdqu8 GHASHDATA2, 2*VL(DST)
- vmovdqu8 GHASHDATA3, 3*VL(DST)
+ vmovdqu8 GHASHDATA0, 0*64(DST)
+ vmovdqu8 GHASHDATA1, 1*64(DST)
+ vmovdqu8 GHASHDATA2, 2*64(DST)
+ vmovdqu8 GHASHDATA3, 3*64(DST)
.endm
-// void aes_gcm_{enc,dec}_update_##suffix(const struct aes_gcm_key_avx10 *key,
-// const u32 le_ctr[4], u8 ghash_acc[16],
-// const u8 *src, u8 *dst, int datalen);
+// void aes_gcm_{enc,dec}_update_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+// const u32 le_ctr[4], u8 ghash_acc[16],
+// const u8 *src, u8 *dst, int datalen);
//
// This macro generates a GCM encryption or decryption update function with the
-// above prototype (with \enc selecting which one). This macro supports both
-// VL=32 and VL=64. _set_veclen must have been invoked with the desired length.
-//
-// This function computes the next portion of the CTR keystream, XOR's it with
-// |datalen| bytes from |src|, and writes the resulting encrypted or decrypted
-// data to |dst|. It also updates the GHASH accumulator |ghash_acc| using the
-// next |datalen| ciphertext bytes.
+// above prototype (with \enc selecting which one). The function computes the
+// next portion of the CTR keystream, XOR's it with |datalen| bytes from |src|,
+// and writes the resulting encrypted or decrypted data to |dst|. It also
+// updates the GHASH accumulator |ghash_acc| using the next |datalen| ciphertext
+// bytes.
//
// |datalen| must be a multiple of 16, except on the last call where it can be
// any length. The caller must do any buffering needed to ensure this. Both
// in-place and out-of-place en/decryption are supported.
//
-// |le_ctr| must give the current counter in little-endian format. For a new
-// message, the low word of the counter must be 2. This function loads the
-// counter from |le_ctr| and increments the loaded counter as needed, but it
-// does *not* store the updated counter back to |le_ctr|. The caller must
-// update |le_ctr| if any more data segments follow. Internally, only the low
-// 32-bit word of the counter is incremented, following the GCM standard.
+// |le_ctr| must give the current counter in little-endian format. This
+// function loads the counter from |le_ctr| and increments the loaded counter as
+// needed, but it does *not* store the updated counter back to |le_ctr|. The
+// caller must update |le_ctr| if any more data segments follow. Internally,
+// only the low 32-bit word of the counter is incremented, following the GCM
+// standard.
.macro _aes_gcm_update enc
// Function arguments
@@ -634,69 +712,69 @@
// Pointer to the last AES round key for the chosen AES variant
.set RNDKEYLAST_PTR, %r11
- // In the main loop, V0-V3 are used as AES input and output. Elsewhere
- // they are used as temporary registers.
+ // In the main loop, %zmm[0-3] are used as AES input and output.
+ // Elsewhere they are used as temporary registers.
// GHASHDATA[0-3] hold the ciphertext blocks and GHASH input data.
- .set GHASHDATA0, V4
+ .set GHASHDATA0, %zmm4
.set GHASHDATA0_XMM, %xmm4
- .set GHASHDATA1, V5
+ .set GHASHDATA1, %zmm5
.set GHASHDATA1_XMM, %xmm5
- .set GHASHDATA2, V6
+ .set GHASHDATA2, %zmm6
.set GHASHDATA2_XMM, %xmm6
- .set GHASHDATA3, V7
+ .set GHASHDATA3, %zmm7
// BSWAP_MASK is the shuffle mask for byte-reflecting 128-bit values
// using vpshufb, copied to all 128-bit lanes.
- .set BSWAP_MASK, V8
+ .set BSWAP_MASK, %zmm8
// RNDKEY temporarily holds the next AES round key.
- .set RNDKEY, V9
+ .set RNDKEY, %zmm9
// GHASH_ACC is the accumulator variable for GHASH. When fully reduced,
// only the lowest 128-bit lane can be nonzero. When not fully reduced,
// more than one lane may be used, and they need to be XOR'd together.
- .set GHASH_ACC, V10
+ .set GHASH_ACC, %zmm10
.set GHASH_ACC_XMM, %xmm10
// LE_CTR_INC is the vector of 32-bit words that need to be added to a
// vector of little-endian counter blocks to advance it forwards.
- .set LE_CTR_INC, V11
+ .set LE_CTR_INC, %zmm11
// LE_CTR contains the next set of little-endian counter blocks.
- .set LE_CTR, V12
+ .set LE_CTR, %zmm12
// RNDKEY0, RNDKEYLAST, and RNDKEY_M[9-1] contain cached AES round keys,
// copied to all 128-bit lanes. RNDKEY0 is the zero-th round key,
// RNDKEYLAST the last, and RNDKEY_M\i the one \i-th from the last.
- .set RNDKEY0, V13
- .set RNDKEYLAST, V14
- .set RNDKEY_M9, V15
- .set RNDKEY_M8, V16
- .set RNDKEY_M7, V17
- .set RNDKEY_M6, V18
- .set RNDKEY_M5, V19
- .set RNDKEY_M4, V20
- .set RNDKEY_M3, V21
- .set RNDKEY_M2, V22
- .set RNDKEY_M1, V23
+ .set RNDKEY0, %zmm13
+ .set RNDKEYLAST, %zmm14
+ .set RNDKEY_M9, %zmm15
+ .set RNDKEY_M8, %zmm16
+ .set RNDKEY_M7, %zmm17
+ .set RNDKEY_M6, %zmm18
+ .set RNDKEY_M5, %zmm19
+ .set RNDKEY_M4, %zmm20
+ .set RNDKEY_M3, %zmm21
+ .set RNDKEY_M2, %zmm22
+ .set RNDKEY_M1, %zmm23
// GHASHTMP[0-2] are temporary variables used by _ghash_step_4x. These
// cannot coincide with anything used for AES encryption, since for
// performance reasons GHASH and AES encryption are interleaved.
- .set GHASHTMP0, V24
- .set GHASHTMP1, V25
- .set GHASHTMP2, V26
+ .set GHASHTMP0, %zmm24
+ .set GHASHTMP1, %zmm25
+ .set GHASHTMP2, %zmm26
- // H_POW[4-1] contain the powers of the hash key H^(4*VL/16)...H^1. The
+ // H_POW[4-1] contain the powers of the hash key H^16...H^1. The
// descending numbering reflects the order of the key powers.
- .set H_POW4, V27
- .set H_POW3, V28
- .set H_POW2, V29
- .set H_POW1, V30
+ .set H_POW4, %zmm27
+ .set H_POW3, %zmm28
+ .set H_POW2, %zmm29
+ .set H_POW1, %zmm30
// GFPOLY contains the .Lgfpoly constant, copied to all 128-bit lanes.
- .set GFPOLY, V31
+ .set GFPOLY, %zmm31
// Load some constants.
vbroadcasti32x4 .Lbswap_mask(%rip), BSWAP_MASK
@@ -719,29 +797,23 @@
// Finish initializing LE_CTR by adding [0, 1, ...] to its low words.
vpaddd .Lctr_pattern(%rip), LE_CTR, LE_CTR
- // Initialize LE_CTR_INC to contain VL/16 in all 128-bit lanes.
-.if VL == 32
- vbroadcasti32x4 .Linc_2blocks(%rip), LE_CTR_INC
-.elseif VL == 64
+ // Load 4 into all 128-bit lanes of LE_CTR_INC.
vbroadcasti32x4 .Linc_4blocks(%rip), LE_CTR_INC
-.else
- .error "Unsupported vector length"
-.endif
- // If there are at least 4*VL bytes of data, then continue into the loop
- // that processes 4*VL bytes of data at a time. Otherwise skip it.
+ // If there are at least 256 bytes of data, then continue into the loop
+ // that processes 256 bytes of data at a time. Otherwise skip it.
//
- // Pre-subtracting 4*VL from DATALEN saves an instruction from the main
+ // Pre-subtracting 256 from DATALEN saves an instruction from the main
// loop and also ensures that at least one write always occurs to
// DATALEN, zero-extending it and allowing DATALEN64 to be used later.
- add $-4*VL, DATALEN // shorter than 'sub 4*VL' when VL=32
+ sub $256, DATALEN
jl .Lcrypt_loop_4x_done\@
// Load powers of the hash key.
- vmovdqu8 OFFSETOFEND_H_POWERS-4*VL(KEY), H_POW4
- vmovdqu8 OFFSETOFEND_H_POWERS-3*VL(KEY), H_POW3
- vmovdqu8 OFFSETOFEND_H_POWERS-2*VL(KEY), H_POW2
- vmovdqu8 OFFSETOFEND_H_POWERS-1*VL(KEY), H_POW1
+ vmovdqu8 OFFSETOFEND_H_POWERS-4*64(KEY), H_POW4
+ vmovdqu8 OFFSETOFEND_H_POWERS-3*64(KEY), H_POW3
+ vmovdqu8 OFFSETOFEND_H_POWERS-2*64(KEY), H_POW2
+ vmovdqu8 OFFSETOFEND_H_POWERS-1*64(KEY), H_POW1
// Main loop: en/decrypt and hash 4 vectors at a time.
//
@@ -770,9 +842,9 @@
cmp %rax, RNDKEYLAST_PTR
jne 1b
_aesenclast_and_xor_4x
- sub $-4*VL, SRC // shorter than 'add 4*VL' when VL=32
- sub $-4*VL, DST
- add $-4*VL, DATALEN
+ add $256, SRC
+ add $256, DST
+ sub $256, DATALEN
jl .Lghash_last_ciphertext_4x\@
.endif
@@ -786,10 +858,10 @@
// If decrypting, load more ciphertext blocks into GHASHDATA[0-3]. If
// encrypting, GHASHDATA[0-3] already contain the previous ciphertext.
.if !\enc
- vmovdqu8 0*VL(SRC), GHASHDATA0
- vmovdqu8 1*VL(SRC), GHASHDATA1
- vmovdqu8 2*VL(SRC), GHASHDATA2
- vmovdqu8 3*VL(SRC), GHASHDATA3
+ vmovdqu8 0*64(SRC), GHASHDATA0
+ vmovdqu8 1*64(SRC), GHASHDATA1
+ vmovdqu8 2*64(SRC), GHASHDATA2
+ vmovdqu8 3*64(SRC), GHASHDATA3
.endif
// Start the AES encryption of the counter blocks.
@@ -809,44 +881,44 @@
_vaesenc_4x RNDKEY
128:
- // Finish the AES encryption of the counter blocks in V0-V3, interleaved
- // with the GHASH update of the ciphertext blocks in GHASHDATA[0-3].
+ // Finish the AES encryption of the counter blocks in %zmm[0-3],
+ // interleaved with the GHASH update of the ciphertext blocks in
+ // GHASHDATA[0-3].
.irp i, 9,8,7,6,5,4,3,2,1
_ghash_step_4x (9 - \i)
_vaesenc_4x RNDKEY_M\i
.endr
_ghash_step_4x 9
_aesenclast_and_xor_4x
- sub $-4*VL, SRC // shorter than 'add 4*VL' when VL=32
- sub $-4*VL, DST
- add $-4*VL, DATALEN
+ add $256, SRC
+ add $256, DST
+ sub $256, DATALEN
jge .Lcrypt_loop_4x\@
.if \enc
.Lghash_last_ciphertext_4x\@:
// Update GHASH with the last set of ciphertext blocks.
-.irp i, 0,1,2,3,4,5,6,7,8,9
- _ghash_step_4x \i
-.endr
+ _ghash_4x
.endif
.Lcrypt_loop_4x_done\@:
- // Undo the extra subtraction by 4*VL and check whether data remains.
- sub $-4*VL, DATALEN // shorter than 'add 4*VL' when VL=32
+ // Undo the extra subtraction by 256 and check whether data remains.
+ add $256, DATALEN
jz .Ldone\@
- // The data length isn't a multiple of 4*VL. Process the remaining data
- // of length 1 <= DATALEN < 4*VL, up to one vector (VL bytes) at a time.
- // Going one vector at a time may seem inefficient compared to having
- // separate code paths for each possible number of vectors remaining.
- // However, using a loop keeps the code size down, and it performs
- // surprising well; modern CPUs will start executing the next iteration
- // before the previous one finishes and also predict the number of loop
- // iterations. For a similar reason, we roll up the AES rounds.
+ // The data length isn't a multiple of 256 bytes. Process the remaining
+ // data of length 1 <= DATALEN < 256, up to one 64-byte vector at a
+ // time. Going one vector at a time may seem inefficient compared to
+ // having separate code paths for each possible number of vectors
+ // remaining. However, using a loop keeps the code size down, and it
+ // performs surprising well; modern CPUs will start executing the next
+ // iteration before the previous one finishes and also predict the
+ // number of loop iterations. For a similar reason, we roll up the AES
+ // rounds.
//
- // On the last iteration, the remaining length may be less than VL.
- // Handle this using masking.
+ // On the last iteration, the remaining length may be less than 64
+ // bytes. Handle this using masking.
//
// Since there are enough key powers available for all remaining data,
// there is no need to do a GHASH reduction after each iteration.
@@ -875,65 +947,60 @@
.Lcrypt_loop_1x\@:
// Select the appropriate mask for this iteration: all 1's if
- // DATALEN >= VL, otherwise DATALEN 1's. Do this branchlessly using the
+ // DATALEN >= 64, otherwise DATALEN 1's. Do this branchlessly using the
// bzhi instruction from BMI2. (This relies on DATALEN <= 255.)
-.if VL < 64
- mov $-1, %eax
- bzhi DATALEN, %eax, %eax
- kmovd %eax, %k1
-.else
mov $-1, %rax
bzhi DATALEN64, %rax, %rax
kmovq %rax, %k1
-.endif
// Encrypt a vector of counter blocks. This does not need to be masked.
- vpshufb BSWAP_MASK, LE_CTR, V0
+ vpshufb BSWAP_MASK, LE_CTR, %zmm0
vpaddd LE_CTR_INC, LE_CTR, LE_CTR
- vpxord RNDKEY0, V0, V0
+ vpxord RNDKEY0, %zmm0, %zmm0
lea 16(KEY), %rax
1:
vbroadcasti32x4 (%rax), RNDKEY
- vaesenc RNDKEY, V0, V0
+ vaesenc RNDKEY, %zmm0, %zmm0
add $16, %rax
cmp %rax, RNDKEYLAST_PTR
jne 1b
- vaesenclast RNDKEYLAST, V0, V0
+ vaesenclast RNDKEYLAST, %zmm0, %zmm0
// XOR the data with the appropriate number of keystream bytes.
- vmovdqu8 (SRC), V1{%k1}{z}
- vpxord V1, V0, V0
- vmovdqu8 V0, (DST){%k1}
+ vmovdqu8 (SRC), %zmm1{%k1}{z}
+ vpxord %zmm1, %zmm0, %zmm0
+ vmovdqu8 %zmm0, (DST){%k1}
// Update GHASH with the ciphertext block(s), without reducing.
//
- // In the case of DATALEN < VL, the ciphertext is zero-padded to VL.
- // (If decrypting, it's done by the above masked load. If encrypting,
- // it's done by the below masked register-to-register move.) Note that
- // if DATALEN <= VL - 16, there will be additional padding beyond the
- // padding of the last block specified by GHASH itself; i.e., there may
- // be whole block(s) that get processed by the GHASH multiplication and
- // reduction instructions but should not actually be included in the
+ // In the case of DATALEN < 64, the ciphertext is zero-padded to 64
+ // bytes. (If decrypting, it's done by the above masked load. If
+ // encrypting, it's done by the below masked register-to-register move.)
+ // Note that if DATALEN <= 48, there will be additional padding beyond
+ // the padding of the last block specified by GHASH itself; i.e., there
+ // may be whole block(s) that get processed by the GHASH multiplication
+ // and reduction instructions but should not actually be included in the
// GHASH. However, any such blocks are all-zeroes, and the values that
// they're multiplied with are also all-zeroes. Therefore they just add
// 0 * 0 = 0 to the final GHASH result, which makes no difference.
vmovdqu8 (POWERS_PTR), H_POW1
.if \enc
- vmovdqu8 V0, V1{%k1}{z}
+ vmovdqu8 %zmm0, %zmm1{%k1}{z}
.endif
- vpshufb BSWAP_MASK, V1, V0
- vpxord GHASH_ACC, V0, V0
- _ghash_mul_noreduce H_POW1, V0, LO, MI, HI, GHASHDATA3, V1, V2, V3
+ vpshufb BSWAP_MASK, %zmm1, %zmm0
+ vpxord GHASH_ACC, %zmm0, %zmm0
+ _ghash_mul_noreduce H_POW1, %zmm0, LO, MI, HI, \
+ GHASHDATA3, %zmm1, %zmm2, %zmm3
vpxor GHASH_ACC_XMM, GHASH_ACC_XMM, GHASH_ACC_XMM
- add $VL, POWERS_PTR
- add $VL, SRC
- add $VL, DST
- sub $VL, DATALEN
+ add $64, POWERS_PTR
+ add $64, SRC
+ add $64, DST
+ sub $64, DATALEN
jg .Lcrypt_loop_1x\@
// Finally, do the GHASH reduction.
- _ghash_reduce LO, MI, HI, GFPOLY, V0
+ _ghash_reduce LO, MI, HI, GFPOLY, %zmm0
_horizontal_xor HI, HI_XMM, GHASH_ACC_XMM, %xmm0, %xmm1, %xmm2
.Ldone\@:
@@ -944,14 +1011,14 @@
RET
.endm
-// void aes_gcm_enc_final_vaes_avx10(const struct aes_gcm_key_avx10 *key,
-// const u32 le_ctr[4], u8 ghash_acc[16],
-// u64 total_aadlen, u64 total_datalen);
-// bool aes_gcm_dec_final_vaes_avx10(const struct aes_gcm_key_avx10 *key,
-// const u32 le_ctr[4],
-// const u8 ghash_acc[16],
-// u64 total_aadlen, u64 total_datalen,
-// const u8 tag[16], int taglen);
+// void aes_gcm_enc_final_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+// const u32 le_ctr[4], u8 ghash_acc[16],
+// u64 total_aadlen, u64 total_datalen);
+// bool aes_gcm_dec_final_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+// const u32 le_ctr[4],
+// const u8 ghash_acc[16],
+// u64 total_aadlen, u64 total_datalen,
+// const u8 tag[16], int taglen);
//
// This macro generates one of the above two functions (with \enc selecting
// which one). Both functions finish computing the GCM authentication tag by
@@ -1081,119 +1148,16 @@
RET
.endm
-_set_veclen 32
-SYM_FUNC_START(aes_gcm_precompute_vaes_avx10_256)
- _aes_gcm_precompute
-SYM_FUNC_END(aes_gcm_precompute_vaes_avx10_256)
-SYM_FUNC_START(aes_gcm_enc_update_vaes_avx10_256)
- _aes_gcm_update 1
-SYM_FUNC_END(aes_gcm_enc_update_vaes_avx10_256)
-SYM_FUNC_START(aes_gcm_dec_update_vaes_avx10_256)
- _aes_gcm_update 0
-SYM_FUNC_END(aes_gcm_dec_update_vaes_avx10_256)
-
-_set_veclen 64
-SYM_FUNC_START(aes_gcm_precompute_vaes_avx10_512)
- _aes_gcm_precompute
-SYM_FUNC_END(aes_gcm_precompute_vaes_avx10_512)
-SYM_FUNC_START(aes_gcm_enc_update_vaes_avx10_512)
+SYM_FUNC_START(aes_gcm_enc_update_vaes_avx512)
_aes_gcm_update 1
-SYM_FUNC_END(aes_gcm_enc_update_vaes_avx10_512)
-SYM_FUNC_START(aes_gcm_dec_update_vaes_avx10_512)
+SYM_FUNC_END(aes_gcm_enc_update_vaes_avx512)
+SYM_FUNC_START(aes_gcm_dec_update_vaes_avx512)
_aes_gcm_update 0
-SYM_FUNC_END(aes_gcm_dec_update_vaes_avx10_512)
-
-// void aes_gcm_aad_update_vaes_avx10(const struct aes_gcm_key_avx10 *key,
-// u8 ghash_acc[16],
-// const u8 *aad, int aadlen);
-//
-// This function processes the AAD (Additional Authenticated Data) in GCM.
-// Using the key |key|, it updates the GHASH accumulator |ghash_acc| with the
-// data given by |aad| and |aadlen|. |key->ghash_key_powers| must have been
-// initialized. On the first call, |ghash_acc| must be all zeroes. |aadlen|
-// must be a multiple of 16, except on the last call where it can be any length.
-// The caller must do any buffering needed to ensure this.
-//
-// AES-GCM is almost always used with small amounts of AAD, less than 32 bytes.
-// Therefore, for AAD processing we currently only provide this implementation
-// which uses 256-bit vectors (ymm registers) and only has a 1x-wide loop. This
-// keeps the code size down, and it enables some micro-optimizations, e.g. using
-// VEX-coded instructions instead of EVEX-coded to save some instruction bytes.
-// To optimize for large amounts of AAD, we could implement a 4x-wide loop and
-// provide a version using 512-bit vectors, but that doesn't seem to be useful.
-SYM_FUNC_START(aes_gcm_aad_update_vaes_avx10)
-
- // Function arguments
- .set KEY, %rdi
- .set GHASH_ACC_PTR, %rsi
- .set AAD, %rdx
- .set AADLEN, %ecx
- .set AADLEN64, %rcx // Zero-extend AADLEN before using!
-
- // Additional local variables.
- // %rax, %ymm0-%ymm3, and %k1 are used as temporary registers.
- .set BSWAP_MASK, %ymm4
- .set GFPOLY, %ymm5
- .set GHASH_ACC, %ymm6
- .set GHASH_ACC_XMM, %xmm6
- .set H_POW1, %ymm7
-
- // Load some constants.
- vbroadcasti128 .Lbswap_mask(%rip), BSWAP_MASK
- vbroadcasti128 .Lgfpoly(%rip), GFPOLY
-
- // Load the GHASH accumulator.
- vmovdqu (GHASH_ACC_PTR), GHASH_ACC_XMM
-
- // Update GHASH with 32 bytes of AAD at a time.
- //
- // Pre-subtracting 32 from AADLEN saves an instruction from the loop and
- // also ensures that at least one write always occurs to AADLEN,
- // zero-extending it and allowing AADLEN64 to be used later.
- sub $32, AADLEN
- jl .Laad_loop_1x_done
- vmovdqu8 OFFSETOFEND_H_POWERS-32(KEY), H_POW1 // [H^2, H^1]
-.Laad_loop_1x:
- vmovdqu (AAD), %ymm0
- vpshufb BSWAP_MASK, %ymm0, %ymm0
- vpxor %ymm0, GHASH_ACC, GHASH_ACC
- _ghash_mul H_POW1, GHASH_ACC, GHASH_ACC, GFPOLY, \
- %ymm0, %ymm1, %ymm2
- vextracti128 $1, GHASH_ACC, %xmm0
- vpxor %xmm0, GHASH_ACC_XMM, GHASH_ACC_XMM
- add $32, AAD
- sub $32, AADLEN
- jge .Laad_loop_1x
-.Laad_loop_1x_done:
- add $32, AADLEN
- jz .Laad_done
-
- // Update GHASH with the remaining 1 <= AADLEN < 32 bytes of AAD.
- mov $-1, %eax
- bzhi AADLEN, %eax, %eax
- kmovd %eax, %k1
- vmovdqu8 (AAD), %ymm0{%k1}{z}
- neg AADLEN64
- and $~15, AADLEN64 // -round_up(AADLEN, 16)
- vmovdqu8 OFFSETOFEND_H_POWERS(KEY,AADLEN64), H_POW1
- vpshufb BSWAP_MASK, %ymm0, %ymm0
- vpxor %ymm0, GHASH_ACC, GHASH_ACC
- _ghash_mul H_POW1, GHASH_ACC, GHASH_ACC, GFPOLY, \
- %ymm0, %ymm1, %ymm2
- vextracti128 $1, GHASH_ACC, %xmm0
- vpxor %xmm0, GHASH_ACC_XMM, GHASH_ACC_XMM
-
-.Laad_done:
- // Store the updated GHASH accumulator back to memory.
- vmovdqu GHASH_ACC_XMM, (GHASH_ACC_PTR)
-
- vzeroupper // This is needed after using ymm or zmm registers.
- RET
-SYM_FUNC_END(aes_gcm_aad_update_vaes_avx10)
+SYM_FUNC_END(aes_gcm_dec_update_vaes_avx512)
-SYM_FUNC_START(aes_gcm_enc_final_vaes_avx10)
+SYM_FUNC_START(aes_gcm_enc_final_vaes_avx512)
_aes_gcm_final 1
-SYM_FUNC_END(aes_gcm_enc_final_vaes_avx10)
-SYM_FUNC_START(aes_gcm_dec_final_vaes_avx10)
+SYM_FUNC_END(aes_gcm_enc_final_vaes_avx512)
+SYM_FUNC_START(aes_gcm_dec_final_vaes_avx512)
_aes_gcm_final 0
-SYM_FUNC_END(aes_gcm_dec_final_vaes_avx10)
+SYM_FUNC_END(aes_gcm_dec_final_vaes_avx512)
diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c
index d953ac470aae..48405e02d6e4 100644
--- a/arch/x86/crypto/aesni-intel_glue.c
+++ b/arch/x86/crypto/aesni-intel_glue.c
@@ -693,8 +693,7 @@ ctr_crypt(struct skcipher_request *req,
* operation into two at the point where the overflow
* will occur. After the first part, add the carry bit.
*/
- p1_nbytes = min_t(unsigned int, nbytes,
- (nblocks - ctr64) * AES_BLOCK_SIZE);
+ p1_nbytes = min(nbytes, (nblocks - ctr64) * AES_BLOCK_SIZE);
(*ctr64_func)(key, walk.src.virt.addr,
walk.dst.virt.addr, p1_nbytes, le_ctr);
le_ctr[0] = 0;
@@ -874,8 +873,38 @@ struct aes_gcm_key_aesni {
#define AES_GCM_KEY_AESNI_SIZE \
(sizeof(struct aes_gcm_key_aesni) + (15 & ~(CRYPTO_MINALIGN - 1)))
-/* Key struct used by the VAES + AVX10 implementations of AES-GCM */
-struct aes_gcm_key_avx10 {
+/* Key struct used by the VAES + AVX2 implementation of AES-GCM */
+struct aes_gcm_key_vaes_avx2 {
+ /*
+ * Common part of the key. The assembly code prefers 16-byte alignment
+ * for the round keys; we get this by them being located at the start of
+ * the struct and the whole struct being 32-byte aligned.
+ */
+ struct aes_gcm_key base;
+
+ /*
+ * Powers of the hash key H^8 through H^1. These are 128-bit values.
+ * They all have an extra factor of x^-1 and are byte-reversed.
+ * The assembly code prefers 32-byte alignment for this.
+ */
+ u64 h_powers[8][2] __aligned(32);
+
+ /*
+ * Each entry in this array contains the two halves of an entry of
+ * h_powers XOR'd together, in the following order:
+ * H^8,H^6,H^7,H^5,H^4,H^2,H^3,H^1 i.e. indices 0,2,1,3,4,6,5,7.
+ * This is used for Karatsuba multiplication.
+ */
+ u64 h_powers_xored[8];
+};
+
+#define AES_GCM_KEY_VAES_AVX2(key) \
+ container_of((key), struct aes_gcm_key_vaes_avx2, base)
+#define AES_GCM_KEY_VAES_AVX2_SIZE \
+ (sizeof(struct aes_gcm_key_vaes_avx2) + (31 & ~(CRYPTO_MINALIGN - 1)))
+
+/* Key struct used by the VAES + AVX512 implementation of AES-GCM */
+struct aes_gcm_key_vaes_avx512 {
/*
* Common part of the key. The assembly code prefers 16-byte alignment
* for the round keys; we get this by them being located at the start of
@@ -895,10 +924,10 @@ struct aes_gcm_key_avx10 {
/* Three padding blocks required by the assembly code */
u64 padding[3][2];
};
-#define AES_GCM_KEY_AVX10(key) \
- container_of((key), struct aes_gcm_key_avx10, base)
-#define AES_GCM_KEY_AVX10_SIZE \
- (sizeof(struct aes_gcm_key_avx10) + (63 & ~(CRYPTO_MINALIGN - 1)))
+#define AES_GCM_KEY_VAES_AVX512(key) \
+ container_of((key), struct aes_gcm_key_vaes_avx512, base)
+#define AES_GCM_KEY_VAES_AVX512_SIZE \
+ (sizeof(struct aes_gcm_key_vaes_avx512) + (63 & ~(CRYPTO_MINALIGN - 1)))
/*
* These flags are passed to the AES-GCM helper functions to specify the
@@ -910,14 +939,16 @@ struct aes_gcm_key_avx10 {
#define FLAG_RFC4106 BIT(0)
#define FLAG_ENC BIT(1)
#define FLAG_AVX BIT(2)
-#define FLAG_AVX10_256 BIT(3)
-#define FLAG_AVX10_512 BIT(4)
+#define FLAG_VAES_AVX2 BIT(3)
+#define FLAG_VAES_AVX512 BIT(4)
static inline struct aes_gcm_key *
aes_gcm_key_get(struct crypto_aead *tfm, int flags)
{
- if (flags & (FLAG_AVX10_256 | FLAG_AVX10_512))
+ if (flags & FLAG_VAES_AVX512)
return PTR_ALIGN(crypto_aead_ctx(tfm), 64);
+ else if (flags & FLAG_VAES_AVX2)
+ return PTR_ALIGN(crypto_aead_ctx(tfm), 32);
else
return PTR_ALIGN(crypto_aead_ctx(tfm), 16);
}
@@ -927,26 +958,16 @@ aes_gcm_precompute_aesni(struct aes_gcm_key_aesni *key);
asmlinkage void
aes_gcm_precompute_aesni_avx(struct aes_gcm_key_aesni *key);
asmlinkage void
-aes_gcm_precompute_vaes_avx10_256(struct aes_gcm_key_avx10 *key);
+aes_gcm_precompute_vaes_avx2(struct aes_gcm_key_vaes_avx2 *key);
asmlinkage void
-aes_gcm_precompute_vaes_avx10_512(struct aes_gcm_key_avx10 *key);
+aes_gcm_precompute_vaes_avx512(struct aes_gcm_key_vaes_avx512 *key);
static void aes_gcm_precompute(struct aes_gcm_key *key, int flags)
{
- /*
- * To make things a bit easier on the assembly side, the AVX10
- * implementations use the same key format. Therefore, a single
- * function using 256-bit vectors would suffice here. However, it's
- * straightforward to provide a 512-bit one because of how the assembly
- * code is structured, and it works nicely because the total size of the
- * key powers is a multiple of 512 bits. So we take advantage of that.
- *
- * A similar situation applies to the AES-NI implementations.
- */
- if (flags & FLAG_AVX10_512)
- aes_gcm_precompute_vaes_avx10_512(AES_GCM_KEY_AVX10(key));
- else if (flags & FLAG_AVX10_256)
- aes_gcm_precompute_vaes_avx10_256(AES_GCM_KEY_AVX10(key));
+ if (flags & FLAG_VAES_AVX512)
+ aes_gcm_precompute_vaes_avx512(AES_GCM_KEY_VAES_AVX512(key));
+ else if (flags & FLAG_VAES_AVX2)
+ aes_gcm_precompute_vaes_avx2(AES_GCM_KEY_VAES_AVX2(key));
else if (flags & FLAG_AVX)
aes_gcm_precompute_aesni_avx(AES_GCM_KEY_AESNI(key));
else
@@ -960,15 +981,21 @@ asmlinkage void
aes_gcm_aad_update_aesni_avx(const struct aes_gcm_key_aesni *key,
u8 ghash_acc[16], const u8 *aad, int aadlen);
asmlinkage void
-aes_gcm_aad_update_vaes_avx10(const struct aes_gcm_key_avx10 *key,
- u8 ghash_acc[16], const u8 *aad, int aadlen);
+aes_gcm_aad_update_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+ u8 ghash_acc[16], const u8 *aad, int aadlen);
+asmlinkage void
+aes_gcm_aad_update_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+ u8 ghash_acc[16], const u8 *aad, int aadlen);
static void aes_gcm_aad_update(const struct aes_gcm_key *key, u8 ghash_acc[16],
const u8 *aad, int aadlen, int flags)
{
- if (flags & (FLAG_AVX10_256 | FLAG_AVX10_512))
- aes_gcm_aad_update_vaes_avx10(AES_GCM_KEY_AVX10(key), ghash_acc,
- aad, aadlen);
+ if (flags & FLAG_VAES_AVX512)
+ aes_gcm_aad_update_vaes_avx512(AES_GCM_KEY_VAES_AVX512(key),
+ ghash_acc, aad, aadlen);
+ else if (flags & FLAG_VAES_AVX2)
+ aes_gcm_aad_update_vaes_avx2(AES_GCM_KEY_VAES_AVX2(key),
+ ghash_acc, aad, aadlen);
else if (flags & FLAG_AVX)
aes_gcm_aad_update_aesni_avx(AES_GCM_KEY_AESNI(key), ghash_acc,
aad, aadlen);
@@ -986,13 +1013,13 @@ aes_gcm_enc_update_aesni_avx(const struct aes_gcm_key_aesni *key,
const u32 le_ctr[4], u8 ghash_acc[16],
const u8 *src, u8 *dst, int datalen);
asmlinkage void
-aes_gcm_enc_update_vaes_avx10_256(const struct aes_gcm_key_avx10 *key,
- const u32 le_ctr[4], u8 ghash_acc[16],
- const u8 *src, u8 *dst, int datalen);
+aes_gcm_enc_update_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+ const u32 le_ctr[4], u8 ghash_acc[16],
+ const u8 *src, u8 *dst, int datalen);
asmlinkage void
-aes_gcm_enc_update_vaes_avx10_512(const struct aes_gcm_key_avx10 *key,
- const u32 le_ctr[4], u8 ghash_acc[16],
- const u8 *src, u8 *dst, int datalen);
+aes_gcm_enc_update_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+ const u32 le_ctr[4], u8 ghash_acc[16],
+ const u8 *src, u8 *dst, int datalen);
asmlinkage void
aes_gcm_dec_update_aesni(const struct aes_gcm_key_aesni *key,
@@ -1003,13 +1030,13 @@ aes_gcm_dec_update_aesni_avx(const struct aes_gcm_key_aesni *key,
const u32 le_ctr[4], u8 ghash_acc[16],
const u8 *src, u8 *dst, int datalen);
asmlinkage void
-aes_gcm_dec_update_vaes_avx10_256(const struct aes_gcm_key_avx10 *key,
- const u32 le_ctr[4], u8 ghash_acc[16],
- const u8 *src, u8 *dst, int datalen);
+aes_gcm_dec_update_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+ const u32 le_ctr[4], u8 ghash_acc[16],
+ const u8 *src, u8 *dst, int datalen);
asmlinkage void
-aes_gcm_dec_update_vaes_avx10_512(const struct aes_gcm_key_avx10 *key,
- const u32 le_ctr[4], u8 ghash_acc[16],
- const u8 *src, u8 *dst, int datalen);
+aes_gcm_dec_update_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+ const u32 le_ctr[4], u8 ghash_acc[16],
+ const u8 *src, u8 *dst, int datalen);
/* __always_inline to optimize out the branches based on @flags */
static __always_inline void
@@ -1018,14 +1045,14 @@ aes_gcm_update(const struct aes_gcm_key *key,
const u8 *src, u8 *dst, int datalen, int flags)
{
if (flags & FLAG_ENC) {
- if (flags & FLAG_AVX10_512)
- aes_gcm_enc_update_vaes_avx10_512(AES_GCM_KEY_AVX10(key),
- le_ctr, ghash_acc,
- src, dst, datalen);
- else if (flags & FLAG_AVX10_256)
- aes_gcm_enc_update_vaes_avx10_256(AES_GCM_KEY_AVX10(key),
- le_ctr, ghash_acc,
- src, dst, datalen);
+ if (flags & FLAG_VAES_AVX512)
+ aes_gcm_enc_update_vaes_avx512(AES_GCM_KEY_VAES_AVX512(key),
+ le_ctr, ghash_acc,
+ src, dst, datalen);
+ else if (flags & FLAG_VAES_AVX2)
+ aes_gcm_enc_update_vaes_avx2(AES_GCM_KEY_VAES_AVX2(key),
+ le_ctr, ghash_acc,
+ src, dst, datalen);
else if (flags & FLAG_AVX)
aes_gcm_enc_update_aesni_avx(AES_GCM_KEY_AESNI(key),
le_ctr, ghash_acc,
@@ -1034,14 +1061,14 @@ aes_gcm_update(const struct aes_gcm_key *key,
aes_gcm_enc_update_aesni(AES_GCM_KEY_AESNI(key), le_ctr,
ghash_acc, src, dst, datalen);
} else {
- if (flags & FLAG_AVX10_512)
- aes_gcm_dec_update_vaes_avx10_512(AES_GCM_KEY_AVX10(key),
- le_ctr, ghash_acc,
- src, dst, datalen);
- else if (flags & FLAG_AVX10_256)
- aes_gcm_dec_update_vaes_avx10_256(AES_GCM_KEY_AVX10(key),
- le_ctr, ghash_acc,
- src, dst, datalen);
+ if (flags & FLAG_VAES_AVX512)
+ aes_gcm_dec_update_vaes_avx512(AES_GCM_KEY_VAES_AVX512(key),
+ le_ctr, ghash_acc,
+ src, dst, datalen);
+ else if (flags & FLAG_VAES_AVX2)
+ aes_gcm_dec_update_vaes_avx2(AES_GCM_KEY_VAES_AVX2(key),
+ le_ctr, ghash_acc,
+ src, dst, datalen);
else if (flags & FLAG_AVX)
aes_gcm_dec_update_aesni_avx(AES_GCM_KEY_AESNI(key),
le_ctr, ghash_acc,
@@ -1062,9 +1089,13 @@ aes_gcm_enc_final_aesni_avx(const struct aes_gcm_key_aesni *key,
const u32 le_ctr[4], u8 ghash_acc[16],
u64 total_aadlen, u64 total_datalen);
asmlinkage void
-aes_gcm_enc_final_vaes_avx10(const struct aes_gcm_key_avx10 *key,
- const u32 le_ctr[4], u8 ghash_acc[16],
- u64 total_aadlen, u64 total_datalen);
+aes_gcm_enc_final_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+ const u32 le_ctr[4], u8 ghash_acc[16],
+ u64 total_aadlen, u64 total_datalen);
+asmlinkage void
+aes_gcm_enc_final_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+ const u32 le_ctr[4], u8 ghash_acc[16],
+ u64 total_aadlen, u64 total_datalen);
/* __always_inline to optimize out the branches based on @flags */
static __always_inline void
@@ -1072,10 +1103,14 @@ aes_gcm_enc_final(const struct aes_gcm_key *key,
const u32 le_ctr[4], u8 ghash_acc[16],
u64 total_aadlen, u64 total_datalen, int flags)
{
- if (flags & (FLAG_AVX10_256 | FLAG_AVX10_512))
- aes_gcm_enc_final_vaes_avx10(AES_GCM_KEY_AVX10(key),
- le_ctr, ghash_acc,
- total_aadlen, total_datalen);
+ if (flags & FLAG_VAES_AVX512)
+ aes_gcm_enc_final_vaes_avx512(AES_GCM_KEY_VAES_AVX512(key),
+ le_ctr, ghash_acc,
+ total_aadlen, total_datalen);
+ else if (flags & FLAG_VAES_AVX2)
+ aes_gcm_enc_final_vaes_avx2(AES_GCM_KEY_VAES_AVX2(key),
+ le_ctr, ghash_acc,
+ total_aadlen, total_datalen);
else if (flags & FLAG_AVX)
aes_gcm_enc_final_aesni_avx(AES_GCM_KEY_AESNI(key),
le_ctr, ghash_acc,
@@ -1097,10 +1132,15 @@ aes_gcm_dec_final_aesni_avx(const struct aes_gcm_key_aesni *key,
u64 total_aadlen, u64 total_datalen,
const u8 tag[16], int taglen);
asmlinkage bool __must_check
-aes_gcm_dec_final_vaes_avx10(const struct aes_gcm_key_avx10 *key,
- const u32 le_ctr[4], const u8 ghash_acc[16],
- u64 total_aadlen, u64 total_datalen,
- const u8 tag[16], int taglen);
+aes_gcm_dec_final_vaes_avx2(const struct aes_gcm_key_vaes_avx2 *key,
+ const u32 le_ctr[4], const u8 ghash_acc[16],
+ u64 total_aadlen, u64 total_datalen,
+ const u8 tag[16], int taglen);
+asmlinkage bool __must_check
+aes_gcm_dec_final_vaes_avx512(const struct aes_gcm_key_vaes_avx512 *key,
+ const u32 le_ctr[4], const u8 ghash_acc[16],
+ u64 total_aadlen, u64 total_datalen,
+ const u8 tag[16], int taglen);
/* __always_inline to optimize out the branches based on @flags */
static __always_inline bool __must_check
@@ -1108,11 +1148,16 @@ aes_gcm_dec_final(const struct aes_gcm_key *key, const u32 le_ctr[4],
u8 ghash_acc[16], u64 total_aadlen, u64 total_datalen,
u8 tag[16], int taglen, int flags)
{
- if (flags & (FLAG_AVX10_256 | FLAG_AVX10_512))
- return aes_gcm_dec_final_vaes_avx10(AES_GCM_KEY_AVX10(key),
- le_ctr, ghash_acc,
- total_aadlen, total_datalen,
- tag, taglen);
+ if (flags & FLAG_VAES_AVX512)
+ return aes_gcm_dec_final_vaes_avx512(AES_GCM_KEY_VAES_AVX512(key),
+ le_ctr, ghash_acc,
+ total_aadlen, total_datalen,
+ tag, taglen);
+ else if (flags & FLAG_VAES_AVX2)
+ return aes_gcm_dec_final_vaes_avx2(AES_GCM_KEY_VAES_AVX2(key),
+ le_ctr, ghash_acc,
+ total_aadlen, total_datalen,
+ tag, taglen);
else if (flags & FLAG_AVX)
return aes_gcm_dec_final_aesni_avx(AES_GCM_KEY_AESNI(key),
le_ctr, ghash_acc,
@@ -1195,10 +1240,14 @@ static int gcm_setkey(struct crypto_aead *tfm, const u8 *raw_key,
BUILD_BUG_ON(offsetof(struct aes_gcm_key_aesni, h_powers) != 496);
BUILD_BUG_ON(offsetof(struct aes_gcm_key_aesni, h_powers_xored) != 624);
BUILD_BUG_ON(offsetof(struct aes_gcm_key_aesni, h_times_x64) != 688);
- BUILD_BUG_ON(offsetof(struct aes_gcm_key_avx10, base.aes_key.key_enc) != 0);
- BUILD_BUG_ON(offsetof(struct aes_gcm_key_avx10, base.aes_key.key_length) != 480);
- BUILD_BUG_ON(offsetof(struct aes_gcm_key_avx10, h_powers) != 512);
- BUILD_BUG_ON(offsetof(struct aes_gcm_key_avx10, padding) != 768);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx2, base.aes_key.key_enc) != 0);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx2, base.aes_key.key_length) != 480);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx2, h_powers) != 512);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx2, h_powers_xored) != 640);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx512, base.aes_key.key_enc) != 0);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx512, base.aes_key.key_length) != 480);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx512, h_powers) != 512);
+ BUILD_BUG_ON(offsetof(struct aes_gcm_key_vaes_avx512, padding) != 768);
if (likely(crypto_simd_usable())) {
err = aes_check_keylen(keylen);
@@ -1231,8 +1280,9 @@ static int gcm_setkey(struct crypto_aead *tfm, const u8 *raw_key,
gf128mul_lle(&h, (const be128 *)x_to_the_minus1);
/* Compute the needed key powers */
- if (flags & (FLAG_AVX10_256 | FLAG_AVX10_512)) {
- struct aes_gcm_key_avx10 *k = AES_GCM_KEY_AVX10(key);
+ if (flags & FLAG_VAES_AVX512) {
+ struct aes_gcm_key_vaes_avx512 *k =
+ AES_GCM_KEY_VAES_AVX512(key);
for (i = ARRAY_SIZE(k->h_powers) - 1; i >= 0; i--) {
k->h_powers[i][0] = be64_to_cpu(h.b);
@@ -1240,6 +1290,22 @@ static int gcm_setkey(struct crypto_aead *tfm, const u8 *raw_key,
gf128mul_lle(&h, &h1);
}
memset(k->padding, 0, sizeof(k->padding));
+ } else if (flags & FLAG_VAES_AVX2) {
+ struct aes_gcm_key_vaes_avx2 *k =
+ AES_GCM_KEY_VAES_AVX2(key);
+ static const u8 indices[8] = { 0, 2, 1, 3, 4, 6, 5, 7 };
+
+ for (i = ARRAY_SIZE(k->h_powers) - 1; i >= 0; i--) {
+ k->h_powers[i][0] = be64_to_cpu(h.b);
+ k->h_powers[i][1] = be64_to_cpu(h.a);
+ gf128mul_lle(&h, &h1);
+ }
+ for (i = 0; i < ARRAY_SIZE(k->h_powers_xored); i++) {
+ int j = indices[i];
+
+ k->h_powers_xored[i] = k->h_powers[j][0] ^
+ k->h_powers[j][1];
+ }
} else {
struct aes_gcm_key_aesni *k = AES_GCM_KEY_AESNI(key);
@@ -1508,15 +1574,15 @@ DEFINE_GCM_ALGS(aesni_avx, FLAG_AVX,
"generic-gcm-aesni-avx", "rfc4106-gcm-aesni-avx",
AES_GCM_KEY_AESNI_SIZE, 500);
-/* aes_gcm_algs_vaes_avx10_256 */
-DEFINE_GCM_ALGS(vaes_avx10_256, FLAG_AVX10_256,
- "generic-gcm-vaes-avx10_256", "rfc4106-gcm-vaes-avx10_256",
- AES_GCM_KEY_AVX10_SIZE, 700);
+/* aes_gcm_algs_vaes_avx2 */
+DEFINE_GCM_ALGS(vaes_avx2, FLAG_VAES_AVX2,
+ "generic-gcm-vaes-avx2", "rfc4106-gcm-vaes-avx2",
+ AES_GCM_KEY_VAES_AVX2_SIZE, 600);
-/* aes_gcm_algs_vaes_avx10_512 */
-DEFINE_GCM_ALGS(vaes_avx10_512, FLAG_AVX10_512,
- "generic-gcm-vaes-avx10_512", "rfc4106-gcm-vaes-avx10_512",
- AES_GCM_KEY_AVX10_SIZE, 800);
+/* aes_gcm_algs_vaes_avx512 */
+DEFINE_GCM_ALGS(vaes_avx512, FLAG_VAES_AVX512,
+ "generic-gcm-vaes-avx512", "rfc4106-gcm-vaes-avx512",
+ AES_GCM_KEY_VAES_AVX512_SIZE, 800);
static int __init register_avx_algs(void)
{
@@ -1548,6 +1614,10 @@ static int __init register_avx_algs(void)
ARRAY_SIZE(skcipher_algs_vaes_avx2));
if (err)
return err;
+ err = crypto_register_aeads(aes_gcm_algs_vaes_avx2,
+ ARRAY_SIZE(aes_gcm_algs_vaes_avx2));
+ if (err)
+ return err;
if (!boot_cpu_has(X86_FEATURE_AVX512BW) ||
!boot_cpu_has(X86_FEATURE_AVX512VL) ||
@@ -1556,26 +1626,21 @@ static int __init register_avx_algs(void)
XFEATURE_MASK_AVX512, NULL))
return 0;
- err = crypto_register_aeads(aes_gcm_algs_vaes_avx10_256,
- ARRAY_SIZE(aes_gcm_algs_vaes_avx10_256));
- if (err)
- return err;
-
if (boot_cpu_has(X86_FEATURE_PREFER_YMM)) {
int i;
for (i = 0; i < ARRAY_SIZE(skcipher_algs_vaes_avx512); i++)
skcipher_algs_vaes_avx512[i].base.cra_priority = 1;
- for (i = 0; i < ARRAY_SIZE(aes_gcm_algs_vaes_avx10_512); i++)
- aes_gcm_algs_vaes_avx10_512[i].base.cra_priority = 1;
+ for (i = 0; i < ARRAY_SIZE(aes_gcm_algs_vaes_avx512); i++)
+ aes_gcm_algs_vaes_avx512[i].base.cra_priority = 1;
}
err = crypto_register_skciphers(skcipher_algs_vaes_avx512,
ARRAY_SIZE(skcipher_algs_vaes_avx512));
if (err)
return err;
- err = crypto_register_aeads(aes_gcm_algs_vaes_avx10_512,
- ARRAY_SIZE(aes_gcm_algs_vaes_avx10_512));
+ err = crypto_register_aeads(aes_gcm_algs_vaes_avx512,
+ ARRAY_SIZE(aes_gcm_algs_vaes_avx512));
if (err)
return err;
@@ -1595,8 +1660,8 @@ static void unregister_avx_algs(void)
unregister_aeads(aes_gcm_algs_aesni_avx);
unregister_skciphers(skcipher_algs_vaes_avx2);
unregister_skciphers(skcipher_algs_vaes_avx512);
- unregister_aeads(aes_gcm_algs_vaes_avx10_256);
- unregister_aeads(aes_gcm_algs_vaes_avx10_512);
+ unregister_aeads(aes_gcm_algs_vaes_avx2);
+ unregister_aeads(aes_gcm_algs_vaes_avx512);
}
#else /* CONFIG_X86_64 */
static struct aead_alg aes_gcm_algs_aesni[0];
diff --git a/arch/x86/crypto/polyval-clmulni_glue.c b/arch/x86/crypto/polyval-clmulni_glue.c
deleted file mode 100644
index 6b466867f91a..000000000000
--- a/arch/x86/crypto/polyval-clmulni_glue.c
+++ /dev/null
@@ -1,180 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Glue code for POLYVAL using PCMULQDQ-NI
- *
- * Copyright (c) 2007 Nokia Siemens Networks - Mikko Herranen <mh1@iki.fi>
- * Copyright (c) 2009 Intel Corp.
- * Author: Huang Ying <ying.huang@intel.com>
- * Copyright 2021 Google LLC
- */
-
-/*
- * Glue code based on ghash-clmulni-intel_glue.c.
- *
- * This implementation of POLYVAL uses montgomery multiplication
- * accelerated by PCLMULQDQ-NI to implement the finite field
- * operations.
- */
-
-#include <asm/cpu_device_id.h>
-#include <asm/fpu/api.h>
-#include <crypto/internal/hash.h>
-#include <crypto/polyval.h>
-#include <crypto/utils.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-
-#define POLYVAL_ALIGN 16
-#define POLYVAL_ALIGN_ATTR __aligned(POLYVAL_ALIGN)
-#define POLYVAL_ALIGN_EXTRA ((POLYVAL_ALIGN - 1) & ~(CRYPTO_MINALIGN - 1))
-#define POLYVAL_CTX_SIZE (sizeof(struct polyval_tfm_ctx) + POLYVAL_ALIGN_EXTRA)
-#define NUM_KEY_POWERS 8
-
-struct polyval_tfm_ctx {
- /*
- * These powers must be in the order h^8, ..., h^1.
- */
- u8 key_powers[NUM_KEY_POWERS][POLYVAL_BLOCK_SIZE] POLYVAL_ALIGN_ATTR;
-};
-
-struct polyval_desc_ctx {
- u8 buffer[POLYVAL_BLOCK_SIZE];
-};
-
-asmlinkage void clmul_polyval_update(const struct polyval_tfm_ctx *keys,
- const u8 *in, size_t nblocks, u8 *accumulator);
-asmlinkage void clmul_polyval_mul(u8 *op1, const u8 *op2);
-
-static inline struct polyval_tfm_ctx *polyval_tfm_ctx(struct crypto_shash *tfm)
-{
- return PTR_ALIGN(crypto_shash_ctx(tfm), POLYVAL_ALIGN);
-}
-
-static void internal_polyval_update(const struct polyval_tfm_ctx *keys,
- const u8 *in, size_t nblocks, u8 *accumulator)
-{
- kernel_fpu_begin();
- clmul_polyval_update(keys, in, nblocks, accumulator);
- kernel_fpu_end();
-}
-
-static void internal_polyval_mul(u8 *op1, const u8 *op2)
-{
- kernel_fpu_begin();
- clmul_polyval_mul(op1, op2);
- kernel_fpu_end();
-}
-
-static int polyval_x86_setkey(struct crypto_shash *tfm,
- const u8 *key, unsigned int keylen)
-{
- struct polyval_tfm_ctx *tctx = polyval_tfm_ctx(tfm);
- int i;
-
- if (keylen != POLYVAL_BLOCK_SIZE)
- return -EINVAL;
-
- memcpy(tctx->key_powers[NUM_KEY_POWERS-1], key, POLYVAL_BLOCK_SIZE);
-
- for (i = NUM_KEY_POWERS-2; i >= 0; i--) {
- memcpy(tctx->key_powers[i], key, POLYVAL_BLOCK_SIZE);
- internal_polyval_mul(tctx->key_powers[i],
- tctx->key_powers[i+1]);
- }
-
- return 0;
-}
-
-static int polyval_x86_init(struct shash_desc *desc)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
-
- memset(dctx, 0, sizeof(*dctx));
-
- return 0;
-}
-
-static int polyval_x86_update(struct shash_desc *desc,
- const u8 *src, unsigned int srclen)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
- const struct polyval_tfm_ctx *tctx = polyval_tfm_ctx(desc->tfm);
- unsigned int nblocks;
-
- do {
- /* Allow rescheduling every 4K bytes. */
- nblocks = min(srclen, 4096U) / POLYVAL_BLOCK_SIZE;
- internal_polyval_update(tctx, src, nblocks, dctx->buffer);
- srclen -= nblocks * POLYVAL_BLOCK_SIZE;
- src += nblocks * POLYVAL_BLOCK_SIZE;
- } while (srclen >= POLYVAL_BLOCK_SIZE);
-
- return srclen;
-}
-
-static int polyval_x86_finup(struct shash_desc *desc, const u8 *src,
- unsigned int len, u8 *dst)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
- const struct polyval_tfm_ctx *tctx = polyval_tfm_ctx(desc->tfm);
-
- if (len) {
- crypto_xor(dctx->buffer, src, len);
- internal_polyval_mul(dctx->buffer,
- tctx->key_powers[NUM_KEY_POWERS-1]);
- }
-
- memcpy(dst, dctx->buffer, POLYVAL_BLOCK_SIZE);
-
- return 0;
-}
-
-static struct shash_alg polyval_alg = {
- .digestsize = POLYVAL_DIGEST_SIZE,
- .init = polyval_x86_init,
- .update = polyval_x86_update,
- .finup = polyval_x86_finup,
- .setkey = polyval_x86_setkey,
- .descsize = sizeof(struct polyval_desc_ctx),
- .base = {
- .cra_name = "polyval",
- .cra_driver_name = "polyval-clmulni",
- .cra_priority = 200,
- .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .cra_blocksize = POLYVAL_BLOCK_SIZE,
- .cra_ctxsize = POLYVAL_CTX_SIZE,
- .cra_module = THIS_MODULE,
- },
-};
-
-__maybe_unused static const struct x86_cpu_id pcmul_cpu_id[] = {
- X86_MATCH_FEATURE(X86_FEATURE_PCLMULQDQ, NULL),
- {}
-};
-MODULE_DEVICE_TABLE(x86cpu, pcmul_cpu_id);
-
-static int __init polyval_clmulni_mod_init(void)
-{
- if (!x86_match_cpu(pcmul_cpu_id))
- return -ENODEV;
-
- if (!boot_cpu_has(X86_FEATURE_AVX))
- return -ENODEV;
-
- return crypto_register_shash(&polyval_alg);
-}
-
-static void __exit polyval_clmulni_mod_exit(void)
-{
- crypto_unregister_shash(&polyval_alg);
-}
-
-module_init(polyval_clmulni_mod_init);
-module_exit(polyval_clmulni_mod_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("POLYVAL hash function accelerated by PCLMULQDQ-NI");
-MODULE_ALIAS_CRYPTO("polyval");
-MODULE_ALIAS_CRYPTO("polyval-clmulni");
diff --git a/arch/x86/entry/entry.S b/arch/x86/entry/entry.S
index 8e9a0cc20a4a..6ba2b3adcef0 100644
--- a/arch/x86/entry/entry.S
+++ b/arch/x86/entry/entry.S
@@ -4,6 +4,7 @@
*/
#include <linux/export.h>
+#include <linux/kvm_types.h>
#include <linux/linkage.h>
#include <linux/objtool.h>
#include <asm/msr-index.h>
@@ -29,8 +30,15 @@ SYM_FUNC_START(write_ibpb)
FILL_RETURN_BUFFER %rax, RSB_CLEAR_LOOPS, X86_BUG_IBPB_NO_RET
RET
SYM_FUNC_END(write_ibpb)
-/* For KVM */
-EXPORT_SYMBOL_GPL(write_ibpb);
+EXPORT_SYMBOL_FOR_KVM(write_ibpb);
+
+SYM_FUNC_START(__WARN_trap)
+ ANNOTATE_NOENDBR
+ ANNOTATE_REACHABLE
+ ud1 (%edx), %_ASM_ARG1
+ RET
+SYM_FUNC_END(__WARN_trap)
+EXPORT_SYMBOL(__WARN_trap)
.popsection
@@ -48,8 +56,7 @@ SYM_CODE_START_NOALIGN(x86_verw_sel)
.word __KERNEL_DS
.align L1_CACHE_BYTES, 0xcc
SYM_CODE_END(x86_verw_sel);
-/* For KVM */
-EXPORT_SYMBOL_GPL(x86_verw_sel);
+EXPORT_SYMBOL_FOR_KVM(x86_verw_sel);
.popsection
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index ed04a968cc7d..f9983a1907bf 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -19,6 +19,7 @@
* - idtentry: Define exception entry points.
*/
#include <linux/export.h>
+#include <linux/kvm_types.h>
#include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/cache.h>
@@ -1566,5 +1567,5 @@ SYM_FUNC_START(clear_bhb_loop)
pop %rbp
RET
SYM_FUNC_END(clear_bhb_loop)
-EXPORT_SYMBOL_GPL(clear_bhb_loop)
+EXPORT_SYMBOL_FOR_KVM(clear_bhb_loop)
STACK_FRAME_NON_STANDARD(clear_bhb_loop)
diff --git a/arch/x86/entry/entry_64_fred.S b/arch/x86/entry/entry_64_fred.S
index fafbd3e68cb8..894f7f16eb80 100644
--- a/arch/x86/entry/entry_64_fred.S
+++ b/arch/x86/entry/entry_64_fred.S
@@ -4,6 +4,7 @@
*/
#include <linux/export.h>
+#include <linux/kvm_types.h>
#include <asm/asm.h>
#include <asm/fred.h>
@@ -146,5 +147,5 @@ SYM_FUNC_START(asm_fred_entry_from_kvm)
RET
SYM_FUNC_END(asm_fred_entry_from_kvm)
-EXPORT_SYMBOL_GPL(asm_fred_entry_from_kvm);
+EXPORT_SYMBOL_FOR_KVM(asm_fred_entry_from_kvm);
#endif
diff --git a/arch/x86/entry/entry_fred.c b/arch/x86/entry/entry_fred.c
index f004a4dc74c2..94e626cc6a07 100644
--- a/arch/x86/entry/entry_fred.c
+++ b/arch/x86/entry/entry_fred.c
@@ -78,13 +78,13 @@ static noinstr void fred_intx(struct pt_regs *regs)
static __always_inline void fred_other(struct pt_regs *regs)
{
/* The compiler can fold these conditions into a single test */
- if (likely(regs->fred_ss.vector == FRED_SYSCALL && regs->fred_ss.lm)) {
+ if (likely(regs->fred_ss.vector == FRED_SYSCALL && regs->fred_ss.l)) {
regs->orig_ax = regs->ax;
regs->ax = -ENOSYS;
do_syscall_64(regs, regs->orig_ax);
return;
} else if (ia32_enabled() &&
- likely(regs->fred_ss.vector == FRED_SYSENTER && !regs->fred_ss.lm)) {
+ likely(regs->fred_ss.vector == FRED_SYSENTER && !regs->fred_ss.l)) {
regs->orig_ax = regs->ax;
regs->ax = -ENOSYS;
do_fast_syscall_32(regs);
diff --git a/arch/x86/entry/syscall_32.c b/arch/x86/entry/syscall_32.c
index 2b15ea17bb7c..a67a644d0cfe 100644
--- a/arch/x86/entry/syscall_32.c
+++ b/arch/x86/entry/syscall_32.c
@@ -274,9 +274,10 @@ static noinstr bool __do_fast_syscall_32(struct pt_regs *regs)
* fetch EBP before invoking any of the syscall entry work
* functions.
*/
- syscall_enter_from_user_mode_prepare(regs);
+ enter_from_user_mode(regs);
instrumentation_begin();
+ local_irq_enable();
/* Fetch EBP from where the vDSO stashed it. */
if (IS_ENABLED(CONFIG_X86_64)) {
/*
diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl
index 4877e16da69a..e979a3eac7a3 100644
--- a/arch/x86/entry/syscalls/syscall_32.tbl
+++ b/arch/x86/entry/syscalls/syscall_32.tbl
@@ -475,3 +475,4 @@
467 i386 open_tree_attr sys_open_tree_attr
468 i386 file_getattr sys_file_getattr
469 i386 file_setattr sys_file_setattr
+470 i386 listns sys_listns
diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl
index ced2a1deecd7..8a4ac4841be6 100644
--- a/arch/x86/entry/syscalls/syscall_64.tbl
+++ b/arch/x86/entry/syscalls/syscall_64.tbl
@@ -394,6 +394,7 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
#
# Due to a historical design error, certain syscalls are numbered differently
diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c
index b20661b8621d..44656d2fb555 100644
--- a/arch/x86/events/amd/core.c
+++ b/arch/x86/events/amd/core.c
@@ -2,6 +2,7 @@
#include <linux/perf_event.h>
#include <linux/jump_label.h>
#include <linux/export.h>
+#include <linux/kvm_types.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -763,7 +764,12 @@ static void amd_pmu_enable_all(int added)
if (!test_bit(idx, cpuc->active_mask))
continue;
- amd_pmu_enable_event(cpuc->events[idx]);
+ /*
+ * FIXME: cpuc->events[idx] can become NULL in a subtle race
+ * condition with NMI->throttle->x86_pmu_stop().
+ */
+ if (cpuc->events[idx])
+ amd_pmu_enable_event(cpuc->events[idx]);
}
}
@@ -1569,7 +1575,7 @@ void amd_pmu_enable_virt(void)
/* Reload all events */
amd_pmu_reload_virt();
}
-EXPORT_SYMBOL_GPL(amd_pmu_enable_virt);
+EXPORT_SYMBOL_FOR_KVM(amd_pmu_enable_virt);
void amd_pmu_disable_virt(void)
{
@@ -1586,4 +1592,4 @@ void amd_pmu_disable_virt(void)
/* Reload all events */
amd_pmu_reload_virt();
}
-EXPORT_SYMBOL_GPL(amd_pmu_disable_virt);
+EXPORT_SYMBOL_FOR_KVM(amd_pmu_disable_virt);
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 745caa6c15a3..0c38a31d5fc7 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -20,6 +20,7 @@
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kdebug.h>
+#include <linux/kvm_types.h>
#include <linux/sched/mm.h>
#include <linux/sched/clock.h>
#include <linux/uaccess.h>
@@ -554,14 +555,22 @@ static inline int precise_br_compat(struct perf_event *event)
return m == b;
}
-int x86_pmu_max_precise(void)
+int x86_pmu_max_precise(struct pmu *pmu)
{
int precise = 0;
- /* Support for constant skid */
if (x86_pmu.pebs_active && !x86_pmu.pebs_broken) {
- precise++;
+ /* arch PEBS */
+ if (x86_pmu.arch_pebs) {
+ precise = 2;
+ if (hybrid(pmu, arch_pebs_cap).pdists)
+ precise++;
+
+ return precise;
+ }
+ /* legacy PEBS - support for constant skid */
+ precise++;
/* Support for IP fixup */
if (x86_pmu.lbr_nr || x86_pmu.intel_cap.pebs_format >= 2)
precise++;
@@ -569,13 +578,14 @@ int x86_pmu_max_precise(void)
if (x86_pmu.pebs_prec_dist)
precise++;
}
+
return precise;
}
int x86_pmu_hw_config(struct perf_event *event)
{
if (event->attr.precise_ip) {
- int precise = x86_pmu_max_precise();
+ int precise = x86_pmu_max_precise(event->pmu);
if (event->attr.precise_ip > precise)
return -EOPNOTSUPP;
@@ -714,7 +724,7 @@ struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr, void *data)
{
return static_call(x86_pmu_guest_get_msrs)(nr, data);
}
-EXPORT_SYMBOL_GPL(perf_guest_get_msrs);
+EXPORT_SYMBOL_FOR_KVM(perf_guest_get_msrs);
/*
* There may be PMI landing after enabled=0. The PMI hitting could be before or
@@ -1344,6 +1354,7 @@ static void x86_pmu_enable(struct pmu *pmu)
hwc->state |= PERF_HES_ARCH;
x86_pmu_stop(event, PERF_EF_UPDATE);
+ cpuc->events[hwc->idx] = NULL;
}
/*
@@ -1365,6 +1376,7 @@ static void x86_pmu_enable(struct pmu *pmu)
* if cpuc->enabled = 0, then no wrmsr as
* per x86_pmu_enable_event()
*/
+ cpuc->events[hwc->idx] = event;
x86_pmu_start(event, PERF_EF_RELOAD);
}
cpuc->n_added = 0;
@@ -1531,7 +1543,6 @@ static void x86_pmu_start(struct perf_event *event, int flags)
event->hw.state = 0;
- cpuc->events[idx] = event;
__set_bit(idx, cpuc->active_mask);
static_call(x86_pmu_enable)(event);
perf_event_update_userpage(event);
@@ -1610,7 +1621,6 @@ void x86_pmu_stop(struct perf_event *event, int flags)
if (test_bit(hwc->idx, cpuc->active_mask)) {
static_call(x86_pmu_disable)(event);
__clear_bit(hwc->idx, cpuc->active_mask);
- cpuc->events[hwc->idx] = NULL;
WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
hwc->state |= PERF_HES_STOPPED;
}
@@ -1648,6 +1658,7 @@ static void x86_pmu_del(struct perf_event *event, int flags)
* Not a TXN, therefore cleanup properly.
*/
x86_pmu_stop(event, PERF_EF_UPDATE);
+ cpuc->events[event->hw.idx] = NULL;
for (i = 0; i < cpuc->n_events; i++) {
if (event == cpuc->event_list[i])
@@ -2629,7 +2640,9 @@ static ssize_t max_precise_show(struct device *cdev,
struct device_attribute *attr,
char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", x86_pmu_max_precise());
+ struct pmu *pmu = dev_get_drvdata(cdev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", x86_pmu_max_precise(pmu));
}
static DEVICE_ATTR_RO(max_precise);
@@ -2789,13 +2802,13 @@ perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *re
return;
}
- if (perf_callchain_store(entry, regs->ip))
- return;
-
- if (perf_hw_regs(regs))
+ if (perf_hw_regs(regs)) {
+ if (perf_callchain_store(entry, regs->ip))
+ return;
unwind_start(&state, current, regs, NULL);
- else
+ } else {
unwind_start(&state, current, NULL, (void *)regs->sp);
+ }
for (; !unwind_done(&state); unwind_next_frame(&state)) {
addr = unwind_get_return_address(&state);
@@ -2845,46 +2858,6 @@ static unsigned long get_segment_base(unsigned int segment)
return get_desc_base(desc);
}
-#ifdef CONFIG_UPROBES
-/*
- * Heuristic-based check if uprobe is installed at the function entry.
- *
- * Under assumption of user code being compiled with frame pointers,
- * `push %rbp/%ebp` is a good indicator that we indeed are.
- *
- * Similarly, `endbr64` (assuming 64-bit mode) is also a common pattern.
- * If we get this wrong, captured stack trace might have one extra bogus
- * entry, but the rest of stack trace will still be meaningful.
- */
-static bool is_uprobe_at_func_entry(struct pt_regs *regs)
-{
- struct arch_uprobe *auprobe;
-
- if (!current->utask)
- return false;
-
- auprobe = current->utask->auprobe;
- if (!auprobe)
- return false;
-
- /* push %rbp/%ebp */
- if (auprobe->insn[0] == 0x55)
- return true;
-
- /* endbr64 (64-bit only) */
- if (user_64bit_mode(regs) && is_endbr((u32 *)auprobe->insn))
- return true;
-
- return false;
-}
-
-#else
-static bool is_uprobe_at_func_entry(struct pt_regs *regs)
-{
- return false;
-}
-#endif /* CONFIG_UPROBES */
-
#ifdef CONFIG_IA32_EMULATION
#include <linux/compat.h>
@@ -3106,7 +3079,7 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
cap->events_mask_len = x86_pmu.events_mask_len;
cap->pebs_ept = x86_pmu.pebs_ept;
}
-EXPORT_SYMBOL_GPL(perf_get_x86_pmu_capability);
+EXPORT_SYMBOL_FOR_KVM(perf_get_x86_pmu_capability);
u64 perf_get_hw_event_config(int hw_event)
{
@@ -3117,4 +3090,4 @@ u64 perf_get_hw_event_config(int hw_event)
return 0;
}
-EXPORT_SYMBOL_GPL(perf_get_hw_event_config);
+EXPORT_SYMBOL_FOR_KVM(perf_get_hw_event_config);
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index 28f5468a6ea3..853fe073bab3 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -2563,6 +2563,44 @@ static void intel_pmu_disable_fixed(struct perf_event *event)
cpuc->fixed_ctrl_val &= ~mask;
}
+static inline void __intel_pmu_update_event_ext(int idx, u64 ext)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ u32 msr;
+
+ if (idx < INTEL_PMC_IDX_FIXED) {
+ msr = MSR_IA32_PMC_V6_GP0_CFG_C +
+ x86_pmu.addr_offset(idx, false);
+ } else {
+ msr = MSR_IA32_PMC_V6_FX0_CFG_C +
+ x86_pmu.addr_offset(idx - INTEL_PMC_IDX_FIXED, false);
+ }
+
+ cpuc->cfg_c_val[idx] = ext;
+ wrmsrq(msr, ext);
+}
+
+static void intel_pmu_disable_event_ext(struct perf_event *event)
+{
+ /*
+ * Only clear CFG_C MSR for PEBS counter group events,
+ * it avoids the HW counter's value to be added into
+ * other PEBS records incorrectly after PEBS counter
+ * group events are disabled.
+ *
+ * For other events, it's unnecessary to clear CFG_C MSRs
+ * since CFG_C doesn't take effect if counter is in
+ * disabled state. That helps to reduce the WRMSR overhead
+ * in context switches.
+ */
+ if (!is_pebs_counter_event_group(event))
+ return;
+
+ __intel_pmu_update_event_ext(event->hw.idx, 0);
+}
+
+DEFINE_STATIC_CALL_NULL(intel_pmu_disable_event_ext, intel_pmu_disable_event_ext);
+
static void intel_pmu_disable_event(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@@ -2571,9 +2609,12 @@ static void intel_pmu_disable_event(struct perf_event *event)
switch (idx) {
case 0 ... INTEL_PMC_IDX_FIXED - 1:
intel_clear_masks(event, idx);
+ static_call_cond(intel_pmu_disable_event_ext)(event);
x86_pmu_disable_event(event);
break;
case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1:
+ static_call_cond(intel_pmu_disable_event_ext)(event);
+ fallthrough;
case INTEL_PMC_IDX_METRIC_BASE ... INTEL_PMC_IDX_METRIC_END:
intel_pmu_disable_fixed(event);
break;
@@ -2940,6 +2981,79 @@ static void intel_pmu_enable_acr(struct perf_event *event)
DEFINE_STATIC_CALL_NULL(intel_pmu_enable_acr_event, intel_pmu_enable_acr);
+static void intel_pmu_enable_event_ext(struct perf_event *event)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+ union arch_pebs_index old, new;
+ struct arch_pebs_cap cap;
+ u64 ext = 0;
+
+ cap = hybrid(cpuc->pmu, arch_pebs_cap);
+
+ if (event->attr.precise_ip) {
+ u64 pebs_data_cfg = intel_get_arch_pebs_data_config(event);
+
+ ext |= ARCH_PEBS_EN;
+ if (hwc->flags & PERF_X86_EVENT_AUTO_RELOAD)
+ ext |= (-hwc->sample_period) & ARCH_PEBS_RELOAD;
+
+ if (pebs_data_cfg && cap.caps) {
+ if (pebs_data_cfg & PEBS_DATACFG_MEMINFO)
+ ext |= ARCH_PEBS_AUX & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_GP)
+ ext |= ARCH_PEBS_GPR & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_XMMS)
+ ext |= ARCH_PEBS_VECR_XMM & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_LBRS)
+ ext |= ARCH_PEBS_LBR & cap.caps;
+
+ if (pebs_data_cfg &
+ (PEBS_DATACFG_CNTR_MASK << PEBS_DATACFG_CNTR_SHIFT))
+ ext |= ARCH_PEBS_CNTR_GP & cap.caps;
+
+ if (pebs_data_cfg &
+ (PEBS_DATACFG_FIX_MASK << PEBS_DATACFG_FIX_SHIFT))
+ ext |= ARCH_PEBS_CNTR_FIXED & cap.caps;
+
+ if (pebs_data_cfg & PEBS_DATACFG_METRICS)
+ ext |= ARCH_PEBS_CNTR_METRICS & cap.caps;
+ }
+
+ if (cpuc->n_pebs == cpuc->n_large_pebs)
+ new.thresh = ARCH_PEBS_THRESH_MULTI;
+ else
+ new.thresh = ARCH_PEBS_THRESH_SINGLE;
+
+ rdmsrq(MSR_IA32_PEBS_INDEX, old.whole);
+ if (new.thresh != old.thresh || !old.en) {
+ if (old.thresh == ARCH_PEBS_THRESH_MULTI && old.wr > 0) {
+ /*
+ * Large PEBS was enabled.
+ * Drain PEBS buffer before applying the single PEBS.
+ */
+ intel_pmu_drain_pebs_buffer();
+ } else {
+ new.wr = 0;
+ new.full = 0;
+ new.en = 1;
+ wrmsrq(MSR_IA32_PEBS_INDEX, new.whole);
+ }
+ }
+ }
+
+ if (is_pebs_counter_event_group(event))
+ ext |= ARCH_PEBS_CNTR_ALLOW;
+
+ if (cpuc->cfg_c_val[hwc->idx] != ext)
+ __intel_pmu_update_event_ext(hwc->idx, ext);
+}
+
+DEFINE_STATIC_CALL_NULL(intel_pmu_enable_event_ext, intel_pmu_enable_event_ext);
+
static void intel_pmu_enable_event(struct perf_event *event)
{
u64 enable_mask = ARCH_PERFMON_EVENTSEL_ENABLE;
@@ -2955,10 +3069,12 @@ static void intel_pmu_enable_event(struct perf_event *event)
enable_mask |= ARCH_PERFMON_EVENTSEL_BR_CNTR;
intel_set_masks(event, idx);
static_call_cond(intel_pmu_enable_acr_event)(event);
+ static_call_cond(intel_pmu_enable_event_ext)(event);
__x86_pmu_enable_event(hwc, enable_mask);
break;
case INTEL_PMC_IDX_FIXED ... INTEL_PMC_IDX_FIXED_BTS - 1:
static_call_cond(intel_pmu_enable_acr_event)(event);
+ static_call_cond(intel_pmu_enable_event_ext)(event);
fallthrough;
case INTEL_PMC_IDX_METRIC_BASE ... INTEL_PMC_IDX_METRIC_END:
intel_pmu_enable_fixed(event);
@@ -3216,6 +3332,19 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
}
/*
+ * Arch PEBS sets bit 54 in the global status register
+ */
+ if (__test_and_clear_bit(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT,
+ (unsigned long *)&status)) {
+ handled++;
+ static_call(x86_pmu_drain_pebs)(regs, &data);
+
+ if (cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS] &&
+ is_pebs_counter_event_group(cpuc->events[INTEL_PMC_IDX_FIXED_SLOTS]))
+ status &= ~GLOBAL_STATUS_PERF_METRICS_OVF_BIT;
+ }
+
+ /*
* Intel PT
*/
if (__test_and_clear_bit(GLOBAL_STATUS_TRACE_TOPAPMI_BIT, (unsigned long *)&status)) {
@@ -3269,7 +3398,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status)
* The PEBS buffer has to be drained before handling the A-PMI
*/
if (is_pebs_counter_event_group(event))
- x86_pmu.drain_pebs(regs, &data);
+ static_call(x86_pmu_drain_pebs)(regs, &data);
last_period = event->hw.last_period;
@@ -4029,7 +4158,9 @@ static unsigned long intel_pmu_large_pebs_flags(struct perf_event *event)
if (!event->attr.exclude_kernel)
flags &= ~PERF_SAMPLE_REGS_USER;
if (event->attr.sample_regs_user & ~PEBS_GP_REGS)
- flags &= ~(PERF_SAMPLE_REGS_USER | PERF_SAMPLE_REGS_INTR);
+ flags &= ~PERF_SAMPLE_REGS_USER;
+ if (event->attr.sample_regs_intr & ~PEBS_GP_REGS)
+ flags &= ~PERF_SAMPLE_REGS_INTR;
return flags;
}
@@ -4204,6 +4335,20 @@ static bool intel_pmu_is_acr_group(struct perf_event *event)
return false;
}
+static inline bool intel_pmu_has_pebs_counter_group(struct pmu *pmu)
+{
+ u64 caps;
+
+ if (x86_pmu.intel_cap.pebs_format >= 6 && x86_pmu.intel_cap.pebs_baseline)
+ return true;
+
+ caps = hybrid(pmu, arch_pebs_cap).caps;
+ if (x86_pmu.arch_pebs && (caps & ARCH_PEBS_CNTR_MASK))
+ return true;
+
+ return false;
+}
+
static inline void intel_pmu_set_acr_cntr_constr(struct perf_event *event,
u64 *cause_mask, int *num)
{
@@ -4237,6 +4382,8 @@ static int intel_pmu_hw_config(struct perf_event *event)
}
if (event->attr.precise_ip) {
+ struct arch_pebs_cap pebs_cap = hybrid(event->pmu, arch_pebs_cap);
+
if ((event->attr.config & INTEL_ARCH_EVENT_MASK) == INTEL_FIXED_VLBR_EVENT)
return -EINVAL;
@@ -4250,6 +4397,15 @@ static int intel_pmu_hw_config(struct perf_event *event)
}
if (x86_pmu.pebs_aliases)
x86_pmu.pebs_aliases(event);
+
+ if (x86_pmu.arch_pebs) {
+ u64 cntr_mask = hybrid(event->pmu, intel_ctrl) &
+ ~GLOBAL_CTRL_EN_PERF_METRICS;
+ u64 pebs_mask = event->attr.precise_ip >= 3 ?
+ pebs_cap.pdists : pebs_cap.counters;
+ if (cntr_mask != pebs_mask)
+ event->hw.dyn_constraint &= pebs_mask;
+ }
}
if (needs_branch_stack(event)) {
@@ -4341,8 +4497,7 @@ static int intel_pmu_hw_config(struct perf_event *event)
}
if ((event->attr.sample_type & PERF_SAMPLE_READ) &&
- (x86_pmu.intel_cap.pebs_format >= 6) &&
- x86_pmu.intel_cap.pebs_baseline &&
+ intel_pmu_has_pebs_counter_group(event->pmu) &&
is_sampling_event(event) &&
event->attr.precise_ip)
event->group_leader->hw.flags |= PERF_X86_EVENT_PEBS_CNTR;
@@ -5212,7 +5367,13 @@ err:
static int intel_pmu_cpu_prepare(int cpu)
{
- return intel_cpuc_prepare(&per_cpu(cpu_hw_events, cpu), cpu);
+ int ret;
+
+ ret = intel_cpuc_prepare(&per_cpu(cpu_hw_events, cpu), cpu);
+ if (ret)
+ return ret;
+
+ return alloc_arch_pebs_buf_on_cpu(cpu);
}
static void flip_smm_bit(void *data)
@@ -5257,6 +5418,163 @@ static void intel_pmu_check_event_constraints(struct event_constraint *event_con
u64 fixed_cntr_mask,
u64 intel_ctrl);
+enum dyn_constr_type {
+ DYN_CONSTR_NONE,
+ DYN_CONSTR_BR_CNTR,
+ DYN_CONSTR_ACR_CNTR,
+ DYN_CONSTR_ACR_CAUSE,
+ DYN_CONSTR_PEBS,
+ DYN_CONSTR_PDIST,
+
+ DYN_CONSTR_MAX,
+};
+
+static const char * const dyn_constr_type_name[] = {
+ [DYN_CONSTR_NONE] = "a normal event",
+ [DYN_CONSTR_BR_CNTR] = "a branch counter logging event",
+ [DYN_CONSTR_ACR_CNTR] = "an auto-counter reload event",
+ [DYN_CONSTR_ACR_CAUSE] = "an auto-counter reload cause event",
+ [DYN_CONSTR_PEBS] = "a PEBS event",
+ [DYN_CONSTR_PDIST] = "a PEBS PDIST event",
+};
+
+static void __intel_pmu_check_dyn_constr(struct event_constraint *constr,
+ enum dyn_constr_type type, u64 mask)
+{
+ struct event_constraint *c1, *c2;
+ int new_weight, check_weight;
+ u64 new_mask, check_mask;
+
+ for_each_event_constraint(c1, constr) {
+ new_mask = c1->idxmsk64 & mask;
+ new_weight = hweight64(new_mask);
+
+ /* ignore topdown perf metrics event */
+ if (c1->idxmsk64 & INTEL_PMC_MSK_TOPDOWN)
+ continue;
+
+ if (!new_weight && fls64(c1->idxmsk64) < INTEL_PMC_IDX_FIXED) {
+ pr_info("The event 0x%llx is not supported as %s.\n",
+ c1->code, dyn_constr_type_name[type]);
+ }
+
+ if (new_weight <= 1)
+ continue;
+
+ for_each_event_constraint(c2, c1 + 1) {
+ bool check_fail = false;
+
+ check_mask = c2->idxmsk64 & mask;
+ check_weight = hweight64(check_mask);
+
+ if (c2->idxmsk64 & INTEL_PMC_MSK_TOPDOWN ||
+ !check_weight)
+ continue;
+
+ /* The same constraints or no overlap */
+ if (new_mask == check_mask ||
+ (new_mask ^ check_mask) == (new_mask | check_mask))
+ continue;
+
+ /*
+ * A scheduler issue may be triggered in the following cases.
+ * - Two overlap constraints have the same weight.
+ * E.g., A constraints: 0x3, B constraints: 0x6
+ * event counter failure case
+ * B PMC[2:1] 1
+ * A PMC[1:0] 0
+ * A PMC[1:0] FAIL
+ * - Two overlap constraints have different weight.
+ * The constraint has a low weight, but has high last bit.
+ * E.g., A constraints: 0x7, B constraints: 0xC
+ * event counter failure case
+ * B PMC[3:2] 2
+ * A PMC[2:0] 0
+ * A PMC[2:0] 1
+ * A PMC[2:0] FAIL
+ */
+ if (new_weight == check_weight) {
+ check_fail = true;
+ } else if (new_weight < check_weight) {
+ if ((new_mask | check_mask) != check_mask &&
+ fls64(new_mask) > fls64(check_mask))
+ check_fail = true;
+ } else {
+ if ((new_mask | check_mask) != new_mask &&
+ fls64(new_mask) < fls64(check_mask))
+ check_fail = true;
+ }
+
+ if (check_fail) {
+ pr_info("The two events 0x%llx and 0x%llx may not be "
+ "fully scheduled under some circumstances as "
+ "%s.\n",
+ c1->code, c2->code, dyn_constr_type_name[type]);
+ }
+ }
+ }
+}
+
+static void intel_pmu_check_dyn_constr(struct pmu *pmu,
+ struct event_constraint *constr,
+ u64 cntr_mask)
+{
+ enum dyn_constr_type i;
+ u64 mask;
+
+ for (i = DYN_CONSTR_NONE; i < DYN_CONSTR_MAX; i++) {
+ mask = 0;
+ switch (i) {
+ case DYN_CONSTR_NONE:
+ mask = cntr_mask;
+ break;
+ case DYN_CONSTR_BR_CNTR:
+ if (x86_pmu.flags & PMU_FL_BR_CNTR)
+ mask = x86_pmu.lbr_counters;
+ break;
+ case DYN_CONSTR_ACR_CNTR:
+ mask = hybrid(pmu, acr_cntr_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0);
+ break;
+ case DYN_CONSTR_ACR_CAUSE:
+ if (hybrid(pmu, acr_cntr_mask64) == hybrid(pmu, acr_cause_mask64))
+ continue;
+ mask = hybrid(pmu, acr_cause_mask64) & GENMASK_ULL(INTEL_PMC_MAX_GENERIC - 1, 0);
+ break;
+ case DYN_CONSTR_PEBS:
+ if (x86_pmu.arch_pebs)
+ mask = hybrid(pmu, arch_pebs_cap).counters;
+ break;
+ case DYN_CONSTR_PDIST:
+ if (x86_pmu.arch_pebs)
+ mask = hybrid(pmu, arch_pebs_cap).pdists;
+ break;
+ default:
+ pr_warn("Unsupported dynamic constraint type %d\n", i);
+ }
+
+ if (mask)
+ __intel_pmu_check_dyn_constr(constr, i, mask);
+ }
+}
+
+static void intel_pmu_check_event_constraints_all(struct pmu *pmu)
+{
+ struct event_constraint *event_constraints = hybrid(pmu, event_constraints);
+ struct event_constraint *pebs_constraints = hybrid(pmu, pebs_constraints);
+ u64 cntr_mask = hybrid(pmu, cntr_mask64);
+ u64 fixed_cntr_mask = hybrid(pmu, fixed_cntr_mask64);
+ u64 intel_ctrl = hybrid(pmu, intel_ctrl);
+
+ intel_pmu_check_event_constraints(event_constraints, cntr_mask,
+ fixed_cntr_mask, intel_ctrl);
+
+ if (event_constraints)
+ intel_pmu_check_dyn_constr(pmu, event_constraints, cntr_mask);
+
+ if (pebs_constraints)
+ intel_pmu_check_dyn_constr(pmu, pebs_constraints, cntr_mask);
+}
+
static void intel_pmu_check_extra_regs(struct extra_reg *extra_regs);
static inline bool intel_pmu_broken_perf_cap(void)
@@ -5269,34 +5587,89 @@ static inline bool intel_pmu_broken_perf_cap(void)
return false;
}
+static inline void __intel_update_pmu_caps(struct pmu *pmu)
+{
+ struct pmu *dest_pmu = pmu ? pmu : x86_get_pmu(smp_processor_id());
+
+ if (hybrid(pmu, arch_pebs_cap).caps & ARCH_PEBS_VECR_XMM)
+ dest_pmu->capabilities |= PERF_PMU_CAP_EXTENDED_REGS;
+}
+
+static inline void __intel_update_large_pebs_flags(struct pmu *pmu)
+{
+ u64 caps = hybrid(pmu, arch_pebs_cap).caps;
+
+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_TIME;
+ if (caps & ARCH_PEBS_LBR)
+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_BRANCH_STACK;
+ if (caps & ARCH_PEBS_CNTR_MASK)
+ x86_pmu.large_pebs_flags |= PERF_SAMPLE_READ;
+
+ if (!(caps & ARCH_PEBS_AUX))
+ x86_pmu.large_pebs_flags &= ~PERF_SAMPLE_DATA_SRC;
+ if (!(caps & ARCH_PEBS_GPR)) {
+ x86_pmu.large_pebs_flags &=
+ ~(PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER);
+ }
+}
+
+#define counter_mask(_gp, _fixed) ((_gp) | ((u64)(_fixed) << INTEL_PMC_IDX_FIXED))
+
static void update_pmu_cap(struct pmu *pmu)
{
- unsigned int cntr, fixed_cntr, ecx, edx;
- union cpuid35_eax eax;
- union cpuid35_ebx ebx;
+ unsigned int eax, ebx, ecx, edx;
+ union cpuid35_eax eax_0;
+ union cpuid35_ebx ebx_0;
+ u64 cntrs_mask = 0;
+ u64 pebs_mask = 0;
+ u64 pdists_mask = 0;
- cpuid(ARCH_PERFMON_EXT_LEAF, &eax.full, &ebx.full, &ecx, &edx);
+ cpuid(ARCH_PERFMON_EXT_LEAF, &eax_0.full, &ebx_0.full, &ecx, &edx);
- if (ebx.split.umask2)
+ if (ebx_0.split.umask2)
hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_UMASK2;
- if (ebx.split.eq)
+ if (ebx_0.split.eq)
hybrid(pmu, config_mask) |= ARCH_PERFMON_EVENTSEL_EQ;
- if (eax.split.cntr_subleaf) {
+ if (eax_0.split.cntr_subleaf) {
cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_NUM_COUNTER_LEAF,
- &cntr, &fixed_cntr, &ecx, &edx);
- hybrid(pmu, cntr_mask64) = cntr;
- hybrid(pmu, fixed_cntr_mask64) = fixed_cntr;
+ &eax, &ebx, &ecx, &edx);
+ hybrid(pmu, cntr_mask64) = eax;
+ hybrid(pmu, fixed_cntr_mask64) = ebx;
+ cntrs_mask = counter_mask(eax, ebx);
}
- if (eax.split.acr_subleaf) {
+ if (eax_0.split.acr_subleaf) {
cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_ACR_LEAF,
- &cntr, &fixed_cntr, &ecx, &edx);
+ &eax, &ebx, &ecx, &edx);
/* The mask of the counters which can be reloaded */
- hybrid(pmu, acr_cntr_mask64) = cntr | ((u64)fixed_cntr << INTEL_PMC_IDX_FIXED);
-
+ hybrid(pmu, acr_cntr_mask64) = counter_mask(eax, ebx);
/* The mask of the counters which can cause a reload of reloadable counters */
- hybrid(pmu, acr_cause_mask64) = ecx | ((u64)edx << INTEL_PMC_IDX_FIXED);
+ hybrid(pmu, acr_cause_mask64) = counter_mask(ecx, edx);
+ }
+
+ /* Bits[5:4] should be set simultaneously if arch-PEBS is supported */
+ if (eax_0.split.pebs_caps_subleaf && eax_0.split.pebs_cnts_subleaf) {
+ cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_CAP_LEAF,
+ &eax, &ebx, &ecx, &edx);
+ hybrid(pmu, arch_pebs_cap).caps = (u64)ebx << 32;
+
+ cpuid_count(ARCH_PERFMON_EXT_LEAF, ARCH_PERFMON_PEBS_COUNTER_LEAF,
+ &eax, &ebx, &ecx, &edx);
+ pebs_mask = counter_mask(eax, ecx);
+ pdists_mask = counter_mask(ebx, edx);
+ hybrid(pmu, arch_pebs_cap).counters = pebs_mask;
+ hybrid(pmu, arch_pebs_cap).pdists = pdists_mask;
+
+ if (WARN_ON((pebs_mask | pdists_mask) & ~cntrs_mask)) {
+ x86_pmu.arch_pebs = 0;
+ } else {
+ __intel_update_pmu_caps(pmu);
+ __intel_update_large_pebs_flags(pmu);
+ }
+ } else {
+ WARN_ON(x86_pmu.arch_pebs == 1);
+ x86_pmu.arch_pebs = 0;
}
if (!intel_pmu_broken_perf_cap()) {
@@ -5319,10 +5692,7 @@ static void intel_pmu_check_hybrid_pmus(struct x86_hybrid_pmu *pmu)
else
pmu->intel_ctrl &= ~GLOBAL_CTRL_EN_PERF_METRICS;
- intel_pmu_check_event_constraints(pmu->event_constraints,
- pmu->cntr_mask64,
- pmu->fixed_cntr_mask64,
- pmu->intel_ctrl);
+ intel_pmu_check_event_constraints_all(&pmu->pmu);
intel_pmu_check_extra_regs(pmu->extra_regs);
}
@@ -5418,6 +5788,7 @@ static void intel_pmu_cpu_starting(int cpu)
return;
init_debug_store_on_cpu(cpu);
+ init_arch_pebs_on_cpu(cpu);
/*
* Deal with CPUs that don't clear their LBRs on power-up, and that may
* even boot with LBRs enabled.
@@ -5456,6 +5827,8 @@ static void intel_pmu_cpu_starting(int cpu)
}
}
+ __intel_update_pmu_caps(cpuc->pmu);
+
if (!cpuc->shared_regs)
return;
@@ -5515,6 +5888,7 @@ static void free_excl_cntrs(struct cpu_hw_events *cpuc)
static void intel_pmu_cpu_dying(int cpu)
{
fini_debug_store_on_cpu(cpu);
+ fini_arch_pebs_on_cpu(cpu);
}
void intel_cpuc_finish(struct cpu_hw_events *cpuc)
@@ -5535,6 +5909,7 @@ static void intel_pmu_cpu_dead(int cpu)
{
struct cpu_hw_events *cpuc = &per_cpu(cpu_hw_events, cpu);
+ release_arch_pebs_buf_on_cpu(cpu);
intel_cpuc_finish(cpuc);
if (is_hybrid() && cpuc->pmu)
@@ -6250,7 +6625,7 @@ tsx_is_visible(struct kobject *kobj, struct attribute *attr, int i)
static umode_t
pebs_is_visible(struct kobject *kobj, struct attribute *attr, int i)
{
- return x86_pmu.ds_pebs ? attr->mode : 0;
+ return intel_pmu_has_pebs() ? attr->mode : 0;
}
static umode_t
@@ -6940,8 +7315,11 @@ __init int intel_pmu_init(void)
* Many features on and after V6 require dynamic constraint,
* e.g., Arch PEBS, ACR.
*/
- if (version >= 6)
+ if (version >= 6) {
x86_pmu.flags |= PMU_FL_DYN_CONSTRAINT;
+ x86_pmu.late_setup = intel_pmu_late_setup;
+ }
+
/*
* Install the hw-cache-events table:
*/
@@ -7596,6 +7974,7 @@ __init int intel_pmu_init(void)
break;
case INTEL_PANTHERLAKE_L:
+ case INTEL_WILDCATLAKE_L:
pr_cont("Pantherlake Hybrid events, ");
name = "pantherlake_hybrid";
goto lnl_common;
@@ -7726,6 +8105,14 @@ __init int intel_pmu_init(void)
if (!is_hybrid() && boot_cpu_has(X86_FEATURE_ARCH_PERFMON_EXT))
update_pmu_cap(NULL);
+ if (x86_pmu.arch_pebs) {
+ static_call_update(intel_pmu_disable_event_ext,
+ intel_pmu_disable_event_ext);
+ static_call_update(intel_pmu_enable_event_ext,
+ intel_pmu_enable_event_ext);
+ pr_cont("Architectural PEBS, ");
+ }
+
intel_pmu_check_counters_mask(&x86_pmu.cntr_mask64,
&x86_pmu.fixed_cntr_mask64,
&x86_pmu.intel_ctrl);
@@ -7734,10 +8121,8 @@ __init int intel_pmu_init(void)
if (x86_pmu.intel_cap.anythread_deprecated)
x86_pmu.format_attrs = intel_arch_formats_attr;
- intel_pmu_check_event_constraints(x86_pmu.event_constraints,
- x86_pmu.cntr_mask64,
- x86_pmu.fixed_cntr_mask64,
- x86_pmu.intel_ctrl);
+ intel_pmu_check_event_constraints_all(NULL);
+
/*
* Access LBR MSR may cause #GP under certain circumstances.
* Check all LBR MSR here.
diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index ec753e39b007..fa67fda6e45b 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -41,7 +41,7 @@
* MSR_CORE_C1_RES: CORE C1 Residency Counter
* perf code: 0x00
* Available model: SLM,AMT,GLM,CNL,ICX,TNT,ADL,RPL
- * MTL,SRF,GRR,ARL,LNL
+ * MTL,SRF,GRR,ARL,LNL,PTL
* Scope: Core (each processor core has a MSR)
* MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter
* perf code: 0x01
@@ -53,31 +53,32 @@
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,
* SKL,KNL,GLM,CNL,KBL,CML,ICL,ICX,
* TGL,TNT,RKL,ADL,RPL,SPR,MTL,SRF,
- * GRR,ARL,LNL
+ * GRR,ARL,LNL,PTL
* 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,MTL,ARL,LNL
+ * ICL,TGL,RKL,ADL,RPL,MTL,ARL,LNL,
+ * PTL
* 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,MTL,ARL,LNL,SRF
+ * RPL,SPR,MTL,ARL,LNL,SRF,PTL
* 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,MTL,ARL,LNL
+ * ADL,RPL,MTL,ARL
* 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,MTL,SRF,
- * ARL,LNL
+ * ARL,LNL,PTL
* Scope: Package (physical package)
* MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter.
* perf code: 0x03
@@ -96,7 +97,7 @@
* 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,MTL,ARL,LNL
+ * TNT,RKL,ADL,RPL,MTL,ARL,LNL,PTL
* Scope: Package (physical package)
* MSR_MODULE_C6_RES_MS: Module C6 Residency Counter.
* perf code: 0x00
@@ -522,7 +523,6 @@ static const struct cstate_model lnl_cstates __initconst = {
BIT(PERF_CSTATE_CORE_C7_RES),
.pkg_events = BIT(PERF_CSTATE_PKG_C2_RES) |
- BIT(PERF_CSTATE_PKG_C3_RES) |
BIT(PERF_CSTATE_PKG_C6_RES) |
BIT(PERF_CSTATE_PKG_C10_RES),
};
@@ -628,6 +628,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = {
X86_MATCH_VFM(INTEL_ATOM_GRACEMONT, &adl_cstates),
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, &srf_cstates),
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT, &grr_cstates),
+ X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, &srf_cstates),
X86_MATCH_VFM(INTEL_ICELAKE_L, &icl_cstates),
X86_MATCH_VFM(INTEL_ICELAKE, &icl_cstates),
@@ -652,6 +653,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = {
X86_MATCH_VFM(INTEL_ARROWLAKE_H, &adl_cstates),
X86_MATCH_VFM(INTEL_ARROWLAKE_U, &adl_cstates),
X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_cstates),
+ X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &lnl_cstates),
{ },
};
MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match);
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index c0b7ac1c7594..feb1c3cf63e4 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -317,7 +317,8 @@ static u64 __grt_latency_data(struct perf_event *event, u64 status,
{
u64 val;
- WARN_ON_ONCE(hybrid_pmu(event->pmu)->pmu_type == hybrid_big);
+ WARN_ON_ONCE(is_hybrid() &&
+ hybrid_pmu(event->pmu)->pmu_type == hybrid_big);
dse &= PERF_PEBS_DATA_SOURCE_GRT_MASK;
val = hybrid_var(event->pmu, pebs_data_source)[dse];
@@ -625,13 +626,18 @@ static int alloc_pebs_buffer(int cpu)
int max, node = cpu_to_node(cpu);
void *buffer, *insn_buff, *cea;
- if (!x86_pmu.ds_pebs)
+ if (!intel_pmu_has_pebs())
return 0;
buffer = dsalloc_pages(bsiz, GFP_KERNEL, cpu);
if (unlikely(!buffer))
return -ENOMEM;
+ if (x86_pmu.arch_pebs) {
+ hwev->pebs_vaddr = buffer;
+ return 0;
+ }
+
/*
* HSW+ already provides us the eventing ip; no need to allocate this
* buffer then.
@@ -644,7 +650,7 @@ static int alloc_pebs_buffer(int cpu)
}
per_cpu(insn_buffer, cpu) = insn_buff;
}
- hwev->ds_pebs_vaddr = buffer;
+ hwev->pebs_vaddr = buffer;
/* Update the cpu entry area mapping */
cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer;
ds->pebs_buffer_base = (unsigned long) cea;
@@ -660,17 +666,20 @@ static void release_pebs_buffer(int cpu)
struct cpu_hw_events *hwev = per_cpu_ptr(&cpu_hw_events, cpu);
void *cea;
- if (!x86_pmu.ds_pebs)
+ if (!intel_pmu_has_pebs())
return;
- kfree(per_cpu(insn_buffer, cpu));
- per_cpu(insn_buffer, cpu) = NULL;
+ if (x86_pmu.ds_pebs) {
+ kfree(per_cpu(insn_buffer, cpu));
+ per_cpu(insn_buffer, cpu) = NULL;
- /* Clear the fixmap */
- cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer;
- ds_clear_cea(cea, x86_pmu.pebs_buffer_size);
- dsfree_pages(hwev->ds_pebs_vaddr, x86_pmu.pebs_buffer_size);
- hwev->ds_pebs_vaddr = NULL;
+ /* Clear the fixmap */
+ cea = &get_cpu_entry_area(cpu)->cpu_debug_buffers.pebs_buffer;
+ ds_clear_cea(cea, x86_pmu.pebs_buffer_size);
+ }
+
+ dsfree_pages(hwev->pebs_vaddr, x86_pmu.pebs_buffer_size);
+ hwev->pebs_vaddr = NULL;
}
static int alloc_bts_buffer(int cpu)
@@ -823,6 +832,56 @@ void reserve_ds_buffers(void)
}
}
+inline int alloc_arch_pebs_buf_on_cpu(int cpu)
+{
+ if (!x86_pmu.arch_pebs)
+ return 0;
+
+ return alloc_pebs_buffer(cpu);
+}
+
+inline void release_arch_pebs_buf_on_cpu(int cpu)
+{
+ if (!x86_pmu.arch_pebs)
+ return;
+
+ release_pebs_buffer(cpu);
+}
+
+void init_arch_pebs_on_cpu(int cpu)
+{
+ struct cpu_hw_events *cpuc = per_cpu_ptr(&cpu_hw_events, cpu);
+ u64 arch_pebs_base;
+
+ if (!x86_pmu.arch_pebs)
+ return;
+
+ if (!cpuc->pebs_vaddr) {
+ WARN(1, "Fail to allocate PEBS buffer on CPU %d\n", cpu);
+ x86_pmu.pebs_active = 0;
+ return;
+ }
+
+ /*
+ * 4KB-aligned pointer of the output buffer
+ * (__alloc_pages_node() return page aligned address)
+ * Buffer Size = 4KB * 2^SIZE
+ * contiguous physical buffer (__alloc_pages_node() with order)
+ */
+ arch_pebs_base = virt_to_phys(cpuc->pebs_vaddr) | PEBS_BUFFER_SHIFT;
+ wrmsr_on_cpu(cpu, MSR_IA32_PEBS_BASE, (u32)arch_pebs_base,
+ (u32)(arch_pebs_base >> 32));
+ x86_pmu.pebs_active = 1;
+}
+
+inline void fini_arch_pebs_on_cpu(int cpu)
+{
+ if (!x86_pmu.arch_pebs)
+ return;
+
+ wrmsr_on_cpu(cpu, MSR_IA32_PEBS_BASE, 0, 0);
+}
+
/*
* BTS
*/
@@ -1470,6 +1529,25 @@ pebs_update_state(bool needed_cb, struct cpu_hw_events *cpuc,
}
}
+u64 intel_get_arch_pebs_data_config(struct perf_event *event)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ u64 pebs_data_cfg = 0;
+ u64 cntr_mask;
+
+ if (WARN_ON(event->hw.idx < 0 || event->hw.idx >= X86_PMC_IDX_MAX))
+ return 0;
+
+ pebs_data_cfg |= pebs_update_adaptive_cfg(event);
+
+ cntr_mask = (PEBS_DATACFG_CNTR_MASK << PEBS_DATACFG_CNTR_SHIFT) |
+ (PEBS_DATACFG_FIX_MASK << PEBS_DATACFG_FIX_SHIFT) |
+ PEBS_DATACFG_CNTR | PEBS_DATACFG_METRICS;
+ pebs_data_cfg |= cpuc->pebs_data_cfg & cntr_mask;
+
+ return pebs_data_cfg;
+}
+
void intel_pmu_pebs_add(struct perf_event *event)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
@@ -1531,6 +1609,15 @@ static inline void intel_pmu_drain_large_pebs(struct cpu_hw_events *cpuc)
intel_pmu_drain_pebs_buffer();
}
+static void __intel_pmu_pebs_enable(struct perf_event *event)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+
+ hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT;
+ cpuc->pebs_enabled |= 1ULL << hwc->idx;
+}
+
void intel_pmu_pebs_enable(struct perf_event *event)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
@@ -1539,9 +1626,7 @@ void intel_pmu_pebs_enable(struct perf_event *event)
struct debug_store *ds = cpuc->ds;
unsigned int idx = hwc->idx;
- hwc->config &= ~ARCH_PERFMON_EVENTSEL_INT;
-
- cpuc->pebs_enabled |= 1ULL << hwc->idx;
+ __intel_pmu_pebs_enable(event);
if ((event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT) && (x86_pmu.version < 5))
cpuc->pebs_enabled |= 1ULL << (hwc->idx + 32);
@@ -1603,14 +1688,22 @@ void intel_pmu_pebs_del(struct perf_event *event)
pebs_update_state(needed_cb, cpuc, event, false);
}
-void intel_pmu_pebs_disable(struct perf_event *event)
+static void __intel_pmu_pebs_disable(struct perf_event *event)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
struct hw_perf_event *hwc = &event->hw;
intel_pmu_drain_large_pebs(cpuc);
-
cpuc->pebs_enabled &= ~(1ULL << hwc->idx);
+ hwc->config |= ARCH_PERFMON_EVENTSEL_INT;
+}
+
+void intel_pmu_pebs_disable(struct perf_event *event)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+
+ __intel_pmu_pebs_disable(event);
if ((event->hw.flags & PERF_X86_EVENT_PEBS_LDLAT) &&
(x86_pmu.version < 5))
@@ -1622,8 +1715,6 @@ void intel_pmu_pebs_disable(struct perf_event *event)
if (cpuc->enabled)
wrmsrq(MSR_IA32_PEBS_ENABLE, cpuc->pebs_enabled);
-
- hwc->config |= ARCH_PERFMON_EVENTSEL_INT;
}
void intel_pmu_pebs_enable_all(void)
@@ -2059,6 +2150,90 @@ static inline void __setup_pebs_counter_group(struct cpu_hw_events *cpuc,
#define PEBS_LATENCY_MASK 0xffff
+static inline void __setup_perf_sample_data(struct perf_event *event,
+ struct pt_regs *iregs,
+ struct perf_sample_data *data)
+{
+ perf_sample_data_init(data, 0, event->hw.last_period);
+
+ /*
+ * We must however always use iregs for the unwinder to stay sane; the
+ * record BP,SP,IP can point into thin air when the record is from a
+ * previous PMI context or an (I)RET happened between the record and
+ * PMI.
+ */
+ perf_sample_save_callchain(data, event, iregs);
+}
+
+static inline void __setup_pebs_basic_group(struct perf_event *event,
+ struct pt_regs *regs,
+ struct perf_sample_data *data,
+ u64 sample_type, u64 ip,
+ u64 tsc, u16 retire)
+{
+ /* The ip in basic is EventingIP */
+ set_linear_ip(regs, ip);
+ regs->flags = PERF_EFLAGS_EXACT;
+ setup_pebs_time(event, data, tsc);
+
+ if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT)
+ data->weight.var3_w = retire;
+}
+
+static inline void __setup_pebs_gpr_group(struct perf_event *event,
+ struct pt_regs *regs,
+ struct pebs_gprs *gprs,
+ u64 sample_type)
+{
+ if (event->attr.precise_ip < 2) {
+ set_linear_ip(regs, gprs->ip);
+ regs->flags &= ~PERF_EFLAGS_EXACT;
+ }
+
+ if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER))
+ adaptive_pebs_save_regs(regs, gprs);
+}
+
+static inline void __setup_pebs_meminfo_group(struct perf_event *event,
+ struct perf_sample_data *data,
+ u64 sample_type, u64 latency,
+ u16 instr_latency, u64 address,
+ u64 aux, u64 tsx_tuning, u64 ax)
+{
+ if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) {
+ u64 tsx_latency = intel_get_tsx_weight(tsx_tuning);
+
+ data->weight.var2_w = instr_latency;
+
+ /*
+ * Although meminfo::latency is defined as a u64,
+ * only the lower 32 bits include the valid data
+ * in practice on Ice Lake and earlier platforms.
+ */
+ if (sample_type & PERF_SAMPLE_WEIGHT)
+ data->weight.full = latency ?: tsx_latency;
+ else
+ data->weight.var1_dw = (u32)latency ?: tsx_latency;
+
+ data->sample_flags |= PERF_SAMPLE_WEIGHT_TYPE;
+ }
+
+ if (sample_type & PERF_SAMPLE_DATA_SRC) {
+ data->data_src.val = get_data_src(event, aux);
+ data->sample_flags |= PERF_SAMPLE_DATA_SRC;
+ }
+
+ if (sample_type & PERF_SAMPLE_ADDR_TYPE) {
+ data->addr = address;
+ data->sample_flags |= PERF_SAMPLE_ADDR;
+ }
+
+ if (sample_type & PERF_SAMPLE_TRANSACTION) {
+ data->txn = intel_get_tsx_transaction(tsx_tuning, ax);
+ data->sample_flags |= PERF_SAMPLE_TRANSACTION;
+ }
+}
+
/*
* With adaptive PEBS the layout depends on what fields are configured.
*/
@@ -2068,12 +2243,14 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
struct pt_regs *regs)
{
struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ u64 sample_type = event->attr.sample_type;
struct pebs_basic *basic = __pebs;
void *next_record = basic + 1;
- u64 sample_type, format_group;
struct pebs_meminfo *meminfo = NULL;
struct pebs_gprs *gprs = NULL;
struct x86_perf_regs *perf_regs;
+ u64 format_group;
+ u16 retire;
if (basic == NULL)
return;
@@ -2081,31 +2258,17 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
perf_regs = container_of(regs, struct x86_perf_regs, regs);
perf_regs->xmm_regs = NULL;
- sample_type = event->attr.sample_type;
format_group = basic->format_group;
- perf_sample_data_init(data, 0, event->hw.last_period);
- setup_pebs_time(event, data, basic->tsc);
-
- /*
- * We must however always use iregs for the unwinder to stay sane; the
- * record BP,SP,IP can point into thin air when the record is from a
- * previous PMI context or an (I)RET happened between the record and
- * PMI.
- */
- perf_sample_save_callchain(data, event, iregs);
+ __setup_perf_sample_data(event, iregs, data);
*regs = *iregs;
- /* The ip in basic is EventingIP */
- set_linear_ip(regs, basic->ip);
- regs->flags = PERF_EFLAGS_EXACT;
- if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT) {
- if (x86_pmu.flags & PMU_FL_RETIRE_LATENCY)
- data->weight.var3_w = basic->retire_latency;
- else
- data->weight.var3_w = 0;
- }
+ /* basic group */
+ retire = x86_pmu.flags & PMU_FL_RETIRE_LATENCY ?
+ basic->retire_latency : 0;
+ __setup_pebs_basic_group(event, regs, data, sample_type,
+ basic->ip, basic->tsc, retire);
/*
* The record for MEMINFO is in front of GP
@@ -2121,54 +2284,20 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
gprs = next_record;
next_record = gprs + 1;
- if (event->attr.precise_ip < 2) {
- set_linear_ip(regs, gprs->ip);
- regs->flags &= ~PERF_EFLAGS_EXACT;
- }
-
- if (sample_type & (PERF_SAMPLE_REGS_INTR | PERF_SAMPLE_REGS_USER))
- adaptive_pebs_save_regs(regs, gprs);
+ __setup_pebs_gpr_group(event, regs, gprs, sample_type);
}
if (format_group & PEBS_DATACFG_MEMINFO) {
- if (sample_type & PERF_SAMPLE_WEIGHT_TYPE) {
- u64 latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ?
- meminfo->cache_latency : meminfo->mem_latency;
-
- if (x86_pmu.flags & PMU_FL_INSTR_LATENCY)
- data->weight.var2_w = meminfo->instr_latency;
-
- /*
- * Although meminfo::latency is defined as a u64,
- * only the lower 32 bits include the valid data
- * in practice on Ice Lake and earlier platforms.
- */
- if (sample_type & PERF_SAMPLE_WEIGHT) {
- data->weight.full = latency ?:
- intel_get_tsx_weight(meminfo->tsx_tuning);
- } else {
- data->weight.var1_dw = (u32)latency ?:
- intel_get_tsx_weight(meminfo->tsx_tuning);
- }
-
- data->sample_flags |= PERF_SAMPLE_WEIGHT_TYPE;
- }
-
- if (sample_type & PERF_SAMPLE_DATA_SRC) {
- data->data_src.val = get_data_src(event, meminfo->aux);
- data->sample_flags |= PERF_SAMPLE_DATA_SRC;
- }
-
- if (sample_type & PERF_SAMPLE_ADDR_TYPE) {
- data->addr = meminfo->address;
- data->sample_flags |= PERF_SAMPLE_ADDR;
- }
-
- if (sample_type & PERF_SAMPLE_TRANSACTION) {
- data->txn = intel_get_tsx_transaction(meminfo->tsx_tuning,
- gprs ? gprs->ax : 0);
- data->sample_flags |= PERF_SAMPLE_TRANSACTION;
- }
+ u64 latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ?
+ meminfo->cache_latency : meminfo->mem_latency;
+ u64 instr_latency = x86_pmu.flags & PMU_FL_INSTR_LATENCY ?
+ meminfo->instr_latency : 0;
+ u64 ax = gprs ? gprs->ax : 0;
+
+ __setup_pebs_meminfo_group(event, data, sample_type, latency,
+ instr_latency, meminfo->address,
+ meminfo->aux, meminfo->tsx_tuning,
+ ax);
}
if (format_group & PEBS_DATACFG_XMMS) {
@@ -2219,6 +2348,135 @@ static void setup_pebs_adaptive_sample_data(struct perf_event *event,
format_group);
}
+static inline bool arch_pebs_record_continued(struct arch_pebs_header *header)
+{
+ /* Continue bit or null PEBS record indicates fragment follows. */
+ return header->cont || !(header->format & GENMASK_ULL(63, 16));
+}
+
+static void setup_arch_pebs_sample_data(struct perf_event *event,
+ struct pt_regs *iregs,
+ void *__pebs,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ u64 sample_type = event->attr.sample_type;
+ struct arch_pebs_header *header = NULL;
+ struct arch_pebs_aux *meminfo = NULL;
+ struct arch_pebs_gprs *gprs = NULL;
+ struct x86_perf_regs *perf_regs;
+ void *next_record;
+ void *at = __pebs;
+
+ if (at == NULL)
+ return;
+
+ perf_regs = container_of(regs, struct x86_perf_regs, regs);
+ perf_regs->xmm_regs = NULL;
+
+ __setup_perf_sample_data(event, iregs, data);
+
+ *regs = *iregs;
+
+again:
+ header = at;
+ next_record = at + sizeof(struct arch_pebs_header);
+ if (header->basic) {
+ struct arch_pebs_basic *basic = next_record;
+ u16 retire = 0;
+
+ next_record = basic + 1;
+
+ if (sample_type & PERF_SAMPLE_WEIGHT_STRUCT)
+ retire = basic->valid ? basic->retire : 0;
+ __setup_pebs_basic_group(event, regs, data, sample_type,
+ basic->ip, basic->tsc, retire);
+ }
+
+ /*
+ * The record for MEMINFO is in front of GP
+ * But PERF_SAMPLE_TRANSACTION needs gprs->ax.
+ * Save the pointer here but process later.
+ */
+ if (header->aux) {
+ meminfo = next_record;
+ next_record = meminfo + 1;
+ }
+
+ if (header->gpr) {
+ gprs = next_record;
+ next_record = gprs + 1;
+
+ __setup_pebs_gpr_group(event, regs,
+ (struct pebs_gprs *)gprs,
+ sample_type);
+ }
+
+ if (header->aux) {
+ u64 ax = gprs ? gprs->ax : 0;
+
+ __setup_pebs_meminfo_group(event, data, sample_type,
+ meminfo->cache_latency,
+ meminfo->instr_latency,
+ meminfo->address, meminfo->aux,
+ meminfo->tsx_tuning, ax);
+ }
+
+ if (header->xmm) {
+ struct pebs_xmm *xmm;
+
+ next_record += sizeof(struct arch_pebs_xer_header);
+
+ xmm = next_record;
+ perf_regs->xmm_regs = xmm->xmm;
+ next_record = xmm + 1;
+ }
+
+ if (header->lbr) {
+ struct arch_pebs_lbr_header *lbr_header = next_record;
+ struct lbr_entry *lbr;
+ int num_lbr;
+
+ next_record = lbr_header + 1;
+ lbr = next_record;
+
+ num_lbr = header->lbr == ARCH_PEBS_LBR_NUM_VAR ?
+ lbr_header->depth :
+ header->lbr * ARCH_PEBS_BASE_LBR_ENTRIES;
+ next_record += num_lbr * sizeof(struct lbr_entry);
+
+ if (has_branch_stack(event)) {
+ intel_pmu_store_pebs_lbrs(lbr);
+ intel_pmu_lbr_save_brstack(data, cpuc, event);
+ }
+ }
+
+ if (header->cntr) {
+ struct arch_pebs_cntr_header *cntr = next_record;
+ unsigned int nr;
+
+ next_record += sizeof(struct arch_pebs_cntr_header);
+
+ if (is_pebs_counter_event_group(event)) {
+ __setup_pebs_counter_group(cpuc, event,
+ (struct pebs_cntr_header *)cntr, next_record);
+ data->sample_flags |= PERF_SAMPLE_READ;
+ }
+
+ nr = hweight32(cntr->cntr) + hweight32(cntr->fixed);
+ if (cntr->metrics == INTEL_CNTR_METRICS)
+ nr += 2;
+ next_record += nr * sizeof(u64);
+ }
+
+ /* Parse followed fragments if there are. */
+ if (arch_pebs_record_continued(header)) {
+ at = at + header->size;
+ goto again;
+ }
+}
+
static inline void *
get_next_pebs_record_by_bit(void *base, void *top, int bit)
{
@@ -2601,6 +2859,57 @@ static void intel_pmu_drain_pebs_nhm(struct pt_regs *iregs, struct perf_sample_d
}
}
+static __always_inline void
+__intel_pmu_handle_pebs_record(struct pt_regs *iregs,
+ struct pt_regs *regs,
+ struct perf_sample_data *data,
+ void *at, u64 pebs_status,
+ short *counts, void **last,
+ setup_fn setup_sample)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ struct perf_event *event;
+ int bit;
+
+ for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) {
+ event = cpuc->events[bit];
+
+ if (WARN_ON_ONCE(!event) ||
+ WARN_ON_ONCE(!event->attr.precise_ip))
+ continue;
+
+ if (counts[bit]++) {
+ __intel_pmu_pebs_event(event, iregs, regs, data,
+ last[bit], setup_sample);
+ }
+
+ last[bit] = at;
+ }
+}
+
+static __always_inline void
+__intel_pmu_handle_last_pebs_record(struct pt_regs *iregs,
+ struct pt_regs *regs,
+ struct perf_sample_data *data,
+ u64 mask, short *counts, void **last,
+ setup_fn setup_sample)
+{
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ struct perf_event *event;
+ int bit;
+
+ for_each_set_bit(bit, (unsigned long *)&mask, X86_PMC_IDX_MAX) {
+ if (!counts[bit])
+ continue;
+
+ event = cpuc->events[bit];
+
+ __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit],
+ counts[bit], setup_sample);
+ }
+
+}
+
static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_data *data)
{
short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {};
@@ -2610,9 +2919,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
struct x86_perf_regs perf_regs;
struct pt_regs *regs = &perf_regs.regs;
struct pebs_basic *basic;
- struct perf_event *event;
void *base, *at, *top;
- int bit;
u64 mask;
if (!x86_pmu.pebs_active)
@@ -2625,6 +2932,7 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
mask = hybrid(cpuc->pmu, pebs_events_mask) |
(hybrid(cpuc->pmu, fixed_cntr_mask64) << INTEL_PMC_IDX_FIXED);
+ mask &= cpuc->pebs_enabled;
if (unlikely(base >= top)) {
intel_pmu_pebs_event_update_no_drain(cpuc, mask);
@@ -2642,38 +2950,114 @@ static void intel_pmu_drain_pebs_icl(struct pt_regs *iregs, struct perf_sample_d
if (basic->format_size != cpuc->pebs_record_size)
continue;
- pebs_status = basic->applicable_counters & cpuc->pebs_enabled & mask;
- for_each_set_bit(bit, (unsigned long *)&pebs_status, X86_PMC_IDX_MAX) {
- event = cpuc->events[bit];
+ pebs_status = mask & basic->applicable_counters;
+ __intel_pmu_handle_pebs_record(iregs, regs, data, at,
+ pebs_status, counts, last,
+ setup_pebs_adaptive_sample_data);
+ }
- if (WARN_ON_ONCE(!event) ||
- WARN_ON_ONCE(!event->attr.precise_ip))
- continue;
+ __intel_pmu_handle_last_pebs_record(iregs, regs, data, mask, counts, last,
+ setup_pebs_adaptive_sample_data);
+}
- if (counts[bit]++) {
- __intel_pmu_pebs_event(event, iregs, regs, data, last[bit],
- setup_pebs_adaptive_sample_data);
- }
- last[bit] = at;
- }
+static void intel_pmu_drain_arch_pebs(struct pt_regs *iregs,
+ struct perf_sample_data *data)
+{
+ short counts[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS] = {};
+ void *last[INTEL_PMC_IDX_FIXED + MAX_FIXED_PEBS_EVENTS];
+ struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events);
+ union arch_pebs_index index;
+ struct x86_perf_regs perf_regs;
+ struct pt_regs *regs = &perf_regs.regs;
+ void *base, *at, *top;
+ u64 mask;
+
+ rdmsrq(MSR_IA32_PEBS_INDEX, index.whole);
+
+ if (unlikely(!index.wr)) {
+ intel_pmu_pebs_event_update_no_drain(cpuc, X86_PMC_IDX_MAX);
+ return;
}
- for_each_set_bit(bit, (unsigned long *)&mask, X86_PMC_IDX_MAX) {
- if (!counts[bit])
+ base = cpuc->pebs_vaddr;
+ top = cpuc->pebs_vaddr + (index.wr << ARCH_PEBS_INDEX_WR_SHIFT);
+
+ index.wr = 0;
+ index.full = 0;
+ index.en = 1;
+ if (cpuc->n_pebs == cpuc->n_large_pebs)
+ index.thresh = ARCH_PEBS_THRESH_MULTI;
+ else
+ index.thresh = ARCH_PEBS_THRESH_SINGLE;
+ wrmsrq(MSR_IA32_PEBS_INDEX, index.whole);
+
+ mask = hybrid(cpuc->pmu, arch_pebs_cap).counters & cpuc->pebs_enabled;
+
+ if (!iregs)
+ iregs = &dummy_iregs;
+
+ /* Process all but the last event for each counter. */
+ for (at = base; at < top;) {
+ struct arch_pebs_header *header;
+ struct arch_pebs_basic *basic;
+ u64 pebs_status;
+
+ header = at;
+
+ if (WARN_ON_ONCE(!header->size))
+ break;
+
+ /* 1st fragment or single record must have basic group */
+ if (!header->basic) {
+ at += header->size;
continue;
+ }
- event = cpuc->events[bit];
+ basic = at + sizeof(struct arch_pebs_header);
+ pebs_status = mask & basic->applicable_counters;
+ __intel_pmu_handle_pebs_record(iregs, regs, data, at,
+ pebs_status, counts, last,
+ setup_arch_pebs_sample_data);
+
+ /* Skip non-last fragments */
+ while (arch_pebs_record_continued(header)) {
+ if (!header->size)
+ break;
+ at += header->size;
+ header = at;
+ }
- __intel_pmu_pebs_last_event(event, iregs, regs, data, last[bit],
- counts[bit], setup_pebs_adaptive_sample_data);
+ /* Skip last fragment or the single record */
+ at += header->size;
}
+
+ __intel_pmu_handle_last_pebs_record(iregs, regs, data, mask,
+ counts, last,
+ setup_arch_pebs_sample_data);
+}
+
+static void __init intel_arch_pebs_init(void)
+{
+ /*
+ * Current hybrid platforms always both support arch-PEBS or not
+ * on all kinds of cores. So directly set x86_pmu.arch_pebs flag
+ * if boot cpu supports arch-PEBS.
+ */
+ x86_pmu.arch_pebs = 1;
+ x86_pmu.pebs_buffer_size = PEBS_BUFFER_SIZE;
+ x86_pmu.drain_pebs = intel_pmu_drain_arch_pebs;
+ x86_pmu.pebs_capable = ~0ULL;
+ x86_pmu.flags |= PMU_FL_PEBS_ALL;
+
+ x86_pmu.pebs_enable = __intel_pmu_pebs_enable;
+ x86_pmu.pebs_disable = __intel_pmu_pebs_disable;
}
/*
* PEBS probe and setup
*/
-void __init intel_pebs_init(void)
+static void __init intel_ds_pebs_init(void)
{
/*
* No support for 32bit formats
@@ -2735,10 +3119,8 @@ void __init intel_pebs_init(void)
break;
case 6:
- if (x86_pmu.intel_cap.pebs_baseline) {
+ if (x86_pmu.intel_cap.pebs_baseline)
x86_pmu.large_pebs_flags |= PERF_SAMPLE_READ;
- x86_pmu.late_setup = intel_pmu_late_setup;
- }
fallthrough;
case 5:
x86_pmu.pebs_ept = 1;
@@ -2788,6 +3170,14 @@ void __init intel_pebs_init(void)
}
}
+void __init intel_pebs_init(void)
+{
+ if (x86_pmu.intel_cap.pebs_format == 0xf)
+ intel_arch_pebs_init();
+ else
+ intel_ds_pebs_init();
+}
+
void perf_restore_debug_store(void)
{
struct debug_store *ds = __this_cpu_read(cpu_hw_events.ds);
diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c
index 7aa59966e7c3..72f2adcda7c6 100644
--- a/arch/x86/events/intel/lbr.c
+++ b/arch/x86/events/intel/lbr.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/kvm_types.h>
#include <linux/perf_event.h>
#include <linux/types.h>
@@ -1705,7 +1706,7 @@ void x86_perf_get_lbr(struct x86_pmu_lbr *lbr)
lbr->info = x86_pmu.lbr_info;
lbr->has_callstack = x86_pmu_has_lbr_callstack();
}
-EXPORT_SYMBOL_GPL(x86_perf_get_lbr);
+EXPORT_SYMBOL_FOR_KVM(x86_perf_get_lbr);
struct event_constraint vlbr_constraint =
__EVENT_CONSTRAINT(INTEL_FIXED_VLBR_EVENT, (1ULL << INTEL_PMC_IDX_FIXED_VLBR),
diff --git a/arch/x86/events/intel/pt.c b/arch/x86/events/intel/pt.c
index e8cf29d2b10c..44524a387c58 100644
--- a/arch/x86/events/intel/pt.c
+++ b/arch/x86/events/intel/pt.c
@@ -17,6 +17,7 @@
#include <linux/limits.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/kvm_types.h>
#include <asm/cpuid/api.h>
#include <asm/perf_event.h>
@@ -82,13 +83,13 @@ u32 intel_pt_validate_cap(u32 *caps, enum pt_capabilities capability)
return (c & cd->mask) >> shift;
}
-EXPORT_SYMBOL_GPL(intel_pt_validate_cap);
+EXPORT_SYMBOL_FOR_KVM(intel_pt_validate_cap);
u32 intel_pt_validate_hw_cap(enum pt_capabilities cap)
{
return intel_pt_validate_cap(pt_pmu.caps, cap);
}
-EXPORT_SYMBOL_GPL(intel_pt_validate_hw_cap);
+EXPORT_SYMBOL_FOR_KVM(intel_pt_validate_hw_cap);
static ssize_t pt_cap_show(struct device *cdev,
struct device_attribute *attr,
@@ -1590,7 +1591,7 @@ void intel_pt_handle_vmx(int on)
local_irq_restore(flags);
}
-EXPORT_SYMBOL_GPL(intel_pt_handle_vmx);
+EXPORT_SYMBOL_FOR_KVM(intel_pt_handle_vmx);
/*
* PMU callbacks
diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index a762f7f5b161..e228e564b15e 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -1325,8 +1325,6 @@ static void uncore_pci_sub_driver_init(void)
continue;
pmu = &type->pmus[UNCORE_PCI_DEV_IDX(ids->driver_data)];
- if (!pmu)
- continue;
if (uncore_pci_get_dev_die_info(pci_sub_dev, &die))
continue;
@@ -1895,6 +1893,7 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = {
X86_MATCH_VFM(INTEL_ARROWLAKE_H, &mtl_uncore_init),
X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_uncore_init),
X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_uncore_init),
+ X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &ptl_uncore_init),
X86_MATCH_VFM(INTEL_SAPPHIRERAPIDS_X, &spr_uncore_init),
X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, &spr_uncore_init),
X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, &gnr_uncore_init),
diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h
index 2b969386dcdd..3161ec0a3416 100644
--- a/arch/x86/events/perf_event.h
+++ b/arch/x86/events/perf_event.h
@@ -283,8 +283,9 @@ struct cpu_hw_events {
* Intel DebugStore bits
*/
struct debug_store *ds;
- void *ds_pebs_vaddr;
void *ds_bts_vaddr;
+ /* DS based PEBS or arch-PEBS buffer address */
+ void *pebs_vaddr;
u64 pebs_enabled;
int n_pebs;
int n_large_pebs;
@@ -303,6 +304,8 @@ struct cpu_hw_events {
/* Intel ACR configuration */
u64 acr_cfg_b[X86_PMC_IDX_MAX];
u64 acr_cfg_c[X86_PMC_IDX_MAX];
+ /* Cached CFG_C values */
+ u64 cfg_c_val[X86_PMC_IDX_MAX];
/*
* Intel LBR bits
@@ -708,6 +711,12 @@ enum hybrid_pmu_type {
hybrid_big_small_tiny = hybrid_big | hybrid_small_tiny,
};
+struct arch_pebs_cap {
+ u64 caps;
+ u64 counters;
+ u64 pdists;
+};
+
struct x86_hybrid_pmu {
struct pmu pmu;
const char *name;
@@ -752,6 +761,8 @@ struct x86_hybrid_pmu {
mid_ack :1,
enabled_ack :1;
+ struct arch_pebs_cap arch_pebs_cap;
+
u64 pebs_data_source[PERF_PEBS_DATA_SOURCE_MAX];
};
@@ -906,7 +917,7 @@ struct x86_pmu {
union perf_capabilities intel_cap;
/*
- * Intel DebugStore bits
+ * Intel DebugStore and PEBS bits
*/
unsigned int bts :1,
bts_active :1,
@@ -917,7 +928,8 @@ struct x86_pmu {
pebs_no_tlb :1,
pebs_no_isolation :1,
pebs_block :1,
- pebs_ept :1;
+ pebs_ept :1,
+ arch_pebs :1;
int pebs_record_size;
int pebs_buffer_size;
u64 pebs_events_mask;
@@ -930,6 +942,11 @@ struct x86_pmu {
u64 pebs_capable;
/*
+ * Intel Architectural PEBS
+ */
+ struct arch_pebs_cap arch_pebs_cap;
+
+ /*
* Intel LBR
*/
unsigned int lbr_tos, lbr_from, lbr_to,
@@ -1124,7 +1141,6 @@ static struct perf_pmu_format_hybrid_attr format_attr_hybrid_##_name = {\
.pmu_type = _pmu, \
}
-int is_x86_event(struct perf_event *event);
struct pmu *x86_get_pmu(unsigned int cpu);
extern struct x86_pmu x86_pmu __read_mostly;
@@ -1217,7 +1233,7 @@ int x86_reserve_hardware(void);
void x86_release_hardware(void);
-int x86_pmu_max_precise(void);
+int x86_pmu_max_precise(struct pmu *pmu);
void hw_perf_lbr_event_destroy(struct perf_event *event);
@@ -1604,6 +1620,14 @@ extern void intel_cpuc_finish(struct cpu_hw_events *cpuc);
int intel_pmu_init(void);
+int alloc_arch_pebs_buf_on_cpu(int cpu);
+
+void release_arch_pebs_buf_on_cpu(int cpu);
+
+void init_arch_pebs_on_cpu(int cpu);
+
+void fini_arch_pebs_on_cpu(int cpu);
+
void init_debug_store_on_cpu(int cpu);
void fini_debug_store_on_cpu(int cpu);
@@ -1760,6 +1784,8 @@ void intel_pmu_pebs_data_source_cmt(void);
void intel_pmu_pebs_data_source_lnl(void);
+u64 intel_get_arch_pebs_data_config(struct perf_event *event);
+
int intel_pmu_setup_lbr_filter(struct perf_event *event);
void intel_pt_interrupt(void);
@@ -1792,6 +1818,11 @@ static inline int intel_pmu_max_num_pebs(struct pmu *pmu)
return fls((u32)hybrid(pmu, pebs_events_mask));
}
+static inline bool intel_pmu_has_pebs(void)
+{
+ return x86_pmu.ds_pebs || x86_pmu.arch_pebs;
+}
+
#else /* CONFIG_CPU_SUP_INTEL */
static inline void reserve_ds_buffers(void)
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index 15bc07a5ebb3..b14c045679e1 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -198,6 +198,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
#define ALTINSTR_ENTRY(ft_flags) \
".pushsection .altinstructions,\"a\"\n" \
+ ANNOTATE_DATA_SPECIAL \
" .long 771b - .\n" /* label */ \
" .long 774f - .\n" /* new instruction */ \
" .4byte " __stringify(ft_flags) "\n" /* feature + flags */ \
@@ -207,6 +208,7 @@ static inline int alternatives_text_reserved(void *start, void *end)
#define ALTINSTR_REPLACEMENT(newinstr) /* replacement */ \
".pushsection .altinstr_replacement, \"ax\"\n" \
+ ANNOTATE_DATA_SPECIAL \
"# ALT: replacement\n" \
"774:\n\t" newinstr "\n775:\n" \
".popsection\n"
@@ -337,6 +339,7 @@ void nop_func(void);
* instruction. See apply_alternatives().
*/
.macro altinstr_entry orig alt ft_flags orig_len alt_len
+ ANNOTATE_DATA_SPECIAL
.long \orig - .
.long \alt - .
.4byte \ft_flags
@@ -365,6 +368,7 @@ void nop_func(void);
.popsection ; \
.pushsection .altinstr_replacement,"ax" ; \
743: \
+ ANNOTATE_DATA_SPECIAL ; \
newinst ; \
744: \
.popsection ;
diff --git a/arch/x86/include/asm/amd/node.h b/arch/x86/include/asm/amd/node.h
index 23fe617898a8..a672b8765fa8 100644
--- a/arch/x86/include/asm/amd/node.h
+++ b/arch/x86/include/asm/amd/node.h
@@ -23,7 +23,6 @@
#define AMD_NODE0_PCI_SLOT 0x18
struct pci_dev *amd_node_get_func(u16 node, u8 func);
-struct pci_dev *amd_node_get_root(u16 node);
static inline u16 amd_num_nodes(void)
{
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index d5c8d3afe196..bd62bd87a841 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -2,6 +2,8 @@
#ifndef _ASM_X86_ASM_H
#define _ASM_X86_ASM_H
+#include <linux/annotate.h>
+
#ifdef __ASSEMBLER__
# define __ASM_FORM(x, ...) x,## __VA_ARGS__
# define __ASM_FORM_RAW(x, ...) x,## __VA_ARGS__
@@ -132,6 +134,7 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
# define _ASM_EXTABLE_TYPE(from, to, type) \
.pushsection "__ex_table","a" ; \
.balign 4 ; \
+ ANNOTATE_DATA_SPECIAL ; \
.long (from) - . ; \
.long (to) - . ; \
.long type ; \
@@ -179,6 +182,7 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
# define _ASM_EXTABLE_TYPE(from, to, type) \
" .pushsection \"__ex_table\",\"a\"\n" \
" .balign 4\n" \
+ ANNOTATE_DATA_SPECIAL \
" .long (" #from ") - .\n" \
" .long (" #to ") - .\n" \
" .long " __stringify(type) " \n" \
@@ -187,6 +191,7 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
# define _ASM_EXTABLE_TYPE_REG(from, to, type, reg) \
" .pushsection \"__ex_table\",\"a\"\n" \
" .balign 4\n" \
+ ANNOTATE_DATA_SPECIAL \
" .long (" #from ") - .\n" \
" .long (" #to ") - .\n" \
DEFINE_EXTABLE_TYPE_REG \
diff --git a/arch/x86/include/asm/bug.h b/arch/x86/include/asm/bug.h
index 880ca15073ed..ab5bba6cf7f5 100644
--- a/arch/x86/include/asm/bug.h
+++ b/arch/x86/include/asm/bug.h
@@ -7,6 +7,11 @@
#include <linux/objtool.h>
#include <asm/asm.h>
+#ifndef __ASSEMBLY__
+struct bug_entry;
+extern void __WARN_trap(struct bug_entry *bug, ...);
+#endif
+
/*
* Despite that some emulators terminate on UD2, we use it for WARN().
*/
@@ -31,52 +36,77 @@
#define BUG_UD2 0xfffe
#define BUG_UD1 0xfffd
#define BUG_UD1_UBSAN 0xfffc
+#define BUG_UD1_WARN 0xfffb
#define BUG_UDB 0xffd6
#define BUG_LOCK 0xfff0
#ifdef CONFIG_GENERIC_BUG
-#ifdef CONFIG_X86_32
-# define __BUG_REL(val) ".long " val
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+#define __BUG_ENTRY_VERBOSE(file, line) \
+ "\t.long " file " - .\t# bug_entry::file\n" \
+ "\t.word " line "\t# bug_entry::line\n"
#else
-# define __BUG_REL(val) ".long " val " - ."
+#define __BUG_ENTRY_VERBOSE(file, line)
#endif
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-#define __BUG_ENTRY(file, line, flags) \
- "2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
- "\t" __BUG_REL(file) "\t# bug_entry::file\n" \
- "\t.word " line "\t# bug_entry::line\n" \
- "\t.word " flags "\t# bug_entry::flags\n"
+#if defined(CONFIG_X86_64) || defined(CONFIG_DEBUG_BUGVERBOSE_DETAILED)
+#define HAVE_ARCH_BUG_FORMAT
+#define __BUG_ENTRY_FORMAT(format) \
+ "\t.long " format " - .\t# bug_entry::format\n"
#else
-#define __BUG_ENTRY(file, line, flags) \
- "2:\t" __BUG_REL("1b") "\t# bug_entry::bug_addr\n" \
- "\t.word " flags "\t# bug_entry::flags\n"
+#define __BUG_ENTRY_FORMAT(format)
+#endif
+
+#ifdef CONFIG_X86_64
+#define HAVE_ARCH_BUG_FORMAT_ARGS
#endif
-#define _BUG_FLAGS_ASM(ins, file, line, flags, size, extra) \
- "1:\t" ins "\n" \
- ".pushsection __bug_table,\"aw\"\n" \
- __BUG_ENTRY(file, line, flags) \
+#define __BUG_ENTRY(format, file, line, flags) \
+ "\t.long 1b - ." "\t# bug_entry::bug_addr\n" \
+ __BUG_ENTRY_FORMAT(format) \
+ __BUG_ENTRY_VERBOSE(file, line) \
+ "\t.word " flags "\t# bug_entry::flags\n"
+
+#define _BUG_FLAGS_ASM(format, file, line, flags, size, extra) \
+ ".pushsection __bug_table,\"aw\"\n\t" \
+ ANNOTATE_DATA_SPECIAL \
+ "2:\n\t" \
+ __BUG_ENTRY(format, file, line, flags) \
"\t.org 2b + " size "\n" \
".popsection\n" \
extra
-#define _BUG_FLAGS(ins, flags, extra) \
+#ifdef CONFIG_DEBUG_BUGVERBOSE_DETAILED
+#define WARN_CONDITION_STR(cond_str) cond_str
+#else
+#define WARN_CONDITION_STR(cond_str) ""
+#endif
+
+#define _BUG_FLAGS(cond_str, ins, flags, extra) \
do { \
- asm_inline volatile(_BUG_FLAGS_ASM(ins, "%c0", \
- "%c1", "%c2", "%c3", extra) \
- : : "i" (__FILE__), "i" (__LINE__), \
- "i" (flags), \
- "i" (sizeof(struct bug_entry))); \
+ asm_inline volatile("1:\t" ins "\n" \
+ _BUG_FLAGS_ASM("%c[fmt]", "%c[file]", \
+ "%c[line]", "%c[fl]", \
+ "%c[size]", extra) \
+ : : [fmt] "i" (WARN_CONDITION_STR(cond_str)), \
+ [file] "i" (__FILE__), \
+ [line] "i" (__LINE__), \
+ [fl] "i" (flags), \
+ [size] "i" (sizeof(struct bug_entry))); \
} while (0)
#define ARCH_WARN_ASM(file, line, flags, size) \
- _BUG_FLAGS_ASM(ASM_UD2, file, line, flags, size, "")
+ ".pushsection .rodata.str1.1, \"aMS\", @progbits, 1\n" \
+ "99:\n" \
+ "\t.string \"\"\n" \
+ ".popsection\n" \
+ "1:\t " ASM_UD2 "\n" \
+ _BUG_FLAGS_ASM("99b", file, line, flags, size, "")
#else
-#define _BUG_FLAGS(ins, flags, extra) asm volatile(ins)
+#define _BUG_FLAGS(cond_str, ins, flags, extra) asm volatile(ins)
#endif /* CONFIG_GENERIC_BUG */
@@ -84,7 +114,7 @@ do { \
#define BUG() \
do { \
instrumentation_begin(); \
- _BUG_FLAGS(ASM_UD2, 0, ""); \
+ _BUG_FLAGS("", ASM_UD2, 0, ""); \
__builtin_unreachable(); \
} while (0)
@@ -97,14 +127,69 @@ do { \
#define ARCH_WARN_REACHABLE ANNOTATE_REACHABLE(1b)
-#define __WARN_FLAGS(flags) \
-do { \
- __auto_type __flags = BUGFLAG_WARNING|(flags); \
- instrumentation_begin(); \
- _BUG_FLAGS(ASM_UD2, __flags, ARCH_WARN_REACHABLE); \
- instrumentation_end(); \
+#define __WARN_FLAGS(cond_str, flags) \
+do { \
+ __auto_type __flags = BUGFLAG_WARNING|(flags); \
+ instrumentation_begin(); \
+ _BUG_FLAGS(cond_str, ASM_UD2, __flags, ARCH_WARN_REACHABLE); \
+ instrumentation_end(); \
} while (0)
+#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
+
+#ifndef __ASSEMBLY__
+#include <linux/static_call_types.h>
+DECLARE_STATIC_CALL(WARN_trap, __WARN_trap);
+
+struct pt_regs;
+struct sysv_va_list { /* from AMD64 System V ABI */
+ unsigned int gp_offset;
+ unsigned int fp_offset;
+ void *overflow_arg_area;
+ void *reg_save_area;
+};
+struct arch_va_list {
+ unsigned long regs[6];
+ struct sysv_va_list args;
+};
+extern void *__warn_args(struct arch_va_list *args, struct pt_regs *regs);
+#endif /* __ASSEMBLY__ */
+
+#define __WARN_bug_entry(flags, format) ({ \
+ struct bug_entry *bug; \
+ asm_inline volatile("lea (2f)(%%rip), %[addr]\n1:\n" \
+ _BUG_FLAGS_ASM("%c[fmt]", "%c[file]", \
+ "%c[line]", "%c[fl]", \
+ "%c[size]", "") \
+ : [addr] "=r" (bug) \
+ : [fmt] "i" (format), \
+ [file] "i" (__FILE__), \
+ [line] "i" (__LINE__), \
+ [fl] "i" (flags), \
+ [size] "i" (sizeof(struct bug_entry))); \
+ bug; })
+
+#define __WARN_print_arg(flags, format, arg...) \
+do { \
+ int __flags = (flags) | BUGFLAG_WARNING | BUGFLAG_ARGS ; \
+ static_call_mod(WARN_trap)(__WARN_bug_entry(__flags, format), ## arg); \
+ asm (""); /* inhibit tail-call optimization */ \
+} while (0)
+
+#define __WARN_printf(taint, fmt, arg...) \
+ __WARN_print_arg(BUGFLAG_TAINT(taint), fmt, ## arg)
+
+#define WARN_ONCE(cond, format, arg...) ({ \
+ int __ret_warn_on = !!(cond); \
+ if (unlikely(__ret_warn_on)) { \
+ __WARN_print_arg(BUGFLAG_ONCE|BUGFLAG_TAINT(TAINT_WARN),\
+ format, ## arg); \
+ } \
+ __ret_warn_on; \
+})
+
+#endif /* HAVE_ARCH_BUG_FORMAT_ARGS */
+
#include <asm-generic/bug.h>
#endif /* _ASM_X86_BUG_H */
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 893cbca37fe9..4b1a6ade1700 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -30,7 +30,7 @@ enum cpuid_leafs
CPUID_6_EAX,
CPUID_8000_000A_EDX,
CPUID_7_ECX,
- CPUID_8000_0007_EBX,
+ CPUID_LNX_6,
CPUID_7_EDX,
CPUID_8000_001F_EAX,
CPUID_8000_0021_EAX,
@@ -101,6 +101,7 @@ static __always_inline bool _static_cpu_has(u16 bit)
asm goto(ALTERNATIVE_TERNARY("jmp 6f", %c[feature], "", "jmp %l[t_no]")
".pushsection .altinstr_aux,\"ax\"\n"
"6:\n"
+ ANNOTATE_DATA_SPECIAL
" testb %[bitnum], %a[cap_byte]\n"
" jnz %l[t_yes]\n"
" jmp %l[t_no]\n"
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index 4091a776e37a..d90ce601917c 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -314,13 +314,14 @@
#define X86_FEATURE_SM4 (12*32+ 2) /* SM4 instructions */
#define X86_FEATURE_AVX_VNNI (12*32+ 4) /* "avx_vnni" AVX VNNI instructions */
#define X86_FEATURE_AVX512_BF16 (12*32+ 5) /* "avx512_bf16" AVX512 BFLOAT16 instructions */
+#define X86_FEATURE_LASS (12*32+ 6) /* "lass" Linear Address Space Separation */
#define X86_FEATURE_CMPCCXADD (12*32+ 7) /* CMPccXADD instructions */
#define X86_FEATURE_ARCH_PERFMON_EXT (12*32+ 8) /* Intel Architectural PerfMon Extension */
#define X86_FEATURE_FZRM (12*32+10) /* Fast zero-length REP MOVSB */
#define X86_FEATURE_FSRS (12*32+11) /* Fast short REP STOSB */
#define X86_FEATURE_FSRC (12*32+12) /* Fast short REP {CMPSB,SCASB} */
#define X86_FEATURE_FRED (12*32+17) /* "fred" Flexible Return and Event Delivery */
-#define X86_FEATURE_LKGS (12*32+18) /* Load "kernel" (userspace) GS */
+#define X86_FEATURE_LKGS (12*32+18) /* Like MOV_GS except MSR_KERNEL_GS_BASE = GS.base */
#define X86_FEATURE_WRMSRNS (12*32+19) /* Non-serializing WRMSR */
#define X86_FEATURE_AMX_FP16 (12*32+21) /* AMX fp16 Support */
#define X86_FEATURE_AVX_IFMA (12*32+23) /* Support for VPMADD52[H,L]UQ */
@@ -407,9 +408,12 @@
#define X86_FEATURE_ENQCMD (16*32+29) /* "enqcmd" ENQCMD and ENQCMDS instructions */
#define X86_FEATURE_SGX_LC (16*32+30) /* "sgx_lc" Software Guard Extensions Launch Control */
-/* AMD-defined CPU features, CPUID level 0x80000007 (EBX), word 17 */
+/*
+ * Linux-defined word for use with scattered/synthetic bits.
+ */
#define X86_FEATURE_OVERFLOW_RECOV (17*32+ 0) /* "overflow_recov" MCA overflow recovery support */
#define X86_FEATURE_SUCCOR (17*32+ 1) /* "succor" Uncorrectable error containment and recovery */
+
#define X86_FEATURE_SMCA (17*32+ 3) /* "smca" Scalable MCA */
/* Intel-defined CPU features, CPUID level 0x00000007:0 (EDX), word 18 */
@@ -499,6 +503,9 @@
#define X86_FEATURE_IBPB_EXIT_TO_USER (21*32+14) /* Use IBPB on exit-to-userspace, see VMSCAPE bug */
#define X86_FEATURE_ABMC (21*32+15) /* Assignable Bandwidth Monitoring Counters */
#define X86_FEATURE_MSR_IMM (21*32+16) /* MSR immediate form instructions */
+#define X86_FEATURE_SGX_EUPDATESVN (21*32+17) /* Support for ENCLS[EUPDATESVN] instruction */
+
+#define X86_FEATURE_SDCIAE (21*32+18) /* L3 Smart Data Cache Injection Allocation Enforcement */
/*
* BUG word(s)
diff --git a/arch/x86/include/asm/fred.h b/arch/x86/include/asm/fred.h
index 12b34d5b2953..2bb65677c079 100644
--- a/arch/x86/include/asm/fred.h
+++ b/arch/x86/include/asm/fred.h
@@ -79,7 +79,7 @@ static __always_inline void fred_entry_from_kvm(unsigned int type, unsigned int
.type = type,
.vector = vector,
.nmi = type == EVENT_TYPE_NMI,
- .lm = 1,
+ .l = 1,
};
asm_fred_entry_from_kvm(ss);
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index 93156ac4ffe0..b08c95872eed 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -56,6 +56,11 @@ arch_ftrace_get_regs(struct ftrace_regs *fregs)
return &arch_ftrace_regs(fregs)->regs;
}
+#define arch_ftrace_partial_regs(regs) do { \
+ regs->flags &= ~X86_EFLAGS_FIXED; \
+ regs->cs = __KERNEL_CS; \
+} while (0)
+
#define arch_ftrace_fill_perf_regs(fregs, _regs) do { \
(_regs)->ip = arch_ftrace_regs(fregs)->regs.ip; \
(_regs)->sp = arch_ftrace_regs(fregs)->regs.sp; \
diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h
index 6e2458088800..fe5d9a10d900 100644
--- a/arch/x86/include/asm/futex.h
+++ b/arch/x86/include/asm/futex.h
@@ -46,38 +46,31 @@ do { \
} while(0)
static __always_inline int arch_futex_atomic_op_inuser(int op, int oparg, int *oval,
- u32 __user *uaddr)
+ u32 __user *uaddr)
{
- if (can_do_masked_user_access())
- uaddr = masked_user_access_begin(uaddr);
- else if (!user_access_begin(uaddr, sizeof(u32)))
- return -EFAULT;
-
- switch (op) {
- case FUTEX_OP_SET:
- unsafe_atomic_op1("xchgl %0, %2", oval, uaddr, oparg, Efault);
- break;
- case FUTEX_OP_ADD:
- unsafe_atomic_op1(LOCK_PREFIX "xaddl %0, %2", oval,
- uaddr, oparg, Efault);
- break;
- case FUTEX_OP_OR:
- unsafe_atomic_op2("orl %4, %3", oval, uaddr, oparg, Efault);
- break;
- case FUTEX_OP_ANDN:
- unsafe_atomic_op2("andl %4, %3", oval, uaddr, ~oparg, Efault);
- break;
- case FUTEX_OP_XOR:
- unsafe_atomic_op2("xorl %4, %3", oval, uaddr, oparg, Efault);
- break;
- default:
- user_access_end();
- return -ENOSYS;
+ scoped_user_rw_access(uaddr, Efault) {
+ switch (op) {
+ case FUTEX_OP_SET:
+ unsafe_atomic_op1("xchgl %0, %2", oval, uaddr, oparg, Efault);
+ break;
+ case FUTEX_OP_ADD:
+ unsafe_atomic_op1(LOCK_PREFIX "xaddl %0, %2", oval, uaddr, oparg, Efault);
+ break;
+ case FUTEX_OP_OR:
+ unsafe_atomic_op2("orl %4, %3", oval, uaddr, oparg, Efault);
+ break;
+ case FUTEX_OP_ANDN:
+ unsafe_atomic_op2("andl %4, %3", oval, uaddr, ~oparg, Efault);
+ break;
+ case FUTEX_OP_XOR:
+ unsafe_atomic_op2("xorl %4, %3", oval, uaddr, oparg, Efault);
+ break;
+ default:
+ return -ENOSYS;
+ }
}
- user_access_end();
return 0;
Efault:
- user_access_end();
return -EFAULT;
}
@@ -86,21 +79,19 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
{
int ret = 0;
- if (can_do_masked_user_access())
- uaddr = masked_user_access_begin(uaddr);
- else if (!user_access_begin(uaddr, sizeof(u32)))
- return -EFAULT;
- asm volatile("\n"
- "1:\t" LOCK_PREFIX "cmpxchgl %3, %2\n"
- "2:\n"
- _ASM_EXTABLE_TYPE_REG(1b, 2b, EX_TYPE_EFAULT_REG, %0) \
- : "+r" (ret), "=a" (oldval), "+m" (*uaddr)
- : "r" (newval), "1" (oldval)
- : "memory"
- );
- user_access_end();
- *uval = oldval;
+ scoped_user_rw_access(uaddr, Efault) {
+ asm_inline volatile("\n"
+ "1:\t" LOCK_PREFIX "cmpxchgl %3, %2\n"
+ "2:\n"
+ _ASM_EXTABLE_TYPE_REG(1b, 2b, EX_TYPE_EFAULT_REG, %0)
+ : "+r" (ret), "=a" (oldval), "+m" (*uaddr)
+ : "r" (newval), "1" (oldval)
+ : "memory");
+ *uval = oldval;
+ }
return ret;
+Efault:
+ return -EFAULT;
}
#endif
diff --git a/arch/x86/include/asm/idtentry.h b/arch/x86/include/asm/idtentry.h
index abd637e54e94..3218770670d3 100644
--- a/arch/x86/include/asm/idtentry.h
+++ b/arch/x86/include/asm/idtentry.h
@@ -393,7 +393,7 @@ static __always_inline void __##func(struct pt_regs *regs)
/**
* DEFINE_IDTENTRY_VC_KERNEL - Emit code for VMM communication handler
- when raised from kernel mode
+ * when raised from kernel mode
* @func: Function name of the entry point
*
* Maps to DEFINE_IDTENTRY_RAW_ERRORCODE
@@ -403,7 +403,7 @@ static __always_inline void __##func(struct pt_regs *regs)
/**
* DEFINE_IDTENTRY_VC_USER - Emit code for VMM communication handler
- when raised from user mode
+ * when raised from user mode
* @func: Function name of the entry point
*
* Maps to DEFINE_IDTENTRY_RAW_ERRORCODE
diff --git a/arch/x86/include/asm/insn-eval.h b/arch/x86/include/asm/insn-eval.h
index 54368a43abf6..4733e9064ee5 100644
--- a/arch/x86/include/asm/insn-eval.h
+++ b/arch/x86/include/asm/insn-eval.h
@@ -44,4 +44,6 @@ enum insn_mmio_type {
enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
+bool insn_is_nop(struct insn *insn);
+
#endif /* _ASM_X86_INSN_EVAL_H */
diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h
index 091f88c8254d..846d21c1a7f8 100644
--- a/arch/x86/include/asm/insn.h
+++ b/arch/x86/include/asm/insn.h
@@ -312,7 +312,6 @@ static inline int insn_offset_immediate(struct insn *insn)
/**
* for_each_insn_prefix() -- Iterate prefixes in the instruction
* @insn: Pointer to struct insn.
- * @idx: Index storage.
* @prefix: Prefix byte.
*
* Iterate prefix bytes of given @insn. Each prefix byte is stored in @prefix
@@ -321,8 +320,8 @@ static inline int insn_offset_immediate(struct insn *insn)
* Since prefixes.nbytes can be bigger than 4 if some prefixes
* are repeated, it cannot be used for looping over the prefixes.
*/
-#define for_each_insn_prefix(insn, idx, prefix) \
- for (idx = 0; idx < ARRAY_SIZE(insn->prefixes.bytes) && (prefix = insn->prefixes.bytes[idx]) != 0; idx++)
+#define for_each_insn_prefix(insn, prefix) \
+ for (int idx = 0; idx < ARRAY_SIZE(insn->prefixes.bytes) && (prefix = insn->prefixes.bytes[idx]) != 0; idx++)
#define POP_SS_OPCODE 0x1f
#define MOV_SREG_OPCODE 0x8e
diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h
index f32a0eca2ae5..950bfd006905 100644
--- a/arch/x86/include/asm/intel-family.h
+++ b/arch/x86/include/asm/intel-family.h
@@ -150,12 +150,12 @@
#define INTEL_LUNARLAKE_M IFM(6, 0xBD) /* Lion Cove / Skymont */
-#define INTEL_PANTHERLAKE_L IFM(6, 0xCC) /* Cougar Cove / Crestmont */
+#define INTEL_PANTHERLAKE_L IFM(6, 0xCC) /* Cougar Cove / Darkmont */
#define INTEL_WILDCATLAKE_L IFM(6, 0xD5)
-#define INTEL_NOVALAKE IFM(18, 0x01)
-#define INTEL_NOVALAKE_L IFM(18, 0x03)
+#define INTEL_NOVALAKE IFM(18, 0x01) /* Coyote Cove / Arctic Wolf */
+#define INTEL_NOVALAKE_L IFM(18, 0x03) /* Coyote Cove / Arctic Wolf */
/* "Small Core" Processors (Atom/E-Core) */
diff --git a/arch/x86/include/asm/intel_ds.h b/arch/x86/include/asm/intel_ds.h
index 5dbeac48a5b9..695f87efbeb8 100644
--- a/arch/x86/include/asm/intel_ds.h
+++ b/arch/x86/include/asm/intel_ds.h
@@ -4,7 +4,15 @@
#include <linux/percpu-defs.h>
#define BTS_BUFFER_SIZE (PAGE_SIZE << 4)
-#define PEBS_BUFFER_SIZE (PAGE_SIZE << 4)
+#define PEBS_BUFFER_SHIFT 4
+#define PEBS_BUFFER_SIZE (PAGE_SIZE << PEBS_BUFFER_SHIFT)
+
+/*
+ * The largest PEBS record could consume a page, ensure
+ * a record at least can be written after triggering PMI.
+ */
+#define ARCH_PEBS_THRESH_MULTI ((PEBS_BUFFER_SIZE - PAGE_SIZE) >> PEBS_BUFFER_SHIFT)
+#define ARCH_PEBS_THRESH_SINGLE 1
/* The maximal number of PEBS events: */
#define MAX_PEBS_EVENTS_FMT4 8
diff --git a/arch/x86/include/asm/jump_label.h b/arch/x86/include/asm/jump_label.h
index 61dd1dee7812..e0a6930a4029 100644
--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -15,6 +15,7 @@
#define JUMP_TABLE_ENTRY(key, label) \
".pushsection __jump_table, \"aw\" \n\t" \
_ASM_ALIGN "\n\t" \
+ ANNOTATE_DATA_SPECIAL \
".long 1b - . \n\t" \
".long " label " - . \n\t" \
_ASM_PTR " " key " - . \n\t" \
diff --git a/arch/x86/include/asm/kvm_types.h b/arch/x86/include/asm/kvm_types.h
index 23268a188e70..d7c704ed1be9 100644
--- a/arch/x86/include/asm/kvm_types.h
+++ b/arch/x86/include/asm/kvm_types.h
@@ -10,6 +10,11 @@
#define KVM_SUB_MODULES kvm-intel
#else
#undef KVM_SUB_MODULES
+/*
+ * Don't export symbols for KVM without vendor modules, as kvm.ko is built iff
+ * at least one vendor module is enabled.
+ */
+#define EXPORT_SYMBOL_FOR_KVM(symbol)
#endif
#define KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE 40
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index 31e3cb550fb3..2d98886de09a 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -48,6 +48,7 @@
/* AMD-specific bits */
#define MCI_STATUS_TCC BIT_ULL(55) /* Task context corrupt */
+#define MCI_STATUS_PADDRV BIT_ULL(54) /* Valid System Physical Address */
#define MCI_STATUS_SYNDV BIT_ULL(53) /* synd reg. valid */
#define MCI_STATUS_DEFERRED BIT_ULL(44) /* uncorrected error, deferred exception */
#define MCI_STATUS_POISON BIT_ULL(43) /* access poisonous data */
@@ -62,6 +63,7 @@
*/
#define MCI_CONFIG_MCAX 0x1
#define MCI_CONFIG_FRUTEXT BIT_ULL(9)
+#define MCI_CONFIG_PADDRV BIT_ULL(11)
#define MCI_IPID_MCATYPE 0xFFFF0000
#define MCI_IPID_HWID 0xFFF
@@ -166,6 +168,12 @@
#define MCE_IN_KERNEL_COPYIN BIT_ULL(7)
/*
+ * Indicates that handler should check and clear Deferred error registers
+ * rather than common ones.
+ */
+#define MCE_CHECK_DFR_REGS BIT_ULL(8)
+
+/*
* This structure contains all data related to the MCE log. Also
* carries a signature to make it easier to find from external
* debugging tools. Each entry is only valid when its finished flag
@@ -302,6 +310,12 @@ DECLARE_PER_CPU(struct mce, injectm);
/* Disable CMCI/polling for MCA bank claimed by firmware */
extern void mce_disable_bank(int bank);
+#ifdef CONFIG_X86_MCE_THRESHOLD
+void mce_save_apei_thr_limit(u32 thr_limit);
+#else
+static inline void mce_save_apei_thr_limit(u32 thr_limit) { }
+#endif /* CONFIG_X86_MCE_THRESHOLD */
+
/*
* Exception handler
*/
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 9e1720d73244..3d0a0950d20a 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -166,6 +166,10 @@
* Processor MMIO stale data
* vulnerabilities.
*/
+#define ARCH_CAP_MCU_ENUM BIT(16) /*
+ * Indicates the presence of microcode update
+ * feature enumeration and status information.
+ */
#define ARCH_CAP_FB_CLEAR BIT(17) /*
* VERW clears CPU fill buffer
* even on MDS_NO CPUs.
@@ -327,6 +331,26 @@
PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE | \
PERF_CAP_PEBS_TIMING_INFO)
+/* Arch PEBS */
+#define MSR_IA32_PEBS_BASE 0x000003f4
+#define MSR_IA32_PEBS_INDEX 0x000003f5
+#define ARCH_PEBS_OFFSET_MASK 0x7fffff
+#define ARCH_PEBS_INDEX_WR_SHIFT 4
+
+#define ARCH_PEBS_RELOAD 0xffffffff
+#define ARCH_PEBS_CNTR_ALLOW BIT_ULL(35)
+#define ARCH_PEBS_CNTR_GP BIT_ULL(36)
+#define ARCH_PEBS_CNTR_FIXED BIT_ULL(37)
+#define ARCH_PEBS_CNTR_METRICS BIT_ULL(38)
+#define ARCH_PEBS_LBR_SHIFT 40
+#define ARCH_PEBS_LBR (0x3ull << ARCH_PEBS_LBR_SHIFT)
+#define ARCH_PEBS_VECR_XMM BIT_ULL(49)
+#define ARCH_PEBS_GPR BIT_ULL(61)
+#define ARCH_PEBS_AUX BIT_ULL(62)
+#define ARCH_PEBS_EN BIT_ULL(63)
+#define ARCH_PEBS_CNTR_MASK (ARCH_PEBS_CNTR_GP | ARCH_PEBS_CNTR_FIXED | \
+ ARCH_PEBS_CNTR_METRICS)
+
#define MSR_IA32_RTIT_CTL 0x00000570
#define RTIT_CTL_TRACEEN BIT(0)
#define RTIT_CTL_CYCLEACC BIT(1)
@@ -929,6 +953,10 @@
#define MSR_IA32_APICBASE_BASE (0xfffff<<12)
#define MSR_IA32_UCODE_WRITE 0x00000079
+
+#define MSR_IA32_MCU_ENUMERATION 0x0000007b
+#define MCU_STAGING BIT(4)
+
#define MSR_IA32_UCODE_REV 0x0000008b
/* Intel SGX Launch Enclave Public Key Hash MSRs */
@@ -1226,6 +1254,8 @@
#define MSR_IA32_VMX_VMFUNC 0x00000491
#define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492
+#define MSR_IA32_MCU_STAGING_MBOX_ADDR 0x000007a5
+
/* Resctrl MSRs: */
/* - Intel: */
#define MSR_IA32_L3_QOS_CFG 0xc81
diff --git a/arch/x86/include/asm/page_64.h b/arch/x86/include/asm/page_64.h
index 015d23f3e01f..2f0e47be79a4 100644
--- a/arch/x86/include/asm/page_64.h
+++ b/arch/x86/include/asm/page_64.h
@@ -9,6 +9,7 @@
#include <asm/alternative.h>
#include <linux/kmsan-checks.h>
+#include <linux/mmdebug.h>
/* duplicated to the one in bootmem.h */
extern unsigned long max_pfn;
@@ -31,18 +32,28 @@ static __always_inline unsigned long __phys_addr_nodebug(unsigned long x)
#ifdef CONFIG_DEBUG_VIRTUAL
extern unsigned long __phys_addr(unsigned long);
-extern unsigned long __phys_addr_symbol(unsigned long);
#else
#define __phys_addr(x) __phys_addr_nodebug(x)
-#define __phys_addr_symbol(x) \
- ((unsigned long)(x) - __START_KERNEL_map + phys_base)
#endif
+static inline unsigned long __phys_addr_symbol(unsigned long x)
+{
+ unsigned long y = x - __START_KERNEL_map;
+
+ /* only check upper bounds since lower bounds will trigger carry */
+ VIRTUAL_BUG_ON(y >= KERNEL_IMAGE_SIZE);
+
+ return y + phys_base;
+}
+
#define __phys_reloc_hide(x) (x)
void clear_page_orig(void *page);
void clear_page_rep(void *page);
void clear_page_erms(void *page);
+KCFI_REFERENCE(clear_page_orig);
+KCFI_REFERENCE(clear_page_rep);
+KCFI_REFERENCE(clear_page_erms);
static inline void clear_page(void *page)
{
diff --git a/arch/x86/include/asm/percpu.h b/arch/x86/include/asm/percpu.h
index 332428caaed2..725d0eff7acd 100644
--- a/arch/x86/include/asm/percpu.h
+++ b/arch/x86/include/asm/percpu.h
@@ -23,6 +23,7 @@
#else /* !__ASSEMBLY__: */
#include <linux/args.h>
+#include <linux/bits.h>
#include <linux/build_bug.h>
#include <linux/stringify.h>
#include <asm/asm.h>
@@ -572,9 +573,9 @@ do { \
#define x86_this_cpu_constant_test_bit(_nr, _var) \
({ \
unsigned long __percpu *addr__ = \
- (unsigned long __percpu *)&(_var) + ((_nr) / BITS_PER_LONG); \
+ (unsigned long __percpu *)&(_var) + BIT_WORD(_nr); \
\
- !!((1UL << ((_nr) % BITS_PER_LONG)) & raw_cpu_read(*addr__)); \
+ !!(BIT_MASK(_nr) & raw_cpu_read(*addr__)); \
})
#define x86_this_cpu_variable_test_bit(_nr, _var) \
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index 49a4d442f3fc..7276ba70c88a 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -141,16 +141,16 @@
#define ARCH_PERFMON_EVENTS_COUNT 7
#define PEBS_DATACFG_MEMINFO BIT_ULL(0)
-#define PEBS_DATACFG_GP BIT_ULL(1)
+#define PEBS_DATACFG_GP BIT_ULL(1)
#define PEBS_DATACFG_XMMS BIT_ULL(2)
#define PEBS_DATACFG_LBRS BIT_ULL(3)
-#define PEBS_DATACFG_LBR_SHIFT 24
#define PEBS_DATACFG_CNTR BIT_ULL(4)
+#define PEBS_DATACFG_METRICS BIT_ULL(5)
+#define PEBS_DATACFG_LBR_SHIFT 24
#define PEBS_DATACFG_CNTR_SHIFT 32
#define PEBS_DATACFG_CNTR_MASK GENMASK_ULL(15, 0)
#define PEBS_DATACFG_FIX_SHIFT 48
#define PEBS_DATACFG_FIX_MASK GENMASK_ULL(7, 0)
-#define PEBS_DATACFG_METRICS BIT_ULL(5)
/* Steal the highest bit of pebs_data_cfg for SW usage */
#define PEBS_UPDATE_DS_SW BIT_ULL(63)
@@ -200,6 +200,8 @@ union cpuid10_edx {
#define ARCH_PERFMON_EXT_LEAF 0x00000023
#define ARCH_PERFMON_NUM_COUNTER_LEAF 0x1
#define ARCH_PERFMON_ACR_LEAF 0x2
+#define ARCH_PERFMON_PEBS_CAP_LEAF 0x4
+#define ARCH_PERFMON_PEBS_COUNTER_LEAF 0x5
union cpuid35_eax {
struct {
@@ -210,7 +212,10 @@ union cpuid35_eax {
unsigned int acr_subleaf:1;
/* Events Sub-Leaf */
unsigned int events_subleaf:1;
- unsigned int reserved:28;
+ /* arch-PEBS Sub-Leaves */
+ unsigned int pebs_caps_subleaf:1;
+ unsigned int pebs_cnts_subleaf:1;
+ unsigned int reserved:26;
} split;
unsigned int full;
};
@@ -432,6 +437,8 @@ static inline bool is_topdown_idx(int idx)
#define GLOBAL_STATUS_LBRS_FROZEN BIT_ULL(GLOBAL_STATUS_LBRS_FROZEN_BIT)
#define GLOBAL_STATUS_TRACE_TOPAPMI_BIT 55
#define GLOBAL_STATUS_TRACE_TOPAPMI BIT_ULL(GLOBAL_STATUS_TRACE_TOPAPMI_BIT)
+#define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT 54
+#define GLOBAL_STATUS_ARCH_PEBS_THRESHOLD BIT_ULL(GLOBAL_STATUS_ARCH_PEBS_THRESHOLD_BIT)
#define GLOBAL_STATUS_PERF_METRICS_OVF_BIT 48
#define GLOBAL_CTRL_EN_PERF_METRICS BIT_ULL(48)
@@ -503,6 +510,107 @@ struct pebs_cntr_header {
#define INTEL_CNTR_METRICS 0x3
/*
+ * Arch PEBS
+ */
+union arch_pebs_index {
+ struct {
+ u64 rsvd:4,
+ wr:23,
+ rsvd2:4,
+ full:1,
+ en:1,
+ rsvd3:3,
+ thresh:23,
+ rsvd4:5;
+ };
+ u64 whole;
+};
+
+struct arch_pebs_header {
+ union {
+ u64 format;
+ struct {
+ u64 size:16, /* Record size */
+ rsvd:14,
+ mode:1, /* 64BIT_MODE */
+ cont:1,
+ rsvd2:3,
+ cntr:5,
+ lbr:2,
+ rsvd3:7,
+ xmm:1,
+ ymmh:1,
+ rsvd4:2,
+ opmask:1,
+ zmmh:1,
+ h16zmm:1,
+ rsvd5:5,
+ gpr:1,
+ aux:1,
+ basic:1;
+ };
+ };
+ u64 rsvd6;
+};
+
+struct arch_pebs_basic {
+ u64 ip;
+ u64 applicable_counters;
+ u64 tsc;
+ u64 retire :16, /* Retire Latency */
+ valid :1,
+ rsvd :47;
+ u64 rsvd2;
+ u64 rsvd3;
+};
+
+struct arch_pebs_aux {
+ u64 address;
+ u64 rsvd;
+ u64 rsvd2;
+ u64 rsvd3;
+ u64 rsvd4;
+ u64 aux;
+ u64 instr_latency :16,
+ pad2 :16,
+ cache_latency :16,
+ pad3 :16;
+ u64 tsx_tuning;
+};
+
+struct arch_pebs_gprs {
+ u64 flags, ip, ax, cx, dx, bx, sp, bp, si, di;
+ u64 r8, r9, r10, r11, r12, r13, r14, r15, ssp;
+ u64 rsvd;
+};
+
+struct arch_pebs_xer_header {
+ u64 xstate;
+ u64 rsvd;
+};
+
+#define ARCH_PEBS_LBR_NAN 0x0
+#define ARCH_PEBS_LBR_NUM_8 0x1
+#define ARCH_PEBS_LBR_NUM_16 0x2
+#define ARCH_PEBS_LBR_NUM_VAR 0x3
+#define ARCH_PEBS_BASE_LBR_ENTRIES 8
+struct arch_pebs_lbr_header {
+ u64 rsvd;
+ u64 ctl;
+ u64 depth;
+ u64 ler_from;
+ u64 ler_to;
+ u64 ler_info;
+};
+
+struct arch_pebs_cntr_header {
+ u32 cntr;
+ u32 fixed;
+ u32 metrics;
+ u32 reserved;
+};
+
+/*
* AMD Extended Performance Monitoring and Debug cpuid feature detection
*/
#define EXT_PERFMON_DEBUG_FEATURES 0x80000022
diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index 50f75467f73d..35d062a2e304 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -84,8 +84,8 @@ struct fred_ss {
: 4,
/* Event was incident to enclave execution */
enclave : 1,
- /* CPU was in long mode */
- lm : 1,
+ /* CPU was in 64-bit mode */
+ l : 1,
/*
* Nested exception during FRED delivery, not set
* for #DF.
@@ -187,12 +187,12 @@ convert_ip_to_linear(struct task_struct *child, struct pt_regs *regs);
extern void send_sigtrap(struct pt_regs *regs, int error_code, int si_code);
-static inline unsigned long regs_return_value(struct pt_regs *regs)
+static __always_inline unsigned long regs_return_value(struct pt_regs *regs)
{
return regs->ax;
}
-static inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
+static __always_inline void regs_set_return_value(struct pt_regs *regs, unsigned long rc)
{
regs->ax = rc;
}
@@ -277,34 +277,34 @@ static __always_inline bool ip_within_syscall_gap(struct pt_regs *regs)
}
#endif
-static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+static __always_inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
{
return regs->sp;
}
-static inline unsigned long instruction_pointer(struct pt_regs *regs)
+static __always_inline unsigned long instruction_pointer(struct pt_regs *regs)
{
return regs->ip;
}
-static inline void instruction_pointer_set(struct pt_regs *regs,
- unsigned long val)
+static __always_inline
+void instruction_pointer_set(struct pt_regs *regs, unsigned long val)
{
regs->ip = val;
}
-static inline unsigned long frame_pointer(struct pt_regs *regs)
+static __always_inline unsigned long frame_pointer(struct pt_regs *regs)
{
return regs->bp;
}
-static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+static __always_inline unsigned long user_stack_pointer(struct pt_regs *regs)
{
return regs->sp;
}
-static inline void user_stack_pointer_set(struct pt_regs *regs,
- unsigned long val)
+static __always_inline
+void user_stack_pointer_set(struct pt_regs *regs, unsigned long val)
{
regs->sp = val;
}
diff --git a/arch/x86/include/asm/runtime-const.h b/arch/x86/include/asm/runtime-const.h
index 8d983cfd06ea..e5a13dc8816e 100644
--- a/arch/x86/include/asm/runtime-const.h
+++ b/arch/x86/include/asm/runtime-const.h
@@ -2,6 +2,10 @@
#ifndef _ASM_RUNTIME_CONST_H
#define _ASM_RUNTIME_CONST_H
+#ifdef MODULE
+ #error "Cannot use runtime-const infrastructure from modules"
+#endif
+
#ifdef __ASSEMBLY__
.macro RUNTIME_CONST_PTR sym reg
diff --git a/arch/x86/include/asm/sgx.h b/arch/x86/include/asm/sgx.h
index 6a0069761508..04958459a7ca 100644
--- a/arch/x86/include/asm/sgx.h
+++ b/arch/x86/include/asm/sgx.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
-/**
+/*
* Copyright(c) 2016-20 Intel Corporation.
*
* Intel Software Guard Extensions (SGX) support.
@@ -28,21 +28,22 @@
#define SGX_CPUID_EPC_MASK GENMASK(3, 0)
enum sgx_encls_function {
- ECREATE = 0x00,
- EADD = 0x01,
- EINIT = 0x02,
- EREMOVE = 0x03,
- EDGBRD = 0x04,
- EDGBWR = 0x05,
- EEXTEND = 0x06,
- ELDU = 0x08,
- EBLOCK = 0x09,
- EPA = 0x0A,
- EWB = 0x0B,
- ETRACK = 0x0C,
- EAUG = 0x0D,
- EMODPR = 0x0E,
- EMODT = 0x0F,
+ ECREATE = 0x00,
+ EADD = 0x01,
+ EINIT = 0x02,
+ EREMOVE = 0x03,
+ EDGBRD = 0x04,
+ EDGBWR = 0x05,
+ EEXTEND = 0x06,
+ ELDU = 0x08,
+ EBLOCK = 0x09,
+ EPA = 0x0A,
+ EWB = 0x0B,
+ ETRACK = 0x0C,
+ EAUG = 0x0D,
+ EMODPR = 0x0E,
+ EMODT = 0x0F,
+ EUPDATESVN = 0x18,
};
/**
@@ -65,15 +66,19 @@ enum sgx_encls_function {
/**
* enum sgx_return_code - The return code type for ENCLS, ENCLU and ENCLV
- * %SGX_EPC_PAGE_CONFLICT: Page is being written by other ENCLS function.
- * %SGX_NOT_TRACKED: Previous ETRACK's shootdown sequence has not
+ * @SGX_EPC_PAGE_CONFLICT: Page is being written by other ENCLS function.
+ * @SGX_NOT_TRACKED: Previous ETRACK's shootdown sequence has not
* been completed yet.
- * %SGX_CHILD_PRESENT SECS has child pages present in the EPC.
- * %SGX_INVALID_EINITTOKEN: EINITTOKEN is invalid and enclave signer's
+ * @SGX_CHILD_PRESENT: SECS has child pages present in the EPC.
+ * @SGX_INVALID_EINITTOKEN: EINITTOKEN is invalid and enclave signer's
* public key does not match IA32_SGXLEPUBKEYHASH.
- * %SGX_PAGE_NOT_MODIFIABLE: The EPC page cannot be modified because it
+ * @SGX_PAGE_NOT_MODIFIABLE: The EPC page cannot be modified because it
* is in the PENDING or MODIFIED state.
- * %SGX_UNMASKED_EVENT: An unmasked event, e.g. INTR, was received
+ * @SGX_INSUFFICIENT_ENTROPY: Insufficient entropy in RNG.
+ * @SGX_NO_UPDATE: EUPDATESVN could not update the CPUSVN because the
+ * current SVN was not newer than CPUSVN. This is the most
+ * common error code returned by EUPDATESVN.
+ * @SGX_UNMASKED_EVENT: An unmasked event, e.g. INTR, was received
*/
enum sgx_return_code {
SGX_EPC_PAGE_CONFLICT = 7,
@@ -81,6 +86,8 @@ enum sgx_return_code {
SGX_CHILD_PRESENT = 13,
SGX_INVALID_EINITTOKEN = 16,
SGX_PAGE_NOT_MODIFIABLE = 20,
+ SGX_INSUFFICIENT_ENTROPY = 29,
+ SGX_NO_UPDATE = 31,
SGX_UNMASKED_EVENT = 128,
};
@@ -89,7 +96,7 @@ enum sgx_return_code {
/**
* enum sgx_miscselect - additional information to an SSA frame
- * %SGX_MISC_EXINFO: Report #PF or #GP to the SSA frame.
+ * @SGX_MISC_EXINFO: Report #PF or #GP to the SSA frame.
*
* Save State Area (SSA) is a stack inside the enclave used to store processor
* state when an exception or interrupt occurs. This enum defines additional
@@ -105,17 +112,17 @@ enum sgx_miscselect {
#define SGX_SSA_MISC_EXINFO_SIZE 16
/**
- * enum sgx_attributes - the attributes field in &struct sgx_secs
- * %SGX_ATTR_INIT: Enclave can be entered (is initialized).
- * %SGX_ATTR_DEBUG: Allow ENCLS(EDBGRD) and ENCLS(EDBGWR).
- * %SGX_ATTR_MODE64BIT: Tell that this a 64-bit enclave.
- * %SGX_ATTR_PROVISIONKEY: Allow to use provisioning keys for remote
+ * enum sgx_attribute - the attributes field in &struct sgx_secs
+ * @SGX_ATTR_INIT: Enclave can be entered (is initialized).
+ * @SGX_ATTR_DEBUG: Allow ENCLS(EDBGRD) and ENCLS(EDBGWR).
+ * @SGX_ATTR_MODE64BIT: Tell that this a 64-bit enclave.
+ * @SGX_ATTR_PROVISIONKEY: Allow to use provisioning keys for remote
* attestation.
- * %SGX_ATTR_KSS: Allow to use key separation and sharing (KSS).
- * %SGX_ATTR_EINITTOKENKEY: Allow to use token signing key that is used to
+ * @SGX_ATTR_KSS: Allow to use key separation and sharing (KSS).
+ * @SGX_ATTR_EINITTOKENKEY: Allow to use token signing key that is used to
* sign cryptographic tokens that can be passed to
* EINIT as an authorization to run an enclave.
- * %SGX_ATTR_ASYNC_EXIT_NOTIFY: Allow enclaves to be notified after an
+ * @SGX_ATTR_ASYNC_EXIT_NOTIFY: Allow enclaves to be notified after an
* asynchronous exit has occurred.
*/
enum sgx_attribute {
@@ -188,7 +195,7 @@ struct sgx_secs {
/**
* enum sgx_tcs_flags - execution flags for TCS
- * %SGX_TCS_DBGOPTIN: If enabled allows single-stepping and breakpoints
+ * @SGX_TCS_DBGOPTIN: If enabled allows single-stepping and breakpoints
* inside an enclave. It is cleared by EADD but can
* be set later with EDBGWR.
*/
@@ -253,11 +260,11 @@ struct sgx_pageinfo {
/**
* enum sgx_page_type - bits in the SECINFO flags defining the page type
- * %SGX_PAGE_TYPE_SECS: a SECS page
- * %SGX_PAGE_TYPE_TCS: a TCS page
- * %SGX_PAGE_TYPE_REG: a regular page
- * %SGX_PAGE_TYPE_VA: a VA page
- * %SGX_PAGE_TYPE_TRIM: a page in trimmed state
+ * @SGX_PAGE_TYPE_SECS: a SECS page
+ * @SGX_PAGE_TYPE_TCS: a TCS page
+ * @SGX_PAGE_TYPE_REG: a regular page
+ * @SGX_PAGE_TYPE_VA: a VA page
+ * @SGX_PAGE_TYPE_TRIM: a page in trimmed state
*
* Make sure when making changes to this enum that its values can still fit
* in the bitfield within &struct sgx_encl_page
@@ -275,14 +282,14 @@ enum sgx_page_type {
/**
* enum sgx_secinfo_flags - the flags field in &struct sgx_secinfo
- * %SGX_SECINFO_R: allow read
- * %SGX_SECINFO_W: allow write
- * %SGX_SECINFO_X: allow execution
- * %SGX_SECINFO_SECS: a SECS page
- * %SGX_SECINFO_TCS: a TCS page
- * %SGX_SECINFO_REG: a regular page
- * %SGX_SECINFO_VA: a VA page
- * %SGX_SECINFO_TRIM: a page in trimmed state
+ * @SGX_SECINFO_R: allow read
+ * @SGX_SECINFO_W: allow write
+ * @SGX_SECINFO_X: allow execution
+ * @SGX_SECINFO_SECS: a SECS page
+ * @SGX_SECINFO_TCS: a TCS page
+ * @SGX_SECINFO_REG: a regular page
+ * @SGX_SECINFO_VA: a VA page
+ * @SGX_SECINFO_TRIM: a page in trimmed state
*/
enum sgx_secinfo_flags {
SGX_SECINFO_R = BIT(0),
diff --git a/arch/x86/include/asm/shared/msr.h b/arch/x86/include/asm/shared/msr.h
index 1e6ec10b3a15..a20b1c08c99f 100644
--- a/arch/x86/include/asm/shared/msr.h
+++ b/arch/x86/include/asm/shared/msr.h
@@ -12,4 +12,19 @@ struct msr {
};
};
+/*
+ * The kernel proper already defines rdmsr()/wrmsr(), but they are not for the
+ * boot kernel since they rely on tracepoint/exception handling infrastructure
+ * that's not available here.
+ */
+static inline void raw_rdmsr(unsigned int reg, struct msr *m)
+{
+ asm volatile("rdmsr" : "=a" (m->l), "=d" (m->h) : "c" (reg));
+}
+
+static inline void raw_wrmsr(unsigned int reg, const struct msr *m)
+{
+ asm volatile("wrmsr" : : "c" (reg), "a"(m->l), "d" (m->h) : "memory");
+}
+
#endif /* _ASM_X86_SHARED_MSR_H */
diff --git a/arch/x86/include/asm/smap.h b/arch/x86/include/asm/smap.h
index 4f84d421d1cf..20a3baae9568 100644
--- a/arch/x86/include/asm/smap.h
+++ b/arch/x86/include/asm/smap.h
@@ -23,18 +23,55 @@
#else /* __ASSEMBLER__ */
+/*
+ * The CLAC/STAC instructions toggle the enforcement of
+ * X86_FEATURE_SMAP along with X86_FEATURE_LASS.
+ *
+ * SMAP enforcement is based on the _PAGE_BIT_USER bit in the page
+ * tables. The kernel is not allowed to touch pages with that bit set
+ * unless the AC bit is set.
+ *
+ * Use stac()/clac() when accessing userspace (_PAGE_USER) mappings,
+ * regardless of location.
+ *
+ * Note: a barrier is implicit in alternative().
+ */
+
static __always_inline void clac(void)
{
- /* Note: a barrier is implicit in alternative() */
alternative("", "clac", X86_FEATURE_SMAP);
}
static __always_inline void stac(void)
{
- /* Note: a barrier is implicit in alternative() */
alternative("", "stac", X86_FEATURE_SMAP);
}
+/*
+ * LASS enforcement is based on bit 63 of the virtual address. The
+ * kernel is not allowed to touch memory in the lower half of the
+ * virtual address space.
+ *
+ * Use lass_stac()/lass_clac() to toggle the AC bit for kernel data
+ * accesses (!_PAGE_USER) that are blocked by LASS, but not by SMAP.
+ *
+ * Even with the AC bit set, LASS will continue to block instruction
+ * fetches from the user half of the address space. To allow those,
+ * clear CR4.LASS to disable the LASS mechanism entirely.
+ *
+ * Note: a barrier is implicit in alternative().
+ */
+
+static __always_inline void lass_clac(void)
+{
+ alternative("", "clac", X86_FEATURE_LASS);
+}
+
+static __always_inline void lass_stac(void)
+{
+ alternative("", "stac", X86_FEATURE_LASS);
+}
+
static __always_inline unsigned long smap_save(void)
{
unsigned long flags;
diff --git a/arch/x86/include/asm/smp.h b/arch/x86/include/asm/smp.h
index 22bfebe6776d..84951572ab81 100644
--- a/arch/x86/include/asm/smp.h
+++ b/arch/x86/include/asm/smp.h
@@ -109,7 +109,7 @@ int common_cpu_up(unsigned int cpunum, struct task_struct *tidle);
int native_kick_ap(unsigned int cpu, struct task_struct *tidle);
int native_cpu_disable(void);
void __noreturn hlt_play_dead(void);
-void native_play_dead(void);
+void __noreturn native_play_dead(void);
void play_dead_common(void);
void wbinvd_on_cpu(int cpu);
void wbinvd_on_all_cpus(void);
diff --git a/arch/x86/include/asm/string.h b/arch/x86/include/asm/string.h
index c3c2c1914d65..9cb5aae7fba9 100644
--- a/arch/x86/include/asm/string.h
+++ b/arch/x86/include/asm/string.h
@@ -1,6 +1,32 @@
/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_STRING_H
+#define _ASM_X86_STRING_H
+
#ifdef CONFIG_X86_32
# include <asm/string_32.h>
#else
# include <asm/string_64.h>
#endif
+
+static __always_inline void *__inline_memcpy(void *to, const void *from, size_t len)
+{
+ void *ret = to;
+
+ asm volatile("rep movsb"
+ : "+D" (to), "+S" (from), "+c" (len)
+ : : "memory");
+ return ret;
+}
+
+static __always_inline void *__inline_memset(void *s, int v, size_t n)
+{
+ void *ret = s;
+
+ asm volatile("rep stosb"
+ : "+D" (s), "+c" (n)
+ : "a" ((uint8_t)v)
+ : "memory");
+ return ret;
+}
+
+#endif /* _ASM_X86_STRING_H */
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 17f6c3fedeee..0581c477d466 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -701,5 +701,6 @@ DEFINE_GHCB_ACCESSORS(sw_exit_info_1)
DEFINE_GHCB_ACCESSORS(sw_exit_info_2)
DEFINE_GHCB_ACCESSORS(sw_scratch)
DEFINE_GHCB_ACCESSORS(xcr0)
+DEFINE_GHCB_ACCESSORS(xss)
#endif
diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h
index 21041898157a..1fadf0cf520c 100644
--- a/arch/x86/include/asm/topology.h
+++ b/arch/x86/include/asm/topology.h
@@ -218,6 +218,12 @@ static inline unsigned int topology_amd_nodes_per_pkg(void)
return __amd_nodes_per_pkg;
}
+#else /* CONFIG_SMP */
+static inline int topology_phys_to_logical_pkg(unsigned int pkg) { return 0; }
+static inline int topology_max_smt_threads(void) { return 1; }
+static inline unsigned int topology_amd_nodes_per_pkg(void) { return 1; }
+#endif /* !CONFIG_SMP */
+
extern struct cpumask __cpu_primary_thread_mask;
#define cpu_primary_thread_mask ((const struct cpumask *)&__cpu_primary_thread_mask)
@@ -241,12 +247,6 @@ static inline bool topology_is_core_online(unsigned int cpu)
}
#define topology_is_core_online topology_is_core_online
-#else /* CONFIG_SMP */
-static inline int topology_phys_to_logical_pkg(unsigned int pkg) { return 0; }
-static inline int topology_max_smt_threads(void) { return 1; }
-static inline unsigned int topology_amd_nodes_per_pkg(void) { return 1; }
-#endif /* !CONFIG_SMP */
-
static inline void arch_fix_phys_package_id(int num, u32 slot)
{
}
@@ -325,4 +325,6 @@ static inline void freq_invariance_set_perf_ratio(u64 ratio, bool turbo_disabled
extern void arch_scale_freq_tick(void);
#define arch_scale_freq_tick arch_scale_freq_tick
+extern int arch_sched_node_distance(int from, int to);
+
#endif /* _ASM_X86_TOPOLOGY_H */
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index 91a3fb8ae7ff..367297b188c3 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -528,18 +528,18 @@ static __must_check __always_inline bool user_access_begin(const void __user *pt
#define user_access_save() smap_save()
#define user_access_restore(x) smap_restore(x)
-#define unsafe_put_user(x, ptr, label) \
+#define arch_unsafe_put_user(x, ptr, label) \
__put_user_size((__typeof__(*(ptr)))(x), (ptr), sizeof(*(ptr)), label)
#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
-#define unsafe_get_user(x, ptr, err_label) \
+#define arch_unsafe_get_user(x, ptr, err_label) \
do { \
__inttype(*(ptr)) __gu_val; \
__get_user_size(__gu_val, (ptr), sizeof(*(ptr)), err_label); \
(x) = (__force __typeof__(*(ptr)))__gu_val; \
} while (0)
#else // !CONFIG_CC_HAS_ASM_GOTO_OUTPUT
-#define unsafe_get_user(x, ptr, err_label) \
+#define arch_unsafe_get_user(x, ptr, err_label) \
do { \
int __gu_err; \
__inttype(*(ptr)) __gu_val; \
@@ -618,11 +618,11 @@ do { \
} while (0)
#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
-#define __get_kernel_nofault(dst, src, type, err_label) \
+#define arch_get_kernel_nofault(dst, src, type, err_label) \
__get_user_size(*((type *)(dst)), (__force type __user *)(src), \
sizeof(type), err_label)
#else // !CONFIG_CC_HAS_ASM_GOTO_OUTPUT
-#define __get_kernel_nofault(dst, src, type, err_label) \
+#define arch_get_kernel_nofault(dst, src, type, err_label) \
do { \
int __kr_err; \
\
@@ -633,7 +633,7 @@ do { \
} while (0)
#endif // CONFIG_CC_HAS_ASM_GOTO_OUTPUT
-#define __put_kernel_nofault(dst, src, type, err_label) \
+#define arch_put_kernel_nofault(dst, src, type, err_label) \
__put_user_size(*((type *)(src)), (__force type __user *)(dst), \
sizeof(type), err_label)
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index c8a5ae35c871..641f45c22f9d 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -12,12 +12,12 @@
#include <asm/cpufeatures.h>
#include <asm/page.h>
#include <asm/percpu.h>
-#include <asm/runtime-const.h>
-/*
- * Virtual variable: there's no actual backing store for this,
- * it can purely be used as 'runtime_const_ptr(USER_PTR_MAX)'
- */
+#ifdef MODULE
+ #define runtime_const_ptr(sym) (sym)
+#else
+ #include <asm/runtime-const.h>
+#endif
extern unsigned long USER_PTR_MAX;
#ifdef CONFIG_ADDRESS_MASKING
diff --git a/arch/x86/include/asm/unwind_user.h b/arch/x86/include/asm/unwind_user.h
new file mode 100644
index 000000000000..12064284bc4e
--- /dev/null
+++ b/arch/x86/include/asm/unwind_user.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_UNWIND_USER_H
+#define _ASM_X86_UNWIND_USER_H
+
+#ifdef CONFIG_HAVE_UNWIND_USER_FP
+
+#include <asm/ptrace.h>
+#include <asm/uprobes.h>
+
+#define ARCH_INIT_USER_FP_FRAME(ws) \
+ .cfa_off = 2*(ws), \
+ .ra_off = -1*(ws), \
+ .fp_off = -2*(ws), \
+ .use_fp = true,
+
+#define ARCH_INIT_USER_FP_ENTRY_FRAME(ws) \
+ .cfa_off = 1*(ws), \
+ .ra_off = -1*(ws), \
+ .fp_off = 0, \
+ .use_fp = false,
+
+static inline int unwind_user_word_size(struct pt_regs *regs)
+{
+ /* We can't unwind VM86 stacks */
+ if (regs->flags & X86_VM_MASK)
+ return 0;
+#ifdef CONFIG_X86_64
+ if (!user_64bit_mode(regs))
+ return sizeof(int);
+#endif
+ return sizeof(long);
+}
+
+static inline bool unwind_user_at_function_start(struct pt_regs *regs)
+{
+ return is_uprobe_at_func_entry(regs);
+}
+
+#endif /* CONFIG_HAVE_UNWIND_USER_FP */
+
+#endif /* _ASM_X86_UNWIND_USER_H */
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index 1ee2e5115955..362210c79998 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -62,4 +62,13 @@ struct arch_uprobe_task {
unsigned int saved_tf;
};
+#ifdef CONFIG_UPROBES
+extern bool is_uprobe_at_func_entry(struct pt_regs *regs);
+#else
+static bool is_uprobe_at_func_entry(struct pt_regs *regs)
+{
+ return false;
+}
+#endif /* CONFIG_UPROBES */
+
#endif /* _ASM_UPROBES_H */
diff --git a/arch/x86/include/uapi/asm/processor-flags.h b/arch/x86/include/uapi/asm/processor-flags.h
index f1a4adc78272..81d0c8bf1137 100644
--- a/arch/x86/include/uapi/asm/processor-flags.h
+++ b/arch/x86/include/uapi/asm/processor-flags.h
@@ -136,6 +136,8 @@
#define X86_CR4_PKE _BITUL(X86_CR4_PKE_BIT)
#define X86_CR4_CET_BIT 23 /* enable Control-flow Enforcement Technology */
#define X86_CR4_CET _BITUL(X86_CR4_CET_BIT)
+#define X86_CR4_LASS_BIT 27 /* enable Linear Address Space Separation support */
+#define X86_CR4_LASS _BITUL(X86_CR4_LASS_BIT)
#define X86_CR4_LAM_SUP_BIT 28 /* LAM for supervisor pointers */
#define X86_CR4_LAM_SUP _BITUL(X86_CR4_LAM_SUP_BIT)
diff --git a/arch/x86/include/uapi/asm/sgx.h b/arch/x86/include/uapi/asm/sgx.h
index 2dd35bbdc822..3c4d52072189 100644
--- a/arch/x86/include/uapi/asm/sgx.h
+++ b/arch/x86/include/uapi/asm/sgx.h
@@ -10,7 +10,7 @@
/**
* enum sgx_page_flags - page control flags
- * %SGX_PAGE_MEASURE: Measure the page contents with a sequence of
+ * @SGX_PAGE_MEASURE: Measure the page contents with a sequence of
* ENCLS[EEXTEND] operations.
*/
enum sgx_page_flags {
@@ -143,6 +143,12 @@ struct sgx_enclave_run;
/**
* typedef sgx_enclave_user_handler_t - Exit handler function accepted by
* __vdso_sgx_enter_enclave()
+ * @rdi: RDI at the time of EEXIT, undefined on AEX
+ * @rsi: RSI at the time of EEXIT, undefined on AEX
+ * @rdx: RDX at the time of EEXIT, undefined on AEX
+ * @rsp: RSP (untrusted) at the time of EEXIT or AEX
+ * @r8: R8 at the time of EEXIT, undefined on AEX
+ * @r9: R9 at the time of EEXIT, undefined on AEX
* @run: The run instance given by the caller
*
* The register parameters contain the snapshot of their values at enclave
@@ -166,7 +172,7 @@ typedef int (*sgx_enclave_user_handler_t)(long rdi, long rsi, long rdx,
* @exception_addr: The address that triggered the exception
* @user_handler: User provided callback run on exception
* @user_data: Data passed to the user handler
- * @reserved Reserved for future extensions
+ * @reserved: Reserved for future extensions
*
* If @user_handler is provided, the handler will be invoked on all return paths
* of the normal flow. The user handler may transfer control, e.g. via a
diff --git a/arch/x86/include/uapi/asm/vmx.h b/arch/x86/include/uapi/asm/vmx.h
index 9792e329343e..1baa86dfe029 100644
--- a/arch/x86/include/uapi/asm/vmx.h
+++ b/arch/x86/include/uapi/asm/vmx.h
@@ -93,6 +93,7 @@
#define EXIT_REASON_TPAUSE 68
#define EXIT_REASON_BUS_LOCK 74
#define EXIT_REASON_NOTIFY 75
+#define EXIT_REASON_SEAMCALL 76
#define EXIT_REASON_TDCALL 77
#define EXIT_REASON_MSR_READ_IMM 84
#define EXIT_REASON_MSR_WRITE_IMM 85
diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c
index 0916f00a992e..e21419e686eb 100644
--- a/arch/x86/kernel/acpi/apei.c
+++ b/arch/x86/kernel/acpi/apei.c
@@ -19,6 +19,8 @@ int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data)
if (!cmc->enabled)
return 0;
+ mce_save_apei_thr_limit(cmc->notify.error_threshold_value);
+
/*
* We expect HEST to provide a list of MC banks that report errors
* in firmware first mode. Otherwise, return non-zero value to
diff --git a/arch/x86/kernel/acpi/cppc.c b/arch/x86/kernel/acpi/cppc.c
index 7047124490f6..d7c8ef1e354d 100644
--- a/arch/x86/kernel/acpi/cppc.c
+++ b/arch/x86/kernel/acpi/cppc.c
@@ -196,7 +196,7 @@ int amd_detect_prefcore(bool *detected)
break;
}
- for_each_present_cpu(cpu) {
+ for_each_online_cpu(cpu) {
u32 tmp;
int ret;
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 8ee5ff547357..74f4c659f9c9 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -9,6 +9,7 @@
#include <asm/text-patching.h>
#include <asm/insn.h>
+#include <asm/insn-eval.h>
#include <asm/ibt.h>
#include <asm/set_memory.h>
#include <asm/nmi.h>
@@ -346,25 +347,6 @@ static void add_nop(u8 *buf, unsigned int len)
}
/*
- * Matches NOP and NOPL, not any of the other possible NOPs.
- */
-static bool insn_is_nop(struct insn *insn)
-{
- /* Anything NOP, but no REP NOP */
- if (insn->opcode.bytes[0] == 0x90 &&
- (!insn->prefixes.nbytes || insn->prefixes.bytes[0] != 0xF3))
- return true;
-
- /* NOPL */
- if (insn->opcode.bytes[0] == 0x0F && insn->opcode.bytes[1] == 0x1F)
- return true;
-
- /* TODO: more nops */
-
- return false;
-}
-
-/*
* Find the offset of the first non-NOP instruction starting at @offset
* but no further than @len.
*/
@@ -559,7 +541,7 @@ EXPORT_SYMBOL(BUG_func);
* Rewrite the "call BUG_func" replacement to point to the target of the
* indirect pv_ops call "call *disp(%ip)".
*/
-static int alt_replace_call(u8 *instr, u8 *insn_buff, struct alt_instr *a)
+static unsigned int alt_replace_call(u8 *instr, u8 *insn_buff, struct alt_instr *a)
{
void *target, *bug = &BUG_func;
s32 disp;
@@ -643,7 +625,7 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
* order.
*/
for (a = start; a < end; a++) {
- int insn_buff_sz = 0;
+ unsigned int insn_buff_sz = 0;
/*
* In case of nested ALTERNATIVE()s the outer alternative might
@@ -683,11 +665,8 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,
memcpy(insn_buff, replacement, a->replacementlen);
insn_buff_sz = a->replacementlen;
- if (a->flags & ALT_FLAG_DIRECT_CALL) {
+ if (a->flags & ALT_FLAG_DIRECT_CALL)
insn_buff_sz = alt_replace_call(instr, insn_buff, a);
- if (insn_buff_sz < 0)
- continue;
- }
for (; insn_buff_sz < a->instrlen; insn_buff_sz++)
insn_buff[insn_buff_sz] = 0x90;
@@ -2244,21 +2223,34 @@ int alternatives_text_reserved(void *start, void *end)
* See entry_{32,64}.S for more details.
*/
-/*
- * We define the int3_magic() function in assembly to control the calling
- * convention such that we can 'call' it from assembly.
- */
+extern void int3_selftest_asm(unsigned int *ptr);
-extern void int3_magic(unsigned int *ptr); /* defined in asm */
+asm (
+" .pushsection .init.text, \"ax\", @progbits\n"
+" .type int3_selftest_asm, @function\n"
+"int3_selftest_asm:\n"
+ ANNOTATE_NOENDBR
+ /*
+ * INT3 padded with NOP to CALL_INSN_SIZE. The INT3 triggers an
+ * exception, then the int3_exception_nb notifier emulates a call to
+ * int3_selftest_callee().
+ */
+" int3; nop; nop; nop; nop\n"
+ ASM_RET
+" .size int3_selftest_asm, . - int3_selftest_asm\n"
+" .popsection\n"
+);
+
+extern void int3_selftest_callee(unsigned int *ptr);
asm (
" .pushsection .init.text, \"ax\", @progbits\n"
-" .type int3_magic, @function\n"
-"int3_magic:\n"
+" .type int3_selftest_callee, @function\n"
+"int3_selftest_callee:\n"
ANNOTATE_NOENDBR
-" movl $1, (%" _ASM_ARG1 ")\n"
+" movl $0x1234, (%" _ASM_ARG1 ")\n"
ASM_RET
-" .size int3_magic, .-int3_magic\n"
+" .size int3_selftest_callee, . - int3_selftest_callee\n"
" .popsection\n"
);
@@ -2267,7 +2259,7 @@ extern void int3_selftest_ip(void); /* defined in asm below */
static int __init
int3_exception_notify(struct notifier_block *self, unsigned long val, void *data)
{
- unsigned long selftest = (unsigned long)&int3_selftest_ip;
+ unsigned long selftest = (unsigned long)&int3_selftest_asm;
struct die_args *args = data;
struct pt_regs *regs = args->regs;
@@ -2282,7 +2274,7 @@ int3_exception_notify(struct notifier_block *self, unsigned long val, void *data
if (regs->ip - INT3_INSN_SIZE != selftest)
return NOTIFY_DONE;
- int3_emulate_call(regs, (unsigned long)&int3_magic);
+ int3_emulate_call(regs, (unsigned long)&int3_selftest_callee);
return NOTIFY_STOP;
}
@@ -2298,19 +2290,11 @@ static noinline void __init int3_selftest(void)
BUG_ON(register_die_notifier(&int3_exception_nb));
/*
- * Basically: int3_magic(&val); but really complicated :-)
- *
- * INT3 padded with NOP to CALL_INSN_SIZE. The int3_exception_nb
- * notifier above will emulate CALL for us.
+ * Basically: int3_selftest_callee(&val); but really complicated :-)
*/
- asm volatile ("int3_selftest_ip:\n\t"
- ANNOTATE_NOENDBR
- " int3; nop; nop; nop; nop\n\t"
- : ASM_CALL_CONSTRAINT
- : __ASM_SEL_RAW(a, D) (&val)
- : "memory");
+ int3_selftest_asm(&val);
- BUG_ON(val != 1);
+ BUG_ON(val != 0x1234);
unregister_die_notifier(&int3_exception_nb);
}
@@ -2469,16 +2453,30 @@ void __init_or_module text_poke_early(void *addr, const void *opcode,
__ro_after_init struct mm_struct *text_poke_mm;
__ro_after_init unsigned long text_poke_mm_addr;
+/*
+ * Text poking creates and uses a mapping in the lower half of the
+ * address space. Relax LASS enforcement when accessing the poking
+ * address.
+ *
+ * objtool enforces a strict policy of "no function calls within AC=1
+ * regions". Adhere to the policy by using inline versions of
+ * memcpy()/memset() that will never result in a function call.
+ */
+
static void text_poke_memcpy(void *dst, const void *src, size_t len)
{
- memcpy(dst, src, len);
+ lass_stac();
+ __inline_memcpy(dst, src, len);
+ lass_clac();
}
static void text_poke_memset(void *dst, const void *src, size_t len)
{
int c = *(const int *)src;
- memset(dst, c, len);
+ lass_stac();
+ __inline_memset(dst, c, len);
+ lass_clac();
}
typedef void text_poke_f(void *dst, const void *src, size_t len);
diff --git a/arch/x86/kernel/amd_node.c b/arch/x86/kernel/amd_node.c
index a40176b62eb5..3d0a4768d603 100644
--- a/arch/x86/kernel/amd_node.c
+++ b/arch/x86/kernel/amd_node.c
@@ -34,62 +34,6 @@ struct pci_dev *amd_node_get_func(u16 node, u8 func)
return pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(AMD_NODE0_PCI_SLOT + node, func));
}
-#define DF_BLK_INST_CNT 0x040
-#define DF_CFG_ADDR_CNTL_LEGACY 0x084
-#define DF_CFG_ADDR_CNTL_DF4 0xC04
-
-#define DF_MAJOR_REVISION GENMASK(27, 24)
-
-static u16 get_cfg_addr_cntl_offset(struct pci_dev *df_f0)
-{
- u32 reg;
-
- /*
- * Revision fields added for DF4 and later.
- *
- * Major revision of '0' is found pre-DF4. Field is Read-as-Zero.
- */
- if (pci_read_config_dword(df_f0, DF_BLK_INST_CNT, &reg))
- return 0;
-
- if (reg & DF_MAJOR_REVISION)
- return DF_CFG_ADDR_CNTL_DF4;
-
- return DF_CFG_ADDR_CNTL_LEGACY;
-}
-
-struct pci_dev *amd_node_get_root(u16 node)
-{
- struct pci_dev *root;
- u16 cntl_off;
- u8 bus;
-
- if (!cpu_feature_enabled(X86_FEATURE_ZEN))
- return NULL;
-
- /*
- * D18F0xXXX [Config Address Control] (DF::CfgAddressCntl)
- * Bits [7:0] (SecBusNum) holds the bus number of the root device for
- * this Data Fabric instance. The segment, device, and function will be 0.
- */
- struct pci_dev *df_f0 __free(pci_dev_put) = amd_node_get_func(node, 0);
- if (!df_f0)
- return NULL;
-
- cntl_off = get_cfg_addr_cntl_offset(df_f0);
- if (!cntl_off)
- return NULL;
-
- if (pci_read_config_byte(df_f0, cntl_off, &bus))
- return NULL;
-
- /* Grab the pointer for the actual root device instance. */
- root = pci_get_domain_bus_and_slot(0, bus, 0);
-
- pci_dbg(root, "is root for AMD node %u\n", node);
- return root;
-}
-
static struct pci_dev **amd_roots;
/* Protect the PCI config register pairs used for SMN. */
@@ -274,51 +218,21 @@ DEFINE_SHOW_STORE_ATTRIBUTE(smn_node);
DEFINE_SHOW_STORE_ATTRIBUTE(smn_address);
DEFINE_SHOW_STORE_ATTRIBUTE(smn_value);
-static int amd_cache_roots(void)
-{
- u16 node, num_nodes = amd_num_nodes();
-
- amd_roots = kcalloc(num_nodes, sizeof(*amd_roots), GFP_KERNEL);
- if (!amd_roots)
- return -ENOMEM;
-
- for (node = 0; node < num_nodes; node++)
- amd_roots[node] = amd_node_get_root(node);
-
- return 0;
-}
-
-static int reserve_root_config_spaces(void)
+static struct pci_dev *get_next_root(struct pci_dev *root)
{
- struct pci_dev *root = NULL;
- struct pci_bus *bus = NULL;
-
- while ((bus = pci_find_next_bus(bus))) {
- /* Root device is Device 0 Function 0 on each Primary Bus. */
- root = pci_get_slot(bus, 0);
- if (!root)
+ while ((root = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, root))) {
+ /* Root device is Device 0 Function 0. */
+ if (root->devfn)
continue;
if (root->vendor != PCI_VENDOR_ID_AMD &&
root->vendor != PCI_VENDOR_ID_HYGON)
continue;
- pci_dbg(root, "Reserving PCI config space\n");
-
- /*
- * There are a few SMN index/data pairs and other registers
- * that shouldn't be accessed by user space.
- * So reserve the entire PCI config space for simplicity rather
- * than covering specific registers piecemeal.
- */
- if (!pci_request_config_region_exclusive(root, 0, PCI_CFG_SPACE_SIZE, NULL)) {
- pci_err(root, "Failed to reserve config space\n");
- return -EEXIST;
- }
+ break;
}
- smn_exclusive = true;
- return 0;
+ return root;
}
static bool enable_dfs;
@@ -332,7 +246,8 @@ __setup("amd_smn_debugfs_enable", amd_smn_enable_dfs);
static int __init amd_smn_init(void)
{
- int err;
+ u16 count, num_roots, roots_per_node, node, num_nodes;
+ struct pci_dev *root;
if (!cpu_feature_enabled(X86_FEATURE_ZEN))
return 0;
@@ -342,13 +257,48 @@ static int __init amd_smn_init(void)
if (amd_roots)
return 0;
- err = amd_cache_roots();
- if (err)
- return err;
+ num_roots = 0;
+ root = NULL;
+ while ((root = get_next_root(root))) {
+ pci_dbg(root, "Reserving PCI config space\n");
- err = reserve_root_config_spaces();
- if (err)
- return err;
+ /*
+ * There are a few SMN index/data pairs and other registers
+ * that shouldn't be accessed by user space. So reserve the
+ * entire PCI config space for simplicity rather than covering
+ * specific registers piecemeal.
+ */
+ if (!pci_request_config_region_exclusive(root, 0, PCI_CFG_SPACE_SIZE, NULL)) {
+ pci_err(root, "Failed to reserve config space\n");
+ return -EEXIST;
+ }
+
+ num_roots++;
+ }
+
+ pr_debug("Found %d AMD root devices\n", num_roots);
+
+ if (!num_roots)
+ return -ENODEV;
+
+ num_nodes = amd_num_nodes();
+ amd_roots = kcalloc(num_nodes, sizeof(*amd_roots), GFP_KERNEL);
+ if (!amd_roots)
+ return -ENOMEM;
+
+ roots_per_node = num_roots / num_nodes;
+
+ count = 0;
+ node = 0;
+ root = NULL;
+ while (node < num_nodes && (root = get_next_root(root))) {
+ /* Use one root for each node and skip the rest. */
+ if (count++ % roots_per_node)
+ continue;
+
+ pci_dbg(root, "is root for AMD node %u\n", node);
+ amd_roots[node++] = root;
+ }
if (enable_dfs) {
debugfs_dir = debugfs_create_dir("amd_smn", arch_debugfs_dir);
@@ -358,6 +308,8 @@ static int __init amd_smn_init(void)
debugfs_create_file("value", 0600, debugfs_dir, NULL, &smn_value_fops);
}
+ smn_exclusive = true;
+
return 0;
}
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 680d305589a3..9c29e12b84e5 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -36,6 +36,7 @@
#include <linux/dmi.h>
#include <linux/smp.h>
#include <linux/mm.h>
+#include <linux/kvm_types.h>
#include <xen/xen.h>
@@ -173,6 +174,7 @@ static struct resource lapic_resource = {
.flags = IORESOURCE_MEM | IORESOURCE_BUSY,
};
+/* Measured in ticks per HZ. */
unsigned int lapic_timer_period = 0;
static void apic_pm_activate(void);
@@ -792,6 +794,7 @@ static int __init calibrate_APIC_clock(void)
{
struct clock_event_device *levt = this_cpu_ptr(&lapic_events);
u64 tsc_perj = 0, tsc_start = 0;
+ long delta_tsc_khz, bus_khz;
unsigned long jif_start;
unsigned long deltaj;
long delta, deltatsc;
@@ -894,14 +897,15 @@ static int __init calibrate_APIC_clock(void)
apic_pr_verbose("..... calibration result: %u\n", lapic_timer_period);
if (boot_cpu_has(X86_FEATURE_TSC)) {
- apic_pr_verbose("..... CPU clock speed is %ld.%04ld MHz.\n",
- (deltatsc / LAPIC_CAL_LOOPS) / (1000000 / HZ),
- (deltatsc / LAPIC_CAL_LOOPS) % (1000000 / HZ));
+ delta_tsc_khz = (deltatsc * HZ) / (1000 * LAPIC_CAL_LOOPS);
+
+ apic_pr_verbose("..... CPU clock speed is %ld.%03ld MHz.\n",
+ delta_tsc_khz / 1000, delta_tsc_khz % 1000);
}
- apic_pr_verbose("..... host bus clock speed is %u.%04u MHz.\n",
- lapic_timer_period / (1000000 / HZ),
- lapic_timer_period % (1000000 / HZ));
+ bus_khz = (long)lapic_timer_period * HZ / 1000;
+ apic_pr_verbose("..... host bus clock speed is %ld.%03ld MHz.\n",
+ bus_khz / 1000, bus_khz % 1000);
/*
* Do a sanity check on the APIC calibration result
@@ -2316,7 +2320,7 @@ u32 x86_msi_msg_get_destid(struct msi_msg *msg, bool extid)
dest |= msg->arch_addr_hi.destid_8_31 << 8;
return dest;
}
-EXPORT_SYMBOL_GPL(x86_msi_msg_get_destid);
+EXPORT_SYMBOL_FOR_KVM(x86_msi_msg_get_destid);
static void __init apic_bsp_up_setup(void)
{
diff --git a/arch/x86/kernel/apic/apic_common.c b/arch/x86/kernel/apic/apic_common.c
index 9ef3be866832..2ed3b5c88c7f 100644
--- a/arch/x86/kernel/apic/apic_common.c
+++ b/arch/x86/kernel/apic/apic_common.c
@@ -4,6 +4,7 @@
* SPDX-License-Identifier: GPL-2.0
*/
#include <linux/irq.h>
+#include <linux/kvm_types.h>
#include <asm/apic.h>
#include "local.h"
@@ -25,7 +26,7 @@ u32 default_cpu_present_to_apicid(int mps_cpu)
else
return BAD_APICID;
}
-EXPORT_SYMBOL_GPL(default_cpu_present_to_apicid);
+EXPORT_SYMBOL_FOR_KVM(default_cpu_present_to_apicid);
/*
* Set up the logical destination ID when the APIC operates in logical
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 5ba2feb2c04c..1e0442e867b1 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -2864,7 +2864,7 @@ int mp_irqdomain_alloc(struct irq_domain *domain, unsigned int virq,
ioapic = mp_irqdomain_ioapic_idx(domain);
pin = info->ioapic.pin;
- if (irq_find_mapping(domain, (irq_hw_number_t)pin) > 0)
+ if (irq_resolve_mapping(domain, (irq_hw_number_t)pin))
return -EEXIST;
data = kzalloc(sizeof(*data), GFP_KERNEL);
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index ccaa51ce63f6..bc94ff1e250a 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -3,7 +3,7 @@
#include <linux/bitops.h>
#include <linux/elf.h>
#include <linux/mm.h>
-
+#include <linux/kvm_types.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
@@ -516,7 +516,7 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
setup_force_cpu_cap(X86_FEATURE_ZEN5);
break;
case 0x50 ... 0x5f:
- case 0x90 ... 0xaf:
+ case 0x80 ... 0xaf:
case 0xc0 ... 0xcf:
setup_force_cpu_cap(X86_FEATURE_ZEN6);
break;
@@ -1035,8 +1035,26 @@ static void init_amd_zen4(struct cpuinfo_x86 *c)
}
}
+static const struct x86_cpu_id zen5_rdseed_microcode[] = {
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x02, 0x1, 0x0b00215a),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x08, 0x1, 0x0b008121),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x11, 0x0, 0x0b101054),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x24, 0x0, 0x0b204037),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x44, 0x0, 0x0b404035),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x44, 0x1, 0x0b404108),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x60, 0x0, 0x0b600037),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x68, 0x0, 0x0b608038),
+ ZEN_MODEL_STEP_UCODE(0x1a, 0x70, 0x0, 0x0b700037),
+ {},
+};
+
static void init_amd_zen5(struct cpuinfo_x86 *c)
{
+ if (!x86_match_min_microcode_rev(zen5_rdseed_microcode)) {
+ clear_cpu_cap(c, X86_FEATURE_RDSEED);
+ msr_clear_bit(MSR_AMD64_CPUID_FN_7, 18);
+ pr_emerg_once("RDSEED32 is broken. Disabling the corresponding CPUID bit.\n");
+ }
}
static void init_amd(struct cpuinfo_x86 *c)
@@ -1300,7 +1318,7 @@ unsigned long amd_get_dr_addr_mask(unsigned int dr)
return per_cpu(amd_dr_addr_mask[dr], smp_processor_id());
}
-EXPORT_SYMBOL_GPL(amd_get_dr_addr_mask);
+EXPORT_SYMBOL_FOR_KVM(amd_get_dr_addr_mask);
static void zenbleed_check_cpu(void *unused)
{
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index 6a526ae1fe99..d8660770dc6a 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -16,6 +16,7 @@
#include <linux/sched/smt.h>
#include <linux/pgtable.h>
#include <linux/bpf.h>
+#include <linux/kvm_types.h>
#include <asm/spec-ctrl.h>
#include <asm/cmdline.h>
@@ -53,56 +54,8 @@
* mitigation option.
*/
-static void __init spectre_v1_select_mitigation(void);
-static void __init spectre_v1_apply_mitigation(void);
-static void __init spectre_v2_select_mitigation(void);
-static void __init spectre_v2_update_mitigation(void);
-static void __init spectre_v2_apply_mitigation(void);
-static void __init retbleed_select_mitigation(void);
-static void __init retbleed_update_mitigation(void);
-static void __init retbleed_apply_mitigation(void);
-static void __init spectre_v2_user_select_mitigation(void);
-static void __init spectre_v2_user_update_mitigation(void);
-static void __init spectre_v2_user_apply_mitigation(void);
-static void __init ssb_select_mitigation(void);
-static void __init ssb_apply_mitigation(void);
-static void __init l1tf_select_mitigation(void);
-static void __init l1tf_apply_mitigation(void);
-static void __init mds_select_mitigation(void);
-static void __init mds_update_mitigation(void);
-static void __init mds_apply_mitigation(void);
-static void __init taa_select_mitigation(void);
-static void __init taa_update_mitigation(void);
-static void __init taa_apply_mitigation(void);
-static void __init mmio_select_mitigation(void);
-static void __init mmio_update_mitigation(void);
-static void __init mmio_apply_mitigation(void);
-static void __init rfds_select_mitigation(void);
-static void __init rfds_update_mitigation(void);
-static void __init rfds_apply_mitigation(void);
-static void __init srbds_select_mitigation(void);
-static void __init srbds_apply_mitigation(void);
-static void __init l1d_flush_select_mitigation(void);
-static void __init srso_select_mitigation(void);
-static void __init srso_update_mitigation(void);
-static void __init srso_apply_mitigation(void);
-static void __init gds_select_mitigation(void);
-static void __init gds_apply_mitigation(void);
-static void __init bhi_select_mitigation(void);
-static void __init bhi_update_mitigation(void);
-static void __init bhi_apply_mitigation(void);
-static void __init its_select_mitigation(void);
-static void __init its_update_mitigation(void);
-static void __init its_apply_mitigation(void);
-static void __init tsa_select_mitigation(void);
-static void __init tsa_apply_mitigation(void);
-static void __init vmscape_select_mitigation(void);
-static void __init vmscape_update_mitigation(void);
-static void __init vmscape_apply_mitigation(void);
-
/* The base value of the SPEC_CTRL MSR without task-specific bits set */
u64 x86_spec_ctrl_base;
-EXPORT_SYMBOL_GPL(x86_spec_ctrl_base);
/* The current value of the SPEC_CTRL MSR with task-specific bits set */
DEFINE_PER_CPU(u64, x86_spec_ctrl_current);
@@ -179,7 +132,7 @@ DEFINE_STATIC_KEY_FALSE(switch_mm_always_ibpb);
/* Control IBPB on vCPU load */
DEFINE_STATIC_KEY_FALSE(switch_vcpu_ibpb);
-EXPORT_SYMBOL_GPL(switch_vcpu_ibpb);
+EXPORT_SYMBOL_FOR_KVM(switch_vcpu_ibpb);
/* Control CPU buffer clear before idling (halt, mwait) */
DEFINE_STATIC_KEY_FALSE(cpu_buf_idle_clear);
@@ -198,7 +151,7 @@ DEFINE_STATIC_KEY_FALSE(switch_mm_cond_l1d_flush);
* mitigation is required.
*/
DEFINE_STATIC_KEY_FALSE(cpu_buf_vm_clear);
-EXPORT_SYMBOL_GPL(cpu_buf_vm_clear);
+EXPORT_SYMBOL_FOR_KVM(cpu_buf_vm_clear);
#undef pr_fmt
#define pr_fmt(fmt) "mitigations: " fmt
@@ -233,99 +186,6 @@ static void __init cpu_print_attack_vectors(void)
}
}
-void __init cpu_select_mitigations(void)
-{
- /*
- * Read the SPEC_CTRL MSR to account for reserved bits which may
- * have unknown values. AMD64_LS_CFG MSR is cached in the early AMD
- * init code as it is not enumerated and depends on the family.
- */
- if (cpu_feature_enabled(X86_FEATURE_MSR_SPEC_CTRL)) {
- rdmsrq(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
-
- /*
- * Previously running kernel (kexec), may have some controls
- * turned ON. Clear them and let the mitigations setup below
- * rediscover them based on configuration.
- */
- x86_spec_ctrl_base &= ~SPEC_CTRL_MITIGATIONS_MASK;
- }
-
- x86_arch_cap_msr = x86_read_arch_cap_msr();
-
- cpu_print_attack_vectors();
-
- /* Select the proper CPU mitigations before patching alternatives: */
- spectre_v1_select_mitigation();
- spectre_v2_select_mitigation();
- retbleed_select_mitigation();
- spectre_v2_user_select_mitigation();
- ssb_select_mitigation();
- l1tf_select_mitigation();
- mds_select_mitigation();
- taa_select_mitigation();
- mmio_select_mitigation();
- rfds_select_mitigation();
- srbds_select_mitigation();
- l1d_flush_select_mitigation();
- srso_select_mitigation();
- gds_select_mitigation();
- its_select_mitigation();
- bhi_select_mitigation();
- tsa_select_mitigation();
- vmscape_select_mitigation();
-
- /*
- * After mitigations are selected, some may need to update their
- * choices.
- */
- spectre_v2_update_mitigation();
- /*
- * retbleed_update_mitigation() relies on the state set by
- * spectre_v2_update_mitigation(); specifically it wants to know about
- * spectre_v2=ibrs.
- */
- retbleed_update_mitigation();
- /*
- * its_update_mitigation() depends on spectre_v2_update_mitigation()
- * and retbleed_update_mitigation().
- */
- its_update_mitigation();
-
- /*
- * spectre_v2_user_update_mitigation() depends on
- * retbleed_update_mitigation(), specifically the STIBP
- * selection is forced for UNRET or IBPB.
- */
- spectre_v2_user_update_mitigation();
- mds_update_mitigation();
- taa_update_mitigation();
- mmio_update_mitigation();
- rfds_update_mitigation();
- bhi_update_mitigation();
- /* srso_update_mitigation() depends on retbleed_update_mitigation(). */
- srso_update_mitigation();
- vmscape_update_mitigation();
-
- spectre_v1_apply_mitigation();
- spectre_v2_apply_mitigation();
- retbleed_apply_mitigation();
- spectre_v2_user_apply_mitigation();
- ssb_apply_mitigation();
- l1tf_apply_mitigation();
- mds_apply_mitigation();
- taa_apply_mitigation();
- mmio_apply_mitigation();
- rfds_apply_mitigation();
- srbds_apply_mitigation();
- srso_apply_mitigation();
- gds_apply_mitigation();
- its_apply_mitigation();
- bhi_apply_mitigation();
- tsa_apply_mitigation();
- vmscape_apply_mitigation();
-}
-
/*
* NOTE: This function is *only* called for SVM, since Intel uses
* MSR_IA32_SPEC_CTRL for SSBD.
@@ -366,7 +226,7 @@ x86_virt_spec_ctrl(u64 guest_virt_spec_ctrl, bool setguest)
speculation_ctrl_update(tif);
}
}
-EXPORT_SYMBOL_GPL(x86_virt_spec_ctrl);
+EXPORT_SYMBOL_FOR_KVM(x86_virt_spec_ctrl);
static void x86_amd_ssb_disable(void)
{
@@ -1032,7 +892,7 @@ bool gds_ucode_mitigated(void)
return (gds_mitigation == GDS_MITIGATION_FULL ||
gds_mitigation == GDS_MITIGATION_FULL_LOCKED);
}
-EXPORT_SYMBOL_GPL(gds_ucode_mitigated);
+EXPORT_SYMBOL_FOR_KVM(gds_ucode_mitigated);
void update_gds_msr(void)
{
@@ -1463,7 +1323,9 @@ static void __init retbleed_update_mitigation(void)
break;
default:
if (retbleed_mitigation != RETBLEED_MITIGATION_STUFF) {
- pr_err(RETBLEED_INTEL_MSG);
+ if (retbleed_mitigation != RETBLEED_MITIGATION_NONE)
+ pr_err(RETBLEED_INTEL_MSG);
+
retbleed_mitigation = RETBLEED_MITIGATION_NONE;
}
}
@@ -1825,13 +1687,6 @@ void unpriv_ebpf_notify(int new_state)
}
#endif
-static inline bool match_option(const char *arg, int arglen, const char *opt)
-{
- int len = strlen(opt);
-
- return len == arglen && !strncmp(arg, opt, len);
-}
-
/* The kernel command line selection for spectre v2 */
enum spectre_v2_mitigation_cmd {
SPECTRE_V2_CMD_NONE,
@@ -2864,7 +2719,7 @@ void x86_spec_ctrl_setup_ap(void)
}
bool itlb_multihit_kvm_mitigation;
-EXPORT_SYMBOL_GPL(itlb_multihit_kvm_mitigation);
+EXPORT_SYMBOL_FOR_KVM(itlb_multihit_kvm_mitigation);
#undef pr_fmt
#define pr_fmt(fmt) "L1TF: " fmt
@@ -2872,11 +2727,9 @@ EXPORT_SYMBOL_GPL(itlb_multihit_kvm_mitigation);
/* Default mitigation for L1TF-affected CPUs */
enum l1tf_mitigations l1tf_mitigation __ro_after_init =
IS_ENABLED(CONFIG_MITIGATION_L1TF) ? L1TF_MITIGATION_AUTO : L1TF_MITIGATION_OFF;
-#if IS_ENABLED(CONFIG_KVM_INTEL)
-EXPORT_SYMBOL_GPL(l1tf_mitigation);
-#endif
+EXPORT_SYMBOL_FOR_KVM(l1tf_mitigation);
enum vmx_l1d_flush_state l1tf_vmx_mitigation = VMENTER_L1D_FLUSH_AUTO;
-EXPORT_SYMBOL_GPL(l1tf_vmx_mitigation);
+EXPORT_SYMBOL_FOR_KVM(l1tf_vmx_mitigation);
/*
* These CPUs all support 44bits physical address space internally in the
@@ -3376,6 +3229,99 @@ void cpu_bugs_smt_update(void)
mutex_unlock(&spec_ctrl_mutex);
}
+void __init cpu_select_mitigations(void)
+{
+ /*
+ * Read the SPEC_CTRL MSR to account for reserved bits which may
+ * have unknown values. AMD64_LS_CFG MSR is cached in the early AMD
+ * init code as it is not enumerated and depends on the family.
+ */
+ if (cpu_feature_enabled(X86_FEATURE_MSR_SPEC_CTRL)) {
+ rdmsrq(MSR_IA32_SPEC_CTRL, x86_spec_ctrl_base);
+
+ /*
+ * Previously running kernel (kexec), may have some controls
+ * turned ON. Clear them and let the mitigations setup below
+ * rediscover them based on configuration.
+ */
+ x86_spec_ctrl_base &= ~SPEC_CTRL_MITIGATIONS_MASK;
+ }
+
+ x86_arch_cap_msr = x86_read_arch_cap_msr();
+
+ cpu_print_attack_vectors();
+
+ /* Select the proper CPU mitigations before patching alternatives: */
+ spectre_v1_select_mitigation();
+ spectre_v2_select_mitigation();
+ retbleed_select_mitigation();
+ spectre_v2_user_select_mitigation();
+ ssb_select_mitigation();
+ l1tf_select_mitigation();
+ mds_select_mitigation();
+ taa_select_mitigation();
+ mmio_select_mitigation();
+ rfds_select_mitigation();
+ srbds_select_mitigation();
+ l1d_flush_select_mitigation();
+ srso_select_mitigation();
+ gds_select_mitigation();
+ its_select_mitigation();
+ bhi_select_mitigation();
+ tsa_select_mitigation();
+ vmscape_select_mitigation();
+
+ /*
+ * After mitigations are selected, some may need to update their
+ * choices.
+ */
+ spectre_v2_update_mitigation();
+ /*
+ * retbleed_update_mitigation() relies on the state set by
+ * spectre_v2_update_mitigation(); specifically it wants to know about
+ * spectre_v2=ibrs.
+ */
+ retbleed_update_mitigation();
+ /*
+ * its_update_mitigation() depends on spectre_v2_update_mitigation()
+ * and retbleed_update_mitigation().
+ */
+ its_update_mitigation();
+
+ /*
+ * spectre_v2_user_update_mitigation() depends on
+ * retbleed_update_mitigation(), specifically the STIBP
+ * selection is forced for UNRET or IBPB.
+ */
+ spectre_v2_user_update_mitigation();
+ mds_update_mitigation();
+ taa_update_mitigation();
+ mmio_update_mitigation();
+ rfds_update_mitigation();
+ bhi_update_mitigation();
+ /* srso_update_mitigation() depends on retbleed_update_mitigation(). */
+ srso_update_mitigation();
+ vmscape_update_mitigation();
+
+ spectre_v1_apply_mitigation();
+ spectre_v2_apply_mitigation();
+ retbleed_apply_mitigation();
+ spectre_v2_user_apply_mitigation();
+ ssb_apply_mitigation();
+ l1tf_apply_mitigation();
+ mds_apply_mitigation();
+ taa_apply_mitigation();
+ mmio_apply_mitigation();
+ rfds_apply_mitigation();
+ srbds_apply_mitigation();
+ srso_apply_mitigation();
+ gds_apply_mitigation();
+ its_apply_mitigation();
+ bhi_apply_mitigation();
+ tsa_apply_mitigation();
+ vmscape_apply_mitigation();
+}
+
#ifdef CONFIG_SYSFS
#define L1TF_DEFAULT_MSG "Mitigation: PTE Inversion"
diff --git a/arch/x86/kernel/cpu/bus_lock.c b/arch/x86/kernel/cpu/bus_lock.c
index 981f8b1f0792..dbc99a47be45 100644
--- a/arch/x86/kernel/cpu/bus_lock.c
+++ b/arch/x86/kernel/cpu/bus_lock.c
@@ -6,6 +6,7 @@
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/cpuhotplug.h>
+#include <linux/kvm_types.h>
#include <asm/cpu_device_id.h>
#include <asm/cmdline.h>
#include <asm/traps.h>
@@ -289,7 +290,7 @@ bool handle_guest_split_lock(unsigned long ip)
force_sig_fault(SIGBUS, BUS_ADRALN, NULL);
return false;
}
-EXPORT_SYMBOL_GPL(handle_guest_split_lock);
+EXPORT_SYMBOL_FOR_KVM(handle_guest_split_lock);
void bus_lock_init(void)
{
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index c7d3512914ca..e7ab22fce3b5 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -7,6 +7,7 @@
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/export.h>
+#include <linux/kvm_types.h>
#include <linux/percpu.h>
#include <linux/string.h>
#include <linux/ctype.h>
@@ -78,6 +79,10 @@
DEFINE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info);
EXPORT_PER_CPU_SYMBOL(cpu_info);
+/* Used for modules: built-in code uses runtime constants */
+unsigned long USER_PTR_MAX;
+EXPORT_SYMBOL(USER_PTR_MAX);
+
u32 elf_hwcap2 __read_mostly;
/* Number of siblings per CPU package */
@@ -401,6 +406,28 @@ out:
cr4_clear_bits(X86_CR4_UMIP);
}
+static __always_inline void setup_lass(struct cpuinfo_x86 *c)
+{
+ if (!cpu_feature_enabled(X86_FEATURE_LASS))
+ return;
+
+ /*
+ * Legacy vsyscall page access causes a #GP when LASS is active.
+ * Disable LASS because the #GP handler doesn't support vsyscall
+ * emulation.
+ *
+ * Also disable LASS when running under EFI, as some runtime and
+ * boot services rely on 1:1 mappings in the lower half.
+ */
+ if (IS_ENABLED(CONFIG_X86_VSYSCALL_EMULATION) ||
+ IS_ENABLED(CONFIG_EFI)) {
+ setup_clear_cpu_cap(X86_FEATURE_LASS);
+ return;
+ }
+
+ cr4_set_bits(X86_CR4_LASS);
+}
+
/* These bits should not change their value after CPU init is finished. */
static const unsigned long cr4_pinned_mask = X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_UMIP |
X86_CR4_FSGSBASE | X86_CR4_CET | X86_CR4_FRED;
@@ -460,14 +487,14 @@ void cr4_update_irqsoff(unsigned long set, unsigned long clear)
__write_cr4(newval);
}
}
-EXPORT_SYMBOL(cr4_update_irqsoff);
+EXPORT_SYMBOL_FOR_KVM(cr4_update_irqsoff);
/* Read the CR4 shadow. */
unsigned long cr4_read_shadow(void)
{
return this_cpu_read(cpu_tlbstate.cr4);
}
-EXPORT_SYMBOL_GPL(cr4_read_shadow);
+EXPORT_SYMBOL_FOR_KVM(cr4_read_shadow);
void cr4_init(void)
{
@@ -722,7 +749,7 @@ void load_direct_gdt(int cpu)
gdt_descr.size = GDT_SIZE - 1;
load_gdt(&gdt_descr);
}
-EXPORT_SYMBOL_GPL(load_direct_gdt);
+EXPORT_SYMBOL_FOR_KVM(load_direct_gdt);
/* Load a fixmap remapping of the per-cpu GDT */
void load_fixmap_gdt(int cpu)
@@ -1021,12 +1048,8 @@ void get_cpu_cap(struct cpuinfo_x86 *c)
c->x86_capability[CPUID_8000_0001_EDX] = edx;
}
- if (c->extended_cpuid_level >= 0x80000007) {
- cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
-
- c->x86_capability[CPUID_8000_0007_EBX] = ebx;
- c->x86_power = edx;
- }
+ if (c->extended_cpuid_level >= 0x80000007)
+ c->x86_power = cpuid_edx(0x80000007);
if (c->extended_cpuid_level >= 0x80000008) {
cpuid(0x80000008, &eax, &ebx, &ecx, &edx);
@@ -2011,10 +2034,10 @@ static void identify_cpu(struct cpuinfo_x86 *c)
/* Disable the PN if appropriate */
squash_the_stupid_serial_number(c);
- /* Set up SMEP/SMAP/UMIP */
setup_smep(c);
setup_smap(c);
setup_umip(c);
+ setup_lass(c);
/* Enable FSGSBASE instructions if available. */
if (cpu_has(c, X86_FEATURE_FSGSBASE)) {
@@ -2579,7 +2602,7 @@ void __init arch_cpu_finalize_init(void)
alternative_instructions();
if (IS_ENABLED(CONFIG_X86_64)) {
- unsigned long USER_PTR_MAX = TASK_SIZE_MAX;
+ USER_PTR_MAX = TASK_SIZE_MAX;
/*
* Enable this when LAM is gated on LASS support
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h
index bc38b2d56f26..5c7a3a71191a 100644
--- a/arch/x86/kernel/cpu/cpu.h
+++ b/arch/x86/kernel/cpu/cpu.h
@@ -42,15 +42,6 @@ extern const struct cpu_dev *const __x86_cpu_dev_start[],
*const __x86_cpu_dev_end[];
#ifdef CONFIG_CPU_SUP_INTEL
-enum tsx_ctrl_states {
- TSX_CTRL_ENABLE,
- TSX_CTRL_DISABLE,
- TSX_CTRL_RTM_ALWAYS_ABORT,
- TSX_CTRL_NOT_SUPPORTED,
-};
-
-extern __ro_after_init enum tsx_ctrl_states tsx_ctrl_state;
-
extern void __init tsx_init(void);
void tsx_ap_init(void);
void intel_unlock_cpuid_leafs(struct cpuinfo_x86 *c);
diff --git a/arch/x86/kernel/cpu/cpuid-deps.c b/arch/x86/kernel/cpu/cpuid-deps.c
index 46efcbd6afa4..146f6f8b0650 100644
--- a/arch/x86/kernel/cpu/cpuid-deps.c
+++ b/arch/x86/kernel/cpu/cpuid-deps.c
@@ -72,6 +72,7 @@ static const struct cpuid_dep cpuid_deps[] = {
{ X86_FEATURE_CQM_MBM_LOCAL, X86_FEATURE_CQM_LLC },
{ X86_FEATURE_BMEC, X86_FEATURE_CQM_MBM_TOTAL },
{ X86_FEATURE_BMEC, X86_FEATURE_CQM_MBM_LOCAL },
+ { X86_FEATURE_SDCIAE, X86_FEATURE_CAT_L3 },
{ X86_FEATURE_AVX512_BF16, X86_FEATURE_AVX512VL },
{ X86_FEATURE_AVX512_FP16, X86_FEATURE_AVX512BW },
{ X86_FEATURE_ENQCMD, X86_FEATURE_XSAVES },
@@ -79,6 +80,7 @@ static const struct cpuid_dep cpuid_deps[] = {
{ X86_FEATURE_SGX_LC, X86_FEATURE_SGX },
{ X86_FEATURE_SGX1, X86_FEATURE_SGX },
{ X86_FEATURE_SGX2, X86_FEATURE_SGX1 },
+ { X86_FEATURE_SGX_EUPDATESVN, X86_FEATURE_SGX1 },
{ X86_FEATURE_SGX_EDECCSSA, X86_FEATURE_SGX1 },
{ X86_FEATURE_XFD, X86_FEATURE_XSAVES },
{ X86_FEATURE_XFD, X86_FEATURE_XGETBV1 },
@@ -89,6 +91,7 @@ static const struct cpuid_dep cpuid_deps[] = {
{ X86_FEATURE_SHSTK, X86_FEATURE_XSAVES },
{ X86_FEATURE_FRED, X86_FEATURE_LKGS },
{ X86_FEATURE_SPEC_CTRL_SSBD, X86_FEATURE_SPEC_CTRL },
+ { X86_FEATURE_LASS, X86_FEATURE_SMAP },
{}
};
diff --git a/arch/x86/kernel/cpu/mce/amd.c b/arch/x86/kernel/cpu/mce/amd.c
index d6906442f49b..3f1dda355307 100644
--- a/arch/x86/kernel/cpu/mce/amd.c
+++ b/arch/x86/kernel/cpu/mce/amd.c
@@ -43,9 +43,6 @@
/* Deferred error settings */
#define MSR_CU_DEF_ERR 0xC0000410
#define MASK_DEF_LVTOFF 0x000000F0
-#define MASK_DEF_INT_TYPE 0x00000006
-#define DEF_LVT_OFF 0x2
-#define DEF_INT_TYPE_APIC 0x2
/* Scalable MCA: */
@@ -54,6 +51,17 @@
static bool thresholding_irq_en;
+struct mce_amd_cpu_data {
+ mce_banks_t thr_intr_banks;
+ mce_banks_t dfr_intr_banks;
+
+ u32 thr_intr_en: 1,
+ dfr_intr_en: 1,
+ __resv: 30;
+};
+
+static DEFINE_PER_CPU_READ_MOSTLY(struct mce_amd_cpu_data, mce_amd_data);
+
static const char * const th_names[] = {
"load_store",
"insn_fetch",
@@ -79,6 +87,8 @@ struct smca_bank {
const struct smca_hwid *hwid;
u32 id; /* Value of MCA_IPID[InstanceId]. */
u8 sysfs_id; /* Value used for sysfs name. */
+ u64 paddrv :1, /* Physical Address Valid bit in MCA_CONFIG */
+ __reserved :63;
};
static DEFINE_PER_CPU_READ_MOSTLY(struct smca_bank[MAX_NR_BANKS], smca_banks);
@@ -264,6 +274,7 @@ void (*deferred_error_int_vector)(void) = default_deferred_error_interrupt;
static void smca_configure(unsigned int bank, unsigned int cpu)
{
+ struct mce_amd_cpu_data *data = this_cpu_ptr(&mce_amd_data);
u8 *bank_counts = this_cpu_ptr(smca_bank_counts);
const struct smca_hwid *s_hwid;
unsigned int i, hwid_mcatype;
@@ -294,11 +305,33 @@ static void smca_configure(unsigned int bank, unsigned int cpu)
* APIC based interrupt. First, check that no interrupt has been
* set.
*/
- if ((low & BIT(5)) && !((high >> 5) & 0x3))
+ if ((low & BIT(5)) && !((high >> 5) & 0x3) && data->dfr_intr_en) {
+ __set_bit(bank, data->dfr_intr_banks);
high |= BIT(5);
+ }
+
+ /*
+ * SMCA Corrected Error Interrupt
+ *
+ * MCA_CONFIG[IntPresent] is bit 10, and tells us if the bank can
+ * send an MCA Thresholding interrupt without the OS initializing
+ * this feature. This can be used if the threshold limit is managed
+ * by the platform.
+ *
+ * MCA_CONFIG[IntEn] is bit 40 (8 in the high portion of the MSR).
+ * The OS should set this to inform the platform that the OS is ready
+ * to handle the MCA Thresholding interrupt.
+ */
+ if ((low & BIT(10)) && data->thr_intr_en) {
+ __set_bit(bank, data->thr_intr_banks);
+ high |= BIT(8);
+ }
this_cpu_ptr(mce_banks_array)[bank].lsb_in_status = !!(low & BIT(8));
+ if (low & MCI_CONFIG_PADDRV)
+ this_cpu_ptr(smca_banks)[bank].paddrv = 1;
+
wrmsr(smca_config, low, high);
}
@@ -368,6 +401,14 @@ static bool lvt_off_valid(struct threshold_block *b, int apic, u32 lo, u32 hi)
{
int msr = (hi & MASK_LVTOFF_HI) >> 20;
+ /*
+ * On SMCA CPUs, LVT offset is programmed at a different MSR, and
+ * the BIOS provides the value. The original field where LVT offset
+ * was set is reserved. Return early here:
+ */
+ if (mce_flags.smca)
+ return false;
+
if (apic < 0) {
pr_err(FW_BUG "cpu %d, failed to setup threshold interrupt "
"for bank %d, block %d (MSR%08X=0x%x%08x)\n", b->cpu,
@@ -376,14 +417,6 @@ static bool lvt_off_valid(struct threshold_block *b, int apic, u32 lo, u32 hi)
}
if (apic != msr) {
- /*
- * On SMCA CPUs, LVT offset is programmed at a different MSR, and
- * the BIOS provides the value. The original field where LVT offset
- * was set is reserved. Return early here:
- */
- if (mce_flags.smca)
- return false;
-
pr_err(FW_BUG "cpu %d, invalid threshold interrupt offset %d "
"for bank %d, block %d (MSR%08X=0x%x%08x)\n",
b->cpu, apic, b->bank, b->block, b->address, hi, lo);
@@ -443,6 +476,36 @@ static void threshold_restart_block(void *_tr)
wrmsr(tr->b->address, lo, hi);
}
+static void threshold_restart_bank(unsigned int bank, bool intr_en)
+{
+ struct threshold_bank **thr_banks = this_cpu_read(threshold_banks);
+ struct threshold_block *block, *tmp;
+ struct thresh_restart tr;
+
+ if (!thr_banks || !thr_banks[bank])
+ return;
+
+ memset(&tr, 0, sizeof(tr));
+
+ list_for_each_entry_safe(block, tmp, &thr_banks[bank]->miscj, miscj) {
+ tr.b = block;
+ tr.b->interrupt_enable = intr_en;
+ threshold_restart_block(&tr);
+ }
+}
+
+/* Try to use the threshold limit reported through APEI. */
+static u16 get_thr_limit(void)
+{
+ u32 thr_limit = mce_get_apei_thr_limit();
+
+ /* Fallback to old default if APEI limit is not available. */
+ if (!thr_limit)
+ return THRESHOLD_MAX;
+
+ return min(thr_limit, THRESHOLD_MAX);
+}
+
static void mce_threshold_block_init(struct threshold_block *b, int offset)
{
struct thresh_restart tr = {
@@ -451,7 +514,7 @@ static void mce_threshold_block_init(struct threshold_block *b, int offset)
.lvt_off = offset,
};
- b->threshold_limit = THRESHOLD_MAX;
+ b->threshold_limit = get_thr_limit();
threshold_restart_block(&tr);
};
@@ -464,41 +527,6 @@ static int setup_APIC_mce_threshold(int reserved, int new)
return reserved;
}
-static int setup_APIC_deferred_error(int reserved, int new)
-{
- if (reserved < 0 && !setup_APIC_eilvt(new, DEFERRED_ERROR_VECTOR,
- APIC_EILVT_MSG_FIX, 0))
- return new;
-
- return reserved;
-}
-
-static void deferred_error_interrupt_enable(struct cpuinfo_x86 *c)
-{
- u32 low = 0, high = 0;
- int def_offset = -1, def_new;
-
- if (rdmsr_safe(MSR_CU_DEF_ERR, &low, &high))
- return;
-
- def_new = (low & MASK_DEF_LVTOFF) >> 4;
- if (!(low & MASK_DEF_LVTOFF)) {
- pr_err(FW_BUG "Your BIOS is not setting up LVT offset 0x2 for deferred error IRQs correctly.\n");
- def_new = DEF_LVT_OFF;
- low = (low & ~MASK_DEF_LVTOFF) | (DEF_LVT_OFF << 4);
- }
-
- def_offset = setup_APIC_deferred_error(def_offset, def_new);
- if ((def_offset == def_new) &&
- (deferred_error_int_vector != amd_deferred_error_interrupt))
- deferred_error_int_vector = amd_deferred_error_interrupt;
-
- if (!mce_flags.smca)
- low = (low & ~MASK_DEF_INT_TYPE) | DEF_INT_TYPE_APIC;
-
- wrmsr(MSR_CU_DEF_ERR, low, high);
-}
-
static u32 get_block_address(u32 current_addr, u32 low, u32 high,
unsigned int bank, unsigned int block,
unsigned int cpu)
@@ -534,12 +562,10 @@ static u32 get_block_address(u32 current_addr, u32 low, u32 high,
return addr;
}
-static int
-prepare_threshold_block(unsigned int bank, unsigned int block, u32 addr,
- int offset, u32 misc_high)
+static int prepare_threshold_block(unsigned int bank, unsigned int block, u32 addr,
+ int offset, u32 misc_high)
{
unsigned int cpu = smp_processor_id();
- u32 smca_low, smca_high;
struct threshold_block b;
int new;
@@ -556,20 +582,13 @@ prepare_threshold_block(unsigned int bank, unsigned int block, u32 addr,
if (!b.interrupt_capable)
goto done;
+ __set_bit(bank, this_cpu_ptr(&mce_amd_data)->thr_intr_banks);
b.interrupt_enable = 1;
- if (!mce_flags.smca) {
- new = (misc_high & MASK_LVTOFF_HI) >> 20;
- goto set_offset;
- }
-
- /* Gather LVT offset for thresholding: */
- if (rdmsr_safe(MSR_CU_DEF_ERR, &smca_low, &smca_high))
- goto out;
-
- new = (smca_low & SMCA_THR_LVT_OFF) >> 12;
+ if (mce_flags.smca)
+ goto done;
-set_offset:
+ new = (misc_high & MASK_LVTOFF_HI) >> 20;
offset = setup_APIC_mce_threshold(offset, new);
if (offset == new)
thresholding_irq_en = true;
@@ -577,7 +596,6 @@ set_offset:
done:
mce_threshold_block_init(&b, offset);
-out:
return offset;
}
@@ -668,6 +686,32 @@ static void amd_apply_cpu_quirks(struct cpuinfo_x86 *c)
mce_banks[0].ctl = 0;
}
+/*
+ * Enable the APIC LVT interrupt vectors once per-CPU. This should be done before hardware is
+ * ready to send interrupts.
+ *
+ * Individual error sources are enabled later during per-bank init.
+ */
+static void smca_enable_interrupt_vectors(void)
+{
+ struct mce_amd_cpu_data *data = this_cpu_ptr(&mce_amd_data);
+ u64 mca_intr_cfg, offset;
+
+ if (!mce_flags.smca || !mce_flags.succor)
+ return;
+
+ if (rdmsrq_safe(MSR_CU_DEF_ERR, &mca_intr_cfg))
+ return;
+
+ offset = (mca_intr_cfg & SMCA_THR_LVT_OFF) >> 12;
+ if (!setup_APIC_eilvt(offset, THRESHOLD_APIC_VECTOR, APIC_EILVT_MSG_FIX, 0))
+ data->thr_intr_en = 1;
+
+ offset = (mca_intr_cfg & MASK_DEF_LVTOFF) >> 4;
+ if (!setup_APIC_eilvt(offset, DEFERRED_ERROR_VECTOR, APIC_EILVT_MSG_FIX, 0))
+ data->dfr_intr_en = 1;
+}
+
/* cpu init entry point, called from mce.c with preempt off */
void mce_amd_feature_init(struct cpuinfo_x86 *c)
{
@@ -679,10 +723,16 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
mce_flags.amd_threshold = 1;
+ smca_enable_interrupt_vectors();
+
for (bank = 0; bank < this_cpu_read(mce_num_banks); ++bank) {
- if (mce_flags.smca)
+ if (mce_flags.smca) {
smca_configure(bank, cpu);
+ if (!this_cpu_ptr(&mce_amd_data)->thr_intr_en)
+ continue;
+ }
+
disable_err_thresholding(c, bank);
for (block = 0; block < NR_BLOCKS; ++block) {
@@ -703,9 +753,6 @@ void mce_amd_feature_init(struct cpuinfo_x86 *c)
offset = prepare_threshold_block(bank, block, address, offset, high);
}
}
-
- if (mce_flags.succor)
- deferred_error_interrupt_enable(c);
}
void smca_bsp_init(void)
@@ -748,9 +795,9 @@ bool amd_mce_is_memory_error(struct mce *m)
}
/*
- * AMD systems do not have an explicit indicator that the value in MCA_ADDR is
- * a system physical address. Therefore, individual cases need to be detected.
- * Future cases and checks will be added as needed.
+ * Some AMD systems have an explicit indicator that the value in MCA_ADDR is a
+ * system physical address. Individual cases though, need to be detected for
+ * other systems. Future cases will be added as needed.
*
* 1) General case
* a) Assume address is not usable.
@@ -764,6 +811,8 @@ bool amd_mce_is_memory_error(struct mce *m)
* a) Reported in legacy bank 4 with extended error code (XEC) 8.
* b) MCA_STATUS[43] is *not* defined as poison in legacy bank 4. Therefore,
* this bit should not be checked.
+ * 4) MCI_STATUS_PADDRVAL is set
+ * a) Will provide a valid system physical address.
*
* NOTE: SMCA UMC memory errors fall into case #1.
*/
@@ -777,6 +826,9 @@ bool amd_mce_usable_address(struct mce *m)
return false;
}
+ if (this_cpu_ptr(smca_banks)[m->bank].paddrv)
+ return m->status & MCI_STATUS_PADDRV;
+
/* Check poison bit for all other bank types. */
if (m->status & MCI_STATUS_POISON)
return true;
@@ -785,37 +837,6 @@ bool amd_mce_usable_address(struct mce *m)
return false;
}
-static void __log_error(unsigned int bank, u64 status, u64 addr, u64 misc)
-{
- struct mce_hw_err err;
- struct mce *m = &err.m;
-
- mce_prep_record(&err);
-
- m->status = status;
- m->misc = misc;
- m->bank = bank;
- m->tsc = rdtsc();
-
- if (m->status & MCI_STATUS_ADDRV) {
- m->addr = addr;
-
- smca_extract_err_addr(m);
- }
-
- if (mce_flags.smca) {
- rdmsrq(MSR_AMD64_SMCA_MCx_IPID(bank), m->ipid);
-
- if (m->status & MCI_STATUS_SYNDV) {
- rdmsrq(MSR_AMD64_SMCA_MCx_SYND(bank), m->synd);
- rdmsrq(MSR_AMD64_SMCA_MCx_SYND1(bank), err.vendor.amd.synd1);
- rdmsrq(MSR_AMD64_SMCA_MCx_SYND2(bank), err.vendor.amd.synd2);
- }
- }
-
- mce_log(&err);
-}
-
DEFINE_IDTENTRY_SYSVEC(sysvec_deferred_error)
{
trace_deferred_error_apic_entry(DEFERRED_ERROR_VECTOR);
@@ -825,103 +846,20 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_deferred_error)
apic_eoi();
}
-/*
- * Returns true if the logged error is deferred. False, otherwise.
- */
-static inline bool
-_log_error_bank(unsigned int bank, u32 msr_stat, u32 msr_addr, u64 misc)
-{
- u64 status, addr = 0;
-
- rdmsrq(msr_stat, status);
- if (!(status & MCI_STATUS_VAL))
- return false;
-
- if (status & MCI_STATUS_ADDRV)
- rdmsrq(msr_addr, addr);
-
- __log_error(bank, status, addr, misc);
-
- wrmsrq(msr_stat, 0);
-
- return status & MCI_STATUS_DEFERRED;
-}
-
-static bool _log_error_deferred(unsigned int bank, u32 misc)
-{
- if (!_log_error_bank(bank, mca_msr_reg(bank, MCA_STATUS),
- mca_msr_reg(bank, MCA_ADDR), misc))
- return false;
-
- /*
- * Non-SMCA systems don't have MCA_DESTAT/MCA_DEADDR registers.
- * Return true here to avoid accessing these registers.
- */
- if (!mce_flags.smca)
- return true;
-
- /* Clear MCA_DESTAT if the deferred error was logged from MCA_STATUS. */
- wrmsrq(MSR_AMD64_SMCA_MCx_DESTAT(bank), 0);
- return true;
-}
-
-/*
- * We have three scenarios for checking for Deferred errors:
- *
- * 1) Non-SMCA systems check MCA_STATUS and log error if found.
- * 2) SMCA systems check MCA_STATUS. If error is found then log it and also
- * clear MCA_DESTAT.
- * 3) SMCA systems check MCA_DESTAT, if error was not found in MCA_STATUS, and
- * log it.
- */
-static void log_error_deferred(unsigned int bank)
-{
- if (_log_error_deferred(bank, 0))
- return;
-
- /*
- * Only deferred errors are logged in MCA_DE{STAT,ADDR} so just check
- * for a valid error.
- */
- _log_error_bank(bank, MSR_AMD64_SMCA_MCx_DESTAT(bank),
- MSR_AMD64_SMCA_MCx_DEADDR(bank), 0);
-}
-
/* APIC interrupt handler for deferred errors */
static void amd_deferred_error_interrupt(void)
{
- unsigned int bank;
-
- for (bank = 0; bank < this_cpu_read(mce_num_banks); ++bank)
- log_error_deferred(bank);
+ machine_check_poll(MCP_TIMESTAMP, &this_cpu_ptr(&mce_amd_data)->dfr_intr_banks);
}
-static void log_error_thresholding(unsigned int bank, u64 misc)
+void mce_amd_handle_storm(unsigned int bank, bool on)
{
- _log_error_deferred(bank, misc);
+ threshold_restart_bank(bank, on);
}
-static void log_and_reset_block(struct threshold_block *block)
+static void amd_reset_thr_limit(unsigned int bank)
{
- struct thresh_restart tr;
- u32 low = 0, high = 0;
-
- if (!block)
- return;
-
- if (rdmsr_safe(block->address, &low, &high))
- return;
-
- if (!(high & MASK_OVERFLOW_HI))
- return;
-
- /* Log the MCE which caused the threshold event. */
- log_error_thresholding(block->bank, ((u64)high << 32) | low);
-
- /* Reset threshold block after logging error. */
- memset(&tr, 0, sizeof(tr));
- tr.b = block;
- threshold_restart_block(&tr);
+ threshold_restart_bank(bank, true);
}
/*
@@ -930,33 +868,21 @@ static void log_and_reset_block(struct threshold_block *block)
*/
static void amd_threshold_interrupt(void)
{
- struct threshold_bank **bp = this_cpu_read(threshold_banks), *thr_bank;
- unsigned int bank, cpu = smp_processor_id();
- struct threshold_block *block, *tmp;
-
- /*
- * Validate that the threshold bank has been initialized already. The
- * handler is installed at boot time, but on a hotplug event the
- * interrupt might fire before the data has been initialized.
- */
- if (!bp)
- return;
-
- for (bank = 0; bank < this_cpu_read(mce_num_banks); ++bank) {
- if (!(per_cpu(bank_map, cpu) & BIT_ULL(bank)))
- continue;
-
- thr_bank = bp[bank];
- if (!thr_bank)
- continue;
-
- list_for_each_entry_safe(block, tmp, &thr_bank->miscj, miscj)
- log_and_reset_block(block);
- }
+ machine_check_poll(MCP_TIMESTAMP, &this_cpu_ptr(&mce_amd_data)->thr_intr_banks);
}
void amd_clear_bank(struct mce *m)
{
+ amd_reset_thr_limit(m->bank);
+
+ /* Clear MCA_DESTAT for all deferred errors even those logged in MCA_STATUS. */
+ if (m->status & MCI_STATUS_DEFERRED)
+ mce_wrmsrq(MSR_AMD64_SMCA_MCx_DESTAT(m->bank), 0);
+
+ /* Don't clear MCA_STATUS if MCA_DESTAT was used exclusively. */
+ if (m->kflags & MCE_CHECK_DFR_REGS)
+ return;
+
mce_wrmsrq(mca_msr_reg(m->bank, MCA_STATUS), 0);
}
@@ -1172,7 +1098,7 @@ static int allocate_threshold_blocks(unsigned int cpu, struct threshold_bank *tb
b->address = address;
b->interrupt_enable = 0;
b->interrupt_capable = lvt_interrupt_supported(bank, high);
- b->threshold_limit = THRESHOLD_MAX;
+ b->threshold_limit = get_thr_limit();
if (b->interrupt_capable) {
default_attrs[2] = &interrupt_enable.attr;
@@ -1183,6 +1109,8 @@ static int allocate_threshold_blocks(unsigned int cpu, struct threshold_bank *tb
list_add(&b->miscj, &tb->miscj);
+ mce_threshold_block_init(b, (high & MASK_LVTOFF_HI) >> 20);
+
err = kobject_init_and_add(&b->kobj, &threshold_ktype, tb->kobj, get_name(cpu, bank, b));
if (err)
goto out_free;
diff --git a/arch/x86/kernel/cpu/mce/core.c b/arch/x86/kernel/cpu/mce/core.c
index 460e90a1a0b1..4aff14e04287 100644
--- a/arch/x86/kernel/cpu/mce/core.c
+++ b/arch/x86/kernel/cpu/mce/core.c
@@ -687,7 +687,10 @@ static noinstr void mce_read_aux(struct mce_hw_err *err, int i)
m->misc = mce_rdmsrq(mca_msr_reg(i, MCA_MISC));
if (m->status & MCI_STATUS_ADDRV) {
- m->addr = mce_rdmsrq(mca_msr_reg(i, MCA_ADDR));
+ if (m->kflags & MCE_CHECK_DFR_REGS)
+ m->addr = mce_rdmsrq(MSR_AMD64_SMCA_MCx_DEADDR(i));
+ else
+ m->addr = mce_rdmsrq(mca_msr_reg(i, MCA_ADDR));
/*
* Mask the reported address by the reported granularity.
@@ -715,6 +718,29 @@ static noinstr void mce_read_aux(struct mce_hw_err *err, int i)
DEFINE_PER_CPU(unsigned, mce_poll_count);
/*
+ * We have three scenarios for checking for Deferred errors:
+ *
+ * 1) Non-SMCA systems check MCA_STATUS and log error if found.
+ * 2) SMCA systems check MCA_STATUS. If error is found then log it and also
+ * clear MCA_DESTAT.
+ * 3) SMCA systems check MCA_DESTAT, if error was not found in MCA_STATUS, and
+ * log it.
+ */
+static bool smca_should_log_poll_error(struct mce *m)
+{
+ if (m->status & MCI_STATUS_VAL)
+ return true;
+
+ m->status = mce_rdmsrq(MSR_AMD64_SMCA_MCx_DESTAT(m->bank));
+ if ((m->status & MCI_STATUS_VAL) && (m->status & MCI_STATUS_DEFERRED)) {
+ m->kflags |= MCE_CHECK_DFR_REGS;
+ return true;
+ }
+
+ return false;
+}
+
+/*
* Newer Intel systems that support software error
* recovery need to make additional checks. Other
* CPUs should skip over uncorrected errors, but log
@@ -740,6 +766,9 @@ static bool should_log_poll_error(enum mcp_flags flags, struct mce_hw_err *err)
{
struct mce *m = &err->m;
+ if (mce_flags.smca)
+ return smca_should_log_poll_error(m);
+
/* If this entry is not valid, ignore it. */
if (!(m->status & MCI_STATUS_VAL))
return false;
diff --git a/arch/x86/kernel/cpu/mce/internal.h b/arch/x86/kernel/cpu/mce/internal.h
index b0e00ec5cc8c..a31cf984619c 100644
--- a/arch/x86/kernel/cpu/mce/internal.h
+++ b/arch/x86/kernel/cpu/mce/internal.h
@@ -67,6 +67,7 @@ void mce_track_storm(struct mce *mce);
void mce_inherit_storm(unsigned int bank);
bool mce_get_storm_mode(void);
void mce_set_storm_mode(bool storm);
+u32 mce_get_apei_thr_limit(void);
#else
static inline void cmci_storm_begin(unsigned int bank) {}
static inline void cmci_storm_end(unsigned int bank) {}
@@ -74,6 +75,7 @@ static inline void mce_track_storm(struct mce *mce) {}
static inline void mce_inherit_storm(unsigned int bank) {}
static inline bool mce_get_storm_mode(void) { return false; }
static inline void mce_set_storm_mode(bool storm) {}
+static inline u32 mce_get_apei_thr_limit(void) { return 0; }
#endif
/*
@@ -267,6 +269,7 @@ void mce_prep_record_per_cpu(unsigned int cpu, struct mce *m);
#ifdef CONFIG_X86_MCE_AMD
void mce_threshold_create_device(unsigned int cpu);
void mce_threshold_remove_device(unsigned int cpu);
+void mce_amd_handle_storm(unsigned int bank, bool on);
extern bool amd_filter_mce(struct mce *m);
bool amd_mce_usable_address(struct mce *m);
void amd_clear_bank(struct mce *m);
@@ -299,6 +302,7 @@ void smca_bsp_init(void);
#else
static inline void mce_threshold_create_device(unsigned int cpu) { }
static inline void mce_threshold_remove_device(unsigned int cpu) { }
+static inline void mce_amd_handle_storm(unsigned int bank, bool on) { }
static inline bool amd_filter_mce(struct mce *m) { return false; }
static inline bool amd_mce_usable_address(struct mce *m) { return false; }
static inline void amd_clear_bank(struct mce *m) { }
diff --git a/arch/x86/kernel/cpu/mce/threshold.c b/arch/x86/kernel/cpu/mce/threshold.c
index f4a007616468..0d13c9ffcba0 100644
--- a/arch/x86/kernel/cpu/mce/threshold.c
+++ b/arch/x86/kernel/cpu/mce/threshold.c
@@ -13,6 +13,19 @@
#include "internal.h"
+static u32 mce_apei_thr_limit;
+
+void mce_save_apei_thr_limit(u32 thr_limit)
+{
+ mce_apei_thr_limit = thr_limit;
+ pr_info("HEST corrected error threshold limit: %u\n", thr_limit);
+}
+
+u32 mce_get_apei_thr_limit(void)
+{
+ return mce_apei_thr_limit;
+}
+
static void default_threshold_interrupt(void)
{
pr_err("Unexpected threshold interrupt at vector %x\n",
@@ -63,6 +76,9 @@ static void mce_handle_storm(unsigned int bank, bool on)
case X86_VENDOR_INTEL:
mce_intel_handle_storm(bank, on);
break;
+ case X86_VENDOR_AMD:
+ mce_amd_handle_storm(bank, on);
+ break;
}
}
@@ -85,7 +101,8 @@ void cmci_storm_end(unsigned int bank)
{
struct mca_storm_desc *storm = this_cpu_ptr(&storm_desc);
- __clear_bit(bank, this_cpu_ptr(mce_poll_banks));
+ if (!mce_flags.amd_threshold)
+ __clear_bit(bank, this_cpu_ptr(mce_poll_banks));
storm->banks[bank].history = 0;
storm->banks[bank].in_storm_mode = false;
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c
index cdce885e2fd5..3821a985f4ff 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -186,60 +186,92 @@ static u32 cpuid_to_ucode_rev(unsigned int val)
return p.ucode_rev;
}
+static u32 get_cutoff_revision(u32 rev)
+{
+ switch (rev >> 8) {
+ case 0x80012: return 0x8001277; break;
+ case 0x80082: return 0x800820f; break;
+ case 0x83010: return 0x830107c; break;
+ case 0x86001: return 0x860010e; break;
+ case 0x86081: return 0x8608108; break;
+ case 0x87010: return 0x8701034; break;
+ case 0x8a000: return 0x8a0000a; break;
+ case 0xa0010: return 0xa00107a; break;
+ case 0xa0011: return 0xa0011da; break;
+ case 0xa0012: return 0xa001243; break;
+ case 0xa0082: return 0xa00820e; break;
+ case 0xa1011: return 0xa101153; break;
+ case 0xa1012: return 0xa10124e; break;
+ case 0xa1081: return 0xa108109; break;
+ case 0xa2010: return 0xa20102f; break;
+ case 0xa2012: return 0xa201212; break;
+ case 0xa4041: return 0xa404109; break;
+ case 0xa5000: return 0xa500013; break;
+ case 0xa6012: return 0xa60120a; break;
+ case 0xa7041: return 0xa704109; break;
+ case 0xa7052: return 0xa705208; break;
+ case 0xa7080: return 0xa708009; break;
+ case 0xa70c0: return 0xa70C009; break;
+ case 0xaa001: return 0xaa00116; break;
+ case 0xaa002: return 0xaa00218; break;
+ case 0xb0021: return 0xb002146; break;
+ case 0xb0081: return 0xb008111; break;
+ case 0xb1010: return 0xb101046; break;
+ case 0xb2040: return 0xb204031; break;
+ case 0xb4040: return 0xb404031; break;
+ case 0xb4041: return 0xb404101; break;
+ case 0xb6000: return 0xb600031; break;
+ case 0xb6080: return 0xb608031; break;
+ case 0xb7000: return 0xb700031; break;
+ default: break;
+
+ }
+ return 0;
+}
+
static bool need_sha_check(u32 cur_rev)
{
+ u32 cutoff;
+
if (!cur_rev) {
cur_rev = cpuid_to_ucode_rev(bsp_cpuid_1_eax);
pr_info_once("No current revision, generating the lowest one: 0x%x\n", cur_rev);
}
- switch (cur_rev >> 8) {
- case 0x80012: return cur_rev <= 0x800126f; break;
- case 0x80082: return cur_rev <= 0x800820f; break;
- case 0x83010: return cur_rev <= 0x830107c; break;
- case 0x86001: return cur_rev <= 0x860010e; break;
- case 0x86081: return cur_rev <= 0x8608108; break;
- case 0x87010: return cur_rev <= 0x8701034; break;
- case 0x8a000: return cur_rev <= 0x8a0000a; break;
- case 0xa0010: return cur_rev <= 0xa00107a; break;
- case 0xa0011: return cur_rev <= 0xa0011da; break;
- case 0xa0012: return cur_rev <= 0xa001243; break;
- case 0xa0082: return cur_rev <= 0xa00820e; break;
- case 0xa1011: return cur_rev <= 0xa101153; break;
- case 0xa1012: return cur_rev <= 0xa10124e; break;
- case 0xa1081: return cur_rev <= 0xa108109; break;
- case 0xa2010: return cur_rev <= 0xa20102f; break;
- case 0xa2012: return cur_rev <= 0xa201212; break;
- case 0xa4041: return cur_rev <= 0xa404109; break;
- case 0xa5000: return cur_rev <= 0xa500013; break;
- case 0xa6012: return cur_rev <= 0xa60120a; break;
- case 0xa7041: return cur_rev <= 0xa704109; break;
- case 0xa7052: return cur_rev <= 0xa705208; break;
- case 0xa7080: return cur_rev <= 0xa708009; break;
- case 0xa70c0: return cur_rev <= 0xa70C009; break;
- case 0xaa001: return cur_rev <= 0xaa00116; break;
- case 0xaa002: return cur_rev <= 0xaa00218; break;
- case 0xb0021: return cur_rev <= 0xb002146; break;
- case 0xb1010: return cur_rev <= 0xb101046; break;
- case 0xb2040: return cur_rev <= 0xb204031; break;
- case 0xb4040: return cur_rev <= 0xb404031; break;
- case 0xb6000: return cur_rev <= 0xb600031; break;
- case 0xb7000: return cur_rev <= 0xb700031; break;
- default: break;
- }
+ cutoff = get_cutoff_revision(cur_rev);
+ if (cutoff)
+ return cur_rev <= cutoff;
pr_info("You should not be seeing this. Please send the following couple of lines to x86-<at>-kernel.org\n");
pr_info("CPUID(1).EAX: 0x%x, current revision: 0x%x\n", bsp_cpuid_1_eax, cur_rev);
return true;
}
+static bool cpu_has_entrysign(void)
+{
+ unsigned int fam = x86_family(bsp_cpuid_1_eax);
+ unsigned int model = x86_model(bsp_cpuid_1_eax);
+
+ if (fam == 0x17 || fam == 0x19)
+ return true;
+
+ if (fam == 0x1a) {
+ if (model <= 0x2f ||
+ (0x40 <= model && model <= 0x4f) ||
+ (0x60 <= model && model <= 0x6f))
+ return true;
+ }
+
+ return false;
+}
+
static bool verify_sha256_digest(u32 patch_id, u32 cur_rev, const u8 *data, unsigned int len)
{
struct patch_digest *pd = NULL;
u8 digest[SHA256_DIGEST_SIZE];
int i;
- if (x86_family(bsp_cpuid_1_eax) < 0x17)
+ if (!cpu_has_entrysign())
return true;
if (!need_sha_check(cur_rev))
@@ -473,6 +505,7 @@ static int verify_patch(const u8 *buf, size_t buf_size, u32 *patch_size)
{
u8 family = x86_family(bsp_cpuid_1_eax);
struct microcode_header_amd *mc_hdr;
+ u32 cur_rev, cutoff, patch_rev;
u32 sh_psize;
u16 proc_id;
u8 patch_fam;
@@ -512,11 +545,32 @@ static int verify_patch(const u8 *buf, size_t buf_size, u32 *patch_size)
proc_id = mc_hdr->processor_rev_id;
patch_fam = 0xf + (proc_id >> 12);
- ucode_dbg("Patch-ID 0x%08x: family: 0x%x\n", mc_hdr->patch_id, patch_fam);
-
if (patch_fam != family)
return 1;
+ cur_rev = get_patch_level();
+
+ /* No cutoff revision means old/unaffected by signing algorithm weakness => matches */
+ cutoff = get_cutoff_revision(cur_rev);
+ if (!cutoff)
+ goto ok;
+
+ patch_rev = mc_hdr->patch_id;
+
+ ucode_dbg("cur_rev: 0x%x, cutoff: 0x%x, patch_rev: 0x%x\n",
+ cur_rev, cutoff, patch_rev);
+
+ if (cur_rev <= cutoff && patch_rev <= cutoff)
+ goto ok;
+
+ if (cur_rev > cutoff && patch_rev > cutoff)
+ goto ok;
+
+ return 1;
+
+ok:
+ ucode_dbg("Patch-ID 0x%08x: family: 0x%x\n", mc_hdr->patch_id, patch_fam);
+
return 0;
}
@@ -585,8 +639,6 @@ static size_t parse_container(u8 *ucode, size_t size, struct cont_desc *desc)
mc = (struct microcode_amd *)(buf + SECTION_HDR_SIZE);
- ucode_dbg("patch_id: 0x%x\n", mc->hdr.patch_id);
-
if (mc_patch_matches(mc, eq_id)) {
desc->psize = patch_size;
desc->mc = mc;
diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c
index f75c140906d0..ccc83b0bf63c 100644
--- a/arch/x86/kernel/cpu/microcode/core.c
+++ b/arch/x86/kernel/cpu/microcode/core.c
@@ -136,7 +136,7 @@ bool __init microcode_loader_disabled(void)
return dis_ucode_ldr;
}
-static void early_parse_cmdline(void)
+static void __init early_parse_cmdline(void)
{
char cmd_buf[64] = {};
char *s, *p = cmd_buf;
@@ -589,6 +589,17 @@ static int load_late_stop_cpus(bool is_safe)
pr_err("You should switch to early loading, if possible.\n");
}
+ /*
+ * Pre-load the microcode image into a staging device. This
+ * process is preemptible and does not require stopping CPUs.
+ * Successful staging simplifies the subsequent late-loading
+ * process, reducing rendezvous time.
+ *
+ * Even if the transfer fails, the update will proceed as usual.
+ */
+ if (microcode_ops->use_staging)
+ microcode_ops->stage_microcode();
+
atomic_set(&late_cpus_in, num_online_cpus());
atomic_set(&offline_in_nmi, 0);
loops_per_usec = loops_per_jiffy / (TICK_NSEC / 1000);
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 371ca6eac00e..8744f3adc2a0 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -13,12 +13,15 @@
#define pr_fmt(fmt) "microcode: " fmt
#include <linux/earlycpio.h>
#include <linux/firmware.h>
+#include <linux/pci_ids.h>
#include <linux/uaccess.h>
#include <linux/initrd.h>
#include <linux/kernel.h>
+#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/uio.h>
+#include <linux/io.h>
#include <linux/mm.h>
#include <asm/cpu_device_id.h>
@@ -33,6 +36,38 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";
#define UCODE_BSP_LOADED ((struct microcode_intel *)0x1UL)
+/* Defines for the microcode staging mailbox interface */
+#define MBOX_REG_NUM 4
+#define MBOX_REG_SIZE sizeof(u32)
+
+#define MBOX_CONTROL_OFFSET 0x0
+#define MBOX_STATUS_OFFSET 0x4
+#define MBOX_WRDATA_OFFSET 0x8
+#define MBOX_RDDATA_OFFSET 0xc
+
+#define MASK_MBOX_CTRL_ABORT BIT(0)
+#define MASK_MBOX_CTRL_GO BIT(31)
+
+#define MASK_MBOX_STATUS_ERROR BIT(2)
+#define MASK_MBOX_STATUS_READY BIT(31)
+
+#define MASK_MBOX_RESP_SUCCESS BIT(0)
+#define MASK_MBOX_RESP_PROGRESS BIT(1)
+#define MASK_MBOX_RESP_ERROR BIT(2)
+
+#define MBOX_CMD_LOAD 0x3
+#define MBOX_OBJ_STAGING 0xb
+#define MBOX_HEADER(size) ((PCI_VENDOR_ID_INTEL) | \
+ (MBOX_OBJ_STAGING << 16) | \
+ ((u64)((size) / sizeof(u32)) << 32))
+
+/* The size of each mailbox header */
+#define MBOX_HEADER_SIZE sizeof(u64)
+/* The size of staging hardware response */
+#define MBOX_RESPONSE_SIZE sizeof(u64)
+
+#define MBOX_XACTION_TIMEOUT_MS (10 * MSEC_PER_SEC)
+
/* Current microcode patch used in early patching on the APs. */
static struct microcode_intel *ucode_patch_va __read_mostly;
static struct microcode_intel *ucode_patch_late __read_mostly;
@@ -54,6 +89,23 @@ struct extended_sigtable {
struct extended_signature sigs[];
};
+/**
+ * struct staging_state - Track the current staging process state
+ *
+ * @mmio_base: MMIO base address for staging
+ * @ucode_len: Total size of the microcode image
+ * @chunk_size: Size of each data piece
+ * @bytes_sent: Total bytes transmitted so far
+ * @offset: Current offset in the microcode image
+ */
+struct staging_state {
+ void __iomem *mmio_base;
+ unsigned int ucode_len;
+ unsigned int chunk_size;
+ unsigned int bytes_sent;
+ unsigned int offset;
+};
+
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE)
#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable))
#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature))
@@ -299,6 +351,298 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,
return size ? NULL : patch;
}
+static inline u32 read_mbox_dword(void __iomem *mmio_base)
+{
+ u32 dword = readl(mmio_base + MBOX_RDDATA_OFFSET);
+
+ /* Acknowledge read completion to the staging hardware */
+ writel(0, mmio_base + MBOX_RDDATA_OFFSET);
+ return dword;
+}
+
+static inline void write_mbox_dword(void __iomem *mmio_base, u32 dword)
+{
+ writel(dword, mmio_base + MBOX_WRDATA_OFFSET);
+}
+
+static inline u64 read_mbox_header(void __iomem *mmio_base)
+{
+ u32 high, low;
+
+ low = read_mbox_dword(mmio_base);
+ high = read_mbox_dword(mmio_base);
+
+ return ((u64)high << 32) | low;
+}
+
+static inline void write_mbox_header(void __iomem *mmio_base, u64 value)
+{
+ write_mbox_dword(mmio_base, value);
+ write_mbox_dword(mmio_base, value >> 32);
+}
+
+static void write_mbox_data(void __iomem *mmio_base, u32 *chunk, unsigned int chunk_bytes)
+{
+ int i;
+
+ /*
+ * The MMIO space is mapped as Uncached (UC). Each write arrives
+ * at the device as an individual transaction in program order.
+ * The device can then reassemble the sequence accordingly.
+ */
+ for (i = 0; i < chunk_bytes / sizeof(u32); i++)
+ write_mbox_dword(mmio_base, chunk[i]);
+}
+
+/*
+ * Prepare for a new microcode transfer: reset hardware and record the
+ * image size.
+ */
+static void init_stage(struct staging_state *ss)
+{
+ ss->ucode_len = get_totalsize(&ucode_patch_late->hdr);
+
+ /*
+ * Abort any ongoing process, effectively resetting the device.
+ * Unlike regular mailbox data processing requests, this
+ * operation does not require a status check.
+ */
+ writel(MASK_MBOX_CTRL_ABORT, ss->mmio_base + MBOX_CONTROL_OFFSET);
+}
+
+/*
+ * Update the chunk size and decide whether another chunk can be sent.
+ * This accounts for remaining data and retry limits.
+ */
+static bool can_send_next_chunk(struct staging_state *ss, int *err)
+{
+ /* A page size or remaining bytes if this is the final chunk */
+ ss->chunk_size = min(PAGE_SIZE, ss->ucode_len - ss->offset);
+
+ /*
+ * Each microcode image is divided into chunks, each at most
+ * one page size. A 10-chunk image would typically require 10
+ * transactions.
+ *
+ * However, the hardware managing the mailbox has limited
+ * resources and may not cache the entire image, potentially
+ * requesting the same chunk multiple times.
+ *
+ * To tolerate this behavior, allow up to twice the expected
+ * number of transactions (i.e., a 10-chunk image can take up to
+ * 20 attempts).
+ *
+ * If the number of attempts exceeds this limit, treat it as
+ * exceeding the maximum allowed transfer size.
+ */
+ if (ss->bytes_sent + ss->chunk_size > ss->ucode_len * 2) {
+ *err = -EMSGSIZE;
+ return false;
+ }
+
+ *err = 0;
+ return true;
+}
+
+/*
+ * The hardware indicates completion by returning a sentinel end offset.
+ */
+static inline bool is_end_offset(u32 offset)
+{
+ return offset == UINT_MAX;
+}
+
+/*
+ * Determine whether staging is complete: either the hardware signaled
+ * the end offset, or no more transactions are permitted (retry limit
+ * reached).
+ */
+static inline bool staging_is_complete(struct staging_state *ss, int *err)
+{
+ return is_end_offset(ss->offset) || !can_send_next_chunk(ss, err);
+}
+
+/*
+ * Wait for the hardware to complete a transaction.
+ * Return 0 on success, or an error code on failure.
+ */
+static int wait_for_transaction(struct staging_state *ss)
+{
+ u32 timeout, status;
+
+ /* Allow time for hardware to complete the operation: */
+ for (timeout = 0; timeout < MBOX_XACTION_TIMEOUT_MS; timeout++) {
+ msleep(1);
+
+ status = readl(ss->mmio_base + MBOX_STATUS_OFFSET);
+ /* Break out early if the hardware is ready: */
+ if (status & MASK_MBOX_STATUS_READY)
+ break;
+ }
+
+ /* Check for explicit error response */
+ if (status & MASK_MBOX_STATUS_ERROR)
+ return -EIO;
+
+ /*
+ * Hardware has neither responded to the action nor signaled any
+ * error. Treat this as a timeout.
+ */
+ if (!(status & MASK_MBOX_STATUS_READY))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/*
+ * Transmit a chunk of the microcode image to the hardware.
+ * Return 0 on success, or an error code on failure.
+ */
+static int send_data_chunk(struct staging_state *ss, void *ucode_ptr)
+{
+ u32 *src_chunk = ucode_ptr + ss->offset;
+ u16 mbox_size;
+
+ /*
+ * Write a 'request' mailbox object in this order:
+ * 1. Mailbox header includes total size
+ * 2. Command header specifies the load operation
+ * 3. Data section contains a microcode chunk
+ *
+ * Thus, the mailbox size is two headers plus the chunk size.
+ */
+ mbox_size = MBOX_HEADER_SIZE * 2 + ss->chunk_size;
+ write_mbox_header(ss->mmio_base, MBOX_HEADER(mbox_size));
+ write_mbox_header(ss->mmio_base, MBOX_CMD_LOAD);
+ write_mbox_data(ss->mmio_base, src_chunk, ss->chunk_size);
+ ss->bytes_sent += ss->chunk_size;
+
+ /* Notify the hardware that the mailbox is ready for processing. */
+ writel(MASK_MBOX_CTRL_GO, ss->mmio_base + MBOX_CONTROL_OFFSET);
+
+ return wait_for_transaction(ss);
+}
+
+/*
+ * Retrieve the next offset from the hardware response.
+ * Return 0 on success, or an error code on failure.
+ */
+static int fetch_next_offset(struct staging_state *ss)
+{
+ const u64 expected_header = MBOX_HEADER(MBOX_HEADER_SIZE + MBOX_RESPONSE_SIZE);
+ u32 offset, status;
+ u64 header;
+
+ /*
+ * The 'response' mailbox returns three fields, in order:
+ * 1. Header
+ * 2. Next offset in the microcode image
+ * 3. Status flags
+ */
+ header = read_mbox_header(ss->mmio_base);
+ offset = read_mbox_dword(ss->mmio_base);
+ status = read_mbox_dword(ss->mmio_base);
+
+ /* All valid responses must start with the expected header. */
+ if (header != expected_header) {
+ pr_err_once("staging: invalid response header (0x%llx)\n", header);
+ return -EBADR;
+ }
+
+ /*
+ * Verify the offset: If not at the end marker, it must not
+ * exceed the microcode image length.
+ */
+ if (!is_end_offset(offset) && offset > ss->ucode_len) {
+ pr_err_once("staging: invalid offset (%u) past the image end (%u)\n",
+ offset, ss->ucode_len);
+ return -EINVAL;
+ }
+
+ /* Hardware may report errors explicitly in the status field */
+ if (status & MASK_MBOX_RESP_ERROR)
+ return -EPROTO;
+
+ ss->offset = offset;
+ return 0;
+}
+
+/*
+ * Handle the staging process using the mailbox MMIO interface. The
+ * microcode image is transferred in chunks until completion.
+ * Return 0 on success or an error code on failure.
+ */
+static int do_stage(u64 mmio_pa)
+{
+ struct staging_state ss = {};
+ int err;
+
+ ss.mmio_base = ioremap(mmio_pa, MBOX_REG_NUM * MBOX_REG_SIZE);
+ if (WARN_ON_ONCE(!ss.mmio_base))
+ return -EADDRNOTAVAIL;
+
+ init_stage(&ss);
+
+ /* Perform the staging process while within the retry limit */
+ while (!staging_is_complete(&ss, &err)) {
+ /* Send a chunk of microcode each time: */
+ err = send_data_chunk(&ss, ucode_patch_late);
+ if (err)
+ break;
+ /*
+ * Then, ask the hardware which piece of the image it
+ * needs next. The same piece may be sent more than once.
+ */
+ err = fetch_next_offset(&ss);
+ if (err)
+ break;
+ }
+
+ iounmap(ss.mmio_base);
+
+ return err;
+}
+
+static void stage_microcode(void)
+{
+ unsigned int pkg_id = UINT_MAX;
+ int cpu, err;
+ u64 mmio_pa;
+
+ if (!IS_ALIGNED(get_totalsize(&ucode_patch_late->hdr), sizeof(u32))) {
+ pr_err("Microcode image 32-bit misaligned (0x%x), staging failed.\n",
+ get_totalsize(&ucode_patch_late->hdr));
+ return;
+ }
+
+ lockdep_assert_cpus_held();
+
+ /*
+ * The MMIO address is unique per package, and all the SMT
+ * primary threads are online here. Find each MMIO space by
+ * their package IDs to avoid duplicate staging.
+ */
+ for_each_cpu(cpu, cpu_primary_thread_mask) {
+ if (topology_logical_package_id(cpu) == pkg_id)
+ continue;
+
+ pkg_id = topology_logical_package_id(cpu);
+
+ err = rdmsrq_on_cpu(cpu, MSR_IA32_MCU_STAGING_MBOX_ADDR, &mmio_pa);
+ if (WARN_ON_ONCE(err))
+ return;
+
+ err = do_stage(mmio_pa);
+ if (err) {
+ pr_err("Error: staging failed (%d) for CPU%d at package %u.\n",
+ err, cpu, pkg_id);
+ return;
+ }
+ }
+
+ pr_info("Staging of patch revision 0x%x succeeded.\n", ucode_patch_late->hdr.rev);
+}
+
static enum ucode_state __apply_microcode(struct ucode_cpu_info *uci,
struct microcode_intel *mc,
u32 *cur_rev)
@@ -627,6 +971,7 @@ static struct microcode_ops microcode_intel_ops = {
.collect_cpu_info = collect_cpu_info,
.apply_microcode = apply_microcode_late,
.finalize_late_load = finalize_late_load,
+ .stage_microcode = stage_microcode,
.use_nmi = IS_ENABLED(CONFIG_X86_64),
};
@@ -638,6 +983,18 @@ static __init void calc_llc_size_per_core(struct cpuinfo_x86 *c)
llc_size_per_core = (unsigned int)llc_size;
}
+static __init bool staging_available(void)
+{
+ u64 val;
+
+ val = x86_read_arch_cap_msr();
+ if (!(val & ARCH_CAP_MCU_ENUM))
+ return false;
+
+ rdmsrq(MSR_IA32_MCU_ENUMERATION, val);
+ return !!(val & MCU_STAGING);
+}
+
struct microcode_ops * __init init_intel_microcode(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
@@ -648,6 +1005,11 @@ struct microcode_ops * __init init_intel_microcode(void)
return NULL;
}
+ if (staging_available()) {
+ microcode_intel_ops.use_staging = true;
+ pr_info("Enabled staging feature.\n");
+ }
+
calc_llc_size_per_core(c);
return &microcode_intel_ops;
diff --git a/arch/x86/kernel/cpu/microcode/internal.h b/arch/x86/kernel/cpu/microcode/internal.h
index ae8dbc2b908d..a10b547eda1e 100644
--- a/arch/x86/kernel/cpu/microcode/internal.h
+++ b/arch/x86/kernel/cpu/microcode/internal.h
@@ -31,10 +31,12 @@ struct microcode_ops {
* See also the "Synchronization" section in microcode_core.c.
*/
enum ucode_state (*apply_microcode)(int cpu);
+ void (*stage_microcode)(void);
int (*collect_cpu_info)(int cpu, struct cpu_signature *csig);
void (*finalize_late_load)(int result);
unsigned int nmi_safe : 1,
- use_nmi : 1;
+ use_nmi : 1,
+ use_staging : 1;
};
struct early_load_data {
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index 8c18327eb10b..0863733858dc 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -89,7 +89,6 @@ static int mtrr_state_set;
u64 mtrr_tom2;
struct mtrr_state_type mtrr_state;
-EXPORT_SYMBOL_GPL(mtrr_state);
/* Reserved bits in the high portion of the MTRRphysBaseN MSR. */
u32 phys_hi_rsvd;
diff --git a/arch/x86/kernel/cpu/mtrr/mtrr.h b/arch/x86/kernel/cpu/mtrr/mtrr.h
index 5655f253d929..2de3bd2f95d1 100644
--- a/arch/x86/kernel/cpu/mtrr/mtrr.h
+++ b/arch/x86/kernel/cpu/mtrr/mtrr.h
@@ -46,10 +46,6 @@ struct set_mtrr_context {
u32 ccr3;
};
-void set_mtrr_done(struct set_mtrr_context *ctxt);
-void set_mtrr_cache_disable(struct set_mtrr_context *ctxt);
-void set_mtrr_prepare_save(struct set_mtrr_context *ctxt);
-
void fill_mtrr_var_range(unsigned int index,
u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi);
bool get_mtrr_state(void);
diff --git a/arch/x86/kernel/cpu/resctrl/core.c b/arch/x86/kernel/cpu/resctrl/core.c
index 06ca5a30140c..3792ab4819dc 100644
--- a/arch/x86/kernel/cpu/resctrl/core.c
+++ b/arch/x86/kernel/cpu/resctrl/core.c
@@ -274,6 +274,11 @@ static void rdt_get_cdp_config(int level)
rdt_resources_all[level].r_resctrl.cdp_capable = true;
}
+static void rdt_set_io_alloc_capable(struct rdt_resource *r)
+{
+ r->cache.io_alloc_capable = true;
+}
+
static void rdt_get_cdp_l3_config(void)
{
rdt_get_cdp_config(RDT_RESOURCE_L3);
@@ -719,6 +724,7 @@ enum {
RDT_FLAG_SMBA,
RDT_FLAG_BMEC,
RDT_FLAG_ABMC,
+ RDT_FLAG_SDCIAE,
};
#define RDT_OPT(idx, n, f) \
@@ -745,6 +751,7 @@ static struct rdt_options rdt_options[] __ro_after_init = {
RDT_OPT(RDT_FLAG_SMBA, "smba", X86_FEATURE_SMBA),
RDT_OPT(RDT_FLAG_BMEC, "bmec", X86_FEATURE_BMEC),
RDT_OPT(RDT_FLAG_ABMC, "abmc", X86_FEATURE_ABMC),
+ RDT_OPT(RDT_FLAG_SDCIAE, "sdciae", X86_FEATURE_SDCIAE),
};
#define NUM_RDT_OPTIONS ARRAY_SIZE(rdt_options)
@@ -853,6 +860,8 @@ static __init bool get_rdt_alloc_resources(void)
rdt_get_cache_alloc_cfg(1, r);
if (rdt_cpu_has(X86_FEATURE_CDP_L3))
rdt_get_cdp_l3_config();
+ if (rdt_cpu_has(X86_FEATURE_SDCIAE))
+ rdt_set_io_alloc_capable(r);
ret = true;
}
if (rdt_cpu_has(X86_FEATURE_CAT_L2)) {
diff --git a/arch/x86/kernel/cpu/resctrl/ctrlmondata.c b/arch/x86/kernel/cpu/resctrl/ctrlmondata.c
index 1189c0df4ad7..b20e705606b8 100644
--- a/arch/x86/kernel/cpu/resctrl/ctrlmondata.c
+++ b/arch/x86/kernel/cpu/resctrl/ctrlmondata.c
@@ -91,3 +91,43 @@ u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain *d,
return hw_dom->ctrl_val[idx];
}
+
+bool resctrl_arch_get_io_alloc_enabled(struct rdt_resource *r)
+{
+ return resctrl_to_arch_res(r)->sdciae_enabled;
+}
+
+static void resctrl_sdciae_set_one_amd(void *arg)
+{
+ bool *enable = arg;
+
+ if (*enable)
+ msr_set_bit(MSR_IA32_L3_QOS_EXT_CFG, SDCIAE_ENABLE_BIT);
+ else
+ msr_clear_bit(MSR_IA32_L3_QOS_EXT_CFG, SDCIAE_ENABLE_BIT);
+}
+
+static void _resctrl_sdciae_enable(struct rdt_resource *r, bool enable)
+{
+ struct rdt_ctrl_domain *d;
+
+ /* Walking r->ctrl_domains, ensure it can't race with cpuhp */
+ lockdep_assert_cpus_held();
+
+ /* Update MSR_IA32_L3_QOS_EXT_CFG MSR on all the CPUs in all domains */
+ list_for_each_entry(d, &r->ctrl_domains, hdr.list)
+ on_each_cpu_mask(&d->hdr.cpu_mask, resctrl_sdciae_set_one_amd, &enable, 1);
+}
+
+int resctrl_arch_io_alloc_enable(struct rdt_resource *r, bool enable)
+{
+ struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r);
+
+ if (hw_res->r_resctrl.cache.io_alloc_capable &&
+ hw_res->sdciae_enabled != enable) {
+ _resctrl_sdciae_enable(r, enable);
+ hw_res->sdciae_enabled = enable;
+ }
+
+ return 0;
+}
diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h
index 9f4c2f0aaf5c..4a916c84a322 100644
--- a/arch/x86/kernel/cpu/resctrl/internal.h
+++ b/arch/x86/kernel/cpu/resctrl/internal.h
@@ -46,6 +46,9 @@ struct arch_mbm_state {
#define ABMC_EXTENDED_EVT_ID BIT(31)
#define ABMC_EVT_ID BIT(0)
+/* Setting bit 1 in MSR_IA32_L3_QOS_EXT_CFG enables the SDCIAE feature. */
+#define SDCIAE_ENABLE_BIT 1
+
/**
* struct rdt_hw_ctrl_domain - Arch private attributes of a set of CPUs that share
* a resource for a control function
@@ -112,6 +115,7 @@ struct msr_param {
* @mbm_width: Monitor width, to detect and correct for overflow.
* @cdp_enabled: CDP state of this resource
* @mbm_cntr_assign_enabled: ABMC feature is enabled
+ * @sdciae_enabled: SDCIAE feature (backing "io_alloc") is enabled.
*
* Members of this structure are either private to the architecture
* e.g. mbm_width, or accessed via helpers that provide abstraction. e.g.
@@ -126,6 +130,7 @@ struct rdt_hw_resource {
unsigned int mbm_width;
bool cdp_enabled;
bool mbm_cntr_assign_enabled;
+ bool sdciae_enabled;
};
static inline struct rdt_hw_resource *resctrl_to_arch_res(struct rdt_resource *r)
diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c
index 2cd25a0d4637..dffcc8307500 100644
--- a/arch/x86/kernel/cpu/resctrl/monitor.c
+++ b/arch/x86/kernel/cpu/resctrl/monitor.c
@@ -361,6 +361,7 @@ static const struct x86_cpu_id snc_cpu_ids[] __initconst = {
X86_MATCH_VFM(INTEL_EMERALDRAPIDS_X, 0),
X86_MATCH_VFM(INTEL_GRANITERAPIDS_X, 0),
X86_MATCH_VFM(INTEL_ATOM_CRESTMONT_X, 0),
+ X86_MATCH_VFM(INTEL_ATOM_DARKMONT_X, 0),
{}
};
@@ -458,7 +459,16 @@ int __init rdt_get_mon_l3_config(struct rdt_resource *r)
r->mon.mbm_cfg_mask = ecx & MAX_EVT_CONFIG_BITS;
}
- if (rdt_cpu_has(X86_FEATURE_ABMC)) {
+ /*
+ * resctrl assumes a system that supports assignable counters can
+ * switch to "default" mode. Ensure that there is a "default" mode
+ * to switch to. This enforces a dependency between the independent
+ * X86_FEATURE_ABMC and X86_FEATURE_CQM_MBM_TOTAL/X86_FEATURE_CQM_MBM_LOCAL
+ * hardware features.
+ */
+ if (rdt_cpu_has(X86_FEATURE_ABMC) &&
+ (rdt_cpu_has(X86_FEATURE_CQM_MBM_TOTAL) ||
+ rdt_cpu_has(X86_FEATURE_CQM_MBM_LOCAL))) {
r->mon.mbm_cntr_assignable = true;
cpuid_count(0x80000020, 5, &eax, &ebx, &ecx, &edx);
r->mon.num_mbm_cntrs = (ebx & GENMASK(15, 0)) + 1;
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index caa4dc885c21..cde4b6cd3471 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -43,7 +43,11 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_PER_THREAD_MBA, CPUID_ECX, 0, 0x00000010, 3 },
{ X86_FEATURE_SGX1, CPUID_EAX, 0, 0x00000012, 0 },
{ X86_FEATURE_SGX2, CPUID_EAX, 1, 0x00000012, 0 },
+ { X86_FEATURE_SGX_EUPDATESVN, CPUID_EAX, 10, 0x00000012, 0 },
{ X86_FEATURE_SGX_EDECCSSA, CPUID_EAX, 11, 0x00000012, 0 },
+ { X86_FEATURE_OVERFLOW_RECOV, CPUID_EBX, 0, 0x80000007, 0 },
+ { X86_FEATURE_SUCCOR, CPUID_EBX, 1, 0x80000007, 0 },
+ { X86_FEATURE_SMCA, CPUID_EBX, 3, 0x80000007, 0 },
{ X86_FEATURE_HW_PSTATE, CPUID_EDX, 7, 0x80000007, 0 },
{ X86_FEATURE_CPB, CPUID_EDX, 9, 0x80000007, 0 },
{ X86_FEATURE_PROC_FEEDBACK, CPUID_EDX, 11, 0x80000007, 0 },
@@ -53,6 +57,7 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_SMBA, CPUID_EBX, 2, 0x80000020, 0 },
{ X86_FEATURE_BMEC, CPUID_EBX, 3, 0x80000020, 0 },
{ X86_FEATURE_ABMC, CPUID_EBX, 5, 0x80000020, 0 },
+ { X86_FEATURE_SDCIAE, CPUID_EBX, 6, 0x80000020, 0 },
{ X86_FEATURE_TSA_SQ_NO, CPUID_ECX, 1, 0x80000021, 0 },
{ X86_FEATURE_TSA_L1_NO, CPUID_ECX, 2, 0x80000021, 0 },
{ X86_FEATURE_AMD_WORKLOAD_CLASS, CPUID_EAX, 22, 0x80000021, 0 },
diff --git a/arch/x86/kernel/cpu/sgx/driver.c b/arch/x86/kernel/cpu/sgx/driver.c
index 7f8d1e11dbee..79d6020dfe9c 100644
--- a/arch/x86/kernel/cpu/sgx/driver.c
+++ b/arch/x86/kernel/cpu/sgx/driver.c
@@ -14,7 +14,7 @@ u64 sgx_attributes_reserved_mask;
u64 sgx_xfrm_reserved_mask = ~0x3;
u32 sgx_misc_reserved_mask;
-static int sgx_open(struct inode *inode, struct file *file)
+static int __sgx_open(struct inode *inode, struct file *file)
{
struct sgx_encl *encl;
int ret;
@@ -41,6 +41,23 @@ static int sgx_open(struct inode *inode, struct file *file)
return 0;
}
+static int sgx_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ ret = sgx_inc_usage_count();
+ if (ret)
+ return ret;
+
+ ret = __sgx_open(inode, file);
+ if (ret) {
+ sgx_dec_usage_count();
+ return ret;
+ }
+
+ return 0;
+}
+
static int sgx_release(struct inode *inode, struct file *file)
{
struct sgx_encl *encl = file->private_data;
diff --git a/arch/x86/kernel/cpu/sgx/encl.c b/arch/x86/kernel/cpu/sgx/encl.c
index 308dbbae6c6e..cf149b9f4916 100644
--- a/arch/x86/kernel/cpu/sgx/encl.c
+++ b/arch/x86/kernel/cpu/sgx/encl.c
@@ -765,6 +765,7 @@ void sgx_encl_release(struct kref *ref)
WARN_ON_ONCE(encl->secs.epc_page);
kfree(encl);
+ sgx_dec_usage_count();
}
/*
diff --git a/arch/x86/kernel/cpu/sgx/encls.h b/arch/x86/kernel/cpu/sgx/encls.h
index 42a088a337c5..74be751199a4 100644
--- a/arch/x86/kernel/cpu/sgx/encls.h
+++ b/arch/x86/kernel/cpu/sgx/encls.h
@@ -233,4 +233,9 @@ static inline int __eaug(struct sgx_pageinfo *pginfo, void *addr)
return __encls_2(EAUG, pginfo, addr);
}
+/* Attempt to update CPUSVN at runtime. */
+static inline int __eupdatesvn(void)
+{
+ return __encls_ret_1(EUPDATESVN, "");
+}
#endif /* _X86_ENCLS_H */
diff --git a/arch/x86/kernel/cpu/sgx/main.c b/arch/x86/kernel/cpu/sgx/main.c
index 2de01b379aa3..dc73194416ac 100644
--- a/arch/x86/kernel/cpu/sgx/main.c
+++ b/arch/x86/kernel/cpu/sgx/main.c
@@ -5,6 +5,7 @@
#include <linux/freezer.h>
#include <linux/highmem.h>
#include <linux/kthread.h>
+#include <linux/kvm_types.h>
#include <linux/miscdevice.h>
#include <linux/node.h>
#include <linux/pagemap.h>
@@ -16,6 +17,7 @@
#include <linux/vmalloc.h>
#include <asm/msr.h>
#include <asm/sgx.h>
+#include <asm/archrandom.h>
#include "driver.h"
#include "encl.h"
#include "encls.h"
@@ -915,7 +917,107 @@ int sgx_set_attribute(unsigned long *allowed_attributes,
*allowed_attributes |= SGX_ATTR_PROVISIONKEY;
return 0;
}
-EXPORT_SYMBOL_GPL(sgx_set_attribute);
+EXPORT_SYMBOL_FOR_KVM(sgx_set_attribute);
+
+/* Counter to count the active SGX users */
+static int sgx_usage_count;
+
+/**
+ * sgx_update_svn() - Attempt to call ENCLS[EUPDATESVN].
+ *
+ * This instruction attempts to update CPUSVN to the
+ * currently loaded microcode update SVN and generate new
+ * cryptographic assets.
+ *
+ * Return:
+ * * %0: - Success or not supported
+ * * %-EAGAIN: - Can be safely retried, failure is due to lack of
+ * * entropy in RNG
+ * * %-EIO: - Unexpected error, retries are not advisable
+ */
+static int sgx_update_svn(void)
+{
+ int ret;
+
+ /*
+ * If EUPDATESVN is not available, it is ok to
+ * silently skip it to comply with legacy behavior.
+ */
+ if (!cpu_feature_enabled(X86_FEATURE_SGX_EUPDATESVN))
+ return 0;
+
+ /*
+ * EPC is guaranteed to be empty when there are no users.
+ * Ensure we are on our first user before proceeding further.
+ */
+ WARN(sgx_usage_count, "Elevated usage count when calling EUPDATESVN\n");
+
+ for (int i = 0; i < RDRAND_RETRY_LOOPS; i++) {
+ ret = __eupdatesvn();
+
+ /* Stop on success or unexpected errors: */
+ if (ret != SGX_INSUFFICIENT_ENTROPY)
+ break;
+ }
+
+ switch (ret) {
+ case 0:
+ /*
+ * SVN successfully updated.
+ * Let users know when the update was successful.
+ */
+ pr_info("SVN updated successfully\n");
+ return 0;
+ case SGX_NO_UPDATE:
+ /*
+ * SVN update failed since the current SVN is
+ * not newer than CPUSVN. This is the most
+ * common case and indicates no harm.
+ */
+ return 0;
+ case SGX_INSUFFICIENT_ENTROPY:
+ /*
+ * SVN update failed due to lack of entropy in DRNG.
+ * Indicate to userspace that it should retry.
+ */
+ return -EAGAIN;
+ default:
+ break;
+ }
+
+ /*
+ * EUPDATESVN was called when EPC is empty, all other error
+ * codes are unexpected.
+ */
+ ENCLS_WARN(ret, "EUPDATESVN");
+ return -EIO;
+}
+
+/* Mutex to ensure no concurrent EPC accesses during EUPDATESVN */
+static DEFINE_MUTEX(sgx_svn_lock);
+
+int sgx_inc_usage_count(void)
+{
+ int ret;
+
+ guard(mutex)(&sgx_svn_lock);
+
+ if (!sgx_usage_count) {
+ ret = sgx_update_svn();
+ if (ret)
+ return ret;
+ }
+
+ sgx_usage_count++;
+
+ return 0;
+}
+
+void sgx_dec_usage_count(void)
+{
+ guard(mutex)(&sgx_svn_lock);
+ sgx_usage_count--;
+}
static int __init sgx_init(void)
{
diff --git a/arch/x86/kernel/cpu/sgx/sgx.h b/arch/x86/kernel/cpu/sgx/sgx.h
index d2dad21259a8..f5940393d9bd 100644
--- a/arch/x86/kernel/cpu/sgx/sgx.h
+++ b/arch/x86/kernel/cpu/sgx/sgx.h
@@ -102,6 +102,9 @@ static inline int __init sgx_vepc_init(void)
}
#endif
+int sgx_inc_usage_count(void);
+void sgx_dec_usage_count(void);
+
void sgx_update_lepubkeyhash(u64 *lepubkeyhash);
#endif /* _X86_SGX_H */
diff --git a/arch/x86/kernel/cpu/sgx/virt.c b/arch/x86/kernel/cpu/sgx/virt.c
index 7aaa3652e31d..8de1f1a755f2 100644
--- a/arch/x86/kernel/cpu/sgx/virt.c
+++ b/arch/x86/kernel/cpu/sgx/virt.c
@@ -5,6 +5,7 @@
* Copyright(c) 2021 Intel Corporation.
*/
+#include <linux/kvm_types.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/mman.h>
@@ -255,10 +256,11 @@ static int sgx_vepc_release(struct inode *inode, struct file *file)
xa_destroy(&vepc->page_array);
kfree(vepc);
+ sgx_dec_usage_count();
return 0;
}
-static int sgx_vepc_open(struct inode *inode, struct file *file)
+static int __sgx_vepc_open(struct inode *inode, struct file *file)
{
struct sgx_vepc *vepc;
@@ -273,6 +275,23 @@ static int sgx_vepc_open(struct inode *inode, struct file *file)
return 0;
}
+static int sgx_vepc_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ ret = sgx_inc_usage_count();
+ if (ret)
+ return ret;
+
+ ret = __sgx_vepc_open(inode, file);
+ if (ret) {
+ sgx_dec_usage_count();
+ return ret;
+ }
+
+ return 0;
+}
+
static long sgx_vepc_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
@@ -363,7 +382,7 @@ int sgx_virt_ecreate(struct sgx_pageinfo *pageinfo, void __user *secs,
WARN_ON_ONCE(ret);
return 0;
}
-EXPORT_SYMBOL_GPL(sgx_virt_ecreate);
+EXPORT_SYMBOL_FOR_KVM(sgx_virt_ecreate);
static int __sgx_virt_einit(void __user *sigstruct, void __user *token,
void __user *secs)
@@ -432,4 +451,4 @@ int sgx_virt_einit(void __user *sigstruct, void __user *token,
return ret;
}
-EXPORT_SYMBOL_GPL(sgx_virt_einit);
+EXPORT_SYMBOL_FOR_KVM(sgx_virt_einit);
diff --git a/arch/x86/kernel/cpu/topology.c b/arch/x86/kernel/cpu/topology.c
index 6073a16628f9..f55ea3cdbf88 100644
--- a/arch/x86/kernel/cpu/topology.c
+++ b/arch/x86/kernel/cpu/topology.c
@@ -75,15 +75,11 @@ bool arch_match_cpu_phys_id(int cpu, u64 phys_id)
return phys_id == (u64)cpuid_to_apicid[cpu];
}
-#ifdef CONFIG_SMP
static void cpu_mark_primary_thread(unsigned int cpu, unsigned int apicid)
{
if (!(apicid & (__max_threads_per_core - 1)))
cpumask_set_cpu(cpu, &__cpu_primary_thread_mask);
}
-#else
-static inline void cpu_mark_primary_thread(unsigned int cpu, unsigned int apicid) { }
-#endif
/*
* Convert the APIC ID to a domain level ID by masking out the low bits
diff --git a/arch/x86/kernel/cpu/topology_common.c b/arch/x86/kernel/cpu/topology_common.c
index b5a5e1411469..71625795d711 100644
--- a/arch/x86/kernel/cpu/topology_common.c
+++ b/arch/x86/kernel/cpu/topology_common.c
@@ -16,6 +16,9 @@ EXPORT_SYMBOL_GPL(x86_topo_system);
unsigned int __amd_nodes_per_pkg __ro_after_init;
EXPORT_SYMBOL_GPL(__amd_nodes_per_pkg);
+/* CPUs which are the primary SMT threads */
+struct cpumask __cpu_primary_thread_mask __read_mostly;
+
void topology_set_dom(struct topo_scan *tscan, enum x86_topology_domains dom,
unsigned int shift, unsigned int ncpus)
{
diff --git a/arch/x86/kernel/cpu/tsx.c b/arch/x86/kernel/cpu/tsx.c
index 49782724a943..209b5a22d880 100644
--- a/arch/x86/kernel/cpu/tsx.c
+++ b/arch/x86/kernel/cpu/tsx.c
@@ -19,7 +19,17 @@
#undef pr_fmt
#define pr_fmt(fmt) "tsx: " fmt
-enum tsx_ctrl_states tsx_ctrl_state __ro_after_init = TSX_CTRL_NOT_SUPPORTED;
+enum tsx_ctrl_states {
+ TSX_CTRL_AUTO,
+ TSX_CTRL_ENABLE,
+ TSX_CTRL_DISABLE,
+ TSX_CTRL_RTM_ALWAYS_ABORT,
+ TSX_CTRL_NOT_SUPPORTED,
+};
+
+static enum tsx_ctrl_states tsx_ctrl_state __ro_after_init =
+ IS_ENABLED(CONFIG_X86_INTEL_TSX_MODE_AUTO) ? TSX_CTRL_AUTO :
+ IS_ENABLED(CONFIG_X86_INTEL_TSX_MODE_OFF) ? TSX_CTRL_DISABLE : TSX_CTRL_ENABLE;
static void tsx_disable(void)
{
@@ -156,11 +166,28 @@ static void tsx_dev_mode_disable(void)
}
}
-void __init tsx_init(void)
+static int __init tsx_parse_cmdline(char *str)
{
- char arg[5] = {};
- int ret;
+ if (!str)
+ return -EINVAL;
+
+ if (!strcmp(str, "on")) {
+ tsx_ctrl_state = TSX_CTRL_ENABLE;
+ } else if (!strcmp(str, "off")) {
+ tsx_ctrl_state = TSX_CTRL_DISABLE;
+ } else if (!strcmp(str, "auto")) {
+ tsx_ctrl_state = TSX_CTRL_AUTO;
+ } else {
+ tsx_ctrl_state = TSX_CTRL_DISABLE;
+ pr_err("invalid option, defaulting to off\n");
+ }
+
+ return 0;
+}
+early_param("tsx", tsx_parse_cmdline);
+void __init tsx_init(void)
+{
tsx_dev_mode_disable();
/*
@@ -194,27 +221,8 @@ void __init tsx_init(void)
return;
}
- ret = cmdline_find_option(boot_command_line, "tsx", arg, sizeof(arg));
- if (ret >= 0) {
- if (!strcmp(arg, "on")) {
- tsx_ctrl_state = TSX_CTRL_ENABLE;
- } else if (!strcmp(arg, "off")) {
- tsx_ctrl_state = TSX_CTRL_DISABLE;
- } else if (!strcmp(arg, "auto")) {
- tsx_ctrl_state = x86_get_tsx_auto_mode();
- } else {
- tsx_ctrl_state = TSX_CTRL_DISABLE;
- pr_err("invalid option, defaulting to off\n");
- }
- } else {
- /* tsx= not provided */
- if (IS_ENABLED(CONFIG_X86_INTEL_TSX_MODE_AUTO))
- tsx_ctrl_state = x86_get_tsx_auto_mode();
- else if (IS_ENABLED(CONFIG_X86_INTEL_TSX_MODE_OFF))
- tsx_ctrl_state = TSX_CTRL_DISABLE;
- else
- tsx_ctrl_state = TSX_CTRL_ENABLE;
- }
+ if (tsx_ctrl_state == TSX_CTRL_AUTO)
+ tsx_ctrl_state = x86_get_tsx_auto_mode();
if (tsx_ctrl_state == TSX_CTRL_DISABLE) {
tsx_disable();
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index 71ee20102a8a..b10684dedc58 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -181,8 +181,8 @@ static void show_regs_if_on_stack(struct stack_info *info, struct pt_regs *regs,
* in false positive reports. Disable instrumentation to avoid those.
*/
__no_kmsan_checks
-static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
- unsigned long *stack, const char *log_lvl)
+static void __show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, const char *log_lvl)
{
struct unwind_state state;
struct stack_info stack_info = {0};
@@ -303,6 +303,25 @@ next:
}
}
+static void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
+ unsigned long *stack, const char *log_lvl)
+{
+ /*
+ * Disable KASAN to avoid false positives during walking another
+ * task's stacks, as values on these stacks may change concurrently
+ * with task execution.
+ */
+ bool disable_kasan = task && task != current;
+
+ if (disable_kasan)
+ kasan_disable_current();
+
+ __show_trace_log_lvl(task, regs, stack, log_lvl);
+
+ if (disable_kasan)
+ kasan_enable_current();
+}
+
void show_stack(struct task_struct *task, unsigned long *sp,
const char *loglvl)
{
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index c3acbd26408b..b15b97d3cb52 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -16,6 +16,7 @@
#include <linux/firmware-map.h>
#include <linux/sort.h>
#include <linux/memory_hotplug.h>
+#include <linux/kvm_types.h>
#include <asm/e820/api.h>
#include <asm/setup.h>
@@ -95,7 +96,7 @@ bool e820__mapped_raw_any(u64 start, u64 end, enum e820_type type)
{
return _e820__mapped_any(e820_table_firmware, start, end, type);
}
-EXPORT_SYMBOL_GPL(e820__mapped_raw_any);
+EXPORT_SYMBOL_FOR_KVM(e820__mapped_raw_any);
bool e820__mapped_any(u64 start, u64 end, enum e820_type type)
{
diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c
index 1f71cc135e9a..da233f20ae6f 100644
--- a/arch/x86/kernel/fpu/core.c
+++ b/arch/x86/kernel/fpu/core.c
@@ -18,6 +18,7 @@
#include <uapi/asm/kvm.h>
#include <linux/hardirq.h>
+#include <linux/kvm_types.h>
#include <linux/pkeys.h>
#include <linux/vmalloc.h>
@@ -276,7 +277,7 @@ bool fpu_alloc_guest_fpstate(struct fpu_guest *gfpu)
return true;
}
-EXPORT_SYMBOL_GPL(fpu_alloc_guest_fpstate);
+EXPORT_SYMBOL_FOR_KVM(fpu_alloc_guest_fpstate);
void fpu_free_guest_fpstate(struct fpu_guest *gfpu)
{
@@ -291,7 +292,7 @@ void fpu_free_guest_fpstate(struct fpu_guest *gfpu)
gfpu->fpstate = NULL;
vfree(fpstate);
}
-EXPORT_SYMBOL_GPL(fpu_free_guest_fpstate);
+EXPORT_SYMBOL_FOR_KVM(fpu_free_guest_fpstate);
/*
* fpu_enable_guest_xfd_features - Check xfeatures against guest perm and enable
@@ -313,7 +314,7 @@ int fpu_enable_guest_xfd_features(struct fpu_guest *guest_fpu, u64 xfeatures)
return __xfd_enable_feature(xfeatures, guest_fpu);
}
-EXPORT_SYMBOL_GPL(fpu_enable_guest_xfd_features);
+EXPORT_SYMBOL_FOR_KVM(fpu_enable_guest_xfd_features);
#ifdef CONFIG_X86_64
void fpu_update_guest_xfd(struct fpu_guest *guest_fpu, u64 xfd)
@@ -324,7 +325,7 @@ void fpu_update_guest_xfd(struct fpu_guest *guest_fpu, u64 xfd)
xfd_update_state(guest_fpu->fpstate);
fpregs_unlock();
}
-EXPORT_SYMBOL_GPL(fpu_update_guest_xfd);
+EXPORT_SYMBOL_FOR_KVM(fpu_update_guest_xfd);
/**
* fpu_sync_guest_vmexit_xfd_state - Synchronize XFD MSR and software state
@@ -348,7 +349,7 @@ void fpu_sync_guest_vmexit_xfd_state(void)
__this_cpu_write(xfd_state, fpstate->xfd);
}
}
-EXPORT_SYMBOL_GPL(fpu_sync_guest_vmexit_xfd_state);
+EXPORT_SYMBOL_FOR_KVM(fpu_sync_guest_vmexit_xfd_state);
#endif /* CONFIG_X86_64 */
int fpu_swap_kvm_fpstate(struct fpu_guest *guest_fpu, bool enter_guest)
@@ -390,7 +391,7 @@ int fpu_swap_kvm_fpstate(struct fpu_guest *guest_fpu, bool enter_guest)
fpregs_unlock();
return 0;
}
-EXPORT_SYMBOL_GPL(fpu_swap_kvm_fpstate);
+EXPORT_SYMBOL_FOR_KVM(fpu_swap_kvm_fpstate);
void fpu_copy_guest_fpstate_to_uabi(struct fpu_guest *gfpu, void *buf,
unsigned int size, u64 xfeatures, u32 pkru)
@@ -409,7 +410,7 @@ void fpu_copy_guest_fpstate_to_uabi(struct fpu_guest *gfpu, void *buf,
ustate->xsave.header.xfeatures = XFEATURE_MASK_FPSSE;
}
}
-EXPORT_SYMBOL_GPL(fpu_copy_guest_fpstate_to_uabi);
+EXPORT_SYMBOL_FOR_KVM(fpu_copy_guest_fpstate_to_uabi);
int fpu_copy_uabi_to_guest_fpstate(struct fpu_guest *gfpu, const void *buf,
u64 xcr0, u32 *vpkru)
@@ -439,7 +440,7 @@ int fpu_copy_uabi_to_guest_fpstate(struct fpu_guest *gfpu, const void *buf,
return copy_uabi_from_kernel_to_xstate(kstate, ustate, vpkru);
}
-EXPORT_SYMBOL_GPL(fpu_copy_uabi_to_guest_fpstate);
+EXPORT_SYMBOL_FOR_KVM(fpu_copy_uabi_to_guest_fpstate);
#endif /* CONFIG_KVM */
void kernel_fpu_begin_mask(unsigned int kfpu_mask)
@@ -825,6 +826,9 @@ void fpu__clear_user_states(struct fpu *fpu)
!fpregs_state_valid(fpu, smp_processor_id()))
os_xrstor_supervisor(fpu->fpstate);
+ /* Ensure XFD state is in sync before reloading XSTATE */
+ xfd_update_state(fpu->fpstate);
+
/* Reset user states in registers. */
restore_fpregs_from_init_fpstate(XFEATURE_MASK_USER_RESTORE);
@@ -854,7 +858,7 @@ void switch_fpu_return(void)
fpregs_restore_userregs();
}
-EXPORT_SYMBOL_GPL(switch_fpu_return);
+EXPORT_SYMBOL_FOR_KVM(switch_fpu_return);
void fpregs_lock_and_load(void)
{
@@ -889,7 +893,7 @@ void fpregs_assert_state_consistent(void)
WARN_ON_FPU(!fpregs_state_valid(fpu, smp_processor_id()));
}
-EXPORT_SYMBOL_GPL(fpregs_assert_state_consistent);
+EXPORT_SYMBOL_FOR_KVM(fpregs_assert_state_consistent);
#endif
void fpregs_mark_activate(void)
diff --git a/arch/x86/kernel/fpu/xstate.c b/arch/x86/kernel/fpu/xstate.c
index 28e4fd65c9da..48113c5193aa 100644
--- a/arch/x86/kernel/fpu/xstate.c
+++ b/arch/x86/kernel/fpu/xstate.c
@@ -8,6 +8,7 @@
#include <linux/compat.h>
#include <linux/cpu.h>
#include <linux/mman.h>
+#include <linux/kvm_types.h>
#include <linux/nospec.h>
#include <linux/pkeys.h>
#include <linux/seq_file.h>
@@ -1058,7 +1059,7 @@ void *get_xsave_addr(struct xregs_state *xsave, int xfeature_nr)
return __raw_xsave_addr(xsave, xfeature_nr);
}
-EXPORT_SYMBOL_GPL(get_xsave_addr);
+EXPORT_SYMBOL_FOR_KVM(get_xsave_addr);
/*
* Given an xstate feature nr, calculate where in the xsave buffer the state is.
@@ -1482,7 +1483,7 @@ void fpstate_clear_xstate_component(struct fpstate *fpstate, unsigned int xfeatu
if (addr)
memset(addr, 0, xstate_sizes[xfeature]);
}
-EXPORT_SYMBOL_GPL(fpstate_clear_xstate_component);
+EXPORT_SYMBOL_FOR_KVM(fpstate_clear_xstate_component);
#endif
#ifdef CONFIG_X86_64
@@ -1818,7 +1819,7 @@ u64 xstate_get_guest_group_perm(void)
{
return xstate_get_group_perm(true);
}
-EXPORT_SYMBOL_GPL(xstate_get_guest_group_perm);
+EXPORT_SYMBOL_FOR_KVM(xstate_get_guest_group_perm);
/**
* fpu_xstate_prctl - xstate permission operations
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 4450acec9390..0543b57f54ee 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -74,7 +74,12 @@ static const char *ftrace_call_replace(unsigned long ip, unsigned long addr)
* No need to translate into a callthunk. The trampoline does
* the depth accounting itself.
*/
- return text_gen_insn(CALL_INSN_OPCODE, (void *)ip, (void *)addr);
+ if (ftrace_is_jmp(addr)) {
+ addr = ftrace_jmp_get(addr);
+ return text_gen_insn(JMP32_INSN_OPCODE, (void *)ip, (void *)addr);
+ } else {
+ return text_gen_insn(CALL_INSN_OPCODE, (void *)ip, (void *)addr);
+ }
}
static int ftrace_verify_code(unsigned long ip, const char *old_code)
diff --git a/arch/x86/kernel/ftrace_64.S b/arch/x86/kernel/ftrace_64.S
index 367da3638167..a132608265f6 100644
--- a/arch/x86/kernel/ftrace_64.S
+++ b/arch/x86/kernel/ftrace_64.S
@@ -285,8 +285,18 @@ SYM_INNER_LABEL(ftrace_regs_caller_end, SYM_L_GLOBAL)
ANNOTATE_NOENDBR
RET
+1:
+ testb $1, %al
+ jz 2f
+ andq $0xfffffffffffffffe, %rax
+ movq %rax, MCOUNT_REG_SIZE+8(%rsp)
+ restore_mcount_regs
+ /* Restore flags */
+ popfq
+ RET
+
/* Swap the flags with orig_rax */
-1: movq MCOUNT_REG_SIZE(%rsp), %rdi
+2: movq MCOUNT_REG_SIZE(%rsp), %rdi
movq %rdi, MCOUNT_REG_SIZE-8(%rsp)
movq %rax, MCOUNT_REG_SIZE(%rsp)
@@ -354,12 +364,17 @@ SYM_CODE_START(return_to_handler)
UNWIND_HINT_UNDEFINED
ANNOTATE_NOENDBR
+ /* Restore return_to_handler value that got eaten by previous ret instruction. */
+ subq $8, %rsp
+ UNWIND_HINT_FUNC
+
/* Save ftrace_regs for function exit context */
subq $(FRAME_SIZE), %rsp
movq %rax, RAX(%rsp)
movq %rdx, RDX(%rsp)
movq %rbp, RBP(%rsp)
+ movq %rsp, RSP(%rsp)
movq %rsp, %rdi
call ftrace_return_to_handler
@@ -368,7 +383,8 @@ SYM_CODE_START(return_to_handler)
movq RDX(%rsp), %rdx
movq RAX(%rsp), %rax
- addq $(FRAME_SIZE), %rsp
+ addq $(FRAME_SIZE) + 8, %rsp
+
/*
* Jump back to the old return address. This cannot be JMP_NOSPEC rdi
* since IBT would demand that contain ENDBR, which simply isn't so for
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index b01644c949b2..f846c15f21ca 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -24,6 +24,7 @@
#include <linux/percpu.h>
#include <linux/kdebug.h>
#include <linux/kernel.h>
+#include <linux/kvm_types.h>
#include <linux/export.h>
#include <linux/sched.h>
#include <linux/smp.h>
@@ -489,7 +490,7 @@ void hw_breakpoint_restore(void)
set_debugreg(DR6_RESERVED, 6);
set_debugreg(__this_cpu_read(cpu_dr7), 7);
}
-EXPORT_SYMBOL_GPL(hw_breakpoint_restore);
+EXPORT_SYMBOL_FOR_KVM(hw_breakpoint_restore);
/*
* Handle debug exception notifications.
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index 10721a125226..86f4e574de02 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -12,6 +12,7 @@
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/irq.h>
+#include <linux/kvm_types.h>
#include <asm/irq_stack.h>
#include <asm/apic.h>
@@ -361,7 +362,7 @@ void kvm_set_posted_intr_wakeup_handler(void (*handler)(void))
synchronize_rcu();
}
}
-EXPORT_SYMBOL_GPL(kvm_set_posted_intr_wakeup_handler);
+EXPORT_SYMBOL_FOR_KVM(kvm_set_posted_intr_wakeup_handler);
/*
* Handler for POSTED_INTERRUPT_VECTOR.
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 3863d7709386..c1fac3a9fecc 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -141,7 +141,6 @@ bool can_boost(struct insn *insn, void *addr)
{
kprobe_opcode_t opcode;
insn_byte_t prefix;
- int i;
if (search_exception_tables((unsigned long)addr))
return false; /* Page fault may occur on this address. */
@@ -154,7 +153,7 @@ bool can_boost(struct insn *insn, void *addr)
if (insn->opcode.nbytes != 1)
return false;
- for_each_insn_prefix(insn, i, prefix) {
+ for_each_insn_prefix(insn, prefix) {
insn_attr_t attr;
attr = inat_get_opcode_attribute(prefix);
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index 0aabd4c4e2c4..6f826a00eca2 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -103,7 +103,6 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val)
asm (
".pushsection .rodata\n"
- "optprobe_template_func:\n"
".global optprobe_template_entry\n"
"optprobe_template_entry:\n"
#ifdef CONFIG_X86_64
@@ -160,9 +159,6 @@ asm (
"optprobe_template_end:\n"
".popsection\n");
-void optprobe_template_func(void);
-STACK_FRAME_NON_STANDARD(optprobe_template_func);
-
#define TMPL_CLAC_IDX \
((long)optprobe_template_clac - (long)optprobe_template_entry)
#define TMPL_MOVE_IDX \
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index b67d7c59dca0..204765004c72 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -29,6 +29,7 @@
#include <linux/syscore_ops.h>
#include <linux/cc_platform.h>
#include <linux/efi.h>
+#include <linux/kvm_types.h>
#include <asm/timer.h>
#include <asm/cpu.h>
#include <asm/traps.h>
@@ -162,7 +163,7 @@ void kvm_async_pf_task_wait_schedule(u32 token)
}
finish_swait(&n.wq, &wait);
}
-EXPORT_SYMBOL_GPL(kvm_async_pf_task_wait_schedule);
+EXPORT_SYMBOL_FOR_KVM(kvm_async_pf_task_wait_schedule);
static void apf_task_wake_one(struct kvm_task_sleep_node *n)
{
@@ -253,7 +254,7 @@ noinstr u32 kvm_read_and_reset_apf_flags(void)
return flags;
}
-EXPORT_SYMBOL_GPL(kvm_read_and_reset_apf_flags);
+EXPORT_SYMBOL_FOR_KVM(kvm_read_and_reset_apf_flags);
noinstr bool __kvm_handle_async_pf(struct pt_regs *regs, u32 token)
{
diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c
index 0ffbae902e2f..11c45ce42694 100644
--- a/arch/x86/kernel/module.c
+++ b/arch/x86/kernel/module.c
@@ -97,6 +97,7 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,
DEBUGP("%s relocate section %u to %u\n",
apply ? "Applying" : "Clearing",
relsec, sechdrs[relsec].sh_info);
+
for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
size_t size;
@@ -162,15 +163,17 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,
if (apply) {
if (memcmp(loc, &zero, size)) {
- pr_err("x86/modules: Invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n",
- (int)ELF64_R_TYPE(rel[i].r_info), loc, val);
+ pr_err("x86/modules: Invalid relocation target, existing value is nonzero for sec %u, idx %u, type %d, loc %lx, val %llx\n",
+ relsec, i, (int)ELF64_R_TYPE(rel[i].r_info),
+ (unsigned long)loc, val);
return -ENOEXEC;
}
write(loc, &val, size);
} else {
if (memcmp(loc, &val, size)) {
- pr_warn("x86/modules: Invalid relocation target, existing value does not match expected value for type %d, loc %p, val %Lx\n",
- (int)ELF64_R_TYPE(rel[i].r_info), loc, val);
+ pr_warn("x86/modules: Invalid relocation target, existing value does not match expected value for sec %u, idx %u, type %d, loc %lx, val %llx\n",
+ relsec, i, (int)ELF64_R_TYPE(rel[i].r_info),
+ (unsigned long)loc, val);
return -ENOEXEC;
}
write(loc, &zero, size);
@@ -179,8 +182,8 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,
return 0;
overflow:
- pr_err("overflow in relocation type %d val %Lx\n",
- (int)ELF64_R_TYPE(rel[i].r_info), val);
+ pr_err("overflow in relocation type %d val %llx sec %u idx %d\n",
+ (int)ELF64_R_TYPE(rel[i].r_info), val, relsec, i);
pr_err("`%s' likely not compiled with -mcmodel=kernel\n",
me->name);
return -ENOEXEC;
diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c
index e17c16c54a37..4469c784eaa0 100644
--- a/arch/x86/kernel/msr.c
+++ b/arch/x86/kernel/msr.c
@@ -98,7 +98,7 @@ static int filter_write(u32 reg)
if (!__ratelimit(&fw_rs))
return 0;
- pr_warn("Write to unrecognized MSR 0x%x by %s (pid: %d).\n",
+ pr_warn("Write to unrecognized MSR 0x%x by %s (pid: %d), tainting CPU_OUT_OF_SPEC.\n",
reg, current->comm, current->pid);
pr_warn("See https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/about for details.\n");
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index be93ec7255bf..3d239ed12744 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -24,6 +24,7 @@
#include <linux/export.h>
#include <linux/atomic.h>
#include <linux/sched/clock.h>
+#include <linux/kvm_types.h>
#include <asm/cpu_entry_area.h>
#include <asm/traps.h>
@@ -613,9 +614,7 @@ DEFINE_IDTENTRY_RAW(exc_nmi_kvm_vmx)
{
exc_nmi(regs);
}
-#if IS_MODULE(CONFIG_KVM_INTEL)
-EXPORT_SYMBOL_GPL(asm_exc_nmi_kvm_vmx);
-#endif
+EXPORT_SYMBOL_FOR_KVM(asm_exc_nmi_kvm_vmx);
#endif
#ifdef CONFIG_NMI_CHECK_CPU
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 52a5c03c353c..432c0a004c60 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -30,6 +30,7 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/export.h>
+#include <linux/kvm_types.h>
#include <linux/ptrace.h>
#include <linux/notifier.h>
#include <linux/kprobes.h>
@@ -303,9 +304,7 @@ void current_save_fsgs(void)
save_fsgs(current);
local_irq_restore(flags);
}
-#if IS_ENABLED(CONFIG_KVM)
-EXPORT_SYMBOL_GPL(current_save_fsgs);
-#endif
+EXPORT_SYMBOL_FOR_KVM(current_save_fsgs);
static __always_inline void loadseg(enum which_selector which,
unsigned short sel)
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 964f6b0a3d68..6032fa9ec753 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -13,6 +13,7 @@
#include <linux/objtool.h>
#include <linux/pgtable.h>
#include <linux/kexec.h>
+#include <linux/kvm_types.h>
#include <acpi/reboot.h>
#include <asm/io.h>
#include <asm/apic.h>
@@ -541,7 +542,7 @@ void cpu_emergency_register_virt_callback(cpu_emergency_virt_cb *callback)
rcu_assign_pointer(cpu_emergency_virt_callback, callback);
}
-EXPORT_SYMBOL_GPL(cpu_emergency_register_virt_callback);
+EXPORT_SYMBOL_FOR_KVM(cpu_emergency_register_virt_callback);
void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback)
{
@@ -551,7 +552,7 @@ void cpu_emergency_unregister_virt_callback(cpu_emergency_virt_cb *callback)
rcu_assign_pointer(cpu_emergency_virt_callback, NULL);
synchronize_rcu();
}
-EXPORT_SYMBOL_GPL(cpu_emergency_unregister_virt_callback);
+EXPORT_SYMBOL_FOR_KVM(cpu_emergency_unregister_virt_callback);
/*
* Disable virtualization, i.e. VMX or SVM, to ensure INIT is recognized during
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index 11e20bb13aca..4ffba68dc57b 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -95,9 +95,12 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
/* Leave CR4 in %r13 to enable the right paging mode later. */
movq %cr4, %r13
- /* Disable global pages immediately to ensure this mapping is RWX */
+ /*
+ * Disable global pages immediately to ensure this mapping is RWX.
+ * Disable LASS before jumping to the identity mapped page.
+ */
movq %r13, %r12
- andq $~(X86_CR4_PGE), %r12
+ andq $~(X86_CR4_PGE | X86_CR4_LASS), %r12
movq %r12, %cr4
/* Save %rsp and CRs. */
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index eb289abece23..5cd6950ab672 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -103,9 +103,6 @@ EXPORT_PER_CPU_SYMBOL(cpu_core_map);
DEFINE_PER_CPU_READ_MOSTLY(cpumask_var_t, cpu_die_map);
EXPORT_PER_CPU_SYMBOL(cpu_die_map);
-/* CPUs which are the primary SMT threads */
-struct cpumask __cpu_primary_thread_mask __read_mostly;
-
/* Representing CPUs for which sibling maps can be computed */
static cpumask_var_t cpu_sibling_setup_mask;
@@ -515,6 +512,76 @@ static void __init build_sched_topology(void)
set_sched_topology(topology);
}
+#ifdef CONFIG_NUMA
+static int sched_avg_remote_distance;
+static int avg_remote_numa_distance(void)
+{
+ int i, j;
+ int distance, nr_remote, total_distance;
+
+ if (sched_avg_remote_distance > 0)
+ return sched_avg_remote_distance;
+
+ nr_remote = 0;
+ total_distance = 0;
+ for_each_node_state(i, N_CPU) {
+ for_each_node_state(j, N_CPU) {
+ distance = node_distance(i, j);
+
+ if (distance >= REMOTE_DISTANCE) {
+ nr_remote++;
+ total_distance += distance;
+ }
+ }
+ }
+ if (nr_remote)
+ sched_avg_remote_distance = total_distance / nr_remote;
+ else
+ sched_avg_remote_distance = REMOTE_DISTANCE;
+
+ return sched_avg_remote_distance;
+}
+
+int arch_sched_node_distance(int from, int to)
+{
+ int d = node_distance(from, to);
+
+ switch (boot_cpu_data.x86_vfm) {
+ case INTEL_GRANITERAPIDS_X:
+ case INTEL_ATOM_DARKMONT_X:
+
+ if (!x86_has_numa_in_package || topology_max_packages() == 1 ||
+ d < REMOTE_DISTANCE)
+ return d;
+
+ /*
+ * With SNC enabled, there could be too many levels of remote
+ * NUMA node distances, creating NUMA domain levels
+ * including local nodes and partial remote nodes.
+ *
+ * Trim finer distance tuning for NUMA nodes in remote package
+ * for the purpose of building sched domains. Group NUMA nodes
+ * in the remote package in the same sched group.
+ * Simplify NUMA domains and avoid extra NUMA levels including
+ * different remote NUMA nodes and local nodes.
+ *
+ * GNR and CWF don't expect systems with more than 2 packages
+ * and more than 2 hops between packages. Single average remote
+ * distance won't be appropriate if there are more than 2
+ * packages as average distance to different remote packages
+ * could be different.
+ */
+ WARN_ONCE(topology_max_packages() > 2,
+ "sched: Expect only up to 2 packages for GNR or CWF, "
+ "but saw %d packages when building sched domains.",
+ topology_max_packages());
+
+ d = avg_remote_numa_distance();
+ }
+ return d;
+}
+#endif /* CONFIG_NUMA */
+
void set_cpu_sibling_map(int cpu)
{
bool has_smt = __max_threads_per_core > 1;
@@ -1328,11 +1395,7 @@ void __noreturn hlt_play_dead(void)
native_halt();
}
-/*
- * native_play_dead() is essentially a __noreturn function, but it can't
- * be marked as such as the compiler may complain about it.
- */
-void native_play_dead(void)
+void __noreturn native_play_dead(void)
{
if (cpu_feature_enabled(X86_FEATURE_KERNEL_IBRS))
__update_spec_ctrl(0);
@@ -1351,7 +1414,7 @@ int native_cpu_disable(void)
return -ENOSYS;
}
-void native_play_dead(void)
+void __noreturn native_play_dead(void)
{
BUG();
}
diff --git a/arch/x86/kernel/static_call.c b/arch/x86/kernel/static_call.c
index 378c388d1b31..2892cdb14563 100644
--- a/arch/x86/kernel/static_call.c
+++ b/arch/x86/kernel/static_call.c
@@ -26,6 +26,11 @@ static const u8 xor5rax[] = { 0x2e, 0x2e, 0x2e, 0x31, 0xc0 };
static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
+/*
+ * ud1 (%edx),%rdi -- see __WARN_trap() / decode_bug()
+ */
+static const u8 warninsn[] = { 0x67, 0x48, 0x0f, 0xb9, 0x3a };
+
static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */
{
u8 ret = 0;
@@ -69,7 +74,10 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
emulate = code;
code = &xor5rax;
}
-
+ if (func == &__WARN_trap) {
+ emulate = code;
+ code = &warninsn;
+ }
break;
case NOP:
@@ -128,7 +136,8 @@ static void __static_call_validate(u8 *insn, bool tail, bool tramp)
} else {
if (opcode == CALL_INSN_OPCODE ||
!memcmp(insn, x86_nops[5], 5) ||
- !memcmp(insn, xor5rax, 5))
+ !memcmp(insn, xor5rax, 5) ||
+ !memcmp(insn, warninsn, 5))
return;
}
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index 6b22611e69cc..bcf1dedc1d00 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -31,6 +31,7 @@
#include <linux/kexec.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
+#include <linux/static_call.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/bug.h>
@@ -102,25 +103,37 @@ __always_inline int is_valid_bugaddr(unsigned long addr)
* UBSan{0}: 67 0f b9 00 ud1 (%eax),%eax
* UBSan{10}: 67 0f b9 40 10 ud1 0x10(%eax),%eax
* static_call: 0f b9 cc ud1 %esp,%ecx
+ * __WARN_trap: 67 48 0f b9 3a ud1 (%edx),%reg
*
- * Notably UBSAN uses EAX, static_call uses ECX.
+ * Notable, since __WARN_trap can use all registers, the distinction between
+ * UD1 users is through R/M.
*/
__always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
{
unsigned long start = addr;
+ u8 v, reg, rm, rex = 0;
+ int type = BUG_UD1;
bool lock = false;
- u8 v;
if (addr < TASK_SIZE_MAX)
return BUG_NONE;
- v = *(u8 *)(addr++);
- if (v == INSN_ASOP)
+ for (;;) {
v = *(u8 *)(addr++);
+ if (v == INSN_ASOP)
+ continue;
- if (v == INSN_LOCK) {
- lock = true;
- v = *(u8 *)(addr++);
+ if (v == INSN_LOCK) {
+ lock = true;
+ continue;
+ }
+
+ if ((v & 0xf0) == 0x40) {
+ rex = v;
+ continue;
+ }
+
+ break;
}
switch (v) {
@@ -156,18 +169,33 @@ __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
if (X86_MODRM_MOD(v) != 3 && X86_MODRM_RM(v) == 4)
addr++; /* SIB */
+ reg = X86_MODRM_REG(v) + 8*!!X86_REX_R(rex);
+ rm = X86_MODRM_RM(v) + 8*!!X86_REX_B(rex);
+
/* Decode immediate, if present */
switch (X86_MODRM_MOD(v)) {
case 0: if (X86_MODRM_RM(v) == 5)
- addr += 4; /* RIP + disp32 */
+ addr += 4; /* RIP + disp32 */
+
+ if (rm == 0) /* (%eax) */
+ type = BUG_UD1_UBSAN;
+
+ if (rm == 2) { /* (%edx) */
+ *imm = reg;
+ type = BUG_UD1_WARN;
+ }
break;
case 1: *imm = *(s8 *)addr;
addr += 1;
+ if (rm == 0) /* (%eax) */
+ type = BUG_UD1_UBSAN;
break;
case 2: *imm = *(s32 *)addr;
addr += 4;
+ if (rm == 0) /* (%eax) */
+ type = BUG_UD1_UBSAN;
break;
case 3: break;
@@ -176,12 +204,76 @@ __always_inline int decode_bug(unsigned long addr, s32 *imm, int *len)
/* record instruction length */
*len = addr - start;
- if (X86_MODRM_REG(v) == 0) /* EAX */
- return BUG_UD1_UBSAN;
+ return type;
+}
- return BUG_UD1;
+static inline unsigned long pt_regs_val(struct pt_regs *regs, int nr)
+{
+ int offset = pt_regs_offset(regs, nr);
+ if (WARN_ON_ONCE(offset < -0))
+ return 0;
+ return *((unsigned long *)((void *)regs + offset));
}
+#ifdef HAVE_ARCH_BUG_FORMAT_ARGS
+DEFINE_STATIC_CALL(WARN_trap, __WARN_trap);
+EXPORT_STATIC_CALL_TRAMP(WARN_trap);
+
+/*
+ * Create a va_list from an exception context.
+ */
+void *__warn_args(struct arch_va_list *args, struct pt_regs *regs)
+{
+ /*
+ * Register save area; populate with function call argument registers
+ */
+ args->regs[0] = regs->di;
+ args->regs[1] = regs->si;
+ args->regs[2] = regs->dx;
+ args->regs[3] = regs->cx;
+ args->regs[4] = regs->r8;
+ args->regs[5] = regs->r9;
+
+ /*
+ * From the ABI document:
+ *
+ * @gp_offset - the element holds the offset in bytes from
+ * reg_save_area to the place where the next available general purpose
+ * argument register is saved. In case all argument registers have
+ * been exhausted, it is set to the value 48 (6*8).
+ *
+ * @fp_offset - the element holds the offset in bytes from
+ * reg_save_area to the place where the next available floating point
+ * argument is saved. In case all argument registers have been
+ * exhausted, it is set to the value 176 (6*8 + 8*16)
+ *
+ * @overflow_arg_area - this pointer is used to fetch arguments passed
+ * on the stack. It is initialized with the address of the first
+ * argument passed on the stack, if any, and then always updated to
+ * point to the start of the next argument on the stack.
+ *
+ * @reg_save_area - the element points to the start of the register
+ * save area.
+ *
+ * Notably the vararg starts with the second argument and there are no
+ * floating point arguments in the kernel.
+ */
+ args->args.gp_offset = 1*8;
+ args->args.fp_offset = 6*8 + 8*16;
+ args->args.reg_save_area = &args->regs;
+ args->args.overflow_arg_area = (void *)regs->sp;
+
+ /*
+ * If the exception came from __WARN_trap, there is a return
+ * address on the stack, skip that. This is why any __WARN_trap()
+ * caller must inhibit tail-call optimization.
+ */
+ if ((void *)regs->ip == &__WARN_trap)
+ args->args.overflow_arg_area += 8;
+
+ return &args->args;
+}
+#endif /* HAVE_ARCH_BUG_FORMAT */
static nokprobe_inline int
do_trap_no_signal(struct task_struct *tsk, int trapnr, const char *str,
@@ -334,6 +426,11 @@ static noinstr bool handle_bug(struct pt_regs *regs)
raw_local_irq_enable();
switch (ud_type) {
+ case BUG_UD1_WARN:
+ if (report_bug_entry((void *)pt_regs_val(regs, ud_imm), regs) == BUG_TRAP_TYPE_WARN)
+ handled = true;
+ break;
+
case BUG_UD2:
if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) {
handled = true;
@@ -635,13 +732,23 @@ DEFINE_IDTENTRY(exc_bounds)
enum kernel_gp_hint {
GP_NO_HINT,
GP_NON_CANONICAL,
- GP_CANONICAL
+ GP_CANONICAL,
+ GP_LASS_VIOLATION,
+ GP_NULL_POINTER,
+};
+
+static const char * const kernel_gp_hint_help[] = {
+ [GP_NON_CANONICAL] = "probably for non-canonical address",
+ [GP_CANONICAL] = "maybe for address",
+ [GP_LASS_VIOLATION] = "probably LASS violation for address",
+ [GP_NULL_POINTER] = "kernel NULL pointer dereference",
};
/*
* When an uncaught #GP occurs, try to determine the memory address accessed by
* the instruction and return that address to the caller. Also, try to figure
- * out whether any part of the access to that address was non-canonical.
+ * out whether any part of the access to that address was non-canonical or
+ * across privilege levels.
*/
static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
unsigned long *addr)
@@ -663,14 +770,28 @@ static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs,
return GP_NO_HINT;
#ifdef CONFIG_X86_64
+ /* Operand is in the kernel half */
+ if (*addr >= ~__VIRTUAL_MASK)
+ return GP_CANONICAL;
+
+ /* The last byte of the operand is not in the user canonical half */
+ if (*addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK)
+ return GP_NON_CANONICAL;
+
/*
- * Check that:
- * - the operand is not in the kernel half
- * - the last byte of the operand is not in the user canonical half
+ * A NULL pointer dereference usually causes a #PF. However, it
+ * can result in a #GP when LASS is active. Provide the same
+ * hint in the rare case that the condition is hit without LASS.
*/
- if (*addr < ~__VIRTUAL_MASK &&
- *addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK)
- return GP_NON_CANONICAL;
+ if (*addr < PAGE_SIZE)
+ return GP_NULL_POINTER;
+
+ /*
+ * Assume that LASS caused the exception, because the address is
+ * canonical and in the user half.
+ */
+ if (cpu_feature_enabled(X86_FEATURE_LASS))
+ return GP_LASS_VIOLATION;
#endif
return GP_CANONICAL;
@@ -833,9 +954,7 @@ DEFINE_IDTENTRY_ERRORCODE(exc_general_protection)
if (hint != GP_NO_HINT)
snprintf(desc, sizeof(desc), GPFSTR ", %s 0x%lx",
- (hint == GP_NON_CANONICAL) ? "probably for non-canonical address"
- : "maybe for address",
- gp_addr);
+ kernel_gp_hint_help[hint], gp_addr);
/*
* KASAN is interested only in the non-canonical case, clear it
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 87e749106dda..7d3e13e14eab 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -11,6 +11,7 @@
#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/clocksource.h>
+#include <linux/kvm_types.h>
#include <linux/percpu.h>
#include <linux/timex.h>
#include <linux/static_key.h>
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index 845aeaf36b8d..7be8e361ca55 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -17,6 +17,7 @@
#include <linux/kdebug.h>
#include <asm/processor.h>
#include <asm/insn.h>
+#include <asm/insn-eval.h>
#include <asm/mmu_context.h>
#include <asm/nops.h>
@@ -258,9 +259,8 @@ static volatile u32 good_2byte_insns[256 / 32] = {
static bool is_prefix_bad(struct insn *insn)
{
insn_byte_t p;
- int i;
- for_each_insn_prefix(insn, i, p) {
+ for_each_insn_prefix(insn, p) {
insn_attr_t attr;
attr = inat_get_opcode_attribute(p);
@@ -1158,35 +1158,12 @@ unlock:
mmap_write_unlock(mm);
}
-static bool insn_is_nop(struct insn *insn)
-{
- return insn->opcode.nbytes == 1 && insn->opcode.bytes[0] == 0x90;
-}
-
-static bool insn_is_nopl(struct insn *insn)
-{
- if (insn->opcode.nbytes != 2)
- return false;
-
- if (insn->opcode.bytes[0] != 0x0f || insn->opcode.bytes[1] != 0x1f)
- return false;
-
- if (!insn->modrm.nbytes)
- return false;
-
- if (X86_MODRM_REG(insn->modrm.bytes[0]) != 0)
- return false;
-
- /* 0f 1f /0 - NOPL */
- return true;
-}
-
static bool can_optimize(struct insn *insn, unsigned long vaddr)
{
if (!insn->x86_64 || insn->length != 5)
return false;
- if (!insn_is_nop(insn) && !insn_is_nopl(insn))
+ if (!insn_is_nop(insn))
return false;
/* We can't do cross page atomic writes yet. */
@@ -1426,19 +1403,14 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
{
u8 opc1 = OPCODE1(insn);
insn_byte_t p;
- int i;
- /* x86_nops[insn->length]; same as jmp with .offs = 0 */
- if (insn->length <= ASM_NOP_MAX &&
- !memcmp(insn->kaddr, x86_nops[insn->length], insn->length))
+ if (insn_is_nop(insn))
goto setup;
switch (opc1) {
case 0xeb: /* jmp 8 */
case 0xe9: /* jmp 32 */
break;
- case 0x90: /* prefix* + nop; same as jmp with .offs = 0 */
- goto setup;
case 0xe8: /* call relative */
branch_clear_offset(auprobe, insn);
@@ -1463,7 +1435,7 @@ static int branch_setup_xol_ops(struct arch_uprobe *auprobe, struct insn *insn)
* Intel and AMD behavior differ in 64-bit mode: Intel ignores 66 prefix.
* No one uses these insns, reject any branch insns with such prefix.
*/
- for_each_insn_prefix(insn, i, p) {
+ for_each_insn_prefix(insn, p) {
if (p == 0x66)
return -ENOTSUPP;
}
@@ -1819,3 +1791,35 @@ bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
else
return regs->sp <= ret->stack;
}
+
+/*
+ * Heuristic-based check if uprobe is installed at the function entry.
+ *
+ * Under assumption of user code being compiled with frame pointers,
+ * `push %rbp/%ebp` is a good indicator that we indeed are.
+ *
+ * Similarly, `endbr64` (assuming 64-bit mode) is also a common pattern.
+ * If we get this wrong, captured stack trace might have one extra bogus
+ * entry, but the rest of stack trace will still be meaningful.
+ */
+bool is_uprobe_at_func_entry(struct pt_regs *regs)
+{
+ struct arch_uprobe *auprobe;
+
+ if (!current->utask)
+ return false;
+
+ auprobe = current->utask->auprobe;
+ if (!auprobe)
+ return false;
+
+ /* push %rbp/%ebp */
+ if (auprobe->insn[0] == 0x55)
+ return true;
+
+ /* endbr64 (64-bit only) */
+ if (user_64bit_mode(regs) && is_endbr((u32 *)auprobe->insn))
+ return true;
+
+ return false;
+}
diff --git a/arch/x86/kvm/reverse_cpuid.h b/arch/x86/kvm/reverse_cpuid.h
index 743ab25ba787..81b4a7acf72e 100644
--- a/arch/x86/kvm/reverse_cpuid.h
+++ b/arch/x86/kvm/reverse_cpuid.h
@@ -78,7 +78,6 @@ static const struct cpuid_reg reverse_cpuid[] = {
[CPUID_6_EAX] = { 6, 0, CPUID_EAX},
[CPUID_8000_000A_EDX] = {0x8000000a, 0, CPUID_EDX},
[CPUID_7_ECX] = { 7, 0, CPUID_ECX},
- [CPUID_8000_0007_EBX] = {0x80000007, 0, CPUID_EBX},
[CPUID_7_EDX] = { 7, 0, CPUID_EDX},
[CPUID_7_1_EAX] = { 7, 1, CPUID_EAX},
[CPUID_12_EAX] = {0x00000012, 0, CPUID_EAX},
diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c
index f286b5706d7c..fef00546c885 100644
--- a/arch/x86/kvm/svm/avic.c
+++ b/arch/x86/kvm/svm/avic.c
@@ -216,7 +216,7 @@ static void avic_deactivate_vmcb(struct vcpu_svm *svm)
* This function is called from IOMMU driver to notify
* SVM to schedule in a particular vCPU of a particular VM.
*/
-int avic_ga_log_notifier(u32 ga_tag)
+static int avic_ga_log_notifier(u32 ga_tag)
{
unsigned long flags;
struct kvm_svm *kvm_svm;
@@ -788,7 +788,7 @@ int avic_init_vcpu(struct vcpu_svm *svm)
struct kvm_vcpu *vcpu = &svm->vcpu;
INIT_LIST_HEAD(&svm->ir_list);
- spin_lock_init(&svm->ir_list_lock);
+ raw_spin_lock_init(&svm->ir_list_lock);
if (!enable_apicv || !irqchip_in_kernel(vcpu->kvm))
return 0;
@@ -816,9 +816,9 @@ static void svm_ir_list_del(struct kvm_kernel_irqfd *irqfd)
if (!vcpu)
return;
- spin_lock_irqsave(&to_svm(vcpu)->ir_list_lock, flags);
+ raw_spin_lock_irqsave(&to_svm(vcpu)->ir_list_lock, flags);
list_del(&irqfd->vcpu_list);
- spin_unlock_irqrestore(&to_svm(vcpu)->ir_list_lock, flags);
+ raw_spin_unlock_irqrestore(&to_svm(vcpu)->ir_list_lock, flags);
}
int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
@@ -855,7 +855,7 @@ int avic_pi_update_irte(struct kvm_kernel_irqfd *irqfd, struct kvm *kvm,
* list of IRQs being posted to the vCPU, to ensure the IRTE
* isn't programmed with stale pCPU/IsRunning information.
*/
- guard(spinlock_irqsave)(&svm->ir_list_lock);
+ guard(raw_spinlock_irqsave)(&svm->ir_list_lock);
/*
* Update the target pCPU for IOMMU doorbells if the vCPU is
@@ -972,7 +972,7 @@ static void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu,
* up-to-date entry information, or that this task will wait until
* svm_ir_list_add() completes to set the new target pCPU.
*/
- spin_lock_irqsave(&svm->ir_list_lock, flags);
+ raw_spin_lock_irqsave(&svm->ir_list_lock, flags);
entry = svm->avic_physical_id_entry;
WARN_ON_ONCE(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK);
@@ -997,7 +997,7 @@ static void __avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu,
avic_update_iommu_vcpu_affinity(vcpu, h_physical_id, action);
- spin_unlock_irqrestore(&svm->ir_list_lock, flags);
+ raw_spin_unlock_irqrestore(&svm->ir_list_lock, flags);
}
void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
@@ -1035,7 +1035,7 @@ static void __avic_vcpu_put(struct kvm_vcpu *vcpu, enum avic_vcpu_action action)
* or that this task will wait until svm_ir_list_add() completes to
* mark the vCPU as not running.
*/
- spin_lock_irqsave(&svm->ir_list_lock, flags);
+ raw_spin_lock_irqsave(&svm->ir_list_lock, flags);
avic_update_iommu_vcpu_affinity(vcpu, -1, action);
@@ -1059,7 +1059,7 @@ static void __avic_vcpu_put(struct kvm_vcpu *vcpu, enum avic_vcpu_action action)
svm->avic_physical_id_entry = entry;
- spin_unlock_irqrestore(&svm->ir_list_lock, flags);
+ raw_spin_unlock_irqrestore(&svm->ir_list_lock, flags);
}
void avic_vcpu_put(struct kvm_vcpu *vcpu)
@@ -1243,3 +1243,9 @@ bool __init avic_hardware_setup(void)
return true;
}
+
+void avic_hardware_unsetup(void)
+{
+ if (avic)
+ amd_iommu_register_ga_log_notifier(NULL);
+}
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index a6443feab252..da6e80b3ac35 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -677,11 +677,10 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm, struct vmcb *vmcb12
*/
svm_copy_lbrs(vmcb02, vmcb12);
vmcb02->save.dbgctl &= ~DEBUGCTL_RESERVED_BITS;
- svm_update_lbrv(&svm->vcpu);
-
- } else if (unlikely(vmcb01->control.virt_ext & LBR_CTL_ENABLE_MASK)) {
+ } else {
svm_copy_lbrs(vmcb02, vmcb01);
}
+ svm_update_lbrv(&svm->vcpu);
}
static inline bool is_evtinj_soft(u32 evtinj)
@@ -833,11 +832,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm,
svm->soft_int_next_rip = vmcb12_rip;
}
- vmcb02->control.virt_ext = vmcb01->control.virt_ext &
- LBR_CTL_ENABLE_MASK;
- if (guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV))
- vmcb02->control.virt_ext |=
- (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK);
+ /* LBR_CTL_ENABLE_MASK is controlled by svm_update_lbrv() */
if (!nested_vmcb_needs_vls_intercept(svm))
vmcb02->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK;
@@ -1189,13 +1184,12 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
kvm_make_request(KVM_REQ_EVENT, &svm->vcpu);
if (unlikely(guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) &&
- (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK))) {
+ (svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK)))
svm_copy_lbrs(vmcb12, vmcb02);
- svm_update_lbrv(vcpu);
- } else if (unlikely(vmcb01->control.virt_ext & LBR_CTL_ENABLE_MASK)) {
+ else
svm_copy_lbrs(vmcb01, vmcb02);
- svm_update_lbrv(vcpu);
- }
+
+ svm_update_lbrv(vcpu);
if (vnmi) {
if (vmcb02->control.int_ctl & V_NMI_BLOCKING_MASK)
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index 153c12dbf3eb..9d29b2e7e855 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -705,7 +705,11 @@ void *svm_alloc_permissions_map(unsigned long size, gfp_t gfp_mask)
static void svm_recalc_lbr_msr_intercepts(struct kvm_vcpu *vcpu)
{
- bool intercept = !(to_svm(vcpu)->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK);
+ struct vcpu_svm *svm = to_svm(vcpu);
+ bool intercept = !(svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK);
+
+ if (intercept == svm->lbr_msrs_intercepted)
+ return;
svm_set_intercept_for_msr(vcpu, MSR_IA32_LASTBRANCHFROMIP, MSR_TYPE_RW, intercept);
svm_set_intercept_for_msr(vcpu, MSR_IA32_LASTBRANCHTOIP, MSR_TYPE_RW, intercept);
@@ -714,6 +718,8 @@ static void svm_recalc_lbr_msr_intercepts(struct kvm_vcpu *vcpu)
if (sev_es_guest(vcpu->kvm))
svm_set_intercept_for_msr(vcpu, MSR_IA32_DEBUGCTLMSR, MSR_TYPE_RW, intercept);
+
+ svm->lbr_msrs_intercepted = intercept;
}
void svm_vcpu_free_msrpm(void *msrpm)
@@ -806,60 +812,43 @@ void svm_copy_lbrs(struct vmcb *to_vmcb, struct vmcb *from_vmcb)
vmcb_mark_dirty(to_vmcb, VMCB_LBR);
}
-void svm_enable_lbrv(struct kvm_vcpu *vcpu)
+static void __svm_enable_lbrv(struct kvm_vcpu *vcpu)
{
- struct vcpu_svm *svm = to_svm(vcpu);
-
- svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK;
- svm_recalc_lbr_msr_intercepts(vcpu);
-
- /* Move the LBR msrs to the vmcb02 so that the guest can see them. */
- if (is_guest_mode(vcpu))
- svm_copy_lbrs(svm->vmcb, svm->vmcb01.ptr);
+ to_svm(vcpu)->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK;
}
-static void svm_disable_lbrv(struct kvm_vcpu *vcpu)
+void svm_enable_lbrv(struct kvm_vcpu *vcpu)
{
- struct vcpu_svm *svm = to_svm(vcpu);
-
- KVM_BUG_ON(sev_es_guest(vcpu->kvm), vcpu->kvm);
- svm->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK;
+ __svm_enable_lbrv(vcpu);
svm_recalc_lbr_msr_intercepts(vcpu);
-
- /*
- * Move the LBR msrs back to the vmcb01 to avoid copying them
- * on nested guest entries.
- */
- if (is_guest_mode(vcpu))
- svm_copy_lbrs(svm->vmcb01.ptr, svm->vmcb);
}
-static struct vmcb *svm_get_lbr_vmcb(struct vcpu_svm *svm)
+static void __svm_disable_lbrv(struct kvm_vcpu *vcpu)
{
- /*
- * If LBR virtualization is disabled, the LBR MSRs are always kept in
- * vmcb01. If LBR virtualization is enabled and L1 is running VMs of
- * its own, the MSRs are moved between vmcb01 and vmcb02 as needed.
- */
- return svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK ? svm->vmcb :
- svm->vmcb01.ptr;
+ KVM_BUG_ON(sev_es_guest(vcpu->kvm), vcpu->kvm);
+ to_svm(vcpu)->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK;
}
void svm_update_lbrv(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
bool current_enable_lbrv = svm->vmcb->control.virt_ext & LBR_CTL_ENABLE_MASK;
- bool enable_lbrv = (svm_get_lbr_vmcb(svm)->save.dbgctl & DEBUGCTLMSR_LBR) ||
+ bool enable_lbrv = (svm->vmcb->save.dbgctl & DEBUGCTLMSR_LBR) ||
(is_guest_mode(vcpu) && guest_cpu_cap_has(vcpu, X86_FEATURE_LBRV) &&
(svm->nested.ctl.virt_ext & LBR_CTL_ENABLE_MASK));
- if (enable_lbrv == current_enable_lbrv)
- return;
+ if (enable_lbrv && !current_enable_lbrv)
+ __svm_enable_lbrv(vcpu);
+ else if (!enable_lbrv && current_enable_lbrv)
+ __svm_disable_lbrv(vcpu);
- if (enable_lbrv)
- svm_enable_lbrv(vcpu);
- else
- svm_disable_lbrv(vcpu);
+ /*
+ * During nested transitions, it is possible that the current VMCB has
+ * LBR_CTL set, but the previous LBR_CTL had it cleared (or vice versa).
+ * In this case, even though LBR_CTL does not need an update, intercepts
+ * do, so always recalculate the intercepts here.
+ */
+ svm_recalc_lbr_msr_intercepts(vcpu);
}
void disable_nmi_singlestep(struct vcpu_svm *svm)
@@ -921,6 +910,8 @@ static void svm_hardware_unsetup(void)
{
int cpu;
+ avic_hardware_unsetup();
+
sev_hardware_unsetup();
for_each_possible_cpu(cpu)
@@ -1236,6 +1227,7 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
}
svm->x2avic_msrs_intercepted = true;
+ svm->lbr_msrs_intercepted = true;
svm->vmcb01.ptr = page_address(vmcb01_page);
svm->vmcb01.pa = __sme_set(page_to_pfn(vmcb01_page) << PAGE_SHIFT);
@@ -2722,19 +2714,19 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
msr_info->data = svm->tsc_aux;
break;
case MSR_IA32_DEBUGCTLMSR:
- msr_info->data = svm_get_lbr_vmcb(svm)->save.dbgctl;
+ msr_info->data = svm->vmcb->save.dbgctl;
break;
case MSR_IA32_LASTBRANCHFROMIP:
- msr_info->data = svm_get_lbr_vmcb(svm)->save.br_from;
+ msr_info->data = svm->vmcb->save.br_from;
break;
case MSR_IA32_LASTBRANCHTOIP:
- msr_info->data = svm_get_lbr_vmcb(svm)->save.br_to;
+ msr_info->data = svm->vmcb->save.br_to;
break;
case MSR_IA32_LASTINTFROMIP:
- msr_info->data = svm_get_lbr_vmcb(svm)->save.last_excp_from;
+ msr_info->data = svm->vmcb->save.last_excp_from;
break;
case MSR_IA32_LASTINTTOIP:
- msr_info->data = svm_get_lbr_vmcb(svm)->save.last_excp_to;
+ msr_info->data = svm->vmcb->save.last_excp_to;
break;
case MSR_VM_HSAVE_PA:
msr_info->data = svm->nested.hsave_msr;
@@ -3002,7 +2994,11 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr)
if (data & DEBUGCTL_RESERVED_BITS)
return 1;
- svm_get_lbr_vmcb(svm)->save.dbgctl = data;
+ if (svm->vmcb->save.dbgctl == data)
+ break;
+
+ svm->vmcb->save.dbgctl = data;
+ vmcb_mark_dirty(svm->vmcb, VMCB_LBR);
svm_update_lbrv(vcpu);
break;
case MSR_VM_HSAVE_PA:
@@ -5386,12 +5382,6 @@ static __init int svm_hardware_setup(void)
svm_hv_hardware_setup();
- for_each_possible_cpu(cpu) {
- r = svm_cpu_init(cpu);
- if (r)
- goto err;
- }
-
enable_apicv = avic_hardware_setup();
if (!enable_apicv) {
enable_ipiv = false;
@@ -5435,6 +5425,13 @@ static __init int svm_hardware_setup(void)
svm_set_cpu_caps();
kvm_caps.inapplicable_quirks &= ~KVM_X86_QUIRK_CD_NW_CLEARED;
+
+ for_each_possible_cpu(cpu) {
+ r = svm_cpu_init(cpu);
+ if (r)
+ goto err;
+ }
+
return 0;
err:
diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h
index e4b04f435b3d..dd78e6402345 100644
--- a/arch/x86/kvm/svm/svm.h
+++ b/arch/x86/kvm/svm/svm.h
@@ -329,13 +329,14 @@ struct vcpu_svm {
* back into remapped mode).
*/
struct list_head ir_list;
- spinlock_t ir_list_lock;
+ raw_spinlock_t ir_list_lock;
struct vcpu_sev_es_state sev_es;
bool guest_state_loaded;
bool x2avic_msrs_intercepted;
+ bool lbr_msrs_intercepted;
/* Guest GIF value, used when vGIF is not enabled */
bool guest_gif;
@@ -805,7 +806,7 @@ extern struct kvm_x86_nested_ops svm_nested_ops;
)
bool __init avic_hardware_setup(void);
-int avic_ga_log_notifier(u32 ga_tag);
+void avic_hardware_unsetup(void);
void avic_vm_destroy(struct kvm *kvm);
int avic_vm_init(struct kvm *kvm);
void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb);
diff --git a/arch/x86/kvm/vmx/common.h b/arch/x86/kvm/vmx/common.h
index bc5ece76533a..412d0829d7a2 100644
--- a/arch/x86/kvm/vmx/common.h
+++ b/arch/x86/kvm/vmx/common.h
@@ -98,7 +98,7 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa,
error_code |= (exit_qualification & EPT_VIOLATION_PROT_MASK)
? PFERR_PRESENT_MASK : 0;
- if (error_code & EPT_VIOLATION_GVA_IS_VALID)
+ if (exit_qualification & EPT_VIOLATION_GVA_IS_VALID)
error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) ?
PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK;
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 76271962cb70..bcea087b642f 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -6728,6 +6728,14 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu,
case EXIT_REASON_NOTIFY:
/* Notify VM exit is not exposed to L1 */
return false;
+ case EXIT_REASON_SEAMCALL:
+ case EXIT_REASON_TDCALL:
+ /*
+ * SEAMCALL and TDCALL unconditionally VM-Exit, but aren't
+ * virtualized by KVM for L1 hypervisors, i.e. L1 should
+ * never want or expect such an exit.
+ */
+ return false;
default:
return true;
}
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index f87c216d976d..91b6f2f3edc2 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -6032,6 +6032,12 @@ static int handle_vmx_instruction(struct kvm_vcpu *vcpu)
return 1;
}
+static int handle_tdx_instruction(struct kvm_vcpu *vcpu)
+{
+ kvm_queue_exception(vcpu, UD_VECTOR);
+ return 1;
+}
+
#ifndef CONFIG_X86_SGX_KVM
static int handle_encls(struct kvm_vcpu *vcpu)
{
@@ -6157,6 +6163,8 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = {
[EXIT_REASON_ENCLS] = handle_encls,
[EXIT_REASON_BUS_LOCK] = handle_bus_lock_vmexit,
[EXIT_REASON_NOTIFY] = handle_notify,
+ [EXIT_REASON_SEAMCALL] = handle_tdx_instruction,
+ [EXIT_REASON_TDCALL] = handle_tdx_instruction,
[EXIT_REASON_MSR_READ_IMM] = handle_rdmsr_imm,
[EXIT_REASON_MSR_WRITE_IMM] = handle_wrmsr_imm,
};
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index b4b5d2d09634..c9c2aa6f4705 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3874,15 +3874,9 @@ static void record_steal_time(struct kvm_vcpu *vcpu)
/*
* Returns true if the MSR in question is managed via XSTATE, i.e. is context
- * switched with the rest of guest FPU state. Note! S_CET is _not_ context
- * switched via XSTATE even though it _is_ saved/restored via XSAVES/XRSTORS.
- * Because S_CET is loaded on VM-Enter and VM-Exit via dedicated VMCS fields,
- * the value saved/restored via XSTATE is always the host's value. That detail
- * is _extremely_ important, as the guest's S_CET must _never_ be resident in
- * hardware while executing in the host. Loading guest values for U_CET and
- * PL[0-3]_SSP while executing in the kernel is safe, as U_CET is specific to
- * userspace, and PL[0-3]_SSP are only consumed when transitioning to lower
- * privilege levels, i.e. are effectively only consumed by userspace as well.
+ * switched with the rest of guest FPU state.
+ *
+ * Note, S_CET is _not_ saved/restored via XSAVES/XRSTORS.
*/
static bool is_xstate_managed_msr(struct kvm_vcpu *vcpu, u32 msr)
{
@@ -3905,6 +3899,11 @@ static bool is_xstate_managed_msr(struct kvm_vcpu *vcpu, u32 msr)
* MSR that is managed via XSTATE. Note, the caller is responsible for doing
* the initial FPU load, this helper only ensures that guest state is resident
* in hardware (the kernel can load its FPU state in IRQ context).
+ *
+ * Note, loading guest values for U_CET and PL[0-3]_SSP while executing in the
+ * kernel is safe, as U_CET is specific to userspace, and PL[0-3]_SSP are only
+ * consumed when transitioning to lower privilege levels, i.e. are effectively
+ * only consumed by userspace as well.
*/
static __always_inline void kvm_access_xstate_msr(struct kvm_vcpu *vcpu,
struct msr_data *msr_info,
@@ -11807,6 +11806,9 @@ static int complete_emulated_mmio(struct kvm_vcpu *vcpu)
/* Swap (qemu) user FPU context for the guest FPU context. */
static void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
{
+ if (KVM_BUG_ON(vcpu->arch.guest_fpu.fpstate->in_use, vcpu->kvm))
+ return;
+
/* Exclude PKRU, it's restored separately immediately after VM-Exit. */
fpu_swap_kvm_fpstate(&vcpu->arch.guest_fpu, true);
trace_kvm_fpu(1);
@@ -11815,6 +11817,9 @@ static void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
/* When vcpu_run ends, restore user space FPU context. */
static void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
{
+ if (KVM_BUG_ON(!vcpu->arch.guest_fpu.fpstate->in_use, vcpu->kvm))
+ return;
+
fpu_swap_kvm_fpstate(&vcpu->arch.guest_fpu, false);
++vcpu->stat.fpu_reload;
trace_kvm_fpu(0);
@@ -12137,9 +12142,6 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
int r;
vcpu_load(vcpu);
- if (kvm_mpx_supported())
- kvm_load_guest_fpu(vcpu);
-
kvm_vcpu_srcu_read_lock(vcpu);
r = kvm_apic_accept_events(vcpu);
@@ -12156,9 +12158,6 @@ int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
out:
kvm_vcpu_srcu_read_unlock(vcpu);
-
- if (kvm_mpx_supported())
- kvm_put_guest_fpu(vcpu);
vcpu_put(vcpu);
return r;
}
@@ -12788,6 +12787,7 @@ static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event)
{
struct fpstate *fpstate = vcpu->arch.guest_fpu.fpstate;
u64 xfeatures_mask;
+ bool fpu_in_use;
int i;
/*
@@ -12811,13 +12811,23 @@ static void kvm_xstate_reset(struct kvm_vcpu *vcpu, bool init_event)
BUILD_BUG_ON(sizeof(xfeatures_mask) * BITS_PER_BYTE <= XFEATURE_MAX);
/*
- * All paths that lead to INIT are required to load the guest's FPU
- * state (because most paths are buried in KVM_RUN).
- */
- kvm_put_guest_fpu(vcpu);
+ * Unload guest FPU state (if necessary) before zeroing XSTATE fields
+ * as the kernel can only modify the state when its resident in memory,
+ * i.e. when it's not loaded into hardware.
+ *
+ * WARN if the vCPU's desire to run, i.e. whether or not its in KVM_RUN,
+ * doesn't match the loaded/in-use state of the FPU, as KVM_RUN is the
+ * only path that can trigger INIT emulation _and_ loads FPU state, and
+ * KVM_RUN should _always_ load FPU state.
+ */
+ WARN_ON_ONCE(vcpu->wants_to_run != fpstate->in_use);
+ fpu_in_use = fpstate->in_use;
+ if (fpu_in_use)
+ kvm_put_guest_fpu(vcpu);
for_each_set_bit(i, (unsigned long *)&xfeatures_mask, XFEATURE_MAX)
fpstate_clear_xstate_component(fpstate, i);
- kvm_load_guest_fpu(vcpu);
+ if (fpu_in_use)
+ kvm_load_guest_fpu(vcpu);
}
void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event)
diff --git a/arch/x86/lib/cache-smp.c b/arch/x86/lib/cache-smp.c
index c5c60d07308c..824664c0ecbd 100644
--- a/arch/x86/lib/cache-smp.c
+++ b/arch/x86/lib/cache-smp.c
@@ -2,6 +2,7 @@
#include <asm/paravirt.h>
#include <linux/smp.h>
#include <linux/export.h>
+#include <linux/kvm_types.h>
static void __wbinvd(void *dummy)
{
@@ -12,7 +13,7 @@ void wbinvd_on_cpu(int cpu)
{
smp_call_function_single(cpu, __wbinvd, NULL, 1);
}
-EXPORT_SYMBOL(wbinvd_on_cpu);
+EXPORT_SYMBOL_FOR_KVM(wbinvd_on_cpu);
void wbinvd_on_all_cpus(void)
{
@@ -24,7 +25,7 @@ void wbinvd_on_cpus_mask(struct cpumask *cpus)
{
on_each_cpu_mask(cpus, __wbinvd, NULL, 1);
}
-EXPORT_SYMBOL_GPL(wbinvd_on_cpus_mask);
+EXPORT_SYMBOL_FOR_KVM(wbinvd_on_cpus_mask);
static void __wbnoinvd(void *dummy)
{
@@ -35,10 +36,10 @@ void wbnoinvd_on_all_cpus(void)
{
on_each_cpu(__wbnoinvd, NULL, 1);
}
-EXPORT_SYMBOL_GPL(wbnoinvd_on_all_cpus);
+EXPORT_SYMBOL_FOR_KVM(wbnoinvd_on_all_cpus);
void wbnoinvd_on_cpus_mask(struct cpumask *cpus)
{
on_each_cpu_mask(cpus, __wbnoinvd, NULL, 1);
}
-EXPORT_SYMBOL_GPL(wbnoinvd_on_cpus_mask);
+EXPORT_SYMBOL_FOR_KVM(wbnoinvd_on_cpus_mask);
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 4e385cbfd444..e03eeec55cfe 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -63,11 +63,10 @@ static bool is_string_insn(struct insn *insn)
bool insn_has_rep_prefix(struct insn *insn)
{
insn_byte_t p;
- int i;
insn_get_prefixes(insn);
- for_each_insn_prefix(insn, i, p) {
+ for_each_insn_prefix(insn, p) {
if (p == 0xf2 || p == 0xf3)
return true;
}
@@ -92,13 +91,13 @@ bool insn_has_rep_prefix(struct insn *insn)
static int get_seg_reg_override_idx(struct insn *insn)
{
int idx = INAT_SEG_REG_DEFAULT;
- int num_overrides = 0, i;
+ int num_overrides = 0;
insn_byte_t p;
insn_get_prefixes(insn);
/* Look for any segment override prefixes. */
- for_each_insn_prefix(insn, i, p) {
+ for_each_insn_prefix(insn, p) {
insn_attr_t attr;
attr = inat_get_opcode_attribute(p);
@@ -1676,3 +1675,147 @@ enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
return type;
}
+
+/*
+ * Recognise typical NOP patterns for both 32bit and 64bit.
+ *
+ * Notably:
+ * - NOP, but not: REP NOP aka PAUSE
+ * - NOPL
+ * - MOV %reg, %reg
+ * - LEA 0(%reg),%reg
+ * - JMP +0
+ *
+ * Must not have false-positives; instructions identified as a NOP might be
+ * emulated as a NOP (uprobe) or Run Length Encoded in a larger NOP
+ * (alternatives).
+ *
+ * False-negatives are fine; need not be exhaustive.
+ */
+bool insn_is_nop(struct insn *insn)
+{
+ u8 b3 = 0, x3 = 0, r3 = 0;
+ u8 b4 = 0, x4 = 0, r4 = 0, m = 0;
+ u8 modrm, modrm_mod, modrm_reg, modrm_rm;
+ u8 sib = 0, sib_scale, sib_index, sib_base;
+ u8 nrex, rex;
+ u8 p, rep = 0;
+
+ if ((nrex = insn->rex_prefix.nbytes)) {
+ rex = insn->rex_prefix.bytes[nrex-1];
+
+ r3 = !!X86_REX_R(rex);
+ x3 = !!X86_REX_X(rex);
+ b3 = !!X86_REX_B(rex);
+ if (nrex > 1) {
+ r4 = !!X86_REX2_R(rex);
+ x4 = !!X86_REX2_X(rex);
+ b4 = !!X86_REX2_B(rex);
+ m = !!X86_REX2_M(rex);
+ }
+
+ } else if (insn->vex_prefix.nbytes) {
+ /*
+ * Ignore VEX encoded NOPs
+ */
+ return false;
+ }
+
+ if (insn->modrm.nbytes) {
+ modrm = insn->modrm.bytes[0];
+ modrm_mod = X86_MODRM_MOD(modrm);
+ modrm_reg = X86_MODRM_REG(modrm) + 8*r3 + 16*r4;
+ modrm_rm = X86_MODRM_RM(modrm) + 8*b3 + 16*b4;
+ modrm = 1;
+ }
+
+ if (insn->sib.nbytes) {
+ sib = insn->sib.bytes[0];
+ sib_scale = X86_SIB_SCALE(sib);
+ sib_index = X86_SIB_INDEX(sib) + 8*x3 + 16*x4;
+ sib_base = X86_SIB_BASE(sib) + 8*b3 + 16*b4;
+ sib = 1;
+
+ modrm_rm = sib_base;
+ }
+
+ for_each_insn_prefix(insn, p) {
+ if (p == 0xf3) /* REPE */
+ rep = 1;
+ }
+
+ /*
+ * Opcode map munging:
+ *
+ * REX2: 0 - single byte opcode
+ * 1 - 0f second byte opcode
+ */
+ switch (m) {
+ case 0: break;
+ case 1: insn->opcode.value <<= 8;
+ insn->opcode.value |= 0x0f;
+ break;
+ default:
+ return false;
+ }
+
+ switch (insn->opcode.bytes[0]) {
+ case 0x0f: /* 2nd byte */
+ break;
+
+ case 0x89: /* MOV */
+ if (modrm_mod != 3) /* register-direct */
+ return false;
+
+ /* native size */
+ if (insn->opnd_bytes != 4 * (1 + insn->x86_64))
+ return false;
+
+ return modrm_reg == modrm_rm; /* MOV %reg, %reg */
+
+ case 0x8d: /* LEA */
+ if (modrm_mod == 0 || modrm_mod == 3) /* register-indirect with disp */
+ return false;
+
+ /* native size */
+ if (insn->opnd_bytes != 4 * (1 + insn->x86_64))
+ return false;
+
+ if (insn->displacement.value != 0)
+ return false;
+
+ if (sib && (sib_scale != 0 || sib_index != 4)) /* (%reg, %eiz, 1) */
+ return false;
+
+ for_each_insn_prefix(insn, p) {
+ if (p != 0x3e) /* DS */
+ return false;
+ }
+
+ return modrm_reg == modrm_rm; /* LEA 0(%reg), %reg */
+
+ case 0x90: /* NOP */
+ if (b3 || b4) /* XCHG %r{8,16,24},%rax */
+ return false;
+
+ if (rep) /* REP NOP := PAUSE */
+ return false;
+
+ return true;
+
+ case 0xe9: /* JMP.d32 */
+ case 0xeb: /* JMP.d8 */
+ return insn->immediate.value == 0; /* JMP +0 */
+
+ default:
+ return false;
+ }
+
+ switch (insn->opcode.bytes[1]) {
+ case 0x1f:
+ return modrm_reg == 0; /* 0f 1f /0 -- NOPL */
+
+ default:
+ return false;
+ }
+}
diff --git a/arch/x86/lib/kaslr.c b/arch/x86/lib/kaslr.c
index b5893928d55c..8c7cd115b484 100644
--- a/arch/x86/lib/kaslr.c
+++ b/arch/x86/lib/kaslr.c
@@ -22,7 +22,7 @@
#include <asm/setup.h>
#define debug_putstr(v) early_printk("%s", v)
-#define has_cpuflag(f) boot_cpu_has(f)
+#define has_cpuflag(f) cpu_feature_enabled(f)
#define get_boot_seed() kaslr_offset()
#endif
diff --git a/arch/x86/lib/msr.c b/arch/x86/lib/msr.c
index 4ef7c6dcbea6..dfdd1da89f36 100644
--- a/arch/x86/lib/msr.c
+++ b/arch/x86/lib/msr.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/export.h>
+#include <linux/kvm_types.h>
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <asm/msr.h>
@@ -103,7 +104,7 @@ int msr_set_bit(u32 msr, u8 bit)
{
return __flip_bit(msr, bit, true);
}
-EXPORT_SYMBOL_GPL(msr_set_bit);
+EXPORT_SYMBOL_FOR_KVM(msr_set_bit);
/**
* msr_clear_bit - Clear @bit in a MSR @msr.
@@ -119,7 +120,7 @@ int msr_clear_bit(u32 msr, u8 bit)
{
return __flip_bit(msr, bit, false);
}
-EXPORT_SYMBOL_GPL(msr_clear_bit);
+EXPORT_SYMBOL_FOR_KVM(msr_clear_bit);
#ifdef CONFIG_TRACEPOINTS
void do_trace_write_msr(u32 msr, u64 val, int failed)
diff --git a/arch/x86/math-emu/poly.h b/arch/x86/math-emu/poly.h
index fc1c887ca073..654bfe4e29a0 100644
--- a/arch/x86/math-emu/poly.h
+++ b/arch/x86/math-emu/poly.h
@@ -39,7 +39,7 @@ asmlinkage void mul_Xsig_Xsig(Xsig *dest, const Xsig *mult);
asmlinkage void shr_Xsig(Xsig *, const int n);
asmlinkage int round_Xsig(Xsig *);
asmlinkage int norm_Xsig(Xsig *);
-asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, const Xsig *dest);
+asmlinkage void div_Xsig(Xsig *x1, const Xsig *x2, Xsig *dest);
/* Macro to extract the most significant 32 bits from a long long */
#define LL_MSW(x) (((unsigned long *)&x)[1])
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index a4700ef6eb64..2afa7a23340e 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -486,7 +486,6 @@ void ptdump_walk_pgd_level_debugfs(struct seq_file *m, struct mm_struct *mm,
#endif
ptdump_walk_pgd_level_core(m, mm, pgd, false, false);
}
-EXPORT_SYMBOL_GPL(ptdump_walk_pgd_level_debugfs);
void ptdump_walk_user_pgd_level_checkwx(void)
{
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 0e4270e20fad..1044aafd5d94 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -504,9 +504,6 @@ phys_pte_init(pte_t *pte_page, unsigned long paddr, unsigned long paddr_end,
continue;
}
- if (0)
- pr_info(" pte=%p addr=%lx pte=%016lx\n", pte, paddr,
- pfn_pte(paddr >> PAGE_SHIFT, PAGE_KERNEL).pte);
pages++;
set_pte_init(pte, pfn_pte(paddr >> PAGE_SHIFT, prot), init);
paddr_last = (paddr & PAGE_MASK) + PAGE_SIZE;
diff --git a/arch/x86/mm/pat/memtype.c b/arch/x86/mm/pat/memtype.c
index b68200a0e0c6..8a3d9722f602 100644
--- a/arch/x86/mm/pat/memtype.c
+++ b/arch/x86/mm/pat/memtype.c
@@ -42,6 +42,7 @@
#include <linux/highmem.h>
#include <linux/fs.h>
#include <linux/rbtree.h>
+#include <linux/kvm_types.h>
#include <asm/cpu_device_id.h>
#include <asm/cacheflush.h>
@@ -697,7 +698,7 @@ bool pat_pfn_immune_to_uc_mtrr(unsigned long pfn)
cm == _PAGE_CACHE_MODE_UC_MINUS ||
cm == _PAGE_CACHE_MODE_WC;
}
-EXPORT_SYMBOL_GPL(pat_pfn_immune_to_uc_mtrr);
+EXPORT_SYMBOL_FOR_KVM(pat_pfn_immune_to_uc_mtrr);
/**
* memtype_reserve_io - Request a memory type mapping for a region of memory
diff --git a/arch/x86/mm/physaddr.c b/arch/x86/mm/physaddr.c
index fc3f3d3e2ef2..8d31c6b9e184 100644
--- a/arch/x86/mm/physaddr.c
+++ b/arch/x86/mm/physaddr.c
@@ -31,17 +31,6 @@ unsigned long __phys_addr(unsigned long x)
return x;
}
EXPORT_SYMBOL(__phys_addr);
-
-unsigned long __phys_addr_symbol(unsigned long x)
-{
- unsigned long y = x - __START_KERNEL_map;
-
- /* only check upper bounds since lower bounds will trigger carry */
- VIRTUAL_BUG_ON(y >= KERNEL_IMAGE_SIZE);
-
- return y + phys_base;
-}
-EXPORT_SYMBOL(__phys_addr_symbol);
#endif
bool __virt_addr_valid(unsigned long x)
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 5d221709353e..f5b93e01e347 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -12,6 +12,7 @@
#include <linux/task_work.h>
#include <linux/mmu_notifier.h>
#include <linux/mmu_context.h>
+#include <linux/kvm_types.h>
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
@@ -1582,7 +1583,7 @@ unsigned long __get_current_cr3_fast(void)
VM_BUG_ON(cr3 != __read_cr3());
return cr3;
}
-EXPORT_SYMBOL_GPL(__get_current_cr3_fast);
+EXPORT_SYMBOL_FOR_KVM(__get_current_cr3_fast);
/*
* Flush one page in the kernel mapping
@@ -1723,7 +1724,7 @@ void __flush_tlb_all(void)
flush_tlb_local();
}
}
-EXPORT_SYMBOL_GPL(__flush_tlb_all);
+EXPORT_SYMBOL_FOR_KVM(__flush_tlb_all);
void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
{
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index d4c93d9e73e4..b69dc7194e2c 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -597,7 +597,8 @@ static int emit_jump(u8 **pprog, void *func, void *ip)
return emit_patch(pprog, func, ip, 0xE9);
}
-static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
+static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
+ enum bpf_text_poke_type new_t,
void *old_addr, void *new_addr)
{
const u8 *nop_insn = x86_nops[5];
@@ -607,9 +608,9 @@ static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
int ret;
memcpy(old_insn, nop_insn, X86_PATCH_SIZE);
- if (old_addr) {
+ if (old_t != BPF_MOD_NOP && old_addr) {
prog = old_insn;
- ret = t == BPF_MOD_CALL ?
+ ret = old_t == BPF_MOD_CALL ?
emit_call(&prog, old_addr, ip) :
emit_jump(&prog, old_addr, ip);
if (ret)
@@ -617,9 +618,9 @@ static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
}
memcpy(new_insn, nop_insn, X86_PATCH_SIZE);
- if (new_addr) {
+ if (new_t != BPF_MOD_NOP && new_addr) {
prog = new_insn;
- ret = t == BPF_MOD_CALL ?
+ ret = new_t == BPF_MOD_CALL ?
emit_call(&prog, new_addr, ip) :
emit_jump(&prog, new_addr, ip);
if (ret)
@@ -640,8 +641,9 @@ out:
return ret;
}
-int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
- void *old_addr, void *new_addr)
+int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,
+ enum bpf_text_poke_type new_t, void *old_addr,
+ void *new_addr)
{
if (!is_kernel_text((long)ip) &&
!is_bpf_text_address((long)ip))
@@ -655,29 +657,43 @@ int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type t,
if (is_endbr(ip))
ip += ENDBR_INSN_SIZE;
- return __bpf_arch_text_poke(ip, t, old_addr, new_addr);
+ return __bpf_arch_text_poke(ip, old_t, new_t, old_addr, new_addr);
}
#define EMIT_LFENCE() EMIT3(0x0F, 0xAE, 0xE8)
-static void emit_indirect_jump(u8 **pprog, int reg, u8 *ip)
+static void __emit_indirect_jump(u8 **pprog, int reg, bool ereg)
{
u8 *prog = *pprog;
+ if (ereg)
+ EMIT1(0x41);
+
+ EMIT2(0xFF, 0xE0 + reg);
+
+ *pprog = prog;
+}
+
+static void emit_indirect_jump(u8 **pprog, int bpf_reg, u8 *ip)
+{
+ u8 *prog = *pprog;
+ int reg = reg2hex[bpf_reg];
+ bool ereg = is_ereg(bpf_reg);
+
if (cpu_feature_enabled(X86_FEATURE_INDIRECT_THUNK_ITS)) {
OPTIMIZER_HIDE_VAR(reg);
- emit_jump(&prog, its_static_thunk(reg), ip);
+ emit_jump(&prog, its_static_thunk(reg + 8*ereg), ip);
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE_LFENCE)) {
EMIT_LFENCE();
- EMIT2(0xFF, 0xE0 + reg);
+ __emit_indirect_jump(&prog, reg, ereg);
} else if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) {
OPTIMIZER_HIDE_VAR(reg);
if (cpu_feature_enabled(X86_FEATURE_CALL_DEPTH))
- emit_jump(&prog, &__x86_indirect_jump_thunk_array[reg], ip);
+ emit_jump(&prog, &__x86_indirect_jump_thunk_array[reg + 8*ereg], ip);
else
- emit_jump(&prog, &__x86_indirect_thunk_array[reg], ip);
+ emit_jump(&prog, &__x86_indirect_thunk_array[reg + 8*ereg], ip);
} else {
- EMIT2(0xFF, 0xE0 + reg); /* jmp *%\reg */
+ __emit_indirect_jump(&prog, reg, ereg);
if (IS_ENABLED(CONFIG_MITIGATION_RETPOLINE) || IS_ENABLED(CONFIG_MITIGATION_SLS))
EMIT1(0xCC); /* int3 */
}
@@ -797,7 +813,7 @@ static void emit_bpf_tail_call_indirect(struct bpf_prog *bpf_prog,
* rdi == ctx (1st arg)
* rcx == prog->bpf_func + X86_TAIL_CALL_OFFSET
*/
- emit_indirect_jump(&prog, 1 /* rcx */, ip + (prog - start));
+ emit_indirect_jump(&prog, BPF_REG_4 /* R4 -> rcx */, ip + (prog - start));
/* out: */
ctx->tail_call_indirect_label = prog - start;
@@ -883,12 +899,13 @@ static void bpf_tail_call_direct_fixup(struct bpf_prog *prog)
target = array->ptrs[poke->tail_call.key];
if (target) {
ret = __bpf_arch_text_poke(poke->tailcall_target,
- BPF_MOD_JUMP, NULL,
+ BPF_MOD_NOP, BPF_MOD_JUMP,
+ NULL,
(u8 *)target->bpf_func +
poke->adj_off);
BUG_ON(ret < 0);
ret = __bpf_arch_text_poke(poke->tailcall_bypass,
- BPF_MOD_JUMP,
+ BPF_MOD_JUMP, BPF_MOD_NOP,
(u8 *)poke->tailcall_target +
X86_PATCH_SIZE, NULL);
BUG_ON(ret < 0);
@@ -2614,6 +2631,9 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
break;
+ case BPF_JMP | BPF_JA | BPF_X:
+ emit_indirect_jump(&prog, insn->dst_reg, image + addrs[i - 1]);
+ break;
case BPF_JMP | BPF_JA:
case BPF_JMP32 | BPF_JA:
if (BPF_CLASS(insn->code) == BPF_JMP) {
@@ -2701,7 +2721,7 @@ emit_jmp:
/* Update cleanup_addr */
ctx->cleanup_addr = proglen;
if (bpf_prog_was_classic(bpf_prog) &&
- !capable(CAP_SYS_ADMIN)) {
+ !ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN)) {
u8 *ip = image + addrs[i - 1];
if (emit_spectre_bhb_barrier(&prog, ip, bpf_prog))
@@ -2830,9 +2850,10 @@ static int get_nr_used_regs(const struct btf_func_model *m)
}
static void save_args(const struct btf_func_model *m, u8 **prog,
- int stack_size, bool for_call_origin)
+ int stack_size, bool for_call_origin, u32 flags)
{
int arg_regs, first_off = 0, nr_regs = 0, nr_stack_slots = 0;
+ bool use_jmp = bpf_trampoline_use_jmp(flags);
int i, j;
/* Store function arguments to stack.
@@ -2873,7 +2894,7 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
*/
for (j = 0; j < arg_regs; j++) {
emit_ldx(prog, BPF_DW, BPF_REG_0, BPF_REG_FP,
- nr_stack_slots * 8 + 0x18);
+ nr_stack_slots * 8 + 16 + (!use_jmp) * 8);
emit_stx(prog, BPF_DW, BPF_REG_FP, BPF_REG_0,
-stack_size);
@@ -3267,12 +3288,17 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
* should be 16-byte aligned. Following code depend on
* that stack_size is already 8-byte aligned.
*/
- stack_size += (stack_size % 16) ? 0 : 8;
+ if (bpf_trampoline_use_jmp(flags)) {
+ /* no rip in the "jmp" case */
+ stack_size += (stack_size % 16) ? 8 : 0;
+ } else {
+ stack_size += (stack_size % 16) ? 0 : 8;
+ }
}
arg_stack_off = stack_size;
- if (flags & BPF_TRAMP_F_SKIP_FRAME) {
+ if (flags & BPF_TRAMP_F_CALL_ORIG) {
/* skip patched call instruction and point orig_call to actual
* body of the kernel function.
*/
@@ -3327,7 +3353,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -ip_off);
}
- save_args(m, &prog, regs_off, false);
+ save_args(m, &prog, regs_off, false, flags);
if (flags & BPF_TRAMP_F_CALL_ORIG) {
/* arg1: mov rdi, im */
@@ -3360,7 +3386,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
if (flags & BPF_TRAMP_F_CALL_ORIG) {
restore_regs(m, &prog, regs_off);
- save_args(m, &prog, arg_stack_off, true);
+ save_args(m, &prog, arg_stack_off, true, flags);
if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) {
/* Before calling the original function, load the
@@ -3543,7 +3569,7 @@ static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs, u8 *image,
if (err)
return err;
- emit_indirect_jump(&prog, 2 /* rdx */, image + (prog - buf));
+ emit_indirect_jump(&prog, BPF_REG_3 /* R3 -> rdx */, image + (prog - buf));
*pprog = prog;
return 0;
@@ -3827,6 +3853,15 @@ out_image:
jit_data->header = header;
jit_data->rw_header = rw_header;
}
+
+ /*
+ * The bpf_prog_update_insn_ptrs function expects addrs to
+ * point to the first byte of the jitted instruction (unlike
+ * the bpf_prog_fill_jited_linfo below, which, for historical
+ * reasons, expects to point to the next instruction)
+ */
+ bpf_prog_update_insn_ptrs(prog, addrs, image);
+
/*
* ctx.prog_offset is used when CFI preambles put code *before*
* the function. See emit_cfi(). For FineIBT specifically this code
@@ -3953,6 +3988,7 @@ void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke,
struct bpf_prog *new, struct bpf_prog *old)
{
u8 *old_addr, *new_addr, *old_bypass_addr;
+ enum bpf_text_poke_type t;
int ret;
old_bypass_addr = old ? NULL : poke->bypass_addr;
@@ -3965,21 +4001,22 @@ void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke,
* the kallsyms check.
*/
if (new) {
+ t = old_addr ? BPF_MOD_JUMP : BPF_MOD_NOP;
ret = __bpf_arch_text_poke(poke->tailcall_target,
- BPF_MOD_JUMP,
+ t, BPF_MOD_JUMP,
old_addr, new_addr);
BUG_ON(ret < 0);
if (!old) {
ret = __bpf_arch_text_poke(poke->tailcall_bypass,
- BPF_MOD_JUMP,
+ BPF_MOD_JUMP, BPF_MOD_NOP,
poke->bypass_addr,
NULL);
BUG_ON(ret < 0);
}
} else {
+ t = old_bypass_addr ? BPF_MOD_JUMP : BPF_MOD_NOP;
ret = __bpf_arch_text_poke(poke->tailcall_bypass,
- BPF_MOD_JUMP,
- old_bypass_addr,
+ t, BPF_MOD_JUMP, old_bypass_addr,
poke->bypass_addr);
BUG_ON(ret < 0);
/* let other CPUs finish the execution of program
@@ -3988,9 +4025,9 @@ void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke,
*/
if (!ret)
synchronize_rcu();
+ t = old_addr ? BPF_MOD_JUMP : BPF_MOD_NOP;
ret = __bpf_arch_text_poke(poke->tailcall_target,
- BPF_MOD_JUMP,
- old_addr, NULL);
+ t, BPF_MOD_NOP, old_addr, NULL);
BUG_ON(ret < 0);
}
}
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index eac403248462..5ce4ebe99774 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -29,6 +29,7 @@
#include <linux/acpi.h>
#include <linux/suspend.h>
#include <linux/idr.h>
+#include <linux/kvm_types.h>
#include <asm/page.h>
#include <asm/special_insns.h>
#include <asm/msr-index.h>
@@ -181,7 +182,7 @@ int tdx_cpu_enable(void)
return 0;
}
-EXPORT_SYMBOL_GPL(tdx_cpu_enable);
+EXPORT_SYMBOL_FOR_KVM(tdx_cpu_enable);
/*
* Add a memory region as a TDX memory block. The caller must make sure
@@ -662,7 +663,7 @@ void tdx_quirk_reset_page(struct page *page)
{
tdx_quirk_reset_paddr(page_to_phys(page), PAGE_SIZE);
}
-EXPORT_SYMBOL_GPL(tdx_quirk_reset_page);
+EXPORT_SYMBOL_FOR_KVM(tdx_quirk_reset_page);
static void tdmr_quirk_reset_pamt(struct tdmr_info *tdmr)
{
@@ -1216,7 +1217,7 @@ int tdx_enable(void)
return ret;
}
-EXPORT_SYMBOL_GPL(tdx_enable);
+EXPORT_SYMBOL_FOR_KVM(tdx_enable);
static bool is_pamt_page(unsigned long phys)
{
@@ -1477,13 +1478,13 @@ const struct tdx_sys_info *tdx_get_sysinfo(void)
return p;
}
-EXPORT_SYMBOL_GPL(tdx_get_sysinfo);
+EXPORT_SYMBOL_FOR_KVM(tdx_get_sysinfo);
u32 tdx_get_nr_guest_keyids(void)
{
return tdx_nr_guest_keyids;
}
-EXPORT_SYMBOL_GPL(tdx_get_nr_guest_keyids);
+EXPORT_SYMBOL_FOR_KVM(tdx_get_nr_guest_keyids);
int tdx_guest_keyid_alloc(void)
{
@@ -1491,13 +1492,13 @@ int tdx_guest_keyid_alloc(void)
tdx_guest_keyid_start + tdx_nr_guest_keyids - 1,
GFP_KERNEL);
}
-EXPORT_SYMBOL_GPL(tdx_guest_keyid_alloc);
+EXPORT_SYMBOL_FOR_KVM(tdx_guest_keyid_alloc);
void tdx_guest_keyid_free(unsigned int keyid)
{
ida_free(&tdx_guest_keyid_pool, keyid);
}
-EXPORT_SYMBOL_GPL(tdx_guest_keyid_free);
+EXPORT_SYMBOL_FOR_KVM(tdx_guest_keyid_free);
static inline u64 tdx_tdr_pa(struct tdx_td *td)
{
@@ -1521,7 +1522,7 @@ noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args)
return __seamcall_dirty_cache(__seamcall_saved_ret, TDH_VP_ENTER, args);
}
-EXPORT_SYMBOL_GPL(tdh_vp_enter);
+EXPORT_SYMBOL_FOR_KVM(tdh_vp_enter);
u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page)
{
@@ -1533,7 +1534,7 @@ u64 tdh_mng_addcx(struct tdx_td *td, struct page *tdcs_page)
tdx_clflush_page(tdcs_page);
return seamcall(TDH_MNG_ADDCX, &args);
}
-EXPORT_SYMBOL_GPL(tdh_mng_addcx);
+EXPORT_SYMBOL_FOR_KVM(tdh_mng_addcx);
u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page *source, u64 *ext_err1, u64 *ext_err2)
{
@@ -1553,7 +1554,7 @@ u64 tdh_mem_page_add(struct tdx_td *td, u64 gpa, struct page *page, struct page
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mem_page_add);
+EXPORT_SYMBOL_FOR_KVM(tdh_mem_page_add);
u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2)
{
@@ -1572,7 +1573,7 @@ u64 tdh_mem_sept_add(struct tdx_td *td, u64 gpa, int level, struct page *page, u
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mem_sept_add);
+EXPORT_SYMBOL_FOR_KVM(tdh_mem_sept_add);
u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page)
{
@@ -1584,7 +1585,7 @@ u64 tdh_vp_addcx(struct tdx_vp *vp, struct page *tdcx_page)
tdx_clflush_page(tdcx_page);
return seamcall(TDH_VP_ADDCX, &args);
}
-EXPORT_SYMBOL_GPL(tdh_vp_addcx);
+EXPORT_SYMBOL_FOR_KVM(tdh_vp_addcx);
u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u64 *ext_err1, u64 *ext_err2)
{
@@ -1603,7 +1604,7 @@ u64 tdh_mem_page_aug(struct tdx_td *td, u64 gpa, int level, struct page *page, u
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mem_page_aug);
+EXPORT_SYMBOL_FOR_KVM(tdh_mem_page_aug);
u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, int level, u64 *ext_err1, u64 *ext_err2)
{
@@ -1620,7 +1621,7 @@ u64 tdh_mem_range_block(struct tdx_td *td, u64 gpa, int level, u64 *ext_err1, u6
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mem_range_block);
+EXPORT_SYMBOL_FOR_KVM(tdh_mem_range_block);
u64 tdh_mng_key_config(struct tdx_td *td)
{
@@ -1630,7 +1631,7 @@ u64 tdh_mng_key_config(struct tdx_td *td)
return seamcall(TDH_MNG_KEY_CONFIG, &args);
}
-EXPORT_SYMBOL_GPL(tdh_mng_key_config);
+EXPORT_SYMBOL_FOR_KVM(tdh_mng_key_config);
u64 tdh_mng_create(struct tdx_td *td, u16 hkid)
{
@@ -1642,7 +1643,7 @@ u64 tdh_mng_create(struct tdx_td *td, u16 hkid)
tdx_clflush_page(td->tdr_page);
return seamcall(TDH_MNG_CREATE, &args);
}
-EXPORT_SYMBOL_GPL(tdh_mng_create);
+EXPORT_SYMBOL_FOR_KVM(tdh_mng_create);
u64 tdh_vp_create(struct tdx_td *td, struct tdx_vp *vp)
{
@@ -1654,7 +1655,7 @@ u64 tdh_vp_create(struct tdx_td *td, struct tdx_vp *vp)
tdx_clflush_page(vp->tdvpr_page);
return seamcall(TDH_VP_CREATE, &args);
}
-EXPORT_SYMBOL_GPL(tdh_vp_create);
+EXPORT_SYMBOL_FOR_KVM(tdh_vp_create);
u64 tdh_mng_rd(struct tdx_td *td, u64 field, u64 *data)
{
@@ -1671,7 +1672,7 @@ u64 tdh_mng_rd(struct tdx_td *td, u64 field, u64 *data)
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mng_rd);
+EXPORT_SYMBOL_FOR_KVM(tdh_mng_rd);
u64 tdh_mr_extend(struct tdx_td *td, u64 gpa, u64 *ext_err1, u64 *ext_err2)
{
@@ -1688,7 +1689,7 @@ u64 tdh_mr_extend(struct tdx_td *td, u64 gpa, u64 *ext_err1, u64 *ext_err2)
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mr_extend);
+EXPORT_SYMBOL_FOR_KVM(tdh_mr_extend);
u64 tdh_mr_finalize(struct tdx_td *td)
{
@@ -1698,7 +1699,7 @@ u64 tdh_mr_finalize(struct tdx_td *td)
return seamcall(TDH_MR_FINALIZE, &args);
}
-EXPORT_SYMBOL_GPL(tdh_mr_finalize);
+EXPORT_SYMBOL_FOR_KVM(tdh_mr_finalize);
u64 tdh_vp_flush(struct tdx_vp *vp)
{
@@ -1708,7 +1709,7 @@ u64 tdh_vp_flush(struct tdx_vp *vp)
return seamcall(TDH_VP_FLUSH, &args);
}
-EXPORT_SYMBOL_GPL(tdh_vp_flush);
+EXPORT_SYMBOL_FOR_KVM(tdh_vp_flush);
u64 tdh_mng_vpflushdone(struct tdx_td *td)
{
@@ -1718,7 +1719,7 @@ u64 tdh_mng_vpflushdone(struct tdx_td *td)
return seamcall(TDH_MNG_VPFLUSHDONE, &args);
}
-EXPORT_SYMBOL_GPL(tdh_mng_vpflushdone);
+EXPORT_SYMBOL_FOR_KVM(tdh_mng_vpflushdone);
u64 tdh_mng_key_freeid(struct tdx_td *td)
{
@@ -1728,7 +1729,7 @@ u64 tdh_mng_key_freeid(struct tdx_td *td)
return seamcall(TDH_MNG_KEY_FREEID, &args);
}
-EXPORT_SYMBOL_GPL(tdh_mng_key_freeid);
+EXPORT_SYMBOL_FOR_KVM(tdh_mng_key_freeid);
u64 tdh_mng_init(struct tdx_td *td, u64 td_params, u64 *extended_err)
{
@@ -1744,7 +1745,7 @@ u64 tdh_mng_init(struct tdx_td *td, u64 td_params, u64 *extended_err)
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mng_init);
+EXPORT_SYMBOL_FOR_KVM(tdh_mng_init);
u64 tdh_vp_rd(struct tdx_vp *vp, u64 field, u64 *data)
{
@@ -1761,7 +1762,7 @@ u64 tdh_vp_rd(struct tdx_vp *vp, u64 field, u64 *data)
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_vp_rd);
+EXPORT_SYMBOL_FOR_KVM(tdh_vp_rd);
u64 tdh_vp_wr(struct tdx_vp *vp, u64 field, u64 data, u64 mask)
{
@@ -1774,7 +1775,7 @@ u64 tdh_vp_wr(struct tdx_vp *vp, u64 field, u64 data, u64 mask)
return seamcall(TDH_VP_WR, &args);
}
-EXPORT_SYMBOL_GPL(tdh_vp_wr);
+EXPORT_SYMBOL_FOR_KVM(tdh_vp_wr);
u64 tdh_vp_init(struct tdx_vp *vp, u64 initial_rcx, u32 x2apicid)
{
@@ -1787,7 +1788,7 @@ u64 tdh_vp_init(struct tdx_vp *vp, u64 initial_rcx, u32 x2apicid)
/* apicid requires version == 1. */
return seamcall(TDH_VP_INIT | (1ULL << TDX_VERSION_SHIFT), &args);
}
-EXPORT_SYMBOL_GPL(tdh_vp_init);
+EXPORT_SYMBOL_FOR_KVM(tdh_vp_init);
/*
* TDX ABI defines output operands as PT, OWNER and SIZE. These are TDX defined fomats.
@@ -1809,7 +1810,7 @@ u64 tdh_phymem_page_reclaim(struct page *page, u64 *tdx_pt, u64 *tdx_owner, u64
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_phymem_page_reclaim);
+EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_reclaim);
u64 tdh_mem_track(struct tdx_td *td)
{
@@ -1819,7 +1820,7 @@ u64 tdh_mem_track(struct tdx_td *td)
return seamcall(TDH_MEM_TRACK, &args);
}
-EXPORT_SYMBOL_GPL(tdh_mem_track);
+EXPORT_SYMBOL_FOR_KVM(tdh_mem_track);
u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, u64 level, u64 *ext_err1, u64 *ext_err2)
{
@@ -1836,7 +1837,7 @@ u64 tdh_mem_page_remove(struct tdx_td *td, u64 gpa, u64 level, u64 *ext_err1, u6
return ret;
}
-EXPORT_SYMBOL_GPL(tdh_mem_page_remove);
+EXPORT_SYMBOL_FOR_KVM(tdh_mem_page_remove);
u64 tdh_phymem_cache_wb(bool resume)
{
@@ -1846,7 +1847,7 @@ u64 tdh_phymem_cache_wb(bool resume)
return seamcall(TDH_PHYMEM_CACHE_WB, &args);
}
-EXPORT_SYMBOL_GPL(tdh_phymem_cache_wb);
+EXPORT_SYMBOL_FOR_KVM(tdh_phymem_cache_wb);
u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td)
{
@@ -1856,7 +1857,7 @@ u64 tdh_phymem_page_wbinvd_tdr(struct tdx_td *td)
return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args);
}
-EXPORT_SYMBOL_GPL(tdh_phymem_page_wbinvd_tdr);
+EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_wbinvd_tdr);
u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page)
{
@@ -1866,7 +1867,7 @@ u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page)
return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args);
}
-EXPORT_SYMBOL_GPL(tdh_phymem_page_wbinvd_hkid);
+EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_wbinvd_hkid);
#ifdef CONFIG_KEXEC_CORE
void tdx_cpu_flush_cache_for_kexec(void)
@@ -1884,5 +1885,5 @@ void tdx_cpu_flush_cache_for_kexec(void)
wbinvd();
this_cpu_write(cache_state_incoherent, false);
}
-EXPORT_SYMBOL_GPL(tdx_cpu_flush_cache_for_kexec);
+EXPORT_SYMBOL_FOR_KVM(tdx_cpu_flush_cache_for_kexec);
#endif
diff --git a/arch/xtensa/configs/audio_kc705_defconfig b/arch/xtensa/configs/audio_kc705_defconfig
index dc942bbac69f..4655d6bd7ee0 100644
--- a/arch/xtensa/configs/audio_kc705_defconfig
+++ b/arch/xtensa/configs/audio_kc705_defconfig
@@ -133,4 +133,3 @@ CONFIG_STACKTRACE=y
CONFIG_RCU_TRACE=y
# CONFIG_FTRACE is not set
# CONFIG_S32C1I_SELFTEST is not set
-CONFIG_CRYPTO_ANSI_CPRNG=y
diff --git a/arch/xtensa/configs/generic_kc705_defconfig b/arch/xtensa/configs/generic_kc705_defconfig
index 3ee7e1c56556..b3de2bd74d54 100644
--- a/arch/xtensa/configs/generic_kc705_defconfig
+++ b/arch/xtensa/configs/generic_kc705_defconfig
@@ -121,4 +121,3 @@ CONFIG_RCU_TRACE=y
# CONFIG_FTRACE is not set
CONFIG_LD_NO_RELAX=y
# CONFIG_S32C1I_SELFTEST is not set
-CONFIG_CRYPTO_ANSI_CPRNG=y
diff --git a/arch/xtensa/configs/iss_defconfig b/arch/xtensa/configs/iss_defconfig
index 32ce8fb068f0..324266824fae 100644
--- a/arch/xtensa/configs/iss_defconfig
+++ b/arch/xtensa/configs/iss_defconfig
@@ -28,4 +28,3 @@ CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y
# CONFIG_FRAME_POINTER is not set
CONFIG_DETECT_HUNG_TASK=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
diff --git a/arch/xtensa/configs/nommu_kc705_defconfig b/arch/xtensa/configs/nommu_kc705_defconfig
index c6e96f0aa700..47dd80930e90 100644
--- a/arch/xtensa/configs/nommu_kc705_defconfig
+++ b/arch/xtensa/configs/nommu_kc705_defconfig
@@ -122,4 +122,3 @@ CONFIG_RCU_TRACE=y
# CONFIG_FTRACE is not set
# CONFIG_LD_NO_RELAX is not set
# CONFIG_CRYPTO_ECHAINIV is not set
-CONFIG_CRYPTO_ANSI_CPRNG=y
diff --git a/arch/xtensa/configs/smp_lx200_defconfig b/arch/xtensa/configs/smp_lx200_defconfig
index 373d42b9e510..c8c92c5b24d5 100644
--- a/arch/xtensa/configs/smp_lx200_defconfig
+++ b/arch/xtensa/configs/smp_lx200_defconfig
@@ -125,4 +125,3 @@ CONFIG_RCU_TRACE=y
# CONFIG_FTRACE is not set
CONFIG_LD_NO_RELAX=y
# CONFIG_S32C1I_SELFTEST is not set
-CONFIG_CRYPTO_ANSI_CPRNG=y
diff --git a/arch/xtensa/configs/virt_defconfig b/arch/xtensa/configs/virt_defconfig
index 72628d31e87a..982dd67ed174 100644
--- a/arch/xtensa/configs/virt_defconfig
+++ b/arch/xtensa/configs/virt_defconfig
@@ -92,7 +92,6 @@ CONFIG_NLS_ISO8859_1=y
CONFIG_CRYPTO_ECHAINIV=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_CRYPTO_DEV_VIRTIO=y
CONFIG_FONTS=y
CONFIG_PRINTK_TIME=y
diff --git a/arch/xtensa/configs/xip_kc705_defconfig b/arch/xtensa/configs/xip_kc705_defconfig
index 5d6013ea70fc..3e9486222a66 100644
--- a/arch/xtensa/configs/xip_kc705_defconfig
+++ b/arch/xtensa/configs/xip_kc705_defconfig
@@ -98,7 +98,6 @@ CONFIG_NLS_ISO8859_1=y
CONFIG_CRYPTO_ECHAINIV=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
-CONFIG_CRYPTO_ANSI_CPRNG=y
CONFIG_PRINTK_TIME=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl
index 374e4cb788d8..438a3b170402 100644
--- a/arch/xtensa/kernel/syscalls/syscall.tbl
+++ b/arch/xtensa/kernel/syscalls/syscall.tbl
@@ -440,3 +440,4 @@
467 common open_tree_attr sys_open_tree_attr
468 common file_getattr sys_file_getattr
469 common file_setattr sys_file_setattr
+470 common listns sys_listns
diff --git a/block/bdev.c b/block/bdev.c
index 810707cca970..b8fbb9576110 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -67,7 +67,7 @@ static void bdev_write_inode(struct block_device *bdev)
int ret;
spin_lock(&inode->i_lock);
- while (inode->i_state & I_DIRTY) {
+ while (inode_state_read(inode) & I_DIRTY) {
spin_unlock(&inode->i_lock);
ret = write_inode_now(inode, true);
if (ret)
@@ -217,9 +217,26 @@ int set_blocksize(struct file *file, int size)
EXPORT_SYMBOL(set_blocksize);
+static int sb_validate_large_blocksize(struct super_block *sb, int size)
+{
+ const char *err_str = NULL;
+
+ if (!(sb->s_type->fs_flags & FS_LBS))
+ err_str = "not supported by filesystem";
+ else if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE))
+ err_str = "is only supported with CONFIG_TRANSPARENT_HUGEPAGE";
+
+ if (!err_str)
+ return 0;
+
+ pr_warn_ratelimited("%s: block size(%d) > page size(%lu) %s\n",
+ sb->s_type->name, size, PAGE_SIZE, err_str);
+ return -EINVAL;
+}
+
int sb_set_blocksize(struct super_block *sb, int size)
{
- if (!(sb->s_type->fs_flags & FS_LBS) && size > PAGE_SIZE)
+ if (size > PAGE_SIZE && sb_validate_large_blocksize(sb, size))
return 0;
if (set_blocksize(sb->s_bdev_file, size))
return 0;
@@ -231,7 +248,7 @@ int sb_set_blocksize(struct super_block *sb, int size)
EXPORT_SYMBOL(sb_set_blocksize);
-int sb_min_blocksize(struct super_block *sb, int size)
+int __must_check sb_min_blocksize(struct super_block *sb, int size)
{
int minsize = bdev_logical_block_size(sb->s_bdev);
if (size < minsize)
@@ -1265,7 +1282,7 @@ void sync_bdevs(bool wait)
struct block_device *bdev;
spin_lock(&inode->i_lock);
- if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW) ||
+ if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW) ||
mapping->nrpages == 0) {
spin_unlock(&inode->i_lock);
continue;
diff --git a/block/bio-integrity-auto.c b/block/bio-integrity-auto.c
index 687952f63bbb..9850c338548d 100644
--- a/block/bio-integrity-auto.c
+++ b/block/bio-integrity-auto.c
@@ -29,7 +29,7 @@ static void bio_integrity_finish(struct bio_integrity_data *bid)
{
bid->bio->bi_integrity = NULL;
bid->bio->bi_opf &= ~REQ_INTEGRITY;
- kfree(bvec_virt(bid->bip.bip_vec));
+ bio_integrity_free_buf(&bid->bip);
mempool_free(bid, &bid_pool);
}
@@ -110,8 +110,6 @@ bool bio_integrity_prep(struct bio *bio)
struct bio_integrity_data *bid;
bool set_flags = true;
gfp_t gfp = GFP_NOIO;
- unsigned int len;
- void *buf;
if (!bi)
return true;
@@ -152,19 +150,12 @@ bool bio_integrity_prep(struct bio *bio)
if (WARN_ON_ONCE(bio_has_crypt_ctx(bio)))
return true;
- /* Allocate kernel buffer for protection data */
- len = bio_integrity_bytes(bi, bio_sectors(bio));
- buf = kmalloc(len, gfp);
- if (!buf)
- goto err_end_io;
bid = mempool_alloc(&bid_pool, GFP_NOIO);
- if (!bid)
- goto err_free_buf;
bio_integrity_init(bio, &bid->bip, &bid->bvec, 1);
-
bid->bio = bio;
-
bid->bip.bip_flags |= BIP_BLOCK_INTEGRITY;
+ bio_integrity_alloc_buf(bio, gfp & __GFP_ZERO);
+
bip_set_seed(&bid->bip, bio->bi_iter.bi_sector);
if (set_flags) {
@@ -176,23 +167,12 @@ bool bio_integrity_prep(struct bio *bio)
bid->bip.bip_flags |= BIP_CHECK_REFTAG;
}
- if (bio_integrity_add_page(bio, virt_to_page(buf), len,
- offset_in_page(buf)) < len)
- goto err_end_io;
-
/* Auto-generate integrity metadata if this is a write */
if (bio_data_dir(bio) == WRITE && bip_should_check(&bid->bip))
blk_integrity_generate(bio);
else
bid->saved_bio_iter = bio->bi_iter;
return true;
-
-err_free_buf:
- kfree(buf);
-err_end_io:
- bio->bi_status = BLK_STS_RESOURCE;
- bio_endio(bio);
- return false;
}
EXPORT_SYMBOL(bio_integrity_prep);
diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index bed26f1ec869..09eeaf6e74b8 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -14,6 +14,45 @@ struct bio_integrity_alloc {
struct bio_vec bvecs[];
};
+static mempool_t integrity_buf_pool;
+
+void bio_integrity_alloc_buf(struct bio *bio, bool zero_buffer)
+{
+ struct blk_integrity *bi = blk_get_integrity(bio->bi_bdev->bd_disk);
+ struct bio_integrity_payload *bip = bio_integrity(bio);
+ unsigned int len = bio_integrity_bytes(bi, bio_sectors(bio));
+ gfp_t gfp = GFP_NOIO | (zero_buffer ? __GFP_ZERO : 0);
+ void *buf;
+
+ buf = kmalloc(len, (gfp & ~__GFP_DIRECT_RECLAIM) |
+ __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN);
+ if (unlikely(!buf)) {
+ struct page *page;
+
+ page = mempool_alloc(&integrity_buf_pool, GFP_NOFS);
+ if (zero_buffer)
+ memset(page_address(page), 0, len);
+ bvec_set_page(&bip->bip_vec[0], page, len, 0);
+ bip->bip_flags |= BIP_MEMPOOL;
+ } else {
+ bvec_set_page(&bip->bip_vec[0], virt_to_page(buf), len,
+ offset_in_page(buf));
+ }
+
+ bip->bip_vcnt = 1;
+ bip->bip_iter.bi_size = len;
+}
+
+void bio_integrity_free_buf(struct bio_integrity_payload *bip)
+{
+ struct bio_vec *bv = &bip->bip_vec[0];
+
+ if (bip->bip_flags & BIP_MEMPOOL)
+ mempool_free(bv->bv_page, &integrity_buf_pool);
+ else
+ kfree(bvec_virt(bv));
+}
+
/**
* bio_integrity_free - Free bio integrity payload
* @bio: bio containing bip to be freed
@@ -438,3 +477,12 @@ int bio_integrity_clone(struct bio *bio, struct bio *bio_src,
return 0;
}
+
+static int __init bio_integrity_initfn(void)
+{
+ if (mempool_init_page_pool(&integrity_buf_pool, BIO_POOL_SIZE,
+ get_order(BLK_INTEGRITY_MAX_SIZE)))
+ panic("bio: can't create integrity buf pool\n");
+ return 0;
+}
+subsys_initcall(bio_integrity_initfn);
diff --git a/block/bio.c b/block/bio.c
index b3a79285c278..7b13bdf72de0 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -253,6 +253,7 @@ void bio_init(struct bio *bio, struct block_device *bdev, struct bio_vec *table,
bio->bi_write_hint = 0;
bio->bi_write_stream = 0;
bio->bi_status = 0;
+ bio->bi_bvec_gap_bit = 0;
bio->bi_iter.bi_sector = 0;
bio->bi_iter.bi_size = 0;
bio->bi_iter.bi_idx = 0;
diff --git a/block/blk-core.c b/block/blk-core.c
index 14ae73eebe0d..8387fe50ea15 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -662,13 +662,13 @@ static void __submit_bio(struct bio *bio)
* bio_list of new bios to be added. ->submit_bio() may indeed add some more
* bios through a recursive call to submit_bio_noacct. If it did, we find a
* non-NULL value in bio_list and re-enter the loop from the top.
- * - In this case we really did just take the bio of the top of the list (no
+ * - In this case we really did just take the bio off the top of the list (no
* pretending) and so remove it from bio_list, and call into ->submit_bio()
* again.
*
* bio_list_on_stack[0] contains bios submitted by the current ->submit_bio.
* bio_list_on_stack[1] contains bios that were submitted before the current
- * ->submit_bio, but that haven't been processed yet.
+ * ->submit_bio(), but that haven't been processed yet.
*/
static void __submit_bio_noacct(struct bio *bio)
{
@@ -743,8 +743,8 @@ void submit_bio_noacct_nocheck(struct bio *bio, bool split)
/*
* We only want one ->submit_bio to be active at a time, else stack
* usage with stacked devices could be a problem. Use current->bio_list
- * to collect a list of requests submited by a ->submit_bio method while
- * it is active, and then process them after it returned.
+ * to collect a list of requests submitted by a ->submit_bio method
+ * while it is active, and then process them after it returned.
*/
if (current->bio_list) {
if (split)
@@ -901,7 +901,7 @@ static void bio_set_ioprio(struct bio *bio)
*
* submit_bio() is used to submit I/O requests to block devices. It is passed a
* fully set up &struct bio that describes the I/O that needs to be done. The
- * bio will be send to the device described by the bi_bdev field.
+ * bio will be sent to the device described by the bi_bdev field.
*
* The success/failure status of the request, along with notification of
* completion, is delivered asynchronously through the ->bi_end_io() callback
@@ -991,7 +991,7 @@ int iocb_bio_iopoll(struct kiocb *kiocb, struct io_comp_batch *iob,
* point to a freshly allocated bio at this point. If that happens
* we have a few cases to consider:
*
- * 1) the bio is beeing initialized and bi_bdev is NULL. We can just
+ * 1) the bio is being initialized and bi_bdev is NULL. We can just
* simply nothing in this case
* 2) the bio points to a not poll enabled device. bio_poll will catch
* this and return 0
diff --git a/block/blk-crypto.c b/block/blk-crypto.c
index 4b1ad84d1b5a..3e7bf1974cbd 100644
--- a/block/blk-crypto.c
+++ b/block/blk-crypto.c
@@ -292,7 +292,7 @@ bool __blk_crypto_bio_prep(struct bio **bio_ptr)
}
if (!bio_crypt_check_alignment(bio)) {
- bio->bi_status = BLK_STS_IOERR;
+ bio->bi_status = BLK_STS_INVAL;
goto fail;
}
diff --git a/block/blk-iocost.c b/block/blk-iocost.c
index 5bfd70311359..a0416927d33d 100644
--- a/block/blk-iocost.c
+++ b/block/blk-iocost.c
@@ -2334,10 +2334,8 @@ static void ioc_timer_fn(struct timer_list *timer)
else
usage_dur = max_t(u64, now.now - ioc->period_at, 1);
- usage = clamp_t(u32,
- DIV64_U64_ROUND_UP(usage_us * WEIGHT_ONE,
- usage_dur),
- 1, WEIGHT_ONE);
+ usage = clamp(DIV64_U64_ROUND_UP(usage_us * WEIGHT_ONE, usage_dur),
+ 1, WEIGHT_ONE);
/*
* Already donating or accumulated enough to start.
diff --git a/block/blk-lib.c b/block/blk-lib.c
index 3030a772d3aa..19e0203cc18a 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -87,11 +87,11 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
{
struct bio *bio = NULL;
struct blk_plug plug;
- int ret;
+ int ret = 0;
blk_start_plug(&plug);
- ret = __blkdev_issue_discard(bdev, sector, nr_sects, gfp_mask, &bio);
- if (!ret && bio) {
+ __blkdev_issue_discard(bdev, sector, nr_sects, gfp_mask, &bio);
+ if (bio) {
ret = submit_bio_wait(bio);
if (ret == -EOPNOTSUPP)
ret = 0;
diff --git a/block/blk-map.c b/block/blk-map.c
index 60faf036fb6e..17a1dc288678 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -459,6 +459,8 @@ int blk_rq_append_bio(struct request *rq, struct bio *bio)
if (rq->bio) {
if (!ll_back_merge_fn(rq, bio, nr_segs))
return -EINVAL;
+ rq->phys_gap_bit = bio_seg_gap(rq->q, rq->biotail, bio,
+ rq->phys_gap_bit);
rq->biotail->bi_next = bio;
rq->biotail = bio;
rq->__data_len += bio->bi_iter.bi_size;
@@ -469,6 +471,7 @@ int blk_rq_append_bio(struct request *rq, struct bio *bio)
rq->nr_phys_segments = nr_segs;
rq->bio = rq->biotail = bio;
rq->__data_len = bio->bi_iter.bi_size;
+ rq->phys_gap_bit = bio->bi_bvec_gap_bit;
return 0;
}
EXPORT_SYMBOL(blk_rq_append_bio);
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 37864c5d287e..d3115d7469df 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -302,6 +302,12 @@ static unsigned int bio_split_alignment(struct bio *bio,
return lim->logical_block_size;
}
+static inline unsigned int bvec_seg_gap(struct bio_vec *bvprv,
+ struct bio_vec *bv)
+{
+ return bv->bv_offset | (bvprv->bv_offset + bvprv->bv_len);
+}
+
/**
* bio_split_io_at - check if and where to split a bio
* @bio: [in] bio to be split
@@ -319,8 +325,8 @@ int bio_split_io_at(struct bio *bio, const struct queue_limits *lim,
unsigned *segs, unsigned max_bytes, unsigned len_align_mask)
{
struct bio_vec bv, bvprv, *bvprvp = NULL;
+ unsigned nsegs = 0, bytes = 0, gaps = 0;
struct bvec_iter iter;
- unsigned nsegs = 0, bytes = 0;
bio_for_each_bvec(bv, bio, iter) {
if (bv.bv_offset & lim->dma_alignment ||
@@ -331,12 +337,15 @@ int bio_split_io_at(struct bio *bio, const struct queue_limits *lim,
* If the queue doesn't support SG gaps and adding this
* offset would create a gap, disallow it.
*/
- if (bvprvp && bvec_gap_to_prev(lim, bvprvp, bv.bv_offset))
- goto split;
+ if (bvprvp) {
+ if (bvec_gap_to_prev(lim, bvprvp, bv.bv_offset))
+ goto split;
+ gaps |= bvec_seg_gap(bvprvp, &bv);
+ }
if (nsegs < lim->max_segments &&
bytes + bv.bv_len <= max_bytes &&
- bv.bv_offset + bv.bv_len <= lim->min_segment_size) {
+ bv.bv_offset + bv.bv_len <= lim->max_fast_segment_size) {
nsegs++;
bytes += bv.bv_len;
} else {
@@ -350,6 +359,7 @@ int bio_split_io_at(struct bio *bio, const struct queue_limits *lim,
}
*segs = nsegs;
+ bio->bi_bvec_gap_bit = ffs(gaps);
return 0;
split:
if (bio->bi_opf & REQ_ATOMIC)
@@ -385,6 +395,7 @@ split:
* big IO can be trival, disable iopoll when split needed.
*/
bio_clear_polled(bio);
+ bio->bi_bvec_gap_bit = ffs(gaps);
return bytes >> SECTOR_SHIFT;
}
EXPORT_SYMBOL_GPL(bio_split_io_at);
@@ -721,6 +732,24 @@ static bool blk_atomic_write_mergeable_rqs(struct request *rq,
return (rq->cmd_flags & REQ_ATOMIC) == (next->cmd_flags & REQ_ATOMIC);
}
+u8 bio_seg_gap(struct request_queue *q, struct bio *prev, struct bio *next,
+ u8 gaps_bit)
+{
+ struct bio_vec pb, nb;
+
+ if (!bio_has_data(prev))
+ return 0;
+
+ gaps_bit = min_not_zero(gaps_bit, prev->bi_bvec_gap_bit);
+ gaps_bit = min_not_zero(gaps_bit, next->bi_bvec_gap_bit);
+
+ bio_get_last_bvec(prev, &pb);
+ bio_get_first_bvec(next, &nb);
+ if (!biovec_phys_mergeable(q, &pb, &nb))
+ gaps_bit = min_not_zero(gaps_bit, ffs(bvec_seg_gap(&pb, &nb)));
+ return gaps_bit;
+}
+
/*
* For non-mq, this has to be called with the request spinlock acquired.
* For mq with scheduling, the appropriate queue wide lock should be held.
@@ -785,6 +814,9 @@ static struct request *attempt_merge(struct request_queue *q,
if (next->start_time_ns < req->start_time_ns)
req->start_time_ns = next->start_time_ns;
+ req->phys_gap_bit = bio_seg_gap(req->q, req->biotail, next->bio,
+ min_not_zero(next->phys_gap_bit,
+ req->phys_gap_bit));
req->biotail->bi_next = next->bio;
req->biotail = next->biotail;
@@ -908,6 +940,8 @@ enum bio_merge_status bio_attempt_back_merge(struct request *req,
if (req->rq_flags & RQF_ZONE_WRITE_PLUGGING)
blk_zone_write_plug_bio_merged(bio);
+ req->phys_gap_bit = bio_seg_gap(req->q, req->biotail, bio,
+ req->phys_gap_bit);
req->biotail->bi_next = bio;
req->biotail = bio;
req->__data_len += bio->bi_iter.bi_size;
@@ -942,6 +976,8 @@ static enum bio_merge_status bio_attempt_front_merge(struct request *req,
blk_update_mixed_merge(req, bio, true);
+ req->phys_gap_bit = bio_seg_gap(req->q, bio, req->bio,
+ req->phys_gap_bit);
bio->bi_next = req->bio;
req->bio = bio;
diff --git a/block/blk-mq-dma.c b/block/blk-mq-dma.c
index 449950029872..b6dbc9767596 100644
--- a/block/blk-mq-dma.c
+++ b/block/blk-mq-dma.c
@@ -79,13 +79,12 @@ static bool blk_map_iter_next(struct request *req, struct blk_map_iter *iter,
static inline bool blk_can_dma_map_iova(struct request *req,
struct device *dma_dev)
{
- return !((queue_virt_boundary(req->q) + 1) &
- dma_get_merge_boundary(dma_dev));
+ return !(req_phys_gap_mask(req) & dma_get_merge_boundary(dma_dev));
}
static bool blk_dma_map_bus(struct blk_dma_iter *iter, struct phys_vec *vec)
{
- iter->addr = pci_p2pdma_bus_addr_map(&iter->p2pdma, vec->paddr);
+ iter->addr = pci_p2pdma_bus_addr_map(iter->p2pdma.mem, vec->paddr);
iter->len = vec->len;
return true;
}
@@ -93,8 +92,13 @@ static bool blk_dma_map_bus(struct blk_dma_iter *iter, struct phys_vec *vec)
static bool blk_dma_map_direct(struct request *req, struct device *dma_dev,
struct blk_dma_iter *iter, struct phys_vec *vec)
{
- iter->addr = dma_map_page(dma_dev, phys_to_page(vec->paddr),
- offset_in_page(vec->paddr), vec->len, rq_dma_dir(req));
+ unsigned int attrs = 0;
+
+ if (iter->p2pdma.map == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE)
+ attrs |= DMA_ATTR_MMIO;
+
+ iter->addr = dma_map_phys(dma_dev, vec->paddr, vec->len,
+ rq_dma_dir(req), attrs);
if (dma_mapping_error(dma_dev, iter->addr)) {
iter->status = BLK_STS_RESOURCE;
return false;
@@ -109,14 +113,18 @@ static bool blk_rq_dma_map_iova(struct request *req, struct device *dma_dev,
{
enum dma_data_direction dir = rq_dma_dir(req);
unsigned int mapped = 0;
+ unsigned int attrs = 0;
int error;
iter->addr = state->addr;
iter->len = dma_iova_size(state);
+ if (iter->p2pdma.map == PCI_P2PDMA_MAP_THRU_HOST_BRIDGE)
+ attrs |= DMA_ATTR_MMIO;
+
do {
error = dma_iova_link(dma_dev, state, vec->paddr, mapped,
- vec->len, dir, 0);
+ vec->len, dir, attrs);
if (error)
break;
mapped += vec->len;
@@ -143,7 +151,7 @@ static inline void blk_rq_map_iter_init(struct request *rq,
.bi_size = rq->special_vec.bv_len,
}
};
- } else if (bio) {
+ } else if (bio) {
*iter = (struct blk_map_iter) {
.bio = bio,
.bvecs = bio->bi_io_vec,
@@ -151,7 +159,7 @@ static inline void blk_rq_map_iter_init(struct request *rq,
};
} else {
/* the internal flush request may not have bio attached */
- *iter = (struct blk_map_iter) {};
+ *iter = (struct blk_map_iter) {};
}
}
@@ -163,6 +171,7 @@ static bool blk_dma_map_iter_start(struct request *req, struct device *dma_dev,
memset(&iter->p2pdma, 0, sizeof(iter->p2pdma));
iter->status = BLK_STS_OK;
+ iter->p2pdma.map = PCI_P2PDMA_MAP_NONE;
/*
* Grab the first segment ASAP because we'll need it to check for P2P
@@ -174,10 +183,6 @@ static bool blk_dma_map_iter_start(struct request *req, struct device *dma_dev,
switch (pci_p2pdma_state(&iter->p2pdma, dma_dev,
phys_to_page(vec.paddr))) {
case PCI_P2PDMA_MAP_BUS_ADDR:
- if (iter->iter.is_integrity)
- bio_integrity(req->bio)->bip_flags |= BIP_P2P_DMA;
- else
- req->cmd_flags |= REQ_P2PDMA;
return blk_dma_map_bus(iter, &vec);
case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
/*
@@ -352,7 +357,7 @@ bool blk_rq_integrity_dma_map_iter_start(struct request *req,
EXPORT_SYMBOL_GPL(blk_rq_integrity_dma_map_iter_start);
/**
- * blk_rq_integrity_dma_map_iter_start - map the next integrity DMA segment for
+ * blk_rq_integrity_dma_map_iter_next - map the next integrity DMA segment for
* a request
* @req: request to map
* @dma_dev: device to map to
diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c
index e0bed16485c3..e26898128a7e 100644
--- a/block/blk-mq-sched.c
+++ b/block/blk-mq-sched.c
@@ -427,11 +427,25 @@ void blk_mq_free_sched_tags(struct elevator_tags *et,
kfree(et);
}
-void blk_mq_free_sched_tags_batch(struct xarray *et_table,
+void blk_mq_free_sched_res(struct elevator_resources *res,
+ struct elevator_type *type,
+ struct blk_mq_tag_set *set)
+{
+ if (res->et) {
+ blk_mq_free_sched_tags(res->et, set);
+ res->et = NULL;
+ }
+ if (res->data) {
+ blk_mq_free_sched_data(type, res->data);
+ res->data = NULL;
+ }
+}
+
+void blk_mq_free_sched_res_batch(struct xarray *elv_tbl,
struct blk_mq_tag_set *set)
{
struct request_queue *q;
- struct elevator_tags *et;
+ struct elv_change_ctx *ctx;
lockdep_assert_held_write(&set->update_nr_hwq_lock);
@@ -444,13 +458,46 @@ void blk_mq_free_sched_tags_batch(struct xarray *et_table,
* concurrently.
*/
if (q->elevator) {
- et = xa_load(et_table, q->id);
- if (unlikely(!et))
+ ctx = xa_load(elv_tbl, q->id);
+ if (!ctx) {
WARN_ON_ONCE(1);
- else
- blk_mq_free_sched_tags(et, set);
+ continue;
+ }
+ blk_mq_free_sched_res(&ctx->res, ctx->type, set);
+ }
+ }
+}
+
+void blk_mq_free_sched_ctx_batch(struct xarray *elv_tbl)
+{
+ unsigned long i;
+ struct elv_change_ctx *ctx;
+
+ xa_for_each(elv_tbl, i, ctx) {
+ xa_erase(elv_tbl, i);
+ kfree(ctx);
+ }
+}
+
+int blk_mq_alloc_sched_ctx_batch(struct xarray *elv_tbl,
+ struct blk_mq_tag_set *set)
+{
+ struct request_queue *q;
+ struct elv_change_ctx *ctx;
+
+ lockdep_assert_held_write(&set->update_nr_hwq_lock);
+
+ list_for_each_entry(q, &set->tag_list, tag_set_list) {
+ ctx = kzalloc(sizeof(struct elv_change_ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (xa_insert(elv_tbl, q->id, ctx, GFP_KERNEL)) {
+ kfree(ctx);
+ return -ENOMEM;
}
}
+ return 0;
}
struct elevator_tags *blk_mq_alloc_sched_tags(struct blk_mq_tag_set *set,
@@ -466,8 +513,7 @@ struct elevator_tags *blk_mq_alloc_sched_tags(struct blk_mq_tag_set *set,
else
nr_tags = nr_hw_queues;
- et = kmalloc(sizeof(struct elevator_tags) +
- nr_tags * sizeof(struct blk_mq_tags *), gfp);
+ et = kmalloc(struct_size(et, tags, nr_tags), gfp);
if (!et)
return NULL;
@@ -498,12 +544,33 @@ out:
return NULL;
}
-int blk_mq_alloc_sched_tags_batch(struct xarray *et_table,
+int blk_mq_alloc_sched_res(struct request_queue *q,
+ struct elevator_type *type,
+ struct elevator_resources *res,
+ unsigned int nr_hw_queues)
+{
+ struct blk_mq_tag_set *set = q->tag_set;
+
+ res->et = blk_mq_alloc_sched_tags(set, nr_hw_queues,
+ blk_mq_default_nr_requests(set));
+ if (!res->et)
+ return -ENOMEM;
+
+ res->data = blk_mq_alloc_sched_data(q, type);
+ if (IS_ERR(res->data)) {
+ blk_mq_free_sched_tags(res->et, set);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int blk_mq_alloc_sched_res_batch(struct xarray *elv_tbl,
struct blk_mq_tag_set *set, unsigned int nr_hw_queues)
{
+ struct elv_change_ctx *ctx;
struct request_queue *q;
- struct elevator_tags *et;
- gfp_t gfp = GFP_NOIO | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY;
+ int ret = -ENOMEM;
lockdep_assert_held_write(&set->update_nr_hwq_lock);
@@ -516,39 +583,44 @@ int blk_mq_alloc_sched_tags_batch(struct xarray *et_table,
* concurrently.
*/
if (q->elevator) {
- et = blk_mq_alloc_sched_tags(set, nr_hw_queues,
- blk_mq_default_nr_requests(set));
- if (!et)
+ ctx = xa_load(elv_tbl, q->id);
+ if (WARN_ON_ONCE(!ctx)) {
+ ret = -ENOENT;
+ goto out_unwind;
+ }
+
+ ret = blk_mq_alloc_sched_res(q, q->elevator->type,
+ &ctx->res, nr_hw_queues);
+ if (ret)
goto out_unwind;
- if (xa_insert(et_table, q->id, et, gfp))
- goto out_free_tags;
}
}
return 0;
-out_free_tags:
- blk_mq_free_sched_tags(et, set);
+
out_unwind:
list_for_each_entry_continue_reverse(q, &set->tag_list, tag_set_list) {
if (q->elevator) {
- et = xa_load(et_table, q->id);
- if (et)
- blk_mq_free_sched_tags(et, set);
+ ctx = xa_load(elv_tbl, q->id);
+ if (ctx)
+ blk_mq_free_sched_res(&ctx->res,
+ ctx->type, set);
}
}
- return -ENOMEM;
+ return ret;
}
/* caller must have a reference to @e, will grab another one if successful */
int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e,
- struct elevator_tags *et)
+ struct elevator_resources *res)
{
unsigned int flags = q->tag_set->flags;
+ struct elevator_tags *et = res->et;
struct blk_mq_hw_ctx *hctx;
struct elevator_queue *eq;
unsigned long i;
int ret;
- eq = elevator_alloc(q, e, et);
+ eq = elevator_alloc(q, e, res);
if (!eq)
return -ENOMEM;
diff --git a/block/blk-mq-sched.h b/block/blk-mq-sched.h
index 8e21a6b1415d..02c40a72e959 100644
--- a/block/blk-mq-sched.h
+++ b/block/blk-mq-sched.h
@@ -19,18 +19,52 @@ void __blk_mq_sched_restart(struct blk_mq_hw_ctx *hctx);
void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx);
int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e,
- struct elevator_tags *et);
+ struct elevator_resources *res);
void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e);
void blk_mq_sched_free_rqs(struct request_queue *q);
struct elevator_tags *blk_mq_alloc_sched_tags(struct blk_mq_tag_set *set,
unsigned int nr_hw_queues, unsigned int nr_requests);
-int blk_mq_alloc_sched_tags_batch(struct xarray *et_table,
+int blk_mq_alloc_sched_res(struct request_queue *q,
+ struct elevator_type *type,
+ struct elevator_resources *res,
+ unsigned int nr_hw_queues);
+int blk_mq_alloc_sched_res_batch(struct xarray *elv_tbl,
struct blk_mq_tag_set *set, unsigned int nr_hw_queues);
+int blk_mq_alloc_sched_ctx_batch(struct xarray *elv_tbl,
+ struct blk_mq_tag_set *set);
+void blk_mq_free_sched_ctx_batch(struct xarray *elv_tbl);
void blk_mq_free_sched_tags(struct elevator_tags *et,
struct blk_mq_tag_set *set);
-void blk_mq_free_sched_tags_batch(struct xarray *et_table,
+void blk_mq_free_sched_res(struct elevator_resources *res,
+ struct elevator_type *type,
+ struct blk_mq_tag_set *set);
+void blk_mq_free_sched_res_batch(struct xarray *et_table,
struct blk_mq_tag_set *set);
+/*
+ * blk_mq_alloc_sched_data() - Allocates scheduler specific data
+ * Returns:
+ * - Pointer to allocated data on success
+ * - NULL if no allocation needed
+ * - ERR_PTR(-ENOMEM) in case of failure
+ */
+static inline void *blk_mq_alloc_sched_data(struct request_queue *q,
+ struct elevator_type *e)
+{
+ void *sched_data;
+
+ if (!e || !e->ops.alloc_sched_data)
+ return NULL;
+
+ sched_data = e->ops.alloc_sched_data(q);
+ return (sched_data) ?: ERR_PTR(-ENOMEM);
+}
+
+static inline void blk_mq_free_sched_data(struct elevator_type *e, void *data)
+{
+ if (e && e->ops.free_sched_data)
+ e->ops.free_sched_data(data);
+}
static inline void blk_mq_sched_restart(struct blk_mq_hw_ctx *hctx)
{
diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c
index 5b664dbdf655..33946cdb5716 100644
--- a/block/blk-mq-tag.c
+++ b/block/blk-mq-tag.c
@@ -499,7 +499,7 @@ void blk_mq_queue_tag_busy_iter(struct request_queue *q, busy_tag_iter_fn *fn,
int srcu_idx;
/*
- * __blk_mq_update_nr_hw_queues() updates nr_hw_queues and hctx_table
+ * __blk_mq_update_nr_hw_queues() updates nr_hw_queues and queue_hw_ctx
* while the queue is frozen. So we can use q_usage_counter to avoid
* racing with it.
*/
diff --git a/block/blk-mq.c b/block/blk-mq.c
index d626d32f6e57..4e96bb246247 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -376,6 +376,7 @@ void blk_rq_init(struct request_queue *q, struct request *rq)
INIT_LIST_HEAD(&rq->queuelist);
rq->q = q;
rq->__sector = (sector_t) -1;
+ rq->phys_gap_bit = 0;
INIT_HLIST_NODE(&rq->hash);
RB_CLEAR_NODE(&rq->rb_node);
rq->tag = BLK_MQ_NO_TAG;
@@ -467,21 +468,26 @@ __blk_mq_alloc_requests_batch(struct blk_mq_alloc_data *data)
unsigned long tag_mask;
int i, nr = 0;
- tag_mask = blk_mq_get_tags(data, data->nr_tags, &tag_offset);
- if (unlikely(!tag_mask))
- return NULL;
+ do {
+ tag_mask = blk_mq_get_tags(data, data->nr_tags - nr, &tag_offset);
+ if (unlikely(!tag_mask)) {
+ if (nr == 0)
+ return NULL;
+ break;
+ }
+ tags = blk_mq_tags_from_data(data);
+ for (i = 0; tag_mask; i++) {
+ if (!(tag_mask & (1UL << i)))
+ continue;
+ tag = tag_offset + i;
+ prefetch(tags->static_rqs[tag]);
+ tag_mask &= ~(1UL << i);
+ rq = blk_mq_rq_ctx_init(data, tags, tag);
+ rq_list_add_head(data->cached_rqs, rq);
+ nr++;
+ }
+ } while (data->nr_tags > nr);
- tags = blk_mq_tags_from_data(data);
- for (i = 0; tag_mask; i++) {
- if (!(tag_mask & (1UL << i)))
- continue;
- tag = tag_offset + i;
- prefetch(tags->static_rqs[tag]);
- tag_mask &= ~(1UL << i);
- rq = blk_mq_rq_ctx_init(data, tags, tag);
- rq_list_add_head(data->cached_rqs, rq);
- nr++;
- }
if (!(data->rq_flags & RQF_SCHED_TAGS))
blk_mq_add_active_requests(data->hctx, nr);
/* caller already holds a reference, add for remainder */
@@ -668,6 +674,7 @@ struct request *blk_mq_alloc_request(struct request_queue *q, blk_opf_t opf,
goto out_queue_exit;
}
rq->__data_len = 0;
+ rq->phys_gap_bit = 0;
rq->__sector = (sector_t) -1;
rq->bio = rq->biotail = NULL;
return rq;
@@ -723,7 +730,7 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q,
* If not tell the caller that it should skip this queue.
*/
ret = -EXDEV;
- data.hctx = xa_load(&q->hctx_table, hctx_idx);
+ data.hctx = q->queue_hw_ctx[hctx_idx];
if (!blk_mq_hw_queue_mapped(data.hctx))
goto out_queue_exit;
cpu = cpumask_first_and(data.hctx->cpumask, cpu_online_mask);
@@ -748,6 +755,7 @@ struct request *blk_mq_alloc_request_hctx(struct request_queue *q,
rq = blk_mq_rq_ctx_init(&data, blk_mq_tags_from_data(&data), tag);
blk_mq_rq_time_init(rq, alloc_time_ns);
rq->__data_len = 0;
+ rq->phys_gap_bit = 0;
rq->__sector = (sector_t) -1;
rq->bio = rq->biotail = NULL;
return rq;
@@ -2674,6 +2682,8 @@ static void blk_mq_bio_to_request(struct request *rq, struct bio *bio,
rq->bio = rq->biotail = bio;
rq->__sector = bio->bi_iter.bi_sector;
rq->__data_len = bio->bi_iter.bi_size;
+ rq->phys_gap_bit = bio->bi_bvec_gap_bit;
+
rq->nr_phys_segments = nr_segs;
if (bio_integrity(bio))
rq->nr_integrity_segments = blk_rq_count_integrity_sg(rq->q,
@@ -3380,6 +3390,7 @@ int blk_rq_prep_clone(struct request *rq, struct request *rq_src,
}
rq->nr_phys_segments = rq_src->nr_phys_segments;
rq->nr_integrity_segments = rq_src->nr_integrity_segments;
+ rq->phys_gap_bit = rq_src->phys_gap_bit;
if (rq->bio && blk_crypto_rq_bio_prep(rq, rq->bio, gfp_mask) < 0)
goto free_and_out;
@@ -3935,8 +3946,6 @@ static void blk_mq_exit_hctx(struct request_queue *q,
blk_free_flush_queue_callback);
hctx->fq = NULL;
- xa_erase(&q->hctx_table, hctx_idx);
-
spin_lock(&q->unused_hctx_lock);
list_add(&hctx->hctx_list, &q->unused_hctx_list);
spin_unlock(&q->unused_hctx_lock);
@@ -3978,14 +3987,8 @@ static int blk_mq_init_hctx(struct request_queue *q,
hctx->numa_node))
goto exit_hctx;
- if (xa_insert(&q->hctx_table, hctx_idx, hctx, GFP_KERNEL))
- goto exit_flush_rq;
-
return 0;
- exit_flush_rq:
- if (set->ops->exit_request)
- set->ops->exit_request(set, hctx->fq->flush_rq, hctx_idx);
exit_hctx:
if (set->ops->exit_hctx)
set->ops->exit_hctx(hctx, hctx_idx);
@@ -4374,7 +4377,7 @@ void blk_mq_release(struct request_queue *q)
kobject_put(&hctx->kobj);
}
- xa_destroy(&q->hctx_table);
+ kfree(q->queue_hw_ctx);
/*
* release .mq_kobj and sw queue's kobject now because
@@ -4518,26 +4521,49 @@ static struct blk_mq_hw_ctx *blk_mq_alloc_and_init_hctx(
static void __blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
struct request_queue *q)
{
- struct blk_mq_hw_ctx *hctx;
- unsigned long i, j;
+ int i, j, end;
+ struct blk_mq_hw_ctx **hctxs = q->queue_hw_ctx;
+
+ if (q->nr_hw_queues < set->nr_hw_queues) {
+ struct blk_mq_hw_ctx **new_hctxs;
+
+ new_hctxs = kcalloc_node(set->nr_hw_queues,
+ sizeof(*new_hctxs), GFP_KERNEL,
+ set->numa_node);
+ if (!new_hctxs)
+ return;
+ if (hctxs)
+ memcpy(new_hctxs, hctxs, q->nr_hw_queues *
+ sizeof(*hctxs));
+ rcu_assign_pointer(q->queue_hw_ctx, new_hctxs);
+ /*
+ * Make sure reading the old queue_hw_ctx from other
+ * context concurrently won't trigger uaf.
+ */
+ synchronize_rcu_expedited();
+ kfree(hctxs);
+ hctxs = new_hctxs;
+ }
for (i = 0; i < set->nr_hw_queues; i++) {
int old_node;
int node = blk_mq_get_hctx_node(set, i);
- struct blk_mq_hw_ctx *old_hctx = xa_load(&q->hctx_table, i);
+ struct blk_mq_hw_ctx *old_hctx = hctxs[i];
if (old_hctx) {
old_node = old_hctx->numa_node;
blk_mq_exit_hctx(q, set, old_hctx, i);
}
- if (!blk_mq_alloc_and_init_hctx(set, q, i, node)) {
+ hctxs[i] = blk_mq_alloc_and_init_hctx(set, q, i, node);
+ if (!hctxs[i]) {
if (!old_hctx)
break;
pr_warn("Allocate new hctx on node %d fails, fallback to previous one on node %d\n",
node, old_node);
- hctx = blk_mq_alloc_and_init_hctx(set, q, i, old_node);
- WARN_ON_ONCE(!hctx);
+ hctxs[i] = blk_mq_alloc_and_init_hctx(set, q, i,
+ old_node);
+ WARN_ON_ONCE(!hctxs[i]);
}
}
/*
@@ -4546,13 +4572,21 @@ static void __blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
*/
if (i != set->nr_hw_queues) {
j = q->nr_hw_queues;
+ end = i;
} else {
j = i;
+ end = q->nr_hw_queues;
q->nr_hw_queues = set->nr_hw_queues;
}
- xa_for_each_start(&q->hctx_table, j, hctx, j)
- blk_mq_exit_hctx(q, set, hctx, j);
+ for (; j < end; j++) {
+ struct blk_mq_hw_ctx *hctx = hctxs[j];
+
+ if (hctx) {
+ blk_mq_exit_hctx(q, set, hctx, j);
+ hctxs[j] = NULL;
+ }
+ }
}
static void blk_mq_realloc_hw_ctxs(struct blk_mq_tag_set *set,
@@ -4588,8 +4622,6 @@ int blk_mq_init_allocated_queue(struct blk_mq_tag_set *set,
INIT_LIST_HEAD(&q->unused_hctx_list);
spin_lock_init(&q->unused_hctx_lock);
- xa_init(&q->hctx_table);
-
blk_mq_realloc_hw_ctxs(set, q);
if (!q->nr_hw_queues)
goto err_hctxs;
@@ -4983,27 +5015,28 @@ struct elevator_tags *blk_mq_update_nr_requests(struct request_queue *q,
* Switch back to the elevator type stored in the xarray.
*/
static void blk_mq_elv_switch_back(struct request_queue *q,
- struct xarray *elv_tbl, struct xarray *et_tbl)
+ struct xarray *elv_tbl)
{
- struct elevator_type *e = xa_load(elv_tbl, q->id);
- struct elevator_tags *t = xa_load(et_tbl, q->id);
+ struct elv_change_ctx *ctx = xa_load(elv_tbl, q->id);
+
+ if (WARN_ON_ONCE(!ctx))
+ return;
/* The elv_update_nr_hw_queues unfreezes the queue. */
- elv_update_nr_hw_queues(q, e, t);
+ elv_update_nr_hw_queues(q, ctx);
/* Drop the reference acquired in blk_mq_elv_switch_none. */
- if (e)
- elevator_put(e);
+ if (ctx->type)
+ elevator_put(ctx->type);
}
/*
- * Stores elevator type in xarray and set current elevator to none. It uses
- * q->id as an index to store the elevator type into the xarray.
+ * Stores elevator name and type in ctx and set current elevator to none.
*/
static int blk_mq_elv_switch_none(struct request_queue *q,
struct xarray *elv_tbl)
{
- int ret = 0;
+ struct elv_change_ctx *ctx;
lockdep_assert_held_write(&q->tag_set->update_nr_hwq_lock);
@@ -5015,10 +5048,11 @@ static int blk_mq_elv_switch_none(struct request_queue *q,
* can't run concurrently.
*/
if (q->elevator) {
+ ctx = xa_load(elv_tbl, q->id);
+ if (WARN_ON_ONCE(!ctx))
+ return -ENOENT;
- ret = xa_insert(elv_tbl, q->id, q->elevator->type, GFP_KERNEL);
- if (WARN_ON_ONCE(ret))
- return ret;
+ ctx->name = q->elevator->type->elevator_name;
/*
* Before we switch elevator to 'none', take a reference to
@@ -5029,9 +5063,14 @@ static int blk_mq_elv_switch_none(struct request_queue *q,
*/
__elevator_get(q->elevator->type);
+ /*
+ * Store elevator type so that we can release the reference
+ * taken above later.
+ */
+ ctx->type = q->elevator->type;
elevator_set_none(q);
}
- return ret;
+ return 0;
}
static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
@@ -5041,7 +5080,7 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
int prev_nr_hw_queues = set->nr_hw_queues;
unsigned int memflags;
int i;
- struct xarray elv_tbl, et_tbl;
+ struct xarray elv_tbl;
bool queues_frozen = false;
lockdep_assert_held(&set->tag_list_lock);
@@ -5055,11 +5094,12 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set,
memflags = memalloc_noio_save();
- xa_init(&et_tbl);
- if (blk_mq_alloc_sched_tags_batch(&et_tbl, set, nr_hw_queues) < 0)
- goto out_memalloc_restore;
-
xa_init(&elv_tbl);
+ if (blk_mq_alloc_sched_ctx_batch(&elv_tbl, set) < 0)
+ goto out_free_ctx;
+
+ if (blk_mq_alloc_sched_res_batch(&elv_tbl, set, nr_hw_queues) < 0)
+ goto out_free_ctx;
list_for_each_entry(q, &set->tag_list, tag_set_list) {
blk_mq_debugfs_unregister_hctxs(q);
@@ -5105,7 +5145,7 @@ switch_back:
/* switch_back expects queue to be frozen */
if (!queues_frozen)
blk_mq_freeze_queue_nomemsave(q);
- blk_mq_elv_switch_back(q, &elv_tbl, &et_tbl);
+ blk_mq_elv_switch_back(q, &elv_tbl);
}
list_for_each_entry(q, &set->tag_list, tag_set_list) {
@@ -5116,9 +5156,9 @@ switch_back:
blk_mq_add_hw_queues_cpuhp(q);
}
+out_free_ctx:
+ blk_mq_free_sched_ctx_batch(&elv_tbl);
xa_destroy(&elv_tbl);
- xa_destroy(&et_tbl);
-out_memalloc_restore:
memalloc_noio_restore(memflags);
/* Free the excess tags when nr_hw_queues shrink. */
@@ -5168,7 +5208,7 @@ int blk_mq_poll(struct request_queue *q, blk_qc_t cookie,
{
if (!blk_mq_can_poll(q))
return 0;
- return blk_hctx_poll(q, xa_load(&q->hctx_table, cookie), iob, flags);
+ return blk_hctx_poll(q, q->queue_hw_ctx[cookie], iob, flags);
}
int blk_rq_poll(struct request *rq, struct io_comp_batch *iob,
diff --git a/block/blk-mq.h b/block/blk-mq.h
index c4fccdeb5441..aa15d31aaae9 100644
--- a/block/blk-mq.h
+++ b/block/blk-mq.h
@@ -84,7 +84,7 @@ static inline struct blk_mq_hw_ctx *blk_mq_map_queue_type(struct request_queue *
enum hctx_type type,
unsigned int cpu)
{
- return xa_load(&q->hctx_table, q->tag_set->map[type].mq_map[cpu]);
+ return queue_hctx((q), (q->tag_set->map[type].mq_map[cpu]));
}
static inline enum hctx_type blk_mq_get_hctx_type(blk_opf_t opf)
diff --git a/block/blk-settings.c b/block/blk-settings.c
index 54cffaae4df4..51401f08ce05 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -123,6 +123,19 @@ static int blk_validate_zoned_limits(struct queue_limits *lim)
return 0;
}
+/*
+ * Maximum size of I/O that needs a block layer integrity buffer. Limited
+ * by the number of intervals for which we can fit the integrity buffer into
+ * the buffer size. Because the buffer is a single segment it is also limited
+ * by the maximum segment size.
+ */
+static inline unsigned int max_integrity_io_size(struct queue_limits *lim)
+{
+ return min_t(unsigned int, lim->max_segment_size,
+ (BLK_INTEGRITY_MAX_SIZE / lim->integrity.metadata_size) <<
+ lim->integrity.interval_exp);
+}
+
static int blk_validate_integrity_limits(struct queue_limits *lim)
{
struct blk_integrity *bi = &lim->integrity;
@@ -184,6 +197,24 @@ static int blk_validate_integrity_limits(struct queue_limits *lim)
if (!bi->interval_exp)
bi->interval_exp = ilog2(lim->logical_block_size);
+ /*
+ * The PI generation / validation helpers do not expect intervals to
+ * straddle multiple bio_vecs. Enforce alignment so that those are
+ * never generated, and that each buffer is aligned as expected.
+ */
+ if (bi->csum_type) {
+ lim->dma_alignment = max(lim->dma_alignment,
+ (1U << bi->interval_exp) - 1);
+ }
+
+ /*
+ * The block layer automatically adds integrity data for bios that don't
+ * already have it. Limit the I/O size so that a single maximum size
+ * metadata segment can cover the integrity data for the entire I/O.
+ */
+ lim->max_sectors = min(lim->max_sectors,
+ max_integrity_io_size(lim) >> SECTOR_SHIFT);
+
return 0;
}
@@ -457,12 +488,12 @@ int blk_validate_limits(struct queue_limits *lim)
return -EINVAL;
}
- /* setup min segment size for building new segment in fast path */
+ /* setup max segment size for building new segment in fast path */
if (lim->seg_boundary_mask > lim->max_segment_size - 1)
seg_size = lim->max_segment_size;
else
seg_size = lim->seg_boundary_mask + 1;
- lim->min_segment_size = min_t(unsigned int, seg_size, PAGE_SIZE);
+ lim->max_fast_segment_size = min_t(unsigned int, seg_size, PAGE_SIZE);
/*
* We require drivers to at least do logical block aligned I/O, but
@@ -525,6 +556,8 @@ int queue_limits_commit_update(struct request_queue *q,
{
int error;
+ lockdep_assert_held(&q->limits_lock);
+
error = blk_validate_limits(lim);
if (error)
goto out_unlock;
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index 76c47fe9b8d6..8684c57498cc 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -143,21 +143,22 @@ queue_ra_store(struct gendisk *disk, const char *page, size_t count)
{
unsigned long ra_kb;
ssize_t ret;
- unsigned int memflags;
struct request_queue *q = disk->queue;
ret = queue_var_store(&ra_kb, page, count);
if (ret < 0)
return ret;
/*
- * ->ra_pages is protected by ->limits_lock because it is usually
- * calculated from the queue limits by queue_limits_commit_update.
+ * The ->ra_pages change below is protected by ->limits_lock because it
+ * is usually calculated from the queue limits by
+ * queue_limits_commit_update().
+ *
+ * bdi->ra_pages reads are not serialized against bdi->ra_pages writes.
+ * Use WRITE_ONCE() to write bdi->ra_pages once.
*/
mutex_lock(&q->limits_lock);
- memflags = blk_mq_freeze_queue(q);
- disk->bdi->ra_pages = ra_kb >> (PAGE_SHIFT - 10);
+ WRITE_ONCE(disk->bdi->ra_pages, ra_kb >> (PAGE_SHIFT - 10));
mutex_unlock(&q->limits_lock);
- blk_mq_unfreeze_queue(q, memflags);
return ret;
}
@@ -375,21 +376,18 @@ static ssize_t queue_nomerges_store(struct gendisk *disk, const char *page,
size_t count)
{
unsigned long nm;
- unsigned int memflags;
struct request_queue *q = disk->queue;
ssize_t ret = queue_var_store(&nm, page, count);
if (ret < 0)
return ret;
- memflags = blk_mq_freeze_queue(q);
blk_queue_flag_clear(QUEUE_FLAG_NOMERGES, q);
blk_queue_flag_clear(QUEUE_FLAG_NOXMERGES, q);
if (nm == 2)
blk_queue_flag_set(QUEUE_FLAG_NOMERGES, q);
else if (nm)
blk_queue_flag_set(QUEUE_FLAG_NOXMERGES, q);
- blk_mq_unfreeze_queue(q, memflags);
return ret;
}
@@ -409,7 +407,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count)
#ifdef CONFIG_SMP
struct request_queue *q = disk->queue;
unsigned long val;
- unsigned int memflags;
ret = queue_var_store(&val, page, count);
if (ret < 0)
@@ -421,7 +418,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count)
* are accessed individually using atomic test_bit operation. So we
* don't grab any lock while updating these flags.
*/
- memflags = blk_mq_freeze_queue(q);
if (val == 2) {
blk_queue_flag_set(QUEUE_FLAG_SAME_COMP, q);
blk_queue_flag_set(QUEUE_FLAG_SAME_FORCE, q);
@@ -432,7 +428,6 @@ queue_rq_affinity_store(struct gendisk *disk, const char *page, size_t count)
blk_queue_flag_clear(QUEUE_FLAG_SAME_COMP, q);
blk_queue_flag_clear(QUEUE_FLAG_SAME_FORCE, q);
}
- blk_mq_unfreeze_queue(q, memflags);
#endif
return ret;
}
@@ -446,11 +441,9 @@ static ssize_t queue_poll_delay_store(struct gendisk *disk, const char *page,
static ssize_t queue_poll_store(struct gendisk *disk, const char *page,
size_t count)
{
- unsigned int memflags;
ssize_t ret = count;
struct request_queue *q = disk->queue;
- memflags = blk_mq_freeze_queue(q);
if (!(q->limits.features & BLK_FEAT_POLL)) {
ret = -EINVAL;
goto out;
@@ -459,7 +452,6 @@ static ssize_t queue_poll_store(struct gendisk *disk, const char *page,
pr_info_ratelimited("writes to the poll attribute are ignored.\n");
pr_info_ratelimited("please use driver specific parameters instead.\n");
out:
- blk_mq_unfreeze_queue(q, memflags);
return ret;
}
@@ -472,7 +464,7 @@ static ssize_t queue_io_timeout_show(struct gendisk *disk, char *page)
static ssize_t queue_io_timeout_store(struct gendisk *disk, const char *page,
size_t count)
{
- unsigned int val, memflags;
+ unsigned int val;
int err;
struct request_queue *q = disk->queue;
@@ -480,9 +472,7 @@ static ssize_t queue_io_timeout_store(struct gendisk *disk, const char *page,
if (err || val == 0)
return -EINVAL;
- memflags = blk_mq_freeze_queue(q);
blk_queue_rq_timeout(q, msecs_to_jiffies(val));
- blk_mq_unfreeze_queue(q, memflags);
return count;
}
diff --git a/block/blk-throttle.c b/block/blk-throttle.c
index 2c5b64b1a724..97188a795848 100644
--- a/block/blk-throttle.c
+++ b/block/blk-throttle.c
@@ -12,7 +12,6 @@
#include <linux/blktrace_api.h>
#include "blk.h"
#include "blk-cgroup-rwstat.h"
-#include "blk-stat.h"
#include "blk-throttle.h"
/* Max dispatch from a group in 1 round */
@@ -22,9 +21,7 @@
#define THROTL_QUANTUM 32
/* Throttling is performed over a slice and after that slice is renewed */
-#define DFL_THROTL_SLICE_HD (HZ / 10)
-#define DFL_THROTL_SLICE_SSD (HZ / 50)
-#define MAX_THROTL_SLICE (HZ)
+#define DFL_THROTL_SLICE (HZ / 10)
/* A workqueue to queue throttle related work */
static struct workqueue_struct *kthrotld_workqueue;
@@ -41,12 +38,8 @@ struct throtl_data
/* Total Number of queued bios on READ and WRITE lists */
unsigned int nr_queued[2];
- unsigned int throtl_slice;
-
/* Work for dispatching throttled bios */
struct work_struct dispatch_work;
-
- bool track_bio_latency;
};
static void throtl_pending_timer_fn(struct timer_list *t);
@@ -451,7 +444,7 @@ static void throtl_dequeue_tg(struct throtl_grp *tg)
static void throtl_schedule_pending_timer(struct throtl_service_queue *sq,
unsigned long expires)
{
- unsigned long max_expire = jiffies + 8 * sq_to_td(sq)->throtl_slice;
+ unsigned long max_expire = jiffies + 8 * DFL_THROTL_SLICE;
/*
* Since we are adjusting the throttle limit dynamically, the sleep
@@ -519,7 +512,7 @@ static inline void throtl_start_new_slice_with_credit(struct throtl_grp *tg,
if (time_after(start, tg->slice_start[rw]))
tg->slice_start[rw] = start;
- tg->slice_end[rw] = jiffies + tg->td->throtl_slice;
+ tg->slice_end[rw] = jiffies + DFL_THROTL_SLICE;
throtl_log(&tg->service_queue,
"[%c] new slice with credit start=%lu end=%lu jiffies=%lu",
rw == READ ? 'R' : 'W', tg->slice_start[rw],
@@ -534,7 +527,7 @@ static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw,
tg->io_disp[rw] = 0;
}
tg->slice_start[rw] = jiffies;
- tg->slice_end[rw] = jiffies + tg->td->throtl_slice;
+ tg->slice_end[rw] = jiffies + DFL_THROTL_SLICE;
throtl_log(&tg->service_queue,
"[%c] new slice start=%lu end=%lu jiffies=%lu",
@@ -545,7 +538,7 @@ static inline void throtl_start_new_slice(struct throtl_grp *tg, bool rw,
static inline void throtl_set_slice_end(struct throtl_grp *tg, bool rw,
unsigned long jiffy_end)
{
- tg->slice_end[rw] = roundup(jiffy_end, tg->td->throtl_slice);
+ tg->slice_end[rw] = roundup(jiffy_end, DFL_THROTL_SLICE);
}
static inline void throtl_extend_slice(struct throtl_grp *tg, bool rw,
@@ -676,12 +669,12 @@ static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw)
* sooner, then we need to reduce slice_end. A high bogus slice_end
* is bad because it does not allow new slice to start.
*/
- throtl_set_slice_end(tg, rw, jiffies + tg->td->throtl_slice);
+ throtl_set_slice_end(tg, rw, jiffies + DFL_THROTL_SLICE);
time_elapsed = rounddown(jiffies - tg->slice_start[rw],
- tg->td->throtl_slice);
+ DFL_THROTL_SLICE);
/* Don't trim slice until at least 2 slices are used */
- if (time_elapsed < tg->td->throtl_slice * 2)
+ if (time_elapsed < DFL_THROTL_SLICE * 2)
return;
/*
@@ -692,7 +685,7 @@ static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw)
* lower rate than expected. Therefore, other than the above rounddown,
* one extra slice is preserved for deviation.
*/
- time_elapsed -= tg->td->throtl_slice;
+ time_elapsed -= DFL_THROTL_SLICE;
bytes_trim = throtl_trim_bps(tg, rw, time_elapsed);
io_trim = throtl_trim_iops(tg, rw, time_elapsed);
if (!bytes_trim && !io_trim)
@@ -702,7 +695,7 @@ static inline void throtl_trim_slice(struct throtl_grp *tg, bool rw)
throtl_log(&tg->service_queue,
"[%c] trim slice nr=%lu bytes=%lld io=%d start=%lu end=%lu jiffies=%lu",
- rw == READ ? 'R' : 'W', time_elapsed / tg->td->throtl_slice,
+ rw == READ ? 'R' : 'W', time_elapsed / DFL_THROTL_SLICE,
bytes_trim, io_trim, tg->slice_start[rw], tg->slice_end[rw],
jiffies);
}
@@ -773,7 +766,7 @@ static unsigned long tg_within_iops_limit(struct throtl_grp *tg, struct bio *bio
jiffy_elapsed = jiffies - tg->slice_start[rw];
/* Round up to the next throttle slice, wait time must be nonzero */
- jiffy_elapsed_rnd = roundup(jiffy_elapsed + 1, tg->td->throtl_slice);
+ jiffy_elapsed_rnd = roundup(jiffy_elapsed + 1, DFL_THROTL_SLICE);
io_allowed = calculate_io_allowed(iops_limit, jiffy_elapsed_rnd);
if (io_allowed > 0 && tg->io_disp[rw] + 1 <= io_allowed)
return 0;
@@ -799,9 +792,9 @@ static unsigned long tg_within_bps_limit(struct throtl_grp *tg, struct bio *bio,
/* Slice has just started. Consider one slice interval */
if (!jiffy_elapsed)
- jiffy_elapsed_rnd = tg->td->throtl_slice;
+ jiffy_elapsed_rnd = DFL_THROTL_SLICE;
- jiffy_elapsed_rnd = roundup(jiffy_elapsed_rnd, tg->td->throtl_slice);
+ jiffy_elapsed_rnd = roundup(jiffy_elapsed_rnd, DFL_THROTL_SLICE);
bytes_allowed = calculate_bytes_allowed(bps_limit, jiffy_elapsed_rnd);
/* Need to consider the case of bytes_allowed overflow. */
if ((bytes_allowed > 0 && tg->bytes_disp[rw] + bio_size <= bytes_allowed)
@@ -853,7 +846,7 @@ static void tg_update_slice(struct throtl_grp *tg, bool rw)
sq_queued(&tg->service_queue, rw) == 0)
throtl_start_new_slice(tg, rw, true);
else
- throtl_extend_slice(tg, rw, jiffies + tg->td->throtl_slice);
+ throtl_extend_slice(tg, rw, jiffies + DFL_THROTL_SLICE);
}
static unsigned long tg_dispatch_bps_time(struct throtl_grp *tg, struct bio *bio)
@@ -1338,18 +1331,8 @@ static int blk_throtl_init(struct gendisk *disk)
if (ret) {
q->td = NULL;
kfree(td);
- goto out;
}
- if (blk_queue_nonrot(q))
- td->throtl_slice = DFL_THROTL_SLICE_SSD;
- else
- td->throtl_slice = DFL_THROTL_SLICE_HD;
- td->track_bio_latency = !queue_is_mq(q);
- if (!td->track_bio_latency)
- blk_stat_enable_accounting(q);
-
-out:
blk_mq_unquiesce_queue(disk->queue);
blk_mq_unfreeze_queue(disk->queue, memflags);
diff --git a/block/blk-zoned.c b/block/blk-zoned.c
index 5e2a5788dc3b..dcc295721c2c 100644
--- a/block/blk-zoned.c
+++ b/block/blk-zoned.c
@@ -33,12 +33,18 @@ static const char *const zone_cond_name[] = {
ZONE_COND_NAME(READONLY),
ZONE_COND_NAME(FULL),
ZONE_COND_NAME(OFFLINE),
+ ZONE_COND_NAME(ACTIVE),
};
#undef ZONE_COND_NAME
/*
* Per-zone write plug.
* @node: hlist_node structure for managing the plug using a hash table.
+ * @bio_list: The list of BIOs that are currently plugged.
+ * @bio_work: Work struct to handle issuing of plugged BIOs
+ * @rcu_head: RCU head to free zone write plugs with an RCU grace period.
+ * @disk: The gendisk the plug belongs to.
+ * @lock: Spinlock to atomically manipulate the plug.
* @ref: Zone write plug reference counter. A zone write plug reference is
* always at least 1 when the plug is hashed in the disk plug hash table.
* The reference is incremented whenever a new BIO needing plugging is
@@ -48,29 +54,44 @@ static const char *const zone_cond_name[] = {
* reference is dropped whenever the zone of the zone write plug is reset,
* finished and when the zone becomes full (last write BIO to the zone
* completes).
- * @lock: Spinlock to atomically manipulate the plug.
* @flags: Flags indicating the plug state.
* @zone_no: The number of the zone the plug is managing.
* @wp_offset: The zone write pointer location relative to the start of the zone
* as a number of 512B sectors.
- * @bio_list: The list of BIOs that are currently plugged.
- * @bio_work: Work struct to handle issuing of plugged BIOs
- * @rcu_head: RCU head to free zone write plugs with an RCU grace period.
- * @disk: The gendisk the plug belongs to.
+ * @cond: Condition of the zone
*/
struct blk_zone_wplug {
struct hlist_node node;
- refcount_t ref;
- spinlock_t lock;
- unsigned int flags;
- unsigned int zone_no;
- unsigned int wp_offset;
struct bio_list bio_list;
struct work_struct bio_work;
struct rcu_head rcu_head;
struct gendisk *disk;
+ spinlock_t lock;
+ refcount_t ref;
+ unsigned int flags;
+ unsigned int zone_no;
+ unsigned int wp_offset;
+ enum blk_zone_cond cond;
};
+static inline bool disk_need_zone_resources(struct gendisk *disk)
+{
+ /*
+ * All request-based zoned devices need zone resources so that the
+ * block layer can automatically handle write BIO plugging. BIO-based
+ * device drivers (e.g. DM devices) are normally responsible for
+ * handling zone write ordering and do not need zone resources, unless
+ * the driver requires zone append emulation.
+ */
+ return queue_is_mq(disk->queue) ||
+ queue_emulates_zone_append(disk->queue);
+}
+
+static inline unsigned int disk_zone_wplugs_hash_size(struct gendisk *disk)
+{
+ return 1U << disk->zone_wplugs_hash_bits;
+}
+
/*
* Zone write plug flags bits:
* - BLK_ZONE_WPLUG_PLUGGED: Indicates that the zone write plug is plugged,
@@ -109,28 +130,108 @@ const char *blk_zone_cond_str(enum blk_zone_cond zone_cond)
}
EXPORT_SYMBOL_GPL(blk_zone_cond_str);
-struct disk_report_zones_cb_args {
- struct gendisk *disk;
- report_zones_cb user_cb;
- void *user_data;
-};
+static void blk_zone_set_cond(u8 *zones_cond, unsigned int zno,
+ enum blk_zone_cond cond)
+{
+ if (!zones_cond)
+ return;
-static void disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
- struct blk_zone *zone);
+ switch (cond) {
+ case BLK_ZONE_COND_IMP_OPEN:
+ case BLK_ZONE_COND_EXP_OPEN:
+ case BLK_ZONE_COND_CLOSED:
+ zones_cond[zno] = BLK_ZONE_COND_ACTIVE;
+ return;
+ case BLK_ZONE_COND_NOT_WP:
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_FULL:
+ case BLK_ZONE_COND_OFFLINE:
+ case BLK_ZONE_COND_READONLY:
+ default:
+ zones_cond[zno] = cond;
+ return;
+ }
+}
-static int disk_report_zones_cb(struct blk_zone *zone, unsigned int idx,
- void *data)
+static void disk_zone_set_cond(struct gendisk *disk, sector_t sector,
+ enum blk_zone_cond cond)
{
- struct disk_report_zones_cb_args *args = data;
- struct gendisk *disk = args->disk;
+ u8 *zones_cond;
- if (disk->zone_wplugs_hash)
- disk_zone_wplug_sync_wp_offset(disk, zone);
+ rcu_read_lock();
+ zones_cond = rcu_dereference(disk->zones_cond);
+ if (zones_cond) {
+ unsigned int zno = disk_zone_no(disk, sector);
+
+ /*
+ * The condition of a conventional, readonly and offline zones
+ * never changes, so do nothing if the target zone is in one of
+ * these conditions.
+ */
+ switch (zones_cond[zno]) {
+ case BLK_ZONE_COND_NOT_WP:
+ case BLK_ZONE_COND_READONLY:
+ case BLK_ZONE_COND_OFFLINE:
+ break;
+ default:
+ blk_zone_set_cond(zones_cond, zno, cond);
+ break;
+ }
+ }
+ rcu_read_unlock();
+}
+
+/**
+ * bdev_zone_is_seq - check if a sector belongs to a sequential write zone
+ * @bdev: block device to check
+ * @sector: sector number
+ *
+ * Check if @sector on @bdev is contained in a sequential write required zone.
+ */
+bool bdev_zone_is_seq(struct block_device *bdev, sector_t sector)
+{
+ struct gendisk *disk = bdev->bd_disk;
+ unsigned int zno = disk_zone_no(disk, sector);
+ bool is_seq = false;
+ u8 *zones_cond;
+
+ if (!bdev_is_zoned(bdev))
+ return false;
+
+ rcu_read_lock();
+ zones_cond = rcu_dereference(disk->zones_cond);
+ if (zones_cond && zno < disk->nr_zones)
+ is_seq = zones_cond[zno] != BLK_ZONE_COND_NOT_WP;
+ rcu_read_unlock();
- if (!args->user_cb)
+ return is_seq;
+}
+EXPORT_SYMBOL_GPL(bdev_zone_is_seq);
+
+/*
+ * Zone report arguments for block device drivers report_zones operation.
+ * @cb: report_zones_cb callback for each reported zone.
+ * @data: Private data passed to report_zones_cb.
+ */
+struct blk_report_zones_args {
+ report_zones_cb cb;
+ void *data;
+ bool report_active;
+};
+
+static int blkdev_do_report_zones(struct block_device *bdev, sector_t sector,
+ unsigned int nr_zones,
+ struct blk_report_zones_args *args)
+{
+ struct gendisk *disk = bdev->bd_disk;
+
+ if (!bdev_is_zoned(bdev) || WARN_ON_ONCE(!disk->fops->report_zones))
+ return -EOPNOTSUPP;
+
+ if (!nr_zones || sector >= get_capacity(disk))
return 0;
- return args->user_cb(zone, idx, args->user_data);
+ return disk->fops->report_zones(disk, sector, nr_zones, args);
}
/**
@@ -155,22 +256,12 @@ static int disk_report_zones_cb(struct blk_zone *zone, unsigned int idx,
int blkdev_report_zones(struct block_device *bdev, sector_t sector,
unsigned int nr_zones, report_zones_cb cb, void *data)
{
- struct gendisk *disk = bdev->bd_disk;
- sector_t capacity = get_capacity(disk);
- struct disk_report_zones_cb_args args = {
- .disk = disk,
- .user_cb = cb,
- .user_data = data,
+ struct blk_report_zones_args args = {
+ .cb = cb,
+ .data = data,
};
- if (!bdev_is_zoned(bdev) || WARN_ON_ONCE(!disk->fops->report_zones))
- return -EOPNOTSUPP;
-
- if (!nr_zones || sector >= capacity)
- return 0;
-
- return disk->fops->report_zones(disk, sector, nr_zones,
- disk_report_zones_cb, &args);
+ return blkdev_do_report_zones(bdev, sector, nr_zones, &args);
}
EXPORT_SYMBOL_GPL(blkdev_report_zones);
@@ -266,7 +357,12 @@ static int blkdev_copy_zone_to_user(struct blk_zone *zone, unsigned int idx,
}
/*
- * BLKREPORTZONE ioctl processing.
+ * Mask of valid input flags for BLKREPORTZONEV2 ioctl.
+ */
+#define BLK_ZONE_REPV2_INPUT_FLAGS BLK_ZONE_REP_CACHED
+
+/*
+ * BLKREPORTZONE and BLKREPORTZONEV2 ioctl processing.
* Called from blkdev_ioctl.
*/
int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,
@@ -290,8 +386,22 @@ int blkdev_report_zones_ioctl(struct block_device *bdev, unsigned int cmd,
return -EINVAL;
args.zones = argp + sizeof(struct blk_zone_report);
- ret = blkdev_report_zones(bdev, rep.sector, rep.nr_zones,
- blkdev_copy_zone_to_user, &args);
+
+ switch (cmd) {
+ case BLKREPORTZONE:
+ ret = blkdev_report_zones(bdev, rep.sector, rep.nr_zones,
+ blkdev_copy_zone_to_user, &args);
+ break;
+ case BLKREPORTZONEV2:
+ if (rep.flags & ~BLK_ZONE_REPV2_INPUT_FLAGS)
+ return -EINVAL;
+ ret = blkdev_report_zones_cached(bdev, rep.sector, rep.nr_zones,
+ blkdev_copy_zone_to_user, &args);
+ break;
+ default:
+ return -EINVAL;
+ }
+
if (ret < 0)
return ret;
@@ -401,6 +511,7 @@ static bool disk_insert_zone_wplug(struct gendisk *disk,
{
struct blk_zone_wplug *zwplg;
unsigned long flags;
+ u8 *zones_cond;
unsigned int idx =
hash_32(zwplug->zone_no, disk->zone_wplugs_hash_bits);
@@ -416,6 +527,20 @@ static bool disk_insert_zone_wplug(struct gendisk *disk,
return false;
}
}
+
+ /*
+ * Set the zone condition: if we do not yet have a zones_cond array
+ * attached to the disk, then this is a zone write plug insert from the
+ * first call to blk_revalidate_disk_zones(), in which case the zone is
+ * necessarilly in the active condition.
+ */
+ zones_cond = rcu_dereference_check(disk->zones_cond,
+ lockdep_is_held(&disk->zone_wplugs_lock));
+ if (zones_cond)
+ zwplug->cond = zones_cond[zwplug->zone_no];
+ else
+ zwplug->cond = BLK_ZONE_COND_ACTIVE;
+
hlist_add_head_rcu(&zwplug->node, &disk->zone_wplugs_hash[idx]);
atomic_inc(&disk->nr_zone_wplugs);
spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags);
@@ -515,10 +640,15 @@ static void disk_remove_zone_wplug(struct gendisk *disk,
/*
* Mark the zone write plug as unhashed and drop the extra reference we
- * took when the plug was inserted in the hash table.
+ * took when the plug was inserted in the hash table. Also update the
+ * disk zone condition array with the current condition of the zone
+ * write plug.
*/
zwplug->flags |= BLK_ZONE_WPLUG_UNHASHED;
spin_lock_irqsave(&disk->zone_wplugs_lock, flags);
+ blk_zone_set_cond(rcu_dereference_check(disk->zones_cond,
+ lockdep_is_held(&disk->zone_wplugs_lock)),
+ zwplug->zone_no, zwplug->cond);
hlist_del_init_rcu(&zwplug->node);
atomic_dec(&disk->nr_zone_wplugs);
spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags);
@@ -600,7 +730,7 @@ static inline void blk_zone_wplug_bio_io_error(struct blk_zone_wplug *zwplug,
bio_clear_flag(bio, BIO_ZONE_WRITE_PLUGGING);
bio_io_error(bio);
disk_put_zone_wplug(zwplug);
- /* Drop the reference taken by disk_zone_wplug_add_bio(() */
+ /* Drop the reference taken by disk_zone_wplug_add_bio(). */
blk_queue_exit(q);
}
@@ -621,6 +751,22 @@ static void disk_zone_wplug_abort(struct blk_zone_wplug *zwplug)
}
/*
+ * Update a zone write plug condition based on the write pointer offset.
+ */
+static void disk_zone_wplug_update_cond(struct gendisk *disk,
+ struct blk_zone_wplug *zwplug)
+{
+ lockdep_assert_held(&zwplug->lock);
+
+ if (disk_zone_wplug_is_full(disk, zwplug))
+ zwplug->cond = BLK_ZONE_COND_FULL;
+ else if (!zwplug->wp_offset)
+ zwplug->cond = BLK_ZONE_COND_EMPTY;
+ else
+ zwplug->cond = BLK_ZONE_COND_ACTIVE;
+}
+
+/*
* Set a zone write plug write pointer offset to the specified value.
* This aborts all plugged BIOs, which is fine as this function is called for
* a zone reset operation, a zone finish operation or if the zone needs a wp
@@ -635,6 +781,8 @@ static void disk_zone_wplug_set_wp_offset(struct gendisk *disk,
/* Update the zone write pointer and abort all plugged BIOs. */
zwplug->flags &= ~BLK_ZONE_WPLUG_NEED_WP_UPDATE;
zwplug->wp_offset = wp_offset;
+ disk_zone_wplug_update_cond(disk, zwplug);
+
disk_zone_wplug_abort(zwplug);
/*
@@ -652,122 +800,399 @@ static unsigned int blk_zone_wp_offset(struct blk_zone *zone)
case BLK_ZONE_COND_IMP_OPEN:
case BLK_ZONE_COND_EXP_OPEN:
case BLK_ZONE_COND_CLOSED:
+ case BLK_ZONE_COND_ACTIVE:
return zone->wp - zone->start;
- case BLK_ZONE_COND_FULL:
- return zone->len;
case BLK_ZONE_COND_EMPTY:
return 0;
+ case BLK_ZONE_COND_FULL:
case BLK_ZONE_COND_NOT_WP:
case BLK_ZONE_COND_OFFLINE:
case BLK_ZONE_COND_READONLY:
default:
/*
- * Conventional, offline and read-only zones do not have a valid
- * write pointer.
+ * Conventional, full, offline and read-only zones do not have
+ * a valid write pointer.
*/
return UINT_MAX;
}
}
-static void disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
- struct blk_zone *zone)
+static unsigned int disk_zone_wplug_sync_wp_offset(struct gendisk *disk,
+ struct blk_zone *zone)
{
struct blk_zone_wplug *zwplug;
- unsigned long flags;
+ unsigned int wp_offset = blk_zone_wp_offset(zone);
zwplug = disk_get_zone_wplug(disk, zone->start);
- if (!zwplug)
- return;
+ if (zwplug) {
+ unsigned long flags;
- spin_lock_irqsave(&zwplug->lock, flags);
- if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE)
- disk_zone_wplug_set_wp_offset(disk, zwplug,
- blk_zone_wp_offset(zone));
- spin_unlock_irqrestore(&zwplug->lock, flags);
+ spin_lock_irqsave(&zwplug->lock, flags);
+ if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE)
+ disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+ disk_put_zone_wplug(zwplug);
+ }
- disk_put_zone_wplug(zwplug);
+ return wp_offset;
}
-static int disk_zone_sync_wp_offset(struct gendisk *disk, sector_t sector)
+/**
+ * disk_report_zone - Report one zone
+ * @disk: Target disk
+ * @zone: The zone to report
+ * @idx: The index of the zone in the overall zone report
+ * @args: report zones callback and data
+ *
+ * Description:
+ * Helper function for block device drivers to report one zone of a zone
+ * report initiated with blkdev_report_zones(). The zone being reported is
+ * specified by @zone and used to update, if necessary, the zone write plug
+ * information for the zone. If @args specifies a user callback function,
+ * this callback is executed.
+ */
+int disk_report_zone(struct gendisk *disk, struct blk_zone *zone,
+ unsigned int idx, struct blk_report_zones_args *args)
{
- struct disk_report_zones_cb_args args = {
- .disk = disk,
+ if (args && args->report_active) {
+ /*
+ * If we come here, then this is a report zones as a fallback
+ * for a cached report. So collapse the implicit open, explicit
+ * open and closed conditions into the active zone condition.
+ */
+ switch (zone->cond) {
+ case BLK_ZONE_COND_IMP_OPEN:
+ case BLK_ZONE_COND_EXP_OPEN:
+ case BLK_ZONE_COND_CLOSED:
+ zone->cond = BLK_ZONE_COND_ACTIVE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (disk->zone_wplugs_hash)
+ disk_zone_wplug_sync_wp_offset(disk, zone);
+
+ if (args && args->cb)
+ return args->cb(zone, idx, args->data);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(disk_report_zone);
+
+static int blkdev_report_zone_cb(struct blk_zone *zone, unsigned int idx,
+ void *data)
+{
+ memcpy(data, zone, sizeof(struct blk_zone));
+ return 0;
+}
+
+static int blkdev_report_zone_fallback(struct block_device *bdev,
+ sector_t sector, struct blk_zone *zone)
+{
+ struct blk_report_zones_args args = {
+ .cb = blkdev_report_zone_cb,
+ .data = zone,
+ .report_active = true,
};
+ int error;
+
+ error = blkdev_do_report_zones(bdev, sector, 1, &args);
+ if (error < 0)
+ return error;
+ if (error == 0)
+ return -EIO;
+ return 0;
+}
- return disk->fops->report_zones(disk, sector, 1,
- disk_report_zones_cb, &args);
+/*
+ * For devices that natively support zone append operations, we do not use zone
+ * write plugging for zone append writes, which makes the zone condition
+ * tracking invalid once zone append was used. In that case fall back to a
+ * regular report zones to get correct information.
+ */
+static inline bool blkdev_has_cached_report_zones(struct block_device *bdev)
+{
+ return disk_need_zone_resources(bdev->bd_disk) &&
+ (bdev_emulates_zone_append(bdev) ||
+ !test_bit(GD_ZONE_APPEND_USED, &bdev->bd_disk->state));
}
-static bool blk_zone_wplug_handle_reset_or_finish(struct bio *bio,
- unsigned int wp_offset)
+/**
+ * blkdev_get_zone_info - Get a single zone information from cached data
+ * @bdev: Target block device
+ * @sector: Sector contained by the target zone
+ * @zone: zone structure to return the zone information
+ *
+ * Description:
+ * Get the zone information for the zone containing @sector using the zone
+ * write plug of the target zone, if one exist, or the disk zone condition
+ * array otherwise. The zone condition may be reported as being
+ * the BLK_ZONE_COND_ACTIVE condition for a zone that is in the implicit
+ * open, explicit open or closed condition.
+ *
+ * Returns 0 on success and a negative error code on failure.
+ */
+int blkdev_get_zone_info(struct block_device *bdev, sector_t sector,
+ struct blk_zone *zone)
{
- struct gendisk *disk = bio->bi_bdev->bd_disk;
- sector_t sector = bio->bi_iter.bi_sector;
+ struct gendisk *disk = bdev->bd_disk;
+ sector_t zone_sectors = bdev_zone_sectors(bdev);
struct blk_zone_wplug *zwplug;
unsigned long flags;
+ u8 *zones_cond;
- /* Conventional zones cannot be reset nor finished. */
- if (!bdev_zone_is_seq(bio->bi_bdev, sector)) {
- bio_io_error(bio);
- return true;
+ if (!bdev_is_zoned(bdev))
+ return -EOPNOTSUPP;
+
+ if (sector >= get_capacity(disk))
+ return -EINVAL;
+
+ memset(zone, 0, sizeof(*zone));
+ sector = bdev_zone_start(bdev, sector);
+
+ if (!blkdev_has_cached_report_zones(bdev))
+ return blkdev_report_zone_fallback(bdev, sector, zone);
+
+ rcu_read_lock();
+ zones_cond = rcu_dereference(disk->zones_cond);
+ if (!disk->zone_wplugs_hash || !zones_cond) {
+ rcu_read_unlock();
+ return blkdev_report_zone_fallback(bdev, sector, zone);
}
+ zone->cond = zones_cond[disk_zone_no(disk, sector)];
+ rcu_read_unlock();
+
+ zone->start = sector;
+ zone->len = zone_sectors;
/*
- * No-wait reset or finish BIOs do not make much sense as the callers
- * issue these as blocking operations in most cases. To avoid issues
- * the BIO execution potentially failing with BLK_STS_AGAIN, warn about
- * REQ_NOWAIT being set and ignore that flag.
+ * If this is a conventional zone, we do not have a zone write plug and
+ * can report the zone immediately.
*/
- if (WARN_ON_ONCE(bio->bi_opf & REQ_NOWAIT))
- bio->bi_opf &= ~REQ_NOWAIT;
+ if (zone->cond == BLK_ZONE_COND_NOT_WP) {
+ zone->type = BLK_ZONE_TYPE_CONVENTIONAL;
+ zone->capacity = zone_sectors;
+ zone->wp = ULLONG_MAX;
+ return 0;
+ }
+
+ /*
+ * This is a sequential write required zone. If the zone is read-only or
+ * offline, only set the zone write pointer to an invalid value and
+ * report the zone.
+ */
+ zone->type = BLK_ZONE_TYPE_SEQWRITE_REQ;
+ if (disk_zone_is_last(disk, zone))
+ zone->capacity = disk->last_zone_capacity;
+ else
+ zone->capacity = disk->zone_capacity;
+
+ if (zone->cond == BLK_ZONE_COND_READONLY ||
+ zone->cond == BLK_ZONE_COND_OFFLINE) {
+ zone->wp = ULLONG_MAX;
+ return 0;
+ }
/*
- * If we have a zone write plug, set its write pointer offset to 0
- * (reset case) or to the zone size (finish case). This will abort all
- * BIOs plugged for the target zone. It is fine as resetting or
- * finishing zones while writes are still in-flight will result in the
+ * If the zone does not have a zone write plug, it is either full or
+ * empty, as we otherwise would have a zone write plug for it. In this
+ * case, set the write pointer accordingly and report the zone.
+ * Otherwise, if we have a zone write plug, use it.
+ */
+ zwplug = disk_get_zone_wplug(disk, sector);
+ if (!zwplug) {
+ if (zone->cond == BLK_ZONE_COND_FULL)
+ zone->wp = ULLONG_MAX;
+ else
+ zone->wp = sector;
+ return 0;
+ }
+
+ spin_lock_irqsave(&zwplug->lock, flags);
+ if (zwplug->flags & BLK_ZONE_WPLUG_NEED_WP_UPDATE) {
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+ disk_put_zone_wplug(zwplug);
+ return blkdev_report_zone_fallback(bdev, sector, zone);
+ }
+ zone->cond = zwplug->cond;
+ zone->wp = sector + zwplug->wp_offset;
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+
+ disk_put_zone_wplug(zwplug);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(blkdev_get_zone_info);
+
+/**
+ * blkdev_report_zones_cached - Get cached zones information
+ * @bdev: Target block device
+ * @sector: Sector from which to report zones
+ * @nr_zones: Maximum number of zones to report
+ * @cb: Callback function called for each reported zone
+ * @data: Private data for the callback function
+ *
+ * Description:
+ * Similar to blkdev_report_zones() but instead of calling into the low level
+ * device driver to get the zone report from the device, use
+ * blkdev_get_zone_info() to generate the report from the disk zone write
+ * plugs and zones condition array. Since calling this function without a
+ * callback does not make sense, @cb must be specified.
+ */
+int blkdev_report_zones_cached(struct block_device *bdev, sector_t sector,
+ unsigned int nr_zones, report_zones_cb cb, void *data)
+{
+ struct gendisk *disk = bdev->bd_disk;
+ sector_t capacity = get_capacity(disk);
+ sector_t zone_sectors = bdev_zone_sectors(bdev);
+ unsigned int idx = 0;
+ struct blk_zone zone;
+ int ret;
+
+ if (!cb || !bdev_is_zoned(bdev) ||
+ WARN_ON_ONCE(!disk->fops->report_zones))
+ return -EOPNOTSUPP;
+
+ if (!nr_zones || sector >= capacity)
+ return 0;
+
+ if (!blkdev_has_cached_report_zones(bdev)) {
+ struct blk_report_zones_args args = {
+ .cb = cb,
+ .data = data,
+ .report_active = true,
+ };
+
+ return blkdev_do_report_zones(bdev, sector, nr_zones, &args);
+ }
+
+ for (sector = bdev_zone_start(bdev, sector);
+ sector < capacity && idx < nr_zones;
+ sector += zone_sectors, idx++) {
+ ret = blkdev_get_zone_info(bdev, sector, &zone);
+ if (ret)
+ return ret;
+
+ ret = cb(&zone, idx, data);
+ if (ret)
+ return ret;
+ }
+
+ return idx;
+}
+EXPORT_SYMBOL_GPL(blkdev_report_zones_cached);
+
+static void blk_zone_reset_bio_endio(struct bio *bio)
+{
+ struct gendisk *disk = bio->bi_bdev->bd_disk;
+ sector_t sector = bio->bi_iter.bi_sector;
+ struct blk_zone_wplug *zwplug;
+
+ /*
+ * If we have a zone write plug, set its write pointer offset to 0.
+ * This will abort all BIOs plugged for the target zone. It is fine as
+ * resetting zones while writes are still in-flight will result in the
* writes failing anyway.
*/
zwplug = disk_get_zone_wplug(disk, sector);
if (zwplug) {
+ unsigned long flags;
+
spin_lock_irqsave(&zwplug->lock, flags);
- disk_zone_wplug_set_wp_offset(disk, zwplug, wp_offset);
+ disk_zone_wplug_set_wp_offset(disk, zwplug, 0);
spin_unlock_irqrestore(&zwplug->lock, flags);
disk_put_zone_wplug(zwplug);
+ } else {
+ disk_zone_set_cond(disk, sector, BLK_ZONE_COND_EMPTY);
}
-
- return false;
}
-static bool blk_zone_wplug_handle_reset_all(struct bio *bio)
+static void blk_zone_reset_all_bio_endio(struct bio *bio)
{
struct gendisk *disk = bio->bi_bdev->bd_disk;
+ sector_t capacity = get_capacity(disk);
struct blk_zone_wplug *zwplug;
unsigned long flags;
sector_t sector;
+ unsigned int i;
+
+ if (atomic_read(&disk->nr_zone_wplugs)) {
+ /* Update the condition of all zone write plugs. */
+ rcu_read_lock();
+ for (i = 0; i < disk_zone_wplugs_hash_size(disk); i++) {
+ hlist_for_each_entry_rcu(zwplug,
+ &disk->zone_wplugs_hash[i],
+ node) {
+ spin_lock_irqsave(&zwplug->lock, flags);
+ disk_zone_wplug_set_wp_offset(disk, zwplug, 0);
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+ }
+ }
+ rcu_read_unlock();
+ }
+
+ /* Update the cached zone conditions. */
+ for (sector = 0; sector < capacity;
+ sector += bdev_zone_sectors(bio->bi_bdev))
+ disk_zone_set_cond(disk, sector, BLK_ZONE_COND_EMPTY);
+ clear_bit(GD_ZONE_APPEND_USED, &disk->state);
+}
+
+static void blk_zone_finish_bio_endio(struct bio *bio)
+{
+ struct block_device *bdev = bio->bi_bdev;
+ struct gendisk *disk = bdev->bd_disk;
+ sector_t sector = bio->bi_iter.bi_sector;
+ struct blk_zone_wplug *zwplug;
/*
- * Set the write pointer offset of all zone write plugs to 0. This will
- * abort all plugged BIOs. It is fine as resetting zones while writes
- * are still in-flight will result in the writes failing anyway.
+ * If we have a zone write plug, set its write pointer offset to the
+ * zone size. This will abort all BIOs plugged for the target zone. It
+ * is fine as resetting zones while writes are still in-flight will
+ * result in the writes failing anyway.
*/
- for (sector = 0; sector < get_capacity(disk);
- sector += disk->queue->limits.chunk_sectors) {
- zwplug = disk_get_zone_wplug(disk, sector);
- if (zwplug) {
- spin_lock_irqsave(&zwplug->lock, flags);
- disk_zone_wplug_set_wp_offset(disk, zwplug, 0);
- spin_unlock_irqrestore(&zwplug->lock, flags);
- disk_put_zone_wplug(zwplug);
- }
+ zwplug = disk_get_zone_wplug(disk, sector);
+ if (zwplug) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&zwplug->lock, flags);
+ disk_zone_wplug_set_wp_offset(disk, zwplug,
+ bdev_zone_sectors(bdev));
+ spin_unlock_irqrestore(&zwplug->lock, flags);
+ disk_put_zone_wplug(zwplug);
+ } else {
+ disk_zone_set_cond(disk, sector, BLK_ZONE_COND_FULL);
}
+}
- return false;
+void blk_zone_mgmt_bio_endio(struct bio *bio)
+{
+ /* If the BIO failed, we have nothing to do. */
+ if (bio->bi_status != BLK_STS_OK)
+ return;
+
+ switch (bio_op(bio)) {
+ case REQ_OP_ZONE_RESET:
+ blk_zone_reset_bio_endio(bio);
+ return;
+ case REQ_OP_ZONE_RESET_ALL:
+ blk_zone_reset_all_bio_endio(bio);
+ return;
+ case REQ_OP_ZONE_FINISH:
+ blk_zone_finish_bio_endio(bio);
+ return;
+ default:
+ return;
+ }
}
static void disk_zone_wplug_schedule_bio_work(struct gendisk *disk,
struct blk_zone_wplug *zwplug)
{
+ lockdep_assert_held(&zwplug->lock);
+
/*
* Take a reference on the zone write plug and schedule the submission
* of the next plugged BIO. blk_zone_wplug_bio_work() will release the
@@ -782,8 +1207,6 @@ static inline void disk_zone_wplug_add_bio(struct gendisk *disk,
struct blk_zone_wplug *zwplug,
struct bio *bio, unsigned int nr_segs)
{
- bool schedule_bio_work = false;
-
/*
* Grab an extra reference on the BIO request queue usage counter.
* This reference will be reused to submit a request for the BIO for
@@ -800,16 +1223,6 @@ static inline void disk_zone_wplug_add_bio(struct gendisk *disk,
bio_clear_polled(bio);
/*
- * REQ_NOWAIT BIOs are always handled using the zone write plug BIO
- * work, which can block. So clear the REQ_NOWAIT flag and schedule the
- * work if this is the first BIO we are plugging.
- */
- if (bio->bi_opf & REQ_NOWAIT) {
- schedule_bio_work = !(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED);
- bio->bi_opf &= ~REQ_NOWAIT;
- }
-
- /*
* Reuse the poll cookie field to store the number of segments when
* split to the hardware limits.
*/
@@ -824,11 +1237,6 @@ static inline void disk_zone_wplug_add_bio(struct gendisk *disk,
bio_list_add(&zwplug->bio_list, bio);
trace_disk_zone_wplug_add_bio(zwplug->disk->queue, zwplug->zone_no,
bio->bi_iter.bi_sector, bio_sectors(bio));
-
- zwplug->flags |= BLK_ZONE_WPLUG_PLUGGED;
-
- if (schedule_bio_work)
- disk_zone_wplug_schedule_bio_work(disk, zwplug);
}
/*
@@ -836,6 +1244,7 @@ static inline void disk_zone_wplug_add_bio(struct gendisk *disk,
*/
void blk_zone_write_plug_bio_merged(struct bio *bio)
{
+ struct gendisk *disk = bio->bi_bdev->bd_disk;
struct blk_zone_wplug *zwplug;
unsigned long flags;
@@ -857,13 +1266,13 @@ void blk_zone_write_plug_bio_merged(struct bio *bio)
* have at least one request and one BIO referencing the zone write
* plug. So this should not fail.
*/
- zwplug = disk_get_zone_wplug(bio->bi_bdev->bd_disk,
- bio->bi_iter.bi_sector);
+ zwplug = disk_get_zone_wplug(disk, bio->bi_iter.bi_sector);
if (WARN_ON_ONCE(!zwplug))
return;
spin_lock_irqsave(&zwplug->lock, flags);
zwplug->wp_offset += bio_sectors(bio);
+ disk_zone_wplug_update_cond(disk, zwplug);
spin_unlock_irqrestore(&zwplug->lock, flags);
}
@@ -922,6 +1331,7 @@ void blk_zone_write_plug_init_request(struct request *req)
/* Drop the reference taken by disk_zone_wplug_add_bio(). */
blk_queue_exit(q);
zwplug->wp_offset += bio_sectors(bio);
+ disk_zone_wplug_update_cond(disk, zwplug);
req_back_sector += bio_sectors(bio);
}
@@ -985,6 +1395,7 @@ static bool blk_zone_wplug_prepare_bio(struct blk_zone_wplug *zwplug,
/* Advance the zone write pointer offset. */
zwplug->wp_offset += bio_sectors(bio);
+ disk_zone_wplug_update_cond(disk, zwplug);
return true;
}
@@ -1036,14 +1447,17 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
bio_set_flag(bio, BIO_ZONE_WRITE_PLUGGING);
/*
- * If the zone is already plugged, add the BIO to the plug BIO list.
- * Do the same for REQ_NOWAIT BIOs to ensure that we will not see a
- * BLK_STS_AGAIN failure if we let the BIO execute.
- * Otherwise, plug and let the BIO execute.
+ * Add REQ_NOWAIT BIOs to the plug list to ensure that we will not see a
+ * BLK_STS_AGAIN failure if we let the caller submit the BIO.
*/
- if ((zwplug->flags & BLK_ZONE_WPLUG_PLUGGED) ||
- (bio->bi_opf & REQ_NOWAIT))
- goto plug;
+ if (bio->bi_opf & REQ_NOWAIT) {
+ bio->bi_opf &= ~REQ_NOWAIT;
+ goto queue_bio;
+ }
+
+ /* If the zone is already plugged, add the BIO to the BIO plug list. */
+ if (zwplug->flags & BLK_ZONE_WPLUG_PLUGGED)
+ goto queue_bio;
if (!blk_zone_wplug_prepare_bio(zwplug, bio)) {
spin_unlock_irqrestore(&zwplug->lock, flags);
@@ -1051,15 +1465,21 @@ static bool blk_zone_wplug_handle_write(struct bio *bio, unsigned int nr_segs)
return true;
}
+ /* Otherwise, plug and let the caller submit the BIO. */
zwplug->flags |= BLK_ZONE_WPLUG_PLUGGED;
spin_unlock_irqrestore(&zwplug->lock, flags);
return false;
-plug:
+queue_bio:
disk_zone_wplug_add_bio(disk, zwplug, bio, nr_segs);
+ if (!(zwplug->flags & BLK_ZONE_WPLUG_PLUGGED)) {
+ zwplug->flags |= BLK_ZONE_WPLUG_PLUGGED;
+ disk_zone_wplug_schedule_bio_work(disk, zwplug);
+ }
+
spin_unlock_irqrestore(&zwplug->lock, flags);
return true;
@@ -1071,6 +1491,9 @@ static void blk_zone_wplug_handle_native_zone_append(struct bio *bio)
struct blk_zone_wplug *zwplug;
unsigned long flags;
+ if (!test_bit(GD_ZONE_APPEND_USED, &disk->state))
+ set_bit(GD_ZONE_APPEND_USED, &disk->state);
+
/*
* We have native support for zone append operations, so we are not
* going to handle @bio through plugging. However, we may already have a
@@ -1106,6 +1529,30 @@ static void blk_zone_wplug_handle_native_zone_append(struct bio *bio)
disk_put_zone_wplug(zwplug);
}
+static bool blk_zone_wplug_handle_zone_mgmt(struct bio *bio)
+{
+ if (bio_op(bio) != REQ_OP_ZONE_RESET_ALL &&
+ !bdev_zone_is_seq(bio->bi_bdev, bio->bi_iter.bi_sector)) {
+ /*
+ * Zone reset and zone finish operations do not apply to
+ * conventional zones.
+ */
+ bio_io_error(bio);
+ return true;
+ }
+
+ /*
+ * No-wait zone management BIOs do not make much sense as the callers
+ * issue these as blocking operations in most cases. To avoid issues
+ * with the BIO execution potentially failing with BLK_STS_AGAIN, warn
+ * about REQ_NOWAIT being set and ignore that flag.
+ */
+ if (WARN_ON_ONCE(bio->bi_opf & REQ_NOWAIT))
+ bio->bi_opf &= ~REQ_NOWAIT;
+
+ return false;
+}
+
/**
* blk_zone_plug_bio - Handle a zone write BIO with zone write plugging
* @bio: The BIO being submitted
@@ -1153,12 +1600,9 @@ bool blk_zone_plug_bio(struct bio *bio, unsigned int nr_segs)
case REQ_OP_WRITE_ZEROES:
return blk_zone_wplug_handle_write(bio, nr_segs);
case REQ_OP_ZONE_RESET:
- return blk_zone_wplug_handle_reset_or_finish(bio, 0);
case REQ_OP_ZONE_FINISH:
- return blk_zone_wplug_handle_reset_or_finish(bio,
- bdev_zone_sectors(bdev));
case REQ_OP_ZONE_RESET_ALL:
- return blk_zone_wplug_handle_reset_all(bio);
+ return blk_zone_wplug_handle_zone_mgmt(bio);
default:
return false;
}
@@ -1332,11 +1776,6 @@ put_zwplug:
disk_put_zone_wplug(zwplug);
}
-static inline unsigned int disk_zone_wplugs_hash_size(struct gendisk *disk)
-{
- return 1U << disk->zone_wplugs_hash_bits;
-}
-
void disk_init_zone_resources(struct gendisk *disk)
{
spin_lock_init(&disk->zone_wplugs_lock);
@@ -1415,31 +1854,30 @@ static void disk_destroy_zone_wplugs_hash_table(struct gendisk *disk)
kfree(disk->zone_wplugs_hash);
disk->zone_wplugs_hash = NULL;
disk->zone_wplugs_hash_bits = 0;
+
+ /*
+ * Wait for the zone write plugs to be RCU-freed before destroying the
+ * mempool.
+ */
+ rcu_barrier();
+ mempool_destroy(disk->zone_wplugs_pool);
+ disk->zone_wplugs_pool = NULL;
}
-static unsigned int disk_set_conv_zones_bitmap(struct gendisk *disk,
- unsigned long *bitmap)
+static void disk_set_zones_cond_array(struct gendisk *disk, u8 *zones_cond)
{
- unsigned int nr_conv_zones = 0;
unsigned long flags;
spin_lock_irqsave(&disk->zone_wplugs_lock, flags);
- if (bitmap)
- nr_conv_zones = bitmap_weight(bitmap, disk->nr_zones);
- bitmap = rcu_replace_pointer(disk->conv_zones_bitmap, bitmap,
- lockdep_is_held(&disk->zone_wplugs_lock));
+ zones_cond = rcu_replace_pointer(disk->zones_cond, zones_cond,
+ lockdep_is_held(&disk->zone_wplugs_lock));
spin_unlock_irqrestore(&disk->zone_wplugs_lock, flags);
- kfree_rcu_mightsleep(bitmap);
-
- return nr_conv_zones;
+ kfree_rcu_mightsleep(zones_cond);
}
void disk_free_zone_resources(struct gendisk *disk)
{
- if (!disk->zone_wplugs_pool)
- return;
-
if (disk->zone_wplugs_wq) {
destroy_workqueue(disk->zone_wplugs_wq);
disk->zone_wplugs_wq = NULL;
@@ -1447,40 +1885,37 @@ void disk_free_zone_resources(struct gendisk *disk)
disk_destroy_zone_wplugs_hash_table(disk);
- /*
- * Wait for the zone write plugs to be RCU-freed before
- * destorying the mempool.
- */
- rcu_barrier();
-
- mempool_destroy(disk->zone_wplugs_pool);
- disk->zone_wplugs_pool = NULL;
-
- disk_set_conv_zones_bitmap(disk, NULL);
+ disk_set_zones_cond_array(disk, NULL);
disk->zone_capacity = 0;
disk->last_zone_capacity = 0;
disk->nr_zones = 0;
}
-static inline bool disk_need_zone_resources(struct gendisk *disk)
-{
- /*
- * All mq zoned devices need zone resources so that the block layer
- * can automatically handle write BIO plugging. BIO-based device drivers
- * (e.g. DM devices) are normally responsible for handling zone write
- * ordering and do not need zone resources, unless the driver requires
- * zone append emulation.
- */
- return queue_is_mq(disk->queue) ||
- queue_emulates_zone_append(disk->queue);
-}
+struct blk_revalidate_zone_args {
+ struct gendisk *disk;
+ u8 *zones_cond;
+ unsigned int nr_zones;
+ unsigned int nr_conv_zones;
+ unsigned int zone_capacity;
+ unsigned int last_zone_capacity;
+ sector_t sector;
+};
static int disk_revalidate_zone_resources(struct gendisk *disk,
- unsigned int nr_zones)
+ struct blk_revalidate_zone_args *args)
{
struct queue_limits *lim = &disk->queue->limits;
unsigned int pool_size;
+ args->disk = disk;
+ args->nr_zones =
+ DIV_ROUND_UP_ULL(get_capacity(disk), lim->chunk_sectors);
+
+ /* Cached zone conditions: 1 byte per zone */
+ args->zones_cond = kzalloc(args->nr_zones, GFP_NOIO);
+ if (!args->zones_cond)
+ return -ENOMEM;
+
if (!disk_need_zone_resources(disk))
return 0;
@@ -1490,7 +1925,8 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
*/
pool_size = max(lim->max_open_zones, lim->max_active_zones);
if (!pool_size)
- pool_size = min(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, nr_zones);
+ pool_size =
+ min(BLK_ZONE_WPLUG_DEFAULT_POOL_SIZE, args->nr_zones);
if (!disk->zone_wplugs_hash)
return disk_alloc_zone_resources(disk, pool_size);
@@ -1498,15 +1934,6 @@ static int disk_revalidate_zone_resources(struct gendisk *disk,
return 0;
}
-struct blk_revalidate_zone_args {
- struct gendisk *disk;
- unsigned long *conv_zones_bitmap;
- unsigned int nr_zones;
- unsigned int zone_capacity;
- unsigned int last_zone_capacity;
- sector_t sector;
-};
-
/*
* Update the disk zone resources information and device queue limits.
* The disk queue is frozen when this is executed.
@@ -1515,30 +1942,34 @@ static int disk_update_zone_resources(struct gendisk *disk,
struct blk_revalidate_zone_args *args)
{
struct request_queue *q = disk->queue;
- unsigned int nr_seq_zones, nr_conv_zones;
- unsigned int pool_size;
+ unsigned int nr_seq_zones;
+ unsigned int pool_size, memflags;
struct queue_limits lim;
+ int ret = 0;
+
+ lim = queue_limits_start_update(q);
+
+ memflags = blk_mq_freeze_queue(q);
disk->nr_zones = args->nr_zones;
- disk->zone_capacity = args->zone_capacity;
- disk->last_zone_capacity = args->last_zone_capacity;
- nr_conv_zones =
- disk_set_conv_zones_bitmap(disk, args->conv_zones_bitmap);
- if (nr_conv_zones >= disk->nr_zones) {
+ if (args->nr_conv_zones >= disk->nr_zones) {
pr_warn("%s: Invalid number of conventional zones %u / %u\n",
- disk->disk_name, nr_conv_zones, disk->nr_zones);
- return -ENODEV;
+ disk->disk_name, args->nr_conv_zones, disk->nr_zones);
+ ret = -ENODEV;
+ goto unfreeze;
}
- lim = queue_limits_start_update(q);
+ disk->zone_capacity = args->zone_capacity;
+ disk->last_zone_capacity = args->last_zone_capacity;
+ disk_set_zones_cond_array(disk, args->zones_cond);
/*
- * Some devices can advertize zone resource limits that are larger than
+ * Some devices can advertise zone resource limits that are larger than
* the number of sequential zones of the zoned block device, e.g. a
* small ZNS namespace. For such case, assume that the zoned device has
* no zone resource limits.
*/
- nr_seq_zones = disk->nr_zones - nr_conv_zones;
+ nr_seq_zones = disk->nr_zones - args->nr_conv_zones;
if (lim.max_open_zones >= nr_seq_zones)
lim.max_open_zones = 0;
if (lim.max_active_zones >= nr_seq_zones)
@@ -1568,7 +1999,53 @@ static int disk_update_zone_resources(struct gendisk *disk,
}
commit:
- return queue_limits_commit_update_frozen(q, &lim);
+ ret = queue_limits_commit_update(q, &lim);
+
+unfreeze:
+ if (ret)
+ disk_free_zone_resources(disk);
+
+ blk_mq_unfreeze_queue(q, memflags);
+
+ return ret;
+}
+
+static int blk_revalidate_zone_cond(struct blk_zone *zone, unsigned int idx,
+ struct blk_revalidate_zone_args *args)
+{
+ enum blk_zone_cond cond = zone->cond;
+
+ /* Check that the zone condition is consistent with the zone type. */
+ switch (cond) {
+ case BLK_ZONE_COND_NOT_WP:
+ if (zone->type != BLK_ZONE_TYPE_CONVENTIONAL)
+ goto invalid_condition;
+ break;
+ case BLK_ZONE_COND_IMP_OPEN:
+ case BLK_ZONE_COND_EXP_OPEN:
+ case BLK_ZONE_COND_CLOSED:
+ case BLK_ZONE_COND_EMPTY:
+ case BLK_ZONE_COND_FULL:
+ case BLK_ZONE_COND_OFFLINE:
+ case BLK_ZONE_COND_READONLY:
+ if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
+ goto invalid_condition;
+ break;
+ default:
+ pr_warn("%s: Invalid zone condition 0x%X\n",
+ args->disk->disk_name, cond);
+ return -ENODEV;
+ }
+
+ blk_zone_set_cond(args->zones_cond, idx, cond);
+
+ return 0;
+
+invalid_condition:
+ pr_warn("%s: Invalid zone condition 0x%x for type 0x%x\n",
+ args->disk->disk_name, cond, zone->type);
+
+ return -ENODEV;
}
static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
@@ -1585,17 +2062,7 @@ static int blk_revalidate_conv_zone(struct blk_zone *zone, unsigned int idx,
if (disk_zone_is_last(disk, zone))
args->last_zone_capacity = zone->capacity;
- if (!disk_need_zone_resources(disk))
- return 0;
-
- if (!args->conv_zones_bitmap) {
- args->conv_zones_bitmap =
- bitmap_zalloc(args->nr_zones, GFP_NOIO);
- if (!args->conv_zones_bitmap)
- return -ENOMEM;
- }
-
- set_bit(idx, args->conv_zones_bitmap);
+ args->nr_conv_zones++;
return 0;
}
@@ -1632,9 +2099,7 @@ static int blk_revalidate_seq_zone(struct blk_zone *zone, unsigned int idx,
if (!queue_emulates_zone_append(disk->queue) || !disk->zone_wplugs_hash)
return 0;
- disk_zone_wplug_sync_wp_offset(disk, zone);
-
- wp_offset = blk_zone_wp_offset(zone);
+ wp_offset = disk_zone_wplug_sync_wp_offset(disk, zone);
if (!wp_offset || wp_offset >= zone->capacity)
return 0;
@@ -1693,6 +2158,11 @@ static int blk_revalidate_zone_cb(struct blk_zone *zone, unsigned int idx,
return -ENODEV;
}
+ /* Check zone condition */
+ ret = blk_revalidate_zone_cond(zone, idx, args);
+ if (ret)
+ return ret;
+
/* Check zone type */
switch (zone->type) {
case BLK_ZONE_TYPE_CONVENTIONAL:
@@ -1733,7 +2203,11 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
sector_t zone_sectors = q->limits.chunk_sectors;
sector_t capacity = get_capacity(disk);
struct blk_revalidate_zone_args args = { };
- unsigned int noio_flag;
+ unsigned int memflags, noio_flag;
+ struct blk_report_zones_args rep_args = {
+ .cb = blk_revalidate_zone_cb,
+ .data = &args,
+ };
int ret = -ENOMEM;
if (WARN_ON_ONCE(!blk_queue_is_zoned(q)))
@@ -1756,17 +2230,14 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
* Ensure that all memory allocations in this context are done as if
* GFP_NOIO was specified.
*/
- args.disk = disk;
- args.nr_zones = (capacity + zone_sectors - 1) >> ilog2(zone_sectors);
noio_flag = memalloc_noio_save();
- ret = disk_revalidate_zone_resources(disk, args.nr_zones);
+ ret = disk_revalidate_zone_resources(disk, &args);
if (ret) {
memalloc_noio_restore(noio_flag);
return ret;
}
- ret = disk->fops->report_zones(disk, 0, UINT_MAX,
- blk_revalidate_zone_cb, &args);
+ ret = disk->fops->report_zones(disk, 0, UINT_MAX, &rep_args);
if (!ret) {
pr_warn("%s: No zones reported\n", disk->disk_name);
ret = -ENODEV;
@@ -1783,20 +2254,14 @@ int blk_revalidate_disk_zones(struct gendisk *disk)
ret = -ENODEV;
}
- /*
- * Set the new disk zone parameters only once the queue is frozen and
- * all I/Os are completed.
- */
if (ret > 0)
- ret = disk_update_zone_resources(disk, &args);
- else
- pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
- if (ret) {
- unsigned int memflags = blk_mq_freeze_queue(q);
+ return disk_update_zone_resources(disk, &args);
- disk_free_zone_resources(disk);
- blk_mq_unfreeze_queue(q, memflags);
- }
+ pr_warn("%s: failed to revalidate zones\n", disk->disk_name);
+
+ memflags = blk_mq_freeze_queue(q);
+ disk_free_zone_resources(disk);
+ blk_mq_unfreeze_queue(q, memflags);
return ret;
}
@@ -1817,6 +2282,7 @@ EXPORT_SYMBOL_GPL(blk_revalidate_disk_zones);
int blk_zone_issue_zeroout(struct block_device *bdev, sector_t sector,
sector_t nr_sects, gfp_t gfp_mask)
{
+ struct gendisk *disk = bdev->bd_disk;
int ret;
if (WARN_ON_ONCE(!bdev_is_zoned(bdev)))
@@ -1832,7 +2298,7 @@ int blk_zone_issue_zeroout(struct block_device *bdev, sector_t sector,
* pointer. Undo this using a report zone to update the zone write
* pointer to the correct current value.
*/
- ret = disk_zone_sync_wp_offset(bdev->bd_disk, sector);
+ ret = disk->fops->report_zones(disk, sector, 1, NULL);
if (ret != 1)
return ret < 0 ? ret : -EIO;
@@ -1851,18 +2317,22 @@ static void queue_zone_wplug_show(struct blk_zone_wplug *zwplug,
unsigned int zwp_wp_offset, zwp_flags;
unsigned int zwp_zone_no, zwp_ref;
unsigned int zwp_bio_list_size;
+ enum blk_zone_cond zwp_cond;
unsigned long flags;
spin_lock_irqsave(&zwplug->lock, flags);
zwp_zone_no = zwplug->zone_no;
zwp_flags = zwplug->flags;
zwp_ref = refcount_read(&zwplug->ref);
+ zwp_cond = zwplug->cond;
zwp_wp_offset = zwplug->wp_offset;
zwp_bio_list_size = bio_list_size(&zwplug->bio_list);
spin_unlock_irqrestore(&zwplug->lock, flags);
- seq_printf(m, "%u 0x%x %u %u %u\n", zwp_zone_no, zwp_flags, zwp_ref,
- zwp_wp_offset, zwp_bio_list_size);
+ seq_printf(m,
+ "Zone no: %u, flags: 0x%x, ref: %u, cond: %s, wp ofst: %u, pending BIO: %u\n",
+ zwp_zone_no, zwp_flags, zwp_ref, blk_zone_cond_str(zwp_cond),
+ zwp_wp_offset, zwp_bio_list_size);
}
int queue_zone_wplugs_show(void *data, struct seq_file *m)
diff --git a/block/blk.h b/block/blk.h
index 170794632135..e4c433f62dfc 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -11,8 +11,7 @@
#include <xen/xen.h>
#include "blk-crypto-internal.h"
-struct elevator_type;
-struct elevator_tags;
+struct elv_change_ctx;
/*
* Default upper limit for the software max_sectors limit used for regular I/Os.
@@ -333,8 +332,8 @@ bool blk_bio_list_merge(struct request_queue *q, struct list_head *list,
bool blk_insert_flush(struct request *rq);
-void elv_update_nr_hw_queues(struct request_queue *q, struct elevator_type *e,
- struct elevator_tags *t);
+void elv_update_nr_hw_queues(struct request_queue *q,
+ struct elv_change_ctx *ctx);
void elevator_set_default(struct request_queue *q);
void elevator_set_none(struct request_queue *q);
@@ -377,7 +376,7 @@ static inline bool bio_may_need_split(struct bio *bio,
if (bio->bi_vcnt != 1)
return true;
return bio->bi_io_vec->bv_len + bio->bi_io_vec->bv_offset >
- lim->min_segment_size;
+ lim->max_fast_segment_size;
}
/**
@@ -489,10 +488,24 @@ static inline bool blk_req_bio_is_zone_append(struct request *rq,
void blk_zone_write_plug_bio_merged(struct bio *bio);
void blk_zone_write_plug_init_request(struct request *rq);
void blk_zone_append_update_request_bio(struct request *rq, struct bio *bio);
+void blk_zone_mgmt_bio_endio(struct bio *bio);
void blk_zone_write_plug_bio_endio(struct bio *bio);
static inline void blk_zone_bio_endio(struct bio *bio)
{
/*
+ * Zone management BIOs may impact zone write plugs (e.g. a zone reset
+ * changes a zone write plug zone write pointer offset), but these
+ * operation do not go through zone write plugging as they may operate
+ * on zones that do not have a zone write
+ * plug. blk_zone_mgmt_bio_endio() handles the potential changes to zone
+ * write plugs that are present.
+ */
+ if (op_is_zone_mgmt(bio_op(bio))) {
+ blk_zone_mgmt_bio_endio(bio);
+ return;
+ }
+
+ /*
* For write BIOs to zoned devices, signal the completion of the BIO so
* that the next write BIO can be submitted by zone write plugging.
*/
diff --git a/block/elevator.c b/block/elevator.c
index e2ebfbf107b3..5b37ef44f52d 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -45,19 +45,6 @@
#include "blk-wbt.h"
#include "blk-cgroup.h"
-/* Holding context data for changing elevator */
-struct elv_change_ctx {
- const char *name;
- bool no_uevent;
-
- /* for unregistering old elevator */
- struct elevator_queue *old;
- /* for registering new elevator */
- struct elevator_queue *new;
- /* holds sched tags data */
- struct elevator_tags *et;
-};
-
static DEFINE_SPINLOCK(elv_list_lock);
static LIST_HEAD(elv_list);
@@ -134,7 +121,7 @@ static struct elevator_type *elevator_find_get(const char *name)
static const struct kobj_type elv_ktype;
struct elevator_queue *elevator_alloc(struct request_queue *q,
- struct elevator_type *e, struct elevator_tags *et)
+ struct elevator_type *e, struct elevator_resources *res)
{
struct elevator_queue *eq;
@@ -147,7 +134,8 @@ struct elevator_queue *elevator_alloc(struct request_queue *q,
kobject_init(&eq->kobj, &elv_ktype);
mutex_init(&eq->sysfs_lock);
hash_init(eq->hash);
- eq->et = et;
+ eq->et = res->et;
+ eq->elevator_data = res->data;
return eq;
}
@@ -593,7 +581,7 @@ static int elevator_switch(struct request_queue *q, struct elv_change_ctx *ctx)
}
if (new_e) {
- ret = blk_mq_init_sched(q, new_e, ctx->et);
+ ret = blk_mq_init_sched(q, new_e, &ctx->res);
if (ret)
goto out_unfreeze;
ctx->new = q->elevator;
@@ -617,7 +605,8 @@ out_unfreeze:
return ret;
}
-static void elv_exit_and_release(struct request_queue *q)
+static void elv_exit_and_release(struct elv_change_ctx *ctx,
+ struct request_queue *q)
{
struct elevator_queue *e;
unsigned memflags;
@@ -629,7 +618,7 @@ static void elv_exit_and_release(struct request_queue *q)
mutex_unlock(&q->elevator_lock);
blk_mq_unfreeze_queue(q, memflags);
if (e) {
- blk_mq_free_sched_tags(e->et, q->tag_set);
+ blk_mq_free_sched_res(&ctx->res, ctx->type, q->tag_set);
kobject_put(&e->kobj);
}
}
@@ -640,11 +629,15 @@ static int elevator_change_done(struct request_queue *q,
int ret = 0;
if (ctx->old) {
+ struct elevator_resources res = {
+ .et = ctx->old->et,
+ .data = ctx->old->elevator_data
+ };
bool enable_wbt = test_bit(ELEVATOR_FLAG_ENABLE_WBT_ON_EXIT,
&ctx->old->flags);
elv_unregister_queue(q, ctx->old);
- blk_mq_free_sched_tags(ctx->old->et, q->tag_set);
+ blk_mq_free_sched_res(&res, ctx->old->type, q->tag_set);
kobject_put(&ctx->old->kobj);
if (enable_wbt)
wbt_enable_default(q->disk);
@@ -652,7 +645,7 @@ static int elevator_change_done(struct request_queue *q,
if (ctx->new) {
ret = elv_register_queue(q, ctx->new, !ctx->no_uevent);
if (ret)
- elv_exit_and_release(q);
+ elv_exit_and_release(ctx, q);
}
return ret;
}
@@ -669,10 +662,10 @@ static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx)
lockdep_assert_held(&set->update_nr_hwq_lock);
if (strncmp(ctx->name, "none", 4)) {
- ctx->et = blk_mq_alloc_sched_tags(set, set->nr_hw_queues,
- blk_mq_default_nr_requests(set));
- if (!ctx->et)
- return -ENOMEM;
+ ret = blk_mq_alloc_sched_res(q, ctx->type, &ctx->res,
+ set->nr_hw_queues);
+ if (ret)
+ return ret;
}
memflags = blk_mq_freeze_queue(q);
@@ -693,11 +686,12 @@ static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx)
blk_mq_unfreeze_queue(q, memflags);
if (!ret)
ret = elevator_change_done(q, ctx);
+
/*
- * Free sched tags if it's allocated but we couldn't switch elevator.
+ * Free sched resource if it's allocated but we couldn't switch elevator.
*/
- if (ctx->et && !ctx->new)
- blk_mq_free_sched_tags(ctx->et, set);
+ if (!ctx->new)
+ blk_mq_free_sched_res(&ctx->res, ctx->type, set);
return ret;
}
@@ -706,32 +700,29 @@ static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx)
* The I/O scheduler depends on the number of hardware queues, this forces a
* reattachment when nr_hw_queues changes.
*/
-void elv_update_nr_hw_queues(struct request_queue *q, struct elevator_type *e,
- struct elevator_tags *t)
+void elv_update_nr_hw_queues(struct request_queue *q,
+ struct elv_change_ctx *ctx)
{
struct blk_mq_tag_set *set = q->tag_set;
- struct elv_change_ctx ctx = {};
int ret = -ENODEV;
WARN_ON_ONCE(q->mq_freeze_depth == 0);
- if (e && !blk_queue_dying(q) && blk_queue_registered(q)) {
- ctx.name = e->elevator_name;
- ctx.et = t;
-
+ if (ctx->type && !blk_queue_dying(q) && blk_queue_registered(q)) {
mutex_lock(&q->elevator_lock);
/* force to reattach elevator after nr_hw_queue is updated */
- ret = elevator_switch(q, &ctx);
+ ret = elevator_switch(q, ctx);
mutex_unlock(&q->elevator_lock);
}
blk_mq_unfreeze_queue_nomemrestore(q);
if (!ret)
- WARN_ON_ONCE(elevator_change_done(q, &ctx));
+ WARN_ON_ONCE(elevator_change_done(q, ctx));
+
/*
- * Free sched tags if it's allocated but we couldn't switch elevator.
+ * Free sched resource if it's allocated but we couldn't switch elevator.
*/
- if (t && !ctx.new)
- blk_mq_free_sched_tags(t, set);
+ if (!ctx->new)
+ blk_mq_free_sched_res(&ctx->res, ctx->type, set);
}
/*
@@ -745,7 +736,6 @@ void elevator_set_default(struct request_queue *q)
.no_uevent = true,
};
int err;
- struct elevator_type *e;
/* now we allow to switch elevator */
blk_queue_flag_clear(QUEUE_FLAG_NO_ELV_SWITCH, q);
@@ -758,8 +748,8 @@ void elevator_set_default(struct request_queue *q)
* have multiple queues or mq-deadline is not available, default
* to "none".
*/
- e = elevator_find_get(ctx.name);
- if (!e)
+ ctx.type = elevator_find_get(ctx.name);
+ if (!ctx.type)
return;
if ((q->nr_hw_queues == 1 ||
@@ -769,7 +759,7 @@ void elevator_set_default(struct request_queue *q)
pr_warn("\"%s\" elevator initialization, failed %d, falling back to \"none\"\n",
ctx.name, err);
}
- elevator_put(e);
+ elevator_put(ctx.type);
}
void elevator_set_none(struct request_queue *q)
@@ -818,6 +808,7 @@ ssize_t elv_iosched_store(struct gendisk *disk, const char *buf,
ctx.name = strstrip(elevator_name);
elv_iosched_load_module(ctx.name);
+ ctx.type = elevator_find_get(ctx.name);
down_read(&set->update_nr_hwq_lock);
if (!blk_queue_no_elv_switch(q)) {
@@ -828,6 +819,9 @@ ssize_t elv_iosched_store(struct gendisk *disk, const char *buf,
ret = -ENOENT;
}
up_read(&set->update_nr_hwq_lock);
+
+ if (ctx.type)
+ elevator_put(ctx.type);
return ret;
}
diff --git a/block/elevator.h b/block/elevator.h
index c4d20155065e..a9d092c5a9e8 100644
--- a/block/elevator.h
+++ b/block/elevator.h
@@ -32,12 +32,36 @@ struct elevator_tags {
struct blk_mq_tags *tags[];
};
+struct elevator_resources {
+ /* holds elevator data */
+ void *data;
+ /* holds elevator tags */
+ struct elevator_tags *et;
+};
+
+/* Holding context data for changing elevator */
+struct elv_change_ctx {
+ const char *name;
+ bool no_uevent;
+
+ /* for unregistering old elevator */
+ struct elevator_queue *old;
+ /* for registering new elevator */
+ struct elevator_queue *new;
+ /* store elevator type */
+ struct elevator_type *type;
+ /* store elevator resources */
+ struct elevator_resources res;
+};
+
struct elevator_mq_ops {
int (*init_sched)(struct request_queue *, struct elevator_queue *);
void (*exit_sched)(struct elevator_queue *);
int (*init_hctx)(struct blk_mq_hw_ctx *, unsigned int);
void (*exit_hctx)(struct blk_mq_hw_ctx *, unsigned int);
void (*depth_updated)(struct request_queue *);
+ void *(*alloc_sched_data)(struct request_queue *);
+ void (*free_sched_data)(void *);
bool (*allow_merge)(struct request_queue *, struct request *, struct bio *);
bool (*bio_merge)(struct request_queue *, struct bio *, unsigned int);
@@ -147,7 +171,6 @@ extern bool elv_attempt_insert_merge(struct request_queue *, struct request *,
struct list_head *);
extern struct request *elv_former_request(struct request_queue *, struct request *);
extern struct request *elv_latter_request(struct request_queue *, struct request *);
-void elevator_init_mq(struct request_queue *q);
/*
* io scheduler registration
@@ -163,7 +186,7 @@ ssize_t elv_iosched_store(struct gendisk *disk, const char *page, size_t count);
extern bool elv_bio_merge_ok(struct request *, struct bio *);
struct elevator_queue *elevator_alloc(struct request_queue *,
- struct elevator_type *, struct elevator_tags *);
+ struct elevator_type *, struct elevator_resources *);
/*
* Helper functions.
diff --git a/block/fops.c b/block/fops.c
index 5e3db9fead77..4dad9c2d5796 100644
--- a/block/fops.c
+++ b/block/fops.c
@@ -540,12 +540,13 @@ const struct address_space_operations def_blk_aops = {
#else /* CONFIG_BUFFER_HEAD */
static int blkdev_read_folio(struct file *file, struct folio *folio)
{
- return iomap_read_folio(folio, &blkdev_iomap_ops);
+ iomap_bio_read_folio(folio, &blkdev_iomap_ops);
+ return 0;
}
static void blkdev_readahead(struct readahead_control *rac)
{
- iomap_readahead(rac, &blkdev_iomap_ops);
+ iomap_bio_readahead(rac, &blkdev_iomap_ops);
}
static ssize_t blkdev_writeback_range(struct iomap_writepage_ctx *wpc,
diff --git a/block/genhd.c b/block/genhd.c
index 9bbc38d12792..69c75117ba2c 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -90,7 +90,7 @@ bool set_capacity_and_notify(struct gendisk *disk, sector_t size)
(disk->flags & GENHD_FL_HIDDEN))
return false;
- pr_info("%s: detected capacity change from %lld to %lld\n",
+ pr_info_ratelimited("%s: detected capacity change from %lld to %lld\n",
disk->disk_name, capacity, size);
/*
@@ -795,11 +795,11 @@ static void disable_elv_switch(struct request_queue *q)
* partitions associated with the gendisk, and unregisters the associated
* request_queue.
*
- * This is the counter to the respective __device_add_disk() call.
+ * This is the counter to the respective device_add_disk() call.
*
* The final removal of the struct gendisk happens when its refcount reaches 0
* with put_disk(), which should be called after del_gendisk(), if
- * __device_add_disk() was used.
+ * device_add_disk() was used.
*
* Drivers exist which depend on the release of the gendisk to be synchronous,
* it should not be deferred.
@@ -1265,7 +1265,7 @@ static const struct attribute_group *disk_attr_groups[] = {
*
* This function releases all allocated resources of the gendisk.
*
- * Drivers which used __device_add_disk() have a gendisk with a request_queue
+ * Drivers which used device_add_disk() have a gendisk with a request_queue
* assigned. Since the request_queue sits on top of the gendisk for these
* drivers we also call blk_put_queue() for them, and we expect the
* request_queue refcount to reach 0 at this point, and so the request_queue
diff --git a/block/ioctl.c b/block/ioctl.c
index d7489a56b33c..2b3ab9bfc413 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -581,6 +581,7 @@ static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,
case BLKGETDISKSEQ:
return put_u64(argp, bdev->bd_disk->diskseq);
case BLKREPORTZONE:
+ case BLKREPORTZONEV2:
return blkdev_report_zones_ioctl(bdev, cmd, arg);
case BLKRESETZONE:
case BLKOPENZONE:
@@ -691,6 +692,7 @@ long blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
/* Incompatible alignment on i386 */
case BLKTRACESETUP:
+ case BLKTRACESETUP2:
return blk_trace_ioctl(bdev, cmd, argp);
default:
break;
@@ -769,14 +771,16 @@ struct blk_iou_cmd {
bool nowait;
};
-static void blk_cmd_complete(struct io_uring_cmd *cmd, unsigned int issue_flags)
+static void blk_cmd_complete(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
struct blk_iou_cmd *bic = io_uring_cmd_to_pdu(cmd, struct blk_iou_cmd);
if (bic->res == -EAGAIN && bic->nowait)
io_uring_cmd_issue_blocking(cmd);
else
- io_uring_cmd_done(cmd, bic->res, issue_flags);
+ io_uring_cmd_done(cmd, bic->res,
+ IO_URING_CMD_TASK_WORK_ISSUE_FLAGS);
}
static void bio_cmd_bio_end_io(struct bio *bio)
diff --git a/block/kyber-iosched.c b/block/kyber-iosched.c
index 18efd6ef2a2b..c1b36ffd19ce 100644
--- a/block/kyber-iosched.c
+++ b/block/kyber-iosched.c
@@ -409,30 +409,42 @@ static void kyber_depth_updated(struct request_queue *q)
static int kyber_init_sched(struct request_queue *q, struct elevator_queue *eq)
{
- struct kyber_queue_data *kqd;
-
- kqd = kyber_queue_data_alloc(q);
- if (IS_ERR(kqd))
- return PTR_ERR(kqd);
-
blk_stat_enable_accounting(q);
blk_queue_flag_clear(QUEUE_FLAG_SQ_SCHED, q);
- eq->elevator_data = kqd;
q->elevator = eq;
kyber_depth_updated(q);
return 0;
}
+static void *kyber_alloc_sched_data(struct request_queue *q)
+{
+ struct kyber_queue_data *kqd;
+
+ kqd = kyber_queue_data_alloc(q);
+ if (IS_ERR(kqd))
+ return NULL;
+
+ return kqd;
+}
+
static void kyber_exit_sched(struct elevator_queue *e)
{
struct kyber_queue_data *kqd = e->elevator_data;
- int i;
timer_shutdown_sync(&kqd->timer);
blk_stat_disable_accounting(kqd->q);
+}
+
+static void kyber_free_sched_data(void *elv_data)
+{
+ struct kyber_queue_data *kqd = elv_data;
+ int i;
+
+ if (!kqd)
+ return;
for (i = 0; i < KYBER_NUM_DOMAINS; i++)
sbitmap_queue_free(&kqd->domain_tokens[i]);
@@ -1004,6 +1016,8 @@ static struct elevator_type kyber_sched = {
.exit_sched = kyber_exit_sched,
.init_hctx = kyber_init_hctx,
.exit_hctx = kyber_exit_hctx,
+ .alloc_sched_data = kyber_alloc_sched_data,
+ .free_sched_data = kyber_free_sched_data,
.limit_depth = kyber_limit_depth,
.bio_merge = kyber_bio_merge,
.prepare_request = kyber_prepare_request,
diff --git a/block/mq-deadline.c b/block/mq-deadline.c
index 3e741d33142d..3e3719093aec 100644
--- a/block/mq-deadline.c
+++ b/block/mq-deadline.c
@@ -71,7 +71,6 @@ struct io_stats_per_prio {
* present on both sort_list[] and fifo_list[].
*/
struct dd_per_prio {
- struct list_head dispatch;
struct rb_root sort_list[DD_DIR_COUNT];
struct list_head fifo_list[DD_DIR_COUNT];
/* Position of the most recently dispatched request. */
@@ -84,6 +83,7 @@ struct deadline_data {
* run time data
*/
+ struct list_head dispatch;
struct dd_per_prio per_prio[DD_PRIO_COUNT];
/* Data direction of latest dispatched request. */
@@ -306,6 +306,19 @@ static bool started_after(struct deadline_data *dd, struct request *rq,
return time_after(start_time, latest_start);
}
+static struct request *dd_start_request(struct deadline_data *dd,
+ enum dd_data_dir data_dir,
+ struct request *rq)
+{
+ u8 ioprio_class = dd_rq_ioclass(rq);
+ enum dd_prio prio = ioprio_class_to_prio[ioprio_class];
+
+ dd->per_prio[prio].latest_pos[data_dir] = blk_rq_pos(rq);
+ dd->per_prio[prio].stats.dispatched++;
+ rq->rq_flags |= RQF_STARTED;
+ return rq;
+}
+
/*
* deadline_dispatch_requests selects the best request according to
* read/write expire, fifo_batch, etc and with a start time <= @latest_start.
@@ -316,21 +329,9 @@ static struct request *__dd_dispatch_request(struct deadline_data *dd,
{
struct request *rq, *next_rq;
enum dd_data_dir data_dir;
- enum dd_prio prio;
- u8 ioprio_class;
lockdep_assert_held(&dd->lock);
- if (!list_empty(&per_prio->dispatch)) {
- rq = list_first_entry(&per_prio->dispatch, struct request,
- queuelist);
- if (started_after(dd, rq, latest_start))
- return NULL;
- list_del_init(&rq->queuelist);
- data_dir = rq_data_dir(rq);
- goto done;
- }
-
/*
* batches are currently reads XOR writes
*/
@@ -410,13 +411,7 @@ dispatch_request:
*/
dd->batching++;
deadline_move_request(dd, per_prio, rq);
-done:
- ioprio_class = dd_rq_ioclass(rq);
- prio = ioprio_class_to_prio[ioprio_class];
- dd->per_prio[prio].latest_pos[data_dir] = blk_rq_pos(rq);
- dd->per_prio[prio].stats.dispatched++;
- rq->rq_flags |= RQF_STARTED;
- return rq;
+ return dd_start_request(dd, data_dir, rq);
}
/*
@@ -463,6 +458,14 @@ static struct request *dd_dispatch_request(struct blk_mq_hw_ctx *hctx)
enum dd_prio prio;
spin_lock(&dd->lock);
+
+ if (!list_empty(&dd->dispatch)) {
+ rq = list_first_entry(&dd->dispatch, struct request, queuelist);
+ list_del_init(&rq->queuelist);
+ dd_start_request(dd, rq_data_dir(rq), rq);
+ goto unlock;
+ }
+
rq = dd_dispatch_prio_aged_requests(dd, now);
if (rq)
goto unlock;
@@ -551,10 +554,10 @@ static int dd_init_sched(struct request_queue *q, struct elevator_queue *eq)
eq->elevator_data = dd;
+ INIT_LIST_HEAD(&dd->dispatch);
for (prio = 0; prio <= DD_PRIO_MAX; prio++) {
struct dd_per_prio *per_prio = &dd->per_prio[prio];
- INIT_LIST_HEAD(&per_prio->dispatch);
INIT_LIST_HEAD(&per_prio->fifo_list[DD_READ]);
INIT_LIST_HEAD(&per_prio->fifo_list[DD_WRITE]);
per_prio->sort_list[DD_READ] = RB_ROOT;
@@ -658,7 +661,7 @@ static void dd_insert_request(struct blk_mq_hw_ctx *hctx, struct request *rq,
trace_block_rq_insert(rq);
if (flags & BLK_MQ_INSERT_AT_HEAD) {
- list_add(&rq->queuelist, &per_prio->dispatch);
+ list_add(&rq->queuelist, &dd->dispatch);
rq->fifo_time = jiffies;
} else {
deadline_add_rq_rb(per_prio, rq);
@@ -725,8 +728,7 @@ static void dd_finish_request(struct request *rq)
static bool dd_has_work_for_prio(struct dd_per_prio *per_prio)
{
- return !list_empty_careful(&per_prio->dispatch) ||
- !list_empty_careful(&per_prio->fifo_list[DD_READ]) ||
+ return !list_empty_careful(&per_prio->fifo_list[DD_READ]) ||
!list_empty_careful(&per_prio->fifo_list[DD_WRITE]);
}
@@ -735,6 +737,9 @@ static bool dd_has_work(struct blk_mq_hw_ctx *hctx)
struct deadline_data *dd = hctx->queue->elevator->elevator_data;
enum dd_prio prio;
+ if (!list_empty_careful(&dd->dispatch))
+ return true;
+
for (prio = 0; prio <= DD_PRIO_MAX; prio++)
if (dd_has_work_for_prio(&dd->per_prio[prio]))
return true;
@@ -943,49 +948,39 @@ static int dd_owned_by_driver_show(void *data, struct seq_file *m)
return 0;
}
-#define DEADLINE_DISPATCH_ATTR(prio) \
-static void *deadline_dispatch##prio##_start(struct seq_file *m, \
- loff_t *pos) \
- __acquires(&dd->lock) \
-{ \
- struct request_queue *q = m->private; \
- struct deadline_data *dd = q->elevator->elevator_data; \
- struct dd_per_prio *per_prio = &dd->per_prio[prio]; \
- \
- spin_lock(&dd->lock); \
- return seq_list_start(&per_prio->dispatch, *pos); \
-} \
- \
-static void *deadline_dispatch##prio##_next(struct seq_file *m, \
- void *v, loff_t *pos) \
-{ \
- struct request_queue *q = m->private; \
- struct deadline_data *dd = q->elevator->elevator_data; \
- struct dd_per_prio *per_prio = &dd->per_prio[prio]; \
- \
- return seq_list_next(v, &per_prio->dispatch, pos); \
-} \
- \
-static void deadline_dispatch##prio##_stop(struct seq_file *m, void *v) \
- __releases(&dd->lock) \
-{ \
- struct request_queue *q = m->private; \
- struct deadline_data *dd = q->elevator->elevator_data; \
- \
- spin_unlock(&dd->lock); \
-} \
- \
-static const struct seq_operations deadline_dispatch##prio##_seq_ops = { \
- .start = deadline_dispatch##prio##_start, \
- .next = deadline_dispatch##prio##_next, \
- .stop = deadline_dispatch##prio##_stop, \
- .show = blk_mq_debugfs_rq_show, \
+static void *deadline_dispatch_start(struct seq_file *m, loff_t *pos)
+ __acquires(&dd->lock)
+{
+ struct request_queue *q = m->private;
+ struct deadline_data *dd = q->elevator->elevator_data;
+
+ spin_lock(&dd->lock);
+ return seq_list_start(&dd->dispatch, *pos);
+}
+
+static void *deadline_dispatch_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct request_queue *q = m->private;
+ struct deadline_data *dd = q->elevator->elevator_data;
+
+ return seq_list_next(v, &dd->dispatch, pos);
+}
+
+static void deadline_dispatch_stop(struct seq_file *m, void *v)
+ __releases(&dd->lock)
+{
+ struct request_queue *q = m->private;
+ struct deadline_data *dd = q->elevator->elevator_data;
+
+ spin_unlock(&dd->lock);
}
-DEADLINE_DISPATCH_ATTR(0);
-DEADLINE_DISPATCH_ATTR(1);
-DEADLINE_DISPATCH_ATTR(2);
-#undef DEADLINE_DISPATCH_ATTR
+static const struct seq_operations deadline_dispatch_seq_ops = {
+ .start = deadline_dispatch_start,
+ .next = deadline_dispatch_next,
+ .stop = deadline_dispatch_stop,
+ .show = blk_mq_debugfs_rq_show,
+};
#define DEADLINE_QUEUE_DDIR_ATTRS(name) \
{#name "_fifo_list", 0400, \
@@ -1008,9 +1003,7 @@ static const struct blk_mq_debugfs_attr deadline_queue_debugfs_attrs[] = {
{"batching", 0400, deadline_batching_show},
{"starved", 0400, deadline_starved_show},
{"async_depth", 0400, dd_async_depth_show},
- {"dispatch0", 0400, .seq_ops = &deadline_dispatch0_seq_ops},
- {"dispatch1", 0400, .seq_ops = &deadline_dispatch1_seq_ops},
- {"dispatch2", 0400, .seq_ops = &deadline_dispatch2_seq_ops},
+ {"dispatch", 0400, .seq_ops = &deadline_dispatch_seq_ops},
{"owned_by_driver", 0400, dd_owned_by_driver_show},
{"queued", 0400, dd_queued_show},
{},
diff --git a/block/partitions/efi.c b/block/partitions/efi.c
index 7acba66eed48..638261e9f2fb 100644
--- a/block/partitions/efi.c
+++ b/block/partitions/efi.c
@@ -215,8 +215,7 @@ check_hybrid:
sz = le32_to_cpu(mbr->partition_record[part].size_in_lba);
if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF)
pr_debug("GPT: mbr size in lba (%u) different than whole disk (%u).\n",
- sz, min_t(uint32_t,
- total_sectors - 1, 0xFFFFFFFF));
+ sz, (uint32_t)min(total_sectors - 1, 0xFFFFFFFF));
}
done:
return ret;
diff --git a/crypto/Kconfig b/crypto/Kconfig
index a04595f9d0ca..2e5b195b1b06 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -25,7 +25,7 @@ menu "Crypto core or helper"
config CRYPTO_FIPS
bool "FIPS 200 compliance"
- depends on (CRYPTO_ANSI_CPRNG || CRYPTO_DRBG) && CRYPTO_SELFTESTS
+ depends on CRYPTO_DRBG && CRYPTO_SELFTESTS
depends on (MODULE_SIG || !MODULES)
help
This option enables the fips boot option which is
@@ -696,7 +696,7 @@ config CRYPTO_ECB
config CRYPTO_HCTR2
tristate "HCTR2"
select CRYPTO_XCTR
- select CRYPTO_POLYVAL
+ select CRYPTO_LIB_POLYVAL
select CRYPTO_MANAGER
help
HCTR2 length-preserving encryption mode
@@ -881,6 +881,7 @@ menu "Hashes, digests, and MACs"
config CRYPTO_BLAKE2B
tristate "BLAKE2b"
select CRYPTO_HASH
+ select CRYPTO_LIB_BLAKE2B
help
BLAKE2b cryptographic hash function (RFC 7693)
@@ -947,16 +948,6 @@ config CRYPTO_MICHAEL_MIC
This algorithm is required for TKIP, but it should not be used for
other purposes because of the weakness of the algorithm.
-config CRYPTO_POLYVAL
- tristate
- select CRYPTO_HASH
- select CRYPTO_LIB_GF128MUL
- help
- POLYVAL hash function for HCTR2
-
- This is used in HCTR2. It is not a general-purpose
- cryptographic hash function.
-
config CRYPTO_RMD160
tristate "RIPEMD-160"
select CRYPTO_HASH
@@ -1005,6 +996,7 @@ config CRYPTO_SHA512
config CRYPTO_SHA3
tristate "SHA-3"
select CRYPTO_HASH
+ select CRYPTO_LIB_SHA3
help
SHA-3 secure hash algorithms (FIPS 202, ISO/IEC 10118-3)
@@ -1169,17 +1161,6 @@ endmenu
menu "Random number generation"
-config CRYPTO_ANSI_CPRNG
- tristate "ANSI PRNG (Pseudo Random Number Generator)"
- select CRYPTO_AES
- select CRYPTO_RNG
- help
- Pseudo RNG (random number generator) (ANSI X9.31 Appendix A.2.4)
-
- This uses the AES cipher algorithm.
-
- Note that this option must be enabled if CRYPTO_FIPS is selected
-
menuconfig CRYPTO_DRBG_MENU
tristate "NIST SP800-90A DRBG (Deterministic Random Bit Generator)"
help
@@ -1205,8 +1186,7 @@ config CRYPTO_DRBG_HASH
config CRYPTO_DRBG_CTR
bool "CTR_DRBG"
- select CRYPTO_AES
- select CRYPTO_CTR
+ select CRYPTO_DF80090A
help
CTR_DRBG variant as defined in NIST SP800-90A.
@@ -1342,6 +1322,11 @@ config CRYPTO_KDF800108_CTR
select CRYPTO_HMAC
select CRYPTO_SHA256
+config CRYPTO_DF80090A
+ tristate
+ select CRYPTO_AES
+ select CRYPTO_CTR
+
endmenu
menu "Userspace interface"
diff --git a/crypto/Makefile b/crypto/Makefile
index e430e6e99b6a..16a35649dd91 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -78,13 +78,12 @@ obj-$(CONFIG_CRYPTO_RMD160) += rmd160.o
obj-$(CONFIG_CRYPTO_SHA1) += sha1.o
obj-$(CONFIG_CRYPTO_SHA256) += sha256.o
obj-$(CONFIG_CRYPTO_SHA512) += sha512.o
-obj-$(CONFIG_CRYPTO_SHA3) += sha3_generic.o
+obj-$(CONFIG_CRYPTO_SHA3) += sha3.o
obj-$(CONFIG_CRYPTO_SM3_GENERIC) += sm3_generic.o
obj-$(CONFIG_CRYPTO_STREEBOG) += streebog_generic.o
obj-$(CONFIG_CRYPTO_WP512) += wp512.o
CFLAGS_wp512.o := $(call cc-option,-fno-schedule-insns) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149
-obj-$(CONFIG_CRYPTO_BLAKE2B) += blake2b_generic.o
-CFLAGS_blake2b_generic.o := -Wframe-larger-than=4096 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105930
+obj-$(CONFIG_CRYPTO_BLAKE2B) += blake2b.o
obj-$(CONFIG_CRYPTO_ECB) += ecb.o
obj-$(CONFIG_CRYPTO_CBC) += cbc.o
obj-$(CONFIG_CRYPTO_PCBC) += pcbc.o
@@ -163,7 +162,6 @@ obj-$(CONFIG_CRYPTO_LZ4HC) += lz4hc.o
obj-$(CONFIG_CRYPTO_XXHASH) += xxhash_generic.o
obj-$(CONFIG_CRYPTO_842) += 842.o
obj-$(CONFIG_CRYPTO_RNG2) += rng.o
-obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o
obj-$(CONFIG_CRYPTO_DRBG) += drbg.o
obj-$(CONFIG_CRYPTO_JITTERENTROPY) += jitterentropy_rng.o
CFLAGS_jitterentropy.o = -O0
@@ -173,7 +171,6 @@ jitterentropy_rng-y := jitterentropy.o jitterentropy-kcapi.o
obj-$(CONFIG_CRYPTO_JITTERENTROPY_TESTINTERFACE) += jitterentropy-testing.o
obj-$(CONFIG_CRYPTO_BENCHMARK) += tcrypt.o
obj-$(CONFIG_CRYPTO_GHASH) += ghash-generic.o
-obj-$(CONFIG_CRYPTO_POLYVAL) += polyval-generic.o
obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o
obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o
obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o
@@ -209,4 +206,6 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
#
obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o
+obj-$(CONFIG_CRYPTO_DF80090A) += df_sp80090a.o
+
obj-$(CONFIG_CRYPTO_KRB5) += krb5/
diff --git a/crypto/aead.c b/crypto/aead.c
index 5d14b775036e..08d44c5e5c33 100644
--- a/crypto/aead.c
+++ b/crypto/aead.c
@@ -120,6 +120,7 @@ static int crypto_aead_init_tfm(struct crypto_tfm *tfm)
struct aead_alg *alg = crypto_aead_alg(aead);
crypto_aead_set_flags(aead, CRYPTO_TFM_NEED_KEY);
+ crypto_aead_set_reqsize(aead, crypto_tfm_alg_reqsize(tfm));
aead->authsize = alg->maxauthsize;
@@ -204,6 +205,25 @@ struct crypto_aead *crypto_alloc_aead(const char *alg_name, u32 type, u32 mask)
}
EXPORT_SYMBOL_GPL(crypto_alloc_aead);
+struct crypto_sync_aead *crypto_alloc_sync_aead(const char *alg_name, u32 type, u32 mask)
+{
+ struct crypto_aead *tfm;
+
+ /* Only sync algorithms are allowed. */
+ mask |= CRYPTO_ALG_ASYNC;
+ type &= ~(CRYPTO_ALG_ASYNC);
+
+ tfm = crypto_alloc_tfm(alg_name, &crypto_aead_type, type, mask);
+
+ if (!IS_ERR(tfm) && WARN_ON(crypto_aead_reqsize(tfm) > MAX_SYNC_AEAD_REQSIZE)) {
+ crypto_free_aead(tfm);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return (struct crypto_sync_aead *)tfm;
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_sync_aead);
+
int crypto_has_aead(const char *alg_name, u32 type, u32 mask)
{
return crypto_type_has_alg(alg_name, &crypto_aead_type, type, mask);
diff --git a/crypto/aegis128-neon.c b/crypto/aegis128-neon.c
index 9ee50549e823..b41807e63bd3 100644
--- a/crypto/aegis128-neon.c
+++ b/crypto/aegis128-neon.c
@@ -4,7 +4,7 @@
*/
#include <asm/cpufeature.h>
-#include <asm/neon.h>
+#include <asm/simd.h>
#include "aegis.h"
#include "aegis-neon.h"
@@ -24,32 +24,28 @@ void crypto_aegis128_init_simd(struct aegis_state *state,
const union aegis_block *key,
const u8 *iv)
{
- kernel_neon_begin();
- crypto_aegis128_init_neon(state, key, iv);
- kernel_neon_end();
+ scoped_ksimd()
+ crypto_aegis128_init_neon(state, key, iv);
}
void crypto_aegis128_update_simd(struct aegis_state *state, const void *msg)
{
- kernel_neon_begin();
- crypto_aegis128_update_neon(state, msg);
- kernel_neon_end();
+ scoped_ksimd()
+ crypto_aegis128_update_neon(state, msg);
}
void crypto_aegis128_encrypt_chunk_simd(struct aegis_state *state, u8 *dst,
const u8 *src, unsigned int size)
{
- kernel_neon_begin();
- crypto_aegis128_encrypt_chunk_neon(state, dst, src, size);
- kernel_neon_end();
+ scoped_ksimd()
+ crypto_aegis128_encrypt_chunk_neon(state, dst, src, size);
}
void crypto_aegis128_decrypt_chunk_simd(struct aegis_state *state, u8 *dst,
const u8 *src, unsigned int size)
{
- kernel_neon_begin();
- crypto_aegis128_decrypt_chunk_neon(state, dst, src, size);
- kernel_neon_end();
+ scoped_ksimd()
+ crypto_aegis128_decrypt_chunk_neon(state, dst, src, size);
}
int crypto_aegis128_final_simd(struct aegis_state *state,
@@ -58,12 +54,7 @@ int crypto_aegis128_final_simd(struct aegis_state *state,
unsigned int cryptlen,
unsigned int authsize)
{
- int ret;
-
- kernel_neon_begin();
- ret = crypto_aegis128_final_neon(state, tag_xor, assoclen, cryptlen,
- authsize);
- kernel_neon_end();
-
- return ret;
+ scoped_ksimd()
+ return crypto_aegis128_final_neon(state, tag_xor, assoclen,
+ cryptlen, authsize);
}
diff --git a/crypto/af_alg.c b/crypto/af_alg.c
index ca6fdcc6c54a..e468714f539d 100644
--- a/crypto/af_alg.c
+++ b/crypto/af_alg.c
@@ -145,7 +145,7 @@ void af_alg_release_parent(struct sock *sk)
}
EXPORT_SYMBOL_GPL(af_alg_release_parent);
-static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+static int alg_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len)
{
const u32 allowed = CRYPTO_ALG_KERN_DRIVER_ONLY;
struct sock *sk = sock->sk;
@@ -1212,15 +1212,14 @@ struct af_alg_async_req *af_alg_alloc_areq(struct sock *sk,
if (unlikely(!areq))
return ERR_PTR(-ENOMEM);
+ memset(areq, 0, areqlen);
+
ctx->inflight = true;
areq->areqlen = areqlen;
areq->sk = sk;
areq->first_rsgl.sgl.sgt.sgl = areq->first_rsgl.sgl.sgl;
- areq->last_rsgl = NULL;
INIT_LIST_HEAD(&areq->rsgl_list);
- areq->tsgl = NULL;
- areq->tsgl_entries = 0;
return areq;
}
diff --git a/crypto/ahash.c b/crypto/ahash.c
index dfb4f5476428..66492ae75fcf 100644
--- a/crypto/ahash.c
+++ b/crypto/ahash.c
@@ -423,7 +423,11 @@ static int ahash_update_finish(struct ahash_request *req, int err)
req->nbytes += nonzero - blen;
- blen = err < 0 ? 0 : err + nonzero;
+ blen = 0;
+ if (err >= 0) {
+ blen = err + nonzero;
+ err = 0;
+ }
if (ahash_request_isvirt(req))
memcpy(buf, req->svirt + req->nbytes - blen, blen);
else
@@ -661,6 +665,12 @@ int crypto_ahash_import_core(struct ahash_request *req, const void *in)
in);
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
return -ENOKEY;
+ if (crypto_ahash_block_only(tfm)) {
+ unsigned int reqsize = crypto_ahash_reqsize(tfm);
+ u8 *buf = ahash_request_ctx(req);
+
+ buf[reqsize - 1] = 0;
+ }
return crypto_ahash_alg(tfm)->import_core(req, in);
}
EXPORT_SYMBOL_GPL(crypto_ahash_import_core);
@@ -674,10 +684,14 @@ int crypto_ahash_import(struct ahash_request *req, const void *in)
if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
return -ENOKEY;
if (crypto_ahash_block_only(tfm)) {
+ unsigned int plen = crypto_ahash_blocksize(tfm) + 1;
unsigned int reqsize = crypto_ahash_reqsize(tfm);
+ unsigned int ss = crypto_ahash_statesize(tfm);
u8 *buf = ahash_request_ctx(req);
- buf[reqsize - 1] = 0;
+ memcpy(buf + reqsize - plen, in + ss - plen, plen);
+ if (buf[reqsize - 1] >= plen)
+ return -EOVERFLOW;
}
return crypto_ahash_alg(tfm)->import(req, in);
}
diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c
index e3f1a4852737..4d3dfc60a16a 100644
--- a/crypto/algif_hash.c
+++ b/crypto/algif_hash.c
@@ -416,9 +416,8 @@ static int hash_accept_parent_nokey(void *private, struct sock *sk)
if (!ctx)
return -ENOMEM;
- ctx->result = NULL;
+ memset(ctx, 0, len);
ctx->len = len;
- ctx->more = false;
crypto_init_wait(&ctx->wait);
ask->private = ctx;
diff --git a/crypto/algif_rng.c b/crypto/algif_rng.c
index 10c41adac3b1..1a86e40c8372 100644
--- a/crypto/algif_rng.c
+++ b/crypto/algif_rng.c
@@ -248,9 +248,8 @@ static int rng_accept_parent(void *private, struct sock *sk)
if (!ctx)
return -ENOMEM;
+ memset(ctx, 0, len);
ctx->len = len;
- ctx->addtl = NULL;
- ctx->addtl_len = 0;
/*
* No seeding done at that point -- if multiple accepts are
diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c
deleted file mode 100644
index 153523ce6076..000000000000
--- a/crypto/ansi_cprng.c
+++ /dev/null
@@ -1,474 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * PRNG: Pseudo Random Number Generator
- * Based on NIST Recommended PRNG From ANSI X9.31 Appendix A.2.4 using
- * AES 128 cipher
- *
- * (C) Neil Horman <nhorman@tuxdriver.com>
- */
-
-#include <crypto/internal/cipher.h>
-#include <crypto/internal/rng.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/string.h>
-
-#define DEFAULT_PRNG_KEY "0123456789abcdef"
-#define DEFAULT_PRNG_KSZ 16
-#define DEFAULT_BLK_SZ 16
-#define DEFAULT_V_SEED "zaybxcwdveuftgsh"
-
-/*
- * Flags for the prng_context flags field
- */
-
-#define PRNG_FIXED_SIZE 0x1
-#define PRNG_NEED_RESET 0x2
-
-/*
- * Note: DT is our counter value
- * I is our intermediate value
- * V is our seed vector
- * See http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
- * for implementation details
- */
-
-
-struct prng_context {
- spinlock_t prng_lock;
- unsigned char rand_data[DEFAULT_BLK_SZ];
- unsigned char last_rand_data[DEFAULT_BLK_SZ];
- unsigned char DT[DEFAULT_BLK_SZ];
- unsigned char I[DEFAULT_BLK_SZ];
- unsigned char V[DEFAULT_BLK_SZ];
- u32 rand_data_valid;
- struct crypto_cipher *tfm;
- u32 flags;
-};
-
-static int dbg;
-
-static void hexdump(char *note, unsigned char *buf, unsigned int len)
-{
- if (dbg) {
- printk(KERN_CRIT "%s", note);
- print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,
- 16, 1,
- buf, len, false);
- }
-}
-
-#define dbgprint(format, args...) do {\
-if (dbg)\
- printk(format, ##args);\
-} while (0)
-
-static void xor_vectors(unsigned char *in1, unsigned char *in2,
- unsigned char *out, unsigned int size)
-{
- int i;
-
- for (i = 0; i < size; i++)
- out[i] = in1[i] ^ in2[i];
-
-}
-/*
- * Returns DEFAULT_BLK_SZ bytes of random data per call
- * returns 0 if generation succeeded, <0 if something went wrong
- */
-static int _get_more_prng_bytes(struct prng_context *ctx, int cont_test)
-{
- int i;
- unsigned char tmp[DEFAULT_BLK_SZ];
- unsigned char *output = NULL;
-
-
- dbgprint(KERN_CRIT "Calling _get_more_prng_bytes for context %p\n",
- ctx);
-
- hexdump("Input DT: ", ctx->DT, DEFAULT_BLK_SZ);
- hexdump("Input I: ", ctx->I, DEFAULT_BLK_SZ);
- hexdump("Input V: ", ctx->V, DEFAULT_BLK_SZ);
-
- /*
- * This algorithm is a 3 stage state machine
- */
- for (i = 0; i < 3; i++) {
-
- switch (i) {
- case 0:
- /*
- * Start by encrypting the counter value
- * This gives us an intermediate value I
- */
- memcpy(tmp, ctx->DT, DEFAULT_BLK_SZ);
- output = ctx->I;
- hexdump("tmp stage 0: ", tmp, DEFAULT_BLK_SZ);
- break;
- case 1:
-
- /*
- * Next xor I with our secret vector V
- * encrypt that result to obtain our
- * pseudo random data which we output
- */
- xor_vectors(ctx->I, ctx->V, tmp, DEFAULT_BLK_SZ);
- hexdump("tmp stage 1: ", tmp, DEFAULT_BLK_SZ);
- output = ctx->rand_data;
- break;
- case 2:
- /*
- * First check that we didn't produce the same
- * random data that we did last time around through this
- */
- if (!memcmp(ctx->rand_data, ctx->last_rand_data,
- DEFAULT_BLK_SZ)) {
- if (cont_test) {
- panic("cprng %p Failed repetition check!\n",
- ctx);
- }
-
- printk(KERN_ERR
- "ctx %p Failed repetition check!\n",
- ctx);
-
- ctx->flags |= PRNG_NEED_RESET;
- return -EINVAL;
- }
- memcpy(ctx->last_rand_data, ctx->rand_data,
- DEFAULT_BLK_SZ);
-
- /*
- * Lastly xor the random data with I
- * and encrypt that to obtain a new secret vector V
- */
- xor_vectors(ctx->rand_data, ctx->I, tmp,
- DEFAULT_BLK_SZ);
- output = ctx->V;
- hexdump("tmp stage 2: ", tmp, DEFAULT_BLK_SZ);
- break;
- }
-
-
- /* do the encryption */
- crypto_cipher_encrypt_one(ctx->tfm, output, tmp);
-
- }
-
- /*
- * Now update our DT value
- */
- for (i = DEFAULT_BLK_SZ - 1; i >= 0; i--) {
- ctx->DT[i] += 1;
- if (ctx->DT[i] != 0)
- break;
- }
-
- dbgprint("Returning new block for context %p\n", ctx);
- ctx->rand_data_valid = 0;
-
- hexdump("Output DT: ", ctx->DT, DEFAULT_BLK_SZ);
- hexdump("Output I: ", ctx->I, DEFAULT_BLK_SZ);
- hexdump("Output V: ", ctx->V, DEFAULT_BLK_SZ);
- hexdump("New Random Data: ", ctx->rand_data, DEFAULT_BLK_SZ);
-
- return 0;
-}
-
-/* Our exported functions */
-static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx,
- int do_cont_test)
-{
- unsigned char *ptr = buf;
- unsigned int byte_count = (unsigned int)nbytes;
- int err;
-
-
- spin_lock_bh(&ctx->prng_lock);
-
- err = -EINVAL;
- if (ctx->flags & PRNG_NEED_RESET)
- goto done;
-
- /*
- * If the FIXED_SIZE flag is on, only return whole blocks of
- * pseudo random data
- */
- err = -EINVAL;
- if (ctx->flags & PRNG_FIXED_SIZE) {
- if (nbytes < DEFAULT_BLK_SZ)
- goto done;
- byte_count = DEFAULT_BLK_SZ;
- }
-
- /*
- * Return 0 in case of success as mandated by the kernel
- * crypto API interface definition.
- */
- err = 0;
-
- dbgprint(KERN_CRIT "getting %d random bytes for context %p\n",
- byte_count, ctx);
-
-
-remainder:
- if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
- if (_get_more_prng_bytes(ctx, do_cont_test) < 0) {
- memset(buf, 0, nbytes);
- err = -EINVAL;
- goto done;
- }
- }
-
- /*
- * Copy any data less than an entire block
- */
- if (byte_count < DEFAULT_BLK_SZ) {
-empty_rbuf:
- while (ctx->rand_data_valid < DEFAULT_BLK_SZ) {
- *ptr = ctx->rand_data[ctx->rand_data_valid];
- ptr++;
- byte_count--;
- ctx->rand_data_valid++;
- if (byte_count == 0)
- goto done;
- }
- }
-
- /*
- * Now copy whole blocks
- */
- for (; byte_count >= DEFAULT_BLK_SZ; byte_count -= DEFAULT_BLK_SZ) {
- if (ctx->rand_data_valid == DEFAULT_BLK_SZ) {
- if (_get_more_prng_bytes(ctx, do_cont_test) < 0) {
- memset(buf, 0, nbytes);
- err = -EINVAL;
- goto done;
- }
- }
- if (ctx->rand_data_valid > 0)
- goto empty_rbuf;
- memcpy(ptr, ctx->rand_data, DEFAULT_BLK_SZ);
- ctx->rand_data_valid += DEFAULT_BLK_SZ;
- ptr += DEFAULT_BLK_SZ;
- }
-
- /*
- * Now go back and get any remaining partial block
- */
- if (byte_count)
- goto remainder;
-
-done:
- spin_unlock_bh(&ctx->prng_lock);
- dbgprint(KERN_CRIT "returning %d from get_prng_bytes in context %p\n",
- err, ctx);
- return err;
-}
-
-static void free_prng_context(struct prng_context *ctx)
-{
- crypto_free_cipher(ctx->tfm);
-}
-
-static int reset_prng_context(struct prng_context *ctx,
- const unsigned char *key, size_t klen,
- const unsigned char *V, const unsigned char *DT)
-{
- int ret;
- const unsigned char *prng_key;
-
- spin_lock_bh(&ctx->prng_lock);
- ctx->flags |= PRNG_NEED_RESET;
-
- prng_key = (key != NULL) ? key : (unsigned char *)DEFAULT_PRNG_KEY;
-
- if (!key)
- klen = DEFAULT_PRNG_KSZ;
-
- if (V)
- memcpy(ctx->V, V, DEFAULT_BLK_SZ);
- else
- memcpy(ctx->V, DEFAULT_V_SEED, DEFAULT_BLK_SZ);
-
- if (DT)
- memcpy(ctx->DT, DT, DEFAULT_BLK_SZ);
- else
- memset(ctx->DT, 0, DEFAULT_BLK_SZ);
-
- memset(ctx->rand_data, 0, DEFAULT_BLK_SZ);
- memset(ctx->last_rand_data, 0, DEFAULT_BLK_SZ);
-
- ctx->rand_data_valid = DEFAULT_BLK_SZ;
-
- ret = crypto_cipher_setkey(ctx->tfm, prng_key, klen);
- if (ret) {
- dbgprint(KERN_CRIT "PRNG: setkey() failed flags=%x\n",
- crypto_cipher_get_flags(ctx->tfm));
- goto out;
- }
-
- ret = 0;
- ctx->flags &= ~PRNG_NEED_RESET;
-out:
- spin_unlock_bh(&ctx->prng_lock);
- return ret;
-}
-
-static int cprng_init(struct crypto_tfm *tfm)
-{
- struct prng_context *ctx = crypto_tfm_ctx(tfm);
-
- spin_lock_init(&ctx->prng_lock);
- ctx->tfm = crypto_alloc_cipher("aes", 0, 0);
- if (IS_ERR(ctx->tfm)) {
- dbgprint(KERN_CRIT "Failed to alloc tfm for context %p\n",
- ctx);
- return PTR_ERR(ctx->tfm);
- }
-
- if (reset_prng_context(ctx, NULL, DEFAULT_PRNG_KSZ, NULL, NULL) < 0)
- return -EINVAL;
-
- /*
- * after allocation, we should always force the user to reset
- * so they don't inadvertently use the insecure default values
- * without specifying them intentially
- */
- ctx->flags |= PRNG_NEED_RESET;
- return 0;
-}
-
-static void cprng_exit(struct crypto_tfm *tfm)
-{
- free_prng_context(crypto_tfm_ctx(tfm));
-}
-
-static int cprng_get_random(struct crypto_rng *tfm,
- const u8 *src, unsigned int slen,
- u8 *rdata, unsigned int dlen)
-{
- struct prng_context *prng = crypto_rng_ctx(tfm);
-
- return get_prng_bytes(rdata, dlen, prng, 0);
-}
-
-/*
- * This is the cprng_registered reset method the seed value is
- * interpreted as the tuple { V KEY DT}
- * V and KEY are required during reset, and DT is optional, detected
- * as being present by testing the length of the seed
- */
-static int cprng_reset(struct crypto_rng *tfm,
- const u8 *seed, unsigned int slen)
-{
- struct prng_context *prng = crypto_rng_ctx(tfm);
- const u8 *key = seed + DEFAULT_BLK_SZ;
- const u8 *dt = NULL;
-
- if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ)
- return -EINVAL;
-
- if (slen >= (2 * DEFAULT_BLK_SZ + DEFAULT_PRNG_KSZ))
- dt = key + DEFAULT_PRNG_KSZ;
-
- reset_prng_context(prng, key, DEFAULT_PRNG_KSZ, seed, dt);
-
- if (prng->flags & PRNG_NEED_RESET)
- return -EINVAL;
- return 0;
-}
-
-#ifdef CONFIG_CRYPTO_FIPS
-static int fips_cprng_get_random(struct crypto_rng *tfm,
- const u8 *src, unsigned int slen,
- u8 *rdata, unsigned int dlen)
-{
- struct prng_context *prng = crypto_rng_ctx(tfm);
-
- return get_prng_bytes(rdata, dlen, prng, 1);
-}
-
-static int fips_cprng_reset(struct crypto_rng *tfm,
- const u8 *seed, unsigned int slen)
-{
- u8 rdata[DEFAULT_BLK_SZ];
- const u8 *key = seed + DEFAULT_BLK_SZ;
- int rc;
-
- struct prng_context *prng = crypto_rng_ctx(tfm);
-
- if (slen < DEFAULT_PRNG_KSZ + DEFAULT_BLK_SZ)
- return -EINVAL;
-
- /* fips strictly requires seed != key */
- if (!memcmp(seed, key, DEFAULT_PRNG_KSZ))
- return -EINVAL;
-
- rc = cprng_reset(tfm, seed, slen);
-
- if (!rc)
- goto out;
-
- /* this primes our continuity test */
- rc = get_prng_bytes(rdata, DEFAULT_BLK_SZ, prng, 0);
- prng->rand_data_valid = DEFAULT_BLK_SZ;
-
-out:
- return rc;
-}
-#endif
-
-static struct rng_alg rng_algs[] = { {
- .generate = cprng_get_random,
- .seed = cprng_reset,
- .seedsize = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ,
- .base = {
- .cra_name = "stdrng",
- .cra_driver_name = "ansi_cprng",
- .cra_priority = 100,
- .cra_ctxsize = sizeof(struct prng_context),
- .cra_module = THIS_MODULE,
- .cra_init = cprng_init,
- .cra_exit = cprng_exit,
- }
-#ifdef CONFIG_CRYPTO_FIPS
-}, {
- .generate = fips_cprng_get_random,
- .seed = fips_cprng_reset,
- .seedsize = DEFAULT_PRNG_KSZ + 2 * DEFAULT_BLK_SZ,
- .base = {
- .cra_name = "fips(ansi_cprng)",
- .cra_driver_name = "fips_ansi_cprng",
- .cra_priority = 300,
- .cra_ctxsize = sizeof(struct prng_context),
- .cra_module = THIS_MODULE,
- .cra_init = cprng_init,
- .cra_exit = cprng_exit,
- }
-#endif
-} };
-
-/* Module initalization */
-static int __init prng_mod_init(void)
-{
- return crypto_register_rngs(rng_algs, ARRAY_SIZE(rng_algs));
-}
-
-static void __exit prng_mod_fini(void)
-{
- crypto_unregister_rngs(rng_algs, ARRAY_SIZE(rng_algs));
-}
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Software Pseudo Random Number Generator");
-MODULE_AUTHOR("Neil Horman <nhorman@tuxdriver.com>");
-module_param(dbg, int, 0);
-MODULE_PARM_DESC(dbg, "Boolean to enable debugging (0/1 == off/on)");
-module_init(prng_mod_init);
-module_exit(prng_mod_fini);
-MODULE_ALIAS_CRYPTO("stdrng");
-MODULE_ALIAS_CRYPTO("ansi_cprng");
-MODULE_IMPORT_NS("CRYPTO_INTERNAL");
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index ba2d9d1ea235..348966ea2175 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -11,6 +11,7 @@
#include <crypto/public_key.h>
#include <linux/seq_file.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <keys/system_keyring.h>
@@ -141,12 +142,17 @@ struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
size_t len_2)
{
struct asymmetric_key_id *kid;
-
- kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
- GFP_KERNEL);
+ size_t kid_sz;
+ size_t len;
+
+ if (check_add_overflow(len_1, len_2, &len))
+ return ERR_PTR(-EOVERFLOW);
+ if (check_add_overflow(sizeof(struct asymmetric_key_id), len, &kid_sz))
+ return ERR_PTR(-EOVERFLOW);
+ kid = kmalloc(kid_sz, GFP_KERNEL);
if (!kid)
return ERR_PTR(-ENOMEM);
- kid->len = len_1 + len_2;
+ kid->len = len;
memcpy(kid->data, val_1, len_1);
memcpy(kid->data + len_1, val_2, len_2);
return kid;
diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c
index afcd4d101ac5..86292965f493 100644
--- a/crypto/asymmetric_keys/restrict.c
+++ b/crypto/asymmetric_keys/restrict.c
@@ -17,9 +17,12 @@ static struct asymmetric_key_id *ca_keyid;
#ifndef MODULE
static struct {
- struct asymmetric_key_id id;
- unsigned char data[10];
+ /* Must be last as it ends in a flexible-array member. */
+ TRAILING_OVERLAP(struct asymmetric_key_id, id, data,
+ unsigned char data[10];
+ );
} cakey;
+static_assert(offsetof(typeof(cakey), id.data) == offsetof(typeof(cakey), data));
static int __init ca_keys_setup(char *str)
{
diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c
index 8df3fa60a44f..b37cae914987 100644
--- a/crypto/asymmetric_keys/x509_cert_parser.c
+++ b/crypto/asymmetric_keys/x509_cert_parser.c
@@ -60,7 +60,7 @@ EXPORT_SYMBOL_GPL(x509_free_certificate);
*/
struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
{
- struct x509_certificate *cert __free(x509_free_certificate);
+ struct x509_certificate *cert __free(x509_free_certificate) = NULL;
struct x509_parse_context *ctx __free(kfree) = NULL;
struct asymmetric_key_id *kid;
long ret;
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 8409d7d36cb4..12e3341e806b 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -148,7 +148,7 @@ not_self_signed:
*/
static int x509_key_preparse(struct key_preparsed_payload *prep)
{
- struct x509_certificate *cert __free(x509_free_certificate);
+ struct x509_certificate *cert __free(x509_free_certificate) = NULL;
struct asymmetric_key_ids *kids __free(kfree) = NULL;
char *p, *desc __free(kfree) = NULL;
const char *q;
diff --git a/crypto/authenc.c b/crypto/authenc.c
index a723769c8777..ac679ce2cb95 100644
--- a/crypto/authenc.c
+++ b/crypto/authenc.c
@@ -37,7 +37,7 @@ struct authenc_request_ctx {
static void authenc_request_complete(struct aead_request *req, int err)
{
- if (err != -EINPROGRESS)
+ if (err != -EINPROGRESS && err != -EBUSY)
aead_request_complete(req, err);
}
@@ -107,27 +107,42 @@ out:
return err;
}
-static void authenc_geniv_ahash_done(void *data, int err)
+static void authenc_geniv_ahash_finish(struct aead_request *req)
{
- struct aead_request *req = data;
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(authenc);
struct authenc_instance_ctx *ictx = aead_instance_ctx(inst);
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
- if (err)
- goto out;
-
scatterwalk_map_and_copy(ahreq->result, req->dst,
req->assoclen + req->cryptlen,
crypto_aead_authsize(authenc), 1);
+}
-out:
+static void authenc_geniv_ahash_done(void *data, int err)
+{
+ struct aead_request *req = data;
+
+ if (!err)
+ authenc_geniv_ahash_finish(req);
aead_request_complete(req, err);
}
-static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
+/*
+ * Used when the ahash request was invoked in the async callback context
+ * of the previous skcipher request. Eat any EINPROGRESS notifications.
+ */
+static void authenc_geniv_ahash_done2(void *data, int err)
+{
+ struct aead_request *req = data;
+
+ if (!err)
+ authenc_geniv_ahash_finish(req);
+ authenc_request_complete(req, err);
+}
+
+static int crypto_authenc_genicv(struct aead_request *req, unsigned int mask)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(authenc);
@@ -136,6 +151,7 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
struct crypto_ahash *auth = ctx->auth;
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
+ unsigned int flags = aead_request_flags(req) & ~mask;
u8 *hash = areq_ctx->tail;
int err;
@@ -143,7 +159,8 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
ahash_request_set_crypt(ahreq, req->dst, hash,
req->assoclen + req->cryptlen);
ahash_request_set_callback(ahreq, flags,
- authenc_geniv_ahash_done, req);
+ mask ? authenc_geniv_ahash_done2 :
+ authenc_geniv_ahash_done, req);
err = crypto_ahash_digest(ahreq);
if (err)
@@ -159,12 +176,11 @@ static void crypto_authenc_encrypt_done(void *data, int err)
{
struct aead_request *areq = data;
- if (err)
- goto out;
-
- err = crypto_authenc_genicv(areq, 0);
-
-out:
+ if (err) {
+ aead_request_complete(areq, err);
+ return;
+ }
+ err = crypto_authenc_genicv(areq, CRYPTO_TFM_REQ_MAY_SLEEP);
authenc_request_complete(areq, err);
}
@@ -199,11 +215,18 @@ static int crypto_authenc_encrypt(struct aead_request *req)
if (err)
return err;
- return crypto_authenc_genicv(req, aead_request_flags(req));
+ return crypto_authenc_genicv(req, 0);
+}
+
+static void authenc_decrypt_tail_done(void *data, int err)
+{
+ struct aead_request *req = data;
+
+ authenc_request_complete(req, err);
}
static int crypto_authenc_decrypt_tail(struct aead_request *req,
- unsigned int flags)
+ unsigned int mask)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(authenc);
@@ -214,6 +237,7 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
struct skcipher_request *skreq = (void *)(areq_ctx->tail +
ictx->reqoff);
unsigned int authsize = crypto_aead_authsize(authenc);
+ unsigned int flags = aead_request_flags(req) & ~mask;
u8 *ihash = ahreq->result + authsize;
struct scatterlist *src, *dst;
@@ -230,7 +254,9 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
skcipher_request_set_tfm(skreq, ctx->enc);
skcipher_request_set_callback(skreq, flags,
- req->base.complete, req->base.data);
+ mask ? authenc_decrypt_tail_done :
+ req->base.complete,
+ mask ? req : req->base.data);
skcipher_request_set_crypt(skreq, src, dst,
req->cryptlen - authsize, req->iv);
@@ -241,12 +267,11 @@ static void authenc_verify_ahash_done(void *data, int err)
{
struct aead_request *req = data;
- if (err)
- goto out;
-
- err = crypto_authenc_decrypt_tail(req, 0);
-
-out:
+ if (err) {
+ aead_request_complete(req, err);
+ return;
+ }
+ err = crypto_authenc_decrypt_tail(req, CRYPTO_TFM_REQ_MAY_SLEEP);
authenc_request_complete(req, err);
}
@@ -273,7 +298,7 @@ static int crypto_authenc_decrypt(struct aead_request *req)
if (err)
return err;
- return crypto_authenc_decrypt_tail(req, aead_request_flags(req));
+ return crypto_authenc_decrypt_tail(req, 0);
}
static int crypto_authenc_init_tfm(struct crypto_aead *tfm)
diff --git a/crypto/blake2b.c b/crypto/blake2b.c
new file mode 100644
index 000000000000..67a6dae43a54
--- /dev/null
+++ b/crypto/blake2b.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Crypto API support for BLAKE2b
+ *
+ * Copyright 2025 Google LLC
+ */
+#include <crypto/blake2b.h>
+#include <crypto/internal/hash.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+struct blake2b_tfm_ctx {
+ unsigned int keylen;
+ u8 key[BLAKE2B_KEY_SIZE];
+};
+
+static int crypto_blake2b_setkey(struct crypto_shash *tfm,
+ const u8 *key, unsigned int keylen)
+{
+ struct blake2b_tfm_ctx *tctx = crypto_shash_ctx(tfm);
+
+ if (keylen > BLAKE2B_KEY_SIZE)
+ return -EINVAL;
+ memcpy(tctx->key, key, keylen);
+ tctx->keylen = keylen;
+ return 0;
+}
+
+#define BLAKE2B_CTX(desc) ((struct blake2b_ctx *)shash_desc_ctx(desc))
+
+static int crypto_blake2b_init(struct shash_desc *desc)
+{
+ const struct blake2b_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
+ unsigned int digestsize = crypto_shash_digestsize(desc->tfm);
+
+ blake2b_init_key(BLAKE2B_CTX(desc), digestsize,
+ tctx->key, tctx->keylen);
+ return 0;
+}
+
+static int crypto_blake2b_update(struct shash_desc *desc,
+ const u8 *data, unsigned int len)
+{
+ blake2b_update(BLAKE2B_CTX(desc), data, len);
+ return 0;
+}
+
+static int crypto_blake2b_final(struct shash_desc *desc, u8 *out)
+{
+ blake2b_final(BLAKE2B_CTX(desc), out);
+ return 0;
+}
+
+static int crypto_blake2b_digest(struct shash_desc *desc,
+ const u8 *data, unsigned int len, u8 *out)
+{
+ const struct blake2b_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
+ unsigned int digestsize = crypto_shash_digestsize(desc->tfm);
+
+ blake2b(tctx->key, tctx->keylen, data, len, out, digestsize);
+ return 0;
+}
+
+#define BLAKE2B_ALG(name, digest_size) \
+ { \
+ .base.cra_name = name, \
+ .base.cra_driver_name = name "-lib", \
+ .base.cra_priority = 300, \
+ .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY, \
+ .base.cra_blocksize = BLAKE2B_BLOCK_SIZE, \
+ .base.cra_ctxsize = sizeof(struct blake2b_tfm_ctx), \
+ .base.cra_module = THIS_MODULE, \
+ .digestsize = digest_size, \
+ .setkey = crypto_blake2b_setkey, \
+ .init = crypto_blake2b_init, \
+ .update = crypto_blake2b_update, \
+ .final = crypto_blake2b_final, \
+ .digest = crypto_blake2b_digest, \
+ .descsize = sizeof(struct blake2b_ctx), \
+ }
+
+static struct shash_alg algs[] = {
+ BLAKE2B_ALG("blake2b-160", BLAKE2B_160_HASH_SIZE),
+ BLAKE2B_ALG("blake2b-256", BLAKE2B_256_HASH_SIZE),
+ BLAKE2B_ALG("blake2b-384", BLAKE2B_384_HASH_SIZE),
+ BLAKE2B_ALG("blake2b-512", BLAKE2B_512_HASH_SIZE),
+};
+
+static int __init crypto_blake2b_mod_init(void)
+{
+ return crypto_register_shashes(algs, ARRAY_SIZE(algs));
+}
+module_init(crypto_blake2b_mod_init);
+
+static void __exit crypto_blake2b_mod_exit(void)
+{
+ crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
+}
+module_exit(crypto_blake2b_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Crypto API support for BLAKE2b");
+
+MODULE_ALIAS_CRYPTO("blake2b-160");
+MODULE_ALIAS_CRYPTO("blake2b-160-lib");
+MODULE_ALIAS_CRYPTO("blake2b-256");
+MODULE_ALIAS_CRYPTO("blake2b-256-lib");
+MODULE_ALIAS_CRYPTO("blake2b-384");
+MODULE_ALIAS_CRYPTO("blake2b-384-lib");
+MODULE_ALIAS_CRYPTO("blake2b-512");
+MODULE_ALIAS_CRYPTO("blake2b-512-lib");
diff --git a/crypto/blake2b_generic.c b/crypto/blake2b_generic.c
deleted file mode 100644
index 60f056217510..000000000000
--- a/crypto/blake2b_generic.c
+++ /dev/null
@@ -1,192 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR Apache-2.0)
-/*
- * Generic implementation of the BLAKE2b digest algorithm. Based on the BLAKE2b
- * reference implementation, but it has been heavily modified for use in the
- * kernel. The reference implementation was:
- *
- * Copyright 2012, Samuel Neves <sneves@dei.uc.pt>. You may use this under
- * the terms of the CC0, the OpenSSL Licence, or the Apache Public License
- * 2.0, at your option. The terms of these licenses can be found at:
- *
- * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
- * - OpenSSL license : https://www.openssl.org/source/license.html
- * - Apache 2.0 : https://www.apache.org/licenses/LICENSE-2.0
- *
- * More information about BLAKE2 can be found at https://blake2.net.
- */
-
-#include <crypto/internal/blake2b.h>
-#include <crypto/internal/hash.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/unaligned.h>
-
-static const u8 blake2b_sigma[12][16] = {
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
- { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
- { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
- { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
- { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
- { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
- { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
- { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
- { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
- { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
- { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
- { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
-};
-
-static void blake2b_increment_counter(struct blake2b_state *S, const u64 inc)
-{
- S->t[0] += inc;
- S->t[1] += (S->t[0] < inc);
-}
-
-#define G(r,i,a,b,c,d) \
- do { \
- a = a + b + m[blake2b_sigma[r][2*i+0]]; \
- d = ror64(d ^ a, 32); \
- c = c + d; \
- b = ror64(b ^ c, 24); \
- a = a + b + m[blake2b_sigma[r][2*i+1]]; \
- d = ror64(d ^ a, 16); \
- c = c + d; \
- b = ror64(b ^ c, 63); \
- } while (0)
-
-#define ROUND(r) \
- do { \
- G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
- G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
- G(r,2,v[ 2],v[ 6],v[10],v[14]); \
- G(r,3,v[ 3],v[ 7],v[11],v[15]); \
- G(r,4,v[ 0],v[ 5],v[10],v[15]); \
- G(r,5,v[ 1],v[ 6],v[11],v[12]); \
- G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
- G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
- } while (0)
-
-static void blake2b_compress_one_generic(struct blake2b_state *S,
- const u8 block[BLAKE2B_BLOCK_SIZE])
-{
- u64 m[16];
- u64 v[16];
- size_t i;
-
- for (i = 0; i < 16; ++i)
- m[i] = get_unaligned_le64(block + i * sizeof(m[i]));
-
- for (i = 0; i < 8; ++i)
- v[i] = S->h[i];
-
- v[ 8] = BLAKE2B_IV0;
- v[ 9] = BLAKE2B_IV1;
- v[10] = BLAKE2B_IV2;
- v[11] = BLAKE2B_IV3;
- v[12] = BLAKE2B_IV4 ^ S->t[0];
- v[13] = BLAKE2B_IV5 ^ S->t[1];
- v[14] = BLAKE2B_IV6 ^ S->f[0];
- v[15] = BLAKE2B_IV7 ^ S->f[1];
-
- ROUND(0);
- ROUND(1);
- ROUND(2);
- ROUND(3);
- ROUND(4);
- ROUND(5);
- ROUND(6);
- ROUND(7);
- ROUND(8);
- ROUND(9);
- ROUND(10);
- ROUND(11);
-#ifdef CONFIG_CC_IS_CLANG
-#pragma nounroll /* https://llvm.org/pr45803 */
-#endif
- for (i = 0; i < 8; ++i)
- S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
-}
-
-#undef G
-#undef ROUND
-
-static void blake2b_compress_generic(struct blake2b_state *state,
- const u8 *block, size_t nblocks, u32 inc)
-{
- do {
- blake2b_increment_counter(state, inc);
- blake2b_compress_one_generic(state, block);
- block += BLAKE2B_BLOCK_SIZE;
- } while (--nblocks);
-}
-
-static int crypto_blake2b_update_generic(struct shash_desc *desc,
- const u8 *in, unsigned int inlen)
-{
- return crypto_blake2b_update_bo(desc, in, inlen,
- blake2b_compress_generic);
-}
-
-static int crypto_blake2b_finup_generic(struct shash_desc *desc, const u8 *in,
- unsigned int inlen, u8 *out)
-{
- return crypto_blake2b_finup(desc, in, inlen, out,
- blake2b_compress_generic);
-}
-
-#define BLAKE2B_ALG(name, driver_name, digest_size) \
- { \
- .base.cra_name = name, \
- .base.cra_driver_name = driver_name, \
- .base.cra_priority = 100, \
- .base.cra_flags = CRYPTO_ALG_OPTIONAL_KEY | \
- CRYPTO_AHASH_ALG_BLOCK_ONLY | \
- CRYPTO_AHASH_ALG_FINAL_NONZERO, \
- .base.cra_blocksize = BLAKE2B_BLOCK_SIZE, \
- .base.cra_ctxsize = sizeof(struct blake2b_tfm_ctx), \
- .base.cra_module = THIS_MODULE, \
- .digestsize = digest_size, \
- .setkey = crypto_blake2b_setkey, \
- .init = crypto_blake2b_init, \
- .update = crypto_blake2b_update_generic, \
- .finup = crypto_blake2b_finup_generic, \
- .descsize = BLAKE2B_DESC_SIZE, \
- .statesize = BLAKE2B_STATE_SIZE, \
- }
-
-static struct shash_alg blake2b_algs[] = {
- BLAKE2B_ALG("blake2b-160", "blake2b-160-generic",
- BLAKE2B_160_HASH_SIZE),
- BLAKE2B_ALG("blake2b-256", "blake2b-256-generic",
- BLAKE2B_256_HASH_SIZE),
- BLAKE2B_ALG("blake2b-384", "blake2b-384-generic",
- BLAKE2B_384_HASH_SIZE),
- BLAKE2B_ALG("blake2b-512", "blake2b-512-generic",
- BLAKE2B_512_HASH_SIZE),
-};
-
-static int __init blake2b_mod_init(void)
-{
- return crypto_register_shashes(blake2b_algs, ARRAY_SIZE(blake2b_algs));
-}
-
-static void __exit blake2b_mod_fini(void)
-{
- crypto_unregister_shashes(blake2b_algs, ARRAY_SIZE(blake2b_algs));
-}
-
-module_init(blake2b_mod_init);
-module_exit(blake2b_mod_fini);
-
-MODULE_AUTHOR("David Sterba <kdave@kernel.org>");
-MODULE_DESCRIPTION("BLAKE2b generic implementation");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_CRYPTO("blake2b-160");
-MODULE_ALIAS_CRYPTO("blake2b-160-generic");
-MODULE_ALIAS_CRYPTO("blake2b-256");
-MODULE_ALIAS_CRYPTO("blake2b-256-generic");
-MODULE_ALIAS_CRYPTO("blake2b-384");
-MODULE_ALIAS_CRYPTO("blake2b-384-generic");
-MODULE_ALIAS_CRYPTO("blake2b-512");
-MODULE_ALIAS_CRYPTO("blake2b-512-generic");
diff --git a/crypto/deflate.c b/crypto/deflate.c
index 21404515dc77..a3e1fff55661 100644
--- a/crypto/deflate.c
+++ b/crypto/deflate.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/overflow.h>
#include <linux/percpu.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
@@ -39,7 +40,7 @@ static void *deflate_alloc_stream(void)
DEFLATE_DEF_MEMLEVEL));
struct deflate_stream *ctx;
- ctx = kvmalloc(sizeof(*ctx) + size, GFP_KERNEL);
+ ctx = kvmalloc(struct_size(ctx, workspace, size), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
diff --git a/crypto/df_sp80090a.c b/crypto/df_sp80090a.c
new file mode 100644
index 000000000000..dc63b31a93fc
--- /dev/null
+++ b/crypto/df_sp80090a.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * NIST SP800-90A DRBG derivation function
+ *
+ * Copyright (C) 2014, Stephan Mueller <smueller@chronox.de>
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <crypto/aes.h>
+#include <crypto/df_sp80090a.h>
+#include <crypto/internal/drbg.h>
+
+static void drbg_kcapi_symsetkey(struct crypto_aes_ctx *aesctx,
+ const unsigned char *key,
+ u8 keylen);
+static void drbg_kcapi_symsetkey(struct crypto_aes_ctx *aesctx,
+ const unsigned char *key, u8 keylen)
+{
+ aes_expandkey(aesctx, key, keylen);
+}
+
+static void drbg_kcapi_sym(struct crypto_aes_ctx *aesctx,
+ unsigned char *outval,
+ const struct drbg_string *in, u8 blocklen_bytes)
+{
+ /* there is only component in *in */
+ BUG_ON(in->len < blocklen_bytes);
+ aes_encrypt(aesctx, outval, in->buf);
+}
+
+/* BCC function for CTR DRBG as defined in 10.4.3 */
+
+static void drbg_ctr_bcc(struct crypto_aes_ctx *aesctx,
+ unsigned char *out, const unsigned char *key,
+ struct list_head *in,
+ u8 blocklen_bytes,
+ u8 keylen)
+{
+ struct drbg_string *curr = NULL;
+ struct drbg_string data;
+ short cnt = 0;
+
+ drbg_string_fill(&data, out, blocklen_bytes);
+
+ /* 10.4.3 step 2 / 4 */
+ drbg_kcapi_symsetkey(aesctx, key, keylen);
+ list_for_each_entry(curr, in, list) {
+ const unsigned char *pos = curr->buf;
+ size_t len = curr->len;
+ /* 10.4.3 step 4.1 */
+ while (len) {
+ /* 10.4.3 step 4.2 */
+ if (blocklen_bytes == cnt) {
+ cnt = 0;
+ drbg_kcapi_sym(aesctx, out, &data, blocklen_bytes);
+ }
+ out[cnt] ^= *pos;
+ pos++;
+ cnt++;
+ len--;
+ }
+ }
+ /* 10.4.3 step 4.2 for last block */
+ if (cnt)
+ drbg_kcapi_sym(aesctx, out, &data, blocklen_bytes);
+}
+
+/*
+ * scratchpad usage: drbg_ctr_update is interlinked with crypto_drbg_ctr_df
+ * (and drbg_ctr_bcc, but this function does not need any temporary buffers),
+ * the scratchpad is used as follows:
+ * drbg_ctr_update:
+ * temp
+ * start: drbg->scratchpad
+ * length: drbg_statelen(drbg) + drbg_blocklen(drbg)
+ * note: the cipher writing into this variable works
+ * blocklen-wise. Now, when the statelen is not a multiple
+ * of blocklen, the generateion loop below "spills over"
+ * by at most blocklen. Thus, we need to give sufficient
+ * memory.
+ * df_data
+ * start: drbg->scratchpad +
+ * drbg_statelen(drbg) + drbg_blocklen(drbg)
+ * length: drbg_statelen(drbg)
+ *
+ * crypto_drbg_ctr_df:
+ * pad
+ * start: df_data + drbg_statelen(drbg)
+ * length: drbg_blocklen(drbg)
+ * iv
+ * start: pad + drbg_blocklen(drbg)
+ * length: drbg_blocklen(drbg)
+ * temp
+ * start: iv + drbg_blocklen(drbg)
+ * length: drbg_satelen(drbg) + drbg_blocklen(drbg)
+ * note: temp is the buffer that the BCC function operates
+ * on. BCC operates blockwise. drbg_statelen(drbg)
+ * is sufficient when the DRBG state length is a multiple
+ * of the block size. For AES192 (and maybe other ciphers)
+ * this is not correct and the length for temp is
+ * insufficient (yes, that also means for such ciphers,
+ * the final output of all BCC rounds are truncated).
+ * Therefore, add drbg_blocklen(drbg) to cover all
+ * possibilities.
+ * refer to crypto_drbg_ctr_df_datalen() to get required length
+ */
+
+/* Derivation Function for CTR DRBG as defined in 10.4.2 */
+int crypto_drbg_ctr_df(struct crypto_aes_ctx *aesctx,
+ unsigned char *df_data, size_t bytes_to_return,
+ struct list_head *seedlist,
+ u8 blocklen_bytes,
+ u8 statelen)
+{
+ unsigned char L_N[8];
+ /* S3 is input */
+ struct drbg_string S1, S2, S4, cipherin;
+ LIST_HEAD(bcc_list);
+ unsigned char *pad = df_data + statelen;
+ unsigned char *iv = pad + blocklen_bytes;
+ unsigned char *temp = iv + blocklen_bytes;
+ size_t padlen = 0;
+ unsigned int templen = 0;
+ /* 10.4.2 step 7 */
+ unsigned int i = 0;
+ /* 10.4.2 step 8 */
+ const unsigned char *K = (unsigned char *)
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x11\x12\x13\x14\x15\x16\x17"
+ "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
+ unsigned char *X;
+ size_t generated_len = 0;
+ size_t inputlen = 0;
+ struct drbg_string *seed = NULL;
+ u8 keylen;
+
+ memset(pad, 0, blocklen_bytes);
+ memset(iv, 0, blocklen_bytes);
+ keylen = statelen - blocklen_bytes;
+ /* 10.4.2 step 1 is implicit as we work byte-wise */
+
+ /* 10.4.2 step 2 */
+ if ((512 / 8) < bytes_to_return)
+ return -EINVAL;
+
+ /* 10.4.2 step 2 -- calculate the entire length of all input data */
+ list_for_each_entry(seed, seedlist, list)
+ inputlen += seed->len;
+ drbg_cpu_to_be32(inputlen, &L_N[0]);
+
+ /* 10.4.2 step 3 */
+ drbg_cpu_to_be32(bytes_to_return, &L_N[4]);
+
+ /* 10.4.2 step 5: length is L_N, input_string, one byte, padding */
+ padlen = (inputlen + sizeof(L_N) + 1) % (blocklen_bytes);
+ /* wrap the padlen appropriately */
+ if (padlen)
+ padlen = blocklen_bytes - padlen;
+ /*
+ * pad / padlen contains the 0x80 byte and the following zero bytes.
+ * As the calculated padlen value only covers the number of zero
+ * bytes, this value has to be incremented by one for the 0x80 byte.
+ */
+ padlen++;
+ pad[0] = 0x80;
+
+ /* 10.4.2 step 4 -- first fill the linked list and then order it */
+ drbg_string_fill(&S1, iv, blocklen_bytes);
+ list_add_tail(&S1.list, &bcc_list);
+ drbg_string_fill(&S2, L_N, sizeof(L_N));
+ list_add_tail(&S2.list, &bcc_list);
+ list_splice_tail(seedlist, &bcc_list);
+ drbg_string_fill(&S4, pad, padlen);
+ list_add_tail(&S4.list, &bcc_list);
+
+ /* 10.4.2 step 9 */
+ while (templen < (keylen + (blocklen_bytes))) {
+ /*
+ * 10.4.2 step 9.1 - the padding is implicit as the buffer
+ * holds zeros after allocation -- even the increment of i
+ * is irrelevant as the increment remains within length of i
+ */
+ drbg_cpu_to_be32(i, iv);
+ /* 10.4.2 step 9.2 -- BCC and concatenation with temp */
+ drbg_ctr_bcc(aesctx, temp + templen, K, &bcc_list,
+ blocklen_bytes, keylen);
+ /* 10.4.2 step 9.3 */
+ i++;
+ templen += blocklen_bytes;
+ }
+
+ /* 10.4.2 step 11 */
+ X = temp + (keylen);
+ drbg_string_fill(&cipherin, X, blocklen_bytes);
+
+ /* 10.4.2 step 12: overwriting of outval is implemented in next step */
+
+ /* 10.4.2 step 13 */
+ drbg_kcapi_symsetkey(aesctx, temp, keylen);
+ while (generated_len < bytes_to_return) {
+ short blocklen = 0;
+ /*
+ * 10.4.2 step 13.1: the truncation of the key length is
+ * implicit as the key is only drbg_blocklen in size based on
+ * the implementation of the cipher function callback
+ */
+ drbg_kcapi_sym(aesctx, X, &cipherin, blocklen_bytes);
+ blocklen = (blocklen_bytes <
+ (bytes_to_return - generated_len)) ?
+ blocklen_bytes :
+ (bytes_to_return - generated_len);
+ /* 10.4.2 step 13.2 and 14 */
+ memcpy(df_data + generated_len, X, blocklen);
+ generated_len += blocklen;
+ }
+
+ memset(iv, 0, blocklen_bytes);
+ memset(temp, 0, statelen + blocklen_bytes);
+ memset(pad, 0, blocklen_bytes);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(crypto_drbg_ctr_df);
+
+MODULE_IMPORT_NS("CRYPTO_INTERNAL");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Derivation Function conformant to SP800-90A");
diff --git a/crypto/drbg.c b/crypto/drbg.c
index dbe4c8bb5ceb..1d433dae9955 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -98,6 +98,7 @@
*/
#include <crypto/drbg.h>
+#include <crypto/df_sp80090a.h>
#include <crypto/internal/cipher.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
@@ -261,26 +262,6 @@ static int drbg_fips_continuous_test(struct drbg_state *drbg,
return 0;
}
-/*
- * Convert an integer into a byte representation of this integer.
- * The byte representation is big-endian
- *
- * @val value to be converted
- * @buf buffer holding the converted integer -- caller must ensure that
- * buffer size is at least 32 bit
- */
-#if (defined(CONFIG_CRYPTO_DRBG_HASH) || defined(CONFIG_CRYPTO_DRBG_CTR))
-static inline void drbg_cpu_to_be32(__u32 val, unsigned char *buf)
-{
- struct s {
- __be32 conv;
- };
- struct s *conversion = (struct s *) buf;
-
- conversion->conv = cpu_to_be32(val);
-}
-#endif /* defined(CONFIG_CRYPTO_DRBG_HASH) || defined(CONFIG_CRYPTO_DRBG_CTR) */
-
/******************************************************************
* CTR DRBG callback functions
******************************************************************/
@@ -294,10 +275,6 @@ MODULE_ALIAS_CRYPTO("drbg_nopr_ctr_aes192");
MODULE_ALIAS_CRYPTO("drbg_pr_ctr_aes128");
MODULE_ALIAS_CRYPTO("drbg_nopr_ctr_aes128");
-static void drbg_kcapi_symsetkey(struct drbg_state *drbg,
- const unsigned char *key);
-static int drbg_kcapi_sym(struct drbg_state *drbg, unsigned char *outval,
- const struct drbg_string *in);
static int drbg_init_sym_kernel(struct drbg_state *drbg);
static int drbg_fini_sym_kernel(struct drbg_state *drbg);
static int drbg_kcapi_sym_ctr(struct drbg_state *drbg,
@@ -305,202 +282,12 @@ static int drbg_kcapi_sym_ctr(struct drbg_state *drbg,
u8 *outbuf, u32 outlen);
#define DRBG_OUTSCRATCHLEN 256
-/* BCC function for CTR DRBG as defined in 10.4.3 */
-static int drbg_ctr_bcc(struct drbg_state *drbg,
- unsigned char *out, const unsigned char *key,
- struct list_head *in)
-{
- int ret = 0;
- struct drbg_string *curr = NULL;
- struct drbg_string data;
- short cnt = 0;
-
- drbg_string_fill(&data, out, drbg_blocklen(drbg));
-
- /* 10.4.3 step 2 / 4 */
- drbg_kcapi_symsetkey(drbg, key);
- list_for_each_entry(curr, in, list) {
- const unsigned char *pos = curr->buf;
- size_t len = curr->len;
- /* 10.4.3 step 4.1 */
- while (len) {
- /* 10.4.3 step 4.2 */
- if (drbg_blocklen(drbg) == cnt) {
- cnt = 0;
- ret = drbg_kcapi_sym(drbg, out, &data);
- if (ret)
- return ret;
- }
- out[cnt] ^= *pos;
- pos++;
- cnt++;
- len--;
- }
- }
- /* 10.4.3 step 4.2 for last block */
- if (cnt)
- ret = drbg_kcapi_sym(drbg, out, &data);
-
- return ret;
-}
-
-/*
- * scratchpad usage: drbg_ctr_update is interlinked with drbg_ctr_df
- * (and drbg_ctr_bcc, but this function does not need any temporary buffers),
- * the scratchpad is used as follows:
- * drbg_ctr_update:
- * temp
- * start: drbg->scratchpad
- * length: drbg_statelen(drbg) + drbg_blocklen(drbg)
- * note: the cipher writing into this variable works
- * blocklen-wise. Now, when the statelen is not a multiple
- * of blocklen, the generateion loop below "spills over"
- * by at most blocklen. Thus, we need to give sufficient
- * memory.
- * df_data
- * start: drbg->scratchpad +
- * drbg_statelen(drbg) + drbg_blocklen(drbg)
- * length: drbg_statelen(drbg)
- *
- * drbg_ctr_df:
- * pad
- * start: df_data + drbg_statelen(drbg)
- * length: drbg_blocklen(drbg)
- * iv
- * start: pad + drbg_blocklen(drbg)
- * length: drbg_blocklen(drbg)
- * temp
- * start: iv + drbg_blocklen(drbg)
- * length: drbg_satelen(drbg) + drbg_blocklen(drbg)
- * note: temp is the buffer that the BCC function operates
- * on. BCC operates blockwise. drbg_statelen(drbg)
- * is sufficient when the DRBG state length is a multiple
- * of the block size. For AES192 (and maybe other ciphers)
- * this is not correct and the length for temp is
- * insufficient (yes, that also means for such ciphers,
- * the final output of all BCC rounds are truncated).
- * Therefore, add drbg_blocklen(drbg) to cover all
- * possibilities.
- */
-
-/* Derivation Function for CTR DRBG as defined in 10.4.2 */
static int drbg_ctr_df(struct drbg_state *drbg,
unsigned char *df_data, size_t bytes_to_return,
struct list_head *seedlist)
{
- int ret = -EFAULT;
- unsigned char L_N[8];
- /* S3 is input */
- struct drbg_string S1, S2, S4, cipherin;
- LIST_HEAD(bcc_list);
- unsigned char *pad = df_data + drbg_statelen(drbg);
- unsigned char *iv = pad + drbg_blocklen(drbg);
- unsigned char *temp = iv + drbg_blocklen(drbg);
- size_t padlen = 0;
- unsigned int templen = 0;
- /* 10.4.2 step 7 */
- unsigned int i = 0;
- /* 10.4.2 step 8 */
- const unsigned char *K = (unsigned char *)
- "\x00\x01\x02\x03\x04\x05\x06\x07"
- "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
- "\x10\x11\x12\x13\x14\x15\x16\x17"
- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
- unsigned char *X;
- size_t generated_len = 0;
- size_t inputlen = 0;
- struct drbg_string *seed = NULL;
-
- memset(pad, 0, drbg_blocklen(drbg));
- memset(iv, 0, drbg_blocklen(drbg));
-
- /* 10.4.2 step 1 is implicit as we work byte-wise */
-
- /* 10.4.2 step 2 */
- if ((512/8) < bytes_to_return)
- return -EINVAL;
-
- /* 10.4.2 step 2 -- calculate the entire length of all input data */
- list_for_each_entry(seed, seedlist, list)
- inputlen += seed->len;
- drbg_cpu_to_be32(inputlen, &L_N[0]);
-
- /* 10.4.2 step 3 */
- drbg_cpu_to_be32(bytes_to_return, &L_N[4]);
-
- /* 10.4.2 step 5: length is L_N, input_string, one byte, padding */
- padlen = (inputlen + sizeof(L_N) + 1) % (drbg_blocklen(drbg));
- /* wrap the padlen appropriately */
- if (padlen)
- padlen = drbg_blocklen(drbg) - padlen;
- /*
- * pad / padlen contains the 0x80 byte and the following zero bytes.
- * As the calculated padlen value only covers the number of zero
- * bytes, this value has to be incremented by one for the 0x80 byte.
- */
- padlen++;
- pad[0] = 0x80;
-
- /* 10.4.2 step 4 -- first fill the linked list and then order it */
- drbg_string_fill(&S1, iv, drbg_blocklen(drbg));
- list_add_tail(&S1.list, &bcc_list);
- drbg_string_fill(&S2, L_N, sizeof(L_N));
- list_add_tail(&S2.list, &bcc_list);
- list_splice_tail(seedlist, &bcc_list);
- drbg_string_fill(&S4, pad, padlen);
- list_add_tail(&S4.list, &bcc_list);
-
- /* 10.4.2 step 9 */
- while (templen < (drbg_keylen(drbg) + (drbg_blocklen(drbg)))) {
- /*
- * 10.4.2 step 9.1 - the padding is implicit as the buffer
- * holds zeros after allocation -- even the increment of i
- * is irrelevant as the increment remains within length of i
- */
- drbg_cpu_to_be32(i, iv);
- /* 10.4.2 step 9.2 -- BCC and concatenation with temp */
- ret = drbg_ctr_bcc(drbg, temp + templen, K, &bcc_list);
- if (ret)
- goto out;
- /* 10.4.2 step 9.3 */
- i++;
- templen += drbg_blocklen(drbg);
- }
-
- /* 10.4.2 step 11 */
- X = temp + (drbg_keylen(drbg));
- drbg_string_fill(&cipherin, X, drbg_blocklen(drbg));
-
- /* 10.4.2 step 12: overwriting of outval is implemented in next step */
-
- /* 10.4.2 step 13 */
- drbg_kcapi_symsetkey(drbg, temp);
- while (generated_len < bytes_to_return) {
- short blocklen = 0;
- /*
- * 10.4.2 step 13.1: the truncation of the key length is
- * implicit as the key is only drbg_blocklen in size based on
- * the implementation of the cipher function callback
- */
- ret = drbg_kcapi_sym(drbg, X, &cipherin);
- if (ret)
- goto out;
- blocklen = (drbg_blocklen(drbg) <
- (bytes_to_return - generated_len)) ?
- drbg_blocklen(drbg) :
- (bytes_to_return - generated_len);
- /* 10.4.2 step 13.2 and 14 */
- memcpy(df_data + generated_len, X, blocklen);
- generated_len += blocklen;
- }
-
- ret = 0;
-
-out:
- memset(iv, 0, drbg_blocklen(drbg));
- memset(temp, 0, drbg_statelen(drbg) + drbg_blocklen(drbg));
- memset(pad, 0, drbg_blocklen(drbg));
- return ret;
+ return crypto_drbg_ctr_df(drbg->priv_data, df_data, drbg_statelen(drbg),
+ seedlist, drbg_blocklen(drbg), drbg_statelen(drbg));
}
/*
@@ -1310,10 +1097,8 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
sb_size = 0;
else if (drbg->core->flags & DRBG_CTR)
sb_size = drbg_statelen(drbg) + drbg_blocklen(drbg) + /* temp */
- drbg_statelen(drbg) + /* df_data */
- drbg_blocklen(drbg) + /* pad */
- drbg_blocklen(drbg) + /* iv */
- drbg_statelen(drbg) + drbg_blocklen(drbg); /* temp */
+ crypto_drbg_ctr_df_datalen(drbg_statelen(drbg),
+ drbg_blocklen(drbg));
else
sb_size = drbg_statelen(drbg) + drbg_blocklen(drbg);
@@ -1658,7 +1443,6 @@ static void drbg_kcapi_set_entropy(struct crypto_rng *tfm,
#if defined(CONFIG_CRYPTO_DRBG_HASH) || defined(CONFIG_CRYPTO_DRBG_HMAC)
struct sdesc {
struct shash_desc shash;
- char ctx[];
};
static int drbg_init_hash_kernel(struct drbg_state *drbg)
@@ -1721,10 +1505,9 @@ static int drbg_kcapi_hash(struct drbg_state *drbg, unsigned char *outval,
#ifdef CONFIG_CRYPTO_DRBG_CTR
static int drbg_fini_sym_kernel(struct drbg_state *drbg)
{
- struct crypto_cipher *tfm =
- (struct crypto_cipher *)drbg->priv_data;
- if (tfm)
- crypto_free_cipher(tfm);
+ struct crypto_aes_ctx *aesctx = (struct crypto_aes_ctx *)drbg->priv_data;
+
+ kfree(aesctx);
drbg->priv_data = NULL;
if (drbg->ctr_handle)
@@ -1743,20 +1526,16 @@ static int drbg_fini_sym_kernel(struct drbg_state *drbg)
static int drbg_init_sym_kernel(struct drbg_state *drbg)
{
- struct crypto_cipher *tfm;
+ struct crypto_aes_ctx *aesctx;
struct crypto_skcipher *sk_tfm;
struct skcipher_request *req;
unsigned int alignmask;
char ctr_name[CRYPTO_MAX_ALG_NAME];
- tfm = crypto_alloc_cipher(drbg->core->backend_cra_name, 0, 0);
- if (IS_ERR(tfm)) {
- pr_info("DRBG: could not allocate cipher TFM handle: %s\n",
- drbg->core->backend_cra_name);
- return PTR_ERR(tfm);
- }
- BUG_ON(drbg_blocklen(drbg) != crypto_cipher_blocksize(tfm));
- drbg->priv_data = tfm;
+ aesctx = kzalloc(sizeof(*aesctx), GFP_KERNEL);
+ if (!aesctx)
+ return -ENOMEM;
+ drbg->priv_data = aesctx;
if (snprintf(ctr_name, CRYPTO_MAX_ALG_NAME, "ctr(%s)",
drbg->core->backend_cra_name) >= CRYPTO_MAX_ALG_NAME) {
@@ -1800,25 +1579,6 @@ static int drbg_init_sym_kernel(struct drbg_state *drbg)
return alignmask;
}
-static void drbg_kcapi_symsetkey(struct drbg_state *drbg,
- const unsigned char *key)
-{
- struct crypto_cipher *tfm = drbg->priv_data;
-
- crypto_cipher_setkey(tfm, key, (drbg_keylen(drbg)));
-}
-
-static int drbg_kcapi_sym(struct drbg_state *drbg, unsigned char *outval,
- const struct drbg_string *in)
-{
- struct crypto_cipher *tfm = drbg->priv_data;
-
- /* there is only component in *in */
- BUG_ON(in->len < drbg_blocklen(drbg));
- crypto_cipher_encrypt_one(tfm, outval, in->buf);
- return 0;
-}
-
static int drbg_kcapi_sym_ctr(struct drbg_state *drbg,
u8 *inbuf, u32 inlen,
u8 *outbuf, u32 outlen)
diff --git a/crypto/fips.c b/crypto/fips.c
index e88a604cb42b..65d2bc070a26 100644
--- a/crypto/fips.c
+++ b/crypto/fips.c
@@ -24,7 +24,10 @@ EXPORT_SYMBOL_GPL(fips_fail_notif_chain);
/* Process kernel command-line parameter at boot time. fips=0 or fips=1 */
static int fips_enable(char *str)
{
- fips_enabled = !!simple_strtol(str, NULL, 0);
+ if (kstrtoint(str, 0, &fips_enabled))
+ return 0;
+
+ fips_enabled = !!fips_enabled;
pr_info("fips mode: %s\n", str_enabled_disabled(fips_enabled));
return 1;
}
diff --git a/crypto/hctr2.c b/crypto/hctr2.c
index c8932777bba8..f4cd6c29b4d3 100644
--- a/crypto/hctr2.c
+++ b/crypto/hctr2.c
@@ -17,7 +17,6 @@
*/
#include <crypto/internal/cipher.h>
-#include <crypto/internal/hash.h>
#include <crypto/internal/skcipher.h>
#include <crypto/polyval.h>
#include <crypto/scatterwalk.h>
@@ -37,23 +36,14 @@
struct hctr2_instance_ctx {
struct crypto_cipher_spawn blockcipher_spawn;
struct crypto_skcipher_spawn xctr_spawn;
- struct crypto_shash_spawn polyval_spawn;
};
struct hctr2_tfm_ctx {
struct crypto_cipher *blockcipher;
struct crypto_skcipher *xctr;
- struct crypto_shash *polyval;
+ struct polyval_key poly_key;
+ struct polyval_elem hashed_tweaklens[2];
u8 L[BLOCKCIPHER_BLOCK_SIZE];
- int hashed_tweak_offset;
- /*
- * This struct is allocated with extra space for two exported hash
- * states. Since the hash state size is not known at compile-time, we
- * can't add these to the struct directly.
- *
- * hashed_tweaklen_divisible;
- * hashed_tweaklen_remainder;
- */
};
struct hctr2_request_ctx {
@@ -63,39 +53,17 @@ struct hctr2_request_ctx {
struct scatterlist *bulk_part_src;
struct scatterlist sg_src[2];
struct scatterlist sg_dst[2];
+ struct polyval_elem hashed_tweak;
/*
- * Sub-request sizes are unknown at compile-time, so they need to go
- * after the members with known sizes.
+ * skcipher sub-request size is unknown at compile-time, so it needs to
+ * go after the members with known sizes.
*/
union {
- struct shash_desc hash_desc;
+ struct polyval_ctx poly_ctx;
struct skcipher_request xctr_req;
} u;
- /*
- * This struct is allocated with extra space for one exported hash
- * state. Since the hash state size is not known at compile-time, we
- * can't add it to the struct directly.
- *
- * hashed_tweak;
- */
};
-static inline u8 *hctr2_hashed_tweaklen(const struct hctr2_tfm_ctx *tctx,
- bool has_remainder)
-{
- u8 *p = (u8 *)tctx + sizeof(*tctx);
-
- if (has_remainder) /* For messages not a multiple of block length */
- p += crypto_shash_statesize(tctx->polyval);
- return p;
-}
-
-static inline u8 *hctr2_hashed_tweak(const struct hctr2_tfm_ctx *tctx,
- struct hctr2_request_ctx *rctx)
-{
- return (u8 *)rctx + tctx->hashed_tweak_offset;
-}
-
/*
* The input data for each HCTR2 hash step begins with a 16-byte block that
* contains the tweak length and a flag that indicates whether the input is evenly
@@ -106,24 +74,23 @@ static inline u8 *hctr2_hashed_tweak(const struct hctr2_tfm_ctx *tctx,
*
* These precomputed hashes are stored in hctr2_tfm_ctx.
*/
-static int hctr2_hash_tweaklen(struct hctr2_tfm_ctx *tctx, bool has_remainder)
+static void hctr2_hash_tweaklens(struct hctr2_tfm_ctx *tctx)
{
- SHASH_DESC_ON_STACK(shash, tfm->polyval);
- __le64 tweak_length_block[2];
- int err;
-
- shash->tfm = tctx->polyval;
- memset(tweak_length_block, 0, sizeof(tweak_length_block));
-
- tweak_length_block[0] = cpu_to_le64(TWEAK_SIZE * 8 * 2 + 2 + has_remainder);
- err = crypto_shash_init(shash);
- if (err)
- return err;
- err = crypto_shash_update(shash, (u8 *)tweak_length_block,
- POLYVAL_BLOCK_SIZE);
- if (err)
- return err;
- return crypto_shash_export(shash, hctr2_hashed_tweaklen(tctx, has_remainder));
+ struct polyval_ctx ctx;
+
+ for (int has_remainder = 0; has_remainder < 2; has_remainder++) {
+ const __le64 tweak_length_block[2] = {
+ cpu_to_le64(TWEAK_SIZE * 8 * 2 + 2 + has_remainder),
+ };
+
+ polyval_init(&ctx, &tctx->poly_key);
+ polyval_update(&ctx, (const u8 *)&tweak_length_block,
+ sizeof(tweak_length_block));
+ static_assert(sizeof(tweak_length_block) == POLYVAL_BLOCK_SIZE);
+ polyval_export_blkaligned(
+ &ctx, &tctx->hashed_tweaklens[has_remainder]);
+ }
+ memzero_explicit(&ctx, sizeof(ctx));
}
static int hctr2_setkey(struct crypto_skcipher *tfm, const u8 *key,
@@ -156,51 +123,42 @@ static int hctr2_setkey(struct crypto_skcipher *tfm, const u8 *key,
tctx->L[0] = 0x01;
crypto_cipher_encrypt_one(tctx->blockcipher, tctx->L, tctx->L);
- crypto_shash_clear_flags(tctx->polyval, CRYPTO_TFM_REQ_MASK);
- crypto_shash_set_flags(tctx->polyval, crypto_skcipher_get_flags(tfm) &
- CRYPTO_TFM_REQ_MASK);
- err = crypto_shash_setkey(tctx->polyval, hbar, BLOCKCIPHER_BLOCK_SIZE);
- if (err)
- return err;
+ static_assert(sizeof(hbar) == POLYVAL_BLOCK_SIZE);
+ polyval_preparekey(&tctx->poly_key, hbar);
memzero_explicit(hbar, sizeof(hbar));
- return hctr2_hash_tweaklen(tctx, true) ?: hctr2_hash_tweaklen(tctx, false);
+ hctr2_hash_tweaklens(tctx);
+ return 0;
}
-static int hctr2_hash_tweak(struct skcipher_request *req)
+static void hctr2_hash_tweak(struct skcipher_request *req)
{
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
const struct hctr2_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
struct hctr2_request_ctx *rctx = skcipher_request_ctx(req);
- struct shash_desc *hash_desc = &rctx->u.hash_desc;
- int err;
+ struct polyval_ctx *poly_ctx = &rctx->u.poly_ctx;
bool has_remainder = req->cryptlen % POLYVAL_BLOCK_SIZE;
- hash_desc->tfm = tctx->polyval;
- err = crypto_shash_import(hash_desc, hctr2_hashed_tweaklen(tctx, has_remainder));
- if (err)
- return err;
- err = crypto_shash_update(hash_desc, req->iv, TWEAK_SIZE);
- if (err)
- return err;
+ polyval_import_blkaligned(poly_ctx, &tctx->poly_key,
+ &tctx->hashed_tweaklens[has_remainder]);
+ polyval_update(poly_ctx, req->iv, TWEAK_SIZE);
// Store the hashed tweak, since we need it when computing both
// H(T || N) and H(T || V).
- return crypto_shash_export(hash_desc, hctr2_hashed_tweak(tctx, rctx));
+ static_assert(TWEAK_SIZE % POLYVAL_BLOCK_SIZE == 0);
+ polyval_export_blkaligned(poly_ctx, &rctx->hashed_tweak);
}
-static int hctr2_hash_message(struct skcipher_request *req,
- struct scatterlist *sgl,
- u8 digest[POLYVAL_DIGEST_SIZE])
+static void hctr2_hash_message(struct skcipher_request *req,
+ struct scatterlist *sgl,
+ u8 digest[POLYVAL_DIGEST_SIZE])
{
- static const u8 padding[BLOCKCIPHER_BLOCK_SIZE] = { 0x1 };
+ static const u8 padding = 0x1;
struct hctr2_request_ctx *rctx = skcipher_request_ctx(req);
- struct shash_desc *hash_desc = &rctx->u.hash_desc;
+ struct polyval_ctx *poly_ctx = &rctx->u.poly_ctx;
const unsigned int bulk_len = req->cryptlen - BLOCKCIPHER_BLOCK_SIZE;
struct sg_mapping_iter miter;
- unsigned int remainder = bulk_len % BLOCKCIPHER_BLOCK_SIZE;
int i;
- int err = 0;
int n = 0;
sg_miter_start(&miter, sgl, sg_nents(sgl),
@@ -208,22 +166,13 @@ static int hctr2_hash_message(struct skcipher_request *req,
for (i = 0; i < bulk_len; i += n) {
sg_miter_next(&miter);
n = min_t(unsigned int, miter.length, bulk_len - i);
- err = crypto_shash_update(hash_desc, miter.addr, n);
- if (err)
- break;
+ polyval_update(poly_ctx, miter.addr, n);
}
sg_miter_stop(&miter);
- if (err)
- return err;
-
- if (remainder) {
- err = crypto_shash_update(hash_desc, padding,
- BLOCKCIPHER_BLOCK_SIZE - remainder);
- if (err)
- return err;
- }
- return crypto_shash_final(hash_desc, digest);
+ if (req->cryptlen % BLOCKCIPHER_BLOCK_SIZE)
+ polyval_update(poly_ctx, &padding, 1);
+ polyval_final(poly_ctx, digest);
}
static int hctr2_finish(struct skcipher_request *req)
@@ -231,19 +180,14 @@ static int hctr2_finish(struct skcipher_request *req)
struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
const struct hctr2_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
struct hctr2_request_ctx *rctx = skcipher_request_ctx(req);
+ struct polyval_ctx *poly_ctx = &rctx->u.poly_ctx;
u8 digest[POLYVAL_DIGEST_SIZE];
- struct shash_desc *hash_desc = &rctx->u.hash_desc;
- int err;
// U = UU ^ H(T || V)
// or M = MM ^ H(T || N)
- hash_desc->tfm = tctx->polyval;
- err = crypto_shash_import(hash_desc, hctr2_hashed_tweak(tctx, rctx));
- if (err)
- return err;
- err = hctr2_hash_message(req, rctx->bulk_part_dst, digest);
- if (err)
- return err;
+ polyval_import_blkaligned(poly_ctx, &tctx->poly_key,
+ &rctx->hashed_tweak);
+ hctr2_hash_message(req, rctx->bulk_part_dst, digest);
crypto_xor(rctx->first_block, digest, BLOCKCIPHER_BLOCK_SIZE);
// Copy U (or M) into dst scatterlist
@@ -269,7 +213,6 @@ static int hctr2_crypt(struct skcipher_request *req, bool enc)
struct hctr2_request_ctx *rctx = skcipher_request_ctx(req);
u8 digest[POLYVAL_DIGEST_SIZE];
int bulk_len = req->cryptlen - BLOCKCIPHER_BLOCK_SIZE;
- int err;
// Requests must be at least one block
if (req->cryptlen < BLOCKCIPHER_BLOCK_SIZE)
@@ -287,12 +230,8 @@ static int hctr2_crypt(struct skcipher_request *req, bool enc)
// MM = M ^ H(T || N)
// or UU = U ^ H(T || V)
- err = hctr2_hash_tweak(req);
- if (err)
- return err;
- err = hctr2_hash_message(req, rctx->bulk_part_src, digest);
- if (err)
- return err;
+ hctr2_hash_tweak(req);
+ hctr2_hash_message(req, rctx->bulk_part_src, digest);
crypto_xor(digest, rctx->first_block, BLOCKCIPHER_BLOCK_SIZE);
// UU = E(MM)
@@ -338,8 +277,6 @@ static int hctr2_init_tfm(struct crypto_skcipher *tfm)
struct hctr2_tfm_ctx *tctx = crypto_skcipher_ctx(tfm);
struct crypto_skcipher *xctr;
struct crypto_cipher *blockcipher;
- struct crypto_shash *polyval;
- unsigned int subreq_size;
int err;
xctr = crypto_spawn_skcipher(&ictx->xctr_spawn);
@@ -352,31 +289,17 @@ static int hctr2_init_tfm(struct crypto_skcipher *tfm)
goto err_free_xctr;
}
- polyval = crypto_spawn_shash(&ictx->polyval_spawn);
- if (IS_ERR(polyval)) {
- err = PTR_ERR(polyval);
- goto err_free_blockcipher;
- }
-
tctx->xctr = xctr;
tctx->blockcipher = blockcipher;
- tctx->polyval = polyval;
BUILD_BUG_ON(offsetofend(struct hctr2_request_ctx, u) !=
sizeof(struct hctr2_request_ctx));
- subreq_size = max(sizeof_field(struct hctr2_request_ctx, u.hash_desc) +
- crypto_shash_descsize(polyval),
- sizeof_field(struct hctr2_request_ctx, u.xctr_req) +
- crypto_skcipher_reqsize(xctr));
-
- tctx->hashed_tweak_offset = offsetof(struct hctr2_request_ctx, u) +
- subreq_size;
- crypto_skcipher_set_reqsize(tfm, tctx->hashed_tweak_offset +
- crypto_shash_statesize(polyval));
+ crypto_skcipher_set_reqsize(
+ tfm, max(sizeof(struct hctr2_request_ctx),
+ offsetofend(struct hctr2_request_ctx, u.xctr_req) +
+ crypto_skcipher_reqsize(xctr)));
return 0;
-err_free_blockcipher:
- crypto_free_cipher(blockcipher);
err_free_xctr:
crypto_free_skcipher(xctr);
return err;
@@ -388,7 +311,6 @@ static void hctr2_exit_tfm(struct crypto_skcipher *tfm)
crypto_free_cipher(tctx->blockcipher);
crypto_free_skcipher(tctx->xctr);
- crypto_free_shash(tctx->polyval);
}
static void hctr2_free_instance(struct skcipher_instance *inst)
@@ -397,21 +319,17 @@ static void hctr2_free_instance(struct skcipher_instance *inst)
crypto_drop_cipher(&ictx->blockcipher_spawn);
crypto_drop_skcipher(&ictx->xctr_spawn);
- crypto_drop_shash(&ictx->polyval_spawn);
kfree(inst);
}
-static int hctr2_create_common(struct crypto_template *tmpl,
- struct rtattr **tb,
- const char *xctr_name,
- const char *polyval_name)
+static int hctr2_create_common(struct crypto_template *tmpl, struct rtattr **tb,
+ const char *xctr_name)
{
struct skcipher_alg_common *xctr_alg;
u32 mask;
struct skcipher_instance *inst;
struct hctr2_instance_ctx *ictx;
struct crypto_alg *blockcipher_alg;
- struct shash_alg *polyval_alg;
char blockcipher_name[CRYPTO_MAX_ALG_NAME];
int len;
int err;
@@ -457,19 +375,6 @@ static int hctr2_create_common(struct crypto_template *tmpl,
if (blockcipher_alg->cra_blocksize != BLOCKCIPHER_BLOCK_SIZE)
goto err_free_inst;
- /* Polyval ε-∆U hash function */
- err = crypto_grab_shash(&ictx->polyval_spawn,
- skcipher_crypto_instance(inst),
- polyval_name, 0, mask);
- if (err)
- goto err_free_inst;
- polyval_alg = crypto_spawn_shash_alg(&ictx->polyval_spawn);
-
- /* Ensure Polyval is being used */
- err = -EINVAL;
- if (strcmp(polyval_alg->base.cra_name, "polyval") != 0)
- goto err_free_inst;
-
/* Instance fields */
err = -ENAMETOOLONG;
@@ -477,22 +382,16 @@ static int hctr2_create_common(struct crypto_template *tmpl,
blockcipher_alg->cra_name) >= CRYPTO_MAX_ALG_NAME)
goto err_free_inst;
if (snprintf(inst->alg.base.cra_driver_name, CRYPTO_MAX_ALG_NAME,
- "hctr2_base(%s,%s)",
- xctr_alg->base.cra_driver_name,
- polyval_alg->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+ "hctr2_base(%s,polyval-lib)",
+ xctr_alg->base.cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
goto err_free_inst;
inst->alg.base.cra_blocksize = BLOCKCIPHER_BLOCK_SIZE;
- inst->alg.base.cra_ctxsize = sizeof(struct hctr2_tfm_ctx) +
- polyval_alg->statesize * 2;
+ inst->alg.base.cra_ctxsize = sizeof(struct hctr2_tfm_ctx);
inst->alg.base.cra_alignmask = xctr_alg->base.cra_alignmask;
- /*
- * The hash function is called twice, so it is weighted higher than the
- * xctr and blockcipher.
- */
inst->alg.base.cra_priority = (2 * xctr_alg->base.cra_priority +
- 4 * polyval_alg->base.cra_priority +
- blockcipher_alg->cra_priority) / 7;
+ blockcipher_alg->cra_priority) /
+ 3;
inst->alg.setkey = hctr2_setkey;
inst->alg.encrypt = hctr2_encrypt;
@@ -525,8 +424,11 @@ static int hctr2_create_base(struct crypto_template *tmpl, struct rtattr **tb)
polyval_name = crypto_attr_alg_name(tb[2]);
if (IS_ERR(polyval_name))
return PTR_ERR(polyval_name);
+ if (strcmp(polyval_name, "polyval") != 0 &&
+ strcmp(polyval_name, "polyval-lib") != 0)
+ return -ENOENT;
- return hctr2_create_common(tmpl, tb, xctr_name, polyval_name);
+ return hctr2_create_common(tmpl, tb, xctr_name);
}
static int hctr2_create(struct crypto_template *tmpl, struct rtattr **tb)
@@ -542,7 +444,7 @@ static int hctr2_create(struct crypto_template *tmpl, struct rtattr **tb)
blockcipher_name) >= CRYPTO_MAX_ALG_NAME)
return -ENAMETOOLONG;
- return hctr2_create_common(tmpl, tb, xctr_name, "polyval");
+ return hctr2_create_common(tmpl, tb, xctr_name);
}
static struct crypto_template hctr2_tmpls[] = {
diff --git a/crypto/jitterentropy-kcapi.c b/crypto/jitterentropy-kcapi.c
index a53de7affe8d..7c880cf34c52 100644
--- a/crypto/jitterentropy-kcapi.c
+++ b/crypto/jitterentropy-kcapi.c
@@ -48,7 +48,7 @@
#include "jitterentropy.h"
-#define JENT_CONDITIONING_HASH "sha3-256-generic"
+#define JENT_CONDITIONING_HASH "sha3-256"
/***************************************************************************
* Helper function
@@ -230,15 +230,7 @@ static int jent_kcapi_init(struct crypto_tfm *tfm)
spin_lock_init(&rng->jent_lock);
- /*
- * Use SHA3-256 as conditioner. We allocate only the generic
- * implementation as we are not interested in high-performance. The
- * execution time of the SHA3 operation is measured and adds to the
- * Jitter RNG's unpredictable behavior. If we have a slower hash
- * implementation, the execution timing variations are larger. When
- * using a fast implementation, we would need to call it more often
- * as its variations are lower.
- */
+ /* Use SHA3-256 as conditioner */
hash = crypto_alloc_shash(JENT_CONDITIONING_HASH, 0, 0);
if (IS_ERR(hash)) {
pr_err("Cannot allocate conditioning digest\n");
diff --git a/crypto/polyval-generic.c b/crypto/polyval-generic.c
deleted file mode 100644
index db8adb56e4ca..000000000000
--- a/crypto/polyval-generic.c
+++ /dev/null
@@ -1,205 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * POLYVAL: hash function for HCTR2.
- *
- * Copyright (c) 2007 Nokia Siemens Networks - Mikko Herranen <mh1@iki.fi>
- * Copyright (c) 2009 Intel Corp.
- * Author: Huang Ying <ying.huang@intel.com>
- * Copyright 2021 Google LLC
- */
-
-/*
- * Code based on crypto/ghash-generic.c
- *
- * POLYVAL is a keyed hash function similar to GHASH. POLYVAL uses a different
- * modulus for finite field multiplication which makes hardware accelerated
- * implementations on little-endian machines faster. POLYVAL is used in the
- * kernel to implement HCTR2, but was originally specified for AES-GCM-SIV
- * (RFC 8452).
- *
- * For more information see:
- * Length-preserving encryption with HCTR2:
- * https://eprint.iacr.org/2021/1441.pdf
- * AES-GCM-SIV: Nonce Misuse-Resistant Authenticated Encryption:
- * https://datatracker.ietf.org/doc/html/rfc8452
- *
- * Like GHASH, POLYVAL is not a cryptographic hash function and should
- * not be used outside of crypto modes explicitly designed to use POLYVAL.
- *
- * This implementation uses a convenient trick involving the GHASH and POLYVAL
- * fields. This trick allows multiplication in the POLYVAL field to be
- * implemented by using multiplication in the GHASH field as a subroutine. An
- * element of the POLYVAL field can be converted to an element of the GHASH
- * field by computing x*REVERSE(a), where REVERSE reverses the byte-ordering of
- * a. Similarly, an element of the GHASH field can be converted back to the
- * POLYVAL field by computing REVERSE(x^{-1}*a). For more information, see:
- * https://datatracker.ietf.org/doc/html/rfc8452#appendix-A
- *
- * By using this trick, we do not need to implement the POLYVAL field for the
- * generic implementation.
- *
- * Warning: this generic implementation is not intended to be used in practice
- * and is not constant time. For practical use, a hardware accelerated
- * implementation of POLYVAL should be used instead.
- *
- */
-
-#include <crypto/gf128mul.h>
-#include <crypto/internal/hash.h>
-#include <crypto/polyval.h>
-#include <crypto/utils.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/unaligned.h>
-
-struct polyval_tfm_ctx {
- struct gf128mul_4k *gf128;
-};
-
-struct polyval_desc_ctx {
- union {
- u8 buffer[POLYVAL_BLOCK_SIZE];
- be128 buffer128;
- };
-};
-
-static void copy_and_reverse(u8 dst[POLYVAL_BLOCK_SIZE],
- const u8 src[POLYVAL_BLOCK_SIZE])
-{
- u64 a = get_unaligned((const u64 *)&src[0]);
- u64 b = get_unaligned((const u64 *)&src[8]);
-
- put_unaligned(swab64(a), (u64 *)&dst[8]);
- put_unaligned(swab64(b), (u64 *)&dst[0]);
-}
-
-static int polyval_setkey(struct crypto_shash *tfm,
- const u8 *key, unsigned int keylen)
-{
- struct polyval_tfm_ctx *ctx = crypto_shash_ctx(tfm);
- be128 k;
-
- if (keylen != POLYVAL_BLOCK_SIZE)
- return -EINVAL;
-
- gf128mul_free_4k(ctx->gf128);
-
- BUILD_BUG_ON(sizeof(k) != POLYVAL_BLOCK_SIZE);
- copy_and_reverse((u8 *)&k, key);
- gf128mul_x_lle(&k, &k);
-
- ctx->gf128 = gf128mul_init_4k_lle(&k);
- memzero_explicit(&k, POLYVAL_BLOCK_SIZE);
-
- if (!ctx->gf128)
- return -ENOMEM;
-
- return 0;
-}
-
-static int polyval_init(struct shash_desc *desc)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
-
- memset(dctx, 0, sizeof(*dctx));
-
- return 0;
-}
-
-static int polyval_update(struct shash_desc *desc,
- const u8 *src, unsigned int srclen)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
- const struct polyval_tfm_ctx *ctx = crypto_shash_ctx(desc->tfm);
- u8 tmp[POLYVAL_BLOCK_SIZE];
-
- do {
- copy_and_reverse(tmp, src);
- crypto_xor(dctx->buffer, tmp, POLYVAL_BLOCK_SIZE);
- gf128mul_4k_lle(&dctx->buffer128, ctx->gf128);
- src += POLYVAL_BLOCK_SIZE;
- srclen -= POLYVAL_BLOCK_SIZE;
- } while (srclen >= POLYVAL_BLOCK_SIZE);
-
- return srclen;
-}
-
-static int polyval_finup(struct shash_desc *desc, const u8 *src,
- unsigned int len, u8 *dst)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
-
- if (len) {
- u8 tmp[POLYVAL_BLOCK_SIZE] = {};
-
- memcpy(tmp, src, len);
- polyval_update(desc, tmp, POLYVAL_BLOCK_SIZE);
- }
- copy_and_reverse(dst, dctx->buffer);
- return 0;
-}
-
-static int polyval_export(struct shash_desc *desc, void *out)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
-
- copy_and_reverse(out, dctx->buffer);
- return 0;
-}
-
-static int polyval_import(struct shash_desc *desc, const void *in)
-{
- struct polyval_desc_ctx *dctx = shash_desc_ctx(desc);
-
- copy_and_reverse(dctx->buffer, in);
- return 0;
-}
-
-static void polyval_exit_tfm(struct crypto_shash *tfm)
-{
- struct polyval_tfm_ctx *ctx = crypto_shash_ctx(tfm);
-
- gf128mul_free_4k(ctx->gf128);
-}
-
-static struct shash_alg polyval_alg = {
- .digestsize = POLYVAL_DIGEST_SIZE,
- .init = polyval_init,
- .update = polyval_update,
- .finup = polyval_finup,
- .setkey = polyval_setkey,
- .export = polyval_export,
- .import = polyval_import,
- .exit_tfm = polyval_exit_tfm,
- .statesize = sizeof(struct polyval_desc_ctx),
- .descsize = sizeof(struct polyval_desc_ctx),
- .base = {
- .cra_name = "polyval",
- .cra_driver_name = "polyval-generic",
- .cra_priority = 100,
- .cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .cra_blocksize = POLYVAL_BLOCK_SIZE,
- .cra_ctxsize = sizeof(struct polyval_tfm_ctx),
- .cra_module = THIS_MODULE,
- },
-};
-
-static int __init polyval_mod_init(void)
-{
- return crypto_register_shash(&polyval_alg);
-}
-
-static void __exit polyval_mod_exit(void)
-{
- crypto_unregister_shash(&polyval_alg);
-}
-
-module_init(polyval_mod_init);
-module_exit(polyval_mod_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("POLYVAL hash function");
-MODULE_ALIAS_CRYPTO("polyval");
-MODULE_ALIAS_CRYPTO("polyval-generic");
diff --git a/crypto/scatterwalk.c b/crypto/scatterwalk.c
index 1d010e2a1b1a..be0e24843806 100644
--- a/crypto/scatterwalk.c
+++ b/crypto/scatterwalk.c
@@ -10,25 +10,10 @@
*/
#include <crypto/scatterwalk.h>
-#include <linux/crypto.h>
-#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
-#include <linux/slab.h>
-
-enum {
- SKCIPHER_WALK_SLOW = 1 << 0,
- SKCIPHER_WALK_COPY = 1 << 1,
- SKCIPHER_WALK_DIFF = 1 << 2,
- SKCIPHER_WALK_SLEEP = 1 << 3,
-};
-
-static inline gfp_t skcipher_walk_gfp(struct skcipher_walk *walk)
-{
- return walk->flags & SKCIPHER_WALK_SLEEP ? GFP_KERNEL : GFP_ATOMIC;
-}
void scatterwalk_skip(struct scatter_walk *walk, unsigned int nbytes)
{
@@ -101,26 +86,97 @@ void memcpy_to_sglist(struct scatterlist *sg, unsigned int start,
}
EXPORT_SYMBOL_GPL(memcpy_to_sglist);
+/**
+ * memcpy_sglist() - Copy data from one scatterlist to another
+ * @dst: The destination scatterlist. Can be NULL if @nbytes == 0.
+ * @src: The source scatterlist. Can be NULL if @nbytes == 0.
+ * @nbytes: Number of bytes to copy
+ *
+ * The scatterlists can describe exactly the same memory, in which case this
+ * function is a no-op. No other overlaps are supported.
+ *
+ * Context: Any context
+ */
void memcpy_sglist(struct scatterlist *dst, struct scatterlist *src,
unsigned int nbytes)
{
- struct skcipher_walk walk = {};
+ unsigned int src_offset, dst_offset;
- if (unlikely(nbytes == 0)) /* in case sg == NULL */
+ if (unlikely(nbytes == 0)) /* in case src and/or dst is NULL */
return;
- walk.total = nbytes;
-
- scatterwalk_start(&walk.in, src);
- scatterwalk_start(&walk.out, dst);
+ src_offset = src->offset;
+ dst_offset = dst->offset;
+ for (;;) {
+ /* Compute the length to copy this step. */
+ unsigned int len = min3(src->offset + src->length - src_offset,
+ dst->offset + dst->length - dst_offset,
+ nbytes);
+ struct page *src_page = sg_page(src);
+ struct page *dst_page = sg_page(dst);
+ const void *src_virt;
+ void *dst_virt;
+
+ if (IS_ENABLED(CONFIG_HIGHMEM)) {
+ /* HIGHMEM: we may have to actually map the pages. */
+ const unsigned int src_oip = offset_in_page(src_offset);
+ const unsigned int dst_oip = offset_in_page(dst_offset);
+ const unsigned int limit = PAGE_SIZE;
+
+ /* Further limit len to not cross a page boundary. */
+ len = min3(len, limit - src_oip, limit - dst_oip);
+
+ /* Compute the source and destination pages. */
+ src_page += src_offset / PAGE_SIZE;
+ dst_page += dst_offset / PAGE_SIZE;
+
+ if (src_page != dst_page) {
+ /* Copy between different pages. */
+ memcpy_page(dst_page, dst_oip,
+ src_page, src_oip, len);
+ flush_dcache_page(dst_page);
+ } else if (src_oip != dst_oip) {
+ /* Copy between different parts of same page. */
+ dst_virt = kmap_local_page(dst_page);
+ memcpy(dst_virt + dst_oip, dst_virt + src_oip,
+ len);
+ kunmap_local(dst_virt);
+ flush_dcache_page(dst_page);
+ } /* Else, it's the same memory. No action needed. */
+ } else {
+ /*
+ * !HIGHMEM: no mapping needed. Just work in the linear
+ * buffer of each sg entry. Note that we can cross page
+ * boundaries, as they are not significant in this case.
+ */
+ src_virt = page_address(src_page) + src_offset;
+ dst_virt = page_address(dst_page) + dst_offset;
+ if (src_virt != dst_virt) {
+ memcpy(dst_virt, src_virt, len);
+ if (ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE)
+ __scatterwalk_flush_dcache_pages(
+ dst_page, dst_offset, len);
+ } /* Else, it's the same memory. No action needed. */
+ }
+ nbytes -= len;
+ if (nbytes == 0) /* No more to copy? */
+ break;
- skcipher_walk_first(&walk, true);
- do {
- if (walk.src.virt.addr != walk.dst.virt.addr)
- memcpy(walk.dst.virt.addr, walk.src.virt.addr,
- walk.nbytes);
- skcipher_walk_done(&walk, 0);
- } while (walk.nbytes);
+ /*
+ * There's more to copy. Advance the offsets by the length
+ * copied this step, and advance the sg entries as needed.
+ */
+ src_offset += len;
+ if (src_offset >= src->offset + src->length) {
+ src = sg_next(src);
+ src_offset = src->offset;
+ }
+ dst_offset += len;
+ if (dst_offset >= dst->offset + dst->length) {
+ dst = sg_next(dst);
+ dst_offset = dst->offset;
+ }
+ }
}
EXPORT_SYMBOL_GPL(memcpy_sglist);
@@ -146,236 +202,3 @@ struct scatterlist *scatterwalk_ffwd(struct scatterlist dst[2],
return dst;
}
EXPORT_SYMBOL_GPL(scatterwalk_ffwd);
-
-static int skcipher_next_slow(struct skcipher_walk *walk, unsigned int bsize)
-{
- unsigned alignmask = walk->alignmask;
- unsigned n;
- void *buffer;
-
- if (!walk->buffer)
- walk->buffer = walk->page;
- buffer = walk->buffer;
- if (!buffer) {
- /* Min size for a buffer of bsize bytes aligned to alignmask */
- n = bsize + (alignmask & ~(crypto_tfm_ctx_alignment() - 1));
-
- buffer = kzalloc(n, skcipher_walk_gfp(walk));
- if (!buffer)
- return skcipher_walk_done(walk, -ENOMEM);
- walk->buffer = buffer;
- }
-
- buffer = PTR_ALIGN(buffer, alignmask + 1);
- memcpy_from_scatterwalk(buffer, &walk->in, bsize);
- walk->out.__addr = buffer;
- walk->in.__addr = walk->out.addr;
-
- walk->nbytes = bsize;
- walk->flags |= SKCIPHER_WALK_SLOW;
-
- return 0;
-}
-
-static int skcipher_next_copy(struct skcipher_walk *walk)
-{
- void *tmp = walk->page;
-
- scatterwalk_map(&walk->in);
- memcpy(tmp, walk->in.addr, walk->nbytes);
- scatterwalk_unmap(&walk->in);
- /*
- * walk->in is advanced later when the number of bytes actually
- * processed (which might be less than walk->nbytes) is known.
- */
-
- walk->in.__addr = tmp;
- walk->out.__addr = tmp;
- return 0;
-}
-
-static int skcipher_next_fast(struct skcipher_walk *walk)
-{
- unsigned long diff;
-
- diff = offset_in_page(walk->in.offset) -
- offset_in_page(walk->out.offset);
- diff |= (u8 *)(sg_page(walk->in.sg) + (walk->in.offset >> PAGE_SHIFT)) -
- (u8 *)(sg_page(walk->out.sg) + (walk->out.offset >> PAGE_SHIFT));
-
- scatterwalk_map(&walk->out);
- walk->in.__addr = walk->out.__addr;
-
- if (diff) {
- walk->flags |= SKCIPHER_WALK_DIFF;
- scatterwalk_map(&walk->in);
- }
-
- return 0;
-}
-
-static int skcipher_walk_next(struct skcipher_walk *walk)
-{
- unsigned int bsize;
- unsigned int n;
-
- n = walk->total;
- bsize = min(walk->stride, max(n, walk->blocksize));
- n = scatterwalk_clamp(&walk->in, n);
- n = scatterwalk_clamp(&walk->out, n);
-
- if (unlikely(n < bsize)) {
- if (unlikely(walk->total < walk->blocksize))
- return skcipher_walk_done(walk, -EINVAL);
-
-slow_path:
- return skcipher_next_slow(walk, bsize);
- }
- walk->nbytes = n;
-
- if (unlikely((walk->in.offset | walk->out.offset) & walk->alignmask)) {
- if (!walk->page) {
- gfp_t gfp = skcipher_walk_gfp(walk);
-
- walk->page = (void *)__get_free_page(gfp);
- if (!walk->page)
- goto slow_path;
- }
- walk->flags |= SKCIPHER_WALK_COPY;
- return skcipher_next_copy(walk);
- }
-
- return skcipher_next_fast(walk);
-}
-
-static int skcipher_copy_iv(struct skcipher_walk *walk)
-{
- unsigned alignmask = walk->alignmask;
- unsigned ivsize = walk->ivsize;
- unsigned aligned_stride = ALIGN(walk->stride, alignmask + 1);
- unsigned size;
- u8 *iv;
-
- /* Min size for a buffer of stride + ivsize, aligned to alignmask */
- size = aligned_stride + ivsize +
- (alignmask & ~(crypto_tfm_ctx_alignment() - 1));
-
- walk->buffer = kmalloc(size, skcipher_walk_gfp(walk));
- if (!walk->buffer)
- return -ENOMEM;
-
- iv = PTR_ALIGN(walk->buffer, alignmask + 1) + aligned_stride;
-
- walk->iv = memcpy(iv, walk->iv, walk->ivsize);
- return 0;
-}
-
-int skcipher_walk_first(struct skcipher_walk *walk, bool atomic)
-{
- if (WARN_ON_ONCE(in_hardirq()))
- return -EDEADLK;
-
- walk->flags = atomic ? 0 : SKCIPHER_WALK_SLEEP;
-
- walk->buffer = NULL;
- if (unlikely(((unsigned long)walk->iv & walk->alignmask))) {
- int err = skcipher_copy_iv(walk);
- if (err)
- return err;
- }
-
- walk->page = NULL;
-
- return skcipher_walk_next(walk);
-}
-EXPORT_SYMBOL_GPL(skcipher_walk_first);
-
-/**
- * skcipher_walk_done() - finish one step of a skcipher_walk
- * @walk: the skcipher_walk
- * @res: number of bytes *not* processed (>= 0) from walk->nbytes,
- * or a -errno value to terminate the walk due to an error
- *
- * This function cleans up after one step of walking through the source and
- * destination scatterlists, and advances to the next step if applicable.
- * walk->nbytes is set to the number of bytes available in the next step,
- * walk->total is set to the new total number of bytes remaining, and
- * walk->{src,dst}.virt.addr is set to the next pair of data pointers. If there
- * is no more data, or if an error occurred (i.e. -errno return), then
- * walk->nbytes and walk->total are set to 0 and all resources owned by the
- * skcipher_walk are freed.
- *
- * Return: 0 or a -errno value. If @res was a -errno value then it will be
- * returned, but other errors may occur too.
- */
-int skcipher_walk_done(struct skcipher_walk *walk, int res)
-{
- unsigned int n = walk->nbytes; /* num bytes processed this step */
- unsigned int total = 0; /* new total remaining */
-
- if (!n)
- goto finish;
-
- if (likely(res >= 0)) {
- n -= res; /* subtract num bytes *not* processed */
- total = walk->total - n;
- }
-
- if (likely(!(walk->flags & (SKCIPHER_WALK_SLOW |
- SKCIPHER_WALK_COPY |
- SKCIPHER_WALK_DIFF)))) {
- scatterwalk_advance(&walk->in, n);
- } else if (walk->flags & SKCIPHER_WALK_DIFF) {
- scatterwalk_done_src(&walk->in, n);
- } else if (walk->flags & SKCIPHER_WALK_COPY) {
- scatterwalk_advance(&walk->in, n);
- scatterwalk_map(&walk->out);
- memcpy(walk->out.addr, walk->page, n);
- } else { /* SKCIPHER_WALK_SLOW */
- if (res > 0) {
- /*
- * Didn't process all bytes. Either the algorithm is
- * broken, or this was the last step and it turned out
- * the message wasn't evenly divisible into blocks but
- * the algorithm requires it.
- */
- res = -EINVAL;
- total = 0;
- } else
- memcpy_to_scatterwalk(&walk->out, walk->out.addr, n);
- goto dst_done;
- }
-
- scatterwalk_done_dst(&walk->out, n);
-dst_done:
-
- if (res > 0)
- res = 0;
-
- walk->total = total;
- walk->nbytes = 0;
-
- if (total) {
- if (walk->flags & SKCIPHER_WALK_SLEEP)
- cond_resched();
- walk->flags &= ~(SKCIPHER_WALK_SLOW | SKCIPHER_WALK_COPY |
- SKCIPHER_WALK_DIFF);
- return skcipher_walk_next(walk);
- }
-
-finish:
- /* Short-circuit for the common/fast path. */
- if (!((unsigned long)walk->buffer | (unsigned long)walk->page))
- goto out;
-
- if (walk->iv != walk->oiv)
- memcpy(walk->oiv, walk->iv, walk->ivsize);
- if (walk->buffer != walk->page)
- kfree(walk->buffer);
- if (walk->page)
- free_page((unsigned long)walk->page);
-
-out:
- return res;
-}
-EXPORT_SYMBOL_GPL(skcipher_walk_done);
diff --git a/crypto/sha3.c b/crypto/sha3.c
new file mode 100644
index 000000000000..8f364979ec89
--- /dev/null
+++ b/crypto/sha3.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Crypto API support for SHA-3
+ * (https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf)
+ */
+#include <crypto/internal/hash.h>
+#include <crypto/sha3.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define SHA3_CTX(desc) ((struct sha3_ctx *)shash_desc_ctx(desc))
+
+static int crypto_sha3_224_init(struct shash_desc *desc)
+{
+ sha3_224_init(SHA3_CTX(desc));
+ return 0;
+}
+
+static int crypto_sha3_256_init(struct shash_desc *desc)
+{
+ sha3_256_init(SHA3_CTX(desc));
+ return 0;
+}
+
+static int crypto_sha3_384_init(struct shash_desc *desc)
+{
+ sha3_384_init(SHA3_CTX(desc));
+ return 0;
+}
+
+static int crypto_sha3_512_init(struct shash_desc *desc)
+{
+ sha3_512_init(SHA3_CTX(desc));
+ return 0;
+}
+
+static int crypto_sha3_update(struct shash_desc *desc, const u8 *data,
+ unsigned int len)
+{
+ sha3_update(SHA3_CTX(desc), data, len);
+ return 0;
+}
+
+static int crypto_sha3_final(struct shash_desc *desc, u8 *out)
+{
+ sha3_final(SHA3_CTX(desc), out);
+ return 0;
+}
+
+static int crypto_sha3_224_digest(struct shash_desc *desc,
+ const u8 *data, unsigned int len, u8 *out)
+{
+ sha3_224(data, len, out);
+ return 0;
+}
+
+static int crypto_sha3_256_digest(struct shash_desc *desc,
+ const u8 *data, unsigned int len, u8 *out)
+{
+ sha3_256(data, len, out);
+ return 0;
+}
+
+static int crypto_sha3_384_digest(struct shash_desc *desc,
+ const u8 *data, unsigned int len, u8 *out)
+{
+ sha3_384(data, len, out);
+ return 0;
+}
+
+static int crypto_sha3_512_digest(struct shash_desc *desc,
+ const u8 *data, unsigned int len, u8 *out)
+{
+ sha3_512(data, len, out);
+ return 0;
+}
+
+static int crypto_sha3_export_core(struct shash_desc *desc, void *out)
+{
+ memcpy(out, SHA3_CTX(desc), sizeof(struct sha3_ctx));
+ return 0;
+}
+
+static int crypto_sha3_import_core(struct shash_desc *desc, const void *in)
+{
+ memcpy(SHA3_CTX(desc), in, sizeof(struct sha3_ctx));
+ return 0;
+}
+
+static struct shash_alg algs[] = { {
+ .digestsize = SHA3_224_DIGEST_SIZE,
+ .init = crypto_sha3_224_init,
+ .update = crypto_sha3_update,
+ .final = crypto_sha3_final,
+ .digest = crypto_sha3_224_digest,
+ .export_core = crypto_sha3_export_core,
+ .import_core = crypto_sha3_import_core,
+ .descsize = sizeof(struct sha3_ctx),
+ .base.cra_name = "sha3-224",
+ .base.cra_driver_name = "sha3-224-lib",
+ .base.cra_blocksize = SHA3_224_BLOCK_SIZE,
+ .base.cra_module = THIS_MODULE,
+}, {
+ .digestsize = SHA3_256_DIGEST_SIZE,
+ .init = crypto_sha3_256_init,
+ .update = crypto_sha3_update,
+ .final = crypto_sha3_final,
+ .digest = crypto_sha3_256_digest,
+ .export_core = crypto_sha3_export_core,
+ .import_core = crypto_sha3_import_core,
+ .descsize = sizeof(struct sha3_ctx),
+ .base.cra_name = "sha3-256",
+ .base.cra_driver_name = "sha3-256-lib",
+ .base.cra_blocksize = SHA3_256_BLOCK_SIZE,
+ .base.cra_module = THIS_MODULE,
+}, {
+ .digestsize = SHA3_384_DIGEST_SIZE,
+ .init = crypto_sha3_384_init,
+ .update = crypto_sha3_update,
+ .final = crypto_sha3_final,
+ .digest = crypto_sha3_384_digest,
+ .export_core = crypto_sha3_export_core,
+ .import_core = crypto_sha3_import_core,
+ .descsize = sizeof(struct sha3_ctx),
+ .base.cra_name = "sha3-384",
+ .base.cra_driver_name = "sha3-384-lib",
+ .base.cra_blocksize = SHA3_384_BLOCK_SIZE,
+ .base.cra_module = THIS_MODULE,
+}, {
+ .digestsize = SHA3_512_DIGEST_SIZE,
+ .init = crypto_sha3_512_init,
+ .update = crypto_sha3_update,
+ .final = crypto_sha3_final,
+ .digest = crypto_sha3_512_digest,
+ .export_core = crypto_sha3_export_core,
+ .import_core = crypto_sha3_import_core,
+ .descsize = sizeof(struct sha3_ctx),
+ .base.cra_name = "sha3-512",
+ .base.cra_driver_name = "sha3-512-lib",
+ .base.cra_blocksize = SHA3_512_BLOCK_SIZE,
+ .base.cra_module = THIS_MODULE,
+} };
+
+static int __init crypto_sha3_mod_init(void)
+{
+ return crypto_register_shashes(algs, ARRAY_SIZE(algs));
+}
+module_init(crypto_sha3_mod_init);
+
+static void __exit crypto_sha3_mod_exit(void)
+{
+ crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
+}
+module_exit(crypto_sha3_mod_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Crypto API support for SHA-3");
+
+MODULE_ALIAS_CRYPTO("sha3-224");
+MODULE_ALIAS_CRYPTO("sha3-224-lib");
+MODULE_ALIAS_CRYPTO("sha3-256");
+MODULE_ALIAS_CRYPTO("sha3-256-lib");
+MODULE_ALIAS_CRYPTO("sha3-384");
+MODULE_ALIAS_CRYPTO("sha3-384-lib");
+MODULE_ALIAS_CRYPTO("sha3-512");
+MODULE_ALIAS_CRYPTO("sha3-512-lib");
diff --git a/crypto/sha3_generic.c b/crypto/sha3_generic.c
deleted file mode 100644
index 41d1e506e6de..000000000000
--- a/crypto/sha3_generic.c
+++ /dev/null
@@ -1,290 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Cryptographic API.
- *
- * SHA-3, as specified in
- * https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
- *
- * SHA-3 code by Jeff Garzik <jeff@garzik.org>
- * Ard Biesheuvel <ard.biesheuvel@linaro.org>
- */
-#include <crypto/internal/hash.h>
-#include <crypto/sha3.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/unaligned.h>
-
-/*
- * On some 32-bit architectures (h8300), GCC ends up using
- * over 1 KB of stack if we inline the round calculation into the loop
- * in keccakf(). On the other hand, on 64-bit architectures with plenty
- * of [64-bit wide] general purpose registers, not inlining it severely
- * hurts performance. So let's use 64-bitness as a heuristic to decide
- * whether to inline or not.
- */
-#ifdef CONFIG_64BIT
-#define SHA3_INLINE inline
-#else
-#define SHA3_INLINE noinline
-#endif
-
-#define KECCAK_ROUNDS 24
-
-static const u64 keccakf_rndc[24] = {
- 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL,
- 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL,
- 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL,
- 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL,
- 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL,
- 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL,
- 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL,
- 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL
-};
-
-/* update the state with given number of rounds */
-
-static SHA3_INLINE void keccakf_round(u64 st[25])
-{
- u64 t[5], tt, bc[5];
-
- /* Theta */
- bc[0] = st[0] ^ st[5] ^ st[10] ^ st[15] ^ st[20];
- bc[1] = st[1] ^ st[6] ^ st[11] ^ st[16] ^ st[21];
- bc[2] = st[2] ^ st[7] ^ st[12] ^ st[17] ^ st[22];
- bc[3] = st[3] ^ st[8] ^ st[13] ^ st[18] ^ st[23];
- bc[4] = st[4] ^ st[9] ^ st[14] ^ st[19] ^ st[24];
-
- t[0] = bc[4] ^ rol64(bc[1], 1);
- t[1] = bc[0] ^ rol64(bc[2], 1);
- t[2] = bc[1] ^ rol64(bc[3], 1);
- t[3] = bc[2] ^ rol64(bc[4], 1);
- t[4] = bc[3] ^ rol64(bc[0], 1);
-
- st[0] ^= t[0];
-
- /* Rho Pi */
- tt = st[1];
- st[ 1] = rol64(st[ 6] ^ t[1], 44);
- st[ 6] = rol64(st[ 9] ^ t[4], 20);
- st[ 9] = rol64(st[22] ^ t[2], 61);
- st[22] = rol64(st[14] ^ t[4], 39);
- st[14] = rol64(st[20] ^ t[0], 18);
- st[20] = rol64(st[ 2] ^ t[2], 62);
- st[ 2] = rol64(st[12] ^ t[2], 43);
- st[12] = rol64(st[13] ^ t[3], 25);
- st[13] = rol64(st[19] ^ t[4], 8);
- st[19] = rol64(st[23] ^ t[3], 56);
- st[23] = rol64(st[15] ^ t[0], 41);
- st[15] = rol64(st[ 4] ^ t[4], 27);
- st[ 4] = rol64(st[24] ^ t[4], 14);
- st[24] = rol64(st[21] ^ t[1], 2);
- st[21] = rol64(st[ 8] ^ t[3], 55);
- st[ 8] = rol64(st[16] ^ t[1], 45);
- st[16] = rol64(st[ 5] ^ t[0], 36);
- st[ 5] = rol64(st[ 3] ^ t[3], 28);
- st[ 3] = rol64(st[18] ^ t[3], 21);
- st[18] = rol64(st[17] ^ t[2], 15);
- st[17] = rol64(st[11] ^ t[1], 10);
- st[11] = rol64(st[ 7] ^ t[2], 6);
- st[ 7] = rol64(st[10] ^ t[0], 3);
- st[10] = rol64( tt ^ t[1], 1);
-
- /* Chi */
- bc[ 0] = ~st[ 1] & st[ 2];
- bc[ 1] = ~st[ 2] & st[ 3];
- bc[ 2] = ~st[ 3] & st[ 4];
- bc[ 3] = ~st[ 4] & st[ 0];
- bc[ 4] = ~st[ 0] & st[ 1];
- st[ 0] ^= bc[ 0];
- st[ 1] ^= bc[ 1];
- st[ 2] ^= bc[ 2];
- st[ 3] ^= bc[ 3];
- st[ 4] ^= bc[ 4];
-
- bc[ 0] = ~st[ 6] & st[ 7];
- bc[ 1] = ~st[ 7] & st[ 8];
- bc[ 2] = ~st[ 8] & st[ 9];
- bc[ 3] = ~st[ 9] & st[ 5];
- bc[ 4] = ~st[ 5] & st[ 6];
- st[ 5] ^= bc[ 0];
- st[ 6] ^= bc[ 1];
- st[ 7] ^= bc[ 2];
- st[ 8] ^= bc[ 3];
- st[ 9] ^= bc[ 4];
-
- bc[ 0] = ~st[11] & st[12];
- bc[ 1] = ~st[12] & st[13];
- bc[ 2] = ~st[13] & st[14];
- bc[ 3] = ~st[14] & st[10];
- bc[ 4] = ~st[10] & st[11];
- st[10] ^= bc[ 0];
- st[11] ^= bc[ 1];
- st[12] ^= bc[ 2];
- st[13] ^= bc[ 3];
- st[14] ^= bc[ 4];
-
- bc[ 0] = ~st[16] & st[17];
- bc[ 1] = ~st[17] & st[18];
- bc[ 2] = ~st[18] & st[19];
- bc[ 3] = ~st[19] & st[15];
- bc[ 4] = ~st[15] & st[16];
- st[15] ^= bc[ 0];
- st[16] ^= bc[ 1];
- st[17] ^= bc[ 2];
- st[18] ^= bc[ 3];
- st[19] ^= bc[ 4];
-
- bc[ 0] = ~st[21] & st[22];
- bc[ 1] = ~st[22] & st[23];
- bc[ 2] = ~st[23] & st[24];
- bc[ 3] = ~st[24] & st[20];
- bc[ 4] = ~st[20] & st[21];
- st[20] ^= bc[ 0];
- st[21] ^= bc[ 1];
- st[22] ^= bc[ 2];
- st[23] ^= bc[ 3];
- st[24] ^= bc[ 4];
-}
-
-static void keccakf(u64 st[25])
-{
- int round;
-
- for (round = 0; round < KECCAK_ROUNDS; round++) {
- keccakf_round(st);
- /* Iota */
- st[0] ^= keccakf_rndc[round];
- }
-}
-
-int crypto_sha3_init(struct shash_desc *desc)
-{
- struct sha3_state *sctx = shash_desc_ctx(desc);
-
- memset(sctx->st, 0, sizeof(sctx->st));
- return 0;
-}
-EXPORT_SYMBOL(crypto_sha3_init);
-
-static int crypto_sha3_update(struct shash_desc *desc, const u8 *data,
- unsigned int len)
-{
- unsigned int rsiz = crypto_shash_blocksize(desc->tfm);
- struct sha3_state *sctx = shash_desc_ctx(desc);
- unsigned int rsizw = rsiz / 8;
-
- do {
- int i;
-
- for (i = 0; i < rsizw; i++)
- sctx->st[i] ^= get_unaligned_le64(data + 8 * i);
- keccakf(sctx->st);
-
- data += rsiz;
- len -= rsiz;
- } while (len >= rsiz);
- return len;
-}
-
-static int crypto_sha3_finup(struct shash_desc *desc, const u8 *src,
- unsigned int len, u8 *out)
-{
- unsigned int digest_size = crypto_shash_digestsize(desc->tfm);
- unsigned int rsiz = crypto_shash_blocksize(desc->tfm);
- struct sha3_state *sctx = shash_desc_ctx(desc);
- __le64 block[SHA3_224_BLOCK_SIZE / 8] = {};
- __le64 *digest = (__le64 *)out;
- unsigned int rsizw = rsiz / 8;
- u8 *p;
- int i;
-
- p = memcpy(block, src, len);
- p[len++] = 0x06;
- p[rsiz - 1] |= 0x80;
-
- for (i = 0; i < rsizw; i++)
- sctx->st[i] ^= le64_to_cpu(block[i]);
- memzero_explicit(block, sizeof(block));
-
- keccakf(sctx->st);
-
- for (i = 0; i < digest_size / 8; i++)
- put_unaligned_le64(sctx->st[i], digest++);
-
- if (digest_size & 4)
- put_unaligned_le32(sctx->st[i], (__le32 *)digest);
-
- return 0;
-}
-
-static struct shash_alg algs[] = { {
- .digestsize = SHA3_224_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = crypto_sha3_update,
- .finup = crypto_sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-224",
- .base.cra_driver_name = "sha3-224-generic",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_224_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
-}, {
- .digestsize = SHA3_256_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = crypto_sha3_update,
- .finup = crypto_sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-256",
- .base.cra_driver_name = "sha3-256-generic",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_256_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
-}, {
- .digestsize = SHA3_384_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = crypto_sha3_update,
- .finup = crypto_sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-384",
- .base.cra_driver_name = "sha3-384-generic",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_384_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
-}, {
- .digestsize = SHA3_512_DIGEST_SIZE,
- .init = crypto_sha3_init,
- .update = crypto_sha3_update,
- .finup = crypto_sha3_finup,
- .descsize = SHA3_STATE_SIZE,
- .base.cra_name = "sha3-512",
- .base.cra_driver_name = "sha3-512-generic",
- .base.cra_flags = CRYPTO_AHASH_ALG_BLOCK_ONLY,
- .base.cra_blocksize = SHA3_512_BLOCK_SIZE,
- .base.cra_module = THIS_MODULE,
-} };
-
-static int __init sha3_generic_mod_init(void)
-{
- return crypto_register_shashes(algs, ARRAY_SIZE(algs));
-}
-
-static void __exit sha3_generic_mod_fini(void)
-{
- crypto_unregister_shashes(algs, ARRAY_SIZE(algs));
-}
-
-module_init(sha3_generic_mod_init);
-module_exit(sha3_generic_mod_fini);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SHA-3 Secure Hash Algorithm");
-
-MODULE_ALIAS_CRYPTO("sha3-224");
-MODULE_ALIAS_CRYPTO("sha3-224-generic");
-MODULE_ALIAS_CRYPTO("sha3-256");
-MODULE_ALIAS_CRYPTO("sha3-256-generic");
-MODULE_ALIAS_CRYPTO("sha3-384");
-MODULE_ALIAS_CRYPTO("sha3-384-generic");
-MODULE_ALIAS_CRYPTO("sha3-512");
-MODULE_ALIAS_CRYPTO("sha3-512-generic");
diff --git a/crypto/skcipher.c b/crypto/skcipher.c
index 8fa5d9686d08..14a820cb06c7 100644
--- a/crypto/skcipher.c
+++ b/crypto/skcipher.c
@@ -17,6 +17,7 @@
#include <linux/cryptouser.h>
#include <linux/err.h>
#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -27,14 +28,258 @@
#define CRYPTO_ALG_TYPE_SKCIPHER_MASK 0x0000000e
+enum {
+ SKCIPHER_WALK_SLOW = 1 << 0,
+ SKCIPHER_WALK_COPY = 1 << 1,
+ SKCIPHER_WALK_DIFF = 1 << 2,
+ SKCIPHER_WALK_SLEEP = 1 << 3,
+};
+
static const struct crypto_type crypto_skcipher_type;
+static int skcipher_walk_next(struct skcipher_walk *walk);
+
+static inline gfp_t skcipher_walk_gfp(struct skcipher_walk *walk)
+{
+ return walk->flags & SKCIPHER_WALK_SLEEP ? GFP_KERNEL : GFP_ATOMIC;
+}
+
static inline struct skcipher_alg *__crypto_skcipher_alg(
struct crypto_alg *alg)
{
return container_of(alg, struct skcipher_alg, base);
}
+/**
+ * skcipher_walk_done() - finish one step of a skcipher_walk
+ * @walk: the skcipher_walk
+ * @res: number of bytes *not* processed (>= 0) from walk->nbytes,
+ * or a -errno value to terminate the walk due to an error
+ *
+ * This function cleans up after one step of walking through the source and
+ * destination scatterlists, and advances to the next step if applicable.
+ * walk->nbytes is set to the number of bytes available in the next step,
+ * walk->total is set to the new total number of bytes remaining, and
+ * walk->{src,dst}.virt.addr is set to the next pair of data pointers. If there
+ * is no more data, or if an error occurred (i.e. -errno return), then
+ * walk->nbytes and walk->total are set to 0 and all resources owned by the
+ * skcipher_walk are freed.
+ *
+ * Return: 0 or a -errno value. If @res was a -errno value then it will be
+ * returned, but other errors may occur too.
+ */
+int skcipher_walk_done(struct skcipher_walk *walk, int res)
+{
+ unsigned int n = walk->nbytes; /* num bytes processed this step */
+ unsigned int total = 0; /* new total remaining */
+
+ if (!n)
+ goto finish;
+
+ if (likely(res >= 0)) {
+ n -= res; /* subtract num bytes *not* processed */
+ total = walk->total - n;
+ }
+
+ if (likely(!(walk->flags & (SKCIPHER_WALK_SLOW |
+ SKCIPHER_WALK_COPY |
+ SKCIPHER_WALK_DIFF)))) {
+ scatterwalk_advance(&walk->in, n);
+ } else if (walk->flags & SKCIPHER_WALK_DIFF) {
+ scatterwalk_done_src(&walk->in, n);
+ } else if (walk->flags & SKCIPHER_WALK_COPY) {
+ scatterwalk_advance(&walk->in, n);
+ scatterwalk_map(&walk->out);
+ memcpy(walk->out.addr, walk->page, n);
+ } else { /* SKCIPHER_WALK_SLOW */
+ if (res > 0) {
+ /*
+ * Didn't process all bytes. Either the algorithm is
+ * broken, or this was the last step and it turned out
+ * the message wasn't evenly divisible into blocks but
+ * the algorithm requires it.
+ */
+ res = -EINVAL;
+ total = 0;
+ } else
+ memcpy_to_scatterwalk(&walk->out, walk->out.addr, n);
+ goto dst_done;
+ }
+
+ scatterwalk_done_dst(&walk->out, n);
+dst_done:
+
+ if (res > 0)
+ res = 0;
+
+ walk->total = total;
+ walk->nbytes = 0;
+
+ if (total) {
+ if (walk->flags & SKCIPHER_WALK_SLEEP)
+ cond_resched();
+ walk->flags &= ~(SKCIPHER_WALK_SLOW | SKCIPHER_WALK_COPY |
+ SKCIPHER_WALK_DIFF);
+ return skcipher_walk_next(walk);
+ }
+
+finish:
+ /* Short-circuit for the common/fast path. */
+ if (!((unsigned long)walk->buffer | (unsigned long)walk->page))
+ goto out;
+
+ if (walk->iv != walk->oiv)
+ memcpy(walk->oiv, walk->iv, walk->ivsize);
+ if (walk->buffer != walk->page)
+ kfree(walk->buffer);
+ if (walk->page)
+ free_page((unsigned long)walk->page);
+
+out:
+ return res;
+}
+EXPORT_SYMBOL_GPL(skcipher_walk_done);
+
+static int skcipher_next_slow(struct skcipher_walk *walk, unsigned int bsize)
+{
+ unsigned alignmask = walk->alignmask;
+ unsigned n;
+ void *buffer;
+
+ if (!walk->buffer)
+ walk->buffer = walk->page;
+ buffer = walk->buffer;
+ if (!buffer) {
+ /* Min size for a buffer of bsize bytes aligned to alignmask */
+ n = bsize + (alignmask & ~(crypto_tfm_ctx_alignment() - 1));
+
+ buffer = kzalloc(n, skcipher_walk_gfp(walk));
+ if (!buffer)
+ return skcipher_walk_done(walk, -ENOMEM);
+ walk->buffer = buffer;
+ }
+
+ buffer = PTR_ALIGN(buffer, alignmask + 1);
+ memcpy_from_scatterwalk(buffer, &walk->in, bsize);
+ walk->out.__addr = buffer;
+ walk->in.__addr = walk->out.addr;
+
+ walk->nbytes = bsize;
+ walk->flags |= SKCIPHER_WALK_SLOW;
+
+ return 0;
+}
+
+static int skcipher_next_copy(struct skcipher_walk *walk)
+{
+ void *tmp = walk->page;
+
+ scatterwalk_map(&walk->in);
+ memcpy(tmp, walk->in.addr, walk->nbytes);
+ scatterwalk_unmap(&walk->in);
+ /*
+ * walk->in is advanced later when the number of bytes actually
+ * processed (which might be less than walk->nbytes) is known.
+ */
+
+ walk->in.__addr = tmp;
+ walk->out.__addr = tmp;
+ return 0;
+}
+
+static int skcipher_next_fast(struct skcipher_walk *walk)
+{
+ unsigned long diff;
+
+ diff = offset_in_page(walk->in.offset) -
+ offset_in_page(walk->out.offset);
+ diff |= (u8 *)(sg_page(walk->in.sg) + (walk->in.offset >> PAGE_SHIFT)) -
+ (u8 *)(sg_page(walk->out.sg) + (walk->out.offset >> PAGE_SHIFT));
+
+ scatterwalk_map(&walk->out);
+ walk->in.__addr = walk->out.__addr;
+
+ if (diff) {
+ walk->flags |= SKCIPHER_WALK_DIFF;
+ scatterwalk_map(&walk->in);
+ }
+
+ return 0;
+}
+
+static int skcipher_walk_next(struct skcipher_walk *walk)
+{
+ unsigned int bsize;
+ unsigned int n;
+
+ n = walk->total;
+ bsize = min(walk->stride, max(n, walk->blocksize));
+ n = scatterwalk_clamp(&walk->in, n);
+ n = scatterwalk_clamp(&walk->out, n);
+
+ if (unlikely(n < bsize)) {
+ if (unlikely(walk->total < walk->blocksize))
+ return skcipher_walk_done(walk, -EINVAL);
+
+slow_path:
+ return skcipher_next_slow(walk, bsize);
+ }
+ walk->nbytes = n;
+
+ if (unlikely((walk->in.offset | walk->out.offset) & walk->alignmask)) {
+ if (!walk->page) {
+ gfp_t gfp = skcipher_walk_gfp(walk);
+
+ walk->page = (void *)__get_free_page(gfp);
+ if (!walk->page)
+ goto slow_path;
+ }
+ walk->flags |= SKCIPHER_WALK_COPY;
+ return skcipher_next_copy(walk);
+ }
+
+ return skcipher_next_fast(walk);
+}
+
+static int skcipher_copy_iv(struct skcipher_walk *walk)
+{
+ unsigned alignmask = walk->alignmask;
+ unsigned ivsize = walk->ivsize;
+ unsigned aligned_stride = ALIGN(walk->stride, alignmask + 1);
+ unsigned size;
+ u8 *iv;
+
+ /* Min size for a buffer of stride + ivsize, aligned to alignmask */
+ size = aligned_stride + ivsize +
+ (alignmask & ~(crypto_tfm_ctx_alignment() - 1));
+
+ walk->buffer = kmalloc(size, skcipher_walk_gfp(walk));
+ if (!walk->buffer)
+ return -ENOMEM;
+
+ iv = PTR_ALIGN(walk->buffer, alignmask + 1) + aligned_stride;
+
+ walk->iv = memcpy(iv, walk->iv, walk->ivsize);
+ return 0;
+}
+
+static int skcipher_walk_first(struct skcipher_walk *walk)
+{
+ if (WARN_ON_ONCE(in_hardirq()))
+ return -EDEADLK;
+
+ walk->buffer = NULL;
+ if (unlikely(((unsigned long)walk->iv & walk->alignmask))) {
+ int err = skcipher_copy_iv(walk);
+ if (err)
+ return err;
+ }
+
+ walk->page = NULL;
+
+ return skcipher_walk_next(walk);
+}
+
int skcipher_walk_virt(struct skcipher_walk *__restrict walk,
struct skcipher_request *__restrict req, bool atomic)
{
@@ -49,8 +294,10 @@ int skcipher_walk_virt(struct skcipher_walk *__restrict walk,
walk->nbytes = 0;
walk->iv = req->iv;
walk->oiv = req->iv;
- if (!(req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP))
- atomic = true;
+ if ((req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) && !atomic)
+ walk->flags = SKCIPHER_WALK_SLEEP;
+ else
+ walk->flags = 0;
if (unlikely(!walk->total))
return 0;
@@ -67,7 +314,7 @@ int skcipher_walk_virt(struct skcipher_walk *__restrict walk,
else
walk->stride = alg->walksize;
- return skcipher_walk_first(walk, atomic);
+ return skcipher_walk_first(walk);
}
EXPORT_SYMBOL_GPL(skcipher_walk_virt);
@@ -80,8 +327,10 @@ static int skcipher_walk_aead_common(struct skcipher_walk *__restrict walk,
walk->nbytes = 0;
walk->iv = req->iv;
walk->oiv = req->iv;
- if (!(req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP))
- atomic = true;
+ if ((req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) && !atomic)
+ walk->flags = SKCIPHER_WALK_SLEEP;
+ else
+ walk->flags = 0;
if (unlikely(!walk->total))
return 0;
@@ -94,7 +343,7 @@ static int skcipher_walk_aead_common(struct skcipher_walk *__restrict walk,
walk->ivsize = crypto_aead_ivsize(tfm);
walk->alignmask = crypto_aead_alignmask(tfm);
- return skcipher_walk_first(walk, atomic);
+ return skcipher_walk_first(walk);
}
int skcipher_walk_aead_encrypt(struct skcipher_walk *__restrict walk,
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index d1d88debbd71..62fef100e599 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -1690,10 +1690,6 @@ static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb)
ret = min(ret, tcrypt_test("ccm(sm4)"));
break;
- case 57:
- ret = min(ret, tcrypt_test("polyval"));
- break;
-
case 58:
ret = min(ret, tcrypt_test("gcm(aria)"));
break;
@@ -1758,10 +1754,6 @@ static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb)
ret = min(ret, tcrypt_test("hmac(streebog512)"));
break;
- case 150:
- ret = min(ret, tcrypt_test("ansi_cprng"));
- break;
-
case 151:
ret = min(ret, tcrypt_test("rfc4106(gcm(aes))"));
break;
@@ -2268,10 +2260,6 @@ static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb)
test_hash_speed("crc32c", sec, generic_hash_speed_template);
if (mode > 300 && mode < 400) break;
fallthrough;
- case 321:
- test_hash_speed("poly1305", sec, poly1305_speed_template);
- if (mode > 300 && mode < 400) break;
- fallthrough;
case 322:
test_hash_speed("sha3-224", sec, generic_hash_speed_template);
if (mode > 300 && mode < 400) break;
diff --git a/crypto/tcrypt.h b/crypto/tcrypt.h
index 7f938ac93e58..85c3f77bcfb4 100644
--- a/crypto/tcrypt.h
+++ b/crypto/tcrypt.h
@@ -96,22 +96,4 @@ static struct hash_speed generic_hash_speed_template[] = {
{ .blen = 0, .plen = 0, }
};
-static struct hash_speed poly1305_speed_template[] = {
- { .blen = 96, .plen = 16, },
- { .blen = 96, .plen = 32, },
- { .blen = 96, .plen = 96, },
- { .blen = 288, .plen = 16, },
- { .blen = 288, .plen = 32, },
- { .blen = 288, .plen = 288, },
- { .blen = 1056, .plen = 32, },
- { .blen = 1056, .plen = 1056, },
- { .blen = 2080, .plen = 32, },
- { .blen = 2080, .plen = 2080, },
- { .blen = 4128, .plen = 4128, },
- { .blen = 8224, .plen = 8224, },
-
- /* End marker */
- { .blen = 0, .plen = 0, }
-};
-
#endif /* _CRYPTO_TCRYPT_H */
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
index 6a490aaa71b9..a302be53896d 100644
--- a/crypto/testmgr.c
+++ b/crypto/testmgr.c
@@ -117,11 +117,6 @@ struct hash_test_suite {
unsigned int count;
};
-struct cprng_test_suite {
- const struct cprng_testvec *vecs;
- unsigned int count;
-};
-
struct drbg_test_suite {
const struct drbg_testvec *vecs;
unsigned int count;
@@ -154,7 +149,6 @@ struct alg_test_desc {
struct cipher_test_suite cipher;
struct comp_test_suite comp;
struct hash_test_suite hash;
- struct cprng_test_suite cprng;
struct drbg_test_suite drbg;
struct akcipher_test_suite akcipher;
struct sig_test_suite sig;
@@ -3442,68 +3436,6 @@ out:
return ret;
}
-static int test_cprng(struct crypto_rng *tfm,
- const struct cprng_testvec *template,
- unsigned int tcount)
-{
- const char *algo = crypto_tfm_alg_driver_name(crypto_rng_tfm(tfm));
- int err = 0, i, j, seedsize;
- u8 *seed;
- char result[32];
-
- seedsize = crypto_rng_seedsize(tfm);
-
- seed = kmalloc(seedsize, GFP_KERNEL);
- if (!seed) {
- printk(KERN_ERR "alg: cprng: Failed to allocate seed space "
- "for %s\n", algo);
- return -ENOMEM;
- }
-
- for (i = 0; i < tcount; i++) {
- memset(result, 0, 32);
-
- memcpy(seed, template[i].v, template[i].vlen);
- memcpy(seed + template[i].vlen, template[i].key,
- template[i].klen);
- memcpy(seed + template[i].vlen + template[i].klen,
- template[i].dt, template[i].dtlen);
-
- err = crypto_rng_reset(tfm, seed, seedsize);
- if (err) {
- printk(KERN_ERR "alg: cprng: Failed to reset rng "
- "for %s\n", algo);
- goto out;
- }
-
- for (j = 0; j < template[i].loops; j++) {
- err = crypto_rng_get_bytes(tfm, result,
- template[i].rlen);
- if (err < 0) {
- printk(KERN_ERR "alg: cprng: Failed to obtain "
- "the correct amount of random data for "
- "%s (requested %d)\n", algo,
- template[i].rlen);
- goto out;
- }
- }
-
- err = memcmp(result, template[i].result,
- template[i].rlen);
- if (err) {
- printk(KERN_ERR "alg: cprng: Test %d failed for %s\n",
- i, algo);
- hexdump(result, template[i].rlen);
- err = -EINVAL;
- goto out;
- }
- }
-
-out:
- kfree(seed);
- return err;
-}
-
static int alg_test_cipher(const struct alg_test_desc *desc,
const char *driver, u32 type, u32 mask)
{
@@ -3550,29 +3482,6 @@ static int alg_test_comp(const struct alg_test_desc *desc, const char *driver,
return err;
}
-static int alg_test_cprng(const struct alg_test_desc *desc, const char *driver,
- u32 type, u32 mask)
-{
- struct crypto_rng *rng;
- int err;
-
- rng = crypto_alloc_rng(driver, type, mask);
- if (IS_ERR(rng)) {
- if (PTR_ERR(rng) == -ENOENT)
- return 0;
- printk(KERN_ERR "alg: cprng: Failed to load transform for %s: "
- "%ld\n", driver, PTR_ERR(rng));
- return PTR_ERR(rng);
- }
-
- err = test_cprng(rng, desc->suite.cprng.vecs, desc->suite.cprng.count);
-
- crypto_free_rng(rng);
-
- return err;
-}
-
-
static int drbg_cavs_test(const struct drbg_testvec *test, int pr,
const char *driver, u32 type, u32 mask)
{
@@ -4171,12 +4080,6 @@ static const struct alg_test_desc alg_test_descs[] = {
.aead = __VECS(aegis128_tv_template)
}
}, {
- .alg = "ansi_cprng",
- .test = alg_test_cprng,
- .suite = {
- .cprng = __VECS(ansi_cprng_aes_tv_template)
- }
- }, {
.alg = "authenc(hmac(md5),ecb(cipher_null))",
.generic_driver = "authenc(hmac-md5-lib,ecb-cipher_null)",
.test = alg_test_aead,
@@ -4332,6 +4235,7 @@ static const struct alg_test_desc alg_test_descs[] = {
.fips_allowed = 1,
}, {
.alg = "blake2b-160",
+ .generic_driver = "blake2b-160-lib",
.test = alg_test_hash,
.fips_allowed = 0,
.suite = {
@@ -4339,6 +4243,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "blake2b-256",
+ .generic_driver = "blake2b-256-lib",
.test = alg_test_hash,
.fips_allowed = 0,
.suite = {
@@ -4346,6 +4251,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "blake2b-384",
+ .generic_driver = "blake2b-384-lib",
.test = alg_test_hash,
.fips_allowed = 0,
.suite = {
@@ -4353,6 +4259,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "blake2b-512",
+ .generic_driver = "blake2b-512-lib",
.test = alg_test_hash,
.fips_allowed = 0,
.suite = {
@@ -5055,8 +4962,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "hctr2(aes)",
- .generic_driver =
- "hctr2_base(xctr(aes-generic),polyval-generic)",
+ .generic_driver = "hctr2_base(xctr(aes-generic),polyval-lib)",
.test = alg_test_skcipher,
.suite = {
.cipher = __VECS(aes_hctr2_tv_template)
@@ -5100,6 +5006,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "hmac(sha3-224)",
+ .generic_driver = "hmac(sha3-224-lib)",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
@@ -5107,6 +5014,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "hmac(sha3-256)",
+ .generic_driver = "hmac(sha3-256-lib)",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
@@ -5114,6 +5022,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "hmac(sha3-384)",
+ .generic_driver = "hmac(sha3-384-lib)",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
@@ -5121,6 +5030,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "hmac(sha3-512)",
+ .generic_driver = "hmac(sha3-512-lib)",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
@@ -5364,12 +5274,6 @@ static const struct alg_test_desc alg_test_descs[] = {
.test = alg_test_null,
.fips_allowed = 1,
}, {
- .alg = "polyval",
- .test = alg_test_hash,
- .suite = {
- .hash = __VECS(polyval_tv_template)
- }
- }, {
.alg = "rfc3686(ctr(aes))",
.test = alg_test_skcipher,
.fips_allowed = 1,
@@ -5474,6 +5378,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "sha3-224",
+ .generic_driver = "sha3-224-lib",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
@@ -5481,6 +5386,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "sha3-256",
+ .generic_driver = "sha3-256-lib",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
@@ -5488,6 +5394,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "sha3-384",
+ .generic_driver = "sha3-384-lib",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
@@ -5495,6 +5402,7 @@ static const struct alg_test_desc alg_test_descs[] = {
}
}, {
.alg = "sha3-512",
+ .generic_driver = "sha3-512-lib",
.test = alg_test_hash,
.fips_allowed = 1,
.suite = {
diff --git a/crypto/testmgr.h b/crypto/testmgr.h
index 268231227282..80bf5f1b67a6 100644
--- a/crypto/testmgr.h
+++ b/crypto/testmgr.h
@@ -119,18 +119,6 @@ struct aead_testvec {
int crypt_error;
};
-struct cprng_testvec {
- const char *key;
- const char *dt;
- const char *v;
- const char *result;
- unsigned char klen;
- unsigned short dtlen;
- unsigned short vlen;
- unsigned short rlen;
- unsigned short loops;
-};
-
struct drbg_testvec {
const unsigned char *entropy;
size_t entropylen;
@@ -9023,6 +9011,126 @@ static const struct cipher_testvec des_tv_template[] = {
.ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
.ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
.len = 8,
+ }, { /* Weak key */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xe0\xe0\xe0\xe0\xf1\xf1\xf1\xf1",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Weak key */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\x1f\x1f\x1f\x1f\x0e\x0e\x0e\x0e",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Weak key */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xfe\xfe\xfe\xfe\xfe\xfe\xfe\xfe",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 1a */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\x01\xfe\x01\xfe\x01\xfe\x01\xfe",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 1b */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xfe\x01\xfe\x01\xfe\x01\xfe\x01",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 2a */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\x1f\xe0\x1f\xe0\x0e\xf1\x0e\xf1",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 2b */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xe0\x1f\xe0\x1f\xf1\x0e\xf1\x0e",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 3a */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\x01\xe0\x01\xe0\x01\xf1\x01\xf1",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 3b */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xe0\x01\xe0\x01\xf1\x01\xf1\x01",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 4a */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\x1f\xfe\x1f\xfe\x0e\xfe\x0e\xfe",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 4b */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xfe\x1f\xfe\x1f\xfe\x0e\xfe\x0e",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 5a */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\x01\x1f\x01\x1f\x01\x0e\x01\x0e",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 5b */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\x1f\x01\x1f\x01\x0e\x01\x0e\x01",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 6a */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xe0\xfe\xe0\xfe\xf1\xfe\xf1\xfe",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
+ }, { /* Semi-weak key pair 6b */
+ .setkey_error = -EINVAL,
+ .wk = 1,
+ .key = "\xfe\xe0\xfe\xe0\xfe\xf1\xfe\xf1",
+ .klen = 8,
+ .ptext = "\x01\x23\x45\x67\x89\xab\xcd\xe7",
+ .ctext = "\xc9\x57\x44\x25\x6a\x5e\xd3\x1d",
+ .len = 8,
}, { /* Two blocks -- for testing encryption across pages */
.key = "\x01\x23\x45\x67\x89\xab\xcd\xef",
.klen = 8,
@@ -22377,100 +22485,6 @@ static const struct aead_testvec aegis128_tv_template[] = {
};
/*
- * ANSI X9.31 Continuous Pseudo-Random Number Generator (AES mode)
- * test vectors, taken from Appendix B.2.9 and B.2.10:
- * http://csrc.nist.gov/groups/STM/cavp/documents/rng/RNGVS.pdf
- * Only AES-128 is supported at this time.
- */
-static const struct cprng_testvec ansi_cprng_aes_tv_template[] = {
- {
- .key = "\xf3\xb1\x66\x6d\x13\x60\x72\x42"
- "\xed\x06\x1c\xab\xb8\xd4\x62\x02",
- .klen = 16,
- .dt = "\xe6\xb3\xbe\x78\x2a\x23\xfa\x62"
- "\xd7\x1d\x4a\xfb\xb0\xe9\x22\xf9",
- .dtlen = 16,
- .v = "\x80\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .vlen = 16,
- .result = "\x59\x53\x1e\xd1\x3b\xb0\xc0\x55"
- "\x84\x79\x66\x85\xc1\x2f\x76\x41",
- .rlen = 16,
- .loops = 1,
- }, {
- .key = "\xf3\xb1\x66\x6d\x13\x60\x72\x42"
- "\xed\x06\x1c\xab\xb8\xd4\x62\x02",
- .klen = 16,
- .dt = "\xe6\xb3\xbe\x78\x2a\x23\xfa\x62"
- "\xd7\x1d\x4a\xfb\xb0\xe9\x22\xfa",
- .dtlen = 16,
- .v = "\xc0\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .vlen = 16,
- .result = "\x7c\x22\x2c\xf4\xca\x8f\xa2\x4c"
- "\x1c\x9c\xb6\x41\xa9\xf3\x22\x0d",
- .rlen = 16,
- .loops = 1,
- }, {
- .key = "\xf3\xb1\x66\x6d\x13\x60\x72\x42"
- "\xed\x06\x1c\xab\xb8\xd4\x62\x02",
- .klen = 16,
- .dt = "\xe6\xb3\xbe\x78\x2a\x23\xfa\x62"
- "\xd7\x1d\x4a\xfb\xb0\xe9\x22\xfb",
- .dtlen = 16,
- .v = "\xe0\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .vlen = 16,
- .result = "\x8a\xaa\x00\x39\x66\x67\x5b\xe5"
- "\x29\x14\x28\x81\xa9\x4d\x4e\xc7",
- .rlen = 16,
- .loops = 1,
- }, {
- .key = "\xf3\xb1\x66\x6d\x13\x60\x72\x42"
- "\xed\x06\x1c\xab\xb8\xd4\x62\x02",
- .klen = 16,
- .dt = "\xe6\xb3\xbe\x78\x2a\x23\xfa\x62"
- "\xd7\x1d\x4a\xfb\xb0\xe9\x22\xfc",
- .dtlen = 16,
- .v = "\xf0\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .vlen = 16,
- .result = "\x88\xdd\xa4\x56\x30\x24\x23\xe5"
- "\xf6\x9d\xa5\x7e\x7b\x95\xc7\x3a",
- .rlen = 16,
- .loops = 1,
- }, {
- .key = "\xf3\xb1\x66\x6d\x13\x60\x72\x42"
- "\xed\x06\x1c\xab\xb8\xd4\x62\x02",
- .klen = 16,
- .dt = "\xe6\xb3\xbe\x78\x2a\x23\xfa\x62"
- "\xd7\x1d\x4a\xfb\xb0\xe9\x22\xfd",
- .dtlen = 16,
- .v = "\xf8\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .vlen = 16,
- .result = "\x05\x25\x92\x46\x61\x79\xd2\xcb"
- "\x78\xc4\x0b\x14\x0a\x5a\x9a\xc8",
- .rlen = 16,
- .loops = 1,
- }, { /* Monte Carlo Test */
- .key = "\x9f\x5b\x51\x20\x0b\xf3\x34\xb5"
- "\xd8\x2b\xe8\xc3\x72\x55\xc8\x48",
- .klen = 16,
- .dt = "\x63\x76\xbb\xe5\x29\x02\xba\x3b"
- "\x67\xc9\x25\xfa\x70\x1f\x11\xac",
- .dtlen = 16,
- .v = "\x57\x2c\x8e\x76\x87\x26\x47\x97"
- "\x7e\x74\xfb\xdd\xc4\x95\x01\xd1",
- .vlen = 16,
- .result = "\x48\xe9\xbd\x0d\x06\xee\x18\xfb"
- "\xe4\x57\x90\xd5\xc3\xfc\x9b\x73",
- .rlen = 16,
- .loops = 10000,
- },
-};
-
-/*
* SP800-90A DRBG Test vectors from
* http://csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors.zip
*
@@ -36237,177 +36251,6 @@ static const struct cipher_testvec aes_xctr_tv_template[] = {
/*
* Test vectors generated using https://github.com/google/hctr2
- *
- * To ensure compatibility with RFC 8452, some tests were sourced from
- * https://datatracker.ietf.org/doc/html/rfc8452
- */
-static const struct hash_testvec polyval_tv_template[] = {
- { // From RFC 8452
- .key = "\x31\x07\x28\xd9\x91\x1f\x1f\x38"
- "\x37\xb2\x43\x16\xc3\xfa\xb9\xa0",
- .plaintext = "\x65\x78\x61\x6d\x70\x6c\x65\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x48\x65\x6c\x6c\x6f\x20\x77\x6f"
- "\x72\x6c\x64\x00\x00\x00\x00\x00"
- "\x38\x00\x00\x00\x00\x00\x00\x00"
- "\x58\x00\x00\x00\x00\x00\x00\x00",
- .digest = "\xad\x7f\xcf\x0b\x51\x69\x85\x16"
- "\x62\x67\x2f\x3c\x5f\x95\x13\x8f",
- .psize = 48,
- .ksize = 16,
- },
- { // From RFC 8452
- .key = "\xd9\xb3\x60\x27\x96\x94\x94\x1a"
- "\xc5\xdb\xc6\x98\x7a\xda\x73\x77",
- .plaintext = "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .digest = "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .psize = 16,
- .ksize = 16,
- },
- { // From RFC 8452
- .key = "\xd9\xb3\x60\x27\x96\x94\x94\x1a"
- "\xc5\xdb\xc6\x98\x7a\xda\x73\x77",
- .plaintext = "\x01\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x40\x00\x00\x00\x00\x00\x00\x00",
- .digest = "\xeb\x93\xb7\x74\x09\x62\xc5\xe4"
- "\x9d\x2a\x90\xa7\xdc\x5c\xec\x74",
- .psize = 32,
- .ksize = 16,
- },
- { // From RFC 8452
- .key = "\xd9\xb3\x60\x27\x96\x94\x94\x1a"
- "\xc5\xdb\xc6\x98\x7a\xda\x73\x77",
- .plaintext = "\x01\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x02\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x03\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x80\x01\x00\x00\x00\x00\x00\x00",
- .digest = "\x81\x38\x87\x46\xbc\x22\xd2\x6b"
- "\x2a\xbc\x3d\xcb\x15\x75\x42\x22",
- .psize = 64,
- .ksize = 16,
- },
- { // From RFC 8452
- .key = "\xd9\xb3\x60\x27\x96\x94\x94\x1a"
- "\xc5\xdb\xc6\x98\x7a\xda\x73\x77",
- .plaintext = "\x01\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x02\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x03\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x04\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x02\x00\x00\x00\x00\x00\x00",
- .digest = "\x1e\x39\xb6\xd3\x34\x4d\x34\x8f"
- "\x60\x44\xf8\x99\x35\xd1\xcf\x78",
- .psize = 80,
- .ksize = 16,
- },
- { // From RFC 8452
- .key = "\xd9\xb3\x60\x27\x96\x94\x94\x1a"
- "\xc5\xdb\xc6\x98\x7a\xda\x73\x77",
- .plaintext = "\x01\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x02\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x03\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x04\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x05\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x08\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x02\x00\x00\x00\x00\x00\x00",
- .digest = "\xff\xcd\x05\xd5\x77\x0f\x34\xad"
- "\x92\x67\xf0\xa5\x99\x94\xb1\x5a",
- .psize = 96,
- .ksize = 16,
- },
- { // Random ( 1)
- .key = "\x90\xcc\xac\xee\xba\xd7\xd4\x68"
- "\x98\xa6\x79\x70\xdf\x66\x15\x6c",
- .plaintext = "",
- .digest = "\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00",
- .psize = 0,
- .ksize = 16,
- },
- { // Random ( 1)
- .key = "\xc1\x45\x71\xf0\x30\x07\x94\xe7"
- "\x3a\xdd\xe4\xc6\x19\x2d\x02\xa2",
- .plaintext = "\xc1\x5d\x47\xc7\x4c\x7c\x5e\x07"
- "\x85\x14\x8f\x79\xcc\x73\x83\xf7"
- "\x35\xb8\xcb\x73\x61\xf0\x53\x31"
- "\xbf\x84\xde\xb6\xde\xaf\xb0\xb8"
- "\xb7\xd9\x11\x91\x89\xfd\x1e\x4c"
- "\x84\x4a\x1f\x2a\x87\xa4\xaf\x62"
- "\x8d\x7d\x58\xf6\x43\x35\xfc\x53"
- "\x8f\x1a\xf6\x12\xe1\x13\x3f\x66"
- "\x91\x4b\x13\xd6\x45\xfb\xb0\x7a"
- "\xe0\x8b\x8e\x99\xf7\x86\x46\x37"
- "\xd1\x22\x9e\x52\xf3\x3f\xd9\x75"
- "\x2c\x2c\xc6\xbb\x0e\x08\x14\x29"
- "\xe8\x50\x2f\xd8\xbe\xf4\xe9\x69"
- "\x4a\xee\xf7\xae\x15\x65\x35\x1e",
- .digest = "\x00\x4f\x5d\xe9\x3b\xc0\xd6\x50"
- "\x3e\x38\x73\x86\xc6\xda\xca\x7f",
- .psize = 112,
- .ksize = 16,
- },
- { // Random ( 1)
- .key = "\x37\xbe\x68\x16\x50\xb9\x4e\xb0"
- "\x47\xde\xe2\xbd\xde\xe4\x48\x09",
- .plaintext = "\x87\xfc\x68\x9f\xff\xf2\x4a\x1e"
- "\x82\x3b\x73\x8f\xc1\xb2\x1b\x7a"
- "\x6c\x4f\x81\xbc\x88\x9b\x6c\xa3"
- "\x9c\xc2\xa5\xbc\x14\x70\x4c\x9b"
- "\x0c\x9f\x59\x92\x16\x4b\x91\x3d"
- "\x18\x55\x22\x68\x12\x8c\x63\xb2"
- "\x51\xcb\x85\x4b\xd2\xae\x0b\x1c"
- "\x5d\x28\x9d\x1d\xb1\xc8\xf0\x77"
- "\xe9\xb5\x07\x4e\x06\xc8\xee\xf8"
- "\x1b\xed\x72\x2a\x55\x7d\x16\xc9"
- "\xf2\x54\xe7\xe9\xe0\x44\x5b\x33"
- "\xb1\x49\xee\xff\x43\xfb\x82\xcd"
- "\x4a\x70\x78\x81\xa4\x34\x36\xe8"
- "\x4c\x28\x54\xa6\x6c\xc3\x6b\x78"
- "\xe7\xc0\x5d\xc6\x5d\x81\xab\x70"
- "\x08\x86\xa1\xfd\xf4\x77\x55\xfd"
- "\xa3\xe9\xe2\x1b\xdf\x99\xb7\x80"
- "\xf9\x0a\x4f\x72\x4a\xd3\xaf\xbb"
- "\xb3\x3b\xeb\x08\x58\x0f\x79\xce"
- "\xa5\x99\x05\x12\x34\xd4\xf4\x86"
- "\x37\x23\x1d\xc8\x49\xc0\x92\xae"
- "\xa6\xac\x9b\x31\x55\xed\x15\xc6"
- "\x05\x17\x37\x8d\x90\x42\xe4\x87"
- "\x89\x62\x88\x69\x1c\x6a\xfd\xe3"
- "\x00\x2b\x47\x1a\x73\xc1\x51\xc2"
- "\xc0\x62\x74\x6a\x9e\xb2\xe5\x21"
- "\xbe\x90\xb5\xb0\x50\xca\x88\x68"
- "\xe1\x9d\x7a\xdf\x6c\xb7\xb9\x98"
- "\xee\x28\x62\x61\x8b\xd1\x47\xf9"
- "\x04\x7a\x0b\x5d\xcd\x2b\x65\xf5"
- "\x12\xa3\xfe\x1a\xaa\x2c\x78\x42"
- "\xb8\xbe\x7d\x74\xeb\x59\xba\xba",
- .digest = "\xae\x11\xd4\x60\x2a\x5f\x9e\x42"
- "\x89\x04\xc2\x34\x8d\x55\x94\x0a",
- .psize = 256,
- .ksize = 16,
- },
-
-};
-
-/*
- * Test vectors generated using https://github.com/google/hctr2
*/
static const struct cipher_testvec aes_hctr2_tv_template[] = {
{
diff --git a/crypto/zstd.c b/crypto/zstd.c
index ac318d333b68..cbbd0413751a 100644
--- a/crypto/zstd.c
+++ b/crypto/zstd.c
@@ -10,6 +10,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/net.h>
+#include <linux/overflow.h>
#include <linux/vmalloc.h>
#include <linux/zstd.h>
#include <crypto/internal/acompress.h>
@@ -25,7 +26,7 @@ struct zstd_ctx {
zstd_dctx *dctx;
size_t wksp_size;
zstd_parameters params;
- u8 wksp[] __aligned(8);
+ u8 wksp[] __aligned(8) __counted_by(wksp_size);
};
static DEFINE_MUTEX(zstd_stream_lock);
@@ -38,13 +39,12 @@ static void *zstd_alloc_stream(void)
params = zstd_get_params(ZSTD_DEF_LEVEL, ZSTD_MAX_SIZE);
- wksp_size = max_t(size_t,
- zstd_cstream_workspace_bound(&params.cParams),
- zstd_dstream_workspace_bound(ZSTD_MAX_SIZE));
+ wksp_size = max(zstd_cstream_workspace_bound(&params.cParams),
+ zstd_dstream_workspace_bound(ZSTD_MAX_SIZE));
if (!wksp_size)
return ERR_PTR(-EINVAL);
- ctx = kvmalloc(sizeof(*ctx) + wksp_size, GFP_KERNEL);
+ ctx = kvmalloc(struct_size(ctx, wksp, wksp_size), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
@@ -75,11 +75,6 @@ static int zstd_init(struct crypto_acomp *acomp_tfm)
return ret;
}
-static void zstd_exit(struct crypto_acomp *acomp_tfm)
-{
- crypto_acomp_free_streams(&zstd_streams);
-}
-
static int zstd_compress_one(struct acomp_req *req, struct zstd_ctx *ctx,
const void *src, void *dst, unsigned int *dlen)
{
@@ -297,7 +292,6 @@ static struct acomp_alg zstd_acomp = {
.cra_module = THIS_MODULE,
},
.init = zstd_init,
- .exit = zstd_exit,
.compress = zstd_compress,
.decompress = zstd_decompress,
};
@@ -310,6 +304,7 @@ static int __init zstd_mod_init(void)
static void __exit zstd_mod_fini(void)
{
crypto_unregister_acomp(&zstd_acomp);
+ crypto_acomp_free_streams(&zstd_streams);
}
module_init(zstd_mod_init);
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 4915a63866b0..3054b50a2f4c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -251,4 +251,6 @@ source "drivers/hte/Kconfig"
source "drivers/cdx/Kconfig"
+source "drivers/resctrl/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 8e1ffa4358d5..20eb17596b89 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -194,6 +194,7 @@ obj-$(CONFIG_HTE) += hte/
obj-$(CONFIG_DRM_ACCEL) += accel/
obj-$(CONFIG_CDX_BUS) += cdx/
obj-$(CONFIG_DPLL) += dpll/
+obj-y += resctrl/
obj-$(CONFIG_DIBS) += dibs/
obj-$(CONFIG_S390) += s390/
diff --git a/drivers/accel/Kconfig b/drivers/accel/Kconfig
index bb01cebc42bf..bdf48ccafcf2 100644
--- a/drivers/accel/Kconfig
+++ b/drivers/accel/Kconfig
@@ -25,6 +25,7 @@ menuconfig DRM_ACCEL
and debugfs).
source "drivers/accel/amdxdna/Kconfig"
+source "drivers/accel/ethosu/Kconfig"
source "drivers/accel/habanalabs/Kconfig"
source "drivers/accel/ivpu/Kconfig"
source "drivers/accel/qaic/Kconfig"
diff --git a/drivers/accel/Makefile b/drivers/accel/Makefile
index ffc3fa588666..1d3a7251b950 100644
--- a/drivers/accel/Makefile
+++ b/drivers/accel/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DRM_ACCEL_AMDXDNA) += amdxdna/
+obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) += ethosu/
obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/
obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/
obj-$(CONFIG_DRM_ACCEL_QAIC) += qaic/
diff --git a/drivers/accel/amdxdna/Makefile b/drivers/accel/amdxdna/Makefile
index 6797dac65efa..6344aaf523fa 100644
--- a/drivers/accel/amdxdna/Makefile
+++ b/drivers/accel/amdxdna/Makefile
@@ -14,6 +14,7 @@ amdxdna-y := \
amdxdna_mailbox.o \
amdxdna_mailbox_helper.o \
amdxdna_pci_drv.o \
+ amdxdna_pm.o \
amdxdna_sysfs.o \
amdxdna_ubuf.o \
npu1_regs.o \
diff --git a/drivers/accel/amdxdna/TODO b/drivers/accel/amdxdna/TODO
index ad8ac6e315b6..0e4bbebeaedf 100644
--- a/drivers/accel/amdxdna/TODO
+++ b/drivers/accel/amdxdna/TODO
@@ -1,2 +1 @@
- Add debugfs support
-- Add debug BO support
diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c
index e9f9b1fa5dc1..42d876a427c5 100644
--- a/drivers/accel/amdxdna/aie2_ctx.c
+++ b/drivers/accel/amdxdna/aie2_ctx.c
@@ -21,6 +21,7 @@
#include "amdxdna_gem.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_pci_drv.h"
+#include "amdxdna_pm.h"
static bool force_cmdlist;
module_param(force_cmdlist, bool, 0600);
@@ -88,7 +89,7 @@ static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hw
goto out;
}
- ret = aie2_config_cu(hwctx);
+ ret = aie2_config_cu(hwctx, NULL);
if (ret) {
XDNA_ERR(xdna, "Config cu failed, ret %d", ret);
goto out;
@@ -167,14 +168,11 @@ static int aie2_hwctx_resume_cb(struct amdxdna_hwctx *hwctx, void *arg)
int aie2_hwctx_resume(struct amdxdna_client *client)
{
- struct amdxdna_dev *xdna = client->xdna;
-
/*
* The resume path cannot guarantee that mailbox channel can be
* regenerated. If this happen, when submit message to this
* mailbox channel, error will return.
*/
- drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
return amdxdna_hwctx_walk(client, NULL, aie2_hwctx_resume_cb);
}
@@ -184,12 +182,13 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
struct dma_fence *fence = job->fence;
trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq);
+
+ amdxdna_pm_suspend_put(job->hwctx->client->xdna);
job->hwctx->priv->completed++;
dma_fence_signal(fence);
up(&job->hwctx->priv->job_sem);
job->job_done = true;
- dma_fence_put(fence);
mmput_async(job->mm);
aie2_job_put(job);
}
@@ -204,10 +203,13 @@ aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size)
cmd_abo = job->cmd_bo;
- if (unlikely(!data))
+ if (unlikely(job->job_timeout)) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_TIMEOUT);
+ ret = -EINVAL;
goto out;
+ }
- if (unlikely(size != sizeof(u32))) {
+ if (unlikely(!data) || unlikely(size != sizeof(u32))) {
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ABORT);
ret = -EINVAL;
goto out;
@@ -226,11 +228,10 @@ out:
}
static int
-aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
+aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size)
{
struct amdxdna_sched_job *job = handle;
int ret = 0;
- u32 status;
if (unlikely(!data))
goto out;
@@ -240,8 +241,7 @@ aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
goto out;
}
- status = readl(data);
- XDNA_DBG(job->hwctx->client->xdna, "Resp status 0x%x", status);
+ job->drv_cmd->result = readl(data);
out:
aie2_sched_notify(job);
@@ -260,6 +260,13 @@ aie2_sched_cmdlist_resp_handler(void *handle, void __iomem *data, size_t size)
int ret = 0;
cmd_abo = job->cmd_bo;
+
+ if (unlikely(job->job_timeout)) {
+ amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_TIMEOUT);
+ ret = -EINVAL;
+ goto out;
+ }
+
if (unlikely(!data) || unlikely(size != sizeof(u32) * 3)) {
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ABORT);
ret = -EINVAL;
@@ -314,8 +321,18 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
kref_get(&job->refcnt);
fence = dma_fence_get(job->fence);
- if (unlikely(!cmd_abo)) {
- ret = aie2_sync_bo(hwctx, job, aie2_sched_nocmd_resp_handler);
+ if (job->drv_cmd) {
+ switch (job->drv_cmd->opcode) {
+ case SYNC_DEBUG_BO:
+ ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
+ break;
+ case ATTACH_DEBUG_BO:
+ ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
goto out;
}
@@ -362,6 +379,7 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
xdna = hwctx->client->xdna;
trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq);
+ job->job_timeout = true;
mutex_lock(&xdna->dev_lock);
aie2_hwctx_stop(xdna, hwctx, sched_job);
@@ -531,13 +549,12 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = HWCTX_MAX_CMDS,
.timeout = msecs_to_jiffies(HWCTX_MAX_TIMEOUT),
- .name = hwctx->name,
+ .name = "amdxdna_js",
.dev = xdna->ddev.dev,
};
struct drm_gpu_scheduler *sched;
struct amdxdna_hwctx_priv *priv;
struct amdxdna_gem_obj *heap;
- struct amdxdna_dev_hdl *ndev;
int i, ret;
priv = kzalloc(sizeof(*hwctx->priv), GFP_KERNEL);
@@ -610,10 +627,14 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
goto free_entity;
}
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto free_col_list;
+
ret = aie2_alloc_resource(hwctx);
if (ret) {
XDNA_ERR(xdna, "Alloc hw resource failed, ret %d", ret);
- goto free_col_list;
+ goto suspend_put;
}
ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
@@ -628,10 +649,9 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
XDNA_ERR(xdna, "Create syncobj failed, ret %d", ret);
goto release_resource;
}
+ amdxdna_pm_suspend_put(xdna);
hwctx->status = HWCTX_STAT_INIT;
- ndev = xdna->dev_handle;
- ndev->hwctx_num++;
init_waitqueue_head(&priv->job_free_wq);
XDNA_DBG(xdna, "hwctx %s init completed", hwctx->name);
@@ -640,6 +660,8 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
release_resource:
aie2_release_resource(hwctx);
+suspend_put:
+ amdxdna_pm_suspend_put(xdna);
free_col_list:
kfree(hwctx->col_list);
free_entity:
@@ -662,26 +684,25 @@ free_priv:
void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
{
- struct amdxdna_dev_hdl *ndev;
struct amdxdna_dev *xdna;
int idx;
xdna = hwctx->client->xdna;
- ndev = xdna->dev_handle;
- ndev->hwctx_num--;
XDNA_DBG(xdna, "%s sequence number %lld", hwctx->name, hwctx->priv->seq);
- drm_sched_entity_destroy(&hwctx->priv->entity);
-
aie2_hwctx_wait_for_idle(hwctx);
/* Request fw to destroy hwctx and cancel the rest pending requests */
aie2_release_resource(hwctx);
+ mutex_unlock(&xdna->dev_lock);
+ drm_sched_entity_destroy(&hwctx->priv->entity);
+
/* Wait for all submitted jobs to be completed or canceled */
wait_event(hwctx->priv->job_free_wq,
atomic64_read(&hwctx->job_submit_cnt) ==
atomic64_read(&hwctx->job_free_cnt));
+ mutex_lock(&xdna->dev_lock);
drm_sched_fini(&hwctx->priv->sched);
aie2_ctx_syncobj_destroy(hwctx);
@@ -697,6 +718,14 @@ void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
kfree(hwctx->cus);
}
+static int aie2_config_cu_resp_handler(void *handle, void __iomem *data, size_t size)
+{
+ struct amdxdna_hwctx *hwctx = handle;
+
+ amdxdna_pm_suspend_put(hwctx->client->xdna);
+ return 0;
+}
+
static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size)
{
struct amdxdna_hwctx_param_config_cu *config = buf;
@@ -728,10 +757,14 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size
if (!hwctx->cus)
return -ENOMEM;
- ret = aie2_config_cu(hwctx);
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto free_cus;
+
+ ret = aie2_config_cu(hwctx, aie2_config_cu_resp_handler);
if (ret) {
XDNA_ERR(xdna, "Config CU to firmware failed, ret %d", ret);
- goto free_cus;
+ goto pm_suspend_put;
}
wmb(); /* To avoid locking in command submit when check status */
@@ -739,12 +772,82 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size
return 0;
+pm_suspend_put:
+ amdxdna_pm_suspend_put(xdna);
free_cus:
kfree(hwctx->cus);
hwctx->cus = NULL;
return ret;
}
+static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq)
+{
+ struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq);
+
+ if (!out_fence) {
+ XDNA_ERR(hwctx->client->xdna, "Failed to get fence");
+ return;
+ }
+
+ dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT);
+ dma_fence_put(out_fence);
+}
+
+static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
+ bool attach)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_drv_cmd cmd = { 0 };
+ struct amdxdna_gem_obj *abo;
+ u64 seq;
+ int ret;
+
+ abo = amdxdna_gem_get_obj(client, bo_hdl, AMDXDNA_BO_DEV);
+ if (!abo) {
+ XDNA_ERR(xdna, "Get bo %d failed", bo_hdl);
+ return -EINVAL;
+ }
+
+ if (attach) {
+ if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE) {
+ ret = -EBUSY;
+ goto put_obj;
+ }
+ cmd.opcode = ATTACH_DEBUG_BO;
+ } else {
+ if (abo->assigned_hwctx != hwctx->id) {
+ ret = -EINVAL;
+ goto put_obj;
+ }
+ cmd.opcode = DETACH_DEBUG_BO;
+ }
+
+ ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
+ &bo_hdl, 1, hwctx->id, &seq);
+ if (ret) {
+ XDNA_ERR(xdna, "Submit command failed");
+ goto put_obj;
+ }
+
+ aie2_cmd_wait(hwctx, seq);
+ if (cmd.result) {
+ XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
+ goto put_obj;
+ }
+
+ if (attach)
+ abo->assigned_hwctx = hwctx->id;
+ else
+ abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE;
+
+ XDNA_DBG(xdna, "Config debug BO %d to %s", bo_hdl, hwctx->name);
+
+put_obj:
+ amdxdna_gem_put_obj(abo);
+ return ret;
+}
+
int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size)
{
struct amdxdna_dev *xdna = hwctx->client->xdna;
@@ -754,14 +857,40 @@ int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *bu
case DRM_AMDXDNA_HWCTX_CONFIG_CU:
return aie2_hwctx_cu_config(hwctx, buf, size);
case DRM_AMDXDNA_HWCTX_ASSIGN_DBG_BUF:
+ return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, true);
case DRM_AMDXDNA_HWCTX_REMOVE_DBG_BUF:
- return -EOPNOTSUPP;
+ return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, false);
default:
XDNA_DBG(xdna, "Not supported type %d", type);
return -EOPNOTSUPP;
}
}
+int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_drv_cmd cmd = { 0 };
+ u64 seq;
+ int ret;
+
+ cmd.opcode = SYNC_DEBUG_BO;
+ ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
+ &debug_bo_hdl, 1, hwctx->id, &seq);
+ if (ret) {
+ XDNA_ERR(xdna, "Submit command failed");
+ return ret;
+ }
+
+ aie2_cmd_wait(hwctx, seq);
+ if (cmd.result) {
+ XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int aie2_populate_range(struct amdxdna_gem_obj *abo)
{
struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
@@ -862,11 +991,15 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
goto free_chain;
}
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto cleanup_job;
+
retry:
ret = drm_gem_lock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
if (ret) {
XDNA_WARN(xdna, "Failed to lock BOs, ret %d", ret);
- goto cleanup_job;
+ goto suspend_put;
}
for (i = 0; i < job->bo_cnt; i++) {
@@ -874,7 +1007,7 @@ retry:
if (ret) {
XDNA_WARN(xdna, "Failed to reserve fences %d", ret);
drm_gem_unlock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
- goto cleanup_job;
+ goto suspend_put;
}
}
@@ -889,12 +1022,12 @@ retry:
msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
} else if (time_after(jiffies, timeout)) {
ret = -ETIME;
- goto cleanup_job;
+ goto suspend_put;
}
ret = aie2_populate_range(abo);
if (ret)
- goto cleanup_job;
+ goto suspend_put;
goto retry;
}
}
@@ -920,6 +1053,8 @@ retry:
return 0;
+suspend_put:
+ amdxdna_pm_suspend_put(xdna);
cleanup_job:
drm_sched_job_cleanup(&job->base);
free_chain:
diff --git a/drivers/accel/amdxdna/aie2_error.c b/drivers/accel/amdxdna/aie2_error.c
index 5ee905632a39..d452008ec4f4 100644
--- a/drivers/accel/amdxdna/aie2_error.c
+++ b/drivers/accel/amdxdna/aie2_error.c
@@ -13,6 +13,7 @@
#include "aie2_msg_priv.h"
#include "aie2_pci.h"
+#include "amdxdna_error.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_pci_drv.h"
@@ -46,6 +47,7 @@ enum aie_module_type {
AIE_MEM_MOD = 0,
AIE_CORE_MOD,
AIE_PL_MOD,
+ AIE_UNKNOWN_MOD,
};
enum aie_error_category {
@@ -143,6 +145,31 @@ static const struct aie_event_category aie_ml_shim_tile_event_cat[] = {
EVENT_CATEGORY(74U, AIE_ERROR_LOCK),
};
+static const enum amdxdna_error_num aie_cat_err_num_map[] = {
+ [AIE_ERROR_SATURATION] = AMDXDNA_ERROR_NUM_AIE_SATURATION,
+ [AIE_ERROR_FP] = AMDXDNA_ERROR_NUM_AIE_FP,
+ [AIE_ERROR_STREAM] = AMDXDNA_ERROR_NUM_AIE_STREAM,
+ [AIE_ERROR_ACCESS] = AMDXDNA_ERROR_NUM_AIE_ACCESS,
+ [AIE_ERROR_BUS] = AMDXDNA_ERROR_NUM_AIE_BUS,
+ [AIE_ERROR_INSTRUCTION] = AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
+ [AIE_ERROR_ECC] = AMDXDNA_ERROR_NUM_AIE_ECC,
+ [AIE_ERROR_LOCK] = AMDXDNA_ERROR_NUM_AIE_LOCK,
+ [AIE_ERROR_DMA] = AMDXDNA_ERROR_NUM_AIE_DMA,
+ [AIE_ERROR_MEM_PARITY] = AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
+ [AIE_ERROR_UNKNOWN] = AMDXDNA_ERROR_NUM_UNKNOWN,
+};
+
+static_assert(ARRAY_SIZE(aie_cat_err_num_map) == AIE_ERROR_UNKNOWN + 1);
+
+static const enum amdxdna_error_module aie_err_mod_map[] = {
+ [AIE_MEM_MOD] = AMDXDNA_ERROR_MODULE_AIE_MEMORY,
+ [AIE_CORE_MOD] = AMDXDNA_ERROR_MODULE_AIE_CORE,
+ [AIE_PL_MOD] = AMDXDNA_ERROR_MODULE_AIE_PL,
+ [AIE_UNKNOWN_MOD] = AMDXDNA_ERROR_MODULE_UNKNOWN,
+};
+
+static_assert(ARRAY_SIZE(aie_err_mod_map) == AIE_UNKNOWN_MOD + 1);
+
static enum aie_error_category
aie_get_error_category(u8 row, u8 event_id, enum aie_module_type mod_type)
{
@@ -176,12 +203,40 @@ aie_get_error_category(u8 row, u8 event_id, enum aie_module_type mod_type)
if (event_id != lut[i].event_id)
continue;
+ if (lut[i].category > AIE_ERROR_UNKNOWN)
+ return AIE_ERROR_UNKNOWN;
+
return lut[i].category;
}
return AIE_ERROR_UNKNOWN;
}
+static void aie2_update_last_async_error(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
+{
+ struct aie_error *errs = err_info;
+ enum amdxdna_error_module err_mod;
+ enum aie_error_category aie_err;
+ enum amdxdna_error_num err_num;
+ struct aie_error *last_err;
+
+ last_err = &errs[num_err - 1];
+ if (last_err->mod_type >= AIE_UNKNOWN_MOD) {
+ err_num = aie_cat_err_num_map[AIE_ERROR_UNKNOWN];
+ err_mod = aie_err_mod_map[AIE_UNKNOWN_MOD];
+ } else {
+ aie_err = aie_get_error_category(last_err->row,
+ last_err->event_id,
+ last_err->mod_type);
+ err_num = aie_cat_err_num_map[aie_err];
+ err_mod = aie_err_mod_map[last_err->mod_type];
+ }
+
+ ndev->last_async_err.err_code = AMDXDNA_ERROR_ENCODE(err_num, err_mod);
+ ndev->last_async_err.ts_us = ktime_to_us(ktime_get_real());
+ ndev->last_async_err.ex_err_code = AMDXDNA_EXTRA_ERR_ENCODE(last_err->row, last_err->col);
+}
+
static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
{
struct aie_error *errs = err_info;
@@ -264,29 +319,14 @@ static void aie2_error_worker(struct work_struct *err_work)
}
mutex_lock(&xdna->dev_lock);
+ aie2_update_last_async_error(e->ndev, info->payload, info->err_cnt);
+
/* Re-sent this event to firmware */
if (aie2_error_event_send(e))
XDNA_WARN(xdna, "Unable to register async event");
mutex_unlock(&xdna->dev_lock);
}
-int aie2_error_async_events_send(struct amdxdna_dev_hdl *ndev)
-{
- struct amdxdna_dev *xdna = ndev->xdna;
- struct async_event *e;
- int i, ret;
-
- drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
- for (i = 0; i < ndev->async_events->event_cnt; i++) {
- e = &ndev->async_events->event[i];
- ret = aie2_error_event_send(e);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
{
struct amdxdna_dev *xdna = ndev->xdna;
@@ -341,6 +381,10 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
e->size = ASYNC_BUF_SIZE;
e->resp.status = MAX_AIE2_STATUS_CODE;
INIT_WORK(&e->work, aie2_error_worker);
+
+ ret = aie2_error_event_send(e);
+ if (ret)
+ goto free_wq;
}
ndev->async_events = events;
@@ -349,6 +393,8 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
events->event_cnt, events->size);
return 0;
+free_wq:
+ destroy_workqueue(events->wq);
free_buf:
dma_free_noncoherent(xdna->ddev.dev, events->size, events->buf,
events->addr, DMA_FROM_DEVICE);
@@ -356,3 +402,18 @@ free_events:
kfree(events);
return ret;
}
+
+int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_get_array *args)
+{
+ struct amdxdna_dev *xdna = ndev->xdna;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ args->num_element = 1;
+ args->element_size = sizeof(ndev->last_async_err);
+ if (copy_to_user(u64_to_user_ptr(args->buffer),
+ &ndev->last_async_err, args->element_size))
+ return -EFAULT;
+
+ return 0;
+}
diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c
index 9caad083543d..d493bb1c3360 100644
--- a/drivers/accel/amdxdna/aie2_message.c
+++ b/drivers/accel/amdxdna/aie2_message.c
@@ -27,6 +27,8 @@
#define DECLARE_AIE2_MSG(name, op) \
DECLARE_XDNA_MSG_COMMON(name, op, MAX_AIE2_STATUS_CODE)
+#define EXEC_MSG_OPS(xdna) ((xdna)->dev_handle->exec_msg_ops)
+
static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
struct xdna_mailbox_msg *msg)
{
@@ -37,7 +39,7 @@ static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
if (!ndev->mgmt_chann)
return -ENODEV;
- drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+ drm_WARN_ON(&xdna->ddev, xdna->rpm_on && !mutex_is_locked(&xdna->dev_lock));
ret = xdna_send_msg_wait(xdna, ndev->mgmt_chann, msg);
if (ret == -ETIME) {
xdna_mailbox_stop_channel(ndev->mgmt_chann);
@@ -45,7 +47,7 @@ static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
ndev->mgmt_chann = NULL;
}
- if (!ret && *hdl->data != AIE2_STATUS_SUCCESS) {
+ if (!ret && *hdl->status != AIE2_STATUS_SUCCESS) {
XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x",
msg->opcode, *hdl->data);
ret = -EINVAL;
@@ -208,6 +210,14 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
hwctx->fw_ctx_id = resp.context_id;
WARN_ONCE(hwctx->fw_ctx_id == -1, "Unexpected context id");
+ if (ndev->force_preempt_enabled) {
+ ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FORCE_PREEMPT, &hwctx->fw_ctx_id);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to enable force preempt %d", ret);
+ return ret;
+ }
+ }
+
cq_pair = &resp.cq_pair[0];
x2i.mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.head_addr);
x2i.mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.tail_addr);
@@ -233,6 +243,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
ret = -EINVAL;
goto out_destroy_context;
}
+ ndev->hwctx_num++;
XDNA_DBG(xdna, "%s mailbox channel irq: %d, msix_id: %d",
hwctx->name, ret, resp.msix_id);
@@ -267,6 +278,7 @@ int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwc
hwctx->fw_ctx_id);
hwctx->priv->mbox_chann = NULL;
hwctx->fw_ctx_id = -1;
+ ndev->hwctx_num--;
return ret;
}
@@ -332,11 +344,6 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
goto fail;
}
- if (resp.status != AIE2_STATUS_SUCCESS) {
- XDNA_ERR(xdna, "Query NPU status failed, status 0x%x", resp.status);
- ret = -EINVAL;
- goto fail;
- }
XDNA_DBG(xdna, "Query NPU status completed");
if (size < resp.size) {
@@ -358,6 +365,55 @@ fail:
return ret;
}
+int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
+ char __user *buf, u32 size,
+ struct amdxdna_drm_query_telemetry_header *header)
+{
+ DECLARE_AIE2_MSG(get_telemetry, MSG_OP_GET_TELEMETRY);
+ struct amdxdna_dev *xdna = ndev->xdna;
+ dma_addr_t dma_addr;
+ u8 *addr;
+ int ret;
+
+ if (header->type >= MAX_TELEMETRY_TYPE)
+ return -EINVAL;
+
+ addr = dma_alloc_noncoherent(xdna->ddev.dev, size, &dma_addr,
+ DMA_FROM_DEVICE, GFP_KERNEL);
+ if (!addr)
+ return -ENOMEM;
+
+ req.buf_addr = dma_addr;
+ req.buf_size = size;
+ req.type = header->type;
+
+ drm_clflush_virt_range(addr, size); /* device can access */
+ ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ if (ret) {
+ XDNA_ERR(xdna, "Query telemetry failed, status %d", ret);
+ goto free_buf;
+ }
+
+ if (size < resp.size) {
+ ret = -EINVAL;
+ XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
+ goto free_buf;
+ }
+
+ if (copy_to_user(buf, addr, resp.size)) {
+ ret = -EFAULT;
+ XDNA_ERR(xdna, "Failed to copy telemetry to user space");
+ goto free_buf;
+ }
+
+ header->major = resp.major;
+ header->minor = resp.minor;
+
+free_buf:
+ dma_free_noncoherent(xdna->ddev.dev, size, addr, dma_addr, DMA_FROM_DEVICE);
+ return ret;
+}
+
int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
void *handle, int (*cb)(void*, void __iomem *, size_t))
{
@@ -377,15 +433,17 @@ int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr,
return xdna_mailbox_send_msg(ndev->mgmt_chann, &msg, TX_TIMEOUT);
}
-int aie2_config_cu(struct amdxdna_hwctx *hwctx)
+int aie2_config_cu(struct amdxdna_hwctx *hwctx,
+ int (*notify_cb)(void *, void __iomem *, size_t))
{
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
struct amdxdna_dev *xdna = hwctx->client->xdna;
u32 shift = xdna->dev_info->dev_mem_buf_shift;
- DECLARE_AIE2_MSG(config_cu, MSG_OP_CONFIG_CU);
+ struct config_cu_req req = { 0 };
+ struct xdna_mailbox_msg msg;
struct drm_gem_object *gobj;
struct amdxdna_gem_obj *abo;
- int ret, i;
+ int i;
if (!chann)
return -ENODEV;
@@ -423,191 +481,386 @@ int aie2_config_cu(struct amdxdna_hwctx *hwctx)
}
req.num_cus = hwctx->cus->num_cus;
- ret = xdna_send_msg_wait(xdna, chann, &msg);
- if (ret == -ETIME)
- aie2_destroy_context(xdna->dev_handle, hwctx);
+ msg.send_data = (u8 *)&req;
+ msg.send_size = sizeof(req);
+ msg.handle = hwctx;
+ msg.opcode = MSG_OP_CONFIG_CU;
+ msg.notify_cb = notify_cb;
+ return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+}
- if (resp.status == AIE2_STATUS_SUCCESS) {
- XDNA_DBG(xdna, "Configure %d CUs, ret %d", req.num_cus, ret);
- return 0;
- }
+static int aie2_init_exec_cu_req(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op)
+{
+ struct execute_buffer_req *cu_req = req;
+ u32 cmd_len;
+ void *cmd;
- XDNA_ERR(xdna, "Command opcode 0x%x failed, status 0x%x ret %d",
- msg.opcode, resp.status, ret);
- return ret;
+ cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (cmd_len > sizeof(cu_req->payload))
+ return -EINVAL;
+
+ cu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (cu_req->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ memcpy(cu_req->payload, cmd, cmd_len);
+
+ *size = sizeof(*cu_req);
+ *msg_op = MSG_OP_EXECUTE_BUFFER_CF;
+ return 0;
}
-int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
- int (*notify_cb)(void *, void __iomem *, size_t))
+static int aie2_init_exec_dpu_req(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op)
{
- struct mailbox_channel *chann = hwctx->priv->mbox_chann;
- struct amdxdna_dev *xdna = hwctx->client->xdna;
- struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
- union {
- struct execute_buffer_req ebuf;
- struct exec_dpu_req dpu;
- } req;
- struct xdna_mailbox_msg msg;
- u32 payload_len;
- void *payload;
- int cu_idx;
- int ret;
- u32 op;
+ struct exec_dpu_req *dpu_req = req;
+ struct amdxdna_cmd_start_npu *sn;
+ u32 cmd_len;
- if (!chann)
- return -ENODEV;
+ sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (cmd_len - sizeof(*sn) > sizeof(dpu_req->payload))
+ return -EINVAL;
- payload = amdxdna_cmd_get_payload(cmd_abo, &payload_len);
- if (!payload) {
- XDNA_ERR(xdna, "Invalid command, cannot get payload");
+ dpu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (dpu_req->cu_idx == INVALID_CU_IDX)
return -EINVAL;
- }
- cu_idx = amdxdna_cmd_get_cu_idx(cmd_abo);
- if (cu_idx < 0) {
- XDNA_DBG(xdna, "Invalid cu idx");
+ dpu_req->inst_buf_addr = sn->buffer;
+ dpu_req->inst_size = sn->buffer_size;
+ dpu_req->inst_prop_cnt = sn->prop_count;
+ memcpy(dpu_req->payload, sn->prop_args, cmd_len - sizeof(*sn));
+
+ *size = sizeof(*dpu_req);
+ *msg_op = MSG_OP_EXEC_DPU;
+ return 0;
+}
+
+static void aie2_init_exec_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt)
+{
+ struct cmd_chain_req *chain_req = req;
+
+ chain_req->buf_addr = slot_addr;
+ chain_req->buf_size = size;
+ chain_req->count = cmd_cnt;
+}
+
+static void aie2_init_npu_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt)
+{
+ struct cmd_chain_npu_req *npu_chain_req = req;
+
+ npu_chain_req->flags = 0;
+ npu_chain_req->reserved = 0;
+ npu_chain_req->buf_addr = slot_addr;
+ npu_chain_req->buf_size = size;
+ npu_chain_req->count = cmd_cnt;
+}
+
+static int
+aie2_cmdlist_fill_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_execbuf_cf *cf_slot = slot;
+ u32 cmd_len;
+ void *cmd;
+
+ cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (*size < sizeof(*cf_slot) + cmd_len)
return -EINVAL;
- }
- op = amdxdna_cmd_get_op(cmd_abo);
- switch (op) {
+ cf_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (cf_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ cf_slot->arg_cnt = cmd_len / sizeof(u32);
+ memcpy(cf_slot->args, cmd, cmd_len);
+ /* Accurate slot size to hint firmware to do necessary copy */
+ *size = sizeof(*cf_slot) + cmd_len;
+ return 0;
+}
+
+static int
+aie2_cmdlist_fill_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_dpu *dpu_slot = slot;
+ struct amdxdna_cmd_start_npu *sn;
+ u32 cmd_len;
+ u32 arg_sz;
+
+ sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*sn);
+ if (cmd_len < sizeof(*sn) || arg_sz > MAX_DPU_ARGS_SIZE)
+ return -EINVAL;
+
+ if (*size < sizeof(*dpu_slot) + arg_sz)
+ return -EINVAL;
+
+ dpu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (dpu_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ dpu_slot->inst_buf_addr = sn->buffer;
+ dpu_slot->inst_size = sn->buffer_size;
+ dpu_slot->inst_prop_cnt = sn->prop_count;
+ dpu_slot->arg_cnt = arg_sz / sizeof(u32);
+ memcpy(dpu_slot->args, sn->prop_args, arg_sz);
+
+ /* Accurate slot size to hint firmware to do necessary copy */
+ *size = sizeof(*dpu_slot) + arg_sz;
+ return 0;
+}
+
+static int aie2_cmdlist_unsupp(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ return -EOPNOTSUPP;
+}
+
+static u32 aie2_get_chain_msg_op(u32 cmd_op)
+{
+ switch (cmd_op) {
case ERT_START_CU:
- if (unlikely(payload_len > sizeof(req.ebuf.payload)))
- XDNA_DBG(xdna, "Invalid ebuf payload len: %d", payload_len);
- req.ebuf.cu_idx = cu_idx;
- memcpy(req.ebuf.payload, payload, sizeof(req.ebuf.payload));
- msg.send_size = sizeof(req.ebuf);
- msg.opcode = MSG_OP_EXECUTE_BUFFER_CF;
- break;
- case ERT_START_NPU: {
- struct amdxdna_cmd_start_npu *sn = payload;
-
- if (unlikely(payload_len - sizeof(*sn) > sizeof(req.dpu.payload)))
- XDNA_DBG(xdna, "Invalid dpu payload len: %d", payload_len);
- req.dpu.inst_buf_addr = sn->buffer;
- req.dpu.inst_size = sn->buffer_size;
- req.dpu.inst_prop_cnt = sn->prop_count;
- req.dpu.cu_idx = cu_idx;
- memcpy(req.dpu.payload, sn->prop_args, sizeof(req.dpu.payload));
- msg.send_size = sizeof(req.dpu);
- msg.opcode = MSG_OP_EXEC_DPU;
+ return MSG_OP_CHAIN_EXEC_BUFFER_CF;
+ case ERT_START_NPU:
+ return MSG_OP_CHAIN_EXEC_DPU;
+ default:
break;
}
- default:
- XDNA_DBG(xdna, "Invalid ERT cmd op code: %d", op);
+
+ return MSG_OP_MAX_OPCODE;
+}
+
+static struct aie2_exec_msg_ops legacy_exec_message_ops = {
+ .init_cu_req = aie2_init_exec_cu_req,
+ .init_dpu_req = aie2_init_exec_dpu_req,
+ .init_chain_req = aie2_init_exec_chain_req,
+ .fill_cf_slot = aie2_cmdlist_fill_cf,
+ .fill_dpu_slot = aie2_cmdlist_fill_dpu,
+ .fill_preempt_slot = aie2_cmdlist_unsupp,
+ .fill_elf_slot = aie2_cmdlist_unsupp,
+ .get_chain_msg_op = aie2_get_chain_msg_op,
+};
+
+static int
+aie2_cmdlist_fill_npu_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ u32 cmd_len;
+ void *cmd;
+
+ cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ if (*size < sizeof(*npu_slot) + cmd_len)
return -EINVAL;
- }
- msg.handle = job;
- msg.notify_cb = notify_cb;
- msg.send_data = (u8 *)&req;
- print_hex_dump_debug("cmd: ", DUMP_PREFIX_OFFSET, 16, 4, &req,
- 0x40, false);
- ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
- if (ret) {
- XDNA_ERR(xdna, "Send message failed");
- return ret;
- }
+ npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (npu_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_NON_ELF;
+ npu_slot->arg_cnt = cmd_len / sizeof(u32);
+ memcpy(npu_slot->args, cmd, cmd_len);
+
+ *size = sizeof(*npu_slot) + cmd_len;
return 0;
}
static int
-aie2_cmdlist_fill_one_slot_cf(void *cmd_buf, u32 offset,
- struct amdxdna_gem_obj *abo, u32 *size)
+aie2_cmdlist_fill_npu_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
{
- struct cmd_chain_slot_execbuf_cf *buf = cmd_buf + offset;
- int cu_idx = amdxdna_cmd_get_cu_idx(abo);
- u32 payload_len;
- void *payload;
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ struct amdxdna_cmd_start_npu *sn;
+ u32 cmd_len;
+ u32 arg_sz;
- if (cu_idx < 0)
+ sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*sn);
+ if (cmd_len < sizeof(*sn) || arg_sz > MAX_NPU_ARGS_SIZE)
return -EINVAL;
- payload = amdxdna_cmd_get_payload(abo, &payload_len);
- if (!payload)
+ if (*size < sizeof(*npu_slot) + arg_sz)
return -EINVAL;
- if (!slot_has_space(*buf, offset, payload_len))
- return -ENOSPC;
+ npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (npu_slot->cu_idx == INVALID_CU_IDX)
+ return -EINVAL;
+
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_PARTIAL_ELF;
+ npu_slot->inst_buf_addr = sn->buffer;
+ npu_slot->inst_size = sn->buffer_size;
+ npu_slot->inst_prop_cnt = sn->prop_count;
+ npu_slot->arg_cnt = arg_sz / sizeof(u32);
+ memcpy(npu_slot->args, sn->prop_args, arg_sz);
- buf->cu_idx = cu_idx;
- buf->arg_cnt = payload_len / sizeof(u32);
- memcpy(buf->args, payload, payload_len);
- /* Accurate buf size to hint firmware to do necessary copy */
- *size = sizeof(*buf) + payload_len;
+ *size = sizeof(*npu_slot) + arg_sz;
return 0;
}
static int
-aie2_cmdlist_fill_one_slot_dpu(void *cmd_buf, u32 offset,
- struct amdxdna_gem_obj *abo, u32 *size)
+aie2_cmdlist_fill_npu_preempt(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
{
- struct cmd_chain_slot_dpu *buf = cmd_buf + offset;
- int cu_idx = amdxdna_cmd_get_cu_idx(abo);
- struct amdxdna_cmd_start_npu *sn;
- u32 payload_len;
- void *payload;
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ struct amdxdna_cmd_preempt_data *pd;
+ u32 cmd_len;
u32 arg_sz;
- if (cu_idx < 0)
+ pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*pd);
+ if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
return -EINVAL;
- payload = amdxdna_cmd_get_payload(abo, &payload_len);
- if (!payload)
+ if (*size < sizeof(*npu_slot) + arg_sz)
return -EINVAL;
- sn = payload;
- arg_sz = payload_len - sizeof(*sn);
- if (payload_len < sizeof(*sn) || arg_sz > MAX_DPU_ARGS_SIZE)
+
+ npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
+ if (npu_slot->cu_idx == INVALID_CU_IDX)
return -EINVAL;
- if (!slot_has_space(*buf, offset, arg_sz))
- return -ENOSPC;
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_PREEMPT;
+ npu_slot->inst_buf_addr = pd->inst_buf;
+ npu_slot->save_buf_addr = pd->save_buf;
+ npu_slot->restore_buf_addr = pd->restore_buf;
+ npu_slot->inst_size = pd->inst_size;
+ npu_slot->save_size = pd->save_size;
+ npu_slot->restore_size = pd->restore_size;
+ npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
+ npu_slot->arg_cnt = arg_sz / sizeof(u32);
+ memcpy(npu_slot->args, pd->prop_args, arg_sz);
+
+ *size = sizeof(*npu_slot) + arg_sz;
+ return 0;
+}
+
+static int
+aie2_cmdlist_fill_npu_elf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
+{
+ struct cmd_chain_slot_npu *npu_slot = slot;
+ struct amdxdna_cmd_preempt_data *pd;
+ u32 cmd_len;
+ u32 arg_sz;
+
+ pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
+ arg_sz = cmd_len - sizeof(*pd);
+ if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
+ return -EINVAL;
- buf->inst_buf_addr = sn->buffer;
- buf->inst_size = sn->buffer_size;
- buf->inst_prop_cnt = sn->prop_count;
- buf->cu_idx = cu_idx;
- buf->arg_cnt = arg_sz / sizeof(u32);
- memcpy(buf->args, sn->prop_args, arg_sz);
+ if (*size < sizeof(*npu_slot) + arg_sz)
+ return -EINVAL;
- /* Accurate buf size to hint firmware to do necessary copy */
- *size = sizeof(*buf) + arg_sz;
+ memset(npu_slot, 0, sizeof(*npu_slot));
+ npu_slot->type = EXEC_NPU_TYPE_ELF;
+ npu_slot->inst_buf_addr = pd->inst_buf;
+ npu_slot->save_buf_addr = pd->save_buf;
+ npu_slot->restore_buf_addr = pd->restore_buf;
+ npu_slot->inst_size = pd->inst_size;
+ npu_slot->save_size = pd->save_size;
+ npu_slot->restore_size = pd->restore_size;
+ npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
+ npu_slot->arg_cnt = 1;
+ npu_slot->args[0] = AIE2_EXEC_BUFFER_KERNEL_OP_TXN;
+
+ *size = struct_size(npu_slot, args, npu_slot->arg_cnt);
return 0;
}
-static int
-aie2_cmdlist_fill_one_slot(u32 op, struct amdxdna_gem_obj *cmdbuf_abo, u32 offset,
- struct amdxdna_gem_obj *abo, u32 *size)
+static u32 aie2_get_npu_chain_msg_op(u32 cmd_op)
+{
+ return MSG_OP_CHAIN_EXEC_NPU;
+}
+
+static struct aie2_exec_msg_ops npu_exec_message_ops = {
+ .init_cu_req = aie2_init_exec_cu_req,
+ .init_dpu_req = aie2_init_exec_dpu_req,
+ .init_chain_req = aie2_init_npu_chain_req,
+ .fill_cf_slot = aie2_cmdlist_fill_npu_cf,
+ .fill_dpu_slot = aie2_cmdlist_fill_npu_dpu,
+ .fill_preempt_slot = aie2_cmdlist_fill_npu_preempt,
+ .fill_elf_slot = aie2_cmdlist_fill_npu_elf,
+ .get_chain_msg_op = aie2_get_npu_chain_msg_op,
+};
+
+static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo,
+ size_t *size, u32 *msg_op)
{
- u32 this_op = amdxdna_cmd_get_op(abo);
- void *cmd_buf = cmdbuf_abo->mem.kva;
+ struct amdxdna_dev *xdna = cmd_abo->client->xdna;
int ret;
+ u32 op;
- if (this_op != op) {
- ret = -EINVAL;
- goto done;
- }
+ op = amdxdna_cmd_get_op(cmd_abo);
switch (op) {
case ERT_START_CU:
- ret = aie2_cmdlist_fill_one_slot_cf(cmd_buf, offset, abo, size);
+ ret = EXEC_MSG_OPS(xdna)->init_cu_req(cmd_abo, req, size, msg_op);
+ if (ret) {
+ XDNA_DBG(xdna, "Init CU req failed ret %d", ret);
+ return ret;
+ }
break;
case ERT_START_NPU:
- ret = aie2_cmdlist_fill_one_slot_dpu(cmd_buf, offset, abo, size);
+ ret = EXEC_MSG_OPS(xdna)->init_dpu_req(cmd_abo, req, size, msg_op);
+ if (ret) {
+ XDNA_DBG(xdna, "Init DPU req failed ret %d", ret);
+ return ret;
+ }
+
break;
default:
+ XDNA_ERR(xdna, "Unsupported op %d", op);
ret = -EOPNOTSUPP;
+ break;
}
-done:
- if (ret) {
- XDNA_ERR(abo->client->xdna, "Can't fill slot for cmd op %d ret %d",
- op, ret);
+ return ret;
+}
+
+static int
+aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
+ size_t *size, u32 *cmd_op)
+{
+ struct amdxdna_dev *xdna = cmd_abo->client->xdna;
+ int ret;
+ u32 op;
+
+ op = amdxdna_cmd_get_op(cmd_abo);
+ if (*cmd_op == ERT_INVALID_CMD)
+ *cmd_op = op;
+ else if (op != *cmd_op)
+ return -EINVAL;
+
+ switch (op) {
+ case ERT_START_CU:
+ ret = EXEC_MSG_OPS(xdna)->fill_cf_slot(cmd_abo, slot, size);
+ break;
+ case ERT_START_NPU:
+ ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size);
+ break;
+ case ERT_START_NPU_PREEMPT:
+ if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
+ return -EOPNOTSUPP;
+ ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size);
+ break;
+ case ERT_START_NPU_PREEMPT_ELF:
+ if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
+ return -EOPNOTSUPP;
+ ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size);
+ break;
+ default:
+ XDNA_INFO(xdna, "Unsupported op %d", op);
+ ret = -EOPNOTSUPP;
+ break;
}
+
return ret;
}
+void aie2_msg_init(struct amdxdna_dev_hdl *ndev)
+{
+ if (AIE2_FEATURE_ON(ndev, AIE2_NPU_COMMAND))
+ ndev->exec_msg_ops = &npu_exec_message_ops;
+ else
+ ndev->exec_msg_ops = &legacy_exec_message_ops;
+}
+
static inline struct amdxdna_gem_obj *
aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job)
{
@@ -616,29 +869,36 @@ aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job)
return job->hwctx->priv->cmd_buf[idx];
}
-static void
-aie2_cmdlist_prepare_request(struct cmd_chain_req *req,
- struct amdxdna_gem_obj *cmdbuf_abo, u32 size, u32 cnt)
+int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t))
{
- req->buf_addr = cmdbuf_abo->mem.dev_addr;
- req->buf_size = size;
- req->count = cnt;
- drm_clflush_virt_range(cmdbuf_abo->mem.kva, size);
- XDNA_DBG(cmdbuf_abo->client->xdna, "Command buf addr 0x%llx size 0x%x count %d",
- req->buf_addr, size, cnt);
-}
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
+ struct xdna_mailbox_msg msg;
+ union exec_req req;
+ int ret;
-static inline u32
-aie2_cmd_op_to_msg_op(u32 op)
-{
- switch (op) {
- case ERT_START_CU:
- return MSG_OP_CHAIN_EXEC_BUFFER_CF;
- case ERT_START_NPU:
- return MSG_OP_CHAIN_EXEC_DPU;
- default:
- return MSG_OP_MAX_OPCODE;
+ if (!chann)
+ return -ENODEV;
+
+ ret = aie2_init_exec_req(&req, cmd_abo, &msg.send_size, &msg.opcode);
+ if (ret)
+ return ret;
+
+ msg.handle = job;
+ msg.notify_cb = notify_cb;
+ msg.send_data = (u8 *)&req;
+ print_hex_dump_debug("cmd: ", DUMP_PREFIX_OFFSET, 16, 4, &req,
+ 0x40, false);
+
+ ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+ if (ret) {
+ XDNA_ERR(xdna, "Send message failed");
+ return ret;
}
+
+ return 0;
}
int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
@@ -649,12 +909,13 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
struct amdxdna_client *client = hwctx->client;
struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
+ struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_cmd_chain *payload;
struct xdna_mailbox_msg msg;
- struct cmd_chain_req req;
+ union exec_chain_req req;
u32 payload_len;
u32 offset = 0;
- u32 size;
+ size_t size;
int ret;
u32 op;
u32 i;
@@ -665,41 +926,42 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
payload_len < struct_size(payload, data, payload->command_count))
return -EINVAL;
+ op = ERT_INVALID_CMD;
for (i = 0; i < payload->command_count; i++) {
u32 boh = (u32)(payload->data[i]);
struct amdxdna_gem_obj *abo;
abo = amdxdna_gem_get_obj(client, boh, AMDXDNA_BO_CMD);
if (!abo) {
- XDNA_ERR(client->xdna, "Failed to find cmd BO %d", boh);
+ XDNA_ERR(xdna, "Failed to find cmd BO %d", boh);
return -ENOENT;
}
- /* All sub-cmd should have same op, use the first one. */
- if (i == 0)
- op = amdxdna_cmd_get_op(abo);
-
- ret = aie2_cmdlist_fill_one_slot(op, cmdbuf_abo, offset, abo, &size);
+ size = cmdbuf_abo->mem.size - offset;
+ ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva + offset,
+ abo, &size, &op);
amdxdna_gem_put_obj(abo);
if (ret)
- return -EINVAL;
+ return ret;
offset += size;
}
+ msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op);
+ if (msg.opcode == MSG_OP_MAX_OPCODE)
+ return -EOPNOTSUPP;
/* The offset is the accumulated total size of the cmd buffer */
- aie2_cmdlist_prepare_request(&req, cmdbuf_abo, offset, payload->command_count);
+ EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr,
+ offset, payload->command_count);
+ drm_clflush_virt_range(cmdbuf_abo->mem.kva, offset);
- msg.opcode = aie2_cmd_op_to_msg_op(op);
- if (msg.opcode == MSG_OP_MAX_OPCODE)
- return -EOPNOTSUPP;
msg.handle = job;
msg.notify_cb = notify_cb;
msg.send_data = (u8 *)&req;
msg.send_size = sizeof(req);
ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
if (ret) {
- XDNA_ERR(hwctx->client->xdna, "Send message failed");
+ XDNA_ERR(xdna, "Send message failed");
return ret;
}
@@ -712,23 +974,27 @@ int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
{
struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job);
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
struct xdna_mailbox_msg msg;
- struct cmd_chain_req req;
- u32 size;
+ union exec_chain_req req;
+ u32 op = ERT_INVALID_CMD;
+ size_t size;
int ret;
- u32 op;
- op = amdxdna_cmd_get_op(cmd_abo);
- ret = aie2_cmdlist_fill_one_slot(op, cmdbuf_abo, 0, cmd_abo, &size);
+ size = cmdbuf_abo->mem.size;
+ ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva, cmd_abo, &size, &op);
if (ret)
return ret;
- aie2_cmdlist_prepare_request(&req, cmdbuf_abo, size, 1);
-
- msg.opcode = aie2_cmd_op_to_msg_op(op);
+ msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op);
if (msg.opcode == MSG_OP_MAX_OPCODE)
return -EOPNOTSUPP;
+
+ EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr,
+ size, 1);
+ drm_clflush_virt_range(cmdbuf_abo->mem.kva, size);
+
msg.handle = job;
msg.notify_cb = notify_cb;
msg.send_data = (u8 *)&req;
@@ -753,7 +1019,7 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int ret = 0;
req.src_addr = 0;
- req.dst_addr = abo->mem.dev_addr - hwctx->client->dev_heap->mem.dev_addr;
+ req.dst_addr = amdxdna_dev_bo_offset(abo);
req.size = abo->mem.size;
/* Device to Host */
@@ -777,3 +1043,32 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
return 0;
}
+
+int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t))
+{
+ struct mailbox_channel *chann = hwctx->priv->mbox_chann;
+ struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]);
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct config_debug_bo_req req;
+ struct xdna_mailbox_msg msg;
+
+ if (job->drv_cmd->opcode == ATTACH_DEBUG_BO)
+ req.config = DEBUG_BO_REGISTER;
+ else
+ req.config = DEBUG_BO_UNREGISTER;
+
+ req.offset = amdxdna_dev_bo_offset(abo);
+ req.size = abo->mem.size;
+
+ XDNA_DBG(xdna, "offset 0x%llx size 0x%llx config %d",
+ req.offset, req.size, req.config);
+
+ msg.handle = job;
+ msg.notify_cb = notify_cb;
+ msg.send_data = (u8 *)&req;
+ msg.send_size = sizeof(req);
+ msg.opcode = MSG_OP_CONFIG_DEBUG_BO;
+
+ return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
+}
diff --git a/drivers/accel/amdxdna/aie2_msg_priv.h b/drivers/accel/amdxdna/aie2_msg_priv.h
index 6df9065b13f6..1c957a6298d3 100644
--- a/drivers/accel/amdxdna/aie2_msg_priv.h
+++ b/drivers/accel/amdxdna/aie2_msg_priv.h
@@ -9,7 +9,8 @@
enum aie2_msg_opcode {
MSG_OP_CREATE_CONTEXT = 0x2,
MSG_OP_DESTROY_CONTEXT = 0x3,
- MSG_OP_SYNC_BO = 0x7,
+ MSG_OP_GET_TELEMETRY = 0x4,
+ MSG_OP_SYNC_BO = 0x7,
MSG_OP_EXECUTE_BUFFER_CF = 0xC,
MSG_OP_QUERY_COL_STATUS = 0xD,
MSG_OP_QUERY_AIE_TILE_INFO = 0xE,
@@ -18,6 +19,8 @@ enum aie2_msg_opcode {
MSG_OP_CONFIG_CU = 0x11,
MSG_OP_CHAIN_EXEC_BUFFER_CF = 0x12,
MSG_OP_CHAIN_EXEC_DPU = 0x13,
+ MSG_OP_CONFIG_DEBUG_BO = 0x14,
+ MSG_OP_CHAIN_EXEC_NPU = 0x18,
MSG_OP_MAX_XRT_OPCODE,
MSG_OP_SUSPEND = 0x101,
MSG_OP_RESUME = 0x102,
@@ -135,6 +138,28 @@ struct destroy_ctx_resp {
enum aie2_msg_status status;
} __packed;
+enum telemetry_type {
+ TELEMETRY_TYPE_DISABLED,
+ TELEMETRY_TYPE_HEALTH,
+ TELEMETRY_TYPE_ERROR_INFO,
+ TELEMETRY_TYPE_PROFILING,
+ TELEMETRY_TYPE_DEBUG,
+ MAX_TELEMETRY_TYPE
+};
+
+struct get_telemetry_req {
+ enum telemetry_type type;
+ __u64 buf_addr;
+ __u32 buf_size;
+} __packed;
+
+struct get_telemetry_resp {
+ __u32 major;
+ __u32 minor;
+ __u32 size;
+ enum aie2_msg_status status;
+} __packed;
+
struct execute_buffer_req {
__u32 cu_idx;
__u32 payload[19];
@@ -148,6 +173,18 @@ struct exec_dpu_req {
__u32 payload[35];
} __packed;
+enum exec_npu_type {
+ EXEC_NPU_TYPE_NON_ELF = 0x1,
+ EXEC_NPU_TYPE_PARTIAL_ELF = 0x2,
+ EXEC_NPU_TYPE_PREEMPT = 0x3,
+ EXEC_NPU_TYPE_ELF = 0x4,
+};
+
+union exec_req {
+ struct execute_buffer_req ebuf;
+ struct exec_dpu_req dpu_req;
+};
+
struct execute_buffer_resp {
enum aie2_msg_status status;
} __packed;
@@ -319,9 +356,6 @@ struct async_event_msg_resp {
} __packed;
#define MAX_CHAIN_CMDBUF_SIZE SZ_4K
-#define slot_has_space(slot, offset, payload_size) \
- (MAX_CHAIN_CMDBUF_SIZE >= (offset) + (payload_size) + \
- sizeof(typeof(slot)))
struct cmd_chain_slot_execbuf_cf {
__u32 cu_idx;
@@ -339,12 +373,41 @@ struct cmd_chain_slot_dpu {
__u32 args[] __counted_by(arg_cnt);
};
+#define MAX_NPU_ARGS_SIZE (26 * sizeof(__u32))
+#define AIE2_EXEC_BUFFER_KERNEL_OP_TXN 3
+struct cmd_chain_slot_npu {
+ enum exec_npu_type type;
+ u64 inst_buf_addr;
+ u64 save_buf_addr;
+ u64 restore_buf_addr;
+ u32 inst_size;
+ u32 save_size;
+ u32 restore_size;
+ u32 inst_prop_cnt;
+ u32 cu_idx;
+ u32 arg_cnt;
+ u32 args[] __counted_by(arg_cnt);
+} __packed;
+
struct cmd_chain_req {
__u64 buf_addr;
__u32 buf_size;
__u32 count;
} __packed;
+struct cmd_chain_npu_req {
+ u32 flags;
+ u32 reserved;
+ u64 buf_addr;
+ u32 buf_size;
+ u32 count;
+} __packed;
+
+union exec_chain_req {
+ struct cmd_chain_npu_req npu_req;
+ struct cmd_chain_req req;
+};
+
struct cmd_chain_resp {
enum aie2_msg_status status;
__u32 fail_cmd_idx;
@@ -365,4 +428,21 @@ struct sync_bo_req {
struct sync_bo_resp {
enum aie2_msg_status status;
} __packed;
+
+#define DEBUG_BO_UNREGISTER 0
+#define DEBUG_BO_REGISTER 1
+struct config_debug_bo_req {
+ __u64 offset;
+ __u64 size;
+ /*
+ * config operations.
+ * DEBUG_BO_REGISTER: Register debug buffer
+ * DEBUG_BO_UNREGISTER: Unregister debug buffer
+ */
+ __u32 config;
+} __packed;
+
+struct config_debug_bo_resp {
+ enum aie2_msg_status status;
+} __packed;
#endif /* _AIE2_MSG_PRIV_H_ */
diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c
index 87c425e3d2b9..ceef1c502e9e 100644
--- a/drivers/accel/amdxdna/aie2_pci.c
+++ b/drivers/accel/amdxdna/aie2_pci.c
@@ -25,6 +25,7 @@
#include "amdxdna_gem.h"
#include "amdxdna_mailbox.h"
#include "amdxdna_pci_drv.h"
+#include "amdxdna_pm.h"
static int aie2_max_col = XRS_MAX_COL;
module_param(aie2_max_col, uint, 0600);
@@ -54,6 +55,7 @@ struct mgmt_mbox_chann_info {
static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 fw_minor)
{
+ const struct aie2_fw_feature_tbl *feature;
struct amdxdna_dev *xdna = ndev->xdna;
/*
@@ -77,6 +79,17 @@ static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 f
XDNA_ERR(xdna, "Firmware minor version smaller than supported");
return -EINVAL;
}
+
+ for (feature = ndev->priv->fw_feature_tbl; feature && feature->min_minor;
+ feature++) {
+ if (fw_minor < feature->min_minor)
+ continue;
+ if (feature->max_minor > 0 && fw_minor > feature->max_minor)
+ continue;
+
+ set_bit(feature->feature, &ndev->feature_mask);
+ }
+
return 0;
}
@@ -170,6 +183,10 @@ int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev,
if (cfg->category != category)
continue;
+ if (cfg->feature_mask &&
+ bitmap_subset(&cfg->feature_mask, &ndev->feature_mask, AIE2_FEATURE_MAX))
+ continue;
+
value = val ? *val : cfg->value;
ret = aie2_set_runtime_cfg(ndev, cfg->type, value);
if (ret) {
@@ -223,15 +240,6 @@ static int aie2_mgmt_fw_init(struct amdxdna_dev_hdl *ndev)
return ret;
}
- if (!ndev->async_events)
- return 0;
-
- ret = aie2_error_async_events_send(ndev);
- if (ret) {
- XDNA_ERR(ndev->xdna, "Send async events failed");
- return ret;
- }
-
return 0;
}
@@ -257,6 +265,8 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev)
return ret;
}
+ ndev->total_col = min(aie2_max_col, ndev->metadata.cols);
+
return 0;
}
@@ -338,6 +348,7 @@ static void aie2_hw_stop(struct amdxdna_dev *xdna)
ndev->mbox = NULL;
aie2_psp_stop(ndev->psp_hdl);
aie2_smu_fini(ndev);
+ aie2_error_async_events_free(ndev);
pci_disable_device(pdev);
ndev->dev_status = AIE2_DEV_INIT;
@@ -424,6 +435,18 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
goto destroy_mgmt_chann;
}
+ ret = aie2_mgmt_fw_query(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to query fw, ret %d", ret);
+ goto destroy_mgmt_chann;
+ }
+
+ ret = aie2_error_async_events_alloc(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "Allocate async events failed, ret %d", ret);
+ goto destroy_mgmt_chann;
+ }
+
ndev->dev_status = AIE2_DEV_START;
return 0;
@@ -459,7 +482,6 @@ static int aie2_hw_resume(struct amdxdna_dev *xdna)
struct amdxdna_client *client;
int ret;
- guard(mutex)(&xdna->dev_lock);
ret = aie2_hw_start(xdna);
if (ret) {
XDNA_ERR(xdna, "Start hardware failed, %d", ret);
@@ -565,13 +587,6 @@ static int aie2_init(struct amdxdna_dev *xdna)
goto release_fw;
}
- ret = aie2_mgmt_fw_query(ndev);
- if (ret) {
- XDNA_ERR(xdna, "Query firmware failed, ret %d", ret);
- goto stop_hw;
- }
- ndev->total_col = min(aie2_max_col, ndev->metadata.cols);
-
xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1;
for (i = 0; i < xrs_cfg.clk_list.num_levels; i++)
xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk;
@@ -587,30 +602,11 @@ static int aie2_init(struct amdxdna_dev *xdna)
goto stop_hw;
}
- ret = aie2_error_async_events_alloc(ndev);
- if (ret) {
- XDNA_ERR(xdna, "Allocate async events failed, ret %d", ret);
- goto stop_hw;
- }
-
- ret = aie2_error_async_events_send(ndev);
- if (ret) {
- XDNA_ERR(xdna, "Send async events failed, ret %d", ret);
- goto async_event_free;
- }
-
- /* Issue a command to make sure firmware handled async events */
- ret = aie2_query_firmware_version(ndev, &ndev->xdna->fw_ver);
- if (ret) {
- XDNA_ERR(xdna, "Re-query firmware version failed");
- goto async_event_free;
- }
-
release_firmware(fw);
+ aie2_msg_init(ndev);
+ amdxdna_pm_init(xdna);
return 0;
-async_event_free:
- aie2_error_async_events_free(ndev);
stop_hw:
aie2_hw_stop(xdna);
release_fw:
@@ -621,10 +617,8 @@ release_fw:
static void aie2_fini(struct amdxdna_dev *xdna)
{
- struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
-
+ amdxdna_pm_fini(xdna);
aie2_hw_stop(xdna);
- aie2_error_async_events_free(ndev);
}
static int aie2_get_aie_status(struct amdxdna_client *client,
@@ -845,7 +839,120 @@ static int aie2_get_hwctx_status(struct amdxdna_client *client,
}
args->buffer_size -= (u32)(array_args.buffer - args->buffer);
- return ret;
+ return 0;
+}
+
+static int aie2_query_resource_info(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_get_resource_info res_info;
+ const struct amdxdna_dev_priv *priv;
+ struct amdxdna_dev_hdl *ndev;
+ struct amdxdna_dev *xdna;
+
+ xdna = client->xdna;
+ ndev = xdna->dev_handle;
+ priv = ndev->priv;
+
+ res_info.npu_clk_max = priv->dpm_clk_tbl[ndev->max_dpm_level].hclk;
+ res_info.npu_tops_max = ndev->max_tops;
+ res_info.npu_task_max = priv->hwctx_limit;
+ res_info.npu_tops_curr = ndev->curr_tops;
+ res_info.npu_task_curr = ndev->hwctx_num;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, sizeof(res_info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int aie2_fill_hwctx_map(struct amdxdna_hwctx *hwctx, void *arg)
+{
+ struct amdxdna_dev *xdna = hwctx->client->xdna;
+ u32 *map = arg;
+
+ if (hwctx->fw_ctx_id >= xdna->dev_handle->priv->hwctx_limit) {
+ XDNA_ERR(xdna, "Invalid fw ctx id %d/%d ", hwctx->fw_ctx_id,
+ xdna->dev_handle->priv->hwctx_limit);
+ return -EINVAL;
+ }
+
+ map[hwctx->fw_ctx_id] = hwctx->id;
+ return 0;
+}
+
+static int aie2_get_telemetry(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_query_telemetry_header *header __free(kfree) = NULL;
+ u32 telemetry_data_sz, header_sz, elem_num;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_client *tmp_client;
+ int ret;
+
+ elem_num = xdna->dev_handle->priv->hwctx_limit;
+ header_sz = struct_size(header, map, elem_num);
+ if (args->buffer_size <= header_sz) {
+ XDNA_ERR(xdna, "Invalid buffer size");
+ return -EINVAL;
+ }
+
+ telemetry_data_sz = args->buffer_size - header_sz;
+ if (telemetry_data_sz > SZ_4M) {
+ XDNA_ERR(xdna, "Buffer size is too big, %d", telemetry_data_sz);
+ return -EINVAL;
+ }
+
+ header = kzalloc(header_sz, GFP_KERNEL);
+ if (!header)
+ return -ENOMEM;
+
+ if (copy_from_user(header, u64_to_user_ptr(args->buffer), sizeof(*header))) {
+ XDNA_ERR(xdna, "Failed to copy telemetry header from user");
+ return -EFAULT;
+ }
+
+ header->map_num_elements = elem_num;
+ list_for_each_entry(tmp_client, &xdna->client_list, node) {
+ ret = amdxdna_hwctx_walk(tmp_client, &header->map,
+ aie2_fill_hwctx_map);
+ if (ret)
+ return ret;
+ }
+
+ ret = aie2_query_telemetry(xdna->dev_handle,
+ u64_to_user_ptr(args->buffer + header_sz),
+ telemetry_data_sz, header);
+ if (ret) {
+ XDNA_ERR(xdna, "Query telemetry failed ret %d", ret);
+ return ret;
+ }
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), header, header_sz)) {
+ XDNA_ERR(xdna, "Copy header failed");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int aie2_get_preempt_state(struct amdxdna_client *client,
+ struct amdxdna_drm_get_info *args)
+{
+ struct amdxdna_drm_attribute_state state = {};
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_dev_hdl *ndev;
+
+ ndev = xdna->dev_handle;
+ if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE)
+ state.state = ndev->force_preempt_enabled;
+ else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE)
+ state.state = ndev->frame_boundary_preempt;
+
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &state, sizeof(state)))
+ return -EFAULT;
+
+ return 0;
}
static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args)
@@ -856,6 +963,10 @@ static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_i
if (!drm_dev_enter(&xdna->ddev, &idx))
return -ENODEV;
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto dev_exit;
+
switch (args->param) {
case DRM_AMDXDNA_QUERY_AIE_STATUS:
ret = aie2_get_aie_status(client, args);
@@ -878,12 +989,25 @@ static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_i
case DRM_AMDXDNA_GET_POWER_MODE:
ret = aie2_get_power_mode(client, args);
break;
+ case DRM_AMDXDNA_QUERY_TELEMETRY:
+ ret = aie2_get_telemetry(client, args);
+ break;
+ case DRM_AMDXDNA_QUERY_RESOURCE_INFO:
+ ret = aie2_query_resource_info(client, args);
+ break;
+ case DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE:
+ case DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE:
+ ret = aie2_get_preempt_state(client, args);
+ break;
default:
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
ret = -EOPNOTSUPP;
}
+
+ amdxdna_pm_suspend_put(xdna);
XDNA_DBG(xdna, "Got param %d", args->param);
+dev_exit:
drm_dev_exit(idx);
return ret;
}
@@ -898,6 +1022,12 @@ static int aie2_query_ctx_status_array(struct amdxdna_client *client,
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+ if (args->element_size > SZ_4K || args->num_element > SZ_1K) {
+ XDNA_DBG(xdna, "Invalid element size %d or number of element %d",
+ args->element_size, args->num_element);
+ return -EINVAL;
+ }
+
array_args.element_size = min(args->element_size,
sizeof(struct amdxdna_drm_hwctx_entry));
array_args.buffer = args->buffer;
@@ -914,7 +1044,7 @@ static int aie2_query_ctx_status_array(struct amdxdna_client *client,
args->num_element = (u32)((array_args.buffer - args->buffer) /
args->element_size);
- return ret;
+ return 0;
}
static int aie2_get_array(struct amdxdna_client *client,
@@ -926,16 +1056,26 @@ static int aie2_get_array(struct amdxdna_client *client,
if (!drm_dev_enter(&xdna->ddev, &idx))
return -ENODEV;
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto dev_exit;
+
switch (args->param) {
case DRM_AMDXDNA_HW_CONTEXT_ALL:
ret = aie2_query_ctx_status_array(client, args);
break;
+ case DRM_AMDXDNA_HW_LAST_ASYNC_ERR:
+ ret = aie2_get_array_async_error(xdna->dev_handle, args);
+ break;
default:
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
ret = -EOPNOTSUPP;
}
+
+ amdxdna_pm_suspend_put(xdna);
XDNA_DBG(xdna, "Got param %d", args->param);
+dev_exit:
drm_dev_exit(idx);
return ret;
}
@@ -965,6 +1105,38 @@ static int aie2_set_power_mode(struct amdxdna_client *client,
return aie2_pm_set_mode(xdna->dev_handle, power_mode);
}
+static int aie2_set_preempt_state(struct amdxdna_client *client,
+ struct amdxdna_drm_set_state *args)
+{
+ struct amdxdna_dev_hdl *ndev = client->xdna->dev_handle;
+ struct amdxdna_drm_attribute_state state;
+ u32 val;
+ int ret;
+
+ if (copy_from_user(&state, u64_to_user_ptr(args->buffer), sizeof(state)))
+ return -EFAULT;
+
+ if (state.state > 1)
+ return -EINVAL;
+
+ if (XDNA_MBZ_DBG(client->xdna, state.pad, sizeof(state.pad)))
+ return -EINVAL;
+
+ if (args->param == DRM_AMDXDNA_SET_FORCE_PREEMPT) {
+ ndev->force_preempt_enabled = state.state;
+ } else if (args->param == DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT) {
+ val = state.state;
+ ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
+ &val);
+ if (ret)
+ return ret;
+
+ ndev->frame_boundary_preempt = state.state;
+ }
+
+ return 0;
+}
+
static int aie2_set_state(struct amdxdna_client *client,
struct amdxdna_drm_set_state *args)
{
@@ -974,16 +1146,26 @@ static int aie2_set_state(struct amdxdna_client *client,
if (!drm_dev_enter(&xdna->ddev, &idx))
return -ENODEV;
+ ret = amdxdna_pm_resume_get(xdna);
+ if (ret)
+ goto dev_exit;
+
switch (args->param) {
case DRM_AMDXDNA_SET_POWER_MODE:
ret = aie2_set_power_mode(client, args);
break;
+ case DRM_AMDXDNA_SET_FORCE_PREEMPT:
+ case DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT:
+ ret = aie2_set_preempt_state(client, args);
+ break;
default:
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
ret = -EOPNOTSUPP;
break;
}
+ amdxdna_pm_suspend_put(xdna);
+dev_exit:
drm_dev_exit(idx);
return ret;
}
@@ -998,6 +1180,7 @@ const struct amdxdna_dev_ops aie2_ops = {
.hwctx_init = aie2_hwctx_init,
.hwctx_fini = aie2_hwctx_fini,
.hwctx_config = aie2_hwctx_config,
+ .hwctx_sync_debug_bo = aie2_hwctx_sync_debug_bo,
.cmd_submit = aie2_cmd_submit,
.hmm_invalidate = aie2_hmm_invalidate,
.get_array = aie2_get_array,
diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h
index 91a8e948f82a..a5f9c42155d1 100644
--- a/drivers/accel/amdxdna/aie2_pci.h
+++ b/drivers/accel/amdxdna/aie2_pci.h
@@ -110,12 +110,15 @@ struct aie_metadata {
enum rt_config_category {
AIE2_RT_CFG_INIT,
AIE2_RT_CFG_CLK_GATING,
+ AIE2_RT_CFG_FORCE_PREEMPT,
+ AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
};
struct rt_config {
u32 type;
u32 value;
u32 category;
+ unsigned long feature_mask;
};
struct dpm_clk_freq {
@@ -156,6 +159,19 @@ enum aie2_dev_status {
AIE2_DEV_START,
};
+struct aie2_exec_msg_ops {
+ int (*init_cu_req)(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op);
+ int (*init_dpu_req)(struct amdxdna_gem_obj *cmd_bo, void *req,
+ size_t *size, u32 *msg_op);
+ void (*init_chain_req)(void *req, u64 slot_addr, size_t size, u32 cmd_cnt);
+ int (*fill_cf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ int (*fill_dpu_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ int (*fill_preempt_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ int (*fill_elf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
+ u32 (*get_chain_msg_op)(u32 cmd_op);
+};
+
struct amdxdna_dev_hdl {
struct amdxdna_dev *xdna;
const struct amdxdna_dev_priv *priv;
@@ -173,6 +189,8 @@ struct amdxdna_dev_hdl {
u32 total_col;
struct aie_version version;
struct aie_metadata metadata;
+ unsigned long feature_mask;
+ struct aie2_exec_msg_ops *exec_msg_ops;
/* power management and clock*/
enum amdxdna_power_mode_type pw_mode;
@@ -182,6 +200,10 @@ struct amdxdna_dev_hdl {
u32 clk_gating;
u32 npuclk_freq;
u32 hclk_freq;
+ u32 max_tops;
+ u32 curr_tops;
+ u32 force_preempt_enabled;
+ u32 frame_boundary_preempt;
/* Mailbox and the management channel */
struct mailbox *mbox;
@@ -190,6 +212,8 @@ struct amdxdna_dev_hdl {
enum aie2_dev_status dev_status;
u32 hwctx_num;
+
+ struct amdxdna_async_error last_async_err;
};
#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
@@ -204,12 +228,27 @@ struct aie2_hw_ops {
int (*set_dpm)(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
};
+enum aie2_fw_feature {
+ AIE2_NPU_COMMAND,
+ AIE2_PREEMPT,
+ AIE2_FEATURE_MAX
+};
+
+struct aie2_fw_feature_tbl {
+ enum aie2_fw_feature feature;
+ u32 max_minor;
+ u32 min_minor;
+};
+
+#define AIE2_FEATURE_ON(ndev, feature) test_bit(feature, &(ndev)->feature_mask)
+
struct amdxdna_dev_priv {
const char *fw_path;
u64 protocol_major;
u64 protocol_minor;
const struct rt_config *rt_config;
const struct dpm_clk_freq *dpm_clk_tbl;
+ const struct aie2_fw_feature_tbl *fw_feature_tbl;
#define COL_ALIGN_NONE 0
#define COL_ALIGN_NATURE 1
@@ -217,6 +256,7 @@ struct amdxdna_dev_priv {
u32 mbox_dev_addr;
/* If mbox_size is 0, use BAR size. See MBOX_SIZE macro */
u32 mbox_size;
+ u32 hwctx_limit;
u32 sram_dev_addr;
struct aie2_bar_off_pair sram_offs[SRAM_MAX_INDEX];
struct aie2_bar_off_pair psp_regs_off[PSP_MAX_REGS];
@@ -234,6 +274,7 @@ extern const struct dpm_clk_freq npu1_dpm_clk_table[];
extern const struct dpm_clk_freq npu4_dpm_clk_table[];
extern const struct rt_config npu1_default_rt_cfg[];
extern const struct rt_config npu4_default_rt_cfg[];
+extern const struct aie2_fw_feature_tbl npu4_fw_feature_table[];
/* aie2_smu.c */
int aie2_smu_init(struct amdxdna_dev_hdl *ndev);
@@ -253,10 +294,12 @@ void aie2_psp_stop(struct psp_device *psp);
/* aie2_error.c */
int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev);
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev);
-int aie2_error_async_events_send(struct amdxdna_dev_hdl *ndev);
int aie2_error_async_msg_thread(void *data);
+int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev,
+ struct amdxdna_drm_get_array *args);
/* aie2_message.c */
+void aie2_msg_init(struct amdxdna_dev_hdl *ndev);
int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev);
int aie2_resume_fw(struct amdxdna_dev_hdl *ndev);
int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value);
@@ -270,9 +313,13 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled);
+int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
+ char __user *buf, u32 size,
+ struct amdxdna_drm_query_telemetry_header *header);
int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
void *handle, int (*cb)(void*, void __iomem *, size_t));
-int aie2_config_cu(struct amdxdna_hwctx *hwctx);
+int aie2_config_cu(struct amdxdna_hwctx *hwctx,
+ int (*notify_cb)(void *, void __iomem *, size_t));
int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, void __iomem *, size_t));
int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
@@ -283,11 +330,14 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
int (*notify_cb)(void *, void __iomem *, size_t));
int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
+ int (*notify_cb)(void *, void __iomem *, size_t));
/* aie2_hwctx.c */
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx);
int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
+int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
void aie2_hwctx_suspend(struct amdxdna_client *client);
int aie2_hwctx_resume(struct amdxdna_client *client);
int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
diff --git a/drivers/accel/amdxdna/aie2_smu.c b/drivers/accel/amdxdna/aie2_smu.c
index d303701b0ded..bd94ee96c2bc 100644
--- a/drivers/accel/amdxdna/aie2_smu.c
+++ b/drivers/accel/amdxdna/aie2_smu.c
@@ -11,6 +11,7 @@
#include "aie2_pci.h"
#include "amdxdna_pci_drv.h"
+#include "amdxdna_pm.h"
#define SMU_RESULT_OK 1
@@ -22,6 +23,13 @@
#define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7
#define AIE2_SMU_SET_HARD_DPMLEVEL 0x8
+#define NPU4_DPM_TOPS(ndev, dpm_level) \
+({ \
+ typeof(ndev) _ndev = ndev; \
+ (4096 * (_ndev)->total_col * \
+ (_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
+})
+
static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
u32 reg_arg, u32 *out)
{
@@ -59,12 +67,16 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
u32 freq;
int ret;
+ ret = amdxdna_pm_resume_get(ndev->xdna);
+ if (ret)
+ return ret;
+
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq);
if (ret) {
XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
- return ret;
+ goto suspend_put;
}
ndev->npuclk_freq = freq;
@@ -73,49 +85,78 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
if (ret) {
XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
- return ret;
+ goto suspend_put;
}
+
+ amdxdna_pm_suspend_put(ndev->xdna);
ndev->hclk_freq = freq;
ndev->dpm_level = dpm_level;
+ ndev->max_tops = 2 * ndev->total_col;
+ ndev->curr_tops = ndev->max_tops * freq / 1028;
XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
ndev->npuclk_freq, ndev->hclk_freq);
return 0;
+
+suspend_put:
+ amdxdna_pm_suspend_put(ndev->xdna);
+ return ret;
}
int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
{
int ret;
+ ret = amdxdna_pm_resume_get(ndev->xdna);
+ if (ret)
+ return ret;
+
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
dpm_level, ret);
- return ret;
+ goto suspend_put;
}
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
dpm_level, ret);
- return ret;
+ goto suspend_put;
}
+ amdxdna_pm_suspend_put(ndev->xdna);
ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
ndev->dpm_level = dpm_level;
+ ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
+ ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);
XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
ndev->npuclk_freq, ndev->hclk_freq);
return 0;
+
+suspend_put:
+ amdxdna_pm_suspend_put(ndev->xdna);
+ return ret;
}
int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
{
int ret;
+ /*
+ * Failing to set power off indicates an unrecoverable hardware or
+ * firmware error.
+ */
+ ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
+ if (ret) {
+ XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
+ return ret;
+ }
+
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
if (ret) {
XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c
index 4bfe4ef20550..d17aef89a0ad 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.c
+++ b/drivers/accel/amdxdna/amdxdna_ctx.c
@@ -113,14 +113,14 @@ void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size)
return &cmd->data[num_masks];
}
-int amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo)
+u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo)
{
struct amdxdna_cmd *cmd = abo->mem.kva;
u32 num_masks, i;
u32 *cu_mask;
if (amdxdna_cmd_get_op(abo) == ERT_CMD_CHAIN)
- return -1;
+ return INVALID_CU_IDX;
num_masks = 1 + FIELD_GET(AMDXDNA_CMD_EXTRA_CU_MASK, cmd->header);
cu_mask = cmd->data;
@@ -129,7 +129,7 @@ int amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo)
return ffs(cu_mask[i]) - 1;
}
- return -1;
+ return INVALID_CU_IDX;
}
/*
@@ -161,19 +161,14 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
if (args->ext || args->ext_flags)
return -EINVAL;
- if (!drm_dev_enter(dev, &idx))
- return -ENODEV;
-
hwctx = kzalloc(sizeof(*hwctx), GFP_KERNEL);
- if (!hwctx) {
- ret = -ENOMEM;
- goto exit;
- }
+ if (!hwctx)
+ return -ENOMEM;
if (copy_from_user(&hwctx->qos, u64_to_user_ptr(args->qos_p), sizeof(hwctx->qos))) {
XDNA_ERR(xdna, "Access QoS info failed");
- ret = -EFAULT;
- goto free_hwctx;
+ kfree(hwctx);
+ return -EFAULT;
}
hwctx->client = client;
@@ -181,30 +176,36 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
hwctx->num_tiles = args->num_tiles;
hwctx->mem_size = args->mem_size;
hwctx->max_opc = args->max_opc;
- ret = xa_alloc_cyclic(&client->hwctx_xa, &hwctx->id, hwctx,
- XA_LIMIT(AMDXDNA_INVALID_CTX_HANDLE + 1, MAX_HWCTX_ID),
- &client->next_hwctxid, GFP_KERNEL);
- if (ret < 0) {
- XDNA_ERR(xdna, "Allocate hwctx ID failed, ret %d", ret);
+
+ guard(mutex)(&xdna->dev_lock);
+
+ if (!drm_dev_enter(dev, &idx)) {
+ ret = -ENODEV;
goto free_hwctx;
}
- hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->id);
+ ret = xdna->dev_info->ops->hwctx_init(hwctx);
+ if (ret) {
+ XDNA_ERR(xdna, "Init hwctx failed, ret %d", ret);
+ goto dev_exit;
+ }
+
+ hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->fw_ctx_id);
if (!hwctx->name) {
ret = -ENOMEM;
- goto rm_id;
+ goto fini_hwctx;
}
- mutex_lock(&xdna->dev_lock);
- ret = xdna->dev_info->ops->hwctx_init(hwctx);
- if (ret) {
- mutex_unlock(&xdna->dev_lock);
- XDNA_ERR(xdna, "Init hwctx failed, ret %d", ret);
+ ret = xa_alloc_cyclic(&client->hwctx_xa, &hwctx->id, hwctx,
+ XA_LIMIT(AMDXDNA_INVALID_CTX_HANDLE + 1, MAX_HWCTX_ID),
+ &client->next_hwctxid, GFP_KERNEL);
+ if (ret < 0) {
+ XDNA_ERR(xdna, "Allocate hwctx ID failed, ret %d", ret);
goto free_name;
}
+
args->handle = hwctx->id;
args->syncobj_handle = hwctx->syncobj_hdl;
- mutex_unlock(&xdna->dev_lock);
atomic64_set(&hwctx->job_submit_cnt, 0);
atomic64_set(&hwctx->job_free_cnt, 0);
@@ -214,12 +215,12 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
free_name:
kfree(hwctx->name);
-rm_id:
- xa_erase(&client->hwctx_xa, hwctx->id);
+fini_hwctx:
+ xdna->dev_info->ops->hwctx_fini(hwctx);
+dev_exit:
+ drm_dev_exit(idx);
free_hwctx:
kfree(hwctx);
-exit:
- drm_dev_exit(idx);
return ret;
}
@@ -327,6 +328,38 @@ unlock_srcu:
return ret;
}
+int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
+{
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_hwctx *hwctx;
+ struct amdxdna_gem_obj *abo;
+ struct drm_gem_object *gobj;
+ int ret, idx;
+
+ if (!xdna->dev_info->ops->hwctx_sync_debug_bo)
+ return -EOPNOTSUPP;
+
+ gobj = drm_gem_object_lookup(client->filp, debug_bo_hdl);
+ if (!gobj)
+ return -EINVAL;
+
+ abo = to_xdna_obj(gobj);
+ guard(mutex)(&xdna->dev_lock);
+ idx = srcu_read_lock(&client->hwctx_srcu);
+ hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx);
+ if (!hwctx) {
+ ret = -EINVAL;
+ goto unlock_srcu;
+ }
+
+ ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl);
+
+unlock_srcu:
+ srcu_read_unlock(&client->hwctx_srcu, idx);
+ drm_gem_object_put(gobj);
+ return ret;
+}
+
static void
amdxdna_arg_bos_put(struct amdxdna_sched_job *job)
{
@@ -389,9 +422,11 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
trace_amdxdna_debug_point(job->hwctx->name, job->seq, "job release");
amdxdna_arg_bos_put(job);
amdxdna_gem_put_obj(job->cmd_bo);
+ dma_fence_put(job->fence);
}
int amdxdna_cmd_submit(struct amdxdna_client *client,
+ struct amdxdna_drv_cmd *drv_cmd,
u32 cmd_bo_hdl, u32 *arg_bo_hdls, u32 arg_bo_cnt,
u32 hwctx_hdl, u64 *seq)
{
@@ -405,6 +440,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
if (!job)
return -ENOMEM;
+ job->drv_cmd = drv_cmd;
+
if (cmd_bo_hdl != AMDXDNA_INVALID_BO_HANDLE) {
job->cmd_bo = amdxdna_gem_get_obj(client, cmd_bo_hdl, AMDXDNA_BO_CMD);
if (!job->cmd_bo) {
@@ -412,8 +449,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
ret = -EINVAL;
goto free_job;
}
- } else {
- job->cmd_bo = NULL;
}
ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
@@ -431,11 +466,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
goto unlock_srcu;
}
- if (hwctx->status != HWCTX_STAT_READY) {
- XDNA_ERR(xdna, "HW Context is not ready");
- ret = -EINVAL;
- goto unlock_srcu;
- }
job->hwctx = hwctx;
job->mm = current->mm;
@@ -512,7 +542,7 @@ static int amdxdna_drm_submit_execbuf(struct amdxdna_client *client,
}
}
- ret = amdxdna_cmd_submit(client, cmd_bo_hdl, arg_bo_hdls,
+ ret = amdxdna_cmd_submit(client, NULL, cmd_bo_hdl, arg_bo_hdls,
args->arg_count, args->hwctx, &args->seq);
if (ret)
XDNA_DBG(xdna, "Submit cmds failed, ret %d", ret);
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h
index 7cd7a55936f0..b6151244d64f 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.h
+++ b/drivers/accel/amdxdna/amdxdna_ctx.h
@@ -13,9 +13,12 @@
struct amdxdna_hwctx_priv;
enum ert_cmd_opcode {
- ERT_START_CU = 0,
- ERT_CMD_CHAIN = 19,
- ERT_START_NPU = 20,
+ ERT_START_CU = 0,
+ ERT_CMD_CHAIN = 19,
+ ERT_START_NPU = 20,
+ ERT_START_NPU_PREEMPT = 21,
+ ERT_START_NPU_PREEMPT_ELF = 22,
+ ERT_INVALID_CMD = ~0U,
};
enum ert_cmd_state {
@@ -54,6 +57,21 @@ struct amdxdna_cmd_chain {
u64 data[] __counted_by(command_count);
};
+/*
+ * Interpretation of the beginning of data payload for ERT_START_NPU_PREEMPT in
+ * amdxdna_cmd. The rest of the payload in amdxdna_cmd is regular kernel args.
+ */
+struct amdxdna_cmd_preempt_data {
+ u64 inst_buf; /* instruction buffer address */
+ u64 save_buf; /* save buffer address */
+ u64 restore_buf; /* restore buffer address */
+ u32 inst_size; /* size of instruction buffer in bytes */
+ u32 save_size; /* size of save buffer in bytes */
+ u32 restore_size; /* size of restore buffer in bytes */
+ u32 inst_prop_cnt; /* properties count */
+ u32 prop_args[]; /* properties and regular kernel arguments */
+};
+
/* Exec buffer command header format */
#define AMDXDNA_CMD_STATE GENMASK(3, 0)
#define AMDXDNA_CMD_EXTRA_CU_MASK GENMASK(11, 10)
@@ -64,6 +82,8 @@ struct amdxdna_cmd {
u32 data[];
};
+#define INVALID_CU_IDX (~0U)
+
struct amdxdna_hwctx {
struct amdxdna_client *client;
struct amdxdna_hwctx_priv *priv;
@@ -95,6 +115,17 @@ struct amdxdna_hwctx {
#define drm_job_to_xdna_job(j) \
container_of(j, struct amdxdna_sched_job, base)
+enum amdxdna_job_opcode {
+ SYNC_DEBUG_BO,
+ ATTACH_DEBUG_BO,
+ DETACH_DEBUG_BO,
+};
+
+struct amdxdna_drv_cmd {
+ enum amdxdna_job_opcode opcode;
+ u32 result;
+};
+
struct amdxdna_sched_job {
struct drm_sched_job base;
struct kref refcnt;
@@ -105,7 +136,9 @@ struct amdxdna_sched_job {
/* user can wait on this fence */
struct dma_fence *out_fence;
bool job_done;
+ bool job_timeout;
u64 seq;
+ struct amdxdna_drv_cmd *drv_cmd;
struct amdxdna_gem_obj *cmd_bo;
size_t bo_cnt;
struct drm_gem_object *bos[] __counted_by(bo_cnt);
@@ -137,15 +170,17 @@ amdxdna_cmd_get_state(struct amdxdna_gem_obj *abo)
}
void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size);
-int amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo);
+u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo);
void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job);
void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
int (*walk)(struct amdxdna_hwctx *hwctx, void *arg));
+int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl);
int amdxdna_cmd_submit(struct amdxdna_client *client,
- u32 cmd_bo_hdls, u32 *arg_bo_hdls, u32 arg_bo_cnt,
+ struct amdxdna_drv_cmd *drv_cmd, u32 cmd_bo_hdls,
+ u32 *arg_bo_hdls, u32 arg_bo_cnt,
u32 hwctx_hdl, u64 *seq);
int amdxdna_cmd_wait(struct amdxdna_client *client, u32 hwctx_hdl,
diff --git a/drivers/accel/amdxdna/amdxdna_error.h b/drivers/accel/amdxdna/amdxdna_error.h
new file mode 100644
index 000000000000..c51de86ec12b
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_error.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_ERROR_H_
+#define _AMDXDNA_ERROR_H_
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+
+#define AMDXDNA_ERR_DRV_AIE 4
+#define AMDXDNA_ERR_SEV_CRITICAL 3
+#define AMDXDNA_ERR_CLASS_AIE 2
+
+#define AMDXDNA_ERR_NUM_MASK GENMASK_U64(15, 0)
+#define AMDXDNA_ERR_DRV_MASK GENMASK_U64(23, 16)
+#define AMDXDNA_ERR_SEV_MASK GENMASK_U64(31, 24)
+#define AMDXDNA_ERR_MOD_MASK GENMASK_U64(39, 32)
+#define AMDXDNA_ERR_CLASS_MASK GENMASK_U64(47, 40)
+
+enum amdxdna_error_num {
+ AMDXDNA_ERROR_NUM_AIE_SATURATION = 3,
+ AMDXDNA_ERROR_NUM_AIE_FP,
+ AMDXDNA_ERROR_NUM_AIE_STREAM,
+ AMDXDNA_ERROR_NUM_AIE_ACCESS,
+ AMDXDNA_ERROR_NUM_AIE_BUS,
+ AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
+ AMDXDNA_ERROR_NUM_AIE_ECC,
+ AMDXDNA_ERROR_NUM_AIE_LOCK,
+ AMDXDNA_ERROR_NUM_AIE_DMA,
+ AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
+ AMDXDNA_ERROR_NUM_UNKNOWN = 15,
+};
+
+enum amdxdna_error_module {
+ AMDXDNA_ERROR_MODULE_AIE_CORE = 3,
+ AMDXDNA_ERROR_MODULE_AIE_MEMORY,
+ AMDXDNA_ERROR_MODULE_AIE_SHIM,
+ AMDXDNA_ERROR_MODULE_AIE_NOC,
+ AMDXDNA_ERROR_MODULE_AIE_PL,
+ AMDXDNA_ERROR_MODULE_UNKNOWN = 8,
+};
+
+#define AMDXDNA_ERROR_ENCODE(err_num, err_mod) \
+ (FIELD_PREP(AMDXDNA_ERR_NUM_MASK, err_num) | \
+ FIELD_PREP_CONST(AMDXDNA_ERR_DRV_MASK, AMDXDNA_ERR_DRV_AIE) | \
+ FIELD_PREP_CONST(AMDXDNA_ERR_SEV_MASK, AMDXDNA_ERR_SEV_CRITICAL) | \
+ FIELD_PREP(AMDXDNA_ERR_MOD_MASK, err_mod) | \
+ FIELD_PREP_CONST(AMDXDNA_ERR_CLASS_MASK, AMDXDNA_ERR_CLASS_AIE))
+
+#define AMDXDNA_EXTRA_ERR_COL_MASK GENMASK_U64(7, 0)
+#define AMDXDNA_EXTRA_ERR_ROW_MASK GENMASK_U64(15, 8)
+
+#define AMDXDNA_EXTRA_ERR_ENCODE(row, col) \
+ (FIELD_PREP(AMDXDNA_EXTRA_ERR_COL_MASK, col) | \
+ FIELD_PREP(AMDXDNA_EXTRA_ERR_ROW_MASK, row))
+
+#endif /* _AMDXDNA_ERROR_H_ */
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
index d407a36eb412..dfa916eeb2d9 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.c
+++ b/drivers/accel/amdxdna/amdxdna_gem.c
@@ -8,6 +8,7 @@
#include <drm/drm_device.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <linux/dma-buf.h>
#include <linux/dma-direct.h>
@@ -392,35 +393,33 @@ static const struct dma_buf_ops amdxdna_dmabuf_ops = {
.vunmap = drm_gem_dmabuf_vunmap,
};
-static int amdxdna_gem_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
+static int amdxdna_gem_obj_vmap(struct amdxdna_gem_obj *abo, void **vaddr)
{
- struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
-
- iosys_map_clear(map);
-
- dma_resv_assert_held(obj->resv);
+ struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
+ int ret;
if (is_import_bo(abo))
- dma_buf_vmap(abo->dma_buf, map);
+ ret = dma_buf_vmap_unlocked(abo->dma_buf, &map);
else
- drm_gem_shmem_object_vmap(obj, map);
+ ret = drm_gem_vmap(to_gobj(abo), &map);
- if (!map->vaddr)
- return -ENOMEM;
-
- return 0;
+ *vaddr = map.vaddr;
+ return ret;
}
-static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
+static void amdxdna_gem_obj_vunmap(struct amdxdna_gem_obj *abo)
{
- struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
+ struct iosys_map map;
+
+ if (!abo->mem.kva)
+ return;
- dma_resv_assert_held(obj->resv);
+ iosys_map_set_vaddr(&map, abo->mem.kva);
if (is_import_bo(abo))
- dma_buf_vunmap(abo->dma_buf, map);
+ dma_buf_vunmap_unlocked(abo->dma_buf, &map);
else
- drm_gem_shmem_object_vunmap(obj, map);
+ drm_gem_vunmap(to_gobj(abo), &map);
}
static struct dma_buf *amdxdna_gem_prime_export(struct drm_gem_object *gobj, int flags)
@@ -455,7 +454,6 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
{
struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev);
struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
- struct iosys_map map = IOSYS_MAP_INIT_VADDR(abo->mem.kva);
XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, abo->mem.dev_addr);
@@ -468,7 +466,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
if (abo->type == AMDXDNA_BO_DEV_HEAP)
drm_mm_takedown(&abo->mm);
- drm_gem_vunmap(gobj, &map);
+ amdxdna_gem_obj_vunmap(abo);
mutex_destroy(&abo->lock);
if (is_import_bo(abo)) {
@@ -489,8 +487,8 @@ static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
.pin = drm_gem_shmem_object_pin,
.unpin = drm_gem_shmem_object_unpin,
.get_sg_table = drm_gem_shmem_object_get_sg_table,
- .vmap = amdxdna_gem_obj_vmap,
- .vunmap = amdxdna_gem_obj_vunmap,
+ .vmap = drm_gem_shmem_object_vmap,
+ .vunmap = drm_gem_shmem_object_vunmap,
.mmap = amdxdna_gem_obj_mmap,
.vm_ops = &drm_gem_shmem_vm_ops,
.export = amdxdna_gem_prime_export,
@@ -663,7 +661,6 @@ amdxdna_drm_create_dev_heap(struct drm_device *dev,
struct drm_file *filp)
{
struct amdxdna_client *client = filp->driver_priv;
- struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
struct amdxdna_dev *xdna = to_xdna_dev(dev);
struct amdxdna_gem_obj *abo;
int ret;
@@ -692,12 +689,11 @@ amdxdna_drm_create_dev_heap(struct drm_device *dev,
abo->mem.dev_addr = client->xdna->dev_info->dev_mem_base;
drm_mm_init(&abo->mm, abo->mem.dev_addr, abo->mem.size);
- ret = drm_gem_vmap(to_gobj(abo), &map);
+ ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva);
if (ret) {
XDNA_ERR(xdna, "Vmap heap bo failed, ret %d", ret);
goto release_obj;
}
- abo->mem.kva = map.vaddr;
client->dev_heap = abo;
drm_gem_object_get(to_gobj(abo));
@@ -748,7 +744,6 @@ amdxdna_drm_create_cmd_bo(struct drm_device *dev,
struct amdxdna_drm_create_bo *args,
struct drm_file *filp)
{
- struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
struct amdxdna_dev *xdna = to_xdna_dev(dev);
struct amdxdna_gem_obj *abo;
int ret;
@@ -770,12 +765,11 @@ amdxdna_drm_create_cmd_bo(struct drm_device *dev,
abo->type = AMDXDNA_BO_CMD;
abo->client = filp->driver_priv;
- ret = drm_gem_vmap(to_gobj(abo), &map);
+ ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva);
if (ret) {
XDNA_ERR(xdna, "Vmap cmd bo failed, ret %d", ret);
goto release_obj;
}
- abo->mem.kva = map.vaddr;
return abo;
@@ -969,6 +963,9 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
args->handle, args->offset, args->size);
+ if (args->direction == SYNC_DIRECT_FROM_DEVICE)
+ ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle);
+
put_obj:
drm_gem_object_put(gobj);
return ret;
diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h
index ae29db94a9d3..f79fc7f3c93b 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.h
+++ b/drivers/accel/amdxdna/amdxdna_gem.h
@@ -7,6 +7,7 @@
#define _AMDXDNA_GEM_H_
#include <linux/hmm.h>
+#include "amdxdna_pci_drv.h"
struct amdxdna_umap {
struct vm_area_struct *vma;
@@ -62,6 +63,11 @@ static inline void amdxdna_gem_put_obj(struct amdxdna_gem_obj *abo)
drm_gem_object_put(to_gobj(abo));
}
+static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo)
+{
+ return abo->mem.dev_addr - abo->client->dev_heap->mem.dev_addr;
+}
+
void amdxdna_umap_put(struct amdxdna_umap *mapp);
struct drm_gem_object *
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c
index da1ac89bb78f..858df97cd3fb 100644
--- a/drivers/accel/amdxdna/amdxdna_mailbox.c
+++ b/drivers/accel/amdxdna/amdxdna_mailbox.c
@@ -194,7 +194,8 @@ static void mailbox_release_msg(struct mailbox_channel *mb_chann,
{
MB_DBG(mb_chann, "msg_id 0x%x msg opcode 0x%x",
mb_msg->pkg.header.id, mb_msg->pkg.header.opcode);
- mb_msg->notify_cb(mb_msg->handle, NULL, 0);
+ if (mb_msg->notify_cb)
+ mb_msg->notify_cb(mb_msg->handle, NULL, 0);
kfree(mb_msg);
}
@@ -248,7 +249,7 @@ mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *heade
{
struct mailbox_msg *mb_msg;
int msg_id;
- int ret;
+ int ret = 0;
msg_id = header->id;
if (!mailbox_validate_msgid(msg_id)) {
@@ -265,9 +266,11 @@ mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *heade
MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x",
header->opcode, header->total_size, header->id);
- ret = mb_msg->notify_cb(mb_msg->handle, data, header->total_size);
- if (unlikely(ret))
- MB_ERR(mb_chann, "Message callback ret %d", ret);
+ if (mb_msg->notify_cb) {
+ ret = mb_msg->notify_cb(mb_msg->handle, data, header->total_size);
+ if (unlikely(ret))
+ MB_ERR(mb_chann, "Message callback ret %d", ret);
+ }
kfree(mb_msg);
return ret;
@@ -513,6 +516,7 @@ xdna_mailbox_create_channel(struct mailbox *mb,
}
mb_chann->bad_state = false;
+ mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
MB_DBG(mb_chann, "Mailbox channel created (irq: %d)", mb_chann->msix_irq);
return mb_chann;
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox_helper.h b/drivers/accel/amdxdna/amdxdna_mailbox_helper.h
index 710ff8873d61..556c712cad0a 100644
--- a/drivers/accel/amdxdna/amdxdna_mailbox_helper.h
+++ b/drivers/accel/amdxdna/amdxdna_mailbox_helper.h
@@ -16,16 +16,18 @@ struct xdna_notify {
u32 *data;
size_t size;
int error;
+ u32 *status;
};
-#define DECLARE_XDNA_MSG_COMMON(name, op, status) \
+#define DECLARE_XDNA_MSG_COMMON(name, op, s) \
struct name##_req req = { 0 }; \
- struct name##_resp resp = { status }; \
+ struct name##_resp resp = { .status = s }; \
struct xdna_notify hdl = { \
.error = 0, \
.data = (u32 *)&resp, \
.size = sizeof(resp), \
.comp = COMPLETION_INITIALIZER_ONSTACK(hdl.comp), \
+ .status = (u32 *)&resp.status, \
}; \
struct xdna_mailbox_msg msg = { \
.send_data = (u8 *)&req, \
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c
index 569cd703729d..1973ab67721b 100644
--- a/drivers/accel/amdxdna/amdxdna_pci_drv.c
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c
@@ -13,13 +13,11 @@
#include <drm/gpu_scheduler.h>
#include <linux/iommu.h>
#include <linux/pci.h>
-#include <linux/pm_runtime.h>
#include "amdxdna_ctx.h"
#include "amdxdna_gem.h"
#include "amdxdna_pci_drv.h"
-
-#define AMDXDNA_AUTOSUSPEND_DELAY 5000 /* milliseconds */
+#include "amdxdna_pm.h"
MODULE_FIRMWARE("amdnpu/1502_00/npu.sbin");
MODULE_FIRMWARE("amdnpu/17f0_10/npu.sbin");
@@ -29,9 +27,14 @@ MODULE_FIRMWARE("amdnpu/17f0_20/npu.sbin");
/*
* 0.0: Initial version
* 0.1: Support getting all hardware contexts by DRM_IOCTL_AMDXDNA_GET_ARRAY
+ * 0.2: Support getting last error hardware error
+ * 0.3: Support firmware debug buffer
+ * 0.4: Support getting resource information
+ * 0.5: Support getting telemetry data
+ * 0.6: Support preemption
*/
#define AMDXDNA_DRIVER_MAJOR 0
-#define AMDXDNA_DRIVER_MINOR 1
+#define AMDXDNA_DRIVER_MINOR 6
/*
* Bind the driver base on (vendor_id, device_id) pair and later use the
@@ -61,17 +64,9 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
struct amdxdna_client *client;
int ret;
- ret = pm_runtime_resume_and_get(ddev->dev);
- if (ret) {
- XDNA_ERR(xdna, "Failed to get rpm, ret %d", ret);
- return ret;
- }
-
client = kzalloc(sizeof(*client), GFP_KERNEL);
- if (!client) {
- ret = -ENOMEM;
- goto put_rpm;
- }
+ if (!client)
+ return -ENOMEM;
client->pid = pid_nr(rcu_access_pointer(filp->pid));
client->xdna = xdna;
@@ -106,9 +101,6 @@ unbind_sva:
iommu_sva_unbind_device(client->sva);
failed:
kfree(client);
-put_rpm:
- pm_runtime_mark_last_busy(ddev->dev);
- pm_runtime_put_autosuspend(ddev->dev);
return ret;
}
@@ -130,8 +122,6 @@ static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp)
XDNA_DBG(xdna, "pid %d closed", client->pid);
kfree(client);
- pm_runtime_mark_last_busy(ddev->dev);
- pm_runtime_put_autosuspend(ddev->dev);
}
static int amdxdna_flush(struct file *f, fl_owner_t id)
@@ -310,19 +300,12 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto failed_dev_fini;
}
- pm_runtime_set_autosuspend_delay(dev, AMDXDNA_AUTOSUSPEND_DELAY);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_allow(dev);
-
ret = drm_dev_register(&xdna->ddev, 0);
if (ret) {
XDNA_ERR(xdna, "DRM register failed, ret %d", ret);
- pm_runtime_forbid(dev);
goto failed_sysfs_fini;
}
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
return 0;
failed_sysfs_fini:
@@ -339,14 +322,10 @@ destroy_notifier_wq:
static void amdxdna_remove(struct pci_dev *pdev)
{
struct amdxdna_dev *xdna = pci_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
struct amdxdna_client *client;
destroy_workqueue(xdna->notifier_wq);
- pm_runtime_get_noresume(dev);
- pm_runtime_forbid(dev);
-
drm_dev_unplug(&xdna->ddev);
amdxdna_sysfs_fini(xdna);
@@ -365,29 +344,9 @@ static void amdxdna_remove(struct pci_dev *pdev)
mutex_unlock(&xdna->dev_lock);
}
-static int amdxdna_pmops_suspend(struct device *dev)
-{
- struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
-
- if (!xdna->dev_info->ops->suspend)
- return -EOPNOTSUPP;
-
- return xdna->dev_info->ops->suspend(xdna);
-}
-
-static int amdxdna_pmops_resume(struct device *dev)
-{
- struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
-
- if (!xdna->dev_info->ops->resume)
- return -EOPNOTSUPP;
-
- return xdna->dev_info->ops->resume(xdna);
-}
-
static const struct dev_pm_ops amdxdna_pm_ops = {
- SYSTEM_SLEEP_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume)
- RUNTIME_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(amdxdna_pm_suspend, amdxdna_pm_resume)
+ RUNTIME_PM_OPS(amdxdna_pm_suspend, amdxdna_pm_resume, NULL)
};
static struct pci_driver amdxdna_pci_driver = {
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h
index 72d6696d49da..c99477f5e454 100644
--- a/drivers/accel/amdxdna/amdxdna_pci_drv.h
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h
@@ -6,6 +6,7 @@
#ifndef _AMDXDNA_PCI_DRV_H_
#define _AMDXDNA_PCI_DRV_H_
+#include <drm/drm_print.h>
#include <linux/workqueue.h>
#include <linux/xarray.h>
@@ -54,6 +55,7 @@ struct amdxdna_dev_ops {
int (*hwctx_init)(struct amdxdna_hwctx *hwctx);
void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
+ int (*hwctx_sync_debug_bo)(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args);
@@ -99,6 +101,7 @@ struct amdxdna_dev {
struct amdxdna_fw_ver fw_ver;
struct rw_semaphore notifier_lock; /* for mmu notifier*/
struct workqueue_struct *notifier_wq;
+ bool rpm_on;
};
/*
diff --git a/drivers/accel/amdxdna/amdxdna_pm.c b/drivers/accel/amdxdna/amdxdna_pm.c
new file mode 100644
index 000000000000..fa38e65d617c
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_pm.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_drv.h>
+#include <linux/pm_runtime.h>
+
+#include "amdxdna_pm.h"
+
+#define AMDXDNA_AUTOSUSPEND_DELAY 5000 /* milliseconds */
+
+int amdxdna_pm_suspend(struct device *dev)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev));
+ int ret = -EOPNOTSUPP;
+ bool rpm;
+
+ if (xdna->dev_info->ops->suspend) {
+ rpm = xdna->rpm_on;
+ xdna->rpm_on = false;
+ ret = xdna->dev_info->ops->suspend(xdna);
+ xdna->rpm_on = rpm;
+ }
+
+ XDNA_DBG(xdna, "Suspend done ret %d", ret);
+ return ret;
+}
+
+int amdxdna_pm_resume(struct device *dev)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev));
+ int ret = -EOPNOTSUPP;
+ bool rpm;
+
+ if (xdna->dev_info->ops->resume) {
+ rpm = xdna->rpm_on;
+ xdna->rpm_on = false;
+ ret = xdna->dev_info->ops->resume(xdna);
+ xdna->rpm_on = rpm;
+ }
+
+ XDNA_DBG(xdna, "Resume done ret %d", ret);
+ return ret;
+}
+
+int amdxdna_pm_resume_get(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+ int ret;
+
+ if (!xdna->rpm_on)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ XDNA_ERR(xdna, "Resume failed: %d", ret);
+ pm_runtime_set_suspended(dev);
+ }
+
+ return ret;
+}
+
+void amdxdna_pm_suspend_put(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+
+ if (!xdna->rpm_on)
+ return;
+
+ pm_runtime_put_autosuspend(dev);
+}
+
+void amdxdna_pm_init(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, AMDXDNA_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_allow(dev);
+ pm_runtime_put_autosuspend(dev);
+ xdna->rpm_on = true;
+}
+
+void amdxdna_pm_fini(struct amdxdna_dev *xdna)
+{
+ struct device *dev = xdna->ddev.dev;
+
+ xdna->rpm_on = false;
+ pm_runtime_get_noresume(dev);
+ pm_runtime_forbid(dev);
+}
diff --git a/drivers/accel/amdxdna/amdxdna_pm.h b/drivers/accel/amdxdna/amdxdna_pm.h
new file mode 100644
index 000000000000..77b2d6e45570
--- /dev/null
+++ b/drivers/accel/amdxdna/amdxdna_pm.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2025, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AMDXDNA_PM_H_
+#define _AMDXDNA_PM_H_
+
+#include "amdxdna_pci_drv.h"
+
+int amdxdna_pm_suspend(struct device *dev);
+int amdxdna_pm_resume(struct device *dev);
+int amdxdna_pm_resume_get(struct amdxdna_dev *xdna);
+void amdxdna_pm_suspend_put(struct amdxdna_dev *xdna);
+void amdxdna_pm_init(struct amdxdna_dev *xdna);
+void amdxdna_pm_fini(struct amdxdna_dev *xdna);
+
+#endif /* _AMDXDNA_PM_H_ */
diff --git a/drivers/accel/amdxdna/npu1_regs.c b/drivers/accel/amdxdna/npu1_regs.c
index e4f6dac7d00f..ec407f3b48fc 100644
--- a/drivers/accel/amdxdna/npu1_regs.c
+++ b/drivers/accel/amdxdna/npu1_regs.c
@@ -46,6 +46,7 @@
const struct rt_config npu1_default_rt_cfg[] = {
{ 2, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
+ { 4, 1, AIE2_RT_CFG_INIT }, /* Debug BO */
{ 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
{ 0 },
};
@@ -62,16 +63,23 @@ const struct dpm_clk_freq npu1_dpm_clk_table[] = {
{ 0 }
};
+static const struct aie2_fw_feature_tbl npu1_fw_feature_table[] = {
+ { .feature = AIE2_NPU_COMMAND, .min_minor = 8 },
+ { 0 }
+};
+
static const struct amdxdna_dev_priv npu1_dev_priv = {
.fw_path = "amdnpu/1502_00/npu.sbin",
.protocol_major = 0x5,
.protocol_minor = 0x7,
.rt_config = npu1_default_rt_cfg,
.dpm_clk_tbl = npu1_dpm_clk_table,
+ .fw_feature_tbl = npu1_fw_feature_table,
.col_align = COL_ALIGN_NONE,
.mbox_dev_addr = NPU1_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU1_SRAM_BAR_BASE,
+ .hwctx_limit = 6,
.sram_offs = {
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU1_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU1_SRAM, MPNPU_SRAM_I2X_MAILBOX_15),
diff --git a/drivers/accel/amdxdna/npu2_regs.c b/drivers/accel/amdxdna/npu2_regs.c
index a081cac75ee0..86f87d0d1354 100644
--- a/drivers/accel/amdxdna/npu2_regs.c
+++ b/drivers/accel/amdxdna/npu2_regs.c
@@ -67,10 +67,12 @@ static const struct amdxdna_dev_priv npu2_dev_priv = {
.protocol_minor = 0x6,
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
.col_align = COL_ALIGN_NATURE,
.mbox_dev_addr = NPU2_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU2_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
.sram_offs = {
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
diff --git a/drivers/accel/amdxdna/npu4_regs.c b/drivers/accel/amdxdna/npu4_regs.c
index 9f2e33182ec6..986a5f28ba24 100644
--- a/drivers/accel/amdxdna/npu4_regs.c
+++ b/drivers/accel/amdxdna/npu4_regs.c
@@ -63,10 +63,14 @@
const struct rt_config npu4_default_rt_cfg[] = {
{ 5, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
+ { 10, 1, AIE2_RT_CFG_INIT }, /* DEBUG BUF */
+ { 14, 0, AIE2_RT_CFG_INIT, BIT_U64(AIE2_PREEMPT) }, /* Frame boundary preemption */
{ 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
{ 2, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
{ 3, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
{ 4, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
+ { 13, 0, AIE2_RT_CFG_FORCE_PREEMPT },
+ { 14, 0, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT },
{ 0 },
};
@@ -82,16 +86,24 @@ const struct dpm_clk_freq npu4_dpm_clk_table[] = {
{ 0 }
};
+const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = {
+ { .feature = AIE2_NPU_COMMAND, .min_minor = 15 },
+ { .feature = AIE2_PREEMPT, .min_minor = 12 },
+ { 0 }
+};
+
static const struct amdxdna_dev_priv npu4_dev_priv = {
.fw_path = "amdnpu/17f0_10/npu.sbin",
.protocol_major = 0x6,
.protocol_minor = 12,
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
.col_align = COL_ALIGN_NATURE,
.mbox_dev_addr = NPU4_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU4_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
.sram_offs = {
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU4_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU4_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
diff --git a/drivers/accel/amdxdna/npu5_regs.c b/drivers/accel/amdxdna/npu5_regs.c
index 5f1cf83461c4..75ad97f0b937 100644
--- a/drivers/accel/amdxdna/npu5_regs.c
+++ b/drivers/accel/amdxdna/npu5_regs.c
@@ -67,10 +67,12 @@ static const struct amdxdna_dev_priv npu5_dev_priv = {
.protocol_minor = 12,
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
.col_align = COL_ALIGN_NATURE,
.mbox_dev_addr = NPU5_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU5_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
.sram_offs = {
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU5_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU5_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
diff --git a/drivers/accel/amdxdna/npu6_regs.c b/drivers/accel/amdxdna/npu6_regs.c
index 94a7005685a7..758dc013fe13 100644
--- a/drivers/accel/amdxdna/npu6_regs.c
+++ b/drivers/accel/amdxdna/npu6_regs.c
@@ -67,10 +67,12 @@ static const struct amdxdna_dev_priv npu6_dev_priv = {
.protocol_minor = 12,
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
+ .fw_feature_tbl = npu4_fw_feature_table,
.col_align = COL_ALIGN_NATURE,
.mbox_dev_addr = NPU6_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU6_SRAM_BAR_BASE,
+ .hwctx_limit = 16,
.sram_offs = {
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
diff --git a/drivers/accel/ethosu/Kconfig b/drivers/accel/ethosu/Kconfig
new file mode 100644
index 000000000000..d25f9b3eb317
--- /dev/null
+++ b/drivers/accel/ethosu/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_ACCEL_ARM_ETHOSU
+ tristate "Arm Ethos-U65/U85 NPU"
+ depends on HAS_IOMEM
+ depends on DRM_ACCEL
+ select DRM_GEM_DMA_HELPER
+ select DRM_SCHED
+ select GENERIC_ALLOCATOR
+ help
+ Enables driver for Arm Ethos-U65/U85 NPUs
diff --git a/drivers/accel/ethosu/Makefile b/drivers/accel/ethosu/Makefile
new file mode 100644
index 000000000000..17db5a600416
--- /dev/null
+++ b/drivers/accel/ethosu/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o
+ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o
diff --git a/drivers/accel/ethosu/ethosu_device.h b/drivers/accel/ethosu/ethosu_device.h
new file mode 100644
index 000000000000..b189fa783d6a
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_device.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0-only or MIT */
+/* Copyright 2025 Arm, Ltd. */
+
+#ifndef __ETHOSU_DEVICE_H__
+#define __ETHOSU_DEVICE_H__
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/types.h>
+
+#include <drm/drm_device.h>
+#include <drm/gpu_scheduler.h>
+
+#include <drm/ethosu_accel.h>
+
+struct clk;
+struct gen_pool;
+
+#define NPU_REG_ID 0x0000
+#define NPU_REG_STATUS 0x0004
+#define NPU_REG_CMD 0x0008
+#define NPU_REG_RESET 0x000c
+#define NPU_REG_QBASE 0x0010
+#define NPU_REG_QBASE_HI 0x0014
+#define NPU_REG_QREAD 0x0018
+#define NPU_REG_QCONFIG 0x001c
+#define NPU_REG_QSIZE 0x0020
+#define NPU_REG_PROT 0x0024
+#define NPU_REG_CONFIG 0x0028
+#define NPU_REG_REGIONCFG 0x003c
+#define NPU_REG_AXILIMIT0 0x0040 // U65
+#define NPU_REG_AXILIMIT1 0x0044 // U65
+#define NPU_REG_AXILIMIT2 0x0048 // U65
+#define NPU_REG_AXILIMIT3 0x004c // U65
+#define NPU_REG_MEM_ATTR0 0x0040 // U85
+#define NPU_REG_MEM_ATTR1 0x0044 // U85
+#define NPU_REG_MEM_ATTR2 0x0048 // U85
+#define NPU_REG_MEM_ATTR3 0x004c // U85
+#define NPU_REG_AXI_SRAM 0x0050 // U85
+#define NPU_REG_AXI_EXT 0x0054 // U85
+
+#define NPU_REG_BASEP(x) (0x0080 + (x) * 8)
+#define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8)
+#define NPU_BASEP_REGION_MAX 8
+
+#define ID_ARCH_MAJOR_MASK GENMASK(31, 28)
+#define ID_ARCH_MINOR_MASK GENMASK(27, 20)
+#define ID_ARCH_PATCH_MASK GENMASK(19, 16)
+#define ID_VER_MAJOR_MASK GENMASK(11, 8)
+#define ID_VER_MINOR_MASK GENMASK(7, 4)
+
+#define CONFIG_MACS_PER_CC_MASK GENMASK(3, 0)
+#define CONFIG_CMD_STREAM_VER_MASK GENMASK(7, 4)
+
+#define STATUS_STATE_RUNNING BIT(0)
+#define STATUS_IRQ_RAISED BIT(1)
+#define STATUS_BUS_STATUS BIT(2)
+#define STATUS_RESET_STATUS BIT(3)
+#define STATUS_CMD_PARSE_ERR BIT(4)
+#define STATUS_CMD_END_REACHED BIT(5)
+
+#define CMD_CLEAR_IRQ BIT(1)
+#define CMD_TRANSITION_TO_RUN BIT(0)
+
+#define RESET_PENDING_CSL BIT(1)
+#define RESET_PENDING_CPL BIT(0)
+
+#define PROT_ACTIVE_CSL BIT(1)
+
+enum ethosu_cmds {
+ NPU_OP_CONV = 0x2,
+ NPU_OP_DEPTHWISE = 0x3,
+ NPU_OP_POOL = 0x5,
+ NPU_OP_ELEMENTWISE = 0x6,
+ NPU_OP_RESIZE = 0x7, // U85 only
+ NPU_OP_DMA_START = 0x10,
+ NPU_SET_IFM_PAD_TOP = 0x100,
+ NPU_SET_IFM_PAD_LEFT = 0x101,
+ NPU_SET_IFM_PAD_RIGHT = 0x102,
+ NPU_SET_IFM_PAD_BOTTOM = 0x103,
+ NPU_SET_IFM_DEPTH_M1 = 0x104,
+ NPU_SET_IFM_PRECISION = 0x105,
+ NPU_SET_IFM_BROADCAST = 0x108,
+ NPU_SET_IFM_WIDTH0_M1 = 0x10a,
+ NPU_SET_IFM_HEIGHT0_M1 = 0x10b,
+ NPU_SET_IFM_HEIGHT1_M1 = 0x10c,
+ NPU_SET_IFM_REGION = 0x10f,
+ NPU_SET_OFM_WIDTH_M1 = 0x111,
+ NPU_SET_OFM_HEIGHT_M1 = 0x112,
+ NPU_SET_OFM_DEPTH_M1 = 0x113,
+ NPU_SET_OFM_PRECISION = 0x114,
+ NPU_SET_OFM_WIDTH0_M1 = 0x11a,
+ NPU_SET_OFM_HEIGHT0_M1 = 0x11b,
+ NPU_SET_OFM_HEIGHT1_M1 = 0x11c,
+ NPU_SET_OFM_REGION = 0x11f,
+ NPU_SET_KERNEL_WIDTH_M1 = 0x120,
+ NPU_SET_KERNEL_HEIGHT_M1 = 0x121,
+ NPU_SET_KERNEL_STRIDE = 0x122,
+ NPU_SET_WEIGHT_REGION = 0x128,
+ NPU_SET_SCALE_REGION = 0x129,
+ NPU_SET_DMA0_SRC_REGION = 0x130,
+ NPU_SET_DMA0_DST_REGION = 0x131,
+ NPU_SET_DMA0_SIZE0 = 0x132,
+ NPU_SET_DMA0_SIZE1 = 0x133,
+ NPU_SET_IFM2_BROADCAST = 0x180,
+ NPU_SET_IFM2_PRECISION = 0x185,
+ NPU_SET_IFM2_WIDTH0_M1 = 0x18a,
+ NPU_SET_IFM2_HEIGHT0_M1 = 0x18b,
+ NPU_SET_IFM2_HEIGHT1_M1 = 0x18c,
+ NPU_SET_IFM2_REGION = 0x18f,
+ NPU_SET_IFM_BASE0 = 0x4000,
+ NPU_SET_IFM_BASE1 = 0x4001,
+ NPU_SET_IFM_BASE2 = 0x4002,
+ NPU_SET_IFM_BASE3 = 0x4003,
+ NPU_SET_IFM_STRIDE_X = 0x4004,
+ NPU_SET_IFM_STRIDE_Y = 0x4005,
+ NPU_SET_IFM_STRIDE_C = 0x4006,
+ NPU_SET_OFM_BASE0 = 0x4010,
+ NPU_SET_OFM_BASE1 = 0x4011,
+ NPU_SET_OFM_BASE2 = 0x4012,
+ NPU_SET_OFM_BASE3 = 0x4013,
+ NPU_SET_OFM_STRIDE_X = 0x4014,
+ NPU_SET_OFM_STRIDE_Y = 0x4015,
+ NPU_SET_OFM_STRIDE_C = 0x4016,
+ NPU_SET_WEIGHT_BASE = 0x4020,
+ NPU_SET_WEIGHT_LENGTH = 0x4021,
+ NPU_SET_SCALE_BASE = 0x4022,
+ NPU_SET_SCALE_LENGTH = 0x4023,
+ NPU_SET_DMA0_SRC = 0x4030,
+ NPU_SET_DMA0_DST = 0x4031,
+ NPU_SET_DMA0_LEN = 0x4032,
+ NPU_SET_DMA0_SRC_STRIDE0 = 0x4033,
+ NPU_SET_DMA0_SRC_STRIDE1 = 0x4034,
+ NPU_SET_DMA0_DST_STRIDE0 = 0x4035,
+ NPU_SET_DMA0_DST_STRIDE1 = 0x4036,
+ NPU_SET_IFM2_BASE0 = 0x4080,
+ NPU_SET_IFM2_BASE1 = 0x4081,
+ NPU_SET_IFM2_BASE2 = 0x4082,
+ NPU_SET_IFM2_BASE3 = 0x4083,
+ NPU_SET_IFM2_STRIDE_X = 0x4084,
+ NPU_SET_IFM2_STRIDE_Y = 0x4085,
+ NPU_SET_IFM2_STRIDE_C = 0x4086,
+ NPU_SET_WEIGHT1_BASE = 0x4090,
+ NPU_SET_WEIGHT1_LENGTH = 0x4091,
+ NPU_SET_SCALE1_BASE = 0x4092,
+ NPU_SET_WEIGHT2_BASE = 0x4092,
+ NPU_SET_SCALE1_LENGTH = 0x4093,
+ NPU_SET_WEIGHT2_LENGTH = 0x4093,
+ NPU_SET_WEIGHT3_BASE = 0x4094,
+ NPU_SET_WEIGHT3_LENGTH = 0x4095,
+};
+
+#define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */
+
+/**
+ * struct ethosu_device - Ethosu device
+ */
+struct ethosu_device {
+ /** @base: Base drm_device. */
+ struct drm_device base;
+
+ /** @iomem: CPU mapping of the registers. */
+ void __iomem *regs;
+
+ void __iomem *sram;
+ struct gen_pool *srampool;
+ dma_addr_t sramphys;
+
+ struct clk_bulk_data *clks;
+ int num_clks;
+ int irq;
+
+ struct drm_ethosu_npu_info npu_info;
+
+ struct ethosu_job *in_flight_job;
+ /* For in_flight_job and ethosu_job_hw_submit() */
+ struct mutex job_lock;
+
+ /* For dma_fence */
+ spinlock_t fence_lock;
+
+ struct drm_gpu_scheduler sched;
+ /* For ethosu_job_do_push() */
+ struct mutex sched_lock;
+ u64 fence_context;
+ u64 emit_seqno;
+};
+
+#define to_ethosu_device(drm_dev) \
+ ((struct ethosu_device *)container_of(drm_dev, struct ethosu_device, base))
+
+static inline bool ethosu_is_u65(const struct ethosu_device *ethosudev)
+{
+ return FIELD_GET(ID_ARCH_MAJOR_MASK, ethosudev->npu_info.id) == 1;
+}
+
+#endif
diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c
new file mode 100644
index 000000000000..e05a69bf5574
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_drv.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-only or MIT
+// Copyright (C) 2025 Arm, Ltd.
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_utils.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_accel.h>
+#include <drm/ethosu_accel.h>
+
+#include "ethosu_drv.h"
+#include "ethosu_device.h"
+#include "ethosu_gem.h"
+#include "ethosu_job.h"
+
+static int ethosu_ioctl_dev_query(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct ethosu_device *ethosudev = to_ethosu_device(ddev);
+ struct drm_ethosu_dev_query *args = data;
+
+ if (!args->pointer) {
+ switch (args->type) {
+ case DRM_ETHOSU_DEV_QUERY_NPU_INFO:
+ args->size = sizeof(ethosudev->npu_info);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ switch (args->type) {
+ case DRM_ETHOSU_DEV_QUERY_NPU_INFO:
+ if (args->size < offsetofend(struct drm_ethosu_npu_info, sram_size))
+ return -EINVAL;
+ return copy_struct_to_user(u64_to_user_ptr(args->pointer),
+ args->size,
+ &ethosudev->npu_info,
+ sizeof(ethosudev->npu_info), NULL);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define ETHOSU_BO_FLAGS DRM_ETHOSU_BO_NO_MMAP
+
+static int ethosu_ioctl_bo_create(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_bo_create *args = data;
+ int cookie, ret;
+
+ if (!drm_dev_enter(ddev, &cookie))
+ return -ENODEV;
+
+ if (!args->size || (args->flags & ~ETHOSU_BO_FLAGS)) {
+ ret = -EINVAL;
+ goto out_dev_exit;
+ }
+
+ ret = ethosu_gem_create_with_handle(file, ddev, &args->size,
+ args->flags, &args->handle);
+
+out_dev_exit:
+ drm_dev_exit(cookie);
+ return ret;
+}
+
+static int ethosu_ioctl_bo_wait(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_bo_wait *args = data;
+ int cookie, ret;
+ unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);
+
+ if (args->pad)
+ return -EINVAL;
+
+ if (!drm_dev_enter(ddev, &cookie))
+ return -ENODEV;
+
+ ret = drm_gem_dma_resv_wait(file, args->handle, true, timeout);
+
+ drm_dev_exit(cookie);
+ return ret;
+}
+
+static int ethosu_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_bo_mmap_offset *args = data;
+ struct drm_gem_object *obj;
+
+ if (args->pad)
+ return -EINVAL;
+
+ obj = drm_gem_object_lookup(file, args->handle);
+ if (!obj)
+ return -ENOENT;
+
+ args->offset = drm_vma_node_offset_addr(&obj->vma_node);
+ drm_gem_object_put(obj);
+ return 0;
+}
+
+static int ethosu_ioctl_cmdstream_bo_create(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ struct drm_ethosu_cmdstream_bo_create *args = data;
+ int cookie, ret;
+
+ if (!drm_dev_enter(ddev, &cookie))
+ return -ENODEV;
+
+ if (!args->size || !args->data || args->pad || args->flags) {
+ ret = -EINVAL;
+ goto out_dev_exit;
+ }
+
+ args->flags |= DRM_ETHOSU_BO_NO_MMAP;
+
+ ret = ethosu_gem_cmdstream_create(file, ddev, args->size, args->data,
+ args->flags, &args->handle);
+
+out_dev_exit:
+ drm_dev_exit(cookie);
+ return ret;
+}
+
+static int ethosu_open(struct drm_device *ddev, struct drm_file *file)
+{
+ int ret = 0;
+
+ if (!try_module_get(THIS_MODULE))
+ return -EINVAL;
+
+ struct ethosu_file_priv __free(kfree) *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err_put_mod;
+ }
+ priv->edev = to_ethosu_device(ddev);
+
+ ret = ethosu_job_open(priv);
+ if (ret)
+ goto err_put_mod;
+
+ file->driver_priv = no_free_ptr(priv);
+ return 0;
+
+err_put_mod:
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file)
+{
+ ethosu_job_close(file->driver_priv);
+ kfree(file->driver_priv);
+ module_put(THIS_MODULE);
+}
+
+static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = {
+#define ETHOSU_IOCTL(n, func, flags) \
+ DRM_IOCTL_DEF_DRV(ETHOSU_##n, ethosu_ioctl_##func, flags)
+
+ ETHOSU_IOCTL(DEV_QUERY, dev_query, 0),
+ ETHOSU_IOCTL(BO_CREATE, bo_create, 0),
+ ETHOSU_IOCTL(BO_WAIT, bo_wait, 0),
+ ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0),
+ ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0),
+ ETHOSU_IOCTL(SUBMIT, submit, 0),
+};
+
+DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops);
+
+/*
+ * Ethosu driver version:
+ * - 1.0 - initial interface
+ */
+static const struct drm_driver ethosu_drm_driver = {
+ .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
+ .open = ethosu_open,
+ .postclose = ethosu_postclose,
+ .ioctls = ethosu_drm_driver_ioctls,
+ .num_ioctls = ARRAY_SIZE(ethosu_drm_driver_ioctls),
+ .fops = &ethosu_drm_driver_fops,
+ .name = "ethosu",
+ .desc = "Arm Ethos-U Accel driver",
+ .major = 1,
+ .minor = 0,
+
+ .gem_create_object = ethosu_gem_create_object,
+};
+
+#define U65_DRAM_AXI_LIMIT_CFG 0x1f3f0002
+#define U65_SRAM_AXI_LIMIT_CFG 0x1f3f00b0
+#define U85_AXI_EXT_CFG 0x00021f3f
+#define U85_AXI_SRAM_CFG 0x00021f3f
+#define U85_MEM_ATTR0_CFG 0x00000000
+#define U85_MEM_ATTR2_CFG 0x000000b7
+
+static int ethosu_reset(struct ethosu_device *ethosudev)
+{
+ int ret;
+ u32 reg;
+
+ writel_relaxed(RESET_PENDING_CSL, ethosudev->regs + NPU_REG_RESET);
+ ret = readl_poll_timeout(ethosudev->regs + NPU_REG_STATUS, reg,
+ !FIELD_GET(STATUS_RESET_STATUS, reg),
+ USEC_PER_MSEC, USEC_PER_SEC);
+ if (ret)
+ return ret;
+
+ if (!FIELD_GET(PROT_ACTIVE_CSL, readl_relaxed(ethosudev->regs + NPU_REG_PROT))) {
+ dev_warn(ethosudev->base.dev, "Could not reset to non-secure mode (PROT = %x)\n",
+ readl_relaxed(ethosudev->regs + NPU_REG_PROT));
+ }
+
+ /*
+ * Assign region 2 (SRAM) to AXI M0 (AXILIMIT0),
+ * everything else to AXI M1 (AXILIMIT2)
+ */
+ writel_relaxed(0x0000aa8a, ethosudev->regs + NPU_REG_REGIONCFG);
+ if (ethosu_is_u65(ethosudev)) {
+ writel_relaxed(U65_SRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT0);
+ writel_relaxed(U65_DRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT2);
+ } else {
+ writel_relaxed(U85_AXI_SRAM_CFG, ethosudev->regs + NPU_REG_AXI_SRAM);
+ writel_relaxed(U85_AXI_EXT_CFG, ethosudev->regs + NPU_REG_AXI_EXT);
+ writel_relaxed(U85_MEM_ATTR0_CFG, ethosudev->regs + NPU_REG_MEM_ATTR0); // SRAM
+ writel_relaxed(U85_MEM_ATTR2_CFG, ethosudev->regs + NPU_REG_MEM_ATTR2); // DRAM
+ }
+
+ if (ethosudev->sram)
+ memset_io(ethosudev->sram, 0, ethosudev->npu_info.sram_size);
+
+ return 0;
+}
+
+static int ethosu_device_resume(struct device *dev)
+{
+ struct ethosu_device *ethosudev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(ethosudev->num_clks, ethosudev->clks);
+ if (ret)
+ return ret;
+
+ ret = ethosu_reset(ethosudev);
+ if (!ret)
+ return 0;
+
+ clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks);
+ return ret;
+}
+
+static int ethosu_device_suspend(struct device *dev)
+{
+ struct ethosu_device *ethosudev = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks);
+ return 0;
+}
+
+static int ethosu_sram_init(struct ethosu_device *ethosudev)
+{
+ ethosudev->npu_info.sram_size = 0;
+
+ ethosudev->srampool = of_gen_pool_get(ethosudev->base.dev->of_node, "sram", 0);
+ if (!ethosudev->srampool)
+ return 0;
+
+ ethosudev->npu_info.sram_size = gen_pool_size(ethosudev->srampool);
+
+ ethosudev->sram = (void __iomem *)gen_pool_dma_alloc(ethosudev->srampool,
+ ethosudev->npu_info.sram_size,
+ &ethosudev->sramphys);
+ if (!ethosudev->sram) {
+ dev_err(ethosudev->base.dev, "failed to allocate from SRAM pool\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int ethosu_init(struct ethosu_device *ethosudev)
+{
+ int ret;
+ u32 id, config;
+
+ ret = ethosu_device_resume(ethosudev->base.dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);
+ pm_runtime_use_autosuspend(ethosudev->base.dev);
+ ret = devm_pm_runtime_set_active_enabled(ethosudev->base.dev);
+ if (ret)
+ return ret;
+ pm_runtime_get_noresume(ethosudev->base.dev);
+
+ ethosudev->npu_info.id = id = readl_relaxed(ethosudev->regs + NPU_REG_ID);
+ ethosudev->npu_info.config = config = readl_relaxed(ethosudev->regs + NPU_REG_CONFIG);
+
+ ethosu_sram_init(ethosudev);
+
+ dev_info(ethosudev->base.dev,
+ "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n",
+ FIELD_GET(ID_ARCH_MAJOR_MASK, id),
+ FIELD_GET(ID_ARCH_MINOR_MASK, id),
+ FIELD_GET(ID_ARCH_PATCH_MASK, id),
+ FIELD_GET(ID_VER_MAJOR_MASK, id),
+ FIELD_GET(ID_VER_MINOR_MASK, id),
+ FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config),
+ 1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config),
+ ethosudev->npu_info.sram_size / 1024);
+
+ return 0;
+}
+
+static int ethosu_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct ethosu_device *ethosudev;
+
+ ethosudev = devm_drm_dev_alloc(&pdev->dev, &ethosu_drm_driver,
+ struct ethosu_device, base);
+ if (IS_ERR(ethosudev))
+ return -ENOMEM;
+ platform_set_drvdata(pdev, ethosudev);
+
+ dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
+
+ ethosudev->regs = devm_platform_ioremap_resource(pdev, 0);
+
+ ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, &ethosudev->clks);
+ if (ethosudev->num_clks < 0)
+ return ethosudev->num_clks;
+
+ ret = ethosu_job_init(ethosudev);
+ if (ret)
+ return ret;
+
+ ret = ethosu_init(ethosudev);
+ if (ret)
+ return ret;
+
+ ret = drm_dev_register(&ethosudev->base, 0);
+ if (ret)
+ pm_runtime_dont_use_autosuspend(ethosudev->base.dev);
+
+ pm_runtime_put_autosuspend(ethosudev->base.dev);
+ return ret;
+}
+
+static void ethosu_remove(struct platform_device *pdev)
+{
+ struct ethosu_device *ethosudev = dev_get_drvdata(&pdev->dev);
+
+ drm_dev_unregister(&ethosudev->base);
+ ethosu_job_fini(ethosudev);
+ if (ethosudev->sram)
+ gen_pool_free(ethosudev->srampool, (unsigned long)ethosudev->sram,
+ ethosudev->npu_info.sram_size);
+}
+
+static const struct of_device_id dt_match[] = {
+ { .compatible = "arm,ethos-u65" },
+ { .compatible = "arm,ethos-u85" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dt_match);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ethosu_pm_ops,
+ ethosu_device_suspend,
+ ethosu_device_resume,
+ NULL);
+
+static struct platform_driver ethosu_driver = {
+ .probe = ethosu_probe,
+ .remove = ethosu_remove,
+ .driver = {
+ .name = "ethosu",
+ .pm = pm_ptr(&ethosu_pm_ops),
+ .of_match_table = dt_match,
+ },
+};
+module_platform_driver(ethosu_driver);
+
+MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
+MODULE_DESCRIPTION("Arm Ethos-U Accel Driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/accel/ethosu/ethosu_drv.h b/drivers/accel/ethosu/ethosu_drv.h
new file mode 100644
index 000000000000..9e21dfe94184
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_drv.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+/* Copyright 2025 Arm, Ltd. */
+#ifndef __ETHOSU_DRV_H__
+#define __ETHOSU_DRV_H__
+
+#include <drm/gpu_scheduler.h>
+
+struct ethosu_device;
+
+struct ethosu_file_priv {
+ struct ethosu_device *edev;
+ struct drm_sched_entity sched_entity;
+};
+
+#endif
diff --git a/drivers/accel/ethosu/ethosu_gem.c b/drivers/accel/ethosu/ethosu_gem.c
new file mode 100644
index 000000000000..473b5f5d7514
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_gem.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0-only or MIT
+/* Copyright 2025 Arm, Ltd. */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <drm/ethosu_accel.h>
+
+#include "ethosu_device.h"
+#include "ethosu_gem.h"
+
+static void ethosu_gem_free_object(struct drm_gem_object *obj)
+{
+ struct ethosu_gem_object *bo = to_ethosu_bo(obj);
+
+ kfree(bo->info);
+ drm_gem_free_mmap_offset(&bo->base.base);
+ drm_gem_dma_free(&bo->base);
+}
+
+static int ethosu_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct ethosu_gem_object *bo = to_ethosu_bo(obj);
+
+ /* Don't allow mmap on objects that have the NO_MMAP flag set. */
+ if (bo->flags & DRM_ETHOSU_BO_NO_MMAP)
+ return -EINVAL;
+
+ return drm_gem_dma_object_mmap(obj, vma);
+}
+
+static const struct drm_gem_object_funcs ethosu_gem_funcs = {
+ .free = ethosu_gem_free_object,
+ .print_info = drm_gem_dma_object_print_info,
+ .get_sg_table = drm_gem_dma_object_get_sg_table,
+ .vmap = drm_gem_dma_object_vmap,
+ .mmap = ethosu_gem_mmap,
+ .vm_ops = &drm_gem_dma_vm_ops,
+};
+
+/**
+ * ethosu_gem_create_object - Implementation of driver->gem_create_object.
+ * @ddev: DRM device
+ * @size: Size in bytes of the memory the object will reference
+ *
+ * This lets the GEM helpers allocate object structs for us, and keep
+ * our BO stats correct.
+ */
+struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev, size_t size)
+{
+ struct ethosu_gem_object *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return ERR_PTR(-ENOMEM);
+
+ obj->base.base.funcs = &ethosu_gem_funcs;
+ return &obj->base.base;
+}
+
+/**
+ * ethosu_gem_create_with_handle() - Create a GEM object and attach it to a handle.
+ * @file: DRM file.
+ * @ddev: DRM device.
+ * @size: Size of the GEM object to allocate.
+ * @flags: Combination of drm_ethosu_bo_flags flags.
+ * @handle: Pointer holding the handle pointing to the new GEM object.
+ *
+ * Return: Zero on success
+ */
+int ethosu_gem_create_with_handle(struct drm_file *file,
+ struct drm_device *ddev,
+ u64 *size, u32 flags, u32 *handle)
+{
+ struct drm_gem_dma_object *mem;
+ struct ethosu_gem_object *bo;
+ int ret;
+
+ mem = drm_gem_dma_create(ddev, *size);
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
+
+ bo = to_ethosu_bo(&mem->base);
+ bo->flags = flags;
+
+ /*
+ * 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, &mem->base, handle);
+ if (!ret)
+ *size = bo->base.base.size;
+
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_put(&mem->base);
+
+ return ret;
+}
+
+struct dma {
+ s8 region;
+ u64 len;
+ u64 offset;
+ s64 stride[2];
+};
+
+struct dma_state {
+ u16 size0;
+ u16 size1;
+ s8 mode;
+ struct dma src;
+ struct dma dst;
+};
+
+struct buffer {
+ u64 base;
+ u32 length;
+ s8 region;
+};
+
+struct feat_matrix {
+ u64 base[4];
+ s64 stride_x;
+ s64 stride_y;
+ s64 stride_c;
+ s8 region;
+ u8 broadcast;
+ u16 stride_kernel;
+ u16 precision;
+ u16 depth;
+ u16 width;
+ u16 width0;
+ u16 height[3];
+ u8 pad_top;
+ u8 pad_left;
+ u8 pad_bottom;
+ u8 pad_right;
+};
+
+struct cmd_state {
+ struct dma_state dma;
+ struct buffer scale[2];
+ struct buffer weight[4];
+ struct feat_matrix ofm;
+ struct feat_matrix ifm;
+ struct feat_matrix ifm2;
+};
+
+static void cmd_state_init(struct cmd_state *st)
+{
+ /* Initialize to all 1s to detect missing setup */
+ memset(st, 0xff, sizeof(*st));
+}
+
+static u64 cmd_to_addr(u32 *cmd)
+{
+ return ((u64)((cmd[0] & 0xff0000) << 16)) | cmd[1];
+}
+
+static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
+ struct dma_state *dma_st, struct dma *dma)
+{
+ s8 mode = dma_st->mode;
+ u64 len = dma->len;
+
+ if (mode >= 1) {
+ len += dma->stride[0];
+ len *= dma_st->size0;
+ }
+ if (mode == 2) {
+ len += dma->stride[1];
+ len *= dma_st->size1;
+ }
+ if (dma->region >= 0)
+ info->region_size[dma->region] = max(info->region_size[dma->region],
+ len + dma->offset);
+
+ return len;
+}
+
+static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
+ struct feat_matrix *fm,
+ u32 x, u32 y, u32 c)
+{
+ u32 element_size, storage = fm->precision >> 14;
+ int tile = 0;
+ u64 addr;
+
+ if (fm->region < 0)
+ return U64_MAX;
+
+ switch (storage) {
+ case 0:
+ if (x >= fm->width0 + 1) {
+ x -= fm->width0 + 1;
+ tile += 1;
+ }
+ if (y >= fm->height[tile] + 1) {
+ y -= fm->height[tile] + 1;
+ tile += 2;
+ }
+ break;
+ case 1:
+ if (y >= fm->height[1] + 1) {
+ y -= fm->height[1] + 1;
+ tile = 2;
+ } else if (y >= fm->height[0] + 1) {
+ y -= fm->height[0] + 1;
+ tile = 1;
+ }
+ break;
+ }
+ if (fm->base[tile] == U64_MAX)
+ return U64_MAX;
+
+ addr = fm->base[tile] + y * fm->stride_y;
+
+ switch ((fm->precision >> 6) & 0x3) { // format
+ case 0: //nhwc:
+ addr += x * fm->stride_x + c;
+ break;
+ case 1: //nhcwb16:
+ element_size = BIT((fm->precision >> 1) & 0x3);
+
+ addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
+ break;
+ }
+
+ info->region_size[fm->region] = max(info->region_size[fm->region], addr + 1);
+
+ return addr;
+}
+
+static int calc_sizes(struct drm_device *ddev,
+ struct ethosu_validated_cmdstream_info *info,
+ u16 op, struct cmd_state *st,
+ bool ifm, bool ifm2, bool weight, bool scale)
+{
+ u64 len;
+
+ if (ifm) {
+ if (st->ifm.stride_kernel == U16_MAX)
+ return -EINVAL;
+ u32 stride_y = ((st->ifm.stride_kernel >> 8) & 0x2) +
+ ((st->ifm.stride_kernel >> 1) & 0x1) + 1;
+ u32 stride_x = ((st->ifm.stride_kernel >> 5) & 0x2) +
+ (st->ifm.stride_kernel & 0x1) + 1;
+ u32 ifm_height = st->ofm.height[2] * stride_y +
+ st->ifm.height[2] - (st->ifm.pad_top + st->ifm.pad_bottom);
+ u32 ifm_width = st->ofm.width * stride_x +
+ st->ifm.width - (st->ifm.pad_left + st->ifm.pad_right);
+
+ len = feat_matrix_length(info, &st->ifm, ifm_width,
+ ifm_height, st->ifm.depth);
+ dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
+ op, st->ifm.region, st->ifm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ if (ifm2) {
+ len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
+ 0, st->ofm.depth);
+ dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
+ op, st->ifm2.region, st->ifm2.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ if (weight) {
+ dev_dbg(ddev->dev, "op %d: W:%d:0x%llx-0x%llx\n",
+ op, st->weight[0].region, st->weight[0].base,
+ st->weight[0].base + st->weight[0].length - 1);
+ if (st->weight[0].region < 0 || st->weight[0].base == U64_MAX ||
+ st->weight[0].length == U32_MAX)
+ return -EINVAL;
+ info->region_size[st->weight[0].region] =
+ max(info->region_size[st->weight[0].region],
+ st->weight[0].base + st->weight[0].length);
+ }
+
+ if (scale) {
+ dev_dbg(ddev->dev, "op %d: S:%d:0x%llx-0x%llx\n",
+ op, st->scale[0].region, st->scale[0].base,
+ st->scale[0].base + st->scale[0].length - 1);
+ if (st->scale[0].region < 0 || st->scale[0].base == U64_MAX ||
+ st->scale[0].length == U32_MAX)
+ return -EINVAL;
+ info->region_size[st->scale[0].region] =
+ max(info->region_size[st->scale[0].region],
+ st->scale[0].base + st->scale[0].length);
+ }
+
+ len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+ st->ofm.height[2], st->ofm.depth);
+ dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
+ op, st->ofm.region, st->ofm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ info->output_region[st->ofm.region] = true;
+
+ return 0;
+}
+
+static int calc_sizes_elemwise(struct drm_device *ddev,
+ struct ethosu_validated_cmdstream_info *info,
+ u16 op, struct cmd_state *st,
+ bool ifm, bool ifm2)
+{
+ u32 height, width, depth;
+ u64 len;
+
+ if (ifm) {
+ height = st->ifm.broadcast & 0x1 ? 0 : st->ofm.height[2];
+ width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
+ depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
+
+ len = feat_matrix_length(info, &st->ifm, width,
+ height, depth);
+ dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
+ op, st->ifm.region, st->ifm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ if (ifm2) {
+ height = st->ifm2.broadcast & 0x1 ? 0 : st->ofm.height[2];
+ width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
+ depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
+
+ len = feat_matrix_length(info, &st->ifm2, width,
+ height, depth);
+ dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
+ op, st->ifm2.region, st->ifm2.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ }
+
+ len = feat_matrix_length(info, &st->ofm, st->ofm.width,
+ st->ofm.height[2], st->ofm.depth);
+ dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
+ op, st->ofm.region, st->ofm.base[0], len);
+ if (len == U64_MAX)
+ return -EINVAL;
+ info->output_region[st->ofm.region] = true;
+
+ return 0;
+}
+
+static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
+ u32 __user *ucmds,
+ struct ethosu_gem_object *bo,
+ u32 size)
+{
+ struct ethosu_validated_cmdstream_info __free(kfree) *info = kzalloc(sizeof(*info), GFP_KERNEL);
+ struct ethosu_device *edev = to_ethosu_device(ddev);
+ u32 *bocmds = bo->base.vaddr;
+ struct cmd_state st;
+ int i, ret;
+
+ if (!info)
+ return -ENOMEM;
+ info->cmd_size = size;
+
+ cmd_state_init(&st);
+
+ for (i = 0; i < size / 4; i++) {
+ bool use_ifm, use_ifm2, use_scale;
+ u64 dstlen, srclen;
+ u16 cmd, param;
+ u32 cmds[2];
+ u64 addr;
+
+ if (get_user(cmds[0], ucmds++))
+ return -EFAULT;
+
+ bocmds[i] = cmds[0];
+
+ cmd = cmds[0];
+ param = cmds[0] >> 16;
+
+ if (cmd & 0x4000) {
+ if (get_user(cmds[1], ucmds++))
+ return -EFAULT;
+
+ i++;
+ bocmds[i] = cmds[1];
+ addr = cmd_to_addr(cmds);
+ }
+
+ switch (cmd) {
+ case NPU_OP_DMA_START:
+ srclen = dma_length(info, &st.dma, &st.dma.src);
+ dstlen = dma_length(info, &st.dma, &st.dma.dst);
+
+ if (st.dma.dst.region >= 0)
+ info->output_region[st.dma.dst.region] = true;
+ dev_dbg(ddev->dev, "cmd: DMA SRC:%d:0x%llx+0x%llx DST:%d:0x%llx+0x%llx\n",
+ st.dma.src.region, st.dma.src.offset, srclen,
+ st.dma.dst.region, st.dma.dst.offset, dstlen);
+ break;
+ case NPU_OP_CONV:
+ case NPU_OP_DEPTHWISE:
+ use_ifm2 = param & 0x1; // weights_ifm2
+ use_scale = !(st.ofm.precision & 0x100);
+ ret = calc_sizes(ddev, info, cmd, &st, true, use_ifm2,
+ !use_ifm2, use_scale);
+ if (ret)
+ return ret;
+ break;
+ case NPU_OP_POOL:
+ use_ifm = param != 0x4; // pooling mode
+ use_scale = !(st.ofm.precision & 0x100);
+ ret = calc_sizes(ddev, info, cmd, &st, use_ifm, false,
+ false, use_scale);
+ if (ret)
+ return ret;
+ break;
+ case NPU_OP_ELEMENTWISE:
+ use_ifm2 = !((st.ifm2.broadcast == 8) || (param == 5) ||
+ (param == 6) || (param == 7) || (param == 0x24));
+ use_ifm = st.ifm.broadcast != 8;
+ ret = calc_sizes_elemwise(ddev, info, cmd, &st, use_ifm, use_ifm2);
+ if (ret)
+ return ret;
+ break;
+ case NPU_OP_RESIZE: // U85 only
+ WARN_ON(1); // TODO
+ break;
+ case NPU_SET_KERNEL_WIDTH_M1:
+ st.ifm.width = param;
+ break;
+ case NPU_SET_KERNEL_HEIGHT_M1:
+ st.ifm.height[2] = param;
+ break;
+ case NPU_SET_KERNEL_STRIDE:
+ st.ifm.stride_kernel = param;
+ break;
+ case NPU_SET_IFM_PAD_TOP:
+ st.ifm.pad_top = param & 0x7f;
+ break;
+ case NPU_SET_IFM_PAD_LEFT:
+ st.ifm.pad_left = param & 0x7f;
+ break;
+ case NPU_SET_IFM_PAD_RIGHT:
+ st.ifm.pad_right = param & 0xff;
+ break;
+ case NPU_SET_IFM_PAD_BOTTOM:
+ st.ifm.pad_bottom = param & 0xff;
+ break;
+ case NPU_SET_IFM_DEPTH_M1:
+ st.ifm.depth = param;
+ break;
+ case NPU_SET_IFM_PRECISION:
+ st.ifm.precision = param;
+ break;
+ case NPU_SET_IFM_BROADCAST:
+ st.ifm.broadcast = param;
+ break;
+ case NPU_SET_IFM_REGION:
+ st.ifm.region = param & 0x7f;
+ break;
+ case NPU_SET_IFM_WIDTH0_M1:
+ st.ifm.width0 = param;
+ break;
+ case NPU_SET_IFM_HEIGHT0_M1:
+ st.ifm.height[0] = param;
+ break;
+ case NPU_SET_IFM_HEIGHT1_M1:
+ st.ifm.height[1] = param;
+ break;
+ case NPU_SET_IFM_BASE0:
+ case NPU_SET_IFM_BASE1:
+ case NPU_SET_IFM_BASE2:
+ case NPU_SET_IFM_BASE3:
+ st.ifm.base[cmd & 0x3] = addr;
+ break;
+ case NPU_SET_IFM_STRIDE_X:
+ st.ifm.stride_x = addr;
+ break;
+ case NPU_SET_IFM_STRIDE_Y:
+ st.ifm.stride_y = addr;
+ break;
+ case NPU_SET_IFM_STRIDE_C:
+ st.ifm.stride_c = addr;
+ break;
+
+ case NPU_SET_OFM_WIDTH_M1:
+ st.ofm.width = param;
+ break;
+ case NPU_SET_OFM_HEIGHT_M1:
+ st.ofm.height[2] = param;
+ break;
+ case NPU_SET_OFM_DEPTH_M1:
+ st.ofm.depth = param;
+ break;
+ case NPU_SET_OFM_PRECISION:
+ st.ofm.precision = param;
+ break;
+ case NPU_SET_OFM_REGION:
+ st.ofm.region = param & 0x7;
+ break;
+ case NPU_SET_OFM_WIDTH0_M1:
+ st.ofm.width0 = param;
+ break;
+ case NPU_SET_OFM_HEIGHT0_M1:
+ st.ofm.height[0] = param;
+ break;
+ case NPU_SET_OFM_HEIGHT1_M1:
+ st.ofm.height[1] = param;
+ break;
+ case NPU_SET_OFM_BASE0:
+ case NPU_SET_OFM_BASE1:
+ case NPU_SET_OFM_BASE2:
+ case NPU_SET_OFM_BASE3:
+ st.ofm.base[cmd & 0x3] = addr;
+ break;
+ case NPU_SET_OFM_STRIDE_X:
+ st.ofm.stride_x = addr;
+ break;
+ case NPU_SET_OFM_STRIDE_Y:
+ st.ofm.stride_y = addr;
+ break;
+ case NPU_SET_OFM_STRIDE_C:
+ st.ofm.stride_c = addr;
+ break;
+
+ case NPU_SET_IFM2_BROADCAST:
+ st.ifm2.broadcast = param;
+ break;
+ case NPU_SET_IFM2_PRECISION:
+ st.ifm2.precision = param;
+ break;
+ case NPU_SET_IFM2_REGION:
+ st.ifm2.region = param & 0x7;
+ break;
+ case NPU_SET_IFM2_WIDTH0_M1:
+ st.ifm2.width0 = param;
+ break;
+ case NPU_SET_IFM2_HEIGHT0_M1:
+ st.ifm2.height[0] = param;
+ break;
+ case NPU_SET_IFM2_HEIGHT1_M1:
+ st.ifm2.height[1] = param;
+ break;
+ case NPU_SET_IFM2_BASE0:
+ case NPU_SET_IFM2_BASE1:
+ case NPU_SET_IFM2_BASE2:
+ case NPU_SET_IFM2_BASE3:
+ st.ifm2.base[cmd & 0x3] = addr;
+ break;
+ case NPU_SET_IFM2_STRIDE_X:
+ st.ifm2.stride_x = addr;
+ break;
+ case NPU_SET_IFM2_STRIDE_Y:
+ st.ifm2.stride_y = addr;
+ break;
+ case NPU_SET_IFM2_STRIDE_C:
+ st.ifm2.stride_c = addr;
+ break;
+
+ case NPU_SET_WEIGHT_REGION:
+ st.weight[0].region = param & 0x7;
+ break;
+ case NPU_SET_SCALE_REGION:
+ st.scale[0].region = param & 0x7;
+ break;
+ case NPU_SET_WEIGHT_BASE:
+ st.weight[0].base = addr;
+ break;
+ case NPU_SET_WEIGHT_LENGTH:
+ st.weight[0].length = cmds[1];
+ break;
+ case NPU_SET_SCALE_BASE:
+ st.scale[0].base = addr;
+ break;
+ case NPU_SET_SCALE_LENGTH:
+ st.scale[0].length = cmds[1];
+ break;
+ case NPU_SET_WEIGHT1_BASE:
+ st.weight[1].base = addr;
+ break;
+ case NPU_SET_WEIGHT1_LENGTH:
+ st.weight[1].length = cmds[1];
+ break;
+ case NPU_SET_SCALE1_BASE: // NPU_SET_WEIGHT2_BASE (U85)
+ if (ethosu_is_u65(edev))
+ st.scale[1].base = addr;
+ else
+ st.weight[2].base = addr;
+ break;
+ case NPU_SET_SCALE1_LENGTH: // NPU_SET_WEIGHT2_LENGTH (U85)
+ if (ethosu_is_u65(edev))
+ st.scale[1].length = cmds[1];
+ else
+ st.weight[1].length = cmds[1];
+ break;
+ case NPU_SET_WEIGHT3_BASE:
+ st.weight[3].base = addr;
+ break;
+ case NPU_SET_WEIGHT3_LENGTH:
+ st.weight[3].length = cmds[1];
+ break;
+
+ case NPU_SET_DMA0_SRC_REGION:
+ if (param & 0x100)
+ st.dma.src.region = -1;
+ else
+ st.dma.src.region = param & 0x7;
+ st.dma.mode = (param >> 9) & 0x3;
+ break;
+ case NPU_SET_DMA0_DST_REGION:
+ if (param & 0x100)
+ st.dma.dst.region = -1;
+ else
+ st.dma.dst.region = param & 0x7;
+ break;
+ case NPU_SET_DMA0_SIZE0:
+ st.dma.size0 = param;
+ break;
+ case NPU_SET_DMA0_SIZE1:
+ st.dma.size1 = param;
+ break;
+ case NPU_SET_DMA0_SRC_STRIDE0:
+ st.dma.src.stride[0] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_SRC_STRIDE1:
+ st.dma.src.stride[1] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_DST_STRIDE0:
+ st.dma.dst.stride[0] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_DST_STRIDE1:
+ st.dma.dst.stride[1] = ((s64)addr << 24) >> 24;
+ break;
+ case NPU_SET_DMA0_SRC:
+ st.dma.src.offset = addr;
+ break;
+ case NPU_SET_DMA0_DST:
+ st.dma.dst.offset = addr;
+ break;
+ case NPU_SET_DMA0_LEN:
+ st.dma.src.len = st.dma.dst.len = addr;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < NPU_BASEP_REGION_MAX; i++) {
+ if (!info->region_size[i])
+ continue;
+ dev_dbg(ddev->dev, "region %d max size: 0x%llx\n",
+ i, info->region_size[i]);
+ }
+
+ bo->info = no_free_ptr(info);
+ return 0;
+}
+
+/**
+ * ethosu_gem_cmdstream_create() - Create a GEM object and attach it to a handle.
+ * @file: DRM file.
+ * @ddev: DRM device.
+ * @exclusive_vm: Exclusive VM. Not NULL if the GEM object can't be shared.
+ * @size: Size of the GEM object to allocate.
+ * @flags: Combination of drm_ethosu_bo_flags flags.
+ * @handle: Pointer holding the handle pointing to the new GEM object.
+ *
+ * Return: Zero on success
+ */
+int ethosu_gem_cmdstream_create(struct drm_file *file,
+ struct drm_device *ddev,
+ u32 size, u64 data, u32 flags, u32 *handle)
+{
+ int ret;
+ struct drm_gem_dma_object *mem;
+ struct ethosu_gem_object *bo;
+
+ mem = drm_gem_dma_create(ddev, size);
+ if (IS_ERR(mem))
+ return PTR_ERR(mem);
+
+ bo = to_ethosu_bo(&mem->base);
+ bo->flags = flags;
+
+ ret = ethosu_gem_cmdstream_copy_and_validate(ddev,
+ (void __user *)(uintptr_t)data,
+ bo, size);
+ if (ret)
+ goto fail;
+
+ /*
+ * 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, &mem->base, handle);
+
+fail:
+ /* drop reference from allocate - handle holds it now. */
+ drm_gem_object_put(&mem->base);
+
+ return ret;
+}
diff --git a/drivers/accel/ethosu/ethosu_gem.h b/drivers/accel/ethosu/ethosu_gem.h
new file mode 100644
index 000000000000..3922895a60fb
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_gem.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2025 Arm, Ltd. */
+
+#ifndef __ETHOSU_GEM_H__
+#define __ETHOSU_GEM_H__
+
+#include "ethosu_device.h"
+#include <drm/drm_gem_dma_helper.h>
+
+struct ethosu_validated_cmdstream_info {
+ u32 cmd_size;
+ u64 region_size[NPU_BASEP_REGION_MAX];
+ bool output_region[NPU_BASEP_REGION_MAX];
+};
+
+/**
+ * struct ethosu_gem_object - Driver specific GEM object.
+ */
+struct ethosu_gem_object {
+ /** @base: Inherit from drm_gem_shmem_object. */
+ struct drm_gem_dma_object base;
+
+ struct ethosu_validated_cmdstream_info *info;
+
+ /** @flags: Combination of drm_ethosu_bo_flags flags. */
+ u32 flags;
+};
+
+static inline
+struct ethosu_gem_object *to_ethosu_bo(struct drm_gem_object *obj)
+{
+ return container_of(to_drm_gem_dma_obj(obj), struct ethosu_gem_object, base);
+}
+
+struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev,
+ size_t size);
+
+int ethosu_gem_create_with_handle(struct drm_file *file,
+ struct drm_device *ddev,
+ u64 *size, u32 flags, uint32_t *handle);
+
+int ethosu_gem_cmdstream_create(struct drm_file *file,
+ struct drm_device *ddev,
+ u32 size, u64 data, u32 flags, u32 *handle);
+
+#endif /* __ETHOSU_GEM_H__ */
diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c
new file mode 100644
index 000000000000..26e7a2f64d71
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_job.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+/* Copyright 2025 Arm, Ltd. */
+
+#include <linux/bitfield.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
+#include <drm/ethosu_accel.h>
+
+#include "ethosu_device.h"
+#include "ethosu_drv.h"
+#include "ethosu_gem.h"
+#include "ethosu_job.h"
+
+#define JOB_TIMEOUT_MS 500
+
+static struct ethosu_job *to_ethosu_job(struct drm_sched_job *sched_job)
+{
+ return container_of(sched_job, struct ethosu_job, base);
+}
+
+static const char *ethosu_fence_get_driver_name(struct dma_fence *fence)
+{
+ return "ethosu";
+}
+
+static const char *ethosu_fence_get_timeline_name(struct dma_fence *fence)
+{
+ return "ethosu-npu";
+}
+
+static const struct dma_fence_ops ethosu_fence_ops = {
+ .get_driver_name = ethosu_fence_get_driver_name,
+ .get_timeline_name = ethosu_fence_get_timeline_name,
+};
+
+static void ethosu_job_hw_submit(struct ethosu_device *dev, struct ethosu_job *job)
+{
+ struct drm_gem_dma_object *cmd_bo = to_drm_gem_dma_obj(job->cmd_bo);
+ struct ethosu_validated_cmdstream_info *cmd_info = to_ethosu_bo(job->cmd_bo)->info;
+
+ for (int i = 0; i < job->region_cnt; i++) {
+ struct drm_gem_dma_object *bo;
+ int region = job->region_bo_num[i];
+
+ bo = to_drm_gem_dma_obj(job->region_bo[i]);
+ writel_relaxed(lower_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP(region));
+ writel_relaxed(upper_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP_HI(region));
+ dev_dbg(dev->base.dev, "Region %d base addr = %pad\n", region, &bo->dma_addr);
+ }
+
+ if (job->sram_size) {
+ writel_relaxed(lower_32_bits(dev->sramphys),
+ dev->regs + NPU_REG_BASEP(ETHOSU_SRAM_REGION));
+ writel_relaxed(upper_32_bits(dev->sramphys),
+ dev->regs + NPU_REG_BASEP_HI(ETHOSU_SRAM_REGION));
+ dev_dbg(dev->base.dev, "Region %d base addr = %pad (SRAM)\n",
+ ETHOSU_SRAM_REGION, &dev->sramphys);
+ }
+
+ writel_relaxed(lower_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE);
+ writel_relaxed(upper_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE_HI);
+ writel_relaxed(cmd_info->cmd_size, dev->regs + NPU_REG_QSIZE);
+
+ writel(CMD_TRANSITION_TO_RUN, dev->regs + NPU_REG_CMD);
+
+ dev_dbg(dev->base.dev,
+ "Submitted cmd at %pad to core\n", &cmd_bo->dma_addr);
+}
+
+static int ethosu_acquire_object_fences(struct ethosu_job *job)
+{
+ int i, ret;
+ struct drm_gem_object **bos = job->region_bo;
+ struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
+
+ for (i = 0; i < job->region_cnt; i++) {
+ bool is_write;
+
+ if (!bos[i])
+ break;
+
+ ret = dma_resv_reserve_fences(bos[i]->resv, 1);
+ if (ret)
+ return ret;
+
+ is_write = info->output_region[job->region_bo_num[i]];
+ ret = drm_sched_job_add_implicit_dependencies(&job->base, bos[i],
+ is_write);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ethosu_attach_object_fences(struct ethosu_job *job)
+{
+ int i;
+ struct dma_fence *fence = job->inference_done_fence;
+ struct drm_gem_object **bos = job->region_bo;
+ struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
+
+ for (i = 0; i < job->region_cnt; i++)
+ if (info->output_region[job->region_bo_num[i]])
+ dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
+}
+
+static int ethosu_job_push(struct ethosu_job *job)
+{
+ struct ww_acquire_ctx acquire_ctx;
+ int ret;
+
+ ret = drm_gem_lock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
+ if (ret)
+ return ret;
+
+ ret = ethosu_acquire_object_fences(job);
+ if (ret)
+ goto out;
+
+ ret = pm_runtime_resume_and_get(job->dev->base.dev);
+ if (!ret) {
+ guard(mutex)(&job->dev->sched_lock);
+
+ drm_sched_job_arm(&job->base);
+ job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished);
+ kref_get(&job->refcount); /* put by scheduler job completion */
+ drm_sched_entity_push_job(&job->base);
+ ethosu_attach_object_fences(job);
+ }
+
+out:
+ drm_gem_unlock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
+ return ret;
+}
+
+static void ethosu_job_cleanup(struct kref *ref)
+{
+ struct ethosu_job *job = container_of(ref, struct ethosu_job,
+ refcount);
+ unsigned int i;
+
+ pm_runtime_put_autosuspend(job->dev->base.dev);
+
+ dma_fence_put(job->done_fence);
+ dma_fence_put(job->inference_done_fence);
+
+ for (i = 0; i < job->region_cnt; i++)
+ drm_gem_object_put(job->region_bo[i]);
+
+ drm_gem_object_put(job->cmd_bo);
+
+ kfree(job);
+}
+
+static void ethosu_job_put(struct ethosu_job *job)
+{
+ kref_put(&job->refcount, ethosu_job_cleanup);
+}
+
+static void ethosu_job_free(struct drm_sched_job *sched_job)
+{
+ struct ethosu_job *job = to_ethosu_job(sched_job);
+
+ drm_sched_job_cleanup(sched_job);
+ ethosu_job_put(job);
+}
+
+static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
+{
+ struct ethosu_job *job = to_ethosu_job(sched_job);
+ struct ethosu_device *dev = job->dev;
+ struct dma_fence *fence = job->done_fence;
+
+ if (unlikely(job->base.s_fence->finished.error))
+ return NULL;
+
+ dma_fence_init(fence, &ethosu_fence_ops, &dev->fence_lock,
+ dev->fence_context, ++dev->emit_seqno);
+ dma_fence_get(fence);
+
+ scoped_guard(mutex, &dev->job_lock) {
+ dev->in_flight_job = job;
+ ethosu_job_hw_submit(dev, job);
+ }
+
+ return fence;
+}
+
+static void ethosu_job_handle_irq(struct ethosu_device *dev)
+{
+ u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
+
+ if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) {
+ dev_err(dev->base.dev, "Error IRQ - %x\n", status);
+ drm_sched_fault(&dev->sched);
+ return;
+ }
+
+ scoped_guard(mutex, &dev->job_lock) {
+ if (dev->in_flight_job) {
+ dma_fence_signal(dev->in_flight_job->done_fence);
+ dev->in_flight_job = NULL;
+ }
+ }
+}
+
+static irqreturn_t ethosu_job_irq_handler_thread(int irq, void *data)
+{
+ struct ethosu_device *dev = data;
+
+ ethosu_job_handle_irq(dev);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ethosu_job_irq_handler(int irq, void *data)
+{
+ struct ethosu_device *dev = data;
+ u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
+
+ if (!(status & STATUS_IRQ_RAISED))
+ return IRQ_NONE;
+
+ writel_relaxed(CMD_CLEAR_IRQ, dev->regs + NPU_REG_CMD);
+ return IRQ_WAKE_THREAD;
+}
+
+static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad)
+{
+ struct ethosu_job *job = to_ethosu_job(bad);
+ struct ethosu_device *dev = job->dev;
+ bool running;
+ u32 *bocmds = to_drm_gem_dma_obj(job->cmd_bo)->vaddr;
+ u32 cmdaddr;
+
+ cmdaddr = readl_relaxed(dev->regs + NPU_REG_QREAD);
+ running = FIELD_GET(STATUS_STATE_RUNNING, readl_relaxed(dev->regs + NPU_REG_STATUS));
+
+ if (running) {
+ int ret;
+ u32 reg;
+
+ ret = readl_relaxed_poll_timeout(dev->regs + NPU_REG_QREAD,
+ reg,
+ reg != cmdaddr,
+ USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+
+ /* If still running and progress is being made, just return */
+ if (!ret)
+ return DRM_GPU_SCHED_STAT_NO_HANG;
+ }
+
+ dev_err(dev->base.dev, "NPU sched timed out: NPU %s, cmdstream offset 0x%x: 0x%x\n",
+ running ? "running" : "stopped",
+ cmdaddr, bocmds[cmdaddr / 4]);
+
+ drm_sched_stop(&dev->sched, bad);
+
+ scoped_guard(mutex, &dev->job_lock)
+ dev->in_flight_job = NULL;
+
+ /* Proceed with reset now. */
+ pm_runtime_force_suspend(dev->base.dev);
+ pm_runtime_force_resume(dev->base.dev);
+
+ /* Restart the scheduler */
+ drm_sched_start(&dev->sched, 0);
+
+ return DRM_GPU_SCHED_STAT_RESET;
+}
+
+static const struct drm_sched_backend_ops ethosu_sched_ops = {
+ .run_job = ethosu_job_run,
+ .timedout_job = ethosu_job_timedout,
+ .free_job = ethosu_job_free
+};
+
+int ethosu_job_init(struct ethosu_device *edev)
+{
+ struct device *dev = edev->base.dev;
+ struct drm_sched_init_args args = {
+ .ops = &ethosu_sched_ops,
+ .num_rqs = DRM_SCHED_PRIORITY_COUNT,
+ .credit_limit = 1,
+ .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
+ .name = dev_name(dev),
+ .dev = dev,
+ };
+ int ret;
+
+ spin_lock_init(&edev->fence_lock);
+ ret = devm_mutex_init(dev, &edev->job_lock);
+ if (ret)
+ return ret;
+ ret = devm_mutex_init(dev, &edev->sched_lock);
+ if (ret)
+ return ret;
+
+ edev->irq = platform_get_irq(to_platform_device(dev), 0);
+ if (edev->irq < 0)
+ return edev->irq;
+
+ ret = devm_request_threaded_irq(dev, edev->irq,
+ ethosu_job_irq_handler,
+ ethosu_job_irq_handler_thread,
+ IRQF_SHARED, KBUILD_MODNAME,
+ edev);
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ return ret;
+ }
+
+ edev->fence_context = dma_fence_context_alloc(1);
+
+ ret = drm_sched_init(&edev->sched, &args);
+ if (ret) {
+ dev_err(dev, "Failed to create scheduler: %d\n", ret);
+ goto err_sched;
+ }
+
+ return 0;
+
+err_sched:
+ drm_sched_fini(&edev->sched);
+ return ret;
+}
+
+void ethosu_job_fini(struct ethosu_device *dev)
+{
+ drm_sched_fini(&dev->sched);
+}
+
+int ethosu_job_open(struct ethosu_file_priv *ethosu_priv)
+{
+ struct ethosu_device *dev = ethosu_priv->edev;
+ struct drm_gpu_scheduler *sched = &dev->sched;
+ int ret;
+
+ ret = drm_sched_entity_init(&ethosu_priv->sched_entity,
+ DRM_SCHED_PRIORITY_NORMAL,
+ &sched, 1, NULL);
+ return WARN_ON(ret);
+}
+
+void ethosu_job_close(struct ethosu_file_priv *ethosu_priv)
+{
+ struct drm_sched_entity *entity = &ethosu_priv->sched_entity;
+
+ drm_sched_entity_destroy(entity);
+}
+
+static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
+ struct drm_ethosu_job *job)
+{
+ struct ethosu_device *edev = to_ethosu_device(dev);
+ struct ethosu_file_priv *file_priv = file->driver_priv;
+ struct ethosu_job *ejob = NULL;
+ struct ethosu_validated_cmdstream_info *cmd_info;
+ int ret = 0;
+
+ /* BO region 2 is reserved if SRAM is used */
+ if (job->region_bo_handles[ETHOSU_SRAM_REGION] && job->sram_size)
+ return -EINVAL;
+
+ if (edev->npu_info.sram_size < job->sram_size)
+ return -EINVAL;
+
+ ejob = kzalloc(sizeof(*ejob), GFP_KERNEL);
+ if (!ejob)
+ return -ENOMEM;
+
+ kref_init(&ejob->refcount);
+
+ ejob->dev = edev;
+ ejob->sram_size = job->sram_size;
+
+ ejob->done_fence = kzalloc(sizeof(*ejob->done_fence), GFP_KERNEL);
+ if (!ejob->done_fence) {
+ ret = -ENOMEM;
+ goto out_cleanup_job;
+ }
+
+ ret = drm_sched_job_init(&ejob->base,
+ &file_priv->sched_entity,
+ 1, NULL, file->client_id);
+ if (ret)
+ goto out_put_job;
+
+ ejob->cmd_bo = drm_gem_object_lookup(file, job->cmd_bo);
+ if (!ejob->cmd_bo) {
+ ret = -ENOENT;
+ goto out_cleanup_job;
+ }
+ cmd_info = to_ethosu_bo(ejob->cmd_bo)->info;
+ if (!cmd_info) {
+ ret = -EINVAL;
+ goto out_cleanup_job;
+ }
+
+ for (int i = 0; i < NPU_BASEP_REGION_MAX; i++) {
+ struct drm_gem_object *gem;
+
+ /* Can only omit a BO handle if the region is not used or used for SRAM */
+ if (!job->region_bo_handles[i] &&
+ (!cmd_info->region_size[i] || (i == ETHOSU_SRAM_REGION && job->sram_size)))
+ continue;
+
+ if (job->region_bo_handles[i] && !cmd_info->region_size[i]) {
+ dev_err(dev->dev,
+ "Cmdstream BO handle %d set for unused region %d\n",
+ job->region_bo_handles[i], i);
+ ret = -EINVAL;
+ goto out_cleanup_job;
+ }
+
+ gem = drm_gem_object_lookup(file, job->region_bo_handles[i]);
+ if (!gem) {
+ dev_err(dev->dev,
+ "Invalid BO handle %d for region %d\n",
+ job->region_bo_handles[i], i);
+ ret = -ENOENT;
+ goto out_cleanup_job;
+ }
+
+ ejob->region_bo[ejob->region_cnt] = gem;
+ ejob->region_bo_num[ejob->region_cnt] = i;
+ ejob->region_cnt++;
+
+ if (to_ethosu_bo(gem)->info) {
+ dev_err(dev->dev,
+ "Cmdstream BO handle %d used for region %d\n",
+ job->region_bo_handles[i], i);
+ ret = -EINVAL;
+ goto out_cleanup_job;
+ }
+
+ /* Verify the command stream doesn't have accesses outside the BO */
+ if (cmd_info->region_size[i] > gem->size) {
+ dev_err(dev->dev,
+ "cmd stream region %d size greater than BO size (%llu > %zu)\n",
+ i, cmd_info->region_size[i], gem->size);
+ ret = -EOVERFLOW;
+ goto out_cleanup_job;
+ }
+ }
+ ret = ethosu_job_push(ejob);
+
+out_cleanup_job:
+ if (ret)
+ drm_sched_job_cleanup(&ejob->base);
+out_put_job:
+ ethosu_job_put(ejob);
+
+ return ret;
+}
+
+int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ethosu_submit *args = data;
+ int ret = 0;
+ unsigned int i = 0;
+
+ if (args->pad) {
+ drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n");
+ return -EINVAL;
+ }
+
+ struct drm_ethosu_job __free(kvfree) *jobs =
+ kvmalloc_array(args->job_count, sizeof(*jobs), GFP_KERNEL);
+ if (!jobs)
+ return -ENOMEM;
+
+ if (copy_from_user(jobs,
+ (void __user *)(uintptr_t)args->jobs,
+ args->job_count * sizeof(*jobs))) {
+ drm_dbg(dev, "Failed to copy incoming job array\n");
+ return -EFAULT;
+ }
+
+ for (i = 0; i < args->job_count; i++) {
+ ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/drivers/accel/ethosu/ethosu_job.h b/drivers/accel/ethosu/ethosu_job.h
new file mode 100644
index 000000000000..ff1cf448d094
--- /dev/null
+++ b/drivers/accel/ethosu/ethosu_job.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
+/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
+/* Copyright 2025 Arm, Ltd. */
+
+#ifndef __ETHOSU_JOB_H__
+#define __ETHOSU_JOB_H__
+
+#include <linux/kref.h>
+#include <drm/gpu_scheduler.h>
+
+struct ethosu_device;
+struct ethosu_file_priv;
+
+struct ethosu_job {
+ struct drm_sched_job base;
+ struct ethosu_device *dev;
+
+ struct drm_gem_object *cmd_bo;
+ struct drm_gem_object *region_bo[NPU_BASEP_REGION_MAX];
+ u8 region_bo_num[NPU_BASEP_REGION_MAX];
+ u8 region_cnt;
+ u32 sram_size;
+
+ /* Fence to be signaled by drm-sched once its done with the job */
+ struct dma_fence *inference_done_fence;
+
+ /* Fence to be signaled by IRQ handler when the job is complete. */
+ struct dma_fence *done_fence;
+
+ struct kref refcount;
+};
+
+int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file);
+
+int ethosu_job_init(struct ethosu_device *dev);
+void ethosu_job_fini(struct ethosu_device *dev);
+int ethosu_job_open(struct ethosu_file_priv *ethosu_priv);
+void ethosu_job_close(struct ethosu_file_priv *ethosu_priv);
+
+#endif
diff --git a/drivers/accel/ivpu/Makefile b/drivers/accel/ivpu/Makefile
index 1029e0bab061..dbf76b8a5b4c 100644
--- a/drivers/accel/ivpu/Makefile
+++ b/drivers/accel/ivpu/Makefile
@@ -6,6 +6,7 @@ intel_vpu-y := \
ivpu_fw.o \
ivpu_fw_log.o \
ivpu_gem.o \
+ ivpu_gem_userptr.o \
ivpu_hw.o \
ivpu_hw_btrs.o \
ivpu_hw_ip.o \
diff --git a/drivers/accel/ivpu/ivpu_debugfs.c b/drivers/accel/ivpu/ivpu_debugfs.c
index cd24ccd20ba6..3bd85ee6c26b 100644
--- a/drivers/accel/ivpu/ivpu_debugfs.c
+++ b/drivers/accel/ivpu/ivpu_debugfs.c
@@ -398,35 +398,25 @@ static int dct_active_set(void *data, u64 active_percent)
DEFINE_DEBUGFS_ATTRIBUTE(ivpu_dct_fops, dct_active_get, dct_active_set, "%llu\n");
+static void print_priority_band(struct seq_file *s, struct ivpu_hw_info *hw,
+ int band, const char *name)
+{
+ seq_printf(s, "%-9s: grace_period %9u process_grace_period %9u process_quantum %9u\n",
+ name,
+ hw->hws.grace_period[band],
+ hw->hws.process_grace_period[band],
+ hw->hws.process_quantum[band]);
+}
+
static int priority_bands_show(struct seq_file *s, void *v)
{
struct ivpu_device *vdev = s->private;
struct ivpu_hw_info *hw = vdev->hw;
- for (int band = VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE;
- band < VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT; band++) {
- switch (band) {
- case VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE:
- seq_puts(s, "Idle: ");
- break;
-
- case VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL:
- seq_puts(s, "Normal: ");
- break;
-
- case VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS:
- seq_puts(s, "Focus: ");
- break;
-
- case VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME:
- seq_puts(s, "Realtime: ");
- break;
- }
-
- seq_printf(s, "grace_period %9u process_grace_period %9u process_quantum %9u\n",
- hw->hws.grace_period[band], hw->hws.process_grace_period[band],
- hw->hws.process_quantum[band]);
- }
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE, "Idle");
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL, "Normal");
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS, "Focus");
+ print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME, "Realtime");
return 0;
}
diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c
index 3289751b4757..3d6fccdefdd6 100644
--- a/drivers/accel/ivpu/ivpu_drv.c
+++ b/drivers/accel/ivpu/ivpu_drv.c
@@ -57,7 +57,7 @@ MODULE_PARM_DESC(pll_max_ratio, "Maximum PLL ratio used to set NPU frequency");
int ivpu_sched_mode = IVPU_SCHED_MODE_AUTO;
module_param_named(sched_mode, ivpu_sched_mode, int, 0444);
-MODULE_PARM_DESC(sched_mode, "Scheduler mode: -1 - Use default scheduler, 0 - Use OS scheduler, 1 - Use HW scheduler");
+MODULE_PARM_DESC(sched_mode, "Scheduler mode: -1 - Use default scheduler, 0 - Use OS scheduler (supported on 27XX - 50XX), 1 - Use HW scheduler");
bool ivpu_disable_mmu_cont_pages;
module_param_named(disable_mmu_cont_pages, ivpu_disable_mmu_cont_pages, bool, 0444);
@@ -134,6 +134,8 @@ bool ivpu_is_capable(struct ivpu_device *vdev, u32 capability)
return true;
case DRM_IVPU_CAP_DMA_MEMORY_RANGE:
return true;
+ case DRM_IVPU_CAP_BO_CREATE_FROM_USERPTR:
+ return true;
case DRM_IVPU_CAP_MANAGE_CMDQ:
return vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW;
default:
@@ -200,6 +202,9 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f
case DRM_IVPU_PARAM_CAPABILITIES:
args->value = ivpu_is_capable(vdev, args->index);
break;
+ case DRM_IVPU_PARAM_PREEMPT_BUFFER_SIZE:
+ args->value = ivpu_fw_preempt_buf_size(vdev);
+ break;
default:
ret = -EINVAL;
break;
@@ -310,6 +315,7 @@ static const struct drm_ioctl_desc ivpu_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(IVPU_CMDQ_CREATE, ivpu_cmdq_create_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_CMDQ_DESTROY, ivpu_cmdq_destroy_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_CMDQ_SUBMIT, ivpu_cmdq_submit_ioctl, 0),
+ DRM_IOCTL_DEF_DRV(IVPU_BO_CREATE_FROM_USERPTR, ivpu_bo_create_from_userptr_ioctl, 0),
};
static int ivpu_wait_for_ready(struct ivpu_device *vdev)
@@ -377,8 +383,7 @@ int ivpu_boot(struct ivpu_device *vdev)
drm_WARN_ON(&vdev->drm, atomic_read(&vdev->job_timeout_counter));
drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa));
- /* Update boot params located at first 4KB of FW memory */
- ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem));
+ ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem_bp));
ret = ivpu_hw_boot_fw(vdev);
if (ret) {
@@ -450,6 +455,9 @@ int ivpu_shutdown(struct ivpu_device *vdev)
static const struct file_operations ivpu_fops = {
.owner = THIS_MODULE,
DRM_ACCEL_FOPS,
+#ifdef CONFIG_PROC_FS
+ .show_fdinfo = drm_show_fdinfo,
+#endif
};
static const struct drm_driver driver = {
@@ -464,6 +472,9 @@ static const struct drm_driver driver = {
.ioctls = ivpu_drm_ioctls,
.num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls),
.fops = &ivpu_fops,
+#ifdef CONFIG_PROC_FS
+ .show_fdinfo = drm_show_memory_stats,
+#endif
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
@@ -705,6 +716,7 @@ static struct pci_device_id ivpu_pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_LNL) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PTL_P) },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_WCL) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_NVL) },
{ }
};
MODULE_DEVICE_TABLE(pci, ivpu_pci_ids);
diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h
index 62ab1c654e63..5b34b6f50e69 100644
--- a/drivers/accel/ivpu/ivpu_drv.h
+++ b/drivers/accel/ivpu/ivpu_drv.h
@@ -27,6 +27,7 @@
#define PCI_DEVICE_ID_LNL 0x643e
#define PCI_DEVICE_ID_PTL_P 0xb03e
#define PCI_DEVICE_ID_WCL 0xfd3e
+#define PCI_DEVICE_ID_NVL 0xd71d
#define IVPU_HW_IP_37XX 37
#define IVPU_HW_IP_40XX 40
@@ -78,6 +79,7 @@
#define IVPU_DBG_KREF BIT(11)
#define IVPU_DBG_RPM BIT(12)
#define IVPU_DBG_MMU_MAP BIT(13)
+#define IVPU_DBG_IOCTL BIT(14)
#define ivpu_err(vdev, fmt, ...) \
drm_err(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
@@ -245,6 +247,8 @@ static inline int ivpu_hw_ip_gen(struct ivpu_device *vdev)
case PCI_DEVICE_ID_PTL_P:
case PCI_DEVICE_ID_WCL:
return IVPU_HW_IP_50XX;
+ case PCI_DEVICE_ID_NVL:
+ return IVPU_HW_IP_60XX;
default:
dump_stack();
ivpu_err(vdev, "Unknown NPU IP generation\n");
@@ -261,6 +265,7 @@ static inline int ivpu_hw_btrs_gen(struct ivpu_device *vdev)
case PCI_DEVICE_ID_LNL:
case PCI_DEVICE_ID_PTL_P:
case PCI_DEVICE_ID_WCL:
+ case PCI_DEVICE_ID_NVL:
return IVPU_HW_BTRS_LNL;
default:
dump_stack();
diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c
index 9db741695401..48386d2cddbb 100644
--- a/drivers/accel/ivpu/ivpu_fw.c
+++ b/drivers/accel/ivpu/ivpu_fw.c
@@ -17,15 +17,10 @@
#include "ivpu_ipc.h"
#include "ivpu_pm.h"
-#define FW_GLOBAL_MEM_START (2ull * SZ_1G)
-#define FW_GLOBAL_MEM_END (3ull * SZ_1G)
-#define FW_SHARED_MEM_SIZE SZ_256M /* Must be aligned to FW_SHARED_MEM_ALIGNMENT */
-#define FW_SHARED_MEM_ALIGNMENT SZ_128K /* VPU MTRR limitation */
-#define FW_RUNTIME_MAX_SIZE SZ_512M
#define FW_SHAVE_NN_MAX_SIZE SZ_2M
-#define FW_RUNTIME_MIN_ADDR (FW_GLOBAL_MEM_START)
-#define FW_RUNTIME_MAX_ADDR (FW_GLOBAL_MEM_END - FW_SHARED_MEM_SIZE)
#define FW_FILE_IMAGE_OFFSET (VPU_FW_HEADER_SIZE + FW_VERSION_HEADER_SIZE)
+#define FW_PREEMPT_BUF_MIN_SIZE SZ_4K
+#define FW_PREEMPT_BUF_MAX_SIZE SZ_32M
#define WATCHDOG_MSS_REDIRECT 32
#define WATCHDOG_NCE_REDIRECT 33
@@ -61,12 +56,14 @@ static struct {
{ IVPU_HW_IP_40XX, "intel/vpu/vpu_40xx_v0.0.bin" },
{ IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v1.bin" },
{ IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v0.0.bin" },
+ { IVPU_HW_IP_60XX, "intel/vpu/vpu_60xx_v1.bin" },
};
/* Production fw_names from the table above */
MODULE_FIRMWARE("intel/vpu/vpu_37xx_v1.bin");
MODULE_FIRMWARE("intel/vpu/vpu_40xx_v1.bin");
MODULE_FIRMWARE("intel/vpu/vpu_50xx_v1.bin");
+MODULE_FIRMWARE("intel/vpu/vpu_60xx_v1.bin");
static int ivpu_fw_request(struct ivpu_device *vdev)
{
@@ -131,9 +128,14 @@ ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const struct vpu_firmware_hea
return false;
}
-static bool is_within_range(u64 addr, size_t size, u64 range_start, size_t range_size)
+bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range)
{
- if (addr < range_start || addr + size > range_start + range_size)
+ u64 addr_end;
+
+ if (!range || check_add_overflow(addr, size, &addr_end))
+ return false;
+
+ if (addr < range->start || addr_end > range->end)
return false;
return true;
@@ -142,6 +144,12 @@ static bool is_within_range(u64 addr, size_t size, u64 range_start, size_t range
static u32
ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr)
{
+ if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX &&
+ ivpu_sched_mode == VPU_SCHEDULING_MODE_OS) {
+ ivpu_warn(vdev, "OS sched mode is not supported, using HW mode\n");
+ return VPU_SCHEDULING_MODE_HW;
+ }
+
if (ivpu_sched_mode != IVPU_SCHED_MODE_AUTO)
return ivpu_sched_mode;
@@ -151,11 +159,56 @@ ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_he
return VPU_SCHEDULING_MODE_HW;
}
+static void
+ivpu_preemption_config_parse(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr)
+{
+ struct ivpu_fw_info *fw = vdev->fw;
+ u32 primary_preempt_buf_size, secondary_preempt_buf_size;
+
+ if (fw_hdr->preemption_buffer_1_max_size)
+ primary_preempt_buf_size = fw_hdr->preemption_buffer_1_max_size;
+ else
+ primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size;
+
+ if (fw_hdr->preemption_buffer_2_max_size)
+ secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_max_size;
+ else
+ secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_size;
+
+ ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size, primary: %u, secondary: %u\n",
+ primary_preempt_buf_size, secondary_preempt_buf_size);
+
+ if (primary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE ||
+ secondary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE) {
+ ivpu_warn(vdev, "Preemption buffers size too small\n");
+ return;
+ }
+
+ if (primary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE ||
+ secondary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE) {
+ ivpu_warn(vdev, "Preemption buffers size too big\n");
+ return;
+ }
+
+ if (fw->sched_mode != VPU_SCHEDULING_MODE_HW)
+ return;
+
+ if (ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE)
+ return;
+
+ vdev->fw->primary_preempt_buf_size = ALIGN(primary_preempt_buf_size, PAGE_SIZE);
+ vdev->fw->secondary_preempt_buf_size = ALIGN(secondary_preempt_buf_size, PAGE_SIZE);
+}
+
static int ivpu_fw_parse(struct ivpu_device *vdev)
{
struct ivpu_fw_info *fw = vdev->fw;
const struct vpu_firmware_header *fw_hdr = (const void *)fw->file->data;
- u64 runtime_addr, image_load_addr, runtime_size, image_size;
+ struct ivpu_addr_range fw_image_range;
+ u64 boot_params_addr, boot_params_size;
+ u64 fw_version_addr, fw_version_size;
+ u64 runtime_addr, runtime_size;
+ u64 image_load_addr, image_size;
if (fw->file->size <= FW_FILE_IMAGE_OFFSET) {
ivpu_err(vdev, "Firmware file is too small: %zu\n", fw->file->size);
@@ -167,18 +220,37 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
return -EINVAL;
}
- runtime_addr = fw_hdr->boot_params_load_address;
- runtime_size = fw_hdr->runtime_size;
- image_load_addr = fw_hdr->image_load_address;
- image_size = fw_hdr->image_size;
+ boot_params_addr = fw_hdr->boot_params_load_address;
+ boot_params_size = SZ_4K;
- if (runtime_addr < FW_RUNTIME_MIN_ADDR || runtime_addr > FW_RUNTIME_MAX_ADDR) {
- ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx\n", runtime_addr);
+ if (!ivpu_is_within_range(boot_params_addr, boot_params_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid boot params address: 0x%llx\n", boot_params_addr);
return -EINVAL;
}
- if (runtime_size < fw->file->size || runtime_size > FW_RUNTIME_MAX_SIZE) {
- ivpu_err(vdev, "Invalid firmware runtime size: %llu\n", runtime_size);
+ fw_version_addr = fw_hdr->firmware_version_load_address;
+ fw_version_size = ALIGN(fw_hdr->firmware_version_size, SZ_4K);
+
+ if (fw_version_size != SZ_4K) {
+ ivpu_err(vdev, "Invalid firmware version size: %u\n",
+ fw_hdr->firmware_version_size);
+ return -EINVAL;
+ }
+
+ if (!ivpu_is_within_range(fw_version_addr, fw_version_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid firmware version address: 0x%llx\n", fw_version_addr);
+ return -EINVAL;
+ }
+
+ runtime_addr = fw_hdr->image_load_address;
+ runtime_size = fw_hdr->runtime_size - boot_params_size - fw_version_size;
+
+ image_load_addr = fw_hdr->image_load_address;
+ image_size = fw_hdr->image_size;
+
+ if (!ivpu_is_within_range(runtime_addr, runtime_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx and size %llu\n",
+ runtime_addr, runtime_size);
return -EINVAL;
}
@@ -187,23 +259,25 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
return -EINVAL;
}
- if (image_load_addr < runtime_addr ||
- image_load_addr + image_size > runtime_addr + runtime_size) {
- ivpu_err(vdev, "Invalid firmware load address size: 0x%llx and size %llu\n",
+ if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n",
image_load_addr, image_size);
return -EINVAL;
}
- if (fw_hdr->shave_nn_fw_size > FW_SHAVE_NN_MAX_SIZE) {
- ivpu_err(vdev, "SHAVE NN firmware is too big: %u\n", fw_hdr->shave_nn_fw_size);
+ if (ivpu_hw_range_init(vdev, &fw_image_range, image_load_addr, image_size))
return -EINVAL;
- }
- if (fw_hdr->entry_point < image_load_addr ||
- fw_hdr->entry_point >= image_load_addr + image_size) {
+ if (!ivpu_is_within_range(fw_hdr->entry_point, SZ_4K, &fw_image_range)) {
ivpu_err(vdev, "Invalid entry point: 0x%llx\n", fw_hdr->entry_point);
return -EINVAL;
}
+
+ if (fw_hdr->shave_nn_fw_size > FW_SHAVE_NN_MAX_SIZE) {
+ ivpu_err(vdev, "SHAVE NN firmware is too big: %u\n", fw_hdr->shave_nn_fw_size);
+ return -EINVAL;
+ }
+
ivpu_dbg(vdev, FW_BOOT, "Header version: 0x%x, format 0x%x\n",
fw_hdr->header_version, fw_hdr->image_format);
@@ -217,6 +291,10 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3))
return -EINVAL;
+ fw->boot_params_addr = boot_params_addr;
+ fw->boot_params_size = boot_params_size;
+ fw->fw_version_addr = fw_version_addr;
+ fw->fw_version_size = fw_version_size;
fw->runtime_addr = runtime_addr;
fw->runtime_size = runtime_size;
fw->image_load_offset = image_load_addr - runtime_addr;
@@ -235,22 +313,13 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
fw->sched_mode = ivpu_fw_sched_mode_select(vdev, fw_hdr);
ivpu_info(vdev, "Scheduler mode: %s\n", fw->sched_mode ? "HW" : "OS");
- if (fw_hdr->preemption_buffer_1_max_size)
- fw->primary_preempt_buf_size = fw_hdr->preemption_buffer_1_max_size;
- else
- fw->primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size;
+ ivpu_preemption_config_parse(vdev, fw_hdr);
+ ivpu_dbg(vdev, FW_BOOT, "Mid-inference preemption %s supported\n",
+ ivpu_fw_preempt_buf_size(vdev) ? "is" : "is not");
- if (fw_hdr->preemption_buffer_2_max_size)
- fw->secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_max_size;
- else
- fw->secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_size;
- ivpu_dbg(vdev, FW_BOOT, "Preemption buffer sizes: primary %u, secondary %u\n",
- fw->primary_preempt_buf_size, fw->secondary_preempt_buf_size);
-
- if (fw_hdr->ro_section_start_address && !is_within_range(fw_hdr->ro_section_start_address,
- fw_hdr->ro_section_size,
- fw_hdr->image_load_address,
- fw_hdr->image_size)) {
+ if (fw_hdr->ro_section_start_address &&
+ !ivpu_is_within_range(fw_hdr->ro_section_start_address, fw_hdr->ro_section_size,
+ &fw_image_range)) {
ivpu_err(vdev, "Invalid read-only section: start address 0x%llx, size %u\n",
fw_hdr->ro_section_start_address, fw_hdr->ro_section_size);
return -EINVAL;
@@ -259,12 +328,18 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
fw->read_only_addr = fw_hdr->ro_section_start_address;
fw->read_only_size = fw_hdr->ro_section_size;
- ivpu_dbg(vdev, FW_BOOT, "Size: file %lu image %u runtime %u shavenn %u\n",
- fw->file->size, fw->image_size, fw->runtime_size, fw->shave_nn_size);
- ivpu_dbg(vdev, FW_BOOT, "Address: runtime 0x%llx, load 0x%llx, entry point 0x%llx\n",
- fw->runtime_addr, image_load_addr, fw->entry_point);
+ ivpu_dbg(vdev, FW_BOOT, "Boot params: address 0x%llx, size %llu\n",
+ fw->boot_params_addr, fw->boot_params_size);
+ ivpu_dbg(vdev, FW_BOOT, "FW version: address 0x%llx, size %llu\n",
+ fw->fw_version_addr, fw->fw_version_size);
+ ivpu_dbg(vdev, FW_BOOT, "Runtime: address 0x%llx, size %u\n",
+ fw->runtime_addr, fw->runtime_size);
+ ivpu_dbg(vdev, FW_BOOT, "Image load offset: 0x%llx, size %u\n",
+ fw->image_load_offset, fw->image_size);
ivpu_dbg(vdev, FW_BOOT, "Read-only section: address 0x%llx, size %u\n",
fw->read_only_addr, fw->read_only_size);
+ ivpu_dbg(vdev, FW_BOOT, "FW entry point: 0x%llx\n", fw->entry_point);
+ ivpu_dbg(vdev, FW_BOOT, "SHAVE NN size: %u\n", fw->shave_nn_size);
return 0;
}
@@ -291,39 +366,33 @@ ivpu_fw_init_wa(struct ivpu_device *vdev)
IVPU_PRINT_WA(disable_d0i3_msg);
}
-static int ivpu_fw_update_global_range(struct ivpu_device *vdev)
-{
- struct ivpu_fw_info *fw = vdev->fw;
- u64 start = ALIGN(fw->runtime_addr + fw->runtime_size, FW_SHARED_MEM_ALIGNMENT);
- u64 size = FW_SHARED_MEM_SIZE;
-
- if (start + size > FW_GLOBAL_MEM_END) {
- ivpu_err(vdev, "No space for shared region, start %lld, size %lld\n", start, size);
- return -EINVAL;
- }
-
- ivpu_hw_range_init(&vdev->hw->ranges.global, start, size);
- return 0;
-}
-
static int ivpu_fw_mem_init(struct ivpu_device *vdev)
{
struct ivpu_fw_info *fw = vdev->fw;
- struct ivpu_addr_range fw_range;
int log_verb_size;
int ret;
- ret = ivpu_fw_update_global_range(vdev);
- if (ret)
- return ret;
+ fw->mem_bp = ivpu_bo_create_runtime(vdev, fw->boot_params_addr, fw->boot_params_size,
+ DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!fw->mem_bp) {
+ ivpu_err(vdev, "Failed to create firmware boot params memory buffer\n");
+ return -ENOMEM;
+ }
- fw_range.start = fw->runtime_addr;
- fw_range.end = fw->runtime_addr + fw->runtime_size;
- fw->mem = ivpu_bo_create(vdev, &vdev->gctx, &fw_range, fw->runtime_size,
- DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ fw->mem_fw_ver = ivpu_bo_create_runtime(vdev, fw->fw_version_addr, fw->fw_version_size,
+ DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
+ if (!fw->mem_fw_ver) {
+ ivpu_err(vdev, "Failed to create firmware version memory buffer\n");
+ ret = -ENOMEM;
+ goto err_free_bp;
+ }
+
+ fw->mem = ivpu_bo_create_runtime(vdev, fw->runtime_addr, fw->runtime_size,
+ DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!fw->mem) {
ivpu_err(vdev, "Failed to create firmware runtime memory buffer\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_free_fw_ver;
}
ret = ivpu_mmu_context_set_pages_ro(vdev, &vdev->gctx, fw->read_only_addr,
@@ -372,6 +441,10 @@ err_free_log_crit:
ivpu_bo_free(fw->mem_log_crit);
err_free_fw_mem:
ivpu_bo_free(fw->mem);
+err_free_fw_ver:
+ ivpu_bo_free(fw->mem_fw_ver);
+err_free_bp:
+ ivpu_bo_free(fw->mem_bp);
return ret;
}
@@ -387,10 +460,14 @@ static void ivpu_fw_mem_fini(struct ivpu_device *vdev)
ivpu_bo_free(fw->mem_log_verb);
ivpu_bo_free(fw->mem_log_crit);
ivpu_bo_free(fw->mem);
+ ivpu_bo_free(fw->mem_fw_ver);
+ ivpu_bo_free(fw->mem_bp);
fw->mem_log_verb = NULL;
fw->mem_log_crit = NULL;
fw->mem = NULL;
+ fw->mem_fw_ver = NULL;
+ fw->mem_bp = NULL;
}
int ivpu_fw_init(struct ivpu_device *vdev)
@@ -483,11 +560,6 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
ivpu_dbg(vdev, FW_BOOT, "boot_params.cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg = 0x%x\n",
boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg);
- ivpu_dbg(vdev, FW_BOOT, "boot_params.global_memory_allocator_base = 0x%llx\n",
- boot_params->global_memory_allocator_base);
- ivpu_dbg(vdev, FW_BOOT, "boot_params.global_memory_allocator_size = 0x%x\n",
- boot_params->global_memory_allocator_size);
-
ivpu_dbg(vdev, FW_BOOT, "boot_params.shave_nn_fw_base = 0x%llx\n",
boot_params->shave_nn_fw_base);
@@ -495,10 +567,6 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
boot_params->watchdog_irq_mss);
ivpu_dbg(vdev, FW_BOOT, "boot_params.watchdog_irq_nce = 0x%x\n",
boot_params->watchdog_irq_nce);
- ivpu_dbg(vdev, FW_BOOT, "boot_params.host_to_vpu_irq = 0x%x\n",
- boot_params->host_to_vpu_irq);
- ivpu_dbg(vdev, FW_BOOT, "boot_params.job_done_irq = 0x%x\n",
- boot_params->job_done_irq);
ivpu_dbg(vdev, FW_BOOT, "boot_params.host_version_id = 0x%x\n",
boot_params->host_version_id);
@@ -546,6 +614,8 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
boot_params->system_time_us);
ivpu_dbg(vdev, FW_BOOT, "boot_params.power_profile = 0x%x\n",
boot_params->power_profile);
+ ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_uses_ecc_mca_signal = 0x%x\n",
+ boot_params->vpu_uses_ecc_mca_signal);
}
void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params)
@@ -572,6 +642,7 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
return;
}
+ memset(boot_params, 0, sizeof(*boot_params));
vdev->pm->is_warmboot = false;
boot_params->magic = VPU_BOOT_PARAMS_MAGIC;
@@ -647,6 +718,8 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
boot_params->d0i3_entry_vpu_ts = 0;
if (IVPU_WA(disable_d0i2))
boot_params->power_profile |= BIT(1);
+ boot_params->vpu_uses_ecc_mca_signal =
+ ivpu_hw_uses_ecc_mca_signal(vdev) ? VPU_BOOT_MCA_ECC_BOTH : 0;
boot_params->system_time_us = ktime_to_us(ktime_get_real());
wmb(); /* Flush WC buffers after writing bootparams */
diff --git a/drivers/accel/ivpu/ivpu_fw.h b/drivers/accel/ivpu/ivpu_fw.h
index 7081913fb0dd..00945892b55e 100644
--- a/drivers/accel/ivpu/ivpu_fw.h
+++ b/drivers/accel/ivpu/ivpu_fw.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) 2020-2024 Intel Corporation
+ * Copyright (C) 2020-2025 Intel Corporation
*/
#ifndef __IVPU_FW_H__
@@ -19,10 +19,16 @@ struct ivpu_fw_info {
const struct firmware *file;
const char *name;
char version[FW_VERSION_STR_SIZE];
+ struct ivpu_bo *mem_bp;
+ struct ivpu_bo *mem_fw_ver;
struct ivpu_bo *mem;
struct ivpu_bo *mem_shave_nn;
struct ivpu_bo *mem_log_crit;
struct ivpu_bo *mem_log_verb;
+ u64 boot_params_addr;
+ u64 boot_params_size;
+ u64 fw_version_addr;
+ u64 fw_version_size;
u64 runtime_addr;
u32 runtime_size;
u64 image_load_offset;
@@ -42,6 +48,7 @@ struct ivpu_fw_info {
u64 last_heartbeat;
};
+bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range);
int ivpu_fw_init(struct ivpu_device *vdev);
void ivpu_fw_fini(struct ivpu_device *vdev);
void ivpu_fw_load(struct ivpu_device *vdev);
@@ -52,4 +59,9 @@ static inline bool ivpu_fw_is_cold_boot(struct ivpu_device *vdev)
return vdev->fw->entry_point == vdev->fw->cold_boot_entry_point;
}
+static inline u32 ivpu_fw_preempt_buf_size(struct ivpu_device *vdev)
+{
+ return vdev->fw->primary_preempt_buf_size + vdev->fw->secondary_preempt_buf_size;
+}
+
#endif /* __IVPU_FW_H__ */
diff --git a/drivers/accel/ivpu/ivpu_gem.c b/drivers/accel/ivpu/ivpu_gem.c
index 59cfcf3eaded..ece68f570b7e 100644
--- a/drivers/accel/ivpu/ivpu_gem.c
+++ b/drivers/accel/ivpu/ivpu_gem.c
@@ -15,6 +15,7 @@
#include <drm/drm_utils.h>
#include "ivpu_drv.h"
+#include "ivpu_fw.h"
#include "ivpu_gem.h"
#include "ivpu_hw.h"
#include "ivpu_mmu.h"
@@ -27,8 +28,8 @@ static const struct drm_gem_object_funcs ivpu_gem_funcs;
static inline void ivpu_dbg_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, const char *action)
{
ivpu_dbg(vdev, BO,
- "%6s: bo %8p vpu_addr %9llx size %8zu ctx %d has_pages %d dma_mapped %d mmu_mapped %d wc %d imported %d\n",
- action, bo, bo->vpu_addr, ivpu_bo_size(bo), bo->ctx_id,
+ "%6s: bo %8p size %9zu ctx %d vpu_addr %9llx pages %d sgt %d mmu_mapped %d wc %d imported %d\n",
+ action, bo, ivpu_bo_size(bo), bo->ctx_id, bo->vpu_addr,
(bool)bo->base.pages, (bool)bo->base.sgt, bo->mmu_mapped, bo->base.map_wc,
(bool)drm_gem_is_imported(&bo->base.base));
}
@@ -43,22 +44,47 @@ static inline void ivpu_bo_unlock(struct ivpu_bo *bo)
dma_resv_unlock(bo->base.base.resv);
}
+static struct sg_table *ivpu_bo_map_attachment(struct ivpu_device *vdev, struct ivpu_bo *bo)
+{
+ struct sg_table *sgt;
+
+ drm_WARN_ON(&vdev->drm, !bo->base.base.import_attach);
+
+ ivpu_bo_lock(bo);
+
+ sgt = bo->base.sgt;
+ if (!sgt) {
+ sgt = dma_buf_map_attachment(bo->base.base.import_attach, DMA_BIDIRECTIONAL);
+ if (IS_ERR(sgt))
+ ivpu_err(vdev, "Failed to map BO in IOMMU: %ld\n", PTR_ERR(sgt));
+ else
+ bo->base.sgt = sgt;
+ }
+
+ ivpu_bo_unlock(bo);
+
+ return sgt;
+}
+
/*
- * ivpu_bo_pin() - pin the backing physical pages and map them to VPU.
+ * ivpu_bo_bind() - pin the backing physical pages and map them to VPU.
*
* This function pins physical memory pages, then maps the physical pages
* to IOMMU address space and finally updates the VPU MMU page tables
* to allow the VPU to translate VPU address to IOMMU address.
*/
-int __must_check ivpu_bo_pin(struct ivpu_bo *bo)
+int __must_check ivpu_bo_bind(struct ivpu_bo *bo)
{
struct ivpu_device *vdev = ivpu_bo_to_vdev(bo);
struct sg_table *sgt;
int ret = 0;
- ivpu_dbg_bo(vdev, bo, "pin");
+ ivpu_dbg_bo(vdev, bo, "bind");
- sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
+ if (bo->base.base.import_attach)
+ sgt = ivpu_bo_map_attachment(vdev, bo);
+ else
+ sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
if (IS_ERR(sgt)) {
ret = PTR_ERR(sgt);
ivpu_err(vdev, "Failed to map BO in IOMMU: %d\n", ret);
@@ -70,7 +96,7 @@ int __must_check ivpu_bo_pin(struct ivpu_bo *bo)
if (!bo->mmu_mapped) {
drm_WARN_ON(&vdev->drm, !bo->ctx);
ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt,
- ivpu_bo_is_snooped(bo));
+ ivpu_bo_is_snooped(bo), ivpu_bo_is_read_only(bo));
if (ret) {
ivpu_err(vdev, "Failed to map BO in MMU: %d\n", ret);
goto unlock;
@@ -99,9 +125,9 @@ ivpu_bo_alloc_vpu_addr(struct ivpu_bo *bo, struct ivpu_mmu_context *ctx,
ret = ivpu_mmu_context_insert_node(ctx, range, ivpu_bo_size(bo), &bo->mm_node);
if (!ret) {
bo->ctx = ctx;
+ bo->ctx_id = ctx->id;
bo->vpu_addr = bo->mm_node.start;
- } else {
- ivpu_err(vdev, "Failed to add BO to context %u: %d\n", ctx->id, ret);
+ ivpu_dbg_bo(vdev, bo, "vaddr");
}
ivpu_bo_unlock(bo);
@@ -115,7 +141,7 @@ static void ivpu_bo_unbind_locked(struct ivpu_bo *bo)
{
struct ivpu_device *vdev = ivpu_bo_to_vdev(bo);
- lockdep_assert(dma_resv_held(bo->base.base.resv) || !kref_read(&bo->base.base.refcount));
+ dma_resv_assert_held(bo->base.base.resv);
if (bo->mmu_mapped) {
drm_WARN_ON(&vdev->drm, !bo->ctx);
@@ -130,13 +156,15 @@ static void ivpu_bo_unbind_locked(struct ivpu_bo *bo)
bo->ctx = NULL;
}
- if (drm_gem_is_imported(&bo->base.base))
- return;
-
if (bo->base.sgt) {
- dma_unmap_sgtable(vdev->drm.dev, bo->base.sgt, DMA_BIDIRECTIONAL, 0);
- sg_free_table(bo->base.sgt);
- kfree(bo->base.sgt);
+ if (bo->base.base.import_attach) {
+ dma_buf_unmap_attachment(bo->base.base.import_attach,
+ bo->base.sgt, DMA_BIDIRECTIONAL);
+ } else {
+ dma_unmap_sgtable(vdev->drm.dev, bo->base.sgt, DMA_BIDIRECTIONAL, 0);
+ sg_free_table(bo->base.sgt);
+ kfree(bo->base.sgt);
+ }
bo->base.sgt = NULL;
}
}
@@ -182,10 +210,11 @@ struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t siz
struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev,
struct dma_buf *dma_buf)
{
+ struct ivpu_device *vdev = to_ivpu_device(dev);
struct device *attach_dev = dev->dev;
struct dma_buf_attachment *attach;
- struct sg_table *sgt;
struct drm_gem_object *obj;
+ struct ivpu_bo *bo;
int ret;
attach = dma_buf_attach(dma_buf, attach_dev);
@@ -194,25 +223,25 @@ struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev,
get_dma_buf(dma_buf);
- sgt = dma_buf_map_attachment_unlocked(attach, DMA_BIDIRECTIONAL);
- if (IS_ERR(sgt)) {
- ret = PTR_ERR(sgt);
- goto fail_detach;
- }
-
- obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
+ obj = drm_gem_shmem_prime_import_sg_table(dev, attach, NULL);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
- goto fail_unmap;
+ goto fail_detach;
}
obj->import_attach = attach;
obj->resv = dma_buf->resv;
+ bo = to_ivpu_bo(obj);
+
+ mutex_lock(&vdev->bo_list_lock);
+ list_add_tail(&bo->bo_list_node, &vdev->bo_list);
+ mutex_unlock(&vdev->bo_list_lock);
+
+ ivpu_dbg(vdev, BO, "import: bo %8p size %9zu\n", bo, ivpu_bo_size(bo));
+
return obj;
-fail_unmap:
- dma_buf_unmap_attachment_unlocked(attach, sgt, DMA_BIDIRECTIONAL);
fail_detach:
dma_buf_detach(dma_buf, attach);
dma_buf_put(dma_buf);
@@ -220,7 +249,7 @@ fail_detach:
return ERR_PTR(ret);
}
-static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 flags, u32 ctx_id)
+static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 flags)
{
struct drm_gem_shmem_object *shmem;
struct ivpu_bo *bo;
@@ -238,7 +267,6 @@ static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 fla
return ERR_CAST(shmem);
bo = to_ivpu_bo(&shmem->base);
- bo->ctx_id = ctx_id;
bo->base.map_wc = flags & DRM_IVPU_BO_WC;
bo->flags = flags;
@@ -246,7 +274,7 @@ static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 fla
list_add_tail(&bo->bo_list_node, &vdev->bo_list);
mutex_unlock(&vdev->bo_list_lock);
- ivpu_dbg_bo(vdev, bo, "alloc");
+ ivpu_dbg(vdev, BO, " alloc: bo %8p size %9llu\n", bo, size);
return bo;
}
@@ -259,8 +287,8 @@ static int ivpu_gem_bo_open(struct drm_gem_object *obj, struct drm_file *file)
struct ivpu_addr_range *range;
if (bo->ctx) {
- ivpu_warn(vdev, "Can't add BO to ctx %u: already in ctx %u\n",
- file_priv->ctx.id, bo->ctx->id);
+ ivpu_dbg(vdev, IOCTL, "Can't add BO %pe to ctx %u: already in ctx %u\n",
+ bo, file_priv->ctx.id, bo->ctx->id);
return -EALREADY;
}
@@ -281,23 +309,41 @@ static void ivpu_gem_bo_free(struct drm_gem_object *obj)
ivpu_dbg_bo(vdev, bo, "free");
+ drm_WARN_ON(&vdev->drm, list_empty(&bo->bo_list_node));
+
mutex_lock(&vdev->bo_list_lock);
list_del(&bo->bo_list_node);
- mutex_unlock(&vdev->bo_list_lock);
drm_WARN_ON(&vdev->drm, !drm_gem_is_imported(&bo->base.base) &&
!dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ));
drm_WARN_ON(&vdev->drm, ivpu_bo_size(bo) == 0);
drm_WARN_ON(&vdev->drm, bo->base.vaddr);
+ ivpu_bo_lock(bo);
ivpu_bo_unbind_locked(bo);
+ ivpu_bo_unlock(bo);
+
+ mutex_unlock(&vdev->bo_list_lock);
+
drm_WARN_ON(&vdev->drm, bo->mmu_mapped);
drm_WARN_ON(&vdev->drm, bo->ctx);
drm_WARN_ON(obj->dev, refcount_read(&bo->base.pages_use_count) > 1);
+ drm_WARN_ON(obj->dev, bo->base.base.vma_node.vm_files.rb_node);
drm_gem_shmem_free(&bo->base);
}
+static enum drm_gem_object_status ivpu_gem_status(struct drm_gem_object *obj)
+{
+ struct ivpu_bo *bo = to_ivpu_bo(obj);
+ enum drm_gem_object_status status = 0;
+
+ if (ivpu_bo_is_resident(bo))
+ status |= DRM_GEM_OBJECT_RESIDENT;
+
+ return status;
+}
+
static const struct drm_gem_object_funcs ivpu_gem_funcs = {
.free = ivpu_gem_bo_free,
.open = ivpu_gem_bo_open,
@@ -308,6 +354,7 @@ static const struct drm_gem_object_funcs ivpu_gem_funcs = {
.vmap = drm_gem_shmem_object_vmap,
.vunmap = drm_gem_shmem_object_vunmap,
.mmap = drm_gem_shmem_object_mmap,
+ .status = ivpu_gem_status,
.vm_ops = &drm_gem_shmem_vm_ops,
};
@@ -320,25 +367,33 @@ int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *fi
struct ivpu_bo *bo;
int ret;
- if (args->flags & ~DRM_IVPU_BO_FLAGS)
+ if (args->flags & ~DRM_IVPU_BO_FLAGS) {
+ ivpu_dbg(vdev, IOCTL, "Invalid BO flags 0x%x\n", args->flags);
return -EINVAL;
+ }
- if (size == 0)
+ if (size == 0) {
+ ivpu_dbg(vdev, IOCTL, "Invalid BO size %llu\n", args->size);
return -EINVAL;
+ }
- bo = ivpu_bo_alloc(vdev, size, args->flags, file_priv->ctx.id);
+ bo = ivpu_bo_alloc(vdev, size, args->flags);
if (IS_ERR(bo)) {
- ivpu_err(vdev, "Failed to allocate BO: %pe (ctx %u size %llu flags 0x%x)",
+ ivpu_dbg(vdev, IOCTL, "Failed to allocate BO: %pe ctx %u size %llu flags 0x%x\n",
bo, file_priv->ctx.id, args->size, args->flags);
return PTR_ERR(bo);
}
+ drm_WARN_ON(&vdev->drm, bo->base.base.handle_count != 0);
+
ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
- if (ret)
- ivpu_err(vdev, "Failed to create handle for BO: %pe (ctx %u size %llu flags 0x%x)",
+ if (ret) {
+ ivpu_dbg(vdev, IOCTL, "Failed to create handle for BO: %pe ctx %u size %llu flags 0x%x\n",
bo, file_priv->ctx.id, args->size, args->flags);
- else
+ } else {
args->vpu_addr = bo->vpu_addr;
+ drm_WARN_ON(&vdev->drm, bo->base.base.handle_count != 1);
+ }
drm_gem_object_put(&bo->base.base);
@@ -360,18 +415,21 @@ ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(range->end));
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(size));
- bo = ivpu_bo_alloc(vdev, size, flags, IVPU_GLOBAL_CONTEXT_MMU_SSID);
+ bo = ivpu_bo_alloc(vdev, size, flags);
if (IS_ERR(bo)) {
- ivpu_err(vdev, "Failed to allocate BO: %pe (vpu_addr 0x%llx size %llu flags 0x%x)",
+ ivpu_err(vdev, "Failed to allocate BO: %pe vpu_addr 0x%llx size %llu flags 0x%x\n",
bo, range->start, size, flags);
return NULL;
}
ret = ivpu_bo_alloc_vpu_addr(bo, ctx, range);
- if (ret)
+ if (ret) {
+ ivpu_err(vdev, "Failed to allocate NPU address for BO: %pe ctx %u size %llu: %d\n",
+ bo, ctx->id, size, ret);
goto err_put;
+ }
- ret = ivpu_bo_pin(bo);
+ ret = ivpu_bo_bind(bo);
if (ret)
goto err_put;
@@ -391,6 +449,21 @@ err_put:
return NULL;
}
+struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags)
+{
+ struct ivpu_addr_range range;
+
+ if (!ivpu_is_within_range(addr, size, &vdev->hw->ranges.runtime)) {
+ ivpu_err(vdev, "Invalid runtime BO address 0x%llx size %llu\n", addr, size);
+ return NULL;
+ }
+
+ if (ivpu_hw_range_init(vdev, &range, addr, size))
+ return NULL;
+
+ return ivpu_bo_create(vdev, &vdev->gctx, &range, size, flags);
+}
+
struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags)
{
return ivpu_bo_create(vdev, &vdev->gctx, &vdev->hw->ranges.global, size, flags);
diff --git a/drivers/accel/ivpu/ivpu_gem.h b/drivers/accel/ivpu/ivpu_gem.h
index aa8ff14f7aae..0c3350f22b55 100644
--- a/drivers/accel/ivpu/ivpu_gem.h
+++ b/drivers/accel/ivpu/ivpu_gem.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) 2020-2023 Intel Corporation
+ * Copyright (C) 2020-2025 Intel Corporation
*/
#ifndef __IVPU_GEM_H__
#define __IVPU_GEM_H__
@@ -24,19 +24,22 @@ struct ivpu_bo {
bool mmu_mapped;
};
-int ivpu_bo_pin(struct ivpu_bo *bo);
+int ivpu_bo_bind(struct ivpu_bo *bo);
void ivpu_bo_unbind_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx);
struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size);
struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf);
struct ivpu_bo *ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
struct ivpu_addr_range *range, u64 size, u32 flags);
+struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags);
struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags);
void ivpu_bo_free(struct ivpu_bo *bo);
int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
+int ivpu_bo_create_from_userptr_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file);
void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p);
void ivpu_bo_list_print(struct drm_device *dev);
@@ -74,6 +77,16 @@ static inline bool ivpu_bo_is_snooped(struct ivpu_bo *bo)
return ivpu_bo_cache_mode(bo) == DRM_IVPU_BO_CACHED;
}
+static inline bool ivpu_bo_is_read_only(struct ivpu_bo *bo)
+{
+ return bo->flags & DRM_IVPU_BO_READ_ONLY;
+}
+
+static inline bool ivpu_bo_is_resident(struct ivpu_bo *bo)
+{
+ return !!bo->base.pages;
+}
+
static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr)
{
if (vpu_addr < bo->vpu_addr)
@@ -96,4 +109,9 @@ static inline u32 cpu_to_vpu_addr(struct ivpu_bo *bo, void *cpu_addr)
return bo->vpu_addr + (cpu_addr - ivpu_bo_vaddr(bo));
}
+static inline bool ivpu_bo_is_mappable(struct ivpu_bo *bo)
+{
+ return bo->flags & DRM_IVPU_BO_MAPPABLE;
+}
+
#endif /* __IVPU_GEM_H__ */
diff --git a/drivers/accel/ivpu/ivpu_gem_userptr.c b/drivers/accel/ivpu/ivpu_gem_userptr.c
new file mode 100644
index 000000000000..25ba606164c0
--- /dev/null
+++ b/drivers/accel/ivpu/ivpu_gem_userptr.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2025 Intel Corporation
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/err.h>
+#include <linux/highmem.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/capability.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gem.h>
+
+#include "ivpu_drv.h"
+#include "ivpu_gem.h"
+
+static struct sg_table *
+ivpu_gem_userptr_dmabuf_map(struct dma_buf_attachment *attachment,
+ enum dma_data_direction direction)
+{
+ struct sg_table *sgt = attachment->dmabuf->priv;
+ int ret;
+
+ ret = dma_map_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return sgt;
+}
+
+static void ivpu_gem_userptr_dmabuf_unmap(struct dma_buf_attachment *attachment,
+ struct sg_table *sgt,
+ enum dma_data_direction direction)
+{
+ dma_unmap_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);
+}
+
+static void ivpu_gem_userptr_dmabuf_release(struct dma_buf *dma_buf)
+{
+ struct sg_table *sgt = dma_buf->priv;
+ struct sg_page_iter page_iter;
+ struct page *page;
+
+ for_each_sgtable_page(sgt, &page_iter, 0) {
+ page = sg_page_iter_page(&page_iter);
+ unpin_user_page(page);
+ }
+
+ sg_free_table(sgt);
+ kfree(sgt);
+}
+
+static const struct dma_buf_ops ivpu_gem_userptr_dmabuf_ops = {
+ .map_dma_buf = ivpu_gem_userptr_dmabuf_map,
+ .unmap_dma_buf = ivpu_gem_userptr_dmabuf_unmap,
+ .release = ivpu_gem_userptr_dmabuf_release,
+};
+
+static struct dma_buf *
+ivpu_create_userptr_dmabuf(struct ivpu_device *vdev, void __user *user_ptr,
+ size_t size, uint32_t flags)
+{
+ struct dma_buf_export_info exp_info = {};
+ struct dma_buf *dma_buf;
+ struct sg_table *sgt;
+ struct page **pages;
+ unsigned long nr_pages = size >> PAGE_SHIFT;
+ unsigned int gup_flags = FOLL_LONGTERM;
+ int ret, i, pinned;
+
+ /* Add FOLL_WRITE only if the BO is not read-only */
+ if (!(flags & DRM_IVPU_BO_READ_ONLY))
+ gup_flags |= FOLL_WRITE;
+
+ pages = kvmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
+
+ pinned = pin_user_pages_fast((unsigned long)user_ptr, nr_pages, gup_flags, pages);
+ if (pinned < 0) {
+ ret = pinned;
+ ivpu_dbg(vdev, IOCTL, "Failed to pin user pages: %d\n", ret);
+ goto free_pages_array;
+ }
+
+ if (pinned != nr_pages) {
+ ivpu_dbg(vdev, IOCTL, "Pinned %d pages, expected %lu\n", pinned, nr_pages);
+ ret = -EFAULT;
+ goto unpin_pages;
+ }
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt) {
+ ret = -ENOMEM;
+ goto unpin_pages;
+ }
+
+ ret = sg_alloc_table_from_pages(sgt, pages, nr_pages, 0, size, GFP_KERNEL);
+ if (ret) {
+ ivpu_dbg(vdev, IOCTL, "Failed to create sg table: %d\n", ret);
+ goto free_sgt;
+ }
+
+ exp_info.exp_name = "ivpu_userptr_dmabuf";
+ exp_info.owner = THIS_MODULE;
+ exp_info.ops = &ivpu_gem_userptr_dmabuf_ops;
+ exp_info.size = size;
+ exp_info.flags = O_RDWR | O_CLOEXEC;
+ exp_info.priv = sgt;
+
+ dma_buf = dma_buf_export(&exp_info);
+ if (IS_ERR(dma_buf)) {
+ ret = PTR_ERR(dma_buf);
+ ivpu_dbg(vdev, IOCTL, "Failed to export userptr dma-buf: %d\n", ret);
+ goto free_sg_table;
+ }
+
+ kvfree(pages);
+ return dma_buf;
+
+free_sg_table:
+ sg_free_table(sgt);
+free_sgt:
+ kfree(sgt);
+unpin_pages:
+ for (i = 0; i < pinned; i++)
+ unpin_user_page(pages[i]);
+free_pages_array:
+ kvfree(pages);
+ return ERR_PTR(ret);
+}
+
+static struct ivpu_bo *
+ivpu_bo_create_from_userptr(struct ivpu_device *vdev, void __user *user_ptr,
+ size_t size, uint32_t flags)
+{
+ struct dma_buf *dma_buf;
+ struct drm_gem_object *obj;
+ struct ivpu_bo *bo;
+
+ dma_buf = ivpu_create_userptr_dmabuf(vdev, user_ptr, size, flags);
+ if (IS_ERR(dma_buf))
+ return ERR_CAST(dma_buf);
+
+ obj = ivpu_gem_prime_import(&vdev->drm, dma_buf);
+ if (IS_ERR(obj)) {
+ dma_buf_put(dma_buf);
+ return ERR_CAST(obj);
+ }
+
+ dma_buf_put(dma_buf);
+
+ bo = to_ivpu_bo(obj);
+ bo->flags = flags;
+
+ return bo;
+}
+
+int ivpu_bo_create_from_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
+{
+ struct drm_ivpu_bo_create_from_userptr *args = data;
+ struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = to_ivpu_device(dev);
+ void __user *user_ptr = u64_to_user_ptr(args->user_ptr);
+ struct ivpu_bo *bo;
+ int ret;
+
+ if (args->flags & ~(DRM_IVPU_BO_HIGH_MEM | DRM_IVPU_BO_DMA_MEM | DRM_IVPU_BO_READ_ONLY)) {
+ ivpu_dbg(vdev, IOCTL, "Invalid BO flags: 0x%x\n", args->flags);
+ return -EINVAL;
+ }
+
+ if (!args->user_ptr || !args->size) {
+ ivpu_dbg(vdev, IOCTL, "Userptr or size are zero: ptr %llx size %llu\n",
+ args->user_ptr, args->size);
+ return -EINVAL;
+ }
+
+ if (!PAGE_ALIGNED(args->user_ptr) || !PAGE_ALIGNED(args->size)) {
+ ivpu_dbg(vdev, IOCTL, "Userptr or size not page aligned: ptr %llx size %llu\n",
+ args->user_ptr, args->size);
+ return -EINVAL;
+ }
+
+ if (!access_ok(user_ptr, args->size)) {
+ ivpu_dbg(vdev, IOCTL, "Userptr is not accessible: ptr %llx size %llu\n",
+ args->user_ptr, args->size);
+ return -EFAULT;
+ }
+
+ bo = ivpu_bo_create_from_userptr(vdev, user_ptr, 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) {
+ ivpu_dbg(vdev, IOCTL, "Failed to create handle for BO: %pe ctx %u size %llu flags 0x%x\n",
+ bo, file_priv->ctx.id, args->size, args->flags);
+ } else {
+ ivpu_dbg(vdev, BO, "Created userptr BO: handle=%u vpu_addr=0x%llx size=%llu flags=0x%x\n",
+ args->handle, bo->vpu_addr, args->size, bo->flags);
+ args->vpu_addr = bo->vpu_addr;
+ }
+
+ drm_gem_object_put(&bo->base.base);
+
+ return ret;
+}
diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c
index 08dcc31b56f4..d69cd0d93569 100644
--- a/drivers/accel/ivpu/ivpu_hw.c
+++ b/drivers/accel/ivpu/ivpu_hw.c
@@ -8,6 +8,8 @@
#include "ivpu_hw_btrs.h"
#include "ivpu_hw_ip.h"
+#include <asm/msr-index.h>
+#include <asm/msr.h>
#include <linux/dmi.h>
#include <linux/fault-inject.h>
#include <linux/pm_runtime.h>
@@ -20,6 +22,10 @@ module_param_named_unsafe(fail_hw, ivpu_fail_hw, charp, 0444);
MODULE_PARM_DESC(fail_hw, "<interval>,<probability>,<space>,<times>");
#endif
+#define FW_SHARED_MEM_ALIGNMENT SZ_512K /* VPU MTRR limitation */
+
+#define ECC_MCA_SIGNAL_ENABLE_MASK 0xff
+
static char *platform_to_str(u32 platform)
{
switch (platform) {
@@ -147,19 +153,39 @@ static void priority_bands_init(struct ivpu_device *vdev)
vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 200000;
}
+int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start, u64 size)
+{
+ u64 end;
+
+ if (!range || check_add_overflow(start, size, &end)) {
+ ivpu_err(vdev, "Invalid range: start 0x%llx size %llu\n", start, size);
+ return -EINVAL;
+ }
+
+ range->start = start;
+ range->end = end;
+
+ return 0;
+}
+
static void memory_ranges_init(struct ivpu_device *vdev)
{
if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
- ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M);
- ivpu_hw_range_init(&vdev->hw->ranges.user, 0x88000000, 511 * SZ_1M);
- ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x180000000, SZ_2G);
- ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_128G);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x84800000, SZ_64M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0xa0000000, 511 * SZ_1M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x180000000, SZ_2G);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.dma, 0x200000000, SZ_128G);
} else {
- ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M);
- ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x80000000, SZ_2G);
- ivpu_hw_range_init(&vdev->hw->ranges.user, 0x100000000, SZ_256G);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x80000000, SZ_64M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x80000000, SZ_2G);
+ ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0x100000000, SZ_256G);
vdev->hw->ranges.dma = vdev->hw->ranges.user;
}
+
+ drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vdev->hw->ranges.global.start,
+ FW_SHARED_MEM_ALIGNMENT));
}
static int wp_enable(struct ivpu_device *vdev)
@@ -373,3 +399,22 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr)
pm_runtime_mark_last_busy(vdev->drm.dev);
return IRQ_HANDLED;
}
+
+bool ivpu_hw_uses_ecc_mca_signal(struct ivpu_device *vdev)
+{
+ unsigned long long msr_integrity_caps;
+ int ret;
+
+ if (ivpu_hw_ip_gen(vdev) < IVPU_HW_IP_50XX)
+ return false;
+
+ ret = rdmsrq_safe(MSR_INTEGRITY_CAPS, &msr_integrity_caps);
+ if (ret) {
+ ivpu_warn(vdev, "Error reading MSR_INTEGRITY_CAPS: %d", ret);
+ return false;
+ }
+
+ ivpu_dbg(vdev, MISC, "MSR_INTEGRITY_CAPS: 0x%llx\n", msr_integrity_caps);
+
+ return msr_integrity_caps & ECC_MCA_SIGNAL_ENABLE_MASK;
+}
diff --git a/drivers/accel/ivpu/ivpu_hw.h b/drivers/accel/ivpu/ivpu_hw.h
index d79668fe1609..b6d0f0d0dccc 100644
--- a/drivers/accel/ivpu/ivpu_hw.h
+++ b/drivers/accel/ivpu/ivpu_hw.h
@@ -21,6 +21,7 @@ struct ivpu_hw_info {
bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq);
} irq;
struct {
+ struct ivpu_addr_range runtime;
struct ivpu_addr_range global;
struct ivpu_addr_range user;
struct ivpu_addr_range shave;
@@ -51,6 +52,8 @@ struct ivpu_hw_info {
};
int ivpu_hw_init(struct ivpu_device *vdev);
+int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start,
+ u64 size);
int ivpu_hw_power_up(struct ivpu_device *vdev);
int ivpu_hw_power_down(struct ivpu_device *vdev);
int ivpu_hw_reset(struct ivpu_device *vdev);
@@ -60,6 +63,7 @@ void ivpu_irq_handlers_init(struct ivpu_device *vdev);
void ivpu_hw_irq_enable(struct ivpu_device *vdev);
void ivpu_hw_irq_disable(struct ivpu_device *vdev);
irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr);
+bool ivpu_hw_uses_ecc_mca_signal(struct ivpu_device *vdev);
static inline u32 ivpu_hw_btrs_irq_handler(struct ivpu_device *vdev, int irq)
{
@@ -71,12 +75,6 @@ static inline u32 ivpu_hw_ip_irq_handler(struct ivpu_device *vdev, int irq)
return vdev->hw->irq.ip_irq_handler(vdev, irq);
}
-static inline void ivpu_hw_range_init(struct ivpu_addr_range *range, u64 start, u64 size)
-{
- range->start = start;
- range->end = start + size;
-}
-
static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range)
{
return range->end - range->start;
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c
index afdb3b2aa72a..06e65c592618 100644
--- a/drivers/accel/ivpu/ivpu_hw_btrs.c
+++ b/drivers/accel/ivpu/ivpu_hw_btrs.c
@@ -321,6 +321,14 @@ static int wait_for_pll_lock(struct ivpu_device *vdev, bool enable)
return REGB_POLL_FLD(VPU_HW_BTRS_MTL_PLL_STATUS, LOCK, exp_val, PLL_TIMEOUT_US);
}
+static int wait_for_cdyn_deassert(struct ivpu_device *vdev)
+{
+ if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
+ return 0;
+
+ return REGB_POLL_FLD(VPU_HW_BTRS_LNL_CDYN, CDYN, 0, PLL_TIMEOUT_US);
+}
+
int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
{
struct wp_request wp;
@@ -354,6 +362,14 @@ int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
return ret;
}
+ if (!enable) {
+ ret = wait_for_cdyn_deassert(vdev);
+ if (ret) {
+ ivpu_err(vdev, "Timed out waiting for CDYN deassert\n");
+ return ret;
+ }
+ }
+
return 0;
}
@@ -673,7 +689,7 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status)) {
ivpu_dbg(vdev, IRQ, "Survivability IRQ\n");
- queue_work(system_wq, &vdev->irq_dct_work);
+ queue_work(system_percpu_wq, &vdev->irq_dct_work);
}
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
@@ -752,7 +768,7 @@ int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable)
}
}
-void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent)
+void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u8 active_percent)
{
u32 val = 0;
u32 cmd = enable ? DCT_ENABLE : DCT_DISABLE;
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.h b/drivers/accel/ivpu/ivpu_hw_btrs.h
index 032c384ac3d4..c4c10e22f30f 100644
--- a/drivers/accel/ivpu/ivpu_hw_btrs.h
+++ b/drivers/accel/ivpu/ivpu_hw_btrs.h
@@ -36,7 +36,7 @@ u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev);
bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq);
bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq);
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable);
-void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent);
+void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u8 active_percent);
u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_telemetry_size_get(struct ivpu_device *vdev);
u32 ivpu_hw_btrs_telemetry_enable_get(struct ivpu_device *vdev);
diff --git a/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h b/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h
index fff2ef2cada6..a81a9ba540fa 100644
--- a/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h
+++ b/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h
@@ -74,6 +74,9 @@
#define VPU_HW_BTRS_LNL_PLL_FREQ 0x00000148u
#define VPU_HW_BTRS_LNL_PLL_FREQ_RATIO_MASK GENMASK(15, 0)
+#define VPU_HW_BTRS_LNL_CDYN 0x0000014cu
+#define VPU_HW_BTRS_LNL_CDYN_CDYN_MASK GENMASK(15, 0)
+
#define VPU_HW_BTRS_LNL_TILE_FUSE 0x00000150u
#define VPU_HW_BTRS_LNL_TILE_FUSE_VALID_MASK BIT_MASK(0)
#define VPU_HW_BTRS_LNL_TILE_FUSE_CONFIG_MASK GENMASK(6, 1)
diff --git a/drivers/accel/ivpu/ivpu_hw_ip.c b/drivers/accel/ivpu/ivpu_hw_ip.c
index 2bf9882ab52e..06aa1e7dc50b 100644
--- a/drivers/accel/ivpu/ivpu_hw_ip.c
+++ b/drivers/accel/ivpu/ivpu_hw_ip.c
@@ -691,6 +691,13 @@ static void pwr_island_delay_set(struct ivpu_device *vdev)
status = high ? 46 : 3;
break;
+ case PCI_DEVICE_ID_NVL:
+ post = high ? 198 : 17;
+ post1 = 0;
+ post2 = high ? 198 : 17;
+ status = 0;
+ break;
+
default:
dump_stack();
ivpu_err(vdev, "Unknown device ID\n");
@@ -889,6 +896,9 @@ static int soc_cpu_drive_40xx(struct ivpu_device *vdev, bool enable)
static int soc_cpu_enable(struct ivpu_device *vdev)
{
+ if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX)
+ return 0;
+
return soc_cpu_drive_40xx(vdev, true);
}
diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c
index 5f00809d448a..1f13bf95b2b3 100644
--- a/drivers/accel/ivpu/ivpu_ipc.c
+++ b/drivers/accel/ivpu/ivpu_ipc.c
@@ -459,7 +459,7 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev)
}
}
- queue_work(system_wq, &vdev->irq_ipc_work);
+ queue_work(system_percpu_wq, &vdev->irq_ipc_work);
}
void ivpu_ipc_irq_work_fn(struct work_struct *work)
diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c
index 060f1fc031d3..4f8564e2878a 100644
--- a/drivers/accel/ivpu/ivpu_job.c
+++ b/drivers/accel/ivpu/ivpu_job.c
@@ -34,22 +34,20 @@ static void ivpu_cmdq_ring_db(struct ivpu_device *vdev, struct ivpu_cmdq *cmdq)
static int ivpu_preemption_buffers_create(struct ivpu_device *vdev,
struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
{
- u64 primary_size = ALIGN(vdev->fw->primary_preempt_buf_size, PAGE_SIZE);
- u64 secondary_size = ALIGN(vdev->fw->secondary_preempt_buf_size, PAGE_SIZE);
-
- if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW ||
- ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE)
+ if (ivpu_fw_preempt_buf_size(vdev) == 0)
return 0;
cmdq->primary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.user,
- primary_size, DRM_IVPU_BO_WC);
+ vdev->fw->primary_preempt_buf_size,
+ DRM_IVPU_BO_WC);
if (!cmdq->primary_preempt_buf) {
ivpu_err(vdev, "Failed to create primary preemption buffer\n");
return -ENOMEM;
}
cmdq->secondary_preempt_buf = ivpu_bo_create(vdev, &file_priv->ctx, &vdev->hw->ranges.dma,
- secondary_size, DRM_IVPU_BO_WC);
+ vdev->fw->secondary_preempt_buf_size,
+ DRM_IVPU_BO_WC);
if (!cmdq->secondary_preempt_buf) {
ivpu_err(vdev, "Failed to create secondary preemption buffer\n");
goto err_free_primary;
@@ -66,20 +64,39 @@ err_free_primary:
static void ivpu_preemption_buffers_free(struct ivpu_device *vdev,
struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
{
- if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW)
- return;
-
if (cmdq->primary_preempt_buf)
ivpu_bo_free(cmdq->primary_preempt_buf);
if (cmdq->secondary_preempt_buf)
ivpu_bo_free(cmdq->secondary_preempt_buf);
}
+static int ivpu_preemption_job_init(struct ivpu_device *vdev, struct ivpu_file_priv *file_priv,
+ struct ivpu_cmdq *cmdq, struct ivpu_job *job)
+{
+ int ret;
+
+ /* Use preemption buffer provided by the user space */
+ if (job->primary_preempt_buf)
+ return 0;
+
+ if (!cmdq->primary_preempt_buf) {
+ /* Allocate per command queue preemption buffers */
+ ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq);
+ if (ret)
+ return ret;
+ }
+
+ /* Use preemption buffers allocated by the kernel */
+ job->primary_preempt_buf = cmdq->primary_preempt_buf;
+ job->secondary_preempt_buf = cmdq->secondary_preempt_buf;
+
+ return 0;
+}
+
static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
{
struct ivpu_device *vdev = file_priv->vdev;
struct ivpu_cmdq *cmdq;
- int ret;
cmdq = kzalloc(sizeof(*cmdq), GFP_KERNEL);
if (!cmdq)
@@ -89,10 +106,6 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
if (!cmdq->mem)
goto err_free_cmdq;
- ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq);
- if (ret)
- ivpu_warn(vdev, "Failed to allocate preemption buffers, preemption limited\n");
-
return cmdq;
err_free_cmdq:
@@ -219,11 +232,13 @@ static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *
ret = ivpu_jsm_register_db(vdev, file_priv->ctx.id, cmdq->db_id,
cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
- if (!ret)
+ if (!ret) {
ivpu_dbg(vdev, JOB, "DB %d registered to cmdq %d ctx %d priority %d\n",
cmdq->db_id, cmdq->id, file_priv->ctx.id, cmdq->priority);
- else
+ } else {
xa_erase(&vdev->db_xa, cmdq->db_id);
+ cmdq->db_id = 0;
+ }
return ret;
}
@@ -333,7 +348,7 @@ static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u32
cmdq = xa_load(&file_priv->cmdq_xa, cmdq_id);
if (!cmdq) {
- ivpu_warn_ratelimited(vdev, "Failed to find command queue with ID: %u\n", cmdq_id);
+ ivpu_dbg(vdev, IOCTL, "Failed to find command queue with ID: %u\n", cmdq_id);
return NULL;
}
@@ -427,17 +442,14 @@ static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job)
if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_SUBMISSION))
entry->flags = VPU_JOB_FLAGS_NULL_SUBMISSION_MASK;
- if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
- if (cmdq->primary_preempt_buf) {
- entry->primary_preempt_buf_addr = cmdq->primary_preempt_buf->vpu_addr;
- entry->primary_preempt_buf_size = ivpu_bo_size(cmdq->primary_preempt_buf);
- }
+ if (job->primary_preempt_buf) {
+ entry->primary_preempt_buf_addr = job->primary_preempt_buf->vpu_addr;
+ entry->primary_preempt_buf_size = ivpu_bo_size(job->primary_preempt_buf);
+ }
- if (cmdq->secondary_preempt_buf) {
- entry->secondary_preempt_buf_addr = cmdq->secondary_preempt_buf->vpu_addr;
- entry->secondary_preempt_buf_size =
- ivpu_bo_size(cmdq->secondary_preempt_buf);
- }
+ if (job->secondary_preempt_buf) {
+ entry->secondary_preempt_buf_addr = job->secondary_preempt_buf->vpu_addr;
+ entry->secondary_preempt_buf_size = ivpu_bo_size(job->secondary_preempt_buf);
}
wmb(); /* Ensure that tail is updated after filling entry */
@@ -522,7 +534,7 @@ ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count)
job->bo_count = bo_count;
job->done_fence = ivpu_fence_create(vdev);
if (!job->done_fence) {
- ivpu_warn_ratelimited(vdev, "Failed to create a fence\n");
+ ivpu_err(vdev, "Failed to create a fence\n");
goto err_free_job;
}
@@ -552,21 +564,26 @@ static struct ivpu_job *ivpu_job_remove_from_submitted_jobs(struct ivpu_device *
return job;
}
-static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
+bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status)
{
- struct ivpu_job *job;
-
lockdep_assert_held(&vdev->submitted_jobs_lock);
- job = xa_load(&vdev->submitted_jobs_xa, job_id);
- if (!job)
- return -ENOENT;
+ switch (job_status) {
+ case VPU_JSM_STATUS_PROCESSING_ERR:
+ case VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN ... VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX:
+ {
+ struct ivpu_job *job = xa_load(&vdev->submitted_jobs_xa, job_id);
- if (job_status == VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW) {
+ if (!job)
+ return false;
+
+ /* Trigger an engine reset */
guard(mutex)(&job->file_priv->lock);
+ job->job_status = job_status;
+
if (job->file_priv->has_mmu_faults)
- return 0;
+ return false;
/*
* Mark context as faulty and defer destruction of the job to jobs abort thread
@@ -574,23 +591,43 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
* status and ensure both are handled in the same way
*/
job->file_priv->has_mmu_faults = true;
- queue_work(system_wq, &vdev->context_abort_work);
- return 0;
+ queue_work(system_percpu_wq, &vdev->context_abort_work);
+ return true;
}
+ default:
+ /* Complete job with error status, engine reset not required */
+ break;
+ }
+
+ return false;
+}
- job = ivpu_job_remove_from_submitted_jobs(vdev, job_id);
+static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status)
+{
+ struct ivpu_job *job;
+
+ lockdep_assert_held(&vdev->submitted_jobs_lock);
+
+ job = xa_load(&vdev->submitted_jobs_xa, job_id);
if (!job)
return -ENOENT;
- if (job->file_priv->has_mmu_faults)
- job_status = DRM_IVPU_JOB_STATUS_ABORTED;
+ ivpu_job_remove_from_submitted_jobs(vdev, job_id);
- job->bos[CMD_BUF_IDX]->job_status = job_status;
+ if (job->job_status == VPU_JSM_STATUS_SUCCESS) {
+ if (job->file_priv->has_mmu_faults)
+ job->job_status = DRM_IVPU_JOB_STATUS_ABORTED;
+ else
+ job->job_status = job_status;
+ }
+
+ job->bos[CMD_BUF_IDX]->job_status = job->job_status;
dma_fence_signal(job->done_fence);
trace_job("done", job);
ivpu_dbg(vdev, JOB, "Job complete: id %3u ctx %2d cmdq_id %u engine %d status 0x%x\n",
- job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx, job_status);
+ job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx,
+ job->job_status);
ivpu_job_destroy(job);
ivpu_stop_job_timeout_detection(vdev);
@@ -650,7 +687,6 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
else
cmdq = ivpu_cmdq_acquire(file_priv, cmdq_id);
if (!cmdq) {
- ivpu_warn_ratelimited(vdev, "Failed to get job queue, ctx %d\n", file_priv->ctx.id);
ret = -EINVAL;
goto err_unlock;
}
@@ -661,6 +697,13 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
goto err_unlock;
}
+ ret = ivpu_preemption_job_init(vdev, file_priv, cmdq, job);
+ if (ret) {
+ ivpu_err(vdev, "Failed to initialize preemption buffers for job %d: %d\n",
+ job->job_id, ret);
+ goto err_unlock;
+ }
+
job->cmdq_id = cmdq->id;
is_first_job = xa_empty(&vdev->submitted_jobs_xa);
@@ -714,7 +757,7 @@ err_unlock:
static int
ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32 *buf_handles,
- u32 buf_count, u32 commands_offset)
+ u32 buf_count, u32 commands_offset, u32 preempt_buffer_index)
{
struct ivpu_file_priv *file_priv = job->file_priv;
struct ivpu_device *vdev = file_priv->vdev;
@@ -727,40 +770,58 @@ ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32
for (i = 0; i < buf_count; i++) {
struct drm_gem_object *obj = drm_gem_object_lookup(file, buf_handles[i]);
- if (!obj)
+ if (!obj) {
+ ivpu_dbg(vdev, IOCTL, "Failed to lookup GEM object with handle %u\n",
+ buf_handles[i]);
return -ENOENT;
+ }
job->bos[i] = to_ivpu_bo(obj);
- ret = ivpu_bo_pin(job->bos[i]);
+ ret = ivpu_bo_bind(job->bos[i]);
if (ret)
return ret;
}
bo = job->bos[CMD_BUF_IDX];
if (!dma_resv_test_signaled(bo->base.base.resv, DMA_RESV_USAGE_READ)) {
- ivpu_warn(vdev, "Buffer is already in use\n");
+ ivpu_dbg(vdev, IOCTL, "Buffer is already in use by another job\n");
return -EBUSY;
}
if (commands_offset >= ivpu_bo_size(bo)) {
- ivpu_warn(vdev, "Invalid command buffer offset %u\n", commands_offset);
+ ivpu_dbg(vdev, IOCTL, "Invalid commands offset %u for buffer size %zu\n",
+ commands_offset, ivpu_bo_size(bo));
return -EINVAL;
}
job->cmd_buf_vpu_addr = bo->vpu_addr + commands_offset;
+ if (preempt_buffer_index) {
+ struct ivpu_bo *preempt_bo = job->bos[preempt_buffer_index];
+
+ if (ivpu_bo_size(preempt_bo) < ivpu_fw_preempt_buf_size(vdev)) {
+ ivpu_dbg(vdev, IOCTL, "Preemption buffer is too small\n");
+ return -EINVAL;
+ }
+ if (ivpu_bo_is_mappable(preempt_bo)) {
+ ivpu_dbg(vdev, IOCTL, "Preemption buffer cannot be mappable\n");
+ return -EINVAL;
+ }
+ job->primary_preempt_buf = preempt_bo;
+ }
+
ret = drm_gem_lock_reservations((struct drm_gem_object **)job->bos, buf_count,
&acquire_ctx);
if (ret) {
- ivpu_warn(vdev, "Failed to lock reservations: %d\n", ret);
+ ivpu_warn_ratelimited(vdev, "Failed to lock reservations: %d\n", ret);
return ret;
}
for (i = 0; i < buf_count; i++) {
ret = dma_resv_reserve_fences(job->bos[i]->base.base.resv, 1);
if (ret) {
- ivpu_warn(vdev, "Failed to reserve fences: %d\n", ret);
+ ivpu_warn_ratelimited(vdev, "Failed to reserve fences: %d\n", ret);
goto unlock_reservations;
}
}
@@ -780,7 +841,7 @@ unlock_reservations:
static int ivpu_submit(struct drm_file *file, struct ivpu_file_priv *file_priv, u32 cmdq_id,
u32 buffer_count, u32 engine, void __user *buffers_ptr, u32 cmds_offset,
- u8 priority)
+ u32 preempt_buffer_index, u8 priority)
{
struct ivpu_device *vdev = file_priv->vdev;
struct ivpu_job *job;
@@ -807,16 +868,14 @@ static int ivpu_submit(struct drm_file *file, struct ivpu_file_priv *file_priv,
job = ivpu_job_create(file_priv, engine, buffer_count);
if (!job) {
- ivpu_err(vdev, "Failed to create job\n");
ret = -ENOMEM;
goto err_exit_dev;
}
- ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, buffer_count, cmds_offset);
- if (ret) {
- ivpu_err(vdev, "Failed to prepare job: %d\n", ret);
+ ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, buffer_count, cmds_offset,
+ preempt_buffer_index);
+ if (ret)
goto err_destroy_job;
- }
down_read(&vdev->pm->reset_lock);
ret = ivpu_job_submit(job, priority, cmdq_id);
@@ -842,58 +901,91 @@ err_free_handles:
int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
struct drm_ivpu_submit *args = data;
u8 priority;
- if (args->engine != DRM_IVPU_ENGINE_COMPUTE)
+ if (args->engine != DRM_IVPU_ENGINE_COMPUTE) {
+ ivpu_dbg(vdev, IOCTL, "Invalid engine %d\n", args->engine);
return -EINVAL;
+ }
- if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME)
+ if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME) {
+ ivpu_dbg(vdev, IOCTL, "Invalid priority %d\n", args->priority);
return -EINVAL;
+ }
- if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT)
+ if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT) {
+ ivpu_dbg(vdev, IOCTL, "Invalid buffer count %u\n", args->buffer_count);
return -EINVAL;
+ }
- if (!IS_ALIGNED(args->commands_offset, 8))
+ if (!IS_ALIGNED(args->commands_offset, 8)) {
+ ivpu_dbg(vdev, IOCTL, "Invalid commands offset %u\n", args->commands_offset);
return -EINVAL;
+ }
- if (!file_priv->ctx.id)
+ if (!file_priv->ctx.id) {
+ ivpu_dbg(vdev, IOCTL, "Context not initialized\n");
return -EINVAL;
+ }
- if (file_priv->has_mmu_faults)
+ if (file_priv->has_mmu_faults) {
+ ivpu_dbg(vdev, IOCTL, "Context %u has MMU faults\n", file_priv->ctx.id);
return -EBADFD;
+ }
priority = ivpu_job_to_jsm_priority(args->priority);
return ivpu_submit(file, file_priv, 0, args->buffer_count, args->engine,
- (void __user *)args->buffers_ptr, args->commands_offset, priority);
+ (void __user *)args->buffers_ptr, args->commands_offset, 0, priority);
}
int ivpu_cmdq_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
+ struct ivpu_device *vdev = file_priv->vdev;
struct drm_ivpu_cmdq_submit *args = data;
- if (!ivpu_is_capable(file_priv->vdev, DRM_IVPU_CAP_MANAGE_CMDQ))
+ if (!ivpu_is_capable(file_priv->vdev, DRM_IVPU_CAP_MANAGE_CMDQ)) {
+ ivpu_dbg(vdev, IOCTL, "Command queue management not supported\n");
return -ENODEV;
+ }
- if (args->cmdq_id < IVPU_CMDQ_MIN_ID || args->cmdq_id > IVPU_CMDQ_MAX_ID)
+ if (args->cmdq_id < IVPU_CMDQ_MIN_ID || args->cmdq_id > IVPU_CMDQ_MAX_ID) {
+ ivpu_dbg(vdev, IOCTL, "Invalid command queue ID %u\n", args->cmdq_id);
return -EINVAL;
+ }
- if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT)
+ if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT) {
+ ivpu_dbg(vdev, IOCTL, "Invalid buffer count %u\n", args->buffer_count);
return -EINVAL;
+ }
- if (!IS_ALIGNED(args->commands_offset, 8))
+ if (args->preempt_buffer_index >= args->buffer_count) {
+ ivpu_dbg(vdev, IOCTL, "Invalid preemption buffer index %u\n",
+ args->preempt_buffer_index);
return -EINVAL;
+ }
- if (!file_priv->ctx.id)
+ if (!IS_ALIGNED(args->commands_offset, 8)) {
+ ivpu_dbg(vdev, IOCTL, "Invalid commands offset %u\n", args->commands_offset);
return -EINVAL;
+ }
- if (file_priv->has_mmu_faults)
+ if (!file_priv->ctx.id) {
+ ivpu_dbg(vdev, IOCTL, "Context not initialized\n");
+ return -EINVAL;
+ }
+
+ if (file_priv->has_mmu_faults) {
+ ivpu_dbg(vdev, IOCTL, "Context %u has MMU faults\n", file_priv->ctx.id);
return -EBADFD;
+ }
return ivpu_submit(file, file_priv, args->cmdq_id, args->buffer_count, VPU_ENGINE_COMPUTE,
- (void __user *)args->buffers_ptr, args->commands_offset, 0);
+ (void __user *)args->buffers_ptr, args->commands_offset,
+ args->preempt_buffer_index, 0);
}
int ivpu_cmdq_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
@@ -904,11 +996,15 @@ int ivpu_cmdq_create_ioctl(struct drm_device *dev, void *data, struct drm_file *
struct ivpu_cmdq *cmdq;
int ret;
- if (!ivpu_is_capable(vdev, DRM_IVPU_CAP_MANAGE_CMDQ))
+ if (!ivpu_is_capable(vdev, DRM_IVPU_CAP_MANAGE_CMDQ)) {
+ ivpu_dbg(vdev, IOCTL, "Command queue management not supported\n");
return -ENODEV;
+ }
- if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME)
+ if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME) {
+ ivpu_dbg(vdev, IOCTL, "Invalid priority %d\n", args->priority);
return -EINVAL;
+ }
ret = ivpu_rpm_get(vdev);
if (ret < 0)
@@ -936,8 +1032,10 @@ int ivpu_cmdq_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file
u32 cmdq_id = 0;
int ret;
- if (!ivpu_is_capable(vdev, DRM_IVPU_CAP_MANAGE_CMDQ))
+ if (!ivpu_is_capable(vdev, DRM_IVPU_CAP_MANAGE_CMDQ)) {
+ ivpu_dbg(vdev, IOCTL, "Command queue management not supported\n");
return -ENODEV;
+ }
ret = ivpu_rpm_get(vdev);
if (ret < 0)
@@ -984,7 +1082,9 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
payload = (struct vpu_ipc_msg_payload_job_done *)&jsm_msg->payload;
mutex_lock(&vdev->submitted_jobs_lock);
- ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
+ if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status))
+ /* No engine error, complete the job normally */
+ ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
mutex_unlock(&vdev->submitted_jobs_lock);
}
@@ -1012,7 +1112,7 @@ void ivpu_context_abort_work_fn(struct work_struct *work)
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
if (ivpu_jsm_reset_engine(vdev, 0))
- return;
+ goto runtime_put;
mutex_lock(&vdev->context_list_lock);
xa_for_each(&vdev->context_xa, ctx_id, file_priv) {
@@ -1036,7 +1136,7 @@ void ivpu_context_abort_work_fn(struct work_struct *work)
goto runtime_put;
if (ivpu_jsm_hws_resume_engine(vdev, 0))
- return;
+ goto runtime_put;
/*
* In hardware scheduling mode NPU already has stopped processing jobs
* and won't send us any further notifications, thus we have to free job related resources
@@ -1049,6 +1149,5 @@ void ivpu_context_abort_work_fn(struct work_struct *work)
mutex_unlock(&vdev->submitted_jobs_lock);
runtime_put:
- pm_runtime_mark_last_busy(vdev->drm.dev);
pm_runtime_put_autosuspend(vdev->drm.dev);
}
diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h
index 2e301c2eea7b..3ab61e6a5616 100644
--- a/drivers/accel/ivpu/ivpu_job.h
+++ b/drivers/accel/ivpu/ivpu_job.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) 2020-2024 Intel Corporation
+ * Copyright (C) 2020-2025 Intel Corporation
*/
#ifndef __IVPU_JOB_H__
@@ -15,12 +15,17 @@ struct ivpu_device;
struct ivpu_file_priv;
/**
- * struct ivpu_cmdq - Object representing device queue used to send jobs.
- * @jobq: Pointer to job queue memory shared with the device
- * @mem: Memory allocated for the job queue, shared with device
- * @entry_count Number of job entries in the queue
- * @db_id: Doorbell assigned to this job queue
- * @db_registered: True if doorbell is registered in device
+ * struct ivpu_cmdq - Represents a command queue for submitting jobs to the VPU.
+ * Tracks queue memory, preemption buffers, and metadata for job management.
+ * @jobq: Pointer to job queue memory shared with the device
+ * @primary_preempt_buf: Primary preemption buffer for this queue (optional)
+ * @secondary_preempt_buf: Secondary preemption buffer for this queue (optional)
+ * @mem: Memory allocated for the job queue, shared with device
+ * @entry_count: Number of job entries in the queue
+ * @id: Unique command queue ID
+ * @db_id: Doorbell ID assigned to this job queue
+ * @priority: Priority level of the command queue
+ * @is_legacy: True if this is a legacy command queue
*/
struct ivpu_cmdq {
struct vpu_job_queue *jobq;
@@ -35,16 +40,22 @@ struct ivpu_cmdq {
};
/**
- * struct ivpu_job - KMD object that represents batchbuffer / DMA buffer.
- * Each batch / DMA buffer is a job to be submitted and executed by the VPU FW.
- * This is a unit of execution, and be tracked by the job_id for
- * any status reporting from VPU FW through IPC JOB RET/DONE message.
- * @file_priv: The client that submitted this job
- * @job_id: Job ID for KMD tracking and job status reporting from VPU FW
- * @status: Status of the Job from IPC JOB RET/DONE message
- * @batch_buffer: CPU vaddr points to the batch buffer memory allocated for the job
- * @submit_status_offset: Offset within batch buffer where job completion handler
- will update the job status
+ * struct ivpu_job - Representing a batch or DMA buffer submitted to the VPU.
+ * Each job is a unit of execution, tracked by job_id for status reporting from VPU FW.
+ * The structure holds all resources and metadata needed for job submission, execution,
+ * and completion handling.
+ * @vdev: Pointer to the VPU device
+ * @file_priv: The client context that submitted this job
+ * @done_fence: Fence signaled when job completes
+ * @cmd_buf_vpu_addr: VPU address of the command buffer for this job
+ * @cmdq_id: Command queue ID used for submission
+ * @job_id: Unique job ID for tracking and status reporting
+ * @engine_idx: Engine index for job execution
+ * @job_status: Status reported by firmware for this job
+ * @primary_preempt_buf: Primary preemption buffer for job
+ * @secondary_preempt_buf: Secondary preemption buffer for job (optional)
+ * @bo_count: Number of buffer objects associated with this job
+ * @bos: Array of buffer objects used by the job (batch buffer is at index 0)
*/
struct ivpu_job {
struct ivpu_device *vdev;
@@ -54,6 +65,9 @@ struct ivpu_job {
u32 cmdq_id;
u32 job_id;
u32 engine_idx;
+ u32 job_status;
+ struct ivpu_bo *primary_preempt_buf;
+ struct ivpu_bo *secondary_preempt_buf;
size_t bo_count;
struct ivpu_bo *bos[] __counted_by(bo_count);
};
@@ -71,6 +85,7 @@ void ivpu_cmdq_abort_all_jobs(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id)
void ivpu_job_done_consumer_init(struct ivpu_device *vdev);
void ivpu_job_done_consumer_fini(struct ivpu_device *vdev);
+bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status);
void ivpu_context_abort_work_fn(struct work_struct *work);
void ivpu_jobs_abort_all(struct ivpu_device *vdev);
diff --git a/drivers/accel/ivpu/ivpu_mmu.c b/drivers/accel/ivpu/ivpu_mmu.c
index 5ea010568faa..e1baf6b64935 100644
--- a/drivers/accel/ivpu/ivpu_mmu.c
+++ b/drivers/accel/ivpu/ivpu_mmu.c
@@ -970,7 +970,7 @@ void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
}
}
- queue_work(system_wq, &vdev->context_abort_work);
+ queue_work(system_percpu_wq, &vdev->context_abort_work);
}
void ivpu_mmu_evtq_dump(struct ivpu_device *vdev)
diff --git a/drivers/accel/ivpu/ivpu_mmu_context.c b/drivers/accel/ivpu/ivpu_mmu_context.c
index f0267efa55aa..87ad593ef47d 100644
--- a/drivers/accel/ivpu/ivpu_mmu_context.c
+++ b/drivers/accel/ivpu/ivpu_mmu_context.c
@@ -430,7 +430,7 @@ static void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_a
int
ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
- u64 vpu_addr, struct sg_table *sgt, bool llc_coherent)
+ u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only)
{
size_t start_vpu_addr = vpu_addr;
struct scatterlist *sg;
@@ -450,6 +450,8 @@ ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
prot = IVPU_MMU_ENTRY_MAPPED;
if (llc_coherent)
prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT;
+ if (read_only)
+ prot |= IVPU_MMU_ENTRY_FLAG_RO;
mutex_lock(&ctx->lock);
@@ -527,7 +529,8 @@ ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ct
ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id);
if (ret)
- ivpu_warn(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret);
+ ivpu_warn_ratelimited(vdev, "Failed to invalidate TLB for ctx %u: %d\n",
+ ctx->id, ret);
}
int
@@ -568,7 +571,7 @@ void ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ct
mutex_init(&ctx->lock);
if (!context_id) {
- start = vdev->hw->ranges.global.start;
+ start = vdev->hw->ranges.runtime.start;
end = vdev->hw->ranges.shave.end;
} else {
start = min_t(u64, vdev->hw->ranges.user.start, vdev->hw->ranges.shave.start);
diff --git a/drivers/accel/ivpu/ivpu_mmu_context.h b/drivers/accel/ivpu/ivpu_mmu_context.h
index f255310968cf..663a11a9db11 100644
--- a/drivers/accel/ivpu/ivpu_mmu_context.h
+++ b/drivers/accel/ivpu/ivpu_mmu_context.h
@@ -42,7 +42,7 @@ int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu
void ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node);
int ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
- u64 vpu_addr, struct sg_table *sgt, bool llc_coherent);
+ u64 vpu_addr, struct sg_table *sgt, bool llc_coherent, bool read_only);
void ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
u64 vpu_addr, struct sg_table *sgt);
int ivpu_mmu_context_set_pages_ro(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
diff --git a/drivers/accel/ivpu/ivpu_ms.c b/drivers/accel/ivpu/ivpu_ms.c
index 2a043baf10ca..1d9c1cb17924 100644
--- a/drivers/accel/ivpu/ivpu_ms.c
+++ b/drivers/accel/ivpu/ivpu_ms.c
@@ -8,6 +8,7 @@
#include "ivpu_drv.h"
#include "ivpu_gem.h"
+#include "ivpu_hw.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_ms.h"
#include "ivpu_pm.h"
@@ -37,8 +38,8 @@ int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
struct drm_ivpu_metric_streamer_start *args = data;
struct ivpu_device *vdev = file_priv->vdev;
struct ivpu_ms_instance *ms;
- u64 single_buff_size;
u32 sample_size;
+ u64 buf_size;
int ret;
if (!args->metric_group_mask || !args->read_period_samples ||
@@ -52,7 +53,8 @@ int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
mutex_lock(&file_priv->ms_lock);
if (get_instance_by_mask(file_priv, args->metric_group_mask)) {
- ivpu_err(vdev, "Instance already exists (mask %#llx)\n", args->metric_group_mask);
+ ivpu_dbg(vdev, IOCTL, "Instance already exists (mask %#llx)\n",
+ args->metric_group_mask);
ret = -EALREADY;
goto unlock;
}
@@ -69,12 +71,18 @@ int ivpu_ms_start_ioctl(struct drm_device *dev, void *data, struct drm_file *fil
if (ret)
goto err_free_ms;
- single_buff_size = sample_size *
- ((u64)args->read_period_samples * MS_READ_PERIOD_MULTIPLIER);
- ms->bo = ivpu_bo_create_global(vdev, PAGE_ALIGN(single_buff_size * MS_NUM_BUFFERS),
- DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
+ buf_size = PAGE_ALIGN((u64)args->read_period_samples * sample_size *
+ MS_READ_PERIOD_MULTIPLIER * MS_NUM_BUFFERS);
+ if (buf_size > ivpu_hw_range_size(&vdev->hw->ranges.global)) {
+ ivpu_dbg(vdev, IOCTL, "Requested MS buffer size %llu exceeds range size %llu\n",
+ buf_size, ivpu_hw_range_size(&vdev->hw->ranges.global));
+ ret = -EINVAL;
+ goto err_free_ms;
+ }
+
+ ms->bo = ivpu_bo_create_global(vdev, buf_size, DRM_IVPU_BO_CACHED | DRM_IVPU_BO_MAPPABLE);
if (!ms->bo) {
- ivpu_err(vdev, "Failed to allocate MS buffer (size %llu)\n", single_buff_size);
+ ivpu_dbg(vdev, IOCTL, "Failed to allocate MS buffer (size %llu)\n", buf_size);
ret = -ENOMEM;
goto err_free_ms;
}
@@ -175,7 +183,8 @@ int ivpu_ms_get_data_ioctl(struct drm_device *dev, void *data, struct drm_file *
ms = get_instance_by_mask(file_priv, args->metric_group_mask);
if (!ms) {
- ivpu_err(vdev, "Instance doesn't exist for mask: %#llx\n", args->metric_group_mask);
+ ivpu_dbg(vdev, IOCTL, "Instance doesn't exist for mask: %#llx\n",
+ args->metric_group_mask);
ret = -EINVAL;
goto unlock;
}
diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c
index 475ddc94f1cf..480c075d87f6 100644
--- a/drivers/accel/ivpu/ivpu_pm.c
+++ b/drivers/accel/ivpu/ivpu_pm.c
@@ -54,7 +54,7 @@ static void ivpu_pm_prepare_cold_boot(struct ivpu_device *vdev)
static void ivpu_pm_prepare_warm_boot(struct ivpu_device *vdev)
{
struct ivpu_fw_info *fw = vdev->fw;
- struct vpu_boot_params *bp = ivpu_bo_vaddr(fw->mem);
+ struct vpu_boot_params *bp = ivpu_bo_vaddr(fw->mem_bp);
if (!bp->save_restore_ret_address) {
ivpu_pm_prepare_cold_boot(vdev);
@@ -186,7 +186,7 @@ void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason)
if (atomic_cmpxchg(&vdev->pm->reset_pending, 0, 1) == 0) {
ivpu_hw_diagnose_failure(vdev);
ivpu_hw_irq_disable(vdev); /* Disable IRQ early to protect from IRQ storm */
- queue_work(system_unbound_wq, &vdev->pm->recovery_work);
+ queue_work(system_dfl_wq, &vdev->pm->recovery_work);
}
}
@@ -226,7 +226,8 @@ void ivpu_start_job_timeout_detection(struct ivpu_device *vdev)
unsigned long timeout_ms = ivpu_tdr_timeout_ms ? ivpu_tdr_timeout_ms : vdev->timeout.tdr;
/* No-op if already queued */
- queue_delayed_work(system_wq, &vdev->pm->job_timeout_work, msecs_to_jiffies(timeout_ms));
+ queue_delayed_work(system_percpu_wq, &vdev->pm->job_timeout_work,
+ msecs_to_jiffies(timeout_ms));
}
void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev)
@@ -359,7 +360,6 @@ int ivpu_rpm_get(struct ivpu_device *vdev)
void ivpu_rpm_put(struct ivpu_device *vdev)
{
- pm_runtime_mark_last_busy(vdev->drm.dev);
pm_runtime_put_autosuspend(vdev->drm.dev);
}
@@ -428,7 +428,6 @@ void ivpu_pm_enable(struct ivpu_device *vdev)
struct device *dev = vdev->drm.dev;
pm_runtime_allow(dev);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
@@ -502,6 +501,11 @@ void ivpu_pm_irq_dct_work_fn(struct work_struct *work)
else
ret = ivpu_pm_dct_disable(vdev);
- if (!ret)
- ivpu_hw_btrs_dct_set_status(vdev, enable, vdev->pm->dct_active_percent);
+ if (!ret) {
+ /* Convert percent to U1.7 format */
+ u8 val = DIV_ROUND_CLOSEST(vdev->pm->dct_active_percent * 128, 100);
+
+ ivpu_hw_btrs_dct_set_status(vdev, enable, val);
+ }
+
}
diff --git a/drivers/accel/ivpu/ivpu_sysfs.c b/drivers/accel/ivpu/ivpu_sysfs.c
index 268ab7744a8b..d250a10caca9 100644
--- a/drivers/accel/ivpu/ivpu_sysfs.c
+++ b/drivers/accel/ivpu/ivpu_sysfs.c
@@ -63,7 +63,8 @@ npu_memory_utilization_show(struct device *dev, struct device_attribute *attr, c
mutex_lock(&vdev->bo_list_lock);
list_for_each_entry(bo, &vdev->bo_list, bo_list_node)
- total_npu_memory += bo->base.base.size;
+ if (ivpu_bo_is_resident(bo))
+ total_npu_memory += ivpu_bo_size(bo);
mutex_unlock(&vdev->bo_list_lock);
return sysfs_emit(buf, "%lld\n", total_npu_memory);
diff --git a/drivers/accel/ivpu/vpu_jsm_api.h b/drivers/accel/ivpu/vpu_jsm_api.h
index 4b6b2b3d2583..bca6a44dc041 100644
--- a/drivers/accel/ivpu/vpu_jsm_api.h
+++ b/drivers/accel/ivpu/vpu_jsm_api.h
@@ -1,15 +1,16 @@
/* SPDX-License-Identifier: MIT */
/*
- * Copyright (c) 2020-2024, Intel Corporation.
+ * Copyright (c) 2020-2025, Intel Corporation.
+ */
+
+/**
+ * @addtogroup Jsm
+ * @{
*/
/**
* @file
* @brief JSM shared definitions
- *
- * @ingroup Jsm
- * @brief JSM shared definitions
- * @{
*/
#ifndef VPU_JSM_API_H
#define VPU_JSM_API_H
@@ -22,7 +23,7 @@
/*
* Minor version changes when API backward compatibility is preserved.
*/
-#define VPU_JSM_API_VER_MINOR 29
+#define VPU_JSM_API_VER_MINOR 33
/*
* API header changed (field names, documentation, formatting) but API itself has not been changed
@@ -71,9 +72,15 @@
#define VPU_JSM_STATUS_MVNCI_OUT_OF_RESOURCES 0xAU
#define VPU_JSM_STATUS_MVNCI_NOT_IMPLEMENTED 0xBU
#define VPU_JSM_STATUS_MVNCI_INTERNAL_ERROR 0xCU
-/* Job status returned when the job was preempted mid-inference */
+/* @deprecated (use VPU_JSM_STATUS_PREEMPTED_MID_COMMAND instead) */
#define VPU_JSM_STATUS_PREEMPTED_MID_INFERENCE 0xDU
+/* Job status returned when the job was preempted mid-command */
+#define VPU_JSM_STATUS_PREEMPTED_MID_COMMAND 0xDU
+/* Range of status codes that require engine reset */
+#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MIN 0xEU
#define VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW 0xEU
+#define VPU_JSM_STATUS_MVNCI_PREEMPTION_TIMED_OUT 0xFU
+#define VPU_JSM_STATUS_ENGINE_RESET_REQUIRED_MAX 0x1FU
/*
* Host <-> VPU IPC channels.
@@ -134,11 +141,21 @@ enum {
* 2. Native fence queues are only supported on VPU 40xx onwards.
*/
VPU_JOB_QUEUE_FLAGS_USE_NATIVE_FENCE_MASK = (1 << 1U),
-
/*
* Enable turbo mode for testing NPU performance; not recommended for regular usage.
*/
- VPU_JOB_QUEUE_FLAGS_TURBO_MODE = (1 << 2U)
+ VPU_JOB_QUEUE_FLAGS_TURBO_MODE = (1 << 2U),
+ /*
+ * Queue error detection mode flag
+ * For 'interactive' queues (this bit not set), the FW will identify queues that have not
+ * completed a job inside the TDR timeout as in error as part of engine reset sequence.
+ * For 'non-interactive' queues (this bit set), the FW will identify queues that have not
+ * progressed the heartbeat inside the non-interactive no-progress timeout as in error as
+ * part of engine reset sequence. Additionally, there is an upper limit applied to these
+ * queues: even if they progress the heartbeat, if they run longer than non-interactive
+ * timeout, then the FW will also identify them as in error.
+ */
+ VPU_JOB_QUEUE_FLAGS_NON_INTERACTIVE = (1 << 3U)
};
/*
@@ -209,7 +226,7 @@ enum {
*/
#define VPU_INLINE_CMD_TYPE_FENCE_SIGNAL 0x2
-/*
+/**
* Job scheduling priority bands for both hardware scheduling and OS scheduling.
*/
enum vpu_job_scheduling_priority_band {
@@ -220,16 +237,16 @@ enum vpu_job_scheduling_priority_band {
VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT = 4,
};
-/*
+/**
* Job format.
* Jobs defines the actual workloads to be executed by a given engine.
*/
struct vpu_job_queue_entry {
- /**< Address of VPU commands batch buffer */
+ /** Address of VPU commands batch buffer */
u64 batch_buf_addr;
- /**< Job ID */
+ /** Job ID */
u32 job_id;
- /**< Flags bit field, see VPU_JOB_FLAGS_* above */
+ /** Flags bit field, see VPU_JOB_FLAGS_* above */
u32 flags;
/**
* Doorbell ring timestamp taken by KMD from SoC's global system clock, in
@@ -237,20 +254,20 @@ struct vpu_job_queue_entry {
* to match other profiling timestamps.
*/
u64 doorbell_timestamp;
- /**< Extra id for job tracking, used only in the firmware perf traces */
+ /** Extra id for job tracking, used only in the firmware perf traces */
u64 host_tracking_id;
- /**< Address of the primary preemption buffer to use for this job */
+ /** Address of the primary preemption buffer to use for this job */
u64 primary_preempt_buf_addr;
- /**< Size of the primary preemption buffer to use for this job */
+ /** Size of the primary preemption buffer to use for this job */
u32 primary_preempt_buf_size;
- /**< Size of secondary preemption buffer to use for this job */
+ /** Size of secondary preemption buffer to use for this job */
u32 secondary_preempt_buf_size;
- /**< Address of secondary preemption buffer to use for this job */
+ /** Address of secondary preemption buffer to use for this job */
u64 secondary_preempt_buf_addr;
u64 reserved_0;
};
-/*
+/**
* Inline command format.
* Inline commands are the commands executed at scheduler level (typically,
* synchronization directives). Inline command and job objects must be of
@@ -258,34 +275,36 @@ struct vpu_job_queue_entry {
*/
struct vpu_inline_cmd {
u64 reserved_0;
- /* Inline command type, see VPU_INLINE_CMD_TYPE_* defines. */
+ /** Inline command type, see VPU_INLINE_CMD_TYPE_* defines. */
u32 type;
- /* Flags bit field, see VPU_JOB_FLAGS_* above. */
+ /** Flags bit field, see VPU_JOB_FLAGS_* above. */
u32 flags;
- /* Inline command payload. Depends on inline command type. */
- union {
- /* Fence (wait and signal) commands' payload. */
- struct {
- /* Fence object handle. */
+ /** Inline command payload. Depends on inline command type. */
+ union payload {
+ /** Fence (wait and signal) commands' payload. */
+ struct fence {
+ /** Fence object handle. */
u64 fence_handle;
- /* User VA of the current fence value. */
+ /** User VA of the current fence value. */
u64 current_value_va;
- /* User VA of the monitored fence value (read-only). */
+ /** User VA of the monitored fence value (read-only). */
u64 monitored_value_va;
- /* Value to wait for or write in fence location. */
+ /** Value to wait for or write in fence location. */
u64 value;
- /* User VA of the log buffer in which to add log entry on completion. */
+ /** User VA of the log buffer in which to add log entry on completion. */
u64 log_buffer_va;
- /* NPU private data. */
+ /** NPU private data. */
u64 npu_private_data;
} fence;
- /* Other commands do not have a payload. */
- /* Payload definition for future inline commands can be inserted here. */
+ /**
+ * Other commands do not have a payload:
+ * Payload definition for future inline commands can be inserted here.
+ */
u64 reserved_1[6];
} payload;
};
-/*
+/**
* Job queue slots can be populated either with job objects or inline command objects.
*/
union vpu_jobq_slot {
@@ -293,7 +312,7 @@ union vpu_jobq_slot {
struct vpu_inline_cmd inline_cmd;
};
-/*
+/**
* Job queue control registers.
*/
struct vpu_job_queue_header {
@@ -301,18 +320,18 @@ struct vpu_job_queue_header {
u32 head;
u32 tail;
u32 flags;
- /* Set to 1 to indicate priority_band field is valid */
+ /** Set to 1 to indicate priority_band field is valid */
u32 priority_band_valid;
- /*
+ /**
* Priority for the work of this job queue, valid only if the HWS is NOT used
- * and the `priority_band_valid` is set to 1. It is applied only during
- * the VPU_JSM_MSG_REGISTER_DB message processing.
- * The device firmware might use the `priority_band` to optimize the power
+ * and the @ref priority_band_valid is set to 1. It is applied only during
+ * the @ref VPU_JSM_MSG_REGISTER_DB message processing.
+ * The device firmware might use the priority_band to optimize the power
* management logic, but it will not affect the order of jobs.
* Available priority bands: @see enum vpu_job_scheduling_priority_band
*/
u32 priority_band;
- /* Inside realtime band assigns a further priority, limited to 0..31 range */
+ /** Inside realtime band assigns a further priority, limited to 0..31 range */
u32 realtime_priority_level;
u32 reserved_0[9];
};
@@ -337,16 +356,16 @@ enum vpu_trace_entity_type {
VPU_TRACE_ENTITY_TYPE_HW_COMPONENT = 2,
};
-/*
+/**
* HWS specific log buffer header details.
* Total size is 32 bytes.
*/
struct vpu_hws_log_buffer_header {
- /* Written by VPU after adding a log entry. Initialised by host to 0. */
+ /** Written by VPU after adding a log entry. Initialised by host to 0. */
u32 first_free_entry_index;
- /* Incremented by VPU every time the VPU writes the 0th entry; initialised by host to 0. */
+ /** Incremented by VPU every time the VPU writes the 0th entry; initialised by host to 0. */
u32 wraparound_count;
- /*
+ /**
* This is the number of buffers that can be stored in the log buffer provided by the host.
* It is written by host before passing buffer to VPU. VPU should consider it read-only.
*/
@@ -354,14 +373,14 @@ struct vpu_hws_log_buffer_header {
u64 reserved[2];
};
-/*
+/**
* HWS specific log buffer entry details.
* Total size is 32 bytes.
*/
struct vpu_hws_log_buffer_entry {
- /* VPU timestamp must be an invariant timer tick (not impacted by DVFS) */
+ /** VPU timestamp must be an invariant timer tick (not impacted by DVFS) */
u64 vpu_timestamp;
- /*
+ /**
* Operation type:
* 0 - context state change
* 1 - queue new work
@@ -371,7 +390,7 @@ struct vpu_hws_log_buffer_entry {
*/
u32 operation_type;
u32 reserved;
- /* Operation data depends on operation type */
+ /** Operation data depends on operation type */
u64 operation_data[2];
};
@@ -381,51 +400,54 @@ enum vpu_hws_native_fence_log_type {
VPU_HWS_NATIVE_FENCE_LOG_TYPE_SIGNALS = 2
};
-/* HWS native fence log buffer header. */
+/** HWS native fence log buffer header. */
struct vpu_hws_native_fence_log_header {
union {
struct {
- /* Index of the first free entry in buffer. */
+ /** Index of the first free entry in buffer. */
u32 first_free_entry_idx;
- /* Incremented each time NPU wraps around the buffer to write next entry. */
+ /**
+ * Incremented whenever the NPU wraps around the buffer and writes
+ * to the first entry again.
+ */
u32 wraparound_count;
};
- /* Field allowing atomic update of both fields above. */
+ /** Field allowing atomic update of both fields above. */
u64 atomic_wraparound_and_entry_idx;
};
- /* Log buffer type, see enum vpu_hws_native_fence_log_type. */
+ /** Log buffer type, see enum vpu_hws_native_fence_log_type. */
u64 type;
- /* Allocated number of entries in the log buffer. */
+ /** Allocated number of entries in the log buffer. */
u64 entry_nb;
u64 reserved[2];
};
-/* Native fence log operation types. */
+/** Native fence log operation types. */
enum vpu_hws_native_fence_log_op {
VPU_HWS_NATIVE_FENCE_LOG_OP_SIGNAL_EXECUTED = 0,
VPU_HWS_NATIVE_FENCE_LOG_OP_WAIT_UNBLOCKED = 1
};
-/* HWS native fence log entry. */
+/** HWS native fence log entry. */
struct vpu_hws_native_fence_log_entry {
- /* Newly signaled/unblocked fence value. */
+ /** Newly signaled/unblocked fence value. */
u64 fence_value;
- /* Native fence object handle to which this operation belongs. */
+ /** Native fence object handle to which this operation belongs. */
u64 fence_handle;
- /* Operation type, see enum vpu_hws_native_fence_log_op. */
+ /** Operation type, see enum vpu_hws_native_fence_log_op. */
u64 op_type;
u64 reserved_0;
- /*
+ /**
* VPU_HWS_NATIVE_FENCE_LOG_OP_WAIT_UNBLOCKED only: Timestamp at which fence
* wait was started (in NPU SysTime).
*/
u64 fence_wait_start_ts;
u64 reserved_1;
- /* Timestamp at which fence operation was completed (in NPU SysTime). */
+ /** Timestamp at which fence operation was completed (in NPU SysTime). */
u64 fence_end_ts;
};
-/* Native fence log buffer. */
+/** Native fence log buffer. */
struct vpu_hws_native_fence_log_buffer {
struct vpu_hws_native_fence_log_header header;
struct vpu_hws_native_fence_log_entry entry[];
@@ -435,10 +457,17 @@ struct vpu_hws_native_fence_log_buffer {
* Host <-> VPU IPC messages types.
*/
enum vpu_ipc_msg_type {
+ /** Unsupported command */
VPU_JSM_MSG_UNKNOWN = 0xFFFFFFFF,
- /* IPC Host -> Device, Async commands */
+ /** IPC Host -> Device, base id for async commands */
VPU_JSM_MSG_ASYNC_CMD = 0x1100,
+ /**
+ * Reset engine. The NPU cancels all the jobs currently executing on the target
+ * engine making the engine become idle and then does a HW reset, before returning
+ * to the host.
+ * @see struct vpu_ipc_msg_payload_engine_reset
+ */
VPU_JSM_MSG_ENGINE_RESET = VPU_JSM_MSG_ASYNC_CMD,
/**
* Preempt engine. The NPU stops (preempts) all the jobs currently
@@ -448,10 +477,24 @@ enum vpu_ipc_msg_type {
* the target engine, but it stops processing them (until the queue doorbell
* is rung again); the host is responsible to reset the job queue, either
* after preemption or when resubmitting jobs to the queue.
+ * @see vpu_ipc_msg_payload_engine_preempt
*/
VPU_JSM_MSG_ENGINE_PREEMPT = 0x1101,
+ /**
+ * OS scheduling doorbell register command
+ * @see vpu_ipc_msg_payload_register_db
+ */
VPU_JSM_MSG_REGISTER_DB = 0x1102,
+ /**
+ * OS scheduling doorbell unregister command
+ * @see vpu_ipc_msg_payload_unregister_db
+ */
VPU_JSM_MSG_UNREGISTER_DB = 0x1103,
+ /**
+ * Query engine heartbeat. Heartbeat is expected to increase monotonically
+ * and increase while work is being progressed by NPU.
+ * @see vpu_ipc_msg_payload_query_engine_hb
+ */
VPU_JSM_MSG_QUERY_ENGINE_HB = 0x1104,
VPU_JSM_MSG_GET_POWER_LEVEL_COUNT = 0x1105,
VPU_JSM_MSG_GET_POWER_LEVEL = 0x1106,
@@ -477,6 +520,7 @@ enum vpu_ipc_msg_type {
* aborted and removed from internal scheduling queues. All doorbells assigned
* to the host_ssid are unregistered and any internal FW resources belonging to
* the host_ssid are released.
+ * @see vpu_ipc_msg_payload_ssid_release
*/
VPU_JSM_MSG_SSID_RELEASE = 0x110e,
/**
@@ -504,43 +548,78 @@ enum vpu_ipc_msg_type {
* @see vpu_jsm_metric_streamer_start
*/
VPU_JSM_MSG_METRIC_STREAMER_INFO = 0x1112,
- /** Control command: Priority band setup */
+ /**
+ * Control command: Priority band setup
+ * @see vpu_ipc_msg_payload_hws_priority_band_setup
+ */
VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP = 0x1113,
- /** Control command: Create command queue */
+ /**
+ * Control command: Create command queue
+ * @see vpu_ipc_msg_payload_hws_create_cmdq
+ */
VPU_JSM_MSG_CREATE_CMD_QUEUE = 0x1114,
- /** Control command: Destroy command queue */
+ /**
+ * Control command: Destroy command queue
+ * @see vpu_ipc_msg_payload_hws_destroy_cmdq
+ */
VPU_JSM_MSG_DESTROY_CMD_QUEUE = 0x1115,
- /** Control command: Set context scheduling properties */
+ /**
+ * Control command: Set context scheduling properties
+ * @see vpu_ipc_msg_payload_hws_set_context_sched_properties
+ */
VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES = 0x1116,
- /*
+ /**
* Register a doorbell to notify VPU of new work. The doorbell may later be
* deallocated or reassigned to another context.
+ * @see vpu_jsm_hws_register_db
*/
VPU_JSM_MSG_HWS_REGISTER_DB = 0x1117,
- /** Control command: Log buffer setting */
+ /**
+ * Control command: Log buffer setting
+ * @see vpu_ipc_msg_payload_hws_set_scheduling_log
+ */
VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG = 0x1118,
- /* Control command: Suspend command queue. */
+ /**
+ * Control command: Suspend command queue.
+ * @see vpu_ipc_msg_payload_hws_suspend_cmdq
+ */
VPU_JSM_MSG_HWS_SUSPEND_CMDQ = 0x1119,
- /* Control command: Resume command queue */
+ /**
+ * Control command: Resume command queue
+ * @see vpu_ipc_msg_payload_hws_resume_cmdq
+ */
VPU_JSM_MSG_HWS_RESUME_CMDQ = 0x111a,
- /* Control command: Resume engine after reset */
+ /**
+ * Control command: Resume engine after reset
+ * @see vpu_ipc_msg_payload_hws_resume_engine
+ */
VPU_JSM_MSG_HWS_ENGINE_RESUME = 0x111b,
- /* Control command: Enable survivability/DCT mode */
+ /**
+ * Control command: Enable survivability/DCT mode
+ * @see vpu_ipc_msg_payload_pwr_dct_control
+ */
VPU_JSM_MSG_DCT_ENABLE = 0x111c,
- /* Control command: Disable survivability/DCT mode */
+ /**
+ * Control command: Disable survivability/DCT mode
+ * This command has no payload
+ */
VPU_JSM_MSG_DCT_DISABLE = 0x111d,
/**
* Dump VPU state. To be used for debug purposes only.
- * NOTE: Please introduce new ASYNC commands before this one. *
+ * This command has no payload.
+ * NOTE: Please introduce new ASYNC commands before this one.
*/
VPU_JSM_MSG_STATE_DUMP = 0x11FF,
- /* IPC Host -> Device, General commands */
+ /** IPC Host -> Device, base id for general commands */
VPU_JSM_MSG_GENERAL_CMD = 0x1200,
+ /** Unsupported command */
VPU_JSM_MSG_BLOB_DEINIT_DEPRECATED = VPU_JSM_MSG_GENERAL_CMD,
/**
* Control dyndbg behavior by executing a dyndbg command; equivalent to
- * Linux command: `echo '<dyndbg_cmd>' > <debugfs>/dynamic_debug/control`.
+ * Linux command:
+ * @verbatim echo '<dyndbg_cmd>' > <debugfs>/dynamic_debug/control @endverbatim
+ * @see vpu_ipc_msg_payload_dyndbg_control
*/
VPU_JSM_MSG_DYNDBG_CONTROL = 0x1201,
/**
@@ -548,17 +627,35 @@ enum vpu_ipc_msg_type {
*/
VPU_JSM_MSG_PWR_D0I3_ENTER = 0x1202,
- /* IPC Device -> Host, Job completion */
+ /**
+ * IPC Device -> Host, Job completion
+ * @see struct vpu_ipc_msg_payload_job_done
+ */
VPU_JSM_MSG_JOB_DONE = 0x2100,
- /* IPC Device -> Host, Fence signalled */
+ /**
+ * IPC Device -> Host, Fence signalled
+ * @see vpu_ipc_msg_payload_native_fence_signalled
+ */
VPU_JSM_MSG_NATIVE_FENCE_SIGNALLED = 0x2101,
/* IPC Device -> Host, Async command completion */
VPU_JSM_MSG_ASYNC_CMD_DONE = 0x2200,
+ /**
+ * IPC Device -> Host, engine reset complete
+ * @see vpu_ipc_msg_payload_engine_reset_done
+ */
VPU_JSM_MSG_ENGINE_RESET_DONE = VPU_JSM_MSG_ASYNC_CMD_DONE,
+ /**
+ * Preempt complete message
+ * @see vpu_ipc_msg_payload_engine_preempt_done
+ */
VPU_JSM_MSG_ENGINE_PREEMPT_DONE = 0x2201,
VPU_JSM_MSG_REGISTER_DB_DONE = 0x2202,
VPU_JSM_MSG_UNREGISTER_DB_DONE = 0x2203,
+ /**
+ * Response to query engine heartbeat.
+ * @see vpu_ipc_msg_payload_query_engine_hb_done
+ */
VPU_JSM_MSG_QUERY_ENGINE_HB_DONE = 0x2204,
VPU_JSM_MSG_GET_POWER_LEVEL_COUNT_DONE = 0x2205,
VPU_JSM_MSG_GET_POWER_LEVEL_DONE = 0x2206,
@@ -575,7 +672,10 @@ enum vpu_ipc_msg_type {
VPU_JSM_MSG_TRACE_GET_CAPABILITY_RSP = 0x220c,
/** Response to VPU_JSM_MSG_TRACE_GET_NAME. */
VPU_JSM_MSG_TRACE_GET_NAME_RSP = 0x220d,
- /** Response to VPU_JSM_MSG_SSID_RELEASE. */
+ /**
+ * Response to VPU_JSM_MSG_SSID_RELEASE.
+ * @see vpu_ipc_msg_payload_ssid_release
+ */
VPU_JSM_MSG_SSID_RELEASE_DONE = 0x220e,
/**
* Response to VPU_JSM_MSG_METRIC_STREAMER_START.
@@ -605,37 +705,71 @@ enum vpu_ipc_msg_type {
/**
* Asynchronous event sent from the VPU to the host either when the current
* metric buffer is full or when the VPU has collected a multiple of
- * @notify_sample_count samples as indicated through the start command
- * (VPU_JSM_MSG_METRIC_STREAMER_START). Returns information about collected
- * metric data.
+ * @ref vpu_jsm_metric_streamer_start::notify_sample_count samples as indicated
+ * through the start command (VPU_JSM_MSG_METRIC_STREAMER_START). Returns
+ * information about collected metric data.
* @see vpu_jsm_metric_streamer_done
*/
VPU_JSM_MSG_METRIC_STREAMER_NOTIFICATION = 0x2213,
- /** Response to control command: Priority band setup */
+ /**
+ * Response to control command: Priority band setup
+ * @see vpu_ipc_msg_payload_hws_priority_band_setup
+ */
VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP_RSP = 0x2214,
- /** Response to control command: Create command queue */
+ /**
+ * Response to control command: Create command queue
+ * @see vpu_ipc_msg_payload_hws_create_cmdq_rsp
+ */
VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP = 0x2215,
- /** Response to control command: Destroy command queue */
+ /**
+ * Response to control command: Destroy command queue
+ * @see vpu_ipc_msg_payload_hws_destroy_cmdq
+ */
VPU_JSM_MSG_DESTROY_CMD_QUEUE_RSP = 0x2216,
- /** Response to control command: Set context scheduling properties */
+ /**
+ * Response to control command: Set context scheduling properties
+ * @see vpu_ipc_msg_payload_hws_set_context_sched_properties
+ */
VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP = 0x2217,
- /** Response to control command: Log buffer setting */
+ /**
+ * Response to control command: Log buffer setting
+ * @see vpu_ipc_msg_payload_hws_set_scheduling_log
+ */
VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG_RSP = 0x2218,
- /* IPC Device -> Host, HWS notify index entry of log buffer written */
+ /**
+ * IPC Device -> Host, HWS notify index entry of log buffer written
+ * @see vpu_ipc_msg_payload_hws_scheduling_log_notification
+ */
VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION = 0x2219,
- /* IPC Device -> Host, HWS completion of a context suspend request */
+ /**
+ * IPC Device -> Host, HWS completion of a context suspend request
+ * @see vpu_ipc_msg_payload_hws_suspend_cmdq
+ */
VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE = 0x221a,
- /* Response to control command: Resume command queue */
+ /**
+ * Response to control command: Resume command queue
+ * @see vpu_ipc_msg_payload_hws_resume_cmdq
+ */
VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP = 0x221b,
- /* Response to control command: Resume engine command response */
+ /**
+ * Response to control command: Resume engine command response
+ * @see vpu_ipc_msg_payload_hws_resume_engine
+ */
VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE = 0x221c,
- /* Response to control command: Enable survivability/DCT mode */
+ /**
+ * Response to control command: Enable survivability/DCT mode
+ * This command has no payload
+ */
VPU_JSM_MSG_DCT_ENABLE_DONE = 0x221d,
- /* Response to control command: Disable survivability/DCT mode */
+ /**
+ * Response to control command: Disable survivability/DCT mode
+ * This command has no payload
+ */
VPU_JSM_MSG_DCT_DISABLE_DONE = 0x221e,
/**
* Response to state dump control command.
- * NOTE: Please introduce new ASYNC responses before this one. *
+ * This command has no payload.
+ * NOTE: Please introduce new ASYNC responses before this one.
*/
VPU_JSM_MSG_STATE_DUMP_RSP = 0x22FF,
@@ -653,57 +787,66 @@ enum vpu_ipc_msg_type {
enum vpu_ipc_msg_status { VPU_JSM_MSG_FREE, VPU_JSM_MSG_ALLOCATED };
-/*
- * Host <-> LRT IPC message payload definitions
+/**
+ * Engine reset request payload
+ * @see VPU_JSM_MSG_ENGINE_RESET
*/
struct vpu_ipc_msg_payload_engine_reset {
- /* Engine to be reset. */
+ /** Engine to be reset. */
u32 engine_idx;
- /* Reserved */
+ /** Reserved */
u32 reserved_0;
};
+/**
+ * Engine preemption request struct
+ * @see VPU_JSM_MSG_ENGINE_PREEMPT
+ */
struct vpu_ipc_msg_payload_engine_preempt {
- /* Engine to be preempted. */
+ /** Engine to be preempted. */
u32 engine_idx;
- /* ID of the preemption request. */
+ /** ID of the preemption request. */
u32 preempt_id;
};
-/*
- * @brief Register doorbell command structure.
+/**
+ * Register doorbell command structure.
* This structure supports doorbell registration for only OS scheduling.
* @see VPU_JSM_MSG_REGISTER_DB
*/
struct vpu_ipc_msg_payload_register_db {
- /* Index of the doorbell to register. */
+ /** Index of the doorbell to register. */
u32 db_idx;
- /* Reserved */
+ /** Reserved */
u32 reserved_0;
- /* Virtual address in Global GTT pointing to the start of job queue. */
+ /** Virtual address in Global GTT pointing to the start of job queue. */
u64 jobq_base;
- /* Size of the job queue in bytes. */
+ /** Size of the job queue in bytes. */
u32 jobq_size;
- /* Host sub-stream ID for the context assigned to the doorbell. */
+ /** Host sub-stream ID for the context assigned to the doorbell. */
u32 host_ssid;
};
/**
- * @brief Unregister doorbell command structure.
+ * Unregister doorbell command structure.
* Request structure to unregister a doorbell for both HW and OS scheduling.
* @see VPU_JSM_MSG_UNREGISTER_DB
*/
struct vpu_ipc_msg_payload_unregister_db {
- /* Index of the doorbell to unregister. */
+ /** Index of the doorbell to unregister. */
u32 db_idx;
- /* Reserved */
+ /** Reserved */
u32 reserved_0;
};
+/**
+ * Heartbeat request structure
+ * @see VPU_JSM_MSG_QUERY_ENGINE_HB
+ */
struct vpu_ipc_msg_payload_query_engine_hb {
- /* Engine to return heartbeat value. */
+ /** Engine to return heartbeat value. */
u32 engine_idx;
- /* Reserved */
+ /** Reserved */
u32 reserved_0;
};
@@ -723,10 +866,14 @@ struct vpu_ipc_msg_payload_power_level {
u32 reserved_0;
};
+/**
+ * Structure for requesting ssid release
+ * @see VPU_JSM_MSG_SSID_RELEASE
+ */
struct vpu_ipc_msg_payload_ssid_release {
- /* Host sub-stream ID for the context to be released. */
+ /** Host sub-stream ID for the context to be released. */
u32 host_ssid;
- /* Reserved */
+ /** Reserved */
u32 reserved_0;
};
@@ -752,7 +899,7 @@ struct vpu_jsm_metric_streamer_start {
u64 sampling_rate;
/**
* If > 0 the VPU will send a VPU_JSM_MSG_METRIC_STREAMER_NOTIFICATION message
- * after every @notify_sample_count samples is collected or dropped by the VPU.
+ * after every @ref notify_sample_count samples is collected or dropped by the VPU.
* If set to UINT_MAX the VPU will only generate a notification when the metric
* buffer is full. If set to 0 the VPU will never generate a notification.
*/
@@ -762,9 +909,9 @@ struct vpu_jsm_metric_streamer_start {
* Address and size of the buffer where the VPU will write metric data. The
* VPU writes all counters from enabled metric groups one after another. If
* there is no space left to write data at the next sample period the VPU
- * will switch to the next buffer (@see next_buffer_addr) and will optionally
- * send a notification to the host driver if @notify_sample_count is non-zero.
- * If @next_buffer_addr is NULL the VPU will stop collecting metric data.
+ * will switch to the next buffer (@ref next_buffer_addr) and will optionally
+ * send a notification to the host driver if @ref notify_sample_count is non-zero.
+ * If @ref next_buffer_addr is NULL the VPU will stop collecting metric data.
*/
u64 buffer_addr;
u64 buffer_size;
@@ -827,63 +974,80 @@ struct vpu_jsm_metric_streamer_update {
u64 next_buffer_size;
};
+/**
+ * Device -> host job completion message.
+ * @see VPU_JSM_MSG_JOB_DONE
+ */
struct vpu_ipc_msg_payload_job_done {
- /* Engine to which the job was submitted. */
+ /** Engine to which the job was submitted. */
u32 engine_idx;
- /* Index of the doorbell to which the job was submitted */
+ /** Index of the doorbell to which the job was submitted */
u32 db_idx;
- /* ID of the completed job */
+ /** ID of the completed job */
u32 job_id;
- /* Status of the completed job */
+ /** Status of the completed job */
u32 job_status;
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved_0;
- /* Command queue id */
+ /** Command queue id */
u64 cmdq_id;
};
-/*
+/**
* Notification message upon native fence signalling.
* @see VPU_JSM_MSG_NATIVE_FENCE_SIGNALLED
*/
struct vpu_ipc_msg_payload_native_fence_signalled {
- /* Engine ID. */
+ /** Engine ID. */
u32 engine_idx;
- /* Host SSID. */
+ /** Host SSID. */
u32 host_ssid;
- /* CMDQ ID */
+ /** CMDQ ID */
u64 cmdq_id;
- /* Fence object handle. */
+ /** Fence object handle. */
u64 fence_handle;
};
+/**
+ * vpu_ipc_msg_payload_engine_reset_done will contain an array of this structure
+ * which contains which queues caused reset if FW was able to detect any error.
+ * @see vpu_ipc_msg_payload_engine_reset_done
+ */
struct vpu_jsm_engine_reset_context {
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved_0;
- /* Command queue id */
+ /** Command queue id */
u64 cmdq_id;
- /* See VPU_ENGINE_RESET_CONTEXT_* defines */
+ /** See VPU_ENGINE_RESET_CONTEXT_* defines */
u64 flags;
};
+/**
+ * Engine reset response.
+ * @see VPU_JSM_MSG_ENGINE_RESET_DONE
+ */
struct vpu_ipc_msg_payload_engine_reset_done {
- /* Engine ordinal */
+ /** Engine ordinal */
u32 engine_idx;
- /* Number of impacted contexts */
+ /** Number of impacted contexts */
u32 num_impacted_contexts;
- /* Array of impacted command queue ids and their flags */
+ /** Array of impacted command queue ids and their flags */
struct vpu_jsm_engine_reset_context
impacted_contexts[VPU_MAX_ENGINE_RESET_IMPACTED_CONTEXTS];
};
+/**
+ * Preemption response struct
+ * @see VPU_JSM_MSG_ENGINE_PREEMPT_DONE
+ */
struct vpu_ipc_msg_payload_engine_preempt_done {
- /* Engine preempted. */
+ /** Engine preempted. */
u32 engine_idx;
- /* ID of the preemption request. */
+ /** ID of the preemption request. */
u32 preempt_id;
};
@@ -912,12 +1076,16 @@ struct vpu_ipc_msg_payload_unregister_db_done {
u32 reserved_0;
};
+/**
+ * Structure for heartbeat response
+ * @see VPU_JSM_MSG_QUERY_ENGINE_HB_DONE
+ */
struct vpu_ipc_msg_payload_query_engine_hb_done {
- /* Engine returning heartbeat value. */
+ /** Engine returning heartbeat value. */
u32 engine_idx;
- /* Reserved */
+ /** Reserved */
u32 reserved_0;
- /* Heartbeat value. */
+ /** Heartbeat value. */
u64 heartbeat;
};
@@ -937,7 +1105,10 @@ struct vpu_ipc_msg_payload_get_power_level_count_done {
u8 power_limit[16];
};
-/* HWS priority band setup request / response */
+/**
+ * HWS priority band setup request / response
+ * @see VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP
+ */
struct vpu_ipc_msg_payload_hws_priority_band_setup {
/*
* Grace period in 100ns units when preempting another priority band for
@@ -964,15 +1135,23 @@ struct vpu_ipc_msg_payload_hws_priority_band_setup {
* TDR timeout value in milliseconds. Default value of 0 meaning no timeout.
*/
u32 tdr_timeout;
+ /* Non-interactive queue timeout for no progress of heartbeat in milliseconds.
+ * Default value of 0 meaning no timeout.
+ */
+ u32 non_interactive_no_progress_timeout;
+ /*
+ * Non-interactive queue upper limit timeout value in milliseconds. Default
+ * value of 0 meaning no timeout.
+ */
+ u32 non_interactive_timeout;
};
-/*
+/**
* @brief HWS create command queue request.
* Host will create a command queue via this command.
* Note: Cmdq group is a handle of an object which
* may contain one or more command queues.
* @see VPU_JSM_MSG_CREATE_CMD_QUEUE
- * @see VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP
*/
struct vpu_ipc_msg_payload_hws_create_cmdq {
/* Process id */
@@ -993,66 +1172,73 @@ struct vpu_ipc_msg_payload_hws_create_cmdq {
u32 reserved_0;
};
-/*
- * @brief HWS create command queue response.
- * @see VPU_JSM_MSG_CREATE_CMD_QUEUE
+/**
+ * HWS create command queue response.
* @see VPU_JSM_MSG_CREATE_CMD_QUEUE_RSP
*/
struct vpu_ipc_msg_payload_hws_create_cmdq_rsp {
- /* Process id */
+ /** Process id */
u64 process_id;
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /* Engine for which queue is being created */
+ /** Engine for which queue is being created */
u32 engine_idx;
- /* Command queue group */
+ /** Command queue group */
u64 cmdq_group;
- /* Command queue id */
+ /** Command queue id */
u64 cmdq_id;
};
-/* HWS destroy command queue request / response */
+/**
+ * HWS destroy command queue request / response
+ * @see VPU_JSM_MSG_DESTROY_CMD_QUEUE
+ * @see VPU_JSM_MSG_DESTROY_CMD_QUEUE_RSP
+ */
struct vpu_ipc_msg_payload_hws_destroy_cmdq {
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved;
- /* Command queue id */
+ /** Command queue id */
u64 cmdq_id;
};
-/* HWS set context scheduling properties request / response */
+/**
+ * HWS set context scheduling properties request / response
+ * @see VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES
+ * @see VPU_JSM_MSG_SET_CONTEXT_SCHED_PROPERTIES_RSP
+ */
struct vpu_ipc_msg_payload_hws_set_context_sched_properties {
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved_0;
- /* Command queue id */
+ /** Command queue id */
u64 cmdq_id;
- /*
+ /**
* Priority band to assign to work of this context.
* Available priority bands: @see enum vpu_job_scheduling_priority_band
*/
u32 priority_band;
- /* Inside realtime band assigns a further priority */
+ /** Inside realtime band assigns a further priority */
u32 realtime_priority_level;
- /* Priority relative to other contexts in the same process */
+ /** Priority relative to other contexts in the same process */
s32 in_process_priority;
- /* Zero padding / Reserved */
+ /** Zero padding / Reserved */
u32 reserved_1;
- /*
+ /**
* Context quantum relative to other contexts of same priority in the same process
* Minimum value supported by NPU is 1ms (10000 in 100ns units).
*/
u64 context_quantum;
- /* Grace period when preempting context of the same priority within the same process */
+ /** Grace period when preempting context of the same priority within the same process */
u64 grace_period_same_priority;
- /* Grace period when preempting context of a lower priority within the same process */
+ /** Grace period when preempting context of a lower priority within the same process */
u64 grace_period_lower_priority;
};
-/*
- * @brief Register doorbell command structure.
+/**
+ * Register doorbell command structure.
* This structure supports doorbell registration for both HW and OS scheduling.
* Note: Queue base and size are added here so that the same structure can be used for
* OS scheduling and HW scheduling. For OS scheduling, cmdq_id will be ignored
@@ -1061,27 +1247,27 @@ struct vpu_ipc_msg_payload_hws_set_context_sched_properties {
* @see VPU_JSM_MSG_HWS_REGISTER_DB
*/
struct vpu_jsm_hws_register_db {
- /* Index of the doorbell to register. */
+ /** Index of the doorbell to register. */
u32 db_id;
- /* Host sub-stream ID for the context assigned to the doorbell. */
+ /** Host sub-stream ID for the context assigned to the doorbell. */
u32 host_ssid;
- /* ID of the command queue associated with the doorbell. */
+ /** ID of the command queue associated with the doorbell. */
u64 cmdq_id;
- /* Virtual address pointing to the start of command queue. */
+ /** Virtual address pointing to the start of command queue. */
u64 cmdq_base;
- /* Size of the command queue in bytes. */
+ /** Size of the command queue in bytes. */
u64 cmdq_size;
};
-/*
- * @brief Structure to set another buffer to be used for scheduling-related logging.
+/**
+ * Structure to set another buffer to be used for scheduling-related logging.
* The size of the logging buffer and the number of entries is defined as part of the
* buffer itself as described next.
* The log buffer received from the host is made up of;
- * - header: 32 bytes in size, as shown in 'struct vpu_hws_log_buffer_header'.
+ * - header: 32 bytes in size, as shown in @ref vpu_hws_log_buffer_header.
* The header contains the number of log entries in the buffer.
* - log entry: 0 to n-1, each log entry is 32 bytes in size, as shown in
- * 'struct vpu_hws_log_buffer_entry'.
+ * @ref vpu_hws_log_buffer_entry.
* The entry contains the VPU timestamp, operation type and data.
* The host should provide the notify index value of log buffer to VPU. This is a
* value defined within the log buffer and when written to will generate the
@@ -1095,30 +1281,30 @@ struct vpu_jsm_hws_register_db {
* @see VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION
*/
struct vpu_ipc_msg_payload_hws_set_scheduling_log {
- /* Engine ordinal */
+ /** Engine ordinal */
u32 engine_idx;
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /*
+ /**
* VPU log buffer virtual address.
* Set to 0 to disable logging for this engine.
*/
u64 vpu_log_buffer_va;
- /*
+ /**
* Notify index of log buffer. VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION
* is generated when an event log is written to this index.
*/
u64 notify_index;
- /*
+ /**
* Field is now deprecated, will be removed when KMD is updated to support removal
*/
u32 enable_extra_events;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved_0;
};
-/*
- * @brief The scheduling log notification is generated by VPU when it writes
+/**
+ * The scheduling log notification is generated by VPU when it writes
* an event into the log buffer at the notify_index. VPU notifies host with
* VPU_JSM_MSG_HWS_SCHEDULING_LOG_NOTIFICATION. This is an asynchronous
* message from VPU to host.
@@ -1126,14 +1312,14 @@ struct vpu_ipc_msg_payload_hws_set_scheduling_log {
* @see VPU_JSM_MSG_HWS_SET_SCHEDULING_LOG
*/
struct vpu_ipc_msg_payload_hws_scheduling_log_notification {
- /* Engine ordinal */
+ /** Engine ordinal */
u32 engine_idx;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved_0;
};
-/*
- * @brief HWS suspend command queue request and done structure.
+/**
+ * HWS suspend command queue request and done structure.
* Host will request the suspend of contexts and VPU will;
* - Suspend all work on this context
* - Preempt any running work
@@ -1152,21 +1338,21 @@ struct vpu_ipc_msg_payload_hws_scheduling_log_notification {
* @see VPU_JSM_MSG_HWS_SUSPEND_CMDQ_DONE
*/
struct vpu_ipc_msg_payload_hws_suspend_cmdq {
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved_0;
- /* Command queue id */
+ /** Command queue id */
u64 cmdq_id;
- /*
+ /**
* Suspend fence value - reported by the VPU suspend context
* completed once suspend is complete.
*/
u64 suspend_fence_value;
};
-/*
- * @brief HWS Resume command queue request / response structure.
+/**
+ * HWS Resume command queue request / response structure.
* Host will request the resume of a context;
* - VPU will resume all work on this context
* - Scheduler will allow this context to be scheduled
@@ -1174,25 +1360,25 @@ struct vpu_ipc_msg_payload_hws_suspend_cmdq {
* @see VPU_JSM_MSG_HWS_RESUME_CMDQ_RSP
*/
struct vpu_ipc_msg_payload_hws_resume_cmdq {
- /* Host SSID */
+ /** Host SSID */
u32 host_ssid;
- /* Zero Padding */
+ /** Zero Padding */
u32 reserved_0;
- /* Command queue id */
+ /** Command queue id */
u64 cmdq_id;
};
-/*
- * @brief HWS Resume engine request / response structure.
- * After a HWS engine reset, all scheduling is stopped on VPU until a engine resume.
+/**
+ * HWS Resume engine request / response structure.
+ * After a HWS engine reset, all scheduling is stopped on VPU until an engine resume.
* Host shall send this command to resume scheduling of any valid queue.
- * @see VPU_JSM_MSG_HWS_RESUME_ENGINE
+ * @see VPU_JSM_MSG_HWS_ENGINE_RESUME
* @see VPU_JSM_MSG_HWS_RESUME_ENGINE_DONE
*/
struct vpu_ipc_msg_payload_hws_resume_engine {
- /* Engine to be resumed */
+ /** Engine to be resumed */
u32 engine_idx;
- /* Reserved */
+ /** Reserved */
u32 reserved_0;
};
@@ -1326,7 +1512,7 @@ struct vpu_jsm_metric_streamer_done {
/**
* Metric group description placed in the metric buffer after successful completion
* of the VPU_JSM_MSG_METRIC_STREAMER_INFO command. This is followed by one or more
- * @vpu_jsm_metric_counter_descriptor records.
+ * @ref vpu_jsm_metric_counter_descriptor records.
* @see VPU_JSM_MSG_METRIC_STREAMER_INFO
*/
struct vpu_jsm_metric_group_descriptor {
@@ -1413,29 +1599,24 @@ struct vpu_jsm_metric_counter_descriptor {
};
/**
- * Payload for VPU_JSM_MSG_DYNDBG_CONTROL requests.
+ * Payload for @ref VPU_JSM_MSG_DYNDBG_CONTROL requests.
*
- * VPU_JSM_MSG_DYNDBG_CONTROL are used to control the VPU FW Dynamic Debug
- * feature, which allows developers to selectively enable / disable MVLOG_DEBUG
- * messages. This is equivalent to the Dynamic Debug functionality provided by
- * Linux
- * (https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html)
- * The host can control Dynamic Debug behavior by sending dyndbg commands, which
- * have the same syntax as Linux
- * dyndbg commands.
+ * VPU_JSM_MSG_DYNDBG_CONTROL requests are used to control the VPU FW dynamic debug
+ * feature, which allows developers to selectively enable/disable code to obtain
+ * additional FW information. This is equivalent to the dynamic debug functionality
+ * provided by Linux. The host can control dynamic debug behavior by sending dyndbg
+ * commands, using the same syntax as for Linux dynamic debug commands.
*
- * NOTE: in order for MVLOG_DEBUG messages to be actually printed, the host
- * still has to set the logging level to MVLOG_DEBUG, using the
- * VPU_JSM_MSG_TRACE_SET_CONFIG command.
+ * @see https://www.kernel.org/doc/html/latest/admin-guide/dynamic-debug-howto.html.
*
- * The host can see the current dynamic debug configuration by executing a
- * special 'show' command. The dyndbg configuration will be printed to the
- * configured logging destination using MVLOG_INFO logging level.
+ * NOTE:
+ * As the dynamic debug feature uses MVLOG messages to provide information, the host
+ * must first set the logging level to MVLOG_DEBUG, using the @ref VPU_JSM_MSG_TRACE_SET_CONFIG
+ * command.
*/
struct vpu_ipc_msg_payload_dyndbg_control {
/**
- * Dyndbg command (same format as Linux dyndbg); must be a NULL-terminated
- * string.
+ * Dyndbg command to be executed.
*/
char dyndbg_cmd[VPU_DYNDBG_CMD_MAX_LEN];
};
@@ -1456,7 +1637,7 @@ struct vpu_ipc_msg_payload_pwr_d0i3_enter {
};
/**
- * Payload for VPU_JSM_MSG_DCT_ENABLE message.
+ * Payload for @ref VPU_JSM_MSG_DCT_ENABLE message.
*
* Default values for DCT active/inactive times are 5.3ms and 30ms respectively,
* corresponding to a 85% duty cycle. This payload allows the host to tune these
@@ -1513,28 +1694,28 @@ union vpu_ipc_msg_payload {
struct vpu_ipc_msg_payload_pwr_dct_control pwr_dct_control;
};
-/*
- * Host <-> LRT IPC message base structure.
+/**
+ * Host <-> NPU IPC message base structure.
*
* NOTE: All instances of this object must be aligned on a 64B boundary
* to allow proper handling of VPU cache operations.
*/
struct vpu_jsm_msg {
- /* Reserved */
+ /** Reserved */
u64 reserved_0;
- /* Message type, see vpu_ipc_msg_type enum. */
+ /** Message type, see @ref vpu_ipc_msg_type. */
u32 type;
- /* Buffer status, see vpu_ipc_msg_status enum. */
+ /** Buffer status, see @ref vpu_ipc_msg_status. */
u32 status;
- /*
+ /**
* Request ID, provided by the host in a request message and passed
* back by VPU in the response message.
*/
u32 request_id;
- /* Request return code set by the VPU, see VPU_JSM_STATUS_* defines. */
+ /** Request return code set by the VPU, see VPU_JSM_STATUS_* defines. */
u32 result;
u64 reserved_1;
- /* Message payload depending on message type, see vpu_ipc_msg_payload union. */
+ /** Message payload depending on message type, see vpu_ipc_msg_payload union. */
union vpu_ipc_msg_payload payload;
};
diff --git a/drivers/accel/qaic/Kconfig b/drivers/accel/qaic/Kconfig
index 5e405a19c157..116e42d152ca 100644
--- a/drivers/accel/qaic/Kconfig
+++ b/drivers/accel/qaic/Kconfig
@@ -9,6 +9,7 @@ config DRM_ACCEL_QAIC
depends on PCI && HAS_IOMEM
depends on MHI_BUS
select CRC32
+ select WANT_DEV_COREDUMP
help
Enables driver for Qualcomm's Cloud AI accelerator PCIe cards that are
designed to accelerate Deep Learning inference workloads.
diff --git a/drivers/accel/qaic/Makefile b/drivers/accel/qaic/Makefile
index 1106b876f737..71f727b74da3 100644
--- a/drivers/accel/qaic/Makefile
+++ b/drivers/accel/qaic/Makefile
@@ -11,6 +11,8 @@ qaic-y := \
qaic_data.o \
qaic_drv.o \
qaic_ras.o \
+ qaic_ssr.o \
+ qaic_sysfs.o \
qaic_timesync.o \
sahara.o
diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h
index 820d133236dd..fa7a8155658c 100644
--- a/drivers/accel/qaic/qaic.h
+++ b/drivers/accel/qaic/qaic.h
@@ -21,6 +21,7 @@
#define QAIC_DBC_BASE SZ_128K
#define QAIC_DBC_SIZE SZ_4K
+#define QAIC_SSR_DBC_SENTINEL U32_MAX /* No ongoing SSR sentinel */
#define QAIC_NO_PARTITION -1
@@ -47,6 +48,22 @@ enum __packed dev_states {
QAIC_ONLINE,
};
+enum dbc_states {
+ /* DBC is free and can be activated */
+ DBC_STATE_IDLE,
+ /* DBC is activated and a workload is running on device */
+ DBC_STATE_ASSIGNED,
+ /* Sub-system associated with this workload has crashed and it will shutdown soon */
+ DBC_STATE_BEFORE_SHUTDOWN,
+ /* Sub-system associated with this workload has crashed and it has shutdown */
+ DBC_STATE_AFTER_SHUTDOWN,
+ /* Sub-system associated with this workload is shutdown and it will be powered up soon */
+ DBC_STATE_BEFORE_POWER_UP,
+ /* Sub-system associated with this workload is now powered up */
+ DBC_STATE_AFTER_POWER_UP,
+ DBC_STATE_MAX,
+};
+
extern bool datapath_polling;
struct qaic_user {
@@ -114,6 +131,8 @@ struct dma_bridge_chan {
unsigned int irq;
/* Polling work item to simulate interrupts */
struct work_struct poll_work;
+ /* Represents various states of this DBC from enum dbc_states */
+ unsigned int state;
};
struct qaic_device {
@@ -161,6 +180,8 @@ struct qaic_device {
struct mhi_device *qts_ch;
/* Work queue for tasks related to MHI "QAIC_TIMESYNC" channel */
struct workqueue_struct *qts_wq;
+ /* MHI "QAIC_TIMESYNC_PERIODIC" channel device */
+ struct mhi_device *mqts_ch;
/* Head of list of page allocated by MHI bootlog device */
struct list_head bootlog;
/* MHI bootlog channel device */
@@ -177,6 +198,14 @@ struct qaic_device {
unsigned int ue_count;
/* Un-correctable non-fatal error count */
unsigned int ue_nf_count;
+ /* MHI SSR channel device */
+ struct mhi_device *ssr_ch;
+ /* Work queue for tasks related to MHI SSR device */
+ struct workqueue_struct *ssr_wq;
+ /* Buffer to collect SSR crashdump via SSR MHI channel */
+ void *ssr_mhi_buf;
+ /* DBC which is under SSR. Sentinel U32_MAX would mean that no SSR in progress */
+ u32 ssr_dbc;
};
struct qaic_drm_device {
@@ -195,6 +224,8 @@ struct qaic_drm_device {
struct list_head users;
/* Synchronizes access to users list */
struct mutex users_mutex;
+ /* Pointer to array of DBC sysfs attributes */
+ void *sysfs_attrs;
};
struct qaic_bo {
@@ -317,6 +348,13 @@ int qaic_partial_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm
int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
int qaic_detach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv);
-void irq_polling_work(struct work_struct *work);
+void qaic_irq_polling_work(struct work_struct *work);
+void qaic_dbc_enter_ssr(struct qaic_device *qdev, u32 dbc_id);
+void qaic_dbc_exit_ssr(struct qaic_device *qdev);
+
+/* qaic_sysfs.c */
+int qaic_sysfs_init(struct qaic_drm_device *qddev);
+void qaic_sysfs_remove(struct qaic_drm_device *qddev);
+void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state);
#endif /* _QAIC_H_ */
diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
index b86a8e48e731..428d8f65bff3 100644
--- a/drivers/accel/qaic/qaic_control.c
+++ b/drivers/accel/qaic/qaic_control.c
@@ -17,6 +17,7 @@
#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/scatterlist.h>
+#include <linux/sched/signal.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
@@ -30,7 +31,7 @@
#define MANAGE_MAGIC_NUMBER ((__force __le32)0x43494151) /* "QAIC" in little endian */
#define QAIC_DBC_Q_GAP SZ_256
#define QAIC_DBC_Q_BUF_ALIGN SZ_4K
-#define QAIC_MANAGE_EXT_MSG_LENGTH SZ_64K /* Max DMA message length */
+#define QAIC_MANAGE_WIRE_MSG_LENGTH SZ_64K /* Max DMA message length */
#define QAIC_WRAPPER_MAX_SIZE SZ_4K
#define QAIC_MHI_RETRY_WAIT_MS 100
#define QAIC_MHI_RETRY_MAX 20
@@ -309,6 +310,7 @@ static void save_dbc_buf(struct qaic_device *qdev, struct ioctl_resources *resou
enable_dbc(qdev, dbc_id, usr);
qdev->dbc[dbc_id].in_use = true;
resources->buf = NULL;
+ set_dbc_state(qdev, dbc_id, DBC_STATE_ASSIGNED);
}
}
@@ -367,7 +369,7 @@ static int encode_passthrough(struct qaic_device *qdev, void *trans, struct wrap
if (in_trans->hdr.len % 8 != 0)
return -EINVAL;
- if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_EXT_MSG_LENGTH)
+ if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_WIRE_MSG_LENGTH)
return -ENOSPC;
trans_wrapper = add_wrapper(wrappers,
@@ -495,7 +497,7 @@ static int encode_addr_size_pairs(struct dma_xfer *xfer, struct wrapper_list *wr
nents = sgt->nents;
nents_dma = nents;
- *size = QAIC_MANAGE_EXT_MSG_LENGTH - msg_hdr_len - sizeof(**out_trans);
+ *size = QAIC_MANAGE_WIRE_MSG_LENGTH - msg_hdr_len - sizeof(**out_trans);
for_each_sgtable_dma_sg(sgt, sg, i) {
*size -= sizeof(*asp);
/* Save 1K for possible follow-up transactions. */
@@ -576,7 +578,7 @@ static int encode_dma(struct qaic_device *qdev, void *trans, struct wrapper_list
/* There should be enough space to hold at least one ASP entry. */
if (size_add(msg_hdr_len, sizeof(*out_trans) + sizeof(struct wire_addr_size_pair)) >
- QAIC_MANAGE_EXT_MSG_LENGTH)
+ QAIC_MANAGE_WIRE_MSG_LENGTH)
return -ENOMEM;
xfer = kmalloc(sizeof(*xfer), GFP_KERNEL);
@@ -645,7 +647,7 @@ static int encode_activate(struct qaic_device *qdev, void *trans, struct wrapper
msg = &wrapper->msg;
msg_hdr_len = le32_to_cpu(msg->hdr.len);
- if (size_add(msg_hdr_len, sizeof(*out_trans)) > QAIC_MANAGE_MAX_MSG_LENGTH)
+ if (size_add(msg_hdr_len, sizeof(*out_trans)) > QAIC_MANAGE_WIRE_MSG_LENGTH)
return -ENOSPC;
if (!in_trans->queue_size)
@@ -655,8 +657,9 @@ static int encode_activate(struct qaic_device *qdev, void *trans, struct wrapper
return -EINVAL;
nelem = in_trans->queue_size;
- size = (get_dbc_req_elem_size() + get_dbc_rsp_elem_size()) * nelem;
- if (size / nelem != get_dbc_req_elem_size() + get_dbc_rsp_elem_size())
+ if (check_mul_overflow((u32)(get_dbc_req_elem_size() + get_dbc_rsp_elem_size()),
+ nelem,
+ &size))
return -EINVAL;
if (size + QAIC_DBC_Q_GAP + QAIC_DBC_Q_BUF_ALIGN < size)
@@ -729,7 +732,7 @@ static int encode_status(struct qaic_device *qdev, void *trans, struct wrapper_l
msg = &wrapper->msg;
msg_hdr_len = le32_to_cpu(msg->hdr.len);
- if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_MAX_MSG_LENGTH)
+ if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_WIRE_MSG_LENGTH)
return -ENOSPC;
trans_wrapper = add_wrapper(wrappers, sizeof(*trans_wrapper));
@@ -810,7 +813,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
}
if (ret)
- break;
+ goto out;
}
if (user_len != user_msg->len)
@@ -921,6 +924,7 @@ static int decode_deactivate(struct qaic_device *qdev, void *trans, u32 *msg_len
}
release_dbc(qdev, dbc_id);
+ set_dbc_state(qdev, dbc_id, DBC_STATE_IDLE);
*msg_len += sizeof(*in_trans);
return 0;
@@ -1052,7 +1056,7 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
init_completion(&elem.xfer_done);
if (likely(!qdev->cntl_lost_buf)) {
/*
- * The max size of request to device is QAIC_MANAGE_EXT_MSG_LENGTH.
+ * The max size of request to device is QAIC_MANAGE_WIRE_MSG_LENGTH.
* The max size of response from device is QAIC_MANAGE_MAX_MSG_LENGTH.
*/
out_buf = kmalloc(QAIC_MANAGE_MAX_MSG_LENGTH, GFP_KERNEL);
@@ -1079,7 +1083,6 @@ static void *msg_xfer(struct qaic_device *qdev, struct wrapper_list *wrappers, u
list_for_each_entry(w, &wrappers->list, list) {
kref_get(&w->ref_count);
- retry_count = 0;
ret = mhi_queue_buf(qdev->cntl_ch, DMA_TO_DEVICE, &w->msg, w->len,
list_is_last(&w->list, &wrappers->list) ? MHI_EOT : MHI_CHAIN);
if (ret) {
diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c
index c4f117edb266..60cb4d65d48e 100644
--- a/drivers/accel/qaic/qaic_data.c
+++ b/drivers/accel/qaic/qaic_data.c
@@ -18,6 +18,7 @@
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
+#include <linux/string.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
@@ -165,7 +166,7 @@ static void free_slice(struct kref *kref)
drm_gem_object_put(&slice->bo->base);
sg_free_table(slice->sgt);
kfree(slice->sgt);
- kfree(slice->reqs);
+ kvfree(slice->reqs);
kfree(slice);
}
@@ -404,7 +405,7 @@ static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo,
goto free_sgt;
}
- slice->reqs = kcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL);
+ slice->reqs = kvcalloc(sgt->nents, sizeof(*slice->reqs), GFP_KERNEL);
if (!slice->reqs) {
ret = -ENOMEM;
goto free_slice;
@@ -430,7 +431,7 @@ static int qaic_map_one_slice(struct qaic_device *qdev, struct qaic_bo *bo,
return 0;
free_req:
- kfree(slice->reqs);
+ kvfree(slice->reqs);
free_slice:
kfree(slice);
free_sgt:
@@ -643,8 +644,36 @@ static void qaic_free_object(struct drm_gem_object *obj)
kfree(bo);
}
+static struct sg_table *qaic_get_sg_table(struct drm_gem_object *obj)
+{
+ struct qaic_bo *bo = to_qaic_bo(obj);
+ struct scatterlist *sg, *sg_in;
+ struct sg_table *sgt, *sgt_in;
+ int i;
+
+ sgt_in = bo->sgt;
+
+ sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
+ if (!sgt)
+ return ERR_PTR(-ENOMEM);
+
+ if (sg_alloc_table(sgt, sgt_in->orig_nents, GFP_KERNEL)) {
+ kfree(sgt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ sg = sgt->sgl;
+ for_each_sgtable_sg(sgt_in, sg_in, i) {
+ memcpy(sg, sg_in, sizeof(*sg));
+ sg = sg_next(sg);
+ }
+
+ return sgt;
+}
+
static const struct drm_gem_object_funcs qaic_gem_funcs = {
.free = qaic_free_object,
+ .get_sg_table = qaic_get_sg_table,
.print_info = qaic_gem_print_info,
.mmap = qaic_gem_object_mmap,
.vm_ops = &drm_vm_ops,
@@ -953,8 +982,9 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
if (args->hdr.count == 0)
return -EINVAL;
- arg_size = args->hdr.count * sizeof(*slice_ent);
- if (arg_size / args->hdr.count != sizeof(*slice_ent))
+ if (check_mul_overflow((unsigned long)args->hdr.count,
+ (unsigned long)sizeof(*slice_ent),
+ &arg_size))
return -EINVAL;
if (!(args->hdr.dir == DMA_TO_DEVICE || args->hdr.dir == DMA_FROM_DEVICE))
@@ -984,18 +1014,12 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
user_data = u64_to_user_ptr(args->data);
- slice_ent = kzalloc(arg_size, GFP_KERNEL);
- if (!slice_ent) {
- ret = -EINVAL;
+ slice_ent = memdup_user(user_data, arg_size);
+ if (IS_ERR(slice_ent)) {
+ ret = PTR_ERR(slice_ent);
goto unlock_dev_srcu;
}
- ret = copy_from_user(slice_ent, user_data, arg_size);
- if (ret) {
- ret = -EFAULT;
- goto free_slice_ent;
- }
-
obj = drm_gem_object_lookup(file_priv, args->hdr.handle);
if (!obj) {
ret = -ENOENT;
@@ -1023,6 +1047,11 @@ int qaic_attach_slice_bo_ioctl(struct drm_device *dev, void *data, struct drm_fi
goto unlock_ch_srcu;
}
+ if (dbc->id == qdev->ssr_dbc) {
+ ret = -EPIPE;
+ goto unlock_ch_srcu;
+ }
+
ret = qaic_prepare_bo(qdev, bo, &args->hdr);
if (ret)
goto unlock_ch_srcu;
@@ -1300,8 +1329,6 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
int usr_rcu_id, qdev_rcu_id;
struct qaic_device *qdev;
struct qaic_user *usr;
- u8 __user *user_data;
- unsigned long n;
u64 received_ts;
u32 queue_level;
u64 submit_ts;
@@ -1314,20 +1341,12 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
received_ts = ktime_get_ns();
size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec);
- n = (unsigned long)size * args->hdr.count;
- if (args->hdr.count == 0 || n / args->hdr.count != size)
+ if (args->hdr.count == 0)
return -EINVAL;
- user_data = u64_to_user_ptr(args->data);
-
- exec = kcalloc(args->hdr.count, size, GFP_KERNEL);
- if (!exec)
- return -ENOMEM;
-
- if (copy_from_user(exec, user_data, n)) {
- ret = -EFAULT;
- goto free_exec;
- }
+ exec = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, size);
+ if (IS_ERR(exec))
+ return PTR_ERR(exec);
usr = file_priv->driver_priv;
usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
@@ -1356,6 +1375,11 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr
goto release_ch_rcu;
}
+ if (dbc->id == qdev->ssr_dbc) {
+ ret = -EPIPE;
+ goto release_ch_rcu;
+ }
+
ret = mutex_lock_interruptible(&dbc->req_lock);
if (ret)
goto release_ch_rcu;
@@ -1396,7 +1420,6 @@ unlock_dev_srcu:
srcu_read_unlock(&qdev->dev_lock, qdev_rcu_id);
unlock_usr_srcu:
srcu_read_unlock(&usr->qddev_lock, usr_rcu_id);
-free_exec:
kfree(exec);
return ret;
}
@@ -1491,7 +1514,7 @@ irqreturn_t dbc_irq_handler(int irq, void *data)
return IRQ_WAKE_THREAD;
}
-void irq_polling_work(struct work_struct *work)
+void qaic_irq_polling_work(struct work_struct *work)
{
struct dma_bridge_chan *dbc = container_of(work, struct dma_bridge_chan, poll_work);
unsigned long flags;
@@ -1709,6 +1732,11 @@ int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file
goto unlock_ch_srcu;
}
+ if (dbc->id == qdev->ssr_dbc) {
+ ret = -EPIPE;
+ goto unlock_ch_srcu;
+ }
+
obj = drm_gem_object_lookup(file_priv, args->handle);
if (!obj) {
ret = -ENOENT;
@@ -1729,6 +1757,9 @@ int qaic_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file
if (!dbc->usr)
ret = -EPERM;
+ if (dbc->id == qdev->ssr_dbc)
+ ret = -EPIPE;
+
put_obj:
drm_gem_object_put(obj);
unlock_ch_srcu:
@@ -1749,7 +1780,8 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file
struct qaic_device *qdev;
struct qaic_user *usr;
struct qaic_bo *bo;
- int ret, i;
+ int ret = 0;
+ int i;
usr = file_priv->driver_priv;
usr_rcu_id = srcu_read_lock(&usr->qddev_lock);
@@ -1770,18 +1802,12 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file
goto unlock_dev_srcu;
}
- ent = kcalloc(args->hdr.count, sizeof(*ent), GFP_KERNEL);
- if (!ent) {
- ret = -EINVAL;
+ ent = memdup_array_user(u64_to_user_ptr(args->data), args->hdr.count, sizeof(*ent));
+ if (IS_ERR(ent)) {
+ ret = PTR_ERR(ent);
goto unlock_dev_srcu;
}
- ret = copy_from_user(ent, u64_to_user_ptr(args->data), args->hdr.count * sizeof(*ent));
- if (ret) {
- ret = -EFAULT;
- goto free_ent;
- }
-
for (i = 0; i < args->hdr.count; i++) {
obj = drm_gem_object_lookup(file_priv, ent[i].handle);
if (!obj) {
@@ -1789,6 +1815,16 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file
goto free_ent;
}
bo = to_qaic_bo(obj);
+ if (!bo->sliced) {
+ drm_gem_object_put(obj);
+ ret = -EINVAL;
+ goto free_ent;
+ }
+ if (bo->dbc->id != args->hdr.dbc_id) {
+ drm_gem_object_put(obj);
+ ret = -EINVAL;
+ goto free_ent;
+ }
/*
* perf stats ioctl is called before wait ioctl is complete then
* the latency information is invalid.
@@ -1927,6 +1963,17 @@ static void empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *db
spin_unlock_irqrestore(&dbc->xfer_lock, flags);
}
+static void sync_empty_xfer_list(struct qaic_device *qdev, struct dma_bridge_chan *dbc)
+{
+ empty_xfer_list(qdev, dbc);
+ synchronize_srcu(&dbc->ch_lock);
+ /*
+ * Threads holding channel lock, may add more elements in the xfer_list.
+ * Flush out these elements from xfer_list.
+ */
+ empty_xfer_list(qdev, dbc);
+}
+
int disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
{
if (!qdev->dbc[dbc_id].usr || qdev->dbc[dbc_id].usr->handle != usr->handle)
@@ -1941,7 +1988,7 @@ int disable_dbc(struct qaic_device *qdev, u32 dbc_id, struct qaic_user *usr)
* enable_dbc - Enable the DBC. DBCs are disabled by removing the context of
* user. Add user context back to DBC to enable it. This function trusts the
* DBC ID passed and expects the DBC to be disabled.
- * @qdev: Qranium device handle
+ * @qdev: qaic device handle
* @dbc_id: ID of the DBC
* @usr: User context
*/
@@ -1955,13 +2002,7 @@ void wakeup_dbc(struct qaic_device *qdev, u32 dbc_id)
struct dma_bridge_chan *dbc = &qdev->dbc[dbc_id];
dbc->usr = NULL;
- empty_xfer_list(qdev, dbc);
- synchronize_srcu(&dbc->ch_lock);
- /*
- * Threads holding channel lock, may add more elements in the xfer_list.
- * Flush out these elements from xfer_list.
- */
- empty_xfer_list(qdev, dbc);
+ sync_empty_xfer_list(qdev, dbc);
}
void release_dbc(struct qaic_device *qdev, u32 dbc_id)
@@ -2002,3 +2043,30 @@ void qaic_data_get_fifo_info(struct dma_bridge_chan *dbc, u32 *head, u32 *tail)
*head = readl(dbc->dbc_base + REQHP_OFF);
*tail = readl(dbc->dbc_base + REQTP_OFF);
}
+
+/*
+ * qaic_dbc_enter_ssr - Prepare to enter in sub system reset(SSR) for given DBC ID.
+ * @qdev: qaic device handle
+ * @dbc_id: ID of the DBC which will enter SSR
+ *
+ * The device will automatically deactivate the workload as not
+ * all errors can be silently recovered. The user will be
+ * notified and will need to decide the required recovery
+ * action to take.
+ */
+void qaic_dbc_enter_ssr(struct qaic_device *qdev, u32 dbc_id)
+{
+ qdev->ssr_dbc = dbc_id;
+ release_dbc(qdev, dbc_id);
+}
+
+/*
+ * qaic_dbc_exit_ssr - Prepare to exit from sub system reset(SSR) for given DBC ID.
+ * @qdev: qaic device handle
+ *
+ * The DBC returns to an operational state and begins accepting work after exiting SSR.
+ */
+void qaic_dbc_exit_ssr(struct qaic_device *qdev)
+{
+ qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL;
+}
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
index e162f4b8a262..4c70bd949d53 100644
--- a/drivers/accel/qaic/qaic_drv.c
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -30,6 +30,7 @@
#include "qaic.h"
#include "qaic_debugfs.h"
#include "qaic_ras.h"
+#include "qaic_ssr.h"
#include "qaic_timesync.h"
#include "sahara.h"
@@ -270,6 +271,13 @@ static int qaic_create_drm_device(struct qaic_device *qdev, s32 partition_id)
return ret;
}
+ ret = qaic_sysfs_init(qddev);
+ if (ret) {
+ drm_dev_unregister(drm);
+ pci_dbg(qdev->pdev, "qaic_sysfs_init failed %d\n", ret);
+ return ret;
+ }
+
qaic_debugfs_init(qddev);
return ret;
@@ -281,6 +289,7 @@ static void qaic_destroy_drm_device(struct qaic_device *qdev, s32 partition_id)
struct drm_device *drm = to_drm(qddev);
struct qaic_user *usr;
+ qaic_sysfs_remove(qddev);
drm_dev_unregister(drm);
qddev->partition_id = 0;
/*
@@ -382,6 +391,7 @@ void qaic_dev_reset_clean_local_state(struct qaic_device *qdev)
qaic_notify_reset(qdev);
/* start tearing things down */
+ qaic_clean_up_ssr(qdev);
for (i = 0; i < qdev->num_dbc; ++i)
release_dbc(qdev, i);
}
@@ -431,11 +441,18 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev,
qdev->qts_wq = qaicm_wq_init(drm, "qaic_ts");
if (IS_ERR(qdev->qts_wq))
return NULL;
+ qdev->ssr_wq = qaicm_wq_init(drm, "qaic_ssr");
+ if (IS_ERR(qdev->ssr_wq))
+ return NULL;
ret = qaicm_srcu_init(drm, &qdev->dev_lock);
if (ret)
return NULL;
+ ret = qaic_ssr_init(qdev, drm);
+ if (ret)
+ pci_info(pdev, "QAIC SSR crashdump collection not supported.\n");
+
qdev->qddev = qddev;
qdev->pdev = pdev;
qddev->qdev = qdev;
@@ -545,7 +562,7 @@ static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev)
qdev->dbc[i].irq = pci_irq_vector(pdev, qdev->single_msi ? 0 : i + 1);
if (!qdev->single_msi)
disable_irq_nosync(qdev->dbc[i].irq);
- INIT_WORK(&qdev->dbc[i].poll_work, irq_polling_work);
+ INIT_WORK(&qdev->dbc[i].poll_work, qaic_irq_polling_work);
}
}
@@ -660,6 +677,92 @@ static const struct pci_error_handlers qaic_pci_err_handler = {
.reset_done = qaic_pci_reset_done,
};
+static bool qaic_is_under_reset(struct qaic_device *qdev)
+{
+ int rcu_id;
+ bool ret;
+
+ rcu_id = srcu_read_lock(&qdev->dev_lock);
+ ret = qdev->dev_state != QAIC_ONLINE;
+ srcu_read_unlock(&qdev->dev_lock, rcu_id);
+ return ret;
+}
+
+static bool qaic_data_path_busy(struct qaic_device *qdev)
+{
+ bool ret = false;
+ int dev_rcu_id;
+ int i;
+
+ dev_rcu_id = srcu_read_lock(&qdev->dev_lock);
+ if (qdev->dev_state != QAIC_ONLINE) {
+ srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
+ return false;
+ }
+ for (i = 0; i < qdev->num_dbc; i++) {
+ struct dma_bridge_chan *dbc = &qdev->dbc[i];
+ unsigned long flags;
+ int ch_rcu_id;
+
+ ch_rcu_id = srcu_read_lock(&dbc->ch_lock);
+ if (!dbc->usr || !dbc->in_use) {
+ srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
+ continue;
+ }
+ spin_lock_irqsave(&dbc->xfer_lock, flags);
+ ret = !list_empty(&dbc->xfer_list);
+ spin_unlock_irqrestore(&dbc->xfer_lock, flags);
+ srcu_read_unlock(&dbc->ch_lock, ch_rcu_id);
+ if (ret)
+ break;
+ }
+ srcu_read_unlock(&qdev->dev_lock, dev_rcu_id);
+ return ret;
+}
+
+static int qaic_pm_suspend(struct device *dev)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
+
+ dev_dbg(dev, "Suspending..\n");
+ if (qaic_data_path_busy(qdev)) {
+ dev_dbg(dev, "Device's datapath is busy. Aborting suspend..\n");
+ return -EBUSY;
+ }
+ if (qaic_is_under_reset(qdev)) {
+ dev_dbg(dev, "Device is under reset. Aborting suspend..\n");
+ return -EBUSY;
+ }
+ qaic_mqts_ch_stop_timer(qdev->mqts_ch);
+ qaic_pci_reset_prepare(qdev->pdev);
+ pci_save_state(qdev->pdev);
+ pci_disable_device(qdev->pdev);
+ pci_set_power_state(qdev->pdev, PCI_D3hot);
+ return 0;
+}
+
+static int qaic_pm_resume(struct device *dev)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
+ int ret;
+
+ dev_dbg(dev, "Resuming..\n");
+ pci_set_power_state(qdev->pdev, PCI_D0);
+ pci_restore_state(qdev->pdev);
+ ret = pci_enable_device(qdev->pdev);
+ if (ret) {
+ dev_err(dev, "pci_enable_device failed on resume %d\n", ret);
+ return ret;
+ }
+ pci_set_master(qdev->pdev);
+ qaic_pci_reset_done(qdev->pdev);
+ return 0;
+}
+
+static const struct dev_pm_ops qaic_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(qaic_pm_suspend, qaic_pm_resume)
+};
+
static struct pci_driver qaic_pci_driver = {
.name = QAIC_NAME,
.id_table = qaic_ids,
@@ -667,6 +770,9 @@ static struct pci_driver qaic_pci_driver = {
.remove = qaic_pci_remove,
.shutdown = qaic_pci_shutdown,
.err_handler = &qaic_pci_err_handler,
+ .driver = {
+ .pm = pm_sleep_ptr(&qaic_pm_ops),
+ },
};
static int __init qaic_init(void)
@@ -702,9 +808,16 @@ static int __init qaic_init(void)
ret = qaic_ras_register();
if (ret)
pr_debug("qaic: qaic_ras_register failed %d\n", ret);
+ ret = qaic_ssr_register();
+ if (ret) {
+ pr_debug("qaic: qaic_ssr_register failed %d\n", ret);
+ goto free_bootlog;
+ }
return 0;
+free_bootlog:
+ qaic_bootlog_unregister();
free_mhi:
mhi_driver_unregister(&qaic_mhi_driver);
free_pci:
@@ -730,6 +843,7 @@ static void __exit qaic_exit(void)
* reinitializing the link_up state after the cleanup is done.
*/
link_up = true;
+ qaic_ssr_unregister();
qaic_ras_unregister();
qaic_bootlog_unregister();
qaic_timesync_deinit();
diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c
index 914ffc4a9970..f1d52a710136 100644
--- a/drivers/accel/qaic/qaic_ras.c
+++ b/drivers/accel/qaic/qaic_ras.c
@@ -514,21 +514,21 @@ static ssize_t ce_count_show(struct device *dev, struct device_attribute *attr,
{
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
- return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ce_count);
+ return sysfs_emit(buf, "%d\n", qdev->ce_count);
}
static ssize_t ue_count_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
- return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_count);
+ return sysfs_emit(buf, "%d\n", qdev->ue_count);
}
static ssize_t ue_nonfatal_count_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(dev));
- return snprintf(buf, PAGE_SIZE, "%d\n", qdev->ue_nf_count);
+ return sysfs_emit(buf, "%d\n", qdev->ue_nf_count);
}
static DEVICE_ATTR_RO(ce_count);
diff --git a/drivers/accel/qaic/qaic_ssr.c b/drivers/accel/qaic/qaic_ssr.c
new file mode 100644
index 000000000000..9b662d690371
--- /dev/null
+++ b/drivers/accel/qaic/qaic_ssr.c
@@ -0,0 +1,815 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+#include <asm/byteorder.h>
+#include <drm/drm_file.h>
+#include <drm/drm_managed.h>
+#include <linux/devcoredump.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mhi.h>
+#include <linux/workqueue.h>
+
+#include "qaic.h"
+#include "qaic_ssr.h"
+
+#define SSR_RESP_MSG_SZ 32
+#define SSR_MHI_BUF_SIZE SZ_64K
+#define SSR_MEM_READ_DATA_SIZE ((u64)SSR_MHI_BUF_SIZE - sizeof(struct ssr_crashdump))
+#define SSR_MEM_READ_CHUNK_SIZE ((u64)SSR_MEM_READ_DATA_SIZE - sizeof(struct ssr_memory_read_rsp))
+
+#define DEBUG_TRANSFER_INFO BIT(0)
+#define DEBUG_TRANSFER_INFO_RSP BIT(1)
+#define MEMORY_READ BIT(2)
+#define MEMORY_READ_RSP BIT(3)
+#define DEBUG_TRANSFER_DONE BIT(4)
+#define DEBUG_TRANSFER_DONE_RSP BIT(5)
+#define SSR_EVENT BIT(8)
+#define SSR_EVENT_RSP BIT(9)
+
+#define SSR_EVENT_NACK BIT(0)
+#define BEFORE_SHUTDOWN BIT(1)
+#define AFTER_SHUTDOWN BIT(2)
+#define BEFORE_POWER_UP BIT(3)
+#define AFTER_POWER_UP BIT(4)
+
+struct debug_info_table {
+ /* Save preferences. Default is mandatory */
+ u64 save_perf;
+ /* Base address of the debug region */
+ u64 mem_base;
+ /* Size of debug region in bytes */
+ u64 len;
+ /* Description */
+ char desc[20];
+ /* Filename of debug region */
+ char filename[20];
+};
+
+struct _ssr_hdr {
+ __le32 cmd;
+ __le32 len;
+ __le32 dbc_id;
+};
+
+struct ssr_hdr {
+ u32 cmd;
+ u32 len;
+ u32 dbc_id;
+};
+
+struct ssr_debug_transfer_info {
+ struct ssr_hdr hdr;
+ u32 resv;
+ u64 tbl_addr;
+ u64 tbl_len;
+} __packed;
+
+struct ssr_debug_transfer_info_rsp {
+ struct _ssr_hdr hdr;
+ __le32 ret;
+} __packed;
+
+struct ssr_memory_read {
+ struct _ssr_hdr hdr;
+ __le32 resv;
+ __le64 addr;
+ __le64 len;
+} __packed;
+
+struct ssr_memory_read_rsp {
+ struct _ssr_hdr hdr;
+ __le32 resv;
+ u8 data[];
+} __packed;
+
+struct ssr_debug_transfer_done {
+ struct _ssr_hdr hdr;
+ __le32 resv;
+} __packed;
+
+struct ssr_debug_transfer_done_rsp {
+ struct _ssr_hdr hdr;
+ __le32 ret;
+} __packed;
+
+struct ssr_event {
+ struct ssr_hdr hdr;
+ u32 event;
+} __packed;
+
+struct ssr_event_rsp {
+ struct _ssr_hdr hdr;
+ __le32 event;
+} __packed;
+
+struct ssr_resp {
+ /* Work struct to schedule work coming on QAIC_SSR channel */
+ struct work_struct work;
+ /* Root struct of device, used to access device resources */
+ struct qaic_device *qdev;
+ /* Buffer used by MHI for transfer requests */
+ u8 data[] __aligned(8);
+};
+
+/* SSR crashdump book keeping structure */
+struct ssr_dump_info {
+ /* DBC associated with this SSR crashdump */
+ struct dma_bridge_chan *dbc;
+ /*
+ * It will be used when we complete the crashdump download and switch
+ * to waiting on SSR events
+ */
+ struct ssr_resp *resp;
+ /* MEMORY READ request MHI buffer.*/
+ struct ssr_memory_read *read_buf_req;
+ /* TRUE: ->read_buf_req is queued for MHI transaction. FALSE: Otherwise */
+ bool read_buf_req_queued;
+ /* Address of table in host */
+ void *tbl_addr;
+ /* Total size of table */
+ u64 tbl_len;
+ /* Offset of table(->tbl_addr) where the new chunk will be dumped */
+ u64 tbl_off;
+ /* Address of table in device/target */
+ u64 tbl_addr_dev;
+ /* Ptr to the entire dump */
+ void *dump_addr;
+ /* Entire crashdump size */
+ u64 dump_sz;
+ /* Offset of crashdump(->dump_addr) where the new chunk will be dumped */
+ u64 dump_off;
+ /* Points to the table entry we are currently downloading */
+ struct debug_info_table *tbl_ent;
+ /* Offset in the current table entry(->tbl_ent) for next chuck */
+ u64 tbl_ent_off;
+};
+
+struct ssr_crashdump {
+ /*
+ * Points to a book keeping struct maintained by MHI SSR device while
+ * downloading a SSR crashdump. It is NULL when crashdump downloading
+ * not in progress.
+ */
+ struct ssr_dump_info *dump_info;
+ /* Work struct to schedule work coming on QAIC_SSR channel */
+ struct work_struct work;
+ /* Root struct of device, used to access device resources */
+ struct qaic_device *qdev;
+ /* Buffer used by MHI for transfer requests */
+ u8 data[];
+};
+
+#define QAIC_SSR_DUMP_V1_MAGIC 0x1234567890abcdef
+#define QAIC_SSR_DUMP_V1_VER 1
+struct dump_file_meta {
+ u64 magic;
+ u64 version;
+ u64 size; /* Total size of the entire dump */
+ u64 tbl_len; /* Length of the table in byte */
+};
+
+/*
+ * Layout of crashdump
+ * +------------------------------------------+
+ * | Crashdump Meta structure |
+ * | type: struct dump_file_meta |
+ * +------------------------------------------+
+ * | Crashdump Table |
+ * | type: array of struct debug_info_table |
+ * | |
+ * | |
+ * | |
+ * +------------------------------------------+
+ * | Crashdump |
+ * | |
+ * | |
+ * | |
+ * | |
+ * | |
+ * +------------------------------------------+
+ */
+
+static void free_ssr_dump_info(struct ssr_crashdump *ssr_crash)
+{
+ struct ssr_dump_info *dump_info = ssr_crash->dump_info;
+
+ ssr_crash->dump_info = NULL;
+ if (!dump_info)
+ return;
+ if (!dump_info->read_buf_req_queued)
+ kfree(dump_info->read_buf_req);
+ vfree(dump_info->tbl_addr);
+ vfree(dump_info->dump_addr);
+ kfree(dump_info);
+}
+
+void qaic_clean_up_ssr(struct qaic_device *qdev)
+{
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+
+ if (!ssr_crash)
+ return;
+
+ qaic_dbc_exit_ssr(qdev);
+ free_ssr_dump_info(ssr_crash);
+}
+
+static int alloc_dump(struct ssr_dump_info *dump_info)
+{
+ struct debug_info_table *tbl_ent = dump_info->tbl_addr;
+ struct dump_file_meta *dump_meta;
+ u64 tbl_sz_lp = 0;
+ u64 dump_size = 0;
+
+ while (tbl_sz_lp < dump_info->tbl_len) {
+ le64_to_cpus(&tbl_ent->save_perf);
+ le64_to_cpus(&tbl_ent->mem_base);
+ le64_to_cpus(&tbl_ent->len);
+
+ if (tbl_ent->len == 0)
+ return -EINVAL;
+
+ dump_size += tbl_ent->len;
+ tbl_ent++;
+ tbl_sz_lp += sizeof(*tbl_ent);
+ }
+
+ dump_info->dump_sz = dump_size + dump_info->tbl_len + sizeof(*dump_meta);
+ dump_info->dump_addr = vzalloc(dump_info->dump_sz);
+ if (!dump_info->dump_addr)
+ return -ENOMEM;
+
+ /* Copy crashdump meta and table */
+ dump_meta = dump_info->dump_addr;
+ dump_meta->magic = QAIC_SSR_DUMP_V1_MAGIC;
+ dump_meta->version = QAIC_SSR_DUMP_V1_VER;
+ dump_meta->size = dump_info->dump_sz;
+ dump_meta->tbl_len = dump_info->tbl_len;
+ memcpy(dump_info->dump_addr + sizeof(*dump_meta), dump_info->tbl_addr, dump_info->tbl_len);
+ /* Offset by crashdump meta and table (copied above) */
+ dump_info->dump_off = dump_info->tbl_len + sizeof(*dump_meta);
+
+ return 0;
+}
+
+static int send_xfer_done(struct qaic_device *qdev, void *resp, u32 dbc_id)
+{
+ struct ssr_debug_transfer_done *xfer_done;
+ int ret;
+
+ xfer_done = kmalloc(sizeof(*xfer_done), GFP_KERNEL);
+ if (!xfer_done) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp, SSR_RESP_MSG_SZ, MHI_EOT);
+ if (ret)
+ goto free_xfer_done;
+
+ xfer_done->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_DONE);
+ xfer_done->hdr.len = cpu_to_le32(sizeof(*xfer_done));
+ xfer_done->hdr.dbc_id = cpu_to_le32(dbc_id);
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, xfer_done, sizeof(*xfer_done), MHI_EOT);
+ if (ret)
+ goto free_xfer_done;
+
+ return 0;
+
+free_xfer_done:
+ kfree(xfer_done);
+out:
+ return ret;
+}
+
+static int mem_read_req(struct qaic_device *qdev, u64 dest_addr, u64 dest_len)
+{
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ struct ssr_memory_read *read_buf_req;
+ struct ssr_dump_info *dump_info;
+ int ret;
+
+ dump_info = ssr_crash->dump_info;
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, ssr_crash->data, SSR_MEM_READ_DATA_SIZE,
+ MHI_EOT);
+ if (ret)
+ goto out;
+
+ read_buf_req = dump_info->read_buf_req;
+ read_buf_req->hdr.cmd = cpu_to_le32(MEMORY_READ);
+ read_buf_req->hdr.len = cpu_to_le32(sizeof(*read_buf_req));
+ read_buf_req->hdr.dbc_id = cpu_to_le32(qdev->ssr_dbc);
+ read_buf_req->addr = cpu_to_le64(dest_addr);
+ read_buf_req->len = cpu_to_le64(dest_len);
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, read_buf_req, sizeof(*read_buf_req),
+ MHI_EOT);
+ if (!ret)
+ dump_info->read_buf_req_queued = true;
+
+out:
+ return ret;
+}
+
+static int ssr_copy_table(struct ssr_dump_info *dump_info, void *data, u64 len)
+{
+ if (len > dump_info->tbl_len - dump_info->tbl_off)
+ return -EINVAL;
+
+ memcpy(dump_info->tbl_addr + dump_info->tbl_off, data, len);
+ dump_info->tbl_off += len;
+
+ /* Entire table has been downloaded, alloc dump memory */
+ if (dump_info->tbl_off == dump_info->tbl_len) {
+ dump_info->tbl_ent = dump_info->tbl_addr;
+ return alloc_dump(dump_info);
+ }
+
+ return 0;
+}
+
+static int ssr_copy_dump(struct ssr_dump_info *dump_info, void *data, u64 len)
+{
+ struct debug_info_table *tbl_ent;
+
+ tbl_ent = dump_info->tbl_ent;
+
+ if (len > tbl_ent->len - dump_info->tbl_ent_off)
+ return -EINVAL;
+
+ memcpy(dump_info->dump_addr + dump_info->dump_off, data, len);
+ dump_info->dump_off += len;
+ dump_info->tbl_ent_off += len;
+
+ /*
+ * Current segment (a entry in table) of the crashdump is complete,
+ * move to next one
+ */
+ if (tbl_ent->len == dump_info->tbl_ent_off) {
+ dump_info->tbl_ent++;
+ dump_info->tbl_ent_off = 0;
+ }
+
+ return 0;
+}
+
+static void ssr_dump_worker(struct work_struct *work)
+{
+ struct ssr_crashdump *ssr_crash = container_of(work, struct ssr_crashdump, work);
+ struct qaic_device *qdev = ssr_crash->qdev;
+ struct ssr_memory_read_rsp *mem_rd_resp;
+ struct debug_info_table *tbl_ent;
+ struct ssr_dump_info *dump_info;
+ u64 dest_addr, dest_len;
+ struct _ssr_hdr *_hdr;
+ struct ssr_hdr hdr;
+ u64 data_len;
+ int ret;
+
+ mem_rd_resp = (struct ssr_memory_read_rsp *)ssr_crash->data;
+ _hdr = &mem_rd_resp->hdr;
+ hdr.cmd = le32_to_cpu(_hdr->cmd);
+ hdr.len = le32_to_cpu(_hdr->len);
+ hdr.dbc_id = le32_to_cpu(_hdr->dbc_id);
+
+ if (hdr.dbc_id != qdev->ssr_dbc)
+ goto reset_device;
+
+ dump_info = ssr_crash->dump_info;
+ if (!dump_info)
+ goto reset_device;
+
+ if (hdr.cmd != MEMORY_READ_RSP)
+ goto free_dump_info;
+
+ if (hdr.len > SSR_MEM_READ_DATA_SIZE)
+ goto free_dump_info;
+
+ data_len = hdr.len - sizeof(*mem_rd_resp);
+
+ if (dump_info->tbl_off < dump_info->tbl_len) /* Chunk belongs to table */
+ ret = ssr_copy_table(dump_info, mem_rd_resp->data, data_len);
+ else /* Chunk belongs to crashdump */
+ ret = ssr_copy_dump(dump_info, mem_rd_resp->data, data_len);
+
+ if (ret)
+ goto free_dump_info;
+
+ if (dump_info->tbl_off < dump_info->tbl_len) {
+ /* Continue downloading table */
+ dest_addr = dump_info->tbl_addr_dev + dump_info->tbl_off;
+ dest_len = min(SSR_MEM_READ_CHUNK_SIZE, dump_info->tbl_len - dump_info->tbl_off);
+ ret = mem_read_req(qdev, dest_addr, dest_len);
+ } else if (dump_info->dump_off < dump_info->dump_sz) {
+ /* Continue downloading crashdump */
+ tbl_ent = dump_info->tbl_ent;
+ dest_addr = tbl_ent->mem_base + dump_info->tbl_ent_off;
+ dest_len = min(SSR_MEM_READ_CHUNK_SIZE, tbl_ent->len - dump_info->tbl_ent_off);
+ ret = mem_read_req(qdev, dest_addr, dest_len);
+ } else {
+ /* Crashdump download complete */
+ ret = send_xfer_done(qdev, dump_info->resp->data, hdr.dbc_id);
+ }
+
+ /* Most likely a MHI xfer has failed */
+ if (ret)
+ goto free_dump_info;
+
+ return;
+
+free_dump_info:
+ /* Free the allocated memory */
+ free_ssr_dump_info(ssr_crash);
+reset_device:
+ /*
+ * After subsystem crashes in device crashdump collection begins but
+ * something went wrong while collecting crashdump, now instead of
+ * handling this error we just reset the device as the best effort has
+ * been made
+ */
+ mhi_soc_reset(qdev->mhi_cntrl);
+}
+
+static struct ssr_dump_info *alloc_dump_info(struct qaic_device *qdev,
+ struct ssr_debug_transfer_info *debug_info)
+{
+ struct ssr_dump_info *dump_info;
+ int ret;
+
+ le64_to_cpus(&debug_info->tbl_len);
+ le64_to_cpus(&debug_info->tbl_addr);
+
+ if (debug_info->tbl_len == 0 ||
+ debug_info->tbl_len % sizeof(struct debug_info_table) != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Allocate SSR crashdump book keeping structure */
+ dump_info = kzalloc(sizeof(*dump_info), GFP_KERNEL);
+ if (!dump_info) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Buffer used to send MEMORY READ request to device via MHI */
+ dump_info->read_buf_req = kzalloc(sizeof(*dump_info->read_buf_req), GFP_KERNEL);
+ if (!dump_info->read_buf_req) {
+ ret = -ENOMEM;
+ goto free_dump_info;
+ }
+
+ /* Crashdump meta table buffer */
+ dump_info->tbl_addr = vzalloc(debug_info->tbl_len);
+ if (!dump_info->tbl_addr) {
+ ret = -ENOMEM;
+ goto free_read_buf_req;
+ }
+
+ dump_info->tbl_addr_dev = debug_info->tbl_addr;
+ dump_info->tbl_len = debug_info->tbl_len;
+
+ return dump_info;
+
+free_read_buf_req:
+ kfree(dump_info->read_buf_req);
+free_dump_info:
+ kfree(dump_info);
+out:
+ return ERR_PTR(ret);
+}
+
+static int dbg_xfer_info_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
+ struct ssr_debug_transfer_info *debug_info)
+{
+ struct ssr_debug_transfer_info_rsp *debug_rsp;
+ struct ssr_crashdump *ssr_crash = NULL;
+ int ret = 0, ret2;
+
+ debug_rsp = kmalloc(sizeof(*debug_rsp), GFP_KERNEL);
+ if (!debug_rsp)
+ return -ENOMEM;
+
+ if (!qdev->ssr_mhi_buf) {
+ ret = -ENOMEM;
+ goto send_rsp;
+ }
+
+ if (dbc->state != DBC_STATE_BEFORE_POWER_UP) {
+ ret = -EINVAL;
+ goto send_rsp;
+ }
+
+ ssr_crash = qdev->ssr_mhi_buf;
+ ssr_crash->dump_info = alloc_dump_info(qdev, debug_info);
+ if (IS_ERR(ssr_crash->dump_info)) {
+ ret = PTR_ERR(ssr_crash->dump_info);
+ ssr_crash->dump_info = NULL;
+ }
+
+send_rsp:
+ debug_rsp->hdr.cmd = cpu_to_le32(DEBUG_TRANSFER_INFO_RSP);
+ debug_rsp->hdr.len = cpu_to_le32(sizeof(*debug_rsp));
+ debug_rsp->hdr.dbc_id = cpu_to_le32(dbc->id);
+ /*
+ * 0 = Return an ACK confirming the host is ready to download crashdump
+ * 1 = Return an NACK confirming the host is not ready to download crashdump
+ */
+ debug_rsp->ret = cpu_to_le32(ret ? 1 : 0);
+
+ ret2 = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, debug_rsp, sizeof(*debug_rsp), MHI_EOT);
+ if (ret2) {
+ free_ssr_dump_info(ssr_crash);
+ kfree(debug_rsp);
+ return ret2;
+ }
+
+ return ret;
+}
+
+static void dbg_xfer_done_rsp(struct qaic_device *qdev, struct dma_bridge_chan *dbc,
+ struct ssr_debug_transfer_done_rsp *xfer_rsp)
+{
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ u32 status = le32_to_cpu(xfer_rsp->ret);
+ struct device *dev = &qdev->pdev->dev;
+ struct ssr_dump_info *dump_info;
+
+ dump_info = ssr_crash->dump_info;
+ if (!dump_info)
+ return;
+
+ if (status) {
+ free_ssr_dump_info(ssr_crash);
+ return;
+ }
+
+ dev_coredumpv(dev, dump_info->dump_addr, dump_info->dump_sz, GFP_KERNEL);
+ /* dev_coredumpv will free dump_info->dump_addr */
+ dump_info->dump_addr = NULL;
+ free_ssr_dump_info(ssr_crash);
+}
+
+static void ssr_worker(struct work_struct *work)
+{
+ struct ssr_resp *resp = container_of(work, struct ssr_resp, work);
+ struct ssr_hdr *hdr = (struct ssr_hdr *)resp->data;
+ struct ssr_dump_info *dump_info = NULL;
+ struct qaic_device *qdev = resp->qdev;
+ struct ssr_crashdump *ssr_crash;
+ struct ssr_event_rsp *event_rsp;
+ struct dma_bridge_chan *dbc;
+ struct ssr_event *event;
+ u32 ssr_event_ack;
+ int ret;
+
+ le32_to_cpus(&hdr->cmd);
+ le32_to_cpus(&hdr->len);
+ le32_to_cpus(&hdr->dbc_id);
+
+ if (hdr->len > SSR_RESP_MSG_SZ)
+ goto out;
+
+ if (hdr->dbc_id >= qdev->num_dbc)
+ goto out;
+
+ dbc = &qdev->dbc[hdr->dbc_id];
+
+ switch (hdr->cmd) {
+ case DEBUG_TRANSFER_INFO:
+ ret = dbg_xfer_info_rsp(qdev, dbc, (struct ssr_debug_transfer_info *)resp->data);
+ if (ret)
+ break;
+
+ ssr_crash = qdev->ssr_mhi_buf;
+ dump_info = ssr_crash->dump_info;
+ dump_info->dbc = dbc;
+ dump_info->resp = resp;
+
+ /* Start by downloading debug table */
+ ret = mem_read_req(qdev, dump_info->tbl_addr_dev,
+ min(dump_info->tbl_len, SSR_MEM_READ_CHUNK_SIZE));
+ if (ret) {
+ free_ssr_dump_info(ssr_crash);
+ break;
+ }
+
+ /*
+ * Till now everything went fine, which means that we will be
+ * collecting crashdump chunk by chunk. Do not queue a response
+ * buffer for SSR cmds till the crashdump is complete.
+ */
+ return;
+ case SSR_EVENT:
+ event = (struct ssr_event *)hdr;
+ le32_to_cpus(&event->event);
+ ssr_event_ack = event->event;
+ ssr_crash = qdev->ssr_mhi_buf;
+
+ switch (event->event) {
+ case BEFORE_SHUTDOWN:
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_SHUTDOWN);
+ qaic_dbc_enter_ssr(qdev, hdr->dbc_id);
+ break;
+ case AFTER_SHUTDOWN:
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_SHUTDOWN);
+ break;
+ case BEFORE_POWER_UP:
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_BEFORE_POWER_UP);
+ break;
+ case AFTER_POWER_UP:
+ /*
+ * If dump info is a non NULL value it means that we
+ * have received this SSR event while downloading a
+ * crashdump for this DBC is still in progress. NACK
+ * the SSR event
+ */
+ if (ssr_crash && ssr_crash->dump_info) {
+ free_ssr_dump_info(ssr_crash);
+ ssr_event_ack = SSR_EVENT_NACK;
+ break;
+ }
+
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_AFTER_POWER_UP);
+ break;
+ default:
+ break;
+ }
+
+ event_rsp = kmalloc(sizeof(*event_rsp), GFP_KERNEL);
+ if (!event_rsp)
+ break;
+
+ event_rsp->hdr.cmd = cpu_to_le32(SSR_EVENT_RSP);
+ event_rsp->hdr.len = cpu_to_le32(sizeof(*event_rsp));
+ event_rsp->hdr.dbc_id = cpu_to_le32(hdr->dbc_id);
+ event_rsp->event = cpu_to_le32(ssr_event_ack);
+
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_TO_DEVICE, event_rsp, sizeof(*event_rsp),
+ MHI_EOT);
+ if (ret)
+ kfree(event_rsp);
+
+ if (event->event == AFTER_POWER_UP && ssr_event_ack != SSR_EVENT_NACK) {
+ qaic_dbc_exit_ssr(qdev);
+ set_dbc_state(qdev, hdr->dbc_id, DBC_STATE_IDLE);
+ }
+
+ break;
+ case DEBUG_TRANSFER_DONE_RSP:
+ dbg_xfer_done_rsp(qdev, dbc, (struct ssr_debug_transfer_done_rsp *)hdr);
+ break;
+ default:
+ break;
+ }
+
+out:
+ ret = mhi_queue_buf(qdev->ssr_ch, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
+ if (ret)
+ kfree(resp);
+}
+
+static int qaic_ssr_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
+{
+ struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
+ struct ssr_resp *resp;
+ int ret;
+
+ ret = mhi_prepare_for_transfer(mhi_dev);
+ if (ret)
+ return ret;
+
+ resp = kzalloc(sizeof(*resp) + SSR_RESP_MSG_SZ, GFP_KERNEL);
+ if (!resp) {
+ mhi_unprepare_from_transfer(mhi_dev);
+ return -ENOMEM;
+ }
+
+ resp->qdev = qdev;
+ INIT_WORK(&resp->work, ssr_worker);
+
+ ret = mhi_queue_buf(mhi_dev, DMA_FROM_DEVICE, resp->data, SSR_RESP_MSG_SZ, MHI_EOT);
+ if (ret) {
+ kfree(resp);
+ mhi_unprepare_from_transfer(mhi_dev);
+ return ret;
+ }
+
+ dev_set_drvdata(&mhi_dev->dev, qdev);
+ qdev->ssr_ch = mhi_dev;
+
+ return 0;
+}
+
+static void qaic_ssr_mhi_remove(struct mhi_device *mhi_dev)
+{
+ struct qaic_device *qdev;
+
+ qdev = dev_get_drvdata(&mhi_dev->dev);
+ mhi_unprepare_from_transfer(qdev->ssr_ch);
+ qdev->ssr_ch = NULL;
+}
+
+static void qaic_ssr_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ struct _ssr_hdr *hdr = mhi_result->buf_addr;
+ struct ssr_dump_info *dump_info;
+
+ if (mhi_result->transaction_status) {
+ kfree(mhi_result->buf_addr);
+ return;
+ }
+
+ /*
+ * MEMORY READ is used to download crashdump. And crashdump is
+ * downloaded chunk by chunk in a series of MEMORY READ SSR commands.
+ * Hence to avoid too many kmalloc() and kfree() of the same MEMORY READ
+ * request buffer, we allocate only one such buffer and free it only
+ * once.
+ */
+ if (le32_to_cpu(hdr->cmd) == MEMORY_READ) {
+ dump_info = ssr_crash->dump_info;
+ if (dump_info) {
+ dump_info->read_buf_req_queued = false;
+ return;
+ }
+ }
+
+ kfree(mhi_result->buf_addr);
+}
+
+static void qaic_ssr_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
+{
+ struct ssr_resp *resp = container_of(mhi_result->buf_addr, struct ssr_resp, data);
+ struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
+ struct ssr_crashdump *ssr_crash = qdev->ssr_mhi_buf;
+ bool memory_read_rsp = false;
+
+ if (ssr_crash && ssr_crash->data == mhi_result->buf_addr)
+ memory_read_rsp = true;
+
+ if (mhi_result->transaction_status) {
+ /* Do not free SSR crashdump buffer as it allocated via managed APIs */
+ if (!memory_read_rsp)
+ kfree(resp);
+ return;
+ }
+
+ if (memory_read_rsp)
+ queue_work(qdev->ssr_wq, &ssr_crash->work);
+ else
+ queue_work(qdev->ssr_wq, &resp->work);
+}
+
+static const struct mhi_device_id qaic_ssr_mhi_match_table[] = {
+ { .chan = "QAIC_SSR", },
+ {},
+};
+
+static struct mhi_driver qaic_ssr_mhi_driver = {
+ .id_table = qaic_ssr_mhi_match_table,
+ .remove = qaic_ssr_mhi_remove,
+ .probe = qaic_ssr_mhi_probe,
+ .ul_xfer_cb = qaic_ssr_mhi_ul_xfer_cb,
+ .dl_xfer_cb = qaic_ssr_mhi_dl_xfer_cb,
+ .driver = {
+ .name = "qaic_ssr",
+ },
+};
+
+int qaic_ssr_init(struct qaic_device *qdev, struct drm_device *drm)
+{
+ struct ssr_crashdump *ssr_crash;
+
+ qdev->ssr_dbc = QAIC_SSR_DBC_SENTINEL;
+
+ /*
+ * Device requests only one SSR at a time. So allocating only one
+ * buffer to download crashdump is good enough.
+ */
+ ssr_crash = drmm_kzalloc(drm, SSR_MHI_BUF_SIZE, GFP_KERNEL);
+ if (!ssr_crash)
+ return -ENOMEM;
+
+ ssr_crash->qdev = qdev;
+ INIT_WORK(&ssr_crash->work, ssr_dump_worker);
+ qdev->ssr_mhi_buf = ssr_crash;
+
+ return 0;
+}
+
+int qaic_ssr_register(void)
+{
+ return mhi_driver_register(&qaic_ssr_mhi_driver);
+}
+
+void qaic_ssr_unregister(void)
+{
+ mhi_driver_unregister(&qaic_ssr_mhi_driver);
+}
diff --git a/drivers/accel/qaic/qaic_ssr.h b/drivers/accel/qaic/qaic_ssr.h
new file mode 100644
index 000000000000..97ccff305750
--- /dev/null
+++ b/drivers/accel/qaic/qaic_ssr.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2021, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __QAIC_SSR_H__
+#define __QAIC_SSR_H__
+
+struct drm_device;
+struct qaic_device;
+
+int qaic_ssr_register(void);
+void qaic_ssr_unregister(void);
+void qaic_clean_up_ssr(struct qaic_device *qdev);
+int qaic_ssr_init(struct qaic_device *qdev, struct drm_device *drm);
+#endif /* __QAIC_SSR_H__ */
diff --git a/drivers/accel/qaic/qaic_sysfs.c b/drivers/accel/qaic/qaic_sysfs.c
new file mode 100644
index 000000000000..e0afb0ffb589
--- /dev/null
+++ b/drivers/accel/qaic/qaic_sysfs.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/* Copyright (c) 2020-2025, The Linux Foundation. All rights reserved. */
+
+#include <drm/drm_file.h>
+#include <drm/drm_managed.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+
+#include "qaic.h"
+
+#define NAME_LEN 14
+
+struct dbc_attribute {
+ struct device_attribute dev_attr;
+ u32 dbc_id;
+ char name[NAME_LEN];
+};
+
+static ssize_t dbc_state_show(struct device *dev, struct device_attribute *a, char *buf)
+{
+ struct dbc_attribute *dbc_attr = container_of(a, struct dbc_attribute, dev_attr);
+ struct drm_minor *minor = dev_get_drvdata(dev);
+ struct qaic_device *qdev;
+
+ qdev = to_qaic_device(minor->dev);
+ return sysfs_emit(buf, "%d\n", qdev->dbc[dbc_attr->dbc_id].state);
+}
+
+void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state)
+{
+ struct device *kdev = to_accel_kdev(qdev->qddev);
+ char *envp[3] = {};
+ char state_str[16];
+ char id_str[12];
+
+ envp[0] = id_str;
+ envp[1] = state_str;
+
+ if (state >= DBC_STATE_MAX)
+ return;
+ if (dbc_id >= qdev->num_dbc)
+ return;
+ if (state == qdev->dbc[dbc_id].state)
+ return;
+
+ scnprintf(id_str, ARRAY_SIZE(id_str), "DBC_ID=%d", dbc_id);
+ scnprintf(state_str, ARRAY_SIZE(state_str), "DBC_STATE=%d", state);
+
+ qdev->dbc[dbc_id].state = state;
+ kobject_uevent_env(&kdev->kobj, KOBJ_CHANGE, envp);
+}
+
+int qaic_sysfs_init(struct qaic_drm_device *qddev)
+{
+ struct device *kdev = to_accel_kdev(qddev);
+ struct drm_device *drm = to_drm(qddev);
+ u32 num_dbc = qddev->qdev->num_dbc;
+ struct dbc_attribute *dbc_attrs;
+ int i, ret;
+
+ dbc_attrs = drmm_kcalloc(drm, num_dbc, sizeof(*dbc_attrs), GFP_KERNEL);
+ if (!dbc_attrs)
+ return -ENOMEM;
+
+ for (i = 0; i < num_dbc; ++i) {
+ struct dbc_attribute *dbc_attr = &dbc_attrs[i];
+
+ sysfs_attr_init(&dbc_attr->dev_attr.attr);
+ dbc_attr->dbc_id = i;
+ scnprintf(dbc_attr->name, NAME_LEN, "dbc%d_state", i);
+ dbc_attr->dev_attr.attr.name = dbc_attr->name;
+ dbc_attr->dev_attr.attr.mode = 0444;
+ dbc_attr->dev_attr.show = dbc_state_show;
+ ret = sysfs_create_file(&kdev->kobj, &dbc_attr->dev_attr.attr);
+ if (ret) {
+ int j;
+
+ for (j = 0; j < i; ++j) {
+ dbc_attr = &dbc_attrs[j];
+ sysfs_remove_file(&kdev->kobj, &dbc_attr->dev_attr.attr);
+ }
+ drmm_kfree(drm, dbc_attrs);
+ return ret;
+ }
+ }
+
+ qddev->sysfs_attrs = dbc_attrs;
+ return 0;
+}
+
+void qaic_sysfs_remove(struct qaic_drm_device *qddev)
+{
+ struct dbc_attribute *dbc_attrs = qddev->sysfs_attrs;
+ struct device *kdev = to_accel_kdev(qddev);
+ u32 num_dbc = qddev->qdev->num_dbc;
+ int i;
+
+ if (!dbc_attrs)
+ return;
+
+ qddev->sysfs_attrs = NULL;
+ for (i = 0; i < num_dbc; ++i)
+ sysfs_remove_file(&kdev->kobj, &dbc_attrs[i].dev_attr.attr);
+ drmm_kfree(to_drm(qddev), dbc_attrs);
+}
diff --git a/drivers/accel/qaic/qaic_timesync.c b/drivers/accel/qaic/qaic_timesync.c
index 3fac540f8e03..8af2475f4f36 100644
--- a/drivers/accel/qaic/qaic_timesync.c
+++ b/drivers/accel/qaic/qaic_timesync.c
@@ -171,6 +171,13 @@ mod_timer:
dev_err(mqtsdev->dev, "%s mod_timer error:%d\n", __func__, ret);
}
+void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev)
+{
+ struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
+
+ timer_delete_sync(&mqtsdev->timer);
+}
+
static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
{
struct qaic_device *qdev = pci_get_drvdata(to_pci_dev(mhi_dev->mhi_cntrl->cntrl_dev));
@@ -206,6 +213,7 @@ static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_devi
timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms);
add_timer(timer);
dev_set_drvdata(&mhi_dev->dev, mqtsdev);
+ qdev->mqts_ch = mhi_dev;
return 0;
@@ -221,6 +229,7 @@ static void qaic_timesync_remove(struct mhi_device *mhi_dev)
{
struct mqts_dev *mqtsdev = dev_get_drvdata(&mhi_dev->dev);
+ mqtsdev->qdev->mqts_ch = NULL;
timer_delete_sync(&mqtsdev->timer);
mhi_unprepare_from_transfer(mqtsdev->mhi_dev);
kfree(mqtsdev->sync_msg);
diff --git a/drivers/accel/qaic/qaic_timesync.h b/drivers/accel/qaic/qaic_timesync.h
index 851b7acd43bb..77b9c2b55057 100644
--- a/drivers/accel/qaic/qaic_timesync.h
+++ b/drivers/accel/qaic/qaic_timesync.h
@@ -6,6 +6,9 @@
#ifndef __QAIC_TIMESYNC_H__
#define __QAIC_TIMESYNC_H__
+#include <linux/mhi.h>
+
int qaic_timesync_init(void);
void qaic_timesync_deinit(void);
+void qaic_mqts_ch_stop_timer(struct mhi_device *mhi_dev);
#endif /* __QAIC_TIMESYNC_H__ */
diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c
index 3ebcc1f7ff58..fd3c3b2d1fd3 100644
--- a/drivers/accel/qaic/sahara.c
+++ b/drivers/accel/qaic/sahara.c
@@ -159,6 +159,7 @@ struct sahara_context {
struct sahara_packet *rx;
struct work_struct fw_work;
struct work_struct dump_work;
+ struct work_struct read_data_work;
struct mhi_device *mhi_dev;
const char * const *image_table;
u32 table_size;
@@ -174,7 +175,10 @@ struct sahara_context {
u64 dump_image_offset;
void *mem_dump_freespace;
u64 dump_images_left;
+ u32 read_data_offset;
+ u32 read_data_length;
bool is_mem_dump_mode;
+ bool non_streaming;
};
static const char * const aic100_image_table[] = {
@@ -194,6 +198,7 @@ static const char * const aic200_image_table[] = {
[23] = "qcom/aic200/aop.mbn",
[32] = "qcom/aic200/tz.mbn",
[33] = "qcom/aic200/hypvm.mbn",
+ [38] = "qcom/aic200/xbl_config.elf",
[39] = "qcom/aic200/aic200_abl.elf",
[40] = "qcom/aic200/apdp.mbn",
[41] = "qcom/aic200/devcfg.mbn",
@@ -202,6 +207,7 @@ static const char * const aic200_image_table[] = {
[49] = "qcom/aic200/shrm.elf",
[50] = "qcom/aic200/cpucp.elf",
[51] = "qcom/aic200/aop_devcfg.mbn",
+ [54] = "qcom/aic200/qupv3fw.elf",
[57] = "qcom/aic200/cpucp_dtbs.elf",
[62] = "qcom/aic200/uefi_dtbs.elf",
[63] = "qcom/aic200/xbl_ac_config.mbn",
@@ -213,9 +219,15 @@ static const char * const aic200_image_table[] = {
[69] = "qcom/aic200/dcd.mbn",
[73] = "qcom/aic200/gearvm.mbn",
[74] = "qcom/aic200/sti.bin",
- [75] = "qcom/aic200/pvs.bin",
+ [76] = "qcom/aic200/tz_qti_config.mbn",
+ [78] = "qcom/aic200/pvs.bin",
};
+static bool is_streaming(struct sahara_context *context)
+{
+ return !context->non_streaming;
+}
+
static int sahara_find_image(struct sahara_context *context, u32 image_id)
{
int ret;
@@ -265,6 +277,8 @@ static void sahara_send_reset(struct sahara_context *context)
int ret;
context->is_mem_dump_mode = false;
+ context->read_data_offset = 0;
+ context->read_data_length = 0;
context->tx[0]->cmd = cpu_to_le32(SAHARA_RESET_CMD);
context->tx[0]->length = cpu_to_le32(SAHARA_RESET_LENGTH);
@@ -319,9 +333,39 @@ static void sahara_hello(struct sahara_context *context)
dev_err(&context->mhi_dev->dev, "Unable to send hello response %d\n", ret);
}
+static int read_data_helper(struct sahara_context *context, int buf_index)
+{
+ enum mhi_flags mhi_flag;
+ u32 pkt_data_len;
+ int ret;
+
+ pkt_data_len = min(context->read_data_length, SAHARA_PACKET_MAX_SIZE);
+
+ memcpy(context->tx[buf_index],
+ &context->firmware->data[context->read_data_offset],
+ pkt_data_len);
+
+ context->read_data_offset += pkt_data_len;
+ context->read_data_length -= pkt_data_len;
+
+ if (is_streaming(context) || !context->read_data_length)
+ mhi_flag = MHI_EOT;
+ else
+ mhi_flag = MHI_CHAIN;
+
+ ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
+ context->tx[buf_index], pkt_data_len, mhi_flag);
+ if (ret) {
+ dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static void sahara_read_data(struct sahara_context *context)
{
- u32 image_id, data_offset, data_len, pkt_data_len;
+ u32 image_id, data_offset, data_len;
int ret;
int i;
@@ -357,7 +401,7 @@ static void sahara_read_data(struct sahara_context *context)
* and is not needed here on error.
*/
- if (data_len > SAHARA_TRANSFER_MAX_SIZE) {
+ if (context->non_streaming && data_len > SAHARA_TRANSFER_MAX_SIZE) {
dev_err(&context->mhi_dev->dev, "Malformed read_data packet - data len %d exceeds max xfer size %d\n",
data_len, SAHARA_TRANSFER_MAX_SIZE);
sahara_send_reset(context);
@@ -378,22 +422,18 @@ static void sahara_read_data(struct sahara_context *context)
return;
}
- for (i = 0; i < SAHARA_NUM_TX_BUF && data_len; ++i) {
- pkt_data_len = min(data_len, SAHARA_PACKET_MAX_SIZE);
-
- memcpy(context->tx[i], &context->firmware->data[data_offset], pkt_data_len);
+ context->read_data_offset = data_offset;
+ context->read_data_length = data_len;
- data_offset += pkt_data_len;
- data_len -= pkt_data_len;
+ if (is_streaming(context)) {
+ schedule_work(&context->read_data_work);
+ return;
+ }
- ret = mhi_queue_buf(context->mhi_dev, DMA_TO_DEVICE,
- context->tx[i], pkt_data_len,
- !data_len ? MHI_EOT : MHI_CHAIN);
- if (ret) {
- dev_err(&context->mhi_dev->dev, "Unable to send read_data response %d\n",
- ret);
- return;
- }
+ for (i = 0; i < SAHARA_NUM_TX_BUF && context->read_data_length; ++i) {
+ ret = read_data_helper(context, i);
+ if (ret)
+ break;
}
}
@@ -538,6 +578,7 @@ static void sahara_parse_dump_table(struct sahara_context *context)
struct sahara_memory_dump_meta_v1 *dump_meta;
u64 table_nents;
u64 dump_length;
+ u64 mul_bytes;
int ret;
u64 i;
@@ -551,8 +592,9 @@ static void sahara_parse_dump_table(struct sahara_context *context)
dev_table[i].description[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
dev_table[i].filename[SAHARA_TABLE_ENTRY_STR_LEN - 1] = 0;
- dump_length = size_add(dump_length, le64_to_cpu(dev_table[i].length));
- if (dump_length == SIZE_MAX) {
+ if (check_add_overflow(dump_length,
+ le64_to_cpu(dev_table[i].length),
+ &dump_length)) {
/* Discard the dump */
sahara_send_reset(context);
return;
@@ -568,14 +610,17 @@ static void sahara_parse_dump_table(struct sahara_context *context)
dev_table[i].filename);
}
- dump_length = size_add(dump_length, sizeof(*dump_meta));
- if (dump_length == SIZE_MAX) {
+ if (check_add_overflow(dump_length, (u64)sizeof(*dump_meta), &dump_length)) {
/* Discard the dump */
sahara_send_reset(context);
return;
}
- dump_length = size_add(dump_length, size_mul(sizeof(*image_out_table), table_nents));
- if (dump_length == SIZE_MAX) {
+ if (check_mul_overflow((u64)sizeof(*image_out_table), table_nents, &mul_bytes)) {
+ /* Discard the dump */
+ sahara_send_reset(context);
+ return;
+ }
+ if (check_add_overflow(dump_length, mul_bytes, &dump_length)) {
/* Discard the dump */
sahara_send_reset(context);
return;
@@ -615,7 +660,7 @@ static void sahara_parse_dump_table(struct sahara_context *context)
/* Request the first chunk of the first image */
context->dump_image = &image_out_table[0];
- dump_length = min(context->dump_image->length, SAHARA_READ_MAX_SIZE);
+ dump_length = min_t(u64, context->dump_image->length, SAHARA_READ_MAX_SIZE);
/* Avoid requesting EOI sized data so that we can identify errors */
if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
@@ -663,7 +708,7 @@ static void sahara_parse_dump_image(struct sahara_context *context)
/* Get next image chunk */
dump_length = context->dump_image->length - context->dump_image_offset;
- dump_length = min(dump_length, SAHARA_READ_MAX_SIZE);
+ dump_length = min_t(u64, dump_length, SAHARA_READ_MAX_SIZE);
/* Avoid requesting EOI sized data so that we can identify errors */
if (dump_length == SAHARA_END_OF_IMAGE_LENGTH)
dump_length = SAHARA_END_OF_IMAGE_LENGTH / 2;
@@ -742,6 +787,13 @@ error:
sahara_send_reset(context);
}
+static void sahara_read_data_processing(struct work_struct *work)
+{
+ struct sahara_context *context = container_of(work, struct sahara_context, read_data_work);
+
+ read_data_helper(context, 0);
+}
+
static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id)
{
struct sahara_context *context;
@@ -756,34 +808,56 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
if (!context->rx)
return -ENOMEM;
+ if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
+ context->image_table = aic200_image_table;
+ context->table_size = ARRAY_SIZE(aic200_image_table);
+ } else {
+ context->image_table = aic100_image_table;
+ context->table_size = ARRAY_SIZE(aic100_image_table);
+ context->non_streaming = true;
+ }
+
/*
- * AIC100 defines SAHARA_TRANSFER_MAX_SIZE as the largest value it
- * will request for READ_DATA. This is larger than
- * SAHARA_PACKET_MAX_SIZE, and we need 9x SAHARA_PACKET_MAX_SIZE to
- * cover SAHARA_TRANSFER_MAX_SIZE. When the remote side issues a
- * READ_DATA, it requires a transfer of the exact size requested. We
- * can use MHI_CHAIN to link multiple buffers into a single transfer
- * but the remote side will not consume the buffers until it sees an
- * EOT, thus we need to allocate enough buffers to put in the tx fifo
- * to cover an entire READ_DATA request of the max size.
+ * There are two firmware implementations for READ_DATA handling.
+ * The older "SBL" implementation defines a Sahara transfer size, and
+ * expects that the response is a single transport transfer. If the
+ * FW wants to transfer a file that is larger than the transfer size,
+ * the FW will issue multiple READ_DATA commands. For this
+ * implementation, we need to allocate enough buffers to contain the
+ * entire Sahara transfer size.
+ *
+ * The newer "XBL" implementation does not define a maximum transfer
+ * size and instead expects the data to be streamed over using the
+ * transport level MTU. The FW will issue a single READ_DATA command
+ * of whatever size, and consume multiple transport level transfers
+ * until the expected amount of data is consumed. For this
+ * implementation we only need a single buffer of the transport MTU
+ * but we'll need to be able to use it multiple times for a single
+ * READ_DATA request.
+ *
+ * AIC100 is the SBL implementation and defines SAHARA_TRANSFER_MAX_SIZE
+ * and we need 9x SAHARA_PACKET_MAX_SIZE to cover that. We can use
+ * MHI_CHAIN to link multiple buffers into a single transfer but the
+ * remote side will not consume the buffers until it sees an EOT, thus
+ * we need to allocate enough buffers to put in the tx fifo to cover an
+ * entire READ_DATA request of the max size.
+ *
+ * AIC200 is the XBL implementation, and so a single buffer will work.
*/
for (i = 0; i < SAHARA_NUM_TX_BUF; ++i) {
- context->tx[i] = devm_kzalloc(&mhi_dev->dev, SAHARA_PACKET_MAX_SIZE, GFP_KERNEL);
+ context->tx[i] = devm_kzalloc(&mhi_dev->dev,
+ SAHARA_PACKET_MAX_SIZE,
+ GFP_KERNEL);
if (!context->tx[i])
return -ENOMEM;
+ if (is_streaming(context))
+ break;
}
context->mhi_dev = mhi_dev;
INIT_WORK(&context->fw_work, sahara_processing);
INIT_WORK(&context->dump_work, sahara_dump_processing);
-
- if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
- context->image_table = aic200_image_table;
- context->table_size = ARRAY_SIZE(aic200_image_table);
- } else {
- context->image_table = aic100_image_table;
- context->table_size = ARRAY_SIZE(aic100_image_table);
- }
+ INIT_WORK(&context->read_data_work, sahara_read_data_processing);
context->active_image_id = SAHARA_IMAGE_ID_NONE;
dev_set_drvdata(&mhi_dev->dev, context);
@@ -814,6 +888,10 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev)
static void sahara_mhi_ul_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
{
+ struct sahara_context *context = dev_get_drvdata(&mhi_dev->dev);
+
+ if (!mhi_result->transaction_status && context->read_data_length && is_streaming(context))
+ schedule_work(&context->read_data_work);
}
static void sahara_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_result *mhi_result)
diff --git a/drivers/accel/rocket/rocket_gem.c b/drivers/accel/rocket/rocket_gem.c
index 0551e11cc184..624c4ecf5a34 100644
--- a/drivers/accel/rocket/rocket_gem.c
+++ b/drivers/accel/rocket/rocket_gem.c
@@ -2,6 +2,7 @@
/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
#include <drm/drm_device.h>
+#include <drm/drm_print.h>
#include <drm/drm_utils.h>
#include <drm/rocket_accel.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/acpi/acpi_mrrm.c b/drivers/acpi/acpi_mrrm.c
index 47ea3ccc2142..6d69554c940e 100644
--- a/drivers/acpi/acpi_mrrm.c
+++ b/drivers/acpi/acpi_mrrm.c
@@ -63,6 +63,9 @@ static __init int acpi_parse_mrrm(struct acpi_table_header *table)
if (!mrrm)
return -ENODEV;
+ if (mrrm->header.revision != 1)
+ return -EINVAL;
+
if (mrrm->flags & ACPI_MRRM_FLAGS_REGION_ASSIGNMENT_OS)
return -EOPNOTSUPP;
@@ -149,26 +152,49 @@ ATTRIBUTE_GROUPS(memory_range);
static __init int add_boot_memory_ranges(void)
{
- struct kobject *pkobj, *kobj;
+ struct kobject *pkobj, *kobj, **kobjs;
int ret = -EINVAL;
- char *name;
+ char name[16];
+ int i;
pkobj = kobject_create_and_add("memory_ranges", acpi_kobj);
+ if (!pkobj)
+ return -ENOMEM;
- for (int i = 0; i < mrrm_mem_entry_num; i++) {
- name = kasprintf(GFP_KERNEL, "range%d", i);
- if (!name) {
- ret = -ENOMEM;
- break;
- }
+ kobjs = kcalloc(mrrm_mem_entry_num, sizeof(*kobjs), GFP_KERNEL);
+ if (!kobjs) {
+ kobject_put(pkobj);
+ return -ENOMEM;
+ }
+ for (i = 0; i < mrrm_mem_entry_num; i++) {
+ scnprintf(name, sizeof(name), "range%d", i);
kobj = kobject_create_and_add(name, pkobj);
+ if (!kobj) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
ret = sysfs_create_groups(kobj, memory_range_groups);
- if (ret)
- return ret;
+ if (ret) {
+ kobject_put(kobj);
+ goto cleanup;
+ }
+ kobjs[i] = kobj;
}
+ kfree(kobjs);
+ return 0;
+
+cleanup:
+ for (int j = 0; j < i; j++) {
+ if (kobjs[j]) {
+ sysfs_remove_groups(kobjs[j], memory_range_groups);
+ kobject_put(kobjs[j]);
+ }
+ }
+ kfree(kobjs);
+ kobject_put(pkobj);
return ret;
}
diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c
index 33418dd6768a..6d870d97ada6 100644
--- a/drivers/acpi/acpi_tad.c
+++ b/drivers/acpi/acpi_tad.c
@@ -90,19 +90,18 @@ static int acpi_tad_set_real_time(struct device *dev, struct acpi_tad_rt *rt)
args[0].buffer.pointer = (u8 *)rt;
args[0].buffer.length = sizeof(*rt);
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, "_SRT", &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status) || retval)
return -EIO;
return 0;
}
-static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt)
+static int acpi_tad_evaluate_grt(struct device *dev, struct acpi_tad_rt *rt)
{
acpi_handle handle = ACPI_HANDLE(dev);
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER };
@@ -111,12 +110,7 @@ static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt)
acpi_status status;
int ret = -EIO;
- pm_runtime_get_sync(dev);
-
status = acpi_evaluate_object(handle, "_GRT", NULL, &output);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status))
goto out_free;
@@ -139,6 +133,21 @@ out_free:
return ret;
}
+static int acpi_tad_get_real_time(struct device *dev, struct acpi_tad_rt *rt)
+{
+ int ret;
+
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
+
+ ret = acpi_tad_evaluate_grt(dev, rt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static char *acpi_tad_rt_next_field(char *s, int *val)
{
char *p;
@@ -266,12 +275,11 @@ static int acpi_tad_wake_set(struct device *dev, char *method, u32 timer_id,
args[0].integer.value = timer_id;
args[1].integer.value = value;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status) || retval)
return -EIO;
@@ -314,12 +322,11 @@ static ssize_t acpi_tad_wake_read(struct device *dev, char *buf, char *method,
args[0].integer.value = timer_id;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, method, &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status))
return -EIO;
@@ -370,12 +377,11 @@ static int acpi_tad_clear_status(struct device *dev, u32 timer_id)
args[0].integer.value = timer_id;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, "_CWS", &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status) || retval)
return -EIO;
@@ -411,12 +417,11 @@ static ssize_t acpi_tad_status_read(struct device *dev, char *buf, u32 timer_id)
args[0].integer.value = timer_id;
- pm_runtime_get_sync(dev);
+ PM_RUNTIME_ACQUIRE(dev, pm);
+ if (PM_RUNTIME_ACQUIRE_ERR(&pm))
+ return -ENXIO;
status = acpi_evaluate_integer(handle, "_GWS", &arg_list, &retval);
-
- pm_runtime_put_sync(dev);
-
if (ACPI_FAILURE(status))
return -EIO;
@@ -563,8 +568,6 @@ static void acpi_tad_remove(struct platform_device *pdev)
device_init_wakeup(dev, false);
- pm_runtime_get_sync(dev);
-
if (dd->capabilities & ACPI_TAD_RT)
sysfs_remove_group(&dev->kobj, &acpi_tad_time_attr_group);
@@ -573,14 +576,16 @@ static void acpi_tad_remove(struct platform_device *pdev)
sysfs_remove_group(&dev->kobj, &acpi_tad_attr_group);
- acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
- acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
- if (dd->capabilities & ACPI_TAD_DC_WAKE) {
- acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
- acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
+ scoped_guard(pm_runtime_noresume, dev) {
+ acpi_tad_disable_timer(dev, ACPI_TAD_AC_TIMER);
+ acpi_tad_clear_status(dev, ACPI_TAD_AC_TIMER);
+ if (dd->capabilities & ACPI_TAD_DC_WAKE) {
+ acpi_tad_disable_timer(dev, ACPI_TAD_DC_TIMER);
+ acpi_tad_clear_status(dev, ACPI_TAD_DC_TIMER);
+ }
}
- pm_runtime_put_sync(dev);
+ pm_runtime_suspend(dev);
pm_runtime_disable(dev);
acpi_remove_cmos_rtc_space_handler(handle);
}
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index 103f29661576..be8e7e18abca 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -1959,8 +1959,10 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
struct acpi_video_device *dev;
mutex_lock(&video->device_list_lock);
- list_for_each_entry(dev, &video->video_device_list, entry)
+ list_for_each_entry(dev, &video->video_device_list, entry) {
acpi_video_dev_remove_notify_handler(dev);
+ cancel_delayed_work_sync(&dev->switch_brightness_work);
+ }
mutex_unlock(&video->device_list_lock);
acpi_video_bus_stop_devices(video);
diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c
index a2ac06a26e92..5670ff5a43cd 100644
--- a/drivers/acpi/acpica/nswalk.c
+++ b/drivers/acpi/acpica/nswalk.c
@@ -169,9 +169,12 @@ acpi_ns_walk_namespace(acpi_object_type type,
if (start_node == ACPI_ROOT_OBJECT) {
start_node = acpi_gbl_root_node;
- if (!start_node) {
- return_ACPI_STATUS(AE_NO_NAMESPACE);
- }
+ }
+
+ /* Avoid walking the namespace if the StartNode is NULL */
+
+ if (!start_node) {
+ return_ACPI_STATUS(AE_NO_NAMESPACE);
}
/* Null child means "get first node" */
diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c
index 049f6c2f1e32..e5631027f7f1 100644
--- a/drivers/acpi/acpica/tbprint.c
+++ b/drivers/acpi/acpica/tbprint.c
@@ -95,6 +95,11 @@ acpi_tb_print_table_header(acpi_physical_address address,
{
struct acpi_table_header local_header;
+#pragma GCC diagnostic push
+#if defined(__GNUC__) && __GNUC__ >= 11
+#pragma GCC diagnostic ignored "-Wstringop-overread"
+#endif
+
if (ACPI_COMPARE_NAMESEG(header->signature, ACPI_SIG_FACS)) {
/* FACS only has signature and length fields */
@@ -143,4 +148,5 @@ acpi_tb_print_table_header(acpi_physical_address address,
local_header.asl_compiler_id,
local_header.asl_compiler_revision));
}
+#pragma GCC diagnostic pop
}
diff --git a/drivers/acpi/apei/einj-core.c b/drivers/acpi/apei/einj-core.c
index 3c87953dbd19..305c240a303f 100644
--- a/drivers/acpi/apei/einj-core.c
+++ b/drivers/acpi/apei/einj-core.c
@@ -182,6 +182,7 @@ bool einj_initialized __ro_after_init;
static void __iomem *einj_param;
static u32 v5param_size;
+static u32 v66param_size;
static bool is_v2;
static void einj_exec_ctx_init(struct apei_exec_context *ctx)
@@ -283,6 +284,24 @@ static void check_vendor_extension(u64 paddr,
acpi_os_unmap_iomem(p, sizeof(v));
}
+static u32 einjv2_init(struct einjv2_extension_struct *e)
+{
+ if (e->revision != 1) {
+ pr_info("Unknown v2 extension revision %u\n", e->revision);
+ return 0;
+ }
+ if (e->length < sizeof(*e) || e->length > PAGE_SIZE) {
+ pr_info(FW_BUG "Bad1 v2 extension length %u\n", e->length);
+ return 0;
+ }
+ if ((e->length - sizeof(*e)) % sizeof(e->component_arr[0])) {
+ pr_info(FW_BUG "Bad2 v2 extension length %u\n", e->length);
+ return 0;
+ }
+
+ return (e->length - sizeof(*e)) / sizeof(e->component_arr[0]);
+}
+
static void __iomem *einj_get_parameter_address(void)
{
int i;
@@ -310,28 +329,21 @@ static void __iomem *einj_get_parameter_address(void)
v5param_size = sizeof(v5param);
p = acpi_os_map_iomem(pa_v5, sizeof(*p));
if (p) {
- int offset, len;
-
memcpy_fromio(&v5param, p, v5param_size);
acpi5 = 1;
check_vendor_extension(pa_v5, &v5param);
- if (is_v2 && available_error_type & ACPI65_EINJV2_SUPP) {
- len = v5param.einjv2_struct.length;
- offset = offsetof(struct einjv2_extension_struct, component_arr);
- max_nr_components = (len - offset) /
- sizeof(v5param.einjv2_struct.component_arr[0]);
- /*
- * The first call to acpi_os_map_iomem above does not include the
- * component array, instead it is used to read and calculate maximum
- * number of components supported by the system. Below, the mapping
- * is expanded to include the component array.
- */
+ if (available_error_type & ACPI65_EINJV2_SUPP) {
+ struct einjv2_extension_struct *e;
+
+ e = &v5param.einjv2_struct;
+ max_nr_components = einjv2_init(e);
+
+ /* remap including einjv2_extension_struct */
acpi_os_unmap_iomem(p, v5param_size);
- offset = offsetof(struct set_error_type_with_address, einjv2_struct);
- v5param_size = offset + struct_size(&v5param.einjv2_struct,
- component_arr, max_nr_components);
- p = acpi_os_map_iomem(pa_v5, v5param_size);
+ v66param_size = v5param_size - sizeof(*e) + e->length;
+ p = acpi_os_map_iomem(pa_v5, v66param_size);
}
+
return p;
}
}
@@ -527,6 +539,7 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
u64 param3, u64 param4)
{
struct apei_exec_context ctx;
+ u32 param_size = is_v2 ? v66param_size : v5param_size;
u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
int i, rc;
@@ -539,11 +552,11 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
if (acpi5) {
struct set_error_type_with_address *v5param;
- v5param = kmalloc(v5param_size, GFP_KERNEL);
+ v5param = kmalloc(param_size, GFP_KERNEL);
if (!v5param)
return -ENOMEM;
- memcpy_fromio(v5param, einj_param, v5param_size);
+ memcpy_fromio(v5param, einj_param, param_size);
v5param->type = type;
if (type & ACPI5_VENDOR_BIT) {
switch (vendor_flags) {
@@ -601,7 +614,7 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
break;
}
}
- memcpy_toio(einj_param, v5param, v5param_size);
+ memcpy_toio(einj_param, v5param, param_size);
kfree(v5param);
} else {
rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
@@ -1132,9 +1145,14 @@ static void einj_remove(struct faux_device *fdev)
struct apei_exec_context ctx;
if (einj_param) {
- acpi_size size = (acpi5) ?
- v5param_size :
- sizeof(struct einj_parameter);
+ acpi_size size;
+
+ if (v66param_size)
+ size = v66param_size;
+ else if (acpi5)
+ size = v5param_size;
+ else
+ size = sizeof(struct einj_parameter);
acpi_os_unmap_iomem(einj_param, size);
if (vendor_errors.size)
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index 97ee19f2cae0..56107aa00274 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -22,6 +22,7 @@
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/acpi.h>
+#include <linux/bitfield.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
@@ -552,26 +553,25 @@ static bool ghes_handle_memory_failure(struct acpi_hest_generic_data *gdata,
}
static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
- int sev, bool sync)
+ int sev, bool sync)
{
struct cper_sec_proc_arm *err = acpi_hest_get_payload(gdata);
int flags = sync ? MF_ACTION_REQUIRED : 0;
+ char error_type[120];
bool queued = false;
int sec_sev, i;
char *p;
- log_arm_hw_error(err);
-
sec_sev = ghes_severity(gdata->error_severity);
+ log_arm_hw_error(err, sec_sev);
if (sev != GHES_SEV_RECOVERABLE || sec_sev != GHES_SEV_RECOVERABLE)
return false;
p = (char *)(err + 1);
for (i = 0; i < err->err_info_num; i++) {
struct cper_arm_err_info *err_info = (struct cper_arm_err_info *)p;
- bool is_cache = (err_info->type == CPER_ARM_CACHE_ERROR);
+ bool is_cache = err_info->type & CPER_ARM_CACHE_ERROR;
bool has_pa = (err_info->validation_bits & CPER_ARM_INFO_VALID_PHYSICAL_ADDR);
- const char *error_type = "unknown error";
/*
* The field (err_info->error_info & BIT(26)) is fixed to set to
@@ -585,12 +585,15 @@ static bool ghes_handle_arm_hw_error(struct acpi_hest_generic_data *gdata,
continue;
}
- if (err_info->type < ARRAY_SIZE(cper_proc_error_type_strs))
- error_type = cper_proc_error_type_strs[err_info->type];
+ cper_bits_to_str(error_type, sizeof(error_type),
+ FIELD_GET(CPER_ARM_ERR_TYPE_MASK, err_info->type),
+ cper_proc_error_type_strs,
+ ARRAY_SIZE(cper_proc_error_type_strs));
pr_warn_ratelimited(FW_WARN GHES_PFX
- "Unhandled processor error type: %s\n",
- error_type);
+ "Unhandled processor error type 0x%02x: %s%s\n",
+ err_info->type, error_type,
+ (err_info->type & ~CPER_ARM_ERR_TYPE_MASK) ? " with reserved bit(s)" : "");
p += err_info->length;
}
@@ -895,11 +898,9 @@ static void ghes_do_proc(struct ghes *ghes,
arch_apei_report_mem_error(sev, mem_err);
queued = ghes_handle_memory_failure(gdata, sev, sync);
- }
- else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
+ } else if (guid_equal(sec_type, &CPER_SEC_PCIE)) {
ghes_handle_aer(gdata);
- }
- else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
+ } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
queued = ghes_handle_arm_hw_error(gdata, sev, sync);
} else if (guid_equal(sec_type, &CPER_SEC_CXL_PROT_ERR)) {
struct cxl_cper_sec_prot_err *prot_err = acpi_hest_get_payload(gdata);
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
index b3ed6212244c..f2fd79f22e7d 100644
--- a/drivers/acpi/arm64/Kconfig
+++ b/drivers/acpi/arm64/Kconfig
@@ -21,3 +21,6 @@ config ACPI_AGDI
config ACPI_APMT
bool
+
+config ACPI_MPAM
+ bool
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
index 05ecde9eaabe..9390b57cb564 100644
--- a/drivers/acpi/arm64/Makefile
+++ b/drivers/acpi/arm64/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_ACPI_APMT) += apmt.o
obj-$(CONFIG_ACPI_FFH) += ffh.o
obj-$(CONFIG_ACPI_GTDT) += gtdt.o
obj-$(CONFIG_ACPI_IORT) += iort.o
+obj-$(CONFIG_ACPI_MPAM) += mpam.o
obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o
obj-$(CONFIG_ARM_AMBA) += amba.o
obj-y += dma.o init.o
diff --git a/drivers/acpi/arm64/gtdt.c b/drivers/acpi/arm64/gtdt.c
index fd995a1d3d24..ffc867bac2d6 100644
--- a/drivers/acpi/arm64/gtdt.c
+++ b/drivers/acpi/arm64/gtdt.c
@@ -303,40 +303,6 @@ error:
return -EINVAL;
}
-/**
- * acpi_arch_timer_mem_init() - Get the info of all GT blocks in GTDT table.
- * @timer_mem: The pointer to the array of struct arch_timer_mem for returning
- * the result of parsing. The element number of this array should
- * be platform_timer_count(the total number of platform timers).
- * @timer_count: It points to a integer variable which is used for storing the
- * number of GT blocks we have parsed.
- *
- * Return: 0 if success, -EINVAL/-ENODEV if error.
- */
-int __init acpi_arch_timer_mem_init(struct arch_timer_mem *timer_mem,
- int *timer_count)
-{
- int ret;
- void *platform_timer;
-
- *timer_count = 0;
- for_each_platform_timer(platform_timer) {
- if (is_timer_block(platform_timer)) {
- ret = gtdt_parse_timer_block(platform_timer, timer_mem);
- if (ret)
- return ret;
- timer_mem++;
- (*timer_count)++;
- }
- }
-
- if (*timer_count)
- pr_info("found %d memory-mapped timer block(s).\n",
- *timer_count);
-
- return 0;
-}
-
/*
* Initialize a SBSA generic Watchdog platform device info from GTDT
*/
@@ -430,10 +396,10 @@ static int __init gtdt_platform_timer_init(void)
continue;
pdev = platform_device_register_data(NULL, "gtdt-arm-mmio-timer",
- gwdt_count, &atm,
+ mmio_timer_count, &atm,
sizeof(atm));
if (IS_ERR(pdev)) {
- pr_err("Can't register timer %d\n", gwdt_count);
+ pr_err("Can't register timer %d\n", mmio_timer_count);
continue;
}
diff --git a/drivers/acpi/arm64/mpam.c b/drivers/acpi/arm64/mpam.c
new file mode 100644
index 000000000000..84963a20c3e7
--- /dev/null
+++ b/drivers/acpi/arm64/mpam.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Arm Ltd.
+
+/* Parse the MPAM ACPI table feeding the discovered nodes into the driver */
+
+#define pr_fmt(fmt) "ACPI MPAM: " fmt
+
+#include <linux/acpi.h>
+#include <linux/arm_mpam.h>
+#include <linux/bits.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/platform_device.h>
+
+#include <acpi/processor.h>
+
+/*
+ * Flags for acpi_table_mpam_msc.*_interrupt_flags.
+ * See 2.1.1 Interrupt Flags, Table 5, of DEN0065B_MPAM_ACPI_3.0-bet.
+ */
+#define ACPI_MPAM_MSC_IRQ_MODE BIT(0)
+#define ACPI_MPAM_MSC_IRQ_TYPE_MASK GENMASK(2, 1)
+#define ACPI_MPAM_MSC_IRQ_TYPE_WIRED 0
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_MASK BIT(3)
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_PROCESSOR 0
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_PROCESSOR_CONTAINER 1
+#define ACPI_MPAM_MSC_IRQ_AFFINITY_VALID BIT(4)
+
+/*
+ * Encodings for the MSC node body interface type field.
+ * See 2.1 MPAM MSC node, Table 4 of DEN0065B_MPAM_ACPI_3.0-bet.
+ */
+#define ACPI_MPAM_MSC_IFACE_MMIO 0x00
+#define ACPI_MPAM_MSC_IFACE_PCC 0x0a
+
+static bool _is_ppi_partition(u32 flags)
+{
+ u32 aff_type, is_ppi;
+ bool ret;
+
+ is_ppi = FIELD_GET(ACPI_MPAM_MSC_IRQ_AFFINITY_VALID, flags);
+ if (!is_ppi)
+ return false;
+
+ aff_type = FIELD_GET(ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_MASK, flags);
+ ret = (aff_type == ACPI_MPAM_MSC_IRQ_AFFINITY_TYPE_PROCESSOR_CONTAINER);
+ if (ret)
+ pr_err_once("Partitioned interrupts not supported\n");
+
+ return ret;
+}
+
+static int acpi_mpam_register_irq(struct platform_device *pdev,
+ u32 intid, u32 flags)
+{
+ int irq;
+ u32 int_type;
+ int trigger;
+
+ if (!intid)
+ return -EINVAL;
+
+ if (_is_ppi_partition(flags))
+ return -EINVAL;
+
+ trigger = FIELD_GET(ACPI_MPAM_MSC_IRQ_MODE, flags);
+ int_type = FIELD_GET(ACPI_MPAM_MSC_IRQ_TYPE_MASK, flags);
+ if (int_type != ACPI_MPAM_MSC_IRQ_TYPE_WIRED)
+ return -EINVAL;
+
+ irq = acpi_register_gsi(&pdev->dev, intid, trigger, ACPI_ACTIVE_HIGH);
+ if (irq < 0)
+ pr_err_once("Failed to register interrupt 0x%x with ACPI\n", intid);
+
+ return irq;
+}
+
+static void acpi_mpam_parse_irqs(struct platform_device *pdev,
+ struct acpi_mpam_msc_node *tbl_msc,
+ struct resource *res, int *res_idx)
+{
+ u32 flags, intid;
+ int irq;
+
+ intid = tbl_msc->overflow_interrupt;
+ flags = tbl_msc->overflow_interrupt_flags;
+ irq = acpi_mpam_register_irq(pdev, intid, flags);
+ if (irq > 0)
+ res[(*res_idx)++] = DEFINE_RES_IRQ_NAMED(irq, "overflow");
+
+ intid = tbl_msc->error_interrupt;
+ flags = tbl_msc->error_interrupt_flags;
+ irq = acpi_mpam_register_irq(pdev, intid, flags);
+ if (irq > 0)
+ res[(*res_idx)++] = DEFINE_RES_IRQ_NAMED(irq, "error");
+}
+
+static int acpi_mpam_parse_resource(struct mpam_msc *msc,
+ struct acpi_mpam_resource_node *res)
+{
+ int level, nid;
+ u32 cache_id;
+
+ switch (res->locator_type) {
+ case ACPI_MPAM_LOCATION_TYPE_PROCESSOR_CACHE:
+ cache_id = res->locator.cache_locator.cache_reference;
+ level = find_acpi_cache_level_from_id(cache_id);
+ if (level <= 0) {
+ pr_err_once("Bad level (%d) for cache with id %u\n", level, cache_id);
+ return -EINVAL;
+ }
+ return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_CACHE,
+ level, cache_id);
+ case ACPI_MPAM_LOCATION_TYPE_MEMORY:
+ nid = pxm_to_node(res->locator.memory_locator.proximity_domain);
+ if (nid == NUMA_NO_NODE) {
+ pr_debug("Bad proximity domain %lld, using node 0 instead\n",
+ res->locator.memory_locator.proximity_domain);
+ nid = 0;
+ }
+ return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_MEMORY,
+ MPAM_CLASS_ID_DEFAULT, nid);
+ default:
+ /* These get discovered later and are treated as unknown */
+ return 0;
+ }
+}
+
+int acpi_mpam_parse_resources(struct mpam_msc *msc,
+ struct acpi_mpam_msc_node *tbl_msc)
+{
+ int i, err;
+ char *ptr, *table_end;
+ struct acpi_mpam_resource_node *resource;
+
+ table_end = (char *)tbl_msc + tbl_msc->length;
+ ptr = (char *)(tbl_msc + 1);
+ for (i = 0; i < tbl_msc->num_resource_nodes; i++) {
+ u64 max_deps, remaining_table;
+
+ if (ptr + sizeof(*resource) > table_end)
+ return -EINVAL;
+
+ resource = (struct acpi_mpam_resource_node *)ptr;
+
+ remaining_table = table_end - ptr;
+ max_deps = remaining_table / sizeof(struct acpi_mpam_func_deps);
+ if (resource->num_functional_deps > max_deps) {
+ pr_debug("MSC has impossible number of functional dependencies\n");
+ return -EINVAL;
+ }
+
+ err = acpi_mpam_parse_resource(msc, resource);
+ if (err)
+ return err;
+
+ ptr += sizeof(*resource);
+ ptr += resource->num_functional_deps * sizeof(struct acpi_mpam_func_deps);
+ }
+
+ return 0;
+}
+
+/*
+ * Creates the device power management link and returns true if the
+ * acpi id is valid and usable for cpu affinity. This is the case
+ * when the linked device is a processor or a processor container.
+ */
+static bool __init parse_msc_pm_link(struct acpi_mpam_msc_node *tbl_msc,
+ struct platform_device *pdev,
+ u32 *acpi_id)
+{
+ char hid[sizeof(tbl_msc->hardware_id_linked_device) + 1] = { 0 };
+ bool acpi_id_valid = false;
+ struct acpi_device *buddy;
+ char uid[11];
+ int len;
+
+ memcpy(hid, &tbl_msc->hardware_id_linked_device,
+ sizeof(tbl_msc->hardware_id_linked_device));
+
+ if (!strcmp(hid, ACPI_PROCESSOR_CONTAINER_HID)) {
+ *acpi_id = tbl_msc->instance_id_linked_device;
+ acpi_id_valid = true;
+ }
+
+ len = snprintf(uid, sizeof(uid), "%u",
+ tbl_msc->instance_id_linked_device);
+ if (len >= sizeof(uid)) {
+ pr_debug("Failed to convert uid of device for power management.");
+ return acpi_id_valid;
+ }
+
+ buddy = acpi_dev_get_first_match_dev(hid, uid, -1);
+ if (buddy) {
+ device_link_add(&pdev->dev, &buddy->dev, DL_FLAG_STATELESS);
+ acpi_dev_put(buddy);
+ }
+
+ return acpi_id_valid;
+}
+
+static int decode_interface_type(struct acpi_mpam_msc_node *tbl_msc,
+ enum mpam_msc_iface *iface)
+{
+ switch (tbl_msc->interface_type) {
+ case ACPI_MPAM_MSC_IFACE_MMIO:
+ *iface = MPAM_IFACE_MMIO;
+ return 0;
+ case ACPI_MPAM_MSC_IFACE_PCC:
+ *iface = MPAM_IFACE_PCC;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct platform_device * __init acpi_mpam_parse_msc(struct acpi_mpam_msc_node *tbl_msc)
+{
+ struct platform_device *pdev __free(platform_device_put) =
+ platform_device_alloc("mpam_msc", tbl_msc->identifier);
+ int next_res = 0, next_prop = 0, err;
+ /* pcc, nrdy, affinity and a sentinel */
+ struct property_entry props[4] = { 0 };
+ /* mmio, 2xirq, no sentinel. */
+ struct resource res[3] = { 0 };
+ struct acpi_device *companion;
+ enum mpam_msc_iface iface;
+ char uid[16];
+ u32 acpi_id;
+
+ if (!pdev)
+ return ERR_PTR(-ENOMEM);
+
+ /* Some power management is described in the namespace: */
+ err = snprintf(uid, sizeof(uid), "%u", tbl_msc->identifier);
+ if (err > 0 && err < sizeof(uid)) {
+ companion = acpi_dev_get_first_match_dev("ARMHAA5C", uid, -1);
+ if (companion) {
+ ACPI_COMPANION_SET(&pdev->dev, companion);
+ acpi_dev_put(companion);
+ } else {
+ pr_debug("MSC.%u: missing namespace entry\n", tbl_msc->identifier);
+ }
+ }
+
+ if (decode_interface_type(tbl_msc, &iface)) {
+ pr_debug("MSC.%u: unknown interface type\n", tbl_msc->identifier);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (iface == MPAM_IFACE_MMIO) {
+ res[next_res++] = DEFINE_RES_MEM_NAMED(tbl_msc->base_address,
+ tbl_msc->mmio_size,
+ "MPAM:MSC");
+ } else if (iface == MPAM_IFACE_PCC) {
+ props[next_prop++] = PROPERTY_ENTRY_U32("pcc-channel",
+ tbl_msc->base_address);
+ }
+
+ acpi_mpam_parse_irqs(pdev, tbl_msc, res, &next_res);
+
+ WARN_ON_ONCE(next_res > ARRAY_SIZE(res));
+ err = platform_device_add_resources(pdev, res, next_res);
+ if (err)
+ return ERR_PTR(err);
+
+ props[next_prop++] = PROPERTY_ENTRY_U32("arm,not-ready-us",
+ tbl_msc->max_nrdy_usec);
+
+ /*
+ * The MSC's CPU affinity is described via its linked power
+ * management device, but only if it points at a Processor or
+ * Processor Container.
+ */
+ if (parse_msc_pm_link(tbl_msc, pdev, &acpi_id))
+ props[next_prop++] = PROPERTY_ENTRY_U32("cpu_affinity", acpi_id);
+
+ WARN_ON_ONCE(next_prop > ARRAY_SIZE(props) - 1);
+ err = device_create_managed_software_node(&pdev->dev, props, NULL);
+ if (err)
+ return ERR_PTR(err);
+
+ /*
+ * Stash the table entry for acpi_mpam_parse_resources() to discover
+ * what this MSC controls.
+ */
+ err = platform_device_add_data(pdev, tbl_msc, tbl_msc->length);
+ if (err)
+ return ERR_PTR(err);
+
+ err = platform_device_add(pdev);
+ if (err)
+ return ERR_PTR(err);
+
+ return_ptr(pdev);
+}
+
+static int __init acpi_mpam_parse(void)
+{
+ char *table_end, *table_offset;
+ struct acpi_mpam_msc_node *tbl_msc;
+ struct platform_device *pdev;
+
+ if (acpi_disabled || !system_supports_mpam())
+ return 0;
+
+ struct acpi_table_header *table __free(acpi_put_table) =
+ acpi_get_table_pointer(ACPI_SIG_MPAM, 0);
+
+ if (IS_ERR(table))
+ return 0;
+
+ if (table->revision < 1) {
+ pr_debug("MPAM ACPI table revision %d not supported\n", table->revision);
+ return 0;
+ }
+
+ table_offset = (char *)(table + 1);
+ table_end = (char *)table + table->length;
+
+ while (table_offset < table_end) {
+ tbl_msc = (struct acpi_mpam_msc_node *)table_offset;
+ if (table_offset + sizeof(*tbl_msc) > table_end ||
+ table_offset + tbl_msc->length > table_end) {
+ pr_err("MSC entry overlaps end of ACPI table\n");
+ return -EINVAL;
+ }
+ table_offset += tbl_msc->length;
+
+ /*
+ * If any of the reserved fields are set, make no attempt to
+ * parse the MSC structure. This MSC will still be counted by
+ * acpi_mpam_count_msc(), meaning the MPAM driver can't probe
+ * against all MSC, and will never be enabled. There is no way
+ * to enable it safely, because we cannot determine safe
+ * system-wide partid and pmg ranges in this situation.
+ */
+ if (tbl_msc->reserved || tbl_msc->reserved1 || tbl_msc->reserved2) {
+ pr_err_once("Unrecognised MSC, MPAM not usable\n");
+ pr_debug("MSC.%u: reserved field set\n", tbl_msc->identifier);
+ continue;
+ }
+
+ if (!tbl_msc->mmio_size) {
+ pr_debug("MSC.%u: marked as disabled\n", tbl_msc->identifier);
+ continue;
+ }
+
+ pdev = acpi_mpam_parse_msc(tbl_msc);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+ }
+
+ return 0;
+}
+
+/**
+ * acpi_mpam_count_msc() - Count the number of MSC described by firmware.
+ *
+ * Returns the number of MSCs, or zero for an error.
+ *
+ * This can be called before or in parallel with acpi_mpam_parse().
+ */
+int acpi_mpam_count_msc(void)
+{
+ char *table_end, *table_offset;
+ struct acpi_mpam_msc_node *tbl_msc;
+ int count = 0;
+
+ if (acpi_disabled || !system_supports_mpam())
+ return 0;
+
+ struct acpi_table_header *table __free(acpi_put_table) =
+ acpi_get_table_pointer(ACPI_SIG_MPAM, 0);
+
+ if (IS_ERR(table))
+ return 0;
+
+ if (table->revision < 1)
+ return 0;
+
+ table_offset = (char *)(table + 1);
+ table_end = (char *)table + table->length;
+
+ while (table_offset < table_end) {
+ tbl_msc = (struct acpi_mpam_msc_node *)table_offset;
+
+ if (table_offset + sizeof(*tbl_msc) > table_end)
+ return -EINVAL;
+ if (tbl_msc->length < sizeof(*tbl_msc))
+ return -EINVAL;
+ if (tbl_msc->length > table_end - table_offset)
+ return -EINVAL;
+ table_offset += tbl_msc->length;
+
+ if (!tbl_msc->mmio_size)
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+/*
+ * Call after ACPI devices have been created, which happens behind acpi_scan_init()
+ * called from subsys_initcall(). PCC requires the mailbox driver, which is
+ * initialised from postcore_initcall().
+ */
+subsys_initcall_sync(acpi_mpam_parse);
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index 67b76492c839..34181fa52e93 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -91,7 +91,6 @@ enum {
};
struct acpi_battery {
- struct mutex lock;
struct mutex update_lock;
struct power_supply *bat;
struct power_supply_desc bat_desc;
@@ -535,11 +534,9 @@ static int acpi_battery_get_info(struct acpi_battery *battery)
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status = AE_ERROR;
- mutex_lock(&battery->lock);
status = acpi_evaluate_object(battery->device->handle,
use_bix ? "_BIX":"_BIF",
NULL, &buffer);
- mutex_unlock(&battery->lock);
if (ACPI_FAILURE(status)) {
acpi_handle_info(battery->device->handle,
@@ -576,11 +573,8 @@ static int acpi_battery_get_state(struct acpi_battery *battery)
msecs_to_jiffies(cache_time)))
return 0;
- mutex_lock(&battery->lock);
status = acpi_evaluate_object(battery->device->handle, "_BST",
NULL, &buffer);
- mutex_unlock(&battery->lock);
-
if (ACPI_FAILURE(status)) {
acpi_handle_info(battery->device->handle,
"_BST evaluation failed: %s",
@@ -628,11 +622,8 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery)
!test_bit(ACPI_BATTERY_ALARM_PRESENT, &battery->flags))
return -ENODEV;
- mutex_lock(&battery->lock);
status = acpi_execute_simple_method(battery->device->handle, "_BTP",
battery->alarm);
- mutex_unlock(&battery->lock);
-
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -1235,9 +1226,6 @@ static int acpi_battery_add(struct acpi_device *device)
strscpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
strscpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
device->driver_data = battery;
- result = devm_mutex_init(&device->dev, &battery->lock);
- if (result)
- return result;
result = devm_mutex_init(&device->dev, &battery->update_lock);
if (result)
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 0a7026040188..3c6dd9b4ba0a 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -619,8 +619,10 @@ static int acpi_button_add(struct acpi_device *device)
input_set_drvdata(input, device);
error = input_register_device(input);
- if (error)
+ if (error) {
+ input_free_device(input);
goto err_remove_fs;
+ }
switch (device->device_type) {
case ACPI_BUS_TYPE_POWER_BUTTON:
diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c
index ab4651205e8a..3bdeeee3414e 100644
--- a/drivers/acpi/cppc_acpi.c
+++ b/drivers/acpi/cppc_acpi.c
@@ -460,7 +460,7 @@ bool acpi_cpc_valid(void)
if (acpi_disabled)
return false;
- for_each_present_cpu(cpu) {
+ for_each_online_cpu(cpu) {
cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
if (!cpc_ptr)
return false;
@@ -476,7 +476,7 @@ bool cppc_allow_fast_switch(void)
struct cpc_desc *cpc_ptr;
int cpu;
- for_each_present_cpu(cpu) {
+ for_each_online_cpu(cpu) {
cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF];
if (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
@@ -750,7 +750,7 @@ int acpi_cppc_processor_probe(struct acpi_processor *pr)
}
/*
- * Disregard _CPC if the number of entries in the return pachage is not
+ * Disregard _CPC if the number of entries in the return package is not
* as expected, but support future revisions being proper supersets of
* the v3 and only causing more entries to be returned by _CPC.
*/
@@ -1435,7 +1435,7 @@ bool cppc_perf_ctrs_in_pcc(void)
{
int cpu;
- for_each_present_cpu(cpu) {
+ for_each_online_cpu(cpu) {
struct cpc_register_resource *ref_perf_reg;
struct cpc_desc *cpc_desc;
diff --git a/drivers/acpi/dptf/Makefile b/drivers/acpi/dptf/Makefile
index 297340682f66..e912a3be1d28 100644
--- a/drivers/acpi/dptf/Makefile
+++ b/drivers/acpi/dptf/Makefile
@@ -1,4 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_ACPI) += int340x_thermal.o
obj-$(CONFIG_DPTF_POWER) += dptf_power.o
obj-$(CONFIG_DPTF_PCH_FIVR) += dptf_pch_fivr.o
diff --git a/drivers/acpi/dptf/dptf_pch_fivr.c b/drivers/acpi/dptf/dptf_pch_fivr.c
index 952216c67d58..8d7e555929d3 100644
--- a/drivers/acpi/dptf/dptf_pch_fivr.c
+++ b/drivers/acpi/dptf/dptf_pch_fivr.c
@@ -41,7 +41,7 @@ static int pch_fivr_read(acpi_handle handle, char *method, struct pch_fivr_resp
ret = 0;
release_buffer:
- kfree(buffer.pointer);
+ ACPI_FREE(buffer.pointer);
return ret;
}
diff --git a/drivers/acpi/dptf/dptf_power.c b/drivers/acpi/dptf/dptf_power.c
index 776914f31b9e..55ccbb8ddbe3 100644
--- a/drivers/acpi/dptf/dptf_power.c
+++ b/drivers/acpi/dptf/dptf_power.c
@@ -240,6 +240,8 @@ static const struct acpi_device_id int3407_device_ids[] = {
{"INTC10D9", 0},
{"INTC1100", 0},
{"INTC1101", 0},
+ {"INTC10F7", 0},
+ {"INTC10F8", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3407_device_ids);
diff --git a/drivers/acpi/dptf/int340x_thermal.c b/drivers/acpi/dptf/int340x_thermal.c
deleted file mode 100644
index a222df059a16..000000000000
--- a/drivers/acpi/dptf/int340x_thermal.c
+++ /dev/null
@@ -1,94 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * ACPI support for int340x thermal drivers
- *
- * Copyright (C) 2014, Intel Corporation
- * Authors: Zhang Rui <rui.zhang@intel.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/module.h>
-
-#include "../internal.h"
-
-#define INT3401_DEVICE 0X01
-static const struct acpi_device_id int340x_thermal_device_ids[] = {
- {"INT3400"},
- {"INT3401", INT3401_DEVICE},
- {"INT3402"},
- {"INT3403"},
- {"INT3404"},
- {"INT3406"},
- {"INT3407"},
- {"INT3408"},
- {"INT3409"},
- {"INT340A"},
- {"INT340B"},
- {"INT3532"},
- {"INTC1040"},
- {"INTC1041"},
- {"INTC1042"},
- {"INTC1043"},
- {"INTC1044"},
- {"INTC1045"},
- {"INTC1046"},
- {"INTC1047"},
- {"INTC1048"},
- {"INTC1049"},
- {"INTC1050"},
- {"INTC1060"},
- {"INTC1061"},
- {"INTC1062"},
- {"INTC1063"},
- {"INTC1064"},
- {"INTC1065"},
- {"INTC1066"},
- {"INTC1068"},
- {"INTC1069"},
- {"INTC106A"},
- {"INTC106B"},
- {"INTC106C"},
- {"INTC106D"},
- {"INTC10A0"},
- {"INTC10A1"},
- {"INTC10A2"},
- {"INTC10A3"},
- {"INTC10A4"},
- {"INTC10A5"},
- {"INTC10D4"},
- {"INTC10D5"},
- {"INTC10D6"},
- {"INTC10D7"},
- {"INTC10D8"},
- {"INTC10D9"},
- {"INTC10FC"},
- {"INTC10FD"},
- {"INTC10FE"},
- {"INTC10FF"},
- {"INTC1100"},
- {"INTC1101"},
- {"INTC1102"},
- {""},
-};
-
-static int int340x_thermal_handler_attach(struct acpi_device *adev,
- const struct acpi_device_id *id)
-{
- if (IS_ENABLED(CONFIG_INT340X_THERMAL))
- acpi_create_platform_device(adev, NULL);
- /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */
- else if (IS_ENABLED(CONFIG_INTEL_SOC_DTS_THERMAL) &&
- id->driver_data == INT3401_DEVICE)
- acpi_create_platform_device(adev, NULL);
- return 1;
-}
-
-static struct acpi_scan_handler int340x_thermal_handler = {
- .ids = int340x_thermal_device_ids,
- .attach = int340x_thermal_handler_attach,
-};
-
-void __init acpi_int340x_thermal_init(void)
-{
- acpi_scan_add_handler(&int340x_thermal_handler);
-}
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 7855bbf752b1..59b3d50ff01e 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -2294,7 +2294,8 @@ static int acpi_ec_init_workqueues(void)
ec_wq = alloc_ordered_workqueue("kec", 0);
if (!ec_query_wq)
- ec_query_wq = alloc_workqueue("kec_query", 0, ec_max_queries);
+ ec_query_wq = alloc_workqueue("kec_query", WQ_PERCPU,
+ ec_max_queries);
if (!ec_wq || !ec_query_wq) {
acpi_ec_destroy_workqueues();
diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h
index 8a28a72a7c6a..97ce3212edf3 100644
--- a/drivers/acpi/fan.h
+++ b/drivers/acpi/fan.h
@@ -11,6 +11,7 @@
#define _ACPI_FAN_H_
#include <linux/kconfig.h>
+#include <linux/limits.h>
#define ACPI_FAN_DEVICE_IDS \
{"INT3404", }, /* Fan */ \
@@ -21,6 +22,7 @@
{"INTC10A2", }, /* Fan for Raptor Lake generation */ \
{"INTC10D6", }, /* Fan for Panther Lake generation */ \
{"INTC10FE", }, /* Fan for Wildcat Lake generation */ \
+ {"INTC10F5", }, /* Fan for Nova Lake generation */ \
{"PNP0C0B", } /* Generic ACPI fan */
#define ACPI_FPS_NAME_LEN 20
@@ -49,24 +51,64 @@ struct acpi_fan_fst {
};
struct acpi_fan {
+ acpi_handle handle;
bool acpi4;
bool has_fst;
struct acpi_fan_fif fif;
struct acpi_fan_fps *fps;
int fps_count;
+ /* A value of 0 means that trippoint-related functions are not supported */
+ u32 fan_trip_granularity;
+#if IS_REACHABLE(CONFIG_HWMON)
+ struct device *hdev;
+#endif
struct thermal_cooling_device *cdev;
struct device_attribute fst_speed;
struct device_attribute fine_grain_control;
};
-int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst);
+/**
+ * acpi_fan_speed_valid - Check if fan speed value is valid
+ * @speeed: Speed value returned by the ACPI firmware
+ *
+ * Check if the fan speed value returned by the ACPI firmware is valid. This function is
+ * necessary as ACPI firmware implementations can return 0xFFFFFFFF to signal that the
+ * ACPI fan does not support speed reporting. Additionally, some buggy ACPI firmware
+ * implementations return a value larger than the 32-bit integer value defined by
+ * the ACPI specification when using placeholder values. Such invalid values are also
+ * detected by this function.
+ *
+ * Returns: True if the fan speed value is valid, false otherwise.
+ */
+static inline bool acpi_fan_speed_valid(u64 speed)
+{
+ return speed < U32_MAX;
+}
+
+/**
+ * acpi_fan_power_valid - Check if fan power value is valid
+ * @power: Power value returned by the ACPI firmware
+ *
+ * Check if the fan power value returned by the ACPI firmware is valid.
+ * See acpi_fan_speed_valid() for details.
+ *
+ * Returns: True if the fan power value is valid, false otherwise.
+ */
+static inline bool acpi_fan_power_valid(u64 power)
+{
+ return power < U32_MAX;
+}
+
+int acpi_fan_get_fst(acpi_handle handle, struct acpi_fan_fst *fst);
int acpi_fan_create_attributes(struct acpi_device *device);
void acpi_fan_delete_attributes(struct acpi_device *device);
#if IS_REACHABLE(CONFIG_HWMON)
-int devm_acpi_fan_create_hwmon(struct acpi_device *device);
+int devm_acpi_fan_create_hwmon(struct device *dev);
+void acpi_fan_notify_hwmon(struct device *dev);
#else
-static inline int devm_acpi_fan_create_hwmon(struct acpi_device *device) { return 0; };
+static inline int devm_acpi_fan_create_hwmon(struct device *dev) { return 0; };
+static inline void acpi_fan_notify_hwmon(struct device *dev) { };
#endif
#endif
diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c
index c1afb7b5ed3d..9b7fa52f3c2a 100644
--- a/drivers/acpi/fan_attr.c
+++ b/drivers/acpi/fan_attr.c
@@ -55,7 +55,7 @@ static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr,
struct acpi_fan_fst fst;
int status;
- status = acpi_fan_get_fst(acpi_dev, &fst);
+ status = acpi_fan_get_fst(acpi_dev->handle, &fst);
if (status)
return status;
diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c
index 04ff608f2ff0..fb08b8549ed7 100644
--- a/drivers/acpi/fan_core.c
+++ b/drivers/acpi/fan_core.c
@@ -7,11 +7,16 @@
* Copyright (C) 2022 Intel Corporation. All rights reserved.
*/
+#include <linux/bits.h>
#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/math.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/uaccess.h>
+#include <linux/uuid.h>
#include <linux/thermal.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
@@ -19,6 +24,26 @@
#include "fan.h"
+#define ACPI_FAN_NOTIFY_STATE_CHANGED 0x80
+
+/*
+ * Defined inside the "Fan Noise Signal" section at
+ * https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/design-guide.
+ */
+static const guid_t acpi_fan_microsoft_guid = GUID_INIT(0xA7611840, 0x99FE, 0x41AE, 0xA4, 0x88,
+ 0x35, 0xC7, 0x59, 0x26, 0xC8, 0xEB);
+#define ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY 1
+#define ACPI_FAN_DSM_SET_TRIP_POINTS 2
+#define ACPI_FAN_DSM_GET_OPERATING_RANGES 3
+
+/*
+ * Ensures that fans with a very low trip point granularity
+ * do not send too many notifications.
+ */
+static uint min_trip_distance = 100;
+module_param(min_trip_distance, uint, 0);
+MODULE_PARM_DESC(min_trip_distance, "Minimum distance between fan speed trip points in RPM");
+
static const struct acpi_device_id fan_device_ids[] = {
ACPI_FAN_DEVICE_IDS,
{"", 0},
@@ -44,25 +69,30 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
return 0;
}
-int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst)
+int acpi_fan_get_fst(acpi_handle handle, struct acpi_fan_fst *fst)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
int ret = 0;
- status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
- if (ACPI_FAILURE(status)) {
- dev_err(&device->dev, "Get fan state failed\n");
- return -ENODEV;
- }
+ status = acpi_evaluate_object(handle, "_FST", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -EIO;
obj = buffer.pointer;
- if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
- obj->package.count != 3 ||
- obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
- dev_err(&device->dev, "Invalid _FST data\n");
- ret = -EINVAL;
+ if (!obj)
+ return -ENODATA;
+
+ if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) {
+ ret = -EPROTO;
+ goto err;
+ }
+
+ if (obj->package.elements[0].type != ACPI_TYPE_INTEGER ||
+ obj->package.elements[1].type != ACPI_TYPE_INTEGER ||
+ obj->package.elements[2].type != ACPI_TYPE_INTEGER) {
+ ret = -EPROTO;
goto err;
}
@@ -81,7 +111,7 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
struct acpi_fan_fst fst;
int status, i;
- status = acpi_fan_get_fst(device, &fst);
+ status = acpi_fan_get_fst(device->handle, &fst);
if (status)
return status;
@@ -303,6 +333,182 @@ err:
return status;
}
+static int acpi_fan_dsm_init(struct device *dev)
+{
+ union acpi_object dummy = {
+ .package = {
+ .type = ACPI_TYPE_PACKAGE,
+ .count = 0,
+ .elements = NULL,
+ },
+ };
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ union acpi_object *obj;
+ int ret = 0;
+
+ if (!acpi_check_dsm(fan->handle, &acpi_fan_microsoft_guid, 0,
+ BIT(ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY) |
+ BIT(ACPI_FAN_DSM_SET_TRIP_POINTS)))
+ return 0;
+
+ dev_info(dev, "Using Microsoft fan extensions\n");
+
+ obj = acpi_evaluate_dsm_typed(fan->handle, &acpi_fan_microsoft_guid, 0,
+ ACPI_FAN_DSM_GET_TRIP_POINT_GRANULARITY, &dummy,
+ ACPI_TYPE_INTEGER);
+ if (!obj)
+ return -EIO;
+
+ if (obj->integer.value > U32_MAX)
+ ret = -EOVERFLOW;
+ else
+ fan->fan_trip_granularity = obj->integer.value;
+
+ kfree(obj);
+
+ return ret;
+}
+
+static int acpi_fan_dsm_set_trip_points(struct device *dev, u64 upper, u64 lower)
+{
+ union acpi_object args[2] = {
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = lower,
+ },
+ },
+ {
+ .integer = {
+ .type = ACPI_TYPE_INTEGER,
+ .value = upper,
+ },
+ },
+ };
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ union acpi_object in = {
+ .package = {
+ .type = ACPI_TYPE_PACKAGE,
+ .count = ARRAY_SIZE(args),
+ .elements = args,
+ },
+ };
+ union acpi_object *obj;
+
+ obj = acpi_evaluate_dsm(fan->handle, &acpi_fan_microsoft_guid, 0,
+ ACPI_FAN_DSM_SET_TRIP_POINTS, &in);
+ kfree(obj);
+
+ return 0;
+}
+
+static int acpi_fan_dsm_start(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ int ret;
+
+ if (!fan->fan_trip_granularity)
+ return 0;
+
+ /*
+ * Some firmware implementations only update the values returned by the
+ * _FST control method when a notification is received. This usually
+ * works with Microsoft Windows as setting up trip points will keep
+ * triggering said notifications, but will cause issues when using _FST
+ * without the Microsoft-specific trip point extension.
+ *
+ * Because of this, an initial notification needs to be triggered to
+ * start the cycle of trip points updates. This is achieved by setting
+ * the trip points sequencially to two separate ranges. As by the
+ * Microsoft specification the firmware should trigger a notification
+ * immediately if the fan speed is outside the trip point range. This
+ * _should_ result in at least one notification as both ranges do not
+ * overlap, meaning that the current fan speed needs to be outside at
+ * least one range.
+ */
+ ret = acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity, 0);
+ if (ret < 0)
+ return ret;
+
+ return acpi_fan_dsm_set_trip_points(dev, fan->fan_trip_granularity * 3,
+ fan->fan_trip_granularity * 2);
+}
+
+static int acpi_fan_dsm_update_trips_points(struct device *dev, struct acpi_fan_fst *fst)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ u64 upper, lower;
+
+ if (!fan->fan_trip_granularity)
+ return 0;
+
+ if (!acpi_fan_speed_valid(fst->speed))
+ return -EINVAL;
+
+ upper = roundup_u64(fst->speed + min_trip_distance, fan->fan_trip_granularity);
+ if (fst->speed <= min_trip_distance) {
+ lower = 0;
+ } else {
+ /*
+ * Valid fan speed values cannot be larger than 32 bit, so
+ * we can safely assume that no overflow will happen here.
+ */
+ lower = rounddown((u32)fst->speed - min_trip_distance, fan->fan_trip_granularity);
+ }
+
+ return acpi_fan_dsm_set_trip_points(dev, upper, lower);
+}
+
+static void acpi_fan_notify_handler(acpi_handle handle, u32 event, void *context)
+{
+ struct device *dev = context;
+ struct acpi_fan_fst fst;
+ int ret;
+
+ switch (event) {
+ case ACPI_FAN_NOTIFY_STATE_CHANGED:
+ /*
+ * The ACPI specification says that we must evaluate _FST when we
+ * receive an ACPI event indicating that the fan state has changed.
+ */
+ ret = acpi_fan_get_fst(handle, &fst);
+ if (ret < 0) {
+ dev_err(dev, "Error retrieving current fan status: %d\n", ret);
+ } else {
+ ret = acpi_fan_dsm_update_trips_points(dev, &fst);
+ if (ret < 0)
+ dev_err(dev, "Failed to update trip points: %d\n", ret);
+ }
+
+ acpi_fan_notify_hwmon(dev);
+ acpi_bus_generate_netlink_event("fan", dev_name(dev), event, 0);
+ break;
+ default:
+ dev_dbg(dev, "Unsupported ACPI notification 0x%x\n", event);
+ break;
+ }
+}
+
+static void acpi_fan_notify_remove(void *data)
+{
+ struct acpi_fan *fan = data;
+
+ acpi_remove_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY, acpi_fan_notify_handler);
+}
+
+static int devm_acpi_fan_notify_init(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+ acpi_status status;
+
+ status = acpi_install_notify_handler(fan->handle, ACPI_DEVICE_NOTIFY,
+ acpi_fan_notify_handler, dev);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ return devm_add_action_or_reset(dev, acpi_fan_notify_remove, fan);
+}
+
static int acpi_fan_probe(struct platform_device *pdev)
{
int result = 0;
@@ -311,11 +517,16 @@ static int acpi_fan_probe(struct platform_device *pdev)
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
char *name;
+ if (!device)
+ return -ENODEV;
+
fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
if (!fan) {
dev_err(&device->dev, "No memory for fan\n");
return -ENOMEM;
}
+
+ fan->handle = device->handle;
device->driver_data = fan;
platform_set_drvdata(pdev, fan);
@@ -337,10 +548,24 @@ static int acpi_fan_probe(struct platform_device *pdev)
}
if (fan->has_fst) {
- result = devm_acpi_fan_create_hwmon(device);
+ result = acpi_fan_dsm_init(&pdev->dev);
+ if (result)
+ return result;
+
+ result = devm_acpi_fan_create_hwmon(&pdev->dev);
+ if (result)
+ return result;
+
+ result = devm_acpi_fan_notify_init(&pdev->dev);
if (result)
return result;
+ result = acpi_fan_dsm_start(&pdev->dev);
+ if (result) {
+ dev_err(&pdev->dev, "Failed to start Microsoft fan extensions\n");
+ return result;
+ }
+
result = acpi_fan_create_attributes(device);
if (result)
return result;
@@ -426,8 +651,14 @@ static int acpi_fan_suspend(struct device *dev)
static int acpi_fan_resume(struct device *dev)
{
- int result;
struct acpi_fan *fan = dev_get_drvdata(dev);
+ int result;
+
+ if (fan->has_fst) {
+ result = acpi_fan_dsm_start(dev);
+ if (result)
+ dev_err(dev, "Failed to start Microsoft fan extensions: %d\n", result);
+ }
if (fan->acpi4)
return 0;
diff --git a/drivers/acpi/fan_hwmon.c b/drivers/acpi/fan_hwmon.c
index e8d90605106e..d3374f8f524b 100644
--- a/drivers/acpi/fan_hwmon.c
+++ b/drivers/acpi/fan_hwmon.c
@@ -15,10 +15,6 @@
#include "fan.h"
-/* Returned when the ACPI fan does not support speed reporting */
-#define FAN_SPEED_UNAVAILABLE U32_MAX
-#define FAN_POWER_UNAVAILABLE U32_MAX
-
static struct acpi_fan_fps *acpi_fan_get_current_fps(struct acpi_fan *fan, u64 control)
{
unsigned int i;
@@ -77,7 +73,7 @@ static umode_t acpi_fan_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_
* when the associated attribute should not be created.
*/
for (i = 0; i < fan->fps_count; i++) {
- if (fan->fps[i].power != FAN_POWER_UNAVAILABLE)
+ if (acpi_fan_power_valid(fan->fps[i].power))
return 0444;
}
@@ -93,13 +89,12 @@ static umode_t acpi_fan_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_
static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
- struct acpi_device *adev = to_acpi_device(dev->parent);
struct acpi_fan *fan = dev_get_drvdata(dev);
struct acpi_fan_fps *fps;
struct acpi_fan_fst fst;
int ret;
- ret = acpi_fan_get_fst(adev, &fst);
+ ret = acpi_fan_get_fst(fan->handle, &fst);
if (ret < 0)
return ret;
@@ -107,7 +102,7 @@ static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
- if (fst.speed == FAN_SPEED_UNAVAILABLE)
+ if (!acpi_fan_speed_valid(fst.speed))
return -ENODEV;
if (fst.speed > LONG_MAX)
@@ -135,7 +130,7 @@ static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
if (!fps)
return -EIO;
- if (fps->power == FAN_POWER_UNAVAILABLE)
+ if (!acpi_fan_power_valid(fps->power))
return -ENODEV;
if (fps->power > LONG_MAX / MICROWATT_PER_MILLIWATT)
@@ -167,12 +162,19 @@ static const struct hwmon_chip_info acpi_fan_hwmon_chip_info = {
.info = acpi_fan_hwmon_info,
};
-int devm_acpi_fan_create_hwmon(struct acpi_device *device)
+void acpi_fan_notify_hwmon(struct device *dev)
+{
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+
+ hwmon_notify_event(fan->hdev, hwmon_fan, hwmon_fan_input, 0);
+}
+
+int devm_acpi_fan_create_hwmon(struct device *dev)
{
- struct acpi_fan *fan = acpi_driver_data(device);
- struct device *hdev;
+ struct acpi_fan *fan = dev_get_drvdata(dev);
+
+ fan->hdev = devm_hwmon_device_register_with_info(dev, "acpi_fan", fan,
+ &acpi_fan_hwmon_chip_info, NULL);
- hdev = devm_hwmon_device_register_with_info(&device->dev, "acpi_fan", fan,
- &acpi_fan_hwmon_chip_info, NULL);
- return PTR_ERR_OR_ZERO(hdev);
+ return PTR_ERR_OR_ZERO(fan->hdev);
}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 63354972ab0b..40f875b265a9 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -27,7 +27,6 @@ static inline void acpi_pci_link_init(void) {}
void acpi_processor_init(void);
void acpi_platform_init(void);
void acpi_pnp_init(void);
-void acpi_int340x_thermal_init(void);
int acpi_sysfs_init(void);
void acpi_gpe_apply_masked_gpes(void);
void acpi_container_init(void);
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
index 76a856c32c4d..d1595156c86a 100644
--- a/drivers/acpi/irq.c
+++ b/drivers/acpi/irq.c
@@ -300,6 +300,25 @@ int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
}
EXPORT_SYMBOL_GPL(acpi_irq_get);
+const struct cpumask *acpi_irq_get_affinity(acpi_handle handle,
+ unsigned int index)
+{
+ struct irq_fwspec_info info;
+ struct irq_fwspec fwspec;
+ unsigned long flags;
+
+ if (acpi_irq_parse_one(handle, index, &fwspec, &flags))
+ return NULL;
+
+ if (irq_populate_fwspec_info(&fwspec, &info))
+ return NULL;
+
+ if (!(info.flags & IRQ_FWSPEC_INFO_AFFINITY_VALID))
+ return NULL;
+
+ return info.affinity;
+}
+
/**
* acpi_set_irq_model - Setup the GSI irqdomain information
* @model: the value assigned to acpi_irq_model
diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c
index 5a36d57289b4..77a81627aaef 100644
--- a/drivers/acpi/numa/hmat.c
+++ b/drivers/acpi/numa/hmat.c
@@ -874,11 +874,33 @@ static void hmat_register_target_devices(struct memory_target *target)
}
}
-static void hmat_register_target(struct memory_target *target)
+static void hmat_hotplug_target(struct memory_target *target)
{
int nid = pxm_to_node(target->memory_pxm);
/*
+ * Skip offline nodes. This can happen when memory marked EFI_MEMORY_SP,
+ * "specific purpose", is applied to all the memory in a proximity
+ * domain leading to * the node being marked offline / unplugged, or if
+ * memory-only "hotplug" node is offline.
+ */
+ if (nid == NUMA_NO_NODE || !node_online(nid))
+ return;
+
+ guard(mutex)(&target_lock);
+ if (target->registered)
+ return;
+
+ hmat_register_target_initiators(target);
+ hmat_register_target_cache(target);
+ hmat_register_target_perf(target, ACCESS_COORDINATE_LOCAL);
+ hmat_register_target_perf(target, ACCESS_COORDINATE_CPU);
+ target->registered = true;
+}
+
+static void hmat_register_target(struct memory_target *target)
+{
+ /*
* Devices may belong to either an offline or online
* node, so unconditionally add them.
*/
@@ -888,32 +910,15 @@ static void hmat_register_target(struct memory_target *target)
* Register generic port perf numbers. The nid may not be
* initialized and is still NUMA_NO_NODE.
*/
- mutex_lock(&target_lock);
- if (*(u16 *)target->gen_port_device_handle) {
- hmat_update_generic_target(target);
- target->registered = true;
+ scoped_guard(mutex, &target_lock) {
+ if (*(u16 *)target->gen_port_device_handle) {
+ hmat_update_generic_target(target);
+ target->registered = true;
+ return;
+ }
}
- mutex_unlock(&target_lock);
- /*
- * Skip offline nodes. This can happen when memory
- * marked EFI_MEMORY_SP, "specific purpose", is applied
- * to all the memory in a proximity domain leading to
- * the node being marked offline / unplugged, or if
- * memory-only "hotplug" node is offline.
- */
- if (nid == NUMA_NO_NODE || !node_online(nid))
- return;
-
- mutex_lock(&target_lock);
- if (!target->registered) {
- hmat_register_target_initiators(target);
- hmat_register_target_cache(target);
- hmat_register_target_perf(target, ACCESS_COORDINATE_LOCAL);
- hmat_register_target_perf(target, ACCESS_COORDINATE_CPU);
- target->registered = true;
- }
- mutex_unlock(&target_lock);
+ hmat_hotplug_target(target);
}
static void hmat_register_targets(void)
@@ -939,7 +944,7 @@ static int hmat_callback(struct notifier_block *self,
if (!target)
return NOTIFY_OK;
- hmat_register_target(target);
+ hmat_hotplug_target(target);
return NOTIFY_OK;
}
diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c
index 53816dfab645..aa87ee1583a4 100644
--- a/drivers/acpi/numa/srat.c
+++ b/drivers/acpi/numa/srat.c
@@ -237,7 +237,7 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
struct acpi_srat_generic_affinity *p =
(struct acpi_srat_generic_affinity *)header;
- if (p->device_handle_type == 0) {
+ if (p->device_handle_type == 1) {
/*
* For pci devices this may be the only place they
* are assigned a proximity domain
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 5ff343096ece..05393a7315fe 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -398,7 +398,7 @@ static void acpi_os_drop_map_ref(struct acpi_ioremap *map)
list_del_rcu(&map->list);
INIT_RCU_WORK(&map->track.rwork, acpi_os_map_remove);
- queue_rcu_work(system_wq, &map->track.rwork);
+ queue_rcu_work(system_percpu_wq, &map->track.rwork);
}
/**
@@ -1694,8 +1694,8 @@ acpi_status __init acpi_os_initialize(void)
acpi_status __init acpi_os_initialize1(void)
{
- kacpid_wq = alloc_workqueue("kacpid", 0, 1);
- kacpi_notify_wq = alloc_workqueue("kacpi_notify", 0, 0);
+ kacpid_wq = alloc_workqueue("kacpid", WQ_PERCPU, 1);
+ kacpi_notify_wq = alloc_workqueue("kacpi_notify", WQ_PERCPU, 0);
kacpi_hotplug_wq = alloc_ordered_workqueue("kacpi_hotplug", 0);
BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);
diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c
index 54676e3d82dd..de5f8c018333 100644
--- a/drivers/acpi/pptt.c
+++ b/drivers/acpi/pptt.c
@@ -21,6 +21,25 @@
#include <linux/cacheinfo.h>
#include <acpi/processor.h>
+/*
+ * The acpi_pptt_cache_v1 in actbl2.h, which is imported from acpica,
+ * only contains the cache_id field rather than all the fields of the
+ * Cache Type Structure. Use this alternative structure until it is
+ * resolved in acpica.
+ */
+struct acpi_pptt_cache_v1_full {
+ struct acpi_subtable_header header;
+ u16 reserved;
+ u32 flags;
+ u32 next_level_of_cache;
+ u32 size;
+ u32 number_of_sets;
+ u8 associativity;
+ u8 attributes;
+ u16 line_size;
+ u32 cache_id;
+} __packed;
+
static struct acpi_subtable_header *fetch_pptt_subtable(struct acpi_table_header *table_hdr,
u32 pptt_ref)
{
@@ -56,6 +75,18 @@ static struct acpi_pptt_cache *fetch_pptt_cache(struct acpi_table_header *table_
return (struct acpi_pptt_cache *)fetch_pptt_subtable(table_hdr, pptt_ref);
}
+static struct acpi_pptt_cache_v1_full *upgrade_pptt_cache(struct acpi_pptt_cache *cache)
+{
+ if (cache->header.length < sizeof(struct acpi_pptt_cache_v1_full))
+ return NULL;
+
+ /* No use for v1 if the only additional field is invalid */
+ if (!(cache->flags & ACPI_PPTT_CACHE_ID_VALID))
+ return NULL;
+
+ return (struct acpi_pptt_cache_v1_full *)cache;
+}
+
static struct acpi_subtable_header *acpi_get_pptt_resource(struct acpi_table_header *table_hdr,
struct acpi_pptt_processor *node,
int resource)
@@ -177,14 +208,14 @@ acpi_find_cache_level(struct acpi_table_header *table_hdr,
}
/**
- * acpi_count_levels() - Given a PPTT table, and a CPU node, count the cache
- * levels and split cache levels (data/instruction).
+ * acpi_count_levels() - Given a PPTT table, and a CPU node, count the
+ * total number of levels and split cache levels (data/instruction).
* @table_hdr: Pointer to the head of the PPTT table
* @cpu_node: processor node we wish to count caches for
- * @levels: Number of levels if success.
* @split_levels: Number of split cache levels (data/instruction) if
* success. Can by NULL.
*
+ * Return: number of levels.
* Given a processor node containing a processing unit, walk into it and count
* how many levels exist solely for it, and then walk up each level until we hit
* the root node (ignore the package level because it may be possible to have
@@ -192,14 +223,18 @@ acpi_find_cache_level(struct acpi_table_header *table_hdr,
* split cache levels (data/instruction) that exist at each level on the way
* up.
*/
-static void acpi_count_levels(struct acpi_table_header *table_hdr,
- struct acpi_pptt_processor *cpu_node,
- unsigned int *levels, unsigned int *split_levels)
+static int acpi_count_levels(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *cpu_node,
+ unsigned int *split_levels)
{
+ int current_level = 0;
+
do {
- acpi_find_cache_level(table_hdr, cpu_node, levels, split_levels, 0, 0);
+ acpi_find_cache_level(table_hdr, cpu_node, &current_level, split_levels, 0, 0);
cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
} while (cpu_node);
+
+ return current_level;
}
/**
@@ -351,7 +386,6 @@ static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *ta
* @this_leaf: Kernel cache info structure being updated
* @found_cache: The PPTT node describing this cache instance
* @cpu_node: A unique reference to describe this cache instance
- * @revision: The revision of the PPTT table
*
* The ACPI spec implies that the fields in the cache structures are used to
* extend and correct the information probed from the hardware. Lets only
@@ -361,10 +395,9 @@ static struct acpi_pptt_cache *acpi_find_cache_node(struct acpi_table_header *ta
*/
static void update_cache_properties(struct cacheinfo *this_leaf,
struct acpi_pptt_cache *found_cache,
- struct acpi_pptt_processor *cpu_node,
- u8 revision)
+ struct acpi_pptt_processor *cpu_node)
{
- struct acpi_pptt_cache_v1* found_cache_v1;
+ struct acpi_pptt_cache_v1_full *found_cache_v1;
this_leaf->fw_token = cpu_node;
if (found_cache->flags & ACPI_PPTT_SIZE_PROPERTY_VALID)
@@ -414,9 +447,8 @@ static void update_cache_properties(struct cacheinfo *this_leaf,
found_cache->flags & ACPI_PPTT_CACHE_TYPE_VALID)
this_leaf->type = CACHE_TYPE_UNIFIED;
- if (revision >= 3 && (found_cache->flags & ACPI_PPTT_CACHE_ID_VALID)) {
- found_cache_v1 = ACPI_ADD_PTR(struct acpi_pptt_cache_v1,
- found_cache, sizeof(struct acpi_pptt_cache));
+ found_cache_v1 = upgrade_pptt_cache(found_cache);
+ if (found_cache_v1) {
this_leaf->id = found_cache_v1->cache_id;
this_leaf->attributes |= CACHE_ID;
}
@@ -441,8 +473,7 @@ static void cache_setup_acpi_cpu(struct acpi_table_header *table,
pr_debug("found = %p %p\n", found_cache, cpu_node);
if (found_cache)
update_cache_properties(this_leaf, found_cache,
- ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table)),
- table->revision);
+ ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table)));
index++;
}
@@ -645,7 +676,7 @@ int acpi_get_cache_info(unsigned int cpu, unsigned int *levels,
if (!cpu_node)
return -ENOENT;
- acpi_count_levels(table, cpu_node, levels, split_levels);
+ *levels = acpi_count_levels(table, cpu_node, split_levels);
pr_debug("Cache Setup: last_level=%d split_levels=%d\n",
*levels, split_levels ? *split_levels : -1);
@@ -817,3 +848,218 @@ int find_acpi_cpu_topology_hetero_id(unsigned int cpu)
return find_acpi_cpu_topology_tag(cpu, PPTT_ABORT_PACKAGE,
ACPI_PPTT_ACPI_IDENTICAL);
}
+
+/**
+ * acpi_pptt_get_child_cpus() - Find all the CPUs below a PPTT
+ * processor hierarchy node
+ *
+ * @table_hdr: A reference to the PPTT table
+ * @parent_node: A pointer to the processor hierarchy node in the
+ * table_hdr
+ * @cpus: A cpumask to fill with the CPUs below @parent_node
+ *
+ * Walks up the PPTT from every possible CPU to find if the provided
+ * @parent_node is a parent of this CPU.
+ */
+static void acpi_pptt_get_child_cpus(struct acpi_table_header *table_hdr,
+ struct acpi_pptt_processor *parent_node,
+ cpumask_t *cpus)
+{
+ struct acpi_pptt_processor *cpu_node;
+ u32 acpi_id;
+ int cpu;
+
+ cpumask_clear(cpus);
+
+ for_each_possible_cpu(cpu) {
+ acpi_id = get_acpi_id_for_cpu(cpu);
+ cpu_node = acpi_find_processor_node(table_hdr, acpi_id);
+
+ while (cpu_node) {
+ if (cpu_node == parent_node) {
+ cpumask_set_cpu(cpu, cpus);
+ break;
+ }
+ cpu_node = fetch_pptt_node(table_hdr, cpu_node->parent);
+ }
+ }
+}
+
+/**
+ * acpi_pptt_get_cpus_from_container() - Populate a cpumask with all CPUs in a
+ * processor container
+ * @acpi_cpu_id: The UID of the processor container
+ * @cpus: The resulting CPU mask
+ *
+ * Find the specified Processor Container, and fill @cpus with all the cpus
+ * below it.
+ *
+ * Not all 'Processor Hierarchy' entries in the PPTT are either a CPU
+ * or a Processor Container, they may exist purely to describe a
+ * Private resource. CPUs have to be leaves, so a Processor Container
+ * is a non-leaf that has the 'ACPI Processor ID valid' flag set.
+ */
+void acpi_pptt_get_cpus_from_container(u32 acpi_cpu_id, cpumask_t *cpus)
+{
+ struct acpi_table_header *table_hdr;
+ struct acpi_subtable_header *entry;
+ unsigned long table_end;
+ u32 proc_sz;
+
+ cpumask_clear(cpus);
+
+ table_hdr = acpi_get_pptt();
+ if (!table_hdr)
+ return;
+
+ table_end = (unsigned long)table_hdr + table_hdr->length;
+ entry = ACPI_ADD_PTR(struct acpi_subtable_header, table_hdr,
+ sizeof(struct acpi_table_pptt));
+ proc_sz = sizeof(struct acpi_pptt_processor);
+ while ((unsigned long)entry + proc_sz <= table_end) {
+ if (entry->type == ACPI_PPTT_TYPE_PROCESSOR) {
+ struct acpi_pptt_processor *cpu_node;
+
+ cpu_node = (struct acpi_pptt_processor *)entry;
+ if (cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID &&
+ !acpi_pptt_leaf_node(table_hdr, cpu_node) &&
+ cpu_node->acpi_processor_id == acpi_cpu_id) {
+ acpi_pptt_get_child_cpus(table_hdr, cpu_node, cpus);
+ break;
+ }
+ }
+ entry = ACPI_ADD_PTR(struct acpi_subtable_header, entry,
+ entry->length);
+ }
+}
+
+/**
+ * find_acpi_cache_level_from_id() - Get the level of the specified cache
+ * @cache_id: The id field of the cache
+ *
+ * Determine the level relative to any CPU for the cache identified by
+ * cache_id. This allows the property to be found even if the CPUs are offline.
+ *
+ * The returned level can be used to group caches that are peers.
+ *
+ * The PPTT table must be rev 3 or later.
+ *
+ * If one CPU's L2 is shared with another CPU as L3, this function will return
+ * an unpredictable value.
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, the revision isn't supported or
+ * the cache cannot be found.
+ * Otherwise returns a value which represents the level of the specified cache.
+ */
+int find_acpi_cache_level_from_id(u32 cache_id)
+{
+ int cpu;
+ struct acpi_table_header *table;
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ if (table->revision < 3)
+ return -ENOENT;
+
+ for_each_possible_cpu(cpu) {
+ bool empty;
+ int level = 1;
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+ struct acpi_pptt_cache *cache;
+ struct acpi_pptt_processor *cpu_node;
+
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+ if (!cpu_node)
+ continue;
+
+ do {
+ int cache_type[] = {CACHE_TYPE_INST, CACHE_TYPE_DATA, CACHE_TYPE_UNIFIED};
+
+ empty = true;
+ for (int i = 0; i < ARRAY_SIZE(cache_type); i++) {
+ struct acpi_pptt_cache_v1_full *cache_v1;
+
+ cache = acpi_find_cache_node(table, acpi_cpu_id, cache_type[i],
+ level, &cpu_node);
+ if (!cache)
+ continue;
+
+ empty = false;
+
+ cache_v1 = upgrade_pptt_cache(cache);
+ if (cache_v1 && cache_v1->cache_id == cache_id)
+ return level;
+ }
+ level++;
+ } while (!empty);
+ }
+
+ return -ENOENT;
+}
+
+/**
+ * acpi_pptt_get_cpumask_from_cache_id() - Get the cpus associated with the
+ * specified cache
+ * @cache_id: The id field of the cache
+ * @cpus: Where to build the cpumask
+ *
+ * Determine which CPUs are below this cache in the PPTT. This allows the property
+ * to be found even if the CPUs are offline.
+ *
+ * The PPTT table must be rev 3 or later,
+ *
+ * Return: -ENOENT if the PPTT doesn't exist, or the cache cannot be found.
+ * Otherwise returns 0 and sets the cpus in the provided cpumask.
+ */
+int acpi_pptt_get_cpumask_from_cache_id(u32 cache_id, cpumask_t *cpus)
+{
+ int cpu;
+ struct acpi_table_header *table;
+
+ cpumask_clear(cpus);
+
+ table = acpi_get_pptt();
+ if (!table)
+ return -ENOENT;
+
+ if (table->revision < 3)
+ return -ENOENT;
+
+ for_each_possible_cpu(cpu) {
+ bool empty;
+ int level = 1;
+ u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu);
+ struct acpi_pptt_cache *cache;
+ struct acpi_pptt_processor *cpu_node;
+
+ cpu_node = acpi_find_processor_node(table, acpi_cpu_id);
+ if (!cpu_node)
+ continue;
+
+ do {
+ int cache_type[] = {CACHE_TYPE_INST, CACHE_TYPE_DATA, CACHE_TYPE_UNIFIED};
+
+ empty = true;
+ for (int i = 0; i < ARRAY_SIZE(cache_type); i++) {
+ struct acpi_pptt_cache_v1_full *cache_v1;
+
+ cache = acpi_find_cache_node(table, acpi_cpu_id, cache_type[i],
+ level, &cpu_node);
+
+ if (!cache)
+ continue;
+
+ empty = false;
+
+ cache_v1 = upgrade_pptt_cache(cache);
+ if (cache_v1 && cache_v1->cache_id == cache_id)
+ cpumask_set_cpu(cpu, cpus);
+ }
+ level++;
+ } while (!empty);
+ }
+
+ return 0;
+}
diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c
index 6792d4385eee..7b8b5d2015ec 100644
--- a/drivers/acpi/prmt.c
+++ b/drivers/acpi/prmt.c
@@ -244,6 +244,12 @@ static struct prm_handler_info *find_prm_handler(const guid_t *guid)
return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
}
+bool acpi_prm_handler_available(const guid_t *guid)
+{
+ return find_prm_handler(guid) && find_prm_module(guid);
+}
+EXPORT_SYMBOL_GPL(acpi_prm_handler_available);
+
/* In-coming PRM commands */
#define PRM_CMD_RUN_SERVICE 0
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 9b6b71a2ffb5..a4498357bd16 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -54,7 +54,7 @@ static int map_x2apic_id(struct acpi_subtable_header *entry,
if (!(apic->lapic_flags & ACPI_MADT_ENABLED))
return -ENODEV;
- if (device_declaration && (apic->uid == acpi_id)) {
+ if (apic->uid == acpi_id && (device_declaration || acpi_id < 255)) {
*apic_id = apic->local_apic_id;
return 0;
}
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 5d824435b26b..65e779be64ff 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -166,7 +166,8 @@ static int __acpi_processor_start(struct acpi_device *device)
if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
dev_dbg(&device->dev, "CPPC data invalid or not present\n");
- acpi_processor_power_init(pr);
+ if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
+ acpi_processor_power_init(pr);
acpi_pss_perf_init(pr);
@@ -262,8 +263,6 @@ static int __init acpi_processor_driver_init(void)
if (result < 0)
return result;
- acpi_processor_register_idle_driver();
-
result = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"acpi/cpu-drv:online",
acpi_soft_cpu_online, NULL);
@@ -302,7 +301,6 @@ static void __exit acpi_processor_driver_exit(void)
cpuhp_remove_state_nocalls(hp_online);
cpuhp_remove_state_nocalls(CPUHP_ACPI_CPUDRV_DEAD);
- acpi_processor_unregister_idle_driver();
driver_unregister(&acpi_processor_driver);
}
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 22b051b94a86..89f2f08b2554 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -51,7 +51,7 @@ module_param(latency_factor, uint, 0644);
static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device);
-static struct cpuidle_driver acpi_idle_driver = {
+struct cpuidle_driver acpi_idle_driver = {
.name = "acpi_idle",
.owner = THIS_MODULE,
};
@@ -732,18 +732,16 @@ static int __cpuidle acpi_idle_enter_s2idle(struct cpuidle_device *dev,
return 0;
}
-static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
- struct cpuidle_device *dev)
+static void acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
+ struct cpuidle_device *dev)
{
int i, count = ACPI_IDLE_STATE_START;
struct acpi_processor_cx *cx;
- struct cpuidle_state *state;
if (max_cstate == 0)
max_cstate = 1;
for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
- state = &acpi_idle_driver.states[count];
cx = &pr->power.states[i];
if (!cx->valid)
@@ -751,27 +749,13 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
per_cpu(acpi_cstate[count], dev->cpu) = cx;
- if (lapic_timer_needs_broadcast(pr, cx))
- state->flags |= CPUIDLE_FLAG_TIMER_STOP;
-
- if (cx->type == ACPI_STATE_C3) {
- state->flags |= CPUIDLE_FLAG_TLB_FLUSHED;
- if (pr->flags.bm_check)
- state->flags |= CPUIDLE_FLAG_RCU_IDLE;
- }
-
count++;
if (count == CPUIDLE_STATE_MAX)
break;
}
-
- if (!count)
- return -EINVAL;
-
- return 0;
}
-static int acpi_processor_setup_cstates(struct acpi_processor *pr)
+static void acpi_processor_setup_cstates(struct acpi_processor *pr)
{
int i, count;
struct acpi_processor_cx *cx;
@@ -818,17 +802,21 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr)
if (cx->type != ACPI_STATE_C1 && !acpi_idle_fallback_to_c1(pr))
state->enter_s2idle = acpi_idle_enter_s2idle;
+ if (lapic_timer_needs_broadcast(pr, cx))
+ state->flags |= CPUIDLE_FLAG_TIMER_STOP;
+
+ if (cx->type == ACPI_STATE_C3) {
+ state->flags |= CPUIDLE_FLAG_TLB_FLUSHED;
+ if (pr->flags.bm_check)
+ state->flags |= CPUIDLE_FLAG_RCU_IDLE;
+ }
+
count++;
if (count == CPUIDLE_STATE_MAX)
break;
}
drv->state_count = count;
-
- if (!count)
- return -EINVAL;
-
- return 0;
}
static inline void acpi_processor_cstate_first_run_checks(void)
@@ -1243,7 +1231,8 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
if (pr->flags.has_lpi)
return acpi_processor_setup_lpi_states(pr);
- return acpi_processor_setup_cstates(pr);
+ acpi_processor_setup_cstates(pr);
+ return 0;
}
/**
@@ -1263,7 +1252,8 @@ static int acpi_processor_setup_cpuidle_dev(struct acpi_processor *pr,
if (pr->flags.has_lpi)
return acpi_processor_ffh_lpi_probe(pr->id);
- return acpi_processor_setup_cpuidle_cx(pr, dev);
+ acpi_processor_setup_cpuidle_cx(pr, dev);
+ return 0;
}
static int acpi_processor_get_power_info(struct acpi_processor *pr)
@@ -1357,102 +1347,79 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
return 0;
}
-void acpi_processor_register_idle_driver(void)
-{
- struct acpi_processor *pr;
- int ret = -ENODEV;
- int cpu;
-
- /*
- * Acpi idle driver is used by all possible CPUs.
- * Install the idle handler by the processor power info of one in them.
- * Note that we use previously set idle handler will be used on
- * platforms that only support C1.
- */
- for_each_cpu(cpu, (struct cpumask *)cpu_possible_mask) {
- pr = per_cpu(processors, cpu);
- if (!pr)
- continue;
-
- ret = acpi_processor_get_power_info(pr);
- if (!ret) {
- pr->flags.power_setup_done = 1;
- acpi_processor_setup_cpuidle_states(pr);
- break;
- }
- }
-
- if (ret) {
- pr_debug("No ACPI power information from any CPUs.\n");
- return;
- }
-
- ret = cpuidle_register_driver(&acpi_idle_driver);
- if (ret) {
- pr_debug("register %s failed.\n", acpi_idle_driver.name);
- return;
- }
- pr_debug("%s registered with cpuidle.\n", acpi_idle_driver.name);
-}
-
-void acpi_processor_unregister_idle_driver(void)
-{
- cpuidle_unregister_driver(&acpi_idle_driver);
-}
+static int acpi_processor_registered;
-void acpi_processor_power_init(struct acpi_processor *pr)
+int acpi_processor_power_init(struct acpi_processor *pr)
{
+ int retval;
struct cpuidle_device *dev;
- /*
- * The code below only works if the current cpuidle driver is the ACPI
- * idle driver.
- */
- if (cpuidle_get_driver() != &acpi_idle_driver)
- return;
-
if (disabled_by_idle_boot_param())
- return;
+ return 0;
acpi_processor_cstate_first_run_checks();
if (!acpi_processor_get_power_info(pr))
pr->flags.power_setup_done = 1;
- if (!pr->flags.power)
- return;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return;
+ /*
+ * Install the idle handler if processor power management is supported.
+ * Note that we use previously set idle handler will be used on
+ * platforms that only support C1.
+ */
+ if (pr->flags.power) {
+ /* Register acpi_idle_driver if not already registered */
+ if (!acpi_processor_registered) {
+ acpi_processor_setup_cpuidle_states(pr);
+ retval = cpuidle_register_driver(&acpi_idle_driver);
+ if (retval)
+ return retval;
+ pr_debug("%s registered with cpuidle\n",
+ acpi_idle_driver.name);
+ }
- per_cpu(acpi_cpuidle_device, pr->id) = dev;
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ per_cpu(acpi_cpuidle_device, pr->id) = dev;
- acpi_processor_setup_cpuidle_dev(pr, dev);
+ acpi_processor_setup_cpuidle_dev(pr, dev);
- /*
- * Register a cpuidle device for this CPU. The cpuidle driver using
- * this device is expected to be registered.
- */
- if (cpuidle_register_device(dev)) {
- per_cpu(acpi_cpuidle_device, pr->id) = NULL;
- kfree(dev);
+ /* Register per-cpu cpuidle_device. Cpuidle driver
+ * must already be registered before registering device
+ */
+ retval = cpuidle_register_device(dev);
+ if (retval) {
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);
+
+ per_cpu(acpi_cpuidle_device, pr->id) = NULL;
+ kfree(dev);
+ return retval;
+ }
+ acpi_processor_registered++;
}
+ return 0;
}
-void acpi_processor_power_exit(struct acpi_processor *pr)
+int acpi_processor_power_exit(struct acpi_processor *pr)
{
struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id);
if (disabled_by_idle_boot_param())
- return;
+ return 0;
if (pr->flags.power) {
cpuidle_unregister_device(dev);
+ acpi_processor_registered--;
+ if (acpi_processor_registered == 0)
+ cpuidle_unregister_driver(&acpi_idle_driver);
+
kfree(dev);
}
pr->flags.power_setup_done = 0;
+ return 0;
}
MODULE_IMPORT_NS("ACPI_PROCESSOR_IDLE");
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 1b997a5497e7..18e90067d567 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -1107,7 +1107,7 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode,
size_t num_args,
struct fwnode_reference_args *args)
{
- return acpi_fwnode_get_reference_args(fwnode, propname, NULL, index, num_args, args);
+ return acpi_fwnode_get_reference_args(fwnode, propname, NULL, num_args, index, args);
}
EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
@@ -1280,7 +1280,7 @@ static int acpi_data_prop_read(const struct acpi_device_data *data,
ret = acpi_copy_property_array_uint(items, (u64 *)val, nval);
break;
case DEV_PROP_STRING:
- nval = min_t(u32, nval, obj->package.count);
+ nval = min(nval, obj->package.count);
if (nval == 0)
return -ENODATA;
@@ -1329,13 +1329,14 @@ static int stop_on_next(struct acpi_device *adev, void *data)
return 0;
}
-/**
+/*
* acpi_get_next_subnode - Return the next child node handle for a fwnode
* @fwnode: Firmware node to find the next child node for.
* @child: Handle to one of the device's child nodes or a null handle.
*/
-struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode,
- struct fwnode_handle *child)
+static struct fwnode_handle *
+acpi_get_next_subnode(const struct fwnode_handle *fwnode,
+ struct fwnode_handle *child)
{
struct acpi_device *adev = to_acpi_device_node(fwnode);
@@ -1472,7 +1473,7 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint(
if (!prev) {
do {
- port = fwnode_get_next_child_node(fwnode, port);
+ port = acpi_get_next_subnode(fwnode, port);
/*
* The names of the port nodes begin with "port@"
* followed by the number of the port node and they also
@@ -1490,14 +1491,17 @@ static struct fwnode_handle *acpi_graph_get_next_endpoint(
if (!port)
return NULL;
- endpoint = fwnode_get_next_child_node(port, prev);
- while (!endpoint) {
- port = fwnode_get_next_child_node(fwnode, port);
- if (!port)
+ do {
+ endpoint = acpi_get_next_subnode(port, prev);
+ if (endpoint)
break;
- if (is_acpi_graph_node(port, "port"))
- endpoint = fwnode_get_next_child_node(port, NULL);
- }
+
+ prev = NULL;
+
+ do {
+ port = acpi_get_next_subnode(fwnode, port);
+ } while (port && !is_acpi_graph_node(port, "port"));
+ } while (port);
/*
* The names of the endpoint nodes begin with "endpoint@" followed by
@@ -1714,6 +1718,7 @@ static int acpi_fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
if (fwnode_property_read_u32(fwnode, "reg", &endpoint->id))
fwnode_property_read_u32(fwnode, "endpoint", &endpoint->id);
+ fwnode_handle_put(port_fwnode);
return 0;
}
diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c
index 683fcfe35c31..7f423405e5ef 100644
--- a/drivers/acpi/riscv/rimt.c
+++ b/drivers/acpi/riscv/rimt.c
@@ -61,30 +61,6 @@ static int rimt_set_fwnode(struct acpi_rimt_node *rimt_node,
return 0;
}
-/**
- * rimt_get_fwnode() - Retrieve fwnode associated with an RIMT node
- *
- * @node: RIMT table node to be looked-up
- *
- * Returns: fwnode_handle pointer on success, NULL on failure
- */
-static struct fwnode_handle *rimt_get_fwnode(struct acpi_rimt_node *node)
-{
- struct fwnode_handle *fwnode = NULL;
- struct rimt_fwnode *curr;
-
- spin_lock(&rimt_fwnode_lock);
- list_for_each_entry(curr, &rimt_fwnode_list, list) {
- if (curr->rimt_node == node) {
- fwnode = curr->fwnode;
- break;
- }
- }
- spin_unlock(&rimt_fwnode_lock);
-
- return fwnode;
-}
-
static acpi_status rimt_match_node_callback(struct acpi_rimt_node *node,
void *context)
{
@@ -202,6 +178,67 @@ static struct acpi_rimt_node *rimt_scan_node(enum acpi_rimt_node_type type,
return NULL;
}
+/*
+ * RISC-V supports IOMMU as a PCI device or a platform device.
+ * When it is a platform device, there should be a namespace device as
+ * well along with RIMT. To create the link between RIMT information and
+ * the platform device, the IOMMU driver should register itself with the
+ * RIMT module. This is true for PCI based IOMMU as well.
+ */
+int rimt_iommu_register(struct device *dev)
+{
+ struct fwnode_handle *rimt_fwnode;
+ struct acpi_rimt_node *node;
+
+ node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_IOMMU, dev);
+ if (!node) {
+ pr_err("Could not find IOMMU node in RIMT\n");
+ return -ENODEV;
+ }
+
+ if (dev_is_pci(dev)) {
+ rimt_fwnode = acpi_alloc_fwnode_static();
+ if (!rimt_fwnode)
+ return -ENOMEM;
+
+ rimt_fwnode->dev = dev;
+ if (!dev->fwnode)
+ dev->fwnode = rimt_fwnode;
+
+ rimt_set_fwnode(node, rimt_fwnode);
+ } else {
+ rimt_set_fwnode(node, dev->fwnode);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_IOMMU_API
+
+/**
+ * rimt_get_fwnode() - Retrieve fwnode associated with an RIMT node
+ *
+ * @node: RIMT table node to be looked-up
+ *
+ * Returns: fwnode_handle pointer on success, NULL on failure
+ */
+static struct fwnode_handle *rimt_get_fwnode(struct acpi_rimt_node *node)
+{
+ struct fwnode_handle *fwnode = NULL;
+ struct rimt_fwnode *curr;
+
+ spin_lock(&rimt_fwnode_lock);
+ list_for_each_entry(curr, &rimt_fwnode_list, list) {
+ if (curr->rimt_node == node) {
+ fwnode = curr->fwnode;
+ break;
+ }
+ }
+ spin_unlock(&rimt_fwnode_lock);
+
+ return fwnode;
+}
+
static bool rimt_pcie_rc_supports_ats(struct acpi_rimt_node *node)
{
struct acpi_rimt_pcie_rc *pci_rc;
@@ -290,43 +327,6 @@ static struct acpi_rimt_node *rimt_node_get_id(struct acpi_rimt_node *node,
return NULL;
}
-/*
- * RISC-V supports IOMMU as a PCI device or a platform device.
- * When it is a platform device, there should be a namespace device as
- * well along with RIMT. To create the link between RIMT information and
- * the platform device, the IOMMU driver should register itself with the
- * RIMT module. This is true for PCI based IOMMU as well.
- */
-int rimt_iommu_register(struct device *dev)
-{
- struct fwnode_handle *rimt_fwnode;
- struct acpi_rimt_node *node;
-
- node = rimt_scan_node(ACPI_RIMT_NODE_TYPE_IOMMU, dev);
- if (!node) {
- pr_err("Could not find IOMMU node in RIMT\n");
- return -ENODEV;
- }
-
- if (dev_is_pci(dev)) {
- rimt_fwnode = acpi_alloc_fwnode_static();
- if (!rimt_fwnode)
- return -ENOMEM;
-
- rimt_fwnode->dev = dev;
- if (!dev->fwnode)
- dev->fwnode = rimt_fwnode;
-
- rimt_set_fwnode(node, rimt_fwnode);
- } else {
- rimt_set_fwnode(node, dev->fwnode);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_IOMMU_API
-
static struct acpi_rimt_node *rimt_node_map_id(struct acpi_rimt_node *node,
u32 id_in, u32 *id_out,
u8 type_mask)
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index a3f95a3fffde..d3edc3bcbf01 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -487,7 +487,7 @@ static int acpi_battery_read(struct acpi_battery *battery)
if (result)
return result;
- battery->present = state & (1 << battery->id);
+ battery->present = !!(state & (1 << battery->id));
if (!battery->present)
return 0;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index ef16d58b2949..416d87f9bd10 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2397,7 +2397,7 @@ static bool acpi_scan_clear_dep_queue(struct acpi_device *adev)
* initial enumeration of devices is complete, put it into the unbound
* workqueue.
*/
- queue_work(system_unbound_wq, &cdw->work);
+ queue_work(system_dfl_wq, &cdw->work);
return true;
}
@@ -2711,7 +2711,6 @@ void __init acpi_scan_init(void)
acpi_watchdog_init();
acpi_pnp_init();
acpi_power_resources_init();
- acpi_int340x_thermal_init();
acpi_init_lpit();
acpi_scan_add_handler(&generic_device_handler);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index c8ee8e42b0f6..68943b98333d 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -642,7 +642,7 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
/*
* Disable all GPE and clear their status bits before interrupts are
* enabled. Some GPEs (like wakeup GPEs) have no handlers and this can
- * prevent them from producing spurious interrups.
+ * prevent them from producing spurious interrupts.
*
* acpi_leave_sleep_state() will reenable specific GPEs later.
*
diff --git a/drivers/acpi/sleep.h b/drivers/acpi/sleep.h
index d960a238be4e..9c3cb109c5d2 100644
--- a/drivers/acpi/sleep.h
+++ b/drivers/acpi/sleep.h
@@ -17,10 +17,7 @@ static inline acpi_status acpi_set_waking_vector(u32 wakeup_address)
extern int acpi_s2idle_begin(void);
extern int acpi_s2idle_prepare(void);
-extern int acpi_s2idle_prepare_late(void);
-extern void acpi_s2idle_check(void);
extern bool acpi_s2idle_wake(void);
-extern void acpi_s2idle_restore_early(void);
extern void acpi_s2idle_restore(void);
extern void acpi_s2idle_end(void);
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
index d4d52d5e9016..73cb933fdc89 100644
--- a/drivers/acpi/spcr.c
+++ b/drivers/acpi/spcr.c
@@ -155,7 +155,7 @@ int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
* Baud Rate field. If this field is zero or not present, Configured
* Baud Rate is used.
*/
- if (table->precise_baudrate)
+ if (table->header.revision >= 4 && table->precise_baudrate)
baud_rate = table->precise_baudrate;
else switch (table->baud_rate) {
case 0:
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index 57fc8bc56166..4286e4af1092 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -408,7 +408,7 @@ static const char table_sigs[][ACPI_NAMESEG_SIZE] __nonstring_array __initconst
ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT,
ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT,
ACPI_SIG_NHLT, ACPI_SIG_AEST, ACPI_SIG_CEDT, ACPI_SIG_AGDI,
- ACPI_SIG_NBFT, ACPI_SIG_SWFT};
+ ACPI_SIG_NBFT, ACPI_SIG_SWFT, ACPI_SIG_MPAM};
#define ACPI_HEADER_SIZE sizeof(struct acpi_table_header)
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 8537395b417b..a511f9ea0267 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -1060,7 +1060,8 @@ static int __init acpi_thermal_init(void)
}
acpi_thermal_pm_queue = alloc_workqueue("acpi_thermal_pm",
- WQ_HIGHPRI | WQ_MEM_RECLAIM, 0);
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!acpi_thermal_pm_queue)
return -ENODEV;
diff --git a/drivers/acpi/x86/lpss.c b/drivers/acpi/x86/lpss.c
index 6daa6372f980..1dcb80ab0d23 100644
--- a/drivers/acpi/x86/lpss.c
+++ b/drivers/acpi/x86/lpss.c
@@ -181,7 +181,7 @@ static void byt_i2c_setup(struct lpss_private_data *pdata)
acpi_status status;
u64 uid;
- /* Expected to always be successfull, but better safe then sorry */
+ /* Expected to always be successful, but better safe then sorry */
if (!acpi_dev_uid_to_integer(pdata->adev, &uid) && uid) {
/* Detect I2C bus shared with PUNIT and ignore its d3 status */
status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host);
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
index dd0b40b9bbe8..6d4d06236f61 100644
--- a/drivers/acpi/x86/s2idle.c
+++ b/drivers/acpi/x86/s2idle.c
@@ -299,34 +299,13 @@ free_acpi_buffer:
ACPI_FREE(out_obj);
}
-/**
- * acpi_get_lps0_constraint - Get the LPS0 constraint for a device.
- * @adev: Device to get the constraint for.
- *
- * The LPS0 constraint is the shallowest (minimum) power state in which the
- * device can be so as to allow the platform as a whole to achieve additional
- * energy conservation by utilizing a system-wide low-power state.
- *
- * Returns:
- * - ACPI power state value of the constraint for @adev on success.
- * - Otherwise, ACPI_STATE_UNKNOWN.
- */
-int acpi_get_lps0_constraint(struct acpi_device *adev)
-{
- struct lpi_constraints *entry;
-
- for_each_lpi_constraint(entry) {
- if (adev->handle == entry->handle)
- return entry->min_dstate;
- }
-
- return ACPI_STATE_UNKNOWN;
-}
-
static void lpi_check_constraints(void)
{
struct lpi_constraints *entry;
+ if (IS_ERR_OR_NULL(lpi_constraints_table))
+ return;
+
for_each_lpi_constraint(entry) {
struct acpi_device *adev = acpi_fetch_acpi_dev(entry->handle);
@@ -508,11 +487,6 @@ static int lps0_device_attach(struct acpi_device *adev,
lps0_device_handle = adev->handle;
- if (acpi_s2idle_vendor_amd())
- lpi_device_get_constraints_amd();
- else
- lpi_device_get_constraints();
-
/*
* Use suspend-to-idle by default if ACPI_FADT_LOW_POWER_S0 is set in
* the FADT and the default suspend mode was not set from the command
@@ -539,7 +513,26 @@ static struct acpi_scan_handler lps0_handler = {
.attach = lps0_device_attach,
};
-int acpi_s2idle_prepare_late(void)
+static int acpi_s2idle_begin_lps0(void)
+{
+ if (pm_debug_messages_on && !lpi_constraints_table) {
+ if (acpi_s2idle_vendor_amd())
+ lpi_device_get_constraints_amd();
+ else
+ lpi_device_get_constraints();
+
+ /*
+ * Try to retrieve the constraints only once because failures
+ * to do so usually are sticky.
+ */
+ if (!lpi_constraints_table)
+ lpi_constraints_table = ERR_PTR(-ENODATA);
+ }
+
+ return acpi_s2idle_begin();
+}
+
+static int acpi_s2idle_prepare_late_lps0(void)
{
struct acpi_s2idle_dev_ops *handler;
@@ -585,7 +578,7 @@ int acpi_s2idle_prepare_late(void)
return 0;
}
-void acpi_s2idle_check(void)
+static void acpi_s2idle_check_lps0(void)
{
struct acpi_s2idle_dev_ops *handler;
@@ -598,7 +591,7 @@ void acpi_s2idle_check(void)
}
}
-void acpi_s2idle_restore_early(void)
+static void acpi_s2idle_restore_early_lps0(void)
{
struct acpi_s2idle_dev_ops *handler;
@@ -636,12 +629,12 @@ void acpi_s2idle_restore_early(void)
}
static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
- .begin = acpi_s2idle_begin,
+ .begin = acpi_s2idle_begin_lps0,
.prepare = acpi_s2idle_prepare,
- .prepare_late = acpi_s2idle_prepare_late,
- .check = acpi_s2idle_check,
+ .prepare_late = acpi_s2idle_prepare_late_lps0,
+ .check = acpi_s2idle_check_lps0,
.wake = acpi_s2idle_wake,
- .restore_early = acpi_s2idle_restore_early,
+ .restore_early = acpi_s2idle_restore_early_lps0,
.restore = acpi_s2idle_restore,
.end = acpi_s2idle_end,
};
diff --git a/drivers/amba/Kconfig b/drivers/amba/Kconfig
index fb6c7e0b4cce..14bb61ff801e 100644
--- a/drivers/amba/Kconfig
+++ b/drivers/amba/Kconfig
@@ -5,7 +5,7 @@ config ARM_AMBA
if ARM_AMBA
config TEGRA_AHB
- bool
+ bool "Enable AHB driver for NVIDIA Tegra SoCs" if COMPILE_TEST
default y if ARCH_TEGRA
help
Adds AHB configuration functionality for NVIDIA Tegra SoCs,
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 74e34a07ef72..952c45ca6e48 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -291,15 +291,14 @@ static int amba_probe(struct device *dev)
if (ret < 0)
break;
- ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON);
+ ret = dev_pm_domain_attach(dev, PD_FLAG_ATTACH_POWER_ON |
+ PD_FLAG_DETACH_POWER_OFF);
if (ret)
break;
ret = amba_get_enable_pclk(pcdev);
- if (ret) {
- dev_pm_domain_detach(dev, true);
+ if (ret)
break;
- }
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
@@ -314,7 +313,6 @@ static int amba_probe(struct device *dev)
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
- dev_pm_domain_detach(dev, true);
} while (0);
return ret;
@@ -336,7 +334,6 @@ static void amba_remove(struct device *dev)
pm_runtime_put_noidle(dev);
amba_put_disable_pclk(pcdev);
- dev_pm_domain_detach(dev, true);
}
static void amba_shutdown(struct device *dev)
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 8c99ceaa303b..a3a1b5c33ba3 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -851,17 +851,8 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong,
} else {
if (!internal)
node->local_weak_refs++;
- if (!node->has_weak_ref && list_empty(&node->work.entry)) {
- if (target_list == NULL) {
- pr_err("invalid inc weak node for %d\n",
- node->debug_id);
- return -EINVAL;
- }
- /*
- * See comment above
- */
+ if (!node->has_weak_ref && target_list && list_empty(&node->work.entry))
binder_enqueue_work_ilocked(&node->work, target_list);
- }
}
return 0;
}
@@ -2418,10 +2409,10 @@ err_fd_not_accepted:
/**
* struct binder_ptr_fixup - data to be fixed-up in target buffer
- * @offset offset in target buffer to fixup
- * @skip_size bytes to skip in copy (fixup will be written later)
- * @fixup_data data to write at fixup offset
- * @node list node
+ * @offset: offset in target buffer to fixup
+ * @skip_size: bytes to skip in copy (fixup will be written later)
+ * @fixup_data: data to write at fixup offset
+ * @node: list node
*
* This is used for the pointer fixup list (pf) which is created and consumed
* during binder_transaction() and is only accessed locally. No
@@ -2438,10 +2429,10 @@ struct binder_ptr_fixup {
/**
* struct binder_sg_copy - scatter-gather data to be copied
- * @offset offset in target buffer
- * @sender_uaddr user address in source buffer
- * @length bytes to copy
- * @node list node
+ * @offset: offset in target buffer
+ * @sender_uaddr: user address in source buffer
+ * @length: bytes to copy
+ * @node: list node
*
* This is used for the sg copy list (sgc) which is created and consumed
* during binder_transaction() and is only accessed locally. No
@@ -4063,14 +4054,15 @@ binder_freeze_notification_done(struct binder_proc *proc,
/**
* binder_free_buf() - free the specified buffer
- * @proc: binder proc that owns buffer
- * @buffer: buffer to be freed
- * @is_failure: failed to send transaction
+ * @proc: binder proc that owns buffer
+ * @thread: binder thread performing the buffer release
+ * @buffer: buffer to be freed
+ * @is_failure: failed to send transaction
*
- * If buffer for an async transaction, enqueue the next async
+ * If the buffer is for an async transaction, enqueue the next async
* transaction from the node.
*
- * Cleanup buffer and free it.
+ * Cleanup the buffer and free it.
*/
static void
binder_free_buf(struct binder_proc *proc,
diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs
index 9921827267d0..b24497cfa292 100644
--- a/drivers/android/binder/error.rs
+++ b/drivers/android/binder/error.rs
@@ -2,6 +2,7 @@
// Copyright (C) 2025 Google LLC.
+use kernel::fmt;
use kernel::prelude::*;
use crate::defs::*;
@@ -76,8 +77,8 @@ impl From<kernel::alloc::AllocError> for BinderError {
}
}
-impl core::fmt::Debug for BinderError {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+impl fmt::Debug for BinderError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.reply {
BR_FAILED_REPLY => match self.source.as_ref() {
Some(source) => f
diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs
index e68c3c8bc55a..53b60035639a 100644
--- a/drivers/android/binder/freeze.rs
+++ b/drivers/android/binder/freeze.rs
@@ -106,13 +106,22 @@ impl DeliverToRead for FreezeMessage {
return Ok(true);
}
if freeze.is_clearing {
- _removed_listener = freeze_entry.remove_node();
+ kernel::warn_on!(freeze.num_cleared_duplicates != 0);
+ if freeze.num_pending_duplicates > 0 {
+ // The primary freeze listener was deleted, so convert a pending duplicate back
+ // into the primary one.
+ freeze.num_pending_duplicates -= 1;
+ freeze.is_pending = true;
+ freeze.is_clearing = true;
+ } else {
+ _removed_listener = freeze_entry.remove_node();
+ }
drop(node_refs);
writer.write_code(BR_CLEAR_FREEZE_NOTIFICATION_DONE)?;
writer.write_payload(&self.cookie.0)?;
Ok(true)
} else {
- let is_frozen = freeze.node.owner.inner.lock().is_frozen;
+ let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
if freeze.last_is_frozen == Some(is_frozen) {
return Ok(true);
}
@@ -245,8 +254,9 @@ impl Process {
);
return Err(EINVAL);
}
- if freeze.is_clearing {
- // Immediately send another FreezeMessage for BR_CLEAR_FREEZE_NOTIFICATION_DONE.
+ let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
+ if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
+ // Immediately send another FreezeMessage.
clear_msg = Some(FreezeMessage::init(alloc, cookie));
}
freeze.is_pending = false;
@@ -321,7 +331,7 @@ impl Process {
KVVec::with_capacity(8, GFP_KERNEL).unwrap_or_else(|_err| KVVec::new());
let mut inner = self.lock_with_nodes();
- let mut curr = inner.nodes.cursor_front();
+ let mut curr = inner.nodes.cursor_front_mut();
while let Some(cursor) = curr {
let (key, node) = cursor.current();
let key = *key;
@@ -335,7 +345,7 @@ impl Process {
// Find the node we were looking at and try again. If the set of nodes was changed,
// then just proceed to the next node. This is ok because we don't guarantee the
// inclusion of nodes that are added or removed in parallel with this operation.
- curr = inner.nodes.cursor_lower_bound(&key);
+ curr = inner.nodes.cursor_lower_bound_mut(&key);
continue;
}
diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
index ade895ef791e..08d362deaf61 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -687,7 +687,7 @@ impl Node {
);
}
if inner.freeze_list.is_empty() {
- _unused_capacity = mem::replace(&mut inner.freeze_list, KVVec::new());
+ _unused_capacity = mem::take(&mut inner.freeze_list);
}
}
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index f13a747e784c..e5237e9ec552 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -72,6 +72,33 @@ impl Mapping {
const PROC_DEFER_FLUSH: u8 = 1;
const PROC_DEFER_RELEASE: u8 = 2;
+#[derive(Copy, Clone)]
+pub(crate) enum IsFrozen {
+ Yes,
+ No,
+ InProgress,
+}
+
+impl IsFrozen {
+ /// Whether incoming transactions should be rejected due to freeze.
+ pub(crate) fn is_frozen(self) -> bool {
+ match self {
+ IsFrozen::Yes => true,
+ IsFrozen::No => false,
+ IsFrozen::InProgress => true,
+ }
+ }
+
+ /// Whether freeze notifications consider this process frozen.
+ pub(crate) fn is_fully_frozen(self) -> bool {
+ match self {
+ IsFrozen::Yes => true,
+ IsFrozen::No => false,
+ IsFrozen::InProgress => false,
+ }
+ }
+}
+
/// The fields of `Process` protected by the spinlock.
pub(crate) struct ProcessInner {
is_manager: bool,
@@ -98,7 +125,7 @@ pub(crate) struct ProcessInner {
/// are woken up.
outstanding_txns: u32,
/// Process is frozen and unable to service binder transactions.
- pub(crate) is_frozen: bool,
+ pub(crate) is_frozen: IsFrozen,
/// Process received sync transactions since last frozen.
pub(crate) sync_recv: bool,
/// Process received async transactions since last frozen.
@@ -124,7 +151,7 @@ impl ProcessInner {
started_thread_count: 0,
defer_work: 0,
outstanding_txns: 0,
- is_frozen: false,
+ is_frozen: IsFrozen::No,
sync_recv: false,
async_recv: false,
binderfs_file: None,
@@ -596,7 +623,7 @@ impl Process {
" ref {}: desc {} {}node {debug_id} s {strong} w {weak}",
r.debug_id,
r.handle,
- if dead { "dead " } else { "" },
+ if dead { "dead " } else { "" }
);
}
}
@@ -1260,7 +1287,7 @@ impl Process {
let is_manager = {
let mut inner = self.inner.lock();
inner.is_dead = true;
- inner.is_frozen = false;
+ inner.is_frozen = IsFrozen::No;
inner.sync_recv = false;
inner.async_recv = false;
inner.is_manager
@@ -1293,7 +1320,7 @@ impl Process {
{
while let Some(node) = {
let mut lock = self.inner.lock();
- lock.nodes.cursor_front().map(|c| c.remove_current().1)
+ lock.nodes.cursor_front_mut().map(|c| c.remove_current().1)
} {
node.to_key_value().1.release();
}
@@ -1346,10 +1373,6 @@ impl Process {
.alloc
.take_for_each(|offset, size, debug_id, odata| {
let ptr = offset + address;
- pr_warn!(
- "{}: removing orphan mapping {offset}:{size}\n",
- self.pid_in_current_ns()
- );
let mut alloc =
Allocation::new(self.clone(), debug_id, offset, size, ptr, false);
if let Some(data) = odata {
@@ -1371,7 +1394,7 @@ impl Process {
return;
}
inner.outstanding_txns -= 1;
- inner.is_frozen && inner.outstanding_txns == 0
+ inner.is_frozen.is_frozen() && inner.outstanding_txns == 0
};
if wake {
@@ -1385,7 +1408,7 @@ impl Process {
let mut inner = self.inner.lock();
inner.sync_recv = false;
inner.async_recv = false;
- inner.is_frozen = false;
+ inner.is_frozen = IsFrozen::No;
drop(inner);
msgs.send_messages();
return Ok(());
@@ -1394,7 +1417,7 @@ impl Process {
let mut inner = self.inner.lock();
inner.sync_recv = false;
inner.async_recv = false;
- inner.is_frozen = true;
+ inner.is_frozen = IsFrozen::InProgress;
if info.timeout_ms > 0 {
let mut jiffies = kernel::time::msecs_to_jiffies(info.timeout_ms);
@@ -1408,7 +1431,7 @@ impl Process {
.wait_interruptible_timeout(&mut inner, jiffies)
{
CondVarTimeoutResult::Signal { .. } => {
- inner.is_frozen = false;
+ inner.is_frozen = IsFrozen::No;
return Err(ERESTARTSYS);
}
CondVarTimeoutResult::Woken { jiffies: remaining } => {
@@ -1422,17 +1445,18 @@ impl Process {
}
if inner.txns_pending_locked() {
- inner.is_frozen = false;
+ inner.is_frozen = IsFrozen::No;
Err(EAGAIN)
} else {
drop(inner);
match self.prepare_freeze_messages() {
Ok(batch) => {
+ self.inner.lock().is_frozen = IsFrozen::Yes;
batch.send_messages();
Ok(())
}
Err(kernel::alloc::AllocError) => {
- self.inner.lock().is_frozen = false;
+ self.inner.lock().is_frozen = IsFrozen::No;
Err(ENOMEM)
}
}
diff --git a/drivers/android/binder/range_alloc/tree.rs b/drivers/android/binder/range_alloc/tree.rs
index 7b1a248fcb02..838fdd2b47ea 100644
--- a/drivers/android/binder/range_alloc/tree.rs
+++ b/drivers/android/binder/range_alloc/tree.rs
@@ -207,7 +207,7 @@ impl<T> TreeRangeAllocator<T> {
}
pub(crate) fn reservation_abort(&mut self, offset: usize) -> Result<FreedRange> {
- let mut cursor = self.tree.cursor_lower_bound(&offset).ok_or_else(|| {
+ let mut cursor = self.tree.cursor_lower_bound_mut(&offset).ok_or_else(|| {
pr_warn!(
"EINVAL from range_alloc.reservation_abort - offset: {}",
offset
diff --git a/drivers/android/binder/stats.rs b/drivers/android/binder/stats.rs
index a83ec111d2cb..037002651941 100644
--- a/drivers/android/binder/stats.rs
+++ b/drivers/android/binder/stats.rs
@@ -61,7 +61,7 @@ impl BinderStats {
mod strings {
use core::str::from_utf8_unchecked;
- use kernel::str::CStr;
+ use kernel::str::{CStr, CStrExt as _};
extern "C" {
static binder_command_strings: [*const u8; super::BC_COUNT];
@@ -72,7 +72,7 @@ mod strings {
// SAFETY: Accessing `binder_command_strings` is always safe.
let c_str_ptr = unsafe { binder_command_strings[i] };
// SAFETY: The `binder_command_strings` array only contains nul-terminated strings.
- let bytes = unsafe { CStr::from_char_ptr(c_str_ptr) }.as_bytes();
+ let bytes = unsafe { CStr::from_char_ptr(c_str_ptr) }.to_bytes();
// SAFETY: The `binder_command_strings` array only contains strings with ascii-chars.
unsafe { from_utf8_unchecked(bytes) }
}
@@ -81,7 +81,7 @@ mod strings {
// SAFETY: Accessing `binder_return_strings` is always safe.
let c_str_ptr = unsafe { binder_return_strings[i] };
// SAFETY: The `binder_command_strings` array only contains nul-terminated strings.
- let bytes = unsafe { CStr::from_char_ptr(c_str_ptr) }.as_bytes();
+ let bytes = unsafe { CStr::from_char_ptr(c_str_ptr) }.to_bytes();
// SAFETY: The `binder_command_strings` array only contains strings with ascii-chars.
unsafe { from_utf8_unchecked(bytes) }
}
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 02512175d622..4bd3c0e417eb 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -249,7 +249,7 @@ impl Transaction {
if oneway {
if let Some(target_node) = self.target_node.clone() {
- if process_inner.is_frozen {
+ if process_inner.is_frozen.is_frozen() {
process_inner.async_recv = true;
if self.flags & TF_UPDATE_TXN != 0 {
if let Some(t_outdated) =
@@ -270,7 +270,7 @@ impl Transaction {
}
}
- if process_inner.is_frozen {
+ if process_inner.is_frozen.is_frozen() {
return Err(BinderError::new_frozen_oneway());
} else {
return Ok(());
@@ -280,7 +280,7 @@ impl Transaction {
}
}
- if process_inner.is_frozen {
+ if process_inner.is_frozen.is_frozen() {
process_inner.sync_recv = true;
return Err(BinderError::new_frozen());
}
diff --git a/drivers/android/binder_netlink.c b/drivers/android/binder_netlink.c
index d05397a50ca6..81e8432b5904 100644
--- a/drivers/android/binder_netlink.c
+++ b/drivers/android/binder_netlink.c
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/binder.yaml */
/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#include <net/netlink.h>
#include <net/genetlink.h>
diff --git a/drivers/android/binder_netlink.h b/drivers/android/binder_netlink.h
index 882c7a6b537e..57399942a5e3 100644
--- a/drivers/android/binder_netlink.h
+++ b/drivers/android/binder_netlink.h
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/binder.yaml */
/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_BINDER_GEN_H
#define _LINUX_BINDER_GEN_H
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 2a210719c4ce..462217935558 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -3006,6 +3006,16 @@ int ata_dev_configure(struct ata_device *dev)
}
dev->n_sectors = ata_id_n_sectors(id);
+ if (ata_id_is_locked(id)) {
+ /*
+ * If Security locked, set capacity to zero to prevent
+ * any I/O, e.g. partition scanning, as any I/O to a
+ * locked drive will result in user visible errors.
+ */
+ ata_dev_info(dev,
+ "Security locked, setting capacity to zero\n");
+ dev->n_sectors = 0;
+ }
/* get current R/W Multiple count setting */
if ((dev->id[47] >> 8) == 0x80 && (dev->id[59] & 0x100)) {
@@ -4206,6 +4216,10 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = {
/* Apacer models with LPM issues */
{ "Apacer AS340*", NULL, ATA_QUIRK_NOLPM },
+ /* Silicon Motion models with LPM issues */
+ { "MD619HXCLDE3TC", "TCVAID", ATA_QUIRK_NOLPM },
+ { "MD619GXCLDE3TC", "TCV35D", ATA_QUIRK_NOLPM },
+
/* These specific Samsung models/firmware-revs do not handle LPM well */
{ "SAMSUNG MZMPC128HBFU-000MV", "CXM14M1Q", ATA_QUIRK_NOLPM },
{ "SAMSUNG SSD PM830 mSATA *", "CXM13D1Q", ATA_QUIRK_NOLPM },
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index b43a3196e2be..434774e71fe6 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -992,6 +992,13 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc)
return;
}
+ if (ata_id_is_locked(dev->id)) {
+ /* Security locked */
+ /* LOGICAL UNIT ACCESS NOT AUTHORIZED */
+ ata_scsi_set_sense(dev, cmd, DATA_PROTECT, 0x74, 0x71);
+ return;
+ }
+
if (!(qc->flags & ATA_QCFLAG_RTF_FILLED)) {
ata_dev_dbg(dev,
"Missing result TF: reporting aborted command\n");
@@ -4894,8 +4901,10 @@ void ata_scsi_dev_rescan(struct work_struct *work)
spin_unlock_irqrestore(ap->lock, flags);
if (do_resume) {
ret = scsi_resume_device(sdev);
- if (ret == -EWOULDBLOCK)
+ if (ret == -EWOULDBLOCK) {
+ scsi_device_put(sdev);
goto unlock_scan;
+ }
dev->flags &= ~ATA_DFLAG_RESUMING;
}
ret = scsi_rescan_device(sdev);
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 1e2a2c33cdc8..785b6e371abf 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -3191,7 +3191,8 @@ void ata_sff_port_init(struct ata_port *ap)
int __init ata_sff_init(void)
{
- ata_sff_wq = alloc_workqueue("ata_sff", WQ_MEM_RECLAIM, WQ_MAX_ACTIVE);
+ ata_sff_wq = alloc_workqueue("ata_sff", WQ_MEM_RECLAIM | WQ_PERCPU,
+ WQ_MAX_ACTIVE);
if (!ata_sff_wq)
return -ENOMEM;
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index 042f6ad1f7c6..fc762dcc61bf 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -75,6 +75,7 @@
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <scsi/scsi_host.h>
#include <linux/libata.h>
@@ -632,9 +633,9 @@ static void it821x_display_disk(struct ata_port *ap, int n, u8 *buf)
cbl = "";
if (mode)
- snprintf(mbuf, 8, "%5s%d", mtype, mode - 1);
+ snprintf(mbuf, sizeof(mbuf), "%5s%d", mtype, mode - 1);
else
- strcpy(mbuf, "PIO");
+ strscpy(mbuf, "PIO");
if (buf[52] == 4)
ata_port_info(ap, "%d: %-6s %-8s %s %s\n",
n, mbuf, types[buf[52]], id, cbl);
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index cf3810933a27..caefcd8c4b3c 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -344,6 +344,7 @@ static const struct pcmcia_device_id pcmcia_devices[] = {
PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79),
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591),
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728),
+ PCMCIA_DEVICE_PROD_ID2("PCMCIA ATA/ATAPI Adapter", 0x888d7b73),
PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1),
PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883),
PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d),
diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c
index 4fea1149e003..f62e38571440 100644
--- a/drivers/atm/fore200e.c
+++ b/drivers/atm/fore200e.c
@@ -1374,7 +1374,9 @@ fore200e_open(struct atm_vcc *vcc)
vcc->dev_data = NULL;
+ mutex_lock(&fore200e->rate_mtx);
fore200e->available_cell_rate += vcc->qos.txtp.max_pcr;
+ mutex_unlock(&fore200e->rate_mtx);
kfree(fore200e_vcc);
return -EINVAL;
diff --git a/drivers/auxdisplay/line-display.c b/drivers/auxdisplay/line-display.c
index 8590a4cd21e0..4e22373fcc1a 100644
--- a/drivers/auxdisplay/line-display.c
+++ b/drivers/auxdisplay/line-display.c
@@ -6,20 +6,23 @@
* Author: Paul Burton <paul.burton@mips.com>
*
* Copyright (C) 2021 Glider bv
+ * Copyright (C) 2025 Jean-François Lessard
*/
#ifndef CONFIG_PANEL_BOOT_MESSAGE
#include <generated/utsrelease.h>
#endif
-#include <linux/container_of.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/idr.h>
#include <linux/jiffies.h>
#include <linux/kstrtox.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/timer.h>
@@ -32,6 +35,87 @@
#define DEFAULT_SCROLL_RATE (HZ / 2)
/**
+ * struct linedisp_attachment - Holds the device to linedisp mapping
+ * @list: List entry for the linedisp_attachments list
+ * @device: Pointer to the device where linedisp attributes are added
+ * @linedisp: Pointer to the linedisp mapped to the device
+ * @direct: true for directly attached device using linedisp_attach(),
+ * false for child registered device using linedisp_register()
+ */
+struct linedisp_attachment {
+ struct list_head list;
+ struct device *device;
+ struct linedisp *linedisp;
+ bool direct;
+};
+
+static LIST_HEAD(linedisp_attachments);
+static DEFINE_SPINLOCK(linedisp_attachments_lock);
+
+static int create_attachment(struct device *dev, struct linedisp *linedisp, bool direct)
+{
+ struct linedisp_attachment *attachment;
+
+ attachment = kzalloc(sizeof(*attachment), GFP_KERNEL);
+ if (!attachment)
+ return -ENOMEM;
+
+ attachment->device = dev;
+ attachment->linedisp = linedisp;
+ attachment->direct = direct;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+ list_add(&attachment->list, &linedisp_attachments);
+
+ return 0;
+}
+
+static struct linedisp *delete_attachment(struct device *dev, bool direct)
+{
+ struct linedisp_attachment *attachment;
+ struct linedisp *linedisp;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
+ if (attachment->device == dev &&
+ attachment->direct == direct)
+ break;
+ }
+
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
+ return NULL;
+
+ linedisp = attachment->linedisp;
+ list_del(&attachment->list);
+ kfree(attachment);
+
+ return linedisp;
+}
+
+static struct linedisp *to_linedisp(struct device *dev)
+{
+ struct linedisp_attachment *attachment;
+
+ guard(spinlock)(&linedisp_attachments_lock);
+
+ list_for_each_entry(attachment, &linedisp_attachments, list) {
+ if (attachment->device == dev)
+ break;
+ }
+
+ if (list_entry_is_head(attachment, &linedisp_attachments, list))
+ return NULL;
+
+ return attachment->linedisp;
+}
+
+static inline bool should_scroll(struct linedisp *linedisp)
+{
+ return linedisp->message_len > linedisp->num_chars && linedisp->scroll_rate;
+}
+
+/**
* linedisp_scroll() - scroll the display by a character
* @t: really a pointer to the private data structure
*
@@ -62,8 +146,7 @@ static void linedisp_scroll(struct timer_list *t)
linedisp->scroll_pos %= linedisp->message_len;
/* rearm the timer */
- if (linedisp->message_len > num_chars && linedisp->scroll_rate)
- mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
+ mod_timer(&linedisp->timer, jiffies + linedisp->scroll_rate);
}
/**
@@ -113,8 +196,16 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
linedisp->message_len = count;
linedisp->scroll_pos = 0;
- /* update the display */
- linedisp_scroll(&linedisp->timer);
+ if (should_scroll(linedisp)) {
+ /* display scrolling message */
+ linedisp_scroll(&linedisp->timer);
+ } else {
+ /* display static message */
+ memset(linedisp->buf, ' ', linedisp->num_chars);
+ memcpy(linedisp->buf, linedisp->message,
+ umin(linedisp->num_chars, linedisp->message_len));
+ linedisp->ops->update(linedisp);
+ }
return 0;
}
@@ -133,7 +224,7 @@ static int linedisp_display(struct linedisp *linedisp, const char *msg,
static ssize_t message_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
return sysfs_emit(buf, "%s\n", linedisp->message);
}
@@ -152,7 +243,7 @@ static ssize_t message_show(struct device *dev, struct device_attribute *attr,
static ssize_t message_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
int err;
err = linedisp_display(linedisp, buf, count);
@@ -161,10 +252,20 @@ static ssize_t message_store(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR_RW(message);
+static ssize_t num_chars_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct linedisp *linedisp = to_linedisp(dev);
+
+ return sysfs_emit(buf, "%u\n", linedisp->num_chars);
+}
+
+static DEVICE_ATTR_RO(num_chars);
+
static ssize_t scroll_step_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
return sysfs_emit(buf, "%u\n", jiffies_to_msecs(linedisp->scroll_rate));
}
@@ -173,7 +274,7 @@ static ssize_t scroll_step_ms_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
unsigned int ms;
int err;
@@ -181,12 +282,12 @@ static ssize_t scroll_step_ms_store(struct device *dev,
if (err)
return err;
+ timer_delete_sync(&linedisp->timer);
+
linedisp->scroll_rate = msecs_to_jiffies(ms);
- if (linedisp->message && linedisp->message_len > linedisp->num_chars) {
- timer_delete_sync(&linedisp->timer);
- if (linedisp->scroll_rate)
- linedisp_scroll(&linedisp->timer);
- }
+
+ if (should_scroll(linedisp))
+ linedisp_scroll(&linedisp->timer);
return count;
}
@@ -195,7 +296,7 @@ static DEVICE_ATTR_RW(scroll_step_ms);
static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
struct linedisp_map *map = linedisp->map;
memcpy(buf, &map->map, map->size);
@@ -205,7 +306,7 @@ static ssize_t map_seg_show(struct device *dev, struct device_attribute *attr, c
static ssize_t map_seg_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
struct linedisp_map *map = linedisp->map;
if (count != map->size)
@@ -223,6 +324,7 @@ static DEVICE_ATTR(map_seg14, 0644, map_seg_show, map_seg_store);
static struct attribute *linedisp_attrs[] = {
&dev_attr_message.attr,
+ &dev_attr_num_chars.attr,
&dev_attr_scroll_step_ms.attr,
&dev_attr_map_seg7.attr,
&dev_attr_map_seg14.attr,
@@ -232,7 +334,7 @@ static struct attribute *linedisp_attrs[] = {
static umode_t linedisp_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
struct linedisp_map *map = linedisp->map;
umode_t mode = attr->mode;
@@ -263,7 +365,7 @@ static DEFINE_IDA(linedisp_id);
static void linedisp_release(struct device *dev)
{
- struct linedisp *linedisp = container_of(dev, struct linedisp, dev);
+ struct linedisp *linedisp = to_linedisp(dev);
kfree(linedisp->map);
kfree(linedisp->message);
@@ -321,12 +423,101 @@ static int linedisp_init_map(struct linedisp *linedisp)
#endif
/**
+ * linedisp_attach - attach a character line display
+ * @linedisp: pointer to character line display structure
+ * @dev: pointer of the device to attach to
+ * @num_chars: the number of characters that can be displayed
+ * @ops: character line display operations
+ *
+ * Directly attach the line-display sysfs attributes to the passed device.
+ * The caller is responsible for calling linedisp_detach() to release resources
+ * after use.
+ *
+ * Return: zero on success, else a negative error code.
+ */
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
+ unsigned int num_chars, const struct linedisp_ops *ops)
+{
+ int err;
+
+ memset(linedisp, 0, sizeof(*linedisp));
+ linedisp->ops = ops;
+ linedisp->num_chars = num_chars;
+ linedisp->scroll_rate = DEFAULT_SCROLL_RATE;
+
+ linedisp->buf = kzalloc(linedisp->num_chars, GFP_KERNEL);
+ if (!linedisp->buf)
+ return -ENOMEM;
+
+ /* initialise a character mapping, if required */
+ err = linedisp_init_map(linedisp);
+ if (err)
+ goto out_free_buf;
+
+ /* initialise a timer for scrolling the message */
+ timer_setup(&linedisp->timer, linedisp_scroll, 0);
+
+ err = create_attachment(dev, linedisp, true);
+ if (err)
+ goto out_del_timer;
+
+ /* display a default message */
+ err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
+ if (err)
+ goto out_del_attach;
+
+ /* add attribute groups to target device */
+ err = device_add_groups(dev, linedisp_groups);
+ if (err)
+ goto out_del_attach;
+
+ return 0;
+
+out_del_attach:
+ delete_attachment(dev, true);
+out_del_timer:
+ timer_delete_sync(&linedisp->timer);
+out_free_buf:
+ kfree(linedisp->buf);
+ return err;
+}
+EXPORT_SYMBOL_NS_GPL(linedisp_attach, "LINEDISP");
+
+/**
+ * linedisp_detach - detach a character line display
+ * @dev: pointer of the device to detach from, that was previously
+ * attached with linedisp_attach()
+ */
+void linedisp_detach(struct device *dev)
+{
+ struct linedisp *linedisp;
+
+ linedisp = delete_attachment(dev, true);
+ if (!linedisp)
+ return;
+
+ timer_delete_sync(&linedisp->timer);
+
+ device_remove_groups(dev, linedisp_groups);
+
+ kfree(linedisp->map);
+ kfree(linedisp->message);
+ kfree(linedisp->buf);
+}
+EXPORT_SYMBOL_NS_GPL(linedisp_detach, "LINEDISP");
+
+/**
* linedisp_register - register a character line display
* @linedisp: pointer to character line display structure
* @parent: parent device
* @num_chars: the number of characters that can be displayed
* @ops: character line display operations
*
+ * Register the line-display sysfs attributes to a new device named
+ * "linedisp.N" added to the passed parent device.
+ * The caller is responsible for calling linedisp_unregister() to release
+ * resources after use.
+ *
* Return: zero on success, else a negative error code.
*/
int linedisp_register(struct linedisp *linedisp, struct device *parent,
@@ -362,19 +553,23 @@ int linedisp_register(struct linedisp *linedisp, struct device *parent,
/* initialise a timer for scrolling the message */
timer_setup(&linedisp->timer, linedisp_scroll, 0);
- err = device_add(&linedisp->dev);
+ err = create_attachment(&linedisp->dev, linedisp, false);
if (err)
goto out_del_timer;
/* display a default message */
err = linedisp_display(linedisp, LINEDISP_INIT_TEXT, -1);
if (err)
- goto out_del_dev;
+ goto out_del_attach;
+
+ err = device_add(&linedisp->dev);
+ if (err)
+ goto out_del_attach;
return 0;
-out_del_dev:
- device_del(&linedisp->dev);
+out_del_attach:
+ delete_attachment(&linedisp->dev, false);
out_del_timer:
timer_delete_sync(&linedisp->timer);
out_put_device:
@@ -391,6 +586,7 @@ EXPORT_SYMBOL_NS_GPL(linedisp_register, "LINEDISP");
void linedisp_unregister(struct linedisp *linedisp)
{
device_del(&linedisp->dev);
+ delete_attachment(&linedisp->dev, false);
timer_delete_sync(&linedisp->timer);
put_device(&linedisp->dev);
}
diff --git a/drivers/auxdisplay/line-display.h b/drivers/auxdisplay/line-display.h
index 4348d7a2f69a..36853b639711 100644
--- a/drivers/auxdisplay/line-display.h
+++ b/drivers/auxdisplay/line-display.h
@@ -6,6 +6,7 @@
* Author: Paul Burton <paul.burton@mips.com>
*
* Copyright (C) 2021 Glider bv
+ * Copyright (C) 2025 Jean-François Lessard
*/
#ifndef _LINEDISP_H
@@ -81,6 +82,9 @@ struct linedisp {
unsigned int id;
};
+int linedisp_attach(struct linedisp *linedisp, struct device *dev,
+ unsigned int num_chars, const struct linedisp_ops *ops);
+void linedisp_detach(struct device *dev);
int linedisp_register(struct linedisp *linedisp, struct device *parent,
unsigned int num_chars, const struct linedisp_ops *ops);
void linedisp_unregister(struct linedisp *linedisp);
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 1037169abb45..e1eff05bea4a 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -292,7 +292,7 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
* frequency (by keeping the initial capacity_freq_ref value).
*/
cpu_clk = of_clk_get(cpu_node, 0);
- if (!PTR_ERR_OR_ZERO(cpu_clk)) {
+ if (!IS_ERR_OR_NULL(cpu_clk)) {
per_cpu(capacity_freq_ref, cpu) =
clk_get_rate(cpu_clk) / HZ_PER_KHZ;
clk_put(cpu_clk);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 3c533dab8fa5..f69dc9c85954 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1784,7 +1784,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data)
return 0;
if (fw_devlink_sync_state == FW_DEVLINK_SYNC_STATE_STRICT) {
- dev_warn(sup, "sync_state() pending due to %s\n",
+ dev_info(sup, "sync_state() pending due to %s\n",
dev_name(link->consumer));
return 0;
}
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c
index 37faf6156d7c..55bdc7f5e59d 100644
--- a/drivers/base/devcoredump.c
+++ b/drivers/base/devcoredump.c
@@ -23,50 +23,46 @@ struct devcd_entry {
void *data;
size_t datalen;
/*
- * Here, mutex is required to serialize the calls to del_wk work between
- * user/kernel space which happens when devcd is added with device_add()
- * and that sends uevent to user space. User space reads the uevents,
- * and calls to devcd_data_write() which try to modify the work which is
- * not even initialized/queued from devcoredump.
+ * There are 2 races for which mutex is required.
*
+ * The first race is between device creation and userspace writing to
+ * schedule immediately destruction.
*
+ * This race is handled by arming the timer before device creation, but
+ * when device creation fails the timer still exists.
*
- * cpu0(X) cpu1(Y)
+ * To solve this, hold the mutex during device_add(), and set
+ * init_completed on success before releasing the mutex.
*
- * dev_coredump() uevent sent to user space
- * device_add() ======================> user space process Y reads the
- * uevents writes to devcd fd
- * which results into writes to
+ * That way the timer will never fire until device_add() is called,
+ * it will do nothing if init_completed is not set. The timer is also
+ * cancelled in that case.
*
- * devcd_data_write()
- * mod_delayed_work()
- * try_to_grab_pending()
- * timer_delete()
- * debug_assert_init()
- * INIT_DELAYED_WORK()
- * schedule_delayed_work()
- *
- *
- * Also, mutex alone would not be enough to avoid scheduling of
- * del_wk work after it get flush from a call to devcd_free()
- * mentioned as below.
- *
- * disabled_store()
- * devcd_free()
- * mutex_lock() devcd_data_write()
- * flush_delayed_work()
- * mutex_unlock()
- * mutex_lock()
- * mod_delayed_work()
- * mutex_unlock()
- * So, delete_work flag is required.
+ * The second race involves multiple parallel invocations of devcd_free(),
+ * add a deleted flag so only 1 can call the destructor.
*/
struct mutex mutex;
- bool delete_work;
+ bool init_completed, deleted;
struct module *owner;
ssize_t (*read)(char *buffer, loff_t offset, size_t count,
void *data, size_t datalen);
void (*free)(void *data);
+ /*
+ * If nothing interferes and device_add() was returns success,
+ * del_wk will destroy the device after the timer fires.
+ *
+ * Multiple userspace processes can interfere in the working of the timer:
+ * - Writing to the coredump will reschedule the timer to run immediately,
+ * if still armed.
+ *
+ * This is handled by using "if (cancel_delayed_work()) {
+ * schedule_delayed_work() }", to prevent re-arming after having
+ * been previously fired.
+ * - Writing to /sys/class/devcoredump/disabled will destroy the
+ * coredump synchronously.
+ * This is handled by using disable_delayed_work_sync(), and then
+ * checking if deleted flag is set with &devcd->mutex held.
+ */
struct delayed_work del_wk;
struct device *failing_dev;
};
@@ -95,14 +91,27 @@ static void devcd_dev_release(struct device *dev)
kfree(devcd);
}
+static void __devcd_del(struct devcd_entry *devcd)
+{
+ devcd->deleted = true;
+ device_del(&devcd->devcd_dev);
+ put_device(&devcd->devcd_dev);
+}
+
static void devcd_del(struct work_struct *wk)
{
struct devcd_entry *devcd;
+ bool init_completed;
devcd = container_of(wk, struct devcd_entry, del_wk.work);
- device_del(&devcd->devcd_dev);
- put_device(&devcd->devcd_dev);
+ /* devcd->mutex serializes against dev_coredumpm_timeout */
+ mutex_lock(&devcd->mutex);
+ init_completed = devcd->init_completed;
+ mutex_unlock(&devcd->mutex);
+
+ if (init_completed)
+ __devcd_del(devcd);
}
static ssize_t devcd_data_read(struct file *filp, struct kobject *kobj,
@@ -122,12 +131,12 @@ static ssize_t devcd_data_write(struct file *filp, struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct devcd_entry *devcd = dev_to_devcd(dev);
- mutex_lock(&devcd->mutex);
- if (!devcd->delete_work) {
- devcd->delete_work = true;
- mod_delayed_work(system_wq, &devcd->del_wk, 0);
- }
- mutex_unlock(&devcd->mutex);
+ /*
+ * Although it's tempting to use mod_delayed work here,
+ * that will cause a reschedule if the timer already fired.
+ */
+ if (cancel_delayed_work(&devcd->del_wk))
+ schedule_delayed_work(&devcd->del_wk, 0);
return count;
}
@@ -151,11 +160,21 @@ static int devcd_free(struct device *dev, void *data)
{
struct devcd_entry *devcd = dev_to_devcd(dev);
+ /*
+ * To prevent a race with devcd_data_write(), disable work and
+ * complete manually instead.
+ *
+ * We cannot rely on the return value of
+ * disable_delayed_work_sync() here, because it might be in the
+ * middle of a cancel_delayed_work + schedule_delayed_work pair.
+ *
+ * devcd->mutex here guards against multiple parallel invocations
+ * of devcd_free().
+ */
+ disable_delayed_work_sync(&devcd->del_wk);
mutex_lock(&devcd->mutex);
- if (!devcd->delete_work)
- devcd->delete_work = true;
-
- flush_delayed_work(&devcd->del_wk);
+ if (!devcd->deleted)
+ __devcd_del(devcd);
mutex_unlock(&devcd->mutex);
return 0;
}
@@ -179,12 +198,10 @@ static ssize_t disabled_show(const struct class *class, const struct class_attri
* put_device() <- last reference
* error = fn(dev, data) devcd_dev_release()
* devcd_free(dev, data) kfree(devcd)
- * mutex_lock(&devcd->mutex);
*
*
* In the above diagram, it looks like disabled_store() would be racing with parallelly
- * running devcd_del() and result in memory abort while acquiring devcd->mutex which
- * is called after kfree of devcd memory after dropping its last reference with
+ * running devcd_del() and result in memory abort after dropping its last reference with
* put_device(). However, this will not happens as fn(dev, data) runs
* with its own reference to device via klist_node so it is not its last reference.
* so, above situation would not occur.
@@ -374,7 +391,7 @@ void dev_coredumpm_timeout(struct device *dev, struct module *owner,
devcd->read = read;
devcd->free = free;
devcd->failing_dev = get_device(dev);
- devcd->delete_work = false;
+ devcd->deleted = false;
mutex_init(&devcd->mutex);
device_initialize(&devcd->devcd_dev);
@@ -383,8 +400,14 @@ void dev_coredumpm_timeout(struct device *dev, struct module *owner,
atomic_inc_return(&devcd_count));
devcd->devcd_dev.class = &devcd_class;
- mutex_lock(&devcd->mutex);
dev_set_uevent_suppress(&devcd->devcd_dev, true);
+
+ /* devcd->mutex prevents devcd_del() completing until init finishes */
+ mutex_lock(&devcd->mutex);
+ devcd->init_completed = false;
+ INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
+ schedule_delayed_work(&devcd->del_wk, timeout);
+
if (device_add(&devcd->devcd_dev))
goto put_device;
@@ -401,13 +424,20 @@ void dev_coredumpm_timeout(struct device *dev, struct module *owner,
dev_set_uevent_suppress(&devcd->devcd_dev, false);
kobject_uevent(&devcd->devcd_dev.kobj, KOBJ_ADD);
- INIT_DELAYED_WORK(&devcd->del_wk, devcd_del);
- schedule_delayed_work(&devcd->del_wk, timeout);
+
+ /*
+ * Safe to run devcd_del() now that we are done with devcd_dev.
+ * Alternatively we could have taken a ref on devcd_dev before
+ * dropping the lock.
+ */
+ devcd->init_completed = true;
mutex_unlock(&devcd->mutex);
return;
put_device:
- put_device(&devcd->devcd_dev);
mutex_unlock(&devcd->mutex);
+ cancel_delayed_work_sync(&devcd->del_wk);
+ put_device(&devcd->devcd_dev);
+
put_module:
module_put(owner);
free:
diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index 9d4e46ad8352..2f576ecf1832 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -180,7 +180,7 @@ static int dev_mkdir(const char *name, umode_t mode)
if (IS_ERR(dentry))
return PTR_ERR(dentry);
- dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode);
+ dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL);
if (!IS_ERR(dentry))
/* mark as kernel-created inode */
d_inode(dentry)->i_private = &thread;
@@ -231,7 +231,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
return PTR_ERR(dentry);
err = vfs_mknod(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode,
- dev->devt);
+ dev->devt, NULL);
if (!err) {
struct iattr newattrs;
@@ -261,7 +261,7 @@ static int dev_rmdir(const char *name)
return PTR_ERR(dentry);
if (d_inode(dentry)->i_private == &thread)
err = vfs_rmdir(&nop_mnt_idmap, d_inode(parent.dentry),
- dentry);
+ dentry, NULL);
else
err = -EPERM;
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index 6942c62fa59d..bee3050a20d9 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -829,8 +829,6 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
size_t offset, u32 opt_flags)
{
struct firmware *fw = NULL;
- struct cred *kern_cred = NULL;
- const struct cred *old_cred;
bool nondirect = false;
int ret;
@@ -871,45 +869,38 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
* called by a driver when serving an unrelated request from userland, we use
* the kernel credentials to read the file.
*/
- kern_cred = prepare_kernel_cred(&init_task);
- if (!kern_cred) {
- ret = -ENOMEM;
- goto out;
- }
- old_cred = override_creds(kern_cred);
+ scoped_with_kernel_creds() {
+ ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
- ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
-
- /* Only full reads can support decompression, platform, and sysfs. */
- if (!(opt_flags & FW_OPT_PARTIAL))
- nondirect = true;
+ /* Only full reads can support decompression, platform, and sysfs. */
+ if (!(opt_flags & FW_OPT_PARTIAL))
+ nondirect = true;
#ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD
- if (ret == -ENOENT && nondirect)
- ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
- fw_decompress_zstd);
+ if (ret == -ENOENT && nondirect)
+ ret = fw_get_filesystem_firmware(device, fw->priv, ".zst",
+ fw_decompress_zstd);
#endif
#ifdef CONFIG_FW_LOADER_COMPRESS_XZ
- if (ret == -ENOENT && nondirect)
- ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
- fw_decompress_xz);
+ if (ret == -ENOENT && nondirect)
+ ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
+ fw_decompress_xz);
#endif
- if (ret == -ENOENT && nondirect)
- ret = firmware_fallback_platform(fw->priv);
+ if (ret == -ENOENT && nondirect)
+ ret = firmware_fallback_platform(fw->priv);
- if (ret) {
- if (!(opt_flags & FW_OPT_NO_WARN))
- dev_warn(device,
- "Direct firmware load for %s failed with error %d\n",
- name, ret);
- if (nondirect)
- ret = firmware_fallback_sysfs(fw, name, device,
- opt_flags, ret);
- } else
- ret = assign_fw(fw, device);
-
- revert_creds(old_cred);
- put_cred(kern_cred);
+ if (ret) {
+ if (!(opt_flags & FW_OPT_NO_WARN))
+ dev_warn(device,
+ "Direct firmware load for %s failed with error %d\n",
+ name, ret);
+ if (nondirect)
+ ret = firmware_fallback_sysfs(fw, name, device,
+ opt_flags, ret);
+ } else {
+ ret = assign_fw(fw, device);
+ }
+ }
out:
if (ret < 0) {
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 6d84a02cfa5d..fc43f2703ae0 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -226,7 +226,6 @@ static int memory_block_online(struct memory_block *mem)
unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr);
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
unsigned long nr_vmemmap_pages = 0;
- struct memory_notify arg;
struct zone *zone;
int ret;
@@ -246,19 +245,9 @@ static int memory_block_online(struct memory_block *mem)
if (mem->altmap)
nr_vmemmap_pages = mem->altmap->free;
- arg.altmap_start_pfn = start_pfn;
- arg.altmap_nr_pages = nr_vmemmap_pages;
- arg.start_pfn = start_pfn + nr_vmemmap_pages;
- arg.nr_pages = nr_pages - nr_vmemmap_pages;
mem_hotplug_begin();
- ret = memory_notify(MEM_PREPARE_ONLINE, &arg);
- ret = notifier_to_errno(ret);
- if (ret)
- goto out_notifier;
-
if (nr_vmemmap_pages) {
- ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages,
- zone, mem->altmap->inaccessible);
+ ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone);
if (ret)
goto out;
}
@@ -280,11 +269,7 @@ static int memory_block_online(struct memory_block *mem)
nr_vmemmap_pages);
mem->zone = zone;
- mem_hotplug_done();
- return ret;
out:
- memory_notify(MEM_FINISH_OFFLINE, &arg);
-out_notifier:
mem_hotplug_done();
return ret;
}
@@ -297,7 +282,6 @@ static int memory_block_offline(struct memory_block *mem)
unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr);
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
unsigned long nr_vmemmap_pages = 0;
- struct memory_notify arg;
int ret;
if (!mem->zone)
@@ -329,11 +313,6 @@ static int memory_block_offline(struct memory_block *mem)
mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages);
mem->zone = NULL;
- arg.altmap_start_pfn = start_pfn;
- arg.altmap_nr_pages = nr_vmemmap_pages;
- arg.start_pfn = start_pfn + nr_vmemmap_pages;
- arg.nr_pages = nr_pages - nr_vmemmap_pages;
- memory_notify(MEM_FINISH_OFFLINE, &arg);
out:
mem_hotplug_done();
return ret;
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 09450349cf32..b45d41b018ca 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -150,25 +150,37 @@ devm_platform_ioremap_resource_byname(struct platform_device *pdev,
EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname);
#endif /* CONFIG_HAS_IOMEM */
+static const struct cpumask *get_irq_affinity(struct platform_device *dev,
+ unsigned int num)
+{
+ const struct cpumask *mask = NULL;
+#ifndef CONFIG_SPARC
+ struct fwnode_handle *fwnode = dev_fwnode(&dev->dev);
+
+ if (is_of_node(fwnode))
+ mask = of_irq_get_affinity(to_of_node(fwnode), num);
+ else if (is_acpi_device_node(fwnode))
+ mask = acpi_irq_get_affinity(ACPI_HANDLE_FWNODE(fwnode), num);
+#endif
+
+ return mask ?: cpu_possible_mask;
+}
+
/**
- * platform_get_irq_optional - get an optional IRQ for a device
- * @dev: platform device
- * @num: IRQ number index
+ * platform_get_irq_affinity - get an optional IRQ and its affinity for a device
+ * @dev: platform device
+ * @num: interrupt number index
+ * @affinity: optional cpumask pointer to get the affinity of a per-cpu interrupt
*
- * Gets an IRQ for a platform device. Device drivers should check the return
- * value for errors so as to not pass a negative integer value to the
- * request_irq() APIs. This is the same as platform_get_irq(), except that it
- * does not print an error message if an IRQ can not be obtained.
- *
- * For example::
- *
- * int irq = platform_get_irq_optional(pdev, 0);
- * if (irq < 0)
- * return irq;
+ * Gets an interupt for a platform device. Device drivers should check the
+ * return value for errors so as to not pass a negative integer value to
+ * the request_irq() APIs. Optional affinity information is provided in the
+ * affinity pointer if available, and NULL otherwise.
*
- * Return: non-zero IRQ number on success, negative error number on failure.
+ * Return: non-zero interrupt number on success, negative error number on failure.
*/
-int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
+int platform_get_irq_affinity(struct platform_device *dev, unsigned int num,
+ const struct cpumask **affinity)
{
int ret;
#ifdef CONFIG_SPARC
@@ -236,8 +248,37 @@ out_not_found:
out:
if (WARN(!ret, "0 is an invalid IRQ number\n"))
return -EINVAL;
+
+ if (ret > 0 && affinity)
+ *affinity = get_irq_affinity(dev, num);
+
return ret;
}
+EXPORT_SYMBOL_GPL(platform_get_irq_affinity);
+
+/**
+ * platform_get_irq_optional - get an optional interrupt for a device
+ * @dev: platform device
+ * @num: interrupt number index
+ *
+ * Gets an interrupt for a platform device. Device drivers should check the
+ * return value for errors so as to not pass a negative integer value to
+ * the request_irq() APIs. This is the same as platform_get_irq(), except
+ * that it does not print an error message if an interrupt can not be
+ * obtained.
+ *
+ * For example::
+ *
+ * int irq = platform_get_irq_optional(pdev, 0);
+ * if (irq < 0)
+ * return irq;
+ *
+ * Return: non-zero interrupt number on success, negative error number on failure.
+ */
+int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
+{
+ return platform_get_irq_affinity(dev, num, NULL);
+}
EXPORT_SYMBOL_GPL(platform_get_irq_optional);
/**
diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c
index 6502720bb564..af99bbcf281c 100644
--- a/drivers/base/power/generic_ops.c
+++ b/drivers/base/power/generic_ops.c
@@ -8,6 +8,13 @@
#include <linux/pm_runtime.h>
#include <linux/export.h>
+#define CALL_PM_OP(dev, op) \
+({ \
+ struct device *_dev = (dev); \
+ const struct dev_pm_ops *pm = _dev->driver ? _dev->driver->pm : NULL; \
+ pm && pm->op ? pm->op(_dev) : 0; \
+})
+
#ifdef CONFIG_PM
/**
* pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems.
@@ -19,12 +26,7 @@
*/
int pm_generic_runtime_suspend(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- int ret;
-
- ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0;
-
- return ret;
+ return CALL_PM_OP(dev, runtime_suspend);
}
EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
@@ -38,12 +40,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend);
*/
int pm_generic_runtime_resume(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
- int ret;
-
- ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0;
-
- return ret;
+ return CALL_PM_OP(dev, runtime_resume);
}
EXPORT_SYMBOL_GPL(pm_generic_runtime_resume);
#endif /* CONFIG_PM */
@@ -72,9 +69,7 @@ int pm_generic_prepare(struct device *dev)
*/
int pm_generic_suspend_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->suspend_noirq ? pm->suspend_noirq(dev) : 0;
+ return CALL_PM_OP(dev, suspend_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
@@ -84,9 +79,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
*/
int pm_generic_suspend_late(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->suspend_late ? pm->suspend_late(dev) : 0;
+ return CALL_PM_OP(dev, suspend_late);
}
EXPORT_SYMBOL_GPL(pm_generic_suspend_late);
@@ -96,9 +89,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_late);
*/
int pm_generic_suspend(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->suspend ? pm->suspend(dev) : 0;
+ return CALL_PM_OP(dev, suspend);
}
EXPORT_SYMBOL_GPL(pm_generic_suspend);
@@ -108,9 +99,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend);
*/
int pm_generic_freeze_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->freeze_noirq ? pm->freeze_noirq(dev) : 0;
+ return CALL_PM_OP(dev, freeze_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
@@ -120,9 +109,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
*/
int pm_generic_freeze(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->freeze ? pm->freeze(dev) : 0;
+ return CALL_PM_OP(dev, freeze);
}
EXPORT_SYMBOL_GPL(pm_generic_freeze);
@@ -132,9 +119,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze);
*/
int pm_generic_poweroff_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->poweroff_noirq ? pm->poweroff_noirq(dev) : 0;
+ return CALL_PM_OP(dev, poweroff_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
@@ -144,9 +129,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
*/
int pm_generic_poweroff_late(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->poweroff_late ? pm->poweroff_late(dev) : 0;
+ return CALL_PM_OP(dev, poweroff_late);
}
EXPORT_SYMBOL_GPL(pm_generic_poweroff_late);
@@ -156,9 +139,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_late);
*/
int pm_generic_poweroff(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->poweroff ? pm->poweroff(dev) : 0;
+ return CALL_PM_OP(dev, poweroff);
}
EXPORT_SYMBOL_GPL(pm_generic_poweroff);
@@ -168,9 +149,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff);
*/
int pm_generic_thaw_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->thaw_noirq ? pm->thaw_noirq(dev) : 0;
+ return CALL_PM_OP(dev, thaw_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
@@ -180,9 +159,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
*/
int pm_generic_thaw(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->thaw ? pm->thaw(dev) : 0;
+ return CALL_PM_OP(dev, thaw);
}
EXPORT_SYMBOL_GPL(pm_generic_thaw);
@@ -192,9 +169,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
*/
int pm_generic_resume_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->resume_noirq ? pm->resume_noirq(dev) : 0;
+ return CALL_PM_OP(dev, resume_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
@@ -204,9 +179,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
*/
int pm_generic_resume_early(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->resume_early ? pm->resume_early(dev) : 0;
+ return CALL_PM_OP(dev, resume_early);
}
EXPORT_SYMBOL_GPL(pm_generic_resume_early);
@@ -216,9 +189,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_early);
*/
int pm_generic_resume(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->resume ? pm->resume(dev) : 0;
+ return CALL_PM_OP(dev, resume);
}
EXPORT_SYMBOL_GPL(pm_generic_resume);
@@ -228,9 +199,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume);
*/
int pm_generic_restore_noirq(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->restore_noirq ? pm->restore_noirq(dev) : 0;
+ return CALL_PM_OP(dev, restore_noirq);
}
EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
@@ -240,9 +209,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
*/
int pm_generic_restore_early(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->restore_early ? pm->restore_early(dev) : 0;
+ return CALL_PM_OP(dev, restore_early);
}
EXPORT_SYMBOL_GPL(pm_generic_restore_early);
@@ -252,9 +219,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_early);
*/
int pm_generic_restore(struct device *dev)
{
- const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
-
- return pm && pm->restore ? pm->restore(dev) : 0;
+ return CALL_PM_OP(dev, restore);
}
EXPORT_SYMBOL_GPL(pm_generic_restore);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index e83503bdc1fd..97a8b4fcf471 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -34,6 +34,7 @@
#include <linux/cpufreq.h>
#include <linux/devfreq.h>
#include <linux/timer.h>
+#include <linux/nmi.h>
#include "../base.h"
#include "power.h"
@@ -95,6 +96,8 @@ static const char *pm_verb(int event)
return "restore";
case PM_EVENT_RECOVER:
return "recover";
+ case PM_EVENT_POWEROFF:
+ return "poweroff";
default:
return "(unknown PM event)";
}
@@ -367,6 +370,7 @@ static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state)
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
return ops->freeze;
+ case PM_EVENT_POWEROFF:
case PM_EVENT_HIBERNATE:
return ops->poweroff;
case PM_EVENT_THAW:
@@ -401,6 +405,7 @@ static pm_callback_t pm_late_early_op(const struct dev_pm_ops *ops,
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
return ops->freeze_late;
+ case PM_EVENT_POWEROFF:
case PM_EVENT_HIBERNATE:
return ops->poweroff_late;
case PM_EVENT_THAW:
@@ -435,6 +440,7 @@ static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t stat
case PM_EVENT_FREEZE:
case PM_EVENT_QUIESCE:
return ops->freeze_noirq;
+ case PM_EVENT_POWEROFF:
case PM_EVENT_HIBERNATE:
return ops->poweroff_noirq;
case PM_EVENT_THAW:
@@ -515,6 +521,11 @@ struct dpm_watchdog {
#define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \
struct dpm_watchdog wd
+static bool __read_mostly dpm_watchdog_all_cpu_backtrace;
+module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644);
+MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace,
+ "Backtrace all CPUs on DPM watchdog timeout");
+
/**
* dpm_watchdog_handler - Driver suspend / resume watchdog handler.
* @t: The timer that PM watchdog depends on.
@@ -530,8 +541,12 @@ static void dpm_watchdog_handler(struct timer_list *t)
unsigned int time_left;
if (wd->fatal) {
+ unsigned int this_cpu = smp_processor_id();
+
dev_emerg(wd->dev, "**** DPM device timeout ****\n");
show_stack(wd->tsk, NULL, KERN_EMERG);
+ if (dpm_watchdog_all_cpu_backtrace)
+ trigger_allbutcpu_cpu_backtrace(this_cpu);
panic("%s %s: unrecoverable failure\n",
dev_driver_string(wd->dev), dev_name(wd->dev));
}
@@ -888,12 +903,15 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy
TRACE_DEVICE(dev);
TRACE_RESUME(0);
- if (dev->power.syscore || dev->power.direct_complete)
+ if (dev->power.direct_complete)
goto Out;
if (!dev->power.is_late_suspended)
goto Out;
+ if (dev->power.syscore)
+ goto Skip;
+
if (!dpm_wait_for_superior(dev, async))
goto Out;
@@ -926,11 +944,11 @@ Run:
Skip:
dev->power.is_late_suspended = false;
+ pm_runtime_enable(dev);
Out:
TRACE_RESUME(error);
- pm_runtime_enable(dev);
complete_all(&dev->power.completion);
if (error) {
@@ -1615,12 +1633,6 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
TRACE_DEVICE(dev);
TRACE_SUSPEND(0);
- /*
- * Disable runtime PM for the device without checking if there is a
- * pending resume request for it.
- */
- __pm_runtime_disable(dev, false);
-
dpm_wait_for_subordinate(dev, async);
if (READ_ONCE(async_error))
@@ -1631,9 +1643,18 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy
goto Complete;
}
- if (dev->power.syscore || dev->power.direct_complete)
+ if (dev->power.direct_complete)
goto Complete;
+ /*
+ * Disable runtime PM for the device without checking if there is a
+ * pending resume request for it.
+ */
+ __pm_runtime_disable(dev, false);
+
+ if (dev->power.syscore)
+ goto Skip;
+
if (dev->pm_domain) {
info = "late power domain ";
callback = pm_late_early_op(&dev->pm_domain->ops, state);
@@ -1664,6 +1685,7 @@ Run:
WRITE_ONCE(async_error, error);
dpm_save_failed_dev(dev_name(dev));
pm_dev_err(dev, state, async ? " async late" : " late", error);
+ pm_runtime_enable(dev);
goto Complete;
}
dpm_propagate_wakeup_to_parent(dev);
@@ -2126,6 +2148,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
device_lock(dev);
dev->power.wakeup_path = false;
+ dev->power.out_band_wakeup = false;
if (dev->power.no_pm_callbacks)
goto unlock;
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 1b11a3cd4acc..62707738caa4 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -90,7 +90,7 @@ static void update_pm_runtime_accounting(struct device *dev)
/*
* Because ktime_get_mono_fast_ns() is not monotonic during
* timekeeping updates, ensure that 'now' is after the last saved
- * timesptamp.
+ * timestamp.
*/
if (now < last)
return;
@@ -217,7 +217,7 @@ static int dev_memalloc_noio(struct device *dev, void *data)
* resume/suspend callback of any one of its ancestors(or the
* block device itself), the deadlock may be triggered inside the
* memory allocation since it might not complete until the block
- * device becomes active and the involed page I/O finishes. The
+ * device becomes active and the involved page I/O finishes. The
* situation is pointed out first by Alan Stern. Network device
* are involved in iSCSI kind of situation.
*
@@ -1210,7 +1210,7 @@ EXPORT_SYMBOL_GPL(__pm_runtime_resume);
*
* Otherwise, if its runtime PM status is %RPM_ACTIVE and (1) @ign_usage_count
* is set, or (2) @dev is not ignoring children and its active child count is
- * nonero, or (3) the runtime PM usage counter of @dev is not zero, increment
+ * nonzero, or (3) the runtime PM usage counter of @dev is not zero, increment
* the usage counter of @dev and return 1.
*
* Otherwise, return 0 without changing the usage counter.
@@ -1664,9 +1664,12 @@ EXPORT_SYMBOL_GPL(devm_pm_runtime_get_noresume);
* pm_runtime_forbid - Block runtime PM of a device.
* @dev: Device to handle.
*
- * Increase the device's usage count and clear its power.runtime_auto flag,
- * so that it cannot be suspended at run time until pm_runtime_allow() is called
- * for it.
+ * Resume @dev if already suspended and block runtime suspend of @dev in such
+ * a way that it can be unblocked via the /sys/devices/.../power/control
+ * interface, or otherwise by calling pm_runtime_allow().
+ *
+ * Calling this function many times in a row has the same effect as calling it
+ * once.
*/
void pm_runtime_forbid(struct device *dev)
{
@@ -1687,7 +1690,13 @@ EXPORT_SYMBOL_GPL(pm_runtime_forbid);
* pm_runtime_allow - Unblock runtime PM of a device.
* @dev: Device to handle.
*
- * Decrease the device's usage count and set its power.runtime_auto flag.
+ * Unblock runtime suspend of @dev after it has been blocked by
+ * pm_runtime_forbid() (for instance, if it has been blocked via the
+ * /sys/devices/.../power/control interface), check if @dev can be
+ * suspended and suspend it in that case.
+ *
+ * Calling this function many times in a row has the same effect as calling it
+ * once.
*/
void pm_runtime_allow(struct device *dev)
{
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
index cd6e559648b2..d8da7195bb00 100644
--- a/drivers/base/power/trace.c
+++ b/drivers/base/power/trace.c
@@ -238,10 +238,8 @@ int show_trace_dev_match(char *buf, size_t size)
unsigned int hash = hash_string(DEVSEED, dev_name(dev),
DEVHASH);
if (hash == value) {
- int len = snprintf(buf, size, "%s\n",
+ int len = scnprintf(buf, size, "%s\n",
dev_driver_string(dev));
- if (len > size)
- len = size;
buf += len;
ret += len;
size -= len;
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index d1283ff1080b..1e1a0e7eeac5 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -189,17 +189,16 @@ static void wakeup_source_remove(struct wakeup_source *ws)
if (WARN_ON(!ws))
return;
+ /*
+ * After shutting down the timer, wakeup_source_activate() will warn if
+ * the given wakeup source is passed to it.
+ */
+ timer_shutdown_sync(&ws->timer);
+
raw_spin_lock_irqsave(&events_lock, flags);
list_del_rcu(&ws->entry);
raw_spin_unlock_irqrestore(&events_lock, flags);
synchronize_srcu(&wakeup_srcu);
-
- timer_delete_sync(&ws->timer);
- /*
- * Clear timer.function to make wakeup_source_not_registered() treat
- * this wakeup source as not registered.
- */
- ws->timer.function = NULL;
}
/**
@@ -506,14 +505,14 @@ int device_set_wakeup_enable(struct device *dev, bool enable)
EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
/**
- * wakeup_source_not_registered - validate the given wakeup source.
+ * wakeup_source_not_usable - validate the given wakeup source.
* @ws: Wakeup source to be validated.
*/
-static bool wakeup_source_not_registered(struct wakeup_source *ws)
+static bool wakeup_source_not_usable(struct wakeup_source *ws)
{
/*
- * Use timer struct to check if the given source is initialized
- * by wakeup_source_add.
+ * Use the timer struct to check if the given wakeup source has been
+ * initialized by wakeup_source_add() and it is not going away.
*/
return ws->timer.function != pm_wakeup_timer_fn;
}
@@ -558,8 +557,7 @@ static void wakeup_source_activate(struct wakeup_source *ws)
{
unsigned int cec;
- if (WARN_ONCE(wakeup_source_not_registered(ws),
- "unregistered wakeup source\n"))
+ if (WARN_ONCE(wakeup_source_not_usable(ws), "unusable wakeup source\n"))
return;
ws->active = true;
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 6f31240ee4a9..1477329410ec 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -186,6 +186,7 @@ struct regcache_ops {
enum regcache_type type;
int (*init)(struct regmap *map);
int (*exit)(struct regmap *map);
+ int (*populate)(struct regmap *map);
#ifdef CONFIG_DEBUG_FS
void (*debugfs_init)(struct regmap *map);
#endif
@@ -288,6 +289,7 @@ enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config);
+extern struct regcache_ops regcache_flat_sparse_ops;
extern struct regcache_ops regcache_rbtree_ops;
extern struct regcache_ops regcache_maple_ops;
extern struct regcache_ops regcache_flat_ops;
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index f36d3618b67c..53cc59c84e2f 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -6,7 +6,11 @@
//
// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
#include <linux/device.h>
+#include <linux/limits.h>
+#include <linux/overflow.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
@@ -18,46 +22,92 @@ static inline unsigned int regcache_flat_get_index(const struct regmap *map,
return regcache_get_index_by_order(map, reg);
}
+struct regcache_flat_data {
+ unsigned long *valid;
+ unsigned int data[];
+};
+
static int regcache_flat_init(struct regmap *map)
{
- int i;
- unsigned int *cache;
+ unsigned int cache_size;
+ struct regcache_flat_data *cache;
if (!map || map->reg_stride_order < 0 || !map->max_register_is_set)
return -EINVAL;
- map->cache = kcalloc(regcache_flat_get_index(map, map->max_register)
- + 1, sizeof(unsigned int), map->alloc_flags);
- if (!map->cache)
+ cache_size = regcache_flat_get_index(map, map->max_register) + 1;
+ cache = kzalloc(struct_size(cache, data, cache_size), map->alloc_flags);
+ if (!cache)
return -ENOMEM;
- cache = map->cache;
+ cache->valid = bitmap_zalloc(cache_size, map->alloc_flags);
+ if (!cache->valid)
+ goto err_free;
+
+ map->cache = cache;
+
+ return 0;
+
+err_free:
+ kfree(cache);
+ return -ENOMEM;
+}
+
+static int regcache_flat_exit(struct regmap *map)
+{
+ struct regcache_flat_data *cache = map->cache;
+
+ if (cache)
+ bitmap_free(cache->valid);
+
+ kfree(cache);
+ map->cache = NULL;
+
+ return 0;
+}
+
+static int regcache_flat_populate(struct regmap *map)
+{
+ struct regcache_flat_data *cache = map->cache;
+ unsigned int i;
for (i = 0; i < map->num_reg_defaults; i++) {
unsigned int reg = map->reg_defaults[i].reg;
unsigned int index = regcache_flat_get_index(map, reg);
- cache[index] = map->reg_defaults[i].def;
+ cache->data[index] = map->reg_defaults[i].def;
+ __set_bit(index, cache->valid);
}
return 0;
}
-static int regcache_flat_exit(struct regmap *map)
+static int regcache_flat_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
{
- kfree(map->cache);
- map->cache = NULL;
+ struct regcache_flat_data *cache = map->cache;
+ unsigned int index = regcache_flat_get_index(map, reg);
+
+ /* legacy behavior: ignore validity, but warn the user */
+ if (unlikely(!test_bit(index, cache->valid)))
+ dev_warn_once(map->dev,
+ "using zero-initialized flat cache, this may cause unexpected behavior");
+
+ *value = cache->data[index];
return 0;
}
-static int regcache_flat_read(struct regmap *map,
- unsigned int reg, unsigned int *value)
+static int regcache_flat_sparse_read(struct regmap *map,
+ unsigned int reg, unsigned int *value)
{
- unsigned int *cache = map->cache;
+ struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
- *value = cache[index];
+ if (unlikely(!test_bit(index, cache->valid)))
+ return -ENOENT;
+
+ *value = cache->data[index];
return 0;
}
@@ -65,10 +115,23 @@ static int regcache_flat_read(struct regmap *map,
static int regcache_flat_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
- unsigned int *cache = map->cache;
+ struct regcache_flat_data *cache = map->cache;
unsigned int index = regcache_flat_get_index(map, reg);
- cache[index] = value;
+ cache->data[index] = value;
+ __set_bit(index, cache->valid);
+
+ return 0;
+}
+
+static int regcache_flat_drop(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ struct regcache_flat_data *cache = map->cache;
+ unsigned int bitmap_min = regcache_flat_get_index(map, min);
+ unsigned int bitmap_max = regcache_flat_get_index(map, max);
+
+ bitmap_clear(cache->valid, bitmap_min, bitmap_max + 1 - bitmap_min);
return 0;
}
@@ -78,6 +141,18 @@ struct regcache_ops regcache_flat_ops = {
.name = "flat",
.init = regcache_flat_init,
.exit = regcache_flat_exit,
+ .populate = regcache_flat_populate,
.read = regcache_flat_read,
.write = regcache_flat_write,
};
+
+struct regcache_ops regcache_flat_sparse_ops = {
+ .type = REGCACHE_FLAT_S,
+ .name = "flat-sparse",
+ .init = regcache_flat_init,
+ .exit = regcache_flat_exit,
+ .populate = regcache_flat_populate,
+ .read = regcache_flat_sparse_read,
+ .write = regcache_flat_write,
+ .drop = regcache_flat_drop,
+};
diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c
index 2319c30283a6..ca1c72b68f31 100644
--- a/drivers/base/regmap/regcache-maple.c
+++ b/drivers/base/regmap/regcache-maple.c
@@ -289,6 +289,23 @@ out:
return ret;
}
+static int regcache_maple_init(struct regmap *map)
+{
+ struct maple_tree *mt;
+
+ mt = kmalloc(sizeof(*mt), map->alloc_flags);
+ if (!mt)
+ return -ENOMEM;
+ map->cache = mt;
+
+ mt_init(mt);
+
+ if (!mt_external_lock(mt) && map->lock_key)
+ lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1);
+
+ return 0;
+}
+
static int regcache_maple_exit(struct regmap *map)
{
struct maple_tree *mt = map->cache;
@@ -340,26 +357,12 @@ static int regcache_maple_insert_block(struct regmap *map, int first,
return ret;
}
-static int regcache_maple_init(struct regmap *map)
+static int regcache_maple_populate(struct regmap *map)
{
- struct maple_tree *mt;
int i;
int ret;
int range_start;
- mt = kmalloc(sizeof(*mt), map->alloc_flags);
- if (!mt)
- return -ENOMEM;
- map->cache = mt;
-
- mt_init(mt);
-
- if (!mt_external_lock(mt) && map->lock_key)
- lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1);
-
- if (!map->num_reg_defaults)
- return 0;
-
range_start = 0;
/* Scan for ranges of contiguous registers */
@@ -369,23 +372,14 @@ static int regcache_maple_init(struct regmap *map)
ret = regcache_maple_insert_block(map, range_start,
i - 1);
if (ret != 0)
- goto err;
+ return ret;
range_start = i;
}
}
/* Add the last block */
- ret = regcache_maple_insert_block(map, range_start,
- map->num_reg_defaults - 1);
- if (ret != 0)
- goto err;
-
- return 0;
-
-err:
- regcache_maple_exit(map);
- return ret;
+ return regcache_maple_insert_block(map, range_start, map->num_reg_defaults - 1);
}
struct regcache_ops regcache_maple_ops = {
@@ -393,6 +387,7 @@ struct regcache_ops regcache_maple_ops = {
.name = "maple",
.init = regcache_maple_init,
.exit = regcache_maple_exit,
+ .populate = regcache_maple_populate,
.read = regcache_maple_read,
.write = regcache_maple_write,
.drop = regcache_maple_drop,
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index a9d17f316e55..3344b82c3799 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -184,8 +184,6 @@ static void rbtree_debugfs_init(struct regmap *map)
static int regcache_rbtree_init(struct regmap *map)
{
struct regcache_rbtree_ctx *rbtree_ctx;
- int i;
- int ret;
map->cache = kmalloc(sizeof *rbtree_ctx, map->alloc_flags);
if (!map->cache)
@@ -195,19 +193,7 @@ static int regcache_rbtree_init(struct regmap *map)
rbtree_ctx->root = RB_ROOT;
rbtree_ctx->cached_rbnode = NULL;
- for (i = 0; i < map->num_reg_defaults; i++) {
- ret = regcache_rbtree_write(map,
- map->reg_defaults[i].reg,
- map->reg_defaults[i].def);
- if (ret)
- goto err;
- }
-
return 0;
-
-err:
- regcache_rbtree_exit(map);
- return ret;
}
static int regcache_rbtree_exit(struct regmap *map)
@@ -239,6 +225,22 @@ static int regcache_rbtree_exit(struct regmap *map)
return 0;
}
+static int regcache_rbtree_populate(struct regmap *map)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < map->num_reg_defaults; i++) {
+ ret = regcache_rbtree_write(map,
+ map->reg_defaults[i].reg,
+ map->reg_defaults[i].def);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int regcache_rbtree_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
@@ -546,6 +548,7 @@ struct regcache_ops regcache_rbtree_ops = {
.name = "rbtree",
.init = regcache_rbtree_init,
.exit = regcache_rbtree_exit,
+ .populate = regcache_rbtree_populate,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = rbtree_debugfs_init,
#endif
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index c7650fa434ad..319c342bf5a0 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -16,6 +16,7 @@
#include "internal.h"
static const struct regcache_ops *cache_types[] = {
+ &regcache_flat_sparse_ops,
&regcache_rbtree_ops,
&regcache_maple_ops,
&regcache_flat_ops,
@@ -221,8 +222,24 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
if (ret)
goto err_free;
}
+
+ if (map->num_reg_defaults && map->cache_ops->populate) {
+ dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name);
+ map->lock(map->lock_arg);
+ ret = map->cache_ops->populate(map);
+ map->unlock(map->lock_arg);
+ if (ret)
+ goto err_exit;
+ }
return 0;
+err_exit:
+ if (map->cache_ops->exit) {
+ dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name);
+ map->lock(map->lock_arg);
+ ret = map->cache_ops->exit(map);
+ map->unlock(map->lock_arg);
+ }
err_free:
kfree(map->reg_defaults);
if (map->cache_free)
diff --git a/drivers/base/regmap/regmap-i3c.c b/drivers/base/regmap/regmap-i3c.c
index b5300b7c477e..6a0f6c826980 100644
--- a/drivers/base/regmap/regmap-i3c.c
+++ b/drivers/base/regmap/regmap-i3c.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+#include <linux/array_size.h>
#include <linux/regmap.h>
#include <linux/i3c/device.h>
#include <linux/i3c/master.h>
@@ -18,7 +19,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count)
},
};
- return i3c_device_do_priv_xfers(i3c, xfers, 1);
+ return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers));
}
static int regmap_i3c_read(void *context,
@@ -37,7 +38,7 @@ static int regmap_i3c_read(void *context,
xfers[1].len = val_size;
xfers[1].data.in = val;
- return i3c_device_do_priv_xfers(i3c, xfers, 2);
+ return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers));
}
static const struct regmap_bus regmap_i3c = {
diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c
index 95c5bf2a78ee..f6fc5ed016da 100644
--- a/drivers/base/regmap/regmap-kunit.c
+++ b/drivers/base/regmap/regmap-kunit.c
@@ -54,6 +54,8 @@ static const char *regcache_type_name(enum regcache_type type)
return "none";
case REGCACHE_FLAT:
return "flat";
+ case REGCACHE_FLAT_S:
+ return "flat-sparse";
case REGCACHE_RBTREE:
return "rbtree";
case REGCACHE_MAPLE:
@@ -93,6 +95,8 @@ static const struct regmap_test_param regcache_types_list[] = {
{ .cache = REGCACHE_NONE, .fast_io = true },
{ .cache = REGCACHE_FLAT },
{ .cache = REGCACHE_FLAT, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S },
+ { .cache = REGCACHE_FLAT_S, .fast_io = true },
{ .cache = REGCACHE_RBTREE },
{ .cache = REGCACHE_RBTREE, .fast_io = true },
{ .cache = REGCACHE_MAPLE },
@@ -104,6 +108,8 @@ KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, param_to_desc);
static const struct regmap_test_param real_cache_types_only_list[] = {
{ .cache = REGCACHE_FLAT },
{ .cache = REGCACHE_FLAT, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S },
+ { .cache = REGCACHE_FLAT_S, .fast_io = true },
{ .cache = REGCACHE_RBTREE },
{ .cache = REGCACHE_RBTREE, .fast_io = true },
{ .cache = REGCACHE_MAPLE },
@@ -119,6 +125,12 @@ static const struct regmap_test_param real_cache_types_list[] = {
{ .cache = REGCACHE_FLAT, .from_reg = 0x2002 },
{ .cache = REGCACHE_FLAT, .from_reg = 0x2003 },
{ .cache = REGCACHE_FLAT, .from_reg = 0x2004 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 },
{ .cache = REGCACHE_RBTREE, .from_reg = 0 },
{ .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true },
{ .cache = REGCACHE_RBTREE, .from_reg = 0x2001 },
@@ -136,6 +148,12 @@ static const struct regmap_test_param real_cache_types_list[] = {
KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc);
static const struct regmap_test_param sparse_cache_types_list[] = {
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 },
+ { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 },
{ .cache = REGCACHE_RBTREE, .from_reg = 0 },
{ .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true },
{ .cache = REGCACHE_RBTREE, .from_reg = 0x2001 },
@@ -1597,6 +1615,8 @@ static const struct regmap_test_param raw_types_list[] = {
{ .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_BIG },
{ .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE },
{ .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG },
{ .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE },
{ .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG },
{ .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE },
@@ -1608,6 +1628,8 @@ KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, param_to_desc);
static const struct regmap_test_param raw_cache_types_list[] = {
{ .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE },
{ .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE },
+ { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG },
{ .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE },
{ .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG },
{ .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE },
diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c
index 86644bbd0710..6a61629f5f89 100644
--- a/drivers/base/regmap/regmap-sdw-mbq.c
+++ b/drivers/base/regmap/regmap-sdw-mbq.c
@@ -15,11 +15,13 @@
struct regmap_mbq_context {
struct device *dev;
+ struct sdw_slave *sdw;
+
+ bool (*readable_reg)(struct device *dev, unsigned int reg);
struct regmap_sdw_mbq_cfg cfg;
int val_size;
- bool (*readable_reg)(struct device *dev, unsigned int reg);
};
static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
@@ -46,7 +48,7 @@ static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned i
static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,
struct regmap_mbq_context *ctx)
{
- struct device *dev = &slave->dev;
+ struct device *dev = ctx->dev;
int val, ret = 0;
dev_dbg(dev, "Deferring transaction for 0x%x\n", reg);
@@ -96,8 +98,7 @@ static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave,
static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
{
struct regmap_mbq_context *ctx = context;
- struct device *dev = ctx->dev;
- struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct sdw_slave *slave = ctx->sdw;
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
@@ -156,8 +157,7 @@ static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave,
static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
{
struct regmap_mbq_context *ctx = context;
- struct device *dev = ctx->dev;
- struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct sdw_slave *slave = ctx->sdw;
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
@@ -208,6 +208,7 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
static struct regmap_mbq_context *
regmap_sdw_mbq_gen_context(struct device *dev,
+ struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config)
{
@@ -218,6 +219,7 @@ regmap_sdw_mbq_gen_context(struct device *dev,
return ERR_PTR(-ENOMEM);
ctx->dev = dev;
+ ctx->sdw = sdw;
if (mbq_config)
ctx->cfg = *mbq_config;
@@ -228,7 +230,7 @@ regmap_sdw_mbq_gen_context(struct device *dev,
return ctx;
}
-struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
+struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
@@ -241,16 +243,16 @@ struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
if (ret)
return ERR_PTR(ret);
- ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
+ ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
- return __regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
+ return __regmap_init(dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);
-struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
+struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
@@ -263,11 +265,11 @@ struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
if (ret)
return ERR_PTR(ret);
- ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
+ ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
- return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
+ return __devm_regmap_init(dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);
diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c
index 54eb7d227cf4..e523fae73004 100644
--- a/drivers/base/regmap/regmap-slimbus.c
+++ b/drivers/base/regmap/regmap-slimbus.c
@@ -48,8 +48,7 @@ struct regmap *__regmap_init_slimbus(struct slim_device *slimbus,
if (IS_ERR(bus))
return ERR_CAST(bus);
- return __regmap_init(&slimbus->dev, bus, &slimbus->dev, config,
- lock_key, lock_name);
+ return __regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_slimbus);
@@ -63,8 +62,7 @@ struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
if (IS_ERR(bus))
return ERR_CAST(bus);
- return __devm_regmap_init(&slimbus->dev, bus, &slimbus, config,
- lock_key, lock_name);
+ return __devm_regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus);
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index be1e9e61a7bf..16a8301c25d6 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -535,14 +535,29 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
ref_array = prop->pointer;
ref = &ref_array[index];
- refnode = software_node_fwnode(ref->node);
+ /*
+ * A software node can reference other software nodes or firmware
+ * nodes (which are the abstraction layer sitting on top of them).
+ * This is done to ensure we can create references to static software
+ * nodes before they're registered with the firmware node framework.
+ * At the time the reference is being resolved, we expect the swnodes
+ * in question to already have been registered and to be backed by
+ * a firmware node. This is why we use the fwnode API below to read the
+ * relevant properties and bump the reference count.
+ */
+
+ if (ref->swnode)
+ refnode = software_node_fwnode(ref->swnode);
+ else if (ref->fwnode)
+ refnode = ref->fwnode;
+ else
+ return -EINVAL;
+
if (!refnode)
return -ENOENT;
if (nargs_prop) {
- error = property_entry_read_int_array(ref->node->properties,
- nargs_prop, sizeof(u32),
- &nargs_prop_val, 1);
+ error = fwnode_property_read_u32(refnode, nargs_prop, &nargs_prop_val);
if (error)
return error;
@@ -555,7 +570,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
if (!args)
return 0;
- args->fwnode = software_node_get(refnode);
+ args->fwnode = fwnode_handle_get(refnode);
args->nargs = nargs;
for (i = 0; i < nargs; i++)
@@ -635,7 +650,10 @@ software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode)
ref = prop->pointer;
- return software_node_get(software_node_fwnode(ref[0].node));
+ if (!ref->swnode)
+ return NULL;
+
+ return software_node_get(software_node_fwnode(ref->swnode));
}
static struct fwnode_handle *
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index 6ecfc821cf83..72f045e6ed51 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -294,6 +294,8 @@ static int bcma_register_devices(struct bcma_bus *bus)
int err;
list_for_each_entry(core, &bus->cores, list) {
+ struct device_node *np;
+
/* We support that core ourselves */
switch (core->id.id) {
case BCMA_CORE_4706_CHIPCOMMON:
@@ -311,6 +313,10 @@ static int bcma_register_devices(struct bcma_bus *bus)
if (bcma_is_core_needed_early(core->id.id))
continue;
+ np = core->dev.of_node;
+ if (np && !of_device_is_available(np))
+ continue;
+
/* Only first GMAC core on BCM4706 is connected and working */
if (core->id.id == BCMA_CORE_4706_MAC_GBIT &&
core->core_unit > 0)
diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c
index 85ca000a0564..d90fa3e7f4cf 100644
--- a/drivers/block/drbd/drbd_bitmap.c
+++ b/drivers/block/drbd/drbd_bitmap.c
@@ -1210,7 +1210,7 @@ static int bm_rw(struct drbd_device *device, const unsigned int flags, unsigned
return err;
}
-/**
+/*
* drbd_bm_read() - Read the whole bitmap from its on disk location.
* @device: DRBD device.
*/
@@ -1221,7 +1221,7 @@ int drbd_bm_read(struct drbd_device *device,
return bm_rw(device, BM_AIO_READ, 0);
}
-/**
+/*
* drbd_bm_write() - Write the whole bitmap to its on disk location.
* @device: DRBD device.
*
@@ -1233,7 +1233,7 @@ int drbd_bm_write(struct drbd_device *device,
return bm_rw(device, 0, 0);
}
-/**
+/*
* drbd_bm_write_all() - Write the whole bitmap to its on disk location.
* @device: DRBD device.
*
@@ -1255,7 +1255,7 @@ int drbd_bm_write_lazy(struct drbd_device *device, unsigned upper_idx) __must_ho
return bm_rw(device, BM_AIO_COPY_PAGES, upper_idx);
}
-/**
+/*
* drbd_bm_write_copy_pages() - Write the whole bitmap to its on disk location.
* @device: DRBD device.
*
@@ -1272,7 +1272,7 @@ int drbd_bm_write_copy_pages(struct drbd_device *device,
return bm_rw(device, BM_AIO_COPY_PAGES, 0);
}
-/**
+/*
* drbd_bm_write_hinted() - Write bitmap pages with "hint" marks, if they have changed.
* @device: DRBD device.
*/
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c
index caaf2781136d..3de919b6f0e1 100644
--- a/drivers/block/drbd/drbd_receiver.c
+++ b/drivers/block/drbd/drbd_receiver.c
@@ -450,7 +450,7 @@ static struct socket *drbd_try_connect(struct drbd_connection *connection)
* a free one dynamically.
*/
what = "bind before connect";
- err = sock->ops->bind(sock, (struct sockaddr *) &src_in6, my_addr_len);
+ err = sock->ops->bind(sock, (struct sockaddr_unsized *) &src_in6, my_addr_len);
if (err < 0)
goto out;
@@ -458,7 +458,7 @@ static struct socket *drbd_try_connect(struct drbd_connection *connection)
* stay C_WF_CONNECTION, don't go Disconnecting! */
disconnect_on_error = 0;
what = "connect";
- err = sock->ops->connect(sock, (struct sockaddr *) &peer_in6, peer_addr_len, 0);
+ err = sock->ops->connect(sock, (struct sockaddr_unsized *) &peer_in6, peer_addr_len, 0);
out:
if (err < 0) {
@@ -537,7 +537,7 @@ static int prepare_listen_socket(struct drbd_connection *connection, struct acce
drbd_setbufsize(s_listen, sndbuf_size, rcvbuf_size);
what = "bind before listen";
- err = s_listen->ops->bind(s_listen, (struct sockaddr *)&my_addr, my_addr_len);
+ err = s_listen->ops->bind(s_listen, (struct sockaddr_unsized *)&my_addr, my_addr_len);
if (err < 0)
goto out;
@@ -1736,13 +1736,13 @@ read_in_block(struct drbd_peer_device *peer_device, u64 id, sector_t sector,
page = peer_req->pages;
page_chain_for_each(page) {
unsigned len = min_t(int, ds, PAGE_SIZE);
- data = kmap(page);
+ data = kmap_local_page(page);
err = drbd_recv_all_warn(peer_device->connection, data, len);
if (drbd_insert_fault(device, DRBD_FAULT_RECEIVE)) {
drbd_err(device, "Fault injection: Corrupting data on receive\n");
data[0] = data[0] ^ (unsigned long)-1;
}
- kunmap(page);
+ kunmap_local(data);
if (err) {
drbd_free_peer_req(device, peer_req);
return NULL;
@@ -1777,7 +1777,7 @@ static int drbd_drain_block(struct drbd_peer_device *peer_device, int data_size)
page = drbd_alloc_pages(peer_device, 1, 1);
- data = kmap(page);
+ data = kmap_local_page(page);
while (data_size) {
unsigned int len = min_t(int, data_size, PAGE_SIZE);
@@ -1786,7 +1786,7 @@ static int drbd_drain_block(struct drbd_peer_device *peer_device, int data_size)
break;
data_size -= len;
}
- kunmap(page);
+ kunmap_local(data);
drbd_free_pages(peer_device->device, page);
return err;
}
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 5336c3c5ca36..c28786e0fe1c 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -329,7 +329,7 @@ static bool initialized;
* This default is used whenever the current disk size is unknown.
* [Now it is rather a minimum]
*/
-#define MAX_DISK_SIZE 4 /* 3984 */
+#define MAX_DISK_SIZE (PAGE_SIZE / 1024)
/*
* globals used by 'result()'
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 13ce229d450c..ebe751f39742 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1908,6 +1908,10 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
goto failed;
}
+ /* We can block in this context, so ignore REQ_NOWAIT. */
+ if (rq->cmd_flags & REQ_NOWAIT)
+ rq->cmd_flags &= ~REQ_NOWAIT;
+
if (cmd_blkcg_css)
kthread_associate_blkcg(cmd_blkcg_css);
if (cmd_memcg_css)
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 1188f32a5e5e..f6c33b21f69e 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -565,24 +565,27 @@ static int __sock_xmit(struct nbd_device *nbd, struct socket *sock, int send,
msg.msg_iter = *iter;
noreclaim_flag = memalloc_noreclaim_save();
- do {
- sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
- sock->sk->sk_use_task_frag = false;
- msg.msg_flags = msg_flags | MSG_NOSIGNAL;
-
- if (send)
- result = sock_sendmsg(sock, &msg);
- else
- result = sock_recvmsg(sock, &msg, msg.msg_flags);
-
- if (result <= 0) {
- if (result == 0)
- result = -EPIPE; /* short read */
- break;
- }
- if (sent)
- *sent += result;
- } while (msg_data_left(&msg));
+
+ scoped_with_kernel_creds() {
+ do {
+ sock->sk->sk_allocation = GFP_NOIO | __GFP_MEMALLOC;
+ sock->sk->sk_use_task_frag = false;
+ msg.msg_flags = msg_flags | MSG_NOSIGNAL;
+
+ if (send)
+ result = sock_sendmsg(sock, &msg);
+ else
+ result = sock_recvmsg(sock, &msg, msg.msg_flags);
+
+ if (result <= 0) {
+ if (result == 0)
+ result = -EPIPE; /* short read */
+ break;
+ }
+ if (sent)
+ *sent += result;
+ } while (msg_data_left(&msg));
+ }
memalloc_noreclaim_restore(noreclaim_flag);
@@ -1018,9 +1021,9 @@ static void recv_work(struct work_struct *work)
nbd_mark_nsock_dead(nbd, nsock, 1);
mutex_unlock(&nsock->tx_lock);
- nbd_config_put(nbd);
atomic_dec(&config->recv_threads);
wake_up(&config->recv_wq);
+ nbd_config_put(nbd);
kfree(args);
}
@@ -2235,12 +2238,13 @@ again:
ret = nbd_start_device(nbd);
out:
- mutex_unlock(&nbd->config_lock);
if (!ret) {
set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags);
refcount_inc(&nbd->config_refs);
nbd_connect_reply(info, nbd->index);
}
+ mutex_unlock(&nbd->config_lock);
+
nbd_config_put(nbd);
if (put_dev)
nbd_put(nbd);
diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c
index f982027e8c85..c7c0fb79a6bf 100644
--- a/drivers/block/null_blk/main.c
+++ b/drivers/block/null_blk/main.c
@@ -1129,26 +1129,28 @@ again:
return 0;
}
-static int copy_to_nullb(struct nullb *nullb, struct page *source,
- unsigned int off, sector_t sector, size_t n, bool is_fua)
+static blk_status_t copy_to_nullb(struct nullb *nullb, void *source,
+ loff_t pos, size_t n, bool is_fua)
{
size_t temp, count = 0;
- unsigned int offset;
struct nullb_page *t_page;
+ sector_t sector;
while (count < n) {
- temp = min_t(size_t, nullb->dev->blocksize, n - count);
+ temp = min3(nullb->dev->blocksize, n - count,
+ PAGE_SIZE - offset_in_page(pos));
+ sector = pos >> SECTOR_SHIFT;
if (null_cache_active(nullb) && !is_fua)
null_make_cache_space(nullb, PAGE_SIZE);
- offset = (sector & SECTOR_MASK) << SECTOR_SHIFT;
t_page = null_insert_page(nullb, sector,
!null_cache_active(nullb) || is_fua);
if (!t_page)
- return -ENOSPC;
+ return BLK_STS_NOSPC;
- memcpy_page(t_page->page, offset, source, off + count, temp);
+ memcpy_to_page(t_page->page, offset_in_page(pos),
+ source + count, temp);
__set_bit(sector & SECTOR_MASK, t_page->bitmap);
@@ -1156,41 +1158,34 @@ static int copy_to_nullb(struct nullb *nullb, struct page *source,
null_free_sector(nullb, sector, true);
count += temp;
- sector += temp >> SECTOR_SHIFT;
+ pos += temp;
}
- return 0;
+ return BLK_STS_OK;
}
-static int copy_from_nullb(struct nullb *nullb, struct page *dest,
- unsigned int off, sector_t sector, size_t n)
+static void copy_from_nullb(struct nullb *nullb, void *dest, loff_t pos,
+ size_t n)
{
size_t temp, count = 0;
- unsigned int offset;
struct nullb_page *t_page;
+ sector_t sector;
while (count < n) {
- temp = min_t(size_t, nullb->dev->blocksize, n - count);
+ temp = min3(nullb->dev->blocksize, n - count,
+ PAGE_SIZE - offset_in_page(pos));
+ sector = pos >> SECTOR_SHIFT;
- offset = (sector & SECTOR_MASK) << SECTOR_SHIFT;
t_page = null_lookup_page(nullb, sector, false,
!null_cache_active(nullb));
-
if (t_page)
- memcpy_page(dest, off + count, t_page->page, offset,
- temp);
+ memcpy_from_page(dest + count, t_page->page,
+ offset_in_page(pos), temp);
else
- memzero_page(dest, off + count, temp);
+ memset(dest + count, 0, temp);
count += temp;
- sector += temp >> SECTOR_SHIFT;
+ pos += temp;
}
- return 0;
-}
-
-static void nullb_fill_pattern(struct nullb *nullb, struct page *page,
- unsigned int len, unsigned int off)
-{
- memset_page(page, off, 0xff, len);
}
blk_status_t null_handle_discard(struct nullb_device *dev,
@@ -1234,34 +1229,39 @@ static blk_status_t null_handle_flush(struct nullb *nullb)
return errno_to_blk_status(err);
}
-static int null_transfer(struct nullb *nullb, struct page *page,
- unsigned int len, unsigned int off, bool is_write, sector_t sector,
+static blk_status_t null_transfer(struct nullb *nullb, struct page *page,
+ unsigned int len, unsigned int off, bool is_write, loff_t pos,
bool is_fua)
{
struct nullb_device *dev = nullb->dev;
+ blk_status_t err = BLK_STS_OK;
unsigned int valid_len = len;
- int err = 0;
+ void *p;
+ p = kmap_local_page(page) + off;
if (!is_write) {
- if (dev->zoned)
+ if (dev->zoned) {
valid_len = null_zone_valid_read_len(nullb,
- sector, len);
+ pos >> SECTOR_SHIFT, len);
+ if (valid_len && valid_len != len)
+ valid_len -= pos & (SECTOR_SIZE - 1);
+ }
if (valid_len) {
- err = copy_from_nullb(nullb, page, off,
- sector, valid_len);
+ copy_from_nullb(nullb, p, pos, valid_len);
off += valid_len;
len -= valid_len;
}
if (len)
- nullb_fill_pattern(nullb, page, len, off);
+ memset(p + valid_len, 0xff, len);
flush_dcache_page(page);
} else {
flush_dcache_page(page);
- err = copy_to_nullb(nullb, page, off, sector, len, is_fua);
+ err = copy_to_nullb(nullb, p, pos, len, is_fua);
}
+ kunmap_local(p);
return err;
}
@@ -1274,9 +1274,9 @@ static blk_status_t null_handle_data_transfer(struct nullb_cmd *cmd,
{
struct request *rq = blk_mq_rq_from_pdu(cmd);
struct nullb *nullb = cmd->nq->dev->nullb;
- int err = 0;
+ blk_status_t err = BLK_STS_OK;
unsigned int len;
- sector_t sector = blk_rq_pos(rq);
+ loff_t pos = blk_rq_pos(rq) << SECTOR_SHIFT;
unsigned int max_bytes = nr_sectors << SECTOR_SHIFT;
unsigned int transferred_bytes = 0;
struct req_iterator iter;
@@ -1288,18 +1288,18 @@ static blk_status_t null_handle_data_transfer(struct nullb_cmd *cmd,
if (transferred_bytes + len > max_bytes)
len = max_bytes - transferred_bytes;
err = null_transfer(nullb, bvec.bv_page, len, bvec.bv_offset,
- op_is_write(req_op(rq)), sector,
+ op_is_write(req_op(rq)), pos,
rq->cmd_flags & REQ_FUA);
if (err)
break;
- sector += len >> SECTOR_SHIFT;
+ pos += len;
transferred_bytes += len;
if (transferred_bytes >= max_bytes)
break;
}
spin_unlock_irq(&nullb->lock);
- return errno_to_blk_status(err);
+ return err;
}
static inline blk_status_t null_handle_throttled(struct nullb_cmd *cmd)
@@ -1949,6 +1949,7 @@ static int null_add_dev(struct nullb_device *dev)
.logical_block_size = dev->blocksize,
.physical_block_size = dev->blocksize,
.max_hw_sectors = dev->max_sectors,
+ .dma_alignment = 1,
};
struct nullb *nullb;
diff --git a/drivers/block/null_blk/null_blk.h b/drivers/block/null_blk/null_blk.h
index 7bb6128dbaaf..6c4c4bbe7dad 100644
--- a/drivers/block/null_blk/null_blk.h
+++ b/drivers/block/null_blk/null_blk.h
@@ -143,7 +143,8 @@ int null_init_zoned_dev(struct nullb_device *dev, struct queue_limits *lim);
int null_register_zoned_dev(struct nullb *nullb);
void null_free_zoned_dev(struct nullb_device *dev);
int null_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data);
+ unsigned int nr_zones,
+ struct blk_report_zones_args *args);
blk_status_t null_process_zoned_cmd(struct nullb_cmd *cmd, enum req_op op,
sector_t sector, sector_t nr_sectors);
size_t null_zone_valid_read_len(struct nullb *nullb,
diff --git a/drivers/block/null_blk/zoned.c b/drivers/block/null_blk/zoned.c
index 4e5728f45989..0ada35dc0989 100644
--- a/drivers/block/null_blk/zoned.c
+++ b/drivers/block/null_blk/zoned.c
@@ -191,7 +191,7 @@ void null_free_zoned_dev(struct nullb_device *dev)
}
int null_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones, struct blk_report_zones_args *args)
{
struct nullb *nullb = disk->private_data;
struct nullb_device *dev = nullb->dev;
@@ -225,7 +225,7 @@ int null_report_zones(struct gendisk *disk, sector_t sector,
blkz.capacity = zone->capacity;
null_unlock_zone(dev, zone);
- error = cb(&blkz, i, data);
+ error = disk_report_zone(disk, &blkz, i, args);
if (error)
return error;
}
@@ -242,7 +242,7 @@ size_t null_zone_valid_read_len(struct nullb *nullb,
{
struct nullb_device *dev = nullb->dev;
struct nullb_zone *zone = &dev->zones[null_zone_no(dev, sector)];
- unsigned int nr_sectors = len >> SECTOR_SHIFT;
+ unsigned int nr_sectors = DIV_ROUND_UP(len, SECTOR_SIZE);
/* Read must be below the write pointer position */
if (zone->type == BLK_ZONE_TYPE_CONVENTIONAL ||
diff --git a/drivers/block/ps3disk.c b/drivers/block/ps3disk.c
index dc9e4a14b885..8892f218a814 100644
--- a/drivers/block/ps3disk.c
+++ b/drivers/block/ps3disk.c
@@ -85,10 +85,14 @@ static void ps3disk_scatter_gather(struct ps3_storage_device *dev,
struct bio_vec bvec;
rq_for_each_segment(bvec, req, iter) {
+ dev_dbg(&dev->sbd.core, "%s:%u: %u sectors from %llu\n",
+ __func__, __LINE__, bio_sectors(iter.bio),
+ iter.bio->bi_iter.bi_sector);
if (gather)
memcpy_from_bvec(dev->bounce_buf + offset, &bvec);
else
memcpy_to_bvec(&bvec, dev->bounce_buf + offset);
+ offset += bvec.bv_len;
}
}
diff --git a/drivers/block/rnbd/rnbd-proto.h b/drivers/block/rnbd/rnbd-proto.h
index f35be51d213c..77360c2a6069 100644
--- a/drivers/block/rnbd/rnbd-proto.h
+++ b/drivers/block/rnbd/rnbd-proto.h
@@ -24,7 +24,7 @@
#define RTRS_PORT 1234
/**
- * enum rnbd_msg_types - RNBD message types
+ * enum rnbd_msg_type - RNBD message types
* @RNBD_MSG_SESS_INFO: initial session info from client to server
* @RNBD_MSG_SESS_INFO_RSP: initial session info from server to client
* @RNBD_MSG_OPEN: open (map) device request
@@ -47,10 +47,11 @@ enum rnbd_msg_type {
*/
struct rnbd_msg_hdr {
__le16 type;
+ /* private: */
__le16 __padding;
};
-/**
+/*
* We allow to map RO many times and RW only once. We allow to map yet another
* time RW, if MIGRATION is provided (second RW export can be required for
* example for VM migration)
@@ -78,6 +79,7 @@ static const __maybe_unused struct {
struct rnbd_msg_sess_info {
struct rnbd_msg_hdr hdr;
u8 ver;
+ /* private: */
u8 reserved[31];
};
@@ -89,6 +91,7 @@ struct rnbd_msg_sess_info {
struct rnbd_msg_sess_info_rsp {
struct rnbd_msg_hdr hdr;
u8 ver;
+ /* private: */
u8 reserved[31];
};
@@ -97,13 +100,16 @@ struct rnbd_msg_sess_info_rsp {
* @hdr: message header
* @access_mode: the mode to open remote device, valid values see:
* enum rnbd_access_mode
- * @device_name: device path on remote side
+ * @dev_name: device path on remote side
*/
struct rnbd_msg_open {
struct rnbd_msg_hdr hdr;
u8 access_mode;
+ /* private: */
u8 resv1;
+ /* public: */
s8 dev_name[NAME_MAX];
+ /* private: */
u8 reserved[3];
};
@@ -155,6 +161,7 @@ struct rnbd_msg_open_rsp {
__le16 secure_discard;
u8 obsolete_rotational;
u8 cache_policy;
+ /* private: */
u8 reserved[10];
};
@@ -187,7 +194,7 @@ struct rnbd_msg_io {
* @RNBD_OP_DISCARD: discard sectors
* @RNBD_OP_SECURE_ERASE: securely erase sectors
* @RNBD_OP_WRITE_ZEROES: write zeroes sectors
-
+ *
* @RNBD_F_SYNC: request is sync (sync write or read)
* @RNBD_F_FUA: forced unit access
*/
diff --git a/drivers/block/rnull/configfs.rs b/drivers/block/rnull/configfs.rs
index 8498e9bae6fd..6713a6d92391 100644
--- a/drivers/block/rnull/configfs.rs
+++ b/drivers/block/rnull/configfs.rs
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
use super::{NullBlkDevice, THIS_MODULE};
-use core::fmt::{Display, Write};
use kernel::{
block::mq::gen_disk::{GenDisk, GenDiskBuilder},
c_str,
configfs::{self, AttributeOperations},
- configfs_attrs, new_mutex,
+ configfs_attrs,
+ fmt::{self, Write as _},
+ new_mutex,
page::PAGE_SIZE,
prelude::*,
str::{kstrtobool_bytes, CString},
@@ -99,8 +100,8 @@ impl TryFrom<u8> for IRQMode {
}
}
-impl Display for IRQMode {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+impl fmt::Display for IRQMode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::None => f.write_str("0")?,
Self::Soft => f.write_str("1")?,
diff --git a/drivers/block/rnull/rnull.rs b/drivers/block/rnull/rnull.rs
index 1ec694d7f1a6..a9d5e575a2c4 100644
--- a/drivers/block/rnull/rnull.rs
+++ b/drivers/block/rnull/rnull.rs
@@ -17,8 +17,7 @@ use kernel::{
error::Result,
pr_info,
prelude::*,
- sync::Arc,
- types::ARef,
+ sync::{aref::ARef, Arc},
};
use pin_init::PinInit;
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 0c74a41a6753..2c715df63f23 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -155,12 +155,13 @@ struct ublk_uring_cmd_pdu {
*/
#define UBLK_REFCOUNT_INIT (REFCOUNT_MAX / 2)
+union ublk_io_buf {
+ __u64 addr;
+ struct ublk_auto_buf_reg auto_reg;
+};
+
struct ublk_io {
- /* userspace buffer address from io cmd */
- union {
- __u64 addr;
- struct ublk_auto_buf_reg buf;
- };
+ union ublk_io_buf buf;
unsigned int flags;
int res;
@@ -203,15 +204,12 @@ struct ublk_queue {
bool fail_io; /* copy of dev->state == UBLK_S_DEV_FAIL_IO */
spinlock_t cancel_lock;
struct ublk_device *dev;
- struct ublk_io ios[];
+ struct ublk_io ios[] __counted_by(q_depth);
};
struct ublk_device {
struct gendisk *ub_disk;
- char *__queues;
-
- unsigned int queue_size;
struct ublksrv_ctrl_dev_info dev_info;
struct blk_mq_tag_set tag_set;
@@ -239,6 +237,8 @@ struct ublk_device {
bool canceling;
pid_t ublksrv_tgid;
struct delayed_work exit_work;
+
+ struct ublk_queue *queues[];
};
/* header of ublk_params */
@@ -265,7 +265,7 @@ static inline bool ublk_dev_is_zoned(const struct ublk_device *ub)
return ub->dev_info.flags & UBLK_F_ZONED;
}
-static inline bool ublk_queue_is_zoned(struct ublk_queue *ubq)
+static inline bool ublk_queue_is_zoned(const struct ublk_queue *ubq)
{
return ubq->flags & UBLK_F_ZONED;
}
@@ -368,7 +368,7 @@ static void *ublk_alloc_report_buffer(struct ublk_device *ublk,
}
static int ublk_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones, struct blk_report_zones_args *args)
{
struct ublk_device *ub = disk->private_data;
unsigned int zone_size_sectors = disk->queue->limits.chunk_sectors;
@@ -431,7 +431,7 @@ free_req:
if (!zone->len)
break;
- ret = cb(zone, i, data);
+ ret = disk_report_zone(disk, zone, i, args);
if (ret)
goto out;
@@ -499,7 +499,7 @@ static blk_status_t ublk_setup_iod_zoned(struct ublk_queue *ubq,
iod->op_flags = ublk_op | ublk_req_build_flags(req);
iod->nr_sectors = blk_rq_sectors(req);
iod->start_sector = blk_rq_pos(req);
- iod->addr = io->addr;
+ iod->addr = io->buf.addr;
return BLK_STS_OK;
}
@@ -781,7 +781,7 @@ static noinline void ublk_put_device(struct ublk_device *ub)
static inline struct ublk_queue *ublk_get_queue(struct ublk_device *dev,
int qid)
{
- return (struct ublk_queue *)&(dev->__queues[qid * dev->queue_size]);
+ return dev->queues[qid];
}
static inline bool ublk_rq_has_data(const struct request *rq)
@@ -914,73 +914,6 @@ static const struct block_device_operations ub_fops = {
.report_zones = ublk_report_zones,
};
-#define UBLK_MAX_PIN_PAGES 32
-
-struct ublk_io_iter {
- struct page *pages[UBLK_MAX_PIN_PAGES];
- struct bio *bio;
- struct bvec_iter iter;
-};
-
-/* return how many pages are copied */
-static void ublk_copy_io_pages(struct ublk_io_iter *data,
- size_t total, size_t pg_off, int dir)
-{
- unsigned done = 0;
- unsigned pg_idx = 0;
-
- while (done < total) {
- struct bio_vec bv = bio_iter_iovec(data->bio, data->iter);
- unsigned int bytes = min3(bv.bv_len, (unsigned)total - done,
- (unsigned)(PAGE_SIZE - pg_off));
- void *bv_buf = bvec_kmap_local(&bv);
- void *pg_buf = kmap_local_page(data->pages[pg_idx]);
-
- if (dir == ITER_DEST)
- memcpy(pg_buf + pg_off, bv_buf, bytes);
- else
- memcpy(bv_buf, pg_buf + pg_off, bytes);
-
- kunmap_local(pg_buf);
- kunmap_local(bv_buf);
-
- /* advance page array */
- pg_off += bytes;
- if (pg_off == PAGE_SIZE) {
- pg_idx += 1;
- pg_off = 0;
- }
-
- done += bytes;
-
- /* advance bio */
- bio_advance_iter_single(data->bio, &data->iter, bytes);
- if (!data->iter.bi_size) {
- data->bio = data->bio->bi_next;
- if (data->bio == NULL)
- break;
- data->iter = data->bio->bi_iter;
- }
- }
-}
-
-static bool ublk_advance_io_iter(const struct request *req,
- struct ublk_io_iter *iter, unsigned int offset)
-{
- struct bio *bio = req->bio;
-
- for_each_bio(bio) {
- if (bio->bi_iter.bi_size > offset) {
- iter->bio = bio;
- iter->iter = bio->bi_iter;
- bio_advance_iter(iter->bio, &iter->iter, offset);
- return true;
- }
- offset -= bio->bi_iter.bi_size;
- }
- return false;
-}
-
/*
* Copy data between request pages and io_iter, and 'offset'
* is the start point of linear offset of request.
@@ -988,34 +921,35 @@ static bool ublk_advance_io_iter(const struct request *req,
static size_t ublk_copy_user_pages(const struct request *req,
unsigned offset, struct iov_iter *uiter, int dir)
{
- struct ublk_io_iter iter;
+ struct req_iterator iter;
+ struct bio_vec bv;
size_t done = 0;
- if (!ublk_advance_io_iter(req, &iter, offset))
- return 0;
+ rq_for_each_segment(bv, req, iter) {
+ void *bv_buf;
+ size_t copied;
- while (iov_iter_count(uiter) && iter.bio) {
- unsigned nr_pages;
- ssize_t len;
- size_t off;
- int i;
-
- len = iov_iter_get_pages2(uiter, iter.pages,
- iov_iter_count(uiter),
- UBLK_MAX_PIN_PAGES, &off);
- if (len <= 0)
- return done;
-
- ublk_copy_io_pages(&iter, len, off, dir);
- nr_pages = DIV_ROUND_UP(len + off, PAGE_SIZE);
- for (i = 0; i < nr_pages; i++) {
- if (dir == ITER_DEST)
- set_page_dirty(iter.pages[i]);
- put_page(iter.pages[i]);
+ if (offset >= bv.bv_len) {
+ offset -= bv.bv_len;
+ continue;
}
- done += len;
- }
+ bv.bv_offset += offset;
+ bv.bv_len -= offset;
+ bv_buf = bvec_kmap_local(&bv);
+ if (dir == ITER_DEST)
+ copied = copy_to_iter(bv_buf, bv.bv_len, uiter);
+ else
+ copied = copy_from_iter(bv_buf, bv.bv_len, uiter);
+
+ kunmap_local(bv_buf);
+
+ done += copied;
+ if (copied < bv.bv_len)
+ break;
+
+ offset = 0;
+ }
return done;
}
@@ -1030,8 +964,9 @@ static inline bool ublk_need_unmap_req(const struct request *req)
(req_op(req) == REQ_OP_READ || req_op(req) == REQ_OP_DRV_IN);
}
-static int ublk_map_io(const struct ublk_queue *ubq, const struct request *req,
- const struct ublk_io *io)
+static unsigned int ublk_map_io(const struct ublk_queue *ubq,
+ const struct request *req,
+ const struct ublk_io *io)
{
const unsigned int rq_bytes = blk_rq_bytes(req);
@@ -1047,13 +982,13 @@ static int ublk_map_io(const struct ublk_queue *ubq, const struct request *req,
struct iov_iter iter;
const int dir = ITER_DEST;
- import_ubuf(dir, u64_to_user_ptr(io->addr), rq_bytes, &iter);
+ import_ubuf(dir, u64_to_user_ptr(io->buf.addr), rq_bytes, &iter);
return ublk_copy_user_pages(req, 0, &iter, dir);
}
return rq_bytes;
}
-static int ublk_unmap_io(bool need_map,
+static unsigned int ublk_unmap_io(bool need_map,
const struct request *req,
const struct ublk_io *io)
{
@@ -1068,7 +1003,7 @@ static int ublk_unmap_io(bool need_map,
WARN_ON_ONCE(io->res > rq_bytes);
- import_ubuf(dir, u64_to_user_ptr(io->addr), io->res, &iter);
+ import_ubuf(dir, u64_to_user_ptr(io->buf.addr), io->res, &iter);
return ublk_copy_user_pages(req, 0, &iter, dir);
}
return rq_bytes;
@@ -1134,7 +1069,7 @@ static blk_status_t ublk_setup_iod(struct ublk_queue *ubq, struct request *req)
iod->op_flags = ublk_op | ublk_req_build_flags(req);
iod->nr_sectors = blk_rq_sectors(req);
iod->start_sector = blk_rq_pos(req);
- iod->addr = io->addr;
+ iod->addr = io->buf.addr;
return BLK_STS_OK;
}
@@ -1233,45 +1168,65 @@ static inline void __ublk_abort_rq(struct ublk_queue *ubq,
}
static void
-ublk_auto_buf_reg_fallback(const struct ublk_queue *ubq, struct ublk_io *io)
+ublk_auto_buf_reg_fallback(const struct ublk_queue *ubq, unsigned tag)
{
- unsigned tag = io - ubq->ios;
struct ublksrv_io_desc *iod = ublk_get_iod(ubq, tag);
iod->op_flags |= UBLK_IO_F_NEED_REG_BUF;
}
-static bool ublk_auto_buf_reg(const struct ublk_queue *ubq, struct request *req,
- struct ublk_io *io, unsigned int issue_flags)
+enum auto_buf_reg_res {
+ AUTO_BUF_REG_FAIL,
+ AUTO_BUF_REG_FALLBACK,
+ AUTO_BUF_REG_OK,
+};
+
+static void ublk_prep_auto_buf_reg_io(const struct ublk_queue *ubq,
+ struct request *req, struct ublk_io *io,
+ struct io_uring_cmd *cmd,
+ enum auto_buf_reg_res res)
+{
+ if (res == AUTO_BUF_REG_OK) {
+ io->task_registered_buffers = 1;
+ io->buf_ctx_handle = io_uring_cmd_ctx_handle(cmd);
+ io->flags |= UBLK_IO_FLAG_AUTO_BUF_REG;
+ }
+ ublk_init_req_ref(ubq, io);
+ __ublk_prep_compl_io_cmd(io, req);
+}
+
+static enum auto_buf_reg_res
+__ublk_do_auto_buf_reg(const struct ublk_queue *ubq, struct request *req,
+ struct ublk_io *io, struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
{
int ret;
- ret = io_buffer_register_bvec(io->cmd, req, ublk_io_release,
- io->buf.index, issue_flags);
+ ret = io_buffer_register_bvec(cmd, req, ublk_io_release,
+ io->buf.auto_reg.index, issue_flags);
if (ret) {
- if (io->buf.flags & UBLK_AUTO_BUF_REG_FALLBACK) {
- ublk_auto_buf_reg_fallback(ubq, io);
- return true;
+ if (io->buf.auto_reg.flags & UBLK_AUTO_BUF_REG_FALLBACK) {
+ ublk_auto_buf_reg_fallback(ubq, req->tag);
+ return AUTO_BUF_REG_FALLBACK;
}
blk_mq_end_request(req, BLK_STS_IOERR);
- return false;
+ return AUTO_BUF_REG_FAIL;
}
- io->task_registered_buffers = 1;
- io->buf_ctx_handle = io_uring_cmd_ctx_handle(io->cmd);
- io->flags |= UBLK_IO_FLAG_AUTO_BUF_REG;
- return true;
+ return AUTO_BUF_REG_OK;
}
-static bool ublk_prep_auto_buf_reg(struct ublk_queue *ubq,
- struct request *req, struct ublk_io *io,
- unsigned int issue_flags)
+static void ublk_do_auto_buf_reg(const struct ublk_queue *ubq, struct request *req,
+ struct ublk_io *io, struct io_uring_cmd *cmd,
+ unsigned int issue_flags)
{
- ublk_init_req_ref(ubq, io);
- if (ublk_support_auto_buf_reg(ubq) && ublk_rq_has_data(req))
- return ublk_auto_buf_reg(ubq, req, io, issue_flags);
+ enum auto_buf_reg_res res = __ublk_do_auto_buf_reg(ubq, req, io, cmd,
+ issue_flags);
- return true;
+ if (res != AUTO_BUF_REG_FAIL) {
+ ublk_prep_auto_buf_reg_io(ubq, req, io, cmd, res);
+ io_uring_cmd_done(cmd, UBLK_IO_RES_OK, issue_flags);
+ }
}
static bool ublk_start_io(const struct ublk_queue *ubq, struct request *req,
@@ -1302,10 +1257,9 @@ static bool ublk_start_io(const struct ublk_queue *ubq, struct request *req,
return true;
}
-static void ublk_dispatch_req(struct ublk_queue *ubq,
- struct request *req,
- unsigned int issue_flags)
+static void ublk_dispatch_req(struct ublk_queue *ubq, struct request *req)
{
+ unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
int tag = req->tag;
struct ublk_io *io = &ubq->ios[tag];
@@ -1344,17 +1298,21 @@ static void ublk_dispatch_req(struct ublk_queue *ubq,
if (!ublk_start_io(ubq, req, io))
return;
- if (ublk_prep_auto_buf_reg(ubq, req, io, issue_flags))
+ if (ublk_support_auto_buf_reg(ubq) && ublk_rq_has_data(req)) {
+ ublk_do_auto_buf_reg(ubq, req, io, io->cmd, issue_flags);
+ } else {
+ ublk_init_req_ref(ubq, io);
ublk_complete_io_cmd(io, req, UBLK_IO_RES_OK, issue_flags);
+ }
}
-static void ublk_cmd_tw_cb(struct io_uring_cmd *cmd,
- unsigned int issue_flags)
+static void ublk_cmd_tw_cb(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
struct ublk_queue *ubq = pdu->ubq;
- ublk_dispatch_req(ubq, pdu->req, issue_flags);
+ ublk_dispatch_req(ubq, pdu->req);
}
static void ublk_queue_cmd(struct ublk_queue *ubq, struct request *rq)
@@ -1366,9 +1324,9 @@ static void ublk_queue_cmd(struct ublk_queue *ubq, struct request *rq)
io_uring_cmd_complete_in_task(cmd, ublk_cmd_tw_cb);
}
-static void ublk_cmd_list_tw_cb(struct io_uring_cmd *cmd,
- unsigned int issue_flags)
+static void ublk_cmd_list_tw_cb(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd);
struct request *rq = pdu->req_list;
struct request *next;
@@ -1376,7 +1334,7 @@ static void ublk_cmd_list_tw_cb(struct io_uring_cmd *cmd,
do {
next = rq->rq_next;
rq->rq_next = NULL;
- ublk_dispatch_req(rq->mq_hctx->driver_data, rq, issue_flags);
+ ublk_dispatch_req(rq->mq_hctx->driver_data, rq);
rq = next;
} while (rq);
}
@@ -1537,7 +1495,7 @@ static void ublk_queue_reinit(struct ublk_device *ub, struct ublk_queue *ubq)
*/
io->flags &= UBLK_IO_FLAG_CANCELED;
io->cmd = NULL;
- io->addr = 0;
+ io->buf.addr = 0;
/*
* old task is PF_EXITING, put it now
@@ -2098,13 +2056,16 @@ static inline int ublk_check_cmd_op(u32 cmd_op)
static inline int ublk_set_auto_buf_reg(struct ublk_io *io, struct io_uring_cmd *cmd)
{
- io->buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr));
+ struct ublk_auto_buf_reg buf;
- if (io->buf.reserved0 || io->buf.reserved1)
+ buf = ublk_sqe_addr_to_auto_buf_reg(READ_ONCE(cmd->sqe->addr));
+
+ if (buf.reserved0 || buf.reserved1)
return -EINVAL;
- if (io->buf.flags & ~UBLK_AUTO_BUF_REG_F_MASK)
+ if (buf.flags & ~UBLK_AUTO_BUF_REG_F_MASK)
return -EINVAL;
+ io->buf.auto_reg = buf;
return 0;
}
@@ -2126,7 +2087,7 @@ static int ublk_handle_auto_buf_reg(struct ublk_io *io,
* this ublk request gets stuck.
*/
if (io->buf_ctx_handle == io_uring_cmd_ctx_handle(cmd))
- *buf_idx = io->buf.index;
+ *buf_idx = io->buf.auto_reg.index;
}
return ublk_set_auto_buf_reg(io, cmd);
@@ -2154,7 +2115,7 @@ ublk_config_io_buf(const struct ublk_device *ub, struct ublk_io *io,
if (ublk_dev_support_auto_buf_reg(ub))
return ublk_handle_auto_buf_reg(io, cmd, buf_idx);
- io->addr = buf_addr;
+ io->buf.addr = buf_addr;
return 0;
}
@@ -2272,39 +2233,41 @@ static int ublk_check_fetch_buf(const struct ublk_device *ub, __u64 buf_addr)
return 0;
}
-static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
- struct ublk_io *io, __u64 buf_addr)
+static int __ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
+ struct ublk_io *io)
{
- int ret = 0;
-
- /*
- * When handling FETCH command for setting up ublk uring queue,
- * ub->mutex is the innermost lock, and we won't block for handling
- * FETCH, so it is fine even for IO_URING_F_NONBLOCK.
- */
- mutex_lock(&ub->mutex);
/* UBLK_IO_FETCH_REQ is only allowed before dev is setup */
- if (ublk_dev_ready(ub)) {
- ret = -EBUSY;
- goto out;
- }
+ if (ublk_dev_ready(ub))
+ return -EBUSY;
/* allow each command to be FETCHed at most once */
- if (io->flags & UBLK_IO_FLAG_ACTIVE) {
- ret = -EINVAL;
- goto out;
- }
+ if (io->flags & UBLK_IO_FLAG_ACTIVE)
+ return -EINVAL;
WARN_ON_ONCE(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV);
ublk_fill_io_cmd(io, cmd);
- ret = ublk_config_io_buf(ub, io, cmd, buf_addr, NULL);
- if (ret)
- goto out;
WRITE_ONCE(io->task, get_task_struct(current));
ublk_mark_io_ready(ub);
-out:
+
+ return 0;
+}
+
+static int ublk_fetch(struct io_uring_cmd *cmd, struct ublk_device *ub,
+ struct ublk_io *io, __u64 buf_addr)
+{
+ int ret;
+
+ /*
+ * When handling FETCH command for setting up ublk uring queue,
+ * ub->mutex is the innermost lock, and we won't block for handling
+ * FETCH, so it is fine even for IO_URING_F_NONBLOCK.
+ */
+ mutex_lock(&ub->mutex);
+ ret = __ublk_fetch(cmd, ub, io);
+ if (!ret)
+ ret = ublk_config_io_buf(ub, io, cmd, buf_addr, NULL);
mutex_unlock(&ub->mutex);
return ret;
}
@@ -2351,7 +2314,7 @@ static bool ublk_get_data(const struct ublk_queue *ubq, struct ublk_io *io,
*/
io->flags &= ~UBLK_IO_FLAG_NEED_GET_DATA;
/* update iod->addr because ublksrv may have passed a new io buffer */
- ublk_get_iod(ubq, req->tag)->addr = io->addr;
+ ublk_get_iod(ubq, req->tag)->addr = io->buf.addr;
pr_devel("%s: update iod->addr: qid %d tag %d io_flags %x addr %llx\n",
__func__, ubq->q_id, req->tag, io->flags,
ublk_get_iod(ubq, req->tag)->addr);
@@ -2367,7 +2330,7 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
u16 buf_idx = UBLK_INVALID_BUF_IDX;
struct ublk_device *ub = cmd->file->private_data;
struct ublk_queue *ubq;
- struct ublk_io *io;
+ struct ublk_io *io = NULL;
u32 cmd_op = cmd->cmd_op;
u16 q_id = READ_ONCE(ub_src->q_id);
u16 tag = READ_ONCE(ub_src->tag);
@@ -2488,7 +2451,7 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd,
out:
pr_devel("%s: complete: cmd op %d, tag %d ret %x io_flags %x\n",
- __func__, cmd_op, tag, ret, io->flags);
+ __func__, cmd_op, tag, ret, io ? io->flags : 0);
return ret;
}
@@ -2523,9 +2486,10 @@ fail_put:
return NULL;
}
-static void ublk_ch_uring_cmd_cb(struct io_uring_cmd *cmd,
- unsigned int issue_flags)
+static void ublk_ch_uring_cmd_cb(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ unsigned int issue_flags = IO_URING_CMD_TASK_WORK_ISSUE_FLAGS;
+ struct io_uring_cmd *cmd = io_uring_cmd_from_tw(tw_req);
int ret = ublk_ch_uring_cmd_local(cmd, issue_flags);
if (ret != -EIOCBQUEUED)
@@ -2575,9 +2539,6 @@ static struct request *ublk_check_and_get_req(struct kiocb *iocb,
size_t buf_off;
u16 tag, q_id;
- if (!ub)
- return ERR_PTR(-EACCES);
-
if (!user_backed_iter(iter))
return ERR_PTR(-EACCES);
@@ -2603,9 +2564,6 @@ static struct request *ublk_check_and_get_req(struct kiocb *iocb,
if (!req)
return ERR_PTR(-EINVAL);
- if (!req->mq_hctx || !req->mq_hctx->driver_data)
- goto fail;
-
if (!ublk_check_ubuf_dir(req, dir))
goto fail;
@@ -2662,9 +2620,13 @@ static const struct file_operations ublk_ch_fops = {
static void ublk_deinit_queue(struct ublk_device *ub, int q_id)
{
- int size = ublk_queue_cmd_buf_size(ub);
- struct ublk_queue *ubq = ublk_get_queue(ub, q_id);
- int i;
+ struct ublk_queue *ubq = ub->queues[q_id];
+ int size, i;
+
+ if (!ubq)
+ return;
+
+ size = ublk_queue_cmd_buf_size(ub);
for (i = 0; i < ubq->q_depth; i++) {
struct ublk_io *io = &ubq->ios[i];
@@ -2676,57 +2638,76 @@ static void ublk_deinit_queue(struct ublk_device *ub, int q_id)
if (ubq->io_cmd_buf)
free_pages((unsigned long)ubq->io_cmd_buf, get_order(size));
+
+ kvfree(ubq);
+ ub->queues[q_id] = NULL;
+}
+
+static int ublk_get_queue_numa_node(struct ublk_device *ub, int q_id)
+{
+ unsigned int cpu;
+
+ /* Find first CPU mapped to this queue */
+ for_each_possible_cpu(cpu) {
+ if (ub->tag_set.map[HCTX_TYPE_DEFAULT].mq_map[cpu] == q_id)
+ return cpu_to_node(cpu);
+ }
+
+ return NUMA_NO_NODE;
}
static int ublk_init_queue(struct ublk_device *ub, int q_id)
{
- struct ublk_queue *ubq = ublk_get_queue(ub, q_id);
+ int depth = ub->dev_info.queue_depth;
gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
- void *ptr;
+ struct ublk_queue *ubq;
+ struct page *page;
+ int numa_node;
int size;
+ /* Determine NUMA node based on queue's CPU affinity */
+ numa_node = ublk_get_queue_numa_node(ub, q_id);
+
+ /* Allocate queue structure on local NUMA node */
+ ubq = kvzalloc_node(struct_size(ubq, ios, depth), GFP_KERNEL,
+ numa_node);
+ if (!ubq)
+ return -ENOMEM;
+
spin_lock_init(&ubq->cancel_lock);
ubq->flags = ub->dev_info.flags;
ubq->q_id = q_id;
- ubq->q_depth = ub->dev_info.queue_depth;
+ ubq->q_depth = depth;
size = ublk_queue_cmd_buf_size(ub);
- ptr = (void *) __get_free_pages(gfp_flags, get_order(size));
- if (!ptr)
+ /* Allocate I/O command buffer on local NUMA node */
+ page = alloc_pages_node(numa_node, gfp_flags, get_order(size));
+ if (!page) {
+ kvfree(ubq);
return -ENOMEM;
+ }
+ ubq->io_cmd_buf = page_address(page);
- ubq->io_cmd_buf = ptr;
+ ub->queues[q_id] = ubq;
ubq->dev = ub;
return 0;
}
static void ublk_deinit_queues(struct ublk_device *ub)
{
- int nr_queues = ub->dev_info.nr_hw_queues;
int i;
- if (!ub->__queues)
- return;
-
- for (i = 0; i < nr_queues; i++)
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++)
ublk_deinit_queue(ub, i);
- kvfree(ub->__queues);
}
static int ublk_init_queues(struct ublk_device *ub)
{
- int nr_queues = ub->dev_info.nr_hw_queues;
- int depth = ub->dev_info.queue_depth;
- int ubq_size = sizeof(struct ublk_queue) + depth * sizeof(struct ublk_io);
- int i, ret = -ENOMEM;
+ int i, ret;
- ub->queue_size = ubq_size;
- ub->__queues = kvcalloc(nr_queues, ubq_size, GFP_KERNEL);
- if (!ub->__queues)
- return ret;
-
- for (i = 0; i < nr_queues; i++) {
- if (ublk_init_queue(ub, i))
+ for (i = 0; i < ub->dev_info.nr_hw_queues; i++) {
+ ret = ublk_init_queue(ub, i);
+ if (ret)
goto fail;
}
@@ -3128,7 +3109,7 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
goto out_unlock;
ret = -ENOMEM;
- ub = kzalloc(sizeof(*ub), GFP_KERNEL);
+ ub = kzalloc(struct_size(ub, queues, info.nr_hw_queues), GFP_KERNEL);
if (!ub)
goto out_unlock;
mutex_init(&ub->mutex);
@@ -3178,17 +3159,17 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
ub->dev_info.nr_hw_queues, nr_cpu_ids);
ublk_align_max_io_size(ub);
- ret = ublk_init_queues(ub);
+ ret = ublk_add_tag_set(ub);
if (ret)
goto out_free_dev_number;
- ret = ublk_add_tag_set(ub);
+ ret = ublk_init_queues(ub);
if (ret)
- goto out_deinit_queues;
+ goto out_free_tag_set;
ret = -EFAULT;
if (copy_to_user(argp, &ub->dev_info, sizeof(info)))
- goto out_free_tag_set;
+ goto out_deinit_queues;
/*
* Add the char dev so that ublksrv daemon can be setup.
@@ -3197,10 +3178,10 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header)
ret = ublk_add_chdev(ub);
goto out_unlock;
-out_free_tag_set:
- blk_mq_free_tag_set(&ub->tag_set);
out_deinit_queues:
ublk_deinit_queues(ub);
+out_free_tag_set:
+ blk_mq_free_tag_set(&ub->tag_set);
out_free_dev_number:
ublk_free_dev_number(ub);
out_free_ub:
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index f061420dfb10..357434bdae99 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -584,7 +584,8 @@ out:
static int virtblk_parse_zone(struct virtio_blk *vblk,
struct virtio_blk_zone_descriptor *entry,
- unsigned int idx, report_zones_cb cb, void *data)
+ unsigned int idx,
+ struct blk_report_zones_args *args)
{
struct blk_zone zone = { };
@@ -650,12 +651,12 @@ static int virtblk_parse_zone(struct virtio_blk *vblk,
* The callback below checks the validity of the reported
* entry data, no need to further validate it here.
*/
- return cb(&zone, idx, data);
+ return disk_report_zone(vblk->disk, &zone, idx, args);
}
static int virtblk_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb,
- void *data)
+ unsigned int nr_zones,
+ struct blk_report_zones_args *args)
{
struct virtio_blk *vblk = disk->private_data;
struct virtio_blk_zone_report *report;
@@ -693,7 +694,7 @@ static int virtblk_report_zones(struct gendisk *disk, sector_t sector,
for (i = 0; i < nz && zone_idx < nr_zones; i++) {
ret = virtblk_parse_zone(vblk, &report->zones[i],
- zone_idx, cb, data);
+ zone_idx, args);
if (ret)
goto fail_report;
@@ -1026,8 +1027,13 @@ static int init_vq(struct virtio_blk *vblk)
out:
kfree(vqs);
kfree(vqs_info);
- if (err)
+ if (err) {
kfree(vblk->vqs);
+ /*
+ * Set to NULL to prevent freeing vqs again during freezing.
+ */
+ vblk->vqs = NULL;
+ }
return err;
}
@@ -1598,6 +1604,12 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
vdev->config->del_vqs(vdev);
kfree(vblk->vqs);
+ /*
+ * Set to NULL to prevent freeing vqs again after a failed vqs
+ * allocation during resume. Note that kfree() already handles NULL
+ * pointers safely.
+ */
+ vblk->vqs = NULL;
return 0;
}
diff --git a/drivers/block/zloop.c b/drivers/block/zloop.c
index a423228e201b..3f50321aa4a7 100644
--- a/drivers/block/zloop.c
+++ b/drivers/block/zloop.c
@@ -32,6 +32,8 @@ enum {
ZLOOP_OPT_NR_QUEUES = (1 << 6),
ZLOOP_OPT_QUEUE_DEPTH = (1 << 7),
ZLOOP_OPT_BUFFERED_IO = (1 << 8),
+ ZLOOP_OPT_ZONE_APPEND = (1 << 9),
+ ZLOOP_OPT_ORDERED_ZONE_APPEND = (1 << 10),
};
static const match_table_t zloop_opt_tokens = {
@@ -44,6 +46,8 @@ static const match_table_t zloop_opt_tokens = {
{ ZLOOP_OPT_NR_QUEUES, "nr_queues=%u" },
{ ZLOOP_OPT_QUEUE_DEPTH, "queue_depth=%u" },
{ ZLOOP_OPT_BUFFERED_IO, "buffered_io" },
+ { ZLOOP_OPT_ZONE_APPEND, "zone_append=%u" },
+ { ZLOOP_OPT_ORDERED_ZONE_APPEND, "ordered_zone_append" },
{ ZLOOP_OPT_ERR, NULL }
};
@@ -56,6 +60,8 @@ static const match_table_t zloop_opt_tokens = {
#define ZLOOP_DEF_NR_QUEUES 1
#define ZLOOP_DEF_QUEUE_DEPTH 128
#define ZLOOP_DEF_BUFFERED_IO false
+#define ZLOOP_DEF_ZONE_APPEND true
+#define ZLOOP_DEF_ORDERED_ZONE_APPEND false
/* Arbitrary limit on the zone size (16GB). */
#define ZLOOP_MAX_ZONE_SIZE_MB 16384
@@ -71,6 +77,8 @@ struct zloop_options {
unsigned int nr_queues;
unsigned int queue_depth;
bool buffered_io;
+ bool zone_append;
+ bool ordered_zone_append;
};
/*
@@ -92,6 +100,7 @@ struct zloop_zone {
unsigned long flags;
struct mutex lock;
+ spinlock_t wp_lock;
enum blk_zone_cond cond;
sector_t start;
sector_t wp;
@@ -108,6 +117,8 @@ struct zloop_device {
struct workqueue_struct *workqueue;
bool buffered_io;
+ bool zone_append;
+ bool ordered_zone_append;
const char *base_dir;
struct file *data_dir;
@@ -147,6 +158,7 @@ static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no)
struct zloop_zone *zone = &zlo->zones[zone_no];
struct kstat stat;
sector_t file_sectors;
+ unsigned long flags;
int ret;
lockdep_assert_held(&zone->lock);
@@ -172,16 +184,18 @@ static int zloop_update_seq_zone(struct zloop_device *zlo, unsigned int zone_no)
return -EINVAL;
}
+ spin_lock_irqsave(&zone->wp_lock, flags);
if (!file_sectors) {
zone->cond = BLK_ZONE_COND_EMPTY;
zone->wp = zone->start;
} else if (file_sectors == zlo->zone_capacity) {
zone->cond = BLK_ZONE_COND_FULL;
- zone->wp = zone->start + zlo->zone_size;
+ zone->wp = ULLONG_MAX;
} else {
zone->cond = BLK_ZONE_COND_CLOSED;
zone->wp = zone->start + file_sectors;
}
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
return 0;
}
@@ -225,6 +239,7 @@ unlock:
static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no)
{
struct zloop_zone *zone = &zlo->zones[zone_no];
+ unsigned long flags;
int ret = 0;
if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
@@ -243,10 +258,12 @@ static int zloop_close_zone(struct zloop_device *zlo, unsigned int zone_no)
break;
case BLK_ZONE_COND_IMP_OPEN:
case BLK_ZONE_COND_EXP_OPEN:
+ spin_lock_irqsave(&zone->wp_lock, flags);
if (zone->wp == zone->start)
zone->cond = BLK_ZONE_COND_EMPTY;
else
zone->cond = BLK_ZONE_COND_CLOSED;
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
break;
case BLK_ZONE_COND_EMPTY:
case BLK_ZONE_COND_FULL:
@@ -264,6 +281,7 @@ unlock:
static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no)
{
struct zloop_zone *zone = &zlo->zones[zone_no];
+ unsigned long flags;
int ret = 0;
if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
@@ -281,9 +299,11 @@ static int zloop_reset_zone(struct zloop_device *zlo, unsigned int zone_no)
goto unlock;
}
+ spin_lock_irqsave(&zone->wp_lock, flags);
zone->cond = BLK_ZONE_COND_EMPTY;
zone->wp = zone->start;
clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
unlock:
mutex_unlock(&zone->lock);
@@ -308,6 +328,7 @@ static int zloop_reset_all_zones(struct zloop_device *zlo)
static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
{
struct zloop_zone *zone = &zlo->zones[zone_no];
+ unsigned long flags;
int ret = 0;
if (test_bit(ZLOOP_ZONE_CONV, &zone->flags))
@@ -325,9 +346,11 @@ static int zloop_finish_zone(struct zloop_device *zlo, unsigned int zone_no)
goto unlock;
}
+ spin_lock_irqsave(&zone->wp_lock, flags);
zone->cond = BLK_ZONE_COND_FULL;
- zone->wp = zone->start + zlo->zone_size;
+ zone->wp = ULLONG_MAX;
clear_bit(ZLOOP_ZONE_SEQ_ERROR, &zone->flags);
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
unlock:
mutex_unlock(&zone->lock);
@@ -369,6 +392,7 @@ static void zloop_rw(struct zloop_cmd *cmd)
struct zloop_zone *zone;
struct iov_iter iter;
struct bio_vec tmp;
+ unsigned long flags;
sector_t zone_end;
int nr_bvec = 0;
int ret;
@@ -378,6 +402,11 @@ static void zloop_rw(struct zloop_cmd *cmd)
cmd->nr_sectors = nr_sectors;
cmd->ret = 0;
+ if (WARN_ON_ONCE(is_append && !zlo->zone_append)) {
+ ret = -EIO;
+ goto out;
+ }
+
/* We should never get an I/O beyond the device capacity. */
if (WARN_ON_ONCE(zone_no >= zlo->nr_zones)) {
ret = -EIO;
@@ -406,16 +435,31 @@ static void zloop_rw(struct zloop_cmd *cmd)
if (!test_bit(ZLOOP_ZONE_CONV, &zone->flags) && is_write) {
mutex_lock(&zone->lock);
- if (is_append) {
- sector = zone->wp;
- cmd->sector = sector;
- }
+ spin_lock_irqsave(&zone->wp_lock, flags);
/*
- * Write operations must be aligned to the write pointer and
- * fully contained within the zone capacity.
+ * Zone append operations always go at the current write
+ * pointer, but regular write operations must already be
+ * aligned to the write pointer when submitted.
*/
- if (sector != zone->wp || zone->wp + nr_sectors > zone_end) {
+ if (is_append) {
+ /*
+ * If ordered zone append is in use, we already checked
+ * and set the target sector in zloop_queue_rq().
+ */
+ if (!zlo->ordered_zone_append) {
+ if (zone->cond == BLK_ZONE_COND_FULL ||
+ zone->wp + nr_sectors > zone_end) {
+ spin_unlock_irqrestore(&zone->wp_lock,
+ flags);
+ ret = -EIO;
+ goto unlock;
+ }
+ sector = zone->wp;
+ }
+ cmd->sector = sector;
+ } else if (sector != zone->wp) {
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
pr_err("Zone %u: unaligned write: sect %llu, wp %llu\n",
zone_no, sector, zone->wp);
ret = -EIO;
@@ -428,13 +472,19 @@ static void zloop_rw(struct zloop_cmd *cmd)
zone->cond = BLK_ZONE_COND_IMP_OPEN;
/*
- * Advance the write pointer of sequential zones. If the write
- * fails, the wp position will be corrected when the next I/O
- * copmpletes.
+ * Advance the write pointer, unless ordered zone append is in
+ * use. If the write fails, the write pointer position will be
+ * corrected when the next I/O starts execution.
*/
- zone->wp += nr_sectors;
- if (zone->wp == zone_end)
- zone->cond = BLK_ZONE_COND_FULL;
+ if (!is_append || !zlo->ordered_zone_append) {
+ zone->wp += nr_sectors;
+ if (zone->wp == zone_end) {
+ zone->cond = BLK_ZONE_COND_FULL;
+ zone->wp = ULLONG_MAX;
+ }
+ }
+
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
}
rq_for_each_bvec(tmp, rq, rq_iter)
@@ -498,6 +548,10 @@ static void zloop_handle_cmd(struct zloop_cmd *cmd)
struct request *rq = blk_mq_rq_from_pdu(cmd);
struct zloop_device *zlo = rq->q->queuedata;
+ /* We can block in this context, so ignore REQ_NOWAIT. */
+ if (rq->cmd_flags & REQ_NOWAIT)
+ rq->cmd_flags &= ~REQ_NOWAIT;
+
switch (req_op(rq)) {
case REQ_OP_READ:
case REQ_OP_WRITE:
@@ -608,6 +662,35 @@ static void zloop_complete_rq(struct request *rq)
blk_mq_end_request(rq, sts);
}
+static bool zloop_set_zone_append_sector(struct request *rq)
+{
+ struct zloop_device *zlo = rq->q->queuedata;
+ unsigned int zone_no = rq_zone_no(rq);
+ struct zloop_zone *zone = &zlo->zones[zone_no];
+ sector_t zone_end = zone->start + zlo->zone_capacity;
+ sector_t nr_sectors = blk_rq_sectors(rq);
+ unsigned long flags;
+
+ spin_lock_irqsave(&zone->wp_lock, flags);
+
+ if (zone->cond == BLK_ZONE_COND_FULL ||
+ zone->wp + nr_sectors > zone_end) {
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+ return false;
+ }
+
+ rq->__sector = zone->wp;
+ zone->wp += blk_rq_sectors(rq);
+ if (zone->wp >= zone_end) {
+ zone->cond = BLK_ZONE_COND_FULL;
+ zone->wp = ULLONG_MAX;
+ }
+
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
+
+ return true;
+}
+
static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
@@ -618,6 +701,16 @@ static blk_status_t zloop_queue_rq(struct blk_mq_hw_ctx *hctx,
if (zlo->state == Zlo_deleting)
return BLK_STS_IOERR;
+ /*
+ * If we need to strongly order zone append operations, set the request
+ * sector to the zone write pointer location now instead of when the
+ * command work runs.
+ */
+ if (zlo->ordered_zone_append && req_op(rq) == REQ_OP_ZONE_APPEND) {
+ if (!zloop_set_zone_append_sector(rq))
+ return BLK_STS_IOERR;
+ }
+
blk_mq_start_request(rq);
INIT_WORK(&cmd->work, zloop_cmd_workfn);
@@ -647,11 +740,12 @@ static int zloop_open(struct gendisk *disk, blk_mode_t mode)
}
static int zloop_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones, struct blk_report_zones_args *args)
{
struct zloop_device *zlo = disk->private_data;
struct blk_zone blkz = {};
unsigned int first, i;
+ unsigned long flags;
int ret;
first = disk_zone_no(disk, sector);
@@ -675,7 +769,9 @@ static int zloop_report_zones(struct gendisk *disk, sector_t sector,
blkz.start = zone->start;
blkz.len = zlo->zone_size;
+ spin_lock_irqsave(&zone->wp_lock, flags);
blkz.wp = zone->wp;
+ spin_unlock_irqrestore(&zone->wp_lock, flags);
blkz.cond = zone->cond;
if (test_bit(ZLOOP_ZONE_CONV, &zone->flags)) {
blkz.type = BLK_ZONE_TYPE_CONVENTIONAL;
@@ -687,7 +783,7 @@ static int zloop_report_zones(struct gendisk *disk, sector_t sector,
mutex_unlock(&zone->lock);
- ret = cb(&blkz, i, data);
+ ret = disk_report_zone(disk, &blkz, i, args);
if (ret)
return ret;
}
@@ -783,6 +879,7 @@ static int zloop_init_zone(struct zloop_device *zlo, struct zloop_options *opts,
int ret;
mutex_init(&zone->lock);
+ spin_lock_init(&zone->wp_lock);
zone->start = (sector_t)zone_no << zlo->zone_shift;
if (!restore)
@@ -884,7 +981,6 @@ static int zloop_ctl_add(struct zloop_options *opts)
{
struct queue_limits lim = {
.max_hw_sectors = SZ_1M >> SECTOR_SHIFT,
- .max_hw_zone_append_sectors = SZ_1M >> SECTOR_SHIFT,
.chunk_sectors = opts->zone_size,
.features = BLK_FEAT_ZONED,
};
@@ -936,6 +1032,9 @@ static int zloop_ctl_add(struct zloop_options *opts)
zlo->nr_zones = nr_zones;
zlo->nr_conv_zones = opts->nr_conv_zones;
zlo->buffered_io = opts->buffered_io;
+ zlo->zone_append = opts->zone_append;
+ if (zlo->zone_append)
+ zlo->ordered_zone_append = opts->ordered_zone_append;
zlo->workqueue = alloc_workqueue("zloop%d", WQ_UNBOUND | WQ_FREEZABLE,
opts->nr_queues * opts->queue_depth, zlo->id);
@@ -976,6 +1075,8 @@ static int zloop_ctl_add(struct zloop_options *opts)
lim.physical_block_size = zlo->block_size;
lim.logical_block_size = zlo->block_size;
+ if (zlo->zone_append)
+ lim.max_hw_zone_append_sectors = lim.max_hw_sectors;
zlo->tag_set.ops = &zloop_mq_ops;
zlo->tag_set.nr_hw_queues = opts->nr_queues;
@@ -1016,10 +1117,14 @@ static int zloop_ctl_add(struct zloop_options *opts)
zlo->state = Zlo_live;
mutex_unlock(&zloop_ctl_mutex);
- pr_info("Added device %d: %u zones of %llu MB, %u B block size\n",
+ pr_info("zloop: device %d, %u zones of %llu MiB, %u B block size\n",
zlo->id, zlo->nr_zones,
((sector_t)zlo->zone_size << SECTOR_SHIFT) >> 20,
zlo->block_size);
+ pr_info("zloop%d: using %s%s zone append\n",
+ zlo->id,
+ zlo->ordered_zone_append ? "ordered " : "",
+ zlo->zone_append ? "native" : "emulated");
return 0;
@@ -1106,6 +1211,8 @@ static int zloop_parse_options(struct zloop_options *opts, const char *buf)
opts->nr_queues = ZLOOP_DEF_NR_QUEUES;
opts->queue_depth = ZLOOP_DEF_QUEUE_DEPTH;
opts->buffered_io = ZLOOP_DEF_BUFFERED_IO;
+ opts->zone_append = ZLOOP_DEF_ZONE_APPEND;
+ opts->ordered_zone_append = ZLOOP_DEF_ORDERED_ZONE_APPEND;
if (!buf)
return 0;
@@ -1215,6 +1322,21 @@ static int zloop_parse_options(struct zloop_options *opts, const char *buf)
case ZLOOP_OPT_BUFFERED_IO:
opts->buffered_io = true;
break;
+ case ZLOOP_OPT_ZONE_APPEND:
+ if (match_uint(args, &token)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ if (token != 0 && token != 1) {
+ pr_err("Invalid zone_append value\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ opts->zone_append = token;
+ break;
+ case ZLOOP_OPT_ORDERED_ZONE_APPEND:
+ opts->ordered_zone_append = true;
+ break;
case ZLOOP_OPT_ERR:
default:
pr_warn("unknown parameter or missing value '%s'\n", p);
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 7df69ccb6600..c5d45cf91f88 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -188,6 +188,7 @@ config BT_HCIUART_3WIRE
bool "Three-wire UART (H5) protocol support"
depends on BT_HCIUART
depends on BT_HCIUART_SERDEV
+ select CRC_CCITT
help
The HCI Three-wire UART Transport Layer makes it possible to
user the Bluetooth HCI over a serial port interface. The HCI
diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c
index b7ba667a3d09..e305d04aac9d 100644
--- a/drivers/bluetooth/bpa10x.c
+++ b/drivers/bluetooth/bpa10x.c
@@ -41,6 +41,7 @@ struct bpa10x_data {
struct usb_anchor rx_anchor;
struct sk_buff *rx_skb[2];
+ struct hci_uart hu;
};
static void bpa10x_tx_complete(struct urb *urb)
@@ -96,7 +97,7 @@ static void bpa10x_rx_complete(struct urb *urb)
if (urb->status == 0) {
bool idx = usb_pipebulk(urb->pipe);
- data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
+ data->rx_skb[idx] = h4_recv_buf(&data->hu, data->rx_skb[idx],
urb->transfer_buffer,
urb->actual_length,
bpa10x_recv_pkts,
@@ -388,6 +389,7 @@ static int bpa10x_probe(struct usb_interface *intf,
hci_set_drvdata(hdev, data);
data->hdev = hdev;
+ data->hu.hdev = hdev;
SET_HCIDEV_DEV(hdev, &intf->dev);
diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c
index 3a3a56ddbb06..d33cc70eec66 100644
--- a/drivers/bluetooth/btbcm.c
+++ b/drivers/bluetooth/btbcm.c
@@ -642,7 +642,9 @@ int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud
snprintf(postfix, sizeof(postfix), "-%4.4x-%4.4x", vid, pid);
}
- fw_name = kmalloc(BCM_FW_NAME_COUNT_MAX * BCM_FW_NAME_LEN, GFP_KERNEL);
+ fw_name = kmalloc_array(BCM_FW_NAME_COUNT_MAX,
+ sizeof(*fw_name),
+ GFP_KERNEL);
if (!fw_name)
return -ENOMEM;
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 6d3963bd56a9..2936b535479f 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -19,6 +19,7 @@
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci_drv.h>
#include "btintel.h"
#include "btintel_pcie.h"
@@ -825,6 +826,11 @@ static inline bool btintel_pcie_in_d0(struct btintel_pcie_data *data)
return !(data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_D3_STATE_READY);
}
+static inline bool btintel_pcie_in_device_halt(struct btintel_pcie_data *data)
+{
+ return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_DEVICE_HALTED;
+}
+
static void btintel_pcie_wr_sleep_cntrl(struct btintel_pcie_data *data,
u32 dxstate)
{
@@ -1467,11 +1473,6 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP1)
btintel_pcie_msix_gp1_handler(data);
- /* This interrupt is triggered by the firmware after updating
- * boot_stage register and image_response register
- */
- if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0)
- btintel_pcie_msix_gp0_handler(data);
/* For TX */
if (intr_fh & BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0) {
@@ -1487,6 +1488,12 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
btintel_pcie_msix_tx_handle(data);
}
+ /* This interrupt is triggered by the firmware after updating
+ * boot_stage register and image_response register
+ */
+ if (intr_hw & BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0)
+ btintel_pcie_msix_gp0_handler(data);
+
/*
* Before sending the interrupt the HW disables it to prevent a nested
* interrupt. This is done by writing 1 to the corresponding bit in
@@ -2354,6 +2361,63 @@ static bool btintel_pcie_wakeup(struct hci_dev *hdev)
return device_may_wakeup(&data->pdev->dev);
}
+static const struct {
+ u16 opcode;
+ const char *desc;
+} btintel_pcie_hci_drv_supported_commands[] = {
+ /* Common commands */
+ { HCI_DRV_OP_READ_INFO, "Read Info" },
+};
+
+static int btintel_pcie_hci_drv_read_info(struct hci_dev *hdev, void *data,
+ u16 data_len)
+{
+ struct hci_drv_rp_read_info *rp;
+ size_t rp_size;
+ int err, i;
+ u16 opcode, num_supported_commands =
+ ARRAY_SIZE(btintel_pcie_hci_drv_supported_commands);
+
+ rp_size = sizeof(*rp) + num_supported_commands * 2;
+
+ rp = kmalloc(rp_size, GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ strscpy_pad(rp->driver_name, KBUILD_MODNAME);
+
+ rp->num_supported_commands = cpu_to_le16(num_supported_commands);
+ for (i = 0; i < num_supported_commands; i++) {
+ opcode = btintel_pcie_hci_drv_supported_commands[i].opcode;
+ bt_dev_dbg(hdev,
+ "Supported HCI Drv command (0x%02x|0x%04x): %s",
+ hci_opcode_ogf(opcode),
+ hci_opcode_ocf(opcode),
+ btintel_pcie_hci_drv_supported_commands[i].desc);
+ rp->supported_commands[i] = cpu_to_le16(opcode);
+ }
+
+ err = hci_drv_cmd_complete(hdev, HCI_DRV_OP_READ_INFO,
+ HCI_DRV_STATUS_SUCCESS,
+ rp, rp_size);
+
+ kfree(rp);
+ return err;
+}
+
+static const struct hci_drv_handler btintel_pcie_hci_drv_common_handlers[] = {
+ { btintel_pcie_hci_drv_read_info, HCI_DRV_READ_INFO_SIZE },
+};
+
+static const struct hci_drv_handler btintel_pcie_hci_drv_specific_handlers[] = {};
+
+static struct hci_drv btintel_pcie_hci_drv = {
+ .common_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_common_handlers),
+ .common_handlers = btintel_pcie_hci_drv_common_handlers,
+ .specific_handler_count = ARRAY_SIZE(btintel_pcie_hci_drv_specific_handlers),
+ .specific_handlers = btintel_pcie_hci_drv_specific_handlers,
+};
+
static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
{
int err;
@@ -2380,6 +2444,7 @@ static int btintel_pcie_setup_hdev(struct btintel_pcie_data *data)
hdev->set_bdaddr = btintel_set_bdaddr;
hdev->reset = btintel_pcie_reset;
hdev->wakeup = btintel_pcie_wakeup;
+ hdev->hci_drv = &btintel_pcie_hci_drv;
err = hci_register_dev(hdev);
if (err < 0) {
@@ -2518,6 +2583,48 @@ static void btintel_pcie_coredump(struct device *dev)
}
#endif
+static int btintel_pcie_set_dxstate(struct btintel_pcie_data *data, u32 dxstate)
+{
+ int retry = 0, status;
+ u32 dx_intr_timeout_ms = 200;
+
+ do {
+ data->gp0_received = false;
+
+ btintel_pcie_wr_sleep_cntrl(data, dxstate);
+
+ status = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
+ msecs_to_jiffies(dx_intr_timeout_ms));
+
+ if (status)
+ return 0;
+
+ bt_dev_warn(data->hdev,
+ "Timeout (%u ms) on alive interrupt for D%d entry, retry count %d",
+ dx_intr_timeout_ms, dxstate, retry);
+
+ /* clear gp0 cause */
+ btintel_pcie_clr_reg_bits(data,
+ BTINTEL_PCIE_CSR_MSIX_HW_INT_CAUSES,
+ BTINTEL_PCIE_MSIX_HW_INT_CAUSES_GP0);
+
+ /* A hardware bug may cause the alive interrupt to be missed.
+ * Check if the controller reached the expected state and retry
+ * the operation only if it hasn't.
+ */
+ if (dxstate == BTINTEL_PCIE_STATE_D0) {
+ if (btintel_pcie_in_d0(data))
+ return 0;
+ } else {
+ if (btintel_pcie_in_d3(data))
+ return 0;
+ }
+
+ } while (++retry < BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES);
+
+ return -EBUSY;
+}
+
static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -2531,26 +2638,20 @@ static int btintel_pcie_suspend_late(struct device *dev, pm_message_t mesg)
dxstate = (mesg.event == PM_EVENT_SUSPEND ?
BTINTEL_PCIE_STATE_D3_HOT : BTINTEL_PCIE_STATE_D3_COLD);
- data->gp0_received = false;
+ data->pm_sx_event = mesg.event;
start = ktime_get();
/* Refer: 6.4.11.7 -> Platform power management */
- btintel_pcie_wr_sleep_cntrl(data, dxstate);
- err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
- msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
- if (err == 0) {
- bt_dev_err(data->hdev,
- "Timeout (%u ms) on alive interrupt for D3 entry",
- BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
- return -EBUSY;
- }
+ err = btintel_pcie_set_dxstate(data, dxstate);
+
+ if (err)
+ return err;
bt_dev_dbg(data->hdev,
"device entered into d3 state from d0 in %lld us",
ktime_to_us(ktime_get() - start));
-
- return 0;
+ return err;
}
static int btintel_pcie_suspend(struct device *dev)
@@ -2580,21 +2681,50 @@ static int btintel_pcie_resume(struct device *dev)
start = ktime_get();
+ /* When the system enters S4 (hibernate) mode, bluetooth device loses
+ * power, which results in the erasure of its loaded firmware.
+ * Consequently, function level reset (flr) is required on system
+ * resume to bring the controller back into an operational state by
+ * initiating a new firmware download.
+ */
+
+ if (data->pm_sx_event == PM_EVENT_FREEZE ||
+ data->pm_sx_event == PM_EVENT_HIBERNATE) {
+ set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
+ btintel_pcie_reset(data->hdev);
+ return 0;
+ }
+
/* Refer: 6.4.11.7 -> Platform power management */
- btintel_pcie_wr_sleep_cntrl(data, BTINTEL_PCIE_STATE_D0);
- err = wait_event_timeout(data->gp0_wait_q, data->gp0_received,
- msecs_to_jiffies(BTINTEL_DEFAULT_INTR_TIMEOUT_MS));
+ err = btintel_pcie_set_dxstate(data, BTINTEL_PCIE_STATE_D0);
+
if (err == 0) {
- bt_dev_err(data->hdev,
- "Timeout (%u ms) on alive interrupt for D0 entry",
- BTINTEL_DEFAULT_INTR_TIMEOUT_MS);
- return -EBUSY;
+ bt_dev_dbg(data->hdev,
+ "device entered into d0 state from d3 in %lld us",
+ ktime_to_us(ktime_get() - start));
+ return err;
}
- bt_dev_dbg(data->hdev,
- "device entered into d0 state from d3 in %lld us",
- ktime_to_us(ktime_get() - start));
- return 0;
+ /* Trigger function level reset if the controller is in error
+ * state during resume() to bring back the controller to
+ * operational mode
+ */
+
+ data->boot_stage_cache = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_BOOT_STAGE_REG);
+ if (btintel_pcie_in_error(data) ||
+ btintel_pcie_in_device_halt(data)) {
+ bt_dev_err(data->hdev, "Controller in error state for D0 entry");
+ if (!test_and_set_bit(BTINTEL_PCIE_COREDUMP_INPROGRESS,
+ &data->flags)) {
+ data->dmp_hdr.trigger_reason =
+ BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT;
+ queue_work(data->workqueue, &data->rx_work);
+ }
+ set_bit(BTINTEL_PCIE_CORE_HALTED, &data->flags);
+ btintel_pcie_reset(data->hdev);
+ }
+ return err;
}
static const struct dev_pm_ops btintel_pcie_pm_ops = {
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 04b21f968ad3..e3d941ffef4a 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -158,6 +158,8 @@ enum msix_mbox_int_causes {
/* Default interrupt timeout in msec */
#define BTINTEL_DEFAULT_INTR_TIMEOUT_MS 3000
+#define BTINTEL_PCIE_DX_TRANSITION_MAX_RETRIES 3
+
/* The number of descriptors in TX queues */
#define BTINTEL_PCIE_TX_DESCS_COUNT 32
@@ -464,6 +466,7 @@ struct btintel_pcie_dump_header {
* @txq: TX Queue struct
* @rxq: RX Queue struct
* @alive_intr_ctxt: Alive interrupt context
+ * @pm_sx_event: PM event on which system got suspended
*/
struct btintel_pcie_data {
struct pci_dev *pdev;
@@ -513,6 +516,7 @@ struct btintel_pcie_data {
u32 alive_intr_ctxt;
struct btintel_pcie_dbgc dbgc;
struct btintel_pcie_dump_header dmp_hdr;
+ u8 pm_sx_event;
};
static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index 50abefba6d04..fba3ab6d30a5 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c
@@ -615,7 +615,6 @@ static void btmtksdio_txrx_work(struct work_struct *work)
sdio_release_host(bdev->func);
- pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
}
@@ -1270,6 +1269,12 @@ static void btmtksdio_reset(struct hci_dev *hdev)
sdio_claim_host(bdev->func);
+ /* set drv_pmctrl if BT is closed before doing reset */
+ if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state)) {
+ sdio_enable_func(bdev->func);
+ btmtksdio_drv_pmctrl(bdev);
+ }
+
sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL);
skb_queue_purge(&bdev->txq);
cancel_work_sync(&bdev->txrx_work);
@@ -1285,6 +1290,12 @@ static void btmtksdio_reset(struct hci_dev *hdev)
goto err;
}
+ /* set fw_pmctrl back if BT is closed after doing reset */
+ if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state)) {
+ btmtksdio_fw_pmctrl(bdev);
+ sdio_disable_func(bdev->func);
+ }
+
clear_bit(BTMTKSDIO_PATCH_ENABLED, &bdev->tx_state);
err:
sdio_release_host(bdev->func);
diff --git a/drivers/bluetooth/btmtkuart.c b/drivers/bluetooth/btmtkuart.c
index d9b90ea2ad38..27aa48ff3ac2 100644
--- a/drivers/bluetooth/btmtkuart.c
+++ b/drivers/bluetooth/btmtkuart.c
@@ -79,6 +79,7 @@ struct btmtkuart_dev {
u16 stp_dlen;
const struct btmtkuart_data *data;
+ struct hci_uart hu;
};
#define btmtkuart_is_standalone(bdev) \
@@ -368,7 +369,7 @@ static void btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count)
sz_left -= adv;
p_left += adv;
- bdev->rx_skb = h4_recv_buf(bdev->hdev, bdev->rx_skb, p_h4,
+ bdev->rx_skb = h4_recv_buf(&bdev->hu, bdev->rx_skb, p_h4,
sz_h4, mtk_recv_pkts,
ARRAY_SIZE(mtk_recv_pkts));
if (IS_ERR(bdev->rx_skb)) {
@@ -858,6 +859,7 @@ static int btmtkuart_probe(struct serdev_device *serdev)
}
bdev->hdev = hdev;
+ bdev->hu.hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, bdev);
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index d5153fed0518..3b1e9224e965 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -212,6 +212,7 @@ struct btnxpuart_dev {
struct ps_data psdata;
struct btnxpuart_data *nxp_data;
struct reset_control *pdn;
+ struct hci_uart hu;
};
#define NXP_V1_FW_REQ_PKT 0xa5
@@ -1756,7 +1757,7 @@ static size_t btnxpuart_receive_buf(struct serdev_device *serdev,
ps_start_timer(nxpdev);
- nxpdev->rx_skb = h4_recv_buf(nxpdev->hdev, nxpdev->rx_skb, data, count,
+ nxpdev->rx_skb = h4_recv_buf(&nxpdev->hu, nxpdev->rx_skb, data, count,
nxp_recv_pkts, ARRAY_SIZE(nxp_recv_pkts));
if (IS_ERR(nxpdev->rx_skb)) {
int err = PTR_ERR(nxpdev->rx_skb);
@@ -1875,6 +1876,7 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
reset_control_deassert(nxpdev->pdn);
nxpdev->hdev = hdev;
+ nxpdev->hu.hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, nxpdev);
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 6abd962502e3..5603b282f9bc 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -50,7 +50,7 @@
#define RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
#define RTL_CHIP_REV (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
-#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}})
+#define RTL_SEC_PROJ (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})
#define RTL_PATCH_SNIPPETS 0x01
#define RTL_PATCH_DUMMY_HEADER 0x02
@@ -72,6 +72,7 @@ enum btrtl_chip_id {
CHIP_ID_8851B = 36,
CHIP_ID_8922A = 44,
CHIP_ID_8852BT = 47,
+ CHIP_ID_8761C = 51,
};
struct id_table {
@@ -230,6 +231,14 @@ static const struct id_table ic_id_table[] = {
.cfg_name = "rtl_bt/rtl8761bu_config",
.hw_info = "rtl8761bu" },
+ /* 8761CU */
+ { IC_INFO(RTL_ROM_LMP_8761A, 0x0e, 0, HCI_USB),
+ .config_needed = false,
+ .has_rom_version = true,
+ .fw_name = "rtl_bt/rtl8761cu_fw",
+ .cfg_name = "rtl_bt/rtl8761cu_config",
+ .hw_info = "rtl8761cu" },
+
/* 8822C with UART interface */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xc, 0x8, HCI_UART),
.config_needed = true,
@@ -344,7 +353,8 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
(ic_id_table[i].hci_rev != hci_rev))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) &&
- (ic_id_table[i].hci_ver != hci_ver))
+ (ic_id_table[i].hci_ver != hci_ver) &&
+ (ic_id_table[i].hci_ver != 0))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) &&
(ic_id_table[i].hci_bus != hci_bus))
@@ -534,7 +544,6 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
{
struct rtl_epatch_header_v2 *hdr;
int rc;
- u8 reg_val[2];
u8 key_id;
u32 num_sections;
struct rtl_section *section;
@@ -549,14 +558,7 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
.len = btrtl_dev->fw_len - 7, /* Cut the tail */
};
- rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
- if (rc < 0)
- return -EIO;
- key_id = reg_val[0];
-
- rtl_dev_dbg(hdev, "%s: key id %u", __func__, key_id);
-
- btrtl_dev->key_id = key_id;
+ key_id = btrtl_dev->key_id;
hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
if (!hdr)
@@ -625,8 +627,10 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
len += entry->len;
}
- if (!len)
+ if (!len) {
+ kvfree(ptr);
return -EPERM;
+ }
*_buf = ptr;
return len;
@@ -668,6 +672,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
{ RTL_ROM_LMP_8851B, 36 }, /* 8851B */
{ RTL_ROM_LMP_8922A, 44 }, /* 8922A */
{ RTL_ROM_LMP_8852A, 47 }, /* 8852BT */
+ { RTL_ROM_LMP_8761A, 51 }, /* 8761C */
};
if (btrtl_dev->fw_len <= 8)
@@ -1068,6 +1073,8 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
u16 hci_rev, lmp_subver;
u8 hci_ver, lmp_ver, chip_type = 0;
int ret;
+ int rc;
+ u8 key_id;
u8 reg_val[2];
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
@@ -1178,6 +1185,14 @@ next:
goto err_free;
}
+ rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
+ if (rc < 0)
+ goto err_free;
+
+ key_id = reg_val[0];
+ btrtl_dev->key_id = key_id;
+ rtl_dev_info(hdev, "%s: key id %u", __func__, key_id);
+
btrtl_dev->fw_len = -EIO;
if (lmp_subver == RTL_ROM_LMP_8852A && hci_rev == 0x000c) {
snprintf(fw_name, sizeof(fw_name), "%s_v2.bin",
@@ -1200,7 +1215,7 @@ next:
goto err_free;
}
- if (btrtl_dev->ic_info->cfg_name) {
+ if (btrtl_dev->ic_info->cfg_name && !btrtl_dev->key_id) {
if (postfix) {
snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
btrtl_dev->ic_info->cfg_name, postfix);
@@ -1301,6 +1316,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
case CHIP_ID_8851B:
case CHIP_ID_8922A:
case CHIP_ID_8852BT:
+ case CHIP_ID_8761C:
hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
/* RTL8852C needs to transmit mSBC data continuously without
@@ -1520,6 +1536,8 @@ MODULE_FIRMWARE("rtl_bt/rtl8761b_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761b_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761bu_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761bu_config.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761cu_fw.bin");
+MODULE_FIRMWARE("rtl_bt/rtl8761cu_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821c_fw.bin");
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 5e9ebf0c5312..8ed3883ab8ee 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -504,6 +504,8 @@ static const struct usb_device_id quirks_table[] = {
/* Realtek 8821CE Bluetooth devices */
{ USB_DEVICE(0x13d3, 0x3529), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3533), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Realtek 8822CE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0xb00c), .driver_info = BTUSB_REALTEK |
@@ -585,6 +587,12 @@ static const struct usb_device_id quirks_table[] = {
/* Realtek 8852BT/8852BE-VT Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x8520), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe12f), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3618), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x13d3, 0x3619), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Realtek 8922AE Bluetooth devices */
{ USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK |
@@ -621,6 +629,8 @@ static const struct usb_device_id quirks_table[] = {
/* Additional MediaTek MT7920 Bluetooth devices */
{ USB_DEVICE(0x0489, 0xe134), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe135), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3620), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x13d3, 0x3621), .driver_info = BTUSB_MEDIATEK |
@@ -685,6 +695,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x0489, 0xe153), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x0489, 0xe170), .driver_info = BTUSB_MEDIATEK |
+ BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04ca, 0x3804), .driver_info = BTUSB_MEDIATEK |
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x04ca, 0x38e4), .driver_info = BTUSB_MEDIATEK |
@@ -781,6 +793,8 @@ static const struct usb_device_id quirks_table[] = {
BTUSB_WIDEBAND_SPEECH },
{ USB_DEVICE(0x2b89, 0x8761), .driver_info = BTUSB_REALTEK |
BTUSB_WIDEBAND_SPEECH },
+ { USB_DEVICE(0x2b89, 0x6275), .driver_info = BTUSB_REALTEK |
+ BTUSB_WIDEBAND_SPEECH },
/* Additional Realtek 8821AE Bluetooth devices */
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
@@ -1131,6 +1145,24 @@ static void btusb_qca_reset(struct hci_dev *hdev)
btusb_reset(hdev);
}
+static u8 btusb_classify_qca_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ /* Some Qualcomm controllers, e.g., QCNFA765 with WCN6855 chip, send debug
+ * packets as ACL frames with connection handle 0x2EDC. These are not real
+ * ACL packets and should be reclassified as HCI_DIAG_PKT to prevent
+ * "ACL packet for unknown connection handle 3804" errors.
+ */
+ if (skb->len >= 2) {
+ u16 handle = get_unaligned_le16(skb->data);
+
+ if (handle == 0x2EDC)
+ return HCI_DIAG_PKT;
+ }
+
+ /* Use default packet type for other packets */
+ return hci_skb_pkt_type(skb);
+}
+
static inline void btusb_free_frags(struct btusb_data *data)
{
unsigned long flags;
@@ -2711,9 +2743,21 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
static void btusb_mtk_claim_iso_intf(struct btusb_data *data)
{
- struct btmtk_data *btmtk_data = hci_get_priv(data->hdev);
+ struct btmtk_data *btmtk_data;
int err;
+ if (!data->hdev)
+ return;
+
+ btmtk_data = hci_get_priv(data->hdev);
+ if (!btmtk_data)
+ return;
+
+ if (!btmtk_data->isopkt_intf) {
+ bt_dev_err(data->hdev, "Can't claim NULL iso interface");
+ return;
+ }
+
/*
* The function usb_driver_claim_interface() is documented to need
* locks held if it's not called from a probe routine. The code here
@@ -2735,17 +2779,30 @@ static void btusb_mtk_claim_iso_intf(struct btusb_data *data)
static void btusb_mtk_release_iso_intf(struct hci_dev *hdev)
{
- struct btmtk_data *btmtk_data = hci_get_priv(hdev);
+ struct btmtk_data *btmtk_data;
+
+ if (!hdev)
+ return;
+
+ btmtk_data = hci_get_priv(hdev);
+ if (!btmtk_data)
+ return;
if (test_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags)) {
usb_kill_anchored_urbs(&btmtk_data->isopkt_anchor);
clear_bit(BTMTK_ISOPKT_RUNNING, &btmtk_data->flags);
- dev_kfree_skb_irq(btmtk_data->isopkt_skb);
- btmtk_data->isopkt_skb = NULL;
- usb_set_intfdata(btmtk_data->isopkt_intf, NULL);
- usb_driver_release_interface(&btusb_driver,
- btmtk_data->isopkt_intf);
+ if (btmtk_data->isopkt_skb) {
+ dev_kfree_skb_irq(btmtk_data->isopkt_skb);
+ btmtk_data->isopkt_skb = NULL;
+ }
+
+ if (btmtk_data->isopkt_intf) {
+ usb_set_intfdata(btmtk_data->isopkt_intf, NULL);
+ usb_driver_release_interface(&btusb_driver,
+ btmtk_data->isopkt_intf);
+ btmtk_data->isopkt_intf = NULL;
+ }
}
clear_bit(BTMTK_ISOPKT_OVER_INTR, &btmtk_data->flags);
@@ -2783,6 +2840,19 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
btusb_stop_traffic(data);
usb_kill_anchored_urbs(&data->tx_anchor);
+ /* Toggle the hard reset line. The MediaTek device is going to
+ * yank itself off the USB and then replug. The cleanup is handled
+ * correctly on the way out (standard USB disconnect), and the new
+ * device is detected cleanly and bound to the driver again like
+ * it should be.
+ */
+ if (data->reset_gpio) {
+ gpiod_set_value_cansleep(data->reset_gpio, 1);
+ msleep(200);
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
+ return 0;
+ }
+
err = btmtk_usb_subsys_reset(hdev, btmtk_data->dev_id);
usb_queue_reset_device(data->intf);
@@ -3236,6 +3306,7 @@ static const struct qca_device_info qca_devices_table[] = {
static const struct qca_custom_firmware qca_custom_btfws[] = {
{ 0x00130201, 0x030A, "QCA2066" },
+ { 0x00130201, 0x030B, "QCA2066" },
{ },
};
@@ -4201,6 +4272,7 @@ static int btusb_probe(struct usb_interface *intf,
data->recv_acl = btusb_recv_acl_qca;
hci_devcd_register(hdev, btusb_coredump_qca, btusb_dump_hdr_qca, NULL);
data->setup_on_usb = btusb_setup_qca;
+ hdev->classify_pkt_type = btusb_classify_qca_pkt_type;
hdev->shutdown = btusb_shutdown_qca;
hdev->set_bdaddr = btusb_set_bdaddr_wcn6855;
hdev->reset = btusb_qca_reset;
@@ -4361,6 +4433,11 @@ static void btusb_disconnect(struct usb_interface *intf)
hci_unregister_dev(hdev);
+ if (data->oob_wake_irq)
+ device_init_wakeup(&data->udev->dev, false);
+ if (data->reset_gpio)
+ gpiod_put(data->reset_gpio);
+
if (intf == data->intf) {
if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc);
@@ -4371,17 +4448,11 @@ static void btusb_disconnect(struct usb_interface *intf)
usb_driver_release_interface(&btusb_driver, data->diag);
usb_driver_release_interface(&btusb_driver, data->intf);
} else if (intf == data->diag) {
- usb_driver_release_interface(&btusb_driver, data->intf);
if (data->isoc)
usb_driver_release_interface(&btusb_driver, data->isoc);
+ usb_driver_release_interface(&btusb_driver, data->intf);
}
- if (data->oob_wake_irq)
- device_init_wakeup(&data->udev->dev, false);
-
- if (data->reset_gpio)
- gpiod_put(data->reset_gpio);
-
hci_free_dev(hdev);
}
diff --git a/drivers/bluetooth/hci_ag6xx.c b/drivers/bluetooth/hci_ag6xx.c
index 2d40302409ff..94588676510f 100644
--- a/drivers/bluetooth/hci_ag6xx.c
+++ b/drivers/bluetooth/hci_ag6xx.c
@@ -105,7 +105,7 @@ static int ag6xx_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- ag6xx->rx_skb = h4_recv_buf(hu->hdev, ag6xx->rx_skb, data, count,
+ ag6xx->rx_skb = h4_recv_buf(hu, ag6xx->rx_skb, data, count,
ag6xx_recv_pkts,
ARRAY_SIZE(ag6xx_recv_pkts));
if (IS_ERR(ag6xx->rx_skb)) {
diff --git a/drivers/bluetooth/hci_aml.c b/drivers/bluetooth/hci_aml.c
index 707e90f80130..b1f32c5a8a3f 100644
--- a/drivers/bluetooth/hci_aml.c
+++ b/drivers/bluetooth/hci_aml.c
@@ -650,7 +650,7 @@ static int aml_recv(struct hci_uart *hu, const void *data, int count)
struct aml_data *aml_data = hu->priv;
int err;
- aml_data->rx_skb = h4_recv_buf(hu->hdev, aml_data->rx_skb, data, count,
+ aml_data->rx_skb = h4_recv_buf(hu, aml_data->rx_skb, data, count,
aml_recv_pkts,
ARRAY_SIZE(aml_recv_pkts));
if (IS_ERR(aml_data->rx_skb)) {
diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c
index dbfe34664633..8d2b5e7f0d6a 100644
--- a/drivers/bluetooth/hci_ath.c
+++ b/drivers/bluetooth/hci_ath.c
@@ -191,7 +191,7 @@ static int ath_recv(struct hci_uart *hu, const void *data, int count)
{
struct ath_struct *ath = hu->priv;
- ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
+ ath->rx_skb = h4_recv_buf(hu, ath->rx_skb, data, count,
ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
if (IS_ERR(ath->rx_skb)) {
int err = PTR_ERR(ath->rx_skb);
diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c
index f96617b85d87..9286a5f40f55 100644
--- a/drivers/bluetooth/hci_bcm.c
+++ b/drivers/bluetooth/hci_bcm.c
@@ -326,7 +326,6 @@ static irqreturn_t bcm_host_wake(int irq, void *data)
bt_dev_dbg(bdev, "Host wake IRQ");
pm_runtime_get(bdev->dev);
- pm_runtime_mark_last_busy(bdev->dev);
pm_runtime_put_autosuspend(bdev->dev);
return IRQ_HANDLED;
@@ -698,7 +697,7 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
+ bcm->rx_skb = h4_recv_buf(hu, bcm->rx_skb, data, count,
bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
if (IS_ERR(bcm->rx_skb)) {
int err = PTR_ERR(bcm->rx_skb);
@@ -710,7 +709,6 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
mutex_lock(&bcm_device_lock);
if (bcm->dev && bcm_device_exists(bcm->dev)) {
pm_runtime_get(bcm->dev->dev);
- pm_runtime_mark_last_busy(bcm->dev->dev);
pm_runtime_put_autosuspend(bcm->dev->dev);
}
mutex_unlock(&bcm_device_lock);
@@ -748,10 +746,8 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
skb = skb_dequeue(&bcm->txq);
- if (bdev) {
- pm_runtime_mark_last_busy(bdev->dev);
+ if (bdev)
pm_runtime_put_autosuspend(bdev->dev);
- }
mutex_unlock(&bcm_device_lock);
diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
index 9070e31a68bf..ec017df8572c 100644
--- a/drivers/bluetooth/hci_h4.c
+++ b/drivers/bluetooth/hci_h4.c
@@ -112,7 +112,7 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count,
+ h4->rx_skb = h4_recv_buf(hu, h4->rx_skb, data, count,
h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
if (IS_ERR(h4->rx_skb)) {
int err = PTR_ERR(h4->rx_skb);
@@ -151,12 +151,12 @@ int __exit h4_deinit(void)
return hci_uart_unregister_proto(&h4p);
}
-struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+struct sk_buff *h4_recv_buf(struct hci_uart *hu, struct sk_buff *skb,
const unsigned char *buffer, int count,
const struct h4_recv_pkt *pkts, int pkts_count)
{
- struct hci_uart *hu = hci_get_drvdata(hdev);
u8 alignment = hu->alignment ? hu->alignment : 1;
+ struct hci_dev *hdev = hu->hdev;
/* Check for error from previous call */
if (IS_ERR(skb))
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index d0d4420c1a0f..96e20a66ecd1 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -7,6 +7,8 @@
*/
#include <linux/acpi.h>
+#include <linux/bitrev.h>
+#include <linux/crc-ccitt.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
@@ -58,6 +60,7 @@ enum {
H5_TX_ACK_REQ, /* Pending ack to send */
H5_WAKEUP_DISABLE, /* Device cannot wake host */
H5_HW_FLOW_CONTROL, /* Use HW flow control */
+ H5_CRC, /* Use CRC */
};
struct h5 {
@@ -141,8 +144,8 @@ static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
static u8 h5_cfg_field(struct h5 *h5)
{
- /* Sliding window size (first 3 bits) */
- return h5->tx_win & 0x07;
+ /* Sliding window size (first 3 bits) and CRC request (fifth bit). */
+ return (h5->tx_win & 0x07) | 0x10;
}
static void h5_timed_event(struct timer_list *t)
@@ -213,7 +216,6 @@ static void h5_peer_reset(struct hci_uart *hu)
static int h5_open(struct hci_uart *hu)
{
struct h5 *h5;
- const unsigned char sync[] = { 0x01, 0x7e };
BT_DBG("hu %p", hu);
@@ -243,9 +245,11 @@ static int h5_open(struct hci_uart *hu)
set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
- /* Send initial sync request */
- h5_link_control(hu, sync, sizeof(sync));
- mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
+ /*
+ * Wait one jiffy because the UART layer won't set HCI_UART_PROTO_READY,
+ * which allows us to send link packets, until this function returns.
+ */
+ mod_timer(&h5->timer, jiffies + 1);
return 0;
}
@@ -360,8 +364,10 @@ static void h5_handle_internal_rx(struct hci_uart *hu)
h5_link_control(hu, conf_rsp, 2);
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_rsp, 2) == 0) {
- if (H5_HDR_LEN(hdr) > 2)
+ if (H5_HDR_LEN(hdr) > 2) {
h5->tx_win = (data[2] & 0x07);
+ assign_bit(H5_CRC, &h5->flags, data[2] & 0x10);
+ }
BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
h5->state = H5_ACTIVE;
hci_uart_init_ready(hu);
@@ -425,7 +431,24 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
static int h5_rx_crc(struct hci_uart *hu, unsigned char c)
{
- h5_complete_rx_pkt(hu);
+ struct h5 *h5 = hu->priv;
+ const unsigned char *hdr = h5->rx_skb->data;
+ u16 crc;
+ __be16 crc_be;
+
+ crc = crc_ccitt(0xffff, hdr, 4 + H5_HDR_LEN(hdr));
+ crc = bitrev16(crc);
+
+ crc_be = cpu_to_be16(crc);
+
+ if (memcmp(&crc_be, hdr + 4 + H5_HDR_LEN(hdr), 2) != 0) {
+ bt_dev_err(hu->hdev, "Received packet with invalid CRC");
+ h5_reset_rx(h5);
+ } else {
+ /* Remove CRC bytes */
+ skb_trim(h5->rx_skb, 4 + H5_HDR_LEN(hdr));
+ h5_complete_rx_pkt(hu);
+ }
return 0;
}
@@ -556,6 +579,7 @@ static void h5_reset_rx(struct h5 *h5)
h5->rx_func = h5_rx_delimiter;
h5->rx_pending = 0;
clear_bit(H5_RX_ESC, &h5->flags);
+ clear_bit(H5_CRC, &h5->flags);
}
static int h5_recv(struct hci_uart *hu, const void *data, int count)
@@ -592,7 +616,6 @@ static int h5_recv(struct hci_uart *hu, const void *data, int count)
if (hu->serdev) {
pm_runtime_get(&hu->serdev->dev);
- pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
}
@@ -634,7 +657,6 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
if (hu->serdev) {
pm_runtime_get_sync(&hu->serdev->dev);
- pm_runtime_mark_last_busy(&hu->serdev->dev);
pm_runtime_put_autosuspend(&hu->serdev->dev);
}
@@ -686,6 +708,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
struct h5 *h5 = hu->priv;
struct sk_buff *nskb;
u8 hdr[4];
+ u16 crc;
int i;
if (!valid_packet_type(pkt_type)) {
@@ -713,6 +736,7 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
/* Reliable packet? */
if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) {
hdr[0] |= 1 << 7;
+ hdr[0] |= (test_bit(H5_CRC, &h5->flags) && 1) << 6;
hdr[0] |= h5->tx_seq;
h5->tx_seq = (h5->tx_seq + 1) % 8;
}
@@ -732,6 +756,15 @@ static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
for (i = 0; i < len; i++)
h5_slip_one_byte(nskb, data[i]);
+ if (H5_HDR_CRC(hdr)) {
+ crc = crc_ccitt(0xffff, hdr, 4);
+ crc = crc_ccitt(crc, data, len);
+ crc = bitrev16(crc);
+
+ h5_slip_one_byte(nskb, (crc >> 8) & 0xff);
+ h5_slip_one_byte(nskb, crc & 0xff);
+ }
+
h5_slip_delim(nskb);
return nskb;
diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c
index 9b353c3d6442..20baf2895dec 100644
--- a/drivers/bluetooth/hci_intel.c
+++ b/drivers/bluetooth/hci_intel.c
@@ -280,7 +280,6 @@ static irqreturn_t intel_irq(int irq, void *dev_id)
/* Host/Controller are now LPM resumed, trigger a new delayed suspend */
pm_runtime_get(&idev->pdev->dev);
- pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
return IRQ_HANDLED;
@@ -371,7 +370,6 @@ static void intel_busy_work(struct work_struct *work)
list_for_each_entry(idev, &intel_device_list, list) {
if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) {
pm_runtime_get(&idev->pdev->dev);
- pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
break;
}
@@ -972,7 +970,7 @@ static int intel_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- intel->rx_skb = h4_recv_buf(hu->hdev, intel->rx_skb, data, count,
+ intel->rx_skb = h4_recv_buf(hu, intel->rx_skb, data, count,
intel_recv_pkts,
ARRAY_SIZE(intel_recv_pkts));
if (IS_ERR(intel->rx_skb)) {
@@ -1003,7 +1001,6 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
list_for_each_entry(idev, &intel_device_list, list) {
if (hu->tty->dev->parent == idev->pdev->dev.parent) {
pm_runtime_get_sync(&idev->pdev->dev);
- pm_runtime_mark_last_busy(&idev->pdev->dev);
pm_runtime_put_autosuspend(&idev->pdev->dev);
break;
}
diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
index 7044c86325ce..6f4e25917b86 100644
--- a/drivers/bluetooth/hci_ll.c
+++ b/drivers/bluetooth/hci_ll.c
@@ -429,7 +429,7 @@ static int ll_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- ll->rx_skb = h4_recv_buf(hu->hdev, ll->rx_skb, data, count,
+ ll->rx_skb = h4_recv_buf(hu, ll->rx_skb, data, count,
ll_recv_pkts, ARRAY_SIZE(ll_recv_pkts));
if (IS_ERR(ll->rx_skb)) {
int err = PTR_ERR(ll->rx_skb);
diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
index e08222395772..8767522ec4c6 100644
--- a/drivers/bluetooth/hci_mrvl.c
+++ b/drivers/bluetooth/hci_mrvl.c
@@ -264,9 +264,9 @@ static int mrvl_recv(struct hci_uart *hu, const void *data, int count)
!test_bit(STATE_FW_LOADED, &mrvl->flags))
return count;
- mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
- mrvl_recv_pkts,
- ARRAY_SIZE(mrvl_recv_pkts));
+ mrvl->rx_skb = h4_recv_buf(hu, mrvl->rx_skb, data, count,
+ mrvl_recv_pkts,
+ ARRAY_SIZE(mrvl_recv_pkts));
if (IS_ERR(mrvl->rx_skb)) {
int err = PTR_ERR(mrvl->rx_skb);
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
diff --git a/drivers/bluetooth/hci_nokia.c b/drivers/bluetooth/hci_nokia.c
index cd7575c20f65..1e65b541f8ad 100644
--- a/drivers/bluetooth/hci_nokia.c
+++ b/drivers/bluetooth/hci_nokia.c
@@ -624,8 +624,8 @@ static int nokia_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb, data, count,
- nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
+ btdev->rx_skb = h4_recv_buf(hu, btdev->rx_skb, data, count,
+ nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
if (IS_ERR(btdev->rx_skb)) {
err = PTR_ERR(btdev->rx_skb);
dev_err(dev, "Frame reassembly failed (%d)", err);
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 4cff4d9be313..888176b0faa9 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1277,7 +1277,7 @@ static int qca_recv(struct hci_uart *hu, const void *data, int count)
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
- qca->rx_skb = h4_recv_buf(hu->hdev, qca->rx_skb, data, count,
+ qca->rx_skb = h4_recv_buf(hu, qca->rx_skb, data, count,
qca_recv_pkts, ARRAY_SIZE(qca_recv_pkts));
if (IS_ERR(qca->rx_skb)) {
int err = PTR_ERR(qca->rx_skb);
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index cbbe79b241ce..48ac7ca9334e 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -162,7 +162,7 @@ struct h4_recv_pkt {
int h4_init(void);
int h4_deinit(void);
-struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+struct sk_buff *h4_recv_buf(struct hci_uart *hu, struct sk_buff *skb,
const unsigned char *buffer, int count,
const struct h4_recv_pkt *pkts, int pkts_count);
#endif
diff --git a/drivers/bus/fsl-mc/mc-sys.c b/drivers/bus/fsl-mc/mc-sys.c
index b22c59d57c8f..31037f41893e 100644
--- a/drivers/bus/fsl-mc/mc-sys.c
+++ b/drivers/bus/fsl-mc/mc-sys.c
@@ -248,7 +248,7 @@ int mc_send_command(struct fsl_mc_io *mc_io, struct fsl_mc_command *cmd)
enum mc_cmd_status status;
unsigned long irq_flags = 0;
- if (in_irq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
+ if (in_hardirq() && !(mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL))
return -EINVAL;
if (mc_io->flags & FSL_MC_IO_ATOMIC_CONTEXT_PORTAL)
diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c
index aa2b135e3ee2..6d6ac409efcf 100644
--- a/drivers/char/hw_random/bcm2835-rng.c
+++ b/drivers/char/hw_random/bcm2835-rng.c
@@ -138,12 +138,11 @@ static const struct of_device_id bcm2835_rng_of_match[] = {
{ .compatible = "brcm,bcm6368-rng"},
{},
};
+MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match);
static int bcm2835_rng_probe(struct platform_device *pdev)
{
- const struct bcm2835_rng_of_data *of_data;
struct device *dev = &pdev->dev;
- const struct of_device_id *rng_id;
struct bcm2835_rng_priv *priv;
int err;
@@ -171,12 +170,10 @@ static int bcm2835_rng_probe(struct platform_device *pdev)
priv->rng.cleanup = bcm2835_rng_cleanup;
if (dev_of_node(dev)) {
- rng_id = of_match_node(bcm2835_rng_of_match, dev->of_node);
- if (!rng_id)
- return -EINVAL;
+ const struct bcm2835_rng_of_data *of_data;
/* Check for rng init function, execute it */
- of_data = rng_id->data;
+ of_data = of_device_get_match_data(dev);
if (of_data)
priv->mask_interrupts = of_data->mask_interrupts;
}
@@ -191,8 +188,6 @@ static int bcm2835_rng_probe(struct platform_device *pdev)
return err;
}
-MODULE_DEVICE_TABLE(of, bcm2835_rng_of_match);
-
static const struct platform_device_id bcm2835_rng_devtype[] = {
{ .name = "bcm2835-rng" },
{ .name = "bcm63xx-rng" },
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 018316f54621..96d7fe41b373 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -341,6 +341,9 @@ static ssize_t rng_current_store(struct device *dev,
if (sysfs_streq(buf, "")) {
err = enable_best_rng();
+ } else if (sysfs_streq(buf, "none")) {
+ cur_rng_set_by_user = 1;
+ drop_current_rng();
} else {
list_for_each_entry(rng, &rng_list, list) {
if (sysfs_streq(rng->name, buf)) {
@@ -392,7 +395,7 @@ static ssize_t rng_available_show(struct device *dev,
strlcat(buf, rng->name, PAGE_SIZE);
strlcat(buf, " ", PAGE_SIZE);
}
- strlcat(buf, "\n", PAGE_SIZE);
+ strlcat(buf, "none\n", PAGE_SIZE);
mutex_unlock(&rng_mutex);
return strlen(buf);
@@ -542,10 +545,10 @@ int hwrng_register(struct hwrng *rng)
init_completion(&rng->dying);
/* Adjust quality field to always have a proper value */
- rng->quality = min_t(u16, min_t(u16, default_quality, 1024), rng->quality ?: 1024);
+ rng->quality = min3(default_quality, 1024, rng->quality ?: 1024);
- if (!current_rng ||
- (!cur_rng_set_by_user && rng->quality > current_rng->quality)) {
+ if (!cur_rng_set_by_user &&
+ (!current_rng || rng->quality > current_rng->quality)) {
/*
* Set new rng as current as the new rng source
* provides better entropy quality and was not
diff --git a/drivers/char/hw_random/s390-trng.c b/drivers/char/hw_random/s390-trng.c
index d27e32e9bfee..3024d5e9fd61 100644
--- a/drivers/char/hw_random/s390-trng.c
+++ b/drivers/char/hw_random/s390-trng.c
@@ -9,8 +9,7 @@
* Author(s): Harald Freudenberger <freude@de.ibm.com>
*/
-#define KMSG_COMPONENT "trng"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "trng: " fmt
#include <linux/hw_random.h>
#include <linux/kernel.h>
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 70e55f5ff85e..5459ffdde8dc 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -275,8 +275,7 @@ void debug_timestamp(struct smi_info *smi_info, char *msg)
struct timespec64 t;
ktime_get_ts64(&t);
- dev_dbg(smi_info->io.dev, "**%s: %lld.%9.9ld\n",
- msg, t.tv_sec, t.tv_nsec);
+ dev_dbg(smi_info->io.dev, "**%s: %ptSp\n", msg, &t);
}
#else
#define debug_timestamp(smi_info, x)
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 1b63f7d2fcda..ef1582a029f4 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -1083,10 +1083,8 @@ static int sender(void *send_info, struct ipmi_smi_msg *msg)
struct timespec64 t;
ktime_get_real_ts64(&t);
- dev_dbg(&ssif_info->client->dev,
- "**Enqueue %02x %02x: %lld.%6.6ld\n",
- msg->data[0], msg->data[1],
- (long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC);
+ dev_dbg(&ssif_info->client->dev, "**Enqueue %02x %02x: %ptSp\n",
+ msg->data[0], msg->data[1], &t);
}
return IPMI_CC_NO_ERROR;
}
diff --git a/drivers/char/random.c b/drivers/char/random.c
index b8b24b6ed3fe..bab03c7c4194 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -259,8 +259,8 @@ static void crng_reseed(struct work_struct *work)
u8 key[CHACHA_KEY_SIZE];
/* Immediately schedule the next reseeding, so that it fires sooner rather than later. */
- if (likely(system_unbound_wq))
- queue_delayed_work(system_unbound_wq, &next_reseed, crng_reseed_interval());
+ if (likely(system_dfl_wq))
+ queue_delayed_work(system_dfl_wq, &next_reseed, crng_reseed_interval());
extract_entropy(key, sizeof(key));
@@ -427,7 +427,7 @@ static void _get_random_bytes(void *buf, size_t len)
/*
* This returns random bytes in arbitrary quantities. The quality of the
- * random bytes is good as /dev/urandom. In order to ensure that the
+ * random bytes is as good as /dev/urandom. In order to ensure that the
* randomness provided by this function is okay, the function
* wait_for_random_bytes() should be called and return 0 at least once
* at any point prior.
@@ -491,7 +491,7 @@ out_zero_chacha:
/*
* Batched entropy returns random integers. The quality of the random
- * number is good as /dev/urandom. In order to ensure that the randomness
+ * number is as good as /dev/urandom. In order to ensure that the randomness
* provided by this function is okay, the function wait_for_random_bytes()
* should be called and return 0 at least once at any point prior.
*/
@@ -636,7 +636,7 @@ enum {
};
static struct {
- struct blake2s_state hash;
+ struct blake2s_ctx hash;
spinlock_t lock;
unsigned int init_bits;
} input_pool = {
@@ -701,7 +701,7 @@ static void extract_entropy(void *buf, size_t len)
/* next_key = HASHPRF(seed, RDSEED || 0) */
block.counter = 0;
- blake2s(next_key, (u8 *)&block, seed, sizeof(next_key), sizeof(block), sizeof(seed));
+ blake2s(seed, sizeof(seed), (const u8 *)&block, sizeof(block), next_key, sizeof(next_key));
blake2s_init_key(&input_pool.hash, BLAKE2S_HASH_SIZE, next_key, sizeof(next_key));
spin_unlock_irqrestore(&input_pool.lock, flags);
@@ -711,7 +711,7 @@ static void extract_entropy(void *buf, size_t len)
i = min_t(size_t, len, BLAKE2S_HASH_SIZE);
/* output = HASHPRF(seed, RDSEED || ++counter) */
++block.counter;
- blake2s(buf, (u8 *)&block, seed, i, sizeof(block), sizeof(seed));
+ blake2s(seed, sizeof(seed), (const u8 *)&block, sizeof(block), buf, i);
len -= i;
buf += i;
}
@@ -741,8 +741,8 @@ static void __cold _credit_init_bits(size_t bits)
if (orig < POOL_READY_BITS && new >= POOL_READY_BITS) {
crng_reseed(NULL); /* Sets crng_init to CRNG_READY under base_crng.lock. */
- if (static_key_initialized && system_unbound_wq)
- queue_work(system_unbound_wq, &set_ready);
+ if (system_dfl_wq)
+ queue_work(system_dfl_wq, &set_ready);
atomic_notifier_call_chain(&random_ready_notifier, 0, NULL);
#ifdef CONFIG_VDSO_GETRANDOM
WRITE_ONCE(vdso_k_rng_data->is_ready, true);
@@ -794,7 +794,7 @@ static void __cold _credit_init_bits(size_t bits)
*
* add_bootloader_randomness() is called by bootloader drivers, such as EFI
* and device tree, and credits its input depending on whether or not the
- * command line option 'random.trust_bootloader'.
+ * command line option 'random.trust_bootloader' is set.
*
* add_vmfork_randomness() adds a unique (but not necessarily secret) ID
* representing the current instance of a VM to the pool, without crediting,
@@ -915,9 +915,8 @@ void __init random_init(void)
add_latent_entropy();
/*
- * If we were initialized by the cpu or bootloader before jump labels
- * or workqueues are initialized, then we should enable the static
- * branch here, where it's guaranteed that these have been initialized.
+ * If we were initialized by the cpu or bootloader before workqueues
+ * are initialized, then we should enable the static branch here.
*/
if (!static_branch_likely(&crng_is_ready) && crng_init >= CRNG_READY)
crng_set_ready(NULL);
@@ -1296,6 +1295,7 @@ static void __cold try_to_generate_entropy(void)
struct entropy_timer_state *stack = PTR_ALIGN((void *)stack_bytes, SMP_CACHE_BYTES);
unsigned int i, num_different = 0;
unsigned long last = random_get_entropy();
+ cpumask_var_t timer_cpus;
int cpu = -1;
for (i = 0; i < NUM_TRIAL_SAMPLES - 1; ++i) {
@@ -1310,13 +1310,15 @@ static void __cold try_to_generate_entropy(void)
atomic_set(&stack->samples, 0);
timer_setup_on_stack(&stack->timer, entropy_timer, 0);
+ if (!alloc_cpumask_var(&timer_cpus, GFP_KERNEL))
+ goto out;
+
while (!crng_ready() && !signal_pending(current)) {
/*
* Check !timer_pending() and then ensure that any previous callback has finished
* executing by checking timer_delete_sync_try(), before queueing the next one.
*/
if (!timer_pending(&stack->timer) && timer_delete_sync_try(&stack->timer) >= 0) {
- struct cpumask timer_cpus;
unsigned int num_cpus;
/*
@@ -1326,19 +1328,19 @@ static void __cold try_to_generate_entropy(void)
preempt_disable();
/* Only schedule callbacks on timer CPUs that are online. */
- cpumask_and(&timer_cpus, housekeeping_cpumask(HK_TYPE_TIMER), cpu_online_mask);
- num_cpus = cpumask_weight(&timer_cpus);
+ cpumask_and(timer_cpus, housekeeping_cpumask(HK_TYPE_TIMER), cpu_online_mask);
+ num_cpus = cpumask_weight(timer_cpus);
/* In very bizarre case of misconfiguration, fallback to all online. */
if (unlikely(num_cpus == 0)) {
- timer_cpus = *cpu_online_mask;
- num_cpus = cpumask_weight(&timer_cpus);
+ *timer_cpus = *cpu_online_mask;
+ num_cpus = cpumask_weight(timer_cpus);
}
/* Basic CPU round-robin, which avoids the current CPU. */
do {
- cpu = cpumask_next(cpu, &timer_cpus);
+ cpu = cpumask_next(cpu, timer_cpus);
if (cpu >= nr_cpu_ids)
- cpu = cpumask_first(&timer_cpus);
+ cpu = cpumask_first(timer_cpus);
} while (cpu == smp_processor_id() && num_cpus > 1);
/* Expiring the timer at `jiffies` means it's the next tick. */
@@ -1354,6 +1356,8 @@ static void __cold try_to_generate_entropy(void)
}
mix_pool_bytes(&stack->entropy, sizeof(stack->entropy));
+ free_cpumask_var(timer_cpus);
+out:
timer_delete_sync(&stack->timer);
timer_destroy_on_stack(&stack->timer);
}
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index e25daf2396d3..082b910ddf0d 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -231,42 +231,6 @@ struct tpm_chip *tpm_default_chip(void)
EXPORT_SYMBOL_GPL(tpm_default_chip);
/**
- * tpm_find_get_ops() - find and reserve a TPM chip
- * @chip: a &struct tpm_chip instance, %NULL for the default chip
- *
- * Finds a TPM chip and reserves its class device and operations. The chip must
- * be released with tpm_put_ops() after use.
- * This function is for internal use only. It supports existing TPM callers
- * by accepting NULL, but those callers should be converted to pass in a chip
- * directly.
- *
- * Return:
- * A reserved &struct tpm_chip instance.
- * %NULL if a chip is not found.
- * %NULL if the chip is not available.
- */
-struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip)
-{
- int rc;
-
- if (chip) {
- if (!tpm_try_get_ops(chip))
- return chip;
- return NULL;
- }
-
- chip = tpm_default_chip();
- if (!chip)
- return NULL;
- rc = tpm_try_get_ops(chip);
- /* release additional reference we got from tpm_default_chip() */
- put_device(&chip->dev);
- if (rc)
- return NULL;
- return chip;
-}
-
-/**
* tpm_dev_release() - free chip memory and the device number
* @dev: the character device for the TPM chip
*
@@ -282,7 +246,6 @@ static void tpm_dev_release(struct device *dev)
kfree(chip->work_space.context_buf);
kfree(chip->work_space.session_buf);
- kfree(chip->allocated_banks);
#ifdef CONFIG_TCG_TPM2_HMAC
kfree(chip->auth);
#endif
diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c
index f2a5e09257dd..f942c0c8e402 100644
--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -275,7 +275,8 @@ void tpm_common_release(struct file *file, struct file_priv *priv)
int __init tpm_dev_common_init(void)
{
- tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM, 0);
+ tpm_dev_wq = alloc_workqueue("tpm_dev_wq", WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
return !tpm_dev_wq ? -ENOMEM : 0;
}
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index c9f173001d0e..f745a098908b 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -313,10 +313,13 @@ int tpm_is_tpm2(struct tpm_chip *chip)
{
int rc;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0;
tpm_put_ops(chip);
@@ -338,10 +341,13 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
{
int rc;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2)
rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL);
else
@@ -369,10 +375,13 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
int rc;
int i;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
for (i = 0; i < chip->nr_allocated_banks; i++) {
if (digests[i].alg_id != chip->allocated_banks[i].alg_id) {
rc = -EINVAL;
@@ -492,10 +501,13 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max)
if (!out || max > TPM_MAX_RNG_DATA)
return -EINVAL;
- chip = tpm_find_get_ops(chip);
if (!chip)
return -ENODEV;
+ rc = tpm_try_get_ops(chip);
+ if (rc)
+ return rc;
+
if (chip->flags & TPM_CHIP_FLAG_TPM2)
rc = tpm2_get_random(chip, out, max);
else
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 2726bd38e5ac..02c07fef41ba 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -267,7 +267,6 @@ static inline void tpm_msleep(unsigned int delay_msec)
int tpm_chip_bootstrap(struct tpm_chip *chip);
int tpm_chip_start(struct tpm_chip *chip);
void tpm_chip_stop(struct tpm_chip *chip);
-struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip);
struct tpm_chip *tpm_chip_alloc(struct device *dev,
const struct tpm_class_ops *ops);
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index cf64c7385105..b49a790f1bd5 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -799,11 +799,6 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
*/
int tpm1_get_pcr_allocation(struct tpm_chip *chip)
{
- chip->allocated_banks = kcalloc(1, sizeof(*chip->allocated_banks),
- GFP_KERNEL);
- if (!chip->allocated_banks)
- return -ENOMEM;
-
chip->allocated_banks[0].alg_id = TPM_ALG_SHA1;
chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1];
chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1;
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 7d77f6fbc152..dd502322f499 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -18,7 +18,7 @@ static bool disable_pcr_integrity;
module_param(disable_pcr_integrity, bool, 0444);
MODULE_PARM_DESC(disable_pcr_integrity, "Disable integrity protection of TPM2_PCR_Extend");
-static struct tpm2_hash tpm2_hash_map[] = {
+struct tpm2_hash tpm2_hash_map[] = {
{HASH_ALGO_SHA1, TPM_ALG_SHA1},
{HASH_ALGO_SHA256, TPM_ALG_SHA256},
{HASH_ALGO_SHA384, TPM_ALG_SHA384},
@@ -26,6 +26,18 @@ static struct tpm2_hash tpm2_hash_map[] = {
{HASH_ALGO_SM3_256, TPM_ALG_SM3_256},
};
+int tpm2_find_hash_alg(unsigned int crypto_id)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++)
+ if (crypto_id == tpm2_hash_map[i].crypto_id)
+ return tpm2_hash_map[i].tpm_id;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(tpm2_find_hash_alg);
+
int tpm2_get_timeouts(struct tpm_chip *chip)
{
chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
@@ -538,11 +550,9 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
nr_possible_banks = be32_to_cpup(
(__be32 *)&buf.data[TPM_HEADER_SIZE + 5]);
-
- chip->allocated_banks = kcalloc(nr_possible_banks,
- sizeof(*chip->allocated_banks),
- GFP_KERNEL);
- if (!chip->allocated_banks) {
+ if (nr_possible_banks > TPM2_MAX_PCR_BANKS) {
+ pr_err("tpm: out of bank capacity: %u > %u\n",
+ nr_possible_banks, TPM2_MAX_PCR_BANKS);
rc = -ENOMEM;
goto out;
}
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index c75a531cfb98..6c25305c256e 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -179,6 +179,7 @@ static int crb_try_pluton_doorbell(struct crb_priv *priv, bool wait_for_complete
*
* @dev: crb device
* @priv: crb private data
+ * @loc: locality
*
* Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ
* The device should respond within TIMEOUT_C by clearing the bit.
@@ -233,6 +234,7 @@ static int crb_go_idle(struct tpm_chip *chip)
*
* @dev: crb device
* @priv: crb private data
+ * @loc: locality
*
* Write CRB_CTRL_REQ_CMD_READY to TPM_CRB_CTRL_REQ
* and poll till the device acknowledge it by clearing the bit.
@@ -412,7 +414,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip)
#ifdef CONFIG_ARM64
/*
* This is a TPM Command Response Buffer start method that invokes a
- * Secure Monitor Call to requrest the firmware to execute or cancel
+ * Secure Monitor Call to request the firmware to execute or cancel
* a TPM 2.0 command.
*/
static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index 8954a8660ffc..e2a1769081b1 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -265,8 +265,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip)
/*
* Dump stack for forensics, as invalid TPM_STS.x could be
- * potentially triggered by impaired tpm_try_get_ops() or
- * tpm_find_get_ops().
+ * potentially triggered by impaired tpm_try_get_ops().
*/
dump_stack();
}
diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c
index 70ce0ca0cb7d..0339c4af0fe5 100644
--- a/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c
+++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523-r.c
@@ -121,11 +121,11 @@ static SUNXI_CCU_GATE_HW(bus_r_ir_rx_clk, "bus-r-ir-rx",
&r_apb0_clk.common.hw, 0x1cc, BIT(0), 0);
static SUNXI_CCU_GATE_HW(bus_r_dma_clk, "bus-r-dma",
- &r_apb0_clk.common.hw, 0x1dc, BIT(0), 0);
+ &r_apb0_clk.common.hw, 0x1dc, BIT(0), CLK_IS_CRITICAL);
static SUNXI_CCU_GATE_HW(bus_r_rtc_clk, "bus-r-rtc",
&r_apb0_clk.common.hw, 0x20c, BIT(0), 0);
static SUNXI_CCU_GATE_HW(bus_r_cpucfg_clk, "bus-r-cpucfg",
- &r_apb0_clk.common.hw, 0x22c, BIT(0), 0);
+ &r_apb0_clk.common.hw, 0x22c, BIT(0), CLK_IS_CRITICAL);
static struct ccu_common *sun55i_a523_r_ccu_clks[] = {
&r_ahb_clk.common,
diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c
index acb532f8361b..20dad06b37ca 100644
--- a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c
+++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c
@@ -300,7 +300,7 @@ static struct ccu_nm pll_audio0_4x_clk = {
.m = _SUNXI_CCU_DIV(16, 6),
.sdm = _SUNXI_CCU_SDM(pll_audio0_sdm_table, BIT(24),
0x178, BIT(31)),
- .min_rate = 180000000U,
+ .min_rate = 90000000U,
.max_rate = 3000000000U,
.common = {
.reg = 0x078,
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index ffcd23668763..aa59e5b13351 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -782,4 +782,15 @@ config NXP_STM_TIMER
Enables the support for NXP System Timer Module found in the
s32g NXP platform series.
+config RTK_SYSTIMER
+ bool "Realtek SYSTIMER support"
+ depends on ARM || ARM64
+ depends on ARCH_REALTEK || COMPILE_TEST
+ select TIMER_OF
+ help
+ This option enables the driver that registers the global 1 MHz hardware
+ counter as a clock event device on Realtek SoCs. Make sure to enable
+ this option only when building for a Realtek platform or for compilation
+ testing.
+
endmenu
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index ec4452ee958f..b46376af6b49 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -95,3 +95,4 @@ obj-$(CONFIG_CLKSRC_LOONGSON1_PWM) += timer-loongson1-pwm.o
obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o
obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o
+obj-$(CONFIG_RTK_SYSTIMER) += timer-realtek.o
diff --git a/drivers/clocksource/arm_arch_timer_mmio.c b/drivers/clocksource/arm_arch_timer_mmio.c
index ebe1987d651e..d10362692fdd 100644
--- a/drivers/clocksource/arm_arch_timer_mmio.c
+++ b/drivers/clocksource/arm_arch_timer_mmio.c
@@ -426,6 +426,7 @@ static struct platform_driver arch_timer_mmio_drv = {
.driver = {
.name = "arch-timer-mmio",
.of_match_table = arch_timer_mmio_of_table,
+ .suppress_bind_attrs = true,
},
.probe = arch_timer_mmio_probe,
};
@@ -434,6 +435,7 @@ builtin_platform_driver(arch_timer_mmio_drv);
static struct platform_driver arch_timer_mmio_acpi_drv = {
.driver = {
.name = "gtdt-arm-mmio-timer",
+ .suppress_bind_attrs = true,
},
.probe = arch_timer_mmio_probe,
};
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 385eb94bbe7c..791b298c995b 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -355,14 +355,6 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
dev_pm_syscore_device(&ch->cmt->pdev->dev, true);
- /* enable clock */
- ret = clk_enable(ch->cmt->clk);
- if (ret) {
- dev_err(&ch->cmt->pdev->dev, "ch%u: cannot enable clock\n",
- ch->index);
- goto err0;
- }
-
/* make sure channel is disabled */
sh_cmt_start_stop_ch(ch, 0);
@@ -384,19 +376,12 @@ static int sh_cmt_enable(struct sh_cmt_channel *ch)
if (ret || sh_cmt_read_cmcnt(ch)) {
dev_err(&ch->cmt->pdev->dev, "ch%u: cannot clear CMCNT\n",
ch->index);
- ret = -ETIMEDOUT;
- goto err1;
+ return -ETIMEDOUT;
}
/* enable channel */
sh_cmt_start_stop_ch(ch, 1);
return 0;
- err1:
- /* stop clock */
- clk_disable(ch->cmt->clk);
-
- err0:
- return ret;
}
static void sh_cmt_disable(struct sh_cmt_channel *ch)
@@ -407,9 +392,6 @@ static void sh_cmt_disable(struct sh_cmt_channel *ch)
/* disable interrupts in CMT block */
sh_cmt_write_cmcsr(ch, 0);
- /* stop clock */
- clk_disable(ch->cmt->clk);
-
dev_pm_syscore_device(&ch->cmt->pdev->dev, false);
}
@@ -583,8 +565,6 @@ static int sh_cmt_start_clocksource(struct sh_cmt_channel *ch)
int ret = 0;
unsigned long flags;
- pm_runtime_get_sync(&ch->cmt->pdev->dev);
-
raw_spin_lock_irqsave(&ch->lock, flags);
if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
@@ -619,8 +599,6 @@ static void sh_cmt_stop_clocksource(struct sh_cmt_channel *ch)
sh_cmt_disable(ch);
raw_spin_unlock_irqrestore(&ch->lock, flags);
-
- pm_runtime_put(&ch->cmt->pdev->dev);
}
static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch)
@@ -630,10 +608,8 @@ static int sh_cmt_start_clockevent(struct sh_cmt_channel *ch)
raw_spin_lock_irqsave(&ch->lock, flags);
- if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) {
- pm_runtime_get_sync(&ch->cmt->pdev->dev);
+ if (!(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
ret = sh_cmt_enable(ch);
- }
if (ret)
goto out;
@@ -656,10 +632,8 @@ static void sh_cmt_stop_clockevent(struct sh_cmt_channel *ch)
ch->flags &= ~FLAG_CLOCKEVENT;
- if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE))) {
+ if (f && !(ch->flags & (FLAG_CLOCKEVENT | FLAG_CLOCKSOURCE)))
sh_cmt_disable(ch);
- pm_runtime_put(&ch->cmt->pdev->dev);
- }
/* adjust the timeout to maximum if only clocksource left */
if (ch->flags & FLAG_CLOCKSOURCE)
@@ -1134,8 +1108,6 @@ static int sh_cmt_setup(struct sh_cmt_device *cmt, struct platform_device *pdev)
mask &= ~(1 << hwidx);
}
- clk_disable(cmt->clk);
-
platform_set_drvdata(pdev, cmt);
return 0;
@@ -1183,8 +1155,6 @@ static int sh_cmt_probe(struct platform_device *pdev)
out:
if (cmt->has_clockevent || cmt->has_clocksource)
pm_runtime_irq_safe(&pdev->dev);
- else
- pm_runtime_idle(&pdev->dev);
return 0;
}
diff --git a/drivers/clocksource/timer-nxp-pit.c b/drivers/clocksource/timer-nxp-pit.c
index 2d0a3554b6bf..d1740f18f718 100644
--- a/drivers/clocksource/timer-nxp-pit.c
+++ b/drivers/clocksource/timer-nxp-pit.c
@@ -374,9 +374,10 @@ static struct platform_driver nxp_pit_driver = {
.driver = {
.name = "nxp-pit",
.of_match_table = pit_timer_of_match,
+ .suppress_bind_attrs = true,
},
.probe = pit_timer_probe,
};
-module_platform_driver(nxp_pit_driver);
+builtin_platform_driver(nxp_pit_driver);
TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
diff --git a/drivers/clocksource/timer-nxp-stm.c b/drivers/clocksource/timer-nxp-stm.c
index bbc40623728f..1ab907233f48 100644
--- a/drivers/clocksource/timer-nxp-stm.c
+++ b/drivers/clocksource/timer-nxp-stm.c
@@ -177,15 +177,15 @@ static void nxp_stm_clocksource_resume(struct clocksource *cs)
nxp_stm_clocksource_enable(cs);
}
-static void __init devm_clocksource_unregister(void *data)
+static void devm_clocksource_unregister(void *data)
{
struct stm_timer *stm_timer = data;
clocksource_unregister(&stm_timer->cs);
}
-static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer,
- const char *name, void __iomem *base, struct clk *clk)
+static int nxp_stm_clocksource_init(struct device *dev, struct stm_timer *stm_timer,
+ const char *name, void __iomem *base, struct clk *clk)
{
int ret;
@@ -208,10 +208,8 @@ static int __init nxp_stm_clocksource_init(struct device *dev, struct stm_timer
return ret;
ret = devm_add_action_or_reset(dev, devm_clocksource_unregister, stm_timer);
- if (ret) {
- clocksource_unregister(&stm_timer->cs);
+ if (ret)
return ret;
- }
stm_sched_clock = stm_timer;
@@ -298,9 +296,9 @@ static void nxp_stm_clockevent_resume(struct clock_event_device *ced)
nxp_stm_module_get(stm_timer);
}
-static int __init nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer,
- const char *name, void __iomem *base, int irq,
- struct clk *clk, int cpu)
+static int nxp_stm_clockevent_per_cpu_init(struct device *dev, struct stm_timer *stm_timer,
+ const char *name, void __iomem *base, int irq,
+ struct clk *clk, int cpu)
{
stm_timer->base = base;
stm_timer->rate = clk_get_rate(clk);
@@ -388,7 +386,7 @@ static irqreturn_t nxp_stm_module_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __init nxp_stm_timer_probe(struct platform_device *pdev)
+static int nxp_stm_timer_probe(struct platform_device *pdev)
{
struct stm_timer *stm_timer;
struct device *dev = &pdev->dev;
@@ -484,14 +482,15 @@ static const struct of_device_id nxp_stm_of_match[] = {
};
MODULE_DEVICE_TABLE(of, nxp_stm_of_match);
-static struct platform_driver nxp_stm_probe = {
+static struct platform_driver nxp_stm_driver = {
.probe = nxp_stm_timer_probe,
.driver = {
.name = "nxp-stm",
.of_match_table = nxp_stm_of_match,
+ .suppress_bind_attrs = true,
},
};
-module_platform_driver(nxp_stm_probe);
+builtin_platform_driver(nxp_stm_driver);
MODULE_DESCRIPTION("NXP System Timer Module driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/clocksource/timer-ralink.c b/drivers/clocksource/timer-ralink.c
index 6ecdb4228f76..68434d9ed910 100644
--- a/drivers/clocksource/timer-ralink.c
+++ b/drivers/clocksource/timer-ralink.c
@@ -130,14 +130,15 @@ static int __init ralink_systick_init(struct device_node *np)
systick.dev.irq = irq_of_parse_and_map(np, 0);
if (!systick.dev.irq) {
pr_err("%pOFn: request_irq failed", np);
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_iounmap;
}
ret = clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name,
SYSTICK_FREQ, 301, 16,
clocksource_mmio_readl_up);
if (ret)
- return ret;
+ goto err_free_irq;
clockevents_register_device(&systick.dev);
@@ -145,6 +146,12 @@ static int __init ralink_systick_init(struct device_node *np)
np, systick.dev.mult, systick.dev.shift);
return 0;
+
+err_free_irq:
+ irq_dispose_mapping(systick.dev.irq);
+err_iounmap:
+ iounmap(systick.membase);
+ return ret;
}
TIMER_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
diff --git a/drivers/clocksource/timer-rda.c b/drivers/clocksource/timer-rda.c
index fd1199c189bf..0be8e05970e2 100644
--- a/drivers/clocksource/timer-rda.c
+++ b/drivers/clocksource/timer-rda.c
@@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/sched_clock.h>
#include "timer-of.h"
@@ -153,7 +154,7 @@ static struct timer_of rda_ostimer_of = {
},
};
-static u64 rda_hwtimer_read(struct clocksource *cs)
+static u64 rda_hwtimer_clocksource_read(void)
{
void __iomem *base = timer_of_base(&rda_ostimer_of);
u32 lo, hi;
@@ -167,6 +168,11 @@ static u64 rda_hwtimer_read(struct clocksource *cs)
return ((u64)hi << 32) | lo;
}
+static u64 rda_hwtimer_read(struct clocksource *cs)
+{
+ return rda_hwtimer_clocksource_read();
+}
+
static struct clocksource rda_hwtimer_clocksource = {
.name = "rda-timer",
.rating = 400,
@@ -185,6 +191,7 @@ static int __init rda_timer_init(struct device_node *np)
return ret;
clocksource_register_hz(&rda_hwtimer_clocksource, rate);
+ sched_clock_register(rda_hwtimer_clocksource_read, 64, rate);
clockevents_config_and_register(&rda_ostimer_of.clkevt, rate,
0x2, UINT_MAX);
diff --git a/drivers/clocksource/timer-realtek.c b/drivers/clocksource/timer-realtek.c
new file mode 100644
index 000000000000..4f0439de9939
--- /dev/null
+++ b/drivers/clocksource/timer-realtek.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Realtek Semiconductor Corp.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/irqflags.h>
+#include <linux/interrupt.h>
+#include "timer-of.h"
+
+#define ENBL 1
+#define DSBL 0
+
+#define SYSTIMER_RATE 1000000
+#define SYSTIMER_MIN_DELTA 0x64
+#define SYSTIMER_MAX_DELTA ULONG_MAX
+
+/* SYSTIMER Register Offset (RTK Internal Use) */
+#define TS_LW_OFST 0x0
+#define TS_HW_OFST 0x4
+#define TS_CMP_VAL_LW_OFST 0x8
+#define TS_CMP_VAL_HW_OFST 0xC
+#define TS_CMP_CTRL_OFST 0x10
+#define TS_CMP_STAT_OFST 0x14
+
+/* SYSTIMER CMP CTRL REG Mask */
+#define TS_CMP_EN_MASK 0x1
+#define TS_WR_EN0_MASK 0x2
+
+static void __iomem *systimer_base;
+
+static u64 rtk_ts64_read(void)
+{
+ u32 low, high;
+ u64 ts;
+
+ /* Caution: Read LSB word (TS_LW_OFST) first then MSB (TS_HW_OFST) */
+ low = readl(systimer_base + TS_LW_OFST);
+ high = readl(systimer_base + TS_HW_OFST);
+ ts = ((u64)high << 32) | low;
+
+ return ts;
+}
+
+static void rtk_cmp_value_write(u64 value)
+{
+ u32 high, low;
+
+ low = value & 0xFFFFFFFF;
+ high = value >> 32;
+
+ writel(high, systimer_base + TS_CMP_VAL_HW_OFST);
+ writel(low, systimer_base + TS_CMP_VAL_LW_OFST);
+}
+
+static inline void rtk_cmp_en_write(bool cmp_en)
+{
+ u32 val;
+
+ val = TS_WR_EN0_MASK;
+ if (cmp_en == ENBL)
+ val |= TS_CMP_EN_MASK;
+
+ writel(val, systimer_base + TS_CMP_CTRL_OFST);
+}
+
+static int rtk_syst_clkevt_next_event(unsigned long cycles, struct clock_event_device *clkevt)
+{
+ u64 cmp_val;
+
+ rtk_cmp_en_write(DSBL);
+ cmp_val = rtk_ts64_read();
+
+ /* Set CMP value to current timestamp plus delta_us */
+ rtk_cmp_value_write(cmp_val + cycles);
+ rtk_cmp_en_write(ENBL);
+ return 0;
+}
+
+static irqreturn_t rtk_ts_match_intr_handler(int irq, void *dev_id)
+{
+ struct clock_event_device *clkevt = dev_id;
+ void __iomem *reg_base;
+ u32 val;
+
+ /* Disable TS CMP Match */
+ rtk_cmp_en_write(DSBL);
+
+ /* Clear TS CMP INTR */
+ reg_base = systimer_base + TS_CMP_STAT_OFST;
+ val = readl(reg_base) & TS_CMP_EN_MASK;
+ writel(val | TS_CMP_EN_MASK, reg_base);
+ clkevt->event_handler(clkevt);
+
+ return IRQ_HANDLED;
+}
+
+static int rtk_syst_shutdown(struct clock_event_device *clkevt)
+{
+ void __iomem *reg_base;
+ u64 cmp_val = 0;
+
+ /* Disable TS CMP Match */
+ rtk_cmp_en_write(DSBL);
+ /* Set compare value to 0 */
+ rtk_cmp_value_write(cmp_val);
+
+ /* Clear TS CMP INTR */
+ reg_base = systimer_base + TS_CMP_STAT_OFST;
+ writel(TS_CMP_EN_MASK, reg_base);
+ return 0;
+}
+
+static struct timer_of rtk_timer_to = {
+ .flags = TIMER_OF_IRQ | TIMER_OF_BASE,
+
+ .clkevt = {
+ .name = "rtk-clkevt",
+ .rating = 300,
+ .cpumask = cpu_possible_mask,
+ .features = CLOCK_EVT_FEAT_DYNIRQ |
+ CLOCK_EVT_FEAT_ONESHOT,
+ .set_next_event = rtk_syst_clkevt_next_event,
+ .set_state_oneshot = rtk_syst_shutdown,
+ .set_state_shutdown = rtk_syst_shutdown,
+ },
+
+ .of_irq = {
+ .flags = IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = rtk_ts_match_intr_handler,
+ },
+};
+
+static int __init rtk_systimer_init(struct device_node *node)
+{
+ int ret;
+
+ ret = timer_of_init(node, &rtk_timer_to);
+ if (ret)
+ return ret;
+
+ systimer_base = timer_of_base(&rtk_timer_to);
+ clockevents_config_and_register(&rtk_timer_to.clkevt, SYSTIMER_RATE,
+ SYSTIMER_MIN_DELTA, SYSTIMER_MAX_DELTA);
+
+ return 0;
+}
+
+TIMER_OF_DECLARE(rtk_systimer, "realtek,rtd1625-systimer", rtk_systimer_init);
diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
index cd1916c05325..e82a95ea4724 100644
--- a/drivers/clocksource/timer-sp804.c
+++ b/drivers/clocksource/timer-sp804.c
@@ -21,6 +21,10 @@
#include <linux/of_irq.h>
#include <linux/sched_clock.h>
+#ifdef CONFIG_ARM
+#include <linux/delay.h>
+#endif
+
#include "timer-sp.h"
/* Hisilicon 64-bit timer(a variant of ARM SP804) */
@@ -102,6 +106,23 @@ static u64 notrace sp804_read(void)
return ~readl_relaxed(sched_clkevt->value);
}
+#ifdef CONFIG_ARM
+static struct delay_timer delay;
+static unsigned long sp804_read_delay_timer_read(void)
+{
+ return sp804_read();
+}
+
+static void sp804_register_delay_timer(int freq)
+{
+ delay.freq = freq;
+ delay.read_current_timer = sp804_read_delay_timer_read;
+ register_current_timer_delay(&delay);
+}
+#else
+static inline void sp804_register_delay_timer(int freq) {}
+#endif
+
static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
const char *name,
struct clk *clk,
@@ -114,6 +135,8 @@ static int __init sp804_clocksource_and_sched_clock_init(void __iomem *base,
if (rate < 0)
return -EINVAL;
+ sp804_register_delay_timer(rate);
+
clkevt = sp804_clkevt_get(base);
writel(0, clkevt->ctrl);
@@ -318,6 +341,7 @@ static int __init sp804_of_init(struct device_node *np, struct sp804_timer *time
if (ret)
goto err;
}
+
initialized = true;
return 0;
diff --git a/drivers/clocksource/timer-sprd.c b/drivers/clocksource/timer-sprd.c
index 430cb99d8d79..2c07dd2af760 100644
--- a/drivers/clocksource/timer-sprd.c
+++ b/drivers/clocksource/timer-sprd.c
@@ -30,6 +30,7 @@
#define TIMER_VALUE_SHDW_HI 0x1c
#define TIMER_VALUE_LO_MASK GENMASK(31, 0)
+#define TIMER_VALUE_HI_MASK GENMASK(31, 0)
static void sprd_timer_enable(void __iomem *base, u32 flag)
{
@@ -162,15 +163,26 @@ static struct timer_of suspend_to = {
static u64 sprd_suspend_timer_read(struct clocksource *cs)
{
- return ~(u64)readl_relaxed(timer_of_base(&suspend_to) +
- TIMER_VALUE_SHDW_LO) & cs->mask;
+ u32 lo, hi;
+
+ do {
+ hi = readl_relaxed(timer_of_base(&suspend_to) +
+ TIMER_VALUE_SHDW_HI);
+ lo = readl_relaxed(timer_of_base(&suspend_to) +
+ TIMER_VALUE_SHDW_LO);
+ } while (hi != readl_relaxed(timer_of_base(&suspend_to) + TIMER_VALUE_SHDW_HI));
+
+ return ~(((u64)hi << 32) | lo);
}
static int sprd_suspend_timer_enable(struct clocksource *cs)
{
- sprd_timer_update_counter(timer_of_base(&suspend_to),
- TIMER_VALUE_LO_MASK);
- sprd_timer_enable(timer_of_base(&suspend_to), TIMER_CTL_PERIOD_MODE);
+ writel_relaxed(TIMER_VALUE_LO_MASK,
+ timer_of_base(&suspend_to) + TIMER_LOAD_LO);
+ writel_relaxed(TIMER_VALUE_HI_MASK,
+ timer_of_base(&suspend_to) + TIMER_LOAD_HI);
+ sprd_timer_enable(timer_of_base(&suspend_to),
+ TIMER_CTL_PERIOD_MODE|TIMER_CTL_64BIT_WIDTH);
return 0;
}
@@ -186,7 +198,7 @@ static struct clocksource suspend_clocksource = {
.read = sprd_suspend_timer_read,
.enable = sprd_suspend_timer_enable,
.disable = sprd_suspend_timer_disable,
- .mask = CLOCKSOURCE_MASK(32),
+ .mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_SUSPEND_NONSTOP,
};
diff --git a/drivers/clocksource/timer-stm32-lp.c b/drivers/clocksource/timer-stm32-lp.c
index c2a699f5c1dd..3d804128c765 100644
--- a/drivers/clocksource/timer-stm32-lp.c
+++ b/drivers/clocksource/timer-stm32-lp.c
@@ -289,5 +289,4 @@ static struct platform_driver stm32_clkevent_lp_driver = {
};
module_platform_driver(stm32_clkevent_lp_driver);
-MODULE_ALIAS("platform:stm32-lptimer-timer");
MODULE_DESCRIPTION("STMicroelectronics STM32 clockevent low power driver");
diff --git a/drivers/comedi/comedi_buf.c b/drivers/comedi/comedi_buf.c
index 002c0e76baff..c7c262a2d8ca 100644
--- a/drivers/comedi/comedi_buf.c
+++ b/drivers/comedi/comedi_buf.c
@@ -317,7 +317,7 @@ static unsigned int comedi_buf_munge(struct comedi_subdevice *s,
unsigned int count = 0;
const unsigned int num_sample_bytes = comedi_bytes_per_sample(s);
- if (!s->munge || (async->cmd.flags & CMDF_RAWDATA)) {
+ if (!s->munge || (async->cmd.flags & CMDF_RAWDATA) || async->cmd.chanlist_len == 0) {
async->munge_count += num_bytes;
return num_bytes;
}
diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c
index 1a299d1f350b..19d457ae4c3b 100644
--- a/drivers/counter/microchip-tcb-capture.c
+++ b/drivers/counter/microchip-tcb-capture.c
@@ -451,7 +451,7 @@ static void mchp_tc_irq_remove(void *ptr)
static int mchp_tc_irq_enable(struct counter_device *const counter, int irq)
{
struct mchp_tc_data *const priv = counter_priv(counter);
- int ret = devm_request_irq(counter->parent, irq, mchp_tc_isr, 0,
+ int ret = devm_request_irq(counter->parent, irq, mchp_tc_isr, IRQF_SHARED,
dev_name(counter->parent), counter);
if (ret < 0)
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 083d8369a591..e73a66785d69 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -395,7 +395,7 @@ static unsigned int check_freqs(struct cpufreq_policy *policy,
cur_freq = extract_freq(policy, get_cur_val(mask, data));
if (cur_freq == freq)
return 1;
- udelay(10);
+ usleep_range(10, 15);
}
return 0;
}
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 298e92d8cc03..c45bc98721d2 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -65,13 +65,13 @@ static const char * const amd_pstate_mode_string[] = {
[AMD_PSTATE_PASSIVE] = "passive",
[AMD_PSTATE_ACTIVE] = "active",
[AMD_PSTATE_GUIDED] = "guided",
- NULL,
};
+static_assert(ARRAY_SIZE(amd_pstate_mode_string) == AMD_PSTATE_MAX);
const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode)
{
- if (mode < 0 || mode >= AMD_PSTATE_MAX)
- return NULL;
+ if (mode < AMD_PSTATE_UNDEFINED || mode >= AMD_PSTATE_MAX)
+ mode = AMD_PSTATE_UNDEFINED;
return amd_pstate_mode_string[mode];
}
EXPORT_SYMBOL_GPL(amd_pstate_get_mode_string);
@@ -110,6 +110,7 @@ enum energy_perf_value_index {
EPP_INDEX_BALANCE_PERFORMANCE,
EPP_INDEX_BALANCE_POWERSAVE,
EPP_INDEX_POWERSAVE,
+ EPP_INDEX_MAX,
};
static const char * const energy_perf_strings[] = {
@@ -118,8 +119,8 @@ static const char * const energy_perf_strings[] = {
[EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance",
[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
[EPP_INDEX_POWERSAVE] = "power",
- NULL
};
+static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
static unsigned int epp_values[] = {
[EPP_INDEX_DEFAULT] = 0,
@@ -127,7 +128,8 @@ static unsigned int epp_values[] = {
[EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_BALANCE_PERFORMANCE,
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
- };
+};
+static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX);
typedef int (*cppc_mode_transition_fn)(int);
@@ -183,7 +185,7 @@ static inline int get_mode_idx_from_str(const char *str, size_t size)
{
int i;
- for (i=0; i < AMD_PSTATE_MAX; i++) {
+ for (i = 0; i < AMD_PSTATE_MAX; i++) {
if (!strncmp(str, amd_pstate_mode_string[i], size))
return i;
}
@@ -1137,16 +1139,15 @@ static ssize_t show_amd_pstate_hw_prefcore(struct cpufreq_policy *policy,
static ssize_t show_energy_performance_available_preferences(
struct cpufreq_policy *policy, char *buf)
{
- int i = 0;
- int offset = 0;
+ int offset = 0, i;
struct amd_cpudata *cpudata = policy->driver_data;
if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE)
return sysfs_emit_at(buf, offset, "%s\n",
energy_perf_strings[EPP_INDEX_PERFORMANCE]);
- while (energy_perf_strings[i] != NULL)
- offset += sysfs_emit_at(buf, offset, "%s ", energy_perf_strings[i++]);
+ for (i = 0; i < ARRAY_SIZE(energy_perf_strings); i++)
+ offset += sysfs_emit_at(buf, offset, "%s ", energy_perf_strings[i]);
offset += sysfs_emit_at(buf, offset, "\n");
@@ -1157,15 +1158,10 @@ static ssize_t store_energy_performance_preference(
struct cpufreq_policy *policy, const char *buf, size_t count)
{
struct amd_cpudata *cpudata = policy->driver_data;
- char str_preference[21];
ssize_t ret;
u8 epp;
- ret = sscanf(buf, "%20s", str_preference);
- if (ret != 1)
- return -EINVAL;
-
- ret = match_string(energy_perf_strings, -1, str_preference);
+ ret = sysfs_match_string(energy_perf_strings, buf);
if (ret < 0)
return -EINVAL;
@@ -1282,7 +1278,7 @@ static int amd_pstate_change_mode_without_dvr_change(int mode)
if (cpu_feature_enabled(X86_FEATURE_CPPC) || cppc_state == AMD_PSTATE_ACTIVE)
return 0;
- for_each_present_cpu(cpu) {
+ for_each_online_cpu(cpu) {
cppc_set_auto_sel(cpu, (cppc_state == AMD_PSTATE_PASSIVE) ? 0 : 1);
}
@@ -1353,9 +1349,8 @@ int amd_pstate_update_status(const char *buf, size_t size)
return -EINVAL;
mode_idx = get_mode_idx_from_str(buf, size);
-
- if (mode_idx < 0 || mode_idx >= AMD_PSTATE_MAX)
- return -EINVAL;
+ if (mode_idx < 0)
+ return mode_idx;
if (mode_state_machine[cppc_state][mode_idx]) {
guard(mutex)(&amd_pstate_driver_lock);
@@ -1614,7 +1609,11 @@ static int amd_pstate_cpu_offline(struct cpufreq_policy *policy)
* min_perf value across kexec reboots. If this CPU is just onlined normally after this, the
* limits, epp and desired perf will get reset to the cached values in cpudata struct
*/
- return amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
+ return amd_pstate_update_perf(policy, perf.bios_min_perf,
+ FIELD_GET(AMD_CPPC_DES_PERF_MASK, cpudata->cppc_req_cached),
+ FIELD_GET(AMD_CPPC_MAX_PERF_MASK, cpudata->cppc_req_cached),
+ FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached),
+ false);
}
static int amd_pstate_suspend(struct cpufreq_policy *policy)
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index e23d9abea135..9eac77c4f294 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -142,16 +142,15 @@ static void cppc_cpufreq_cpu_fie_init(struct cpufreq_policy *policy)
init_irq_work(&cppc_fi->irq_work, cppc_irq_work);
ret = cppc_get_perf_ctrs(cpu, &cppc_fi->prev_perf_fb_ctrs);
- if (ret) {
- pr_warn("%s: failed to read perf counters for cpu:%d: %d\n",
- __func__, cpu, ret);
- /*
- * Don't abort if the CPU was offline while the driver
- * was getting registered.
- */
- if (cpu_online(cpu))
- return;
+ /*
+ * Don't abort as the CPU was offline while the driver was
+ * getting registered.
+ */
+ if (ret && cpu_online(cpu)) {
+ pr_debug("%s: failed to read perf counters for cpu:%d: %d\n",
+ __func__, cpu, ret);
+ return;
}
}
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index cd1816a12bb9..a1d11ecd1ac8 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -87,6 +87,7 @@ static const struct of_device_id allowlist[] __initconst = {
{ .compatible = "st-ericsson,u9540", },
{ .compatible = "starfive,jh7110", },
+ { .compatible = "starfive,jh7110s", },
{ .compatible = "ti,omap2", },
{ .compatible = "ti,omap4", },
@@ -218,20 +219,13 @@ static bool __init cpu0_node_has_opp_v2_prop(void)
static int __init cpufreq_dt_platdev_init(void)
{
- struct device_node *np __free(device_node) = of_find_node_by_path("/");
- const struct of_device_id *match;
- const void *data = NULL;
+ const void *data;
- if (!np)
- return -ENODEV;
-
- match = of_match_node(allowlist, np);
- if (match) {
- data = match->data;
+ data = of_machine_get_match_data(allowlist);
+ if (data)
goto create_pdev;
- }
- if (cpu0_node_has_opp_v2_prop() && !of_match_node(blocklist, np))
+ if (cpu0_node_has_opp_v2_prop() && !of_machine_device_match(blocklist))
goto create_pdev;
return -ENODEV;
diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c
index fedad1081973..fbbbe501cf2d 100644
--- a/drivers/cpufreq/cpufreq-nforce2.c
+++ b/drivers/cpufreq/cpufreq-nforce2.c
@@ -145,6 +145,8 @@ static unsigned int nforce2_fsb_read(int bootfsb)
pci_read_config_dword(nforce2_sub5, NFORCE2_BOOTFSB, &fsb);
fsb /= 1000000;
+ pci_dev_put(nforce2_sub5);
+
/* Check if PLL register is already set */
pci_read_config_byte(nforce2_dev, NFORCE2_PLLENABLE, (u8 *)&temp);
@@ -426,6 +428,7 @@ static int __init nforce2_init(void)
static void __exit nforce2_exit(void)
{
cpufreq_unregister_driver(&nforce2_driver);
+ pci_dev_put(nforce2_dev);
}
module_init(nforce2_init);
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 852e024facc3..4472bb1ec83c 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1421,9 +1421,12 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy,
* If there is a problem with its frequency table, take it
* offline and drop it.
*/
- ret = cpufreq_table_validate_and_sort(policy);
- if (ret)
- goto out_offline_policy;
+ if (policy->freq_table_sorted != CPUFREQ_TABLE_SORTED_ASCENDING &&
+ policy->freq_table_sorted != CPUFREQ_TABLE_SORTED_DESCENDING) {
+ ret = cpufreq_table_validate_and_sort(policy);
+ if (ret)
+ goto out_offline_policy;
+ }
/* related_cpus should at least include policy->cpus. */
cpumask_copy(policy->related_cpus, policy->cpus);
@@ -2550,7 +2553,7 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
for_each_inactive_policy(policy) {
if (!strcmp(policy->last_governor, governor->name)) {
policy->governor = NULL;
- strcpy(policy->last_governor, "\0");
+ policy->last_governor[0] = '\0';
}
}
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 38897bb14a2c..ec4abe374573 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -575,13 +575,18 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu)
int scaling = cpu->pstate.scaling;
int freq;
- pr_debug("CPU%d: perf_ctl_max_phys = %d\n", cpu->cpu, perf_ctl_max_phys);
- pr_debug("CPU%d: perf_ctl_turbo = %d\n", cpu->cpu, perf_ctl_turbo);
- pr_debug("CPU%d: perf_ctl_scaling = %d\n", cpu->cpu, perf_ctl_scaling);
+ pr_debug("CPU%d: PERF_CTL max_phys = %d\n", cpu->cpu, perf_ctl_max_phys);
+ pr_debug("CPU%d: PERF_CTL turbo = %d\n", cpu->cpu, perf_ctl_turbo);
+ pr_debug("CPU%d: PERF_CTL scaling = %d\n", cpu->cpu, perf_ctl_scaling);
pr_debug("CPU%d: HWP_CAP guaranteed = %d\n", cpu->cpu, cpu->pstate.max_pstate);
pr_debug("CPU%d: HWP_CAP highest = %d\n", cpu->cpu, cpu->pstate.turbo_pstate);
pr_debug("CPU%d: HWP-to-frequency scaling factor: %d\n", cpu->cpu, scaling);
+ if (scaling == perf_ctl_scaling)
+ return;
+
+ hwp_is_hybrid = true;
+
cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling,
perf_ctl_scaling);
cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling,
@@ -603,9 +608,6 @@ static bool turbo_is_disabled(void)
{
u64 misc_en;
- if (!cpu_feature_enabled(X86_FEATURE_IDA))
- return true;
-
rdmsrq(MSR_IA32_MISC_ENABLE, misc_en);
return !!(misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE);
@@ -912,6 +914,11 @@ static struct freq_attr *hwp_cpufreq_attrs[] = {
[HWP_CPUFREQ_ATTR_COUNT] = NULL,
};
+static u8 hybrid_get_cpu_type(unsigned int cpu)
+{
+ return cpu_data(cpu).topo.intel_type;
+}
+
static bool no_cas __ro_after_init;
static struct cpudata *hybrid_max_perf_cpu __read_mostly;
@@ -928,11 +935,8 @@ static int hybrid_active_power(struct device *dev, unsigned long *power,
unsigned long *freq)
{
/*
- * Create "utilization bins" of 0-40%, 40%-60%, 60%-80%, and 80%-100%
- * of the maximum capacity such that two CPUs of the same type will be
- * regarded as equally attractive if the utilization of each of them
- * falls into the same bin, which should prevent tasks from being
- * migrated between them too often.
+ * Create four "states" corresponding to 40%, 60%, 80%, and 100% of the
+ * full capacity.
*
* For this purpose, return the "frequency" of 2 for the first
* performance level and otherwise leave the value set by the caller.
@@ -946,38 +950,40 @@ static int hybrid_active_power(struct device *dev, unsigned long *power,
return 0;
}
+static bool hybrid_has_l3(unsigned int cpu)
+{
+ struct cpu_cacheinfo *cacheinfo = get_cpu_cacheinfo(cpu);
+ unsigned int i;
+
+ if (!cacheinfo)
+ return false;
+
+ for (i = 0; i < cacheinfo->num_leaves; i++) {
+ if (cacheinfo->info_list[i].level == 3)
+ return true;
+ }
+
+ return false;
+}
+
static int hybrid_get_cost(struct device *dev, unsigned long freq,
unsigned long *cost)
{
- struct pstate_data *pstate = &all_cpu_data[dev->id]->pstate;
- struct cpu_cacheinfo *cacheinfo = get_cpu_cacheinfo(dev->id);
-
+ /* Facilitate load balancing between CPUs of the same type. */
+ *cost = freq;
/*
- * The smaller the perf-to-frequency scaling factor, the larger the IPC
- * ratio between the given CPU and the least capable CPU in the system.
- * Regard that IPC ratio as the primary cost component and assume that
- * the scaling factors for different CPU types will differ by at least
- * 5% and they will not be above INTEL_PSTATE_CORE_SCALING.
+ * Adjust the cost depending on CPU type.
*
- * Add the freq value to the cost, so that the cost of running on CPUs
- * of the same type in different "utilization bins" is different.
+ * The idea is to start loading up LPE-cores before E-cores and start
+ * to populate E-cores when LPE-cores are utilized above 60% of the
+ * capacity. Similarly, P-cores start to be populated when E-cores are
+ * utilized above 60% of the capacity.
*/
- *cost = div_u64(100ULL * INTEL_PSTATE_CORE_SCALING, pstate->scaling) + freq;
- /*
- * Increase the cost slightly for CPUs able to access L3 to avoid
- * touching it in case some other CPUs of the same type can do the work
- * without it.
- */
- if (cacheinfo) {
- unsigned int i;
-
- /* Check if L3 cache is there. */
- for (i = 0; i < cacheinfo->num_leaves; i++) {
- if (cacheinfo->info_list[i].level == 3) {
- *cost += 2;
- break;
- }
- }
+ if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) {
+ if (hybrid_has_l3(dev->id)) /* E-core */
+ *cost += 1;
+ } else { /* P-core */
+ *cost += 2;
}
return 0;
@@ -1040,9 +1046,9 @@ static void hybrid_set_cpu_capacity(struct cpudata *cpu)
topology_set_cpu_scale(cpu->cpu, arch_scale_cpu_capacity(cpu->cpu));
- pr_debug("CPU%d: perf = %u, max. perf = %u, base perf = %d\n", cpu->cpu,
- cpu->capacity_perf, hybrid_max_perf_cpu->capacity_perf,
- cpu->pstate.max_pstate_physical);
+ pr_debug("CPU%d: capacity perf = %u, base perf = %u, sys max perf = %u\n",
+ cpu->cpu, cpu->capacity_perf, cpu->pstate.max_pstate_physical,
+ hybrid_max_perf_cpu->capacity_perf);
}
static void hybrid_clear_cpu_capacity(unsigned int cpunum)
@@ -1387,7 +1393,8 @@ static void set_power_ctl_ee_state(bool input)
{
u64 power_ctl;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
+
rdmsrq(MSR_IA32_POWER_CTL, power_ctl);
if (input) {
power_ctl &= ~BIT(MSR_IA32_POWER_CTL_BIT_EE);
@@ -1397,7 +1404,6 @@ static void set_power_ctl_ee_state(bool input)
power_ctl_ee_state = POWER_CTL_EE_DISABLE;
}
wrmsrq(MSR_IA32_POWER_CTL, power_ctl);
- mutex_unlock(&intel_pstate_driver_lock);
}
static void intel_pstate_hwp_enable(struct cpudata *cpudata);
@@ -1519,13 +1525,9 @@ static int intel_pstate_update_status(const char *buf, size_t size);
static ssize_t show_status(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- ssize_t ret;
-
- mutex_lock(&intel_pstate_driver_lock);
- ret = intel_pstate_show_status(buf);
- mutex_unlock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
- return ret;
+ return intel_pstate_show_status(buf);
}
static ssize_t store_status(struct kobject *a, struct kobj_attribute *b,
@@ -1534,11 +1536,13 @@ static ssize_t store_status(struct kobject *a, struct kobj_attribute *b,
char *p = memchr(buf, '\n', count);
int ret;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
+
ret = intel_pstate_update_status(buf, p ? p - buf : count);
- mutex_unlock(&intel_pstate_driver_lock);
+ if (ret < 0)
+ return ret;
- return ret < 0 ? ret : count;
+ return count;
}
static ssize_t show_turbo_pct(struct kobject *kobj,
@@ -1548,12 +1552,10 @@ static ssize_t show_turbo_pct(struct kobject *kobj,
int total, no_turbo, turbo_pct;
uint32_t turbo_fp;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
- if (!intel_pstate_driver) {
- mutex_unlock(&intel_pstate_driver_lock);
+ if (!intel_pstate_driver)
return -EAGAIN;
- }
cpu = all_cpu_data[0];
@@ -1562,8 +1564,6 @@ static ssize_t show_turbo_pct(struct kobject *kobj,
turbo_fp = div_fp(no_turbo, total);
turbo_pct = 100 - fp_toint(mul_fp(turbo_fp, int_tofp(100)));
- mutex_unlock(&intel_pstate_driver_lock);
-
return sprintf(buf, "%u\n", turbo_pct);
}
@@ -1573,38 +1573,26 @@ static ssize_t show_num_pstates(struct kobject *kobj,
struct cpudata *cpu;
int total;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
- if (!intel_pstate_driver) {
- mutex_unlock(&intel_pstate_driver_lock);
+ if (!intel_pstate_driver)
return -EAGAIN;
- }
cpu = all_cpu_data[0];
total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1;
- mutex_unlock(&intel_pstate_driver_lock);
-
return sprintf(buf, "%u\n", total);
}
static ssize_t show_no_turbo(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- ssize_t ret;
-
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
- if (!intel_pstate_driver) {
- mutex_unlock(&intel_pstate_driver_lock);
+ if (!intel_pstate_driver)
return -EAGAIN;
- }
- ret = sprintf(buf, "%u\n", global.no_turbo);
-
- mutex_unlock(&intel_pstate_driver_lock);
-
- return ret;
+ return sprintf(buf, "%u\n", global.no_turbo);
}
static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b,
@@ -1616,29 +1604,25 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b,
if (sscanf(buf, "%u", &input) != 1)
return -EINVAL;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
- if (!intel_pstate_driver) {
- count = -EAGAIN;
- goto unlock_driver;
- }
+ if (!intel_pstate_driver)
+ return -EAGAIN;
no_turbo = !!clamp_t(int, input, 0, 1);
WRITE_ONCE(global.turbo_disabled, turbo_is_disabled());
if (global.turbo_disabled && !no_turbo) {
pr_notice("Turbo disabled by BIOS or unavailable on processor\n");
- count = -EPERM;
if (global.no_turbo)
- goto unlock_driver;
- else
- no_turbo = 1;
- }
+ return -EPERM;
- if (no_turbo == global.no_turbo) {
- goto unlock_driver;
+ no_turbo = 1;
}
+ if (no_turbo == global.no_turbo)
+ return count;
+
WRITE_ONCE(global.no_turbo, no_turbo);
mutex_lock(&intel_pstate_limits_lock);
@@ -1657,9 +1641,6 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b,
intel_pstate_update_limits_for_all();
arch_set_max_freq_ratio(no_turbo);
-unlock_driver:
- mutex_unlock(&intel_pstate_driver_lock);
-
return count;
}
@@ -1709,12 +1690,10 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct kobj_attribute *b,
if (ret != 1)
return -EINVAL;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
- if (!intel_pstate_driver) {
- mutex_unlock(&intel_pstate_driver_lock);
+ if (!intel_pstate_driver)
return -EAGAIN;
- }
mutex_lock(&intel_pstate_limits_lock);
@@ -1727,8 +1706,6 @@ static ssize_t store_max_perf_pct(struct kobject *a, struct kobj_attribute *b,
else
update_qos_requests(FREQ_QOS_MAX);
- mutex_unlock(&intel_pstate_driver_lock);
-
return count;
}
@@ -1742,12 +1719,10 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct kobj_attribute *b,
if (ret != 1)
return -EINVAL;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
- if (!intel_pstate_driver) {
- mutex_unlock(&intel_pstate_driver_lock);
+ if (!intel_pstate_driver)
return -EAGAIN;
- }
mutex_lock(&intel_pstate_limits_lock);
@@ -1761,8 +1736,6 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct kobj_attribute *b,
else
update_qos_requests(FREQ_QOS_MIN);
- mutex_unlock(&intel_pstate_driver_lock);
-
return count;
}
@@ -1783,10 +1756,10 @@ static ssize_t store_hwp_dynamic_boost(struct kobject *a,
if (ret)
return ret;
- mutex_lock(&intel_pstate_driver_lock);
+ guard(mutex)(&intel_pstate_driver_lock);
+
hwp_boost = !!input;
intel_pstate_update_policies();
- mutex_unlock(&intel_pstate_driver_lock);
return count;
}
@@ -2075,6 +2048,18 @@ static void intel_pstate_hwp_enable(struct cpudata *cpudata)
intel_pstate_update_epp_defaults(cpudata);
}
+static u64 get_perf_ctl_val(int pstate)
+{
+ u64 val;
+
+ val = (u64)pstate << 8;
+ if (READ_ONCE(global.no_turbo) && !READ_ONCE(global.turbo_disabled) &&
+ cpu_feature_enabled(X86_FEATURE_IDA))
+ val |= (u64)1 << 32;
+
+ return val;
+}
+
static int atom_get_min_pstate(int not_used)
{
u64 value;
@@ -2101,14 +2086,10 @@ static int atom_get_turbo_pstate(int not_used)
static u64 atom_get_val(struct cpudata *cpudata, int pstate)
{
- u64 val;
+ u64 val = get_perf_ctl_val(pstate);
int32_t vid_fp;
u32 vid;
- val = (u64)pstate << 8;
- if (READ_ONCE(global.no_turbo) && !READ_ONCE(global.turbo_disabled))
- val |= (u64)1 << 32;
-
vid_fp = cpudata->vid.min + mul_fp(
int_tofp(pstate - cpudata->pstate.min_pstate),
cpudata->vid.ratio);
@@ -2268,13 +2249,7 @@ static int core_get_turbo_pstate(int cpu)
static u64 core_get_val(struct cpudata *cpudata, int pstate)
{
- u64 val;
-
- val = (u64)pstate << 8;
- if (READ_ONCE(global.no_turbo) && !READ_ONCE(global.turbo_disabled))
- val |= (u64)1 << 32;
-
- return val;
+ return get_perf_ctl_val(pstate);
}
static int knl_get_aperf_mperf_shift(void)
@@ -2298,18 +2273,14 @@ static int knl_get_turbo_pstate(int cpu)
static int hwp_get_cpu_scaling(int cpu)
{
if (hybrid_scaling_factor) {
- struct cpuinfo_x86 *c = &cpu_data(cpu);
- u8 cpu_type = c->topo.intel_type;
-
/*
* Return the hybrid scaling factor for P-cores and use the
* default core scaling for E-cores.
*/
- if (cpu_type == INTEL_CPU_TYPE_CORE)
+ if (hybrid_get_cpu_type(cpu) == INTEL_CPU_TYPE_CORE)
return hybrid_scaling_factor;
- if (cpu_type == INTEL_CPU_TYPE_ATOM)
- return core_get_scaling();
+ return core_get_scaling();
}
/* Use core scaling on non-hybrid systems. */
@@ -2344,11 +2315,10 @@ static void intel_pstate_set_min_pstate(struct cpudata *cpu)
static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
{
- int perf_ctl_max_phys = pstate_funcs.get_max_physical(cpu->cpu);
int perf_ctl_scaling = pstate_funcs.get_scaling();
+ cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu);
cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu);
- cpu->pstate.max_pstate_physical = perf_ctl_max_phys;
cpu->pstate.perf_ctl_scaling = perf_ctl_scaling;
if (hwp_active && !hwp_mode_bdw) {
@@ -2356,10 +2326,7 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
if (pstate_funcs.get_cpu_scaling) {
cpu->pstate.scaling = pstate_funcs.get_cpu_scaling(cpu->cpu);
- if (cpu->pstate.scaling != perf_ctl_scaling) {
- intel_pstate_hybrid_hwp_adjust(cpu);
- hwp_is_hybrid = true;
- }
+ intel_pstate_hybrid_hwp_adjust(cpu);
} else {
cpu->pstate.scaling = perf_ctl_scaling;
}
@@ -2761,6 +2728,7 @@ static const struct x86_cpu_id intel_pstate_cpu_oob_ids[] __initconst = {
X86_MATCH(INTEL_ATOM_CRESTMONT, core_funcs),
X86_MATCH(INTEL_ATOM_CRESTMONT_X, core_funcs),
X86_MATCH(INTEL_ATOM_DARKMONT_X, core_funcs),
+ X86_MATCH(INTEL_DIAMONDRAPIDS_X, core_funcs),
{}
};
#endif
@@ -3913,9 +3881,9 @@ hwp_cpu_matched:
}
- mutex_lock(&intel_pstate_driver_lock);
- rc = intel_pstate_register_driver(default_driver);
- mutex_unlock(&intel_pstate_driver_lock);
+ scoped_guard(mutex, &intel_pstate_driver_lock) {
+ rc = intel_pstate_register_driver(default_driver);
+ }
if (rc) {
intel_pstate_sysfs_remove();
return rc;
diff --git a/drivers/cpufreq/mediatek-cpufreq.c b/drivers/cpufreq/mediatek-cpufreq.c
index 5d50a231f944..052ca7cd2f4f 100644
--- a/drivers/cpufreq/mediatek-cpufreq.c
+++ b/drivers/cpufreq/mediatek-cpufreq.c
@@ -764,22 +764,14 @@ MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines);
static int __init mtk_cpufreq_driver_init(void)
{
- struct device_node *np;
- const struct of_device_id *match;
const struct mtk_cpufreq_platform_data *data;
int err;
- np = of_find_node_by_path("/");
- if (!np)
- return -ENODEV;
-
- match = of_match_node(mtk_cpufreq_machines, np);
- of_node_put(np);
- if (!match) {
+ data = of_machine_get_match_data(mtk_cpufreq_machines);
+ if (!data) {
pr_debug("Machine is not compatible with mtk-cpufreq\n");
return -ENODEV;
}
- data = match->data;
err = platform_driver_register(&mtk_cpufreq_platdrv);
if (err)
diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c
index 765a5bb81829..81e16b5a0245 100644
--- a/drivers/cpufreq/qcom-cpufreq-nvmem.c
+++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c
@@ -256,13 +256,22 @@ len_error:
return ret;
}
+static const struct of_device_id qcom_cpufreq_ipq806x_match_list[] __maybe_unused = {
+ { .compatible = "qcom,ipq8062", .data = (const void *)QCOM_ID_IPQ8062 },
+ { .compatible = "qcom,ipq8064", .data = (const void *)QCOM_ID_IPQ8064 },
+ { .compatible = "qcom,ipq8065", .data = (const void *)QCOM_ID_IPQ8065 },
+ { .compatible = "qcom,ipq8066", .data = (const void *)QCOM_ID_IPQ8066 },
+ { .compatible = "qcom,ipq8068", .data = (const void *)QCOM_ID_IPQ8068 },
+ { .compatible = "qcom,ipq8069", .data = (const void *)QCOM_ID_IPQ8069 },
+};
+
static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev,
struct nvmem_cell *speedbin_nvmem,
char **pvs_name,
struct qcom_cpufreq_drv *drv)
{
+ int msm_id = -1, ret = 0;
int speed = 0, pvs = 0;
- int msm_id, ret = 0;
u8 *speedbin;
size_t len;
@@ -279,8 +288,30 @@ static int qcom_cpufreq_ipq8064_name_version(struct device *cpu_dev,
get_krait_bin_format_a(cpu_dev, &speed, &pvs, speedbin);
ret = qcom_smem_get_soc_id(&msm_id);
- if (ret)
+ if (ret == -ENODEV) {
+ const struct of_device_id *match;
+ struct device_node *root;
+
+ root = of_find_node_by_path("/");
+ if (!root) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* Fallback to compatible match with no SMEM initialized */
+ match = of_match_node(qcom_cpufreq_ipq806x_match_list, root);
+ of_node_put(root);
+ if (!match) {
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ /* We found a matching device, get the msm_id from the data entry */
+ msm_id = (int)(uintptr_t)match->data;
+ ret = 0;
+ } else if (ret) {
goto exit;
+ }
switch (msm_id) {
case QCOM_ID_IPQ8062:
diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c
index 4215621deb3f..ba8a1c96427a 100644
--- a/drivers/cpufreq/s5pv210-cpufreq.c
+++ b/drivers/cpufreq/s5pv210-cpufreq.c
@@ -518,7 +518,7 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
if (policy->cpu != 0) {
ret = -EINVAL;
- goto out_dmc1;
+ goto out;
}
/*
@@ -530,7 +530,7 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
if ((mem_type != LPDDR) && (mem_type != LPDDR2)) {
pr_err("CPUFreq doesn't support this memory type\n");
ret = -EINVAL;
- goto out_dmc1;
+ goto out;
}
/* Find current refresh counter and frequency each DMC */
@@ -544,6 +544,8 @@ static int s5pv210_cpu_init(struct cpufreq_policy *policy)
cpufreq_generic_init(policy, s5pv210_freq_table, 40000);
return 0;
+out:
+ clk_put(dmc1_clk);
out_dmc1:
clk_put(dmc0_clk);
out_dmc0:
diff --git a/drivers/cpufreq/sun50i-cpufreq-nvmem.c b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
index 744312a44279..4fffc8e83692 100644
--- a/drivers/cpufreq/sun50i-cpufreq-nvmem.c
+++ b/drivers/cpufreq/sun50i-cpufreq-nvmem.c
@@ -332,13 +332,6 @@ static const struct of_device_id sun50i_cpufreq_match_list[] = {
};
MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list);
-static const struct of_device_id *sun50i_cpufreq_match_node(void)
-{
- struct device_node *np __free(device_node) = of_find_node_by_path("/");
-
- return of_match_node(sun50i_cpufreq_match_list, np);
-}
-
/*
* Since the driver depends on nvmem drivers, which may return EPROBE_DEFER,
* all the real activity is done in the probe, which may be defered as well.
@@ -346,11 +339,9 @@ static const struct of_device_id *sun50i_cpufreq_match_node(void)
*/
static int __init sun50i_cpufreq_init(void)
{
- const struct of_device_id *match;
int ret;
- match = sun50i_cpufreq_match_node();
- if (!match)
+ if (!of_machine_device_match(sun50i_cpufreq_match_list))
return -ENODEV;
ret = platform_driver_register(&sun50i_cpufreq_driver);
diff --git a/drivers/cpufreq/tegra186-cpufreq.c b/drivers/cpufreq/tegra186-cpufreq.c
index 136ab102f636..34ed943c5f34 100644
--- a/drivers/cpufreq/tegra186-cpufreq.c
+++ b/drivers/cpufreq/tegra186-cpufreq.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/units.h>
#include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h>
@@ -58,7 +59,7 @@ static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
};
struct tegra186_cpufreq_cluster {
- struct cpufreq_frequency_table *table;
+ struct cpufreq_frequency_table *bpmp_lut;
u32 ref_clk_khz;
u32 div;
};
@@ -66,16 +67,119 @@ struct tegra186_cpufreq_cluster {
struct tegra186_cpufreq_data {
void __iomem *regs;
const struct tegra186_cpufreq_cpu *cpus;
+ bool icc_dram_bw_scaling;
struct tegra186_cpufreq_cluster clusters[];
};
+static int tegra_cpufreq_set_bw(struct cpufreq_policy *policy, unsigned long freq_khz)
+{
+ struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
+ struct device *dev;
+ int ret;
+
+ dev = get_cpu_device(policy->cpu);
+ if (!dev)
+ return -ENODEV;
+
+ struct dev_pm_opp *opp __free(put_opp) =
+ dev_pm_opp_find_freq_exact(dev, freq_khz * HZ_PER_KHZ, true);
+ if (IS_ERR(opp))
+ return PTR_ERR(opp);
+
+ ret = dev_pm_opp_set_opp(dev, opp);
+ if (ret)
+ data->icc_dram_bw_scaling = false;
+
+ return ret;
+}
+
+static int tegra_cpufreq_init_cpufreq_table(struct cpufreq_policy *policy,
+ struct cpufreq_frequency_table *bpmp_lut,
+ struct cpufreq_frequency_table **opp_table)
+{
+ struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
+ struct cpufreq_frequency_table *freq_table = NULL;
+ struct cpufreq_frequency_table *pos;
+ struct device *cpu_dev;
+ unsigned long rate;
+ int ret, max_opps;
+ int j = 0;
+
+ cpu_dev = get_cpu_device(policy->cpu);
+ if (!cpu_dev) {
+ pr_err("%s: failed to get cpu%d device\n", __func__, policy->cpu);
+ return -ENODEV;
+ }
+
+ /* Initialize OPP table mentioned in operating-points-v2 property in DT */
+ ret = dev_pm_opp_of_add_table_indexed(cpu_dev, 0);
+ if (ret) {
+ dev_err(cpu_dev, "Invalid or empty opp table in device tree\n");
+ data->icc_dram_bw_scaling = false;
+ return ret;
+ }
+
+ max_opps = dev_pm_opp_get_opp_count(cpu_dev);
+ if (max_opps <= 0) {
+ dev_err(cpu_dev, "Failed to add OPPs\n");
+ return max_opps;
+ }
+
+ /* Disable all opps and cross-validate against LUT later */
+ for (rate = 0; ; rate++) {
+ struct dev_pm_opp *opp __free(put_opp) =
+ dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
+ if (IS_ERR(opp))
+ break;
+
+ dev_pm_opp_disable(cpu_dev, rate);
+ }
+
+ freq_table = kcalloc((max_opps + 1), sizeof(*freq_table), GFP_KERNEL);
+ if (!freq_table)
+ return -ENOMEM;
+
+ /*
+ * Cross check the frequencies from BPMP-FW LUT against the OPP's present in DT.
+ * Enable only those DT OPP's which are present in LUT also.
+ */
+ cpufreq_for_each_valid_entry(pos, bpmp_lut) {
+ struct dev_pm_opp *opp __free(put_opp) =
+ dev_pm_opp_find_freq_exact(cpu_dev, pos->frequency * HZ_PER_KHZ, false);
+ if (IS_ERR(opp))
+ continue;
+
+ ret = dev_pm_opp_enable(cpu_dev, pos->frequency * HZ_PER_KHZ);
+ if (ret < 0)
+ return ret;
+
+ freq_table[j].driver_data = pos->driver_data;
+ freq_table[j].frequency = pos->frequency;
+ j++;
+ }
+
+ freq_table[j].driver_data = pos->driver_data;
+ freq_table[j].frequency = CPUFREQ_TABLE_END;
+
+ *opp_table = &freq_table[0];
+
+ dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
+
+ /* Prime interconnect data */
+ tegra_cpufreq_set_bw(policy, freq_table[j - 1].frequency);
+
+ return ret;
+}
+
static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
{
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;
+ struct cpufreq_frequency_table *freq_table;
+ struct cpufreq_frequency_table *bpmp_lut;
u32 cpu;
+ int ret;
- policy->freq_table = data->clusters[cluster].table;
policy->cpuinfo.transition_latency = 300 * 1000;
policy->driver_data = NULL;
@@ -85,6 +189,20 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
cpumask_set_cpu(cpu, policy->cpus);
}
+ bpmp_lut = data->clusters[cluster].bpmp_lut;
+
+ if (data->icc_dram_bw_scaling) {
+ ret = tegra_cpufreq_init_cpufreq_table(policy, bpmp_lut, &freq_table);
+ if (!ret) {
+ policy->freq_table = freq_table;
+ return 0;
+ }
+ }
+
+ data->icc_dram_bw_scaling = false;
+ policy->freq_table = bpmp_lut;
+ pr_info("OPP tables missing from DT, EMC frequency scaling disabled\n");
+
return 0;
}
@@ -102,6 +220,10 @@ static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
writel(edvd_val, data->regs + edvd_offset);
}
+ if (data->icc_dram_bw_scaling)
+ tegra_cpufreq_set_bw(policy, tbl->frequency);
+
+
return 0;
}
@@ -134,7 +256,7 @@ static struct cpufreq_driver tegra186_cpufreq_driver = {
.init = tegra186_cpufreq_init,
};
-static struct cpufreq_frequency_table *init_vhint_table(
+static struct cpufreq_frequency_table *tegra_cpufreq_bpmp_read_lut(
struct platform_device *pdev, struct tegra_bpmp *bpmp,
struct tegra186_cpufreq_cluster *cluster, unsigned int cluster_id,
int *num_rates)
@@ -229,6 +351,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
{
struct tegra186_cpufreq_data *data;
struct tegra_bpmp *bpmp;
+ struct device *cpu_dev;
unsigned int i = 0, err, edvd_offset;
int num_rates = 0;
u32 edvd_val, cpu;
@@ -254,9 +377,9 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
for (i = 0; i < TEGRA186_NUM_CLUSTERS; i++) {
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
- cluster->table = init_vhint_table(pdev, bpmp, cluster, i, &num_rates);
- if (IS_ERR(cluster->table)) {
- err = PTR_ERR(cluster->table);
+ cluster->bpmp_lut = tegra_cpufreq_bpmp_read_lut(pdev, bpmp, cluster, i, &num_rates);
+ if (IS_ERR(cluster->bpmp_lut)) {
+ err = PTR_ERR(cluster->bpmp_lut);
goto put_bpmp;
} else if (!num_rates) {
err = -EINVAL;
@@ -265,7 +388,7 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
for (cpu = 0; cpu < ARRAY_SIZE(tegra186_cpus); cpu++) {
if (data->cpus[cpu].bpmp_cluster_id == i) {
- edvd_val = cluster->table[num_rates - 1].driver_data;
+ edvd_val = cluster->bpmp_lut[num_rates - 1].driver_data;
edvd_offset = data->cpus[cpu].edvd_offset;
writel(edvd_val, data->regs + edvd_offset);
}
@@ -274,6 +397,19 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
tegra186_cpufreq_driver.driver_data = data;
+ /* Check for optional OPPv2 and interconnect paths on CPU0 to enable ICC scaling */
+ cpu_dev = get_cpu_device(0);
+ if (!cpu_dev) {
+ err = -EPROBE_DEFER;
+ goto put_bpmp;
+ }
+
+ if (dev_pm_opp_of_get_opp_desc_node(cpu_dev)) {
+ err = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
+ if (!err)
+ data->icc_dram_bw_scaling = true;
+ }
+
err = cpufreq_register_driver(&tegra186_cpufreq_driver);
put_bpmp:
diff --git a/drivers/cpufreq/tegra194-cpufreq.c b/drivers/cpufreq/tegra194-cpufreq.c
index 9b4f516f313e..695599e1001f 100644
--- a/drivers/cpufreq/tegra194-cpufreq.c
+++ b/drivers/cpufreq/tegra194-cpufreq.c
@@ -750,7 +750,8 @@ static int tegra194_cpufreq_probe(struct platform_device *pdev)
if (IS_ERR(bpmp))
return PTR_ERR(bpmp);
- read_counters_wq = alloc_workqueue("read_counters_wq", __WQ_LEGACY, 1);
+ read_counters_wq = alloc_workqueue("read_counters_wq",
+ __WQ_LEGACY | WQ_PERCPU, 1);
if (!read_counters_wq) {
dev_err(&pdev->dev, "fail to create_workqueue\n");
err = -EINVAL;
diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c
index 4abba42fcc31..08f6bf2f6409 100644
--- a/drivers/cpuidle/cpuidle-big_little.c
+++ b/drivers/cpuidle/cpuidle-big_little.c
@@ -166,20 +166,11 @@ static const struct of_device_id compatible_machine_match[] = {
static int __init bl_idle_init(void)
{
int ret;
- struct device_node *root = of_find_node_by_path("/");
- const struct of_device_id *match_id;
-
- if (!root)
- return -ENODEV;
/*
* Initialize the driver just for a compliant set of machines
*/
- match_id = of_match_node(compatible_machine_match, root);
-
- of_node_put(root);
-
- if (!match_id)
+ if (!of_machine_device_match(compatible_machine_match))
return -ENODEV;
if (!mcpm_is_available())
diff --git a/drivers/cpuidle/cpuidle-psci.c b/drivers/cpuidle/cpuidle-psci.c
index b19bc60cc627..e75d85a8f90d 100644
--- a/drivers/cpuidle/cpuidle-psci.c
+++ b/drivers/cpuidle/cpuidle-psci.c
@@ -382,8 +382,8 @@ static int psci_idle_init_cpu(struct device *dev, int cpu)
drv->states[0].exit_latency = 1;
drv->states[0].target_residency = 1;
drv->states[0].power_usage = UINT_MAX;
- strcpy(drv->states[0].name, "WFI");
- strcpy(drv->states[0].desc, "ARM WFI");
+ strscpy(drv->states[0].name, "WFI");
+ strscpy(drv->states[0].desc, "ARM WFI");
/*
* If no DT idle states are detected (ret == 0) let the driver
diff --git a/drivers/cpuidle/cpuidle-riscv-sbi.c b/drivers/cpuidle/cpuidle-riscv-sbi.c
index a360bc4d20b7..19be6475d356 100644
--- a/drivers/cpuidle/cpuidle-riscv-sbi.c
+++ b/drivers/cpuidle/cpuidle-riscv-sbi.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
@@ -303,8 +304,8 @@ static int sbi_cpuidle_init_cpu(struct device *dev, int cpu)
drv->states[0].exit_latency = 1;
drv->states[0].target_residency = 1;
drv->states[0].power_usage = UINT_MAX;
- strcpy(drv->states[0].name, "WFI");
- strcpy(drv->states[0].desc, "RISC-V WFI");
+ strscpy(drv->states[0].name, "WFI");
+ strscpy(drv->states[0].desc, "RISC-V WFI");
/*
* If no DT idle states are detected (ret == 0) let the driver
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 56132e843c99..c7876e9e024f 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -184,20 +184,22 @@ static noinstr void enter_s2idle_proper(struct cpuidle_driver *drv,
* cpuidle_enter_s2idle - Enter an idle state suitable for suspend-to-idle.
* @drv: cpuidle driver for the given CPU.
* @dev: cpuidle device for the given CPU.
+ * @latency_limit_ns: Idle state exit latency limit
*
* If there are states with the ->enter_s2idle callback, find the deepest of
* them and enter it with frozen tick.
*/
-int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev)
+int cpuidle_enter_s2idle(struct cpuidle_driver *drv, struct cpuidle_device *dev,
+ u64 latency_limit_ns)
{
int index;
/*
- * Find the deepest state with ->enter_s2idle present, which guarantees
- * that interrupts won't be enabled when it exits and allows the tick to
- * be frozen safely.
+ * Find the deepest state with ->enter_s2idle present that meets the
+ * specified latency limit, which guarantees that interrupts won't be
+ * enabled when it exits and allows the tick to be frozen safely.
*/
- index = find_deepest_state(drv, dev, U64_MAX, 0, true);
+ index = find_deepest_state(drv, dev, latency_limit_ns, 0, true);
if (index > 0) {
enter_s2idle_proper(drv, dev, index);
local_irq_enable();
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 9bbfa594c442..370664c47e65 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -8,6 +8,8 @@
* This code is licenced under the GPL.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/mutex.h>
#include <linux/module.h>
#include <linux/sched.h>
@@ -193,6 +195,14 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv)
s->exit_latency_ns = 0;
else
s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC);
+
+ /*
+ * Warn if the exit latency of a CPU idle state exceeds its
+ * target residency which is assumed to never happen in cpuidle
+ * in multiple places.
+ */
+ if (s->exit_latency_ns > s->target_residency_ns)
+ pr_warn("Idle state %d target residency too low\n", i);
}
}
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 0d0f9751ff8f..5d0e7f78c6c5 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -111,6 +111,10 @@ s64 cpuidle_governor_latency_req(unsigned int cpu)
struct device *device = get_cpu_device(cpu);
int device_req = dev_pm_qos_raw_resume_latency(device);
int global_req = cpu_latency_qos_limit();
+ int global_wake_req = cpu_wakeup_latency_qos_limit();
+
+ if (global_req > global_wake_req)
+ global_req = global_wake_req;
if (device_req > global_req)
device_req = global_req;
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 4d9aa5ce31f0..64d6f7a1c776 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -188,20 +188,17 @@ again:
*
* This can deal with workloads that have long pauses interspersed
* with sporadic activity with a bunch of short pauses.
+ *
+ * However, if the number of remaining samples is too small to exclude
+ * any more outliers, allow the deepest available idle state to be
+ * selected because there are systems where the time spent by CPUs in
+ * deep idle states is correlated to the maximum frequency the CPUs
+ * can get to. On those systems, shallow idle states should be avoided
+ * unless there is a clear indication that the given CPU is most likley
+ * going to be woken up shortly.
*/
- if (divisor * 4 <= INTERVALS * 3) {
- /*
- * If there are sufficiently many data points still under
- * consideration after the outliers have been eliminated,
- * returning without a prediction would be a mistake because it
- * is likely that the next interval will not exceed the current
- * maximum, so return the latter in that case.
- */
- if (divisor >= INTERVALS / 2)
- return max;
-
+ if (divisor * 4 <= INTERVALS * 3)
return UINT_MAX;
- }
/* Update the thresholds for the next round. */
if (avg - min > max - avg)
@@ -320,11 +317,15 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
}
/*
- * Use a physical idle state, not busy polling, unless a timer
- * is going to trigger soon enough.
+ * Use a physical idle state instead of busy polling so long as
+ * its target residency is below the residency threshold, its
+ * exit latency is not greater than the predicted idle duration,
+ * and the next timer doesn't expire soon.
*/
if ((drv->states[idx].flags & CPUIDLE_FLAG_POLLING) &&
- s->target_residency_ns <= data->next_timer_ns) {
+ s->target_residency_ns < RESIDENCY_THRESHOLD_NS &&
+ s->target_residency_ns <= data->next_timer_ns &&
+ s->exit_latency_ns <= predicted_ns) {
predicted_ns = s->target_residency_ns;
idx = i;
break;
diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c
index bfa55c1eab5b..81ac5fd58a1c 100644
--- a/drivers/cpuidle/governors/teo.c
+++ b/drivers/cpuidle/governors/teo.c
@@ -76,7 +76,7 @@
* likely woken up by a non-timer wakeup source).
*
* 2. If the second sum computed in step 1 is greater than a half of the sum of
- * both metrics for the candidate state bin and all subsequent bins(if any),
+ * both metrics for the candidate state bin and all subsequent bins (if any),
* a shallower idle state is likely to be more suitable, so look for it.
*
* - Traverse the enabled idle states shallower than the candidate one in the
@@ -133,21 +133,33 @@ struct teo_bin {
* @sleep_length_ns: Time till the closest timer event (at the selection time).
* @state_bins: Idle state data bins for this CPU.
* @total: Grand total of the "intercepts" and "hits" metrics for all bins.
+ * @total_tick: Wakeups by the scheduler tick.
* @tick_intercepts: "Intercepts" before TICK_NSEC.
* @short_idles: Wakeups after short idle periods.
- * @artificial_wakeup: Set if the wakeup has been triggered by a safety net.
+ * @tick_wakeup: Set if the last wakeup was by the scheduler tick.
*/
struct teo_cpu {
s64 sleep_length_ns;
struct teo_bin state_bins[CPUIDLE_STATE_MAX];
unsigned int total;
+ unsigned int total_tick;
unsigned int tick_intercepts;
unsigned int short_idles;
- bool artificial_wakeup;
+ bool tick_wakeup;
};
static DEFINE_PER_CPU(struct teo_cpu, teo_cpus);
+static void teo_decay(unsigned int *metric)
+{
+ unsigned int delta = *metric >> DECAY_SHIFT;
+
+ if (delta)
+ *metric -= delta;
+ else
+ *metric = 0;
+}
+
/**
* teo_update - Update CPU metrics after wakeup.
* @drv: cpuidle driver containing state data.
@@ -155,21 +167,22 @@ static DEFINE_PER_CPU(struct teo_cpu, teo_cpus);
*/
static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
+ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus);
int i, idx_timer = 0, idx_duration = 0;
- s64 target_residency_ns;
- u64 measured_ns;
+ s64 target_residency_ns, measured_ns;
+ unsigned int total = 0;
- cpu_data->short_idles -= cpu_data->short_idles >> DECAY_SHIFT;
+ teo_decay(&cpu_data->short_idles);
- if (cpu_data->artificial_wakeup) {
+ if (dev->poll_time_limit) {
+ dev->poll_time_limit = false;
/*
- * If one of the safety nets has triggered, assume that this
+ * Polling state timeout has triggered, so assume that this
* might have been a long sleep.
*/
- measured_ns = U64_MAX;
+ measured_ns = S64_MAX;
} else {
- u64 lat_ns = drv->states[dev->last_state_idx].exit_latency_ns;
+ s64 lat_ns = drv->states[dev->last_state_idx].exit_latency_ns;
measured_ns = dev->last_residency_ns;
/*
@@ -196,8 +209,10 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
for (i = 0; i < drv->state_count; i++) {
struct teo_bin *bin = &cpu_data->state_bins[i];
- bin->hits -= bin->hits >> DECAY_SHIFT;
- bin->intercepts -= bin->intercepts >> DECAY_SHIFT;
+ teo_decay(&bin->hits);
+ total += bin->hits;
+ teo_decay(&bin->intercepts);
+ total += bin->intercepts;
target_residency_ns = drv->states[i].target_residency_ns;
@@ -208,7 +223,24 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
}
}
- cpu_data->tick_intercepts -= cpu_data->tick_intercepts >> DECAY_SHIFT;
+ cpu_data->total = total + PULSE;
+
+ teo_decay(&cpu_data->tick_intercepts);
+
+ teo_decay(&cpu_data->total_tick);
+ if (cpu_data->tick_wakeup) {
+ cpu_data->total_tick += PULSE;
+ /*
+ * If tick wakeups dominate the wakeup pattern, count this one
+ * as a hit on the deepest available idle state to increase the
+ * likelihood of stopping the tick.
+ */
+ if (3 * cpu_data->total_tick > 2 * cpu_data->total) {
+ cpu_data->state_bins[drv->state_count-1].hits += PULSE;
+ return;
+ }
+ }
+
/*
* If the measured idle duration falls into the same bin as the sleep
* length, this is a "hit", so update the "hits" metric for that bin.
@@ -219,18 +251,9 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
cpu_data->state_bins[idx_timer].hits += PULSE;
} else {
cpu_data->state_bins[idx_duration].intercepts += PULSE;
- if (TICK_NSEC <= measured_ns)
+ if (measured_ns <= TICK_NSEC)
cpu_data->tick_intercepts += PULSE;
}
-
- cpu_data->total -= cpu_data->total >> DECAY_SHIFT;
- cpu_data->total += PULSE;
-}
-
-static bool teo_state_ok(int i, struct cpuidle_driver *drv)
-{
- return !tick_nohz_tick_stopped() ||
- drv->states[i].target_residency_ns >= TICK_NSEC;
}
/**
@@ -239,17 +262,15 @@ static bool teo_state_ok(int i, struct cpuidle_driver *drv)
* @dev: Target CPU.
* @state_idx: Index of the capping idle state.
* @duration_ns: Idle duration value to match.
- * @no_poll: Don't consider polling states.
*/
static int teo_find_shallower_state(struct cpuidle_driver *drv,
struct cpuidle_device *dev, int state_idx,
- s64 duration_ns, bool no_poll)
+ s64 duration_ns)
{
int i;
for (i = state_idx - 1; i >= 0; i--) {
- if (dev->states_usage[i].disable ||
- (no_poll && drv->states[i].flags & CPUIDLE_FLAG_POLLING))
+ if (dev->states_usage[i].disable)
continue;
state_idx = i;
@@ -268,7 +289,7 @@ static int teo_find_shallower_state(struct cpuidle_driver *drv,
static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
bool *stop_tick)
{
- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
+ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus);
s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
ktime_t delta_tick = TICK_NSEC / 2;
unsigned int idx_intercept_sum = 0;
@@ -356,7 +377,18 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* better choice.
*/
if (2 * idx_intercept_sum > cpu_data->total - idx_hit_sum) {
- int first_suitable_idx = idx;
+ int min_idx = idx0;
+
+ if (tick_nohz_tick_stopped()) {
+ /*
+ * Look for the shallowest idle state below the current
+ * candidate one whose target residency is at least
+ * equal to the tick period length.
+ */
+ while (min_idx < idx &&
+ drv->states[min_idx].target_residency_ns < TICK_NSEC)
+ min_idx++;
+ }
/*
* Look for the deepest idle state whose target residency had
@@ -366,49 +398,14 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* Take the possible duration limitation present if the tick
* has been stopped already into account.
*/
- intercept_sum = 0;
-
- for (i = idx - 1; i >= 0; i--) {
- struct teo_bin *bin = &cpu_data->state_bins[i];
-
- intercept_sum += bin->intercepts;
-
- if (2 * intercept_sum > idx_intercept_sum) {
- /*
- * Use the current state unless it is too
- * shallow or disabled, in which case take the
- * first enabled state that is deep enough.
- */
- if (teo_state_ok(i, drv) &&
- !dev->states_usage[i].disable) {
- idx = i;
- break;
- }
- idx = first_suitable_idx;
- break;
- }
+ for (i = idx - 1, intercept_sum = 0; i >= min_idx; i--) {
+ intercept_sum += cpu_data->state_bins[i].intercepts;
if (dev->states_usage[i].disable)
continue;
- if (teo_state_ok(i, drv)) {
- /*
- * The current state is deep enough, but still
- * there may be a better one.
- */
- first_suitable_idx = i;
- continue;
- }
-
- /*
- * The current state is too shallow, so if no suitable
- * states other than the initial candidate have been
- * found, give up (the remaining states to check are
- * shallower still), but otherwise the first suitable
- * state other than the initial candidate may turn out
- * to be preferable.
- */
- if (first_suitable_idx == idx)
+ idx = i;
+ if (2 * intercept_sum > idx_intercept_sum)
break;
}
}
@@ -458,11 +455,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
* If the closest expected timer is before the target residency of the
* candidate state, a shallower one needs to be found.
*/
- if (drv->states[idx].target_residency_ns > duration_ns) {
- i = teo_find_shallower_state(drv, dev, idx, duration_ns, false);
- if (teo_state_ok(i, drv))
- idx = i;
- }
+ if (drv->states[idx].target_residency_ns > duration_ns)
+ idx = teo_find_shallower_state(drv, dev, idx, duration_ns);
/*
* If the selected state's target residency is below the tick length
@@ -490,7 +484,7 @@ end:
*/
if (idx > idx0 &&
drv->states[idx].target_residency_ns > delta_tick)
- idx = teo_find_shallower_state(drv, dev, idx, delta_tick, false);
+ idx = teo_find_shallower_state(drv, dev, idx, delta_tick);
out_tick:
*stop_tick = false;
@@ -504,20 +498,11 @@ out_tick:
*/
static void teo_reflect(struct cpuidle_device *dev, int state)
{
- struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
+ struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus);
+
+ cpu_data->tick_wakeup = tick_nohz_idle_got_tick();
dev->last_state_idx = state;
- if (dev->poll_time_limit ||
- (tick_nohz_idle_got_tick() && cpu_data->sleep_length_ns > TICK_NSEC)) {
- /*
- * The wakeup was not "genuine", but triggered by one of the
- * safety nets.
- */
- dev->poll_time_limit = false;
- cpu_data->artificial_wakeup = true;
- } else {
- cpu_data->artificial_wakeup = false;
- }
}
/**
diff --git a/drivers/cpuidle/poll_state.c b/drivers/cpuidle/poll_state.c
index 9b6d90a72601..c7524e4c522a 100644
--- a/drivers/cpuidle/poll_state.c
+++ b/drivers/cpuidle/poll_state.c
@@ -4,9 +4,13 @@
*/
#include <linux/cpuidle.h>
+#include <linux/export.h>
+#include <linux/irqflags.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/sched/idle.h>
+#include <linux/sprintf.h>
+#include <linux/types.h>
#define POLL_IDLE_RELAX_COUNT 200
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index a6688d54984c..8d3b5d2890f8 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -728,6 +728,7 @@ config CRYPTO_DEV_TEGRA
config CRYPTO_DEV_XILINX_TRNG
tristate "Support for Xilinx True Random Generator"
depends on ZYNQMP_FIRMWARE || COMPILE_TEST
+ select CRYPTO_DF80090A
select CRYPTO_RNG
select HW_RANDOM
help
diff --git a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c
index 8bc08089f044..36a1ebca2e70 100644
--- a/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c
+++ b/drivers/crypto/allwinner/sun8i-ss/sun8i-ss-hash.c
@@ -502,6 +502,7 @@ int sun8i_ss_hash_run(struct crypto_engine *engine, void *breq)
algt = container_of(alg, struct sun8i_ss_alg_template, alg.hash.base);
ss = algt->ss;
+ j = 0;
digestsize = crypto_ahash_digestsize(tfm);
if (digestsize == SHA224_DIGEST_SIZE)
@@ -536,7 +537,6 @@ int sun8i_ss_hash_run(struct crypto_engine *engine, void *breq)
goto err_dma_result;
}
- j = 0;
len = areq->nbytes;
sg = areq->src;
i = 0;
diff --git a/drivers/crypto/aspeed/aspeed-acry.c b/drivers/crypto/aspeed/aspeed-acry.c
index 8d1c79aaca07..5993bcba9716 100644
--- a/drivers/crypto/aspeed/aspeed-acry.c
+++ b/drivers/crypto/aspeed/aspeed-acry.c
@@ -787,7 +787,6 @@ static int aspeed_acry_probe(struct platform_device *pdev)
err_engine_rsa_start:
crypto_engine_exit(acry_dev->crypt_engine_rsa);
clk_exit:
- clk_disable_unprepare(acry_dev->clk);
return rc;
}
@@ -799,7 +798,6 @@ static void aspeed_acry_remove(struct platform_device *pdev)
aspeed_acry_unregister(acry_dev);
crypto_engine_exit(acry_dev->crypt_engine_rsa);
tasklet_kill(&acry_dev->done_task);
- clk_disable_unprepare(acry_dev->clk);
}
MODULE_DEVICE_TABLE(of, aspeed_acry_of_matches);
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index a895e4289efa..9688d116d07e 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -402,7 +402,7 @@ EXPORT_SYMBOL(atmel_i2c_probe);
static int __init atmel_i2c_init(void)
{
- atmel_wq = alloc_workqueue("atmel_wq", 0, 0);
+ atmel_wq = alloc_workqueue("atmel_wq", WQ_PERCPU, 0);
return atmel_wq ? 0 : -ENOMEM;
}
diff --git a/drivers/crypto/axis/artpec6_crypto.c b/drivers/crypto/axis/artpec6_crypto.c
index 75ee065da1ec..b04d6379244a 100644
--- a/drivers/crypto/axis/artpec6_crypto.c
+++ b/drivers/crypto/axis/artpec6_crypto.c
@@ -252,7 +252,7 @@ struct artpec6_crypto_dma_descriptors {
};
enum artpec6_crypto_variant {
- ARTPEC6_CRYPTO,
+ ARTPEC6_CRYPTO = 1,
ARTPEC7_CRYPTO,
};
@@ -2842,7 +2842,6 @@ MODULE_DEVICE_TABLE(of, artpec6_crypto_of_match);
static int artpec6_crypto_probe(struct platform_device *pdev)
{
- const struct of_device_id *match;
enum artpec6_crypto_variant variant;
struct artpec6_crypto *ac;
struct device *dev = &pdev->dev;
@@ -2853,12 +2852,10 @@ static int artpec6_crypto_probe(struct platform_device *pdev)
if (artpec6_crypto_dev)
return -ENODEV;
- match = of_match_node(artpec6_crypto_of_match, dev->of_node);
- if (!match)
+ variant = (enum artpec6_crypto_variant)of_device_get_match_data(dev);
+ if (!variant)
return -EINVAL;
- variant = (enum artpec6_crypto_variant)match->data;
-
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
diff --git a/drivers/crypto/caam/blob_gen.c b/drivers/crypto/caam/blob_gen.c
index 079a22cc9f02..c18dbac56493 100644
--- a/drivers/crypto/caam/blob_gen.c
+++ b/drivers/crypto/caam/blob_gen.c
@@ -2,13 +2,14 @@
/*
* Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
* Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
- * Copyright 2024 NXP
+ * Copyright 2024-2025 NXP
*/
#define pr_fmt(fmt) "caam blob_gen: " fmt
#include <linux/bitfield.h>
#include <linux/device.h>
+#include <keys/trusted-type.h>
#include <soc/fsl/caam-blob.h>
#include "compat.h"
@@ -60,18 +61,27 @@ static void caam_blob_job_done(struct device *dev, u32 *desc, u32 err, void *con
complete(&res->completion);
}
+static u32 check_caam_state(struct device *jrdev)
+{
+ const struct caam_drv_private *ctrlpriv;
+
+ ctrlpriv = dev_get_drvdata(jrdev->parent);
+ return FIELD_GET(CSTA_MOO, rd_reg32(&ctrlpriv->jr[0]->perfmon.status));
+}
+
int caam_process_blob(struct caam_blob_priv *priv,
struct caam_blob_info *info, bool encap)
{
- const struct caam_drv_private *ctrlpriv;
struct caam_blob_job_result testres;
struct device *jrdev = &priv->jrdev;
dma_addr_t dma_in, dma_out;
int op = OP_PCLID_BLOB;
+ int hwbk_caam_ovhd = 0;
size_t output_len;
u32 *desc;
u32 moo;
int ret;
+ int len;
if (info->key_mod_len > CAAM_BLOB_KEYMOD_LENGTH)
return -EINVAL;
@@ -82,14 +92,29 @@ int caam_process_blob(struct caam_blob_priv *priv,
} else {
op |= OP_TYPE_DECAP_PROTOCOL;
output_len = info->input_len - CAAM_BLOB_OVERHEAD;
+ info->output_len = output_len;
+ }
+
+ if (encap && info->pkey_info.is_pkey) {
+ op |= OP_PCL_BLOB_BLACK;
+ if (info->pkey_info.key_enc_algo == CAAM_ENC_ALGO_CCM) {
+ op |= OP_PCL_BLOB_EKT;
+ hwbk_caam_ovhd = CAAM_CCM_OVERHEAD;
+ }
+ if ((info->input_len + hwbk_caam_ovhd) > MAX_KEY_SIZE)
+ return -EINVAL;
+
+ len = info->input_len + hwbk_caam_ovhd;
+ } else {
+ len = info->input_len;
}
desc = kzalloc(CAAM_BLOB_DESC_BYTES_MAX, GFP_KERNEL);
if (!desc)
return -ENOMEM;
- dma_in = dma_map_single(jrdev, info->input, info->input_len,
- DMA_TO_DEVICE);
+ dma_in = dma_map_single(jrdev, info->input, len,
+ encap ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE);
if (dma_mapping_error(jrdev, dma_in)) {
dev_err(jrdev, "unable to map input DMA buffer\n");
ret = -ENOMEM;
@@ -104,8 +129,7 @@ int caam_process_blob(struct caam_blob_priv *priv,
goto out_unmap_in;
}
- ctrlpriv = dev_get_drvdata(jrdev->parent);
- moo = FIELD_GET(CSTA_MOO, rd_reg32(&ctrlpriv->jr[0]->perfmon.status));
+ moo = check_caam_state(jrdev);
if (moo != CSTA_MOO_SECURE && moo != CSTA_MOO_TRUSTED)
dev_warn(jrdev,
"using insecure test key, enable HAB to use unique device key!\n");
@@ -117,18 +141,48 @@ int caam_process_blob(struct caam_blob_priv *priv,
* Class 1 Context DWords 0+1+2+3. The random BK is stored in the
* Class 1 Key Register. Operation Mode is set to AES-CCM.
*/
-
init_job_desc(desc, 0);
+
+ if (encap && info->pkey_info.is_pkey) {
+ /*!1. key command used to load class 1 key register
+ * from input plain key.
+ */
+ append_key(desc, dma_in, info->input_len,
+ CLASS_1 | KEY_DEST_CLASS_REG);
+ /*!2. Fifostore to store protected key from class 1 key register. */
+ if (info->pkey_info.key_enc_algo == CAAM_ENC_ALGO_CCM) {
+ append_fifo_store(desc, dma_in, info->input_len,
+ LDST_CLASS_1_CCB |
+ FIFOST_TYPE_KEY_CCM_JKEK);
+ } else {
+ append_fifo_store(desc, dma_in, info->input_len,
+ LDST_CLASS_1_CCB |
+ FIFOST_TYPE_KEY_KEK);
+ }
+ /*
+ * JUMP_OFFSET specifies the offset of the JUMP target from
+ * the JUMP command's address in the descriptor buffer.
+ */
+ append_jump(desc, JUMP_COND_NOP | BIT(0) << JUMP_OFFSET_SHIFT);
+ }
+
+ /*!3. Load class 2 key with key modifier. */
append_key_as_imm(desc, info->key_mod, info->key_mod_len,
- info->key_mod_len, CLASS_2 | KEY_DEST_CLASS_REG);
- append_seq_in_ptr_intlen(desc, dma_in, info->input_len, 0);
- append_seq_out_ptr_intlen(desc, dma_out, output_len, 0);
+ info->key_mod_len, CLASS_2 | KEY_DEST_CLASS_REG);
+
+ /*!4. SEQ IN PTR Command. */
+ append_seq_in_ptr(desc, dma_in, info->input_len, 0);
+
+ /*!5. SEQ OUT PTR Command. */
+ append_seq_out_ptr(desc, dma_out, output_len, 0);
+
+ /*!6. Blob encapsulation/decapsulation PROTOCOL Command. */
append_operation(desc, op);
- print_hex_dump_debug("data@"__stringify(__LINE__)": ",
+ print_hex_dump_debug("data@" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 1, info->input,
- info->input_len, false);
- print_hex_dump_debug("jobdesc@"__stringify(__LINE__)": ",
+ len, false);
+ print_hex_dump_debug("jobdesc@" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 1, desc,
desc_bytes(desc), false);
@@ -139,7 +193,7 @@ int caam_process_blob(struct caam_blob_priv *priv,
if (ret == -EINPROGRESS) {
wait_for_completion(&testres.completion);
ret = testres.err;
- print_hex_dump_debug("output@"__stringify(__LINE__)": ",
+ print_hex_dump_debug("output@" __stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 1, info->output,
output_len, false);
}
@@ -149,10 +203,10 @@ int caam_process_blob(struct caam_blob_priv *priv,
dma_unmap_single(jrdev, dma_out, output_len, DMA_FROM_DEVICE);
out_unmap_in:
- dma_unmap_single(jrdev, dma_in, info->input_len, DMA_TO_DEVICE);
+ dma_unmap_single(jrdev, dma_in, len,
+ encap ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE);
out_free:
kfree(desc);
-
return ret;
}
EXPORT_SYMBOL(caam_process_blob);
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 2cfb1b8d8c7c..32a6e6e15ee2 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -3,7 +3,7 @@
* caam - Freescale FSL CAAM support for crypto API
*
* Copyright 2008-2011 Freescale Semiconductor, Inc.
- * Copyright 2016-2019, 2023 NXP
+ * Copyright 2016-2019, 2023, 2025 NXP
*
* Based on talitos crypto API driver.
*
@@ -61,13 +61,16 @@
#include <crypto/internal/engine.h>
#include <crypto/internal/skcipher.h>
#include <crypto/xts.h>
+#include <keys/trusted-type.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/key-type.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <soc/fsl/caam-blob.h>
/*
* crypto alg
@@ -119,12 +122,15 @@ struct caam_ctx {
dma_addr_t sh_desc_enc_dma;
dma_addr_t sh_desc_dec_dma;
dma_addr_t key_dma;
+ u8 protected_key[CAAM_MAX_KEY_SIZE];
+ dma_addr_t protected_key_dma;
enum dma_data_direction dir;
struct device *jrdev;
struct alginfo adata;
struct alginfo cdata;
unsigned int authsize;
bool xts_key_fallback;
+ bool is_blob;
struct crypto_skcipher *fallback;
};
@@ -751,9 +757,14 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key,
print_hex_dump_debug("key in @"__stringify(__LINE__)": ",
DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+ /* Here keylen is actual key length */
ctx->cdata.keylen = keylen;
ctx->cdata.key_virt = key;
ctx->cdata.key_inline = true;
+ /* Here protected key len is plain key length */
+ ctx->cdata.plain_keylen = keylen;
+ ctx->cdata.key_cmd_opt = 0;
+
/* skcipher_encrypt shared descriptor */
desc = ctx->sh_desc_enc;
@@ -772,6 +783,62 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key,
return 0;
}
+static int paes_skcipher_setkey(struct crypto_skcipher *skcipher,
+ const u8 *key,
+ unsigned int keylen)
+{
+ struct caam_pkey_info *pkey_info = (struct caam_pkey_info *)key;
+ struct caam_ctx *ctx = crypto_skcipher_ctx_dma(skcipher);
+ struct device *jrdev = ctx->jrdev;
+ int err;
+
+ ctx->cdata.key_inline = false;
+
+ keylen = keylen - CAAM_PKEY_HEADER;
+
+ /* Retrieve the length of key */
+ ctx->cdata.plain_keylen = pkey_info->plain_key_sz;
+
+ /* Retrieve the length of blob*/
+ ctx->cdata.keylen = keylen;
+
+ /* Retrieve the address of the blob */
+ ctx->cdata.key_virt = pkey_info->key_buf;
+
+ /* Validate key length for AES algorithms */
+ err = aes_check_keylen(ctx->cdata.plain_keylen);
+ if (err) {
+ dev_err(jrdev, "bad key length\n");
+ return err;
+ }
+
+ /* set command option */
+ ctx->cdata.key_cmd_opt |= KEY_ENC;
+
+ /* check if the Protected-Key is CCM key */
+ if (pkey_info->key_enc_algo == CAAM_ENC_ALGO_CCM)
+ ctx->cdata.key_cmd_opt |= KEY_EKT;
+
+ memcpy(ctx->key, ctx->cdata.key_virt, keylen);
+ dma_sync_single_for_device(jrdev, ctx->key_dma, keylen, DMA_TO_DEVICE);
+ ctx->cdata.key_dma = ctx->key_dma;
+
+ if (pkey_info->key_enc_algo == CAAM_ENC_ALGO_CCM)
+ ctx->protected_key_dma = dma_map_single(jrdev, ctx->protected_key,
+ ctx->cdata.plain_keylen +
+ CAAM_CCM_OVERHEAD,
+ DMA_FROM_DEVICE);
+ else
+ ctx->protected_key_dma = dma_map_single(jrdev, ctx->protected_key,
+ ctx->cdata.plain_keylen,
+ DMA_FROM_DEVICE);
+
+ ctx->cdata.protected_key_dma = ctx->protected_key_dma;
+ ctx->is_blob = true;
+
+ return 0;
+}
+
static int aes_skcipher_setkey(struct crypto_skcipher *skcipher,
const u8 *key, unsigned int keylen)
{
@@ -1254,7 +1321,9 @@ static void init_skcipher_job(struct skcipher_request *req,
struct caam_ctx *ctx = crypto_skcipher_ctx_dma(skcipher);
struct device *jrdev = ctx->jrdev;
int ivsize = crypto_skcipher_ivsize(skcipher);
- u32 *desc = edesc->hw_desc;
+ u32 *desc = !ctx->is_blob ? edesc->hw_desc :
+ (u32 *)((u8 *)edesc->hw_desc + CAAM_DESC_BYTES_MAX);
+ dma_addr_t desc_dma;
u32 *sh_desc;
u32 in_options = 0, out_options = 0;
dma_addr_t src_dma, dst_dma, ptr;
@@ -1269,11 +1338,6 @@ static void init_skcipher_job(struct skcipher_request *req,
DUMP_PREFIX_ADDRESS, 16, 4, req->src,
edesc->src_nents > 1 ? 100 : req->cryptlen, 1);
- sh_desc = encrypt ? ctx->sh_desc_enc : ctx->sh_desc_dec;
- ptr = encrypt ? ctx->sh_desc_enc_dma : ctx->sh_desc_dec_dma;
-
- len = desc_len(sh_desc);
- init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
if (ivsize || edesc->mapped_src_nents > 1) {
src_dma = edesc->sec4_sg_dma;
@@ -1283,8 +1347,6 @@ static void init_skcipher_job(struct skcipher_request *req,
src_dma = sg_dma_address(req->src);
}
- append_seq_in_ptr(desc, src_dma, req->cryptlen + ivsize, in_options);
-
if (likely(req->src == req->dst)) {
dst_dma = src_dma + !!ivsize * sizeof(struct sec4_sg_entry);
out_options = in_options;
@@ -1296,7 +1358,25 @@ static void init_skcipher_job(struct skcipher_request *req,
out_options = LDST_SGF;
}
- append_seq_out_ptr(desc, dst_dma, req->cryptlen + ivsize, out_options);
+ if (ctx->is_blob) {
+ cnstr_desc_skcipher_enc_dec(desc, &ctx->cdata,
+ src_dma, dst_dma, req->cryptlen + ivsize,
+ in_options, out_options,
+ ivsize, encrypt);
+
+ desc_dma = dma_map_single(jrdev, desc, desc_bytes(desc), DMA_TO_DEVICE);
+
+ cnstr_desc_protected_blob_decap(edesc->hw_desc, &ctx->cdata, desc_dma);
+ } else {
+ sh_desc = encrypt ? ctx->sh_desc_enc : ctx->sh_desc_dec;
+ ptr = encrypt ? ctx->sh_desc_enc_dma : ctx->sh_desc_dec_dma;
+
+ len = desc_len(sh_desc);
+ init_job_desc_shared(desc, ptr, len, HDR_SHARE_DEFER | HDR_REVERSE);
+ append_seq_in_ptr(desc, src_dma, req->cryptlen + ivsize, in_options);
+
+ append_seq_out_ptr(desc, dst_dma, req->cryptlen + ivsize, out_options);
+ }
}
/*
@@ -1817,6 +1897,7 @@ static inline int skcipher_crypt(struct skcipher_request *req, bool encrypt)
struct caam_drv_private *ctrlpriv = dev_get_drvdata(jrdev->parent);
u32 *desc;
int ret = 0;
+ int len;
/*
* XTS is expected to return an error even for input length = 0
@@ -1842,8 +1923,12 @@ static inline int skcipher_crypt(struct skcipher_request *req, bool encrypt)
crypto_skcipher_decrypt(&rctx->fallback_req);
}
+ len = DESC_JOB_IO_LEN * CAAM_CMD_SZ;
+ if (ctx->is_blob)
+ len += CAAM_DESC_BYTES_MAX;
+
/* allocate extended descriptor */
- edesc = skcipher_edesc_alloc(req, DESC_JOB_IO_LEN * CAAM_CMD_SZ);
+ edesc = skcipher_edesc_alloc(req, len);
if (IS_ERR(edesc))
return PTR_ERR(edesc);
@@ -1888,6 +1973,27 @@ static struct caam_skcipher_alg driver_algs[] = {
{
.skcipher.base = {
.base = {
+ .cra_name = "cbc(paes)",
+ .cra_driver_name = "cbc-paes-caam",
+ .cra_blocksize = AES_BLOCK_SIZE,
+ },
+ .setkey = paes_skcipher_setkey,
+ .encrypt = skcipher_encrypt,
+ .decrypt = skcipher_decrypt,
+ .min_keysize = AES_MIN_KEY_SIZE + CAAM_BLOB_OVERHEAD +
+ CAAM_PKEY_HEADER,
+ .max_keysize = AES_MAX_KEY_SIZE + CAAM_BLOB_OVERHEAD +
+ CAAM_PKEY_HEADER,
+ .ivsize = AES_BLOCK_SIZE,
+ },
+ .skcipher.op = {
+ .do_one_request = skcipher_do_one_req,
+ },
+ .caam.class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
+ },
+ {
+ .skcipher.base = {
+ .base = {
.cra_name = "cbc(aes)",
.cra_driver_name = "cbc-aes-caam",
.cra_blocksize = AES_BLOCK_SIZE,
diff --git a/drivers/crypto/caam/caamalg_desc.c b/drivers/crypto/caam/caamalg_desc.c
index 7571e1ac913b..04c1105eb1f5 100644
--- a/drivers/crypto/caam/caamalg_desc.c
+++ b/drivers/crypto/caam/caamalg_desc.c
@@ -2,12 +2,13 @@
/*
* Shared descriptors for aead, skcipher algorithms
*
- * Copyright 2016-2019 NXP
+ * Copyright 2016-2019, 2025 NXP
*/
#include "compat.h"
#include "desc_constr.h"
#include "caamalg_desc.h"
+#include <soc/fsl/caam-blob.h>
/*
* For aead functions, read payload and write payload,
@@ -1364,6 +1365,84 @@ static inline void skcipher_append_src_dst(u32 *desc)
append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | KEY_VLF);
}
+void cnstr_desc_skcipher_enc_dec(u32 * const desc, struct alginfo *cdata,
+ dma_addr_t src, dma_addr_t dst, unsigned int data_sz,
+ unsigned int in_options, unsigned int out_options,
+ unsigned int ivsize, const bool encrypt)
+{
+ u32 options = cdata->algtype | OP_ALG_AS_INIT;
+
+ if (encrypt)
+ options |= OP_ALG_ENCRYPT;
+ else
+ options |= OP_ALG_DECRYPT;
+
+ init_job_desc(desc, 0);
+
+ append_jump(desc, JUMP_JSL | JUMP_TYPE_LOCAL |
+ JUMP_COND_NOP | JUMP_TEST_ALL | 1);
+
+ append_key(desc, cdata->protected_key_dma, cdata->plain_keylen,
+ CLASS_1 | KEY_DEST_CLASS_REG | cdata->key_cmd_opt);
+
+ append_seq_in_ptr(desc, src, data_sz, in_options);
+
+ append_seq_out_ptr(desc, dst, data_sz, out_options);
+
+ /* Load IV, if there is one */
+ if (ivsize)
+ append_seq_load(desc, ivsize, LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB);
+
+ append_operation(desc, options);
+
+ skcipher_append_src_dst(desc);
+
+ /* Store IV */
+ if (ivsize)
+ append_seq_store(desc, ivsize, LDST_SRCDST_BYTE_CONTEXT |
+ LDST_CLASS_1_CCB);
+
+ print_hex_dump_debug("skcipher_enc_dec job desc@" __stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc, desc_bytes(desc),
+ 1);
+}
+EXPORT_SYMBOL(cnstr_desc_skcipher_enc_dec);
+
+void cnstr_desc_protected_blob_decap(u32 * const desc, struct alginfo *cdata,
+ dma_addr_t next_desc_addr)
+{
+ u32 protected_store;
+
+ init_job_desc(desc, 0);
+
+ /* Load key modifier */
+ append_load_as_imm(desc, KEYMOD, sizeof(KEYMOD) - 1,
+ LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY);
+
+ append_seq_in_ptr_intlen(desc, cdata->key_dma,
+ cdata->plain_keylen + CAAM_BLOB_OVERHEAD, 0);
+
+ append_seq_out_ptr_intlen(desc, cdata->protected_key_dma,
+ cdata->plain_keylen, 0);
+
+ protected_store = OP_PCLID_BLOB | OP_PCL_BLOB_BLACK;
+ if ((cdata->key_cmd_opt >> KEY_EKT_OFFSET) & 1)
+ protected_store |= OP_PCL_BLOB_EKT;
+
+ append_operation(desc, OP_TYPE_DECAP_PROTOCOL | protected_store);
+
+ if (next_desc_addr) {
+ append_jump(desc, JUMP_TYPE_NONLOCAL | JUMP_TEST_ALL);
+ append_ptr(desc, next_desc_addr);
+ }
+
+ print_hex_dump_debug("protected blob decap job desc@" __stringify(__LINE__) ":",
+ DUMP_PREFIX_ADDRESS, 16, 4, desc,
+ desc_bytes(desc), 1);
+}
+EXPORT_SYMBOL(cnstr_desc_protected_blob_decap);
+
/**
* cnstr_shdsc_skcipher_encap - skcipher encapsulation shared descriptor
* @desc: pointer to buffer used for descriptor construction
@@ -1391,7 +1470,8 @@ void cnstr_shdsc_skcipher_encap(u32 * const desc, struct alginfo *cdata,
/* Load class1 key only */
append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
- cdata->keylen, CLASS_1 | KEY_DEST_CLASS_REG);
+ cdata->plain_keylen, CLASS_1 | KEY_DEST_CLASS_REG
+ | cdata->key_cmd_opt);
/* Load nonce into CONTEXT1 reg */
if (is_rfc3686) {
@@ -1466,7 +1546,8 @@ void cnstr_shdsc_skcipher_decap(u32 * const desc, struct alginfo *cdata,
/* Load class1 key only */
append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
- cdata->keylen, CLASS_1 | KEY_DEST_CLASS_REG);
+ cdata->plain_keylen, CLASS_1 | KEY_DEST_CLASS_REG
+ | cdata->key_cmd_opt);
/* Load nonce into CONTEXT1 reg */
if (is_rfc3686) {
diff --git a/drivers/crypto/caam/caamalg_desc.h b/drivers/crypto/caam/caamalg_desc.h
index f2893393ba5e..323490a4a756 100644
--- a/drivers/crypto/caam/caamalg_desc.h
+++ b/drivers/crypto/caam/caamalg_desc.h
@@ -2,7 +2,7 @@
/*
* Shared descriptors for aead, skcipher algorithms
*
- * Copyright 2016 NXP
+ * Copyright 2016, 2025 NXP
*/
#ifndef _CAAMALG_DESC_H_
@@ -48,6 +48,9 @@
#define DESC_SKCIPHER_DEC_LEN (DESC_SKCIPHER_BASE + \
16 * CAAM_CMD_SZ)
+/* Key modifier for CAAM Protected blobs */
+#define KEYMOD "SECURE_KEY"
+
void cnstr_shdsc_aead_null_encap(u32 * const desc, struct alginfo *adata,
unsigned int icvsize, int era);
@@ -113,4 +116,12 @@ void cnstr_shdsc_xts_skcipher_encap(u32 * const desc, struct alginfo *cdata);
void cnstr_shdsc_xts_skcipher_decap(u32 * const desc, struct alginfo *cdata);
+void cnstr_desc_protected_blob_decap(u32 * const desc, struct alginfo *cdata,
+ dma_addr_t next_desc);
+
+void cnstr_desc_skcipher_enc_dec(u32 * const desc, struct alginfo *cdata,
+ dma_addr_t src, dma_addr_t dst, unsigned int data_sz,
+ unsigned int in_options, unsigned int out_options,
+ unsigned int ivsize, const bool encrypt);
+
#endif /* _CAAMALG_DESC_H_ */
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index b3d14a7f4dd1..0eb43c862516 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -181,7 +181,9 @@ static inline void test_len(struct hwrng *rng, size_t len, bool wait)
struct device *dev = ctx->ctrldev;
buf = kcalloc(CAAM_RNG_MAX_FIFO_STORE_SIZE, sizeof(u8), GFP_KERNEL);
-
+ if (!buf) {
+ return;
+ }
while (len > 0) {
read_len = rng->read(rng, buf, len, wait);
diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h
index e13470901586..c28e94fcb8c7 100644
--- a/drivers/crypto/caam/desc.h
+++ b/drivers/crypto/caam/desc.h
@@ -4,7 +4,7 @@
* Definitions to support CAAM descriptor instruction generation
*
* Copyright 2008-2011 Freescale Semiconductor, Inc.
- * Copyright 2018 NXP
+ * Copyright 2018, 2025 NXP
*/
#ifndef DESC_H
@@ -162,6 +162,7 @@
* Enhanced Encryption of Key
*/
#define KEY_EKT 0x00100000
+#define KEY_EKT_OFFSET 20
/*
* Encrypted with Trusted Key
@@ -403,6 +404,7 @@
#define FIFOST_TYPE_PKHA_N (0x08 << FIFOST_TYPE_SHIFT)
#define FIFOST_TYPE_PKHA_A (0x0c << FIFOST_TYPE_SHIFT)
#define FIFOST_TYPE_PKHA_B (0x0d << FIFOST_TYPE_SHIFT)
+#define FIFOST_TYPE_KEY_CCM_JKEK (0x14 << FIFOST_TYPE_SHIFT)
#define FIFOST_TYPE_AF_SBOX_JKEK (0x20 << FIFOST_TYPE_SHIFT)
#define FIFOST_TYPE_AF_SBOX_TKEK (0x21 << FIFOST_TYPE_SHIFT)
#define FIFOST_TYPE_PKHA_E_JKEK (0x22 << FIFOST_TYPE_SHIFT)
@@ -1001,6 +1003,11 @@
#define OP_PCL_TLS12_AES_256_CBC_SHA384 0xff63
#define OP_PCL_TLS12_AES_256_CBC_SHA512 0xff65
+/* Blob protocol protinfo bits */
+
+#define OP_PCL_BLOB_BLACK 0x0004
+#define OP_PCL_BLOB_EKT 0x0100
+
/* For DTLS - OP_PCLID_DTLS */
#define OP_PCL_DTLS_AES_128_CBC_SHA 0x002f
diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h
index 824c94d44f94..2a29dd2c9c8a 100644
--- a/drivers/crypto/caam/desc_constr.h
+++ b/drivers/crypto/caam/desc_constr.h
@@ -3,7 +3,7 @@
* caam descriptor construction helper functions
*
* Copyright 2008-2012 Freescale Semiconductor, Inc.
- * Copyright 2019 NXP
+ * Copyright 2019, 2025 NXP
*/
#ifndef DESC_CONSTR_H
@@ -498,17 +498,23 @@ do { \
* @keylen: length of the provided algorithm key, in bytes
* @keylen_pad: padded length of the provided algorithm key, in bytes
* @key_dma: dma (bus) address where algorithm key resides
+ * @protected_key_dma: dma (bus) address where protected key resides
* @key_virt: virtual address where algorithm key resides
* @key_inline: true - key can be inlined in the descriptor; false - key is
* referenced by the descriptor
+ * @plain_keylen: size of the key to be loaded by the CAAM
+ * @key_cmd_opt: optional parameters for KEY command
*/
struct alginfo {
u32 algtype;
unsigned int keylen;
unsigned int keylen_pad;
dma_addr_t key_dma;
+ dma_addr_t protected_key_dma;
const void *key_virt;
bool key_inline;
+ u32 plain_keylen;
+ u32 key_cmd_opt;
};
/**
diff --git a/drivers/crypto/cavium/nitrox/nitrox_mbx.c b/drivers/crypto/cavium/nitrox/nitrox_mbx.c
index d4e06999af9b..a6a76e50ba84 100644
--- a/drivers/crypto/cavium/nitrox/nitrox_mbx.c
+++ b/drivers/crypto/cavium/nitrox/nitrox_mbx.c
@@ -192,7 +192,7 @@ int nitrox_mbox_init(struct nitrox_device *ndev)
}
/* allocate pf2vf response workqueue */
- ndev->iov.pf2vf_wq = alloc_workqueue("nitrox_pf2vf", 0, 0);
+ ndev->iov.pf2vf_wq = alloc_workqueue("nitrox_pf2vf", WQ_PERCPU, 0);
if (!ndev->iov.pf2vf_wq) {
kfree(ndev->iov.vfdev);
ndev->iov.vfdev = NULL;
diff --git a/drivers/crypto/ccp/ccp-dev.c b/drivers/crypto/ccp/ccp-dev.c
index c531d13d971f..246801912e1a 100644
--- a/drivers/crypto/ccp/ccp-dev.c
+++ b/drivers/crypto/ccp/ccp-dev.c
@@ -507,7 +507,7 @@ int ccp_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
struct ccp_device *ccp = container_of(rng, struct ccp_device, hwrng);
u32 trng_value;
- int len = min_t(int, sizeof(trng_value), max);
+ int len = min(sizeof(trng_value), max);
/* Locking is provided by the caller so we can update device
* hwrng-related fields safely
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
index 0d13d47c164b..b28a6f50daaa 100644
--- a/drivers/crypto/ccp/sev-dev.c
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -259,27 +259,20 @@ static int sev_cmd_buffer_len(int cmd)
static struct file *open_file_as_root(const char *filename, int flags, umode_t mode)
{
- struct file *fp;
- struct path root;
- struct cred *cred;
- const struct cred *old_cred;
+ struct path root __free(path_put) = {};
task_lock(&init_task);
get_fs_root(init_task.fs, &root);
task_unlock(&init_task);
- cred = prepare_creds();
+ CLASS(prepare_creds, cred)();
if (!cred)
return ERR_PTR(-ENOMEM);
- cred->fsuid = GLOBAL_ROOT_UID;
- old_cred = override_creds(cred);
- fp = file_open_root(&root, filename, flags, mode);
- path_put(&root);
-
- put_cred(revert_creds(old_cred));
+ cred->fsuid = GLOBAL_ROOT_UID;
- return fp;
+ scoped_with_creds(cred)
+ return file_open_root(&root, filename, flags, mode);
}
static int sev_read_init_ex_file(void)
diff --git a/drivers/crypto/ccp/sp-dev.h b/drivers/crypto/ccp/sp-dev.h
index 6f9d7063257d..1335a83fe052 100644
--- a/drivers/crypto/ccp/sp-dev.h
+++ b/drivers/crypto/ccp/sp-dev.h
@@ -95,7 +95,7 @@ struct sp_device {
struct device *dev;
- struct sp_dev_vdata *dev_vdata;
+ const struct sp_dev_vdata *dev_vdata;
unsigned int ord;
char name[SP_MAX_NAME_LEN];
diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c
index e7bb803912a6..8891ceee1d7d 100644
--- a/drivers/crypto/ccp/sp-pci.c
+++ b/drivers/crypto/ccp/sp-pci.c
@@ -459,6 +459,17 @@ static const struct psp_vdata pspv6 = {
.intsts_reg = 0x10514, /* P2CMSG_INTSTS */
};
+static const struct psp_vdata pspv7 = {
+ .tee = &teev2,
+ .cmdresp_reg = 0x10944, /* C2PMSG_17 */
+ .cmdbuff_addr_lo_reg = 0x10948, /* C2PMSG_18 */
+ .cmdbuff_addr_hi_reg = 0x1094c, /* C2PMSG_19 */
+ .bootloader_info_reg = 0x109ec, /* C2PMSG_59 */
+ .feature_reg = 0x109fc, /* C2PMSG_63 */
+ .inten_reg = 0x10510, /* P2CMSG_INTEN */
+ .intsts_reg = 0x10514, /* P2CMSG_INTSTS */
+};
+
#endif
static const struct sp_dev_vdata dev_vdata[] = {
@@ -525,6 +536,13 @@ static const struct sp_dev_vdata dev_vdata[] = {
.psp_vdata = &pspv6,
#endif
},
+ { /* 9 */
+ .bar = 2,
+#ifdef CONFIG_CRYPTO_DEV_SP_PSP
+ .psp_vdata = &pspv7,
+#endif
+ },
+
};
static const struct pci_device_id sp_pci_table[] = {
{ PCI_VDEVICE(AMD, 0x1537), (kernel_ulong_t)&dev_vdata[0] },
@@ -539,6 +557,7 @@ static const struct pci_device_id sp_pci_table[] = {
{ PCI_VDEVICE(AMD, 0x17E0), (kernel_ulong_t)&dev_vdata[7] },
{ PCI_VDEVICE(AMD, 0x156E), (kernel_ulong_t)&dev_vdata[8] },
{ PCI_VDEVICE(AMD, 0x17D8), (kernel_ulong_t)&dev_vdata[8] },
+ { PCI_VDEVICE(AMD, 0x115A), (kernel_ulong_t)&dev_vdata[9] },
/* Last entry must be zero */
{ 0, }
};
diff --git a/drivers/crypto/ccp/sp-platform.c b/drivers/crypto/ccp/sp-platform.c
index 3933cac1694d..3f9843fa7782 100644
--- a/drivers/crypto/ccp/sp-platform.c
+++ b/drivers/crypto/ccp/sp-platform.c
@@ -52,24 +52,13 @@ static const struct of_device_id sp_of_match[] = {
};
MODULE_DEVICE_TABLE(of, sp_of_match);
-static struct sp_dev_vdata *sp_get_of_version(struct platform_device *pdev)
-{
- const struct of_device_id *match;
-
- match = of_match_node(sp_of_match, pdev->dev.of_node);
- if (match && match->data)
- return (struct sp_dev_vdata *)match->data;
-
- return NULL;
-}
-
-static struct sp_dev_vdata *sp_get_acpi_version(struct platform_device *pdev)
+static const struct sp_dev_vdata *sp_get_acpi_version(struct platform_device *pdev)
{
const struct acpi_device_id *match;
match = acpi_match_device(sp_acpi_match, &pdev->dev);
if (match && match->driver_data)
- return (struct sp_dev_vdata *)match->driver_data;
+ return (const struct sp_dev_vdata *)match->driver_data;
return NULL;
}
@@ -123,7 +112,7 @@ static int sp_platform_probe(struct platform_device *pdev)
goto e_err;
sp->dev_specific = sp_platform;
- sp->dev_vdata = pdev->dev.of_node ? sp_get_of_version(pdev)
+ sp->dev_vdata = pdev->dev.of_node ? of_device_get_match_data(&pdev->dev)
: sp_get_acpi_version(pdev);
if (!sp->dev_vdata) {
ret = -ENODEV;
diff --git a/drivers/crypto/ccree/cc_buffer_mgr.c b/drivers/crypto/ccree/cc_buffer_mgr.c
index 3963bb91321f..dc7e0cd51c25 100644
--- a/drivers/crypto/ccree/cc_buffer_mgr.c
+++ b/drivers/crypto/ccree/cc_buffer_mgr.c
@@ -1235,6 +1235,7 @@ int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx,
int rc = 0;
u32 dummy = 0;
u32 mapped_nents = 0;
+ int sg_nents;
dev_dbg(dev, " update params : curr_buff=%p curr_buff_cnt=0x%X nbytes=0x%X src=%p curr_index=%u\n",
curr_buff, *curr_buff_cnt, nbytes, src, areq_ctx->buff_index);
@@ -1248,7 +1249,10 @@ int cc_map_hash_request_update(struct cc_drvdata *drvdata, void *ctx,
if (total_in_len < block_size) {
dev_dbg(dev, " less than one block: curr_buff=%p *curr_buff_cnt=0x%X copy_to=%p\n",
curr_buff, *curr_buff_cnt, &curr_buff[*curr_buff_cnt]);
- areq_ctx->in_nents = sg_nents_for_len(src, nbytes);
+ sg_nents = sg_nents_for_len(src, nbytes);
+ if (sg_nents < 0)
+ return sg_nents;
+ areq_ctx->in_nents = sg_nents;
sg_copy_to_buffer(src, areq_ctx->in_nents,
&curr_buff[*curr_buff_cnt], nbytes);
*curr_buff_cnt += nbytes;
diff --git a/drivers/crypto/hifn_795x.c b/drivers/crypto/hifn_795x.c
index 925991526745..edf36f6add52 100644
--- a/drivers/crypto/hifn_795x.c
+++ b/drivers/crypto/hifn_795x.c
@@ -913,11 +913,10 @@ static void hifn_init_pll(struct hifn_device *dev)
else
pllcfg |= HIFN_PLL_REF_CLK_HBI;
- if (hifn_pll_ref[3] != '\0')
- freq = simple_strtoul(hifn_pll_ref + 3, NULL, 10);
- else {
+ if (hifn_pll_ref[3] == '\0' ||
+ kstrtouint(hifn_pll_ref + 3, 10, &freq)) {
freq = 66;
- dev_info(&dev->pdev->dev, "assuming %uMHz clock speed, override with hifn_pll_ref=%.3s<frequency>\n",
+ dev_info(&dev->pdev->dev, "assuming %u MHz clock speed, override with hifn_pll_ref=%.3s<frequency>\n",
freq, hifn_pll_ref);
}
diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c
index a5b96adf2d1e..f8bfff5dd0bd 100644
--- a/drivers/crypto/hisilicon/qm.c
+++ b/drivers/crypto/hisilicon/qm.c
@@ -64,10 +64,10 @@
#define QM_EQE_AEQE_SIZE (2UL << 12)
#define QM_EQC_PHASE_SHIFT 16
-#define QM_EQE_PHASE(eqe) ((le32_to_cpu((eqe)->dw0) >> 16) & 0x1)
+#define QM_EQE_PHASE(dw0) (((dw0) >> 16) & 0x1)
#define QM_EQE_CQN_MASK GENMASK(15, 0)
-#define QM_AEQE_PHASE(aeqe) ((le32_to_cpu((aeqe)->dw0) >> 16) & 0x1)
+#define QM_AEQE_PHASE(dw0) (((dw0) >> 16) & 0x1)
#define QM_AEQE_TYPE_SHIFT 17
#define QM_AEQE_TYPE_MASK 0xf
#define QM_AEQE_CQN_MASK GENMASK(15, 0)
@@ -976,23 +976,23 @@ static void qm_get_complete_eqe_num(struct hisi_qm *qm)
{
struct qm_eqe *eqe = qm->eqe + qm->status.eq_head;
struct hisi_qm_poll_data *poll_data = NULL;
+ u32 dw0 = le32_to_cpu(eqe->dw0);
u16 eq_depth = qm->eq_depth;
u16 cqn, eqe_num = 0;
- if (QM_EQE_PHASE(eqe) != qm->status.eqc_phase) {
+ if (QM_EQE_PHASE(dw0) != qm->status.eqc_phase) {
atomic64_inc(&qm->debug.dfx.err_irq_cnt);
qm_db(qm, 0, QM_DOORBELL_CMD_EQ, qm->status.eq_head, 0);
return;
}
- cqn = le32_to_cpu(eqe->dw0) & QM_EQE_CQN_MASK;
+ cqn = dw0 & QM_EQE_CQN_MASK;
if (unlikely(cqn >= qm->qp_num))
return;
poll_data = &qm->poll_data[cqn];
- while (QM_EQE_PHASE(eqe) == qm->status.eqc_phase) {
- cqn = le32_to_cpu(eqe->dw0) & QM_EQE_CQN_MASK;
- poll_data->qp_finish_id[eqe_num] = cqn;
+ while (QM_EQE_PHASE(dw0) != qm->status.eqc_phase) {
+ poll_data->qp_finish_id[eqe_num] = dw0 & QM_EQE_CQN_MASK;
eqe_num++;
if (qm->status.eq_head == eq_depth - 1) {
@@ -1006,6 +1006,8 @@ static void qm_get_complete_eqe_num(struct hisi_qm *qm)
if (eqe_num == (eq_depth >> 1) - 1)
break;
+
+ dw0 = le32_to_cpu(eqe->dw0);
}
poll_data->eqe_num = eqe_num;
@@ -1098,15 +1100,15 @@ static irqreturn_t qm_aeq_thread(int irq, void *data)
{
struct hisi_qm *qm = data;
struct qm_aeqe *aeqe = qm->aeqe + qm->status.aeq_head;
+ u32 dw0 = le32_to_cpu(aeqe->dw0);
u16 aeq_depth = qm->aeq_depth;
u32 type, qp_id;
atomic64_inc(&qm->debug.dfx.aeq_irq_cnt);
- while (QM_AEQE_PHASE(aeqe) == qm->status.aeqc_phase) {
- type = (le32_to_cpu(aeqe->dw0) >> QM_AEQE_TYPE_SHIFT) &
- QM_AEQE_TYPE_MASK;
- qp_id = le32_to_cpu(aeqe->dw0) & QM_AEQE_CQN_MASK;
+ while (QM_AEQE_PHASE(dw0) == qm->status.aeqc_phase) {
+ type = (dw0 >> QM_AEQE_TYPE_SHIFT) & QM_AEQE_TYPE_MASK;
+ qp_id = dw0 & QM_AEQE_CQN_MASK;
switch (type) {
case QM_EQ_OVERFLOW:
@@ -1134,6 +1136,7 @@ static irqreturn_t qm_aeq_thread(int irq, void *data)
aeqe++;
qm->status.aeq_head++;
}
+ dw0 = le32_to_cpu(aeqe->dw0);
}
qm_db(qm, 0, QM_DOORBELL_CMD_AEQ, qm->status.aeq_head, 0);
@@ -1283,6 +1286,13 @@ static void qm_vft_data_cfg(struct hisi_qm *qm, enum vft_type type, u32 base,
(factor->cbs_s << QM_SHAPER_FACTOR_CBS_S_SHIFT);
}
break;
+ /*
+ * Note: The current logic only needs to handle the above three types
+ * If new types are added, they need to be supplemented here,
+ * otherwise undefined behavior may occur.
+ */
+ default:
+ break;
}
}
@@ -2652,10 +2662,10 @@ static int qm_hw_err_isolate(struct hisi_qm *qm)
}
}
list_add(&hw_err->list, &isolate->qm_hw_errs);
- mutex_unlock(&isolate->isolate_lock);
if (count >= isolate->err_threshold)
isolate->is_isolate = true;
+ mutex_unlock(&isolate->isolate_lock);
return 0;
}
@@ -2664,12 +2674,10 @@ static void qm_hw_err_destroy(struct hisi_qm *qm)
{
struct qm_hw_err *err, *tmp;
- mutex_lock(&qm->isolate_data.isolate_lock);
list_for_each_entry_safe(err, tmp, &qm->isolate_data.qm_hw_errs, list) {
list_del(&err->list);
kfree(err);
}
- mutex_unlock(&qm->isolate_data.isolate_lock);
}
static enum uacce_dev_state hisi_qm_get_isolate_state(struct uacce_device *uacce)
@@ -2697,10 +2705,12 @@ static int hisi_qm_isolate_threshold_write(struct uacce_device *uacce, u32 num)
if (qm->isolate_data.is_isolate)
return -EPERM;
+ mutex_lock(&qm->isolate_data.isolate_lock);
qm->isolate_data.err_threshold = num;
/* After the policy is updated, need to reset the hardware err list */
qm_hw_err_destroy(qm);
+ mutex_unlock(&qm->isolate_data.isolate_lock);
return 0;
}
@@ -2737,7 +2747,10 @@ static void qm_remove_uacce(struct hisi_qm *qm)
struct uacce_device *uacce = qm->uacce;
if (qm->use_sva) {
+ mutex_lock(&qm->isolate_data.isolate_lock);
qm_hw_err_destroy(qm);
+ mutex_unlock(&qm->isolate_data.isolate_lock);
+
uacce_remove(uacce);
qm->uacce = NULL;
}
@@ -3019,11 +3032,36 @@ static void qm_put_pci_res(struct hisi_qm *qm)
pci_release_mem_regions(pdev);
}
+static void hisi_mig_region_clear(struct hisi_qm *qm)
+{
+ u32 val;
+
+ /* Clear migration region set of PF */
+ if (qm->fun_type == QM_HW_PF && qm->ver > QM_HW_V3) {
+ val = readl(qm->io_base + QM_MIG_REGION_SEL);
+ val &= ~QM_MIG_REGION_EN;
+ writel(val, qm->io_base + QM_MIG_REGION_SEL);
+ }
+}
+
+static void hisi_mig_region_enable(struct hisi_qm *qm)
+{
+ u32 val;
+
+ /* Select migration region of PF */
+ if (qm->fun_type == QM_HW_PF && qm->ver > QM_HW_V3) {
+ val = readl(qm->io_base + QM_MIG_REGION_SEL);
+ val |= QM_MIG_REGION_EN;
+ writel(val, qm->io_base + QM_MIG_REGION_SEL);
+ }
+}
+
static void hisi_qm_pci_uninit(struct hisi_qm *qm)
{
struct pci_dev *pdev = qm->pdev;
pci_free_irq_vectors(pdev);
+ hisi_mig_region_clear(qm);
qm_put_pci_res(qm);
pci_disable_device(pdev);
}
@@ -3678,6 +3716,7 @@ static void qm_clear_vft_config(struct hisi_qm *qm)
static int qm_func_shaper_enable(struct hisi_qm *qm, u32 fun_index, u32 qos)
{
struct device *dev = &qm->pdev->dev;
+ struct qm_shaper_factor t_factor;
u32 ir = qos * QM_QOS_RATE;
int ret, total_vfs, i;
@@ -3685,6 +3724,7 @@ static int qm_func_shaper_enable(struct hisi_qm *qm, u32 fun_index, u32 qos)
if (fun_index > total_vfs)
return -EINVAL;
+ memcpy(&t_factor, &qm->factor[fun_index], sizeof(t_factor));
qm->factor[fun_index].func_qos = qos;
ret = qm_get_shaper_para(ir, &qm->factor[fun_index]);
@@ -3698,11 +3738,21 @@ static int qm_func_shaper_enable(struct hisi_qm *qm, u32 fun_index, u32 qos)
ret = qm_set_vft_common(qm, SHAPER_VFT, fun_index, i, 1);
if (ret) {
dev_err(dev, "type: %d, failed to set shaper vft!\n", i);
- return -EINVAL;
+ goto back_func_qos;
}
}
return 0;
+
+back_func_qos:
+ memcpy(&qm->factor[fun_index], &t_factor, sizeof(t_factor));
+ for (i--; i >= ALG_TYPE_0; i--) {
+ ret = qm_set_vft_common(qm, SHAPER_VFT, fun_index, i, 1);
+ if (ret)
+ dev_err(dev, "failed to restore shaper vft during rollback!\n");
+ }
+
+ return -EINVAL;
}
static u32 qm_get_shaper_vft_qos(struct hisi_qm *qm, u32 fun_index)
@@ -3871,10 +3921,12 @@ static ssize_t qm_get_qos_value(struct hisi_qm *qm, const char *buf,
pdev = container_of(dev, struct pci_dev, dev);
if (pci_physfn(pdev) != qm->pdev) {
pci_err(qm->pdev, "the pdev input does not match the pf!\n");
+ put_device(dev);
return -EINVAL;
}
*fun_index = pdev->devfn;
+ put_device(dev);
return 0;
}
@@ -5725,6 +5777,7 @@ int hisi_qm_init(struct hisi_qm *qm)
goto err_free_qm_memory;
qm_cmd_init(qm);
+ hisi_mig_region_enable(qm);
return 0;
@@ -5863,6 +5916,7 @@ static int qm_rebuild_for_resume(struct hisi_qm *qm)
}
qm_cmd_init(qm);
+ hisi_mig_region_enable(qm);
hisi_qm_dev_err_init(qm);
/* Set the doorbell timeout to QM_DB_TIMEOUT_CFG ns. */
writel(QM_DB_TIMEOUT_SET, qm->io_base + QM_DB_TIMEOUT_CFG);
diff --git a/drivers/crypto/hisilicon/sgl.c b/drivers/crypto/hisilicon/sgl.c
index 7a9ef2a9972a..24c7b6ab285b 100644
--- a/drivers/crypto/hisilicon/sgl.c
+++ b/drivers/crypto/hisilicon/sgl.c
@@ -245,11 +245,6 @@ hisi_acc_sg_buf_map_to_hw_sgl(struct device *dev, struct scatterlist *sgl,
}
curr_hw_sgl = acc_get_sgl(pool, index, &curr_sgl_dma);
- if (IS_ERR(curr_hw_sgl)) {
- dev_err(dev, "Get SGL error!\n");
- ret = -ENOMEM;
- goto err_unmap;
- }
curr_hw_sgl->entry_length_in_sgl = cpu_to_le16(pool->sge_nr);
curr_hw_sge = curr_hw_sgl->sge_entries;
diff --git a/drivers/crypto/intel/iaa/iaa_crypto_main.c b/drivers/crypto/intel/iaa/iaa_crypto_main.c
index 23f585219fb4..d0058757b000 100644
--- a/drivers/crypto/intel/iaa/iaa_crypto_main.c
+++ b/drivers/crypto/intel/iaa/iaa_crypto_main.c
@@ -805,7 +805,7 @@ static int save_iaa_wq(struct idxd_wq *wq)
if (!cpus_per_iaa)
cpus_per_iaa = 1;
out:
- return 0;
+ return ret;
}
static void remove_iaa_wq(struct idxd_wq *wq)
diff --git a/drivers/crypto/intel/qat/qat_common/adf_aer.c b/drivers/crypto/intel/qat/qat_common/adf_aer.c
index 35679b21ff63..11728cf32653 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_aer.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_aer.c
@@ -105,7 +105,6 @@ void adf_dev_restore(struct adf_accel_dev *accel_dev)
accel_dev->accel_id);
hw_device->reset_device(accel_dev);
pci_restore_state(pdev);
- pci_save_state(pdev);
}
}
@@ -204,7 +203,6 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev)
if (!pdev->is_busmaster)
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
res = adf_dev_up(accel_dev, false);
if (res && res != -EALREADY)
return PCI_ERS_RESULT_DISCONNECT;
@@ -276,11 +274,11 @@ int adf_notify_fatal_error(struct adf_accel_dev *accel_dev)
int adf_init_aer(void)
{
device_reset_wq = alloc_workqueue("qat_device_reset_wq",
- WQ_MEM_RECLAIM, 0);
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!device_reset_wq)
return -EFAULT;
- device_sriov_wq = alloc_workqueue("qat_device_sriov_wq", 0, 0);
+ device_sriov_wq = alloc_workqueue("qat_device_sriov_wq", WQ_PERCPU, 0);
if (!device_sriov_wq) {
destroy_workqueue(device_reset_wq);
device_reset_wq = NULL;
diff --git a/drivers/crypto/intel/qat/qat_common/adf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_isr.c
index 12e565613661..4639d7fd93e6 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_isr.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_isr.c
@@ -384,7 +384,8 @@ EXPORT_SYMBOL_GPL(adf_isr_resource_alloc);
*/
int __init adf_init_misc_wq(void)
{
- adf_misc_wq = alloc_workqueue("qat_misc_wq", WQ_MEM_RECLAIM, 0);
+ adf_misc_wq = alloc_workqueue("qat_misc_wq",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
return !adf_misc_wq ? -ENOMEM : 0;
}
diff --git a/drivers/crypto/intel/qat/qat_common/adf_sriov.c b/drivers/crypto/intel/qat/qat_common/adf_sriov.c
index 31d1ef0cb1f5..bb904ba4bf84 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_sriov.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_sriov.c
@@ -299,7 +299,8 @@ EXPORT_SYMBOL_GPL(adf_sriov_configure);
int __init adf_init_pf_wq(void)
{
/* Workqueue for PF2VF responses */
- pf2vf_resp_wq = alloc_workqueue("qat_pf2vf_resp_wq", WQ_MEM_RECLAIM, 0);
+ pf2vf_resp_wq = alloc_workqueue("qat_pf2vf_resp_wq",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
return !pf2vf_resp_wq ? -ENOMEM : 0;
}
diff --git a/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c b/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c
index a4636ec9f9ca..d0fef20a3df4 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_vf_isr.c
@@ -299,7 +299,8 @@ EXPORT_SYMBOL_GPL(adf_flush_vf_wq);
*/
int __init adf_init_vf_wq(void)
{
- adf_vf_stop_wq = alloc_workqueue("adf_vf_stop_wq", WQ_MEM_RECLAIM, 0);
+ adf_vf_stop_wq = alloc_workqueue("adf_vf_stop_wq",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
return !adf_vf_stop_wq ? -EFAULT : 0;
}
diff --git a/drivers/crypto/intel/qat/qat_common/qat_uclo.c b/drivers/crypto/intel/qat/qat_common/qat_uclo.c
index 18c3e4416dc5..06d49cb781ae 100644
--- a/drivers/crypto/intel/qat/qat_common/qat_uclo.c
+++ b/drivers/crypto/intel/qat/qat_common/qat_uclo.c
@@ -200,20 +200,12 @@ qat_uclo_cleanup_batch_init_list(struct icp_qat_fw_loader_handle *handle,
static int qat_uclo_parse_num(char *str, unsigned int *num)
{
- char buf[16] = {0};
- unsigned long ae = 0;
- int i;
-
- strscpy(buf, str, sizeof(buf));
- for (i = 0; i < 16; i++) {
- if (!isdigit(buf[i])) {
- buf[i] = '\0';
- break;
- }
- }
- if ((kstrtoul(buf, 10, &ae)))
- return -EFAULT;
+ unsigned long long ae;
+ char *end;
+ ae = simple_strtoull(str, &end, 10);
+ if (ae > UINT_MAX || str == end || (end - str) > 19)
+ return -EINVAL;
*num = (unsigned int)ae;
return 0;
}
diff --git a/drivers/crypto/marvell/cesa/cesa.c b/drivers/crypto/marvell/cesa/cesa.c
index 9c21f5d835d2..301bdf239e7d 100644
--- a/drivers/crypto/marvell/cesa/cesa.c
+++ b/drivers/crypto/marvell/cesa/cesa.c
@@ -420,7 +420,6 @@ static int mv_cesa_probe(struct platform_device *pdev)
{
const struct mv_cesa_caps *caps = &orion_caps;
const struct mbus_dram_target_info *dram;
- const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct mv_cesa_dev *cesa;
struct mv_cesa_engine *engines;
@@ -433,11 +432,9 @@ static int mv_cesa_probe(struct platform_device *pdev)
}
if (dev->of_node) {
- match = of_match_node(mv_cesa_of_match_table, dev->of_node);
- if (!match || !match->data)
+ caps = of_device_get_match_data(dev);
+ if (!caps)
return -ENOTSUPP;
-
- caps = match->data;
}
cesa = devm_kzalloc(dev, sizeof(*cesa), GFP_KERNEL);
diff --git a/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c b/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c
index 215a1a8ba7e9..07a74f702c3a 100644
--- a/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c
+++ b/drivers/crypto/marvell/octeontx2/otx2_cpt_devlink.c
@@ -24,7 +24,8 @@ static int otx2_cpt_dl_egrp_delete(struct devlink *dl, u32 id,
}
static int otx2_cpt_dl_uc_info(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
ctx->val.vstr[0] = '\0';
@@ -32,7 +33,8 @@ static int otx2_cpt_dl_uc_info(struct devlink *dl, u32 id,
}
static int otx2_cpt_dl_t106_mode_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct otx2_cpt_devlink *cpt_dl = devlink_priv(dl);
struct otx2_cptpf_dev *cptpf = cpt_dl->cptpf;
diff --git a/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c b/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c
index ebdf4efa09d4..b5cc5401f704 100644
--- a/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c
+++ b/drivers/crypto/marvell/octeontx2/otx2_cptpf_ucode.c
@@ -3,6 +3,7 @@
#include <linux/ctype.h>
#include <linux/firmware.h>
+#include <linux/string.h>
#include <linux/string_choices.h>
#include "otx2_cptpf_ucode.h"
#include "otx2_cpt_common.h"
@@ -458,13 +459,13 @@ static int cpt_ucode_load_fw(struct pci_dev *pdev, struct fw_info_t *fw_info,
u16 rid)
{
char filename[OTX2_CPT_NAME_LENGTH];
- char eng_type[8] = {0};
+ char eng_type[8];
int ret, e, i;
INIT_LIST_HEAD(&fw_info->ucodes);
for (e = 1; e < OTX2_CPT_MAX_ENG_TYPES; e++) {
- strcpy(eng_type, get_eng_type_str(e));
+ strscpy(eng_type, get_eng_type_str(e));
for (i = 0; i < strlen(eng_type); i++)
eng_type[i] = tolower(eng_type[i]);
diff --git a/drivers/crypto/qce/core.c b/drivers/crypto/qce/core.c
index e95e84486d9a..b966f3365b7d 100644
--- a/drivers/crypto/qce/core.c
+++ b/drivers/crypto/qce/core.c
@@ -21,7 +21,6 @@
#include "sha.h"
#include "aead.h"
-#define QCE_MAJOR_VERSION5 0x05
#define QCE_QUEUE_LENGTH 1
#define QCE_DEFAULT_MEM_BANDWIDTH 393600
@@ -161,7 +160,7 @@ static int qce_check_version(struct qce_device *qce)
* the driver does not support v5 with minor 0 because it has special
* alignment requirements.
*/
- if (major != QCE_MAJOR_VERSION5 || minor == 0)
+ if (major == 5 && minor == 0)
return -ENODEV;
qce->burst_size = QCE_BAM_BURST_SIZE;
diff --git a/drivers/crypto/qce/dma.c b/drivers/crypto/qce/dma.c
index 1dec7aea852d..68cafd4741ad 100644
--- a/drivers/crypto/qce/dma.c
+++ b/drivers/crypto/qce/dma.c
@@ -24,11 +24,13 @@ int devm_qce_dma_request(struct device *dev, struct qce_dma_data *dma)
dma->txchan = dma_request_chan(dev, "tx");
if (IS_ERR(dma->txchan))
- return PTR_ERR(dma->txchan);
+ return dev_err_probe(dev, PTR_ERR(dma->txchan),
+ "Failed to get TX DMA channel\n");
dma->rxchan = dma_request_chan(dev, "rx");
if (IS_ERR(dma->rxchan)) {
- ret = PTR_ERR(dma->rxchan);
+ ret = dev_err_probe(dev, PTR_ERR(dma->rxchan),
+ "Failed to get RX DMA channel\n");
goto error_rx;
}
diff --git a/drivers/crypto/rockchip/rk3288_crypto_skcipher.c b/drivers/crypto/rockchip/rk3288_crypto_skcipher.c
index 9393e10671c2..e80f9148c012 100644
--- a/drivers/crypto/rockchip/rk3288_crypto_skcipher.c
+++ b/drivers/crypto/rockchip/rk3288_crypto_skcipher.c
@@ -321,8 +321,7 @@ static int rk_cipher_run(struct crypto_engine *engine, void *async_req)
algt->stat_req++;
rkc->nreq++;
- ivsize = crypto_skcipher_ivsize(tfm);
- if (areq->iv && crypto_skcipher_ivsize(tfm) > 0) {
+ if (areq->iv && ivsize > 0) {
if (rctx->mode & RK_CRYPTO_DEC) {
offset = areq->cryptlen - ivsize;
scatterwalk_map_and_copy(rctx->backup_iv, areq->src,
diff --git a/drivers/crypto/starfive/jh7110-hash.c b/drivers/crypto/starfive/jh7110-hash.c
index e6839c7bfb73..54b7af4a7aee 100644
--- a/drivers/crypto/starfive/jh7110-hash.c
+++ b/drivers/crypto/starfive/jh7110-hash.c
@@ -325,6 +325,7 @@ static int starfive_hash_digest(struct ahash_request *req)
struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm);
struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req);
struct starfive_cryp_dev *cryp = ctx->cryp;
+ int sg_len;
memset(rctx, 0, sizeof(struct starfive_cryp_request_ctx));
@@ -333,7 +334,10 @@ static int starfive_hash_digest(struct ahash_request *req)
rctx->in_sg = req->src;
rctx->blksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
rctx->digsize = crypto_ahash_digestsize(tfm);
- rctx->in_sg_len = sg_nents_for_len(rctx->in_sg, rctx->total);
+ sg_len = sg_nents_for_len(rctx->in_sg, rctx->total);
+ if (sg_len < 0)
+ return sg_len;
+ rctx->in_sg_len = sg_len;
ctx->rctx = rctx;
return crypto_transfer_hash_request_to_engine(cryp->engine, req);
diff --git a/drivers/crypto/ti/Kconfig b/drivers/crypto/ti/Kconfig
index d4f91c1e0cb5..a3692ceec49b 100644
--- a/drivers/crypto/ti/Kconfig
+++ b/drivers/crypto/ti/Kconfig
@@ -6,6 +6,7 @@ config CRYPTO_DEV_TI_DTHEV2
select CRYPTO_SKCIPHER
select CRYPTO_ECB
select CRYPTO_CBC
+ select CRYPTO_XTS
help
This enables support for the TI DTHE V2 hw cryptography engine
which can be found on TI K3 SOCs. Selecting this enables use
diff --git a/drivers/crypto/ti/dthev2-aes.c b/drivers/crypto/ti/dthev2-aes.c
index 3547c41fa4ed..156729ccc50e 100644
--- a/drivers/crypto/ti/dthev2-aes.c
+++ b/drivers/crypto/ti/dthev2-aes.c
@@ -25,6 +25,7 @@
// AES Engine
#define DTHE_P_AES_BASE 0x7000
+
#define DTHE_P_AES_KEY1_0 0x0038
#define DTHE_P_AES_KEY1_1 0x003C
#define DTHE_P_AES_KEY1_2 0x0030
@@ -33,6 +34,16 @@
#define DTHE_P_AES_KEY1_5 0x002C
#define DTHE_P_AES_KEY1_6 0x0020
#define DTHE_P_AES_KEY1_7 0x0024
+
+#define DTHE_P_AES_KEY2_0 0x0018
+#define DTHE_P_AES_KEY2_1 0x001C
+#define DTHE_P_AES_KEY2_2 0x0010
+#define DTHE_P_AES_KEY2_3 0x0014
+#define DTHE_P_AES_KEY2_4 0x0008
+#define DTHE_P_AES_KEY2_5 0x000C
+#define DTHE_P_AES_KEY2_6 0x0000
+#define DTHE_P_AES_KEY2_7 0x0004
+
#define DTHE_P_AES_IV_IN_0 0x0040
#define DTHE_P_AES_IV_IN_1 0x0044
#define DTHE_P_AES_IV_IN_2 0x0048
@@ -52,6 +63,7 @@
enum aes_ctrl_mode_masks {
AES_CTRL_ECB_MASK = 0x00,
AES_CTRL_CBC_MASK = BIT(5),
+ AES_CTRL_XTS_MASK = BIT(12) | BIT(11),
};
#define DTHE_AES_CTRL_MODE_CLEAR_MASK ~GENMASK(28, 5)
@@ -88,6 +100,31 @@ static int dthe_cipher_init_tfm(struct crypto_skcipher *tfm)
return 0;
}
+static int dthe_cipher_xts_init_tfm(struct crypto_skcipher *tfm)
+{
+ struct dthe_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
+ struct dthe_data *dev_data = dthe_get_dev(ctx);
+
+ ctx->dev_data = dev_data;
+ ctx->keylen = 0;
+
+ ctx->skcipher_fb = crypto_alloc_sync_skcipher("xts(aes)", 0,
+ CRYPTO_ALG_NEED_FALLBACK);
+ if (IS_ERR(ctx->skcipher_fb)) {
+ dev_err(dev_data->dev, "fallback driver xts(aes) couldn't be loaded\n");
+ return PTR_ERR(ctx->skcipher_fb);
+ }
+
+ return 0;
+}
+
+static void dthe_cipher_xts_exit_tfm(struct crypto_skcipher *tfm)
+{
+ struct dthe_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
+
+ crypto_free_sync_skcipher(ctx->skcipher_fb);
+}
+
static int dthe_aes_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen)
{
struct dthe_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
@@ -119,6 +156,27 @@ static int dthe_aes_cbc_setkey(struct crypto_skcipher *tfm, const u8 *key, unsig
return dthe_aes_setkey(tfm, key, keylen);
}
+static int dthe_aes_xts_setkey(struct crypto_skcipher *tfm, const u8 *key, unsigned int keylen)
+{
+ struct dthe_tfm_ctx *ctx = crypto_skcipher_ctx(tfm);
+
+ if (keylen != 2 * AES_KEYSIZE_128 &&
+ keylen != 2 * AES_KEYSIZE_192 &&
+ keylen != 2 * AES_KEYSIZE_256)
+ return -EINVAL;
+
+ ctx->aes_mode = DTHE_AES_XTS;
+ ctx->keylen = keylen / 2;
+ memcpy(ctx->key, key, keylen);
+
+ crypto_sync_skcipher_clear_flags(ctx->skcipher_fb, CRYPTO_TFM_REQ_MASK);
+ crypto_sync_skcipher_set_flags(ctx->skcipher_fb,
+ crypto_skcipher_get_flags(tfm) &
+ CRYPTO_TFM_REQ_MASK);
+
+ return crypto_sync_skcipher_setkey(ctx->skcipher_fb, key, keylen);
+}
+
static void dthe_aes_set_ctrl_key(struct dthe_tfm_ctx *ctx,
struct dthe_aes_req_ctx *rctx,
u32 *iv_in)
@@ -141,6 +199,24 @@ static void dthe_aes_set_ctrl_key(struct dthe_tfm_ctx *ctx,
writel_relaxed(ctx->key[7], aes_base_reg + DTHE_P_AES_KEY1_7);
}
+ if (ctx->aes_mode == DTHE_AES_XTS) {
+ size_t key2_offset = ctx->keylen / sizeof(u32);
+
+ writel_relaxed(ctx->key[key2_offset + 0], aes_base_reg + DTHE_P_AES_KEY2_0);
+ writel_relaxed(ctx->key[key2_offset + 1], aes_base_reg + DTHE_P_AES_KEY2_1);
+ writel_relaxed(ctx->key[key2_offset + 2], aes_base_reg + DTHE_P_AES_KEY2_2);
+ writel_relaxed(ctx->key[key2_offset + 3], aes_base_reg + DTHE_P_AES_KEY2_3);
+
+ if (ctx->keylen > AES_KEYSIZE_128) {
+ writel_relaxed(ctx->key[key2_offset + 4], aes_base_reg + DTHE_P_AES_KEY2_4);
+ writel_relaxed(ctx->key[key2_offset + 5], aes_base_reg + DTHE_P_AES_KEY2_5);
+ }
+ if (ctx->keylen == AES_KEYSIZE_256) {
+ writel_relaxed(ctx->key[key2_offset + 6], aes_base_reg + DTHE_P_AES_KEY2_6);
+ writel_relaxed(ctx->key[key2_offset + 7], aes_base_reg + DTHE_P_AES_KEY2_7);
+ }
+ }
+
if (rctx->enc)
ctrl_val |= DTHE_AES_CTRL_DIR_ENC;
@@ -160,6 +236,9 @@ static void dthe_aes_set_ctrl_key(struct dthe_tfm_ctx *ctx,
case DTHE_AES_CBC:
ctrl_val |= AES_CTRL_CBC_MASK;
break;
+ case DTHE_AES_XTS:
+ ctrl_val |= AES_CTRL_XTS_MASK;
+ break;
}
if (iv_in) {
@@ -315,24 +394,45 @@ aes_err:
local_bh_disable();
crypto_finalize_skcipher_request(dev_data->engine, req, ret);
local_bh_enable();
- return ret;
+ return 0;
}
static int dthe_aes_crypt(struct skcipher_request *req)
{
struct dthe_tfm_ctx *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req));
+ struct dthe_aes_req_ctx *rctx = skcipher_request_ctx(req);
struct dthe_data *dev_data = dthe_get_dev(ctx);
struct crypto_engine *engine;
/*
- * If data is not a multiple of AES_BLOCK_SIZE, need to return -EINVAL
- * If data length input is zero, no need to do any operation.
+ * If data is not a multiple of AES_BLOCK_SIZE:
+ * - need to return -EINVAL for ECB, CBC as they are block ciphers
+ * - need to fallback to software as H/W doesn't support Ciphertext Stealing for XTS
*/
- if (req->cryptlen % AES_BLOCK_SIZE)
+ if (req->cryptlen % AES_BLOCK_SIZE) {
+ if (ctx->aes_mode == DTHE_AES_XTS) {
+ SYNC_SKCIPHER_REQUEST_ON_STACK(subreq, ctx->skcipher_fb);
+
+ skcipher_request_set_callback(subreq, skcipher_request_flags(req),
+ req->base.complete, req->base.data);
+ skcipher_request_set_crypt(subreq, req->src, req->dst,
+ req->cryptlen, req->iv);
+
+ return rctx->enc ? crypto_skcipher_encrypt(subreq) :
+ crypto_skcipher_decrypt(subreq);
+ }
return -EINVAL;
+ }
- if (req->cryptlen == 0)
+ /*
+ * If data length input is zero, no need to do any operation.
+ * Except for XTS mode, where data length should be non-zero.
+ */
+ if (req->cryptlen == 0) {
+ if (ctx->aes_mode == DTHE_AES_XTS)
+ return -EINVAL;
return 0;
+ }
engine = dev_data->engine;
return crypto_transfer_skcipher_request_to_engine(engine, req);
@@ -399,7 +499,32 @@ static struct skcipher_engine_alg cipher_algs[] = {
.cra_module = THIS_MODULE,
},
.op.do_one_request = dthe_aes_run,
- } /* CBC AES */
+ }, /* CBC AES */
+ {
+ .base.init = dthe_cipher_xts_init_tfm,
+ .base.exit = dthe_cipher_xts_exit_tfm,
+ .base.setkey = dthe_aes_xts_setkey,
+ .base.encrypt = dthe_aes_encrypt,
+ .base.decrypt = dthe_aes_decrypt,
+ .base.min_keysize = AES_MIN_KEY_SIZE * 2,
+ .base.max_keysize = AES_MAX_KEY_SIZE * 2,
+ .base.ivsize = AES_IV_SIZE,
+ .base.base = {
+ .cra_name = "xts(aes)",
+ .cra_driver_name = "xts-aes-dthev2",
+ .cra_priority = 299,
+ .cra_flags = CRYPTO_ALG_TYPE_SKCIPHER |
+ CRYPTO_ALG_ASYNC |
+ CRYPTO_ALG_KERN_DRIVER_ONLY |
+ CRYPTO_ALG_NEED_FALLBACK,
+ .cra_alignmask = AES_BLOCK_SIZE - 1,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct dthe_tfm_ctx),
+ .cra_reqsize = sizeof(struct dthe_aes_req_ctx),
+ .cra_module = THIS_MODULE,
+ },
+ .op.do_one_request = dthe_aes_run,
+ }, /* XTS AES */
};
int dthe_register_aes_algs(void)
diff --git a/drivers/crypto/ti/dthev2-common.h b/drivers/crypto/ti/dthev2-common.h
index 68c94acda8aa..c7a06a4c353f 100644
--- a/drivers/crypto/ti/dthev2-common.h
+++ b/drivers/crypto/ti/dthev2-common.h
@@ -27,10 +27,16 @@
#define DTHE_REG_SIZE 4
#define DTHE_DMA_TIMEOUT_MS 2000
+/*
+ * Size of largest possible key (of all algorithms) to be stored in dthe_tfm_ctx
+ * This is currently the keysize of XTS-AES-256 which is 512 bits (64 bytes)
+ */
+#define DTHE_MAX_KEYSIZE (AES_MAX_KEY_SIZE * 2)
enum dthe_aes_mode {
DTHE_AES_ECB = 0,
DTHE_AES_CBC,
+ DTHE_AES_XTS,
};
/* Driver specific struct definitions */
@@ -73,12 +79,14 @@ struct dthe_list {
* @keylen: AES key length
* @key: AES key
* @aes_mode: AES mode
+ * @skcipher_fb: Fallback crypto skcipher handle for AES-XTS mode
*/
struct dthe_tfm_ctx {
struct dthe_data *dev_data;
unsigned int keylen;
- u32 key[AES_KEYSIZE_256 / sizeof(u32)];
+ u32 key[DTHE_MAX_KEYSIZE / sizeof(u32)];
enum dthe_aes_mode aes_mode;
+ struct crypto_sync_skcipher *skcipher_fb;
};
/**
diff --git a/drivers/crypto/xilinx/xilinx-trng.c b/drivers/crypto/xilinx/xilinx-trng.c
index 4e4700d68127..db0fbb28ff32 100644
--- a/drivers/crypto/xilinx/xilinx-trng.c
+++ b/drivers/crypto/xilinx/xilinx-trng.c
@@ -8,7 +8,6 @@
#include <linux/clk.h>
#include <linux/crypto.h>
#include <linux/delay.h>
-#include <linux/errno.h>
#include <linux/firmware/xlnx-zynqmp.h>
#include <linux/hw_random.h>
#include <linux/io.h>
@@ -18,10 +17,11 @@
#include <linux/mutex.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
-#include <linux/string.h>
+#include <crypto/aes.h>
+#include <crypto/df_sp80090a.h>
+#include <crypto/internal/drbg.h>
#include <crypto/internal/cipher.h>
#include <crypto/internal/rng.h>
-#include <crypto/aes.h>
/* TRNG Registers Offsets */
#define TRNG_STATUS_OFFSET 0x4U
@@ -59,6 +59,8 @@
struct xilinx_rng {
void __iomem *rng_base;
struct device *dev;
+ unsigned char *scratchpadbuf;
+ struct crypto_aes_ctx *aesctx;
struct mutex lock; /* Protect access to TRNG device */
struct hwrng trng;
};
@@ -182,9 +184,13 @@ static void xtrng_enable_entropy(struct xilinx_rng *rng)
static int xtrng_reseed_internal(struct xilinx_rng *rng)
{
u8 entropy[TRNG_ENTROPY_SEED_LEN_BYTES];
+ struct drbg_string data;
+ LIST_HEAD(seedlist);
u32 val;
int ret;
+ drbg_string_fill(&data, entropy, TRNG_SEED_LEN_BYTES);
+ list_add_tail(&data.list, &seedlist);
memset(entropy, 0, sizeof(entropy));
xtrng_enable_entropy(rng);
@@ -192,9 +198,14 @@ static int xtrng_reseed_internal(struct xilinx_rng *rng)
ret = xtrng_collect_random_data(rng, entropy, TRNG_SEED_LEN_BYTES, true);
if (ret != TRNG_SEED_LEN_BYTES)
return -EINVAL;
+ ret = crypto_drbg_ctr_df(rng->aesctx, rng->scratchpadbuf,
+ TRNG_SEED_LEN_BYTES, &seedlist, AES_BLOCK_SIZE,
+ TRNG_SEED_LEN_BYTES);
+ if (ret)
+ return ret;
xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET,
- (u32 *)entropy, TRNG_NUM_INIT_REGS);
+ (u32 *)rng->scratchpadbuf, TRNG_NUM_INIT_REGS);
/* select reseed operation */
iowrite32(TRNG_CTRL_PRNGXS_MASK, rng->rng_base + TRNG_CTRL_OFFSET);
@@ -324,6 +335,7 @@ static void xtrng_hwrng_unregister(struct hwrng *trng)
static int xtrng_probe(struct platform_device *pdev)
{
struct xilinx_rng *rng;
+ size_t sb_size;
int ret;
rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
@@ -333,15 +345,26 @@ static int xtrng_probe(struct platform_device *pdev)
rng->dev = &pdev->dev;
rng->rng_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(rng->rng_base)) {
- dev_err(&pdev->dev, "Failed to map resource %ld\n", PTR_ERR(rng->rng_base));
+ dev_err(&pdev->dev, "Failed to map resource %pe\n", rng->rng_base);
return PTR_ERR(rng->rng_base);
}
+ rng->aesctx = devm_kzalloc(&pdev->dev, sizeof(*rng->aesctx), GFP_KERNEL);
+ if (!rng->aesctx)
+ return -ENOMEM;
+
+ sb_size = crypto_drbg_ctr_df_datalen(TRNG_SEED_LEN_BYTES, AES_BLOCK_SIZE);
+ rng->scratchpadbuf = devm_kzalloc(&pdev->dev, sb_size, GFP_KERNEL);
+ if (!rng->scratchpadbuf) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
xtrng_trng_reset(rng->rng_base);
ret = xtrng_reseed_internal(rng);
if (ret) {
dev_err(&pdev->dev, "TRNG Seed fail\n");
- return ret;
+ goto end;
}
xilinx_rng_dev = rng;
@@ -349,8 +372,9 @@ static int xtrng_probe(struct platform_device *pdev)
ret = crypto_register_rng(&xtrng_trng_alg);
if (ret) {
dev_err(&pdev->dev, "Crypto Random device registration failed: %d\n", ret);
- return ret;
+ goto end;
}
+
ret = xtrng_hwrng_register(&rng->trng);
if (ret) {
dev_err(&pdev->dev, "HWRNG device registration failed: %d\n", ret);
@@ -363,6 +387,7 @@ static int xtrng_probe(struct platform_device *pdev)
crypto_rng_free:
crypto_unregister_rng(&xtrng_trng_alg);
+end:
return ret;
}
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index bd2e282ca93a..77ac940e3013 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -11,25 +11,36 @@
#include "cxlpci.h"
#include "cxl.h"
-struct cxl_cxims_data {
- int nr_maps;
- u64 xormaps[] __counted_by(nr_maps);
-};
-
static const guid_t acpi_cxl_qtg_id_guid =
GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
-static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
+#define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1)
+static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {
+ [1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4
+};
+
+static const int valid_hbiw[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
+
+u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw)
{
- struct cxl_cxims_data *cximsd = cxlrd->platform_data;
- int hbiw = cxlrd->cxlsd.nr_targets;
+ int nr_maps_to_apply = -1;
u64 val;
int pos;
- /* No xormaps for host bridge interleave ways of 1 or 3 */
- if (hbiw == 1 || hbiw == 3)
- return addr;
+ /*
+ * Strictly validate hbiw since this function is used for testing and
+ * that nullifies any expectation of trusted parameters from the CXL
+ * Region Driver.
+ */
+ for (int i = 0; i < ARRAY_SIZE(valid_hbiw); i++) {
+ if (valid_hbiw[i] == hbiw) {
+ nr_maps_to_apply = hbiw_to_nr_maps[hbiw];
+ break;
+ }
+ }
+ if (nr_maps_to_apply == -1 || nr_maps_to_apply > cximsd->nr_maps)
+ return ULLONG_MAX;
/*
* In regions using XOR interleave arithmetic the CXL HPA may not
@@ -60,6 +71,14 @@ static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
return addr;
}
+EXPORT_SYMBOL_FOR_MODULES(cxl_do_xormap_calc, "cxl_translate");
+
+static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
+{
+ struct cxl_cxims_data *cximsd = cxlrd->platform_data;
+
+ return cxl_do_xormap_calc(cximsd, addr, cxlrd->cxlsd.nr_targets);
+}
struct cxl_cxims_context {
struct device *dev;
@@ -353,7 +372,7 @@ static int cxl_acpi_set_cache_size(struct cxl_root_decoder *cxlrd)
rc = hmat_get_extended_linear_cache_size(&res, nid, &cache_size);
if (rc)
- return rc;
+ return 0;
/*
* The cache range is expected to be within the CFMWS.
@@ -378,21 +397,18 @@ static void cxl_setup_extended_linear_cache(struct cxl_root_decoder *cxlrd)
int rc;
rc = cxl_acpi_set_cache_size(cxlrd);
- if (!rc)
- return;
-
- if (rc != -EOPNOTSUPP) {
+ if (rc) {
/*
- * Failing to support extended linear cache region resize does not
+ * Failing to retrieve extended linear cache region resize does not
* prevent the region from functioning. Only causes cxl list showing
* incorrect region size.
*/
dev_warn(cxlrd->cxlsd.cxld.dev.parent,
- "Extended linear cache calculation failed rc:%d\n", rc);
- }
+ "Extended linear cache retrieval failed rc:%d\n", rc);
- /* Ignoring return code */
- cxlrd->cache_size = 0;
+ /* Ignoring return code */
+ cxlrd->cache_size = 0;
+ }
}
DEFINE_FREE(put_cxlrd, struct cxl_root_decoder *,
@@ -453,8 +469,6 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
ig = CXL_DECODER_MIN_GRANULARITY;
cxld->interleave_granularity = ig;
- cxl_setup_extended_linear_cache(cxlrd);
-
if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) {
if (ways != 1 && ways != 3) {
cxims_ctx = (struct cxl_cxims_context) {
@@ -470,18 +484,13 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
return -EINVAL;
}
}
+ cxlrd->ops.hpa_to_spa = cxl_apply_xor_maps;
+ cxlrd->ops.spa_to_hpa = cxl_apply_xor_maps;
}
- cxlrd->qos_class = cfmws->qtg_id;
-
- if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_XOR) {
- cxlrd->ops = kzalloc(sizeof(*cxlrd->ops), GFP_KERNEL);
- if (!cxlrd->ops)
- return -ENOMEM;
+ cxl_setup_extended_linear_cache(cxlrd);
- cxlrd->ops->hpa_to_spa = cxl_apply_xor_maps;
- cxlrd->ops->spa_to_hpa = cxl_apply_xor_maps;
- }
+ cxlrd->qos_class = cfmws->qtg_id;
rc = cxl_decoder_add(cxld);
if (rc)
diff --git a/drivers/cxl/core/cdat.c b/drivers/cxl/core/cdat.c
index c4bd6e8a0cf0..7120b5f2e31f 100644
--- a/drivers/cxl/core/cdat.c
+++ b/drivers/cxl/core/cdat.c
@@ -826,7 +826,7 @@ static struct xarray *cxl_switch_gather_bandwidth(struct cxl_region *cxlr,
cxl_coordinates_combine(coords, coords, ctx->coord);
/*
- * Take the min of the calculated bandwdith and the upstream
+ * Take the min of the calculated bandwidth and the upstream
* switch SSLBIS bandwidth if there's a parent switch
*/
if (!is_root)
@@ -949,7 +949,7 @@ static struct xarray *cxl_hb_gather_bandwidth(struct xarray *xa)
/**
* cxl_region_update_bandwidth - Update the bandwidth access coordinates of a region
* @cxlr: The region being operated on
- * @input_xa: xarray holds cxl_perf_ctx wht calculated bandwidth per ACPI0017 instance
+ * @input_xa: xarray holds cxl_perf_ctx with calculated bandwidth per ACPI0017 instance
*/
static void cxl_region_update_bandwidth(struct cxl_region *cxlr,
struct xarray *input_xa)
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index d3a094ca01ad..1c5d2022c87a 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -905,6 +905,9 @@ static void cxl_decoder_reset(struct cxl_decoder *cxld)
if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
return;
+ if (test_bit(CXL_DECODER_F_LOCK, &cxld->flags))
+ return;
+
if (port->commit_end == id)
cxl_port_commit_reap(cxld);
else
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c
index 18825e1505d6..5b023a0178a4 100644
--- a/drivers/cxl/core/pci.c
+++ b/drivers/cxl/core/pci.c
@@ -71,85 +71,6 @@ struct cxl_dport *__devm_cxl_add_dport_by_dev(struct cxl_port *port,
}
EXPORT_SYMBOL_NS_GPL(__devm_cxl_add_dport_by_dev, "CXL");
-struct cxl_walk_context {
- struct pci_bus *bus;
- struct cxl_port *port;
- int type;
- int error;
- int count;
-};
-
-static int match_add_dports(struct pci_dev *pdev, void *data)
-{
- struct cxl_walk_context *ctx = data;
- struct cxl_port *port = ctx->port;
- int type = pci_pcie_type(pdev);
- struct cxl_register_map map;
- struct cxl_dport *dport;
- u32 lnkcap, port_num;
- int rc;
-
- if (pdev->bus != ctx->bus)
- return 0;
- if (!pci_is_pcie(pdev))
- return 0;
- if (type != ctx->type)
- return 0;
- if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
- &lnkcap))
- return 0;
-
- rc = cxl_find_regblock(pdev, CXL_REGLOC_RBI_COMPONENT, &map);
- if (rc)
- dev_dbg(&port->dev, "failed to find component registers\n");
-
- port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
- dport = devm_cxl_add_dport(port, &pdev->dev, port_num, map.resource);
- if (IS_ERR(dport)) {
- ctx->error = PTR_ERR(dport);
- return PTR_ERR(dport);
- }
- ctx->count++;
-
- return 0;
-}
-
-/**
- * devm_cxl_port_enumerate_dports - enumerate downstream ports of the upstream port
- * @port: cxl_port whose ->uport_dev is the upstream of dports to be enumerated
- *
- * Returns a positive number of dports enumerated or a negative error
- * code.
- */
-int devm_cxl_port_enumerate_dports(struct cxl_port *port)
-{
- struct pci_bus *bus = cxl_port_to_pci_bus(port);
- struct cxl_walk_context ctx;
- int type;
-
- if (!bus)
- return -ENXIO;
-
- if (pci_is_root_bus(bus))
- type = PCI_EXP_TYPE_ROOT_PORT;
- else
- type = PCI_EXP_TYPE_DOWNSTREAM;
-
- ctx = (struct cxl_walk_context) {
- .port = port,
- .bus = bus,
- .type = type,
- };
- pci_walk_bus(bus, match_add_dports, &ctx);
-
- if (ctx.count == 0)
- return -ENODEV;
- if (ctx.error)
- return ctx.error;
- return ctx.count;
-}
-EXPORT_SYMBOL_NS_GPL(devm_cxl_port_enumerate_dports, "CXL");
-
static int cxl_dvsec_mem_range_valid(struct cxl_dev_state *cxlds, int id)
{
struct pci_dev *pdev = to_pci_dev(cxlds->dev);
@@ -1217,6 +1138,14 @@ int cxl_gpf_port_setup(struct cxl_dport *dport)
return 0;
}
+struct cxl_walk_context {
+ struct pci_bus *bus;
+ struct cxl_port *port;
+ int type;
+ int error;
+ int count;
+};
+
static int count_dports(struct pci_dev *pdev, void *data)
{
struct cxl_walk_context *ctx = data;
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index 8128fd2b5b31..fef3aa0c6680 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -459,7 +459,6 @@ static void cxl_root_decoder_release(struct device *dev)
if (atomic_read(&cxlrd->region_id) >= 0)
memregion_free(atomic_read(&cxlrd->region_id));
__cxl_decoder_release(&cxlrd->cxlsd.cxld);
- kfree(cxlrd->ops);
kfree(cxlrd);
}
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index b06fee1978ba..82d229c8f9bf 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -245,6 +245,9 @@ static void cxl_region_decode_reset(struct cxl_region *cxlr, int count)
struct cxl_region_params *p = &cxlr->params;
int i;
+ if (test_bit(CXL_REGION_F_LOCK, &cxlr->flags))
+ return;
+
/*
* Before region teardown attempt to flush, evict any data cached for
* this region, or scream loudly about missing arch / platform support
@@ -419,6 +422,9 @@ static ssize_t commit_store(struct device *dev, struct device_attribute *attr,
return len;
}
+ if (test_bit(CXL_REGION_F_LOCK, &cxlr->flags))
+ return -EPERM;
+
rc = queue_reset(cxlr);
if (rc)
return rc;
@@ -461,21 +467,6 @@ static ssize_t commit_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(commit);
-static umode_t cxl_region_visible(struct kobject *kobj, struct attribute *a,
- int n)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct cxl_region *cxlr = to_cxl_region(dev);
-
- /*
- * Support tooling that expects to find a 'uuid' attribute for all
- * regions regardless of mode.
- */
- if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_PARTMODE_PMEM)
- return 0444;
- return a->mode;
-}
-
static ssize_t interleave_ways_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -754,6 +745,21 @@ static ssize_t size_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RW(size);
+static ssize_t extended_linear_cache_size_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cxl_region *cxlr = to_cxl_region(dev);
+ struct cxl_region_params *p = &cxlr->params;
+ ssize_t rc;
+
+ ACQUIRE(rwsem_read_intr, rwsem)(&cxl_rwsem.region);
+ if ((rc = ACQUIRE_ERR(rwsem_read_intr, &rwsem)))
+ return rc;
+ return sysfs_emit(buf, "%#llx\n", p->cache_size);
+}
+static DEVICE_ATTR_RO(extended_linear_cache_size);
+
static struct attribute *cxl_region_attrs[] = {
&dev_attr_uuid.attr,
&dev_attr_commit.attr,
@@ -762,9 +768,34 @@ static struct attribute *cxl_region_attrs[] = {
&dev_attr_resource.attr,
&dev_attr_size.attr,
&dev_attr_mode.attr,
+ &dev_attr_extended_linear_cache_size.attr,
NULL,
};
+static umode_t cxl_region_visible(struct kobject *kobj, struct attribute *a,
+ int n)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct cxl_region *cxlr = to_cxl_region(dev);
+
+ /*
+ * Support tooling that expects to find a 'uuid' attribute for all
+ * regions regardless of mode.
+ */
+ if (a == &dev_attr_uuid.attr && cxlr->mode != CXL_PARTMODE_PMEM)
+ return 0444;
+
+ /*
+ * Don't display extended linear cache attribute if there is no
+ * extended linear cache.
+ */
+ if (a == &dev_attr_extended_linear_cache_size.attr &&
+ cxlr->params.cache_size == 0)
+ return 0;
+
+ return a->mode;
+}
+
static const struct attribute_group cxl_region_group = {
.attrs = cxl_region_attrs,
.is_visible = cxl_region_visible,
@@ -838,16 +869,16 @@ static int match_free_decoder(struct device *dev, const void *data)
return 1;
}
-static bool region_res_match_cxl_range(const struct cxl_region_params *p,
- const struct range *range)
+static bool spa_maps_hpa(const struct cxl_region_params *p,
+ const struct range *range)
{
if (!p->res)
return false;
/*
- * If an extended linear cache region then the CXL range is assumed
- * to be fronted by the DRAM range in current known implementation.
- * This assumption will be made until a variant implementation exists.
+ * The extended linear cache region is constructed by a 1:1 ratio
+ * where the SPA maps equal amounts of DRAM and CXL HPA capacity with
+ * CXL decoders at the high end of the SPA range.
*/
return p->res->start + p->cache_size == range->start &&
p->res->end == range->end;
@@ -865,7 +896,7 @@ static int match_auto_decoder(struct device *dev, const void *data)
cxld = to_cxl_decoder(dev);
r = &cxld->hpa_range;
- if (region_res_match_cxl_range(p, r))
+ if (spa_maps_hpa(p, r))
return 1;
return 0;
@@ -1059,6 +1090,16 @@ static int cxl_rr_assign_decoder(struct cxl_port *port, struct cxl_region *cxlr,
return 0;
}
+static void cxl_region_set_lock(struct cxl_region *cxlr,
+ struct cxl_decoder *cxld)
+{
+ if (!test_bit(CXL_DECODER_F_LOCK, &cxld->flags))
+ return;
+
+ set_bit(CXL_REGION_F_LOCK, &cxlr->flags);
+ clear_bit(CXL_REGION_F_NEEDS_RESET, &cxlr->flags);
+}
+
/**
* cxl_port_attach_region() - track a region's interest in a port by endpoint
* @port: port to add a new region reference 'struct cxl_region_ref'
@@ -1170,6 +1211,8 @@ static int cxl_port_attach_region(struct cxl_port *port,
}
}
+ cxl_region_set_lock(cxlr, cxld);
+
rc = cxl_rr_ep_add(cxl_rr, cxled);
if (rc) {
dev_dbg(&cxlr->dev,
@@ -1328,7 +1371,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
struct cxl_endpoint_decoder *cxled)
{
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
- int parent_iw, parent_ig, ig, iw, rc, inc = 0, pos = cxled->pos;
+ int parent_iw, parent_ig, ig, iw, rc, pos = cxled->pos;
struct cxl_port *parent_port = to_cxl_port(port->dev.parent);
struct cxl_region_ref *cxl_rr = cxl_rr_load(port, cxlr);
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
@@ -1465,7 +1508,7 @@ static int cxl_port_setup_targets(struct cxl_port *port,
if (test_bit(CXL_REGION_F_AUTO, &cxlr->flags)) {
if (cxld->interleave_ways != iw ||
(iw > 1 && cxld->interleave_granularity != ig) ||
- !region_res_match_cxl_range(p, &cxld->hpa_range) ||
+ !spa_maps_hpa(p, &cxld->hpa_range) ||
((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) {
dev_err(&cxlr->dev,
"%s:%s %s expected iw: %d ig: %d %pr\n",
@@ -1520,9 +1563,8 @@ add_target:
cxlsd->target[cxl_rr->nr_targets_set] = ep->dport;
cxlsd->cxld.target_map[cxl_rr->nr_targets_set] = ep->dport->port_id;
}
- inc = 1;
+ cxl_rr->nr_targets_set++;
out_target_set:
- cxl_rr->nr_targets_set += inc;
dev_dbg(&cxlr->dev, "%s:%s target[%d] = %s for %s:%s @ %d\n",
dev_name(port->uport_dev), dev_name(&port->dev),
cxl_rr->nr_targets_set - 1, dev_name(ep->dport->dport_dev),
@@ -2439,6 +2481,7 @@ static struct cxl_region *cxl_region_alloc(struct cxl_root_decoder *cxlrd, int i
dev->bus = &cxl_bus_type;
dev->type = &cxl_region_type;
cxlr->id = id;
+ cxl_region_set_lock(cxlr, &cxlrd->cxlsd.cxld);
return cxlr;
}
@@ -2924,38 +2967,119 @@ static bool cxl_is_hpa_in_chunk(u64 hpa, struct cxl_region *cxlr, int pos)
return false;
}
-static bool has_hpa_to_spa(struct cxl_root_decoder *cxlrd)
+#define CXL_POS_ZERO 0
+/**
+ * cxl_validate_translation_params
+ * @eiw: encoded interleave ways
+ * @eig: encoded interleave granularity
+ * @pos: position in interleave
+ *
+ * Callers pass CXL_POS_ZERO when no position parameter needs validating.
+ *
+ * Returns: 0 on success, -EINVAL on first invalid parameter
+ */
+int cxl_validate_translation_params(u8 eiw, u16 eig, int pos)
{
- return cxlrd->ops && cxlrd->ops->hpa_to_spa;
+ int ways, gran;
+
+ if (eiw_to_ways(eiw, &ways)) {
+ pr_debug("%s: invalid eiw=%u\n", __func__, eiw);
+ return -EINVAL;
+ }
+ if (eig_to_granularity(eig, &gran)) {
+ pr_debug("%s: invalid eig=%u\n", __func__, eig);
+ return -EINVAL;
+ }
+ if (pos < 0 || pos >= ways) {
+ pr_debug("%s: invalid pos=%d for ways=%u\n", __func__, pos,
+ ways);
+ return -EINVAL;
+ }
+
+ return 0;
}
+EXPORT_SYMBOL_FOR_MODULES(cxl_validate_translation_params, "cxl_translate");
-static bool has_spa_to_hpa(struct cxl_root_decoder *cxlrd)
+u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig)
{
- return cxlrd->ops && cxlrd->ops->spa_to_hpa;
+ u64 dpa_offset, bits_lower, bits_upper, temp;
+ int ret;
+
+ ret = cxl_validate_translation_params(eiw, eig, CXL_POS_ZERO);
+ if (ret)
+ return ULLONG_MAX;
+
+ /*
+ * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
+ * Lower bits [IG+7:0] pass through unchanged
+ * (eiw < 8)
+ * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
+ * Clear the position bits to isolate upper section, then
+ * reverse the left shift by eiw that occurred during DPA->HPA
+ * (eiw >= 8)
+ * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
+ * Extract upper bits from the correct bit range and divide by 3
+ * to recover the original DPA upper bits
+ */
+ bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
+ if (eiw < 8) {
+ temp = hpa_offset &= ~GENMASK_ULL(eig + eiw + 8 - 1, 0);
+ dpa_offset = temp >> eiw;
+ } else {
+ bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
+ dpa_offset = bits_upper << (eig + 8);
+ }
+ dpa_offset |= bits_lower;
+
+ return dpa_offset;
}
+EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_dpa_offset, "cxl_translate");
-u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
- u64 dpa)
+int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig)
{
- struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
- u64 dpa_offset, hpa_offset, bits_upper, mask_upper, hpa;
- struct cxl_region_params *p = &cxlr->params;
- struct cxl_endpoint_decoder *cxled = NULL;
- u16 eig = 0;
- u8 eiw = 0;
- int pos;
+ unsigned int ways = 0;
+ u64 shifted, rem;
+ int pos, ret;
- for (int i = 0; i < p->nr_targets; i++) {
- cxled = p->targets[i];
- if (cxlmd == cxled_to_memdev(cxled))
- break;
+ ret = cxl_validate_translation_params(eiw, eig, CXL_POS_ZERO);
+ if (ret)
+ return ret;
+
+ if (!eiw)
+ /* position is 0 if no interleaving */
+ return 0;
+
+ /*
+ * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
+ * eiw < 8
+ * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
+ * Per spec "remove IW bits starting with bit position IG+8"
+ * eiw >= 8
+ * Position is not explicitly stored in HPA_OFFSET bits. It is
+ * derived from the modulo operation of the upper bits using
+ * the total number of interleave ways.
+ */
+ if (eiw < 8) {
+ pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
+ } else {
+ shifted = hpa_offset >> (eig + 8);
+ eiw_to_ways(eiw, &ways);
+ div64_u64_rem(shifted, ways, &rem);
+ pos = rem;
}
- if (!cxled || cxlmd != cxled_to_memdev(cxled))
- return ULLONG_MAX;
- pos = cxled->pos;
- ways_to_eiw(p->interleave_ways, &eiw);
- granularity_to_eig(p->interleave_granularity, &eig);
+ return pos;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_position, "cxl_translate");
+
+u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig)
+{
+ u64 mask_upper, hpa_offset, bits_upper;
+ int ret;
+
+ ret = cxl_validate_translation_params(eiw, eig, pos);
+ if (ret)
+ return ULLONG_MAX;
/*
* The device position in the region interleave set was removed
@@ -2967,9 +3091,6 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
* 8.2.4.19.13 Implementation Note: Device Decode Logic
*/
- /* Remove the dpa base */
- dpa_offset = dpa - cxl_dpa_resource_start(cxled);
-
mask_upper = GENMASK_ULL(51, eig + 8);
if (eiw < 8) {
@@ -2984,12 +3105,43 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
/* The lower bits remain unchanged */
hpa_offset |= dpa_offset & GENMASK_ULL(eig + 7, 0);
+ return hpa_offset;
+}
+EXPORT_SYMBOL_FOR_MODULES(cxl_calculate_hpa_offset, "cxl_translate");
+
+u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
+ u64 dpa)
+{
+ struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
+ struct cxl_region_params *p = &cxlr->params;
+ struct cxl_endpoint_decoder *cxled = NULL;
+ u64 dpa_offset, hpa_offset, hpa;
+ u16 eig = 0;
+ u8 eiw = 0;
+ int pos;
+
+ for (int i = 0; i < p->nr_targets; i++) {
+ if (cxlmd == cxled_to_memdev(p->targets[i])) {
+ cxled = p->targets[i];
+ break;
+ }
+ }
+ if (!cxled)
+ return ULLONG_MAX;
+
+ pos = cxled->pos;
+ ways_to_eiw(p->interleave_ways, &eiw);
+ granularity_to_eig(p->interleave_granularity, &eig);
+
+ dpa_offset = dpa - cxl_dpa_resource_start(cxled);
+ hpa_offset = cxl_calculate_hpa_offset(dpa_offset, pos, eiw, eig);
+
/* Apply the hpa_offset to the region base address */
hpa = hpa_offset + p->res->start + p->cache_size;
/* Root decoder translation overrides typical modulo decode */
- if (has_hpa_to_spa(cxlrd))
- hpa = cxlrd->ops->hpa_to_spa(cxlrd, hpa);
+ if (cxlrd->ops.hpa_to_spa)
+ hpa = cxlrd->ops.hpa_to_spa(cxlrd, hpa);
if (!cxl_resource_contains_addr(p->res, hpa)) {
dev_dbg(&cxlr->dev,
@@ -2998,7 +3150,7 @@ u64 cxl_dpa_to_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
}
/* Simple chunk check, by pos & gran, only applies to modulo decodes */
- if (!has_hpa_to_spa(cxlrd) && (!cxl_is_hpa_in_chunk(hpa, cxlr, pos)))
+ if (!cxlrd->ops.hpa_to_spa && !cxl_is_hpa_in_chunk(hpa, cxlr, pos))
return ULLONG_MAX;
return hpa;
@@ -3016,8 +3168,6 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(cxlr->dev.parent);
struct cxl_endpoint_decoder *cxled;
u64 hpa, hpa_offset, dpa_offset;
- u64 bits_upper, bits_lower;
- u64 shifted, rem, temp;
u16 eig = 0;
u8 eiw = 0;
int pos;
@@ -3033,56 +3183,21 @@ static int region_offset_to_dpa_result(struct cxl_region *cxlr, u64 offset,
* If the root decoder has SPA to CXL HPA callback, use it. Otherwise
* CXL HPA is assumed to equal SPA.
*/
- if (has_spa_to_hpa(cxlrd)) {
- hpa = cxlrd->ops->spa_to_hpa(cxlrd, p->res->start + offset);
+ if (cxlrd->ops.spa_to_hpa) {
+ hpa = cxlrd->ops.spa_to_hpa(cxlrd, p->res->start + offset);
hpa_offset = hpa - p->res->start;
} else {
hpa_offset = offset;
}
- /*
- * Interleave position: CXL Spec 3.2 Section 8.2.4.20.13
- * eiw < 8
- * Position is in the IW bits at HPA_OFFSET[IG+8+IW-1:IG+8].
- * Per spec "remove IW bits starting with bit position IG+8"
- * eiw >= 8
- * Position is not explicitly stored in HPA_OFFSET bits. It is
- * derived from the modulo operation of the upper bits using
- * the total number of interleave ways.
- */
- if (eiw < 8) {
- pos = (hpa_offset >> (eig + 8)) & GENMASK(eiw - 1, 0);
- } else {
- shifted = hpa_offset >> (eig + 8);
- div64_u64_rem(shifted, p->interleave_ways, &rem);
- pos = rem;
- }
+
+ pos = cxl_calculate_position(hpa_offset, eiw, eig);
if (pos < 0 || pos >= p->nr_targets) {
dev_dbg(&cxlr->dev, "Invalid position %d for %d targets\n",
pos, p->nr_targets);
return -ENXIO;
}
- /*
- * DPA offset: CXL Spec 3.2 Section 8.2.4.20.13
- * Lower bits [IG+7:0] pass through unchanged
- * (eiw < 8)
- * Per spec: DPAOffset[51:IG+8] = (HPAOffset[51:IG+IW+8] >> IW)
- * Clear the position bits to isolate upper section, then
- * reverse the left shift by eiw that occurred during DPA->HPA
- * (eiw >= 8)
- * Per spec: DPAOffset[51:IG+8] = HPAOffset[51:IG+IW] / 3
- * Extract upper bits from the correct bit range and divide by 3
- * to recover the original DPA upper bits
- */
- bits_lower = hpa_offset & GENMASK_ULL(eig + 7, 0);
- if (eiw < 8) {
- temp = hpa_offset &= ~((u64)GENMASK(eig + eiw + 8 - 1, 0));
- dpa_offset = temp >> eiw;
- } else {
- bits_upper = div64_u64(hpa_offset >> (eig + eiw), 3);
- dpa_offset = bits_upper << (eig + 8);
- }
- dpa_offset |= bits_lower;
+ dpa_offset = cxl_calculate_dpa_offset(hpa_offset, eiw, eig);
/* Look-up and return the result: a memdev and a DPA */
for (int i = 0; i < p->nr_targets; i++) {
@@ -3398,7 +3513,7 @@ static int match_region_by_range(struct device *dev, const void *data)
p = &cxlr->params;
guard(rwsem_read)(&cxl_rwsem.region);
- return region_res_match_cxl_range(p, r);
+ return spa_maps_hpa(p, r);
}
static int cxl_extended_linear_cache_resize(struct cxl_region *cxlr,
@@ -3479,6 +3594,10 @@ static int __construct_region(struct cxl_region *cxlr,
"Extended linear cache calculation failed rc:%d\n", rc);
}
+ rc = sysfs_update_group(&cxlr->dev.kobj, &cxl_region_group);
+ if (rc)
+ return rc;
+
rc = insert_resource(cxlrd->res, res);
if (rc) {
/*
@@ -3702,6 +3821,7 @@ static int cxl_region_debugfs_poison_inject(void *data, u64 offset)
if (validate_region_offset(cxlr, offset))
return -EINVAL;
+ offset -= cxlr->params.cache_size;
rc = region_offset_to_dpa_result(cxlr, offset, &result);
if (rc || !result.cxlmd || result.dpa == ULLONG_MAX) {
dev_dbg(&cxlr->dev,
@@ -3734,6 +3854,7 @@ static int cxl_region_debugfs_poison_clear(void *data, u64 offset)
if (validate_region_offset(cxlr, offset))
return -EINVAL;
+ offset -= cxlr->params.cache_size;
rc = region_offset_to_dpa_result(cxlr, offset, &result);
if (rc || !result.cxlmd || result.dpa == ULLONG_MAX) {
dev_dbg(&cxlr->dev,
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 231ddccf8977..ba17fa86d249 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -451,7 +451,7 @@ struct cxl_root_decoder {
void *platform_data;
struct mutex range_lock;
int qos_class;
- struct cxl_rd_ops *ops;
+ struct cxl_rd_ops ops;
struct cxl_switch_decoder cxlsd;
};
@@ -517,6 +517,14 @@ enum cxl_partition_mode {
*/
#define CXL_REGION_F_NEEDS_RESET 1
+/*
+ * Indicate whether this region is locked due to 1 or more decoders that have
+ * been locked. The approach of all or nothing is taken with regard to the
+ * locked attribute. CXL_REGION_F_NEEDS_RESET should not be set if this flag is
+ * set.
+ */
+#define CXL_REGION_F_LOCK 2
+
/**
* struct cxl_region - CXL region
* @dev: This region's device
@@ -738,6 +746,25 @@ static inline bool is_cxl_root(struct cxl_port *port)
return port->uport_dev == port->dev.parent;
}
+/* Address translation functions exported to cxl_translate test module only */
+int cxl_validate_translation_params(u8 eiw, u16 eig, int pos);
+u64 cxl_calculate_hpa_offset(u64 dpa_offset, int pos, u8 eiw, u16 eig);
+u64 cxl_calculate_dpa_offset(u64 hpa_offset, u8 eiw, u16 eig);
+int cxl_calculate_position(u64 hpa_offset, u8 eiw, u16 eig);
+struct cxl_cxims_data {
+ int nr_maps;
+ u64 xormaps[] __counted_by(nr_maps);
+};
+
+#if IS_ENABLED(CONFIG_CXL_ACPI)
+u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw);
+#else
+static inline u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw)
+{
+ return ULLONG_MAX;
+}
+#endif
+
int cxl_num_decoders_committed(struct cxl_port *port);
bool is_cxl_port(const struct device *dev);
struct cxl_port *to_cxl_port(const struct device *dev);
diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h
index 7ae621e618e7..1d526bea8431 100644
--- a/drivers/cxl/cxlpci.h
+++ b/drivers/cxl/cxlpci.h
@@ -127,7 +127,6 @@ static inline bool cxl_pci_flit_256(struct pci_dev *pdev)
return lnksta2 & PCI_EXP_LNKSTA2_FLIT;
}
-int devm_cxl_port_enumerate_dports(struct cxl_port *port);
struct cxl_dev_state;
void read_cdat_data(struct cxl_port *port);
void cxl_cor_error_detected(struct pci_dev *pdev);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index bd100ac31672..0be4e508affe 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -136,7 +136,7 @@ static irqreturn_t cxl_pci_mbox_irq(int irq, void *id)
if (opcode == CXL_MBOX_OP_SANITIZE) {
mutex_lock(&cxl_mbox->mbox_mutex);
if (mds->security.sanitize_node)
- mod_delayed_work(system_wq, &mds->security.poll_dwork, 0);
+ mod_delayed_work(system_percpu_wq, &mds->security.poll_dwork, 0);
mutex_unlock(&cxl_mbox->mbox_mutex);
} else {
/* short-circuit the wait in __cxl_pci_mbox_send_cmd() */
diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index d7714d8afb0f..c00b9dff4a06 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -433,7 +433,7 @@ static struct dax_device *dax_dev_get(dev_t devt)
return NULL;
dax_dev = to_dax_dev(inode);
- if (inode->i_state & I_NEW) {
+ if (inode_state_read_once(inode) & I_NEW) {
set_bit(DAXDEV_ALIVE, &dax_dev->flags);
inode->i_cdev = &dax_dev->cdev;
inode->i_mode = S_IFCHR;
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 2e8d01d47f69..00979f2e0e27 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -20,6 +20,7 @@
#include <linux/stat.h>
#include <linux/pm_opp.h>
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/list.h>
@@ -28,7 +29,6 @@
#include <linux/of.h>
#include <linux/pm_qos.h>
#include <linux/units.h>
-#include "governor.h"
#define CREATE_TRACE_POINTS
#include <trace/events/devfreq.h>
diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c
index 953cf9a1e9f7..8cd6f9a59f64 100644
--- a/drivers/devfreq/governor_passive.c
+++ b/drivers/devfreq/governor_passive.c
@@ -14,8 +14,33 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/units.h>
-#include "governor.h"
+
+/**
+ * struct devfreq_cpu_data - Hold the per-cpu data
+ * @node: list node
+ * @dev: reference to cpu device.
+ * @first_cpu: the cpumask of the first cpu of a policy.
+ * @opp_table: reference to cpu opp table.
+ * @cur_freq: the current frequency of the cpu.
+ * @min_freq: the min frequency of the cpu.
+ * @max_freq: the max frequency of the cpu.
+ *
+ * This structure stores the required cpu_data of a cpu.
+ * This is auto-populated by the governor.
+ */
+struct devfreq_cpu_data {
+ struct list_head node;
+
+ struct device *dev;
+ unsigned int first_cpu;
+
+ struct opp_table *opp_table;
+ unsigned int cur_freq;
+ unsigned int min_freq;
+ unsigned int max_freq;
+};
static struct devfreq_cpu_data *
get_parent_cpu_data(struct devfreq_passive_data *p_data,
diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c
index 2e4e981446fa..fdb22bf512cf 100644
--- a/drivers/devfreq/governor_performance.c
+++ b/drivers/devfreq/governor_performance.c
@@ -7,8 +7,8 @@
*/
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/module.h>
-#include "governor.h"
static int devfreq_performance_func(struct devfreq *df,
unsigned long *freq)
diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c
index f059e8814804..ee2d6ec8a512 100644
--- a/drivers/devfreq/governor_powersave.c
+++ b/drivers/devfreq/governor_powersave.c
@@ -7,8 +7,8 @@
*/
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/module.h>
-#include "governor.h"
static int devfreq_powersave_func(struct devfreq *df,
unsigned long *freq)
diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c
index c23435736367..ac9c5e9e51a4 100644
--- a/drivers/devfreq/governor_simpleondemand.c
+++ b/drivers/devfreq/governor_simpleondemand.c
@@ -9,12 +9,12 @@
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/math64.h>
-#include "governor.h"
/* Default constants for DevFreq-Simple-Ondemand (DFSO) */
#define DFSO_UPTHRESHOLD (90)
-#define DFSO_DOWNDIFFERENCTIAL (5)
+#define DFSO_DOWNDIFFERENTIAL (5)
static int devfreq_simple_ondemand_func(struct devfreq *df,
unsigned long *freq)
{
@@ -22,7 +22,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df,
struct devfreq_dev_status *stat;
unsigned long long a, b;
unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD;
- unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL;
+ unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENTIAL;
struct devfreq_simple_ondemand_data *data = df->data;
err = devfreq_update_stats(df);
diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c
index 175de0c0b50e..395174f93960 100644
--- a/drivers/devfreq/governor_userspace.c
+++ b/drivers/devfreq/governor_userspace.c
@@ -9,11 +9,11 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/kstrtox.h>
#include <linux/pm.h>
#include <linux/mutex.h>
#include <linux/module.h>
-#include "governor.h"
struct userspace_data {
unsigned long user_frequency;
diff --git a/drivers/devfreq/hisi_uncore_freq.c b/drivers/devfreq/hisi_uncore_freq.c
index 96d1815059e3..4d00d813c8ac 100644
--- a/drivers/devfreq/hisi_uncore_freq.c
+++ b/drivers/devfreq/hisi_uncore_freq.c
@@ -9,6 +9,7 @@
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/errno.h>
@@ -26,8 +27,6 @@
#include <linux/units.h>
#include <acpi/pcc.h>
-#include "governor.h"
-
struct hisi_uncore_pcc_data {
u16 status;
u16 resv;
@@ -265,10 +264,11 @@ static int hisi_uncore_target(struct device *dev, unsigned long *freq,
dev_err(dev, "Failed to get opp for freq %lu hz\n", *freq);
return PTR_ERR(opp);
}
- dev_pm_opp_put(opp);
data = (u32)(dev_pm_opp_get_freq(opp) / HZ_PER_MHZ);
+ dev_pm_opp_put(opp);
+
return hisi_uncore_cmd_send(uncore, HUCF_PCC_CMD_SET_FREQ, &data);
}
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 4a4f0106ab9d..8b57194ac698 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -9,9 +9,11 @@
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/devfreq.h>
+#include <linux/devfreq-governor.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -21,8 +23,6 @@
#include <soc/tegra/fuse.h>
-#include "governor.h"
-
#define ACTMON_GLB_STATUS 0x0
#define ACTMON_GLB_PERIOD_CTRL 0x4
@@ -326,14 +326,9 @@ static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
unsigned int i;
const struct tegra_actmon_emc_ratio *ratio = actmon_emc_ratios;
- for (i = 0; i < ARRAY_SIZE(actmon_emc_ratios); i++, ratio++) {
- if (cpu_freq >= ratio->cpu_freq) {
- if (ratio->emc_freq >= tegra->max_freq)
- return tegra->max_freq;
- else
- return ratio->emc_freq;
- }
- }
+ for (i = 0; i < ARRAY_SIZE(actmon_emc_ratios); i++, ratio++)
+ if (cpu_freq >= ratio->cpu_freq)
+ return min(ratio->emc_freq, tegra->max_freq);
return 0;
}
diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c
index 0374f8350ff7..b8c16586706c 100644
--- a/drivers/dibs/dibs_main.c
+++ b/drivers/dibs/dibs_main.c
@@ -6,8 +6,7 @@
*
* Copyright IBM Corp. 2025
*/
-#define KMSG_COMPONENT "dibs"
-#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#define pr_fmt(fmt) "dibs: " fmt
#include <linux/module.h>
#include <linux/types.h>
@@ -254,9 +253,6 @@ static int __init dibs_init(void)
{
int rc;
- memset(clients, 0, sizeof(clients));
- max_client = 0;
-
dibs_class = class_create("dibs");
if (IS_ERR(dibs_class))
return PTR_ERR(dibs_class);
@@ -274,5 +270,5 @@ static void __exit dibs_exit(void)
class_destroy(dibs_class);
}
-module_init(dibs_init);
+subsys_initcall(dibs_init);
module_exit(dibs_exit);
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 70ec901edf2c..2008fb7481b3 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
- dma-fence-unwrap.o dma-resv.o
+ dma-fence-unwrap.o dma-resv.o dma-buf-mapping.o
obj-$(CONFIG_DMABUF_HEAPS) += dma-heap.o
obj-$(CONFIG_DMABUF_HEAPS) += heaps/
obj-$(CONFIG_SYNC_FILE) += sync_file.o
diff --git a/drivers/dma-buf/dma-buf-mapping.c b/drivers/dma-buf/dma-buf-mapping.c
new file mode 100644
index 000000000000..b7352e609fbd
--- /dev/null
+++ b/drivers/dma-buf/dma-buf-mapping.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * DMA BUF Mapping Helpers
+ *
+ */
+#include <linux/dma-buf-mapping.h>
+#include <linux/dma-resv.h>
+
+static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
+ dma_addr_t addr)
+{
+ unsigned int len, nents;
+ int i;
+
+ nents = DIV_ROUND_UP(length, UINT_MAX);
+ for (i = 0; i < nents; i++) {
+ len = min_t(size_t, length, UINT_MAX);
+ length -= len;
+ /*
+ * DMABUF abuses scatterlist to create a scatterlist
+ * that does not have any CPU list, only the DMA list.
+ * Always set the page related values to NULL to ensure
+ * importers can't use it. The phys_addr based DMA API
+ * does not require the CPU list for mapping or unmapping.
+ */
+ sg_set_page(sgl, NULL, 0, 0);
+ sg_dma_address(sgl) = addr + (dma_addr_t)i * UINT_MAX;
+ sg_dma_len(sgl) = len;
+ sgl = sg_next(sgl);
+ }
+
+ return sgl;
+}
+
+static unsigned int calc_sg_nents(struct dma_iova_state *state,
+ struct dma_buf_phys_vec *phys_vec,
+ size_t nr_ranges, size_t size)
+{
+ unsigned int nents = 0;
+ size_t i;
+
+ if (!state || !dma_use_iova(state)) {
+ for (i = 0; i < nr_ranges; i++)
+ nents += DIV_ROUND_UP(phys_vec[i].len, UINT_MAX);
+ } else {
+ /*
+ * In IOVA case, there is only one SG entry which spans
+ * for whole IOVA address space, but we need to make sure
+ * that it fits sg->length, maybe we need more.
+ */
+ nents = DIV_ROUND_UP(size, UINT_MAX);
+ }
+
+ return nents;
+}
+
+/**
+ * struct dma_buf_dma - holds DMA mapping information
+ * @sgt: Scatter-gather table
+ * @state: DMA IOVA state relevant in IOMMU-based DMA
+ * @size: Total size of DMA transfer
+ */
+struct dma_buf_dma {
+ struct sg_table sgt;
+ struct dma_iova_state *state;
+ size_t size;
+};
+
+/**
+ * dma_buf_phys_vec_to_sgt - Returns the scatterlist table of the attachment
+ * from arrays of physical vectors. This funciton is intended for MMIO memory
+ * only.
+ * @attach: [in] attachment whose scatterlist is to be returned
+ * @provider: [in] p2pdma provider
+ * @phys_vec: [in] array of physical vectors
+ * @nr_ranges: [in] number of entries in phys_vec array
+ * @size: [in] total size of phys_vec
+ * @dir: [in] direction of DMA transfer
+ *
+ * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR
+ * on error. May return -EINTR if it is interrupted by a signal.
+ *
+ * On success, the DMA addresses and lengths in the returned scatterlist are
+ * PAGE_SIZE aligned.
+ *
+ * A mapping must be unmapped by using dma_buf_free_sgt().
+ *
+ * NOTE: This function is intended for exporters. If direct traffic routing is
+ * mandatory exporter should call routing pci_p2pdma_map_type() before calling
+ * this function.
+ */
+struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
+ struct p2pdma_provider *provider,
+ struct dma_buf_phys_vec *phys_vec,
+ size_t nr_ranges, size_t size,
+ enum dma_data_direction dir)
+{
+ unsigned int nents, mapped_len = 0;
+ struct dma_buf_dma *dma;
+ struct scatterlist *sgl;
+ dma_addr_t addr;
+ size_t i;
+ int ret;
+
+ dma_resv_assert_held(attach->dmabuf->resv);
+
+ if (WARN_ON(!attach || !attach->dmabuf || !provider))
+ /* This function is supposed to work on MMIO memory only */
+ return ERR_PTR(-EINVAL);
+
+ dma = kzalloc(sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return ERR_PTR(-ENOMEM);
+
+ switch (pci_p2pdma_map_type(provider, attach->dev)) {
+ case PCI_P2PDMA_MAP_BUS_ADDR:
+ /*
+ * There is no need in IOVA at all for this flow.
+ */
+ break;
+ case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
+ dma->state = kzalloc(sizeof(*dma->state), GFP_KERNEL);
+ if (!dma->state) {
+ ret = -ENOMEM;
+ goto err_free_dma;
+ }
+
+ dma_iova_try_alloc(attach->dev, dma->state, 0, size);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_free_dma;
+ }
+
+ nents = calc_sg_nents(dma->state, phys_vec, nr_ranges, size);
+ ret = sg_alloc_table(&dma->sgt, nents, GFP_KERNEL | __GFP_ZERO);
+ if (ret)
+ goto err_free_state;
+
+ sgl = dma->sgt.sgl;
+
+ for (i = 0; i < nr_ranges; i++) {
+ if (!dma->state) {
+ addr = pci_p2pdma_bus_addr_map(provider,
+ phys_vec[i].paddr);
+ } else if (dma_use_iova(dma->state)) {
+ ret = dma_iova_link(attach->dev, dma->state,
+ phys_vec[i].paddr, 0,
+ phys_vec[i].len, dir,
+ DMA_ATTR_MMIO);
+ if (ret)
+ goto err_unmap_dma;
+
+ mapped_len += phys_vec[i].len;
+ } else {
+ addr = dma_map_phys(attach->dev, phys_vec[i].paddr,
+ phys_vec[i].len, dir,
+ DMA_ATTR_MMIO);
+ ret = dma_mapping_error(attach->dev, addr);
+ if (ret)
+ goto err_unmap_dma;
+ }
+
+ if (!dma->state || !dma_use_iova(dma->state))
+ sgl = fill_sg_entry(sgl, phys_vec[i].len, addr);
+ }
+
+ if (dma->state && dma_use_iova(dma->state)) {
+ WARN_ON_ONCE(mapped_len != size);
+ ret = dma_iova_sync(attach->dev, dma->state, 0, mapped_len);
+ if (ret)
+ goto err_unmap_dma;
+
+ sgl = fill_sg_entry(sgl, mapped_len, dma->state->addr);
+ }
+
+ dma->size = size;
+
+ /*
+ * No CPU list included — set orig_nents = 0 so others can detect
+ * this via SG table (use nents only).
+ */
+ dma->sgt.orig_nents = 0;
+
+
+ /*
+ * SGL must be NULL to indicate that SGL is the last one
+ * and we allocated correct number of entries in sg_alloc_table()
+ */
+ WARN_ON_ONCE(sgl);
+ return &dma->sgt;
+
+err_unmap_dma:
+ if (!i || !dma->state) {
+ ; /* Do nothing */
+ } else if (dma_use_iova(dma->state)) {
+ dma_iova_destroy(attach->dev, dma->state, mapped_len, dir,
+ DMA_ATTR_MMIO);
+ } else {
+ for_each_sgtable_dma_sg(&dma->sgt, sgl, i)
+ dma_unmap_phys(attach->dev, sg_dma_address(sgl),
+ sg_dma_len(sgl), dir, DMA_ATTR_MMIO);
+ }
+ sg_free_table(&dma->sgt);
+err_free_state:
+ kfree(dma->state);
+err_free_dma:
+ kfree(dma);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_NS_GPL(dma_buf_phys_vec_to_sgt, "DMA_BUF");
+
+/**
+ * dma_buf_free_sgt- unmaps the buffer
+ * @attach: [in] attachment to unmap buffer from
+ * @sgt: [in] scatterlist info of the buffer to unmap
+ * @dir: [in] direction of DMA transfer
+ *
+ * This unmaps a DMA mapping for @attached obtained
+ * by dma_buf_phys_vec_to_sgt().
+ */
+void dma_buf_free_sgt(struct dma_buf_attachment *attach, struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+ struct dma_buf_dma *dma = container_of(sgt, struct dma_buf_dma, sgt);
+ int i;
+
+ dma_resv_assert_held(attach->dmabuf->resv);
+
+ if (!dma->state) {
+ ; /* Do nothing */
+ } else if (dma_use_iova(dma->state)) {
+ dma_iova_destroy(attach->dev, dma->state, dma->size, dir,
+ DMA_ATTR_MMIO);
+ } else {
+ struct scatterlist *sgl;
+
+ for_each_sgtable_dma_sg(sgt, sgl, i)
+ dma_unmap_phys(attach->dev, sg_dma_address(sgl),
+ sg_dma_len(sgl), dir, DMA_ATTR_MMIO);
+ }
+
+ sg_free_table(sgt);
+ kfree(dma->state);
+ kfree(dma);
+
+}
+EXPORT_SYMBOL_NS_GPL(dma_buf_free_sgt, "DMA_BUF");
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 2bcf9ceca997..edaa9e4ee4ae 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -768,18 +768,10 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_export, "DMA_BUF");
*/
int dma_buf_fd(struct dma_buf *dmabuf, int flags)
{
- int fd;
-
if (!dmabuf || !dmabuf->file)
return -EINVAL;
- fd = get_unused_fd_flags(flags);
- if (fd < 0)
- return fd;
-
- fd_install(fd, dmabuf->file);
-
- return fd;
+ return FD_ADD(flags, dmabuf->file);
}
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index 3f78c56b58dc..b4f5c8635276 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -121,29 +121,27 @@ static const struct dma_fence_ops dma_fence_stub_ops = {
.get_timeline_name = dma_fence_stub_get_name,
};
+static int __init dma_fence_init_stub(void)
+{
+ dma_fence_init(&dma_fence_stub, &dma_fence_stub_ops,
+ &dma_fence_stub_lock, 0, 0);
+
+ set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
+ &dma_fence_stub.flags);
+
+ dma_fence_signal(&dma_fence_stub);
+ return 0;
+}
+subsys_initcall(dma_fence_init_stub);
+
/**
* dma_fence_get_stub - return a signaled fence
*
- * Return a stub fence which is already signaled. The fence's
- * timestamp corresponds to the first time after boot this
- * function is called.
+ * Return a stub fence which is already signaled. The fence's timestamp
+ * corresponds to the initialisation time of the linux kernel.
*/
struct dma_fence *dma_fence_get_stub(void)
{
- spin_lock(&dma_fence_stub_lock);
- if (!dma_fence_stub.ops) {
- dma_fence_init(&dma_fence_stub,
- &dma_fence_stub_ops,
- &dma_fence_stub_lock,
- 0, 0);
-
- set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
- &dma_fence_stub.flags);
-
- dma_fence_signal_locked(&dma_fence_stub);
- }
- spin_unlock(&dma_fence_stub_lock);
-
return dma_fence_get(&dma_fence_stub);
}
EXPORT_SYMBOL(dma_fence_get_stub);
@@ -999,19 +997,21 @@ EXPORT_SYMBOL(dma_fence_set_deadline);
*/
void dma_fence_describe(struct dma_fence *fence, struct seq_file *seq)
{
- const char __rcu *timeline;
- const char __rcu *driver;
+ const char __rcu *timeline = "";
+ const char __rcu *driver = "";
+ const char *signaled = "";
rcu_read_lock();
- timeline = dma_fence_timeline_name(fence);
- driver = dma_fence_driver_name(fence);
+ if (!dma_fence_is_signaled(fence)) {
+ timeline = dma_fence_timeline_name(fence);
+ driver = dma_fence_driver_name(fence);
+ signaled = "un";
+ }
- seq_printf(seq, "%s %s seq %llu %ssignalled\n",
- rcu_dereference(driver),
- rcu_dereference(timeline),
- fence->seqno,
- dma_fence_is_signaled(fence) ? "" : "un");
+ seq_printf(seq, "%llu:%llu %s %s %ssignalled\n",
+ fence->context, fence->seqno, timeline, driver,
+ signaled);
rcu_read_unlock();
}
@@ -1141,7 +1141,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence)
"RCU protection is required for safe access to returned string");
if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags))
- return fence->ops->get_driver_name(fence);
+ return fence->ops->get_timeline_name(fence);
else
return "signaled-timeline";
}
diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig
index bb369b38b001..a5eef06c4226 100644
--- a/drivers/dma-buf/heaps/Kconfig
+++ b/drivers/dma-buf/heaps/Kconfig
@@ -12,13 +12,3 @@ config DMABUF_HEAPS_CMA
Choose this option to enable dma-buf CMA heap. This heap is backed
by the Contiguous Memory Allocator (CMA). If your system has these
regions, you should say Y here.
-
-config DMABUF_HEAPS_CMA_LEGACY
- bool "Legacy DMA-BUF CMA Heap"
- default y
- depends on DMABUF_HEAPS_CMA
- help
- Add a duplicate CMA-backed dma-buf heap with legacy naming derived
- from the CMA area's devicetree node, or "reserved" if the area is not
- defined in the devicetree. This uses the same underlying allocator as
- CONFIG_DMABUF_HEAPS_CMA.
diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c
index 0df007111975..42f88193eab9 100644
--- a/drivers/dma-buf/heaps/cma_heap.c
+++ b/drivers/dma-buf/heaps/cma_heap.c
@@ -14,6 +14,7 @@
#include <linux/cma.h>
#include <linux/dma-buf.h>
+#include <linux/dma-buf/heaps/cma.h>
#include <linux/dma-heap.h>
#include <linux/dma-map-ops.h>
#include <linux/err.h>
@@ -21,12 +22,27 @@
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#define DEFAULT_CMA_NAME "default_cma_region"
+static struct cma *dma_areas[MAX_CMA_AREAS] __initdata;
+static unsigned int dma_areas_num __initdata;
+
+int __init dma_heap_cma_register_heap(struct cma *cma)
+{
+ if (dma_areas_num >= ARRAY_SIZE(dma_areas))
+ return -EINVAL;
+
+ dma_areas[dma_areas_num++] = cma;
+
+ return 0;
+}
+
struct cma_heap {
struct dma_heap *heap;
struct cma *cma;
@@ -395,33 +411,30 @@ static int __init __add_cma_heap(struct cma *cma, const char *name)
return 0;
}
-static int __init add_default_cma_heap(void)
+static int __init add_cma_heaps(void)
{
struct cma *default_cma = dev_get_cma_area(NULL);
- const char *legacy_cma_name;
+ unsigned int i;
int ret;
- if (!default_cma)
- return 0;
+ if (default_cma) {
+ ret = __add_cma_heap(default_cma, DEFAULT_CMA_NAME);
+ if (ret)
+ return ret;
+ }
- ret = __add_cma_heap(default_cma, DEFAULT_CMA_NAME);
- if (ret)
- return ret;
+ for (i = 0; i < dma_areas_num; i++) {
+ struct cma *cma = dma_areas[i];
- if (IS_ENABLED(CONFIG_DMABUF_HEAPS_CMA_LEGACY)) {
- legacy_cma_name = cma_get_name(default_cma);
- if (!strcmp(legacy_cma_name, DEFAULT_CMA_NAME)) {
- pr_warn("legacy name and default name are the same, skipping legacy heap\n");
- return 0;
+ ret = __add_cma_heap(cma, cma_get_name(cma));
+ if (ret) {
+ pr_warn("Failed to add CMA heap %s", cma_get_name(cma));
+ continue;
}
- ret = __add_cma_heap(default_cma, legacy_cma_name);
- if (ret)
- pr_warn("failed to add legacy heap: %pe\n",
- ERR_PTR(ret));
}
return 0;
}
-module_init(add_default_cma_heap);
+module_init(add_cma_heaps);
MODULE_DESCRIPTION("DMA-BUF CMA Heap");
diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index bbe7881f1360..4c782fe33fd4 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -186,20 +186,35 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
struct system_heap_buffer *buffer = dmabuf->priv;
struct sg_table *table = &buffer->sg_table;
unsigned long addr = vma->vm_start;
- struct sg_page_iter piter;
- int ret;
+ unsigned long pgoff = vma->vm_pgoff;
+ struct scatterlist *sg;
+ int i, ret;
+
+ for_each_sgtable_sg(table, sg, i) {
+ unsigned long n = sg->length >> PAGE_SHIFT;
- for_each_sgtable_page(table, &piter, vma->vm_pgoff) {
- struct page *page = sg_page_iter_page(&piter);
+ if (pgoff < n)
+ break;
+ pgoff -= n;
+ }
+
+ for (; sg && addr < vma->vm_end; sg = sg_next(sg)) {
+ unsigned long n = (sg->length >> PAGE_SHIFT) - pgoff;
+ struct page *page = sg_page(sg) + pgoff;
+ unsigned long size = n << PAGE_SHIFT;
+
+ if (addr + size > vma->vm_end)
+ size = vma->vm_end - addr;
- ret = remap_pfn_range(vma, addr, page_to_pfn(page), PAGE_SIZE,
- vma->vm_page_prot);
+ ret = remap_pfn_range(vma, addr, page_to_pfn(page),
+ size, vma->vm_page_prot);
if (ret)
return ret;
- addr += PAGE_SIZE;
- if (addr >= vma->vm_end)
- return 0;
+
+ addr += size;
+ pgoff = 0;
}
+
return 0;
}
diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c
index 3c20f1d31cf5..6f09d13be6b6 100644
--- a/drivers/dma-buf/sw_sync.c
+++ b/drivers/dma-buf/sw_sync.c
@@ -8,6 +8,7 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
+#include <linux/panic.h>
#include <linux/slab.h>
#include <linux/sync_file.h>
@@ -349,6 +350,9 @@ static long sw_sync_ioctl_create_fence(struct sync_timeline *obj,
struct sync_file *sync_file;
struct sw_sync_create_fence_data data;
+ /* SW sync fence are inherently unsafe and can deadlock the kernel */
+ add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK);
+
if (fd < 0)
return fd;
diff --git a/drivers/dma-buf/sync_debug.c b/drivers/dma-buf/sync_debug.c
index 67cd69551e42..9e5d662cd4e8 100644
--- a/drivers/dma-buf/sync_debug.c
+++ b/drivers/dma-buf/sync_debug.c
@@ -59,7 +59,7 @@ static void sync_print_fence(struct seq_file *s,
struct timespec64 ts64 =
ktime_to_timespec64(fence->timestamp);
- seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
+ seq_printf(s, "@%ptSp", &ts64);
}
seq_printf(s, ": %lld", fence->seqno);
diff --git a/drivers/dma/ioat/init.c b/drivers/dma/ioat/init.c
index 02f68b328511..227398673b73 100644
--- a/drivers/dma/ioat/init.c
+++ b/drivers/dma/ioat/init.c
@@ -1286,7 +1286,6 @@ static pci_ers_result_t ioat_pcie_error_slot_reset(struct pci_dev *pdev)
} else {
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
pci_wake_from_d3(pdev, false);
}
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
index 74c1f0ca95f2..64944f601ee5 100644
--- a/drivers/dpll/dpll_netlink.c
+++ b/drivers/dpll/dpll_netlink.c
@@ -637,6 +637,10 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
ret = dpll_msg_add_pin_freq(msg, pin, ref, extack);
if (ret)
return ret;
+ if (prop->phase_gran &&
+ nla_put_u32(msg, DPLL_A_PIN_PHASE_ADJUST_GRAN,
+ prop->phase_gran))
+ return -EMSGSIZE;
if (nla_put_s32(msg, DPLL_A_PIN_PHASE_ADJUST_MIN,
prop->phase_range.min))
return -EMSGSIZE;
@@ -1261,7 +1265,13 @@ dpll_pin_phase_adj_set(struct dpll_pin *pin, struct nlattr *phase_adj_attr,
if (phase_adj > pin->prop.phase_range.max ||
phase_adj < pin->prop.phase_range.min) {
NL_SET_ERR_MSG_ATTR(extack, phase_adj_attr,
- "phase adjust value not supported");
+ "phase adjust value of out range");
+ return -EINVAL;
+ }
+ if (pin->prop.phase_gran && phase_adj % (s32)pin->prop.phase_gran) {
+ NL_SET_ERR_MSG_ATTR_FMT(extack, phase_adj_attr,
+ "phase adjust value not multiple of %u",
+ pin->prop.phase_gran);
return -EINVAL;
}
@@ -1559,16 +1569,18 @@ int dpll_nl_pin_id_get_doit(struct sk_buff *skb, struct genl_info *info)
return -EMSGSIZE;
}
pin = dpll_pin_find_from_nlattr(info);
- if (!IS_ERR(pin)) {
- if (!dpll_pin_available(pin)) {
- nlmsg_free(msg);
- return -ENODEV;
- }
- ret = dpll_msg_add_pin_handle(msg, pin);
- if (ret) {
- nlmsg_free(msg);
- return ret;
- }
+ if (IS_ERR(pin)) {
+ nlmsg_free(msg);
+ return PTR_ERR(pin);
+ }
+ if (!dpll_pin_available(pin)) {
+ nlmsg_free(msg);
+ return -ENODEV;
+ }
+ ret = dpll_msg_add_pin_handle(msg, pin);
+ if (ret) {
+ nlmsg_free(msg);
+ return ret;
}
genlmsg_end(msg, hdr);
@@ -1735,12 +1747,14 @@ int dpll_nl_device_id_get_doit(struct sk_buff *skb, struct genl_info *info)
}
dpll = dpll_device_find_from_nlattr(info);
- if (!IS_ERR(dpll)) {
- ret = dpll_msg_add_dev_handle(msg, dpll);
- if (ret) {
- nlmsg_free(msg);
- return ret;
- }
+ if (IS_ERR(dpll)) {
+ nlmsg_free(msg);
+ return PTR_ERR(dpll);
+ }
+ ret = dpll_msg_add_dev_handle(msg, dpll);
+ if (ret) {
+ nlmsg_free(msg);
+ return ret;
}
genlmsg_end(msg, hdr);
diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c
index 3c6d570babf8..36d11ff195df 100644
--- a/drivers/dpll/dpll_nl.c
+++ b/drivers/dpll/dpll_nl.c
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/dpll.yaml */
/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#include <net/netlink.h>
#include <net/genetlink.h>
diff --git a/drivers/dpll/dpll_nl.h b/drivers/dpll/dpll_nl.h
index 3da10cfe9a6e..7419679b6977 100644
--- a/drivers/dpll/dpll_nl.h
+++ b/drivers/dpll/dpll_nl.h
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/dpll.yaml */
/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_DPLL_GEN_H
#define _LINUX_DPLL_GEN_H
diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile
index 84e22aae57e5..bd324c7fe710 100644
--- a/drivers/dpll/zl3073x/Makefile
+++ b/drivers/dpll/zl3073x/Makefile
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ZL3073X) += zl3073x.o
-zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o prop.o
+zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o \
+ out.o prop.o ref.o synth.o
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
zl3073x_i2c-objs := i2c.o
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
index e42e527813cf..383e2397dd03 100644
--- a/drivers/dpll/zl3073x/core.c
+++ b/drivers/dpll/zl3073x/core.c
@@ -129,47 +129,6 @@ const struct regmap_config zl3073x_regmap_config = {
};
EXPORT_SYMBOL_NS_GPL(zl3073x_regmap_config, "ZL3073X");
-/**
- * zl3073x_ref_freq_factorize - factorize given frequency
- * @freq: input frequency
- * @base: base frequency
- * @mult: multiplier
- *
- * Checks if the given frequency can be factorized using one of the
- * supported base frequencies. If so the base frequency and multiplier
- * are stored into appropriate parameters if they are not NULL.
- *
- * Return: 0 on success, -EINVAL if the frequency cannot be factorized
- */
-int
-zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
-{
- static const u16 base_freqs[] = {
- 1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125,
- 128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000,
- 1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250,
- 6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250,
- 32000, 40000, 50000, 62500,
- };
- u32 div;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(base_freqs); i++) {
- div = freq / base_freqs[i];
-
- if (div <= U16_MAX && (freq % base_freqs[i]) == 0) {
- if (base)
- *base = base_freqs[i];
- if (mult)
- *mult = div;
-
- return 0;
- }
- }
-
- return -EINVAL;
-}
-
static bool
zl3073x_check_reg(struct zl3073x_dev *zldev, unsigned int reg, size_t size)
{
@@ -593,190 +552,6 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
return rc;
}
-/**
- * zl3073x_ref_state_fetch - get input reference state
- * @zldev: pointer to zl3073x_dev structure
- * @index: input reference index to fetch state for
- *
- * Function fetches information for the given input reference that are
- * invariant and stores them for later use.
- *
- * Return: 0 on success, <0 on error
- */
-static int
-zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
-{
- struct zl3073x_ref *input = &zldev->ref[index];
- u8 ref_config;
- int rc;
-
- /* If the input is differential then the configuration for N-pin
- * reference is ignored and P-pin config is used for both.
- */
- if (zl3073x_is_n_pin(index) &&
- zl3073x_ref_is_diff(zldev, index - 1)) {
- input->enabled = zl3073x_ref_is_enabled(zldev, index - 1);
- input->diff = true;
-
- return 0;
- }
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read reference configuration */
- rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
- ZL_REG_REF_MB_MASK, BIT(index));
- if (rc)
- return rc;
-
- /* Read ref_config register */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref_config);
- if (rc)
- return rc;
-
- input->enabled = FIELD_GET(ZL_REF_CONFIG_ENABLE, ref_config);
- input->diff = FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref_config);
-
- dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
- str_enabled_disabled(input->enabled),
- input->diff ? "differential" : "single-ended");
-
- return rc;
-}
-
-/**
- * zl3073x_out_state_fetch - get output state
- * @zldev: pointer to zl3073x_dev structure
- * @index: output index to fetch state for
- *
- * Function fetches information for the given output (not output pin)
- * that are invariant and stores them for later use.
- *
- * Return: 0 on success, <0 on error
- */
-static int
-zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
-{
- struct zl3073x_out *out = &zldev->out[index];
- u8 output_ctrl, output_mode;
- int rc;
-
- /* Read output configuration */
- rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &output_ctrl);
- if (rc)
- return rc;
-
- /* Store info about output enablement and synthesizer the output
- * is connected to.
- */
- out->enabled = FIELD_GET(ZL_OUTPUT_CTRL_EN, output_ctrl);
- out->synth = FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, output_ctrl);
-
- dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
- str_enabled_disabled(out->enabled), out->synth);
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read output configuration */
- rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
- ZL_REG_OUTPUT_MB_MASK, BIT(index));
- if (rc)
- return rc;
-
- /* Read output_mode */
- rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
- if (rc)
- return rc;
-
- /* Extract and store output signal format */
- out->signal_format = FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT,
- output_mode);
-
- dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
- out->signal_format);
-
- return rc;
-}
-
-/**
- * zl3073x_synth_state_fetch - get synth state
- * @zldev: pointer to zl3073x_dev structure
- * @index: synth index to fetch state for
- *
- * Function fetches information for the given synthesizer that are
- * invariant and stores them for later use.
- *
- * Return: 0 on success, <0 on error
- */
-static int
-zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
-{
- struct zl3073x_synth *synth = &zldev->synth[index];
- u16 base, m, n;
- u8 synth_ctrl;
- u32 mult;
- int rc;
-
- /* Read synth control register */
- rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth_ctrl);
- if (rc)
- return rc;
-
- /* Store info about synth enablement and DPLL channel the synth is
- * driven by.
- */
- synth->enabled = FIELD_GET(ZL_SYNTH_CTRL_EN, synth_ctrl);
- synth->dpll = FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth_ctrl);
-
- dev_dbg(zldev->dev, "SYNTH%u is %s and driven by DPLL%u\n", index,
- str_enabled_disabled(synth->enabled), synth->dpll);
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read synth configuration */
- rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
- ZL_REG_SYNTH_MB_MASK, BIT(index));
- if (rc)
- return rc;
-
- /* The output frequency is determined by the following formula:
- * base * multiplier * numerator / denominator
- *
- * Read registers with these values
- */
- rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &base);
- if (rc)
- return rc;
-
- rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &mult);
- if (rc)
- return rc;
-
- rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &m);
- if (rc)
- return rc;
-
- rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &n);
- if (rc)
- return rc;
-
- /* Check denominator for zero to avoid div by 0 */
- if (!n) {
- dev_err(zldev->dev,
- "Zero divisor for SYNTH%u retrieved from device\n",
- index);
- return -EINVAL;
- }
-
- /* Compute and store synth frequency */
- zldev->synth[index].freq = div_u64(mul_u32_u32(base * m, mult), n);
-
- dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
- zldev->synth[index].freq);
-
- return rc;
-}
-
static int
zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
{
@@ -816,6 +591,21 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
return rc;
}
+static void
+zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
+{
+ int i, rc;
+
+ for (i = 0; i < ZL3073X_NUM_REFS; i++) {
+ rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
+ &zldev->ref[i].mon_status);
+ if (rc)
+ dev_warn(zldev->dev,
+ "Failed to get REF%u status: %pe\n", i,
+ ERR_PTR(rc));
+ }
+}
+
/**
* zl3073x_ref_phase_offsets_update - update reference phase offsets
* @zldev: pointer to zl3073x_dev structure
@@ -935,6 +725,9 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
struct zl3073x_dpll *zldpll;
int rc;
+ /* Update input references status */
+ zl3073x_dev_ref_status_update(zldev);
+
/* Update DPLL-to-connected-ref phase offsets registers */
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
if (rc)
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
index 1dca4ddcf235..09bca2d0926d 100644
--- a/drivers/dpll/zl3073x/core.h
+++ b/drivers/dpll/zl3073x/core.h
@@ -9,7 +9,10 @@
#include <linux/mutex.h>
#include <linux/types.h>
+#include "out.h"
+#include "ref.h"
#include "regs.h"
+#include "synth.h"
struct device;
struct regmap;
@@ -28,42 +31,6 @@ struct zl3073x_dpll;
ZL3073X_NUM_OUTPUT_PINS)
/**
- * struct zl3073x_ref - input reference invariant info
- * @enabled: input reference is enabled or disabled
- * @diff: true if input reference is differential
- * @ffo: current fractional frequency offset
- */
-struct zl3073x_ref {
- bool enabled;
- bool diff;
- s64 ffo;
-};
-
-/**
- * struct zl3073x_out - output invariant info
- * @enabled: out is enabled or disabled
- * @synth: synthesizer the out is connected to
- * @signal_format: out signal format
- */
-struct zl3073x_out {
- bool enabled;
- u8 synth;
- u8 signal_format;
-};
-
-/**
- * struct zl3073x_synth - synthesizer invariant info
- * @freq: synthesizer frequency
- * @dpll: ID of DPLL the synthesizer is driven by
- * @enabled: synth is enabled or disabled
- */
-struct zl3073x_synth {
- u32 freq;
- u8 dpll;
- bool enabled;
-};
-
-/**
* struct zl3073x_dev - zl3073x device
* @dev: pointer to device
* @regmap: regmap to access device registers
@@ -175,7 +142,6 @@ int zl3073x_write_hwreg_seq(struct zl3073x_dev *zldev,
* Misc operations
*****************/
-int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
static inline bool
@@ -217,172 +183,141 @@ zl3073x_output_pin_out_get(u8 id)
}
/**
- * zl3073x_ref_ffo_get - get current fractional frequency offset
+ * zl3073x_dev_ref_freq_get - get input reference frequency
* @zldev: pointer to zl3073x device
* @index: input reference index
*
- * Return: the latest measured fractional frequency offset
+ * Return: frequency of given input reference
*/
-static inline s64
-zl3073x_ref_ffo_get(struct zl3073x_dev *zldev, u8 index)
+static inline u32
+zl3073x_dev_ref_freq_get(struct zl3073x_dev *zldev, u8 index)
{
- return zldev->ref[index].ffo;
+ const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
+
+ return zl3073x_ref_freq_get(ref);
}
/**
- * zl3073x_ref_is_diff - check if the given input reference is differential
+ * zl3073x_dev_ref_is_diff - check if the given input reference is differential
* @zldev: pointer to zl3073x device
* @index: input reference index
*
* Return: true if reference is differential, false if reference is single-ended
*/
static inline bool
-zl3073x_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
+zl3073x_dev_ref_is_diff(struct zl3073x_dev *zldev, u8 index)
{
- return zldev->ref[index].diff;
+ const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
+
+ return zl3073x_ref_is_diff(ref);
}
-/**
- * zl3073x_ref_is_enabled - check if the given input reference is enabled
+/*
+ * zl3073x_dev_ref_is_status_ok - check the given input reference status
* @zldev: pointer to zl3073x device
* @index: input reference index
*
- * Return: true if input refernce is enabled, false otherwise
+ * Return: true if the status is ok, false otherwise
*/
static inline bool
-zl3073x_ref_is_enabled(struct zl3073x_dev *zldev, u8 index)
+zl3073x_dev_ref_is_status_ok(struct zl3073x_dev *zldev, u8 index)
{
- return zldev->ref[index].enabled;
-}
+ const struct zl3073x_ref *ref = zl3073x_ref_state_get(zldev, index);
-/**
- * zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
- * @zldev: pointer to zl3073x device
- * @index: synth index
- *
- * Return: ID of DPLL the given synthetizer is driven by
- */
-static inline u8
-zl3073x_synth_dpll_get(struct zl3073x_dev *zldev, u8 index)
-{
- return zldev->synth[index].dpll;
+ return zl3073x_ref_is_status_ok(ref);
}
/**
- * zl3073x_synth_freq_get - get synth current freq
+ * zl3073x_dev_synth_freq_get - get synth current freq
* @zldev: pointer to zl3073x device
* @index: synth index
*
* Return: frequency of given synthetizer
*/
static inline u32
-zl3073x_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
+zl3073x_dev_synth_freq_get(struct zl3073x_dev *zldev, u8 index)
{
- return zldev->synth[index].freq;
-}
+ const struct zl3073x_synth *synth;
-/**
- * zl3073x_synth_is_enabled - check if the given synth is enabled
- * @zldev: pointer to zl3073x device
- * @index: synth index
- *
- * Return: true if synth is enabled, false otherwise
- */
-static inline bool
-zl3073x_synth_is_enabled(struct zl3073x_dev *zldev, u8 index)
-{
- return zldev->synth[index].enabled;
+ synth = zl3073x_synth_state_get(zldev, index);
+ return zl3073x_synth_freq_get(synth);
}
/**
- * zl3073x_out_synth_get - get synth connected to given output
+ * zl3073x_dev_out_synth_get - get synth connected to given output
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: index of synth connected to given output.
*/
static inline u8
-zl3073x_out_synth_get(struct zl3073x_dev *zldev, u8 index)
+zl3073x_dev_out_synth_get(struct zl3073x_dev *zldev, u8 index)
{
- return zldev->out[index].synth;
+ const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
+
+ return zl3073x_out_synth_get(out);
}
/**
- * zl3073x_out_is_enabled - check if the given output is enabled
+ * zl3073x_dev_out_is_enabled - check if the given output is enabled
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: true if the output is enabled, false otherwise
*/
static inline bool
-zl3073x_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
+zl3073x_dev_out_is_enabled(struct zl3073x_dev *zldev, u8 index)
{
- u8 synth;
+ const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
+ const struct zl3073x_synth *synth;
+ u8 synth_id;
/* Output is enabled only if associated synth is enabled */
- synth = zl3073x_out_synth_get(zldev, index);
- if (zl3073x_synth_is_enabled(zldev, synth))
- return zldev->out[index].enabled;
+ synth_id = zl3073x_out_synth_get(out);
+ synth = zl3073x_synth_state_get(zldev, synth_id);
- return false;
+ return zl3073x_synth_is_enabled(synth) && zl3073x_out_is_enabled(out);
}
/**
- * zl3073x_out_signal_format_get - get output signal format
- * @zldev: pointer to zl3073x device
- * @index: output index
- *
- * Return: signal format of given output
- */
-static inline u8
-zl3073x_out_signal_format_get(struct zl3073x_dev *zldev, u8 index)
-{
- return zldev->out[index].signal_format;
-}
-
-/**
- * zl3073x_out_dpll_get - get DPLL ID the output is driven by
+ * zl3073x_dev_out_dpll_get - get DPLL ID the output is driven by
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: ID of DPLL the given output is driven by
*/
static inline
-u8 zl3073x_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
+u8 zl3073x_dev_out_dpll_get(struct zl3073x_dev *zldev, u8 index)
{
- u8 synth;
+ const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
+ const struct zl3073x_synth *synth;
+ u8 synth_id;
/* Get synthesizer connected to given output */
- synth = zl3073x_out_synth_get(zldev, index);
+ synth_id = zl3073x_out_synth_get(out);
+ synth = zl3073x_synth_state_get(zldev, synth_id);
/* Return DPLL that drives the synth */
- return zl3073x_synth_dpll_get(zldev, synth);
+ return zl3073x_synth_dpll_get(synth);
}
/**
- * zl3073x_out_is_diff - check if the given output is differential
+ * zl3073x_dev_out_is_diff - check if the given output is differential
* @zldev: pointer to zl3073x device
* @index: output index
*
* Return: true if output is differential, false if output is single-ended
*/
static inline bool
-zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
+zl3073x_dev_out_is_diff(struct zl3073x_dev *zldev, u8 index)
{
- switch (zl3073x_out_signal_format_get(zldev, index)) {
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF:
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
- return true;
- default:
- break;
- }
+ const struct zl3073x_out *out = zl3073x_out_state_get(zldev, index);
- return false;
+ return zl3073x_out_is_diff(out);
}
/**
- * zl3073x_output_pin_is_enabled - check if the given output pin is enabled
+ * zl3073x_dev_output_pin_is_enabled - check if the given output pin is enabled
* @zldev: pointer to zl3073x device
* @id: output pin id
*
@@ -392,16 +327,21 @@ zl3073x_out_is_diff(struct zl3073x_dev *zldev, u8 index)
* Return: true if output pin is enabled, false if output pin is disabled
*/
static inline bool
-zl3073x_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
+zl3073x_dev_output_pin_is_enabled(struct zl3073x_dev *zldev, u8 id)
{
- u8 output = zl3073x_output_pin_out_get(id);
+ u8 out_id = zl3073x_output_pin_out_get(id);
+ const struct zl3073x_out *out;
+
+ out = zl3073x_out_state_get(zldev, out_id);
- /* Check if the whole output is enabled */
- if (!zl3073x_out_is_enabled(zldev, output))
+ /* Check if the output is enabled - call _dev_ helper that
+ * additionally checks for attached synth enablement.
+ */
+ if (!zl3073x_dev_out_is_enabled(zldev, out_id))
return false;
/* Check signal format */
- switch (zl3073x_out_signal_format_get(zldev, output)) {
+ switch (zl3073x_out_signal_format_get(out)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DISABLED:
/* Both output pins are disabled by signal format */
return false;
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
index 93dc93eec79e..9879d85d29af 100644
--- a/drivers/dpll/zl3073x/dpll.c
+++ b/drivers/dpll/zl3073x/dpll.c
@@ -35,6 +35,7 @@
* @prio: pin priority <0, 14>
* @selectable: pin is selectable in automatic mode
* @esync_control: embedded sync is controllable
+ * @phase_gran: phase adjustment granularity
* @pin_state: last saved pin state
* @phase_offset: last saved pin phase offset
* @freq_offset: last saved fractional frequency offset
@@ -49,6 +50,7 @@ struct zl3073x_dpll_pin {
u8 prio;
bool selectable;
bool esync_control;
+ s32 phase_gran;
enum dpll_pin_state pin_state;
s64 phase_offset;
s64 freq_offset;
@@ -98,60 +100,6 @@ zl3073x_dpll_pin_direction_get(const struct dpll_pin *dpll_pin, void *pin_priv,
return 0;
}
-/**
- * zl3073x_dpll_input_ref_frequency_get - get input reference frequency
- * @zldpll: pointer to zl3073x_dpll
- * @ref_id: reference id
- * @frequency: pointer to variable to store frequency
- *
- * Reads frequency of given input reference.
- *
- * Return: 0 on success, <0 on error
- */
-static int
-zl3073x_dpll_input_ref_frequency_get(struct zl3073x_dpll *zldpll, u8 ref_id,
- u32 *frequency)
-{
- struct zl3073x_dev *zldev = zldpll->dev;
- u16 base, mult, num, denom;
- int rc;
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read reference configuration */
- rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
- ZL_REG_REF_MB_MASK, BIT(ref_id));
- if (rc)
- return rc;
-
- /* Read registers to compute resulting frequency */
- rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &base);
- if (rc)
- return rc;
- rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &mult);
- if (rc)
- return rc;
- rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &num);
- if (rc)
- return rc;
- rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &denom);
- if (rc)
- return rc;
-
- /* Sanity check that HW has not returned zero denominator */
- if (!denom) {
- dev_err(zldev->dev,
- "Zero divisor for ref %u frequency got from device\n",
- ref_id);
- return -EINVAL;
- }
-
- /* Compute the frequency */
- *frequency = mul_u64_u32_div(base * mult, num, denom);
-
- return rc;
-}
-
static int
zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
void *pin_priv,
@@ -163,39 +111,15 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- u8 ref, ref_sync_ctrl, sync_mode;
- u32 esync_div, ref_freq;
- int rc;
+ const struct zl3073x_ref *ref;
+ u8 ref_id;
- /* Get reference frequency */
- ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_dpll_input_ref_frequency_get(zldpll, pin->id, &ref_freq);
- if (rc)
- return rc;
-
- guard(mutex)(&zldev->multiop_lock);
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = zl3073x_ref_state_get(zldev, ref_id);
- /* Read reference configuration into mailbox */
- rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
- ZL_REG_REF_MB_MASK, BIT(ref));
- if (rc)
- return rc;
-
- /* Get ref sync mode */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl);
- if (rc)
- return rc;
-
- /* Get esync divisor */
- rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &esync_div);
- if (rc)
- return rc;
-
- sync_mode = FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref_sync_ctrl);
-
- switch (sync_mode) {
+ switch (FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl)) {
case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75:
- esync->freq = (esync_div == ZL_REF_ESYNC_DIV_1HZ) ? 1 : 0;
+ esync->freq = ref->esync_n_div == ZL_REF_ESYNC_DIV_1HZ ? 1 : 0;
esync->pulse = 25;
break;
default:
@@ -207,7 +131,7 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
/* If the pin supports esync control expose its range but only
* if the current reference frequency is > 1 Hz.
*/
- if (pin->esync_control && ref_freq > 1) {
+ if (pin->esync_control && zl3073x_ref_freq_get(ref) > 1) {
esync->range = esync_freq_ranges;
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
} else {
@@ -215,7 +139,7 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
esync->range_num = 0;
}
- return rc;
+ return 0;
}
static int
@@ -228,22 +152,11 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- u8 ref, ref_sync_ctrl, sync_mode;
- int rc;
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read reference configuration into mailbox */
- ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
- ZL_REG_REF_MB_MASK, BIT(ref));
- if (rc)
- return rc;
+ struct zl3073x_ref ref;
+ u8 ref_id, sync_mode;
- /* Get ref sync mode */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref_sync_ctrl);
- if (rc)
- return rc;
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = *zl3073x_ref_state_get(zldev, ref_id);
/* Use freq == 0 to disable esync */
if (!freq)
@@ -251,25 +164,16 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
else
sync_mode = ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75;
- ref_sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE;
- ref_sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode);
-
- /* Update ref sync control register */
- rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL, ref_sync_ctrl);
- if (rc)
- return rc;
+ ref.sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE;
+ ref.sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode);
if (freq) {
- /* 1 Hz is only supported frequnecy currently */
- rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
- ZL_REF_ESYNC_DIV_1HZ);
- if (rc)
- return rc;
+ /* 1 Hz is only supported frequency now */
+ ref.esync_n_div = ZL_REF_ESYNC_DIV_1HZ;
}
- /* Commit reference configuration */
- return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
- ZL_REG_REF_MB_MASK, BIT(ref));
+ /* Update reference configuration */
+ return zl3073x_ref_state_set(zldev, ref_id, &ref);
}
static int
@@ -293,17 +197,12 @@ zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
{
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dpll_pin *pin = pin_priv;
- u32 ref_freq;
- u8 ref;
- int rc;
+ u8 ref_id;
- /* Read and return ref frequency */
- ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref, &ref_freq);
- if (!rc)
- *frequency = ref_freq;
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ *frequency = zl3073x_dev_ref_freq_get(zldpll->dev, ref_id);
- return rc;
+ return 0;
}
static int
@@ -316,39 +215,18 @@ zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- u16 base, mult;
- u8 ref;
- int rc;
+ struct zl3073x_ref ref;
+ u8 ref_id;
- /* Get base frequency and multiplier for the requested frequency */
- rc = zl3073x_ref_freq_factorize(frequency, &base, &mult);
- if (rc)
- return rc;
+ /* Get reference state */
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = *zl3073x_ref_state_get(zldev, ref_id);
- guard(mutex)(&zldev->multiop_lock);
-
- /* Load reference configuration */
- ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
- ZL_REG_REF_MB_MASK, BIT(ref));
-
- /* Update base frequency, multiplier, numerator & denominator */
- rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE, base);
- if (rc)
- return rc;
- rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT, mult);
- if (rc)
- return rc;
- rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M, 1);
- if (rc)
- return rc;
- rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N, 1);
- if (rc)
- return rc;
+ /* Update frequency */
+ zl3073x_ref_freq_set(&ref, frequency);
- /* Commit reference configuration */
- return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
- ZL_REG_REF_MB_MASK, BIT(ref));
+ /* Commit reference state */
+ return zl3073x_ref_state_set(zldev, ref_id, &ref);
}
/**
@@ -495,19 +373,10 @@ zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
if (rc)
return rc;
- if (ZL3073X_DPLL_REF_IS_VALID(*ref)) {
- u8 ref_status;
-
- /* Read the reference monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(*ref),
- &ref_status);
- if (rc)
- return rc;
-
- /* If the monitor indicates an error nothing is connected */
- if (ref_status != ZL_REF_MON_STATUS_OK)
- *ref = ZL3073X_DPLL_REF_NONE;
- }
+ /* If the monitor indicates an error nothing is connected */
+ if (ZL3073X_DPLL_REF_IS_VALID(*ref) &&
+ !zl3073x_dev_ref_is_status_ok(zldev, *ref))
+ *ref = ZL3073X_DPLL_REF_NONE;
return 0;
}
@@ -522,34 +391,25 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- u8 conn_ref, ref, ref_status;
+ const struct zl3073x_ref *ref;
+ u8 conn_id, ref_id;
s64 ref_phase;
int rc;
/* Get currently connected reference */
- rc = zl3073x_dpll_connected_ref_get(zldpll, &conn_ref);
+ rc = zl3073x_dpll_connected_ref_get(zldpll, &conn_id);
if (rc)
return rc;
/* Report phase offset only for currently connected pin if the phase
- * monitor feature is disabled.
+ * monitor feature is disabled and only if the input pin signal is
+ * present.
*/
- ref = zl3073x_input_pin_ref_get(pin->id);
- if (!zldpll->phase_monitor && ref != conn_ref) {
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = zl3073x_ref_state_get(zldev, ref_id);
+ if ((!zldpll->phase_monitor && ref_id != conn_id) ||
+ !zl3073x_ref_is_status_ok(ref)) {
*phase_offset = 0;
-
- return 0;
- }
-
- /* Get this pin monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &ref_status);
- if (rc)
- return rc;
-
- /* Report phase offset only if the input pin signal is present */
- if (ref_status != ZL_REF_MON_STATUS_OK) {
- *phase_offset = 0;
-
return 0;
}
@@ -559,20 +419,12 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
* the phase offset is modded to the period of the signal
* the dpll is locked to.
*/
- if (ZL3073X_DPLL_REF_IS_VALID(conn_ref) && conn_ref != ref) {
+ if (ZL3073X_DPLL_REF_IS_VALID(conn_id) && conn_id != ref_id) {
u32 conn_freq, ref_freq;
- /* Get frequency of connected ref */
- rc = zl3073x_dpll_input_ref_frequency_get(zldpll, conn_ref,
- &conn_freq);
- if (rc)
- return rc;
-
- /* Get frequency of given ref */
- rc = zl3073x_dpll_input_ref_frequency_get(zldpll, ref,
- &ref_freq);
- if (rc)
- return rc;
+ /* Get frequency of connected and given ref */
+ conn_freq = zl3073x_dev_ref_freq_get(zldev, conn_id);
+ ref_freq = zl3073x_ref_freq_get(ref);
if (conn_freq > ref_freq) {
s64 conn_period, div_factor;
@@ -599,33 +451,23 @@ zl3073x_dpll_input_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
+ const struct zl3073x_ref *ref;
s64 phase_comp;
- u8 ref;
- int rc;
-
- guard(mutex)(&zldev->multiop_lock);
+ u8 ref_id;
/* Read reference configuration */
- ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
- ZL_REG_REF_MB_MASK, BIT(ref));
- if (rc)
- return rc;
-
- /* Read current phase offset compensation */
- rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, &phase_comp);
- if (rc)
- return rc;
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = zl3073x_ref_state_get(zldev, ref_id);
/* Perform sign extension for 48bit signed value */
- phase_comp = sign_extend64(phase_comp, 47);
+ phase_comp = sign_extend64(ref->phase_comp, 47);
/* Reverse two's complement negation applied during set and convert
* to 32bit signed int
*/
*phase_adjust = (s32)-phase_comp;
- return rc;
+ return 0;
}
static int
@@ -639,32 +481,20 @@ zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- s64 phase_comp;
- u8 ref;
- int rc;
+ struct zl3073x_ref ref;
+ u8 ref_id;
+
+ /* Read reference configuration */
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = *zl3073x_ref_state_get(zldev, ref_id);
/* The value in the register is stored as two's complement negation
* of requested value.
*/
- phase_comp = -phase_adjust;
+ ref.phase_comp = -phase_adjust;
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read reference configuration */
- ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
- ZL_REG_REF_MB_MASK, BIT(ref));
- if (rc)
- return rc;
-
- /* Write the requested value into the compensation register */
- rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP, phase_comp);
- if (rc)
- return rc;
-
- /* Commit reference configuration */
- return zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
- ZL_REG_REF_MB_MASK, BIT(ref));
+ /* Update reference configuration */
+ return zl3073x_ref_state_set(zldev, ref_id, &ref);
}
/**
@@ -775,7 +605,7 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
- u8 ref, ref_conn, status;
+ u8 ref, ref_conn;
int rc;
ref = zl3073x_input_pin_ref_get(pin->id);
@@ -795,20 +625,9 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
* pin as selectable.
*/
if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
- pin->selectable) {
- /* Read reference monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref),
- &status);
- if (rc)
- return rc;
-
- /* If the monitor indicates errors report the reference
- * as disconnected
- */
- if (status == ZL_REF_MON_STATUS_OK) {
- *state = DPLL_PIN_STATE_SELECTABLE;
- return 0;
- }
+ zl3073x_dev_ref_is_status_ok(zldev, ref) && pin->selectable) {
+ *state = DPLL_PIN_STATE_SELECTABLE;
+ return 0;
}
/* Otherwise report the pin as disconnected */
@@ -951,21 +770,19 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- struct device *dev = zldev->dev;
- u32 esync_period, esync_width;
- u8 clock_type, synth;
- u8 out, output_mode;
- u32 output_div;
+ const struct zl3073x_synth *synth;
+ const struct zl3073x_out *out;
+ u8 clock_type, out_id;
u32 synth_freq;
- int rc;
- out = zl3073x_output_pin_out_get(pin->id);
+ out_id = zl3073x_output_pin_out_get(pin->id);
+ out = zl3073x_out_state_get(zldev, out_id);
/* If N-division is enabled, esync is not supported. The register used
* for N-division is also used for the esync divider so both cannot
* be used.
*/
- switch (zl3073x_out_signal_format_get(zldev, out)) {
+ switch (zl3073x_out_signal_format_get(out)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
return -EOPNOTSUPP;
@@ -973,38 +790,11 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
break;
}
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read output configuration into mailbox */
- rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
- if (rc)
- return rc;
-
- /* Read output mode */
- rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
- if (rc)
- return rc;
-
- /* Read output divisor */
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
- if (rc)
- return rc;
-
- /* Check output divisor for zero */
- if (!output_div) {
- dev_err(dev, "Zero divisor for OUTPUT%u got from device\n",
- out);
- return -EINVAL;
- }
-
- /* Get synth attached to output pin */
- synth = zl3073x_out_synth_get(zldev, out);
+ /* Get attached synth frequency */
+ synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(out));
+ synth_freq = zl3073x_synth_freq_get(synth);
- /* Get synth frequency */
- synth_freq = zl3073x_synth_freq_get(zldev, synth);
-
- clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, output_mode);
+ clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
/* No need to read esync data if it is not enabled */
esync->freq = 0;
@@ -1013,38 +803,21 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
goto finish;
}
- /* Read esync period */
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &esync_period);
- if (rc)
- return rc;
-
- /* Check esync divisor for zero */
- if (!esync_period) {
- dev_err(dev, "Zero esync divisor for OUTPUT%u got from device\n",
- out);
- return -EINVAL;
- }
-
- /* Get esync pulse width in units of half synth cycles */
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, &esync_width);
- if (rc)
- return rc;
-
/* Compute esync frequency */
- esync->freq = synth_freq / output_div / esync_period;
+ esync->freq = synth_freq / out->div / out->esync_n_period;
/* By comparing the esync_pulse_width to the half of the pulse width
* the esync pulse percentage can be determined.
* Note that half pulse width is in units of half synth cycles, which
* is why it reduces down to be output_div.
*/
- esync->pulse = (50 * esync_width) / output_div;
+ esync->pulse = (50 * out->esync_n_width) / out->div;
finish:
/* Set supported esync ranges if the pin supports esync control and
* if the output frequency is > 1 Hz.
*/
- if (pin->esync_control && (synth_freq / output_div) > 1) {
+ if (pin->esync_control && (synth_freq / out->div) > 1) {
esync->range = esync_freq_ranges;
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
} else {
@@ -1062,21 +835,22 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
void *dpll_priv, u64 freq,
struct netlink_ext_ack *extack)
{
- u32 esync_period, esync_width, output_div;
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- u8 clock_type, out, output_mode, synth;
+ const struct zl3073x_synth *synth;
+ struct zl3073x_out out;
+ u8 clock_type, out_id;
u32 synth_freq;
- int rc;
- out = zl3073x_output_pin_out_get(pin->id);
+ out_id = zl3073x_output_pin_out_get(pin->id);
+ out = *zl3073x_out_state_get(zldev, out_id);
/* If N-division is enabled, esync is not supported. The register used
* for N-division is also used for the esync divider so both cannot
* be used.
*/
- switch (zl3073x_out_signal_format_get(zldev, out)) {
+ switch (zl3073x_out_signal_format_get(&out)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
return -EOPNOTSUPP;
@@ -1084,19 +858,6 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
break;
}
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read output configuration into mailbox */
- rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
- if (rc)
- return rc;
-
- /* Read output mode */
- rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &output_mode);
- if (rc)
- return rc;
-
/* Select clock type */
if (freq)
clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
@@ -1104,38 +865,19 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL;
/* Update clock type in output mode */
- output_mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
- output_mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
- rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, output_mode);
- if (rc)
- return rc;
+ out.mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
+ out.mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
/* If esync is being disabled just write mailbox and finish */
if (!freq)
goto write_mailbox;
- /* Get synth attached to output pin */
- synth = zl3073x_out_synth_get(zldev, out);
-
- /* Get synth frequency */
- synth_freq = zl3073x_synth_freq_get(zldev, synth);
-
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
- if (rc)
- return rc;
-
- /* Check output divisor for zero */
- if (!output_div) {
- dev_err(zldev->dev,
- "Zero divisor for OUTPUT%u got from device\n", out);
- return -EINVAL;
- }
+ /* Get attached synth frequency */
+ synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(&out));
+ synth_freq = zl3073x_synth_freq_get(synth);
/* Compute and update esync period */
- esync_period = synth_freq / (u32)freq / output_div;
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, esync_period);
- if (rc)
- return rc;
+ out.esync_n_period = synth_freq / (u32)freq / out.div;
/* Half of the period in units of 1/2 synth cycle can be represented by
* the output_div. To get the supported esync pulse width of 25% of the
@@ -1143,15 +885,11 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
* assumes that output_div is even, otherwise some resolution will be
* lost.
*/
- esync_width = output_div / 2;
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, esync_width);
- if (rc)
- return rc;
+ out.esync_n_width = out.div / 2;
write_mailbox:
/* Commit output configuration */
- return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
+ return zl3073x_out_state_set(zldev, out_id, &out);
}
static int
@@ -1164,83 +902,46 @@ zl3073x_dpll_output_pin_frequency_get(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- struct device *dev = zldev->dev;
- u8 out, signal_format, synth;
- u32 output_div, synth_freq;
- int rc;
-
- out = zl3073x_output_pin_out_get(pin->id);
- synth = zl3073x_out_synth_get(zldev, out);
- synth_freq = zl3073x_synth_freq_get(zldev, synth);
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read output configuration into mailbox */
- rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
- if (rc)
- return rc;
-
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &output_div);
- if (rc)
- return rc;
+ const struct zl3073x_synth *synth;
+ const struct zl3073x_out *out;
+ u32 synth_freq;
+ u8 out_id;
- /* Check output divisor for zero */
- if (!output_div) {
- dev_err(dev, "Zero divisor for output %u got from device\n",
- out);
- return -EINVAL;
- }
+ out_id = zl3073x_output_pin_out_get(pin->id);
+ out = zl3073x_out_state_get(zldev, out_id);
- /* Read used signal format for the given output */
- signal_format = zl3073x_out_signal_format_get(zldev, out);
+ /* Get attached synth frequency */
+ synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(out));
+ synth_freq = zl3073x_synth_freq_get(synth);
- switch (signal_format) {
+ switch (zl3073x_out_signal_format_get(out)) {
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
/* In case of divided format we have to distiguish between
* given output pin type.
+ *
+ * For P-pin the resulting frequency is computed as simple
+ * division of synth frequency and output divisor.
+ *
+ * For N-pin we have to divide additionally by divisor stored
+ * in esync_n_period output mailbox register that is used as
+ * N-pin divisor for these modes.
*/
- if (zl3073x_dpll_is_p_pin(pin)) {
- /* For P-pin the resulting frequency is computed as
- * simple division of synth frequency and output
- * divisor.
- */
- *frequency = synth_freq / output_div;
- } else {
- /* For N-pin we have to divide additionally by
- * divisor stored in esync_period output mailbox
- * register that is used as N-pin divisor for these
- * modes.
- */
- u32 ndiv;
-
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
- &ndiv);
- if (rc)
- return rc;
+ *frequency = synth_freq / out->div;
- /* Check N-pin divisor for zero */
- if (!ndiv) {
- dev_err(dev,
- "Zero N-pin divisor for output %u got from device\n",
- out);
- return -EINVAL;
- }
+ if (!zl3073x_dpll_is_p_pin(pin))
+ *frequency = (u32)*frequency / out->esync_n_period;
- /* Compute final divisor for N-pin */
- *frequency = synth_freq / output_div / ndiv;
- }
break;
default:
/* In other modes the resulting frequency is computed as
* division of synth frequency and output divisor.
*/
- *frequency = synth_freq / output_div;
+ *frequency = synth_freq / out->div;
break;
}
- return rc;
+ return 0;
}
static int
@@ -1253,28 +954,21 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- struct device *dev = zldev->dev;
- u32 output_n_freq, output_p_freq;
- u8 out, signal_format, synth;
- u32 cur_div, new_div, ndiv;
- u32 synth_freq;
- int rc;
+ const struct zl3073x_synth *synth;
+ u8 out_id, signal_format;
+ u32 new_div, synth_freq;
+ struct zl3073x_out out;
+
+ out_id = zl3073x_output_pin_out_get(pin->id);
+ out = *zl3073x_out_state_get(zldev, out_id);
- out = zl3073x_output_pin_out_get(pin->id);
- synth = zl3073x_out_synth_get(zldev, out);
- synth_freq = zl3073x_synth_freq_get(zldev, synth);
+ /* Get attached synth frequency and compute new divisor */
+ synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(&out));
+ synth_freq = zl3073x_synth_freq_get(synth);
new_div = synth_freq / (u32)frequency;
/* Get used signal format for the given output */
- signal_format = zl3073x_out_signal_format_get(zldev, out);
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Load output configuration */
- rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
- if (rc)
- return rc;
+ signal_format = zl3073x_out_signal_format_get(&out);
/* Check signal format */
if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV &&
@@ -1282,99 +976,50 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
/* For non N-divided signal formats the frequency is computed
* as division of synth frequency and output divisor.
*/
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
- if (rc)
- return rc;
+ out.div = new_div;
/* For 50/50 duty cycle the divisor is equal to width */
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
- if (rc)
- return rc;
+ out.width = new_div;
/* Commit output configuration */
- return zl3073x_mb_op(zldev,
- ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
- }
-
- /* For N-divided signal format get current divisor */
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &cur_div);
- if (rc)
- return rc;
-
- /* Check output divisor for zero */
- if (!cur_div) {
- dev_err(dev, "Zero divisor for output %u got from device\n",
- out);
- return -EINVAL;
- }
-
- /* Get N-pin divisor (shares the same register with esync */
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, &ndiv);
- if (rc)
- return rc;
-
- /* Check N-pin divisor for zero */
- if (!ndiv) {
- dev_err(dev,
- "Zero N-pin divisor for output %u got from device\n",
- out);
- return -EINVAL;
+ return zl3073x_out_state_set(zldev, out_id, &out);
}
- /* Compute current output frequency for P-pin */
- output_p_freq = synth_freq / cur_div;
-
- /* Compute current N-pin frequency */
- output_n_freq = output_p_freq / ndiv;
-
if (zl3073x_dpll_is_p_pin(pin)) {
/* We are going to change output frequency for P-pin but
* if the requested frequency is less than current N-pin
* frequency then indicate a failure as we are not able
* to compute N-pin divisor to keep its frequency unchanged.
+ *
+ * Update divisor for N-pin to keep N-pin frequency.
*/
- if (frequency <= output_n_freq)
+ out.esync_n_period = (out.esync_n_period * out.div) / new_div;
+ if (!out.esync_n_period)
return -EINVAL;
/* Update the output divisor */
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, new_div);
- if (rc)
- return rc;
+ out.div = new_div;
/* For 50/50 duty cycle the divisor is equal to width */
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, new_div);
- if (rc)
- return rc;
-
- /* Compute new divisor for N-pin */
- ndiv = (u32)frequency / output_n_freq;
+ out.width = out.div;
} else {
/* We are going to change frequency of N-pin but if
* the requested freq is greater or equal than freq of P-pin
* in the output pair we cannot compute divisor for the N-pin.
* In this case indicate a failure.
+ *
+ * Update divisor for N-pin
*/
- if (output_p_freq <= frequency)
+ out.esync_n_period = div64_u64(synth_freq, frequency * out.div);
+ if (!out.esync_n_period)
return -EINVAL;
-
- /* Compute new divisor for N-pin */
- ndiv = output_p_freq / (u32)frequency;
}
- /* Update divisor for the N-pin */
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD, ndiv);
- if (rc)
- return rc;
-
/* For 50/50 duty cycle the divisor is equal to width */
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH, ndiv);
- if (rc)
- return rc;
+ out.esync_n_width = out.esync_n_period;
/* Commit output configuration */
- return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
+ return zl3073x_out_state_set(zldev, out_id, &out);
}
static int
@@ -1388,42 +1033,18 @@ zl3073x_dpll_output_pin_phase_adjust_get(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- u32 synth_freq;
- s32 phase_comp;
- u8 out, synth;
- int rc;
-
- out = zl3073x_output_pin_out_get(pin->id);
- synth = zl3073x_out_synth_get(zldev, out);
- synth_freq = zl3073x_synth_freq_get(zldev, synth);
+ const struct zl3073x_out *out;
+ u8 out_id;
- /* Check synth freq for zero */
- if (!synth_freq) {
- dev_err(zldev->dev, "Got zero synth frequency for output %u\n",
- out);
- return -EINVAL;
- }
+ out_id = zl3073x_output_pin_out_get(pin->id);
+ out = zl3073x_out_state_get(zldev, out_id);
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read output configuration */
- rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
- if (rc)
- return rc;
-
- /* Read current output phase compensation */
- rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, &phase_comp);
- if (rc)
- return rc;
-
- /* Value in register is expressed in half synth clock cycles */
- phase_comp *= (int)div_u64(PSEC_PER_SEC, 2 * synth_freq);
-
- /* Reverse two's complement negation applied during 'set' */
- *phase_adjust = -phase_comp;
+ /* Convert value to ps and reverse two's complement negation applied
+ * during 'set'
+ */
+ *phase_adjust = -out->phase_comp * pin->phase_gran;
- return rc;
+ return 0;
}
static int
@@ -1437,52 +1058,19 @@ zl3073x_dpll_output_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
struct zl3073x_dpll *zldpll = dpll_priv;
struct zl3073x_dev *zldev = zldpll->dev;
struct zl3073x_dpll_pin *pin = pin_priv;
- int half_synth_cycle;
- u32 synth_freq;
- u8 out, synth;
- int rc;
-
- /* Get attached synth */
- out = zl3073x_output_pin_out_get(pin->id);
- synth = zl3073x_out_synth_get(zldev, out);
+ struct zl3073x_out out;
+ u8 out_id;
- /* Get synth's frequency */
- synth_freq = zl3073x_synth_freq_get(zldev, synth);
-
- /* Value in register is expressed in half synth clock cycles so
- * the given phase adjustment a multiple of half synth clock.
- */
- half_synth_cycle = (int)div_u64(PSEC_PER_SEC, 2 * synth_freq);
-
- if ((phase_adjust % half_synth_cycle) != 0) {
- NL_SET_ERR_MSG_FMT(extack,
- "Phase adjustment value has to be multiple of %d",
- half_synth_cycle);
- return -EINVAL;
- }
- phase_adjust /= half_synth_cycle;
+ out_id = zl3073x_output_pin_out_get(pin->id);
+ out = *zl3073x_out_state_get(zldev, out_id);
/* The value in the register is stored as two's complement negation
- * of requested value.
+ * of requested value and expressed in half synth clock cycles.
*/
- phase_adjust = -phase_adjust;
-
- guard(mutex)(&zldev->multiop_lock);
-
- /* Read output configuration */
- rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
- if (rc)
- return rc;
-
- /* Write the requested value into the compensation register */
- rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP, phase_adjust);
- if (rc)
- return rc;
+ out.phase_comp = -phase_adjust / pin->phase_gran;
/* Update output configuration from mailbox */
- return zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
- ZL_REG_OUTPUT_MB_MASK, BIT(out));
+ return zl3073x_out_state_set(zldev, out_id, &out);
}
static int
@@ -1758,9 +1346,10 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
if (IS_ERR(props))
return PTR_ERR(props);
- /* Save package label & esync capability */
+ /* Save package label, esync capability and phase adjust granularity */
strscpy(pin->label, props->package_label);
pin->esync_control = props->esync_control;
+ pin->phase_gran = props->dpll_props.phase_gran;
if (zl3073x_dpll_is_input_pin(pin)) {
rc = zl3073x_dpll_ref_prio_get(pin, &pin->prio);
@@ -1878,33 +1467,32 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,
const char *name;
if (dir == DPLL_PIN_DIRECTION_INPUT) {
- u8 ref = zl3073x_input_pin_ref_get(index);
-
- name = "REF";
+ u8 ref_id = zl3073x_input_pin_ref_get(index);
+ const struct zl3073x_ref *ref;
/* Skip the pin if the DPLL is running in NCO mode */
if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_NCO)
return false;
- is_diff = zl3073x_ref_is_diff(zldev, ref);
- is_enabled = zl3073x_ref_is_enabled(zldev, ref);
+ name = "REF";
+ ref = zl3073x_ref_state_get(zldev, ref_id);
+ is_diff = zl3073x_ref_is_diff(ref);
+ is_enabled = zl3073x_ref_is_enabled(ref);
} else {
/* Output P&N pair shares single HW output */
u8 out = zl3073x_output_pin_out_get(index);
- name = "OUT";
-
/* Skip the pin if it is connected to different DPLL channel */
- if (zl3073x_out_dpll_get(zldev, out) != zldpll->id) {
+ if (zl3073x_dev_out_dpll_get(zldev, out) != zldpll->id) {
dev_dbg(zldev->dev,
- "%s%u is driven by different DPLL\n", name,
- out);
+ "OUT%u is driven by different DPLL\n", out);
return false;
}
- is_diff = zl3073x_out_is_diff(zldev, out);
- is_enabled = zl3073x_out_is_enabled(zldev, out);
+ name = "OUT";
+ is_diff = zl3073x_dev_out_is_diff(zldev, out);
+ is_enabled = zl3073x_dev_output_pin_is_enabled(zldev, index);
}
/* Skip N-pin if the corresponding input/output is differential */
@@ -2061,42 +1649,26 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin)
struct zl3073x_dev *zldev = zldpll->dev;
unsigned int reg;
s64 phase_offset;
- u8 ref;
+ u8 ref_id;
int rc;
- ref = zl3073x_input_pin_ref_get(pin->id);
+ /* No phase offset if the ref monitor reports signal errors */
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ if (!zl3073x_dev_ref_is_status_ok(zldev, ref_id))
+ return false;
/* Select register to read phase offset value depending on pin and
* phase monitor state:
* 1) For connected pin use dpll_phase_err_data register
* 2) For other pins use appropriate ref_phase register if the phase
- * monitor feature is enabled and reference monitor does not
- * report signal errors for given input pin
+ * monitor feature is enabled.
*/
- if (pin->pin_state == DPLL_PIN_STATE_CONNECTED) {
+ if (pin->pin_state == DPLL_PIN_STATE_CONNECTED)
reg = ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id);
- } else if (zldpll->phase_monitor) {
- u8 status;
-
- /* Get reference monitor status */
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref),
- &status);
- if (rc) {
- dev_err(zldev->dev,
- "Failed to read %s refmon status: %pe\n",
- pin->label, ERR_PTR(rc));
-
- return false;
- }
-
- if (status != ZL_REF_MON_STATUS_OK)
- return false;
-
- reg = ZL_REG_REF_PHASE(ref);
- } else {
- /* The pin is not connected or phase monitor disabled */
+ else if (zldpll->phase_monitor)
+ reg = ZL_REG_REF_PHASE(ref_id);
+ else
return false;
- }
/* Read measured phase offset value */
rc = zl3073x_read_u48(zldev, reg, &phase_offset);
@@ -2135,32 +1707,22 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
{
struct zl3073x_dpll *zldpll = pin->dpll;
struct zl3073x_dev *zldev = zldpll->dev;
- u8 ref, status;
- s64 ffo;
- int rc;
+ const struct zl3073x_ref *ref;
+ u8 ref_id;
/* Get reference monitor status */
- ref = zl3073x_input_pin_ref_get(pin->id);
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(ref), &status);
- if (rc) {
- dev_err(zldev->dev, "Failed to read %s refmon status: %pe\n",
- pin->label, ERR_PTR(rc));
-
- return false;
- }
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
+ ref = zl3073x_ref_state_get(zldev, ref_id);
/* Do not report ffo changes if the reference monitor report errors */
- if (status != ZL_REF_MON_STATUS_OK)
+ if (!zl3073x_ref_is_status_ok(ref))
return false;
- /* Get the latest measured ref's ffo */
- ffo = zl3073x_ref_ffo_get(zldev, ref);
-
/* Compare with previous value */
- if (pin->freq_offset != ffo) {
+ if (pin->freq_offset != ref->ffo) {
dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n",
- pin->label, pin->freq_offset, ffo);
- pin->freq_offset = ffo;
+ pin->label, pin->freq_offset, ref->ffo);
+ pin->freq_offset = ref->ffo;
return true;
}
diff --git a/drivers/dpll/zl3073x/fw.c b/drivers/dpll/zl3073x/fw.c
index def37fe8d9b0..55b638247f4b 100644
--- a/drivers/dpll/zl3073x/fw.c
+++ b/drivers/dpll/zl3073x/fw.c
@@ -352,12 +352,12 @@ struct zl3073x_fw *zl3073x_fw_load(struct zl3073x_dev *zldev, const char *data,
}
/**
- * zl3073x_flash_bundle_flash - Flash all components
+ * zl3073x_fw_component_flash - Flash all components
* @zldev: zl3073x device structure
- * @components: pointer to components array
+ * @comp: pointer to components array
* @extack: netlink extack pointer to report errors
*
- * Returns 0 in case of success or negative number otherwise.
+ * Return: 0 in case of success or negative number otherwise.
*/
static int
zl3073x_fw_component_flash(struct zl3073x_dev *zldev,
diff --git a/drivers/dpll/zl3073x/out.c b/drivers/dpll/zl3073x/out.c
new file mode 100644
index 000000000000..86829a0c1c02
--- /dev/null
+++ b/drivers/dpll/zl3073x/out.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/string.h>
+#include <linux/string_choices.h>
+#include <linux/types.h>
+
+#include "core.h"
+#include "out.h"
+
+/**
+ * zl3073x_out_state_fetch - fetch output state from hardware
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: output index to fetch state for
+ *
+ * Function fetches state of the given output from hardware and stores it
+ * for later use.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+ struct zl3073x_out *out = &zldev->out[index];
+ int rc;
+
+ /* Read output configuration */
+ rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_CTRL(index), &out->ctrl);
+ if (rc)
+ return rc;
+
+ dev_dbg(zldev->dev, "OUT%u is %s and connected to SYNTH%u\n", index,
+ str_enabled_disabled(zl3073x_out_is_enabled(out)),
+ zl3073x_out_synth_get(out));
+
+ guard(mutex)(&zldev->multiop_lock);
+
+ /* Read output configuration */
+ rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
+ ZL_REG_OUTPUT_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ /* Read output mode */
+ rc = zl3073x_read_u8(zldev, ZL_REG_OUTPUT_MODE, &out->mode);
+ if (rc)
+ return rc;
+
+ dev_dbg(zldev->dev, "OUT%u has signal format 0x%02x\n", index,
+ zl3073x_out_signal_format_get(out));
+
+ /* Read output divisor */
+ rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_DIV, &out->div);
+ if (rc)
+ return rc;
+
+ if (!out->div) {
+ dev_err(zldev->dev, "Zero divisor for OUT%u got from device\n",
+ index);
+ return -EINVAL;
+ }
+
+ dev_dbg(zldev->dev, "OUT%u divisor: %u\n", index, out->div);
+
+ /* Read output width */
+ rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_WIDTH, &out->width);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
+ &out->esync_n_period);
+ if (rc)
+ return rc;
+
+ if (!out->esync_n_period) {
+ dev_err(zldev->dev,
+ "Zero esync divisor for OUT%u got from device\n",
+ index);
+ return -EINVAL;
+ }
+
+ rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
+ &out->esync_n_width);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
+ &out->phase_comp);
+ if (rc)
+ return rc;
+
+ return rc;
+}
+
+/**
+ * zl3073x_out_state_get - get current output state
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: output index to get state for
+ *
+ * Return: pointer to given output state
+ */
+const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
+ u8 index)
+{
+ return &zldev->out[index];
+}
+
+int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
+ const struct zl3073x_out *out)
+{
+ struct zl3073x_out *dout = &zldev->out[index];
+ int rc;
+
+ guard(mutex)(&zldev->multiop_lock);
+
+ /* Read output configuration into mailbox */
+ rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_RD,
+ ZL_REG_OUTPUT_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ /* Update mailbox with changed values */
+ if (dout->div != out->div)
+ rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_DIV, out->div);
+ if (!rc && dout->width != out->width)
+ rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_WIDTH, out->width);
+ if (!rc && dout->esync_n_period != out->esync_n_period)
+ rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_PERIOD,
+ out->esync_n_period);
+ if (!rc && dout->esync_n_width != out->esync_n_width)
+ rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_ESYNC_WIDTH,
+ out->esync_n_width);
+ if (!rc && dout->mode != out->mode)
+ rc = zl3073x_write_u8(zldev, ZL_REG_OUTPUT_MODE, out->mode);
+ if (!rc && dout->phase_comp != out->phase_comp)
+ rc = zl3073x_write_u32(zldev, ZL_REG_OUTPUT_PHASE_COMP,
+ out->phase_comp);
+ if (rc)
+ return rc;
+
+ /* Commit output configuration */
+ rc = zl3073x_mb_op(zldev, ZL_REG_OUTPUT_MB_SEM, ZL_OUTPUT_MB_SEM_WR,
+ ZL_REG_OUTPUT_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ /* After successful commit store new state */
+ dout->div = out->div;
+ dout->width = out->width;
+ dout->esync_n_period = out->esync_n_period;
+ dout->esync_n_width = out->esync_n_width;
+ dout->mode = out->mode;
+ dout->phase_comp = out->phase_comp;
+
+ return 0;
+}
diff --git a/drivers/dpll/zl3073x/out.h b/drivers/dpll/zl3073x/out.h
new file mode 100644
index 000000000000..e8ea7a0e0f07
--- /dev/null
+++ b/drivers/dpll/zl3073x/out.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ZL3073X_OUT_H
+#define _ZL3073X_OUT_H
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+
+#include "regs.h"
+
+struct zl3073x_dev;
+
+/**
+ * struct zl3073x_out - output state
+ * @div: output divisor
+ * @width: output pulse width
+ * @esync_n_period: embedded sync or n-pin period (for n-div formats)
+ * @esync_n_width: embedded sync or n-pin pulse width
+ * @phase_comp: phase compensation
+ * @ctrl: output control
+ * @mode: output mode
+ */
+struct zl3073x_out {
+ u32 div;
+ u32 width;
+ u32 esync_n_period;
+ u32 esync_n_width;
+ s32 phase_comp;
+ u8 ctrl;
+ u8 mode;
+};
+
+int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index);
+const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
+ u8 index);
+
+int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
+ const struct zl3073x_out *out);
+
+/**
+ * zl3073x_out_signal_format_get - get output signal format
+ * @out: pointer to out state
+ *
+ * Return: signal format of given output
+ */
+static inline u8 zl3073x_out_signal_format_get(const struct zl3073x_out *out)
+{
+ return FIELD_GET(ZL_OUTPUT_MODE_SIGNAL_FORMAT, out->mode);
+}
+
+/**
+ * zl3073x_out_is_diff - check if the given output is differential
+ * @out: pointer to out state
+ *
+ * Return: true if output is differential, false if output is single-ended
+ */
+static inline bool zl3073x_out_is_diff(const struct zl3073x_out *out)
+{
+ switch (zl3073x_out_signal_format_get(out)) {
+ case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LVDS:
+ case ZL_OUTPUT_MODE_SIGNAL_FORMAT_DIFF:
+ case ZL_OUTPUT_MODE_SIGNAL_FORMAT_LOWVCM:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/**
+ * zl3073x_out_is_enabled - check if the given output is enabled
+ * @out: pointer to out state
+ *
+ * Return: true if output is enabled, false if output is disabled
+ */
+static inline bool zl3073x_out_is_enabled(const struct zl3073x_out *out)
+{
+ return !!FIELD_GET(ZL_OUTPUT_CTRL_EN, out->ctrl);
+}
+
+/**
+ * zl3073x_out_synth_get - get synth connected to given output
+ * @out: pointer to out state
+ *
+ * Return: index of synth connected to given output.
+ */
+static inline u8 zl3073x_out_synth_get(const struct zl3073x_out *out)
+{
+ return FIELD_GET(ZL_OUTPUT_CTRL_SYNTH_SEL, out->ctrl);
+}
+
+#endif /* _ZL3073X_OUT_H */
diff --git a/drivers/dpll/zl3073x/prop.c b/drivers/dpll/zl3073x/prop.c
index 4cf7e8aefcb3..4ed153087570 100644
--- a/drivers/dpll/zl3073x/prop.c
+++ b/drivers/dpll/zl3073x/prop.c
@@ -46,10 +46,10 @@ zl3073x_pin_check_freq(struct zl3073x_dev *zldev, enum dpll_pin_direction dir,
/* Get output pin synthesizer */
out = zl3073x_output_pin_out_get(id);
- synth = zl3073x_out_synth_get(zldev, out);
+ synth = zl3073x_dev_out_synth_get(zldev, out);
/* Get synth frequency */
- synth_freq = zl3073x_synth_freq_get(zldev, synth);
+ synth_freq = zl3073x_dev_synth_freq_get(zldev, synth);
/* Check the frequency divides synth frequency */
if (synth_freq % (u32)freq)
@@ -93,13 +93,13 @@ zl3073x_prop_pin_package_label_set(struct zl3073x_dev *zldev,
prefix = "REF";
ref = zl3073x_input_pin_ref_get(id);
- is_diff = zl3073x_ref_is_diff(zldev, ref);
+ is_diff = zl3073x_dev_ref_is_diff(zldev, ref);
} else {
u8 out;
prefix = "OUT";
out = zl3073x_output_pin_out_get(id);
- is_diff = zl3073x_out_is_diff(zldev, out);
+ is_diff = zl3073x_dev_out_is_diff(zldev, out);
}
if (!is_diff)
@@ -208,7 +208,18 @@ struct zl3073x_pin_props *zl3073x_pin_props_get(struct zl3073x_dev *zldev,
DPLL_PIN_CAPABILITIES_PRIORITY_CAN_CHANGE |
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE;
} else {
+ u8 out, synth;
+ u32 f;
+
props->dpll_props.type = DPLL_PIN_TYPE_GNSS;
+
+ /* The output pin phase adjustment granularity equals half of
+ * the synth frequency count.
+ */
+ out = zl3073x_output_pin_out_get(index);
+ synth = zl3073x_dev_out_synth_get(zldev, out);
+ f = 2 * zl3073x_dev_synth_freq_get(zldev, synth);
+ props->dpll_props.phase_gran = f ? div_u64(PSEC_PER_SEC, f) : 1;
}
props->dpll_props.phase_range.min = S32_MIN;
diff --git a/drivers/dpll/zl3073x/ref.c b/drivers/dpll/zl3073x/ref.c
new file mode 100644
index 000000000000..aa2de13effa8
--- /dev/null
+++ b/drivers/dpll/zl3073x/ref.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/string.h>
+#include <linux/string_choices.h>
+#include <linux/types.h>
+
+#include "core.h"
+#include "ref.h"
+
+/**
+ * zl3073x_ref_freq_factorize - factorize given frequency
+ * @freq: input frequency
+ * @base: base frequency
+ * @mult: multiplier
+ *
+ * Checks if the given frequency can be factorized using one of the
+ * supported base frequencies. If so the base frequency and multiplier
+ * are stored into appropriate parameters if they are not NULL.
+ *
+ * Return: 0 on success, -EINVAL if the frequency cannot be factorized
+ */
+int
+zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
+{
+ static const u16 base_freqs[] = {
+ 1, 2, 4, 5, 8, 10, 16, 20, 25, 32, 40, 50, 64, 80, 100, 125,
+ 128, 160, 200, 250, 256, 320, 400, 500, 625, 640, 800, 1000,
+ 1250, 1280, 1600, 2000, 2500, 3125, 3200, 4000, 5000, 6250,
+ 6400, 8000, 10000, 12500, 15625, 16000, 20000, 25000, 31250,
+ 32000, 40000, 50000, 62500,
+ };
+ u32 div;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(base_freqs); i++) {
+ div = freq / base_freqs[i];
+
+ if (div <= U16_MAX && (freq % base_freqs[i]) == 0) {
+ if (base)
+ *base = base_freqs[i];
+ if (mult)
+ *mult = div;
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * zl3073x_ref_state_fetch - fetch input reference state from hardware
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: input reference index to fetch state for
+ *
+ * Function fetches state for the given input reference from hardware and
+ * stores it for later use.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+ struct zl3073x_ref *ref = &zldev->ref[index];
+ int rc;
+
+ /* For differential type inputs the N-pin reference shares
+ * part of the configuration with the P-pin counterpart.
+ */
+ if (zl3073x_is_n_pin(index) && zl3073x_ref_is_diff(ref - 1)) {
+ struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
+
+ /* Copy the shared items from the P-pin */
+ ref->config = p_ref->config;
+ ref->esync_n_div = p_ref->esync_n_div;
+ ref->freq_base = p_ref->freq_base;
+ ref->freq_mult = p_ref->freq_mult;
+ ref->freq_ratio_m = p_ref->freq_ratio_m;
+ ref->freq_ratio_n = p_ref->freq_ratio_n;
+ ref->phase_comp = p_ref->phase_comp;
+ ref->sync_ctrl = p_ref->sync_ctrl;
+
+ return 0; /* Finish - no non-shared items for now */
+ }
+
+ guard(mutex)(&zldev->multiop_lock);
+
+ /* Read reference configuration */
+ rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
+ ZL_REG_REF_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ /* Read ref_config register */
+ rc = zl3073x_read_u8(zldev, ZL_REG_REF_CONFIG, &ref->config);
+ if (rc)
+ return rc;
+
+ /* Read frequency related registers */
+ rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_BASE, &ref->freq_base);
+ if (rc)
+ return rc;
+ rc = zl3073x_read_u16(zldev, ZL_REG_REF_FREQ_MULT, &ref->freq_mult);
+ if (rc)
+ return rc;
+ rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_M, &ref->freq_ratio_m);
+ if (rc)
+ return rc;
+ rc = zl3073x_read_u16(zldev, ZL_REG_REF_RATIO_N, &ref->freq_ratio_n);
+ if (rc)
+ return rc;
+
+ /* Read eSync and N-div rated registers */
+ rc = zl3073x_read_u32(zldev, ZL_REG_REF_ESYNC_DIV, &ref->esync_n_div);
+ if (rc)
+ return rc;
+ rc = zl3073x_read_u8(zldev, ZL_REG_REF_SYNC_CTRL, &ref->sync_ctrl);
+ if (rc)
+ return rc;
+
+ /* Read phase compensation register */
+ rc = zl3073x_read_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
+ &ref->phase_comp);
+ if (rc)
+ return rc;
+
+ dev_dbg(zldev->dev, "REF%u is %s and configured as %s\n", index,
+ str_enabled_disabled(zl3073x_ref_is_enabled(ref)),
+ zl3073x_ref_is_diff(ref) ? "differential" : "single-ended");
+
+ return rc;
+}
+
+/**
+ * zl3073x_ref_state_get - get current input reference state
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: input reference index to get state for
+ *
+ * Return: pointer to given input reference state
+ */
+const struct zl3073x_ref *
+zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
+{
+ return &zldev->ref[index];
+}
+
+int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
+ const struct zl3073x_ref *ref)
+{
+ struct zl3073x_ref *dref = &zldev->ref[index];
+ int rc;
+
+ guard(mutex)(&zldev->multiop_lock);
+
+ /* Read reference configuration into mailbox */
+ rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_RD,
+ ZL_REG_REF_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ /* Update mailbox with changed values */
+ if (dref->freq_base != ref->freq_base)
+ rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_BASE,
+ ref->freq_base);
+ if (!rc && dref->freq_mult != ref->freq_mult)
+ rc = zl3073x_write_u16(zldev, ZL_REG_REF_FREQ_MULT,
+ ref->freq_mult);
+ if (!rc && dref->freq_ratio_m != ref->freq_ratio_m)
+ rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_M,
+ ref->freq_ratio_m);
+ if (!rc && dref->freq_ratio_n != ref->freq_ratio_n)
+ rc = zl3073x_write_u16(zldev, ZL_REG_REF_RATIO_N,
+ ref->freq_ratio_n);
+ if (!rc && dref->esync_n_div != ref->esync_n_div)
+ rc = zl3073x_write_u32(zldev, ZL_REG_REF_ESYNC_DIV,
+ ref->esync_n_div);
+ if (!rc && dref->sync_ctrl != ref->sync_ctrl)
+ rc = zl3073x_write_u8(zldev, ZL_REG_REF_SYNC_CTRL,
+ ref->sync_ctrl);
+ if (!rc && dref->phase_comp != ref->phase_comp)
+ rc = zl3073x_write_u48(zldev, ZL_REG_REF_PHASE_OFFSET_COMP,
+ ref->phase_comp);
+ if (rc)
+ return rc;
+
+ /* Commit reference configuration */
+ rc = zl3073x_mb_op(zldev, ZL_REG_REF_MB_SEM, ZL_REF_MB_SEM_WR,
+ ZL_REG_REF_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ /* After successful commit store new state */
+ dref->freq_base = ref->freq_base;
+ dref->freq_mult = ref->freq_mult;
+ dref->freq_ratio_m = ref->freq_ratio_m;
+ dref->freq_ratio_n = ref->freq_ratio_n;
+ dref->esync_n_div = ref->esync_n_div;
+ dref->sync_ctrl = ref->sync_ctrl;
+ dref->phase_comp = ref->phase_comp;
+
+ return 0;
+}
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
new file mode 100644
index 000000000000..efc7f59cd9f9
--- /dev/null
+++ b/drivers/dpll/zl3073x/ref.h
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ZL3073X_REF_H
+#define _ZL3073X_REF_H
+
+#include <linux/bitfield.h>
+#include <linux/math64.h>
+#include <linux/types.h>
+
+#include "regs.h"
+
+struct zl3073x_dev;
+
+/**
+ * struct zl3073x_ref - input reference state
+ * @ffo: current fractional frequency offset
+ * @phase_comp: phase compensation
+ * @esync_n_div: divisor for embedded sync or n-divided signal formats
+ * @freq_base: frequency base
+ * @freq_mult: frequnecy multiplier
+ * @freq_ratio_m: FEC mode multiplier
+ * @freq_ratio_n: FEC mode divisor
+ * @config: reference config
+ * @sync_ctrl: reference sync control
+ * @mon_status: reference monitor status
+ */
+struct zl3073x_ref {
+ s64 ffo;
+ u64 phase_comp;
+ u32 esync_n_div;
+ u16 freq_base;
+ u16 freq_mult;
+ u16 freq_ratio_m;
+ u16 freq_ratio_n;
+ u8 config;
+ u8 sync_ctrl;
+ u8 mon_status;
+};
+
+int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index);
+
+const struct zl3073x_ref *zl3073x_ref_state_get(struct zl3073x_dev *zldev,
+ u8 index);
+
+int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
+ const struct zl3073x_ref *ref);
+
+int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
+
+/**
+ * zl3073x_ref_ffo_get - get current fractional frequency offset
+ * @ref: pointer to ref state
+ *
+ * Return: the latest measured fractional frequency offset
+ */
+static inline s64
+zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
+{
+ return ref->ffo;
+}
+
+/**
+ * zl3073x_ref_freq_get - get given input reference frequency
+ * @ref: pointer to ref state
+ *
+ * Return: frequency of the given input reference
+ */
+static inline u32
+zl3073x_ref_freq_get(const struct zl3073x_ref *ref)
+{
+ return mul_u64_u32_div(ref->freq_base * ref->freq_mult,
+ ref->freq_ratio_m, ref->freq_ratio_n);
+}
+
+/**
+ * zl3073x_ref_freq_set - set given input reference frequency
+ * @ref: pointer to ref state
+ * @freq: frequency to be set
+ *
+ * Return: 0 on success, <0 when frequency cannot be factorized
+ */
+static inline int
+zl3073x_ref_freq_set(struct zl3073x_ref *ref, u32 freq)
+{
+ u16 base, mult;
+ int rc;
+
+ rc = zl3073x_ref_freq_factorize(freq, &base, &mult);
+ if (rc)
+ return rc;
+
+ ref->freq_base = base;
+ ref->freq_mult = mult;
+
+ return 0;
+}
+
+/**
+ * zl3073x_ref_is_diff - check if the given input reference is differential
+ * @ref: pointer to ref state
+ *
+ * Return: true if reference is differential, false if reference is single-ended
+ */
+static inline bool
+zl3073x_ref_is_diff(const struct zl3073x_ref *ref)
+{
+ return !!FIELD_GET(ZL_REF_CONFIG_DIFF_EN, ref->config);
+}
+
+/**
+ * zl3073x_ref_is_enabled - check if the given input reference is enabled
+ * @ref: pointer to ref state
+ *
+ * Return: true if input refernce is enabled, false otherwise
+ */
+static inline bool
+zl3073x_ref_is_enabled(const struct zl3073x_ref *ref)
+{
+ return !!FIELD_GET(ZL_REF_CONFIG_ENABLE, ref->config);
+}
+
+/**
+ * zl3073x_ref_is_status_ok - check the given input reference status
+ * @ref: pointer to ref state
+ *
+ * Return: true if the status is ok, false otherwise
+ */
+static inline bool
+zl3073x_ref_is_status_ok(const struct zl3073x_ref *ref)
+{
+ return ref->mon_status == ZL_REF_MON_STATUS_OK;
+}
+
+#endif /* _ZL3073X_REF_H */
diff --git a/drivers/dpll/zl3073x/synth.c b/drivers/dpll/zl3073x/synth.c
new file mode 100644
index 000000000000..da839572dab2
--- /dev/null
+++ b/drivers/dpll/zl3073x/synth.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/dev_printk.h>
+#include <linux/string.h>
+#include <linux/string_choices.h>
+#include <linux/types.h>
+
+#include "core.h"
+#include "synth.h"
+
+/**
+ * zl3073x_synth_state_fetch - fetch synth state from hardware
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: synth index to fetch state for
+ *
+ * Function fetches state of the given synthesizer from the hardware and
+ * stores it for later use.
+ *
+ * Return: 0 on success, <0 on error
+ */
+int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 index)
+{
+ struct zl3073x_synth *synth = &zldev->synth[index];
+ int rc;
+
+ /* Read synth control register */
+ rc = zl3073x_read_u8(zldev, ZL_REG_SYNTH_CTRL(index), &synth->ctrl);
+ if (rc)
+ return rc;
+
+ guard(mutex)(&zldev->multiop_lock);
+
+ /* Read synth configuration */
+ rc = zl3073x_mb_op(zldev, ZL_REG_SYNTH_MB_SEM, ZL_SYNTH_MB_SEM_RD,
+ ZL_REG_SYNTH_MB_MASK, BIT(index));
+ if (rc)
+ return rc;
+
+ /* The output frequency is determined by the following formula:
+ * base * multiplier * numerator / denominator
+ *
+ * Read registers with these values
+ */
+ rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_BASE, &synth->freq_base);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_u32(zldev, ZL_REG_SYNTH_FREQ_MULT, &synth->freq_mult);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_M, &synth->freq_m);
+ if (rc)
+ return rc;
+
+ rc = zl3073x_read_u16(zldev, ZL_REG_SYNTH_FREQ_N, &synth->freq_n);
+ if (rc)
+ return rc;
+
+ /* Check denominator for zero to avoid div by 0 */
+ if (!synth->freq_n) {
+ dev_err(zldev->dev,
+ "Zero divisor for SYNTH%u retrieved from device\n",
+ index);
+ return -EINVAL;
+ }
+
+ dev_dbg(zldev->dev, "SYNTH%u frequency: %u Hz\n", index,
+ zl3073x_synth_freq_get(synth));
+
+ return rc;
+}
+
+/**
+ * zl3073x_synth_state_get - get current synth state
+ * @zldev: pointer to zl3073x_dev structure
+ * @index: synth index to get state for
+ *
+ * Return: pointer to given synth state
+ */
+const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
+ u8 index)
+{
+ return &zldev->synth[index];
+}
diff --git a/drivers/dpll/zl3073x/synth.h b/drivers/dpll/zl3073x/synth.h
new file mode 100644
index 000000000000..6c55eb8a888c
--- /dev/null
+++ b/drivers/dpll/zl3073x/synth.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef _ZL3073X_SYNTH_H
+#define _ZL3073X_SYNTH_H
+
+#include <linux/bitfield.h>
+#include <linux/math64.h>
+#include <linux/types.h>
+
+#include "regs.h"
+
+struct zl3073x_dev;
+
+/**
+ * struct zl3073x_synth - synthesizer state
+ * @freq_mult: frequency multiplier
+ * @freq_base: frequency base
+ * @freq_m: frequency numerator
+ * @freq_n: frequency denominator
+ * @ctrl: synth control
+ */
+struct zl3073x_synth {
+ u32 freq_mult;
+ u16 freq_base;
+ u16 freq_m;
+ u16 freq_n;
+ u8 ctrl;
+};
+
+int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
+
+const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
+ u8 synth_id);
+
+int zl3073x_synth_state_set(struct zl3073x_dev *zldev, u8 synth_id,
+ const struct zl3073x_synth *synth);
+
+/**
+ * zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
+ * @synth: pointer to synth state
+ *
+ * Return: ID of DPLL the given synthetizer is driven by
+ */
+static inline u8 zl3073x_synth_dpll_get(const struct zl3073x_synth *synth)
+{
+ return FIELD_GET(ZL_SYNTH_CTRL_DPLL_SEL, synth->ctrl);
+}
+
+/**
+ * zl3073x_synth_freq_get - get synth current freq
+ * @synth: pointer to synth state
+ *
+ * Return: frequency of given synthetizer
+ */
+static inline u32 zl3073x_synth_freq_get(const struct zl3073x_synth *synth)
+{
+ return mul_u64_u32_div(synth->freq_base * synth->freq_m,
+ synth->freq_mult, synth->freq_n);
+}
+
+/**
+ * zl3073x_synth_is_enabled - check if the given synth is enabled
+ * @synth: pointer to synth state
+ *
+ * Return: true if synth is enabled, false otherwise
+ */
+static inline bool zl3073x_synth_is_enabled(const struct zl3073x_synth *synth)
+{
+ return FIELD_GET(ZL_SYNTH_CTRL_EN, synth->ctrl);
+}
+
+#endif /* _ZL3073X_SYNTH_H */
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 39352b9b7a7e..81e40543ffd8 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -23,14 +23,6 @@ menuconfig EDAC
if EDAC
-config EDAC_LEGACY_SYSFS
- bool "EDAC legacy sysfs"
- default y
- help
- Enable the compatibility sysfs nodes.
- Use 'Y' if your edac utilities aren't ported to work with the newer
- structures.
-
config EDAC_DEBUG
bool "Debugging"
select DEBUG_FS
@@ -291,6 +283,18 @@ config EDAC_I10NM
system has non-volatile DIMMs you should also manually
select CONFIG_ACPI_NFIT.
+config EDAC_IMH
+ tristate "Intel Integrated Memory/IO Hub MC"
+ depends on X86_64 && X86_MCE_INTEL && ACPI
+ depends on ACPI_NFIT || !ACPI_NFIT # if ACPI_NFIT=m, EDAC_IMH can't be y
+ select DMI
+ select ACPI_ADXL
+ help
+ Support for error detection and correction the Intel
+ Integrated Memory/IO Hub Memory Controller. This MC IP is
+ first used on the Diamond Rapids servers but may appear on
+ others in the future.
+
config EDAC_PND2
tristate "Intel Pondicherry2"
depends on PCI && X86_64 && X86_MCE_INTEL
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 1c14796410a3..8429b1e856bc 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -65,6 +65,9 @@ obj-$(CONFIG_EDAC_SKX) += skx_edac.o skx_edac_common.o
i10nm_edac-y := i10nm_base.o
obj-$(CONFIG_EDAC_I10NM) += i10nm_edac.o skx_edac_common.o
+imh_edac-y := imh_base.o
+obj-$(CONFIG_EDAC_IMH) += imh_edac.o skx_edac_common.o
+
obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o
obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
index 103b2c2eba2a..0c5b94e64ea1 100644
--- a/drivers/edac/altera_edac.c
+++ b/drivers/edac/altera_edac.c
@@ -1184,10 +1184,22 @@ altr_check_ocram_deps_init(struct altr_edac_device_dev *device)
if (ret)
return ret;
- /* Verify OCRAM has been initialized */
+ /*
+ * Verify that OCRAM has been initialized.
+ * During a warm reset, OCRAM contents are retained, but the control
+ * and status registers are reset to their default values. Therefore,
+ * ECC must be explicitly re-enabled in the control register.
+ * Error condition: if INITCOMPLETEA is clear and ECC_EN is already set.
+ */
if (!ecc_test_bits(ALTR_A10_ECC_INITCOMPLETEA,
- (base + ALTR_A10_ECC_INITSTAT_OFST)))
- return -ENODEV;
+ (base + ALTR_A10_ECC_INITSTAT_OFST))) {
+ if (!ecc_test_bits(ALTR_A10_ECC_EN,
+ (base + ALTR_A10_ECC_CTRL_OFST)))
+ ecc_set_bits(ALTR_A10_ECC_EN,
+ (base + ALTR_A10_ECC_CTRL_OFST));
+ else
+ return -ENODEV;
+ }
/* Enable IRQ on Single Bit Error */
writel(ALTR_A10_ECC_SERRINTEN, (base + ALTR_A10_ECC_ERRINTENS_OFST));
@@ -1357,7 +1369,7 @@ static const struct edac_device_prv_data a10_enetecc_data = {
.ue_set_mask = ALTR_A10_ECC_TDERRA,
.set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,
.ecc_irq_handler = altr_edac_a10_ecc_irq,
- .inject_fops = &altr_edac_a10_device_inject2_fops,
+ .inject_fops = &altr_edac_a10_device_inject_fops,
};
#endif /* CONFIG_EDAC_ALTERA_ETHERNET */
@@ -1447,7 +1459,7 @@ static const struct edac_device_prv_data a10_usbecc_data = {
.ue_set_mask = ALTR_A10_ECC_TDERRA,
.set_err_ofst = ALTR_A10_ECC_INTTEST_OFST,
.ecc_irq_handler = altr_edac_a10_ecc_irq,
- .inject_fops = &altr_edac_a10_device_inject2_fops,
+ .inject_fops = &altr_edac_a10_device_inject_fops,
};
#endif /* CONFIG_EDAC_ALTERA_USB */
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 2f6ab783bf20..2391f3469961 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -3732,6 +3732,7 @@ static void hw_info_put(struct amd64_pvt *pvt)
pci_dev_put(pvt->F1);
pci_dev_put(pvt->F2);
kfree(pvt->umc);
+ kfree(pvt->csels);
}
static struct low_ops umc_ops = {
@@ -3766,6 +3767,7 @@ static int per_family_init(struct amd64_pvt *pvt)
pvt->stepping = boot_cpu_data.x86_stepping;
pvt->model = boot_cpu_data.x86_model;
pvt->fam = boot_cpu_data.x86;
+ char *tmp_name = NULL;
pvt->max_mcs = 2;
/*
@@ -3779,7 +3781,7 @@ static int per_family_init(struct amd64_pvt *pvt)
switch (pvt->fam) {
case 0xf:
- pvt->ctl_name = (pvt->ext_model >= K8_REV_F) ?
+ tmp_name = (pvt->ext_model >= K8_REV_F) ?
"K8 revF or later" : "K8 revE or earlier";
pvt->f1_id = PCI_DEVICE_ID_AMD_K8_NB_ADDRMAP;
pvt->f2_id = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL;
@@ -3788,7 +3790,6 @@ static int per_family_init(struct amd64_pvt *pvt)
break;
case 0x10:
- pvt->ctl_name = "F10h";
pvt->f1_id = PCI_DEVICE_ID_AMD_10H_NB_MAP;
pvt->f2_id = PCI_DEVICE_ID_AMD_10H_NB_DRAM;
pvt->ops->dbam_to_cs = f10_dbam_to_chip_select;
@@ -3797,12 +3798,10 @@ static int per_family_init(struct amd64_pvt *pvt)
case 0x15:
switch (pvt->model) {
case 0x30:
- pvt->ctl_name = "F15h_M30h";
pvt->f1_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F1;
pvt->f2_id = PCI_DEVICE_ID_AMD_15H_M30H_NB_F2;
break;
case 0x60:
- pvt->ctl_name = "F15h_M60h";
pvt->f1_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F1;
pvt->f2_id = PCI_DEVICE_ID_AMD_15H_M60H_NB_F2;
pvt->ops->dbam_to_cs = f15_m60h_dbam_to_chip_select;
@@ -3811,7 +3810,6 @@ static int per_family_init(struct amd64_pvt *pvt)
/* Richland is only client */
return -ENODEV;
default:
- pvt->ctl_name = "F15h";
pvt->f1_id = PCI_DEVICE_ID_AMD_15H_NB_F1;
pvt->f2_id = PCI_DEVICE_ID_AMD_15H_NB_F2;
pvt->ops->dbam_to_cs = f15_dbam_to_chip_select;
@@ -3822,12 +3820,10 @@ static int per_family_init(struct amd64_pvt *pvt)
case 0x16:
switch (pvt->model) {
case 0x30:
- pvt->ctl_name = "F16h_M30h";
pvt->f1_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F1;
pvt->f2_id = PCI_DEVICE_ID_AMD_16H_M30H_NB_F2;
break;
default:
- pvt->ctl_name = "F16h";
pvt->f1_id = PCI_DEVICE_ID_AMD_16H_NB_F1;
pvt->f2_id = PCI_DEVICE_ID_AMD_16H_NB_F2;
break;
@@ -3836,76 +3832,51 @@ static int per_family_init(struct amd64_pvt *pvt)
case 0x17:
switch (pvt->model) {
- case 0x10 ... 0x2f:
- pvt->ctl_name = "F17h_M10h";
- break;
case 0x30 ... 0x3f:
- pvt->ctl_name = "F17h_M30h";
pvt->max_mcs = 8;
break;
- case 0x60 ... 0x6f:
- pvt->ctl_name = "F17h_M60h";
- break;
- case 0x70 ... 0x7f:
- pvt->ctl_name = "F17h_M70h";
- break;
default:
- pvt->ctl_name = "F17h";
break;
}
break;
case 0x18:
- pvt->ctl_name = "F18h";
break;
case 0x19:
switch (pvt->model) {
case 0x00 ... 0x0f:
- pvt->ctl_name = "F19h";
pvt->max_mcs = 8;
break;
case 0x10 ... 0x1f:
- pvt->ctl_name = "F19h_M10h";
pvt->max_mcs = 12;
pvt->flags.zn_regs_v2 = 1;
break;
- case 0x20 ... 0x2f:
- pvt->ctl_name = "F19h_M20h";
- break;
case 0x30 ... 0x3f:
if (pvt->F3->device == PCI_DEVICE_ID_AMD_MI200_DF_F3) {
- pvt->ctl_name = "MI200";
+ tmp_name = "MI200";
pvt->max_mcs = 4;
pvt->dram_type = MEM_HBM2;
pvt->gpu_umc_base = 0x50000;
pvt->ops = &gpu_ops;
} else {
- pvt->ctl_name = "F19h_M30h";
pvt->max_mcs = 8;
}
break;
- case 0x50 ... 0x5f:
- pvt->ctl_name = "F19h_M50h";
- break;
case 0x60 ... 0x6f:
- pvt->ctl_name = "F19h_M60h";
pvt->flags.zn_regs_v2 = 1;
break;
case 0x70 ... 0x7f:
- pvt->ctl_name = "F19h_M70h";
pvt->max_mcs = 4;
pvt->flags.zn_regs_v2 = 1;
break;
case 0x90 ... 0x9f:
- pvt->ctl_name = "F19h_M90h";
pvt->max_mcs = 4;
pvt->dram_type = MEM_HBM3;
pvt->gpu_umc_base = 0x90000;
pvt->ops = &gpu_ops;
break;
case 0xa0 ... 0xaf:
- pvt->ctl_name = "F19h_MA0h";
pvt->max_mcs = 12;
pvt->flags.zn_regs_v2 = 1;
break;
@@ -3915,34 +3886,22 @@ static int per_family_init(struct amd64_pvt *pvt)
case 0x1A:
switch (pvt->model) {
case 0x00 ... 0x1f:
- pvt->ctl_name = "F1Ah";
pvt->max_mcs = 12;
pvt->flags.zn_regs_v2 = 1;
break;
case 0x40 ... 0x4f:
- pvt->ctl_name = "F1Ah_M40h";
pvt->flags.zn_regs_v2 = 1;
break;
case 0x50 ... 0x57:
- pvt->ctl_name = "F1Ah_M50h";
+ case 0xc0 ... 0xc7:
pvt->max_mcs = 16;
pvt->flags.zn_regs_v2 = 1;
break;
case 0x90 ... 0x9f:
- pvt->ctl_name = "F1Ah_M90h";
- pvt->max_mcs = 8;
- pvt->flags.zn_regs_v2 = 1;
- break;
case 0xa0 ... 0xaf:
- pvt->ctl_name = "F1Ah_MA0h";
pvt->max_mcs = 8;
pvt->flags.zn_regs_v2 = 1;
break;
- case 0xc0 ... 0xc7:
- pvt->ctl_name = "F1Ah_MC0h";
- pvt->max_mcs = 16;
- pvt->flags.zn_regs_v2 = 1;
- break;
}
break;
@@ -3951,6 +3910,16 @@ static int per_family_init(struct amd64_pvt *pvt)
return -ENODEV;
}
+ if (tmp_name)
+ scnprintf(pvt->ctl_name, sizeof(pvt->ctl_name), tmp_name);
+ else
+ scnprintf(pvt->ctl_name, sizeof(pvt->ctl_name), "F%02Xh_M%02Xh",
+ pvt->fam, pvt->model);
+
+ pvt->csels = kcalloc(pvt->max_mcs, sizeof(*pvt->csels), GFP_KERNEL);
+ if (!pvt->csels)
+ return -ENOMEM;
+
return 0;
}
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
index d70b8a8d0b09..1757c1b99fc8 100644
--- a/drivers/edac/amd64_edac.h
+++ b/drivers/edac/amd64_edac.h
@@ -96,11 +96,12 @@
/* Hardware limit on ChipSelect rows per MC and processors per system */
#define NUM_CHIPSELECTS 8
#define DRAM_RANGES 8
-#define NUM_CONTROLLERS 16
#define ON true
#define OFF false
+#define MAX_CTL_NAMELEN 19
+
/*
* PCI-defined configuration space registers
*/
@@ -346,7 +347,7 @@ struct amd64_pvt {
u32 dbam1; /* DRAM Base Address Mapping reg for DCT1 */
/* one for each DCT/UMC */
- struct chip_select csels[NUM_CONTROLLERS];
+ struct chip_select *csels;
/* DRAM base and limit pairs F1x[78,70,68,60,58,50,48,40] */
struct dram_range ranges[DRAM_RANGES];
@@ -362,7 +363,7 @@ struct amd64_pvt {
/* x4, x8, or x16 syndromes in use */
u8 ecc_sym_sz;
- const char *ctl_name;
+ char ctl_name[MAX_CTL_NAMELEN];
u16 f1_id, f2_id;
/* Maximum number of memory controllers per die/node. */
u8 max_mcs;
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index 8689631f1905..091cc6aae8a9 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -115,401 +115,6 @@ static const char * const edac_caps[] = {
[EDAC_S16ECD16ED] = "S16ECD16ED"
};
-#ifdef CONFIG_EDAC_LEGACY_SYSFS
-/*
- * EDAC sysfs CSROW data structures and methods
- */
-
-#define to_csrow(k) container_of(k, struct csrow_info, dev)
-
-/*
- * We need it to avoid namespace conflicts between the legacy API
- * and the per-dimm/per-rank one
- */
-#define DEVICE_ATTR_LEGACY(_name, _mode, _show, _store) \
- static struct device_attribute dev_attr_legacy_##_name = __ATTR(_name, _mode, _show, _store)
-
-struct dev_ch_attribute {
- struct device_attribute attr;
- unsigned int channel;
-};
-
-#define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \
- static struct dev_ch_attribute dev_attr_legacy_##_name = \
- { __ATTR(_name, _mode, _show, _store), (_var) }
-
-#define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel)
-
-/* Set of more default csrow<id> attribute show/store functions */
-static ssize_t csrow_ue_count_show(struct device *dev,
- struct device_attribute *mattr, char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
-
- return sysfs_emit(data, "%u\n", csrow->ue_count);
-}
-
-static ssize_t csrow_ce_count_show(struct device *dev,
- struct device_attribute *mattr, char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
-
- return sysfs_emit(data, "%u\n", csrow->ce_count);
-}
-
-static ssize_t csrow_size_show(struct device *dev,
- struct device_attribute *mattr, char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
- int i;
- u32 nr_pages = 0;
-
- for (i = 0; i < csrow->nr_channels; i++)
- nr_pages += csrow->channels[i]->dimm->nr_pages;
- return sysfs_emit(data, "%u\n", PAGES_TO_MiB(nr_pages));
-}
-
-static ssize_t csrow_mem_type_show(struct device *dev,
- struct device_attribute *mattr, char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
-
- return sysfs_emit(data, "%s\n", edac_mem_types[csrow->channels[0]->dimm->mtype]);
-}
-
-static ssize_t csrow_dev_type_show(struct device *dev,
- struct device_attribute *mattr, char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
-
- return sysfs_emit(data, "%s\n", dev_types[csrow->channels[0]->dimm->dtype]);
-}
-
-static ssize_t csrow_edac_mode_show(struct device *dev,
- struct device_attribute *mattr,
- char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
-
- return sysfs_emit(data, "%s\n", edac_caps[csrow->channels[0]->dimm->edac_mode]);
-}
-
-/* show/store functions for DIMM Label attributes */
-static ssize_t channel_dimm_label_show(struct device *dev,
- struct device_attribute *mattr,
- char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
- unsigned int chan = to_channel(mattr);
- struct rank_info *rank = csrow->channels[chan];
-
- /* if field has not been initialized, there is nothing to send */
- if (!rank->dimm->label[0])
- return 0;
-
- return sysfs_emit(data, "%s\n", rank->dimm->label);
-}
-
-static ssize_t channel_dimm_label_store(struct device *dev,
- struct device_attribute *mattr,
- const char *data, size_t count)
-{
- struct csrow_info *csrow = to_csrow(dev);
- unsigned int chan = to_channel(mattr);
- struct rank_info *rank = csrow->channels[chan];
- size_t copy_count = count;
-
- if (count == 0)
- return -EINVAL;
-
- if (data[count - 1] == '\0' || data[count - 1] == '\n')
- copy_count -= 1;
-
- if (copy_count == 0 || copy_count >= sizeof(rank->dimm->label))
- return -EINVAL;
-
- memcpy(rank->dimm->label, data, copy_count);
- rank->dimm->label[copy_count] = '\0';
-
- return count;
-}
-
-/* show function for dynamic chX_ce_count attribute */
-static ssize_t channel_ce_count_show(struct device *dev,
- struct device_attribute *mattr, char *data)
-{
- struct csrow_info *csrow = to_csrow(dev);
- unsigned int chan = to_channel(mattr);
- struct rank_info *rank = csrow->channels[chan];
-
- return sysfs_emit(data, "%u\n", rank->ce_count);
-}
-
-/* cwrow<id>/attribute files */
-DEVICE_ATTR_LEGACY(size_mb, S_IRUGO, csrow_size_show, NULL);
-DEVICE_ATTR_LEGACY(dev_type, S_IRUGO, csrow_dev_type_show, NULL);
-DEVICE_ATTR_LEGACY(mem_type, S_IRUGO, csrow_mem_type_show, NULL);
-DEVICE_ATTR_LEGACY(edac_mode, S_IRUGO, csrow_edac_mode_show, NULL);
-DEVICE_ATTR_LEGACY(ue_count, S_IRUGO, csrow_ue_count_show, NULL);
-DEVICE_ATTR_LEGACY(ce_count, S_IRUGO, csrow_ce_count_show, NULL);
-
-/* default attributes of the CSROW<id> object */
-static struct attribute *csrow_attrs[] = {
- &dev_attr_legacy_dev_type.attr,
- &dev_attr_legacy_mem_type.attr,
- &dev_attr_legacy_edac_mode.attr,
- &dev_attr_legacy_size_mb.attr,
- &dev_attr_legacy_ue_count.attr,
- &dev_attr_legacy_ce_count.attr,
- NULL,
-};
-
-static const struct attribute_group csrow_attr_grp = {
- .attrs = csrow_attrs,
-};
-
-static const struct attribute_group *csrow_attr_groups[] = {
- &csrow_attr_grp,
- NULL
-};
-
-static const struct device_type csrow_attr_type = {
- .groups = csrow_attr_groups,
-};
-
-/*
- * possible dynamic channel DIMM Label attribute files
- *
- */
-DEVICE_CHANNEL(ch0_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 0);
-DEVICE_CHANNEL(ch1_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 1);
-DEVICE_CHANNEL(ch2_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 2);
-DEVICE_CHANNEL(ch3_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 3);
-DEVICE_CHANNEL(ch4_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 4);
-DEVICE_CHANNEL(ch5_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 5);
-DEVICE_CHANNEL(ch6_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 6);
-DEVICE_CHANNEL(ch7_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 7);
-DEVICE_CHANNEL(ch8_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 8);
-DEVICE_CHANNEL(ch9_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 9);
-DEVICE_CHANNEL(ch10_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 10);
-DEVICE_CHANNEL(ch11_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 11);
-DEVICE_CHANNEL(ch12_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 12);
-DEVICE_CHANNEL(ch13_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 13);
-DEVICE_CHANNEL(ch14_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 14);
-DEVICE_CHANNEL(ch15_dimm_label, S_IRUGO | S_IWUSR,
- channel_dimm_label_show, channel_dimm_label_store, 15);
-
-/* Total possible dynamic DIMM Label attribute file table */
-static struct attribute *dynamic_csrow_dimm_attr[] = {
- &dev_attr_legacy_ch0_dimm_label.attr.attr,
- &dev_attr_legacy_ch1_dimm_label.attr.attr,
- &dev_attr_legacy_ch2_dimm_label.attr.attr,
- &dev_attr_legacy_ch3_dimm_label.attr.attr,
- &dev_attr_legacy_ch4_dimm_label.attr.attr,
- &dev_attr_legacy_ch5_dimm_label.attr.attr,
- &dev_attr_legacy_ch6_dimm_label.attr.attr,
- &dev_attr_legacy_ch7_dimm_label.attr.attr,
- &dev_attr_legacy_ch8_dimm_label.attr.attr,
- &dev_attr_legacy_ch9_dimm_label.attr.attr,
- &dev_attr_legacy_ch10_dimm_label.attr.attr,
- &dev_attr_legacy_ch11_dimm_label.attr.attr,
- &dev_attr_legacy_ch12_dimm_label.attr.attr,
- &dev_attr_legacy_ch13_dimm_label.attr.attr,
- &dev_attr_legacy_ch14_dimm_label.attr.attr,
- &dev_attr_legacy_ch15_dimm_label.attr.attr,
- NULL
-};
-
-/* possible dynamic channel ce_count attribute files */
-DEVICE_CHANNEL(ch0_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 0);
-DEVICE_CHANNEL(ch1_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 1);
-DEVICE_CHANNEL(ch2_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 2);
-DEVICE_CHANNEL(ch3_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 3);
-DEVICE_CHANNEL(ch4_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 4);
-DEVICE_CHANNEL(ch5_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 5);
-DEVICE_CHANNEL(ch6_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 6);
-DEVICE_CHANNEL(ch7_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 7);
-DEVICE_CHANNEL(ch8_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 8);
-DEVICE_CHANNEL(ch9_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 9);
-DEVICE_CHANNEL(ch10_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 10);
-DEVICE_CHANNEL(ch11_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 11);
-DEVICE_CHANNEL(ch12_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 12);
-DEVICE_CHANNEL(ch13_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 13);
-DEVICE_CHANNEL(ch14_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 14);
-DEVICE_CHANNEL(ch15_ce_count, S_IRUGO,
- channel_ce_count_show, NULL, 15);
-
-/* Total possible dynamic ce_count attribute file table */
-static struct attribute *dynamic_csrow_ce_count_attr[] = {
- &dev_attr_legacy_ch0_ce_count.attr.attr,
- &dev_attr_legacy_ch1_ce_count.attr.attr,
- &dev_attr_legacy_ch2_ce_count.attr.attr,
- &dev_attr_legacy_ch3_ce_count.attr.attr,
- &dev_attr_legacy_ch4_ce_count.attr.attr,
- &dev_attr_legacy_ch5_ce_count.attr.attr,
- &dev_attr_legacy_ch6_ce_count.attr.attr,
- &dev_attr_legacy_ch7_ce_count.attr.attr,
- &dev_attr_legacy_ch8_ce_count.attr.attr,
- &dev_attr_legacy_ch9_ce_count.attr.attr,
- &dev_attr_legacy_ch10_ce_count.attr.attr,
- &dev_attr_legacy_ch11_ce_count.attr.attr,
- &dev_attr_legacy_ch12_ce_count.attr.attr,
- &dev_attr_legacy_ch13_ce_count.attr.attr,
- &dev_attr_legacy_ch14_ce_count.attr.attr,
- &dev_attr_legacy_ch15_ce_count.attr.attr,
- NULL
-};
-
-static umode_t csrow_dev_is_visible(struct kobject *kobj,
- struct attribute *attr, int idx)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct csrow_info *csrow = container_of(dev, struct csrow_info, dev);
-
- if (idx >= csrow->nr_channels)
- return 0;
-
- if (idx >= ARRAY_SIZE(dynamic_csrow_ce_count_attr) - 1) {
- WARN_ONCE(1, "idx: %d\n", idx);
- return 0;
- }
-
- /* Only expose populated DIMMs */
- if (!csrow->channels[idx]->dimm->nr_pages)
- return 0;
-
- return attr->mode;
-}
-
-
-static const struct attribute_group csrow_dev_dimm_group = {
- .attrs = dynamic_csrow_dimm_attr,
- .is_visible = csrow_dev_is_visible,
-};
-
-static const struct attribute_group csrow_dev_ce_count_group = {
- .attrs = dynamic_csrow_ce_count_attr,
- .is_visible = csrow_dev_is_visible,
-};
-
-static const struct attribute_group *csrow_dev_groups[] = {
- &csrow_dev_dimm_group,
- &csrow_dev_ce_count_group,
- NULL
-};
-
-static void csrow_release(struct device *dev)
-{
- /*
- * Nothing to do, just unregister sysfs here. The mci
- * device owns the data and will also release it.
- */
-}
-
-static inline int nr_pages_per_csrow(struct csrow_info *csrow)
-{
- int chan, nr_pages = 0;
-
- for (chan = 0; chan < csrow->nr_channels; chan++)
- nr_pages += csrow->channels[chan]->dimm->nr_pages;
-
- return nr_pages;
-}
-
-/* Create a CSROW object under specified edac_mc_device */
-static int edac_create_csrow_object(struct mem_ctl_info *mci,
- struct csrow_info *csrow, int index)
-{
- int err;
-
- csrow->dev.type = &csrow_attr_type;
- csrow->dev.groups = csrow_dev_groups;
- csrow->dev.release = csrow_release;
- device_initialize(&csrow->dev);
- csrow->dev.parent = &mci->dev;
- csrow->mci = mci;
- dev_set_name(&csrow->dev, "csrow%d", index);
- dev_set_drvdata(&csrow->dev, csrow);
-
- err = device_add(&csrow->dev);
- if (err) {
- edac_dbg(1, "failure: create device %s\n", dev_name(&csrow->dev));
- put_device(&csrow->dev);
- return err;
- }
-
- edac_dbg(0, "device %s created\n", dev_name(&csrow->dev));
-
- return 0;
-}
-
-/* Create a CSROW object under specified edac_mc_device */
-static int edac_create_csrow_objects(struct mem_ctl_info *mci)
-{
- int err, i;
- struct csrow_info *csrow;
-
- for (i = 0; i < mci->nr_csrows; i++) {
- csrow = mci->csrows[i];
- if (!nr_pages_per_csrow(csrow))
- continue;
- err = edac_create_csrow_object(mci, mci->csrows[i], i);
- if (err < 0)
- goto error;
- }
- return 0;
-
-error:
- for (--i; i >= 0; i--) {
- if (device_is_registered(&mci->csrows[i]->dev))
- device_unregister(&mci->csrows[i]->dev);
- }
-
- return err;
-}
-
-static void edac_delete_csrow_objects(struct mem_ctl_info *mci)
-{
- int i;
-
- for (i = 0; i < mci->nr_csrows; i++) {
- if (device_is_registered(&mci->csrows[i]->dev))
- device_unregister(&mci->csrows[i]->dev);
- }
-}
-
-#endif
-
/*
* Per-dimm (or per-rank) devices
*/
@@ -989,12 +594,6 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci,
goto fail;
}
-#ifdef CONFIG_EDAC_LEGACY_SYSFS
- err = edac_create_csrow_objects(mci);
- if (err < 0)
- goto fail;
-#endif
-
edac_create_debugfs_nodes(mci);
return 0;
@@ -1019,9 +618,6 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
#ifdef CONFIG_EDAC_DEBUG
edac_debugfs_remove_recursive(mci->debugfs);
#endif
-#ifdef CONFIG_EDAC_LEGACY_SYSFS
- edac_delete_csrow_objects(mci);
-#endif
mci_for_each_dimm(mci, dimm) {
if (!device_is_registered(&dimm->dev))
diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c
index 1eb0136c6fbd..d80c88818691 100644
--- a/drivers/edac/ghes_edac.c
+++ b/drivers/edac/ghes_edac.c
@@ -15,6 +15,7 @@
#include "edac_module.h"
#include <ras/ras_event.h>
#include <linux/notifier.h>
+#include <linux/string.h>
#define OTHER_DETAIL_LEN 400
@@ -332,7 +333,7 @@ static int ghes_edac_report_mem_error(struct notifier_block *nb,
p = pvt->msg;
p += snprintf(p, sizeof(pvt->msg), "%s", cper_mem_err_type_str(etype));
} else {
- strcpy(pvt->msg, "unknown error");
+ strscpy(pvt->msg, "unknown error");
}
/* Error address */
@@ -357,14 +358,14 @@ static int ghes_edac_report_mem_error(struct notifier_block *nb,
dimm = find_dimm_by_handle(mci, mem_err->mem_dev_handle);
if (dimm) {
e->top_layer = dimm->idx;
- strcpy(e->label, dimm->label);
+ strscpy(e->label, dimm->label);
}
}
if (p > e->location)
*(p - 1) = '\0';
if (!*e->label)
- strcpy(e->label, "unknown memory");
+ strscpy(e->label, "unknown memory");
/* All other fields are mapped on e->other_detail */
p = pvt->other_detail;
diff --git a/drivers/edac/i10nm_base.c b/drivers/edac/i10nm_base.c
index 2010a47149f4..89b3e8cc38b1 100644
--- a/drivers/edac/i10nm_base.c
+++ b/drivers/edac/i10nm_base.c
@@ -1198,7 +1198,8 @@ static int __init i10nm_init(void)
d->imc[i].num_dimms = cfg->ddr_dimm_num;
}
- rc = skx_register_mci(&d->imc[i], d->imc[i].mdev,
+ rc = skx_register_mci(&d->imc[i], &d->imc[i].mdev->dev,
+ pci_name(d->imc[i].mdev),
"Intel_10nm Socket", EDAC_MOD_STR,
i10nm_get_dimm_config, cfg);
if (rc < 0)
diff --git a/drivers/edac/ie31200_edac.c b/drivers/edac/ie31200_edac.c
index 5a080ab65476..8d4ddaa85ae8 100644
--- a/drivers/edac/ie31200_edac.c
+++ b/drivers/edac/ie31200_edac.c
@@ -526,6 +526,7 @@ static int ie31200_register_mci(struct pci_dev *pdev, struct res_config *cfg, in
ie31200_pvt.priv[mc] = priv;
return 0;
fail_unmap:
+ put_device(&priv->dev);
iounmap(window);
fail_free:
edac_mc_free(mci);
@@ -598,6 +599,7 @@ static void ie31200_unregister_mcis(void)
mci = priv->mci;
edac_mc_del_mc(mci->pdev);
iounmap(priv->window);
+ put_device(&priv->dev);
edac_mc_free(mci);
}
}
diff --git a/drivers/edac/igen6_edac.c b/drivers/edac/igen6_edac.c
index 2fc59f9eed69..553c31a2d922 100644
--- a/drivers/edac/igen6_edac.c
+++ b/drivers/edac/igen6_edac.c
@@ -1300,6 +1300,7 @@ static int igen6_register_mci(int mc, void __iomem *window, struct pci_dev *pdev
imc->mci = mci;
return 0;
fail3:
+ put_device(&imc->dev);
mci->pvt_info = NULL;
kfree(mci->ctl_name);
fail2:
@@ -1326,6 +1327,7 @@ static void igen6_unregister_mcis(void)
kfree(mci->ctl_name);
mci->pvt_info = NULL;
edac_mc_free(mci);
+ put_device(&imc->dev);
iounmap(imc->window);
}
}
diff --git a/drivers/edac/imh_base.c b/drivers/edac/imh_base.c
new file mode 100644
index 000000000000..4348b3883b45
--- /dev/null
+++ b/drivers/edac/imh_base.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Intel(R) servers with Integrated Memory/IO Hub-based memory controller.
+ * Copyright (c) 2025, Intel Corporation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+#include <asm/mce.h>
+#include <asm/cpu.h>
+#include "edac_module.h"
+#include "skx_common.h"
+
+#define IMH_REVISION "v0.0.1"
+#define EDAC_MOD_STR "imh_edac"
+
+/* Debug macros */
+#define imh_printk(level, fmt, arg...) \
+ edac_printk(level, "imh", fmt, ##arg)
+
+/* Configuration Agent(Ubox) */
+#define MMIO_BASE_H(reg) (((u64)GET_BITFIELD(reg, 0, 29)) << 23)
+#define SOCKET_ID(reg) GET_BITFIELD(reg, 0, 3)
+
+/* PUNIT */
+#define DDR_IMC_BITMAP(reg) GET_BITFIELD(reg, 23, 30)
+
+/* Memory Controller */
+#define ECC_ENABLED(reg) GET_BITFIELD(reg, 2, 2)
+#define DIMM_POPULATED(reg) GET_BITFIELD(reg, 15, 15)
+
+/* System Cache Agent(SCA) */
+#define TOLM(reg) (((u64)GET_BITFIELD(reg, 16, 31)) << 16)
+#define TOHM(reg) (((u64)GET_BITFIELD(reg, 16, 51)) << 16)
+
+/* Home Agent (HA) */
+#define NMCACHING(reg) GET_BITFIELD(reg, 8, 8)
+
+/**
+ * struct local_reg - A register as described in the local package view.
+ *
+ * @pkg: (input) The package where the register is located.
+ * @pbase: (input) The IP MMIO base physical address in the local package view.
+ * @size: (input) The IP MMIO size.
+ * @offset: (input) The register offset from the IP MMIO base @pbase.
+ * @width: (input) The register width in byte.
+ * @vbase: (internal) The IP MMIO base virtual address.
+ * @val: (output) The register value.
+ */
+struct local_reg {
+ int pkg;
+ u64 pbase;
+ u32 size;
+ u32 offset;
+ u8 width;
+ void __iomem *vbase;
+ u64 val;
+};
+
+#define DEFINE_LOCAL_REG(name, cfg, package, north, ip_name, ip_idx, reg_name) \
+ struct local_reg name = { \
+ .pkg = package, \
+ .pbase = (north ? (cfg)->mmio_base_l_north : \
+ (cfg)->mmio_base_l_south) + \
+ (cfg)->ip_name##_base + \
+ (cfg)->ip_name##_size * (ip_idx), \
+ .size = (cfg)->ip_name##_size, \
+ .offset = (cfg)->ip_name##_reg_##reg_name##_offset, \
+ .width = (cfg)->ip_name##_reg_##reg_name##_width, \
+ }
+
+static u64 readx(void __iomem *addr, u8 width)
+{
+ switch (width) {
+ case 1:
+ return readb(addr);
+ case 2:
+ return readw(addr);
+ case 4:
+ return readl(addr);
+ case 8:
+ return readq(addr);
+ default:
+ imh_printk(KERN_ERR, "Invalid reg 0x%p width %d\n", addr, width);
+ return 0;
+ }
+}
+
+static void __read_local_reg(void *reg)
+{
+ struct local_reg *r = (struct local_reg *)reg;
+
+ r->val = readx(r->vbase + r->offset, r->width);
+}
+
+/* Read a local-view register. */
+static bool read_local_reg(struct local_reg *reg)
+{
+ int cpu;
+
+ /* Get the target CPU in the package @reg->pkg. */
+ for_each_online_cpu(cpu) {
+ if (reg->pkg == topology_physical_package_id(cpu))
+ break;
+ }
+
+ if (cpu >= nr_cpu_ids)
+ return false;
+
+ reg->vbase = ioremap(reg->pbase, reg->size);
+ if (!reg->vbase) {
+ imh_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", reg->pbase);
+ return false;
+ }
+
+ /* Get the target CPU to read the register. */
+ smp_call_function_single(cpu, __read_local_reg, reg, 1);
+ iounmap(reg->vbase);
+
+ return true;
+}
+
+/* Get the bitmap of memory controller instances in package @pkg. */
+static u32 get_imc_bitmap(struct res_config *cfg, int pkg, bool north)
+{
+ DEFINE_LOCAL_REG(reg, cfg, pkg, north, pcu, 0, capid3);
+
+ if (!read_local_reg(&reg))
+ return 0;
+
+ edac_dbg(2, "Pkg%d %s mc instances bitmap 0x%llx (reg 0x%llx)\n",
+ pkg, north ? "north" : "south",
+ DDR_IMC_BITMAP(reg.val), reg.val);
+
+ return DDR_IMC_BITMAP(reg.val);
+}
+
+static void imc_release(struct device *dev)
+{
+ edac_dbg(2, "imc device %s released\n", dev_name(dev));
+ kfree(dev);
+}
+
+static int __get_ddr_munits(struct res_config *cfg, struct skx_dev *d,
+ bool north, int lmc)
+{
+ unsigned long size = cfg->ddr_chan_mmio_sz * cfg->ddr_chan_num;
+ unsigned long bitmap = get_imc_bitmap(cfg, d->pkg, north);
+ void __iomem *mbase;
+ struct device *dev;
+ int i, rc, pmc;
+ u64 base;
+
+ for_each_set_bit(i, &bitmap, sizeof(bitmap) * 8) {
+ base = north ? d->mmio_base_h_north : d->mmio_base_h_south;
+ base += cfg->ddr_imc_base + size * i;
+
+ edac_dbg(2, "Pkg%d mc%d mmio base 0x%llx size 0x%lx\n",
+ d->pkg, lmc, base, size);
+
+ /* Set up the imc MMIO. */
+ mbase = ioremap(base, size);
+ if (!mbase) {
+ imh_printk(KERN_ERR, "Failed to ioremap 0x%llx\n", base);
+ return -ENOMEM;
+ }
+
+ d->imc[lmc].mbase = mbase;
+ d->imc[lmc].lmc = lmc;
+
+ /* Create the imc device instance. */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->release = imc_release;
+ device_initialize(dev);
+ rc = dev_set_name(dev, "0x%llx", base);
+ if (rc) {
+ imh_printk(KERN_ERR, "Failed to set dev name\n");
+ put_device(dev);
+ return rc;
+ }
+
+ d->imc[lmc].dev = dev;
+
+ /* Set up the imc index mapping. */
+ pmc = north ? i : 8 + i;
+ skx_set_mc_mapping(d, pmc, lmc);
+
+ lmc++;
+ }
+
+ return lmc;
+}
+
+static bool get_ddr_munits(struct res_config *cfg, struct skx_dev *d)
+{
+ int lmc = __get_ddr_munits(cfg, d, true, 0);
+
+ if (lmc < 0)
+ return false;
+
+ lmc = __get_ddr_munits(cfg, d, false, lmc);
+ if (lmc <= 0)
+ return false;
+
+ return true;
+}
+
+static bool get_socket_id(struct res_config *cfg, struct skx_dev *d)
+{
+ DEFINE_LOCAL_REG(reg, cfg, d->pkg, true, ubox, 0, socket_id);
+ u8 src_id;
+ int i;
+
+ if (!read_local_reg(&reg))
+ return false;
+
+ src_id = SOCKET_ID(reg.val);
+ edac_dbg(2, "socket id 0x%x (reg 0x%llx)\n", src_id, reg.val);
+
+ for (i = 0; i < cfg->ddr_imc_num; i++)
+ d->imc[i].src_id = src_id;
+
+ return true;
+}
+
+/* Get TOLM (Top Of Low Memory) and TOHM (Top Of High Memory) parameters. */
+static bool imh_get_tolm_tohm(struct res_config *cfg, u64 *tolm, u64 *tohm)
+{
+ DEFINE_LOCAL_REG(reg, cfg, 0, true, sca, 0, tolm);
+
+ if (!read_local_reg(&reg))
+ return false;
+
+ *tolm = TOLM(reg.val);
+ edac_dbg(2, "tolm 0x%llx (reg 0x%llx)\n", *tolm, reg.val);
+
+ DEFINE_LOCAL_REG(reg2, cfg, 0, true, sca, 0, tohm);
+
+ if (!read_local_reg(&reg2))
+ return false;
+
+ *tohm = TOHM(reg2.val);
+ edac_dbg(2, "tohm 0x%llx (reg 0x%llx)\n", *tohm, reg2.val);
+
+ return true;
+}
+
+/* Get the system-view MMIO_BASE_H for {north,south}-IMH. */
+static int imh_get_all_mmio_base_h(struct res_config *cfg, struct list_head *edac_list)
+{
+ int i, n = topology_max_packages(), imc_num = cfg->ddr_imc_num + cfg->hbm_imc_num;
+ struct skx_dev *d;
+
+ for (i = 0; i < n; i++) {
+ d = kzalloc(struct_size(d, imc, imc_num), GFP_KERNEL);
+ if (!d)
+ return -ENOMEM;
+
+ DEFINE_LOCAL_REG(reg, cfg, i, true, ubox, 0, mmio_base);
+
+ /* Get MMIO_BASE_H for the north-IMH. */
+ if (!read_local_reg(&reg) || !reg.val) {
+ kfree(d);
+ imh_printk(KERN_ERR, "Pkg%d has no north mmio_base_h\n", i);
+ return -ENODEV;
+ }
+
+ d->mmio_base_h_north = MMIO_BASE_H(reg.val);
+ edac_dbg(2, "Pkg%d north mmio_base_h 0x%llx (reg 0x%llx)\n",
+ i, d->mmio_base_h_north, reg.val);
+
+ /* Get MMIO_BASE_H for the south-IMH (optional). */
+ DEFINE_LOCAL_REG(reg2, cfg, i, false, ubox, 0, mmio_base);
+
+ if (read_local_reg(&reg2)) {
+ d->mmio_base_h_south = MMIO_BASE_H(reg2.val);
+ edac_dbg(2, "Pkg%d south mmio_base_h 0x%llx (reg 0x%llx)\n",
+ i, d->mmio_base_h_south, reg2.val);
+ }
+
+ d->pkg = i;
+ d->num_imc = imc_num;
+ skx_init_mc_mapping(d);
+ list_add_tail(&d->list, edac_list);
+ }
+
+ return 0;
+}
+
+/* Get the number of per-package memory controllers. */
+static int imh_get_imc_num(struct res_config *cfg)
+{
+ int imc_num = hweight32(get_imc_bitmap(cfg, 0, true)) +
+ hweight32(get_imc_bitmap(cfg, 0, false));
+
+ if (!imc_num) {
+ imh_printk(KERN_ERR, "Invalid mc number\n");
+ return -ENODEV;
+ }
+
+ if (cfg->ddr_imc_num != imc_num) {
+ /*
+ * Update the configuration data to reflect the number of
+ * present DDR memory controllers.
+ */
+ cfg->ddr_imc_num = imc_num;
+ edac_dbg(2, "Set ddr mc number %d\n", imc_num);
+ }
+
+ return 0;
+}
+
+/* Get all memory controllers' parameters. */
+static int imh_get_munits(struct res_config *cfg, struct list_head *edac_list)
+{
+ struct skx_imc *imc;
+ struct skx_dev *d;
+ u8 mc = 0;
+ int i;
+
+ list_for_each_entry(d, edac_list, list) {
+ if (!get_ddr_munits(cfg, d)) {
+ imh_printk(KERN_ERR, "No mc found\n");
+ return -ENODEV;
+ }
+
+ if (!get_socket_id(cfg, d)) {
+ imh_printk(KERN_ERR, "Failed to get socket id\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < cfg->ddr_imc_num; i++) {
+ imc = &d->imc[i];
+ if (!imc->mbase)
+ continue;
+
+ imc->chan_mmio_sz = cfg->ddr_chan_mmio_sz;
+ imc->num_channels = cfg->ddr_chan_num;
+ imc->num_dimms = cfg->ddr_dimm_num;
+ imc->mc = mc++;
+ }
+ }
+
+ return 0;
+}
+
+static bool check_2lm_enabled(struct res_config *cfg, struct skx_dev *d, int ha_idx)
+{
+ DEFINE_LOCAL_REG(reg, cfg, d->pkg, true, ha, ha_idx, mode);
+
+ if (!read_local_reg(&reg))
+ return false;
+
+ if (!NMCACHING(reg.val))
+ return false;
+
+ edac_dbg(2, "2-level memory configuration (reg 0x%llx, ha idx %d)\n", reg.val, ha_idx);
+ return true;
+}
+
+/* Check whether the system has a 2-level memory configuration. */
+static bool imh_2lm_enabled(struct res_config *cfg, struct list_head *head)
+{
+ struct skx_dev *d;
+ int i;
+
+ list_for_each_entry(d, head, list) {
+ for (i = 0; i < cfg->ddr_imc_num; i++)
+ if (check_2lm_enabled(cfg, d, i))
+ return true;
+ }
+
+ return false;
+}
+
+/* Helpers to read memory controller registers */
+static u64 read_imc_reg(struct skx_imc *imc, int chan, u32 offset, u8 width)
+{
+ return readx(imc->mbase + imc->chan_mmio_sz * chan + offset, width);
+}
+
+static u32 read_imc_mcmtr(struct res_config *cfg, struct skx_imc *imc, int chan)
+{
+ return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_mcmtr_offset, cfg->ddr_reg_mcmtr_width);
+}
+
+static u32 read_imc_dimmmtr(struct res_config *cfg, struct skx_imc *imc, int chan, int dimm)
+{
+ return (u32)read_imc_reg(imc, chan, cfg->ddr_reg_dimmmtr_offset +
+ cfg->ddr_reg_dimmmtr_width * dimm,
+ cfg->ddr_reg_dimmmtr_width);
+}
+
+static bool ecc_enabled(u32 mcmtr)
+{
+ return (bool)ECC_ENABLED(mcmtr);
+}
+
+static bool dimm_populated(u32 dimmmtr)
+{
+ return (bool)DIMM_POPULATED(dimmmtr);
+}
+
+/* Get each DIMM's configurations of the memory controller @mci. */
+static int imh_get_dimm_config(struct mem_ctl_info *mci, struct res_config *cfg)
+{
+ struct skx_pvt *pvt = mci->pvt_info;
+ struct skx_imc *imc = pvt->imc;
+ struct dimm_info *dimm;
+ u32 mcmtr, dimmmtr;
+ int i, j, ndimms;
+
+ for (i = 0; i < imc->num_channels; i++) {
+ if (!imc->mbase)
+ continue;
+
+ mcmtr = read_imc_mcmtr(cfg, imc, i);
+
+ for (ndimms = 0, j = 0; j < imc->num_dimms; j++) {
+ dimmmtr = read_imc_dimmmtr(cfg, imc, i, j);
+ edac_dbg(1, "mcmtr 0x%x dimmmtr 0x%x (mc%d ch%d dimm%d)\n",
+ mcmtr, dimmmtr, imc->mc, i, j);
+
+ if (!dimm_populated(dimmmtr))
+ continue;
+
+ dimm = edac_get_dimm(mci, i, j, 0);
+ ndimms += skx_get_dimm_info(dimmmtr, 0, 0, dimm,
+ imc, i, j, cfg);
+ }
+
+ if (ndimms && !ecc_enabled(mcmtr)) {
+ imh_printk(KERN_ERR, "ECC is disabled on mc%d ch%d\n",
+ imc->mc, i);
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+/* Register all memory controllers to the EDAC core. */
+static int imh_register_mci(struct res_config *cfg, struct list_head *edac_list)
+{
+ struct skx_imc *imc;
+ struct skx_dev *d;
+ int i, rc;
+
+ list_for_each_entry(d, edac_list, list) {
+ for (i = 0; i < cfg->ddr_imc_num; i++) {
+ imc = &d->imc[i];
+ if (!imc->mbase)
+ continue;
+
+ rc = skx_register_mci(imc, imc->dev,
+ dev_name(imc->dev),
+ "Intel IMH-based Socket",
+ EDAC_MOD_STR,
+ imh_get_dimm_config, cfg);
+ if (rc)
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static struct res_config dmr_cfg = {
+ .type = DMR,
+ .support_ddr5 = true,
+ .mmio_base_l_north = 0xf6800000,
+ .mmio_base_l_south = 0xf6000000,
+ .ddr_chan_num = 1,
+ .ddr_dimm_num = 2,
+ .ddr_imc_base = 0x39b000,
+ .ddr_chan_mmio_sz = 0x8000,
+ .ddr_reg_mcmtr_offset = 0x360,
+ .ddr_reg_mcmtr_width = 4,
+ .ddr_reg_dimmmtr_offset = 0x370,
+ .ddr_reg_dimmmtr_width = 4,
+ .ubox_base = 0x0,
+ .ubox_size = 0x2000,
+ .ubox_reg_mmio_base_offset = 0x580,
+ .ubox_reg_mmio_base_width = 4,
+ .ubox_reg_socket_id_offset = 0x1080,
+ .ubox_reg_socket_id_width = 4,
+ .pcu_base = 0x3000,
+ .pcu_size = 0x10000,
+ .pcu_reg_capid3_offset = 0x290,
+ .pcu_reg_capid3_width = 4,
+ .sca_base = 0x24c000,
+ .sca_size = 0x2500,
+ .sca_reg_tolm_offset = 0x2100,
+ .sca_reg_tolm_width = 8,
+ .sca_reg_tohm_offset = 0x2108,
+ .sca_reg_tohm_width = 8,
+ .ha_base = 0x3eb000,
+ .ha_size = 0x1000,
+ .ha_reg_mode_offset = 0x4a0,
+ .ha_reg_mode_width = 4,
+};
+
+static const struct x86_cpu_id imh_cpuids[] = {
+ X86_MATCH_VFM(INTEL_DIAMONDRAPIDS_X, &dmr_cfg),
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, imh_cpuids);
+
+static struct notifier_block imh_mce_dec = {
+ .notifier_call = skx_mce_check_error,
+ .priority = MCE_PRIO_EDAC,
+};
+
+static int __init imh_init(void)
+{
+ const struct x86_cpu_id *id;
+ struct list_head *edac_list;
+ struct res_config *cfg;
+ const char *owner;
+ u64 tolm, tohm;
+ int rc;
+
+ edac_dbg(2, "\n");
+
+ if (ghes_get_devices())
+ return -EBUSY;
+
+ owner = edac_get_owner();
+ if (owner && strncmp(owner, EDAC_MOD_STR, sizeof(EDAC_MOD_STR)))
+ return -EBUSY;
+
+ if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
+ return -ENODEV;
+
+ id = x86_match_cpu(imh_cpuids);
+ if (!id)
+ return -ENODEV;
+ cfg = (struct res_config *)id->driver_data;
+ skx_set_res_cfg(cfg);
+
+ if (!imh_get_tolm_tohm(cfg, &tolm, &tohm))
+ return -ENODEV;
+
+ skx_set_hi_lo(tolm, tohm);
+
+ rc = imh_get_imc_num(cfg);
+ if (rc < 0)
+ goto fail;
+
+ edac_list = skx_get_edac_list();
+
+ rc = imh_get_all_mmio_base_h(cfg, edac_list);
+ if (rc)
+ goto fail;
+
+ rc = imh_get_munits(cfg, edac_list);
+ if (rc)
+ goto fail;
+
+ skx_set_mem_cfg(imh_2lm_enabled(cfg, edac_list));
+
+ rc = imh_register_mci(cfg, edac_list);
+ if (rc)
+ goto fail;
+
+ rc = skx_adxl_get();
+ if (rc)
+ goto fail;
+
+ opstate_init();
+ mce_register_decode_chain(&imh_mce_dec);
+ skx_setup_debug("imh_test");
+
+ imh_printk(KERN_INFO, "%s\n", IMH_REVISION);
+
+ return 0;
+fail:
+ skx_remove();
+ return rc;
+}
+
+static void __exit imh_exit(void)
+{
+ edac_dbg(2, "\n");
+
+ skx_teardown_debug();
+ mce_unregister_decode_chain(&imh_mce_dec);
+ skx_adxl_put();
+ skx_remove();
+}
+
+module_init(imh_init);
+module_exit(imh_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Qiuxu Zhuo");
+MODULE_DESCRIPTION("MC Driver for Intel servers using IMH-based memory controller");
diff --git a/drivers/edac/skx_base.c b/drivers/edac/skx_base.c
index 078ddf95cc6e..aa6593ccda2d 100644
--- a/drivers/edac/skx_base.c
+++ b/drivers/edac/skx_base.c
@@ -662,8 +662,8 @@ static int __init skx_init(void)
d->imc[i].src_id = src_id;
d->imc[i].num_channels = cfg->ddr_chan_num;
d->imc[i].num_dimms = cfg->ddr_dimm_num;
-
- rc = skx_register_mci(&d->imc[i], d->imc[i].chan[0].cdev,
+ rc = skx_register_mci(&d->imc[i], &d->imc[i].chan[0].cdev->dev,
+ pci_name(d->imc[i].chan[0].cdev),
"Skylake Socket", EDAC_MOD_STR,
skx_get_dimm_config, cfg);
if (rc < 0)
diff --git a/drivers/edac/skx_common.c b/drivers/edac/skx_common.c
index 724842f512ac..3276afe43922 100644
--- a/drivers/edac/skx_common.c
+++ b/drivers/edac/skx_common.c
@@ -124,7 +124,7 @@ void skx_adxl_put(void)
}
EXPORT_SYMBOL_GPL(skx_adxl_put);
-static void skx_init_mc_mapping(struct skx_dev *d)
+void skx_init_mc_mapping(struct skx_dev *d)
{
/*
* By default, the BIOS presents all memory controllers within each
@@ -135,6 +135,7 @@ static void skx_init_mc_mapping(struct skx_dev *d)
for (int i = 0; i < d->num_imc; i++)
d->imc[i].mc_mapping = i;
}
+EXPORT_SYMBOL_GPL(skx_init_mc_mapping);
void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc)
{
@@ -384,6 +385,12 @@ int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list)
}
EXPORT_SYMBOL_GPL(skx_get_all_bus_mappings);
+struct list_head *skx_get_edac_list(void)
+{
+ return &dev_edac_list;
+}
+EXPORT_SYMBOL_GPL(skx_get_edac_list);
+
int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm)
{
struct pci_dev *pdev;
@@ -424,6 +431,13 @@ fail:
}
EXPORT_SYMBOL_GPL(skx_get_hi_lo);
+void skx_set_hi_lo(u64 tolm, u64 tohm)
+{
+ skx_tolm = tolm;
+ skx_tohm = tohm;
+}
+EXPORT_SYMBOL_GPL(skx_set_hi_lo);
+
static int skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add,
int minval, int maxval, const char *name)
{
@@ -437,7 +451,7 @@ static int skx_get_dimm_attr(u32 reg, int lobit, int hibit, int add,
}
#define numrank(reg) skx_get_dimm_attr(reg, 12, 13, 0, 0, 2, "ranks")
-#define numrow(reg) skx_get_dimm_attr(reg, 2, 4, 12, 1, 6, "rows")
+#define numrow(reg) skx_get_dimm_attr(reg, 2, 4, 12, 1, 7, "rows")
#define numcol(reg) skx_get_dimm_attr(reg, 0, 1, 10, 0, 2, "cols")
int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm,
@@ -545,9 +559,9 @@ unknown_size:
}
EXPORT_SYMBOL_GPL(skx_get_nvdimm_info);
-int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev,
- const char *ctl_name, const char *mod_str,
- get_dimm_config_f get_dimm_config,
+int skx_register_mci(struct skx_imc *imc, struct device *dev,
+ const char *dev_name, const char *ctl_name,
+ const char *mod_str, get_dimm_config_f get_dimm_config,
struct res_config *cfg)
{
struct mem_ctl_info *mci;
@@ -588,7 +602,7 @@ int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev,
mci->edac_ctl_cap = EDAC_FLAG_NONE;
mci->edac_cap = EDAC_FLAG_NONE;
mci->mod_name = mod_str;
- mci->dev_name = pci_name(pdev);
+ mci->dev_name = dev_name;
mci->ctl_page_to_phys = NULL;
rc = get_dimm_config(mci, cfg);
@@ -596,7 +610,7 @@ int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev,
goto fail;
/* Record ptr to the generic device */
- mci->pdev = &pdev->dev;
+ mci->pdev = dev;
/* Add this new MC control structure to EDAC's list of MCs */
if (unlikely(edac_mc_add_mc(mci))) {
@@ -810,6 +824,9 @@ void skx_remove(void)
if (d->imc[i].mbase)
iounmap(d->imc[i].mbase);
+ if (d->imc[i].dev)
+ put_device(d->imc[i].dev);
+
for (j = 0; j < d->imc[i].num_channels; j++) {
if (d->imc[i].chan[j].cdev)
pci_dev_put(d->imc[i].chan[j].cdev);
@@ -833,7 +850,7 @@ EXPORT_SYMBOL_GPL(skx_remove);
/*
* Debug feature.
* Exercise the address decode logic by writing an address to
- * /sys/kernel/debug/edac/{skx,i10nm}_test/addr.
+ * /sys/kernel/debug/edac/{skx,i10nm,imh}_test/addr.
*/
static struct dentry *skx_test;
diff --git a/drivers/edac/skx_common.h b/drivers/edac/skx_common.h
index 73ba89786cdf..f88038e5b18c 100644
--- a/drivers/edac/skx_common.h
+++ b/drivers/edac/skx_common.h
@@ -121,20 +121,33 @@ struct reg_rrl {
* memory controllers on the die.
*/
struct skx_dev {
- struct list_head list;
+ /* {skx,i10nm}_edac */
u8 bus[4];
int seg;
struct pci_dev *sad_all;
struct pci_dev *util_all;
- struct pci_dev *uracu; /* for i10nm CPU */
- struct pci_dev *pcu_cr3; /* for HBM memory detection */
+ struct pci_dev *uracu;
+ struct pci_dev *pcu_cr3;
u32 mcroute;
+
+ /* imh_edac */
+ /* System-view MMIO base physical addresses. */
+ u64 mmio_base_h_north;
+ u64 mmio_base_h_south;
+ int pkg;
+
int num_imc;
+ struct list_head list;
struct skx_imc {
+ /* i10nm_edac */
+ struct pci_dev *mdev;
+
+ /* imh_edac */
+ struct device *dev;
+
struct mem_ctl_info *mci;
- struct pci_dev *mdev; /* for i10nm CPU */
- void __iomem *mbase; /* for i10nm CPU */
- int chan_mmio_sz; /* for i10nm CPU */
+ void __iomem *mbase;
+ int chan_mmio_sz;
int num_channels; /* channels per memory controller */
int num_dimms; /* dimms per channel */
bool hbm_mc;
@@ -178,7 +191,8 @@ enum type {
SKX,
I10NM,
SPR,
- GNR
+ GNR,
+ DMR,
};
enum {
@@ -237,10 +251,6 @@ struct pci_bdf {
struct res_config {
enum type type;
- /* Configuration agent device ID */
- unsigned int decs_did;
- /* Default bus number configuration register offset */
- int busno_cfg_offset;
/* DDR memory controllers per socket */
int ddr_imc_num;
/* DDR channels per DDR memory controller */
@@ -258,23 +268,57 @@ struct res_config {
/* Per HBM channel memory-mapped I/O size */
int hbm_chan_mmio_sz;
bool support_ddr5;
- /* SAD device BDF */
- struct pci_bdf sad_all_bdf;
- /* PCU device BDF */
- struct pci_bdf pcu_cr3_bdf;
- /* UTIL device BDF */
- struct pci_bdf util_all_bdf;
- /* URACU device BDF */
- struct pci_bdf uracu_bdf;
- /* DDR mdev device BDF */
- struct pci_bdf ddr_mdev_bdf;
- /* HBM mdev device BDF */
- struct pci_bdf hbm_mdev_bdf;
- int sad_all_offset;
/* RRL register sets per DDR channel */
struct reg_rrl *reg_rrl_ddr;
/* RRL register sets per HBM channel */
struct reg_rrl *reg_rrl_hbm[2];
+ union {
+ /* {skx,i10nm}_edac */
+ struct {
+ /* Configuration agent device ID */
+ unsigned int decs_did;
+ /* Default bus number configuration register offset */
+ int busno_cfg_offset;
+ struct pci_bdf sad_all_bdf;
+ struct pci_bdf pcu_cr3_bdf;
+ struct pci_bdf util_all_bdf;
+ struct pci_bdf uracu_bdf;
+ struct pci_bdf ddr_mdev_bdf;
+ struct pci_bdf hbm_mdev_bdf;
+ int sad_all_offset;
+ };
+ /* imh_edac */
+ struct {
+ /* MMIO base physical address in local package view */
+ u64 mmio_base_l_north;
+ u64 mmio_base_l_south;
+ u64 ddr_imc_base;
+ u64 ddr_reg_mcmtr_offset;
+ u8 ddr_reg_mcmtr_width;
+ u64 ddr_reg_dimmmtr_offset;
+ u8 ddr_reg_dimmmtr_width;
+ u64 ubox_base;
+ u32 ubox_size;
+ u32 ubox_reg_mmio_base_offset;
+ u8 ubox_reg_mmio_base_width;
+ u32 ubox_reg_socket_id_offset;
+ u8 ubox_reg_socket_id_width;
+ u64 pcu_base;
+ u32 pcu_size;
+ u32 pcu_reg_capid3_offset;
+ u8 pcu_reg_capid3_width;
+ u64 sca_base;
+ u32 sca_size;
+ u32 sca_reg_tolm_offset;
+ u8 sca_reg_tolm_width;
+ u32 sca_reg_tohm_offset;
+ u8 sca_reg_tohm_width;
+ u64 ha_base;
+ u32 ha_size;
+ u32 ha_reg_mode_offset;
+ u8 ha_reg_mode_width;
+ };
+ };
};
typedef int (*get_dimm_config_f)(struct mem_ctl_info *mci,
@@ -287,13 +331,17 @@ void skx_adxl_put(void);
void skx_set_decode(skx_decode_f decode, skx_show_retry_log_f show_retry_log);
void skx_set_mem_cfg(bool mem_cfg_2lm);
void skx_set_res_cfg(struct res_config *cfg);
+void skx_init_mc_mapping(struct skx_dev *d);
void skx_set_mc_mapping(struct skx_dev *d, u8 pmc, u8 lmc);
int skx_get_src_id(struct skx_dev *d, int off, u8 *id);
int skx_get_all_bus_mappings(struct res_config *cfg, struct list_head **list);
+struct list_head *skx_get_edac_list(void);
+
int skx_get_hi_lo(unsigned int did, int off[], u64 *tolm, u64 *tohm);
+void skx_set_hi_lo(u64 tolm, u64 tohm);
int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm,
struct skx_imc *imc, int chan, int dimmno,
@@ -302,7 +350,7 @@ int skx_get_dimm_info(u32 mtr, u32 mcmtr, u32 amap, struct dimm_info *dimm,
int skx_get_nvdimm_info(struct dimm_info *dimm, struct skx_imc *imc,
int chan, int dimmno, const char *mod_str);
-int skx_register_mci(struct skx_imc *imc, struct pci_dev *pdev,
+int skx_register_mci(struct skx_imc *imc, struct device *dev, const char *dev_name,
const char *ctl_name, const char *mod_str,
get_dimm_config_f get_dimm_config,
struct res_config *cfg);
diff --git a/drivers/edac/versalnet_edac.c b/drivers/edac/versalnet_edac.c
index 7c5db8bf0595..1a1092793092 100644
--- a/drivers/edac/versalnet_edac.c
+++ b/drivers/edac/versalnet_edac.c
@@ -433,7 +433,7 @@ static void handle_error(struct mc_priv *priv, struct ecc_status *stat,
phys_addr_t pfn;
int err;
- if (WARN_ON_ONCE(ctl_num > NUM_CONTROLLERS))
+ if (WARN_ON_ONCE(ctl_num >= NUM_CONTROLLERS))
return;
mci = priv->mci[ctl_num];
@@ -605,21 +605,23 @@ static int rpmsg_cb(struct rpmsg_device *rpdev, void *data,
length = result[MSG_ERR_LENGTH];
offset = result[MSG_ERR_OFFSET];
+ /*
+ * The data can come in two stretches. Construct the regs from two
+ * messages. The offset indicates the offset from which the data is to
+ * be taken.
+ */
+ for (i = 0 ; i < length; i++) {
+ k = offset + i;
+ j = ERROR_DATA + i;
+ mc_priv->regs[k] = result[j];
+ }
+
if (result[TOTAL_ERR_LENGTH] > length) {
if (!mc_priv->part_len)
mc_priv->part_len = length;
else
mc_priv->part_len += length;
- /*
- * The data can come in 2 stretches. Construct the regs from 2
- * messages the offset indicates the offset from which the data is to
- * be taken
- */
- for (i = 0 ; i < length; i++) {
- k = offset + i;
- j = ERROR_DATA + i;
- mc_priv->regs[k] = result[j];
- }
+
if (mc_priv->part_len < result[TOTAL_ERR_LENGTH])
return 0;
mc_priv->part_len = 0;
@@ -705,7 +707,7 @@ static int rpmsg_cb(struct rpmsg_device *rpdev, void *data,
/* Convert to bytes */
length = result[TOTAL_ERR_LENGTH] * 4;
log_non_standard_event(sec_type, &amd_versalnet_guid, mc_priv->message,
- sec_sev, (void *)&result[ERROR_DATA], length);
+ sec_sev, (void *)&mc_priv->regs, length);
return 0;
}
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index e5e0174a0335..0462d7b9e547 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -86,8 +86,6 @@ static size_t config_rom_length = 1 + 4 + 1 + 1;
*/
#define DEFAULT_SPLIT_TIMEOUT (2 * 8000)
-#define CANON_OUI 0x000085
-
static void generate_config_rom(struct fw_card *card, __be32 *config_rom)
{
struct fw_descriptor *desc;
@@ -308,11 +306,9 @@ __must_hold(&card->lock)
cpu_to_be32(local_id),
};
bool grace = time_is_before_jiffies64(card->reset_jiffies + msecs_to_jiffies(125));
- bool irm_is_1394_1995_only = false;
- bool keep_this_irm = false;
struct fw_node *irm_node;
struct fw_device *irm_device;
- int irm_node_id;
+ int irm_node_id, irm_device_quirks = 0;
int rcode;
lockdep_assert_held(&card->lock);
@@ -328,15 +324,12 @@ __must_hold(&card->lock)
return BM_CONTENTION_OUTCOME_IRM_HAS_LINK_OFF;
}
+ // NOTE: It is likely that the quirk detection for IRM device has not done yet.
irm_device = fw_node_get_device(irm_node);
- if (irm_device && irm_device->config_rom) {
- irm_is_1394_1995_only = (irm_device->config_rom[2] & 0x000000f0) == 0;
-
- // Canon MV5i works unreliably if it is not root node.
- keep_this_irm = irm_device->config_rom[3] >> 8 == CANON_OUI;
- }
-
- if (irm_is_1394_1995_only && !keep_this_irm) {
+ if (irm_device)
+ irm_device_quirks = READ_ONCE(irm_device->quirks);
+ if ((irm_device_quirks & FW_DEVICE_QUIRK_IRM_IS_1394_1995_ONLY) &&
+ !(irm_device_quirks & FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER)) {
fw_notice(card, "IRM is not 1394a compliant, making local node (%02x) root\n",
local_id);
return BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY;
@@ -373,7 +366,7 @@ __must_hold(&card->lock)
return BM_CONTENTION_OUTCOME_IRM_HOLDS_LOCAL_NODE_AS_BM;
}
default:
- if (!keep_this_irm) {
+ if (!(irm_device_quirks & FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER)) {
fw_notice(card, "BM lock failed (%s), making local node (%02x) root\n",
fw_rcode_string(rcode), local_id);
return BM_CONTENTION_OUTCOME_IRM_COMPLIES_1394_1995_ONLY;
@@ -577,6 +570,8 @@ void fw_card_initialize(struct fw_card *card,
INIT_LIST_HEAD(&card->transactions.list);
spin_lock_init(&card->transactions.lock);
+ spin_lock_init(&card->topology_map.lock);
+
card->split_timeout.hi = DEFAULT_SPLIT_TIMEOUT / 8000;
card->split_timeout.lo = (DEFAULT_SPLIT_TIMEOUT % 8000) << 19;
card->split_timeout.cycles = DEFAULT_SPLIT_TIMEOUT;
@@ -791,9 +786,13 @@ void fw_core_remove_card(struct fw_card *card)
/* Switch off most of the card driver interface. */
dummy_driver.free_iso_context = card->driver->free_iso_context;
dummy_driver.stop_iso = card->driver->stop_iso;
+ dummy_driver.disable = card->driver->disable;
card->driver = &dummy_driver;
+
drain_workqueue(card->isoc_wq);
drain_workqueue(card->async_wq);
+ card->driver->disable(card);
+ fw_cancel_pending_transactions(card);
scoped_guard(spinlock_irqsave, &card->lock)
fw_destroy_nodes(card);
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index 457a0da024a7..9b0080397154 100644
--- a/drivers/firewire/core-device.c
+++ b/drivers/firewire/core-device.c
@@ -542,8 +542,83 @@ static struct device_attribute fw_device_attributes[] = {
__ATTR_NULL,
};
-static int read_rom(struct fw_device *device,
- int generation, int index, u32 *data)
+#define CANON_OUI 0x000085
+
+static int detect_quirks_by_bus_information_block(const u32 *bus_information_block)
+{
+ int quirks = 0;
+
+ if ((bus_information_block[2] & 0x000000f0) == 0)
+ quirks |= FW_DEVICE_QUIRK_IRM_IS_1394_1995_ONLY;
+
+ if ((bus_information_block[3] >> 8) == CANON_OUI)
+ quirks |= FW_DEVICE_QUIRK_IRM_IGNORES_BUS_MANAGER;
+
+ return quirks;
+}
+
+struct entry_match {
+ unsigned int index;
+ u32 value;
+};
+
+static const struct entry_match motu_audio_express_matches[] = {
+ { 1, 0x030001f2 },
+ { 3, 0xd1000002 },
+ { 4, 0x8d000005 },
+ { 6, 0x120001f2 },
+ { 7, 0x13000033 },
+ { 8, 0x17104800 },
+};
+
+static const struct entry_match tascam_fw_series_matches[] = {
+ { 1, 0x0300022e },
+ { 3, 0x8d000006 },
+ { 4, 0xd1000001 },
+ { 6, 0x1200022e },
+ { 8, 0xd4000004 },
+};
+
+static int detect_quirks_by_root_directory(const u32 *root_directory, unsigned int length)
+{
+ static const struct {
+ enum fw_device_quirk quirk;
+ const struct entry_match *matches;
+ unsigned int match_count;
+ } *entry, entries[] = {
+ {
+ .quirk = FW_DEVICE_QUIRK_ACK_PACKET_WITH_INVALID_PENDING_CODE,
+ .matches = motu_audio_express_matches,
+ .match_count = ARRAY_SIZE(motu_audio_express_matches),
+ },
+ {
+ .quirk = FW_DEVICE_QUIRK_UNSTABLE_AT_S400,
+ .matches = tascam_fw_series_matches,
+ .match_count = ARRAY_SIZE(tascam_fw_series_matches),
+ },
+ };
+ int quirks = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ int j;
+
+ entry = entries + i;
+ for (j = 0; j < entry->match_count; ++j) {
+ unsigned int index = entry->matches[j].index;
+ unsigned int value = entry->matches[j].value;
+
+ if ((length < index) || (root_directory[index] != value))
+ break;
+ }
+ if (j == entry->match_count)
+ quirks |= entry->quirk;
+ }
+
+ return quirks;
+}
+
+static int read_rom(struct fw_device *device, int generation, int speed, int index, u32 *data)
{
u64 offset = (CSR_REGISTER_BASE | CSR_CONFIG_ROM) + index * 4;
int i, rcode;
@@ -554,7 +629,7 @@ static int read_rom(struct fw_device *device,
for (i = 10; i < 100; i += 10) {
rcode = fw_run_transaction(device->card,
TCODE_READ_QUADLET_REQUEST, device->node_id,
- generation, device->max_speed, offset, data, 4);
+ generation, speed, offset, data, 4);
if (rcode != RCODE_BUSY)
break;
msleep(i);
@@ -578,10 +653,11 @@ static int read_rom(struct fw_device *device,
static int read_config_rom(struct fw_device *device, int generation)
{
struct fw_card *card = device->card;
- const u32 *old_rom, *new_rom;
- u32 *rom, *stack;
+ const u32 *new_rom, *old_rom __free(kfree) = NULL;
+ u32 *stack, *rom __free(kfree) = NULL;
u32 sp, key;
- int i, end, length, ret;
+ int i, end, length, ret, speed;
+ int quirks;
rom = kmalloc(sizeof(*rom) * MAX_CONFIG_ROM_SIZE +
sizeof(*stack) * MAX_CONFIG_ROM_SIZE, GFP_KERNEL);
@@ -591,13 +667,13 @@ static int read_config_rom(struct fw_device *device, int generation)
stack = &rom[MAX_CONFIG_ROM_SIZE];
memset(rom, 0, sizeof(*rom) * MAX_CONFIG_ROM_SIZE);
- device->max_speed = SCODE_100;
+ speed = SCODE_100;
/* First read the bus info block. */
for (i = 0; i < 5; i++) {
- ret = read_rom(device, generation, i, &rom[i]);
+ ret = read_rom(device, generation, speed, i, &rom[i]);
if (ret != RCODE_COMPLETE)
- goto out;
+ return ret;
/*
* As per IEEE1212 7.2, during initialization, devices can
* reply with a 0 for the first quadlet of the config
@@ -606,39 +682,14 @@ static int read_config_rom(struct fw_device *device, int generation)
* harddisk). In that case we just fail, and the
* retry mechanism will try again later.
*/
- if (i == 0 && rom[i] == 0) {
- ret = RCODE_BUSY;
- goto out;
- }
+ if (i == 0 && rom[i] == 0)
+ return RCODE_BUSY;
}
- device->max_speed = device->node->max_speed;
-
- /*
- * Determine the speed of
- * - devices with link speed less than PHY speed,
- * - devices with 1394b PHY (unless only connected to 1394a PHYs),
- * - all devices if there are 1394b repeaters.
- * Note, we cannot use the bus info block's link_spd as starting point
- * because some buggy firmwares set it lower than necessary and because
- * 1394-1995 nodes do not have the field.
- */
- if ((rom[2] & 0x7) < device->max_speed ||
- device->max_speed == SCODE_BETA ||
- card->beta_repeaters_present) {
- u32 dummy;
-
- /* for S1600 and S3200 */
- if (device->max_speed == SCODE_BETA)
- device->max_speed = card->link_speed;
+ quirks = detect_quirks_by_bus_information_block(rom);
- while (device->max_speed > SCODE_100) {
- if (read_rom(device, generation, 0, &dummy) ==
- RCODE_COMPLETE)
- break;
- device->max_speed--;
- }
- }
+ // Just prevent from torn writing/reading.
+ WRITE_ONCE(device->quirks, quirks);
/*
* Now parse the config rom. The config rom is a recursive
@@ -659,15 +710,13 @@ static int read_config_rom(struct fw_device *device, int generation)
*/
key = stack[--sp];
i = key & 0xffffff;
- if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE)) {
- ret = -ENXIO;
- goto out;
- }
+ if (WARN_ON(i >= MAX_CONFIG_ROM_SIZE))
+ return -ENXIO;
/* Read header quadlet for the block to get the length. */
- ret = read_rom(device, generation, i, &rom[i]);
+ ret = read_rom(device, generation, speed, i, &rom[i]);
if (ret != RCODE_COMPLETE)
- goto out;
+ return ret;
end = i + (rom[i] >> 16) + 1;
if (end > MAX_CONFIG_ROM_SIZE) {
/*
@@ -689,9 +738,9 @@ static int read_config_rom(struct fw_device *device, int generation)
* it references another block, and push it in that case.
*/
for (; i < end; i++) {
- ret = read_rom(device, generation, i, &rom[i]);
+ ret = read_rom(device, generation, speed, i, &rom[i]);
if (ret != RCODE_COMPLETE)
- goto out;
+ return ret;
if ((key >> 30) != 3 || (rom[i] >> 30) < 2)
continue;
@@ -716,27 +765,54 @@ static int read_config_rom(struct fw_device *device, int generation)
length = i;
}
+ quirks |= detect_quirks_by_root_directory(rom + ROOT_DIR_OFFSET, length - ROOT_DIR_OFFSET);
+
+ // Just prevent from torn writing/reading.
+ WRITE_ONCE(device->quirks, quirks);
+
+ if (unlikely(quirks & FW_DEVICE_QUIRK_UNSTABLE_AT_S400))
+ speed = SCODE_200;
+ else
+ speed = device->node->max_speed;
+
+ // Determine the speed of
+ // - devices with link speed less than PHY speed,
+ // - devices with 1394b PHY (unless only connected to 1394a PHYs),
+ // - all devices if there are 1394b repeaters.
+ // Note, we cannot use the bus info block's link_spd as starting point because some buggy
+ // firmwares set it lower than necessary and because 1394-1995 nodes do not have the field.
+ if ((rom[2] & 0x7) < speed || speed == SCODE_BETA || card->beta_repeaters_present) {
+ u32 dummy;
+
+ // for S1600 and S3200.
+ if (speed == SCODE_BETA)
+ speed = card->link_speed;
+
+ while (speed > SCODE_100) {
+ if (read_rom(device, generation, speed, 0, &dummy) ==
+ RCODE_COMPLETE)
+ break;
+ --speed;
+ }
+ }
+
+ device->max_speed = speed;
+
old_rom = device->config_rom;
new_rom = kmemdup(rom, length * 4, GFP_KERNEL);
- if (new_rom == NULL) {
- ret = -ENOMEM;
- goto out;
- }
+ if (new_rom == NULL)
+ return -ENOMEM;
scoped_guard(rwsem_write, &fw_device_rwsem) {
device->config_rom = new_rom;
device->config_rom_length = length;
}
- kfree(old_rom);
- ret = RCODE_COMPLETE;
device->max_rec = rom[2] >> 12 & 0xf;
device->cmc = rom[2] >> 30 & 1;
device->irmc = rom[2] >> 31 & 1;
- out:
- kfree(rom);
- return ret;
+ return RCODE_COMPLETE;
}
static void fw_unit_release(struct device *dev)
@@ -1122,10 +1198,10 @@ static void fw_device_init(struct work_struct *work)
device->workfn = fw_device_shutdown;
fw_schedule_device_work(device, SHUTDOWN_DELAY);
} else {
- fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n",
+ fw_notice(card, "created device %s: GUID %08x%08x, S%d00, quirks %08x\n",
dev_name(&device->device),
device->config_rom[3], device->config_rom[4],
- 1 << device->max_speed);
+ 1 << device->max_speed, device->quirks);
device->config_rom_retries = 0;
set_broadcast_channel(device, device->generation);
@@ -1160,7 +1236,7 @@ static int reread_config_rom(struct fw_device *device, int generation,
int i, rcode;
for (i = 0; i < 6; i++) {
- rcode = read_rom(device, generation, i, &q);
+ rcode = read_rom(device, generation, device->max_speed, i, &q);
if (rcode != RCODE_COMPLETE)
return rcode;
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index 2f73bcd5696f..ed3ae8cdb0cd 100644
--- a/drivers/firewire/core-topology.c
+++ b/drivers/firewire/core-topology.c
@@ -441,12 +441,13 @@ static void update_topology_map(__be32 *buffer, size_t buffer_size, int root_nod
const u32 *self_ids, int self_id_count)
{
__be32 *map = buffer;
+ u32 next_generation = be32_to_cpu(buffer[1]) + 1;
int node_count = (root_node_id & 0x3f) + 1;
memset(map, 0, buffer_size);
*map++ = cpu_to_be32((self_id_count + 2) << 16);
- *map++ = cpu_to_be32(be32_to_cpu(buffer[1]) + 1);
+ *map++ = cpu_to_be32(next_generation);
*map++ = cpu_to_be32((node_count << 16) | self_id_count);
while (self_id_count--)
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index dd3656a0c1ff..7fea11a5e359 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -44,28 +44,68 @@ static int try_cancel_split_timeout(struct fw_transaction *t)
return 1;
}
-static int close_transaction(struct fw_transaction *transaction, struct fw_card *card, int rcode,
- u32 response_tstamp)
+// card->transactions.lock must be acquired in advance.
+static void remove_transaction_entry(struct fw_card *card, struct fw_transaction *entry)
{
- struct fw_transaction *t = NULL, *iter;
+ list_del_init(&entry->link);
+ card->transactions.tlabel_mask &= ~(1ULL << entry->tlabel);
+}
+
+// Must be called without holding card->transactions.lock.
+void fw_cancel_pending_transactions(struct fw_card *card)
+{
+ struct fw_transaction *t, *tmp;
+ LIST_HEAD(pending_list);
// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
// local destination never runs in any type of IRQ context.
scoped_guard(spinlock_irqsave, &card->transactions.lock) {
- list_for_each_entry(iter, &card->transactions.list, link) {
- if (iter == transaction) {
- if (try_cancel_split_timeout(iter)) {
- list_del_init(&iter->link);
- card->transactions.tlabel_mask &= ~(1ULL << iter->tlabel);
- t = iter;
- }
- break;
- }
+ list_for_each_entry_safe(t, tmp, &card->transactions.list, link) {
+ if (try_cancel_split_timeout(t))
+ list_move(&t->link, &pending_list);
}
}
- if (!t)
- return -ENOENT;
+ list_for_each_entry_safe(t, tmp, &pending_list, link) {
+ list_del(&t->link);
+
+ if (!t->with_tstamp) {
+ t->callback.without_tstamp(card, RCODE_CANCELLED, NULL, 0,
+ t->callback_data);
+ } else {
+ t->callback.with_tstamp(card, RCODE_CANCELLED, t->packet.timestamp, 0,
+ NULL, 0, t->callback_data);
+ }
+ }
+}
+
+// card->transactions.lock must be acquired in advance.
+#define find_and_pop_transaction_entry(card, condition) \
+({ \
+ struct fw_transaction *iter, *t = NULL; \
+ list_for_each_entry(iter, &card->transactions.list, link) { \
+ if (condition) { \
+ t = iter; \
+ break; \
+ } \
+ } \
+ if (t && try_cancel_split_timeout(t)) \
+ remove_transaction_entry(card, t); \
+ t; \
+})
+
+static int close_transaction(struct fw_transaction *transaction, struct fw_card *card, int rcode,
+ u32 response_tstamp)
+{
+ struct fw_transaction *t;
+
+ // NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
+ // local destination never runs in any type of IRQ context.
+ scoped_guard(spinlock_irqsave, &card->transactions.lock) {
+ t = find_and_pop_transaction_entry(card, iter == transaction);
+ if (!t)
+ return -ENOENT;
+ }
if (!t->with_tstamp) {
t->callback.without_tstamp(card, rcode, NULL, 0, t->callback_data);
@@ -122,8 +162,7 @@ static void split_transaction_timeout_callback(struct timer_list *timer)
scoped_guard(spinlock_irqsave, &card->transactions.lock) {
if (list_empty(&t->link))
return;
- list_del(&t->link);
- card->transactions.tlabel_mask &= ~(1ULL << t->tlabel);
+ remove_transaction_entry(card, t);
}
if (!t->with_tstamp) {
@@ -269,7 +308,7 @@ static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
}
static int allocate_tlabel(struct fw_card *card)
-__must_hold(&card->transactions_lock)
+__must_hold(&card->transactions.lock)
{
int tlabel;
@@ -1097,7 +1136,7 @@ EXPORT_SYMBOL(fw_core_handle_request);
void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
{
- struct fw_transaction *t = NULL, *iter;
+ struct fw_transaction *t = NULL;
u32 *data;
size_t data_length;
int tcode, tlabel, source, rcode;
@@ -1139,16 +1178,8 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
// NOTE: This can be without irqsave when we can guarantee that __fw_send_request() for
// local destination never runs in any type of IRQ context.
scoped_guard(spinlock_irqsave, &card->transactions.lock) {
- list_for_each_entry(iter, &card->transactions.list, link) {
- if (iter->node_id == source && iter->tlabel == tlabel) {
- if (try_cancel_split_timeout(iter)) {
- list_del_init(&iter->link);
- card->transactions.tlabel_mask &= ~(1ULL << iter->tlabel);
- t = iter;
- }
- break;
- }
- }
+ t = find_and_pop_transaction_entry(card,
+ iter->node_id == source && iter->tlabel == tlabel);
}
trace_async_response_inbound((uintptr_t)t, card->index, p->generation, p->speed, p->ack,
@@ -1437,7 +1468,8 @@ static int __init fw_core_init(void)
{
int ret;
- fw_workqueue = alloc_workqueue("firewire", WQ_MEM_RECLAIM, 0);
+ fw_workqueue = alloc_workqueue("firewire", WQ_MEM_RECLAIM | WQ_UNBOUND,
+ 0);
if (!fw_workqueue)
return -ENOMEM;
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index e67395ce26b5..41fb39d9a4e6 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -65,6 +65,9 @@ struct fw_card_driver {
int (*enable)(struct fw_card *card,
const __be32 *config_rom, size_t length);
+ // After returning the call, any function is no longer triggered to handle hardware event.
+ void (*disable)(struct fw_card *card);
+
int (*read_phy_reg)(struct fw_card *card, int address);
int (*update_phy_reg)(struct fw_card *card, int address,
int clear_bits, int set_bits);
@@ -284,6 +287,8 @@ void fw_fill_response(struct fw_packet *response, u32 *request_header,
void fw_request_get(struct fw_request *request);
void fw_request_put(struct fw_request *request);
+void fw_cancel_pending_transactions(struct fw_card *card);
+
// Convert the value of IEEE 1394 CYCLE_TIME register to the format of timeStamp field in
// descriptors of 1394 OHCI.
static inline u32 cycle_time_to_ohci_tstamp(u32 tstamp)
diff --git a/drivers/firewire/init_ohci1394_dma.c b/drivers/firewire/init_ohci1394_dma.c
index 48b879e9e831..121f0c2f6401 100644
--- a/drivers/firewire/init_ohci1394_dma.c
+++ b/drivers/firewire/init_ohci1394_dma.c
@@ -167,6 +167,7 @@ static inline void __init init_ohci1394_initialize(struct ohci *ohci)
/**
* init_ohci1394_wait_for_busresets - wait until bus resets are completed
+ * @ohci: Pointer to the OHCI-1394 controller structure
*
* OHCI1394 initialization itself and any device going on- or offline
* and any cable issue cause a IEEE1394 bus reset. The OHCI1394 spec
@@ -189,6 +190,8 @@ static inline void __init init_ohci1394_wait_for_busresets(struct ohci *ohci)
/**
* init_ohci1394_enable_physical_dma - Enable physical DMA for remote debugging
+ * @ohci: Pointer to the OHCI-1394 controller structure
+ *
* This enables remote DMA access over IEEE1394 from every host for the low
* 4GB of address space. DMA accesses above 4GB are not available currently.
*/
@@ -201,6 +204,8 @@ static inline void __init init_ohci1394_enable_physical_dma(struct ohci *ohci)
/**
* init_ohci1394_reset_and_init_dma - init controller and enable DMA
+ * @ohci: Pointer to the OHCI-1394 controller structure
+ *
* This initializes the given controller and enables physical DMA engine in it.
*/
static inline void __init init_ohci1394_reset_and_init_dma(struct ohci *ohci)
@@ -230,6 +235,10 @@ static inline void __init init_ohci1394_reset_and_init_dma(struct ohci *ohci)
/**
* init_ohci1394_controller - Map the registers of the controller and init DMA
+ * @num: PCI bus number
+ * @slot: PCI device number
+ * @func: PCI function number
+ *
* This maps the registers of the specified controller and initializes it
*/
static inline void __init init_ohci1394_controller(int num, int slot, int func)
@@ -284,6 +293,7 @@ void __init init_ohci1394_dma_on_all_controllers(void)
/**
* setup_ohci1394_dma - enables early OHCI1394 DMA initialization
+ * @opt: Kernel command line parameter string
*/
static int __init setup_ohci1394_dma(char *opt)
{
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 030aed5453a1..e3e78dc42530 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -1319,6 +1319,14 @@ static void at_context_flush(struct at_context *ctx)
enable_work(&ctx->work);
}
+static int find_fw_device(struct device *dev, const void *data)
+{
+ struct fw_device *device = fw_device(dev);
+ const u32 *params = data;
+
+ return (device->generation == params[0]) && (device->node_id == params[1]);
+}
+
static int handle_at_packet(struct context *context,
struct descriptor *d,
struct descriptor *last)
@@ -1390,6 +1398,27 @@ static int handle_at_packet(struct context *context,
fallthrough;
default:
+ if (unlikely(evt == 0x10)) {
+ u32 params[2] = {
+ packet->generation,
+ async_header_get_destination(packet->header),
+ };
+ struct device *dev;
+
+ fw_card_get(&ohci->card);
+ dev = device_find_child(ohci->card.device, (const void *)params, find_fw_device);
+ fw_card_put(&ohci->card);
+ if (dev) {
+ struct fw_device *device = fw_device(dev);
+ int quirks = READ_ONCE(device->quirks);
+
+ put_device(dev);
+ if (quirks & FW_DEVICE_QUIRK_ACK_PACKET_WITH_INVALID_PENDING_CODE) {
+ packet->ack = ACK_PENDING;
+ break;
+ }
+ }
+ }
packet->ack = RCODE_SEND_ERROR;
break;
}
@@ -2379,6 +2408,41 @@ static int ohci_enable(struct fw_card *card,
return 0;
}
+static void ohci_disable(struct fw_card *card)
+{
+ struct pci_dev *pdev = to_pci_dev(card->device);
+ struct fw_ohci *ohci = pci_get_drvdata(pdev);
+ int i, irq = pci_irq_vector(pdev, 0);
+
+ // If the removal is happening from the suspend state, LPS won't be enabled and host
+ // registers (eg., IntMaskClear) won't be accessible.
+ if (!(reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_LPS))
+ return;
+
+ reg_write(ohci, OHCI1394_IntMaskClear, ~0);
+ flush_writes(ohci);
+
+ if (irq >= 0)
+ synchronize_irq(irq);
+
+ flush_work(&ohci->ar_request_ctx.work);
+ flush_work(&ohci->ar_response_ctx.work);
+ flush_work(&ohci->at_request_ctx.work);
+ flush_work(&ohci->at_response_ctx.work);
+
+ for (i = 0; i < ohci->n_ir; ++i) {
+ if (!(ohci->ir_context_mask & BIT(i)))
+ flush_work(&ohci->ir_context_list[i].base.work);
+ }
+ for (i = 0; i < ohci->n_it; ++i) {
+ if (!(ohci->it_context_mask & BIT(i)))
+ flush_work(&ohci->it_context_list[i].base.work);
+ }
+
+ at_context_flush(&ohci->at_request_ctx);
+ at_context_flush(&ohci->at_response_ctx);
+}
+
static int ohci_set_config_rom(struct fw_card *card,
const __be32 *config_rom, size_t length)
{
@@ -3413,6 +3477,7 @@ static int ohci_flush_iso_completions(struct fw_iso_context *base)
static const struct fw_card_driver ohci_driver = {
.enable = ohci_enable,
+ .disable = ohci_disable,
.read_phy_reg = ohci_read_phy_reg,
.update_phy_reg = ohci_update_phy_reg,
.set_config_rom = ohci_set_config_rom,
@@ -3652,21 +3717,8 @@ static void pci_remove(struct pci_dev *dev)
struct fw_ohci *ohci = pci_get_drvdata(dev);
int irq;
- /*
- * If the removal is happening from the suspend state, LPS won't be
- * enabled and host registers (eg., IntMaskClear) won't be accessible.
- */
- if (reg_read(ohci, OHCI1394_HCControlSet) & OHCI1394_HCControl_LPS) {
- reg_write(ohci, OHCI1394_IntMaskClear, ~0);
- flush_writes(ohci);
- }
fw_core_remove_card(&ohci->card);
- /*
- * FIXME: Fail all pending packets here, now that the upper
- * layers can't queue any more.
- */
-
software_reset(ohci);
irq = pci_irq_vector(dev, 0);
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index 65bf1685350a..c72ee4756585 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -649,6 +649,26 @@ static u16 ffa_memory_attributes_get(u32 func_id)
return FFA_MEM_NORMAL | FFA_MEM_WRITE_BACK | FFA_MEM_INNER_SHAREABLE;
}
+static void ffa_emad_impdef_value_init(u32 version, void *dst, void *src)
+{
+ struct ffa_mem_region_attributes *ep_mem_access;
+
+ if (FFA_EMAD_HAS_IMPDEF_FIELD(version))
+ memcpy(dst, src, sizeof(ep_mem_access->impdef_val));
+}
+
+static void
+ffa_mem_region_additional_setup(u32 version, struct ffa_mem_region *mem_region)
+{
+ if (!FFA_MEM_REGION_HAS_EP_MEM_OFFSET(version)) {
+ mem_region->ep_mem_size = 0;
+ } else {
+ mem_region->ep_mem_size = ffa_emad_size_get(version);
+ mem_region->ep_mem_offset = sizeof(*mem_region);
+ memset(mem_region->reserved, 0, 12);
+ }
+}
+
static int
ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
struct ffa_mem_ops_args *args)
@@ -667,27 +687,24 @@ ffa_setup_and_transmit(u32 func_id, void *buffer, u32 max_fragsize,
mem_region->flags = args->flags;
mem_region->sender_id = drv_info->vm_id;
mem_region->attributes = ffa_memory_attributes_get(func_id);
- ep_mem_access = buffer +
- ffa_mem_desc_offset(buffer, 0, drv_info->version);
composite_offset = ffa_mem_desc_offset(buffer, args->nattrs,
drv_info->version);
- for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) {
+ for (idx = 0; idx < args->nattrs; idx++) {
+ ep_mem_access = buffer +
+ ffa_mem_desc_offset(buffer, idx, drv_info->version);
ep_mem_access->receiver = args->attrs[idx].receiver;
ep_mem_access->attrs = args->attrs[idx].attrs;
ep_mem_access->composite_off = composite_offset;
ep_mem_access->flag = 0;
ep_mem_access->reserved = 0;
+ ffa_emad_impdef_value_init(drv_info->version,
+ ep_mem_access->impdef_val,
+ args->attrs[idx].impdef_val);
}
mem_region->handle = 0;
mem_region->ep_count = args->nattrs;
- if (drv_info->version <= FFA_VERSION_1_0) {
- mem_region->ep_mem_size = 0;
- } else {
- mem_region->ep_mem_size = sizeof(*ep_mem_access);
- mem_region->ep_mem_offset = sizeof(*mem_region);
- memset(mem_region->reserved, 0, 12);
- }
+ ffa_mem_region_additional_setup(drv_info->version, mem_region);
composite = buffer + composite_offset;
composite->total_pg_cnt = ffa_get_num_pages_sg(args->sg);
diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
index 07b9e629276d..7c35c95fddba 100644
--- a/drivers/firmware/arm_scmi/common.h
+++ b/drivers/firmware/arm_scmi/common.h
@@ -309,16 +309,36 @@ enum debug_counters {
SCMI_DEBUG_COUNTERS_LAST
};
-static inline void scmi_inc_count(atomic_t *arr, int stat)
+/**
+ * struct scmi_debug_info - Debug common info
+ * @top_dentry: A reference to the top debugfs dentry
+ * @name: Name of this SCMI instance
+ * @type: Type of this SCMI instance
+ * @is_atomic: Flag to state if the transport of this instance is atomic
+ * @counters: An array of atomic_c's used for tracking statistics (if enabled)
+ */
+struct scmi_debug_info {
+ struct dentry *top_dentry;
+ const char *name;
+ const char *type;
+ bool is_atomic;
+ atomic_t counters[SCMI_DEBUG_COUNTERS_LAST];
+};
+
+static inline void scmi_inc_count(struct scmi_debug_info *dbg, int stat)
{
- if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS))
- atomic_inc(&arr[stat]);
+ if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) {
+ if (dbg)
+ atomic_inc(&dbg->counters[stat]);
+ }
}
-static inline void scmi_dec_count(atomic_t *arr, int stat)
+static inline void scmi_dec_count(struct scmi_debug_info *dbg, int stat)
{
- if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS))
- atomic_dec(&arr[stat]);
+ if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) {
+ if (dbg)
+ atomic_dec(&dbg->counters[stat]);
+ }
}
enum scmi_bad_msg {
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index bd56a877fdfc..5caa9191a8d1 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -116,22 +116,6 @@ struct scmi_protocol_instance {
#define ph_to_pi(h) container_of(h, struct scmi_protocol_instance, ph)
/**
- * struct scmi_debug_info - Debug common info
- * @top_dentry: A reference to the top debugfs dentry
- * @name: Name of this SCMI instance
- * @type: Type of this SCMI instance
- * @is_atomic: Flag to state if the transport of this instance is atomic
- * @counters: An array of atomic_c's used for tracking statistics (if enabled)
- */
-struct scmi_debug_info {
- struct dentry *top_dentry;
- const char *name;
- const char *type;
- bool is_atomic;
- atomic_t counters[SCMI_DEBUG_COUNTERS_LAST];
-};
-
-/**
* struct scmi_info - Structure representing a SCMI instance
*
* @id: A sequence number starting from zero identifying this instance
@@ -610,7 +594,7 @@ scmi_xfer_inflight_register_unlocked(struct scmi_xfer *xfer,
/* Set in-flight */
set_bit(xfer->hdr.seq, minfo->xfer_alloc_table);
hash_add(minfo->pending_xfers, &xfer->node, xfer->hdr.seq);
- scmi_inc_count(info->dbg->counters, XFERS_INFLIGHT);
+ scmi_inc_count(info->dbg, XFERS_INFLIGHT);
xfer->pending = true;
}
@@ -819,8 +803,9 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
hash_del(&xfer->node);
xfer->pending = false;
- scmi_dec_count(info->dbg->counters, XFERS_INFLIGHT);
+ scmi_dec_count(info->dbg, XFERS_INFLIGHT);
}
+ xfer->flags = 0;
hlist_add_head(&xfer->node, &minfo->free_xfers);
}
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
@@ -839,8 +824,6 @@ void scmi_xfer_raw_put(const struct scmi_handle *handle, struct scmi_xfer *xfer)
{
struct scmi_info *info = handle_to_scmi_info(handle);
- xfer->flags &= ~SCMI_XFER_FLAG_IS_RAW;
- xfer->flags &= ~SCMI_XFER_FLAG_CHAN_SET;
return __scmi_xfer_put(&info->tx_minfo, xfer);
}
@@ -1034,7 +1017,7 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
spin_unlock_irqrestore(&minfo->xfer_lock, flags);
scmi_bad_message_trace(cinfo, msg_hdr, MSG_UNEXPECTED);
- scmi_inc_count(info->dbg->counters, ERR_MSG_UNEXPECTED);
+ scmi_inc_count(info->dbg, ERR_MSG_UNEXPECTED);
return xfer;
}
@@ -1062,7 +1045,7 @@ scmi_xfer_command_acquire(struct scmi_chan_info *cinfo, u32 msg_hdr)
msg_type, xfer_id, msg_hdr, xfer->state);
scmi_bad_message_trace(cinfo, msg_hdr, MSG_INVALID);
- scmi_inc_count(info->dbg->counters, ERR_MSG_INVALID);
+ scmi_inc_count(info->dbg, ERR_MSG_INVALID);
/* On error the refcount incremented above has to be dropped */
__scmi_xfer_put(minfo, xfer);
@@ -1107,7 +1090,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
PTR_ERR(xfer));
scmi_bad_message_trace(cinfo, msg_hdr, MSG_NOMEM);
- scmi_inc_count(info->dbg->counters, ERR_MSG_NOMEM);
+ scmi_inc_count(info->dbg, ERR_MSG_NOMEM);
scmi_clear_channel(info, cinfo);
return;
@@ -1123,7 +1106,7 @@ static void scmi_handle_notification(struct scmi_chan_info *cinfo,
trace_scmi_msg_dump(info->id, cinfo->id, xfer->hdr.protocol_id,
xfer->hdr.id, "NOTI", xfer->hdr.seq,
xfer->hdr.status, xfer->rx.buf, xfer->rx.len);
- scmi_inc_count(info->dbg->counters, NOTIFICATION_OK);
+ scmi_inc_count(info->dbg, NOTIFICATION_OK);
scmi_notify(cinfo->handle, xfer->hdr.protocol_id,
xfer->hdr.id, xfer->rx.buf, xfer->rx.len, ts);
@@ -1183,10 +1166,10 @@ static void scmi_handle_response(struct scmi_chan_info *cinfo,
if (xfer->hdr.type == MSG_TYPE_DELAYED_RESP) {
scmi_clear_channel(info, cinfo);
complete(xfer->async_done);
- scmi_inc_count(info->dbg->counters, DELAYED_RESPONSE_OK);
+ scmi_inc_count(info->dbg, DELAYED_RESPONSE_OK);
} else {
complete(&xfer->done);
- scmi_inc_count(info->dbg->counters, RESPONSE_OK);
+ scmi_inc_count(info->dbg, RESPONSE_OK);
}
if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
@@ -1296,7 +1279,7 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
"timed out in resp(caller: %pS) - polling\n",
(void *)_RET_IP_);
ret = -ETIMEDOUT;
- scmi_inc_count(info->dbg->counters, XFERS_RESPONSE_POLLED_TIMEOUT);
+ scmi_inc_count(info->dbg, XFERS_RESPONSE_POLLED_TIMEOUT);
}
}
@@ -1321,7 +1304,7 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
"RESP" : "resp",
xfer->hdr.seq, xfer->hdr.status,
xfer->rx.buf, xfer->rx.len);
- scmi_inc_count(info->dbg->counters, RESPONSE_POLLED_OK);
+ scmi_inc_count(info->dbg, RESPONSE_POLLED_OK);
if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
scmi_raw_message_report(info->raw, xfer,
@@ -1336,7 +1319,7 @@ static int scmi_wait_for_reply(struct device *dev, const struct scmi_desc *desc,
dev_err(dev, "timed out in resp(caller: %pS)\n",
(void *)_RET_IP_);
ret = -ETIMEDOUT;
- scmi_inc_count(info->dbg->counters, XFERS_RESPONSE_TIMEOUT);
+ scmi_inc_count(info->dbg, XFERS_RESPONSE_TIMEOUT);
}
}
@@ -1420,13 +1403,13 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
!is_transport_polling_capable(info->desc)) {
dev_warn_once(dev,
"Polling mode is not supported by transport.\n");
- scmi_inc_count(info->dbg->counters, SENT_FAIL_POLLING_UNSUPPORTED);
+ scmi_inc_count(info->dbg, SENT_FAIL_POLLING_UNSUPPORTED);
return -EINVAL;
}
cinfo = idr_find(&info->tx_idr, pi->proto->id);
if (unlikely(!cinfo)) {
- scmi_inc_count(info->dbg->counters, SENT_FAIL_CHANNEL_NOT_FOUND);
+ scmi_inc_count(info->dbg, SENT_FAIL_CHANNEL_NOT_FOUND);
return -EINVAL;
}
/* True ONLY if also supported by transport. */
@@ -1461,19 +1444,19 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
ret = info->desc->ops->send_message(cinfo, xfer);
if (ret < 0) {
dev_dbg(dev, "Failed to send message %d\n", ret);
- scmi_inc_count(info->dbg->counters, SENT_FAIL);
+ scmi_inc_count(info->dbg, SENT_FAIL);
return ret;
}
trace_scmi_msg_dump(info->id, cinfo->id, xfer->hdr.protocol_id,
xfer->hdr.id, "CMND", xfer->hdr.seq,
xfer->hdr.status, xfer->tx.buf, xfer->tx.len);
- scmi_inc_count(info->dbg->counters, SENT_OK);
+ scmi_inc_count(info->dbg, SENT_OK);
ret = scmi_wait_for_message_response(cinfo, xfer);
if (!ret && xfer->hdr.status) {
ret = scmi_to_linux_errno(xfer->hdr.status);
- scmi_inc_count(info->dbg->counters, ERR_PROTOCOL);
+ scmi_inc_count(info->dbg, ERR_PROTOCOL);
}
if (info->desc->ops->mark_txdone)
@@ -3044,9 +3027,6 @@ static int scmi_debugfs_raw_mode_setup(struct scmi_info *info)
u8 channels[SCMI_MAX_CHANNELS] = {};
DECLARE_BITMAP(protos, SCMI_MAX_CHANNELS) = {};
- if (!info->dbg)
- return -EINVAL;
-
/* Enumerate all channels to collect their ids */
idr_for_each_entry(&info->tx_idr, cinfo, id) {
/*
@@ -3218,7 +3198,7 @@ static int scmi_probe(struct platform_device *pdev)
if (!info->dbg)
dev_warn(dev, "Failed to setup SCMI debugfs.\n");
- if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
+ if (info->dbg && IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) {
ret = scmi_debugfs_raw_mode_setup(info);
if (!coex) {
if (ret)
@@ -3423,6 +3403,9 @@ int scmi_inflight_count(const struct scmi_handle *handle)
if (IS_ENABLED(CONFIG_ARM_SCMI_DEBUG_COUNTERS)) {
struct scmi_info *info = handle_to_scmi_info(handle);
+ if (!info->dbg)
+ return 0;
+
return atomic_read(&info->dbg->counters[XFERS_INFLIGHT]);
} else {
return 0;
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c
index f51047d8ea64..525ac0f0a75d 100644
--- a/drivers/firmware/cirrus/cs_dsp.c
+++ b/drivers/firmware/cirrus/cs_dsp.c
@@ -9,9 +9,11 @@
* Cirrus Logic International Semiconductor Ltd.
*/
+#include <linux/cleanup.h>
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -316,44 +318,6 @@ struct cs_dsp_alg_region_list_item {
struct cs_dsp_alg_region alg_region;
};
-struct cs_dsp_buf {
- struct list_head list;
- void *buf;
-};
-
-static struct cs_dsp_buf *cs_dsp_buf_alloc(const void *src, size_t len,
- struct list_head *list)
-{
- struct cs_dsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
-
- if (buf == NULL)
- return NULL;
-
- buf->buf = vmalloc(len);
- if (!buf->buf) {
- kfree(buf);
- return NULL;
- }
- memcpy(buf->buf, src, len);
-
- if (list)
- list_add_tail(&buf->list, list);
-
- return buf;
-}
-
-static void cs_dsp_buf_free(struct list_head *list)
-{
- while (!list_empty(list)) {
- struct cs_dsp_buf *buf = list_first_entry(list,
- struct cs_dsp_buf,
- list);
- list_del(&buf->list);
- vfree(buf->buf);
- kfree(buf);
- }
-}
-
/**
* cs_dsp_mem_region_name() - Return a name string for a memory type
* @type: the memory type to match
@@ -388,18 +352,14 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_mem_region_name, "FW_CS_DSP");
#ifdef CONFIG_DEBUG_FS
static void cs_dsp_debugfs_save_wmfwname(struct cs_dsp *dsp, const char *s)
{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
kfree(dsp->wmfw_file_name);
- dsp->wmfw_file_name = tmp;
+ dsp->wmfw_file_name = kstrdup(s, GFP_KERNEL);
}
static void cs_dsp_debugfs_save_binname(struct cs_dsp *dsp, const char *s)
{
- char *tmp = kasprintf(GFP_KERNEL, "%s\n", s);
-
kfree(dsp->bin_file_name);
- dsp->bin_file_name = tmp;
+ dsp->bin_file_name = kstrdup(s, GFP_KERNEL);
}
static void cs_dsp_debugfs_clear(struct cs_dsp *dsp)
@@ -410,24 +370,33 @@ static void cs_dsp_debugfs_clear(struct cs_dsp *dsp)
dsp->bin_file_name = NULL;
}
+static ssize_t cs_dsp_debugfs_string_read(struct cs_dsp *dsp,
+ char __user *user_buf,
+ size_t count, loff_t *ppos,
+ const char **pstr)
+{
+ const char *str __free(kfree) = NULL;
+
+ scoped_guard(mutex, &dsp->pwr_lock) {
+ if (!*pstr)
+ return 0;
+
+ str = kasprintf(GFP_KERNEL, "%s\n", *pstr);
+ if (!str)
+ return -ENOMEM;
+
+ return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str));
+ }
+}
+
static ssize_t cs_dsp_debugfs_wmfw_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct cs_dsp *dsp = file->private_data;
- ssize_t ret;
-
- mutex_lock(&dsp->pwr_lock);
- if (!dsp->wmfw_file_name || !dsp->booted)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->wmfw_file_name,
- strlen(dsp->wmfw_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
+ return cs_dsp_debugfs_string_read(dsp, user_buf, count, ppos,
+ &dsp->wmfw_file_name);
}
static ssize_t cs_dsp_debugfs_bin_read(struct file *file,
@@ -435,19 +404,9 @@ static ssize_t cs_dsp_debugfs_bin_read(struct file *file,
size_t count, loff_t *ppos)
{
struct cs_dsp *dsp = file->private_data;
- ssize_t ret;
-
- mutex_lock(&dsp->pwr_lock);
- if (!dsp->bin_file_name || !dsp->booted)
- ret = 0;
- else
- ret = simple_read_from_buffer(user_buf, count, ppos,
- dsp->bin_file_name,
- strlen(dsp->bin_file_name));
-
- mutex_unlock(&dsp->pwr_lock);
- return ret;
+ return cs_dsp_debugfs_string_read(dsp, user_buf, count, ppos,
+ &dsp->bin_file_name);
}
static const struct {
@@ -479,9 +438,11 @@ static int cs_dsp_debugfs_read_controls_show(struct seq_file *s, void *ignored)
struct cs_dsp_coeff_ctl *ctl;
unsigned int reg;
+ guard(mutex)(&dsp->pwr_lock);
+
list_for_each_entry(ctl, &dsp->ctl_list, list) {
cs_dsp_coeff_base_reg(ctl, &reg, 0);
- seq_printf(s, "%22.*s: %#8zx %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n",
+ seq_printf(s, "%22.*s: %#8x %s:%08x %#8x %s %#8x %#4x %c%c%c%c %s %s\n",
ctl->subname_len, ctl->subname, ctl->len,
cs_dsp_mem_region_name(ctl->alg_region.type),
ctl->offset, reg, ctl->fw_name, ctl->alg_region.alg, ctl->type,
@@ -1028,7 +989,7 @@ static void cs_dsp_signal_event_controls(struct cs_dsp *dsp,
static void cs_dsp_free_ctl_blk(struct cs_dsp_coeff_ctl *ctl)
{
- kfree(ctl->cache);
+ kvfree(ctl->cache);
kfree(ctl->subname);
kfree(ctl);
}
@@ -1078,7 +1039,7 @@ static int cs_dsp_create_control(struct cs_dsp *dsp,
ctl->type = type;
ctl->offset = offset;
ctl->len = len;
- ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+ ctl->cache = kvzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
ret = -ENOMEM;
goto err_ctl_subname;
@@ -1096,7 +1057,7 @@ static int cs_dsp_create_control(struct cs_dsp *dsp,
err_list_del:
list_del(&ctl->list);
- kfree(ctl->cache);
+ kvfree(ctl->cache);
err_ctl_subname:
kfree(ctl->subname);
err_ctl:
@@ -1485,7 +1446,9 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
const struct wmfw_region *region;
const struct cs_dsp_region *mem;
const char *region_name;
- struct cs_dsp_buf *buf;
+ u8 *buf __free(kfree) = NULL;
+ size_t buf_len = 0;
+ size_t region_len;
unsigned int reg;
int regions = 0;
int ret, offset, type;
@@ -1605,23 +1568,23 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
region_name);
if (reg) {
- buf = cs_dsp_buf_alloc(region->data,
- le32_to_cpu(region->len),
- &buf_list);
- if (!buf) {
- cs_dsp_err(dsp, "Out of memory\n");
- ret = -ENOMEM;
- goto out_fw;
+ region_len = le32_to_cpu(region->len);
+ if (region_len > buf_len) {
+ buf_len = round_up(region_len, PAGE_SIZE);
+ kfree(buf);
+ buf = kmalloc(buf_len, GFP_KERNEL | GFP_DMA);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out_fw;
+ }
}
- ret = regmap_raw_write(regmap, reg, buf->buf,
- le32_to_cpu(region->len));
+ memcpy(buf, region->data, region_len);
+ ret = regmap_raw_write(regmap, reg, buf, region_len);
if (ret != 0) {
cs_dsp_err(dsp,
- "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
- file, regions,
- le32_to_cpu(region->len), offset,
- region_name, ret);
+ "%s.%d: Failed to write %zu bytes at %d in %s: %d\n",
+ file, regions, region_len, offset, region_name, ret);
goto out_fw;
}
}
@@ -1638,8 +1601,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
ret = 0;
out_fw:
- cs_dsp_buf_free(&buf_list);
-
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
@@ -2171,7 +2132,9 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
struct cs_dsp_alg_region *alg_region;
const char *region_name;
int ret, pos, blocks, type, offset, reg, version;
- struct cs_dsp_buf *buf;
+ u8 *buf __free(kfree) = NULL;
+ size_t buf_len = 0;
+ size_t region_len;
if (!firmware)
return 0;
@@ -2313,20 +2276,22 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
}
if (reg) {
- buf = cs_dsp_buf_alloc(blk->data,
- le32_to_cpu(blk->len),
- &buf_list);
- if (!buf) {
- cs_dsp_err(dsp, "Out of memory\n");
- ret = -ENOMEM;
- goto out_fw;
+ region_len = le32_to_cpu(blk->len);
+ if (region_len > buf_len) {
+ buf_len = round_up(region_len, PAGE_SIZE);
+ kfree(buf);
+ buf = kmalloc(buf_len, GFP_KERNEL | GFP_DMA);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto out_fw;
+ }
}
- cs_dsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
- file, blocks, le32_to_cpu(blk->len),
- reg);
- ret = regmap_raw_write(regmap, reg, buf->buf,
- le32_to_cpu(blk->len));
+ memcpy(buf, blk->data, region_len);
+
+ cs_dsp_dbg(dsp, "%s.%d: Writing %zu bytes at %x\n",
+ file, blocks, region_len, reg);
+ ret = regmap_raw_write(regmap, reg, buf, region_len);
if (ret != 0) {
cs_dsp_err(dsp,
"%s.%d: Failed to write to %x in %s: %d\n",
@@ -2346,8 +2311,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
ret = 0;
out_fw:
- cs_dsp_buf_free(&buf_list);
-
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
@@ -2366,6 +2329,9 @@ static int cs_dsp_create_name(struct cs_dsp *dsp)
return 0;
}
+static const struct cs_dsp_client_ops cs_dsp_default_client_ops = {
+};
+
static int cs_dsp_common_init(struct cs_dsp *dsp)
{
int ret;
@@ -2379,6 +2345,9 @@ static int cs_dsp_common_init(struct cs_dsp *dsp)
mutex_init(&dsp->pwr_lock);
+ if (!dsp->client_ops)
+ dsp->client_ops = &cs_dsp_default_client_ops;
+
#ifdef CONFIG_DEBUG_FS
/* Ensure this is invalid if client never provides a debugfs root */
dsp->debugfs_root = ERR_PTR(-ENODEV);
diff --git a/drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c b/drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
index 8a9b66a3b7d3..e5a389808e5f 100644
--- a/drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
+++ b/drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
@@ -600,6 +600,7 @@ KUNIT_ARRAY_PARAM(cs_dsp_callbacks_ops,
static const struct cs_dsp_callbacks_test_param cs_dsp_no_callbacks_cases[] = {
{ .ops = &cs_dsp_callback_test_empty_client_ops, .case_name = "empty ops" },
+ { .ops = NULL, .case_name = "NULL ops" },
};
KUNIT_ARRAY_PARAM(cs_dsp_no_callbacks,
diff --git a/drivers/firmware/efi/cper-arm.c b/drivers/firmware/efi/cper-arm.c
index f0a63d09d3c4..76542a53e202 100644
--- a/drivers/firmware/efi/cper-arm.c
+++ b/drivers/firmware/efi/cper-arm.c
@@ -93,15 +93,11 @@ static void cper_print_arm_err_info(const char *pfx, u32 type,
bool proc_context_corrupt, corrected, precise_pc, restartable_pc;
bool time_out, access_mode;
- /* If the type is unknown, bail. */
- if (type > CPER_ARM_MAX_TYPE)
- return;
-
/*
* Vendor type errors have error information values that are vendor
* specific.
*/
- if (type == CPER_ARM_VENDOR_ERROR)
+ if (type & CPER_ARM_VENDOR_ERROR)
return;
if (error_info & CPER_ARM_ERR_VALID_TRANSACTION_TYPE) {
@@ -116,43 +112,38 @@ static void cper_print_arm_err_info(const char *pfx, u32 type,
if (error_info & CPER_ARM_ERR_VALID_OPERATION_TYPE) {
op_type = ((error_info >> CPER_ARM_ERR_OPERATION_SHIFT)
& CPER_ARM_ERR_OPERATION_MASK);
- switch (type) {
- case CPER_ARM_CACHE_ERROR:
+ if (type & CPER_ARM_CACHE_ERROR) {
if (op_type < ARRAY_SIZE(arm_cache_err_op_strs)) {
- printk("%soperation type: %s\n", pfx,
+ printk("%scache error, operation type: %s\n", pfx,
arm_cache_err_op_strs[op_type]);
}
- break;
- case CPER_ARM_TLB_ERROR:
+ }
+ if (type & CPER_ARM_TLB_ERROR) {
if (op_type < ARRAY_SIZE(arm_tlb_err_op_strs)) {
- printk("%soperation type: %s\n", pfx,
+ printk("%sTLB error, operation type: %s\n", pfx,
arm_tlb_err_op_strs[op_type]);
}
- break;
- case CPER_ARM_BUS_ERROR:
+ }
+ if (type & CPER_ARM_BUS_ERROR) {
if (op_type < ARRAY_SIZE(arm_bus_err_op_strs)) {
- printk("%soperation type: %s\n", pfx,
+ printk("%sbus error, operation type: %s\n", pfx,
arm_bus_err_op_strs[op_type]);
}
- break;
}
}
if (error_info & CPER_ARM_ERR_VALID_LEVEL) {
level = ((error_info >> CPER_ARM_ERR_LEVEL_SHIFT)
& CPER_ARM_ERR_LEVEL_MASK);
- switch (type) {
- case CPER_ARM_CACHE_ERROR:
+ if (type & CPER_ARM_CACHE_ERROR)
printk("%scache level: %d\n", pfx, level);
- break;
- case CPER_ARM_TLB_ERROR:
+
+ if (type & CPER_ARM_TLB_ERROR)
printk("%sTLB level: %d\n", pfx, level);
- break;
- case CPER_ARM_BUS_ERROR:
+
+ if (type & CPER_ARM_BUS_ERROR)
printk("%saffinity level at which the bus error occurred: %d\n",
pfx, level);
- break;
- }
}
if (error_info & CPER_ARM_ERR_VALID_PROC_CONTEXT_CORRUPT) {
@@ -240,7 +231,8 @@ void cper_print_proc_arm(const char *pfx,
int i, len, max_ctx_type;
struct cper_arm_err_info *err_info;
struct cper_arm_ctx_info *ctx_info;
- char newpfx[64], infopfx[64];
+ char newpfx[64], infopfx[ARRAY_SIZE(newpfx) + 1];
+ char error_type[120];
printk("%sMIDR: 0x%016llx\n", pfx, proc->midr);
@@ -289,9 +281,15 @@ void cper_print_proc_arm(const char *pfx,
newpfx);
}
- printk("%serror_type: %d, %s\n", newpfx, err_info->type,
- err_info->type < ARRAY_SIZE(cper_proc_error_type_strs) ?
- cper_proc_error_type_strs[err_info->type] : "unknown");
+ cper_bits_to_str(error_type, sizeof(error_type),
+ FIELD_GET(CPER_ARM_ERR_TYPE_MASK, err_info->type),
+ cper_proc_error_type_strs,
+ ARRAY_SIZE(cper_proc_error_type_strs));
+
+ printk("%serror_type: 0x%02x: %s%s\n", newpfx, err_info->type,
+ error_type,
+ (err_info->type & ~CPER_ARM_ERR_TYPE_MASK) ? " with reserved bit(s)" : "");
+
if (err_info->validation_bits & CPER_ARM_INFO_VALID_ERR_INFO) {
printk("%serror_info: 0x%016llx\n", newpfx,
err_info->error_info);
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index 928409199a1a..0232bd040f61 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -12,6 +12,7 @@
* Specification version 2.4.
*/
+#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/time.h>
@@ -69,7 +70,7 @@ const char *cper_severity_str(unsigned int severity)
}
EXPORT_SYMBOL_GPL(cper_severity_str);
-/*
+/**
* cper_print_bits - print strings for set bits
* @pfx: prefix for each line, including log level and prefix string
* @bits: bit mask
@@ -106,6 +107,65 @@ void cper_print_bits(const char *pfx, unsigned int bits,
printk("%s\n", buf);
}
+/**
+ * cper_bits_to_str - return a string for set bits
+ * @buf: buffer to store the output string
+ * @buf_size: size of the output string buffer
+ * @bits: bit mask
+ * @strs: string array, indexed by bit position
+ * @strs_size: size of the string array: @strs
+ *
+ * Add to @buf the bitmask in hexadecimal. Then, for each set bit in @bits,
+ * add the corresponding string describing the bit in @strs to @buf.
+ *
+ * A typical example is::
+ *
+ * const char * const bits[] = {
+ * "bit 3 name",
+ * "bit 4 name",
+ * "bit 5 name",
+ * };
+ * char str[120];
+ * unsigned int bitmask = BIT(3) | BIT(5);
+ * #define MASK GENMASK(5,3)
+ *
+ * cper_bits_to_str(str, sizeof(str), FIELD_GET(MASK, bitmask),
+ * bits, ARRAY_SIZE(bits));
+ *
+ * The above code fills the string ``str`` with ``bit 3 name|bit 5 name``.
+ *
+ * Return: number of bytes stored or an error code if lower than zero.
+ */
+int cper_bits_to_str(char *buf, int buf_size, unsigned long bits,
+ const char * const strs[], unsigned int strs_size)
+{
+ int len = buf_size;
+ char *str = buf;
+ int i, size;
+
+ *buf = '\0';
+
+ for_each_set_bit(i, &bits, strs_size) {
+ if (!(bits & BIT_ULL(i)))
+ continue;
+
+ if (*buf && len > 0) {
+ *str = '|';
+ len--;
+ str++;
+ }
+
+ size = strscpy(str, strs[i], len);
+ if (size < 0)
+ return size;
+
+ len -= size;
+ str += size;
+ }
+ return len - buf_size;
+}
+EXPORT_SYMBOL_GPL(cper_bits_to_str);
+
static const char * const proc_type_strs[] = {
"IA32/X64",
"IA64",
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 1ce428e2ac8a..a9070d00b833 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -74,6 +74,9 @@ struct mm_struct efi_mm = {
.page_table_lock = __SPIN_LOCK_UNLOCKED(efi_mm.page_table_lock),
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
.cpu_bitmap = { [BITS_TO_LONGS(NR_CPUS)] = 0},
+#ifdef CONFIG_SCHED_MM_CID
+ .mm_cid.lock = __RAW_SPIN_LOCK_UNLOCKED(efi_mm.mm_cid.lock),
+#endif
};
struct workqueue_struct *efi_rts_wq;
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index 94b05e4451dd..7d15a85d579f 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -11,12 +11,12 @@ cflags-y := $(KBUILD_CFLAGS)
cflags-$(CONFIG_X86_32) := -march=i386
cflags-$(CONFIG_X86_64) := -mcmodel=small
-cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -std=gnu11 \
+cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -std=gnu11 -fms-extensions \
-fPIC -fno-strict-aliasing -mno-red-zone \
-mno-mmx -mno-sse -fshort-wchar \
-Wno-pointer-sign \
$(call cc-disable-warning, address-of-packed-member) \
- $(call cc-disable-warning, gnu) \
+ $(if $(CONFIG_CC_IS_CLANG),-Wno-gnu -Wno-microsoft-anon-tag) \
-fno-asynchronous-unwind-tables \
$(CLANG_FLAGS)
diff --git a/drivers/firmware/efi/libstub/efi-stub.c b/drivers/firmware/efi/libstub/efi-stub.c
index 874f63b4a383..9cb814c5ba1b 100644
--- a/drivers/firmware/efi/libstub/efi-stub.c
+++ b/drivers/firmware/efi/libstub/efi-stub.c
@@ -56,7 +56,7 @@ static struct screen_info *setup_graphics(void)
{
struct screen_info *si, tmp = {};
- if (efi_setup_gop(&tmp) != EFI_SUCCESS)
+ if (efi_setup_graphics(&tmp, NULL) != EFI_SUCCESS)
return NULL;
si = alloc_screen_info();
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index f5ba032863a9..b2fb0c3fa721 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -34,6 +34,9 @@
#define EFI_ALLOC_LIMIT ULONG_MAX
#endif
+struct edid_info;
+struct screen_info;
+
extern bool efi_no5lvl;
extern bool efi_nochunk;
extern bool efi_nokaslr;
@@ -578,6 +581,32 @@ union efi_graphics_output_protocol {
} mixed_mode;
};
+typedef union efi_edid_discovered_protocol efi_edid_discovered_protocol_t;
+
+union efi_edid_discovered_protocol {
+ struct {
+ u32 size_of_edid;
+ u8 *edid;
+ };
+ struct {
+ u32 size_of_edid;
+ u32 edid;
+ } mixed_mode;
+};
+
+typedef union efi_edid_active_protocol efi_edid_active_protocol_t;
+
+union efi_edid_active_protocol {
+ struct {
+ u32 size_of_edid;
+ u8 *edid;
+ };
+ struct {
+ u32 size_of_edid;
+ u32 edid;
+ } mixed_mode;
+};
+
typedef union {
struct {
u32 revision;
@@ -1085,7 +1114,7 @@ efi_status_t efi_parse_options(char const *cmdline);
void efi_parse_option_graphics(char *option);
-efi_status_t efi_setup_gop(struct screen_info *si);
+efi_status_t efi_setup_graphics(struct screen_info *si, struct edid_info *edid);
efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
const efi_char16_t *optstr,
diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c
index 3785fb4986b4..72d74436a7a4 100644
--- a/drivers/firmware/efi/libstub/gop.c
+++ b/drivers/firmware/efi/libstub/gop.c
@@ -12,6 +12,7 @@
#include <linux/string.h>
#include <asm/efi.h>
#include <asm/setup.h>
+#include <video/edid.h>
#include "efistub.h"
@@ -367,24 +368,31 @@ static void find_bits(u32 mask, u8 *pos, u8 *size)
*size = __fls(mask) - *pos + 1;
}
-static void
-setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
- efi_pixel_bitmask_t pixel_info, int pixel_format)
+static void setup_screen_info(struct screen_info *si, const efi_graphics_output_protocol_t *gop)
{
- if (pixel_format == PIXEL_BIT_MASK) {
- find_bits(pixel_info.red_mask,
- &si->red_pos, &si->red_size);
- find_bits(pixel_info.green_mask,
- &si->green_pos, &si->green_size);
- find_bits(pixel_info.blue_mask,
- &si->blue_pos, &si->blue_size);
- find_bits(pixel_info.reserved_mask,
- &si->rsvd_pos, &si->rsvd_size);
- si->lfb_depth = si->red_size + si->green_size +
- si->blue_size + si->rsvd_size;
- si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
+ const efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
+ const efi_graphics_output_mode_info_t *info = efi_table_attr(mode, info);
+
+ si->orig_video_isVGA = VIDEO_TYPE_EFI;
+
+ si->lfb_width = info->horizontal_resolution;
+ si->lfb_height = info->vertical_resolution;
+
+ efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
+ &si->lfb_base, &si->ext_lfb_base);
+ if (si->ext_lfb_base)
+ si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
+ si->pages = 1;
+
+ if (info->pixel_format == PIXEL_BIT_MASK) {
+ find_bits(info->pixel_information.red_mask, &si->red_pos, &si->red_size);
+ find_bits(info->pixel_information.green_mask, &si->green_pos, &si->green_size);
+ find_bits(info->pixel_information.blue_mask, &si->blue_pos, &si->blue_size);
+ find_bits(info->pixel_information.reserved_mask, &si->rsvd_pos, &si->rsvd_size);
+ si->lfb_depth = si->red_size + si->green_size + si->blue_size + si->rsvd_size;
+ si->lfb_linelength = (info->pixels_per_scan_line * si->lfb_depth) / 8;
} else {
- if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
+ if (info->pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
si->red_pos = 0;
si->blue_pos = 16;
} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
@@ -394,20 +402,33 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
si->green_pos = 8;
si->rsvd_pos = 24;
- si->red_size = si->green_size =
- si->blue_size = si->rsvd_size = 8;
-
+ si->red_size = 8;
+ si->green_size = 8;
+ si->blue_size = 8;
+ si->rsvd_size = 8;
si->lfb_depth = 32;
- si->lfb_linelength = pixels_per_scan_line * 4;
+ si->lfb_linelength = info->pixels_per_scan_line * 4;
}
+
+ si->lfb_size = si->lfb_linelength * si->lfb_height;
+ si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
}
-static efi_graphics_output_protocol_t *find_gop(unsigned long num,
- const efi_handle_t handles[])
+static void setup_edid_info(struct edid_info *edid, u32 gop_size_of_edid, u8 *gop_edid)
+{
+ if (!gop_edid || gop_size_of_edid < 128)
+ memset(edid->dummy, 0, sizeof(edid->dummy));
+ else
+ memcpy(edid->dummy, gop_edid, min(gop_size_of_edid, sizeof(edid->dummy)));
+}
+
+static efi_handle_t find_handle_with_primary_gop(unsigned long num, const efi_handle_t handles[],
+ efi_graphics_output_protocol_t **found_gop)
{
efi_graphics_output_protocol_t *first_gop;
- efi_handle_t h;
+ efi_handle_t h, first_gop_handle;
+ first_gop_handle = NULL;
first_gop = NULL;
for_each_efi_handle(h, handles, num) {
@@ -442,21 +463,25 @@ static efi_graphics_output_protocol_t *find_gop(unsigned long num,
*/
status = efi_bs_call(handle_protocol, h,
&EFI_CONSOLE_OUT_DEVICE_GUID, &dummy);
- if (status == EFI_SUCCESS)
- return gop;
-
- if (!first_gop)
+ if (status == EFI_SUCCESS) {
+ if (found_gop)
+ *found_gop = gop;
+ return h;
+ } else if (!first_gop_handle) {
+ first_gop_handle = h;
first_gop = gop;
+ }
}
- return first_gop;
+ if (found_gop)
+ *found_gop = first_gop;
+ return first_gop_handle;
}
-efi_status_t efi_setup_gop(struct screen_info *si)
+efi_status_t efi_setup_graphics(struct screen_info *si, struct edid_info *edid)
{
efi_handle_t *handles __free(efi_pool) = NULL;
- efi_graphics_output_protocol_mode_t *mode;
- efi_graphics_output_mode_info_t *info;
+ efi_handle_t handle;
efi_graphics_output_protocol_t *gop;
efi_status_t status;
unsigned long num;
@@ -467,35 +492,41 @@ efi_status_t efi_setup_gop(struct screen_info *si)
if (status != EFI_SUCCESS)
return status;
- gop = find_gop(num, handles);
- if (!gop)
+ handle = find_handle_with_primary_gop(num, handles, &gop);
+ if (!handle)
return EFI_NOT_FOUND;
/* Change mode if requested */
set_mode(gop);
/* EFI framebuffer */
- mode = efi_table_attr(gop, mode);
- info = efi_table_attr(mode, info);
-
- si->orig_video_isVGA = VIDEO_TYPE_EFI;
-
- si->lfb_width = info->horizontal_resolution;
- si->lfb_height = info->vertical_resolution;
-
- efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
- &si->lfb_base, &si->ext_lfb_base);
- if (si->ext_lfb_base)
- si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
-
- si->pages = 1;
-
- setup_pixel_info(si, info->pixels_per_scan_line,
- info->pixel_information, info->pixel_format);
-
- si->lfb_size = si->lfb_linelength * si->lfb_height;
+ if (si)
+ setup_screen_info(si, gop);
+
+ /* Display EDID for primary GOP */
+ if (edid) {
+ efi_edid_discovered_protocol_t *discovered_edid;
+ efi_edid_active_protocol_t *active_edid;
+ u32 gop_size_of_edid = 0;
+ u8 *gop_edid = NULL;
+
+ status = efi_bs_call(handle_protocol, handle, &EFI_EDID_ACTIVE_PROTOCOL_GUID,
+ (void **)&active_edid);
+ if (status == EFI_SUCCESS) {
+ gop_size_of_edid = active_edid->size_of_edid;
+ gop_edid = active_edid->edid;
+ } else {
+ status = efi_bs_call(handle_protocol, handle,
+ &EFI_EDID_DISCOVERED_PROTOCOL_GUID,
+ (void **)&discovered_edid);
+ if (status == EFI_SUCCESS) {
+ gop_size_of_edid = discovered_edid->size_of_edid;
+ gop_edid = discovered_edid->edid;
+ }
+ }
- si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
+ setup_edid_info(edid, gop_size_of_edid, gop_edid);
+ }
return EFI_SUCCESS;
}
diff --git a/drivers/firmware/efi/libstub/x86-5lvl.c b/drivers/firmware/efi/libstub/x86-5lvl.c
index f1c5fb45d5f7..c00d0ae7ed5d 100644
--- a/drivers/firmware/efi/libstub/x86-5lvl.c
+++ b/drivers/firmware/efi/libstub/x86-5lvl.c
@@ -66,7 +66,7 @@ void efi_5level_switch(void)
bool have_la57 = native_read_cr4() & X86_CR4_LA57;
bool need_toggle = want_la57 ^ have_la57;
u64 *pgt = (void *)la57_toggle + PAGE_SIZE;
- u64 *cr3 = (u64 *)__native_read_cr3();
+ pgd_t *cr3 = (pgd_t *)native_read_cr3_pa();
u64 *new_cr3;
if (!la57_toggle || !need_toggle)
@@ -82,7 +82,7 @@ void efi_5level_switch(void)
new_cr3[0] = (u64)cr3 | _PAGE_TABLE_NOENC;
} else {
/* take the new root table pointer from the current entry #0 */
- new_cr3 = (u64 *)(cr3[0] & PAGE_MASK);
+ new_cr3 = (u64 *)(native_pgd_val(cr3[0]) & PTE_PFN_MASK);
/* copy the new root table if it is not 32-bit addressable */
if ((u64)new_cr3 > U32_MAX)
diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c
index 761121a77f9e..cef32e2c82d8 100644
--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -203,6 +203,104 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
}
}
+struct smbios_entry_point {
+ u8 anchor[4];
+ u8 ep_checksum;
+ u8 ep_length;
+ u8 major_version;
+ u8 minor_version;
+ u16 max_size_entry;
+ u8 ep_rev;
+ u8 reserved[5];
+
+ struct __packed {
+ u8 anchor[5];
+ u8 checksum;
+ u16 st_length;
+ u32 st_address;
+ u16 number_of_entries;
+ u8 bcd_rev;
+ } intm;
+};
+
+static bool verify_ep_checksum(const void *ptr, int length)
+{
+ u8 sum = 0;
+
+ for (int i = 0; i < length; i++)
+ sum += ((u8 *)ptr)[i];
+
+ return sum == 0;
+}
+
+static bool verify_ep_integrity(const struct smbios_entry_point *ep)
+{
+ if (memcmp(ep->anchor, "_SM_", sizeof(ep->anchor)) != 0)
+ return false;
+
+ if (memcmp(ep->intm.anchor, "_DMI_", sizeof(ep->intm.anchor)) != 0)
+ return false;
+
+ if (!verify_ep_checksum(ep, ep->ep_length) ||
+ !verify_ep_checksum(&ep->intm, sizeof(ep->intm)))
+ return false;
+
+ return true;
+}
+
+static const struct efi_smbios_record *search_record(void *table, u32 length,
+ u8 type)
+{
+ const u8 *p, *end;
+
+ p = (u8 *)table;
+ end = p + length;
+
+ while (p + sizeof(struct efi_smbios_record) < end) {
+ const struct efi_smbios_record *hdr =
+ (struct efi_smbios_record *)p;
+ const u8 *next;
+
+ if (hdr->type == type)
+ return hdr;
+
+ /* Type 127 = End-of-Table */
+ if (hdr->type == 0x7F)
+ return NULL;
+
+ /* Jumping to the unformed section */
+ next = p + hdr->length;
+
+ /* Unformed section ends with 0000h */
+ while ((next[0] != 0 || next[1] != 0) && next + 1 < end)
+ next++;
+
+ next += 2;
+ p = next;
+ }
+
+ return NULL;
+}
+
+static const struct efi_smbios_record *get_table_record(u8 type)
+{
+ const struct smbios_entry_point *ep;
+
+ /*
+ * Locate the legacy 32-bit SMBIOS entrypoint in memory, and parse it
+ * directly. Needed by some Macs that do not implement the EFI protocol.
+ */
+ ep = get_efi_config_table(SMBIOS_TABLE_GUID);
+ if (!ep)
+ return NULL;
+
+ if (!verify_ep_integrity(ep))
+ return NULL;
+
+ return search_record((void *)(unsigned long)ep->intm.st_address,
+ ep->intm.st_length, type);
+}
+
static bool apple_match_product_name(void)
{
static const char type1_product_matches[][15] = {
@@ -218,7 +316,8 @@ static bool apple_match_product_name(void)
const struct efi_smbios_type1_record *record;
const u8 *product;
- record = (struct efi_smbios_type1_record *)efi_get_smbios_record(1);
+ record = (struct efi_smbios_type1_record *)
+ (efi_get_smbios_record(1) ?: get_table_record(1));
if (!record)
return false;
@@ -388,8 +487,9 @@ static void setup_quirks(struct boot_params *boot_params)
static void setup_graphics(struct boot_params *boot_params)
{
struct screen_info *si = memset(&boot_params->screen_info, 0, sizeof(*si));
+ struct edid_info *edid = memset(&boot_params->edid_info, 0, sizeof(*edid));
- efi_setup_gop(si);
+ efi_setup_graphics(si, edid);
}
static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status)
diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c
index c38b1a335590..e727cc5909cb 100644
--- a/drivers/firmware/efi/memattr.c
+++ b/drivers/firmware/efi/memattr.c
@@ -19,19 +19,19 @@ unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR;
* Reserve the memory associated with the Memory Attributes configuration
* table, if it exists.
*/
-int __init efi_memattr_init(void)
+void __init efi_memattr_init(void)
{
efi_memory_attributes_table_t *tbl;
unsigned long size;
if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR)
- return 0;
+ return;
tbl = early_memremap(efi_mem_attr_table, sizeof(*tbl));
if (!tbl) {
pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n",
efi_mem_attr_table);
- return -ENOMEM;
+ return;
}
if (tbl->version > 2) {
@@ -61,7 +61,6 @@ int __init efi_memattr_init(void)
unmap:
early_memunmap(tbl, sizeof(*tbl));
- return 0;
}
/*
diff --git a/drivers/firmware/efi/riscv-runtime.c b/drivers/firmware/efi/riscv-runtime.c
index fa71cd898120..4a2588358be2 100644
--- a/drivers/firmware/efi/riscv-runtime.c
+++ b/drivers/firmware/efi/riscv-runtime.c
@@ -36,20 +36,12 @@ static bool __init efi_virtmap_init(void)
init_new_context(NULL, &efi_mm);
for_each_efi_memory_desc(md) {
- phys_addr_t phys = md->phys_addr;
- int ret;
-
if (!(md->attribute & EFI_MEMORY_RUNTIME))
continue;
if (md->virt_addr == U64_MAX)
return false;
- ret = efi_create_mapping(&efi_mm, md);
- if (ret) {
- pr_warn(" EFI remap %pa: failed to create mapping (%d)\n",
- &phys, ret);
- return false;
- }
+ efi_create_mapping(&efi_mm, md);
}
if (efi_memattr_apply_permissions(&efi_mm, efi_set_mapping_permissions))
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 708b777857d3..da8d29621644 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -202,6 +202,8 @@ void efi_call_virt_check_flags(unsigned long flags, const void *caller)
*/
static DEFINE_SEMAPHORE(efi_runtime_lock, 1);
+static struct task_struct *efi_runtime_lock_owner;
+
/*
* Expose the EFI runtime lock to the UV platform
*/
@@ -219,6 +221,8 @@ static void __nocfi efi_call_rts(struct work_struct *work)
efi_status_t status = EFI_NOT_FOUND;
unsigned long flags;
+ efi_runtime_lock_owner = current;
+
arch_efi_call_virt_setup();
flags = efi_call_virt_save_flags();
@@ -310,6 +314,7 @@ static void __nocfi efi_call_rts(struct work_struct *work)
efi_rts_work.status = status;
complete(&efi_rts_work.efi_rts_comp);
+ efi_runtime_lock_owner = NULL;
}
static efi_status_t __efi_queue_work(enum efi_rts_ids id,
@@ -444,8 +449,10 @@ virt_efi_set_variable_nb(efi_char16_t *name, efi_guid_t *vendor, u32 attr,
if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY;
+ efi_runtime_lock_owner = current;
status = efi_call_virt_pointer(efi.runtime, set_variable, name, vendor,
attr, data_size, data);
+ efi_runtime_lock_owner = NULL;
up(&efi_runtime_lock);
return status;
}
@@ -481,9 +488,11 @@ virt_efi_query_variable_info_nb(u32 attr, u64 *storage_space,
if (down_trylock(&efi_runtime_lock))
return EFI_NOT_READY;
+ efi_runtime_lock_owner = current;
status = efi_call_virt_pointer(efi.runtime, query_variable_info, attr,
storage_space, remaining_space,
max_variable_size);
+ efi_runtime_lock_owner = NULL;
up(&efi_runtime_lock);
return status;
}
@@ -509,12 +518,13 @@ virt_efi_reset_system(int reset_type, efi_status_t status,
return;
}
+ efi_runtime_lock_owner = current;
arch_efi_call_virt_setup();
efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM;
arch_efi_call_virt(efi.runtime, reset_system, reset_type, status,
data_size, data);
arch_efi_call_virt_teardown();
-
+ efi_runtime_lock_owner = NULL;
up(&efi_runtime_lock);
}
@@ -587,3 +597,8 @@ efi_call_acpi_prm_handler(efi_status_t (__efiapi *handler_addr)(u64, void *),
}
#endif
+
+void efi_runtime_assert_lock_held(void)
+{
+ WARN_ON(efi_runtime_lock_owner != current);
+}
diff --git a/drivers/firmware/efi/stmm/mm_communication.h b/drivers/firmware/efi/stmm/mm_communication.h
index 52a1f32cd1eb..06e7663f96dc 100644
--- a/drivers/firmware/efi/stmm/mm_communication.h
+++ b/drivers/firmware/efi/stmm/mm_communication.h
@@ -32,7 +32,7 @@
/**
* struct efi_mm_communicate_header - Header used for SMM variable communication
-
+ *
* @header_guid: header use for disambiguation of content
* @message_len: length of the message. Does not include the size of the
* header
@@ -111,7 +111,7 @@ struct efi_mm_communicate_header {
/**
* struct smm_variable_communicate_header - Used for SMM variable communication
-
+ *
* @function: function to call in Smm.
* @ret_status: return status
* @data: payload
@@ -128,7 +128,7 @@ struct smm_variable_communicate_header {
/**
* struct smm_variable_access - Used to communicate with StMM by
* SetVariable and GetVariable.
-
+ *
* @guid: vendor GUID
* @data_size: size of EFI variable data
* @name_size: size of EFI name
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index e777b7cb9b12..1a6f85e463e0 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -2018,21 +2018,6 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = {
{ }
};
-static bool qcom_scm_qseecom_machine_is_allowed(void)
-{
- struct device_node *np;
- bool match;
-
- np = of_find_node_by_path("/");
- if (!np)
- return false;
-
- match = of_match_node(qcom_scm_qseecom_allowlist, np);
- of_node_put(np);
-
- return match;
-}
-
static void qcom_scm_qseecom_free(void *data)
{
struct platform_device *qseecom_dev = data;
@@ -2064,7 +2049,7 @@ static int qcom_scm_qseecom_init(struct qcom_scm *scm)
dev_info(scm->dev, "qseecom: found qseecom with version 0x%x\n", version);
- if (!qcom_scm_qseecom_machine_is_allowed()) {
+ if (!of_machine_device_match(qcom_scm_qseecom_allowlist)) {
dev_info(scm->dev, "qseecom: untested machine, skipping\n");
return 0;
}
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c
index e3f990d888d7..00f58e27f6de 100644
--- a/drivers/firmware/stratix10-svc.c
+++ b/drivers/firmware/stratix10-svc.c
@@ -134,6 +134,7 @@ struct stratix10_svc_data {
* @complete_status: state for completion
* @svc_fifo_lock: protect access to service message data queue
* @invoke_fn: function to issue secure monitor call or hypervisor call
+ * @svc: manages the list of client svc drivers
*
* This struct is used to create communication channels for service clients, to
* handle secure monitor or hypervisor call.
@@ -150,6 +151,7 @@ struct stratix10_svc_controller {
struct completion complete_status;
spinlock_t svc_fifo_lock;
svc_invoke_fn *invoke_fn;
+ struct stratix10_svc *svc;
};
/**
@@ -1206,6 +1208,7 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
ret = -ENOMEM;
goto err_free_kfifo;
}
+ controller->svc = svc;
svc->stratix10_svc_rsu = platform_device_alloc(STRATIX10_RSU, 0);
if (!svc->stratix10_svc_rsu) {
@@ -1237,8 +1240,6 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
if (ret)
goto err_unregister_fcs_dev;
- dev_set_drvdata(dev, svc);
-
pr_info("Intel Service Layer Driver Initialized\n");
return 0;
@@ -1256,8 +1257,8 @@ err_destroy_pool:
static void stratix10_svc_drv_remove(struct platform_device *pdev)
{
- struct stratix10_svc *svc = dev_get_drvdata(&pdev->dev);
struct stratix10_svc_controller *ctrl = platform_get_drvdata(pdev);
+ struct stratix10_svc *svc = ctrl->svc;
of_platform_depopulate(ctrl->dev);
diff --git a/drivers/gnss/ubx.c b/drivers/gnss/ubx.c
index 92402f6082c4..23894ff75ff9 100644
--- a/drivers/gnss/ubx.c
+++ b/drivers/gnss/ubx.c
@@ -66,6 +66,7 @@ static const struct gnss_serial_ops ubx_gserial_ops = {
static int ubx_probe(struct serdev_device *serdev)
{
struct gnss_serial *gserial;
+ struct gpio_desc *safeboot;
struct gpio_desc *reset;
struct ubx_data *data;
int ret;
@@ -92,6 +93,13 @@ static int ubx_probe(struct serdev_device *serdev)
if (ret < 0 && ret != -ENODEV)
goto err_free_gserial;
+ /* Deassert safeboot */
+ safeboot = devm_gpiod_get_optional(&serdev->dev, "safeboot", GPIOD_OUT_LOW);
+ if (IS_ERR(safeboot)) {
+ ret = PTR_ERR(safeboot);
+ goto err_free_gserial;
+ }
+
/* Deassert reset */
reset = devm_gpiod_get_optional(&serdev->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset)) {
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7ee3afbc2b05..c74da29253e8 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -6,6 +6,9 @@
config GPIOLIB_LEGACY
def_bool y
+config HAVE_SHARED_GPIOS
+ bool
+
menuconfig GPIOLIB
bool "GPIO Support"
help
@@ -42,13 +45,10 @@ config GPIOLIB_IRQCHIP
select IRQ_DOMAIN
bool
-config OF_GPIO_MM_GPIOCHIP
- bool
- help
- This adds support for the legacy 'struct of_mm_gpio_chip' interface
- from PowerPC. Existing drivers using this interface need to select
- this symbol, but new drivers should use the generic gpio-regmap
- infrastructure instead.
+config GPIO_SHARED
+ def_bool y
+ depends on HAVE_SHARED_GPIOS || COMPILE_TEST
+ select AUXILIARY_BUS
config DEBUG_GPIO
bool "Debug GPIO calls"
@@ -476,7 +476,6 @@ config GPIO_MENZ127
config GPIO_MM_LANTIQ
bool "Lantiq Memory mapped GPIOs"
depends on LANTIQ && SOC_XWAY
- select OF_GPIO_MM_GPIOCHIP
help
This enables support for memory mapped GPIOs on the External Bus Unit
(EBU) found on Lantiq SoCs. The GPIOs are output only as they are
@@ -1413,7 +1412,7 @@ config HTC_EGPIO
config GPIO_ELKHARTLAKE
tristate "Intel Elkhart Lake PSE GPIO support"
- depends on X86 || COMPILE_TEST
+ depends on INTEL_EHL_PSE_IO
select GPIO_TANGIER
help
Select this option to enable GPIO support for Intel Elkhart Lake
@@ -1565,6 +1564,15 @@ config GPIO_PMIC_EIC_SPRD
help
Say yes here to support Spreadtrum PMIC EIC device.
+config GPIO_QIXIS_FPGA
+ tristate "NXP QIXIS FPGA GPIO support"
+ depends on MFD_SIMPLE_MFD_I2C || COMPILE_TEST
+ select GPIO_REGMAP
+ help
+ This enables support for the GPIOs found in the QIXIS FPGA which is
+ integrated on some NXP Layerscape boards such as LX2160ARDB and
+ LS1046AQDS.
+
config GPIO_RC5T583
bool "RICOH RC5T583 GPIO"
depends on MFD_RC5T583
@@ -2017,6 +2025,15 @@ config GPIO_SIM
This enables the GPIO simulator - a configfs-based GPIO testing
driver.
+config GPIO_SHARED_PROXY
+ tristate "Proxy driver for non-exclusive GPIOs"
+ default m
+ depends on GPIO_SHARED || COMPILE_TEST
+ select AUXILIARY_BUS
+ help
+ This enables the GPIO shared proxy driver - an abstraction layer
+ for GPIO pins that are shared by multiple devices.
+
endmenu
menu "GPIO Debugging utilities"
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ec296fa14bfd..2421a8fd3733 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o
gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o
obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o
+obj-$(CONFIG_GPIO_SHARED) += gpiolib-shared.o
# Device drivers. Generally keep list sorted alphabetically
obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o
@@ -146,6 +147,7 @@ obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
+obj-$(CONFIG_GPIO_QIXIS_FPGA) += gpio-qixis-fpga.o
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
@@ -159,6 +161,7 @@ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+obj-$(CONFIG_GPIO_SHARED_PROXY) += gpio-shared-proxy.o
obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_SIM) += gpio-sim.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO
index 8ed74e05903a..5acaeab029ec 100644
--- a/drivers/gpio/TODO
+++ b/drivers/gpio/TODO
@@ -86,17 +86,6 @@ Work items:
-------------------------------------------------------------------------------
-Get rid of <linux/gpio/legacy-of-mm-gpiochip.h>
-
-Work items:
-
-- Get rid of struct of_mm_gpio_chip altogether: use the generic MMIO
- GPIO for all current users (see below). Delete struct of_mm_gpio_chip,
- to_of_mm_gpio_chip(), of_mm_gpiochip_add_data(), of_mm_gpiochip_remove(),
- CONFIG_OF_GPIO_MM_GPIOCHIP from the kernel.
-
--------------------------------------------------------------------------------
-
Collect drivers
Collect GPIO drivers from arch/* and other places that should be placed
diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c
index ffe7e1cb6b23..fe5c10cd5c32 100644
--- a/drivers/gpio/gpio-104-idio-16.c
+++ b/drivers/gpio/gpio-104-idio-16.c
@@ -59,6 +59,7 @@ static const struct regmap_config idio_16_regmap_config = {
.reg_stride = 1,
.val_bits = 8,
.io_port = true,
+ .max_register = 0x5,
.wr_table = &idio_16_wr_table,
.rd_table = &idio_16_rd_table,
.volatile_table = &idio_16_rd_table,
diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c
index 37600faf4a4b..416f265d09d0 100644
--- a/drivers/gpio/gpio-aggregator.c
+++ b/drivers/gpio/gpio-aggregator.c
@@ -723,6 +723,7 @@ struct gpiochip_fwd *devm_gpiochip_fwd_alloc(struct device *dev,
chip->get_multiple = gpio_fwd_get_multiple_locked;
chip->set = gpio_fwd_set;
chip->set_multiple = gpio_fwd_set_multiple_locked;
+ chip->set_config = gpio_fwd_set_config;
chip->to_irq = gpio_fwd_to_irq;
chip->base = -1;
chip->ngpio = ngpios;
diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c
index 7953a9c4e36d..2e0ae953dd99 100644
--- a/drivers/gpio/gpio-aspeed.c
+++ b/drivers/gpio/gpio-aspeed.c
@@ -24,12 +24,11 @@
/*
* These two headers aren't meant to be used by GPIO drivers. We need
- * them in order to access gpio_chip_hwgpio() which we need to implement
+ * them in order to access gpiod_hwgpio() which we need to implement
* the aspeed specific API which allows the coprocessor to request
* access to some GPIOs and to arbitrate between coprocessor and ARM.
*/
#include <linux/gpio/consumer.h>
-#include "gpiolib.h"
/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
@@ -942,7 +941,7 @@ int aspeed_gpio_copro_grab_gpio(struct gpio_desc *desc,
{
struct gpio_chip *chip = gpiod_to_chip(desc);
struct aspeed_gpio *gpio = gpiochip_get_data(chip);
- int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
+ int rc = 0, bindex, offset = gpiod_hwgpio(desc);
const struct aspeed_gpio_bank *bank = to_bank(offset);
if (!aspeed_gpio_support_copro(gpio))
@@ -987,7 +986,7 @@ int aspeed_gpio_copro_release_gpio(struct gpio_desc *desc)
{
struct gpio_chip *chip = gpiod_to_chip(desc);
struct aspeed_gpio *gpio = gpiochip_get_data(chip);
- int rc = 0, bindex, offset = gpio_chip_hwgpio(desc);
+ int rc = 0, bindex, offset = gpiod_hwgpio(desc);
if (!aspeed_gpio_support_copro(gpio))
return -EOPNOTSUPP;
diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c
index f40c9472588b..af9287ff5dc4 100644
--- a/drivers/gpio/gpio-brcmstb.c
+++ b/drivers/gpio/gpio-brcmstb.c
@@ -533,7 +533,6 @@ static void brcmstb_gpio_shutdown(struct platform_device *pdev)
brcmstb_gpio_quiesce(&pdev->dev, false);
}
-#ifdef CONFIG_PM_SLEEP
static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
struct brcmstb_gpio_bank *bank)
{
@@ -572,14 +571,9 @@ static int brcmstb_gpio_resume(struct device *dev)
return 0;
}
-#else
-#define brcmstb_gpio_suspend NULL
-#define brcmstb_gpio_resume NULL
-#endif /* CONFIG_PM_SLEEP */
-
static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
- .suspend_noirq = brcmstb_gpio_suspend,
- .resume_noirq = brcmstb_gpio_resume,
+ .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend),
+ .resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume),
};
static int brcmstb_gpio_probe(struct platform_device *pdev)
@@ -755,7 +749,7 @@ static struct platform_driver brcmstb_gpio_driver = {
.driver = {
.name = "brcmstb-gpio",
.of_match_table = brcmstb_gpio_of_match,
- .pm = &brcmstb_gpio_pm_ops,
+ .pm = pm_sleep_ptr(&brcmstb_gpio_pm_ops),
},
.probe = brcmstb_gpio_probe,
.remove = brcmstb_gpio_remove,
diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c
index 05401da03ca3..324eeb77dbd5 100644
--- a/drivers/gpio/gpio-bt8xx.c
+++ b/drivers/gpio/gpio-bt8xx.c
@@ -52,10 +52,8 @@ struct bt8xxgpio {
struct pci_dev *pdev;
struct gpio_chip gpio;
-#ifdef CONFIG_PM
u32 saved_outen;
u32 saved_data;
-#endif
};
#define bgwrite(dat, adr) writel((dat), bg->mmio+(adr))
@@ -224,9 +222,10 @@ static void bt8xxgpio_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
}
-#ifdef CONFIG_PM
-static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state)
+
+static int bt8xxgpio_suspend(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct bt8xxgpio *bg = pci_get_drvdata(pdev);
scoped_guard(spinlock_irqsave, &bg->lock) {
@@ -238,23 +237,13 @@ static int bt8xxgpio_suspend(struct pci_dev *pdev, pm_message_t state)
bgwrite(0x0, BT848_GPIO_OUT_EN);
}
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
-
return 0;
}
-static int bt8xxgpio_resume(struct pci_dev *pdev)
+static int bt8xxgpio_resume(struct device *dev)
{
+ struct pci_dev *pdev = to_pci_dev(dev);
struct bt8xxgpio *bg = pci_get_drvdata(pdev);
- int err;
-
- pci_set_power_state(pdev, PCI_D0);
- err = pci_enable_device(pdev);
- if (err)
- return err;
- pci_restore_state(pdev);
guard(spinlock_irqsave)(&bg->lock);
@@ -267,10 +256,8 @@ static int bt8xxgpio_resume(struct pci_dev *pdev)
return 0;
}
-#else
-#define bt8xxgpio_suspend NULL
-#define bt8xxgpio_resume NULL
-#endif /* CONFIG_PM */
+
+static DEFINE_SIMPLE_DEV_PM_OPS(bt8xxgpio_pm_ops, bt8xxgpio_suspend, bt8xxgpio_resume);
static const struct pci_device_id bt8xxgpio_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848) },
@@ -286,8 +273,7 @@ static struct pci_driver bt8xxgpio_pci_driver = {
.id_table = bt8xxgpio_pci_tbl,
.probe = bt8xxgpio_probe,
.remove = bt8xxgpio_remove,
- .suspend = bt8xxgpio_suspend,
- .resume = bt8xxgpio_resume,
+ .driver.pm = &bt8xxgpio_pm_ops,
};
module_pci_driver(bt8xxgpio_pci_driver);
diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c
index b42ff46d292b..4986c465c9a8 100644
--- a/drivers/gpio/gpio-dwapb.c
+++ b/drivers/gpio/gpio-dwapb.c
@@ -79,7 +79,6 @@ struct dwapb_platform_data {
unsigned int nports;
};
-#ifdef CONFIG_PM_SLEEP
/* Store GPIO context across system-wide suspend/resume transitions */
struct dwapb_context {
u32 data;
@@ -92,7 +91,6 @@ struct dwapb_context {
u32 int_deb;
u32 wake_en;
};
-#endif
struct dwapb_gpio_port_irqchip {
unsigned int nr_irqs;
@@ -103,9 +101,7 @@ struct dwapb_gpio_port {
struct gpio_generic_chip chip;
struct dwapb_gpio_port_irqchip *pirq;
struct dwapb_gpio *gpio;
-#ifdef CONFIG_PM_SLEEP
struct dwapb_context *ctx;
-#endif
unsigned int idx;
};
@@ -363,7 +359,6 @@ static int dwapb_irq_set_type(struct irq_data *d, u32 type)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
@@ -378,9 +373,6 @@ static int dwapb_irq_set_wake(struct irq_data *d, unsigned int enable)
return 0;
}
-#else
-#define dwapb_irq_set_wake NULL
-#endif
static const struct irq_chip dwapb_irq_chip = {
.name = DWAPB_DRIVER_NAME,
@@ -390,7 +382,7 @@ static const struct irq_chip dwapb_irq_chip = {
.irq_set_type = dwapb_irq_set_type,
.irq_enable = dwapb_irq_enable,
.irq_disable = dwapb_irq_disable,
- .irq_set_wake = dwapb_irq_set_wake,
+ .irq_set_wake = pm_sleep_ptr(dwapb_irq_set_wake),
.flags = IRQCHIP_IMMUTABLE,
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
@@ -759,7 +751,6 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_SLEEP
static int dwapb_gpio_suspend(struct device *dev)
{
struct dwapb_gpio *gpio = dev_get_drvdata(dev);
@@ -844,15 +835,14 @@ static int dwapb_gpio_resume(struct device *dev)
return 0;
}
-#endif
-static SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops, dwapb_gpio_suspend,
- dwapb_gpio_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(dwapb_gpio_pm_ops,
+ dwapb_gpio_suspend, dwapb_gpio_resume);
static struct platform_driver dwapb_gpio_driver = {
.driver = {
.name = DWAPB_DRIVER_NAME,
- .pm = &dwapb_gpio_pm_ops,
+ .pm = pm_sleep_ptr(&dwapb_gpio_pm_ops),
.of_match_table = dwapb_of_match,
.acpi_match_table = dwapb_acpi_match,
},
diff --git a/drivers/gpio/gpio-elkhartlake.c b/drivers/gpio/gpio-elkhartlake.c
index 95de52d2cc63..b96e7928b6e5 100644
--- a/drivers/gpio/gpio-elkhartlake.c
+++ b/drivers/gpio/gpio-elkhartlake.c
@@ -2,43 +2,46 @@
/*
* Intel Elkhart Lake PSE GPIO driver
*
- * Copyright (c) 2023 Intel Corporation.
+ * Copyright (c) 2023, 2025 Intel Corporation.
*
* Authors: Pandith N <pandith.n@intel.com>
* Raag Jadav <raag.jadav@intel.com>
*/
+#include <linux/auxiliary_bus.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/pm.h>
+#include <linux/ehl_pse_io_aux.h>
+
#include "gpio-tangier.h"
/* Each Intel EHL PSE GPIO Controller has 30 GPIO pins */
#define EHL_PSE_NGPIO 30
-static int ehl_gpio_probe(struct platform_device *pdev)
+static int ehl_gpio_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)
{
- struct device *dev = &pdev->dev;
+ struct device *dev = &adev->dev;
+ struct ehl_pse_io_data *data;
struct tng_gpio *priv;
- int irq, ret;
+ int ret;
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ data = dev_get_platdata(dev);
+ if (!data)
+ return -ENODATA;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ priv->reg_base = devm_ioremap_resource(dev, &data->mem);
if (IS_ERR(priv->reg_base))
return PTR_ERR(priv->reg_base);
priv->dev = dev;
- priv->irq = irq;
+ priv->irq = data->irq;
priv->info.base = -1;
priv->info.ngpio = EHL_PSE_NGPIO;
@@ -51,25 +54,24 @@ static int ehl_gpio_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "tng_gpio_probe error\n");
- platform_set_drvdata(pdev, priv);
+ auxiliary_set_drvdata(adev, priv);
return 0;
}
-static const struct platform_device_id ehl_gpio_ids[] = {
- { "gpio-elkhartlake" },
+static const struct auxiliary_device_id ehl_gpio_ids[] = {
+ { EHL_PSE_IO_NAME "." EHL_PSE_GPIO_NAME },
{ }
};
-MODULE_DEVICE_TABLE(platform, ehl_gpio_ids);
+MODULE_DEVICE_TABLE(auxiliary, ehl_gpio_ids);
-static struct platform_driver ehl_gpio_driver = {
+static struct auxiliary_driver ehl_gpio_driver = {
.driver = {
- .name = "gpio-elkhartlake",
.pm = pm_sleep_ptr(&tng_gpio_pm_ops),
},
.probe = ehl_gpio_probe,
.id_table = ehl_gpio_ids,
};
-module_platform_driver(ehl_gpio_driver);
+module_auxiliary_driver(ehl_gpio_driver);
MODULE_AUTHOR("Pandith N <pandith.n@intel.com>");
MODULE_AUTHOR("Raag Jadav <raag.jadav@intel.com>");
diff --git a/drivers/gpio/gpio-fxl6408.c b/drivers/gpio/gpio-fxl6408.c
index 86ebc66b1104..afc1b8461dab 100644
--- a/drivers/gpio/gpio-fxl6408.c
+++ b/drivers/gpio/gpio-fxl6408.c
@@ -123,6 +123,8 @@ static int fxl6408_probe(struct i2c_client *client)
if (ret)
return ret;
+ i2c_set_clientdata(client, gpio_config.regmap);
+
/* Disable High-Z of outputs, so that our OUTPUT updates actually take effect. */
ret = regmap_write(gpio_config.regmap, FXL6408_REG_OUTPUT_HIGH_Z, 0);
if (ret)
@@ -131,6 +133,16 @@ static int fxl6408_probe(struct i2c_client *client)
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
}
+static int fxl6408_resume(struct device *dev)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ regcache_mark_dirty(regmap);
+ return regcache_sync(regmap);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(fxl6408_pm_ops, NULL, fxl6408_resume);
+
static const __maybe_unused struct of_device_id fxl6408_dt_ids[] = {
{ .compatible = "fcs,fxl6408" },
{ }
@@ -146,6 +158,7 @@ MODULE_DEVICE_TABLE(i2c, fxl6408_id);
static struct i2c_driver fxl6408_driver = {
.driver = {
.name = "fxl6408",
+ .pm = pm_sleep_ptr(&fxl6408_pm_ops),
.of_match_table = fxl6408_dt_ids,
},
.probe = fxl6408_probe,
diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c
index 0c0f97fa14fc..e4fa84e22726 100644
--- a/drivers/gpio/gpio-grgpio.c
+++ b/drivers/gpio/gpio-grgpio.c
@@ -46,7 +46,7 @@
/* Structure for an irq of the core - called an underlying irq */
struct grgpio_uirq {
- u8 refcnt; /* Reference counter to manage requesting/freeing of uirq */
+ atomic_t refcnt; /* Reference counter to manage requesting/freeing of uirq */
u8 uirq; /* Underlying irq of the gpio driver */
};
@@ -242,30 +242,22 @@ static int grgpio_irq_map(struct irq_domain *d, unsigned int irq,
irq, offset);
gpio_generic_chip_lock_irqsave(&priv->chip, flags);
-
- /* Request underlying irq if not already requested */
lirq->irq = irq;
uirq = &priv->uirqs[lirq->index];
- if (uirq->refcnt == 0) {
- /*
- * FIXME: This is not how locking works at all, you can't just
- * release the lock for a moment to do something that can't
- * sleep...
- */
- gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
+ gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
+
+ /* Request underlying irq if not already requested */
+ if (atomic_fetch_add(1, &uirq->refcnt) == 0) {
ret = request_irq(uirq->uirq, grgpio_irq_handler, 0,
dev_name(priv->dev), priv);
if (ret) {
dev_err(priv->dev,
"Could not request underlying irq %d\n",
uirq->uirq);
+ atomic_dec(&uirq->refcnt); /* rollback */
return ret;
}
- gpio_generic_chip_lock_irqsave(&priv->chip, flags);
}
- uirq->refcnt++;
-
- gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
/* Setup irq */
irq_set_chip_data(irq, priv);
@@ -306,8 +298,7 @@ static void grgpio_irq_unmap(struct irq_domain *d, unsigned int irq)
if (index >= 0) {
uirq = &priv->uirqs[lirq->index];
- uirq->refcnt--;
- if (uirq->refcnt == 0) {
+ if (atomic_dec_and_test(&uirq->refcnt)) {
gpio_generic_chip_unlock_irqrestore(&priv->chip, flags);
free_irq(uirq->uirq, priv);
return;
@@ -434,6 +425,7 @@ static int grgpio_probe(struct platform_device *ofdev)
continue;
}
priv->uirqs[lirq->index].uirq = ret;
+ atomic_set(&priv->uirqs[lirq->index].refcnt, 0);
}
}
diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c
index 2eaed83214d8..72935d6dbebf 100644
--- a/drivers/gpio/gpio-htc-egpio.c
+++ b/drivers/gpio/gpio-htc-egpio.c
@@ -364,21 +364,20 @@ static int __init egpio_probe(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int egpio_suspend(struct platform_device *pdev, pm_message_t state)
+static int egpio_suspend(struct device *dev)
{
- struct egpio_info *ei = platform_get_drvdata(pdev);
+ struct egpio_info *ei = dev_get_drvdata(dev);
- if (ei->chained_irq && device_may_wakeup(&pdev->dev))
+ if (ei->chained_irq && device_may_wakeup(dev))
enable_irq_wake(ei->chained_irq);
return 0;
}
-static int egpio_resume(struct platform_device *pdev)
+static int egpio_resume(struct device *dev)
{
- struct egpio_info *ei = platform_get_drvdata(pdev);
+ struct egpio_info *ei = dev_get_drvdata(dev);
- if (ei->chained_irq && device_may_wakeup(&pdev->dev))
+ if (ei->chained_irq && device_may_wakeup(dev))
disable_irq_wake(ei->chained_irq);
/* Update registers from the cache, in case
@@ -386,19 +385,15 @@ static int egpio_resume(struct platform_device *pdev)
egpio_write_cache(ei);
return 0;
}
-#else
-#define egpio_suspend NULL
-#define egpio_resume NULL
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(egpio_pm_ops, egpio_suspend, egpio_resume);
static struct platform_driver egpio_driver = {
.driver = {
.name = "htc-egpio",
.suppress_bind_attrs = true,
+ .pm = pm_sleep_ptr(&egpio_pm_ops),
},
- .suspend = egpio_suspend,
- .resume = egpio_resume,
};
static int __init egpio_init(void)
diff --git a/drivers/gpio/gpio-idio-16.c b/drivers/gpio/gpio-idio-16.c
index 0103be977c66..4fbae6f6a497 100644
--- a/drivers/gpio/gpio-idio-16.c
+++ b/drivers/gpio/gpio-idio-16.c
@@ -6,6 +6,7 @@
#define DEFAULT_SYMBOL_NAMESPACE "GPIO_IDIO_16"
+#include <linux/bitmap.h>
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -107,6 +108,7 @@ int devm_idio_16_regmap_register(struct device *const dev,
struct idio_16_data *data;
struct regmap_irq_chip *chip;
struct regmap_irq_chip_data *chip_data;
+ DECLARE_BITMAP(fixed_direction_output, IDIO_16_NGPIO);
if (!config->parent)
return -EINVAL;
@@ -164,6 +166,9 @@ int devm_idio_16_regmap_register(struct device *const dev,
gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
gpio_config.reg_mask_xlate = idio_16_reg_mask_xlate;
+ bitmap_from_u64(fixed_direction_output, GENMASK_U64(15, 0));
+ gpio_config.fixed_direction_output = fixed_direction_output;
+
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
}
EXPORT_SYMBOL_GPL(devm_idio_16_regmap_register);
diff --git a/drivers/gpio/gpio-latch.c b/drivers/gpio/gpio-latch.c
index c64aaa896766..452a9ce61488 100644
--- a/drivers/gpio/gpio-latch.c
+++ b/drivers/gpio/gpio-latch.c
@@ -48,8 +48,6 @@
#include <linux/property.h>
#include <linux/delay.h>
-#include "gpiolib.h"
-
struct gpio_latch_priv {
struct gpio_chip gc;
struct gpio_descs *clk_gpios;
diff --git a/drivers/gpio/gpio-ljca.c b/drivers/gpio/gpio-ljca.c
index 3b4f8830c741..f32d1d237795 100644
--- a/drivers/gpio/gpio-ljca.c
+++ b/drivers/gpio/gpio-ljca.c
@@ -286,22 +286,14 @@ static void ljca_gpio_event_cb(void *context, u8 cmd, const void *evt_data,
{
const struct ljca_gpio_packet *packet = evt_data;
struct ljca_gpio_dev *ljca_gpio = context;
- int i, irq;
+ int i;
if (cmd != LJCA_GPIO_INT_EVENT)
return;
for (i = 0; i < packet->num; i++) {
- irq = irq_find_mapping(ljca_gpio->gc.irq.domain,
- packet->item[i].index);
- if (!irq) {
- dev_err(ljca_gpio->gc.parent,
- "gpio_id %u does not mapped to IRQ yet\n",
- packet->item[i].index);
- return;
- }
-
- generic_handle_domain_irq(ljca_gpio->gc.irq.domain, irq);
+ generic_handle_domain_irq(ljca_gpio->gc.irq.domain,
+ packet->item[i].index);
set_bit(packet->item[i].index, ljca_gpio->reenable_irqs);
}
diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c
index 02f181cb219e..77d07e31366f 100644
--- a/drivers/gpio/gpio-loongson-64bit.c
+++ b/drivers/gpio/gpio-loongson-64bit.c
@@ -312,6 +312,7 @@ static int loongson_gpio_init(struct platform_device *pdev, struct loongson_gpio
lgpio->chip.gc.direction_output = loongson_gpio_direction_output;
lgpio->chip.gc.set = loongson_gpio_set;
lgpio->chip.gc.parent = &pdev->dev;
+ lgpio->chip.gc.base = -1;
spin_lock_init(&lgpio->lock);
}
@@ -407,11 +408,11 @@ static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data0 = {
static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data1 = {
.label = "ls2k2000_gpio",
- .mode = BIT_CTRL_MODE,
- .conf_offset = 0x0,
- .in_offset = 0x20,
- .out_offset = 0x10,
- .inten_offset = 0x30,
+ .mode = BYTE_CTRL_MODE,
+ .conf_offset = 0x800,
+ .in_offset = 0xa00,
+ .out_offset = 0x900,
+ .inten_offset = 0xb00,
};
static const struct loongson_gpio_chip_data loongson_gpio_ls2k2000_data2 = {
diff --git a/drivers/gpio/gpio-menz127.c b/drivers/gpio/gpio-menz127.c
index da2bf9381cc4..52b13c6ae496 100644
--- a/drivers/gpio/gpio-menz127.c
+++ b/drivers/gpio/gpio-menz127.c
@@ -24,6 +24,11 @@
#define MEN_Z127_ODER 0x1C
#define GPIO_TO_DBCNT_REG(gpio) ((gpio * 4) + 0x80)
+/* MEN Z127 supported model ids*/
+#define MEN_Z127_ID 0x7f
+#define MEN_Z034_ID 0x22
+#define MEN_Z037_ID 0x25
+
#define MEN_Z127_DB_MIN_US 50
/* 16 bit compare register. Each bit represents 50us */
#define MEN_Z127_DB_MAX_US (0xffff * MEN_Z127_DB_MIN_US)
@@ -140,6 +145,7 @@ static int men_z127_probe(struct mcb_device *mdev,
struct men_z127_gpio *men_z127_gpio;
struct device *dev = &mdev->dev;
int ret;
+ unsigned long sz;
men_z127_gpio = devm_kzalloc(dev, sizeof(struct men_z127_gpio),
GFP_KERNEL);
@@ -163,9 +169,21 @@ static int men_z127_probe(struct mcb_device *mdev,
mcb_set_drvdata(mdev, men_z127_gpio);
+ switch (mdev->id) {
+ case MEN_Z127_ID:
+ sz = 4;
+ break;
+ case MEN_Z034_ID:
+ case MEN_Z037_ID:
+ sz = 1;
+ break;
+ default:
+ return dev_err_probe(&mdev->dev, -EINVAL, "no size found for id %d", mdev->id);
+ }
+
config = (struct gpio_generic_chip_config) {
.dev = &mdev->dev,
- .sz = 4,
+ .sz = sz,
.dat = men_z127_gpio->reg_base + MEN_Z127_PSR,
.set = men_z127_gpio->reg_base + MEN_Z127_CTRL,
.dirout = men_z127_gpio->reg_base + MEN_Z127_GPIODR,
@@ -186,7 +204,9 @@ static int men_z127_probe(struct mcb_device *mdev,
}
static const struct mcb_device_id men_z127_ids[] = {
- { .device = 0x7f },
+ { .device = MEN_Z127_ID },
+ { .device = MEN_Z034_ID },
+ { .device = MEN_Z037_ID },
{ }
};
MODULE_DEVICE_TABLE(mcb, men_z127_ids);
@@ -201,7 +221,7 @@ static struct mcb_driver men_z127_driver = {
module_mcb_driver(men_z127_driver);
MODULE_AUTHOR("Andreas Werner <andreas.werner@men.de>");
-MODULE_DESCRIPTION("MEN 16z127 GPIO Controller");
+MODULE_DESCRIPTION("MEN GPIO Controller");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("mcb:16z127");
MODULE_IMPORT_NS("MCB");
diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c
index f6af81bf2b13..6576e5dcb0ee 100644
--- a/drivers/gpio/gpio-ml-ioh.c
+++ b/drivers/gpio/gpio-ml-ioh.c
@@ -160,7 +160,7 @@ static int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
/*
* Save register configuration and disable interrupts.
*/
-static void __maybe_unused ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
+static void ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
{
int i;
@@ -186,7 +186,7 @@ static void __maybe_unused ioh_gpio_save_reg_conf(struct ioh_gpio *chip)
/*
* This function restores the register configuration of the GPIO device.
*/
-static void __maybe_unused ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
+static void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip)
{
int i;
@@ -479,7 +479,7 @@ static int ioh_gpio_probe(struct pci_dev *pdev,
return 0;
}
-static int __maybe_unused ioh_gpio_suspend(struct device *dev)
+static int ioh_gpio_suspend(struct device *dev)
{
struct ioh_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
@@ -491,7 +491,7 @@ static int __maybe_unused ioh_gpio_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused ioh_gpio_resume(struct device *dev)
+static int ioh_gpio_resume(struct device *dev)
{
struct ioh_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
@@ -505,7 +505,7 @@ static int __maybe_unused ioh_gpio_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(ioh_gpio_pm_ops, ioh_gpio_suspend, ioh_gpio_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(ioh_gpio_pm_ops, ioh_gpio_suspend, ioh_gpio_resume);
static const struct pci_device_id ioh_gpio_pcidev_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) },
@@ -518,7 +518,7 @@ static struct pci_driver ioh_gpio_driver = {
.id_table = ioh_gpio_pcidev_id,
.probe = ioh_gpio_probe,
.driver = {
- .pm = &ioh_gpio_pm_ops,
+ .pm = pm_sleep_ptr(&ioh_gpio_pm_ops),
},
};
diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c
index abffce3894fc..6668686a28ff 100644
--- a/drivers/gpio/gpio-mlxbf2.c
+++ b/drivers/gpio/gpio-mlxbf2.c
@@ -424,7 +424,7 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev)
+static int mlxbf2_gpio_suspend(struct device *dev)
{
struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
@@ -436,7 +436,7 @@ static int __maybe_unused mlxbf2_gpio_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused mlxbf2_gpio_resume(struct device *dev)
+static int mlxbf2_gpio_resume(struct device *dev)
{
struct mlxbf2_gpio_context *gs = dev_get_drvdata(dev);
@@ -447,7 +447,7 @@ static int __maybe_unused mlxbf2_gpio_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(mlxbf2_pm_ops, mlxbf2_gpio_suspend, mlxbf2_gpio_resume);
static const struct acpi_device_id __maybe_unused mlxbf2_gpio_acpi_match[] = {
{ "MLNXBF22", 0 },
@@ -459,7 +459,7 @@ static struct platform_driver mlxbf2_gpio_driver = {
.driver = {
.name = "mlxbf2_gpio",
.acpi_match_table = mlxbf2_gpio_acpi_match,
- .pm = &mlxbf2_pm_ops,
+ .pm = pm_sleep_ptr(&mlxbf2_pm_ops),
},
.probe = mlxbf2_gpio_probe,
};
diff --git a/drivers/gpio/gpio-mm-lantiq.c b/drivers/gpio/gpio-mm-lantiq.c
index 8f1405733d98..1bd98c50a459 100644
--- a/drivers/gpio/gpio-mm-lantiq.c
+++ b/drivers/gpio/gpio-mm-lantiq.c
@@ -10,7 +10,6 @@
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/gpio/driver.h>
-#include <linux/gpio/legacy-of-mm-gpiochip.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -27,7 +26,8 @@
#define LTQ_EBU_WP 0x80000000 /* write protect bit */
struct ltq_mm {
- struct of_mm_gpio_chip mmchip;
+ struct gpio_chip gc;
+ void __iomem *regs;
u16 shadow; /* shadow the latches state */
};
@@ -44,7 +44,7 @@ static void ltq_mm_apply(struct ltq_mm *chip)
spin_lock_irqsave(&ebu_lock, flags);
ltq_ebu_w32(LTQ_EBU_BUSCON, LTQ_EBU_BUSCON1);
- __raw_writew(chip->shadow, chip->mmchip.regs);
+ __raw_writew(chip->shadow, chip->regs);
ltq_ebu_w32(LTQ_EBU_BUSCON | LTQ_EBU_WP, LTQ_EBU_BUSCON1);
spin_unlock_irqrestore(&ebu_lock, flags);
}
@@ -52,8 +52,8 @@ static void ltq_mm_apply(struct ltq_mm *chip)
/**
* ltq_mm_set() - gpio_chip->set - set gpios.
* @gc: Pointer to gpio_chip device structure.
- * @gpio: GPIO signal number.
- * @val: Value to be written to specified signal.
+ * @offset: GPIO signal number.
+ * @value: Value to be written to specified signal.
*
* Set the shadow value and call ltq_mm_apply. Always returns 0.
*/
@@ -73,8 +73,8 @@ static int ltq_mm_set(struct gpio_chip *gc, unsigned int offset, int value)
/**
* ltq_mm_dir_out() - gpio_chip->dir_out - set gpio direction.
* @gc: Pointer to gpio_chip device structure.
- * @gpio: GPIO signal number.
- * @val: Value to be written to specified signal.
+ * @offset: GPIO signal number.
+ * @value: Value to be written to specified signal.
*
* Same as ltq_mm_set, always returns 0.
*/
@@ -85,21 +85,21 @@ static int ltq_mm_dir_out(struct gpio_chip *gc, unsigned offset, int value)
/**
* ltq_mm_save_regs() - Set initial values of GPIO pins
- * @mm_gc: pointer to memory mapped GPIO chip structure
+ * @chip: Pointer to our private data structure.
*/
-static void ltq_mm_save_regs(struct of_mm_gpio_chip *mm_gc)
+static void ltq_mm_save_regs(struct ltq_mm *chip)
{
- struct ltq_mm *chip =
- container_of(mm_gc, struct ltq_mm, mmchip);
-
/* tell the ebu controller which memory address we will be using */
- ltq_ebu_w32(CPHYSADDR(chip->mmchip.regs) | 0x1, LTQ_EBU_ADDRSEL1);
+ ltq_ebu_w32(CPHYSADDR((__force void *)chip->regs) | 0x1, LTQ_EBU_ADDRSEL1);
ltq_mm_apply(chip);
}
static int ltq_mm_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct gpio_chip *gc;
struct ltq_mm *chip;
u32 shadow;
@@ -107,25 +107,29 @@ static int ltq_mm_probe(struct platform_device *pdev)
if (!chip)
return -ENOMEM;
- platform_set_drvdata(pdev, chip);
+ gc = &chip->gc;
+
+ gc->base = -1;
+ gc->ngpio = 16;
+ gc->direction_output = ltq_mm_dir_out;
+ gc->set = ltq_mm_set;
+ gc->parent = dev;
+ gc->owner = THIS_MODULE;
+ gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np);
+ if (!gc->label)
+ return -ENOMEM;
+
+ chip->regs = devm_of_iomap(dev, np, 0, NULL);
+ if (IS_ERR(chip->regs))
+ return PTR_ERR(chip->regs);
- chip->mmchip.gc.ngpio = 16;
- chip->mmchip.gc.direction_output = ltq_mm_dir_out;
- chip->mmchip.gc.set = ltq_mm_set;
- chip->mmchip.save_regs = ltq_mm_save_regs;
+ ltq_mm_save_regs(chip);
/* store the shadow value if one was passed by the devicetree */
if (!of_property_read_u32(pdev->dev.of_node, "lantiq,shadow", &shadow))
chip->shadow = shadow;
- return of_mm_gpiochip_add_data(pdev->dev.of_node, &chip->mmchip, chip);
-}
-
-static void ltq_mm_remove(struct platform_device *pdev)
-{
- struct ltq_mm *chip = platform_get_drvdata(pdev);
-
- of_mm_gpiochip_remove(&chip->mmchip);
+ return devm_gpiochip_add_data(dev, gc, chip);
}
static const struct of_device_id ltq_mm_match[] = {
@@ -136,7 +140,6 @@ MODULE_DEVICE_TABLE(of, ltq_mm_match);
static struct platform_driver ltq_mm_driver = {
.probe = ltq_mm_probe,
- .remove = ltq_mm_remove,
.driver = {
.name = "gpio-mm-ltq",
.of_match_table = ltq_mm_match,
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index 7d6dd36cf1ae..b3a26a06260b 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -41,6 +41,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
*/
#include <linux/bitops.h>
+#include <linux/cleanup.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/init.h>
@@ -61,69 +62,69 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include "gpiolib.h"
-static void bgpio_write8(void __iomem *reg, unsigned long data)
+static void gpio_mmio_write8(void __iomem *reg, unsigned long data)
{
writeb(data, reg);
}
-static unsigned long bgpio_read8(void __iomem *reg)
+static unsigned long gpio_mmio_read8(void __iomem *reg)
{
return readb(reg);
}
-static void bgpio_write16(void __iomem *reg, unsigned long data)
+static void gpio_mmio_write16(void __iomem *reg, unsigned long data)
{
writew(data, reg);
}
-static unsigned long bgpio_read16(void __iomem *reg)
+static unsigned long gpio_mmio_read16(void __iomem *reg)
{
return readw(reg);
}
-static void bgpio_write32(void __iomem *reg, unsigned long data)
+static void gpio_mmio_write32(void __iomem *reg, unsigned long data)
{
writel(data, reg);
}
-static unsigned long bgpio_read32(void __iomem *reg)
+static unsigned long gpio_mmio_read32(void __iomem *reg)
{
return readl(reg);
}
#if BITS_PER_LONG >= 64
-static void bgpio_write64(void __iomem *reg, unsigned long data)
+static void gpio_mmio_write64(void __iomem *reg, unsigned long data)
{
writeq(data, reg);
}
-static unsigned long bgpio_read64(void __iomem *reg)
+static unsigned long gpio_mmio_read64(void __iomem *reg)
{
return readq(reg);
}
#endif /* BITS_PER_LONG >= 64 */
-static void bgpio_write16be(void __iomem *reg, unsigned long data)
+static void gpio_mmio_write16be(void __iomem *reg, unsigned long data)
{
iowrite16be(data, reg);
}
-static unsigned long bgpio_read16be(void __iomem *reg)
+static unsigned long gpio_mmio_read16be(void __iomem *reg)
{
return ioread16be(reg);
}
-static void bgpio_write32be(void __iomem *reg, unsigned long data)
+static void gpio_mmio_write32be(void __iomem *reg, unsigned long data)
{
iowrite32be(data, reg);
}
-static unsigned long bgpio_read32be(void __iomem *reg)
+static unsigned long gpio_mmio_read32be(void __iomem *reg)
{
return ioread32be(reg);
}
-static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line)
+static unsigned long gpio_mmio_line2mask(struct gpio_chip *gc, unsigned int line)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@@ -132,10 +133,10 @@ static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line)
return BIT(line);
}
-static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
+static int gpio_mmio_get_set(struct gpio_chip *gc, unsigned int gpio)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- unsigned long pinmask = bgpio_line2mask(gc, gpio);
+ unsigned long pinmask = gpio_mmio_line2mask(gc, gpio);
bool dir = !!(chip->sdir & pinmask);
if (dir)
@@ -148,8 +149,8 @@ static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio)
* This assumes that the bits in the GPIO register are in native endianness.
* We only assign the function pointer if we have that.
*/
-static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask,
- unsigned long *bits)
+static int gpio_mmio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long get_mask = 0, set_mask = 0;
@@ -168,18 +169,18 @@ static int bgpio_get_set_multiple(struct gpio_chip *gc, unsigned long *mask,
return 0;
}
-static int bgpio_get(struct gpio_chip *gc, unsigned int gpio)
+static int gpio_mmio_get(struct gpio_chip *gc, unsigned int gpio)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- return !!(chip->read_reg(chip->reg_dat) & bgpio_line2mask(gc, gpio));
+ return !!(chip->read_reg(chip->reg_dat) & gpio_mmio_line2mask(gc, gpio));
}
/*
* This only works if the bits in the GPIO register are in native endianness.
*/
-static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
- unsigned long *bits)
+static int gpio_mmio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@@ -192,8 +193,8 @@ static int bgpio_get_multiple(struct gpio_chip *gc, unsigned long *mask,
/*
* With big endian mirrored bit order it becomes more tedious.
*/
-static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
- unsigned long *bits)
+static int gpio_mmio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long readmask = 0;
@@ -205,7 +206,7 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
/* Create a mirrored mask */
for_each_set_bit(bit, mask, gc->ngpio)
- readmask |= bgpio_line2mask(gc, bit);
+ readmask |= gpio_mmio_line2mask(gc, bit);
/* Read the register */
val = chip->read_reg(chip->reg_dat) & readmask;
@@ -215,23 +216,22 @@ static int bgpio_get_multiple_be(struct gpio_chip *gc, unsigned long *mask,
* in bit 0 ... line 31 in bit 31 for a 32bit register.
*/
for_each_set_bit(bit, &val, gc->ngpio)
- *bits |= bgpio_line2mask(gc, bit);
+ *bits |= gpio_mmio_line2mask(gc, bit);
return 0;
}
-static int bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val)
+static int gpio_mmio_set_none(struct gpio_chip *gc, unsigned int gpio, int val)
{
return 0;
}
-static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
+static int gpio_mmio_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- unsigned long mask = bgpio_line2mask(gc, gpio);
- unsigned long flags;
+ unsigned long mask = gpio_mmio_line2mask(gc, gpio);
- raw_spin_lock_irqsave(&chip->lock, flags);
+ guard(raw_spinlock)(&chip->lock);
if (val)
chip->sdata |= mask;
@@ -240,16 +240,14 @@ static int bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
chip->write_reg(chip->reg_dat, chip->sdata);
- raw_spin_unlock_irqrestore(&chip->lock, flags);
-
return 0;
}
-static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
- int val)
+static int gpio_mmio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
+ int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- unsigned long mask = bgpio_line2mask(gc, gpio);
+ unsigned long mask = gpio_mmio_line2mask(gc, gpio);
if (val)
chip->write_reg(chip->reg_set, mask);
@@ -259,12 +257,12 @@ static int bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
return 0;
}
-static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
+static int gpio_mmio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- unsigned long mask = bgpio_line2mask(gc, gpio), flags;
+ unsigned long mask = gpio_mmio_line2mask(gc, gpio);
- raw_spin_lock_irqsave(&chip->lock, flags);
+ guard(raw_spinlock)(&chip->lock);
if (val)
chip->sdata |= mask;
@@ -273,15 +271,14 @@ static int bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
chip->write_reg(chip->reg_set, chip->sdata);
- raw_spin_unlock_irqrestore(&chip->lock, flags);
-
return 0;
}
-static void bgpio_multiple_get_masks(struct gpio_chip *gc,
- unsigned long *mask, unsigned long *bits,
- unsigned long *set_mask,
- unsigned long *clear_mask)
+static void gpio_mmio_multiple_get_masks(struct gpio_chip *gc,
+ unsigned long *mask,
+ unsigned long *bits,
+ unsigned long *set_mask,
+ unsigned long *clear_mask)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
int i;
@@ -291,60 +288,58 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc,
for_each_set_bit(i, mask, chip->bits) {
if (test_bit(i, bits))
- *set_mask |= bgpio_line2mask(gc, i);
+ *set_mask |= gpio_mmio_line2mask(gc, i);
else
- *clear_mask |= bgpio_line2mask(gc, i);
+ *clear_mask |= gpio_mmio_line2mask(gc, i);
}
}
-static void bgpio_set_multiple_single_reg(struct gpio_chip *gc,
- unsigned long *mask,
- unsigned long *bits,
- void __iomem *reg)
+static void gpio_mmio_set_multiple_single_reg(struct gpio_chip *gc,
+ unsigned long *mask,
+ unsigned long *bits,
+ void __iomem *reg)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- unsigned long flags, set_mask, clear_mask;
+ unsigned long set_mask, clear_mask;
- raw_spin_lock_irqsave(&chip->lock, flags);
+ guard(raw_spinlock)(&chip->lock);
- bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
+ gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
chip->sdata |= set_mask;
chip->sdata &= ~clear_mask;
chip->write_reg(reg, chip->sdata);
-
- raw_spin_unlock_irqrestore(&chip->lock, flags);
}
-static int bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
- unsigned long *bits)
+static int gpio_mmio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_dat);
+ gpio_mmio_set_multiple_single_reg(gc, mask, bits, chip->reg_dat);
return 0;
}
-static int bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
- unsigned long *bits)
+static int gpio_mmio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
+ unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- bgpio_set_multiple_single_reg(gc, mask, bits, chip->reg_set);
+ gpio_mmio_set_multiple_single_reg(gc, mask, bits, chip->reg_set);
return 0;
}
-static int bgpio_set_multiple_with_clear(struct gpio_chip *gc,
- unsigned long *mask,
- unsigned long *bits)
+static int gpio_mmio_set_multiple_with_clear(struct gpio_chip *gc,
+ unsigned long *mask,
+ unsigned long *bits)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
unsigned long set_mask, clear_mask;
- bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
+ gpio_mmio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
if (set_mask)
chip->write_reg(chip->reg_set, set_mask);
@@ -354,7 +349,8 @@ static int bgpio_set_multiple_with_clear(struct gpio_chip *gc,
return 0;
}
-static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_out)
+static int gpio_mmio_dir_return(struct gpio_chip *gc, unsigned int gpio,
+ bool dir_out)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@@ -367,131 +363,125 @@ static int bgpio_dir_return(struct gpio_chip *gc, unsigned int gpio, bool dir_ou
return pinctrl_gpio_direction_input(gc, gpio);
}
-static int bgpio_dir_in_err(struct gpio_chip *gc, unsigned int gpio)
+static int gpio_mmio_dir_in_err(struct gpio_chip *gc, unsigned int gpio)
{
return -EINVAL;
}
-static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
+static int gpio_mmio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
- return bgpio_dir_return(gc, gpio, false);
+ return gpio_mmio_dir_return(gc, gpio, false);
}
-static int bgpio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
- int val)
+static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
+ int val)
{
return -EINVAL;
}
-static int bgpio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
- int val)
+static int gpio_mmio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
+ int val)
{
gc->set(gc, gpio, val);
- return bgpio_dir_return(gc, gpio, true);
+ return gpio_mmio_dir_return(gc, gpio, true);
}
-static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
+static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- unsigned long flags;
- raw_spin_lock_irqsave(&chip->lock, flags);
+ scoped_guard(raw_spinlock, &chip->lock) {
+ chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
- chip->sdir &= ~bgpio_line2mask(gc, gpio);
-
- if (chip->reg_dir_in)
- chip->write_reg(chip->reg_dir_in, ~chip->sdir);
- if (chip->reg_dir_out)
- chip->write_reg(chip->reg_dir_out, chip->sdir);
-
- raw_spin_unlock_irqrestore(&chip->lock, flags);
+ if (chip->reg_dir_in)
+ chip->write_reg(chip->reg_dir_in, ~chip->sdir);
+ if (chip->reg_dir_out)
+ chip->write_reg(chip->reg_dir_out, chip->sdir);
+ }
- return bgpio_dir_return(gc, gpio, false);
+ return gpio_mmio_dir_return(gc, gpio, false);
}
-static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
+static int gpio_mmio_get_dir(struct gpio_chip *gc, unsigned int gpio)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
/* Return 0 if output, 1 if input */
if (chip->dir_unreadable) {
- if (chip->sdir & bgpio_line2mask(gc, gpio))
+ if (chip->sdir & gpio_mmio_line2mask(gc, gpio))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
if (chip->reg_dir_out) {
- if (chip->read_reg(chip->reg_dir_out) & bgpio_line2mask(gc, gpio))
+ if (chip->read_reg(chip->reg_dir_out) & gpio_mmio_line2mask(gc, gpio))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
if (chip->reg_dir_in)
- if (!(chip->read_reg(chip->reg_dir_in) & bgpio_line2mask(gc, gpio)))
+ if (!(chip->read_reg(chip->reg_dir_in) & gpio_mmio_line2mask(gc, gpio)))
return GPIO_LINE_DIRECTION_OUT;
return GPIO_LINE_DIRECTION_IN;
}
-static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
+static void gpio_mmio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
- unsigned long flags;
- raw_spin_lock_irqsave(&chip->lock, flags);
+ guard(raw_spinlock)(&chip->lock);
- chip->sdir |= bgpio_line2mask(gc, gpio);
+ chip->sdir |= gpio_mmio_line2mask(gc, gpio);
if (chip->reg_dir_in)
chip->write_reg(chip->reg_dir_in, ~chip->sdir);
if (chip->reg_dir_out)
chip->write_reg(chip->reg_dir_out, chip->sdir);
-
- raw_spin_unlock_irqrestore(&chip->lock, flags);
}
-static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
- int val)
+static int gpio_mmio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
+ int val)
{
- bgpio_dir_out(gc, gpio, val);
+ gpio_mmio_dir_out(gc, gpio, val);
gc->set(gc, gpio, val);
- return bgpio_dir_return(gc, gpio, true);
+ return gpio_mmio_dir_return(gc, gpio, true);
}
-static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
- int val)
+static int gpio_mmio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
+ int val)
{
gc->set(gc, gpio, val);
- bgpio_dir_out(gc, gpio, val);
- return bgpio_dir_return(gc, gpio, true);
+ gpio_mmio_dir_out(gc, gpio, val);
+ return gpio_mmio_dir_return(gc, gpio, true);
}
-static int bgpio_setup_accessors(struct device *dev,
- struct gpio_generic_chip *chip,
- bool byte_be)
+static int gpio_mmio_setup_accessors(struct device *dev,
+ struct gpio_generic_chip *chip,
+ bool byte_be)
{
switch (chip->bits) {
case 8:
- chip->read_reg = bgpio_read8;
- chip->write_reg = bgpio_write8;
+ chip->read_reg = gpio_mmio_read8;
+ chip->write_reg = gpio_mmio_write8;
break;
case 16:
if (byte_be) {
- chip->read_reg = bgpio_read16be;
- chip->write_reg = bgpio_write16be;
+ chip->read_reg = gpio_mmio_read16be;
+ chip->write_reg = gpio_mmio_write16be;
} else {
- chip->read_reg = bgpio_read16;
- chip->write_reg = bgpio_write16;
+ chip->read_reg = gpio_mmio_read16;
+ chip->write_reg = gpio_mmio_write16;
}
break;
case 32:
if (byte_be) {
- chip->read_reg = bgpio_read32be;
- chip->write_reg = bgpio_write32be;
+ chip->read_reg = gpio_mmio_read32be;
+ chip->write_reg = gpio_mmio_write32be;
} else {
- chip->read_reg = bgpio_read32;
- chip->write_reg = bgpio_write32;
+ chip->read_reg = gpio_mmio_read32;
+ chip->write_reg = gpio_mmio_write32;
}
break;
#if BITS_PER_LONG >= 64
@@ -501,8 +491,8 @@ static int bgpio_setup_accessors(struct device *dev,
"64 bit big endian byte order unsupported\n");
return -EINVAL;
} else {
- chip->read_reg = bgpio_read64;
- chip->write_reg = bgpio_write64;
+ chip->read_reg = gpio_mmio_read64;
+ chip->write_reg = gpio_mmio_write64;
}
break;
#endif /* BITS_PER_LONG >= 64 */
@@ -536,8 +526,8 @@ static int bgpio_setup_accessors(struct device *dev,
* - an input direction register (named "dirin") where a 1 bit indicates
* the GPIO is an input.
*/
-static int bgpio_setup_io(struct gpio_generic_chip *chip,
- const struct gpio_generic_chip_config *cfg)
+static int gpio_mmio_setup_io(struct gpio_generic_chip *chip,
+ const struct gpio_generic_chip_config *cfg)
{
struct gpio_chip *gc = &chip->gc;
@@ -548,25 +538,25 @@ static int bgpio_setup_io(struct gpio_generic_chip *chip,
if (cfg->set && cfg->clr) {
chip->reg_set = cfg->set;
chip->reg_clr = cfg->clr;
- gc->set = bgpio_set_with_clear;
- gc->set_multiple = bgpio_set_multiple_with_clear;
+ gc->set = gpio_mmio_set_with_clear;
+ gc->set_multiple = gpio_mmio_set_multiple_with_clear;
} else if (cfg->set && !cfg->clr) {
chip->reg_set = cfg->set;
- gc->set = bgpio_set_set;
- gc->set_multiple = bgpio_set_multiple_set;
+ gc->set = gpio_mmio_set_set;
+ gc->set_multiple = gpio_mmio_set_multiple_set;
} else if (cfg->flags & GPIO_GENERIC_NO_OUTPUT) {
- gc->set = bgpio_set_none;
+ gc->set = gpio_mmio_set_none;
gc->set_multiple = NULL;
} else {
- gc->set = bgpio_set;
- gc->set_multiple = bgpio_set_multiple;
+ gc->set = gpio_mmio_set;
+ gc->set_multiple = gpio_mmio_set_multiple;
}
if (!(cfg->flags & GPIO_GENERIC_UNREADABLE_REG_SET) &&
(cfg->flags & GPIO_GENERIC_READ_OUTPUT_REG_SET)) {
- gc->get = bgpio_get_set;
+ gc->get = gpio_mmio_get_set;
if (!chip->be_bits)
- gc->get_multiple = bgpio_get_set_multiple;
+ gc->get_multiple = gpio_mmio_get_set_multiple;
/*
* We deliberately avoid assigning the ->get_multiple() call
* for big endian mirrored registers which are ALSO reflecting
@@ -575,18 +565,18 @@ static int bgpio_setup_io(struct gpio_generic_chip *chip,
* reading each line individually in that fringe case.
*/
} else {
- gc->get = bgpio_get;
+ gc->get = gpio_mmio_get;
if (chip->be_bits)
- gc->get_multiple = bgpio_get_multiple_be;
+ gc->get_multiple = gpio_mmio_get_multiple_be;
else
- gc->get_multiple = bgpio_get_multiple;
+ gc->get_multiple = gpio_mmio_get_multiple;
}
return 0;
}
-static int bgpio_setup_direction(struct gpio_generic_chip *chip,
- const struct gpio_generic_chip_config *cfg)
+static int gpio_mmio_setup_direction(struct gpio_generic_chip *chip,
+ const struct gpio_generic_chip_config *cfg)
{
struct gpio_chip *gc = &chip->gc;
@@ -594,27 +584,27 @@ static int bgpio_setup_direction(struct gpio_generic_chip *chip,
chip->reg_dir_out = cfg->dirout;
chip->reg_dir_in = cfg->dirin;
if (cfg->flags & GPIO_GENERIC_NO_SET_ON_INPUT)
- gc->direction_output = bgpio_dir_out_dir_first;
+ gc->direction_output = gpio_mmio_dir_out_dir_first;
else
- gc->direction_output = bgpio_dir_out_val_first;
- gc->direction_input = bgpio_dir_in;
- gc->get_direction = bgpio_get_dir;
+ gc->direction_output = gpio_mmio_dir_out_val_first;
+ gc->direction_input = gpio_mmio_dir_in;
+ gc->get_direction = gpio_mmio_get_dir;
} else {
if (cfg->flags & GPIO_GENERIC_NO_OUTPUT)
- gc->direction_output = bgpio_dir_out_err;
+ gc->direction_output = gpio_mmio_dir_out_err;
else
- gc->direction_output = bgpio_simple_dir_out;
+ gc->direction_output = gpio_mmio_simple_dir_out;
if (cfg->flags & GPIO_GENERIC_NO_INPUT)
- gc->direction_input = bgpio_dir_in_err;
+ gc->direction_input = gpio_mmio_dir_in_err;
else
- gc->direction_input = bgpio_simple_dir_in;
+ gc->direction_input = gpio_mmio_simple_dir_in;
}
return 0;
}
-static int bgpio_request(struct gpio_chip *gc, unsigned int gpio_pin)
+static int gpio_mmio_request(struct gpio_chip *gc, unsigned int gpio_pin)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
@@ -653,23 +643,23 @@ int gpio_generic_chip_init(struct gpio_generic_chip *chip,
gc->parent = dev;
gc->label = dev_name(dev);
gc->base = -1;
- gc->request = bgpio_request;
+ gc->request = gpio_mmio_request;
chip->be_bits = !!(flags & GPIO_GENERIC_BIG_ENDIAN);
ret = gpiochip_get_ngpios(gc, dev);
if (ret)
gc->ngpio = chip->bits;
- ret = bgpio_setup_io(chip, cfg);
+ ret = gpio_mmio_setup_io(chip, cfg);
if (ret)
return ret;
- ret = bgpio_setup_accessors(dev, chip,
+ ret = gpio_mmio_setup_accessors(dev, chip,
flags & GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER);
if (ret)
return ret;
- ret = bgpio_setup_direction(chip, cfg);
+ ret = gpio_mmio_setup_direction(chip, cfg);
if (ret)
return ret;
@@ -680,7 +670,7 @@ int gpio_generic_chip_init(struct gpio_generic_chip *chip,
}
chip->sdata = chip->read_reg(chip->reg_dat);
- if (gc->set == bgpio_set_set &&
+ if (gc->set == gpio_mmio_set_set &&
!(flags & GPIO_GENERIC_UNREADABLE_REG_SET))
chip->sdata = chip->read_reg(chip->reg_set);
@@ -712,9 +702,8 @@ EXPORT_SYMBOL_GPL(gpio_generic_chip_init);
#if IS_ENABLED(CONFIG_GPIO_GENERIC_PLATFORM)
-static void __iomem *bgpio_map(struct platform_device *pdev,
- const char *name,
- resource_size_t sane_sz)
+static void __iomem *gpio_mmio_map(struct platform_device *pdev,
+ const char *name, resource_size_t sane_sz)
{
struct resource *r;
resource_size_t sz;
@@ -730,16 +719,16 @@ static void __iomem *bgpio_map(struct platform_device *pdev,
return devm_ioremap_resource(&pdev->dev, r);
}
-static const struct of_device_id bgpio_of_match[] = {
+static const struct of_device_id gpio_mmio_of_match[] = {
{ .compatible = "brcm,bcm6345-gpio" },
{ .compatible = "wd,mbl-gpio" },
{ .compatible = "ni,169445-nand-gpio" },
{ .compatible = "intel,ixp4xx-expansion-bus-mmio-gpio" },
{ }
};
-MODULE_DEVICE_TABLE(of, bgpio_of_match);
+MODULE_DEVICE_TABLE(of, gpio_mmio_of_match);
-static int bgpio_pdev_probe(struct platform_device *pdev)
+static int gpio_mmio_pdev_probe(struct platform_device *pdev)
{
struct gpio_generic_chip_config config;
struct gpio_generic_chip *gen_gc;
@@ -762,23 +751,23 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
sz = resource_size(r);
- dat = bgpio_map(pdev, "dat", sz);
+ dat = gpio_mmio_map(pdev, "dat", sz);
if (IS_ERR(dat))
return PTR_ERR(dat);
- set = bgpio_map(pdev, "set", sz);
+ set = gpio_mmio_map(pdev, "set", sz);
if (IS_ERR(set))
return PTR_ERR(set);
- clr = bgpio_map(pdev, "clr", sz);
+ clr = gpio_mmio_map(pdev, "clr", sz);
if (IS_ERR(clr))
return PTR_ERR(clr);
- dirout = bgpio_map(pdev, "dirout", sz);
+ dirout = gpio_mmio_map(pdev, "dirout", sz);
if (IS_ERR(dirout))
return PTR_ERR(dirout);
- dirin = bgpio_map(pdev, "dirin", sz);
+ dirin = gpio_mmio_map(pdev, "dirin", sz);
if (IS_ERR(dirin))
return PTR_ERR(dirin);
@@ -824,25 +813,25 @@ static int bgpio_pdev_probe(struct platform_device *pdev)
return devm_gpiochip_add_data(&pdev->dev, &gen_gc->gc, NULL);
}
-static const struct platform_device_id bgpio_id_table[] = {
+static const struct platform_device_id gpio_mmio_id_table[] = {
{
.name = "basic-mmio-gpio",
.driver_data = 0,
},
{ }
};
-MODULE_DEVICE_TABLE(platform, bgpio_id_table);
+MODULE_DEVICE_TABLE(platform, gpio_mmio_id_table);
-static struct platform_driver bgpio_driver = {
+static struct platform_driver gpio_mmio_driver = {
.driver = {
.name = "basic-mmio-gpio",
- .of_match_table = bgpio_of_match,
+ .of_match_table = gpio_mmio_of_match,
},
- .id_table = bgpio_id_table,
- .probe = bgpio_pdev_probe,
+ .id_table = gpio_mmio_id_table,
+ .probe = gpio_mmio_pdev_probe,
};
-module_platform_driver(bgpio_driver);
+module_platform_driver(gpio_mmio_driver);
#endif /* CONFIG_GPIO_GENERIC_PLATFORM */
diff --git a/drivers/gpio/gpio-mpsse.c b/drivers/gpio/gpio-mpsse.c
index 9f42bb30b4ec..ace652ba4df1 100644
--- a/drivers/gpio/gpio-mpsse.c
+++ b/drivers/gpio/gpio-mpsse.c
@@ -10,6 +10,7 @@
#include <linux/cleanup.h>
#include <linux/gpio/driver.h>
#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/usb.h>
struct mpsse_priv {
@@ -17,8 +18,10 @@ struct mpsse_priv {
struct usb_device *udev; /* USB device encompassing all MPSSEs */
struct usb_interface *intf; /* USB interface for this MPSSE */
u8 intf_id; /* USB interface number for this MPSSE */
- struct work_struct irq_work; /* polling work thread */
+ struct list_head workers; /* polling work threads */
struct mutex irq_mutex; /* lock over irq_data */
+ struct mutex irq_race; /* race for polling worker teardown */
+ raw_spinlock_t irq_spin; /* protects worker list */
atomic_t irq_type[16]; /* pin -> edge detection type */
atomic_t irq_enabled;
int id;
@@ -26,6 +29,9 @@ struct mpsse_priv {
u8 gpio_outputs[2]; /* Output states for GPIOs [L, H] */
u8 gpio_dir[2]; /* Directions for GPIOs [L, H] */
+ unsigned long dir_in; /* Bitmask of valid input pins */
+ unsigned long dir_out; /* Bitmask of valid output pins */
+
u8 *bulk_in_buf; /* Extra recv buffer to grab status bytes */
struct usb_endpoint_descriptor *bulk_in;
@@ -34,6 +40,14 @@ struct mpsse_priv {
struct mutex io_mutex; /* sync I/O with disconnect */
};
+struct mpsse_worker {
+ struct mpsse_priv *priv;
+ struct work_struct work;
+ atomic_t cancelled;
+ struct list_head list; /* linked list */
+ struct list_head destroy; /* teardown linked list */
+};
+
struct bulk_desc {
bool tx; /* direction of bulk transfer */
u8 *data; /* input (tx) or output (rx) */
@@ -43,8 +57,27 @@ struct bulk_desc {
int timeout;
};
+#define MPSSE_NGPIO 16
+
+struct mpsse_quirk {
+ const char *names[MPSSE_NGPIO]; /* Pin names, if applicable */
+ unsigned long dir_in; /* Bitmask of valid input pins */
+ unsigned long dir_out; /* Bitmask of valid output pins */
+};
+
+static struct mpsse_quirk bryx_brik_quirk = {
+ .names = {
+ [3] = "Push to Talk",
+ [5] = "Channel Activity",
+ },
+ .dir_out = BIT(3), /* Push to Talk */
+ .dir_in = BIT(5), /* Channel Activity */
+};
+
static const struct usb_device_id gpio_mpsse_table[] = {
{ USB_DEVICE(0x0c52, 0xa064) }, /* SeaLevel Systems, Inc. */
+ { USB_DEVICE(0x0403, 0x6988), /* FTDI, assigned to Bryx */
+ .driver_info = (kernel_ulong_t)&bryx_brik_quirk},
{ } /* Terminating entry */
};
@@ -160,6 +193,32 @@ static int gpio_mpsse_get_bank(struct mpsse_priv *priv, u8 bank)
return buf;
}
+static int mpsse_ensure_supported(struct gpio_chip *chip,
+ unsigned long mask, int direction)
+{
+ unsigned long supported, unsupported;
+ char *type = "input";
+ struct mpsse_priv *priv = gpiochip_get_data(chip);
+
+ supported = priv->dir_in;
+ if (direction == GPIO_LINE_DIRECTION_OUT) {
+ supported = priv->dir_out;
+ type = "output";
+ }
+
+ /* An invalid bit was in the provided mask */
+ unsupported = mask & ~supported;
+ if (unsupported) {
+ dev_err(&priv->udev->dev,
+ "mpsse: GPIO %lu doesn't support %s\n",
+ find_first_bit(&unsupported, sizeof(unsupported) * 8),
+ type);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
@@ -167,6 +226,10 @@ static int gpio_mpsse_set_multiple(struct gpio_chip *chip, unsigned long *mask,
int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip);
+ ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_OUT);
+ if (ret)
+ return ret;
+
guard(mutex)(&priv->io_mutex);
for_each_set_clump8(i, bank_mask, mask, chip->ngpio) {
bank = i / 8;
@@ -194,6 +257,10 @@ static int gpio_mpsse_get_multiple(struct gpio_chip *chip, unsigned long *mask,
int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip);
+ ret = mpsse_ensure_supported(chip, *mask, GPIO_LINE_DIRECTION_IN);
+ if (ret)
+ return ret;
+
guard(mutex)(&priv->io_mutex);
for_each_set_clump8(i, bank_mask, mask, chip->ngpio) {
bank = i / 8;
@@ -242,10 +309,15 @@ static int gpio_mpsse_gpio_set(struct gpio_chip *chip, unsigned int offset,
static int gpio_mpsse_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
+ int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip);
int bank = (offset & 8) >> 3;
int bank_offset = offset & 7;
+ ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_OUT);
+ if (ret)
+ return ret;
+
scoped_guard(mutex, &priv->io_mutex)
priv->gpio_dir[bank] |= BIT(bank_offset);
@@ -255,15 +327,19 @@ static int gpio_mpsse_direction_output(struct gpio_chip *chip,
static int gpio_mpsse_direction_input(struct gpio_chip *chip,
unsigned int offset)
{
+ int ret;
struct mpsse_priv *priv = gpiochip_get_data(chip);
int bank = (offset & 8) >> 3;
int bank_offset = offset & 7;
+ ret = mpsse_ensure_supported(chip, BIT(offset), GPIO_LINE_DIRECTION_IN);
+ if (ret)
+ return ret;
+
guard(mutex)(&priv->io_mutex);
priv->gpio_dir[bank] &= ~BIT(bank_offset);
- gpio_mpsse_set_bank(priv, bank);
- return 0;
+ return gpio_mpsse_set_bank(priv, bank);
}
static int gpio_mpsse_get_direction(struct gpio_chip *chip,
@@ -284,18 +360,62 @@ static int gpio_mpsse_get_direction(struct gpio_chip *chip,
return ret;
}
-static void gpio_mpsse_poll(struct work_struct *work)
+/*
+ * Stops all workers except `my_worker`.
+ * Safe to call only when `irq_race` is held.
+ */
+static void gpio_mpsse_stop_all_except(struct mpsse_priv *priv,
+ struct mpsse_worker *my_worker)
+{
+ struct mpsse_worker *worker, *worker_tmp;
+ struct list_head destructors = LIST_HEAD_INIT(destructors);
+
+ scoped_guard(raw_spinlock_irqsave, &priv->irq_spin) {
+ list_for_each_entry_safe(worker, worker_tmp,
+ &priv->workers, list) {
+ /* Don't stop ourselves */
+ if (worker == my_worker)
+ continue;
+
+ list_del(&worker->list);
+
+ /* Give worker a chance to terminate itself */
+ atomic_set(&worker->cancelled, 1);
+ /* Keep track of stuff to cancel */
+ INIT_LIST_HEAD(&worker->destroy);
+ list_add(&worker->destroy, &destructors);
+ }
+ }
+
+ list_for_each_entry_safe(worker, worker_tmp,
+ &destructors, destroy) {
+ list_del(&worker->destroy);
+ cancel_work_sync(&worker->work);
+ kfree(worker);
+ }
+}
+
+static void gpio_mpsse_poll(struct work_struct *my_work)
{
unsigned long pin_mask, pin_states, flags;
int irq_enabled, offset, err, value, fire_irq,
irq, old_value[16], irq_type[16];
- struct mpsse_priv *priv = container_of(work, struct mpsse_priv,
- irq_work);
+ struct mpsse_worker *my_worker = container_of(my_work, struct mpsse_worker, work);
+ struct mpsse_priv *priv = my_worker->priv;
for (offset = 0; offset < priv->gpio.ngpio; ++offset)
old_value[offset] = -1;
- while ((irq_enabled = atomic_read(&priv->irq_enabled))) {
+ /*
+ * We only want one worker. Workers race to acquire irq_race and tear
+ * down all other workers. This is a cond guard so that we don't deadlock
+ * trying to cancel a worker.
+ */
+ scoped_cond_guard(mutex_try, return, &priv->irq_race)
+ gpio_mpsse_stop_all_except(priv, my_worker);
+
+ while ((irq_enabled = atomic_read(&priv->irq_enabled)) &&
+ !atomic_read(&my_worker->cancelled)) {
usleep_range(MPSSE_POLL_INTERVAL, MPSSE_POLL_INTERVAL + 1000);
/* Cleanup will trigger at the end of the loop */
guard(mutex)(&priv->irq_mutex);
@@ -370,21 +490,45 @@ static int gpio_mpsse_set_irq_type(struct irq_data *irqd, unsigned int type)
static void gpio_mpsse_irq_disable(struct irq_data *irqd)
{
+ struct mpsse_worker *worker;
struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd);
atomic_and(~BIT(irqd->hwirq), &priv->irq_enabled);
gpiochip_disable_irq(&priv->gpio, irqd->hwirq);
+
+ /*
+ * Can't actually do teardown in IRQ context (it blocks).
+ * As a result, these workers will stick around until irq is reenabled
+ * or device gets disconnected
+ */
+ scoped_guard(raw_spinlock_irqsave, &priv->irq_spin)
+ list_for_each_entry(worker, &priv->workers, list)
+ atomic_set(&worker->cancelled, 1);
}
static void gpio_mpsse_irq_enable(struct irq_data *irqd)
{
+ struct mpsse_worker *worker;
struct mpsse_priv *priv = irq_data_get_irq_chip_data(irqd);
gpiochip_enable_irq(&priv->gpio, irqd->hwirq);
/* If no-one else was using the IRQ, enable it */
if (!atomic_fetch_or(BIT(irqd->hwirq), &priv->irq_enabled)) {
- INIT_WORK(&priv->irq_work, gpio_mpsse_poll);
- schedule_work(&priv->irq_work);
+ /*
+ * Can't be devm because it uses a non-raw spinlock (illegal in
+ * this context, where a raw spinlock is held by our caller)
+ */
+ worker = kzalloc(sizeof(*worker), GFP_NOWAIT);
+ if (!worker)
+ return;
+
+ worker->priv = priv;
+ INIT_LIST_HEAD(&worker->list);
+ INIT_WORK(&worker->work, gpio_mpsse_poll);
+ schedule_work(&worker->work);
+
+ scoped_guard(raw_spinlock_irqsave, &priv->irq_spin)
+ list_add(&worker->list, &priv->workers);
}
}
@@ -404,18 +548,49 @@ static void gpio_mpsse_ida_remove(void *data)
ida_free(&gpio_mpsse_ida, priv->id);
}
+static int mpsse_init_valid_mask(struct gpio_chip *chip,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct mpsse_priv *priv = gpiochip_get_data(chip);
+
+ if (WARN_ON(priv == NULL))
+ return -ENODEV;
+
+ *valid_mask = priv->dir_in | priv->dir_out;
+
+ return 0;
+}
+
+static void mpsse_irq_init_valid_mask(struct gpio_chip *chip,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct mpsse_priv *priv = gpiochip_get_data(chip);
+
+ if (WARN_ON(priv == NULL))
+ return;
+
+ /* Can only use IRQ on input capable pins */
+ *valid_mask = priv->dir_in;
+}
+
static int gpio_mpsse_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct mpsse_priv *priv;
struct device *dev;
+ char *serial;
int err;
+ struct mpsse_quirk *quirk = (void *)id->driver_info;
dev = &interface->dev;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ INIT_LIST_HEAD(&priv->workers);
+
priv->udev = usb_get_dev(interface_to_usbdev(interface));
priv->intf = interface;
priv->intf_id = interface->cur_altsetting->desc.bInterfaceNumber;
@@ -436,9 +611,21 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
if (err)
return err;
+ err = devm_mutex_init(dev, &priv->irq_race);
+ if (err)
+ return err;
+
+ raw_spin_lock_init(&priv->irq_spin);
+
+ serial = priv->udev->serial;
+ if (!serial)
+ serial = "NONE";
+
priv->gpio.label = devm_kasprintf(dev, GFP_KERNEL,
- "gpio-mpsse.%d.%d",
- priv->id, priv->intf_id);
+ "MPSSE%04x:%04x.%d.%d.%s",
+ id->idVendor, id->idProduct,
+ priv->intf_id, priv->id,
+ serial);
if (!priv->gpio.label)
return -ENOMEM;
@@ -452,10 +639,20 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
priv->gpio.get_multiple = gpio_mpsse_get_multiple;
priv->gpio.set_multiple = gpio_mpsse_set_multiple;
priv->gpio.base = -1;
- priv->gpio.ngpio = 16;
+ priv->gpio.ngpio = MPSSE_NGPIO;
priv->gpio.offset = priv->intf_id * priv->gpio.ngpio;
priv->gpio.can_sleep = 1;
+ if (quirk) {
+ priv->dir_out = quirk->dir_out;
+ priv->dir_in = quirk->dir_in;
+ priv->gpio.names = quirk->names;
+ priv->gpio.init_valid_mask = mpsse_init_valid_mask;
+ } else {
+ priv->dir_in = U16_MAX;
+ priv->dir_out = U16_MAX;
+ }
+
err = usb_find_common_endpoints(interface->cur_altsetting,
&priv->bulk_in, &priv->bulk_out,
NULL, NULL);
@@ -494,6 +691,7 @@ static int gpio_mpsse_probe(struct usb_interface *interface,
priv->gpio.irq.parents = NULL;
priv->gpio.irq.default_type = IRQ_TYPE_NONE;
priv->gpio.irq.handler = handle_simple_irq;
+ priv->gpio.irq.init_valid_mask = mpsse_irq_init_valid_mask;
err = devm_gpiochip_add_data(dev, &priv->gpio, priv);
if (err)
@@ -506,6 +704,13 @@ static void gpio_mpsse_disconnect(struct usb_interface *intf)
{
struct mpsse_priv *priv = usb_get_intfdata(intf);
+ /*
+ * Lock prevents double-free of worker from here and the teardown
+ * step at the beginning of gpio_mpsse_poll
+ */
+ scoped_guard(mutex, &priv->irq_race)
+ gpio_mpsse_stop_all_except(priv, NULL);
+
priv->intf = NULL;
usb_set_intfdata(intf, NULL);
usb_put_dev(priv->udev);
diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c
index b0cccd856840..7345afdc78de 100644
--- a/drivers/gpio/gpio-msc313.c
+++ b/drivers/gpio/gpio-msc313.c
@@ -694,7 +694,7 @@ static const struct of_device_id msc313_gpio_of_match[] = {
* SoC goes into suspend to memory mode so we need to save some
* of the register bits before suspending and put it back when resuming
*/
-static int __maybe_unused msc313_gpio_suspend(struct device *dev)
+static int msc313_gpio_suspend(struct device *dev)
{
struct msc313_gpio *gpio = dev_get_drvdata(dev);
int i;
@@ -705,7 +705,7 @@ static int __maybe_unused msc313_gpio_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused msc313_gpio_resume(struct device *dev)
+static int msc313_gpio_resume(struct device *dev)
{
struct msc313_gpio *gpio = dev_get_drvdata(dev);
int i;
@@ -716,13 +716,13 @@ static int __maybe_unused msc313_gpio_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume);
static struct platform_driver msc313_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = msc313_gpio_of_match,
- .pm = &msc313_gpio_ops,
+ .pm = pm_sleep_ptr(&msc313_gpio_ops),
},
.probe = msc313_gpio_probe,
};
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index ac799fced950..22c36b79e249 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -573,11 +573,10 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
for (i = 0; i < mvchip->chip.ngpio; i++) {
int irq;
- irq = irq_find_mapping(mvchip->domain, i);
-
if (!(cause & BIT(i)))
continue;
+ irq = irq_find_mapping(mvchip->domain, i);
type = irq_get_trigger_type(irq);
if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) {
/* Swap polarity (race with GPIO line) */
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index a268c76bdca6..e136e81794df 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -1503,7 +1503,7 @@ static void omap_gpio_remove(struct platform_device *pdev)
clk_unprepare(bank->dbck);
}
-static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
+static int omap_gpio_runtime_suspend(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags;
@@ -1516,7 +1516,7 @@ static int __maybe_unused omap_gpio_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
+static int omap_gpio_runtime_resume(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
unsigned long flags;
@@ -1529,7 +1529,7 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
return 0;
}
-static int __maybe_unused omap_gpio_suspend(struct device *dev)
+static int omap_gpio_suspend(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
@@ -1541,7 +1541,7 @@ static int __maybe_unused omap_gpio_suspend(struct device *dev)
return omap_gpio_runtime_suspend(dev);
}
-static int __maybe_unused omap_gpio_resume(struct device *dev)
+static int omap_gpio_resume(struct device *dev)
{
struct gpio_bank *bank = dev_get_drvdata(dev);
@@ -1554,9 +1554,8 @@ static int __maybe_unused omap_gpio_resume(struct device *dev)
}
static const struct dev_pm_ops gpio_pm_ops = {
- SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
- NULL)
- SET_LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume)
+ RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume, NULL)
+ LATE_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume)
};
static struct platform_driver omap_gpio_driver = {
@@ -1564,7 +1563,7 @@ static struct platform_driver omap_gpio_driver = {
.remove = omap_gpio_remove,
.driver = {
.name = "omap_gpio",
- .pm = &gpio_pm_ops,
+ .pm = pm_ptr(&gpio_pm_ops),
.of_match_table = omap_gpio_match,
},
};
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index b46927f55038..0a3916cc2772 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -306,7 +306,7 @@ static inline u8 pca953x_get_bit_mask(struct pca953x_chip *chip, unsigned int of
* Interrupt mask register 0x40 + 5 * bank_size RW
* Interrupt status register 0x40 + 6 * bank_size R
*
- * - Registers with bit 0x80 set, the AI bit
+ * - Registers with bit 0x80 set, the AI bit (auto increment)
* The bit is cleared and the registers fall into one of the
* categories above.
*/
@@ -854,10 +854,13 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)
int level;
if (chip->driver_data & PCA_PCAL) {
+ DECLARE_BITMAP(latched_inputs, MAX_LINE);
guard(mutex)(&chip->i2c_lock);
- /* Enable latch on interrupt-enabled inputs */
- pca953x_write_regs(chip, PCAL953X_IN_LATCH, chip->irq_mask);
+ /* Enable latch on edge-triggered interrupt-enabled inputs */
+ bitmap_or(latched_inputs, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio);
+ bitmap_and(latched_inputs, latched_inputs, chip->irq_mask, gc->ngpio);
+ pca953x_write_regs(chip, PCAL953X_IN_LATCH, latched_inputs);
bitmap_complement(irq_mask, chip->irq_mask, gc->ngpio);
@@ -1203,10 +1206,10 @@ static int pca953x_probe(struct i2c_client *client)
pca953x_setup_gpio(chip, chip->driver_data & PCA_GPIO_MASK);
if (NBANK(chip) > 2 || PCA_CHIP_TYPE(chip->driver_data) == PCA957X_TYPE) {
- dev_info(dev, "using AI\n");
+ dev_info(dev, "using auto increment\n");
regmap_config = &pca953x_ai_i2c_regmap;
} else {
- dev_info(dev, "using no AI\n");
+ dev_info(dev, "using no auto increment\n");
regmap_config = &pca953x_i2c_regmap;
}
diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c
index 9925687e05fb..4ffa0955a9e3 100644
--- a/drivers/gpio/gpio-pch.c
+++ b/drivers/gpio/gpio-pch.c
@@ -171,7 +171,7 @@ static int pch_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr)
/*
* Save register configuration and disable interrupts.
*/
-static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip)
+static void pch_gpio_save_reg_conf(struct pch_gpio *chip)
{
chip->pch_gpio_reg.ien_reg = ioread32(&chip->reg->ien);
chip->pch_gpio_reg.imask_reg = ioread32(&chip->reg->imask);
@@ -187,7 +187,7 @@ static void __maybe_unused pch_gpio_save_reg_conf(struct pch_gpio *chip)
/*
* This function restores the register configuration of the GPIO device.
*/
-static void __maybe_unused pch_gpio_restore_reg_conf(struct pch_gpio *chip)
+static void pch_gpio_restore_reg_conf(struct pch_gpio *chip)
{
iowrite32(chip->pch_gpio_reg.ien_reg, &chip->reg->ien);
iowrite32(chip->pch_gpio_reg.imask_reg, &chip->reg->imask);
@@ -402,7 +402,7 @@ static int pch_gpio_probe(struct pci_dev *pdev,
return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]);
}
-static int __maybe_unused pch_gpio_suspend(struct device *dev)
+static int pch_gpio_suspend(struct device *dev)
{
struct pch_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
@@ -414,7 +414,7 @@ static int __maybe_unused pch_gpio_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused pch_gpio_resume(struct device *dev)
+static int pch_gpio_resume(struct device *dev)
{
struct pch_gpio *chip = dev_get_drvdata(dev);
unsigned long flags;
@@ -428,7 +428,7 @@ static int __maybe_unused pch_gpio_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume);
static const struct pci_device_id pch_gpio_pcidev_id[] = {
{ PCI_DEVICE_DATA(INTEL, EG20T_PCH, INTEL_EG20T_PCH) },
@@ -444,7 +444,7 @@ static struct pci_driver pch_gpio_driver = {
.id_table = pch_gpio_pcidev_id,
.probe = pch_gpio_probe,
.driver = {
- .pm = &pch_gpio_pm_ops,
+ .pm = pm_sleep_ptr(&pch_gpio_pm_ops),
},
};
diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c
index 476cea1b5ed7..9d28ca8e1d6f 100644
--- a/drivers/gpio/gpio-pci-idio-16.c
+++ b/drivers/gpio/gpio-pci-idio-16.c
@@ -41,6 +41,7 @@ static const struct regmap_config idio_16_regmap_config = {
.reg_stride = 1,
.val_bits = 8,
.io_port = true,
+ .max_register = 0x7,
.wr_table = &idio_16_wr_table,
.rd_table = &idio_16_rd_table,
.volatile_table = &idio_16_rd_table,
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 02e4ffcf5a6f..919cf86fd590 100644
--- a/drivers/gpio/gpio-pl061.c
+++ b/drivers/gpio/gpio-pl061.c
@@ -37,7 +37,6 @@
#define PL061_GPIO_NR 8
-#ifdef CONFIG_PM
struct pl061_context_save_regs {
u8 gpio_data;
u8 gpio_dir;
@@ -46,7 +45,6 @@ struct pl061_context_save_regs {
u8 gpio_iev;
u8 gpio_ie;
};
-#endif
struct pl061 {
raw_spinlock_t lock;
@@ -55,9 +53,7 @@ struct pl061 {
struct gpio_chip gc;
int parent_irq;
-#ifdef CONFIG_PM
struct pl061_context_save_regs csave_regs;
-#endif
};
static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
@@ -367,7 +363,6 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
return 0;
}
-#ifdef CONFIG_PM
static int pl061_suspend(struct device *dev)
{
struct pl061 *pl061 = dev_get_drvdata(dev);
@@ -411,13 +406,7 @@ static int pl061_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops pl061_dev_pm_ops = {
- .suspend = pl061_suspend,
- .resume = pl061_resume,
- .freeze = pl061_suspend,
- .restore = pl061_resume,
-};
-#endif
+static DEFINE_SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume);
static const struct amba_id pl061_ids[] = {
{
@@ -431,9 +420,7 @@ MODULE_DEVICE_TABLE(amba, pl061_ids);
static struct amba_driver pl061_gpio_driver = {
.drv = {
.name = "pl061_gpio",
-#ifdef CONFIG_PM
- .pm = &pl061_dev_pm_ops,
-#endif
+ .pm = pm_sleep_ptr(&pl061_dev_pm_ops),
},
.id_table = pl061_ids,
.probe = pl061_probe,
diff --git a/drivers/gpio/gpio-qixis-fpga.c b/drivers/gpio/gpio-qixis-fpga.c
new file mode 100644
index 000000000000..6e67f43ac0bd
--- /dev/null
+++ b/drivers/gpio/gpio-qixis-fpga.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Layerscape GPIO QIXIS FPGA driver
+ *
+ * Copyright 2025 NXP
+ */
+
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct qixis_cpld_gpio_config {
+ u64 output_lines;
+};
+
+static const struct qixis_cpld_gpio_config lx2160ardb_sfp_cfg = {
+ .output_lines = BIT(0),
+};
+
+static const struct qixis_cpld_gpio_config ls1046aqds_stat_pres2_cfg = {
+ .output_lines = 0x0,
+};
+
+static const struct regmap_config regmap_config_8r_8v = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int qixis_cpld_gpio_probe(struct platform_device *pdev)
+{
+ DECLARE_BITMAP(fixed_direction_output, 8);
+ const struct qixis_cpld_gpio_config *cfg;
+ struct gpio_regmap_config config = {0};
+ struct regmap *regmap;
+ void __iomem *reg;
+ u32 base;
+ int ret;
+
+ if (!pdev->dev.parent)
+ return -ENODEV;
+
+ cfg = device_get_match_data(&pdev->dev);
+
+ ret = device_property_read_u32(&pdev->dev, "reg", &base);
+ if (ret)
+ return ret;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap) {
+ /* In case there is no regmap configured by the parent device,
+ * create our own from the MMIO space.
+ */
+ reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ regmap = devm_regmap_init_mmio(&pdev->dev, reg, &regmap_config_8r_8v);
+ if (!regmap)
+ return -ENODEV;
+
+ /* In this case, the offset of our register is 0 inside the
+ * regmap area that we just created.
+ */
+ base = 0;
+ }
+ config.reg_dat_base = GPIO_REGMAP_ADDR(base);
+ config.reg_set_base = GPIO_REGMAP_ADDR(base);
+
+ config.drvdata = (void *)cfg;
+ config.regmap = regmap;
+ config.parent = &pdev->dev;
+ config.ngpio_per_reg = 8;
+ config.ngpio = 8;
+
+ bitmap_from_u64(fixed_direction_output, cfg->output_lines);
+ config.fixed_direction_output = fixed_direction_output;
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&pdev->dev, &config));
+}
+
+static const struct of_device_id qixis_cpld_gpio_of_match[] = {
+ {
+ .compatible = "fsl,lx2160ardb-fpga-gpio-sfp",
+ .data = &lx2160ardb_sfp_cfg,
+ },
+ {
+ .compatible = "fsl,ls1046aqds-fpga-gpio-stat-pres2",
+ .data = &ls1046aqds_stat_pres2_cfg,
+ },
+
+ {}
+};
+MODULE_DEVICE_TABLE(of, qixis_cpld_gpio_of_match);
+
+static struct platform_driver qixis_cpld_gpio_driver = {
+ .probe = qixis_cpld_gpio_probe,
+ .driver = {
+ .name = "gpio-qixis-cpld",
+ .of_match_table = qixis_cpld_gpio_of_match,
+ },
+};
+module_platform_driver(qixis_cpld_gpio_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
+MODULE_DESCRIPTION("Layerscape GPIO QIXIS FPGA driver");
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index ab9e4077fa60..e5ba38e65c10 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -31,6 +31,7 @@ struct gpio_regmap {
unsigned int reg_clr_base;
unsigned int reg_dir_in_base;
unsigned int reg_dir_out_base;
+ unsigned long *fixed_direction_output;
#ifdef CONFIG_REGMAP_IRQ
int regmap_irq_line;
@@ -81,7 +82,11 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
if (ret)
return ret;
- ret = regmap_read(gpio->regmap, reg, &val);
+ /* ensure we don't spoil any register cache with pin input values */
+ if (gpio->reg_dat_base == gpio->reg_set_base)
+ ret = regmap_read_bypassed(gpio->regmap, reg, &val);
+ else
+ ret = regmap_read(gpio->regmap, reg, &val);
if (ret)
return ret;
@@ -93,7 +98,7 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
unsigned int base = gpio_regmap_addr(gpio->reg_set_base);
- unsigned int reg, mask;
+ unsigned int reg, mask, mask_val;
int ret;
ret = gpio->reg_mask_xlate(gpio, base, offset, &reg, &mask);
@@ -101,9 +106,15 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
return ret;
if (val)
- ret = regmap_update_bits(gpio->regmap, reg, mask, mask);
+ mask_val = mask;
else
- ret = regmap_update_bits(gpio->regmap, reg, mask, 0);
+ mask_val = 0;
+
+ /* ignore input values which shadow the old output value */
+ if (gpio->reg_dat_base == gpio->reg_set_base)
+ ret = regmap_write_bits(gpio->regmap, reg, mask, mask_val);
+ else
+ ret = regmap_update_bits(gpio->regmap, reg, mask, mask_val);
return ret;
}
@@ -134,6 +145,13 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
unsigned int base, val, reg, mask;
int invert, ret;
+ if (gpio->fixed_direction_output) {
+ if (test_bit(offset, gpio->fixed_direction_output))
+ return GPIO_LINE_DIRECTION_OUT;
+ else
+ return GPIO_LINE_DIRECTION_IN;
+ }
+
if (gpio->reg_dat_base && !gpio->reg_set_base)
return GPIO_LINE_DIRECTION_IN;
if (gpio->reg_set_base && !gpio->reg_dat_base)
@@ -284,6 +302,17 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
goto err_free_gpio;
}
+ if (config->fixed_direction_output) {
+ gpio->fixed_direction_output = bitmap_alloc(chip->ngpio,
+ GFP_KERNEL);
+ if (!gpio->fixed_direction_output) {
+ ret = -ENOMEM;
+ goto err_free_gpio;
+ }
+ bitmap_copy(gpio->fixed_direction_output,
+ config->fixed_direction_output, chip->ngpio);
+ }
+
/* if not set, assume there is only one register */
gpio->ngpio_per_reg = config->ngpio_per_reg;
if (!gpio->ngpio_per_reg)
@@ -300,7 +329,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
ret = gpiochip_add_data(chip, gpio);
if (ret < 0)
- goto err_free_gpio;
+ goto err_free_bitmap;
#ifdef CONFIG_REGMAP_IRQ
if (config->regmap_irq_chip) {
@@ -309,7 +338,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
config->regmap_irq_line, config->regmap_irq_flags,
0, config->regmap_irq_chip, &gpio->irq_chip_data);
if (ret)
- goto err_free_gpio;
+ goto err_free_bitmap;
irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);
} else
@@ -326,6 +355,8 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
err_remove_gpiochip:
gpiochip_remove(chip);
+err_free_bitmap:
+ bitmap_free(gpio->fixed_direction_output);
err_free_gpio:
kfree(gpio);
return ERR_PTR(ret);
@@ -344,6 +375,7 @@ void gpio_regmap_unregister(struct gpio_regmap *gpio)
#endif
gpiochip_remove(&gpio->gpio_chip);
+ bitmap_free(gpio->fixed_direction_output);
kfree(gpio);
}
EXPORT_SYMBOL_GPL(gpio_regmap_unregister);
diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c
new file mode 100644
index 000000000000..29d7d2e4dfc0
--- /dev/null
+++ b/drivers/gpio/gpio-shared-proxy.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/string_choices.h>
+#include <linux/types.h>
+
+#include "gpiolib-shared.h"
+
+struct gpio_shared_proxy_data {
+ struct gpio_chip gc;
+ struct gpio_shared_desc *shared_desc;
+ struct device *dev;
+ bool voted_high;
+};
+
+static int
+gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy,
+ int (*set_func)(struct gpio_desc *desc, int value),
+ int value)
+{
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int ret = 0;
+
+ gpio_shared_lockdep_assert(shared_desc);
+
+ if (value) {
+ /* User wants to set value to high. */
+ if (proxy->voted_high)
+ /* Already voted for high, nothing to do. */
+ goto out;
+
+ /* Haven't voted for high yet. */
+ if (!shared_desc->highcnt) {
+ /*
+ * Current value is low, need to actually set value
+ * to high.
+ */
+ ret = set_func(desc, 1);
+ if (ret)
+ goto out;
+ }
+
+ shared_desc->highcnt++;
+ proxy->voted_high = true;
+
+ goto out;
+ }
+
+ /* Desired value is low. */
+ if (!proxy->voted_high)
+ /* We didn't vote for high, nothing to do. */
+ goto out;
+
+ /* We previously voted for high. */
+ if (shared_desc->highcnt == 1) {
+ /* This is the last remaining vote for high, set value to low. */
+ ret = set_func(desc, 0);
+ if (ret)
+ goto out;
+ }
+
+ shared_desc->highcnt--;
+ proxy->voted_high = false;
+
+out:
+ if (shared_desc->highcnt)
+ dev_dbg(proxy->dev,
+ "Voted for value '%s', effective value is 'high', number of votes for 'high': %u\n",
+ str_high_low(value), shared_desc->highcnt);
+ else
+ dev_dbg(proxy->dev, "Voted for value 'low', effective value is 'low'\n");
+
+ return ret;
+}
+
+static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ proxy->shared_desc->usecnt++;
+
+ dev_dbg(proxy->dev, "Shared GPIO requested, number of users: %u\n",
+ proxy->shared_desc->usecnt);
+
+ return 0;
+}
+
+static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ proxy->shared_desc->usecnt--;
+
+ dev_dbg(proxy->dev, "Shared GPIO freed, number of users: %u\n",
+ proxy->shared_desc->usecnt);
+}
+
+static int gpio_shared_proxy_set_config(struct gpio_chip *gc,
+ unsigned int offset, unsigned long cfg)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int ret;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ if (shared_desc->usecnt > 1) {
+ if (shared_desc->cfg != cfg) {
+ dev_dbg(proxy->dev,
+ "Shared GPIO's configuration already set, accepting changes but users may conflict!!\n");
+ } else {
+ dev_dbg(proxy->dev, "Equal config requested, nothing to do\n");
+ return 0;
+ }
+ }
+
+ ret = gpiod_set_config(desc, cfg);
+ if (ret && ret != -ENOTSUPP)
+ return ret;
+
+ shared_desc->cfg = cfg;
+ return 0;
+}
+
+static int gpio_shared_proxy_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int dir;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ if (shared_desc->usecnt == 1) {
+ dev_dbg(proxy->dev,
+ "Only one user of this shared GPIO, allowing to set direction to input\n");
+
+ return gpiod_direction_input(desc);
+ }
+
+ dir = gpiod_get_direction(desc);
+ if (dir < 0)
+ return dir;
+
+ if (dir == GPIO_LINE_DIRECTION_OUT) {
+ dev_dbg(proxy->dev,
+ "Shared GPIO's direction already set to output, refusing to change\n");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+ struct gpio_shared_desc *shared_desc = proxy->shared_desc;
+ struct gpio_desc *desc = shared_desc->desc;
+ int ret, dir;
+
+ guard(gpio_shared_desc_lock)(shared_desc);
+
+ if (shared_desc->usecnt == 1) {
+ dev_dbg(proxy->dev,
+ "Only one user of this shared GPIO, allowing to set direction to output with value '%s'\n",
+ str_high_low(value));
+
+ ret = gpiod_direction_output(desc, value);
+ if (ret)
+ return ret;
+
+ if (value) {
+ proxy->voted_high = true;
+ shared_desc->highcnt = 1;
+ } else {
+ proxy->voted_high = false;
+ shared_desc->highcnt = 0;
+ }
+
+ return 0;
+ }
+
+ dir = gpiod_get_direction(desc);
+ if (dir < 0)
+ return dir;
+
+ if (dir == GPIO_LINE_DIRECTION_IN) {
+ dev_dbg(proxy->dev,
+ "Shared GPIO's direction already set to input, refusing to change\n");
+ return -EPERM;
+ }
+
+ return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value);
+}
+
+static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_get_value(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_get_value_cansleep(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy,
+ int (*set_func)(struct gpio_desc *desc, int value),
+ int value)
+{
+ guard(gpio_shared_desc_lock)(proxy->shared_desc);
+
+ return gpio_shared_proxy_set_unlocked(proxy, set_func, value);
+}
+
+static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value);
+}
+
+static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value);
+}
+
+static int gpio_shared_proxy_get_direction(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_get_direction(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_to_irq(struct gpio_chip *gc, unsigned int offset)
+{
+ struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
+
+ return gpiod_to_irq(proxy->shared_desc->desc);
+}
+
+static int gpio_shared_proxy_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct gpio_shared_proxy_data *proxy;
+ struct gpio_shared_desc *shared_desc;
+ struct device *dev = &adev->dev;
+ struct gpio_chip *gc;
+
+ shared_desc = devm_gpiod_shared_get(dev);
+ if (IS_ERR(shared_desc))
+ return PTR_ERR(shared_desc);
+
+ proxy = devm_kzalloc(dev, sizeof(*proxy), GFP_KERNEL);
+ if (!proxy)
+ return -ENOMEM;
+
+ proxy->shared_desc = shared_desc;
+ proxy->dev = dev;
+
+ gc = &proxy->gc;
+ gc->base = -1;
+ gc->ngpio = 1;
+ gc->label = dev_name(dev);
+ gc->parent = dev;
+ gc->owner = THIS_MODULE;
+ gc->can_sleep = shared_desc->can_sleep;
+
+ gc->request = gpio_shared_proxy_request;
+ gc->free = gpio_shared_proxy_free;
+ gc->set_config = gpio_shared_proxy_set_config;
+ gc->direction_input = gpio_shared_proxy_direction_input;
+ gc->direction_output = gpio_shared_proxy_direction_output;
+ if (gc->can_sleep) {
+ gc->set = gpio_shared_proxy_set_cansleep;
+ gc->get = gpio_shared_proxy_get_cansleep;
+ } else {
+ gc->set = gpio_shared_proxy_set;
+ gc->get = gpio_shared_proxy_get;
+ }
+ gc->get_direction = gpio_shared_proxy_get_direction;
+ gc->to_irq = gpio_shared_proxy_to_irq;
+
+ return devm_gpiochip_add_data(dev, &proxy->gc, proxy);
+}
+
+static const struct auxiliary_device_id gpio_shared_proxy_id_table[] = {
+ { .name = "gpiolib_shared.proxy" },
+ {},
+};
+MODULE_DEVICE_TABLE(auxiliary, gpio_shared_proxy_id_table);
+
+static struct auxiliary_driver gpio_shared_proxy_driver = {
+ .driver = {
+ .name = "gpio-shared-proxy",
+ .suppress_bind_attrs = true,
+ },
+ .probe = gpio_shared_proxy_probe,
+ .id_table = gpio_shared_proxy_id_table,
+};
+module_auxiliary_driver(gpio_shared_proxy_driver);
+
+MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
+MODULE_DESCRIPTION("Shared GPIO mux driver.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c
index 09a448ce3eec..3c8fd322a713 100644
--- a/drivers/gpio/gpio-tb10x.c
+++ b/drivers/gpio/gpio-tb10x.c
@@ -50,25 +50,6 @@ static inline u32 tb10x_reg_read(struct tb10x_gpio *gpio, unsigned int offs)
return ioread32(gpio->base + offs);
}
-static inline void tb10x_reg_write(struct tb10x_gpio *gpio, unsigned int offs,
- u32 val)
-{
- iowrite32(val, gpio->base + offs);
-}
-
-static inline void tb10x_set_bits(struct tb10x_gpio *gpio, unsigned int offs,
- u32 mask, u32 val)
-{
- u32 r;
-
- guard(gpio_generic_lock_irqsave)(&gpio->chip);
-
- r = tb10x_reg_read(gpio, offs);
- r = (r & ~mask) | (val & mask);
-
- tb10x_reg_write(gpio, offs, r);
-}
-
static int tb10x_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
struct tb10x_gpio *tb10x_gpio = gpiochip_get_data(chip);
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index 4d3db6e06eeb..b1498b59a921 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2016-2022 NVIDIA Corporation
+ * Copyright (c) 2016-2025 NVIDIA Corporation
*
* Author: Thierry Reding <treding@nvidia.com>
* Dipen Patel <dpatel@nvidia.com>
@@ -69,6 +69,30 @@
#define TEGRA186_GPIO_INTERRUPT_STATUS(x) (0x100 + (x) * 4)
+/* Tegra410 GPIOs implemented by the COMPUTE GPIO controller */
+#define TEGRA410_COMPUTE_GPIO_PORT_A 0
+#define TEGRA410_COMPUTE_GPIO_PORT_B 1
+#define TEGRA410_COMPUTE_GPIO_PORT_C 2
+#define TEGRA410_COMPUTE_GPIO_PORT_D 3
+#define TEGRA410_COMPUTE_GPIO_PORT_E 4
+
+/* Tegra410 GPIOs implemented by the SYSTEM GPIO controller */
+#define TEGRA410_SYSTEM_GPIO_PORT_A 0
+#define TEGRA410_SYSTEM_GPIO_PORT_B 1
+#define TEGRA410_SYSTEM_GPIO_PORT_C 2
+#define TEGRA410_SYSTEM_GPIO_PORT_D 3
+#define TEGRA410_SYSTEM_GPIO_PORT_E 4
+#define TEGRA410_SYSTEM_GPIO_PORT_I 5
+#define TEGRA410_SYSTEM_GPIO_PORT_J 6
+#define TEGRA410_SYSTEM_GPIO_PORT_K 7
+#define TEGRA410_SYSTEM_GPIO_PORT_L 8
+#define TEGRA410_SYSTEM_GPIO_PORT_M 9
+#define TEGRA410_SYSTEM_GPIO_PORT_N 10
+#define TEGRA410_SYSTEM_GPIO_PORT_P 11
+#define TEGRA410_SYSTEM_GPIO_PORT_Q 12
+#define TEGRA410_SYSTEM_GPIO_PORT_R 13
+#define TEGRA410_SYSTEM_GPIO_PORT_V 14
+
struct tegra_gpio_port {
const char *name;
unsigned int bank;
@@ -85,6 +109,7 @@ struct tegra_gpio_soc {
const struct tegra_gpio_port *ports;
unsigned int num_ports;
const char *name;
+ const char *prefix;
unsigned int instance;
unsigned int num_irqs_per_bank;
@@ -916,8 +941,12 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
char *name;
for (j = 0; j < port->pins; j++) {
- name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL,
- "P%s.%02x", port->name, j);
+ if (gpio->soc->prefix)
+ name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "%s-P%s.%02x",
+ gpio->soc->prefix, port->name, j);
+ else
+ name = devm_kasprintf(gpio->gpio.parent, GFP_KERNEL, "P%s.%02x",
+ port->name, j);
if (!name)
return -ENOMEM;
@@ -1002,14 +1031,17 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
return devm_gpiochip_add_data(&pdev->dev, &gpio->gpio, gpio);
}
-#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA186_MAIN_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
+#define TEGRA_GPIO_PORT(_prefix, _name, _bank, _port, _pins) \
+ [_prefix##_GPIO_PORT_##_name] = { \
+ .name = #_name, \
+ .bank = _bank, \
+ .port = _port, \
+ .pins = _pins, \
}
+#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA186_MAIN, _name, _bank, _port, _pins)
+
static const struct tegra_gpio_port tegra186_main_ports[] = {
TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7),
TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7),
@@ -1045,13 +1077,8 @@ static const struct tegra_gpio_soc tegra186_main_soc = {
.has_vm_support = false,
};
-#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA186_AON_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA186_AON, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra186_aon_ports[] = {
TEGRA186_AON_GPIO_PORT( S, 0, 1, 5),
@@ -1073,13 +1100,8 @@ static const struct tegra_gpio_soc tegra186_aon_soc = {
.has_vm_support = false,
};
-#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA194_MAIN_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA194_MAIN, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra194_main_ports[] = {
TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8),
@@ -1129,13 +1151,8 @@ static const struct tegra_gpio_soc tegra194_main_soc = {
.has_vm_support = true,
};
-#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA194_AON_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA194_AON, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra194_aon_ports[] = {
TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8),
@@ -1155,13 +1172,8 @@ static const struct tegra_gpio_soc tegra194_aon_soc = {
.has_vm_support = false,
};
-#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA234_MAIN_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA234_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA234_MAIN, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra234_main_ports[] = {
TEGRA234_MAIN_GPIO_PORT( A, 0, 0, 8),
@@ -1200,13 +1212,8 @@ static const struct tegra_gpio_soc tegra234_main_soc = {
.has_vm_support = true,
};
-#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA234_AON_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA234_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA234_AON, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra234_aon_ports[] = {
TEGRA234_AON_GPIO_PORT(AA, 0, 4, 8),
@@ -1227,13 +1234,8 @@ static const struct tegra_gpio_soc tegra234_aon_soc = {
.has_vm_support = false,
};
-#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA241_MAIN_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA241_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA241_MAIN, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra241_main_ports[] = {
TEGRA241_MAIN_GPIO_PORT(A, 0, 0, 8),
@@ -1258,13 +1260,8 @@ static const struct tegra_gpio_soc tegra241_main_soc = {
.has_vm_support = false,
};
-#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA241_AON_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA241_AON_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA241_AON, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra241_aon_ports[] = {
TEGRA241_AON_GPIO_PORT(AA, 0, 0, 8),
@@ -1280,13 +1277,8 @@ static const struct tegra_gpio_soc tegra241_aon_soc = {
.has_vm_support = false,
};
-#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
- [TEGRA256_MAIN_GPIO_PORT_##_name] = { \
- .name = #_name, \
- .bank = _bank, \
- .port = _port, \
- .pins = _pins, \
- }
+#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins)
static const struct tegra_gpio_port tegra256_main_ports[] = {
TEGRA256_MAIN_GPIO_PORT(A, 0, 0, 8),
@@ -1304,6 +1296,56 @@ static const struct tegra_gpio_soc tegra256_main_soc = {
.has_vm_support = true,
};
+#define TEGRA410_COMPUTE_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA410_COMPUTE, _name, _bank, _port, _pins)
+
+static const struct tegra_gpio_port tegra410_compute_ports[] = {
+ TEGRA410_COMPUTE_GPIO_PORT(A, 0, 0, 3),
+ TEGRA410_COMPUTE_GPIO_PORT(B, 1, 0, 8),
+ TEGRA410_COMPUTE_GPIO_PORT(C, 1, 1, 3),
+ TEGRA410_COMPUTE_GPIO_PORT(D, 2, 0, 8),
+ TEGRA410_COMPUTE_GPIO_PORT(E, 2, 1, 8),
+};
+
+static const struct tegra_gpio_soc tegra410_compute_soc = {
+ .num_ports = ARRAY_SIZE(tegra410_compute_ports),
+ .ports = tegra410_compute_ports,
+ .name = "tegra410-gpio-compute",
+ .prefix = "COMPUTE",
+ .num_irqs_per_bank = 8,
+ .instance = 0,
+};
+
+#define TEGRA410_SYSTEM_GPIO_PORT(_name, _bank, _port, _pins) \
+ TEGRA_GPIO_PORT(TEGRA410_SYSTEM, _name, _bank, _port, _pins)
+
+static const struct tegra_gpio_port tegra410_system_ports[] = {
+ TEGRA410_SYSTEM_GPIO_PORT(A, 0, 0, 7),
+ TEGRA410_SYSTEM_GPIO_PORT(B, 0, 1, 8),
+ TEGRA410_SYSTEM_GPIO_PORT(C, 0, 2, 8),
+ TEGRA410_SYSTEM_GPIO_PORT(D, 0, 3, 8),
+ TEGRA410_SYSTEM_GPIO_PORT(E, 0, 4, 6),
+ TEGRA410_SYSTEM_GPIO_PORT(I, 1, 0, 8),
+ TEGRA410_SYSTEM_GPIO_PORT(J, 1, 1, 7),
+ TEGRA410_SYSTEM_GPIO_PORT(K, 1, 2, 7),
+ TEGRA410_SYSTEM_GPIO_PORT(L, 1, 3, 7),
+ TEGRA410_SYSTEM_GPIO_PORT(M, 2, 0, 7),
+ TEGRA410_SYSTEM_GPIO_PORT(N, 2, 1, 6),
+ TEGRA410_SYSTEM_GPIO_PORT(P, 2, 2, 8),
+ TEGRA410_SYSTEM_GPIO_PORT(Q, 2, 3, 3),
+ TEGRA410_SYSTEM_GPIO_PORT(R, 2, 4, 2),
+ TEGRA410_SYSTEM_GPIO_PORT(V, 1, 4, 2),
+};
+
+static const struct tegra_gpio_soc tegra410_system_soc = {
+ .num_ports = ARRAY_SIZE(tegra410_system_ports),
+ .ports = tegra410_system_ports,
+ .name = "tegra410-gpio-system",
+ .prefix = "SYSTEM",
+ .num_irqs_per_bank = 8,
+ .instance = 0,
+};
+
static const struct of_device_id tegra186_gpio_of_match[] = {
{
.compatible = "nvidia,tegra186-gpio",
@@ -1339,6 +1381,8 @@ static const struct acpi_device_id tegra186_gpio_acpi_match[] = {
{ .id = "NVDA0408", .driver_data = (kernel_ulong_t)&tegra194_aon_soc },
{ .id = "NVDA0508", .driver_data = (kernel_ulong_t)&tegra241_main_soc },
{ .id = "NVDA0608", .driver_data = (kernel_ulong_t)&tegra241_aon_soc },
+ { .id = "NVDA0708", .driver_data = (kernel_ulong_t)&tegra410_compute_soc },
+ { .id = "NVDA0808", .driver_data = (kernel_ulong_t)&tegra410_system_soc },
{}
};
MODULE_DEVICE_TABLE(acpi, tegra186_gpio_acpi_match);
diff --git a/drivers/gpio/gpio-tqmx86.c b/drivers/gpio/gpio-tqmx86.c
index 27dd09273292..eedfc0e371e3 100644
--- a/drivers/gpio/gpio-tqmx86.c
+++ b/drivers/gpio/gpio-tqmx86.c
@@ -279,19 +279,18 @@ static void tqmx86_gpio_irq_handler(struct irq_desc *desc)
}
/* Minimal runtime PM is needed by the IRQ subsystem */
-static int __maybe_unused tqmx86_gpio_runtime_suspend(struct device *dev)
+static int tqmx86_gpio_runtime_suspend(struct device *dev)
{
return 0;
}
-static int __maybe_unused tqmx86_gpio_runtime_resume(struct device *dev)
+static int tqmx86_gpio_runtime_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops tqmx86_gpio_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend,
- tqmx86_gpio_runtime_resume, NULL)
+ RUNTIME_PM_OPS(tqmx86_gpio_runtime_suspend, tqmx86_gpio_runtime_resume, NULL)
};
static void tqmx86_init_irq_valid_mask(struct gpio_chip *chip,
@@ -425,7 +424,7 @@ out_pm_dis:
static struct platform_driver tqmx86_gpio_driver = {
.driver = {
.name = "tqmx86-gpio",
- .pm = &tqmx86_gpio_dev_pm_ops,
+ .pm = pm_ptr(&tqmx86_gpio_dev_pm_ops),
},
.probe = tqmx86_gpio_probe,
};
diff --git a/drivers/gpio/gpio-uniphier.c b/drivers/gpio/gpio-uniphier.c
index 197bb1d22b3c..0574dde5b5bb 100644
--- a/drivers/gpio/gpio-uniphier.c
+++ b/drivers/gpio/gpio-uniphier.c
@@ -426,7 +426,7 @@ static void uniphier_gpio_remove(struct platform_device *pdev)
irq_domain_remove(priv->domain);
}
-static int __maybe_unused uniphier_gpio_suspend(struct device *dev)
+static int uniphier_gpio_suspend(struct device *dev)
{
struct uniphier_gpio_priv *priv = dev_get_drvdata(dev);
unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio);
@@ -448,7 +448,7 @@ static int __maybe_unused uniphier_gpio_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused uniphier_gpio_resume(struct device *dev)
+static int uniphier_gpio_resume(struct device *dev)
{
struct uniphier_gpio_priv *priv = dev_get_drvdata(dev);
unsigned int nbanks = uniphier_gpio_get_nbanks(priv->chip.ngpio);
@@ -473,8 +473,7 @@ static int __maybe_unused uniphier_gpio_resume(struct device *dev)
}
static const struct dev_pm_ops uniphier_gpio_pm_ops = {
- SET_LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend,
- uniphier_gpio_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(uniphier_gpio_suspend, uniphier_gpio_resume)
};
static const struct of_device_id uniphier_gpio_match[] = {
@@ -489,7 +488,7 @@ static struct platform_driver uniphier_gpio_driver = {
.driver = {
.name = "uniphier-gpio",
.of_match_table = uniphier_gpio_match,
- .pm = &uniphier_gpio_pm_ops,
+ .pm = pm_sleep_ptr(&uniphier_gpio_pm_ops),
},
};
module_platform_driver(uniphier_gpio_driver);
diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c
index a10eab7d2617..37f2ce20f1ae 100644
--- a/drivers/gpio/gpio-virtuser.c
+++ b/drivers/gpio/gpio-virtuser.c
@@ -500,9 +500,7 @@ static int gpio_virtuser_value_set(void *data, u64 val)
if (val > 1)
return -EINVAL;
- gpiod_set_value_cansleep(ld->ad.desc, (int)val);
-
- return 0;
+ return gpiod_set_value_cansleep(ld->ad.desc, (int)val);
}
DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_fops,
@@ -543,7 +541,7 @@ static void gpio_virtuser_set_value_atomic(struct irq_work *work)
struct gpio_virtuser_irq_work_context *ctx =
to_gpio_virtuser_irq_work_context(work);
- gpiod_set_value(ctx->desc, ctx->val);
+ ctx->ret = gpiod_set_value(ctx->desc, ctx->val);
complete(&ctx->work_completion);
}
@@ -562,7 +560,7 @@ static int gpio_virtuser_value_atomic_set(void *data, u64 val)
gpio_virtuser_irq_work_queue_sync(&ctx);
- return 0;
+ return ctx.ret;
}
DEFINE_DEBUGFS_ATTRIBUTE(gpio_virtuser_value_atomic_fops,
diff --git a/drivers/gpio/gpio-xgene.c b/drivers/gpio/gpio-xgene.c
index 4f627de3f56c..809668449dbe 100644
--- a/drivers/gpio/gpio-xgene.c
+++ b/drivers/gpio/gpio-xgene.c
@@ -130,7 +130,7 @@ static int xgene_gpio_dir_out(struct gpio_chip *gc,
return 0;
}
-static __maybe_unused int xgene_gpio_suspend(struct device *dev)
+static int xgene_gpio_suspend(struct device *dev)
{
struct xgene_gpio *gpio = dev_get_drvdata(dev);
unsigned long bank_offset;
@@ -143,7 +143,7 @@ static __maybe_unused int xgene_gpio_suspend(struct device *dev)
return 0;
}
-static __maybe_unused int xgene_gpio_resume(struct device *dev)
+static int xgene_gpio_resume(struct device *dev)
{
struct xgene_gpio *gpio = dev_get_drvdata(dev);
unsigned long bank_offset;
@@ -156,7 +156,7 @@ static __maybe_unused int xgene_gpio_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(xgene_gpio_pm, xgene_gpio_suspend, xgene_gpio_resume);
static int xgene_gpio_probe(struct platform_device *pdev)
{
@@ -204,7 +204,7 @@ static struct platform_driver xgene_gpio_driver = {
.name = "xgene-gpio",
.of_match_table = xgene_gpio_of_match,
.acpi_match_table = ACPI_PTR(xgene_gpio_acpi_match),
- .pm = &xgene_gpio_pm,
+ .pm = pm_sleep_ptr(&xgene_gpio_pm),
},
.probe = xgene_gpio_probe,
};
diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c
index 83675ac81077..be4b4d730547 100644
--- a/drivers/gpio/gpio-xilinx.c
+++ b/drivers/gpio/gpio-xilinx.c
@@ -286,7 +286,7 @@ static void xgpio_free(struct gpio_chip *chip, unsigned int offset)
pm_runtime_put(chip->parent);
}
-static int __maybe_unused xgpio_suspend(struct device *dev)
+static int xgpio_suspend(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
@@ -327,7 +327,7 @@ static void xgpio_irq_ack(struct irq_data *irq_data)
{
}
-static int __maybe_unused xgpio_resume(struct device *dev)
+static int xgpio_resume(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
@@ -343,7 +343,7 @@ static int __maybe_unused xgpio_resume(struct device *dev)
return 0;
}
-static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
+static int xgpio_runtime_suspend(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
@@ -352,7 +352,7 @@ static int __maybe_unused xgpio_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused xgpio_runtime_resume(struct device *dev)
+static int xgpio_runtime_resume(struct device *dev)
{
struct xgpio_instance *gpio = dev_get_drvdata(dev);
@@ -360,9 +360,8 @@ static int __maybe_unused xgpio_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops xgpio_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
- SET_RUNTIME_PM_OPS(xgpio_runtime_suspend,
- xgpio_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(xgpio_suspend, xgpio_resume)
+ RUNTIME_PM_OPS(xgpio_runtime_suspend, xgpio_runtime_resume, NULL)
};
/**
@@ -682,7 +681,7 @@ static struct platform_driver xgpio_plat_driver = {
.driver = {
.name = "gpio-xilinx",
.of_match_table = xgpio_of_match,
- .pm = &xgpio_dev_pm_ops,
+ .pm = pm_ptr(&xgpio_dev_pm_ops),
},
};
diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c
index 0ffd76e8951f..97780c57ab56 100644
--- a/drivers/gpio/gpio-zynq.c
+++ b/drivers/gpio/gpio-zynq.c
@@ -735,7 +735,7 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio)
}
}
-static int __maybe_unused zynq_gpio_suspend(struct device *dev)
+static int zynq_gpio_suspend(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
@@ -756,7 +756,7 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused zynq_gpio_resume(struct device *dev)
+static int zynq_gpio_resume(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
struct irq_data *data = irq_get_irq_data(gpio->irq);
@@ -779,7 +779,7 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev)
return 0;
}
-static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
+static int zynq_gpio_runtime_suspend(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
@@ -788,7 +788,7 @@ static int __maybe_unused zynq_gpio_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused zynq_gpio_runtime_resume(struct device *dev)
+static int zynq_gpio_runtime_resume(struct device *dev)
{
struct zynq_gpio *gpio = dev_get_drvdata(dev);
@@ -814,9 +814,8 @@ static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset)
}
static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume)
- SET_RUNTIME_PM_OPS(zynq_gpio_runtime_suspend,
- zynq_gpio_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(zynq_gpio_suspend, zynq_gpio_resume)
+ RUNTIME_PM_OPS(zynq_gpio_runtime_suspend, zynq_gpio_runtime_resume, NULL)
};
static const struct zynq_platform_data versal_gpio_def = {
@@ -1022,7 +1021,7 @@ static void zynq_gpio_remove(struct platform_device *pdev)
static struct platform_driver zynq_gpio_driver = {
.driver = {
.name = DRIVER_NAME,
- .pm = &zynq_gpio_dev_pm_ops,
+ .pm = pm_ptr(&zynq_gpio_dev_pm_ops),
.of_match_table = zynq_gpio_of_match,
},
.probe = zynq_gpio_probe,
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index 284e762d92c4..83dd227dbbec 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -291,6 +291,19 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity)
return GPIOD_ASIS;
}
+static void acpi_gpio_set_debounce_timeout(struct gpio_desc *desc,
+ unsigned int acpi_debounce)
+{
+ int ret;
+
+ /* ACPI uses hundredths of milliseconds units */
+ acpi_debounce *= 10;
+ ret = gpio_set_debounce_timeout(desc, acpi_debounce);
+ if (ret)
+ gpiod_warn(desc, "Failed to set debounce-timeout %u: %d\n",
+ acpi_debounce, ret);
+}
+
static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,
struct acpi_resource_gpio *agpio,
unsigned int index,
@@ -300,18 +313,12 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip,
enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity);
unsigned int pin = agpio->pin_table[index];
struct gpio_desc *desc;
- int ret;
desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags);
if (IS_ERR(desc))
return desc;
- /* ACPI uses hundredths of milliseconds units */
- ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout * 10);
- if (ret)
- dev_warn(chip->parent,
- "Failed to set debounce-timeout for pin 0x%04X, err %d\n",
- pin, ret);
+ acpi_gpio_set_debounce_timeout(desc, agpio->debounce_timeout);
return desc;
}
@@ -375,8 +382,8 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares,
desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event");
if (IS_ERR(desc)) {
dev_err(chip->parent,
- "Failed to request GPIO for pin 0x%04X, err %ld\n",
- pin, PTR_ERR(desc));
+ "Failed to request GPIO for pin 0x%04X, err %pe\n",
+ pin, desc);
return AE_OK;
}
@@ -944,7 +951,6 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
bool can_fallback = acpi_can_fallback_to_crs(adev, con_id);
struct acpi_gpio_info info = {};
struct gpio_desc *desc;
- int ret;
desc = __acpi_find_gpio(fwnode, con_id, idx, can_fallback, &info);
if (IS_ERR(desc))
@@ -959,10 +965,7 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode,
acpi_gpio_update_gpiod_flags(dflags, &info);
acpi_gpio_update_gpiod_lookup_flags(lookupflags, &info);
- /* ACPI uses hundredths of milliseconds units */
- ret = gpio_set_debounce_timeout(desc, info.debounce * 10);
- if (ret)
- return ERR_PTR(ret);
+ acpi_gpio_set_debounce_timeout(desc, info.debounce);
return desc;
}
@@ -1096,7 +1099,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address,
return AE_BAD_PARAMETER;
}
- length = min_t(u16, agpio->pin_table_length, pin_index + bits);
+ length = min(agpio->pin_table_length, pin_index + bits);
for (i = pin_index; i < length; ++i) {
unsigned int pin = agpio->pin_table[i];
struct acpi_gpio_connection *conn;
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index 175836467f21..3735c9fe1502 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -298,12 +298,13 @@ static const struct file_operations linehandle_fileops = {
#endif
};
+DEFINE_FREE(linehandle_free, struct linehandle_state *, if (!IS_ERR_OR_NULL(_T)) linehandle_free(_T))
+
static int linehandle_create(struct gpio_device *gdev, void __user *ip)
{
struct gpiohandle_request handlereq;
- struct linehandle_state *lh;
- struct file *file;
- int fd, i, ret;
+ struct linehandle_state *lh __free(linehandle_free) = NULL;
+ int i, ret;
u32 lflags;
if (copy_from_user(&handlereq, ip, sizeof(handlereq)))
@@ -327,10 +328,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
lh->label = kstrndup(handlereq.consumer_label,
sizeof(handlereq.consumer_label) - 1,
GFP_KERNEL);
- if (!lh->label) {
- ret = -ENOMEM;
- goto out_free_lh;
- }
+ if (!lh->label)
+ return -ENOMEM;
}
lh->num_descs = handlereq.lines;
@@ -340,20 +339,18 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
u32 offset = handlereq.lineoffsets[i];
struct gpio_desc *desc = gpio_device_get_desc(gdev, offset);
- if (IS_ERR(desc)) {
- ret = PTR_ERR(desc);
- goto out_free_lh;
- }
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
ret = gpiod_request_user(desc, lh->label);
if (ret)
- goto out_free_lh;
+ return ret;
lh->descs[i] = desc;
linehandle_flags_to_desc_flags(handlereq.flags, &desc->flags);
ret = gpiod_set_transitory(desc, false);
if (ret < 0)
- goto out_free_lh;
+ return ret;
/*
* Lines have to be requested explicitly for input
@@ -364,11 +361,11 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
ret = gpiod_direction_output_nonotify(desc, val);
if (ret)
- goto out_free_lh;
+ return ret;
} else if (lflags & GPIOHANDLE_REQUEST_INPUT) {
ret = gpiod_direction_input_nonotify(desc);
if (ret)
- goto out_free_lh;
+ return ret;
}
gpiod_line_state_notify(desc, GPIO_V2_LINE_CHANGED_REQUESTED);
@@ -377,44 +374,23 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
offset);
}
- fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto out_free_lh;
- }
-
- file = anon_inode_getfile("gpio-linehandle",
- &linehandle_fileops,
- lh,
- O_RDONLY | O_CLOEXEC);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto out_put_unused_fd;
- }
+ FD_PREPARE(fdf, O_RDONLY | O_CLOEXEC,
+ anon_inode_getfile("gpio-linehandle", &linehandle_fileops,
+ lh, O_RDONLY | O_CLOEXEC));
+ if (fdf.err)
+ return fdf.err;
+ retain_and_null_ptr(lh);
- handlereq.fd = fd;
- if (copy_to_user(ip, &handlereq, sizeof(handlereq))) {
- /*
- * fput() will trigger the release() callback, so do not go onto
- * the regular error cleanup path here.
- */
- fput(file);
- put_unused_fd(fd);
+ handlereq.fd = fd_prepare_fd(fdf);
+ if (copy_to_user(ip, &handlereq, sizeof(handlereq)))
return -EFAULT;
- }
- fd_install(fd, file);
+ fd_publish(fdf);
dev_dbg(&gdev->dev, "registered chardev handle for %d lines\n",
lh->num_descs);
return 0;
-
-out_put_unused_fd:
- put_unused_fd(fd);
-out_free_lh:
- linehandle_free(lh);
- return ret;
}
#endif /* CONFIG_GPIO_CDEV_V1 */
@@ -676,7 +652,7 @@ static enum hte_return process_hw_ts_thread(void *p)
}
le.line_seqno = line->line_seqno;
le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
- le.offset = gpio_chip_hwgpio(line->desc);
+ le.offset = gpiod_hwgpio(line->desc);
linereq_put_event(lr, &le);
@@ -700,7 +676,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
if (READ_ONCE(line->sw_debounced)) {
line->total_discard_seq++;
line->last_seqno = ts->seq;
- mod_delayed_work(system_wq, &line->work,
+ mod_delayed_work(system_percpu_wq, &line->work,
usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
} else {
if (unlikely(ts->seq < line->line_seqno))
@@ -793,7 +769,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p)
line->line_seqno++;
le.line_seqno = line->line_seqno;
le.seqno = (lr->num_lines == 1) ? le.line_seqno : line->req_seqno;
- le.offset = gpio_chip_hwgpio(line->desc);
+ le.offset = gpiod_hwgpio(line->desc);
linereq_put_event(lr, &le);
@@ -841,7 +817,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *p)
{
struct line *line = p;
- mod_delayed_work(system_wq, &line->work,
+ mod_delayed_work(system_percpu_wq, &line->work,
usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
return IRQ_HANDLED;
@@ -891,7 +867,7 @@ static void debounce_work_func(struct work_struct *work)
lr = line->req;
le.timestamp_ns = line_event_timestamp(line);
- le.offset = gpio_chip_hwgpio(line->desc);
+ le.offset = gpiod_hwgpio(line->desc);
#ifdef CONFIG_HTE
if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) {
/* discard events except the last one */
@@ -1591,7 +1567,7 @@ static void linereq_show_fdinfo(struct seq_file *out, struct file *file)
for (i = 0; i < lr->num_lines; i++)
seq_printf(out, "gpio-line:\t%d\n",
- gpio_chip_hwgpio(lr->lines[i].desc));
+ gpiod_hwgpio(lr->lines[i].desc));
}
#endif
@@ -2244,7 +2220,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
return;
memset(info, 0, sizeof(*info));
- info->offset = gpio_chip_hwgpio(desc);
+ info->offset = gpiod_hwgpio(desc);
if (desc->name)
strscpy(info->name, desc->name, sizeof(info->name));
@@ -2548,8 +2524,15 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb);
struct lineinfo_changed_ctx *ctx;
struct gpio_desc *desc = data;
+ struct file *fp;
+
+ if (!test_bit(gpiod_hwgpio(desc), cdev->watched_lines))
+ return NOTIFY_DONE;
- if (!test_bit(gpio_chip_hwgpio(desc), cdev->watched_lines))
+ /* Keep the file descriptor alive for the duration of the notification. */
+ fp = get_file_active(&cdev->fp);
+ if (!fp)
+ /* Chardev file descriptor was or is being released. */
return NOTIFY_DONE;
/*
@@ -2575,8 +2558,6 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
/* Keep the GPIO device alive until we emit the event. */
ctx->gdev = gpio_device_get(desc->gdev);
ctx->cdev = cdev;
- /* Keep the file descriptor alive too. */
- get_file(ctx->cdev->fp);
INIT_WORK(&ctx->work, lineinfo_changed_func);
queue_work(ctx->gdev->line_state_wq, &ctx->work);
@@ -2823,7 +2804,7 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt)
if (!gc)
return -ENODEV;
- chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
+ gpiochip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id);
return 0;
}
diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c
index 3bc93ccadb5b..ef3f2ef30cf2 100644
--- a/drivers/gpio/gpiolib-legacy.c
+++ b/drivers/gpio/gpiolib-legacy.c
@@ -34,30 +34,20 @@ EXPORT_SYMBOL_GPL(gpio_free);
*/
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label)
{
- struct gpio_desc *desc;
int err;
- /* Compatibility: assume unavailable "valid" GPIOs will appear later */
- desc = gpio_to_desc(gpio);
- if (!desc)
- return -EPROBE_DEFER;
-
- err = gpiod_request(desc, label);
+ err = gpio_request(gpio, label);
if (err)
return err;
if (flags & GPIOF_IN)
- err = gpiod_direction_input(desc);
+ err = gpio_direction_input(gpio);
else
- err = gpiod_direction_output_raw(desc, !!(flags & GPIOF_OUT_INIT_HIGH));
+ err = gpio_direction_output(gpio, !!(flags & GPIOF_OUT_INIT_HIGH));
if (err)
- goto free_gpio;
-
- return 0;
+ gpio_free(gpio);
- free_gpio:
- gpiod_free(desc);
return err;
}
EXPORT_SYMBOL_GPL(gpio_request_one);
@@ -78,11 +68,9 @@ int gpio_request(unsigned gpio, const char *label)
}
EXPORT_SYMBOL_GPL(gpio_request);
-static void devm_gpio_release(struct device *dev, void *res)
+static void devm_gpio_release(void *gpio)
{
- unsigned *gpio = res;
-
- gpio_free(*gpio);
+ gpio_free((unsigned)(unsigned long)gpio);
}
/**
@@ -100,22 +88,22 @@ static void devm_gpio_release(struct device *dev, void *res)
int devm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags, const char *label)
{
- unsigned *dr;
int rc;
- dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);
- if (!dr)
- return -ENOMEM;
+ rc = gpio_request(gpio, label);
+ if (rc)
+ return rc;
+
+ if (flags & GPIOF_IN)
+ rc = gpio_direction_input(gpio);
+ else
+ rc = gpio_direction_output(gpio, !!(flags & GPIOF_OUT_INIT_HIGH));
- rc = gpio_request_one(gpio, flags, label);
if (rc) {
- devres_free(dr);
+ gpio_free(gpio);
return rc;
}
- *dr = gpio;
- devres_add(dev, dr);
-
- return 0;
+ return devm_add_action_or_reset(dev, devm_gpio_release, (void *)(unsigned long)gpio);
}
EXPORT_SYMBOL_GPL(devm_gpio_request_one);
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index fad4edf9cc5c..8657379e9165 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -1031,85 +1031,6 @@ static int of_gpio_threecell_xlate(struct gpio_chip *gc,
return gpiospec->args[1];
}
-#if IS_ENABLED(CONFIG_OF_GPIO_MM_GPIOCHIP)
-#include <linux/gpio/legacy-of-mm-gpiochip.h>
-/**
- * of_mm_gpiochip_add_data - Add memory mapped GPIO chip (bank)
- * @np: device node of the GPIO chip
- * @mm_gc: pointer to the of_mm_gpio_chip allocated structure
- * @data: driver data to store in the struct gpio_chip
- *
- * To use this function you should allocate and fill mm_gc with:
- *
- * 1) In the gpio_chip structure:
- * - all the callbacks
- * - of_gpio_n_cells
- * - of_xlate callback (optional)
- *
- * 3) In the of_mm_gpio_chip structure:
- * - save_regs callback (optional)
- *
- * If succeeded, this function will map bank's memory and will
- * do all necessary work for you. Then you'll able to use .regs
- * to manage GPIOs from the callbacks.
- *
- * Returns:
- * 0 on success, or negative errno on failure.
- */
-int of_mm_gpiochip_add_data(struct device_node *np,
- struct of_mm_gpio_chip *mm_gc,
- void *data)
-{
- int ret = -ENOMEM;
- struct gpio_chip *gc = &mm_gc->gc;
-
- gc->label = kasprintf(GFP_KERNEL, "%pOF", np);
- if (!gc->label)
- goto err0;
-
- mm_gc->regs = of_iomap(np, 0);
- if (!mm_gc->regs)
- goto err1;
-
- gc->base = -1;
-
- if (mm_gc->save_regs)
- mm_gc->save_regs(mm_gc);
-
- fwnode_handle_put(mm_gc->gc.fwnode);
- mm_gc->gc.fwnode = fwnode_handle_get(of_fwnode_handle(np));
-
- ret = gpiochip_add_data(gc, data);
- if (ret)
- goto err2;
-
- return 0;
-err2:
- of_node_put(np);
- iounmap(mm_gc->regs);
-err1:
- kfree(gc->label);
-err0:
- pr_err("%pOF: GPIO chip registration failed with status %d\n", np, ret);
- return ret;
-}
-EXPORT_SYMBOL_GPL(of_mm_gpiochip_add_data);
-
-/**
- * of_mm_gpiochip_remove - Remove memory mapped GPIO chip (bank)
- * @mm_gc: pointer to the of_mm_gpio_chip allocated structure
- */
-void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
-{
- struct gpio_chip *gc = &mm_gc->gc;
-
- gpiochip_remove(gc);
- iounmap(mm_gc->regs);
- kfree(gc->label);
-}
-EXPORT_SYMBOL_GPL(of_mm_gpiochip_remove);
-#endif
-
#ifdef CONFIG_PINCTRL
static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
{
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
new file mode 100644
index 000000000000..8bdd107b1ad1
--- /dev/null
+++ b/drivers/gpio/gpiolib-shared.c
@@ -0,0 +1,656 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/auxiliary_bus.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
+#include <linux/idr.h>
+#include <linux/kref.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/overflow.h>
+#include <linux/printk.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "gpiolib.h"
+#include "gpiolib-shared.h"
+
+/* Represents a single reference to a GPIO pin. */
+struct gpio_shared_ref {
+ struct list_head list;
+ /* Firmware node associated with this GPIO's consumer. */
+ struct fwnode_handle *fwnode;
+ /* GPIO flags this consumer uses for the request. */
+ enum gpiod_flags flags;
+ char *con_id;
+ int dev_id;
+ struct auxiliary_device adev;
+ struct gpiod_lookup_table *lookup;
+};
+
+/* Represents a single GPIO pin. */
+struct gpio_shared_entry {
+ struct list_head list;
+ /* Firmware node associated with the GPIO controller. */
+ struct fwnode_handle *fwnode;
+ /* Hardware offset of the GPIO within its chip. */
+ unsigned int offset;
+ /* Index in the property value array. */
+ size_t index;
+ struct mutex lock;
+ struct gpio_shared_desc *shared_desc;
+ struct kref ref;
+ struct list_head refs;
+};
+
+static LIST_HEAD(gpio_shared_list);
+static DEFINE_MUTEX(gpio_shared_lock);
+static DEFINE_IDA(gpio_shared_ida);
+
+#if IS_ENABLED(CONFIG_OF)
+static struct gpio_shared_entry *
+gpio_shared_find_entry(struct fwnode_handle *controller_node,
+ unsigned int offset)
+{
+ struct gpio_shared_entry *entry;
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ if (entry->fwnode == controller_node && entry->offset == offset)
+ return entry;
+ }
+
+ return NULL;
+}
+
+/* Handle all special nodes that we should ignore. */
+static bool gpio_shared_of_node_ignore(struct device_node *node)
+{
+ /*
+ * __symbols__ is a special, internal node and should not be considered
+ * when scanning for shared GPIOs.
+ */
+ if (of_node_name_eq(node, "__symbols__"))
+ return true;
+
+ /*
+ * GPIO hogs have a "gpios" property which is not a phandle and can't
+ * possibly refer to a shared GPIO.
+ */
+ if (of_property_present(node, "gpio-hog"))
+ return true;
+
+ return false;
+}
+
+static int gpio_shared_of_traverse(struct device_node *curr)
+{
+ struct gpio_shared_entry *entry;
+ size_t con_id_len, suffix_len;
+ struct fwnode_handle *fwnode;
+ struct of_phandle_args args;
+ struct property *prop;
+ unsigned int offset;
+ const char *suffix;
+ int ret, count, i;
+
+ if (gpio_shared_of_node_ignore(curr))
+ return 0;
+
+ for_each_property_of_node(curr, prop) {
+ /*
+ * The standard name for a GPIO property is "foo-gpios"
+ * or "foo-gpio". Some bindings also use "gpios" or "gpio".
+ * There are some legacy device-trees which have a different
+ * naming convention and for which we have rename quirks in
+ * place in gpiolib-of.c. I don't think any of them require
+ * support for shared GPIOs so for now let's just ignore
+ * them. We can always just export the quirk list and
+ * iterate over it here.
+ */
+ if (!strends(prop->name, "-gpios") &&
+ !strends(prop->name, "-gpio") &&
+ strcmp(prop->name, "gpios") != 0 &&
+ strcmp(prop->name, "gpio") != 0)
+ continue;
+
+ count = of_count_phandle_with_args(curr, prop->name,
+ "#gpio-cells");
+ if (count <= 0)
+ continue;
+
+ for (i = 0; i < count; i++) {
+ struct device_node *np __free(device_node) = NULL;
+
+ ret = of_parse_phandle_with_args(curr, prop->name,
+ "#gpio-cells", i,
+ &args);
+ if (ret)
+ continue;
+
+ np = args.np;
+
+ if (!of_property_present(np, "gpio-controller"))
+ continue;
+
+ /*
+ * We support 1, 2 and 3 cell GPIO bindings in the
+ * kernel currently. There's only one old MIPS dts that
+ * has a one-cell binding but there's no associated
+ * consumer so it may as well be an error. There don't
+ * seem to be any 3-cell users of non-exclusive GPIOs,
+ * so we can skip this as well. Let's occupy ourselves
+ * with the predominant 2-cell binding with the first
+ * cell indicating the hardware offset of the GPIO and
+ * the second defining the GPIO flags of the request.
+ */
+ if (args.args_count != 2)
+ continue;
+
+ fwnode = of_fwnode_handle(args.np);
+ offset = args.args[0];
+
+ entry = gpio_shared_find_entry(fwnode, offset);
+ if (!entry) {
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->fwnode = fwnode_handle_get(fwnode);
+ entry->offset = offset;
+ entry->index = count;
+ INIT_LIST_HEAD(&entry->refs);
+ mutex_init(&entry->lock);
+
+ list_add_tail(&entry->list, &gpio_shared_list);
+ }
+
+ struct gpio_shared_ref *ref __free(kfree) =
+ kzalloc(sizeof(*ref), GFP_KERNEL);
+ if (!ref)
+ return -ENOMEM;
+
+ ref->fwnode = fwnode_handle_get(of_fwnode_handle(curr));
+ ref->flags = args.args[1];
+
+ if (strends(prop->name, "gpios"))
+ suffix = "-gpios";
+ else if (strends(prop->name, "gpio"))
+ suffix = "-gpio";
+ else
+ suffix = NULL;
+ if (!suffix)
+ continue;
+
+ /* We only set con_id if there's actually one. */
+ if (strcmp(prop->name, "gpios") && strcmp(prop->name, "gpio")) {
+ ref->con_id = kstrdup(prop->name, GFP_KERNEL);
+ if (!ref->con_id)
+ return -ENOMEM;
+
+ con_id_len = strlen(ref->con_id);
+ suffix_len = strlen(suffix);
+
+ ref->con_id[con_id_len - suffix_len] = '\0';
+ }
+
+ ref->dev_id = ida_alloc(&gpio_shared_ida, GFP_KERNEL);
+ if (ref->dev_id < 0) {
+ kfree(ref->con_id);
+ return -ENOMEM;
+ }
+
+ if (!list_empty(&entry->refs))
+ pr_debug("GPIO %u at %s is shared by multiple firmware nodes\n",
+ entry->offset, fwnode_get_name(entry->fwnode));
+
+ list_add_tail(&no_free_ptr(ref)->list, &entry->refs);
+ }
+ }
+
+ for_each_child_of_node_scoped(curr, child) {
+ ret = gpio_shared_of_traverse(child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gpio_shared_of_scan(void)
+{
+ if (of_root)
+ return gpio_shared_of_traverse(of_root);
+
+ return 0;
+}
+#else
+static int gpio_shared_of_scan(void)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
+static void gpio_shared_adev_release(struct device *dev)
+{
+
+}
+
+static int gpio_shared_make_adev(struct gpio_device *gdev,
+ struct gpio_shared_entry *entry,
+ struct gpio_shared_ref *ref)
+{
+ struct auxiliary_device *adev = &ref->adev;
+ int ret;
+
+ lockdep_assert_held(&gpio_shared_lock);
+
+ memset(adev, 0, sizeof(*adev));
+
+ adev->id = ref->dev_id;
+ adev->name = "proxy";
+ adev->dev.parent = gdev->dev.parent;
+ adev->dev.platform_data = entry;
+ adev->dev.release = gpio_shared_adev_release;
+
+ ret = auxiliary_device_init(adev);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_device_add(adev);
+ if (ret) {
+ auxiliary_device_uninit(adev);
+ return ret;
+ }
+
+ pr_debug("Created an auxiliary GPIO proxy %s for GPIO device %s\n",
+ dev_name(&adev->dev), gpio_device_get_label(gdev));
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_RESET_GPIO)
+/*
+ * Special case: reset-gpio is an auxiliary device that's created dynamically
+ * and put in between the GPIO controller and consumers of shared GPIOs
+ * referred to by the "reset-gpios" property.
+ *
+ * If the supposed consumer of a shared GPIO didn't match any of the mappings
+ * we created when scanning the firmware nodes, it's still possible that it's
+ * the reset-gpio device which didn't exist at the time of the scan.
+ *
+ * This function verifies it an return true if it's the case.
+ */
+static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
+ struct gpio_shared_entry *entry,
+ struct gpio_shared_ref *ref)
+{
+ struct fwnode_handle *reset_fwnode = dev_fwnode(consumer);
+ struct fwnode_reference_args ref_args, aux_args;
+ struct device *parent = consumer->parent;
+ bool match;
+ int ret;
+
+ /* The reset-gpio device must have a parent AND a firmware node. */
+ if (!parent || !reset_fwnode)
+ return false;
+
+ /*
+ * FIXME: use device_is_compatible() once the reset-gpio drivers gains
+ * a compatible string which it currently does not have.
+ */
+ if (!strstarts(dev_name(consumer), "reset.gpio."))
+ return false;
+
+ /*
+ * Parent of the reset-gpio auxiliary device is the GPIO chip whose
+ * fwnode we stored in the entry structure.
+ */
+ if (!device_match_fwnode(parent, entry->fwnode))
+ return false;
+
+ /*
+ * The device associated with the shared reference's firmware node is
+ * the consumer of the reset control exposed by the reset-gpio device.
+ * It must have a "reset-gpios" property that's referencing the entry's
+ * firmware node.
+ *
+ * The reference args must agree between the real consumer and the
+ * auxiliary reset-gpio device.
+ */
+ ret = fwnode_property_get_reference_args(ref->fwnode, "reset-gpios",
+ NULL, 2, 0, &ref_args);
+ if (ret)
+ return false;
+
+ ret = fwnode_property_get_reference_args(reset_fwnode, "reset-gpios",
+ NULL, 2, 0, &aux_args);
+ if (ret) {
+ fwnode_handle_put(ref_args.fwnode);
+ return false;
+ }
+
+ match = ((ref_args.fwnode == entry->fwnode) &&
+ (aux_args.fwnode == entry->fwnode) &&
+ (ref_args.args[0] == aux_args.args[0]));
+
+ fwnode_handle_put(ref_args.fwnode);
+ fwnode_handle_put(aux_args.fwnode);
+ return match;
+}
+#else
+static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
+ struct gpio_shared_entry *entry,
+ struct gpio_shared_ref *ref)
+{
+ return false;
+}
+#endif /* CONFIG_RESET_GPIO */
+
+int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags)
+{
+ const char *dev_id = dev_name(consumer);
+ struct gpio_shared_entry *entry;
+ struct gpio_shared_ref *ref;
+
+ struct gpiod_lookup_table *lookup __free(kfree) =
+ kzalloc(struct_size(lookup, table, 2), GFP_KERNEL);
+ if (!lookup)
+ return -ENOMEM;
+
+ guard(mutex)(&gpio_shared_lock);
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ list_for_each_entry(ref, &entry->refs, list) {
+ if (!device_match_fwnode(consumer, ref->fwnode) &&
+ !gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
+ continue;
+
+ /* We've already done that on a previous request. */
+ if (ref->lookup)
+ return 0;
+
+ char *key __free(kfree) =
+ kasprintf(GFP_KERNEL,
+ KBUILD_MODNAME ".proxy.%u",
+ ref->adev.id);
+ if (!key)
+ return -ENOMEM;
+
+ pr_debug("Adding machine lookup entry for a shared GPIO for consumer %s, with key '%s' and con_id '%s'\n",
+ dev_id, key, ref->con_id ?: "none");
+
+ lookup->dev_id = dev_id;
+ lookup->table[0] = GPIO_LOOKUP(no_free_ptr(key), 0,
+ ref->con_id, lflags);
+
+ gpiod_add_lookup_table(no_free_ptr(lookup));
+
+ return 0;
+ }
+ }
+
+ /* We warn here because this can only happen if the programmer borked. */
+ WARN_ON(1);
+ return -ENOENT;
+}
+
+static void gpio_shared_remove_adev(struct auxiliary_device *adev)
+{
+ lockdep_assert_held(&gpio_shared_lock);
+
+ auxiliary_device_uninit(adev);
+ auxiliary_device_delete(adev);
+}
+
+int gpio_device_setup_shared(struct gpio_device *gdev)
+{
+ struct gpio_shared_entry *entry;
+ struct gpio_shared_ref *ref;
+ unsigned long *flags;
+ int ret;
+
+ guard(mutex)(&gpio_shared_lock);
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ list_for_each_entry(ref, &entry->refs, list) {
+ if (gdev->dev.parent == &ref->adev.dev) {
+ /*
+ * This is a shared GPIO proxy. Mark its
+ * descriptor as such and return here.
+ */
+ __set_bit(GPIOD_FLAG_SHARED_PROXY,
+ &gdev->descs[0].flags);
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * This is not a shared GPIO proxy but it still may be the device
+ * exposing shared pins. Find them and create the proxy devices.
+ */
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ if (!device_match_fwnode(&gdev->dev, entry->fwnode))
+ continue;
+
+ if (list_count_nodes(&entry->refs) <= 1)
+ continue;
+
+ flags = &gdev->descs[entry->offset].flags;
+
+ __set_bit(GPIOD_FLAG_SHARED, flags);
+ /*
+ * Shared GPIOs are not requested via the normal path. Make
+ * them inaccessible to anyone even before we register the
+ * chip.
+ */
+ __set_bit(GPIOD_FLAG_REQUESTED, flags);
+
+ pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
+ entry->offset, gpio_device_get_label(gdev));
+
+ list_for_each_entry(ref, &entry->refs, list) {
+ pr_debug("Setting up a shared GPIO entry for %s\n",
+ fwnode_get_name(ref->fwnode));
+
+ ret = gpio_shared_make_adev(gdev, entry, ref);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void gpio_device_teardown_shared(struct gpio_device *gdev)
+{
+ struct gpio_shared_entry *entry;
+ struct gpio_shared_ref *ref;
+
+ guard(mutex)(&gpio_shared_lock);
+
+ list_for_each_entry(entry, &gpio_shared_list, list) {
+ if (!device_match_fwnode(&gdev->dev, entry->fwnode))
+ continue;
+
+ list_for_each_entry(ref, &entry->refs, list) {
+ gpiod_remove_lookup_table(ref->lookup);
+ kfree(ref->lookup->table[0].key);
+ kfree(ref->lookup);
+ ref->lookup = NULL;
+ gpio_shared_remove_adev(&ref->adev);
+ }
+ }
+}
+
+static void gpio_shared_release(struct kref *kref)
+{
+ struct gpio_shared_entry *entry =
+ container_of(kref, struct gpio_shared_entry, ref);
+ struct gpio_shared_desc *shared_desc;
+
+ guard(mutex)(&entry->lock);
+
+ shared_desc = entry->shared_desc;
+ gpio_device_put(shared_desc->desc->gdev);
+ if (shared_desc->can_sleep)
+ mutex_destroy(&shared_desc->mutex);
+ kfree(shared_desc);
+ entry->shared_desc = NULL;
+}
+
+static void gpiod_shared_put(void *data)
+{
+ struct gpio_shared_entry *entry = data;
+
+ lockdep_assert_not_held(&gpio_shared_lock);
+
+ kref_put(&entry->ref, gpio_shared_release);
+}
+
+static struct gpio_shared_desc *
+gpiod_shared_desc_create(struct gpio_shared_entry *entry)
+{
+ struct gpio_shared_desc *shared_desc;
+ struct gpio_device *gdev;
+
+ lockdep_assert_held(&entry->lock);
+
+ shared_desc = kzalloc(sizeof(*shared_desc), GFP_KERNEL);
+ if (!shared_desc)
+ return ERR_PTR(-ENOMEM);
+
+ gdev = gpio_device_find_by_fwnode(entry->fwnode);
+ if (!gdev) {
+ kfree(shared_desc);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ shared_desc->desc = &gdev->descs[entry->offset];
+ shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
+ if (shared_desc->can_sleep)
+ mutex_init(&shared_desc->mutex);
+ else
+ spin_lock_init(&shared_desc->spinlock);
+
+ return shared_desc;
+}
+
+struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev)
+{
+ struct gpio_shared_desc *shared_desc;
+ struct gpio_shared_entry *entry;
+ int ret;
+
+ lockdep_assert_not_held(&gpio_shared_lock);
+
+ entry = dev_get_platdata(dev);
+ if (WARN_ON(!entry))
+ /* Programmer bug */
+ return ERR_PTR(-ENOENT);
+
+ scoped_guard(mutex, &entry->lock) {
+ if (entry->shared_desc) {
+ kref_get(&entry->ref);
+ shared_desc = entry->shared_desc;
+ } else {
+ shared_desc = gpiod_shared_desc_create(entry);
+ if (IS_ERR(shared_desc))
+ return ERR_CAST(shared_desc);
+
+ kref_init(&entry->ref);
+ entry->shared_desc = shared_desc;
+ }
+
+ pr_debug("Device %s acquired a reference to the shared GPIO %u owned by %s\n",
+ dev_name(dev), gpiod_hwgpio(shared_desc->desc),
+ gpio_device_get_label(shared_desc->desc->gdev));
+ }
+
+ ret = devm_add_action_or_reset(dev, gpiod_shared_put, entry);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return shared_desc;
+}
+EXPORT_SYMBOL_GPL(devm_gpiod_shared_get);
+
+static void gpio_shared_drop_ref(struct gpio_shared_ref *ref)
+{
+ list_del(&ref->list);
+ kfree(ref->con_id);
+ ida_free(&gpio_shared_ida, ref->dev_id);
+ fwnode_handle_put(ref->fwnode);
+ kfree(ref);
+}
+
+static void gpio_shared_drop_entry(struct gpio_shared_entry *entry)
+{
+ list_del(&entry->list);
+ mutex_destroy(&entry->lock);
+ fwnode_handle_put(entry->fwnode);
+ kfree(entry);
+}
+
+/*
+ * This is only called if gpio_shared_init() fails so it's in fact __init and
+ * not __exit.
+ */
+static void __init gpio_shared_teardown(void)
+{
+ struct gpio_shared_entry *entry, *epos;
+ struct gpio_shared_ref *ref, *rpos;
+
+ list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) {
+ list_for_each_entry_safe(ref, rpos, &entry->refs, list)
+ gpio_shared_drop_ref(ref);
+
+ gpio_shared_drop_entry(entry);
+ }
+}
+
+static void gpio_shared_free_exclusive(void)
+{
+ struct gpio_shared_entry *entry, *epos;
+
+ list_for_each_entry_safe(entry, epos, &gpio_shared_list, list) {
+ if (list_count_nodes(&entry->refs) > 1)
+ continue;
+
+ gpio_shared_drop_ref(list_first_entry(&entry->refs,
+ struct gpio_shared_ref,
+ list));
+ gpio_shared_drop_entry(entry);
+ }
+}
+
+static int __init gpio_shared_init(void)
+{
+ int ret;
+
+ /* Right now, we only support OF-based systems. */
+ ret = gpio_shared_of_scan();
+ if (ret) {
+ gpio_shared_teardown();
+ pr_err("Failed to scan OF nodes for shared GPIOs: %d\n", ret);
+ return ret;
+ }
+
+ gpio_shared_free_exclusive();
+
+ pr_debug("Finished scanning firmware nodes for shared GPIOs\n");
+ return 0;
+}
+postcore_initcall(gpio_shared_init);
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
new file mode 100644
index 000000000000..667dbdff3585
--- /dev/null
+++ b/drivers/gpio/gpiolib-shared.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_GPIO_SHARED_H
+#define __LINUX_GPIO_SHARED_H
+
+#include <linux/cleanup.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+struct gpio_device;
+struct gpio_desc;
+struct device;
+
+#if IS_ENABLED(CONFIG_GPIO_SHARED)
+
+int gpio_device_setup_shared(struct gpio_device *gdev);
+void gpio_device_teardown_shared(struct gpio_device *gdev);
+int gpio_shared_add_proxy_lookup(struct device *consumer, unsigned long lflags);
+
+#else
+
+static inline int gpio_device_setup_shared(struct gpio_device *gdev)
+{
+ return 0;
+}
+
+static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { }
+
+static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
+ unsigned long lflags)
+{
+ return 0;
+}
+
+#endif /* CONFIG_GPIO_SHARED */
+
+struct gpio_shared_desc {
+ struct gpio_desc *desc;
+ bool can_sleep;
+ unsigned long cfg;
+ unsigned int usecnt;
+ unsigned int highcnt;
+ union {
+ struct mutex mutex;
+ spinlock_t spinlock;
+ };
+};
+
+struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev);
+
+DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc,
+ if (_T->lock->can_sleep)
+ mutex_lock(&_T->lock->mutex);
+ else
+ spin_lock_irqsave(&_T->lock->spinlock, _T->flags),
+ if (_T->lock->can_sleep)
+ mutex_unlock(&_T->lock->mutex);
+ else
+ spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags),
+ unsigned long flags)
+
+static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc)
+{
+ if (shared_desc->can_sleep)
+ lockdep_assert_held(&shared_desc->mutex);
+ else
+ lockdep_assert_held(&shared_desc->spinlock);
+}
+
+#endif /* __LINUX_GPIO_SHARED_H */
diff --git a/drivers/gpio/gpiolib-swnode.c b/drivers/gpio/gpiolib-swnode.c
index f21dbc28cf2c..b44f35d68459 100644
--- a/drivers/gpio/gpiolib-swnode.c
+++ b/drivers/gpio/gpiolib-swnode.c
@@ -31,7 +31,7 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
gdev_node = to_software_node(fwnode);
if (!gdev_node || !gdev_node->name)
- return ERR_PTR(-EINVAL);
+ goto fwnode_lookup;
/*
* Check for a special node that identifies undefined GPIOs, this is
@@ -41,7 +41,8 @@ static struct gpio_device *swnode_get_gpio_device(struct fwnode_handle *fwnode)
!strcmp(gdev_node->name, GPIOLIB_SWNODE_UNDEFINED_NAME))
return ERR_PTR(-ENOENT);
- gdev = gpio_device_find_by_label(gdev_node->name);
+fwnode_lookup:
+ gdev = gpio_device_find_by_fwnode(fwnode);
return gdev ?: ERR_PTR(-EPROBE_DEFER);
}
diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c
index 9a849245b358..cd553acf3055 100644
--- a/drivers/gpio/gpiolib-sysfs.c
+++ b/drivers/gpio/gpiolib-sysfs.c
@@ -244,7 +244,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
* Remove this redundant call (along with the corresponding unlock)
* when those drivers have been fixed.
*/
- ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
+ ret = gpiochip_lock_as_irq(guard.gc, gpiod_hwgpio(desc));
if (ret < 0)
goto err_clr_bits;
@@ -258,7 +258,7 @@ static int gpio_sysfs_request_irq(struct gpiod_data *data, unsigned char flags)
return 0;
err_unlock:
- gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
+ gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc));
err_clr_bits:
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
@@ -280,7 +280,7 @@ static void gpio_sysfs_free_irq(struct gpiod_data *data)
data->irq_flags = 0;
free_irq(data->irq, data);
- gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc));
+ gpiochip_unlock_as_irq(guard.gc, gpiod_hwgpio(desc));
clear_bit(GPIOD_FLAG_EDGE_RISING, &desc->flags);
clear_bit(GPIOD_FLAG_EDGE_FALLING, &desc->flags);
}
@@ -478,10 +478,10 @@ static int export_gpio_desc(struct gpio_desc *desc)
if (!guard.gc)
return -ENODEV;
- offset = gpio_chip_hwgpio(desc);
+ offset = gpiod_hwgpio(desc);
if (!gpiochip_line_is_valid(guard.gc, offset)) {
pr_debug_ratelimited("%s: GPIO %d masked\n", __func__,
- gpio_chip_hwgpio(desc));
+ gpiod_hwgpio(desc));
return -EINVAL;
}
@@ -823,7 +823,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
}
desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u",
- gpio_chip_hwgpio(desc));
+ gpiod_hwgpio(desc));
if (!desc_data->chip_attr_group.name) {
status = -ENOMEM;
goto err_put_dirent;
@@ -843,7 +843,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
if (status)
goto err_free_name;
- path = kasprintf(GFP_KERNEL, "gpio%u/value", gpio_chip_hwgpio(desc));
+ path = kasprintf(GFP_KERNEL, "gpio%u/value", gpiod_hwgpio(desc));
if (!path) {
status = -ENOMEM;
goto err_remove_groups;
@@ -1091,7 +1091,7 @@ static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data)
ret = gpiochip_sysfs_register(gdev);
if (ret)
- chip_err(gc, "failed to register the sysfs entry: %d\n", ret);
+ gpiochip_err(gc, "failed to register the sysfs entry: %d\n", ret);
return 0;
}
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 9952e412da50..91e0c384f34a 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -37,6 +37,7 @@
#include "gpiolib-acpi.h"
#include "gpiolib-cdev.h"
#include "gpiolib-of.h"
+#include "gpiolib-shared.h"
#include "gpiolib-swnode.h"
#include "gpiolib-sysfs.h"
#include "gpiolib.h"
@@ -235,6 +236,19 @@ int desc_to_gpio(const struct gpio_desc *desc)
}
EXPORT_SYMBOL_GPL(desc_to_gpio);
+/**
+ * gpiod_hwgpio - Return the GPIO number of the passed descriptor relative to
+ * its chip.
+ * @desc: GPIO descriptor
+ *
+ * Returns:
+ * Hardware offset of the GPIO represented by the descriptor.
+ */
+int gpiod_hwgpio(const struct gpio_desc *desc)
+{
+ return desc - &desc->gdev->descs[0];
+}
+EXPORT_SYMBOL_GPL(gpiod_hwgpio);
/**
* gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs
@@ -443,7 +457,7 @@ int gpiod_get_direction(struct gpio_desc *desc)
if (!guard.gc)
return -ENODEV;
- offset = gpio_chip_hwgpio(desc);
+ offset = gpiod_hwgpio(desc);
flags = READ_ONCE(desc->flags);
/*
@@ -921,8 +935,8 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog)
desc = gpiochip_get_desc(gc, hog->chip_hwnum);
if (IS_ERR(desc)) {
- chip_err(gc, "%s: unable to get GPIO desc: %ld\n", __func__,
- PTR_ERR(desc));
+ gpiochip_err(gc, "%s: unable to get GPIO desc: %ld\n",
+ __func__, PTR_ERR(desc));
return;
}
@@ -1124,7 +1138,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiodev_add_to_list_unlocked(gdev);
if (ret) {
- chip_err(gc, "GPIO integer space overlap, cannot add chip\n");
+ gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n");
goto err_free_label;
}
}
@@ -1200,6 +1214,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret)
goto err_remove_irqchip_mask;
+ ret = gpio_device_setup_shared(gdev);
+ if (ret)
+ goto err_remove_irqchip;
+
/*
* By first adding the chardev, and then adding the device,
* we get a device node entry in sysfs under
@@ -1211,10 +1229,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (gpiolib_initialized) {
ret = gpiochip_setup_dev(gdev);
if (ret)
- goto err_remove_irqchip;
+ goto err_teardown_shared;
}
+
return 0;
+err_teardown_shared:
+ gpio_device_teardown_shared(gdev);
err_remove_irqchip:
gpiochip_irqchip_remove(gc);
err_remove_irqchip_mask:
@@ -1283,6 +1304,7 @@ void gpiochip_remove(struct gpio_chip *gc)
/* Numb the device, cancelling all outstanding operations */
rcu_assign_pointer(gdev->chip, NULL);
synchronize_srcu(&gdev->srcu);
+ gpio_device_teardown_shared(gdev);
gpiochip_irqchip_remove(gc);
acpi_gpiochip_remove(gc);
of_gpiochip_remove(gc);
@@ -1528,8 +1550,7 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,
&parent_hwirq,
&parent_type);
if (ret) {
- chip_err(gc, "skip set-up on hwirq %d\n",
- i);
+ gpiochip_err(gc, "skip set-up on hwirq %d\n", i);
continue;
}
@@ -1542,15 +1563,14 @@ static void gpiochip_set_hierarchical_irqchip(struct gpio_chip *gc,
ret = irq_domain_alloc_irqs(gc->irq.domain, 1,
NUMA_NO_NODE, &fwspec);
if (ret < 0) {
- chip_err(gc,
- "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n",
- i, parent_hwirq,
- ret);
+ gpiochip_err(gc,
+ "can not allocate irq for GPIO line %d parent hwirq %d in hierarchy domain: %d\n",
+ i, parent_hwirq, ret);
}
}
}
- chip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__);
+ gpiochip_err(gc, "%s unknown fwnode type proceed anyway\n", __func__);
return;
}
@@ -1602,15 +1622,15 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
if (ret)
return ret;
- chip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq);
+ gpiochip_dbg(gc, "allocate IRQ %d, hwirq %lu\n", irq, hwirq);
ret = girq->child_to_parent_hwirq(gc, hwirq, type,
&parent_hwirq, &parent_type);
if (ret) {
- chip_err(gc, "can't look up hwirq %lu\n", hwirq);
+ gpiochip_err(gc, "can't look up hwirq %lu\n", hwirq);
return ret;
}
- chip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
+ gpiochip_dbg(gc, "found parent hwirq %u\n", parent_hwirq);
/*
* We set handle_bad_irq because the .set_type() should
@@ -1631,8 +1651,8 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
if (ret)
return ret;
- chip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
- irq, parent_hwirq);
+ gpiochip_dbg(gc, "alloc_irqs_parent for %d parent hwirq %d\n",
+ irq, parent_hwirq);
irq_set_lockdep_class(irq, gc->irq.lock_key, gc->irq.request_key);
ret = irq_domain_alloc_irqs_parent(d, irq, 1, &gpio_parent_fwspec);
/*
@@ -1642,9 +1662,9 @@ static int gpiochip_hierarchy_irq_domain_alloc(struct irq_domain *d,
if (irq_domain_is_msi(d->parent) && (ret == -EEXIST))
ret = 0;
if (ret)
- chip_err(gc,
- "failed to allocate parent hwirq %d for hwirq %lu\n",
- parent_hwirq, hwirq);
+ gpiochip_err(gc,
+ "failed to allocate parent hwirq %d for hwirq %lu\n",
+ parent_hwirq, hwirq);
return ret;
}
@@ -1720,7 +1740,7 @@ static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
if (!gc->irq.child_to_parent_hwirq ||
!gc->irq.fwnode) {
- chip_err(gc, "missing irqdomain vital data\n");
+ gpiochip_err(gc, "missing irqdomain vital data\n");
return ERR_PTR(-EINVAL);
}
@@ -1993,7 +2013,7 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
if (irqchip->flags & IRQCHIP_IMMUTABLE)
return;
- chip_warn(gc, "not an immutable chip, please consider fixing it!\n");
+ gpiochip_warn(gc, "not an immutable chip, please consider fixing it!\n");
if (!irqchip->irq_request_resources &&
!irqchip->irq_release_resources) {
@@ -2009,8 +2029,8 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc)
* ...and if so, give a gentle warning that this is bad
* practice.
*/
- chip_info(gc,
- "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
+ gpiochip_info(gc,
+ "detected irqchip that is shared with multiple gpiochips: please fix the driver.\n");
return;
}
@@ -2039,7 +2059,8 @@ static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc,
return -EINVAL;
if (gc->to_irq)
- chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
+ gpiochip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n",
+ __func__);
gc->to_irq = gpiochip_to_irq;
gc->irq.domain = domain;
@@ -2080,7 +2101,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gc,
return 0;
if (gc->irq.parent_handler && gc->can_sleep) {
- chip_err(gc, "you cannot have chained interrupts on a chip that may sleep\n");
+ gpiochip_err(gc, "you cannot have chained interrupts on a chip that may sleep\n");
return -EINVAL;
}
@@ -2316,10 +2337,8 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc,
int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
- if (!pin_range) {
- chip_err(gc, "failed to allocate pin ranges\n");
+ if (!pin_range)
return -ENOMEM;
- }
/* Use local offset as range ID */
pin_range->range.id = gpio_offset;
@@ -2338,7 +2357,7 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc,
pinctrl_add_gpio_range(pctldev, &pin_range->range);
- chip_dbg(gc, "created GPIO range %d->%d ==> %s PINGRP %s\n",
+ gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s PINGRP %s\n",
gpio_offset, gpio_offset + pin_range->range.npins - 1,
pinctrl_dev_get_devname(pctldev), pin_group);
@@ -2379,10 +2398,8 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
int ret;
pin_range = kzalloc(sizeof(*pin_range), GFP_KERNEL);
- if (!pin_range) {
- chip_err(gc, "failed to allocate pin ranges\n");
+ if (!pin_range)
return -ENOMEM;
- }
/* Use local offset as range ID */
pin_range->range.id = gpio_offset;
@@ -2396,19 +2413,18 @@ int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
&pin_range->range);
if (IS_ERR(pin_range->pctldev)) {
ret = PTR_ERR(pin_range->pctldev);
- chip_err(gc, "could not create pin range\n");
+ gpiochip_err(gc, "could not create pin range\n");
kfree(pin_range);
return ret;
}
if (pin_range->range.pins)
- chip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }",
- gpio_offset, gpio_offset + npins - 1,
- pinctl_name, npins, pins[0]);
+ gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }",
+ gpio_offset, gpio_offset + npins - 1,
+ pinctl_name, npins, pins[0]);
else
- chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
- gpio_offset, gpio_offset + npins - 1,
- pinctl_name,
- pin_offset, pin_offset + npins - 1);
+ gpiochip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
+ gpio_offset, gpio_offset + npins - 1, pinctl_name,
+ pin_offset, pin_offset + npins - 1);
list_add_tail(&pin_range->node, &gdev->pin_ranges);
@@ -2452,7 +2468,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label)
if (test_and_set_bit(GPIOD_FLAG_REQUESTED, &desc->flags))
return -EBUSY;
- offset = gpio_chip_hwgpio(desc);
+ offset = gpiod_hwgpio(desc);
if (!gpiochip_line_is_valid(guard.gc, offset))
return -EINVAL;
@@ -2514,7 +2530,7 @@ static void gpiod_free_commit(struct gpio_desc *desc)
if (guard.gc && test_bit(GPIOD_FLAG_REQUESTED, &flags)) {
if (guard.gc->free)
- guard.gc->free(guard.gc, gpio_chip_hwgpio(desc));
+ guard.gc->free(guard.gc, gpiod_hwgpio(desc));
clear_bit(GPIOD_FLAG_ACTIVE_LOW, &flags);
clear_bit(GPIOD_FLAG_REQUESTED, &flags);
@@ -2618,7 +2634,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
int ret;
if (IS_ERR(desc)) {
- chip_err(gc, "failed to get GPIO %s descriptor\n", name);
+ gpiochip_err(gc, "failed to get GPIO %s descriptor\n", name);
return desc;
}
@@ -2629,7 +2645,7 @@ struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
ret = gpiod_configure_flags(desc, label, lflags, dflags);
if (ret) {
gpiod_free_commit(desc);
- chip_err(gc, "setup of own GPIO %s failed\n", name);
+ gpiochip_err(gc, "setup of own GPIO %s failed\n", name);
return ERR_PTR(ret);
}
@@ -2674,7 +2690,7 @@ int gpio_do_set_config(struct gpio_desc *desc, unsigned long config)
if (!guard.gc->set_config)
return -ENOTSUPP;
- ret = guard.gc->set_config(guard.gc, gpio_chip_hwgpio(desc), config);
+ ret = guard.gc->set_config(guard.gc, gpiod_hwgpio(desc), config);
if (ret > 0)
ret = -EBADE;
@@ -2705,7 +2721,7 @@ static int gpio_set_config_with_argument_optional(struct gpio_desc *desc,
u32 argument)
{
struct device *dev = &desc->gdev->dev;
- int gpio = gpio_chip_hwgpio(desc);
+ int gpio = gpiod_hwgpio(desc);
int ret;
ret = gpio_set_config_with_argument(desc, mode, argument);
@@ -2868,9 +2884,9 @@ int gpiod_direction_input_nonotify(struct gpio_desc *desc)
*/
if (guard.gc->direction_input) {
ret = gpiochip_direction_input(guard.gc,
- gpio_chip_hwgpio(desc));
+ gpiod_hwgpio(desc));
} else if (guard.gc->get_direction) {
- dir = gpiochip_get_direction(guard.gc, gpio_chip_hwgpio(desc));
+ dir = gpiochip_get_direction(guard.gc, gpiod_hwgpio(desc));
if (dir < 0)
return dir;
@@ -2929,12 +2945,12 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
if (guard.gc->direction_output) {
ret = gpiochip_direction_output(guard.gc,
- gpio_chip_hwgpio(desc), val);
+ gpiod_hwgpio(desc), val);
} else {
/* Check that we are in output mode if we can */
if (guard.gc->get_direction) {
dir = gpiochip_get_direction(guard.gc,
- gpio_chip_hwgpio(desc));
+ gpiod_hwgpio(desc));
if (dir < 0)
return dir;
@@ -2949,7 +2965,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value)
* If we can't actively set the direction, we are some
* output-only chip, so just drive the output as desired.
*/
- ret = gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), val);
+ ret = gpiochip_set(guard.gc, gpiod_hwgpio(desc), val);
if (ret)
return ret;
}
@@ -3100,7 +3116,7 @@ int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
}
ret = guard.gc->en_hw_timestamp(guard.gc,
- gpio_chip_hwgpio(desc), flags);
+ gpiod_hwgpio(desc), flags);
if (ret)
gpiod_warn(desc, "%s: hw ts request failed\n", __func__);
@@ -3132,7 +3148,7 @@ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags)
return -ENOTSUPP;
}
- ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc),
+ ret = guard.gc->dis_hw_timestamp(guard.gc, gpiod_hwgpio(desc),
flags);
if (ret)
gpiod_warn(desc, "%s: hw ts release failed\n", __func__);
@@ -3263,7 +3279,7 @@ static int gpiochip_get(struct gpio_chip *gc, unsigned int offset)
static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *desc)
{
- return gc->get ? gpiochip_get(gc, gpio_chip_hwgpio(desc)) : -EIO;
+ return gc->get ? gpiochip_get(gc, gpiod_hwgpio(desc)) : -EIO;
}
/* I/O calls are only valid after configuration completed; the relevant
@@ -3423,7 +3439,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
first = i;
do {
const struct gpio_desc *desc = desc_array[i];
- int hwgpio = gpio_chip_hwgpio(desc);
+ int hwgpio = gpiod_hwgpio(desc);
__set_bit(hwgpio, mask);
i++;
@@ -3445,7 +3461,7 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
for (j = first; j < i; ) {
const struct gpio_desc *desc = desc_array[j];
- int hwgpio = gpio_chip_hwgpio(desc);
+ int hwgpio = gpiod_hwgpio(desc);
int value = test_bit(hwgpio, bits);
if (!raw && test_bit(GPIOD_FLAG_ACTIVE_LOW, &desc->flags))
@@ -3582,7 +3598,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value);
*/
static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
{
- int ret = 0, offset = gpio_chip_hwgpio(desc);
+ int ret = 0, offset = gpiod_hwgpio(desc);
CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc)
@@ -3611,7 +3627,7 @@ static int gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
*/
static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value)
{
- int ret = 0, offset = gpio_chip_hwgpio(desc);
+ int ret = 0, offset = gpiod_hwgpio(desc);
CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc)
@@ -3643,7 +3659,7 @@ static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
return -ENODEV;
trace_gpio_value(desc_to_gpio(desc), 0, value);
- return gpiochip_set(guard.gc, gpio_chip_hwgpio(desc), value);
+ return gpiochip_set(guard.gc, gpiod_hwgpio(desc), value);
}
/*
@@ -3766,7 +3782,7 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
do {
struct gpio_desc *desc = desc_array[i];
- int hwgpio = gpio_chip_hwgpio(desc);
+ int hwgpio = gpiod_hwgpio(desc);
int value = test_bit(i, value_bitmap);
if (unlikely(!test_bit(GPIOD_FLAG_IS_OUT, &desc->flags)))
@@ -3982,6 +3998,26 @@ int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name)
EXPORT_SYMBOL_GPL(gpiod_set_consumer_name);
/**
+ * gpiod_is_shared() - check if this GPIO can be shared by multiple consumers
+ * @desc: GPIO to inspect
+ *
+ * Returns:
+ * True if this GPIO can be shared by multiple consumers at once. False if it's
+ * a regular, exclusive GPIO.
+ *
+ * Note:
+ * This function returning true does not mean that this GPIO is currently being
+ * shared. It means the GPIO core has registered the fact that the firmware
+ * configuration indicates that it can be shared by multiple consumers and is
+ * in charge of arbitrating the access.
+ */
+bool gpiod_is_shared(const struct gpio_desc *desc)
+{
+ return test_bit(GPIOD_FLAG_SHARED_PROXY, &desc->flags);
+}
+EXPORT_SYMBOL_GPL(gpiod_is_shared);
+
+/**
* gpiod_to_irq() - return the IRQ corresponding to a GPIO
* @desc: gpio whose IRQ will be returned (already requested)
*
@@ -4006,7 +4042,7 @@ int gpiod_to_irq(const struct gpio_desc *desc)
if (!gc)
return -ENODEV;
- offset = gpio_chip_hwgpio(desc);
+ offset = gpiod_hwgpio(desc);
if (gc->to_irq) {
ret = gc->to_irq(gc, offset);
if (ret)
@@ -4056,8 +4092,8 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
int dir = gpiod_get_direction(desc);
if (dir < 0) {
- chip_err(gc, "%s: cannot get GPIO direction\n",
- __func__);
+ gpiochip_err(gc, "%s: cannot get GPIO direction\n",
+ __func__);
return dir;
}
}
@@ -4065,9 +4101,9 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset)
/* To be valid for IRQ the line needs to be input or open drain */
if (test_bit(GPIOD_FLAG_IS_OUT, &desc->flags) &&
!test_bit(GPIOD_FLAG_OPEN_DRAIN, &desc->flags)) {
- chip_err(gc,
- "%s: tried to flag a GPIO set as output for IRQ\n",
- __func__);
+ gpiochip_err(gc,
+ "%s: tried to flag a GPIO set as output for IRQ\n",
+ __func__);
return -EIO;
}
@@ -4144,7 +4180,7 @@ int gpiochip_reqres_irq(struct gpio_chip *gc, unsigned int offset)
ret = gpiochip_lock_as_irq(gc, offset);
if (ret) {
- chip_err(gc, "unable to lock HW IRQ %u for IRQ\n", offset);
+ gpiochip_err(gc, "unable to lock HW IRQ %u for IRQ\n", offset);
module_put(gc->gpiodev->owner);
return ret;
}
@@ -4652,11 +4688,29 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
scoped_guard(srcu, &gpio_devices_srcu) {
desc = gpiod_fwnode_lookup(fwnode, consumer, con_id, idx,
&flags, &lookupflags);
+ if (!IS_ERR_OR_NULL(desc) &&
+ test_bit(GPIOD_FLAG_SHARED, &desc->flags)) {
+ /*
+ * We're dealing with a GPIO shared by multiple
+ * consumers. This is the moment to add the machine
+ * lookup table for the proxy device as previously
+ * we only knew the consumer's fwnode.
+ */
+ ret = gpio_shared_add_proxy_lookup(consumer, lookupflags);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* Trigger platform lookup for shared GPIO proxy. */
+ desc = ERR_PTR(-ENOENT);
+ /* Trigger it even for fwnode-only gpiod_get(). */
+ platform_lookup_allowed = true;
+ }
+
if (gpiod_not_found(desc) && platform_lookup_allowed) {
/*
* Either we are not using DT or ACPI, or their lookup
- * did not return a result. In that case, use platform
- * lookup as a fallback.
+ * did not return a result or this is a shared GPIO. In
+ * that case, use platform lookup as a fallback.
*/
dev_dbg(consumer,
"using lookup tables for GPIO lookup\n");
@@ -4679,14 +4733,19 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
return ERR_PTR(ret);
/*
- * This happens when there are several consumers for
- * the same GPIO line: we just return here without
- * further initialization. It is a bit of a hack.
- * This is necessary to support fixed regulators.
+ * This happens when there are several consumers for the same
+ * GPIO line: we just return here without further
+ * initialization. It's a hack introduced long ago to support
+ * fixed regulators. We now have a better solution with
+ * automated scanning where affected platforms just need to
+ * select the provided Kconfig option.
*
- * FIXME: Make this more sane and safe.
+ * FIXME: Remove the GPIOD_FLAGS_BIT_NONEXCLUSIVE flag after
+ * making sure all platforms use the new mechanism.
*/
- dev_info(consumer, "nonexclusive access to GPIO for %s\n", name);
+ dev_info(consumer,
+ "nonexclusive access to GPIO for %s, consider updating your code to using gpio-shared-proxy\n",
+ name);
return desc;
}
@@ -4963,7 +5022,7 @@ int gpiod_hog(struct gpio_desc *desc, const char *name,
if (test_and_set_bit(GPIOD_FLAG_IS_HOGGED, &desc->flags))
return 0;
- hwnum = gpio_chip_hwgpio(desc);
+ hwnum = gpiod_hwgpio(desc);
local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name,
lflags, dflags);
@@ -5044,7 +5103,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
* If pin hardware number of array member 0 is also 0, select
* its chip as a candidate for fast bitmap processing path.
*/
- if (descs->ndescs == 0 && gpio_chip_hwgpio(desc) == 0) {
+ if (descs->ndescs == 0 && gpiod_hwgpio(desc) == 0) {
struct gpio_descs *array;
bitmap_size = BITS_TO_LONGS(gdev->ngpio > count ?
@@ -5089,7 +5148,7 @@ struct gpio_descs *__must_check gpiod_get_array(struct device *dev,
* Detect array members which belong to the 'fast' chip
* but their pins are not in hardware order.
*/
- else if (gpio_chip_hwgpio(desc) != descs->ndescs) {
+ else if (gpiod_hwgpio(desc) != descs->ndescs) {
/*
* Don't use fast path if all array members processed so
* far belong to the same chip as this one but its pin
@@ -5296,6 +5355,8 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
struct gpio_device *gdev;
loff_t index = *pos;
+ s->private = NULL;
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return NULL;
@@ -5329,7 +5390,11 @@ static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
static void gpiolib_seq_stop(struct seq_file *s, void *v)
{
- struct gpiolib_seq_priv *priv = s->private;
+ struct gpiolib_seq_priv *priv;
+
+ priv = s->private;
+ if (!priv)
+ return;
srcu_read_unlock(&gpio_devices_srcu, priv->idx);
kfree(priv);
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index 2a003a7311e7..77f6f2936dc2 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -204,6 +204,8 @@ struct gpio_desc {
#define GPIOD_FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */
#define GPIOD_FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */
#define GPIOD_FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */
+#define GPIOD_FLAG_SHARED 20 /* GPIO is shared by multiple consumers */
+#define GPIOD_FLAG_SHARED_PROXY 21 /* GPIO is a virtual proxy to a physically shared pin. */
/* Connection label */
struct gpio_desc_label __rcu *label;
@@ -273,49 +275,30 @@ int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev);
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *gc, unsigned int hwnum);
const char *gpiod_get_label(struct gpio_desc *desc);
-/*
- * Return the GPIO number of the passed descriptor relative to its chip
- */
-static inline int gpio_chip_hwgpio(const struct gpio_desc *desc)
-{
- return desc - &desc->gdev->descs[0];
-}
-
/* With descriptor prefix */
-#define gpiod_err(desc, fmt, ...) \
+#define __gpiod_pr(level, desc, fmt, ...) \
do { \
scoped_guard(srcu, &desc->gdev->desc_srcu) { \
- pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
- gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
+ pr_##level("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
+ gpiod_get_label(desc) ?: "?", ##__VA_ARGS__); \
} \
} while (0)
-#define gpiod_warn(desc, fmt, ...) \
-do { \
- scoped_guard(srcu, &desc->gdev->desc_srcu) { \
- pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
- gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
- } \
-} while (0)
+#define gpiod_err(desc, fmt, ...) __gpiod_pr(err, desc, fmt, ##__VA_ARGS__)
+#define gpiod_warn(desc, fmt, ...) __gpiod_pr(warn, desc, fmt, ##__VA_ARGS__)
+#define gpiod_dbg(desc, fmt, ...) __gpiod_pr(debug, desc, fmt, ##__VA_ARGS__)
+
+/* With chip prefix */
-#define gpiod_dbg(desc, fmt, ...) \
+#define __gpiochip_pr(level, gc, fmt, ...) \
do { \
- scoped_guard(srcu, &desc->gdev->desc_srcu) { \
- pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \
- gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \
- } \
+ dev_##level(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__); \
} while (0)
-/* With chip prefix */
-
-#define chip_err(gc, fmt, ...) \
- dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
-#define chip_warn(gc, fmt, ...) \
- dev_warn(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
-#define chip_info(gc, fmt, ...) \
- dev_info(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
-#define chip_dbg(gc, fmt, ...) \
- dev_dbg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__)
+#define gpiochip_err(gc, fmt, ...) __gpiochip_pr(err, gc, fmt, ##__VA_ARGS__)
+#define gpiochip_warn(gc, fmt, ...) __gpiochip_pr(warn, gc, fmt, ##__VA_ARGS__)
+#define gpiochip_info(gc, fmt, ...) __gpiochip_pr(info, gc, fmt, ##__VA_ARGS__)
+#define gpiochip_dbg(gc, fmt, ...) __gpiochip_pr(dbg, gc, fmt, ##__VA_ARGS__)
#endif /* GPIOLIB_H */
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 4b2f7d794275..0e1c668b46d2 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -6,7 +6,7 @@
CFLAGS-$(CONFIG_DRM_USE_DYNAMIC_DEBUG) += -DDYNAMIC_DEBUG_MODULE
# Unconditionally enable W=1 warnings locally
-# --- begin copy-paste W=1 warnings from scripts/Makefile.extrawarn
+# --- begin copy-paste W=1 warnings from scripts/Makefile.warn
subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter
subdir-ccflags-y += $(call cc-option, -Wrestrict)
subdir-ccflags-y += -Wmissing-format-attribute
@@ -41,6 +41,7 @@ drm-y := \
drm_bridge.o \
drm_cache.o \
drm_color_mgmt.o \
+ drm_colorop.o \
drm_connector.o \
drm_crtc.o \
drm_displayid.o \
@@ -76,7 +77,8 @@ drm-y := \
drm-$(CONFIG_DRM_CLIENT) += \
drm_client.o \
drm_client_event.o \
- drm_client_modeset.o
+ drm_client_modeset.o \
+ drm_client_sysrq.o
drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
@@ -150,7 +152,8 @@ drm_kms_helper-y := \
drm_plane_helper.o \
drm_probe_helper.o \
drm_self_refresh_helper.o \
- drm_simple_kms_helper.o
+ drm_simple_kms_helper.o \
+ drm_vblank_helper.o
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
@@ -245,7 +248,7 @@ always-$(CONFIG_DRM_HEADER_TEST) += \
quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@)
cmd_hdrtest = \
$(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \
- PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \
+ PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \
touch $@
$(obj)/%.hdrtest: $(src)/%.h FORCE
diff --git a/drivers/gpu/drm/adp/adp_drv.c b/drivers/gpu/drm/adp/adp_drv.c
index 54cde090c3f4..4554cf75565e 100644
--- a/drivers/gpu/drm/adp/adp_drv.c
+++ b/drivers/gpu/drm/adp/adp_drv.c
@@ -16,6 +16,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig
index 1acfed2f92ef..7f515be5185d 100644
--- a/drivers/gpu/drm/amd/amdgpu/Kconfig
+++ b/drivers/gpu/drm/amd/amdgpu/Kconfig
@@ -43,14 +43,16 @@ config DRM_AMDGPU_SI
bool "Enable amdgpu support for SI parts"
depends on DRM_AMDGPU
help
- Choose this option if you want to enable experimental support
+ Choose this option if you want to enable support
for SI (Southern Islands) asics.
- SI is already supported in radeon. Experimental support for SI
- in amdgpu will be disabled by default and is still provided by
- radeon. Use module options to override this:
+ SI (Southern Islands) are first generation GCN GPUs,
+ supported by both drivers: radeon (old) and amdgpu (new).
+ By default, SI dedicated GPUs are supported by amdgpu.
- radeon.si_support=0 amdgpu.si_support=1
+ Use module options to override this:
+ To use radeon for SI,
+ radeon.si_support=1 amdgpu.si_support=0
config DRM_AMDGPU_CIK
bool "Enable amdgpu support for CIK parts"
@@ -59,11 +61,17 @@ config DRM_AMDGPU_CIK
Choose this option if you want to enable support for CIK (Sea
Islands) asics.
- CIK is already supported in radeon. Support for CIK in amdgpu
- will be disabled by default and is still provided by radeon.
- Use module options to override this:
+ CIK (Sea Islands) are second generation GCN GPUs,
+ supported by both drivers: radeon (old) and amdgpu (new).
+ By default,
+ CIK dedicated GPUs are supported by amdgpu
+ CIK APUs are supported by radeon
+ Use module options to override this:
+ To use amdgpu for CIK,
radeon.cik_support=0 amdgpu.cik_support=1
+ To use radeon for CIK,
+ radeon.cik_support=1 amdgpu.cik_support=0
config DRM_AMDGPU_USERPTR
bool "Always enable userptr write support"
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile
index 64e7acff8f18..c88760fb52ea 100644
--- a/drivers/gpu/drm/amd/amdgpu/Makefile
+++ b/drivers/gpu/drm/amd/amdgpu/Makefile
@@ -37,7 +37,8 @@ ccflags-y := -I$(FULL_AMD_PATH)/include/asic_reg \
-I$(FULL_AMD_DISPLAY_PATH)/modules/inc \
-I$(FULL_AMD_DISPLAY_PATH)/dc \
-I$(FULL_AMD_DISPLAY_PATH)/amdgpu_dm \
- -I$(FULL_AMD_PATH)/amdkfd
+ -I$(FULL_AMD_PATH)/amdkfd \
+ -I$(FULL_AMD_PATH)/ras/ras_mgr
# Locally disable W=1 warnings enabled in drm subsystem Makefile
subdir-ccflags-y += -Wno-override-init
@@ -77,7 +78,7 @@ amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o cik_ih.o \
dce_v8_0.o gfx_v7_0.o cik_sdma.o uvd_v4_2.o vce_v2_0.o
amdgpu-$(CONFIG_DRM_AMDGPU_SI)+= si.o gmc_v6_0.o gfx_v6_0.o si_ih.o si_dma.o dce_v6_0.o \
- uvd_v3_1.o
+ uvd_v3_1.o vce_v1_0.o
amdgpu-y += \
vi.o mxgpu_vi.o nbio_v6_1.o soc15.o emu_soc.o mxgpu_ai.o nbio_v7_0.o vega10_reg_init.o \
@@ -324,4 +325,9 @@ amdgpu-y += \
isp_v4_1_1.o
endif
+AMD_GPU_RAS_PATH := ../ras
+AMD_GPU_RAS_FULL_PATH := $(FULL_AMD_PATH)/ras
+include $(AMD_GPU_RAS_FULL_PATH)/Makefile
+amdgpu-y += $(AMD_GPU_RAS_FILES)
+
obj-$(CONFIG_DRM_AMDGPU)+= amdgpu.o
diff --git a/drivers/gpu/drm/amd/amdgpu/aldebaran.c b/drivers/gpu/drm/amd/amdgpu/aldebaran.c
index 9569dc16dd3d..daa7b23bc775 100644
--- a/drivers/gpu/drm/amd/amdgpu/aldebaran.c
+++ b/drivers/gpu/drm/amd/amdgpu/aldebaran.c
@@ -88,6 +88,10 @@ static int aldebaran_mode2_suspend_ip(struct amdgpu_device *adev)
uint32_t ip_block;
int r, i;
+ /* Skip suspend of SDMA IP versions >= 4.4.2. They are multi-aid */
+ if (adev->aid_mask)
+ ip_block_mask &= ~BIT(AMD_IP_BLOCK_TYPE_SDMA);
+
amdgpu_device_set_pg_state(adev, AMD_PG_STATE_UNGATE);
amdgpu_device_set_cg_state(adev, AMD_CG_STATE_UNGATE);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 6f5b4a0e0a34..9f9774f58ce1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -372,13 +372,15 @@ void amdgpu_device_ip_get_clockgating_state(struct amdgpu_device *adev,
u64 *flags);
int amdgpu_device_ip_wait_for_idle(struct amdgpu_device *adev,
enum amd_ip_block_type block_type);
+bool amdgpu_device_ip_is_hw(struct amdgpu_device *adev,
+ enum amd_ip_block_type block_type);
bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev,
enum amd_ip_block_type block_type);
int amdgpu_ip_block_suspend(struct amdgpu_ip_block *ip_block);
int amdgpu_ip_block_resume(struct amdgpu_ip_block *ip_block);
-#define AMDGPU_MAX_IP_NUM 16
+#define AMDGPU_MAX_IP_NUM AMD_IP_BLOCK_TYPE_NUM
struct amdgpu_ip_block_status {
bool valid;
@@ -839,8 +841,6 @@ struct amd_powerplay {
const struct amd_pm_funcs *pp_funcs;
};
-struct ip_discovery_top;
-
/* polaris10 kickers */
#define ASICID_IS_P20(did, rid) (((did == 0x67DF) && \
((rid == 0xE3) || \
@@ -972,8 +972,7 @@ struct amdgpu_device {
struct notifier_block acpi_nb;
struct notifier_block pm_nb;
struct amdgpu_i2c_chan *i2c_bus[AMDGPU_MAX_I2C_BUS];
- struct debugfs_blob_wrapper debugfs_vbios_blob;
- struct debugfs_blob_wrapper debugfs_discovery_blob;
+ struct debugfs_blob_wrapper debugfs_vbios_blob;
struct mutex srbm_mutex;
/* GRBM index mutex. Protects concurrent access to GRBM index */
struct mutex grbm_idx_mutex;
@@ -1063,6 +1062,9 @@ struct amdgpu_device {
u32 log2_max_MBps;
} mm_stats;
+ /* discovery*/
+ struct amdgpu_discovery_info discovery;
+
/* display */
bool enable_virtual_display;
struct amdgpu_vkms_output *amdgpu_vkms_output;
@@ -1174,6 +1176,12 @@ struct amdgpu_device {
* queue fence.
*/
struct xarray userq_xa;
+ /**
+ * @userq_doorbell_xa: Global user queue map (doorbell index → queue)
+ * Key: doorbell_index (unique global identifier for the queue)
+ * Value: struct amdgpu_usermode_queue
+ */
+ struct xarray userq_doorbell_xa;
/* df */
struct amdgpu_df df;
@@ -1265,8 +1273,6 @@ struct amdgpu_device {
struct list_head ras_list;
- struct ip_discovery_top *ip_top;
-
struct amdgpu_reset_domain *reset_domain;
struct mutex benchmark_mutex;
@@ -1309,9 +1315,8 @@ struct amdgpu_device {
*/
bool apu_prefer_gtt;
- struct list_head userq_mgr_list;
- struct mutex userq_mutex;
bool userq_halt_for_enforce_isolation;
+ struct work_struct userq_reset_work;
struct amdgpu_uid *uid_info;
/* KFD
@@ -1535,11 +1540,6 @@ int emu_soc_asic_init(struct amdgpu_device *adev);
#define amdgpu_asic_read_bios_from_rom(adev, b, l) (adev)->asic_funcs->read_bios_from_rom((adev), (b), (l))
#define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v)))
#define amdgpu_asic_get_config_memsize(adev) (adev)->asic_funcs->get_config_memsize((adev))
-#define amdgpu_asic_flush_hdp(adev, r) \
- ((adev)->asic_funcs->flush_hdp ? (adev)->asic_funcs->flush_hdp((adev), (r)) : (adev)->hdp.funcs->flush_hdp((adev), (r)))
-#define amdgpu_asic_invalidate_hdp(adev, r) \
- ((adev)->asic_funcs->invalidate_hdp ? (adev)->asic_funcs->invalidate_hdp((adev), (r)) : \
- ((adev)->hdp.funcs->invalidate_hdp ? (adev)->hdp.funcs->invalidate_hdp((adev), (r)) : (void)0))
#define amdgpu_asic_need_full_reset(adev) (adev)->asic_funcs->need_full_reset((adev))
#define amdgpu_asic_init_doorbell_index(adev) (adev)->asic_funcs->init_doorbell_index((adev))
#define amdgpu_asic_get_pcie_usage(adev, cnt0, cnt1) ((adev)->asic_funcs->get_pcie_usage((adev), (cnt0), (cnt1)))
@@ -1638,7 +1638,6 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
struct drm_file *file_priv);
void amdgpu_driver_release_kms(struct drm_device *dev);
-int amdgpu_device_ip_suspend(struct amdgpu_device *adev);
int amdgpu_device_prepare(struct drm_device *dev);
void amdgpu_device_complete(struct drm_device *dev);
int amdgpu_device_suspend(struct drm_device *dev, bool fbcon);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
index 4926996f94da..381ef205b0df 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c
@@ -302,17 +302,19 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
adev->acp.acp_res[2].end = adev->acp.acp_res[2].start;
adev->acp.acp_cell[0].name = "acp_audio_dma";
+ adev->acp.acp_cell[0].id = 0;
adev->acp.acp_cell[0].num_resources = 3;
adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0];
adev->acp.acp_cell[0].platform_data = &adev->asic_type;
adev->acp.acp_cell[0].pdata_size = sizeof(adev->asic_type);
adev->acp.acp_cell[1].name = "designware-i2s";
+ adev->acp.acp_cell[1].id = 1;
adev->acp.acp_cell[1].num_resources = 1;
adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
adev->acp.acp_cell[1].platform_data = &i2s_pdata[0];
adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);
- r = mfd_add_hotplug_devices(adev->acp.parent, adev->acp.acp_cell, 2);
+ r = mfd_add_devices(adev->acp.parent, 0, adev->acp.acp_cell, 2, NULL, 0, NULL);
if (r)
goto failure;
r = device_for_each_child(adev->acp.parent, &adev->acp.acp_genpd->gpd,
@@ -410,30 +412,34 @@ static int acp_hw_init(struct amdgpu_ip_block *ip_block)
adev->acp.acp_res[4].end = adev->acp.acp_res[4].start;
adev->acp.acp_cell[0].name = "acp_audio_dma";
+ adev->acp.acp_cell[0].id = 0;
adev->acp.acp_cell[0].num_resources = 5;
adev->acp.acp_cell[0].resources = &adev->acp.acp_res[0];
adev->acp.acp_cell[0].platform_data = &adev->asic_type;
adev->acp.acp_cell[0].pdata_size = sizeof(adev->asic_type);
adev->acp.acp_cell[1].name = "designware-i2s";
+ adev->acp.acp_cell[1].id = 1;
adev->acp.acp_cell[1].num_resources = 1;
adev->acp.acp_cell[1].resources = &adev->acp.acp_res[1];
adev->acp.acp_cell[1].platform_data = &i2s_pdata[0];
adev->acp.acp_cell[1].pdata_size = sizeof(struct i2s_platform_data);
adev->acp.acp_cell[2].name = "designware-i2s";
+ adev->acp.acp_cell[2].id = 2;
adev->acp.acp_cell[2].num_resources = 1;
adev->acp.acp_cell[2].resources = &adev->acp.acp_res[2];
adev->acp.acp_cell[2].platform_data = &i2s_pdata[1];
adev->acp.acp_cell[2].pdata_size = sizeof(struct i2s_platform_data);
adev->acp.acp_cell[3].name = "designware-i2s";
+ adev->acp.acp_cell[3].id = 3;
adev->acp.acp_cell[3].num_resources = 1;
adev->acp.acp_cell[3].resources = &adev->acp.acp_res[3];
adev->acp.acp_cell[3].platform_data = &i2s_pdata[2];
adev->acp.acp_cell[3].pdata_size = sizeof(struct i2s_platform_data);
- r = mfd_add_hotplug_devices(adev->acp.parent, adev->acp.acp_cell, ACP_DEVS);
+ r = mfd_add_devices(adev->acp.parent, 0, adev->acp.acp_cell, ACP_DEVS, NULL, 0, NULL);
if (r)
goto failure;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
index 6c62e27b9800..d31460a9e958 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
@@ -507,7 +507,6 @@ static int amdgpu_atif_handler(struct amdgpu_device *adev,
pm_runtime_get_sync(adev_to_drm(adev)->dev);
/* Just fire off a uevent and let userspace tell us what to do */
drm_helper_hpd_irq_event(adev_to_drm(adev));
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
index 9e120c934cc1..8bdfcde2029b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
@@ -71,7 +71,7 @@ struct kgd_mem {
struct mutex lock;
struct amdgpu_bo *bo;
struct dma_buf *dmabuf;
- struct hmm_range *range;
+ struct amdgpu_hmm_range *range;
struct list_head attachments;
/* protected by amdkfd_process_info.lock */
struct list_head validate_list;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index a2ca9acf8c4e..b1c24c8fa686 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -1057,7 +1057,7 @@ static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr,
struct amdkfd_process_info *process_info = mem->process_info;
struct amdgpu_bo *bo = mem->bo;
struct ttm_operation_ctx ctx = { true, false };
- struct hmm_range *range;
+ struct amdgpu_hmm_range *range;
int ret = 0;
mutex_lock(&process_info->lock);
@@ -1089,8 +1089,15 @@ static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr,
return 0;
}
- ret = amdgpu_ttm_tt_get_user_pages(bo, &range);
+ range = amdgpu_hmm_range_alloc(NULL);
+ if (unlikely(!range)) {
+ ret = -ENOMEM;
+ goto unregister_out;
+ }
+
+ ret = amdgpu_ttm_tt_get_user_pages(bo, range);
if (ret) {
+ amdgpu_hmm_range_free(range);
if (ret == -EAGAIN)
pr_debug("Failed to get user pages, try again\n");
else
@@ -1113,7 +1120,7 @@ static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr,
amdgpu_bo_unreserve(bo);
release_out:
- amdgpu_ttm_tt_get_user_pages_done(bo->tbo.ttm, range);
+ amdgpu_hmm_range_free(range);
unregister_out:
if (ret)
amdgpu_hmm_unregister(bo);
@@ -1267,6 +1274,10 @@ static int unmap_bo_from_gpuvm(struct kgd_mem *mem,
(void)amdgpu_vm_bo_unmap(adev, bo_va, entry->va);
+ /* VM entity stopped if process killed, don't clear freed pt bo */
+ if (!amdgpu_vm_ready(vm))
+ return 0;
+
(void)amdgpu_vm_clear_freed(adev, vm, &bo_va->last_pt_update);
(void)amdgpu_sync_fence(sync, bo_va->last_pt_update, GFP_KERNEL);
@@ -1916,7 +1927,7 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu(
if (amdgpu_ttm_tt_get_usermm(mem->bo->tbo.ttm)) {
amdgpu_hmm_unregister(mem->bo);
mutex_lock(&process_info->notifier_lock);
- amdgpu_ttm_tt_discard_user_pages(mem->bo->tbo.ttm, mem->range);
+ amdgpu_hmm_range_free(mem->range);
mutex_unlock(&process_info->notifier_lock);
}
@@ -1954,9 +1965,7 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu(
*/
if (size) {
if (!is_imported &&
- (mem->bo->preferred_domains == AMDGPU_GEM_DOMAIN_VRAM ||
- (adev->apu_prefer_gtt &&
- mem->bo->preferred_domains == AMDGPU_GEM_DOMAIN_GTT)))
+ mem->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_VRAM)
*size = bo_size;
else
*size = 0;
@@ -2542,7 +2551,7 @@ static int update_invalid_user_pages(struct amdkfd_process_info *process_info,
bo = mem->bo;
- amdgpu_ttm_tt_discard_user_pages(bo->tbo.ttm, mem->range);
+ amdgpu_hmm_range_free(mem->range);
mem->range = NULL;
/* BO reservations and getting user pages (hmm_range_fault)
@@ -2566,9 +2575,14 @@ static int update_invalid_user_pages(struct amdkfd_process_info *process_info,
}
}
+ mem->range = amdgpu_hmm_range_alloc(NULL);
+ if (unlikely(!mem->range))
+ return -ENOMEM;
/* Get updated user pages */
- ret = amdgpu_ttm_tt_get_user_pages(bo, &mem->range);
+ ret = amdgpu_ttm_tt_get_user_pages(bo, mem->range);
if (ret) {
+ amdgpu_hmm_range_free(mem->range);
+ mem->range = NULL;
pr_debug("Failed %d to get user pages\n", ret);
/* Return -EFAULT bad address error as success. It will
@@ -2741,8 +2755,8 @@ static int confirm_valid_user_pages_locked(struct amdkfd_process_info *process_i
continue;
/* Only check mem with hmm range associated */
- valid = amdgpu_ttm_tt_get_user_pages_done(
- mem->bo->tbo.ttm, mem->range);
+ valid = amdgpu_hmm_range_valid(mem->range);
+ amdgpu_hmm_range_free(mem->range);
mem->range = NULL;
if (!valid) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
index c7d32fb216e4..636385c80f64 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
@@ -181,19 +181,22 @@ int amdgpu_atomfirmware_allocate_fb_scratch(struct amdgpu_device *adev)
u8 frev, crev;
int usage_bytes = 0;
- if (amdgpu_atom_parse_data_header(ctx, index, NULL, &frev, &crev, &data_offset)) {
- if (frev == 2 && crev == 1) {
- fw_usage_v2_1 =
- (struct vram_usagebyfirmware_v2_1 *)(ctx->bios + data_offset);
- amdgpu_atomfirmware_allocate_fb_v2_1(adev,
- fw_usage_v2_1,
- &usage_bytes);
- } else if (frev >= 2 && crev >= 2) {
- fw_usage_v2_2 =
- (struct vram_usagebyfirmware_v2_2 *)(ctx->bios + data_offset);
- amdgpu_atomfirmware_allocate_fb_v2_2(adev,
- fw_usage_v2_2,
- &usage_bytes);
+ /* Skip atomfirmware allocation for SRIOV VFs when dynamic crit regn is enabled */
+ if (!(amdgpu_sriov_vf(adev) && adev->virt.is_dynamic_crit_regn_enabled)) {
+ if (amdgpu_atom_parse_data_header(ctx, index, NULL, &frev, &crev, &data_offset)) {
+ if (frev == 2 && crev == 1) {
+ fw_usage_v2_1 =
+ (struct vram_usagebyfirmware_v2_1 *)(ctx->bios + data_offset);
+ amdgpu_atomfirmware_allocate_fb_v2_1(adev,
+ fw_usage_v2_1,
+ &usage_bytes);
+ } else if (frev >= 2 && crev >= 2) {
+ fw_usage_v2_2 =
+ (struct vram_usagebyfirmware_v2_2 *)(ctx->bios + data_offset);
+ amdgpu_atomfirmware_allocate_fb_v2_2(adev,
+ fw_usage_v2_2,
+ &usage_bytes);
+ }
}
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
index 00e96419fcda..35d04e69aec0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c
@@ -96,13 +96,14 @@ void amdgpu_bios_release(struct amdgpu_device *adev)
* part of the system bios. On boot, the system bios puts a
* copy of the igp rom at the start of vram if a discrete card is
* present.
- * For SR-IOV, the vbios image is also put in VRAM in the VF.
+ * For SR-IOV, if dynamic critical region is not enabled,
+ * the vbios image is also put at the start of VRAM in the VF.
*/
static bool amdgpu_read_bios_from_vram(struct amdgpu_device *adev)
{
- uint8_t __iomem *bios;
+ uint8_t __iomem *bios = NULL;
resource_size_t vram_base;
- resource_size_t size = 256 * 1024; /* ??? */
+ u32 size = 256U * 1024U; /* ??? */
if (!(adev->flags & AMD_IS_APU))
if (amdgpu_device_need_post(adev))
@@ -114,18 +115,33 @@ static bool amdgpu_read_bios_from_vram(struct amdgpu_device *adev)
adev->bios = NULL;
vram_base = pci_resource_start(adev->pdev, 0);
- bios = ioremap_wc(vram_base, size);
- if (!bios)
- return false;
adev->bios = kmalloc(size, GFP_KERNEL);
- if (!adev->bios) {
- iounmap(bios);
+ if (!adev->bios)
return false;
+
+ /* For SRIOV with dynamic critical region is enabled,
+ * the vbios image is put at a dynamic offset of VRAM in the VF.
+ * If dynamic critical region is disabled, follow the existing logic as on baremetal.
+ */
+ if (amdgpu_sriov_vf(adev) && adev->virt.is_dynamic_crit_regn_enabled) {
+ if (amdgpu_virt_get_dynamic_data_info(adev,
+ AMD_SRIOV_MSG_VBIOS_IMG_TABLE_ID, adev->bios, &size)) {
+ amdgpu_bios_release(adev);
+ return false;
+ }
+ } else {
+ bios = ioremap_wc(vram_base, size);
+ if (!bios) {
+ amdgpu_bios_release(adev);
+ return false;
+ }
+
+ memcpy_fromio(adev->bios, bios, size);
+ iounmap(bios);
}
+
adev->bios_size = size;
- memcpy_fromio(adev->bios, bios, size);
- iounmap(bios);
if (!check_atom_bios(adev, size)) {
amdgpu_bios_release(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
index a716c9886c74..2b5e7c46a39d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h
@@ -38,7 +38,7 @@ struct amdgpu_bo_list_entry {
struct amdgpu_bo *bo;
struct amdgpu_bo_va *bo_va;
uint32_t priority;
- struct hmm_range *range;
+ struct amdgpu_hmm_range *range;
bool user_invalidated;
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
index 47e9bfba0642..9f96d568acf2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
@@ -734,10 +734,8 @@ amdgpu_connector_lvds_detect(struct drm_connector *connector, bool force)
amdgpu_connector_update_scratch_regs(connector, ret);
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
@@ -919,10 +917,8 @@ amdgpu_connector_vga_detect(struct drm_connector *connector, bool force)
amdgpu_connector_update_scratch_regs(connector, ret);
out:
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
@@ -1146,10 +1142,8 @@ out:
amdgpu_connector_update_scratch_regs(connector, ret);
exit:
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
@@ -1486,10 +1480,8 @@ amdgpu_connector_dp_detect(struct drm_connector *connector, bool force)
amdgpu_connector_update_scratch_regs(connector, ret);
out:
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
connector->connector_type == DRM_MODE_CONNECTOR_eDP)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c
index ef996493115f..425a3e564360 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: MIT
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h
index bcb97d245673..353421807387 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: MIT */
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 2f6a96af7fb1..ecdfe6cb36cc 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -29,7 +29,6 @@
#include <linux/pagemap.h>
#include <linux/sync_file.h>
#include <linux/dma-buf.h>
-#include <linux/hmm.h>
#include <drm/amdgpu_drm.h>
#include <drm/drm_syncobj.h>
@@ -41,6 +40,7 @@
#include "amdgpu_gmc.h"
#include "amdgpu_gem.h"
#include "amdgpu_ras.h"
+#include "amdgpu_hmm.h"
static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p,
struct amdgpu_device *adev,
@@ -891,12 +891,17 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
bool userpage_invalidated = false;
struct amdgpu_bo *bo = e->bo;
- r = amdgpu_ttm_tt_get_user_pages(bo, &e->range);
+ e->range = amdgpu_hmm_range_alloc(NULL);
+ if (unlikely(!e->range))
+ return -ENOMEM;
+
+ r = amdgpu_ttm_tt_get_user_pages(bo, e->range);
if (r)
goto out_free_user_pages;
for (i = 0; i < bo->tbo.ttm->num_pages; i++) {
- if (bo->tbo.ttm->pages[i] != hmm_pfn_to_page(e->range->hmm_pfns[i])) {
+ if (bo->tbo.ttm->pages[i] !=
+ hmm_pfn_to_page(e->range->hmm_range.hmm_pfns[i])) {
userpage_invalidated = true;
break;
}
@@ -990,9 +995,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
out_free_user_pages:
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
- struct amdgpu_bo *bo = e->bo;
-
- amdgpu_ttm_tt_get_user_pages_done(bo->tbo.ttm, e->range);
+ amdgpu_hmm_range_free(e->range);
e->range = NULL;
}
mutex_unlock(&p->bo_list->bo_list_mutex);
@@ -1323,8 +1326,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
*/
r = 0;
amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) {
- r |= !amdgpu_ttm_tt_get_user_pages_done(e->bo->tbo.ttm,
- e->range);
+ r |= !amdgpu_hmm_range_valid(e->range);
+ amdgpu_hmm_range_free(e->range);
e->range = NULL;
}
if (r) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
index f5d5c45ddc0d..afedea02188d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
@@ -236,7 +236,7 @@ static int amdgpu_ctx_init_entity(struct amdgpu_ctx *ctx, u32 hw_ip,
r = amdgpu_xcp_select_scheds(adev, hw_ip, hw_prio, fpriv,
&num_scheds, &scheds);
if (r)
- goto cleanup_entity;
+ goto error_free_entity;
}
/* disable load balance if the hw engine retains context among dependent jobs */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
index a70651050acf..62d43b8cbe58 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c
@@ -129,7 +129,6 @@ static int amdgpu_debugfs_process_reg_op(bool read, struct file *f,
if (use_bank) {
if ((sh_bank != 0xFFFFFFFF && sh_bank >= adev->gfx.config.max_sh_per_se) ||
(se_bank != 0xFFFFFFFF && se_bank >= adev->gfx.config.max_shader_engines)) {
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
return -EINVAL;
@@ -179,7 +178,6 @@ end:
if (pm_pg_lock)
mutex_unlock(&adev->pm.mutex);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
@@ -255,7 +253,6 @@ static ssize_t amdgpu_debugfs_regs2_op(struct file *f, char __user *buf, u32 off
if (rd->id.use_grbm) {
if ((rd->id.grbm.sh != 0xFFFFFFFF && rd->id.grbm.sh >= adev->gfx.config.max_sh_per_se) ||
(rd->id.grbm.se != 0xFFFFFFFF && rd->id.grbm.se >= adev->gfx.config.max_shader_engines)) {
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
mutex_unlock(&rd->lock);
@@ -310,7 +307,6 @@ end:
mutex_unlock(&rd->lock);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
@@ -446,7 +442,6 @@ static ssize_t amdgpu_debugfs_gprwave_read(struct file *f, char __user *buf, siz
amdgpu_gfx_select_se_sh(adev, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, rd->id.xcc_id);
mutex_unlock(&adev->grbm_idx_mutex);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
if (!x) {
@@ -557,7 +552,6 @@ static ssize_t amdgpu_debugfs_regs_pcie_read(struct file *f, char __user *buf,
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
return r;
@@ -617,7 +611,6 @@ static ssize_t amdgpu_debugfs_regs_pcie_write(struct file *f, const char __user
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
return r;
@@ -676,7 +669,6 @@ static ssize_t amdgpu_debugfs_regs_didt_read(struct file *f, char __user *buf,
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
return r;
@@ -736,7 +728,6 @@ static ssize_t amdgpu_debugfs_regs_didt_write(struct file *f, const char __user
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
return r;
@@ -795,7 +786,6 @@ static ssize_t amdgpu_debugfs_regs_smc_read(struct file *f, char __user *buf,
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
return r;
@@ -855,7 +845,6 @@ static ssize_t amdgpu_debugfs_regs_smc_write(struct file *f, const char __user *
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev);
return r;
@@ -1003,7 +992,6 @@ static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf,
r = amdgpu_dpm_read_sensor(adev, idx, &values[0], &valuesize);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
if (r) {
@@ -1094,7 +1082,6 @@ static ssize_t amdgpu_debugfs_wave_read(struct file *f, char __user *buf,
amdgpu_gfx_select_se_sh(adev, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0);
mutex_unlock(&adev->grbm_idx_mutex);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
if (!x) {
@@ -1192,7 +1179,6 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf,
amdgpu_gfx_select_se_sh(adev, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0);
mutex_unlock(&adev->grbm_idx_mutex);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
while (size) {
@@ -1266,7 +1252,6 @@ static ssize_t amdgpu_debugfs_gfxoff_residency_read(struct file *f, char __user
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
@@ -1315,7 +1300,6 @@ static ssize_t amdgpu_debugfs_gfxoff_residency_write(struct file *f, const char
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
@@ -1365,7 +1349,6 @@ static ssize_t amdgpu_debugfs_gfxoff_count_read(struct file *f, char __user *buf
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
@@ -1414,7 +1397,6 @@ static ssize_t amdgpu_debugfs_gfxoff_write(struct file *f, const char __user *bu
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
@@ -1460,7 +1442,6 @@ static ssize_t amdgpu_debugfs_gfxoff_read(struct file *f, char __user *buf,
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
@@ -1501,7 +1482,6 @@ static ssize_t amdgpu_debugfs_gfxoff_status_read(struct file *f, char __user *bu
r = result;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
@@ -1701,7 +1681,6 @@ static int amdgpu_debugfs_test_ib_show(struct seq_file *m, void *unused)
up_write(&adev->reset_domain->sem);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return 0;
@@ -1721,7 +1700,6 @@ static int amdgpu_debugfs_evict_vram(void *data, u64 *val)
*val = amdgpu_ttm_evict_resources(adev, TTM_PL_VRAM);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return 0;
@@ -1742,7 +1720,6 @@ static int amdgpu_debugfs_evict_gtt(void *data, u64 *val)
*val = amdgpu_ttm_evict_resources(adev, TTM_PL_TT);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return 0;
@@ -1762,7 +1739,6 @@ static int amdgpu_debugfs_benchmark(void *data, u64 val)
r = amdgpu_benchmark(adev, val);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return r;
@@ -1902,7 +1878,7 @@ no_preempt:
continue;
}
job = to_amdgpu_job(s_job);
- if (preempted && (&job->hw_fence.base) == fence)
+ if (preempted && (&job->hw_fence->base) == fence)
/* mark the job as preempted */
job->preemption_status |= AMDGPU_IB_PREEMPTED;
}
@@ -2014,7 +1990,6 @@ static int amdgpu_debugfs_sclk_set(void *data, u64 val)
ret = -EINVAL;
out:
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return ret;
@@ -2123,10 +2098,9 @@ int amdgpu_debugfs_init(struct amdgpu_device *adev)
debugfs_create_blob("amdgpu_vbios", 0444, root,
&adev->debugfs_vbios_blob);
- adev->debugfs_discovery_blob.data = adev->mman.discovery_bin;
- adev->debugfs_discovery_blob.size = adev->mman.discovery_tmr_size;
- debugfs_create_blob("amdgpu_discovery", 0444, root,
- &adev->debugfs_discovery_blob);
+ if (adev->discovery.debugfs_blob.size)
+ debugfs_create_blob("amdgpu_discovery", 0444, root,
+ &adev->discovery.debugfs_blob);
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
index 8a026bc9ea44..4e2fe6674db8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c
@@ -217,8 +217,7 @@ amdgpu_devcoredump_read(char *buffer, loff_t offset, size_t count,
drm_printf(&p, "version: " AMDGPU_COREDUMP_VERSION "\n");
drm_printf(&p, "kernel: " UTS_RELEASE "\n");
drm_printf(&p, "module: " KBUILD_MODNAME "\n");
- drm_printf(&p, "time: %lld.%09ld\n", coredump->reset_time.tv_sec,
- coredump->reset_time.tv_nsec);
+ drm_printf(&p, "time: %ptSp\n", &coredump->reset_time);
if (coredump->reset_task_info.task.pid)
drm_printf(&p, "process_name: %s PID: %d\n",
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 3d032c4e2dce..58c3ffe707d1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -71,6 +71,7 @@
#include "amdgpu_xgmi.h"
#include "amdgpu_ras.h"
+#include "amdgpu_ras_mgr.h"
#include "amdgpu_pmu.h"
#include "amdgpu_fru_eeprom.h"
#include "amdgpu_reset.h"
@@ -179,6 +180,10 @@ struct amdgpu_init_level amdgpu_init_minimal_xgmi = {
BIT(AMD_IP_BLOCK_TYPE_PSP)
};
+static int amdgpu_device_ip_resume_phase1(struct amdgpu_device *adev);
+static int amdgpu_device_ip_resume_phase2(struct amdgpu_device *adev);
+static int amdgpu_device_ip_resume_phase3(struct amdgpu_device *adev);
+
static void amdgpu_device_load_switch_state(struct amdgpu_device *adev);
static inline bool amdgpu_ip_member_of_hwini(struct amdgpu_device *adev,
@@ -1673,9 +1678,9 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device *adev)
int rbar_size = pci_rebar_bytes_to_size(adev->gmc.real_vram_size);
struct pci_bus *root;
struct resource *res;
+ int max_size, r;
unsigned int i;
u16 cmd;
- int r;
if (!IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT))
return 0;
@@ -1721,30 +1726,28 @@ int amdgpu_device_resize_fb_bar(struct amdgpu_device *adev)
return 0;
/* Limit the BAR size to what is available */
- rbar_size = min(fls(pci_rebar_get_possible_sizes(adev->pdev, 0)) - 1,
- rbar_size);
+ max_size = pci_rebar_get_max_size(adev->pdev, 0);
+ if (max_size < 0)
+ return 0;
+ rbar_size = min(max_size, rbar_size);
/* Disable memory decoding while we change the BAR addresses and size */
pci_read_config_word(adev->pdev, PCI_COMMAND, &cmd);
pci_write_config_word(adev->pdev, PCI_COMMAND,
cmd & ~PCI_COMMAND_MEMORY);
- /* Free the VRAM and doorbell BAR, we most likely need to move both. */
+ /* Tear down doorbell as resizing will release BARs */
amdgpu_doorbell_fini(adev);
- if (adev->asic_type >= CHIP_BONAIRE)
- pci_release_resource(adev->pdev, 2);
- pci_release_resource(adev->pdev, 0);
-
- r = pci_resize_resource(adev->pdev, 0, rbar_size);
+ r = pci_resize_resource(adev->pdev, 0, rbar_size,
+ (adev->asic_type >= CHIP_BONAIRE) ? 1 << 5
+ : 1 << 2);
if (r == -ENOSPC)
dev_info(adev->dev,
"Not enough PCI address space for a large BAR.");
else if (r && r != -ENOTSUPP)
dev_err(adev->dev, "Problem resizing BAR0 (%d).", r);
- pci_assign_unassigned_bus_resources(adev->pdev->bus);
-
/* When the doorbell or fb BAR isn't available we have no chance of
* using the device.
*/
@@ -2387,7 +2390,7 @@ int amdgpu_device_ip_wait_for_idle(struct amdgpu_device *adev,
}
/**
- * amdgpu_device_ip_is_valid - is the hardware IP enabled
+ * amdgpu_device_ip_is_hw - is the hardware IP enabled
*
* @adev: amdgpu_device pointer
* @block_type: Type of hardware IP (SMU, GFX, UVD, etc.)
@@ -2395,6 +2398,27 @@ int amdgpu_device_ip_wait_for_idle(struct amdgpu_device *adev,
* Check if the hardware IP is enable or not.
* Returns true if it the IP is enable, false if not.
*/
+bool amdgpu_device_ip_is_hw(struct amdgpu_device *adev,
+ enum amd_ip_block_type block_type)
+{
+ int i;
+
+ for (i = 0; i < adev->num_ip_blocks; i++) {
+ if (adev->ip_blocks[i].version->type == block_type)
+ return adev->ip_blocks[i].status.hw;
+ }
+ return false;
+}
+
+/**
+ * amdgpu_device_ip_is_valid - is the hardware IP valid
+ *
+ * @adev: amdgpu_device pointer
+ * @block_type: Type of hardware IP (SMU, GFX, UVD, etc.)
+ *
+ * Check if the hardware IP is valid or not.
+ * Returns true if it the IP is valid, false if not.
+ */
bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev,
enum amd_ip_block_type block_type)
{
@@ -2473,6 +2497,7 @@ static const char *ip_block_names[] = {
[AMD_IP_BLOCK_TYPE_VPE] = "vpe",
[AMD_IP_BLOCK_TYPE_UMSCH_MM] = "umsch_mm",
[AMD_IP_BLOCK_TYPE_ISP] = "isp",
+ [AMD_IP_BLOCK_TYPE_RAS] = "ras",
};
static const char *ip_block_name(struct amdgpu_device *adev, enum amd_ip_block_type type)
@@ -2633,11 +2658,13 @@ static int amdgpu_device_parse_gpu_info_fw(struct amdgpu_device *adev)
chip_name = "arcturus";
break;
case CHIP_NAVI12:
- if (adev->mman.discovery_bin)
+ if (adev->discovery.bin)
return 0;
chip_name = "navi12";
break;
case CHIP_CYAN_SKILLFISH:
+ if (adev->discovery.bin)
+ return 0;
chip_name = "cyan_skillfish";
break;
}
@@ -2761,6 +2788,10 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
r = amdgpu_virt_request_full_gpu(adev, true);
if (r)
return r;
+
+ r = amdgpu_virt_init_critical_region(adev);
+ if (r)
+ return r;
}
switch (adev->asic_type) {
@@ -3414,10 +3445,11 @@ int amdgpu_device_set_pg_state(struct amdgpu_device *adev,
(adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GFX ||
adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_SDMA))
continue;
- /* skip CG for VCE/UVD, it's handled specially */
+ /* skip CG for VCE/UVD/VPE, it's handled specially */
if (adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_UVD &&
adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_VCE &&
adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_VCN &&
+ adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_VPE &&
adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_JPEG &&
adev->ip_blocks[i].version->funcs->set_powergating_state) {
/* enable powergating to save power */
@@ -3649,6 +3681,20 @@ static int amdgpu_device_ip_fini_early(struct amdgpu_device *adev)
"failed to release exclusive mode on fini\n");
}
+ /*
+ * Driver reload on the APU can fail due to firmware validation because
+ * the PSP is always running, as it is shared across the whole SoC.
+ * This same issue does not occur on dGPU because it has a mechanism
+ * that checks whether the PSP is running. A solution for those issues
+ * in the APU is to trigger a GPU reset, but this should be done during
+ * the unload phase to avoid adding boot latency and screen flicker.
+ */
+ if ((adev->flags & AMD_IS_APU) && !adev->gmc.is_app_apu) {
+ r = amdgpu_asic_reset(adev);
+ if (r)
+ dev_err(adev->dev, "asic reset on %s failed\n", __func__);
+ }
+
return 0;
}
@@ -3759,7 +3805,7 @@ static void amdgpu_device_delay_enable_gfx_off(struct work_struct *work)
*/
static int amdgpu_device_ip_suspend_phase1(struct amdgpu_device *adev)
{
- int i, r;
+ int i, r, rec;
amdgpu_device_set_pg_state(adev, AMD_PG_STATE_UNGATE);
amdgpu_device_set_cg_state(adev, AMD_CG_STATE_UNGATE);
@@ -3780,13 +3826,25 @@ static int amdgpu_device_ip_suspend_phase1(struct amdgpu_device *adev)
if (adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_DCE)
continue;
- /* XXX handle errors */
r = amdgpu_ip_block_suspend(&adev->ip_blocks[i]);
if (r)
- return r;
+ goto unwind;
}
return 0;
+unwind:
+ rec = amdgpu_device_ip_resume_phase3(adev);
+ if (rec)
+ dev_err(adev->dev,
+ "amdgpu_device_ip_resume_phase3 failed during unwind: %d\n",
+ rec);
+
+ amdgpu_dpm_set_df_cstate(adev, DF_CSTATE_ALLOW);
+
+ amdgpu_device_set_pg_state(adev, AMD_PG_STATE_GATE);
+ amdgpu_device_set_cg_state(adev, AMD_CG_STATE_GATE);
+
+ return r;
}
/**
@@ -3802,7 +3860,7 @@ static int amdgpu_device_ip_suspend_phase1(struct amdgpu_device *adev)
*/
static int amdgpu_device_ip_suspend_phase2(struct amdgpu_device *adev)
{
- int i, r;
+ int i, r, rec;
if (adev->in_s0ix)
amdgpu_dpm_gfx_state_change(adev, sGpuChangeState_D3Entry);
@@ -3863,9 +3921,9 @@ static int amdgpu_device_ip_suspend_phase2(struct amdgpu_device *adev)
adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_PSP)
continue;
- /* XXX handle errors */
r = amdgpu_ip_block_suspend(&adev->ip_blocks[i]);
- adev->ip_blocks[i].status.hw = false;
+ if (r)
+ goto unwind;
/* handle putting the SMC in the appropriate state */
if (!amdgpu_sriov_vf(adev)) {
@@ -3875,13 +3933,40 @@ static int amdgpu_device_ip_suspend_phase2(struct amdgpu_device *adev)
dev_err(adev->dev,
"SMC failed to set mp1 state %d, %d\n",
adev->mp1_state, r);
- return r;
+ goto unwind;
}
}
}
}
return 0;
+unwind:
+ /* suspend phase 2 = resume phase 1 + resume phase 2 */
+ rec = amdgpu_device_ip_resume_phase1(adev);
+ if (rec) {
+ dev_err(adev->dev,
+ "amdgpu_device_ip_resume_phase1 failed during unwind: %d\n",
+ rec);
+ return r;
+ }
+
+ rec = amdgpu_device_fw_loading(adev);
+ if (rec) {
+ dev_err(adev->dev,
+ "amdgpu_device_fw_loading failed during unwind: %d\n",
+ rec);
+ return r;
+ }
+
+ rec = amdgpu_device_ip_resume_phase2(adev);
+ if (rec) {
+ dev_err(adev->dev,
+ "amdgpu_device_ip_resume_phase2 failed during unwind: %d\n",
+ rec);
+ return r;
+ }
+
+ return r;
}
/**
@@ -3895,7 +3980,7 @@ static int amdgpu_device_ip_suspend_phase2(struct amdgpu_device *adev)
* in each IP into a state suitable for suspend.
* Returns 0 on success, negative error code on failure.
*/
-int amdgpu_device_ip_suspend(struct amdgpu_device *adev)
+static int amdgpu_device_ip_suspend(struct amdgpu_device *adev)
{
int r;
@@ -4179,25 +4264,13 @@ bool amdgpu_device_asic_has_dc_support(struct pci_dev *pdev,
case CHIP_PITCAIRN:
case CHIP_VERDE:
case CHIP_OLAND:
- /*
- * We have systems in the wild with these ASICs that require
- * LVDS and VGA support which is not supported with DC.
- *
- * Fallback to the non-DC driver here by default so as not to
- * cause regressions.
- */
-#if defined(CONFIG_DRM_AMD_DC_SI)
- return amdgpu_dc > 0;
-#else
- return false;
-#endif
- case CHIP_BONAIRE:
+ return amdgpu_dc != 0 && IS_ENABLED(CONFIG_DRM_AMD_DC_SI);
case CHIP_KAVERI:
case CHIP_KABINI:
case CHIP_MULLINS:
/*
* We have systems in the wild with these ASICs that require
- * VGA support which is not supported with DC.
+ * TRAVIS and NUTMEG support which is not supported with DC.
*
* Fallback to the non-DC driver here by default so as not to
* cause regressions.
@@ -4285,58 +4358,53 @@ static int amdgpu_device_get_job_timeout_settings(struct amdgpu_device *adev)
long timeout;
int ret = 0;
- /*
- * By default timeout for jobs is 10 sec
- */
- adev->compute_timeout = adev->gfx_timeout = msecs_to_jiffies(10000);
- adev->sdma_timeout = adev->video_timeout = adev->gfx_timeout;
+ /* By default timeout for all queues is 2 sec */
+ adev->gfx_timeout = adev->compute_timeout = adev->sdma_timeout =
+ adev->video_timeout = msecs_to_jiffies(2000);
- if (strnlen(input, AMDGPU_MAX_TIMEOUT_PARAM_LENGTH)) {
- while ((timeout_setting = strsep(&input, ",")) &&
- strnlen(timeout_setting, AMDGPU_MAX_TIMEOUT_PARAM_LENGTH)) {
- ret = kstrtol(timeout_setting, 0, &timeout);
- if (ret)
- return ret;
+ if (!strnlen(input, AMDGPU_MAX_TIMEOUT_PARAM_LENGTH))
+ return 0;
- if (timeout == 0) {
- index++;
- continue;
- } else if (timeout < 0) {
- timeout = MAX_SCHEDULE_TIMEOUT;
- dev_warn(adev->dev, "lockup timeout disabled");
- add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK);
- } else {
- timeout = msecs_to_jiffies(timeout);
- }
+ while ((timeout_setting = strsep(&input, ",")) &&
+ strnlen(timeout_setting, AMDGPU_MAX_TIMEOUT_PARAM_LENGTH)) {
+ ret = kstrtol(timeout_setting, 0, &timeout);
+ if (ret)
+ return ret;
- switch (index++) {
- case 0:
- adev->gfx_timeout = timeout;
- break;
- case 1:
- adev->compute_timeout = timeout;
- break;
- case 2:
- adev->sdma_timeout = timeout;
- break;
- case 3:
- adev->video_timeout = timeout;
- break;
- default:
- break;
- }
+ if (timeout == 0) {
+ index++;
+ continue;
+ } else if (timeout < 0) {
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ dev_warn(adev->dev, "lockup timeout disabled");
+ add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK);
+ } else {
+ timeout = msecs_to_jiffies(timeout);
}
- /*
- * There is only one value specified and
- * it should apply to all non-compute jobs.
- */
- if (index == 1) {
- adev->sdma_timeout = adev->video_timeout = adev->gfx_timeout;
- if (amdgpu_sriov_vf(adev) || amdgpu_passthrough(adev))
- adev->compute_timeout = adev->gfx_timeout;
+
+ switch (index++) {
+ case 0:
+ adev->gfx_timeout = timeout;
+ break;
+ case 1:
+ adev->compute_timeout = timeout;
+ break;
+ case 2:
+ adev->sdma_timeout = timeout;
+ break;
+ case 3:
+ adev->video_timeout = timeout;
+ break;
+ default:
+ break;
}
}
+ /* When only one value specified apply it to all queues. */
+ if (index == 1)
+ adev->gfx_timeout = adev->compute_timeout = adev->sdma_timeout =
+ adev->video_timeout = timeout;
+
return ret;
}
@@ -4391,6 +4459,55 @@ static void amdgpu_device_set_mcbp(struct amdgpu_device *adev)
dev_info(adev->dev, "MCBP is enabled\n");
}
+static int amdgpu_device_sys_interface_init(struct amdgpu_device *adev)
+{
+ int r;
+
+ r = amdgpu_atombios_sysfs_init(adev);
+ if (r)
+ drm_err(&adev->ddev,
+ "registering atombios sysfs failed (%d).\n", r);
+
+ r = amdgpu_pm_sysfs_init(adev);
+ if (r)
+ dev_err(adev->dev, "registering pm sysfs failed (%d).\n", r);
+
+ r = amdgpu_ucode_sysfs_init(adev);
+ if (r) {
+ adev->ucode_sysfs_en = false;
+ dev_err(adev->dev, "Creating firmware sysfs failed (%d).\n", r);
+ } else
+ adev->ucode_sysfs_en = true;
+
+ r = amdgpu_device_attr_sysfs_init(adev);
+ if (r)
+ dev_err(adev->dev, "Could not create amdgpu device attr\n");
+
+ r = devm_device_add_group(adev->dev, &amdgpu_board_attrs_group);
+ if (r)
+ dev_err(adev->dev,
+ "Could not create amdgpu board attributes\n");
+
+ amdgpu_fru_sysfs_init(adev);
+ amdgpu_reg_state_sysfs_init(adev);
+ amdgpu_xcp_sysfs_init(adev);
+
+ return r;
+}
+
+static void amdgpu_device_sys_interface_fini(struct amdgpu_device *adev)
+{
+ if (adev->pm.sysfs_initialized)
+ amdgpu_pm_sysfs_fini(adev);
+ if (adev->ucode_sysfs_en)
+ amdgpu_ucode_sysfs_fini(adev);
+ amdgpu_device_attr_sysfs_fini(adev);
+ amdgpu_fru_sysfs_fini(adev);
+
+ amdgpu_reg_state_sysfs_fini(adev);
+ amdgpu_xcp_sysfs_fini(adev);
+}
+
/**
* amdgpu_device_init - initialize the driver
*
@@ -4490,7 +4607,6 @@ int amdgpu_device_init(struct amdgpu_device *adev,
mutex_init(&adev->gfx.userq_sch_mutex);
mutex_init(&adev->gfx.workload_profile_mutex);
mutex_init(&adev->vcn.workload_profile_mutex);
- mutex_init(&adev->userq_mutex);
amdgpu_device_init_apu_flags(adev);
@@ -4518,7 +4634,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
INIT_LIST_HEAD(&adev->pm.od_kobj_list);
- INIT_LIST_HEAD(&adev->userq_mgr_list);
+ xa_init(&adev->userq_doorbell_xa);
INIT_DELAYED_WORK(&adev->delayed_init_work,
amdgpu_device_delayed_init_work_handler);
@@ -4541,6 +4657,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
}
INIT_WORK(&adev->xgmi_reset_work, amdgpu_device_xgmi_reset_func);
+ INIT_WORK(&adev->userq_reset_work, amdgpu_userq_reset_work);
adev->gfx.gfx_off_req_count = 1;
adev->gfx.gfx_off_residency = 0;
@@ -4814,39 +4931,14 @@ fence_driver_init:
flush_delayed_work(&adev->delayed_init_work);
}
+ if (adev->init_lvl->level == AMDGPU_INIT_LEVEL_MINIMAL_XGMI)
+ amdgpu_xgmi_reset_on_init(adev);
/*
* Place those sysfs registering after `late_init`. As some of those
* operations performed in `late_init` might affect the sysfs
* interfaces creating.
*/
- r = amdgpu_atombios_sysfs_init(adev);
- if (r)
- drm_err(&adev->ddev,
- "registering atombios sysfs failed (%d).\n", r);
-
- r = amdgpu_pm_sysfs_init(adev);
- if (r)
- dev_err(adev->dev, "registering pm sysfs failed (%d).\n", r);
-
- r = amdgpu_ucode_sysfs_init(adev);
- if (r) {
- adev->ucode_sysfs_en = false;
- dev_err(adev->dev, "Creating firmware sysfs failed (%d).\n", r);
- } else
- adev->ucode_sysfs_en = true;
-
- r = amdgpu_device_attr_sysfs_init(adev);
- if (r)
- dev_err(adev->dev, "Could not create amdgpu device attr\n");
-
- r = devm_device_add_group(adev->dev, &amdgpu_board_attrs_group);
- if (r)
- dev_err(adev->dev,
- "Could not create amdgpu board attributes\n");
-
- amdgpu_fru_sysfs_init(adev);
- amdgpu_reg_state_sysfs_init(adev);
- amdgpu_xcp_sysfs_init(adev);
+ r = amdgpu_device_sys_interface_init(adev);
if (IS_ENABLED(CONFIG_PERF_EVENTS))
r = amdgpu_pmu_init(adev);
@@ -4874,9 +4966,6 @@ fence_driver_init:
if (px)
vga_switcheroo_init_domain_pm_ops(adev->dev, &adev->vga_pm_domain);
- if (adev->init_lvl->level == AMDGPU_INIT_LEVEL_MINIMAL_XGMI)
- amdgpu_xgmi_reset_on_init(adev);
-
amdgpu_device_check_iommu_direct_map(adev);
adev->pm_nb.notifier_call = amdgpu_device_pm_notifier;
@@ -4968,15 +5057,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
}
amdgpu_fence_driver_hw_fini(adev);
- if (adev->pm.sysfs_initialized)
- amdgpu_pm_sysfs_fini(adev);
- if (adev->ucode_sysfs_en)
- amdgpu_ucode_sysfs_fini(adev);
- amdgpu_device_attr_sysfs_fini(adev);
- amdgpu_fru_sysfs_fini(adev);
-
- amdgpu_reg_state_sysfs_fini(adev);
- amdgpu_xcp_sysfs_fini(adev);
+ amdgpu_device_sys_interface_fini(adev);
/* disable ras feature must before hw fini */
amdgpu_ras_pre_fini(adev);
@@ -5051,7 +5132,7 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev)
if (IS_ENABLED(CONFIG_PERF_EVENTS))
amdgpu_pmu_fini(adev);
- if (adev->mman.discovery_bin)
+ if (adev->discovery.bin)
amdgpu_discovery_fini(adev);
amdgpu_reset_put_reset_domain(adev->reset_domain);
@@ -5199,7 +5280,7 @@ void amdgpu_device_complete(struct drm_device *dev)
int amdgpu_device_suspend(struct drm_device *dev, bool notify_clients)
{
struct amdgpu_device *adev = drm_to_adev(dev);
- int r = 0;
+ int r, rec;
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
@@ -5215,39 +5296,92 @@ int amdgpu_device_suspend(struct drm_device *dev, bool notify_clients)
return r;
}
- if (amdgpu_acpi_smart_shift_update(adev, AMDGPU_SS_DEV_D3))
- dev_warn(adev->dev, "smart shift update failed\n");
+ r = amdgpu_acpi_smart_shift_update(adev, AMDGPU_SS_DEV_D3);
+ if (r)
+ goto unwind_sriov;
if (notify_clients)
- drm_client_dev_suspend(adev_to_drm(adev), false);
+ drm_client_dev_suspend(adev_to_drm(adev));
cancel_delayed_work_sync(&adev->delayed_init_work);
amdgpu_ras_suspend(adev);
- amdgpu_device_ip_suspend_phase1(adev);
+ r = amdgpu_device_ip_suspend_phase1(adev);
+ if (r)
+ goto unwind_smartshift;
amdgpu_amdkfd_suspend(adev, !amdgpu_sriov_vf(adev) && !adev->in_runpm);
- amdgpu_userq_suspend(adev);
+ r = amdgpu_userq_suspend(adev);
+ if (r)
+ goto unwind_ip_phase1;
r = amdgpu_device_evict_resources(adev);
if (r)
- return r;
+ goto unwind_userq;
amdgpu_ttm_set_buffer_funcs_status(adev, false);
amdgpu_fence_driver_hw_fini(adev);
- amdgpu_device_ip_suspend_phase2(adev);
+ r = amdgpu_device_ip_suspend_phase2(adev);
+ if (r)
+ goto unwind_evict;
if (amdgpu_sriov_vf(adev))
amdgpu_virt_release_full_gpu(adev, false);
- r = amdgpu_dpm_notify_rlc_state(adev, false);
- if (r)
+ return 0;
+
+unwind_evict:
+ if (adev->mman.buffer_funcs_ring->sched.ready)
+ amdgpu_ttm_set_buffer_funcs_status(adev, true);
+ amdgpu_fence_driver_hw_init(adev);
+
+unwind_userq:
+ rec = amdgpu_userq_resume(adev);
+ if (rec) {
+ dev_warn(adev->dev, "failed to re-initialize user queues: %d\n", rec);
return r;
+ }
+ rec = amdgpu_amdkfd_resume(adev, !amdgpu_sriov_vf(adev) && !adev->in_runpm);
+ if (rec) {
+ dev_warn(adev->dev, "failed to re-initialize kfd: %d\n", rec);
+ return r;
+ }
- return 0;
+unwind_ip_phase1:
+ /* suspend phase 1 = resume phase 3 */
+ rec = amdgpu_device_ip_resume_phase3(adev);
+ if (rec) {
+ dev_warn(adev->dev, "failed to re-initialize IPs phase1: %d\n", rec);
+ return r;
+ }
+
+unwind_smartshift:
+ rec = amdgpu_acpi_smart_shift_update(adev, AMDGPU_SS_DEV_D0);
+ if (rec) {
+ dev_warn(adev->dev, "failed to re-update smart shift: %d\n", rec);
+ return r;
+ }
+
+ if (notify_clients)
+ drm_client_dev_resume(adev_to_drm(adev));
+
+ amdgpu_ras_resume(adev);
+
+unwind_sriov:
+ if (amdgpu_sriov_vf(adev)) {
+ rec = amdgpu_virt_request_full_gpu(adev, true);
+ if (rec) {
+ dev_warn(adev->dev, "failed to reinitialize sriov: %d\n", rec);
+ return r;
+ }
+ }
+
+ adev->in_suspend = adev->in_s0ix = adev->in_s3 = false;
+
+ return r;
}
static inline int amdgpu_virt_resume(struct amdgpu_device *adev)
@@ -5353,7 +5487,7 @@ exit:
flush_delayed_work(&adev->delayed_init_work);
if (notify_clients)
- drm_client_dev_resume(adev_to_drm(adev), false);
+ drm_client_dev_resume(adev_to_drm(adev));
amdgpu_ras_resume(adev);
@@ -5809,11 +5943,6 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev,
if (!amdgpu_ring_sched_ready(ring))
continue;
- /* Clear job fence from fence drv to avoid force_completion
- * leave NULL and vm flush fence in fence drv
- */
- amdgpu_fence_driver_clear_job_fences(ring);
-
/* after all hw jobs are reset, hw fence is meaningless, so force_completion */
amdgpu_fence_driver_force_completion(ring);
}
@@ -5958,7 +6087,11 @@ int amdgpu_device_reinit_after_reset(struct amdgpu_reset_context *reset_context)
if (r)
goto out;
- drm_client_dev_resume(adev_to_drm(tmp_adev), false);
+ r = amdgpu_userq_post_reset(tmp_adev, vram_lost);
+ if (r)
+ goto out;
+
+ drm_client_dev_resume(adev_to_drm(tmp_adev));
/*
* The GPU enters bad state once faulty pages
@@ -6180,6 +6313,7 @@ static inline void amdgpu_device_stop_pending_resets(struct amdgpu_device *adev)
if (!amdgpu_sriov_vf(adev))
cancel_work(&adev->reset_work);
#endif
+ cancel_work(&adev->userq_reset_work);
if (adev->kfd.dev)
cancel_work(&adev->kfd.reset_work);
@@ -6293,13 +6427,15 @@ static void amdgpu_device_halt_activities(struct amdgpu_device *adev,
*/
amdgpu_unregister_gpu_instance(tmp_adev);
- drm_client_dev_suspend(adev_to_drm(tmp_adev), false);
+ drm_client_dev_suspend(adev_to_drm(tmp_adev));
/* disable ras on ALL IPs */
if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev) &&
amdgpu_device_ip_need_full_reset(tmp_adev))
amdgpu_ras_suspend(tmp_adev);
+ amdgpu_userq_pre_reset(tmp_adev);
+
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = tmp_adev->rings[i];
@@ -6529,6 +6665,9 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
goto end_reset;
}
+ /* Cannot be called after locking reset domain */
+ amdgpu_ras_pre_reset(adev, &device_list);
+
/* We need to lock reset domain only once both for XGMI and single device */
amdgpu_device_recovery_get_reset_lock(adev, &device_list);
@@ -6542,7 +6681,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
*
* job->base holds a reference to parent fence
*/
- if (job && dma_fence_is_signaled(&job->hw_fence.base)) {
+ if (job && dma_fence_is_signaled(&job->hw_fence->base)) {
job_signaled = true;
dev_info(adev->dev, "Guilty job already signaled, skipping HW reset");
goto skip_hw_reset;
@@ -6559,6 +6698,7 @@ skip_sched_resume:
amdgpu_device_gpu_resume(adev, &device_list, need_emergency_restart);
reset_unlock:
amdgpu_device_recovery_put_reset_lock(adev, &device_list);
+ amdgpu_ras_post_reset(adev, &device_list);
end_reset:
if (hive) {
mutex_unlock(&hive->hive_lock);
@@ -7286,10 +7426,17 @@ void amdgpu_device_flush_hdp(struct amdgpu_device *adev,
if (adev->gmc.xgmi.connected_to_cpu)
return;
- if (ring && ring->funcs->emit_hdp_flush)
+ if (ring && ring->funcs->emit_hdp_flush) {
amdgpu_ring_emit_hdp_flush(ring);
- else
- amdgpu_asic_flush_hdp(adev, ring);
+ return;
+ }
+
+ if (!ring && amdgpu_sriov_runtime(adev)) {
+ if (!amdgpu_kiq_hdp_flush(adev))
+ return;
+ }
+
+ amdgpu_hdp_flush(adev, ring);
}
void amdgpu_device_invalidate_hdp(struct amdgpu_device *adev,
@@ -7302,7 +7449,7 @@ void amdgpu_device_invalidate_hdp(struct amdgpu_device *adev,
if (adev->gmc.xgmi.connected_to_cpu)
return;
- amdgpu_asic_invalidate_hdp(adev, ring);
+ amdgpu_hdp_invalidate(adev, ring);
}
int amdgpu_in_reset(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
index dd7b2b796427..fa2a22dfa048 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c
@@ -107,6 +107,7 @@
#include "vcn_v5_0_1.h"
#include "jpeg_v5_0_0.h"
#include "jpeg_v5_0_1.h"
+#include "amdgpu_ras_mgr.h"
#include "amdgpu_vpe.h"
#if defined(CONFIG_DRM_AMD_ISP)
@@ -254,9 +255,9 @@ static int amdgpu_discovery_read_binary_from_sysmem(struct amdgpu_device *adev,
pos = tmr_offset + tmr_size - DISCOVERY_TMR_OFFSET;
/* This region is read-only and reserved from system use */
- discv_regn = memremap(pos, adev->mman.discovery_tmr_size, MEMREMAP_WC);
+ discv_regn = memremap(pos, adev->discovery.size, MEMREMAP_WC);
if (discv_regn) {
- memcpy(binary, discv_regn, adev->mman.discovery_tmr_size);
+ memcpy(binary, discv_regn, adev->discovery.size);
memunmap(discv_regn);
return 0;
}
@@ -298,10 +299,31 @@ static int amdgpu_discovery_read_binary_from_mem(struct amdgpu_device *adev,
else
vram_size <<= 20;
+ /*
+ * If in VRAM, discovery TMR is marked for reservation. If it is in system mem,
+ * then it is not required to be reserved.
+ */
if (sz_valid) {
- uint64_t pos = vram_size - DISCOVERY_TMR_OFFSET;
- amdgpu_device_vram_access(adev, pos, (uint32_t *)binary,
- adev->mman.discovery_tmr_size, false);
+ if (amdgpu_sriov_vf(adev) && adev->virt.is_dynamic_crit_regn_enabled) {
+ /* For SRIOV VFs with dynamic critical region enabled,
+ * we will get the IPD binary via below call.
+ * If dynamic critical is disabled, fall through to normal seq.
+ */
+ if (amdgpu_virt_get_dynamic_data_info(adev,
+ AMD_SRIOV_MSG_IPD_TABLE_ID, binary,
+ &adev->discovery.size)) {
+ dev_err(adev->dev,
+ "failed to read discovery info from dynamic critical region.");
+ ret = -EINVAL;
+ goto exit;
+ }
+ } else {
+ uint64_t pos = vram_size - DISCOVERY_TMR_OFFSET;
+
+ amdgpu_device_vram_access(adev, pos, (uint32_t *)binary,
+ adev->discovery.size, false);
+ adev->discovery.reserve_tmr = true;
+ }
} else {
ret = amdgpu_discovery_read_binary_from_sysmem(adev, binary);
}
@@ -310,7 +332,7 @@ static int amdgpu_discovery_read_binary_from_mem(struct amdgpu_device *adev,
dev_err(adev->dev,
"failed to read discovery info from memory, vram size read: %llx",
vram_size);
-
+exit:
return ret;
}
@@ -389,6 +411,7 @@ static void amdgpu_discovery_harvest_config_quirk(struct amdgpu_device *adev)
static int amdgpu_discovery_verify_npsinfo(struct amdgpu_device *adev,
struct binary_header *bhdr)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct table_info *info;
uint16_t checksum;
uint16_t offset;
@@ -398,14 +421,14 @@ static int amdgpu_discovery_verify_npsinfo(struct amdgpu_device *adev,
checksum = le16_to_cpu(info->checksum);
struct nps_info_header *nhdr =
- (struct nps_info_header *)(adev->mman.discovery_bin + offset);
+ (struct nps_info_header *)(discovery_bin + offset);
if (le32_to_cpu(nhdr->table_id) != NPS_INFO_TABLE_ID) {
dev_dbg(adev->dev, "invalid ip discovery nps info table id\n");
return -EINVAL;
}
- if (!amdgpu_discovery_verify_checksum(adev->mman.discovery_bin + offset,
+ if (!amdgpu_discovery_verify_checksum(discovery_bin + offset,
le32_to_cpu(nhdr->size_bytes),
checksum)) {
dev_dbg(adev->dev, "invalid nps info data table checksum\n");
@@ -417,8 +440,11 @@ static int amdgpu_discovery_verify_npsinfo(struct amdgpu_device *adev,
static const char *amdgpu_discovery_get_fw_name(struct amdgpu_device *adev)
{
- if (amdgpu_discovery == 2)
+ if (amdgpu_discovery == 2) {
+ /* Assume there is valid discovery TMR in VRAM even if binary is sideloaded */
+ adev->discovery.reserve_tmr = true;
return "amdgpu/ip_discovery.bin";
+ }
switch (adev->asic_type) {
case CHIP_VEGA10:
@@ -447,49 +473,53 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
{
struct table_info *info;
struct binary_header *bhdr;
+ uint8_t *discovery_bin;
const char *fw_name;
uint16_t offset;
uint16_t size;
uint16_t checksum;
int r;
- adev->mman.discovery_tmr_size = DISCOVERY_TMR_SIZE;
- adev->mman.discovery_bin = kzalloc(adev->mman.discovery_tmr_size, GFP_KERNEL);
- if (!adev->mman.discovery_bin)
+ adev->discovery.bin = kzalloc(DISCOVERY_TMR_SIZE, GFP_KERNEL);
+ if (!adev->discovery.bin)
return -ENOMEM;
+ adev->discovery.size = DISCOVERY_TMR_SIZE;
+ adev->discovery.debugfs_blob.data = adev->discovery.bin;
+ adev->discovery.debugfs_blob.size = adev->discovery.size;
+ discovery_bin = adev->discovery.bin;
/* Read from file if it is the preferred option */
fw_name = amdgpu_discovery_get_fw_name(adev);
if (fw_name != NULL) {
drm_dbg(&adev->ddev, "use ip discovery information from file");
- r = amdgpu_discovery_read_binary_from_file(adev, adev->mman.discovery_bin, fw_name);
+ r = amdgpu_discovery_read_binary_from_file(adev, discovery_bin,
+ fw_name);
if (r)
goto out;
} else {
drm_dbg(&adev->ddev, "use ip discovery information from memory");
- r = amdgpu_discovery_read_binary_from_mem(
- adev, adev->mman.discovery_bin);
+ r = amdgpu_discovery_read_binary_from_mem(adev, discovery_bin);
if (r)
goto out;
}
/* check the ip discovery binary signature */
- if (!amdgpu_discovery_verify_binary_signature(adev->mman.discovery_bin)) {
+ if (!amdgpu_discovery_verify_binary_signature(discovery_bin)) {
dev_err(adev->dev,
"get invalid ip discovery binary signature\n");
r = -EINVAL;
goto out;
}
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
+ bhdr = (struct binary_header *)discovery_bin;
offset = offsetof(struct binary_header, binary_checksum) +
sizeof(bhdr->binary_checksum);
size = le16_to_cpu(bhdr->binary_size) - offset;
checksum = le16_to_cpu(bhdr->binary_checksum);
- if (!amdgpu_discovery_verify_checksum(adev->mman.discovery_bin + offset,
- size, checksum)) {
+ if (!amdgpu_discovery_verify_checksum(discovery_bin + offset, size,
+ checksum)) {
dev_err(adev->dev, "invalid ip discovery binary checksum\n");
r = -EINVAL;
goto out;
@@ -501,15 +531,16 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
if (offset) {
struct ip_discovery_header *ihdr =
- (struct ip_discovery_header *)(adev->mman.discovery_bin + offset);
+ (struct ip_discovery_header *)(discovery_bin + offset);
if (le32_to_cpu(ihdr->signature) != DISCOVERY_TABLE_SIGNATURE) {
dev_err(adev->dev, "invalid ip discovery data table signature\n");
r = -EINVAL;
goto out;
}
- if (!amdgpu_discovery_verify_checksum(adev->mman.discovery_bin + offset,
- le16_to_cpu(ihdr->size), checksum)) {
+ if (!amdgpu_discovery_verify_checksum(discovery_bin + offset,
+ le16_to_cpu(ihdr->size),
+ checksum)) {
dev_err(adev->dev, "invalid ip discovery data table checksum\n");
r = -EINVAL;
goto out;
@@ -522,7 +553,7 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
if (offset) {
struct gpu_info_header *ghdr =
- (struct gpu_info_header *)(adev->mman.discovery_bin + offset);
+ (struct gpu_info_header *)(discovery_bin + offset);
if (le32_to_cpu(ghdr->table_id) != GC_TABLE_ID) {
dev_err(adev->dev, "invalid ip discovery gc table id\n");
@@ -530,8 +561,9 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
goto out;
}
- if (!amdgpu_discovery_verify_checksum(adev->mman.discovery_bin + offset,
- le32_to_cpu(ghdr->size), checksum)) {
+ if (!amdgpu_discovery_verify_checksum(discovery_bin + offset,
+ le32_to_cpu(ghdr->size),
+ checksum)) {
dev_err(adev->dev, "invalid gc data table checksum\n");
r = -EINVAL;
goto out;
@@ -544,7 +576,7 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
if (offset) {
struct harvest_info_header *hhdr =
- (struct harvest_info_header *)(adev->mman.discovery_bin + offset);
+ (struct harvest_info_header *)(discovery_bin + offset);
if (le32_to_cpu(hhdr->signature) != HARVEST_TABLE_SIGNATURE) {
dev_err(adev->dev, "invalid ip discovery harvest table signature\n");
@@ -552,8 +584,9 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
goto out;
}
- if (!amdgpu_discovery_verify_checksum(adev->mman.discovery_bin + offset,
- sizeof(struct harvest_table), checksum)) {
+ if (!amdgpu_discovery_verify_checksum(
+ discovery_bin + offset,
+ sizeof(struct harvest_table), checksum)) {
dev_err(adev->dev, "invalid harvest data table checksum\n");
r = -EINVAL;
goto out;
@@ -566,7 +599,7 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
if (offset) {
struct vcn_info_header *vhdr =
- (struct vcn_info_header *)(adev->mman.discovery_bin + offset);
+ (struct vcn_info_header *)(discovery_bin + offset);
if (le32_to_cpu(vhdr->table_id) != VCN_INFO_TABLE_ID) {
dev_err(adev->dev, "invalid ip discovery vcn table id\n");
@@ -574,8 +607,9 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
goto out;
}
- if (!amdgpu_discovery_verify_checksum(adev->mman.discovery_bin + offset,
- le32_to_cpu(vhdr->size_bytes), checksum)) {
+ if (!amdgpu_discovery_verify_checksum(
+ discovery_bin + offset,
+ le32_to_cpu(vhdr->size_bytes), checksum)) {
dev_err(adev->dev, "invalid vcn data table checksum\n");
r = -EINVAL;
goto out;
@@ -588,7 +622,7 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
if (0 && offset) {
struct mall_info_header *mhdr =
- (struct mall_info_header *)(adev->mman.discovery_bin + offset);
+ (struct mall_info_header *)(discovery_bin + offset);
if (le32_to_cpu(mhdr->table_id) != MALL_INFO_TABLE_ID) {
dev_err(adev->dev, "invalid ip discovery mall table id\n");
@@ -596,8 +630,9 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
goto out;
}
- if (!amdgpu_discovery_verify_checksum(adev->mman.discovery_bin + offset,
- le32_to_cpu(mhdr->size_bytes), checksum)) {
+ if (!amdgpu_discovery_verify_checksum(
+ discovery_bin + offset,
+ le32_to_cpu(mhdr->size_bytes), checksum)) {
dev_err(adev->dev, "invalid mall data table checksum\n");
r = -EINVAL;
goto out;
@@ -607,8 +642,8 @@ static int amdgpu_discovery_init(struct amdgpu_device *adev)
return 0;
out:
- kfree(adev->mman.discovery_bin);
- adev->mman.discovery_bin = NULL;
+ kfree(adev->discovery.bin);
+ adev->discovery.bin = NULL;
if ((amdgpu_discovery != 2) &&
(RREG32(mmIP_DISCOVERY_VERSION) == 4))
amdgpu_ras_query_boot_status(adev, 4);
@@ -620,8 +655,8 @@ static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev);
void amdgpu_discovery_fini(struct amdgpu_device *adev)
{
amdgpu_discovery_sysfs_fini(adev);
- kfree(adev->mman.discovery_bin);
- adev->mman.discovery_bin = NULL;
+ kfree(adev->discovery.bin);
+ adev->discovery.bin = NULL;
}
static int amdgpu_discovery_validate_ip(struct amdgpu_device *adev,
@@ -646,6 +681,7 @@ static int amdgpu_discovery_validate_ip(struct amdgpu_device *adev,
static void amdgpu_discovery_read_harvest_bit_per_ip(struct amdgpu_device *adev,
uint32_t *vcn_harvest_count)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct binary_header *bhdr;
struct ip_discovery_header *ihdr;
struct die_header *dhdr;
@@ -655,21 +691,21 @@ static void amdgpu_discovery_read_harvest_bit_per_ip(struct amdgpu_device *adev,
uint8_t inst;
int i, j;
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
- ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +
- le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
+ bhdr = (struct binary_header *)discovery_bin;
+ ihdr = (struct ip_discovery_header
+ *)(discovery_bin +
+ le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
num_dies = le16_to_cpu(ihdr->num_dies);
/* scan harvest bit of all IP data structures */
for (i = 0; i < num_dies; i++) {
die_offset = le16_to_cpu(ihdr->die_info[i].die_offset);
- dhdr = (struct die_header *)(adev->mman.discovery_bin + die_offset);
+ dhdr = (struct die_header *)(discovery_bin + die_offset);
num_ips = le16_to_cpu(dhdr->num_ips);
ip_offset = die_offset + sizeof(*dhdr);
for (j = 0; j < num_ips; j++) {
- ip = (struct ip *)(adev->mman.discovery_bin +
- ip_offset);
+ ip = (struct ip *)(discovery_bin + ip_offset);
inst = ip->number_instance;
hw_id = le16_to_cpu(ip->hw_id);
if (amdgpu_discovery_validate_ip(adev, inst, hw_id))
@@ -711,13 +747,14 @@ static void amdgpu_discovery_read_from_harvest_table(struct amdgpu_device *adev,
uint32_t *vcn_harvest_count,
uint32_t *umc_harvest_count)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct binary_header *bhdr;
struct harvest_table *harvest_info;
u16 offset;
int i;
uint32_t umc_harvest_config = 0;
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
+ bhdr = (struct binary_header *)discovery_bin;
offset = le16_to_cpu(bhdr->table_list[HARVEST_INFO].offset);
if (!offset) {
@@ -725,7 +762,7 @@ static void amdgpu_discovery_read_from_harvest_table(struct amdgpu_device *adev,
return;
}
- harvest_info = (struct harvest_table *)(adev->mman.discovery_bin + offset);
+ harvest_info = (struct harvest_table *)(discovery_bin + offset);
for (i = 0; i < 32; i++) {
if (le16_to_cpu(harvest_info->list[i].hw_id) == 0)
@@ -1021,8 +1058,8 @@ static void ip_disc_release(struct kobject *kobj)
kobj);
struct amdgpu_device *adev = ip_top->adev;
- adev->ip_top = NULL;
kfree(ip_top);
+ adev->discovery.ip_top = NULL;
}
static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev,
@@ -1062,6 +1099,7 @@ static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
const size_t _ip_offset, const int num_ips,
bool reg_base_64)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
int ii, jj, kk, res;
uint16_t hw_id;
uint8_t inst;
@@ -1079,7 +1117,7 @@ static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev,
struct ip_v4 *ip;
struct ip_hw_instance *ip_hw_instance;
- ip = (struct ip_v4 *)(adev->mman.discovery_bin + ip_offset);
+ ip = (struct ip_v4 *)(discovery_bin + ip_offset);
inst = ip->instance_number;
hw_id = le16_to_cpu(ip->hw_id);
if (amdgpu_discovery_validate_ip(adev, inst, hw_id) ||
@@ -1166,17 +1204,20 @@ next_ip:
static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
{
+ struct ip_discovery_top *ip_top = adev->discovery.ip_top;
+ uint8_t *discovery_bin = adev->discovery.bin;
struct binary_header *bhdr;
struct ip_discovery_header *ihdr;
struct die_header *dhdr;
- struct kset *die_kset = &adev->ip_top->die_kset;
+ struct kset *die_kset = &ip_top->die_kset;
u16 num_dies, die_offset, num_ips;
size_t ip_offset;
int ii, res;
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
- ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +
- le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
+ bhdr = (struct binary_header *)discovery_bin;
+ ihdr = (struct ip_discovery_header
+ *)(discovery_bin +
+ le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
num_dies = le16_to_cpu(ihdr->num_dies);
DRM_DEBUG("number of dies: %d\n", num_dies);
@@ -1185,7 +1226,7 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
struct ip_die_entry *ip_die_entry;
die_offset = le16_to_cpu(ihdr->die_info[ii].die_offset);
- dhdr = (struct die_header *)(adev->mman.discovery_bin + die_offset);
+ dhdr = (struct die_header *)(discovery_bin + die_offset);
num_ips = le16_to_cpu(dhdr->num_ips);
ip_offset = die_offset + sizeof(*dhdr);
@@ -1219,30 +1260,32 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev)
static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
+ struct ip_discovery_top *ip_top;
struct kset *die_kset;
int res, ii;
- if (!adev->mman.discovery_bin)
+ if (!discovery_bin)
return -EINVAL;
- adev->ip_top = kzalloc(sizeof(*adev->ip_top), GFP_KERNEL);
- if (!adev->ip_top)
+ ip_top = kzalloc(sizeof(*ip_top), GFP_KERNEL);
+ if (!ip_top)
return -ENOMEM;
- adev->ip_top->adev = adev;
-
- res = kobject_init_and_add(&adev->ip_top->kobj, &ip_discovery_ktype,
+ ip_top->adev = adev;
+ adev->discovery.ip_top = ip_top;
+ res = kobject_init_and_add(&ip_top->kobj, &ip_discovery_ktype,
&adev->dev->kobj, "ip_discovery");
if (res) {
DRM_ERROR("Couldn't init and add ip_discovery/");
goto Err;
}
- die_kset = &adev->ip_top->die_kset;
+ die_kset = &ip_top->die_kset;
kobject_set_name(&die_kset->kobj, "%s", "die");
- die_kset->kobj.parent = &adev->ip_top->kobj;
+ die_kset->kobj.parent = &ip_top->kobj;
die_kset->kobj.ktype = &die_kobj_ktype;
- res = kset_register(&adev->ip_top->die_kset);
+ res = kset_register(&ip_top->die_kset);
if (res) {
DRM_ERROR("Couldn't register die_kset");
goto Err;
@@ -1256,7 +1299,7 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev)
return res;
Err:
- kobject_put(&adev->ip_top->kobj);
+ kobject_put(&ip_top->kobj);
return res;
}
@@ -1301,10 +1344,11 @@ static void amdgpu_discovery_sysfs_die_free(struct ip_die_entry *ip_die_entry)
static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
{
+ struct ip_discovery_top *ip_top = adev->discovery.ip_top;
struct list_head *el, *tmp;
struct kset *die_kset;
- die_kset = &adev->ip_top->die_kset;
+ die_kset = &ip_top->die_kset;
spin_lock(&die_kset->list_lock);
list_for_each_prev_safe(el, tmp, &die_kset->list) {
list_del_init(el);
@@ -1313,8 +1357,8 @@ static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev)
spin_lock(&die_kset->list_lock);
}
spin_unlock(&die_kset->list_lock);
- kobject_put(&adev->ip_top->die_kset.kobj);
- kobject_put(&adev->ip_top->kobj);
+ kobject_put(&ip_top->die_kset.kobj);
+ kobject_put(&ip_top->kobj);
}
/* ================================================== */
@@ -1325,6 +1369,7 @@ static int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
struct binary_header *bhdr;
struct ip_discovery_header *ihdr;
struct die_header *dhdr;
+ uint8_t *discovery_bin;
struct ip_v4 *ip;
uint16_t die_offset;
uint16_t ip_offset;
@@ -1340,22 +1385,23 @@ static int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
r = amdgpu_discovery_init(adev);
if (r)
return r;
-
+ discovery_bin = adev->discovery.bin;
wafl_ver = 0;
adev->gfx.xcc_mask = 0;
adev->sdma.sdma_mask = 0;
adev->vcn.inst_mask = 0;
adev->jpeg.inst_mask = 0;
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
- ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +
- le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
+ bhdr = (struct binary_header *)discovery_bin;
+ ihdr = (struct ip_discovery_header
+ *)(discovery_bin +
+ le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset));
num_dies = le16_to_cpu(ihdr->num_dies);
DRM_DEBUG("number of dies: %d\n", num_dies);
for (i = 0; i < num_dies; i++) {
die_offset = le16_to_cpu(ihdr->die_info[i].die_offset);
- dhdr = (struct die_header *)(adev->mman.discovery_bin + die_offset);
+ dhdr = (struct die_header *)(discovery_bin + die_offset);
num_ips = le16_to_cpu(dhdr->num_ips);
ip_offset = die_offset + sizeof(*dhdr);
@@ -1369,7 +1415,7 @@ static int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev)
le16_to_cpu(dhdr->die_id), num_ips);
for (j = 0; j < num_ips; j++) {
- ip = (struct ip_v4 *)(adev->mman.discovery_bin + ip_offset);
+ ip = (struct ip_v4 *)(discovery_bin + ip_offset);
inst = ip->instance_number;
hw_id = le16_to_cpu(ip->hw_id);
@@ -1519,16 +1565,16 @@ next_ip:
static void amdgpu_discovery_harvest_ip(struct amdgpu_device *adev)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct ip_discovery_header *ihdr;
struct binary_header *bhdr;
int vcn_harvest_count = 0;
int umc_harvest_count = 0;
uint16_t offset, ihdr_ver;
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
+ bhdr = (struct binary_header *)discovery_bin;
offset = le16_to_cpu(bhdr->table_list[IP_DISCOVERY].offset);
- ihdr = (struct ip_discovery_header *)(adev->mman.discovery_bin +
- offset);
+ ihdr = (struct ip_discovery_header *)(discovery_bin + offset);
ihdr_ver = le16_to_cpu(ihdr->version);
/*
* Harvest table does not fit Navi1x and legacy GPUs,
@@ -1575,22 +1621,23 @@ union gc_info {
static int amdgpu_discovery_get_gfx_info(struct amdgpu_device *adev)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct binary_header *bhdr;
union gc_info *gc_info;
u16 offset;
- if (!adev->mman.discovery_bin) {
+ if (!discovery_bin) {
DRM_ERROR("ip discovery uninitialized\n");
return -EINVAL;
}
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
+ bhdr = (struct binary_header *)discovery_bin;
offset = le16_to_cpu(bhdr->table_list[GC].offset);
if (!offset)
return 0;
- gc_info = (union gc_info *)(adev->mman.discovery_bin + offset);
+ gc_info = (union gc_info *)(discovery_bin + offset);
switch (le16_to_cpu(gc_info->v1.header.version_major)) {
case 1:
@@ -1683,24 +1730,25 @@ union mall_info {
static int amdgpu_discovery_get_mall_info(struct amdgpu_device *adev)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct binary_header *bhdr;
union mall_info *mall_info;
u32 u, mall_size_per_umc, m_s_present, half_use;
u64 mall_size;
u16 offset;
- if (!adev->mman.discovery_bin) {
+ if (!discovery_bin) {
DRM_ERROR("ip discovery uninitialized\n");
return -EINVAL;
}
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
+ bhdr = (struct binary_header *)discovery_bin;
offset = le16_to_cpu(bhdr->table_list[MALL_INFO].offset);
if (!offset)
return 0;
- mall_info = (union mall_info *)(adev->mman.discovery_bin + offset);
+ mall_info = (union mall_info *)(discovery_bin + offset);
switch (le16_to_cpu(mall_info->v1.header.version_major)) {
case 1:
@@ -1739,12 +1787,13 @@ union vcn_info {
static int amdgpu_discovery_get_vcn_info(struct amdgpu_device *adev)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct binary_header *bhdr;
union vcn_info *vcn_info;
u16 offset;
int v;
- if (!adev->mman.discovery_bin) {
+ if (!discovery_bin) {
DRM_ERROR("ip discovery uninitialized\n");
return -EINVAL;
}
@@ -1759,13 +1808,13 @@ static int amdgpu_discovery_get_vcn_info(struct amdgpu_device *adev)
return -EINVAL;
}
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
+ bhdr = (struct binary_header *)discovery_bin;
offset = le16_to_cpu(bhdr->table_list[VCN_INFO].offset);
if (!offset)
return 0;
- vcn_info = (union vcn_info *)(adev->mman.discovery_bin + offset);
+ vcn_info = (union vcn_info *)(discovery_bin + offset);
switch (le16_to_cpu(vcn_info->v1.header.version_major)) {
case 1:
@@ -1825,6 +1874,7 @@ int amdgpu_discovery_get_nps_info(struct amdgpu_device *adev,
struct amdgpu_gmc_memrange **ranges,
int *range_cnt, bool refresh)
{
+ uint8_t *discovery_bin = adev->discovery.bin;
struct amdgpu_gmc_memrange *mem_ranges;
struct binary_header *bhdr;
union nps_info *nps_info;
@@ -1841,13 +1891,13 @@ int amdgpu_discovery_get_nps_info(struct amdgpu_device *adev,
return r;
nps_info = &nps_data;
} else {
- if (!adev->mman.discovery_bin) {
+ if (!discovery_bin) {
dev_err(adev->dev,
"fetch mem range failed, ip discovery uninitialized\n");
return -EINVAL;
}
- bhdr = (struct binary_header *)adev->mman.discovery_bin;
+ bhdr = (struct binary_header *)discovery_bin;
offset = le16_to_cpu(bhdr->table_list[NPS_INFO].offset);
if (!offset)
@@ -1857,8 +1907,7 @@ int amdgpu_discovery_get_nps_info(struct amdgpu_device *adev,
if (amdgpu_discovery_verify_npsinfo(adev, bhdr))
return -ENOENT;
- nps_info =
- (union nps_info *)(adev->mman.discovery_bin + offset);
+ nps_info = (union nps_info *)(discovery_bin + offset);
}
switch (le16_to_cpu(nps_info->v1.header.version_major)) {
@@ -2361,6 +2410,21 @@ static int amdgpu_discovery_set_sdma_ip_blocks(struct amdgpu_device *adev)
amdgpu_ip_version(adev, SDMA0_HWIP, 0));
return -EINVAL;
}
+
+ return 0;
+}
+
+static int amdgpu_discovery_set_ras_ip_blocks(struct amdgpu_device *adev)
+{
+ switch (amdgpu_ip_version(adev, MP0_HWIP, 0)) {
+ case IP_VERSION(13, 0, 6):
+ case IP_VERSION(13, 0, 12):
+ case IP_VERSION(13, 0, 14):
+ amdgpu_device_ip_block_add(adev, &ras_v1_0_ip_block);
+ break;
+ default:
+ break;
+ }
return 0;
}
@@ -3141,6 +3205,10 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev)
if (r)
return r;
+ r = amdgpu_discovery_set_ras_ip_blocks(adev);
+ if (r)
+ return r;
+
if ((adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT &&
!amdgpu_sriov_vf(adev)) ||
(adev->firmware.load_type == AMDGPU_FW_LOAD_RLC_BACKDOOR_AUTO && amdgpu_dpm == 1)) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
index b44d56465c5b..4ce04486cc31 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h
@@ -24,9 +24,21 @@
#ifndef __AMDGPU_DISCOVERY__
#define __AMDGPU_DISCOVERY__
+#include <linux/debugfs.h>
+
#define DISCOVERY_TMR_SIZE (10 << 10)
#define DISCOVERY_TMR_OFFSET (64 << 10)
+struct ip_discovery_top;
+
+struct amdgpu_discovery_info {
+ struct debugfs_blob_wrapper debugfs_blob;
+ struct ip_discovery_top *ip_top;
+ uint32_t size;
+ uint8_t *bin;
+ bool reserve_tmr;
+};
+
void amdgpu_discovery_fini(struct amdgpu_device *adev);
int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index 51bab32fd8c6..b5d34797d606 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -332,8 +332,6 @@ int amdgpu_display_crtc_set_config(struct drm_mode_set *set,
if (crtc->enabled)
active = true;
- pm_runtime_mark_last_busy(dev->dev);
-
adev = drm_to_adev(dev);
/* if we have active crtcs and we don't have a power ref,
* take the current one
@@ -1365,6 +1363,64 @@ static const struct drm_prop_enum_list amdgpu_dither_enum_list[] = {
{ AMDGPU_FMT_DITHER_ENABLE, "on" },
};
+/**
+ * DOC: property for adaptive backlight modulation
+ *
+ * The 'adaptive backlight modulation' property is used for the compositor to
+ * directly control the adaptive backlight modulation power savings feature
+ * that is part of DCN hardware.
+ *
+ * The property will be attached specifically to eDP panels that support it.
+ *
+ * The property is by default set to 'sysfs' to allow the sysfs file 'panel_power_savings'
+ * to be able to control it.
+ * If set to 'off' the compositor will ensure it stays off.
+ * The other values 'min', 'bias min', 'bias max', and 'max' will control the
+ * intensity of the power savings.
+ *
+ * Modifying this value can have implications on color accuracy, so tread
+ * carefully.
+ */
+static int amdgpu_display_setup_abm_prop(struct amdgpu_device *adev)
+{
+ const struct drm_prop_enum_list props[] = {
+ { ABM_SYSFS_CONTROL, "sysfs" },
+ { ABM_LEVEL_OFF, "off" },
+ { ABM_LEVEL_MIN, "min" },
+ { ABM_LEVEL_BIAS_MIN, "bias min" },
+ { ABM_LEVEL_BIAS_MAX, "bias max" },
+ { ABM_LEVEL_MAX, "max" },
+ };
+ struct drm_property *prop;
+ int i;
+
+ if (!adev->dc_enabled)
+ return 0;
+
+ prop = drm_property_create(adev_to_drm(adev), DRM_MODE_PROP_ENUM,
+ "adaptive backlight modulation",
+ 6);
+ if (!prop)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(props); i++) {
+ int ret;
+
+ ret = drm_property_add_enum(prop, props[i].type,
+ props[i].name);
+
+ if (ret) {
+ drm_property_destroy(adev_to_drm(adev), prop);
+
+ return ret;
+ }
+ }
+
+ adev->mode_info.abm_level_property = prop;
+
+ return 0;
+}
+
int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
{
int sz;
@@ -1411,7 +1467,7 @@ int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
"dither",
amdgpu_dither_enum_list, sz);
- return 0;
+ return amdgpu_display_setup_abm_prop(adev);
}
void amdgpu_display_update_priority(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
index 930c171473b4..49a29bf47a37 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
@@ -55,4 +55,11 @@ int amdgpu_display_resume_helper(struct amdgpu_device *adev);
int amdgpu_display_get_scanout_buffer(struct drm_plane *plane,
struct drm_scanout_buffer *sb);
+#define ABM_SYSFS_CONTROL -1
+#define ABM_LEVEL_OFF 0
+#define ABM_LEVEL_MIN 1
+#define ABM_LEVEL_BIAS_MIN 2
+#define ABM_LEVEL_BIAS_MAX 3
+#define ABM_LEVEL_MAX 4
+
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
index 8561ad7f6180..e22cfa7c6d32 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c
@@ -81,13 +81,44 @@ static int amdgpu_dma_buf_attach(struct dma_buf *dmabuf,
struct drm_gem_object *obj = dmabuf->priv;
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
+ int r;
+
+ /*
+ * Disable peer-to-peer access for DCC-enabled VRAM surfaces on GFX12+.
+ * Such buffers cannot be safely accessed over P2P due to device-local
+ * compression metadata. Fallback to system-memory path instead.
+ * Device supports GFX12 (GC 12.x or newer)
+ * BO was created with the AMDGPU_GEM_CREATE_GFX12_DCC flag
+ *
+ */
+ if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(12, 0, 0) &&
+ bo->flags & AMDGPU_GEM_CREATE_GFX12_DCC)
+ attach->peer2peer = false;
+
+ /*
+ * Disable peer-to-peer access for DCC-enabled VRAM surfaces on GFX12+.
+ * Such buffers cannot be safely accessed over P2P due to device-local
+ * compression metadata. Fallback to system-memory path instead.
+ * Device supports GFX12 (GC 12.x or newer)
+ * BO was created with the AMDGPU_GEM_CREATE_GFX12_DCC flag
+ *
+ */
+ if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(12, 0, 0) &&
+ bo->flags & AMDGPU_GEM_CREATE_GFX12_DCC)
+ attach->peer2peer = false;
if (!amdgpu_dmabuf_is_xgmi_accessible(attach_adev, bo) &&
pci_p2pdma_distance(adev->pdev, attach->dev, false) < 0)
attach->peer2peer = false;
+ r = dma_resv_lock(bo->tbo.base.resv, NULL);
+ if (r)
+ return r;
+
amdgpu_vm_bo_update_shared(bo);
+ dma_resv_unlock(bo->tbo.base.resv);
+
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 61268aa82df4..2dfbddcef9ab 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -312,7 +312,7 @@ module_param_named(moverate, amdgpu_moverate, int, 0600);
* DOC: audio (int)
* Set HDMI/DPAudio. Only affects non-DC display handling. The default is -1 (Enabled), set 0 to disabled it.
*/
-MODULE_PARM_DESC(audio, "Audio enable (-1 = auto, 0 = disable, 1 = enable)");
+MODULE_PARM_DESC(audio, "HDMI/DP Audio enable for non DC displays (-1 = auto, 0 = disable, 1 = enable)");
module_param_named(audio, amdgpu_audio, int, 0444);
/**
@@ -354,22 +354,16 @@ module_param_named(svm_default_granularity, amdgpu_svm_default_granularity, uint
* DOC: lockup_timeout (string)
* Set GPU scheduler timeout value in ms.
*
- * The format can be [Non-Compute] or [GFX,Compute,SDMA,Video]. That is there can be one or
- * multiple values specified. 0 and negative values are invalidated. They will be adjusted
- * to the default timeout.
+ * The format can be [single value] for setting all timeouts at once or
+ * [GFX,Compute,SDMA,Video] to set individual timeouts.
+ * Negative values mean infinity.
*
- * - With one value specified, the setting will apply to all non-compute jobs.
- * - With multiple values specified, the first one will be for GFX.
- * The second one is for Compute. The third and fourth ones are
- * for SDMA and Video.
- *
- * By default(with no lockup_timeout settings), the timeout for all jobs is 10000.
+ * By default(with no lockup_timeout settings), the timeout for all queues is 2000.
*/
MODULE_PARM_DESC(lockup_timeout,
- "GPU lockup timeout in ms (default: 10000 for all jobs. "
- "0: keep default value. negative: infinity timeout), format: for bare metal [Non-Compute] or [GFX,Compute,SDMA,Video]; "
- "for passthrough or sriov [all jobs] or [GFX,Compute,SDMA,Video].");
-module_param_string(lockup_timeout, amdgpu_lockup_timeout, sizeof(amdgpu_lockup_timeout), 0444);
+ "GPU lockup timeout in ms (default: 2000. 0: keep default value. negative: infinity timeout), format: [single value for all] or [GFX,Compute,SDMA,Video].");
+module_param_string(lockup_timeout, amdgpu_lockup_timeout,
+ sizeof(amdgpu_lockup_timeout), 0444);
/**
* DOC: dpm (int)
@@ -624,39 +618,39 @@ module_param_named(timeout_period, amdgpu_watchdog_timer.period, uint, 0644);
/**
* DOC: si_support (int)
- * Set SI support driver. This parameter works after set config CONFIG_DRM_AMDGPU_SI. For SI asic, when radeon driver is enabled,
- * set value 0 to use radeon driver, while set value 1 to use amdgpu driver. The default is using radeon driver when it available,
- * otherwise using amdgpu driver.
- */
+ * 1 = enabled, 0 = disabled, -1 = default
+ *
+ * SI (Southern Islands) are first generation GCN GPUs, supported by both
+ * drivers: radeon (old) and amdgpu (new). This parameter controls whether
+ * amdgpu should support SI.
+ * By default, SI dedicated GPUs are supported by amdgpu.
+ * Only relevant when CONFIG_DRM_AMDGPU_SI is enabled to build SI support in amdgpu.
+ * See also radeon.si_support which should be disabled when amdgpu.si_support is
+ * enabled, and vice versa.
+ */
+int amdgpu_si_support = -1;
#ifdef CONFIG_DRM_AMDGPU_SI
-
-#if IS_ENABLED(CONFIG_DRM_RADEON) || IS_ENABLED(CONFIG_DRM_RADEON_MODULE)
-int amdgpu_si_support;
-MODULE_PARM_DESC(si_support, "SI support (1 = enabled, 0 = disabled (default))");
-#else
-int amdgpu_si_support = 1;
-MODULE_PARM_DESC(si_support, "SI support (1 = enabled (default), 0 = disabled)");
-#endif
-
+MODULE_PARM_DESC(si_support, "SI support (1 = enabled, 0 = disabled, -1 = default)");
module_param_named(si_support, amdgpu_si_support, int, 0444);
#endif
/**
* DOC: cik_support (int)
- * Set CIK support driver. This parameter works after set config CONFIG_DRM_AMDGPU_CIK. For CIK asic, when radeon driver is enabled,
- * set value 0 to use radeon driver, while set value 1 to use amdgpu driver. The default is using radeon driver when it available,
- * otherwise using amdgpu driver.
- */
+ * 1 = enabled, 0 = disabled, -1 = default
+ *
+ * CIK (Sea Islands) are second generation GCN GPUs, supported by both
+ * drivers: radeon (old) and amdgpu (new). This parameter controls whether
+ * amdgpu should support CIK.
+ * By default:
+ * - CIK dedicated GPUs are supported by amdgpu.
+ * - CIK APUs are supported by radeon (except when radeon is not built).
+ * Only relevant when CONFIG_DRM_AMDGPU_CIK is enabled to build CIK support in amdgpu.
+ * See also radeon.cik_support which should be disabled when amdgpu.cik_support is
+ * enabled, and vice versa.
+ */
+int amdgpu_cik_support = -1;
#ifdef CONFIG_DRM_AMDGPU_CIK
-
-#if IS_ENABLED(CONFIG_DRM_RADEON) || IS_ENABLED(CONFIG_DRM_RADEON_MODULE)
-int amdgpu_cik_support;
-MODULE_PARM_DESC(cik_support, "CIK support (1 = enabled, 0 = disabled (default))");
-#else
-int amdgpu_cik_support = 1;
-MODULE_PARM_DESC(cik_support, "CIK support (1 = enabled (default), 0 = disabled)");
-#endif
-
+MODULE_PARM_DESC(cik_support, "CIK support (1 = enabled, 0 = disabled, -1 = default)");
module_param_named(cik_support, amdgpu_cik_support, int, 0444);
#endif
@@ -2234,7 +2228,6 @@ static void amdgpu_get_secondary_funcs(struct amdgpu_device *adev)
adev->pdev->bus->number, i);
if (p) {
pm_runtime_get_sync(&p->dev);
- pm_runtime_mark_last_busy(&p->dev);
pm_runtime_put_autosuspend(&p->dev);
pci_dev_put(p);
}
@@ -2313,6 +2306,72 @@ static unsigned long amdgpu_fix_asic_type(struct pci_dev *pdev, unsigned long fl
return flags;
}
+static bool amdgpu_support_enabled(struct device *dev,
+ const enum amd_asic_type family)
+{
+ const char *gen;
+ const char *param;
+ int module_param = -1;
+ bool radeon_support_built = IS_ENABLED(CONFIG_DRM_RADEON);
+ bool amdgpu_support_built = false;
+ bool support_by_default = false;
+
+ switch (family) {
+ case CHIP_TAHITI:
+ case CHIP_PITCAIRN:
+ case CHIP_VERDE:
+ case CHIP_OLAND:
+ case CHIP_HAINAN:
+ gen = "SI";
+ param = "si_support";
+ module_param = amdgpu_si_support;
+ amdgpu_support_built = IS_ENABLED(CONFIG_DRM_AMDGPU_SI);
+ support_by_default = true;
+ break;
+
+ case CHIP_BONAIRE:
+ case CHIP_HAWAII:
+ support_by_default = true;
+ fallthrough;
+ case CHIP_KAVERI:
+ case CHIP_KABINI:
+ case CHIP_MULLINS:
+ gen = "CIK";
+ param = "cik_support";
+ module_param = amdgpu_cik_support;
+ amdgpu_support_built = IS_ENABLED(CONFIG_DRM_AMDGPU_CIK);
+ break;
+
+ default:
+ /* All other chips are supported by amdgpu only */
+ return true;
+ }
+
+ if (!amdgpu_support_built) {
+ dev_info(dev, "amdgpu built without %s support\n", gen);
+ return false;
+ }
+
+ if ((module_param == -1 && (support_by_default || !radeon_support_built)) ||
+ module_param == 1) {
+ if (radeon_support_built)
+ dev_info(dev, "%s support provided by amdgpu.\n"
+ "Use radeon.%s=1 amdgpu.%s=0 to override.\n",
+ gen, param, param);
+
+ return true;
+ }
+
+ if (radeon_support_built)
+ dev_info(dev, "%s support provided by radeon.\n"
+ "Use radeon.%s=0 amdgpu.%s=1 to override.\n",
+ gen, param, param);
+ else if (module_param == 0)
+ dev_info(dev, "%s support disabled by module param\n", gen);
+
+ return false;
+}
+
static int amdgpu_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -2360,48 +2419,8 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
return -ENOTSUPP;
}
- switch (flags & AMD_ASIC_MASK) {
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- case CHIP_OLAND:
- case CHIP_HAINAN:
-#ifdef CONFIG_DRM_AMDGPU_SI
- if (!amdgpu_si_support) {
- dev_info(&pdev->dev,
- "SI support provided by radeon.\n");
- dev_info(&pdev->dev,
- "Use radeon.si_support=0 amdgpu.si_support=1 to override.\n"
- );
- return -ENODEV;
- }
- break;
-#else
- dev_info(&pdev->dev, "amdgpu is built without SI support.\n");
+ if (!amdgpu_support_enabled(&pdev->dev, flags & AMD_ASIC_MASK))
return -ENODEV;
-#endif
- case CHIP_KAVERI:
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- case CHIP_KABINI:
- case CHIP_MULLINS:
-#ifdef CONFIG_DRM_AMDGPU_CIK
- if (!amdgpu_cik_support) {
- dev_info(&pdev->dev,
- "CIK support provided by radeon.\n");
- dev_info(&pdev->dev,
- "Use radeon.cik_support=0 amdgpu.cik_support=1 to override.\n"
- );
- return -ENODEV;
- }
- break;
-#else
- dev_info(&pdev->dev, "amdgpu is built without CIK support.\n");
- return -ENODEV;
-#endif
- default:
- break;
- }
adev = devm_drm_dev_alloc(&pdev->dev, &amdgpu_kms_driver, typeof(*adev), ddev);
if (IS_ERR(adev))
@@ -2480,7 +2499,6 @@ retry_init:
pm_runtime_allow(ddev->dev);
- pm_runtime_mark_last_busy(ddev->dev);
pm_runtime_put_autosuspend(ddev->dev);
pci_wake_from_d3(pdev, TRUE);
@@ -2564,7 +2582,8 @@ amdgpu_pci_shutdown(struct pci_dev *pdev)
*/
if (!amdgpu_passthrough(adev))
adev->mp1_state = PP_MP1_STATE_UNLOAD;
- amdgpu_device_ip_suspend(adev);
+ amdgpu_device_prepare(dev);
+ amdgpu_device_suspend(dev, true);
adev->mp1_state = PP_MP1_STATE_NONE;
}
@@ -2632,9 +2651,14 @@ static int amdgpu_pmops_suspend_noirq(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(drm_dev);
+ int r;
- if (amdgpu_acpi_should_gpu_reset(adev))
- return amdgpu_asic_reset(adev);
+ if (amdgpu_acpi_should_gpu_reset(adev)) {
+ amdgpu_device_lock_reset_domain(adev->reset_domain);
+ r = amdgpu_asic_reset(adev);
+ amdgpu_device_unlock_reset_domain(adev->reset_domain);
+ return r;
+ }
return 0;
}
@@ -2777,22 +2801,8 @@ static int amdgpu_runtime_idle_check_userq(struct device *dev)
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
struct amdgpu_device *adev = drm_to_adev(drm_dev);
- struct amdgpu_usermode_queue *queue;
- struct amdgpu_userq_mgr *uqm, *tmp;
- int queue_id;
- int ret = 0;
-
- mutex_lock(&adev->userq_mutex);
- list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) {
- idr_for_each_entry(&uqm->userq_idr, queue, queue_id) {
- ret = -EBUSY;
- goto done;
- }
- }
-done:
- mutex_unlock(&adev->userq_mutex);
- return ret;
+ return xa_empty(&adev->userq_doorbell_xa) ? 0 : -EBUSY;
}
static int amdgpu_pmops_runtime_suspend(struct device *dev)
@@ -2939,7 +2949,6 @@ static int amdgpu_pmops_runtime_idle(struct device *dev)
ret = amdgpu_runtime_idle_check_userq(dev);
done:
- pm_runtime_mark_last_busy(dev);
pm_runtime_autosuspend(dev);
return ret;
}
@@ -2975,7 +2984,6 @@ long amdgpu_drm_ioctl(struct file *filp,
ret = drm_ioctl(filp, cmd, arg);
- pm_runtime_mark_last_busy(dev->dev);
out:
pm_runtime_put_autosuspend(dev->dev);
return ret;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
index 18a7829122d2..c7843e336310 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
@@ -45,16 +45,11 @@
* Cast helper
*/
static const struct dma_fence_ops amdgpu_fence_ops;
-static const struct dma_fence_ops amdgpu_job_fence_ops;
static inline struct amdgpu_fence *to_amdgpu_fence(struct dma_fence *f)
{
struct amdgpu_fence *__f = container_of(f, struct amdgpu_fence, base);
- if (__f->base.ops == &amdgpu_fence_ops ||
- __f->base.ops == &amdgpu_job_fence_ops)
- return __f;
-
- return NULL;
+ return __f;
}
/**
@@ -98,51 +93,32 @@ static u32 amdgpu_fence_read(struct amdgpu_ring *ring)
* amdgpu_fence_emit - emit a fence on the requested ring
*
* @ring: ring the fence is associated with
- * @f: resulting fence object
* @af: amdgpu fence input
* @flags: flags to pass into the subordinate .emit_fence() call
*
* Emits a fence command on the requested ring (all asics).
* Returns 0 on success, -ENOMEM on failure.
*/
-int amdgpu_fence_emit(struct amdgpu_ring *ring, struct dma_fence **f,
- struct amdgpu_fence *af, unsigned int flags)
+int amdgpu_fence_emit(struct amdgpu_ring *ring, struct amdgpu_fence *af,
+ unsigned int flags)
{
struct amdgpu_device *adev = ring->adev;
struct dma_fence *fence;
- struct amdgpu_fence *am_fence;
struct dma_fence __rcu **ptr;
uint32_t seq;
int r;
- if (!af) {
- /* create a separate hw fence */
- am_fence = kzalloc(sizeof(*am_fence), GFP_KERNEL);
- if (!am_fence)
- return -ENOMEM;
- } else {
- am_fence = af;
- }
- fence = &am_fence->base;
- am_fence->ring = ring;
+ fence = &af->base;
+ af->ring = ring;
seq = ++ring->fence_drv.sync_seq;
- am_fence->seq = seq;
- if (af) {
- dma_fence_init(fence, &amdgpu_job_fence_ops,
- &ring->fence_drv.lock,
- adev->fence_context + ring->idx, seq);
- /* Against remove in amdgpu_job_{free, free_cb} */
- dma_fence_get(fence);
- } else {
- dma_fence_init(fence, &amdgpu_fence_ops,
- &ring->fence_drv.lock,
- adev->fence_context + ring->idx, seq);
- }
+ dma_fence_init(fence, &amdgpu_fence_ops,
+ &ring->fence_drv.lock,
+ adev->fence_context + ring->idx, seq);
amdgpu_ring_emit_fence(ring, ring->fence_drv.gpu_addr,
seq, flags | AMDGPU_FENCE_FLAG_INT);
- amdgpu_fence_save_wptr(fence);
+ amdgpu_fence_save_wptr(af);
pm_runtime_get_noresume(adev_to_drm(adev)->dev);
ptr = &ring->fence_drv.fences[seq & ring->fence_drv.num_fences_mask];
if (unlikely(rcu_dereference_protected(*ptr, 1))) {
@@ -167,8 +143,6 @@ int amdgpu_fence_emit(struct amdgpu_ring *ring, struct dma_fence **f,
*/
rcu_assign_pointer(*ptr, dma_fence_get(fence));
- *f = fence;
-
return 0;
}
@@ -276,7 +250,6 @@ bool amdgpu_fence_process(struct amdgpu_ring *ring)
drv->signalled_wptr = am_fence->wptr;
dma_fence_signal(fence);
dma_fence_put(fence);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
} while (last_seq != seq);
@@ -670,36 +643,6 @@ void amdgpu_fence_driver_hw_init(struct amdgpu_device *adev)
}
/**
- * amdgpu_fence_driver_clear_job_fences - clear job embedded fences of ring
- *
- * @ring: fence of the ring to be cleared
- *
- */
-void amdgpu_fence_driver_clear_job_fences(struct amdgpu_ring *ring)
-{
- int i;
- struct dma_fence *old, **ptr;
-
- for (i = 0; i <= ring->fence_drv.num_fences_mask; i++) {
- ptr = &ring->fence_drv.fences[i];
- old = rcu_dereference_protected(*ptr, 1);
- if (old && old->ops == &amdgpu_job_fence_ops) {
- struct amdgpu_job *job;
-
- /* For non-scheduler bad job, i.e. failed ib test, we need to signal
- * it right here or we won't be able to track them in fence_drv
- * and they will remain unsignaled during sa_bo free.
- */
- job = container_of(old, struct amdgpu_job, hw_fence.base);
- if (!job->base.s_fence && !dma_fence_is_signaled(old))
- dma_fence_signal(old);
- RCU_INIT_POINTER(*ptr, NULL);
- dma_fence_put(old);
- }
- }
-}
-
-/**
* amdgpu_fence_driver_set_error - set error code on fences
* @ring: the ring which contains the fences
* @error: the error code to set
@@ -755,7 +698,7 @@ void amdgpu_fence_driver_force_completion(struct amdgpu_ring *ring)
/**
* amdgpu_fence_driver_guilty_force_completion - force signal of specified sequence
*
- * @fence: fence of the ring to signal
+ * @af: fence of the ring to signal
*
*/
void amdgpu_fence_driver_guilty_force_completion(struct amdgpu_fence *af)
@@ -792,15 +735,13 @@ void amdgpu_fence_driver_guilty_force_completion(struct amdgpu_fence *af)
} while (last_seq != seq);
spin_unlock_irqrestore(&ring->fence_drv.lock, flags);
/* signal the guilty fence */
- amdgpu_fence_write(ring, af->seq);
+ amdgpu_fence_write(ring, (u32)af->base.seqno);
amdgpu_fence_process(ring);
}
-void amdgpu_fence_save_wptr(struct dma_fence *fence)
+void amdgpu_fence_save_wptr(struct amdgpu_fence *af)
{
- struct amdgpu_fence *am_fence = container_of(fence, struct amdgpu_fence, base);
-
- am_fence->wptr = am_fence->ring->wptr;
+ af->wptr = af->ring->wptr;
}
static void amdgpu_ring_backup_unprocessed_command(struct amdgpu_ring *ring,
@@ -866,13 +807,6 @@ static const char *amdgpu_fence_get_timeline_name(struct dma_fence *f)
return (const char *)to_amdgpu_fence(f)->ring->name;
}
-static const char *amdgpu_job_fence_get_timeline_name(struct dma_fence *f)
-{
- struct amdgpu_job *job = container_of(f, struct amdgpu_job, hw_fence.base);
-
- return (const char *)to_amdgpu_ring(job->base.sched)->name;
-}
-
/**
* amdgpu_fence_enable_signaling - enable signalling on fence
* @f: fence
@@ -890,23 +824,6 @@ static bool amdgpu_fence_enable_signaling(struct dma_fence *f)
}
/**
- * amdgpu_job_fence_enable_signaling - enable signalling on job fence
- * @f: fence
- *
- * This is the simliar function with amdgpu_fence_enable_signaling above, it
- * only handles the job embedded fence.
- */
-static bool amdgpu_job_fence_enable_signaling(struct dma_fence *f)
-{
- struct amdgpu_job *job = container_of(f, struct amdgpu_job, hw_fence.base);
-
- if (!timer_pending(&to_amdgpu_ring(job->base.sched)->fence_drv.fallback_timer))
- amdgpu_fence_schedule_fallback(to_amdgpu_ring(job->base.sched));
-
- return true;
-}
-
-/**
* amdgpu_fence_free - free up the fence memory
*
* @rcu: RCU callback head
@@ -922,21 +839,6 @@ static void amdgpu_fence_free(struct rcu_head *rcu)
}
/**
- * amdgpu_job_fence_free - free up the job with embedded fence
- *
- * @rcu: RCU callback head
- *
- * Free up the job with embedded fence after the RCU grace period.
- */
-static void amdgpu_job_fence_free(struct rcu_head *rcu)
-{
- struct dma_fence *f = container_of(rcu, struct dma_fence, rcu);
-
- /* free job if fence has a parent job */
- kfree(container_of(f, struct amdgpu_job, hw_fence.base));
-}
-
-/**
* amdgpu_fence_release - callback that fence can be freed
*
* @f: fence
@@ -949,19 +851,6 @@ static void amdgpu_fence_release(struct dma_fence *f)
call_rcu(&f->rcu, amdgpu_fence_free);
}
-/**
- * amdgpu_job_fence_release - callback that job embedded fence can be freed
- *
- * @f: fence
- *
- * This is the simliar function with amdgpu_fence_release above, it
- * only handles the job embedded fence.
- */
-static void amdgpu_job_fence_release(struct dma_fence *f)
-{
- call_rcu(&f->rcu, amdgpu_job_fence_free);
-}
-
static const struct dma_fence_ops amdgpu_fence_ops = {
.get_driver_name = amdgpu_fence_get_driver_name,
.get_timeline_name = amdgpu_fence_get_timeline_name,
@@ -969,13 +858,6 @@ static const struct dma_fence_ops amdgpu_fence_ops = {
.release = amdgpu_fence_release,
};
-static const struct dma_fence_ops amdgpu_job_fence_ops = {
- .get_driver_name = amdgpu_fence_get_driver_name,
- .get_timeline_name = amdgpu_job_fence_get_timeline_name,
- .enable_signaling = amdgpu_job_fence_enable_signaling,
- .release = amdgpu_job_fence_release,
-};
-
/*
* Fence debugfs
*/
@@ -1045,7 +927,6 @@ static int gpu_recover_get(void *data, u64 *val)
*val = atomic_read(&adev->reset_domain->reset_res);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c
index b2033f8352f5..d2237ce9da70 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c
@@ -302,7 +302,6 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset,
int pages)
{
unsigned t;
- unsigned p;
int i, j;
u64 page_base;
/* Starting from VEGA10, system bit must be 0 to mean invalid. */
@@ -316,8 +315,7 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset,
return;
t = offset / AMDGPU_GPU_PAGE_SIZE;
- p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
- for (i = 0; i < pages; i++, p++) {
+ for (i = 0; i < pages; i++) {
page_base = adev->dummy_page_addr;
if (!adev->gart.ptr)
continue;
@@ -370,6 +368,42 @@ void amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset,
}
/**
+ * amdgpu_gart_map_vram_range - map VRAM pages into the GART page table
+ *
+ * @adev: amdgpu_device pointer
+ * @pa: physical address of the first page to be mapped
+ * @start_page: first page to map in the GART aperture
+ * @num_pages: number of pages to be mapped
+ * @flags: page table entry flags
+ * @dst: CPU address of the GART table
+ *
+ * Binds a BO that is allocated in VRAM to the GART page table
+ * (all ASICs).
+ *
+ * Useful when a kernel BO is located in VRAM but
+ * needs to be accessed from the GART address space.
+ */
+void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa,
+ uint64_t start_page, uint64_t num_pages,
+ uint64_t flags, void *dst)
+{
+ u32 i, idx;
+
+ /* The SYSTEM flag indicates the pages aren't in VRAM. */
+ WARN_ON_ONCE(flags & AMDGPU_PTE_SYSTEM);
+
+ if (!drm_dev_enter(adev_to_drm(adev), &idx))
+ return;
+
+ for (i = 0; i < num_pages; ++i) {
+ amdgpu_gmc_set_pte_pde(adev, adev->gart.ptr,
+ start_page + i, pa + AMDGPU_GPU_PAGE_SIZE * i, flags);
+ }
+
+ drm_dev_exit(idx);
+}
+
+/**
* amdgpu_gart_bind - bind pages into the gart page table
*
* @adev: amdgpu_device pointer
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h
index 7cc980bf4725..d3118275ddae 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gart.h
@@ -64,5 +64,8 @@ void amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset,
void *dst);
void amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset,
int pages, dma_addr_t *dma_addr, uint64_t flags);
+void amdgpu_gart_map_vram_range(struct amdgpu_device *adev, uint64_t pa,
+ uint64_t start_page, uint64_t num_pages,
+ uint64_t flags, void *dst);
void amdgpu_gart_invalidate_tlb(struct amdgpu_device *adev);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
index b7ebae289bea..3e38c5db2987 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -198,7 +198,7 @@ static void amdgpu_gem_object_free(struct drm_gem_object *gobj)
struct amdgpu_bo *aobj = gem_to_amdgpu_bo(gobj);
amdgpu_hmm_unregister(aobj);
- ttm_bo_put(&aobj->tbo);
+ ttm_bo_fini(&aobj->tbo);
}
int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size,
@@ -531,7 +531,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
struct drm_amdgpu_gem_userptr *args = data;
struct amdgpu_fpriv *fpriv = filp->driver_priv;
struct drm_gem_object *gobj;
- struct hmm_range *range;
+ struct amdgpu_hmm_range *range;
struct amdgpu_bo *bo;
uint32_t handle;
int r;
@@ -572,10 +572,14 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
goto release_object;
if (args->flags & AMDGPU_GEM_USERPTR_VALIDATE) {
- r = amdgpu_ttm_tt_get_user_pages(bo, &range);
- if (r)
+ range = amdgpu_hmm_range_alloc(NULL);
+ if (unlikely(!range))
+ return -ENOMEM;
+ r = amdgpu_ttm_tt_get_user_pages(bo, range);
+ if (r) {
+ amdgpu_hmm_range_free(range);
goto release_object;
-
+ }
r = amdgpu_bo_reserve(bo, true);
if (r)
goto user_pages_done;
@@ -597,8 +601,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data,
user_pages_done:
if (args->flags & AMDGPU_GEM_USERPTR_VALIDATE)
- amdgpu_ttm_tt_get_user_pages_done(bo->tbo.ttm, range);
-
+ amdgpu_hmm_range_free(range);
release_object:
drm_gem_object_put(gobj);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
index ebe2b4c68b0f..8b118c53f351 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
@@ -33,6 +33,7 @@
#include "amdgpu_reset.h"
#include "amdgpu_xcp.h"
#include "amdgpu_xgmi.h"
+#include "amdgpu_mes.h"
#include "nvd.h"
/* delay 0.1 second to enable gfx off feature */
@@ -1194,6 +1195,75 @@ failed_kiq_write:
dev_err(adev->dev, "failed to write reg:%x\n", reg);
}
+int amdgpu_kiq_hdp_flush(struct amdgpu_device *adev)
+{
+ signed long r, cnt = 0;
+ unsigned long flags;
+ uint32_t seq;
+ struct amdgpu_kiq *kiq = &adev->gfx.kiq[0];
+ struct amdgpu_ring *ring = &kiq->ring;
+
+ if (amdgpu_device_skip_hw_access(adev))
+ return 0;
+
+ if (adev->enable_mes_kiq && adev->mes.ring[0].sched.ready)
+ return amdgpu_mes_hdp_flush(adev);
+
+ if (!ring->funcs->emit_hdp_flush) {
+ return -EOPNOTSUPP;
+ }
+
+ spin_lock_irqsave(&kiq->ring_lock, flags);
+ r = amdgpu_ring_alloc(ring, 32);
+ if (r)
+ goto failed_unlock;
+
+ amdgpu_ring_emit_hdp_flush(ring);
+ r = amdgpu_fence_emit_polling(ring, &seq, MAX_KIQ_REG_WAIT);
+ if (r)
+ goto failed_undo;
+
+ amdgpu_ring_commit(ring);
+ spin_unlock_irqrestore(&kiq->ring_lock, flags);
+
+ r = amdgpu_fence_wait_polling(ring, seq, MAX_KIQ_REG_WAIT);
+
+ /* don't wait anymore for gpu reset case because this way may
+ * block gpu_recover() routine forever, e.g. this virt_kiq_rreg
+ * is triggered in TTM and ttm_bo_lock_delayed_workqueue() will
+ * never return if we keep waiting in virt_kiq_rreg, which cause
+ * gpu_recover() hang there.
+ *
+ * also don't wait anymore for IRQ context
+ * */
+ if (r < 1 && (amdgpu_in_reset(adev) || in_interrupt()))
+ goto failed_kiq_hdp_flush;
+
+ might_sleep();
+ while (r < 1 && cnt++ < MAX_KIQ_REG_TRY) {
+ if (amdgpu_in_reset(adev))
+ goto failed_kiq_hdp_flush;
+
+ msleep(MAX_KIQ_REG_BAILOUT_INTERVAL);
+ r = amdgpu_fence_wait_polling(ring, seq, MAX_KIQ_REG_WAIT);
+ }
+
+ if (cnt > MAX_KIQ_REG_TRY) {
+ dev_err(adev->dev, "failed to flush HDP via KIQ timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+
+failed_undo:
+ amdgpu_ring_undo(ring);
+failed_unlock:
+ spin_unlock_irqrestore(&kiq->ring_lock, flags);
+failed_kiq_hdp_flush:
+ dev_err(adev->dev, "failed to flush HDP via KIQ\n");
+ return r < 0 ? r : -EIO;
+}
+
int amdgpu_gfx_get_num_kcq(struct amdgpu_device *adev)
{
if (amdgpu_num_kcq == -1) {
@@ -1600,7 +1670,6 @@ static ssize_t amdgpu_gfx_set_run_cleaner_shader(struct device *dev,
ret = amdgpu_gfx_run_cleaner_shader(adev, value);
- pm_runtime_mark_last_busy(ddev->dev);
pm_runtime_put_autosuspend(ddev->dev);
if (ret)
@@ -2485,3 +2554,4 @@ void amdgpu_debugfs_compute_sched_mask_init(struct amdgpu_device *adev)
&amdgpu_debugfs_compute_sched_mask_fops);
#endif
}
+
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
index fb5f7a0ee029..efd61a1ccc66 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h
@@ -615,6 +615,7 @@ int amdgpu_gfx_cp_ecc_error_irq(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry);
uint32_t amdgpu_kiq_rreg(struct amdgpu_device *adev, uint32_t reg, uint32_t xcc_id);
void amdgpu_kiq_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v, uint32_t xcc_id);
+int amdgpu_kiq_hdp_flush(struct amdgpu_device *adev);
int amdgpu_gfx_get_num_kcq(struct amdgpu_device *adev);
void amdgpu_gfx_cp_init_microcode(struct amdgpu_device *adev, uint32_t ucode_id);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
index 9dcf51991b5b..869bceb0fe2c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c
@@ -597,6 +597,9 @@ int amdgpu_gmc_allocate_vm_inv_eng(struct amdgpu_device *adev)
/* reserve engine 5 for firmware */
if (adev->enable_mes)
vm_inv_engs[i] &= ~(1 << 5);
+ /* reserve engine 6 for uni mes */
+ if (adev->enable_uni_mes)
+ vm_inv_engs[i] &= ~(1 << 6);
/* reserve mmhub engine 3 for firmware */
if (adev->enable_umsch_mm)
vm_inv_engs[i] &= ~(1 << 3);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
index 55097ca10738..727342689d4b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h
@@ -86,6 +86,11 @@ enum amdgpu_memory_partition {
#define AMDGPU_MAX_MEM_RANGES 8
+#define AMDGPU_GMC9_FAULT_SOURCE_DATA_RETRY 0x80
+#define AMDGPU_GMC9_FAULT_SOURCE_DATA_READ 0x40
+#define AMDGPU_GMC9_FAULT_SOURCE_DATA_WRITE 0x20
+#define AMDGPU_GMC9_FAULT_SOURCE_DATA_EXE 0x10
+
/*
* GMC page fault information
*/
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
index 0760e70402ec..895c1e4c6747 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
@@ -284,6 +284,7 @@ int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size)
ttm_resource_manager_init(man, &adev->mman.bdev, gtt_size);
start = AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS;
+ start += amdgpu_vce_required_gart_pages(adev);
size = (adev->gmc.gart_size >> PAGE_SHIFT) - start;
drm_mm_init(&mgr->mm, start, size);
spin_lock_init(&mgr->lock);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.c
index 6e02fb9ac2f6..5a60d69a3e1f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.c
@@ -66,3 +66,19 @@ void amdgpu_hdp_generic_flush(struct amdgpu_device *adev,
0);
}
}
+
+void amdgpu_hdp_invalidate(struct amdgpu_device *adev, struct amdgpu_ring *ring)
+{
+ if (adev->asic_funcs && adev->asic_funcs->invalidate_hdp)
+ adev->asic_funcs->invalidate_hdp(adev, ring);
+ else if (adev->hdp.funcs && adev->hdp.funcs->invalidate_hdp)
+ adev->hdp.funcs->invalidate_hdp(adev, ring);
+}
+
+void amdgpu_hdp_flush(struct amdgpu_device *adev, struct amdgpu_ring *ring)
+{
+ if (adev->asic_funcs && adev->asic_funcs->flush_hdp)
+ adev->asic_funcs->flush_hdp(adev, ring);
+ else if (adev->hdp.funcs && adev->hdp.funcs->flush_hdp)
+ adev->hdp.funcs->flush_hdp(adev, ring);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.h
index 4cfd932b7e91..d9f488fa76b9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hdp.h
@@ -46,4 +46,8 @@ struct amdgpu_hdp {
int amdgpu_hdp_ras_sw_init(struct amdgpu_device *adev);
void amdgpu_hdp_generic_flush(struct amdgpu_device *adev,
struct amdgpu_ring *ring);
+void amdgpu_hdp_invalidate(struct amdgpu_device *adev,
+ struct amdgpu_ring *ring);
+void amdgpu_hdp_flush(struct amdgpu_device *adev,
+ struct amdgpu_ring *ring);
#endif /* __AMDGPU_HDP_H__ */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
index 2c6a6b858112..90d26d820bac 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
@@ -168,17 +168,13 @@ void amdgpu_hmm_unregister(struct amdgpu_bo *bo)
int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
uint64_t start, uint64_t npages, bool readonly,
void *owner,
- struct hmm_range **phmm_range)
+ struct amdgpu_hmm_range *range)
{
- struct hmm_range *hmm_range;
unsigned long end;
unsigned long timeout;
unsigned long *pfns;
int r = 0;
-
- hmm_range = kzalloc(sizeof(*hmm_range), GFP_KERNEL);
- if (unlikely(!hmm_range))
- return -ENOMEM;
+ struct hmm_range *hmm_range = &range->hmm_range;
pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
if (unlikely(!pfns)) {
@@ -221,28 +217,77 @@ retry:
hmm_range->start = start;
hmm_range->hmm_pfns = pfns;
- *phmm_range = hmm_range;
-
return 0;
out_free_pfns:
kvfree(pfns);
+ hmm_range->hmm_pfns = NULL;
out_free_range:
- kfree(hmm_range);
-
if (r == -EBUSY)
r = -EAGAIN;
return r;
}
-bool amdgpu_hmm_range_get_pages_done(struct hmm_range *hmm_range)
+/**
+ * amdgpu_hmm_range_valid - check if an HMM range is still valid
+ * @range: pointer to the &struct amdgpu_hmm_range to validate
+ *
+ * Determines whether the given HMM range @range is still valid by
+ * checking for invalidations via the MMU notifier sequence. This is
+ * typically used to verify that the range has not been invalidated
+ * by concurrent address space updates before it is accessed.
+ *
+ * Return:
+ * * true if @range is valid and can be used safely
+ * * false if @range is NULL or has been invalidated
+ */
+bool amdgpu_hmm_range_valid(struct amdgpu_hmm_range *range)
{
- bool r;
+ if (!range)
+ return false;
- r = mmu_interval_read_retry(hmm_range->notifier,
- hmm_range->notifier_seq);
- kvfree(hmm_range->hmm_pfns);
- kfree(hmm_range);
+ return !mmu_interval_read_retry(range->hmm_range.notifier,
+ range->hmm_range.notifier_seq);
+}
- return r;
+/**
+ * amdgpu_hmm_range_alloc - allocate and initialize an AMDGPU HMM range
+ * @bo: optional buffer object to associate with this HMM range
+ *
+ * Allocates memory for amdgpu_hmm_range and associates it with the @bo passed.
+ * The reference count of the @bo is incremented.
+ *
+ * Return:
+ * Pointer to a newly allocated struct amdgpu_hmm_range on success,
+ * or NULL if memory allocation fails.
+ */
+struct amdgpu_hmm_range *amdgpu_hmm_range_alloc(struct amdgpu_bo *bo)
+{
+ struct amdgpu_hmm_range *range;
+
+ range = kzalloc(sizeof(*range), GFP_KERNEL);
+ if (!range)
+ return NULL;
+
+ range->bo = amdgpu_bo_ref(bo);
+ return range;
+}
+
+/**
+ * amdgpu_hmm_range_free - release an AMDGPU HMM range
+ * @range: pointer to the range object to free
+ *
+ * Releases all resources held by @range, including the associated
+ * hmm_pfns and the dropping reference of associated bo if any.
+ *
+ * Return: void
+ */
+void amdgpu_hmm_range_free(struct amdgpu_hmm_range *range)
+{
+ if (!range)
+ return;
+
+ kvfree(range->hmm_range.hmm_pfns);
+ amdgpu_bo_unref(&range->bo);
+ kfree(range);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h
index 953e1d06de20..140bc9cd57b4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h
@@ -31,13 +31,20 @@
#include <linux/interval_tree.h>
#include <linux/mmu_notifier.h>
+struct amdgpu_hmm_range {
+ struct hmm_range hmm_range;
+ struct amdgpu_bo *bo;
+};
+
int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
uint64_t start, uint64_t npages, bool readonly,
void *owner,
- struct hmm_range **phmm_range);
-bool amdgpu_hmm_range_get_pages_done(struct hmm_range *hmm_range);
+ struct amdgpu_hmm_range *range);
#if defined(CONFIG_HMM_MIRROR)
+bool amdgpu_hmm_range_valid(struct amdgpu_hmm_range *range);
+struct amdgpu_hmm_range *amdgpu_hmm_range_alloc(struct amdgpu_bo *bo);
+void amdgpu_hmm_range_free(struct amdgpu_hmm_range *range);
int amdgpu_hmm_register(struct amdgpu_bo *bo, unsigned long addr);
void amdgpu_hmm_unregister(struct amdgpu_bo *bo);
#else
@@ -47,7 +54,20 @@ static inline int amdgpu_hmm_register(struct amdgpu_bo *bo, unsigned long addr)
"add CONFIG_ZONE_DEVICE=y in config file to fix this\n");
return -ENODEV;
}
+
static inline void amdgpu_hmm_unregister(struct amdgpu_bo *bo) {}
+
+static inline bool amdgpu_hmm_range_valid(struct amdgpu_hmm_range *range)
+{
+ return false;
+}
+
+static inline struct amdgpu_hmm_range *amdgpu_hmm_range_alloc(struct amdgpu_bo *bo)
+{
+ return NULL;
+}
+
+static inline void amdgpu_hmm_range_free(struct amdgpu_hmm_range *range) {}
#endif
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c
index 7d9bcb72e8dd..586a58facca1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c
@@ -149,17 +149,19 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
if (job) {
vm = job->vm;
fence_ctx = job->base.s_fence ?
- job->base.s_fence->scheduled.context : 0;
+ job->base.s_fence->finished.context : 0;
shadow_va = job->shadow_va;
csa_va = job->csa_va;
gds_va = job->gds_va;
init_shadow = job->init_shadow;
- af = &job->hw_fence;
+ af = job->hw_fence;
/* Save the context of the job for reset handling.
* The driver needs this so it can skip the ring
* contents for guilty contexts.
*/
- af->context = job->base.s_fence ? job->base.s_fence->finished.context : 0;
+ af->context = fence_ctx;
+ /* the vm fence is also part of the job's context */
+ job->hw_vm_fence->context = fence_ctx;
} else {
vm = NULL;
fence_ctx = 0;
@@ -167,23 +169,28 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
csa_va = 0;
gds_va = 0;
init_shadow = false;
- af = NULL;
+ af = kzalloc(sizeof(*af), GFP_ATOMIC);
+ if (!af)
+ return -ENOMEM;
}
if (!ring->sched.ready) {
dev_err(adev->dev, "couldn't schedule ib on ring <%s>\n", ring->name);
- return -EINVAL;
+ r = -EINVAL;
+ goto free_fence;
}
if (vm && !job->vmid) {
dev_err(adev->dev, "VM IB without ID\n");
- return -EINVAL;
+ r = -EINVAL;
+ goto free_fence;
}
if ((ib->flags & AMDGPU_IB_FLAGS_SECURE) &&
(!ring->funcs->secure_submission_supported)) {
dev_err(adev->dev, "secure submissions not supported on ring <%s>\n", ring->name);
- return -EINVAL;
+ r = -EINVAL;
+ goto free_fence;
}
alloc_size = ring->funcs->emit_frame_size + num_ibs *
@@ -192,7 +199,7 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
r = amdgpu_ring_alloc(ring, alloc_size);
if (r) {
dev_err(adev->dev, "scheduling IB failed (%d).\n", r);
- return r;
+ goto free_fence;
}
need_ctx_switch = ring->current_ctx != fence_ctx;
@@ -289,7 +296,7 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
amdgpu_ring_init_cond_exec(ring, ring->cond_exe_gpu_addr);
}
- r = amdgpu_fence_emit(ring, f, af, fence_flags);
+ r = amdgpu_fence_emit(ring, af, fence_flags);
if (r) {
dev_err(adev->dev, "failed to emit fence (%d)\n", r);
if (job && job->vmid)
@@ -297,6 +304,10 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
amdgpu_ring_undo(ring);
return r;
}
+ *f = &af->base;
+ /* get a ref for the job */
+ if (job)
+ dma_fence_get(*f);
if (ring->funcs->insert_end)
ring->funcs->insert_end(ring);
@@ -317,12 +328,17 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned int num_ibs,
* fence so we know what rings contents to backup
* after we reset the queue.
*/
- amdgpu_fence_save_wptr(*f);
+ amdgpu_fence_save_wptr(af);
amdgpu_ring_ib_end(ring);
amdgpu_ring_commit(ring);
return 0;
+
+free_fence:
+ if (!job)
+ kfree(af);
+ return r;
}
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
index 3ef5bc95642c..9cab36322c16 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
@@ -201,58 +201,34 @@ static int amdgpu_vmid_grab_idle(struct amdgpu_ring *ring,
struct amdgpu_device *adev = ring->adev;
unsigned vmhub = ring->vm_hub;
struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub];
- struct dma_fence **fences;
- unsigned i;
+ /* If anybody is waiting for a VMID let everybody wait for fairness */
if (!dma_fence_is_signaled(ring->vmid_wait)) {
*fence = dma_fence_get(ring->vmid_wait);
return 0;
}
- fences = kmalloc_array(id_mgr->num_ids, sizeof(void *), GFP_NOWAIT);
- if (!fences)
- return -ENOMEM;
-
/* Check if we have an idle VMID */
- i = 0;
- list_for_each_entry((*idle), &id_mgr->ids_lru, list) {
+ list_for_each_entry_reverse((*idle), &id_mgr->ids_lru, list) {
/* Don't use per engine and per process VMID at the same time */
struct amdgpu_ring *r = adev->vm_manager.concurrent_flush ?
NULL : ring;
- fences[i] = amdgpu_sync_peek_fence(&(*idle)->active, r);
- if (!fences[i])
- break;
- ++i;
+ *fence = amdgpu_sync_peek_fence(&(*idle)->active, r);
+ if (!(*fence))
+ return 0;
}
- /* If we can't find a idle VMID to use, wait till one becomes available */
- if (&(*idle)->list == &id_mgr->ids_lru) {
- u64 fence_context = adev->vm_manager.fence_context + ring->idx;
- unsigned seqno = ++adev->vm_manager.seqno[ring->idx];
- struct dma_fence_array *array;
- unsigned j;
-
- *idle = NULL;
- for (j = 0; j < i; ++j)
- dma_fence_get(fences[j]);
-
- array = dma_fence_array_create(i, fences, fence_context,
- seqno, true);
- if (!array) {
- for (j = 0; j < i; ++j)
- dma_fence_put(fences[j]);
- kfree(fences);
- return -ENOMEM;
- }
-
- *fence = dma_fence_get(&array->base);
- dma_fence_put(ring->vmid_wait);
- ring->vmid_wait = &array->base;
- return 0;
- }
- kfree(fences);
+ /*
+ * If we can't find a idle VMID to use, wait on a fence from the least
+ * recently used in the hope that it will be available soon.
+ */
+ *idle = NULL;
+ dma_fence_put(ring->vmid_wait);
+ ring->vmid_wait = dma_fence_get(*fence);
+ /* This is the reference we return */
+ dma_fence_get(*fence);
return 0;
}
@@ -313,7 +289,7 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm,
* user of the VMID.
*/
r = amdgpu_sync_fence(&(*id)->active, &job->base.s_fence->finished,
- GFP_NOWAIT);
+ GFP_ATOMIC);
if (r)
return r;
@@ -373,7 +349,7 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm,
*/
r = amdgpu_sync_fence(&(*id)->active,
&job->base.s_fence->finished,
- GFP_NOWAIT);
+ GFP_ATOMIC);
if (r)
return r;
@@ -426,7 +402,7 @@ int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
/* Remember this submission as user of the VMID */
r = amdgpu_sync_fence(&id->active,
&job->base.s_fence->finished,
- GFP_NOWAIT);
+ GFP_ATOMIC);
if (r)
goto error;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_isp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_isp.c
index 9cddbf50442a..37270c4dab8d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_isp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_isp.c
@@ -280,6 +280,8 @@ int isp_kernel_buffer_alloc(struct device *dev, u64 size,
if (ret)
return ret;
+ /* Ensure *bo is NULL so a new BO will be created */
+ *bo = NULL;
ret = amdgpu_bo_create_kernel(adev,
size,
ISP_MC_ADDR_ALIGN,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index d020a890a0ea..0a0dcbf0798d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -130,14 +130,12 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job)
}
/* attempt a per ring reset */
- if (unlikely(adev->debug_disable_gpu_ring_reset)) {
- dev_err(adev->dev, "Ring reset disabled by debug mask\n");
- } else if (amdgpu_gpu_recovery &&
- amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_PER_QUEUE) &&
- ring->funcs->reset) {
+ if (amdgpu_gpu_recovery &&
+ amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_PER_QUEUE) &&
+ ring->funcs->reset) {
dev_err(adev->dev, "Starting %s ring reset\n",
s_job->sched->name);
- r = amdgpu_ring_reset(ring, job->vmid, &job->hw_fence);
+ r = amdgpu_ring_reset(ring, job->vmid, job->hw_fence);
if (!r) {
atomic_inc(&ring->adev->gpu_reset_counter);
dev_err(adev->dev, "Ring %s reset succeeded\n",
@@ -186,6 +184,9 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm,
unsigned int num_ibs, struct amdgpu_job **job,
u64 drm_client_id)
{
+ struct amdgpu_fence *af;
+ int r;
+
if (num_ibs == 0)
return -EINVAL;
@@ -193,6 +194,20 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm,
if (!*job)
return -ENOMEM;
+ af = kzalloc(sizeof(struct amdgpu_fence), GFP_KERNEL);
+ if (!af) {
+ r = -ENOMEM;
+ goto err_job;
+ }
+ (*job)->hw_fence = af;
+
+ af = kzalloc(sizeof(struct amdgpu_fence), GFP_KERNEL);
+ if (!af) {
+ r = -ENOMEM;
+ goto err_fence;
+ }
+ (*job)->hw_vm_fence = af;
+
(*job)->vm = vm;
amdgpu_sync_create(&(*job)->explicit_sync);
@@ -204,6 +219,14 @@ int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm,
return drm_sched_job_init(&(*job)->base, entity, 1, owner,
drm_client_id);
+
+err_fence:
+ kfree((*job)->hw_fence);
+err_job:
+ kfree(*job);
+ *job = NULL;
+
+ return r;
}
int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev,
@@ -223,7 +246,10 @@ int amdgpu_job_alloc_with_ib(struct amdgpu_device *adev,
if (r) {
if (entity)
drm_sched_job_cleanup(&(*job)->base);
+ kfree((*job)->hw_vm_fence);
+ kfree((*job)->hw_fence);
kfree(*job);
+ *job = NULL;
}
return r;
@@ -251,11 +277,11 @@ void amdgpu_job_free_resources(struct amdgpu_job *job)
struct dma_fence *f;
unsigned i;
- /* Check if any fences where initialized */
+ /* Check if any fences were initialized */
if (job->base.s_fence && job->base.s_fence->finished.ops)
f = &job->base.s_fence->finished;
- else if (job->hw_fence.base.ops)
- f = &job->hw_fence.base;
+ else if (job->hw_fence && job->hw_fence->base.ops)
+ f = &job->hw_fence->base;
else
f = NULL;
@@ -271,11 +297,16 @@ static void amdgpu_job_free_cb(struct drm_sched_job *s_job)
amdgpu_sync_free(&job->explicit_sync);
- /* only put the hw fence if has embedded fence */
- if (!job->hw_fence.base.ops)
- kfree(job);
+ if (job->hw_fence->base.ops)
+ dma_fence_put(&job->hw_fence->base);
+ else
+ kfree(job->hw_fence);
+ if (job->hw_vm_fence->base.ops)
+ dma_fence_put(&job->hw_vm_fence->base);
else
- dma_fence_put(&job->hw_fence.base);
+ kfree(job->hw_vm_fence);
+
+ kfree(job);
}
void amdgpu_job_set_gang_leader(struct amdgpu_job *job,
@@ -304,10 +335,16 @@ void amdgpu_job_free(struct amdgpu_job *job)
if (job->gang_submit != &job->base.s_fence->scheduled)
dma_fence_put(job->gang_submit);
- if (!job->hw_fence.base.ops)
- kfree(job);
+ if (job->hw_fence->base.ops)
+ dma_fence_put(&job->hw_fence->base);
+ else
+ kfree(job->hw_fence);
+ if (job->hw_vm_fence->base.ops)
+ dma_fence_put(&job->hw_vm_fence->base);
else
- dma_fence_put(&job->hw_fence.base);
+ kfree(job->hw_vm_fence);
+
+ kfree(job);
}
struct dma_fence *amdgpu_job_submit(struct amdgpu_job *job)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
index 4a6487eb6cb5..7abf069d17d4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
@@ -64,7 +64,8 @@ struct amdgpu_job {
struct drm_sched_job base;
struct amdgpu_vm *vm;
struct amdgpu_sync explicit_sync;
- struct amdgpu_fence hw_fence;
+ struct amdgpu_fence *hw_fence;
+ struct amdgpu_fence *hw_vm_fence;
struct dma_fence *gang_submit;
uint32_t preamble_status;
uint32_t preemption_status;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index b3e6b3fcdf2c..6ee77f431d56 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -1471,7 +1471,6 @@ error_pasid:
kfree(fpriv);
out_suspend:
- pm_runtime_mark_last_busy(dev->dev);
pm_put:
pm_runtime_put_autosuspend(dev->dev);
@@ -1539,7 +1538,6 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
kfree(fpriv);
file_priv->driver_priv = NULL;
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
index 4883adcfbb4b..9c182ce501af 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c
@@ -105,8 +105,8 @@ int amdgpu_mes_init(struct amdgpu_device *adev)
spin_lock_init(&adev->mes.ring_lock[i]);
adev->mes.total_max_queue = AMDGPU_FENCE_MES_QUEUE_ID_MASK;
- adev->mes.vmid_mask_mmhub = 0xffffff00;
- adev->mes.vmid_mask_gfxhub = adev->gfx.disable_kq ? 0xfffffffe : 0xffffff00;
+ adev->mes.vmid_mask_mmhub = 0xFF00;
+ adev->mes.vmid_mask_gfxhub = adev->gfx.disable_kq ? 0xFFFE : 0xFF00;
num_pipes = adev->gfx.me.num_pipe_per_me * adev->gfx.me.num_me;
if (num_pipes > AMDGPU_MES_MAX_GFX_PIPES)
@@ -528,6 +528,18 @@ error:
return r;
}
+int amdgpu_mes_hdp_flush(struct amdgpu_device *adev)
+{
+ uint32_t hdp_flush_req_offset, hdp_flush_done_offset, ref_and_mask;
+
+ hdp_flush_req_offset = adev->nbio.funcs->get_hdp_flush_req_offset(adev);
+ hdp_flush_done_offset = adev->nbio.funcs->get_hdp_flush_done_offset(adev);
+ ref_and_mask = adev->nbio.hdp_flush_reg->ref_and_mask_cp0;
+
+ return amdgpu_mes_reg_write_reg_wait(adev, hdp_flush_req_offset, hdp_flush_done_offset,
+ ref_and_mask, ref_and_mask);
+}
+
int amdgpu_mes_set_shader_debugger(struct amdgpu_device *adev,
uint64_t process_context_addr,
uint32_t spi_gdbg_per_vmid_cntl,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
index 97c137c90f97..e989225b354b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h
@@ -239,6 +239,7 @@ struct mes_add_queue_input {
struct mes_remove_queue_input {
uint32_t doorbell_offset;
uint64_t gang_context_addr;
+ bool remove_queue_after_reset;
};
struct mes_map_legacy_queue_input {
@@ -428,6 +429,7 @@ int amdgpu_mes_wreg(struct amdgpu_device *adev,
int amdgpu_mes_reg_write_reg_wait(struct amdgpu_device *adev,
uint32_t reg0, uint32_t reg1,
uint32_t ref, uint32_t mask);
+int amdgpu_mes_hdp_flush(struct amdgpu_device *adev);
int amdgpu_mes_set_shader_debugger(struct amdgpu_device *adev,
uint64_t process_context_addr,
uint32_t spi_gdbg_per_vmid_cntl,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
index 20460cfd09bc..dc8d2f52c7d6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -326,6 +326,8 @@ struct amdgpu_mode_info {
struct drm_property *audio_property;
/* FMT dithering */
struct drm_property *dither_property;
+ /* Adaptive Backlight Modulation (power feature) */
+ struct drm_property *abm_level_property;
/* hardcoded DFP edid from BIOS */
const struct drm_edid *bios_hardcoded_edid;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
index 656b8a931dae..52c2d1731aab 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
@@ -96,6 +96,7 @@ struct amdgpu_bo_va {
* if non-zero, cannot unmap from GPU because user queues may still access it
*/
unsigned int queue_refcount;
+ atomic_t userq_va_mapped;
};
struct amdgpu_bo {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
index 8c0e5d03de50..0b10497d487c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
@@ -1539,6 +1539,7 @@ static void psp_xgmi_reflect_topology_info(struct psp_context *psp,
uint64_t src_node_id = psp->adev->gmc.xgmi.node_id;
uint64_t dst_node_id = node_info.node_id;
uint8_t dst_num_hops = node_info.num_hops;
+ uint8_t dst_is_sharing_enabled = node_info.is_sharing_enabled;
uint8_t dst_num_links = node_info.num_links;
hive = amdgpu_get_xgmi_hive(psp->adev);
@@ -1558,13 +1559,20 @@ static void psp_xgmi_reflect_topology_info(struct psp_context *psp,
continue;
mirror_top_info->nodes[j].num_hops = dst_num_hops;
- /*
- * prevent 0 num_links value re-reflection since reflection
+ mirror_top_info->nodes[j].is_sharing_enabled = dst_is_sharing_enabled;
+ /* prevent 0 num_links value re-reflection since reflection
* criteria is based on num_hops (direct or indirect).
- *
*/
- if (dst_num_links)
+ if (dst_num_links) {
mirror_top_info->nodes[j].num_links = dst_num_links;
+ /* swap src and dst due to frame of reference */
+ for (int k = 0; k < dst_num_links; k++) {
+ mirror_top_info->nodes[j].port_num[k].src_xgmi_port_num =
+ node_info.port_num[k].dst_xgmi_port_num;
+ mirror_top_info->nodes[j].port_num[k].dst_xgmi_port_num =
+ node_info.port_num[k].src_xgmi_port_num;
+ }
+ }
break;
}
@@ -1639,9 +1647,10 @@ int psp_xgmi_get_topology_info(struct psp_context *psp,
amdgpu_ip_version(psp->adev, MP0_HWIP, 0) ==
IP_VERSION(13, 0, 6) ||
amdgpu_ip_version(psp->adev, MP0_HWIP, 0) ==
- IP_VERSION(13, 0, 14);
- bool ta_port_num_support = amdgpu_sriov_vf(psp->adev) ? 0 :
- psp->xgmi_context.xgmi_ta_caps & EXTEND_PEER_LINK_INFO_CMD_FLAG;
+ IP_VERSION(13, 0, 14) ||
+ amdgpu_sriov_vf(psp->adev);
+ bool ta_port_num_support = psp->xgmi_context.xgmi_ta_caps & EXTEND_PEER_LINK_INFO_CMD_FLAG ||
+ amdgpu_sriov_xgmi_ta_ext_peer_link_en(psp->adev);
/* popluate the shared output buffer rather than the cmd input buffer
* with node_ids as the input for GET_PEER_LINKS command execution.
@@ -2355,8 +2364,11 @@ static int psp_securedisplay_initialize(struct psp_context *psp)
if (!ret && !psp->securedisplay_context.context.resp_status) {
psp->securedisplay_context.context.initialized = true;
mutex_init(&psp->securedisplay_context.mutex);
- } else
+ } else {
+ /* don't try again */
+ psp->securedisplay_context.context.bin_desc.size_bytes = 0;
return ret;
+ }
mutex_lock(&psp->securedisplay_context.mutex);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c
index 123bcf5c2bb1..bacf888735db 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c
@@ -101,7 +101,6 @@ static ssize_t amdgpu_rap_debugfs_write(struct file *f, const char __user *buf,
}
amdgpu_gfx_off_ctrl(adev, true);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return size;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
index e0ee21150860..2a6cf7963dde 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c
@@ -41,6 +41,7 @@
#include "atom.h"
#include "amdgpu_reset.h"
#include "amdgpu_psp.h"
+#include "amdgpu_ras_mgr.h"
#ifdef CONFIG_X86_MCE_AMD
#include <asm/mce.h>
@@ -149,6 +150,8 @@ static void amdgpu_ras_critical_region_fini(struct amdgpu_device *adev);
#ifdef CONFIG_X86_MCE_AMD
static void amdgpu_register_bad_pages_mca_notifier(struct amdgpu_device *adev);
+static void
+amdgpu_unregister_bad_pages_mca_notifier(struct amdgpu_device *adev);
struct mce_notifier_adev_list {
struct amdgpu_device *devs[MAX_GPU_INSTANCE];
int num_gpu;
@@ -611,6 +614,8 @@ static ssize_t amdgpu_ras_debugfs_ctrl_write(struct file *f,
return size;
}
+static int amdgpu_uniras_clear_badpages_info(struct amdgpu_device *adev);
+
/**
* DOC: AMDGPU RAS debugfs EEPROM table reset interface
*
@@ -635,6 +640,11 @@ static ssize_t amdgpu_ras_debugfs_eeprom_write(struct file *f,
(struct amdgpu_device *)file_inode(f)->i_private;
int ret;
+ if (amdgpu_uniras_enabled(adev)) {
+ ret = amdgpu_uniras_clear_badpages_info(adev);
+ return ret ? ret : size;
+ }
+
ret = amdgpu_ras_eeprom_reset_table(
&(amdgpu_ras_get_context(adev)->eeprom_control));
@@ -1542,9 +1552,51 @@ out_fini_err_data:
return ret;
}
+static int amdgpu_uniras_clear_badpages_info(struct amdgpu_device *adev)
+{
+ struct ras_cmd_dev_handle req = {0};
+ int ret;
+
+ ret = amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__CLEAR_BAD_PAGE_INFO,
+ &req, sizeof(req), NULL, 0);
+ if (ret) {
+ dev_err(adev->dev, "Failed to clear bad pages info, ret: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int amdgpu_uniras_query_block_ecc(struct amdgpu_device *adev,
+ struct ras_query_if *info)
+{
+ struct ras_cmd_block_ecc_info_req req = {0};
+ struct ras_cmd_block_ecc_info_rsp rsp = {0};
+ int ret;
+
+ if (!info)
+ return -EINVAL;
+
+ req.block_id = info->head.block;
+ req.subblock_id = info->head.sub_block_index;
+
+ ret = amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__GET_BLOCK_ECC_STATUS,
+ &req, sizeof(req), &rsp, sizeof(rsp));
+ if (!ret) {
+ info->ce_count = rsp.ce_count;
+ info->ue_count = rsp.ue_count;
+ info->de_count = rsp.de_count;
+ }
+
+ return ret;
+}
+
int amdgpu_ras_query_error_status(struct amdgpu_device *adev, struct ras_query_if *info)
{
- return amdgpu_ras_query_error_status_with_event(adev, info, RAS_EVENT_TYPE_INVALID);
+ if (amdgpu_uniras_enabled(adev))
+ return amdgpu_uniras_query_block_ecc(adev, info);
+ else
+ return amdgpu_ras_query_error_status_with_event(adev, info, RAS_EVENT_TYPE_INVALID);
}
int amdgpu_ras_reset_error_count(struct amdgpu_device *adev,
@@ -1596,6 +1648,27 @@ int amdgpu_ras_reset_error_status(struct amdgpu_device *adev,
return 0;
}
+static int amdgpu_uniras_error_inject(struct amdgpu_device *adev,
+ struct ras_inject_if *info)
+{
+ struct ras_cmd_inject_error_req inject_req;
+ struct ras_cmd_inject_error_rsp rsp;
+
+ if (!info)
+ return -EINVAL;
+
+ memset(&inject_req, 0, sizeof(inject_req));
+ inject_req.block_id = info->head.block;
+ inject_req.subblock_id = info->head.sub_block_index;
+ inject_req.address = info->address;
+ inject_req.error_type = info->head.type;
+ inject_req.instance_mask = info->instance_mask;
+ inject_req.method = info->value;
+
+ return amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__INJECT_ERROR,
+ &inject_req, sizeof(inject_req), &rsp, sizeof(rsp));
+}
+
/* wrapper of psp_ras_trigger_error */
int amdgpu_ras_error_inject(struct amdgpu_device *adev,
struct ras_inject_if *info)
@@ -1613,6 +1686,9 @@ int amdgpu_ras_error_inject(struct amdgpu_device *adev,
info->head.block,
info->head.sub_block_index);
+ if (amdgpu_uniras_enabled(adev))
+ return amdgpu_uniras_error_inject(adev, info);
+
/* inject on guest isn't allowed, return success directly */
if (amdgpu_sriov_vf(adev))
return 0;
@@ -1757,7 +1833,9 @@ int amdgpu_ras_query_error_count(struct amdgpu_device *adev,
/* sysfs begin */
static int amdgpu_ras_badpages_read(struct amdgpu_device *adev,
- struct ras_badpage **bps, unsigned int *count);
+ struct ras_badpage *bps, uint32_t count, uint32_t start);
+static int amdgpu_uniras_badpages_read(struct amdgpu_device *adev,
+ struct ras_badpage *bps, uint32_t count, uint32_t start);
static char *amdgpu_ras_badpage_flags_str(unsigned int flags)
{
@@ -1815,19 +1893,50 @@ static ssize_t amdgpu_ras_sysfs_badpages_read(struct file *f,
unsigned int end = div64_ul(ppos + count - 1, element_size);
ssize_t s = 0;
struct ras_badpage *bps = NULL;
- unsigned int bps_count = 0;
+ int bps_count = 0, i, status;
+ uint64_t address;
memset(buf, 0, count);
- if (amdgpu_ras_badpages_read(adev, &bps, &bps_count))
+ bps_count = end - start;
+ bps = kmalloc_array(bps_count, sizeof(*bps), GFP_KERNEL);
+ if (!bps)
+ return 0;
+
+ memset(bps, 0, sizeof(*bps) * bps_count);
+
+ if (amdgpu_uniras_enabled(adev))
+ bps_count = amdgpu_uniras_badpages_read(adev, bps, bps_count, start);
+ else
+ bps_count = amdgpu_ras_badpages_read(adev, bps, bps_count, start);
+
+ if (bps_count <= 0) {
+ kfree(bps);
return 0;
+ }
+
+ for (i = 0; i < bps_count; i++) {
+ address = ((uint64_t)bps[i].bp) << AMDGPU_GPU_PAGE_SHIFT;
+ if (amdgpu_ras_check_critical_address(adev, address))
+ continue;
+
+ bps[i].size = AMDGPU_GPU_PAGE_SIZE;
+
+ status = amdgpu_vram_mgr_query_page_status(&adev->mman.vram_mgr,
+ address);
+ if (status == -EBUSY)
+ bps[i].flags = AMDGPU_RAS_RETIRE_PAGE_PENDING;
+ else if (status == -ENOENT)
+ bps[i].flags = AMDGPU_RAS_RETIRE_PAGE_FAULT;
+ else
+ bps[i].flags = AMDGPU_RAS_RETIRE_PAGE_RESERVED;
- for (; start < end && start < bps_count; start++)
s += scnprintf(&buf[s], element_size + 1,
"0x%08x : 0x%08x : %1s\n",
- bps[start].bp,
- bps[start].size,
- amdgpu_ras_badpage_flags_str(bps[start].flags));
+ bps[i].bp,
+ bps[i].size,
+ amdgpu_ras_badpage_flags_str(bps[i].flags));
+ }
kfree(bps);
@@ -1843,12 +1952,42 @@ static ssize_t amdgpu_ras_sysfs_features_read(struct device *dev,
return sysfs_emit(buf, "feature mask: 0x%x\n", con->features);
}
+static bool amdgpu_ras_get_version_info(struct amdgpu_device *adev, u32 *major,
+ u32 *minor, u32 *rev)
+{
+ int i;
+
+ if (!adev || !major || !minor || !rev || !amdgpu_uniras_enabled(adev))
+ return false;
+
+ for (i = 0; i < adev->num_ip_blocks; i++) {
+ if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_RAS) {
+ *major = adev->ip_blocks[i].version->major;
+ *minor = adev->ip_blocks[i].version->minor;
+ *rev = adev->ip_blocks[i].version->rev;
+ return true;
+ }
+ }
+
+ return false;
+}
+
static ssize_t amdgpu_ras_sysfs_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct amdgpu_ras *con =
container_of(attr, struct amdgpu_ras, version_attr);
- return sysfs_emit(buf, "table version: 0x%x\n", con->eeprom_control.tbl_hdr.version);
+ u32 major, minor, rev;
+ ssize_t size = 0;
+
+ size += sysfs_emit_at(buf, size, "table version: 0x%x\n",
+ con->eeprom_control.tbl_hdr.version);
+
+ if (amdgpu_ras_get_version_info(con->adev, &major, &minor, &rev))
+ size += sysfs_emit_at(buf, size, "ras version: %u.%u.%u\n",
+ major, minor, rev);
+
+ return size;
}
static ssize_t amdgpu_ras_sysfs_schema_show(struct device *dev,
@@ -2241,6 +2380,11 @@ void amdgpu_ras_interrupt_fatal_error_handler(struct amdgpu_device *adev)
amdgpu_ras_is_err_state(adev, AMDGPU_RAS_BLOCK__ANY))
return;
+ if (amdgpu_uniras_enabled(adev)) {
+ amdgpu_ras_mgr_handle_fatal_interrupt(adev, NULL);
+ return;
+ }
+
if (adev->nbio.ras &&
adev->nbio.ras->handle_ras_controller_intr_no_bifring)
adev->nbio.ras->handle_ras_controller_intr_no_bifring(adev);
@@ -2411,6 +2555,16 @@ int amdgpu_ras_interrupt_dispatch(struct amdgpu_device *adev,
struct ras_manager *obj;
struct ras_ih_data *data;
+ if (amdgpu_uniras_enabled(adev)) {
+ struct ras_ih_info ih_info;
+
+ memset(&ih_info, 0, sizeof(ih_info));
+ ih_info.block = info->head.block;
+ memcpy(&ih_info.iv_entry, info->entry, sizeof(struct amdgpu_iv_entry));
+
+ return amdgpu_ras_mgr_handle_controller_interrupt(adev, &ih_info);
+ }
+
obj = amdgpu_ras_find_obj(adev, &info->head);
if (!obj)
return -EINVAL;
@@ -2605,62 +2759,83 @@ static void amdgpu_ras_query_err_status(struct amdgpu_device *adev)
}
}
-/* recovery begin */
-
-/* return 0 on success.
- * caller need free bps.
- */
static int amdgpu_ras_badpages_read(struct amdgpu_device *adev,
- struct ras_badpage **bps, unsigned int *count)
+ struct ras_badpage *bps, uint32_t count, uint32_t start)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
struct ras_err_handler_data *data;
- int i = 0;
- int ret = 0, status;
+ int r = 0;
+ uint32_t i;
if (!con || !con->eh_data || !bps || !count)
return -EINVAL;
mutex_lock(&con->recovery_lock);
data = con->eh_data;
- if (!data || data->count == 0) {
- *bps = NULL;
- ret = -EINVAL;
- goto out;
+ if (start < data->count) {
+ for (i = start; i < data->count; i++) {
+ if (!data->bps[i].ts)
+ continue;
+
+ bps[r].bp = data->bps[i].retired_page;
+ r++;
+ if (r >= count)
+ break;
+ }
}
+ mutex_unlock(&con->recovery_lock);
- *bps = kmalloc_array(data->count, sizeof(struct ras_badpage), GFP_KERNEL);
- if (!*bps) {
- ret = -ENOMEM;
- goto out;
- }
+ return r;
+}
- for (; i < data->count; i++) {
- if (!data->bps[i].ts)
- continue;
+static int amdgpu_uniras_badpages_read(struct amdgpu_device *adev,
+ struct ras_badpage *bps, uint32_t count, uint32_t start)
+{
+ struct ras_cmd_bad_pages_info_req cmd_input;
+ struct ras_cmd_bad_pages_info_rsp *output;
+ uint32_t group, start_group, end_group;
+ uint32_t pos, pos_in_group;
+ int r = 0, i;
- (*bps)[i] = (struct ras_badpage){
- .bp = data->bps[i].retired_page,
- .size = AMDGPU_GPU_PAGE_SIZE,
- .flags = AMDGPU_RAS_RETIRE_PAGE_RESERVED,
- };
+ if (!bps || !count)
+ return -EINVAL;
- if (amdgpu_ras_check_critical_address(adev,
- data->bps[i].retired_page << AMDGPU_GPU_PAGE_SHIFT))
- continue;
+ output = kmalloc(sizeof(*output), GFP_KERNEL);
+ if (!output)
+ return -ENOMEM;
- status = amdgpu_vram_mgr_query_page_status(&adev->mman.vram_mgr,
- data->bps[i].retired_page << AMDGPU_GPU_PAGE_SHIFT);
- if (status == -EBUSY)
- (*bps)[i].flags = AMDGPU_RAS_RETIRE_PAGE_PENDING;
- else if (status == -ENOENT)
- (*bps)[i].flags = AMDGPU_RAS_RETIRE_PAGE_FAULT;
+ memset(&cmd_input, 0, sizeof(cmd_input));
+
+ start_group = start / RAS_CMD_MAX_BAD_PAGES_PER_GROUP;
+ end_group = (start + count + RAS_CMD_MAX_BAD_PAGES_PER_GROUP - 1) /
+ RAS_CMD_MAX_BAD_PAGES_PER_GROUP;
+
+ pos = start;
+ for (group = start_group; group < end_group; group++) {
+ memset(output, 0, sizeof(*output));
+ cmd_input.group_index = group;
+ if (amdgpu_ras_mgr_handle_ras_cmd(adev, RAS_CMD__GET_BAD_PAGES,
+ &cmd_input, sizeof(cmd_input), output, sizeof(*output)))
+ goto out;
+
+ if (pos >= output->bp_total_cnt)
+ goto out;
+
+ pos_in_group = pos - group * RAS_CMD_MAX_BAD_PAGES_PER_GROUP;
+ for (i = pos_in_group; i < output->bp_in_group; i++, pos++) {
+ if (!output->records[i].ts)
+ continue;
+
+ bps[r].bp = output->records[i].retired_page;
+ r++;
+ if (r >= count)
+ goto out;
+ }
}
- *count = con->bad_page_num;
out:
- mutex_unlock(&con->recovery_lock);
- return ret;
+ kfree(output);
+ return r;
}
static void amdgpu_ras_set_fed_all(struct amdgpu_device *adev,
@@ -2748,8 +2923,12 @@ static void amdgpu_ras_do_recovery(struct work_struct *work)
type = amdgpu_ras_get_fatal_error_event(adev);
list_for_each_entry(remote_adev,
device_list_handle, gmc.xgmi.head) {
- amdgpu_ras_query_err_status(remote_adev);
- amdgpu_ras_log_on_err_counter(remote_adev, type);
+ if (amdgpu_uniras_enabled(remote_adev)) {
+ amdgpu_ras_mgr_update_ras_ecc(remote_adev);
+ } else {
+ amdgpu_ras_query_err_status(remote_adev);
+ amdgpu_ras_log_on_err_counter(remote_adev, type);
+ }
}
}
@@ -2837,8 +3016,13 @@ static int amdgpu_ras_mca2pa_by_idx(struct amdgpu_device *adev,
addr_in.ma.err_addr = bps->address;
addr_in.ma.socket_id = socket;
addr_in.ma.ch_inst = bps->mem_channel;
- /* tell RAS TA the node instance is not used */
- addr_in.ma.node_inst = TA_RAS_INV_NODE;
+ if (!amdgpu_ras_smu_eeprom_supported(adev)) {
+ /* tell RAS TA the node instance is not used */
+ addr_in.ma.node_inst = TA_RAS_INV_NODE;
+ } else {
+ addr_in.ma.umc_inst = bps->mcumc_id;
+ addr_in.ma.node_inst = bps->cu;
+ }
if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr)
ret = adev->umc.ras->convert_ras_err_addr(adev, err_data,
@@ -2981,8 +3165,16 @@ static int __amdgpu_ras_convert_rec_from_rom(struct amdgpu_device *adev,
int i = 0;
enum amdgpu_memory_partition save_nps;
- save_nps = (bps->retired_page >> UMC_NPS_SHIFT) & UMC_NPS_MASK;
- bps->retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT);
+ if (!amdgpu_ras_smu_eeprom_supported(adev)) {
+ save_nps = (bps->retired_page >> UMC_NPS_SHIFT) & UMC_NPS_MASK;
+ bps->retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT);
+ } else {
+ /* if pmfw manages eeprom, save_nps is not stored on eeprom,
+ * we should always convert mca address into physical address,
+ * make save_nps different from nps
+ */
+ save_nps = nps + 1;
+ }
if (save_nps == nps) {
if (amdgpu_umc_pages_in_a_row(adev, err_data,
@@ -3048,7 +3240,8 @@ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev,
if (from_rom) {
/* there is no pa recs in V3, so skip pa recs processing */
- if (control->tbl_hdr.version < RAS_TABLE_VER_V3) {
+ if ((control->tbl_hdr.version < RAS_TABLE_VER_V3) &&
+ !amdgpu_ras_smu_eeprom_supported(adev)) {
for (i = 0; i < pages; i++) {
if (control->ras_num_recs - i >= adev->umc.retire_unit) {
if ((bps[i].address == bps[i + 1].address) &&
@@ -3118,7 +3311,13 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev,
mutex_lock(&con->recovery_lock);
control = &con->eeprom_control;
data = con->eh_data;
- unit_num = data->count / adev->umc.retire_unit - control->ras_num_recs;
+ if (amdgpu_ras_smu_eeprom_supported(adev))
+ unit_num = control->ras_num_recs -
+ control->ras_num_recs_old;
+ else
+ unit_num = data->count / adev->umc.retire_unit -
+ control->ras_num_recs;
+
save_count = con->bad_page_num - control->ras_num_bad_pages;
mutex_unlock(&con->recovery_lock);
@@ -3126,7 +3325,7 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev,
*new_cnt = unit_num;
/* only new entries are saved */
- if (unit_num > 0) {
+ if (unit_num && save_count) {
/*old asics only save pa to eeprom like before*/
if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12) {
if (amdgpu_ras_eeprom_append(control,
@@ -3179,7 +3378,8 @@ static int amdgpu_ras_load_bad_pages(struct amdgpu_device *adev)
/*In V3, there is no pa recs, and some cases(when address==0) may be parsed
as pa recs, so add verion check to avoid it.
*/
- if (control->tbl_hdr.version < RAS_TABLE_VER_V3) {
+ if ((control->tbl_hdr.version < RAS_TABLE_VER_V3) &&
+ !amdgpu_ras_smu_eeprom_supported(adev)) {
for (i = 0; i < control->ras_num_recs; i++) {
if ((control->ras_num_recs - i) >= adev->umc.retire_unit) {
if ((bps[i].address == bps[i + 1].address) &&
@@ -3590,7 +3790,12 @@ int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev)
if (!con || amdgpu_sriov_vf(adev))
return 0;
+ if (amdgpu_uniras_enabled(adev))
+ return 0;
+
control = &con->eeprom_control;
+ con->ras_smu_drv = amdgpu_dpm_get_ras_smu_driver(adev);
+
ret = amdgpu_ras_eeprom_init(control);
control->is_eeprom_valid = !ret;
@@ -3751,7 +3956,9 @@ static int amdgpu_ras_recovery_fini(struct amdgpu_device *adev)
mutex_unlock(&con->recovery_lock);
amdgpu_ras_critical_region_init(adev);
-
+#ifdef CONFIG_X86_MCE_AMD
+ amdgpu_unregister_bad_pages_mca_notifier(adev);
+#endif
return 0;
}
/* recovery end */
@@ -3975,7 +4182,6 @@ static void amdgpu_ras_counte_dw(struct work_struct *work)
atomic_set(&con->ras_ue_count, ue_count);
}
- pm_runtime_mark_last_busy(dev->dev);
Out:
pm_runtime_put_autosuspend(dev->dev);
}
@@ -4584,6 +4790,9 @@ int amdgpu_ras_mark_ras_event_caller(struct amdgpu_device *adev, enum ras_event_
struct ras_event_state *event_state;
int ret = 0;
+ if (amdgpu_uniras_enabled(adev))
+ return 0;
+
if (type >= RAS_EVENT_TYPE_COUNT) {
ret = -EINVAL;
goto out;
@@ -4634,20 +4843,18 @@ u64 amdgpu_ras_acquire_event_id(struct amdgpu_device *adev, enum ras_event_type
return id;
}
-void amdgpu_ras_global_ras_isr(struct amdgpu_device *adev)
+int amdgpu_ras_global_ras_isr(struct amdgpu_device *adev)
{
if (atomic_cmpxchg(&amdgpu_ras_in_intr, 0, 1) == 0) {
struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
enum ras_event_type type = RAS_EVENT_TYPE_FATAL;
- u64 event_id;
+ u64 event_id = RAS_EVENT_INVALID_ID;
- if (amdgpu_ras_mark_ras_event(adev, type)) {
- dev_err(adev->dev,
- "uncorrectable hardware error (ERREVENT_ATHUB_INTERRUPT) detected!\n");
- return;
- }
+ if (amdgpu_uniras_enabled(adev))
+ return 0;
- event_id = amdgpu_ras_acquire_event_id(adev, type);
+ if (!amdgpu_ras_mark_ras_event(adev, type))
+ event_id = amdgpu_ras_acquire_event_id(adev, type);
RAS_EVENT_LOG(adev, event_id, "uncorrectable hardware error"
"(ERREVENT_ATHUB_INTERRUPT) detected!\n");
@@ -4656,6 +4863,8 @@ void amdgpu_ras_global_ras_isr(struct amdgpu_device *adev)
ras->gpu_reset_flags |= AMDGPU_RAS_GPU_RESET_MODE1_RESET;
amdgpu_ras_reset_gpu(adev);
}
+
+ return -EBUSY;
}
bool amdgpu_ras_need_emergency_restart(struct amdgpu_device *adev)
@@ -4783,6 +4992,28 @@ static void amdgpu_register_bad_pages_mca_notifier(struct amdgpu_device *adev)
notifier_registered = true;
}
}
+static void amdgpu_unregister_bad_pages_mca_notifier(struct amdgpu_device *adev)
+{
+ int i, j;
+
+ if (!notifier_registered && !mce_adev_list.num_gpu)
+ return;
+ for (i = 0, j = 0; i < mce_adev_list.num_gpu; i++) {
+ if (mce_adev_list.devs[i] == adev)
+ mce_adev_list.devs[i] = NULL;
+ if (!mce_adev_list.devs[i])
+ ++j;
+ }
+
+ if (j == mce_adev_list.num_gpu) {
+ mce_adev_list.num_gpu = 0;
+ /* Unregister x86 notifier with MCE subsystem. */
+ if (notifier_registered) {
+ mce_unregister_decode_chain(&amdgpu_bad_page_nb);
+ notifier_registered = false;
+ }
+ }
+}
#endif
struct amdgpu_ras *amdgpu_ras_get_context(struct amdgpu_device *adev)
@@ -5408,6 +5639,9 @@ bool amdgpu_ras_is_rma(struct amdgpu_device *adev)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ if (amdgpu_uniras_enabled(adev))
+ return amdgpu_ras_mgr_is_rma(adev);
+
if (!con)
return false;
@@ -5490,3 +5724,25 @@ bool amdgpu_ras_check_critical_address(struct amdgpu_device *adev, uint64_t addr
return ret;
}
+
+void amdgpu_ras_pre_reset(struct amdgpu_device *adev,
+ struct list_head *device_list)
+{
+ struct amdgpu_device *tmp_adev = NULL;
+
+ list_for_each_entry(tmp_adev, device_list, reset_list) {
+ if (amdgpu_uniras_enabled(tmp_adev))
+ amdgpu_ras_mgr_pre_reset(tmp_adev);
+ }
+}
+
+void amdgpu_ras_post_reset(struct amdgpu_device *adev,
+ struct list_head *device_list)
+{
+ struct amdgpu_device *tmp_adev = NULL;
+
+ list_for_each_entry(tmp_adev, device_list, reset_list) {
+ if (amdgpu_uniras_enabled(tmp_adev))
+ amdgpu_ras_mgr_post_reset(tmp_adev);
+ }
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
index 6cf0dfd38be8..ff44190d7d98 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h
@@ -503,7 +503,34 @@ struct ras_critical_region {
uint64_t size;
};
+struct ras_eeprom_table_version {
+ uint32_t minor : 16;
+ uint32_t major : 16;
+};
+
+struct ras_eeprom_smu_funcs {
+ int (*get_ras_table_version)(struct amdgpu_device *adev,
+ uint32_t *table_version);
+ int (*get_badpage_count)(struct amdgpu_device *adev, uint32_t *count, uint32_t timeout);
+ int (*get_badpage_mca_addr)(struct amdgpu_device *adev, uint16_t index, uint64_t *mca_addr);
+ int (*set_timestamp)(struct amdgpu_device *adev, uint64_t timestamp);
+ int (*get_timestamp)(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *timestamp);
+ int (*get_badpage_ipid)(struct amdgpu_device *adev, uint16_t index, uint64_t *ipid);
+ int (*erase_ras_table)(struct amdgpu_device *adev, uint32_t *result);
+};
+
+enum ras_smu_feature_flags {
+ RAS_SMU_FEATURE_BIT__RAS_EEPROM = BIT_ULL(0),
+};
+
+struct ras_smu_drv {
+ const struct ras_eeprom_smu_funcs *smu_eeprom_funcs;
+ void (*ras_smu_feature_flags)(struct amdgpu_device *adev, uint64_t *flags);
+};
+
struct amdgpu_ras {
+ void *ras_mgr;
/* ras infrastructure */
/* for ras itself. */
uint32_t features;
@@ -590,6 +617,10 @@ struct amdgpu_ras {
/* Protect poison injection */
struct mutex poison_lock;
+
+ /* Disable/Enable uniras switch */
+ bool uniras_enabled;
+ const struct ras_smu_drv *ras_smu_drv;
};
struct ras_fs_data {
@@ -909,7 +940,7 @@ static inline void amdgpu_ras_intr_cleared(void)
atomic_set(&amdgpu_ras_in_intr, 0);
}
-void amdgpu_ras_global_ras_isr(struct amdgpu_device *adev);
+int amdgpu_ras_global_ras_isr(struct amdgpu_device *adev);
void amdgpu_ras_set_error_query_ready(struct amdgpu_device *adev, bool ready);
@@ -1008,4 +1039,9 @@ void amdgpu_ras_event_log_print(struct amdgpu_device *adev, u64 event_id,
const char *fmt, ...);
bool amdgpu_ras_is_rma(struct amdgpu_device *adev);
+
+void amdgpu_ras_pre_reset(struct amdgpu_device *adev,
+ struct list_head *device_list);
+void amdgpu_ras_post_reset(struct amdgpu_device *adev,
+ struct list_head *device_list);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
index 3eb3fb55ccb0..64dd7a81bff5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c
@@ -32,6 +32,7 @@
#include <linux/uaccess.h>
#include "amdgpu_reset.h"
+#include "amdgpu_ras_mgr.h"
/* These are memory addresses as would be seen by one or more EEPROM
* chips strung on the I2C bus, usually by manipulating pins 1-3 of a
@@ -123,6 +124,8 @@
RAS_TABLE_V2_1_INFO_SIZE) \
/ RAS_TABLE_RECORD_SIZE)
+#define RAS_SMU_MESSAGE_TIMEOUT_MS 1000 /* 1s */
+
/* Given a zero-based index of an EEPROM RAS record, yields the EEPROM
* offset off of RAS_TABLE_START. That is, this is something you can
* add to control->i2c_address, and then tell I2C layer to read
@@ -443,40 +446,57 @@ int amdgpu_ras_eeprom_reset_table(struct amdgpu_ras_eeprom_control *control)
struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
struct amdgpu_ras_eeprom_table_ras_info *rai = &control->tbl_rai;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ u32 erase_res = 0;
u8 csum;
int res;
mutex_lock(&control->ras_tbl_mutex);
- hdr->header = RAS_TABLE_HDR_VAL;
- amdgpu_ras_set_eeprom_table_version(control);
+ if (!amdgpu_ras_smu_eeprom_supported(adev)) {
+ hdr->header = RAS_TABLE_HDR_VAL;
+ amdgpu_ras_set_eeprom_table_version(control);
- if (hdr->version >= RAS_TABLE_VER_V2_1) {
- hdr->first_rec_offset = RAS_RECORD_START_V2_1;
- hdr->tbl_size = RAS_TABLE_HEADER_SIZE +
- RAS_TABLE_V2_1_INFO_SIZE;
- rai->rma_status = GPU_HEALTH_USABLE;
- /**
- * GPU health represented as a percentage.
- * 0 means worst health, 100 means fully health.
- */
- rai->health_percent = 100;
- /* ecc_page_threshold = 0 means disable bad page retirement */
- rai->ecc_page_threshold = con->bad_page_cnt_threshold;
+ if (hdr->version >= RAS_TABLE_VER_V2_1) {
+ hdr->first_rec_offset = RAS_RECORD_START_V2_1;
+ hdr->tbl_size = RAS_TABLE_HEADER_SIZE +
+ RAS_TABLE_V2_1_INFO_SIZE;
+ rai->rma_status = GPU_HEALTH_USABLE;
+
+ control->ras_record_offset = RAS_RECORD_START_V2_1;
+ control->ras_max_record_count = RAS_MAX_RECORD_COUNT_V2_1;
+ /**
+ * GPU health represented as a percentage.
+ * 0 means worst health, 100 means fully health.
+ */
+ rai->health_percent = 100;
+ /* ecc_page_threshold = 0 means disable bad page retirement */
+ rai->ecc_page_threshold = con->bad_page_cnt_threshold;
+ } else {
+ hdr->first_rec_offset = RAS_RECORD_START;
+ hdr->tbl_size = RAS_TABLE_HEADER_SIZE;
+
+ control->ras_record_offset = RAS_RECORD_START;
+ control->ras_max_record_count = RAS_MAX_RECORD_COUNT;
+ }
+
+ csum = __calc_hdr_byte_sum(control);
+ if (hdr->version >= RAS_TABLE_VER_V2_1)
+ csum += __calc_ras_info_byte_sum(control);
+ csum = -csum;
+ hdr->checksum = csum;
+ res = __write_table_header(control);
+ if (!res && hdr->version > RAS_TABLE_VER_V1)
+ res = __write_table_ras_info(control);
} else {
- hdr->first_rec_offset = RAS_RECORD_START;
- hdr->tbl_size = RAS_TABLE_HEADER_SIZE;
+ res = amdgpu_ras_smu_erase_ras_table(adev, &erase_res);
+ if (res || erase_res) {
+ dev_warn(adev->dev, "RAS EEPROM reset failed, res:%d result:%d",
+ res, erase_res);
+ if (!res)
+ res = -EIO;
+ }
}
- csum = __calc_hdr_byte_sum(control);
- if (hdr->version >= RAS_TABLE_VER_V2_1)
- csum += __calc_ras_info_byte_sum(control);
- csum = -csum;
- hdr->checksum = csum;
- res = __write_table_header(control);
- if (!res && hdr->version > RAS_TABLE_VER_V1)
- res = __write_table_ras_info(control);
-
control->ras_num_recs = 0;
control->ras_num_bad_pages = 0;
control->ras_num_mca_recs = 0;
@@ -556,6 +576,9 @@ bool amdgpu_ras_eeprom_check_err_threshold(struct amdgpu_device *adev)
{
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ if (amdgpu_uniras_enabled(adev))
+ return amdgpu_ras_mgr_check_eeprom_safety_watermark(adev);
+
if (!__is_ras_eeprom_supported(adev) ||
!amdgpu_bad_page_threshold)
return false;
@@ -766,7 +789,8 @@ amdgpu_ras_eeprom_update_header(struct amdgpu_ras_eeprom_control *control)
"Saved bad pages %d reaches threshold value %d\n",
control->ras_num_bad_pages, ras->bad_page_cnt_threshold);
- if (adev->cper.enabled && amdgpu_cper_generate_bp_threshold_record(adev))
+ if (adev->cper.enabled && !amdgpu_uniras_enabled(adev) &&
+ amdgpu_cper_generate_bp_threshold_record(adev))
dev_warn(adev->dev, "fail to generate bad page threshold cper records\n");
if ((amdgpu_bad_page_threshold != -1) &&
@@ -849,6 +873,71 @@ Out:
return res;
}
+int amdgpu_ras_eeprom_update_record_num(struct amdgpu_ras_eeprom_control *control)
+{
+ struct amdgpu_device *adev = to_amdgpu_device(control);
+ int ret, retry = 20;
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return 0;
+
+ control->ras_num_recs_old = control->ras_num_recs;
+
+ do {
+ /* 1000ms timeout is long enough, smu_get_badpage_count won't
+ * return -EBUSY before timeout.
+ */
+ ret = amdgpu_ras_smu_get_badpage_count(adev,
+ &(control->ras_num_recs), RAS_SMU_MESSAGE_TIMEOUT_MS);
+ if (!ret &&
+ (control->ras_num_recs_old == control->ras_num_recs)) {
+ /* record number update in PMFW needs some time,
+ * smu_get_badpage_count may return immediately without
+ * count update, sleep for a while and retry again.
+ */
+ msleep(50);
+ retry--;
+ } else {
+ break;
+ }
+ } while (retry);
+
+ /* no update of record number is not a real failure,
+ * don't print warning here
+ */
+ if (!ret && (control->ras_num_recs_old == control->ras_num_recs))
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int amdgpu_ras_smu_eeprom_append(struct amdgpu_ras_eeprom_control *control)
+{
+ struct amdgpu_device *adev = to_amdgpu_device(control);
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev) || !con)
+ return 0;
+
+ control->ras_num_bad_pages = con->bad_page_num;
+
+ if (amdgpu_bad_page_threshold != 0 &&
+ control->ras_num_bad_pages > con->bad_page_cnt_threshold) {
+ dev_warn(adev->dev,
+ "Saved bad pages %d reaches threshold value %d\n",
+ control->ras_num_bad_pages, con->bad_page_cnt_threshold);
+
+ if (adev->cper.enabled && amdgpu_cper_generate_bp_threshold_record(adev))
+ dev_warn(adev->dev, "fail to generate bad page threshold cper records\n");
+
+ if ((amdgpu_bad_page_threshold != -1) &&
+ (amdgpu_bad_page_threshold != -2))
+ con->is_rma = true;
+ }
+
+ return 0;
+}
+
/**
* amdgpu_ras_eeprom_append -- append records to the EEPROM RAS table
* @control: pointer to control structure
@@ -873,6 +962,9 @@ int amdgpu_ras_eeprom_append(struct amdgpu_ras_eeprom_control *control,
if (!__is_ras_eeprom_supported(adev))
return 0;
+ if (amdgpu_ras_smu_eeprom_supported(adev))
+ return amdgpu_ras_smu_eeprom_append(control);
+
if (num == 0) {
dev_err(adev->dev, "will not append 0 records\n");
return -EINVAL;
@@ -948,6 +1040,50 @@ static int __amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control,
return res;
}
+int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control,
+ struct eeprom_table_record *record, u32 rec_idx,
+ const u32 num)
+{
+ struct amdgpu_device *adev = to_amdgpu_device(control);
+ uint64_t ts, end_idx;
+ int i, ret;
+ u64 mca, ipid;
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return 0;
+
+ if (!adev->umc.ras || !adev->umc.ras->mca_ipid_parse)
+ return -EOPNOTSUPP;
+
+ end_idx = rec_idx + num;
+ for (i = rec_idx; i < end_idx; i++) {
+ ret = amdgpu_ras_smu_get_badpage_mca_addr(adev, i, &mca);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_ras_smu_get_badpage_ipid(adev, i, &ipid);
+ if (ret)
+ return ret;
+
+ ret = amdgpu_ras_smu_get_timestamp(adev, i, &ts);
+ if (ret)
+ return ret;
+
+ record[i - rec_idx].address = mca;
+ /* retired_page (pa) is unused now */
+ record[i - rec_idx].retired_page = 0x1ULL;
+ record[i - rec_idx].ts = ts;
+ record[i - rec_idx].err_type = AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE;
+
+ adev->umc.ras->mca_ipid_parse(adev, ipid,
+ (uint32_t *)&(record[i - rec_idx].cu),
+ (uint32_t *)&(record[i - rec_idx].mem_channel),
+ (uint32_t *)&(record[i - rec_idx].mcumc_id), NULL);
+ }
+
+ return 0;
+}
+
/**
* amdgpu_ras_eeprom_read -- read EEPROM
* @control: pointer to control structure
@@ -969,6 +1105,9 @@ int amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control,
u8 *buf, *pp;
u32 g0, g1;
+ if (amdgpu_ras_smu_eeprom_supported(adev))
+ return amdgpu_ras_eeprom_read_idx(control, record, 0, num);
+
if (!__is_ras_eeprom_supported(adev))
return 0;
@@ -1140,6 +1279,10 @@ static ssize_t amdgpu_ras_debugfs_table_read(struct file *f, char __user *buf,
int res = -EFAULT;
size_t data_len;
+ /* pmfw manages eeprom data by itself */
+ if (amdgpu_ras_smu_eeprom_supported(adev))
+ return 0;
+
mutex_lock(&control->ras_tbl_mutex);
/* We want *pos - data_len > 0, which means there's
@@ -1370,6 +1513,42 @@ Out:
return res == RAS_TABLE_V2_1_INFO_SIZE ? 0 : res;
}
+static int amdgpu_ras_smu_eeprom_init(struct amdgpu_ras_eeprom_control *control)
+{
+ struct amdgpu_device *adev = to_amdgpu_device(control);
+ struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr;
+ struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
+ uint64_t local_time;
+ int res;
+
+ ras->is_rma = false;
+
+ if (!__is_ras_eeprom_supported(adev))
+ return 0;
+ mutex_init(&control->ras_tbl_mutex);
+
+ res = amdgpu_ras_smu_get_table_version(adev, &(hdr->version));
+ if (res)
+ return res;
+
+ res = amdgpu_ras_smu_get_badpage_count(adev,
+ &(control->ras_num_recs), 100);
+ if (res)
+ return res;
+
+ local_time = (uint64_t)ktime_get_real_seconds();
+ res = amdgpu_ras_smu_set_timestamp(adev, local_time);
+ if (res)
+ return res;
+
+ control->ras_max_record_count = 4000;
+
+ control->ras_num_mca_recs = 0;
+ control->ras_num_pa_recs = 0;
+
+ return 0;
+}
+
int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
{
struct amdgpu_device *adev = to_amdgpu_device(control);
@@ -1378,6 +1557,9 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
int res;
+ if (amdgpu_ras_smu_eeprom_supported(adev))
+ return amdgpu_ras_smu_eeprom_init(control);
+
ras->is_rma = false;
if (!__is_ras_eeprom_supported(adev))
@@ -1444,6 +1626,47 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control)
return 0;
}
+static int amdgpu_ras_smu_eeprom_check(struct amdgpu_ras_eeprom_control *control)
+{
+ struct amdgpu_device *adev = to_amdgpu_device(control);
+ struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
+
+ if (!__is_ras_eeprom_supported(adev))
+ return 0;
+
+ control->ras_num_bad_pages = ras->bad_page_num;
+
+ if ((ras->bad_page_cnt_threshold < control->ras_num_bad_pages) &&
+ amdgpu_bad_page_threshold != 0) {
+ dev_warn(adev->dev,
+ "RAS records:%d exceed threshold:%d\n",
+ control->ras_num_bad_pages, ras->bad_page_cnt_threshold);
+ if ((amdgpu_bad_page_threshold == -1) ||
+ (amdgpu_bad_page_threshold == -2)) {
+ dev_warn(adev->dev,
+ "Please consult AMD Service Action Guide (SAG) for appropriate service procedures\n");
+ } else {
+ ras->is_rma = true;
+ dev_warn(adev->dev,
+ "User defined threshold is set, runtime service will be halt when threshold is reached\n");
+ }
+
+ return 0;
+ }
+
+ dev_dbg(adev->dev,
+ "Found existing EEPROM table with %d records",
+ control->ras_num_bad_pages);
+
+ /* Warn if we are at 90% of the threshold or above
+ */
+ if (10 * control->ras_num_bad_pages >= 9 * ras->bad_page_cnt_threshold)
+ dev_warn(adev->dev, "RAS records:%u exceeds 90%% of threshold:%d",
+ control->ras_num_bad_pages,
+ ras->bad_page_cnt_threshold);
+ return 0;
+}
+
int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control)
{
struct amdgpu_device *adev = to_amdgpu_device(control);
@@ -1451,6 +1674,9 @@ int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control)
struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
int res = 0;
+ if (amdgpu_ras_smu_eeprom_supported(adev))
+ return amdgpu_ras_smu_eeprom_check(control);
+
if (!__is_ras_eeprom_supported(adev))
return 0;
@@ -1541,7 +1767,8 @@ void amdgpu_ras_eeprom_check_and_recover(struct amdgpu_device *adev)
struct amdgpu_ras_eeprom_control *control;
int res;
- if (!__is_ras_eeprom_supported(adev) || !ras)
+ if (!__is_ras_eeprom_supported(adev) || !ras ||
+ amdgpu_ras_smu_eeprom_supported(adev))
return;
control = &ras->eeprom_control;
if (!control->is_eeprom_valid)
@@ -1561,4 +1788,143 @@ void amdgpu_ras_eeprom_check_and_recover(struct amdgpu_device *adev)
control->is_eeprom_valid = false;
}
return;
-} \ No newline at end of file
+}
+
+static const struct ras_smu_drv *amdgpu_ras_get_smu_ras_drv(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras *ras = amdgpu_ras_get_context(adev);
+
+ if (!ras)
+ return NULL;
+
+ return ras->ras_smu_drv;
+}
+
+static uint64_t amdgpu_ras_smu_get_feature_flags(struct amdgpu_device *adev)
+{
+ const struct ras_smu_drv *ras_smu_drv = amdgpu_ras_get_smu_ras_drv(adev);
+ uint64_t flags = 0ULL;
+
+ if (!ras_smu_drv)
+ goto out;
+
+ if (ras_smu_drv->ras_smu_feature_flags)
+ ras_smu_drv->ras_smu_feature_flags(adev, &flags);
+
+out:
+ return flags;
+}
+
+bool amdgpu_ras_smu_eeprom_supported(struct amdgpu_device *adev)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+ uint64_t flags = 0ULL;
+
+ if (!__is_ras_eeprom_supported(adev) || !smu_ras_drv)
+ return false;
+
+ if (!smu_ras_drv->smu_eeprom_funcs)
+ return false;
+
+ flags = amdgpu_ras_smu_get_feature_flags(adev);
+
+ return !!(flags & RAS_SMU_FEATURE_BIT__RAS_EEPROM);
+}
+
+int amdgpu_ras_smu_get_table_version(struct amdgpu_device *adev,
+ uint32_t *table_version)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return -EOPNOTSUPP;
+
+ if (smu_ras_drv->smu_eeprom_funcs->get_ras_table_version)
+ return smu_ras_drv->smu_eeprom_funcs->get_ras_table_version(adev,
+ table_version);
+ return -EOPNOTSUPP;
+}
+
+int amdgpu_ras_smu_get_badpage_count(struct amdgpu_device *adev,
+ uint32_t *count, uint32_t timeout)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return -EOPNOTSUPP;
+
+ if (smu_ras_drv->smu_eeprom_funcs->get_badpage_count)
+ return smu_ras_drv->smu_eeprom_funcs->get_badpage_count(adev,
+ count, timeout);
+ return -EOPNOTSUPP;
+}
+
+int amdgpu_ras_smu_get_badpage_mca_addr(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *mca_addr)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return -EOPNOTSUPP;
+
+ if (smu_ras_drv->smu_eeprom_funcs->get_badpage_mca_addr)
+ return smu_ras_drv->smu_eeprom_funcs->get_badpage_mca_addr(adev,
+ index, mca_addr);
+ return -EOPNOTSUPP;
+}
+
+int amdgpu_ras_smu_set_timestamp(struct amdgpu_device *adev,
+ uint64_t timestamp)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return -EOPNOTSUPP;
+
+ if (smu_ras_drv->smu_eeprom_funcs->set_timestamp)
+ return smu_ras_drv->smu_eeprom_funcs->set_timestamp(adev,
+ timestamp);
+ return -EOPNOTSUPP;
+}
+
+int amdgpu_ras_smu_get_timestamp(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *timestamp)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return -EOPNOTSUPP;
+
+ if (smu_ras_drv->smu_eeprom_funcs->get_timestamp)
+ return smu_ras_drv->smu_eeprom_funcs->get_timestamp(adev,
+ index, timestamp);
+ return -EOPNOTSUPP;
+}
+
+int amdgpu_ras_smu_get_badpage_ipid(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *ipid)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return -EOPNOTSUPP;
+
+ if (smu_ras_drv->smu_eeprom_funcs->get_badpage_ipid)
+ return smu_ras_drv->smu_eeprom_funcs->get_badpage_ipid(adev,
+ index, ipid);
+ return -EOPNOTSUPP;
+}
+
+int amdgpu_ras_smu_erase_ras_table(struct amdgpu_device *adev,
+ uint32_t *result)
+{
+ const struct ras_smu_drv *smu_ras_drv = amdgpu_ras_get_smu_ras_drv(adev);
+
+ if (!amdgpu_ras_smu_eeprom_supported(adev))
+ return -EOPNOTSUPP;
+
+ if (smu_ras_drv->smu_eeprom_funcs->erase_ras_table)
+ return smu_ras_drv->smu_eeprom_funcs->erase_ras_table(adev,
+ result);
+ return -EOPNOTSUPP;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
index ebfca4cb5688..2e5d63957e71 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h
@@ -82,6 +82,7 @@ struct amdgpu_ras_eeprom_control {
/* Number of records in the table.
*/
u32 ras_num_recs;
+ u32 ras_num_recs_old;
/* the bad page number is ras_num_recs or
* ras_num_recs * umc.retire_unit
@@ -163,6 +164,35 @@ int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control);
void amdgpu_ras_eeprom_check_and_recover(struct amdgpu_device *adev);
+bool amdgpu_ras_smu_eeprom_supported(struct amdgpu_device *adev);
+
+int amdgpu_ras_smu_get_table_version(struct amdgpu_device *adev,
+ uint32_t *table_version);
+
+int amdgpu_ras_smu_get_badpage_count(struct amdgpu_device *adev,
+ uint32_t *count, uint32_t timeout);
+
+int amdgpu_ras_smu_get_badpage_mca_addr(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *mca_addr);
+
+int amdgpu_ras_smu_set_timestamp(struct amdgpu_device *adev,
+ uint64_t timestamp);
+
+int amdgpu_ras_smu_get_timestamp(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *timestamp);
+
+int amdgpu_ras_smu_get_badpage_ipid(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *ipid);
+
+int amdgpu_ras_smu_erase_ras_table(struct amdgpu_device *adev,
+ uint32_t *result);
+
+int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control,
+ struct eeprom_table_record *record, u32 rec_idx,
+ const u32 num);
+
+int amdgpu_ras_eeprom_update_record_num(struct amdgpu_ras_eeprom_control *control);
+
extern const struct file_operations amdgpu_ras_debugfs_eeprom_size_ops;
extern const struct file_operations amdgpu_ras_debugfs_eeprom_table_ops;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
index 5ec5c3ff22bb..c596b6df2e2d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
@@ -33,6 +33,7 @@
#include <drm/amdgpu_drm.h>
#include "amdgpu.h"
+#include "amdgpu_ras_mgr.h"
#include "atom.h"
/*
@@ -159,8 +160,16 @@ void amdgpu_ring_insert_nop(struct amdgpu_ring *ring, uint32_t count)
*/
void amdgpu_ring_generic_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib)
{
- while (ib->length_dw & ring->funcs->align_mask)
- ib->ptr[ib->length_dw++] = ring->funcs->nop;
+ u32 align_mask = ring->funcs->align_mask;
+ u32 count = ib->length_dw & align_mask;
+
+ if (count) {
+ count = align_mask + 1 - count;
+
+ memset32(&ib->ptr[ib->length_dw], ring->funcs->nop, count);
+
+ ib->length_dw += count;
+ }
}
/**
@@ -460,9 +469,6 @@ bool amdgpu_ring_soft_recovery(struct amdgpu_ring *ring, unsigned int vmid,
ktime_t deadline;
bool ret;
- if (unlikely(ring->adev->debug_disable_soft_recovery))
- return false;
-
deadline = ktime_add_us(ktime_get(), 10000);
if (amdgpu_sriov_vf(ring->adev) || !ring->funcs->soft_recovery || !fence)
@@ -490,6 +496,66 @@ bool amdgpu_ring_soft_recovery(struct amdgpu_ring *ring, unsigned int vmid,
*/
#if defined(CONFIG_DEBUG_FS)
+static ssize_t amdgpu_ras_cper_debugfs_read(struct file *f, char __user *buf,
+ size_t size, loff_t *offset)
+{
+ const uint8_t ring_header_size = 12;
+ struct amdgpu_ring *ring = file_inode(f)->i_private;
+ struct ras_cmd_cper_snapshot_req *snapshot_req __free(kfree) =
+ kzalloc(sizeof(struct ras_cmd_cper_snapshot_req), GFP_KERNEL);
+ struct ras_cmd_cper_snapshot_rsp *snapshot_rsp __free(kfree) =
+ kzalloc(sizeof(struct ras_cmd_cper_snapshot_rsp), GFP_KERNEL);
+ struct ras_cmd_cper_record_req *record_req __free(kfree) =
+ kzalloc(sizeof(struct ras_cmd_cper_record_req), GFP_KERNEL);
+ struct ras_cmd_cper_record_rsp *record_rsp __free(kfree) =
+ kzalloc(sizeof(struct ras_cmd_cper_record_rsp), GFP_KERNEL);
+ uint8_t *ring_header __free(kfree) =
+ kzalloc(ring_header_size, GFP_KERNEL);
+ uint32_t total_cper_num;
+ uint64_t start_cper_id;
+ int r;
+
+ if (!snapshot_req || !snapshot_rsp || !record_req || !record_rsp ||
+ !ring_header)
+ return -ENOMEM;
+
+ if (!(*offset)) {
+ /* Need at least 12 bytes for the header on the first read */
+ if (size < ring_header_size)
+ return -EINVAL;
+
+ if (copy_to_user(buf, ring_header, ring_header_size))
+ return -EFAULT;
+ buf += ring_header_size;
+ size -= ring_header_size;
+ }
+
+ r = amdgpu_ras_mgr_handle_ras_cmd(ring->adev,
+ RAS_CMD__GET_CPER_SNAPSHOT,
+ snapshot_req, sizeof(struct ras_cmd_cper_snapshot_req),
+ snapshot_rsp, sizeof(struct ras_cmd_cper_snapshot_rsp));
+ if (r || !snapshot_rsp->total_cper_num)
+ return r;
+
+ start_cper_id = snapshot_rsp->start_cper_id;
+ total_cper_num = snapshot_rsp->total_cper_num;
+
+ record_req->buf_ptr = (uint64_t)(uintptr_t)buf;
+ record_req->buf_size = size;
+ record_req->cper_start_id = start_cper_id + *offset;
+ record_req->cper_num = total_cper_num;
+ r = amdgpu_ras_mgr_handle_ras_cmd(ring->adev, RAS_CMD__GET_CPER_RECORD,
+ record_req, sizeof(struct ras_cmd_cper_record_req),
+ record_rsp, sizeof(struct ras_cmd_cper_record_rsp));
+ if (r)
+ return r;
+
+ r = *offset ? record_rsp->real_data_size : record_rsp->real_data_size + ring_header_size;
+ (*offset) += record_rsp->real_cper_num;
+
+ return r;
+}
+
/* Layout of file is 12 bytes consisting of
* - rptr
* - wptr
@@ -506,6 +572,9 @@ static ssize_t amdgpu_debugfs_ring_read(struct file *f, char __user *buf,
loff_t i;
int r;
+ if (ring->funcs->type == AMDGPU_RING_TYPE_CPER && amdgpu_uniras_enabled(ring->adev))
+ return amdgpu_ras_cper_debugfs_read(f, buf, size, pos);
+
if (*pos & 3 || size & 3)
return -EINVAL;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
index 4b46e3c26ff3..7a27c6c4bb44 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
@@ -83,6 +83,7 @@ enum amdgpu_ring_type {
AMDGPU_RING_TYPE_MES,
AMDGPU_RING_TYPE_UMSCH_MM,
AMDGPU_RING_TYPE_CPER,
+ AMDGPU_RING_TYPE_MAX,
};
enum amdgpu_ib_pool_type {
@@ -147,16 +148,14 @@ struct amdgpu_fence {
u64 wptr;
/* fence context for resets */
u64 context;
- uint32_t seq;
};
extern const struct drm_sched_backend_ops amdgpu_sched_ops;
-void amdgpu_fence_driver_clear_job_fences(struct amdgpu_ring *ring);
void amdgpu_fence_driver_set_error(struct amdgpu_ring *ring, int error);
void amdgpu_fence_driver_force_completion(struct amdgpu_ring *ring);
void amdgpu_fence_driver_guilty_force_completion(struct amdgpu_fence *af);
-void amdgpu_fence_save_wptr(struct dma_fence *fence);
+void amdgpu_fence_save_wptr(struct amdgpu_fence *af);
int amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring);
int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring,
@@ -166,8 +165,8 @@ void amdgpu_fence_driver_hw_init(struct amdgpu_device *adev);
void amdgpu_fence_driver_hw_fini(struct amdgpu_device *adev);
int amdgpu_fence_driver_sw_init(struct amdgpu_device *adev);
void amdgpu_fence_driver_sw_fini(struct amdgpu_device *adev);
-int amdgpu_fence_emit(struct amdgpu_ring *ring, struct dma_fence **f,
- struct amdgpu_fence *af, unsigned int flags);
+int amdgpu_fence_emit(struct amdgpu_ring *ring, struct amdgpu_fence *af,
+ unsigned int flags);
int amdgpu_fence_emit_polling(struct amdgpu_ring *ring, uint32_t *s,
uint32_t timeout);
bool amdgpu_fence_process(struct amdgpu_ring *ring);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_securedisplay.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_securedisplay.c
index 41ebe690eeff..3739be1b71e0 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_securedisplay.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_securedisplay.c
@@ -159,7 +159,6 @@ static ssize_t amdgpu_securedisplay_debugfs_write(struct file *f, const char __u
dev_err(adev->dev, "Invalid input: %s\n", str);
}
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return size;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index aa9ee5dffa45..2b931e855abd 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -188,7 +188,6 @@ static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo,
struct amdgpu_job *job;
void *cpu_addr;
uint64_t flags;
- unsigned int i;
int r;
BUG_ON(adev->mman.buffer_funcs->copy_max_bytes <
@@ -255,16 +254,9 @@ static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo,
dma_addr = &bo->ttm->dma_address[mm_cur->start >> PAGE_SHIFT];
amdgpu_gart_map(adev, 0, num_pages, dma_addr, flags, cpu_addr);
} else {
- dma_addr_t dma_address;
-
- dma_address = mm_cur->start;
- dma_address += adev->vm_manager.vram_base_offset;
+ u64 pa = mm_cur->start + adev->vm_manager.vram_base_offset;
- for (i = 0; i < num_pages; ++i) {
- amdgpu_gart_map(adev, i << PAGE_SHIFT, 1, &dma_address,
- flags, cpu_addr);
- dma_address += PAGE_SIZE;
- }
+ amdgpu_gart_map_vram_range(adev, pa, 0, num_pages, flags, cpu_addr);
}
dma_fence_put(amdgpu_job_submit(job));
@@ -286,12 +278,13 @@ static int amdgpu_ttm_map_buffer(struct ttm_buffer_object *bo,
* move and different for a BO to BO copy.
*
*/
-int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
- const struct amdgpu_copy_mem *src,
- const struct amdgpu_copy_mem *dst,
- uint64_t size, bool tmz,
- struct dma_resv *resv,
- struct dma_fence **f)
+__attribute__((nonnull))
+static int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
+ const struct amdgpu_copy_mem *src,
+ const struct amdgpu_copy_mem *dst,
+ uint64_t size, bool tmz,
+ struct dma_resv *resv,
+ struct dma_fence **f)
{
struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
struct amdgpu_res_cursor src_mm, dst_mm;
@@ -365,9 +358,7 @@ int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
}
error:
mutex_unlock(&adev->mman.gtt_window_lock);
- if (f)
- *f = dma_fence_get(fence);
- dma_fence_put(fence);
+ *f = fence;
return r;
}
@@ -706,10 +697,11 @@ struct amdgpu_ttm_tt {
* memory and start HMM tracking CPU page table update
*
* Calling function must call amdgpu_ttm_tt_userptr_range_done() once and only
- * once afterwards to stop HMM tracking
+ * once afterwards to stop HMM tracking. Its the caller responsibility to ensure
+ * that range is a valid memory and it is freed too.
*/
int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo,
- struct hmm_range **range)
+ struct amdgpu_hmm_range *range)
{
struct ttm_tt *ttm = bo->tbo.ttm;
struct amdgpu_ttm_tt *gtt = ttm_to_amdgpu_ttm_tt(ttm);
@@ -719,9 +711,6 @@ int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo,
bool readonly;
int r = 0;
- /* Make sure get_user_pages_done() can cleanup gracefully */
- *range = NULL;
-
mm = bo->notifier.mm;
if (unlikely(!mm)) {
DRM_DEBUG_DRIVER("BO is not registered?\n");
@@ -756,38 +745,6 @@ out_unlock:
return r;
}
-/* amdgpu_ttm_tt_discard_user_pages - Discard range and pfn array allocations
- */
-void amdgpu_ttm_tt_discard_user_pages(struct ttm_tt *ttm,
- struct hmm_range *range)
-{
- struct amdgpu_ttm_tt *gtt = (void *)ttm;
-
- if (gtt && gtt->userptr && range)
- amdgpu_hmm_range_get_pages_done(range);
-}
-
-/*
- * amdgpu_ttm_tt_get_user_pages_done - stop HMM track the CPU page table change
- * Check if the pages backing this ttm range have been invalidated
- *
- * Returns: true if pages are still valid
- */
-bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm,
- struct hmm_range *range)
-{
- struct amdgpu_ttm_tt *gtt = ttm_to_amdgpu_ttm_tt(ttm);
-
- if (!gtt || !gtt->userptr || !range)
- return false;
-
- DRM_DEBUG_DRIVER("user_pages_done 0x%llx pages 0x%x\n",
- gtt->userptr, ttm->num_pages);
-
- WARN_ONCE(!range->hmm_pfns, "No user pages to check\n");
-
- return !amdgpu_hmm_range_get_pages_done(range);
-}
#endif
/*
@@ -797,12 +754,12 @@ bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm,
* that backs user memory and will ultimately be mapped into the device
* address space.
*/
-void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct hmm_range *range)
+void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct amdgpu_hmm_range *range)
{
unsigned long i;
for (i = 0; i < ttm->num_pages; ++i)
- ttm->pages[i] = range ? hmm_pfn_to_page(range->hmm_pfns[i]) : NULL;
+ ttm->pages[i] = range ? hmm_pfn_to_page(range->hmm_range.hmm_pfns[i]) : NULL;
}
/*
@@ -1372,7 +1329,7 @@ uint64_t amdgpu_ttm_tt_pde_flags(struct ttm_tt *ttm, struct ttm_resource *mem)
mem->mem_type == AMDGPU_PL_MMIO_REMAP)) {
flags |= AMDGPU_PTE_SYSTEM;
- if (ttm->caching == ttm_cached)
+ if (ttm && ttm->caching == ttm_cached)
flags |= AMDGPU_PTE_SNOOPED;
}
@@ -1529,6 +1486,7 @@ static int amdgpu_ttm_access_memory_sdma(struct ttm_buffer_object *bo,
if (r)
goto out;
+ mutex_lock(&adev->mman.gtt_window_lock);
amdgpu_res_first(abo->tbo.resource, offset, len, &src_mm);
src_addr = amdgpu_ttm_domain_start(adev, bo->resource->mem_type) +
src_mm.start;
@@ -1543,6 +1501,7 @@ static int amdgpu_ttm_access_memory_sdma(struct ttm_buffer_object *bo,
WARN_ON(job->ibs[0].length_dw > num_dw);
fence = amdgpu_job_submit(job);
+ mutex_unlock(&adev->mman.gtt_window_lock);
if (!dma_fence_wait_timeout(fence, false, adev->sdma_timeout))
r = -ETIMEDOUT;
@@ -1804,18 +1763,14 @@ static int amdgpu_ttm_reserve_tmr(struct amdgpu_device *adev)
ctx->init = PSP_MEM_TRAIN_RESERVE_SUCCESS;
}
- if (!adev->gmc.is_app_apu) {
- ret = amdgpu_bo_create_kernel_at(
- adev, adev->gmc.real_vram_size - reserve_size,
- reserve_size, &adev->mman.fw_reserved_memory, NULL);
- if (ret) {
- dev_err(adev->dev, "alloc tmr failed(%d)!\n", ret);
- amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory,
- NULL, NULL);
- return ret;
- }
- } else {
- DRM_DEBUG_DRIVER("backdoor fw loading path for PSP TMR, no reservation needed\n");
+ ret = amdgpu_bo_create_kernel_at(
+ adev, adev->gmc.real_vram_size - reserve_size, reserve_size,
+ &adev->mman.fw_reserved_memory, NULL);
+ if (ret) {
+ dev_err(adev->dev, "alloc tmr failed(%d)!\n", ret);
+ amdgpu_bo_free_kernel(&adev->mman.fw_reserved_memory, NULL,
+ NULL);
+ return ret;
}
return 0;
@@ -1837,7 +1792,7 @@ static int amdgpu_ttm_pools_init(struct amdgpu_device *adev)
for (i = 0; i < adev->gmc.num_mem_partitions; i++) {
ttm_pool_init(&adev->mman.ttm_pools[i], adev->dev,
adev->gmc.mem_partitions[i].numa.node,
- false, false);
+ TTM_ALLOCATION_POOL_BENEFICIAL_ORDER(get_order(SZ_2M)));
}
return 0;
}
@@ -1930,8 +1885,11 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
r = ttm_device_init(&adev->mman.bdev, &amdgpu_bo_driver, adev->dev,
adev_to_drm(adev)->anon_inode->i_mapping,
adev_to_drm(adev)->vma_offset_manager,
- adev->need_swiotlb,
- dma_addressing_limited(adev->dev));
+ (adev->need_swiotlb ?
+ TTM_ALLOCATION_POOL_USE_DMA_ALLOC : 0) |
+ (dma_addressing_limited(adev->dev) ?
+ TTM_ALLOCATION_POOL_USE_DMA32 : 0) |
+ TTM_ALLOCATION_POOL_BENEFICIAL_ORDER(get_order(SZ_2M)));
if (r) {
dev_err(adev->dev,
"failed initializing buffer object driver(%d).\n", r);
@@ -1980,19 +1938,19 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
return r;
/*
- *The reserved vram for driver must be pinned to the specified
- *place on the VRAM, so reserve it early.
+ * The reserved VRAM for the driver must be pinned to a specific
+ * location in VRAM, so reserve it early.
*/
r = amdgpu_ttm_drv_reserve_vram_init(adev);
if (r)
return r;
/*
- * only NAVI10 and onwards ASIC support for IP discovery.
- * If IP discovery enabled, a block of memory should be
- * reserved for IP discovey.
+ * only NAVI10 and later ASICs support IP discovery.
+ * If IP discovery is enabled, a block of memory should be
+ * reserved for it.
*/
- if (adev->mman.discovery_bin) {
+ if (adev->discovery.reserve_tmr) {
r = amdgpu_ttm_reserve_tmr(adev);
if (r)
return r;
@@ -2229,8 +2187,10 @@ void amdgpu_ttm_set_buffer_funcs_status(struct amdgpu_device *adev, bool enable)
} else {
drm_sched_entity_destroy(&adev->mman.high_pr);
drm_sched_entity_destroy(&adev->mman.low_pr);
- dma_fence_put(man->move);
- man->move = NULL;
+ /* Drop all the old fences since re-creating the scheduler entities
+ * will allocate new contexts.
+ */
+ ttm_resource_manager_cleanup(man);
}
/* this just adjusts TTM size idea, which sets lpfn to the correct value */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
index 0be2728aa872..577ee04ce0bf 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
@@ -28,6 +28,7 @@
#include <drm/gpu_scheduler.h>
#include <drm/ttm/ttm_placement.h>
#include "amdgpu_vram_mgr.h"
+#include "amdgpu_hmm.h"
#define AMDGPU_PL_GDS (TTM_PL_PRIV + 0)
#define AMDGPU_PL_GWS (TTM_PL_PRIV + 1)
@@ -82,9 +83,6 @@ struct amdgpu_mman {
uint64_t stolen_reserved_offset;
uint64_t stolen_reserved_size;
- /* discovery */
- uint8_t *discovery_bin;
- uint32_t discovery_tmr_size;
/* fw reserved memory */
struct amdgpu_bo *fw_reserved_memory;
struct amdgpu_bo *fw_reserved_memory_extend;
@@ -170,12 +168,6 @@ int amdgpu_copy_buffer(struct amdgpu_ring *ring, uint64_t src_offset,
struct dma_resv *resv,
struct dma_fence **fence, bool direct_submit,
bool vm_needs_flush, uint32_t copy_flags);
-int amdgpu_ttm_copy_mem_to_mem(struct amdgpu_device *adev,
- const struct amdgpu_copy_mem *src,
- const struct amdgpu_copy_mem *dst,
- uint64_t size, bool tmz,
- struct dma_resv *resv,
- struct dma_fence **f);
int amdgpu_ttm_clear_buffer(struct amdgpu_bo *bo,
struct dma_resv *resv,
struct dma_fence **fence);
@@ -192,29 +184,16 @@ uint64_t amdgpu_ttm_domain_start(struct amdgpu_device *adev, uint32_t type);
#if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR)
int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo,
- struct hmm_range **range);
-void amdgpu_ttm_tt_discard_user_pages(struct ttm_tt *ttm,
- struct hmm_range *range);
-bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm,
- struct hmm_range *range);
+ struct amdgpu_hmm_range *range);
#else
static inline int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo,
- struct hmm_range **range)
+ struct amdgpu_hmm_range *range)
{
return -EPERM;
}
-static inline void amdgpu_ttm_tt_discard_user_pages(struct ttm_tt *ttm,
- struct hmm_range *range)
-{
-}
-static inline bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm,
- struct hmm_range *range)
-{
- return false;
-}
#endif
-void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct hmm_range *range);
+void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct amdgpu_hmm_range *range);
int amdgpu_ttm_tt_get_userptr(const struct ttm_buffer_object *tbo,
uint64_t *user_addr);
int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c
index 2e039fb778ea..3f0b0e9af4f3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c
@@ -24,6 +24,7 @@
#include <linux/sort.h>
#include "amdgpu.h"
#include "umc_v6_7.h"
+#include "amdgpu_ras_mgr.h"
#define MAX_UMC_POISON_POLLING_TIME_SYNC 20 //ms
#define MAX_UMC_HASH_STRING_SIZE 256
@@ -96,67 +97,96 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev,
{
struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status;
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ struct amdgpu_ras_eeprom_control *control = &con->eeprom_control;
unsigned int error_query_mode;
int ret = 0;
unsigned long err_count;
amdgpu_ras_get_error_query_mode(adev, &error_query_mode);
+ err_data->err_addr =
+ kcalloc(adev->umc.max_ras_err_cnt_per_query,
+ sizeof(struct eeprom_table_record), GFP_KERNEL);
+
+ /* still call query_ras_error_address to clear error status
+ * even NOMEM error is encountered
+ */
+ if (!err_data->err_addr)
+ dev_warn(adev->dev,
+ "Failed to alloc memory for umc error address record!\n");
+ else
+ err_data->err_addr_len = adev->umc.max_ras_err_cnt_per_query;
+
mutex_lock(&con->page_retirement_lock);
- ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(con->umc_ecc));
- if (ret == -EOPNOTSUPP &&
- error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) {
- if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
- adev->umc.ras->ras_block.hw_ops->query_ras_error_count)
- adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev, ras_error_status);
-
- if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
- adev->umc.ras->ras_block.hw_ops->query_ras_error_address &&
- adev->umc.max_ras_err_cnt_per_query) {
- err_data->err_addr =
- kcalloc(adev->umc.max_ras_err_cnt_per_query,
- sizeof(struct eeprom_table_record), GFP_KERNEL);
-
- /* still call query_ras_error_address to clear error status
- * even NOMEM error is encountered
- */
- if(!err_data->err_addr)
- dev_warn(adev->dev, "Failed to alloc memory for "
- "umc error address record!\n");
- else
- err_data->err_addr_len = adev->umc.max_ras_err_cnt_per_query;
-
- /* umc query_ras_error_address is also responsible for clearing
- * error status
- */
- adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev, ras_error_status);
+ if (!amdgpu_ras_smu_eeprom_supported(adev)) {
+ ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(con->umc_ecc));
+ if (ret == -EOPNOTSUPP &&
+ error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) {
+ if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_count)
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev,
+ ras_error_status);
+
+ if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_address &&
+ adev->umc.max_ras_err_cnt_per_query) {
+ err_data->err_addr =
+ kcalloc(adev->umc.max_ras_err_cnt_per_query,
+ sizeof(struct eeprom_table_record), GFP_KERNEL);
+
+ /* still call query_ras_error_address to clear error status
+ * even NOMEM error is encountered
+ */
+ if (!err_data->err_addr)
+ dev_warn(adev->dev,
+ "Failed to alloc memory for umc error address record!\n");
+ else
+ err_data->err_addr_len =
+ adev->umc.max_ras_err_cnt_per_query;
+
+ /* umc query_ras_error_address is also responsible for clearing
+ * error status
+ */
+ adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev,
+ ras_error_status);
+ }
+ } else if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY ||
+ (!ret && error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY)) {
+ if (adev->umc.ras &&
+ adev->umc.ras->ecc_info_query_ras_error_count)
+ adev->umc.ras->ecc_info_query_ras_error_count(adev,
+ ras_error_status);
+
+ if (adev->umc.ras &&
+ adev->umc.ras->ecc_info_query_ras_error_address &&
+ adev->umc.max_ras_err_cnt_per_query) {
+ err_data->err_addr =
+ kcalloc(adev->umc.max_ras_err_cnt_per_query,
+ sizeof(struct eeprom_table_record), GFP_KERNEL);
+
+ /* still call query_ras_error_address to clear error status
+ * even NOMEM error is encountered
+ */
+ if (!err_data->err_addr)
+ dev_warn(adev->dev,
+ "Failed to alloc memory for umc error address record!\n");
+ else
+ err_data->err_addr_len =
+ adev->umc.max_ras_err_cnt_per_query;
+
+ /* umc query_ras_error_address is also responsible for clearing
+ * error status
+ */
+ adev->umc.ras->ecc_info_query_ras_error_address(adev,
+ ras_error_status);
+ }
}
- } else if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY ||
- (!ret && error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY)) {
- if (adev->umc.ras &&
- adev->umc.ras->ecc_info_query_ras_error_count)
- adev->umc.ras->ecc_info_query_ras_error_count(adev, ras_error_status);
-
- if (adev->umc.ras &&
- adev->umc.ras->ecc_info_query_ras_error_address &&
- adev->umc.max_ras_err_cnt_per_query) {
- err_data->err_addr =
- kcalloc(adev->umc.max_ras_err_cnt_per_query,
- sizeof(struct eeprom_table_record), GFP_KERNEL);
-
- /* still call query_ras_error_address to clear error status
- * even NOMEM error is encountered
- */
- if(!err_data->err_addr)
- dev_warn(adev->dev, "Failed to alloc memory for "
- "umc error address record!\n");
- else
- err_data->err_addr_len = adev->umc.max_ras_err_cnt_per_query;
-
- /* umc query_ras_error_address is also responsible for clearing
- * error status
- */
- adev->umc.ras->ecc_info_query_ras_error_address(adev, ras_error_status);
+ } else {
+ if (!amdgpu_ras_eeprom_update_record_num(control)) {
+ err_data->err_addr_cnt = err_data->de_count =
+ control->ras_num_recs - control->ras_num_recs_old;
+ amdgpu_ras_eeprom_read_idx(control, err_data->err_addr,
+ control->ras_num_recs_old, err_data->de_count);
}
}
@@ -166,7 +196,7 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev,
if ((amdgpu_bad_page_threshold != 0) &&
err_data->err_addr_cnt) {
amdgpu_ras_add_bad_pages(adev, err_data->err_addr,
- err_data->err_addr_cnt, false);
+ err_data->err_addr_cnt, amdgpu_ras_smu_eeprom_supported(adev));
amdgpu_ras_save_bad_pages(adev, &err_count);
amdgpu_dpm_send_hbm_bad_pages_num(adev,
@@ -244,6 +274,15 @@ int amdgpu_umc_pasid_poison_handler(struct amdgpu_device *adev,
}
amdgpu_ras_error_data_fini(&err_data);
+ } else if (amdgpu_uniras_enabled(adev)) {
+ struct ras_ih_info ih_info = {0};
+
+ ih_info.block = block;
+ ih_info.pasid = pasid;
+ ih_info.reset = reset;
+ ih_info.pasid_fn = pasid_fn;
+ ih_info.data = data;
+ amdgpu_ras_mgr_handle_consumer_interrupt(adev, &ih_info);
} else {
struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
int ret;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h
index ec203f9e5ffa..28dff750c47e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h
@@ -113,6 +113,8 @@ struct amdgpu_umc_ras {
uint32_t (*get_die_id_from_pa)(struct amdgpu_device *adev,
uint64_t mca_addr, uint64_t retired_page);
void (*get_retire_flip_bits)(struct amdgpu_device *adev);
+ void (*mca_ipid_parse)(struct amdgpu_device *adev, uint64_t ipid,
+ uint32_t *did, uint32_t *ch, uint32_t *umc_inst, uint32_t *sid);
};
struct amdgpu_umc_funcs {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 1add21160d21..9a969175900e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@ -25,10 +25,13 @@
#include <drm/drm_auth.h>
#include <drm/drm_exec.h>
#include <linux/pm_runtime.h>
+#include <drm/drm_drv.h>
#include "amdgpu.h"
+#include "amdgpu_reset.h"
#include "amdgpu_vm.h"
#include "amdgpu_userq.h"
+#include "amdgpu_hmm.h"
#include "amdgpu_userq_fence.h"
u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev)
@@ -44,10 +47,130 @@ u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev)
return userq_ip_mask;
}
-int amdgpu_userq_input_va_validate(struct amdgpu_vm *vm, u64 addr,
- u64 expected_size)
+static bool amdgpu_userq_is_reset_type_supported(struct amdgpu_device *adev,
+ enum amdgpu_ring_type ring_type, int reset_type)
+{
+
+ if (ring_type < 0 || ring_type >= AMDGPU_RING_TYPE_MAX)
+ return false;
+
+ switch (ring_type) {
+ case AMDGPU_RING_TYPE_GFX:
+ if (adev->gfx.gfx_supported_reset & reset_type)
+ return true;
+ break;
+ case AMDGPU_RING_TYPE_COMPUTE:
+ if (adev->gfx.compute_supported_reset & reset_type)
+ return true;
+ break;
+ case AMDGPU_RING_TYPE_SDMA:
+ if (adev->sdma.supported_reset & reset_type)
+ return true;
+ break;
+ case AMDGPU_RING_TYPE_VCN_DEC:
+ case AMDGPU_RING_TYPE_VCN_ENC:
+ if (adev->vcn.supported_reset & reset_type)
+ return true;
+ break;
+ case AMDGPU_RING_TYPE_VCN_JPEG:
+ if (adev->jpeg.supported_reset & reset_type)
+ return true;
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+static void amdgpu_userq_gpu_reset(struct amdgpu_device *adev)
+{
+ if (amdgpu_device_should_recover_gpu(adev)) {
+ amdgpu_reset_domain_schedule(adev->reset_domain,
+ &adev->userq_reset_work);
+ /* Wait for the reset job to complete */
+ flush_work(&adev->userq_reset_work);
+ }
+}
+
+static int
+amdgpu_userq_detect_and_reset_queues(struct amdgpu_userq_mgr *uq_mgr)
+{
+ struct amdgpu_device *adev = uq_mgr->adev;
+ const int queue_types[] = {
+ AMDGPU_RING_TYPE_COMPUTE,
+ AMDGPU_RING_TYPE_GFX,
+ AMDGPU_RING_TYPE_SDMA
+ };
+ const int num_queue_types = ARRAY_SIZE(queue_types);
+ bool gpu_reset = false;
+ int r = 0;
+ int i;
+
+ /* Warning if current process mutex is not held */
+ WARN_ON(!mutex_is_locked(&uq_mgr->userq_mutex));
+
+ if (unlikely(adev->debug_disable_gpu_ring_reset)) {
+ dev_err(adev->dev, "userq reset disabled by debug mask\n");
+ return 0;
+ }
+
+ /*
+ * If GPU recovery feature is disabled system-wide,
+ * skip all reset detection logic
+ */
+ if (!amdgpu_gpu_recovery)
+ return 0;
+
+ /*
+ * Iterate through all queue types to detect and reset problematic queues
+ * Process each queue type in the defined order
+ */
+ for (i = 0; i < num_queue_types; i++) {
+ int ring_type = queue_types[i];
+ const struct amdgpu_userq_funcs *funcs = adev->userq_funcs[ring_type];
+
+ if (!amdgpu_userq_is_reset_type_supported(adev, ring_type, AMDGPU_RESET_TYPE_PER_QUEUE))
+ continue;
+
+ if (atomic_read(&uq_mgr->userq_count[ring_type]) > 0 &&
+ funcs && funcs->detect_and_reset) {
+ r = funcs->detect_and_reset(adev, ring_type);
+ if (r) {
+ gpu_reset = true;
+ break;
+ }
+ }
+ }
+
+ if (gpu_reset)
+ amdgpu_userq_gpu_reset(adev);
+
+ return r;
+}
+
+static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue,
+ struct amdgpu_bo_va_mapping *va_map, u64 addr)
+{
+ struct amdgpu_userq_va_cursor *va_cursor;
+ struct userq_va_list;
+
+ va_cursor = kzalloc(sizeof(*va_cursor), GFP_KERNEL);
+ if (!va_cursor)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&va_cursor->list);
+ va_cursor->gpu_addr = addr;
+ atomic_set(&va_map->bo_va->userq_va_mapped, 1);
+ list_add(&va_cursor->list, &queue->userq_va_list);
+
+ return 0;
+}
+
+int amdgpu_userq_input_va_validate(struct amdgpu_usermode_queue *queue,
+ u64 addr, u64 expected_size)
{
struct amdgpu_bo_va_mapping *va_map;
+ struct amdgpu_vm *vm = queue->vm;
u64 user_addr;
u64 size;
int r = 0;
@@ -67,6 +190,7 @@ int amdgpu_userq_input_va_validate(struct amdgpu_vm *vm, u64 addr,
/* Only validate the userq whether resident in the VM mapping range */
if (user_addr >= va_map->start &&
va_map->last - user_addr + 1 >= size) {
+ amdgpu_userq_buffer_va_list_add(queue, va_map, user_addr);
amdgpu_bo_unreserve(vm->root.bo);
return 0;
}
@@ -77,6 +201,76 @@ out_err:
return r;
}
+static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr)
+{
+ struct amdgpu_bo_va_mapping *mapping;
+ bool r;
+
+ if (amdgpu_bo_reserve(vm->root.bo, false))
+ return false;
+
+ mapping = amdgpu_vm_bo_lookup_mapping(vm, addr);
+ if (!IS_ERR_OR_NULL(mapping) && atomic_read(&mapping->bo_va->userq_va_mapped))
+ r = true;
+ else
+ r = false;
+ amdgpu_bo_unreserve(vm->root.bo);
+
+ return r;
+}
+
+static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue)
+{
+ struct amdgpu_userq_va_cursor *va_cursor, *tmp;
+ int r = 0;
+
+ list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) {
+ r += amdgpu_userq_buffer_va_mapped(queue->vm, va_cursor->gpu_addr);
+ dev_dbg(queue->userq_mgr->adev->dev,
+ "validate the userq mapping:%p va:%llx r:%d\n",
+ queue, va_cursor->gpu_addr, r);
+ }
+
+ if (r != 0)
+ return true;
+
+ return false;
+}
+
+static void amdgpu_userq_buffer_va_list_del(struct amdgpu_bo_va_mapping *mapping,
+ struct amdgpu_userq_va_cursor *va_cursor)
+{
+ atomic_set(&mapping->bo_va->userq_va_mapped, 0);
+ list_del(&va_cursor->list);
+ kfree(va_cursor);
+}
+
+static int amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev,
+ struct amdgpu_usermode_queue *queue)
+{
+ struct amdgpu_userq_va_cursor *va_cursor, *tmp;
+ struct amdgpu_bo_va_mapping *mapping;
+ int r;
+
+ r = amdgpu_bo_reserve(queue->vm->root.bo, false);
+ if (r)
+ return r;
+
+ list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) {
+ mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, va_cursor->gpu_addr);
+ if (!mapping) {
+ r = -EINVAL;
+ goto err;
+ }
+ dev_dbg(adev->dev, "delete the userq:%p va:%llx\n",
+ queue, va_cursor->gpu_addr);
+ amdgpu_userq_buffer_va_list_del(mapping, va_cursor);
+ }
+err:
+ amdgpu_bo_unreserve(queue->vm->root.bo);
+ return r;
+}
+
static int
amdgpu_userq_preempt_helper(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_usermode_queue *queue)
@@ -84,17 +278,22 @@ amdgpu_userq_preempt_helper(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_device *adev = uq_mgr->adev;
const struct amdgpu_userq_funcs *userq_funcs =
adev->userq_funcs[queue->queue_type];
+ bool found_hung_queue = false;
int r = 0;
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
r = userq_funcs->preempt(uq_mgr, queue);
if (r) {
queue->state = AMDGPU_USERQ_STATE_HUNG;
+ found_hung_queue = true;
} else {
queue->state = AMDGPU_USERQ_STATE_PREEMPTED;
}
}
+ if (found_hung_queue)
+ amdgpu_userq_detect_and_reset_queues(uq_mgr);
+
return r;
}
@@ -126,16 +325,23 @@ amdgpu_userq_unmap_helper(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_device *adev = uq_mgr->adev;
const struct amdgpu_userq_funcs *userq_funcs =
adev->userq_funcs[queue->queue_type];
+ bool found_hung_queue = false;
int r = 0;
if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) ||
(queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
r = userq_funcs->unmap(uq_mgr, queue);
- if (r)
+ if (r) {
queue->state = AMDGPU_USERQ_STATE_HUNG;
- else
+ found_hung_queue = true;
+ } else {
queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
+ }
}
+
+ if (found_hung_queue)
+ amdgpu_userq_detect_and_reset_queues(uq_mgr);
+
return r;
}
@@ -152,26 +358,33 @@ amdgpu_userq_map_helper(struct amdgpu_userq_mgr *uq_mgr,
r = userq_funcs->map(uq_mgr, queue);
if (r) {
queue->state = AMDGPU_USERQ_STATE_HUNG;
+ amdgpu_userq_detect_and_reset_queues(uq_mgr);
} else {
queue->state = AMDGPU_USERQ_STATE_MAPPED;
}
}
+
return r;
}
-static void
+static int
amdgpu_userq_wait_for_last_fence(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_usermode_queue *queue)
{
struct dma_fence *f = queue->last_fence;
- int ret;
+ int ret = 0;
if (f && !dma_fence_is_signaled(f)) {
- ret = dma_fence_wait_timeout(f, true, msecs_to_jiffies(100));
- if (ret <= 0)
+ ret = dma_fence_wait_timeout(f, true, MAX_SCHEDULE_TIMEOUT);
+ if (ret <= 0) {
drm_file_err(uq_mgr->file, "Timed out waiting for fence=%llu:%llu\n",
f->context, f->seqno);
+ queue->state = AMDGPU_USERQ_STATE_HUNG;
+ return -ETIME;
+ }
}
+
+ return ret;
}
static void
@@ -182,16 +395,27 @@ amdgpu_userq_cleanup(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_device *adev = uq_mgr->adev;
const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type];
+ /* Wait for mode-1 reset to complete */
+ down_read(&adev->reset_domain->sem);
+
+ /* Drop the userq reference. */
+ amdgpu_userq_buffer_vas_list_cleanup(adev, queue);
uq_funcs->mqd_destroy(uq_mgr, queue);
amdgpu_userq_fence_driver_free(queue);
- idr_remove(&uq_mgr->userq_idr, queue_id);
+ /* Use interrupt-safe locking since IRQ handlers may access these XArrays */
+ xa_erase_irq(&uq_mgr->userq_mgr_xa, (unsigned long)queue_id);
+ xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
+ queue->userq_mgr = NULL;
+ list_del(&queue->userq_va_list);
kfree(queue);
+
+ up_read(&adev->reset_domain->sem);
}
static struct amdgpu_usermode_queue *
amdgpu_userq_find(struct amdgpu_userq_mgr *uq_mgr, int qid)
{
- return idr_find(&uq_mgr->userq_idr, qid);
+ return xa_load(&uq_mgr->userq_mgr_xa, qid);
}
void
@@ -319,17 +543,6 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr,
case AMDGPU_HW_IP_DMA:
db_size = sizeof(u64);
break;
-
- case AMDGPU_HW_IP_VCN_ENC:
- db_size = sizeof(u32);
- db_info->doorbell_offset += AMDGPU_NAVI10_DOORBELL64_VCN0_1 << 1;
- break;
-
- case AMDGPU_HW_IP_VPE:
- db_size = sizeof(u32);
- db_info->doorbell_offset += AMDGPU_NAVI10_DOORBELL64_VPE << 1;
- break;
-
default:
drm_file_err(uq_mgr->file, "[Usermode queues] IP %d not support\n",
db_info->queue_type);
@@ -378,10 +591,11 @@ amdgpu_userq_destroy(struct drm_file *filp, int queue_id)
amdgpu_bo_unreserve(queue->db_obj.obj);
}
amdgpu_bo_unref(&queue->db_obj.obj);
-
+ atomic_dec(&uq_mgr->userq_count[queue->queue_type]);
#if defined(CONFIG_DEBUG_FS)
debugfs_remove_recursive(queue->debugfs_queue);
#endif
+ amdgpu_userq_detect_and_reset_queues(uq_mgr);
r = amdgpu_userq_unmap_helper(uq_mgr, queue);
/*TODO: It requires a reset for userq hw unmap error*/
if (unlikely(r != AMDGPU_USERQ_STATE_UNMAPPED)) {
@@ -391,7 +605,6 @@ amdgpu_userq_destroy(struct drm_file *filp, int queue_id)
amdgpu_userq_cleanup(uq_mgr, queue, queue_id);
mutex_unlock(&uq_mgr->userq_mutex);
- pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
@@ -463,8 +676,9 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
struct amdgpu_db_info db_info;
char *queue_name;
bool skip_map_queue;
+ u32 qid;
uint64_t index;
- int qid, r = 0;
+ int r = 0;
int priority =
(args->in.flags & AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_MASK) >>
AMDGPU_USERQ_CREATE_FLAGS_QUEUE_PRIORITY_SHIFT;
@@ -487,7 +701,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
*
* This will also make sure we have a valid eviction fence ready to be used.
*/
- mutex_lock(&adev->userq_mutex);
amdgpu_userq_ensure_ev_fence(&fpriv->userq_mgr, &fpriv->evf_mgr);
uq_funcs = adev->userq_funcs[args->in.ip_type];
@@ -505,14 +718,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
goto unlock;
}
- /* Validate the userq virtual address.*/
- if (amdgpu_userq_input_va_validate(&fpriv->vm, args->in.queue_va, args->in.queue_size) ||
- amdgpu_userq_input_va_validate(&fpriv->vm, args->in.rptr_va, AMDGPU_GPU_PAGE_SIZE) ||
- amdgpu_userq_input_va_validate(&fpriv->vm, args->in.wptr_va, AMDGPU_GPU_PAGE_SIZE)) {
- r = -EINVAL;
- kfree(queue);
- goto unlock;
- }
+ INIT_LIST_HEAD(&queue->userq_va_list);
queue->doorbell_handle = args->in.doorbell_handle;
queue->queue_type = args->in.ip_type;
queue->vm = &fpriv->vm;
@@ -523,6 +729,15 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
db_info.db_obj = &queue->db_obj;
db_info.doorbell_offset = args->in.doorbell_offset;
+ /* Validate the userq virtual address.*/
+ if (amdgpu_userq_input_va_validate(queue, args->in.queue_va, args->in.queue_size) ||
+ amdgpu_userq_input_va_validate(queue, args->in.rptr_va, AMDGPU_GPU_PAGE_SIZE) ||
+ amdgpu_userq_input_va_validate(queue, args->in.wptr_va, AMDGPU_GPU_PAGE_SIZE)) {
+ r = -EINVAL;
+ kfree(queue);
+ goto unlock;
+ }
+
/* Convert relative doorbell offset into absolute doorbell index */
index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp);
if (index == (uint64_t)-EINVAL) {
@@ -548,16 +763,27 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
goto unlock;
}
+ /* Wait for mode-1 reset to complete */
+ down_read(&adev->reset_domain->sem);
+ r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue, GFP_KERNEL));
+ if (r) {
+ kfree(queue);
+ up_read(&adev->reset_domain->sem);
+ goto unlock;
+ }
- qid = idr_alloc(&uq_mgr->userq_idr, queue, 1, AMDGPU_MAX_USERQ_COUNT, GFP_KERNEL);
- if (qid < 0) {
+ r = xa_alloc(&uq_mgr->userq_mgr_xa, &qid, queue, XA_LIMIT(1, AMDGPU_MAX_USERQ_COUNT), GFP_KERNEL);
+ if (r) {
drm_file_err(uq_mgr->file, "Failed to allocate a queue id\n");
amdgpu_userq_fence_driver_free(queue);
uq_funcs->mqd_destroy(uq_mgr, queue);
kfree(queue);
r = -ENOMEM;
+ up_read(&adev->reset_domain->sem);
goto unlock;
}
+ up_read(&adev->reset_domain->sem);
+ queue->userq_mgr = uq_mgr;
/* don't map the queue if scheduling is halted */
if (adev->userq_halt_for_enforce_isolation &&
@@ -570,7 +796,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
r = amdgpu_userq_map_helper(uq_mgr, queue);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
- idr_remove(&uq_mgr->userq_idr, qid);
+ xa_erase(&uq_mgr->userq_mgr_xa, qid);
amdgpu_userq_fence_driver_free(queue);
uq_funcs->mqd_destroy(uq_mgr, queue);
kfree(queue);
@@ -592,10 +818,10 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
kfree(queue_name);
args->out.queue_id = qid;
+ atomic_inc(&uq_mgr->userq_count[queue->queue_type]);
unlock:
mutex_unlock(&uq_mgr->userq_mutex);
- mutex_unlock(&adev->userq_mutex);
return r;
}
@@ -693,11 +919,19 @@ static int
amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr)
{
struct amdgpu_usermode_queue *queue;
- int queue_id;
+ unsigned long queue_id;
int ret = 0, r;
/* Resume all the queues for this process */
- idr_for_each_entry(&uq_mgr->userq_idr, queue, queue_id) {
+ xa_for_each(&uq_mgr->userq_mgr_xa, queue_id, queue) {
+
+ if (!amdgpu_userq_buffer_vas_mapped(queue)) {
+ drm_file_err(uq_mgr->file,
+ "trying restore queue without va mapping\n");
+ queue->state = AMDGPU_USERQ_STATE_INVALID_VA;
+ continue;
+ }
+
r = amdgpu_userq_restore_helper(uq_mgr, queue);
if (r)
ret = r;
@@ -760,12 +994,21 @@ static int
amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
{
struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr);
+ bool invalidated = false, new_addition = false;
+ struct ttm_operation_ctx ctx = { true, false };
struct amdgpu_device *adev = uq_mgr->adev;
+ struct amdgpu_hmm_range *range;
struct amdgpu_vm *vm = &fpriv->vm;
+ unsigned long key, tmp_key;
struct amdgpu_bo_va *bo_va;
+ struct amdgpu_bo *bo;
struct drm_exec exec;
+ struct xarray xa;
int ret;
+ xa_init(&xa);
+
+retry_lock:
drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 0);
drm_exec_until_all_locked(&exec) {
ret = amdgpu_vm_lock_pd(vm, &exec, 1);
@@ -792,10 +1035,74 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
goto unlock_all;
}
+ if (invalidated) {
+ xa_for_each(&xa, tmp_key, range) {
+ bo = range->bo;
+ amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_CPU);
+ ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
+ if (ret)
+ goto unlock_all;
+
+ amdgpu_ttm_tt_set_user_pages(bo->tbo.ttm, range);
+
+ amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_GTT);
+ ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);
+ if (ret)
+ goto unlock_all;
+ }
+ invalidated = false;
+ }
+
ret = amdgpu_vm_handle_moved(adev, vm, NULL);
if (ret)
goto unlock_all;
+ key = 0;
+ /* Validate User Ptr BOs */
+ list_for_each_entry(bo_va, &vm->done, base.vm_status) {
+ bo = bo_va->base.bo;
+ if (!bo)
+ continue;
+
+ if (!amdgpu_ttm_tt_is_userptr(bo->tbo.ttm))
+ continue;
+
+ range = xa_load(&xa, key);
+ if (range && range->bo != bo) {
+ xa_erase(&xa, key);
+ amdgpu_hmm_range_free(range);
+ range = NULL;
+ }
+
+ if (!range) {
+ range = amdgpu_hmm_range_alloc(bo);
+ if (!range) {
+ ret = -ENOMEM;
+ goto unlock_all;
+ }
+
+ xa_store(&xa, key, range, GFP_KERNEL);
+ new_addition = true;
+ }
+ key++;
+ }
+
+ if (new_addition) {
+ drm_exec_fini(&exec);
+ xa_for_each(&xa, tmp_key, range) {
+ if (!range)
+ continue;
+ bo = range->bo;
+ ret = amdgpu_ttm_tt_get_user_pages(bo, range);
+ if (ret)
+ goto unlock_all;
+ }
+
+ invalidated = true;
+ new_addition = false;
+ goto retry_lock;
+ }
+
ret = amdgpu_vm_update_pdes(adev, vm, false);
if (ret)
goto unlock_all;
@@ -815,6 +1122,13 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
unlock_all:
drm_exec_fini(&exec);
+ xa_for_each(&xa, tmp_key, range) {
+ if (!range)
+ continue;
+ bo = range->bo;
+ amdgpu_hmm_range_free(range);
+ }
+ xa_destroy(&xa);
return ret;
}
@@ -848,11 +1162,12 @@ static int
amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
{
struct amdgpu_usermode_queue *queue;
- int queue_id;
+ unsigned long queue_id;
int ret = 0, r;
+ amdgpu_userq_detect_and_reset_queues(uq_mgr);
/* Try to unmap all the queues in this process ctx */
- idr_for_each_entry(&uq_mgr->userq_idr, queue, queue_id) {
+ xa_for_each(&uq_mgr->userq_mgr_xa, queue_id, queue) {
r = amdgpu_userq_preempt_helper(uq_mgr, queue);
if (r)
ret = r;
@@ -863,13 +1178,31 @@ amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
return ret;
}
+void amdgpu_userq_reset_work(struct work_struct *work)
+{
+ struct amdgpu_device *adev = container_of(work, struct amdgpu_device,
+ userq_reset_work);
+ struct amdgpu_reset_context reset_context;
+
+ memset(&reset_context, 0, sizeof(reset_context));
+
+ reset_context.method = AMD_RESET_METHOD_NONE;
+ reset_context.reset_req_dev = adev;
+ reset_context.src = AMDGPU_RESET_SRC_USERQ;
+ set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags);
+ /*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/
+
+ amdgpu_device_gpu_recover(adev, NULL, &reset_context);
+}
+
static int
amdgpu_userq_wait_for_signal(struct amdgpu_userq_mgr *uq_mgr)
{
struct amdgpu_usermode_queue *queue;
- int queue_id, ret;
+ unsigned long queue_id;
+ int ret;
- idr_for_each_entry(&uq_mgr->userq_idr, queue, queue_id) {
+ xa_for_each(&uq_mgr->userq_mgr_xa, queue_id, queue) {
struct dma_fence *f = queue->last_fence;
if (!f || dma_fence_is_signaled(f))
@@ -889,22 +1222,19 @@ void
amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_eviction_fence *ev_fence)
{
- int ret;
struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr);
struct amdgpu_eviction_fence_mgr *evf_mgr = &fpriv->evf_mgr;
+ struct amdgpu_device *adev = uq_mgr->adev;
+ int ret;
/* Wait for any pending userqueue fence work to finish */
ret = amdgpu_userq_wait_for_signal(uq_mgr);
- if (ret) {
- drm_file_err(uq_mgr->file, "Not evicting userqueue, timeout waiting for work\n");
- return;
- }
+ if (ret)
+ dev_err(adev->dev, "Not evicting userqueue, timeout waiting for work\n");
ret = amdgpu_userq_evict_all(uq_mgr);
- if (ret) {
- drm_file_err(uq_mgr->file, "Failed to evict userqueue\n");
- return;
- }
+ if (ret)
+ dev_err(adev->dev, "Failed to evict userqueue\n");
/* Signal current eviction fence */
amdgpu_eviction_fence_signal(evf_mgr, ev_fence);
@@ -922,44 +1252,31 @@ int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *f
struct amdgpu_device *adev)
{
mutex_init(&userq_mgr->userq_mutex);
- idr_init_base(&userq_mgr->userq_idr, 1);
+ xa_init_flags(&userq_mgr->userq_mgr_xa, XA_FLAGS_ALLOC);
userq_mgr->adev = adev;
userq_mgr->file = file_priv;
- mutex_lock(&adev->userq_mutex);
- list_add(&userq_mgr->list, &adev->userq_mgr_list);
- mutex_unlock(&adev->userq_mutex);
-
INIT_DELAYED_WORK(&userq_mgr->resume_work, amdgpu_userq_restore_worker);
return 0;
}
void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr)
{
- struct amdgpu_device *adev = userq_mgr->adev;
struct amdgpu_usermode_queue *queue;
- struct amdgpu_userq_mgr *uqm, *tmp;
- uint32_t queue_id;
+ unsigned long queue_id;
cancel_delayed_work_sync(&userq_mgr->resume_work);
- mutex_lock(&adev->userq_mutex);
mutex_lock(&userq_mgr->userq_mutex);
- idr_for_each_entry(&userq_mgr->userq_idr, queue, queue_id) {
+ amdgpu_userq_detect_and_reset_queues(userq_mgr);
+ xa_for_each(&userq_mgr->userq_mgr_xa, queue_id, queue) {
amdgpu_userq_wait_for_last_fence(userq_mgr, queue);
amdgpu_userq_unmap_helper(userq_mgr, queue);
amdgpu_userq_cleanup(userq_mgr, queue, queue_id);
}
- list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) {
- if (uqm == userq_mgr) {
- list_del(&uqm->list);
- break;
- }
- }
- idr_destroy(&userq_mgr->userq_idr);
+ xa_destroy(&userq_mgr->userq_mgr_xa);
mutex_unlock(&userq_mgr->userq_mutex);
- mutex_unlock(&adev->userq_mutex);
mutex_destroy(&userq_mgr->userq_mutex);
}
@@ -967,57 +1284,51 @@ int amdgpu_userq_suspend(struct amdgpu_device *adev)
{
u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev);
struct amdgpu_usermode_queue *queue;
- struct amdgpu_userq_mgr *uqm, *tmp;
- int queue_id;
- int ret = 0, r;
+ struct amdgpu_userq_mgr *uqm;
+ unsigned long queue_id;
+ int r;
if (!ip_mask)
return 0;
- mutex_lock(&adev->userq_mutex);
- list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) {
+ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
+ uqm = queue->userq_mgr;
cancel_delayed_work_sync(&uqm->resume_work);
- mutex_lock(&uqm->userq_mutex);
- idr_for_each_entry(&uqm->userq_idr, queue, queue_id) {
- if (adev->in_s0ix)
- r = amdgpu_userq_preempt_helper(uqm, queue);
- else
- r = amdgpu_userq_unmap_helper(uqm, queue);
- if (r)
- ret = r;
- }
- mutex_unlock(&uqm->userq_mutex);
+ guard(mutex)(&uqm->userq_mutex);
+ amdgpu_userq_detect_and_reset_queues(uqm);
+ if (adev->in_s0ix)
+ r = amdgpu_userq_preempt_helper(uqm, queue);
+ else
+ r = amdgpu_userq_unmap_helper(uqm, queue);
+ if (r)
+ return r;
}
- mutex_unlock(&adev->userq_mutex);
- return ret;
+ return 0;
}
int amdgpu_userq_resume(struct amdgpu_device *adev)
{
u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev);
struct amdgpu_usermode_queue *queue;
- struct amdgpu_userq_mgr *uqm, *tmp;
- int queue_id;
- int ret = 0, r;
+ struct amdgpu_userq_mgr *uqm;
+ unsigned long queue_id;
+ int r;
if (!ip_mask)
return 0;
- mutex_lock(&adev->userq_mutex);
- list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) {
- mutex_lock(&uqm->userq_mutex);
- idr_for_each_entry(&uqm->userq_idr, queue, queue_id) {
- if (adev->in_s0ix)
- r = amdgpu_userq_restore_helper(uqm, queue);
- else
- r = amdgpu_userq_map_helper(uqm, queue);
- if (r)
- ret = r;
- }
- mutex_unlock(&uqm->userq_mutex);
+ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
+ uqm = queue->userq_mgr;
+ guard(mutex)(&uqm->userq_mutex);
+ if (adev->in_s0ix)
+ r = amdgpu_userq_restore_helper(uqm, queue);
+ else
+ r = amdgpu_userq_map_helper(uqm, queue);
+ if (r)
+ return r;
}
- mutex_unlock(&adev->userq_mutex);
- return ret;
+
+ return 0;
}
int amdgpu_userq_stop_sched_for_enforce_isolation(struct amdgpu_device *adev,
@@ -1025,33 +1336,32 @@ int amdgpu_userq_stop_sched_for_enforce_isolation(struct amdgpu_device *adev,
{
u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev);
struct amdgpu_usermode_queue *queue;
- struct amdgpu_userq_mgr *uqm, *tmp;
- int queue_id;
+ struct amdgpu_userq_mgr *uqm;
+ unsigned long queue_id;
int ret = 0, r;
/* only need to stop gfx/compute */
if (!(ip_mask & ((1 << AMDGPU_HW_IP_GFX) | (1 << AMDGPU_HW_IP_COMPUTE))))
return 0;
- mutex_lock(&adev->userq_mutex);
if (adev->userq_halt_for_enforce_isolation)
dev_warn(adev->dev, "userq scheduling already stopped!\n");
adev->userq_halt_for_enforce_isolation = true;
- list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) {
+ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
+ uqm = queue->userq_mgr;
cancel_delayed_work_sync(&uqm->resume_work);
mutex_lock(&uqm->userq_mutex);
- idr_for_each_entry(&uqm->userq_idr, queue, queue_id) {
- if (((queue->queue_type == AMDGPU_HW_IP_GFX) ||
- (queue->queue_type == AMDGPU_HW_IP_COMPUTE)) &&
- (queue->xcp_id == idx)) {
- r = amdgpu_userq_preempt_helper(uqm, queue);
- if (r)
- ret = r;
- }
+ if (((queue->queue_type == AMDGPU_HW_IP_GFX) ||
+ (queue->queue_type == AMDGPU_HW_IP_COMPUTE)) &&
+ (queue->xcp_id == idx)) {
+ amdgpu_userq_detect_and_reset_queues(uqm);
+ r = amdgpu_userq_preempt_helper(uqm, queue);
+ if (r)
+ ret = r;
}
mutex_unlock(&uqm->userq_mutex);
}
- mutex_unlock(&adev->userq_mutex);
+
return ret;
}
@@ -1060,21 +1370,20 @@ int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev,
{
u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev);
struct amdgpu_usermode_queue *queue;
- struct amdgpu_userq_mgr *uqm, *tmp;
- int queue_id;
+ struct amdgpu_userq_mgr *uqm;
+ unsigned long queue_id;
int ret = 0, r;
/* only need to stop gfx/compute */
if (!(ip_mask & ((1 << AMDGPU_HW_IP_GFX) | (1 << AMDGPU_HW_IP_COMPUTE))))
return 0;
- mutex_lock(&adev->userq_mutex);
if (!adev->userq_halt_for_enforce_isolation)
dev_warn(adev->dev, "userq scheduling already started!\n");
adev->userq_halt_for_enforce_isolation = false;
- list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) {
+ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
+ uqm = queue->userq_mgr;
mutex_lock(&uqm->userq_mutex);
- idr_for_each_entry(&uqm->userq_idr, queue, queue_id) {
if (((queue->queue_type == AMDGPU_HW_IP_GFX) ||
(queue->queue_type == AMDGPU_HW_IP_COMPUTE)) &&
(queue->xcp_id == idx)) {
@@ -1082,9 +1391,92 @@ int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev,
if (r)
ret = r;
}
- }
mutex_unlock(&uqm->userq_mutex);
}
- mutex_unlock(&adev->userq_mutex);
+
return ret;
}
+
+int amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev,
+ struct amdgpu_bo_va_mapping *mapping,
+ uint64_t saddr)
+{
+ u32 ip_mask = amdgpu_userq_get_supported_ip_mask(adev);
+ struct amdgpu_bo_va *bo_va = mapping->bo_va;
+ struct dma_resv *resv = bo_va->base.bo->tbo.base.resv;
+ int ret = 0;
+
+ if (!ip_mask)
+ return 0;
+
+ dev_warn_once(adev->dev, "now unmapping a vital queue va:%llx\n", saddr);
+ /**
+ * The userq VA mapping reservation should include the eviction fence,
+ * if the eviction fence can't signal successfully during unmapping,
+ * then driver will warn to flag this improper unmap of the userq VA.
+ * Note: The eviction fence may be attached to different BOs, and this
+ * unmap is only for one kind of userq VAs, so at this point suppose
+ * the eviction fence is always unsignaled.
+ */
+ if (!dma_resv_test_signaled(resv, DMA_RESV_USAGE_BOOKKEEP)) {
+ ret = dma_resv_wait_timeout(resv, DMA_RESV_USAGE_BOOKKEEP, true,
+ MAX_SCHEDULE_TIMEOUT);
+ if (ret <= 0)
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void amdgpu_userq_pre_reset(struct amdgpu_device *adev)
+{
+ const struct amdgpu_userq_funcs *userq_funcs;
+ struct amdgpu_usermode_queue *queue;
+ struct amdgpu_userq_mgr *uqm;
+ unsigned long queue_id;
+
+ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
+ uqm = queue->userq_mgr;
+ cancel_delayed_work_sync(&uqm->resume_work);
+ if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
+ amdgpu_userq_wait_for_last_fence(uqm, queue);
+ userq_funcs = adev->userq_funcs[queue->queue_type];
+ userq_funcs->unmap(uqm, queue);
+ /* just mark all queues as hung at this point.
+ * if unmap succeeds, we could map again
+ * in amdgpu_userq_post_reset() if vram is not lost
+ */
+ queue->state = AMDGPU_USERQ_STATE_HUNG;
+ amdgpu_userq_fence_driver_force_completion(queue);
+ }
+ }
+}
+
+int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
+{
+ /* if any queue state is AMDGPU_USERQ_STATE_UNMAPPED
+ * at this point, we should be able to map it again
+ * and continue if vram is not lost.
+ */
+ struct amdgpu_userq_mgr *uqm;
+ struct amdgpu_usermode_queue *queue;
+ const struct amdgpu_userq_funcs *userq_funcs;
+ unsigned long queue_id;
+ int r = 0;
+
+ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
+ uqm = queue->userq_mgr;
+ if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) {
+ userq_funcs = adev->userq_funcs[queue->queue_type];
+ /* Re-map queue */
+ r = userq_funcs->map(uqm, queue);
+ if (r) {
+ dev_err(adev->dev, "Failed to remap queue %ld\n", queue_id);
+ continue;
+ }
+ queue->state = AMDGPU_USERQ_STATE_MAPPED;
+ }
+ }
+
+ return r;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
index c027dd916672..c37444427a14 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h
@@ -37,6 +37,7 @@ enum amdgpu_userq_state {
AMDGPU_USERQ_STATE_MAPPED,
AMDGPU_USERQ_STATE_PREEMPTED,
AMDGPU_USERQ_STATE_HUNG,
+ AMDGPU_USERQ_STATE_INVALID_VA,
};
struct amdgpu_mqd_prop;
@@ -47,6 +48,11 @@ struct amdgpu_userq_obj {
struct amdgpu_bo *obj;
};
+struct amdgpu_userq_va_cursor {
+ u64 gpu_addr;
+ struct list_head list;
+};
+
struct amdgpu_usermode_queue {
int queue_type;
enum amdgpu_userq_state state;
@@ -66,6 +72,8 @@ struct amdgpu_usermode_queue {
u32 xcp_id;
int priority;
struct dentry *debugfs_queue;
+
+ struct list_head userq_va_list;
};
struct amdgpu_userq_funcs {
@@ -88,12 +96,17 @@ struct amdgpu_userq_funcs {
/* Usermode queues for gfx */
struct amdgpu_userq_mgr {
- struct idr userq_idr;
+ /**
+ * @userq_mgr_xa: Per-process user queue map (queue ID → queue)
+ * Key: queue_id (unique ID within the process's userq manager)
+ * Value: struct amdgpu_usermode_queue
+ */
+ struct xarray userq_mgr_xa;
struct mutex userq_mutex;
struct amdgpu_device *adev;
struct delayed_work resume_work;
- struct list_head list;
struct drm_file *file;
+ atomic_t userq_count[AMDGPU_RING_TYPE_MAX];
};
struct amdgpu_db_info {
@@ -136,7 +149,13 @@ int amdgpu_userq_stop_sched_for_enforce_isolation(struct amdgpu_device *adev,
u32 idx);
int amdgpu_userq_start_sched_for_enforce_isolation(struct amdgpu_device *adev,
u32 idx);
-
-int amdgpu_userq_input_va_validate(struct amdgpu_vm *vm, u64 addr,
- u64 expected_size);
+void amdgpu_userq_reset_work(struct work_struct *work);
+void amdgpu_userq_pre_reset(struct amdgpu_device *adev);
+int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost);
+
+int amdgpu_userq_input_va_validate(struct amdgpu_usermode_queue *queue,
+ u64 addr, u64 expected_size);
+int amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev,
+ struct amdgpu_bo_va_mapping *mapping,
+ uint64_t saddr);
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
index 761bad98da3e..eba9fb359047 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
@@ -151,15 +151,16 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
{
struct amdgpu_userq_fence *userq_fence, *tmp;
struct dma_fence *fence;
+ unsigned long flags;
u64 rptr;
int i;
if (!fence_drv)
return;
+ spin_lock_irqsave(&fence_drv->fence_list_lock, flags);
rptr = amdgpu_userq_fence_read(fence_drv);
- spin_lock(&fence_drv->fence_list_lock);
list_for_each_entry_safe(userq_fence, tmp, &fence_drv->fences, link) {
fence = &userq_fence->base;
@@ -174,7 +175,7 @@ void amdgpu_userq_fence_driver_process(struct amdgpu_userq_fence_driver *fence_d
list_del(&userq_fence->link);
dma_fence_put(fence);
}
- spin_unlock(&fence_drv->fence_list_lock);
+ spin_unlock_irqrestore(&fence_drv->fence_list_lock, flags);
}
void amdgpu_userq_fence_driver_destroy(struct kref *ref)
@@ -386,6 +387,7 @@ static int amdgpu_userq_fence_read_wptr(struct amdgpu_usermode_queue *queue,
amdgpu_bo_unreserve(queue->vm->root.bo);
r = amdgpu_bo_reserve(bo, true);
if (r) {
+ amdgpu_bo_unref(&bo);
DRM_ERROR("Failed to reserve userqueue wptr bo");
return r;
}
@@ -537,7 +539,7 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data,
}
/* Retrieve the user queue */
- queue = idr_find(&userq_mgr->userq_idr, args->queue_id);
+ queue = xa_load(&userq_mgr->userq_mgr_xa, args->queue_id);
if (!queue) {
r = -ENOENT;
goto put_gobj_write;
@@ -899,7 +901,7 @@ int amdgpu_userq_wait_ioctl(struct drm_device *dev, void *data,
*/
num_fences = dma_fence_dedup_array(fences, num_fences);
- waitq = idr_find(&userq_mgr->userq_idr, wait_info->waitq_id);
+ waitq = xa_load(&userq_mgr->userq_mgr_xa, wait_info->waitq_id);
if (!waitq) {
r = -EINVAL;
goto free_fences;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
index ce318f5de047..a7d8f1ce6ac2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
@@ -41,6 +41,9 @@
#define VCE_IDLE_TIMEOUT msecs_to_jiffies(1000)
/* Firmware Names */
+#ifdef CONFIG_DRM_AMDGPU_SI
+#define FIRMWARE_VCE_V1_0 "amdgpu/vce_1_0_0.bin"
+#endif
#ifdef CONFIG_DRM_AMDGPU_CIK
#define FIRMWARE_BONAIRE "amdgpu/bonaire_vce.bin"
#define FIRMWARE_KABINI "amdgpu/kabini_vce.bin"
@@ -61,6 +64,9 @@
#define FIRMWARE_VEGA12 "amdgpu/vega12_vce.bin"
#define FIRMWARE_VEGA20 "amdgpu/vega20_vce.bin"
+#ifdef CONFIG_DRM_AMDGPU_SI
+MODULE_FIRMWARE(FIRMWARE_VCE_V1_0);
+#endif
#ifdef CONFIG_DRM_AMDGPU_CIK
MODULE_FIRMWARE(FIRMWARE_BONAIRE);
MODULE_FIRMWARE(FIRMWARE_KABINI);
@@ -88,82 +94,93 @@ static int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,
bool direct, struct dma_fence **fence);
/**
- * amdgpu_vce_sw_init - allocate memory, load vce firmware
+ * amdgpu_vce_firmware_name() - determine the firmware file name for VCE
*
* @adev: amdgpu_device pointer
- * @size: size for the new BO
*
- * First step to get VCE online, allocate memory and load the firmware
+ * Each chip that has VCE IP may need a different firmware.
+ * This function returns the name of the VCE firmware file
+ * appropriate for the current chip.
*/
-int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)
+static const char *amdgpu_vce_firmware_name(struct amdgpu_device *adev)
{
- const char *fw_name;
- const struct common_firmware_header *hdr;
- unsigned int ucode_version, version_major, version_minor, binary_id;
- int i, r;
-
switch (adev->asic_type) {
+#ifdef CONFIG_DRM_AMDGPU_SI
+ case CHIP_PITCAIRN:
+ case CHIP_TAHITI:
+ case CHIP_VERDE:
+ return FIRMWARE_VCE_V1_0;
+#endif
#ifdef CONFIG_DRM_AMDGPU_CIK
case CHIP_BONAIRE:
- fw_name = FIRMWARE_BONAIRE;
- break;
+ return FIRMWARE_BONAIRE;
case CHIP_KAVERI:
- fw_name = FIRMWARE_KAVERI;
- break;
+ return FIRMWARE_KAVERI;
case CHIP_KABINI:
- fw_name = FIRMWARE_KABINI;
- break;
+ return FIRMWARE_KABINI;
case CHIP_HAWAII:
- fw_name = FIRMWARE_HAWAII;
- break;
+ return FIRMWARE_HAWAII;
case CHIP_MULLINS:
- fw_name = FIRMWARE_MULLINS;
- break;
+ return FIRMWARE_MULLINS;
#endif
case CHIP_TONGA:
- fw_name = FIRMWARE_TONGA;
- break;
+ return FIRMWARE_TONGA;
case CHIP_CARRIZO:
- fw_name = FIRMWARE_CARRIZO;
- break;
+ return FIRMWARE_CARRIZO;
case CHIP_FIJI:
- fw_name = FIRMWARE_FIJI;
- break;
+ return FIRMWARE_FIJI;
case CHIP_STONEY:
- fw_name = FIRMWARE_STONEY;
- break;
+ return FIRMWARE_STONEY;
case CHIP_POLARIS10:
- fw_name = FIRMWARE_POLARIS10;
- break;
+ return FIRMWARE_POLARIS10;
case CHIP_POLARIS11:
- fw_name = FIRMWARE_POLARIS11;
- break;
+ return FIRMWARE_POLARIS11;
case CHIP_POLARIS12:
- fw_name = FIRMWARE_POLARIS12;
- break;
+ return FIRMWARE_POLARIS12;
case CHIP_VEGAM:
- fw_name = FIRMWARE_VEGAM;
- break;
+ return FIRMWARE_VEGAM;
case CHIP_VEGA10:
- fw_name = FIRMWARE_VEGA10;
- break;
+ return FIRMWARE_VEGA10;
case CHIP_VEGA12:
- fw_name = FIRMWARE_VEGA12;
- break;
+ return FIRMWARE_VEGA12;
case CHIP_VEGA20:
- fw_name = FIRMWARE_VEGA20;
- break;
+ return FIRMWARE_VEGA20;
default:
- return -EINVAL;
+ return NULL;
}
+}
+
+/**
+ * amdgpu_vce_early_init() - try to load VCE firmware
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Tries to load the VCE firmware.
+ *
+ * When not found, returns ENOENT so that the driver can
+ * still load and initialize the rest of the IP blocks.
+ * The GPU can function just fine without VCE, they will just
+ * not support video encoding.
+ */
+int amdgpu_vce_early_init(struct amdgpu_device *adev)
+{
+ const char *fw_name = amdgpu_vce_firmware_name(adev);
+ const struct common_firmware_header *hdr;
+ unsigned int ucode_version, version_major, version_minor, binary_id;
+ int r;
+
+ if (!fw_name)
+ return -ENOENT;
r = amdgpu_ucode_request(adev, &adev->vce.fw, AMDGPU_UCODE_REQUIRED, "%s", fw_name);
if (r) {
- dev_err(adev->dev, "amdgpu_vce: Can't validate firmware \"%s\"\n",
- fw_name);
+ dev_err(adev->dev,
+ "amdgpu_vce: Firmware \"%s\" not found or failed to validate (%d)\n",
+ fw_name, r);
+
amdgpu_ucode_release(&adev->vce.fw);
- return r;
+ return -ENOENT;
}
hdr = (const struct common_firmware_header *)adev->vce.fw->data;
@@ -172,11 +189,35 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)
version_major = (ucode_version >> 20) & 0xfff;
version_minor = (ucode_version >> 8) & 0xfff;
binary_id = ucode_version & 0xff;
- DRM_INFO("Found VCE firmware Version: %d.%d Binary ID: %d\n",
+ dev_info(adev->dev, "Found VCE firmware Version: %d.%d Binary ID: %d\n",
version_major, version_minor, binary_id);
adev->vce.fw_version = ((version_major << 24) | (version_minor << 16) |
(binary_id << 8));
+ return 0;
+}
+
+/**
+ * amdgpu_vce_sw_init() - allocate memory for VCE BO
+ *
+ * @adev: amdgpu_device pointer
+ * @size: size for the new BO
+ *
+ * First step to get VCE online: allocate memory for VCE BO.
+ * The VCE firmware binary is copied into the VCE BO later,
+ * in amdgpu_vce_resume. The VCE executes its code from the
+ * VCE BO and also uses the space in this BO for its stack and data.
+ *
+ * Ideally this BO should be placed in VRAM for optimal performance,
+ * although technically it also runs from system RAM (albeit slowly).
+ */
+int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)
+{
+ int i, r;
+
+ if (!adev->vce.fw)
+ return -ENOENT;
+
r = amdgpu_bo_create_kernel(adev, size, PAGE_SIZE,
AMDGPU_GEM_DOMAIN_VRAM |
AMDGPU_GEM_DOMAIN_GTT,
@@ -285,40 +326,23 @@ int amdgpu_vce_suspend(struct amdgpu_device *adev)
*/
int amdgpu_vce_resume(struct amdgpu_device *adev)
{
- void *cpu_addr;
const struct common_firmware_header *hdr;
unsigned int offset;
- int r, idx;
+ int idx;
if (adev->vce.vcpu_bo == NULL)
return -EINVAL;
- r = amdgpu_bo_reserve(adev->vce.vcpu_bo, false);
- if (r) {
- dev_err(adev->dev, "(%d) failed to reserve VCE bo\n", r);
- return r;
- }
-
- r = amdgpu_bo_kmap(adev->vce.vcpu_bo, &cpu_addr);
- if (r) {
- amdgpu_bo_unreserve(adev->vce.vcpu_bo);
- dev_err(adev->dev, "(%d) VCE map failed\n", r);
- return r;
- }
-
hdr = (const struct common_firmware_header *)adev->vce.fw->data;
offset = le32_to_cpu(hdr->ucode_array_offset_bytes);
if (drm_dev_enter(adev_to_drm(adev), &idx)) {
- memcpy_toio(cpu_addr, adev->vce.fw->data + offset,
+ memset_io(adev->vce.cpu_addr, 0, amdgpu_bo_size(adev->vce.vcpu_bo));
+ memcpy_toio(adev->vce.cpu_addr, adev->vce.fw->data + offset,
adev->vce.fw->size - offset);
drm_dev_exit(idx);
}
- amdgpu_bo_kunmap(adev->vce.vcpu_bo);
-
- amdgpu_bo_unreserve(adev->vce.vcpu_bo);
-
return 0;
}
@@ -427,6 +451,24 @@ void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp)
}
/**
+ * amdgpu_vce_required_gart_pages() - gets number of GART pages required by VCE
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Returns how many GART pages we need before GTT for the VCE IP block.
+ * For VCE1, see vce_v1_0_ensure_vcpu_bo_32bit_addr for details.
+ * For VCE2+, this is not needed so return zero.
+ */
+u32 amdgpu_vce_required_gart_pages(struct amdgpu_device *adev)
+{
+ /* VCE IP block not added yet, so can't use amdgpu_ip_version */
+ if (adev->family == AMDGPU_FAMILY_SI)
+ return 512;
+
+ return 0;
+}
+
+/**
* amdgpu_vce_get_create_msg - generate a VCE create msg
*
* @ring: ring we should submit the msg to
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h
index 6e53f872d084..1c3464ce5037 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h
@@ -51,14 +51,17 @@ struct amdgpu_vce {
struct drm_sched_entity entity;
uint32_t srbm_soft_reset;
unsigned num_rings;
+ uint32_t keyselect;
};
+int amdgpu_vce_early_init(struct amdgpu_device *adev);
int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size);
int amdgpu_vce_sw_fini(struct amdgpu_device *adev);
int amdgpu_vce_entity_init(struct amdgpu_device *adev, struct amdgpu_ring *ring);
int amdgpu_vce_suspend(struct amdgpu_device *adev);
int amdgpu_vce_resume(struct amdgpu_device *adev);
void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp);
+u32 amdgpu_vce_required_gart_pages(struct amdgpu_device *adev);
int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, struct amdgpu_job *job,
struct amdgpu_ib *ib);
int amdgpu_vce_ring_parse_cs_vm(struct amdgpu_cs_parser *p,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
index dc8a17bcc3c8..82624b44e661 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h
@@ -100,7 +100,8 @@
#define SOC15_DPG_MODE_OFFSET(ip, inst_idx, reg) \
({ \
- uint32_t internal_reg_offset, addr; \
+ /* To avoid a -Wunused-but-set-variable warning. */ \
+ uint32_t internal_reg_offset __maybe_unused, addr; \
bool video_range, video1_range, aon_range, aon1_range; \
\
addr = (adev->reg_offset[ip##_HWIP][inst_idx][reg##_BASE_IDX] + reg); \
@@ -161,7 +162,8 @@
#define SOC24_DPG_MODE_OFFSET(ip, inst_idx, reg) \
({ \
- uint32_t internal_reg_offset, addr; \
+ /* To avoid a -Wunused-but-set-variable warning. */ \
+ uint32_t internal_reg_offset __maybe_unused, addr; \
bool video_range, video1_range, aon_range, aon1_range; \
\
addr = (adev->reg_offset[ip##_HWIP][inst_idx][reg##_BASE_IDX] + reg); \
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c
index f96beb96c75c..47a6ce4fdc74 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c
@@ -44,6 +44,18 @@
vf2pf_info->ucode_info[ucode].version = ver; \
} while (0)
+#define mmRCC_CONFIG_MEMSIZE 0xde3
+
+const char *amdgpu_virt_dynamic_crit_table_name[] = {
+ "IP DISCOVERY",
+ "VBIOS IMG",
+ "RAS TELEMETRY",
+ "DATA EXCHANGE",
+ "BAD PAGE INFO",
+ "INIT HEADER",
+ "LAST",
+};
+
bool amdgpu_virt_mmio_blocked(struct amdgpu_device *adev)
{
/* By now all MMIO pages except mailbox are blocked */
@@ -150,9 +162,10 @@ void amdgpu_virt_request_init_data(struct amdgpu_device *adev)
virt->ops->req_init_data(adev);
if (adev->virt.req_init_data_ver > 0)
- DRM_INFO("host supports REQ_INIT_DATA handshake\n");
+ dev_info(adev->dev, "host supports REQ_INIT_DATA handshake of critical_region_version %d\n",
+ adev->virt.req_init_data_ver);
else
- DRM_WARN("host doesn't support REQ_INIT_DATA handshake\n");
+ dev_warn(adev->dev, "host doesn't support REQ_INIT_DATA handshake\n");
}
/**
@@ -205,12 +218,12 @@ int amdgpu_virt_alloc_mm_table(struct amdgpu_device *adev)
&adev->virt.mm_table.gpu_addr,
(void *)&adev->virt.mm_table.cpu_addr);
if (r) {
- DRM_ERROR("failed to alloc mm table and error = %d.\n", r);
+ dev_err(adev->dev, "failed to alloc mm table and error = %d.\n", r);
return r;
}
memset((void *)adev->virt.mm_table.cpu_addr, 0, PAGE_SIZE);
- DRM_INFO("MM table gpu addr = 0x%llx, cpu addr = %p.\n",
+ dev_info(adev->dev, "MM table gpu addr = 0x%llx, cpu addr = %p.\n",
adev->virt.mm_table.gpu_addr,
adev->virt.mm_table.cpu_addr);
return 0;
@@ -390,7 +403,9 @@ static void amdgpu_virt_ras_reserve_bps(struct amdgpu_device *adev)
if (amdgpu_bo_create_kernel_at(adev, bp << AMDGPU_GPU_PAGE_SHIFT,
AMDGPU_GPU_PAGE_SIZE,
&bo, NULL))
- DRM_DEBUG("RAS WARN: reserve vram for retired page %llx fail\n", bp);
+ dev_dbg(adev->dev,
+ "RAS WARN: reserve vram for retired page %llx fail\n",
+ bp);
data->bps_bo[i] = bo;
}
data->last_reserved = i + 1;
@@ -658,10 +673,34 @@ out:
schedule_delayed_work(&(adev->virt.vf2pf_work), adev->virt.vf2pf_update_interval_ms);
}
+static int amdgpu_virt_read_exchange_data_from_mem(struct amdgpu_device *adev, uint32_t *pfvf_data)
+{
+ uint32_t dataexchange_offset =
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].offset;
+ uint32_t dataexchange_size =
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].size_kb << 10;
+ uint64_t pos = 0;
+
+ dev_info(adev->dev,
+ "Got data exchange info from dynamic crit_region_table at offset 0x%x with size of 0x%x bytes.\n",
+ dataexchange_offset, dataexchange_size);
+
+ if (!IS_ALIGNED(dataexchange_offset, 4) || !IS_ALIGNED(dataexchange_size, 4)) {
+ dev_err(adev->dev, "Data exchange data not aligned to 4 bytes\n");
+ return -EINVAL;
+ }
+
+ pos = (uint64_t)dataexchange_offset;
+ amdgpu_device_vram_access(adev, pos, pfvf_data,
+ dataexchange_size, false);
+
+ return 0;
+}
+
void amdgpu_virt_fini_data_exchange(struct amdgpu_device *adev)
{
if (adev->virt.vf2pf_update_interval_ms != 0) {
- DRM_INFO("clean up the vf2pf work item\n");
+ dev_info(adev->dev, "clean up the vf2pf work item\n");
cancel_delayed_work_sync(&adev->virt.vf2pf_work);
adev->virt.vf2pf_update_interval_ms = 0;
}
@@ -669,13 +708,15 @@ void amdgpu_virt_fini_data_exchange(struct amdgpu_device *adev)
void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev)
{
+ uint32_t *pfvf_data = NULL;
+
adev->virt.fw_reserve.p_pf2vf = NULL;
adev->virt.fw_reserve.p_vf2pf = NULL;
adev->virt.vf2pf_update_interval_ms = 0;
adev->virt.vf2pf_update_retry_cnt = 0;
if (adev->mman.fw_vram_usage_va && adev->mman.drv_vram_usage_va) {
- DRM_WARN("Currently fw_vram and drv_vram should not have values at the same time!");
+ dev_warn(adev->dev, "Currently fw_vram and drv_vram should not have values at the same time!");
} else if (adev->mman.fw_vram_usage_va || adev->mman.drv_vram_usage_va) {
/* go through this logic in ip_init and reset to init workqueue*/
amdgpu_virt_exchange_data(adev);
@@ -684,11 +725,34 @@ void amdgpu_virt_init_data_exchange(struct amdgpu_device *adev)
schedule_delayed_work(&(adev->virt.vf2pf_work), msecs_to_jiffies(adev->virt.vf2pf_update_interval_ms));
} else if (adev->bios != NULL) {
/* got through this logic in early init stage to get necessary flags, e.g. rlcg_acc related*/
- adev->virt.fw_reserve.p_pf2vf =
- (struct amd_sriov_msg_pf2vf_info_header *)
- (adev->bios + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB << 10));
+ if (adev->virt.req_init_data_ver == GPU_CRIT_REGION_V2) {
+ pfvf_data =
+ kzalloc(adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].size_kb << 10,
+ GFP_KERNEL);
+ if (!pfvf_data) {
+ dev_err(adev->dev, "Failed to allocate memory for pfvf_data\n");
+ return;
+ }
- amdgpu_virt_read_pf2vf_data(adev);
+ if (amdgpu_virt_read_exchange_data_from_mem(adev, pfvf_data))
+ goto free_pfvf_data;
+
+ adev->virt.fw_reserve.p_pf2vf =
+ (struct amd_sriov_msg_pf2vf_info_header *)pfvf_data;
+
+ amdgpu_virt_read_pf2vf_data(adev);
+
+free_pfvf_data:
+ kfree(pfvf_data);
+ pfvf_data = NULL;
+ adev->virt.fw_reserve.p_pf2vf = NULL;
+ } else {
+ adev->virt.fw_reserve.p_pf2vf =
+ (struct amd_sriov_msg_pf2vf_info_header *)
+ (adev->bios + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 << 10));
+
+ amdgpu_virt_read_pf2vf_data(adev);
+ }
}
}
@@ -701,23 +765,38 @@ void amdgpu_virt_exchange_data(struct amdgpu_device *adev)
if (adev->mman.fw_vram_usage_va || adev->mman.drv_vram_usage_va) {
if (adev->mman.fw_vram_usage_va) {
- adev->virt.fw_reserve.p_pf2vf =
- (struct amd_sriov_msg_pf2vf_info_header *)
- (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB << 10));
- adev->virt.fw_reserve.p_vf2pf =
- (struct amd_sriov_msg_vf2pf_info_header *)
- (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB << 10));
- adev->virt.fw_reserve.ras_telemetry =
- (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB << 10));
+ if (adev->virt.req_init_data_ver == GPU_CRIT_REGION_V2) {
+ adev->virt.fw_reserve.p_pf2vf =
+ (struct amd_sriov_msg_pf2vf_info_header *)
+ (adev->mman.fw_vram_usage_va +
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].offset);
+ adev->virt.fw_reserve.p_vf2pf =
+ (struct amd_sriov_msg_vf2pf_info_header *)
+ (adev->mman.fw_vram_usage_va +
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].offset +
+ (AMD_SRIOV_MSG_SIZE_KB << 10));
+ adev->virt.fw_reserve.ras_telemetry =
+ (adev->mman.fw_vram_usage_va +
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_RAS_TELEMETRY_TABLE_ID].offset);
+ } else {
+ adev->virt.fw_reserve.p_pf2vf =
+ (struct amd_sriov_msg_pf2vf_info_header *)
+ (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 << 10));
+ adev->virt.fw_reserve.p_vf2pf =
+ (struct amd_sriov_msg_vf2pf_info_header *)
+ (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 << 10));
+ adev->virt.fw_reserve.ras_telemetry =
+ (adev->mman.fw_vram_usage_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB_V1 << 10));
+ }
} else if (adev->mman.drv_vram_usage_va) {
adev->virt.fw_reserve.p_pf2vf =
(struct amd_sriov_msg_pf2vf_info_header *)
- (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB << 10));
+ (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 << 10));
adev->virt.fw_reserve.p_vf2pf =
(struct amd_sriov_msg_vf2pf_info_header *)
- (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB << 10));
+ (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 << 10));
adev->virt.fw_reserve.ras_telemetry =
- (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB << 10));
+ (adev->mman.drv_vram_usage_va + (AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB_V1 << 10));
}
amdgpu_virt_read_pf2vf_data(adev);
@@ -816,7 +895,7 @@ static bool amdgpu_virt_init_req_data(struct amdgpu_device *adev, u32 reg)
break;
default: /* other chip doesn't support SRIOV */
is_sriov = false;
- DRM_ERROR("Unknown asic type: %d!\n", adev->asic_type);
+ dev_err(adev->dev, "Unknown asic type: %d!\n", adev->asic_type);
break;
}
}
@@ -838,10 +917,220 @@ static void amdgpu_virt_init_ras(struct amdgpu_device *adev)
RATELIMIT_MSG_ON_RELEASE);
mutex_init(&adev->virt.ras.ras_telemetry_mutex);
+ mutex_init(&adev->virt.access_req_mutex);
adev->virt.ras.cper_rptr = 0;
}
+static uint8_t amdgpu_virt_crit_region_calc_checksum(uint8_t *buf_start, uint8_t *buf_end)
+{
+ uint32_t sum = 0;
+
+ if (buf_start >= buf_end)
+ return 0;
+
+ for (; buf_start < buf_end; buf_start++)
+ sum += buf_start[0];
+
+ return 0xffffffff - sum;
+}
+
+int amdgpu_virt_init_critical_region(struct amdgpu_device *adev)
+{
+ struct amd_sriov_msg_init_data_header *init_data_hdr = NULL;
+ u64 init_hdr_offset = adev->virt.init_data_header.offset;
+ u64 init_hdr_size = (u64)adev->virt.init_data_header.size_kb << 10; /* KB → bytes */
+ u64 vram_size;
+ u64 end;
+ int r = 0;
+ uint8_t checksum = 0;
+
+ /* Skip below init if critical region version != v2 */
+ if (adev->virt.req_init_data_ver != GPU_CRIT_REGION_V2)
+ return 0;
+
+ if (init_hdr_offset < 0) {
+ dev_err(adev->dev, "Invalid init header offset\n");
+ return -EINVAL;
+ }
+
+ vram_size = RREG32(mmRCC_CONFIG_MEMSIZE);
+ if (!vram_size || vram_size == U32_MAX)
+ return -EINVAL;
+ vram_size <<= 20;
+
+ if (check_add_overflow(init_hdr_offset, init_hdr_size, &end) || end > vram_size) {
+ dev_err(adev->dev, "init_data_header exceeds VRAM size, exiting\n");
+ return -EINVAL;
+ }
+
+ /* Allocate for init_data_hdr */
+ init_data_hdr = kzalloc(sizeof(struct amd_sriov_msg_init_data_header), GFP_KERNEL);
+ if (!init_data_hdr)
+ return -ENOMEM;
+
+ amdgpu_device_vram_access(adev, (uint64_t)init_hdr_offset, (uint32_t *)init_data_hdr,
+ sizeof(struct amd_sriov_msg_init_data_header), false);
+
+ /* Table validation */
+ if (strncmp(init_data_hdr->signature,
+ AMDGPU_SRIOV_CRIT_DATA_SIGNATURE,
+ AMDGPU_SRIOV_CRIT_DATA_SIG_LEN) != 0) {
+ dev_err(adev->dev, "Invalid init data signature: %.4s\n",
+ init_data_hdr->signature);
+ r = -EINVAL;
+ goto out;
+ }
+
+ checksum = amdgpu_virt_crit_region_calc_checksum(
+ (uint8_t *)&init_data_hdr->initdata_offset,
+ (uint8_t *)init_data_hdr +
+ sizeof(struct amd_sriov_msg_init_data_header));
+ if (checksum != init_data_hdr->checksum) {
+ dev_err(adev->dev, "Found unmatching checksum from calculation 0x%x and init_data 0x%x\n",
+ checksum, init_data_hdr->checksum);
+ r = -EINVAL;
+ goto out;
+ }
+
+ memset(&adev->virt.crit_regn, 0, sizeof(adev->virt.crit_regn));
+ memset(adev->virt.crit_regn_tbl, 0, sizeof(adev->virt.crit_regn_tbl));
+
+ adev->virt.crit_regn.offset = init_data_hdr->initdata_offset;
+ adev->virt.crit_regn.size_kb = init_data_hdr->initdata_size_in_kb;
+
+ /* Validation and initialization for each table entry */
+ if (IS_SRIOV_CRIT_REGN_ENTRY_VALID(init_data_hdr, AMD_SRIOV_MSG_IPD_TABLE_ID)) {
+ if (!init_data_hdr->ip_discovery_size_in_kb ||
+ init_data_hdr->ip_discovery_size_in_kb > DISCOVERY_TMR_SIZE) {
+ dev_err(adev->dev, "Invalid %s size: 0x%x\n",
+ amdgpu_virt_dynamic_crit_table_name[AMD_SRIOV_MSG_IPD_TABLE_ID],
+ init_data_hdr->ip_discovery_size_in_kb);
+ r = -EINVAL;
+ goto out;
+ }
+
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_IPD_TABLE_ID].offset =
+ init_data_hdr->ip_discovery_offset;
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_IPD_TABLE_ID].size_kb =
+ init_data_hdr->ip_discovery_size_in_kb;
+ }
+
+ if (IS_SRIOV_CRIT_REGN_ENTRY_VALID(init_data_hdr, AMD_SRIOV_MSG_VBIOS_IMG_TABLE_ID)) {
+ if (!init_data_hdr->vbios_img_size_in_kb) {
+ dev_err(adev->dev, "Invalid %s size: 0x%x\n",
+ amdgpu_virt_dynamic_crit_table_name[AMD_SRIOV_MSG_VBIOS_IMG_TABLE_ID],
+ init_data_hdr->vbios_img_size_in_kb);
+ r = -EINVAL;
+ goto out;
+ }
+
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_VBIOS_IMG_TABLE_ID].offset =
+ init_data_hdr->vbios_img_offset;
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_VBIOS_IMG_TABLE_ID].size_kb =
+ init_data_hdr->vbios_img_size_in_kb;
+ }
+
+ if (IS_SRIOV_CRIT_REGN_ENTRY_VALID(init_data_hdr, AMD_SRIOV_MSG_RAS_TELEMETRY_TABLE_ID)) {
+ if (!init_data_hdr->ras_tele_info_size_in_kb) {
+ dev_err(adev->dev, "Invalid %s size: 0x%x\n",
+ amdgpu_virt_dynamic_crit_table_name[AMD_SRIOV_MSG_RAS_TELEMETRY_TABLE_ID],
+ init_data_hdr->ras_tele_info_size_in_kb);
+ r = -EINVAL;
+ goto out;
+ }
+
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_RAS_TELEMETRY_TABLE_ID].offset =
+ init_data_hdr->ras_tele_info_offset;
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_RAS_TELEMETRY_TABLE_ID].size_kb =
+ init_data_hdr->ras_tele_info_size_in_kb;
+ }
+
+ if (IS_SRIOV_CRIT_REGN_ENTRY_VALID(init_data_hdr, AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID)) {
+ if (!init_data_hdr->dataexchange_size_in_kb) {
+ dev_err(adev->dev, "Invalid %s size: 0x%x\n",
+ amdgpu_virt_dynamic_crit_table_name[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID],
+ init_data_hdr->dataexchange_size_in_kb);
+ r = -EINVAL;
+ goto out;
+ }
+
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].offset =
+ init_data_hdr->dataexchange_offset;
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID].size_kb =
+ init_data_hdr->dataexchange_size_in_kb;
+ }
+
+ if (IS_SRIOV_CRIT_REGN_ENTRY_VALID(init_data_hdr, AMD_SRIOV_MSG_BAD_PAGE_INFO_TABLE_ID)) {
+ if (!init_data_hdr->bad_page_size_in_kb) {
+ dev_err(adev->dev, "Invalid %s size: 0x%x\n",
+ amdgpu_virt_dynamic_crit_table_name[AMD_SRIOV_MSG_BAD_PAGE_INFO_TABLE_ID],
+ init_data_hdr->bad_page_size_in_kb);
+ r = -EINVAL;
+ goto out;
+ }
+
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_BAD_PAGE_INFO_TABLE_ID].offset =
+ init_data_hdr->bad_page_info_offset;
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_BAD_PAGE_INFO_TABLE_ID].size_kb =
+ init_data_hdr->bad_page_size_in_kb;
+ }
+
+ /* Validation for critical region info */
+ if (adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_IPD_TABLE_ID].size_kb > DISCOVERY_TMR_SIZE) {
+ dev_err(adev->dev, "Invalid IP discovery size: 0x%x\n",
+ adev->virt.crit_regn_tbl[AMD_SRIOV_MSG_IPD_TABLE_ID].size_kb);
+ r = -EINVAL;
+ goto out;
+ }
+
+ /* reserved memory starts from crit region base offset with the size of 5MB */
+ adev->mman.fw_vram_usage_start_offset = adev->virt.crit_regn.offset;
+ adev->mman.fw_vram_usage_size = adev->virt.crit_regn.size_kb << 10;
+ dev_info(adev->dev,
+ "critical region v%d requested to reserve memory start at %08llx with %llu KB.\n",
+ init_data_hdr->version,
+ adev->mman.fw_vram_usage_start_offset,
+ adev->mman.fw_vram_usage_size >> 10);
+
+ adev->virt.is_dynamic_crit_regn_enabled = true;
+
+out:
+ kfree(init_data_hdr);
+ init_data_hdr = NULL;
+
+ return r;
+}
+
+int amdgpu_virt_get_dynamic_data_info(struct amdgpu_device *adev,
+ int data_id, uint8_t *binary, u32 *size)
+{
+ uint32_t data_offset = 0;
+ uint32_t data_size = 0;
+ enum amd_sriov_msg_table_id_enum data_table_id = data_id;
+
+ if (data_table_id >= AMD_SRIOV_MSG_MAX_TABLE_ID)
+ return -EINVAL;
+
+ data_offset = adev->virt.crit_regn_tbl[data_table_id].offset;
+ data_size = adev->virt.crit_regn_tbl[data_table_id].size_kb << 10;
+
+ /* Validate on input params */
+ if (!binary || !size || *size < (uint64_t)data_size)
+ return -EINVAL;
+
+ /* Proceed to copy the dynamic content */
+ amdgpu_device_vram_access(adev,
+ (uint64_t)data_offset, (uint32_t *)binary, data_size, false);
+ *size = (uint64_t)data_size;
+
+ dev_dbg(adev->dev,
+ "Got %s info from dynamic crit_region_table at offset 0x%x with size of 0x%x bytes.\n",
+ amdgpu_virt_dynamic_crit_table_name[data_id], data_offset, data_size);
+
+ return 0;
+}
+
void amdgpu_virt_init(struct amdgpu_device *adev)
{
bool is_sriov = false;
@@ -1289,7 +1578,7 @@ amdgpu_ras_block_to_sriov(struct amdgpu_device *adev, enum amdgpu_ras_block bloc
case AMDGPU_RAS_BLOCK__MPIO:
return RAS_TELEMETRY_GPU_BLOCK_MPIO;
default:
- DRM_WARN_ONCE("Unsupported SRIOV RAS telemetry block 0x%x\n",
+ dev_warn(adev->dev, "Unsupported SRIOV RAS telemetry block 0x%x\n",
block);
return RAS_TELEMETRY_GPU_BLOCK_COUNT;
}
@@ -1304,7 +1593,7 @@ static int amdgpu_virt_cache_host_error_counts(struct amdgpu_device *adev,
checksum = host_telemetry->header.checksum;
used_size = host_telemetry->header.used_size;
- if (used_size > (AMD_SRIOV_RAS_TELEMETRY_SIZE_KB << 10))
+ if (used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
return 0;
tmp = kmemdup(&host_telemetry->body.error_count, used_size, GFP_KERNEL);
@@ -1383,7 +1672,7 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev,
checksum = host_telemetry->header.checksum;
used_size = host_telemetry->header.used_size;
- if (used_size > (AMD_SRIOV_RAS_TELEMETRY_SIZE_KB << 10))
+ if (used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
return -EINVAL;
cper_dump = kmemdup(&host_telemetry->body.cper_dump, used_size, GFP_KERNEL);
@@ -1515,7 +1804,7 @@ static int amdgpu_virt_cache_chk_criti_hit(struct amdgpu_device *adev,
checksum = host_telemetry->header.checksum;
used_size = host_telemetry->header.used_size;
- if (used_size > (AMD_SRIOV_RAS_TELEMETRY_SIZE_KB << 10))
+ if (used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
return 0;
tmp = kmemdup(&host_telemetry->body.chk_criti, used_size, GFP_KERNEL);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h
index d1172c8e58c4..01d5bca2dee1 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h
@@ -54,6 +54,12 @@
#define AMDGPU_VF2PF_UPDATE_MAX_RETRY_LIMIT 2
+/* Signature used to validate the SR-IOV dynamic critical region init data header ("INDA") */
+#define AMDGPU_SRIOV_CRIT_DATA_SIGNATURE "INDA"
+#define AMDGPU_SRIOV_CRIT_DATA_SIG_LEN 4
+
+#define IS_SRIOV_CRIT_REGN_ENTRY_VALID(hdr, id) ((hdr)->valid_tables & (1 << (id)))
+
enum amdgpu_sriov_vf_mode {
SRIOV_VF_MODE_BARE_METAL = 0,
SRIOV_VF_MODE_ONE_VF,
@@ -144,6 +150,7 @@ enum AMDGIM_FEATURE_FLAG {
AMDGIM_FEATURE_RAS_CAPS = (1 << 9),
AMDGIM_FEATURE_RAS_TELEMETRY = (1 << 10),
AMDGIM_FEATURE_RAS_CPER = (1 << 11),
+ AMDGIM_FEATURE_XGMI_TA_EXT_PEER_LINK = (1 << 12),
};
enum AMDGIM_REG_ACCESS_FLAG {
@@ -262,6 +269,11 @@ struct amdgpu_virt_ras {
DECLARE_ATTR_CAP_CLASS(amdgpu_virt, AMDGPU_VIRT_CAPS_LIST);
+struct amdgpu_virt_region {
+ uint32_t offset;
+ uint32_t size_kb;
+};
+
/* GPU virtualization */
struct amdgpu_virt {
uint32_t caps;
@@ -289,6 +301,12 @@ struct amdgpu_virt {
bool ras_init_done;
uint32_t reg_access;
+ /* dynamic(v2) critical regions */
+ struct amdgpu_virt_region init_data_header;
+ struct amdgpu_virt_region crit_regn;
+ struct amdgpu_virt_region crit_regn_tbl[AMD_SRIOV_MSG_MAX_TABLE_ID];
+ bool is_dynamic_crit_regn_enabled;
+
/* vf2pf message */
struct delayed_work vf2pf_work;
uint32_t vf2pf_update_interval_ms;
@@ -307,6 +325,8 @@ struct amdgpu_virt {
/* Spinlock to protect access to the RLCG register interface */
spinlock_t rlcg_reg_lock;
+ struct mutex access_req_mutex;
+
union amd_sriov_ras_caps ras_en_caps;
union amd_sriov_ras_caps ras_telemetry_en_caps;
struct amdgpu_virt_ras ras;
@@ -378,6 +398,9 @@ struct amdgpu_video_codec_info;
#define amdgpu_sriov_ras_cper_en(adev) \
((adev)->virt.gim_feature & AMDGIM_FEATURE_RAS_CPER)
+#define amdgpu_sriov_xgmi_ta_ext_peer_link_en(adev) \
+((adev)->virt.gim_feature & AMDGIM_FEATURE_XGMI_TA_EXT_PEER_LINK)
+
static inline bool is_virtual_machine(void)
{
#if defined(CONFIG_X86)
@@ -424,6 +447,10 @@ void amdgpu_virt_exchange_data(struct amdgpu_device *adev);
void amdgpu_virt_fini_data_exchange(struct amdgpu_device *adev);
void amdgpu_virt_init(struct amdgpu_device *adev);
+int amdgpu_virt_init_critical_region(struct amdgpu_device *adev);
+int amdgpu_virt_get_dynamic_data_info(struct amdgpu_device *adev,
+ int data_id, uint8_t *binary, u32 *size);
+
bool amdgpu_virt_can_access_debugfs(struct amdgpu_device *adev);
int amdgpu_virt_enable_access_debugfs(struct amdgpu_device *adev);
void amdgpu_virt_disable_access_debugfs(struct amdgpu_device *adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index c1a801203949..a67285118c37 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -484,15 +484,19 @@ int amdgpu_vm_lock_done_list(struct amdgpu_vm *vm, struct drm_exec *exec,
spin_lock(&vm->status_lock);
while (!list_is_head(prev->next, &vm->done)) {
bo_va = list_entry(prev->next, typeof(*bo_va), base.vm_status);
- spin_unlock(&vm->status_lock);
bo = bo_va->base.bo;
if (bo) {
+ amdgpu_bo_ref(bo);
+ spin_unlock(&vm->status_lock);
+
ret = drm_exec_prepare_obj(exec, &bo->tbo.base, 1);
+ amdgpu_bo_unref(&bo);
if (unlikely(ret))
return ret;
+
+ spin_lock(&vm->status_lock);
}
- spin_lock(&vm->status_lock);
prev = prev->next;
}
spin_unlock(&vm->status_lock);
@@ -779,7 +783,6 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job,
bool cleaner_shader_needed = false;
bool pasid_mapping_needed = false;
struct dma_fence *fence = NULL;
- struct amdgpu_fence *af;
unsigned int patch;
int r;
@@ -842,12 +845,12 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job,
}
if (vm_flush_needed || pasid_mapping_needed || cleaner_shader_needed) {
- r = amdgpu_fence_emit(ring, &fence, NULL, 0);
+ r = amdgpu_fence_emit(ring, job->hw_vm_fence, 0);
if (r)
return r;
- /* this is part of the job's context */
- af = container_of(fence, struct amdgpu_fence, base);
- af->context = job->base.s_fence ? job->base.s_fence->finished.context : 0;
+ fence = &job->hw_vm_fence->base;
+ /* get a ref for the job */
+ dma_fence_get(fence);
}
if (vm_flush_needed) {
@@ -1066,7 +1069,7 @@ amdgpu_vm_tlb_flush(struct amdgpu_vm_update_params *params,
}
/* Prepare a TLB flush fence to be attached to PTs */
- if (!params->unlocked && vm->is_compute_context) {
+ if (!params->unlocked) {
amdgpu_vm_tlb_fence_create(params->adev, vm, fence);
/* Makes sure no PD/PT is freed before the flush */
@@ -1952,6 +1955,7 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev,
struct amdgpu_bo_va_mapping *mapping;
struct amdgpu_vm *vm = bo_va->base.vm;
bool valid = true;
+ int r;
saddr /= AMDGPU_GPU_PAGE_SIZE;
@@ -1972,6 +1976,17 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev,
return -ENOENT;
}
+ /* It's unlikely to happen that the mapping userq hasn't been idled
+ * during user requests GEM unmap IOCTL except for forcing the unmap
+ * from user space.
+ */
+ if (unlikely(atomic_read(&bo_va->userq_va_mapped) > 0)) {
+ r = amdgpu_userq_gem_va_unmap_validate(adev, mapping, saddr);
+ if (unlikely(r == -EBUSY))
+ dev_warn_once(adev->dev,
+ "Attempt to unmap an active userq buffer\n");
+ }
+
list_del(&mapping->list);
amdgpu_vm_it_remove(mapping, &vm->va);
mapping->bo_va = NULL;
@@ -2078,7 +2093,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev,
struct amdgpu_bo *bo = before->bo_va->base.bo;
amdgpu_vm_it_insert(before, &vm->va);
- if (before->flags & AMDGPU_PTE_PRT_FLAG(adev))
+ if (before->flags & AMDGPU_VM_PAGE_PRT)
amdgpu_vm_prt_get(adev);
if (amdgpu_vm_is_bo_always_valid(vm, bo) &&
@@ -2093,7 +2108,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev,
struct amdgpu_bo *bo = after->bo_va->base.bo;
amdgpu_vm_it_insert(after, &vm->va);
- if (after->flags & AMDGPU_PTE_PRT_FLAG(adev))
+ if (after->flags & AMDGPU_VM_PAGE_PRT)
amdgpu_vm_prt_get(adev);
if (amdgpu_vm_is_bo_always_valid(vm, bo) &&
@@ -2828,8 +2843,6 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
*/
void amdgpu_vm_manager_init(struct amdgpu_device *adev)
{
- unsigned i;
-
/* Concurrent flushes are only possible starting with Vega10 and
* are broken on Navi10 and Navi14.
*/
@@ -2838,11 +2851,6 @@ void amdgpu_vm_manager_init(struct amdgpu_device *adev)
adev->asic_type == CHIP_NAVI14);
amdgpu_vmid_mgr_init(adev);
- adev->vm_manager.fence_context =
- dma_fence_context_alloc(AMDGPU_MAX_RINGS);
- for (i = 0; i < AMDGPU_MAX_RINGS; ++i)
- adev->vm_manager.seqno[i] = 0;
-
spin_lock_init(&adev->vm_manager.prt_lock);
atomic_set(&adev->vm_manager.num_prt_users, 0);
@@ -2908,8 +2916,7 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
switch (args->in.op) {
case AMDGPU_VM_OP_RESERVE_VMID:
/* We only have requirement to reserve vmid from gfxhub */
- amdgpu_vmid_alloc_reserved(adev, vm, AMDGPU_GFXHUB(0));
- break;
+ return amdgpu_vmid_alloc_reserved(adev, vm, AMDGPU_GFXHUB(0));
case AMDGPU_VM_OP_UNRESERVE_VMID:
amdgpu_vmid_free_reserved(adev, vm, AMDGPU_GFXHUB(0));
break;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
index cf0ec94e8a07..15d757c016cb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
@@ -453,10 +453,6 @@ struct amdgpu_vm_manager {
unsigned int first_kfd_vmid;
bool concurrent_flush;
- /* Handling of VM fences */
- u64 fence_context;
- unsigned seqno[AMDGPU_MAX_RINGS];
-
uint64_t max_pfn;
uint32_t num_level;
uint32_t block_size;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c
index 474bfe36c0c2..aa78c2ee9e21 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vpe.c
@@ -322,6 +322,26 @@ static int vpe_early_init(struct amdgpu_ip_block *ip_block)
return 0;
}
+static bool vpe_need_dpm0_at_power_down(struct amdgpu_device *adev)
+{
+ switch (amdgpu_ip_version(adev, VPE_HWIP, 0)) {
+ case IP_VERSION(6, 1, 1):
+ return adev->pm.fw_version < 0x0a640500;
+ default:
+ return false;
+ }
+}
+
+static int vpe_get_dpm_level(struct amdgpu_device *adev)
+{
+ struct amdgpu_vpe *vpe = &adev->vpe;
+
+ if (!adev->pm.dpm_enabled)
+ return 0;
+
+ return RREG32(vpe_get_reg_offset(vpe, 0, vpe->regs.dpm_request_lv));
+}
+
static void vpe_idle_work_handler(struct work_struct *work)
{
struct amdgpu_device *adev =
@@ -329,11 +349,17 @@ static void vpe_idle_work_handler(struct work_struct *work)
unsigned int fences = 0;
fences += amdgpu_fence_count_emitted(&adev->vpe.ring);
+ if (fences)
+ goto reschedule;
- if (fences == 0)
- amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VPE, AMD_PG_STATE_GATE);
- else
- schedule_delayed_work(&adev->vpe.idle_work, VPE_IDLE_TIMEOUT);
+ if (vpe_need_dpm0_at_power_down(adev) && vpe_get_dpm_level(adev) != 0)
+ goto reschedule;
+
+ amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VPE, AMD_PG_STATE_GATE);
+ return;
+
+reschedule:
+ schedule_delayed_work(&adev->vpe.idle_work, VPE_IDLE_TIMEOUT);
}
static int vpe_common_init(struct amdgpu_vpe *vpe)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c
index 1ede308a7c67..aad530c46a9f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c
@@ -298,6 +298,9 @@ int amdgpu_xgmi_get_ext_link(struct amdgpu_device *adev, int link_num)
{
int link_map_6_4_x[8] = { 0, 3, 1, 2, 7, 6, 4, 5 };
+ if (adev->gmc.xgmi.num_physical_nodes <= 1)
+ return -EINVAL;
+
switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) {
case IP_VERSION(6, 4, 0):
case IP_VERSION(6, 4, 1):
@@ -333,6 +336,10 @@ static u32 xgmi_v6_4_get_link_status(struct amdgpu_device *adev, int global_link
}
i = global_link_num / n;
+
+ if (!(adev->aid_mask & BIT(i)))
+ return U32_MAX;
+
addr += adev->asic_funcs->encode_ext_smn_addressing(i);
return RREG32_PCIE_EXT(addr);
@@ -342,6 +349,9 @@ int amdgpu_get_xgmi_link_status(struct amdgpu_device *adev, int global_link_num)
{
u32 xgmi_state_reg_val;
+ if (adev->gmc.xgmi.num_physical_nodes <= 1)
+ return -EINVAL;
+
switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) {
case IP_VERSION(6, 4, 0):
case IP_VERSION(6, 4, 1):
@@ -958,28 +968,6 @@ static int amdgpu_xgmi_initialize_hive_get_data_partition(struct amdgpu_hive_inf
return 0;
}
-static void amdgpu_xgmi_fill_topology_info(struct amdgpu_device *adev,
- struct amdgpu_device *peer_adev)
-{
- struct psp_xgmi_topology_info *top_info = &adev->psp.xgmi_context.top_info;
- struct psp_xgmi_topology_info *peer_info = &peer_adev->psp.xgmi_context.top_info;
-
- for (int i = 0; i < peer_info->num_nodes; i++) {
- if (peer_info->nodes[i].node_id == adev->gmc.xgmi.node_id) {
- for (int j = 0; j < top_info->num_nodes; j++) {
- if (top_info->nodes[j].node_id == peer_adev->gmc.xgmi.node_id) {
- peer_info->nodes[i].num_hops = top_info->nodes[j].num_hops;
- peer_info->nodes[i].is_sharing_enabled =
- top_info->nodes[j].is_sharing_enabled;
- peer_info->nodes[i].num_links =
- top_info->nodes[j].num_links;
- return;
- }
- }
- }
- }
-}
-
int amdgpu_xgmi_add_device(struct amdgpu_device *adev)
{
struct psp_xgmi_topology_info *top_info;
@@ -1065,11 +1053,6 @@ int amdgpu_xgmi_add_device(struct amdgpu_device *adev)
/* To do: continue with some node failed or disable the whole hive*/
goto exit_unlock;
}
-
- /* fill the topology info for peers instead of getting from PSP */
- list_for_each_entry(tmp_adev, &hive->device_list, gmc.xgmi.head) {
- amdgpu_xgmi_fill_topology_info(adev, tmp_adev);
- }
} else {
/* get latest topology info for each device from psp */
list_for_each_entry(tmp_adev, &hive->device_list, gmc.xgmi.head) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h
index 3a79ed7d8031..3cdb1e0eca37 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h
@@ -23,26 +23,84 @@
#ifndef AMDGV_SRIOV_MSG__H_
#define AMDGV_SRIOV_MSG__H_
-/* unit in kilobytes */
-#define AMD_SRIOV_MSG_VBIOS_OFFSET 0
-#define AMD_SRIOV_MSG_VBIOS_SIZE_KB 64
-#define AMD_SRIOV_MSG_DATAEXCHANGE_OFFSET_KB AMD_SRIOV_MSG_VBIOS_SIZE_KB
-#define AMD_SRIOV_MSG_DATAEXCHANGE_SIZE_KB 4
-#define AMD_SRIOV_MSG_TMR_OFFSET_KB 2048
-#define AMD_SRIOV_MSG_BAD_PAGE_SIZE_KB 2
-#define AMD_SRIOV_RAS_TELEMETRY_SIZE_KB 64
+#define AMD_SRIOV_MSG_SIZE_KB 1
+
/*
- * layout
+ * layout v1
* 0 64KB 65KB 66KB 68KB 132KB
* | VBIOS | PF2VF | VF2PF | Bad Page | RAS Telemetry Region | ...
* | 64KB | 1KB | 1KB | 2KB | 64KB | ...
*/
-#define AMD_SRIOV_MSG_SIZE_KB 1
-#define AMD_SRIOV_MSG_PF2VF_OFFSET_KB AMD_SRIOV_MSG_DATAEXCHANGE_OFFSET_KB
-#define AMD_SRIOV_MSG_VF2PF_OFFSET_KB (AMD_SRIOV_MSG_PF2VF_OFFSET_KB + AMD_SRIOV_MSG_SIZE_KB)
-#define AMD_SRIOV_MSG_BAD_PAGE_OFFSET_KB (AMD_SRIOV_MSG_VF2PF_OFFSET_KB + AMD_SRIOV_MSG_SIZE_KB)
-#define AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB (AMD_SRIOV_MSG_BAD_PAGE_OFFSET_KB + AMD_SRIOV_MSG_BAD_PAGE_SIZE_KB)
+/*
+ * layout v2 (offsets are dynamically allocated and the offsets below are examples)
+ * 0 1KB 64KB 65KB 66KB 68KB 132KB
+ * | INITD_H | VBIOS | PF2VF | VF2PF | Bad Page | RAS Telemetry Region | ...
+ * | 1KB | 64KB | 1KB | 1KB | 2KB | 64KB | ...
+ *
+ * Note: PF2VF + VF2PF + Bad Page = DataExchange region (allocated contiguously)
+ */
+
+/* v1 layout sizes */
+#define AMD_SRIOV_MSG_VBIOS_SIZE_KB_V1 64
+#define AMD_SRIOV_MSG_PF2VF_SIZE_KB_V1 1
+#define AMD_SRIOV_MSG_VF2PF_SIZE_KB_V1 1
+#define AMD_SRIOV_MSG_BAD_PAGE_SIZE_KB_V1 2
+#define AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 64
+#define AMD_SRIOV_MSG_DATAEXCHANGE_SIZE_KB_V1 \
+ (AMD_SRIOV_MSG_PF2VF_SIZE_KB_V1 + AMD_SRIOV_MSG_VF2PF_SIZE_KB_V1 + \
+ AMD_SRIOV_MSG_BAD_PAGE_SIZE_KB_V1)
+
+/* v1 offsets */
+#define AMD_SRIOV_MSG_VBIOS_OFFSET_V1 0
+#define AMD_SRIOV_MSG_DATAEXCHANGE_OFFSET_KB_V1 AMD_SRIOV_MSG_VBIOS_SIZE_KB_V1
+#define AMD_SRIOV_MSG_TMR_OFFSET_KB 2048
+#define AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 AMD_SRIOV_MSG_DATAEXCHANGE_OFFSET_KB_V1
+#define AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 \
+ (AMD_SRIOV_MSG_PF2VF_OFFSET_KB_V1 + AMD_SRIOV_MSG_SIZE_KB)
+#define AMD_SRIOV_MSG_BAD_PAGE_OFFSET_KB_V1 \
+ (AMD_SRIOV_MSG_VF2PF_OFFSET_KB_V1 + AMD_SRIOV_MSG_SIZE_KB)
+#define AMD_SRIOV_MSG_RAS_TELEMETRY_OFFSET_KB_V1 \
+ (AMD_SRIOV_MSG_BAD_PAGE_OFFSET_KB_V1 + AMD_SRIOV_MSG_BAD_PAGE_SIZE_KB_V1)
+#define AMD_SRIOV_MSG_INIT_DATA_TOT_SIZE_KB_V1 \
+ (AMD_SRIOV_MSG_VBIOS_SIZE_KB_V1 + AMD_SRIOV_MSG_DATAEXCHANGE_SIZE_KB_V1 + \
+ AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1)
+
+enum amd_sriov_crit_region_version {
+ GPU_CRIT_REGION_V1 = 1,
+ GPU_CRIT_REGION_V2 = 2,
+};
+
+/* v2 layout offset enum (in order of allocation) */
+enum amd_sriov_msg_table_id_enum {
+ AMD_SRIOV_MSG_IPD_TABLE_ID = 0,
+ AMD_SRIOV_MSG_VBIOS_IMG_TABLE_ID,
+ AMD_SRIOV_MSG_RAS_TELEMETRY_TABLE_ID,
+ AMD_SRIOV_MSG_DATAEXCHANGE_TABLE_ID,
+ AMD_SRIOV_MSG_BAD_PAGE_INFO_TABLE_ID,
+ AMD_SRIOV_MSG_INITD_H_TABLE_ID,
+ AMD_SRIOV_MSG_MAX_TABLE_ID,
+};
+
+struct amd_sriov_msg_init_data_header {
+ char signature[4]; /* "INDA" */
+ uint32_t version;
+ uint32_t checksum;
+ uint32_t initdata_offset; /* 0 */
+ uint32_t initdata_size_in_kb; /* 5MB */
+ uint32_t valid_tables;
+ uint32_t vbios_img_offset;
+ uint32_t vbios_img_size_in_kb;
+ uint32_t dataexchange_offset;
+ uint32_t dataexchange_size_in_kb;
+ uint32_t ras_tele_info_offset;
+ uint32_t ras_tele_info_size_in_kb;
+ uint32_t ip_discovery_offset;
+ uint32_t ip_discovery_size_in_kb;
+ uint32_t bad_page_info_offset;
+ uint32_t bad_page_size_in_kb;
+ uint32_t reserved[8];
+};
/*
* PF2VF history log:
@@ -102,7 +160,8 @@ union amd_sriov_msg_feature_flags {
uint32_t ras_caps : 1;
uint32_t ras_telemetry : 1;
uint32_t ras_cper : 1;
- uint32_t reserved : 20;
+ uint32_t xgmi_ta_ext_peer_link : 1;
+ uint32_t reserved : 19;
} flags;
uint32_t all;
};
@@ -140,8 +199,9 @@ union amd_sriov_ras_caps {
uint64_t block_jpeg : 1;
uint64_t block_ih : 1;
uint64_t block_mpio : 1;
+ uint64_t block_mmsch : 1;
uint64_t poison_propogation_mode : 1;
- uint64_t reserved : 44;
+ uint64_t reserved : 43;
} bits;
uint64_t all;
};
diff --git a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c
index 811124ff88a8..f9e2edf5260b 100644
--- a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c
+++ b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c
@@ -407,7 +407,8 @@ static int aqua_vanjaram_switch_partition_mode(struct amdgpu_xcp_mgr *xcp_mgr,
return -EINVAL;
}
- if (adev->kfd.init_complete && !amdgpu_in_reset(adev))
+ if (adev->kfd.init_complete && !amdgpu_in_reset(adev) &&
+ !adev->in_suspend)
flags |= AMDGPU_XCP_OPS_KFD;
if (flags & AMDGPU_XCP_OPS_KFD) {
diff --git a/drivers/gpu/drm/amd/amdgpu/cik_ih.c b/drivers/gpu/drm/amd/amdgpu/cik_ih.c
index 41f4705bdbbd..876a3256dba4 100644
--- a/drivers/gpu/drm/amd/amdgpu/cik_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/cik_ih.c
@@ -156,6 +156,9 @@ static int cik_ih_irq_init(struct amdgpu_device *adev)
/* enable irqs */
cik_ih_enable_interrupts(adev);
+ if (adev->irq.ih_soft.ring_size)
+ adev->irq.ih_soft.enabled = true;
+
return 0;
}
@@ -192,6 +195,9 @@ static u32 cik_ih_get_wptr(struct amdgpu_device *adev,
wptr = le32_to_cpu(*ih->wptr_cpu);
+ if (ih == &adev->irq.ih_soft)
+ goto out;
+
if (wptr & IH_RB_WPTR__RB_OVERFLOW_MASK) {
wptr &= ~IH_RB_WPTR__RB_OVERFLOW_MASK;
/* When a ring buffer overflow happen start parsing interrupt
@@ -211,6 +217,8 @@ static u32 cik_ih_get_wptr(struct amdgpu_device *adev,
tmp &= ~IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK;
WREG32(mmIH_RB_CNTL, tmp);
}
+
+out:
return (wptr & ih->ptr_mask);
}
@@ -306,6 +314,10 @@ static int cik_ih_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ r = amdgpu_ih_ring_init(adev, &adev->irq.ih_soft, IH_SW_RING_SIZE, true);
+ if (r)
+ return r;
+
r = amdgpu_irq_init(adev);
return r;
diff --git a/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c b/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c
index 96616a865aac..ed1e25661706 100644
--- a/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c
+++ b/drivers/gpu/drm/amd/amdgpu/cyan_skillfish_reg_init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: MIT
/*
* Copyright 2018 Advanced Micro Devices, Inc.
*
diff --git a/drivers/gpu/drm/amd/amdgpu/cz_ih.c b/drivers/gpu/drm/amd/amdgpu/cz_ih.c
index 2f891fb846d5..bc7a2e06ab5f 100644
--- a/drivers/gpu/drm/amd/amdgpu/cz_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/cz_ih.c
@@ -157,6 +157,9 @@ static int cz_ih_irq_init(struct amdgpu_device *adev)
/* enable interrupts */
cz_ih_enable_interrupts(adev);
+ if (adev->irq.ih_soft.ring_size)
+ adev->irq.ih_soft.enabled = true;
+
return 0;
}
@@ -194,6 +197,9 @@ static u32 cz_ih_get_wptr(struct amdgpu_device *adev,
wptr = le32_to_cpu(*ih->wptr_cpu);
+ if (ih == &adev->irq.ih_soft)
+ goto out;
+
if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
goto out;
@@ -297,6 +303,10 @@ static int cz_ih_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ r = amdgpu_ih_ring_init(adev, &adev->irq.ih_soft, IH_SW_RING_SIZE, true);
+ if (r)
+ return r;
+
r = amdgpu_irq_init(adev);
return r;
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
index 8841d7213de4..d75b9940f248 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c
@@ -4956,7 +4956,8 @@ static int gfx_v10_0_sw_init(struct amdgpu_ip_block *ip_block)
amdgpu_get_soft_full_reset_mask(&adev->gfx.gfx_ring[0]);
adev->gfx.compute_supported_reset =
amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]);
- if (!amdgpu_sriov_vf(adev)) {
+ if (!amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset) {
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
}
@@ -9951,6 +9952,7 @@ static const struct amdgpu_ring_funcs gfx_v10_0_ring_funcs_kiq = {
.emit_wreg = gfx_v10_0_ring_emit_wreg,
.emit_reg_wait = gfx_v10_0_ring_emit_reg_wait,
.emit_reg_write_reg_wait = gfx_v10_0_ring_emit_reg_write_reg_wait,
+ .emit_hdp_flush = gfx_v10_0_ring_emit_hdp_flush,
};
static void gfx_v10_0_set_ring_funcs(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index d61eb9f187c6..8a2ee2de390f 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@ -1821,13 +1821,15 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(11, 0, 3):
if ((adev->gfx.me_fw_version >= 2280) &&
(adev->gfx.mec_fw_version >= 2410) &&
- !amdgpu_sriov_vf(adev)) {
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset) {
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
}
break;
default:
- if (!amdgpu_sriov_vf(adev)) {
+ if (!amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset) {
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
}
@@ -2438,7 +2440,7 @@ static int gfx_v11_0_rlc_load_microcode(struct amdgpu_device *adev)
if (version_minor == 3)
gfx_v11_0_load_rlcp_rlcv_microcode(adev);
}
-
+
return 0;
}
@@ -3886,7 +3888,7 @@ static int gfx_v11_0_cp_compute_load_microcode(struct amdgpu_device *adev)
}
memcpy(fw, fw_data, fw_size);
-
+
amdgpu_bo_kunmap(adev->gfx.mec.mec_fw_obj);
amdgpu_bo_unreserve(adev->gfx.mec.mec_fw_obj);
@@ -5872,9 +5874,9 @@ static void gfx_v11_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
if (flags & AMDGPU_IB_PREEMPTED)
control |= INDIRECT_BUFFER_PRE_RESUME(1);
- if (vmid)
+ if (vmid && !ring->adev->gfx.rs64_enable)
gfx_v11_0_ring_emit_de_meta(ring,
- (!amdgpu_sriov_vf(ring->adev) && flags & AMDGPU_IB_PREEMPTED) ? true : false);
+ !amdgpu_sriov_vf(ring->adev) && (flags & AMDGPU_IB_PREEMPTED));
}
amdgpu_ring_write(ring, header);
@@ -7318,6 +7320,7 @@ static const struct amdgpu_ring_funcs gfx_v11_0_ring_funcs_kiq = {
.emit_wreg = gfx_v11_0_ring_emit_wreg,
.emit_reg_wait = gfx_v11_0_ring_emit_reg_wait,
.emit_reg_write_reg_wait = gfx_v11_0_ring_emit_reg_write_reg_wait,
+ .emit_hdp_flush = gfx_v11_0_ring_emit_hdp_flush,
};
static void gfx_v11_0_set_ring_funcs(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index 93fde0f9af87..d01d2712cf57 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@ -1548,7 +1548,8 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(12, 0, 1):
if ((adev->gfx.me_fw_version >= 2660) &&
(adev->gfx.mec_fw_version >= 2920) &&
- !amdgpu_sriov_vf(adev)) {
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset) {
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
}
@@ -5595,6 +5596,7 @@ static const struct amdgpu_ring_funcs gfx_v12_0_ring_funcs_kiq = {
.emit_wreg = gfx_v12_0_ring_emit_wreg,
.emit_reg_wait = gfx_v12_0_ring_emit_reg_wait,
.emit_reg_write_reg_wait = gfx_v12_0_ring_emit_reg_write_reg_wait,
+ .emit_hdp_flush = gfx_v12_0_ring_emit_hdp_flush,
};
static void gfx_v12_0_set_ring_funcs(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
index 7693b7953426..80565392313f 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
@@ -3102,6 +3102,11 @@ static int gfx_v6_0_sw_init(struct amdgpu_ip_block *ip_block)
return r;
}
+ adev->gfx.gfx_supported_reset =
+ amdgpu_get_soft_full_reset_mask(&adev->gfx.gfx_ring[0]);
+ adev->gfx.compute_supported_reset =
+ amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]);
+
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
index 5976ed55d9db..2b7aba22ecc1 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
@@ -4399,6 +4399,11 @@ static int gfx_v7_0_sw_init(struct amdgpu_ip_block *ip_block)
gfx_v7_0_gpu_early_init(adev);
+ adev->gfx.gfx_supported_reset =
+ amdgpu_get_soft_full_reset_mask(&adev->gfx.gfx_ring[0]);
+ adev->gfx.compute_supported_reset =
+ amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]);
+
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
index 0856ff65288c..1c87375e1dd5 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
@@ -2023,6 +2023,11 @@ static int gfx_v8_0_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ adev->gfx.gfx_supported_reset =
+ amdgpu_get_soft_full_reset_mask(&adev->gfx.gfx_ring[0]);
+ adev->gfx.compute_supported_reset =
+ amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]);
+
return 0;
}
@@ -6939,6 +6944,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_kiq = {
.pad_ib = amdgpu_ring_generic_pad_ib,
.emit_rreg = gfx_v8_0_ring_emit_rreg,
.emit_wreg = gfx_v8_0_ring_emit_wreg,
+ .emit_hdp_flush = gfx_v8_0_ring_emit_hdp_flush,
};
static void gfx_v8_0_set_ring_funcs(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
index dd19a97436db..0148d7ff34d9 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
@@ -2409,7 +2409,7 @@ static int gfx_v9_0_sw_init(struct amdgpu_ip_block *ip_block)
amdgpu_get_soft_full_reset_mask(&adev->gfx.gfx_ring[0]);
adev->gfx.compute_supported_reset =
amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]);
- if (!amdgpu_sriov_vf(adev))
+ if (!amdgpu_sriov_vf(adev) && !adev->debug_disable_gpu_ring_reset)
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
r = amdgpu_gfx_kiq_init(adev, GFX9_MEC_HPD_SIZE, 0);
@@ -7586,6 +7586,7 @@ static const struct amdgpu_ring_funcs gfx_v9_0_ring_funcs_kiq = {
.emit_wreg = gfx_v9_0_ring_emit_wreg,
.emit_reg_wait = gfx_v9_0_ring_emit_reg_wait,
.emit_reg_write_reg_wait = gfx_v9_0_ring_emit_reg_write_reg_wait,
+ .emit_hdp_flush = gfx_v9_0_ring_emit_hdp_flush,
};
static void gfx_v9_0_set_ring_funcs(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
index 77f9d5b9a556..cbb74ffc4792 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c
@@ -1149,14 +1149,16 @@ static int gfx_v9_4_3_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(9, 4, 3):
case IP_VERSION(9, 4, 4):
if ((adev->gfx.mec_fw_version >= 155) &&
- !amdgpu_sriov_vf(adev)) {
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset) {
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_PIPE;
}
break;
case IP_VERSION(9, 5, 0):
if ((adev->gfx.mec_fw_version >= 21) &&
- !amdgpu_sriov_vf(adev)) {
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset) {
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_PER_PIPE;
}
@@ -2152,7 +2154,8 @@ static int gfx_v9_4_3_xcc_kiq_init_queue(struct amdgpu_ring *ring, int xcc_id)
return 0;
}
-static int gfx_v9_4_3_xcc_kcq_init_queue(struct amdgpu_ring *ring, int xcc_id, bool restore)
+static void gfx_v9_4_3_xcc_kcq_init_queue(struct amdgpu_ring *ring, int xcc_id,
+ bool restore)
{
struct amdgpu_device *adev = ring->adev;
struct v9_mqd *mqd = ring->mqd_ptr;
@@ -2186,8 +2189,6 @@ static int gfx_v9_4_3_xcc_kcq_init_queue(struct amdgpu_ring *ring, int xcc_id, b
atomic64_set((atomic64_t *)&adev->wb.wb[ring->wptr_offs], 0);
amdgpu_ring_clear_ring(ring);
}
-
- return 0;
}
static int gfx_v9_4_3_xcc_kcq_fini_register(struct amdgpu_device *adev, int xcc_id)
@@ -2220,7 +2221,7 @@ static int gfx_v9_4_3_xcc_kiq_resume(struct amdgpu_device *adev, int xcc_id)
static int gfx_v9_4_3_xcc_kcq_resume(struct amdgpu_device *adev, int xcc_id)
{
struct amdgpu_ring *ring;
- int i, r;
+ int i;
gfx_v9_4_3_xcc_cp_compute_enable(adev, true, xcc_id);
@@ -2228,9 +2229,7 @@ static int gfx_v9_4_3_xcc_kcq_resume(struct amdgpu_device *adev, int xcc_id)
ring = &adev->gfx.compute_ring[i + xcc_id *
adev->gfx.num_compute_rings];
- r = gfx_v9_4_3_xcc_kcq_init_queue(ring, xcc_id, false);
- if (r)
- return r;
+ gfx_v9_4_3_xcc_kcq_init_queue(ring, xcc_id, false);
}
return amdgpu_gfx_enable_kcq(adev, xcc_id);
@@ -2292,7 +2291,9 @@ static int gfx_v9_4_3_cp_resume(struct amdgpu_device *adev)
r = amdgpu_xcp_init(adev->xcp_mgr, num_xcp, mode);
} else {
- if (amdgpu_xcp_query_partition_mode(adev->xcp_mgr,
+ if (adev->in_suspend)
+ amdgpu_xcp_restore_partition_mode(adev->xcp_mgr);
+ else if (amdgpu_xcp_query_partition_mode(adev->xcp_mgr,
AMDGPU_XCP_FL_NONE) ==
AMDGPU_UNKNOWN_COMPUTE_PARTITION_MODE)
r = amdgpu_xcp_switch_partition_mode(
@@ -3605,11 +3606,8 @@ pipe_reset:
return r;
}
- r = gfx_v9_4_3_xcc_kcq_init_queue(ring, ring->xcc_id, true);
- if (r) {
- dev_err(adev->dev, "fail to init kcq\n");
- return r;
- }
+ gfx_v9_4_3_xcc_kcq_init_queue(ring, ring->xcc_id, true);
+
spin_lock_irqsave(&kiq->ring_lock, flags);
r = amdgpu_ring_alloc(kiq_ring, kiq->pmf->map_queues_size);
if (r) {
@@ -4798,6 +4796,7 @@ static const struct amdgpu_ring_funcs gfx_v9_4_3_ring_funcs_kiq = {
.emit_wreg = gfx_v9_4_3_ring_emit_wreg,
.emit_reg_wait = gfx_v9_4_3_ring_emit_reg_wait,
.emit_reg_write_reg_wait = gfx_v9_4_3_ring_emit_reg_write_reg_wait,
+ .emit_hdp_flush = gfx_v9_4_3_ring_emit_hdp_flush,
};
static void gfx_v9_4_3_set_ring_funcs(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c
index d7499be8c4bf..ce6e04242c52 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v10_0.c
@@ -103,8 +103,10 @@ static int gmc_v10_0_process_interrupt(struct amdgpu_device *adev,
uint32_t vmhub_index = entry->client_id == SOC15_IH_CLIENTID_VMC ?
AMDGPU_MMHUB0(0) : AMDGPU_GFXHUB(0);
struct amdgpu_vmhub *hub = &adev->vmhub[vmhub_index];
- bool retry_fault = !!(entry->src_data[1] & 0x80);
- bool write_fault = !!(entry->src_data[1] & 0x20);
+ bool retry_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_RETRY);
+ bool write_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_WRITE);
struct amdgpu_task_info *task_info;
uint32_t status = 0;
u64 addr;
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c
index 7bc389d9f5c4..ba59ee8e398a 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c
@@ -103,12 +103,41 @@ static int gmc_v11_0_process_interrupt(struct amdgpu_device *adev,
uint32_t vmhub_index = entry->client_id == SOC21_IH_CLIENTID_VMC ?
AMDGPU_MMHUB0(0) : AMDGPU_GFXHUB(0);
struct amdgpu_vmhub *hub = &adev->vmhub[vmhub_index];
+ bool retry_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_RETRY);
+ bool write_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_WRITE);
uint32_t status = 0;
u64 addr;
addr = (u64)entry->src_data[0] << 12;
addr |= ((u64)entry->src_data[1] & 0xf) << 44;
+ if (retry_fault) {
+ /* Returning 1 here also prevents sending the IV to the KFD */
+
+ /* Process it only if it's the first fault for this address */
+ if (entry->ih != &adev->irq.ih_soft &&
+ amdgpu_gmc_filter_faults(adev, entry->ih, addr, entry->pasid,
+ entry->timestamp))
+ return 1;
+
+ /* Delegate it to a different ring if the hardware hasn't
+ * already done it.
+ */
+ if (entry->ih == &adev->irq.ih) {
+ amdgpu_irq_delegate(adev, entry, 8);
+ return 1;
+ }
+
+ /* Try to handle the recoverable page faults by filling page
+ * tables
+ */
+ if (amdgpu_vm_handle_fault(adev, entry->pasid, 0, 0, addr,
+ entry->timestamp, write_fault))
+ return 1;
+ }
+
if (!amdgpu_sriov_vf(adev)) {
/*
* Issue a dummy read to wait for the status register to
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c
index f4a19357ccbc..7a9d6894e321 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v12_0.c
@@ -91,6 +91,10 @@ static int gmc_v12_0_process_interrupt(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
struct amdgpu_vmhub *hub;
+ bool retry_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_RETRY);
+ bool write_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_WRITE);
uint32_t status = 0;
u64 addr;
@@ -102,6 +106,31 @@ static int gmc_v12_0_process_interrupt(struct amdgpu_device *adev,
else
hub = &adev->vmhub[AMDGPU_GFXHUB(0)];
+ if (retry_fault) {
+ /* Returning 1 here also prevents sending the IV to the KFD */
+
+ /* Process it only if it's the first fault for this address */
+ if (entry->ih != &adev->irq.ih_soft &&
+ amdgpu_gmc_filter_faults(adev, entry->ih, addr, entry->pasid,
+ entry->timestamp))
+ return 1;
+
+ /* Delegate it to a different ring if the hardware hasn't
+ * already done it.
+ */
+ if (entry->ih == &adev->irq.ih) {
+ amdgpu_irq_delegate(adev, entry, 8);
+ return 1;
+ }
+
+ /* Try to handle the recoverable page faults by filling page
+ * tables
+ */
+ if (amdgpu_vm_handle_fault(adev, entry->pasid, 0, 0, addr,
+ entry->timestamp, write_fault))
+ return 1;
+ }
+
if (!amdgpu_sriov_vf(adev)) {
/*
* Issue a dummy read to wait for the status register to
@@ -312,9 +341,7 @@ static void gmc_v12_0_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid,
return;
}
- mutex_lock(&adev->mman.gtt_window_lock);
gmc_v12_0_flush_vm_hub(adev, vmid, vmhub, 0);
- mutex_unlock(&adev->mman.gtt_window_lock);
return;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
index f6ad7911f1e6..a8ec95f42926 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
@@ -213,7 +213,7 @@ static void gmc_v6_0_vram_gtt_location(struct amdgpu_device *adev,
amdgpu_gmc_set_agp_default(adev, mc);
amdgpu_gmc_vram_location(adev, mc, base);
- amdgpu_gmc_gart_location(adev, mc, AMDGPU_GART_PLACEMENT_BEST_FIT);
+ amdgpu_gmc_gart_location(adev, mc, AMDGPU_GART_PLACEMENT_LOW);
}
static void gmc_v6_0_mc_program(struct amdgpu_device *adev)
@@ -610,23 +610,21 @@ static void gmc_v6_0_gart_disable(struct amdgpu_device *adev)
}
static void gmc_v6_0_vm_decode_fault(struct amdgpu_device *adev,
- u32 status, u32 addr, u32 mc_client)
+ u32 status, u32 addr)
{
u32 mc_id;
u32 vmid = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS, VMID);
u32 protections = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS,
PROTECTIONS);
- char block[5] = { mc_client >> 24, (mc_client >> 16) & 0xff,
- (mc_client >> 8) & 0xff, mc_client & 0xff, 0 };
mc_id = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS,
MEMORY_CLIENT_ID);
- dev_err(adev->dev, "VM fault (0x%02x, vmid %d) at page %u, %s from '%s' (0x%08x) (%d)\n",
+ dev_err(adev->dev, "VM fault (0x%02x, vmid %d) at page %u, %s from %d\n",
protections, vmid, addr,
REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS,
MEMORY_CLIENT_RW) ?
- "write" : "read", block, mc_client, mc_id);
+ "write" : "read", mc_id);
}
static const u32 mc_cg_registers[] = {
@@ -1072,6 +1070,12 @@ static int gmc_v6_0_process_interrupt(struct amdgpu_device *adev,
{
u32 addr, status;
+ /* Delegate to the soft IRQ handler ring */
+ if (adev->irq.ih_soft.enabled && entry->ih != &adev->irq.ih_soft) {
+ amdgpu_irq_delegate(adev, entry, 4);
+ return 1;
+ }
+
addr = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_ADDR);
status = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_STATUS);
WREG32_P(mmVM_CONTEXT1_CNTL2, 1, ~1);
@@ -1079,6 +1083,10 @@ static int gmc_v6_0_process_interrupt(struct amdgpu_device *adev,
if (!addr && !status)
return 0;
+ amdgpu_vm_update_fault_cache(adev, entry->pasid,
+ ((u64)addr) << AMDGPU_GPU_PAGE_SHIFT,
+ status, AMDGPU_GFXHUB(0));
+
if (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_FIRST)
gmc_v6_0_set_fault_enable_default(adev, false);
@@ -1089,7 +1097,7 @@ static int gmc_v6_0_process_interrupt(struct amdgpu_device *adev,
addr);
dev_err(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
status);
- gmc_v6_0_vm_decode_fault(adev, status, addr, 0);
+ gmc_v6_0_vm_decode_fault(adev, status, addr);
}
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
index 0e5e54d0a9a5..fbd0bf147f50 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
@@ -1261,6 +1261,12 @@ static int gmc_v7_0_process_interrupt(struct amdgpu_device *adev,
{
u32 addr, status, mc_client, vmid;
+ /* Delegate to the soft IRQ handler ring */
+ if (adev->irq.ih_soft.enabled && entry->ih != &adev->irq.ih_soft) {
+ amdgpu_irq_delegate(adev, entry, 4);
+ return 1;
+ }
+
addr = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_ADDR);
status = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_STATUS);
mc_client = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_MCCLIENT);
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
index e1509480dfc2..6551b60f2584 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
@@ -1439,6 +1439,12 @@ static int gmc_v8_0_process_interrupt(struct amdgpu_device *adev,
return 0;
}
+ /* Delegate to the soft IRQ handler ring */
+ if (adev->irq.ih_soft.enabled && entry->ih != &adev->irq.ih_soft) {
+ amdgpu_irq_delegate(adev, entry, 4);
+ return 1;
+ }
+
addr = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_ADDR);
status = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_STATUS);
mc_client = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_MCCLIENT);
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
index 0d1dd587db5f..8ad7519f7b58 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
@@ -544,8 +544,10 @@ static int gmc_v9_0_process_interrupt(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
- bool retry_fault = !!(entry->src_data[1] & 0x80);
- bool write_fault = !!(entry->src_data[1] & 0x20);
+ bool retry_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_RETRY);
+ bool write_fault = !!(entry->src_data[1] &
+ AMDGPU_GMC9_FAULT_SOURCE_DATA_WRITE);
uint32_t status = 0, cid = 0, rw = 0, fed = 0;
struct amdgpu_task_info *task_info;
struct amdgpu_vmhub *hub;
@@ -1843,6 +1845,10 @@ static void gmc_v9_4_3_init_vram_info(struct amdgpu_device *adev)
if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0))
adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM3E;
+ if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4) &&
+ adev->rev_id == 0x3)
+ adev->gmc.vram_type = AMDGPU_VRAM_TYPE_HBM3E;
+
if (!(adev->flags & AMD_IS_APU) && !amdgpu_sriov_vf(adev)) {
vram_info = RREG32(regBIF_BIOS_SCRATCH_4);
adev->gmc.vram_vendor = vram_info & 0xF;
diff --git a/drivers/gpu/drm/amd/amdgpu/iceland_ih.c b/drivers/gpu/drm/amd/amdgpu/iceland_ih.c
index 1317ede131b6..01cadf898c00 100644
--- a/drivers/gpu/drm/amd/amdgpu/iceland_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/iceland_ih.c
@@ -157,6 +157,9 @@ static int iceland_ih_irq_init(struct amdgpu_device *adev)
/* enable interrupts */
iceland_ih_enable_interrupts(adev);
+ if (adev->irq.ih_soft.ring_size)
+ adev->irq.ih_soft.enabled = true;
+
return 0;
}
@@ -194,6 +197,9 @@ static u32 iceland_ih_get_wptr(struct amdgpu_device *adev,
wptr = le32_to_cpu(*ih->wptr_cpu);
+ if (ih == &adev->irq.ih_soft)
+ goto out;
+
if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
goto out;
@@ -296,6 +302,10 @@ static int iceland_ih_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ r = amdgpu_ih_ring_init(adev, &adev->irq.ih_soft, IH_SW_RING_SIZE, true);
+ if (r)
+ return r;
+
r = amdgpu_irq_init(adev);
return r;
diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
index baf097d2e1ac..ab0bf880d3d8 100644
--- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c
@@ -878,6 +878,7 @@ static const struct amdgpu_ring_funcs jpeg_v5_0_1_dec_ring_vm_funcs = {
.get_rptr = jpeg_v5_0_1_dec_ring_get_rptr,
.get_wptr = jpeg_v5_0_1_dec_ring_get_wptr,
.set_wptr = jpeg_v5_0_1_dec_ring_set_wptr,
+ .parse_cs = amdgpu_jpeg_dec_parse_cs,
.emit_frame_size =
SOC15_FLUSH_GPU_TLB_NUM_WREG * 6 +
SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 8 +
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
index 1cd9eaeef38f..64cae89357b6 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c
@@ -205,10 +205,11 @@ static int mes_userq_detect_and_reset(struct amdgpu_device *adev,
int db_array_size = amdgpu_mes_get_hung_queue_db_array_size(adev);
struct mes_detect_and_reset_queue_input input;
struct amdgpu_usermode_queue *queue;
- struct amdgpu_userq_mgr *uqm, *tmp;
unsigned int hung_db_num = 0;
- int queue_id, r, i;
+ unsigned long queue_id;
u32 db_array[8];
+ bool found_hung_queue = false;
+ int r, i;
if (db_array_size > 8) {
dev_err(adev->dev, "DB array size (%d vs 8) too small\n",
@@ -227,22 +228,26 @@ static int mes_userq_detect_and_reset(struct amdgpu_device *adev,
if (r) {
dev_err(adev->dev, "Failed to detect and reset queues, err (%d)\n", r);
} else if (hung_db_num) {
- list_for_each_entry_safe(uqm, tmp, &adev->userq_mgr_list, list) {
- idr_for_each_entry(&uqm->userq_idr, queue, queue_id) {
- if (queue->queue_type == queue_type) {
- for (i = 0; i < hung_db_num; i++) {
- if (queue->doorbell_index == db_array[i]) {
- queue->state = AMDGPU_USERQ_STATE_HUNG;
- atomic_inc(&adev->gpu_reset_counter);
- amdgpu_userq_fence_driver_force_completion(queue);
- drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL);
- }
+ xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
+ if (queue->queue_type == queue_type) {
+ for (i = 0; i < hung_db_num; i++) {
+ if (queue->doorbell_index == db_array[i]) {
+ queue->state = AMDGPU_USERQ_STATE_HUNG;
+ found_hung_queue = true;
+ atomic_inc(&adev->gpu_reset_counter);
+ amdgpu_userq_fence_driver_force_completion(queue);
+ drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL);
}
}
}
}
}
+ if (found_hung_queue) {
+ /* Resume scheduling after hang recovery */
+ r = amdgpu_mes_resume(adev);
+ }
+
return r;
}
@@ -254,7 +259,6 @@ static int mes_userq_mqd_create(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_mqd *mqd_hw_default = &adev->mqds[queue->queue_type];
struct drm_amdgpu_userq_in *mqd_user = args_in;
struct amdgpu_mqd_prop *userq_props;
- struct amdgpu_gfx_shadow_info shadow_info;
int r;
/* Structure to initialize MQD for userqueue using generic MQD init function */
@@ -280,8 +284,6 @@ static int mes_userq_mqd_create(struct amdgpu_userq_mgr *uq_mgr,
userq_props->doorbell_index = queue->doorbell_index;
userq_props->fence_address = queue->fence_drv->gpu_addr;
- if (adev->gfx.funcs->get_gfx_shadow_info)
- adev->gfx.funcs->get_gfx_shadow_info(adev, &shadow_info, true);
if (queue->queue_type == AMDGPU_HW_IP_COMPUTE) {
struct drm_amdgpu_userq_mqd_compute_gfx11 *compute_mqd;
@@ -298,8 +300,9 @@ static int mes_userq_mqd_create(struct amdgpu_userq_mgr *uq_mgr,
goto free_mqd;
}
- if (amdgpu_userq_input_va_validate(queue->vm, compute_mqd->eop_va,
- max_t(u32, PAGE_SIZE, AMDGPU_GPU_PAGE_SIZE)))
+ r = amdgpu_userq_input_va_validate(queue, compute_mqd->eop_va,
+ 2048);
+ if (r)
goto free_mqd;
userq_props->eop_gpu_addr = compute_mqd->eop_va;
@@ -311,6 +314,14 @@ static int mes_userq_mqd_create(struct amdgpu_userq_mgr *uq_mgr,
kfree(compute_mqd);
} else if (queue->queue_type == AMDGPU_HW_IP_GFX) {
struct drm_amdgpu_userq_mqd_gfx11 *mqd_gfx_v11;
+ struct amdgpu_gfx_shadow_info shadow_info;
+
+ if (adev->gfx.funcs->get_gfx_shadow_info) {
+ adev->gfx.funcs->get_gfx_shadow_info(adev, &shadow_info, true);
+ } else {
+ r = -EINVAL;
+ goto free_mqd;
+ }
if (mqd_user->mqd_size != sizeof(*mqd_gfx_v11) || !mqd_user->mqd) {
DRM_ERROR("Invalid GFX MQD\n");
@@ -330,8 +341,13 @@ static int mes_userq_mqd_create(struct amdgpu_userq_mgr *uq_mgr,
userq_props->tmz_queue =
mqd_user->flags & AMDGPU_USERQ_CREATE_FLAGS_QUEUE_SECURE;
- if (amdgpu_userq_input_va_validate(queue->vm, mqd_gfx_v11->shadow_va,
- shadow_info.shadow_size))
+ r = amdgpu_userq_input_va_validate(queue, mqd_gfx_v11->shadow_va,
+ shadow_info.shadow_size);
+ if (r)
+ goto free_mqd;
+ r = amdgpu_userq_input_va_validate(queue, mqd_gfx_v11->csa_va,
+ shadow_info.csa_size);
+ if (r)
goto free_mqd;
kfree(mqd_gfx_v11);
@@ -350,9 +366,9 @@ static int mes_userq_mqd_create(struct amdgpu_userq_mgr *uq_mgr,
r = -ENOMEM;
goto free_mqd;
}
-
- if (amdgpu_userq_input_va_validate(queue->vm, mqd_sdma_v11->csa_va,
- shadow_info.csa_size))
+ r = amdgpu_userq_input_va_validate(queue, mqd_sdma_v11->csa_va,
+ 32);
+ if (r)
goto free_mqd;
userq_props->csa_addr = mqd_sdma_v11->csa_va;
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
index da575bb1377f..3a52754b5cad 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
@@ -369,6 +369,7 @@ static int mes_v11_0_remove_hw_queue(struct amdgpu_mes *mes,
struct mes_remove_queue_input *input)
{
union MESAPI__REMOVE_QUEUE mes_remove_queue_pkt;
+ uint32_t mes_rev = mes->sched_version & AMDGPU_MES_VERSION_MASK;
memset(&mes_remove_queue_pkt, 0, sizeof(mes_remove_queue_pkt));
@@ -379,6 +380,9 @@ static int mes_v11_0_remove_hw_queue(struct amdgpu_mes *mes,
mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset;
mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr;
+ if (mes_rev >= 0x60)
+ mes_remove_queue_pkt.remove_queue_after_reset = input->remove_queue_after_reset;
+
return mes_v11_0_submit_pkt_and_poll_completion(mes,
&mes_remove_queue_pkt, sizeof(mes_remove_queue_pkt),
offsetof(union MESAPI__REMOVE_QUEUE, api_status));
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c
index 7f3512d9de07..744e95d3984a 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c
@@ -361,6 +361,7 @@ static int mes_v12_0_remove_hw_queue(struct amdgpu_mes *mes,
struct mes_remove_queue_input *input)
{
union MESAPI__REMOVE_QUEUE mes_remove_queue_pkt;
+ uint32_t mes_rev = mes->sched_version & AMDGPU_MES_VERSION_MASK;
memset(&mes_remove_queue_pkt, 0, sizeof(mes_remove_queue_pkt));
@@ -371,6 +372,9 @@ static int mes_v12_0_remove_hw_queue(struct amdgpu_mes *mes,
mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset;
mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr;
+ if (mes_rev >= 0x5a)
+ mes_remove_queue_pkt.remove_queue_after_reset = input->remove_queue_after_reset;
+
return mes_v12_0_submit_pkt_and_poll_completion(mes,
AMDGPU_MES_SCHED_PIPE,
&mes_remove_queue_pkt, sizeof(mes_remove_queue_pkt),
diff --git a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c
index e5282a5d05d9..e7cd07383d56 100644
--- a/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c
+++ b/drivers/gpu/drm/amd/amdgpu/mxgpu_nv.c
@@ -173,13 +173,17 @@ static void xgpu_nv_mailbox_trans_msg (struct amdgpu_device *adev,
static int xgpu_nv_send_access_requests_with_param(struct amdgpu_device *adev,
enum idh_request req, u32 data1, u32 data2, u32 data3)
{
- int r, retry = 1;
+ struct amdgpu_virt *virt = &adev->virt;
+ int r = 0, retry = 1;
enum idh_event event = -1;
+ mutex_lock(&virt->access_req_mutex);
send_request:
- if (amdgpu_ras_is_rma(adev))
- return -ENODEV;
+ if (amdgpu_ras_is_rma(adev)) {
+ r = -ENODEV;
+ goto out;
+ }
xgpu_nv_mailbox_trans_msg(adev, req, data1, data2, data3);
@@ -217,17 +221,25 @@ send_request:
if (req != IDH_REQ_GPU_INIT_DATA) {
dev_err(adev->dev, "Doesn't get msg:%d from pf, error=%d\n", event, r);
- return r;
+ goto out;
} else /* host doesn't support REQ_GPU_INIT_DATA handshake */
adev->virt.req_init_data_ver = 0;
} else {
if (req == IDH_REQ_GPU_INIT_DATA) {
- adev->virt.req_init_data_ver =
- RREG32_NO_KIQ(mmMAILBOX_MSGBUF_RCV_DW1);
-
- /* assume V1 in case host doesn't set version number */
- if (adev->virt.req_init_data_ver < 1)
- adev->virt.req_init_data_ver = 1;
+ switch (RREG32_NO_KIQ(mmMAILBOX_MSGBUF_RCV_DW1)) {
+ case GPU_CRIT_REGION_V2:
+ adev->virt.req_init_data_ver = GPU_CRIT_REGION_V2;
+ adev->virt.init_data_header.offset =
+ RREG32_NO_KIQ(mmMAILBOX_MSGBUF_RCV_DW2);
+ adev->virt.init_data_header.size_kb =
+ RREG32_NO_KIQ(mmMAILBOX_MSGBUF_RCV_DW3);
+ break;
+ default:
+ adev->virt.req_init_data_ver = GPU_CRIT_REGION_V1;
+ adev->virt.init_data_header.offset = -1;
+ adev->virt.init_data_header.size_kb = 0;
+ break;
+ }
}
}
@@ -238,7 +250,10 @@ send_request:
}
}
- return 0;
+out:
+ mutex_unlock(&virt->access_req_mutex);
+
+ return r;
}
static int xgpu_nv_send_access_requests(struct amdgpu_device *adev,
@@ -285,7 +300,8 @@ static int xgpu_nv_release_full_gpu_access(struct amdgpu_device *adev,
static int xgpu_nv_request_init_data(struct amdgpu_device *adev)
{
- return xgpu_nv_send_access_requests(adev, IDH_REQ_GPU_INIT_DATA);
+ return xgpu_nv_send_access_requests_with_param(adev, IDH_REQ_GPU_INIT_DATA,
+ 0, GPU_CRIT_REGION_V2, 0);
}
static int xgpu_nv_mailbox_ack_irq(struct amdgpu_device *adev,
diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c
index 1c22bc11c1f8..bdfd2917e3ca 100644
--- a/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c
+++ b/drivers/gpu/drm/amd/amdgpu/nbio_v7_9.c
@@ -41,19 +41,21 @@ static void nbio_v7_9_remap_hdp_registers(struct amdgpu_device *adev)
static u32 nbio_v7_9_get_rev_id(struct amdgpu_device *adev)
{
- u32 tmp;
-
- tmp = IP_VERSION_SUBREV(amdgpu_ip_version_full(adev, NBIO_HWIP, 0));
- /* If it is VF or subrevision holds a non-zero value, that should be used */
- if (tmp || amdgpu_sriov_vf(adev))
- return tmp;
+ u32 rev_id;
- /* If discovery subrev is not updated, use register version */
- tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0);
- tmp = REG_GET_FIELD(tmp, RCC_STRAP0_RCC_DEV0_EPF0_STRAP0,
- STRAP_ATI_REV_ID_DEV0_F0);
+ /*
+ * fetch the sub-revision field from the IP-discovery table
+ * (returns zero if the table entry is not populated).
+ */
+ if (amdgpu_sriov_vf(adev)) {
+ rev_id = IP_VERSION_SUBREV(amdgpu_ip_version_full(adev, NBIO_HWIP, 0));
+ } else {
+ rev_id = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0);
+ rev_id = REG_GET_FIELD(rev_id, RCC_STRAP0_RCC_DEV0_EPF0_STRAP0,
+ STRAP_ATI_REV_ID_DEV0_F0);
+ }
- return tmp;
+ return rev_id;
}
static void nbio_v7_9_mc_access_enable(struct amdgpu_device *adev, bool enable)
diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c
index 64b240b51f1a..a9be7a505026 100644
--- a/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/psp_v11_0.c
@@ -142,13 +142,37 @@ static int psp_v11_0_init_microcode(struct psp_context *psp)
return err;
}
-static int psp_v11_0_wait_for_bootloader(struct psp_context *psp)
+static int psp_v11_wait_for_tos_unload(struct psp_context *psp)
{
struct amdgpu_device *adev = psp->adev;
+ uint32_t sol_reg1, sol_reg2;
+ int retry_loop;
+ /* Wait for the TOS to be unloaded */
+ for (retry_loop = 0; retry_loop < 20; retry_loop++) {
+ sol_reg1 = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_81);
+ usleep_range(1000, 2000);
+ sol_reg2 = RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_81);
+ if (sol_reg1 == sol_reg2)
+ return 0;
+ }
+ dev_err(adev->dev, "TOS unload failed, C2PMSG_33: %x C2PMSG_81: %x",
+ RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_33),
+ RREG32_SOC15(MP0, 0, mmMP0_SMN_C2PMSG_81));
+
+ return -ETIME;
+}
+
+static int psp_v11_0_wait_for_bootloader(struct psp_context *psp)
+{
+ struct amdgpu_device *adev = psp->adev;
int ret;
int retry_loop;
+ /* For a reset done at the end of S3, only wait for TOS to be unloaded */
+ if (adev->in_s3 && !(adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev))
+ return psp_v11_wait_for_tos_unload(psp);
+
for (retry_loop = 0; retry_loop < 20; retry_loop++) {
/* Wait for bootloader to signify that is
ready having bit 31 of C2PMSG_35 set to 1 */
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
index 36b1ca73c2ed..a1443990d5c6 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c
@@ -2361,11 +2361,15 @@ static void sdma_v4_4_2_update_reset_mask(struct amdgpu_device *adev)
switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
case IP_VERSION(9, 4, 3):
case IP_VERSION(9, 4, 4):
- if ((adev->gfx.mec_fw_version >= 0xb0) && amdgpu_dpm_reset_sdma_is_supported(adev))
+ if ((adev->gfx.mec_fw_version >= 0xb0) &&
+ amdgpu_dpm_reset_sdma_is_supported(adev) &&
+ !adev->debug_disable_gpu_ring_reset)
adev->sdma.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
break;
case IP_VERSION(9, 5, 0):
- if ((adev->gfx.mec_fw_version >= 0xf) && amdgpu_dpm_reset_sdma_is_supported(adev))
+ if ((adev->gfx.mec_fw_version >= 0xf) &&
+ amdgpu_dpm_reset_sdma_is_supported(adev) &&
+ !adev->debug_disable_gpu_ring_reset)
adev->sdma.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
break;
default:
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
index 7dc67a22a7a0..8ddc4df06a1f 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c
@@ -1429,7 +1429,8 @@ static int sdma_v5_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(5, 0, 2):
case IP_VERSION(5, 0, 5):
if ((adev->sdma.instance[0].fw_version >= 35) &&
- !amdgpu_sriov_vf(adev))
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset)
adev->sdma.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
break;
default:
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c
index 3bd44c24f692..51101b0aa2fa 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c
@@ -342,7 +342,7 @@ static void sdma_v5_2_ring_emit_hdp_flush(struct amdgpu_ring *ring)
const struct nbio_hdp_flush_reg *nbio_hf_reg = adev->nbio.hdp_flush_reg;
if (ring->me > 1) {
- amdgpu_asic_flush_hdp(adev, ring);
+ amdgpu_hdp_flush(adev, ring);
} else {
ref_and_mask = nbio_hf_reg->ref_and_mask_sdma0 << ring->me;
@@ -1348,12 +1348,14 @@ static int sdma_v5_2_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(5, 2, 3):
case IP_VERSION(5, 2, 4):
if ((adev->sdma.instance[0].fw_version >= 76) &&
- !amdgpu_sriov_vf(adev))
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset)
adev->sdma.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
break;
case IP_VERSION(5, 2, 5):
if ((adev->sdma.instance[0].fw_version >= 34) &&
- !amdgpu_sriov_vf(adev))
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset)
adev->sdma.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
break;
default:
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
index db6e41967f12..217040044987 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c
@@ -1356,7 +1356,8 @@ static int sdma_v6_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(6, 0, 2):
case IP_VERSION(6, 0, 3):
if ((adev->sdma.instance[0].fw_version >= 21) &&
- !amdgpu_sriov_vf(adev))
+ !amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset)
adev->sdma.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
break;
default:
@@ -1389,7 +1390,7 @@ static int sdma_v6_0_sw_init(struct amdgpu_ip_block *ip_block)
adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs;
break;
case IP_VERSION(6, 0, 3):
- if ((adev->sdma.instance[0].fw_version >= 27) && !adev->sdma.disable_uq)
+ if (adev->sdma.instance[0].fw_version >= 29 && !adev->sdma.disable_uq)
adev->userq_funcs[AMDGPU_HW_IP_DMA] = &userq_mes_funcs;
break;
case IP_VERSION(6, 1, 0):
diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
index 326ecc8d37d2..2b81344dcd66 100644
--- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c
@@ -1337,7 +1337,8 @@ static int sdma_v7_0_sw_init(struct amdgpu_ip_block *ip_block)
adev->sdma.supported_reset =
amdgpu_get_soft_full_reset_mask(&adev->sdma.instance[0].ring);
- if (!amdgpu_sriov_vf(adev))
+ if (!amdgpu_sriov_vf(adev) &&
+ !adev->debug_disable_gpu_ring_reset)
adev->sdma.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
r = amdgpu_sdma_sysfs_reset_mask_init(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c
index e0f139de7991..f7288372ee61 100644
--- a/drivers/gpu/drm/amd/amdgpu/si.c
+++ b/drivers/gpu/drm/amd/amdgpu/si.c
@@ -45,6 +45,7 @@
#include "dce_v6_0.h"
#include "si.h"
#include "uvd_v3_1.h"
+#include "vce_v1_0.h"
#include "uvd/uvd_4_0_d.h"
@@ -921,8 +922,6 @@ static const u32 hainan_mgcg_cgcg_init[] =
0x3630, 0xfffffff0, 0x00000100,
};
-/* XXX: update when we support VCE */
-#if 0
/* tahiti, pitcairn, verde */
static const struct amdgpu_video_codec_info tahiti_video_codecs_encode_array[] =
{
@@ -940,13 +939,7 @@ static const struct amdgpu_video_codecs tahiti_video_codecs_encode =
.codec_count = ARRAY_SIZE(tahiti_video_codecs_encode_array),
.codec_array = tahiti_video_codecs_encode_array,
};
-#else
-static const struct amdgpu_video_codecs tahiti_video_codecs_encode =
-{
- .codec_count = 0,
- .codec_array = NULL,
-};
-#endif
+
/* oland and hainan don't support encode */
static const struct amdgpu_video_codecs hainan_video_codecs_encode =
{
@@ -1925,6 +1918,14 @@ static int si_set_vce_clocks(struct amdgpu_device *adev, u32 evclk, u32 ecclk)
~VCEPLL_BYPASS_EN_MASK);
if (!evclk || !ecclk) {
+ /*
+ * On some chips, the PLL takes way too long to get out of
+ * sleep mode, causing a timeout waiting on CTLACK/CTLACK2.
+ * Leave the PLL running in bypass mode.
+ */
+ if (adev->pdev->device == 0x6780)
+ return 0;
+
/* Keep the Bypass mode, put PLL to sleep */
WREG32_SMC_P(CG_VCEPLL_FUNC_CNTL, VCEPLL_SLEEP_MASK,
~VCEPLL_SLEEP_MASK);
@@ -2717,7 +2718,7 @@ int si_set_ip_blocks(struct amdgpu_device *adev)
else
amdgpu_device_ip_block_add(adev, &dce_v6_0_ip_block);
amdgpu_device_ip_block_add(adev, &uvd_v3_1_ip_block);
- /* amdgpu_device_ip_block_add(adev, &vce_v1_0_ip_block); */
+ amdgpu_device_ip_block_add(adev, &vce_v1_0_ip_block);
break;
case CHIP_OLAND:
amdgpu_device_ip_block_add(adev, &si_common_ip_block);
@@ -2735,7 +2736,6 @@ int si_set_ip_blocks(struct amdgpu_device *adev)
else
amdgpu_device_ip_block_add(adev, &dce_v6_4_ip_block);
amdgpu_device_ip_block_add(adev, &uvd_v3_1_ip_block);
- /* amdgpu_device_ip_block_add(adev, &vce_v1_0_ip_block); */
break;
case CHIP_HAINAN:
amdgpu_device_ip_block_add(adev, &si_common_ip_block);
diff --git a/drivers/gpu/drm/amd/amdgpu/si_ih.c b/drivers/gpu/drm/amd/amdgpu/si_ih.c
index 1df00f8a2406..66f650f87243 100644
--- a/drivers/gpu/drm/amd/amdgpu/si_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/si_ih.c
@@ -96,6 +96,9 @@ static int si_ih_irq_init(struct amdgpu_device *adev)
pci_set_master(adev->pdev);
si_ih_enable_interrupts(adev);
+ if (adev->irq.ih_soft.ring_size)
+ adev->irq.ih_soft.enabled = true;
+
return 0;
}
@@ -112,6 +115,9 @@ static u32 si_ih_get_wptr(struct amdgpu_device *adev,
wptr = le32_to_cpu(*ih->wptr_cpu);
+ if (ih == &adev->irq.ih_soft)
+ goto out;
+
if (wptr & IH_RB_WPTR__RB_OVERFLOW_MASK) {
wptr &= ~IH_RB_WPTR__RB_OVERFLOW_MASK;
dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
@@ -127,6 +133,8 @@ static u32 si_ih_get_wptr(struct amdgpu_device *adev,
tmp &= ~IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK;
WREG32(IH_RB_CNTL, tmp);
}
+
+out:
return (wptr & ih->ptr_mask);
}
@@ -175,6 +183,10 @@ static int si_ih_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ r = amdgpu_ih_ring_init(adev, &adev->irq.ih_soft, IH_SW_RING_SIZE, true);
+ if (r)
+ return r;
+
return amdgpu_irq_init(adev);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/sid.h b/drivers/gpu/drm/amd/amdgpu/sid.h
index cbd4f8951cfa..561462a8332e 100644
--- a/drivers/gpu/drm/amd/amdgpu/sid.h
+++ b/drivers/gpu/drm/amd/amdgpu/sid.h
@@ -582,45 +582,6 @@
#define DMA_PACKET_NOP 0xf
/* VCE */
-#define VCE_STATUS 0x20004
-#define VCE_VCPU_CNTL 0x20014
-#define VCE_CLK_EN (1 << 0)
-#define VCE_VCPU_CACHE_OFFSET0 0x20024
-#define VCE_VCPU_CACHE_SIZE0 0x20028
-#define VCE_VCPU_CACHE_OFFSET1 0x2002c
-#define VCE_VCPU_CACHE_SIZE1 0x20030
-#define VCE_VCPU_CACHE_OFFSET2 0x20034
-#define VCE_VCPU_CACHE_SIZE2 0x20038
-#define VCE_SOFT_RESET 0x20120
-#define VCE_ECPU_SOFT_RESET (1 << 0)
-#define VCE_FME_SOFT_RESET (1 << 2)
-#define VCE_RB_BASE_LO2 0x2016c
-#define VCE_RB_BASE_HI2 0x20170
-#define VCE_RB_SIZE2 0x20174
-#define VCE_RB_RPTR2 0x20178
-#define VCE_RB_WPTR2 0x2017c
-#define VCE_RB_BASE_LO 0x20180
-#define VCE_RB_BASE_HI 0x20184
-#define VCE_RB_SIZE 0x20188
-#define VCE_RB_RPTR 0x2018c
-#define VCE_RB_WPTR 0x20190
-#define VCE_CLOCK_GATING_A 0x202f8
-#define VCE_CLOCK_GATING_B 0x202fc
-#define VCE_UENC_CLOCK_GATING 0x205bc
-#define VCE_UENC_REG_CLOCK_GATING 0x205c0
-#define VCE_FW_REG_STATUS 0x20e10
-# define VCE_FW_REG_STATUS_BUSY (1 << 0)
-# define VCE_FW_REG_STATUS_PASS (1 << 3)
-# define VCE_FW_REG_STATUS_DONE (1 << 11)
-#define VCE_LMI_FW_START_KEYSEL 0x20e18
-#define VCE_LMI_FW_PERIODIC_CTRL 0x20e20
-#define VCE_LMI_CTRL2 0x20e74
-#define VCE_LMI_CTRL 0x20e98
-#define VCE_LMI_VM_CTRL 0x20ea0
-#define VCE_LMI_SWAP_CNTL 0x20eb4
-#define VCE_LMI_SWAP_CNTL1 0x20eb8
-#define VCE_LMI_CACHE_CTRL 0x20ef4
-
#define VCE_CMD_NO_OP 0x00000000
#define VCE_CMD_END 0x00000001
#define VCE_CMD_IB 0x00000002
@@ -629,7 +590,6 @@
#define VCE_CMD_IB_AUTO 0x00000005
#define VCE_CMD_SEMAPHORE 0x00000006
-
//#dce stupp
/* display controller offsets used for crtc/cur/lut/grph/viewport/etc. */
#define CRTC0_REGISTER_OFFSET (0x1b7c - 0x1b7c) //(0x6df0 - 0x6df0)/4
diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c
index 9785fada4fa7..42f5d9c0e3af 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc15.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc15.c
@@ -853,10 +853,6 @@ static bool soc15_need_reset_on_init(struct amdgpu_device *adev)
{
u32 sol_reg;
- /* CP hangs in IGT reloading test on RN, reset to WA */
- if (adev->asic_type == CHIP_RENOIR)
- return true;
-
if (amdgpu_gmc_need_reset_on_init(adev))
return true;
if (amdgpu_psp_tos_reload_needed(adev))
diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
index 7d17ae56f901..ee8038df17e3 100644
--- a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
+++ b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c
@@ -159,6 +159,9 @@ static int tonga_ih_irq_init(struct amdgpu_device *adev)
/* enable interrupts */
tonga_ih_enable_interrupts(adev);
+ if (adev->irq.ih_soft.ring_size)
+ adev->irq.ih_soft.enabled = true;
+
return 0;
}
@@ -196,6 +199,9 @@ static u32 tonga_ih_get_wptr(struct amdgpu_device *adev,
wptr = le32_to_cpu(*ih->wptr_cpu);
+ if (ih == &adev->irq.ih_soft)
+ goto out;
+
if (!REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW))
goto out;
@@ -306,6 +312,10 @@ static int tonga_ih_sw_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
+ r = amdgpu_ih_ring_init(adev, &adev->irq.ih_soft, IH_SW_RING_SIZE, true);
+ if (r)
+ return r;
+
adev->irq.ih.use_doorbell = true;
adev->irq.ih.doorbell_index = adev->doorbell_index.ih;
diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
index 8dc32787d625..0f5b1719fda5 100644
--- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c
@@ -711,6 +711,19 @@ static uint32_t umc_v12_0_get_die_id(struct amdgpu_device *adev,
return die;
}
+static void umc_v12_0_mca_ipid_parse(struct amdgpu_device *adev, uint64_t ipid,
+ uint32_t *did, uint32_t *ch, uint32_t *umc_inst, uint32_t *sid)
+{
+ if (did)
+ *did = MCA_IPID_2_DIE_ID(ipid);
+ if (ch)
+ *ch = MCA_IPID_2_UMC_CH(ipid);
+ if (umc_inst)
+ *umc_inst = MCA_IPID_2_UMC_INST(ipid);
+ if (sid)
+ *sid = MCA_IPID_2_SOCKET_ID(ipid);
+}
+
struct amdgpu_umc_ras umc_v12_0_ras = {
.ras_block = {
.hw_ops = &umc_v12_0_ras_hw_ops,
@@ -724,5 +737,6 @@ struct amdgpu_umc_ras umc_v12_0_ras = {
.convert_ras_err_addr = umc_v12_0_convert_error_address,
.get_die_id_from_pa = umc_v12_0_get_die_id,
.get_retire_flip_bits = umc_v12_0_get_retire_flip_bits,
+ .mca_ipid_parse = umc_v12_0_mca_ipid_parse,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c
new file mode 100644
index 000000000000..9ae424618556
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.c
@@ -0,0 +1,839 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ * Copyright 2025 Valve Corporation
+ * Copyright 2025 Alexandre Demers
+ * All Rights Reserved.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * Authors: Christian König <christian.koenig@amd.com>
+ * Timur Kristóf <timur.kristof@gmail.com>
+ * Alexandre Demers <alexandre.f.demers@gmail.com>
+ */
+
+#include <linux/firmware.h>
+
+#include "amdgpu.h"
+#include "amdgpu_vce.h"
+#include "amdgpu_gart.h"
+#include "sid.h"
+#include "vce_v1_0.h"
+#include "vce/vce_1_0_d.h"
+#include "vce/vce_1_0_sh_mask.h"
+#include "oss/oss_1_0_d.h"
+#include "oss/oss_1_0_sh_mask.h"
+
+#define VCE_V1_0_FW_SIZE (256 * 1024)
+#define VCE_V1_0_STACK_SIZE (64 * 1024)
+#define VCE_V1_0_DATA_SIZE (7808 * (AMDGPU_MAX_VCE_HANDLES + 1))
+#define VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK 0x02
+
+#define VCE_V1_0_GART_PAGE_START \
+ (AMDGPU_GTT_MAX_TRANSFER_SIZE * AMDGPU_GTT_NUM_TRANSFER_WINDOWS)
+#define VCE_V1_0_GART_ADDR_START \
+ (VCE_V1_0_GART_PAGE_START * AMDGPU_GPU_PAGE_SIZE)
+
+static void vce_v1_0_set_ring_funcs(struct amdgpu_device *adev);
+static void vce_v1_0_set_irq_funcs(struct amdgpu_device *adev);
+
+struct vce_v1_0_fw_signature {
+ int32_t offset;
+ uint32_t length;
+ int32_t number;
+ struct {
+ uint32_t chip_id;
+ uint32_t keyselect;
+ uint32_t nonce[4];
+ uint32_t sigval[4];
+ } val[8];
+};
+
+/**
+ * vce_v1_0_ring_get_rptr - get read pointer
+ *
+ * @ring: amdgpu_ring pointer
+ *
+ * Returns the current hardware read pointer
+ */
+static uint64_t vce_v1_0_ring_get_rptr(struct amdgpu_ring *ring)
+{
+ struct amdgpu_device *adev = ring->adev;
+
+ if (ring->me == 0)
+ return RREG32(mmVCE_RB_RPTR);
+ else
+ return RREG32(mmVCE_RB_RPTR2);
+}
+
+/**
+ * vce_v1_0_ring_get_wptr - get write pointer
+ *
+ * @ring: amdgpu_ring pointer
+ *
+ * Returns the current hardware write pointer
+ */
+static uint64_t vce_v1_0_ring_get_wptr(struct amdgpu_ring *ring)
+{
+ struct amdgpu_device *adev = ring->adev;
+
+ if (ring->me == 0)
+ return RREG32(mmVCE_RB_WPTR);
+ else
+ return RREG32(mmVCE_RB_WPTR2);
+}
+
+/**
+ * vce_v1_0_ring_set_wptr - set write pointer
+ *
+ * @ring: amdgpu_ring pointer
+ *
+ * Commits the write pointer to the hardware
+ */
+static void vce_v1_0_ring_set_wptr(struct amdgpu_ring *ring)
+{
+ struct amdgpu_device *adev = ring->adev;
+
+ if (ring->me == 0)
+ WREG32(mmVCE_RB_WPTR, lower_32_bits(ring->wptr));
+ else
+ WREG32(mmVCE_RB_WPTR2, lower_32_bits(ring->wptr));
+}
+
+static int vce_v1_0_lmi_clean(struct amdgpu_device *adev)
+{
+ int i, j;
+
+ for (i = 0; i < 10; ++i) {
+ for (j = 0; j < 100; ++j) {
+ if (RREG32(mmVCE_LMI_STATUS) & 0x337f)
+ return 0;
+
+ mdelay(10);
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int vce_v1_0_firmware_loaded(struct amdgpu_device *adev)
+{
+ int i, j;
+
+ for (i = 0; i < 10; ++i) {
+ for (j = 0; j < 100; ++j) {
+ if (RREG32(mmVCE_STATUS) & VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK)
+ return 0;
+ mdelay(10);
+ }
+
+ dev_err(adev->dev, "VCE not responding, trying to reset the ECPU\n");
+
+ WREG32_P(mmVCE_SOFT_RESET,
+ VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
+ ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+ mdelay(10);
+ WREG32_P(mmVCE_SOFT_RESET, 0,
+ ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+ mdelay(10);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void vce_v1_0_init_cg(struct amdgpu_device *adev)
+{
+ u32 tmp;
+
+ tmp = RREG32(mmVCE_CLOCK_GATING_A);
+ tmp |= VCE_CLOCK_GATING_A__CGC_DYN_CLOCK_MODE_MASK;
+ WREG32(mmVCE_CLOCK_GATING_A, tmp);
+
+ tmp = RREG32(mmVCE_CLOCK_GATING_B);
+ tmp |= 0x1e;
+ tmp &= ~0xe100e1;
+ WREG32(mmVCE_CLOCK_GATING_B, tmp);
+
+ tmp = RREG32(mmVCE_UENC_CLOCK_GATING);
+ tmp &= ~0xff9ff000;
+ WREG32(mmVCE_UENC_CLOCK_GATING, tmp);
+
+ tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
+ tmp &= ~0x3ff;
+ WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp);
+}
+
+/**
+ * vce_v1_0_load_fw_signature - load firmware signature into VCPU BO
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * The VCE1 firmware validation mechanism needs a firmware signature.
+ * This function finds the signature appropriate for the current
+ * ASIC and writes that into the VCPU BO.
+ */
+static int vce_v1_0_load_fw_signature(struct amdgpu_device *adev)
+{
+ const struct common_firmware_header *hdr;
+ struct vce_v1_0_fw_signature *sign;
+ unsigned int ucode_offset;
+ uint32_t chip_id;
+ u32 *cpu_addr;
+ int i;
+
+ hdr = (const struct common_firmware_header *)adev->vce.fw->data;
+ ucode_offset = le32_to_cpu(hdr->ucode_array_offset_bytes);
+ cpu_addr = adev->vce.cpu_addr;
+
+ sign = (void *)adev->vce.fw->data + ucode_offset;
+
+ switch (adev->asic_type) {
+ case CHIP_TAHITI:
+ chip_id = 0x01000014;
+ break;
+ case CHIP_VERDE:
+ chip_id = 0x01000015;
+ break;
+ case CHIP_PITCAIRN:
+ chip_id = 0x01000016;
+ break;
+ default:
+ dev_err(adev->dev, "asic_type %#010x was not found!", adev->asic_type);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < le32_to_cpu(sign->number); ++i) {
+ if (le32_to_cpu(sign->val[i].chip_id) == chip_id)
+ break;
+ }
+
+ if (i == le32_to_cpu(sign->number)) {
+ dev_err(adev->dev, "chip_id 0x%x for %s was not found in VCE firmware",
+ chip_id, amdgpu_asic_name[adev->asic_type]);
+ return -EINVAL;
+ }
+
+ cpu_addr += (256 - 64) / 4;
+ memcpy_toio(&cpu_addr[0], &sign->val[i].nonce[0], 16);
+ cpu_addr[4] = cpu_to_le32(le32_to_cpu(sign->length) + 64);
+
+ memset_io(&cpu_addr[5], 0, 44);
+ memcpy_toio(&cpu_addr[16], &sign[1], hdr->ucode_size_bytes - sizeof(*sign));
+
+ cpu_addr += (le32_to_cpu(sign->length) + 64) / 4;
+ memcpy_toio(&cpu_addr[0], &sign->val[i].sigval[0], 16);
+
+ adev->vce.keyselect = le32_to_cpu(sign->val[i].keyselect);
+
+ return 0;
+}
+
+static int vce_v1_0_wait_for_fw_validation(struct amdgpu_device *adev)
+{
+ int i;
+
+ dev_dbg(adev->dev, "VCE keyselect: %d", adev->vce.keyselect);
+ WREG32(mmVCE_LMI_FW_START_KEYSEL, adev->vce.keyselect);
+
+ for (i = 0; i < 10; ++i) {
+ mdelay(10);
+ if (RREG32(mmVCE_FW_REG_STATUS) & VCE_FW_REG_STATUS__DONE_MASK)
+ break;
+ }
+
+ if (!(RREG32(mmVCE_FW_REG_STATUS) & VCE_FW_REG_STATUS__DONE_MASK)) {
+ dev_err(adev->dev, "VCE FW validation timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!(RREG32(mmVCE_FW_REG_STATUS) & VCE_FW_REG_STATUS__PASS_MASK)) {
+ dev_err(adev->dev, "VCE FW validation failed\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 10; ++i) {
+ mdelay(10);
+ if (!(RREG32(mmVCE_FW_REG_STATUS) & VCE_FW_REG_STATUS__BUSY_MASK))
+ break;
+ }
+
+ if (RREG32(mmVCE_FW_REG_STATUS) & VCE_FW_REG_STATUS__BUSY_MASK) {
+ dev_err(adev->dev, "VCE FW busy timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int vce_v1_0_mc_resume(struct amdgpu_device *adev)
+{
+ uint32_t offset;
+ uint32_t size;
+
+ /*
+ * When the keyselect is already set, don't perturb VCE FW.
+ * Validation seems to always fail the second time.
+ */
+ if (RREG32(mmVCE_LMI_FW_START_KEYSEL)) {
+ dev_dbg(adev->dev, "keyselect already set: 0x%x (on CPU: 0x%x)\n",
+ RREG32(mmVCE_LMI_FW_START_KEYSEL), adev->vce.keyselect);
+
+ WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100);
+ return 0;
+ }
+
+ WREG32_P(mmVCE_CLOCK_GATING_A, 0, ~(1 << 16));
+ WREG32_P(mmVCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000);
+ WREG32_P(mmVCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F);
+ WREG32(mmVCE_CLOCK_GATING_B, 0);
+
+ WREG32_P(mmVCE_LMI_FW_PERIODIC_CTRL, 0x4, ~0x4);
+
+ WREG32(mmVCE_LMI_CTRL, 0x00398000);
+
+ WREG32_P(mmVCE_LMI_CACHE_CTRL, 0x0, ~0x1);
+ WREG32(mmVCE_LMI_SWAP_CNTL, 0);
+ WREG32(mmVCE_LMI_SWAP_CNTL1, 0);
+ WREG32(mmVCE_LMI_VM_CTRL, 0);
+
+ WREG32(mmVCE_VCPU_SCRATCH7, AMDGPU_MAX_VCE_HANDLES);
+
+ offset = adev->vce.gpu_addr + AMDGPU_VCE_FIRMWARE_OFFSET;
+ size = VCE_V1_0_FW_SIZE;
+ WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff);
+ WREG32(mmVCE_VCPU_CACHE_SIZE0, size);
+
+ offset += size;
+ size = VCE_V1_0_STACK_SIZE;
+ WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset & 0x7fffffff);
+ WREG32(mmVCE_VCPU_CACHE_SIZE1, size);
+
+ offset += size;
+ size = VCE_V1_0_DATA_SIZE;
+ WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset & 0x7fffffff);
+ WREG32(mmVCE_VCPU_CACHE_SIZE2, size);
+
+ WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100);
+
+ return vce_v1_0_wait_for_fw_validation(adev);
+}
+
+/**
+ * vce_v1_0_is_idle() - Check idle status of VCE1 IP block
+ *
+ * @ip_block: amdgpu_ip_block pointer
+ *
+ * Check whether VCE is busy according to VCE_STATUS.
+ * Also check whether the SRBM thinks VCE is busy, although
+ * SRBM_STATUS.VCE_BUSY seems to be bogus because it
+ * appears to mirror the VCE_STATUS.VCPU_REPORT_FW_LOADED bit.
+ */
+static bool vce_v1_0_is_idle(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ bool busy =
+ (RREG32(mmVCE_STATUS) & (VCE_STATUS__JOB_BUSY_MASK | VCE_STATUS__UENC_BUSY_MASK)) ||
+ (RREG32(mmSRBM_STATUS2) & SRBM_STATUS2__VCE_BUSY_MASK);
+
+ return !busy;
+}
+
+static int vce_v1_0_wait_for_idle(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ unsigned int i;
+
+ for (i = 0; i < adev->usec_timeout; i++) {
+ udelay(1);
+ if (vce_v1_0_is_idle(ip_block))
+ return 0;
+ }
+ return -ETIMEDOUT;
+}
+
+/**
+ * vce_v1_0_start - start VCE block
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Setup and start the VCE block
+ */
+static int vce_v1_0_start(struct amdgpu_device *adev)
+{
+ struct amdgpu_ring *ring;
+ int r;
+
+ WREG32_P(mmVCE_STATUS, 1, ~1);
+
+ r = vce_v1_0_mc_resume(adev);
+ if (r)
+ return r;
+
+ ring = &adev->vce.ring[0];
+ WREG32(mmVCE_RB_RPTR, lower_32_bits(ring->wptr));
+ WREG32(mmVCE_RB_WPTR, lower_32_bits(ring->wptr));
+ WREG32(mmVCE_RB_BASE_LO, lower_32_bits(ring->gpu_addr));
+ WREG32(mmVCE_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
+ WREG32(mmVCE_RB_SIZE, ring->ring_size / 4);
+
+ ring = &adev->vce.ring[1];
+ WREG32(mmVCE_RB_RPTR2, lower_32_bits(ring->wptr));
+ WREG32(mmVCE_RB_WPTR2, lower_32_bits(ring->wptr));
+ WREG32(mmVCE_RB_BASE_LO2, lower_32_bits(ring->gpu_addr));
+ WREG32(mmVCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
+ WREG32(mmVCE_RB_SIZE2, ring->ring_size / 4);
+
+ WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK,
+ ~VCE_VCPU_CNTL__CLK_EN_MASK);
+
+ WREG32_P(mmVCE_SOFT_RESET,
+ VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK |
+ VCE_SOFT_RESET__FME_SOFT_RESET_MASK,
+ ~(VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK |
+ VCE_SOFT_RESET__FME_SOFT_RESET_MASK));
+
+ mdelay(100);
+
+ WREG32_P(mmVCE_SOFT_RESET, 0,
+ ~(VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK |
+ VCE_SOFT_RESET__FME_SOFT_RESET_MASK));
+
+ r = vce_v1_0_firmware_loaded(adev);
+
+ /* Clear VCE_STATUS, otherwise SRBM thinks VCE1 is busy. */
+ WREG32(mmVCE_STATUS, 0);
+
+ if (r) {
+ dev_err(adev->dev, "VCE not responding, giving up\n");
+ return r;
+ }
+
+ return 0;
+}
+
+static int vce_v1_0_stop(struct amdgpu_device *adev)
+{
+ struct amdgpu_ip_block *ip_block;
+ int status;
+ int i;
+
+ ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_VCE);
+ if (!ip_block)
+ return -EINVAL;
+
+ if (vce_v1_0_lmi_clean(adev))
+ dev_warn(adev->dev, "VCE not idle\n");
+
+ if (vce_v1_0_wait_for_idle(ip_block))
+ dev_warn(adev->dev, "VCE busy: VCE_STATUS=0x%x, SRBM_STATUS2=0x%x\n",
+ RREG32(mmVCE_STATUS), RREG32(mmSRBM_STATUS2));
+
+ /* Stall UMC and register bus before resetting VCPU */
+ WREG32_P(mmVCE_LMI_CTRL2, 1 << 8, ~(1 << 8));
+
+ for (i = 0; i < 100; ++i) {
+ status = RREG32(mmVCE_LMI_STATUS);
+ if (status & 0x240)
+ break;
+ mdelay(1);
+ }
+
+ WREG32_P(mmVCE_VCPU_CNTL, 0, ~VCE_VCPU_CNTL__CLK_EN_MASK);
+
+ WREG32_P(mmVCE_SOFT_RESET,
+ VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK |
+ VCE_SOFT_RESET__FME_SOFT_RESET_MASK,
+ ~(VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK |
+ VCE_SOFT_RESET__FME_SOFT_RESET_MASK));
+
+ WREG32(mmVCE_STATUS, 0);
+
+ return 0;
+}
+
+static void vce_v1_0_enable_mgcg(struct amdgpu_device *adev, bool enable)
+{
+ u32 tmp;
+
+ if (enable && (adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)) {
+ tmp = RREG32(mmVCE_CLOCK_GATING_A);
+ tmp |= VCE_CLOCK_GATING_A__CGC_DYN_CLOCK_MODE_MASK;
+ WREG32(mmVCE_CLOCK_GATING_A, tmp);
+
+ tmp = RREG32(mmVCE_UENC_CLOCK_GATING);
+ tmp &= ~0x1ff000;
+ tmp |= 0xff800000;
+ WREG32(mmVCE_UENC_CLOCK_GATING, tmp);
+
+ tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
+ tmp &= ~0x3ff;
+ WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp);
+ } else {
+ tmp = RREG32(mmVCE_CLOCK_GATING_A);
+ tmp &= ~VCE_CLOCK_GATING_A__CGC_DYN_CLOCK_MODE_MASK;
+ WREG32(mmVCE_CLOCK_GATING_A, tmp);
+
+ tmp = RREG32(mmVCE_UENC_CLOCK_GATING);
+ tmp |= 0x1ff000;
+ tmp &= ~0xff800000;
+ WREG32(mmVCE_UENC_CLOCK_GATING, tmp);
+
+ tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
+ tmp |= 0x3ff;
+ WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp);
+ }
+}
+
+static int vce_v1_0_early_init(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ int r;
+
+ r = amdgpu_vce_early_init(adev);
+ if (r)
+ return r;
+
+ adev->vce.num_rings = 2;
+
+ vce_v1_0_set_ring_funcs(adev);
+ vce_v1_0_set_irq_funcs(adev);
+
+ return 0;
+}
+
+/**
+ * vce_v1_0_ensure_vcpu_bo_32bit_addr() - ensure the VCPU BO has a 32-bit address
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Due to various hardware limitations, the VCE1 requires
+ * the VCPU BO to be in the low 32 bit address range.
+ * Ensure that the VCPU BO has a 32-bit GPU address,
+ * or return an error code when that isn't possible.
+ *
+ * To accomodate that, we put GART to the LOW address range
+ * and reserve some GART pages where we map the VCPU BO,
+ * so that it gets a 32-bit address.
+ */
+static int vce_v1_0_ensure_vcpu_bo_32bit_addr(struct amdgpu_device *adev)
+{
+ u64 gpu_addr = amdgpu_bo_gpu_offset(adev->vce.vcpu_bo);
+ u64 bo_size = amdgpu_bo_size(adev->vce.vcpu_bo);
+ u64 max_vcpu_bo_addr = 0xffffffff - bo_size;
+ u64 num_pages = ALIGN(bo_size, AMDGPU_GPU_PAGE_SIZE) / AMDGPU_GPU_PAGE_SIZE;
+ u64 pa = amdgpu_gmc_vram_pa(adev, adev->vce.vcpu_bo);
+ u64 flags = AMDGPU_PTE_READABLE | AMDGPU_PTE_WRITEABLE | AMDGPU_PTE_VALID;
+
+ /*
+ * Check if the VCPU BO already has a 32-bit address.
+ * Eg. if MC is configured to put VRAM in the low address range.
+ */
+ if (gpu_addr <= max_vcpu_bo_addr)
+ return 0;
+
+ /* Check if we can map the VCPU BO in GART to a 32-bit address. */
+ if (adev->gmc.gart_start + VCE_V1_0_GART_ADDR_START > max_vcpu_bo_addr)
+ return -EINVAL;
+
+ amdgpu_gart_map_vram_range(adev, pa, VCE_V1_0_GART_PAGE_START,
+ num_pages, flags, adev->gart.ptr);
+ adev->vce.gpu_addr = adev->gmc.gart_start + VCE_V1_0_GART_ADDR_START;
+ if (adev->vce.gpu_addr > max_vcpu_bo_addr)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int vce_v1_0_sw_init(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ struct amdgpu_ring *ring;
+ int r, i;
+
+ r = amdgpu_irq_add_id(adev, AMDGPU_IRQ_CLIENTID_LEGACY, 167, &adev->vce.irq);
+ if (r)
+ return r;
+
+ r = amdgpu_vce_sw_init(adev, VCE_V1_0_FW_SIZE +
+ VCE_V1_0_STACK_SIZE + VCE_V1_0_DATA_SIZE);
+ if (r)
+ return r;
+
+ r = amdgpu_vce_resume(adev);
+ if (r)
+ return r;
+ r = vce_v1_0_load_fw_signature(adev);
+ if (r)
+ return r;
+ r = vce_v1_0_ensure_vcpu_bo_32bit_addr(adev);
+ if (r)
+ return r;
+
+ for (i = 0; i < adev->vce.num_rings; i++) {
+ enum amdgpu_ring_priority_level hw_prio = amdgpu_vce_get_ring_prio(i);
+
+ ring = &adev->vce.ring[i];
+ sprintf(ring->name, "vce%d", i);
+ r = amdgpu_ring_init(adev, ring, 512, &adev->vce.irq, 0,
+ hw_prio, NULL);
+ if (r)
+ return r;
+ }
+
+ return r;
+}
+
+static int vce_v1_0_sw_fini(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ int r;
+
+ r = amdgpu_vce_suspend(adev);
+ if (r)
+ return r;
+
+ return amdgpu_vce_sw_fini(adev);
+}
+
+/**
+ * vce_v1_0_hw_init - start and test VCE block
+ *
+ * @ip_block: Pointer to the amdgpu_ip_block for this hw instance.
+ *
+ * Initialize the hardware, boot up the VCPU and do some testing
+ */
+static int vce_v1_0_hw_init(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ int i, r;
+
+ if (adev->pm.dpm_enabled)
+ amdgpu_dpm_enable_vce(adev, true);
+ else
+ amdgpu_asic_set_vce_clocks(adev, 10000, 10000);
+
+ for (i = 0; i < adev->vce.num_rings; i++) {
+ r = amdgpu_ring_test_helper(&adev->vce.ring[i]);
+ if (r)
+ return r;
+ }
+
+ dev_info(adev->dev, "VCE initialized successfully.\n");
+
+ return 0;
+}
+
+static int vce_v1_0_hw_fini(struct amdgpu_ip_block *ip_block)
+{
+ int r;
+
+ r = vce_v1_0_stop(ip_block->adev);
+ if (r)
+ return r;
+
+ cancel_delayed_work_sync(&ip_block->adev->vce.idle_work);
+ return 0;
+}
+
+static int vce_v1_0_suspend(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ int r;
+
+ /*
+ * Proper cleanups before halting the HW engine:
+ * - cancel the delayed idle work
+ * - enable powergating
+ * - enable clockgating
+ * - disable dpm
+ *
+ * TODO: to align with the VCN implementation, move the
+ * jobs for clockgating/powergating/dpm setting to
+ * ->set_powergating_state().
+ */
+ cancel_delayed_work_sync(&adev->vce.idle_work);
+
+ if (adev->pm.dpm_enabled) {
+ amdgpu_dpm_enable_vce(adev, false);
+ } else {
+ amdgpu_asic_set_vce_clocks(adev, 0, 0);
+ amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+ AMD_PG_STATE_GATE);
+ amdgpu_device_ip_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+ AMD_CG_STATE_GATE);
+ }
+
+ r = vce_v1_0_hw_fini(ip_block);
+ if (r) {
+ dev_err(adev->dev, "vce_v1_0_hw_fini() failed with error %i", r);
+ return r;
+ }
+
+ return amdgpu_vce_suspend(adev);
+}
+
+static int vce_v1_0_resume(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ int r;
+
+ r = amdgpu_vce_resume(adev);
+ if (r)
+ return r;
+ r = vce_v1_0_load_fw_signature(adev);
+ if (r)
+ return r;
+ r = vce_v1_0_ensure_vcpu_bo_32bit_addr(adev);
+ if (r)
+ return r;
+
+ return vce_v1_0_hw_init(ip_block);
+}
+
+static int vce_v1_0_set_interrupt_state(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ unsigned int type,
+ enum amdgpu_interrupt_state state)
+{
+ uint32_t val = 0;
+
+ if (state == AMDGPU_IRQ_STATE_ENABLE)
+ val |= VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK;
+
+ WREG32_P(mmVCE_SYS_INT_EN, val,
+ ~VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK);
+ return 0;
+}
+
+static int vce_v1_0_process_interrupt(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry)
+{
+ dev_dbg(adev->dev, "IH: VCE\n");
+ switch (entry->src_data[0]) {
+ case 0:
+ case 1:
+ amdgpu_fence_process(&adev->vce.ring[entry->src_data[0]]);
+ break;
+ default:
+ dev_err(adev->dev, "Unhandled interrupt: %d %d\n",
+ entry->src_id, entry->src_data[0]);
+ break;
+ }
+
+ return 0;
+}
+
+static int vce_v1_0_set_clockgating_state(struct amdgpu_ip_block *ip_block,
+ enum amd_clockgating_state state)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+
+ vce_v1_0_init_cg(adev);
+ vce_v1_0_enable_mgcg(adev, state == AMD_CG_STATE_GATE);
+
+ return 0;
+}
+
+static int vce_v1_0_set_powergating_state(struct amdgpu_ip_block *ip_block,
+ enum amd_powergating_state state)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+
+ /*
+ * This doesn't actually powergate the VCE block.
+ * That's done in the dpm code via the SMC. This
+ * just re-inits the block as necessary. The actual
+ * gating still happens in the dpm code. We should
+ * revisit this when there is a cleaner line between
+ * the smc and the hw blocks
+ */
+ if (state == AMD_PG_STATE_GATE)
+ return vce_v1_0_stop(adev);
+ else
+ return vce_v1_0_start(adev);
+}
+
+static const struct amd_ip_funcs vce_v1_0_ip_funcs = {
+ .name = "vce_v1_0",
+ .early_init = vce_v1_0_early_init,
+ .sw_init = vce_v1_0_sw_init,
+ .sw_fini = vce_v1_0_sw_fini,
+ .hw_init = vce_v1_0_hw_init,
+ .hw_fini = vce_v1_0_hw_fini,
+ .suspend = vce_v1_0_suspend,
+ .resume = vce_v1_0_resume,
+ .is_idle = vce_v1_0_is_idle,
+ .wait_for_idle = vce_v1_0_wait_for_idle,
+ .set_clockgating_state = vce_v1_0_set_clockgating_state,
+ .set_powergating_state = vce_v1_0_set_powergating_state,
+};
+
+static const struct amdgpu_ring_funcs vce_v1_0_ring_funcs = {
+ .type = AMDGPU_RING_TYPE_VCE,
+ .align_mask = 0xf,
+ .nop = VCE_CMD_NO_OP,
+ .support_64bit_ptrs = false,
+ .no_user_fence = true,
+ .get_rptr = vce_v1_0_ring_get_rptr,
+ .get_wptr = vce_v1_0_ring_get_wptr,
+ .set_wptr = vce_v1_0_ring_set_wptr,
+ .parse_cs = amdgpu_vce_ring_parse_cs,
+ .emit_frame_size = 6, /* amdgpu_vce_ring_emit_fence x1 no user fence */
+ .emit_ib_size = 4, /* amdgpu_vce_ring_emit_ib */
+ .emit_ib = amdgpu_vce_ring_emit_ib,
+ .emit_fence = amdgpu_vce_ring_emit_fence,
+ .test_ring = amdgpu_vce_ring_test_ring,
+ .test_ib = amdgpu_vce_ring_test_ib,
+ .insert_nop = amdgpu_ring_insert_nop,
+ .pad_ib = amdgpu_ring_generic_pad_ib,
+ .begin_use = amdgpu_vce_ring_begin_use,
+ .end_use = amdgpu_vce_ring_end_use,
+};
+
+static void vce_v1_0_set_ring_funcs(struct amdgpu_device *adev)
+{
+ int i;
+
+ for (i = 0; i < adev->vce.num_rings; i++) {
+ adev->vce.ring[i].funcs = &vce_v1_0_ring_funcs;
+ adev->vce.ring[i].me = i;
+ }
+};
+
+static const struct amdgpu_irq_src_funcs vce_v1_0_irq_funcs = {
+ .set = vce_v1_0_set_interrupt_state,
+ .process = vce_v1_0_process_interrupt,
+};
+
+static void vce_v1_0_set_irq_funcs(struct amdgpu_device *adev)
+{
+ adev->vce.irq.num_types = 1;
+ adev->vce.irq.funcs = &vce_v1_0_irq_funcs;
+};
+
+const struct amdgpu_ip_block_version vce_v1_0_ip_block = {
+ .type = AMD_IP_BLOCK_TYPE_VCE,
+ .major = 1,
+ .minor = 0,
+ .rev = 0,
+ .funcs = &vce_v1_0_ip_funcs,
+};
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v1_0.h b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.h
new file mode 100644
index 000000000000..206e7bec897f
--- /dev/null
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v1_0.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ * Copyright 2025 Valve Corporation
+ * Copyright 2025 Alexandre Demers
+ *
+ * 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.
+ *
+ */
+
+#ifndef __VCE_V1_0_H__
+#define __VCE_V1_0_H__
+
+extern const struct amdgpu_ip_block_version vce_v1_0_ip_block;
+
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c
index bee3e904a6bc..8ea8a6193492 100644
--- a/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v2_0.c
@@ -407,6 +407,11 @@ static void vce_v2_0_enable_mgcg(struct amdgpu_device *adev, bool enable,
static int vce_v2_0_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
+ int r;
+
+ r = amdgpu_vce_early_init(adev);
+ if (r)
+ return r;
adev->vce.num_rings = 2;
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
index 708123899c41..719e9643c43d 100644
--- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
@@ -399,6 +399,7 @@ static unsigned vce_v3_0_get_harvest_config(struct amdgpu_device *adev)
static int vce_v3_0_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
+ int r;
adev->vce.harvest_config = vce_v3_0_get_harvest_config(adev);
@@ -407,6 +408,10 @@ static int vce_v3_0_early_init(struct amdgpu_ip_block *ip_block)
(AMDGPU_VCE_HARVEST_VCE0 | AMDGPU_VCE_HARVEST_VCE1))
return -ENOENT;
+ r = amdgpu_vce_early_init(adev);
+ if (r)
+ return r;
+
adev->vce.num_rings = 3;
vce_v3_0_set_ring_funcs(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c
index 335bda64ff5b..2d64002bed61 100644
--- a/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/vce_v4_0.c
@@ -410,6 +410,11 @@ static int vce_v4_0_stop(struct amdgpu_device *adev)
static int vce_v4_0_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
+ int r;
+
+ r = amdgpu_vce_early_init(adev);
+ if (r)
+ return r;
if (amdgpu_sriov_vf(adev)) /* currently only VCN0 support SRIOV */
adev->vce.num_rings = 1;
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
index eacf4e93ba2f..cb7123ec1a5d 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c
@@ -141,7 +141,7 @@ static int vcn_v4_0_3_late_init(struct amdgpu_ip_block *ip_block)
adev->vcn.supported_reset =
amdgpu_get_soft_full_reset_mask(&adev->vcn.inst[0].ring_enc[0]);
- if (amdgpu_dpm_reset_vcn_is_supported(adev))
+ if (amdgpu_dpm_reset_vcn_is_supported(adev) && !amdgpu_sriov_vf(adev))
adev->vcn.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
index 714350cabf2f..8bd457dea4cf 100644
--- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c
@@ -122,7 +122,9 @@ static int vcn_v5_0_1_late_init(struct amdgpu_ip_block *ip_block)
switch (amdgpu_ip_version(adev, MP0_HWIP, 0)) {
case IP_VERSION(13, 0, 12):
- if ((adev->psp.sos.fw_version >= 0x00450025) && amdgpu_dpm_reset_vcn_is_supported(adev))
+ if ((adev->psp.sos.fw_version >= 0x00450025) &&
+ amdgpu_dpm_reset_vcn_is_supported(adev) &&
+ !amdgpu_sriov_vf(adev))
adev->vcn.supported_reset |= AMDGPU_RESET_TYPE_PER_QUEUE;
break;
default:
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 0f0719528bcc..22925df6a791 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -2826,7 +2826,7 @@ retry:
static int runtime_disable(struct kfd_process *p)
{
- int i = 0, ret;
+ int i = 0, ret = 0;
bool was_enabled = p->runtime_info.runtime_state == DEBUG_RUNTIME_STATE_ENABLED;
p->runtime_info.runtime_state = DEBUG_RUNTIME_STATE_DISABLED;
@@ -2863,6 +2863,7 @@ static int runtime_disable(struct kfd_process *p)
/* disable ttmp setup */
for (i = 0; i < p->n_pdds; i++) {
struct kfd_process_device *pdd = p->pdds[i];
+ int last_err = 0;
if (kfd_dbg_is_per_vmid_supported(pdd->dev)) {
pdd->spi_dbg_override =
@@ -2872,14 +2873,17 @@ static int runtime_disable(struct kfd_process *p)
pdd->dev->vm_info.last_vmid_kfd);
if (!pdd->dev->kfd->shared_resources.enable_mes)
- debug_refresh_runlist(pdd->dev->dqm);
+ last_err = debug_refresh_runlist(pdd->dev->dqm);
else
- kfd_dbg_set_mes_debug_mode(pdd,
+ last_err = kfd_dbg_set_mes_debug_mode(pdd,
!kfd_dbg_has_cwsr_workaround(pdd->dev));
+
+ if (last_err)
+ ret = last_err;
}
}
- return 0;
+ return ret;
}
static int kfd_ioctl_runtime_enable(struct file *filep, struct kfd_process *p, void *data)
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 6e7bc983fc0b..d7a2e7178ea9 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -1897,6 +1897,8 @@ fail_packet_manager_init:
static int stop_cpsch(struct device_queue_manager *dqm)
{
+ int ret = 0;
+
dqm_lock(dqm);
if (!dqm->sched_running) {
dqm_unlock(dqm);
@@ -1904,9 +1906,10 @@ static int stop_cpsch(struct device_queue_manager *dqm)
}
if (!dqm->dev->kfd->shared_resources.enable_mes)
- unmap_queues_cpsch(dqm, KFD_UNMAP_QUEUES_FILTER_ALL_QUEUES, 0, USE_DEFAULT_GRACE_PERIOD, false);
+ ret = unmap_queues_cpsch(dqm, KFD_UNMAP_QUEUES_FILTER_ALL_QUEUES,
+ 0, USE_DEFAULT_GRACE_PERIOD, false);
else
- remove_all_kfd_queues_mes(dqm);
+ ret = remove_all_kfd_queues_mes(dqm);
dqm->sched_running = false;
@@ -1920,7 +1923,7 @@ static int stop_cpsch(struct device_queue_manager *dqm)
dqm->detect_hang_info = NULL;
dqm_unlock(dqm);
- return 0;
+ return ret;
}
static int create_kernel_queue_cpsch(struct device_queue_manager *dqm,
@@ -2091,7 +2094,8 @@ int amdkfd_fence_wait_timeout(struct device_queue_manager *dqm,
while (*fence_addr != fence_value) {
/* Fatal err detected, this response won't come */
- if (amdgpu_amdkfd_is_fed(dqm->dev->adev))
+ if (amdgpu_amdkfd_is_fed(dqm->dev->adev) ||
+ amdgpu_in_reset(dqm->dev->adev))
return -EIO;
if (time_after(jiffies, end_jiffies)) {
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index 82905f3e54dd..5a190dd6be4e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -748,16 +748,6 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
uint64_t *slots = page_slots(p->signal_page);
uint32_t id;
- /*
- * If id is valid but slot is not signaled, GPU may signal the same event twice
- * before driver have chance to process the first interrupt, then signal slot is
- * auto-reset after set_event wakeup the user space, just drop the second event as
- * the application only need wakeup once.
- */
- if ((valid_id_bits > 31 || (1U << valid_id_bits) >= KFD_SIGNAL_EVENT_LIMIT) &&
- partial_id < KFD_SIGNAL_EVENT_LIMIT && slots[partial_id] == UNSIGNALED_EVENT_SLOT)
- goto out_unlock;
-
if (valid_id_bits)
pr_debug_ratelimited("Partial ID invalid: %u (%u valid bits)\n",
partial_id, valid_id_bits);
@@ -786,7 +776,6 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id,
}
}
-out_unlock:
rcu_read_unlock();
kfd_unref_process(p);
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c
index 4ceb251312a6..d76fb61869c7 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c
@@ -28,6 +28,7 @@
#include "kfd_device_queue_manager.h"
#include "kfd_smi_events.h"
#include "amdgpu_ras.h"
+#include "amdgpu_ras_mgr.h"
/*
* GFX9 SQ Interrupts
@@ -228,7 +229,11 @@ static void event_interrupt_poison_consumption_v9(struct kfd_node *dev,
kfd_signal_poison_consumed_event(dev, pasid);
- event_id = amdgpu_ras_acquire_event_id(dev->adev, type);
+ if (amdgpu_uniras_enabled(dev->adev))
+ event_id = amdgpu_ras_mgr_gen_ras_event_seqno(dev->adev,
+ RAS_SEQNO_TYPE_POISON_CONSUMPTION);
+ else
+ event_id = amdgpu_ras_acquire_event_id(dev->adev, type);
RAS_EVENT_LOG(dev->adev, event_id,
"poison is consumed by client %d, kick off gpu reset flow\n", client_id);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
index 59a5a3fea65d..46c84fc60af1 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
@@ -21,7 +21,6 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <linux/types.h>
-#include <linux/hmm.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/migrate.h>
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.h b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.h
index 2eebf67f9c2c..2b7fd442d29c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.h
@@ -31,7 +31,6 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/sched/mm.h>
-#include <linux/hmm.h>
#include "kfd_priv.h"
#include "kfd_svm.h"
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index ddfe30c13e9d..a085faac9fe1 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -1083,7 +1083,6 @@ static void kfd_process_destroy_pdds(struct kfd_process *p)
* for auto suspend
*/
if (pdd->runtime_inuse) {
- pm_runtime_mark_last_busy(adev_to_drm(pdd->dev->adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(pdd->dev->adev)->dev);
pdd->runtime_inuse = false;
}
@@ -1162,9 +1161,6 @@ static void kfd_process_wq_release(struct work_struct *work)
release_work);
struct dma_fence *ef;
- kfd_process_dequeue_from_all_devices(p);
- pqm_uninit(&p->pqm);
-
/*
* If GPU in reset, user queues may still running, wait for reset complete.
*/
@@ -1226,6 +1222,14 @@ static void kfd_process_notifier_release_internal(struct kfd_process *p)
cancel_delayed_work_sync(&p->eviction_work);
cancel_delayed_work_sync(&p->restore_work);
+ /*
+ * Dequeue and destroy user queues, it is not safe for GPU to access
+ * system memory after mmu release notifier callback returns because
+ * exit_mmap free process memory afterwards.
+ */
+ kfd_process_dequeue_from_all_devices(p);
+ pqm_uninit(&p->pqm);
+
for (i = 0; i < p->n_pdds; i++) {
struct kfd_process_device *pdd = p->pdds[i];
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
index a65c67cf56ff..f1e7583650c4 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
@@ -297,16 +297,16 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
goto out_err_unreserve;
}
- if (properties->ctx_save_restore_area_size != topo_dev->node_props.cwsr_size) {
- pr_debug("queue cwsr size 0x%x not equal to node cwsr size 0x%x\n",
+ if (properties->ctx_save_restore_area_size < topo_dev->node_props.cwsr_size) {
+ pr_debug("queue cwsr size 0x%x not sufficient for node cwsr size 0x%x\n",
properties->ctx_save_restore_area_size,
topo_dev->node_props.cwsr_size);
err = -EINVAL;
goto out_err_unreserve;
}
- total_cwsr_size = (topo_dev->node_props.cwsr_size + topo_dev->node_props.debug_memory_size)
- * NUM_XCC(pdd->dev->xcc_mask);
+ total_cwsr_size = (properties->ctx_save_restore_area_size +
+ topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
err = kfd_queue_buffer_get(vm, (void *)properties->ctx_save_restore_area_address,
@@ -352,8 +352,8 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
if (!topo_dev)
return -EINVAL;
- total_cwsr_size = (topo_dev->node_props.cwsr_size + topo_dev->node_props.debug_memory_size)
- * NUM_XCC(pdd->dev->xcc_mask);
+ total_cwsr_size = (properties->ctx_save_restore_area_size +
+ topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
kfd_queue_buffer_svm_put(pdd, properties->ctx_save_restore_area_address, total_cwsr_size);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 9d72411c3379..97c2270f278f 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -1698,7 +1698,7 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
start = map_start << PAGE_SHIFT;
end = (map_last + 1) << PAGE_SHIFT;
for (addr = start; !r && addr < end; ) {
- struct hmm_range *hmm_range = NULL;
+ struct amdgpu_hmm_range *range = NULL;
unsigned long map_start_vma;
unsigned long map_last_vma;
struct vm_area_struct *vma;
@@ -1737,9 +1737,12 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
}
WRITE_ONCE(p->svms.faulting_task, current);
- r = amdgpu_hmm_range_get_pages(&prange->notifier, addr, npages,
- readonly, owner,
- &hmm_range);
+ range = amdgpu_hmm_range_alloc(NULL);
+ if (likely(range))
+ r = amdgpu_hmm_range_get_pages(&prange->notifier, addr, npages,
+ readonly, owner, range);
+ else
+ r = -ENOMEM;
WRITE_ONCE(p->svms.faulting_task, NULL);
if (r)
pr_debug("failed %d to get svm range pages\n", r);
@@ -1750,7 +1753,7 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
if (!r) {
offset = (addr >> PAGE_SHIFT) - prange->start;
r = svm_range_dma_map(prange, ctx->bitmap, offset, npages,
- hmm_range->hmm_pfns);
+ range->hmm_range.hmm_pfns);
if (r)
pr_debug("failed %d to dma map range\n", r);
}
@@ -1758,14 +1761,17 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
svm_range_lock(prange);
/* Free backing memory of hmm_range if it was initialized
- * Overrride return value to TRY AGAIN only if prior returns
+ * Override return value to TRY AGAIN only if prior returns
* were successful
*/
- if (hmm_range && amdgpu_hmm_range_get_pages_done(hmm_range) && !r) {
+ if (range && !amdgpu_hmm_range_valid(range) && !r) {
pr_debug("hmm update the range, need validate again\n");
r = -EAGAIN;
}
+ /* Free the hmm range */
+ amdgpu_hmm_range_free(range);
+
if (!r && !list_empty(&prange->child_list)) {
pr_debug("range split by unmap in parallel, validate again\n");
r = -EAGAIN;
@@ -3687,6 +3693,8 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
svm_range_apply_attrs(p, prange, nattr, attrs, &update_mapping);
/* TODO: unmap ranges from GPU that lost access */
}
+ update_mapping |= !p->xnack_enabled && !list_empty(&remap_list);
+
list_for_each_entry_safe(prange, next, &remove_list, update_list) {
pr_debug("unlink old 0x%p prange 0x%p [0x%lx 0x%lx]\n",
prange->svms, prange, prange->start,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h
index 01c7a4877904..a63dfc95b602 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.h
@@ -31,7 +31,6 @@
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/sched/mm.h>
-#include <linux/hmm.h>
#include "amdgpu.h"
#include "kfd_priv.h"
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
index 5c98746eb72d..811636af14ea 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
@@ -530,7 +530,9 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr,
sysfs_show_32bit_prop(buffer, offs, "sdma_fw_version",
dev->gpu->kfd->sdma_fw_version);
sysfs_show_64bit_prop(buffer, offs, "unique_id",
- dev->gpu->xcp ?
+ dev->gpu->xcp &&
+ (dev->gpu->xcp->xcp_mgr->mode !=
+ AMDGPU_SPX_PARTITION_MODE) ?
dev->gpu->xcp->unique_id :
dev->gpu->adev->unique_id);
sysfs_show_32bit_prop(buffer, offs, "num_xcc",
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
index 7329b8cc2576..8e949fe77312 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
@@ -39,7 +39,8 @@ AMDGPUDM = \
amdgpu_dm_psr.o \
amdgpu_dm_replay.o \
amdgpu_dm_quirks.o \
- amdgpu_dm_wb.o
+ amdgpu_dm_wb.o \
+ amdgpu_dm_colorop.o
ifdef CONFIG_DRM_AMD_DC_FP
AMDGPUDM += dc_fpu.o
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 6597475e245d..740711ac1037 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -551,13 +551,13 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
struct dc_stream_state *stream,
struct dc_crtc_timing_adjust *adjust)
{
- struct vupdate_offload_work *offload_work = kzalloc(sizeof(*offload_work), GFP_KERNEL);
+ struct vupdate_offload_work *offload_work = kzalloc(sizeof(*offload_work), GFP_NOWAIT);
if (!offload_work) {
drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n");
return;
}
- struct dc_crtc_timing_adjust *adjust_copy = kzalloc(sizeof(*adjust_copy), GFP_KERNEL);
+ struct dc_crtc_timing_adjust *adjust_copy = kzalloc(sizeof(*adjust_copy), GFP_NOWAIT);
if (!adjust_copy) {
drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n");
kfree(offload_work);
@@ -3392,6 +3392,67 @@ static void apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
}
}
+/**
+ * amdgpu_dm_dump_links_and_sinks - Debug dump of all DC links and their sinks
+ * @adev: amdgpu device pointer
+ *
+ * Iterates through all DC links and dumps information about local and remote
+ * (MST) sinks. Should be called after connector detection is complete to see
+ * the final state of all links.
+ */
+static void amdgpu_dm_dump_links_and_sinks(struct amdgpu_device *adev)
+{
+ struct dc *dc = adev->dm.dc;
+ struct drm_device *dev = adev_to_drm(adev);
+ int li;
+
+ if (!dc)
+ return;
+
+ for (li = 0; li < dc->link_count; li++) {
+ struct dc_link *l = dc->links[li];
+ const char *name = NULL;
+ int rs;
+
+ if (!l)
+ continue;
+ if (l->local_sink && l->local_sink->edid_caps.display_name[0])
+ name = l->local_sink->edid_caps.display_name;
+ else
+ name = "n/a";
+
+ drm_dbg_kms(dev,
+ "LINK_DUMP[%d]: local_sink=%p type=%d sink_signal=%d sink_count=%u edid_name=%s mst_capable=%d mst_alloc_streams=%d\n",
+ li,
+ l->local_sink,
+ l->type,
+ l->local_sink ? l->local_sink->sink_signal : SIGNAL_TYPE_NONE,
+ l->sink_count,
+ name,
+ l->dpcd_caps.is_mst_capable,
+ l->mst_stream_alloc_table.stream_count);
+
+ /* Dump remote (MST) sinks if any */
+ for (rs = 0; rs < l->sink_count; rs++) {
+ struct dc_sink *rsink = l->remote_sinks[rs];
+ const char *rname = NULL;
+
+ if (!rsink)
+ continue;
+ if (rsink->edid_caps.display_name[0])
+ rname = rsink->edid_caps.display_name;
+ else
+ rname = "n/a";
+ drm_dbg_kms(dev,
+ " REMOTE_SINK[%d:%d]: sink=%p signal=%d edid_name=%s\n",
+ li, rs,
+ rsink,
+ rsink->sink_signal,
+ rname);
+ }
+ }
+}
+
static int dm_resume(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -3563,6 +3624,7 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
/* Do mst topology probing after resuming cached state*/
drm_connector_list_iter_begin(ddev, &iter);
drm_for_each_connector_iter(connector, &iter) {
+ bool init = false;
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
continue;
@@ -3572,10 +3634,23 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
aconnector->mst_root)
continue;
- drm_dp_mst_topology_queue_probe(&aconnector->mst_mgr);
+ scoped_guard(mutex, &aconnector->mst_mgr.lock) {
+ init = !aconnector->mst_mgr.mst_primary;
+ }
+ if (init)
+ dm_helpers_dp_mst_start_top_mgr(aconnector->dc_link->ctx,
+ aconnector->dc_link, false);
+ else
+ drm_dp_mst_topology_queue_probe(&aconnector->mst_mgr);
}
drm_connector_list_iter_end(&iter);
+ /* Debug dump: list all DC links and their associated sinks after detection
+ * is complete for all connectors. This provides a comprehensive view of the
+ * final state without repeating the dump for each connector.
+ */
+ amdgpu_dm_dump_links_and_sinks(adev);
+
amdgpu_dm_irq_resume_late(adev);
amdgpu_dm_smu_write_watermarks_table(adev);
@@ -3786,7 +3861,9 @@ void amdgpu_dm_update_connector_after_detect(
drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
aconnector->connector_id, aconnector->dc_sink, sink);
- guard(mutex)(&dev->mode_config.mutex);
+ /* When polling, DRM has already locked the mutex for us. */
+ if (!drm_kms_helper_is_poll_worker())
+ mutex_lock(&dev->mode_config.mutex);
/*
* 1. Update status of the drm connector
@@ -3849,6 +3926,101 @@ void amdgpu_dm_update_connector_after_detect(
}
update_subconnector_property(aconnector);
+
+ /* When polling, the mutex will be unlocked for us by DRM. */
+ if (!drm_kms_helper_is_poll_worker())
+ mutex_unlock(&dev->mode_config.mutex);
+}
+
+static bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2)
+{
+ if (!sink1 || !sink2)
+ return false;
+ if (sink1->sink_signal != sink2->sink_signal)
+ return false;
+
+ if (sink1->dc_edid.length != sink2->dc_edid.length)
+ return false;
+
+ if (memcmp(sink1->dc_edid.raw_edid, sink2->dc_edid.raw_edid,
+ sink1->dc_edid.length) != 0)
+ return false;
+ return true;
+}
+
+
+/**
+ * DOC: hdmi_hpd_debounce_work
+ *
+ * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD
+ * (such as during power save transitions), this delay determines how long to
+ * wait before processing the HPD event. This allows distinguishing between a
+ * physical unplug (>hdmi_hpd_debounce_delay)
+ * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay).
+ *
+ * If the toggle is less than this delay, the driver compares sink capabilities
+ * and permits a hotplug event if they changed.
+ *
+ * The default value of 1500ms was chosen based on experimental testing with
+ * various monitors that exhibit spontaneous HPD toggling behavior.
+ */
+static void hdmi_hpd_debounce_work(struct work_struct *work)
+{
+ struct amdgpu_dm_connector *aconnector =
+ container_of(to_delayed_work(work), struct amdgpu_dm_connector,
+ hdmi_hpd_debounce_work);
+ struct drm_connector *connector = &aconnector->base;
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dc *dc = aconnector->dc_link->ctx->dc;
+ bool fake_reconnect = false;
+ bool reallow_idle = false;
+ bool ret = false;
+ guard(mutex)(&aconnector->hpd_lock);
+
+ /* Re-detect the display */
+ scoped_guard(mutex, &adev->dm.dc_lock) {
+ if (dc->caps.ips_support && dc->ctx->dmub_srv->idle_allowed) {
+ dc_allow_idle_optimizations(dc, false);
+ reallow_idle = true;
+ }
+ ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
+ }
+
+ if (ret) {
+ /* Apply workaround delay for certain panels */
+ apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
+ /* Compare sinks to determine if this was a spontaneous HPD toggle */
+ if (are_sinks_equal(aconnector->dc_link->local_sink, aconnector->hdmi_prev_sink)) {
+ /*
+ * Sinks match - this was a spontaneous HDMI HPD toggle.
+ */
+ drm_dbg_kms(dev, "HDMI HPD: Sink unchanged after debounce, internal re-enable\n");
+ fake_reconnect = true;
+ }
+
+ /* Update connector state */
+ amdgpu_dm_update_connector_after_detect(aconnector);
+
+ drm_modeset_lock_all(dev);
+ dm_restore_drm_connector_state(dev, connector);
+ drm_modeset_unlock_all(dev);
+
+ /* Only notify OS if sink actually changed */
+ if (!fake_reconnect && aconnector->base.force == DRM_FORCE_UNSPECIFIED)
+ drm_kms_helper_hotplug_event(dev);
+ }
+
+ /* Release the cached sink reference */
+ if (aconnector->hdmi_prev_sink) {
+ dc_sink_release(aconnector->hdmi_prev_sink);
+ aconnector->hdmi_prev_sink = NULL;
+ }
+
+ scoped_guard(mutex, &adev->dm.dc_lock) {
+ if (reallow_idle && dc->caps.ips_support)
+ dc_allow_idle_optimizations(dc, true);
+ }
}
static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
@@ -3860,6 +4032,7 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
struct dc *dc = aconnector->dc_link->ctx->dc;
bool ret = false;
+ bool debounce_required = false;
if (adev->dm.disable_hpd_irq)
return;
@@ -3882,6 +4055,14 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type))
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
+ /*
+ * Check for HDMI disconnect with debounce enabled.
+ */
+ debounce_required = (aconnector->hdmi_hpd_debounce_delay_ms > 0 &&
+ dc_is_hdmi_signal(aconnector->dc_link->connector_signal) &&
+ new_connection_type == dc_connection_none &&
+ aconnector->dc_link->local_sink != NULL);
+
if (aconnector->base.force && new_connection_type == dc_connection_none) {
emulated_link_detect(aconnector->dc_link);
@@ -3891,7 +4072,34 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
drm_kms_helper_connector_hotplug_event(connector);
+ } else if (debounce_required) {
+ /*
+ * HDMI disconnect detected - schedule delayed work instead of
+ * processing immediately. This allows us to coalesce spurious
+ * HDMI signals from physical unplugs.
+ */
+ drm_dbg_kms(dev, "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n",
+ aconnector->hdmi_hpd_debounce_delay_ms);
+
+ /* Cache the current sink for later comparison */
+ if (aconnector->hdmi_prev_sink)
+ dc_sink_release(aconnector->hdmi_prev_sink);
+ aconnector->hdmi_prev_sink = aconnector->dc_link->local_sink;
+ if (aconnector->hdmi_prev_sink)
+ dc_sink_retain(aconnector->hdmi_prev_sink);
+
+ /* Schedule delayed detection. */
+ if (mod_delayed_work(system_wq,
+ &aconnector->hdmi_hpd_debounce_work,
+ msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms)))
+ drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n");
+
} else {
+
+ /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */
+ if (delayed_work_pending(&aconnector->hdmi_hpd_debounce_work))
+ return;
+
scoped_guard(mutex, &adev->dm.dc_lock) {
dc_exit_ips_for_hw_access(dc);
ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
@@ -4917,6 +5125,21 @@ static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
struct dc_link *link;
u32 brightness;
bool rc, reallow_idle = false;
+ struct drm_connector *connector;
+
+ list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+
+ if (aconnector->bl_idx != bl_idx)
+ continue;
+
+ /* if connector is off, save the brightness for next time it's on */
+ if (!aconnector->base.encoder) {
+ dm->brightness[bl_idx] = user_brightness;
+ dm->actual_brightness[bl_idx] = 0;
+ return;
+ }
+ }
amdgpu_dm_update_backlight_caps(dm, bl_idx);
caps = &dm->backlight_caps[bl_idx];
@@ -5133,6 +5356,7 @@ static int initialize_plane(struct amdgpu_display_manager *dm,
static void setup_backlight_device(struct amdgpu_display_manager *dm,
struct amdgpu_dm_connector *aconnector)
{
+ struct amdgpu_dm_backlight_caps *caps;
struct dc_link *link = aconnector->dc_link;
int bl_idx = dm->num_of_edps;
@@ -5152,6 +5376,13 @@ static void setup_backlight_device(struct amdgpu_display_manager *dm,
dm->num_of_edps++;
update_connector_ext_caps(aconnector);
+ caps = &dm->backlight_caps[aconnector->bl_idx];
+
+ /* Only offer ABM property when non-OLED and user didn't turn off by module parameter */
+ if (!caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
+ drm_object_attach_property(&aconnector->base.base,
+ dm->adev->mode_info.abm_level_property,
+ ABM_SYSFS_CONTROL);
}
static void amdgpu_set_panel_orientation(struct drm_connector *connector);
@@ -5407,6 +5638,12 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
amdgpu_set_panel_orientation(&aconnector->base);
}
+ /* Debug dump: list all DC links and their associated sinks after detection
+ * is complete for all connectors. This provides a comprehensive view of the
+ * final state without repeating the dump for each connector.
+ */
+ amdgpu_dm_dump_links_and_sinks(adev);
+
/* Software is initialized. Now we can register interrupt handlers. */
switch (adev->asic_type) {
#if defined(CONFIG_DRM_AMD_DC_SI)
@@ -5793,6 +6030,10 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state,
*color_space = COLOR_SPACE_SRGB;
+ /* Ignore properties when DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set */
+ if (plane_state->state && plane_state->state->plane_color_pipeline)
+ return 0;
+
/* DRM color properties only affect non-RGB formats. */
if (format < SURFACE_PIXEL_FORMAT_VIDEO_BEGIN)
return 0;
@@ -7145,29 +7386,117 @@ finish:
return stream;
}
+/**
+ * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display
+ * @aconnector: DM connector to poll (owns @base drm_connector and @dc_link)
+ * @force: if true, force polling even when DAC load detection was used
+ *
+ * Used for connectors that don't support HPD (hotplug detection) to
+ * periodically check whether the connector is connected to a display.
+ *
+ * When connection was determined via DAC load detection, we avoid
+ * re-running it on normal polls to prevent visible glitches, unless
+ * @force is set.
+ *
+ * Return: The probed connector status (connected/disconnected/unknown).
+ */
static enum drm_connector_status
-amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
+amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force)
{
- bool connected;
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+ struct drm_connector *connector = &aconnector->base;
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dc_link *link = aconnector->dc_link;
+ enum dc_connection_type conn_type = dc_connection_none;
+ enum drm_connector_status status = connector_status_disconnected;
- /*
- * Notes:
- * 1. This interface is NOT called in context of HPD irq.
- * 2. This interface *is called* in context of user-mode ioctl. Which
- * makes it a bad place for *any* MST-related activity.
+ /* When we determined the connection using DAC load detection,
+ * do NOT poll the connector do detect disconnect because
+ * that would run DAC load detection again which can cause
+ * visible visual glitches.
+ *
+ * Only allow to poll such a connector again when forcing.
*/
+ if (!force && link->local_sink && link->type == dc_connection_dac_load)
+ return connector->status;
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED &&
- !aconnector->fake_enable)
- connected = (aconnector->dc_sink != NULL);
- else
- connected = (aconnector->base.force == DRM_FORCE_ON ||
- aconnector->base.force == DRM_FORCE_ON_DIGITAL);
+ mutex_lock(&aconnector->hpd_lock);
+
+ if (dc_link_detect_connection_type(aconnector->dc_link, &conn_type) &&
+ conn_type != dc_connection_none) {
+ mutex_lock(&adev->dm.dc_lock);
+
+ /* Only call full link detection when a sink isn't created yet,
+ * ie. just when the display is plugged in, otherwise we risk flickering.
+ */
+ if (link->local_sink ||
+ dc_link_detect(link, DETECT_REASON_HPD))
+ status = connector_status_connected;
+
+ mutex_unlock(&adev->dm.dc_lock);
+ }
+
+ if (connector->status != status) {
+ if (status == connector_status_disconnected) {
+ if (link->local_sink)
+ dc_sink_release(link->local_sink);
+
+ link->local_sink = NULL;
+ link->dpcd_sink_count = 0;
+ link->type = dc_connection_none;
+ }
+
+ amdgpu_dm_update_connector_after_detect(aconnector);
+ }
+
+ mutex_unlock(&aconnector->hpd_lock);
+ return status;
+}
+
+/**
+ * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display
+ *
+ * A connector is considered connected when it has a sink that is not NULL.
+ * For connectors that support HPD (hotplug detection), the connection is
+ * handled in the HPD interrupt.
+ * For connectors that may not support HPD, such as analog connectors,
+ * DRM will call this function repeatedly to poll them.
+ *
+ * Notes:
+ * 1. This interface is NOT called in context of HPD irq.
+ * 2. This interface *is called* in context of user-mode ioctl. Which
+ * makes it a bad place for *any* MST-related activity.
+ *
+ * @connector: The DRM connector we are checking. We convert it to
+ * amdgpu_dm_connector so we can read the DC link and state.
+ * @force: If true, do a full detect again. This is used even when
+ * a lighter check would normally be used to avoid flicker.
+ *
+ * Return: The connector status (connected, disconnected, or unknown).
+ *
+ */
+static enum drm_connector_status
+amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
update_subconnector_property(aconnector);
- return (connected ? connector_status_connected :
+ if (aconnector->base.force == DRM_FORCE_ON ||
+ aconnector->base.force == DRM_FORCE_ON_DIGITAL)
+ return connector_status_connected;
+ else if (aconnector->base.force == DRM_FORCE_OFF)
+ return connector_status_disconnected;
+
+ /* Poll analog connectors and only when either
+ * disconnected or connected to an analog display.
+ */
+ if (drm_kms_helper_is_poll_worker() &&
+ dc_connector_supports_analog(aconnector->dc_link->link_id.id) &&
+ (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog))
+ return amdgpu_dm_connector_poll(aconnector, force);
+
+ return (aconnector->dc_sink ? connector_status_connected :
connector_status_disconnected);
}
@@ -7218,6 +7547,20 @@ int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
} else if (property == adev->mode_info.underscan_property) {
dm_new_state->underscan_enable = val;
ret = 0;
+ } else if (property == adev->mode_info.abm_level_property) {
+ switch (val) {
+ case ABM_SYSFS_CONTROL:
+ dm_new_state->abm_sysfs_forbidden = false;
+ break;
+ case ABM_LEVEL_OFF:
+ dm_new_state->abm_sysfs_forbidden = true;
+ dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
+ break;
+ default:
+ dm_new_state->abm_sysfs_forbidden = true;
+ dm_new_state->abm_level = val;
+ }
+ ret = 0;
}
return ret;
@@ -7260,6 +7603,13 @@ int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
} else if (property == adev->mode_info.underscan_property) {
*val = dm_state->underscan_enable;
ret = 0;
+ } else if (property == adev->mode_info.abm_level_property) {
+ if (!dm_state->abm_sysfs_forbidden)
+ *val = ABM_SYSFS_CONTROL;
+ else
+ *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ?
+ dm_state->abm_level : 0;
+ ret = 0;
}
return ret;
@@ -7312,10 +7662,16 @@ static ssize_t panel_power_savings_store(struct device *device,
return -EINVAL;
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- to_dm_connector_state(connector->state)->abm_level = val ?:
- ABM_LEVEL_IMMEDIATE_DISABLE;
+ if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden)
+ ret = -EBUSY;
+ else
+ to_dm_connector_state(connector->state)->abm_level = val ?:
+ ABM_LEVEL_IMMEDIATE_DISABLE;
drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ if (ret)
+ return ret;
+
drm_kms_helper_hotplug_event(dev);
return count;
@@ -7380,6 +7736,13 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
if (aconnector->mst_mgr.dev)
drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr);
+ /* Cancel and flush any pending HDMI HPD debounce work */
+ cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work);
+ if (aconnector->hdmi_prev_sink) {
+ dc_sink_release(aconnector->hdmi_prev_sink);
+ aconnector->hdmi_prev_sink = NULL;
+ }
+
if (aconnector->bl_idx != -1) {
backlight_device_unregister(dm->backlight_dev[aconnector->bl_idx]);
dm->backlight_dev[aconnector->bl_idx] = NULL;
@@ -8030,7 +8393,7 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
"mode %dx%d@%dHz is not native, enabling scaling\n",
adjusted_mode->hdisplay, adjusted_mode->vdisplay,
drm_mode_vrefresh(adjusted_mode));
- dm_new_connector_state->scaling = RMX_FULL;
+ dm_new_connector_state->scaling = RMX_ASPECT;
}
return 0;
}
@@ -8155,7 +8518,7 @@ static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_state *state,
return 0;
}
-static int to_drm_connector_type(enum signal_type st)
+static int to_drm_connector_type(enum signal_type st, uint32_t connector_id)
{
switch (st) {
case SIGNAL_TYPE_HDMI_TYPE_A:
@@ -8171,6 +8534,10 @@ static int to_drm_connector_type(enum signal_type st)
return DRM_MODE_CONNECTOR_DisplayPort;
case SIGNAL_TYPE_DVI_DUAL_LINK:
case SIGNAL_TYPE_DVI_SINGLE_LINK:
+ if (connector_id == CONNECTOR_ID_SINGLE_LINK_DVII ||
+ connector_id == CONNECTOR_ID_DUAL_LINK_DVII)
+ return DRM_MODE_CONNECTOR_DVII;
+
return DRM_MODE_CONNECTOR_DVID;
case SIGNAL_TYPE_VIRTUAL:
return DRM_MODE_CONNECTOR_VIRTUAL;
@@ -8222,7 +8589,7 @@ static void amdgpu_dm_get_native_mode(struct drm_connector *connector)
static struct drm_display_mode *
amdgpu_dm_create_common_mode(struct drm_encoder *encoder,
- char *name,
+ const char *name,
int hdisplay, int vdisplay)
{
struct drm_device *dev = encoder->dev;
@@ -8244,6 +8611,24 @@ amdgpu_dm_create_common_mode(struct drm_encoder *encoder,
}
+static const struct amdgpu_dm_mode_size {
+ char name[DRM_DISPLAY_MODE_LEN];
+ int w;
+ int h;
+} common_modes[] = {
+ { "640x480", 640, 480},
+ { "800x600", 800, 600},
+ { "1024x768", 1024, 768},
+ { "1280x720", 1280, 720},
+ { "1280x800", 1280, 800},
+ {"1280x1024", 1280, 1024},
+ { "1440x900", 1440, 900},
+ {"1680x1050", 1680, 1050},
+ {"1600x1200", 1600, 1200},
+ {"1920x1080", 1920, 1080},
+ {"1920x1200", 1920, 1200}
+};
+
static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
struct drm_connector *connector)
{
@@ -8254,23 +8639,6 @@ static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
to_amdgpu_dm_connector(connector);
int i;
int n;
- struct mode_size {
- char name[DRM_DISPLAY_MODE_LEN];
- int w;
- int h;
- } common_modes[] = {
- { "640x480", 640, 480},
- { "800x600", 800, 600},
- { "1024x768", 1024, 768},
- { "1280x720", 1280, 720},
- { "1280x800", 1280, 800},
- {"1280x1024", 1280, 1024},
- { "1440x900", 1440, 900},
- {"1680x1050", 1680, 1050},
- {"1600x1200", 1600, 1200},
- {"1920x1080", 1920, 1080},
- {"1920x1200", 1920, 1200}
- };
if ((connector->connector_type != DRM_MODE_CONNECTOR_eDP) &&
(connector->connector_type != DRM_MODE_CONNECTOR_LVDS))
@@ -8471,6 +8839,16 @@ static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connect
if (!(amdgpu_freesync_vid_mode && drm_edid))
return;
+ if (!amdgpu_dm_connector->dc_sink || !amdgpu_dm_connector->dc_link)
+ return;
+
+ if (!dc_supports_vrr(amdgpu_dm_connector->dc_sink->ctx->dce_version))
+ return;
+
+ if (dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id) &&
+ amdgpu_dm_connector->dc_sink->edid_caps.analog)
+ return;
+
if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
amdgpu_dm_connector->num_modes +=
add_fs_modes(amdgpu_dm_connector);
@@ -8480,11 +8858,11 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
{
struct amdgpu_dm_connector *amdgpu_dm_connector =
to_amdgpu_dm_connector(connector);
+ struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
struct drm_encoder *encoder;
const struct drm_edid *drm_edid = amdgpu_dm_connector->drm_edid;
- struct dc_link_settings *verified_link_cap =
- &amdgpu_dm_connector->dc_link->verified_link_cap;
- const struct dc *dc = amdgpu_dm_connector->dc_link->dc;
+ struct dc_link_settings *verified_link_cap = &dc_link->verified_link_cap;
+ const struct dc *dc = dc_link->dc;
encoder = amdgpu_dm_connector_to_encoder(connector);
@@ -8494,6 +8872,17 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
if (dc->link_srv->dp_get_encoding_format(verified_link_cap) == DP_128b_132b_ENCODING)
amdgpu_dm_connector->num_modes +=
drm_add_modes_noedid(connector, 1920, 1080);
+
+ if (amdgpu_dm_connector->dc_sink &&
+ amdgpu_dm_connector->dc_sink->edid_caps.analog &&
+ dc_connector_supports_analog(dc_link->link_id.id)) {
+ /* Analog monitor connected by DAC load detection.
+ * Add common modes. It will be up to the user to select one that works.
+ */
+ for (int i = 0; i < ARRAY_SIZE(common_modes); i++)
+ amdgpu_dm_connector->num_modes += drm_add_modes_noedid(
+ connector, common_modes[i].w, common_modes[i].h);
+ }
} else {
amdgpu_dm_connector_ddc_get_modes(connector, drm_edid);
if (encoder)
@@ -8541,6 +8930,10 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
mutex_init(&aconnector->hpd_lock);
mutex_init(&aconnector->handle_mst_msg_ready);
+ aconnector->hdmi_hpd_debounce_delay_ms = AMDGPU_DM_HDMI_HPD_DEBOUNCE_MS;
+ INIT_DELAYED_WORK(&aconnector->hdmi_hpd_debounce_work, hdmi_hpd_debounce_work);
+ aconnector->hdmi_prev_sink = NULL;
+
/*
* configure support HPD hot plug connector_>polled default value is 0
* which means HPD hot plug not supported
@@ -8562,6 +8955,11 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
case DRM_MODE_CONNECTOR_DVID:
aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
break;
+ case DRM_MODE_CONNECTOR_DVII:
+ case DRM_MODE_CONNECTOR_VGA:
+ aconnector->base.polled =
+ DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+ break;
default:
break;
}
@@ -8763,7 +9161,7 @@ static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
goto out_free;
}
- connector_type = to_drm_connector_type(link->connector_signal);
+ connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id);
res = drm_connector_init_with_ddc(
dm->ddev,
@@ -10519,7 +10917,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
* Here we create an empty update on each plane.
* To fix this, DC should permit updating only stream properties.
*/
- dummy_updates = kzalloc(sizeof(struct dc_surface_update) * MAX_SURFACES, GFP_ATOMIC);
+ dummy_updates = kzalloc(sizeof(struct dc_surface_update) * MAX_SURFACES, GFP_KERNEL);
if (!dummy_updates) {
drm_err(adev_to_drm(adev), "Failed to allocate memory for dummy_updates.\n");
continue;
@@ -12446,7 +12844,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
int j = state->num_private_objs-1;
dm_atomic_destroy_state(obj,
- state->private_objs[i].state);
+ state->private_objs[i].state_to_destroy);
/* If i is not at the end of the array then the
* last element needs to be moved to where i was
@@ -12457,7 +12855,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
state->private_objs[j];
state->private_objs[j].ptr = NULL;
- state->private_objs[j].state = NULL;
+ state->private_objs[j].state_to_destroy = NULL;
state->private_objs[j].old_state = NULL;
state->private_objs[j].new_state = NULL;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index db75e991ac7b..ef97cede9926 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -59,6 +59,7 @@
#define AMDGPU_HDR_MULT_DEFAULT (0x100000000LL)
+#define AMDGPU_DM_HDMI_HPD_DEBOUNCE_MS 1500
/*
#include "include/amdgpu_dal_power_if.h"
#include "amdgpu_dm_irq.h"
@@ -819,6 +820,11 @@ struct amdgpu_dm_connector {
bool pack_sdp_v1_3;
enum adaptive_sync_type as_type;
struct amdgpu_hdmi_vsdb_info vsdb_info;
+
+ /* HDMI HPD debounce support */
+ unsigned int hdmi_hpd_debounce_delay_ms;
+ struct delayed_work hdmi_hpd_debounce_work;
+ struct dc_sink *hdmi_prev_sink;
};
static inline void amdgpu_dm_set_mst_status(uint8_t *status,
@@ -993,6 +999,7 @@ struct dm_connector_state {
bool underscan_enable;
bool freesync_capable;
bool update_hdcp;
+ bool abm_sysfs_forbidden;
uint8_t abm_level;
int vcpi_slots;
uint64_t pbn;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
index a4ac6d442278..1dcc79b35225 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c
@@ -26,12 +26,39 @@
#include "amdgpu.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
+#include "amdgpu_dm_colorop.h"
#include "dc.h"
#include "modules/color/color_gamma.h"
/**
* DOC: overview
*
+ * We have three types of color management in the AMD display driver.
+ * 1. the legacy &drm_crtc DEGAMMA, CTM, and GAMMA properties
+ * 2. AMD driver private color management on &drm_plane and &drm_crtc
+ * 3. AMD plane color pipeline
+ *
+ * The CRTC properties are the original color management. When they were
+ * implemented per-plane color management was not a thing yet. Because
+ * of that we could get away with plumbing the DEGAMMA and CTM
+ * properties to pre-blending HW functions. This is incompatible with
+ * per-plane color management, such as via the AMD private properties or
+ * the new drm_plane color pipeline. The only compatible CRTC property
+ * with per-plane color management is the GAMMA property as it is
+ * applied post-blending.
+ *
+ * The AMD driver private color management properties are only exposed
+ * when the kernel is built explicitly with -DAMD_PRIVATE_COLOR. They
+ * are temporary building blocks on the path to full-fledged &drm_plane
+ * and &drm_crtc color pipelines and lay the driver's groundwork for the
+ * color pipelines.
+ *
+ * The AMD plane color pipeline describes AMD's &drm_colorops via the
+ * &drm_plane's COLOR_PIPELINE property.
+ *
+ * drm_crtc Properties
+ * -------------------
+ *
* The DC interface to HW gives us the following color management blocks
* per pipe (surface):
*
@@ -42,36 +69,93 @@
* - Surface regamma LUT (normalized)
* - Output CSC (normalized)
*
- * But these aren't a direct mapping to DRM color properties. The current DRM
- * interface exposes CRTC degamma, CRTC CTM and CRTC regamma while our hardware
- * is essentially giving:
+ * But these aren't a direct mapping to DRM color properties. The
+ * current DRM interface exposes CRTC degamma, CRTC CTM and CRTC regamma
+ * while our hardware is essentially giving:
*
* Plane CTM -> Plane degamma -> Plane CTM -> Plane regamma -> Plane CTM
*
- * The input gamma LUT block isn't really applicable here since it operates
- * on the actual input data itself rather than the HW fp representation. The
- * input and output CSC blocks are technically available to use as part of
- * the DC interface but are typically used internally by DC for conversions
- * between color spaces. These could be blended together with user
- * adjustments in the future but for now these should remain untouched.
+ * The input gamma LUT block isn't really applicable here since it
+ * operates on the actual input data itself rather than the HW fp
+ * representation. The input and output CSC blocks are technically
+ * available to use as part of the DC interface but are typically used
+ * internally by DC for conversions between color spaces. These could be
+ * blended together with user adjustments in the future but for now
+ * these should remain untouched.
+ *
+ * The pipe blending also happens after these blocks so we don't
+ * actually support any CRTC props with correct blending with multiple
+ * planes - but we can still support CRTC color management properties in
+ * DM in most single plane cases correctly with clever management of the
+ * DC interface in DM.
+ *
+ * As per DRM documentation, blocks should be in hardware bypass when
+ * their respective property is set to NULL. A linear DGM/RGM LUT should
+ * also considered as putting the respective block into bypass mode.
+ *
+ * This means that the following configuration is assumed to be the
+ * default:
+ *
+ * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ... CRTC
+ * DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass
+ *
+ * AMD Private Color Management on drm_plane
+ * -----------------------------------------
+ *
+ * The AMD private color management properties on a &drm_plane are:
*
- * The pipe blending also happens after these blocks so we don't actually
- * support any CRTC props with correct blending with multiple planes - but we
- * can still support CRTC color management properties in DM in most single
- * plane cases correctly with clever management of the DC interface in DM.
+ * - AMD_PLANE_DEGAMMA_LUT
+ * - AMD_PLANE_DEGAMMA_LUT_SIZE
+ * - AMD_PLANE_DEGAMMA_TF
+ * - AMD_PLANE_HDR_MULT
+ * - AMD_PLANE_CTM
+ * - AMD_PLANE_SHAPER_LUT
+ * - AMD_PLANE_SHAPER_LUT_SIZE
+ * - AMD_PLANE_SHAPER_TF
+ * - AMD_PLANE_LUT3D
+ * - AMD_PLANE_LUT3D_SIZE
+ * - AMD_PLANE_BLEND_LUT
+ * - AMD_PLANE_BLEND_LUT_SIZE
+ * - AMD_PLANE_BLEND_TF
*
- * As per DRM documentation, blocks should be in hardware bypass when their
- * respective property is set to NULL. A linear DGM/RGM LUT should also
- * considered as putting the respective block into bypass mode.
+ * The AMD private color management property on a &drm_crtc is:
*
- * This means that the following
- * configuration is assumed to be the default:
+ * - AMD_CRTC_REGAMMA_TF
+ *
+ * Use of these properties is discouraged.
+ *
+ * AMD plane color pipeline
+ * ------------------------
+ *
+ * The AMD &drm_plane color pipeline is advertised for DCN generations
+ * 3.0 and newer. It exposes these elements in this order:
+ *
+ * 1. 1D curve colorop
+ * 2. Multiplier
+ * 3. 3x4 CTM
+ * 4. 1D curve colorop
+ * 5. 1D LUT
+ * 6. 3D LUT
+ * 7. 1D curve colorop
+ * 8. 1D LUT
+ *
+ * The multiplier (#2) is a simple multiplier that is applied to all
+ * channels.
+ *
+ * The 3x4 CTM (#3) is a simple 3x4 matrix.
+ *
+ * #1, and #7 are non-linear to linear curves. #4 is a linear to
+ * non-linear curve. They support sRGB, PQ, and BT.709/BT.2020 EOTFs or
+ * their inverse.
+ *
+ * The 1D LUTs (#5 and #8) are plain 4096 entry LUTs.
+ *
+ * The 3DLUT (#6) is a tetrahedrally interpolated 17 cube LUT.
*
- * Plane DGM Bypass -> Plane CTM Bypass -> Plane RGM Bypass -> ...
- * CRTC DGM Bypass -> CRTC CTM Bypass -> CRTC RGM Bypass
*/
#define MAX_DRM_LUT_VALUE 0xFFFF
+#define MAX_DRM_LUT32_VALUE 0xFFFFFFFF
#define SDR_WHITE_LEVEL_INIT_VALUE 80
/**
@@ -342,6 +426,21 @@ __extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size)
}
/**
+ * __extract_blob_lut32 - Extracts the DRM lut and lut size from a blob.
+ * @blob: DRM color mgmt property blob
+ * @size: lut size
+ *
+ * Returns:
+ * DRM LUT or NULL
+ */
+static const struct drm_color_lut32 *
+__extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size)
+{
+ *size = blob ? drm_color_lut32_size(blob) : 0;
+ return blob ? (struct drm_color_lut32 *)blob->data : NULL;
+}
+
+/**
* __is_lut_linear - check if the given lut is a linear mapping of values
* @lut: given lut to check values
* @size: lut size
@@ -415,6 +514,24 @@ static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
}
/**
+ * __drm_lut32_to_dc_gamma - convert the drm_color_lut to dc_gamma.
+ * @lut: DRM lookup table for color conversion
+ * @gamma: DC gamma to set entries
+ *
+ * The conversion depends on the size of the lut - whether or not it's legacy.
+ */
+static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc_gamma *gamma)
+{
+ int i;
+
+ for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
+ gamma->entries.red[i] = dc_fixpt_from_fraction(lut[i].red, MAX_DRM_LUT32_VALUE);
+ gamma->entries.green[i] = dc_fixpt_from_fraction(lut[i].green, MAX_DRM_LUT32_VALUE);
+ gamma->entries.blue[i] = dc_fixpt_from_fraction(lut[i].blue, MAX_DRM_LUT32_VALUE);
+ }
+}
+
+/**
* __drm_ctm_to_dc_matrix - converts a DRM CTM to a DC CSC float matrix
* @ctm: DRM color transformation matrix
* @matrix: DC CSC float matrix
@@ -566,6 +683,63 @@ static int __set_output_tf(struct dc_transfer_func *func,
return res ? 0 : -ENOMEM;
}
+/**
+ * __set_output_tf_32 - calculates the output transfer function based on expected input space.
+ * @func: transfer function
+ * @lut: lookup table that defines the color space
+ * @lut_size: size of respective lut
+ * @has_rom: if ROM can be used for hardcoded curve
+ *
+ * Returns:
+ * 0 in case of success. -ENOMEM if fails.
+ */
+static int __set_output_tf_32(struct dc_transfer_func *func,
+ const struct drm_color_lut32 *lut, uint32_t lut_size,
+ bool has_rom)
+{
+ struct dc_gamma *gamma = NULL;
+ struct calculate_buffer cal_buffer = {0};
+ bool res;
+
+ cal_buffer.buffer_index = -1;
+
+ if (lut_size) {
+ gamma = dc_create_gamma();
+ if (!gamma)
+ return -ENOMEM;
+
+ gamma->num_entries = lut_size;
+ __drm_lut32_to_dc_gamma(lut, gamma);
+ }
+
+ if (func->tf == TRANSFER_FUNCTION_LINEAR) {
+ /*
+ * Color module doesn't like calculating regamma params
+ * on top of a linear input. But degamma params can be used
+ * instead to simulate this.
+ */
+ if (gamma)
+ gamma->type = GAMMA_CUSTOM;
+ res = mod_color_calculate_degamma_params(NULL, func,
+ gamma, gamma != NULL);
+ } else {
+ /*
+ * Assume sRGB. The actual mapping will depend on whether the
+ * input was legacy or not.
+ */
+ if (gamma)
+ gamma->type = GAMMA_CS_TFM_1D;
+ res = mod_color_calculate_regamma_params(func, gamma, gamma != NULL,
+ has_rom, NULL, &cal_buffer);
+ }
+
+ if (gamma)
+ dc_gamma_release(&gamma);
+
+ return res ? 0 : -ENOMEM;
+}
+
+
static int amdgpu_dm_set_atomic_regamma(struct dc_transfer_func *out_tf,
const struct drm_color_lut *regamma_lut,
uint32_t regamma_size, bool has_rom,
@@ -638,6 +812,42 @@ static int __set_input_tf(struct dc_color_caps *caps, struct dc_transfer_func *f
return res ? 0 : -ENOMEM;
}
+/**
+ * __set_input_tf_32 - calculates the input transfer function based on expected
+ * input space.
+ * @caps: dc color capabilities
+ * @func: transfer function
+ * @lut: lookup table that defines the color space
+ * @lut_size: size of respective lut.
+ *
+ * Returns:
+ * 0 in case of success. -ENOMEM if fails.
+ */
+static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func *func,
+ const struct drm_color_lut32 *lut, uint32_t lut_size)
+{
+ struct dc_gamma *gamma = NULL;
+ bool res;
+
+ if (lut_size) {
+ gamma = dc_create_gamma();
+ if (!gamma)
+ return -ENOMEM;
+
+ gamma->type = GAMMA_CUSTOM;
+ gamma->num_entries = lut_size;
+
+ __drm_lut32_to_dc_gamma(lut, gamma);
+ }
+
+ res = mod_color_calculate_degamma_params(caps, func, gamma, gamma != NULL);
+
+ if (gamma)
+ dc_gamma_release(&gamma);
+
+ return res ? 0 : -ENOMEM;
+}
+
static enum dc_transfer_func_predefined
amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)
{
@@ -667,6 +877,27 @@ amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)
}
}
+static enum dc_transfer_func_predefined
+amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf)
+{
+ switch (tf) {
+ case DRM_COLOROP_1D_CURVE_SRGB_EOTF:
+ case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF:
+ return TRANSFER_FUNCTION_SRGB;
+ case DRM_COLOROP_1D_CURVE_PQ_125_EOTF:
+ case DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF:
+ return TRANSFER_FUNCTION_PQ;
+ case DRM_COLOROP_1D_CURVE_BT2020_INV_OETF:
+ case DRM_COLOROP_1D_CURVE_BT2020_OETF:
+ return TRANSFER_FUNCTION_BT709;
+ case DRM_COLOROP_1D_CURVE_GAMMA22:
+ case DRM_COLOROP_1D_CURVE_GAMMA22_INV:
+ return TRANSFER_FUNCTION_GAMMA22;
+ default:
+ return TRANSFER_FUNCTION_LINEAR;
+ }
+}
+
static void __to_dc_lut3d_color(struct dc_rgb *rgb,
const struct drm_color_lut lut,
int bit_precision)
@@ -720,6 +951,59 @@ static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
__to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
}
+static void __to_dc_lut3d_32_color(struct dc_rgb *rgb,
+ const struct drm_color_lut32 lut,
+ int bit_precision)
+{
+ rgb->red = drm_color_lut32_extract(lut.red, bit_precision);
+ rgb->green = drm_color_lut32_extract(lut.green, bit_precision);
+ rgb->blue = drm_color_lut32_extract(lut.blue, bit_precision);
+}
+
+static void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
+ uint32_t lut3d_size,
+ struct tetrahedral_params *params,
+ bool use_tetrahedral_9,
+ int bit_depth)
+{
+ struct dc_rgb *lut0;
+ struct dc_rgb *lut1;
+ struct dc_rgb *lut2;
+ struct dc_rgb *lut3;
+ int lut_i, i;
+
+
+ if (use_tetrahedral_9) {
+ lut0 = params->tetrahedral_9.lut0;
+ lut1 = params->tetrahedral_9.lut1;
+ lut2 = params->tetrahedral_9.lut2;
+ lut3 = params->tetrahedral_9.lut3;
+ } else {
+ lut0 = params->tetrahedral_17.lut0;
+ lut1 = params->tetrahedral_17.lut1;
+ lut2 = params->tetrahedral_17.lut2;
+ lut3 = params->tetrahedral_17.lut3;
+ }
+
+ for (lut_i = 0, i = 0; i < lut3d_size - 4; lut_i++, i += 4) {
+ /*
+ * We should consider the 3D LUT RGB values are distributed
+ * along four arrays lut0-3 where the first sizes 1229 and the
+ * other 1228. The bit depth supported for 3dlut channel is
+ * 12-bit, but DC also supports 10-bit.
+ *
+ * TODO: improve color pipeline API to enable the userspace set
+ * bit depth and 3D LUT size/stride, as specified by VA-API.
+ */
+ __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth);
+ __to_dc_lut3d_32_color(&lut1[lut_i], lut[i + 1], bit_depth);
+ __to_dc_lut3d_32_color(&lut2[lut_i], lut[i + 2], bit_depth);
+ __to_dc_lut3d_32_color(&lut3[lut_i], lut[i + 3], bit_depth);
+ }
+ /* lut0 has 1229 points (lut_size/4 + 1) */
+ __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth);
+}
+
/* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream
* @drm_lut3d: user 3D LUT
* @drm_lut3d_size: size of 3D LUT
@@ -1178,6 +1462,360 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state,
}
static int
+__set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state,
+ struct drm_colorop_state *colorop_state)
+{
+ struct dc_transfer_func *tf = &dc_plane_state->in_transfer_func;
+ struct drm_colorop *colorop = colorop_state->colorop;
+ struct drm_device *drm = colorop->dev;
+
+ if (colorop->type != DRM_COLOROP_1D_CURVE)
+ return -EINVAL;
+
+ if (!(BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs))
+ return -EINVAL;
+
+ if (colorop_state->bypass) {
+ tf->type = TF_TYPE_BYPASS;
+ tf->tf = TRANSFER_FUNCTION_LINEAR;
+ return 0;
+ }
+
+ drm_dbg(drm, "Degamma colorop with ID: %d\n", colorop->base.id);
+
+ tf->type = TF_TYPE_PREDEFINED;
+ tf->tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
+
+ return 0;
+}
+
+static int
+__set_dm_plane_colorop_degamma(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_colorop *old_colorop;
+ struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_atomic_state *state = plane_state->state;
+ int i = 0;
+
+ old_colorop = colorop;
+
+ /* 1st op: 1d curve - degamma */
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_degam_tfs)) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (!colorop_state)
+ return -EINVAL;
+
+ return __set_colorop_in_tf_1d_curve(dc_plane_state, colorop_state);
+}
+
+static int
+__set_dm_plane_colorop_3x4_matrix(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_colorop *old_colorop;
+ struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_atomic_state *state = plane_state->state;
+ const struct drm_device *dev = colorop->dev;
+ const struct drm_property_blob *blob;
+ struct drm_color_ctm_3x4 *ctm = NULL;
+ int i = 0;
+
+ /* 3x4 matrix */
+ old_colorop = colorop;
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ new_colorop_state->colorop->type == DRM_COLOROP_CTM_3X4) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_CTM_3X4) {
+ drm_dbg(dev, "3x4 matrix colorop with ID: %d\n", colorop->base.id);
+ blob = colorop_state->data;
+ if (blob->length == sizeof(struct drm_color_ctm_3x4)) {
+ ctm = (struct drm_color_ctm_3x4 *) blob->data;
+ __drm_ctm_3x4_to_dc_matrix(ctm, dc_plane_state->gamut_remap_matrix.matrix);
+ dc_plane_state->gamut_remap_matrix.enable_remap = true;
+ dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
+ } else {
+ drm_warn(dev, "blob->length (%zu) isn't equal to drm_color_ctm_3x4 (%zu)\n",
+ blob->length, sizeof(struct drm_color_ctm_3x4));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int
+__set_dm_plane_colorop_multiplier(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_colorop *old_colorop;
+ struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_atomic_state *state = plane_state->state;
+ const struct drm_device *dev = colorop->dev;
+ int i = 0;
+
+ /* Multiplier */
+ old_colorop = colorop;
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ new_colorop_state->colorop->type == DRM_COLOROP_MULTIPLIER) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_MULTIPLIER) {
+ drm_dbg(dev, "Multiplier colorop with ID: %d\n", colorop->base.id);
+ dc_plane_state->hdr_mult = amdgpu_dm_fixpt_from_s3132(colorop_state->multiplier);
+ }
+
+ return 0;
+}
+
+static int
+__set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_colorop *old_colorop;
+ struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_atomic_state *state = plane_state->state;
+ enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
+ struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
+ const struct drm_color_lut32 *shaper_lut;
+ struct drm_device *dev = colorop->dev;
+ bool enabled = false;
+ u32 shaper_size;
+ int i = 0, ret = 0;
+
+ /* 1D Curve - SHAPER TF */
+ old_colorop = colorop;
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE) {
+ drm_dbg(dev, "Shaper TF colorop with ID: %d\n", colorop->base.id);
+ tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+ tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
+ tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+ ret = __set_output_tf(tf, 0, 0, false);
+ if (ret)
+ return ret;
+ enabled = true;
+ }
+
+ /* 1D LUT - SHAPER LUT */
+ colorop = old_colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no Shaper LUT colorop found\n");
+ return -EINVAL;
+ }
+
+ old_colorop = colorop;
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT) {
+ drm_dbg(dev, "Shaper LUT colorop with ID: %d\n", colorop->base.id);
+ tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+ tf->tf = default_tf;
+ tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+ shaper_lut = __extract_blob_lut32(colorop_state->data, &shaper_size);
+ shaper_size = shaper_lut != NULL ? shaper_size : 0;
+
+ /* Custom LUT size must be the same as supported size */
+ if (shaper_size == colorop->size) {
+ ret = __set_output_tf_32(tf, shaper_lut, shaper_size, false);
+ if (ret)
+ return ret;
+ enabled = true;
+ }
+ }
+
+ if (!enabled)
+ tf->type = TF_TYPE_BYPASS;
+
+ return 0;
+}
+
+/* __set_colorop_3dlut - set DRM 3D LUT to DC stream
+ * @drm_lut3d: user 3D LUT
+ * @drm_lut3d_size: size of 3D LUT
+ * @lut3d: DC 3D LUT
+ *
+ * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it
+ * on DCN accordingly.
+ *
+ * Returns:
+ * 0 on success. -EINVAL if drm_lut3d_size is zero.
+ */
+static int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d,
+ uint32_t drm_lut3d_size,
+ struct dc_3dlut *lut)
+{
+ if (!drm_lut3d_size) {
+ lut->state.bits.initialized = 0;
+ return -EINVAL;
+ }
+
+ /* Only supports 17x17x17 3D LUT (12-bit) now */
+ lut->lut_3d.use_12bits = true;
+ lut->lut_3d.use_tetrahedral_9 = false;
+
+ lut->state.bits.initialized = 1;
+ __drm_3dlut32_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &lut->lut_3d,
+ lut->lut_3d.use_tetrahedral_9, 12);
+
+ return 0;
+}
+
+static int
+__set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_colorop *old_colorop;
+ struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func;
+ struct drm_atomic_state *state = plane_state->state;
+ const struct amdgpu_device *adev = drm_to_adev(colorop->dev);
+ const struct drm_device *dev = colorop->dev;
+ const struct drm_color_lut32 *lut3d;
+ uint32_t lut3d_size;
+ int i = 0, ret = 0;
+
+ /* 3D LUT */
+ old_colorop = colorop;
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ new_colorop_state->colorop->type == DRM_COLOROP_3D_LUT) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_3D_LUT) {
+ if (!adev->dm.dc->caps.color.dpp.hw_3d_lut) {
+ drm_dbg(dev, "3D LUT is not supported by hardware\n");
+ return -EINVAL;
+ }
+
+ drm_dbg(dev, "3D LUT colorop with ID: %d\n", colorop->base.id);
+ lut3d = __extract_blob_lut32(colorop_state->data, &lut3d_size);
+ lut3d_size = lut3d != NULL ? lut3d_size : 0;
+ ret = __set_colorop_3dlut(lut3d, lut3d_size, &dc_plane_state->lut3d_func);
+ if (ret) {
+ drm_dbg(dev, "3D LUT colorop with ID: %d has LUT size = %d\n",
+ colorop->base.id, lut3d_size);
+ return ret;
+ }
+
+ /* 3D LUT requires shaper. If shaper colorop is bypassed, enable shaper curve
+ * with TRANSFER_FUNCTION_LINEAR
+ */
+ if (tf->type == TF_TYPE_BYPASS) {
+ tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+ tf->tf = TRANSFER_FUNCTION_LINEAR;
+ tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+ ret = __set_output_tf_32(tf, NULL, 0, false);
+ }
+ }
+
+ return ret;
+}
+
+static int
+__set_dm_plane_colorop_blend(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_colorop *old_colorop;
+ struct drm_colorop_state *colorop_state = NULL, *new_colorop_state;
+ struct drm_atomic_state *state = plane_state->state;
+ enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR;
+ struct dc_transfer_func *tf = &dc_plane_state->blend_tf;
+ const struct drm_color_lut32 *blend_lut = NULL;
+ struct drm_device *dev = colorop->dev;
+ uint32_t blend_size = 0;
+ int i = 0;
+
+ /* 1D Curve - BLND TF */
+ old_colorop = colorop;
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE &&
+ (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) {
+ drm_dbg(dev, "Blend TF colorop with ID: %d\n", colorop->base.id);
+ tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+ tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type);
+ tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+ __set_input_tf_32(NULL, tf, blend_lut, blend_size);
+ }
+
+ /* 1D Curve - BLND LUT */
+ colorop = old_colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no Blend LUT colorop found\n");
+ return -EINVAL;
+ }
+
+ old_colorop = colorop;
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == old_colorop &&
+ new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) {
+ colorop_state = new_colorop_state;
+ break;
+ }
+ }
+
+ if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT &&
+ (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) {
+ drm_dbg(dev, "Blend LUT colorop with ID: %d\n", colorop->base.id);
+ tf->type = TF_TYPE_DISTRIBUTED_POINTS;
+ tf->tf = default_tf;
+ tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE;
+ blend_lut = __extract_blob_lut32(colorop_state->data, &blend_size);
+ blend_size = blend_lut != NULL ? blend_size : 0;
+
+ /* Custom LUT size must be the same as supported size */
+ if (blend_size == colorop->size)
+ __set_input_tf_32(NULL, tf, blend_lut, blend_size);
+ }
+
+ return 0;
+}
+
+static int
amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
struct dc_plane_state *dc_plane_state)
{
@@ -1227,6 +1865,93 @@ amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state,
return 0;
}
+static int
+amdgpu_dm_plane_set_colorop_properties(struct drm_plane_state *plane_state,
+ struct dc_plane_state *dc_plane_state)
+{
+ struct drm_colorop *colorop = plane_state->color_pipeline;
+ struct drm_device *dev = plane_state->plane->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ int ret;
+
+ /* 1D Curve - DEGAM TF */
+ if (!colorop)
+ return -EINVAL;
+
+ ret = __set_dm_plane_colorop_degamma(plane_state, dc_plane_state, colorop);
+ if (ret)
+ return ret;
+
+ /* Multiplier */
+ colorop = colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no multiplier colorop found\n");
+ return -EINVAL;
+ }
+
+ ret = __set_dm_plane_colorop_multiplier(plane_state, dc_plane_state, colorop);
+ if (ret)
+ return ret;
+
+ /* 3x4 matrix */
+ colorop = colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no 3x4 matrix colorop found\n");
+ return -EINVAL;
+ }
+
+ ret = __set_dm_plane_colorop_3x4_matrix(plane_state, dc_plane_state, colorop);
+ if (ret)
+ return ret;
+
+ if (adev->dm.dc->caps.color.dpp.hw_3d_lut) {
+ /* 1D Curve & LUT - SHAPER TF & LUT */
+ colorop = colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no Shaper TF colorop found\n");
+ return -EINVAL;
+ }
+
+ ret = __set_dm_plane_colorop_shaper(plane_state, dc_plane_state, colorop);
+ if (ret)
+ return ret;
+
+ /* Shaper LUT colorop is already handled, just skip here */
+ colorop = colorop->next;
+ if (!colorop)
+ return -EINVAL;
+
+ /* 3D LUT */
+ colorop = colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no 3D LUT colorop found\n");
+ return -EINVAL;
+ }
+
+ ret = __set_dm_plane_colorop_3dlut(plane_state, dc_plane_state, colorop);
+ if (ret)
+ return ret;
+ }
+
+ /* 1D Curve & LUT - BLND TF & LUT */
+ colorop = colorop->next;
+ if (!colorop) {
+ drm_dbg(dev, "no Blend TF colorop found\n");
+ return -EINVAL;
+ }
+
+ ret = __set_dm_plane_colorop_blend(plane_state, dc_plane_state, colorop);
+ if (ret)
+ return ret;
+
+ /* BLND LUT colorop is already handled, just skip here */
+ colorop = colorop->next;
+ if (!colorop)
+ return -EINVAL;
+
+ return 0;
+}
+
/**
* amdgpu_dm_update_plane_color_mgmt: Maps DRM color management to DC plane.
* @crtc: amdgpu_dm crtc state
@@ -1323,5 +2048,8 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc,
dc_plane_state->input_csc_color_matrix.enable_adjustment = false;
}
+ if (!amdgpu_dm_plane_set_colorop_properties(plane_state, dc_plane_state))
+ return 0;
+
return amdgpu_dm_plane_set_color_properties(plane_state, dc_plane_state);
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
new file mode 100644
index 000000000000..d585618b8064
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2023 Advanced Micro Devices, 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.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include <drm/drm_print.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_property.h>
+#include <drm/drm_colorop.h>
+
+#include "amdgpu.h"
+#include "amdgpu_dm_colorop.h"
+#include "dc.h"
+
+const u64 amdgpu_dm_supported_degam_tfs =
+ BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+ BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
+ BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
+ BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
+
+const u64 amdgpu_dm_supported_shaper_tfs =
+ BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
+ BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) |
+ BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF) |
+ BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
+
+const u64 amdgpu_dm_supported_blnd_tfs =
+ BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+ BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
+ BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
+ BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
+
+#define MAX_COLOR_PIPELINE_OPS 10
+
+#define LUT3D_SIZE 17
+
+int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list)
+{
+ struct drm_colorop *ops[MAX_COLOR_PIPELINE_OPS];
+ struct drm_device *dev = plane->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ int ret;
+ int i = 0;
+
+ memset(ops, 0, sizeof(ops));
+
+ /* 1D curve - DEGAM TF */
+ ops[i] = kzalloc(sizeof(*ops[0]), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane,
+ amdgpu_dm_supported_degam_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ list->type = ops[i]->base.id;
+ list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[i]->base.id);
+
+ i++;
+
+ /* Multiplier */
+ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_mult_init(dev, ops[i], plane, DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i-1], ops[i]);
+
+ i++;
+
+ /* 3x4 matrix */
+ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane, DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i-1], ops[i]);
+
+ i++;
+
+ if (adev->dm.dc->caps.color.dpp.hw_3d_lut) {
+ /* 1D curve - SHAPER TF */
+ ops[i] = kzalloc(sizeof(*ops[0]), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane,
+ amdgpu_dm_supported_shaper_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i-1], ops[i]);
+
+ i++;
+
+ /* 1D LUT - SHAPER LUT */
+ ops[i] = kzalloc(sizeof(*ops[0]), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES,
+ DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i-1], ops[i]);
+
+ i++;
+
+ /* 3D LUT */
+ ops[i] = kzalloc(sizeof(*ops[0]), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_3dlut_init(dev, ops[i], plane, LUT3D_SIZE,
+ DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i-1], ops[i]);
+
+ i++;
+ }
+
+ /* 1D curve - BLND TF */
+ ops[i] = kzalloc(sizeof(*ops[0]), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane,
+ amdgpu_dm_supported_blnd_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i - 1], ops[i]);
+
+ i++;
+
+ /* 1D LUT - BLND LUT */
+ ops[i] = kzalloc(sizeof(struct drm_colorop), GFP_KERNEL);
+ if (!ops[i]) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_curve_1d_lut_init(dev, ops[i], plane, MAX_COLOR_LUT_ENTRIES,
+ DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i-1], ops[i]);
+ return 0;
+
+cleanup:
+ if (ret == -ENOMEM)
+ drm_err(plane->dev, "KMS: Failed to allocate colorop\n");
+
+ drm_colorop_pipeline_destroy(dev);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
new file mode 100644
index 000000000000..2e1617ffc8ee
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2023 Advanced Micro Devices, 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.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef __AMDGPU_DM_COLOROP_H__
+#define __AMDGPU_DM_COLOROP_H__
+
+extern const u64 amdgpu_dm_supported_degam_tfs;
+extern const u64 amdgpu_dm_supported_shaper_tfs;
+extern const u64 amdgpu_dm_supported_blnd_tfs;
+
+int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list);
+
+#endif /* __AMDGPU_DM_COLOROP_H__*/
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 1ec9d03ad747..697e232acebf 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@ -248,6 +248,8 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
struct vblank_control_work *vblank_work =
container_of(work, struct vblank_control_work, work);
struct amdgpu_display_manager *dm = vblank_work->dm;
+ struct amdgpu_device *adev = drm_to_adev(dm->ddev);
+ int r;
mutex_lock(&dm->dc_lock);
@@ -277,7 +279,16 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
if (dm->active_vblank_irq_count == 0) {
dc_post_update_surfaces_to_stream(dm->dc);
+
+ r = amdgpu_dpm_pause_power_profile(adev, true);
+ if (r)
+ dev_warn(adev->dev, "failed to set default power profile mode\n");
+
dc_allow_idle_optimizations(dm->dc, true);
+
+ r = amdgpu_dpm_pause_power_profile(adev, false);
+ if (r)
+ dev_warn(adev->dev, "failed to restore the power profile mode\n");
}
mutex_unlock(&dm->dc_lock);
@@ -297,8 +308,12 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
int irq_type;
int rc = 0;
- if (acrtc->otg_inst == -1)
- goto skip;
+ if (enable && !acrtc->base.enabled) {
+ drm_dbg_vbl(crtc->dev,
+ "Reject vblank enable on unconfigured CRTC %d (enabled=%d)\n",
+ acrtc->crtc_id, acrtc->base.enabled);
+ return -EINVAL;
+ }
irq_type = amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
@@ -383,7 +398,7 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
return rc;
}
#endif
-skip:
+
if (amdgpu_in_reset(adev))
return 0;
@@ -721,7 +736,7 @@ int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
{
struct amdgpu_crtc *acrtc = NULL;
struct drm_plane *cursor_plane;
- bool is_dcn;
+ bool has_degamma;
int res = -ENOMEM;
cursor_plane = kzalloc(sizeof(*cursor_plane), GFP_KERNEL);
@@ -760,20 +775,18 @@ int amdgpu_dm_crtc_init(struct amdgpu_display_manager *dm,
dm->adev->mode_info.crtcs[crtc_index] = acrtc;
- /* Don't enable DRM CRTC degamma property for DCE since it doesn't
- * support programmable degamma anywhere.
- */
- is_dcn = dm->adev->dm.dc->caps.color.dpp.dcn_arch;
- /* Dont't enable DRM CRTC degamma property for DCN401 since the
- * pre-blending degamma LUT doesn't apply to cursor, and therefore
- * can't work similar to a post-blending degamma LUT as in other hw
- * versions.
- * TODO: revisit it once KMS plane color API is merged.
+ /* Don't enable DRM CRTC degamma property for
+ * 1. Degamma is replaced by color pipeline.
+ * 2. DCE since it doesn't support programmable degamma anywhere.
+ * 3. DCN401 since pre-blending degamma LUT doesn't apply to cursor.
*/
- drm_crtc_enable_color_mgmt(&acrtc->base,
- (is_dcn &&
- dm->adev->dm.dc->ctx->dce_version != DCN_VERSION_4_01) ?
- MAX_COLOR_LUT_ENTRIES : 0,
+ if (plane->color_pipeline_property)
+ has_degamma = false;
+ else
+ has_degamma = dm->adev->dm.dc->caps.color.dpp.dcn_arch &&
+ dm->adev->dm.dc->ctx->dce_version != DCN_VERSION_4_01;
+
+ drm_crtc_enable_color_mgmt(&acrtc->base, has_degamma ? MAX_COLOR_LUT_ENTRIES : 0,
true, MAX_COLOR_LUT_ENTRIES);
drm_mode_crtc_set_gamma_size(&acrtc->base, MAX_COLOR_LEGACY_LUT_ENTRIES);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
index f263e1a4537e..a9839485f2a2 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
@@ -759,6 +759,7 @@ static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __us
int max_param_num = 11;
enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
bool disable_hpd = false;
+ bool supports_hpd = link->irq_source_hpd != DC_IRQ_SOURCE_INVALID;
bool valid_test_pattern = false;
uint8_t param_nums = 0;
/* init with default 80bit custom pattern */
@@ -850,7 +851,7 @@ static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __us
* because it might have been disabled after a test pattern was set.
* AUX depends on HPD * sequence dependent, do not move!
*/
- if (!disable_hpd)
+ if (supports_hpd && !disable_hpd)
dc_link_enable_hpd(link);
prefer_link_settings.lane_count = link->verified_link_cap.lane_count;
@@ -888,7 +889,7 @@ static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __us
* Need disable interrupt to avoid SW driver disable DP output. This is
* done after the test pattern is set.
*/
- if (valid_test_pattern && disable_hpd)
+ if (valid_test_pattern && supports_hpd && disable_hpd)
dc_link_disable_hpd(link);
kfree(wr_buf);
@@ -1302,7 +1303,8 @@ static int odm_combine_segments_show(struct seq_file *m, void *unused)
if (connector->status != connector_status_connected)
return -ENODEV;
- if (pipe_ctx != NULL && pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments)
+ if (pipe_ctx && pipe_ctx->stream_res.tg &&
+ pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments)
pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments(pipe_ctx->stream_res.tg, &segments);
seq_printf(m, "%d\n", segments);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
index 19038f336155..85ce558cefc5 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
@@ -201,6 +201,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
struct mod_hdcp_link_adjustment link_adjust;
struct mod_hdcp_display_adjustment display_adjust;
unsigned int conn_index = aconnector->base.index;
+ const struct dc *dc = aconnector->dc_link->dc;
guard(mutex)(&hdcp_w->mutex);
drm_connector_get(&aconnector->base);
@@ -231,6 +232,9 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
link_adjust.hdcp1.disable = 1;
link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1;
}
+ link_adjust.hdcp2.use_fw_locality_check =
+ (dc->caps.fused_io_supported || dc->debug.hdcp_lc_force_fw_enable);
+ link_adjust.hdcp2.use_sw_locality_fallback = dc->debug.hdcp_lc_enable_sw_fallback;
schedule_delayed_work(&hdcp_w->property_validate_dwork,
msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
@@ -534,6 +538,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
struct dc_sink *sink = NULL;
bool link_is_hdcp14 = false;
+ const struct dc *dc = aconnector->dc_link->dc;
if (config->dpms_off) {
hdcp_remove_display(hdcp_work, link_index, aconnector);
@@ -575,6 +580,8 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
link->adjust.auth_delay = 2;
link->adjust.retry_limit = MAX_NUM_OF_ATTEMPTS;
link->adjust.hdcp1.disable = 0;
+ link->adjust.hdcp2.use_fw_locality_check = (dc->caps.fused_io_supported || dc->debug.hdcp_lc_force_fw_enable);
+ link->adjust.hdcp2.use_sw_locality_fallback = dc->debug.hdcp_lc_enable_sw_fallback;
hdcp_w->encryption_status[display->index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
DRM_DEBUG_DRIVER("[HDCP_DM] display %d, CP %d, type %d\n", aconnector->base.index,
@@ -786,15 +793,8 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,
ddc_funcs->read_i2c = lp_read_i2c;
ddc_funcs->write_dpcd = lp_write_dpcd;
ddc_funcs->read_dpcd = lp_read_dpcd;
-
- config->debug.lc_enable_sw_fallback = dc->debug.hdcp_lc_enable_sw_fallback;
- if (dc->caps.fused_io_supported || dc->debug.hdcp_lc_force_fw_enable) {
- ddc_funcs->atomic_write_poll_read_i2c = lp_atomic_write_poll_read_i2c;
- ddc_funcs->atomic_write_poll_read_aux = lp_atomic_write_poll_read_aux;
- } else {
- ddc_funcs->atomic_write_poll_read_i2c = NULL;
- ddc_funcs->atomic_write_poll_read_aux = NULL;
- }
+ ddc_funcs->atomic_write_poll_read_i2c = lp_atomic_write_poll_read_i2c;
+ ddc_funcs->atomic_write_poll_read_aux = lp_atomic_write_poll_read_aux;
memset(hdcp_work[i].aconnector, 0,
sizeof(struct amdgpu_dm_connector *) *
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 fe100e4c9801..ac98c746c3de 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
@@ -83,6 +83,7 @@ static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct
edid_caps->panel_patch.remove_sink_ext_caps = true;
break;
case drm_edid_encode_panel_id('S', 'D', 'C', 0x4154):
+ case drm_edid_encode_panel_id('S', 'D', 'C', 0x4171):
drm_dbg_driver(dev, "Disabling VSC on monitor with panel id %X\n", panel_id);
edid_caps->panel_patch.disable_colorimetry = true;
break;
@@ -130,6 +131,7 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
edid_caps->serial_number = edid_buf->serial;
edid_caps->manufacture_week = edid_buf->mfg_week;
edid_caps->manufacture_year = edid_buf->mfg_year;
+ edid_caps->analog = !(edid_buf->input & DRM_EDID_INPUT_DIGITAL);
drm_edid_get_monitor_name(edid_buf,
edid_caps->display_name,
@@ -996,8 +998,8 @@ enum dc_edid_status dm_helpers_read_local_edid(
struct amdgpu_dm_connector *aconnector = link->priv;
struct drm_connector *connector = &aconnector->base;
struct i2c_adapter *ddc;
- int retry = 3;
- enum dc_edid_status edid_status;
+ int retry = 25;
+ enum dc_edid_status edid_status = EDID_NO_RESPONSE;
const struct drm_edid *drm_edid;
const struct edid *edid;
@@ -1027,7 +1029,7 @@ enum dc_edid_status dm_helpers_read_local_edid(
}
if (!drm_edid)
- return EDID_NO_RESPONSE;
+ continue;
edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
if (!edid ||
@@ -1045,7 +1047,7 @@ enum dc_edid_status dm_helpers_read_local_edid(
&sink->dc_edid,
&sink->edid_caps);
- } while (edid_status == EDID_BAD_CHECKSUM && --retry > 0);
+ } while ((edid_status == EDID_BAD_CHECKSUM || edid_status == EDID_NO_RESPONSE) && --retry > 0);
if (edid_status != EDID_OK)
DRM_ERROR("EDID err: %d, on connector: %s",
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
index a1c722112c22..0a2a3f233a0e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
@@ -476,6 +476,7 @@ void amdgpu_dm_irq_fini(struct amdgpu_device *adev)
void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
{
+ struct drm_device *dev = adev_to_drm(adev);
int src;
struct list_head *hnd_list_h;
struct list_head *hnd_list_l;
@@ -512,6 +513,9 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
}
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
+
+ if (dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_disable(dev);
}
void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
@@ -537,6 +541,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
{
+ struct drm_device *dev = adev_to_drm(adev);
int src;
struct list_head *hnd_list_h, *hnd_list_l;
unsigned long irq_table_flags;
@@ -557,6 +562,9 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
}
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
+
+ if (dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_enable(dev);
}
/*
@@ -893,6 +901,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
struct drm_connector_list_iter iter;
int irq_type;
int i;
+ bool use_polling = false;
/* First, clear all hpd and hpdrx interrupts */
for (i = DC_IRQ_SOURCE_HPD1; i <= DC_IRQ_SOURCE_HPD6RX; i++) {
@@ -906,6 +915,8 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
struct amdgpu_dm_connector *amdgpu_dm_connector;
const struct dc_link *dc_link;
+ use_polling |= connector->polled != DRM_CONNECTOR_POLL_HPD;
+
if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
continue;
@@ -947,6 +958,9 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
}
}
drm_connector_list_iter_end(&iter);
+
+ if (use_polling)
+ drm_kms_helper_poll_init(dev);
}
/**
@@ -997,4 +1011,7 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
}
}
drm_connector_list_iter_end(&iter);
+
+ if (dev->mode_config.poll_enabled)
+ drm_kms_helper_poll_fini(dev);
}
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 5e92eaa67aa3..dbd1da4d85d3 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
@@ -884,26 +884,28 @@ struct dsc_mst_fairness_params {
};
#if defined(CONFIG_DRM_AMD_DC_FP)
-static uint16_t get_fec_overhead_multiplier(struct dc_link *dc_link)
+static uint64_t kbps_to_pbn(int kbps, bool is_peak_pbn)
{
- u8 link_coding_cap;
- uint16_t fec_overhead_multiplier_x1000 = PBN_FEC_OVERHEAD_MULTIPLIER_8B_10B;
+ uint64_t effective_kbps = (uint64_t)kbps;
- link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(dc_link);
- if (link_coding_cap == DP_128b_132b_ENCODING)
- fec_overhead_multiplier_x1000 = PBN_FEC_OVERHEAD_MULTIPLIER_128B_132B;
+ if (is_peak_pbn) { // add 0.6% (1006/1000) overhead into effective kbps
+ effective_kbps *= 1006;
+ effective_kbps = div_u64(effective_kbps, 1000);
+ }
- return fec_overhead_multiplier_x1000;
+ return (uint64_t) DIV64_U64_ROUND_UP(effective_kbps * 64, (54 * 8 * 1000));
}
-static int kbps_to_peak_pbn(int kbps, uint16_t fec_overhead_multiplier_x1000)
+static uint32_t pbn_to_kbps(unsigned int pbn, bool with_margin)
{
- u64 peak_kbps = kbps;
+ uint64_t pbn_effective = (uint64_t)pbn;
+
+ if (with_margin) // deduct 0.6% (994/1000) overhead from effective pbn
+ pbn_effective *= (1000000 / PEAK_FACTOR_X1000);
+ else
+ pbn_effective *= 1000;
- peak_kbps *= 1006;
- peak_kbps *= fec_overhead_multiplier_x1000;
- peak_kbps = div_u64(peak_kbps, 1000 * 1000);
- return (int) DIV64_U64_ROUND_UP(peak_kbps * 64, (54 * 8 * 1000));
+ return DIV_U64_ROUND_UP(pbn_effective * 8 * 54, 64);
}
static void set_dsc_configs_from_fairness_vars(struct dsc_mst_fairness_params *params,
@@ -974,7 +976,7 @@ static int bpp_x16_from_pbn(struct dsc_mst_fairness_params param, int pbn)
dc_dsc_get_default_config_option(param.sink->ctx->dc, &dsc_options);
dsc_options.max_target_bpp_limit_override_x16 = drm_connector->display_info.max_dsc_bpp * 16;
- kbps = div_u64((u64)pbn * 994 * 8 * 54, 64);
+ kbps = pbn_to_kbps(pbn, false);
dc_dsc_compute_config(
param.sink->ctx->dc->res_pool->dscs[0],
&param.sink->dsc_caps.dsc_dec_caps,
@@ -1003,12 +1005,11 @@ static int increase_dsc_bpp(struct drm_atomic_state *state,
int link_timeslots_used;
int fair_pbn_alloc;
int ret = 0;
- uint16_t fec_overhead_multiplier_x1000 = get_fec_overhead_multiplier(dc_link);
for (i = 0; i < count; i++) {
if (vars[i + k].dsc_enabled) {
initial_slack[i] =
- kbps_to_peak_pbn(params[i].bw_range.max_kbps, fec_overhead_multiplier_x1000) - vars[i + k].pbn;
+ kbps_to_pbn(params[i].bw_range.max_kbps, false) - vars[i + k].pbn;
bpp_increased[i] = false;
remaining_to_increase += 1;
} else {
@@ -1104,7 +1105,6 @@ static int try_disable_dsc(struct drm_atomic_state *state,
int next_index;
int remaining_to_try = 0;
int ret;
- uint16_t fec_overhead_multiplier_x1000 = get_fec_overhead_multiplier(dc_link);
int var_pbn;
for (i = 0; i < count; i++) {
@@ -1137,7 +1137,7 @@ static int try_disable_dsc(struct drm_atomic_state *state,
DRM_DEBUG_DRIVER("MST_DSC index #%d, try no compression\n", next_index);
var_pbn = vars[next_index].pbn;
- vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.stream_kbps, fec_overhead_multiplier_x1000);
+ vars[next_index].pbn = kbps_to_pbn(params[next_index].bw_range.stream_kbps, true);
ret = drm_dp_atomic_find_time_slots(state,
params[next_index].port->mgr,
params[next_index].port,
@@ -1197,7 +1197,6 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
int count = 0;
int i, k, ret;
bool debugfs_overwrite = false;
- uint16_t fec_overhead_multiplier_x1000 = get_fec_overhead_multiplier(dc_link);
struct drm_connector_state *new_conn_state;
memset(params, 0, sizeof(params));
@@ -1278,7 +1277,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
DRM_DEBUG_DRIVER("MST_DSC Try no compression\n");
for (i = 0; i < count; i++) {
vars[i + k].aconnector = params[i].aconnector;
- vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps, fec_overhead_multiplier_x1000);
+ vars[i + k].pbn = kbps_to_pbn(params[i].bw_range.stream_kbps, false);
vars[i + k].dsc_enabled = false;
vars[i + k].bpp_x16 = 0;
ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr, params[i].port,
@@ -1300,7 +1299,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
DRM_DEBUG_DRIVER("MST_DSC Try max compression\n");
for (i = 0; i < count; i++) {
if (params[i].compression_possible && params[i].clock_force_enable != DSC_CLK_FORCE_DISABLE) {
- vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.min_kbps, fec_overhead_multiplier_x1000);
+ vars[i + k].pbn = kbps_to_pbn(params[i].bw_range.min_kbps, false);
vars[i + k].dsc_enabled = true;
vars[i + k].bpp_x16 = params[i].bw_range.min_target_bpp_x16;
ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr,
@@ -1308,7 +1307,7 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
if (ret < 0)
return ret;
} else {
- vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps, fec_overhead_multiplier_x1000);
+ vars[i + k].pbn = kbps_to_pbn(params[i].bw_range.stream_kbps, false);
vars[i + k].dsc_enabled = false;
vars[i + k].bpp_x16 = 0;
ret = drm_dp_atomic_find_time_slots(state, params[i].port->mgr,
@@ -1763,18 +1762,6 @@ clean_exit:
return ret;
}
-static uint32_t kbps_from_pbn(unsigned int pbn)
-{
- uint64_t kbps = (uint64_t)pbn;
-
- kbps *= (1000000 / PEAK_FACTOR_X1000);
- kbps *= 8;
- kbps *= 54;
- kbps /= 64;
-
- return (uint32_t)kbps;
-}
-
static bool is_dsc_common_config_possible(struct dc_stream_state *stream,
struct dc_dsc_bw_range *bw_range)
{
@@ -1873,7 +1860,7 @@ enum dc_status dm_dp_mst_is_port_support_mode(
dc_link_get_highest_encoding_format(stream->link));
cur_link_settings = stream->link->verified_link_cap;
root_link_bw_in_kbps = dc_link_bandwidth_kbps(aconnector->dc_link, &cur_link_settings);
- virtual_channel_bw_in_kbps = kbps_from_pbn(aconnector->mst_output_port->full_pbn);
+ virtual_channel_bw_in_kbps = pbn_to_kbps(aconnector->mst_output_port->full_pbn, true);
/* pick the end to end bw bottleneck */
end_to_end_bw_in_kbps = min(root_link_bw_in_kbps, virtual_channel_bw_in_kbps);
@@ -1926,7 +1913,7 @@ enum dc_status dm_dp_mst_is_port_support_mode(
immediate_upstream_port = aconnector->mst_output_port->parent->port_parent;
if (immediate_upstream_port) {
- virtual_channel_bw_in_kbps = kbps_from_pbn(immediate_upstream_port->full_pbn);
+ virtual_channel_bw_in_kbps = pbn_to_kbps(immediate_upstream_port->full_pbn, true);
virtual_channel_bw_in_kbps = min(root_link_bw_in_kbps, virtual_channel_bw_in_kbps);
} else {
/* For topology LCT 1 case - only one mstb*/
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
index e027798ece03..2e3ee78999d9 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
@@ -37,6 +37,7 @@
#include "amdgpu_display.h"
#include "amdgpu_dm_trace.h"
#include "amdgpu_dm_plane.h"
+#include "amdgpu_dm_colorop.h"
#include "gc/gc_11_0_0_offset.h"
#include "gc/gc_11_0_0_sh_mask.h"
@@ -1782,6 +1783,39 @@ dm_atomic_plane_get_property(struct drm_plane *plane,
return 0;
}
+#else
+
+#define MAX_COLOR_PIPELINES 5
+
+static int
+dm_plane_init_colorops(struct drm_plane *plane)
+{
+ struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
+ struct drm_device *dev = plane->dev;
+ struct amdgpu_device *adev = drm_to_adev(dev);
+ struct dc *dc = adev->dm.dc;
+ int len = 0;
+ int ret;
+
+ if (plane->type == DRM_PLANE_TYPE_CURSOR)
+ return 0;
+
+ /* initialize pipeline */
+ if (dc->ctx->dce_version >= DCN_VERSION_3_0) {
+ ret = amdgpu_dm_initialize_default_pipeline(plane, &pipelines[len]);
+ if (ret) {
+ drm_err(plane->dev, "Failed to create color pipeline for plane %d: %d\n",
+ plane->base.id, ret);
+ return ret;
+ }
+ len++;
+
+ /* Create COLOR_PIPELINE property and attach */
+ drm_plane_create_color_pipeline_property(plane, pipelines, len);
+ }
+
+ return 0;
+}
#endif
static const struct drm_plane_funcs dm_plane_funcs = {
@@ -1890,7 +1924,12 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
#ifdef AMD_PRIVATE_COLOR
dm_atomic_plane_attach_color_mgmt_properties(dm, plane);
+#else
+ res = dm_plane_init_colorops(plane);
+ if (res)
+ return res;
#endif
+
/* Create (reset) the plane state */
if (plane->funcs->reset)
plane->funcs->reset(plane);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c
index 80704d709e44..da94e3544b65 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c
@@ -162,7 +162,7 @@ bool amdgpu_dm_replay_enable(struct dc_stream_state *stream, bool wait)
if (link) {
link->dc->link_srv->edp_setup_replay(link, stream);
- link->dc->link_srv->edp_set_coasting_vtotal(link, stream->timing.v_total);
+ link->dc->link_srv->edp_set_coasting_vtotal(link, stream->timing.v_total, 0);
DRM_DEBUG_DRIVER("Enabling replay...\n");
link->dc->link_srv->edp_set_replay_allow_active(link, &replay_active, wait, false, NULL);
return true;
diff --git a/drivers/gpu/drm/amd/display/dc/Makefile b/drivers/gpu/drm/amd/display/dc/Makefile
index dc943abd6dba..7277ed21552f 100644
--- a/drivers/gpu/drm/amd/display/dc/Makefile
+++ b/drivers/gpu/drm/amd/display/dc/Makefile
@@ -36,7 +36,7 @@ DC_LIBS += dcn30
DC_LIBS += dcn301
DC_LIBS += dcn31
DC_LIBS += dml
-DC_LIBS += dml2
+DC_LIBS += dml2_0
DC_LIBS += soc_and_ip_translator
endif
diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c
index 154fd2c18e88..d1471f34e419 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c
+++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c
@@ -67,7 +67,9 @@ static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp,
ATOM_OBJECT *object);
static struct device_id device_type_from_device_id(uint16_t device_id);
static uint32_t signal_to_ss_id(enum as_signal_type signal);
-static uint32_t get_support_mask_for_device_id(struct device_id device_id);
+static uint32_t get_support_mask_for_device_id(
+ enum dal_device_type device_type,
+ uint32_t enum_id);
static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record(
struct bios_parser *bp,
ATOM_OBJECT *object);
@@ -441,6 +443,7 @@ static enum bp_result get_firmware_info_v1_4(
le32_to_cpu(firmware_info->ulMinPixelClockPLL_Output) * 10;
info->pll_info.max_output_pxl_clk_pll_frequency =
le32_to_cpu(firmware_info->ulMaxPixelClockPLL_Output) * 10;
+ info->max_pixel_clock = le16_to_cpu(firmware_info->usMaxPixelClock) * 10;
if (firmware_info->usFirmwareCapability.sbfAccess.MemoryClockSS_Support)
/* Since there is no information on the SS, report conservative
@@ -497,6 +500,7 @@ static enum bp_result get_firmware_info_v2_1(
info->external_clock_source_frequency_for_dp =
le16_to_cpu(firmwareInfo->usUniphyDPModeExtClkFreq) * 10;
info->min_allowed_bl_level = firmwareInfo->ucMinAllowedBL_Level;
+ info->max_pixel_clock = le16_to_cpu(firmwareInfo->usMaxPixelClock) * 10;
/* There should be only one entry in the SS info table for Memory Clock
*/
@@ -736,18 +740,94 @@ static enum bp_result bios_parser_transmitter_control(
return bp->cmd_tbl.transmitter_control(bp, cntl);
}
+static enum bp_result bios_parser_select_crtc_source(
+ struct dc_bios *dcb,
+ struct bp_crtc_source_select *bp_params)
+{
+ struct bios_parser *bp = BP_FROM_DCB(dcb);
+
+ if (!bp->cmd_tbl.select_crtc_source)
+ return BP_RESULT_FAILURE;
+
+ return bp->cmd_tbl.select_crtc_source(bp, bp_params);
+}
+
static enum bp_result bios_parser_encoder_control(
struct dc_bios *dcb,
struct bp_encoder_control *cntl)
{
struct bios_parser *bp = BP_FROM_DCB(dcb);
+ if (cntl->engine_id == ENGINE_ID_DACA) {
+ if (!bp->cmd_tbl.dac1_encoder_control)
+ return BP_RESULT_FAILURE;
+
+ return bp->cmd_tbl.dac1_encoder_control(
+ bp, cntl->action == ENCODER_CONTROL_ENABLE,
+ cntl->pixel_clock, ATOM_DAC1_PS2);
+ } else if (cntl->engine_id == ENGINE_ID_DACB) {
+ if (!bp->cmd_tbl.dac2_encoder_control)
+ return BP_RESULT_FAILURE;
+
+ return bp->cmd_tbl.dac2_encoder_control(
+ bp, cntl->action == ENCODER_CONTROL_ENABLE,
+ cntl->pixel_clock, ATOM_DAC1_PS2);
+ }
+
if (!bp->cmd_tbl.dig_encoder_control)
return BP_RESULT_FAILURE;
return bp->cmd_tbl.dig_encoder_control(bp, cntl);
}
+static enum bp_result bios_parser_dac_load_detection(
+ struct dc_bios *dcb,
+ enum engine_id engine_id,
+ enum dal_device_type device_type,
+ uint32_t enum_id)
+{
+ struct bios_parser *bp = BP_FROM_DCB(dcb);
+ struct dc_context *ctx = dcb->ctx;
+ struct bp_load_detection_parameters bp_params = {0};
+ enum bp_result bp_result;
+ uint32_t bios_0_scratch;
+ uint32_t device_id_mask = 0;
+
+ bp_params.engine_id = engine_id;
+ bp_params.device_id = get_support_mask_for_device_id(device_type, enum_id);
+
+ if (engine_id != ENGINE_ID_DACA &&
+ engine_id != ENGINE_ID_DACB)
+ return BP_RESULT_UNSUPPORTED;
+
+ if (!bp->cmd_tbl.dac_load_detection)
+ return BP_RESULT_UNSUPPORTED;
+
+ if (bp_params.device_id == ATOM_DEVICE_CRT1_SUPPORT)
+ device_id_mask = ATOM_S0_CRT1_MASK;
+ else if (bp_params.device_id == ATOM_DEVICE_CRT2_SUPPORT)
+ device_id_mask = ATOM_S0_CRT2_MASK;
+ else
+ return BP_RESULT_UNSUPPORTED;
+
+ /* BIOS will write the detected devices to BIOS_SCRATCH_0, clear corresponding bit */
+ bios_0_scratch = dm_read_reg(ctx, bp->base.regs->BIOS_SCRATCH_0);
+ bios_0_scratch &= ~device_id_mask;
+ dm_write_reg(ctx, bp->base.regs->BIOS_SCRATCH_0, bios_0_scratch);
+
+ bp_result = bp->cmd_tbl.dac_load_detection(bp, &bp_params);
+
+ if (bp_result != BP_RESULT_OK)
+ return bp_result;
+
+ bios_0_scratch = dm_read_reg(ctx, bp->base.regs->BIOS_SCRATCH_0);
+
+ if (bios_0_scratch & device_id_mask)
+ return BP_RESULT_OK;
+
+ return BP_RESULT_FAILURE;
+}
+
static enum bp_result bios_parser_adjust_pixel_clock(
struct dc_bios *dcb,
struct bp_adjust_pixel_clock_parameters *bp_params)
@@ -858,7 +938,7 @@ static bool bios_parser_is_device_id_supported(
{
struct bios_parser *bp = BP_FROM_DCB(dcb);
- uint32_t mask = get_support_mask_for_device_id(id);
+ uint32_t mask = get_support_mask_for_device_id(id.device_type, id.enum_id);
return (le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport) & mask) != 0;
}
@@ -2149,11 +2229,10 @@ static uint32_t signal_to_ss_id(enum as_signal_type signal)
return clk_id_ss;
}
-static uint32_t get_support_mask_for_device_id(struct device_id device_id)
+static uint32_t get_support_mask_for_device_id(
+ enum dal_device_type device_type,
+ uint32_t enum_id)
{
- enum dal_device_type device_type = device_id.device_type;
- uint32_t enum_id = device_id.enum_id;
-
switch (device_type) {
case DEVICE_TYPE_LCD:
switch (enum_id) {
@@ -2829,8 +2908,12 @@ static const struct dc_vbios_funcs vbios_funcs = {
.is_device_id_supported = bios_parser_is_device_id_supported,
/* COMMANDS */
+ .select_crtc_source = bios_parser_select_crtc_source,
+
.encoder_control = bios_parser_encoder_control,
+ .dac_load_detection = bios_parser_dac_load_detection,
+
.transmitter_control = bios_parser_transmitter_control,
.enable_crtc = bios_parser_enable_crtc,
diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
index 04eb647acc4e..550a9f1d03f8 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
+++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c
@@ -1480,10 +1480,10 @@ static enum bp_result get_embedded_panel_info_v2_1(
/* not provided by VBIOS */
info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = 0;
- info->lcd_timing.misc_info.H_SYNC_POLARITY = ~(uint32_t) (lvds->lcd_timing.miscinfo
- & ATOM_HSYNC_POLARITY);
- info->lcd_timing.misc_info.V_SYNC_POLARITY = ~(uint32_t) (lvds->lcd_timing.miscinfo
- & ATOM_VSYNC_POLARITY);
+ info->lcd_timing.misc_info.H_SYNC_POLARITY = !(lvds->lcd_timing.miscinfo &
+ ATOM_HSYNC_POLARITY);
+ info->lcd_timing.misc_info.V_SYNC_POLARITY = !(lvds->lcd_timing.miscinfo &
+ ATOM_VSYNC_POLARITY);
/* not provided by VBIOS */
info->lcd_timing.misc_info.VERTICAL_CUT_OFF = 0;
diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.c b/drivers/gpu/drm/amd/display/dc/bios/command_table.c
index 58e88778da7f..22457f417e65 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/command_table.c
+++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.c
@@ -52,7 +52,9 @@ static void init_transmitter_control(struct bios_parser *bp);
static void init_set_pixel_clock(struct bios_parser *bp);
static void init_enable_spread_spectrum_on_ppll(struct bios_parser *bp);
static void init_adjust_display_pll(struct bios_parser *bp);
+static void init_select_crtc_source(struct bios_parser *bp);
static void init_dac_encoder_control(struct bios_parser *bp);
+static void init_dac_load_detection(struct bios_parser *bp);
static void init_dac_output_control(struct bios_parser *bp);
static void init_set_crtc_timing(struct bios_parser *bp);
static void init_enable_crtc(struct bios_parser *bp);
@@ -69,7 +71,9 @@ void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp)
init_set_pixel_clock(bp);
init_enable_spread_spectrum_on_ppll(bp);
init_adjust_display_pll(bp);
+ init_select_crtc_source(bp);
init_dac_encoder_control(bp);
+ init_dac_load_detection(bp);
init_dac_output_control(bp);
init_set_crtc_timing(bp);
init_enable_crtc(bp);
@@ -1612,6 +1616,198 @@ static enum bp_result adjust_display_pll_v3(
/*******************************************************************************
********************************************************************************
**
+ ** SELECT CRTC SOURCE
+ **
+ ********************************************************************************
+ *******************************************************************************/
+
+static enum bp_result select_crtc_source_v1(
+ struct bios_parser *bp,
+ struct bp_crtc_source_select *bp_params);
+static enum bp_result select_crtc_source_v2(
+ struct bios_parser *bp,
+ struct bp_crtc_source_select *bp_params);
+static enum bp_result select_crtc_source_v3(
+ struct bios_parser *bp,
+ struct bp_crtc_source_select *bp_params);
+
+static void init_select_crtc_source(struct bios_parser *bp)
+{
+ switch (BIOS_CMD_TABLE_PARA_REVISION(SelectCRTC_Source)) {
+ case 1:
+ bp->cmd_tbl.select_crtc_source = select_crtc_source_v1;
+ break;
+ case 2:
+ bp->cmd_tbl.select_crtc_source = select_crtc_source_v2;
+ break;
+ case 3:
+ bp->cmd_tbl.select_crtc_source = select_crtc_source_v3;
+ break;
+ default:
+ bp->cmd_tbl.select_crtc_source = NULL;
+ break;
+ }
+}
+
+static enum bp_result select_crtc_source_v1(
+ struct bios_parser *bp,
+ struct bp_crtc_source_select *bp_params)
+{
+ enum bp_result result = BP_RESULT_FAILURE;
+ SELECT_CRTC_SOURCE_PS_ALLOCATION params;
+
+ if (!bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, &params.ucCRTC))
+ return BP_RESULT_BADINPUT;
+
+ switch (bp_params->engine_id) {
+ case ENGINE_ID_DACA:
+ params.ucDevice = ATOM_DEVICE_CRT1_INDEX;
+ break;
+ case ENGINE_ID_DACB:
+ params.ucDevice = ATOM_DEVICE_CRT2_INDEX;
+ break;
+ default:
+ return BP_RESULT_BADINPUT;
+ }
+
+ if (EXEC_BIOS_CMD_TABLE(SelectCRTC_Source, params))
+ result = BP_RESULT_OK;
+
+ return result;
+}
+
+static bool select_crtc_source_v2_encoder_id(
+ enum engine_id engine_id, uint8_t *out_encoder_id)
+{
+ uint8_t encoder_id = 0;
+
+ switch (engine_id) {
+ case ENGINE_ID_DIGA:
+ encoder_id = ASIC_INT_DIG1_ENCODER_ID;
+ break;
+ case ENGINE_ID_DIGB:
+ encoder_id = ASIC_INT_DIG2_ENCODER_ID;
+ break;
+ case ENGINE_ID_DIGC:
+ encoder_id = ASIC_INT_DIG3_ENCODER_ID;
+ break;
+ case ENGINE_ID_DIGD:
+ encoder_id = ASIC_INT_DIG4_ENCODER_ID;
+ break;
+ case ENGINE_ID_DIGE:
+ encoder_id = ASIC_INT_DIG5_ENCODER_ID;
+ break;
+ case ENGINE_ID_DIGF:
+ encoder_id = ASIC_INT_DIG6_ENCODER_ID;
+ break;
+ case ENGINE_ID_DIGG:
+ encoder_id = ASIC_INT_DIG7_ENCODER_ID;
+ break;
+ case ENGINE_ID_DACA:
+ encoder_id = ASIC_INT_DAC1_ENCODER_ID;
+ break;
+ case ENGINE_ID_DACB:
+ encoder_id = ASIC_INT_DAC2_ENCODER_ID;
+ break;
+ default:
+ return false;
+ }
+
+ *out_encoder_id = encoder_id;
+ return true;
+}
+
+static bool select_crtc_source_v2_encoder_mode(
+ enum signal_type signal_type, uint8_t *out_encoder_mode)
+{
+ uint8_t encoder_mode = 0;
+
+ switch (signal_type) {
+ case SIGNAL_TYPE_DVI_SINGLE_LINK:
+ case SIGNAL_TYPE_DVI_DUAL_LINK:
+ encoder_mode = ATOM_ENCODER_MODE_DVI;
+ break;
+ case SIGNAL_TYPE_HDMI_TYPE_A:
+ encoder_mode = ATOM_ENCODER_MODE_HDMI;
+ break;
+ case SIGNAL_TYPE_LVDS:
+ encoder_mode = ATOM_ENCODER_MODE_LVDS;
+ break;
+ case SIGNAL_TYPE_RGB:
+ encoder_mode = ATOM_ENCODER_MODE_CRT;
+ break;
+ case SIGNAL_TYPE_DISPLAY_PORT:
+ encoder_mode = ATOM_ENCODER_MODE_DP;
+ break;
+ case SIGNAL_TYPE_DISPLAY_PORT_MST:
+ encoder_mode = ATOM_ENCODER_MODE_DP_MST;
+ break;
+ case SIGNAL_TYPE_EDP:
+ encoder_mode = ATOM_ENCODER_MODE_DP;
+ break;
+ default:
+ return false;
+ }
+
+ *out_encoder_mode = encoder_mode;
+ return true;
+}
+
+static enum bp_result select_crtc_source_v2(
+ struct bios_parser *bp,
+ struct bp_crtc_source_select *bp_params)
+{
+ enum bp_result result = BP_RESULT_FAILURE;
+ SELECT_CRTC_SOURCE_PARAMETERS_V3 params;
+
+ if (!bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, &params.ucCRTC))
+ return BP_RESULT_BADINPUT;
+
+ if (!select_crtc_source_v2_encoder_id(
+ bp_params->engine_id,
+ &params.ucEncoderID))
+ return BP_RESULT_BADINPUT;
+ if (!select_crtc_source_v2_encoder_mode(
+ bp_params->sink_signal,
+ &params.ucEncodeMode))
+ return BP_RESULT_BADINPUT;
+
+ if (EXEC_BIOS_CMD_TABLE(SelectCRTC_Source, params))
+ result = BP_RESULT_OK;
+
+ return result;
+}
+
+static enum bp_result select_crtc_source_v3(
+ struct bios_parser *bp,
+ struct bp_crtc_source_select *bp_params)
+{
+ enum bp_result result = BP_RESULT_FAILURE;
+ SELECT_CRTC_SOURCE_PARAMETERS_V3 params;
+
+ if (!bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, &params.ucCRTC))
+ return BP_RESULT_BADINPUT;
+
+ if (!select_crtc_source_v2_encoder_id(
+ bp_params->engine_id,
+ &params.ucEncoderID))
+ return BP_RESULT_BADINPUT;
+ if (!select_crtc_source_v2_encoder_mode(
+ bp_params->sink_signal,
+ &params.ucEncodeMode))
+ return BP_RESULT_BADINPUT;
+
+ params.ucDstBpc = bp_params->bit_depth;
+
+ if (EXEC_BIOS_CMD_TABLE(SelectCRTC_Source, params))
+ result = BP_RESULT_OK;
+
+ return result;
+}
+
+/*******************************************************************************
+ ********************************************************************************
+ **
** DAC ENCODER CONTROL
**
********************************************************************************
@@ -1711,6 +1907,96 @@ static enum bp_result dac2_encoder_control_v1(
/*******************************************************************************
********************************************************************************
**
+ ** DAC LOAD DETECTION
+ **
+ ********************************************************************************
+ *******************************************************************************/
+
+static enum bp_result dac_load_detection_v1(
+ struct bios_parser *bp,
+ struct bp_load_detection_parameters *bp_params);
+
+static enum bp_result dac_load_detection_v3(
+ struct bios_parser *bp,
+ struct bp_load_detection_parameters *bp_params);
+
+static void init_dac_load_detection(struct bios_parser *bp)
+{
+ switch (BIOS_CMD_TABLE_PARA_REVISION(DAC_LoadDetection)) {
+ case 1:
+ case 2:
+ bp->cmd_tbl.dac_load_detection = dac_load_detection_v1;
+ break;
+ case 3:
+ default:
+ bp->cmd_tbl.dac_load_detection = dac_load_detection_v3;
+ break;
+ }
+}
+
+static void dac_load_detect_prepare_params(
+ struct _DAC_LOAD_DETECTION_PS_ALLOCATION *params,
+ enum engine_id engine_id,
+ uint16_t device_id,
+ uint8_t misc)
+{
+ uint8_t dac_type = ENGINE_ID_DACA;
+
+ if (engine_id == ENGINE_ID_DACB)
+ dac_type = ATOM_DAC_B;
+
+ params->sDacload.usDeviceID = cpu_to_le16(device_id);
+ params->sDacload.ucDacType = dac_type;
+ params->sDacload.ucMisc = misc;
+}
+
+static enum bp_result dac_load_detection_v1(
+ struct bios_parser *bp,
+ struct bp_load_detection_parameters *bp_params)
+{
+ enum bp_result result = BP_RESULT_FAILURE;
+ DAC_LOAD_DETECTION_PS_ALLOCATION params;
+
+ dac_load_detect_prepare_params(
+ &params,
+ bp_params->engine_id,
+ bp_params->device_id,
+ 0);
+
+ if (EXEC_BIOS_CMD_TABLE(DAC_LoadDetection, params))
+ result = BP_RESULT_OK;
+
+ return result;
+}
+
+static enum bp_result dac_load_detection_v3(
+ struct bios_parser *bp,
+ struct bp_load_detection_parameters *bp_params)
+{
+ enum bp_result result = BP_RESULT_FAILURE;
+ DAC_LOAD_DETECTION_PS_ALLOCATION params;
+
+ uint8_t misc = 0;
+
+ if (bp_params->device_id == ATOM_DEVICE_CV_SUPPORT ||
+ bp_params->device_id == ATOM_DEVICE_TV1_SUPPORT)
+ misc = DAC_LOAD_MISC_YPrPb;
+
+ dac_load_detect_prepare_params(
+ &params,
+ bp_params->engine_id,
+ bp_params->device_id,
+ misc);
+
+ if (EXEC_BIOS_CMD_TABLE(DAC_LoadDetection, params))
+ result = BP_RESULT_OK;
+
+ return result;
+}
+
+/*******************************************************************************
+ ********************************************************************************
+ **
** DAC OUTPUT CONTROL
**
********************************************************************************
diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.h b/drivers/gpu/drm/amd/display/dc/bios/command_table.h
index ad533775e724..e89b1ba0048b 100644
--- a/drivers/gpu/drm/amd/display/dc/bios/command_table.h
+++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.h
@@ -52,6 +52,9 @@ struct cmd_tbl {
enum bp_result (*adjust_display_pll)(
struct bios_parser *bp,
struct bp_adjust_pixel_clock_parameters *bp_params);
+ enum bp_result (*select_crtc_source)(
+ struct bios_parser *bp,
+ struct bp_crtc_source_select *bp_params);
enum bp_result (*dac1_encoder_control)(
struct bios_parser *bp,
bool enable,
@@ -68,6 +71,9 @@ struct cmd_tbl {
enum bp_result (*dac2_output_control)(
struct bios_parser *bp,
bool enable);
+ enum bp_result (*dac_load_detection)(
+ struct bios_parser *bp,
+ struct bp_load_detection_parameters *bp_params);
enum bp_result (*set_crtc_timing)(
struct bios_parser *bp,
struct bp_hw_crtc_timing_parameters *bp_params);
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c
index 9e63fa72101c..db687a13174d 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c
@@ -509,16 +509,16 @@ void dcn314_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_b
regs_and_bypass->dtbclk = internal.CLK1_CLK4_CURRENT_CNT / 10;
regs_and_bypass->dppclk_bypass = internal.CLK1_CLK1_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dppclk_bypass < 0 || regs_and_bypass->dppclk_bypass > 4)
+ if (regs_and_bypass->dppclk_bypass > 4)
regs_and_bypass->dppclk_bypass = 0;
regs_and_bypass->dcfclk_bypass = internal.CLK1_CLK3_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dcfclk_bypass < 0 || regs_and_bypass->dcfclk_bypass > 4)
+ if (regs_and_bypass->dcfclk_bypass > 4)
regs_and_bypass->dcfclk_bypass = 0;
regs_and_bypass->dispclk_bypass = internal.CLK1_CLK0_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dispclk_bypass < 0 || regs_and_bypass->dispclk_bypass > 4)
+ if (regs_and_bypass->dispclk_bypass > 4)
regs_and_bypass->dispclk_bypass = 0;
regs_and_bypass->dprefclk_bypass = internal.CLK1_CLK2_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dprefclk_bypass < 0 || regs_and_bypass->dprefclk_bypass > 4)
+ if (regs_and_bypass->dprefclk_bypass > 4)
regs_and_bypass->dprefclk_bypass = 0;
}
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c
index b315ed91e010..3a881451e9da 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.c
@@ -40,7 +40,7 @@
#include "dm_helpers.h"
#include "dc_dmub_srv.h"
-
+#include "reg_helper.h"
#include "logger_types.h"
#undef DC_LOGGER
#define DC_LOGGER \
@@ -48,9 +48,43 @@
#include "link_service.h"
+#define MAX_INSTANCE 7
+#define MAX_SEGMENT 8
+
+struct IP_BASE_INSTANCE {
+ unsigned int segment[MAX_SEGMENT];
+};
+
+struct IP_BASE {
+ struct IP_BASE_INSTANCE instance[MAX_INSTANCE];
+};
+
+static const struct IP_BASE CLK_BASE = { { { { 0x00016C00, 0x02401800, 0, 0, 0, 0, 0, 0 } },
+ { { 0x00016E00, 0x02401C00, 0, 0, 0, 0, 0, 0 } },
+ { { 0x00017000, 0x02402000, 0, 0, 0, 0, 0, 0 } },
+ { { 0x00017200, 0x02402400, 0, 0, 0, 0, 0, 0 } },
+ { { 0x0001B000, 0x0242D800, 0, 0, 0, 0, 0, 0 } },
+ { { 0x0001B200, 0x0242DC00, 0, 0, 0, 0, 0, 0 } } } };
+
+#define regCLK1_CLK0_CURRENT_CNT 0x0314
+#define regCLK1_CLK0_CURRENT_CNT_BASE_IDX 0
+#define regCLK1_CLK1_CURRENT_CNT 0x0315
+#define regCLK1_CLK1_CURRENT_CNT_BASE_IDX 0
+#define regCLK1_CLK2_CURRENT_CNT 0x0316
+#define regCLK1_CLK2_CURRENT_CNT_BASE_IDX 0
+#define regCLK1_CLK3_CURRENT_CNT 0x0317
+#define regCLK1_CLK3_CURRENT_CNT_BASE_IDX 0
+#define regCLK1_CLK4_CURRENT_CNT 0x0318
+#define regCLK1_CLK4_CURRENT_CNT_BASE_IDX 0
+#define regCLK1_CLK5_CURRENT_CNT 0x0319
+#define regCLK1_CLK5_CURRENT_CNT_BASE_IDX 0
+
#define TO_CLK_MGR_DCN315(clk_mgr)\
container_of(clk_mgr, struct clk_mgr_dcn315, base)
+#define REG(reg_name) \
+ (CLK_BASE.instance[0].segment[reg ## reg_name ## _BASE_IDX] + reg ## reg_name)
+
#define UNSUPPORTED_DCFCLK 10000000
#define MIN_DPP_DISP_CLK 100000
@@ -245,9 +279,38 @@ static void dcn315_update_clocks(struct clk_mgr *clk_mgr_base,
dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}
+static void dcn315_dump_clk_registers_internal(struct dcn35_clk_internal *internal, struct clk_mgr *clk_mgr_base)
+{
+ struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
+
+ // read dtbclk
+ internal->CLK1_CLK4_CURRENT_CNT = REG_READ(CLK1_CLK4_CURRENT_CNT);
+
+ // read dcfclk
+ internal->CLK1_CLK3_CURRENT_CNT = REG_READ(CLK1_CLK3_CURRENT_CNT);
+
+ // read dppclk
+ internal->CLK1_CLK1_CURRENT_CNT = REG_READ(CLK1_CLK1_CURRENT_CNT);
+
+ // read dprefclk
+ internal->CLK1_CLK2_CURRENT_CNT = REG_READ(CLK1_CLK2_CURRENT_CNT);
+
+ // read dispclk
+ internal->CLK1_CLK0_CURRENT_CNT = REG_READ(CLK1_CLK0_CURRENT_CNT);
+}
+
static void dcn315_dump_clk_registers(struct clk_state_registers_and_bypass *regs_and_bypass,
struct clk_mgr *clk_mgr_base, struct clk_log_info *log_info)
{
+ struct dcn35_clk_internal internal = {0};
+
+ dcn315_dump_clk_registers_internal(&internal, clk_mgr_base);
+
+ regs_and_bypass->dcfclk = internal.CLK1_CLK3_CURRENT_CNT / 10;
+ regs_and_bypass->dprefclk = internal.CLK1_CLK2_CURRENT_CNT / 10;
+ regs_and_bypass->dispclk = internal.CLK1_CLK0_CURRENT_CNT / 10;
+ regs_and_bypass->dppclk = internal.CLK1_CLK1_CURRENT_CNT / 10;
+ regs_and_bypass->dtbclk = internal.CLK1_CLK4_CURRENT_CNT / 10;
return;
}
@@ -594,13 +657,32 @@ static struct clk_mgr_funcs dcn315_funcs = {
.get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz,
.get_dtb_ref_clk_frequency = dcn31_get_dtb_ref_freq_khz,
.update_clocks = dcn315_update_clocks,
- .init_clocks = dcn31_init_clocks,
+ .init_clocks = dcn315_init_clocks,
.enable_pme_wa = dcn315_enable_pme_wa,
.are_clock_states_equal = dcn31_are_clock_states_equal,
.notify_wm_ranges = dcn315_notify_wm_ranges
};
extern struct clk_mgr_funcs dcn3_fpga_funcs;
+void dcn315_init_clocks(struct clk_mgr *clk_mgr)
+{
+ struct clk_mgr_internal *clk_mgr_int = TO_CLK_MGR_INTERNAL(clk_mgr);
+ uint32_t ref_dtbclk = clk_mgr->clks.ref_dtbclk_khz;
+ struct clk_mgr_dcn315 *clk_mgr_dcn315 = TO_CLK_MGR_DCN315(clk_mgr_int);
+ struct clk_log_info log_info = {0};
+
+ memset(&(clk_mgr->clks), 0, sizeof(struct dc_clocks));
+ // Assumption is that boot state always supports pstate
+ clk_mgr->clks.ref_dtbclk_khz = ref_dtbclk; // restore ref_dtbclk
+ clk_mgr->clks.p_state_change_support = true;
+ clk_mgr->clks.prev_p_state_change_support = true;
+ clk_mgr->clks.pwr_state = DCN_PWR_STATE_UNKNOWN;
+ clk_mgr->clks.zstate_support = DCN_ZSTATE_SUPPORT_UNKNOWN;
+
+ dcn315_dump_clk_registers(&clk_mgr->boot_snapshot, &clk_mgr_dcn315->base.base, &log_info);
+ clk_mgr->clks.dispclk_khz = clk_mgr->boot_snapshot.dispclk * 1000;
+}
+
void dcn315_clk_mgr_construct(
struct dc_context *ctx,
struct clk_mgr_dcn315 *clk_mgr,
@@ -661,6 +743,7 @@ void dcn315_clk_mgr_construct(
/* Saved clocks configured at boot for debug purposes */
dcn315_dump_clk_registers(&clk_mgr->base.base.boot_snapshot,
&clk_mgr->base.base, &log_info);
+ clk_mgr->base.base.clks.dispclk_khz = clk_mgr->base.base.boot_snapshot.dispclk * 1000;
clk_mgr->base.base.dprefclk_khz = 600000;
clk_mgr->base.base.dprefclk_khz = dcn315_smu_get_dpref_clk(&clk_mgr->base);
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.h
index ac36ddf5dd1a..642ae3d4a790 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn315/dcn315_clk_mgr.h
@@ -44,6 +44,7 @@ void dcn315_clk_mgr_construct(struct dc_context *ctx,
struct pp_smu_funcs *pp_smu,
struct dccg *dccg);
+void dcn315_init_clocks(struct clk_mgr *clk_mgr);
void dcn315_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr_int);
#endif //__DCN315_CLK_MGR_H__
diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c
index b11383fba35f..dfd0c9505af0 100644
--- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c
@@ -394,6 +394,8 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base,
display_count = dcn35_get_active_display_cnt_wa(dc, context, &all_active_disps);
if (new_clocks->dtbclk_en && !new_clocks->ref_dtbclk_khz)
new_clocks->ref_dtbclk_khz = 600000;
+ else if (!new_clocks->dtbclk_en && new_clocks->ref_dtbclk_khz > 590000)
+ new_clocks->ref_dtbclk_khz = 0;
/*
* if it is safe to lower, but we are already in the lower state, we don't have to do anything
@@ -435,7 +437,7 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base,
actual_dtbclk = REG_READ(CLK1_CLK4_CURRENT_CNT);
- if (actual_dtbclk) {
+ if (actual_dtbclk > 590000) {
clk_mgr_base->clks.ref_dtbclk_khz = new_clocks->ref_dtbclk_khz;
clk_mgr_base->clks.dtbclk_en = new_clocks->dtbclk_en;
}
@@ -633,16 +635,16 @@ static void dcn35_save_clk_registers(struct clk_state_registers_and_bypass *regs
regs_and_bypass->dtbclk = internal.CLK1_CLK4_CURRENT_CNT / 10;
regs_and_bypass->dppclk_bypass = internal.CLK1_CLK1_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dppclk_bypass < 0 || regs_and_bypass->dppclk_bypass > 4)
+ if (regs_and_bypass->dppclk_bypass > 4)
regs_and_bypass->dppclk_bypass = 0;
regs_and_bypass->dcfclk_bypass = internal.CLK1_CLK3_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dcfclk_bypass < 0 || regs_and_bypass->dcfclk_bypass > 4)
+ if (regs_and_bypass->dcfclk_bypass > 4)
regs_and_bypass->dcfclk_bypass = 0;
regs_and_bypass->dispclk_bypass = internal.CLK1_CLK0_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dispclk_bypass < 0 || regs_and_bypass->dispclk_bypass > 4)
+ if (regs_and_bypass->dispclk_bypass > 4)
regs_and_bypass->dispclk_bypass = 0;
regs_and_bypass->dprefclk_bypass = internal.CLK1_CLK2_BYPASS_CNTL & 0x0007;
- if (regs_and_bypass->dprefclk_bypass < 0 || regs_and_bypass->dprefclk_bypass > 4)
+ if (regs_and_bypass->dprefclk_bypass > 4)
regs_and_bypass->dprefclk_bypass = 0;
if (clk_mgr->base.base.ctx->dc->debug.pstate_enabled) {
@@ -1293,6 +1295,35 @@ static void dcn35_update_clocks_fpga(struct clk_mgr *clk_mgr,
dcn35_update_clocks_update_dtb_dto(clk_mgr_int, context, clk_mgr->clks.ref_dtbclk_khz);
}
+static unsigned int dcn35_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type clk_type)
+{
+ struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
+
+ unsigned int num_clk_levels;
+
+ switch (clk_type) {
+ case CLK_TYPE_DISPCLK:
+ num_clk_levels = clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_dispclk_levels;
+ return num_clk_levels ?
+ clk_mgr->base.bw_params->clk_table.entries[num_clk_levels - 1].dispclk_mhz * 1000 :
+ clk_mgr->base.boot_snapshot.dispclk;
+ case CLK_TYPE_DPPCLK:
+ num_clk_levels = clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_dppclk_levels;
+ return num_clk_levels ?
+ clk_mgr->base.bw_params->clk_table.entries[num_clk_levels - 1].dppclk_mhz * 1000 :
+ clk_mgr->base.boot_snapshot.dppclk;
+ case CLK_TYPE_DSCCLK:
+ num_clk_levels = clk_mgr->base.bw_params->clk_table.num_entries_per_clk.num_dispclk_levels;
+ return num_clk_levels ?
+ clk_mgr->base.bw_params->clk_table.entries[num_clk_levels - 1].dispclk_mhz * 1000 / 3 :
+ clk_mgr->base.boot_snapshot.dispclk / 3;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static struct clk_mgr_funcs dcn35_funcs = {
.get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz,
.get_dtb_ref_clk_frequency = dcn31_get_dtb_ref_freq_khz,
@@ -1304,6 +1335,7 @@ static struct clk_mgr_funcs dcn35_funcs = {
.set_low_power_state = dcn35_set_low_power_state,
.exit_low_power_state = dcn35_exit_low_power_state,
.is_ips_supported = dcn35_is_ips_supported,
+ .get_max_clock_khz = dcn35_get_max_clock_khz,
};
struct clk_mgr_funcs dcn35_fpga_funcs = {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index 5f2d5638c819..8be9cbd43e18 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -83,7 +83,7 @@
#include "hw_sequencer_private.h"
#if defined(CONFIG_DRM_AMD_DC_FP)
-#include "dml2/dml2_internal_types.h"
+#include "dml2_0/dml2_internal_types.h"
#include "soc_and_ip_translator.h"
#endif
@@ -148,10 +148,16 @@ static const char DC_BUILD_ID[] = "production-build";
/* Private functions */
-static inline void elevate_update_type(enum surface_update_type *original, enum surface_update_type new)
+static inline void elevate_update_type(
+ struct surface_update_descriptor *descriptor,
+ enum surface_update_type new_type,
+ enum dc_lock_descriptor new_locks
+)
{
- if (new > *original)
- *original = new;
+ if (new_type > descriptor->update_type)
+ descriptor->update_type = new_type;
+
+ descriptor->lock_descriptor |= new_locks;
}
static void destroy_links(struct dc *dc)
@@ -297,6 +303,7 @@ static bool create_links(
link->link_id.id = CONNECTOR_ID_VIRTUAL;
link->link_id.enum_id = ENUM_ID_1;
link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
+ link->replay_settings.config.replay_version = DC_REPLAY_VERSION_UNSUPPORTED;
link->link_enc = kzalloc(sizeof(*link->link_enc), GFP_KERNEL);
if (!link->link_enc) {
@@ -493,9 +500,14 @@ bool dc_stream_adjust_vmin_vmax(struct dc *dc,
1,
*adjust);
stream->adjust.timing_adjust_pending = false;
+
+ if (dc->hwss.notify_cursor_offload_drr_update)
+ dc->hwss.notify_cursor_offload_drr_update(dc, dc->current_state, stream);
+
return true;
}
}
+
return false;
}
@@ -1143,8 +1155,8 @@ static bool dc_construct(struct dc *dc,
/* set i2c speed if not done by the respective dcnxxx__resource.c */
if (dc->caps.i2c_speed_in_khz_hdcp == 0)
dc->caps.i2c_speed_in_khz_hdcp = dc->caps.i2c_speed_in_khz;
- if (dc->caps.max_optimizable_video_width == 0)
- dc->caps.max_optimizable_video_width = 5120;
+ if (dc->check_config.max_optimizable_video_width == 0)
+ dc->check_config.max_optimizable_video_width = 5120;
dc->clk_mgr = dc_clk_mgr_create(dc->ctx, dc->res_pool->pp_smu, dc->res_pool->dccg);
if (!dc->clk_mgr)
goto fail;
@@ -2135,6 +2147,14 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
if (!dcb->funcs->is_accelerated_mode(dcb)) {
disable_vbios_mode_if_required(dc, context);
dc->hwss.enable_accelerated_mode(dc, context);
+ } else if (get_seamless_boot_stream_count(dc->current_state) > 0) {
+ /* If the previous Stream still retains the apply seamless boot flag,
+ * it means the OS has not actually performed a flip yet.
+ * At this point, if we receive dc_commit_streams again, we should
+ * once more check whether the actual HW timing matches what the OS
+ * has provided
+ */
+ disable_vbios_mode_if_required(dc, context);
}
if (dc->hwseq->funcs.wait_for_pipe_update_if_needed) {
@@ -2158,8 +2178,8 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
*/
if (dc->hwss.subvp_pipe_control_lock)
dc->hwss.subvp_pipe_control_lock(dc, context, true, true, NULL, subvp_prev_use);
- if (dc->hwss.fams2_global_control_lock)
- dc->hwss.fams2_global_control_lock(dc, context, true);
+ if (dc->hwss.dmub_hw_control_lock)
+ dc->hwss.dmub_hw_control_lock(dc, context, true);
if (dc->hwss.update_dsc_pg)
dc->hwss.update_dsc_pg(dc, context, false);
@@ -2188,8 +2208,14 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe);
}
+ for (i = 0; i < dc->current_state->stream_count; i++)
+ dc_dmub_srv_control_cursor_offload(dc, dc->current_state, dc->current_state->streams[i], false);
+
result = dc->hwss.apply_ctx_to_hw(dc, context);
+ for (i = 0; i < context->stream_count; i++)
+ dc_dmub_srv_control_cursor_offload(dc, context, context->streams[i], true);
+
if (result != DC_OK) {
/* Application of dc_state to hardware stopped. */
dc->current_state->res_ctx.link_enc_cfg_ctx.mode = LINK_ENC_CFG_STEADY;
@@ -2229,8 +2255,8 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c
dc->hwss.commit_subvp_config(dc, context);
if (dc->hwss.subvp_pipe_control_lock)
dc->hwss.subvp_pipe_control_lock(dc, context, false, true, NULL, subvp_prev_use);
- if (dc->hwss.fams2_global_control_lock)
- dc->hwss.fams2_global_control_lock(dc, context, false);
+ if (dc->hwss.dmub_hw_control_lock)
+ dc->hwss.dmub_hw_control_lock(dc, context, false);
for (i = 0; i < context->stream_count; i++) {
const struct dc_link *link = context->streams[i]->link;
@@ -2645,47 +2671,50 @@ static bool is_surface_in_context(
return false;
}
-static enum surface_update_type get_plane_info_update_type(const struct dc *dc, const struct dc_surface_update *u)
+static struct surface_update_descriptor get_plane_info_update_type(const struct dc_surface_update *u)
{
union surface_update_flags *update_flags = &u->surface->update_flags;
- enum surface_update_type update_type = UPDATE_TYPE_FAST;
+ struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
if (!u->plane_info)
- return UPDATE_TYPE_FAST;
+ return update_type;
+
+ // `plane_info` present means at least `STREAM` lock is required
+ elevate_update_type(&update_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
if (u->plane_info->color_space != u->surface->color_space) {
update_flags->bits.color_space_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_MED);
+ elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->horizontal_mirror != u->surface->horizontal_mirror) {
update_flags->bits.horizontal_mirror_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_MED);
+ elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->rotation != u->surface->rotation) {
update_flags->bits.rotation_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->format != u->surface->format) {
update_flags->bits.pixel_format_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->stereo_format != u->surface->stereo_format) {
update_flags->bits.stereo_format_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->per_pixel_alpha != u->surface->per_pixel_alpha) {
update_flags->bits.per_pixel_alpha_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_MED);
+ elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->global_alpha_value != u->surface->global_alpha_value) {
update_flags->bits.global_alpha_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_MED);
+ elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->dcc.enable != u->surface->dcc.enable
@@ -2697,7 +2726,7 @@ static enum surface_update_type get_plane_info_update_type(const struct dc *dc,
* recalculate stutter period.
*/
update_flags->bits.dcc_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (resource_pixel_format_to_bpp(u->plane_info->format) !=
@@ -2706,30 +2735,41 @@ static enum surface_update_type get_plane_info_update_type(const struct dc *dc,
* and DML calculation
*/
update_flags->bits.bpp_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->plane_size.surface_pitch != u->surface->plane_size.surface_pitch
|| u->plane_info->plane_size.chroma_pitch != u->surface->plane_size.chroma_pitch) {
update_flags->bits.plane_size_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_MED);
+ elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
+ const struct dc_tiling_info *tiling = &u->plane_info->tiling_info;
- if (memcmp(&u->plane_info->tiling_info, &u->surface->tiling_info,
- sizeof(struct dc_tiling_info)) != 0) {
+ if (memcmp(tiling, &u->surface->tiling_info, sizeof(*tiling)) != 0) {
update_flags->bits.swizzle_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_MED);
-
- /* todo: below are HW dependent, we should add a hook to
- * DCE/N resource and validated there.
- */
- if (!dc->debug.skip_full_updated_if_possible) {
- /* swizzled mode requires RQ to be setup properly,
- * thus need to run DML to calculate RQ settings
- */
- update_flags->bits.bandwidth_change = 1;
- elevate_update_type(&update_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
+
+ switch (tiling->gfxversion) {
+ case DcGfxVersion9:
+ case DcGfxVersion10:
+ case DcGfxVersion11:
+ if (tiling->gfx9.swizzle != DC_SW_LINEAR) {
+ update_flags->bits.bandwidth_change = 1;
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
+ }
+ break;
+ case DcGfxAddr3:
+ if (tiling->gfx_addr3.swizzle != DC_ADDR3_SW_LINEAR) {
+ update_flags->bits.bandwidth_change = 1;
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
+ }
+ break;
+ case DcGfxVersion7:
+ case DcGfxVersion8:
+ case DcGfxVersionUnknown:
+ default:
+ break;
}
}
@@ -2737,14 +2777,18 @@ static enum surface_update_type get_plane_info_update_type(const struct dc *dc,
return update_type;
}
-static enum surface_update_type get_scaling_info_update_type(
- const struct dc *dc,
+static struct surface_update_descriptor get_scaling_info_update_type(
+ const struct dc_check_config *check_config,
const struct dc_surface_update *u)
{
union surface_update_flags *update_flags = &u->surface->update_flags;
+ struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
if (!u->scaling_info)
- return UPDATE_TYPE_FAST;
+ return update_type;
+
+ // `scaling_info` present means at least `STREAM` lock is required
+ elevate_update_type(&update_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
if (u->scaling_info->src_rect.width != u->surface->src_rect.width
|| u->scaling_info->src_rect.height != u->surface->src_rect.height
@@ -2755,6 +2799,7 @@ static enum surface_update_type get_scaling_info_update_type(
|| u->scaling_info->scaling_quality.integer_scaling !=
u->surface->scaling_quality.integer_scaling) {
update_flags->bits.scaling_change = 1;
+ elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
if (u->scaling_info->src_rect.width > u->surface->src_rect.width
|| u->scaling_info->src_rect.height > u->surface->src_rect.height)
@@ -2768,7 +2813,7 @@ static enum surface_update_type get_scaling_info_update_type(
/* Making dst rect smaller requires a bandwidth change */
update_flags->bits.bandwidth_change = 1;
- if (u->scaling_info->src_rect.width > dc->caps.max_optimizable_video_width &&
+ if (u->scaling_info->src_rect.width > check_config->max_optimizable_video_width &&
(u->scaling_info->clip_rect.width > u->surface->clip_rect.width ||
u->scaling_info->clip_rect.height > u->surface->clip_rect.height))
/* Changing clip size of a large surface may result in MPC slice count change */
@@ -2780,123 +2825,109 @@ static enum surface_update_type get_scaling_info_update_type(
|| u->scaling_info->clip_rect.x != u->surface->clip_rect.x
|| u->scaling_info->clip_rect.y != u->surface->clip_rect.y
|| u->scaling_info->dst_rect.x != u->surface->dst_rect.x
- || u->scaling_info->dst_rect.y != u->surface->dst_rect.y)
+ || u->scaling_info->dst_rect.y != u->surface->dst_rect.y) {
+ elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
update_flags->bits.position_change = 1;
+ }
- /* process every update flag before returning */
- if (update_flags->bits.clock_change
- || update_flags->bits.bandwidth_change
- || update_flags->bits.scaling_change)
- return UPDATE_TYPE_FULL;
-
- if (update_flags->bits.position_change)
- return UPDATE_TYPE_MED;
-
- return UPDATE_TYPE_FAST;
+ return update_type;
}
-static enum surface_update_type det_surface_update(const struct dc *dc,
- const struct dc_surface_update *u)
+static struct surface_update_descriptor det_surface_update(
+ const struct dc_check_config *check_config,
+ struct dc_surface_update *u)
{
- const struct dc_state *context = dc->current_state;
- enum surface_update_type type;
- enum surface_update_type overall_type = UPDATE_TYPE_FAST;
+ struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
union surface_update_flags *update_flags = &u->surface->update_flags;
- if (!is_surface_in_context(context, u->surface) || u->surface->force_full_update) {
+ if (u->surface->force_full_update) {
update_flags->raw = 0xFFFFFFFF;
- return UPDATE_TYPE_FULL;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
+ return overall_type;
}
update_flags->raw = 0; // Reset all flags
- type = get_plane_info_update_type(dc, u);
- elevate_update_type(&overall_type, type);
+ struct surface_update_descriptor inner_type = get_plane_info_update_type(u);
+
+ elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor);
- type = get_scaling_info_update_type(dc, u);
- elevate_update_type(&overall_type, type);
+ inner_type = get_scaling_info_update_type(check_config, u);
+ elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor);
if (u->flip_addr) {
update_flags->bits.addr_update = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+
if (u->flip_addr->address.tmz_surface != u->surface->address.tmz_surface) {
update_flags->bits.tmz_changed = 1;
- elevate_update_type(&overall_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
- if (u->in_transfer_func)
+ if (u->in_transfer_func) {
update_flags->bits.in_transfer_func_change = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
+ }
- if (u->input_csc_color_matrix)
+ if (u->input_csc_color_matrix) {
update_flags->bits.input_csc_change = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+ }
- if (u->coeff_reduction_factor)
+ if (u->coeff_reduction_factor) {
update_flags->bits.coeff_reduction_change = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+ }
- if (u->gamut_remap_matrix)
+ if (u->gamut_remap_matrix) {
update_flags->bits.gamut_remap_change = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+ }
- if (u->blend_tf)
+ if (u->blend_tf || (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) {
update_flags->bits.gamma_change = 1;
-
- if (u->gamma) {
- enum surface_pixel_format format = SURFACE_PIXEL_FORMAT_GRPH_BEGIN;
-
- if (u->plane_info)
- format = u->plane_info->format;
- else
- format = u->surface->format;
-
- if (dce_use_lut(format))
- update_flags->bits.gamma_change = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
- if (u->lut3d_func || u->func_shaper)
+ if (u->lut3d_func || u->func_shaper) {
update_flags->bits.lut_3d = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+ }
if (u->hdr_mult.value)
if (u->hdr_mult.value != u->surface->hdr_mult.value) {
+ // TODO: Should be fast?
update_flags->bits.hdr_mult = 1;
- elevate_update_type(&overall_type, UPDATE_TYPE_MED);
+ elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->sdr_white_level_nits)
if (u->sdr_white_level_nits != u->surface->sdr_white_level_nits) {
+ // TODO: Should be fast?
update_flags->bits.sdr_white_level_nits = 1;
- elevate_update_type(&overall_type, UPDATE_TYPE_FULL);
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->cm2_params) {
- if ((u->cm2_params->component_settings.shaper_3dlut_setting
- != u->surface->mcm_shaper_3dlut_setting)
- || (u->cm2_params->component_settings.lut1d_enable
- != u->surface->mcm_lut1d_enable))
- update_flags->bits.mcm_transfer_function_enable_change = 1;
- if (u->cm2_params->cm2_luts.lut3d_data.lut3d_src
- != u->surface->mcm_luts.lut3d_data.lut3d_src)
+ if (u->cm2_params->component_settings.shaper_3dlut_setting != u->surface->mcm_shaper_3dlut_setting
+ || u->cm2_params->component_settings.lut1d_enable != u->surface->mcm_lut1d_enable
+ || u->cm2_params->cm2_luts.lut3d_data.lut3d_src != u->surface->mcm_luts.lut3d_data.lut3d_src) {
update_flags->bits.mcm_transfer_function_enable_change = 1;
- }
- if (update_flags->bits.in_transfer_func_change) {
- type = UPDATE_TYPE_MED;
- elevate_update_type(&overall_type, type);
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
+ }
}
if (update_flags->bits.lut_3d &&
u->surface->mcm_luts.lut3d_data.lut3d_src != DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) {
- type = UPDATE_TYPE_FULL;
- elevate_update_type(&overall_type, type);
- }
- if (update_flags->bits.mcm_transfer_function_enable_change) {
- type = UPDATE_TYPE_FULL;
- elevate_update_type(&overall_type, type);
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
- if (dc->debug.enable_legacy_fast_update &&
+ if (check_config->enable_legacy_fast_update &&
(update_flags->bits.gamma_change ||
update_flags->bits.gamut_remap_change ||
update_flags->bits.input_csc_change ||
update_flags->bits.coeff_reduction_change)) {
- type = UPDATE_TYPE_FULL;
- elevate_update_type(&overall_type, type);
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
return overall_type;
}
@@ -2924,32 +2955,26 @@ static void force_immediate_gsl_plane_flip(struct dc *dc, struct dc_surface_upda
}
}
-static enum surface_update_type check_update_surfaces_for_stream(
- struct dc *dc,
+static struct surface_update_descriptor check_update_surfaces_for_stream(
+ const struct dc_check_config *check_config,
struct dc_surface_update *updates,
int surface_count,
- struct dc_stream_update *stream_update,
- const struct dc_stream_status *stream_status)
+ struct dc_stream_update *stream_update)
{
- int i;
- enum surface_update_type overall_type = UPDATE_TYPE_FAST;
-
- if (dc->idle_optimizations_allowed || dc_can_clear_cursor_limit(dc))
- overall_type = UPDATE_TYPE_FULL;
-
- if (stream_status == NULL || stream_status->plane_count != surface_count)
- overall_type = UPDATE_TYPE_FULL;
+ struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
if (stream_update && stream_update->pending_test_pattern) {
- overall_type = UPDATE_TYPE_FULL;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (stream_update && stream_update->hw_cursor_req) {
- overall_type = UPDATE_TYPE_FULL;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
/* some stream updates require passive update */
if (stream_update) {
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+
union stream_update_flags *su_flags = &stream_update->stream->update_flags;
if ((stream_update->src.height != 0 && stream_update->src.width != 0) ||
@@ -2957,14 +2982,16 @@ static enum surface_update_type check_update_surfaces_for_stream(
stream_update->integer_scaling_update)
su_flags->bits.scaling = 1;
- if (dc->debug.enable_legacy_fast_update && stream_update->out_transfer_func)
+ if (check_config->enable_legacy_fast_update && stream_update->out_transfer_func)
su_flags->bits.out_tf = 1;
if (stream_update->abm_level)
su_flags->bits.abm_level = 1;
- if (stream_update->dpms_off)
+ if (stream_update->dpms_off) {
su_flags->bits.dpms_off = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL | LOCK_DESCRIPTOR_LINK);
+ }
if (stream_update->gamut_remap)
su_flags->bits.gamut_remap = 1;
@@ -2992,24 +3019,27 @@ static enum surface_update_type check_update_surfaces_for_stream(
if (stream_update->output_color_space)
su_flags->bits.out_csc = 1;
- if (su_flags->raw != 0)
- overall_type = UPDATE_TYPE_FULL;
+ // TODO: Make each elevation explicit, as to not override fast stream in crct_timing_adjust
+ if (su_flags->raw)
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
- if (stream_update->output_csc_transform)
+ // Non-global cases
+ if (stream_update->output_csc_transform) {
su_flags->bits.out_csc = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+ }
- /* Output transfer function changes do not require bandwidth recalculation,
- * so don't trigger a full update
- */
- if (!dc->debug.enable_legacy_fast_update && stream_update->out_transfer_func)
+ if (!check_config->enable_legacy_fast_update && stream_update->out_transfer_func) {
su_flags->bits.out_tf = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
+ }
}
- for (i = 0 ; i < surface_count; i++) {
- enum surface_update_type type =
- det_surface_update(dc, &updates[i]);
+ for (int i = 0 ; i < surface_count; i++) {
+ struct surface_update_descriptor inner_type =
+ det_surface_update(check_config, &updates[i]);
- elevate_update_type(&overall_type, type);
+ elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor);
}
return overall_type;
@@ -3020,44 +3050,18 @@ static enum surface_update_type check_update_surfaces_for_stream(
*
* See :c:type:`enum surface_update_type <surface_update_type>` for explanation of update types
*/
-enum surface_update_type dc_check_update_surfaces_for_stream(
- struct dc *dc,
+struct surface_update_descriptor dc_check_update_surfaces_for_stream(
+ const struct dc_check_config *check_config,
struct dc_surface_update *updates,
int surface_count,
- struct dc_stream_update *stream_update,
- const struct dc_stream_status *stream_status)
+ struct dc_stream_update *stream_update)
{
- int i;
- enum surface_update_type type;
-
if (stream_update)
stream_update->stream->update_flags.raw = 0;
- for (i = 0; i < surface_count; i++)
+ for (size_t i = 0; i < surface_count; i++)
updates[i].surface->update_flags.raw = 0;
- type = check_update_surfaces_for_stream(dc, updates, surface_count, stream_update, stream_status);
- if (type == UPDATE_TYPE_FULL) {
- if (stream_update) {
- uint32_t dsc_changed = stream_update->stream->update_flags.bits.dsc_changed;
- stream_update->stream->update_flags.raw = 0xFFFFFFFF;
- stream_update->stream->update_flags.bits.dsc_changed = dsc_changed;
- }
- for (i = 0; i < surface_count; i++)
- updates[i].surface->update_flags.raw = 0xFFFFFFFF;
- }
-
- if (type == UPDATE_TYPE_FAST) {
- // If there's an available clock comparator, we use that.
- if (dc->clk_mgr->funcs->are_clock_states_equal) {
- if (!dc->clk_mgr->funcs->are_clock_states_equal(&dc->clk_mgr->clks, &dc->current_state->bw_ctx.bw.dcn.clk))
- dc->optimized_required = true;
- // Else we fallback to mem compare.
- } else if (memcmp(&dc->current_state->bw_ctx.bw.dcn.clk, &dc->clk_mgr->clks, offsetof(struct dc_clocks, prev_p_state_change_support)) != 0) {
- dc->optimized_required = true;
- }
- }
-
- return type;
+ return check_update_surfaces_for_stream(check_config, updates, surface_count, stream_update);
}
static struct dc_stream_status *stream_get_status(
@@ -3388,7 +3392,11 @@ static void restore_planes_and_stream_state(
for (i = 0; i < status->plane_count; i++) {
dc_plane_copy_config(status->plane_states[i], &scratch->plane_states[i]);
}
+
+ // refcount is persistent
+ struct kref temp_refcount = stream->refcount;
*stream = scratch->stream_state;
+ stream->refcount = temp_refcount;
}
/**
@@ -3426,6 +3434,13 @@ static void update_seamless_boot_flags(struct dc *dc,
}
}
+static bool full_update_required_weak(
+ const struct dc *dc,
+ const struct dc_surface_update *srf_updates,
+ int surface_count,
+ const struct dc_stream_update *stream_update,
+ const struct dc_stream_state *stream);
+
/**
* update_planes_and_stream_state() - The function takes planes and stream
* updates as inputs and determines the appropriate update type. If update type
@@ -3472,7 +3487,10 @@ static bool update_planes_and_stream_state(struct dc *dc,
context = dc->current_state;
update_type = dc_check_update_surfaces_for_stream(
- dc, srf_updates, surface_count, stream_update, stream_status);
+ &dc->check_config, srf_updates, surface_count, stream_update).update_type;
+ if (full_update_required_weak(dc, srf_updates, surface_count, stream_update, stream))
+ update_type = UPDATE_TYPE_FULL;
+
/* It is possible to receive a flip for one plane while there are multiple flip_immediate planes in the same stream.
* E.g. Desktop and MPO plane are flip_immediate but only the MPO plane received a flip
* Force the other flip_immediate planes to flip so GSL doesn't wait for a flip that won't come.
@@ -3504,6 +3522,16 @@ static bool update_planes_and_stream_state(struct dc *dc,
}
}
+ if (update_type == UPDATE_TYPE_FULL) {
+ if (stream_update) {
+ uint32_t dsc_changed = stream_update->stream->update_flags.bits.dsc_changed;
+ stream_update->stream->update_flags.raw = 0xFFFFFFFF;
+ stream_update->stream->update_flags.bits.dsc_changed = dsc_changed;
+ }
+ for (i = 0; i < surface_count; i++)
+ srf_updates[i].surface->update_flags.raw = 0xFFFFFFFF;
+ }
+
if (update_type >= update_surface_trace_level)
update_surface_trace(dc, srf_updates, surface_count);
@@ -4149,7 +4177,7 @@ static void commit_planes_for_stream(struct dc *dc,
if ((update_type != UPDATE_TYPE_FAST) && stream->update_flags.bits.dsc_changed)
if (top_pipe_to_program &&
top_pipe_to_program->stream_res.tg->funcs->lock_doublebuffer_enable) {
- if (should_use_dmub_lock(stream->link)) {
+ if (should_use_dmub_inbox1_lock(dc, stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
@@ -4176,16 +4204,16 @@ static void commit_planes_for_stream(struct dc *dc,
if (dc->hwss.subvp_pipe_control_lock)
dc->hwss.subvp_pipe_control_lock(dc, context, true, should_lock_all_pipes, NULL, subvp_prev_use);
- if (dc->hwss.fams2_global_control_lock)
- dc->hwss.fams2_global_control_lock(dc, context, true);
+ if (dc->hwss.dmub_hw_control_lock)
+ dc->hwss.dmub_hw_control_lock(dc, context, true);
dc->hwss.interdependent_update_lock(dc, context, true);
} else {
if (dc->hwss.subvp_pipe_control_lock)
dc->hwss.subvp_pipe_control_lock(dc, context, true, should_lock_all_pipes, top_pipe_to_program, subvp_prev_use);
- if (dc->hwss.fams2_global_control_lock)
- dc->hwss.fams2_global_control_lock(dc, context, true);
+ if (dc->hwss.dmub_hw_control_lock)
+ dc->hwss.dmub_hw_control_lock(dc, context, true);
/* Lock the top pipe while updating plane addrs, since freesync requires
* plane addr update event triggers to be synchronized.
@@ -4228,9 +4256,8 @@ static void commit_planes_for_stream(struct dc *dc,
dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes,
NULL, subvp_prev_use);
- if (dc->hwss.fams2_global_control_lock)
- dc->hwss.fams2_global_control_lock(dc, context, false);
-
+ if (dc->hwss.dmub_hw_control_lock)
+ dc->hwss.dmub_hw_control_lock(dc, context, false);
return;
}
@@ -4419,7 +4446,7 @@ static void commit_planes_for_stream(struct dc *dc,
top_pipe_to_program->stream_res.tg,
CRTC_STATE_VACTIVE);
- if (should_use_dmub_lock(stream->link)) {
+ if (should_use_dmub_inbox1_lock(dc, stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
@@ -4467,13 +4494,13 @@ static void commit_planes_for_stream(struct dc *dc,
if (should_lock_all_pipes && dc->hwss.interdependent_update_lock) {
if (dc->hwss.subvp_pipe_control_lock)
dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, NULL, subvp_prev_use);
- if (dc->hwss.fams2_global_control_lock)
- dc->hwss.fams2_global_control_lock(dc, context, false);
+ if (dc->hwss.dmub_hw_control_lock)
+ dc->hwss.dmub_hw_control_lock(dc, context, false);
} else {
if (dc->hwss.subvp_pipe_control_lock)
dc->hwss.subvp_pipe_control_lock(dc, context, false, should_lock_all_pipes, top_pipe_to_program, subvp_prev_use);
- if (dc->hwss.fams2_global_control_lock)
- dc->hwss.fams2_global_control_lock(dc, context, false);
+ if (dc->hwss.dmub_hw_control_lock)
+ dc->hwss.dmub_hw_control_lock(dc, context, false);
}
// Fire manual trigger only when bottom plane is flipped
@@ -4489,6 +4516,8 @@ static void commit_planes_for_stream(struct dc *dc,
pipe_ctx->plane_state->skip_manual_trigger)
continue;
+ if (dc->hwss.program_cursor_offload_now)
+ dc->hwss.program_cursor_offload_now(dc, pipe_ctx);
if (pipe_ctx->stream_res.tg->funcs->program_manual_trigger)
pipe_ctx->stream_res.tg->funcs->program_manual_trigger(pipe_ctx->stream_res.tg);
}
@@ -4994,7 +5023,7 @@ void populate_fast_updates(struct dc_fast_update *fast_update,
}
}
-static bool fast_updates_exist(struct dc_fast_update *fast_update, int surface_count)
+static bool fast_updates_exist(const struct dc_fast_update *fast_update, int surface_count)
{
int i;
@@ -5035,18 +5064,44 @@ bool fast_nonaddr_updates_exist(struct dc_fast_update *fast_update, int surface_
return false;
}
-static bool full_update_required(struct dc *dc,
- struct dc_surface_update *srf_updates,
+static bool full_update_required_weak(
+ const struct dc *dc,
+ const struct dc_surface_update *srf_updates,
int surface_count,
- struct dc_stream_update *stream_update,
- struct dc_stream_state *stream)
+ const struct dc_stream_update *stream_update,
+ const struct dc_stream_state *stream)
{
-
- int i;
- struct dc_stream_status *stream_status;
const struct dc_state *context = dc->current_state;
+ if (srf_updates)
+ for (int i = 0; i < surface_count; i++)
+ if (!is_surface_in_context(context, srf_updates[i].surface))
+ return true;
- for (i = 0; i < surface_count; i++) {
+ if (stream) {
+ const struct dc_stream_status *stream_status = dc_stream_get_status_const(stream);
+ if (stream_status == NULL || stream_status->plane_count != surface_count)
+ return true;
+ }
+ if (dc->idle_optimizations_allowed)
+ return true;
+
+ if (dc_can_clear_cursor_limit(dc))
+ return true;
+
+ return false;
+}
+
+static bool full_update_required(
+ const struct dc *dc,
+ const struct dc_surface_update *srf_updates,
+ int surface_count,
+ const struct dc_stream_update *stream_update,
+ const struct dc_stream_state *stream)
+{
+ if (full_update_required_weak(dc, srf_updates, surface_count, stream_update, stream))
+ return true;
+
+ for (int i = 0; i < surface_count; i++) {
if (srf_updates &&
(srf_updates[i].plane_info ||
srf_updates[i].scaling_info ||
@@ -5062,8 +5117,7 @@ static bool full_update_required(struct dc *dc,
srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface) ||
(srf_updates[i].cm2_params &&
(srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting != srf_updates[i].surface->mcm_shaper_3dlut_setting ||
- srf_updates[i].cm2_params->component_settings.lut1d_enable != srf_updates[i].surface->mcm_lut1d_enable)) ||
- !is_surface_in_context(context, srf_updates[i].surface)))
+ srf_updates[i].cm2_params->component_settings.lut1d_enable != srf_updates[i].surface->mcm_lut1d_enable))))
return true;
}
@@ -5099,26 +5153,16 @@ static bool full_update_required(struct dc *dc,
stream_update->hw_cursor_req))
return true;
- if (stream) {
- stream_status = dc_stream_get_status(stream);
- if (stream_status == NULL || stream_status->plane_count != surface_count)
- return true;
- }
- if (dc->idle_optimizations_allowed)
- return true;
-
- if (dc_can_clear_cursor_limit(dc))
- return true;
-
return false;
}
-static bool fast_update_only(struct dc *dc,
- struct dc_fast_update *fast_update,
- struct dc_surface_update *srf_updates,
+static bool fast_update_only(
+ const struct dc *dc,
+ const struct dc_fast_update *fast_update,
+ const struct dc_surface_update *srf_updates,
int surface_count,
- struct dc_stream_update *stream_update,
- struct dc_stream_state *stream)
+ const struct dc_stream_update *stream_update,
+ const struct dc_stream_state *stream)
{
return fast_updates_exist(fast_update, surface_count)
&& !full_update_required(dc, srf_updates, surface_count, stream_update, stream);
@@ -5181,7 +5225,7 @@ static bool update_planes_and_stream_v2(struct dc *dc,
commit_minimal_transition_state_in_dc_update(dc, context, stream,
srf_updates, surface_count);
- if (is_fast_update_only && !dc->debug.enable_legacy_fast_update) {
+ if (is_fast_update_only && !dc->check_config.enable_legacy_fast_update) {
commit_planes_for_stream_fast(dc,
srf_updates,
surface_count,
@@ -5224,7 +5268,7 @@ static void commit_planes_and_stream_update_on_current_context(struct dc *dc,
stream_update);
if (fast_update_only(dc, fast_update, srf_updates, surface_count,
stream_update, stream) &&
- !dc->debug.enable_legacy_fast_update)
+ !dc->check_config.enable_legacy_fast_update)
commit_planes_for_stream_fast(dc,
srf_updates,
surface_count,
@@ -5350,7 +5394,8 @@ bool dc_update_planes_and_stream(struct dc *dc,
* specially handle compatibility problems with transitions among those
* features as they are now transparent to the new sequence.
*/
- if (dc->ctx->dce_version >= DCN_VERSION_4_01)
+ if (dc->ctx->dce_version >= DCN_VERSION_4_01 || dc->ctx->dce_version == DCN_VERSION_3_2 ||
+ dc->ctx->dce_version == DCN_VERSION_3_21)
ret = update_planes_and_stream_v3(dc, srf_updates,
surface_count, stream, stream_update);
else
@@ -5935,6 +5980,101 @@ bool dc_process_dmub_aux_transfer_async(struct dc *dc,
return true;
}
+bool dc_smart_power_oled_enable(const struct dc_link *link, bool enable, uint16_t peak_nits,
+ uint8_t debug_control, uint16_t fixed_CLL, uint32_t triggerline)
+{
+ bool status = false;
+ struct dc *dc = link->ctx->dc;
+ union dmub_rb_cmd cmd;
+ uint8_t otg_inst = 0;
+ unsigned int panel_inst = 0;
+ struct pipe_ctx *pipe_ctx = NULL;
+ struct resource_context *res_ctx = &link->ctx->dc->current_state->res_ctx;
+ int i = 0;
+
+ // get panel_inst
+ if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst))
+ return status;
+
+ // get otg_inst
+ for (i = 0; i < MAX_PIPES; i++) {
+ if (res_ctx &&
+ res_ctx->pipe_ctx[i].stream &&
+ res_ctx->pipe_ctx[i].stream->link &&
+ res_ctx->pipe_ctx[i].stream->link == link &&
+ res_ctx->pipe_ctx[i].stream->link->connector_signal == SIGNAL_TYPE_EDP) {
+ pipe_ctx = &res_ctx->pipe_ctx[i];
+ //TODO: refactor for multi edp support
+ break;
+ }
+ }
+
+ if (pipe_ctx)
+ otg_inst = pipe_ctx->stream_res.tg->inst;
+
+ // before enable smart power OLED, we need to call set pipe for DMUB to set ABM config
+ if (enable) {
+ if (dc->hwss.set_pipe && pipe_ctx)
+ dc->hwss.set_pipe(pipe_ctx);
+ }
+
+ // fill in cmd
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.smart_power_oled_enable.header.type = DMUB_CMD__SMART_POWER_OLED;
+ cmd.smart_power_oled_enable.header.sub_type = DMUB_CMD__SMART_POWER_OLED_ENABLE;
+ cmd.smart_power_oled_enable.header.payload_bytes =
+ sizeof(struct dmub_rb_cmd_smart_power_oled_enable_data) - sizeof(struct dmub_cmd_header);
+ cmd.smart_power_oled_enable.header.ret_status = 1;
+ cmd.smart_power_oled_enable.data.enable = enable;
+ cmd.smart_power_oled_enable.data.panel_inst = panel_inst;
+ cmd.smart_power_oled_enable.data.peak_nits = peak_nits;
+ cmd.smart_power_oled_enable.data.otg_inst = otg_inst;
+ cmd.smart_power_oled_enable.data.digfe_inst = link->link_enc->preferred_engine;
+ cmd.smart_power_oled_enable.data.digbe_inst = link->link_enc->transmitter;
+
+ cmd.smart_power_oled_enable.data.debugcontrol = debug_control;
+ cmd.smart_power_oled_enable.data.triggerline = triggerline;
+ cmd.smart_power_oled_enable.data.fixed_max_cll = fixed_CLL;
+
+ // send cmd
+ status = dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
+
+ return status;
+}
+
+bool dc_smart_power_oled_get_max_cll(const struct dc_link *link, unsigned int *pCurrent_MaxCLL)
+{
+ struct dc *dc = link->ctx->dc;
+ union dmub_rb_cmd cmd;
+ bool status = false;
+ unsigned int panel_inst = 0;
+
+ // get panel_inst
+ if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst))
+ return status;
+
+ // fill in cmd
+ memset(&cmd, 0, sizeof(cmd));
+
+ cmd.smart_power_oled_getmaxcll.header.type = DMUB_CMD__SMART_POWER_OLED;
+ cmd.smart_power_oled_getmaxcll.header.sub_type = DMUB_CMD__SMART_POWER_OLED_GETMAXCLL;
+ cmd.smart_power_oled_getmaxcll.header.payload_bytes = sizeof(cmd.smart_power_oled_getmaxcll.data);
+ cmd.smart_power_oled_getmaxcll.header.ret_status = 1;
+
+ cmd.smart_power_oled_getmaxcll.data.input.panel_inst = panel_inst;
+
+ // send cmd and wait for reply
+ status = dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY);
+
+ if (status)
+ *pCurrent_MaxCLL = cmd.smart_power_oled_getmaxcll.data.output.current_max_cll;
+ else
+ *pCurrent_MaxCLL = 0;
+
+ return status;
+}
+
uint8_t get_link_index_from_dpia_port_index(const struct dc *dc,
uint8_t dpia_port_index)
{
@@ -6349,7 +6489,7 @@ bool dc_is_cursor_limit_pending(struct dc *dc)
return false;
}
-bool dc_can_clear_cursor_limit(struct dc *dc)
+bool dc_can_clear_cursor_limit(const struct dc *dc)
{
uint32_t i;
@@ -6378,3 +6518,576 @@ void dc_get_underflow_debug_data_for_otg(struct dc *dc, int primary_otg_inst,
if (dc->hwss.get_underflow_debug_data)
dc->hwss.get_underflow_debug_data(dc, tg, out_data);
}
+
+void dc_get_power_feature_status(struct dc *dc, int primary_otg_inst,
+ struct power_features *out_data)
+{
+ out_data->uclk_p_state = dc->current_state->clk_mgr->clks.p_state_change_support;
+ out_data->fams = dc->current_state->bw_ctx.bw.dcn.clk.fw_based_mclk_switching;
+}
+
+bool dc_capture_register_software_state(struct dc *dc, struct dc_register_software_state *state)
+{
+ struct dc_state *context;
+ struct resource_context *res_ctx;
+ int i;
+
+ if (!dc || !dc->current_state || !state) {
+ if (state)
+ state->state_valid = false;
+ return false;
+ }
+
+ /* Initialize the state structure */
+ memset(state, 0, sizeof(struct dc_register_software_state));
+
+ context = dc->current_state;
+ res_ctx = &context->res_ctx;
+
+ /* Count active pipes and streams */
+ state->active_pipe_count = 0;
+ state->active_stream_count = context->stream_count;
+
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ if (res_ctx->pipe_ctx[i].stream)
+ state->active_pipe_count++;
+ }
+
+ /* Capture HUBP programming state for each pipe */
+ for (i = 0; i < MAX_PIPES && i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
+
+ state->hubp[i].valid_stream = false;
+ if (!pipe_ctx->stream)
+ continue;
+
+ state->hubp[i].valid_stream = true;
+
+ /* HUBP register programming variables */
+ if (pipe_ctx->stream_res.tg)
+ state->hubp[i].vtg_sel = pipe_ctx->stream_res.tg->inst;
+
+ state->hubp[i].hubp_clock_enable = (pipe_ctx->plane_res.hubp != NULL) ? 1 : 0;
+
+ state->hubp[i].valid_plane_state = false;
+ if (pipe_ctx->plane_state) {
+ state->hubp[i].valid_plane_state = true;
+ state->hubp[i].surface_pixel_format = pipe_ctx->plane_state->format;
+ state->hubp[i].rotation_angle = pipe_ctx->plane_state->rotation;
+ state->hubp[i].h_mirror_en = pipe_ctx->plane_state->horizontal_mirror ? 1 : 0;
+
+ /* Surface size */
+ if (pipe_ctx->plane_state->plane_size.surface_size.width > 0) {
+ state->hubp[i].surface_size_width = pipe_ctx->plane_state->plane_size.surface_size.width;
+ state->hubp[i].surface_size_height = pipe_ctx->plane_state->plane_size.surface_size.height;
+ }
+
+ /* Viewport dimensions from scaler data */
+ if (pipe_ctx->plane_state->src_rect.width > 0) {
+ state->hubp[i].pri_viewport_width = pipe_ctx->plane_state->src_rect.width;
+ state->hubp[i].pri_viewport_height = pipe_ctx->plane_state->src_rect.height;
+ state->hubp[i].pri_viewport_x_start = pipe_ctx->plane_state->src_rect.x;
+ state->hubp[i].pri_viewport_y_start = pipe_ctx->plane_state->src_rect.y;
+ }
+
+ /* DCC settings */
+ state->hubp[i].surface_dcc_en = (pipe_ctx->plane_state->dcc.enable) ? 1 : 0;
+ state->hubp[i].surface_dcc_ind_64b_blk = pipe_ctx->plane_state->dcc.independent_64b_blks;
+ state->hubp[i].surface_dcc_ind_128b_blk = pipe_ctx->plane_state->dcc.dcc_ind_blk;
+
+ /* Surface pitch */
+ state->hubp[i].surface_pitch = pipe_ctx->plane_state->plane_size.surface_pitch;
+ state->hubp[i].meta_pitch = pipe_ctx->plane_state->dcc.meta_pitch;
+ state->hubp[i].chroma_pitch = pipe_ctx->plane_state->plane_size.chroma_pitch;
+ state->hubp[i].meta_pitch_c = pipe_ctx->plane_state->dcc.meta_pitch_c;
+
+ /* Surface addresses - primary */
+ state->hubp[i].primary_surface_address_low = pipe_ctx->plane_state->address.grph.addr.low_part;
+ state->hubp[i].primary_surface_address_high = pipe_ctx->plane_state->address.grph.addr.high_part;
+ state->hubp[i].primary_meta_surface_address_low = pipe_ctx->plane_state->address.grph.meta_addr.low_part;
+ state->hubp[i].primary_meta_surface_address_high = pipe_ctx->plane_state->address.grph.meta_addr.high_part;
+
+ /* TMZ settings */
+ state->hubp[i].primary_surface_tmz = pipe_ctx->plane_state->address.tmz_surface;
+ state->hubp[i].primary_meta_surface_tmz = pipe_ctx->plane_state->address.tmz_surface;
+
+ /* Tiling configuration */
+ state->hubp[i].min_dc_gfx_version9 = false;
+ if (pipe_ctx->plane_state->tiling_info.gfxversion >= DcGfxVersion9) {
+ state->hubp[i].min_dc_gfx_version9 = true;
+ state->hubp[i].sw_mode = pipe_ctx->plane_state->tiling_info.gfx9.swizzle;
+ state->hubp[i].num_pipes = pipe_ctx->plane_state->tiling_info.gfx9.num_pipes;
+ state->hubp[i].num_banks = pipe_ctx->plane_state->tiling_info.gfx9.num_banks;
+ state->hubp[i].pipe_interleave = pipe_ctx->plane_state->tiling_info.gfx9.pipe_interleave;
+ state->hubp[i].num_shader_engines = pipe_ctx->plane_state->tiling_info.gfx9.num_shader_engines;
+ state->hubp[i].num_rb_per_se = pipe_ctx->plane_state->tiling_info.gfx9.num_rb_per_se;
+ state->hubp[i].num_pkrs = pipe_ctx->plane_state->tiling_info.gfx9.num_pkrs;
+ }
+ }
+
+ /* DML Request Size Configuration */
+ if (pipe_ctx->rq_regs.rq_regs_l.chunk_size > 0) {
+ state->hubp[i].rq_chunk_size = pipe_ctx->rq_regs.rq_regs_l.chunk_size;
+ state->hubp[i].rq_min_chunk_size = pipe_ctx->rq_regs.rq_regs_l.min_chunk_size;
+ state->hubp[i].rq_meta_chunk_size = pipe_ctx->rq_regs.rq_regs_l.meta_chunk_size;
+ state->hubp[i].rq_min_meta_chunk_size = pipe_ctx->rq_regs.rq_regs_l.min_meta_chunk_size;
+ state->hubp[i].rq_dpte_group_size = pipe_ctx->rq_regs.rq_regs_l.dpte_group_size;
+ state->hubp[i].rq_mpte_group_size = pipe_ctx->rq_regs.rq_regs_l.mpte_group_size;
+ state->hubp[i].rq_swath_height_l = pipe_ctx->rq_regs.rq_regs_l.swath_height;
+ state->hubp[i].rq_pte_row_height_l = pipe_ctx->rq_regs.rq_regs_l.pte_row_height_linear;
+ }
+
+ /* Chroma request size configuration */
+ if (pipe_ctx->rq_regs.rq_regs_c.chunk_size > 0) {
+ state->hubp[i].rq_chunk_size_c = pipe_ctx->rq_regs.rq_regs_c.chunk_size;
+ state->hubp[i].rq_min_chunk_size_c = pipe_ctx->rq_regs.rq_regs_c.min_chunk_size;
+ state->hubp[i].rq_meta_chunk_size_c = pipe_ctx->rq_regs.rq_regs_c.meta_chunk_size;
+ state->hubp[i].rq_min_meta_chunk_size_c = pipe_ctx->rq_regs.rq_regs_c.min_meta_chunk_size;
+ state->hubp[i].rq_dpte_group_size_c = pipe_ctx->rq_regs.rq_regs_c.dpte_group_size;
+ state->hubp[i].rq_mpte_group_size_c = pipe_ctx->rq_regs.rq_regs_c.mpte_group_size;
+ state->hubp[i].rq_swath_height_c = pipe_ctx->rq_regs.rq_regs_c.swath_height;
+ state->hubp[i].rq_pte_row_height_c = pipe_ctx->rq_regs.rq_regs_c.pte_row_height_linear;
+ }
+
+ /* DML expansion modes */
+ state->hubp[i].drq_expansion_mode = pipe_ctx->rq_regs.drq_expansion_mode;
+ state->hubp[i].prq_expansion_mode = pipe_ctx->rq_regs.prq_expansion_mode;
+ state->hubp[i].mrq_expansion_mode = pipe_ctx->rq_regs.mrq_expansion_mode;
+ state->hubp[i].crq_expansion_mode = pipe_ctx->rq_regs.crq_expansion_mode;
+
+ /* DML DLG parameters - nominal */
+ state->hubp[i].dst_y_per_vm_vblank = pipe_ctx->dlg_regs.dst_y_per_vm_vblank;
+ state->hubp[i].dst_y_per_row_vblank = pipe_ctx->dlg_regs.dst_y_per_row_vblank;
+ state->hubp[i].dst_y_per_vm_flip = pipe_ctx->dlg_regs.dst_y_per_vm_flip;
+ state->hubp[i].dst_y_per_row_flip = pipe_ctx->dlg_regs.dst_y_per_row_flip;
+
+ /* DML prefetch settings */
+ state->hubp[i].dst_y_prefetch = pipe_ctx->dlg_regs.dst_y_prefetch;
+ state->hubp[i].vratio_prefetch = pipe_ctx->dlg_regs.vratio_prefetch;
+ state->hubp[i].vratio_prefetch_c = pipe_ctx->dlg_regs.vratio_prefetch_c;
+
+ /* TTU parameters */
+ state->hubp[i].qos_level_low_wm = pipe_ctx->ttu_regs.qos_level_low_wm;
+ state->hubp[i].qos_level_high_wm = pipe_ctx->ttu_regs.qos_level_high_wm;
+ state->hubp[i].qos_level_flip = pipe_ctx->ttu_regs.qos_level_flip;
+ state->hubp[i].min_ttu_vblank = pipe_ctx->ttu_regs.min_ttu_vblank;
+ }
+
+ /* Capture HUBBUB programming state */
+ if (dc->res_pool->hubbub) {
+ /* Individual DET buffer sizes - software state variables that program DET registers */
+ for (i = 0; i < 4 && i < dc->res_pool->pipe_count; i++) {
+ uint32_t det_size = res_ctx->pipe_ctx[i].det_buffer_size_kb;
+ switch (i) {
+ case 0:
+ state->hubbub.det0_size = det_size;
+ break;
+ case 1:
+ state->hubbub.det1_size = det_size;
+ break;
+ case 2:
+ state->hubbub.det2_size = det_size;
+ break;
+ case 3:
+ state->hubbub.det3_size = det_size;
+ break;
+ }
+ }
+
+ /* Compression buffer configuration - software state that programs COMPBUF_SIZE register */
+ // TODO: Handle logic for legacy DCN pre-DCN401
+ state->hubbub.compbuf_size = context->bw_ctx.bw.dcn.arb_regs.compbuf_size;
+ }
+
+ /* Capture DPP programming state for each pipe */
+ for (i = 0; i < MAX_PIPES && i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
+
+ if (!pipe_ctx->stream)
+ continue;
+
+ state->dpp[i].dpp_clock_enable = (pipe_ctx->plane_res.dpp != NULL) ? 1 : 0;
+
+ if (pipe_ctx->plane_state && pipe_ctx->plane_res.scl_data.recout.width > 0) {
+ /* Access dscl_prog_data directly - this contains the actual software state used for register programming */
+ struct dscl_prog_data *dscl_data = &pipe_ctx->plane_res.scl_data.dscl_prog_data;
+
+ /* Recout (Rectangle of Interest) configuration - software state that programs RECOUT registers */
+ state->dpp[i].recout_start_x = dscl_data->recout.x;
+ state->dpp[i].recout_start_y = dscl_data->recout.y;
+ state->dpp[i].recout_width = dscl_data->recout.width;
+ state->dpp[i].recout_height = dscl_data->recout.height;
+
+ /* MPC (Multiple Pipe/Plane Combiner) size - software state that programs MPC_SIZE registers */
+ state->dpp[i].mpc_width = dscl_data->mpc_size.width;
+ state->dpp[i].mpc_height = dscl_data->mpc_size.height;
+
+ /* DSCL mode - software state that programs SCL_MODE registers */
+ state->dpp[i].dscl_mode = dscl_data->dscl_mode;
+
+ /* Scaler ratios - software state that programs scale ratio registers (use actual programmed ratios) */
+ state->dpp[i].horz_ratio_int = dscl_data->ratios.h_scale_ratio >> 19; // Extract integer part from programmed ratio
+ state->dpp[i].vert_ratio_int = dscl_data->ratios.v_scale_ratio >> 19; // Extract integer part from programmed ratio
+
+ /* Basic scaler taps - software state that programs tap control registers (use actual programmed taps) */
+ state->dpp[i].h_taps = dscl_data->taps.h_taps + 1; // dscl_prog_data.taps stores (taps - 1), so add 1 back
+ state->dpp[i].v_taps = dscl_data->taps.v_taps + 1; // dscl_prog_data.taps stores (taps - 1), so add 1 back
+ }
+ }
+
+ /* Capture essential clock state for underflow analysis */
+ if (dc->clk_mgr && dc->clk_mgr->clks.dispclk_khz > 0) {
+ /* Core display clocks affecting bandwidth and timing */
+ state->dccg.dispclk_khz = dc->clk_mgr->clks.dispclk_khz;
+
+ /* Per-pipe clock configuration - only capture what's essential */
+ for (i = 0; i < MAX_PIPES && i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
+ if (pipe_ctx->stream) {
+ /* Essential clocks that directly affect underflow risk */
+ state->dccg.dppclk_khz[i] = dc->clk_mgr->clks.dppclk_khz;
+ state->dccg.pixclk_khz[i] = pipe_ctx->stream->timing.pix_clk_100hz / 10;
+ state->dccg.dppclk_enable[i] = 1;
+
+ /* DP stream clock only for DP signals */
+ if (pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
+ pipe_ctx->stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
+ state->dccg.dpstreamclk_enable[i] = 1;
+ } else {
+ state->dccg.dpstreamclk_enable[i] = 0;
+ }
+ } else {
+ /* Inactive pipe - no clocks */
+ state->dccg.dppclk_khz[i] = 0;
+ state->dccg.pixclk_khz[i] = 0;
+ state->dccg.dppclk_enable[i] = 0;
+ if (i < 4) {
+ state->dccg.dpstreamclk_enable[i] = 0;
+ }
+ }
+ }
+
+ /* DSC clock state - only when actually using DSC */
+ for (i = 0; i < MAX_PIPES; i++) {
+ struct pipe_ctx *pipe_ctx = (i < dc->res_pool->pipe_count) ? &res_ctx->pipe_ctx[i] : NULL;
+ if (pipe_ctx && pipe_ctx->stream && pipe_ctx->stream->timing.dsc_cfg.num_slices_h > 0) {
+ state->dccg.dscclk_khz[i] = 400000; /* Typical DSC clock frequency */
+ } else {
+ state->dccg.dscclk_khz[i] = 0;
+ }
+ }
+
+ /* SYMCLK32 LE Control - only the essential HPO state for underflow analysis */
+ for (i = 0; i < 2; i++) {
+ state->dccg.symclk32_le_enable[i] = 0; /* Default: disabled */
+ }
+
+ }
+
+ /* Capture essential DSC configuration for underflow analysis */
+ for (i = 0; i < MAX_PIPES && i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
+
+ if (pipe_ctx->stream && pipe_ctx->stream->timing.dsc_cfg.num_slices_h > 0) {
+ /* DSC is enabled - capture essential configuration */
+ state->dsc[i].dsc_clock_enable = 1;
+
+ /* DSC configuration affecting bandwidth and timing */
+ struct dc_dsc_config *dsc_cfg = &pipe_ctx->stream->timing.dsc_cfg;
+ state->dsc[i].dsc_num_slices_h = dsc_cfg->num_slices_h;
+ state->dsc[i].dsc_num_slices_v = dsc_cfg->num_slices_v;
+ state->dsc[i].dsc_bits_per_pixel = dsc_cfg->bits_per_pixel;
+
+ /* OPP pipe source for DSC forwarding */
+ if (pipe_ctx->stream_res.opp) {
+ state->dsc[i].dscrm_dsc_forward_enable = 1;
+ state->dsc[i].dscrm_dsc_opp_pipe_source = pipe_ctx->stream_res.opp->inst;
+ } else {
+ state->dsc[i].dscrm_dsc_forward_enable = 0;
+ state->dsc[i].dscrm_dsc_opp_pipe_source = 0;
+ }
+ } else {
+ /* DSC not enabled - clear all fields */
+ memset(&state->dsc[i], 0, sizeof(state->dsc[i]));
+ }
+ }
+
+ /* Capture MPC programming state - comprehensive register field coverage */
+ for (i = 0; i < MAX_PIPES && i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
+
+ if (pipe_ctx->plane_state && pipe_ctx->stream) {
+ struct dc_plane_state *plane_state = pipe_ctx->plane_state;
+
+ /* MPCC blending tree and mode control - capture actual blend configuration */
+ state->mpc.mpcc_mode[i] = (plane_state->blend_tf.type != TF_TYPE_BYPASS) ? 1 : 0;
+ state->mpc.mpcc_alpha_blend_mode[i] = plane_state->per_pixel_alpha ? 1 : 0;
+ state->mpc.mpcc_alpha_multiplied_mode[i] = plane_state->pre_multiplied_alpha ? 1 : 0;
+ state->mpc.mpcc_blnd_active_overlap_only[i] = 0; /* Default - no overlap restriction */
+ state->mpc.mpcc_global_alpha[i] = plane_state->global_alpha_value;
+ state->mpc.mpcc_global_gain[i] = plane_state->global_alpha ? 255 : 0;
+ state->mpc.mpcc_bg_bpc[i] = 8; /* Standard 8-bit background */
+ state->mpc.mpcc_bot_gain_mode[i] = 0; /* Standard gain mode */
+
+ /* MPCC blending tree connections - capture tree topology */
+ if (pipe_ctx->bottom_pipe) {
+ state->mpc.mpcc_bot_sel[i] = pipe_ctx->bottom_pipe->pipe_idx;
+ } else {
+ state->mpc.mpcc_bot_sel[i] = 0xF; /* No bottom connection */
+ }
+ state->mpc.mpcc_top_sel[i] = pipe_ctx->pipe_idx; /* This pipe's DPP ID */
+
+ /* MPCC output gamma control - capture gamma programming */
+ if (plane_state->gamma_correction.type != GAMMA_CS_TFM_1D && plane_state->gamma_correction.num_entries > 0) {
+ state->mpc.mpcc_ogam_mode[i] = 1; /* Gamma enabled */
+ state->mpc.mpcc_ogam_select[i] = 0; /* Bank A selection */
+ state->mpc.mpcc_ogam_pwl_disable[i] = 0; /* PWL enabled */
+ } else {
+ state->mpc.mpcc_ogam_mode[i] = 0; /* Bypass mode */
+ state->mpc.mpcc_ogam_select[i] = 0;
+ state->mpc.mpcc_ogam_pwl_disable[i] = 1; /* PWL disabled */
+ }
+
+ /* MPCC pipe assignment and operational status */
+ if (pipe_ctx->stream_res.opp) {
+ state->mpc.mpcc_opp_id[i] = pipe_ctx->stream_res.opp->inst;
+ } else {
+ state->mpc.mpcc_opp_id[i] = 0xF; /* No OPP assignment */
+ }
+
+ /* MPCC status indicators - active pipe state */
+ state->mpc.mpcc_idle[i] = 0; /* Active pipe - not idle */
+ state->mpc.mpcc_busy[i] = 1; /* Active pipe - busy processing */
+
+ } else {
+ /* Pipe not active - set disabled/idle state for all fields */
+ state->mpc.mpcc_mode[i] = 0;
+ state->mpc.mpcc_alpha_blend_mode[i] = 0;
+ state->mpc.mpcc_alpha_multiplied_mode[i] = 0;
+ state->mpc.mpcc_blnd_active_overlap_only[i] = 0;
+ state->mpc.mpcc_global_alpha[i] = 0;
+ state->mpc.mpcc_global_gain[i] = 0;
+ state->mpc.mpcc_bg_bpc[i] = 0;
+ state->mpc.mpcc_bot_gain_mode[i] = 0;
+ state->mpc.mpcc_bot_sel[i] = 0xF; /* No bottom connection */
+ state->mpc.mpcc_top_sel[i] = 0xF; /* No top connection */
+ state->mpc.mpcc_ogam_mode[i] = 0; /* Bypass */
+ state->mpc.mpcc_ogam_select[i] = 0;
+ state->mpc.mpcc_ogam_pwl_disable[i] = 1; /* PWL disabled */
+ state->mpc.mpcc_opp_id[i] = 0xF; /* No OPP assignment */
+ state->mpc.mpcc_idle[i] = 1; /* Idle */
+ state->mpc.mpcc_busy[i] = 0; /* Not busy */
+ }
+ }
+
+ /* Capture OPP programming state for each pipe - comprehensive register field coverage */
+ for (i = 0; i < MAX_PIPES && i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
+
+ if (!pipe_ctx->stream)
+ continue;
+
+ if (pipe_ctx->stream_res.opp) {
+ struct dc_crtc_timing *timing = &pipe_ctx->stream->timing;
+
+ /* OPP Pipe Control */
+ state->opp[i].opp_pipe_clock_enable = 1; /* Active pipe has clock enabled */
+
+ /* Display Pattern Generator (DPG) Control - 19 fields */
+ if (pipe_ctx->stream->test_pattern.type != DP_TEST_PATTERN_VIDEO_MODE) {
+ state->opp[i].dpg_enable = 1;
+ } else {
+ /* Video mode - DPG disabled */
+ state->opp[i].dpg_enable = 0;
+ }
+
+ /* Format Control (FMT) - 18 fields */
+ state->opp[i].fmt_pixel_encoding = timing->pixel_encoding;
+
+ /* Chroma subsampling mode based on pixel encoding */
+ if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR420) {
+ state->opp[i].fmt_subsampling_mode = 1; /* 4:2:0 subsampling */
+ } else if (timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
+ state->opp[i].fmt_subsampling_mode = 2; /* 4:2:2 subsampling */
+ } else {
+ state->opp[i].fmt_subsampling_mode = 0; /* No subsampling (4:4:4) */
+ }
+
+ state->opp[i].fmt_cbcr_bit_reduction_bypass = (timing->pixel_encoding == PIXEL_ENCODING_RGB) ? 1 : 0;
+ state->opp[i].fmt_stereosync_override = (timing->timing_3d_format != TIMING_3D_FORMAT_NONE) ? 1 : 0;
+
+ /* Dithering control based on bit depth */
+ if (timing->display_color_depth < COLOR_DEPTH_121212) {
+ state->opp[i].fmt_spatial_dither_frame_counter_max = 15; /* Typical frame counter max */
+ state->opp[i].fmt_spatial_dither_frame_counter_bit_swap = 0; /* No bit swapping */
+ state->opp[i].fmt_spatial_dither_enable = 1;
+ state->opp[i].fmt_spatial_dither_mode = 0; /* Spatial dithering mode */
+ state->opp[i].fmt_spatial_dither_depth = timing->display_color_depth;
+ state->opp[i].fmt_temporal_dither_enable = 0; /* Spatial dithering preferred */
+ } else {
+ state->opp[i].fmt_spatial_dither_frame_counter_max = 0;
+ state->opp[i].fmt_spatial_dither_frame_counter_bit_swap = 0;
+ state->opp[i].fmt_spatial_dither_enable = 0;
+ state->opp[i].fmt_spatial_dither_mode = 0;
+ state->opp[i].fmt_spatial_dither_depth = 0;
+ state->opp[i].fmt_temporal_dither_enable = 0;
+ }
+
+ /* Truncation control for bit depth reduction */
+ if (timing->display_color_depth < COLOR_DEPTH_121212) {
+ state->opp[i].fmt_truncate_enable = 1;
+ state->opp[i].fmt_truncate_depth = timing->display_color_depth;
+ state->opp[i].fmt_truncate_mode = 0; /* Round mode */
+ } else {
+ state->opp[i].fmt_truncate_enable = 0;
+ state->opp[i].fmt_truncate_depth = 0;
+ state->opp[i].fmt_truncate_mode = 0;
+ }
+
+ /* Data clamping control */
+ state->opp[i].fmt_clamp_data_enable = 1; /* Clamping typically enabled */
+ state->opp[i].fmt_clamp_color_format = timing->pixel_encoding;
+
+ /* Dynamic expansion for limited range content */
+ if (timing->pixel_encoding != PIXEL_ENCODING_RGB) {
+ state->opp[i].fmt_dynamic_exp_enable = 1; /* YCbCr typically needs expansion */
+ state->opp[i].fmt_dynamic_exp_mode = 0; /* Standard expansion */
+ } else {
+ state->opp[i].fmt_dynamic_exp_enable = 0; /* RGB typically full range */
+ state->opp[i].fmt_dynamic_exp_mode = 0;
+ }
+
+ /* Legacy field for compatibility */
+ state->opp[i].fmt_bit_depth_control = timing->display_color_depth;
+
+ /* Output Buffer (OPPBUF) Control - 6 fields */
+ state->opp[i].oppbuf_active_width = timing->h_addressable;
+ state->opp[i].oppbuf_pixel_repetition = 0; /* No pixel repetition by default */
+
+ /* Multi-Stream Output (MSO) / ODM segmentation */
+ if (pipe_ctx->next_odm_pipe) {
+ state->opp[i].oppbuf_display_segmentation = 1; /* Segmented display */
+ state->opp[i].oppbuf_overlap_pixel_num = 0; /* ODM overlap pixels */
+ } else {
+ state->opp[i].oppbuf_display_segmentation = 0; /* Single segment */
+ state->opp[i].oppbuf_overlap_pixel_num = 0;
+ }
+
+ /* 3D/Stereo control */
+ if (timing->timing_3d_format != TIMING_3D_FORMAT_NONE) {
+ state->opp[i].oppbuf_3d_vact_space1_size = 30; /* Typical stereo blanking */
+ state->opp[i].oppbuf_3d_vact_space2_size = 30;
+ } else {
+ state->opp[i].oppbuf_3d_vact_space1_size = 0;
+ state->opp[i].oppbuf_3d_vact_space2_size = 0;
+ }
+
+ /* DSC Forward Config - 3 fields */
+ if (timing->dsc_cfg.num_slices_h > 0) {
+ state->opp[i].dscrm_dsc_forward_enable = 1;
+ state->opp[i].dscrm_dsc_opp_pipe_source = pipe_ctx->stream_res.opp->inst;
+ state->opp[i].dscrm_dsc_forward_enable_status = 1; /* Status follows enable */
+ } else {
+ state->opp[i].dscrm_dsc_forward_enable = 0;
+ state->opp[i].dscrm_dsc_opp_pipe_source = 0;
+ state->opp[i].dscrm_dsc_forward_enable_status = 0;
+ }
+ } else {
+ /* No OPP resource - set all fields to disabled state */
+ memset(&state->opp[i], 0, sizeof(state->opp[i]));
+ }
+ }
+
+ /* Capture OPTC programming state for each pipe - comprehensive register field coverage */
+ for (i = 0; i < MAX_PIPES && i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &res_ctx->pipe_ctx[i];
+
+ if (!pipe_ctx->stream)
+ continue;
+
+ if (pipe_ctx->stream_res.tg) {
+ struct dc_crtc_timing *timing = &pipe_ctx->stream->timing;
+
+ state->optc[i].otg_master_inst = pipe_ctx->stream_res.tg->inst;
+
+ /* OTG_CONTROL register - 5 fields */
+ state->optc[i].otg_master_enable = 1; /* Active stream */
+ state->optc[i].otg_disable_point_cntl = 0; /* Normal operation */
+ state->optc[i].otg_start_point_cntl = 0; /* Normal start */
+ state->optc[i].otg_field_number_cntl = (timing->flags.INTERLACE) ? 1 : 0;
+ state->optc[i].otg_out_mux = 0; /* Direct output */
+
+ /* OTG Horizontal Timing - 7 fields */
+ state->optc[i].otg_h_total = timing->h_total;
+ state->optc[i].otg_h_blank_start = timing->h_addressable;
+ state->optc[i].otg_h_blank_end = timing->h_total - timing->h_front_porch;
+ state->optc[i].otg_h_sync_start = timing->h_addressable + timing->h_front_porch;
+ state->optc[i].otg_h_sync_end = timing->h_addressable + timing->h_front_porch + timing->h_sync_width;
+ state->optc[i].otg_h_sync_polarity = timing->flags.HSYNC_POSITIVE_POLARITY ? 0 : 1;
+ state->optc[i].otg_h_timing_div_mode = (pipe_ctx->next_odm_pipe) ? 1 : 0; /* ODM divide mode */
+
+ /* OTG Vertical Timing - 7 fields */
+ state->optc[i].otg_v_total = timing->v_total;
+ state->optc[i].otg_v_blank_start = timing->v_addressable;
+ state->optc[i].otg_v_blank_end = timing->v_total - timing->v_front_porch;
+ state->optc[i].otg_v_sync_start = timing->v_addressable + timing->v_front_porch;
+ state->optc[i].otg_v_sync_end = timing->v_addressable + timing->v_front_porch + timing->v_sync_width;
+ state->optc[i].otg_v_sync_polarity = timing->flags.VSYNC_POSITIVE_POLARITY ? 0 : 1;
+ state->optc[i].otg_v_sync_mode = 0; /* Normal sync mode */
+
+ /* Initialize remaining core fields with appropriate defaults */
+ // TODO: Update logic for accurate vtotal min/max
+ state->optc[i].otg_v_total_max = timing->v_total + 100; /* Typical DRR range */
+ state->optc[i].otg_v_total_min = timing->v_total - 50;
+ state->optc[i].otg_v_total_mid = timing->v_total;
+
+ /* ODM configuration */
+ // TODO: Update logic to have complete ODM mappings (e.g. 3:1 and 4:1) stored in single pipe
+ if (pipe_ctx->next_odm_pipe) {
+ state->optc[i].optc_seg0_src_sel = pipe_ctx->stream_res.opp ? pipe_ctx->stream_res.opp->inst : 0;
+ state->optc[i].optc_seg1_src_sel = pipe_ctx->next_odm_pipe->stream_res.opp ? pipe_ctx->next_odm_pipe->stream_res.opp->inst : 0;
+ state->optc[i].optc_num_of_input_segment = 1; /* 2 segments - 1 */
+ } else {
+ state->optc[i].optc_seg0_src_sel = pipe_ctx->stream_res.opp ? pipe_ctx->stream_res.opp->inst : 0;
+ state->optc[i].optc_seg1_src_sel = 0;
+ state->optc[i].optc_num_of_input_segment = 0; /* Single segment */
+ }
+
+ /* DSC configuration */
+ if (timing->dsc_cfg.num_slices_h > 0) {
+ state->optc[i].optc_dsc_mode = 1; /* DSC enabled */
+ state->optc[i].optc_dsc_bytes_per_pixel = timing->dsc_cfg.bits_per_pixel / 16; /* Convert to bytes */
+ state->optc[i].optc_dsc_slice_width = timing->h_addressable / timing->dsc_cfg.num_slices_h;
+ } else {
+ state->optc[i].optc_dsc_mode = 0;
+ state->optc[i].optc_dsc_bytes_per_pixel = 0;
+ state->optc[i].optc_dsc_slice_width = 0;
+ }
+
+ /* Essential control fields */
+ state->optc[i].otg_stereo_enable = (timing->timing_3d_format != TIMING_3D_FORMAT_NONE) ? 1 : 0;
+ state->optc[i].otg_interlace_enable = timing->flags.INTERLACE ? 1 : 0;
+ state->optc[i].otg_clock_enable = 1; /* OTG clock enabled */
+ state->optc[i].vtg0_enable = 1; /* VTG enabled for timing generation */
+
+ /* Initialize other key fields to defaults */
+ state->optc[i].optc_input_pix_clk_en = 1;
+ state->optc[i].optc_segment_width = (pipe_ctx->next_odm_pipe) ? (timing->h_addressable / 2) : timing->h_addressable;
+ state->optc[i].otg_vready_offset = 1;
+ state->optc[i].otg_vstartup_start = timing->v_addressable + 10;
+ state->optc[i].otg_vupdate_offset = 0;
+ state->optc[i].otg_vupdate_width = 5;
+ } else {
+ /* No timing generator resource - initialize all fields to 0 */
+ memset(&state->optc[i], 0, sizeof(state->optc[i]));
+ }
+ }
+
+ state->state_valid = true;
+ return true;
+}
+
+void dc_log_preos_dmcub_info(const struct dc *dc)
+{
+ dc_dmub_srv_log_preos_dmcub_info(dc->ctx->dmub_srv);
+}
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 d82b1cb467f4..e2763b60482a 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
@@ -32,6 +32,13 @@
#include "resource.h"
#include "dc_dmub_srv.h"
#include "dc_state_priv.h"
+#include "opp.h"
+#include "dsc.h"
+#include "dchubbub.h"
+#include "dccg.h"
+#include "abm.h"
+#include "dcn10/dcn10_hubbub.h"
+#include "dce/dmub_hw_lock_mgr.h"
#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0]))
#define MAX_NUM_MCACHE 8
@@ -258,7 +265,7 @@ void color_space_to_black_color(
black_color_format[BLACK_COLOR_FORMAT_RGB_LIMITED];
break;
- /**
+ /*
* Remove default and add case for all color space
* so when we forget to add new color space
* compiler will give a warning
@@ -755,11 +762,13 @@ void hwss_build_fast_sequence(struct dc *dc,
block_sequence[*num_steps].func = DMUB_SUBVP_PIPE_CONTROL_LOCK_FAST;
(*num_steps)++;
}
- if (dc->hwss.fams2_global_control_lock_fast) {
- block_sequence[*num_steps].params.fams2_global_control_lock_fast_params.dc = dc;
- block_sequence[*num_steps].params.fams2_global_control_lock_fast_params.lock = true;
- block_sequence[*num_steps].params.fams2_global_control_lock_fast_params.is_required = dc_state_is_fams2_in_use(dc, context);
- block_sequence[*num_steps].func = DMUB_FAMS2_GLOBAL_CONTROL_LOCK_FAST;
+ if (dc->hwss.dmub_hw_control_lock_fast) {
+ block_sequence[*num_steps].params.dmub_hw_control_lock_fast_params.dc = dc;
+ block_sequence[*num_steps].params.dmub_hw_control_lock_fast_params.lock = true;
+ block_sequence[*num_steps].params.dmub_hw_control_lock_fast_params.is_required =
+ dc_state_is_fams2_in_use(dc, context) ||
+ dmub_hw_lock_mgr_does_link_require_lock(dc, stream->link);
+ block_sequence[*num_steps].func = DMUB_HW_CONTROL_LOCK_FAST;
(*num_steps)++;
}
if (dc->hwss.pipe_control_lock) {
@@ -784,7 +793,7 @@ void hwss_build_fast_sequence(struct dc *dc,
while (current_mpc_pipe) {
if (current_mpc_pipe->plane_state) {
if (dc->hwss.set_flip_control_gsl && current_mpc_pipe->plane_state->update_flags.raw) {
- block_sequence[*num_steps].params.set_flip_control_gsl_params.pipe_ctx = current_mpc_pipe;
+ block_sequence[*num_steps].params.set_flip_control_gsl_params.hubp = current_mpc_pipe->plane_res.hubp;
block_sequence[*num_steps].params.set_flip_control_gsl_params.flip_immediate = current_mpc_pipe->plane_state->flip_immediate;
block_sequence[*num_steps].func = HUBP_SET_FLIP_CONTROL_GSL;
(*num_steps)++;
@@ -894,11 +903,11 @@ void hwss_build_fast_sequence(struct dc *dc,
block_sequence[*num_steps].func = DMUB_SUBVP_PIPE_CONTROL_LOCK_FAST;
(*num_steps)++;
}
- if (dc->hwss.fams2_global_control_lock_fast) {
- block_sequence[*num_steps].params.fams2_global_control_lock_fast_params.dc = dc;
- block_sequence[*num_steps].params.fams2_global_control_lock_fast_params.lock = false;
- block_sequence[*num_steps].params.fams2_global_control_lock_fast_params.is_required = dc_state_is_fams2_in_use(dc, context);
- block_sequence[*num_steps].func = DMUB_FAMS2_GLOBAL_CONTROL_LOCK_FAST;
+ if (dc->hwss.dmub_hw_control_lock_fast) {
+ block_sequence[*num_steps].params.dmub_hw_control_lock_fast_params.dc = dc;
+ block_sequence[*num_steps].params.dmub_hw_control_lock_fast_params.lock = false;
+ block_sequence[*num_steps].params.dmub_hw_control_lock_fast_params.is_required = dc_state_is_fams2_in_use(dc, context);
+ block_sequence[*num_steps].func = DMUB_HW_CONTROL_LOCK_FAST;
(*num_steps)++;
}
@@ -911,6 +920,13 @@ void hwss_build_fast_sequence(struct dc *dc,
current_mpc_pipe->stream && current_mpc_pipe->plane_state &&
current_mpc_pipe->plane_state->update_flags.bits.addr_update &&
!current_mpc_pipe->plane_state->skip_manual_trigger) {
+ if (dc->hwss.program_cursor_offload_now) {
+ block_sequence[*num_steps].params.program_cursor_update_now_params.dc = dc;
+ block_sequence[*num_steps].params.program_cursor_update_now_params.pipe_ctx = current_mpc_pipe;
+ block_sequence[*num_steps].func = PROGRAM_CURSOR_UPDATE_NOW;
+ (*num_steps)++;
+ }
+
block_sequence[*num_steps].params.program_manual_trigger_params.pipe_ctx = current_mpc_pipe;
block_sequence[*num_steps].func = OPTC_PROGRAM_MANUAL_TRIGGER;
(*num_steps)++;
@@ -942,8 +958,9 @@ void hwss_execute_sequence(struct dc *dc,
params->pipe_control_lock_params.lock);
break;
case HUBP_SET_FLIP_CONTROL_GSL:
- dc->hwss.set_flip_control_gsl(params->set_flip_control_gsl_params.pipe_ctx,
- params->set_flip_control_gsl_params.flip_immediate);
+ params->set_flip_control_gsl_params.hubp->funcs->hubp_set_flip_control_surface_gsl(
+ params->set_flip_control_gsl_params.hubp,
+ params->set_flip_control_gsl_params.flip_immediate);
break;
case HUBP_PROGRAM_TRIPLEBUFFER:
dc->hwss.program_triplebuffer(params->program_triplebuffer_params.dc,
@@ -1001,8 +1018,301 @@ void hwss_execute_sequence(struct dc *dc,
params->wait_for_dcc_meta_propagation_params.dc,
params->wait_for_dcc_meta_propagation_params.top_pipe_to_program);
break;
- case DMUB_FAMS2_GLOBAL_CONTROL_LOCK_FAST:
- dc->hwss.fams2_global_control_lock_fast(params);
+ case DMUB_HW_CONTROL_LOCK_FAST:
+ dc->hwss.dmub_hw_control_lock_fast(params);
+ break;
+ case HUBP_PROGRAM_SURFACE_CONFIG:
+ hwss_program_surface_config(params);
+ break;
+ case HUBP_PROGRAM_MCACHE_ID:
+ hwss_program_mcache_id_and_split_coordinate(params);
+ break;
+ case PROGRAM_CURSOR_UPDATE_NOW:
+ dc->hwss.program_cursor_offload_now(
+ params->program_cursor_update_now_params.dc,
+ params->program_cursor_update_now_params.pipe_ctx);
+ break;
+ case HUBP_WAIT_PIPE_READ_START:
+ params->hubp_wait_pipe_read_start_params.hubp->funcs->hubp_wait_pipe_read_start(
+ params->hubp_wait_pipe_read_start_params.hubp);
+ break;
+ case HWS_APPLY_UPDATE_FLAGS_FOR_PHANTOM:
+ dc->hwss.apply_update_flags_for_phantom(params->apply_update_flags_for_phantom_params.pipe_ctx);
+ break;
+ case HWS_UPDATE_PHANTOM_VP_POSITION:
+ dc->hwss.update_phantom_vp_position(params->update_phantom_vp_position_params.dc,
+ params->update_phantom_vp_position_params.context,
+ params->update_phantom_vp_position_params.pipe_ctx);
+ break;
+ case OPTC_SET_ODM_COMBINE:
+ hwss_set_odm_combine(params);
+ break;
+ case OPTC_SET_ODM_BYPASS:
+ hwss_set_odm_bypass(params);
+ break;
+ case OPP_PIPE_CLOCK_CONTROL:
+ hwss_opp_pipe_clock_control(params);
+ break;
+ case OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL:
+ hwss_opp_program_left_edge_extra_pixel(params);
+ break;
+ case DCCG_SET_DTO_DSCCLK:
+ hwss_dccg_set_dto_dscclk(params);
+ break;
+ case DSC_SET_CONFIG:
+ hwss_dsc_set_config(params);
+ break;
+ case DSC_ENABLE:
+ hwss_dsc_enable(params);
+ break;
+ case TG_SET_DSC_CONFIG:
+ hwss_tg_set_dsc_config(params);
+ break;
+ case DSC_DISCONNECT:
+ hwss_dsc_disconnect(params);
+ break;
+ case DSC_READ_STATE:
+ hwss_dsc_read_state(params);
+ break;
+ case DSC_CALCULATE_AND_SET_CONFIG:
+ hwss_dsc_calculate_and_set_config(params);
+ break;
+ case DSC_ENABLE_WITH_OPP:
+ hwss_dsc_enable_with_opp(params);
+ break;
+ case TG_PROGRAM_GLOBAL_SYNC:
+ hwss_tg_program_global_sync(params);
+ break;
+ case TG_WAIT_FOR_STATE:
+ hwss_tg_wait_for_state(params);
+ break;
+ case TG_SET_VTG_PARAMS:
+ hwss_tg_set_vtg_params(params);
+ break;
+ case TG_SETUP_VERTICAL_INTERRUPT2:
+ hwss_tg_setup_vertical_interrupt2(params);
+ break;
+ case DPP_SET_HDR_MULTIPLIER:
+ hwss_dpp_set_hdr_multiplier(params);
+ break;
+ case HUBP_PROGRAM_DET_SIZE:
+ hwss_program_det_size(params);
+ break;
+ case HUBP_PROGRAM_DET_SEGMENTS:
+ hwss_program_det_segments(params);
+ break;
+ case OPP_SET_DYN_EXPANSION:
+ hwss_opp_set_dyn_expansion(params);
+ break;
+ case OPP_PROGRAM_FMT:
+ hwss_opp_program_fmt(params);
+ break;
+ case OPP_PROGRAM_BIT_DEPTH_REDUCTION:
+ hwss_opp_program_bit_depth_reduction(params);
+ break;
+ case OPP_SET_DISP_PATTERN_GENERATOR:
+ hwss_opp_set_disp_pattern_generator(params);
+ break;
+ case ABM_SET_PIPE:
+ hwss_set_abm_pipe(params);
+ break;
+ case ABM_SET_LEVEL:
+ hwss_set_abm_level(params);
+ break;
+ case ABM_SET_IMMEDIATE_DISABLE:
+ hwss_set_abm_immediate_disable(params);
+ break;
+ case MPC_REMOVE_MPCC:
+ hwss_mpc_remove_mpcc(params);
+ break;
+ case OPP_SET_MPCC_DISCONNECT_PENDING:
+ hwss_opp_set_mpcc_disconnect_pending(params);
+ break;
+ case DC_SET_OPTIMIZED_REQUIRED:
+ hwss_dc_set_optimized_required(params);
+ break;
+ case HUBP_DISCONNECT:
+ hwss_hubp_disconnect(params);
+ break;
+ case HUBBUB_FORCE_PSTATE_CHANGE_CONTROL:
+ hwss_hubbub_force_pstate_change_control(params);
+ break;
+ case TG_ENABLE_CRTC:
+ hwss_tg_enable_crtc(params);
+ break;
+ case TG_SET_GSL:
+ hwss_tg_set_gsl(params);
+ break;
+ case TG_SET_GSL_SOURCE_SELECT:
+ hwss_tg_set_gsl_source_select(params);
+ break;
+ case HUBP_WAIT_FLIP_PENDING:
+ hwss_hubp_wait_flip_pending(params);
+ break;
+ case TG_WAIT_DOUBLE_BUFFER_PENDING:
+ hwss_tg_wait_double_buffer_pending(params);
+ break;
+ case UPDATE_FORCE_PSTATE:
+ hwss_update_force_pstate(params);
+ break;
+ case HUBBUB_APPLY_DEDCN21_147_WA:
+ hwss_hubbub_apply_dedcn21_147_wa(params);
+ break;
+ case HUBBUB_ALLOW_SELF_REFRESH_CONTROL:
+ hwss_hubbub_allow_self_refresh_control(params);
+ break;
+ case TG_GET_FRAME_COUNT:
+ hwss_tg_get_frame_count(params);
+ break;
+ case MPC_SET_DWB_MUX:
+ hwss_mpc_set_dwb_mux(params);
+ break;
+ case MPC_DISABLE_DWB_MUX:
+ hwss_mpc_disable_dwb_mux(params);
+ break;
+ case MCIF_WB_CONFIG_BUF:
+ hwss_mcif_wb_config_buf(params);
+ break;
+ case MCIF_WB_CONFIG_ARB:
+ hwss_mcif_wb_config_arb(params);
+ break;
+ case MCIF_WB_ENABLE:
+ hwss_mcif_wb_enable(params);
+ break;
+ case MCIF_WB_DISABLE:
+ hwss_mcif_wb_disable(params);
+ break;
+ case DWBC_ENABLE:
+ hwss_dwbc_enable(params);
+ break;
+ case DWBC_DISABLE:
+ hwss_dwbc_disable(params);
+ break;
+ case DWBC_UPDATE:
+ hwss_dwbc_update(params);
+ break;
+ case HUBP_UPDATE_MALL_SEL:
+ hwss_hubp_update_mall_sel(params);
+ break;
+ case HUBP_PREPARE_SUBVP_BUFFERING:
+ hwss_hubp_prepare_subvp_buffering(params);
+ break;
+ case HUBP_SET_BLANK_EN:
+ hwss_hubp_set_blank_en(params);
+ break;
+ case HUBP_DISABLE_CONTROL:
+ hwss_hubp_disable_control(params);
+ break;
+ case HUBBUB_SOFT_RESET:
+ hwss_hubbub_soft_reset(params);
+ break;
+ case HUBP_CLK_CNTL:
+ hwss_hubp_clk_cntl(params);
+ break;
+ case HUBP_INIT:
+ hwss_hubp_init(params);
+ break;
+ case HUBP_SET_VM_SYSTEM_APERTURE_SETTINGS:
+ hwss_hubp_set_vm_system_aperture_settings(params);
+ break;
+ case HUBP_SET_FLIP_INT:
+ hwss_hubp_set_flip_int(params);
+ break;
+ case DPP_DPPCLK_CONTROL:
+ hwss_dpp_dppclk_control(params);
+ break;
+ case DISABLE_PHANTOM_CRTC:
+ hwss_disable_phantom_crtc(params);
+ break;
+ case DSC_PG_STATUS:
+ hwss_dsc_pg_status(params);
+ break;
+ case DSC_WAIT_DISCONNECT_PENDING_CLEAR:
+ hwss_dsc_wait_disconnect_pending_clear(params);
+ break;
+ case DSC_DISABLE:
+ hwss_dsc_disable(params);
+ break;
+ case DCCG_SET_REF_DSCCLK:
+ hwss_dccg_set_ref_dscclk(params);
+ break;
+ case DPP_PG_CONTROL:
+ hwss_dpp_pg_control(params);
+ break;
+ case HUBP_PG_CONTROL:
+ hwss_hubp_pg_control(params);
+ break;
+ case HUBP_RESET:
+ hwss_hubp_reset(params);
+ break;
+ case DPP_RESET:
+ hwss_dpp_reset(params);
+ break;
+ case DPP_ROOT_CLOCK_CONTROL:
+ hwss_dpp_root_clock_control(params);
+ break;
+ case DC_IP_REQUEST_CNTL:
+ hwss_dc_ip_request_cntl(params);
+ break;
+ case DCCG_UPDATE_DPP_DTO:
+ hwss_dccg_update_dpp_dto(params);
+ break;
+ case HUBP_VTG_SEL:
+ hwss_hubp_vtg_sel(params);
+ break;
+ case HUBP_SETUP2:
+ hwss_hubp_setup2(params);
+ break;
+ case HUBP_SETUP:
+ hwss_hubp_setup(params);
+ break;
+ case HUBP_SET_UNBOUNDED_REQUESTING:
+ hwss_hubp_set_unbounded_requesting(params);
+ break;
+ case HUBP_SETUP_INTERDEPENDENT2:
+ hwss_hubp_setup_interdependent2(params);
+ break;
+ case HUBP_SETUP_INTERDEPENDENT:
+ hwss_hubp_setup_interdependent(params);
+ break;
+ case DPP_SET_CURSOR_MATRIX:
+ hwss_dpp_set_cursor_matrix(params);
+ break;
+ case MPC_UPDATE_BLENDING:
+ hwss_mpc_update_blending(params);
+ break;
+ case MPC_ASSERT_IDLE_MPCC:
+ hwss_mpc_assert_idle_mpcc(params);
+ break;
+ case MPC_INSERT_PLANE:
+ hwss_mpc_insert_plane(params);
+ break;
+ case DPP_SET_SCALER:
+ hwss_dpp_set_scaler(params);
+ break;
+ case HUBP_MEM_PROGRAM_VIEWPORT:
+ hwss_hubp_mem_program_viewport(params);
+ break;
+ case ABORT_CURSOR_OFFLOAD_UPDATE:
+ hwss_abort_cursor_offload_update(params);
+ break;
+ case SET_CURSOR_ATTRIBUTE:
+ hwss_set_cursor_attribute(params);
+ break;
+ case SET_CURSOR_POSITION:
+ hwss_set_cursor_position(params);
+ break;
+ case SET_CURSOR_SDR_WHITE_LEVEL:
+ hwss_set_cursor_sdr_white_level(params);
+ break;
+ case PROGRAM_OUTPUT_CSC:
+ hwss_program_output_csc(params);
+ break;
+ case HUBP_SET_BLANK:
+ hwss_hubp_set_blank(params);
+ break;
+ case PHANTOM_HUBP_POST_ENABLE:
+ hwss_phantom_hubp_post_enable(params);
break;
default:
ASSERT(false);
@@ -1011,6 +1321,338 @@ void hwss_execute_sequence(struct dc *dc,
}
}
+/*
+ * Helper function to add OPTC pipe control lock to block sequence
+ */
+void hwss_add_optc_pipe_control_lock(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ bool lock)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.pipe_control_lock_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.pipe_control_lock_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].params.pipe_control_lock_params.lock = lock;
+ seq_state->steps[*seq_state->num_steps].func = OPTC_PIPE_CONTROL_LOCK;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP set flip control GSL to block sequence
+ */
+void hwss_add_hubp_set_flip_control_gsl(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool flip_immediate)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_flip_control_gsl_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.set_flip_control_gsl_params.flip_immediate = flip_immediate;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SET_FLIP_CONTROL_GSL;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP program triplebuffer to block sequence
+ */
+void hwss_add_hubp_program_triplebuffer(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ bool enableTripleBuffer)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.program_triplebuffer_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.program_triplebuffer_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].params.program_triplebuffer_params.enableTripleBuffer = enableTripleBuffer;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_PROGRAM_TRIPLEBUFFER;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP update plane address to block sequence
+ */
+void hwss_add_hubp_update_plane_addr(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.update_plane_addr_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.update_plane_addr_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_UPDATE_PLANE_ADDR;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add DPP set input transfer function to block sequence
+ */
+void hwss_add_dpp_set_input_transfer_func(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_plane_state *plane_state)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_input_transfer_func_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.set_input_transfer_func_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].params.set_input_transfer_func_params.plane_state = plane_state;
+ seq_state->steps[*seq_state->num_steps].func = DPP_SET_INPUT_TRANSFER_FUNC;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add DPP program gamut remap to block sequence
+ */
+void hwss_add_dpp_program_gamut_remap(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.program_gamut_remap_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].func = DPP_PROGRAM_GAMUT_REMAP;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add DPP program bias and scale to block sequence
+ */
+void hwss_add_dpp_program_bias_and_scale(struct block_sequence_state *seq_state, struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.program_bias_and_scale_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].func = DPP_PROGRAM_BIAS_AND_SCALE;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add OPTC program manual trigger to block sequence
+ */
+void hwss_add_optc_program_manual_trigger(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.program_manual_trigger_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].func = OPTC_PROGRAM_MANUAL_TRIGGER;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add DPP set output transfer function to block sequence
+ */
+void hwss_add_dpp_set_output_transfer_func(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_stream_state *stream)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_output_transfer_func_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.set_output_transfer_func_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].params.set_output_transfer_func_params.stream = stream;
+ seq_state->steps[*seq_state->num_steps].func = DPP_SET_OUTPUT_TRANSFER_FUNC;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add MPC update visual confirm to block sequence
+ */
+void hwss_add_mpc_update_visual_confirm(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ int mpcc_id)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.update_visual_confirm_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.update_visual_confirm_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].params.update_visual_confirm_params.mpcc_id = mpcc_id;
+ seq_state->steps[*seq_state->num_steps].func = MPC_UPDATE_VISUAL_CONFIRM;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add MPC power on MPC mem PWR to block sequence
+ */
+void hwss_add_mpc_power_on_mpc_mem_pwr(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ int mpcc_id,
+ bool power_on)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.power_on_mpc_mem_pwr_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.power_on_mpc_mem_pwr_params.mpcc_id = mpcc_id;
+ seq_state->steps[*seq_state->num_steps].params.power_on_mpc_mem_pwr_params.power_on = power_on;
+ seq_state->steps[*seq_state->num_steps].func = MPC_POWER_ON_MPC_MEM_PWR;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add MPC set output CSC to block sequence
+ */
+void hwss_add_mpc_set_output_csc(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ int opp_id,
+ const uint16_t *regval,
+ enum mpc_output_csc_mode ocsc_mode)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_output_csc_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.set_output_csc_params.opp_id = opp_id;
+ seq_state->steps[*seq_state->num_steps].params.set_output_csc_params.regval = regval;
+ seq_state->steps[*seq_state->num_steps].params.set_output_csc_params.ocsc_mode = ocsc_mode;
+ seq_state->steps[*seq_state->num_steps].func = MPC_SET_OUTPUT_CSC;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add MPC set OCSC default to block sequence
+ */
+void hwss_add_mpc_set_ocsc_default(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ int opp_id,
+ enum dc_color_space colorspace,
+ enum mpc_output_csc_mode ocsc_mode)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_ocsc_default_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.set_ocsc_default_params.opp_id = opp_id;
+ seq_state->steps[*seq_state->num_steps].params.set_ocsc_default_params.color_space = colorspace;
+ seq_state->steps[*seq_state->num_steps].params.set_ocsc_default_params.ocsc_mode = ocsc_mode;
+ seq_state->steps[*seq_state->num_steps].func = MPC_SET_OCSC_DEFAULT;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add DMUB send DMCUB command to block sequence
+ */
+void hwss_add_dmub_send_dmcub_cmd(struct block_sequence_state *seq_state,
+ struct dc_context *ctx,
+ union dmub_rb_cmd *cmd,
+ enum dm_dmub_wait_type wait_type)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.send_dmcub_cmd_params.ctx = ctx;
+ seq_state->steps[*seq_state->num_steps].params.send_dmcub_cmd_params.cmd = cmd;
+ seq_state->steps[*seq_state->num_steps].params.send_dmcub_cmd_params.wait_type = wait_type;
+ seq_state->steps[*seq_state->num_steps].func = DMUB_SEND_DMCUB_CMD;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add DMUB SubVP save surface address to block sequence
+ */
+void hwss_add_dmub_subvp_save_surf_addr(struct block_sequence_state *seq_state,
+ struct dc_dmub_srv *dc_dmub_srv,
+ struct dc_plane_address *addr,
+ uint8_t subvp_index)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.subvp_save_surf_addr.dc_dmub_srv = dc_dmub_srv;
+ seq_state->steps[*seq_state->num_steps].params.subvp_save_surf_addr.addr = addr;
+ seq_state->steps[*seq_state->num_steps].params.subvp_save_surf_addr.subvp_index = subvp_index;
+ seq_state->steps[*seq_state->num_steps].func = DMUB_SUBVP_SAVE_SURF_ADDR;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP wait for DCC meta propagation to block sequence
+ */
+void hwss_add_hubp_wait_for_dcc_meta_prop(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *top_pipe_to_program)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.wait_for_dcc_meta_propagation_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.wait_for_dcc_meta_propagation_params.top_pipe_to_program = top_pipe_to_program;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_WAIT_FOR_DCC_META_PROP;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP wait pipe read start to block sequence
+ */
+void hwss_add_hubp_wait_pipe_read_start(struct block_sequence_state *seq_state,
+ struct hubp *hubp)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.hubp_wait_pipe_read_start_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_WAIT_PIPE_READ_START;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HWS apply update flags for phantom to block sequence
+ */
+void hwss_add_hws_apply_update_flags_for_phantom(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.apply_update_flags_for_phantom_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].func = HWS_APPLY_UPDATE_FLAGS_FOR_PHANTOM;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HWS update phantom VP position to block sequence
+ */
+void hwss_add_hws_update_phantom_vp_position(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct dc_state *context,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.update_phantom_vp_position_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.update_phantom_vp_position_params.context = context;
+ seq_state->steps[*seq_state->num_steps].params.update_phantom_vp_position_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].func = HWS_UPDATE_PHANTOM_VP_POSITION;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add OPTC set ODM combine to block sequence
+ */
+void hwss_add_optc_set_odm_combine(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, int opp_inst[MAX_PIPES], int opp_head_count,
+ int odm_slice_width, int last_odm_slice_width)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_odm_combine_params.tg = tg;
+ memcpy(seq_state->steps[*seq_state->num_steps].params.set_odm_combine_params.opp_inst, opp_inst, sizeof(int) * MAX_PIPES);
+ seq_state->steps[*seq_state->num_steps].params.set_odm_combine_params.opp_head_count = opp_head_count;
+ seq_state->steps[*seq_state->num_steps].params.set_odm_combine_params.odm_slice_width = odm_slice_width;
+ seq_state->steps[*seq_state->num_steps].params.set_odm_combine_params.last_odm_slice_width = last_odm_slice_width;
+ seq_state->steps[*seq_state->num_steps].func = OPTC_SET_ODM_COMBINE;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add OPTC set ODM bypass to block sequence
+ */
+void hwss_add_optc_set_odm_bypass(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, struct dc_crtc_timing *timing)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_odm_bypass_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.set_odm_bypass_params.timing = timing;
+ seq_state->steps[*seq_state->num_steps].func = OPTC_SET_ODM_BYPASS;
+ (*seq_state->num_steps)++;
+ }
+}
+
void hwss_send_dmcub_cmd(union block_sequence_params *params)
{
struct dc_context *ctx = params->send_dmcub_cmd_params.ctx;
@@ -1020,6 +1662,276 @@ void hwss_send_dmcub_cmd(union block_sequence_params *params)
dc_wake_and_execute_dmub_cmd(ctx, cmd, wait_type);
}
+/*
+ * Helper function to add TG program global sync to block sequence
+ */
+void hwss_add_tg_program_global_sync(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ int vready_offset,
+ unsigned int vstartup_lines,
+ unsigned int vupdate_offset_pixels,
+ unsigned int vupdate_vupdate_width_pixels,
+ unsigned int pstate_keepout_start_lines)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.tg_program_global_sync_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_program_global_sync_params.vready_offset = vready_offset;
+ seq_state->steps[*seq_state->num_steps].params.tg_program_global_sync_params.vstartup_lines = vstartup_lines;
+ seq_state->steps[*seq_state->num_steps].params.tg_program_global_sync_params.vupdate_offset_pixels = vupdate_offset_pixels;
+ seq_state->steps[*seq_state->num_steps].params.tg_program_global_sync_params.vupdate_vupdate_width_pixels = vupdate_vupdate_width_pixels;
+ seq_state->steps[*seq_state->num_steps].params.tg_program_global_sync_params.pstate_keepout_start_lines = pstate_keepout_start_lines;
+ seq_state->steps[*seq_state->num_steps].func = TG_PROGRAM_GLOBAL_SYNC;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add TG wait for state to block sequence
+ */
+void hwss_add_tg_wait_for_state(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ enum crtc_state state)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.tg_wait_for_state_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_wait_for_state_params.state = state;
+ seq_state->steps[*seq_state->num_steps].func = TG_WAIT_FOR_STATE;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add TG set VTG params to block sequence
+ */
+void hwss_add_tg_set_vtg_params(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ struct dc_crtc_timing *dc_crtc_timing,
+ bool program_fp2)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.tg_set_vtg_params_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_vtg_params_params.timing = dc_crtc_timing;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_vtg_params_params.program_fp2 = program_fp2;
+ seq_state->steps[*seq_state->num_steps].func = TG_SET_VTG_PARAMS;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add TG setup vertical interrupt2 to block sequence
+ */
+void hwss_add_tg_setup_vertical_interrupt2(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, int start_line)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.tg_setup_vertical_interrupt2_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_setup_vertical_interrupt2_params.start_line = start_line;
+ seq_state->steps[*seq_state->num_steps].func = TG_SETUP_VERTICAL_INTERRUPT2;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add DPP set HDR multiplier to block sequence
+ */
+void hwss_add_dpp_set_hdr_multiplier(struct block_sequence_state *seq_state,
+ struct dpp *dpp, uint32_t hw_mult)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.dpp_set_hdr_multiplier_params.dpp = dpp;
+ seq_state->steps[*seq_state->num_steps].params.dpp_set_hdr_multiplier_params.hw_mult = hw_mult;
+ seq_state->steps[*seq_state->num_steps].func = DPP_SET_HDR_MULTIPLIER;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP program DET size to block sequence
+ */
+void hwss_add_hubp_program_det_size(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ unsigned int hubp_inst,
+ unsigned int det_buffer_size_kb)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.program_det_size_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.program_det_size_params.hubp_inst = hubp_inst;
+ seq_state->steps[*seq_state->num_steps].params.program_det_size_params.det_buffer_size_kb = det_buffer_size_kb;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_PROGRAM_DET_SIZE;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_program_mcache_id(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct dml2_hubp_pipe_mcache_regs *mcache_regs)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.program_mcache_id_and_split_coordinate.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.program_mcache_id_and_split_coordinate.mcache_regs = mcache_regs;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_PROGRAM_MCACHE_ID;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubbub_force_pstate_change_control(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ bool enable,
+ bool wait)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.hubbub_force_pstate_change_control_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_force_pstate_change_control_params.enable = enable;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_force_pstate_change_control_params.wait = wait;
+ seq_state->steps[*seq_state->num_steps].func = HUBBUB_FORCE_PSTATE_CHANGE_CONTROL;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP program DET segments to block sequence
+ */
+void hwss_add_hubp_program_det_segments(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ unsigned int hubp_inst,
+ unsigned int det_size)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.program_det_segments_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.program_det_segments_params.hubp_inst = hubp_inst;
+ seq_state->steps[*seq_state->num_steps].params.program_det_segments_params.det_size = det_size;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_PROGRAM_DET_SEGMENTS;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add OPP set dynamic expansion to block sequence
+ */
+void hwss_add_opp_set_dyn_expansion(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ enum dc_color_space color_space,
+ enum dc_color_depth color_depth,
+ enum signal_type signal)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.opp_set_dyn_expansion_params.opp = opp;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_dyn_expansion_params.color_space = color_space;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_dyn_expansion_params.color_depth = color_depth;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_dyn_expansion_params.signal = signal;
+ seq_state->steps[*seq_state->num_steps].func = OPP_SET_DYN_EXPANSION;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add OPP program FMT to block sequence
+ */
+void hwss_add_opp_program_fmt(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ struct bit_depth_reduction_params *fmt_bit_depth,
+ struct clamping_and_pixel_encoding_params *clamping)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.opp_program_fmt_params.opp = opp;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_fmt_params.fmt_bit_depth = fmt_bit_depth;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_fmt_params.clamping = clamping;
+ seq_state->steps[*seq_state->num_steps].func = OPP_PROGRAM_FMT;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_opp_program_left_edge_extra_pixel(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ enum dc_pixel_encoding pixel_encoding,
+ bool is_otg_master)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_left_edge_extra_pixel_params.opp = opp;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_left_edge_extra_pixel_params.pixel_encoding = pixel_encoding;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_left_edge_extra_pixel_params.is_otg_master = is_otg_master;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add ABM set pipe to block sequence
+ */
+void hwss_add_abm_set_pipe(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_abm_pipe_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.set_abm_pipe_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].func = ABM_SET_PIPE;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add ABM set level to block sequence
+ */
+void hwss_add_abm_set_level(struct block_sequence_state *seq_state,
+ struct abm *abm,
+ uint32_t abm_level)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.set_abm_level_params.abm = abm;
+ seq_state->steps[*seq_state->num_steps].params.set_abm_level_params.abm_level = abm_level;
+ seq_state->steps[*seq_state->num_steps].func = ABM_SET_LEVEL;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add TG enable CRTC to block sequence
+ */
+void hwss_add_tg_enable_crtc(struct block_sequence_state *seq_state,
+ struct timing_generator *tg)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.tg_enable_crtc_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].func = TG_ENABLE_CRTC;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP wait flip pending to block sequence
+ */
+void hwss_add_hubp_wait_flip_pending(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ unsigned int timeout_us,
+ unsigned int polling_interval_us)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.hubp_wait_flip_pending_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_wait_flip_pending_params.timeout_us = timeout_us;
+ seq_state->steps[*seq_state->num_steps].params.hubp_wait_flip_pending_params.polling_interval_us = polling_interval_us;
+ seq_state->steps[*seq_state->num_steps].func = HUBP_WAIT_FLIP_PENDING;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add TG wait double buffer pending to block sequence
+ */
+void hwss_add_tg_wait_double_buffer_pending(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ unsigned int timeout_us,
+ unsigned int polling_interval_us)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].params.tg_wait_double_buffer_pending_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_wait_double_buffer_pending_params.timeout_us = timeout_us;
+ seq_state->steps[*seq_state->num_steps].params.tg_wait_double_buffer_pending_params.polling_interval_us = polling_interval_us;
+ seq_state->steps[*seq_state->num_steps].func = TG_WAIT_DOUBLE_BUFFER_PENDING;
+ (*seq_state->num_steps)++;
+ }
+}
+
void hwss_program_manual_trigger(union block_sequence_params *params)
{
struct pipe_ctx *pipe_ctx = params->program_manual_trigger_params.pipe_ctx;
@@ -1046,12 +1958,6 @@ void hwss_setup_dpp(union block_sequence_params *params)
plane_state->color_space,
NULL);
}
-
- if (dpp && dpp->funcs->set_cursor_matrix) {
- dpp->funcs->set_cursor_matrix(dpp,
- plane_state->color_space,
- plane_state->cursor_csc_color_matrix);
- }
}
void hwss_program_bias_and_scale(union block_sequence_params *params)
@@ -1062,9 +1968,8 @@ void hwss_program_bias_and_scale(union block_sequence_params *params)
struct dc_bias_and_scale bns_params = plane_state->bias_and_scale;
//TODO :for CNVC set scale and bias registers if necessary
- if (dpp->funcs->dpp_program_bias_and_scale) {
+ if (dpp->funcs->dpp_program_bias_and_scale)
dpp->funcs->dpp_program_bias_and_scale(dpp, &bns_params);
- }
}
void hwss_power_on_mpc_mem_pwr(union block_sequence_params *params)
@@ -1114,6 +2019,39 @@ void hwss_subvp_save_surf_addr(union block_sequence_params *params)
dc_dmub_srv_subvp_save_surf_addr(dc_dmub_srv, addr, subvp_index);
}
+void hwss_program_surface_config(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->program_surface_config_params.hubp;
+ enum surface_pixel_format format = params->program_surface_config_params.format;
+ struct dc_tiling_info *tiling_info = params->program_surface_config_params.tiling_info;
+ struct plane_size size = params->program_surface_config_params.plane_size;
+ enum dc_rotation_angle rotation = params->program_surface_config_params.rotation;
+ struct dc_plane_dcc_param *dcc = params->program_surface_config_params.dcc;
+ bool horizontal_mirror = params->program_surface_config_params.horizontal_mirror;
+ int compat_level = params->program_surface_config_params.compat_level;
+
+ hubp->funcs->hubp_program_surface_config(
+ hubp,
+ format,
+ tiling_info,
+ &size,
+ rotation,
+ dcc,
+ horizontal_mirror,
+ compat_level);
+
+ hubp->power_gated = false;
+}
+
+void hwss_program_mcache_id_and_split_coordinate(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->program_mcache_id_and_split_coordinate.hubp;
+ struct dml2_hubp_pipe_mcache_regs *mcache_regs = params->program_mcache_id_and_split_coordinate.mcache_regs;
+
+ hubp->funcs->hubp_program_mcache_id_and_split_coordinate(hubp, mcache_regs);
+
+}
+
void get_surface_tile_visual_confirm_color(
struct pipe_ctx *pipe_ctx,
struct tg_color *color)
@@ -1188,6 +2126,7 @@ void hwss_wait_for_odm_update_pending_complete(struct dc *dc, struct dc_state *c
void hwss_wait_for_no_pipes_pending(struct dc *dc, struct dc_state *context)
{
int i;
+
for (i = 0; i < MAX_PIPES; i++) {
int count = 0;
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
@@ -1264,3 +2203,1869 @@ void hwss_process_outstanding_hw_updates(struct dc *dc, struct dc_state *dc_cont
if (dc->hwss.program_outstanding_updates)
dc->hwss.program_outstanding_updates(dc, dc_context);
}
+
+void hwss_set_odm_combine(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->set_odm_combine_params.tg;
+ int *opp_inst = params->set_odm_combine_params.opp_inst;
+ int opp_head_count = params->set_odm_combine_params.opp_head_count;
+ int odm_slice_width = params->set_odm_combine_params.odm_slice_width;
+ int last_odm_slice_width = params->set_odm_combine_params.last_odm_slice_width;
+
+ if (tg && tg->funcs->set_odm_combine)
+ tg->funcs->set_odm_combine(tg, opp_inst, opp_head_count,
+ odm_slice_width, last_odm_slice_width);
+}
+
+void hwss_set_odm_bypass(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->set_odm_bypass_params.tg;
+ const struct dc_crtc_timing *timing = params->set_odm_bypass_params.timing;
+
+ if (tg && tg->funcs->set_odm_bypass)
+ tg->funcs->set_odm_bypass(tg, timing);
+}
+
+void hwss_opp_pipe_clock_control(union block_sequence_params *params)
+{
+ struct output_pixel_processor *opp = params->opp_pipe_clock_control_params.opp;
+ bool enable = params->opp_pipe_clock_control_params.enable;
+
+ if (opp && opp->funcs->opp_pipe_clock_control)
+ opp->funcs->opp_pipe_clock_control(opp, enable);
+}
+
+void hwss_opp_program_left_edge_extra_pixel(union block_sequence_params *params)
+{
+ struct output_pixel_processor *opp = params->opp_program_left_edge_extra_pixel_params.opp;
+ enum dc_pixel_encoding pixel_encoding = params->opp_program_left_edge_extra_pixel_params.pixel_encoding;
+ bool is_otg_master = params->opp_program_left_edge_extra_pixel_params.is_otg_master;
+
+ if (opp && opp->funcs->opp_program_left_edge_extra_pixel)
+ opp->funcs->opp_program_left_edge_extra_pixel(opp, pixel_encoding, is_otg_master);
+}
+
+void hwss_dccg_set_dto_dscclk(union block_sequence_params *params)
+{
+ struct dccg *dccg = params->dccg_set_dto_dscclk_params.dccg;
+ int inst = params->dccg_set_dto_dscclk_params.inst;
+ int num_slices_h = params->dccg_set_dto_dscclk_params.num_slices_h;
+
+ if (dccg && dccg->funcs->set_dto_dscclk)
+ dccg->funcs->set_dto_dscclk(dccg, inst, num_slices_h);
+}
+
+void hwss_dsc_set_config(union block_sequence_params *params)
+{
+ struct display_stream_compressor *dsc = params->dsc_set_config_params.dsc;
+ struct dsc_config *dsc_cfg = params->dsc_set_config_params.dsc_cfg;
+ struct dsc_optc_config *dsc_optc_cfg = params->dsc_set_config_params.dsc_optc_cfg;
+
+ if (dsc && dsc->funcs->dsc_set_config)
+ dsc->funcs->dsc_set_config(dsc, dsc_cfg, dsc_optc_cfg);
+}
+
+void hwss_dsc_enable(union block_sequence_params *params)
+{
+ struct display_stream_compressor *dsc = params->dsc_enable_params.dsc;
+ int opp_inst = params->dsc_enable_params.opp_inst;
+
+ if (dsc && dsc->funcs->dsc_enable)
+ dsc->funcs->dsc_enable(dsc, opp_inst);
+}
+
+void hwss_tg_set_dsc_config(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_set_dsc_config_params.tg;
+ enum optc_dsc_mode optc_dsc_mode = OPTC_DSC_DISABLED;
+ uint32_t bytes_per_pixel = 0;
+ uint32_t slice_width = 0;
+
+ if (params->tg_set_dsc_config_params.enable) {
+ struct dsc_optc_config *dsc_optc_cfg = params->tg_set_dsc_config_params.dsc_optc_cfg;
+
+ if (dsc_optc_cfg) {
+ bytes_per_pixel = dsc_optc_cfg->bytes_per_pixel;
+ slice_width = dsc_optc_cfg->slice_width;
+ optc_dsc_mode = dsc_optc_cfg->is_pixel_format_444 ?
+ OPTC_DSC_ENABLED_444 : OPTC_DSC_ENABLED_NATIVE_SUBSAMPLED;
+ }
+ }
+
+ if (tg && tg->funcs->set_dsc_config)
+ tg->funcs->set_dsc_config(tg, optc_dsc_mode, bytes_per_pixel, slice_width);
+}
+
+void hwss_dsc_disconnect(union block_sequence_params *params)
+{
+ struct display_stream_compressor *dsc = params->dsc_disconnect_params.dsc;
+
+ if (dsc && dsc->funcs->dsc_disconnect)
+ dsc->funcs->dsc_disconnect(dsc);
+}
+
+void hwss_dsc_read_state(union block_sequence_params *params)
+{
+ struct display_stream_compressor *dsc = params->dsc_read_state_params.dsc;
+ struct dcn_dsc_state *dsc_state = params->dsc_read_state_params.dsc_state;
+
+ if (dsc && dsc->funcs->dsc_read_state)
+ dsc->funcs->dsc_read_state(dsc, dsc_state);
+}
+
+void hwss_dsc_calculate_and_set_config(union block_sequence_params *params)
+{
+ struct pipe_ctx *pipe_ctx = params->dsc_calculate_and_set_config_params.pipe_ctx;
+ struct pipe_ctx *top_pipe = pipe_ctx;
+ bool enable = params->dsc_calculate_and_set_config_params.enable;
+ int opp_cnt = params->dsc_calculate_and_set_config_params.opp_cnt;
+
+ struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc;
+ struct dc_stream_state *stream = pipe_ctx->stream;
+
+ if (!dsc || !enable)
+ return;
+
+ /* Calculate DSC configuration - extracted from dcn32_update_dsc_on_stream */
+ struct dsc_config dsc_cfg;
+
+ while (top_pipe->prev_odm_pipe)
+ top_pipe = top_pipe->prev_odm_pipe;
+
+ dsc_cfg.pic_width = (stream->timing.h_addressable + top_pipe->dsc_padding_params.dsc_hactive_padding +
+ stream->timing.h_border_left + stream->timing.h_border_right) / opp_cnt;
+ dsc_cfg.pic_height = stream->timing.v_addressable + stream->timing.v_border_top + stream->timing.v_border_bottom;
+ dsc_cfg.pixel_encoding = stream->timing.pixel_encoding;
+ dsc_cfg.color_depth = stream->timing.display_color_depth;
+ dsc_cfg.is_odm = top_pipe->next_odm_pipe ? true : false;
+ dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
+ dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
+ dsc_cfg.dsc_padding = top_pipe->dsc_padding_params.dsc_hactive_padding;
+
+ /* Set DSC configuration */
+ if (dsc->funcs->dsc_set_config)
+ dsc->funcs->dsc_set_config(dsc, &dsc_cfg,
+ &params->dsc_calculate_and_set_config_params.dsc_optc_cfg);
+}
+
+void hwss_dsc_enable_with_opp(union block_sequence_params *params)
+{
+ struct pipe_ctx *pipe_ctx = params->dsc_enable_with_opp_params.pipe_ctx;
+ struct display_stream_compressor *dsc = pipe_ctx->stream_res.dsc;
+
+ if (dsc && dsc->funcs->dsc_enable)
+ dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst);
+}
+
+void hwss_tg_program_global_sync(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_program_global_sync_params.tg;
+ int vready_offset = params->tg_program_global_sync_params.vready_offset;
+ unsigned int vstartup_lines = params->tg_program_global_sync_params.vstartup_lines;
+ unsigned int vupdate_offset_pixels = params->tg_program_global_sync_params.vupdate_offset_pixels;
+ unsigned int vupdate_vupdate_width_pixels = params->tg_program_global_sync_params.vupdate_vupdate_width_pixels;
+ unsigned int pstate_keepout_start_lines = params->tg_program_global_sync_params.pstate_keepout_start_lines;
+
+ if (tg->funcs->program_global_sync) {
+ tg->funcs->program_global_sync(tg, vready_offset, vstartup_lines,
+ vupdate_offset_pixels, vupdate_vupdate_width_pixels, pstate_keepout_start_lines);
+ }
+}
+
+void hwss_tg_wait_for_state(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_wait_for_state_params.tg;
+ enum crtc_state state = params->tg_wait_for_state_params.state;
+
+ if (tg->funcs->wait_for_state)
+ tg->funcs->wait_for_state(tg, state);
+}
+
+void hwss_tg_set_vtg_params(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_set_vtg_params_params.tg;
+ struct dc_crtc_timing *timing = params->tg_set_vtg_params_params.timing;
+ bool program_fp2 = params->tg_set_vtg_params_params.program_fp2;
+
+ if (tg->funcs->set_vtg_params)
+ tg->funcs->set_vtg_params(tg, timing, program_fp2);
+}
+
+void hwss_tg_setup_vertical_interrupt2(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_setup_vertical_interrupt2_params.tg;
+ int start_line = params->tg_setup_vertical_interrupt2_params.start_line;
+
+ if (tg->funcs->setup_vertical_interrupt2)
+ tg->funcs->setup_vertical_interrupt2(tg, start_line);
+}
+
+void hwss_dpp_set_hdr_multiplier(union block_sequence_params *params)
+{
+ struct dpp *dpp = params->dpp_set_hdr_multiplier_params.dpp;
+ uint32_t hw_mult = params->dpp_set_hdr_multiplier_params.hw_mult;
+
+ if (dpp->funcs->dpp_set_hdr_multiplier)
+ dpp->funcs->dpp_set_hdr_multiplier(dpp, hw_mult);
+}
+
+void hwss_program_det_size(union block_sequence_params *params)
+{
+ struct hubbub *hubbub = params->program_det_size_params.hubbub;
+ unsigned int hubp_inst = params->program_det_size_params.hubp_inst;
+ unsigned int det_buffer_size_kb = params->program_det_size_params.det_buffer_size_kb;
+
+ if (hubbub->funcs->program_det_size)
+ hubbub->funcs->program_det_size(hubbub, hubp_inst, det_buffer_size_kb);
+}
+
+void hwss_program_det_segments(union block_sequence_params *params)
+{
+ struct hubbub *hubbub = params->program_det_segments_params.hubbub;
+ unsigned int hubp_inst = params->program_det_segments_params.hubp_inst;
+ unsigned int det_size = params->program_det_segments_params.det_size;
+
+ if (hubbub->funcs->program_det_segments)
+ hubbub->funcs->program_det_segments(hubbub, hubp_inst, det_size);
+}
+
+void hwss_opp_set_dyn_expansion(union block_sequence_params *params)
+{
+ struct output_pixel_processor *opp = params->opp_set_dyn_expansion_params.opp;
+ enum dc_color_space color_space = params->opp_set_dyn_expansion_params.color_space;
+ enum dc_color_depth color_depth = params->opp_set_dyn_expansion_params.color_depth;
+ enum signal_type signal = params->opp_set_dyn_expansion_params.signal;
+
+ if (opp->funcs->opp_set_dyn_expansion)
+ opp->funcs->opp_set_dyn_expansion(opp, color_space, color_depth, signal);
+}
+
+void hwss_opp_program_fmt(union block_sequence_params *params)
+{
+ struct output_pixel_processor *opp = params->opp_program_fmt_params.opp;
+ struct bit_depth_reduction_params *fmt_bit_depth = params->opp_program_fmt_params.fmt_bit_depth;
+ struct clamping_and_pixel_encoding_params *clamping = params->opp_program_fmt_params.clamping;
+
+ if (opp->funcs->opp_program_fmt)
+ opp->funcs->opp_program_fmt(opp, fmt_bit_depth, clamping);
+}
+
+void hwss_opp_program_bit_depth_reduction(union block_sequence_params *params)
+{
+ struct output_pixel_processor *opp = params->opp_program_bit_depth_reduction_params.opp;
+ bool use_default_params = params->opp_program_bit_depth_reduction_params.use_default_params;
+ struct pipe_ctx *pipe_ctx = params->opp_program_bit_depth_reduction_params.pipe_ctx;
+ struct bit_depth_reduction_params bit_depth_params;
+
+ if (use_default_params)
+ memset(&bit_depth_params, 0, sizeof(bit_depth_params));
+ else
+ resource_build_bit_depth_reduction_params(pipe_ctx->stream, &bit_depth_params);
+
+ if (opp->funcs->opp_program_bit_depth_reduction)
+ opp->funcs->opp_program_bit_depth_reduction(opp, &bit_depth_params);
+}
+
+void hwss_opp_set_disp_pattern_generator(union block_sequence_params *params)
+{
+ struct output_pixel_processor *opp = params->opp_set_disp_pattern_generator_params.opp;
+ enum controller_dp_test_pattern test_pattern = params->opp_set_disp_pattern_generator_params.test_pattern;
+ enum controller_dp_color_space color_space = params->opp_set_disp_pattern_generator_params.color_space;
+ enum dc_color_depth color_depth = params->opp_set_disp_pattern_generator_params.color_depth;
+ struct tg_color *solid_color = params->opp_set_disp_pattern_generator_params.use_solid_color ?
+ &params->opp_set_disp_pattern_generator_params.solid_color : NULL;
+ int width = params->opp_set_disp_pattern_generator_params.width;
+ int height = params->opp_set_disp_pattern_generator_params.height;
+ int offset = params->opp_set_disp_pattern_generator_params.offset;
+
+ if (opp && opp->funcs->opp_set_disp_pattern_generator) {
+ opp->funcs->opp_set_disp_pattern_generator(opp, test_pattern, color_space,
+ color_depth, solid_color, width, height, offset);
+ }
+}
+
+void hwss_set_abm_pipe(union block_sequence_params *params)
+{
+ struct dc *dc = params->set_abm_pipe_params.dc;
+ struct pipe_ctx *pipe_ctx = params->set_abm_pipe_params.pipe_ctx;
+
+ dc->hwss.set_pipe(pipe_ctx);
+}
+
+void hwss_set_abm_level(union block_sequence_params *params)
+{
+ struct abm *abm = params->set_abm_level_params.abm;
+ unsigned int abm_level = params->set_abm_level_params.abm_level;
+
+ if (abm->funcs->set_abm_level)
+ abm->funcs->set_abm_level(abm, abm_level);
+}
+
+void hwss_set_abm_immediate_disable(union block_sequence_params *params)
+{
+ struct dc *dc = params->set_abm_immediate_disable_params.dc;
+ struct pipe_ctx *pipe_ctx = params->set_abm_immediate_disable_params.pipe_ctx;
+
+ if (dc && dc->hwss.set_abm_immediate_disable)
+ dc->hwss.set_abm_immediate_disable(pipe_ctx);
+}
+
+void hwss_mpc_remove_mpcc(union block_sequence_params *params)
+{
+ struct mpc *mpc = params->mpc_remove_mpcc_params.mpc;
+ struct mpc_tree *mpc_tree_params = params->mpc_remove_mpcc_params.mpc_tree_params;
+ struct mpcc *mpcc_to_remove = params->mpc_remove_mpcc_params.mpcc_to_remove;
+
+ mpc->funcs->remove_mpcc(mpc, mpc_tree_params, mpcc_to_remove);
+}
+
+void hwss_opp_set_mpcc_disconnect_pending(union block_sequence_params *params)
+{
+ struct output_pixel_processor *opp = params->opp_set_mpcc_disconnect_pending_params.opp;
+ int mpcc_inst = params->opp_set_mpcc_disconnect_pending_params.mpcc_inst;
+ bool pending = params->opp_set_mpcc_disconnect_pending_params.pending;
+
+ opp->mpcc_disconnect_pending[mpcc_inst] = pending;
+}
+
+void hwss_dc_set_optimized_required(union block_sequence_params *params)
+{
+ struct dc *dc = params->dc_set_optimized_required_params.dc;
+ bool optimized_required = params->dc_set_optimized_required_params.optimized_required;
+
+ dc->optimized_required = optimized_required;
+}
+
+void hwss_hubp_disconnect(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_disconnect_params.hubp;
+
+ if (hubp->funcs->hubp_disconnect)
+ hubp->funcs->hubp_disconnect(hubp);
+}
+
+void hwss_hubbub_force_pstate_change_control(union block_sequence_params *params)
+{
+ struct hubbub *hubbub = params->hubbub_force_pstate_change_control_params.hubbub;
+ bool enable = params->hubbub_force_pstate_change_control_params.enable;
+ bool wait = params->hubbub_force_pstate_change_control_params.wait;
+
+ if (hubbub->funcs->force_pstate_change_control) {
+ hubbub->funcs->force_pstate_change_control(hubbub, enable, wait);
+ /* Add delay when enabling pstate change control */
+ if (enable)
+ udelay(500);
+ }
+}
+
+void hwss_tg_enable_crtc(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_enable_crtc_params.tg;
+
+ if (tg->funcs->enable_crtc)
+ tg->funcs->enable_crtc(tg);
+}
+
+void hwss_tg_set_gsl(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_set_gsl_params.tg;
+ struct gsl_params *gsl = &params->tg_set_gsl_params.gsl;
+
+ if (tg->funcs->set_gsl)
+ tg->funcs->set_gsl(tg, gsl);
+}
+
+void hwss_tg_set_gsl_source_select(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_set_gsl_source_select_params.tg;
+ int group_idx = params->tg_set_gsl_source_select_params.group_idx;
+ uint32_t gsl_ready_signal = params->tg_set_gsl_source_select_params.gsl_ready_signal;
+
+ if (tg->funcs->set_gsl_source_select)
+ tg->funcs->set_gsl_source_select(tg, group_idx, gsl_ready_signal);
+}
+
+void hwss_hubp_wait_flip_pending(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_wait_flip_pending_params.hubp;
+ unsigned int timeout_us = params->hubp_wait_flip_pending_params.timeout_us;
+ unsigned int polling_interval_us = params->hubp_wait_flip_pending_params.polling_interval_us;
+ int j = 0;
+
+ for (j = 0; j < timeout_us / polling_interval_us
+ && hubp->funcs->hubp_is_flip_pending(hubp); j++)
+ udelay(polling_interval_us);
+}
+
+void hwss_tg_wait_double_buffer_pending(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_wait_double_buffer_pending_params.tg;
+ unsigned int timeout_us = params->tg_wait_double_buffer_pending_params.timeout_us;
+ unsigned int polling_interval_us = params->tg_wait_double_buffer_pending_params.polling_interval_us;
+ int j = 0;
+
+ if (tg->funcs->get_optc_double_buffer_pending) {
+ for (j = 0; j < timeout_us / polling_interval_us
+ && tg->funcs->get_optc_double_buffer_pending(tg); j++)
+ udelay(polling_interval_us);
+ }
+}
+
+void hwss_update_force_pstate(union block_sequence_params *params)
+{
+ struct dc *dc = params->update_force_pstate_params.dc;
+ struct dc_state *context = params->update_force_pstate_params.context;
+ struct dce_hwseq *hwseq = dc->hwseq;
+
+ if (hwseq->funcs.update_force_pstate)
+ hwseq->funcs.update_force_pstate(dc, context);
+}
+
+void hwss_hubbub_apply_dedcn21_147_wa(union block_sequence_params *params)
+{
+ struct hubbub *hubbub = params->hubbub_apply_dedcn21_147_wa_params.hubbub;
+
+ hubbub->funcs->apply_DEDCN21_147_wa(hubbub);
+}
+
+void hwss_hubbub_allow_self_refresh_control(union block_sequence_params *params)
+{
+ struct hubbub *hubbub = params->hubbub_allow_self_refresh_control_params.hubbub;
+ bool allow = params->hubbub_allow_self_refresh_control_params.allow;
+
+ hubbub->funcs->allow_self_refresh_control(hubbub, allow);
+
+ if (!allow && params->hubbub_allow_self_refresh_control_params.disallow_self_refresh_applied)
+ *params->hubbub_allow_self_refresh_control_params.disallow_self_refresh_applied = true;
+}
+
+void hwss_tg_get_frame_count(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->tg_get_frame_count_params.tg;
+ unsigned int *frame_count = params->tg_get_frame_count_params.frame_count;
+
+ *frame_count = tg->funcs->get_frame_count(tg);
+}
+
+void hwss_mpc_set_dwb_mux(union block_sequence_params *params)
+{
+ struct mpc *mpc = params->mpc_set_dwb_mux_params.mpc;
+ int dwb_id = params->mpc_set_dwb_mux_params.dwb_id;
+ int mpcc_id = params->mpc_set_dwb_mux_params.mpcc_id;
+
+ if (mpc->funcs->set_dwb_mux)
+ mpc->funcs->set_dwb_mux(mpc, dwb_id, mpcc_id);
+}
+
+void hwss_mpc_disable_dwb_mux(union block_sequence_params *params)
+{
+ struct mpc *mpc = params->mpc_disable_dwb_mux_params.mpc;
+ unsigned int dwb_id = params->mpc_disable_dwb_mux_params.dwb_id;
+
+ if (mpc->funcs->disable_dwb_mux)
+ mpc->funcs->disable_dwb_mux(mpc, dwb_id);
+}
+
+void hwss_mcif_wb_config_buf(union block_sequence_params *params)
+{
+ struct mcif_wb *mcif_wb = params->mcif_wb_config_buf_params.mcif_wb;
+ struct mcif_buf_params *mcif_buf_params = params->mcif_wb_config_buf_params.mcif_buf_params;
+ unsigned int dest_height = params->mcif_wb_config_buf_params.dest_height;
+
+ if (mcif_wb->funcs->config_mcif_buf)
+ mcif_wb->funcs->config_mcif_buf(mcif_wb, mcif_buf_params, dest_height);
+}
+
+void hwss_mcif_wb_config_arb(union block_sequence_params *params)
+{
+ struct mcif_wb *mcif_wb = params->mcif_wb_config_arb_params.mcif_wb;
+ struct mcif_arb_params *mcif_arb_params = params->mcif_wb_config_arb_params.mcif_arb_params;
+
+ if (mcif_wb->funcs->config_mcif_arb)
+ mcif_wb->funcs->config_mcif_arb(mcif_wb, mcif_arb_params);
+}
+
+void hwss_mcif_wb_enable(union block_sequence_params *params)
+{
+ struct mcif_wb *mcif_wb = params->mcif_wb_enable_params.mcif_wb;
+
+ if (mcif_wb->funcs->enable_mcif)
+ mcif_wb->funcs->enable_mcif(mcif_wb);
+}
+
+void hwss_mcif_wb_disable(union block_sequence_params *params)
+{
+ struct mcif_wb *mcif_wb = params->mcif_wb_disable_params.mcif_wb;
+
+ if (mcif_wb->funcs->disable_mcif)
+ mcif_wb->funcs->disable_mcif(mcif_wb);
+}
+
+void hwss_dwbc_enable(union block_sequence_params *params)
+{
+ struct dwbc *dwb = params->dwbc_enable_params.dwb;
+ struct dc_dwb_params *dwb_params = params->dwbc_enable_params.dwb_params;
+
+ if (dwb->funcs->enable)
+ dwb->funcs->enable(dwb, dwb_params);
+}
+
+void hwss_dwbc_disable(union block_sequence_params *params)
+{
+ struct dwbc *dwb = params->dwbc_disable_params.dwb;
+
+ if (dwb->funcs->disable)
+ dwb->funcs->disable(dwb);
+}
+
+void hwss_dwbc_update(union block_sequence_params *params)
+{
+ struct dwbc *dwb = params->dwbc_update_params.dwb;
+ struct dc_dwb_params *dwb_params = params->dwbc_update_params.dwb_params;
+
+ if (dwb->funcs->update)
+ dwb->funcs->update(dwb, dwb_params);
+}
+
+void hwss_hubp_update_mall_sel(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_update_mall_sel_params.hubp;
+ uint32_t mall_sel = params->hubp_update_mall_sel_params.mall_sel;
+ bool cache_cursor = params->hubp_update_mall_sel_params.cache_cursor;
+
+ if (hubp && hubp->funcs->hubp_update_mall_sel)
+ hubp->funcs->hubp_update_mall_sel(hubp, mall_sel, cache_cursor);
+}
+
+void hwss_hubp_prepare_subvp_buffering(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_prepare_subvp_buffering_params.hubp;
+ bool enable = params->hubp_prepare_subvp_buffering_params.enable;
+
+ if (hubp && hubp->funcs->hubp_prepare_subvp_buffering)
+ hubp->funcs->hubp_prepare_subvp_buffering(hubp, enable);
+}
+
+void hwss_hubp_set_blank_en(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_set_blank_en_params.hubp;
+ bool enable = params->hubp_set_blank_en_params.enable;
+
+ if (hubp && hubp->funcs->set_hubp_blank_en)
+ hubp->funcs->set_hubp_blank_en(hubp, enable);
+}
+
+void hwss_hubp_disable_control(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_disable_control_params.hubp;
+ bool disable = params->hubp_disable_control_params.disable;
+
+ if (hubp && hubp->funcs->hubp_disable_control)
+ hubp->funcs->hubp_disable_control(hubp, disable);
+}
+
+void hwss_hubbub_soft_reset(union block_sequence_params *params)
+{
+ struct hubbub *hubbub = params->hubbub_soft_reset_params.hubbub;
+ bool reset = params->hubbub_soft_reset_params.reset;
+
+ if (hubbub)
+ params->hubbub_soft_reset_params.hubbub_soft_reset(hubbub, reset);
+}
+
+void hwss_hubp_clk_cntl(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_clk_cntl_params.hubp;
+ bool enable = params->hubp_clk_cntl_params.enable;
+
+ if (hubp && hubp->funcs->hubp_clk_cntl) {
+ hubp->funcs->hubp_clk_cntl(hubp, enable);
+ hubp->power_gated = !enable;
+ }
+}
+
+void hwss_hubp_init(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_init_params.hubp;
+
+ if (hubp && hubp->funcs->hubp_init)
+ hubp->funcs->hubp_init(hubp);
+}
+
+void hwss_hubp_set_vm_system_aperture_settings(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_set_vm_system_aperture_settings_params.hubp;
+ struct vm_system_aperture_param apt;
+
+ apt.sys_default = params->hubp_set_vm_system_aperture_settings_params.sys_default;
+ apt.sys_high = params->hubp_set_vm_system_aperture_settings_params.sys_high;
+ apt.sys_low = params->hubp_set_vm_system_aperture_settings_params.sys_low;
+
+ if (hubp && hubp->funcs->hubp_set_vm_system_aperture_settings)
+ hubp->funcs->hubp_set_vm_system_aperture_settings(hubp, &apt);
+}
+
+void hwss_hubp_set_flip_int(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_set_flip_int_params.hubp;
+
+ if (hubp && hubp->funcs->hubp_set_flip_int)
+ hubp->funcs->hubp_set_flip_int(hubp);
+}
+
+void hwss_dpp_dppclk_control(union block_sequence_params *params)
+{
+ struct dpp *dpp = params->dpp_dppclk_control_params.dpp;
+ bool dppclk_div = params->dpp_dppclk_control_params.dppclk_div;
+ bool enable = params->dpp_dppclk_control_params.enable;
+
+ if (dpp && dpp->funcs->dpp_dppclk_control)
+ dpp->funcs->dpp_dppclk_control(dpp, dppclk_div, enable);
+}
+
+void hwss_disable_phantom_crtc(union block_sequence_params *params)
+{
+ struct timing_generator *tg = params->disable_phantom_crtc_params.tg;
+
+ if (tg && tg->funcs->disable_phantom_crtc)
+ tg->funcs->disable_phantom_crtc(tg);
+}
+
+void hwss_dsc_pg_status(union block_sequence_params *params)
+{
+ struct dce_hwseq *hws = params->dsc_pg_status_params.hws;
+ int dsc_inst = params->dsc_pg_status_params.dsc_inst;
+
+ if (hws && hws->funcs.dsc_pg_status)
+ params->dsc_pg_status_params.is_ungated = hws->funcs.dsc_pg_status(hws, dsc_inst);
+}
+
+void hwss_dsc_wait_disconnect_pending_clear(union block_sequence_params *params)
+{
+ struct display_stream_compressor *dsc = params->dsc_wait_disconnect_pending_clear_params.dsc;
+
+ if (!params->dsc_wait_disconnect_pending_clear_params.is_ungated)
+ return;
+ if (*params->dsc_wait_disconnect_pending_clear_params.is_ungated == false)
+ return;
+
+ if (dsc && dsc->funcs->dsc_wait_disconnect_pending_clear)
+ dsc->funcs->dsc_wait_disconnect_pending_clear(dsc);
+}
+
+void hwss_dsc_disable(union block_sequence_params *params)
+{
+ struct display_stream_compressor *dsc = params->dsc_disable_params.dsc;
+
+ if (!params->dsc_disable_params.is_ungated)
+ return;
+ if (*params->dsc_disable_params.is_ungated == false)
+ return;
+
+ if (dsc && dsc->funcs->dsc_disable)
+ dsc->funcs->dsc_disable(dsc);
+}
+
+void hwss_dccg_set_ref_dscclk(union block_sequence_params *params)
+{
+ struct dccg *dccg = params->dccg_set_ref_dscclk_params.dccg;
+ int dsc_inst = params->dccg_set_ref_dscclk_params.dsc_inst;
+
+ if (!params->dccg_set_ref_dscclk_params.is_ungated)
+ return;
+ if (*params->dccg_set_ref_dscclk_params.is_ungated == false)
+ return;
+
+ if (dccg && dccg->funcs->set_ref_dscclk)
+ dccg->funcs->set_ref_dscclk(dccg, dsc_inst);
+}
+
+void hwss_dpp_pg_control(union block_sequence_params *params)
+{
+ struct dce_hwseq *hws = params->dpp_pg_control_params.hws;
+ unsigned int dpp_inst = params->dpp_pg_control_params.dpp_inst;
+ bool power_on = params->dpp_pg_control_params.power_on;
+
+ if (hws->funcs.dpp_pg_control)
+ hws->funcs.dpp_pg_control(hws, dpp_inst, power_on);
+}
+
+void hwss_hubp_pg_control(union block_sequence_params *params)
+{
+ struct dce_hwseq *hws = params->hubp_pg_control_params.hws;
+ unsigned int hubp_inst = params->hubp_pg_control_params.hubp_inst;
+ bool power_on = params->hubp_pg_control_params.power_on;
+
+ if (hws->funcs.hubp_pg_control)
+ hws->funcs.hubp_pg_control(hws, hubp_inst, power_on);
+}
+
+void hwss_hubp_reset(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_reset_params.hubp;
+
+ if (hubp && hubp->funcs->hubp_reset)
+ hubp->funcs->hubp_reset(hubp);
+}
+
+void hwss_dpp_reset(union block_sequence_params *params)
+{
+ struct dpp *dpp = params->dpp_reset_params.dpp;
+
+ if (dpp && dpp->funcs->dpp_reset)
+ dpp->funcs->dpp_reset(dpp);
+}
+
+void hwss_dpp_root_clock_control(union block_sequence_params *params)
+{
+ struct dce_hwseq *hws = params->dpp_root_clock_control_params.hws;
+ unsigned int dpp_inst = params->dpp_root_clock_control_params.dpp_inst;
+ bool clock_on = params->dpp_root_clock_control_params.clock_on;
+
+ if (hws->funcs.dpp_root_clock_control)
+ hws->funcs.dpp_root_clock_control(hws, dpp_inst, clock_on);
+}
+
+void hwss_dc_ip_request_cntl(union block_sequence_params *params)
+{
+ struct dc *dc = params->dc_ip_request_cntl_params.dc;
+ bool enable = params->dc_ip_request_cntl_params.enable;
+ struct dce_hwseq *hws = dc->hwseq;
+
+ if (hws->funcs.dc_ip_request_cntl)
+ hws->funcs.dc_ip_request_cntl(dc, enable);
+}
+
+void hwss_dccg_update_dpp_dto(union block_sequence_params *params)
+{
+ struct dccg *dccg = params->dccg_update_dpp_dto_params.dccg;
+ int dpp_inst = params->dccg_update_dpp_dto_params.dpp_inst;
+ int dppclk_khz = params->dccg_update_dpp_dto_params.dppclk_khz;
+
+ if (dccg && dccg->funcs->update_dpp_dto)
+ dccg->funcs->update_dpp_dto(dccg, dpp_inst, dppclk_khz);
+}
+
+void hwss_hubp_vtg_sel(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_vtg_sel_params.hubp;
+ uint32_t otg_inst = params->hubp_vtg_sel_params.otg_inst;
+
+ if (hubp && hubp->funcs->hubp_vtg_sel)
+ hubp->funcs->hubp_vtg_sel(hubp, otg_inst);
+}
+
+void hwss_hubp_setup2(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_setup2_params.hubp;
+ struct dml2_dchub_per_pipe_register_set *hubp_regs = params->hubp_setup2_params.hubp_regs;
+ union dml2_global_sync_programming *global_sync = params->hubp_setup2_params.global_sync;
+ struct dc_crtc_timing *timing = params->hubp_setup2_params.timing;
+
+ if (hubp && hubp->funcs->hubp_setup2)
+ hubp->funcs->hubp_setup2(hubp, hubp_regs, global_sync, timing);
+}
+
+void hwss_hubp_setup(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_setup_params.hubp;
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs = params->hubp_setup_params.dlg_regs;
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs = params->hubp_setup_params.ttu_regs;
+ struct _vcs_dpi_display_rq_regs_st *rq_regs = params->hubp_setup_params.rq_regs;
+ struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest = params->hubp_setup_params.pipe_dest;
+
+ if (hubp && hubp->funcs->hubp_setup)
+ hubp->funcs->hubp_setup(hubp, dlg_regs, ttu_regs, rq_regs, pipe_dest);
+}
+
+void hwss_hubp_set_unbounded_requesting(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_set_unbounded_requesting_params.hubp;
+ bool unbounded_req = params->hubp_set_unbounded_requesting_params.unbounded_req;
+
+ if (hubp && hubp->funcs->set_unbounded_requesting)
+ hubp->funcs->set_unbounded_requesting(hubp, unbounded_req);
+}
+
+void hwss_hubp_setup_interdependent2(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_setup_interdependent2_params.hubp;
+ struct dml2_dchub_per_pipe_register_set *hubp_regs = params->hubp_setup_interdependent2_params.hubp_regs;
+
+ if (hubp && hubp->funcs->hubp_setup_interdependent2)
+ hubp->funcs->hubp_setup_interdependent2(hubp, hubp_regs);
+}
+
+void hwss_hubp_setup_interdependent(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_setup_interdependent_params.hubp;
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs = params->hubp_setup_interdependent_params.dlg_regs;
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs = params->hubp_setup_interdependent_params.ttu_regs;
+
+ if (hubp && hubp->funcs->hubp_setup_interdependent)
+ hubp->funcs->hubp_setup_interdependent(hubp, dlg_regs, ttu_regs);
+}
+
+void hwss_dpp_set_cursor_matrix(union block_sequence_params *params)
+{
+ struct dpp *dpp = params->dpp_set_cursor_matrix_params.dpp;
+ enum dc_color_space color_space = params->dpp_set_cursor_matrix_params.color_space;
+ struct dc_csc_transform *cursor_csc_color_matrix = params->dpp_set_cursor_matrix_params.cursor_csc_color_matrix;
+
+ if (dpp && dpp->funcs->set_cursor_matrix)
+ dpp->funcs->set_cursor_matrix(dpp, color_space, *cursor_csc_color_matrix);
+}
+
+void hwss_mpc_update_mpcc(union block_sequence_params *params)
+{
+ struct dc *dc = params->mpc_update_mpcc_params.dc;
+ struct pipe_ctx *pipe_ctx = params->mpc_update_mpcc_params.pipe_ctx;
+ struct dce_hwseq *hws = dc->hwseq;
+
+ if (hws->funcs.update_mpcc)
+ hws->funcs.update_mpcc(dc, pipe_ctx);
+}
+
+void hwss_mpc_update_blending(union block_sequence_params *params)
+{
+ struct mpc *mpc = params->mpc_update_blending_params.mpc;
+ struct mpcc_blnd_cfg *blnd_cfg = &params->mpc_update_blending_params.blnd_cfg;
+ int mpcc_id = params->mpc_update_blending_params.mpcc_id;
+
+ if (mpc && mpc->funcs->update_blending)
+ mpc->funcs->update_blending(mpc, blnd_cfg, mpcc_id);
+}
+
+void hwss_mpc_assert_idle_mpcc(union block_sequence_params *params)
+{
+ struct mpc *mpc = params->mpc_assert_idle_mpcc_params.mpc;
+ int mpcc_id = params->mpc_assert_idle_mpcc_params.mpcc_id;
+
+ if (mpc && mpc->funcs->wait_for_idle)
+ mpc->funcs->wait_for_idle(mpc, mpcc_id);
+}
+
+void hwss_mpc_insert_plane(union block_sequence_params *params)
+{
+ struct mpc *mpc = params->mpc_insert_plane_params.mpc;
+ struct mpc_tree *tree = params->mpc_insert_plane_params.mpc_tree_params;
+ struct mpcc_blnd_cfg *blnd_cfg = &params->mpc_insert_plane_params.blnd_cfg;
+ struct mpcc_sm_cfg *sm_cfg = params->mpc_insert_plane_params.sm_cfg;
+ struct mpcc *insert_above_mpcc = params->mpc_insert_plane_params.insert_above_mpcc;
+ int mpcc_id = params->mpc_insert_plane_params.mpcc_id;
+ int dpp_id = params->mpc_insert_plane_params.dpp_id;
+
+ if (mpc && mpc->funcs->insert_plane)
+ mpc->funcs->insert_plane(mpc, tree, blnd_cfg, sm_cfg, insert_above_mpcc,
+ dpp_id, mpcc_id);
+}
+
+void hwss_dpp_set_scaler(union block_sequence_params *params)
+{
+ struct dpp *dpp = params->dpp_set_scaler_params.dpp;
+ const struct scaler_data *scl_data = params->dpp_set_scaler_params.scl_data;
+
+ if (dpp && dpp->funcs->dpp_set_scaler)
+ dpp->funcs->dpp_set_scaler(dpp, scl_data);
+}
+
+void hwss_hubp_mem_program_viewport(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_mem_program_viewport_params.hubp;
+ const struct rect *viewport = params->hubp_mem_program_viewport_params.viewport;
+ const struct rect *viewport_c = params->hubp_mem_program_viewport_params.viewport_c;
+
+ if (hubp && hubp->funcs->mem_program_viewport)
+ hubp->funcs->mem_program_viewport(hubp, viewport, viewport_c);
+}
+
+void hwss_abort_cursor_offload_update(union block_sequence_params *params)
+{
+ struct dc *dc = params->abort_cursor_offload_update_params.dc;
+ struct pipe_ctx *pipe_ctx = params->abort_cursor_offload_update_params.pipe_ctx;
+
+ if (dc && dc->hwss.abort_cursor_offload_update)
+ dc->hwss.abort_cursor_offload_update(dc, pipe_ctx);
+}
+
+void hwss_set_cursor_attribute(union block_sequence_params *params)
+{
+ struct dc *dc = params->set_cursor_attribute_params.dc;
+ struct pipe_ctx *pipe_ctx = params->set_cursor_attribute_params.pipe_ctx;
+
+ if (dc && dc->hwss.set_cursor_attribute)
+ dc->hwss.set_cursor_attribute(pipe_ctx);
+}
+
+void hwss_set_cursor_position(union block_sequence_params *params)
+{
+ struct dc *dc = params->set_cursor_position_params.dc;
+ struct pipe_ctx *pipe_ctx = params->set_cursor_position_params.pipe_ctx;
+
+ if (dc && dc->hwss.set_cursor_position)
+ dc->hwss.set_cursor_position(pipe_ctx);
+}
+
+void hwss_set_cursor_sdr_white_level(union block_sequence_params *params)
+{
+ struct dc *dc = params->set_cursor_sdr_white_level_params.dc;
+ struct pipe_ctx *pipe_ctx = params->set_cursor_sdr_white_level_params.pipe_ctx;
+
+ if (dc && dc->hwss.set_cursor_sdr_white_level)
+ dc->hwss.set_cursor_sdr_white_level(pipe_ctx);
+}
+
+void hwss_program_output_csc(union block_sequence_params *params)
+{
+ struct dc *dc = params->program_output_csc_params.dc;
+ struct pipe_ctx *pipe_ctx = params->program_output_csc_params.pipe_ctx;
+ enum dc_color_space colorspace = params->program_output_csc_params.colorspace;
+ uint16_t *matrix = params->program_output_csc_params.matrix;
+ int opp_id = params->program_output_csc_params.opp_id;
+
+ if (dc && dc->hwss.program_output_csc)
+ dc->hwss.program_output_csc(dc, pipe_ctx, colorspace, matrix, opp_id);
+}
+
+void hwss_hubp_set_blank(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->hubp_set_blank_params.hubp;
+ bool blank = params->hubp_set_blank_params.blank;
+
+ if (hubp && hubp->funcs->set_blank)
+ hubp->funcs->set_blank(hubp, blank);
+}
+
+void hwss_phantom_hubp_post_enable(union block_sequence_params *params)
+{
+ struct hubp *hubp = params->phantom_hubp_post_enable_params.hubp;
+
+ if (hubp && hubp->funcs->phantom_hubp_post_enable)
+ hubp->funcs->phantom_hubp_post_enable(hubp);
+}
+
+void hwss_add_dccg_set_dto_dscclk(struct block_sequence_state *seq_state,
+ struct dccg *dccg, int inst, int num_slices_h)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DCCG_SET_DTO_DSCCLK;
+ seq_state->steps[*seq_state->num_steps].params.dccg_set_dto_dscclk_params.dccg = dccg;
+ seq_state->steps[*seq_state->num_steps].params.dccg_set_dto_dscclk_params.inst = inst;
+ seq_state->steps[*seq_state->num_steps].params.dccg_set_dto_dscclk_params.num_slices_h = num_slices_h;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dsc_calculate_and_set_config(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx, bool enable, int opp_cnt)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DSC_CALCULATE_AND_SET_CONFIG;
+ seq_state->steps[*seq_state->num_steps].params.dsc_calculate_and_set_config_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].params.dsc_calculate_and_set_config_params.enable = enable;
+ seq_state->steps[*seq_state->num_steps].params.dsc_calculate_and_set_config_params.opp_cnt = opp_cnt;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_mpc_remove_mpcc(struct block_sequence_state *seq_state,
+ struct mpc *mpc, struct mpc_tree *mpc_tree_params, struct mpcc *mpcc_to_remove)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MPC_REMOVE_MPCC;
+ seq_state->steps[*seq_state->num_steps].params.mpc_remove_mpcc_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.mpc_remove_mpcc_params.mpc_tree_params = mpc_tree_params;
+ seq_state->steps[*seq_state->num_steps].params.mpc_remove_mpcc_params.mpcc_to_remove = mpcc_to_remove;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_opp_set_mpcc_disconnect_pending(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp, int mpcc_inst, bool pending)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = OPP_SET_MPCC_DISCONNECT_PENDING;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_mpcc_disconnect_pending_params.opp = opp;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_mpcc_disconnect_pending_params.mpcc_inst = mpcc_inst;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_mpcc_disconnect_pending_params.pending = pending;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_disconnect(struct block_sequence_state *seq_state,
+ struct hubp *hubp)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_DISCONNECT;
+ seq_state->steps[*seq_state->num_steps].params.hubp_disconnect_params.hubp = hubp;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dsc_enable_with_opp(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DSC_ENABLE_WITH_OPP;
+ seq_state->steps[*seq_state->num_steps].params.dsc_enable_with_opp_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_tg_set_dsc_config(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, struct dsc_optc_config *dsc_optc_cfg, bool enable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = TG_SET_DSC_CONFIG;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_dsc_config_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_dsc_config_params.dsc_optc_cfg = dsc_optc_cfg;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_dsc_config_params.enable = enable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dsc_disconnect(struct block_sequence_state *seq_state,
+ struct display_stream_compressor *dsc)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DSC_DISCONNECT;
+ seq_state->steps[*seq_state->num_steps].params.dsc_disconnect_params.dsc = dsc;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dc_set_optimized_required(struct block_sequence_state *seq_state,
+ struct dc *dc, bool optimized_required)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DC_SET_OPTIMIZED_REQUIRED;
+ seq_state->steps[*seq_state->num_steps].params.dc_set_optimized_required_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.dc_set_optimized_required_params.optimized_required = optimized_required;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_abm_set_immediate_disable(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = ABM_SET_IMMEDIATE_DISABLE;
+ seq_state->steps[*seq_state->num_steps].params.set_abm_immediate_disable_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.set_abm_immediate_disable_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_opp_set_disp_pattern_generator(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ enum controller_dp_test_pattern test_pattern,
+ enum controller_dp_color_space color_space,
+ enum dc_color_depth color_depth,
+ struct tg_color solid_color,
+ bool use_solid_color,
+ int width,
+ int height,
+ int offset)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = OPP_SET_DISP_PATTERN_GENERATOR;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.opp = opp;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.test_pattern = test_pattern;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.color_space = color_space;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.color_depth = color_depth;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.solid_color = solid_color;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.use_solid_color = use_solid_color;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.width = width;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.height = height;
+ seq_state->steps[*seq_state->num_steps].params.opp_set_disp_pattern_generator_params.offset = offset;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add MPC update blending to block sequence
+ */
+void hwss_add_mpc_update_blending(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ struct mpcc_blnd_cfg blnd_cfg,
+ int mpcc_id)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MPC_UPDATE_BLENDING;
+ seq_state->steps[*seq_state->num_steps].params.mpc_update_blending_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.mpc_update_blending_params.blnd_cfg = blnd_cfg;
+ seq_state->steps[*seq_state->num_steps].params.mpc_update_blending_params.mpcc_id = mpcc_id;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add MPC insert plane to block sequence
+ */
+void hwss_add_mpc_insert_plane(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ struct mpc_tree *mpc_tree_params,
+ struct mpcc_blnd_cfg blnd_cfg,
+ struct mpcc_sm_cfg *sm_cfg,
+ struct mpcc *insert_above_mpcc,
+ int dpp_id,
+ int mpcc_id)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MPC_INSERT_PLANE;
+ seq_state->steps[*seq_state->num_steps].params.mpc_insert_plane_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.mpc_insert_plane_params.mpc_tree_params = mpc_tree_params;
+ seq_state->steps[*seq_state->num_steps].params.mpc_insert_plane_params.blnd_cfg = blnd_cfg;
+ seq_state->steps[*seq_state->num_steps].params.mpc_insert_plane_params.sm_cfg = sm_cfg;
+ seq_state->steps[*seq_state->num_steps].params.mpc_insert_plane_params.insert_above_mpcc = insert_above_mpcc;
+ seq_state->steps[*seq_state->num_steps].params.mpc_insert_plane_params.dpp_id = dpp_id;
+ seq_state->steps[*seq_state->num_steps].params.mpc_insert_plane_params.mpcc_id = mpcc_id;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add MPC assert idle MPCC to block sequence
+ */
+void hwss_add_mpc_assert_idle_mpcc(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ int mpcc_id)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MPC_ASSERT_IDLE_MPCC;
+ seq_state->steps[*seq_state->num_steps].params.mpc_assert_idle_mpcc_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.mpc_assert_idle_mpcc_params.mpcc_id = mpcc_id;
+ (*seq_state->num_steps)++;
+ }
+}
+
+/*
+ * Helper function to add HUBP set blank to block sequence
+ */
+void hwss_add_hubp_set_blank(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool blank)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SET_BLANK;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_blank_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_blank_params.blank = blank;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_opp_program_bit_depth_reduction(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ bool use_default_params,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = OPP_PROGRAM_BIT_DEPTH_REDUCTION;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_bit_depth_reduction_params.opp = opp;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_bit_depth_reduction_params.use_default_params = use_default_params;
+ seq_state->steps[*seq_state->num_steps].params.opp_program_bit_depth_reduction_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dc_ip_request_cntl(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ bool enable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DC_IP_REQUEST_CNTL;
+ seq_state->steps[*seq_state->num_steps].params.dc_ip_request_cntl_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.dc_ip_request_cntl_params.enable = enable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dwbc_update(struct block_sequence_state *seq_state,
+ struct dwbc *dwb,
+ struct dc_dwb_params *dwb_params)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DWBC_UPDATE;
+ seq_state->steps[*seq_state->num_steps].params.dwbc_update_params.dwb = dwb;
+ seq_state->steps[*seq_state->num_steps].params.dwbc_update_params.dwb_params = dwb_params;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_mcif_wb_config_buf(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb,
+ struct mcif_buf_params *mcif_buf_params,
+ unsigned int dest_height)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MCIF_WB_CONFIG_BUF;
+ seq_state->steps[*seq_state->num_steps].params.mcif_wb_config_buf_params.mcif_wb = mcif_wb;
+ seq_state->steps[*seq_state->num_steps].params.mcif_wb_config_buf_params.mcif_buf_params = mcif_buf_params;
+ seq_state->steps[*seq_state->num_steps].params.mcif_wb_config_buf_params.dest_height = dest_height;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_mcif_wb_config_arb(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb,
+ struct mcif_arb_params *mcif_arb_params)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MCIF_WB_CONFIG_ARB;
+ seq_state->steps[*seq_state->num_steps].params.mcif_wb_config_arb_params.mcif_wb = mcif_wb;
+ seq_state->steps[*seq_state->num_steps].params.mcif_wb_config_arb_params.mcif_arb_params = mcif_arb_params;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_mcif_wb_enable(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MCIF_WB_ENABLE;
+ seq_state->steps[*seq_state->num_steps].params.mcif_wb_enable_params.mcif_wb = mcif_wb;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_mcif_wb_disable(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MCIF_WB_DISABLE;
+ seq_state->steps[*seq_state->num_steps].params.mcif_wb_disable_params.mcif_wb = mcif_wb;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_mpc_set_dwb_mux(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ int dwb_id,
+ int mpcc_id)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MPC_SET_DWB_MUX;
+ seq_state->steps[*seq_state->num_steps].params.mpc_set_dwb_mux_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.mpc_set_dwb_mux_params.dwb_id = dwb_id;
+ seq_state->steps[*seq_state->num_steps].params.mpc_set_dwb_mux_params.mpcc_id = mpcc_id;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_mpc_disable_dwb_mux(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ unsigned int dwb_id)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = MPC_DISABLE_DWB_MUX;
+ seq_state->steps[*seq_state->num_steps].params.mpc_disable_dwb_mux_params.mpc = mpc;
+ seq_state->steps[*seq_state->num_steps].params.mpc_disable_dwb_mux_params.dwb_id = dwb_id;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dwbc_enable(struct block_sequence_state *seq_state,
+ struct dwbc *dwb,
+ struct dc_dwb_params *dwb_params)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DWBC_ENABLE;
+ seq_state->steps[*seq_state->num_steps].params.dwbc_enable_params.dwb = dwb;
+ seq_state->steps[*seq_state->num_steps].params.dwbc_enable_params.dwb_params = dwb_params;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dwbc_disable(struct block_sequence_state *seq_state,
+ struct dwbc *dwb)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DWBC_DISABLE;
+ seq_state->steps[*seq_state->num_steps].params.dwbc_disable_params.dwb = dwb;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_tg_set_gsl(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ struct gsl_params gsl)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = TG_SET_GSL;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_gsl_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_gsl_params.gsl = gsl;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_tg_set_gsl_source_select(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ int group_idx,
+ uint32_t gsl_ready_signal)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = TG_SET_GSL_SOURCE_SELECT;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_gsl_source_select_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_gsl_source_select_params.group_idx = group_idx;
+ seq_state->steps[*seq_state->num_steps].params.tg_set_gsl_source_select_params.gsl_ready_signal = gsl_ready_signal;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_update_mall_sel(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ uint32_t mall_sel,
+ bool cache_cursor)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_UPDATE_MALL_SEL;
+ seq_state->steps[*seq_state->num_steps].params.hubp_update_mall_sel_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_update_mall_sel_params.mall_sel = mall_sel;
+ seq_state->steps[*seq_state->num_steps].params.hubp_update_mall_sel_params.cache_cursor = cache_cursor;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_prepare_subvp_buffering(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool enable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_PREPARE_SUBVP_BUFFERING;
+ seq_state->steps[*seq_state->num_steps].params.hubp_prepare_subvp_buffering_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_prepare_subvp_buffering_params.enable = enable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_set_blank_en(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool enable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SET_BLANK_EN;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_blank_en_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_blank_en_params.enable = enable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_disable_control(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool disable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_DISABLE_CONTROL;
+ seq_state->steps[*seq_state->num_steps].params.hubp_disable_control_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_disable_control_params.disable = disable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubbub_soft_reset(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ void (*hubbub_soft_reset)(struct hubbub *hubbub, bool reset),
+ bool reset)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBBUB_SOFT_RESET;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_soft_reset_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_soft_reset_params.hubbub_soft_reset = hubbub_soft_reset;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_soft_reset_params.reset = reset;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_clk_cntl(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool enable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_CLK_CNTL;
+ seq_state->steps[*seq_state->num_steps].params.hubp_clk_cntl_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_clk_cntl_params.enable = enable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dpp_dppclk_control(struct block_sequence_state *seq_state,
+ struct dpp *dpp,
+ bool dppclk_div,
+ bool enable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DPP_DPPCLK_CONTROL;
+ seq_state->steps[*seq_state->num_steps].params.dpp_dppclk_control_params.dpp = dpp;
+ seq_state->steps[*seq_state->num_steps].params.dpp_dppclk_control_params.dppclk_div = dppclk_div;
+ seq_state->steps[*seq_state->num_steps].params.dpp_dppclk_control_params.enable = enable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_disable_phantom_crtc(struct block_sequence_state *seq_state,
+ struct timing_generator *tg)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DISABLE_PHANTOM_CRTC;
+ seq_state->steps[*seq_state->num_steps].params.disable_phantom_crtc_params.tg = tg;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dsc_pg_status(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ int dsc_inst,
+ bool is_ungated)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DSC_PG_STATUS;
+ seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.hws = hws;
+ seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.dsc_inst = dsc_inst;
+ seq_state->steps[*seq_state->num_steps].params.dsc_pg_status_params.is_ungated = is_ungated;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dsc_wait_disconnect_pending_clear(struct block_sequence_state *seq_state,
+ struct display_stream_compressor *dsc,
+ bool *is_ungated)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DSC_WAIT_DISCONNECT_PENDING_CLEAR;
+ seq_state->steps[*seq_state->num_steps].params.dsc_wait_disconnect_pending_clear_params.dsc = dsc;
+ seq_state->steps[*seq_state->num_steps].params.dsc_wait_disconnect_pending_clear_params.is_ungated = is_ungated;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dsc_disable(struct block_sequence_state *seq_state,
+ struct display_stream_compressor *dsc,
+ bool *is_ungated)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DSC_DISABLE;
+ seq_state->steps[*seq_state->num_steps].params.dsc_disable_params.dsc = dsc;
+ seq_state->steps[*seq_state->num_steps].params.dsc_disable_params.is_ungated = is_ungated;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dccg_set_ref_dscclk(struct block_sequence_state *seq_state,
+ struct dccg *dccg,
+ int dsc_inst,
+ bool *is_ungated)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DCCG_SET_REF_DSCCLK;
+ seq_state->steps[*seq_state->num_steps].params.dccg_set_ref_dscclk_params.dccg = dccg;
+ seq_state->steps[*seq_state->num_steps].params.dccg_set_ref_dscclk_params.dsc_inst = dsc_inst;
+ seq_state->steps[*seq_state->num_steps].params.dccg_set_ref_dscclk_params.is_ungated = is_ungated;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dpp_root_clock_control(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ unsigned int dpp_inst,
+ bool clock_on)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DPP_ROOT_CLOCK_CONTROL;
+ seq_state->steps[*seq_state->num_steps].params.dpp_root_clock_control_params.hws = hws;
+ seq_state->steps[*seq_state->num_steps].params.dpp_root_clock_control_params.dpp_inst = dpp_inst;
+ seq_state->steps[*seq_state->num_steps].params.dpp_root_clock_control_params.clock_on = clock_on;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dpp_pg_control(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ unsigned int dpp_inst,
+ bool power_on)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DPP_PG_CONTROL;
+ seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.hws = hws;
+ seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.dpp_inst = dpp_inst;
+ seq_state->steps[*seq_state->num_steps].params.dpp_pg_control_params.power_on = power_on;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_pg_control(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ unsigned int hubp_inst,
+ bool power_on)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_PG_CONTROL;
+ seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.hws = hws;
+ seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.hubp_inst = hubp_inst;
+ seq_state->steps[*seq_state->num_steps].params.hubp_pg_control_params.power_on = power_on;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_init(struct block_sequence_state *seq_state,
+ struct hubp *hubp)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_INIT;
+ seq_state->steps[*seq_state->num_steps].params.hubp_init_params.hubp = hubp;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_reset(struct block_sequence_state *seq_state,
+ struct hubp *hubp)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_RESET;
+ seq_state->steps[*seq_state->num_steps].params.hubp_reset_params.hubp = hubp;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dpp_reset(struct block_sequence_state *seq_state,
+ struct dpp *dpp)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DPP_RESET;
+ seq_state->steps[*seq_state->num_steps].params.dpp_reset_params.dpp = dpp;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_opp_pipe_clock_control(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ bool enable)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = OPP_PIPE_CLOCK_CONTROL;
+ seq_state->steps[*seq_state->num_steps].params.opp_pipe_clock_control_params.opp = opp;
+ seq_state->steps[*seq_state->num_steps].params.opp_pipe_clock_control_params.enable = enable;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_set_vm_system_aperture_settings(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ uint64_t sys_default,
+ uint64_t sys_low,
+ uint64_t sys_high)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SET_VM_SYSTEM_APERTURE_SETTINGS;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_vm_system_aperture_settings_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_vm_system_aperture_settings_params.sys_default.quad_part = sys_default;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_vm_system_aperture_settings_params.sys_low.quad_part = sys_low;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_vm_system_aperture_settings_params.sys_high.quad_part = sys_high;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_set_flip_int(struct block_sequence_state *seq_state,
+ struct hubp *hubp)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SET_FLIP_INT;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_flip_int_params.hubp = hubp;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dccg_update_dpp_dto(struct block_sequence_state *seq_state,
+ struct dccg *dccg,
+ int dpp_inst,
+ int dppclk_khz)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DCCG_UPDATE_DPP_DTO;
+ seq_state->steps[*seq_state->num_steps].params.dccg_update_dpp_dto_params.dccg = dccg;
+ seq_state->steps[*seq_state->num_steps].params.dccg_update_dpp_dto_params.dpp_inst = dpp_inst;
+ seq_state->steps[*seq_state->num_steps].params.dccg_update_dpp_dto_params.dppclk_khz = dppclk_khz;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_vtg_sel(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ uint32_t otg_inst)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_VTG_SEL;
+ seq_state->steps[*seq_state->num_steps].params.hubp_vtg_sel_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_vtg_sel_params.otg_inst = otg_inst;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_setup2(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct dml2_dchub_per_pipe_register_set *hubp_regs,
+ union dml2_global_sync_programming *global_sync,
+ struct dc_crtc_timing *timing)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SETUP2;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup2_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup2_params.hubp_regs = hubp_regs;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup2_params.global_sync = global_sync;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup2_params.timing = timing;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_setup(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs,
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs,
+ struct _vcs_dpi_display_rq_regs_st *rq_regs,
+ struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SETUP;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_params.dlg_regs = dlg_regs;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_params.ttu_regs = ttu_regs;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_params.rq_regs = rq_regs;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_params.pipe_dest = pipe_dest;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_set_unbounded_requesting(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool unbounded_req)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SET_UNBOUNDED_REQUESTING;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_unbounded_requesting_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_set_unbounded_requesting_params.unbounded_req = unbounded_req;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_setup_interdependent2(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct dml2_dchub_per_pipe_register_set *hubp_regs)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SETUP_INTERDEPENDENT2;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_interdependent2_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_interdependent2_params.hubp_regs = hubp_regs;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_setup_interdependent(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs,
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_SETUP_INTERDEPENDENT;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_interdependent_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_interdependent_params.dlg_regs = dlg_regs;
+ seq_state->steps[*seq_state->num_steps].params.hubp_setup_interdependent_params.ttu_regs = ttu_regs;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_program_surface_config(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ enum surface_pixel_format format,
+ struct dc_tiling_info *tiling_info,
+ struct plane_size plane_size,
+ enum dc_rotation_angle rotation,
+ struct dc_plane_dcc_param *dcc,
+ bool horizontal_mirror,
+ int compat_level)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_PROGRAM_SURFACE_CONFIG;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.format = format;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.tiling_info = tiling_info;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.plane_size = plane_size;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.rotation = rotation;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.dcc = dcc;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.horizontal_mirror = horizontal_mirror;
+ seq_state->steps[*seq_state->num_steps].params.program_surface_config_params.compat_level = compat_level;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dpp_setup_dpp(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DPP_SETUP_DPP;
+ seq_state->steps[*seq_state->num_steps].params.setup_dpp_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dpp_set_cursor_matrix(struct block_sequence_state *seq_state,
+ struct dpp *dpp,
+ enum dc_color_space color_space,
+ struct dc_csc_transform *cursor_csc_color_matrix)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DPP_SET_CURSOR_MATRIX;
+ seq_state->steps[*seq_state->num_steps].params.dpp_set_cursor_matrix_params.dpp = dpp;
+ seq_state->steps[*seq_state->num_steps].params.dpp_set_cursor_matrix_params.color_space = color_space;
+ seq_state->steps[*seq_state->num_steps].params.dpp_set_cursor_matrix_params.cursor_csc_color_matrix = cursor_csc_color_matrix;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_dpp_set_scaler(struct block_sequence_state *seq_state,
+ struct dpp *dpp,
+ const struct scaler_data *scl_data)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = DPP_SET_SCALER;
+ seq_state->steps[*seq_state->num_steps].params.dpp_set_scaler_params.dpp = dpp;
+ seq_state->steps[*seq_state->num_steps].params.dpp_set_scaler_params.scl_data = scl_data;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubp_mem_program_viewport(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ const struct rect *viewport,
+ const struct rect *viewport_c)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBP_MEM_PROGRAM_VIEWPORT;
+ seq_state->steps[*seq_state->num_steps].params.hubp_mem_program_viewport_params.hubp = hubp;
+ seq_state->steps[*seq_state->num_steps].params.hubp_mem_program_viewport_params.viewport = viewport;
+ seq_state->steps[*seq_state->num_steps].params.hubp_mem_program_viewport_params.viewport_c = viewport_c;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_abort_cursor_offload_update(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = ABORT_CURSOR_OFFLOAD_UPDATE;
+ seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.abort_cursor_offload_update_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_set_cursor_attribute(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = SET_CURSOR_ATTRIBUTE;
+ seq_state->steps[*seq_state->num_steps].params.set_cursor_attribute_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.set_cursor_attribute_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_set_cursor_position(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = SET_CURSOR_POSITION;
+ seq_state->steps[*seq_state->num_steps].params.set_cursor_position_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.set_cursor_position_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_set_cursor_sdr_white_level(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = SET_CURSOR_SDR_WHITE_LEVEL;
+ seq_state->steps[*seq_state->num_steps].params.set_cursor_sdr_white_level_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.set_cursor_sdr_white_level_params.pipe_ctx = pipe_ctx;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_program_output_csc(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ enum dc_color_space colorspace,
+ uint16_t *matrix,
+ int opp_id)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = PROGRAM_OUTPUT_CSC;
+ seq_state->steps[*seq_state->num_steps].params.program_output_csc_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.program_output_csc_params.pipe_ctx = pipe_ctx;
+ seq_state->steps[*seq_state->num_steps].params.program_output_csc_params.colorspace = colorspace;
+ seq_state->steps[*seq_state->num_steps].params.program_output_csc_params.matrix = matrix;
+ seq_state->steps[*seq_state->num_steps].params.program_output_csc_params.opp_id = opp_id;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_phantom_hubp_post_enable(struct block_sequence_state *seq_state,
+ struct hubp *hubp)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = PHANTOM_HUBP_POST_ENABLE;
+ seq_state->steps[*seq_state->num_steps].params.phantom_hubp_post_enable_params.hubp = hubp;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_update_force_pstate(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct dc_state *context)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = UPDATE_FORCE_PSTATE;
+ seq_state->steps[*seq_state->num_steps].params.update_force_pstate_params.dc = dc;
+ seq_state->steps[*seq_state->num_steps].params.update_force_pstate_params.context = context;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubbub_apply_dedcn21_147_wa(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBBUB_APPLY_DEDCN21_147_WA;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_apply_dedcn21_147_wa_params.hubbub = hubbub;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_hubbub_allow_self_refresh_control(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ bool allow,
+ bool *disallow_self_refresh_applied)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = HUBBUB_ALLOW_SELF_REFRESH_CONTROL;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_allow_self_refresh_control_params.hubbub = hubbub;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_allow_self_refresh_control_params.allow = allow;
+ seq_state->steps[*seq_state->num_steps].params.hubbub_allow_self_refresh_control_params.disallow_self_refresh_applied = disallow_self_refresh_applied;
+ (*seq_state->num_steps)++;
+ }
+}
+
+void hwss_add_tg_get_frame_count(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ unsigned int *frame_count)
+{
+ if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) {
+ seq_state->steps[*seq_state->num_steps].func = TG_GET_FRAME_COUNT;
+ seq_state->steps[*seq_state->num_steps].params.tg_get_frame_count_params.tg = tg;
+ seq_state->steps[*seq_state->num_steps].params.tg_get_frame_count_params.frame_count = frame_count;
+ (*seq_state->num_steps)++;
+ }
+}
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c
index a180f68f711c..deb23d20bca6 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c
@@ -522,10 +522,10 @@ struct link_encoder *link_enc_cfg_get_link_enc_used_by_link(
struct link_encoder *link_enc_cfg_get_next_avail_link_enc(struct dc *dc)
{
struct link_encoder *link_enc = NULL;
- enum engine_id encs_assigned[MAX_DIG_LINK_ENCODERS];
+ enum engine_id encs_assigned[MAX_LINK_ENCODERS];
int i;
- for (i = 0; i < MAX_DIG_LINK_ENCODERS; i++)
+ for (i = 0; i < MAX_LINK_ENCODERS; i++)
encs_assigned[i] = ENGINE_ID_UNKNOWN;
/* Add assigned encoders to list. */
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index bc5dedf5f60c..848c267ef11e 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
@@ -95,10 +95,44 @@
#define DC_LOGGER \
dc->ctx->logger
#define DC_LOGGER_INIT(logger)
-#include "dml2/dml2_wrapper.h"
+#include "dml2_0/dml2_wrapper.h"
#define UNABLE_TO_SPLIT -1
+static void capture_pipe_topology_data(struct dc *dc, int plane_idx, int slice_idx, int stream_idx,
+ int dpp_inst, int opp_inst, int tg_inst, bool is_phantom_pipe)
+{
+ struct pipe_topology_snapshot *current_snapshot = &dc->debug_data.topology_history.snapshots[dc->debug_data.topology_history.current_snapshot_index];
+
+ if (current_snapshot->line_count >= MAX_PIPES)
+ return;
+
+ current_snapshot->pipe_log_lines[current_snapshot->line_count].is_phantom_pipe = is_phantom_pipe;
+ current_snapshot->pipe_log_lines[current_snapshot->line_count].plane_idx = plane_idx;
+ current_snapshot->pipe_log_lines[current_snapshot->line_count].slice_idx = slice_idx;
+ current_snapshot->pipe_log_lines[current_snapshot->line_count].stream_idx = stream_idx;
+ current_snapshot->pipe_log_lines[current_snapshot->line_count].dpp_inst = dpp_inst;
+ current_snapshot->pipe_log_lines[current_snapshot->line_count].opp_inst = opp_inst;
+ current_snapshot->pipe_log_lines[current_snapshot->line_count].tg_inst = tg_inst;
+
+ current_snapshot->line_count++;
+}
+
+static void start_new_topology_snapshot(struct dc *dc, struct dc_state *state)
+{
+ // Move to next snapshot slot (circular buffer)
+ dc->debug_data.topology_history.current_snapshot_index = (dc->debug_data.topology_history.current_snapshot_index + 1) % MAX_TOPOLOGY_SNAPSHOTS;
+
+ // Clear the new snapshot
+ struct pipe_topology_snapshot *current_snapshot = &dc->debug_data.topology_history.snapshots[dc->debug_data.topology_history.current_snapshot_index];
+ memset(current_snapshot, 0, sizeof(*current_snapshot));
+
+ // Set metadata
+ current_snapshot->timestamp_us = dm_get_timestamp(dc->ctx);
+ current_snapshot->stream_count = state->stream_count;
+ current_snapshot->phantom_stream_count = state->phantom_stream_count;
+}
+
enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id)
{
enum dce_version dc_version = DCE_VERSION_UNKNOWN;
@@ -446,6 +480,14 @@ bool resource_construct(
DC_ERR("DC: failed to create stream_encoder!\n");
pool->stream_enc_count++;
}
+
+ for (i = 0; i < caps->num_analog_stream_encoder; i++) {
+ pool->stream_enc[caps->num_stream_encoder + i] =
+ create_funcs->create_stream_encoder(ENGINE_ID_DACA + i, ctx);
+ if (pool->stream_enc[caps->num_stream_encoder + i] == NULL)
+ DC_ERR("DC: failed to create analog stream_encoder %d!\n", i);
+ pool->stream_enc_count++;
+ }
}
pool->hpo_dp_stream_enc_count = 0;
@@ -2303,10 +2345,11 @@ bool resource_is_odm_topology_changed(const struct pipe_ctx *otg_master_a,
static void resource_log_pipe(struct dc *dc, struct pipe_ctx *pipe,
int stream_idx, int slice_idx, int plane_idx, int slice_count,
- bool is_primary)
+ bool is_primary, bool is_phantom_pipe)
{
DC_LOGGER_INIT(dc->ctx->logger);
+ // new format for logging: bit storing code
if (slice_idx == 0 && plane_idx == 0 && is_primary) {
/* case 0 (OTG master pipe with plane) */
DC_LOG_DC(" | plane%d slice%d stream%d|",
@@ -2315,6 +2358,10 @@ static void resource_log_pipe(struct dc *dc, struct pipe_ctx *pipe,
pipe->plane_res.dpp->inst,
pipe->stream_res.opp->inst,
pipe->stream_res.tg->inst);
+ capture_pipe_topology_data(dc, plane_idx, slice_idx, stream_idx,
+ pipe->plane_res.dpp->inst,
+ pipe->stream_res.opp->inst,
+ pipe->stream_res.tg->inst, is_phantom_pipe);
} else if (slice_idx == 0 && plane_idx == -1) {
/* case 1 (OTG master pipe without plane) */
DC_LOG_DC(" | slice%d stream%d|",
@@ -2323,6 +2370,10 @@ static void resource_log_pipe(struct dc *dc, struct pipe_ctx *pipe,
pipe->stream_res.opp->inst,
pipe->stream_res.opp->inst,
pipe->stream_res.tg->inst);
+ capture_pipe_topology_data(dc, 0xF, slice_idx, stream_idx,
+ pipe->plane_res.dpp->inst,
+ pipe->stream_res.opp->inst,
+ pipe->stream_res.tg->inst, is_phantom_pipe);
} else if (slice_idx != 0 && plane_idx == 0 && is_primary) {
/* case 2 (OPP head pipe with plane) */
DC_LOG_DC(" | plane%d slice%d | |",
@@ -2330,27 +2381,43 @@ static void resource_log_pipe(struct dc *dc, struct pipe_ctx *pipe,
DC_LOG_DC(" |DPP%d----OPP%d----| |",
pipe->plane_res.dpp->inst,
pipe->stream_res.opp->inst);
+ capture_pipe_topology_data(dc, plane_idx, slice_idx, stream_idx,
+ pipe->plane_res.dpp->inst,
+ pipe->stream_res.opp->inst,
+ pipe->stream_res.tg->inst, is_phantom_pipe);
} else if (slice_idx != 0 && plane_idx == -1) {
/* case 3 (OPP head pipe without plane) */
DC_LOG_DC(" | slice%d | |", slice_idx);
DC_LOG_DC(" |DPG%d----OPP%d----| |",
pipe->plane_res.dpp->inst,
pipe->stream_res.opp->inst);
+ capture_pipe_topology_data(dc, 0xF, slice_idx, stream_idx,
+ pipe->plane_res.dpp->inst,
+ pipe->stream_res.opp->inst,
+ pipe->stream_res.tg->inst, is_phantom_pipe);
} else if (slice_idx == slice_count - 1) {
/* case 4 (DPP pipe in last slice) */
DC_LOG_DC(" | plane%d | |", plane_idx);
DC_LOG_DC(" |DPP%d----| |",
pipe->plane_res.dpp->inst);
+ capture_pipe_topology_data(dc, plane_idx, slice_idx, stream_idx,
+ pipe->plane_res.dpp->inst,
+ pipe->stream_res.opp->inst,
+ pipe->stream_res.tg->inst, is_phantom_pipe);
} else {
/* case 5 (DPP pipe not in last slice) */
DC_LOG_DC(" | plane%d | | |", plane_idx);
DC_LOG_DC(" |DPP%d----| | |",
pipe->plane_res.dpp->inst);
+ capture_pipe_topology_data(dc, plane_idx, slice_idx, stream_idx,
+ pipe->plane_res.dpp->inst,
+ pipe->stream_res.opp->inst,
+ pipe->stream_res.tg->inst, is_phantom_pipe);
}
}
static void resource_log_pipe_for_stream(struct dc *dc, struct dc_state *state,
- struct pipe_ctx *otg_master, int stream_idx)
+ struct pipe_ctx *otg_master, int stream_idx, bool is_phantom_pipe)
{
struct pipe_ctx *opp_heads[MAX_PIPES];
struct pipe_ctx *dpp_pipes[MAX_PIPES];
@@ -2376,12 +2443,12 @@ static void resource_log_pipe_for_stream(struct dc *dc, struct dc_state *state,
resource_log_pipe(dc, dpp_pipes[dpp_idx],
stream_idx, slice_idx,
plane_idx, slice_count,
- is_primary);
+ is_primary, is_phantom_pipe);
}
} else {
resource_log_pipe(dc, opp_heads[slice_idx],
stream_idx, slice_idx, plane_idx,
- slice_count, true);
+ slice_count, true, is_phantom_pipe);
}
}
@@ -2412,6 +2479,10 @@ void resource_log_pipe_topology_update(struct dc *dc, struct dc_state *state)
struct pipe_ctx *otg_master;
int stream_idx, phantom_stream_idx;
DC_LOGGER_INIT(dc->ctx->logger);
+ bool is_phantom_pipe = false;
+
+ // Start a new snapshot for this topology update
+ start_new_topology_snapshot(dc, state);
DC_LOG_DC(" pipe topology update");
DC_LOG_DC(" ________________________");
@@ -2425,9 +2496,10 @@ void resource_log_pipe_topology_update(struct dc *dc, struct dc_state *state)
if (!otg_master)
continue;
- resource_log_pipe_for_stream(dc, state, otg_master, stream_idx);
+ resource_log_pipe_for_stream(dc, state, otg_master, stream_idx, is_phantom_pipe);
}
if (state->phantom_stream_count > 0) {
+ is_phantom_pipe = true;
DC_LOG_DC(" | (phantom pipes) |");
for (stream_idx = 0; stream_idx < state->stream_count; stream_idx++) {
if (state->stream_status[stream_idx].mall_stream_config.type != SUBVP_MAIN)
@@ -2440,7 +2512,7 @@ void resource_log_pipe_topology_update(struct dc *dc, struct dc_state *state)
if (!otg_master)
continue;
- resource_log_pipe_for_stream(dc, state, otg_master, stream_idx);
+ resource_log_pipe_for_stream(dc, state, otg_master, stream_idx, is_phantom_pipe);
}
}
DC_LOG_DC(" |________________________|\n");
@@ -2690,17 +2762,40 @@ static inline int find_fixed_dio_link_enc(const struct dc_link *link)
}
static inline int find_free_dio_link_enc(const struct resource_context *res_ctx,
- const struct dc_link *link, const struct resource_pool *pool)
+ const struct dc_link *link, const struct resource_pool *pool, struct dc_stream_state *stream)
{
- int i;
+ int i, j = -1;
+ int stream_enc_inst = -1;
int enc_count = pool->dig_link_enc_count;
- /* for dpia, check preferred encoder first and then the next one */
- for (i = 0; i < enc_count; i++)
- if (res_ctx->dio_link_enc_ref_cnts[(link->dpia_preferred_eng_id + i) % enc_count] == 0)
- break;
+ /* Find stream encoder instance for the stream */
+ if (stream) {
+ for (i = 0; i < pool->pipe_count; i++) {
+ if ((res_ctx->pipe_ctx[i].stream == stream) &&
+ (res_ctx->pipe_ctx[i].stream_res.stream_enc != NULL)) {
+ stream_enc_inst = res_ctx->pipe_ctx[i].stream_res.stream_enc->id;
+ break;
+ }
+ }
+ }
+
+ /* Assign dpia preferred > stream enc instance > available */
+ for (i = 0; i < enc_count; i++) {
+ if (res_ctx->dio_link_enc_ref_cnts[i] == 0) {
+ if (j == -1)
+ j = i;
+
+ if (link->dpia_preferred_eng_id == i) {
+ j = i;
+ break;
+ }
- return (i >= 0 && i < enc_count) ? (link->dpia_preferred_eng_id + i) % enc_count : -1;
+ if (stream_enc_inst == i) {
+ j = stream_enc_inst;
+ }
+ }
+ }
+ return j;
}
static inline void acquire_dio_link_enc(
@@ -2781,7 +2876,7 @@ static bool add_dio_link_enc_to_ctx(const struct dc *dc,
retain_dio_link_enc(res_ctx, enc_index);
} else {
if (stream->link->is_dig_mapping_flexible)
- enc_index = find_free_dio_link_enc(res_ctx, stream->link, pool);
+ enc_index = find_free_dio_link_enc(res_ctx, stream->link, pool, stream);
else {
int link_index = 0;
@@ -2791,7 +2886,7 @@ static bool add_dio_link_enc_to_ctx(const struct dc *dc,
* one into the acquiring link.
*/
if (enc_index >= 0 && is_dio_enc_acquired_by_other_link(stream->link, enc_index, &link_index)) {
- int new_enc_index = find_free_dio_link_enc(res_ctx, dc->links[link_index], pool);
+ int new_enc_index = find_free_dio_link_enc(res_ctx, dc->links[link_index], pool, stream);
if (new_enc_index >= 0)
swap_dio_link_enc_to_muxable_ctx(context, pool, new_enc_index, enc_index);
@@ -5201,7 +5296,7 @@ struct link_encoder *get_temp_dio_link_enc(
enc_index = link->eng_id;
if (enc_index < 0)
- enc_index = find_free_dio_link_enc(res_ctx, link, pool);
+ enc_index = find_free_dio_link_enc(res_ctx, link, pool, NULL);
if (enc_index >= 0)
link_enc = pool->link_encoders[enc_index];
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_state.c b/drivers/gpu/drm/amd/display/dc/core/dc_state.c
index c61300a7cb1c..2de8ef4a58ec 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_state.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_state.c
@@ -35,8 +35,8 @@
#include "link_enc_cfg.h"
#if defined(CONFIG_DRM_AMD_DC_FP)
-#include "dml2/dml2_wrapper.h"
-#include "dml2/dml2_internal_types.h"
+#include "dml2_0/dml2_wrapper.h"
+#include "dml2_0/dml2_internal_types.h"
#endif
#define DC_LOGGER \
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
index 9ac2d41f8fca..129cd5f84983 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_stream.c
@@ -224,6 +224,14 @@ struct dc_stream_status *dc_stream_get_status(
return dc_state_get_stream_status(dc->current_state, stream);
}
+const struct dc_stream_status *dc_stream_get_status_const(
+ const struct dc_stream_state *stream)
+{
+ struct dc *dc = stream->ctx->dc;
+
+ return dc_state_get_stream_status(dc->current_state, stream);
+}
+
void program_cursor_attributes(
struct dc *dc,
struct dc_stream_state *stream)
@@ -231,6 +239,7 @@ void program_cursor_attributes(
int i;
struct resource_context *res_ctx;
struct pipe_ctx *pipe_to_program = NULL;
+ bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc);
if (!stream)
return;
@@ -245,9 +254,14 @@ void program_cursor_attributes(
if (!pipe_to_program) {
pipe_to_program = pipe_ctx;
- dc->hwss.cursor_lock(dc, pipe_to_program, true);
- if (pipe_to_program->next_odm_pipe)
- dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, true);
+
+ if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update) {
+ dc->hwss.begin_cursor_offload_update(dc, pipe_ctx);
+ } else {
+ dc->hwss.cursor_lock(dc, pipe_to_program, true);
+ if (pipe_to_program->next_odm_pipe)
+ dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, true);
+ }
}
dc->hwss.set_cursor_attribute(pipe_ctx);
@@ -255,12 +269,18 @@ void program_cursor_attributes(
dc_send_update_cursor_info_to_dmu(pipe_ctx, i);
if (dc->hwss.set_cursor_sdr_white_level)
dc->hwss.set_cursor_sdr_white_level(pipe_ctx);
+ if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe)
+ dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx);
}
if (pipe_to_program) {
- dc->hwss.cursor_lock(dc, pipe_to_program, false);
- if (pipe_to_program->next_odm_pipe)
- dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, false);
+ if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update) {
+ dc->hwss.commit_cursor_offload_update(dc, pipe_to_program);
+ } else {
+ dc->hwss.cursor_lock(dc, pipe_to_program, false);
+ if (pipe_to_program->next_odm_pipe)
+ dc->hwss.cursor_lock(dc, pipe_to_program->next_odm_pipe, false);
+ }
}
}
@@ -366,6 +386,7 @@ void program_cursor_position(
int i;
struct resource_context *res_ctx;
struct pipe_ctx *pipe_to_program = NULL;
+ bool enable_cursor_offload = dc_dmub_srv_is_cursor_offload_enabled(dc);
if (!stream)
return;
@@ -384,16 +405,27 @@ void program_cursor_position(
if (!pipe_to_program) {
pipe_to_program = pipe_ctx;
- dc->hwss.cursor_lock(dc, pipe_to_program, true);
+
+ if (enable_cursor_offload && dc->hwss.begin_cursor_offload_update)
+ dc->hwss.begin_cursor_offload_update(dc, pipe_ctx);
+ else
+ dc->hwss.cursor_lock(dc, pipe_to_program, true);
}
dc->hwss.set_cursor_position(pipe_ctx);
+ if (enable_cursor_offload && dc->hwss.update_cursor_offload_pipe)
+ dc->hwss.update_cursor_offload_pipe(dc, pipe_ctx);
+
if (dc->ctx->dmub_srv)
dc_send_update_cursor_info_to_dmu(pipe_ctx, i);
}
- if (pipe_to_program)
- dc->hwss.cursor_lock(dc, pipe_to_program, false);
+ if (pipe_to_program) {
+ if (enable_cursor_offload && dc->hwss.commit_cursor_offload_update)
+ dc->hwss.commit_cursor_offload_update(dc, pipe_to_program);
+ else
+ dc->hwss.cursor_lock(dc, pipe_to_program, false);
+ }
}
bool dc_stream_set_cursor_position(
@@ -705,9 +737,14 @@ bool dc_stream_get_scanoutpos(const struct dc_stream_state *stream,
{
uint8_t i;
bool ret = false;
- struct dc *dc = stream->ctx->dc;
- struct resource_context *res_ctx =
- &dc->current_state->res_ctx;
+ struct dc *dc;
+ struct resource_context *res_ctx;
+
+ if (!stream->ctx)
+ return false;
+
+ dc = stream->ctx->dc;
+ res_ctx = &dc->current_state->res_ctx;
dc_exit_ips_for_hw_access(dc);
@@ -855,9 +892,11 @@ void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream)
stream->sink->sink_signal != SIGNAL_TYPE_NONE) {
DC_LOG_DC(
- "\tdispname: %s signal: %x\n",
+ "\tsignal: %x dispname: %s manufacturer_id: 0x%x product_id: 0x%x\n",
+ stream->signal,
stream->sink->edid_caps.display_name,
- stream->signal);
+ stream->sink->edid_caps.manufacturer_id,
+ stream->sink->edid_caps.product_id);
}
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 98f0b6b3c213..29edfa51ea2c 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -42,7 +42,7 @@
#include "inc/hw/dmcu.h"
#include "dml/display_mode_lib.h"
-#include "dml2/dml2_wrapper.h"
+#include "dml2_0/dml2_wrapper.h"
#include "dmub/inc/dmub_cmd.h"
@@ -54,8 +54,16 @@ struct abm_save_restore;
struct aux_payload;
struct set_config_cmd_payload;
struct dmub_notification;
+struct dcn_hubbub_reg_state;
+struct dcn_hubp_reg_state;
+struct dcn_dpp_reg_state;
+struct dcn_mpc_reg_state;
+struct dcn_opp_reg_state;
+struct dcn_dsc_reg_state;
+struct dcn_optc_reg_state;
+struct dcn_dccg_reg_state;
-#define DC_VER "3.2.351"
+#define DC_VER "3.2.359"
/**
* MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC
@@ -278,6 +286,15 @@ struct dc_scl_caps {
bool sharpener_support;
};
+struct dc_check_config {
+ /**
+ * max video plane width that can be safely assumed to be always
+ * supported by single DPP pipe.
+ */
+ unsigned int max_optimizable_video_width;
+ bool enable_legacy_fast_update;
+};
+
struct dc_caps {
uint32_t max_streams;
uint32_t max_links;
@@ -293,11 +310,6 @@ struct dc_caps {
unsigned int max_cursor_size;
unsigned int max_buffered_cursor_size;
unsigned int max_video_width;
- /*
- * max video plane width that can be safely assumed to be always
- * supported by single DPP pipe.
- */
- unsigned int max_optimizable_video_width;
unsigned int min_horizontal_blanking_period;
int linear_pitch_alignment;
bool dcc_const_color;
@@ -455,6 +467,18 @@ enum surface_update_type {
UPDATE_TYPE_FULL, /* may need to shuffle resources */
};
+enum dc_lock_descriptor {
+ LOCK_DESCRIPTOR_NONE = 0x0,
+ LOCK_DESCRIPTOR_STREAM = 0x1,
+ LOCK_DESCRIPTOR_LINK = 0x2,
+ LOCK_DESCRIPTOR_GLOBAL = 0x4,
+};
+
+struct surface_update_descriptor {
+ enum surface_update_type update_type;
+ enum dc_lock_descriptor lock_descriptor;
+};
+
/* Forward declaration*/
struct dc;
struct dc_plane_state;
@@ -530,6 +554,7 @@ struct dc_config {
bool set_pipe_unlock_order;
bool enable_dpia_pre_training;
bool unify_link_enc_assignment;
+ bool enable_cursor_offload;
struct spl_sharpness_range dcn_sharpness_range;
struct spl_sharpness_range dcn_override_sharpness_range;
};
@@ -849,8 +874,7 @@ union dpia_debug_options {
uint32_t enable_force_tbt3_work_around:1; /* bit 4 */
uint32_t disable_usb4_pm_support:1; /* bit 5 */
uint32_t enable_usb4_bw_zero_alloc_patch:1; /* bit 6 */
- uint32_t enable_bw_allocation_mode:1; /* bit 7 */
- uint32_t reserved:24;
+ uint32_t reserved:25;
} bits;
uint32_t raw;
};
@@ -875,6 +899,7 @@ struct dc_debug_data {
uint32_t ltFailCount;
uint32_t i2cErrorCount;
uint32_t auxErrorCount;
+ struct pipe_topology_history topology_history;
};
struct dc_phy_addr_space_config {
@@ -1120,7 +1145,6 @@ struct dc_debug_options {
uint32_t fpo_vactive_min_active_margin_us;
uint32_t fpo_vactive_max_blank_us;
bool enable_hpo_pg_support;
- bool enable_legacy_fast_update;
bool disable_dc_mode_overwrite;
bool replay_skip_crtc_disabled;
bool ignore_pg;/*do nothing, let pmfw control it*/
@@ -1152,7 +1176,6 @@ struct dc_debug_options {
bool enable_ips_visual_confirm;
unsigned int sharpen_policy;
unsigned int scale_to_sharpness_policy;
- bool skip_full_updated_if_possible;
unsigned int enable_oled_edp_power_up_opt;
bool enable_hblank_borrow;
bool force_subvp_df_throttle;
@@ -1164,6 +1187,7 @@ struct dc_debug_options {
unsigned int auxless_alpm_lfps_t1t2_us;
short auxless_alpm_lfps_t1t2_offset_us;
bool disable_stutter_for_wm_program;
+ bool enable_block_sequence_programming;
};
@@ -1702,6 +1726,7 @@ struct dc {
struct dc_debug_options debug;
struct dc_versions versions;
struct dc_caps caps;
+ struct dc_check_config check_config;
struct dc_cap_funcs cap_funcs;
struct dc_config config;
struct dc_bounding_box_overrides bb_overrides;
@@ -1830,20 +1855,26 @@ struct dc_surface_update {
};
struct dc_underflow_debug_data {
- uint32_t otg_inst;
- uint32_t otg_underflow;
- uint32_t h_position;
- uint32_t v_position;
- uint32_t otg_frame_count;
- struct dc_underflow_per_hubp_debug_data {
- uint32_t hubp_underflow;
- uint32_t hubp_in_blank;
- uint32_t hubp_readline;
- uint32_t det_config_error;
- } hubps[MAX_PIPES];
- uint32_t curr_det_sizes[MAX_PIPES];
- uint32_t target_det_sizes[MAX_PIPES];
- uint32_t compbuf_config_error;
+ struct dcn_hubbub_reg_state *hubbub_reg_state;
+ struct dcn_hubp_reg_state *hubp_reg_state[MAX_PIPES];
+ struct dcn_dpp_reg_state *dpp_reg_state[MAX_PIPES];
+ struct dcn_mpc_reg_state *mpc_reg_state[MAX_PIPES];
+ struct dcn_opp_reg_state *opp_reg_state[MAX_PIPES];
+ struct dcn_dsc_reg_state *dsc_reg_state[MAX_PIPES];
+ struct dcn_optc_reg_state *optc_reg_state[MAX_PIPES];
+ struct dcn_dccg_reg_state *dccg_reg_state[MAX_PIPES];
+};
+
+struct power_features {
+ bool ips;
+ bool rcg;
+ bool replay;
+ bool dds;
+ bool sprs;
+ bool psr;
+ bool fams;
+ bool mpo;
+ bool uclk_p_state;
};
/*
@@ -2688,6 +2719,13 @@ bool dc_process_dmub_aux_transfer_async(struct dc *dc,
uint32_t link_index,
struct aux_payload *payload);
+/*
+ * smart power OLED Interfaces
+ */
+bool dc_smart_power_oled_enable(const struct dc_link *link, bool enable, uint16_t peak_nits,
+ uint8_t debug_control, uint16_t fixed_CLL, uint32_t triggerline);
+bool dc_smart_power_oled_get_max_cll(const struct dc_link *link, unsigned int *pCurrent_MaxCLL);
+
/* Get dc link index from dpia port index */
uint8_t get_link_index_from_dpia_port_index(const struct dc *dc,
uint8_t dpia_port_index);
@@ -2721,6 +2759,8 @@ unsigned int dc_get_det_buffer_size_from_state(const struct dc_state *context);
bool dc_get_host_router_index(const struct dc_link *link, unsigned int *host_router_index);
+void dc_log_preos_dmcub_info(const struct dc *dc);
+
/* DSC Interfaces */
#include "dc_dsc.h"
@@ -2736,7 +2776,7 @@ bool dc_is_timing_changed(struct dc_stream_state *cur_stream,
struct dc_stream_state *new_stream);
bool dc_is_cursor_limit_pending(struct dc *dc);
-bool dc_can_clear_cursor_limit(struct dc *dc);
+bool dc_can_clear_cursor_limit(const struct dc *dc);
/**
* dc_get_underflow_debug_data_for_otg() - Retrieve underflow debug data.
@@ -2751,4 +2791,493 @@ bool dc_can_clear_cursor_limit(struct dc *dc);
*/
void dc_get_underflow_debug_data_for_otg(struct dc *dc, int primary_otg_inst, struct dc_underflow_debug_data *out_data);
+void dc_get_power_feature_status(struct dc *dc, int primary_otg_inst, struct power_features *out_data);
+
+/**
+ * Software state variables used to program register fields across the display pipeline
+ */
+struct dc_register_software_state {
+ /* HUBP register programming variables for each pipe */
+ struct {
+ bool valid_plane_state;
+ bool valid_stream;
+ bool min_dc_gfx_version9;
+ uint32_t vtg_sel; /* DCHUBP_CNTL->HUBP_VTG_SEL from pipe_ctx->stream_res.tg->inst */
+ uint32_t hubp_clock_enable; /* HUBP_CLK_CNTL->HUBP_CLOCK_ENABLE from power management */
+ uint32_t surface_pixel_format; /* DCSURF_SURFACE_CONFIG->SURFACE_PIXEL_FORMAT from plane_state->format */
+ uint32_t rotation_angle; /* DCSURF_SURFACE_CONFIG->ROTATION_ANGLE from plane_state->rotation */
+ uint32_t h_mirror_en; /* DCSURF_SURFACE_CONFIG->H_MIRROR_EN from plane_state->horizontal_mirror */
+ uint32_t surface_dcc_en; /* DCSURF_SURFACE_CONTROL->PRIMARY_SURFACE_DCC_EN from dcc->enable */
+ uint32_t surface_size_width; /* HUBP_SIZE->SURFACE_SIZE_WIDTH from plane_size.surface_size.width */
+ uint32_t surface_size_height; /* HUBP_SIZE->SURFACE_SIZE_HEIGHT from plane_size.surface_size.height */
+ uint32_t pri_viewport_width; /* DCSURF_PRI_VIEWPORT_DIMENSION->PRI_VIEWPORT_WIDTH from scaler_data.viewport.width */
+ uint32_t pri_viewport_height; /* DCSURF_PRI_VIEWPORT_DIMENSION->PRI_VIEWPORT_HEIGHT from scaler_data.viewport.height */
+ uint32_t pri_viewport_x_start; /* DCSURF_PRI_VIEWPORT_START->PRI_VIEWPORT_X_START from scaler_data.viewport.x */
+ uint32_t pri_viewport_y_start; /* DCSURF_PRI_VIEWPORT_START->PRI_VIEWPORT_Y_START from scaler_data.viewport.y */
+ uint32_t cursor_enable; /* CURSOR_CONTROL->CURSOR_ENABLE from cursor_attributes.enable */
+ uint32_t cursor_width; /* CURSOR_SETTINGS->CURSOR_WIDTH from cursor_position.width */
+ uint32_t cursor_height; /* CURSOR_SETTINGS->CURSOR_HEIGHT from cursor_position.height */
+
+ /* Additional DCC configuration */
+ uint32_t surface_dcc_ind_64b_blk; /* DCSURF_SURFACE_CONTROL->PRIMARY_SURFACE_DCC_IND_64B_BLK from dcc.independent_64b_blks */
+ uint32_t surface_dcc_ind_128b_blk; /* DCSURF_SURFACE_CONTROL->PRIMARY_SURFACE_DCC_IND_128B_BLK from dcc.independent_128b_blks */
+
+ /* Surface pitch configuration */
+ uint32_t surface_pitch; /* DCSURF_SURFACE_PITCH->PITCH from plane_size.surface_pitch */
+ uint32_t meta_pitch; /* DCSURF_SURFACE_PITCH->META_PITCH from dcc.meta_pitch */
+ uint32_t chroma_pitch; /* DCSURF_SURFACE_PITCH_C->PITCH_C from plane_size.chroma_pitch */
+ uint32_t meta_pitch_c; /* DCSURF_SURFACE_PITCH_C->META_PITCH_C from dcc.meta_pitch_c */
+
+ /* Surface addresses */
+ uint32_t primary_surface_address_low; /* DCSURF_PRIMARY_SURFACE_ADDRESS->PRIMARY_SURFACE_ADDRESS from address.grph.addr.low_part */
+ uint32_t primary_surface_address_high; /* DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH->PRIMARY_SURFACE_ADDRESS_HIGH from address.grph.addr.high_part */
+ uint32_t primary_meta_surface_address_low; /* DCSURF_PRIMARY_META_SURFACE_ADDRESS->PRIMARY_META_SURFACE_ADDRESS from address.grph.meta_addr.low_part */
+ uint32_t primary_meta_surface_address_high; /* DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH->PRIMARY_META_SURFACE_ADDRESS_HIGH from address.grph.meta_addr.high_part */
+
+ /* TMZ configuration */
+ uint32_t primary_surface_tmz; /* DCSURF_SURFACE_CONTROL->PRIMARY_SURFACE_TMZ from address.tmz_surface */
+ uint32_t primary_meta_surface_tmz; /* DCSURF_SURFACE_CONTROL->PRIMARY_META_SURFACE_TMZ from address.tmz_surface */
+
+ /* Tiling configuration */
+ uint32_t sw_mode; /* DCSURF_TILING_CONFIG->SW_MODE from tiling_info.gfx9.swizzle */
+ uint32_t num_pipes; /* DCSURF_ADDR_CONFIG->NUM_PIPES from tiling_info.gfx9.num_pipes */
+ uint32_t num_banks; /* DCSURF_ADDR_CONFIG->NUM_BANKS from tiling_info.gfx9.num_banks */
+ uint32_t pipe_interleave; /* DCSURF_ADDR_CONFIG->PIPE_INTERLEAVE from tiling_info.gfx9.pipe_interleave */
+ uint32_t num_shader_engines; /* DCSURF_ADDR_CONFIG->NUM_SE from tiling_info.gfx9.num_shader_engines */
+ uint32_t num_rb_per_se; /* DCSURF_ADDR_CONFIG->NUM_RB_PER_SE from tiling_info.gfx9.num_rb_per_se */
+ uint32_t num_pkrs; /* DCSURF_ADDR_CONFIG->NUM_PKRS from tiling_info.gfx9.num_pkrs */
+
+ /* DML Request Size Configuration - Luma */
+ uint32_t rq_chunk_size; /* DCHUBP_REQ_SIZE_CONFIG->CHUNK_SIZE from rq_regs.rq_regs_l.chunk_size */
+ uint32_t rq_min_chunk_size; /* DCHUBP_REQ_SIZE_CONFIG->MIN_CHUNK_SIZE from rq_regs.rq_regs_l.min_chunk_size */
+ uint32_t rq_meta_chunk_size; /* DCHUBP_REQ_SIZE_CONFIG->META_CHUNK_SIZE from rq_regs.rq_regs_l.meta_chunk_size */
+ uint32_t rq_min_meta_chunk_size; /* DCHUBP_REQ_SIZE_CONFIG->MIN_META_CHUNK_SIZE from rq_regs.rq_regs_l.min_meta_chunk_size */
+ uint32_t rq_dpte_group_size; /* DCHUBP_REQ_SIZE_CONFIG->DPTE_GROUP_SIZE from rq_regs.rq_regs_l.dpte_group_size */
+ uint32_t rq_mpte_group_size; /* DCHUBP_REQ_SIZE_CONFIG->MPTE_GROUP_SIZE from rq_regs.rq_regs_l.mpte_group_size */
+ uint32_t rq_swath_height_l; /* DCHUBP_REQ_SIZE_CONFIG->SWATH_HEIGHT_L from rq_regs.rq_regs_l.swath_height */
+ uint32_t rq_pte_row_height_l; /* DCHUBP_REQ_SIZE_CONFIG->PTE_ROW_HEIGHT_L from rq_regs.rq_regs_l.pte_row_height */
+
+ /* DML Request Size Configuration - Chroma */
+ uint32_t rq_chunk_size_c; /* DCHUBP_REQ_SIZE_CONFIG_C->CHUNK_SIZE_C from rq_regs.rq_regs_c.chunk_size */
+ uint32_t rq_min_chunk_size_c; /* DCHUBP_REQ_SIZE_CONFIG_C->MIN_CHUNK_SIZE_C from rq_regs.rq_regs_c.min_chunk_size */
+ uint32_t rq_meta_chunk_size_c; /* DCHUBP_REQ_SIZE_CONFIG_C->META_CHUNK_SIZE_C from rq_regs.rq_regs_c.meta_chunk_size */
+ uint32_t rq_min_meta_chunk_size_c; /* DCHUBP_REQ_SIZE_CONFIG_C->MIN_META_CHUNK_SIZE_C from rq_regs.rq_regs_c.min_meta_chunk_size */
+ uint32_t rq_dpte_group_size_c; /* DCHUBP_REQ_SIZE_CONFIG_C->DPTE_GROUP_SIZE_C from rq_regs.rq_regs_c.dpte_group_size */
+ uint32_t rq_mpte_group_size_c; /* DCHUBP_REQ_SIZE_CONFIG_C->MPTE_GROUP_SIZE_C from rq_regs.rq_regs_c.mpte_group_size */
+ uint32_t rq_swath_height_c; /* DCHUBP_REQ_SIZE_CONFIG_C->SWATH_HEIGHT_C from rq_regs.rq_regs_c.swath_height */
+ uint32_t rq_pte_row_height_c; /* DCHUBP_REQ_SIZE_CONFIG_C->PTE_ROW_HEIGHT_C from rq_regs.rq_regs_c.pte_row_height */
+
+ /* DML Expansion Modes */
+ uint32_t drq_expansion_mode; /* DCN_EXPANSION_MODE->DRQ_EXPANSION_MODE from rq_regs.drq_expansion_mode */
+ uint32_t prq_expansion_mode; /* DCN_EXPANSION_MODE->PRQ_EXPANSION_MODE from rq_regs.prq_expansion_mode */
+ uint32_t mrq_expansion_mode; /* DCN_EXPANSION_MODE->MRQ_EXPANSION_MODE from rq_regs.mrq_expansion_mode */
+ uint32_t crq_expansion_mode; /* DCN_EXPANSION_MODE->CRQ_EXPANSION_MODE from rq_regs.crq_expansion_mode */
+
+ /* DML DLG parameters - nominal */
+ uint32_t dst_y_per_vm_vblank; /* NOM_PARAMETERS_0->DST_Y_PER_VM_VBLANK from dlg_regs.dst_y_per_vm_vblank */
+ uint32_t dst_y_per_row_vblank; /* NOM_PARAMETERS_0->DST_Y_PER_ROW_VBLANK from dlg_regs.dst_y_per_row_vblank */
+ uint32_t dst_y_per_vm_flip; /* NOM_PARAMETERS_1->DST_Y_PER_VM_FLIP from dlg_regs.dst_y_per_vm_flip */
+ uint32_t dst_y_per_row_flip; /* NOM_PARAMETERS_1->DST_Y_PER_ROW_FLIP from dlg_regs.dst_y_per_row_flip */
+
+ /* DML prefetch settings */
+ uint32_t dst_y_prefetch; /* PREFETCH_SETTINS->DST_Y_PREFETCH from dlg_regs.dst_y_prefetch */
+ uint32_t vratio_prefetch; /* PREFETCH_SETTINS->VRATIO_PREFETCH from dlg_regs.vratio_prefetch */
+ uint32_t vratio_prefetch_c; /* PREFETCH_SETTINS_C->VRATIO_PREFETCH_C from dlg_regs.vratio_prefetch_c */
+
+ /* TTU parameters */
+ uint32_t qos_level_low_wm; /* TTU_CNTL1->QoSLevelLowWaterMark from ttu_regs.qos_level_low_wm */
+ uint32_t qos_level_high_wm; /* TTU_CNTL1->QoSLevelHighWaterMark from ttu_regs.qos_level_high_wm */
+ uint32_t qos_level_flip; /* TTU_CNTL2->QoS_LEVEL_FLIP_L from ttu_regs.qos_level_flip */
+ uint32_t min_ttu_vblank; /* DCN_GLOBAL_TTU_CNTL->MIN_TTU_VBLANK from ttu_regs.min_ttu_vblank */
+ } hubp[MAX_PIPES];
+
+ /* HUBBUB register programming variables */
+ struct {
+ /* Individual DET buffer control per pipe - software state that programs DET registers */
+ uint32_t det0_size; /* DCHUBBUB_DET0_CTRL->DET0_SIZE from hubbub->funcs->program_det_size(hubbub, 0, det_buffer_size_kb) */
+ uint32_t det1_size; /* DCHUBBUB_DET1_CTRL->DET1_SIZE from hubbub->funcs->program_det_size(hubbub, 1, det_buffer_size_kb) */
+ uint32_t det2_size; /* DCHUBBUB_DET2_CTRL->DET2_SIZE from hubbub->funcs->program_det_size(hubbub, 2, det_buffer_size_kb) */
+ uint32_t det3_size; /* DCHUBBUB_DET3_CTRL->DET3_SIZE from hubbub->funcs->program_det_size(hubbub, 3, det_buffer_size_kb) */
+
+ /* Compression buffer control - software state that programs COMPBUF registers */
+ uint32_t compbuf_size; /* DCHUBBUB_COMPBUF_CTRL->COMPBUF_SIZE from hubbub->funcs->program_compbuf_size(hubbub, compbuf_size_kb, safe_to_increase) */
+ uint32_t compbuf_reserved_space_64b; /* COMPBUF_RESERVED_SPACE->COMPBUF_RESERVED_SPACE_64B from hubbub2->pixel_chunk_size / 32 */
+ uint32_t compbuf_reserved_space_zs; /* COMPBUF_RESERVED_SPACE->COMPBUF_RESERVED_SPACE_ZS from hubbub2->pixel_chunk_size / 128 */
+ } hubbub;
+
+ /* DPP register programming variables for each pipe (simplified for available fields) */
+ struct {
+ uint32_t dpp_clock_enable; /* DPP_CONTROL->DPP_CLOCK_ENABLE from dppclk_enable */
+
+ /* Recout (Rectangle of Interest) configuration */
+ uint32_t recout_start_x; /* RECOUT_START->RECOUT_START_X from pipe_ctx->plane_res.scl_data.recout.x */
+ uint32_t recout_start_y; /* RECOUT_START->RECOUT_START_Y from pipe_ctx->plane_res.scl_data.recout.y */
+ uint32_t recout_width; /* RECOUT_SIZE->RECOUT_WIDTH from pipe_ctx->plane_res.scl_data.recout.width */
+ uint32_t recout_height; /* RECOUT_SIZE->RECOUT_HEIGHT from pipe_ctx->plane_res.scl_data.recout.height */
+
+ /* MPC (Multiple Pipe/Plane Combiner) size configuration */
+ uint32_t mpc_width; /* MPC_SIZE->MPC_WIDTH from pipe_ctx->plane_res.scl_data.h_active */
+ uint32_t mpc_height; /* MPC_SIZE->MPC_HEIGHT from pipe_ctx->plane_res.scl_data.v_active */
+
+ /* DSCL mode configuration */
+ uint32_t dscl_mode; /* SCL_MODE->DSCL_MODE from pipe_ctx->plane_res.scl_data.dscl_prog_data.dscl_mode */
+
+ /* Scaler ratios (simplified to integer parts) */
+ uint32_t horz_ratio_int; /* SCL_HORZ_FILTER_SCALE_RATIO->SCL_H_SCALE_RATIO integer part from ratios.horz */
+ uint32_t vert_ratio_int; /* SCL_VERT_FILTER_SCALE_RATIO->SCL_V_SCALE_RATIO integer part from ratios.vert */
+
+ /* Basic scaler taps */
+ uint32_t h_taps; /* SCL_TAP_CONTROL->SCL_H_NUM_TAPS from taps.h_taps */
+ uint32_t v_taps; /* SCL_TAP_CONTROL->SCL_V_NUM_TAPS from taps.v_taps */
+ } dpp[MAX_PIPES];
+
+ /* DCCG register programming variables */
+ struct {
+ /* Core Display Clock Control */
+ uint32_t dispclk_khz; /* DENTIST_DISPCLK_CNTL->DENTIST_DISPCLK_WDIVIDER from clk_mgr.dispclk_khz */
+ uint32_t dc_mem_global_pwr_req_dis; /* DC_MEM_GLOBAL_PWR_REQ_CNTL->DC_MEM_GLOBAL_PWR_REQ_DIS from memory power management settings */
+
+ /* DPP Clock Control - 4 fields per pipe */
+ uint32_t dppclk_khz[MAX_PIPES]; /* DPPCLK_CTRL->DPPCLK_R_GATE_DISABLE from dpp_clocks[pipe] */
+ uint32_t dppclk_enable[MAX_PIPES]; /* DPPCLK_CTRL->DPPCLK0_EN,DPPCLK1_EN,DPPCLK2_EN,DPPCLK3_EN from dccg31_update_dpp_dto() */
+ uint32_t dppclk_dto_enable[MAX_PIPES]; /* DPPCLK_DTO_CTRL->DPPCLK_DTO_ENABLE from dccg->dpp_clock_gated[dpp_inst] state */
+ uint32_t dppclk_dto_phase[MAX_PIPES]; /* DPPCLK0_DTO_PARAM->DPPCLK0_DTO_PHASE from phase calculation req_dppclk/ref_dppclk */
+ uint32_t dppclk_dto_modulo[MAX_PIPES]; /* DPPCLK0_DTO_PARAM->DPPCLK0_DTO_MODULO from modulo = 0xff */
+
+ /* DSC Clock Control - 4 fields per DSC resource */
+ uint32_t dscclk_khz[MAX_PIPES]; /* DSCCLK_DTO_CTRL->DSCCLK_DTO_ENABLE from dsc_clocks */
+ uint32_t dscclk_dto_enable[MAX_PIPES]; /* DSCCLK_DTO_CTRL->DSCCLK0_DTO_ENABLE,DSCCLK1_DTO_ENABLE,DSCCLK2_DTO_ENABLE,DSCCLK3_DTO_ENABLE */
+ uint32_t dscclk_dto_phase[MAX_PIPES]; /* DSCCLK0_DTO_PARAM->DSCCLK0_DTO_PHASE from dccg31_enable_dscclk() */
+ uint32_t dscclk_dto_modulo[MAX_PIPES]; /* DSCCLK0_DTO_PARAM->DSCCLK0_DTO_MODULO from dccg31_enable_dscclk() */
+
+ /* Pixel Clock Control - per pipe */
+ uint32_t pixclk_khz[MAX_PIPES]; /* PIXCLK_RESYNC_CNTL->PIXCLK_RESYNC_ENABLE from stream.timing.pix_clk_100hz */
+ uint32_t otg_pixel_rate_div[MAX_PIPES]; /* OTG_PIXEL_RATE_DIV->OTG_PIXEL_RATE_DIV from OTG pixel rate divider control */
+ uint32_t dtbclk_dto_enable[MAX_PIPES]; /* OTG0_PIXEL_RATE_CNTL->DTBCLK_DTO_ENABLE from dccg31_set_dtbclk_dto() */
+ uint32_t pipe_dto_src_sel[MAX_PIPES]; /* OTG0_PIXEL_RATE_CNTL->PIPE_DTO_SRC_SEL from dccg31_set_dtbclk_dto() source selection */
+ uint32_t dtbclk_dto_div[MAX_PIPES]; /* OTG0_PIXEL_RATE_CNTL->DTBCLK_DTO_DIV from dtbdto_div calculation */
+ uint32_t otg_add_pixel[MAX_PIPES]; /* OTG0_PIXEL_RATE_CNTL->OTG_ADD_PIXEL from dccg31_otg_add_pixel() */
+ uint32_t otg_drop_pixel[MAX_PIPES]; /* OTG0_PIXEL_RATE_CNTL->OTG_DROP_PIXEL from dccg31_otg_drop_pixel() */
+
+ /* DTBCLK DTO Control - 4 DTOs */
+ uint32_t dtbclk_dto_modulo[4]; /* DTBCLK_DTO0_MODULO->DTBCLK_DTO0_MODULO from dccg31_set_dtbclk_dto() modulo calculation */
+ uint32_t dtbclk_dto_phase[4]; /* DTBCLK_DTO0_PHASE->DTBCLK_DTO0_PHASE from phase calculation pixclk_khz/ref_dtbclk_khz */
+ uint32_t dtbclk_dto_dbuf_en; /* DTBCLK_DTO_DBUF_EN->DTBCLK DTO data buffer enable */
+
+ /* DP Stream Clock Control - 4 pipes */
+ uint32_t dpstreamclk_enable[MAX_PIPES]; /* DPSTREAMCLK_CNTL->DPSTREAMCLK_PIPE0_EN,DPSTREAMCLK_PIPE1_EN,DPSTREAMCLK_PIPE2_EN,DPSTREAMCLK_PIPE3_EN */
+ uint32_t dp_dto_modulo[4]; /* DP_DTO0_MODULO->DP_DTO0_MODULO from DP stream DTO programming */
+ uint32_t dp_dto_phase[4]; /* DP_DTO0_PHASE->DP_DTO0_PHASE from DP stream DTO programming */
+ uint32_t dp_dto_dbuf_en; /* DP_DTO_DBUF_EN->DP DTO data buffer enable */
+
+ /* PHY Symbol Clock Control - 5 PHYs (A,B,C,D,E) */
+ uint32_t phy_symclk_force_en[5]; /* PHYASYMCLK_CLOCK_CNTL->PHYASYMCLK_FORCE_EN from dccg31_set_physymclk() force_enable */
+ uint32_t phy_symclk_force_src_sel[5]; /* PHYASYMCLK_CLOCK_CNTL->PHYASYMCLK_FORCE_SRC_SEL from dccg31_set_physymclk() clk_src */
+ uint32_t phy_symclk_gate_disable[5]; /* DCCG_GATE_DISABLE_CNTL2->PHYASYMCLK_GATE_DISABLE from debug.root_clock_optimization.bits.physymclk */
+
+ /* SYMCLK32 SE Control - 4 instances */
+ uint32_t symclk32_se_src_sel[4]; /* SYMCLK32_SE_CNTL->SYMCLK32_SE0_SRC_SEL from dccg31_enable_symclk32_se() with get_phy_mux_symclk() mapping */
+ uint32_t symclk32_se_enable[4]; /* SYMCLK32_SE_CNTL->SYMCLK32_SE0_EN from dccg31_enable_symclk32_se() enable */
+ uint32_t symclk32_se_gate_disable[4]; /* DCCG_GATE_DISABLE_CNTL3->SYMCLK32_SE0_GATE_DISABLE from debug.root_clock_optimization.bits.symclk32_se */
+
+ /* SYMCLK32 LE Control - 2 instances */
+ uint32_t symclk32_le_src_sel[2]; /* SYMCLK32_LE_CNTL->SYMCLK32_LE0_SRC_SEL from dccg31_enable_symclk32_le() phyd32clk source */
+ uint32_t symclk32_le_enable[2]; /* SYMCLK32_LE_CNTL->SYMCLK32_LE0_EN from dccg31_enable_symclk32_le() enable */
+ uint32_t symclk32_le_gate_disable[2]; /* DCCG_GATE_DISABLE_CNTL3->SYMCLK32_LE0_GATE_DISABLE from debug.root_clock_optimization.bits.symclk32_le */
+
+ /* DPIA Clock Control */
+ uint32_t dpiaclk_540m_dto_modulo; /* DPIACLK_540M_DTO_MODULO->DPIA 540MHz DTO modulo */
+ uint32_t dpiaclk_540m_dto_phase; /* DPIACLK_540M_DTO_PHASE->DPIA 540MHz DTO phase */
+ uint32_t dpiaclk_810m_dto_modulo; /* DPIACLK_810M_DTO_MODULO->DPIA 810MHz DTO modulo */
+ uint32_t dpiaclk_810m_dto_phase; /* DPIACLK_810M_DTO_PHASE->DPIA 810MHz DTO phase */
+ uint32_t dpiaclk_dto_cntl; /* DPIACLK_DTO_CNTL->DPIA clock DTO control */
+ uint32_t dpiasymclk_cntl; /* DPIASYMCLK_CNTL->DPIA symbol clock control */
+
+ /* Clock Gating Control */
+ uint32_t dccg_gate_disable_cntl; /* DCCG_GATE_DISABLE_CNTL->Clock gate disable control from dccg31_init() */
+ uint32_t dpstreamclk_gate_disable; /* DCCG_GATE_DISABLE_CNTL3->DPSTREAMCLK_GATE_DISABLE from debug.root_clock_optimization.bits.dpstream */
+ uint32_t dpstreamclk_root_gate_disable; /* DCCG_GATE_DISABLE_CNTL3->DPSTREAMCLK_ROOT_GATE_DISABLE from debug.root_clock_optimization.bits.dpstream */
+
+ /* VSync Control */
+ uint32_t vsync_cnt_ctrl; /* DCCG_VSYNC_CNT_CTRL->VSync counter control */
+ uint32_t vsync_cnt_int_ctrl; /* DCCG_VSYNC_CNT_INT_CTRL->VSync counter interrupt control */
+ uint32_t vsync_otg_latch_value[6]; /* DCCG_VSYNC_OTG0_LATCH_VALUE->OTG0 VSync latch value (for OTG0-5) */
+
+ /* Time Base Control */
+ uint32_t microsecond_time_base_div; /* MICROSECOND_TIME_BASE_DIV->Microsecond time base divider */
+ uint32_t millisecond_time_base_div; /* MILLISECOND_TIME_BASE_DIV->Millisecond time base divider */
+ } dccg;
+
+ /* DSC essential configuration for underflow analysis */
+ struct {
+ /* DSC active state - critical for bandwidth analysis */
+ uint32_t dsc_clock_enable; /* DSC enabled - affects bandwidth requirements */
+
+ /* DSC configuration affecting bandwidth and timing */
+ uint32_t dsc_num_slices_h; /* Horizontal slice count - affects throughput */
+ uint32_t dsc_num_slices_v; /* Vertical slice count - affects throughput */
+ uint32_t dsc_bits_per_pixel; /* Compression ratio - affects bandwidth */
+
+ /* OPP integration - affects pipeline flow */
+ uint32_t dscrm_dsc_forward_enable; /* DSC forwarding to OPP enabled */
+ uint32_t dscrm_dsc_opp_pipe_source; /* Which OPP receives DSC output */
+ } dsc[MAX_PIPES];
+
+ /* MPC register programming variables */
+ struct {
+ /* MPCC blending tree and mode control */
+ uint32_t mpcc_mode[MAX_PIPES]; /* MPCC_CONTROL->MPCC_MODE from blend_cfg.blend_mode */
+ uint32_t mpcc_alpha_blend_mode[MAX_PIPES]; /* MPCC_CONTROL->MPCC_ALPHA_BLND_MODE from blend_cfg.alpha_mode */
+ uint32_t mpcc_alpha_multiplied_mode[MAX_PIPES]; /* MPCC_CONTROL->MPCC_ALPHA_MULTIPLIED_MODE from blend_cfg.pre_multiplied_alpha */
+ uint32_t mpcc_blnd_active_overlap_only[MAX_PIPES]; /* MPCC_CONTROL->MPCC_BLND_ACTIVE_OVERLAP_ONLY from blend_cfg.overlap_only */
+ uint32_t mpcc_global_alpha[MAX_PIPES]; /* MPCC_CONTROL->MPCC_GLOBAL_ALPHA from blend_cfg.global_alpha */
+ uint32_t mpcc_global_gain[MAX_PIPES]; /* MPCC_CONTROL->MPCC_GLOBAL_GAIN from blend_cfg.global_gain */
+ uint32_t mpcc_bg_bpc[MAX_PIPES]; /* MPCC_CONTROL->MPCC_BG_BPC from background color depth */
+ uint32_t mpcc_bot_gain_mode[MAX_PIPES]; /* MPCC_CONTROL->MPCC_BOT_GAIN_MODE from bottom layer gain control */
+
+ /* MPCC blending tree connections */
+ uint32_t mpcc_bot_sel[MAX_PIPES]; /* MPCC_BOT_SEL->MPCC_BOT_SEL from mpcc_state->bot_sel */
+ uint32_t mpcc_top_sel[MAX_PIPES]; /* MPCC_TOP_SEL->MPCC_TOP_SEL from mpcc_state->dpp_id */
+
+ /* MPCC output gamma control */
+ uint32_t mpcc_ogam_mode[MAX_PIPES]; /* MPCC_OGAM_CONTROL->MPCC_OGAM_MODE from output gamma mode */
+ uint32_t mpcc_ogam_select[MAX_PIPES]; /* MPCC_OGAM_CONTROL->MPCC_OGAM_SELECT from gamma LUT bank selection */
+ uint32_t mpcc_ogam_pwl_disable[MAX_PIPES]; /* MPCC_OGAM_CONTROL->MPCC_OGAM_PWL_DISABLE from PWL control */
+
+ /* MPCC pipe assignment and status */
+ uint32_t mpcc_opp_id[MAX_PIPES]; /* MPCC_OPP_ID->MPCC_OPP_ID from mpcc_state->opp_id */
+ uint32_t mpcc_idle[MAX_PIPES]; /* MPCC_STATUS->MPCC_IDLE from mpcc idle status */
+ uint32_t mpcc_busy[MAX_PIPES]; /* MPCC_STATUS->MPCC_BUSY from mpcc busy status */
+
+ /* MPC output processing */
+ uint32_t mpc_out_csc_mode; /* MPC_OUT_CSC_COEF->MPC_OUT_CSC_MODE from output_csc */
+ uint32_t mpc_out_gamma_mode; /* MPC_OUT_GAMMA_LUT->MPC_OUT_GAMMA_MODE from output_gamma */
+ } mpc;
+
+ /* OPP register programming variables for each pipe */
+ struct {
+ /* Display Pattern Generator (DPG) Control - 19 fields from DPG_CONTROL register */
+ uint32_t dpg_enable; /* DPG_CONTROL->DPG_EN from test_pattern parameter (enable/disable) */
+
+ /* Format Control (FMT) - 18 fields from FMT_CONTROL register */
+ uint32_t fmt_pixel_encoding; /* FMT_CONTROL->FMT_PIXEL_ENCODING from clamping->pixel_encoding */
+ uint32_t fmt_subsampling_mode; /* FMT_CONTROL->FMT_SUBSAMPLING_MODE from force_chroma_subsampling_1tap */
+ uint32_t fmt_cbcr_bit_reduction_bypass; /* FMT_CONTROL->FMT_CBCR_BIT_REDUCTION_BYPASS from pixel_encoding bypass control */
+ uint32_t fmt_stereosync_override; /* FMT_CONTROL->FMT_STEREOSYNC_OVERRIDE from stereo timing override */
+ uint32_t fmt_spatial_dither_frame_counter_max; /* FMT_CONTROL->FMT_SPATIAL_DITHER_FRAME_COUNTER_MAX from fmt_bit_depth->flags */
+ uint32_t fmt_spatial_dither_frame_counter_bit_swap; /* FMT_CONTROL->FMT_SPATIAL_DITHER_FRAME_COUNTER_BIT_SWAP from dither control */
+ uint32_t fmt_truncate_enable; /* FMT_CONTROL->FMT_TRUNCATE_EN from fmt_bit_depth->flags.TRUNCATE_ENABLED */
+ uint32_t fmt_truncate_depth; /* FMT_CONTROL->FMT_TRUNCATE_DEPTH from fmt_bit_depth->flags.TRUNCATE_DEPTH */
+ uint32_t fmt_truncate_mode; /* FMT_CONTROL->FMT_TRUNCATE_MODE from fmt_bit_depth->flags.TRUNCATE_MODE */
+ uint32_t fmt_spatial_dither_enable; /* FMT_CONTROL->FMT_SPATIAL_DITHER_EN from fmt_bit_depth->flags.SPATIAL_DITHER_ENABLED */
+ uint32_t fmt_spatial_dither_mode; /* FMT_CONTROL->FMT_SPATIAL_DITHER_MODE from fmt_bit_depth->flags.SPATIAL_DITHER_MODE */
+ uint32_t fmt_spatial_dither_depth; /* FMT_CONTROL->FMT_SPATIAL_DITHER_DEPTH from fmt_bit_depth->flags.SPATIAL_DITHER_DEPTH */
+ uint32_t fmt_temporal_dither_enable; /* FMT_CONTROL->FMT_TEMPORAL_DITHER_EN from fmt_bit_depth->flags.TEMPORAL_DITHER_ENABLED */
+ uint32_t fmt_clamp_data_enable; /* FMT_CONTROL->FMT_CLAMP_DATA_EN from clamping->clamping_range enable */
+ uint32_t fmt_clamp_color_format; /* FMT_CONTROL->FMT_CLAMP_COLOR_FORMAT from clamping->color_format */
+ uint32_t fmt_dynamic_exp_enable; /* FMT_CONTROL->FMT_DYNAMIC_EXP_EN from color_sp/color_dpth/signal */
+ uint32_t fmt_dynamic_exp_mode; /* FMT_CONTROL->FMT_DYNAMIC_EXP_MODE from color space mode mapping */
+ uint32_t fmt_bit_depth_control; /* Legacy field - kept for compatibility */
+
+ /* OPP Pipe Control - 1 field from OPP_PIPE_CONTROL register */
+ uint32_t opp_pipe_clock_enable; /* OPP_PIPE_CONTROL->OPP_PIPE_CLOCK_EN from enable parameter (bool) */
+
+ /* OPP CRC Control - 3 fields from OPP_PIPE_CRC_CONTROL register */
+ uint32_t opp_crc_enable; /* OPP_PIPE_CRC_CONTROL->CRC_EN from CRC enable control */
+ uint32_t opp_crc_select_source; /* OPP_PIPE_CRC_CONTROL->CRC_SELECT_SOURCE from CRC source selection */
+ uint32_t opp_crc_stereo_cont; /* OPP_PIPE_CRC_CONTROL->CRC_STEREO_CONT from stereo continuous CRC */
+
+ /* Output Buffer (OPPBUF) Control - 6 fields from OPPBUF_CONTROL register */
+ uint32_t oppbuf_active_width; /* OPPBUF_CONTROL->OPPBUF_ACTIVE_WIDTH from oppbuf_params->active_width */
+ uint32_t oppbuf_pixel_repetition; /* OPPBUF_CONTROL->OPPBUF_PIXEL_REPETITION from oppbuf_params->pixel_repetition */
+ uint32_t oppbuf_display_segmentation; /* OPPBUF_CONTROL->OPPBUF_DISPLAY_SEGMENTATION from oppbuf_params->mso_segmentation */
+ uint32_t oppbuf_overlap_pixel_num; /* OPPBUF_CONTROL->OPPBUF_OVERLAP_PIXEL_NUM from oppbuf_params->mso_overlap_pixel_num */
+ uint32_t oppbuf_3d_vact_space1_size; /* OPPBUF_CONTROL->OPPBUF_3D_VACT_SPACE1_SIZE from 3D timing space1_size */
+ uint32_t oppbuf_3d_vact_space2_size; /* OPPBUF_CONTROL->OPPBUF_3D_VACT_SPACE2_SIZE from 3D timing space2_size */
+
+ /* DSC Forward Config - 3 fields from DSCRM_DSC_FORWARD_CONFIG register */
+ uint32_t dscrm_dsc_forward_enable; /* DSCRM_DSC_FORWARD_CONFIG->DSCRM_DSC_FORWARD_EN from DSC forward enable control */
+ uint32_t dscrm_dsc_opp_pipe_source; /* DSCRM_DSC_FORWARD_CONFIG->DSCRM_DSC_OPP_PIPE_SOURCE from opp_pipe parameter */
+ uint32_t dscrm_dsc_forward_enable_status; /* DSCRM_DSC_FORWARD_CONFIG->DSCRM_DSC_FORWARD_EN_STATUS from DSC forward status (read-only) */
+ } opp[MAX_PIPES];
+
+ /* OPTC register programming variables for each pipe */
+ struct {
+ uint32_t otg_master_inst;
+
+ /* OTG_CONTROL register - 5 fields for OTG control */
+ uint32_t otg_master_enable; /* OTG_CONTROL->OTG_MASTER_EN from timing enable/disable control */
+ uint32_t otg_disable_point_cntl; /* OTG_CONTROL->OTG_DISABLE_POINT_CNTL from disable timing control */
+ uint32_t otg_start_point_cntl; /* OTG_CONTROL->OTG_START_POINT_CNTL from start timing control */
+ uint32_t otg_field_number_cntl; /* OTG_CONTROL->OTG_FIELD_NUMBER_CNTL from interlace field control */
+ uint32_t otg_out_mux; /* OTG_CONTROL->OTG_OUT_MUX from output mux selection */
+
+ /* OTG Horizontal Timing - 7 fields */
+ uint32_t otg_h_total; /* OTG_H_TOTAL->OTG_H_TOTAL from dc_crtc_timing->h_total */
+ uint32_t otg_h_blank_start; /* OTG_H_BLANK_START_END->OTG_H_BLANK_START from dc_crtc_timing->h_front_porch */
+ uint32_t otg_h_blank_end; /* OTG_H_BLANK_START_END->OTG_H_BLANK_END from dc_crtc_timing->h_addressable_video_pixel_width */
+ uint32_t otg_h_sync_start; /* OTG_H_SYNC_A->OTG_H_SYNC_A_START from dc_crtc_timing->h_sync_width */
+ uint32_t otg_h_sync_end; /* OTG_H_SYNC_A->OTG_H_SYNC_A_END from calculated sync end position */
+ uint32_t otg_h_sync_polarity; /* OTG_H_SYNC_A_CNTL->OTG_H_SYNC_A_POL from dc_crtc_timing->flags.HSYNC_POSITIVE_POLARITY */
+ uint32_t otg_h_timing_div_mode; /* OTG_H_TIMING_CNTL->OTG_H_TIMING_DIV_MODE from horizontal timing division mode */
+
+ /* OTG Vertical Timing - 7 fields */
+ uint32_t otg_v_total; /* OTG_V_TOTAL->OTG_V_TOTAL from dc_crtc_timing->v_total */
+ uint32_t otg_v_blank_start; /* OTG_V_BLANK_START_END->OTG_V_BLANK_START from dc_crtc_timing->v_front_porch */
+ uint32_t otg_v_blank_end; /* OTG_V_BLANK_START_END->OTG_V_BLANK_END from dc_crtc_timing->v_addressable_video_line_width */
+ uint32_t otg_v_sync_start; /* OTG_V_SYNC_A->OTG_V_SYNC_A_START from dc_crtc_timing->v_sync_width */
+ uint32_t otg_v_sync_end; /* OTG_V_SYNC_A->OTG_V_SYNC_A_END from calculated sync end position */
+ uint32_t otg_v_sync_polarity; /* OTG_V_SYNC_A_CNTL->OTG_V_SYNC_A_POL from dc_crtc_timing->flags.VSYNC_POSITIVE_POLARITY */
+ uint32_t otg_v_sync_mode; /* OTG_V_SYNC_A_CNTL->OTG_V_SYNC_MODE from sync mode selection */
+
+ /* OTG DRR (Dynamic Refresh Rate) Control - 8 fields */
+ uint32_t otg_v_total_max; /* OTG_V_TOTAL_MAX->OTG_V_TOTAL_MAX from drr_params->vertical_total_max */
+ uint32_t otg_v_total_min; /* OTG_V_TOTAL_MIN->OTG_V_TOTAL_MIN from drr_params->vertical_total_min */
+ uint32_t otg_v_total_mid; /* OTG_V_TOTAL_MID->OTG_V_TOTAL_MID from drr_params->vertical_total_mid */
+ uint32_t otg_v_total_max_sel; /* OTG_V_TOTAL_CONTROL->OTG_V_TOTAL_MAX_SEL from DRR max selection enable */
+ uint32_t otg_v_total_min_sel; /* OTG_V_TOTAL_CONTROL->OTG_V_TOTAL_MIN_SEL from DRR min selection enable */
+ uint32_t otg_vtotal_mid_replacing_max_en; /* OTG_V_TOTAL_CONTROL->OTG_VTOTAL_MID_REPLACING_MAX_EN from DRR mid-frame enable */
+ uint32_t otg_vtotal_mid_frame_num; /* OTG_V_TOTAL_CONTROL->OTG_VTOTAL_MID_FRAME_NUM from drr_params->vertical_total_mid_frame_num */
+ uint32_t otg_set_v_total_min_mask; /* OTG_V_TOTAL_CONTROL->OTG_SET_V_TOTAL_MIN_MASK from DRR trigger mask */
+ uint32_t otg_force_lock_on_event; /* OTG_V_TOTAL_CONTROL->OTG_FORCE_LOCK_ON_EVENT from DRR force lock control */
+
+ /* OPTC Data Source and ODM - 6 fields */
+ uint32_t optc_seg0_src_sel; /* OPTC_DATA_SOURCE_SELECT->OPTC_SEG0_SRC_SEL from opp_id[0] ODM segment 0 source */
+ uint32_t optc_seg1_src_sel; /* OPTC_DATA_SOURCE_SELECT->OPTC_SEG1_SRC_SEL from opp_id[1] ODM segment 1 source */
+ uint32_t optc_seg2_src_sel; /* OPTC_DATA_SOURCE_SELECT->OPTC_SEG2_SRC_SEL from opp_id[2] ODM segment 2 source */
+ uint32_t optc_seg3_src_sel; /* OPTC_DATA_SOURCE_SELECT->OPTC_SEG3_SRC_SEL from opp_id[3] ODM segment 3 source */
+ uint32_t optc_num_of_input_segment; /* OPTC_DATA_SOURCE_SELECT->OPTC_NUM_OF_INPUT_SEGMENT from opp_cnt-1 number of input segments */
+ uint32_t optc_mem_sel; /* OPTC_MEMORY_CONFIG->OPTC_MEM_SEL from memory_mask ODM memory selection */
+
+ /* OPTC Data Format and DSC - 4 fields */
+ uint32_t optc_data_format; /* OPTC_DATA_FORMAT_CONTROL->OPTC_DATA_FORMAT from data format selection */
+ uint32_t optc_dsc_mode; /* OPTC_DATA_FORMAT_CONTROL->OPTC_DSC_MODE from dsc_mode parameter */
+ uint32_t optc_dsc_bytes_per_pixel; /* OPTC_BYTES_PER_PIXEL->OPTC_DSC_BYTES_PER_PIXEL from dsc_bytes_per_pixel parameter */
+ uint32_t optc_segment_width; /* OPTC_WIDTH_CONTROL->OPTC_SEGMENT_WIDTH from segment_width parameter */
+ uint32_t optc_dsc_slice_width; /* OPTC_WIDTH_CONTROL->OPTC_DSC_SLICE_WIDTH from dsc_slice_width parameter */
+
+ /* OPTC Clock and Underflow Control - 4 fields */
+ uint32_t optc_input_pix_clk_en; /* OPTC_INPUT_CLOCK_CONTROL->OPTC_INPUT_PIX_CLK_EN from pixel clock enable */
+ uint32_t optc_underflow_occurred_status; /* OPTC_INPUT_GLOBAL_CONTROL->OPTC_UNDERFLOW_OCCURRED_STATUS from underflow status (read-only) */
+ uint32_t optc_underflow_clear; /* OPTC_INPUT_GLOBAL_CONTROL->OPTC_UNDERFLOW_CLEAR from underflow clear control */
+ uint32_t otg_clock_enable; /* OTG_CLOCK_CONTROL->OTG_CLOCK_EN from OTG clock enable */
+ uint32_t otg_clock_gate_dis; /* OTG_CLOCK_CONTROL->OTG_CLOCK_GATE_DIS from clock gate disable */
+
+ /* OTG Stereo and 3D Control - 6 fields */
+ uint32_t otg_stereo_enable; /* OTG_STEREO_CONTROL->OTG_STEREO_EN from stereo enable control */
+ uint32_t otg_stereo_sync_output_line_num; /* OTG_STEREO_CONTROL->OTG_STEREO_SYNC_OUTPUT_LINE_NUM from timing->stereo_3d_format line num */
+ uint32_t otg_stereo_sync_output_polarity; /* OTG_STEREO_CONTROL->OTG_STEREO_SYNC_OUTPUT_POLARITY from stereo polarity control */
+ uint32_t otg_3d_structure_en; /* OTG_3D_STRUCTURE_CONTROL->OTG_3D_STRUCTURE_EN from 3D structure enable */
+ uint32_t otg_3d_structure_v_update_mode; /* OTG_3D_STRUCTURE_CONTROL->OTG_3D_STRUCTURE_V_UPDATE_MODE from 3D vertical update mode */
+ uint32_t otg_3d_structure_stereo_sel_ovr; /* OTG_3D_STRUCTURE_CONTROL->OTG_3D_STRUCTURE_STEREO_SEL_OVR from 3D stereo selection override */
+ uint32_t otg_interlace_enable; /* OTG_INTERLACE_CONTROL->OTG_INTERLACE_ENABLE from dc_crtc_timing->flags.INTERLACE */
+
+ /* OTG GSL (Global Sync Lock) Control - 5 fields */
+ uint32_t otg_gsl0_en; /* OTG_GSL_CONTROL->OTG_GSL0_EN from GSL group 0 enable */
+ uint32_t otg_gsl1_en; /* OTG_GSL_CONTROL->OTG_GSL1_EN from GSL group 1 enable */
+ uint32_t otg_gsl2_en; /* OTG_GSL_CONTROL->OTG_GSL2_EN from GSL group 2 enable */
+ uint32_t otg_gsl_master_en; /* OTG_GSL_CONTROL->OTG_GSL_MASTER_EN from GSL master enable */
+ uint32_t otg_gsl_master_mode; /* OTG_GSL_CONTROL->OTG_GSL_MASTER_MODE from gsl_params->gsl_master mode */
+
+ /* OTG DRR Advanced Control - 4 fields */
+ uint32_t otg_v_total_last_used_by_drr; /* OTG_DRR_CONTROL->OTG_V_TOTAL_LAST_USED_BY_DRR from last used DRR V_TOTAL (read-only) */
+ uint32_t otg_drr_trigger_window_start_x; /* OTG_DRR_TRIGGER_WINDOW->OTG_DRR_TRIGGER_WINDOW_START_X from window_start parameter */
+ uint32_t otg_drr_trigger_window_end_x; /* OTG_DRR_TRIGGER_WINDOW->OTG_DRR_TRIGGER_WINDOW_END_X from window_end parameter */
+ uint32_t otg_drr_v_total_change_limit; /* OTG_DRR_V_TOTAL_CHANGE->OTG_DRR_V_TOTAL_CHANGE_LIMIT from limit parameter */
+
+ /* OTG DSC Position Control - 2 fields */
+ uint32_t otg_dsc_start_position_x; /* OTG_DSC_START_POSITION->OTG_DSC_START_POSITION_X from DSC start X position */
+ uint32_t otg_dsc_start_position_line_num; /* OTG_DSC_START_POSITION->OTG_DSC_START_POSITION_LINE_NUM from DSC start line number */
+
+ /* OTG Double Buffer Control - 2 fields */
+ uint32_t otg_drr_timing_dbuf_update_mode; /* OTG_DOUBLE_BUFFER_CONTROL->OTG_DRR_TIMING_DBUF_UPDATE_MODE from DRR double buffer mode */
+ uint32_t otg_blank_data_double_buffer_en; /* OTG_DOUBLE_BUFFER_CONTROL->OTG_BLANK_DATA_DOUBLE_BUFFER_EN from blank data double buffer enable */
+
+ /* OTG Vertical Interrupts - 6 fields */
+ uint32_t otg_vertical_interrupt0_int_enable; /* OTG_VERTICAL_INTERRUPT0_CONTROL->OTG_VERTICAL_INTERRUPT0_INT_ENABLE from interrupt 0 enable */
+ uint32_t otg_vertical_interrupt0_line_start; /* OTG_VERTICAL_INTERRUPT0_POSITION->OTG_VERTICAL_INTERRUPT0_LINE_START from start_line parameter */
+ uint32_t otg_vertical_interrupt1_int_enable; /* OTG_VERTICAL_INTERRUPT1_CONTROL->OTG_VERTICAL_INTERRUPT1_INT_ENABLE from interrupt 1 enable */
+ uint32_t otg_vertical_interrupt1_line_start; /* OTG_VERTICAL_INTERRUPT1_POSITION->OTG_VERTICAL_INTERRUPT1_LINE_START from start_line parameter */
+ uint32_t otg_vertical_interrupt2_int_enable; /* OTG_VERTICAL_INTERRUPT2_CONTROL->OTG_VERTICAL_INTERRUPT2_INT_ENABLE from interrupt 2 enable */
+ uint32_t otg_vertical_interrupt2_line_start; /* OTG_VERTICAL_INTERRUPT2_POSITION->OTG_VERTICAL_INTERRUPT2_LINE_START from start_line parameter */
+
+ /* OTG Global Sync Parameters - 6 fields */
+ uint32_t otg_vready_offset; /* OTG_VREADY_PARAM->OTG_VREADY_OFFSET from vready_offset parameter */
+ uint32_t otg_vstartup_start; /* OTG_VSTARTUP_PARAM->OTG_VSTARTUP_START from vstartup_start parameter */
+ uint32_t otg_vupdate_offset; /* OTG_VUPDATE_PARAM->OTG_VUPDATE_OFFSET from vupdate_offset parameter */
+ uint32_t otg_vupdate_width; /* OTG_VUPDATE_PARAM->OTG_VUPDATE_WIDTH from vupdate_width parameter */
+ uint32_t master_update_lock_vupdate_keepout_start_offset; /* OTG_VUPDATE_KEEPOUT->MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_START_OFFSET from pstate_keepout start */
+ uint32_t master_update_lock_vupdate_keepout_end_offset; /* OTG_VUPDATE_KEEPOUT->MASTER_UPDATE_LOCK_VUPDATE_KEEPOUT_END_OFFSET from pstate_keepout end */
+
+ /* OTG Manual Trigger Control - 11 fields */
+ uint32_t otg_triga_source_select; /* OTG_TRIGA_CNTL->OTG_TRIGA_SOURCE_SELECT from trigger A source selection */
+ uint32_t otg_triga_source_pipe_select; /* OTG_TRIGA_CNTL->OTG_TRIGA_SOURCE_PIPE_SELECT from trigger A pipe selection */
+ uint32_t otg_triga_rising_edge_detect_cntl; /* OTG_TRIGA_CNTL->OTG_TRIGA_RISING_EDGE_DETECT_CNTL from trigger A rising edge detect */
+ uint32_t otg_triga_falling_edge_detect_cntl; /* OTG_TRIGA_CNTL->OTG_TRIGA_FALLING_EDGE_DETECT_CNTL from trigger A falling edge detect */
+ uint32_t otg_triga_polarity_select; /* OTG_TRIGA_CNTL->OTG_TRIGA_POLARITY_SELECT from trigger A polarity selection */
+ uint32_t otg_triga_frequency_select; /* OTG_TRIGA_CNTL->OTG_TRIGA_FREQUENCY_SELECT from trigger A frequency selection */
+ uint32_t otg_triga_delay; /* OTG_TRIGA_CNTL->OTG_TRIGA_DELAY from trigger A delay */
+ uint32_t otg_triga_clear; /* OTG_TRIGA_CNTL->OTG_TRIGA_CLEAR from trigger A clear */
+ uint32_t otg_triga_manual_trig; /* OTG_TRIGA_MANUAL_TRIG->OTG_TRIGA_MANUAL_TRIG from manual trigger A */
+ uint32_t otg_trigb_source_select; /* OTG_TRIGB_CNTL->OTG_TRIGB_SOURCE_SELECT from trigger B source selection */
+ uint32_t otg_trigb_polarity_select; /* OTG_TRIGB_CNTL->OTG_TRIGB_POLARITY_SELECT from trigger B polarity selection */
+ uint32_t otg_trigb_manual_trig; /* OTG_TRIGB_MANUAL_TRIG->OTG_TRIGB_MANUAL_TRIG from manual trigger B */
+
+ /* OTG Static Screen and Update Control - 6 fields */
+ uint32_t otg_static_screen_event_mask; /* OTG_STATIC_SCREEN_CONTROL->OTG_STATIC_SCREEN_EVENT_MASK from event_triggers parameter */
+ uint32_t otg_static_screen_frame_count; /* OTG_STATIC_SCREEN_CONTROL->OTG_STATIC_SCREEN_FRAME_COUNT from num_frames parameter */
+ uint32_t master_update_lock; /* OTG_MASTER_UPDATE_LOCK->MASTER_UPDATE_LOCK from update lock control */
+ uint32_t master_update_mode; /* OTG_MASTER_UPDATE_MODE->MASTER_UPDATE_MODE from update mode selection */
+ uint32_t otg_force_count_now_mode; /* OTG_FORCE_COUNT_NOW_CNTL->OTG_FORCE_COUNT_NOW_MODE from force count mode */
+ uint32_t otg_force_count_now_clear; /* OTG_FORCE_COUNT_NOW_CNTL->OTG_FORCE_COUNT_NOW_CLEAR from force count clear */
+
+ /* VTG Control - 3 fields */
+ uint32_t vtg0_enable; /* CONTROL->VTG0_ENABLE from VTG enable control */
+ uint32_t vtg0_fp2; /* CONTROL->VTG0_FP2 from VTG front porch 2 */
+ uint32_t vtg0_vcount_init; /* CONTROL->VTG0_VCOUNT_INIT from VTG vertical count init */
+
+ /* OTG Status (Read-Only) - 12 fields */
+ uint32_t otg_v_blank; /* OTG_STATUS->OTG_V_BLANK from vertical blank status (read-only) */
+ uint32_t otg_v_active_disp; /* OTG_STATUS->OTG_V_ACTIVE_DISP from vertical active display (read-only) */
+ uint32_t otg_frame_count; /* OTG_STATUS_FRAME_COUNT->OTG_FRAME_COUNT from frame count (read-only) */
+ uint32_t otg_horz_count; /* OTG_STATUS_POSITION->OTG_HORZ_COUNT from horizontal position (read-only) */
+ uint32_t otg_vert_count; /* OTG_STATUS_POSITION->OTG_VERT_COUNT from vertical position (read-only) */
+ uint32_t otg_horz_count_hv; /* OTG_STATUS_HV_COUNT->OTG_HORZ_COUNT from horizontal count (read-only) */
+ uint32_t otg_vert_count_nom; /* OTG_STATUS_HV_COUNT->OTG_VERT_COUNT_NOM from vertical count nominal (read-only) */
+ uint32_t otg_flip_pending; /* OTG_PIPE_UPDATE_STATUS->OTG_FLIP_PENDING from flip pending status (read-only) */
+ uint32_t otg_dc_reg_update_pending; /* OTG_PIPE_UPDATE_STATUS->OTG_DC_REG_UPDATE_PENDING from DC register update pending (read-only) */
+ uint32_t otg_cursor_update_pending; /* OTG_PIPE_UPDATE_STATUS->OTG_CURSOR_UPDATE_PENDING from cursor update pending (read-only) */
+ uint32_t otg_vupdate_keepout_status; /* OTG_PIPE_UPDATE_STATUS->OTG_VUPDATE_KEEPOUT_STATUS from VUPDATE keepout status (read-only) */
+ } optc[MAX_PIPES];
+
+ /* Metadata */
+ uint32_t active_pipe_count;
+ uint32_t active_stream_count;
+ bool state_valid;
+};
+
+/**
+ * dc_capture_register_software_state() - Capture software state for register programming
+ * @dc: DC context containing current display configuration
+ * @state: Pointer to dc_register_software_state structure to populate
+ *
+ * Extracts all software state variables that are used to program hardware register
+ * fields across the display driver pipeline. This provides a complete snapshot
+ * of the software configuration that drives hardware register programming.
+ *
+ * The function traverses the DC context and extracts values from:
+ * - Stream configurations (timing, format, DSC settings)
+ * - Plane states (surface format, rotation, scaling, cursor)
+ * - Pipe contexts (resource allocation, blending, viewport)
+ * - Clock manager (display clocks, DPP clocks, pixel clocks)
+ * - Resource context (DET buffer allocation, ODM configuration)
+ *
+ * This is essential for underflow debugging as it captures the exact software
+ * state that determines how registers are programmed, allowing analysis of
+ * whether underflow is caused by incorrect register programming or timing issues.
+ *
+ * Return: true if state was successfully captured, false on error
+ */
+bool dc_capture_register_software_state(struct dc *dc, struct dc_register_software_state *state);
+
#endif /* DC_INTERFACE_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dc_bios_types.h b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h
index 5fa5e2b63fb7..40d7a7d83c40 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_bios_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_bios_types.h
@@ -91,9 +91,17 @@ struct dc_vbios_funcs {
struct device_id id);
/* COMMANDS */
+ enum bp_result (*select_crtc_source)(
+ struct dc_bios *bios,
+ struct bp_crtc_source_select *bp_params);
enum bp_result (*encoder_control)(
struct dc_bios *bios,
struct bp_encoder_control *cntl);
+ enum bp_result (*dac_load_detection)(
+ struct dc_bios *bios,
+ enum engine_id engine_id,
+ enum dal_device_type device_type,
+ uint32_t enum_id);
enum bp_result (*transmitter_control)(
struct dc_bios *bios,
struct bp_transmitter_control *cntl);
@@ -165,6 +173,7 @@ struct dc_vbios_funcs {
};
struct bios_registers {
+ uint32_t BIOS_SCRATCH_0;
uint32_t BIOS_SCRATCH_3;
uint32_t BIOS_SCRATCH_6;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
index 53a088ebddef..7b09af1cb306 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c
@@ -442,7 +442,6 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru
int i = 0, k = 0;
int ramp_up_num_steps = 1; // TODO: Ramp is currently disabled. Reenable it.
uint8_t visual_confirm_enabled;
- int pipe_idx = 0;
struct dc_stream_status *stream_status = NULL;
if (dc == NULL)
@@ -457,7 +456,7 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru
cmd.fw_assisted_mclk_switch.config_data.visual_confirm_enabled = visual_confirm_enabled;
if (should_manage_pstate) {
- for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->stream)
@@ -472,7 +471,6 @@ bool dc_dmub_srv_p_state_delegate(struct dc *dc, bool should_manage_pstate, stru
cmd.fw_assisted_mclk_switch.config_data.vactive_stretch_margin_us = dc->debug.fpo_vactive_margin_us;
break;
}
- pipe_idx++;
}
}
@@ -872,7 +870,7 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
bool enable)
{
uint8_t cmd_pipe_index = 0;
- uint32_t i, pipe_idx;
+ uint32_t i;
uint8_t subvp_count = 0;
union dmub_rb_cmd cmd;
struct pipe_ctx *subvp_pipes[2];
@@ -899,7 +897,7 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
if (enable) {
// For each pipe that is a "main" SUBVP pipe, fill in pipe data for DMUB SUBVP cmd
- for (i = 0, pipe_idx = 0; i < dc->res_pool->pipe_count; i++) {
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
pipe_mall_type = dc_state_get_pipe_subvp_type(context, pipe);
@@ -922,7 +920,6 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc,
populate_subvp_cmd_vblank_pipe_info(dc, context, &cmd, pipe, cmd_pipe_index++);
}
- pipe_idx++;
}
if (subvp_count == 2) {
update_subvp_prefetch_end_to_mall_start(dc, context, &cmd, subvp_pipes);
@@ -1174,6 +1171,100 @@ void dc_dmub_srv_subvp_save_surf_addr(const struct dc_dmub_srv *dc_dmub_srv, con
dmub_srv_subvp_save_surf_addr(dc_dmub_srv->dmub, addr, subvp_index);
}
+void dc_dmub_srv_cursor_offload_init(struct dc *dc)
+{
+ struct dmub_rb_cmd_cursor_offload_init *init;
+ struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv;
+ union dmub_rb_cmd cmd;
+
+ if (!dc->config.enable_cursor_offload)
+ return;
+
+ if (!dc_dmub_srv->dmub->meta_info.feature_bits.bits.cursor_offload_v1_support)
+ return;
+
+ if (!dc_dmub_srv->dmub->cursor_offload_fb.gpu_addr || !dc_dmub_srv->dmub->cursor_offload_fb.cpu_addr)
+ return;
+
+ if (!dc_dmub_srv->dmub->cursor_offload_v1)
+ return;
+
+ if (!dc_dmub_srv->dmub->shared_state)
+ return;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ init = &cmd.cursor_offload_init;
+ init->header.type = DMUB_CMD__CURSOR_OFFLOAD;
+ init->header.sub_type = DMUB_CMD__CURSOR_OFFLOAD_INIT;
+ init->header.payload_bytes = sizeof(init->init_data);
+ init->init_data.state_addr.quad_part = dc_dmub_srv->dmub->cursor_offload_fb.gpu_addr;
+ init->init_data.state_size = dc_dmub_srv->dmub->cursor_offload_fb.size;
+
+ dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
+
+ dc_dmub_srv->cursor_offload_enabled = true;
+}
+
+void dc_dmub_srv_control_cursor_offload(struct dc *dc, struct dc_state *context,
+ const struct dc_stream_state *stream, bool enable)
+{
+ struct pipe_ctx const *pipe_ctx;
+ struct dmub_rb_cmd_cursor_offload_stream_cntl *cntl;
+ union dmub_rb_cmd cmd;
+
+ if (!dc_dmub_srv_is_cursor_offload_enabled(dc))
+ return;
+
+ if (!stream)
+ return;
+
+ pipe_ctx = resource_get_otg_master_for_stream(&context->res_ctx, stream);
+ if (!pipe_ctx || !pipe_ctx->stream_res.tg || pipe_ctx->stream != stream)
+ return;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cntl = &cmd.cursor_offload_stream_ctnl;
+ cntl->header.type = DMUB_CMD__CURSOR_OFFLOAD;
+ cntl->header.sub_type =
+ enable ? DMUB_CMD__CURSOR_OFFLOAD_STREAM_ENABLE : DMUB_CMD__CURSOR_OFFLOAD_STREAM_DISABLE;
+ cntl->header.payload_bytes = sizeof(cntl->data);
+
+ cntl->data.otg_inst = pipe_ctx->stream_res.tg->inst;
+ cntl->data.line_time_in_ns = 1u + (uint32_t)(div64_u64(stream->timing.h_total * 1000000ull,
+ stream->timing.pix_clk_100hz / 10));
+
+ cntl->data.v_total_max = stream->adjust.v_total_max > stream->timing.v_total ?
+ stream->adjust.v_total_max :
+ stream->timing.v_total;
+
+ dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd,
+ enable ? DM_DMUB_WAIT_TYPE_NO_WAIT : DM_DMUB_WAIT_TYPE_WAIT);
+}
+
+void dc_dmub_srv_program_cursor_now(struct dc *dc, const struct pipe_ctx *pipe)
+{
+ struct dmub_rb_cmd_cursor_offload_stream_cntl *cntl;
+ union dmub_rb_cmd cmd;
+
+ if (!dc_dmub_srv_is_cursor_offload_enabled(dc))
+ return;
+
+ if (!pipe || !pipe->stream || !pipe->stream_res.tg)
+ return;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ cntl = &cmd.cursor_offload_stream_ctnl;
+ cntl->header.type = DMUB_CMD__CURSOR_OFFLOAD;
+ cntl->header.sub_type = DMUB_CMD__CURSOR_OFFLOAD_STREAM_PROGRAM;
+ cntl->header.payload_bytes = sizeof(cntl->data);
+ cntl->data.otg_inst = pipe->stream_res.tg->inst;
+
+ dc_wake_and_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_NO_WAIT);
+}
+
bool dc_dmub_srv_is_hw_pwr_up(struct dc_dmub_srv *dc_dmub_srv, bool wait)
{
struct dc_context *dc_ctx;
@@ -1993,6 +2084,9 @@ bool dmub_lsdma_init(struct dc_dmub_srv *dc_dmub_srv)
struct dmub_cmd_lsdma_data *lsdma_data = &cmd.lsdma.lsdma_data;
bool result;
+ if (!dc_dmub_srv->dmub->feature_caps.lsdma_support_in_dmu)
+ return false;
+
memset(&cmd, 0, sizeof(cmd));
cmd.cmd_common.header.type = DMUB_CMD__LSDMA;
@@ -2231,6 +2325,11 @@ bool dmub_lsdma_send_poll_reg_write_command(struct dc_dmub_srv *dc_dmub_srv, uin
return result;
}
+bool dc_dmub_srv_is_cursor_offload_enabled(const struct dc *dc)
+{
+ return dc->ctx->dmub_srv && dc->ctx->dmub_srv->cursor_offload_enabled;
+}
+
void dc_dmub_srv_release_hw(const struct dc *dc)
{
struct dc_dmub_srv *dc_dmub_srv = dc->ctx->dmub_srv;
@@ -2248,3 +2347,24 @@ void dc_dmub_srv_release_hw(const struct dc *dc)
dm_execute_dmub_cmd(dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}
+
+void dc_dmub_srv_log_preos_dmcub_info(struct dc_dmub_srv *dc_dmub_srv)
+{
+ struct dmub_srv *dmub;
+
+ if (!dc_dmub_srv || !dc_dmub_srv->dmub)
+ return;
+
+ dmub = dc_dmub_srv->dmub;
+
+ if (dmub_srv_get_preos_info(dmub)) {
+ DC_LOG_DEBUG("%s: PreOS DMCUB Info", __func__);
+ DC_LOG_DEBUG("fw_version : 0x%08x", dmub->preos_info.fw_version);
+ DC_LOG_DEBUG("boot_options : 0x%08x", dmub->preos_info.boot_options);
+ DC_LOG_DEBUG("boot_status : 0x%08x", dmub->preos_info.boot_status);
+ DC_LOG_DEBUG("trace_buffer_phy_addr : 0x%016llx", dmub->preos_info.trace_buffer_phy_addr);
+ DC_LOG_DEBUG("trace_buffer_size_bytes : 0x%08x", dmub->preos_info.trace_buffer_size);
+ DC_LOG_DEBUG("fb_base : 0x%016llx", dmub->preos_info.fb_base);
+ DC_LOG_DEBUG("fb_offset : 0x%016llx", dmub->preos_info.fb_offset);
+ }
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
index 7ef93444ef3c..72e0a41f39f0 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h
@@ -56,6 +56,7 @@ struct dc_dmub_srv {
union dmub_shared_state_ips_driver_signals driver_signals;
bool idle_allowed;
bool needs_idle_wake;
+ bool cursor_offload_enabled;
};
bool dc_dmub_srv_wait_for_pending(struct dc_dmub_srv *dc_dmub_srv);
@@ -326,9 +327,51 @@ bool dc_dmub_srv_ips_query_residency_info(const struct dc_context *ctx, uint8_t
enum ips_residency_mode ips_mode);
/**
+ * dc_dmub_srv_cursor_offload_init() - Enables or disables cursor offloading for a stream.
+ *
+ * @dc: pointer to DC object
+ */
+void dc_dmub_srv_cursor_offload_init(struct dc *dc);
+
+/**
+ * dc_dmub_srv_control_cursor_offload() - Enables or disables cursor offloading for a stream.
+ *
+ * @dc: pointer to DC object
+ * @context: the DC context to reference for pipe allocations
+ * @stream: the stream to control
+ * @enable: true to enable cursor offload, false to disable
+ */
+void dc_dmub_srv_control_cursor_offload(struct dc *dc, struct dc_state *context,
+ const struct dc_stream_state *stream, bool enable);
+
+/**
+ * dc_dmub_srv_program_cursor_now() - Requests immediate cursor programming for a given pipe.
+ *
+ * @dc: pointer to DC object
+ * @pipe: top-most pipe for a stream.
+ */
+void dc_dmub_srv_program_cursor_now(struct dc *dc, const struct pipe_ctx *pipe);
+
+/**
+ * dc_dmub_srv_is_cursor_offload_enabled() - Checks if cursor offload is supported.
+ *
+ * @dc: pointer to DC object
+ *
+ * Return: true if cursor offload is supported, false otherwise
+ */
+bool dc_dmub_srv_is_cursor_offload_enabled(const struct dc *dc);
+
+/**
* dc_dmub_srv_release_hw() - Notifies DMUB service that HW access is no longer required.
*
* @dc - pointer to DC object
*/
void dc_dmub_srv_release_hw(const struct dc *dc);
+
+/**
+ * dc_dmub_srv_log_preos_dmcub_info() - Logs preos dmcub fw info.
+ *
+ * @dc - pointer to DC object
+ */
+void dc_dmub_srv_log_preos_dmcub_info(struct dc_dmub_srv *dc_dmub_srv);
#endif /* _DMUB_DC_SRV_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h
index db669ccb1d58..79e1696def63 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h
@@ -1157,6 +1157,16 @@ struct dprx_states {
bool cable_id_written;
};
+union dpcd_panel_replay_capability_supported {
+ struct {
+ unsigned char PANEL_REPLAY_SUPPORT :1;
+ unsigned char SELECTIVE_UPDATE_SUPPORT :1;
+ unsigned char EARLY_TRANSPORT_SUPPORT :1;
+ unsigned char RESERVED :5;
+ } bits;
+ unsigned char raw;
+};
+
enum dpcd_downstream_port_max_bpc {
DOWN_STREAM_MAX_8BPC = 0,
DOWN_STREAM_MAX_10BPC,
@@ -1280,6 +1290,7 @@ struct dpcd_caps {
struct edp_psr_info psr_info;
struct replay_info pr_info;
+ union dpcd_panel_replay_capability_supported pr_caps_supported;
uint16_t edp_oled_emission_rate;
union dp_receive_port0_cap receive_port0_cap;
/* Indicates the number of SST links supported by MSO (Multi-Stream Output) */
@@ -1346,6 +1357,31 @@ union dpcd_replay_configuration {
unsigned char raw;
};
+union panel_replay_enable_and_configuration_1 {
+ struct {
+ unsigned char PANEL_REPLAY_ENABLE :1;
+ unsigned char PANEL_REPLAY_CRC_ENABLE :1;
+ unsigned char IRQ_HPD_ASSDP_MISSING :1;
+ unsigned char IRQ_HPD_VSCSDP_UNCORRECTABLE_ERROR :1;
+ unsigned char IRQ_HPD_RFB_ERROR :1;
+ unsigned char IRQ_HPD_ACTIVE_FRAME_CRC_ERROR :1;
+ unsigned char PANEL_REPLAY_SELECTIVE_UPDATE_ENABLE :1;
+ unsigned char PANEL_REPLAY_EARLY_TRANSPORT_ENABLE :1;
+ } bits;
+ unsigned char raw;
+};
+
+union panel_replay_enable_and_configuration_2 {
+ struct {
+ unsigned char SINK_REFRESH_RATE_UNLOCK_GRANTED :1;
+ unsigned char RESERVED :1;
+ unsigned char SU_Y_GRANULARITY_EXT_VALUE_ENABLED :1;
+ unsigned char SU_Y_GRANULARITY_EXT_VALUE :4;
+ unsigned char SU_REGION_SCAN_LINE_CAPTURE_INDICATION :1;
+ } bits;
+ unsigned char raw;
+};
+
union dpcd_alpm_configuration {
struct {
unsigned char ENABLE : 1;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c b/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c
index 55704d4457ef..37d1a79e8241 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c
+++ b/drivers/gpu/drm/amd/display/dc/dc_spl_translate.c
@@ -147,6 +147,8 @@ void translate_SPL_in_params_from_pipe_ctx(struct pipe_ctx *pipe_ctx, struct spl
spl_in->prefer_easf = false;
else if (pipe_ctx->stream->ctx->dc->debug.force_easf == 2)
spl_in->disable_easf = true;
+ else if (pipe_ctx->stream->ctx->dc->debug.force_easf == 3)
+ spl_in->override_easf = true;
/* Translate adaptive sharpening preference */
unsigned int sharpness_setting = pipe_ctx->stream->ctx->dc->debug.force_sharpness;
unsigned int force_sharpness_level = pipe_ctx->stream->ctx->dc->debug.force_sharpness_level;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index 76cf9fdedab0..321cfe92d799 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -473,12 +473,11 @@ void dc_enable_stereo(
/* Triggers multi-stream synchronization. */
void dc_trigger_sync(struct dc *dc, struct dc_state *context);
-enum surface_update_type dc_check_update_surfaces_for_stream(
- struct dc *dc,
+struct surface_update_descriptor dc_check_update_surfaces_for_stream(
+ const struct dc_check_config *check_config,
struct dc_surface_update *updates,
int surface_count,
- struct dc_stream_update *stream_update,
- const struct dc_stream_status *stream_status);
+ struct dc_stream_update *stream_update);
/**
* Create a new default stream for the requested sink
@@ -492,8 +491,8 @@ void update_stream_signal(struct dc_stream_state *stream, struct dc_sink *sink);
void dc_stream_retain(struct dc_stream_state *dc_stream);
void dc_stream_release(struct dc_stream_state *dc_stream);
-struct dc_stream_status *dc_stream_get_status(
- struct dc_stream_state *dc_stream);
+struct dc_stream_status *dc_stream_get_status(struct dc_stream_state *dc_stream);
+const struct dc_stream_status *dc_stream_get_status_const(const struct dc_stream_state *dc_stream);
/*******************************************************************************
* Cursor interfaces - To manages the cursor within a stream
diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
index b5aa03a3e39c..f46039f64203 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
@@ -185,6 +185,10 @@ struct dc_panel_patch {
unsigned int wait_after_dpcd_poweroff_ms;
};
+/**
+ * struct dc_edid_caps - Capabilities read from EDID.
+ * @analog: Whether the monitor is analog. Used by DVI-I handling.
+ */
struct dc_edid_caps {
/* sink identification */
uint16_t manufacturer_id;
@@ -212,6 +216,8 @@ struct dc_edid_caps {
bool edid_hdmi;
bool hdr_supported;
bool rr_capable;
+ bool scdc_present;
+ bool analog;
struct dc_panel_patch panel_patch;
};
@@ -347,7 +353,8 @@ enum dc_connection_type {
dc_connection_none,
dc_connection_single,
dc_connection_mst_branch,
- dc_connection_sst_branch
+ dc_connection_sst_branch,
+ dc_connection_dac_load
};
struct dc_csc_adjustments {
@@ -934,6 +941,12 @@ enum dc_psr_version {
DC_PSR_VERSION_UNSUPPORTED = 0xFFFFFFFF,
};
+enum dc_replay_version {
+ DC_FREESYNC_REPLAY = 0,
+ DC_VESA_PANEL_REPLAY = 1,
+ DC_REPLAY_VERSION_UNSUPPORTED = 0XFF,
+};
+
/* Possible values of display_endpoint_id.endpoint */
enum display_endpoint_type {
DISPLAY_ENDPOINT_PHY = 0, /* Physical connector. */
@@ -1086,6 +1099,7 @@ enum replay_FW_Message_type {
Replay_Set_Residency_Frameupdate_Timer,
Replay_Set_Pseudo_VTotal,
Replay_Disabled_Adaptive_Sync_SDP,
+ Replay_Set_Version,
Replay_Set_General_Cmd,
};
@@ -1121,6 +1135,8 @@ union replay_low_refresh_rate_enable_options {
};
struct replay_config {
+ /* Replay version */
+ enum dc_replay_version replay_version;
/* Replay feature is supported */
bool replay_supported;
/* Replay caps support DPCD & EDID caps*/
@@ -1177,6 +1193,10 @@ struct replay_settings {
uint32_t coasting_vtotal_table[PR_COASTING_TYPE_NUM];
/* Defer Update Coasting vtotal table */
uint32_t defer_update_coasting_vtotal_table[PR_COASTING_TYPE_NUM];
+ /* Skip frame number table */
+ uint32_t frame_skip_number_table[PR_COASTING_TYPE_NUM];
+ /* Defer skip frame number table */
+ uint32_t defer_frame_skip_number_table[PR_COASTING_TYPE_NUM];
/* Maximum link off frame count */
uint32_t link_off_frame_count;
/* Replay pseudo vtotal for low refresh rate*/
@@ -1185,6 +1205,8 @@ struct replay_settings {
uint16_t last_pseudo_vtotal;
/* Replay desync error */
uint32_t replay_desync_error_fail_count;
+ /* The frame skip number dal send to DMUB */
+ uint16_t frame_skip_number;
};
/* To split out "global" and "per-panel" config settings.
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c
index 5999b2da3a01..33d8bd91cb01 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.c
@@ -148,7 +148,7 @@ struct dccg *dccg2_create(
const struct dccg_shift *dccg_shift,
const struct dccg_mask *dccg_mask)
{
- struct dcn_dccg *dccg_dcn = kzalloc(sizeof(*dccg_dcn), GFP_ATOMIC);
+ struct dcn_dccg *dccg_dcn = kzalloc(sizeof(*dccg_dcn), GFP_KERNEL);
struct dccg *base;
if (dccg_dcn == NULL) {
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.h
index a9b88f5e0c04..8bdffd9ff31b 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.h
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn20/dcn20_dccg.h
@@ -425,7 +425,69 @@ struct dccg_mask {
uint32_t SYMCLKD_CLOCK_ENABLE; \
uint32_t SYMCLKE_CLOCK_ENABLE; \
uint32_t DP_DTO_MODULO[MAX_PIPES]; \
- uint32_t DP_DTO_PHASE[MAX_PIPES]
+ uint32_t DP_DTO_PHASE[MAX_PIPES]; \
+ uint32_t DC_MEM_GLOBAL_PWR_REQ_CNTL; \
+ uint32_t DCCG_AUDIO_DTO0_MODULE; \
+ uint32_t DCCG_AUDIO_DTO0_PHASE; \
+ uint32_t DCCG_AUDIO_DTO1_MODULE; \
+ uint32_t DCCG_AUDIO_DTO1_PHASE; \
+ uint32_t DCCG_CAC_STATUS; \
+ uint32_t DCCG_CAC_STATUS2; \
+ uint32_t DCCG_DISP_CNTL_REG; \
+ uint32_t DCCG_DS_CNTL; \
+ uint32_t DCCG_DS_DTO_INCR; \
+ uint32_t DCCG_DS_DTO_MODULO; \
+ uint32_t DCCG_DS_HW_CAL_INTERVAL; \
+ uint32_t DCCG_GTC_CNTL; \
+ uint32_t DCCG_GTC_CURRENT; \
+ uint32_t DCCG_GTC_DTO_INCR; \
+ uint32_t DCCG_GTC_DTO_MODULO; \
+ uint32_t DCCG_PERFMON_CNTL; \
+ uint32_t DCCG_PERFMON_CNTL2; \
+ uint32_t DCCG_SOFT_RESET; \
+ uint32_t DCCG_TEST_CLK_SEL; \
+ uint32_t DCCG_VSYNC_CNT_CTRL; \
+ uint32_t DCCG_VSYNC_CNT_INT_CTRL; \
+ uint32_t DCCG_VSYNC_OTG0_LATCH_VALUE; \
+ uint32_t DCCG_VSYNC_OTG1_LATCH_VALUE; \
+ uint32_t DCCG_VSYNC_OTG2_LATCH_VALUE; \
+ uint32_t DCCG_VSYNC_OTG3_LATCH_VALUE; \
+ uint32_t DCCG_VSYNC_OTG4_LATCH_VALUE; \
+ uint32_t DCCG_VSYNC_OTG5_LATCH_VALUE; \
+ uint32_t DISPCLK_CGTT_BLK_CTRL_REG; \
+ uint32_t DP_DTO_DBUF_EN; \
+ uint32_t DPIACLK_540M_DTO_MODULO; \
+ uint32_t DPIACLK_540M_DTO_PHASE; \
+ uint32_t DPIACLK_810M_DTO_MODULO; \
+ uint32_t DPIACLK_810M_DTO_PHASE; \
+ uint32_t DPIACLK_DTO_CNTL; \
+ uint32_t DPIASYMCLK_CNTL; \
+ uint32_t DPPCLK_CGTT_BLK_CTRL_REG; \
+ uint32_t DPREFCLK_CGTT_BLK_CTRL_REG; \
+ uint32_t DPREFCLK_CNTL; \
+ uint32_t DTBCLK_DTO_DBUF_EN; \
+ uint32_t FORCE_SYMCLK_DISABLE; \
+ uint32_t HDMICHARCLK0_CLOCK_CNTL; \
+ uint32_t MICROSECOND_TIME_BASE_DIV; \
+ uint32_t MILLISECOND_TIME_BASE_DIV; \
+ uint32_t OTG0_PHYPLL_PIXEL_RATE_CNTL; \
+ uint32_t OTG0_PIXEL_RATE_CNTL; \
+ uint32_t OTG1_PHYPLL_PIXEL_RATE_CNTL; \
+ uint32_t OTG1_PIXEL_RATE_CNTL; \
+ uint32_t OTG2_PHYPLL_PIXEL_RATE_CNTL; \
+ uint32_t OTG2_PIXEL_RATE_CNTL; \
+ uint32_t OTG3_PHYPLL_PIXEL_RATE_CNTL; \
+ uint32_t OTG3_PIXEL_RATE_CNTL; \
+ uint32_t PHYPLLA_PIXCLK_RESYNC_CNTL; \
+ uint32_t PHYPLLB_PIXCLK_RESYNC_CNTL; \
+ uint32_t PHYPLLC_PIXCLK_RESYNC_CNTL; \
+ uint32_t PHYPLLD_PIXCLK_RESYNC_CNTL; \
+ uint32_t PHYPLLE_PIXCLK_RESYNC_CNTL; \
+ uint32_t REFCLK_CGTT_BLK_CTRL_REG; \
+ uint32_t SOCCLK_CGTT_BLK_CTRL_REG; \
+ uint32_t SYMCLK_CGTT_BLK_CTRL_REG; \
+ uint32_t SYMCLK_PSP_CNTL
+
struct dccg_registers {
DCCG_REG_VARIABLE_LIST;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c
index 8664f0c4c9b7..97df04b7e39d 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.c
@@ -709,6 +709,128 @@ void dccg31_otg_drop_pixel(struct dccg *dccg,
OTG_DROP_PIXEL[otg_inst], 1);
}
+void dccg31_read_reg_state(struct dccg *dccg, struct dcn_dccg_reg_state *dccg_reg_state)
+{
+ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
+
+ dccg_reg_state->dc_mem_global_pwr_req_cntl = REG_READ(DC_MEM_GLOBAL_PWR_REQ_CNTL);
+ dccg_reg_state->dccg_audio_dtbclk_dto_modulo = REG_READ(DCCG_AUDIO_DTBCLK_DTO_MODULO);
+ dccg_reg_state->dccg_audio_dtbclk_dto_phase = REG_READ(DCCG_AUDIO_DTBCLK_DTO_PHASE);
+ dccg_reg_state->dccg_audio_dto_source = REG_READ(DCCG_AUDIO_DTO_SOURCE);
+ dccg_reg_state->dccg_audio_dto0_module = REG_READ(DCCG_AUDIO_DTO0_MODULE);
+ dccg_reg_state->dccg_audio_dto0_phase = REG_READ(DCCG_AUDIO_DTO0_PHASE);
+ dccg_reg_state->dccg_audio_dto1_module = REG_READ(DCCG_AUDIO_DTO1_MODULE);
+ dccg_reg_state->dccg_audio_dto1_phase = REG_READ(DCCG_AUDIO_DTO1_PHASE);
+ dccg_reg_state->dccg_cac_status = REG_READ(DCCG_CAC_STATUS);
+ dccg_reg_state->dccg_cac_status2 = REG_READ(DCCG_CAC_STATUS2);
+ dccg_reg_state->dccg_disp_cntl_reg = REG_READ(DCCG_DISP_CNTL_REG);
+ dccg_reg_state->dccg_ds_cntl = REG_READ(DCCG_DS_CNTL);
+ dccg_reg_state->dccg_ds_dto_incr = REG_READ(DCCG_DS_DTO_INCR);
+ dccg_reg_state->dccg_ds_dto_modulo = REG_READ(DCCG_DS_DTO_MODULO);
+ dccg_reg_state->dccg_ds_hw_cal_interval = REG_READ(DCCG_DS_HW_CAL_INTERVAL);
+ dccg_reg_state->dccg_gate_disable_cntl = REG_READ(DCCG_GATE_DISABLE_CNTL);
+ dccg_reg_state->dccg_gate_disable_cntl2 = REG_READ(DCCG_GATE_DISABLE_CNTL2);
+ dccg_reg_state->dccg_gate_disable_cntl3 = REG_READ(DCCG_GATE_DISABLE_CNTL3);
+ dccg_reg_state->dccg_gate_disable_cntl4 = REG_READ(DCCG_GATE_DISABLE_CNTL4);
+ dccg_reg_state->dccg_gate_disable_cntl5 = REG_READ(DCCG_GATE_DISABLE_CNTL5);
+ dccg_reg_state->dccg_gate_disable_cntl6 = REG_READ(DCCG_GATE_DISABLE_CNTL6);
+ dccg_reg_state->dccg_global_fgcg_rep_cntl = REG_READ(DCCG_GLOBAL_FGCG_REP_CNTL);
+ dccg_reg_state->dccg_gtc_cntl = REG_READ(DCCG_GTC_CNTL);
+ dccg_reg_state->dccg_gtc_current = REG_READ(DCCG_GTC_CURRENT);
+ dccg_reg_state->dccg_gtc_dto_incr = REG_READ(DCCG_GTC_DTO_INCR);
+ dccg_reg_state->dccg_gtc_dto_modulo = REG_READ(DCCG_GTC_DTO_MODULO);
+ dccg_reg_state->dccg_perfmon_cntl = REG_READ(DCCG_PERFMON_CNTL);
+ dccg_reg_state->dccg_perfmon_cntl2 = REG_READ(DCCG_PERFMON_CNTL2);
+ dccg_reg_state->dccg_soft_reset = REG_READ(DCCG_SOFT_RESET);
+ dccg_reg_state->dccg_test_clk_sel = REG_READ(DCCG_TEST_CLK_SEL);
+ dccg_reg_state->dccg_vsync_cnt_ctrl = REG_READ(DCCG_VSYNC_CNT_CTRL);
+ dccg_reg_state->dccg_vsync_cnt_int_ctrl = REG_READ(DCCG_VSYNC_CNT_INT_CTRL);
+ dccg_reg_state->dccg_vsync_otg0_latch_value = REG_READ(DCCG_VSYNC_OTG0_LATCH_VALUE);
+ dccg_reg_state->dccg_vsync_otg1_latch_value = REG_READ(DCCG_VSYNC_OTG1_LATCH_VALUE);
+ dccg_reg_state->dccg_vsync_otg2_latch_value = REG_READ(DCCG_VSYNC_OTG2_LATCH_VALUE);
+ dccg_reg_state->dccg_vsync_otg3_latch_value = REG_READ(DCCG_VSYNC_OTG3_LATCH_VALUE);
+ dccg_reg_state->dccg_vsync_otg4_latch_value = REG_READ(DCCG_VSYNC_OTG4_LATCH_VALUE);
+ dccg_reg_state->dccg_vsync_otg5_latch_value = REG_READ(DCCG_VSYNC_OTG5_LATCH_VALUE);
+ dccg_reg_state->dispclk_cgtt_blk_ctrl_reg = REG_READ(DISPCLK_CGTT_BLK_CTRL_REG);
+ dccg_reg_state->dispclk_freq_change_cntl = REG_READ(DISPCLK_FREQ_CHANGE_CNTL);
+ dccg_reg_state->dp_dto_dbuf_en = REG_READ(DP_DTO_DBUF_EN);
+ dccg_reg_state->dp_dto0_modulo = REG_READ(DP_DTO_MODULO[0]);
+ dccg_reg_state->dp_dto0_phase = REG_READ(DP_DTO_PHASE[0]);
+ dccg_reg_state->dp_dto1_modulo = REG_READ(DP_DTO_MODULO[1]);
+ dccg_reg_state->dp_dto1_phase = REG_READ(DP_DTO_PHASE[1]);
+ dccg_reg_state->dp_dto2_modulo = REG_READ(DP_DTO_MODULO[2]);
+ dccg_reg_state->dp_dto2_phase = REG_READ(DP_DTO_PHASE[2]);
+ dccg_reg_state->dp_dto3_modulo = REG_READ(DP_DTO_MODULO[3]);
+ dccg_reg_state->dp_dto3_phase = REG_READ(DP_DTO_PHASE[3]);
+ dccg_reg_state->dpiaclk_540m_dto_modulo = REG_READ(DPIACLK_540M_DTO_MODULO);
+ dccg_reg_state->dpiaclk_540m_dto_phase = REG_READ(DPIACLK_540M_DTO_PHASE);
+ dccg_reg_state->dpiaclk_810m_dto_modulo = REG_READ(DPIACLK_810M_DTO_MODULO);
+ dccg_reg_state->dpiaclk_810m_dto_phase = REG_READ(DPIACLK_810M_DTO_PHASE);
+ dccg_reg_state->dpiaclk_dto_cntl = REG_READ(DPIACLK_DTO_CNTL);
+ dccg_reg_state->dpiasymclk_cntl = REG_READ(DPIASYMCLK_CNTL);
+ dccg_reg_state->dppclk_cgtt_blk_ctrl_reg = REG_READ(DPPCLK_CGTT_BLK_CTRL_REG);
+ dccg_reg_state->dppclk_ctrl = REG_READ(DPPCLK_CTRL);
+ dccg_reg_state->dppclk_dto_ctrl = REG_READ(DPPCLK_DTO_CTRL);
+ dccg_reg_state->dppclk0_dto_param = REG_READ(DPPCLK_DTO_PARAM[0]);
+ dccg_reg_state->dppclk1_dto_param = REG_READ(DPPCLK_DTO_PARAM[1]);
+ dccg_reg_state->dppclk2_dto_param = REG_READ(DPPCLK_DTO_PARAM[2]);
+ dccg_reg_state->dppclk3_dto_param = REG_READ(DPPCLK_DTO_PARAM[3]);
+ dccg_reg_state->dprefclk_cgtt_blk_ctrl_reg = REG_READ(DPREFCLK_CGTT_BLK_CTRL_REG);
+ dccg_reg_state->dprefclk_cntl = REG_READ(DPREFCLK_CNTL);
+ dccg_reg_state->dpstreamclk_cntl = REG_READ(DPSTREAMCLK_CNTL);
+ dccg_reg_state->dscclk_dto_ctrl = REG_READ(DSCCLK_DTO_CTRL);
+ dccg_reg_state->dscclk0_dto_param = REG_READ(DSCCLK0_DTO_PARAM);
+ dccg_reg_state->dscclk1_dto_param = REG_READ(DSCCLK1_DTO_PARAM);
+ dccg_reg_state->dscclk2_dto_param = REG_READ(DSCCLK2_DTO_PARAM);
+ dccg_reg_state->dscclk3_dto_param = REG_READ(DSCCLK3_DTO_PARAM);
+ dccg_reg_state->dtbclk_dto_dbuf_en = REG_READ(DTBCLK_DTO_DBUF_EN);
+ dccg_reg_state->dtbclk_dto0_modulo = REG_READ(DTBCLK_DTO_MODULO[0]);
+ dccg_reg_state->dtbclk_dto0_phase = REG_READ(DTBCLK_DTO_PHASE[0]);
+ dccg_reg_state->dtbclk_dto1_modulo = REG_READ(DTBCLK_DTO_MODULO[1]);
+ dccg_reg_state->dtbclk_dto1_phase = REG_READ(DTBCLK_DTO_PHASE[1]);
+ dccg_reg_state->dtbclk_dto2_modulo = REG_READ(DTBCLK_DTO_MODULO[2]);
+ dccg_reg_state->dtbclk_dto2_phase = REG_READ(DTBCLK_DTO_PHASE[2]);
+ dccg_reg_state->dtbclk_dto3_modulo = REG_READ(DTBCLK_DTO_MODULO[3]);
+ dccg_reg_state->dtbclk_dto3_phase = REG_READ(DTBCLK_DTO_PHASE[3]);
+ dccg_reg_state->dtbclk_p_cntl = REG_READ(DTBCLK_P_CNTL);
+ dccg_reg_state->force_symclk_disable = REG_READ(FORCE_SYMCLK_DISABLE);
+ dccg_reg_state->hdmicharclk0_clock_cntl = REG_READ(HDMICHARCLK0_CLOCK_CNTL);
+ dccg_reg_state->hdmistreamclk_cntl = REG_READ(HDMISTREAMCLK_CNTL);
+ dccg_reg_state->hdmistreamclk0_dto_param = REG_READ(HDMISTREAMCLK0_DTO_PARAM);
+ dccg_reg_state->microsecond_time_base_div = REG_READ(MICROSECOND_TIME_BASE_DIV);
+ dccg_reg_state->millisecond_time_base_div = REG_READ(MILLISECOND_TIME_BASE_DIV);
+ dccg_reg_state->otg_pixel_rate_div = REG_READ(OTG_PIXEL_RATE_DIV);
+ dccg_reg_state->otg0_phypll_pixel_rate_cntl = REG_READ(OTG0_PHYPLL_PIXEL_RATE_CNTL);
+ dccg_reg_state->otg0_pixel_rate_cntl = REG_READ(OTG0_PIXEL_RATE_CNTL);
+ dccg_reg_state->otg1_phypll_pixel_rate_cntl = REG_READ(OTG1_PHYPLL_PIXEL_RATE_CNTL);
+ dccg_reg_state->otg1_pixel_rate_cntl = REG_READ(OTG1_PIXEL_RATE_CNTL);
+ dccg_reg_state->otg2_phypll_pixel_rate_cntl = REG_READ(OTG2_PHYPLL_PIXEL_RATE_CNTL);
+ dccg_reg_state->otg2_pixel_rate_cntl = REG_READ(OTG2_PIXEL_RATE_CNTL);
+ dccg_reg_state->otg3_phypll_pixel_rate_cntl = REG_READ(OTG3_PHYPLL_PIXEL_RATE_CNTL);
+ dccg_reg_state->otg3_pixel_rate_cntl = REG_READ(OTG3_PIXEL_RATE_CNTL);
+ dccg_reg_state->phyasymclk_clock_cntl = REG_READ(PHYASYMCLK_CLOCK_CNTL);
+ dccg_reg_state->phybsymclk_clock_cntl = REG_READ(PHYBSYMCLK_CLOCK_CNTL);
+ dccg_reg_state->phycsymclk_clock_cntl = REG_READ(PHYCSYMCLK_CLOCK_CNTL);
+ dccg_reg_state->phydsymclk_clock_cntl = REG_READ(PHYDSYMCLK_CLOCK_CNTL);
+ dccg_reg_state->phyesymclk_clock_cntl = REG_READ(PHYESYMCLK_CLOCK_CNTL);
+ dccg_reg_state->phyplla_pixclk_resync_cntl = REG_READ(PHYPLLA_PIXCLK_RESYNC_CNTL);
+ dccg_reg_state->phypllb_pixclk_resync_cntl = REG_READ(PHYPLLB_PIXCLK_RESYNC_CNTL);
+ dccg_reg_state->phypllc_pixclk_resync_cntl = REG_READ(PHYPLLC_PIXCLK_RESYNC_CNTL);
+ dccg_reg_state->phyplld_pixclk_resync_cntl = REG_READ(PHYPLLD_PIXCLK_RESYNC_CNTL);
+ dccg_reg_state->phyplle_pixclk_resync_cntl = REG_READ(PHYPLLE_PIXCLK_RESYNC_CNTL);
+ dccg_reg_state->refclk_cgtt_blk_ctrl_reg = REG_READ(REFCLK_CGTT_BLK_CTRL_REG);
+ dccg_reg_state->socclk_cgtt_blk_ctrl_reg = REG_READ(SOCCLK_CGTT_BLK_CTRL_REG);
+ dccg_reg_state->symclk_cgtt_blk_ctrl_reg = REG_READ(SYMCLK_CGTT_BLK_CTRL_REG);
+ dccg_reg_state->symclk_psp_cntl = REG_READ(SYMCLK_PSP_CNTL);
+ dccg_reg_state->symclk32_le_cntl = REG_READ(SYMCLK32_LE_CNTL);
+ dccg_reg_state->symclk32_se_cntl = REG_READ(SYMCLK32_SE_CNTL);
+ dccg_reg_state->symclka_clock_enable = REG_READ(SYMCLKA_CLOCK_ENABLE);
+ dccg_reg_state->symclkb_clock_enable = REG_READ(SYMCLKB_CLOCK_ENABLE);
+ dccg_reg_state->symclkc_clock_enable = REG_READ(SYMCLKC_CLOCK_ENABLE);
+ dccg_reg_state->symclkd_clock_enable = REG_READ(SYMCLKD_CLOCK_ENABLE);
+ dccg_reg_state->symclke_clock_enable = REG_READ(SYMCLKE_CLOCK_ENABLE);
+}
+
static const struct dccg_funcs dccg31_funcs = {
.update_dpp_dto = dccg31_update_dpp_dto,
.get_dccg_ref_freq = dccg31_get_dccg_ref_freq,
@@ -727,6 +849,7 @@ static const struct dccg_funcs dccg31_funcs = {
.set_dispclk_change_mode = dccg31_set_dispclk_change_mode,
.disable_dsc = dccg31_disable_dscclk,
.enable_dsc = dccg31_enable_dscclk,
+ .dccg_read_reg_state = dccg31_read_reg_state,
};
struct dccg *dccg31_create(
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h
index cd261051dc2c..bf659920d4cc 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn31/dcn31_dccg.h
@@ -236,4 +236,6 @@ void dccg31_disable_dscclk(struct dccg *dccg, int inst);
void dccg31_enable_dscclk(struct dccg *dccg, int inst);
+void dccg31_read_reg_state(struct dccg *dccg, struct dcn_dccg_reg_state *dccg_reg_state);
+
#endif //__DCN31_DCCG_H__
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c
index 8f6edd8e9beb..ef3db6beba25 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.c
@@ -377,7 +377,8 @@ static const struct dccg_funcs dccg314_funcs = {
.get_pixel_rate_div = dccg314_get_pixel_rate_div,
.trigger_dio_fifo_resync = dccg314_trigger_dio_fifo_resync,
.set_valid_pixel_rate = dccg314_set_valid_pixel_rate,
- .set_dtbclk_p_src = dccg314_set_dtbclk_p_src
+ .set_dtbclk_p_src = dccg314_set_dtbclk_p_src,
+ .dccg_read_reg_state = dccg31_read_reg_state
};
struct dccg *dccg314_create(
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.h
index 60ea1d248deb..a609635f35db 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.h
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn314/dcn314_dccg.h
@@ -74,8 +74,7 @@
SR(DCCG_GATE_DISABLE_CNTL3),\
SR(HDMISTREAMCLK0_DTO_PARAM),\
SR(OTG_PIXEL_RATE_DIV),\
- SR(DTBCLK_P_CNTL),\
- SR(DCCG_AUDIO_DTO_SOURCE)
+ SR(DTBCLK_P_CNTL)
#define DCCG_MASK_SH_LIST_DCN314_COMMON(mask_sh) \
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 0, mask_sh),\
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c
index de6d62401362..bd2f528137b2 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.c
@@ -1114,6 +1114,16 @@ static void dccg35_trigger_dio_fifo_resync(struct dccg *dccg)
if (dispclk_rdivider_value != 0)
REG_UPDATE(DENTIST_DISPCLK_CNTL, DENTIST_DISPCLK_WDIVIDER, dispclk_rdivider_value);
}
+static void dccg35_wait_for_dentist_change_done(
+ struct dccg *dccg)
+{
+ struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
+
+ uint32_t dentist_dispclk_value = REG_READ(DENTIST_DISPCLK_CNTL);
+
+ REG_WRITE(DENTIST_DISPCLK_CNTL, dentist_dispclk_value);
+ REG_WAIT(DENTIST_DISPCLK_CNTL, DENTIST_DISPCLK_CHG_DONE, 1, 50, 2000);
+}
static void dcn35_set_dppclk_enable(struct dccg *dccg,
uint32_t dpp_inst, uint32_t enable)
@@ -1174,9 +1184,9 @@ static void dccg35_update_dpp_dto(struct dccg *dccg, int dpp_inst,
dcn35_set_dppclk_enable(dccg, dpp_inst, true);
} else {
dcn35_set_dppclk_enable(dccg, dpp_inst, false);
- /*we have this in hwss: disable_plane*/
- //dccg35_set_dppclk_rcg(dccg, dpp_inst, true);
+ dccg35_set_dppclk_rcg(dccg, dpp_inst, true);
}
+ udelay(10);
dccg->pipe_dppclk_khz[dpp_inst] = req_dppclk;
}
@@ -1300,6 +1310,8 @@ static void dccg35_set_pixel_rate_div(
BREAK_TO_DEBUGGER();
return;
}
+ if (otg_inst < 4)
+ dccg35_wait_for_dentist_change_done(dccg);
}
static void dccg35_set_dtbclk_p_src(
@@ -1411,7 +1423,7 @@ static void dccg35_set_dtbclk_dto(
__func__, params->otg_inst, params->pixclk_khz,
params->ref_dtbclk_khz, req_dtbclk_khz, phase, modulo);
- } else {
+ } else if (!params->ref_dtbclk_khz && !req_dtbclk_khz) {
switch (params->otg_inst) {
case 0:
REG_UPDATE(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P0_GATE_DISABLE, 0);
@@ -1664,7 +1676,7 @@ static void dccg35_dpp_root_clock_control(
{
struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
- if (dccg->dpp_clock_gated[dpp_inst] == clock_on)
+ if (dccg->dpp_clock_gated[dpp_inst] != clock_on)
return;
if (clock_on) {
@@ -1682,9 +1694,12 @@ static void dccg35_dpp_root_clock_control(
DPPCLK0_DTO_PHASE, 0,
DPPCLK0_DTO_MODULO, 1);
/*we have this in hwss: disable_plane*/
- //dccg35_set_dppclk_rcg(dccg, dpp_inst, true);
+ dccg35_set_dppclk_rcg(dccg, dpp_inst, true);
}
+ // wait for clock to fully ramp
+ udelay(10);
+
dccg->dpp_clock_gated[dpp_inst] = !clock_on;
DC_LOG_DEBUG("%s: dpp_inst(%d) clock_on = %d\n", __func__, dpp_inst, clock_on);
}
@@ -2438,6 +2453,7 @@ static const struct dccg_funcs dccg35_funcs = {
.disable_symclk_se = dccg35_disable_symclk_se,
.set_dtbclk_p_src = dccg35_set_dtbclk_p_src,
.dccg_root_gate_disable_control = dccg35_root_gate_disable_control,
+ .dccg_read_reg_state = dccg31_read_reg_state,
};
struct dccg *dccg35_create(
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.h
index 51f98c5c51c4..7b9c36456cd9 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.h
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn35/dcn35_dccg.h
@@ -41,8 +41,9 @@
SR(SYMCLKA_CLOCK_ENABLE),\
SR(SYMCLKB_CLOCK_ENABLE),\
SR(SYMCLKC_CLOCK_ENABLE),\
- SR(SYMCLKD_CLOCK_ENABLE),\
- SR(SYMCLKE_CLOCK_ENABLE)
+ SR(SYMCLKD_CLOCK_ENABLE), \
+ SR(SYMCLKE_CLOCK_ENABLE),\
+ SR(SYMCLK_PSP_CNTL)
#define DCCG_MASK_SH_LIST_DCN35(mask_sh) \
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 0, mask_sh),\
@@ -231,6 +232,14 @@
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_GATE_DISABLE, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_STEP_DELAY, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_STEP_SIZE, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_FREQ_RAMP_DONE, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_MAX_ERRDET_CYCLES, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DCCG_FIFO_ERRDET_RESET, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DCCG_FIFO_ERRDET_STATE, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DCCG_FIFO_ERRDET_OVR_EN, mask_sh),\
+ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_CHG_FWD_CORR_DISABLE, mask_sh),\
struct dccg *dccg35_create(
struct dc_context *ctx,
diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c
index 0b8ed9b94d3c..663a18ee5162 100644
--- a/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c
+++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn401/dcn401_dccg.c
@@ -886,6 +886,7 @@ static const struct dccg_funcs dccg401_funcs = {
.enable_symclk_se = dccg401_enable_symclk_se,
.disable_symclk_se = dccg401_disable_symclk_se,
.set_dtbclk_p_src = dccg401_set_dtbclk_p_src,
+ .dccg_read_reg_state = dccg31_read_reg_state
};
struct dccg *dccg401_create(
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c
index a6006776333d..2dcf394edf22 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.c
@@ -283,7 +283,7 @@ struct abm *dce_abm_create(
const struct dce_abm_shift *abm_shift,
const struct dce_abm_mask *abm_mask)
{
- struct dce_abm *abm_dce = kzalloc(sizeof(*abm_dce), GFP_ATOMIC);
+ struct dce_abm *abm_dce = kzalloc(sizeof(*abm_dce), GFP_KERNEL);
if (abm_dce == NULL) {
BREAK_TO_DEBUGGER();
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c
index eeed840073fe..fcad61c618a1 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_audio.c
@@ -1143,7 +1143,8 @@ void dce_aud_wall_dto_setup(
REG_UPDATE(DCCG_AUDIO_DTO1_PHASE,
DCCG_AUDIO_DTO1_PHASE, clock_info.audio_dto_phase);
- REG_UPDATE(DCCG_AUDIO_DTO_SOURCE,
+ if (aud->masks->DCCG_AUDIO_DTO2_USE_512FBR_DTO)
+ REG_UPDATE(DCCG_AUDIO_DTO_SOURCE,
DCCG_AUDIO_DTO2_USE_512FBR_DTO, 1);
}
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
index a8e79104b684..5f8fba45d98d 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_dmcu.c
@@ -1126,7 +1126,7 @@ struct dmcu *dcn10_dmcu_create(
const struct dce_dmcu_shift *dmcu_shift,
const struct dce_dmcu_mask *dmcu_mask)
{
- struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_ATOMIC);
+ struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_KERNEL);
if (dmcu_dce == NULL) {
BREAK_TO_DEBUGGER();
@@ -1147,7 +1147,7 @@ struct dmcu *dcn20_dmcu_create(
const struct dce_dmcu_shift *dmcu_shift,
const struct dce_dmcu_mask *dmcu_mask)
{
- struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_ATOMIC);
+ struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_KERNEL);
if (dmcu_dce == NULL) {
BREAK_TO_DEBUGGER();
@@ -1168,7 +1168,7 @@ struct dmcu *dcn21_dmcu_create(
const struct dce_dmcu_shift *dmcu_shift,
const struct dce_dmcu_mask *dmcu_mask)
{
- struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_ATOMIC);
+ struct dce_dmcu *dmcu_dce = kzalloc(sizeof(*dmcu_dce), GFP_KERNEL);
if (dmcu_dce == NULL) {
BREAK_TO_DEBUGGER();
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c
index 0c50fe266c8a..87dbb8d7ed27 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.c
@@ -302,6 +302,10 @@ static void setup_panel_mode(
if (ctx->dc->caps.psp_setup_panel_mode)
return;
+ /* The code below is only applicable to encoders with a digital transmitter. */
+ if (enc110->base.transmitter == TRANSMITTER_UNKNOWN)
+ return;
+
ASSERT(REG(DP_DPHY_INTERNAL_CTRL));
value = REG_READ(DP_DPHY_INTERNAL_CTRL);
@@ -804,6 +808,33 @@ bool dce110_link_encoder_validate_dp_output(
return true;
}
+static bool dce110_link_encoder_validate_rgb_output(
+ const struct dce110_link_encoder *enc110,
+ const struct dc_crtc_timing *crtc_timing)
+{
+ /* When the VBIOS doesn't specify any limits, use 400 MHz.
+ * The value comes from amdgpu_atombios_get_clock_info.
+ */
+ uint32_t max_pixel_clock_khz = 400000;
+
+ if (enc110->base.ctx->dc_bios->fw_info_valid &&
+ enc110->base.ctx->dc_bios->fw_info.max_pixel_clock) {
+ max_pixel_clock_khz =
+ enc110->base.ctx->dc_bios->fw_info.max_pixel_clock;
+ }
+
+ if (crtc_timing->pix_clk_100hz > max_pixel_clock_khz * 10)
+ return false;
+
+ if (crtc_timing->display_color_depth != COLOR_DEPTH_888)
+ return false;
+
+ if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB)
+ return false;
+
+ return true;
+}
+
void dce110_link_encoder_construct(
struct dce110_link_encoder *enc110,
const struct encoder_init_data *init_data,
@@ -824,6 +855,7 @@ void dce110_link_encoder_construct(
enc110->base.connector = init_data->connector;
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
+ enc110->base.analog_engine = init_data->analog_engine;
enc110->base.features = *enc_features;
@@ -847,6 +879,11 @@ void dce110_link_encoder_construct(
SIGNAL_TYPE_EDP |
SIGNAL_TYPE_HDMI_TYPE_A;
+ if ((enc110->base.connector.id == CONNECTOR_ID_DUAL_LINK_DVII ||
+ enc110->base.connector.id == CONNECTOR_ID_SINGLE_LINK_DVII) &&
+ enc110->base.analog_engine != ENGINE_ID_UNKNOWN)
+ enc110->base.output_signals |= SIGNAL_TYPE_RGB;
+
/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
* SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
* SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
@@ -885,6 +922,13 @@ void dce110_link_encoder_construct(
enc110->base.preferred_engine = ENGINE_ID_DIGG;
break;
default:
+ if (init_data->analog_engine != ENGINE_ID_UNKNOWN) {
+ /* The connector is analog-only, ie. VGA */
+ enc110->base.preferred_engine = init_data->analog_engine;
+ enc110->base.output_signals = SIGNAL_TYPE_RGB;
+ enc110->base.transmitter = TRANSMITTER_UNKNOWN;
+ break;
+ }
ASSERT_CRITICAL(false);
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
}
@@ -939,6 +983,10 @@ bool dce110_link_encoder_validate_output_with_stream(
is_valid = dce110_link_encoder_validate_dp_output(
enc110, &stream->timing);
break;
+ case SIGNAL_TYPE_RGB:
+ is_valid = dce110_link_encoder_validate_rgb_output(
+ enc110, &stream->timing);
+ break;
case SIGNAL_TYPE_EDP:
case SIGNAL_TYPE_LVDS:
is_valid = stream->timing.pixel_encoding == PIXEL_ENCODING_RGB;
@@ -969,6 +1017,10 @@ void dce110_link_encoder_hw_init(
cntl.coherent = false;
cntl.hpd_sel = enc110->base.hpd_source;
+ /* The code below is only applicable to encoders with a digital transmitter. */
+ if (enc110->base.transmitter == TRANSMITTER_UNKNOWN)
+ return;
+
if (enc110->base.connector.id == CONNECTOR_ID_EDP)
cntl.signal = SIGNAL_TYPE_EDP;
@@ -1034,6 +1086,8 @@ void dce110_link_encoder_setup(
/* DP MST */
REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 5);
break;
+ case SIGNAL_TYPE_RGB:
+ break;
default:
ASSERT_CRITICAL(false);
/* invalid mode ! */
@@ -1282,6 +1336,24 @@ void dce110_link_encoder_disable_output(
struct bp_transmitter_control cntl = { 0 };
enum bp_result result;
+ switch (enc->analog_engine) {
+ case ENGINE_ID_DACA:
+ REG_UPDATE(DAC_ENABLE, DAC_ENABLE, 0);
+ break;
+ case ENGINE_ID_DACB:
+ /* DACB doesn't seem to be present on DCE6+,
+ * although there are references to it in the register file.
+ */
+ DC_LOG_ERROR("%s DACB is unsupported\n", __func__);
+ break;
+ default:
+ break;
+ }
+
+ /* The code below only applies to connectors that support digital signals. */
+ if (enc->transmitter == TRANSMITTER_UNKNOWN)
+ return;
+
if (!dce110_is_dig_enabled(enc)) {
/* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */
return;
@@ -1726,6 +1798,7 @@ void dce60_link_encoder_construct(
enc110->base.connector = init_data->connector;
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
+ enc110->base.analog_engine = init_data->analog_engine;
enc110->base.features = *enc_features;
@@ -1749,6 +1822,11 @@ void dce60_link_encoder_construct(
SIGNAL_TYPE_EDP |
SIGNAL_TYPE_HDMI_TYPE_A;
+ if ((enc110->base.connector.id == CONNECTOR_ID_DUAL_LINK_DVII ||
+ enc110->base.connector.id == CONNECTOR_ID_SINGLE_LINK_DVII) &&
+ enc110->base.analog_engine != ENGINE_ID_UNKNOWN)
+ enc110->base.output_signals |= SIGNAL_TYPE_RGB;
+
/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
* SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
* SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
@@ -1787,6 +1865,13 @@ void dce60_link_encoder_construct(
enc110->base.preferred_engine = ENGINE_ID_DIGG;
break;
default:
+ if (init_data->analog_engine != ENGINE_ID_UNKNOWN) {
+ /* The connector is analog-only, ie. VGA */
+ enc110->base.preferred_engine = init_data->analog_engine;
+ enc110->base.output_signals = SIGNAL_TYPE_RGB;
+ enc110->base.transmitter = TRANSMITTER_UNKNOWN;
+ break;
+ }
ASSERT_CRITICAL(false);
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h
index 261c70e01e33..c58b69bc319b 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_link_encoder.h
@@ -101,18 +101,21 @@
SRI(DP_SEC_CNTL, DP, id), \
SRI(DP_VID_STREAM_CNTL, DP, id), \
SRI(DP_DPHY_FAST_TRAINING, DP, id), \
- SRI(DP_SEC_CNTL1, DP, id)
+ SRI(DP_SEC_CNTL1, DP, id), \
+ SR(DAC_ENABLE)
#endif
#define LE_DCE80_REG_LIST(id)\
SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \
- LE_COMMON_REG_LIST_BASE(id)
+ LE_COMMON_REG_LIST_BASE(id), \
+ SR(DAC_ENABLE)
#define LE_DCE100_REG_LIST(id)\
LE_COMMON_REG_LIST_BASE(id), \
SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \
SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \
- SR(DCI_MEM_PWR_STATUS)
+ SR(DCI_MEM_PWR_STATUS), \
+ SR(DAC_ENABLE)
#define LE_DCE110_REG_LIST(id)\
LE_COMMON_REG_LIST_BASE(id), \
@@ -181,6 +184,9 @@ struct dce110_link_enc_registers {
uint32_t DP_DPHY_BS_SR_SWAP_CNTL;
uint32_t DP_DPHY_HBR2_PATTERN_CONTROL;
uint32_t DP_SEC_CNTL1;
+
+ /* DAC registers */
+ uint32_t DAC_ENABLE;
};
struct dce110_link_encoder {
@@ -215,10 +221,6 @@ bool dce110_link_encoder_validate_dvi_output(
enum signal_type signal,
const struct dc_crtc_timing *crtc_timing);
-bool dce110_link_encoder_validate_rgb_output(
- const struct dce110_link_encoder *enc110,
- const struct dc_crtc_timing *crtc_timing);
-
bool dce110_link_encoder_validate_dp_output(
const struct dce110_link_encoder *enc110,
const struct dc_crtc_timing *crtc_timing);
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
index 1130d7619b26..574618d5d4a4 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c
@@ -1567,3 +1567,17 @@ void dce110_stream_encoder_construct(
enc110->se_shift = se_shift;
enc110->se_mask = se_mask;
}
+
+static const struct stream_encoder_funcs dce110_an_str_enc_funcs = {};
+
+void dce110_analog_stream_encoder_construct(
+ struct dce110_stream_encoder *enc110,
+ struct dc_context *ctx,
+ struct dc_bios *bp,
+ enum engine_id eng_id)
+{
+ enc110->base.funcs = &dce110_an_str_enc_funcs;
+ enc110->base.ctx = ctx;
+ enc110->base.id = eng_id;
+ enc110->base.bp = bp;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h
index cc5020a8e1e1..068de1392121 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h
+++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h
@@ -708,6 +708,11 @@ void dce110_stream_encoder_construct(
const struct dce_stream_encoder_shift *se_shift,
const struct dce_stream_encoder_mask *se_mask);
+void dce110_analog_stream_encoder_construct(
+ struct dce110_stream_encoder *enc110,
+ struct dc_context *ctx,
+ struct dc_bios *bp,
+ enum engine_id eng_id);
void dce110_se_audio_mute_control(
struct stream_encoder *enc, bool mute);
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c
index d37ecfdde4f1..5bfa2b0d2afd 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.c
@@ -61,10 +61,9 @@ void dmub_hw_lock_mgr_inbox0_cmd(struct dc_dmub_srv *dmub_srv,
dc_dmub_srv_wait_for_inbox0_ack(dmub_srv);
}
-bool should_use_dmub_lock(struct dc_link *link)
+bool dmub_hw_lock_mgr_does_link_require_lock(const struct dc *dc, const struct dc_link *link)
{
- /* ASIC doesn't support DMUB */
- if (!link->ctx->dmub_srv)
+ if (!link)
return false;
if (link->psr_settings.psr_version == DC_PSR_VERSION_SU_1)
@@ -73,16 +72,38 @@ bool should_use_dmub_lock(struct dc_link *link)
if (link->replay_settings.replay_feature_enabled)
return true;
- /* only use HW lock for PSR1 on single eDP */
if (link->psr_settings.psr_version == DC_PSR_VERSION_1) {
struct dc_link *edp_links[MAX_NUM_EDP];
int edp_num;
- dc_get_edp_links(link->dc, edp_links, &edp_num);
-
+ dc_get_edp_links(dc, edp_links, &edp_num);
if (edp_num == 1)
return true;
}
+ return false;
+}
+bool dmub_hw_lock_mgr_does_context_require_lock(const struct dc *dc, const struct dc_state *context)
+{
+ if (!context)
+ return false;
+ for (int i = 0; i < context->stream_count; i++) {
+ const struct dc_link *link = context->streams[i]->link;
+
+ if (dmub_hw_lock_mgr_does_link_require_lock(dc, link))
+ return true;
+ }
return false;
}
+
+bool should_use_dmub_inbox1_lock(const struct dc *dc, const struct dc_link *link)
+{
+ /* ASIC doesn't support DMUB */
+ if (!dc->ctx->dmub_srv)
+ return false;
+
+ if (dc->ctx->dce_version >= DCN_VERSION_4_01)
+ return false;
+
+ return dmub_hw_lock_mgr_does_link_require_lock(dc, link);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h
index 5a72b168fb4a..4c80ca8484ad 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h
+++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_hw_lock_mgr.h
@@ -37,6 +37,16 @@ void dmub_hw_lock_mgr_cmd(struct dc_dmub_srv *dmub_srv,
void dmub_hw_lock_mgr_inbox0_cmd(struct dc_dmub_srv *dmub_srv,
union dmub_inbox0_cmd_lock_hw hw_lock_cmd);
-bool should_use_dmub_lock(struct dc_link *link);
+/**
+ * should_use_dmub_inbox1_lock() - Checks if the DMCUB hardware lock via inbox1 should be used.
+ *
+ * @dc: pointer to DC object
+ * @link: optional pointer to the link object to check for enabled link features
+ *
+ * Return: true if the inbox1 lock should be used, false otherwise
+ */
+bool should_use_dmub_inbox1_lock(const struct dc *dc, const struct dc_link *link);
+bool dmub_hw_lock_mgr_does_link_require_lock(const struct dc *dc, const struct dc_link *link);
+bool dmub_hw_lock_mgr_does_context_require_lock(const struct dc *dc, const struct dc_state *context);
#endif /*_DMUB_HW_LOCK_MGR_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c
index f9542edff14b..cf1372aaff6c 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c
+++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.c
@@ -213,7 +213,8 @@ static bool dmub_replay_copy_settings(struct dmub_replay *dmub,
*/
static void dmub_replay_set_coasting_vtotal(struct dmub_replay *dmub,
uint32_t coasting_vtotal,
- uint8_t panel_inst)
+ uint8_t panel_inst,
+ uint16_t frame_skip_number)
{
union dmub_rb_cmd cmd;
struct dc_context *dc = dmub->ctx;
@@ -227,6 +228,7 @@ static void dmub_replay_set_coasting_vtotal(struct dmub_replay *dmub,
pCmd->header.payload_bytes = sizeof(struct dmub_cmd_replay_set_coasting_vtotal_data);
pCmd->replay_set_coasting_vtotal_data.coasting_vtotal = (coasting_vtotal & 0xFFFF);
pCmd->replay_set_coasting_vtotal_data.coasting_vtotal_high = (coasting_vtotal & 0xFFFF0000) >> 16;
+ pCmd->replay_set_coasting_vtotal_data.frame_skip_number = frame_skip_number;
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}
@@ -283,7 +285,7 @@ static void dmub_replay_residency(struct dmub_replay *dmub, uint8_t panel_inst,
* Set REPLAY power optimization flags and coasting vtotal.
*/
static void dmub_replay_set_power_opt_and_coasting_vtotal(struct dmub_replay *dmub,
- unsigned int power_opt, uint8_t panel_inst, uint32_t coasting_vtotal)
+ unsigned int power_opt, uint8_t panel_inst, uint32_t coasting_vtotal, uint16_t frame_skip_number)
{
union dmub_rb_cmd cmd;
struct dc_context *dc = dmub->ctx;
@@ -301,6 +303,7 @@ static void dmub_replay_set_power_opt_and_coasting_vtotal(struct dmub_replay *dm
pCmd->replay_set_power_opt_data.panel_inst = panel_inst;
pCmd->replay_set_coasting_vtotal_data.coasting_vtotal = (coasting_vtotal & 0xFFFF);
pCmd->replay_set_coasting_vtotal_data.coasting_vtotal_high = (coasting_vtotal & 0xFFFF0000) >> 16;
+ pCmd->replay_set_coasting_vtotal_data.frame_skip_number = frame_skip_number;
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}
@@ -384,6 +387,19 @@ static void dmub_replay_send_cmd(struct dmub_replay *dmub,
cmd.replay_disabled_adaptive_sync_sdp.data.force_disabled =
cmd_element->disabled_adaptive_sync_sdp_data.force_disabled;
break;
+ case Replay_Set_Version:
+ //Header
+ cmd.replay_set_version.header.sub_type =
+ DMUB_CMD__REPLAY_SET_VERSION;
+ cmd.replay_set_version.header.payload_bytes =
+ sizeof(struct dmub_rb_cmd_replay_set_version) -
+ sizeof(struct dmub_cmd_header);
+ //Cmd Body
+ cmd.replay_set_version.replay_set_version_data.panel_inst =
+ cmd_element->version_data.panel_inst;
+ cmd.replay_set_version.replay_set_version_data.version =
+ cmd_element->version_data.version;
+ break;
case Replay_Set_General_Cmd:
//Header
cmd.replay_set_general_cmd.header.sub_type =
diff --git a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h
index e6346c0ffc0e..07c79739a980 100644
--- a/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h
+++ b/drivers/gpu/drm/amd/display/dc/dce/dmub_replay.h
@@ -27,11 +27,12 @@ struct dmub_replay_funcs {
void (*replay_send_cmd)(struct dmub_replay *dmub,
enum replay_FW_Message_type msg, union dmub_replay_cmd_set *cmd_element);
void (*replay_set_coasting_vtotal)(struct dmub_replay *dmub, uint32_t coasting_vtotal,
- uint8_t panel_inst);
+ uint8_t panel_inst, uint16_t frame_skip_number);
void (*replay_residency)(struct dmub_replay *dmub,
uint8_t panel_inst, uint32_t *residency, const bool is_start, const enum pr_residency_mode mode);
void (*replay_set_power_opt_and_coasting_vtotal)(struct dmub_replay *dmub,
- unsigned int power_opt, uint8_t panel_inst, uint32_t coasting_vtotal);
+ unsigned int power_opt, uint8_t panel_inst, uint32_t coasting_vtotal,
+ uint16_t frame_skip_number);
};
struct dmub_replay *dmub_replay_create(struct dc_context *ctx);
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/Makefile b/drivers/gpu/drm/amd/display/dc/dml2/Makefile
deleted file mode 100644
index 4c21ce42054c..000000000000
--- a/drivers/gpu/drm/amd/display/dc/dml2/Makefile
+++ /dev/null
@@ -1,141 +0,0 @@
-# SPDX-License-Identifier: MIT */
-#
-# Copyright 2023 Advanced Micro Devices, 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.
-#
-# Authors: AMD
-#
-# Makefile for dml2.
-
-dml2_ccflags := $(CC_FLAGS_FPU)
-dml2_rcflags := $(CC_FLAGS_NO_FPU)
-
-ifneq ($(CONFIG_FRAME_WARN),0)
- ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)),y)
- ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
- frame_warn_limit := 4096
- else
- frame_warn_limit := 3072
- endif
- else
- frame_warn_limit := 2048
- endif
-
- ifeq ($(call test-lt, $(CONFIG_FRAME_WARN), $(frame_warn_limit)),y)
- frame_warn_flag := -Wframe-larger-than=$(frame_warn_limit)
- endif
-endif
-
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/src/dml2_core
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/src/dml2_mcg/
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/src/dml2_dpmm/
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/src/dml2_pmo/
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/src/dml2_standalone_libraries/
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/src/inc
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/inc
-subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2/dml21/
-
-CFLAGS_$(AMDDALPATH)/dc/dml2/display_mode_core.o := $(dml2_ccflags) $(frame_warn_flag)
-CFLAGS_$(AMDDALPATH)/dc/dml2/display_mode_util.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml2_wrapper.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml2_utils.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml2_policy.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml2_translation_helper.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml2_mall_phantom.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml_display_rq_dlg_calc.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml2_dc_resource_mgmt.o := $(dml2_ccflags)
-
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/display_mode_core.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/display_mode_util.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml2_wrapper.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml2_utils.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml2_policy.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml2_translation_helper.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml2_mall_phantom.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml_display_rq_dlg_calc.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml2_dc_resource_mgmt.o := $(dml2_rcflags)
-
-DML2 = display_mode_core.o display_mode_util.o dml2_wrapper.o \
- dml2_utils.o dml2_policy.o dml2_translation_helper.o dml2_dc_resource_mgmt.o dml2_mall_phantom.o \
- dml_display_rq_dlg_calc.o
-
-AMD_DAL_DML2 = $(addprefix $(AMDDALPATH)/dc/dml2/,$(DML2))
-
-AMD_DISPLAY_FILES += $(AMD_DAL_DML2)
-
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_ccflags) $(frame_warn_flag)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_ccflags) $(frame_warn_flag)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_top/dml2_top_interfaces.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_factory.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/src/dml21_wrapper.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/dml21_translation_helper.o := $(dml2_ccflags)
-CFLAGS_$(AMDDALPATH)/dc/dml2/dml21/dml21_utils.o := $(dml2_ccflags)
-
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_factory.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_top/dml2_top_interfaces.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/src/dml21_wrapper.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/dml21_translation_helper.o := $(dml2_rcflags)
-CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2/dml21/dml21_utils.o := $(dml2_rcflags)
-
-DML21 := src/dml2_top/dml2_top_interfaces.o
-DML21 += src/dml2_top/dml2_top_soc15.o
-DML21 += src/dml2_core/dml2_core_dcn4.o
-DML21 += src/dml2_core/dml2_core_utils.o
-DML21 += src/dml2_core/dml2_core_factory.o
-DML21 += src/dml2_core/dml2_core_dcn4_calcs.o
-DML21 += src/dml2_dpmm/dml2_dpmm_dcn4.o
-DML21 += src/dml2_dpmm/dml2_dpmm_factory.o
-DML21 += src/dml2_mcg/dml2_mcg_dcn4.o
-DML21 += src/dml2_mcg/dml2_mcg_factory.o
-DML21 += src/dml2_pmo/dml2_pmo_dcn3.o
-DML21 += src/dml2_pmo/dml2_pmo_factory.o
-DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o
-DML21 += src/dml2_standalone_libraries/lib_float_math.o
-DML21 += dml21_translation_helper.o
-DML21 += dml21_wrapper.o
-DML21 += dml21_utils.o
-
-AMD_DAL_DML21 = $(addprefix $(AMDDALPATH)/dc/dml2/dml21/,$(DML21))
-
-AMD_DISPLAY_FILES += $(AMD_DAL_DML21)
-
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
new file mode 100644
index 000000000000..97e068b6bf6b
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/Makefile
@@ -0,0 +1,140 @@
+# SPDX-License-Identifier: MIT */
+#
+# Copyright 2023 Advanced Micro Devices, 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.
+#
+# Authors: AMD
+#
+# Makefile for dml2.
+
+dml2_ccflags := $(CC_FLAGS_FPU)
+dml2_rcflags := $(CC_FLAGS_NO_FPU)
+
+ifneq ($(CONFIG_FRAME_WARN),0)
+ ifeq ($(filter y,$(CONFIG_KASAN)$(CONFIG_KCSAN)),y)
+ ifeq ($(CONFIG_CC_IS_CLANG)$(CONFIG_COMPILE_TEST),yy)
+ frame_warn_limit := 4096
+ else
+ frame_warn_limit := 3072
+ endif
+ else
+ frame_warn_limit := 2056
+ endif
+
+ ifeq ($(call test-lt, $(CONFIG_FRAME_WARN), $(frame_warn_limit)),y)
+ frame_warn_flag := -Wframe-larger-than=$(frame_warn_limit)
+ endif
+endif
+
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_core
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_mcg/
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_dpmm/
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_pmo/
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_standalone_libraries/
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/inc
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/inc
+subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/
+
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/display_mode_core.o := $(dml2_ccflags) $(frame_warn_flag)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/display_mode_util.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_wrapper.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_utils.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_policy.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_translation_helper.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_mall_phantom.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml_display_rq_dlg_calc.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml2_dc_resource_mgmt.o := $(dml2_ccflags)
+
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/display_mode_core.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/display_mode_util.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_wrapper.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_utils.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_policy.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_translation_helper.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_mall_phantom.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml_display_rq_dlg_calc.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml2_dc_resource_mgmt.o := $(dml2_rcflags)
+
+DML2 = display_mode_core.o display_mode_util.o dml2_wrapper.o \
+ dml2_utils.o dml2_policy.o dml2_translation_helper.o dml2_dc_resource_mgmt.o dml2_mall_phantom.o \
+ dml_display_rq_dlg_calc.o
+
+AMD_DAL_DML2 = $(addprefix $(AMDDALPATH)/dc/dml2_0/,$(DML2))
+
+AMD_DISPLAY_FILES += $(AMD_DAL_DML2)
+
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_ccflags) $(frame_warn_flag)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_ccflags) $(frame_warn_flag)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml21_wrapper.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_translation_helper.o := $(dml2_ccflags)
+CFLAGS_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_utils.o := $(dml2_ccflags)
+
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/src/dml21_wrapper.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_translation_helper.o := $(dml2_rcflags)
+CFLAGS_REMOVE_$(AMDDALPATH)/dc/dml2_0/dml21/dml21_utils.o := $(dml2_rcflags)
+
+DML21 := src/dml2_top/dml2_top_interfaces.o
+DML21 += src/dml2_top/dml2_top_soc15.o
+DML21 += src/dml2_core/dml2_core_dcn4.o
+DML21 += src/dml2_core/dml2_core_utils.o
+DML21 += src/dml2_core/dml2_core_factory.o
+DML21 += src/dml2_core/dml2_core_dcn4_calcs.o
+DML21 += src/dml2_dpmm/dml2_dpmm_dcn4.o
+DML21 += src/dml2_dpmm/dml2_dpmm_factory.o
+DML21 += src/dml2_mcg/dml2_mcg_dcn4.o
+DML21 += src/dml2_mcg/dml2_mcg_factory.o
+DML21 += src/dml2_pmo/dml2_pmo_dcn3.o
+DML21 += src/dml2_pmo/dml2_pmo_factory.o
+DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o
+DML21 += src/dml2_standalone_libraries/lib_float_math.o
+DML21 += dml21_translation_helper.o
+DML21 += dml21_wrapper.o
+DML21 += dml21_utils.o
+
+AMD_DAL_DML21 = $(addprefix $(AMDDALPATH)/dc/dml2_0/dml21/,$(DML21))
+
+AMD_DISPLAY_FILES += $(AMD_DAL_DML21)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/cmntypes.h b/drivers/gpu/drm/amd/display/dc/dml2_0/cmntypes.h
index e450445bc05d..b954c9648fbe 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/cmntypes.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/cmntypes.h
@@ -53,17 +53,17 @@ typedef const void *const_pvoid;
typedef const char *const_pchar;
typedef struct rgba_struct {
- uint8 a;
- uint8 r;
- uint8 g;
- uint8 b;
+ uint8 a;
+ uint8 r;
+ uint8 g;
+ uint8 b;
} rgba_t;
typedef struct {
- uint8 blue;
- uint8 green;
- uint8 red;
- uint8 alpha;
+ uint8 blue;
+ uint8 green;
+ uint8 red;
+ uint8 alpha;
} gen_color_t;
typedef union {
@@ -87,7 +87,7 @@ typedef union {
} uintfloat64;
#ifndef UNREFERENCED_PARAMETER
-#define UNREFERENCED_PARAMETER(x) x = x
+#define UNREFERENCED_PARAMETER(x) (x = x)
#endif
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c
index 4b9b2e84d381..c468f492b876 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.c
@@ -10205,6 +10205,7 @@ dml_bool_t dml_get_is_phantom_pipe(struct display_mode_lib_st *mode_lib, dml_uin
return (mode_lib->ms.cache_display_cfg.plane.UseMALLForPStateChange[plane_idx] == dml_use_mall_pstate_change_phantom_pipe);
}
+
#define dml_get_per_surface_var_func(variable, type, interval_var) type dml_get_##variable(struct display_mode_lib_st *mode_lib, dml_uint_t surface_idx) \
{ \
dml_uint_t plane_idx; \
@@ -10333,3 +10334,4 @@ dml_get_per_surface_var_func(bigk_fragment_size, dml_uint_t, mode_lib->mp.BIGK_F
dml_get_per_surface_var_func(dpte_bytes_per_row, dml_uint_t, mode_lib->mp.PixelPTEBytesPerRow);
dml_get_per_surface_var_func(meta_bytes_per_row, dml_uint_t, mode_lib->mp.MetaRowByte);
dml_get_per_surface_var_func(det_buffer_size_kbytes, dml_uint_t, mode_lib->ms.DETBufferSizeInKByte);
+
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.h b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.h
index a38ed89c47a9..a38ed89c47a9 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core_structs.h b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core_structs.h
index dbeb08466092..5b40dcdc4406 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_core_structs.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_core_structs.h
@@ -274,7 +274,6 @@ enum dml_clk_cfg_policy {
dml_use_state_freq = 2
};
-
struct soc_state_bounding_box_st {
dml_float_t socclk_mhz;
dml_float_t dscclk_mhz;
@@ -1894,7 +1893,7 @@ struct display_mode_lib_scratch_st {
struct CalculatePrefetchSchedule_params_st CalculatePrefetchSchedule_params;
};
-/// @brief Represent the overall soc/ip enviroment. It contains data structure represent the soc/ip characteristic and also structures that hold calculation output
+/// @brief Represent the overall soc/ip environment. It contains data structure represent the soc/ip characteristic and also structures that hold calculation output
struct display_mode_lib_st {
dml_uint_t project;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_lib_defines.h b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_lib_defines.h
index 14d389525296..e574c81edf5e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_lib_defines.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_lib_defines.h
@@ -52,7 +52,7 @@
#define __DML_VBA_DEBUG__
#define __DML_VBA_ENABLE_INLINE_CHECK_ 0
#define __DML_VBA_MIN_VSTARTUP__ 9 //<brief At which vstartup the DML start to try if the mode can be supported
-#define __DML_ARB_TO_RET_DELAY__ 7 + 95 //<brief Delay in DCFCLK from ARB to DET (1st num is ARB to SDPIF, 2nd number is SDPIF to DET)
+#define __DML_ARB_TO_RET_DELAY__ (7 + 95) //<brief Delay in DCFCLK from ARB to DET (1st num is ARB to SDPIF, 2nd number is SDPIF to DET)
#define __DML_MIN_DCFCLK_FACTOR__ 1.15 //<brief fudge factor for min dcfclk calclation
#define __DML_MAX_VRATIO_PRE__ 4.0 //<brief Prefetch schedule max vratio
#define __DML_MAX_VRATIO_PRE_OTO__ 4.0 //<brief Prefetch schedule max vratio for one to one scheduling calculation for prefetch
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_util.c b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c
index 89890c88fd66..89890c88fd66 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_util.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_util.h b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.h
index 113b0265e1d1..a82b49cf7fb0 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/display_mode_util.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/display_mode_util.h
@@ -30,7 +30,6 @@
#include "display_mode_core_structs.h"
#include "cmntypes.h"
-
#include "dml_assert.h"
#include "dml_logging.h"
@@ -72,5 +71,4 @@ __DML_DLL_EXPORT__ dml_uint_t dml_get_plane_idx(const struct display_mode_lib_st
__DML_DLL_EXPORT__ dml_uint_t dml_get_pipe_idx(const struct display_mode_lib_st *mode_lib, dml_uint_t plane_idx);
__DML_DLL_EXPORT__ void dml_calc_pipe_plane_mapping(const struct dml_hw_resource_st *hw, dml_uint_t *pipe_plane);
-
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c
index bf5e7f4e0416..bf5e7f4e0416 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.h
index 9880d3e0398e..9880d3e0398e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_translation_helper.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_translation_helper.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c
index ee721606b883..ee721606b883 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_utils.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_utils.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h
index 4bff52eaaef8..4bff52eaaef8 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_utils.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c
index 08f7f03b1023..798abb2b2e67 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.c
@@ -224,9 +224,7 @@ static bool dml21_mode_check_and_programming(const struct dc *in_dc, struct dc_s
dml_ctx->config.svp_pstate.callbacks.release_phantom_streams_and_planes(in_dc, context);
/* Populate stream, plane mappings and other fields in display config. */
- DC_FP_START();
result = dml21_map_dc_state_into_dml_display_cfg(in_dc, context, dml_ctx);
- DC_FP_END();
if (!result)
return false;
@@ -281,9 +279,7 @@ static bool dml21_check_mode_support(const struct dc *in_dc, struct dc_state *co
dml_ctx->config.svp_pstate.callbacks.release_phantom_streams_and_planes(in_dc, context);
mode_support->dml2_instance = dml_init->dml2_instance;
- DC_FP_START();
dml21_map_dc_state_into_dml_display_cfg(in_dc, context, dml_ctx);
- DC_FP_END();
dml_ctx->v21.mode_programming.dml2_instance->scratch.build_mode_programming_locals.mode_programming_params.programming = dml_ctx->v21.mode_programming.programming;
DC_FP_START();
is_supported = dml2_check_mode_supported(mode_support);
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h
index 15f92029d2e5..15f92029d2e5 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/dml21_wrapper.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/bounding_boxes/dcn4_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn4_soc_bb.h
index 793e1c038efd..16a4f97bca4e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/bounding_boxes/dcn4_soc_bb.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn4_soc_bb.h
@@ -2,7 +2,6 @@
//
// Copyright 2024 Advanced Micro Devices, Inc.
-
#ifndef __DML_DML_DCN4_SOC_BB__
#define __DML_DML_DCN4_SOC_BB__
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml2_external_lib_deps.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml2_external_lib_deps.h
index 281d7ad230d8..281d7ad230d8 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml2_external_lib_deps.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml2_external_lib_deps.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top.h
index a64ec4dcf11a..a64ec4dcf11a 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_dchub_registers.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h
index 91955bbe24b8..bf57df42d1d9 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_dchub_registers.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_dchub_registers.h
@@ -46,7 +46,6 @@ struct dml2_display_dlg_regs {
uint32_t dst_y_delta_drq_limit;
uint32_t refcyc_per_vm_dmdata;
uint32_t dmdata_dl_delta;
- uint32_t dst_y_svp_drq_limit;
// MRQ
uint32_t refcyc_per_meta_chunk_vblank_l;
@@ -122,6 +121,8 @@ struct dml2_display_rq_regs {
uint32_t crq_expansion_mode;
uint32_t plane1_base_address;
uint32_t unbounded_request_enabled;
+ bool pte_buffer_mode;
+ bool force_one_row_for_frame;
// MRQ
uint32_t mrq_expansion_mode;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_display_cfg_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h
index e8dc6471c0be..35aa954248cd 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_display_cfg_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_display_cfg_types.h
@@ -49,6 +49,11 @@ enum dml2_source_format_class {
dml2_422_packed_12 = 18
};
+enum dml2_sample_positioning {
+ dml2_interstitial = 0,
+ dml2_cosited = 1
+};
+
enum dml2_rotation_angle {
dml2_rotation_0 = 0,
dml2_rotation_90 = 1,
@@ -82,6 +87,15 @@ enum dml2_output_link_dp_rate {
dml2_dp_rate_uhbr20 = 6
};
+enum dml2_pstate_type {
+ dml2_pstate_type_uclk = 0,
+ dml2_pstate_type_fclk = 1,
+ dml2_pstate_type_ppt = 2,
+ dml2_pstate_type_temp_read = 3,
+ dml2_pstate_type_dummy_pstate = 4,
+ dml2_pstate_type_count = 5
+};
+
enum dml2_uclk_pstate_change_strategy {
dml2_uclk_pstate_change_strategy_auto = 0,
dml2_uclk_pstate_change_strategy_force_vactive = 1,
@@ -222,7 +236,11 @@ struct dml2_composition_cfg {
struct {
bool enabled;
+ bool easf_enabled;
+ bool isharp_enabled;
bool upsp_enabled;
+ enum dml2_sample_positioning upsp_sample_positioning;
+ unsigned int upsp_vtaps;
struct {
double h_ratio;
double v_ratio;
@@ -384,7 +402,7 @@ struct dml2_plane_parameters {
// reserved_vblank_time_ns is the minimum time to reserve in vblank for Twait
// The actual reserved vblank time used for the corresponding stream in mode_programming would be at least as much as this per-plane override.
long reserved_vblank_time_ns;
- unsigned int max_vactive_det_fill_delay_us; // 0 = no reserved time, +ve = explicit max delay
+ unsigned int max_vactive_det_fill_delay_us[dml2_pstate_type_count]; // 0 = no reserved time, +ve = explicit max delay
unsigned int gpuvm_min_page_size_kbytes;
unsigned int hostvm_min_page_size_kbytes;
@@ -413,7 +431,6 @@ struct dml2_stream_parameters {
bool disable_dynamic_odm;
bool disable_subvp;
int minimum_vblank_idle_requirement_us;
- bool minimize_active_latency_hiding;
struct {
struct {
@@ -456,6 +473,7 @@ struct dml2_display_cfg {
bool enable;
bool value;
} force_nom_det_size_kbytes;
+
bool mode_support_check_disable;
bool mcache_admissibility_check_disable;
bool surface_viewport_size_check_disable;
@@ -478,7 +496,6 @@ struct dml2_display_cfg {
bool synchronize_ddr_displays_for_uclk_pstate_change;
bool max_outstanding_when_urgent_expected_disable;
bool enable_subvp_implicit_pmo; //enables PMO to switch pipe uclk strategy to subvp, and generate phantom programming
- unsigned int best_effort_min_active_latency_hiding_us;
bool all_streams_blanked;
} overrides;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_policy_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_policy_types.h
index 8f624a912e78..8f624a912e78 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_policy_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_policy_types.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_soc_parameter_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h
index 176f55947664..1fbc520c2540 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_soc_parameter_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h
@@ -89,8 +89,8 @@ struct dml2_soc_qos_parameters {
struct dml2_soc_power_management_parameters {
double dram_clk_change_blackout_us;
- double dram_clk_change_read_only_us;
- double dram_clk_change_write_only_us;
+ double dram_clk_change_read_only_us; // deprecated
+ double dram_clk_change_write_only_us; // deprecated
double fclk_change_blackout_us;
double g7_ppt_blackout_us;
double g7_temperature_read_blackout_us;
@@ -145,6 +145,8 @@ struct dml2_soc_bb {
struct dml2_soc_vmin_clock_limits vmin_limit;
double lower_bound_bandwidth_dchub;
+ double fraction_of_urgent_bandwidth_nominal_target;
+ double fraction_of_urgent_bandwidth_flip_target;
unsigned int dprefclk_mhz;
unsigned int xtalclk_mhz;
unsigned int pcie_refclk_mhz;
@@ -170,6 +172,7 @@ struct dml2_soc_bb {
struct dml2_ip_capabilities {
unsigned int pipe_count;
unsigned int otg_count;
+ unsigned int TDLUT_33cube_count;
unsigned int num_dsc;
unsigned int max_num_dp2p0_streams;
unsigned int max_num_hdmi_frl_outputs;
@@ -188,7 +191,9 @@ struct dml2_ip_capabilities {
unsigned int subvp_prefetch_end_to_mall_start_us;
unsigned int subvp_fw_processing_delay;
unsigned int max_vactive_det_fill_delay_us;
-
+ unsigned int ppt_max_allow_delay_us;
+ unsigned int temp_read_max_allow_delay_us;
+ unsigned int dummy_pstate_max_allow_delay_us;
/* FAMS2 delays */
struct {
unsigned int max_allow_delay_us;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
index 41adb1104d0f..452e4a2e72c0 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/inc/dml_top_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_types.h
@@ -70,6 +70,8 @@ struct dml2_pmo_options {
bool disable_dyn_odm;
bool disable_dyn_odm_for_multi_stream;
bool disable_dyn_odm_for_stream_with_svp;
+ struct dml2_pmo_pstate_strategy *override_strategy_lists[DML2_MAX_PLANES];
+ unsigned int num_override_strategies_per_list[DML2_MAX_PLANES];
};
struct dml2_options {
@@ -310,6 +312,7 @@ struct dml2_mode_support_info {
bool NumberOfOTGSupport;
bool NumberOfHDMIFRLSupport;
bool NumberOfDP2p0Support;
+ bool NumberOfTDLUT33cubeSupport;
bool WritebackScaleRatioAndTapsSupport;
bool CursorSupport;
bool PitchSupport;
@@ -357,6 +360,8 @@ struct dml2_mode_support_info {
unsigned int AlignedCPitch[DML2_MAX_PLANES];
bool g6_temp_read_support;
bool temp_read_or_ppt_support;
+ bool qos_bandwidth_support;
+ bool dcfclk_support;
}; // dml2_mode_support_info
struct dml2_display_cfg_programming {
@@ -671,6 +676,8 @@ struct dml2_display_cfg_programming {
unsigned int PrefetchMode[DML2_MAX_PLANES]; // LEGACY_ONLY
bool ROBUrgencyAvoidance;
double LowestPrefetchMargin;
+
+ unsigned int pstate_recout_reduction_lines[DML2_MAX_PLANES];
} misc;
struct dml2_mode_support_info mode_support_info;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c
index 6ee37386f672..eba948e187c1 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.c
@@ -28,6 +28,7 @@ struct dml2_core_ip_params core_dcn4_ip_caps_base = {
.writeback_interface_buffer_size_kbytes = 90,
//Number of pipes after DCN Pipe harvesting
.max_num_dpp = 4,
+ .max_num_opp = 4,
.max_num_otg = 4,
.max_num_wb = 1,
.max_dchub_pscl_bw_pix_per_clk = 4,
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.h
index a68bb001a346..a68bb001a346 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
index bf62d42b3f78..a02e9fd6b5ca 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
@@ -1303,6 +1303,7 @@ static double TruncToValidBPP(
MinDSCBPP = 8;
MaxDSCBPP = 16;
} else {
+
if (Output == dml2_hdmi || Output == dml2_hdmifrl) {
NonDSCBPP0 = 24;
NonDSCBPP1 = 24;
@@ -1320,6 +1321,7 @@ static double TruncToValidBPP(
MaxDSCBPP = 16;
}
}
+
if (Output == dml2_dp2p0) {
MaxLinkBPP = LinkBitRate * Lanes / PixelClock * 128.0 / 132.0 * 383.0 / 384.0 * 65536.0 / 65540.0;
} else if (DSCEnable && Output == dml2_dp) {
@@ -4047,7 +4049,9 @@ static bool ValidateODMMode(enum dml2_odm_mode ODMMode,
bool UseDSC,
unsigned int NumberOfDSCSlices,
unsigned int TotalNumberOfActiveDPP,
+ unsigned int TotalNumberOfActiveOPP,
unsigned int MaxNumDPP,
+ unsigned int MaxNumOPP,
double DISPCLKRequired,
unsigned int NumberOfDPPRequired,
unsigned int MaxHActiveForDSC,
@@ -4063,7 +4067,7 @@ static bool ValidateODMMode(enum dml2_odm_mode ODMMode,
if (DISPCLKRequired > MaxDispclk)
return false;
- if ((TotalNumberOfActiveDPP + NumberOfDPPRequired) > MaxNumDPP)
+ if ((TotalNumberOfActiveDPP + NumberOfDPPRequired) > MaxNumDPP || (TotalNumberOfActiveOPP + NumberOfDPPRequired) > MaxNumOPP)
return false;
if (are_odm_segments_symmetrical) {
if (HActive % (NumberOfDPPRequired * pixels_per_clock_cycle))
@@ -4109,7 +4113,9 @@ static noinline_for_stack void CalculateODMMode(
double MaxDispclk,
bool DSCEnable,
unsigned int TotalNumberOfActiveDPP,
+ unsigned int TotalNumberOfActiveOPP,
unsigned int MaxNumDPP,
+ unsigned int MaxNumOPP,
double PixelClock,
unsigned int NumberOfDSCSlices,
@@ -4179,7 +4185,9 @@ static noinline_for_stack void CalculateODMMode(
UseDSC,
NumberOfDSCSlices,
TotalNumberOfActiveDPP,
+ TotalNumberOfActiveOPP,
MaxNumDPP,
+ MaxNumOPP,
DISPCLKRequired,
NumberOfDPPRequired,
MaxHActiveForDSC,
@@ -6964,7 +6972,7 @@ static void calculate_bytes_to_fetch_required_to_hide_latency(
stream_index = p->display_cfg->plane_descriptors[plane_index].stream_index;
- dst_lines_to_hide = (unsigned int)math_ceil(p->latency_to_hide_us /
+ dst_lines_to_hide = (unsigned int)math_ceil(p->latency_to_hide_us[0] /
((double)p->display_cfg->stream_descriptors[stream_index].timing.h_total /
(double)p->display_cfg->stream_descriptors[stream_index].timing.pixel_clock_khz * 1000.0));
@@ -7061,9 +7069,9 @@ static void calculate_excess_vactive_bandwidth_required(
excess_vactive_fill_bw_l[plane_index] = 0.0;
excess_vactive_fill_bw_c[plane_index] = 0.0;
- if (display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us > 0) {
- excess_vactive_fill_bw_l[plane_index] = (double)bytes_required_l[plane_index] / (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us;
- excess_vactive_fill_bw_c[plane_index] = (double)bytes_required_c[plane_index] / (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us;
+ if (display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk] > 0) {
+ excess_vactive_fill_bw_l[plane_index] = (double)bytes_required_l[plane_index] / (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk];
+ excess_vactive_fill_bw_c[plane_index] = (double)bytes_required_c[plane_index] / (double)display_cfg->plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk];
}
}
}
@@ -8358,6 +8366,7 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
CalculateSwathAndDETConfiguration(&mode_lib->scratch, CalculateSwathAndDETConfiguration_params);
mode_lib->ms.TotalNumberOfActiveDPP = 0;
+ mode_lib->ms.TotalNumberOfActiveOPP = 0;
mode_lib->ms.support.TotalAvailablePipesSupport = true;
for (k = 0; k < mode_lib->ms.num_active_planes; ++k) {
@@ -8393,7 +8402,9 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
mode_lib->ms.max_dispclk_freq_mhz,
false, // DSCEnable
mode_lib->ms.TotalNumberOfActiveDPP,
+ mode_lib->ms.TotalNumberOfActiveOPP,
mode_lib->ip.max_num_dpp,
+ mode_lib->ip.max_num_opp,
((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000),
mode_lib->ms.support.NumberOfDSCSlices[k],
@@ -8412,7 +8423,9 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
mode_lib->ms.max_dispclk_freq_mhz,
true, // DSCEnable
mode_lib->ms.TotalNumberOfActiveDPP,
+ mode_lib->ms.TotalNumberOfActiveOPP,
mode_lib->ip.max_num_dpp,
+ mode_lib->ip.max_num_opp,
((double)display_cfg->stream_descriptors[display_cfg->plane_descriptors[k].stream_index].timing.pixel_clock_khz / 1000),
mode_lib->ms.support.NumberOfDSCSlices[k],
@@ -8516,20 +8529,23 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
for (k = 0; k < mode_lib->ms.num_active_planes; ++k) {
mode_lib->ms.MPCCombine[k] = false;
mode_lib->ms.NoOfDPP[k] = 1;
+ mode_lib->ms.NoOfOPP[k] = 1;
if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_4to1) {
mode_lib->ms.MPCCombine[k] = false;
mode_lib->ms.NoOfDPP[k] = 4;
+ mode_lib->ms.NoOfOPP[k] = 4;
} else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_3to1) {
mode_lib->ms.MPCCombine[k] = false;
mode_lib->ms.NoOfDPP[k] = 3;
+ mode_lib->ms.NoOfOPP[k] = 3;
} else if (mode_lib->ms.ODMMode[k] == dml2_odm_mode_combine_2to1) {
mode_lib->ms.MPCCombine[k] = false;
mode_lib->ms.NoOfDPP[k] = 2;
+ mode_lib->ms.NoOfOPP[k] = 2;
} else if (display_cfg->plane_descriptors[k].overrides.mpcc_combine_factor == 2) {
mode_lib->ms.MPCCombine[k] = true;
mode_lib->ms.NoOfDPP[k] = 2;
- mode_lib->ms.TotalNumberOfActiveDPP++;
} else if (display_cfg->plane_descriptors[k].overrides.mpcc_combine_factor == 1) {
mode_lib->ms.MPCCombine[k] = false;
mode_lib->ms.NoOfDPP[k] = 1;
@@ -8540,7 +8556,6 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
if ((mode_lib->ms.MinDPPCLKUsingSingleDPP[k] > mode_lib->ms.max_dppclk_freq_mhz) || !mode_lib->ms.SingleDPPViewportSizeSupportPerSurface[k]) {
mode_lib->ms.MPCCombine[k] = true;
mode_lib->ms.NoOfDPP[k] = 2;
- mode_lib->ms.TotalNumberOfActiveDPP++;
}
}
#if defined(__DML_VBA_DEBUG__)
@@ -8548,8 +8563,16 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
#endif
}
+ mode_lib->ms.TotalNumberOfActiveDPP = 0;
+ mode_lib->ms.TotalNumberOfActiveOPP = 0;
+ for (k = 0; k < mode_lib->ms.num_active_planes; ++k) {
+ mode_lib->ms.TotalNumberOfActiveDPP += mode_lib->ms.NoOfDPP[k];
+ mode_lib->ms.TotalNumberOfActiveOPP += mode_lib->ms.NoOfOPP[k];
+ }
if (mode_lib->ms.TotalNumberOfActiveDPP > (unsigned int)mode_lib->ip.max_num_dpp)
mode_lib->ms.support.TotalAvailablePipesSupport = false;
+ if (mode_lib->ms.TotalNumberOfActiveOPP > (unsigned int)mode_lib->ip.max_num_opp)
+ mode_lib->ms.support.TotalAvailablePipesSupport = false;
mode_lib->ms.TotalNumberOfSingleDPPSurfaces = 0;
@@ -9028,11 +9051,11 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
calculate_bytes_to_fetch_required_to_hide_latency_params->swath_width_c = mode_lib->ms.SwathWidthC;
calculate_bytes_to_fetch_required_to_hide_latency_params->swath_height_l = mode_lib->ms.SwathHeightY;
calculate_bytes_to_fetch_required_to_hide_latency_params->swath_height_c = mode_lib->ms.SwathHeightC;
- calculate_bytes_to_fetch_required_to_hide_latency_params->latency_to_hide_us = mode_lib->soc.power_management_parameters.dram_clk_change_blackout_us;
+ calculate_bytes_to_fetch_required_to_hide_latency_params->latency_to_hide_us[0] = mode_lib->soc.power_management_parameters.dram_clk_change_blackout_us;
/* outputs */
- calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_l = s->pstate_bytes_required_l;
- calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_c = s->pstate_bytes_required_c;
+ calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_l = s->pstate_bytes_required_l[dml2_pstate_type_uclk];
+ calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_c = s->pstate_bytes_required_c[dml2_pstate_type_uclk];
calculate_bytes_to_fetch_required_to_hide_latency(calculate_bytes_to_fetch_required_to_hide_latency_params);
@@ -9040,8 +9063,8 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
calculate_excess_vactive_bandwidth_required(
display_cfg,
mode_lib->ms.num_active_planes,
- s->pstate_bytes_required_l,
- s->pstate_bytes_required_c,
+ s->pstate_bytes_required_l[dml2_pstate_type_uclk],
+ s->pstate_bytes_required_c[dml2_pstate_type_uclk],
/* outputs */
mode_lib->ms.excess_vactive_fill_bw_l,
mode_lib->ms.excess_vactive_fill_bw_c);
@@ -9483,8 +9506,8 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
calculate_vactive_det_fill_latency(
display_cfg,
mode_lib->ms.num_active_planes,
- s->pstate_bytes_required_l,
- s->pstate_bytes_required_c,
+ s->pstate_bytes_required_l[dml2_pstate_type_uclk],
+ s->pstate_bytes_required_c[dml2_pstate_type_uclk],
mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p0,
mode_lib->ms.dcc_dram_bw_nom_overhead_factor_p1,
mode_lib->ms.vactive_sw_bw_l,
@@ -9492,7 +9515,7 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out
mode_lib->ms.surface_avg_vactive_required_bw,
mode_lib->ms.surface_peak_required_bw,
/* outputs */
- mode_lib->ms.dram_change_vactive_det_fill_delay_us);
+ mode_lib->ms.pstate_vactive_det_fill_delay_us[dml2_pstate_type_uclk]);
#ifdef __DML_VBA_DEBUG__
DML_LOG_VERBOSE("DML::%s: max_urgent_latency_us = %f\n", __func__, s->mSOCParameters.max_urgent_latency_us);
@@ -10986,11 +11009,11 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex
calculate_bytes_to_fetch_required_to_hide_latency_params->swath_width_c = mode_lib->mp.SwathWidthC;
calculate_bytes_to_fetch_required_to_hide_latency_params->swath_height_l = mode_lib->mp.SwathHeightY;
calculate_bytes_to_fetch_required_to_hide_latency_params->swath_height_c = mode_lib->mp.SwathHeightC;
- calculate_bytes_to_fetch_required_to_hide_latency_params->latency_to_hide_us = mode_lib->soc.power_management_parameters.dram_clk_change_blackout_us;
+ calculate_bytes_to_fetch_required_to_hide_latency_params->latency_to_hide_us[0] = mode_lib->soc.power_management_parameters.dram_clk_change_blackout_us;
/* outputs */
- calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_l = s->pstate_bytes_required_l;
- calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_c = s->pstate_bytes_required_c;
+ calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_l = s->pstate_bytes_required_l[dml2_pstate_type_uclk];
+ calculate_bytes_to_fetch_required_to_hide_latency_params->bytes_required_c = s->pstate_bytes_required_c[dml2_pstate_type_uclk];
calculate_bytes_to_fetch_required_to_hide_latency(calculate_bytes_to_fetch_required_to_hide_latency_params);
@@ -10998,8 +11021,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex
calculate_excess_vactive_bandwidth_required(
display_cfg,
s->num_active_planes,
- s->pstate_bytes_required_l,
- s->pstate_bytes_required_c,
+ s->pstate_bytes_required_l[dml2_pstate_type_uclk],
+ s->pstate_bytes_required_c[dml2_pstate_type_uclk],
/* outputs */
mode_lib->mp.excess_vactive_fill_bw_l,
mode_lib->mp.excess_vactive_fill_bw_c);
@@ -12756,7 +12779,7 @@ void dml2_core_calcs_get_stream_fams2_programming(const struct dml2_core_interna
{
const struct dml2_plane_parameters *plane_descriptor = &display_cfg->display_config.plane_descriptors[plane_index];
const struct dml2_stream_parameters *stream_descriptor = &display_cfg->display_config.stream_descriptors[plane_descriptor->stream_index];
- const struct dml2_fams2_meta *stream_fams2_meta = &display_cfg->stage3.stream_fams2_meta[plane_descriptor->stream_index];
+ const struct dml2_pstate_meta *stream_pstate_meta = &display_cfg->stage3.stream_pstate_meta[plane_descriptor->stream_index];
struct dmub_fams2_cmd_stream_static_base_state *base_programming = &fams2_base_programming->stream_v1.base;
union dmub_fams2_cmd_stream_static_sub_state *sub_programming = &fams2_sub_programming->stream_v1.sub_state;
@@ -12771,24 +12794,24 @@ void dml2_core_calcs_get_stream_fams2_programming(const struct dml2_core_interna
/* from display configuration */
base_programming->htotal = (uint16_t)stream_descriptor->timing.h_total;
base_programming->vtotal = (uint16_t)stream_descriptor->timing.v_total;
- base_programming->vblank_start = (uint16_t)(stream_fams2_meta->nom_vtotal -
+ base_programming->vblank_start = (uint16_t)(stream_pstate_meta->nom_vtotal -
stream_descriptor->timing.v_front_porch);
- base_programming->vblank_end = (uint16_t)(stream_fams2_meta->nom_vtotal -
+ base_programming->vblank_end = (uint16_t)(stream_pstate_meta->nom_vtotal -
stream_descriptor->timing.v_front_porch -
stream_descriptor->timing.v_active);
base_programming->config.bits.is_drr = stream_descriptor->timing.drr_config.enabled;
/* from meta */
base_programming->otg_vline_time_ns =
- (unsigned int)(stream_fams2_meta->otg_vline_time_us * 1000.0);
- base_programming->scheduling_delay_otg_vlines = (uint8_t)stream_fams2_meta->scheduling_delay_otg_vlines;
- base_programming->contention_delay_otg_vlines = (uint8_t)stream_fams2_meta->contention_delay_otg_vlines;
- base_programming->vline_int_ack_delay_otg_vlines = (uint8_t)stream_fams2_meta->vertical_interrupt_ack_delay_otg_vlines;
- base_programming->drr_keepout_otg_vline = (uint16_t)(stream_fams2_meta->nom_vtotal -
+ (unsigned int)(stream_pstate_meta->otg_vline_time_us * 1000.0);
+ base_programming->scheduling_delay_otg_vlines = (uint8_t)stream_pstate_meta->scheduling_delay_otg_vlines;
+ base_programming->contention_delay_otg_vlines = (uint8_t)stream_pstate_meta->contention_delay_otg_vlines;
+ base_programming->vline_int_ack_delay_otg_vlines = (uint8_t)stream_pstate_meta->vertical_interrupt_ack_delay_otg_vlines;
+ base_programming->drr_keepout_otg_vline = (uint16_t)(stream_pstate_meta->nom_vtotal -
stream_descriptor->timing.v_front_porch -
- stream_fams2_meta->method_drr.programming_delay_otg_vlines);
- base_programming->allow_to_target_delay_otg_vlines = (uint8_t)stream_fams2_meta->allow_to_target_delay_otg_vlines;
- base_programming->max_vtotal = (uint16_t)stream_fams2_meta->max_vtotal;
+ stream_pstate_meta->method_drr.programming_delay_otg_vlines);
+ base_programming->allow_to_target_delay_otg_vlines = (uint8_t)stream_pstate_meta->allow_to_target_delay_otg_vlines;
+ base_programming->max_vtotal = (uint16_t)stream_pstate_meta->max_vtotal;
/* from core */
base_programming->config.bits.min_ttu_vblank_usable = true;
@@ -12807,11 +12830,11 @@ void dml2_core_calcs_get_stream_fams2_programming(const struct dml2_core_interna
/* legacy vactive */
base_programming->type = FAMS2_STREAM_TYPE_VACTIVE;
sub_programming->legacy.vactive_det_fill_delay_otg_vlines =
- (uint8_t)stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines;
+ (uint8_t)stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines;
base_programming->allow_start_otg_vline =
- (uint16_t)stream_fams2_meta->method_vactive.common.allow_start_otg_vline;
+ (uint16_t)stream_pstate_meta->method_vactive.common.allow_start_otg_vline;
base_programming->allow_end_otg_vline =
- (uint16_t)stream_fams2_meta->method_vactive.common.allow_end_otg_vline;
+ (uint16_t)stream_pstate_meta->method_vactive.common.allow_end_otg_vline;
base_programming->config.bits.clamp_vtotal_min = true;
break;
case dml2_pstate_method_vblank:
@@ -12819,22 +12842,22 @@ void dml2_core_calcs_get_stream_fams2_programming(const struct dml2_core_interna
/* legacy vblank */
base_programming->type = FAMS2_STREAM_TYPE_VBLANK;
base_programming->allow_start_otg_vline =
- (uint16_t)stream_fams2_meta->method_vblank.common.allow_start_otg_vline;
+ (uint16_t)stream_pstate_meta->method_vblank.common.allow_start_otg_vline;
base_programming->allow_end_otg_vline =
- (uint16_t)stream_fams2_meta->method_vblank.common.allow_end_otg_vline;
+ (uint16_t)stream_pstate_meta->method_vblank.common.allow_end_otg_vline;
base_programming->config.bits.clamp_vtotal_min = true;
break;
case dml2_pstate_method_fw_drr:
/* drr */
base_programming->type = FAMS2_STREAM_TYPE_DRR;
sub_programming->drr.programming_delay_otg_vlines =
- (uint8_t)stream_fams2_meta->method_drr.programming_delay_otg_vlines;
+ (uint8_t)stream_pstate_meta->method_drr.programming_delay_otg_vlines;
sub_programming->drr.nom_stretched_vtotal =
- (uint16_t)stream_fams2_meta->method_drr.stretched_vtotal;
+ (uint16_t)stream_pstate_meta->method_drr.stretched_vtotal;
base_programming->allow_start_otg_vline =
- (uint16_t)stream_fams2_meta->method_drr.common.allow_start_otg_vline;
+ (uint16_t)stream_pstate_meta->method_drr.common.allow_start_otg_vline;
base_programming->allow_end_otg_vline =
- (uint16_t)stream_fams2_meta->method_drr.common.allow_end_otg_vline;
+ (uint16_t)stream_pstate_meta->method_drr.common.allow_end_otg_vline;
/* drr only clamps to vtotal min for single display */
base_programming->config.bits.clamp_vtotal_min = display_cfg->display_config.num_streams == 1;
sub_programming->drr.only_stretch_if_required = true;
@@ -12847,13 +12870,13 @@ void dml2_core_calcs_get_stream_fams2_programming(const struct dml2_core_interna
(uint16_t)(plane_descriptor->composition.scaler_info.plane0.v_ratio * 1000.0);
sub_programming->subvp.vratio_denominator = 1000;
sub_programming->subvp.programming_delay_otg_vlines =
- (uint8_t)stream_fams2_meta->method_subvp.programming_delay_otg_vlines;
+ (uint8_t)stream_pstate_meta->method_subvp.programming_delay_otg_vlines;
sub_programming->subvp.prefetch_to_mall_otg_vlines =
- (uint8_t)stream_fams2_meta->method_subvp.prefetch_to_mall_delay_otg_vlines;
+ (uint8_t)stream_pstate_meta->method_subvp.prefetch_to_mall_delay_otg_vlines;
sub_programming->subvp.phantom_vtotal =
- (uint16_t)stream_fams2_meta->method_subvp.phantom_vtotal;
+ (uint16_t)stream_pstate_meta->method_subvp.phantom_vtotal;
sub_programming->subvp.phantom_vactive =
- (uint16_t)stream_fams2_meta->method_subvp.phantom_vactive;
+ (uint16_t)stream_pstate_meta->method_subvp.phantom_vactive;
sub_programming->subvp.config.bits.is_multi_planar =
plane_descriptor->surface.plane1.height > 0;
sub_programming->subvp.config.bits.is_yuv420 =
@@ -12862,9 +12885,9 @@ void dml2_core_calcs_get_stream_fams2_programming(const struct dml2_core_interna
plane_descriptor->pixel_format == dml2_420_12;
base_programming->allow_start_otg_vline =
- (uint16_t)stream_fams2_meta->method_subvp.common.allow_start_otg_vline;
+ (uint16_t)stream_pstate_meta->method_subvp.common.allow_start_otg_vline;
base_programming->allow_end_otg_vline =
- (uint16_t)stream_fams2_meta->method_subvp.common.allow_end_otg_vline;
+ (uint16_t)stream_pstate_meta->method_subvp.common.allow_end_otg_vline;
base_programming->config.bits.clamp_vtotal_min = true;
break;
case dml2_pstate_method_reserved_hw:
@@ -12920,7 +12943,8 @@ void dml2_core_calcs_get_plane_support_info(const struct dml2_display_cfg *displ
out->active_latency_hiding_us = (int)mode_lib->ms.VActiveLatencyHidingUs[plane_idx];
- out->dram_change_vactive_det_fill_delay_us = (unsigned int)math_ceil(mode_lib->ms.dram_change_vactive_det_fill_delay_us[plane_idx]);
+ out->vactive_det_fill_delay_us[dml2_pstate_type_uclk] =
+ (unsigned int)math_ceil(mode_lib->ms.pstate_vactive_det_fill_delay_us[dml2_pstate_type_uclk][plane_idx]);
}
void dml2_core_calcs_get_stream_support_info(const struct dml2_display_cfg *display_cfg, const struct dml2_core_internal_display_mode_lib *mode_lib, struct core_stream_support_info *out, int plane_index)
@@ -13001,7 +13025,7 @@ void dml2_core_calcs_get_informative(const struct dml2_core_internal_display_mod
out->informative.mode_support_info.InvalidCombinationOfMALLUseForPState = mode_lib->ms.support.InvalidCombinationOfMALLUseForPState;
out->informative.mode_support_info.ExceededMALLSize = mode_lib->ms.support.ExceededMALLSize;
out->informative.mode_support_info.EnoughWritebackUnits = mode_lib->ms.support.EnoughWritebackUnits;
- out->informative.mode_support_info.temp_read_or_ppt_support = mode_lib->ms.support.temp_read_or_ppt_support;
+ out->informative.mode_support_info.temp_read_or_ppt_support = mode_lib->ms.support.global_temp_read_or_ppt_supported;
out->informative.mode_support_info.g6_temp_read_support = mode_lib->ms.support.g6_temp_read_support;
out->informative.mode_support_info.ExceededMultistreamSlots = mode_lib->ms.support.ExceededMultistreamSlots;
@@ -13027,7 +13051,10 @@ void dml2_core_calcs_get_informative(const struct dml2_core_internal_display_mod
out->informative.mode_support_info.VRatioInPrefetchSupported = mode_lib->ms.support.VRatioInPrefetchSupported;
out->informative.mode_support_info.DISPCLK_DPPCLK_Support = mode_lib->ms.support.DISPCLK_DPPCLK_Support;
out->informative.mode_support_info.TotalAvailablePipesSupport = mode_lib->ms.support.TotalAvailablePipesSupport;
+ out->informative.mode_support_info.NumberOfTDLUT33cubeSupport = mode_lib->ms.support.NumberOfTDLUT33cubeSupport;
out->informative.mode_support_info.ViewportSizeSupport = mode_lib->ms.support.ViewportSizeSupport;
+ out->informative.mode_support_info.qos_bandwidth_support = mode_lib->ms.support.qos_bandwidth_support;
+ out->informative.mode_support_info.dcfclk_support = mode_lib->ms.support.dcfclk_support;
for (k = 0; k < out->display_config.num_planes; k++) {
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h
index 27ef0e096b25..27ef0e096b25 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_dcn4_calcs.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c
index 640087e862f8..cc4f0663c6d6 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c
@@ -15,6 +15,8 @@ bool dml2_core_create(enum dml2_project_id project_id, struct dml2_core_instance
memset(out, 0, sizeof(struct dml2_core_instance));
+ out->project_id = project_id;
+
switch (project_id) {
case dml2_project_dcn4x_stage1:
result = false;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.h
index 411c514fe65c..411c514fe65c 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_factory.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
index ffb8c09f37a5..1087a8c926ff 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_shared_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h
@@ -36,7 +36,9 @@ struct dml2_core_ip_params {
unsigned int max_line_buffer_lines;
unsigned int writeback_interface_buffer_size_kbytes;
unsigned int max_num_dpp;
+ unsigned int max_num_opp;
unsigned int max_num_otg;
+ unsigned int TDLUT_33cube_count;
unsigned int max_num_wb;
unsigned int max_dchub_pscl_bw_pix_per_clk;
unsigned int max_pscl_lb_bw_pix_per_clk;
@@ -46,6 +48,7 @@ struct dml2_core_ip_params {
double max_vscl_ratio;
unsigned int max_hscl_taps;
unsigned int max_vscl_taps;
+ unsigned int odm_combine_support_mask;
unsigned int num_dsc;
unsigned int maximum_dsc_bits_per_component;
unsigned int maximum_pixels_per_line_per_dsc_unit;
@@ -82,7 +85,6 @@ struct dml2_core_ip_params {
unsigned int subvp_swath_height_margin_lines;
unsigned int subvp_fw_processing_delay_us;
unsigned int subvp_pstate_allow_width_us;
-
// MRQ
bool dcn_mrq_present;
unsigned int zero_size_buffer_entries;
@@ -103,6 +105,8 @@ struct dml2_core_internal_DmlPipe {
unsigned int DPPPerSurface;
bool ScalerEnabled;
bool UPSPEnabled;
+ unsigned int UPSPVTaps;
+ enum dml2_sample_positioning UPSPSamplePositioning;
enum dml2_rotation_angle RotationAngle;
bool mirrored;
unsigned int ViewportHeight;
@@ -230,6 +234,7 @@ struct dml2_core_internal_mode_support_info {
bool MSOOrODMSplitWithNonDPLink;
bool NotEnoughLanesForMSO;
bool NumberOfOTGSupport;
+ bool NumberOfTDLUT33cubeSupport;
bool NumberOfHDMIFRLSupport;
bool NumberOfDP2p0Support;
bool WritebackScaleRatioAndTapsSupport;
@@ -259,8 +264,11 @@ struct dml2_core_internal_mode_support_info {
bool DCCMetaBufferSizeNotExceeded;
enum dml2_pstate_change_support DRAMClockChangeSupport[DML2_MAX_PLANES];
enum dml2_pstate_change_support FCLKChangeSupport[DML2_MAX_PLANES];
+ enum dml2_pstate_change_support temp_read_or_ppt_support[DML2_MAX_PLANES];
+ bool global_dram_clock_change_support_required;
bool global_dram_clock_change_supported;
bool global_fclk_change_supported;
+ bool global_temp_read_or_ppt_supported;
bool USRRetrainingSupport;
bool AvgBandwidthSupport;
bool UrgVactiveBandwidthSupport;
@@ -331,7 +339,6 @@ struct dml2_core_internal_mode_support_info {
bool incorrect_imall_usage;
bool g6_temp_read_support;
- bool temp_read_or_ppt_support;
struct dml2_core_internal_watermarks watermarks;
bool dcfclk_support;
@@ -566,6 +573,7 @@ struct dml2_core_internal_mode_support {
enum dml2_odm_mode ODMMode[DML2_MAX_PLANES];
unsigned int SurfaceSizeInMALL[DML2_MAX_PLANES];
unsigned int NoOfDPP[DML2_MAX_PLANES];
+ unsigned int NoOfOPP[DML2_MAX_PLANES];
bool MPCCombine[DML2_MAX_PLANES];
double dcfclk_deepsleep;
double MinDPPCLKUsingSingleDPP[DML2_MAX_PLANES];
@@ -576,6 +584,7 @@ struct dml2_core_internal_mode_support {
bool PTEBufferSizeNotExceeded[DML2_MAX_PLANES];
bool DCCMetaBufferSizeNotExceeded[DML2_MAX_PLANES];
unsigned int TotalNumberOfActiveDPP;
+ unsigned int TotalNumberOfActiveOPP;
unsigned int TotalNumberOfSingleDPPSurfaces;
unsigned int TotalNumberOfDCCActiveDPP;
unsigned int Total3dlutActive;
@@ -584,7 +593,7 @@ struct dml2_core_internal_mode_support {
double VActiveLatencyHidingMargin[DML2_MAX_PLANES];
double VActiveLatencyHidingUs[DML2_MAX_PLANES];
unsigned int MaxVStartupLines[DML2_MAX_PLANES];
- double dram_change_vactive_det_fill_delay_us[DML2_MAX_PLANES];
+ double pstate_vactive_det_fill_delay_us[dml2_pstate_type_count][DML2_MAX_PLANES];
unsigned int num_mcaches_l[DML2_MAX_PLANES];
unsigned int mcache_row_bytes_l[DML2_MAX_PLANES];
@@ -614,8 +623,8 @@ struct dml2_core_internal_mode_support {
unsigned int dpte_row_bytes_per_row_l[DML2_MAX_PLANES];
unsigned int dpte_row_bytes_per_row_c[DML2_MAX_PLANES];
- unsigned int pstate_bytes_required_l[DML2_MAX_PLANES];
- unsigned int pstate_bytes_required_c[DML2_MAX_PLANES];
+ unsigned int pstate_bytes_required_l[dml2_pstate_type_count][DML2_MAX_PLANES];
+ unsigned int pstate_bytes_required_c[dml2_pstate_type_count][DML2_MAX_PLANES];
unsigned int cursor_bytes_per_chunk[DML2_MAX_PLANES];
unsigned int cursor_bytes_per_line[DML2_MAX_PLANES];
@@ -639,7 +648,7 @@ struct dml2_core_internal_mode_support {
unsigned int DSTYAfterScaler[DML2_MAX_PLANES];
unsigned int DSTXAfterScaler[DML2_MAX_PLANES];
- enum dml2_pstate_method pstate_switch_modes[DML2_MAX_PLANES];
+ enum dml2_pstate_method uclk_pstate_switch_modes[DML2_MAX_PLANES];
};
/// @brief A mega structure that houses various info for model programming step.
@@ -830,6 +839,7 @@ struct dml2_core_internal_mode_program {
double max_urgent_latency_us;
double df_response_time_us;
+ enum dml2_pstate_method uclk_pstate_switch_modes[DML2_MAX_PLANES];
// -------------------
// Output
// -------------------
@@ -956,11 +966,12 @@ struct dml2_core_internal_mode_program {
double MaxActiveFCLKChangeLatencySupported;
bool USRRetrainingSupport;
bool g6_temp_read_support;
- bool temp_read_or_ppt_support;
enum dml2_pstate_change_support FCLKChangeSupport[DML2_MAX_PLANES];
enum dml2_pstate_change_support DRAMClockChangeSupport[DML2_MAX_PLANES];
+ enum dml2_pstate_change_support temp_read_or_ppt_support[DML2_MAX_PLANES];
bool global_dram_clock_change_supported;
bool global_fclk_change_supported;
+ bool global_temp_read_or_ppt_supported;
double MaxActiveDRAMClockChangeLatencySupported[DML2_MAX_PLANES];
double WritebackAllowFCLKChangeEndPosition[DML2_MAX_PLANES];
double WritebackAllowDRAMClockChangeEndPosition[DML2_MAX_PLANES];
@@ -1127,8 +1138,8 @@ struct dml2_core_calcs_mode_support_locals {
unsigned int cursor_bytes[DML2_MAX_PLANES];
bool stream_visited[DML2_MAX_PLANES];
- unsigned int pstate_bytes_required_l[DML2_MAX_PLANES];
- unsigned int pstate_bytes_required_c[DML2_MAX_PLANES];
+ unsigned int pstate_bytes_required_l[dml2_pstate_type_count][DML2_MAX_PLANES];
+ unsigned int pstate_bytes_required_c[dml2_pstate_type_count][DML2_MAX_PLANES];
double prefetch_sw_bytes[DML2_MAX_PLANES];
double Tpre_rounded[DML2_MAX_PLANES];
@@ -1219,8 +1230,8 @@ struct dml2_core_calcs_mode_programming_locals {
double Tr0_trips_flip_rounded[DML2_MAX_PLANES];
unsigned int per_pipe_flip_bytes[DML2_MAX_PLANES];
- unsigned int pstate_bytes_required_l[DML2_MAX_PLANES];
- unsigned int pstate_bytes_required_c[DML2_MAX_PLANES];
+ unsigned int pstate_bytes_required_l[dml2_pstate_type_count][DML2_MAX_PLANES];
+ unsigned int pstate_bytes_required_c[dml2_pstate_type_count][DML2_MAX_PLANES];
double prefetch_sw_bytes[DML2_MAX_PLANES];
double Tpre_rounded[DML2_MAX_PLANES];
@@ -1306,7 +1317,7 @@ struct dml2_core_calcs_CalculateVMRowAndSwath_params {
unsigned int HostVMMinPageSize;
unsigned int DCCMetaBufferSizeBytes;
bool mrq_present;
- enum dml2_pstate_method pstate_switch_modes[DML2_MAX_PLANES];
+ enum dml2_pstate_method *uclk_pstate_switch_modes;
// Output
bool *PTEBufferSizeNotExceeded;
@@ -1733,10 +1744,12 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_param
unsigned int max_request_size_bytes;
unsigned int *meta_row_height_l;
unsigned int *meta_row_height_c;
+ enum dml2_pstate_method *uclk_pstate_switch_modes;
// Output
struct dml2_core_internal_watermarks *Watermark;
enum dml2_pstate_change_support *DRAMClockChangeSupport;
+ bool *global_dram_clock_change_support_required;
bool *global_dram_clock_change_supported;
double *MaxActiveDRAMClockChangeLatencySupported;
unsigned int *SubViewportLinesNeededInMALL;
@@ -1747,10 +1760,10 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_param
double *VActiveLatencyHidingMargin;
double *VActiveLatencyHidingUs;
bool *g6_temp_read_support;
- bool *temp_read_or_ppt_support;
+ enum dml2_pstate_change_support *temp_read_or_ppt_support;
+ bool *global_temp_read_or_ppt_supported;
};
-
struct dml2_core_calcs_CalculateSwathAndDETConfiguration_params {
const struct dml2_display_cfg *display_cfg;
unsigned int ConfigReturnBufferSizeInKByte;
@@ -2240,7 +2253,7 @@ struct dml2_core_calcs_calculate_bytes_to_fetch_required_to_hide_latency_params
unsigned int *swath_width_c;
unsigned int *swath_height_l;
unsigned int *swath_height_c;
- double latency_to_hide_us;
+ double latency_to_hide_us[DML2_MAX_PLANES];
/* outputs */
unsigned int *bytes_required_l;
@@ -2308,6 +2321,7 @@ struct dml2_core_calcs_mode_support_ex {
const struct dml2_display_cfg *in_display_cfg;
const struct dml2_mcg_min_clock_table *min_clk_table;
int min_clk_index;
+ enum dml2_project_id project_id;
//unsigned int in_state_index;
struct dml2_core_internal_mode_support_info *out_evaluation_info;
};
@@ -2320,6 +2334,7 @@ struct dml2_core_calcs_mode_programming_ex {
const struct dml2_mcg_min_clock_table *min_clk_table;
const struct core_display_cfg_support_info *cfg_support_info;
int min_clk_index;
+ enum dml2_project_id project_id;
struct dml2_display_cfg_programming *programming;
};
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c
index 5f301befed16..b57d0f6ea6a1 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_utils.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.c
@@ -306,6 +306,8 @@ void dml2_core_utils_print_mode_support_info(const struct dml2_core_internal_mod
DML_LOG_VERBOSE("DML: support: ExceededMALLSize = %d\n", support->ExceededMALLSize);
if (!fail_only || support->g6_temp_read_support == 0)
DML_LOG_VERBOSE("DML: support: g6_temp_read_support = %d\n", support->g6_temp_read_support);
+ if (!fail_only || (support->global_dram_clock_change_supported == 0 && support->global_dram_clock_change_support_required))
+ DML_LOG_VERBOSE("DML: support: dram_clock_change_support = %d\n", support->global_dram_clock_change_supported);
if (!fail_only || support->ImmediateFlipSupport == 0)
DML_LOG_VERBOSE("DML: support: ImmediateFlipSupport = %d\n", support->ImmediateFlipSupport);
if (!fail_only || support->LinkCapacitySupport == 0)
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_utils.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h
index 95f0d017add4..95f0d017add4 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_core/dml2_core_utils.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_utils.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c
index 22969a533a7b..22969a533a7b 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.h
index e7b58f2efda4..e7b58f2efda4 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_dcn4.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c
index dfd01440737d..dfd01440737d 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.h
index 20ba2e446f1d..20ba2e446f1d 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_dpmm/dml2_dpmm_factory.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_dpmm/dml2_dpmm_factory.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c
index a265f254152c..a265f254152c 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.h
index 02da6f45cbf7..f54fde8fba90 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_dcn4.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_dcn4.h
@@ -10,4 +10,4 @@
bool mcg_dcn4_build_min_clock_table(struct dml2_mcg_build_min_clock_table_params_in_out *in_out);
bool mcg_dcn4_unit_test(void);
-#endif
+#endif \ No newline at end of file
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c
index c60b8fe90819..c60b8fe90819 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.h
index ad307deca3b0..ad307deca3b0 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_mcg/dml2_mcg_factory.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_mcg/dml2_mcg_factory.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.c
index 1b9579a32ff2..1b9579a32ff2 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.h
index f00bd9e72a86..f00bd9e72a86 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn3.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn3.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
index d88b3e0082dd..c26e100fcaf2 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.c
@@ -642,6 +642,11 @@ bool pmo_dcn4_fams2_initialize(struct dml2_pmo_initialize_in_out *in_out)
int i = 0;
struct dml2_pmo_instance *pmo = in_out->instance;
+ unsigned int base_list_size = 0;
+ const struct dml2_pmo_pstate_strategy *base_list = NULL;
+ unsigned int *expanded_list_size = NULL;
+ struct dml2_pmo_pstate_strategy *expanded_list = NULL;
+
pmo->soc_bb = in_out->soc_bb;
pmo->ip_caps = in_out->ip_caps;
pmo->mpc_combine_limit = 2;
@@ -656,53 +661,71 @@ bool pmo_dcn4_fams2_initialize(struct dml2_pmo_initialize_in_out *in_out)
pmo->options = in_out->options;
/* generate permutations of p-state configs from base strategy list */
- for (i = 1; i <= PMO_DCN4_MAX_DISPLAYS; i++) {
- switch (i) {
+ for (i = 0; i < PMO_DCN4_MAX_DISPLAYS; i++) {
+ switch (i+1) {
case 1:
- DML_ASSERT(base_strategy_list_1_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
-
- /* populate list */
- pmo_dcn4_fams2_expand_base_pstate_strategies(
- base_strategy_list_1_display,
- base_strategy_list_1_display_size,
- i,
- pmo->init_data.pmo_dcn4.expanded_strategy_list_1_display,
- &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]);
+ if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
+ base_list = pmo->options->override_strategy_lists[i];
+ base_list_size = pmo->options->num_override_strategies_per_list[i];
+ } else {
+ base_list = base_strategy_list_1_display;
+ base_list_size = base_strategy_list_1_display_size;
+ }
+
+ expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
+ expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_1_display;
+
break;
case 2:
- DML_ASSERT(base_strategy_list_2_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
-
- /* populate list */
- pmo_dcn4_fams2_expand_base_pstate_strategies(
- base_strategy_list_2_display,
- base_strategy_list_2_display_size,
- i,
- pmo->init_data.pmo_dcn4.expanded_strategy_list_2_display,
- &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]);
+ if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
+ base_list = pmo->options->override_strategy_lists[i];
+ base_list_size = pmo->options->num_override_strategies_per_list[i];
+ } else {
+ base_list = base_strategy_list_2_display;
+ base_list_size = base_strategy_list_2_display_size;
+ }
+
+ expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
+ expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_2_display;
+
break;
case 3:
- DML_ASSERT(base_strategy_list_3_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
-
- /* populate list */
- pmo_dcn4_fams2_expand_base_pstate_strategies(
- base_strategy_list_3_display,
- base_strategy_list_3_display_size,
- i,
- pmo->init_data.pmo_dcn4.expanded_strategy_list_3_display,
- &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]);
+ if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
+ base_list = pmo->options->override_strategy_lists[i];
+ base_list_size = pmo->options->num_override_strategies_per_list[i];
+ } else {
+ base_list = base_strategy_list_3_display;
+ base_list_size = base_strategy_list_3_display_size;
+ }
+
+ expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
+ expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_3_display;
+
break;
case 4:
- DML_ASSERT(base_strategy_list_4_display_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
-
- /* populate list */
- pmo_dcn4_fams2_expand_base_pstate_strategies(
- base_strategy_list_4_display,
- base_strategy_list_4_display_size,
- i,
- pmo->init_data.pmo_dcn4.expanded_strategy_list_4_display,
- &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i - 1]);
+ if (pmo->options->override_strategy_lists[i] && pmo->options->num_override_strategies_per_list[i]) {
+ base_list = pmo->options->override_strategy_lists[i];
+ base_list_size = pmo->options->num_override_strategies_per_list[i];
+ } else {
+ base_list = base_strategy_list_4_display;
+ base_list_size = base_strategy_list_4_display_size;
+ }
+
+ expanded_list_size = &pmo->init_data.pmo_dcn4.num_expanded_strategies_per_list[i];
+ expanded_list = pmo->init_data.pmo_dcn4.expanded_strategy_list_4_display;
+
break;
}
+
+ DML_ASSERT(base_list_size <= PMO_DCN4_MAX_BASE_STRATEGIES);
+
+ /* populate list */
+ pmo_dcn4_fams2_expand_base_pstate_strategies(
+ base_list,
+ base_list_size,
+ i + 1,
+ expanded_list,
+ expanded_list_size);
}
return true;
@@ -1026,13 +1049,13 @@ static bool all_timings_support_vblank(const struct dml2_pmo_instance *pmo,
return synchronizable;
}
-static unsigned int calc_svp_microschedule(const struct dml2_fams2_meta *fams2_meta)
+static unsigned int calc_svp_microschedule(const struct dml2_pstate_meta *pstate_meta)
{
- return fams2_meta->contention_delay_otg_vlines +
- fams2_meta->method_subvp.programming_delay_otg_vlines +
- fams2_meta->method_subvp.phantom_vtotal +
- fams2_meta->method_subvp.prefetch_to_mall_delay_otg_vlines +
- fams2_meta->dram_clk_change_blackout_otg_vlines;
+ return pstate_meta->contention_delay_otg_vlines +
+ pstate_meta->method_subvp.programming_delay_otg_vlines +
+ pstate_meta->method_subvp.phantom_vtotal +
+ pstate_meta->method_subvp.prefetch_to_mall_delay_otg_vlines +
+ pstate_meta->blackout_otg_vlines;
}
static bool all_timings_support_drr(const struct dml2_pmo_instance *pmo,
@@ -1042,29 +1065,29 @@ static bool all_timings_support_drr(const struct dml2_pmo_instance *pmo,
unsigned int i;
for (i = 0; i < DML2_MAX_PLANES; i++) {
const struct dml2_stream_parameters *stream_descriptor;
- const struct dml2_fams2_meta *stream_fams2_meta;
+ const struct dml2_pstate_meta *stream_pstate_meta;
if (is_bit_set_in_bitfield(mask, i)) {
stream_descriptor = &display_config->display_config.stream_descriptors[i];
- stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[i];
+ stream_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[i];
if (!stream_descriptor->timing.drr_config.enabled)
return false;
/* cannot support required vtotal */
- if (stream_fams2_meta->method_drr.stretched_vtotal > stream_fams2_meta->max_vtotal) {
+ if (stream_pstate_meta->method_drr.stretched_vtotal > stream_pstate_meta->max_vtotal) {
return false;
}
/* check rr is within bounds */
- if (stream_fams2_meta->nom_refresh_rate_hz < pmo->fams_params.v2.drr.refresh_rate_limit_min ||
- stream_fams2_meta->nom_refresh_rate_hz > pmo->fams_params.v2.drr.refresh_rate_limit_max) {
+ if (stream_pstate_meta->nom_refresh_rate_hz < pmo->fams_params.v2.drr.refresh_rate_limit_min ||
+ stream_pstate_meta->nom_refresh_rate_hz > pmo->fams_params.v2.drr.refresh_rate_limit_max) {
return false;
}
/* check required stretch is allowed */
if (stream_descriptor->timing.drr_config.max_instant_vtotal_delta > 0 &&
- stream_fams2_meta->method_drr.stretched_vtotal - stream_fams2_meta->nom_vtotal > stream_descriptor->timing.drr_config.max_instant_vtotal_delta) {
+ stream_pstate_meta->method_drr.stretched_vtotal - stream_pstate_meta->nom_vtotal > (int)stream_descriptor->timing.drr_config.max_instant_vtotal_delta) {
return false;
}
}
@@ -1079,7 +1102,7 @@ static bool all_timings_support_svp(const struct dml2_pmo_instance *pmo,
{
const struct dml2_stream_parameters *stream_descriptor;
const struct dml2_plane_parameters *plane_descriptor;
- const struct dml2_fams2_meta *stream_fams2_meta;
+ const struct dml2_pstate_meta *stream_pstate_meta;
unsigned int microschedule_vlines;
unsigned int i;
unsigned int mcaches_per_plane;
@@ -1124,13 +1147,13 @@ static bool all_timings_support_svp(const struct dml2_pmo_instance *pmo,
for (i = 0; i < DML2_MAX_PLANES; i++) {
if (is_bit_set_in_bitfield(mask, i)) {
stream_descriptor = &display_config->display_config.stream_descriptors[i];
- stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[i];
+ stream_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[i];
if (stream_descriptor->overrides.disable_subvp) {
return false;
}
- microschedule_vlines = calc_svp_microschedule(&pmo->scratch.pmo_dcn4.stream_fams2_meta[i]);
+ microschedule_vlines = calc_svp_microschedule(&pmo->scratch.pmo_dcn4.stream_pstate_meta[i]);
/* block if using an interlaced timing */
if (stream_descriptor->timing.interlaced) {
@@ -1141,8 +1164,8 @@ static bool all_timings_support_svp(const struct dml2_pmo_instance *pmo,
* 2) refresh rate must be within the allowed bounds
*/
if (microschedule_vlines >= stream_descriptor->timing.v_active ||
- (stream_fams2_meta->nom_refresh_rate_hz < pmo->fams_params.v2.subvp.refresh_rate_limit_min ||
- stream_fams2_meta->nom_refresh_rate_hz > pmo->fams_params.v2.subvp.refresh_rate_limit_max)) {
+ (stream_pstate_meta->nom_refresh_rate_hz < pmo->fams_params.v2.subvp.refresh_rate_limit_min ||
+ stream_pstate_meta->nom_refresh_rate_hz > pmo->fams_params.v2.subvp.refresh_rate_limit_max)) {
return false;
}
}
@@ -1232,43 +1255,43 @@ static bool all_planes_match_method(const struct display_configuation_with_meta
}
static void build_method_scheduling_params(
- struct dml2_fams2_per_method_common_meta *stream_method_fams2_meta,
- struct dml2_fams2_meta *stream_fams2_meta)
+ struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta,
+ struct dml2_pstate_meta *stream_pstate_meta)
{
- stream_method_fams2_meta->allow_time_us =
- (double)((int)stream_method_fams2_meta->allow_end_otg_vline - (int)stream_method_fams2_meta->allow_start_otg_vline) *
- stream_fams2_meta->otg_vline_time_us;
- if (stream_method_fams2_meta->allow_time_us >= stream_method_fams2_meta->period_us) {
+ stream_method_pstate_meta->allow_time_us =
+ (double)((int)stream_method_pstate_meta->allow_end_otg_vline - (int)stream_method_pstate_meta->allow_start_otg_vline) *
+ stream_pstate_meta->otg_vline_time_us;
+ if (stream_method_pstate_meta->allow_time_us >= stream_method_pstate_meta->period_us) {
/* when allow wave overlaps an entire frame, it is always schedulable (DRR can do this)*/
- stream_method_fams2_meta->disallow_time_us = 0.0;
+ stream_method_pstate_meta->disallow_time_us = 0.0;
} else {
- stream_method_fams2_meta->disallow_time_us =
- stream_method_fams2_meta->period_us - stream_method_fams2_meta->allow_time_us;
+ stream_method_pstate_meta->disallow_time_us =
+ stream_method_pstate_meta->period_us - stream_method_pstate_meta->allow_time_us;
}
}
-static struct dml2_fams2_per_method_common_meta *get_per_method_common_meta(
+static struct dml2_pstate_per_method_common_meta *get_per_method_common_meta(
struct dml2_pmo_instance *pmo,
enum dml2_pstate_method stream_pstate_method,
int stream_idx)
{
- struct dml2_fams2_per_method_common_meta *stream_method_fams2_meta = NULL;
+ struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta = NULL;
switch (stream_pstate_method) {
case dml2_pstate_method_vactive:
case dml2_pstate_method_fw_vactive_drr:
- stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_vactive.common;
+ stream_method_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_idx].method_vactive.common;
break;
case dml2_pstate_method_vblank:
case dml2_pstate_method_fw_vblank_drr:
- stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_vblank.common;
+ stream_method_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_idx].method_vblank.common;
break;
case dml2_pstate_method_fw_svp:
case dml2_pstate_method_fw_svp_drr:
- stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_subvp.common;
+ stream_method_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_idx].method_subvp.common;
break;
case dml2_pstate_method_fw_drr:
- stream_method_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_idx].method_drr.common;
+ stream_method_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_idx].method_drr.common;
break;
case dml2_pstate_method_reserved_hw:
case dml2_pstate_method_reserved_fw:
@@ -1277,10 +1300,10 @@ static struct dml2_fams2_per_method_common_meta *get_per_method_common_meta(
case dml2_pstate_method_count:
case dml2_pstate_method_na:
default:
- stream_method_fams2_meta = NULL;
+ stream_method_pstate_meta = NULL;
}
- return stream_method_fams2_meta;
+ return stream_method_pstate_meta;
}
static bool is_timing_group_schedulable(
@@ -1288,10 +1311,10 @@ static bool is_timing_group_schedulable(
const struct display_configuation_with_meta *display_cfg,
const struct dml2_pmo_pstate_strategy *pstate_strategy,
const unsigned int timing_group_idx,
- struct dml2_fams2_per_method_common_meta *group_fams2_meta)
+ struct dml2_pstate_per_method_common_meta *group_pstate_meta)
{
unsigned int i;
- struct dml2_fams2_per_method_common_meta *stream_method_fams2_meta;
+ struct dml2_pstate_per_method_common_meta *stream_method_pstate_meta;
unsigned int base_stream_idx = 0;
struct dml2_pmo_scratch *s = &pmo->scratch;
@@ -1305,31 +1328,31 @@ static bool is_timing_group_schedulable(
}
/* init allow start and end lines for timing group */
- stream_method_fams2_meta = get_per_method_common_meta(pmo, pstate_strategy->per_stream_pstate_method[base_stream_idx], base_stream_idx);
- if (!stream_method_fams2_meta)
+ stream_method_pstate_meta = get_per_method_common_meta(pmo, pstate_strategy->per_stream_pstate_method[base_stream_idx], base_stream_idx);
+ if (!stream_method_pstate_meta)
return false;
- group_fams2_meta->allow_start_otg_vline = stream_method_fams2_meta->allow_start_otg_vline;
- group_fams2_meta->allow_end_otg_vline = stream_method_fams2_meta->allow_end_otg_vline;
- group_fams2_meta->period_us = stream_method_fams2_meta->period_us;
+ group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline;
+ group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline;
+ group_pstate_meta->period_us = stream_method_pstate_meta->period_us;
for (i = base_stream_idx + 1; i < display_cfg->display_config.num_streams; i++) {
if (is_bit_set_in_bitfield(pmo->scratch.pmo_dcn4.synchronized_timing_group_masks[timing_group_idx], i)) {
- stream_method_fams2_meta = get_per_method_common_meta(pmo, pstate_strategy->per_stream_pstate_method[i], i);
- if (!stream_method_fams2_meta)
+ stream_method_pstate_meta = get_per_method_common_meta(pmo, pstate_strategy->per_stream_pstate_method[i], i);
+ if (!stream_method_pstate_meta)
continue;
- if (group_fams2_meta->allow_start_otg_vline < stream_method_fams2_meta->allow_start_otg_vline) {
+ if (group_pstate_meta->allow_start_otg_vline < stream_method_pstate_meta->allow_start_otg_vline) {
/* set group allow start to larger otg vline */
- group_fams2_meta->allow_start_otg_vline = stream_method_fams2_meta->allow_start_otg_vline;
+ group_pstate_meta->allow_start_otg_vline = stream_method_pstate_meta->allow_start_otg_vline;
}
- if (group_fams2_meta->allow_end_otg_vline > stream_method_fams2_meta->allow_end_otg_vline) {
+ if (group_pstate_meta->allow_end_otg_vline > stream_method_pstate_meta->allow_end_otg_vline) {
/* set group allow end to smaller otg vline */
- group_fams2_meta->allow_end_otg_vline = stream_method_fams2_meta->allow_end_otg_vline;
+ group_pstate_meta->allow_end_otg_vline = stream_method_pstate_meta->allow_end_otg_vline;
}
/* check waveform still has positive width */
- if (group_fams2_meta->allow_start_otg_vline >= group_fams2_meta->allow_end_otg_vline) {
+ if (group_pstate_meta->allow_start_otg_vline >= group_pstate_meta->allow_end_otg_vline) {
/* timing group is not schedulable */
return false;
}
@@ -1337,10 +1360,10 @@ static bool is_timing_group_schedulable(
}
/* calculate the rest of the meta */
- build_method_scheduling_params(group_fams2_meta, &pmo->scratch.pmo_dcn4.stream_fams2_meta[base_stream_idx]);
+ build_method_scheduling_params(group_pstate_meta, &pmo->scratch.pmo_dcn4.stream_pstate_meta[base_stream_idx]);
- return group_fams2_meta->allow_time_us > 0.0 &&
- group_fams2_meta->disallow_time_us < pmo->ip_caps->fams2.max_allow_delay_us;
+ return group_pstate_meta->allow_time_us > 0.0 &&
+ group_pstate_meta->disallow_time_us < pmo->ip_caps->fams2.max_allow_delay_us;
}
static bool is_config_schedulable(
@@ -1354,7 +1377,7 @@ static bool is_config_schedulable(
double max_allow_delay_us = 0.0;
- memset(s->pmo_dcn4.group_common_fams2_meta, 0, sizeof(s->pmo_dcn4.group_common_fams2_meta));
+ memset(s->pmo_dcn4.group_common_pstate_meta, 0, sizeof(s->pmo_dcn4.group_common_pstate_meta));
memset(s->pmo_dcn4.sorted_group_gtl_disallow_index, 0, sizeof(unsigned int) * DML2_MAX_PLANES);
/* search for a general solution to the schedule */
@@ -1369,12 +1392,12 @@ static bool is_config_schedulable(
for (i = 0; i < s->pmo_dcn4.num_timing_groups; i++) {
s->pmo_dcn4.sorted_group_gtl_disallow_index[i] = i;
s->pmo_dcn4.sorted_group_gtl_period_index[i] = i;
- if (!is_timing_group_schedulable(pmo, display_cfg, pstate_strategy, i, &s->pmo_dcn4.group_common_fams2_meta[i])) {
+ if (!is_timing_group_schedulable(pmo, display_cfg, pstate_strategy, i, &s->pmo_dcn4.group_common_pstate_meta[i])) {
/* synchronized timing group was not schedulable */
schedulable = false;
break;
}
- max_allow_delay_us += s->pmo_dcn4.group_common_fams2_meta[i].disallow_time_us;
+ max_allow_delay_us += s->pmo_dcn4.group_common_pstate_meta[i].disallow_time_us;
}
if ((schedulable && s->pmo_dcn4.num_timing_groups <= 1) || !schedulable) {
@@ -1391,8 +1414,8 @@ static bool is_config_schedulable(
bool swapped = false;
for (j = 0; j < s->pmo_dcn4.num_timing_groups - 1; j++) {
- double j_disallow_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_disallow_index[j]].disallow_time_us;
- double jp1_disallow_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_disallow_index[j + 1]].disallow_time_us;
+ double j_disallow_us = s->pmo_dcn4.group_common_pstate_meta[s->pmo_dcn4.sorted_group_gtl_disallow_index[j]].disallow_time_us;
+ double jp1_disallow_us = s->pmo_dcn4.group_common_pstate_meta[s->pmo_dcn4.sorted_group_gtl_disallow_index[j + 1]].disallow_time_us;
if (j_disallow_us < jp1_disallow_us) {
/* swap as A < B */
swap(s->pmo_dcn4.sorted_group_gtl_disallow_index[j],
@@ -1410,19 +1433,19 @@ static bool is_config_schedulable(
* other display, or when >2 streams continue to halve the remaining allow time.
*/
for (i = 0; i < s->pmo_dcn4.num_timing_groups; i++) {
- if (s->pmo_dcn4.group_common_fams2_meta[i].disallow_time_us <= 0.0) {
+ if (s->pmo_dcn4.group_common_pstate_meta[i].disallow_time_us <= 0.0) {
/* this timing group always allows */
continue;
}
- double max_allow_time_us = s->pmo_dcn4.group_common_fams2_meta[i].allow_time_us;
+ double max_allow_time_us = s->pmo_dcn4.group_common_pstate_meta[i].allow_time_us;
for (j = 0; j < s->pmo_dcn4.num_timing_groups; j++) {
unsigned int sorted_j = s->pmo_dcn4.sorted_group_gtl_disallow_index[j];
/* stream can't overlap itself */
- if (i != sorted_j && s->pmo_dcn4.group_common_fams2_meta[sorted_j].disallow_time_us > 0.0) {
+ if (i != sorted_j && s->pmo_dcn4.group_common_pstate_meta[sorted_j].disallow_time_us > 0.0) {
max_allow_time_us = math_min2(
- s->pmo_dcn4.group_common_fams2_meta[sorted_j].allow_time_us,
- (max_allow_time_us - s->pmo_dcn4.group_common_fams2_meta[sorted_j].disallow_time_us) / 2);
+ s->pmo_dcn4.group_common_pstate_meta[sorted_j].allow_time_us,
+ (max_allow_time_us - s->pmo_dcn4.group_common_pstate_meta[sorted_j].disallow_time_us) / 2);
if (max_allow_time_us < 0.0) {
/* failed exit early */
@@ -1450,8 +1473,8 @@ static bool is_config_schedulable(
bool swapped = false;
for (j = 0; j < s->pmo_dcn4.num_timing_groups - 1; j++) {
- double j_period_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_period_index[j]].period_us;
- double jp1_period_us = s->pmo_dcn4.group_common_fams2_meta[s->pmo_dcn4.sorted_group_gtl_period_index[j + 1]].period_us;
+ double j_period_us = s->pmo_dcn4.group_common_pstate_meta[s->pmo_dcn4.sorted_group_gtl_period_index[j]].period_us;
+ double jp1_period_us = s->pmo_dcn4.group_common_pstate_meta[s->pmo_dcn4.sorted_group_gtl_period_index[j + 1]].period_us;
if (j_period_us < jp1_period_us) {
/* swap as A < B */
swap(s->pmo_dcn4.sorted_group_gtl_period_index[j],
@@ -1470,7 +1493,7 @@ static bool is_config_schedulable(
unsigned int sorted_i = s->pmo_dcn4.sorted_group_gtl_period_index[i];
unsigned int sorted_ip1 = s->pmo_dcn4.sorted_group_gtl_period_index[i + 1];
- if (s->pmo_dcn4.group_common_fams2_meta[sorted_i].allow_time_us < s->pmo_dcn4.group_common_fams2_meta[sorted_ip1].period_us ||
+ if (s->pmo_dcn4.group_common_pstate_meta[sorted_i].allow_time_us < s->pmo_dcn4.group_common_pstate_meta[sorted_ip1].period_us ||
(s->pmo_dcn4.group_is_drr_enabled[sorted_ip1] && s->pmo_dcn4.group_is_drr_active[sorted_ip1])) {
schedulable = false;
break;
@@ -1492,18 +1515,18 @@ static bool is_config_schedulable(
/* default period_0 > period_1 */
unsigned int lrg_idx = 0;
unsigned int sml_idx = 1;
- if (s->pmo_dcn4.group_common_fams2_meta[0].period_us < s->pmo_dcn4.group_common_fams2_meta[1].period_us) {
+ if (s->pmo_dcn4.group_common_pstate_meta[0].period_us < s->pmo_dcn4.group_common_pstate_meta[1].period_us) {
/* period_0 < period_1 */
lrg_idx = 1;
sml_idx = 0;
}
- period_ratio = s->pmo_dcn4.group_common_fams2_meta[lrg_idx].period_us / s->pmo_dcn4.group_common_fams2_meta[sml_idx].period_us;
- shift_per_period = s->pmo_dcn4.group_common_fams2_meta[sml_idx].period_us * (period_ratio - math_floor(period_ratio));
- max_shift_us = s->pmo_dcn4.group_common_fams2_meta[lrg_idx].disallow_time_us - s->pmo_dcn4.group_common_fams2_meta[sml_idx].allow_time_us;
- max_allow_delay_us = max_shift_us / shift_per_period * s->pmo_dcn4.group_common_fams2_meta[lrg_idx].period_us;
+ period_ratio = s->pmo_dcn4.group_common_pstate_meta[lrg_idx].period_us / s->pmo_dcn4.group_common_pstate_meta[sml_idx].period_us;
+ shift_per_period = s->pmo_dcn4.group_common_pstate_meta[sml_idx].period_us * (period_ratio - math_floor(period_ratio));
+ max_shift_us = s->pmo_dcn4.group_common_pstate_meta[lrg_idx].disallow_time_us - s->pmo_dcn4.group_common_pstate_meta[sml_idx].allow_time_us;
+ max_allow_delay_us = max_shift_us / shift_per_period * s->pmo_dcn4.group_common_pstate_meta[lrg_idx].period_us;
if (shift_per_period > 0.0 &&
- shift_per_period < s->pmo_dcn4.group_common_fams2_meta[lrg_idx].allow_time_us + s->pmo_dcn4.group_common_fams2_meta[sml_idx].allow_time_us &&
+ shift_per_period < s->pmo_dcn4.group_common_pstate_meta[lrg_idx].allow_time_us + s->pmo_dcn4.group_common_pstate_meta[sml_idx].allow_time_us &&
max_allow_delay_us < pmo->ip_caps->fams2.max_allow_delay_us) {
schedulable = true;
}
@@ -1646,22 +1669,22 @@ static int get_vactive_pstate_margin(const struct display_configuation_with_meta
return min_vactive_margin_us;
}
-static unsigned int get_vactive_det_fill_latency_delay_us(const struct display_configuation_with_meta *display_cfg, int plane_mask)
+static int get_vactive_det_fill_latency_delay_us(const struct display_configuation_with_meta *display_cfg, int plane_mask)
{
unsigned char i;
- unsigned int max_vactive_fill_us = 0;
+ int max_vactive_fill_us = 0;
for (i = 0; i < DML2_MAX_PLANES; i++) {
if (is_bit_set_in_bitfield(plane_mask, i)) {
- if (display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].dram_change_vactive_det_fill_delay_us > max_vactive_fill_us)
- max_vactive_fill_us = display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].dram_change_vactive_det_fill_delay_us;
+ if (display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[dml2_pstate_type_uclk] > max_vactive_fill_us)
+ max_vactive_fill_us = display_cfg->mode_support_result.cfg_support_info.plane_support_info[i].vactive_det_fill_delay_us[dml2_pstate_type_uclk];
}
}
return max_vactive_fill_us;
}
-static void build_fams2_meta_per_stream(struct dml2_pmo_instance *pmo,
+static void build_pstate_meta_per_stream(struct dml2_pmo_instance *pmo,
struct display_configuation_with_meta *display_config,
int stream_index)
{
@@ -1669,7 +1692,7 @@ static void build_fams2_meta_per_stream(struct dml2_pmo_instance *pmo,
const struct dml2_stream_parameters *stream_descriptor = &display_config->display_config.stream_descriptors[stream_index];
const struct core_stream_support_info *stream_info = &display_config->mode_support_result.cfg_support_info.stream_support_info[stream_index];
const struct dml2_timing_cfg *timing = &stream_descriptor->timing;
- struct dml2_fams2_meta *stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index];
+ struct dml2_pstate_meta *stream_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_index];
/* worst case all other streams require some programming at the same time, 0 if only 1 stream */
unsigned int contention_delay_us = (ip_caps->fams2.vertical_interrupt_ack_delay_us +
@@ -1677,142 +1700,142 @@ static void build_fams2_meta_per_stream(struct dml2_pmo_instance *pmo,
(display_config->display_config.num_streams - 1);
/* common */
- stream_fams2_meta->valid = true;
- stream_fams2_meta->otg_vline_time_us = (double)timing->h_total / timing->pixel_clock_khz * 1000.0;
- stream_fams2_meta->nom_vtotal = stream_descriptor->timing.vblank_nom + stream_descriptor->timing.v_active;
- stream_fams2_meta->nom_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
- (stream_fams2_meta->nom_vtotal * timing->h_total);
- stream_fams2_meta->nom_frame_time_us =
- (double)stream_fams2_meta->nom_vtotal * stream_fams2_meta->otg_vline_time_us;
- stream_fams2_meta->vblank_start = timing->v_blank_end + timing->v_active;
+ stream_pstate_meta->valid = true;
+ stream_pstate_meta->otg_vline_time_us = (double)timing->h_total / timing->pixel_clock_khz * 1000.0;
+ stream_pstate_meta->nom_vtotal = stream_descriptor->timing.vblank_nom + stream_descriptor->timing.v_active;
+ stream_pstate_meta->nom_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
+ (stream_pstate_meta->nom_vtotal * timing->h_total);
+ stream_pstate_meta->nom_frame_time_us =
+ (double)stream_pstate_meta->nom_vtotal * stream_pstate_meta->otg_vline_time_us;
+ stream_pstate_meta->vblank_start = timing->v_blank_end + timing->v_active;
if (stream_descriptor->timing.drr_config.enabled == true) {
if (stream_descriptor->timing.drr_config.min_refresh_uhz != 0.0) {
- stream_fams2_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
+ stream_pstate_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
((double)stream_descriptor->timing.drr_config.min_refresh_uhz * stream_descriptor->timing.h_total) * 1e9);
} else {
/* assume min of 48Hz */
- stream_fams2_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
+ stream_pstate_meta->max_vtotal = (unsigned int)math_floor((double)stream_descriptor->timing.pixel_clock_khz /
(48000000.0 * stream_descriptor->timing.h_total) * 1e9);
}
} else {
- stream_fams2_meta->max_vtotal = stream_fams2_meta->nom_vtotal;
- }
- stream_fams2_meta->min_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
- (stream_fams2_meta->max_vtotal * timing->h_total);
- stream_fams2_meta->max_frame_time_us =
- (double)stream_fams2_meta->max_vtotal * stream_fams2_meta->otg_vline_time_us;
-
- stream_fams2_meta->scheduling_delay_otg_vlines =
- (unsigned int)math_ceil(ip_caps->fams2.scheduling_delay_us / stream_fams2_meta->otg_vline_time_us);
- stream_fams2_meta->vertical_interrupt_ack_delay_otg_vlines =
- (unsigned int)math_ceil(ip_caps->fams2.vertical_interrupt_ack_delay_us / stream_fams2_meta->otg_vline_time_us);
- stream_fams2_meta->contention_delay_otg_vlines =
- (unsigned int)math_ceil(contention_delay_us / stream_fams2_meta->otg_vline_time_us);
+ stream_pstate_meta->max_vtotal = stream_pstate_meta->nom_vtotal;
+ }
+ stream_pstate_meta->min_refresh_rate_hz = timing->pixel_clock_khz * 1000.0 /
+ (stream_pstate_meta->max_vtotal * timing->h_total);
+ stream_pstate_meta->max_frame_time_us =
+ (double)stream_pstate_meta->max_vtotal * stream_pstate_meta->otg_vline_time_us;
+
+ stream_pstate_meta->scheduling_delay_otg_vlines =
+ (unsigned int)math_ceil(ip_caps->fams2.scheduling_delay_us / stream_pstate_meta->otg_vline_time_us);
+ stream_pstate_meta->vertical_interrupt_ack_delay_otg_vlines =
+ (unsigned int)math_ceil(ip_caps->fams2.vertical_interrupt_ack_delay_us / stream_pstate_meta->otg_vline_time_us);
+ stream_pstate_meta->contention_delay_otg_vlines =
+ (unsigned int)math_ceil(contention_delay_us / stream_pstate_meta->otg_vline_time_us);
/* worst case allow to target needs to account for all streams' allow events overlapping, and 1 line for error */
- stream_fams2_meta->allow_to_target_delay_otg_vlines =
- (unsigned int)(math_ceil((ip_caps->fams2.vertical_interrupt_ack_delay_us + contention_delay_us + ip_caps->fams2.allow_programming_delay_us) / stream_fams2_meta->otg_vline_time_us)) + 1;
- stream_fams2_meta->min_allow_width_otg_vlines =
- (unsigned int)math_ceil(ip_caps->fams2.min_allow_width_us / stream_fams2_meta->otg_vline_time_us);
+ stream_pstate_meta->allow_to_target_delay_otg_vlines =
+ (unsigned int)(math_ceil((ip_caps->fams2.vertical_interrupt_ack_delay_us + contention_delay_us + ip_caps->fams2.allow_programming_delay_us) / stream_pstate_meta->otg_vline_time_us)) + 1;
+ stream_pstate_meta->min_allow_width_otg_vlines =
+ (unsigned int)math_ceil(ip_caps->fams2.min_allow_width_us / stream_pstate_meta->otg_vline_time_us);
/* this value should account for urgent latency */
- stream_fams2_meta->dram_clk_change_blackout_otg_vlines =
+ stream_pstate_meta->blackout_otg_vlines =
(unsigned int)math_ceil(pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us /
- stream_fams2_meta->otg_vline_time_us);
+ stream_pstate_meta->otg_vline_time_us);
/* scheduling params should be built based on the worst case for allow_time:disallow_time */
/* vactive */
if (display_config->display_config.num_streams == 1) {
/* for single stream, guarantee at least an instant of allow */
- stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (unsigned int)math_floor(
+ stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines = (unsigned int)math_floor(
math_max2(0.0,
- timing->v_active - math_max2(1.0, stream_fams2_meta->min_allow_width_otg_vlines) - stream_fams2_meta->dram_clk_change_blackout_otg_vlines));
+ timing->v_active - math_max2(1.0, stream_pstate_meta->min_allow_width_otg_vlines) - stream_pstate_meta->blackout_otg_vlines));
} else {
/* for multi stream, bound to a max fill time defined by IP caps */
- stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines =
- (unsigned int)math_floor((double)ip_caps->max_vactive_det_fill_delay_us / stream_fams2_meta->otg_vline_time_us);
+ stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines =
+ (unsigned int)math_floor((double)ip_caps->max_vactive_det_fill_delay_us / stream_pstate_meta->otg_vline_time_us);
}
- stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_us = stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines * stream_fams2_meta->otg_vline_time_us;
+ stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us = stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines * stream_pstate_meta->otg_vline_time_us;
- if (stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_us > 0.0) {
- stream_fams2_meta->method_vactive.common.allow_start_otg_vline =
- timing->v_blank_end + stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines;
- stream_fams2_meta->method_vactive.common.allow_end_otg_vline =
- stream_fams2_meta->vblank_start -
- stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
+ if (stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us > 0.0) {
+ stream_pstate_meta->method_vactive.common.allow_start_otg_vline =
+ timing->v_blank_end + stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_otg_vlines;
+ stream_pstate_meta->method_vactive.common.allow_end_otg_vline =
+ stream_pstate_meta->vblank_start -
+ stream_pstate_meta->blackout_otg_vlines;
} else {
- stream_fams2_meta->method_vactive.common.allow_start_otg_vline = 0;
- stream_fams2_meta->method_vactive.common.allow_end_otg_vline = 0;
+ stream_pstate_meta->method_vactive.common.allow_start_otg_vline = 0;
+ stream_pstate_meta->method_vactive.common.allow_end_otg_vline = 0;
}
- stream_fams2_meta->method_vactive.common.period_us = stream_fams2_meta->nom_frame_time_us;
- build_method_scheduling_params(&stream_fams2_meta->method_vactive.common, stream_fams2_meta);
+ stream_pstate_meta->method_vactive.common.period_us = stream_pstate_meta->nom_frame_time_us;
+ build_method_scheduling_params(&stream_pstate_meta->method_vactive.common, stream_pstate_meta);
/* vblank */
- stream_fams2_meta->method_vblank.common.allow_start_otg_vline = stream_fams2_meta->vblank_start;
- stream_fams2_meta->method_vblank.common.allow_end_otg_vline =
- stream_fams2_meta->method_vblank.common.allow_start_otg_vline + 1;
- stream_fams2_meta->method_vblank.common.period_us = stream_fams2_meta->nom_frame_time_us;
- build_method_scheduling_params(&stream_fams2_meta->method_vblank.common, stream_fams2_meta);
+ stream_pstate_meta->method_vblank.common.allow_start_otg_vline = stream_pstate_meta->vblank_start;
+ stream_pstate_meta->method_vblank.common.allow_end_otg_vline =
+ stream_pstate_meta->method_vblank.common.allow_start_otg_vline + 1;
+ stream_pstate_meta->method_vblank.common.period_us = stream_pstate_meta->nom_frame_time_us;
+ build_method_scheduling_params(&stream_pstate_meta->method_vblank.common, stream_pstate_meta);
/* subvp */
- stream_fams2_meta->method_subvp.programming_delay_otg_vlines =
- (unsigned int)math_ceil(ip_caps->fams2.subvp_programming_delay_us / stream_fams2_meta->otg_vline_time_us);
- stream_fams2_meta->method_subvp.df_throttle_delay_otg_vlines =
- (unsigned int)math_ceil(ip_caps->fams2.subvp_df_throttle_delay_us / stream_fams2_meta->otg_vline_time_us);
- stream_fams2_meta->method_subvp.prefetch_to_mall_delay_otg_vlines =
- (unsigned int)math_ceil(ip_caps->fams2.subvp_prefetch_to_mall_delay_us / stream_fams2_meta->otg_vline_time_us);
- stream_fams2_meta->method_subvp.phantom_vactive =
- stream_fams2_meta->allow_to_target_delay_otg_vlines +
- stream_fams2_meta->min_allow_width_otg_vlines +
+ stream_pstate_meta->method_subvp.programming_delay_otg_vlines =
+ (unsigned int)math_ceil(ip_caps->fams2.subvp_programming_delay_us / stream_pstate_meta->otg_vline_time_us);
+ stream_pstate_meta->method_subvp.df_throttle_delay_otg_vlines =
+ (unsigned int)math_ceil(ip_caps->fams2.subvp_df_throttle_delay_us / stream_pstate_meta->otg_vline_time_us);
+ stream_pstate_meta->method_subvp.prefetch_to_mall_delay_otg_vlines =
+ (unsigned int)math_ceil(ip_caps->fams2.subvp_prefetch_to_mall_delay_us / stream_pstate_meta->otg_vline_time_us);
+ stream_pstate_meta->method_subvp.phantom_vactive =
+ stream_pstate_meta->allow_to_target_delay_otg_vlines +
+ stream_pstate_meta->min_allow_width_otg_vlines +
stream_info->phantom_min_v_active;
- stream_fams2_meta->method_subvp.phantom_vfp =
- stream_fams2_meta->method_subvp.df_throttle_delay_otg_vlines;
+ stream_pstate_meta->method_subvp.phantom_vfp =
+ stream_pstate_meta->method_subvp.df_throttle_delay_otg_vlines;
/* phantom vtotal = v_bp(vstartup) + v_sync(1) + v_fp(throttle_delay) + v_active(allow_to_target + min_allow + min_vactive)*/
- stream_fams2_meta->method_subvp.phantom_vtotal =
+ stream_pstate_meta->method_subvp.phantom_vtotal =
stream_info->phantom_v_startup +
- stream_fams2_meta->method_subvp.phantom_vfp +
+ stream_pstate_meta->method_subvp.phantom_vfp +
1 +
- stream_fams2_meta->method_subvp.df_throttle_delay_otg_vlines +
- stream_fams2_meta->method_subvp.phantom_vactive;
- stream_fams2_meta->method_subvp.common.allow_start_otg_vline =
+ stream_pstate_meta->method_subvp.df_throttle_delay_otg_vlines +
+ stream_pstate_meta->method_subvp.phantom_vactive;
+ stream_pstate_meta->method_subvp.common.allow_start_otg_vline =
stream_descriptor->timing.v_blank_end +
- stream_fams2_meta->contention_delay_otg_vlines +
- stream_fams2_meta->method_subvp.programming_delay_otg_vlines +
- stream_fams2_meta->method_subvp.phantom_vtotal +
- stream_fams2_meta->method_subvp.prefetch_to_mall_delay_otg_vlines +
- stream_fams2_meta->allow_to_target_delay_otg_vlines;
- stream_fams2_meta->method_subvp.common.allow_end_otg_vline =
- stream_fams2_meta->vblank_start -
- stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
- stream_fams2_meta->method_subvp.common.period_us = stream_fams2_meta->nom_frame_time_us;
- build_method_scheduling_params(&stream_fams2_meta->method_subvp.common, stream_fams2_meta);
+ stream_pstate_meta->contention_delay_otg_vlines +
+ stream_pstate_meta->method_subvp.programming_delay_otg_vlines +
+ stream_pstate_meta->method_subvp.phantom_vtotal +
+ stream_pstate_meta->method_subvp.prefetch_to_mall_delay_otg_vlines +
+ stream_pstate_meta->allow_to_target_delay_otg_vlines;
+ stream_pstate_meta->method_subvp.common.allow_end_otg_vline =
+ stream_pstate_meta->vblank_start -
+ stream_pstate_meta->blackout_otg_vlines;
+ stream_pstate_meta->method_subvp.common.period_us = stream_pstate_meta->nom_frame_time_us;
+ build_method_scheduling_params(&stream_pstate_meta->method_subvp.common, stream_pstate_meta);
/* drr */
- stream_fams2_meta->method_drr.programming_delay_otg_vlines =
- (unsigned int)math_ceil(ip_caps->fams2.drr_programming_delay_us / stream_fams2_meta->otg_vline_time_us);
- stream_fams2_meta->method_drr.common.allow_start_otg_vline =
- stream_fams2_meta->vblank_start +
- stream_fams2_meta->allow_to_target_delay_otg_vlines;
- stream_fams2_meta->method_drr.common.period_us = stream_fams2_meta->nom_frame_time_us;
+ stream_pstate_meta->method_drr.programming_delay_otg_vlines =
+ (unsigned int)math_ceil(ip_caps->fams2.drr_programming_delay_us / stream_pstate_meta->otg_vline_time_us);
+ stream_pstate_meta->method_drr.common.allow_start_otg_vline =
+ stream_pstate_meta->vblank_start +
+ stream_pstate_meta->allow_to_target_delay_otg_vlines;
+ stream_pstate_meta->method_drr.common.period_us = stream_pstate_meta->nom_frame_time_us;
if (display_config->display_config.num_streams <= 1) {
/* only need to stretch vblank for blackout time */
- stream_fams2_meta->method_drr.stretched_vtotal =
- stream_fams2_meta->nom_vtotal +
- stream_fams2_meta->allow_to_target_delay_otg_vlines +
- stream_fams2_meta->min_allow_width_otg_vlines +
- stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
+ stream_pstate_meta->method_drr.stretched_vtotal =
+ stream_pstate_meta->nom_vtotal +
+ stream_pstate_meta->allow_to_target_delay_otg_vlines +
+ stream_pstate_meta->min_allow_width_otg_vlines +
+ stream_pstate_meta->blackout_otg_vlines;
} else {
/* multi display needs to always be schedulable */
- stream_fams2_meta->method_drr.stretched_vtotal =
- stream_fams2_meta->nom_vtotal * 2 +
- stream_fams2_meta->allow_to_target_delay_otg_vlines +
- stream_fams2_meta->min_allow_width_otg_vlines +
- stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
- }
- stream_fams2_meta->method_drr.common.allow_end_otg_vline =
- stream_fams2_meta->method_drr.stretched_vtotal -
- stream_fams2_meta->dram_clk_change_blackout_otg_vlines;
- build_method_scheduling_params(&stream_fams2_meta->method_drr.common, stream_fams2_meta);
+ stream_pstate_meta->method_drr.stretched_vtotal =
+ stream_pstate_meta->nom_vtotal * 2 +
+ stream_pstate_meta->allow_to_target_delay_otg_vlines +
+ stream_pstate_meta->min_allow_width_otg_vlines +
+ stream_pstate_meta->blackout_otg_vlines;
+ }
+ stream_pstate_meta->method_drr.common.allow_end_otg_vline =
+ stream_pstate_meta->method_drr.stretched_vtotal -
+ stream_pstate_meta->blackout_otg_vlines;
+ build_method_scheduling_params(&stream_pstate_meta->method_drr.common, stream_pstate_meta);
}
static void build_subvp_meta_per_stream(struct dml2_pmo_instance *pmo,
@@ -1820,14 +1843,14 @@ static void build_subvp_meta_per_stream(struct dml2_pmo_instance *pmo,
int stream_index)
{
struct dml2_implicit_svp_meta *stream_svp_meta = &pmo->scratch.pmo_dcn4.stream_svp_meta[stream_index];
- struct dml2_fams2_meta *stream_fams2_meta = &pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index];
+ struct dml2_pstate_meta *stream_pstate_meta = &pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_index];
stream_svp_meta->valid = true;
/* PMO FAMS2 precaulcates these values */
- stream_svp_meta->v_active = stream_fams2_meta->method_subvp.phantom_vactive;
- stream_svp_meta->v_front_porch = stream_fams2_meta->method_subvp.phantom_vfp;
- stream_svp_meta->v_total = stream_fams2_meta->method_subvp.phantom_vtotal;
+ stream_svp_meta->v_active = stream_pstate_meta->method_subvp.phantom_vactive;
+ stream_svp_meta->v_front_porch = stream_pstate_meta->method_subvp.phantom_vfp;
+ stream_svp_meta->v_total = stream_pstate_meta->method_subvp.phantom_vtotal;
}
bool pmo_dcn4_fams2_init_for_pstate_support(struct dml2_pmo_init_for_pstate_support_in_out *in_out)
@@ -1879,7 +1902,7 @@ bool pmo_dcn4_fams2_init_for_pstate_support(struct dml2_pmo_init_for_pstate_supp
set_bit_in_bitfield(&s->pmo_dcn4.stream_vactive_capability_mask, stream_index);
/* FAMS2 meta */
- build_fams2_meta_per_stream(pmo, display_config, stream_index);
+ build_pstate_meta_per_stream(pmo, display_config, stream_index);
/* SVP meta */
build_subvp_meta_per_stream(pmo, display_config, stream_index);
@@ -1939,9 +1962,6 @@ static void reset_display_configuration(struct display_configuation_with_meta *d
for (stream_index = 0; stream_index < display_config->display_config.num_streams; stream_index++) {
display_config->stage3.stream_svp_meta[stream_index].valid = false;
-
- display_config->display_config.stream_descriptors[stream_index].overrides.minimize_active_latency_hiding = false;
- display_config->display_config.overrides.best_effort_min_active_latency_hiding_us = 0;
}
for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
@@ -1974,7 +1994,6 @@ static void setup_planes_for_drr_by_mask(struct display_configuation_with_meta *
plane->overrides.uclk_pstate_change_strategy = dml2_uclk_pstate_change_strategy_force_drr;
display_config->stage3.pstate_switch_modes[plane_index] = dml2_pstate_method_fw_drr;
-
}
}
}
@@ -2040,7 +2059,6 @@ static void setup_planes_for_vblank_by_mask(struct display_configuation_with_met
plane->overrides.reserved_vblank_time_ns);
display_config->stage3.pstate_switch_modes[plane_index] = dml2_pstate_method_vblank;
-
}
}
}
@@ -2055,6 +2073,7 @@ static void setup_planes_for_vblank_drr_by_mask(struct display_configuation_with
for (plane_index = 0; plane_index < display_config->display_config.num_planes; plane_index++) {
if (is_bit_set_in_bitfield(plane_mask, plane_index)) {
plane = &display_config->display_config.plane_descriptors[plane_index];
+
plane->overrides.reserved_vblank_time_ns = (long)(pmo->soc_bb->power_management_parameters.dram_clk_change_blackout_us * 1000);
display_config->stage3.pstate_switch_modes[plane_index] = dml2_pstate_method_fw_vblank_drr;
@@ -2076,8 +2095,8 @@ static void setup_planes_for_vactive_by_mask(struct display_configuation_with_me
display_config->stage3.pstate_switch_modes[plane_index] = dml2_pstate_method_vactive;
if (!pmo->options->disable_vactive_det_fill_bw_pad) {
- display_config->display_config.plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us =
- (unsigned int)math_floor(pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us);
+ display_config->display_config.plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk] =
+ (unsigned int)math_floor(pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us);
}
}
}
@@ -2097,8 +2116,8 @@ static void setup_planes_for_vactive_drr_by_mask(struct display_configuation_wit
display_config->stage3.pstate_switch_modes[plane_index] = dml2_pstate_method_fw_vactive_drr;
if (!pmo->options->disable_vactive_det_fill_bw_pad) {
- display_config->display_config.plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us =
- (unsigned int)math_floor(pmo->scratch.pmo_dcn4.stream_fams2_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us);
+ display_config->display_config.plane_descriptors[plane_index].overrides.max_vactive_det_fill_delay_us[dml2_pstate_type_uclk] =
+ (unsigned int)math_floor(pmo->scratch.pmo_dcn4.stream_pstate_meta[stream_index].method_vactive.max_vactive_det_fill_delay_us);
}
}
}
@@ -2144,9 +2163,9 @@ static bool setup_display_config(struct display_configuation_with_meta *display_
/* copy FAMS2 meta */
if (success) {
display_config->stage3.fams2_required = fams2_required;
- memcpy(&display_config->stage3.stream_fams2_meta,
- &scratch->pmo_dcn4.stream_fams2_meta,
- sizeof(struct dml2_fams2_meta) * DML2_MAX_PLANES);
+ memcpy(&display_config->stage3.stream_pstate_meta,
+ &scratch->pmo_dcn4.stream_pstate_meta,
+ sizeof(struct dml2_pstate_meta) * DML2_MAX_PLANES);
}
return success;
@@ -2188,12 +2207,12 @@ bool pmo_dcn4_fams2_test_for_pstate_support(struct dml2_pmo_test_for_pstate_supp
return false;
for (stream_index = 0; stream_index < in_out->base_display_config->display_config.num_streams; stream_index++) {
- struct dml2_fams2_meta *stream_fams2_meta = &s->pmo_dcn4.stream_fams2_meta[stream_index];
+ struct dml2_pstate_meta *stream_pstate_meta = &s->pmo_dcn4.stream_pstate_meta[stream_index];
if (s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_vactive ||
s->pmo_dcn4.pstate_strategy_candidates[s->pmo_dcn4.cur_pstate_candidate].per_stream_pstate_method[stream_index] == dml2_pstate_method_fw_vactive_drr) {
if (get_vactive_pstate_margin(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) < (MIN_VACTIVE_MARGIN_PCT * in_out->instance->soc_bb->power_management_parameters.dram_clk_change_blackout_us) ||
- get_vactive_det_fill_latency_delay_us(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) > stream_fams2_meta->method_vactive.max_vactive_det_fill_delay_us) {
+ get_vactive_det_fill_latency_delay_us(in_out->base_display_config, s->pmo_dcn4.stream_plane_mask[stream_index]) > stream_pstate_meta->method_vactive.max_vactive_det_fill_delay_us) {
p_state_supported = false;
break;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h
index 6baab7ad6ecc..6baab7ad6ecc 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_dcn4_fams2.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c
index 55d2464365d0..55d2464365d0 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.h
index 7218de1824cc..b90f6263cd85 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_pmo/dml2_pmo_factory.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_pmo/dml2_pmo_factory.h
@@ -10,4 +10,4 @@
bool dml2_pmo_create(enum dml2_project_id project_id, struct dml2_pmo_instance *out);
-#endif
+#endif \ No newline at end of file
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c
index e17b5ceba447..e17b5ceba447 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.h
index e13b0c5939b0..e13b0c5939b0 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_standalone_libraries/lib_float_math.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_standalone_libraries/lib_float_math.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_interfaces.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.c
index 5a33e2f357f4..5a33e2f357f4 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_interfaces.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_interfaces.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_legacy.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.c
index 5e14d85821e2..5e14d85821e2 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_legacy.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_legacy.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.h
index 14d0ae03dce6..14d0ae03dce6 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_legacy.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_legacy.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c
index 4a7c4c62111e..4a7c4c62111e 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.h
index 53bd8602f9ef..53bd8602f9ef 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/dml2_top/dml2_top_soc15.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_top/dml2_top_soc15.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/inc/dml2_debug.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_debug.h
index 611c80f4f1bf..611c80f4f1bf 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/inc/dml2_debug.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_debug.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/inc/dml2_internal_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h
index d52aa82283b3..1a6c0727cd2a 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml21/src/inc/dml2_internal_shared_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h
@@ -152,7 +152,7 @@ struct core_plane_support_info {
int active_latency_hiding_us;
int mall_svp_size_requirement_ways;
int nominal_vblank_pstate_latency_hiding_us;
- unsigned int dram_change_vactive_det_fill_delay_us;
+ int vactive_det_fill_delay_us[dml2_pstate_type_count];
};
struct core_stream_support_info {
@@ -209,6 +209,7 @@ struct dml2_core_mode_support_result {
unsigned int uclk_pstate_supported;
unsigned int fclk_pstate_supported;
+ struct dml2_core_internal_watermarks watermarks;
} global;
struct {
@@ -255,56 +256,70 @@ struct dml2_implicit_svp_meta {
unsigned long v_front_porch;
};
-struct dml2_fams2_per_method_common_meta {
+struct dml2_pstate_per_method_common_meta {
/* generic params */
- unsigned int allow_start_otg_vline;
- unsigned int allow_end_otg_vline;
+ int allow_start_otg_vline;
+ int allow_end_otg_vline;
/* scheduling params */
double allow_time_us;
double disallow_time_us;
double period_us;
};
-struct dml2_fams2_meta {
+struct dml2_pstate_meta {
bool valid;
double otg_vline_time_us;
- unsigned int scheduling_delay_otg_vlines;
- unsigned int vertical_interrupt_ack_delay_otg_vlines;
- unsigned int allow_to_target_delay_otg_vlines;
- unsigned int contention_delay_otg_vlines;
- unsigned int min_allow_width_otg_vlines;
- unsigned int nom_vtotal;
- unsigned int vblank_start;
+ int scheduling_delay_otg_vlines;
+ int vertical_interrupt_ack_delay_otg_vlines;
+ int allow_to_target_delay_otg_vlines;
+ int contention_delay_otg_vlines;
+ int min_allow_width_otg_vlines;
+ int nom_vtotal;
+ int vblank_start;
double nom_refresh_rate_hz;
double nom_frame_time_us;
- unsigned int max_vtotal;
+ int max_vtotal;
double min_refresh_rate_hz;
double max_frame_time_us;
- unsigned int dram_clk_change_blackout_otg_vlines;
+ int blackout_otg_vlines;
+ int max_allow_delay_otg_vlines;
+ double nom_vblank_time_us;
struct {
double max_vactive_det_fill_delay_us;
- unsigned int max_vactive_det_fill_delay_otg_vlines;
- struct dml2_fams2_per_method_common_meta common;
+ double vactive_latency_hiding_us;
+ double reserved_vblank_required_us;
+ int max_vactive_det_fill_delay_otg_vlines;
+ int reserved_blank_required_vlines;
+ struct dml2_pstate_per_method_common_meta common;
} method_vactive;
struct {
- struct dml2_fams2_per_method_common_meta common;
+ struct dml2_pstate_per_method_common_meta common;
} method_vblank;
struct {
- unsigned int programming_delay_otg_vlines;
- unsigned int df_throttle_delay_otg_vlines;
- unsigned int prefetch_to_mall_delay_otg_vlines;
+ int programming_delay_otg_vlines;
+ int df_throttle_delay_otg_vlines;
+ int prefetch_to_mall_delay_otg_vlines;
unsigned long phantom_vactive;
unsigned long phantom_vfp;
unsigned long phantom_vtotal;
- struct dml2_fams2_per_method_common_meta common;
+ struct dml2_pstate_per_method_common_meta common;
} method_subvp;
struct {
- unsigned int programming_delay_otg_vlines;
- unsigned int stretched_vtotal;
- struct dml2_fams2_per_method_common_meta common;
+ int programming_delay_otg_vlines;
+ int stretched_vtotal;
+ struct dml2_pstate_per_method_common_meta common;
} method_drr;
};
+/* mask of synchronized timings by stream index */
+struct dml2_pmo_synchronized_timing_groups {
+ unsigned int num_timing_groups;
+ unsigned int synchronized_timing_group_masks[DML2_MAX_PLANES];
+ bool group_is_drr_enabled[DML2_MAX_PLANES];
+ bool group_is_drr_active[DML2_MAX_PLANES];
+ double group_line_time_us[DML2_MAX_PLANES];
+};
+
struct dml2_optimization_stage3_state {
bool performed;
bool success;
@@ -319,7 +334,7 @@ struct dml2_optimization_stage3_state {
// Meta-data for FAMS2
bool fams2_required;
- struct dml2_fams2_meta stream_fams2_meta[DML2_MAX_PLANES];
+ struct dml2_pstate_meta stream_pstate_meta[DML2_MAX_PLANES];
int min_clk_index_for_latency;
};
@@ -472,6 +487,7 @@ struct dml2_core_scratch {
};
struct dml2_core_instance {
+ enum dml2_project_id project_id;
struct dml2_mcg_min_clock_table *minimum_clock_table;
struct dml2_core_internal_state_inputs inputs;
struct dml2_core_internal_state_intermediates intermediates;
@@ -619,6 +635,12 @@ struct dml2_pmo_optimize_for_stutter_in_out {
#define PMO_DCN4_MAX_NUM_VARIANTS 2
#define PMO_DCN4_MAX_BASE_STRATEGIES 10
+struct dml2_scheduling_check_locals {
+ struct dml2_pstate_per_method_common_meta group_common_pstate_meta[DML2_MAX_PLANES];
+ unsigned int sorted_group_gtl_disallow_index[DML2_MAX_PLANES];
+ unsigned int sorted_group_gtl_period_index[DML2_MAX_PLANES];
+};
+
struct dml2_pmo_scratch {
union {
struct {
@@ -648,7 +670,7 @@ struct dml2_pmo_scratch {
// Stores all the implicit SVP meta information indexed by stream index of the display
// configuration under inspection, built at optimization stage init
struct dml2_implicit_svp_meta stream_svp_meta[DML2_MAX_PLANES];
- struct dml2_fams2_meta stream_fams2_meta[DML2_MAX_PLANES];
+ struct dml2_pstate_meta stream_pstate_meta[DML2_MAX_PLANES];
unsigned int optimal_vblank_reserved_time_for_stutter_us[DML2_PMO_STUTTER_CANDIDATE_LIST_SIZE];
unsigned int num_stutter_candidates;
@@ -663,7 +685,7 @@ struct dml2_pmo_scratch {
double group_line_time_us[DML2_MAX_PLANES];
/* scheduling check locals */
- struct dml2_fams2_per_method_common_meta group_common_fams2_meta[DML2_MAX_PLANES];
+ struct dml2_pstate_per_method_common_meta group_common_pstate_meta[DML2_MAX_PLANES];
unsigned int sorted_group_gtl_disallow_index[DML2_MAX_PLANES];
unsigned int sorted_group_gtl_period_index[DML2_MAX_PLANES];
double group_phase_offset[DML2_MAX_PLANES];
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c
index 4cfe64aa8492..4cfe64aa8492 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.h
index 1538b708d8be..1538b708d8be 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_resource_mgmt.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_resource_mgmt.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_types.h
index 7ca7f2a743c2..7ca7f2a743c2 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_dc_types.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_internal_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_internal_types.h
index 140ec01545db..55b3e3ca54f7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_internal_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_internal_types.h
@@ -23,7 +23,7 @@
* Authors: AMD
*
*/
-
+
#ifndef __DML2_INTERNAL_TYPES_H__
#define __DML2_INTERNAL_TYPES_H__
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c
index c59f825cfae9..66040c877d68 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.c
@@ -24,6 +24,7 @@
*
*/
+
#include "dml2_dc_types.h"
#include "dml2_internal_types.h"
#include "dml2_utils.h"
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.h
index 9d64851f54e7..9d64851f54e7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_mall_phantom.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_mall_phantom.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.c
index ef693f608d59..ef693f608d59 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.h
index e83e05248592..e83e05248592 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_policy.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_policy.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
index 3b866e876bf4..d834cb595afa 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.c
@@ -301,6 +301,7 @@ void dml2_init_socbb_params(struct dml2_context *dml2, const struct dc *in_dc, s
out->pct_ideal_dram_bw_after_urgent_pixel_only = 65.0;
break;
+
case dml_project_dcn401:
out->pct_ideal_fabric_bw_after_urgent = 76; //67;
out->max_avg_sdp_bw_use_normal_percent = 75; //80;
@@ -424,6 +425,8 @@ void dml2_init_soc_states(struct dml2_context *dml2, const struct dc *in_dc,
p->in_states->state_array[1].dcfclk_mhz = 1434.0;
p->in_states->state_array[1].dram_speed_mts = 1000 * transactions_per_mem_clock;
break;
+
+
case dml_project_dcn401:
p->in_states->num_states = 2;
transactions_per_mem_clock = 16;
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.h
index d764773938f4..d764773938f4 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_translation_helper.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_translation_helper.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c
index 9a33158b63bf..9a33158b63bf 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_utils.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_utils.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.h
index 04fcfe637119..04fcfe637119 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_utils.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_utils.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c
index 9deb03a18ccc..9deb03a18ccc 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.h
index c384e141cebc..c384e141cebc 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml2_wrapper.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml2_wrapper.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml_assert.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_assert.h
index 17f0972b1af7..17f0972b1af7 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml_assert.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_assert.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml_depedencies.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_depedencies.h
index f7d30b47beff..d459f93cf40b 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml_depedencies.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_depedencies.h
@@ -31,3 +31,4 @@
*/
#include "os_types.h"
#include "cmntypes.h"
+
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml_display_rq_dlg_calc.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c
index 00d22e542469..00d22e542469 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml_display_rq_dlg_calc.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.c
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml_display_rq_dlg_calc.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.h
index bf491cf0582d..bf491cf0582d 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml_display_rq_dlg_calc.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_display_rq_dlg_calc.h
diff --git a/drivers/gpu/drm/amd/display/dc/dml2/dml_logging.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_logging.h
index 2a2f84e07ca8..7fadbe6d7af4 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2/dml_logging.h
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml_logging.h
@@ -23,6 +23,7 @@
* Authors: AMD
*
*/
+
#ifndef __DML_LOGGING_H__
#define __DML_LOGGING_H__
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c
index 01480a04f85e..ce91e5d28956 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.c
@@ -199,6 +199,8 @@ void dpp_reset(struct dpp *dpp_base)
memset(&dpp->scl_data, 0, sizeof(dpp->scl_data));
memset(&dpp->pwl_data, 0, sizeof(dpp->pwl_data));
+
+ dpp_base->cursor_offload = false;
}
@@ -484,10 +486,12 @@ void dpp1_set_cursor_position(
cur_en = 0; /* not visible beyond top edge*/
if (dpp_base->pos.cur0_ctl.bits.cur0_enable != cur_en) {
- REG_UPDATE(CURSOR0_CONTROL, CUR0_ENABLE, cur_en);
-
- dpp_base->pos.cur0_ctl.bits.cur0_enable = cur_en;
+ if (!dpp_base->cursor_offload)
+ REG_UPDATE(CURSOR0_CONTROL, CUR0_ENABLE, cur_en);
}
+
+ dpp_base->pos.cur0_ctl.bits.cur0_enable = cur_en;
+ dpp_base->att.cur0_ctl.bits.cur0_enable = cur_en;
}
void dpp1_cnv_set_optional_cursor_attributes(
@@ -497,8 +501,13 @@ void dpp1_cnv_set_optional_cursor_attributes(
struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base);
if (attr) {
- REG_UPDATE(CURSOR0_FP_SCALE_BIAS, CUR0_FP_BIAS, attr->bias);
- REG_UPDATE(CURSOR0_FP_SCALE_BIAS, CUR0_FP_SCALE, attr->scale);
+ if (!dpp_base->cursor_offload) {
+ REG_UPDATE(CURSOR0_FP_SCALE_BIAS, CUR0_FP_BIAS, attr->bias);
+ REG_UPDATE(CURSOR0_FP_SCALE_BIAS, CUR0_FP_SCALE, attr->scale);
+ }
+
+ dpp_base->att.fp_scale_bias.bits.fp_bias = attr->bias;
+ dpp_base->att.fp_scale_bias.bits.fp_scale = attr->scale;
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h
index f466182963f7..b12f34345a58 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp.h
@@ -1348,7 +1348,8 @@ struct dcn_dpp_mask {
uint32_t CURSOR0_COLOR1; \
uint32_t DPP_CONTROL; \
uint32_t CM_HDR_MULT_COEF; \
- uint32_t CURSOR0_FP_SCALE_BIAS;
+ uint32_t CURSOR0_FP_SCALE_BIAS; \
+ uint32_t OBUF_CONTROL;
struct dcn_dpp_registers {
DPP_COMMON_REG_VARIABLE_LIST
@@ -1450,7 +1451,6 @@ void dpp1_set_degamma(
void dpp1_set_degamma_pwl(struct dpp *dpp_base,
const struct pwl_params *params);
-
void dpp_read_state(struct dpp *dpp_base,
struct dcn_dpp_state *s);
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c
index 09be2a90cc79..ef4a16117181 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.c
@@ -84,6 +84,22 @@ void dpp30_read_state(struct dpp *dpp_base, struct dcn_dpp_state *s)
}
}
+void dpp30_read_reg_state(struct dpp *dpp_base, struct dcn_dpp_reg_state *dpp_reg_state)
+{
+ struct dcn3_dpp *dpp = TO_DCN30_DPP(dpp_base);
+
+ dpp_reg_state->recout_start = REG_READ(RECOUT_START);
+ dpp_reg_state->recout_size = REG_READ(RECOUT_SIZE);
+ dpp_reg_state->scl_horz_filter_scale_ratio = REG_READ(SCL_HORZ_FILTER_SCALE_RATIO);
+ dpp_reg_state->scl_vert_filter_scale_ratio = REG_READ(SCL_VERT_FILTER_SCALE_RATIO);
+ dpp_reg_state->scl_mode = REG_READ(SCL_MODE);
+ dpp_reg_state->cm_control = REG_READ(CM_CONTROL);
+ dpp_reg_state->dpp_control = REG_READ(DPP_CONTROL);
+ dpp_reg_state->dscl_control = REG_READ(DSCL_CONTROL);
+ dpp_reg_state->obuf_control = REG_READ(OBUF_CONTROL);
+ dpp_reg_state->mpc_size = REG_READ(MPC_SIZE);
+}
+
/*program post scaler scs block in dpp CM*/
void dpp3_program_post_csc(
struct dpp *dpp_base,
@@ -396,17 +412,21 @@ void dpp3_set_cursor_attributes(
}
}
- REG_UPDATE_3(CURSOR0_CONTROL,
- CUR0_MODE, color_format,
- CUR0_EXPANSION_MODE, 0,
- CUR0_ROM_EN, cur_rom_en);
+ if (!dpp_base->cursor_offload)
+ REG_UPDATE_3(CURSOR0_CONTROL,
+ CUR0_MODE, color_format,
+ CUR0_EXPANSION_MODE, 0,
+ CUR0_ROM_EN, cur_rom_en);
if (color_format == CURSOR_MODE_MONO) {
/* todo: clarify what to program these to */
- REG_UPDATE(CURSOR0_COLOR0,
- CUR0_COLOR0, 0x00000000);
- REG_UPDATE(CURSOR0_COLOR1,
- CUR0_COLOR1, 0xFFFFFFFF);
+
+ if (!dpp_base->cursor_offload) {
+ REG_UPDATE(CURSOR0_COLOR0,
+ CUR0_COLOR0, 0x00000000);
+ REG_UPDATE(CURSOR0_COLOR1,
+ CUR0_COLOR1, 0xFFFFFFFF);
+ }
}
dpp_base->att.cur0_ctl.bits.expansion_mode = 0;
@@ -578,9 +598,6 @@ static void dpp3_power_on_blnd_lut(
dpp_base->ctx->dc->optimized_required = true;
dpp_base->deferred_reg_writes.bits.disable_blnd_lut = true;
}
- } else {
- REG_SET(CM_MEM_PWR_CTRL, 0,
- BLNDGAM_MEM_PWR_FORCE, power_on == true ? 0 : 1);
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.h b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.h
index f236824126e9..d4a70b4379ea 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.h
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn30/dcn30_dpp.h
@@ -594,6 +594,8 @@ void dpp3_program_CM_dealpha(
void dpp30_read_state(struct dpp *dpp_base,
struct dcn_dpp_state *s);
+void dpp30_read_reg_state(struct dpp *dpp_base, struct dcn_dpp_reg_state *dpp_reg_state);
+
bool dpp3_get_optimal_number_of_taps(
struct dpp *dpp,
struct scaler_data *scl_data,
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn32/dcn32_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn32/dcn32_dpp.c
index fa67e54bf94e..8a5aa5e86850 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn32/dcn32_dpp.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn32/dcn32_dpp.c
@@ -134,6 +134,7 @@ static struct dpp_funcs dcn32_dpp_funcs = {
.dpp_dppclk_control = dpp1_dppclk_control,
.dpp_set_hdr_multiplier = dpp3_set_hdr_multiplier,
.dpp_get_gamut_remap = dpp3_cm_get_gamut_remap,
+ .dpp_read_reg_state = dpp30_read_reg_state,
};
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn35/dcn35_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn35/dcn35_dpp.c
index f7a373a3d70a..977d83bf7741 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn35/dcn35_dpp.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn35/dcn35_dpp.c
@@ -95,6 +95,7 @@ void dpp35_program_bias_and_scale_fcnv(
static struct dpp_funcs dcn35_dpp_funcs = {
.dpp_program_gamcor_lut = dpp3_program_gamcor_lut,
.dpp_read_state = dpp30_read_state,
+ .dpp_read_reg_state = dpp30_read_reg_state,
.dpp_reset = dpp_reset,
.dpp_set_scaler = dpp1_dscl_set_scaler_manual_scale,
.dpp_get_optimal_number_of_taps = dpp3_get_optimal_number_of_taps,
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.c
index 36187f890d5d..96c2c853de42 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp.c
@@ -248,6 +248,7 @@ static struct dpp_funcs dcn401_dpp_funcs = {
.set_optional_cursor_attributes = dpp401_set_optional_cursor_attributes,
.dpp_dppclk_control = dpp1_dppclk_control,
.dpp_set_hdr_multiplier = dpp3_set_hdr_multiplier,
+ .dpp_read_reg_state = dpp30_read_reg_state,
.set_cursor_matrix = dpp401_set_cursor_matrix,
};
diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c
index 7aab77b58869..62bf7cea21d8 100644
--- a/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c
+++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn401/dcn401_dpp_cm.c
@@ -103,17 +103,21 @@ void dpp401_set_cursor_attributes(
}
}
- REG_UPDATE_3(CURSOR0_CONTROL,
- CUR0_MODE, color_format,
- CUR0_EXPANSION_MODE, 0,
- CUR0_ROM_EN, cur_rom_en);
+ if (!dpp_base->cursor_offload)
+ REG_UPDATE_3(CURSOR0_CONTROL,
+ CUR0_MODE, color_format,
+ CUR0_EXPANSION_MODE, 0,
+ CUR0_ROM_EN, cur_rom_en);
if (color_format == CURSOR_MODE_MONO) {
/* todo: clarify what to program these to */
- REG_UPDATE(CURSOR0_COLOR0,
- CUR0_COLOR0, 0x00000000);
- REG_UPDATE(CURSOR0_COLOR1,
- CUR0_COLOR1, 0xFFFFFFFF);
+
+ if (!dpp_base->cursor_offload) {
+ REG_UPDATE(CURSOR0_COLOR0,
+ CUR0_COLOR0, 0x00000000);
+ REG_UPDATE(CURSOR0_COLOR1,
+ CUR0_COLOR1, 0xFFFFFFFF);
+ }
}
dpp_base->att.cur0_ctl.bits.expansion_mode = 0;
@@ -132,10 +136,12 @@ void dpp401_set_cursor_position(
uint32_t cur_en = pos->enable ? 1 : 0;
if (dpp_base->pos.cur0_ctl.bits.cur0_enable != cur_en) {
- REG_UPDATE(CURSOR0_CONTROL, CUR0_ENABLE, cur_en);
-
- dpp_base->pos.cur0_ctl.bits.cur0_enable = cur_en;
+ if (!dpp_base->cursor_offload)
+ REG_UPDATE(CURSOR0_CONTROL, CUR0_ENABLE, cur_en);
}
+
+ dpp_base->pos.cur0_ctl.bits.cur0_enable = cur_en;
+ dpp_base->att.cur0_ctl.bits.cur0_enable = cur_en;
}
void dpp401_set_optional_cursor_attributes(
@@ -145,10 +151,17 @@ void dpp401_set_optional_cursor_attributes(
struct dcn401_dpp *dpp = TO_DCN401_DPP(dpp_base);
if (attr) {
- REG_UPDATE(CURSOR0_FP_SCALE_BIAS_G_Y, CUR0_FP_BIAS_G_Y, attr->bias);
- REG_UPDATE(CURSOR0_FP_SCALE_BIAS_G_Y, CUR0_FP_SCALE_G_Y, attr->scale);
- REG_UPDATE(CURSOR0_FP_SCALE_BIAS_RB_CRCB, CUR0_FP_BIAS_RB_CRCB, attr->bias);
- REG_UPDATE(CURSOR0_FP_SCALE_BIAS_RB_CRCB, CUR0_FP_SCALE_RB_CRCB, attr->scale);
+ if (!dpp_base->cursor_offload) {
+ REG_UPDATE(CURSOR0_FP_SCALE_BIAS_G_Y, CUR0_FP_BIAS_G_Y, attr->bias);
+ REG_UPDATE(CURSOR0_FP_SCALE_BIAS_G_Y, CUR0_FP_SCALE_G_Y, attr->scale);
+ REG_UPDATE(CURSOR0_FP_SCALE_BIAS_RB_CRCB, CUR0_FP_BIAS_RB_CRCB, attr->bias);
+ REG_UPDATE(CURSOR0_FP_SCALE_BIAS_RB_CRCB, CUR0_FP_SCALE_RB_CRCB, attr->scale);
+ }
+
+ dpp_base->att.fp_scale_bias_g_y.bits.fp_bias_g_y = attr->bias;
+ dpp_base->att.fp_scale_bias_g_y.bits.fp_scale_g_y = attr->scale;
+ dpp_base->att.fp_scale_bias_rb_crcb.bits.fp_bias_rb_crcb = attr->bias;
+ dpp_base->att.fp_scale_bias_rb_crcb.bits.fp_scale_rb_crcb = attr->scale;
}
}
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c
index 89f0d999bf35..242f1e6f0d8f 100644
--- a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.c
@@ -35,6 +35,7 @@ static void dsc_write_to_registers(struct display_stream_compressor *dsc, const
static const struct dsc_funcs dcn20_dsc_funcs = {
.dsc_get_enc_caps = dsc2_get_enc_caps,
.dsc_read_state = dsc2_read_state,
+ .dsc_read_reg_state = dsc2_read_reg_state,
.dsc_validate_stream = dsc2_validate_stream,
.dsc_set_config = dsc2_set_config,
.dsc_get_packed_pps = dsc2_get_packed_pps,
@@ -155,6 +156,13 @@ void dsc2_read_state(struct display_stream_compressor *dsc, struct dcn_dsc_state
DSCRM_DSC_OPP_PIPE_SOURCE, &s->dsc_opp_source);
}
+void dsc2_read_reg_state(struct display_stream_compressor *dsc, struct dcn_dsc_reg_state *dccg_reg_state)
+{
+ struct dcn20_dsc *dsc20 = TO_DCN20_DSC(dsc);
+
+ dccg_reg_state->dsc_top_control = REG_READ(DSC_TOP_CONTROL);
+ dccg_reg_state->dscc_interrupt_control_status = REG_READ(DSCC_INTERRUPT_CONTROL_STATUS);
+}
bool dsc2_validate_stream(struct display_stream_compressor *dsc, const struct dsc_config *dsc_cfg)
{
@@ -407,7 +415,7 @@ bool dsc_prepare_config(const struct dsc_config *dsc_cfg, struct dsc_reg_values
dsc_reg_vals->ich_reset_at_eol = (dsc_cfg->is_odm || dsc_reg_vals->num_slices_h > 1) ? 0xF : 0;
// Need to find the ceiling value for the slice width
- dsc_reg_vals->pps.slice_width = (dsc_cfg->pic_width + dsc_cfg->dc_dsc_cfg.num_slices_h - 1) / dsc_cfg->dc_dsc_cfg.num_slices_h;
+ dsc_reg_vals->pps.slice_width = (dsc_cfg->pic_width + dsc_cfg->dsc_padding + dsc_cfg->dc_dsc_cfg.num_slices_h - 1) / dsc_cfg->dc_dsc_cfg.num_slices_h;
// TODO: in addition to validating slice height (pic height must be divisible by slice height),
// see what happens when the same condition doesn't apply for slice_width/pic_width.
dsc_reg_vals->pps.slice_height = dsc_cfg->pic_height / dsc_cfg->dc_dsc_cfg.num_slices_v;
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.h b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.h
index a9c04fc95bd1..2337c3a97235 100644
--- a/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.h
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn20/dcn20_dsc.h
@@ -606,6 +606,7 @@ bool dsc2_get_packed_pps(struct display_stream_compressor *dsc,
uint8_t *dsc_packed_pps);
void dsc2_read_state(struct display_stream_compressor *dsc, struct dcn_dsc_state *s);
+void dsc2_read_reg_state(struct display_stream_compressor *dsc, struct dcn_dsc_reg_state *dccg_reg_state);
bool dsc2_validate_stream(struct display_stream_compressor *dsc, const struct dsc_config *dsc_cfg);
void dsc2_set_config(struct display_stream_compressor *dsc, const struct dsc_config *dsc_cfg,
struct dsc_optc_config *dsc_optc_cfg);
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c
index 6f4f5a3c4861..e712985f7abd 100644
--- a/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn35/dcn35_dsc.c
@@ -28,10 +28,11 @@
#include "reg_helper.h"
static void dsc35_enable(struct display_stream_compressor *dsc, int opp_pipe);
+static void dsc35_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsigned int max_dscclk_khz);
static const struct dsc_funcs dcn35_dsc_funcs = {
- .dsc_get_enc_caps = dsc2_get_enc_caps,
.dsc_read_state = dsc2_read_state,
+ .dsc_read_reg_state = dsc2_read_reg_state,
.dsc_validate_stream = dsc2_validate_stream,
.dsc_set_config = dsc2_set_config,
.dsc_get_packed_pps = dsc2_get_packed_pps,
@@ -39,6 +40,7 @@ static const struct dsc_funcs dcn35_dsc_funcs = {
.dsc_disable = dsc2_disable,
.dsc_disconnect = dsc2_disconnect,
.dsc_wait_disconnect_pending_clear = dsc2_wait_disconnect_pending_clear,
+ .dsc_get_single_enc_caps = dsc35_get_single_enc_caps,
};
/* Macro definitios for REG_SET macros*/
@@ -110,3 +112,31 @@ void dsc35_set_fgcg(struct dcn20_dsc *dsc20, bool enable)
{
REG_UPDATE(DSC_TOP_CONTROL, DSC_FGCG_REP_DIS, !enable);
}
+
+void dsc35_get_single_enc_caps(struct dsc_enc_caps *dsc_enc_caps, unsigned int max_dscclk_khz)
+{
+ dsc_enc_caps->dsc_version = 0x21; /* v1.2 - DP spec defined it in reverse order and we kept it */
+
+ dsc_enc_caps->slice_caps.bits.NUM_SLICES_1 = 1;
+ dsc_enc_caps->slice_caps.bits.NUM_SLICES_2 = 1;
+ dsc_enc_caps->slice_caps.bits.NUM_SLICES_3 = 1;
+ dsc_enc_caps->slice_caps.bits.NUM_SLICES_4 = 1;
+
+ dsc_enc_caps->lb_bit_depth = 13;
+ dsc_enc_caps->is_block_pred_supported = true;
+
+ dsc_enc_caps->color_formats.bits.RGB = 1;
+ dsc_enc_caps->color_formats.bits.YCBCR_444 = 1;
+ dsc_enc_caps->color_formats.bits.YCBCR_SIMPLE_422 = 1;
+ dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_422 = 0;
+ dsc_enc_caps->color_formats.bits.YCBCR_NATIVE_420 = 1;
+
+ dsc_enc_caps->color_depth.bits.COLOR_DEPTH_8_BPC = 1;
+ dsc_enc_caps->color_depth.bits.COLOR_DEPTH_10_BPC = 1;
+ dsc_enc_caps->color_depth.bits.COLOR_DEPTH_12_BPC = 1;
+
+ dsc_enc_caps->max_total_throughput_mps = max_dscclk_khz * 3 / 1000;
+
+ dsc_enc_caps->max_slice_width = 5184; /* (including 64 overlap pixels for eDP MSO mode) */
+ dsc_enc_caps->bpp_increment_div = 16; /* 1/16th of a bit */
+}
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c
index 7bd92ae8b13e..c1bdbb38c690 100644
--- a/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dcn401/dcn401_dsc.c
@@ -26,6 +26,7 @@ static const struct dsc_funcs dcn401_dsc_funcs = {
.dsc_disconnect = dsc401_disconnect,
.dsc_wait_disconnect_pending_clear = dsc401_wait_disconnect_pending_clear,
.dsc_get_single_enc_caps = dsc401_get_single_enc_caps,
+ .dsc_read_reg_state = dsc2_read_reg_state
};
/* Macro definitios for REG_SET macros*/
diff --git a/drivers/gpu/drm/amd/display/dc/dsc/dsc.h b/drivers/gpu/drm/amd/display/dc/dsc/dsc.h
index b0bd1f9425b5..81c83d5fe042 100644
--- a/drivers/gpu/drm/amd/display/dc/dsc/dsc.h
+++ b/drivers/gpu/drm/amd/display/dc/dsc/dsc.h
@@ -41,6 +41,7 @@ struct dsc_config {
enum dc_color_depth color_depth; /* Bits per component */
bool is_odm;
struct dc_dsc_config dc_dsc_cfg;
+ uint32_t dsc_padding;
};
@@ -65,6 +66,10 @@ struct dcn_dsc_state {
uint32_t dsc_opp_source;
};
+struct dcn_dsc_reg_state {
+ uint32_t dsc_top_control;
+ uint32_t dscc_interrupt_control_status;
+};
/* DSC encoder capabilities
* They differ from the DPCD DSC caps because they are based on AMD DSC encoder caps.
@@ -99,6 +104,7 @@ struct dsc_enc_caps {
struct dsc_funcs {
void (*dsc_get_enc_caps)(struct dsc_enc_caps *dsc_enc_caps, int pixel_clock_100Hz);
void (*dsc_read_state)(struct display_stream_compressor *dsc, struct dcn_dsc_state *s);
+ void (*dsc_read_reg_state)(struct display_stream_compressor *dsc, struct dcn_dsc_reg_state *dccg_reg_state);
bool (*dsc_validate_stream)(struct display_stream_compressor *dsc, const struct dsc_config *dsc_cfg);
void (*dsc_set_config)(struct display_stream_compressor *dsc, const struct dsc_config *dsc_cfg,
struct dsc_optc_config *dsc_optc_cfg);
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c
index e7e5f6d4778e..181a93dc46e6 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.c
@@ -440,33 +440,15 @@ void hubbub3_init_watermarks(struct hubbub *hubbub)
REG_WRITE(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, reg);
}
-void hubbub3_get_det_sizes(struct hubbub *hubbub, uint32_t *curr_det_sizes, uint32_t *target_det_sizes)
+void hubbub3_read_reg_state(struct hubbub *hubbub, struct dcn_hubbub_reg_state *hubbub_reg_state)
{
struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
- REG_GET_2(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, &curr_det_sizes[0],
- DET0_SIZE, &target_det_sizes[0]);
-
- REG_GET_2(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, &curr_det_sizes[1],
- DET1_SIZE, &target_det_sizes[1]);
-
- REG_GET_2(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, &curr_det_sizes[2],
- DET2_SIZE, &target_det_sizes[2]);
-
- REG_GET_2(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, &curr_det_sizes[3],
- DET3_SIZE, &target_det_sizes[3]);
-
-}
-
-uint32_t hubbub3_compbuf_config_error(struct hubbub *hubbub)
-{
- struct dcn20_hubbub *hubbub1 = TO_DCN20_HUBBUB(hubbub);
- uint32_t compbuf_config_error = 0;
-
- REG_GET(DCHUBBUB_COMPBUF_CTRL, CONFIG_ERROR,
- &compbuf_config_error);
-
- return compbuf_config_error;
+ hubbub_reg_state->det0_ctrl = REG_READ(DCHUBBUB_DET0_CTRL);
+ hubbub_reg_state->det1_ctrl = REG_READ(DCHUBBUB_DET1_CTRL);
+ hubbub_reg_state->det2_ctrl = REG_READ(DCHUBBUB_DET2_CTRL);
+ hubbub_reg_state->det3_ctrl = REG_READ(DCHUBBUB_DET3_CTRL);
+ hubbub_reg_state->compbuf_ctrl = REG_READ(DCHUBBUB_COMPBUF_CTRL);
}
static const struct hubbub_funcs hubbub30_funcs = {
@@ -486,8 +468,7 @@ static const struct hubbub_funcs hubbub30_funcs = {
.force_pstate_change_control = hubbub3_force_pstate_change_control,
.init_watermarks = hubbub3_init_watermarks,
.hubbub_read_state = hubbub2_read_state,
- .get_det_sizes = hubbub3_get_det_sizes,
- .compbuf_config_error = hubbub3_compbuf_config_error,
+ .hubbub_read_reg_state = hubbub3_read_reg_state
};
void hubbub3_construct(struct dcn20_hubbub *hubbub3,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h
index 49a469969d36..9e14de3ccaee 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn30/dcn30_hubbub.h
@@ -133,10 +133,6 @@ void hubbub3_force_pstate_change_control(struct hubbub *hubbub,
void hubbub3_init_watermarks(struct hubbub *hubbub);
-void hubbub3_get_det_sizes(struct hubbub *hubbub,
- uint32_t *curr_det_sizes,
- uint32_t *target_det_sizes);
-
-uint32_t hubbub3_compbuf_config_error(struct hubbub *hubbub);
+void hubbub3_read_reg_state(struct hubbub *hubbub, struct dcn_hubbub_reg_state *hubbub_reg_state);
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c
index cdb20251a154..5a03758e3de6 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c
@@ -933,8 +933,8 @@ int hubbub31_init_dchub_sys_ctx(struct hubbub *hubbub,
dcn20_vmid_setup(&hubbub2->vmid[15], &phys_config);
}
-
- dcn21_dchvm_init(hubbub);
+ if (hubbub->funcs->dchvm_init)
+ hubbub->funcs->dchvm_init(hubbub);
return NUM_VMID;
}
@@ -1071,8 +1071,8 @@ static const struct hubbub_funcs hubbub31_funcs = {
.program_compbuf_size = dcn31_program_compbuf_size,
.init_crb = dcn31_init_crb,
.hubbub_read_state = hubbub2_read_state,
- .get_det_sizes = hubbub3_get_det_sizes,
- .compbuf_config_error = hubbub3_compbuf_config_error,
+ .hubbub_read_reg_state = hubbub3_read_reg_state,
+ .dchvm_init = dcn21_dchvm_init
};
void hubbub31_construct(struct dcn20_hubbub *hubbub31,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c
index 4d4ca6d77bbd..237331b35378 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn32/dcn32_hubbub.c
@@ -1037,8 +1037,7 @@ static const struct hubbub_funcs hubbub32_funcs = {
.force_usr_retraining_allow = hubbub32_force_usr_retraining_allow,
.set_request_limit = hubbub32_set_request_limit,
.get_mall_en = hubbub32_get_mall_en,
- .get_det_sizes = hubbub3_get_det_sizes,
- .compbuf_config_error = hubbub3_compbuf_config_error,
+ .hubbub_read_reg_state = hubbub3_read_reg_state
};
void hubbub32_construct(struct dcn20_hubbub *hubbub2,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c
index a443722a8632..43ba399f4822 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c
@@ -549,6 +549,55 @@ void hubbub35_init(struct hubbub *hubbub)
memset(&hubbub2->watermarks.a.cstate_pstate, 0, sizeof(hubbub2->watermarks.a.cstate_pstate));
}
+void dcn35_dchvm_init(struct hubbub *hubbub)
+{
+ struct dcn20_hubbub *hubbub2 = TO_DCN20_HUBBUB(hubbub);
+ uint32_t riommu_active;
+ int i;
+
+ //Init DCHVM block
+ REG_UPDATE(DCHVM_CTRL0, HOSTVM_INIT_REQ, 1);
+
+ //Poll until RIOMMU_ACTIVE = 1
+ for (i = 0; i < 100; i++) {
+ REG_GET(DCHVM_RIOMMU_STAT0, RIOMMU_ACTIVE, &riommu_active);
+
+ if (riommu_active)
+ break;
+ else
+ udelay(5);
+ }
+
+ if (riommu_active) {
+ // Disable gating and memory power requests
+ REG_UPDATE(DCHVM_MEM_CTRL, HVM_GPUVMRET_PWR_REQ_DIS, 1);
+ REG_UPDATE_4(DCHVM_CLK_CTRL,
+ HVM_DISPCLK_R_GATE_DIS, 1,
+ HVM_DISPCLK_G_GATE_DIS, 1,
+ HVM_DCFCLK_R_GATE_DIS, 1,
+ HVM_DCFCLK_G_GATE_DIS, 1);
+
+ //Reflect the power status of DCHUBBUB
+ REG_UPDATE(DCHVM_RIOMMU_CTRL0, HOSTVM_POWERSTATUS, 1);
+
+ //Start rIOMMU prefetching
+ REG_UPDATE(DCHVM_RIOMMU_CTRL0, HOSTVM_PREFETCH_REQ, 1);
+
+ //Poll until HOSTVM_PREFETCH_DONE = 1
+ REG_WAIT(DCHVM_RIOMMU_STAT0, HOSTVM_PREFETCH_DONE, 1, 5, 100);
+
+ //Enable memory power requests
+ REG_UPDATE(DCHVM_MEM_CTRL, HVM_GPUVMRET_PWR_REQ_DIS, 0);
+ // Enable dynamic clock gating
+ REG_UPDATE_4(DCHVM_CLK_CTRL,
+ HVM_DISPCLK_R_GATE_DIS, 0,
+ HVM_DISPCLK_G_GATE_DIS, 0,
+ HVM_DCFCLK_R_GATE_DIS, 0,
+ HVM_DCFCLK_G_GATE_DIS, 0);
+ hubbub->riommu_active = true;
+ }
+}
+
/*static void hubbub35_set_request_limit(struct hubbub *hubbub,
int memory_channel_count,
int words_per_channel)
@@ -589,8 +638,8 @@ static const struct hubbub_funcs hubbub35_funcs = {
.hubbub_read_state = hubbub2_read_state,
.force_usr_retraining_allow = hubbub32_force_usr_retraining_allow,
.dchubbub_init = hubbub35_init,
- .get_det_sizes = hubbub3_get_det_sizes,
- .compbuf_config_error = hubbub3_compbuf_config_error,
+ .hubbub_read_reg_state = hubbub3_read_reg_state,
+ .dchvm_init = dcn35_dchvm_init
};
void hubbub35_construct(struct dcn20_hubbub *hubbub2,
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h
index 23fecf88556c..9f65fff1bd4d 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.h
@@ -168,4 +168,5 @@ void dcn35_program_compbuf_size(struct hubbub *hubbub,
unsigned int compbuf_size_kb, bool safe_to_increase);
void dcn35_init_crb(struct hubbub *hubbub);
void hubbub35_init(struct hubbub *hubbub);
+void dcn35_dchvm_init(struct hubbub *hubbub);
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c
index a36273a52880..d11afd1ce72a 100644
--- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c
+++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn401/dcn401_hubbub.c
@@ -1247,8 +1247,7 @@ static const struct hubbub_funcs hubbub4_01_funcs = {
.program_compbuf_segments = dcn401_program_compbuf_segments,
.wait_for_det_update = dcn401_wait_for_det_update,
.program_arbiter = dcn401_program_arbiter,
- .get_det_sizes = hubbub3_get_det_sizes,
- .compbuf_config_error = hubbub3_compbuf_config_error,
+ .hubbub_read_reg_state = hubbub3_read_reg_state
};
void hubbub401_construct(struct dcn20_hubbub *hubbub2,
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c
index 9b026600b90e..6378e3fd7249 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.c
@@ -550,6 +550,7 @@ void hubp_reset(struct hubp *hubp)
{
memset(&hubp->pos, 0, sizeof(hubp->pos));
memset(&hubp->att, 0, sizeof(hubp->att));
+ hubp->cursor_offload = false;
}
void hubp1_program_surface_config(
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h
index cf2eb9793008..f2571076fc50 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn10/dcn10_hubp.h
@@ -105,7 +105,9 @@
SRI(DCN_CUR0_TTU_CNTL0, HUBPREQ, id),\
SRI(DCN_CUR0_TTU_CNTL1, HUBPREQ, id),\
SRI(HUBP_CLK_CNTL, HUBP, id),\
- SRI(HUBPRET_READ_LINE_VALUE, HUBPRET, id)
+ SRI(HUBPRET_READ_LINE_VALUE, HUBPRET, id),\
+ SRI(HUBP_MEASURE_WIN_CTRL_DCFCLK, HUBP, id),\
+ SRI(HUBP_MEASURE_WIN_CTRL_DPPCLK, HUBP, id)
/* Register address initialization macro for ASICs with VM */
#define HUBP_REG_LIST_DCN_VM(id)\
@@ -251,7 +253,19 @@
uint32_t CURSOR_HOT_SPOT; \
uint32_t CURSOR_DST_OFFSET; \
uint32_t HUBP_CLK_CNTL; \
- uint32_t HUBPRET_READ_LINE_VALUE
+ uint32_t HUBPRET_READ_LINE_VALUE; \
+ uint32_t HUBP_MEASURE_WIN_CTRL_DCFCLK; \
+ uint32_t HUBP_MEASURE_WIN_CTRL_DPPCLK; \
+ uint32_t HUBPRET_INTERRUPT; \
+ uint32_t HUBPRET_MEM_PWR_CTRL; \
+ uint32_t HUBPRET_MEM_PWR_STATUS; \
+ uint32_t HUBPRET_READ_LINE_CTRL0; \
+ uint32_t HUBPRET_READ_LINE_CTRL1; \
+ uint32_t HUBPRET_READ_LINE0; \
+ uint32_t HUBPRET_READ_LINE1; \
+ uint32_t HUBPREQ_MEM_PWR_CTRL; \
+ uint32_t HUBPREQ_MEM_PWR_STATUS
+
#define HUBP_SF(reg_name, field_name, post_fix)\
.field_name = reg_name ## __ ## field_name ## post_fix
@@ -688,6 +702,123 @@ struct dcn_fl_regs_st {
uint32_t lut_fl_mode;
uint32_t lut_fl_format;
};
+struct dcn_hubp_reg_state {
+ uint32_t hubp_cntl;
+ uint32_t mall_config;
+ uint32_t mall_sub_vp;
+ uint32_t hubp_req_size_config;
+ uint32_t hubp_req_size_config_c;
+ uint32_t vmpg_config;
+ uint32_t addr_config;
+ uint32_t pri_viewport_dimension;
+ uint32_t pri_viewport_dimension_c;
+ uint32_t pri_viewport_start;
+ uint32_t pri_viewport_start_c;
+ uint32_t sec_viewport_dimension;
+ uint32_t sec_viewport_dimension_c;
+ uint32_t sec_viewport_start;
+ uint32_t sec_viewport_start_c;
+ uint32_t surface_config;
+ uint32_t tiling_config;
+ uint32_t clk_cntl;
+ uint32_t mall_status;
+ uint32_t measure_win_ctrl_dcfclk;
+ uint32_t measure_win_ctrl_dppclk;
+
+ uint32_t blank_offset_0;
+ uint32_t blank_offset_1;
+ uint32_t cursor_settings;
+ uint32_t dcn_cur0_ttu_cntl0;
+ uint32_t dcn_cur0_ttu_cntl1;
+ uint32_t dcn_cur1_ttu_cntl0;
+ uint32_t dcn_cur1_ttu_cntl1;
+ uint32_t dcn_dmdat_vm_cntl;
+ uint32_t dcn_expansion_mode;
+ uint32_t dcn_global_ttu_cntl;
+ uint32_t dcn_surf0_ttu_cntl0;
+ uint32_t dcn_surf0_ttu_cntl1;
+ uint32_t dcn_surf1_ttu_cntl0;
+ uint32_t dcn_surf1_ttu_cntl1;
+ uint32_t dcn_ttu_qos_wm;
+ uint32_t dcn_vm_mx_l1_tlb_cntl;
+ uint32_t dcn_vm_system_aperture_high_addr;
+ uint32_t dcn_vm_system_aperture_low_addr;
+ uint32_t dcsurf_flip_control;
+ uint32_t dcsurf_flip_control2;
+ uint32_t dcsurf_primary_meta_surface_address;
+ uint32_t dcsurf_primary_meta_surface_address_c;
+ uint32_t dcsurf_primary_meta_surface_address_high;
+ uint32_t dcsurf_primary_meta_surface_address_high_c;
+ uint32_t dcsurf_primary_surface_address;
+ uint32_t dcsurf_primary_surface_address_c;
+ uint32_t dcsurf_primary_surface_address_high;
+ uint32_t dcsurf_primary_surface_address_high_c;
+ uint32_t dcsurf_secondary_meta_surface_address;
+ uint32_t dcsurf_secondary_meta_surface_address_c;
+ uint32_t dcsurf_secondary_meta_surface_address_high;
+ uint32_t dcsurf_secondary_meta_surface_address_high_c;
+ uint32_t dcsurf_secondary_surface_address;
+ uint32_t dcsurf_secondary_surface_address_c;
+ uint32_t dcsurf_secondary_surface_address_high;
+ uint32_t dcsurf_secondary_surface_address_high_c;
+ uint32_t dcsurf_surface_control;
+ uint32_t dcsurf_surface_earliest_inuse;
+ uint32_t dcsurf_surface_earliest_inuse_c;
+ uint32_t dcsurf_surface_earliest_inuse_high;
+ uint32_t dcsurf_surface_earliest_inuse_high_c;
+ uint32_t dcsurf_surface_flip_interrupt;
+ uint32_t dcsurf_surface_inuse;
+ uint32_t dcsurf_surface_inuse_c;
+ uint32_t dcsurf_surface_inuse_high;
+ uint32_t dcsurf_surface_inuse_high_c;
+ uint32_t dcsurf_surface_pitch;
+ uint32_t dcsurf_surface_pitch_c;
+ uint32_t dst_after_scaler;
+ uint32_t dst_dimensions;
+ uint32_t dst_y_delta_drq_limit;
+ uint32_t flip_parameters_0;
+ uint32_t flip_parameters_1;
+ uint32_t flip_parameters_2;
+ uint32_t flip_parameters_3;
+ uint32_t flip_parameters_4;
+ uint32_t flip_parameters_5;
+ uint32_t flip_parameters_6;
+ uint32_t hubpreq_mem_pwr_ctrl;
+ uint32_t hubpreq_mem_pwr_status;
+ uint32_t nom_parameters_0;
+ uint32_t nom_parameters_1;
+ uint32_t nom_parameters_2;
+ uint32_t nom_parameters_3;
+ uint32_t nom_parameters_4;
+ uint32_t nom_parameters_5;
+ uint32_t nom_parameters_6;
+ uint32_t nom_parameters_7;
+ uint32_t per_line_delivery;
+ uint32_t per_line_delivery_pre;
+ uint32_t prefetch_settings;
+ uint32_t prefetch_settings_c;
+ uint32_t ref_freq_to_pix_freq;
+ uint32_t uclk_pstate_force;
+ uint32_t vblank_parameters_0;
+ uint32_t vblank_parameters_1;
+ uint32_t vblank_parameters_2;
+ uint32_t vblank_parameters_3;
+ uint32_t vblank_parameters_4;
+ uint32_t vblank_parameters_5;
+ uint32_t vblank_parameters_6;
+ uint32_t vmid_settings_0;
+
+ uint32_t hubpret_control;
+ uint32_t hubpret_interrupt;
+ uint32_t hubpret_mem_pwr_ctrl;
+ uint32_t hubpret_mem_pwr_status;
+ uint32_t hubpret_read_line_ctrl0;
+ uint32_t hubpret_read_line_ctrl1;
+ uint32_t hubpret_read_line_status;
+ uint32_t hubpret_read_line_value;
+ uint32_t hubpret_read_line0;
+ uint32_t hubpret_read_line1;
+};
struct dcn_hubp_state {
struct _vcs_dpi_display_dlg_regs_st dlg_attr;
@@ -718,7 +849,6 @@ struct dcn_hubp_state {
uint32_t hubp_cntl;
uint32_t flip_control;
};
-
struct dcn10_hubp {
struct hubp base;
struct dcn_hubp_state state;
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c
index 91259b896e03..92288de4cc10 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.c
@@ -613,26 +613,28 @@ void hubp2_cursor_set_attributes(
hubp->curs_attr = *attr;
- REG_UPDATE(CURSOR_SURFACE_ADDRESS_HIGH,
- CURSOR_SURFACE_ADDRESS_HIGH, attr->address.high_part);
- REG_UPDATE(CURSOR_SURFACE_ADDRESS,
- CURSOR_SURFACE_ADDRESS, attr->address.low_part);
-
- REG_UPDATE_2(CURSOR_SIZE,
- CURSOR_WIDTH, attr->width,
- CURSOR_HEIGHT, attr->height);
-
- REG_UPDATE_4(CURSOR_CONTROL,
- CURSOR_MODE, attr->color_format,
- CURSOR_2X_MAGNIFY, attr->attribute_flags.bits.ENABLE_MAGNIFICATION,
- CURSOR_PITCH, hw_pitch,
- CURSOR_LINES_PER_CHUNK, lpc);
-
- REG_SET_2(CURSOR_SETTINGS, 0,
- /* no shift of the cursor HDL schedule */
- CURSOR0_DST_Y_OFFSET, 0,
- /* used to shift the cursor chunk request deadline */
- CURSOR0_CHUNK_HDL_ADJUST, 3);
+ if (!hubp->cursor_offload) {
+ REG_UPDATE(CURSOR_SURFACE_ADDRESS_HIGH,
+ CURSOR_SURFACE_ADDRESS_HIGH, attr->address.high_part);
+ REG_UPDATE(CURSOR_SURFACE_ADDRESS,
+ CURSOR_SURFACE_ADDRESS, attr->address.low_part);
+
+ REG_UPDATE_2(CURSOR_SIZE,
+ CURSOR_WIDTH, attr->width,
+ CURSOR_HEIGHT, attr->height);
+
+ REG_UPDATE_4(CURSOR_CONTROL,
+ CURSOR_MODE, attr->color_format,
+ CURSOR_2X_MAGNIFY, attr->attribute_flags.bits.ENABLE_MAGNIFICATION,
+ CURSOR_PITCH, hw_pitch,
+ CURSOR_LINES_PER_CHUNK, lpc);
+
+ REG_SET_2(CURSOR_SETTINGS, 0,
+ /* no shift of the cursor HDL schedule */
+ CURSOR0_DST_Y_OFFSET, 0,
+ /* used to shift the cursor chunk request deadline */
+ CURSOR0_CHUNK_HDL_ADJUST, 3);
+ }
hubp->att.SURFACE_ADDR_HIGH = attr->address.high_part;
hubp->att.SURFACE_ADDR = attr->address.low_part;
@@ -1059,23 +1061,28 @@ void hubp2_cursor_set_position(
cur_en = 0; /* not visible beyond top edge*/
if (hubp->pos.cur_ctl.bits.cur_enable != cur_en) {
- if (cur_en && REG_READ(CURSOR_SURFACE_ADDRESS) == 0)
+ bool cursor_not_programmed = hubp->att.SURFACE_ADDR == 0 && hubp->att.SURFACE_ADDR_HIGH == 0;
+
+ if (cur_en && cursor_not_programmed)
hubp->funcs->set_cursor_attributes(hubp, &hubp->curs_attr);
- REG_UPDATE(CURSOR_CONTROL,
- CURSOR_ENABLE, cur_en);
+ if (!hubp->cursor_offload)
+ REG_UPDATE(CURSOR_CONTROL, CURSOR_ENABLE, cur_en);
}
- REG_SET_2(CURSOR_POSITION, 0,
- CURSOR_X_POSITION, pos->x,
- CURSOR_Y_POSITION, pos->y);
+ if (!hubp->cursor_offload) {
+ REG_SET_2(CURSOR_POSITION, 0,
+ CURSOR_X_POSITION, pos->x,
+ CURSOR_Y_POSITION, pos->y);
- REG_SET_2(CURSOR_HOT_SPOT, 0,
- CURSOR_HOT_SPOT_X, pos->x_hotspot,
- CURSOR_HOT_SPOT_Y, pos->y_hotspot);
+ REG_SET_2(CURSOR_HOT_SPOT, 0,
+ CURSOR_HOT_SPOT_X, pos->x_hotspot,
+ CURSOR_HOT_SPOT_Y, pos->y_hotspot);
+
+ REG_SET(CURSOR_DST_OFFSET, 0,
+ CURSOR_DST_X_OFFSET, dst_x_offset);
+ }
- REG_SET(CURSOR_DST_OFFSET, 0,
- CURSOR_DST_X_OFFSET, dst_x_offset);
/* TODO Handle surface pixel formats other than 4:4:4 */
/* Cursor Position Register Config */
hubp->pos.cur_ctl.bits.cur_enable = cur_en;
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h
index f325db555102..7062e6653062 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn20/dcn20_hubp.h
@@ -145,7 +145,8 @@
uint32_t FLIP_PARAMETERS_2;\
uint32_t DCN_CUR1_TTU_CNTL0;\
uint32_t DCN_CUR1_TTU_CNTL1;\
- uint32_t VMID_SETTINGS_0
+ uint32_t VMID_SETTINGS_0;\
+ uint32_t DST_Y_DELTA_DRQ_LIMIT
/*shared with dcn3.x*/
#define DCN21_HUBP_REG_COMMON_VARIABLE_LIST \
@@ -176,7 +177,10 @@
uint32_t HUBP_3DLUT_CONTROL;\
uint32_t HUBP_3DLUT_DLG_PARAM;\
uint32_t DCSURF_VIEWPORT_MCACHE_SPLIT_COORDINATE;\
- uint32_t DCHUBP_MCACHEID_CONFIG
+ uint32_t DCHUBP_MCACHEID_CONFIG;\
+ uint32_t DCHUBP_MALL_SUB_VP;\
+ uint32_t DCHUBP_ADDR_CONFIG;\
+ uint32_t HUBP_MALL_STATUS
#define DCN2_HUBP_REG_FIELD_VARIABLE_LIST(type) \
DCN_HUBP_REG_FIELD_BASE_LIST(type); \
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c
index e2740482e1cf..08ea0a1b9e7f 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn21/dcn21_hubp.c
@@ -73,8 +73,6 @@
* On any mode switch, if the new reg values are smaller than the current values,
* then update the regs with the new values.
*
- * Link to the ticket: http://ontrack-internal.amd.com/browse/DEDCN21-142
- *
*/
void apply_DEDCN21_142_wa_for_hostvm_deadline(
struct hubp *hubp,
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c
index 556214b2227d..0cc6f4558989 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.c
@@ -476,6 +476,126 @@ void hubp3_read_state(struct hubp *hubp)
}
+void hubp3_read_reg_state(struct hubp *hubp, struct dcn_hubp_reg_state *reg_state)
+{
+ struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp);
+
+ reg_state->hubp_cntl = REG_READ(DCHUBP_CNTL);
+ reg_state->mall_config = REG_READ(DCHUBP_MALL_CONFIG);
+ reg_state->mall_sub_vp = REG_READ(DCHUBP_MALL_SUB_VP);
+ reg_state->hubp_req_size_config = REG_READ(DCHUBP_REQ_SIZE_CONFIG);
+ reg_state->hubp_req_size_config_c = REG_READ(DCHUBP_REQ_SIZE_CONFIG_C);
+ reg_state->vmpg_config = REG_READ(DCHUBP_VMPG_CONFIG);
+ reg_state->addr_config = REG_READ(DCSURF_ADDR_CONFIG);
+ reg_state->pri_viewport_dimension = REG_READ(DCSURF_PRI_VIEWPORT_DIMENSION);
+ reg_state->pri_viewport_dimension_c = REG_READ(DCSURF_PRI_VIEWPORT_DIMENSION_C);
+ reg_state->pri_viewport_start = REG_READ(DCSURF_PRI_VIEWPORT_START);
+ reg_state->pri_viewport_start_c = REG_READ(DCSURF_PRI_VIEWPORT_START_C);
+ reg_state->sec_viewport_dimension = REG_READ(DCSURF_SEC_VIEWPORT_DIMENSION);
+ reg_state->sec_viewport_dimension_c = REG_READ(DCSURF_SEC_VIEWPORT_DIMENSION_C);
+ reg_state->sec_viewport_start = REG_READ(DCSURF_SEC_VIEWPORT_START);
+ reg_state->sec_viewport_start_c = REG_READ(DCSURF_SEC_VIEWPORT_START_C);
+ reg_state->surface_config = REG_READ(DCSURF_SURFACE_CONFIG);
+ reg_state->tiling_config = REG_READ(DCSURF_TILING_CONFIG);
+ reg_state->clk_cntl = REG_READ(HUBP_CLK_CNTL);
+ reg_state->mall_status = REG_READ(HUBP_MALL_STATUS);
+ reg_state->measure_win_ctrl_dcfclk = REG_READ(HUBP_MEASURE_WIN_CTRL_DCFCLK);
+ reg_state->measure_win_ctrl_dppclk = REG_READ(HUBP_MEASURE_WIN_CTRL_DPPCLK);
+
+ reg_state->blank_offset_0 = REG_READ(BLANK_OFFSET_0);
+ reg_state->blank_offset_1 = REG_READ(BLANK_OFFSET_1);
+ reg_state->cursor_settings = REG_READ(CURSOR_SETTINGS);
+ reg_state->dcn_cur0_ttu_cntl0 = REG_READ(DCN_CUR0_TTU_CNTL0);
+ reg_state->dcn_cur0_ttu_cntl1 = REG_READ(DCN_CUR0_TTU_CNTL1);
+ reg_state->dcn_cur1_ttu_cntl0 = REG_READ(DCN_CUR1_TTU_CNTL0);
+ reg_state->dcn_cur1_ttu_cntl1 = REG_READ(DCN_CUR1_TTU_CNTL1);
+ reg_state->dcn_dmdat_vm_cntl = REG_READ(DCN_DMDATA_VM_CNTL);
+ reg_state->dcn_expansion_mode = REG_READ(DCN_EXPANSION_MODE);
+ reg_state->dcn_global_ttu_cntl = REG_READ(DCN_GLOBAL_TTU_CNTL);
+ reg_state->dcn_surf0_ttu_cntl0 = REG_READ(DCN_SURF0_TTU_CNTL0);
+ reg_state->dcn_surf0_ttu_cntl1 = REG_READ(DCN_SURF0_TTU_CNTL1);
+ reg_state->dcn_surf1_ttu_cntl0 = REG_READ(DCN_SURF1_TTU_CNTL0);
+ reg_state->dcn_surf1_ttu_cntl1 = REG_READ(DCN_SURF1_TTU_CNTL1);
+ reg_state->dcn_ttu_qos_wm = REG_READ(DCN_TTU_QOS_WM);
+ reg_state->dcn_vm_mx_l1_tlb_cntl = REG_READ(DCN_VM_MX_L1_TLB_CNTL);
+ reg_state->dcn_vm_system_aperture_high_addr = REG_READ(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR);
+ reg_state->dcn_vm_system_aperture_low_addr = REG_READ(DCN_VM_SYSTEM_APERTURE_LOW_ADDR);
+ reg_state->dcsurf_flip_control = REG_READ(DCSURF_FLIP_CONTROL);
+ reg_state->dcsurf_flip_control2 = REG_READ(DCSURF_FLIP_CONTROL2);
+ reg_state->dcsurf_primary_meta_surface_address = REG_READ(DCSURF_PRIMARY_META_SURFACE_ADDRESS);
+ reg_state->dcsurf_primary_meta_surface_address_c = REG_READ(DCSURF_PRIMARY_META_SURFACE_ADDRESS_C);
+ reg_state->dcsurf_primary_meta_surface_address_high = REG_READ(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH);
+ reg_state->dcsurf_primary_meta_surface_address_high_c = REG_READ(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C);
+ reg_state->dcsurf_primary_surface_address = REG_READ(DCSURF_PRIMARY_SURFACE_ADDRESS);
+ reg_state->dcsurf_primary_surface_address_c = REG_READ(DCSURF_PRIMARY_SURFACE_ADDRESS_C);
+ reg_state->dcsurf_primary_surface_address_high = REG_READ(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH);
+ reg_state->dcsurf_primary_surface_address_high_c = REG_READ(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C);
+ reg_state->dcsurf_secondary_meta_surface_address = REG_READ(DCSURF_SECONDARY_META_SURFACE_ADDRESS);
+ reg_state->dcsurf_secondary_meta_surface_address_c = REG_READ(DCSURF_SECONDARY_META_SURFACE_ADDRESS_C);
+ reg_state->dcsurf_secondary_meta_surface_address_high = REG_READ(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH);
+ reg_state->dcsurf_secondary_meta_surface_address_high_c = REG_READ(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH_C);
+ reg_state->dcsurf_secondary_surface_address = REG_READ(DCSURF_SECONDARY_SURFACE_ADDRESS);
+ reg_state->dcsurf_secondary_surface_address_c = REG_READ(DCSURF_SECONDARY_SURFACE_ADDRESS_C);
+ reg_state->dcsurf_secondary_surface_address_high = REG_READ(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH);
+ reg_state->dcsurf_secondary_surface_address_high_c = REG_READ(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C);
+ reg_state->dcsurf_surface_control = REG_READ(DCSURF_SURFACE_CONTROL);
+ reg_state->dcsurf_surface_earliest_inuse = REG_READ(DCSURF_SURFACE_EARLIEST_INUSE);
+ reg_state->dcsurf_surface_earliest_inuse_c = REG_READ(DCSURF_SURFACE_EARLIEST_INUSE_C);
+ reg_state->dcsurf_surface_earliest_inuse_high = REG_READ(DCSURF_SURFACE_EARLIEST_INUSE_HIGH);
+ reg_state->dcsurf_surface_earliest_inuse_high_c = REG_READ(DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C);
+ reg_state->dcsurf_surface_flip_interrupt = REG_READ(DCSURF_SURFACE_FLIP_INTERRUPT);
+ reg_state->dcsurf_surface_inuse = REG_READ(DCSURF_SURFACE_INUSE);
+ reg_state->dcsurf_surface_inuse_c = REG_READ(DCSURF_SURFACE_INUSE_C);
+ reg_state->dcsurf_surface_inuse_high = REG_READ(DCSURF_SURFACE_INUSE_HIGH);
+ reg_state->dcsurf_surface_inuse_high_c = REG_READ(DCSURF_SURFACE_INUSE_HIGH_C);
+ reg_state->dcsurf_surface_pitch = REG_READ(DCSURF_SURFACE_PITCH);
+ reg_state->dcsurf_surface_pitch_c = REG_READ(DCSURF_SURFACE_PITCH_C);
+ reg_state->dst_after_scaler = REG_READ(DST_AFTER_SCALER);
+ reg_state->dst_dimensions = REG_READ(DST_DIMENSIONS);
+ reg_state->dst_y_delta_drq_limit = REG_READ(DST_Y_DELTA_DRQ_LIMIT);
+ reg_state->flip_parameters_0 = REG_READ(FLIP_PARAMETERS_0);
+ reg_state->flip_parameters_1 = REG_READ(FLIP_PARAMETERS_1);
+ reg_state->flip_parameters_2 = REG_READ(FLIP_PARAMETERS_2);
+ reg_state->flip_parameters_3 = REG_READ(FLIP_PARAMETERS_3);
+ reg_state->flip_parameters_4 = REG_READ(FLIP_PARAMETERS_4);
+ reg_state->flip_parameters_5 = REG_READ(FLIP_PARAMETERS_5);
+ reg_state->flip_parameters_6 = REG_READ(FLIP_PARAMETERS_6);
+ reg_state->hubpreq_mem_pwr_ctrl = REG_READ(HUBPREQ_MEM_PWR_CTRL);
+ reg_state->hubpreq_mem_pwr_status = REG_READ(HUBPREQ_MEM_PWR_STATUS);
+ reg_state->nom_parameters_0 = REG_READ(NOM_PARAMETERS_0);
+ reg_state->nom_parameters_1 = REG_READ(NOM_PARAMETERS_1);
+ reg_state->nom_parameters_2 = REG_READ(NOM_PARAMETERS_2);
+ reg_state->nom_parameters_3 = REG_READ(NOM_PARAMETERS_3);
+ reg_state->nom_parameters_4 = REG_READ(NOM_PARAMETERS_4);
+ reg_state->nom_parameters_5 = REG_READ(NOM_PARAMETERS_5);
+ reg_state->nom_parameters_6 = REG_READ(NOM_PARAMETERS_6);
+ reg_state->nom_parameters_7 = REG_READ(NOM_PARAMETERS_7);
+ reg_state->per_line_delivery = REG_READ(PER_LINE_DELIVERY);
+ reg_state->per_line_delivery_pre = REG_READ(PER_LINE_DELIVERY_PRE);
+ reg_state->prefetch_settings = REG_READ(PREFETCH_SETTINGS);
+ reg_state->prefetch_settings_c = REG_READ(PREFETCH_SETTINGS_C);
+ reg_state->ref_freq_to_pix_freq = REG_READ(REF_FREQ_TO_PIX_FREQ);
+ reg_state->uclk_pstate_force = REG_READ(UCLK_PSTATE_FORCE);
+ reg_state->vblank_parameters_0 = REG_READ(VBLANK_PARAMETERS_0);
+ reg_state->vblank_parameters_1 = REG_READ(VBLANK_PARAMETERS_1);
+ reg_state->vblank_parameters_2 = REG_READ(VBLANK_PARAMETERS_2);
+ reg_state->vblank_parameters_3 = REG_READ(VBLANK_PARAMETERS_3);
+ reg_state->vblank_parameters_4 = REG_READ(VBLANK_PARAMETERS_4);
+ reg_state->vblank_parameters_5 = REG_READ(VBLANK_PARAMETERS_5);
+ reg_state->vblank_parameters_6 = REG_READ(VBLANK_PARAMETERS_6);
+ reg_state->vmid_settings_0 = REG_READ(VMID_SETTINGS_0);
+ reg_state->hubpret_control = REG_READ(HUBPRET_CONTROL);
+ reg_state->hubpret_interrupt = REG_READ(HUBPRET_INTERRUPT);
+ reg_state->hubpret_mem_pwr_ctrl = REG_READ(HUBPRET_MEM_PWR_CTRL);
+ reg_state->hubpret_mem_pwr_status = REG_READ(HUBPRET_MEM_PWR_STATUS);
+ reg_state->hubpret_read_line_ctrl0 = REG_READ(HUBPRET_READ_LINE_CTRL0);
+ reg_state->hubpret_read_line_ctrl1 = REG_READ(HUBPRET_READ_LINE_CTRL1);
+ reg_state->hubpret_read_line_status = REG_READ(HUBPRET_READ_LINE_STATUS);
+ reg_state->hubpret_read_line_value = REG_READ(HUBPRET_READ_LINE_VALUE);
+ reg_state->hubpret_read_line0 = REG_READ(HUBPRET_READ_LINE0);
+ reg_state->hubpret_read_line1 = REG_READ(HUBPRET_READ_LINE1);
+}
+
void hubp3_setup(
struct hubp *hubp,
struct _vcs_dpi_display_dlg_regs_st *dlg_attr,
@@ -505,30 +625,6 @@ void hubp3_init(struct hubp *hubp)
hubp_reset(hubp);
}
-uint32_t hubp3_get_current_read_line(struct hubp *hubp)
-{
- uint32_t read_line = 0;
- struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp);
-
- REG_GET(HUBPRET_READ_LINE_VALUE,
- PIPE_READ_LINE,
- &read_line);
-
- return read_line;
-}
-
-unsigned int hubp3_get_underflow_status(struct hubp *hubp)
-{
- uint32_t hubp_underflow = 0;
- struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp);
-
- REG_GET(DCHUBP_CNTL,
- HUBP_UNDERFLOW_STATUS,
- &hubp_underflow);
-
- return hubp_underflow;
-}
-
static struct hubp_funcs dcn30_hubp_funcs = {
.hubp_enable_tripleBuffer = hubp2_enable_triplebuffer,
.hubp_is_triplebuffer_enabled = hubp2_is_triplebuffer_enabled,
@@ -558,8 +654,7 @@ static struct hubp_funcs dcn30_hubp_funcs = {
.hubp_soft_reset = hubp1_soft_reset,
.hubp_set_flip_int = hubp1_set_flip_int,
.hubp_clear_tiling = hubp3_clear_tiling,
- .hubp_get_underflow_status = hubp3_get_underflow_status,
- .hubp_get_current_read_line = hubp3_get_current_read_line,
+ .hubp_read_reg_state = hubp3_read_reg_state
};
bool hubp3_construct(
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h
index 842f4eb72cc8..c767e9f4f9b3 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn30/dcn30_hubp.h
@@ -296,6 +296,8 @@ void hubp3_dmdata_set_attributes(
void hubp3_read_state(struct hubp *hubp);
+void hubp3_read_reg_state(struct hubp *hubp, struct dcn_hubp_reg_state *reg_state);
+
void hubp3_init(struct hubp *hubp);
void hubp3_clear_tiling(struct hubp *hubp);
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c
index 47101847c2b7..189045f85039 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn31/dcn31_hubp.c
@@ -110,9 +110,7 @@ static struct hubp_funcs dcn31_hubp_funcs = {
.hubp_in_blank = hubp1_in_blank,
.program_extended_blank = hubp31_program_extended_blank,
.hubp_clear_tiling = hubp3_clear_tiling,
- .hubp_get_underflow_status = hubp3_get_underflow_status,
- .hubp_get_current_read_line = hubp3_get_current_read_line,
- .hubp_get_det_config_error = hubp31_get_det_config_error,
+ .hubp_read_reg_state = hubp3_read_reg_state,
};
bool hubp31_construct(
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c
index a5f23bb2a76a..a781085b046b 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn32/dcn32_hubp.c
@@ -118,29 +118,7 @@ void hubp32_cursor_set_attributes(
uint32_t cursor_width = ((attr->width + 63) / 64) * 64;
uint32_t cursor_height = attr->height;
uint32_t cursor_size = cursor_width * cursor_height;
-
- hubp->curs_attr = *attr;
-
- REG_UPDATE(CURSOR_SURFACE_ADDRESS_HIGH,
- CURSOR_SURFACE_ADDRESS_HIGH, attr->address.high_part);
- REG_UPDATE(CURSOR_SURFACE_ADDRESS,
- CURSOR_SURFACE_ADDRESS, attr->address.low_part);
-
- REG_UPDATE_2(CURSOR_SIZE,
- CURSOR_WIDTH, attr->width,
- CURSOR_HEIGHT, attr->height);
-
- REG_UPDATE_4(CURSOR_CONTROL,
- CURSOR_MODE, attr->color_format,
- CURSOR_2X_MAGNIFY, attr->attribute_flags.bits.ENABLE_MAGNIFICATION,
- CURSOR_PITCH, hw_pitch,
- CURSOR_LINES_PER_CHUNK, lpc);
-
- REG_SET_2(CURSOR_SETTINGS, 0,
- /* no shift of the cursor HDL schedule */
- CURSOR0_DST_Y_OFFSET, 0,
- /* used to shift the cursor chunk request deadline */
- CURSOR0_CHUNK_HDL_ADJUST, 3);
+ bool use_mall_for_cursor;
switch (attr->color_format) {
case CURSOR_MODE_MONO:
@@ -158,11 +136,49 @@ void hubp32_cursor_set_attributes(
cursor_size *= 8;
break;
}
+ use_mall_for_cursor = cursor_size > 16384 ? 1 : 0;
+
+ hubp->curs_attr = *attr;
- if (cursor_size > 16384)
- REG_UPDATE(DCHUBP_MALL_CONFIG, USE_MALL_FOR_CURSOR, true);
- else
- REG_UPDATE(DCHUBP_MALL_CONFIG, USE_MALL_FOR_CURSOR, false);
+ if (!hubp->cursor_offload) {
+ REG_UPDATE(CURSOR_SURFACE_ADDRESS_HIGH,
+ CURSOR_SURFACE_ADDRESS_HIGH, attr->address.high_part);
+ REG_UPDATE(CURSOR_SURFACE_ADDRESS,
+ CURSOR_SURFACE_ADDRESS, attr->address.low_part);
+
+ REG_UPDATE_2(CURSOR_SIZE,
+ CURSOR_WIDTH, attr->width,
+ CURSOR_HEIGHT, attr->height);
+
+ REG_UPDATE_4(CURSOR_CONTROL,
+ CURSOR_MODE, attr->color_format,
+ CURSOR_2X_MAGNIFY, attr->attribute_flags.bits.ENABLE_MAGNIFICATION,
+ CURSOR_PITCH, hw_pitch,
+ CURSOR_LINES_PER_CHUNK, lpc);
+
+ REG_SET_2(CURSOR_SETTINGS, 0,
+ /* no shift of the cursor HDL schedule */
+ CURSOR0_DST_Y_OFFSET, 0,
+ /* used to shift the cursor chunk request deadline */
+ CURSOR0_CHUNK_HDL_ADJUST, 3);
+
+ REG_UPDATE(DCHUBP_MALL_CONFIG, USE_MALL_FOR_CURSOR, use_mall_for_cursor);
+ }
+ hubp->att.SURFACE_ADDR_HIGH = attr->address.high_part;
+ hubp->att.SURFACE_ADDR = attr->address.low_part;
+ hubp->att.size.bits.width = attr->width;
+ hubp->att.size.bits.height = attr->height;
+ hubp->att.cur_ctl.bits.mode = attr->color_format;
+
+ hubp->cur_rect.w = attr->width;
+ hubp->cur_rect.h = attr->height;
+
+ hubp->att.cur_ctl.bits.pitch = hw_pitch;
+ hubp->att.cur_ctl.bits.line_per_chunk = lpc;
+ hubp->att.cur_ctl.bits.cur_2x_magnify = attr->attribute_flags.bits.ENABLE_MAGNIFICATION;
+ hubp->att.settings.bits.dst_y_offset = 0;
+ hubp->att.settings.bits.chunk_hdl_adjust = 3;
+ hubp->use_mall_for_cursor = use_mall_for_cursor;
}
void hubp32_init(struct hubp *hubp)
{
@@ -206,9 +222,7 @@ static struct hubp_funcs dcn32_hubp_funcs = {
.hubp_update_mall_sel = hubp32_update_mall_sel,
.hubp_prepare_subvp_buffering = hubp32_prepare_subvp_buffering,
.hubp_clear_tiling = hubp3_clear_tiling,
- .hubp_get_underflow_status = hubp3_get_underflow_status,
- .hubp_get_current_read_line = hubp3_get_current_read_line,
- .hubp_get_det_config_error = hubp31_get_det_config_error,
+ .hubp_read_reg_state = hubp3_read_reg_state
};
bool hubp32_construct(
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c
index b140808f21af..79c583e258c7 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn35/dcn35_hubp.c
@@ -209,6 +209,7 @@ static struct hubp_funcs dcn35_hubp_funcs = {
.dmdata_load = hubp2_dmdata_load,
.dmdata_status_done = hubp2_dmdata_status_done,
.hubp_read_state = hubp3_read_state,
+ .hubp_read_reg_state = hubp3_read_reg_state,
.hubp_clear_underflow = hubp2_clear_underflow,
.hubp_set_flip_control_surface_gsl = hubp2_set_flip_control_surface_gsl,
.hubp_init = hubp35_init,
@@ -218,9 +219,6 @@ static struct hubp_funcs dcn35_hubp_funcs = {
.hubp_in_blank = hubp1_in_blank,
.program_extended_blank = hubp31_program_extended_blank_value,
.hubp_clear_tiling = hubp3_clear_tiling,
- .hubp_get_underflow_status = hubp3_get_underflow_status,
- .hubp_get_current_read_line = hubp3_get_current_read_line,
- .hubp_get_det_config_error = hubp31_get_det_config_error,
};
bool hubp35_construct(
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c
index 0fcbc6a35be6..f01eae50d02f 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c
@@ -783,21 +783,23 @@ void hubp401_cursor_set_position(
if (cur_en && REG_READ(CURSOR_SURFACE_ADDRESS) == 0)
hubp->funcs->set_cursor_attributes(hubp, &hubp->curs_attr);
- REG_UPDATE(CURSOR_CONTROL,
- CURSOR_ENABLE, cur_en);
+ if (!hubp->cursor_offload)
+ REG_UPDATE(CURSOR_CONTROL,
+ CURSOR_ENABLE, cur_en);
}
- REG_SET_2(CURSOR_POSITION, 0,
- CURSOR_X_POSITION, x_pos,
- CURSOR_Y_POSITION, y_pos);
+ if (!hubp->cursor_offload) {
+ REG_SET_2(CURSOR_POSITION, 0,
+ CURSOR_X_POSITION, x_pos,
+ CURSOR_Y_POSITION, y_pos);
- REG_SET_2(CURSOR_HOT_SPOT, 0,
- CURSOR_HOT_SPOT_X, pos->x_hotspot,
- CURSOR_HOT_SPOT_Y, pos->y_hotspot);
-
- REG_SET(CURSOR_DST_OFFSET, 0,
- CURSOR_DST_X_OFFSET, dst_x_offset);
+ REG_SET_2(CURSOR_HOT_SPOT, 0,
+ CURSOR_HOT_SPOT_X, pos->x_hotspot,
+ CURSOR_HOT_SPOT_Y, pos->y_hotspot);
+ REG_SET(CURSOR_DST_OFFSET, 0,
+ CURSOR_DST_X_OFFSET, dst_x_offset);
+ }
/* Cursor Position Register Config */
hubp->pos.cur_ctl.bits.cur_enable = cur_en;
hubp->pos.position.bits.x_pos = pos->x;
@@ -1071,9 +1073,7 @@ static struct hubp_funcs dcn401_hubp_funcs = {
.hubp_get_3dlut_fl_done = hubp401_get_3dlut_fl_done,
.hubp_clear_tiling = hubp401_clear_tiling,
.hubp_program_3dlut_fl_config = hubp401_program_3dlut_fl_config,
- .hubp_get_underflow_status = hubp3_get_underflow_status,
- .hubp_get_current_read_line = hubp3_get_current_read_line,
- .hubp_get_det_config_error = hubp31_get_det_config_error,
+ .hubp_read_reg_state = hubp3_read_reg_state
};
bool hubp401_construct(
diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h
index fdabbeec8ffa..4570b8016de5 100644
--- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h
+++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.h
@@ -31,7 +31,7 @@
#include "dcn30/dcn30_hubp.h"
#include "dcn31/dcn31_hubp.h"
#include "dcn32/dcn32_hubp.h"
-#include "dml2/dml21/inc/dml_top_dchub_registers.h"
+#include "dml2_0/dml21/inc/dml_top_dchub_registers.h"
#define HUBP_3DLUT_FL_REG_LIST_DCN401(inst)\
SRI_ARR_US(_3DLUT_FL_CONFIG, HUBP, inst),\
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
index 24184b4eb352..8fe399939220 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
@@ -659,6 +659,20 @@ void dce110_update_info_frame(struct pipe_ctx *pipe_ctx)
}
}
+static void
+dce110_dac_encoder_control(struct pipe_ctx *pipe_ctx, bool enable)
+{
+ struct dc_link *link = pipe_ctx->stream->link;
+ struct dc_bios *bios = link->ctx->dc_bios;
+ struct bp_encoder_control encoder_control = {0};
+
+ encoder_control.action = enable ? ENCODER_CONTROL_ENABLE : ENCODER_CONTROL_DISABLE;
+ encoder_control.engine_id = link->link_enc->analog_engine;
+ encoder_control.pixel_clock = pipe_ctx->stream->timing.pix_clk_100hz / 10;
+
+ bios->funcs->encoder_control(bios, &encoder_control);
+}
+
void dce110_enable_stream(struct pipe_ctx *pipe_ctx)
{
enum dc_lane_count lane_count =
@@ -671,7 +685,6 @@ void dce110_enable_stream(struct pipe_ctx *pipe_ctx)
uint32_t early_control = 0;
struct timing_generator *tg = pipe_ctx->stream_res.tg;
- link_hwss->setup_stream_attribute(pipe_ctx);
link_hwss->setup_stream_encoder(pipe_ctx);
dc->hwss.update_info_frame(pipe_ctx);
@@ -689,6 +702,9 @@ void dce110_enable_stream(struct pipe_ctx *pipe_ctx)
early_control = lane_count;
tg->funcs->set_early_control(tg, early_control);
+
+ if (dc_is_rgb_signal(pipe_ctx->stream->signal))
+ dce110_dac_encoder_control(pipe_ctx, true);
}
static enum bp_result link_transmitter_control(
@@ -1086,6 +1102,9 @@ void dce110_enable_audio_stream(struct pipe_ctx *pipe_ctx)
if (!pipe_ctx->stream)
return;
+ if (dc_is_rgb_signal(pipe_ctx->stream->signal))
+ return;
+
dc = pipe_ctx->stream->ctx->dc;
clk_mgr = dc->clk_mgr;
link_hwss = get_link_hwss(pipe_ctx->stream->link, &pipe_ctx->link_res);
@@ -1122,6 +1141,9 @@ void dce110_disable_audio_stream(struct pipe_ctx *pipe_ctx)
if (!pipe_ctx || !pipe_ctx->stream)
return;
+ if (dc_is_rgb_signal(pipe_ctx->stream->signal))
+ return;
+
dc = pipe_ctx->stream->ctx->dc;
clk_mgr = dc->clk_mgr;
link_hwss = get_link_hwss(pipe_ctx->stream->link, &pipe_ctx->link_res);
@@ -1196,6 +1218,9 @@ void dce110_disable_stream(struct pipe_ctx *pipe_ctx)
dccg->funcs->disable_symclk_se(dccg, stream_enc->stream_enc_inst,
link_enc->transmitter - TRANSMITTER_UNIPHY_A);
}
+
+ if (dc_is_rgb_signal(pipe_ctx->stream->signal))
+ dce110_dac_encoder_control(pipe_ctx, false);
}
void dce110_unblank_stream(struct pipe_ctx *pipe_ctx,
@@ -1581,6 +1606,51 @@ static enum dc_status dce110_enable_stream_timing(
return DC_OK;
}
+static void
+dce110_select_crtc_source(struct pipe_ctx *pipe_ctx)
+{
+ struct dc_link *link = pipe_ctx->stream->link;
+ struct dc_bios *bios = link->ctx->dc_bios;
+ struct bp_crtc_source_select crtc_source_select = {0};
+ enum engine_id engine_id = link->link_enc->preferred_engine;
+ uint8_t bit_depth;
+
+ if (dc_is_rgb_signal(pipe_ctx->stream->signal))
+ engine_id = link->link_enc->analog_engine;
+
+ switch (pipe_ctx->stream->timing.display_color_depth) {
+ case COLOR_DEPTH_UNDEFINED:
+ bit_depth = 0;
+ break;
+ case COLOR_DEPTH_666:
+ bit_depth = 6;
+ break;
+ default:
+ case COLOR_DEPTH_888:
+ bit_depth = 8;
+ break;
+ case COLOR_DEPTH_101010:
+ bit_depth = 10;
+ break;
+ case COLOR_DEPTH_121212:
+ bit_depth = 12;
+ break;
+ case COLOR_DEPTH_141414:
+ bit_depth = 14;
+ break;
+ case COLOR_DEPTH_161616:
+ bit_depth = 16;
+ break;
+ }
+
+ crtc_source_select.controller_id = CONTROLLER_ID_D0 + pipe_ctx->stream_res.tg->inst;
+ crtc_source_select.bit_depth = bit_depth;
+ crtc_source_select.engine_id = engine_id;
+ crtc_source_select.sink_signal = pipe_ctx->stream->signal;
+
+ bios->funcs->select_crtc_source(bios, &crtc_source_select);
+}
+
enum dc_status dce110_apply_single_controller_ctx_to_hw(
struct pipe_ctx *pipe_ctx,
struct dc_state *context,
@@ -1600,6 +1670,10 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw(
hws->funcs.disable_stream_gating(dc, pipe_ctx);
}
+ if (pipe_ctx->stream->signal == SIGNAL_TYPE_RGB) {
+ dce110_select_crtc_source(pipe_ctx);
+ }
+
if (pipe_ctx->stream_res.audio != NULL) {
struct audio_output audio_output = {0};
@@ -1679,7 +1753,8 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw(
pipe_ctx->stream_res.tg->funcs->set_static_screen_control(
pipe_ctx->stream_res.tg, event_triggers, 2);
- if (!dc_is_virtual_signal(pipe_ctx->stream->signal))
+ if (!dc_is_virtual_signal(pipe_ctx->stream->signal) &&
+ !dc_is_rgb_signal(pipe_ctx->stream->signal))
pipe_ctx->stream_res.stream_enc->funcs->dig_connect_to_otg(
pipe_ctx->stream_res.stream_enc,
pipe_ctx->stream_res.tg->inst);
@@ -1913,6 +1988,7 @@ void dce110_enable_accelerated_mode(struct dc *dc, struct dc_state *context)
bool can_apply_edp_fast_boot = false;
bool can_apply_seamless_boot = false;
bool keep_edp_vdd_on = false;
+ bool should_clean_dsc_block = true;
struct dc_bios *dcb = dc->ctx->dc_bios;
DC_LOGGER_INIT();
@@ -2005,9 +2081,15 @@ void dce110_enable_accelerated_mode(struct dc *dc, struct dc_state *context)
power_down_all_hw_blocks(dc);
/* DSC could be enabled on eDP during VBIOS post.
- * To clean up dsc blocks if eDP is in link but not active.
+ * To clean up dsc blocks if all eDP dpms_off is true.
*/
- if (edp_link_with_sink && (edp_stream_num == 0))
+ for (i = 0; i < edp_stream_num; i++) {
+ if (!edp_streams[i]->dpms_off) {
+ should_clean_dsc_block = false;
+ }
+ }
+
+ if (should_clean_dsc_block)
clean_up_dsc_blocks(dc);
disable_vga_and_power_gate_all_controllers(dc);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
index e9fe97f0c4ea..fa62e40a9858 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c
@@ -2245,7 +2245,7 @@ void dcn10_cursor_lock(struct dc *dc, struct pipe_ctx *pipe, bool lock)
if (lock)
delay_cursor_until_vupdate(dc, pipe);
- if (pipe->stream && should_use_dmub_lock(pipe->stream->link)) {
+ if (pipe->stream && should_use_dmub_inbox1_lock(dc, pipe->stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
@@ -3090,6 +3090,9 @@ static void dcn10_update_dchubp_dpp(
}
if (pipe_ctx->stream->cursor_attributes.address.quad_part != 0) {
+ if (dc->hwss.abort_cursor_offload_update)
+ dc->hwss.abort_cursor_offload_update(dc, pipe_ctx);
+
dc->hwss.set_cursor_attribute(pipe_ctx);
dc->hwss.set_cursor_position(pipe_ctx);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c
index 9477c9f9e196..c8ff8ae85a03 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c
@@ -614,6 +614,14 @@ void dcn20_dpp_pg_control(
* DOMAIN11_PGFSM_PWR_STATUS, pwr_status,
* 1, 1000);
*/
+
+ /* Force disable cursor on plane powerdown on DPP 5 using dpp_force_disable_cursor */
+ if (!power_on) {
+ struct dpp *dpp5 = hws->ctx->dc->res_pool->dpps[dpp_inst];
+ if (dpp5 && dpp5->funcs->dpp_force_disable_cursor)
+ dpp5->funcs->dpp_force_disable_cursor(dpp5);
+ }
+
break;
default:
BREAK_TO_DEBUGGER();
@@ -1449,7 +1457,7 @@ void dcn20_pipe_control_lock(
!flip_immediate)
dcn20_setup_gsl_group_as_lock(dc, pipe, false);
- if (pipe->stream && should_use_dmub_lock(pipe->stream->link)) {
+ if (pipe->stream && should_use_dmub_inbox1_lock(dc, pipe->stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
@@ -1793,6 +1801,9 @@ void dcn20_update_dchubp_dpp(
if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed ||
pipe_ctx->update_flags.bits.scaler || viewport_changed == true) &&
pipe_ctx->stream->cursor_attributes.address.quad_part != 0) {
+ if (dc->hwss.abort_cursor_offload_update)
+ dc->hwss.abort_cursor_offload_update(dc, pipe_ctx);
+
dc->hwss.set_cursor_attribute(pipe_ctx);
dc->hwss.set_cursor_position(pipe_ctx);
@@ -3052,8 +3063,6 @@ void dcn20_enable_stream(struct pipe_ctx *pipe_ctx)
link_enc->transmitter - TRANSMITTER_UNIPHY_A);
}
- link_hwss->setup_stream_attribute(pipe_ctx);
-
if (dc->res_pool->dccg->funcs->set_pixel_rate_div)
dc->res_pool->dccg->funcs->set_pixel_rate_div(
dc->res_pool->dccg,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
index e47ed5571dfd..81bcadf5e57e 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c
@@ -53,7 +53,8 @@
#include "link_service.h"
#include "dc_state_priv.h"
-
+#define TO_DCN_DCCG(dccg)\
+ container_of(dccg, struct dcn_dccg, base)
#define DC_LOGGER_INIT(logger)
@@ -1235,44 +1236,47 @@ void dcn30_get_underflow_debug_data(const struct dc *dc,
{
struct hubbub *hubbub = dc->res_pool->hubbub;
- if (tg) {
- uint32_t v_blank_start = 0, v_blank_end = 0;
-
- out_data->otg_inst = tg->inst;
-
- tg->funcs->get_scanoutpos(tg,
- &v_blank_start,
- &v_blank_end,
- &out_data->h_position,
- &out_data->v_position);
-
- out_data->otg_frame_count = tg->funcs->get_frame_count(tg);
-
- out_data->otg_underflow = tg->funcs->is_optc_underflow_occurred(tg);
+ if (hubbub) {
+ if (hubbub->funcs->hubbub_read_reg_state) {
+ hubbub->funcs->hubbub_read_reg_state(hubbub, out_data->hubbub_reg_state);
+ }
}
for (int i = 0; i < MAX_PIPES; i++) {
struct hubp *hubp = dc->res_pool->hubps[i];
-
- if (hubp) {
- if (hubp->funcs->hubp_get_underflow_status)
- out_data->hubps[i].hubp_underflow = hubp->funcs->hubp_get_underflow_status(hubp);
-
- if (hubp->funcs->hubp_in_blank)
- out_data->hubps[i].hubp_in_blank = hubp->funcs->hubp_in_blank(hubp);
-
- if (hubp->funcs->hubp_get_current_read_line)
- out_data->hubps[i].hubp_readline = hubp->funcs->hubp_get_current_read_line(hubp);
-
- if (hubp->funcs->hubp_get_det_config_error)
- out_data->hubps[i].det_config_error = hubp->funcs->hubp_get_det_config_error(hubp);
- }
+ struct dpp *dpp = dc->res_pool->dpps[i];
+ struct output_pixel_processor *opp = dc->res_pool->opps[i];
+ struct display_stream_compressor *dsc = dc->res_pool->dscs[i];
+ struct mpc *mpc = dc->res_pool->mpc;
+ struct timing_generator *optc = dc->res_pool->timing_generators[i];
+ struct dccg *dccg = dc->res_pool->dccg;
+
+ if (hubp)
+ if (hubp->funcs->hubp_read_reg_state)
+ hubp->funcs->hubp_read_reg_state(hubp, out_data->hubp_reg_state[i]);
+
+ if (dpp)
+ if (dpp->funcs->dpp_read_reg_state)
+ dpp->funcs->dpp_read_reg_state(dpp, out_data->dpp_reg_state[i]);
+
+ if (opp)
+ if (opp->funcs->opp_read_reg_state)
+ opp->funcs->opp_read_reg_state(opp, out_data->opp_reg_state[i]);
+
+ if (dsc)
+ if (dsc->funcs->dsc_read_reg_state)
+ dsc->funcs->dsc_read_reg_state(dsc, out_data->dsc_reg_state[i]);
+
+ if (mpc)
+ if (mpc->funcs->mpc_read_reg_state)
+ mpc->funcs->mpc_read_reg_state(mpc, i, out_data->mpc_reg_state[i]);
+
+ if (optc)
+ if (optc->funcs->optc_read_reg_state)
+ optc->funcs->optc_read_reg_state(optc, out_data->optc_reg_state[i]);
+
+ if (dccg)
+ if (dccg->funcs->dccg_read_reg_state)
+ dccg->funcs->dccg_read_reg_state(dccg, out_data->dccg_reg_state[i]);
}
-
- if (hubbub->funcs->get_det_sizes)
- hubbub->funcs->get_det_sizes(hubbub, out_data->curr_det_sizes, out_data->target_det_sizes);
-
- if (hubbub->funcs->compbuf_config_error)
- out_data->compbuf_config_error = hubbub->funcs->compbuf_config_error(hubbub);
-
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c
index b822f2dffff0..d1ecdb92b072 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn31/dcn31_hwseq.c
@@ -710,7 +710,8 @@ bool dcn31_set_backlight_level(struct pipe_ctx *pipe_ctx,
panel_cntl->inst,
panel_cntl->pwrseq_inst);
- dmub_abm_set_backlight(dc, backlight_level_params, panel_cntl->inst);
+ if (backlight_level_params->control_type != BACKLIGHT_CONTROL_AMD_AUX)
+ dmub_abm_set_backlight(dc, backlight_level_params, panel_cntl->inst);
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c
index f925f669f2a4..4ee6ed610de0 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn314/dcn314_hwseq.c
@@ -108,6 +108,7 @@ static void update_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0);
dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
+ dsc_cfg.dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding;
dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg);
dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
index f39292952702..bf19ba65d09a 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c
@@ -1061,6 +1061,7 @@ void dcn32_update_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0);
dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
+ dsc_cfg.dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding;
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, dsc->inst, dsc_cfg.dc_dsc_cfg.num_slices_h);
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
index 05011061822c..7aa0f452e8f7 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c
@@ -364,6 +364,7 @@ static void update_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0);
dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
+ dsc_cfg.dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding;
dsc->funcs->dsc_set_config(dsc, &dsc_cfg, &dsc_optc_cfg);
dsc->funcs->dsc_enable(dsc, pipe_ctx->stream_res.opp->inst);
@@ -816,8 +817,6 @@ void dcn35_enable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx,
struct dc_state *context)
{
struct dpp *dpp = pipe_ctx->plane_res.dpp;
- struct dccg *dccg = dc->res_pool->dccg;
-
/* enable DCFCLK current DCHUB */
pipe_ctx->plane_res.hubp->funcs->hubp_clk_cntl(pipe_ctx->plane_res.hubp, true);
@@ -825,7 +824,6 @@ void dcn35_enable_plane(struct dc *dc, struct pipe_ctx *pipe_ctx,
/* initialize HUBP on power up */
pipe_ctx->plane_res.hubp->funcs->hubp_init(pipe_ctx->plane_res.hubp);
/*make sure DPPCLK is on*/
- dccg->funcs->dccg_root_gate_disable_control(dccg, dpp->inst, true);
dpp->funcs->dpp_dppclk_control(dpp, false, true);
/* make sure OPP_PIPE_CLOCK_EN = 1 */
pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control(
@@ -859,7 +857,6 @@ void dcn35_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx)
{
struct hubp *hubp = pipe_ctx->plane_res.hubp;
struct dpp *dpp = pipe_ctx->plane_res.dpp;
- struct dccg *dccg = dc->res_pool->dccg;
dc->hwss.wait_for_mpcc_disconnect(dc, dc->res_pool, pipe_ctx);
@@ -878,7 +875,6 @@ void dcn35_plane_atomic_disable(struct dc *dc, struct pipe_ctx *pipe_ctx)
hubp->funcs->hubp_clk_cntl(hubp, false);
dpp->funcs->dpp_dppclk_control(dpp, false, false);
- dccg->funcs->dccg_root_gate_disable_control(dccg, dpp->inst, false);
hubp->power_gated = true;
@@ -1592,3 +1588,141 @@ void dcn35_hardware_release(struct dc *dc)
if (dc->hwss.hw_block_power_up)
dc->hwss.hw_block_power_up(dc, &pg_update_state);
}
+
+void dcn35_abort_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe)
+{
+ if (!dc_dmub_srv_is_cursor_offload_enabled(dc))
+ return;
+
+ /*
+ * Insert a blank update to modify the write index and set pipe_mask to 0.
+ *
+ * While the DMU is interlocked with driver full pipe programming via
+ * the DMU HW lock, if the cursor update begins to execute after a full
+ * pipe programming occurs there are two possible issues:
+ *
+ * 1. Outdated cursor information is programmed, replacing the current update
+ * 2. The cursor update in firmware holds the cursor lock, preventing
+ * the current update from being latched atomically in the same frame
+ * as the rest of the update.
+ *
+ * This blank update, treated as a no-op, will allow the firmware to skip
+ * the programming.
+ */
+
+ if (dc->hwss.begin_cursor_offload_update)
+ dc->hwss.begin_cursor_offload_update(dc, pipe);
+
+ if (dc->hwss.commit_cursor_offload_update)
+ dc->hwss.commit_cursor_offload_update(dc, pipe);
+}
+
+void dcn35_begin_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe)
+{
+ volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1;
+ const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe);
+ uint32_t stream_idx, write_idx, payload_idx;
+
+ if (!top_pipe)
+ return;
+
+ stream_idx = top_pipe->pipe_idx;
+ write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */
+ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads);
+
+ cs->offload_streams[stream_idx].payloads[payload_idx].write_idx_start = write_idx;
+
+ if (pipe->plane_res.hubp)
+ pipe->plane_res.hubp->cursor_offload = true;
+
+ if (pipe->plane_res.dpp)
+ pipe->plane_res.dpp->cursor_offload = true;
+}
+
+void dcn35_commit_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe)
+{
+ volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1;
+ volatile struct dmub_shared_state_cursor_offload_stream_v1 *shared_stream;
+ const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe);
+ uint32_t stream_idx, write_idx, payload_idx;
+
+ if (pipe->plane_res.hubp)
+ pipe->plane_res.hubp->cursor_offload = false;
+
+ if (pipe->plane_res.dpp)
+ pipe->plane_res.dpp->cursor_offload = false;
+
+ if (!top_pipe)
+ return;
+
+ stream_idx = top_pipe->pipe_idx;
+ write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */
+ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads);
+
+ shared_stream = &dc->ctx->dmub_srv->dmub->shared_state[DMUB_SHARED_STATE_FEATURE__CURSOR_OFFLOAD_V1]
+ .data.cursor_offload_v1.offload_streams[stream_idx];
+
+ shared_stream->last_write_idx = write_idx;
+
+ cs->offload_streams[stream_idx].write_idx = write_idx;
+ cs->offload_streams[stream_idx].payloads[payload_idx].write_idx_finish = write_idx;
+}
+
+void dcn35_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe)
+{
+ volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1;
+ const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe);
+ const struct hubp *hubp = pipe->plane_res.hubp;
+ const struct dpp *dpp = pipe->plane_res.dpp;
+ volatile struct dmub_cursor_offload_pipe_data_dcn30_v1 *p;
+ uint32_t stream_idx, write_idx, payload_idx;
+
+ if (!top_pipe || !hubp || !dpp)
+ return;
+
+ stream_idx = top_pipe->pipe_idx;
+ write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */
+ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads);
+
+ p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe->pipe_idx].dcn30;
+
+ p->CURSOR0_0_CURSOR_SURFACE_ADDRESS = hubp->att.SURFACE_ADDR;
+ p->CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH = hubp->att.SURFACE_ADDR_HIGH;
+ p->CURSOR0_0_CURSOR_SIZE__CURSOR_WIDTH = hubp->att.size.bits.width;
+ p->CURSOR0_0_CURSOR_SIZE__CURSOR_HEIGHT = hubp->att.size.bits.height;
+ p->CURSOR0_0_CURSOR_POSITION__CURSOR_X_POSITION = hubp->pos.position.bits.x_pos;
+ p->CURSOR0_0_CURSOR_POSITION__CURSOR_Y_POSITION = hubp->pos.position.bits.y_pos;
+ p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_X = hubp->pos.hot_spot.bits.x_hot;
+ p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_Y = hubp->pos.hot_spot.bits.y_hot;
+ p->CURSOR0_0_CURSOR_DST_OFFSET__CURSOR_DST_X_OFFSET = hubp->pos.dst_offset.bits.dst_x_offset;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_ENABLE = hubp->pos.cur_ctl.bits.cur_enable;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_MODE = hubp->att.cur_ctl.bits.mode;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY = hubp->pos.cur_ctl.bits.cur_2x_magnify;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_PITCH = hubp->att.cur_ctl.bits.pitch;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_LINES_PER_CHUNK = hubp->att.cur_ctl.bits.line_per_chunk;
+
+ p->CNVC_CUR0_CURSOR0_CONTROL__CUR0_ENABLE = dpp->att.cur0_ctl.bits.cur0_enable;
+ p->CNVC_CUR0_CURSOR0_CONTROL__CUR0_MODE = dpp->att.cur0_ctl.bits.mode;
+ p->CNVC_CUR0_CURSOR0_CONTROL__CUR0_EXPANSION_MODE = dpp->att.cur0_ctl.bits.expansion_mode;
+ p->CNVC_CUR0_CURSOR0_CONTROL__CUR0_ROM_EN = dpp->att.cur0_ctl.bits.cur0_rom_en;
+ p->CNVC_CUR0_CURSOR0_COLOR0__CUR0_COLOR0 = 0x000000;
+ p->CNVC_CUR0_CURSOR0_COLOR1__CUR0_COLOR1 = 0xFFFFFF;
+ p->CNVC_CUR0_CURSOR0_FP_SCALE_BIAS__CUR0_FP_BIAS = dpp->att.fp_scale_bias.bits.fp_bias;
+ p->CNVC_CUR0_CURSOR0_FP_SCALE_BIAS__CUR0_FP_SCALE = dpp->att.fp_scale_bias.bits.fp_scale;
+
+ p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_DST_Y_OFFSET = hubp->att.settings.bits.dst_y_offset;
+ p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST = hubp->att.settings.bits.chunk_hdl_adjust;
+
+ cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe->pipe_idx);
+}
+
+void dcn35_notify_cursor_offload_drr_update(struct dc *dc, struct dc_state *context,
+ const struct dc_stream_state *stream)
+{
+ dc_dmub_srv_control_cursor_offload(dc, context, stream, true);
+}
+
+void dcn35_program_cursor_offload_now(struct dc *dc, const struct pipe_ctx *pipe)
+{
+ dc_dmub_srv_program_cursor_now(dc, pipe);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h
index 0b1d6f608edd..1ff41dba556c 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.h
@@ -101,4 +101,12 @@ bool dcn35_is_dp_dig_pixel_rate_div_policy(struct pipe_ctx *pipe_ctx);
void dcn35_hardware_release(struct dc *dc);
+void dcn35_abort_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe);
+void dcn35_begin_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe);
+void dcn35_commit_cursor_offload_update(struct dc *dc, const struct pipe_ctx *pipe);
+void dcn35_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe);
+void dcn35_notify_cursor_offload_drr_update(struct dc *dc, struct dc_state *context,
+ const struct dc_stream_state *stream);
+void dcn35_program_cursor_offload_now(struct dc *dc, const struct pipe_ctx *pipe);
+
#endif /* __DC_HWSS_DCN35_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
index f2f16a0bdb4f..5a66c9db2670 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
@@ -86,6 +86,12 @@ static const struct hw_sequencer_funcs dcn35_funcs = {
.set_cursor_position = dcn10_set_cursor_position,
.set_cursor_attribute = dcn10_set_cursor_attribute,
.set_cursor_sdr_white_level = dcn10_set_cursor_sdr_white_level,
+ .abort_cursor_offload_update = dcn35_abort_cursor_offload_update,
+ .begin_cursor_offload_update = dcn35_begin_cursor_offload_update,
+ .commit_cursor_offload_update = dcn35_commit_cursor_offload_update,
+ .update_cursor_offload_pipe = dcn35_update_cursor_offload_pipe,
+ .notify_cursor_offload_drr_update = dcn35_notify_cursor_offload_drr_update,
+ .program_cursor_offload_now = dcn35_program_cursor_offload_now,
.setup_periodic_interrupt = dcn10_setup_periodic_interrupt,
.set_clock = dcn10_set_clock,
.get_clock = dcn10_get_clock,
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
index 7c276c319086..2fbc22afb89c 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c
@@ -26,9 +26,11 @@
#include "clk_mgr.h"
#include "dsc.h"
#include "link_service.h"
+#include "custom_float.h"
#include "dce/dmub_hw_lock_mgr.h"
#include "dcn10/dcn10_cm_common.h"
+#include "dcn10/dcn10_hubbub.h"
#include "dcn20/dcn20_optc.h"
#include "dcn30/dcn30_cm_common.h"
#include "dcn32/dcn32_hwseq.h"
@@ -36,6 +38,7 @@
#include "dcn401/dcn401_resource.h"
#include "dc_state_priv.h"
#include "link_enc_cfg.h"
+#include "../hw_sequencer.h"
#define DC_LOGGER_INIT(logger)
@@ -200,6 +203,9 @@ void dcn401_init_hw(struct dc *dc)
*/
struct dc_link *link = dc->links[i];
+ if (link->ep_type != DISPLAY_ENDPOINT_PHY)
+ continue;
+
link->link_enc->funcs->hw_init(link->link_enc);
/* Check for enabled DIG to identify enabled display */
@@ -968,8 +974,6 @@ void dcn401_enable_stream(struct pipe_ctx *pipe_ctx)
}
}
- link_hwss->setup_stream_attribute(pipe_ctx);
-
if (dc->res_pool->dccg->funcs->set_pixel_rate_div) {
dc->res_pool->dccg->funcs->set_pixel_rate_div(
dc->res_pool->dccg,
@@ -1404,9 +1408,9 @@ void dcn401_prepare_bandwidth(struct dc *dc,
}
if (dc->debug.fams2_config.bits.enable) {
- dcn401_fams2_global_control_lock(dc, context, true);
+ dcn401_dmub_hw_control_lock(dc, context, true);
dcn401_fams2_update_config(dc, context, false);
- dcn401_fams2_global_control_lock(dc, context, false);
+ dcn401_dmub_hw_control_lock(dc, context, false);
}
if (p_state_change_support != context->bw_ctx.bw.dcn.clk.p_state_change_support) {
@@ -1425,9 +1429,9 @@ void dcn401_optimize_bandwidth(
/* enable fams2 if needed */
if (dc->debug.fams2_config.bits.enable) {
- dcn401_fams2_global_control_lock(dc, context, true);
+ dcn401_dmub_hw_control_lock(dc, context, true);
dcn401_fams2_update_config(dc, context, true);
- dcn401_fams2_global_control_lock(dc, context, false);
+ dcn401_dmub_hw_control_lock(dc, context, false);
}
/* program dchubbub watermarks */
@@ -1466,14 +1470,17 @@ void dcn401_optimize_bandwidth(
}
}
-void dcn401_fams2_global_control_lock(struct dc *dc,
+void dcn401_dmub_hw_control_lock(struct dc *dc,
struct dc_state *context,
bool lock)
{
/* use always for now */
union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 };
- if (!dc->ctx || !dc->ctx->dmub_srv || !dc->debug.fams2_config.bits.enable)
+ if (!dc->ctx || !dc->ctx->dmub_srv)
+ return;
+
+ if (!dc->debug.fams2_config.bits.enable && !dc_dmub_srv_is_cursor_offload_enabled(dc))
return;
hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK;
@@ -1483,12 +1490,12 @@ void dcn401_fams2_global_control_lock(struct dc *dc,
dmub_hw_lock_mgr_inbox0_cmd(dc->ctx->dmub_srv, hw_lock_cmd);
}
-void dcn401_fams2_global_control_lock_fast(union block_sequence_params *params)
+void dcn401_dmub_hw_control_lock_fast(union block_sequence_params *params)
{
- struct dc *dc = params->fams2_global_control_lock_fast_params.dc;
- bool lock = params->fams2_global_control_lock_fast_params.lock;
+ struct dc *dc = params->dmub_hw_control_lock_fast_params.dc;
+ bool lock = params->dmub_hw_control_lock_fast_params.lock;
- if (params->fams2_global_control_lock_fast_params.is_required) {
+ if (params->dmub_hw_control_lock_fast_params.is_required) {
union dmub_inbox0_cmd_lock_hw hw_lock_cmd = { 0 };
hw_lock_cmd.bits.command_code = DMUB_INBOX0_CMD__HW_LOCK;
@@ -1595,6 +1602,143 @@ void dcn401_update_odm(struct dc *dc, struct dc_state *context,
dc->hwseq->funcs.blank_pixel_data(dc, otg_master, true);
}
+static void dcn401_add_dsc_sequence_for_odm_change(struct dc *dc, struct dc_state *context,
+ struct pipe_ctx *otg_master, struct block_sequence_state *seq_state)
+{
+ struct pipe_ctx *old_pipe;
+ struct pipe_ctx *new_pipe;
+ struct pipe_ctx *old_opp_heads[MAX_PIPES];
+ struct pipe_ctx *old_otg_master;
+ int old_opp_head_count = 0;
+ int i;
+
+ old_otg_master = &dc->current_state->res_ctx.pipe_ctx[otg_master->pipe_idx];
+
+ if (resource_is_pipe_type(old_otg_master, OTG_MASTER)) {
+ old_opp_head_count = resource_get_opp_heads_for_otg_master(old_otg_master,
+ &dc->current_state->res_ctx,
+ old_opp_heads);
+ } else {
+ old_otg_master = NULL;
+ }
+
+ /* Process new DSC configuration if DSC is enabled */
+ if (otg_master->stream_res.dsc && otg_master->stream->timing.flags.DSC) {
+ struct dc_stream_state *stream = otg_master->stream;
+ struct pipe_ctx *odm_pipe;
+ int opp_cnt = 1;
+ int last_dsc_calc = 0;
+ bool should_use_dto_dscclk = (dc->res_pool->dccg->funcs->set_dto_dscclk != NULL) &&
+ stream->timing.pix_clk_100hz > 480000;
+
+ /* Count ODM pipes */
+ for (odm_pipe = otg_master->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
+ opp_cnt++;
+
+ int num_slices_h = stream->timing.dsc_cfg.num_slices_h / opp_cnt;
+
+ /* Step 1: Set DTO DSCCLK for main DSC if needed */
+ if (should_use_dto_dscclk) {
+ hwss_add_dccg_set_dto_dscclk(seq_state, dc->res_pool->dccg,
+ otg_master->stream_res.dsc->inst, num_slices_h);
+ }
+
+ /* Step 2: Calculate and set DSC config for main DSC */
+ last_dsc_calc = *seq_state->num_steps;
+ hwss_add_dsc_calculate_and_set_config(seq_state, otg_master, true, opp_cnt);
+
+ /* Step 3: Enable main DSC block */
+ hwss_add_dsc_enable_with_opp(seq_state, otg_master);
+
+ /* Step 4: Configure and enable ODM DSC blocks */
+ for (odm_pipe = otg_master->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
+ if (!odm_pipe->stream_res.dsc)
+ continue;
+
+ /* Set DTO DSCCLK for ODM DSC if needed */
+ if (should_use_dto_dscclk) {
+ hwss_add_dccg_set_dto_dscclk(seq_state, dc->res_pool->dccg,
+ odm_pipe->stream_res.dsc->inst, num_slices_h);
+ }
+
+ /* Calculate and set DSC config for ODM DSC */
+ last_dsc_calc = *seq_state->num_steps;
+ hwss_add_dsc_calculate_and_set_config(seq_state, odm_pipe, true, opp_cnt);
+
+ /* Enable ODM DSC block */
+ hwss_add_dsc_enable_with_opp(seq_state, odm_pipe);
+ }
+
+ /* Step 5: Configure DSC in timing generator */
+ hwss_add_tg_set_dsc_config(seq_state, otg_master->stream_res.tg,
+ &seq_state->steps[last_dsc_calc].params.dsc_calculate_and_set_config_params.dsc_optc_cfg, true);
+ } else if (otg_master->stream_res.dsc && !otg_master->stream->timing.flags.DSC) {
+ /* Disable DSC in OPTC */
+ hwss_add_tg_set_dsc_config(seq_state, otg_master->stream_res.tg, NULL, false);
+
+ hwss_add_dsc_disconnect(seq_state, otg_master->stream_res.dsc);
+ }
+
+ /* Disable DSC for old pipes that no longer need it */
+ if (old_otg_master && old_otg_master->stream_res.dsc) {
+ for (i = 0; i < old_opp_head_count; i++) {
+ old_pipe = old_opp_heads[i];
+ new_pipe = &context->res_ctx.pipe_ctx[old_pipe->pipe_idx];
+
+ /* If old pipe had DSC but new pipe doesn't, disable the old DSC */
+ if (old_pipe->stream_res.dsc && !new_pipe->stream_res.dsc) {
+ /* Then disconnect DSC block */
+ hwss_add_dsc_disconnect(seq_state, old_pipe->stream_res.dsc);
+ }
+ }
+ }
+}
+
+void dcn401_update_odm_sequence(struct dc *dc, struct dc_state *context,
+ struct pipe_ctx *otg_master, struct block_sequence_state *seq_state)
+{
+ struct pipe_ctx *opp_heads[MAX_PIPES];
+ int opp_inst[MAX_PIPES] = {0};
+ int opp_head_count;
+ int odm_slice_width = resource_get_odm_slice_dst_width(otg_master, false);
+ int last_odm_slice_width = resource_get_odm_slice_dst_width(otg_master, true);
+ int i;
+
+ opp_head_count = resource_get_opp_heads_for_otg_master(
+ otg_master, &context->res_ctx, opp_heads);
+
+ for (i = 0; i < opp_head_count; i++)
+ opp_inst[i] = opp_heads[i]->stream_res.opp->inst;
+
+ /* Add ODM combine/bypass operation to sequence */
+ if (opp_head_count > 1) {
+ hwss_add_optc_set_odm_combine(seq_state, otg_master->stream_res.tg, opp_inst,
+ opp_head_count, odm_slice_width, last_odm_slice_width);
+ } else {
+ hwss_add_optc_set_odm_bypass(seq_state, otg_master->stream_res.tg, &otg_master->stream->timing);
+ }
+
+ /* Add OPP operations to sequence */
+ for (i = 0; i < opp_head_count; i++) {
+ /* Add OPP pipe clock control operation */
+ hwss_add_opp_pipe_clock_control(seq_state, opp_heads[i]->stream_res.opp, true);
+
+ /* Add OPP program left edge extra pixel operation */
+ hwss_add_opp_program_left_edge_extra_pixel(seq_state, opp_heads[i]->stream_res.opp,
+ opp_heads[i]->stream->timing.pixel_encoding, resource_is_pipe_type(opp_heads[i], OTG_MASTER));
+ }
+
+ /* Add DSC update operations to sequence */
+ dcn401_add_dsc_sequence_for_odm_change(dc, context, otg_master, seq_state);
+
+ /* Add blank pixel data operation if needed */
+ if (!resource_is_pipe_type(otg_master, DPP_PIPE)) {
+ if (dc->hwseq->funcs.blank_pixel_data_sequence)
+ dc->hwseq->funcs.blank_pixel_data_sequence(
+ dc, otg_master, true, seq_state);
+ }
+}
+
void dcn401_unblank_stream(struct pipe_ctx *pipe_ctx,
struct dc_link_settings *link_settings)
{
@@ -2083,6 +2227,157 @@ void dcn401_program_pipe(
}
}
+/*
+ * dcn401_program_pipe_sequence - Sequence-based version of dcn401_program_pipe
+ *
+ * This function creates a sequence-based version of the original dcn401_program_pipe
+ * function. Instead of directly calling hardware programming functions, it appends
+ * sequence steps to the provided block_sequence array that can later be executed
+ * as part of hwss_execute_sequence.
+ *
+ */
+void dcn401_program_pipe_sequence(
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ struct dce_hwseq *hws = dc->hwseq;
+
+ /* Only need to unblank on top pipe */
+ if (resource_is_pipe_type(pipe_ctx, OTG_MASTER)) {
+ if (pipe_ctx->update_flags.bits.enable ||
+ pipe_ctx->update_flags.bits.odm ||
+ pipe_ctx->stream->update_flags.bits.abm_level) {
+ if (dc->hwseq->funcs.blank_pixel_data_sequence)
+ dc->hwseq->funcs.blank_pixel_data_sequence(dc, pipe_ctx,
+ !pipe_ctx->plane_state || !pipe_ctx->plane_state->visible,
+ seq_state);
+ }
+ }
+
+ /* Only update TG on top pipe */
+ if (pipe_ctx->update_flags.bits.global_sync && !pipe_ctx->top_pipe
+ && !pipe_ctx->prev_odm_pipe) {
+
+ /* Step 1: Program global sync */
+ hwss_add_tg_program_global_sync(seq_state, pipe_ctx->stream_res.tg,
+ dcn401_calculate_vready_offset_for_group(pipe_ctx),
+ (unsigned int)pipe_ctx->global_sync.dcn4x.vstartup_lines,
+ (unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_offset_pixels,
+ (unsigned int)pipe_ctx->global_sync.dcn4x.vupdate_vupdate_width_pixels,
+ (unsigned int)pipe_ctx->global_sync.dcn4x.pstate_keepout_start_lines);
+
+ /* Step 2: Wait for VACTIVE state (if not phantom pipe) */
+ if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM)
+ hwss_add_tg_wait_for_state(seq_state, pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
+
+ /* Step 3: Set VTG params */
+ hwss_add_tg_set_vtg_params(seq_state, pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing, true);
+
+ /* Step 4: Setup vupdate interrupt (if available) */
+ if (hws->funcs.setup_vupdate_interrupt)
+ dcn401_setup_vupdate_interrupt_sequence(dc, pipe_ctx, seq_state);
+ }
+
+ if (pipe_ctx->update_flags.bits.odm) {
+ if (hws->funcs.update_odm_sequence)
+ hws->funcs.update_odm_sequence(dc, context, pipe_ctx, seq_state);
+ }
+
+ if (pipe_ctx->update_flags.bits.enable) {
+ if (dc->hwss.enable_plane_sequence)
+ dc->hwss.enable_plane_sequence(dc, pipe_ctx, context, seq_state);
+ }
+
+ if (pipe_ctx->update_flags.bits.det_size) {
+ if (dc->res_pool->hubbub->funcs->program_det_size) {
+ hwss_add_hubp_program_det_size(seq_state, dc->res_pool->hubbub,
+ pipe_ctx->plane_res.hubp->inst, pipe_ctx->det_buffer_size_kb);
+ }
+
+ if (dc->res_pool->hubbub->funcs->program_det_segments) {
+ hwss_add_hubp_program_det_segments(seq_state, dc->res_pool->hubbub,
+ pipe_ctx->plane_res.hubp->inst, pipe_ctx->hubp_regs.det_size);
+ }
+ }
+
+ if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw ||
+ pipe_ctx->plane_state->update_flags.raw ||
+ pipe_ctx->stream->update_flags.raw)) {
+
+ if (dc->hwss.update_dchubp_dpp_sequence)
+ dc->hwss.update_dchubp_dpp_sequence(dc, pipe_ctx, context, seq_state);
+ }
+
+ if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable ||
+ pipe_ctx->plane_state->update_flags.bits.hdr_mult)) {
+
+ hws->funcs.set_hdr_multiplier_sequence(pipe_ctx, seq_state);
+ }
+
+ if (pipe_ctx->plane_state &&
+ (pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_flags.bits.gamma_change ||
+ pipe_ctx->plane_state->update_flags.bits.lut_3d ||
+ pipe_ctx->update_flags.bits.enable)) {
+
+ hwss_add_dpp_set_input_transfer_func(seq_state, dc, pipe_ctx, pipe_ctx->plane_state);
+ }
+
+ /* dcn10_translate_regamma_to_hw_format takes 750us to finish
+ * only do gamma programming for powering on, internal memcmp to avoid
+ * updating on slave planes
+ */
+ if (pipe_ctx->update_flags.bits.enable ||
+ pipe_ctx->update_flags.bits.plane_changed ||
+ pipe_ctx->stream->update_flags.bits.out_tf) {
+ hwss_add_dpp_set_output_transfer_func(seq_state, dc, pipe_ctx, pipe_ctx->stream);
+ }
+
+ /* If the pipe has been enabled or has a different opp, we
+ * should reprogram the fmt. This deals with cases where
+ * interation between mpc and odm combine on different streams
+ * causes a different pipe to be chosen to odm combine with.
+ */
+ if (pipe_ctx->update_flags.bits.enable
+ || pipe_ctx->update_flags.bits.opp_changed) {
+
+ hwss_add_opp_set_dyn_expansion(seq_state, pipe_ctx->stream_res.opp, COLOR_SPACE_YCBCR601,
+ pipe_ctx->stream->timing.display_color_depth, pipe_ctx->stream->signal);
+
+ hwss_add_opp_program_fmt(seq_state, pipe_ctx->stream_res.opp,
+ &pipe_ctx->stream->bit_depth_params, &pipe_ctx->stream->clamping);
+ }
+
+ /* Set ABM pipe after other pipe configurations done */
+ if ((pipe_ctx->plane_state && pipe_ctx->plane_state->visible)) {
+ if (pipe_ctx->stream_res.abm) {
+ hwss_add_abm_set_pipe(seq_state, dc, pipe_ctx);
+
+ hwss_add_abm_set_level(seq_state, pipe_ctx->stream_res.abm, pipe_ctx->stream->abm_level);
+ }
+ }
+
+ if (pipe_ctx->update_flags.bits.test_pattern_changed) {
+ struct output_pixel_processor *odm_opp = pipe_ctx->stream_res.opp;
+
+ hwss_add_opp_program_bit_depth_reduction(seq_state, odm_opp, true, pipe_ctx);
+
+ hwss_add_opp_set_disp_pattern_generator(seq_state,
+ odm_opp,
+ pipe_ctx->stream_res.test_pattern_params.test_pattern,
+ pipe_ctx->stream_res.test_pattern_params.color_space,
+ pipe_ctx->stream_res.test_pattern_params.color_depth,
+ (struct tg_color){0},
+ false,
+ pipe_ctx->stream_res.test_pattern_params.width,
+ pipe_ctx->stream_res.test_pattern_params.height,
+ pipe_ctx->stream_res.test_pattern_params.offset);
+ }
+
+}
+
void dcn401_program_front_end_for_ctx(
struct dc *dc,
struct dc_state *context)
@@ -2160,7 +2455,6 @@ void dcn401_program_front_end_for_ctx(
&& context->res_ctx.pipe_ctx[i].stream)
hws->funcs.blank_pixel_data(dc, &context->res_ctx.pipe_ctx[i], true);
-
/* Disconnect mpcc */
for (i = 0; i < dc->res_pool->pipe_count; i++)
if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable
@@ -2239,11 +2533,11 @@ void dcn401_program_front_end_for_ctx(
/* Avoid underflow by check of pipe line read when adding 2nd plane. */
if (hws->wa.wait_hubpret_read_start_during_mpo_transition &&
- !pipe->top_pipe &&
- pipe->stream &&
- pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start &&
- dc->current_state->stream_status[0].plane_count == 1 &&
- context->stream_status[0].plane_count > 1) {
+ !pipe->top_pipe &&
+ pipe->stream &&
+ pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start &&
+ dc->current_state->stream_status[0].plane_count == 1 &&
+ context->stream_status[0].plane_count > 1) {
pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start(pipe->plane_res.hubp);
}
}
@@ -2355,7 +2649,6 @@ void dcn401_post_unlock_program_front_end(
*/
if (hwseq->funcs.update_force_pstate)
dc->hwseq->funcs.update_force_pstate(dc, context);
-
/* Only program the MALL registers after all the main and phantom pipes
* are done programming.
*/
@@ -2669,3 +2962,1084 @@ void dcn401_plane_atomic_power_down(struct dc *dc,
if (hws->funcs.dpp_root_clock_control)
hws->funcs.dpp_root_clock_control(hws, dpp->inst, false);
}
+
+void dcn401_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe)
+{
+ volatile struct dmub_cursor_offload_v1 *cs = dc->ctx->dmub_srv->dmub->cursor_offload_v1;
+ const struct pipe_ctx *top_pipe = resource_get_otg_master(pipe);
+ const struct hubp *hubp = pipe->plane_res.hubp;
+ const struct dpp *dpp = pipe->plane_res.dpp;
+ volatile struct dmub_cursor_offload_pipe_data_dcn401_v1 *p;
+ uint32_t stream_idx, write_idx, payload_idx;
+
+ if (!top_pipe || !hubp || !dpp)
+ return;
+
+ stream_idx = top_pipe->pipe_idx;
+ write_idx = cs->offload_streams[stream_idx].write_idx + 1; /* new payload (+1) */
+ payload_idx = write_idx % ARRAY_SIZE(cs->offload_streams[stream_idx].payloads);
+
+ p = &cs->offload_streams[stream_idx].payloads[payload_idx].pipe_data[pipe->pipe_idx].dcn401;
+
+ p->CURSOR0_0_CURSOR_SURFACE_ADDRESS = hubp->att.SURFACE_ADDR;
+ p->CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH = hubp->att.SURFACE_ADDR_HIGH;
+ p->CURSOR0_0_CURSOR_SIZE__CURSOR_WIDTH = hubp->att.size.bits.width;
+ p->CURSOR0_0_CURSOR_SIZE__CURSOR_HEIGHT = hubp->att.size.bits.height;
+ p->CURSOR0_0_CURSOR_POSITION__CURSOR_X_POSITION = hubp->pos.position.bits.x_pos;
+ p->CURSOR0_0_CURSOR_POSITION__CURSOR_Y_POSITION = hubp->pos.position.bits.y_pos;
+ p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_X = hubp->pos.hot_spot.bits.x_hot;
+ p->CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_Y = hubp->pos.hot_spot.bits.y_hot;
+ p->CURSOR0_0_CURSOR_DST_OFFSET__CURSOR_DST_X_OFFSET = hubp->pos.dst_offset.bits.dst_x_offset;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_ENABLE = hubp->pos.cur_ctl.bits.cur_enable;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_MODE = hubp->att.cur_ctl.bits.mode;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY = hubp->pos.cur_ctl.bits.cur_2x_magnify;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_PITCH = hubp->att.cur_ctl.bits.pitch;
+ p->CURSOR0_0_CURSOR_CONTROL__CURSOR_LINES_PER_CHUNK = hubp->att.cur_ctl.bits.line_per_chunk;
+
+ p->CM_CUR0_CURSOR0_CONTROL__CUR0_ENABLE = dpp->att.cur0_ctl.bits.cur0_enable;
+ p->CM_CUR0_CURSOR0_CONTROL__CUR0_MODE = dpp->att.cur0_ctl.bits.mode;
+ p->CM_CUR0_CURSOR0_CONTROL__CUR0_EXPANSION_MODE = dpp->att.cur0_ctl.bits.expansion_mode;
+ p->CM_CUR0_CURSOR0_CONTROL__CUR0_ROM_EN = dpp->att.cur0_ctl.bits.cur0_rom_en;
+ p->CM_CUR0_CURSOR0_COLOR0__CUR0_COLOR0 = 0x000000;
+ p->CM_CUR0_CURSOR0_COLOR1__CUR0_COLOR1 = 0xFFFFFF;
+
+ p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_BIAS_G_Y =
+ dpp->att.fp_scale_bias_g_y.bits.fp_bias_g_y;
+ p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_SCALE_G_Y =
+ dpp->att.fp_scale_bias_g_y.bits.fp_scale_g_y;
+ p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_BIAS_RB_CRCB =
+ dpp->att.fp_scale_bias_rb_crcb.bits.fp_bias_rb_crcb;
+ p->CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_SCALE_RB_CRCB =
+ dpp->att.fp_scale_bias_rb_crcb.bits.fp_scale_rb_crcb;
+
+ p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_DST_Y_OFFSET = hubp->att.settings.bits.dst_y_offset;
+ p->HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST = hubp->att.settings.bits.chunk_hdl_adjust;
+ p->HUBP0_DCHUBP_MALL_CONFIG__USE_MALL_FOR_CURSOR = hubp->use_mall_for_cursor;
+
+ cs->offload_streams[stream_idx].payloads[payload_idx].pipe_mask |= (1u << pipe->pipe_idx);
+}
+
+void dcn401_plane_atomic_power_down_sequence(struct dc *dc,
+ struct dpp *dpp,
+ struct hubp *hubp,
+ struct block_sequence_state *seq_state)
+{
+ struct dce_hwseq *hws = dc->hwseq;
+ uint32_t org_ip_request_cntl = 0;
+
+ DC_LOGGER_INIT(dc->ctx->logger);
+
+ /* Check and set DC_IP_REQUEST_CNTL if needed */
+ if (REG(DC_IP_REQUEST_CNTL)) {
+ REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
+ if (org_ip_request_cntl == 0)
+ hwss_add_dc_ip_request_cntl(seq_state, dc, true);
+ }
+
+ /* DPP power gating control */
+ hwss_add_dpp_pg_control(seq_state, hws, dpp->inst, false);
+
+ /* HUBP power gating control */
+ hwss_add_hubp_pg_control(seq_state, hws, hubp->inst, false);
+
+ /* HUBP reset */
+ hwss_add_hubp_reset(seq_state, hubp);
+
+ /* DPP reset */
+ hwss_add_dpp_reset(seq_state, dpp);
+
+ /* Restore DC_IP_REQUEST_CNTL if it was originally 0 */
+ if (org_ip_request_cntl == 0 && REG(DC_IP_REQUEST_CNTL))
+ hwss_add_dc_ip_request_cntl(seq_state, dc, false);
+
+ DC_LOG_DEBUG("Power gated front end %d\n", hubp->inst);
+
+ /* DPP root clock control */
+ hwss_add_dpp_root_clock_control(seq_state, hws, dpp->inst, false);
+}
+
+/* trigger HW to start disconnect plane from stream on the next vsync using block sequence */
+void dcn401_plane_atomic_disconnect_sequence(struct dc *dc,
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state)
+{
+ struct hubp *hubp = pipe_ctx->plane_res.hubp;
+ int dpp_id = pipe_ctx->plane_res.dpp->inst;
+ struct mpc *mpc = dc->res_pool->mpc;
+ struct mpc_tree *mpc_tree_params;
+ struct mpcc *mpcc_to_remove = NULL;
+ struct output_pixel_processor *opp = pipe_ctx->stream_res.opp;
+
+ mpc_tree_params = &(opp->mpc_tree_params);
+ mpcc_to_remove = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, dpp_id);
+
+ /*Already reset*/
+ if (mpcc_to_remove == NULL)
+ return;
+
+ /* Step 1: Remove MPCC from MPC tree */
+ hwss_add_mpc_remove_mpcc(seq_state, mpc, mpc_tree_params, mpcc_to_remove);
+
+ // Phantom pipes have OTG disabled by default, so MPCC_STATUS will never assert idle,
+ // so don't wait for MPCC_IDLE in the programming sequence
+ if (dc_state_get_pipe_subvp_type(state, pipe_ctx) != SUBVP_PHANTOM) {
+ /* Step 2: Set MPCC disconnect pending flag */
+ hwss_add_opp_set_mpcc_disconnect_pending(seq_state, opp, pipe_ctx->plane_res.mpcc_inst, true);
+ }
+
+ /* Step 3: Set optimized required flag */
+ hwss_add_dc_set_optimized_required(seq_state, dc, true);
+
+ /* Step 4: Disconnect HUBP if function exists */
+ if (hubp->funcs->hubp_disconnect)
+ hwss_add_hubp_disconnect(seq_state, hubp);
+
+ /* Step 5: Verify pstate change high if debug sanity checks are enabled */
+ if (dc->debug.sanity_checks)
+ dc->hwseq->funcs.verify_allow_pstate_change_high_sequence(dc, seq_state);
+}
+
+void dcn401_blank_pixel_data_sequence(
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ bool blank,
+ struct block_sequence_state *seq_state)
+{
+ struct tg_color black_color = {0};
+ struct stream_resource *stream_res = &pipe_ctx->stream_res;
+ struct dc_stream_state *stream = pipe_ctx->stream;
+ enum dc_color_space color_space = stream->output_color_space;
+ enum controller_dp_test_pattern test_pattern = CONTROLLER_DP_TEST_PATTERN_SOLID_COLOR;
+ enum controller_dp_color_space test_pattern_color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
+ struct pipe_ctx *odm_pipe;
+ struct rect odm_slice_src;
+
+ if (stream->link->test_pattern_enabled)
+ return;
+
+ /* get opp dpg blank color */
+ color_space_to_black_color(dc, color_space, &black_color);
+
+ if (blank) {
+ /* Set ABM immediate disable */
+ hwss_add_abm_set_immediate_disable(seq_state, dc, pipe_ctx);
+
+ if (dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE) {
+ test_pattern = CONTROLLER_DP_TEST_PATTERN_COLORSQUARES;
+ test_pattern_color_space = CONTROLLER_DP_COLOR_SPACE_RGB;
+ }
+ } else {
+ test_pattern = CONTROLLER_DP_TEST_PATTERN_VIDEOMODE;
+ }
+
+ odm_pipe = pipe_ctx;
+
+ /* Set display pattern generator for all ODM pipes */
+ while (odm_pipe->next_odm_pipe) {
+ odm_slice_src = resource_get_odm_slice_src_rect(odm_pipe);
+
+ hwss_add_opp_set_disp_pattern_generator(seq_state,
+ odm_pipe->stream_res.opp,
+ test_pattern,
+ test_pattern_color_space,
+ stream->timing.display_color_depth,
+ black_color,
+ true,
+ odm_slice_src.width,
+ odm_slice_src.height,
+ odm_slice_src.x);
+
+ odm_pipe = odm_pipe->next_odm_pipe;
+ }
+
+ /* Set display pattern generator for final ODM pipe */
+ odm_slice_src = resource_get_odm_slice_src_rect(odm_pipe);
+
+ hwss_add_opp_set_disp_pattern_generator(seq_state,
+ odm_pipe->stream_res.opp,
+ test_pattern,
+ test_pattern_color_space,
+ stream->timing.display_color_depth,
+ black_color,
+ true,
+ odm_slice_src.width,
+ odm_slice_src.height,
+ odm_slice_src.x);
+
+ /* Handle ABM level setting when not blanking */
+ if (!blank) {
+ if (stream_res->abm) {
+ /* Set pipe for ABM */
+ hwss_add_abm_set_pipe(seq_state, dc, pipe_ctx);
+
+ /* Set ABM level */
+ hwss_add_abm_set_level(seq_state, stream_res->abm, stream->abm_level);
+ }
+ }
+}
+
+void dcn401_program_all_writeback_pipes_in_tree_sequence(
+ struct dc *dc,
+ const struct dc_stream_state *stream,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ struct dwbc *dwb;
+ int i_wb, i_pipe;
+
+ if (!stream || stream->num_wb_info > dc->res_pool->res_cap->num_dwb)
+ return;
+
+ /* For each writeback pipe */
+ for (i_wb = 0; i_wb < stream->num_wb_info; i_wb++) {
+ /* Get direct pointer to writeback info */
+ struct dc_writeback_info *wb_info = (struct dc_writeback_info *)&stream->writeback_info[i_wb];
+ int mpcc_inst = -1;
+
+ if (wb_info->wb_enabled) {
+ /* Get the MPCC instance for writeback_source_plane */
+ for (i_pipe = 0; i_pipe < dc->res_pool->pipe_count; i_pipe++) {
+ struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[i_pipe];
+
+ if (!pipe_ctx->plane_state)
+ continue;
+
+ if (pipe_ctx->plane_state == wb_info->writeback_source_plane) {
+ mpcc_inst = pipe_ctx->plane_res.mpcc_inst;
+ break;
+ }
+ }
+
+ if (mpcc_inst == -1) {
+ /* Disable writeback pipe and disconnect from MPCC
+ * if source plane has been removed
+ */
+ dcn401_disable_writeback_sequence(dc, wb_info, seq_state);
+ continue;
+ }
+
+ ASSERT(wb_info->dwb_pipe_inst < dc->res_pool->res_cap->num_dwb);
+ dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
+
+ if (dwb->funcs->is_enabled(dwb)) {
+ /* Writeback pipe already enabled, only need to update */
+ dcn401_update_writeback_sequence(dc, wb_info, context, seq_state);
+ } else {
+ /* Enable writeback pipe and connect to MPCC */
+ dcn401_enable_writeback_sequence(dc, wb_info, context, mpcc_inst, seq_state);
+ }
+ } else {
+ /* Disable writeback pipe and disconnect from MPCC */
+ dcn401_disable_writeback_sequence(dc, wb_info, seq_state);
+ }
+ }
+}
+
+void dcn401_enable_writeback_sequence(
+ struct dc *dc,
+ struct dc_writeback_info *wb_info,
+ struct dc_state *context,
+ int mpcc_inst,
+ struct block_sequence_state *seq_state)
+{
+ struct dwbc *dwb;
+ struct mcif_wb *mcif_wb;
+
+ if (!wb_info->wb_enabled || wb_info->dwb_pipe_inst >= dc->res_pool->res_cap->num_dwb)
+ return;
+
+ dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
+ mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
+
+ /* Update DWBC with new parameters */
+ hwss_add_dwbc_update(seq_state, dwb, &wb_info->dwb_params);
+
+ /* Configure MCIF_WB buffer settings */
+ hwss_add_mcif_wb_config_buf(seq_state, mcif_wb, &wb_info->mcif_buf_params, wb_info->dwb_params.dest_height);
+
+ /* Configure MCIF_WB arbitration */
+ hwss_add_mcif_wb_config_arb(seq_state, mcif_wb, &context->bw_ctx.bw.dcn.bw_writeback.mcif_wb_arb[wb_info->dwb_pipe_inst]);
+
+ /* Enable MCIF_WB */
+ hwss_add_mcif_wb_enable(seq_state, mcif_wb);
+
+ /* Set DWB MUX to connect writeback to MPCC */
+ hwss_add_mpc_set_dwb_mux(seq_state, dc->res_pool->mpc, wb_info->dwb_pipe_inst, mpcc_inst);
+
+ /* Enable DWBC */
+ hwss_add_dwbc_enable(seq_state, dwb, &wb_info->dwb_params);
+}
+
+void dcn401_disable_writeback_sequence(
+ struct dc *dc,
+ struct dc_writeback_info *wb_info,
+ struct block_sequence_state *seq_state)
+{
+ struct dwbc *dwb;
+ struct mcif_wb *mcif_wb;
+
+ if (wb_info->dwb_pipe_inst >= dc->res_pool->res_cap->num_dwb)
+ return;
+
+ dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
+ mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
+
+ /* Disable DWBC */
+ hwss_add_dwbc_disable(seq_state, dwb);
+
+ /* Disable DWB MUX */
+ hwss_add_mpc_disable_dwb_mux(seq_state, dc->res_pool->mpc, wb_info->dwb_pipe_inst);
+
+ /* Disable MCIF_WB */
+ hwss_add_mcif_wb_disable(seq_state, mcif_wb);
+}
+
+void dcn401_update_writeback_sequence(
+ struct dc *dc,
+ struct dc_writeback_info *wb_info,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ struct dwbc *dwb;
+ struct mcif_wb *mcif_wb;
+
+ if (!wb_info->wb_enabled || wb_info->dwb_pipe_inst >= dc->res_pool->res_cap->num_dwb)
+ return;
+
+ dwb = dc->res_pool->dwbc[wb_info->dwb_pipe_inst];
+ mcif_wb = dc->res_pool->mcif_wb[wb_info->dwb_pipe_inst];
+
+ /* Update writeback pipe */
+ hwss_add_dwbc_update(seq_state, dwb, &wb_info->dwb_params);
+
+ /* Update MCIF_WB buffer settings if needed */
+ hwss_add_mcif_wb_config_buf(seq_state, mcif_wb, &wb_info->mcif_buf_params, wb_info->dwb_params.dest_height);
+}
+
+static int find_free_gsl_group(const struct dc *dc)
+{
+ if (dc->res_pool->gsl_groups.gsl_0 == 0)
+ return 1;
+ if (dc->res_pool->gsl_groups.gsl_1 == 0)
+ return 2;
+ if (dc->res_pool->gsl_groups.gsl_2 == 0)
+ return 3;
+
+ return 0;
+}
+
+void dcn401_setup_gsl_group_as_lock_sequence(
+ const struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ bool enable,
+ struct block_sequence_state *seq_state)
+{
+ struct gsl_params gsl;
+ int group_idx;
+
+ memset(&gsl, 0, sizeof(struct gsl_params));
+
+ if (enable) {
+ /* return if group already assigned since GSL was set up
+ * for vsync flip, we would unassign so it can't be "left over"
+ */
+ if (pipe_ctx->stream_res.gsl_group > 0)
+ return;
+
+ group_idx = find_free_gsl_group(dc);
+ ASSERT(group_idx != 0);
+ pipe_ctx->stream_res.gsl_group = group_idx;
+
+ /* set gsl group reg field and mark resource used */
+ switch (group_idx) {
+ case 1:
+ gsl.gsl0_en = 1;
+ dc->res_pool->gsl_groups.gsl_0 = 1;
+ break;
+ case 2:
+ gsl.gsl1_en = 1;
+ dc->res_pool->gsl_groups.gsl_1 = 1;
+ break;
+ case 3:
+ gsl.gsl2_en = 1;
+ dc->res_pool->gsl_groups.gsl_2 = 1;
+ break;
+ default:
+ BREAK_TO_DEBUGGER();
+ return; // invalid case
+ }
+ gsl.gsl_master_en = 1;
+ } else {
+ group_idx = pipe_ctx->stream_res.gsl_group;
+ if (group_idx == 0)
+ return; // if not in use, just return
+
+ pipe_ctx->stream_res.gsl_group = 0;
+
+ /* unset gsl group reg field and mark resource free */
+ switch (group_idx) {
+ case 1:
+ gsl.gsl0_en = 0;
+ dc->res_pool->gsl_groups.gsl_0 = 0;
+ break;
+ case 2:
+ gsl.gsl1_en = 0;
+ dc->res_pool->gsl_groups.gsl_1 = 0;
+ break;
+ case 3:
+ gsl.gsl2_en = 0;
+ dc->res_pool->gsl_groups.gsl_2 = 0;
+ break;
+ default:
+ BREAK_TO_DEBUGGER();
+ return;
+ }
+ gsl.gsl_master_en = 0;
+ }
+
+ hwss_add_tg_set_gsl(seq_state, pipe_ctx->stream_res.tg, gsl);
+ hwss_add_tg_set_gsl_source_select(seq_state, pipe_ctx->stream_res.tg, group_idx, enable ? 4 : 0);
+}
+
+void dcn401_disable_plane_sequence(
+ struct dc *dc,
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state)
+{
+ bool is_phantom = dc_state_get_pipe_subvp_type(state, pipe_ctx) == SUBVP_PHANTOM;
+ struct timing_generator *tg = is_phantom ? pipe_ctx->stream_res.tg : NULL;
+
+ if (!pipe_ctx->plane_res.hubp || pipe_ctx->plane_res.hubp->power_gated)
+ return;
+
+ /* Wait for MPCC disconnect */
+ if (dc->hwss.wait_for_mpcc_disconnect_sequence)
+ dc->hwss.wait_for_mpcc_disconnect_sequence(dc, dc->res_pool, pipe_ctx, seq_state);
+
+ /* In flip immediate with pipe splitting case GSL is used for synchronization
+ * so we must disable it when the plane is disabled.
+ */
+ if (pipe_ctx->stream_res.gsl_group != 0)
+ dcn401_setup_gsl_group_as_lock_sequence(dc, pipe_ctx, false, seq_state);
+
+ /* Update HUBP mall sel */
+ if (pipe_ctx->plane_res.hubp && pipe_ctx->plane_res.hubp->funcs->hubp_update_mall_sel)
+ hwss_add_hubp_update_mall_sel(seq_state, pipe_ctx->plane_res.hubp, 0, false);
+
+ /* Set flip control GSL */
+ hwss_add_hubp_set_flip_control_gsl(seq_state, pipe_ctx->plane_res.hubp, false);
+
+ /* HUBP clock control */
+ hwss_add_hubp_clk_cntl(seq_state, pipe_ctx->plane_res.hubp, false);
+
+ /* DPP clock control */
+ hwss_add_dpp_dppclk_control(seq_state, pipe_ctx->plane_res.dpp, false, false);
+
+ /* Plane atomic power down */
+ if (dc->hwseq->funcs.plane_atomic_power_down_sequence)
+ dc->hwseq->funcs.plane_atomic_power_down_sequence(dc, pipe_ctx->plane_res.dpp,
+ pipe_ctx->plane_res.hubp, seq_state);
+
+ pipe_ctx->stream = NULL;
+ memset(&pipe_ctx->stream_res, 0, sizeof(pipe_ctx->stream_res));
+ memset(&pipe_ctx->plane_res, 0, sizeof(pipe_ctx->plane_res));
+ pipe_ctx->top_pipe = NULL;
+ pipe_ctx->bottom_pipe = NULL;
+ pipe_ctx->prev_odm_pipe = NULL;
+ pipe_ctx->next_odm_pipe = NULL;
+ pipe_ctx->plane_state = NULL;
+
+ /* Turn back off the phantom OTG after the phantom plane is fully disabled */
+ if (is_phantom && tg && tg->funcs->disable_phantom_crtc)
+ hwss_add_disable_phantom_crtc(seq_state, tg);
+}
+
+void dcn401_post_unlock_reset_opp_sequence(
+ struct dc *dc,
+ struct pipe_ctx *opp_head,
+ struct block_sequence_state *seq_state)
+{
+ struct display_stream_compressor *dsc = opp_head->stream_res.dsc;
+ struct dccg *dccg = dc->res_pool->dccg;
+
+ /* Wait for all DPP pipes in current mpc blending tree completes double
+ * buffered disconnection before resetting OPP
+ */
+ if (dc->hwss.wait_for_mpcc_disconnect_sequence)
+ dc->hwss.wait_for_mpcc_disconnect_sequence(dc, dc->res_pool, opp_head, seq_state);
+
+ if (dsc) {
+ bool *is_ungated = NULL;
+ /* Check DSC power gate status */
+ if (dc->hwseq && dc->hwseq->funcs.dsc_pg_status)
+ hwss_add_dsc_pg_status(seq_state, dc->hwseq, dsc->inst, false);
+
+ /* Seamless update specific where we will postpone non
+ * double buffered DSCCLK disable logic in post unlock
+ * sequence after DSC is disconnected from OPP but not
+ * yet power gated.
+ */
+
+ /* DSC wait disconnect pending clear */
+ hwss_add_dsc_wait_disconnect_pending_clear(seq_state, dsc, is_ungated);
+
+ /* DSC disable */
+ hwss_add_dsc_disable(seq_state, dsc, is_ungated);
+
+ /* Set reference DSCCLK */
+ if (dccg && dccg->funcs->set_ref_dscclk)
+ hwss_add_dccg_set_ref_dscclk(seq_state, dccg, dsc->inst, 0);
+ }
+}
+
+void dcn401_dc_ip_request_cntl(struct dc *dc, bool enable)
+{
+ struct dce_hwseq *hws = dc->hwseq;
+
+ if (REG(DC_IP_REQUEST_CNTL))
+ REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, enable ? 1 : 0);
+}
+
+void dcn401_enable_plane_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ struct dce_hwseq *hws = dc->hwseq;
+ uint32_t org_ip_request_cntl = 0;
+
+ if (!pipe_ctx->plane_res.dpp || !pipe_ctx->plane_res.hubp || !pipe_ctx->stream_res.opp)
+ return;
+
+ if (REG(DC_IP_REQUEST_CNTL))
+ REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl);
+
+ /* Step 1: DPP root clock control - enable clock */
+ if (hws->funcs.dpp_root_clock_control)
+ hwss_add_dpp_root_clock_control(seq_state, hws, pipe_ctx->plane_res.dpp->inst, true);
+
+ /* Step 2: Enable DC IP request (if needed) */
+ if (hws->funcs.dc_ip_request_cntl)
+ hwss_add_dc_ip_request_cntl(seq_state, dc, true);
+
+ /* Step 3: DPP power gating control - power on */
+ if (REG(DC_IP_REQUEST_CNTL) && hws->funcs.dpp_pg_control)
+ hwss_add_dpp_pg_control(seq_state, hws, pipe_ctx->plane_res.dpp->inst, true);
+
+ /* Step 4: HUBP power gating control - power on */
+ if (REG(DC_IP_REQUEST_CNTL) && hws->funcs.hubp_pg_control)
+ hwss_add_hubp_pg_control(seq_state, hws, pipe_ctx->plane_res.hubp->inst, true);
+
+ /* Step 5: Disable DC IP request (restore state) */
+ if (org_ip_request_cntl == 0 && hws->funcs.dc_ip_request_cntl)
+ hwss_add_dc_ip_request_cntl(seq_state, dc, false);
+
+ /* Step 6: HUBP clock control - enable DCFCLK */
+ if (pipe_ctx->plane_res.hubp->funcs->hubp_clk_cntl)
+ hwss_add_hubp_clk_cntl(seq_state, pipe_ctx->plane_res.hubp, true);
+
+ /* Step 7: HUBP initialization */
+ if (pipe_ctx->plane_res.hubp->funcs->hubp_init)
+ hwss_add_hubp_init(seq_state, pipe_ctx->plane_res.hubp);
+
+ /* Step 8: OPP pipe clock control - enable */
+ if (pipe_ctx->stream_res.opp->funcs->opp_pipe_clock_control)
+ hwss_add_opp_pipe_clock_control(seq_state, pipe_ctx->stream_res.opp, true);
+
+ /* Step 9: VM system aperture settings */
+ if (dc->vm_pa_config.valid && pipe_ctx->plane_res.hubp->funcs->hubp_set_vm_system_aperture_settings) {
+ hwss_add_hubp_set_vm_system_aperture_settings(seq_state, pipe_ctx->plane_res.hubp, 0,
+ dc->vm_pa_config.system_aperture.start_addr, dc->vm_pa_config.system_aperture.end_addr);
+ }
+
+ /* Step 10: Flip interrupt setup */
+ if (!pipe_ctx->top_pipe
+ && pipe_ctx->plane_state
+ && pipe_ctx->plane_state->flip_int_enabled
+ && pipe_ctx->plane_res.hubp->funcs->hubp_set_flip_int) {
+ hwss_add_hubp_set_flip_int(seq_state, pipe_ctx->plane_res.hubp);
+ }
+}
+
+void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ struct dce_hwseq *hws = dc->hwseq;
+ struct hubp *hubp = pipe_ctx->plane_res.hubp;
+ struct dpp *dpp = pipe_ctx->plane_res.dpp;
+ struct dc_plane_state *plane_state = pipe_ctx->plane_state;
+ struct dccg *dccg = dc->res_pool->dccg;
+ bool viewport_changed = false;
+ enum mall_stream_type pipe_mall_type = dc_state_get_pipe_subvp_type(context, pipe_ctx);
+
+ if (!hubp || !dpp || !plane_state)
+ return;
+
+ /* Step 1: DPP DPPCLK control */
+ if (pipe_ctx->update_flags.bits.dppclk)
+ hwss_add_dpp_dppclk_control(seq_state, dpp, false, true);
+
+ /* Step 2: DCCG update DPP DTO */
+ if (pipe_ctx->update_flags.bits.enable)
+ hwss_add_dccg_update_dpp_dto(seq_state, dccg, dpp->inst, pipe_ctx->plane_res.bw.dppclk_khz);
+
+ /* Step 3: HUBP VTG selection */
+ if (pipe_ctx->update_flags.bits.hubp_rq_dlg_ttu) {
+ hwss_add_hubp_vtg_sel(seq_state, hubp, pipe_ctx->stream_res.tg->inst);
+
+ /* Step 4: HUBP setup (choose setup2 or setup) */
+ if (hubp->funcs->hubp_setup2) {
+ hwss_add_hubp_setup2(seq_state, hubp, &pipe_ctx->hubp_regs,
+ &pipe_ctx->global_sync, &pipe_ctx->stream->timing);
+ } else if (hubp->funcs->hubp_setup) {
+ hwss_add_hubp_setup(seq_state, hubp, &pipe_ctx->dlg_regs,
+ &pipe_ctx->ttu_regs, &pipe_ctx->rq_regs, &pipe_ctx->pipe_dlg_param);
+ }
+ }
+
+ /* Step 5: Set unbounded requesting */
+ if (pipe_ctx->update_flags.bits.unbounded_req && hubp->funcs->set_unbounded_requesting)
+ hwss_add_hubp_set_unbounded_requesting(seq_state, hubp, pipe_ctx->unbounded_req);
+
+ /* Step 6: HUBP interdependent setup */
+ if (pipe_ctx->update_flags.bits.hubp_interdependent) {
+ if (hubp->funcs->hubp_setup_interdependent2)
+ hwss_add_hubp_setup_interdependent2(seq_state, hubp, &pipe_ctx->hubp_regs);
+ else if (hubp->funcs->hubp_setup_interdependent)
+ hwss_add_hubp_setup_interdependent(seq_state, hubp, &pipe_ctx->dlg_regs, &pipe_ctx->ttu_regs);
+ }
+
+ /* Step 7: DPP setup - input CSC and format setup */
+ if (pipe_ctx->update_flags.bits.enable ||
+ pipe_ctx->update_flags.bits.plane_changed ||
+ plane_state->update_flags.bits.bpp_change ||
+ plane_state->update_flags.bits.input_csc_change ||
+ plane_state->update_flags.bits.color_space_change ||
+ plane_state->update_flags.bits.coeff_reduction_change) {
+ hwss_add_dpp_setup_dpp(seq_state, pipe_ctx);
+
+ /* Step 8: DPP cursor matrix setup */
+ if (dpp->funcs->set_cursor_matrix) {
+ hwss_add_dpp_set_cursor_matrix(seq_state, dpp, plane_state->color_space,
+ &plane_state->cursor_csc_color_matrix);
+ }
+
+ /* Step 9: DPP program bias and scale */
+ if (dpp->funcs->dpp_program_bias_and_scale)
+ hwss_add_dpp_program_bias_and_scale(seq_state, pipe_ctx);
+ }
+
+ /* Step 10: MPCC updates */
+ if (pipe_ctx->update_flags.bits.mpcc ||
+ pipe_ctx->update_flags.bits.plane_changed ||
+ plane_state->update_flags.bits.global_alpha_change ||
+ plane_state->update_flags.bits.per_pixel_alpha_change) {
+
+ /* Check if update_mpcc_sequence is implemented and prefer it over single MPC_UPDATE_MPCC step */
+ if (hws->funcs.update_mpcc_sequence)
+ hws->funcs.update_mpcc_sequence(dc, pipe_ctx, seq_state);
+ }
+
+ /* Step 11: DPP scaler setup */
+ if (pipe_ctx->update_flags.bits.scaler ||
+ plane_state->update_flags.bits.scaling_change ||
+ plane_state->update_flags.bits.position_change ||
+ plane_state->update_flags.bits.per_pixel_alpha_change ||
+ pipe_ctx->stream->update_flags.bits.scaling) {
+ pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha;
+ ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP);
+ hwss_add_dpp_set_scaler(seq_state, pipe_ctx->plane_res.dpp, &pipe_ctx->plane_res.scl_data);
+ }
+
+ /* Step 12: HUBP viewport programming */
+ if (pipe_ctx->update_flags.bits.viewport ||
+ (context == dc->current_state && plane_state->update_flags.bits.position_change) ||
+ (context == dc->current_state && plane_state->update_flags.bits.scaling_change) ||
+ (context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) {
+ hwss_add_hubp_mem_program_viewport(seq_state, hubp,
+ &pipe_ctx->plane_res.scl_data.viewport, &pipe_ctx->plane_res.scl_data.viewport_c);
+ viewport_changed = true;
+ }
+
+ /* Step 13: HUBP program mcache if available */
+ if (hubp->funcs->hubp_program_mcache_id_and_split_coordinate)
+ hwss_add_hubp_program_mcache_id(seq_state, hubp, &pipe_ctx->mcache_regs);
+
+ /* Step 14: Cursor attribute setup */
+ if ((pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed ||
+ pipe_ctx->update_flags.bits.scaler || viewport_changed == true) &&
+ pipe_ctx->stream->cursor_attributes.address.quad_part != 0) {
+
+ hwss_add_abort_cursor_offload_update(seq_state, dc, pipe_ctx);
+
+ hwss_add_set_cursor_attribute(seq_state, dc, pipe_ctx);
+
+ /* Step 15: Cursor position setup */
+ hwss_add_set_cursor_position(seq_state, dc, pipe_ctx);
+
+ /* Step 16: Cursor SDR white level */
+ if (dc->hwss.set_cursor_sdr_white_level)
+ hwss_add_set_cursor_sdr_white_level(seq_state, dc, pipe_ctx);
+ }
+
+ /* Step 17: Gamut remap and output CSC */
+ if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed ||
+ pipe_ctx->update_flags.bits.plane_changed ||
+ pipe_ctx->stream->update_flags.bits.gamut_remap ||
+ plane_state->update_flags.bits.gamut_remap_change ||
+ pipe_ctx->stream->update_flags.bits.out_csc) {
+
+ /* Gamut remap */
+ hwss_add_dpp_program_gamut_remap(seq_state, pipe_ctx);
+
+ /* Output CSC */
+ hwss_add_program_output_csc(seq_state, dc, pipe_ctx, pipe_ctx->stream->output_color_space,
+ pipe_ctx->stream->csc_color_matrix.matrix, hubp->opp_id);
+ }
+
+ /* Step 18: HUBP surface configuration */
+ if (pipe_ctx->update_flags.bits.enable ||
+ pipe_ctx->update_flags.bits.plane_changed ||
+ pipe_ctx->update_flags.bits.opp_changed ||
+ plane_state->update_flags.bits.pixel_format_change ||
+ plane_state->update_flags.bits.horizontal_mirror_change ||
+ plane_state->update_flags.bits.rotation_change ||
+ plane_state->update_flags.bits.swizzle_change ||
+ plane_state->update_flags.bits.dcc_change ||
+ plane_state->update_flags.bits.bpp_change ||
+ plane_state->update_flags.bits.scaling_change ||
+ plane_state->update_flags.bits.plane_size_change) {
+ struct plane_size size = plane_state->plane_size;
+
+ size.surface_size = pipe_ctx->plane_res.scl_data.viewport;
+ hwss_add_hubp_program_surface_config(seq_state, hubp,
+ plane_state->format, &plane_state->tiling_info, size,
+ plane_state->rotation, &plane_state->dcc,
+ plane_state->horizontal_mirror, 0);
+ hubp->power_gated = false;
+ }
+
+ /* Step 19: Update plane address (with SubVP support) */
+ if (pipe_ctx->update_flags.bits.enable ||
+ pipe_ctx->update_flags.bits.plane_changed ||
+ plane_state->update_flags.bits.addr_update) {
+
+ /* SubVP save surface address if needed */
+ if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && pipe_mall_type == SUBVP_MAIN) {
+ hwss_add_dmub_subvp_save_surf_addr(seq_state, dc->ctx->dmub_srv,
+ &pipe_ctx->plane_state->address, pipe_ctx->subvp_index);
+ }
+
+ /* Update plane address */
+ hwss_add_hubp_update_plane_addr(seq_state, dc, pipe_ctx);
+ }
+
+ /* Step 20: HUBP set blank - enable plane */
+ if (pipe_ctx->update_flags.bits.enable)
+ hwss_add_hubp_set_blank(seq_state, hubp, false);
+
+ /* Step 21: Phantom HUBP post enable */
+ if (pipe_mall_type == SUBVP_PHANTOM && hubp->funcs->phantom_hubp_post_enable)
+ hwss_add_phantom_hubp_post_enable(seq_state, hubp);
+}
+
+void dcn401_update_mpcc_sequence(struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state)
+{
+ struct hubp *hubp = pipe_ctx->plane_res.hubp;
+ struct mpcc_blnd_cfg blnd_cfg = {0};
+ bool per_pixel_alpha;
+ int mpcc_id;
+ struct mpcc *new_mpcc;
+ struct mpc *mpc = dc->res_pool->mpc;
+ struct mpc_tree *mpc_tree_params = &(pipe_ctx->stream_res.opp->mpc_tree_params);
+
+ if (!hubp || !pipe_ctx->plane_state)
+ return;
+
+ per_pixel_alpha = pipe_ctx->plane_state->per_pixel_alpha;
+
+ /* Initialize blend configuration */
+ blnd_cfg.overlap_only = false;
+ blnd_cfg.global_gain = 0xff;
+
+ if (per_pixel_alpha) {
+ blnd_cfg.pre_multiplied_alpha = pipe_ctx->plane_state->pre_multiplied_alpha;
+ if (pipe_ctx->plane_state->global_alpha) {
+ blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA_COMBINED_GLOBAL_GAIN;
+ blnd_cfg.global_gain = pipe_ctx->plane_state->global_alpha_value;
+ } else {
+ blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_PER_PIXEL_ALPHA;
+ }
+ } else {
+ blnd_cfg.pre_multiplied_alpha = false;
+ blnd_cfg.alpha_mode = MPCC_ALPHA_BLEND_MODE_GLOBAL_ALPHA;
+ }
+
+ if (pipe_ctx->plane_state->global_alpha)
+ blnd_cfg.global_alpha = pipe_ctx->plane_state->global_alpha_value;
+ else
+ blnd_cfg.global_alpha = 0xff;
+
+ blnd_cfg.background_color_bpc = 4;
+ blnd_cfg.bottom_gain_mode = 0;
+ blnd_cfg.top_gain = 0x1f000;
+ blnd_cfg.bottom_inside_gain = 0x1f000;
+ blnd_cfg.bottom_outside_gain = 0x1f000;
+
+ if (pipe_ctx->plane_state->format == SURFACE_PIXEL_FORMAT_GRPH_RGBE_ALPHA)
+ blnd_cfg.pre_multiplied_alpha = false;
+
+ /* MPCC instance is equal to HUBP instance */
+ mpcc_id = hubp->inst;
+
+ /* Step 1: Update blending if no full update needed */
+ if (!pipe_ctx->plane_state->update_flags.bits.full_update &&
+ !pipe_ctx->update_flags.bits.mpcc) {
+
+ /* Update blending configuration */
+ hwss_add_mpc_update_blending(seq_state, mpc, blnd_cfg, mpcc_id);
+
+ /* Update visual confirm color */
+ hwss_add_mpc_update_visual_confirm(seq_state, dc, pipe_ctx, mpcc_id);
+ return;
+ }
+
+ /* Step 2: Get existing MPCC for DPP */
+ new_mpcc = mpc->funcs->get_mpcc_for_dpp(mpc_tree_params, mpcc_id);
+
+ /* Step 3: Remove MPCC if being used */
+ if (new_mpcc != NULL) {
+ hwss_add_mpc_remove_mpcc(seq_state, mpc, mpc_tree_params, new_mpcc);
+ } else {
+ /* Step 4: Assert MPCC idle (debug only) */
+ if (dc->debug.sanity_checks)
+ hwss_add_mpc_assert_idle_mpcc(seq_state, mpc, mpcc_id);
+ }
+
+ /* Step 5: Insert new plane into MPC tree */
+ hwss_add_mpc_insert_plane(seq_state, mpc, mpc_tree_params, blnd_cfg, NULL, NULL, hubp->inst, mpcc_id);
+
+ /* Step 6: Update visual confirm color */
+ hwss_add_mpc_update_visual_confirm(seq_state, dc, pipe_ctx, mpcc_id);
+
+ /* Step 7: Set HUBP OPP and MPCC IDs */
+ hubp->opp_id = pipe_ctx->stream_res.opp->inst;
+ hubp->mpcc_id = mpcc_id;
+}
+
+static struct hubp *get_hubp_by_inst(struct resource_pool *res_pool, int mpcc_inst)
+{
+ int i;
+
+ for (i = 0; i < res_pool->pipe_count; i++) {
+ if (res_pool->hubps[i]->inst == mpcc_inst)
+ return res_pool->hubps[i];
+ }
+ ASSERT(false);
+ return NULL;
+}
+
+void dcn401_wait_for_mpcc_disconnect_sequence(
+ struct dc *dc,
+ struct resource_pool *res_pool,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state)
+{
+ int mpcc_inst;
+
+ if (dc->debug.sanity_checks)
+ dc->hwseq->funcs.verify_allow_pstate_change_high_sequence(dc, seq_state);
+
+ if (!pipe_ctx->stream_res.opp)
+ return;
+
+ for (mpcc_inst = 0; mpcc_inst < MAX_PIPES; mpcc_inst++) {
+ if (pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst]) {
+ struct hubp *hubp = get_hubp_by_inst(res_pool, mpcc_inst);
+
+ if (pipe_ctx->stream_res.tg &&
+ pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
+ hwss_add_mpc_assert_idle_mpcc(seq_state, res_pool->mpc, mpcc_inst);
+ }
+ pipe_ctx->stream_res.opp->mpcc_disconnect_pending[mpcc_inst] = false;
+ if (hubp)
+ hwss_add_hubp_set_blank(seq_state, hubp, true);
+ }
+ }
+
+ if (dc->debug.sanity_checks)
+ dc->hwseq->funcs.verify_allow_pstate_change_high_sequence(dc, seq_state);
+}
+
+void dcn401_setup_vupdate_interrupt_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state)
+{
+ struct timing_generator *tg = pipe_ctx->stream_res.tg;
+ int start_line = dc->hwss.get_vupdate_offset_from_vsync(pipe_ctx);
+
+ if (start_line < 0)
+ start_line = 0;
+
+ if (tg->funcs->setup_vertical_interrupt2)
+ hwss_add_tg_setup_vertical_interrupt2(seq_state, tg, start_line);
+}
+
+void dcn401_set_hdr_multiplier_sequence(struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state)
+{
+ struct fixed31_32 multiplier = pipe_ctx->plane_state->hdr_mult;
+ uint32_t hw_mult = 0x1f000; // 1.0 default multiplier
+ struct custom_float_format fmt;
+
+ fmt.exponenta_bits = 6;
+ fmt.mantissa_bits = 12;
+ fmt.sign = true;
+
+ if (!dc_fixpt_eq(multiplier, dc_fixpt_from_int(0))) // check != 0
+ convert_to_custom_float_format(multiplier, &fmt, &hw_mult);
+
+ hwss_add_dpp_set_hdr_multiplier(seq_state, pipe_ctx->plane_res.dpp, hw_mult);
+}
+
+void dcn401_program_mall_pipe_config_sequence(struct dc *dc, struct dc_state *context,
+ struct block_sequence_state *seq_state)
+{
+ int i;
+ unsigned int num_ways = dcn401_calculate_cab_allocation(dc, context);
+ bool cache_cursor = false;
+
+ // Don't force p-state disallow -- can't block dummy p-state
+
+ // Update MALL_SEL register for each pipe (break down update_mall_sel call)
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
+ struct hubp *hubp = pipe->plane_res.hubp;
+
+ if (pipe->stream && pipe->plane_state && hubp && hubp->funcs->hubp_update_mall_sel) {
+ int cursor_size = hubp->curs_attr.pitch * hubp->curs_attr.height;
+
+ switch (hubp->curs_attr.color_format) {
+ case CURSOR_MODE_MONO:
+ cursor_size /= 2;
+ break;
+ case CURSOR_MODE_COLOR_1BIT_AND:
+ case CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA:
+ case CURSOR_MODE_COLOR_UN_PRE_MULTIPLIED_ALPHA:
+ cursor_size *= 4;
+ break;
+
+ case CURSOR_MODE_COLOR_64BIT_FP_PRE_MULTIPLIED:
+ case CURSOR_MODE_COLOR_64BIT_FP_UN_PRE_MULTIPLIED:
+ default:
+ cursor_size *= 8;
+ break;
+ }
+
+ if (cursor_size > 16384)
+ cache_cursor = true;
+
+ if (dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
+ hwss_add_hubp_update_mall_sel(seq_state, hubp, 1, false);
+ } else {
+ // MALL not supported with Stereo3D
+ uint32_t mall_sel = (num_ways <= dc->caps.cache_num_ways &&
+ pipe->stream->link->psr_settings.psr_version == DC_PSR_VERSION_UNSUPPORTED &&
+ pipe->plane_state->address.type != PLN_ADDR_TYPE_GRPH_STEREO &&
+ !pipe->plane_state->address.tmz_surface) ? 2 : 0;
+ hwss_add_hubp_update_mall_sel(seq_state, hubp, mall_sel, cache_cursor);
+ }
+ }
+ }
+
+ // Program FORCE_ONE_ROW_FOR_FRAME and CURSOR_REQ_MODE for main subvp pipes
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
+ struct hubp *hubp = pipe->plane_res.hubp;
+
+ if (pipe->stream && hubp && hubp->funcs->hubp_prepare_subvp_buffering) {
+ if (dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_MAIN)
+ hwss_add_hubp_prepare_subvp_buffering(seq_state, hubp, true);
+ }
+ }
+}
+
+void dcn401_verify_allow_pstate_change_high_sequence(struct dc *dc,
+ struct block_sequence_state *seq_state)
+{
+ struct hubbub *hubbub = dc->res_pool->hubbub;
+
+ if (!hubbub->funcs->verify_allow_pstate_change_high)
+ return;
+
+ if (!hubbub->funcs->verify_allow_pstate_change_high(hubbub)) {
+ /* Attempt hardware workaround force recovery */
+ dcn401_hw_wa_force_recovery_sequence(dc, seq_state);
+ }
+}
+
+bool dcn401_hw_wa_force_recovery_sequence(struct dc *dc,
+ struct block_sequence_state *seq_state)
+{
+ struct hubp *hubp;
+ unsigned int i;
+
+ if (!dc->debug.recovery_enabled)
+ return false;
+
+ /* Step 1: Set HUBP_BLANK_EN=1 for all active pipes */
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
+
+ if (pipe_ctx != NULL) {
+ hubp = pipe_ctx->plane_res.hubp;
+ if (hubp != NULL && hubp->funcs->set_hubp_blank_en)
+ hwss_add_hubp_set_blank_en(seq_state, hubp, true);
+ }
+ }
+
+ /* Step 2: DCHUBBUB_GLOBAL_SOFT_RESET=1 */
+ hwss_add_hubbub_soft_reset(seq_state, dc->res_pool->hubbub, hubbub1_soft_reset, true);
+
+ /* Step 3: Set HUBP_DISABLE=1 for all active pipes */
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
+
+ if (pipe_ctx != NULL) {
+ hubp = pipe_ctx->plane_res.hubp;
+ if (hubp != NULL && hubp->funcs->hubp_disable_control)
+ hwss_add_hubp_disable_control(seq_state, hubp, true);
+ }
+ }
+
+ /* Step 4: Set HUBP_DISABLE=0 for all active pipes */
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
+
+ if (pipe_ctx != NULL) {
+ hubp = pipe_ctx->plane_res.hubp;
+ if (hubp != NULL && hubp->funcs->hubp_disable_control)
+ hwss_add_hubp_disable_control(seq_state, hubp, false);
+ }
+ }
+
+ /* Step 5: DCHUBBUB_GLOBAL_SOFT_RESET=0 */
+ hwss_add_hubbub_soft_reset(seq_state, dc->res_pool->hubbub, hubbub1_soft_reset, false);
+
+ /* Step 6: Set HUBP_BLANK_EN=0 for all active pipes */
+ for (i = 0; i < dc->res_pool->pipe_count; i++) {
+ struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
+
+ if (pipe_ctx != NULL) {
+ hubp = pipe_ctx->plane_res.hubp;
+ if (hubp != NULL && hubp->funcs->set_hubp_blank_en)
+ hwss_add_hubp_set_blank_en(seq_state, hubp, false);
+ }
+ }
+
+ return true;
+}
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h
index 2621b7725267..f78162ab859b 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h
@@ -9,6 +9,7 @@
#include "dc.h"
#include "dc_stream.h"
#include "hw_sequencer_private.h"
+#include "hwss/hw_sequencer.h"
#include "dcn401/dcn401_dccg.h"
struct dc;
@@ -73,15 +74,17 @@ void dcn401_optimize_bandwidth(
struct dc *dc,
struct dc_state *context);
-void dcn401_fams2_global_control_lock(struct dc *dc,
+void dcn401_dmub_hw_control_lock(struct dc *dc,
struct dc_state *context,
bool lock);
void dcn401_fams2_update_config(struct dc *dc, struct dc_state *context, bool enable);
-void dcn401_fams2_global_control_lock_fast(union block_sequence_params *params);
+void dcn401_dmub_hw_control_lock_fast(union block_sequence_params *params);
void dcn401_unblank_stream(struct pipe_ctx *pipe_ctx, struct dc_link_settings *link_settings);
void dcn401_hardware_release(struct dc *dc);
void dcn401_update_odm(struct dc *dc, struct dc_state *context,
struct pipe_ctx *otg_master);
+void dcn401_update_odm_sequence(struct dc *dc, struct dc_state *context,
+ struct pipe_ctx *otg_master, struct block_sequence_state *seq_state);
void adjust_hotspot_between_slices_for_2x_magnify(uint32_t cursor_width, struct dc_cursor_position *pos_cpy);
void dcn401_wait_for_det_buffer_update_under_otg_master(struct dc *dc, struct dc_state *context, struct pipe_ctx *otg_master);
void dcn401_interdependent_update_lock(struct dc *dc, struct dc_state *context, bool lock);
@@ -97,6 +100,11 @@ void dcn401_program_pipe(
struct dc *dc,
struct pipe_ctx *pipe_ctx,
struct dc_state *context);
+void dcn401_program_pipe_sequence(
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
void dcn401_perform_3dlut_wa_unlock(struct pipe_ctx *pipe_ctx);
void dcn401_program_front_end_for_ctx(struct dc *dc, struct dc_state *context);
void dcn401_post_unlock_program_front_end(struct dc *dc, struct dc_state *context);
@@ -109,5 +117,97 @@ void dcn401_detect_pipe_changes(
void dcn401_plane_atomic_power_down(struct dc *dc,
struct dpp *dpp,
struct hubp *hubp);
+void dcn401_plane_atomic_power_down_sequence(struct dc *dc,
+ struct dpp *dpp,
+ struct hubp *hubp,
+ struct block_sequence_state *seq_state);
+void dcn401_plane_atomic_disconnect_sequence(struct dc *dc,
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
+void dcn401_blank_pixel_data_sequence(
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ bool blank,
+ struct block_sequence_state *seq_state);
void dcn401_initialize_min_clocks(struct dc *dc);
+void dcn401_update_cursor_offload_pipe(struct dc *dc, const struct pipe_ctx *pipe);
+
+void dcn401_program_all_writeback_pipes_in_tree_sequence(
+ struct dc *dc,
+ const struct dc_stream_state *stream,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
+
+void dcn401_enable_writeback_sequence(
+ struct dc *dc,
+ struct dc_writeback_info *wb_info,
+ struct dc_state *context,
+ int mpcc_inst,
+ struct block_sequence_state *seq_state);
+
+void dcn401_disable_writeback_sequence(
+ struct dc *dc,
+ struct dc_writeback_info *wb_info,
+ struct block_sequence_state *seq_state);
+
+void dcn401_update_writeback_sequence(
+ struct dc *dc,
+ struct dc_writeback_info *wb_info,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
+
+void dcn401_setup_gsl_group_as_lock_sequence(
+ const struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ bool enable,
+ struct block_sequence_state *seq_state);
+
+void dcn401_disable_plane_sequence(
+ struct dc *dc,
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
+
+void dcn401_post_unlock_reset_opp_sequence(
+ struct dc *dc,
+ struct pipe_ctx *opp_head,
+ struct block_sequence_state *seq_state);
+
+void dcn401_dc_ip_request_cntl(struct dc *dc, bool enable);
+
+void dcn401_enable_plane_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
+
+void dcn401_update_dchubp_dpp_sequence(struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
+
+void dcn401_update_mpcc_sequence(struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
+
+void dcn401_wait_for_mpcc_disconnect_sequence(
+ struct dc *dc,
+ struct resource_pool *res_pool,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
+
+void dcn401_setup_vupdate_interrupt_sequence(struct dc *dc, struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
+
+void dcn401_set_hdr_multiplier_sequence(struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
+
+void dcn401_program_mall_pipe_config_sequence(struct dc *dc, struct dc_state *context,
+ struct block_sequence_state *seq_state);
+
+void dcn401_verify_allow_pstate_change_high_sequence(struct dc *dc,
+ struct block_sequence_state *seq_state);
+
+bool dcn401_hw_wa_force_recovery_sequence(struct dc *dc,
+ struct block_sequence_state *seq_state);
+
#endif /* __DC_HWSS_DCN401_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
index d6e11b7e4fce..162096ce0bdf 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c
@@ -9,6 +9,7 @@
#include "dcn30/dcn30_hwseq.h"
#include "dcn31/dcn31_hwseq.h"
#include "dcn32/dcn32_hwseq.h"
+#include "dcn35/dcn35_hwseq.h"
#include "dcn401/dcn401_hwseq.h"
#include "dcn401_init.h"
@@ -38,6 +39,7 @@ static const struct hw_sequencer_funcs dcn401_funcs = {
.enable_audio_stream = dce110_enable_audio_stream,
.disable_audio_stream = dce110_disable_audio_stream,
.disable_plane = dcn20_disable_plane,
+ .disable_plane_sequence = dcn401_disable_plane_sequence,
.pipe_control_lock = dcn20_pipe_control_lock,
.interdependent_update_lock = dcn401_interdependent_update_lock,
.cursor_lock = dcn10_cursor_lock,
@@ -53,6 +55,7 @@ static const struct hw_sequencer_funcs dcn401_funcs = {
.get_hw_state = dcn10_get_hw_state,
.clear_status_bits = dcn10_clear_status_bits,
.wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect,
+ .wait_for_mpcc_disconnect_sequence = dcn401_wait_for_mpcc_disconnect_sequence,
.edp_backlight_control = dce110_edp_backlight_control,
.edp_power_control = dce110_edp_power_control,
.edp_wait_for_hpd_ready = dce110_edp_wait_for_hpd_ready,
@@ -60,6 +63,12 @@ static const struct hw_sequencer_funcs dcn401_funcs = {
.set_cursor_position = dcn401_set_cursor_position,
.set_cursor_attribute = dcn10_set_cursor_attribute,
.set_cursor_sdr_white_level = dcn10_set_cursor_sdr_white_level,
+ .abort_cursor_offload_update = dcn35_abort_cursor_offload_update,
+ .begin_cursor_offload_update = dcn35_begin_cursor_offload_update,
+ .commit_cursor_offload_update = dcn35_commit_cursor_offload_update,
+ .update_cursor_offload_pipe = dcn401_update_cursor_offload_pipe,
+ .notify_cursor_offload_drr_update = dcn35_notify_cursor_offload_drr_update,
+ .program_cursor_offload_now = dcn35_program_cursor_offload_now,
.setup_periodic_interrupt = dcn10_setup_periodic_interrupt,
.set_clock = dcn10_set_clock,
.get_clock = dcn10_get_clock,
@@ -95,55 +104,70 @@ static const struct hw_sequencer_funcs dcn401_funcs = {
.apply_update_flags_for_phantom = dcn32_apply_update_flags_for_phantom,
.wait_for_dcc_meta_propagation = dcn401_wait_for_dcc_meta_propagation,
.is_pipe_topology_transition_seamless = dcn32_is_pipe_topology_transition_seamless,
- .fams2_global_control_lock = dcn401_fams2_global_control_lock,
+ .dmub_hw_control_lock = dcn401_dmub_hw_control_lock,
.fams2_update_config = dcn401_fams2_update_config,
- .fams2_global_control_lock_fast = dcn401_fams2_global_control_lock_fast,
+ .dmub_hw_control_lock_fast = dcn401_dmub_hw_control_lock_fast,
.program_outstanding_updates = dcn401_program_outstanding_updates,
.wait_for_all_pending_updates = dcn30_wait_for_all_pending_updates,
.detect_pipe_changes = dcn401_detect_pipe_changes,
.enable_plane = dcn20_enable_plane,
+ .enable_plane_sequence = dcn401_enable_plane_sequence,
.update_dchubp_dpp = dcn20_update_dchubp_dpp,
+ .update_dchubp_dpp_sequence = dcn401_update_dchubp_dpp_sequence,
.post_unlock_reset_opp = dcn20_post_unlock_reset_opp,
+ .post_unlock_reset_opp_sequence = dcn401_post_unlock_reset_opp_sequence,
.get_underflow_debug_data = dcn30_get_underflow_debug_data,
};
static const struct hwseq_private_funcs dcn401_private_funcs = {
.init_pipes = dcn10_init_pipes,
.plane_atomic_disconnect = dcn10_plane_atomic_disconnect,
+ .plane_atomic_disconnect_sequence = dcn401_plane_atomic_disconnect_sequence,
.update_mpcc = dcn20_update_mpcc,
+ .update_mpcc_sequence = dcn401_update_mpcc_sequence,
.set_input_transfer_func = dcn32_set_input_transfer_func,
.set_output_transfer_func = dcn401_set_output_transfer_func,
.power_down = dce110_power_down,
.enable_display_power_gating = dcn10_dummy_display_power_gating,
.blank_pixel_data = dcn20_blank_pixel_data,
+ .blank_pixel_data_sequence = dcn401_blank_pixel_data_sequence,
.reset_hw_ctx_wrap = dcn401_reset_hw_ctx_wrap,
.enable_stream_timing = dcn401_enable_stream_timing,
.edp_backlight_control = dce110_edp_backlight_control,
.setup_vupdate_interrupt = dcn20_setup_vupdate_interrupt,
+ .setup_vupdate_interrupt_sequence = dcn401_setup_vupdate_interrupt_sequence,
.did_underflow_occur = dcn10_did_underflow_occur,
.init_blank = dcn32_init_blank,
.disable_vga = dcn20_disable_vga,
.bios_golden_init = dcn10_bios_golden_init,
.plane_atomic_disable = dcn20_plane_atomic_disable,
.plane_atomic_power_down = dcn401_plane_atomic_power_down,
+ .plane_atomic_power_down_sequence = dcn401_plane_atomic_power_down_sequence,
.enable_power_gating_plane = dcn32_enable_power_gating_plane,
.hubp_pg_control = dcn32_hubp_pg_control,
.program_all_writeback_pipes_in_tree = dcn30_program_all_writeback_pipes_in_tree,
+ .program_all_writeback_pipes_in_tree_sequence = dcn401_program_all_writeback_pipes_in_tree_sequence,
.update_odm = dcn401_update_odm,
+ .update_odm_sequence = dcn401_update_odm_sequence,
.dsc_pg_control = dcn32_dsc_pg_control,
.dsc_pg_status = dcn32_dsc_pg_status,
.set_hdr_multiplier = dcn10_set_hdr_multiplier,
+ .set_hdr_multiplier_sequence = dcn401_set_hdr_multiplier_sequence,
.verify_allow_pstate_change_high = dcn10_verify_allow_pstate_change_high,
+ .verify_allow_pstate_change_high_sequence = dcn401_verify_allow_pstate_change_high_sequence,
.wait_for_blank_complete = dcn20_wait_for_blank_complete,
.dccg_init = dcn20_dccg_init,
.set_mcm_luts = dcn401_set_mcm_luts,
.program_mall_pipe_config = dcn32_program_mall_pipe_config,
+ .program_mall_pipe_config_sequence = dcn401_program_mall_pipe_config_sequence,
.update_mall_sel = dcn32_update_mall_sel,
.calculate_dccg_k1_k2_values = NULL,
.apply_single_controller_ctx_to_hw = dce110_apply_single_controller_ctx_to_hw,
.reset_back_end_for_pipe = dcn401_reset_back_end_for_pipe,
.populate_mcm_luts = NULL,
.perform_3dlut_wa_unlock = dcn401_perform_3dlut_wa_unlock,
+ .program_pipe_sequence = dcn401_program_pipe_sequence,
+ .dc_ip_request_cntl = dcn401_dc_ip_request_cntl,
};
void dcn401_hw_sequencer_init_functions(struct dc *dc)
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
index 1723bbcf2c46..8ed9eea40c56 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h
@@ -31,6 +31,8 @@
#include "inc/hw/opp.h"
#include "inc/hw/link_encoder.h"
#include "inc/core_status.h"
+#include "inc/hw/hw_shared.h"
+#include "dsc/dsc.h"
struct pipe_ctx;
struct dc_state;
@@ -48,6 +50,8 @@ struct dc_dmub_cmd;
struct pg_block_update;
struct drr_params;
struct dc_underflow_debug_data;
+struct dsc_optc_config;
+struct vm_system_aperture_param;
struct subvp_pipe_control_lock_fast_params {
struct dc *dc;
@@ -62,7 +66,7 @@ struct pipe_control_lock_params {
};
struct set_flip_control_gsl_params {
- struct pipe_ctx *pipe_ctx;
+ struct hubp *hubp;
bool flip_immediate;
};
@@ -148,12 +152,587 @@ struct wait_for_dcc_meta_propagation_params {
const struct pipe_ctx *top_pipe_to_program;
};
-struct fams2_global_control_lock_fast_params {
+struct dmub_hw_control_lock_fast_params {
struct dc *dc;
bool is_required;
bool lock;
};
+struct program_surface_config_params {
+ struct hubp *hubp;
+ enum surface_pixel_format format;
+ struct dc_tiling_info *tiling_info;
+ struct plane_size plane_size;
+ enum dc_rotation_angle rotation;
+ struct dc_plane_dcc_param *dcc;
+ bool horizontal_mirror;
+ int compat_level;
+};
+
+struct program_mcache_id_and_split_coordinate {
+ struct hubp *hubp;
+ struct dml2_hubp_pipe_mcache_regs *mcache_regs;
+};
+
+struct program_cursor_update_now_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct hubp_wait_pipe_read_start_params {
+ struct hubp *hubp;
+};
+
+struct apply_update_flags_for_phantom_params {
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct update_phantom_vp_position_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+ struct dc_state *context;
+};
+
+struct set_odm_combine_params {
+ struct timing_generator *tg;
+ int opp_inst[MAX_PIPES];
+ int opp_head_count;
+ int odm_slice_width;
+ int last_odm_slice_width;
+};
+
+struct set_odm_bypass_params {
+ struct timing_generator *tg;
+ const struct dc_crtc_timing *timing;
+};
+
+struct opp_pipe_clock_control_params {
+ struct output_pixel_processor *opp;
+ bool enable;
+};
+
+struct opp_program_left_edge_extra_pixel_params {
+ struct output_pixel_processor *opp;
+ enum dc_pixel_encoding pixel_encoding;
+ bool is_otg_master;
+};
+
+struct dccg_set_dto_dscclk_params {
+ struct dccg *dccg;
+ int inst;
+ int num_slices_h;
+};
+
+struct dsc_set_config_params {
+ struct display_stream_compressor *dsc;
+ struct dsc_config *dsc_cfg;
+ struct dsc_optc_config *dsc_optc_cfg;
+};
+
+struct dsc_enable_params {
+ struct display_stream_compressor *dsc;
+ int opp_inst;
+};
+
+struct tg_set_dsc_config_params {
+ struct timing_generator *tg;
+ struct dsc_optc_config *dsc_optc_cfg;
+ bool enable;
+};
+
+struct dsc_disconnect_params {
+ struct display_stream_compressor *dsc;
+};
+
+struct dsc_read_state_params {
+ struct display_stream_compressor *dsc;
+ struct dcn_dsc_state *dsc_state;
+};
+
+struct dsc_calculate_and_set_config_params {
+ struct pipe_ctx *pipe_ctx;
+ struct dsc_optc_config dsc_optc_cfg;
+ bool enable;
+ int opp_cnt;
+};
+
+struct dsc_enable_with_opp_params {
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct program_tg_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+ struct dc_state *context;
+};
+
+struct tg_program_global_sync_params {
+ struct timing_generator *tg;
+ int vready_offset;
+ unsigned int vstartup_lines;
+ unsigned int vupdate_offset_pixels;
+ unsigned int vupdate_vupdate_width_pixels;
+ unsigned int pstate_keepout_start_lines;
+};
+
+struct tg_wait_for_state_params {
+ struct timing_generator *tg;
+ enum crtc_state state;
+};
+
+struct tg_set_vtg_params_params {
+ struct timing_generator *tg;
+ struct dc_crtc_timing *timing;
+ bool program_fp2;
+};
+
+struct tg_set_gsl_params {
+ struct timing_generator *tg;
+ struct gsl_params gsl;
+};
+
+struct tg_set_gsl_source_select_params {
+ struct timing_generator *tg;
+ int group_idx;
+ uint32_t gsl_ready_signal;
+};
+
+struct setup_vupdate_interrupt_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct tg_setup_vertical_interrupt2_params {
+ struct timing_generator *tg;
+ int start_line;
+};
+
+struct dpp_set_hdr_multiplier_params {
+ struct dpp *dpp;
+ uint32_t hw_mult;
+};
+
+struct program_det_size_params {
+ struct hubbub *hubbub;
+ unsigned int hubp_inst;
+ unsigned int det_buffer_size_kb;
+};
+
+struct program_det_segments_params {
+ struct hubbub *hubbub;
+ unsigned int hubp_inst;
+ unsigned int det_size;
+};
+
+struct update_dchubp_dpp_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+ struct dc_state *context;
+};
+
+struct opp_set_dyn_expansion_params {
+ struct output_pixel_processor *opp;
+ enum dc_color_space color_space;
+ enum dc_color_depth color_depth;
+ enum signal_type signal;
+};
+
+struct opp_program_fmt_params {
+ struct output_pixel_processor *opp;
+ struct bit_depth_reduction_params *fmt_bit_depth;
+ struct clamping_and_pixel_encoding_params *clamping;
+};
+
+struct opp_program_bit_depth_reduction_params {
+ struct output_pixel_processor *opp;
+ bool use_default_params;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct opp_set_disp_pattern_generator_params {
+ struct output_pixel_processor *opp;
+ enum controller_dp_test_pattern test_pattern;
+ enum controller_dp_color_space color_space;
+ enum dc_color_depth color_depth;
+ struct tg_color solid_color;
+ bool use_solid_color;
+ int width;
+ int height;
+ int offset;
+};
+
+struct set_abm_pipe_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct set_abm_level_params {
+ struct abm *abm;
+ unsigned int abm_level;
+};
+
+struct set_abm_immediate_disable_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct set_disp_pattern_generator_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+ enum controller_dp_test_pattern test_pattern;
+ enum controller_dp_color_space color_space;
+ enum dc_color_depth color_depth;
+ const struct tg_color *solid_color;
+ int width;
+ int height;
+ int offset;
+};
+
+struct mpc_update_blending_params {
+ struct mpc *mpc;
+ struct mpcc_blnd_cfg blnd_cfg;
+ int mpcc_id;
+};
+
+struct mpc_assert_idle_mpcc_params {
+ struct mpc *mpc;
+ int mpcc_id;
+};
+
+struct mpc_insert_plane_params {
+ struct mpc *mpc;
+ struct mpc_tree *mpc_tree_params;
+ struct mpcc_blnd_cfg blnd_cfg;
+ struct mpcc_sm_cfg *sm_cfg;
+ struct mpcc *insert_above_mpcc;
+ int dpp_id;
+ int mpcc_id;
+};
+
+struct mpc_remove_mpcc_params {
+ struct mpc *mpc;
+ struct mpc_tree *mpc_tree_params;
+ struct mpcc *mpcc_to_remove;
+};
+
+struct opp_set_mpcc_disconnect_pending_params {
+ struct output_pixel_processor *opp;
+ int mpcc_inst;
+ bool pending;
+};
+
+struct dc_set_optimized_required_params {
+ struct dc *dc;
+ bool optimized_required;
+};
+
+struct hubp_disconnect_params {
+ struct hubp *hubp;
+};
+
+struct hubbub_force_pstate_change_control_params {
+ struct hubbub *hubbub;
+ bool enable;
+ bool wait;
+};
+
+struct tg_enable_crtc_params {
+ struct timing_generator *tg;
+};
+
+struct hubp_wait_flip_pending_params {
+ struct hubp *hubp;
+ unsigned int timeout_us;
+ unsigned int polling_interval_us;
+};
+
+struct tg_wait_double_buffer_pending_params {
+ struct timing_generator *tg;
+ unsigned int timeout_us;
+ unsigned int polling_interval_us;
+};
+
+struct update_force_pstate_params {
+ struct dc *dc;
+ struct dc_state *context;
+};
+
+struct hubbub_apply_dedcn21_147_wa_params {
+ struct hubbub *hubbub;
+};
+
+struct hubbub_allow_self_refresh_control_params {
+ struct hubbub *hubbub;
+ bool allow;
+ bool *disallow_self_refresh_applied;
+};
+
+struct tg_get_frame_count_params {
+ struct timing_generator *tg;
+ unsigned int *frame_count;
+};
+
+struct mpc_set_dwb_mux_params {
+ struct mpc *mpc;
+ int dwb_id;
+ int mpcc_id;
+};
+
+struct mpc_disable_dwb_mux_params {
+ struct mpc *mpc;
+ unsigned int dwb_id;
+};
+
+struct mcif_wb_config_buf_params {
+ struct mcif_wb *mcif_wb;
+ struct mcif_buf_params *mcif_buf_params;
+ unsigned int dest_height;
+};
+
+struct mcif_wb_config_arb_params {
+ struct mcif_wb *mcif_wb;
+ struct mcif_arb_params *mcif_arb_params;
+};
+
+struct mcif_wb_enable_params {
+ struct mcif_wb *mcif_wb;
+};
+
+struct mcif_wb_disable_params {
+ struct mcif_wb *mcif_wb;
+};
+
+struct dwbc_enable_params {
+ struct dwbc *dwb;
+ struct dc_dwb_params *dwb_params;
+};
+
+struct dwbc_disable_params {
+ struct dwbc *dwb;
+};
+
+struct dwbc_update_params {
+ struct dwbc *dwb;
+ struct dc_dwb_params *dwb_params;
+};
+
+struct hubp_update_mall_sel_params {
+ struct hubp *hubp;
+ uint32_t mall_sel;
+ bool cache_cursor;
+};
+
+struct hubp_prepare_subvp_buffering_params {
+ struct hubp *hubp;
+ bool enable;
+};
+
+struct hubp_set_blank_en_params {
+ struct hubp *hubp;
+ bool enable;
+};
+
+struct hubp_disable_control_params {
+ struct hubp *hubp;
+ bool disable;
+};
+
+struct hubbub_soft_reset_params {
+ struct hubbub *hubbub;
+ void (*hubbub_soft_reset)(struct hubbub *hubbub, bool reset);
+ bool reset;
+};
+
+struct hubp_clk_cntl_params {
+ struct hubp *hubp;
+ bool enable;
+};
+
+struct hubp_init_params {
+ struct hubp *hubp;
+};
+
+struct hubp_set_vm_system_aperture_settings_params {
+ struct hubp *hubp;
+ //struct vm_system_aperture_param apt;
+ PHYSICAL_ADDRESS_LOC sys_default;
+ PHYSICAL_ADDRESS_LOC sys_low;
+ PHYSICAL_ADDRESS_LOC sys_high;
+};
+
+struct hubp_set_flip_int_params {
+ struct hubp *hubp;
+};
+
+struct dpp_dppclk_control_params {
+ struct dpp *dpp;
+ bool dppclk_div;
+ bool enable;
+};
+
+struct disable_phantom_crtc_params {
+ struct timing_generator *tg;
+};
+
+struct dpp_pg_control_params {
+ struct dce_hwseq *hws;
+ unsigned int dpp_inst;
+ bool power_on;
+};
+
+struct hubp_pg_control_params {
+ struct dce_hwseq *hws;
+ unsigned int hubp_inst;
+ bool power_on;
+};
+
+struct hubp_reset_params {
+ struct hubp *hubp;
+};
+
+struct dpp_reset_params {
+ struct dpp *dpp;
+};
+
+struct dpp_root_clock_control_params {
+ struct dce_hwseq *hws;
+ unsigned int dpp_inst;
+ bool clock_on;
+};
+
+struct dc_ip_request_cntl_params {
+ struct dc *dc;
+ bool enable;
+};
+
+struct dsc_pg_status_params {
+ struct dce_hwseq *hws;
+ int dsc_inst;
+ bool is_ungated;
+};
+
+struct dsc_wait_disconnect_pending_clear_params {
+ struct display_stream_compressor *dsc;
+ bool *is_ungated;
+};
+
+struct dsc_disable_params {
+ struct display_stream_compressor *dsc;
+ bool *is_ungated;
+};
+
+struct dccg_set_ref_dscclk_params {
+ struct dccg *dccg;
+ int dsc_inst;
+ bool *is_ungated;
+};
+
+struct dccg_update_dpp_dto_params {
+ struct dccg *dccg;
+ int dpp_inst;
+ int dppclk_khz;
+};
+
+struct hubp_vtg_sel_params {
+ struct hubp *hubp;
+ uint32_t otg_inst;
+};
+
+struct hubp_setup2_params {
+ struct hubp *hubp;
+ struct dml2_dchub_per_pipe_register_set *hubp_regs;
+ union dml2_global_sync_programming *global_sync;
+ struct dc_crtc_timing *timing;
+};
+
+struct hubp_setup_params {
+ struct hubp *hubp;
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs;
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs;
+ struct _vcs_dpi_display_rq_regs_st *rq_regs;
+ struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest;
+};
+
+struct hubp_set_unbounded_requesting_params {
+ struct hubp *hubp;
+ bool unbounded_req;
+};
+
+struct hubp_setup_interdependent2_params {
+ struct hubp *hubp;
+ struct dml2_dchub_per_pipe_register_set *hubp_regs;
+};
+
+struct hubp_setup_interdependent_params {
+ struct hubp *hubp;
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs;
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs;
+};
+
+struct dpp_set_cursor_matrix_params {
+ struct dpp *dpp;
+ enum dc_color_space color_space;
+ struct dc_csc_transform *cursor_csc_color_matrix;
+};
+
+struct mpc_update_mpcc_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct dpp_set_scaler_params {
+ struct dpp *dpp;
+ const struct scaler_data *scl_data;
+};
+
+struct hubp_mem_program_viewport_params {
+ struct hubp *hubp;
+ const struct rect *viewport;
+ const struct rect *viewport_c;
+};
+
+struct hubp_program_mcache_id_and_split_coordinate_params {
+ struct hubp *hubp;
+ struct mcache_regs_struct *mcache_regs;
+};
+
+struct abort_cursor_offload_update_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct set_cursor_attribute_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct set_cursor_position_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct set_cursor_sdr_white_level_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+};
+
+struct program_output_csc_params {
+ struct dc *dc;
+ struct pipe_ctx *pipe_ctx;
+ enum dc_color_space colorspace;
+ uint16_t *matrix;
+ int opp_id;
+};
+
+struct hubp_set_blank_params {
+ struct hubp *hubp;
+ bool blank;
+};
+
+struct phantom_hubp_post_enable_params {
+ struct hubp *hubp;
+};
+
union block_sequence_params {
struct update_plane_addr_params update_plane_addr_params;
struct subvp_pipe_control_lock_fast_params subvp_pipe_control_lock_fast_params;
@@ -173,7 +752,108 @@ union block_sequence_params {
struct set_ocsc_default_params set_ocsc_default_params;
struct subvp_save_surf_addr subvp_save_surf_addr;
struct wait_for_dcc_meta_propagation_params wait_for_dcc_meta_propagation_params;
- struct fams2_global_control_lock_fast_params fams2_global_control_lock_fast_params;
+ struct dmub_hw_control_lock_fast_params dmub_hw_control_lock_fast_params;
+ struct program_surface_config_params program_surface_config_params;
+ struct program_mcache_id_and_split_coordinate program_mcache_id_and_split_coordinate;
+ struct program_cursor_update_now_params program_cursor_update_now_params;
+ struct hubp_wait_pipe_read_start_params hubp_wait_pipe_read_start_params;
+ struct apply_update_flags_for_phantom_params apply_update_flags_for_phantom_params;
+ struct update_phantom_vp_position_params update_phantom_vp_position_params;
+ struct set_odm_combine_params set_odm_combine_params;
+ struct set_odm_bypass_params set_odm_bypass_params;
+ struct opp_pipe_clock_control_params opp_pipe_clock_control_params;
+ struct opp_program_left_edge_extra_pixel_params opp_program_left_edge_extra_pixel_params;
+ struct dccg_set_dto_dscclk_params dccg_set_dto_dscclk_params;
+ struct dsc_set_config_params dsc_set_config_params;
+ struct dsc_enable_params dsc_enable_params;
+ struct tg_set_dsc_config_params tg_set_dsc_config_params;
+ struct dsc_disconnect_params dsc_disconnect_params;
+ struct dsc_read_state_params dsc_read_state_params;
+ struct dsc_calculate_and_set_config_params dsc_calculate_and_set_config_params;
+ struct dsc_enable_with_opp_params dsc_enable_with_opp_params;
+ struct program_tg_params program_tg_params;
+ struct tg_program_global_sync_params tg_program_global_sync_params;
+ struct tg_wait_for_state_params tg_wait_for_state_params;
+ struct tg_set_vtg_params_params tg_set_vtg_params_params;
+ struct tg_setup_vertical_interrupt2_params tg_setup_vertical_interrupt2_params;
+ struct dpp_set_hdr_multiplier_params dpp_set_hdr_multiplier_params;
+ struct tg_set_gsl_params tg_set_gsl_params;
+ struct tg_set_gsl_source_select_params tg_set_gsl_source_select_params;
+ struct setup_vupdate_interrupt_params setup_vupdate_interrupt_params;
+ struct program_det_size_params program_det_size_params;
+ struct program_det_segments_params program_det_segments_params;
+ struct update_dchubp_dpp_params update_dchubp_dpp_params;
+ struct opp_set_dyn_expansion_params opp_set_dyn_expansion_params;
+ struct opp_program_fmt_params opp_program_fmt_params;
+ struct opp_program_bit_depth_reduction_params opp_program_bit_depth_reduction_params;
+ struct opp_set_disp_pattern_generator_params opp_set_disp_pattern_generator_params;
+ struct set_abm_pipe_params set_abm_pipe_params;
+ struct set_abm_level_params set_abm_level_params;
+ struct set_abm_immediate_disable_params set_abm_immediate_disable_params;
+ struct set_disp_pattern_generator_params set_disp_pattern_generator_params;
+ struct mpc_remove_mpcc_params mpc_remove_mpcc_params;
+ struct opp_set_mpcc_disconnect_pending_params opp_set_mpcc_disconnect_pending_params;
+ struct dc_set_optimized_required_params dc_set_optimized_required_params;
+ struct hubp_disconnect_params hubp_disconnect_params;
+ struct hubbub_force_pstate_change_control_params hubbub_force_pstate_change_control_params;
+ struct tg_enable_crtc_params tg_enable_crtc_params;
+ struct hubp_wait_flip_pending_params hubp_wait_flip_pending_params;
+ struct tg_wait_double_buffer_pending_params tg_wait_double_buffer_pending_params;
+ struct update_force_pstate_params update_force_pstate_params;
+ struct hubbub_apply_dedcn21_147_wa_params hubbub_apply_dedcn21_147_wa_params;
+ struct hubbub_allow_self_refresh_control_params hubbub_allow_self_refresh_control_params;
+ struct tg_get_frame_count_params tg_get_frame_count_params;
+ struct mpc_set_dwb_mux_params mpc_set_dwb_mux_params;
+ struct mpc_disable_dwb_mux_params mpc_disable_dwb_mux_params;
+ struct mcif_wb_config_buf_params mcif_wb_config_buf_params;
+ struct mcif_wb_config_arb_params mcif_wb_config_arb_params;
+ struct mcif_wb_enable_params mcif_wb_enable_params;
+ struct mcif_wb_disable_params mcif_wb_disable_params;
+ struct dwbc_enable_params dwbc_enable_params;
+ struct dwbc_disable_params dwbc_disable_params;
+ struct dwbc_update_params dwbc_update_params;
+ struct hubp_update_mall_sel_params hubp_update_mall_sel_params;
+ struct hubp_prepare_subvp_buffering_params hubp_prepare_subvp_buffering_params;
+ struct hubp_set_blank_en_params hubp_set_blank_en_params;
+ struct hubp_disable_control_params hubp_disable_control_params;
+ struct hubbub_soft_reset_params hubbub_soft_reset_params;
+ struct hubp_clk_cntl_params hubp_clk_cntl_params;
+ struct hubp_init_params hubp_init_params;
+ struct hubp_set_vm_system_aperture_settings_params hubp_set_vm_system_aperture_settings_params;
+ struct hubp_set_flip_int_params hubp_set_flip_int_params;
+ struct dpp_dppclk_control_params dpp_dppclk_control_params;
+ struct disable_phantom_crtc_params disable_phantom_crtc_params;
+ struct dpp_pg_control_params dpp_pg_control_params;
+ struct hubp_pg_control_params hubp_pg_control_params;
+ struct hubp_reset_params hubp_reset_params;
+ struct dpp_reset_params dpp_reset_params;
+ struct dpp_root_clock_control_params dpp_root_clock_control_params;
+ struct dc_ip_request_cntl_params dc_ip_request_cntl_params;
+ struct dsc_pg_status_params dsc_pg_status_params;
+ struct dsc_wait_disconnect_pending_clear_params dsc_wait_disconnect_pending_clear_params;
+ struct dsc_disable_params dsc_disable_params;
+ struct dccg_set_ref_dscclk_params dccg_set_ref_dscclk_params;
+ struct dccg_update_dpp_dto_params dccg_update_dpp_dto_params;
+ struct hubp_vtg_sel_params hubp_vtg_sel_params;
+ struct hubp_setup2_params hubp_setup2_params;
+ struct hubp_setup_params hubp_setup_params;
+ struct hubp_set_unbounded_requesting_params hubp_set_unbounded_requesting_params;
+ struct hubp_setup_interdependent2_params hubp_setup_interdependent2_params;
+ struct hubp_setup_interdependent_params hubp_setup_interdependent_params;
+ struct dpp_set_cursor_matrix_params dpp_set_cursor_matrix_params;
+ struct mpc_update_mpcc_params mpc_update_mpcc_params;
+ struct mpc_update_blending_params mpc_update_blending_params;
+ struct mpc_assert_idle_mpcc_params mpc_assert_idle_mpcc_params;
+ struct mpc_insert_plane_params mpc_insert_plane_params;
+ struct dpp_set_scaler_params dpp_set_scaler_params;
+ struct hubp_mem_program_viewport_params hubp_mem_program_viewport_params;
+ struct abort_cursor_offload_update_params abort_cursor_offload_update_params;
+ struct set_cursor_attribute_params set_cursor_attribute_params;
+ struct set_cursor_position_params set_cursor_position_params;
+ struct set_cursor_sdr_white_level_params set_cursor_sdr_white_level_params;
+ struct program_output_csc_params program_output_csc_params;
+ struct hubp_set_blank_params hubp_set_blank_params;
+ struct phantom_hubp_post_enable_params phantom_hubp_post_enable_params;
};
enum block_sequence_func {
@@ -189,13 +869,111 @@ enum block_sequence_func {
DPP_SETUP_DPP,
DPP_PROGRAM_BIAS_AND_SCALE,
DPP_SET_OUTPUT_TRANSFER_FUNC,
+ DPP_SET_HDR_MULTIPLIER,
MPC_UPDATE_VISUAL_CONFIRM,
MPC_POWER_ON_MPC_MEM_PWR,
MPC_SET_OUTPUT_CSC,
MPC_SET_OCSC_DEFAULT,
DMUB_SUBVP_SAVE_SURF_ADDR,
HUBP_WAIT_FOR_DCC_META_PROP,
- DMUB_FAMS2_GLOBAL_CONTROL_LOCK_FAST,
+ DMUB_HW_CONTROL_LOCK_FAST,
+ HUBP_PROGRAM_SURFACE_CONFIG,
+ HUBP_PROGRAM_MCACHE_ID,
+ PROGRAM_CURSOR_UPDATE_NOW,
+ HUBP_WAIT_PIPE_READ_START,
+ HWS_APPLY_UPDATE_FLAGS_FOR_PHANTOM,
+ HWS_UPDATE_PHANTOM_VP_POSITION,
+ OPTC_SET_ODM_COMBINE,
+ OPTC_SET_ODM_BYPASS,
+ OPP_PIPE_CLOCK_CONTROL,
+ OPP_PROGRAM_LEFT_EDGE_EXTRA_PIXEL,
+ DCCG_SET_DTO_DSCCLK,
+ DSC_SET_CONFIG,
+ DSC_ENABLE,
+ TG_SET_DSC_CONFIG,
+ DSC_DISCONNECT,
+ DSC_READ_STATE,
+ DSC_CALCULATE_AND_SET_CONFIG,
+ DSC_ENABLE_WITH_OPP,
+ TG_PROGRAM_GLOBAL_SYNC,
+ TG_WAIT_FOR_STATE,
+ TG_SET_VTG_PARAMS,
+ TG_SETUP_VERTICAL_INTERRUPT2,
+ HUBP_PROGRAM_DET_SIZE,
+ HUBP_PROGRAM_DET_SEGMENTS,
+ OPP_SET_DYN_EXPANSION,
+ OPP_PROGRAM_FMT,
+ OPP_PROGRAM_BIT_DEPTH_REDUCTION,
+ OPP_SET_DISP_PATTERN_GENERATOR,
+ ABM_SET_PIPE,
+ ABM_SET_LEVEL,
+ ABM_SET_IMMEDIATE_DISABLE,
+ MPC_REMOVE_MPCC,
+ OPP_SET_MPCC_DISCONNECT_PENDING,
+ DC_SET_OPTIMIZED_REQUIRED,
+ HUBP_DISCONNECT,
+ HUBBUB_FORCE_PSTATE_CHANGE_CONTROL,
+ TG_ENABLE_CRTC,
+ TG_SET_GSL,
+ TG_SET_GSL_SOURCE_SELECT,
+ HUBP_WAIT_FLIP_PENDING,
+ TG_WAIT_DOUBLE_BUFFER_PENDING,
+ UPDATE_FORCE_PSTATE,
+ PROGRAM_MALL_PIPE_CONFIG,
+ HUBBUB_APPLY_DEDCN21_147_WA,
+ HUBBUB_ALLOW_SELF_REFRESH_CONTROL,
+ TG_GET_FRAME_COUNT,
+ MPC_SET_DWB_MUX,
+ MPC_DISABLE_DWB_MUX,
+ MCIF_WB_CONFIG_BUF,
+ MCIF_WB_CONFIG_ARB,
+ MCIF_WB_ENABLE,
+ MCIF_WB_DISABLE,
+ DWBC_ENABLE,
+ DWBC_DISABLE,
+ DWBC_UPDATE,
+ HUBP_UPDATE_MALL_SEL,
+ HUBP_PREPARE_SUBVP_BUFFERING,
+ HUBP_SET_BLANK_EN,
+ HUBP_DISABLE_CONTROL,
+ HUBBUB_SOFT_RESET,
+ HUBP_CLK_CNTL,
+ HUBP_INIT,
+ HUBP_SET_VM_SYSTEM_APERTURE_SETTINGS,
+ HUBP_SET_FLIP_INT,
+ DPP_DPPCLK_CONTROL,
+ DISABLE_PHANTOM_CRTC,
+ DSC_PG_STATUS,
+ DSC_WAIT_DISCONNECT_PENDING_CLEAR,
+ DSC_DISABLE,
+ DCCG_SET_REF_DSCCLK,
+ DPP_PG_CONTROL,
+ HUBP_PG_CONTROL,
+ HUBP_RESET,
+ DPP_RESET,
+ DPP_ROOT_CLOCK_CONTROL,
+ DC_IP_REQUEST_CNTL,
+ DCCG_UPDATE_DPP_DTO,
+ HUBP_VTG_SEL,
+ HUBP_SETUP2,
+ HUBP_SETUP,
+ HUBP_SET_UNBOUNDED_REQUESTING,
+ HUBP_SETUP_INTERDEPENDENT2,
+ HUBP_SETUP_INTERDEPENDENT,
+ DPP_SET_CURSOR_MATRIX,
+ MPC_UPDATE_BLENDING,
+ MPC_ASSERT_IDLE_MPCC,
+ MPC_INSERT_PLANE,
+ DPP_SET_SCALER,
+ HUBP_MEM_PROGRAM_VIEWPORT,
+ ABORT_CURSOR_OFFLOAD_UPDATE,
+ SET_CURSOR_ATTRIBUTE,
+ SET_CURSOR_POSITION,
+ SET_CURSOR_SDR_WHITE_LEVEL,
+ PROGRAM_OUTPUT_CSC,
+ HUBP_SET_LEGACY_TILING_COMPAT_LEVEL,
+ HUBP_SET_BLANK,
+ PHANTOM_HUBP_POST_ENABLE,
/* This must be the last value in this enum, add new ones above */
HWSS_BLOCK_SEQUENCE_FUNC_COUNT
};
@@ -205,6 +983,11 @@ struct block_sequence {
enum block_sequence_func func;
};
+struct block_sequence_state {
+ struct block_sequence *steps;
+ unsigned int *num_steps;
+};
+
#define MAX_HWSS_BLOCK_SEQUENCE_SIZE (HWSS_BLOCK_SEQUENCE_FUNC_COUNT * MAX_PIPES)
struct hw_sequencer_funcs {
@@ -222,6 +1005,8 @@ struct hw_sequencer_funcs {
enum dc_status (*apply_ctx_to_hw)(struct dc *dc,
struct dc_state *context);
void (*disable_plane)(struct dc *dc, struct dc_state *state, struct pipe_ctx *pipe_ctx);
+ void (*disable_plane_sequence)(struct dc *dc, struct dc_state *state, struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
void (*disable_pixel_data)(struct dc *dc, struct pipe_ctx *pipe_ctx, bool blank);
void (*apply_ctx_for_surface)(struct dc *dc,
const struct dc_stream_state *stream,
@@ -239,6 +1024,10 @@ struct hw_sequencer_funcs {
void (*wait_for_mpcc_disconnect)(struct dc *dc,
struct resource_pool *res_pool,
struct pipe_ctx *pipe_ctx);
+ void (*wait_for_mpcc_disconnect_sequence)(struct dc *dc,
+ struct resource_pool *res_pool,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
void (*edp_backlight_control)(
struct dc_link *link,
bool enable);
@@ -310,6 +1099,13 @@ struct hw_sequencer_funcs {
void (*set_cursor_position)(struct pipe_ctx *pipe);
void (*set_cursor_attribute)(struct pipe_ctx *pipe);
void (*set_cursor_sdr_white_level)(struct pipe_ctx *pipe);
+ void (*abort_cursor_offload_update)(struct dc *dc, const struct pipe_ctx *pipe);
+ void (*begin_cursor_offload_update)(struct dc *dc, const struct pipe_ctx *pipe);
+ void (*commit_cursor_offload_update)(struct dc *dc, const struct pipe_ctx *pipe);
+ void (*update_cursor_offload_pipe)(struct dc *dc, const struct pipe_ctx *pipe);
+ void (*notify_cursor_offload_drr_update)(struct dc *dc, struct dc_state *context,
+ const struct dc_stream_state *stream);
+ void (*program_cursor_offload_now)(struct dc *dc, const struct pipe_ctx *pipe);
/* Colour Related */
void (*program_gamut_remap)(struct pipe_ctx *pipe_ctx);
@@ -452,13 +1248,13 @@ struct hw_sequencer_funcs {
const struct dc_state *new_ctx);
void (*wait_for_dcc_meta_propagation)(const struct dc *dc,
const struct pipe_ctx *top_pipe_to_program);
- void (*fams2_global_control_lock)(struct dc *dc,
+ void (*dmub_hw_control_lock)(struct dc *dc,
struct dc_state *context,
bool lock);
void (*fams2_update_config)(struct dc *dc,
struct dc_state *context,
bool enable);
- void (*fams2_global_control_lock_fast)(union block_sequence_params *params);
+ void (*dmub_hw_control_lock_fast)(union block_sequence_params *params);
void (*set_long_vtotal)(struct pipe_ctx **pipe_ctx, int num_pipes, uint32_t v_total_min, uint32_t v_total_max);
void (*program_outstanding_updates)(struct dc *dc,
struct dc_state *context);
@@ -471,11 +1267,23 @@ struct hw_sequencer_funcs {
void (*enable_plane)(struct dc *dc,
struct pipe_ctx *pipe_ctx,
struct dc_state *context);
+ void (*enable_plane_sequence)(struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
void (*update_dchubp_dpp)(struct dc *dc,
struct pipe_ctx *pipe_ctx,
struct dc_state *context);
+ void (*update_dchubp_dpp_sequence)(struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
void (*post_unlock_reset_opp)(struct dc *dc,
struct pipe_ctx *opp_head);
+ void (*post_unlock_reset_opp_sequence)(
+ struct dc *dc,
+ struct pipe_ctx *opp_head,
+ struct block_sequence_state *seq_state);
void (*get_underflow_debug_data)(const struct dc *dc,
struct timing_generator *tg,
struct dc_underflow_debug_data *out_data);
@@ -588,4 +1396,630 @@ void hwss_set_ocsc_default(union block_sequence_params *params);
void hwss_subvp_save_surf_addr(union block_sequence_params *params);
+void hwss_program_surface_config(union block_sequence_params *params);
+
+void hwss_program_mcache_id_and_split_coordinate(union block_sequence_params *params);
+
+void hwss_set_odm_combine(union block_sequence_params *params);
+
+void hwss_set_odm_bypass(union block_sequence_params *params);
+
+void hwss_opp_pipe_clock_control(union block_sequence_params *params);
+
+void hwss_opp_program_left_edge_extra_pixel(union block_sequence_params *params);
+
+void hwss_blank_pixel_data(union block_sequence_params *params);
+
+void hwss_dccg_set_dto_dscclk(union block_sequence_params *params);
+
+void hwss_dsc_set_config(union block_sequence_params *params);
+
+void hwss_dsc_enable(union block_sequence_params *params);
+
+void hwss_tg_set_dsc_config(union block_sequence_params *params);
+
+void hwss_dsc_disconnect(union block_sequence_params *params);
+
+void hwss_dsc_read_state(union block_sequence_params *params);
+
+void hwss_dsc_calculate_and_set_config(union block_sequence_params *params);
+
+void hwss_dsc_enable_with_opp(union block_sequence_params *params);
+
+void hwss_program_tg(union block_sequence_params *params);
+
+void hwss_tg_program_global_sync(union block_sequence_params *params);
+
+void hwss_tg_wait_for_state(union block_sequence_params *params);
+
+void hwss_tg_set_vtg_params(union block_sequence_params *params);
+
+void hwss_tg_setup_vertical_interrupt2(union block_sequence_params *params);
+
+void hwss_dpp_set_hdr_multiplier(union block_sequence_params *params);
+
+void hwss_program_det_size(union block_sequence_params *params);
+
+void hwss_program_det_segments(union block_sequence_params *params);
+
+void hwss_opp_set_dyn_expansion(union block_sequence_params *params);
+
+void hwss_opp_program_fmt(union block_sequence_params *params);
+
+void hwss_opp_program_bit_depth_reduction(union block_sequence_params *params);
+
+void hwss_opp_set_disp_pattern_generator(union block_sequence_params *params);
+
+void hwss_set_abm_pipe(union block_sequence_params *params);
+
+void hwss_set_abm_level(union block_sequence_params *params);
+
+void hwss_set_abm_immediate_disable(union block_sequence_params *params);
+
+void hwss_mpc_remove_mpcc(union block_sequence_params *params);
+
+void hwss_opp_set_mpcc_disconnect_pending(union block_sequence_params *params);
+
+void hwss_dc_set_optimized_required(union block_sequence_params *params);
+
+void hwss_hubp_disconnect(union block_sequence_params *params);
+
+void hwss_hubbub_force_pstate_change_control(union block_sequence_params *params);
+
+void hwss_tg_enable_crtc(union block_sequence_params *params);
+
+void hwss_tg_set_gsl(union block_sequence_params *params);
+
+void hwss_tg_set_gsl_source_select(union block_sequence_params *params);
+
+void hwss_hubp_wait_flip_pending(union block_sequence_params *params);
+
+void hwss_tg_wait_double_buffer_pending(union block_sequence_params *params);
+
+void hwss_update_force_pstate(union block_sequence_params *params);
+
+void hwss_hubbub_apply_dedcn21_147_wa(union block_sequence_params *params);
+
+void hwss_hubbub_allow_self_refresh_control(union block_sequence_params *params);
+
+void hwss_tg_get_frame_count(union block_sequence_params *params);
+
+void hwss_mpc_set_dwb_mux(union block_sequence_params *params);
+
+void hwss_mpc_disable_dwb_mux(union block_sequence_params *params);
+
+void hwss_mcif_wb_config_buf(union block_sequence_params *params);
+
+void hwss_mcif_wb_config_arb(union block_sequence_params *params);
+
+void hwss_mcif_wb_enable(union block_sequence_params *params);
+
+void hwss_mcif_wb_disable(union block_sequence_params *params);
+
+void hwss_dwbc_enable(union block_sequence_params *params);
+
+void hwss_dwbc_disable(union block_sequence_params *params);
+
+void hwss_dwbc_update(union block_sequence_params *params);
+
+void hwss_hubp_update_mall_sel(union block_sequence_params *params);
+
+void hwss_hubp_prepare_subvp_buffering(union block_sequence_params *params);
+
+void hwss_hubp_set_blank_en(union block_sequence_params *params);
+
+void hwss_hubp_disable_control(union block_sequence_params *params);
+
+void hwss_hubbub_soft_reset(union block_sequence_params *params);
+
+void hwss_hubp_clk_cntl(union block_sequence_params *params);
+
+void hwss_hubp_init(union block_sequence_params *params);
+
+void hwss_hubp_set_vm_system_aperture_settings(union block_sequence_params *params);
+
+void hwss_hubp_set_flip_int(union block_sequence_params *params);
+
+void hwss_dpp_dppclk_control(union block_sequence_params *params);
+
+void hwss_disable_phantom_crtc(union block_sequence_params *params);
+
+void hwss_dsc_pg_status(union block_sequence_params *params);
+
+void hwss_dsc_wait_disconnect_pending_clear(union block_sequence_params *params);
+
+void hwss_dsc_disable(union block_sequence_params *params);
+
+void hwss_dccg_set_ref_dscclk(union block_sequence_params *params);
+
+void hwss_dpp_pg_control(union block_sequence_params *params);
+
+void hwss_hubp_pg_control(union block_sequence_params *params);
+
+void hwss_hubp_reset(union block_sequence_params *params);
+
+void hwss_dpp_reset(union block_sequence_params *params);
+
+void hwss_dpp_root_clock_control(union block_sequence_params *params);
+
+void hwss_dc_ip_request_cntl(union block_sequence_params *params);
+
+void hwss_dccg_update_dpp_dto(union block_sequence_params *params);
+
+void hwss_hubp_vtg_sel(union block_sequence_params *params);
+
+void hwss_hubp_setup2(union block_sequence_params *params);
+
+void hwss_hubp_setup(union block_sequence_params *params);
+
+void hwss_hubp_set_unbounded_requesting(union block_sequence_params *params);
+
+void hwss_hubp_setup_interdependent2(union block_sequence_params *params);
+
+void hwss_hubp_setup_interdependent(union block_sequence_params *params);
+
+void hwss_dpp_set_cursor_matrix(union block_sequence_params *params);
+
+void hwss_mpc_update_mpcc(union block_sequence_params *params);
+
+void hwss_mpc_update_blending(union block_sequence_params *params);
+
+void hwss_mpc_assert_idle_mpcc(union block_sequence_params *params);
+
+void hwss_mpc_insert_plane(union block_sequence_params *params);
+
+void hwss_dpp_set_scaler(union block_sequence_params *params);
+
+void hwss_hubp_mem_program_viewport(union block_sequence_params *params);
+
+void hwss_abort_cursor_offload_update(union block_sequence_params *params);
+
+void hwss_set_cursor_attribute(union block_sequence_params *params);
+
+void hwss_set_cursor_position(union block_sequence_params *params);
+
+void hwss_set_cursor_sdr_white_level(union block_sequence_params *params);
+
+void hwss_program_output_csc(union block_sequence_params *params);
+
+void hwss_hubp_set_legacy_tiling_compat_level(union block_sequence_params *params);
+
+void hwss_hubp_set_blank(union block_sequence_params *params);
+
+void hwss_phantom_hubp_post_enable(union block_sequence_params *params);
+
+void hwss_add_optc_pipe_control_lock(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx, bool lock);
+
+void hwss_add_hubp_set_flip_control_gsl(struct block_sequence_state *seq_state,
+ struct hubp *hubp, bool flip_immediate);
+
+void hwss_add_hubp_program_triplebuffer(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx, bool enableTripleBuffer);
+
+void hwss_add_hubp_update_plane_addr(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx);
+
+void hwss_add_dpp_set_input_transfer_func(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_plane_state *plane_state);
+
+void hwss_add_dpp_program_gamut_remap(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_dpp_program_bias_and_scale(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_optc_program_manual_trigger(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_dpp_set_output_transfer_func(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx, struct dc_stream_state *stream);
+
+void hwss_add_mpc_update_visual_confirm(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx, int mpcc_id);
+
+void hwss_add_mpc_power_on_mpc_mem_pwr(struct block_sequence_state *seq_state,
+ struct mpc *mpc, int mpcc_id, bool power_on);
+
+void hwss_add_mpc_set_output_csc(struct block_sequence_state *seq_state,
+ struct mpc *mpc, int opp_id, const uint16_t *regval, enum mpc_output_csc_mode ocsc_mode);
+
+void hwss_add_mpc_set_ocsc_default(struct block_sequence_state *seq_state,
+ struct mpc *mpc, int opp_id, enum dc_color_space colorspace, enum mpc_output_csc_mode ocsc_mode);
+
+void hwss_add_dmub_send_dmcub_cmd(struct block_sequence_state *seq_state,
+ struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type);
+
+void hwss_add_dmub_subvp_save_surf_addr(struct block_sequence_state *seq_state,
+ struct dc_dmub_srv *dc_dmub_srv, struct dc_plane_address *addr, uint8_t subvp_index);
+
+void hwss_add_hubp_wait_for_dcc_meta_prop(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *top_pipe_to_program);
+
+void hwss_add_hubp_wait_pipe_read_start(struct block_sequence_state *seq_state,
+ struct hubp *hubp);
+
+void hwss_add_hws_apply_update_flags_for_phantom(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_hws_update_phantom_vp_position(struct block_sequence_state *seq_state,
+ struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx);
+
+void hwss_add_optc_set_odm_combine(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, int opp_inst[MAX_PIPES], int opp_head_count,
+ int odm_slice_width, int last_odm_slice_width);
+
+void hwss_add_optc_set_odm_bypass(struct block_sequence_state *seq_state,
+ struct timing_generator *optc, struct dc_crtc_timing *timing);
+
+void hwss_add_tg_program_global_sync(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ int vready_offset,
+ unsigned int vstartup_lines,
+ unsigned int vupdate_offset_pixels,
+ unsigned int vupdate_vupdate_width_pixels,
+ unsigned int pstate_keepout_start_lines);
+
+void hwss_add_tg_wait_for_state(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, enum crtc_state state);
+
+void hwss_add_tg_set_vtg_params(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, struct dc_crtc_timing *dc_crtc_timing, bool program_fp2);
+
+void hwss_add_tg_setup_vertical_interrupt2(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, int start_line);
+
+void hwss_add_dpp_set_hdr_multiplier(struct block_sequence_state *seq_state,
+ struct dpp *dpp, uint32_t hw_mult);
+
+void hwss_add_hubp_program_det_size(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub, unsigned int hubp_inst, unsigned int det_buffer_size_kb);
+
+void hwss_add_hubp_program_mcache_id(struct block_sequence_state *seq_state,
+ struct hubp *hubp, struct dml2_hubp_pipe_mcache_regs *mcache_regs);
+
+void hwss_add_hubbub_force_pstate_change_control(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub, bool enable, bool wait);
+
+void hwss_add_hubp_program_det_segments(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub, unsigned int hubp_inst, unsigned int det_size);
+
+void hwss_add_opp_set_dyn_expansion(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp, enum dc_color_space color_sp,
+ enum dc_color_depth color_dpth, enum signal_type signal);
+
+void hwss_add_opp_program_fmt(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp, struct bit_depth_reduction_params *fmt_bit_depth,
+ struct clamping_and_pixel_encoding_params *clamping);
+
+void hwss_add_abm_set_pipe(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx);
+
+void hwss_add_abm_set_level(struct block_sequence_state *seq_state,
+ struct abm *abm, uint32_t abm_level);
+
+void hwss_add_tg_enable_crtc(struct block_sequence_state *seq_state,
+ struct timing_generator *tg);
+
+void hwss_add_hubp_wait_flip_pending(struct block_sequence_state *seq_state,
+ struct hubp *hubp, unsigned int timeout_us, unsigned int polling_interval_us);
+
+void hwss_add_tg_wait_double_buffer_pending(struct block_sequence_state *seq_state,
+ struct timing_generator *tg, unsigned int timeout_us, unsigned int polling_interval_us);
+
+void hwss_add_dccg_set_dto_dscclk(struct block_sequence_state *seq_state,
+ struct dccg *dccg, int inst, int num_slices_h);
+
+void hwss_add_dsc_calculate_and_set_config(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx, bool enable, int opp_cnt);
+
+void hwss_add_mpc_remove_mpcc(struct block_sequence_state *seq_state,
+ struct mpc *mpc, struct mpc_tree *mpc_tree_params, struct mpcc *mpcc_to_remove);
+
+void hwss_add_opp_set_mpcc_disconnect_pending(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp, int mpcc_inst, bool pending);
+
+void hwss_add_hubp_disconnect(struct block_sequence_state *seq_state,
+ struct hubp *hubp);
+
+void hwss_add_dsc_enable_with_opp(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_dsc_disconnect(struct block_sequence_state *seq_state,
+ struct display_stream_compressor *dsc);
+
+void hwss_add_dc_set_optimized_required(struct block_sequence_state *seq_state,
+ struct dc *dc, bool optimized_required);
+
+void hwss_add_abm_set_immediate_disable(struct block_sequence_state *seq_state,
+ struct dc *dc, struct pipe_ctx *pipe_ctx);
+
+void hwss_add_opp_set_disp_pattern_generator(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ enum controller_dp_test_pattern test_pattern,
+ enum controller_dp_color_space color_space,
+ enum dc_color_depth color_depth,
+ struct tg_color solid_color,
+ bool use_solid_color,
+ int width,
+ int height,
+ int offset);
+
+void hwss_add_opp_program_bit_depth_reduction(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ bool use_default_params,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_dc_ip_request_cntl(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ bool enable);
+
+void hwss_add_dwbc_update(struct block_sequence_state *seq_state,
+ struct dwbc *dwb,
+ struct dc_dwb_params *dwb_params);
+
+void hwss_add_mcif_wb_config_buf(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb,
+ struct mcif_buf_params *mcif_buf_params,
+ unsigned int dest_height);
+
+void hwss_add_mcif_wb_config_arb(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb,
+ struct mcif_arb_params *mcif_arb_params);
+
+void hwss_add_mcif_wb_enable(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb);
+
+void hwss_add_mcif_wb_disable(struct block_sequence_state *seq_state,
+ struct mcif_wb *mcif_wb);
+
+void hwss_add_mpc_set_dwb_mux(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ int dwb_id,
+ int mpcc_id);
+
+void hwss_add_mpc_disable_dwb_mux(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ unsigned int dwb_id);
+
+void hwss_add_dwbc_enable(struct block_sequence_state *seq_state,
+ struct dwbc *dwb,
+ struct dc_dwb_params *dwb_params);
+
+void hwss_add_dwbc_disable(struct block_sequence_state *seq_state,
+ struct dwbc *dwb);
+
+void hwss_add_tg_set_gsl(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ struct gsl_params gsl);
+
+void hwss_add_tg_set_gsl_source_select(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ int group_idx,
+ uint32_t gsl_ready_signal);
+
+void hwss_add_hubp_update_mall_sel(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ uint32_t mall_sel,
+ bool cache_cursor);
+
+void hwss_add_hubp_prepare_subvp_buffering(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool enable);
+
+void hwss_add_hubp_set_blank_en(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool enable);
+
+void hwss_add_hubp_disable_control(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool disable);
+
+void hwss_add_hubbub_soft_reset(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ void (*hubbub_soft_reset)(struct hubbub *hubbub, bool reset),
+ bool reset);
+
+void hwss_add_hubp_clk_cntl(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool enable);
+
+void hwss_add_dpp_dppclk_control(struct block_sequence_state *seq_state,
+ struct dpp *dpp,
+ bool dppclk_div,
+ bool enable);
+
+void hwss_add_disable_phantom_crtc(struct block_sequence_state *seq_state,
+ struct timing_generator *tg);
+
+void hwss_add_dsc_pg_status(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ int dsc_inst,
+ bool is_ungated);
+
+void hwss_add_dsc_wait_disconnect_pending_clear(struct block_sequence_state *seq_state,
+ struct display_stream_compressor *dsc,
+ bool *is_ungated);
+
+void hwss_add_dsc_disable(struct block_sequence_state *seq_state,
+ struct display_stream_compressor *dsc,
+ bool *is_ungated);
+
+void hwss_add_dccg_set_ref_dscclk(struct block_sequence_state *seq_state,
+ struct dccg *dccg,
+ int dsc_inst,
+ bool *is_ungated);
+
+void hwss_add_dpp_root_clock_control(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ unsigned int dpp_inst,
+ bool clock_on);
+
+void hwss_add_dpp_pg_control(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ unsigned int dpp_inst,
+ bool power_on);
+
+void hwss_add_hubp_pg_control(struct block_sequence_state *seq_state,
+ struct dce_hwseq *hws,
+ unsigned int hubp_inst,
+ bool power_on);
+
+void hwss_add_hubp_set_blank(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool blank);
+
+void hwss_add_hubp_init(struct block_sequence_state *seq_state,
+ struct hubp *hubp);
+
+void hwss_add_hubp_reset(struct block_sequence_state *seq_state,
+ struct hubp *hubp);
+
+void hwss_add_dpp_reset(struct block_sequence_state *seq_state,
+ struct dpp *dpp);
+
+void hwss_add_opp_pipe_clock_control(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ bool enable);
+
+void hwss_add_hubp_set_vm_system_aperture_settings(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ uint64_t sys_default,
+ uint64_t sys_low,
+ uint64_t sys_high);
+
+void hwss_add_hubp_set_flip_int(struct block_sequence_state *seq_state,
+ struct hubp *hubp);
+
+void hwss_add_dccg_update_dpp_dto(struct block_sequence_state *seq_state,
+ struct dccg *dccg,
+ int dpp_inst,
+ int dppclk_khz);
+
+void hwss_add_hubp_vtg_sel(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ uint32_t otg_inst);
+
+void hwss_add_hubp_setup2(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct dml2_dchub_per_pipe_register_set *hubp_regs,
+ union dml2_global_sync_programming *global_sync,
+ struct dc_crtc_timing *timing);
+
+void hwss_add_hubp_setup(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs,
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs,
+ struct _vcs_dpi_display_rq_regs_st *rq_regs,
+ struct _vcs_dpi_display_pipe_dest_params_st *pipe_dest);
+
+void hwss_add_hubp_set_unbounded_requesting(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ bool unbounded_req);
+
+void hwss_add_hubp_setup_interdependent2(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct dml2_dchub_per_pipe_register_set *hubp_regs);
+
+void hwss_add_hubp_setup_interdependent(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ struct _vcs_dpi_display_dlg_regs_st *dlg_regs,
+ struct _vcs_dpi_display_ttu_regs_st *ttu_regs);
+void hwss_add_hubp_program_surface_config(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ enum surface_pixel_format format,
+ struct dc_tiling_info *tiling_info,
+ struct plane_size plane_size,
+ enum dc_rotation_angle rotation,
+ struct dc_plane_dcc_param *dcc,
+ bool horizontal_mirror,
+ int compat_level);
+
+void hwss_add_dpp_setup_dpp(struct block_sequence_state *seq_state,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_dpp_set_cursor_matrix(struct block_sequence_state *seq_state,
+ struct dpp *dpp,
+ enum dc_color_space color_space,
+ struct dc_csc_transform *cursor_csc_color_matrix);
+
+void hwss_add_mpc_update_blending(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ struct mpcc_blnd_cfg blnd_cfg,
+ int mpcc_id);
+
+void hwss_add_mpc_assert_idle_mpcc(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ int mpcc_id);
+
+void hwss_add_mpc_insert_plane(struct block_sequence_state *seq_state,
+ struct mpc *mpc,
+ struct mpc_tree *mpc_tree_params,
+ struct mpcc_blnd_cfg blnd_cfg,
+ struct mpcc_sm_cfg *sm_cfg,
+ struct mpcc *insert_above_mpcc,
+ int dpp_id,
+ int mpcc_id);
+
+void hwss_add_dpp_set_scaler(struct block_sequence_state *seq_state,
+ struct dpp *dpp,
+ const struct scaler_data *scl_data);
+
+void hwss_add_hubp_mem_program_viewport(struct block_sequence_state *seq_state,
+ struct hubp *hubp,
+ const struct rect *viewport,
+ const struct rect *viewport_c);
+
+void hwss_add_abort_cursor_offload_update(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_set_cursor_attribute(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_set_cursor_position(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_set_cursor_sdr_white_level(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx);
+
+void hwss_add_program_output_csc(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ enum dc_color_space colorspace,
+ uint16_t *matrix,
+ int opp_id);
+
+void hwss_add_phantom_hubp_post_enable(struct block_sequence_state *seq_state,
+ struct hubp *hubp);
+
+void hwss_add_update_force_pstate(struct block_sequence_state *seq_state,
+ struct dc *dc,
+ struct dc_state *context);
+
+void hwss_add_hubbub_apply_dedcn21_147_wa(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub);
+
+void hwss_add_hubbub_allow_self_refresh_control(struct block_sequence_state *seq_state,
+ struct hubbub *hubbub,
+ bool allow,
+ bool *disallow_self_refresh_applied);
+
+void hwss_add_tg_get_frame_count(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ unsigned int *frame_count);
+
+void hwss_add_tg_set_dsc_config(struct block_sequence_state *seq_state,
+ struct timing_generator *tg,
+ struct dsc_optc_config *dsc_optc_cfg,
+ bool enable);
+
+void hwss_add_opp_program_left_edge_extra_pixel(struct block_sequence_state *seq_state,
+ struct output_pixel_processor *opp,
+ enum dc_pixel_encoding pixel_encoding,
+ bool is_otg_master);
+
#endif /* __DC_HW_SEQUENCER_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h
index 1e2d247fbbac..406db231bc72 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h
+++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h
@@ -27,6 +27,7 @@
#define __DC_HW_SEQUENCER_PRIVATE_H__
#include "dc_types.h"
+#include "hw_sequencer.h"
enum pipe_gating_control {
PIPE_GATING_CONTROL_DISABLE = 0,
@@ -80,7 +81,13 @@ struct hwseq_private_funcs {
void (*plane_atomic_disconnect)(struct dc *dc,
struct dc_state *state,
struct pipe_ctx *pipe_ctx);
+ void (*plane_atomic_disconnect_sequence)(struct dc *dc,
+ struct dc_state *state,
+ struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
void (*update_mpcc)(struct dc *dc, struct pipe_ctx *pipe_ctx);
+ void (*update_mpcc_sequence)(struct dc *dc, struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
bool (*set_input_transfer_func)(struct dc *dc,
struct pipe_ctx *pipe_ctx,
const struct dc_plane_state *plane_state);
@@ -97,6 +104,10 @@ struct hwseq_private_funcs {
void (*blank_pixel_data)(struct dc *dc,
struct pipe_ctx *pipe_ctx,
bool blank);
+ void (*blank_pixel_data_sequence)(struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ bool blank,
+ struct block_sequence_state *seq_state);
enum dc_status (*enable_stream_timing)(
struct pipe_ctx *pipe_ctx,
struct dc_state *context,
@@ -105,6 +116,8 @@ struct hwseq_private_funcs {
bool enable);
void (*setup_vupdate_interrupt)(struct dc *dc,
struct pipe_ctx *pipe_ctx);
+ void (*setup_vupdate_interrupt_sequence)(struct dc *dc, struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
bool (*did_underflow_occur)(struct dc *dc, struct pipe_ctx *pipe_ctx);
void (*init_blank)(struct dc *dc, struct timing_generator *tg);
void (*disable_vga)(struct dce_hwseq *hws);
@@ -112,6 +125,10 @@ struct hwseq_private_funcs {
void (*plane_atomic_power_down)(struct dc *dc,
struct dpp *dpp,
struct hubp *hubp);
+ void (*plane_atomic_power_down_sequence)(struct dc *dc,
+ struct dpp *dpp,
+ struct hubp *hubp,
+ struct block_sequence_state *seq_state);
void (*plane_atomic_disable)(struct dc *dc, struct pipe_ctx *pipe_ctx);
void (*enable_power_gating_plane)(struct dce_hwseq *hws,
bool enable);
@@ -140,15 +157,31 @@ struct hwseq_private_funcs {
unsigned int dsc_inst);
void (*update_odm)(struct dc *dc, struct dc_state *context,
struct pipe_ctx *pipe_ctx);
+ void (*update_odm_sequence)(struct dc *dc, struct dc_state *context,
+ struct pipe_ctx *pipe_ctx, struct block_sequence_state *seq_state);
void (*program_all_writeback_pipes_in_tree)(struct dc *dc,
const struct dc_stream_state *stream,
struct dc_state *context);
+ void (*program_all_writeback_pipes_in_tree_sequence)(
+ struct dc *dc,
+ const struct dc_stream_state *stream,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
bool (*s0i3_golden_init_wa)(struct dc *dc);
void (*set_hdr_multiplier)(struct pipe_ctx *pipe_ctx);
+ void (*set_hdr_multiplier_sequence)(struct pipe_ctx *pipe_ctx,
+ struct block_sequence_state *seq_state);
void (*verify_allow_pstate_change_high)(struct dc *dc);
+ void (*verify_allow_pstate_change_high_sequence)(struct dc *dc,
+ struct block_sequence_state *seq_state);
void (*program_pipe)(struct dc *dc,
struct pipe_ctx *pipe_ctx,
struct dc_state *context);
+ void (*program_pipe_sequence)(
+ struct dc *dc,
+ struct pipe_ctx *pipe_ctx,
+ struct dc_state *context,
+ struct block_sequence_state *seq_state);
bool (*wait_for_blank_complete)(struct output_pixel_processor *opp);
void (*dccg_init)(struct dce_hwseq *hws);
bool (*set_blend_lut)(struct pipe_ctx *pipe_ctx,
@@ -163,6 +196,8 @@ struct hwseq_private_funcs {
void (*enable_plane)(struct dc *dc, struct pipe_ctx *pipe_ctx,
struct dc_state *context);
void (*program_mall_pipe_config)(struct dc *dc, struct dc_state *context);
+ void (*program_mall_pipe_config_sequence)(struct dc *dc, struct dc_state *context,
+ struct block_sequence_state *seq_state);
void (*update_force_pstate)(struct dc *dc, struct dc_state *context);
void (*update_mall_sel)(struct dc *dc, struct dc_state *context);
unsigned int (*calculate_dccg_k1_k2_values)(struct pipe_ctx *pipe_ctx,
@@ -186,6 +221,7 @@ struct hwseq_private_funcs {
void (*perform_3dlut_wa_unlock)(struct pipe_ctx *pipe_ctx);
void (*wait_for_pipe_update_if_needed)(struct dc *dc, struct pipe_ctx *pipe_ctx, bool is_surface_update_only);
void (*set_wait_for_update_needed_for_pipe)(struct dc *dc, struct pipe_ctx *pipe_ctx);
+ void (*dc_ip_request_cntl)(struct dc *dc, bool enable);
};
struct dce_hwseq {
diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h
index d11893f8c916..5ed2cd344804 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h
@@ -58,8 +58,8 @@
#include "transform.h"
#include "dpp.h"
-#include "dml2/dml21/inc/dml_top_dchub_registers.h"
-#include "dml2/dml21/inc/dml_top_types.h"
+#include "dml2_0/dml21/inc/dml_top_dchub_registers.h"
+#include "dml2_0/dml21/inc/dml_top_types.h"
struct resource_pool;
struct dc_state;
@@ -274,7 +274,7 @@ struct resource_pool {
/* An array for accessing the link encoder objects that have been created.
* Index in array corresponds to engine ID - viz. 0: ENGINE_ID_DIGA
*/
- struct link_encoder *link_encoders[MAX_DIG_LINK_ENCODERS];
+ struct link_encoder *link_encoders[MAX_LINK_ENCODERS];
/* Number of DIG link encoder objects created - i.e. number of valid
* entries in link_encoders array.
*/
@@ -514,7 +514,7 @@ struct pipe_ctx {
struct link_enc_cfg_context {
enum link_enc_cfg_mode mode;
struct link_enc_assignment link_enc_assignments[MAX_PIPES];
- enum engine_id link_enc_avail[MAX_DIG_LINK_ENCODERS];
+ enum engine_id link_enc_avail[MAX_LINK_ENCODERS];
struct link_enc_assignment transient_assignments[MAX_PIPES];
};
@@ -526,8 +526,8 @@ struct resource_context {
uint8_t dp_clock_source_ref_count;
bool is_dsc_acquired[MAX_PIPES];
struct link_enc_cfg_context link_enc_cfg_ctx;
- unsigned int dio_link_enc_to_link_idx[MAX_DIG_LINK_ENCODERS];
- int dio_link_enc_ref_cnts[MAX_DIG_LINK_ENCODERS];
+ unsigned int dio_link_enc_to_link_idx[MAX_LINK_ENCODERS];
+ int dio_link_enc_ref_cnts[MAX_LINK_ENCODERS];
bool is_hpo_dp_stream_enc_acquired[MAX_HPO_DP2_ENCODERS];
unsigned int hpo_dp_link_enc_to_link_idx[MAX_HPO_DP2_LINK_ENCODERS];
int hpo_dp_link_enc_ref_cnts[MAX_HPO_DP2_LINK_ENCODERS];
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/cursor_reg_cache.h b/drivers/gpu/drm/amd/display/dc/inc/hw/cursor_reg_cache.h
index 45645f9fd86c..7ce2f417f86a 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/cursor_reg_cache.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/cursor_reg_cache.h
@@ -57,9 +57,9 @@ struct cursor_attribute_cache_hubp {
} size;
union reg_cursor_settings_cfg {
struct {
- uint32_t dst_y_offset: 8;
- uint32_t chunk_hdl_adjust: 2;
- uint32_t reserved: 22;
+ uint32_t dst_y_offset: 8;
+ uint32_t chunk_hdl_adjust: 2;
+ uint32_t reserved: 22;
} bits;
uint32_t raw;
} settings;
@@ -83,12 +83,34 @@ union reg_cur0_control_cfg {
} bits;
uint32_t raw;
};
+
struct cursor_position_cache_dpp {
union reg_cur0_control_cfg cur0_ctl;
};
struct cursor_attribute_cache_dpp {
union reg_cur0_control_cfg cur0_ctl;
+ union reg_cur0_fp_scale_bias {
+ struct {
+ uint32_t fp_bias: 16;
+ uint32_t fp_scale: 16;
+ } bits;
+ uint32_t raw;
+ } fp_scale_bias;
+ union reg_cur0_fp_scale_bias_g_y {
+ struct {
+ uint32_t fp_bias_g_y: 16;
+ uint32_t fp_scale_g_y: 16;
+ } bits;
+ uint32_t raw;
+ } fp_scale_bias_g_y;
+ union reg_cur0_fp_scale_bias_rb_crcb {
+ struct {
+ uint32_t fp_bias_rb_crcb: 16;
+ uint32_t fp_scale_rb_crcb: 16;
+ } bits;
+ uint32_t raw;
+ } fp_scale_bias_rb_crcb;
};
struct cursor_attributes_cfg {
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h
index 61c4d2a7db1c..500a601e99b5 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h
@@ -71,6 +71,125 @@ enum pixel_rate_div {
PIXEL_RATE_DIV_NA = 0xF
};
+struct dcn_dccg_reg_state {
+ uint32_t dc_mem_global_pwr_req_cntl;
+ uint32_t dccg_audio_dtbclk_dto_modulo;
+ uint32_t dccg_audio_dtbclk_dto_phase;
+ uint32_t dccg_audio_dto_source;
+ uint32_t dccg_audio_dto0_module;
+ uint32_t dccg_audio_dto0_phase;
+ uint32_t dccg_audio_dto1_module;
+ uint32_t dccg_audio_dto1_phase;
+ uint32_t dccg_cac_status;
+ uint32_t dccg_cac_status2;
+ uint32_t dccg_disp_cntl_reg;
+ uint32_t dccg_ds_cntl;
+ uint32_t dccg_ds_dto_incr;
+ uint32_t dccg_ds_dto_modulo;
+ uint32_t dccg_ds_hw_cal_interval;
+ uint32_t dccg_gate_disable_cntl;
+ uint32_t dccg_gate_disable_cntl2;
+ uint32_t dccg_gate_disable_cntl3;
+ uint32_t dccg_gate_disable_cntl4;
+ uint32_t dccg_gate_disable_cntl5;
+ uint32_t dccg_gate_disable_cntl6;
+ uint32_t dccg_global_fgcg_rep_cntl;
+ uint32_t dccg_gtc_cntl;
+ uint32_t dccg_gtc_current;
+ uint32_t dccg_gtc_dto_incr;
+ uint32_t dccg_gtc_dto_modulo;
+ uint32_t dccg_perfmon_cntl;
+ uint32_t dccg_perfmon_cntl2;
+ uint32_t dccg_soft_reset;
+ uint32_t dccg_test_clk_sel;
+ uint32_t dccg_vsync_cnt_ctrl;
+ uint32_t dccg_vsync_cnt_int_ctrl;
+ uint32_t dccg_vsync_otg0_latch_value;
+ uint32_t dccg_vsync_otg1_latch_value;
+ uint32_t dccg_vsync_otg2_latch_value;
+ uint32_t dccg_vsync_otg3_latch_value;
+ uint32_t dccg_vsync_otg4_latch_value;
+ uint32_t dccg_vsync_otg5_latch_value;
+ uint32_t dispclk_cgtt_blk_ctrl_reg;
+ uint32_t dispclk_freq_change_cntl;
+ uint32_t dp_dto_dbuf_en;
+ uint32_t dp_dto0_modulo;
+ uint32_t dp_dto0_phase;
+ uint32_t dp_dto1_modulo;
+ uint32_t dp_dto1_phase;
+ uint32_t dp_dto2_modulo;
+ uint32_t dp_dto2_phase;
+ uint32_t dp_dto3_modulo;
+ uint32_t dp_dto3_phase;
+ uint32_t dpiaclk_540m_dto_modulo;
+ uint32_t dpiaclk_540m_dto_phase;
+ uint32_t dpiaclk_810m_dto_modulo;
+ uint32_t dpiaclk_810m_dto_phase;
+ uint32_t dpiaclk_dto_cntl;
+ uint32_t dpiasymclk_cntl;
+ uint32_t dppclk_cgtt_blk_ctrl_reg;
+ uint32_t dppclk_ctrl;
+ uint32_t dppclk_dto_ctrl;
+ uint32_t dppclk0_dto_param;
+ uint32_t dppclk1_dto_param;
+ uint32_t dppclk2_dto_param;
+ uint32_t dppclk3_dto_param;
+ uint32_t dprefclk_cgtt_blk_ctrl_reg;
+ uint32_t dprefclk_cntl;
+ uint32_t dpstreamclk_cntl;
+ uint32_t dscclk_dto_ctrl;
+ uint32_t dscclk0_dto_param;
+ uint32_t dscclk1_dto_param;
+ uint32_t dscclk2_dto_param;
+ uint32_t dscclk3_dto_param;
+ uint32_t dtbclk_dto_dbuf_en;
+ uint32_t dtbclk_dto0_modulo;
+ uint32_t dtbclk_dto0_phase;
+ uint32_t dtbclk_dto1_modulo;
+ uint32_t dtbclk_dto1_phase;
+ uint32_t dtbclk_dto2_modulo;
+ uint32_t dtbclk_dto2_phase;
+ uint32_t dtbclk_dto3_modulo;
+ uint32_t dtbclk_dto3_phase;
+ uint32_t dtbclk_p_cntl;
+ uint32_t force_symclk_disable;
+ uint32_t hdmicharclk0_clock_cntl;
+ uint32_t hdmistreamclk_cntl;
+ uint32_t hdmistreamclk0_dto_param;
+ uint32_t microsecond_time_base_div;
+ uint32_t millisecond_time_base_div;
+ uint32_t otg_pixel_rate_div;
+ uint32_t otg0_phypll_pixel_rate_cntl;
+ uint32_t otg0_pixel_rate_cntl;
+ uint32_t otg1_phypll_pixel_rate_cntl;
+ uint32_t otg1_pixel_rate_cntl;
+ uint32_t otg2_phypll_pixel_rate_cntl;
+ uint32_t otg2_pixel_rate_cntl;
+ uint32_t otg3_phypll_pixel_rate_cntl;
+ uint32_t otg3_pixel_rate_cntl;
+ uint32_t phyasymclk_clock_cntl;
+ uint32_t phybsymclk_clock_cntl;
+ uint32_t phycsymclk_clock_cntl;
+ uint32_t phydsymclk_clock_cntl;
+ uint32_t phyesymclk_clock_cntl;
+ uint32_t phyplla_pixclk_resync_cntl;
+ uint32_t phypllb_pixclk_resync_cntl;
+ uint32_t phypllc_pixclk_resync_cntl;
+ uint32_t phyplld_pixclk_resync_cntl;
+ uint32_t phyplle_pixclk_resync_cntl;
+ uint32_t refclk_cgtt_blk_ctrl_reg;
+ uint32_t socclk_cgtt_blk_ctrl_reg;
+ uint32_t symclk_cgtt_blk_ctrl_reg;
+ uint32_t symclk_psp_cntl;
+ uint32_t symclk32_le_cntl;
+ uint32_t symclk32_se_cntl;
+ uint32_t symclka_clock_enable;
+ uint32_t symclkb_clock_enable;
+ uint32_t symclkc_clock_enable;
+ uint32_t symclkd_clock_enable;
+ uint32_t symclke_clock_enable;
+};
+
struct dccg {
struct dc_context *ctx;
const struct dccg_funcs *funcs;
@@ -81,7 +200,6 @@ struct dccg {
//int audio_dtbclk_khz;/* TODO needs to be removed */
//int ref_dtbclk_khz;/* TODO needs to be removed */
};
-
struct dtbclk_dto_params {
const struct dc_crtc_timing *timing;
int otg_inst;
@@ -214,6 +332,7 @@ struct dccg_funcs {
void (*set_dto_dscclk)(struct dccg *dccg, uint32_t dsc_inst, uint32_t num_slices_h);
void (*set_ref_dscclk)(struct dccg *dccg, uint32_t dsc_inst);
void (*dccg_root_gate_disable_control)(struct dccg *dccg, uint32_t pipe_idx, uint32_t disable_clock_gating);
+ void (*dccg_read_reg_state)(struct dccg *dccg, struct dcn_dccg_reg_state *dccg_reg_state);
};
#endif //__DAL_DCCG_H__
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
index 843a18287c83..1ddfa30411c8 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h
@@ -137,6 +137,14 @@ struct dcn_hubbub_state {
uint32_t dram_state_cntl;
};
+struct dcn_hubbub_reg_state {
+ uint32_t det0_ctrl;
+ uint32_t det1_ctrl;
+ uint32_t det2_ctrl;
+ uint32_t det3_ctrl;
+ uint32_t compbuf_ctrl;
+};
+
struct hubbub_system_latencies {
uint32_t max_latency_ns;
uint32_t avg_latency_ns;
@@ -216,6 +224,8 @@ struct hubbub_funcs {
void (*init_watermarks)(struct hubbub *hubbub);
+ void (*hubbub_read_reg_state)(struct hubbub *hubbub, struct dcn_hubbub_reg_state *hubbub_reg_state);
+
/**
* @program_det_size:
*
@@ -242,17 +252,39 @@ struct hubbub_funcs {
void (*program_compbuf_segments)(struct hubbub *hubbub, unsigned compbuf_size_seg, bool safe_to_increase);
void (*wait_for_det_update)(struct hubbub *hubbub, int hubp_inst);
bool (*program_arbiter)(struct hubbub *hubbub, struct dml2_display_arb_regs *arb_regs, bool safe_to_lower);
- void (*get_det_sizes)(struct hubbub *hubbub, uint32_t *curr_det_sizes, uint32_t *target_det_sizes);
- uint32_t (*compbuf_config_error)(struct hubbub *hubbub);
- struct hubbub_perfmon_funcs{
- void (*start_system_latency_measurement)(struct hubbub *hubbub);
- void (*get_system_latency_result)(struct hubbub *hubbub, uint32_t refclk_mhz, struct hubbub_system_latencies *latencies);
- void (*start_in_order_bandwidth_measurement)(struct hubbub *hubbub);
- void (*get_in_order_bandwidth_result)(struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t *bandwidth_mbps);
- void (*start_urgent_ramp_latency_measurement)(struct hubbub *hubbub, const struct hubbub_urgent_latency_params *params);
- void (*get_urgent_ramp_latency_result)(struct hubbub *hubbub, uint32_t refclk_mhz, uint32_t *latency_ns);
+ void (*dchvm_init)(struct hubbub *hubbub);
+
+ struct hubbub_perfmon_funcs {
void (*reset)(struct hubbub *hubbub);
+ void (*start_measuring_max_memory_latency_ns)(
+ struct hubbub *hubbub);
+ uint32_t (*get_max_memory_latency_ns)(struct hubbub *hubbub,
+ uint32_t refclk_mhz, uint32_t *sample_count);
+ void (*start_measuring_average_memory_latency_ns)(
+ struct hubbub *hubbub);
+ uint32_t (*get_average_memory_latency_ns)(struct hubbub *hubbub,
+ uint32_t refclk_mhz, uint32_t *sample_count);
+ void (*start_measuring_urgent_ramp_latency_ns)(
+ struct hubbub *hubbub,
+ const struct hubbub_urgent_latency_params *params);
+ uint32_t (*get_urgent_ramp_latency_ns)(struct hubbub *hubbub,
+ uint32_t refclk_mhz);
+ void (*start_measuring_unbounded_bandwidth_mbps)(
+ struct hubbub *hubbub);
+ uint32_t (*get_unbounded_bandwidth_mbps)(struct hubbub *hubbub,
+ uint32_t refclk_mhz, uint32_t *duration_ns);
+ void (*start_measuring_average_bandwidth_mbps)(
+ struct hubbub *hubbub);
+ uint32_t (*get_average_bandwidth_mbps)(struct hubbub *hubbub,
+ uint32_t refclk_mhz, uint32_t min_duration_ns,
+ uint32_t *duration_ns);
} perfmon;
+
+ struct hubbub_qos_funcs {
+ void (*force_display_nominal_profile)(struct hubbub *hubbub);
+ void (*force_display_urgent_profile)(struct hubbub *hubbub);
+ void (*reset_display_qos_profile)(struct hubbub *hubbub);
+ } qos;
};
struct hubbub {
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h
index 1b7c085dc2cc..d88b57d4f512 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dpp.h
@@ -65,7 +65,6 @@ union defer_reg_writes {
} bits;
uint32_t raw;
};
-
struct dpp {
const struct dpp_funcs *funcs;
struct dc_context *ctx;
@@ -84,6 +83,7 @@ struct dpp {
struct pwl_params shaper_params;
bool cm_bypass_mode;
+ bool cursor_offload;
struct cursor_position_cache_dpp pos;
struct cursor_attribute_cache_dpp att;
@@ -202,6 +202,19 @@ struct dcn_dpp_state {
uint32_t gamcor_mode;
};
+struct dcn_dpp_reg_state {
+ uint32_t recout_start;
+ uint32_t recout_size;
+ uint32_t scl_horz_filter_scale_ratio;
+ uint32_t scl_vert_filter_scale_ratio;
+ uint32_t scl_mode;
+ uint32_t cm_control;
+ uint32_t dpp_control;
+ uint32_t dscl_control;
+ uint32_t obuf_control;
+ uint32_t mpc_size;
+};
+
struct CM_bias_params {
uint32_t cm_bias_cr_r;
uint32_t cm_bias_y_g;
@@ -225,6 +238,8 @@ struct dpp_funcs {
void (*dpp_read_state)(struct dpp *dpp, struct dcn_dpp_state *s);
+ void (*dpp_read_reg_state)(struct dpp *dpp, struct dcn_dpp_reg_state *dpp_reg_state);
+
void (*dpp_reset)(struct dpp *dpp);
void (*dpp_set_scaler)(struct dpp *dpp,
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
index 2b874d2cc61c..a79019365af8 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h
@@ -41,8 +41,8 @@
#include "mem_input.h"
#include "cursor_reg_cache.h"
-#include "dml2/dml21/inc/dml_top_dchub_registers.h"
-#include "dml2/dml21/inc/dml_top_types.h"
+#include "dml2_0/dml21/inc/dml_top_dchub_registers.h"
+#include "dml2_0/dml21/inc/dml_top_types.h"
#define OPP_ID_INVALID 0xf
#define MAX_TTU 0xffffff
@@ -126,11 +126,13 @@ struct hubp {
int mpcc_id;
struct dc_cursor_attributes curs_attr;
struct dc_cursor_position curs_pos;
+ bool cursor_offload;
bool power_gated;
struct cursor_position_cache_hubp pos;
struct cursor_attribute_cache_hubp att;
struct cursor_rect cur_rect;
+ bool use_mall_for_cursor;
};
struct surface_flip_registers {
@@ -236,6 +238,7 @@ struct hubp_funcs {
void (*hubp_clk_cntl)(struct hubp *hubp, bool enable);
void (*hubp_vtg_sel)(struct hubp *hubp, uint32_t otg_inst);
void (*hubp_read_state)(struct hubp *hubp);
+ void (*hubp_read_reg_state)(struct hubp *hubp, struct dcn_hubp_reg_state *reg_state);
void (*hubp_clear_underflow)(struct hubp *hubp);
void (*hubp_disable_control)(struct hubp *hubp, bool disable_hubp);
unsigned int (*hubp_get_underflow_status)(struct hubp *hubp);
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h
index 41c76ba9ba56..a61d12ec61bc 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hw_shared.h
@@ -44,12 +44,67 @@
*/
#define MAX_PIPES 6
#define MAX_PHANTOM_PIPES (MAX_PIPES / 2)
-#define MAX_LINKS (MAX_PIPES * 2 +2)
+
+#define MAX_DPIA 6
+#define MAX_CONNECTOR 6
+#define MAX_VIRTUAL_LINKS 4
+
+#define MAX_LINKS (MAX_DPIA + MAX_CONNECTOR + MAX_VIRTUAL_LINKS)
+
+/**
+ * define MAX_DIG_LINK_ENCODERS - maximum number of digital encoders
+ *
+ * Digital encoders are ENGINE_ID_DIGA...G, there are at most 7,
+ * although not every GPU may have that many.
+ */
#define MAX_DIG_LINK_ENCODERS 7
+
+/**
+ * define MAX_DAC_LINK_ENCODERS - maximum number of analog link encoders
+ *
+ * Analog encoders are ENGINE_ID_DACA/B, there are at most 2,
+ * although not every GPU may have that many. Modern GPUs typically
+ * don't have analog encoders.
+ */
+#define MAX_DAC_LINK_ENCODERS 2
+
+/**
+ * define MAX_LINK_ENCODERS - maximum number link encoders in total
+ *
+ * This includes both analog and digital encoders.
+ */
+#define MAX_LINK_ENCODERS (MAX_DIG_LINK_ENCODERS + MAX_DAC_LINK_ENCODERS)
+
#define MAX_DWB_PIPES 1
#define MAX_HPO_DP2_ENCODERS 4
#define MAX_HPO_DP2_LINK_ENCODERS 4
+/* Pipe topology snapshot structures */
+#define MAX_TOPOLOGY_SNAPSHOTS 4
+
+struct pipe_topology_line {
+ bool is_phantom_pipe;
+ int plane_idx;
+ int slice_idx;
+ int stream_idx;
+ int dpp_inst;
+ int opp_inst;
+ int tg_inst;
+};
+
+struct pipe_topology_snapshot {
+ struct pipe_topology_line pipe_log_lines[MAX_PIPES];
+ int line_count;
+ uint64_t timestamp_us;
+ int stream_count;
+ int phantom_stream_count;
+};
+
+struct pipe_topology_history {
+ struct pipe_topology_snapshot snapshots[MAX_TOPOLOGY_SNAPSHOTS];
+ int current_snapshot_index;
+};
+
struct gamma_curve {
uint32_t offset;
uint32_t segments_num;
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h
index 08c16ba52a51..df512920a9fa 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/link_encoder.h
@@ -47,6 +47,7 @@ struct encoder_init_data {
enum hpd_source_id hpd_source;
/* TODO: in DAL2, here was pointer to EventManagerInterface */
struct graphics_object_id encoder;
+ enum engine_id analog_engine;
struct dc_context *ctx;
enum transmitter transmitter;
};
@@ -83,6 +84,7 @@ struct link_encoder {
struct graphics_object_id connector;
uint32_t output_signals;
enum engine_id preferred_engine;
+ enum engine_id analog_engine;
struct encoder_feature_support features;
enum transmitter transmitter;
enum hpd_source_id hpd_source;
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h
index 42fbc70f7056..d468bc85566a 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mem_input.h
@@ -29,7 +29,7 @@
#include "include/grph_object_id.h"
#include "dml/display_mode_structs.h"
-#include "dml2/dml21/inc/dml_top_dchub_registers.h"
+#include "dml2_0/dml21/inc/dml_top_dchub_registers.h"
struct dchub_init_data;
struct cstate_pstate_watermarks_st {
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h
index 22960ee03dee..a8d1abe20f62 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/mpc.h
@@ -350,6 +350,15 @@ struct mpcc_state {
struct mpc_rmcm_regs rmcm_regs;
};
+struct dcn_mpc_reg_state {
+ uint32_t mpcc_bot_sel;
+ uint32_t mpcc_control;
+ uint32_t mpcc_status;
+ uint32_t mpcc_top_sel;
+ uint32_t mpcc_opp_id;
+ uint32_t mpcc_ogam_control;
+};
+
/**
* struct mpc_funcs - funcs
*/
@@ -373,6 +382,24 @@ struct mpc_funcs {
struct mpc *mpc,
int mpcc_inst,
struct mpcc_state *s);
+ /**
+ * @mpc_read_reg_state:
+ *
+ * Read MPC register state for debugging underflow purposes.
+ *
+ * Parameters:
+ *
+ * - [in] mpc - MPC context
+ * - [out] reg_state - MPC register state structure
+ *
+ * Return:
+ *
+ * void
+ */
+ void (*mpc_read_reg_state)(
+ struct mpc *mpc,
+ int mpcc_inst,
+ struct dcn_mpc_reg_state *mpc_reg_state);
/**
* @insert_plane:
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/opp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/opp.h
index 747679cb4944..e1428a83ecbc 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/opp.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/opp.h
@@ -297,6 +297,16 @@ struct oppbuf_params {
uint32_t num_segment_padded_pixels;
};
+struct dcn_opp_reg_state {
+ uint32_t dpg_control;
+ uint32_t fmt_control;
+ uint32_t oppbuf_control;
+ uint32_t opp_pipe_control;
+ uint32_t opp_pipe_crc_control;
+ uint32_t opp_abm_control;
+ uint32_t dscrm_dsc_forward_config;
+};
+
struct opp_funcs {
@@ -368,6 +378,9 @@ struct opp_funcs {
struct output_pixel_processor *opp,
enum dc_pixel_encoding pixel_encoding,
bool is_primary);
+
+ void (*opp_read_reg_state)(
+ struct output_pixel_processor *opp, struct dcn_opp_reg_state *opp_reg_state);
};
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
index f2de2cf23859..da7bf59c4b9d 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h
@@ -175,6 +175,135 @@ struct dcn_otg_state {
uint32_t otg_double_buffer_control;
};
+struct dcn_optc_reg_state {
+ uint32_t optc_bytes_per_pixel;
+ uint32_t optc_data_format_control;
+ uint32_t optc_data_source_select;
+ uint32_t optc_input_clock_control;
+ uint32_t optc_input_global_control;
+ uint32_t optc_input_spare_register;
+ uint32_t optc_memory_config;
+ uint32_t optc_rsmu_underflow;
+ uint32_t optc_underflow_threshold;
+ uint32_t optc_width_control;
+
+ uint32_t otg_3d_structure_control;
+ uint32_t otg_clock_control;
+ uint32_t otg_control;
+ uint32_t otg_count_control;
+ uint32_t otg_count_reset;
+ uint32_t otg_crc_cntl;
+ uint32_t otg_crc_sig_blue_control_mask;
+ uint32_t otg_crc_sig_red_green_mask;
+ uint32_t otg_crc0_data_b;
+ uint32_t otg_crc0_data_rg;
+ uint32_t otg_crc0_windowa_x_control;
+ uint32_t otg_crc0_windowa_x_control_readback;
+ uint32_t otg_crc0_windowa_y_control;
+ uint32_t otg_crc0_windowa_y_control_readback;
+ uint32_t otg_crc0_windowb_x_control;
+ uint32_t otg_crc0_windowb_x_control_readback;
+ uint32_t otg_crc0_windowb_y_control;
+ uint32_t otg_crc0_windowb_y_control_readback;
+ uint32_t otg_crc1_data_b;
+ uint32_t otg_crc1_data_rg;
+ uint32_t otg_crc1_windowa_x_control;
+ uint32_t otg_crc1_windowa_x_control_readback;
+ uint32_t otg_crc1_windowa_y_control;
+ uint32_t otg_crc1_windowa_y_control_readback;
+ uint32_t otg_crc1_windowb_x_control;
+ uint32_t otg_crc1_windowb_x_control_readback;
+ uint32_t otg_crc1_windowb_y_control;
+ uint32_t otg_crc1_windowb_y_control_readback;
+ uint32_t otg_crc2_data_b;
+ uint32_t otg_crc2_data_rg;
+ uint32_t otg_crc3_data_b;
+ uint32_t otg_crc3_data_rg;
+ uint32_t otg_dlpc_control;
+ uint32_t otg_double_buffer_control;
+ uint32_t otg_drr_control2;
+ uint32_t otg_drr_control;
+ uint32_t otg_drr_timing_int_status;
+ uint32_t otg_drr_trigger_window;
+ uint32_t otg_drr_v_total_change;
+ uint32_t otg_drr_v_total_reach_range;
+ uint32_t otg_dsc_start_position;
+ uint32_t otg_force_count_now_cntl;
+ uint32_t otg_global_control0;
+ uint32_t otg_global_control1;
+ uint32_t otg_global_control2;
+ uint32_t otg_global_control3;
+ uint32_t otg_global_control4;
+ uint32_t otg_global_sync_status;
+ uint32_t otg_gsl_control;
+ uint32_t otg_gsl_vsync_gap;
+ uint32_t otg_gsl_window_x;
+ uint32_t otg_gsl_window_y;
+ uint32_t otg_h_blank_start_end;
+ uint32_t otg_h_sync_a;
+ uint32_t otg_h_sync_a_cntl;
+ uint32_t otg_h_timing_cntl;
+ uint32_t otg_h_total;
+ uint32_t otg_interlace_control;
+ uint32_t otg_interlace_status;
+ uint32_t otg_interrupt_control;
+ uint32_t otg_long_vblank_status;
+ uint32_t otg_m_const_dto0;
+ uint32_t otg_m_const_dto1;
+ uint32_t otg_manual_force_vsync_next_line;
+ uint32_t otg_master_en;
+ uint32_t otg_master_update_lock;
+ uint32_t otg_master_update_mode;
+ uint32_t otg_nom_vert_position;
+ uint32_t otg_pipe_update_status;
+ uint32_t otg_pixel_data_readback0;
+ uint32_t otg_pixel_data_readback1;
+ uint32_t otg_request_control;
+ uint32_t otg_snapshot_control;
+ uint32_t otg_snapshot_frame;
+ uint32_t otg_snapshot_position;
+ uint32_t otg_snapshot_status;
+ uint32_t otg_spare_register;
+ uint32_t otg_static_screen_control;
+ uint32_t otg_status;
+ uint32_t otg_status_frame_count;
+ uint32_t otg_status_hv_count;
+ uint32_t otg_status_position;
+ uint32_t otg_status_vf_count;
+ uint32_t otg_stereo_control;
+ uint32_t otg_stereo_force_next_eye;
+ uint32_t otg_stereo_status;
+ uint32_t otg_trig_manual_control;
+ uint32_t otg_triga_cntl;
+ uint32_t otg_triga_manual_trig;
+ uint32_t otg_trigb_cntl;
+ uint32_t otg_trigb_manual_trig;
+ uint32_t otg_update_lock;
+ uint32_t otg_v_blank_start_end;
+ uint32_t otg_v_count_stop_control;
+ uint32_t otg_v_count_stop_control2;
+ uint32_t otg_v_sync_a;
+ uint32_t otg_v_sync_a_cntl;
+ uint32_t otg_v_total;
+ uint32_t otg_v_total_control;
+ uint32_t otg_v_total_int_status;
+ uint32_t otg_v_total_max;
+ uint32_t otg_v_total_mid;
+ uint32_t otg_v_total_min;
+ uint32_t otg_vert_sync_control;
+ uint32_t otg_vertical_interrupt0_control;
+ uint32_t otg_vertical_interrupt0_position;
+ uint32_t otg_vertical_interrupt1_control;
+ uint32_t otg_vertical_interrupt1_position;
+ uint32_t otg_vertical_interrupt2_control;
+ uint32_t otg_vertical_interrupt2_position;
+ uint32_t otg_vready_param;
+ uint32_t otg_vstartup_param;
+ uint32_t otg_vsync_nom_int_status;
+ uint32_t otg_vupdate_keepout;
+ uint32_t otg_vupdate_param;
+};
+
/**
* struct timing_generator - Entry point to Output Timing Generator feature.
*/
@@ -381,6 +510,7 @@ struct timing_generator_funcs {
void (*set_vupdate_keepout)(struct timing_generator *tg, bool enable);
bool (*wait_update_lock_status)(struct timing_generator *tg, bool locked);
void (*read_otg_state)(struct timing_generator *tg, struct dcn_otg_state *s);
+ void (*optc_read_reg_state)(struct timing_generator *tg, struct dcn_optc_reg_state *optc_reg_state);
};
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_service.h b/drivers/gpu/drm/amd/display/dc/inc/link_service.h
index 1e34e84160aa..6f94e48a24d1 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/link_service.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/link_service.h
@@ -292,12 +292,12 @@ struct link_service {
enum replay_FW_Message_type msg,
union dmub_replay_cmd_set *cmd_data);
bool (*edp_set_coasting_vtotal)(
- struct dc_link *link, uint32_t coasting_vtotal);
+ struct dc_link *link, uint32_t coasting_vtotal, uint16_t frame_skip_number);
bool (*edp_replay_residency)(const struct dc_link *link,
unsigned int *residency, const bool is_start,
const enum pr_residency_mode mode);
bool (*edp_set_replay_power_opt_and_coasting_vtotal)(struct dc_link *link,
- const unsigned int *power_opts, uint32_t coasting_vtotal);
+ const unsigned int *power_opts, uint32_t coasting_vtotal, uint16_t frame_skip_number);
bool (*edp_wait_for_t12)(struct dc_link *link);
bool (*edp_is_ilr_optimization_required)(struct dc_link *link,
diff --git a/drivers/gpu/drm/amd/display/dc/inc/resource.h b/drivers/gpu/drm/amd/display/dc/inc/resource.h
index 4e26a16a8743..79746d931471 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/resource.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/resource.h
@@ -49,6 +49,7 @@ struct resource_caps {
int num_video_plane;
int num_audio;
int num_stream_encoder;
+ int num_analog_stream_encoder;
int num_pll;
int num_dwb;
int num_ddc;
diff --git a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
index 9e33bf937a69..1045c268672e 100644
--- a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
+++ b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
@@ -78,6 +78,7 @@ static void dp_retrain_link_dp_test(struct dc_link *link,
struct audio_output audio_output[MAX_PIPES];
struct dc_stream_state *streams_on_link[MAX_PIPES];
int num_streams_on_link = 0;
+ struct dc *dc = (struct dc *)link->dc;
needs_divider_update = (link->dc->link_srv->dp_get_encoding_format(link_setting) !=
link->dc->link_srv->dp_get_encoding_format((const struct dc_link_settings *) &link->cur_link_settings));
@@ -150,7 +151,7 @@ static void dp_retrain_link_dp_test(struct dc_link *link,
if (streams_on_link[i] && streams_on_link[i]->link && streams_on_link[i]->link == link) {
stream_update.stream = streams_on_link[i];
stream_update.dpms_off = &dpms_off;
- dc_update_planes_and_stream(state->clk_mgr->ctx->dc, NULL, 0, streams_on_link[i], &stream_update);
+ dc_update_planes_and_stream(dc, NULL, 0, streams_on_link[i], &stream_update);
}
}
}
@@ -876,7 +877,7 @@ bool dp_set_test_pattern(
return false;
if (pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_enable) {
- if (should_use_dmub_lock(pipe_ctx->stream->link)) {
+ if (should_use_dmub_inbox1_lock(pipe_ctx->stream->link->dc, pipe_ctx->stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
@@ -924,7 +925,7 @@ bool dp_set_test_pattern(
CRTC_STATE_VACTIVE);
if (pipe_ctx->stream_res.tg->funcs->lock_doublebuffer_disable) {
- if (should_use_dmub_lock(pipe_ctx->stream->link)) {
+ if (should_use_dmub_inbox1_lock(pipe_ctx->stream->link->dc, pipe_ctx->stream->link)) {
union dmub_hw_lock_flags hw_locks = { 0 };
struct dmub_hw_lock_inst_flags inst_flags = { 0 };
diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c
index 892907991f91..befa67b2b2ae 100644
--- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c
+++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_dio.c
@@ -58,8 +58,9 @@ void setup_dio_stream_encoder(struct pipe_ctx *pipe_ctx)
return;
}
- link_enc->funcs->connect_dig_be_to_fe(link_enc,
- pipe_ctx->stream_res.stream_enc->id, true);
+ if (!dc_is_rgb_signal(pipe_ctx->stream->signal))
+ link_enc->funcs->connect_dig_be_to_fe(link_enc,
+ pipe_ctx->stream_res.stream_enc->id, true);
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(pipe_ctx->stream->link,
DPCD_SOURCE_SEQ_AFTER_CONNECT_DIG_FE_BE);
@@ -98,10 +99,13 @@ void reset_dio_stream_encoder(struct pipe_ctx *pipe_ctx)
if (stream_enc->funcs->enable_stream)
stream_enc->funcs->enable_stream(stream_enc,
pipe_ctx->stream->signal, false);
- link_enc->funcs->connect_dig_be_to_fe(
- link_enc,
- pipe_ctx->stream_res.stream_enc->id,
- false);
+
+ if (!dc_is_rgb_signal(pipe_ctx->stream->signal))
+ link_enc->funcs->connect_dig_be_to_fe(
+ link_enc,
+ pipe_ctx->stream_res.stream_enc->id,
+ false);
+
if (dc_is_dp_signal(pipe_ctx->stream->signal))
pipe_ctx->stream->ctx->dc->link_srv->dp_trace_source_sequence(
pipe_ctx->stream->link,
@@ -115,7 +119,8 @@ void setup_dio_stream_attribute(struct pipe_ctx *pipe_ctx)
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
- if (!dc_is_virtual_signal(stream->signal))
+ if (!dc_is_virtual_signal(stream->signal) &&
+ !dc_is_rgb_signal(stream->signal))
stream_encoder->funcs->setup_stereo_sync(
stream_encoder,
pipe_ctx->stream_res.tg->inst,
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
index 85303167a553..6d31f4967f1a 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -270,6 +270,10 @@ static void read_scdc_caps(struct ddc_service *ddc_service,
uint8_t slave_address = HDMI_SCDC_ADDRESS;
uint8_t offset = HDMI_SCDC_MANUFACTURER_OUI;
+ if (ddc_service->link->local_sink &&
+ !ddc_service->link->local_sink->edid_caps.scdc_present)
+ return;
+
link_query_ddc_data(ddc_service, slave_address, &offset,
sizeof(offset), sink->scdc_caps.manufacturer_OUI.byte,
sizeof(sink->scdc_caps.manufacturer_OUI.byte));
@@ -858,6 +862,94 @@ static void verify_link_capability(struct dc_link *link, struct dc_sink *sink,
verify_link_capability_non_destructive(link);
}
+/**
+ * link_detect_evaluate_edid_header() - Evaluate if an EDID header is acceptable.
+ *
+ * Evaluates an 8-byte EDID header to check if it's good enough
+ * for the purpose of determining whether a display is connected
+ * without reading the full EDID.
+ *
+ * @edid_header: The first 8 bytes of the EDID read from DDC.
+ *
+ * Return: true if the header looks valid (>= 6 of 8 bytes match the
+ * expected 00/FF pattern), false otherwise.
+ */
+static bool link_detect_evaluate_edid_header(uint8_t edid_header[8])
+{
+ int edid_header_score = 0;
+ int i;
+
+ for (i = 0; i < 8; ++i)
+ edid_header_score += edid_header[i] == ((i == 0 || i == 7) ? 0x00 : 0xff);
+
+ return edid_header_score >= 6;
+}
+
+/**
+ * link_detect_ddc_probe() - Probe the DDC to see if a display is connected.
+ *
+ * Detect whether a display is connected to DDC without reading full EDID.
+ * Reads only the EDID header (the first 8 bytes of EDID) from DDC and
+ * evaluates whether that matches.
+ *
+ * @link: DC link whose DDC/I2C is probed for the EDID header.
+ *
+ * Return: true if the EDID header was read and passes validation,
+ * false otherwise.
+ */
+static bool link_detect_ddc_probe(struct dc_link *link)
+{
+ if (!link->ddc)
+ return false;
+
+ uint8_t edid_header[8] = {0};
+ bool ddc_probed = i2c_read(link->ddc, 0x50, edid_header, sizeof(edid_header));
+
+ if (!ddc_probed)
+ return false;
+
+ if (!link_detect_evaluate_edid_header(edid_header))
+ return false;
+
+ return true;
+}
+
+/**
+ * link_detect_dac_load_detect() - Performs DAC load detection.
+ *
+ * Load detection can be used to detect the presence of an
+ * analog display when we can't read DDC. This causes a visible
+ * visual glitch so it should be used sparingly.
+ *
+ * @link: DC link to test using the DAC load-detect path.
+ *
+ * Return: true if the VBIOS load-detect call reports OK, false
+ * otherwise.
+ */
+static bool link_detect_dac_load_detect(struct dc_link *link)
+{
+ struct dc_bios *bios = link->ctx->dc_bios;
+ struct link_encoder *link_enc = link->link_enc;
+ enum engine_id engine_id = link_enc->preferred_engine;
+ enum dal_device_type device_type = DEVICE_TYPE_CRT;
+ enum bp_result bp_result;
+ uint32_t enum_id;
+
+ switch (engine_id) {
+ case ENGINE_ID_DACB:
+ enum_id = 2;
+ break;
+ case ENGINE_ID_DACA:
+ default:
+ engine_id = ENGINE_ID_DACA;
+ enum_id = 1;
+ break;
+ }
+
+ bp_result = bios->funcs->dac_load_detection(bios, engine_id, device_type, enum_id);
+ return bp_result == BP_RESULT_OK;
+}
+
/*
* detect_link_and_local_sink() - Detect if a sink is attached to a given link
*
@@ -942,6 +1034,12 @@ static bool detect_link_and_local_sink(struct dc_link *link,
break;
}
+ case SIGNAL_TYPE_RGB: {
+ sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
+ sink_caps.signal = SIGNAL_TYPE_RGB;
+ break;
+ }
+
case SIGNAL_TYPE_LVDS: {
sink_caps.transaction_type = DDC_TRANSACTION_TYPE_I2C;
sink_caps.signal = SIGNAL_TYPE_LVDS;
@@ -1066,7 +1164,30 @@ static bool detect_link_and_local_sink(struct dc_link *link,
DC_LOG_ERROR("Partial EDID valid, abandon invalid blocks.\n");
break;
case EDID_NO_RESPONSE:
+ /* Analog connectors without EDID:
+ * - old monitor that actually doesn't have EDID
+ * - cheap DVI-A cable or adapter that doesn't connect DDC
+ */
+ if (dc_connector_supports_analog(link->link_id.id)) {
+ /* If we didn't do DAC load detection yet, do it now
+ * to verify there really is a display connected.
+ */
+ if (link->type != dc_connection_dac_load &&
+ !link_detect_dac_load_detect(link)) {
+ if (prev_sink)
+ dc_sink_release(prev_sink);
+ link_disconnect_sink(link);
+ return false;
+ }
+
+ DC_LOG_INFO("%s detected analog display without EDID\n", __func__);
+ link->type = dc_connection_dac_load;
+ sink->edid_caps.analog = true;
+ break;
+ }
+
DC_LOG_ERROR("No EDID read.\n");
+
/*
* Abort detection for non-DP connectors if we have
* no EDID
@@ -1133,14 +1254,23 @@ static bool detect_link_and_local_sink(struct dc_link *link,
sink = prev_sink;
prev_sink = NULL;
}
- query_hdcp_capability(sink->sink_signal, link);
+
+ if (!sink->edid_caps.analog)
+ query_hdcp_capability(sink->sink_signal, link);
}
+ /* DVI-I connector connected to analog display. */
+ if ((link->link_id.id == CONNECTOR_ID_DUAL_LINK_DVII ||
+ link->link_id.id == CONNECTOR_ID_SINGLE_LINK_DVII) &&
+ sink->edid_caps.analog)
+ sink->sink_signal = SIGNAL_TYPE_RGB;
+
/* HDMI-DVI Dongle */
if (sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A &&
!sink->edid_caps.edid_hdmi)
sink->sink_signal = SIGNAL_TYPE_DVI_SINGLE_LINK;
else if (dc_is_dvi_signal(sink->sink_signal) &&
+ dc_is_dvi_signal(link->connector_signal) &&
aud_support->hdmi_audio_native &&
sink->edid_caps.edid_hdmi)
sink->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
@@ -1232,6 +1362,36 @@ static bool detect_link_and_local_sink(struct dc_link *link,
return true;
}
+/**
+ * link_detect_analog() - Determines if an analog sink is connected.
+ *
+ * @link: DC link to evaluate (must support analog signalling).
+ * @type: Updated with the detected connection type:
+ * dc_connection_single (analog via DDC),
+ * dc_connection_dac_load (via load-detect),
+ * or dc_connection_none.
+ *
+ * Return: true if detection completed.
+ */
+static bool link_detect_analog(struct dc_link *link, enum dc_connection_type *type)
+{
+ /* Don't care about connectors that don't support an analog signal. */
+ ASSERT(dc_connector_supports_analog(link->link_id.id));
+
+ if (link_detect_ddc_probe(link)) {
+ *type = dc_connection_single;
+ return true;
+ }
+
+ if (link_detect_dac_load_detect(link)) {
+ *type = dc_connection_dac_load;
+ return true;
+ }
+
+ *type = dc_connection_none;
+ return true;
+}
+
/*
* link_detect_connection_type() - Determine if there is a sink connected
*
@@ -1248,6 +1408,17 @@ bool link_detect_connection_type(struct dc_link *link, enum dc_connection_type *
return true;
}
+ /* Ignore the HPD pin (if any) for analog connectors.
+ * Instead rely on DDC and DAC.
+ *
+ * - VGA connectors don't have any HPD at all.
+ * - Some DVI-A cables don't connect the HPD pin.
+ * - Some DVI-A cables pull up the HPD pin.
+ * (So it's high even when no display is connected.)
+ */
+ if (dc_connector_supports_analog(link->link_id.id))
+ return link_detect_analog(link, type);
+
if (link->connector_signal == SIGNAL_TYPE_EDP) {
/*in case it is not on*/
if (!link->dc->config.edp_no_power_sequencing)
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
index 83419e1a9036..6ae134147617 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
@@ -841,6 +841,7 @@ void link_set_dsc_on_stream(struct pipe_ctx *pipe_ctx, bool enable)
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
ASSERT(dsc_cfg.dc_dsc_cfg.num_slices_h % opp_cnt == 0);
dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
+ dsc_cfg.dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding;
if (should_use_dto_dscclk)
dccg->funcs->set_dto_dscclk(dccg, dsc->inst, dsc_cfg.dc_dsc_cfg.num_slices_h);
@@ -970,6 +971,7 @@ bool link_set_dsc_pps_packet(struct pipe_ctx *pipe_ctx, bool enable, bool immedi
dsc_cfg.color_depth = stream->timing.display_color_depth;
dsc_cfg.is_odm = pipe_ctx->next_odm_pipe ? true : false;
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
+ dsc_cfg.dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding;
dsc->funcs->dsc_get_packed_pps(dsc, &dsc_cfg, &dsc_packed_pps[0]);
memcpy(&stream->dsc_packed_pps[0], &dsc_packed_pps[0], sizeof(stream->dsc_packed_pps));
@@ -2224,7 +2226,11 @@ static enum dc_status enable_link(
{
enum dc_status status = DC_ERROR_UNEXPECTED;
struct dc_stream_state *stream = pipe_ctx->stream;
- struct dc_link *link = stream->link;
+ struct dc_link *link = NULL;
+
+ if (stream == NULL)
+ return DC_ERROR_UNEXPECTED;
+ link = stream->link;
/* There's some scenarios where driver is unloaded with display
* still enabled. When driver is reloaded, it may cause a display
@@ -2256,6 +2262,9 @@ static enum dc_status enable_link(
enable_link_lvds(pipe_ctx);
status = DC_OK;
break;
+ case SIGNAL_TYPE_RGB:
+ status = DC_OK;
+ break;
case SIGNAL_TYPE_VIRTUAL:
status = enable_link_virtual(pipe_ctx);
break;
@@ -2458,6 +2467,7 @@ void link_set_dpms_on(
struct link_encoder *link_enc = pipe_ctx->link_res.dio_link_enc;
enum otg_out_mux_dest otg_out_dest = OUT_MUX_DIO;
struct vpg *vpg = pipe_ctx->stream_res.stream_enc->vpg;
+ const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
bool apply_edp_fast_boot_optimization =
pipe_ctx->stream->apply_edp_fast_boot_optimization;
@@ -2502,6 +2512,8 @@ void link_set_dpms_on(
pipe_ctx->stream_res.tg->funcs->set_out_mux(pipe_ctx->stream_res.tg, otg_out_dest);
}
+ link_hwss->setup_stream_attribute(pipe_ctx);
+
pipe_ctx->stream->apply_edp_fast_boot_optimization = false;
// Enable VPG before building infoframe
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
index 31a73867cd4c..a6e2b0821969 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c
@@ -451,6 +451,46 @@ static enum channel_id get_ddc_line(struct dc_link *link)
return channel;
}
+static enum engine_id find_analog_engine(struct dc_link *link)
+{
+ struct dc_bios *bp = link->ctx->dc_bios;
+ struct graphics_object_id encoder = {0};
+ enum bp_result bp_result = BP_RESULT_OK;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ bp_result = bp->funcs->get_src_obj(bp, link->link_id, i, &encoder);
+
+ if (bp_result != BP_RESULT_OK)
+ return ENGINE_ID_UNKNOWN;
+
+ switch (encoder.id) {
+ case ENCODER_ID_INTERNAL_DAC1:
+ case ENCODER_ID_INTERNAL_KLDSCP_DAC1:
+ return ENGINE_ID_DACA;
+ case ENCODER_ID_INTERNAL_DAC2:
+ case ENCODER_ID_INTERNAL_KLDSCP_DAC2:
+ return ENGINE_ID_DACB;
+ }
+ }
+
+ return ENGINE_ID_UNKNOWN;
+}
+
+static bool transmitter_supported(const enum transmitter transmitter)
+{
+ return transmitter != TRANSMITTER_UNKNOWN &&
+ transmitter != TRANSMITTER_NUTMEG_CRT &&
+ transmitter != TRANSMITTER_TRAVIS_CRT &&
+ transmitter != TRANSMITTER_TRAVIS_LCD;
+}
+
+static bool analog_engine_supported(const enum engine_id engine_id)
+{
+ return engine_id == ENGINE_ID_DACA ||
+ engine_id == ENGINE_ID_DACB;
+}
+
static bool construct_phy(struct dc_link *link,
const struct link_init_data *init_params)
{
@@ -482,10 +522,23 @@ static bool construct_phy(struct dc_link *link,
link->link_id =
bios->funcs->get_connector_id(bios, init_params->connector_index);
+ /* Determine early if the link has any supported encoders,
+ * so that we avoid initializing DDC and HPD, etc.
+ */
+ bp_funcs->get_src_obj(bios, link->link_id, 0, &enc_init_data.encoder);
+ enc_init_data.transmitter = translate_encoder_to_transmitter(enc_init_data.encoder);
+ enc_init_data.analog_engine = find_analog_engine(link);
+
link->ep_type = DISPLAY_ENDPOINT_PHY;
DC_LOG_DC("BIOS object table - link_id: %d", link->link_id.id);
+ if (!transmitter_supported(enc_init_data.transmitter) &&
+ !analog_engine_supported(enc_init_data.analog_engine)) {
+ DC_LOG_WARNING("link_id %d has unsupported encoder\n", link->link_id.id);
+ goto unsupported_fail;
+ }
+
if (bios->funcs->get_disp_connector_caps_info) {
bios->funcs->get_disp_connector_caps_info(bios, link->link_id, &disp_connect_caps_info);
link->is_internal_display = disp_connect_caps_info.INTERNAL_DISPLAY;
@@ -530,6 +583,9 @@ static bool construct_phy(struct dc_link *link,
case CONNECTOR_ID_DUAL_LINK_DVII:
link->connector_signal = SIGNAL_TYPE_DVI_DUAL_LINK;
break;
+ case CONNECTOR_ID_VGA:
+ link->connector_signal = SIGNAL_TYPE_RGB;
+ break;
case CONNECTOR_ID_DISPLAY_PORT:
case CONNECTOR_ID_MXM:
case CONNECTOR_ID_USBC:
@@ -611,16 +667,12 @@ static bool construct_phy(struct dc_link *link,
dal_ddc_get_line(get_ddc_pin(link->ddc));
enc_init_data.ctx = dc_ctx;
- bp_funcs->get_src_obj(dc_ctx->dc_bios, link->link_id, 0,
- &enc_init_data.encoder);
enc_init_data.connector = link->link_id;
enc_init_data.channel = get_ddc_line(link);
enc_init_data.hpd_source = get_hpd_line(link);
link->hpd_src = enc_init_data.hpd_source;
- enc_init_data.transmitter =
- translate_encoder_to_transmitter(enc_init_data.encoder);
link->link_enc =
link->dc->res_pool->funcs->link_enc_create(dc_ctx, &enc_init_data);
@@ -735,6 +787,7 @@ static bool construct_phy(struct dc_link *link,
link->psr_settings.psr_vtotal_control_support = false;
link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
+ link->replay_settings.config.replay_version = DC_REPLAY_VERSION_UNSUPPORTED;
DC_LOG_DC("BIOS object table - %s finished successfully.\n", __func__);
return true;
@@ -753,6 +806,7 @@ create_fail:
link->hpd_gpio = NULL;
}
+unsupported_fail:
DC_LOG_DC("BIOS object table - %s failed.\n", __func__);
return false;
}
@@ -816,9 +870,7 @@ static bool construct_dpia(struct dc_link *link,
/* TODO: Create link encoder */
link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
-
- /* Some docks seem to NAK I2C writes to segment pointer with mot=0. */
- link->wa_flags.dp_mot_reset_segment = true;
+ link->replay_settings.config.replay_version = DC_REPLAY_VERSION_UNSUPPORTED;
return true;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
index 267180e7bc48..5d2bcce2f669 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c
@@ -549,7 +549,8 @@ void write_scdc_data(struct ddc_service *ddc_service,
/*Lower than 340 Scramble bit from SCDC caps*/
if (ddc_service->link->local_sink &&
- ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite)
+ (ddc_service->link->local_sink->edid_caps.panel_patch.skip_scdc_overwrite ||
+ !ddc_service->link->local_sink->edid_caps.scdc_present))
return;
link_query_ddc_data(ddc_service, slave_address, &offset,
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
index b12c11bd6a14..ad90a0106938 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
@@ -357,7 +357,9 @@ bool dp_should_enable_fec(const struct dc_link *link)
{
bool force_disable = false;
- if (link->fec_state == dc_link_fec_enabled)
+ if (link->dc->debug.disable_fec)
+ force_disable = true;
+ else if (link->fec_state == dc_link_fec_enabled)
force_disable = false;
else if (link->connector_signal != SIGNAL_TYPE_DISPLAY_PORT_MST &&
link->local_sink &&
@@ -424,6 +426,21 @@ static enum dc_link_rate get_link_rate_from_max_link_bw(
return link_rate;
}
+static enum dc_lane_count get_lttpr_max_lane_count(struct dc_link *link)
+{
+ enum dc_lane_count lttpr_max_lane_count = LANE_COUNT_UNKNOWN;
+
+ if (link->dpcd_caps.lttpr_caps.max_lane_count <= LANE_COUNT_DP_MAX)
+ lttpr_max_lane_count = link->dpcd_caps.lttpr_caps.max_lane_count;
+
+ /* if bw_allocation is enabled and nrd_max_lane_count is set, use it */
+ if (link->dpia_bw_alloc_config.bw_alloc_enabled &&
+ link->dpia_bw_alloc_config.nrd_max_lane_count > 0)
+ lttpr_max_lane_count = link->dpia_bw_alloc_config.nrd_max_lane_count;
+
+ return lttpr_max_lane_count;
+}
+
static enum dc_link_rate get_lttpr_max_link_rate(struct dc_link *link)
{
@@ -438,6 +455,11 @@ static enum dc_link_rate get_lttpr_max_link_rate(struct dc_link *link)
break;
}
+ /* if bw_allocation is enabled and nrd_max_link_rate is set, use it */
+ if (link->dpia_bw_alloc_config.bw_alloc_enabled &&
+ link->dpia_bw_alloc_config.nrd_max_link_rate > 0)
+ lttpr_max_link_rate = link->dpia_bw_alloc_config.nrd_max_link_rate;
+
if (link->dpcd_caps.lttpr_caps.supported_128b_132b_rates.bits.UHBR20)
lttpr_max_link_rate = LINK_RATE_UHBR20;
else if (link->dpcd_caps.lttpr_caps.supported_128b_132b_rates.bits.UHBR13_5)
@@ -1691,7 +1713,7 @@ static bool retrieve_link_cap(struct dc_link *link)
union edp_configuration_cap edp_config_cap;
union dp_downstream_port_present ds_port = { 0 };
enum dc_status status = DC_ERROR_UNEXPECTED;
- uint32_t read_dpcd_retry_cnt = 3;
+ uint32_t read_dpcd_retry_cnt = 20;
int i;
struct dp_sink_hw_fw_revision dp_hw_fw_revision;
const uint32_t post_oui_delay = 30; // 30ms
@@ -1734,12 +1756,13 @@ static bool retrieve_link_cap(struct dc_link *link)
}
dpcd_set_source_specific_data(link);
- /* Sink may need to configure internals based on vendor, so allow some
- * time before proceeding with possibly vendor specific transactions
- */
- msleep(post_oui_delay);
for (i = 0; i < read_dpcd_retry_cnt; i++) {
+ /*
+ * Sink may need to configure internals based on vendor, so allow some
+ * time before proceeding with possibly vendor specific transactions
+ */
+ msleep(post_oui_delay);
status = core_link_read_dpcd(
link,
DP_DPCD_REV,
@@ -1845,6 +1868,12 @@ static bool retrieve_link_cap(struct dc_link *link)
link->dpcd_caps.is_mst_capable = read_is_mst_supported(link);
DC_LOG_DC("%s: MST_Support: %s\n", __func__, str_yes_no(link->dpcd_caps.is_mst_capable));
+ /* Some MST docks seem to NAK I2C writes to segment pointer with mot=0. */
+ if (link->dpcd_caps.is_mst_capable)
+ link->wa_flags.dp_mot_reset_segment = true;
+ else
+ link->wa_flags.dp_mot_reset_segment = false;
+
get_active_converter_info(ds_port.byte, link);
dp_wa_power_up_0010FA(link, dpcd_data, sizeof(dpcd_data));
@@ -2063,6 +2092,11 @@ static bool retrieve_link_cap(struct dc_link *link)
link->dpcd_caps.max_uncompressed_pixel_rate_cap.raw,
sizeof(link->dpcd_caps.max_uncompressed_pixel_rate_cap.raw));
+ core_link_read_dpcd(link,
+ DP_PANEL_REPLAY_CAPABILITY_SUPPORT,
+ &link->dpcd_caps.pr_caps_supported.raw,
+ sizeof(link->dpcd_caps.pr_caps_supported.raw));
+
/* Read DP tunneling information. */
status = dpcd_get_tunneling_device_data(link);
if (status != DC_OK)
@@ -2241,6 +2275,7 @@ const struct dc_link_settings *dp_get_verified_link_cap(
struct dc_link_settings dp_get_max_link_cap(struct dc_link *link)
{
struct dc_link_settings max_link_cap = {0};
+ enum dc_lane_count lttpr_max_lane_count;
enum dc_link_rate lttpr_max_link_rate;
enum dc_link_rate cable_max_link_rate;
struct resource_context *res_ctx = &link->dc->current_state->res_ctx;
@@ -2305,8 +2340,11 @@ struct dc_link_settings dp_get_max_link_cap(struct dc_link *link)
/* Some LTTPR devices do not report valid DPCD revisions, if so, do not take it's link cap into consideration. */
if (link->dpcd_caps.lttpr_caps.revision.raw >= DPCD_REV_14) {
- if (link->dpcd_caps.lttpr_caps.max_lane_count < max_link_cap.lane_count)
- max_link_cap.lane_count = link->dpcd_caps.lttpr_caps.max_lane_count;
+ lttpr_max_lane_count = get_lttpr_max_lane_count(link);
+
+ if (lttpr_max_lane_count < max_link_cap.lane_count)
+ max_link_cap.lane_count = lttpr_max_lane_count;
+
lttpr_max_link_rate = get_lttpr_max_link_rate(link);
if (lttpr_max_link_rate < max_link_cap.link_rate)
@@ -2412,6 +2450,11 @@ bool dp_verify_link_cap_with_retries(
dp_trace_detect_lt_init(link);
+ DC_LOG_HW_LINK_TRAINING("%s: Link[%d] LinkRate=0x%x LaneCount=%d",
+ __func__, link->link_index,
+ known_limit_link_setting->link_rate,
+ known_limit_link_setting->lane_count);
+
if (link->link_enc && link->link_enc->features.flags.bits.DP_IS_USB_C &&
link->dc->debug.usbc_combo_phy_reset_wa)
apply_usbc_combo_phy_reset_wa(link, known_limit_link_setting);
@@ -2448,6 +2491,11 @@ bool dp_verify_link_cap_with_retries(
dp_trace_lt_fail_count_update(link, fail_count, true);
dp_trace_set_lt_end_timestamp(link, true);
+ DC_LOG_HW_LINK_TRAINING("%s: Link[%d] Exit. is_success=%d fail_count=%d",
+ __func__, link->link_index,
+ success,
+ fail_count);
+
return success;
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
index 8a3c18ae97a7..c958d3f600c8 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_dpia_bw.c
@@ -225,11 +225,6 @@ bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link)
bool ret = false;
uint8_t val;
- if (link->dc->debug.dpia_debug.bits.enable_bw_allocation_mode == false) {
- DC_LOG_DEBUG("%s: link[%d] DPTX BW allocation mode disabled", __func__, link->link_index);
- return false;
- }
-
val = DPTX_BW_ALLOC_MODE_ENABLE | DPTX_BW_ALLOC_UNMASK_IRQ;
if (core_link_write_dpcd(link, DPTX_BW_ALLOCATION_MODE_CONTROL, &val, sizeof(uint8_t)) == DC_OK) {
@@ -273,17 +268,28 @@ bool link_dpia_enable_usb4_dp_bw_alloc_mode(struct dc_link *link)
*/
void link_dp_dpia_handle_bw_alloc_status(struct dc_link *link, uint8_t status)
{
- link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
-
if (status & DP_TUNNELING_BW_REQUEST_SUCCEEDED) {
DC_LOG_DEBUG("%s: BW Allocation request succeeded on link(%d)",
__func__, link->link_index);
- } else if (status & DP_TUNNELING_BW_REQUEST_FAILED) {
+ }
+
+ if (status & DP_TUNNELING_BW_REQUEST_FAILED) {
DC_LOG_DEBUG("%s: BW Allocation request failed on link(%d) allocated/estimated BW=%d",
__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
link_dpia_send_bw_alloc_request(link, link->dpia_bw_alloc_config.estimated_bw);
- } else if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) {
+ }
+
+ if (status & DP_TUNNELING_BW_ALLOC_CAP_CHANGED) {
+ link->dpia_bw_alloc_config.bw_granularity = get_bw_granularity(link);
+
+ DC_LOG_DEBUG("%s: Granularity changed on link(%d) new granularity=%d",
+ __func__, link->link_index, link->dpia_bw_alloc_config.bw_granularity);
+ }
+
+ if (status & DP_TUNNELING_ESTIMATED_BW_CHANGED) {
+ link->dpia_bw_alloc_config.estimated_bw = get_estimated_bw(link);
+
DC_LOG_DEBUG("%s: Estimated BW changed on link(%d) new estimated BW=%d",
__func__, link->link_index, link->dpia_bw_alloc_config.estimated_bw);
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
index 693477413347..4b01ab0a5a7f 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c
@@ -398,10 +398,12 @@ bool dp_should_allow_hpd_rx_irq(const struct dc_link *link)
* Don't handle RX IRQ unless one of following is met:
* 1) The link is established (cur_link_settings != unknown)
* 2) We know we're dealing with a branch device, SST or MST
+ * 3) The link is bw_alloc enabled.
*/
if ((link->cur_link_settings.lane_count != LANE_COUNT_UNKNOWN) ||
- is_dp_branch_device(link))
+ is_dp_branch_device(link) ||
+ link->dpia_bw_alloc_config.bw_alloc_enabled)
return true;
return false;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
index 5e806edbb9f6..c56e69eb27ef 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c
@@ -949,7 +949,7 @@ bool edp_set_replay_allow_active(struct dc_link *link, const bool *allow_active,
/* Set power optimization flag */
if (power_opts && link->replay_settings.replay_power_opt_active != *power_opts) {
if (replay != NULL && link->replay_settings.replay_feature_enabled &&
- replay->funcs->replay_set_power_opt) {
+ replay->funcs->replay_set_power_opt) {
replay->funcs->replay_set_power_opt(replay, *power_opts, panel_inst);
link->replay_settings.replay_power_opt_active = *power_opts;
}
@@ -984,7 +984,117 @@ bool edp_get_replay_state(const struct dc_link *link, uint64_t *state)
return true;
}
-bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream)
+static bool edp_setup_panel_replay(struct dc_link *link, const struct dc_stream_state *stream)
+{
+ /* To-do: Setup Replay */
+ struct dc *dc;
+ struct dmub_replay *replay;
+ int i;
+ unsigned int panel_inst;
+ struct replay_context replay_context = { 0 };
+ unsigned int lineTimeInNs = 0;
+
+ union panel_replay_enable_and_configuration_1 pr_config_1 = { 0 };
+ union panel_replay_enable_and_configuration_2 pr_config_2 = { 0 };
+
+ union dpcd_alpm_configuration alpm_config;
+
+ replay_context.controllerId = CONTROLLER_ID_UNDEFINED;
+
+ if (!link)
+ return false;
+
+ //Clear Panel Replay enable & config
+ dm_helpers_dp_write_dpcd(link->ctx, link,
+ DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1,
+ (uint8_t *)&(pr_config_1.raw), sizeof(uint8_t));
+
+ dm_helpers_dp_write_dpcd(link->ctx, link,
+ DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2,
+ (uint8_t *)&(pr_config_2.raw), sizeof(uint8_t));
+
+ if (!(link->replay_settings.config.replay_supported))
+ return false;
+
+ dc = link->ctx->dc;
+
+ //not sure should keep or not
+ replay = dc->res_pool->replay;
+
+ if (!replay)
+ return false;
+
+ if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst))
+ return false;
+
+ replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel;
+ replay_context.digbe_inst = link->link_enc->transmitter;
+ replay_context.digfe_inst = link->link_enc->preferred_engine;
+
+ for (i = 0; i < MAX_PIPES; i++) {
+ if (dc->current_state->res_ctx.pipe_ctx[i].stream
+ == stream) {
+ /* dmcu -1 for all controller id values,
+ * therefore +1 here
+ */
+ replay_context.controllerId =
+ dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg->inst + 1;
+ break;
+ }
+ }
+
+ lineTimeInNs =
+ ((stream->timing.h_total * 1000000) /
+ (stream->timing.pix_clk_100hz / 10)) + 1;
+
+ replay_context.line_time_in_ns = lineTimeInNs;
+
+ link->replay_settings.replay_feature_enabled =
+ replay->funcs->replay_copy_settings(replay, link, &replay_context, panel_inst);
+
+ if (link->replay_settings.replay_feature_enabled) {
+ pr_config_1.bits.PANEL_REPLAY_ENABLE = 1;
+ pr_config_1.bits.PANEL_REPLAY_CRC_ENABLE = 1;
+ pr_config_1.bits.IRQ_HPD_ASSDP_MISSING = 1;
+ pr_config_1.bits.IRQ_HPD_VSCSDP_UNCORRECTABLE_ERROR = 1;
+ pr_config_1.bits.IRQ_HPD_RFB_ERROR = 1;
+ pr_config_1.bits.IRQ_HPD_ACTIVE_FRAME_CRC_ERROR = 1;
+ pr_config_1.bits.PANEL_REPLAY_SELECTIVE_UPDATE_ENABLE = 1;
+ pr_config_1.bits.PANEL_REPLAY_EARLY_TRANSPORT_ENABLE = 1;
+
+ pr_config_2.bits.SINK_REFRESH_RATE_UNLOCK_GRANTED = 0;
+ pr_config_2.bits.SU_Y_GRANULARITY_EXT_VALUE_ENABLED = 0;
+ pr_config_2.bits.SU_REGION_SCAN_LINE_CAPTURE_INDICATION = 0;
+
+ dm_helpers_dp_write_dpcd(link->ctx, link,
+ DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1,
+ (uint8_t *)&(pr_config_1.raw), sizeof(uint8_t));
+
+ dm_helpers_dp_write_dpcd(link->ctx, link,
+ DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2,
+ (uint8_t *)&(pr_config_2.raw), sizeof(uint8_t));
+
+ //ALPM Setup
+ memset(&alpm_config, 0, sizeof(alpm_config));
+ alpm_config.bits.ENABLE = link->replay_settings.config.alpm_mode != DC_ALPM_UNSUPPORTED ? 1 : 0;
+
+ if (link->replay_settings.config.alpm_mode == DC_ALPM_AUXLESS) {
+ alpm_config.bits.ALPM_MODE_SEL = 1;
+ alpm_config.bits.ACDS_PERIOD_DURATION = 1;
+ }
+
+ dm_helpers_dp_write_dpcd(
+ link->ctx,
+ link,
+ DP_RECEIVER_ALPM_CONFIG,
+ &alpm_config.raw,
+ sizeof(alpm_config.raw));
+ }
+
+ return true;
+}
+
+static bool edp_setup_freesync_replay(struct dc_link *link, const struct dc_stream_state *stream)
{
/* To-do: Setup Replay */
struct dc *dc;
@@ -1080,6 +1190,18 @@ bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream
return true;
}
+bool edp_setup_replay(struct dc_link *link, const struct dc_stream_state *stream)
+{
+ if (!link)
+ return false;
+ if (link->replay_settings.config.replay_version == DC_VESA_PANEL_REPLAY)
+ return edp_setup_panel_replay(link, stream);
+ else if (link->replay_settings.config.replay_version == DC_FREESYNC_REPLAY)
+ return edp_setup_freesync_replay(link, stream);
+ else
+ return false;
+}
+
/*
* This is general Interface for Replay to set an 32 bit variable to dmub
* replay_FW_Message_type: Indicates which instruction or variable pass to DMUB
@@ -1110,7 +1232,7 @@ bool edp_send_replay_cmd(struct dc_link *link,
return true;
}
-bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal)
+bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal, uint16_t frame_skip_number)
{
struct dc *dc = link->ctx->dc;
struct dmub_replay *replay = dc->res_pool->replay;
@@ -1122,9 +1244,11 @@ bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal)
if (!dc_get_edp_link_panel_inst(dc, link, &panel_inst))
return false;
- if (coasting_vtotal && link->replay_settings.coasting_vtotal != coasting_vtotal) {
- replay->funcs->replay_set_coasting_vtotal(replay, coasting_vtotal, panel_inst);
+ if (coasting_vtotal && (link->replay_settings.coasting_vtotal != coasting_vtotal ||
+ link->replay_settings.frame_skip_number != frame_skip_number)) {
+ replay->funcs->replay_set_coasting_vtotal(replay, coasting_vtotal, panel_inst, frame_skip_number);
link->replay_settings.coasting_vtotal = coasting_vtotal;
+ link->replay_settings.frame_skip_number = frame_skip_number;
}
return true;
@@ -1152,7 +1276,7 @@ bool edp_replay_residency(const struct dc_link *link,
}
bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link,
- const unsigned int *power_opts, uint32_t coasting_vtotal)
+ const unsigned int *power_opts, uint32_t coasting_vtotal, uint16_t frame_skip_number)
{
struct dc *dc = link->ctx->dc;
struct dmub_replay *replay = dc->res_pool->replay;
@@ -1163,13 +1287,16 @@ bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link,
/* Only both power and coasting vtotal changed, this func could return true */
if (power_opts && link->replay_settings.replay_power_opt_active != *power_opts &&
- coasting_vtotal && link->replay_settings.coasting_vtotal != coasting_vtotal) {
+ (coasting_vtotal &&
+ (link->replay_settings.coasting_vtotal != coasting_vtotal ||
+ link->replay_settings.frame_skip_number != frame_skip_number))) {
if (link->replay_settings.replay_feature_enabled &&
replay->funcs->replay_set_power_opt_and_coasting_vtotal) {
replay->funcs->replay_set_power_opt_and_coasting_vtotal(replay,
- *power_opts, panel_inst, coasting_vtotal);
+ *power_opts, panel_inst, coasting_vtotal, frame_skip_number);
link->replay_settings.replay_power_opt_active = *power_opts;
link->replay_settings.coasting_vtotal = coasting_vtotal;
+ link->replay_settings.frame_skip_number = frame_skip_number;
} else
return false;
} else
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h
index 62a6344e613e..dd79c7cd2828 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.h
@@ -59,12 +59,12 @@ bool edp_setup_replay(struct dc_link *link,
bool edp_send_replay_cmd(struct dc_link *link,
enum replay_FW_Message_type msg,
union dmub_replay_cmd_set *cmd_data);
-bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal);
+bool edp_set_coasting_vtotal(struct dc_link *link, uint32_t coasting_vtotal, uint16_t frame_skip_number);
bool edp_replay_residency(const struct dc_link *link,
unsigned int *residency, const bool is_start, const enum pr_residency_mode mode);
bool edp_get_replay_state(const struct dc_link *link, uint64_t *state);
bool edp_set_replay_power_opt_and_coasting_vtotal(struct dc_link *link,
- const unsigned int *power_opts, uint32_t coasting_vtotal);
+ const unsigned int *power_opts, uint32_t coasting_vtotal, uint16_t frame_skip_number);
bool edp_wait_for_t12(struct dc_link *link);
bool edp_is_ilr_optimization_required(struct dc_link *link,
struct dc_crtc_timing *crtc_timing);
diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c
index 85298b8a1b5e..6bfd2c1294e5 100644
--- a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c
+++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.c
@@ -1514,6 +1514,21 @@ static void mpc3_read_mpcc_state(
MPCC_OGAM_SELECT_CURRENT, &s->rgam_lut);
}
+void mpc3_read_reg_state(
+ struct mpc *mpc,
+ int mpcc_inst, struct dcn_mpc_reg_state *mpc_reg_state)
+{
+ struct dcn30_mpc *mpc30 = TO_DCN30_MPC(mpc);
+
+ mpc_reg_state->mpcc_bot_sel = REG_READ(MPCC_BOT_SEL[mpcc_inst]);
+ mpc_reg_state->mpcc_control = REG_READ(MPCC_CONTROL[mpcc_inst]);
+ mpc_reg_state->mpcc_ogam_control = REG_READ(MPCC_OGAM_CONTROL[mpcc_inst]);
+ mpc_reg_state->mpcc_opp_id = REG_READ(MPCC_OPP_ID[mpcc_inst]);
+ mpc_reg_state->mpcc_status = REG_READ(MPCC_STATUS[mpcc_inst]);
+ mpc_reg_state->mpcc_top_sel = REG_READ(MPCC_TOP_SEL[mpcc_inst]);
+
+}
+
static const struct mpc_funcs dcn30_mpc_funcs = {
.read_mpcc_state = mpc3_read_mpcc_state,
.insert_plane = mpc1_insert_plane,
@@ -1544,6 +1559,7 @@ static const struct mpc_funcs dcn30_mpc_funcs = {
.release_rmu = mpcc3_release_rmu,
.power_on_mpc_mem_pwr = mpc3_power_on_ogam_lut,
.get_mpc_out_mux = mpc1_get_mpc_out_mux,
+ .mpc_read_reg_state = mpc3_read_reg_state,
.set_bg_color = mpc1_set_bg_color,
.set_mpc_mem_lp_mode = mpc3_set_mpc_mem_lp_mode,
};
diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.h b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.h
index 103f29900a2c..e2f147d17178 100644
--- a/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.h
+++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn30/dcn30_mpc.h
@@ -1096,6 +1096,11 @@ void mpc3_power_on_ogam_lut(
struct mpc *mpc, int mpcc_id,
bool power_on);
+void mpc3_read_reg_state(
+ struct mpc *mpc,
+ int mpcc_inst,
+ struct dcn_mpc_reg_state *mpc_reg_state);
+
void mpc3_init_mpcc(struct mpcc *mpcc, int mpcc_inst);
enum dc_lut_mode mpc3_get_ogam_current(
diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c
index 6f0e017a8ae2..83bbbf34bcac 100644
--- a/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c
+++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn32/dcn32_mpc.c
@@ -1020,6 +1020,7 @@ static const struct mpc_funcs dcn32_mpc_funcs = {
.release_rmu = NULL,
.power_on_mpc_mem_pwr = mpc3_power_on_ogam_lut,
.get_mpc_out_mux = mpc1_get_mpc_out_mux,
+ .mpc_read_reg_state = mpc3_read_reg_state,
.set_bg_color = mpc1_set_bg_color,
};
diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c
index e1a0308dee57..eeac13fdd6f5 100644
--- a/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c
+++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn401/dcn401_mpc.c
@@ -598,6 +598,7 @@ static const struct mpc_funcs dcn401_mpc_funcs = {
.release_rmu = NULL,
.power_on_mpc_mem_pwr = mpc3_power_on_ogam_lut,
.get_mpc_out_mux = mpc1_get_mpc_out_mux,
+ .mpc_read_reg_state = mpc3_read_reg_state,
.set_bg_color = mpc1_set_bg_color,
.set_movable_cm_location = mpc401_set_movable_cm_location,
.update_3dlut_fast_load_select = mpc401_update_3dlut_fast_load_select,
diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c
index 71e9288d60ed..45d418636d0c 100644
--- a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c
+++ b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.c
@@ -372,6 +372,17 @@ void opp1_pipe_clock_control(struct output_pixel_processor *opp, bool enable)
REG_UPDATE(OPP_PIPE_CONTROL, OPP_PIPE_CLOCK_EN, regval);
}
+
+void opp1_read_reg_state(struct output_pixel_processor *opp, struct dcn_opp_reg_state *opp_reg_state)
+{
+ struct dcn10_opp *oppn10 = TO_DCN10_OPP(opp);
+
+ opp_reg_state->fmt_control = REG_READ(FMT_CONTROL);
+ opp_reg_state->opp_pipe_control = REG_READ(OPP_PIPE_CONTROL);
+ opp_reg_state->opp_pipe_crc_control = REG_READ(OPP_PIPE_CRC_CONTROL);
+ opp_reg_state->oppbuf_control = REG_READ(OPPBUF_CONTROL);
+}
+
/*****************************************/
/* Constructor, Destructor */
/*****************************************/
@@ -392,7 +403,8 @@ static const struct opp_funcs dcn10_opp_funcs = {
.opp_program_dpg_dimensions = NULL,
.dpg_is_blanked = NULL,
.dpg_is_pending = NULL,
- .opp_destroy = opp1_destroy
+ .opp_destroy = opp1_destroy,
+ .opp_read_reg_state = opp1_read_reg_state
};
void dcn10_opp_construct(struct dcn10_opp *oppn10,
diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.h b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.h
index c87de68a509e..38d0d530a9b7 100644
--- a/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.h
+++ b/drivers/gpu/drm/amd/display/dc/opp/dcn10/dcn10_opp.h
@@ -63,7 +63,8 @@
uint32_t OPPBUF_CONTROL1; \
uint32_t OPPBUF_3D_PARAMETERS_0; \
uint32_t OPPBUF_3D_PARAMETERS_1; \
- uint32_t OPP_PIPE_CONTROL
+ uint32_t OPP_PIPE_CONTROL; \
+ uint32_t OPP_PIPE_CRC_CONTROL
#define OPP_MASK_SH_LIST_DCN(mask_sh) \
OPP_SF(FMT0_FMT_BIT_DEPTH_CONTROL, FMT_TRUNCATE_EN, mask_sh), \
@@ -153,7 +154,6 @@ struct dcn10_opp {
const struct dcn10_opp_registers *regs;
const struct dcn10_opp_shift *opp_shift;
const struct dcn10_opp_mask *opp_mask;
-
bool is_write_to_ram_a_safe;
};
@@ -188,4 +188,6 @@ void opp1_pipe_clock_control(struct output_pixel_processor *opp, bool enable);
void opp1_destroy(struct output_pixel_processor **opp);
+void opp1_read_reg_state(struct output_pixel_processor *opp, struct dcn_opp_reg_state *opp_reg_state);
+
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c
index f5fe0cac7cb0..ce826a5be4c7 100644
--- a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c
+++ b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c
@@ -377,6 +377,18 @@ uint32_t opp2_get_left_edge_extra_pixel_count(struct output_pixel_processor *opp
return 0;
}
+void opp2_read_reg_state(struct output_pixel_processor *opp, struct dcn_opp_reg_state *opp_reg_state)
+{
+ struct dcn20_opp *oppn20 = TO_DCN20_OPP(opp);
+
+ opp_reg_state->dpg_control = REG_READ(DPG_CONTROL);
+ opp_reg_state->fmt_control = REG_READ(FMT_CONTROL);
+ opp_reg_state->opp_pipe_control = REG_READ(OPP_PIPE_CONTROL);
+ opp_reg_state->opp_pipe_crc_control = REG_READ(OPP_PIPE_CRC_CONTROL);
+ opp_reg_state->oppbuf_control = REG_READ(OPPBUF_CONTROL);
+ opp_reg_state->dscrm_dsc_forward_config = REG_READ(DSCRM_DSC_FORWARD_CONFIG);
+}
+
/*****************************************/
/* Constructor, Destructor */
/*****************************************/
@@ -395,6 +407,7 @@ static struct opp_funcs dcn20_opp_funcs = {
.opp_destroy = opp1_destroy,
.opp_program_left_edge_extra_pixel = opp2_program_left_edge_extra_pixel,
.opp_get_left_edge_extra_pixel_count = opp2_get_left_edge_extra_pixel_count,
+ .opp_read_reg_state = opp2_read_reg_state
};
void dcn20_opp_construct(struct dcn20_opp *oppn20,
diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.h b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.h
index 34936e6c49f3..fb0c047c1788 100644
--- a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.h
+++ b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.h
@@ -59,7 +59,8 @@
uint32_t DPG_COLOUR_G_Y; \
uint32_t DPG_COLOUR_R_CR; \
uint32_t DPG_RAMP_CONTROL; \
- uint32_t DPG_STATUS
+ uint32_t DPG_STATUS; \
+ uint32_t DSCRM_DSC_FORWARD_CONFIG
#define OPP_DPG_MASK_SH_LIST(mask_sh) \
OPP_SF(DPG0_DPG_CONTROL, DPG_EN, mask_sh), \
@@ -171,4 +172,7 @@ void opp2_program_left_edge_extra_pixel (
uint32_t opp2_get_left_edge_extra_pixel_count(struct output_pixel_processor *opp,
enum dc_pixel_encoding pixel_encoding, bool is_primary);
+
+void opp2_read_reg_state(struct output_pixel_processor *opp, struct dcn_opp_reg_state *opp_reg_state);
+
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.c
index 3542b51c9aac..e11c4e16402f 100644
--- a/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.c
+++ b/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.c
@@ -51,3 +51,16 @@ void dcn35_opp_set_fgcg(struct dcn20_opp *oppn20, bool enable)
{
REG_UPDATE(OPP_TOP_CLK_CONTROL, OPP_FGCG_REP_DIS, !enable);
}
+
+void dcn35_opp_read_reg_state(struct output_pixel_processor *opp, struct dcn_opp_reg_state *opp_reg_state)
+{
+ struct dcn20_opp *oppn20 = TO_DCN20_OPP(opp);
+
+ opp_reg_state->dpg_control = REG_READ(DPG_CONTROL);
+ opp_reg_state->fmt_control = REG_READ(FMT_CONTROL);
+ opp_reg_state->opp_abm_control = REG_READ(OPP_ABM_CONTROL);
+ opp_reg_state->opp_pipe_control = REG_READ(OPP_PIPE_CONTROL);
+ opp_reg_state->opp_pipe_crc_control = REG_READ(OPP_PIPE_CRC_CONTROL);
+ opp_reg_state->oppbuf_control = REG_READ(OPPBUF_CONTROL);
+ opp_reg_state->dscrm_dsc_forward_config = REG_READ(DSCRM_DSC_FORWARD_CONFIG);
+}
diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.h b/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.h
index a9a413527801..c6cace90e8f2 100644
--- a/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.h
+++ b/drivers/gpu/drm/amd/display/dc/opp/dcn35/dcn35_opp.h
@@ -31,7 +31,8 @@
#define OPP_REG_VARIABLE_LIST_DCN3_5 \
OPP_REG_VARIABLE_LIST_DCN2_0; \
- uint32_t OPP_TOP_CLK_CONTROL
+ uint32_t OPP_TOP_CLK_CONTROL; \
+ uint32_t OPP_ABM_CONTROL
#define OPP_MASK_SH_LIST_DCN35(mask_sh) \
OPP_MASK_SH_LIST_DCN20(mask_sh), \
@@ -64,4 +65,5 @@ void dcn35_opp_construct(struct dcn20_opp *oppn20,
void dcn35_opp_set_fgcg(struct dcn20_opp *oppn20, bool enable);
+void dcn35_opp_read_reg_state(struct output_pixel_processor *opp, struct dcn_opp_reg_state *opp_reg_state);
#endif
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h
index 8b2a8455eb56..803bcc25601c 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.h
@@ -209,7 +209,43 @@
uint32_t OPTC_WIDTH_CONTROL2; \
uint32_t OTG_PSTATE_REGISTER; \
uint32_t OTG_PIPE_UPDATE_STATUS; \
- uint32_t INTERRUPT_DEST
+ uint32_t INTERRUPT_DEST; \
+ uint32_t OPTC_INPUT_SPARE_REGISTER; \
+ uint32_t OPTC_RSMU_UNDERFLOW; \
+ uint32_t OPTC_UNDERFLOW_THRESHOLD; \
+ uint32_t OTG_COUNT_CONTROL; \
+ uint32_t OTG_COUNT_RESET; \
+ uint32_t OTG_CRC_SIG_BLUE_CONTROL_MASK; \
+ uint32_t OTG_CRC_SIG_RED_GREEN_MASK; \
+ uint32_t OTG_DLPC_CONTROL; \
+ uint32_t OTG_DRR_CONTROL2; \
+ uint32_t OTG_DRR_TIMING_INT_STATUS; \
+ uint32_t OTG_GLOBAL_CONTROL3; \
+ uint32_t OTG_GLOBAL_SYNC_STATUS; \
+ uint32_t OTG_GSL_VSYNC_GAP; \
+ uint32_t OTG_INTERLACE_STATUS; \
+ uint32_t OTG_INTERRUPT_CONTROL; \
+ uint32_t OTG_LONG_VBLANK_STATUS; \
+ uint32_t OTG_MANUAL_FORCE_VSYNC_NEXT_LINE; \
+ uint32_t OTG_MASTER_EN; \
+ uint32_t OTG_PIXEL_DATA_READBACK0; \
+ uint32_t OTG_PIXEL_DATA_READBACK1; \
+ uint32_t OTG_REQUEST_CONTROL; \
+ uint32_t OTG_SNAPSHOT_CONTROL; \
+ uint32_t OTG_SNAPSHOT_FRAME; \
+ uint32_t OTG_SNAPSHOT_POSITION; \
+ uint32_t OTG_SNAPSHOT_STATUS; \
+ uint32_t OTG_SPARE_REGISTER; \
+ uint32_t OTG_STATUS_HV_COUNT; \
+ uint32_t OTG_STATUS_VF_COUNT; \
+ uint32_t OTG_STEREO_FORCE_NEXT_EYE; \
+ uint32_t OTG_TRIG_MANUAL_CONTROL; \
+ uint32_t OTG_TRIGB_CNTL; \
+ uint32_t OTG_TRIGB_MANUAL_TRIG; \
+ uint32_t OTG_UPDATE_LOCK; \
+ uint32_t OTG_V_TOTAL_INT_STATUS; \
+ uint32_t OTG_VSYNC_NOM_INT_STATUS
+
struct dcn_optc_registers {
OPTC_REG_VARIABLE_LIST_DCN;
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
index 4f1830ba619f..c6417538090f 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c
@@ -315,6 +315,136 @@ void optc31_read_otg_state(struct timing_generator *optc,
s->otg_double_buffer_control = REG_READ(OTG_DOUBLE_BUFFER_CONTROL);
}
+void optc31_read_reg_state(struct timing_generator *optc, struct dcn_optc_reg_state *optc_reg_state)
+{
+ struct optc *optc1 = DCN10TG_FROM_TG(optc);
+
+ optc_reg_state->optc_bytes_per_pixel = REG_READ(OPTC_BYTES_PER_PIXEL);
+ optc_reg_state->optc_data_format_control = REG_READ(OPTC_DATA_FORMAT_CONTROL);
+ optc_reg_state->optc_data_source_select = REG_READ(OPTC_DATA_SOURCE_SELECT);
+ optc_reg_state->optc_input_clock_control = REG_READ(OPTC_INPUT_CLOCK_CONTROL);
+ optc_reg_state->optc_input_global_control = REG_READ(OPTC_INPUT_GLOBAL_CONTROL);
+ optc_reg_state->optc_input_spare_register = REG_READ(OPTC_INPUT_SPARE_REGISTER);
+ optc_reg_state->optc_memory_config = REG_READ(OPTC_MEMORY_CONFIG);
+ optc_reg_state->optc_rsmu_underflow = REG_READ(OPTC_RSMU_UNDERFLOW);
+ optc_reg_state->optc_underflow_threshold = REG_READ(OPTC_UNDERFLOW_THRESHOLD);
+ optc_reg_state->optc_width_control = REG_READ(OPTC_WIDTH_CONTROL);
+ optc_reg_state->otg_3d_structure_control = REG_READ(OTG_3D_STRUCTURE_CONTROL);
+ optc_reg_state->otg_clock_control = REG_READ(OTG_CLOCK_CONTROL);
+ optc_reg_state->otg_control = REG_READ(OTG_CONTROL);
+ optc_reg_state->otg_count_control = REG_READ(OTG_COUNT_CONTROL);
+ optc_reg_state->otg_count_reset = REG_READ(OTG_COUNT_RESET);
+ optc_reg_state->otg_crc_cntl = REG_READ(OTG_CRC_CNTL);
+ optc_reg_state->otg_crc_sig_blue_control_mask = REG_READ(OTG_CRC_SIG_BLUE_CONTROL_MASK);
+ optc_reg_state->otg_crc_sig_red_green_mask = REG_READ(OTG_CRC_SIG_RED_GREEN_MASK);
+ optc_reg_state->otg_crc0_data_b = REG_READ(OTG_CRC0_DATA_B);
+ optc_reg_state->otg_crc0_data_rg = REG_READ(OTG_CRC0_DATA_RG);
+ optc_reg_state->otg_crc0_windowa_x_control = REG_READ(OTG_CRC0_WINDOWA_X_CONTROL);
+ optc_reg_state->otg_crc0_windowa_x_control_readback = REG_READ(OTG_CRC0_WINDOWA_X_CONTROL_READBACK);
+ optc_reg_state->otg_crc0_windowa_y_control = REG_READ(OTG_CRC0_WINDOWA_Y_CONTROL);
+ optc_reg_state->otg_crc0_windowa_y_control_readback = REG_READ(OTG_CRC0_WINDOWA_Y_CONTROL_READBACK);
+ optc_reg_state->otg_crc0_windowb_x_control = REG_READ(OTG_CRC0_WINDOWB_X_CONTROL);
+ optc_reg_state->otg_crc0_windowb_x_control_readback = REG_READ(OTG_CRC0_WINDOWB_X_CONTROL_READBACK);
+ optc_reg_state->otg_crc0_windowb_y_control = REG_READ(OTG_CRC0_WINDOWB_Y_CONTROL);
+ optc_reg_state->otg_crc0_windowb_y_control_readback = REG_READ(OTG_CRC0_WINDOWB_Y_CONTROL_READBACK);
+ optc_reg_state->otg_crc1_data_b = REG_READ(OTG_CRC1_DATA_B);
+ optc_reg_state->otg_crc1_data_rg = REG_READ(OTG_CRC1_DATA_RG);
+ optc_reg_state->otg_crc1_windowa_x_control = REG_READ(OTG_CRC1_WINDOWA_X_CONTROL);
+ optc_reg_state->otg_crc1_windowa_x_control_readback = REG_READ(OTG_CRC1_WINDOWA_X_CONTROL_READBACK);
+ optc_reg_state->otg_crc1_windowa_y_control = REG_READ(OTG_CRC1_WINDOWA_Y_CONTROL);
+ optc_reg_state->otg_crc1_windowa_y_control_readback = REG_READ(OTG_CRC1_WINDOWA_Y_CONTROL_READBACK);
+ optc_reg_state->otg_crc1_windowb_x_control = REG_READ(OTG_CRC1_WINDOWB_X_CONTROL);
+ optc_reg_state->otg_crc1_windowb_x_control_readback = REG_READ(OTG_CRC1_WINDOWB_X_CONTROL_READBACK);
+ optc_reg_state->otg_crc1_windowb_y_control = REG_READ(OTG_CRC1_WINDOWB_Y_CONTROL);
+ optc_reg_state->otg_crc1_windowb_y_control_readback = REG_READ(OTG_CRC1_WINDOWB_Y_CONTROL_READBACK);
+ optc_reg_state->otg_crc2_data_b = REG_READ(OTG_CRC2_DATA_B);
+ optc_reg_state->otg_crc2_data_rg = REG_READ(OTG_CRC2_DATA_RG);
+ optc_reg_state->otg_crc3_data_b = REG_READ(OTG_CRC3_DATA_B);
+ optc_reg_state->otg_crc3_data_rg = REG_READ(OTG_CRC3_DATA_RG);
+ optc_reg_state->otg_dlpc_control = REG_READ(OTG_DLPC_CONTROL);
+ optc_reg_state->otg_double_buffer_control = REG_READ(OTG_DOUBLE_BUFFER_CONTROL);
+ optc_reg_state->otg_drr_control2 = REG_READ(OTG_DRR_CONTROL2);
+ optc_reg_state->otg_drr_control = REG_READ(OTG_DRR_CONTROL);
+ optc_reg_state->otg_drr_timing_int_status = REG_READ(OTG_DRR_TIMING_INT_STATUS);
+ optc_reg_state->otg_drr_trigger_window = REG_READ(OTG_DRR_TRIGGER_WINDOW);
+ optc_reg_state->otg_drr_v_total_change = REG_READ(OTG_DRR_V_TOTAL_CHANGE);
+ optc_reg_state->otg_dsc_start_position = REG_READ(OTG_DSC_START_POSITION);
+ optc_reg_state->otg_force_count_now_cntl = REG_READ(OTG_FORCE_COUNT_NOW_CNTL);
+ optc_reg_state->otg_global_control0 = REG_READ(OTG_GLOBAL_CONTROL0);
+ optc_reg_state->otg_global_control1 = REG_READ(OTG_GLOBAL_CONTROL1);
+ optc_reg_state->otg_global_control2 = REG_READ(OTG_GLOBAL_CONTROL2);
+ optc_reg_state->otg_global_control3 = REG_READ(OTG_GLOBAL_CONTROL3);
+ optc_reg_state->otg_global_control4 = REG_READ(OTG_GLOBAL_CONTROL4);
+ optc_reg_state->otg_global_sync_status = REG_READ(OTG_GLOBAL_SYNC_STATUS);
+ optc_reg_state->otg_gsl_control = REG_READ(OTG_GSL_CONTROL);
+ optc_reg_state->otg_gsl_vsync_gap = REG_READ(OTG_GSL_VSYNC_GAP);
+ optc_reg_state->otg_gsl_window_x = REG_READ(OTG_GSL_WINDOW_X);
+ optc_reg_state->otg_gsl_window_y = REG_READ(OTG_GSL_WINDOW_Y);
+ optc_reg_state->otg_h_blank_start_end = REG_READ(OTG_H_BLANK_START_END);
+ optc_reg_state->otg_h_sync_a = REG_READ(OTG_H_SYNC_A);
+ optc_reg_state->otg_h_sync_a_cntl = REG_READ(OTG_H_SYNC_A_CNTL);
+ optc_reg_state->otg_h_timing_cntl = REG_READ(OTG_H_TIMING_CNTL);
+ optc_reg_state->otg_h_total = REG_READ(OTG_H_TOTAL);
+ optc_reg_state->otg_interlace_control = REG_READ(OTG_INTERLACE_CONTROL);
+ optc_reg_state->otg_interlace_status = REG_READ(OTG_INTERLACE_STATUS);
+ optc_reg_state->otg_interrupt_control = REG_READ(OTG_INTERRUPT_CONTROL);
+ optc_reg_state->otg_long_vblank_status = REG_READ(OTG_LONG_VBLANK_STATUS);
+ optc_reg_state->otg_m_const_dto0 = REG_READ(OTG_M_CONST_DTO0);
+ optc_reg_state->otg_m_const_dto1 = REG_READ(OTG_M_CONST_DTO1);
+ optc_reg_state->otg_manual_force_vsync_next_line = REG_READ(OTG_MANUAL_FORCE_VSYNC_NEXT_LINE);
+ optc_reg_state->otg_master_en = REG_READ(OTG_MASTER_EN);
+ optc_reg_state->otg_master_update_lock = REG_READ(OTG_MASTER_UPDATE_LOCK);
+ optc_reg_state->otg_master_update_mode = REG_READ(OTG_MASTER_UPDATE_MODE);
+ optc_reg_state->otg_nom_vert_position = REG_READ(OTG_NOM_VERT_POSITION);
+ optc_reg_state->otg_pipe_update_status = REG_READ(OTG_PIPE_UPDATE_STATUS);
+ optc_reg_state->otg_pixel_data_readback0 = REG_READ(OTG_PIXEL_DATA_READBACK0);
+ optc_reg_state->otg_pixel_data_readback1 = REG_READ(OTG_PIXEL_DATA_READBACK1);
+ optc_reg_state->otg_request_control = REG_READ(OTG_REQUEST_CONTROL);
+ optc_reg_state->otg_snapshot_control = REG_READ(OTG_SNAPSHOT_CONTROL);
+ optc_reg_state->otg_snapshot_frame = REG_READ(OTG_SNAPSHOT_FRAME);
+ optc_reg_state->otg_snapshot_position = REG_READ(OTG_SNAPSHOT_POSITION);
+ optc_reg_state->otg_snapshot_status = REG_READ(OTG_SNAPSHOT_STATUS);
+ optc_reg_state->otg_spare_register = REG_READ(OTG_SPARE_REGISTER);
+ optc_reg_state->otg_static_screen_control = REG_READ(OTG_STATIC_SCREEN_CONTROL);
+ optc_reg_state->otg_status = REG_READ(OTG_STATUS);
+ optc_reg_state->otg_status_frame_count = REG_READ(OTG_STATUS_FRAME_COUNT);
+ optc_reg_state->otg_status_hv_count = REG_READ(OTG_STATUS_HV_COUNT);
+ optc_reg_state->otg_status_position = REG_READ(OTG_STATUS_POSITION);
+ optc_reg_state->otg_status_vf_count = REG_READ(OTG_STATUS_VF_COUNT);
+ optc_reg_state->otg_stereo_control = REG_READ(OTG_STEREO_CONTROL);
+ optc_reg_state->otg_stereo_force_next_eye = REG_READ(OTG_STEREO_FORCE_NEXT_EYE);
+ optc_reg_state->otg_stereo_status = REG_READ(OTG_STEREO_STATUS);
+ optc_reg_state->otg_trig_manual_control = REG_READ(OTG_TRIG_MANUAL_CONTROL);
+ optc_reg_state->otg_triga_cntl = REG_READ(OTG_TRIGA_CNTL);
+ optc_reg_state->otg_triga_manual_trig = REG_READ(OTG_TRIGA_MANUAL_TRIG);
+ optc_reg_state->otg_trigb_cntl = REG_READ(OTG_TRIGB_CNTL);
+ optc_reg_state->otg_trigb_manual_trig = REG_READ(OTG_TRIGB_MANUAL_TRIG);
+ optc_reg_state->otg_update_lock = REG_READ(OTG_UPDATE_LOCK);
+ optc_reg_state->otg_v_blank_start_end = REG_READ(OTG_V_BLANK_START_END);
+ optc_reg_state->otg_v_count_stop_control = REG_READ(OTG_V_COUNT_STOP_CONTROL);
+ optc_reg_state->otg_v_count_stop_control2 = REG_READ(OTG_V_COUNT_STOP_CONTROL2);
+ optc_reg_state->otg_v_sync_a = REG_READ(OTG_V_SYNC_A);
+ optc_reg_state->otg_v_sync_a_cntl = REG_READ(OTG_V_SYNC_A_CNTL);
+ optc_reg_state->otg_v_total = REG_READ(OTG_V_TOTAL);
+ optc_reg_state->otg_v_total_control = REG_READ(OTG_V_TOTAL_CONTROL);
+ optc_reg_state->otg_v_total_int_status = REG_READ(OTG_V_TOTAL_INT_STATUS);
+ optc_reg_state->otg_v_total_max = REG_READ(OTG_V_TOTAL_MAX);
+ optc_reg_state->otg_v_total_mid = REG_READ(OTG_V_TOTAL_MID);
+ optc_reg_state->otg_v_total_min = REG_READ(OTG_V_TOTAL_MIN);
+ optc_reg_state->otg_vert_sync_control = REG_READ(OTG_VERT_SYNC_CONTROL);
+ optc_reg_state->otg_vertical_interrupt0_control = REG_READ(OTG_VERTICAL_INTERRUPT0_CONTROL);
+ optc_reg_state->otg_vertical_interrupt0_position = REG_READ(OTG_VERTICAL_INTERRUPT0_POSITION);
+ optc_reg_state->otg_vertical_interrupt1_control = REG_READ(OTG_VERTICAL_INTERRUPT1_CONTROL);
+ optc_reg_state->otg_vertical_interrupt1_position = REG_READ(OTG_VERTICAL_INTERRUPT1_POSITION);
+ optc_reg_state->otg_vertical_interrupt2_control = REG_READ(OTG_VERTICAL_INTERRUPT2_CONTROL);
+ optc_reg_state->otg_vertical_interrupt2_position = REG_READ(OTG_VERTICAL_INTERRUPT2_POSITION);
+ optc_reg_state->otg_vready_param = REG_READ(OTG_VREADY_PARAM);
+ optc_reg_state->otg_vstartup_param = REG_READ(OTG_VSTARTUP_PARAM);
+ optc_reg_state->otg_vsync_nom_int_status = REG_READ(OTG_VSYNC_NOM_INT_STATUS);
+ optc_reg_state->otg_vupdate_keepout = REG_READ(OTG_VUPDATE_KEEPOUT);
+ optc_reg_state->otg_vupdate_param = REG_READ(OTG_VUPDATE_PARAM);
+}
+
static const struct timing_generator_funcs dcn31_tg_funcs = {
.validate_timing = optc1_validate_timing,
.program_timing = optc1_program_timing,
@@ -377,6 +507,7 @@ static const struct timing_generator_funcs dcn31_tg_funcs = {
.init_odm = optc3_init_odm,
.is_two_pixels_per_container = optc1_is_two_pixels_per_container,
.read_otg_state = optc31_read_otg_state,
+ .optc_read_reg_state = optc31_read_reg_state,
};
void dcn31_timing_generator_init(struct optc *optc1)
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h
index 0f72c274f40b..98f7d2e299c5 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.h
@@ -274,4 +274,6 @@ void optc3_init_odm(struct timing_generator *optc);
void optc31_read_otg_state(struct timing_generator *optc,
struct dcn_otg_state *s);
+void optc31_read_reg_state(struct timing_generator *optc, struct dcn_optc_reg_state *optc_reg_state);
+
#endif /* __DC_OPTC_DCN31_H__ */
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
index 4a2caca37255..43ff957288b2 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c
@@ -256,6 +256,7 @@ static const struct timing_generator_funcs dcn314_tg_funcs = {
.set_h_timing_div_manual_mode = optc314_set_h_timing_div_manual_mode,
.is_two_pixels_per_container = optc1_is_two_pixels_per_container,
.read_otg_state = optc31_read_otg_state,
+ .optc_read_reg_state = optc31_read_reg_state,
};
void dcn314_timing_generator_init(struct optc *optc1)
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
index b2b226bcd871..3dcb0d0c931c 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c
@@ -365,6 +365,7 @@ static const struct timing_generator_funcs dcn32_tg_funcs = {
.get_otg_double_buffer_pending = optc3_get_otg_update_pending,
.get_pipe_update_pending = optc3_get_pipe_update_pending,
.read_otg_state = optc31_read_otg_state,
+ .optc_read_reg_state = optc31_read_reg_state,
};
void dcn32_timing_generator_init(struct optc *optc1)
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
index 52d5ea98c86b..f699e95059f3 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c
@@ -511,6 +511,7 @@ static const struct timing_generator_funcs dcn35_tg_funcs = {
.set_long_vtotal = optc35_set_long_vtotal,
.is_two_pixels_per_container = optc1_is_two_pixels_per_container,
.read_otg_state = optc31_read_otg_state,
+ .optc_read_reg_state = optc31_read_reg_state,
};
void dcn35_timing_generator_init(struct optc *optc1)
diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
index 5af13706e601..a8e978d1fae8 100644
--- a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
+++ b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c
@@ -533,6 +533,7 @@ static const struct timing_generator_funcs dcn401_tg_funcs = {
.set_vupdate_keepout = optc401_set_vupdate_keepout,
.wait_update_lock_status = optc401_wait_update_lock_status,
.read_otg_state = optc31_read_otg_state,
+ .optc_read_reg_state = optc31_read_reg_state,
};
void dcn401_timing_generator_init(struct optc *optc1)
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
index c4b4dc3ad8c9..d40d91ec2035 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce100/dce100_resource.c
@@ -78,6 +78,7 @@
#endif
#ifndef mmBIOS_SCRATCH_2
+ #define mmBIOS_SCRATCH_0 0x05C9
#define mmBIOS_SCRATCH_2 0x05CB
#define mmBIOS_SCRATCH_3 0x05CC
#define mmBIOS_SCRATCH_6 0x05CF
@@ -225,6 +226,7 @@ static const struct dce110_link_enc_registers link_enc_regs[] = {
link_regs(4),
link_regs(5),
link_regs(6),
+ { .DAC_ENABLE = mmDAC_ENABLE },
};
#define stream_enc_regs(id)\
@@ -368,6 +370,7 @@ static const struct dce_abm_mask abm_mask = {
#define DCFE_MEM_PWR_CTRL_REG_BASE 0x1b03
static const struct bios_registers bios_regs = {
+ .BIOS_SCRATCH_0 = mmBIOS_SCRATCH_0,
.BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3,
.BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6
};
@@ -375,6 +378,7 @@ static const struct bios_registers bios_regs = {
static const struct resource_caps res_cap = {
.num_timing_generator = 6,
.num_audio = 6,
+ .num_analog_stream_encoder = 1,
.num_stream_encoder = 6,
.num_pll = 3,
.num_ddc = 6,
@@ -402,8 +406,10 @@ static const struct dc_plane_cap plane_cap = {
}
};
-static const struct dc_debug_options debug_defaults = {
- .enable_legacy_fast_update = true,
+static const struct dc_debug_options debug_defaults = { 0 };
+
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
};
#define CTX ctx
@@ -484,6 +490,11 @@ static struct stream_encoder *dce100_stream_encoder_create(
if (!enc110)
return NULL;
+ if (eng_id == ENGINE_ID_DACA || eng_id == ENGINE_ID_DACB) {
+ dce110_analog_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id);
+ return &enc110->base;
+ }
+
dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id,
&stream_enc_regs[eng_id], &se_shift, &se_mask);
return &enc110->base;
@@ -624,7 +635,20 @@ static struct link_encoder *dce100_link_encoder_create(
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
int link_regs_id;
- if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
+ if (!enc110)
+ return NULL;
+
+ if (enc_init_data->connector.id == CONNECTOR_ID_VGA) {
+ dce110_link_encoder_construct(enc110,
+ enc_init_data,
+ &link_enc_feature,
+ &link_enc_regs[ENGINE_ID_DACA],
+ NULL,
+ NULL);
+ return &enc110->base;
+ }
+
+ if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
return NULL;
link_regs_id =
@@ -952,6 +976,10 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
int i;
int j = -1;
struct dc_link *link = stream->link;
+ enum engine_id preferred_engine = link->link_enc->preferred_engine;
+
+ if (dc_is_rgb_signal(stream->signal))
+ preferred_engine = link->link_enc->analog_engine;
for (i = 0; i < pool->stream_enc_count; i++) {
if (!res_ctx->is_stream_enc_acquired[i] &&
@@ -960,8 +988,7 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
* in daisy chain use case
*/
j = i;
- if (pool->stream_enc[i]->id ==
- link->link_enc->preferred_engine)
+ if (pool->stream_enc[i]->id == preferred_engine)
return pool->stream_enc[i];
}
}
@@ -1093,6 +1120,7 @@ static bool dce100_resource_construct(
dc->caps.disable_dp_clk_share = true;
dc->caps.extended_aux_timeout_support = false;
dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
for (i = 0; i < pool->base.pipe_count; i++) {
pool->base.timing_generators[i] =
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c
index cccde5a6f3cd..cd54382c0af3 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce110/dce110_resource.c
@@ -82,6 +82,7 @@
#endif
#ifndef mmBIOS_SCRATCH_2
+ #define mmBIOS_SCRATCH_0 0x05C9
#define mmBIOS_SCRATCH_2 0x05CB
#define mmBIOS_SCRATCH_3 0x05CC
#define mmBIOS_SCRATCH_6 0x05CF
@@ -377,6 +378,7 @@ static const struct dce110_clk_src_mask cs_mask = {
};
static const struct bios_registers bios_regs = {
+ .BIOS_SCRATCH_0 = mmBIOS_SCRATCH_0,
.BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3,
.BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6
};
@@ -424,7 +426,9 @@ static const struct dc_plane_cap plane_cap = {
64
};
-static const struct dc_debug_options debug_defaults = {
+static const struct dc_debug_options debug_defaults = { 0 };
+
+static const struct dc_check_config config_defaults = {
.enable_legacy_fast_update = true,
};
@@ -1376,6 +1380,7 @@ static bool dce110_resource_construct(
dc->caps.is_apu = true;
dc->caps.extended_aux_timeout_support = false;
dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c
index 869a8e515fc0..3f0a6bc4dcc2 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce112/dce112_resource.c
@@ -76,6 +76,7 @@
#endif
#ifndef mmBIOS_SCRATCH_2
+ #define mmBIOS_SCRATCH_0 0x05C9
#define mmBIOS_SCRATCH_2 0x05CB
#define mmBIOS_SCRATCH_3 0x05CC
#define mmBIOS_SCRATCH_6 0x05CF
@@ -385,6 +386,7 @@ static const struct dce110_clk_src_mask cs_mask = {
};
static const struct bios_registers bios_regs = {
+ .BIOS_SCRATCH_0 = mmBIOS_SCRATCH_0,
.BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3,
.BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6
};
@@ -429,8 +431,10 @@ static const struct dc_plane_cap plane_cap = {
64
};
-static const struct dc_debug_options debug_defaults = {
- .enable_legacy_fast_update = true,
+static const struct dc_debug_options debug_defaults = { 0 };
+
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
};
#define CTX ctx
@@ -1247,6 +1251,7 @@ static bool dce112_resource_construct(
dc->caps.dual_link_dvi = true;
dc->caps.extended_aux_timeout_support = false;
dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c
index 540e04ec1e2d..b1570b6b1af3 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce120/dce120_resource.c
@@ -491,6 +491,7 @@ static struct dce_i2c_hw *dce120_i2c_hw_create(
return dce_i2c_hw;
}
static const struct bios_registers bios_regs = {
+ .BIOS_SCRATCH_0 = mmBIOS_SCRATCH_0 + NBIO_BASE(mmBIOS_SCRATCH_0_BASE_IDX),
.BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3 + NBIO_BASE(mmBIOS_SCRATCH_3_BASE_IDX),
.BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6 + NBIO_BASE(mmBIOS_SCRATCH_6_BASE_IDX)
};
@@ -526,8 +527,11 @@ static const struct dc_plane_cap plane_cap = {
};
static const struct dc_debug_options debug_defaults = {
- .disable_clock_gate = true,
- .enable_legacy_fast_update = true,
+ .disable_clock_gate = true,
+};
+
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
};
static struct clock_source *dce120_clock_source_create(
@@ -1089,6 +1093,7 @@ static bool dce120_resource_construct(
dc->caps.psp_setup_panel_mode = true;
dc->caps.extended_aux_timeout_support = false;
dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c
index b75be6ad64f6..f0152933bee2 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce60/dce60_resource.c
@@ -80,6 +80,7 @@
#ifndef mmBIOS_SCRATCH_2
+ #define mmBIOS_SCRATCH_0 0x05C9
#define mmBIOS_SCRATCH_2 0x05CB
#define mmBIOS_SCRATCH_3 0x05CC
#define mmBIOS_SCRATCH_6 0x05CF
@@ -240,7 +241,9 @@ static const struct dce110_link_enc_registers link_enc_regs[] = {
link_regs(2),
link_regs(3),
link_regs(4),
- link_regs(5)
+ link_regs(5),
+ {0},
+ { .DAC_ENABLE = mmDAC_ENABLE },
};
#define stream_enc_regs(id)\
@@ -366,6 +369,7 @@ static const struct dce110_clk_src_mask cs_mask = {
};
static const struct bios_registers bios_regs = {
+ .BIOS_SCRATCH_0 = mmBIOS_SCRATCH_0,
.BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3,
.BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6
};
@@ -373,6 +377,7 @@ static const struct bios_registers bios_regs = {
static const struct resource_caps res_cap = {
.num_timing_generator = 6,
.num_audio = 6,
+ .num_analog_stream_encoder = 1,
.num_stream_encoder = 6,
.num_pll = 3,
.num_ddc = 6,
@@ -382,6 +387,7 @@ static const struct resource_caps res_cap_61 = {
.num_timing_generator = 4,
.num_audio = 6,
.num_stream_encoder = 6,
+ .num_analog_stream_encoder = 1,
.num_pll = 3,
.num_ddc = 6,
};
@@ -389,6 +395,7 @@ static const struct resource_caps res_cap_61 = {
static const struct resource_caps res_cap_64 = {
.num_timing_generator = 2,
.num_audio = 2,
+ .num_analog_stream_encoder = 1,
.num_stream_encoder = 2,
.num_pll = 3,
.num_ddc = 2,
@@ -599,6 +606,11 @@ static struct stream_encoder *dce60_stream_encoder_create(
if (!enc110)
return NULL;
+ if (eng_id == ENGINE_ID_DACA || eng_id == ENGINE_ID_DACB) {
+ dce110_analog_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id);
+ return &enc110->base;
+ }
+
dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id,
&stream_enc_regs[eng_id],
&se_shift, &se_mask);
@@ -718,7 +730,20 @@ static struct link_encoder *dce60_link_encoder_create(
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
int link_regs_id;
- if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
+ if (!enc110)
+ return NULL;
+
+ if (enc_init_data->connector.id == CONNECTOR_ID_VGA) {
+ dce110_link_encoder_construct(enc110,
+ enc_init_data,
+ &link_enc_feature,
+ &link_enc_regs[ENGINE_ID_DACA],
+ NULL,
+ NULL);
+ return &enc110->base;
+ }
+
+ if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
return NULL;
link_regs_id =
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c
index 5b7769745202..8687104cabb7 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dce80/dce80_resource.c
@@ -78,6 +78,7 @@
#ifndef mmBIOS_SCRATCH_2
+ #define mmBIOS_SCRATCH_0 0x05C9
#define mmBIOS_SCRATCH_2 0x05CB
#define mmBIOS_SCRATCH_3 0x05CC
#define mmBIOS_SCRATCH_6 0x05CF
@@ -241,6 +242,7 @@ static const struct dce110_link_enc_registers link_enc_regs[] = {
link_regs(4),
link_regs(5),
link_regs(6),
+ { .DAC_ENABLE = mmDAC_ENABLE },
};
#define stream_enc_regs(id)\
@@ -368,6 +370,7 @@ static const struct dce110_clk_src_mask cs_mask = {
};
static const struct bios_registers bios_regs = {
+ .BIOS_SCRATCH_0 = mmBIOS_SCRATCH_0,
.BIOS_SCRATCH_3 = mmBIOS_SCRATCH_3,
.BIOS_SCRATCH_6 = mmBIOS_SCRATCH_6
};
@@ -375,6 +378,7 @@ static const struct bios_registers bios_regs = {
static const struct resource_caps res_cap = {
.num_timing_generator = 6,
.num_audio = 6,
+ .num_analog_stream_encoder = 1,
.num_stream_encoder = 6,
.num_pll = 3,
.num_ddc = 6,
@@ -383,6 +387,7 @@ static const struct resource_caps res_cap = {
static const struct resource_caps res_cap_81 = {
.num_timing_generator = 4,
.num_audio = 7,
+ .num_analog_stream_encoder = 1,
.num_stream_encoder = 7,
.num_pll = 3,
.num_ddc = 6,
@@ -391,6 +396,7 @@ static const struct resource_caps res_cap_81 = {
static const struct resource_caps res_cap_83 = {
.num_timing_generator = 2,
.num_audio = 6,
+ .num_analog_stream_encoder = 1,
.num_stream_encoder = 6,
.num_pll = 2,
.num_ddc = 2,
@@ -418,8 +424,10 @@ static const struct dc_plane_cap plane_cap = {
}
};
-static const struct dc_debug_options debug_defaults = {
- .enable_legacy_fast_update = true,
+static const struct dc_debug_options debug_defaults = { 0 };
+
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
};
static const struct dce_dmcu_registers dmcu_regs = {
@@ -605,6 +613,11 @@ static struct stream_encoder *dce80_stream_encoder_create(
if (!enc110)
return NULL;
+ if (eng_id == ENGINE_ID_DACA || eng_id == ENGINE_ID_DACB) {
+ dce110_analog_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id);
+ return &enc110->base;
+ }
+
dce110_stream_encoder_construct(enc110, ctx, ctx->dc_bios, eng_id,
&stream_enc_regs[eng_id],
&se_shift, &se_mask);
@@ -724,7 +737,20 @@ static struct link_encoder *dce80_link_encoder_create(
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
int link_regs_id;
- if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
+ if (!enc110)
+ return NULL;
+
+ if (enc_init_data->connector.id == CONNECTOR_ID_VGA) {
+ dce110_link_encoder_construct(enc110,
+ enc_init_data,
+ &link_enc_feature,
+ &link_enc_regs[ENGINE_ID_DACA],
+ NULL,
+ NULL);
+ return &enc110->base;
+ }
+
+ if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
return NULL;
link_regs_id =
@@ -919,6 +945,7 @@ static bool dce80_construct(
dc->caps.dual_link_dvi = true;
dc->caps.extended_aux_timeout_support = false;
dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
@@ -1320,6 +1347,7 @@ static bool dce83_construct(
dc->caps.min_horizontal_blanking_period = 80;
dc->caps.is_apu = true;
dc->debug = debug_defaults;
+ dc->check_config = config_defaults;
/*************************************************
* Create resources *
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c
index 652c05c35494..f12367adf145 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn10/dcn10_resource.c
@@ -556,10 +556,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.recovery_enabled = false, /*enable this by default after testing.*/
.max_downscale_src_width = 3840,
.underflow_assert_delay_us = 0xFFFFFFFF,
- .enable_legacy_fast_update = true,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static void dcn10_dpp_destroy(struct dpp **dpp)
{
kfree(TO_DCN10_DPP(*dpp));
@@ -1395,6 +1398,8 @@ static bool dcn10_resource_construct(
dc->caps.color.mpc.ogam_rom_caps.pq = 0;
dc->caps.color.mpc.ogam_rom_caps.hlg = 0;
dc->caps.color.mpc.ocsc = 0;
+ dc->debug = debug_defaults_drv;
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c
index 84b38d2d6967..6679c1a14f2f 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn20/dcn20_resource.c
@@ -718,10 +718,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.scl_reset_length10 = true,
.sanity_checks = false,
.underflow_assert_delay_us = 0xFFFFFFFF,
- .enable_legacy_fast_update = true,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
void dcn20_dpp_destroy(struct dpp **dpp)
{
kfree(TO_DCN20_DPP(*dpp));
@@ -733,7 +736,7 @@ struct dpp *dcn20_dpp_create(
uint32_t inst)
{
struct dcn20_dpp *dpp =
- kzalloc(sizeof(struct dcn20_dpp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn20_dpp), GFP_KERNEL);
if (!dpp)
return NULL;
@@ -751,7 +754,7 @@ struct input_pixel_processor *dcn20_ipp_create(
struct dc_context *ctx, uint32_t inst)
{
struct dcn10_ipp *ipp =
- kzalloc(sizeof(struct dcn10_ipp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn10_ipp), GFP_KERNEL);
if (!ipp) {
BREAK_TO_DEBUGGER();
@@ -768,7 +771,7 @@ struct output_pixel_processor *dcn20_opp_create(
struct dc_context *ctx, uint32_t inst)
{
struct dcn20_opp *opp =
- kzalloc(sizeof(struct dcn20_opp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn20_opp), GFP_KERNEL);
if (!opp) {
BREAK_TO_DEBUGGER();
@@ -785,7 +788,7 @@ struct dce_aux *dcn20_aux_engine_create(
uint32_t inst)
{
struct aux_engine_dce110 *aux_engine =
- kzalloc(sizeof(struct aux_engine_dce110), GFP_ATOMIC);
+ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL);
if (!aux_engine)
return NULL;
@@ -823,7 +826,7 @@ struct dce_i2c_hw *dcn20_i2c_hw_create(
uint32_t inst)
{
struct dce_i2c_hw *dce_i2c_hw =
- kzalloc(sizeof(struct dce_i2c_hw), GFP_ATOMIC);
+ kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL);
if (!dce_i2c_hw)
return NULL;
@@ -835,8 +838,7 @@ struct dce_i2c_hw *dcn20_i2c_hw_create(
}
struct mpc *dcn20_mpc_create(struct dc_context *ctx)
{
- struct dcn20_mpc *mpc20 = kzalloc(sizeof(struct dcn20_mpc),
- GFP_ATOMIC);
+ struct dcn20_mpc *mpc20 = kzalloc(sizeof(struct dcn20_mpc), GFP_KERNEL);
if (!mpc20)
return NULL;
@@ -853,8 +855,7 @@ struct mpc *dcn20_mpc_create(struct dc_context *ctx)
struct hubbub *dcn20_hubbub_create(struct dc_context *ctx)
{
int i;
- struct dcn20_hubbub *hubbub = kzalloc(sizeof(struct dcn20_hubbub),
- GFP_ATOMIC);
+ struct dcn20_hubbub *hubbub = kzalloc(sizeof(struct dcn20_hubbub), GFP_KERNEL);
if (!hubbub)
return NULL;
@@ -882,7 +883,7 @@ struct timing_generator *dcn20_timing_generator_create(
uint32_t instance)
{
struct optc *tgn10 =
- kzalloc(sizeof(struct optc), GFP_ATOMIC);
+ kzalloc(sizeof(struct optc), GFP_KERNEL);
if (!tgn10)
return NULL;
@@ -962,7 +963,7 @@ static struct clock_source *dcn20_clock_source_create(
bool dp_clk_src)
{
struct dce110_clk_src *clk_src =
- kzalloc(sizeof(struct dce110_clk_src), GFP_ATOMIC);
+ kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL);
if (!clk_src)
return NULL;
@@ -1061,7 +1062,7 @@ struct display_stream_compressor *dcn20_dsc_create(
struct dc_context *ctx, uint32_t inst)
{
struct dcn20_dsc *dsc =
- kzalloc(sizeof(struct dcn20_dsc), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn20_dsc), GFP_KERNEL);
if (!dsc) {
BREAK_TO_DEBUGGER();
@@ -1198,7 +1199,7 @@ struct hubp *dcn20_hubp_create(
uint32_t inst)
{
struct dcn20_hubp *hubp2 =
- kzalloc(sizeof(struct dcn20_hubp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn20_hubp), GFP_KERNEL);
if (!hubp2)
return NULL;
@@ -1668,6 +1669,7 @@ bool dcn20_validate_dsc(struct dc *dc, struct dc_state *new_ctx)
dsc_cfg.is_odm = pipe_ctx->next_odm_pipe ? true : false;
dsc_cfg.dc_dsc_cfg = stream->timing.dsc_cfg;
dsc_cfg.dc_dsc_cfg.num_slices_h /= opp_cnt;
+ dsc_cfg.dsc_padding = pipe_ctx->dsc_padding_params.dsc_hactive_padding;
if (!pipe_ctx->stream_res.dsc->funcs->dsc_validate_stream(pipe_ctx->stream_res.dsc, &dsc_cfg))
return false;
@@ -2286,7 +2288,7 @@ bool dcn20_mmhubbub_create(struct dc_context *ctx, struct resource_pool *pool)
static struct pp_smu_funcs *dcn20_pp_smu_create(struct dc_context *ctx)
{
- struct pp_smu_funcs *pp_smu = kzalloc(sizeof(*pp_smu), GFP_ATOMIC);
+ struct pp_smu_funcs *pp_smu = kzalloc(sizeof(*pp_smu), GFP_KERNEL);
if (!pp_smu)
return pp_smu;
@@ -2472,6 +2474,7 @@ static bool dcn20_resource_construct(
dc->caps.color.mpc.ocsc = 1;
dc->caps.dp_hdmi21_pcon_support = true;
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
@@ -2765,7 +2768,7 @@ struct resource_pool *dcn20_create_resource_pool(
struct dc *dc)
{
struct dcn20_resource_pool *pool =
- kzalloc(sizeof(struct dcn20_resource_pool), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn20_resource_pool), GFP_KERNEL);
if (!pool)
return NULL;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c
index e4a1338d21e0..055107843a70 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn201/dcn201_resource.c
@@ -614,10 +614,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.sanity_checks = false,
.underflow_assert_delay_us = 0xFFFFFFFF,
.enable_tri_buf = true,
- .enable_legacy_fast_update = true,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static void dcn201_dpp_destroy(struct dpp **dpp)
{
kfree(TO_DCN201_DPP(*dpp));
@@ -629,7 +632,7 @@ static struct dpp *dcn201_dpp_create(
uint32_t inst)
{
struct dcn201_dpp *dpp =
- kzalloc(sizeof(struct dcn201_dpp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn201_dpp), GFP_KERNEL);
if (!dpp)
return NULL;
@@ -646,7 +649,7 @@ static struct input_pixel_processor *dcn201_ipp_create(
struct dc_context *ctx, uint32_t inst)
{
struct dcn10_ipp *ipp =
- kzalloc(sizeof(struct dcn10_ipp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn10_ipp), GFP_KERNEL);
if (!ipp) {
return NULL;
@@ -662,7 +665,7 @@ static struct output_pixel_processor *dcn201_opp_create(
struct dc_context *ctx, uint32_t inst)
{
struct dcn201_opp *opp =
- kzalloc(sizeof(struct dcn201_opp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn201_opp), GFP_KERNEL);
if (!opp) {
return NULL;
@@ -677,7 +680,7 @@ static struct dce_aux *dcn201_aux_engine_create(struct dc_context *ctx,
uint32_t inst)
{
struct aux_engine_dce110 *aux_engine =
- kzalloc(sizeof(struct aux_engine_dce110), GFP_ATOMIC);
+ kzalloc(sizeof(struct aux_engine_dce110), GFP_KERNEL);
if (!aux_engine)
return NULL;
@@ -710,7 +713,7 @@ static struct dce_i2c_hw *dcn201_i2c_hw_create(struct dc_context *ctx,
uint32_t inst)
{
struct dce_i2c_hw *dce_i2c_hw =
- kzalloc(sizeof(struct dce_i2c_hw), GFP_ATOMIC);
+ kzalloc(sizeof(struct dce_i2c_hw), GFP_KERNEL);
if (!dce_i2c_hw)
return NULL;
@@ -723,8 +726,7 @@ static struct dce_i2c_hw *dcn201_i2c_hw_create(struct dc_context *ctx,
static struct mpc *dcn201_mpc_create(struct dc_context *ctx, uint32_t num_mpcc)
{
- struct dcn201_mpc *mpc201 = kzalloc(sizeof(struct dcn201_mpc),
- GFP_ATOMIC);
+ struct dcn201_mpc *mpc201 = kzalloc(sizeof(struct dcn201_mpc), GFP_KERNEL);
if (!mpc201)
return NULL;
@@ -740,8 +742,7 @@ static struct mpc *dcn201_mpc_create(struct dc_context *ctx, uint32_t num_mpcc)
static struct hubbub *dcn201_hubbub_create(struct dc_context *ctx)
{
- struct dcn20_hubbub *hubbub = kzalloc(sizeof(struct dcn20_hubbub),
- GFP_ATOMIC);
+ struct dcn20_hubbub *hubbub = kzalloc(sizeof(struct dcn20_hubbub), GFP_KERNEL);
if (!hubbub)
return NULL;
@@ -759,7 +760,7 @@ static struct timing_generator *dcn201_timing_generator_create(
uint32_t instance)
{
struct optc *tgn10 =
- kzalloc(sizeof(struct optc), GFP_ATOMIC);
+ kzalloc(sizeof(struct optc), GFP_KERNEL);
if (!tgn10)
return NULL;
@@ -793,7 +794,7 @@ static struct link_encoder *dcn201_link_encoder_create(
const struct encoder_init_data *enc_init_data)
{
struct dcn20_link_encoder *enc20 =
- kzalloc(sizeof(struct dcn20_link_encoder), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL);
struct dcn10_link_encoder *enc10;
if (!enc20 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
@@ -821,7 +822,7 @@ static struct clock_source *dcn201_clock_source_create(
bool dp_clk_src)
{
struct dce110_clk_src *clk_src =
- kzalloc(sizeof(struct dce110_clk_src), GFP_ATOMIC);
+ kzalloc(sizeof(struct dce110_clk_src), GFP_KERNEL);
if (!clk_src)
return NULL;
@@ -856,7 +857,7 @@ static struct stream_encoder *dcn201_stream_encoder_create(
struct dc_context *ctx)
{
struct dcn10_stream_encoder *enc1 =
- kzalloc(sizeof(struct dcn10_stream_encoder), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn10_stream_encoder), GFP_KERNEL);
if (!enc1)
return NULL;
@@ -883,7 +884,7 @@ static const struct dce_hwseq_mask hwseq_mask = {
static struct dce_hwseq *dcn201_hwseq_create(
struct dc_context *ctx)
{
- struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_ATOMIC);
+ struct dce_hwseq *hws = kzalloc(sizeof(struct dce_hwseq), GFP_KERNEL);
if (hws) {
hws->ctx = ctx;
@@ -983,7 +984,7 @@ static struct hubp *dcn201_hubp_create(
uint32_t inst)
{
struct dcn201_hubp *hubp201 =
- kzalloc(sizeof(struct dcn201_hubp), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn201_hubp), GFP_KERNEL);
if (!hubp201)
return NULL;
@@ -1153,6 +1154,7 @@ static bool dcn201_resource_construct(
dc->caps.color.mpc.ocsc = 1;
dc->debug = debug_defaults_drv;
+ dc->check_config = config_defaults;
/*a0 only, remove later*/
dc->work_arounds.no_connect_phy_config = true;
@@ -1303,7 +1305,7 @@ struct resource_pool *dcn201_create_resource_pool(
struct dc *dc)
{
struct dcn201_resource_pool *pool =
- kzalloc(sizeof(struct dcn201_resource_pool), GFP_ATOMIC);
+ kzalloc(sizeof(struct dcn201_resource_pool), GFP_KERNEL);
if (!pool)
return NULL;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c
index 918742a42ded..2060acd5ae09 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn21/dcn21_resource.c
@@ -626,10 +626,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.usbc_combo_phy_reset_wa = true,
.dmub_command_table = true,
.use_max_lb = true,
- .enable_legacy_fast_update = true,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1458,6 +1461,7 @@ static bool dcn21_resource_construct(
dc->caps.color.mpc.ocsc = 1;
dc->caps.dp_hdmi21_pcon_support = true;
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
index ff63f59ff928..d0ebb733e802 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c
@@ -727,10 +727,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.dmub_command_table = true,
.use_max_lb = true,
.exit_idle_opt_for_cursor_updates = true,
- .enable_legacy_fast_update = false,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = false,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -2374,6 +2377,7 @@ static bool dcn30_resource_construct(
dc->caps.vbios_lttpr_aware = (bp_query_result == BP_RESULT_OK) && !!is_vbios_interop_enabled;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c
index 82a205a7c25c..3ad6a3d4858e 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn301/dcn301_resource.c
@@ -701,10 +701,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.dmub_command_table = true,
.use_max_lb = false,
.exit_idle_opt_for_cursor_updates = true,
- .enable_legacy_fast_update = true,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static void dcn301_dpp_destroy(struct dpp **dpp)
{
kfree(TO_DCN20_DPP(*dpp));
@@ -1498,6 +1501,7 @@ static bool dcn301_resource_construct(
bp_query_result = ctx->dc_bios->funcs->get_lttpr_interop(ctx->dc_bios, &is_vbios_interop_enabled);
dc->caps.vbios_lttpr_aware = (bp_query_result == BP_RESULT_OK) && !!is_vbios_interop_enabled;
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
index 61623cb518d9..c0d4a1dc94f8 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c
@@ -98,10 +98,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.dmub_command_table = true,
.use_max_lb = true,
.exit_idle_opt_for_cursor_updates = true,
- .enable_legacy_fast_update = false,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = false,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1290,6 +1293,7 @@ static bool dcn302_resource_construct(
&is_vbios_interop_enabled);
dc->caps.vbios_lttpr_aware = (bp_query_result == BP_RESULT_OK) && !!is_vbios_interop_enabled;
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
index 02b9a84f2db3..75e09c2c283e 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c
@@ -98,10 +98,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.dmub_command_table = true,
.use_max_lb = true,
.exit_idle_opt_for_cursor_updates = true,
- .enable_legacy_fast_update = false,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = false,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1234,6 +1237,7 @@ static bool dcn303_resource_construct(
bp_query_result = ctx->dc_bios->funcs->get_lttpr_interop(ctx->dc_bios, &is_vbios_interop_enabled);
dc->caps.vbios_lttpr_aware = (bp_query_result == BP_RESULT_OK) && !!is_vbios_interop_enabled;
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
index 3ed7f50554e2..0d667b54ccf8 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c
@@ -888,12 +888,15 @@ static const struct dc_debug_options debug_defaults_drv = {
}
},
.disable_z10 = true,
- .enable_legacy_fast_update = true,
.enable_z9_disable_interface = true, /* Allow support for the PMFW interface for disable Z9*/
.dml_hostvm_override = DML_HOSTVM_OVERRIDE_FALSE,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1978,6 +1981,7 @@ static bool dcn31_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
index d4917a35b991..3ccde75a4ecb 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c
@@ -924,12 +924,15 @@ static const struct dc_debug_options debug_defaults_drv = {
},
.seamless_boot_odm_combine = true,
- .enable_legacy_fast_update = true,
.using_dml2 = false,
.disable_dsc_power_gate = true,
.min_disp_clk_khz = 100000,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1910,6 +1913,7 @@ static bool dcn314_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
index 82cc78c291d8..4e962f522f1b 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c
@@ -887,9 +887,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.afmt = true,
}
},
- .enable_legacy_fast_update = true,
.psr_power_use_phy_fsm = 0,
.using_dml2 = false,
+ .min_disp_clk_khz = 100000,
+};
+
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
};
static const struct dc_panel_config panel_config_defaults = {
@@ -1939,6 +1943,7 @@ static bool dcn315_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
index 636110e48d01..5a95dd54cb42 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c
@@ -882,10 +882,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.afmt = true,
}
},
- .enable_legacy_fast_update = true,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1815,6 +1818,7 @@ static bool dcn316_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
index 3965a7f1b64b..b276fec3e479 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c
@@ -92,7 +92,7 @@
#include "dc_state_priv.h"
-#include "dml2/dml2_wrapper.h"
+#include "dml2_0/dml2_wrapper.h"
#define DC_LOGGER_INIT(logger)
@@ -738,10 +738,13 @@ static const struct dc_debug_options debug_defaults_drv = {
.disable_dp_plus_plus_wa = true,
.fpo_vactive_min_active_margin_us = 200,
.fpo_vactive_max_blank_us = 1000,
- .enable_legacy_fast_update = false,
.disable_stutter_for_wm_program = true
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = false,
+};
+
static struct dce_aux *dcn32_aux_engine_create(
struct dc_context *ctx,
uint32_t inst)
@@ -1844,7 +1847,7 @@ enum dc_status dcn32_validate_bandwidth(struct dc *dc,
dc_state_set_stream_cursor_subvp_limit(stream, context, true);
status = DC_FAIL_HW_CURSOR_SUPPORT;
}
- };
+ }
}
if (validate_mode == DC_VALIDATE_MODE_AND_PROGRAMMING && status == DC_FAIL_HW_CURSOR_SUPPORT) {
@@ -2197,7 +2200,8 @@ static bool dcn32_resource_construct(
dc->caps.i2c_speed_in_khz_hdcp = 100; /*1.4 w/a applied by default*/
/* TODO: Bring max_cursor_size back to 256 after subvp cursor corruption is fixed*/
dc->caps.max_cursor_size = 64;
- dc->caps.max_buffered_cursor_size = 64; // sqrt(16 * 1024 / 4)
+ /* floor(sqrt(buf_size_bytes / bpp ) * bpp, fixed_req_size) / bpp = max_width */
+ dc->caps.max_buffered_cursor_size = 64; // floor(sqrt(16 * 1024 / 4) * 4, 256) / 4 = 64
dc->caps.min_horizontal_blanking_period = 80;
dc->caps.dmdata_alloc_size = 2048;
dc->caps.mall_size_per_mem_channel = 4;
@@ -2294,6 +2298,7 @@ static bool dcn32_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
index ad214986f7ac..3466ca34c93f 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c
@@ -731,11 +731,14 @@ static const struct dc_debug_options debug_defaults_drv = {
.disable_subvp_high_refresh = false,
.fpo_vactive_min_active_margin_us = 200,
.fpo_vactive_max_blank_us = 1000,
- .enable_legacy_fast_update = false,
.disable_dc_mode_overwrite = true,
.using_dml2 = false,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = false,
+};
+
static struct dce_aux *dcn321_aux_engine_create(
struct dc_context *ctx,
uint32_t inst)
@@ -1797,6 +1800,7 @@ static bool dcn321_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
index fff57f23f4f7..ef69898d2cc5 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c
@@ -33,7 +33,7 @@
#include "resource.h"
#include "include/irq_service_interface.h"
#include "dcn35_resource.h"
-#include "dml2/dml2_wrapper.h"
+#include "dml2_0/dml2_wrapper.h"
#include "dcn20/dcn20_resource.h"
#include "dcn30/dcn30_resource.h"
@@ -767,7 +767,6 @@ static const struct dc_debug_options debug_defaults_drv = {
.using_dml2 = true,
.support_eDP1_5 = true,
.enable_hpo_pg_support = false,
- .enable_legacy_fast_update = true,
.enable_single_display_2to1_odm_policy = true,
.disable_idle_power_optimizations = false,
.dmcub_emulation = false,
@@ -788,6 +787,10 @@ static const struct dc_debug_options debug_defaults_drv = {
.min_disp_clk_khz = 50000,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1946,6 +1949,7 @@ static bool dcn35_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
index 0abd163b425e..f3c614c4490c 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c
@@ -83,7 +83,7 @@
#include "vm_helper.h"
#include "dcn20/dcn20_vmid.h"
-#include "dml2/dml2_wrapper.h"
+#include "dml2_0/dml2_wrapper.h"
#include "link_enc_cfg.h"
#define DC_LOGGER_INIT(logger)
@@ -747,7 +747,6 @@ static const struct dc_debug_options debug_defaults_drv = {
.using_dml2 = true,
.support_eDP1_5 = true,
.enable_hpo_pg_support = false,
- .enable_legacy_fast_update = true,
.enable_single_display_2to1_odm_policy = true,
.disable_idle_power_optimizations = false,
.dmcub_emulation = false,
@@ -768,6 +767,10 @@ static const struct dc_debug_options debug_defaults_drv = {
.min_disp_clk_khz = 50000,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1917,6 +1920,7 @@ static bool dcn351_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
index ca125ee6c2fb..6469d5fe2e6d 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c
@@ -11,7 +11,7 @@
#include "resource.h"
#include "include/irq_service_interface.h"
#include "dcn36_resource.h"
-#include "dml2/dml2_wrapper.h"
+#include "dml2_0/dml2_wrapper.h"
#include "dcn20/dcn20_resource.h"
#include "dcn30/dcn30_resource.h"
@@ -748,7 +748,6 @@ static const struct dc_debug_options debug_defaults_drv = {
.using_dml2 = true,
.support_eDP1_5 = true,
.enable_hpo_pg_support = false,
- .enable_legacy_fast_update = true,
.enable_single_display_2to1_odm_policy = true,
.disable_idle_power_optimizations = false,
.dmcub_emulation = false,
@@ -769,6 +768,10 @@ static const struct dc_debug_options debug_defaults_drv = {
.min_disp_clk_khz = 50000,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = true,
+};
+
static const struct dc_panel_config panel_config_defaults = {
.psr = {
.disable_psr = false,
@@ -1918,6 +1921,7 @@ static bool dcn36_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c
index 1d18807e4749..875ae97489d3 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.c
@@ -73,7 +73,7 @@
#include "dc_state_priv.h"
-#include "dml2/dml2_wrapper.h"
+#include "dml2_0/dml2_wrapper.h"
#define DC_LOGGER_INIT(logger)
@@ -721,7 +721,6 @@ static const struct dc_debug_options debug_defaults_drv = {
.alloc_extra_way_for_cursor = true,
.min_prefetch_in_strobe_ns = 60000, // 60us
.disable_unbounded_requesting = false,
- .enable_legacy_fast_update = false,
.dcc_meta_propagation_delay_us = 10,
.fams_version = {
.minor = 1,
@@ -737,6 +736,10 @@ static const struct dc_debug_options debug_defaults_drv = {
.force_cositing = CHROMA_COSITING_NONE + 1,
};
+static const struct dc_check_config config_defaults = {
+ .enable_legacy_fast_update = false,
+};
+
static struct dce_aux *dcn401_aux_engine_create(
struct dc_context *ctx,
uint32_t inst)
@@ -1668,7 +1671,7 @@ enum dc_status dcn401_validate_bandwidth(struct dc *dc,
dc_state_set_stream_cursor_subvp_limit(stream, context, true);
status = DC_FAIL_HW_CURSOR_SUPPORT;
}
- };
+ }
}
if (validate_mode == DC_VALIDATE_MODE_AND_PROGRAMMING && status == DC_FAIL_HW_CURSOR_SUPPORT) {
@@ -1995,6 +1998,7 @@ static bool dcn401_resource_construct(
dc->caps.vbios_lttpr_aware = true;
}
}
+ dc->check_config = config_defaults;
if (dc->ctx->dce_environment == DCE_ENV_PRODUCTION_DRV)
dc->debug = debug_defaults_drv;
diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h
index 0fc66487d800..e1fa2e80a15a 100644
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn401/dcn401_resource.h
@@ -227,7 +227,8 @@ void dcn401_prepare_mcache_programming(struct dc *dc, struct dc_state *context);
#define LE_DCN401_REG_LIST_RI(id) \
LE_DCN3_REG_LIST_RI(id), \
SRI_ARR(DP_DPHY_INTERNAL_CTRL, DP, id), \
- SRI_ARR(DIG_BE_CLK_CNTL, DIG, id)
+ SRI_ARR(DIG_BE_CLK_CNTL, DIG, id),\
+ SR_ARR(DIO_CLK_CNTL, id)
/* DPP */
#define DPP_REG_LIST_DCN401_COMMON_RI(id) \
diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h
index 21d842857601..88c11b6be004 100644
--- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h
+++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h
@@ -9,7 +9,7 @@
#include "dc.h"
#include "clk_mgr.h"
#include "soc_and_ip_translator.h"
-#include "dml2/dml21/inc/dml_top_soc_parameter_types.h"
+#include "dml2_0/dml21/inc/dml_top_soc_parameter_types.h"
void dcn401_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator);
diff --git a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c
index b1fb0f8a253a..7a839984dbc0 100644
--- a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c
+++ b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl.c
@@ -1018,6 +1018,21 @@ static bool spl_get_optimal_number_of_taps(
spl_scratch->scl_data.taps.h_taps_c = 6;
spl_scratch->scl_data.taps.v_taps_c = 6;
}
+
+ /* Override mode: keep EASF enabled but use input taps if valid */
+ if (spl_in->override_easf) {
+ spl_scratch->scl_data.taps.h_taps = (in_taps->h_taps != 0) ? in_taps->h_taps : spl_scratch->scl_data.taps.h_taps;
+ spl_scratch->scl_data.taps.v_taps = (in_taps->v_taps != 0) ? in_taps->v_taps : spl_scratch->scl_data.taps.v_taps;
+ spl_scratch->scl_data.taps.h_taps_c = (in_taps->h_taps_c != 0) ? in_taps->h_taps_c : spl_scratch->scl_data.taps.h_taps_c;
+ spl_scratch->scl_data.taps.v_taps_c = (in_taps->v_taps_c != 0) ? in_taps->v_taps_c : spl_scratch->scl_data.taps.v_taps_c;
+
+ if ((spl_scratch->scl_data.taps.h_taps > 6) || (spl_scratch->scl_data.taps.v_taps > 6))
+ skip_easf = true;
+ if ((spl_scratch->scl_data.taps.h_taps > 1) && (spl_scratch->scl_data.taps.h_taps % 2))
+ spl_scratch->scl_data.taps.h_taps--;
+ if ((spl_scratch->scl_data.taps.h_taps_c > 1) && (spl_scratch->scl_data.taps.h_taps_c % 2))
+ spl_scratch->scl_data.taps.h_taps_c--;
+ }
}
/*Ensure we can support the requested number of vtaps*/
diff --git a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h
index 23d254dea18f..20e4e52a77ac 100644
--- a/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h
+++ b/drivers/gpu/drm/amd/display/dc/sspl/dc_spl_types.h
@@ -545,6 +545,7 @@ struct spl_in {
enum linear_light_scaling lls_pref; // Linear Light Scaling
bool prefer_easf;
bool disable_easf;
+ bool override_easf; /* If true, keep EASF enabled but use provided in_taps */
struct spl_debug debug;
bool is_fullscreen;
bool is_hdr_on;
diff --git a/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c
index 6ffc74fc9dcd..ad088d70e189 100644
--- a/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c
+++ b/drivers/gpu/drm/amd/display/dc/virtual/virtual_stream_encoder.c
@@ -44,11 +44,6 @@ static void virtual_stream_encoder_dvi_set_stream_attribute(
struct dc_crtc_timing *crtc_timing,
bool is_dual_link) {}
-static void virtual_stream_encoder_lvds_set_stream_attribute(
- struct stream_encoder *enc,
- struct dc_crtc_timing *crtc_timing)
-{}
-
static void virtual_stream_encoder_set_throttled_vcp_size(
struct stream_encoder *enc,
struct fixed31_32 avg_time_slots_per_mtp)
@@ -120,8 +115,6 @@ static const struct stream_encoder_funcs virtual_str_enc_funcs = {
virtual_stream_encoder_hdmi_set_stream_attribute,
.dvi_set_stream_attribute =
virtual_stream_encoder_dvi_set_stream_attribute,
- .lvds_set_stream_attribute =
- virtual_stream_encoder_lvds_set_stream_attribute,
.set_throttled_vcp_size =
virtual_stream_encoder_set_throttled_vcp_size,
.update_hdmi_info_packets =
diff --git a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h
index 338fdc651f2c..9d0168986fe7 100644
--- a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h
+++ b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h
@@ -132,6 +132,7 @@ enum dmub_window_id {
DMUB_WINDOW_IB_MEM,
DMUB_WINDOW_SHARED_STATE,
DMUB_WINDOW_LSDMA_BUFFER,
+ DMUB_WINDOW_CURSOR_OFFLOAD,
DMUB_WINDOW_TOTAL,
};
@@ -317,6 +318,7 @@ struct dmub_srv_hw_params {
bool enable_non_transparent_setconfig;
bool lower_hbr3_phy_ssc;
bool override_hbr3_pll_vco;
+ bool disable_dpia_bw_allocation;
};
/**
@@ -361,6 +363,19 @@ struct dmub_diagnostic_data {
uint8_t is_pwait : 1;
};
+/**
+ * struct dmub_preos_info - preos fw info before loading post os fw.
+ */
+struct dmub_preos_info {
+ uint64_t fb_base;
+ uint64_t fb_offset;
+ uint64_t trace_buffer_phy_addr;
+ uint32_t trace_buffer_size;
+ uint32_t fw_version;
+ uint32_t boot_status;
+ uint32_t boot_options;
+};
+
struct dmub_srv_inbox {
/* generic status */
uint64_t num_submitted;
@@ -486,6 +501,7 @@ struct dmub_srv_hw_funcs {
uint32_t (*get_current_time)(struct dmub_srv *dmub);
void (*get_diagnostic_data)(struct dmub_srv *dmub);
+ bool (*get_preos_fw_info)(struct dmub_srv *dmub);
bool (*should_detect)(struct dmub_srv *dmub);
void (*init_reg_offsets)(struct dmub_srv *dmub, struct dc_context *ctx);
@@ -535,7 +551,8 @@ struct dmub_srv_create_params {
* @fw_version: the current firmware version, if any
* @is_virtual: false if hardware support only
* @shared_state: dmub shared state between firmware and driver
- * @fw_state: dmub firmware state pointer
+ * @cursor_offload_v1: Cursor offload state
+ * @fw_state: dmub firmware state pointer (debug purpose only)
*/
struct dmub_srv {
enum dmub_asic asic;
@@ -544,7 +561,9 @@ struct dmub_srv {
bool is_virtual;
struct dmub_fb scratch_mem_fb;
struct dmub_fb ib_mem_gart;
+ struct dmub_fb cursor_offload_fb;
volatile struct dmub_shared_state_feature_block *shared_state;
+ volatile struct dmub_cursor_offload_v1 *cursor_offload_v1;
volatile const struct dmub_fw_state *fw_state;
/* private: internal use only */
@@ -583,6 +602,7 @@ struct dmub_srv {
enum dmub_srv_power_state_type power_state;
struct dmub_diagnostic_data debug;
struct dmub_fb lsdma_rb_fb;
+ struct dmub_preos_info preos_info;
};
/**
@@ -1068,4 +1088,14 @@ enum dmub_status dmub_srv_wait_for_inbox_free(struct dmub_srv *dmub,
*/
enum dmub_status dmub_srv_update_inbox_status(struct dmub_srv *dmub);
+/**
+ * dmub_srv_get_preos_info() - retrieves preos fw info
+ * @dmub: the dmub service
+ *
+ * Return:
+ * true - preos fw info retrieved successfully
+ * false - preos fw info not retrieved successfully
+ */
+bool dmub_srv_get_preos_info(struct dmub_srv *dmub);
+
#endif /* _DMUB_SRV_H_ */
diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
index 92248224b713..3f2a0ed02c59 100644
--- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
+++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h
@@ -485,7 +485,19 @@ union replay_debug_flags {
*/
uint32_t enable_visual_confirm_debug : 1;
- uint32_t reserved : 18;
+ /**
+ * 0x4000 (bit 14)
+ * @debug_log_enabled: Debug Log Enabled
+ */
+ uint32_t debug_log_enabled : 1;
+
+ /**
+ * 0x8000 (bit 15)
+ * @enable_sub_feature_visual_confirm: Enable Sub Feature Visual Confirm
+ */
+ uint32_t enable_sub_feature_visual_confirm : 1;
+
+ uint32_t reserved : 16;
} bitfields;
uint32_t u32All;
@@ -593,6 +605,104 @@ union replay_hw_flags {
uint32_t u32All;
};
+/**
+ * Flags that can be set by driver to change some Panel Replay behaviour.
+ */
+union pr_debug_flags {
+ struct {
+ /**
+ * 0x1 (bit 0)
+ * Enable visual confirm in FW.
+ */
+ uint32_t visual_confirm : 1;
+
+ /**
+ * 0x2 (bit 1)
+ * @skip_crc: Set if need to skip CRC.
+ */
+ uint32_t skip_crc : 1;
+
+ /**
+ * 0x4 (bit 2)
+ * @force_link_power_on: Force disable ALPM control
+ */
+ uint32_t force_link_power_on : 1;
+
+ /**
+ * 0x8 (bit 3)
+ * @force_phy_power_on: Force phy power on
+ */
+ uint32_t force_phy_power_on : 1;
+
+ /**
+ * 0x10 (bit 4)
+ * @skip_crtc_disabled: CRTC disable skipped
+ */
+ uint32_t skip_crtc_disabled : 1;
+
+ /*
+ * 0x20 (bit 5)
+ * @visual_confirm_rate_control: Enable Visual Confirm rate control detection
+ */
+ uint32_t visual_confirm_rate_control : 1;
+
+ uint32_t reserved : 26;
+ } bitfields;
+
+ uint32_t u32All;
+};
+
+union pr_hw_flags {
+ struct {
+ /**
+ * @allow_alpm_fw_standby_mode: To indicate whether the
+ * ALPM FW standby mode is allowed
+ */
+ uint32_t allow_alpm_fw_standby_mode : 1;
+
+ /*
+ * @dsc_enable_status: DSC enable status in driver
+ */
+ uint32_t dsc_enable_status : 1;
+
+ /**
+ * @fec_enable_status: receive fec enable/disable status from driver
+ */
+ uint32_t fec_enable_status : 1;
+
+ /*
+ * @smu_optimizations_en: SMU power optimization.
+ * Only when active display is Replay capable and display enters Replay.
+ * Trigger interrupt to SMU to powerup/down.
+ */
+ uint32_t smu_optimizations_en : 1;
+
+ /**
+ * @phy_power_state: Indicates current phy power state
+ */
+ uint32_t phy_power_state : 1;
+
+ /**
+ * @link_power_state: Indicates current link power state
+ */
+ uint32_t link_power_state : 1;
+ /**
+ * Use TPS3 signal when restore main link.
+ */
+ uint32_t force_wakeup_by_tps3 : 1;
+ /**
+ * @is_alpm_initialized: Indicates whether ALPM is initialized
+ */
+ uint32_t is_alpm_initialized : 1;
+ /**
+ * @alpm_mode: Indicates ALPM mode selected
+ */
+ uint32_t alpm_mode : 2;
+ } bitfields;
+
+ uint32_t u32All;
+};
+
union fw_assisted_mclk_switch_version {
struct {
uint8_t minor : 5;
@@ -617,6 +727,7 @@ struct dmub_feature_caps {
uint8_t replay_supported;
uint8_t replay_reserved[3];
uint8_t abm_aux_backlight_support;
+ uint8_t lsdma_support_in_dmu;
};
struct dmub_visual_confirm_color {
@@ -629,6 +740,112 @@ struct dmub_visual_confirm_color {
uint16_t panel_inst;
};
+/**
+ * struct dmub_cursor_offload_pipe_data_dcn30_v1 - DCN30+ per pipe data.
+ */
+struct dmub_cursor_offload_pipe_data_dcn30_v1 {
+ uint32_t CURSOR0_0_CURSOR_SURFACE_ADDRESS;
+ uint32_t CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH;
+ uint32_t CURSOR0_0_CURSOR_SIZE__CURSOR_WIDTH : 16;
+ uint32_t CURSOR0_0_CURSOR_SIZE__CURSOR_HEIGHT : 16;
+ uint32_t CURSOR0_0_CURSOR_POSITION__CURSOR_X_POSITION : 16;
+ uint32_t CURSOR0_0_CURSOR_POSITION__CURSOR_Y_POSITION : 16;
+ uint32_t CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_X : 16;
+ uint32_t CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_Y : 16;
+ uint32_t CURSOR0_0_CURSOR_DST_OFFSET__CURSOR_DST_X_OFFSET : 13;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_ENABLE : 1;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_MODE : 3;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY : 1;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_PITCH : 2;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_LINES_PER_CHUNK : 5;
+ uint32_t reserved0[4];
+ uint32_t CNVC_CUR0_CURSOR0_CONTROL__CUR0_ENABLE : 1;
+ uint32_t CNVC_CUR0_CURSOR0_CONTROL__CUR0_MODE : 3;
+ uint32_t CNVC_CUR0_CURSOR0_CONTROL__CUR0_EXPANSION_MODE : 1;
+ uint32_t CNVC_CUR0_CURSOR0_CONTROL__CUR0_ROM_EN : 1;
+ uint32_t CNVC_CUR0_CURSOR0_COLOR0__CUR0_COLOR0 : 24;
+ uint32_t CNVC_CUR0_CURSOR0_COLOR1__CUR0_COLOR1 : 24;
+ uint32_t CNVC_CUR0_CURSOR0_FP_SCALE_BIAS__CUR0_FP_BIAS : 16;
+ uint32_t CNVC_CUR0_CURSOR0_FP_SCALE_BIAS__CUR0_FP_SCALE, : 16;
+ uint32_t reserved1[5];
+ uint32_t HUBPREQ0_CURSOR_SETTINGS__CURSOR0_DST_Y_OFFSET : 8;
+ uint32_t HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST : 8;
+ uint32_t reserved2[3];
+};
+
+/**
+ * struct dmub_cursor_offload_pipe_data_dcn401_v1 - DCN401 per pipe data.
+ */
+struct dmub_cursor_offload_pipe_data_dcn401_v1 {
+ uint32_t CURSOR0_0_CURSOR_SURFACE_ADDRESS;
+ uint32_t CURSOR0_0_CURSOR_SURFACE_ADDRESS_HIGH;
+ uint32_t CURSOR0_0_CURSOR_SIZE__CURSOR_WIDTH : 16;
+ uint32_t CURSOR0_0_CURSOR_SIZE__CURSOR_HEIGHT : 16;
+ uint32_t CURSOR0_0_CURSOR_POSITION__CURSOR_X_POSITION : 16;
+ uint32_t CURSOR0_0_CURSOR_POSITION__CURSOR_Y_POSITION : 16;
+ uint32_t CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_X : 16;
+ uint32_t CURSOR0_0_CURSOR_HOT_SPOT__CURSOR_HOT_SPOT_Y : 16;
+ uint32_t CURSOR0_0_CURSOR_DST_OFFSET__CURSOR_DST_X_OFFSET : 13;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_ENABLE : 1;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_MODE : 3;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_2X_MAGNIFY : 1;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_PITCH : 2;
+ uint32_t CURSOR0_0_CURSOR_CONTROL__CURSOR_LINES_PER_CHUNK : 5;
+ uint32_t reserved0[4];
+ uint32_t CM_CUR0_CURSOR0_CONTROL__CUR0_ENABLE : 1;
+ uint32_t CM_CUR0_CURSOR0_CONTROL__CUR0_MODE : 3;
+ uint32_t CM_CUR0_CURSOR0_CONTROL__CUR0_EXPANSION_MODE : 1;
+ uint32_t CM_CUR0_CURSOR0_CONTROL__CUR0_ROM_EN : 1;
+ uint32_t CM_CUR0_CURSOR0_COLOR0__CUR0_COLOR0 : 24;
+ uint32_t CM_CUR0_CURSOR0_COLOR1__CUR0_COLOR1 : 24;
+ uint32_t CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_BIAS_G_Y : 16;
+ uint32_t CM_CUR0_CURSOR0_FP_SCALE_BIAS_G_Y__CUR0_FP_SCALE_G_Y, : 16;
+ uint32_t CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_BIAS_RB_CRCB : 16;
+ uint32_t CM_CUR0_CURSOR0_FP_SCALE_BIAS_RB_CRCB__CUR0_FP_SCALE_RB_CRCB : 16;
+ uint32_t reserved1[4];
+ uint32_t HUBPREQ0_CURSOR_SETTINGS__CURSOR0_DST_Y_OFFSET : 8;
+ uint32_t HUBPREQ0_CURSOR_SETTINGS__CURSOR0_CHUNK_HDL_ADJUST : 8;
+ uint32_t HUBP0_DCHUBP_MALL_CONFIG__USE_MALL_FOR_CURSOR : 1;
+ uint32_t reserved2[3];
+};
+
+/**
+ * struct dmub_cursor_offload_pipe_data_v1 - Per pipe data for cursor offload.
+ */
+struct dmub_cursor_offload_pipe_data_v1 {
+ union {
+ struct dmub_cursor_offload_pipe_data_dcn30_v1 dcn30; /**< DCN30 cursor data. */
+ struct dmub_cursor_offload_pipe_data_dcn401_v1 dcn401; /**< DCN401 cursor data. */
+ uint8_t payload[96]; /**< Guarantees the cursor pipe data size per-pipe. */
+ };
+};
+
+/**
+ * struct dmub_cursor_offload_payload_data_v1 - A payload of stream data.
+ */
+struct dmub_cursor_offload_payload_data_v1 {
+ uint32_t write_idx_start; /**< Write index, updated before pipe_data is written. */
+ uint32_t write_idx_finish; /**< Write index, updated after pipe_data is written. */
+ uint32_t pipe_mask; /**< Mask of pipes to update. */
+ uint32_t reserved; /**< Reserved for future use. */
+ struct dmub_cursor_offload_pipe_data_v1 pipe_data[6]; /**< Per-pipe cursor data. */
+};
+
+/**
+ * struct dmub_cursor_offload_stream_v1 - Per-stream data for cursor offload.
+ */
+struct dmub_cursor_offload_stream_v1 {
+ struct dmub_cursor_offload_payload_data_v1 payloads[4]; /**< A small buffer of cursor payloads. */
+ uint32_t write_idx; /**< The index of the last written payload. */
+};
+
+/**
+ * struct dmub_cursor_offload_v1 - Cursor offload feature state.
+ */
+struct dmub_cursor_offload_v1 {
+ struct dmub_cursor_offload_stream_v1 offload_streams[6]; /**< Per-stream cursor offload data */
+};
+
//==============================================================================
//</DMUB_TYPES>=================================================================
//==============================================================================
@@ -648,7 +865,8 @@ struct dmub_visual_confirm_color {
union dmub_fw_meta_feature_bits {
struct {
uint32_t shared_state_link_detection : 1; /**< 1 supports link detection via shared state */
- uint32_t reserved : 31;
+ uint32_t cursor_offload_v1_support: 1; /**< 1 supports cursor offload */
+ uint32_t reserved : 30;
} bits; /**< status bits */
uint32_t all; /**< 32-bit access to status bits */
};
@@ -814,6 +1032,28 @@ enum dmub_ips_comand_type {
};
/**
+ * enum dmub_cursor_offload_comand_type - Cursor offload subcommands.
+ */
+enum dmub_cursor_offload_comand_type {
+ /**
+ * Initializes the cursor offload feature.
+ */
+ DMUB_CMD__CURSOR_OFFLOAD_INIT = 0,
+ /**
+ * Enables cursor offloading for a stream and updates the timing parameters.
+ */
+ DMUB_CMD__CURSOR_OFFLOAD_STREAM_ENABLE = 1,
+ /**
+ * Disables cursor offloading for a given stream.
+ */
+ DMUB_CMD__CURSOR_OFFLOAD_STREAM_DISABLE = 2,
+ /**
+ * Programs the latest data for a given stream.
+ */
+ DMUB_CMD__CURSOR_OFFLOAD_STREAM_PROGRAM = 3,
+};
+
+/**
* union dmub_fw_boot_options - Boot option definitions for SCRATCH14
*/
union dmub_fw_boot_options {
@@ -844,7 +1084,8 @@ union dmub_fw_boot_options {
uint32_t disable_sldo_opt: 1; /**< 1 to disable SLDO optimizations */
uint32_t lower_hbr3_phy_ssc: 1; /**< 1 to lower hbr3 phy ssc to 0.125 percent */
uint32_t override_hbr3_pll_vco: 1; /**< 1 to override the hbr3 pll vco to 0 */
- uint32_t reserved : 5; /**< reserved */
+ uint32_t disable_dpia_bw_allocation: 1; /**< 1 to disable the USB4 DPIA BW allocation */
+ uint32_t reserved : 4; /**< reserved */
} bits; /**< boot bits */
uint32_t all; /**< 32-bit access to bits */
};
@@ -877,6 +1118,7 @@ enum dmub_shared_state_feature_id {
DMUB_SHARED_SHARE_FEATURE__IPS_FW = 1,
DMUB_SHARED_SHARE_FEATURE__IPS_DRIVER = 2,
DMUB_SHARED_SHARE_FEATURE__DEBUG_SETUP = 3,
+ DMUB_SHARED_STATE_FEATURE__CURSOR_OFFLOAD_V1 = 4,
DMUB_SHARED_STATE_FEATURE__LAST, /* Total number of features. */
};
@@ -958,6 +1200,22 @@ struct dmub_shared_state_ips_driver {
}; /* 248-bytes, fixed */
/**
+ * struct dmub_shared_state_cursor_offload_v1 - Header metadata for cursor offload.
+ */
+struct dmub_shared_state_cursor_offload_stream_v1 {
+ uint32_t last_write_idx; /**< Last write index */
+ uint8_t reserved[28]; /**< Reserved bytes. */
+}; /* 32-bytes, fixed */
+
+/**
+ * struct dmub_shared_state_cursor_offload_v1 - Header metadata for cursor offload.
+ */
+struct dmub_shared_state_cursor_offload_v1 {
+ struct dmub_shared_state_cursor_offload_stream_v1 offload_streams[6]; /**< stream state, 32-bytes each */
+ uint8_t reserved[56]; /**< reserved for future use */
+}; /* 248-bytes, fixed */
+
+/**
* enum dmub_shared_state_feature_common - Generic payload.
*/
struct dmub_shared_state_feature_common {
@@ -983,6 +1241,7 @@ struct dmub_shared_state_feature_block {
struct dmub_shared_state_ips_fw ips_fw; /**< IPS firmware state */
struct dmub_shared_state_ips_driver ips_driver; /**< IPS driver state */
struct dmub_shared_state_debug_setup debug_setup; /**< Debug setup */
+ struct dmub_shared_state_cursor_offload_v1 cursor_offload_v1; /**< Cursor offload */
} data; /**< Shared state data. */
}; /* 256-bytes, fixed */
@@ -1572,6 +1831,25 @@ enum dmub_cmd_type {
*/
DMUB_CMD__IPS = 91,
+ /**
+ * Command type use for Cursor offload.
+ */
+ DMUB_CMD__CURSOR_OFFLOAD = 92,
+
+ /**
+ * Command type used for all SMART_POWER_OLED commands.
+ */
+ DMUB_CMD__SMART_POWER_OLED = 93,
+
+ /**
+ * Command type use for all Panel Replay commands.
+ */
+ DMUB_CMD__PR = 94,
+
+
+ /**
+ * Command type use for VBIOS shared commands.
+ */
DMUB_CMD__VBIOS = 128,
};
@@ -2369,6 +2647,7 @@ struct dmub_cmd_fams2_global_config {
union dmub_cmd_fams2_config {
struct dmub_cmd_fams2_global_config global;
+// coverity[cert_dcl37_c_violation:FALSE] errno.h, stddef.h, stdint.h not included in atombios.h
struct dmub_fams2_stream_static_state stream; //v0
union {
struct dmub_fams2_cmd_stream_static_base_state base;
@@ -3981,6 +4260,33 @@ enum replay_state {
};
/**
+ * Definition of a panel replay state
+ */
+enum pr_state {
+ PR_STATE_0 = 0x00, // State 0 steady state
+ // Pending SDP and Unlock before back to State 0
+ PR_STATE_0_PENDING_SDP_AND_UNLOCK = 0x01,
+ PR_STATE_1 = 0x10, // State 1
+ PR_STATE_2 = 0x20, // State 2 steady state
+ // Pending frame transmission before transition to State 2
+ PR_STATE_2_PENDING_FRAME_TRANSMISSION = 0x30,
+ // Active and Powered Up
+ PR_STATE_2_POWERED = 0x31,
+ // Active and Powered Down, but need to blank HUBP after DPG_EN latch
+ PR_STATE_2_PENDING_HUBP_BLANK = 0x32,
+ // Active and Pending Power Up
+ PR_STATE_2_PENDING_POWER_UP = 0x33,
+ // Active and Powered Up, Pending DPG latch
+ PR_STATE_2_PENDING_LOCK_FOR_DPG_POWER_ON = 0x34,
+ // Active and Powered Up, Pending SDP and Unlock
+ PR_STATE_2_PENDING_SDP_AND_UNLOCK = 0x35,
+ // Pending transmission of AS SDP for timing sync, but no rfb update
+ PR_STATE_2_PENDING_AS_SDP = 0x36,
+ // Invalid
+ PR_STATE_INVALID = 0xFF,
+};
+
+/**
* Replay command sub-types.
*/
enum dmub_cmd_replay_type {
@@ -4030,6 +4336,25 @@ enum dmub_cmd_replay_type {
DMUB_CMD__REPLAY_SET_GENERAL_CMD = 16,
};
+/*
+ * Panel Replay sub-types
+ */
+enum dmub_cmd_panel_replay_type {
+ DMUB_CMD__PR_ENABLE = 0,
+ DMUB_CMD__PR_COPY_SETTINGS = 1,
+ DMUB_CMD__PR_UPDATE_STATE = 2,
+ DMUB_CMD__PR_GENERAL_CMD = 3,
+};
+
+enum dmub_cmd_panel_replay_state_update_subtype {
+ PR_STATE_UPDATE_COASTING_VTOTAL = 0x1,
+ PR_STATE_UPDATE_SYNC_MODE = 0x2,
+};
+
+enum dmub_cmd_panel_replay_general_subtype {
+ PR_GENERAL_CMD_DEBUG_OPTION = 0x1,
+};
+
/**
* Replay general command sub-types.
*/
@@ -4045,6 +4370,7 @@ enum dmub_cmd_replay_general_subtype {
REPLAY_GENERAL_CMD_DISABLED_DESYNC_ERROR_DETECTION,
REPLAY_GENERAL_CMD_UPDATE_ERROR_STATUS,
REPLAY_GENERAL_CMD_SET_LOW_RR_ACTIVATE,
+ REPLAY_GENERAL_CMD_VIDEO_CONFERENCING,
};
struct dmub_alpm_auxless_data {
@@ -4182,17 +4508,13 @@ struct dmub_cmd_replay_set_version_data {
*/
uint8_t panel_inst;
/**
- * PSR version that FW should implement.
+ * Replay version that FW should implement.
*/
enum replay_version version;
/**
- * PSR control version.
- */
- uint8_t cmd_version;
- /**
* Explicit padding to 4 byte boundary.
*/
- uint8_t pad[2];
+ uint8_t pad[3];
};
/**
@@ -4238,6 +4560,45 @@ enum replay_enable {
};
/**
+ * Data passed from driver to FW in a DMUB_CMD__SMART_POWER_OLED_ENABLE command.
+ */
+struct dmub_rb_cmd_smart_power_oled_enable_data {
+ /**
+ * SMART_POWER_OLED enable or disable.
+ */
+ uint8_t enable;
+ /**
+ * Panel Instance.
+ * Panel isntance to identify which replay_state to use
+ * Currently the support is only for 0 or 1
+ */
+ uint8_t panel_inst;
+
+ uint16_t peak_nits;
+ /**
+ * OTG HW instance.
+ */
+ uint8_t otg_inst;
+ /**
+ * DIG FE HW instance.
+ */
+ uint8_t digfe_inst;
+ /**
+ * DIG BE HW instance.
+ */
+ uint8_t digbe_inst;
+ uint8_t debugcontrol;
+ /*
+ * vertical interrupt trigger line
+ */
+ uint32_t triggerline;
+
+ uint16_t fixed_max_cll;
+
+ uint8_t pad[2];
+};
+
+/**
* Data passed from driver to FW in a DMUB_CMD__REPLAY_ENABLE command.
*/
struct dmub_rb_cmd_replay_enable_data {
@@ -4408,9 +4769,9 @@ struct dmub_cmd_replay_set_coasting_vtotal_data {
*/
uint16_t coasting_vtotal_high;
/**
- * Explicit padding to 4 byte boundary.
+ * frame skip number.
*/
- uint8_t pad[2];
+ uint16_t frame_skip_number;
};
/**
@@ -4571,6 +4932,58 @@ union dmub_replay_cmd_set {
};
/**
+ * SMART POWER OLED command sub-types.
+ */
+enum dmub_cmd_smart_power_oled_type {
+
+ /**
+ * Enable/Disable SMART_POWER_OLED.
+ */
+ DMUB_CMD__SMART_POWER_OLED_ENABLE = 1,
+ /**
+ * Get current MaxCLL value if SMART POWER OLED is enabled.
+ */
+ DMUB_CMD__SMART_POWER_OLED_GETMAXCLL = 2,
+};
+
+/**
+ * Definition of a DMUB_CMD__SMART_POWER_OLED command.
+ */
+struct dmub_rb_cmd_smart_power_oled_enable {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+
+ struct dmub_rb_cmd_smart_power_oled_enable_data data;
+};
+
+struct dmub_cmd_smart_power_oled_getmaxcll_input {
+ uint8_t panel_inst;
+ uint8_t pad[3];
+};
+
+struct dmub_cmd_smart_power_oled_getmaxcll_output {
+ uint16_t current_max_cll;
+ uint8_t pad[2];
+};
+
+/**
+ * Definition of a DMUB_CMD__SMART_POWER_OLED command.
+ */
+struct dmub_rb_cmd_smart_power_oled_getmaxcll {
+ struct dmub_cmd_header header; /**< Command header */
+ /**
+ * Data passed from driver to FW in a DMUB_CMD__SMART_POWER_OLED_GETMAXCLL command.
+ */
+ union dmub_cmd_smart_power_oled_getmaxcll_data {
+ struct dmub_cmd_smart_power_oled_getmaxcll_input input; /**< Input */
+ struct dmub_cmd_smart_power_oled_getmaxcll_output output; /**< Output */
+ uint32_t output_raw; /**< Raw data output */
+ } data;
+};
+
+/**
* Set of HW components that can be locked.
*
* Note: If updating with more HW components, fields
@@ -4652,6 +5065,7 @@ enum hw_lock_client {
*/
HW_LOCK_CLIENT_REPLAY = 4,
HW_LOCK_CLIENT_FAMS2 = 5,
+ HW_LOCK_CLIENT_CURSOR_OFFLOAD = 6,
/**
* Invalid client.
*/
@@ -6064,6 +6478,257 @@ struct dmub_rb_cmd_ips_query_residency_info {
};
/**
+ * struct dmub_cmd_cursor_offload_init_data - Payload for cursor offload init command.
+ */
+struct dmub_cmd_cursor_offload_init_data {
+ union dmub_addr state_addr; /**< State address for dmub_cursor_offload */
+ uint32_t state_size; /**< State size for dmub_cursor_offload */
+};
+
+/**
+ * struct dmub_rb_cmd_cursor_offload_init - Data for initializing cursor offload.
+ */
+struct dmub_rb_cmd_cursor_offload_init {
+ struct dmub_cmd_header header;
+ struct dmub_cmd_cursor_offload_init_data init_data;
+};
+
+/**
+ * struct dmub_cmd_cursor_offload_stream_data - Payload for cursor offload stream command.
+ */
+struct dmub_cmd_cursor_offload_stream_data {
+ uint32_t otg_inst: 4; /**< OTG instance to control */
+ uint32_t reserved: 28; /**< Reserved for future use */
+ uint32_t line_time_in_ns; /**< Line time in ns for the OTG */
+ uint32_t v_total_max; /**< OTG v_total_max */
+};
+
+/**
+ * struct dmub_rb_cmd_cursor_offload_stream_cntl - Controls a stream for cursor offload.
+ */
+struct dmub_rb_cmd_cursor_offload_stream_cntl {
+ struct dmub_cmd_header header;
+ struct dmub_cmd_cursor_offload_stream_data data;
+};
+
+/**
+ * Data passed from driver to FW in a DMUB_CMD__PR_ENABLE command.
+ */
+struct dmub_cmd_pr_enable_data {
+ /**
+ * Panel Replay enable or disable.
+ */
+ uint8_t enable;
+ /**
+ * Panel Instance.
+ * Panel isntance to identify which replay_state to use
+ * Currently the support is only for 0 or 1
+ */
+ uint8_t panel_inst;
+ /**
+ * Phy state to enter.
+ * Values to use are defined in dmub_phy_fsm_state
+ */
+ uint8_t phy_fsm_state;
+ /**
+ * Phy rate for DP - RBR/HBR/HBR2/HBR3.
+ * Set this using enum phy_link_rate.
+ * This does not support HDMI/DP2 for now.
+ */
+ uint8_t phy_rate;
+ /**
+ * @hpo_stream_enc_inst: HPO stream encoder instance
+ */
+ uint8_t hpo_stream_enc_inst;
+ /**
+ * @hpo_link_enc_inst: HPO link encoder instance
+ */
+ uint8_t hpo_link_enc_inst;
+ /**
+ * @pad: Align structure to 4 byte boundary.
+ */
+ uint8_t pad[2];
+};
+
+/**
+ * Definition of a DMUB_CMD__PR_ENABLE command.
+ * Panel Replay enable/disable is controlled using action in data.
+ */
+struct dmub_rb_cmd_pr_enable {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+
+ struct dmub_cmd_pr_enable_data data;
+};
+
+/**
+ * Data passed from driver to FW in a DMUB_CMD__PR_COPY_SETTINGS command.
+ */
+struct dmub_cmd_pr_copy_settings_data {
+ /**
+ * Flags that can be set by driver to change some replay behaviour.
+ */
+ union pr_debug_flags debug;
+
+ /**
+ * @flags: Flags used to determine feature functionality.
+ */
+ union pr_hw_flags flags;
+
+ /**
+ * DPP HW instance.
+ */
+ uint8_t dpp_inst;
+ /**
+ * OTG HW instance.
+ */
+ uint8_t otg_inst;
+ /**
+ * DIG FE HW instance.
+ */
+ uint8_t digfe_inst;
+ /**
+ * DIG BE HW instance.
+ */
+ uint8_t digbe_inst;
+ /**
+ * AUX HW instance.
+ */
+ uint8_t aux_inst;
+ /**
+ * Panel Instance.
+ * Panel isntance to identify which psr_state to use
+ * Currently the support is only for 0 or 1
+ */
+ uint8_t panel_inst;
+ /**
+ * Length of each horizontal line in ns.
+ */
+ uint32_t line_time_in_ns;
+ /**
+ * PHY instance.
+ */
+ uint8_t dpphy_inst;
+ /**
+ * Determines if SMU optimzations are enabled/disabled.
+ */
+ uint8_t smu_optimizations_en;
+ /*
+ * Use FSM state for Replay power up/down
+ */
+ uint8_t use_phy_fsm;
+ /*
+ * Use FSFT afftet pixel clk
+ */
+ uint32_t pix_clk_100hz;
+ /*
+ * Use Original pixel clock
+ */
+ uint32_t sink_pix_clk_100hz;
+ /**
+ * Use for AUX-less ALPM LFPS wake operation
+ */
+ struct dmub_alpm_auxless_data auxless_alpm_data;
+ /**
+ * @hpo_stream_enc_inst: HPO stream encoder instance
+ */
+ uint8_t hpo_stream_enc_inst;
+ /**
+ * @hpo_link_enc_inst: HPO link encoder instance
+ */
+ uint8_t hpo_link_enc_inst;
+ /**
+ * @pad: Align structure to 4 byte boundary.
+ */
+ uint8_t pad[2];
+};
+
+/**
+ * Definition of a DMUB_CMD__PR_COPY_SETTINGS command.
+ */
+struct dmub_rb_cmd_pr_copy_settings {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+ /**
+ * Data passed from driver to FW in a DMUB_CMD__PR_COPY_SETTINGS command.
+ */
+ struct dmub_cmd_pr_copy_settings_data data;
+};
+
+struct dmub_cmd_pr_update_state_data {
+ /**
+ * Panel Instance.
+ * Panel isntance to identify which psr_state to use
+ * Currently the support is only for 0 or 1
+ */
+ uint8_t panel_inst;
+
+ uint8_t pad[3]; // align to 4-byte boundary
+ /*
+ * Update flags to control the update behavior.
+ */
+ uint32_t update_flag;
+ /**
+ * state/data to set.
+ */
+ uint32_t coasting_vtotal;
+ uint32_t sync_mode;
+};
+
+struct dmub_cmd_pr_general_cmd_data {
+ /**
+ * Panel Instance.
+ * Panel isntance to identify which psr_state to use
+ * Currently the support is only for 0 or 1
+ */
+ uint8_t panel_inst;
+ /**
+ * subtype: PR general cmd sub type
+ */
+ uint8_t subtype;
+
+ uint8_t pad[2];
+ /**
+ * config data by different subtypes
+ */
+ union {
+ uint32_t u32All;
+ } data;
+};
+
+/**
+ * Definition of a DMUB_CMD__PR_UPDATE_STATE command.
+ */
+struct dmub_rb_cmd_pr_update_state {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+ /**
+ * Data passed from driver to FW in a DMUB_CMD__PR_UPDATE_STATE command.
+ */
+ struct dmub_cmd_pr_update_state_data data;
+};
+
+/**
+ * Definition of a DMUB_CMD__PR_GENERAL_CMD command.
+ */
+struct dmub_rb_cmd_pr_general_cmd {
+ /**
+ * Command header.
+ */
+ struct dmub_cmd_header header;
+ /**
+ * Data passed from driver to FW in a DMUB_CMD__PR_GENERAL_CMD command.
+ */
+ struct dmub_cmd_pr_general_cmd_data data;
+};
+
+/**
* union dmub_rb_cmd - DMUB inbox command.
*/
union dmub_rb_cmd {
@@ -6392,6 +7057,38 @@ union dmub_rb_cmd {
struct dmub_rb_cmd_ips_residency_cntl ips_residency_cntl;
struct dmub_rb_cmd_ips_query_residency_info ips_query_residency_info;
+ /**
+ * Definition of a DMUB_CMD__CURSOR_OFFLOAD_INIT command.
+ */
+ struct dmub_rb_cmd_cursor_offload_init cursor_offload_init;
+ /**
+ * Definition of a DMUB_CMD__CURSOR_OFFLOAD control commands.
+ * - DMUB_CMD__CURSOR_OFFLOAD_STREAM_ENABLE
+ * - DMUB_CMD__CURSOR_OFFLOAD_STREAM_DISABLE
+ * - DMUB_CMD__CURSOR_OFFLOAD_STREAM_PROGRAM
+ * - DMUB_CMD__CURSOR_OFFLOAD_STREAM_UPDATE_DRR
+ */
+ struct dmub_rb_cmd_cursor_offload_stream_cntl cursor_offload_stream_ctnl;
+ /**
+ * Definition of a DMUB_CMD__SMART_POWER_OLED_ENABLE command.
+ */
+ struct dmub_rb_cmd_smart_power_oled_enable smart_power_oled_enable;
+ /**
+ * Definition of a DMUB_CMD__DMUB_CMD__SMART_POWER_OLED_GETMAXCLL command.
+ */
+ struct dmub_rb_cmd_smart_power_oled_getmaxcll smart_power_oled_getmaxcll;
+ /*
+ * Definition of a DMUB_CMD__REPLAY_COPY_SETTINGS command.
+ */
+ struct dmub_rb_cmd_pr_copy_settings pr_copy_settings;
+ /**
+ * Definition of a DMUB_CMD__REPLAY_ENABLE command.
+ */
+ struct dmub_rb_cmd_pr_enable pr_enable;
+
+ struct dmub_rb_cmd_pr_update_state pr_update_state;
+
+ struct dmub_rb_cmd_pr_general_cmd pr_general_cmd;
};
/**
diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c
index 4777c7203b2c..cd04d7c756c3 100644
--- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c
+++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c
@@ -380,6 +380,7 @@ void dmub_dcn31_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmu
boot_options.bits.override_hbr3_pll_vco = params->override_hbr3_pll_vco;
boot_options.bits.sel_mux_phy_c_d_phy_f_g = (dmub->asic == DMUB_ASIC_DCN31B) ? 1 : 0;
+ boot_options.bits.disable_dpia_bw_allocation = params->disable_dpia_bw_allocation;
REG_WRITE(DMCUB_SCRATCH14, boot_options.all);
}
diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c
index ce041f6239dc..7e9856289910 100644
--- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c
+++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn32.c
@@ -89,50 +89,58 @@ static inline void dmub_dcn32_translate_addr(const union dmub_addr *addr_in,
void dmub_dcn32_reset(struct dmub_srv *dmub)
{
union dmub_gpint_data_register cmd;
- const uint32_t timeout = 100000;
- uint32_t in_reset, is_enabled, scratch, i, pwait_mode;
+ const uint32_t timeout_us = 1 * 1000 * 1000; //1s
+ const uint32_t poll_delay_us = 1; //1us
+ uint32_t i = 0;
+ uint32_t enabled, in_reset, scratch, pwait_mode;
- REG_GET(DMCUB_CNTL2, DMCUB_SOFT_RESET, &in_reset);
- REG_GET(DMCUB_CNTL, DMCUB_ENABLE, &is_enabled);
+ REG_GET(DMCUB_CNTL,
+ DMCUB_ENABLE, &enabled);
+ REG_GET(DMCUB_CNTL2,
+ DMCUB_SOFT_RESET, &in_reset);
- if (in_reset == 0 && is_enabled != 0) {
+ if (enabled && in_reset == 0) {
cmd.bits.status = 1;
cmd.bits.command_code = DMUB_GPINT__STOP_FW;
cmd.bits.param = 0;
dmub->hw_funcs.set_gpint(dmub, cmd);
- for (i = 0; i < timeout; ++i) {
- if (dmub->hw_funcs.is_gpint_acked(dmub, cmd))
- break;
-
- udelay(1);
- }
-
- for (i = 0; i < timeout; ++i) {
+ for (; i < timeout_us; i++) {
scratch = REG_READ(DMCUB_SCRATCH7);
if (scratch == DMUB_GPINT__STOP_FW_RESPONSE)
break;
- udelay(1);
+ udelay(poll_delay_us);
}
- for (i = 0; i < timeout; ++i) {
+ for (; i < timeout_us; i++) {
REG_GET(DMCUB_CNTL, DMCUB_PWAIT_MODE_STATUS, &pwait_mode);
if (pwait_mode & (1 << 0))
break;
- udelay(1);
+ udelay(poll_delay_us);
}
- /* Force reset in case we timed out, DMCUB is likely hung. */
}
- if (is_enabled) {
+ if (enabled) {
REG_UPDATE(DMCUB_CNTL2, DMCUB_SOFT_RESET, 1);
udelay(1);
REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
}
+ if (i >= timeout_us) {
+ /* timeout should never occur */
+ BREAK_TO_DEBUGGER();
+ }
+
+ REG_UPDATE(DMCUB_REGION3_CW2_TOP_ADDRESS, DMCUB_REGION3_CW2_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW3_TOP_ADDRESS, DMCUB_REGION3_CW3_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW4_TOP_ADDRESS, DMCUB_REGION3_CW4_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW5_TOP_ADDRESS, DMCUB_REGION3_CW5_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW7_TOP_ADDRESS, DMCUB_REGION3_CW7_ENABLE, 0);
+
REG_WRITE(DMCUB_INBOX1_RPTR, 0);
REG_WRITE(DMCUB_INBOX1_WPTR, 0);
REG_WRITE(DMCUB_OUTBOX1_RPTR, 0);
@@ -141,7 +149,7 @@ void dmub_dcn32_reset(struct dmub_srv *dmub)
REG_WRITE(DMCUB_OUTBOX0_WPTR, 0);
REG_WRITE(DMCUB_SCRATCH0, 0);
- /* Clear the GPINT command manually so we don't send anything during boot. */
+ /* Clear the GPINT command manually so we don't reset again. */
cmd.all = 0;
dmub->hw_funcs.set_gpint(dmub, cmd);
}
@@ -163,7 +171,9 @@ void dmub_dcn32_backdoor_load(struct dmub_srv *dmub,
dmub_dcn32_get_fb_base_offset(dmub, &fb_base, &fb_offset);
+ /* reset and disable DMCUB and MMHUBBUB DMUIF */
REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1);
+ REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
dmub_dcn32_translate_addr(&cw0->offset, fb_base, fb_offset, &offset);
@@ -193,7 +203,9 @@ void dmub_dcn32_backdoor_load_zfb_mode(struct dmub_srv *dmub,
{
union dmub_addr offset;
+ /* reset and disable DMCUB and MMHUBBUB DMUIF */
REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1);
+ REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
offset = cw0->offset;
diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c
index 834e5434ccb8..e13557ed97be 100644
--- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c
+++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.c
@@ -418,6 +418,7 @@ void dmub_dcn35_enable_dmub_boot_options(struct dmub_srv *dmub, const struct dmu
boot_options.bits.disable_sldo_opt = params->disable_sldo_opt;
boot_options.bits.enable_non_transparent_setconfig = params->enable_non_transparent_setconfig;
boot_options.bits.lower_hbr3_phy_ssc = params->lower_hbr3_phy_ssc;
+ boot_options.bits.disable_dpia_bw_allocation = params->disable_dpia_bw_allocation;
REG_WRITE(DMCUB_SCRATCH14, boot_options.all);
}
@@ -520,6 +521,45 @@ void dmub_dcn35_get_diagnostic_data(struct dmub_srv *dmub)
dmub->debug.gpint_datain0 = REG_READ(DMCUB_GPINT_DATAIN0);
}
+
+bool dmub_dcn35_get_preos_fw_info(struct dmub_srv *dmub)
+{
+ uint64_t region3_cw5_offset;
+ uint32_t top_addr, top_addr_enable, offset_low;
+ uint32_t offset_high, base_addr, fw_version;
+ bool is_vbios_fw = false;
+
+ memset(&dmub->preos_info, 0, sizeof(dmub->preos_info));
+
+ fw_version = REG_READ(DMCUB_SCRATCH1);
+ is_vbios_fw = ((fw_version >> 6) & 0x01) ? true : false;
+ if (!is_vbios_fw)
+ return false;
+
+ dmub->preos_info.boot_status = REG_READ(DMCUB_SCRATCH0);
+ dmub->preos_info.fw_version = REG_READ(DMCUB_SCRATCH1);
+ dmub->preos_info.boot_options = REG_READ(DMCUB_SCRATCH14);
+ REG_GET(DMCUB_REGION3_CW5_TOP_ADDRESS,
+ DMCUB_REGION3_CW5_ENABLE, &top_addr_enable);
+ if (top_addr_enable) {
+ dmub_dcn35_get_fb_base_offset(dmub,
+ &dmub->preos_info.fb_base, &dmub->preos_info.fb_offset);
+ offset_low = REG_READ(DMCUB_REGION3_CW5_OFFSET);
+ offset_high = REG_READ(DMCUB_REGION3_CW5_OFFSET_HIGH);
+ region3_cw5_offset = ((uint64_t)offset_high << 32) | offset_low;
+ dmub->preos_info.trace_buffer_phy_addr = region3_cw5_offset
+ - dmub->preos_info.fb_base + dmub->preos_info.fb_offset;
+
+ REG_GET(DMCUB_REGION3_CW5_TOP_ADDRESS,
+ DMCUB_REGION3_CW5_TOP_ADDRESS, &top_addr);
+ base_addr = REG_READ(DMCUB_REGION3_CW5_BASE_ADDRESS) & 0x1FFFFFFF;
+ dmub->preos_info.trace_buffer_size =
+ (top_addr > base_addr) ? (top_addr - base_addr + 1) : 0;
+ }
+
+ return true;
+}
+
void dmub_dcn35_configure_dmub_in_system_memory(struct dmub_srv *dmub)
{
/* DMCUB_REGION3_TMR_AXI_SPACE values:
diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.h
index 39fcb7275da5..92e6695a2c9b 100644
--- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.h
+++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn35.h
@@ -285,4 +285,6 @@ bool dmub_dcn35_is_hw_powered_up(struct dmub_srv *dmub);
void dmub_srv_dcn35_regs_init(struct dmub_srv *dmub, struct dc_context *ctx);
+bool dmub_dcn35_get_preos_fw_info(struct dmub_srv *dmub);
+
#endif /* _DMUB_DCN35_H_ */
diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c
index b31adbd0d685..95542299e3b3 100644
--- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c
+++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn401.c
@@ -81,7 +81,7 @@ void dmub_dcn401_reset(struct dmub_srv *dmub)
dmub->hw_funcs.set_gpint(dmub, cmd);
for (; i < timeout_us; i++) {
- scratch = dmub->hw_funcs.get_gpint_response(dmub);
+ scratch = REG_READ(DMCUB_SCRATCH7);
if (scratch == DMUB_GPINT__STOP_FW_RESPONSE)
break;
@@ -97,11 +97,24 @@ void dmub_dcn401_reset(struct dmub_srv *dmub)
}
}
+ if (enabled) {
+ REG_UPDATE(DMCUB_CNTL2, DMCUB_SOFT_RESET, 1);
+ udelay(1);
+ REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
+ }
+
if (i >= timeout_us) {
/* timeout should never occur */
BREAK_TO_DEBUGGER();
}
+ REG_UPDATE(DMCUB_REGION3_CW2_TOP_ADDRESS, DMCUB_REGION3_CW2_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW3_TOP_ADDRESS, DMCUB_REGION3_CW3_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW4_TOP_ADDRESS, DMCUB_REGION3_CW4_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW5_TOP_ADDRESS, DMCUB_REGION3_CW5_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW6_TOP_ADDRESS, DMCUB_REGION3_CW6_ENABLE, 0);
+ REG_UPDATE(DMCUB_REGION3_CW7_TOP_ADDRESS, DMCUB_REGION3_CW7_ENABLE, 0);
+
REG_WRITE(DMCUB_INBOX1_RPTR, 0);
REG_WRITE(DMCUB_INBOX1_WPTR, 0);
REG_WRITE(DMCUB_OUTBOX1_RPTR, 0);
@@ -134,7 +147,6 @@ void dmub_dcn401_backdoor_load(struct dmub_srv *dmub,
/* reset and disable DMCUB and MMHUBBUB DMUIF */
REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1);
- REG_UPDATE(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET, 1);
REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
dmub_dcn401_translate_addr(&cw0->offset, fb_base, fb_offset, &offset);
@@ -168,7 +180,6 @@ void dmub_dcn401_backdoor_load_zfb_mode(struct dmub_srv *dmub,
/* reset and disable DMCUB and MMHUBBUB DMUIF */
REG_UPDATE(DMCUB_SEC_CNTL, DMCUB_SEC_RESET, 1);
- REG_UPDATE(MMHUBBUB_SOFT_RESET, DMUIF_SOFT_RESET, 1);
REG_UPDATE(DMCUB_CNTL, DMCUB_ENABLE, 0);
offset = cw0->offset;
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 b17a19400c06..a6ae1d2e9685 100644
--- a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c
+++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c
@@ -66,7 +66,7 @@
#define DMUB_SCRATCH_MEM_SIZE (1024)
/* Default indirect buffer size. */
-#define DMUB_IB_MEM_SIZE (1280)
+#define DMUB_IB_MEM_SIZE (2560)
/* Default LSDMA ring buffer size. */
#define DMUB_LSDMA_RB_SIZE (64 * 1024)
@@ -359,6 +359,7 @@ static bool dmub_srv_hw_setup(struct dmub_srv *dmub, enum dmub_asic asic)
funcs->get_current_time = dmub_dcn35_get_current_time;
funcs->get_diagnostic_data = dmub_dcn35_get_diagnostic_data;
+ funcs->get_preos_fw_info = dmub_dcn35_get_preos_fw_info;
funcs->init_reg_offsets = dmub_srv_dcn35_regs_init;
if (asic == DMUB_ASIC_DCN351)
@@ -564,10 +565,11 @@ enum dmub_status
window_sizes[DMUB_WINDOW_4_MAILBOX] = DMUB_MAILBOX_SIZE;
window_sizes[DMUB_WINDOW_5_TRACEBUFF] = trace_buffer_size;
window_sizes[DMUB_WINDOW_6_FW_STATE] = fw_state_size;
- window_sizes[DMUB_WINDOW_7_SCRATCH_MEM] = DMUB_SCRATCH_MEM_SIZE;
+ window_sizes[DMUB_WINDOW_7_SCRATCH_MEM] = dmub_align(DMUB_SCRATCH_MEM_SIZE, 64);
window_sizes[DMUB_WINDOW_IB_MEM] = DMUB_IB_MEM_SIZE;
window_sizes[DMUB_WINDOW_SHARED_STATE] = max(DMUB_FW_HEADER_SHARED_STATE_SIZE, shared_state_size);
window_sizes[DMUB_WINDOW_LSDMA_BUFFER] = DMUB_LSDMA_RB_SIZE;
+ window_sizes[DMUB_WINDOW_CURSOR_OFFLOAD] = dmub_align(sizeof(struct dmub_cursor_offload_v1), 64);
out->fb_size =
dmub_srv_calc_regions_for_memory_type(params, out, window_sizes, DMUB_WINDOW_MEMORY_TYPE_FB);
@@ -652,21 +654,22 @@ enum dmub_status dmub_srv_hw_init(struct dmub_srv *dmub,
struct dmub_fb *mail_fb = params->fb[DMUB_WINDOW_4_MAILBOX];
struct dmub_fb *tracebuff_fb = params->fb[DMUB_WINDOW_5_TRACEBUFF];
struct dmub_fb *fw_state_fb = params->fb[DMUB_WINDOW_6_FW_STATE];
- struct dmub_fb *scratch_mem_fb = params->fb[DMUB_WINDOW_7_SCRATCH_MEM];
- struct dmub_fb *ib_mem_gart = params->fb[DMUB_WINDOW_IB_MEM];
struct dmub_fb *shared_state_fb = params->fb[DMUB_WINDOW_SHARED_STATE];
struct dmub_rb_init_params rb_params, outbox0_rb_params;
struct dmub_window cw0, cw1, cw2, cw3, cw4, cw5, cw6, region6;
struct dmub_region inbox1, outbox1, outbox0;
+ uint32_t i;
+
if (!dmub->sw_init)
return DMUB_STATUS_INVALID;
- if (!inst_fb || !stack_fb || !data_fb || !bios_fb || !mail_fb ||
- !tracebuff_fb || !fw_state_fb || !scratch_mem_fb || !ib_mem_gart) {
- ASSERT(0);
- return DMUB_STATUS_INVALID;
+ for (i = 0; i < DMUB_WINDOW_TOTAL; ++i) {
+ if (!params->fb[i]) {
+ ASSERT(0);
+ return DMUB_STATUS_INVALID;
+ }
}
dmub->fb_base = params->fb_base;
@@ -748,9 +751,11 @@ enum dmub_status dmub_srv_hw_init(struct dmub_srv *dmub,
dmub->shared_state = shared_state_fb->cpu_addr;
- dmub->scratch_mem_fb = *scratch_mem_fb;
+ dmub->scratch_mem_fb = *params->fb[DMUB_WINDOW_7_SCRATCH_MEM];
+ dmub->ib_mem_gart = *params->fb[DMUB_WINDOW_IB_MEM];
- dmub->ib_mem_gart = *ib_mem_gart;
+ dmub->cursor_offload_fb = *params->fb[DMUB_WINDOW_CURSOR_OFFLOAD];
+ dmub->cursor_offload_v1 = (struct dmub_cursor_offload_v1 *)dmub->cursor_offload_fb.cpu_addr;
if (dmub->hw_funcs.setup_windows)
dmub->hw_funcs.setup_windows(dmub, &cw2, &cw3, &cw4, &cw5, &cw6, &region6);
@@ -1368,3 +1373,11 @@ enum dmub_status dmub_srv_update_inbox_status(struct dmub_srv *dmub)
return DMUB_STATUS_OK;
}
+
+bool dmub_srv_get_preos_info(struct dmub_srv *dmub)
+{
+ if (!dmub || !dmub->hw_funcs.get_preos_fw_info)
+ return false;
+
+ return dmub->hw_funcs.get_preos_fw_info(dmub);
+}
diff --git a/drivers/gpu/drm/amd/display/include/bios_parser_types.h b/drivers/gpu/drm/amd/display/include/bios_parser_types.h
index 812377d9e48f..973b6bdbac63 100644
--- a/drivers/gpu/drm/amd/display/include/bios_parser_types.h
+++ b/drivers/gpu/drm/amd/display/include/bios_parser_types.h
@@ -135,12 +135,8 @@ struct bp_external_encoder_control {
struct bp_crtc_source_select {
enum engine_id engine_id;
enum controller_id controller_id;
- /* from GPU Tx aka asic_signal */
- enum signal_type signal;
- /* sink_signal may differ from asicSignal if Translator encoder */
enum signal_type sink_signal;
- enum display_output_bit_depth display_output_bit_depth;
- bool enable_dp_audio;
+ uint8_t bit_depth;
};
struct bp_transmitter_control {
@@ -166,6 +162,11 @@ struct bp_transmitter_control {
bool single_pll_mode;
};
+struct bp_load_detection_parameters {
+ enum engine_id engine_id;
+ uint16_t device_id;
+};
+
struct bp_hw_crtc_timing_parameters {
enum controller_id controller_id;
/* horizontal part */
diff --git a/drivers/gpu/drm/amd/display/include/dpcd_defs.h b/drivers/gpu/drm/amd/display/include/dpcd_defs.h
index de8f3cfed6c8..07b937b92efc 100644
--- a/drivers/gpu/drm/amd/display/include/dpcd_defs.h
+++ b/drivers/gpu/drm/amd/display/include/dpcd_defs.h
@@ -30,6 +30,22 @@
#ifndef DP_SINK_HW_REVISION_START // can remove this once the define gets into linux drm_dp_helper.h
#define DP_SINK_HW_REVISION_START 0x409
#endif
+/* Panel Replay*/
+#ifndef DP_PANEL_REPLAY_CAPABILITY_SUPPORT // can remove this once the define gets into linux drm_dp_helper.h
+#define DP_PANEL_REPLAY_CAPABILITY_SUPPORT 0x0b0
+#endif /* DP_PANEL_REPLAY_CAPABILITY_SUPPORT */
+#ifndef DP_PANEL_REPLAY_CAPABILITY // can remove this once the define gets into linux drm_dp_helper.h
+#define DP_PANEL_REPLAY_CAPABILITY 0x0b1
+#endif /* DP_PANEL_REPLAY_CAPABILITY */
+#ifndef DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1 // can remove this once the define gets into linux drm_dp_helper.h
+#define DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1 0x1b0
+#endif /* DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_1 */
+#ifndef DP_PANEL_REPLAY_ENABLE // can remove this once the define gets into linux drm_dp_helper.h
+#define DP_PANEL_REPLAY_ENABLE (1 << 0)
+#endif /* DP_PANEL_REPLAY_ENABLE */
+#ifndef DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2 // can remove this once the define gets into linux drm_dp_helper.h
+#define DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2 0x1b1
+#endif /* DP_PANEL_REPLAY_ENABLE_AND_CONFIGURATION_2 */
enum dpcd_revision {
DPCD_REV_10 = 0x10,
diff --git a/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h
index cc467031651d..38a77fa9b4af 100644
--- a/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h
+++ b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h
@@ -169,6 +169,7 @@ struct dc_firmware_info {
uint32_t engine_clk_ss_percentage;
} feature;
+ uint32_t max_pixel_clock; /* in KHz */
uint32_t default_display_engine_pll_frequency; /* in KHz */
uint32_t external_clock_source_frequency_for_dp; /* in KHz */
uint32_t smu_gpu_pll_output_freq; /* in KHz */
diff --git a/drivers/gpu/drm/amd/display/include/grph_object_id.h b/drivers/gpu/drm/amd/display/include/grph_object_id.h
index 54e33062b3c0..1386fa124e85 100644
--- a/drivers/gpu/drm/amd/display/include/grph_object_id.h
+++ b/drivers/gpu/drm/amd/display/include/grph_object_id.h
@@ -310,4 +310,11 @@ static inline bool dal_graphics_object_id_equal(
}
return false;
}
+
+static inline bool dc_connector_supports_analog(const enum connector_id conn)
+{
+ return conn == CONNECTOR_ID_VGA ||
+ conn == CONNECTOR_ID_SINGLE_LINK_DVII ||
+ conn == CONNECTOR_ID_DUAL_LINK_DVII;
+}
#endif
diff --git a/drivers/gpu/drm/amd/display/include/signal_types.h b/drivers/gpu/drm/amd/display/include/signal_types.h
index a10d6b988aab..3a2c2d2fb629 100644
--- a/drivers/gpu/drm/amd/display/include/signal_types.h
+++ b/drivers/gpu/drm/amd/display/include/signal_types.h
@@ -118,6 +118,18 @@ static inline bool dc_is_dvi_signal(enum signal_type signal)
}
}
+/**
+ * dc_is_rgb_signal() - Whether the signal is analog RGB.
+ *
+ * Returns whether the given signal type is an analog RGB signal
+ * that is used with a DAC on VGA or DVI-I connectors.
+ * Not to be confused with other uses of "RGB", such as RGB color space.
+ */
+static inline bool dc_is_rgb_signal(enum signal_type signal)
+{
+ return (signal == SIGNAL_TYPE_RGB);
+}
+
static inline bool dc_is_tmds_signal(enum signal_type signal)
{
switch (signal) {
diff --git a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
index ce421bcddcb0..1aae46d703ba 100644
--- a/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
+++ b/drivers/gpu/drm/amd/display/modules/freesync/freesync.c
@@ -1260,6 +1260,17 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
update_v_total_for_static_ramp(
core_freesync, stream, in_out_vrr);
}
+
+ /*
+ * If VRR is inactive, set vtotal min and max to nominal vtotal
+ */
+ if (in_out_vrr->state == VRR_STATE_INACTIVE) {
+ in_out_vrr->adjust.v_total_min =
+ mod_freesync_calc_v_total_from_refresh(stream,
+ in_out_vrr->max_refresh_in_uhz);
+ in_out_vrr->adjust.v_total_max = in_out_vrr->adjust.v_total_min;
+ return;
+ }
}
unsigned long long mod_freesync_calc_nominal_field_rate(
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
index c760216a6240..ca402ddcdacc 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.c
@@ -354,7 +354,7 @@ enum mod_hdcp_status mod_hdcp_add_display(struct mod_hdcp *hdcp,
/* reset retry counters */
reset_retry_counts(hdcp);
- /* reset error trace */
+ /* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* add display to connection */
@@ -400,7 +400,7 @@ enum mod_hdcp_status mod_hdcp_remove_display(struct mod_hdcp *hdcp,
/* clear retry counters */
reset_retry_counts(hdcp);
- /* reset error trace */
+ /* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* remove display */
@@ -464,7 +464,7 @@ enum mod_hdcp_status mod_hdcp_update_display(struct mod_hdcp *hdcp,
/* clear retry counters */
reset_retry_counts(hdcp);
- /* reset error trace */
+ /* reset trace */
memset(&hdcp->connection.trace, 0, sizeof(hdcp->connection.trace));
/* set new adjustment */
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h
index a37634942b07..26a351a184f3 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp.h
@@ -88,6 +88,7 @@ struct mod_hdcp_transition_input_hdcp2 {
uint8_t lc_init_write;
uint8_t l_prime_available_poll;
uint8_t l_prime_read;
+ uint8_t l_prime_combo_read;
uint8_t l_prime_validation;
uint8_t eks_prepare;
uint8_t eks_write;
@@ -508,7 +509,7 @@ static inline void set_auth_complete(struct mod_hdcp *hdcp,
struct mod_hdcp_output *output)
{
output->auth_complete = 1;
- mod_hdcp_log_ddc_trace(hdcp);
+ HDCP_AUTH_COMPLETE_TRACE(hdcp);
}
/* connection topology helpers */
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c
index 8bc377560787..1bbd728d4345 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp1_execution.c
@@ -29,6 +29,7 @@ static inline enum mod_hdcp_status validate_bksv(struct mod_hdcp *hdcp)
{
uint64_t n = 0;
uint8_t count = 0;
+ enum mod_hdcp_status status;
u8 bksv[sizeof(n)] = { };
memcpy(bksv, hdcp->auth.msg.hdcp1.bksv, sizeof(hdcp->auth.msg.hdcp1.bksv));
@@ -38,8 +39,14 @@ static inline enum mod_hdcp_status validate_bksv(struct mod_hdcp *hdcp)
count++;
n &= (n - 1);
}
- return (count == 20) ? MOD_HDCP_STATUS_SUCCESS :
- MOD_HDCP_STATUS_HDCP1_INVALID_BKSV;
+
+ if (count == 20) {
+ hdcp->connection.trace.hdcp1.attempt_count++;
+ status = MOD_HDCP_STATUS_SUCCESS;
+ } else {
+ status = MOD_HDCP_STATUS_HDCP1_INVALID_BKSV;
+ }
+ return status;
}
static inline enum mod_hdcp_status check_ksv_ready(struct mod_hdcp *hdcp)
@@ -135,6 +142,8 @@ static inline enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp)
if (get_device_count(hdcp) == 0)
return MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE;
+ hdcp->connection.trace.hdcp1.downstream_device_count = get_device_count(hdcp);
+
/* Some MST display may choose to report the internal panel as an HDCP RX.
* To update this condition with 1(because the immediate repeater's internal
* panel is possibly not included in DEVICE_COUNT) + get_device_count(hdcp).
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c
index bb8ae80b37f8..27500abf9fee 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_execution.c
@@ -48,6 +48,7 @@ static inline enum mod_hdcp_status check_receiver_id_list_ready(struct mod_hdcp
static inline enum mod_hdcp_status check_hdcp2_capable(struct mod_hdcp *hdcp)
{
enum mod_hdcp_status status;
+ struct mod_hdcp_trace *trace = &hdcp->connection.trace;
if (is_dp_hdcp(hdcp))
status = (hdcp->auth.msg.hdcp2.rxcaps_dp[0] == HDCP_2_2_RX_CAPS_VERSION_VAL) &&
@@ -55,9 +56,14 @@ static inline enum mod_hdcp_status check_hdcp2_capable(struct mod_hdcp *hdcp)
MOD_HDCP_STATUS_SUCCESS :
MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE;
else
- status = (hdcp->auth.msg.hdcp2.hdcp2version_hdmi & HDCP_2_2_HDMI_SUPPORT_MASK) ?
- MOD_HDCP_STATUS_SUCCESS :
- MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE;
+ status = (hdcp->auth.msg.hdcp2.hdcp2version_hdmi
+ & HDCP_2_2_HDMI_SUPPORT_MASK)
+ ? MOD_HDCP_STATUS_SUCCESS
+ : MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE;
+
+ if (status == MOD_HDCP_STATUS_SUCCESS)
+ trace->hdcp2.attempt_count++;
+
return status;
}
@@ -201,10 +207,17 @@ static inline uint8_t get_device_count(struct mod_hdcp *hdcp)
static enum mod_hdcp_status check_device_count(struct mod_hdcp *hdcp)
{
+ struct mod_hdcp_trace *trace = &hdcp->connection.trace;
+
/* Avoid device count == 0 to do authentication */
if (get_device_count(hdcp) == 0)
return MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE;
+ trace->hdcp2.downstream_device_count = get_device_count(hdcp);
+ trace->hdcp2.hdcp1_device_downstream =
+ HDCP_2_2_HDCP1_DEVICE_CONNECTED(hdcp->auth.msg.hdcp2.rx_id_list[2]);
+ trace->hdcp2.hdcp2_legacy_device_downstream =
+ HDCP_2_2_HDCP_2_0_REP_CONNECTED(hdcp->auth.msg.hdcp2.rx_id_list[2]);
/* Some MST display may choose to report the internal panel as an HDCP RX. */
/* To update this condition with 1(because the immediate repeater's internal */
/* panel is possibly not included in DEVICE_COUNT) + get_device_count(hdcp). */
@@ -452,54 +465,11 @@ out:
return status;
}
-static enum mod_hdcp_status locality_check_sw(struct mod_hdcp *hdcp,
- struct mod_hdcp_event_context *event_ctx,
- struct mod_hdcp_transition_input_hdcp2 *input)
-{
- enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
-
- if (!mod_hdcp_execute_and_set(mod_hdcp_write_lc_init,
- &input->lc_init_write, &status,
- hdcp, "lc_init_write"))
- goto out;
- if (is_dp_hdcp(hdcp))
- msleep(16);
- else
- if (!mod_hdcp_execute_and_set(poll_l_prime_available,
- &input->l_prime_available_poll, &status,
- hdcp, "l_prime_available_poll"))
- goto out;
- if (!mod_hdcp_execute_and_set(mod_hdcp_read_l_prime,
- &input->l_prime_read, &status,
- hdcp, "l_prime_read"))
- goto out;
-out:
- return status;
-}
-
-static enum mod_hdcp_status locality_check_fw(struct mod_hdcp *hdcp,
- struct mod_hdcp_event_context *event_ctx,
- struct mod_hdcp_transition_input_hdcp2 *input)
-{
- enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
-
- if (!mod_hdcp_execute_and_set(mod_hdcp_write_poll_read_lc_fw,
- &input->l_prime_read, &status,
- hdcp, "l_prime_read"))
- goto out;
-
-out:
- return status;
-}
-
static enum mod_hdcp_status locality_check(struct mod_hdcp *hdcp,
struct mod_hdcp_event_context *event_ctx,
struct mod_hdcp_transition_input_hdcp2 *input)
{
enum mod_hdcp_status status = MOD_HDCP_STATUS_SUCCESS;
- const bool use_fw = hdcp->config.ddc.funcs.atomic_write_poll_read_i2c
- && hdcp->config.ddc.funcs.atomic_write_poll_read_aux
- && !hdcp->connection.link.adjust.hdcp2.force_sw_locality_check;
if (event_ctx->event != MOD_HDCP_EVENT_CALLBACK) {
event_ctx->unexpected_event = 1;
@@ -511,9 +481,28 @@ static enum mod_hdcp_status locality_check(struct mod_hdcp *hdcp,
hdcp, "lc_init_prepare"))
goto out;
- status = (use_fw ? locality_check_fw : locality_check_sw)(hdcp, event_ctx, input);
- if (status != MOD_HDCP_STATUS_SUCCESS)
- goto out;
+ if (hdcp->connection.link.adjust.hdcp2.use_fw_locality_check) {
+ if (!mod_hdcp_execute_and_set(mod_hdcp_write_poll_read_lc_fw,
+ &input->l_prime_combo_read, &status,
+ hdcp, "l_prime_combo_read"))
+ goto out;
+ } else {
+ if (!mod_hdcp_execute_and_set(mod_hdcp_write_lc_init,
+ &input->lc_init_write, &status,
+ hdcp, "lc_init_write"))
+ goto out;
+ if (is_dp_hdcp(hdcp))
+ msleep(16);
+ else
+ if (!mod_hdcp_execute_and_set(poll_l_prime_available,
+ &input->l_prime_available_poll, &status,
+ hdcp, "l_prime_available_poll"))
+ goto out;
+ if (!mod_hdcp_execute_and_set(mod_hdcp_read_l_prime,
+ &input->l_prime_read, &status,
+ hdcp, "l_prime_read"))
+ goto out;
+ }
if (!mod_hdcp_execute_and_set(mod_hdcp_hdcp2_validate_l_prime,
&input->l_prime_validation, &status,
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c
index 89ffb89e1932..9316312a4df5 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp2_transition.c
@@ -184,31 +184,33 @@ enum mod_hdcp_status mod_hdcp_hdcp2_transition(struct mod_hdcp *hdcp,
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A2_LOCALITY_CHECK);
break;
- case H2_A2_LOCALITY_CHECK: {
- const bool use_fw = hdcp->config.ddc.funcs.atomic_write_poll_read_i2c
- && !adjust->hdcp2.force_sw_locality_check;
-
- /*
- * 1A-05: consider disconnection after LC init a failure
- * 1A-13-1: consider invalid l' a failure
- * 1A-13-2: consider l' timeout a failure
- */
+ case H2_A2_LOCALITY_CHECK:
+ /* 1A-05: consider disconnection after LC init a failure */
if (hdcp->state.stay_count > 10 ||
- input->lc_init_prepare != PASS ||
- (!use_fw && input->lc_init_write != PASS) ||
- (!use_fw && input->l_prime_available_poll != PASS)) {
+ input->lc_init_prepare != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
- } else if (input->l_prime_read != PASS) {
- if (use_fw && hdcp->config.debug.lc_enable_sw_fallback) {
- adjust->hdcp2.force_sw_locality_check = true;
+ } else if (adjust->hdcp2.use_fw_locality_check &&
+ input->l_prime_combo_read != PASS) {
+ /* 1A-13-2: consider l' timeout a failure */
+ if (adjust->hdcp2.use_sw_locality_fallback) {
+ /* switch to software locality check */
+ adjust->hdcp2.use_fw_locality_check = 0;
callback_in_ms(0, output);
+ increment_stay_counter(hdcp);
break;
}
-
+ fail_and_restart_in_ms(0, &status, output);
+ break;
+ } else if (!adjust->hdcp2.use_fw_locality_check &&
+ (input->lc_init_write != PASS ||
+ input->l_prime_available_poll != PASS ||
+ input->l_prime_read != PASS)) {
+ /* 1A-13-2: consider l' timeout a failure */
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->l_prime_validation != PASS) {
+ /* 1A-13-1: consider invalid l' a failure */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
@@ -216,7 +218,6 @@ enum mod_hdcp_status mod_hdcp_hdcp2_transition(struct mod_hdcp *hdcp,
callback_in_ms(0, output);
set_state_id(hdcp, output, H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER);
break;
- }
case H2_A3_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
if (input->eks_prepare != PASS ||
input->eks_write != PASS) {
@@ -510,26 +511,29 @@ enum mod_hdcp_status mod_hdcp_hdcp2_dp_transition(struct mod_hdcp *hdcp,
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A2_LOCALITY_CHECK);
break;
- case D2_A2_LOCALITY_CHECK: {
- const bool use_fw = hdcp->config.ddc.funcs.atomic_write_poll_read_aux
- && !adjust->hdcp2.force_sw_locality_check;
-
+ case D2_A2_LOCALITY_CHECK:
if (hdcp->state.stay_count > 10 ||
- input->lc_init_prepare != PASS ||
- (!use_fw && input->lc_init_write != PASS)) {
- /* 1A-12: consider invalid l' a failure */
+ input->lc_init_prepare != PASS) {
fail_and_restart_in_ms(0, &status, output);
break;
- } else if (input->l_prime_read != PASS) {
- if (use_fw && hdcp->config.debug.lc_enable_sw_fallback) {
- adjust->hdcp2.force_sw_locality_check = true;
+ } else if (adjust->hdcp2.use_fw_locality_check &&
+ input->l_prime_combo_read != PASS) {
+ if (adjust->hdcp2.use_sw_locality_fallback) {
+ /* switch to software locality check */
+ adjust->hdcp2.use_fw_locality_check = 0;
callback_in_ms(0, output);
+ increment_stay_counter(hdcp);
break;
}
-
+ fail_and_restart_in_ms(0, &status, output);
+ break;
+ } else if (!adjust->hdcp2.use_fw_locality_check &&
+ (input->lc_init_write != PASS ||
+ input->l_prime_read != PASS)) {
fail_and_restart_in_ms(0, &status, output);
break;
} else if (input->l_prime_validation != PASS) {
+ /* 1A-12: consider invalid l' a failure */
callback_in_ms(0, output);
increment_stay_counter(hdcp);
break;
@@ -537,7 +541,6 @@ enum mod_hdcp_status mod_hdcp_hdcp2_dp_transition(struct mod_hdcp *hdcp,
callback_in_ms(0, output);
set_state_id(hdcp, output, D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER);
break;
- }
case D2_A34_EXCHANGE_KS_AND_TEST_FOR_REPEATER:
if (input->eks_prepare != PASS ||
input->eks_write != PASS) {
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c
index 2e6408579194..0ca39873f807 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_ddc.c
@@ -758,6 +758,6 @@ enum mod_hdcp_status mod_hdcp_write_poll_read_lc_fw(struct mod_hdcp *hdcp)
{
const bool success = (is_dp_hdcp(hdcp) ? write_stall_read_lc_fw_aux : write_poll_read_lc_fw_i2c)(hdcp);
- return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_DDC_FAILURE;
+ return success ? MOD_HDCP_STATUS_SUCCESS : MOD_HDCP_STATUS_HDCP2_LOCALITY_COMBO_READ_FAILURE;
}
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c
index 6b3b5f610907..5cb979c2cf8c 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c
@@ -125,129 +125,11 @@ void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp)
}
}
+#define CASE_FORMAT(entry) case entry: return #entry;
char *mod_hdcp_status_to_str(int32_t status)
{
switch (status) {
- case MOD_HDCP_STATUS_SUCCESS:
- return "MOD_HDCP_STATUS_SUCCESS";
- case MOD_HDCP_STATUS_FAILURE:
- return "MOD_HDCP_STATUS_FAILURE";
- case MOD_HDCP_STATUS_RESET_NEEDED:
- return "MOD_HDCP_STATUS_RESET_NEEDED";
- case MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND:
- return "MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND";
- case MOD_HDCP_STATUS_DISPLAY_NOT_FOUND:
- return "MOD_HDCP_STATUS_DISPLAY_NOT_FOUND";
- case MOD_HDCP_STATUS_INVALID_STATE:
- return "MOD_HDCP_STATUS_INVALID_STATE";
- case MOD_HDCP_STATUS_NOT_IMPLEMENTED:
- return "MOD_HDCP_STATUS_NOT_IMPLEMENTED";
- case MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE:
- return "MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE";
- case MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE:
- return "MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE";
- case MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE:
- return "MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE";
- case MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE:
- return "MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER:
- return "MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER";
- case MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE:
- return "MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE";
- case MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING:
- return "MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING";
- case MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_BKSV_REVOKED:
- return "MOD_HDCP_STATUS_HDCP1_BKSV_REVOKED";
- case MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY:
- return "MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY";
- case MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_KSV_LIST_REVOKED:
- return "MOD_HDCP_STATUS_HDCP1_KSV_LIST_REVOKED";
- case MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED:
- return "MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED";
- case MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE:
- return "MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE";
- case MOD_HDCP_STATUS_HDCP1_INVALID_BKSV:
- return "MOD_HDCP_STATUS_HDCP1_INVALID_BKSV";
- case MOD_HDCP_STATUS_DDC_FAILURE:
- return "MOD_HDCP_STATUS_DDC_FAILURE";
- case MOD_HDCP_STATUS_INVALID_OPERATION:
- return "MOD_HDCP_STATUS_INVALID_OPERATION";
- case MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE:
- return "MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE";
- case MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING:
- return "MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING";
- case MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING:
- return "MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING";
- case MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING:
- return "MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING";
- case MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED:
- return "MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED";
- case MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING:
- return "MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING";
- case MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED:
- return "MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED";
- case MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY:
- return "MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY";
- case MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING:
- return "MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING";
- case MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST:
- return "MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST";
- case MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE";
- case MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE:
- return "MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE";
- case MOD_HDCP_STATUS_UNSUPPORTED_PSP_VER_FAILURE:
- return "MOD_HDCP_STATUS_UNSUPPORTED_PSP_VER_FAILURE";
+ MOD_HDCP_STATUS_LIST(CASE_FORMAT)
default:
return "MOD_HDCP_STATUS_UNKNOWN";
}
diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h
index 1d83c1b9da10..26553aa4c5ca 100644
--- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h
+++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.h
@@ -31,6 +31,7 @@
#define HDCP_LOG_FSM(hdcp, ...) DRM_DEBUG_KMS(__VA_ARGS__)
#define HDCP_LOG_TOP(hdcp, ...) pr_debug("[HDCP_TOP]:"__VA_ARGS__)
#define HDCP_LOG_DDC(hdcp, ...) pr_debug("[HDCP_DDC]:"__VA_ARGS__)
+#define HDCP_LOG_TRA(hdcp) do {} while (0)
/* default logs */
#define HDCP_ERROR_TRACE(hdcp, status) \
@@ -131,4 +132,9 @@
HDCP_LOG_TOP(hdcp, "[Link %d] %s display %d", hdcp->config.index, __func__, i); \
} while (0)
+#define HDCP_AUTH_COMPLETE_TRACE(hdcp) do { \
+ mod_hdcp_log_ddc_trace(hdcp); \
+ HDCP_LOG_TRA(hdcp); \
+} while (0)
+
#endif // MOD_HDCP_LOG_H_
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h
index b51ddf2846df..835467225458 100644
--- a/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h
+++ b/drivers/gpu/drm/amd/display/modules/inc/mod_hdcp.h
@@ -35,69 +35,74 @@ struct mod_hdcp;
#define MAX_NUM_OF_DISPLAYS 6
#define MAX_NUM_OF_ATTEMPTS 4
#define MAX_NUM_OF_ERROR_TRACE 10
+#define MOD_HDCP_STATUS_LIST(FORMAT) \
+ FORMAT(MOD_HDCP_STATUS_SUCCESS) \
+ FORMAT(MOD_HDCP_STATUS_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_RESET_NEEDED) \
+ FORMAT(MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND) \
+ FORMAT(MOD_HDCP_STATUS_DISPLAY_NOT_FOUND) \
+ FORMAT(MOD_HDCP_STATUS_INVALID_STATE) \
+ FORMAT(MOD_HDCP_STATUS_NOT_IMPLEMENTED) \
+ FORMAT(MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_BKSV_REVOKED) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_KSV_LIST_REVOKED) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP1_INVALID_BKSV) \
+ FORMAT(MOD_HDCP_STATUS_DDC_FAILURE) /* TODO: specific errors */ \
+ FORMAT(MOD_HDCP_STATUS_INVALID_OPERATION) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_UNSUPPORTED_PSP_VER_FAILURE) \
+ FORMAT(MOD_HDCP_STATUS_HDCP2_LOCALITY_COMBO_READ_FAILURE)
+
+#define ENUM_FORMAT(entry) entry,
/* detailed return status */
enum mod_hdcp_status {
- MOD_HDCP_STATUS_SUCCESS = 0,
- MOD_HDCP_STATUS_FAILURE,
- MOD_HDCP_STATUS_RESET_NEEDED,
- MOD_HDCP_STATUS_DISPLAY_OUT_OF_BOUND,
- MOD_HDCP_STATUS_DISPLAY_NOT_FOUND,
- MOD_HDCP_STATUS_INVALID_STATE,
- MOD_HDCP_STATUS_NOT_IMPLEMENTED,
- MOD_HDCP_STATUS_INTERNAL_POLICY_FAILURE,
- MOD_HDCP_STATUS_UPDATE_TOPOLOGY_FAILURE,
- MOD_HDCP_STATUS_CREATE_PSP_SERVICE_FAILURE,
- MOD_HDCP_STATUS_DESTROY_PSP_SERVICE_FAILURE,
- MOD_HDCP_STATUS_HDCP1_CREATE_SESSION_FAILURE,
- MOD_HDCP_STATUS_HDCP1_DESTROY_SESSION_FAILURE,
- MOD_HDCP_STATUS_HDCP1_VALIDATE_ENCRYPTION_FAILURE,
- MOD_HDCP_STATUS_HDCP1_NOT_HDCP_REPEATER,
- MOD_HDCP_STATUS_HDCP1_NOT_CAPABLE,
- MOD_HDCP_STATUS_HDCP1_R0_PRIME_PENDING,
- MOD_HDCP_STATUS_HDCP1_VALIDATE_RX_FAILURE,
- MOD_HDCP_STATUS_HDCP1_BKSV_REVOKED,
- MOD_HDCP_STATUS_HDCP1_KSV_LIST_NOT_READY,
- MOD_HDCP_STATUS_HDCP1_VALIDATE_KSV_LIST_FAILURE,
- MOD_HDCP_STATUS_HDCP1_KSV_LIST_REVOKED,
- MOD_HDCP_STATUS_HDCP1_ENABLE_ENCRYPTION_FAILURE,
- MOD_HDCP_STATUS_HDCP1_ENABLE_STREAM_ENCRYPTION_FAILURE,
- MOD_HDCP_STATUS_HDCP1_MAX_CASCADE_EXCEEDED_FAILURE,
- MOD_HDCP_STATUS_HDCP1_MAX_DEVS_EXCEEDED_FAILURE,
- MOD_HDCP_STATUS_HDCP1_DEVICE_COUNT_MISMATCH_FAILURE,
- MOD_HDCP_STATUS_HDCP1_LINK_INTEGRITY_FAILURE,
- MOD_HDCP_STATUS_HDCP1_REAUTH_REQUEST_ISSUED,
- MOD_HDCP_STATUS_HDCP1_LINK_MAINTENANCE_FAILURE,
- MOD_HDCP_STATUS_HDCP1_INVALID_BKSV,
- MOD_HDCP_STATUS_DDC_FAILURE, /* TODO: specific errors */
- MOD_HDCP_STATUS_INVALID_OPERATION,
- MOD_HDCP_STATUS_HDCP2_NOT_CAPABLE,
- MOD_HDCP_STATUS_HDCP2_CREATE_SESSION_FAILURE,
- MOD_HDCP_STATUS_HDCP2_DESTROY_SESSION_FAILURE,
- MOD_HDCP_STATUS_HDCP2_PREP_AKE_INIT_FAILURE,
- MOD_HDCP_STATUS_HDCP2_AKE_CERT_PENDING,
- MOD_HDCP_STATUS_HDCP2_H_PRIME_PENDING,
- MOD_HDCP_STATUS_HDCP2_PAIRING_INFO_PENDING,
- MOD_HDCP_STATUS_HDCP2_VALIDATE_AKE_CERT_FAILURE,
- MOD_HDCP_STATUS_HDCP2_AKE_CERT_REVOKED,
- MOD_HDCP_STATUS_HDCP2_VALIDATE_H_PRIME_FAILURE,
- MOD_HDCP_STATUS_HDCP2_VALIDATE_PAIRING_INFO_FAILURE,
- MOD_HDCP_STATUS_HDCP2_PREP_LC_INIT_FAILURE,
- MOD_HDCP_STATUS_HDCP2_L_PRIME_PENDING,
- MOD_HDCP_STATUS_HDCP2_VALIDATE_L_PRIME_FAILURE,
- MOD_HDCP_STATUS_HDCP2_PREP_EKS_FAILURE,
- MOD_HDCP_STATUS_HDCP2_ENABLE_ENCRYPTION_FAILURE,
- MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_NOT_READY,
- MOD_HDCP_STATUS_HDCP2_VALIDATE_RX_ID_LIST_FAILURE,
- MOD_HDCP_STATUS_HDCP2_RX_ID_LIST_REVOKED,
- MOD_HDCP_STATUS_HDCP2_ENABLE_STREAM_ENCRYPTION_FAILURE,
- MOD_HDCP_STATUS_HDCP2_STREAM_READY_PENDING,
- MOD_HDCP_STATUS_HDCP2_VALIDATE_STREAM_READY_FAILURE,
- MOD_HDCP_STATUS_HDCP2_PREPARE_STREAM_MANAGEMENT_FAILURE,
- MOD_HDCP_STATUS_HDCP2_REAUTH_REQUEST,
- MOD_HDCP_STATUS_HDCP2_REAUTH_LINK_INTEGRITY_FAILURE,
- MOD_HDCP_STATUS_HDCP2_DEVICE_COUNT_MISMATCH_FAILURE,
- MOD_HDCP_STATUS_UNSUPPORTED_PSP_VER_FAILURE,
+ MOD_HDCP_STATUS_LIST(ENUM_FORMAT)
};
struct mod_hdcp_displayport {
@@ -214,8 +219,9 @@ struct mod_hdcp_link_adjustment_hdcp2 {
uint8_t force_type : 2;
uint8_t force_no_stored_km : 1;
uint8_t increase_h_prime_timeout: 1;
- uint8_t force_sw_locality_check : 1;
- uint8_t reserved : 2;
+ uint8_t use_fw_locality_check : 1;
+ uint8_t use_sw_locality_fallback: 1;
+ uint8_t reserved : 1;
};
struct mod_hdcp_link_adjustment {
@@ -230,9 +236,23 @@ struct mod_hdcp_error {
uint8_t state_id;
};
+struct mod_hdcp1_trace {
+ uint8_t attempt_count;
+ uint8_t downstream_device_count;
+};
+
+struct mod_hdcp2_trace {
+ uint8_t attempt_count;
+ uint8_t downstream_device_count;
+ uint8_t hdcp1_device_downstream;
+ uint8_t hdcp2_legacy_device_downstream;
+};
+
struct mod_hdcp_trace {
struct mod_hdcp_error errors[MAX_NUM_OF_ERROR_TRACE];
uint8_t error_count;
+ struct mod_hdcp1_trace hdcp1;
+ struct mod_hdcp2_trace hdcp2;
};
enum mod_hdcp_encryption_status {
@@ -303,10 +323,6 @@ struct mod_hdcp_display_query {
struct mod_hdcp_config {
struct mod_hdcp_psp psp;
struct mod_hdcp_ddc ddc;
- struct {
- uint8_t lc_enable_sw_fallback : 1;
- uint8_t reserved : 7;
- } debug;
uint8_t index;
};
diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c
index 29ccd3532d13..fd139b219bf9 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c
+++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.c
@@ -975,6 +975,34 @@ bool psr_su_set_dsc_slice_height(struct dc *dc, struct dc_link *link,
return true;
}
+void set_replay_frame_skip_number(struct dc_link *link,
+ enum replay_coasting_vtotal_type type,
+ uint32_t coasting_vtotal_refresh_rate_mhz,
+ uint32_t flicker_free_refresh_rate_mhz,
+ bool is_defer)
+{
+ uint32_t *frame_skip_number_array = NULL;
+ uint32_t frame_skip_number = 0;
+
+ if (link == NULL || flicker_free_refresh_rate_mhz == 0 || coasting_vtotal_refresh_rate_mhz == 0)
+ return;
+
+ if (is_defer)
+ frame_skip_number_array = link->replay_settings.defer_frame_skip_number_table;
+ else
+ frame_skip_number_array = link->replay_settings.frame_skip_number_table;
+
+ if (frame_skip_number_array == NULL)
+ return;
+
+ frame_skip_number = coasting_vtotal_refresh_rate_mhz / flicker_free_refresh_rate_mhz;
+
+ if (frame_skip_number >= 1)
+ frame_skip_number_array[type] = frame_skip_number - 1;
+ else
+ frame_skip_number_array[type] = 0;
+}
+
void set_replay_defer_update_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal)
@@ -987,6 +1015,8 @@ void update_replay_coasting_vtotal_from_defer(struct dc_link *link,
{
link->replay_settings.coasting_vtotal_table[type] =
link->replay_settings.defer_update_coasting_vtotal_table[type];
+ link->replay_settings.frame_skip_number_table[type] =
+ link->replay_settings.defer_frame_skip_number_table[type];
}
void set_replay_coasting_vtotal(struct dc_link *link,
@@ -1007,6 +1037,9 @@ void calculate_replay_link_off_frame_count(struct dc_link *link,
uint8_t max_link_off_frame_count = 0;
uint16_t max_deviation_line = 0, pixel_deviation_per_line = 0;
+ if (!link || link->replay_settings.config.replay_version != DC_FREESYNC_REPLAY)
+ return;
+
max_deviation_line = link->dpcd_caps.pr_info.max_deviation_line;
pixel_deviation_per_line = link->dpcd_caps.pr_info.pixel_deviation_per_line;
diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h
index 391209a3bf29..87d31d9dce5a 100644
--- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.h
+++ b/drivers/gpu/drm/amd/display/modules/power/power_helpers.h
@@ -60,6 +60,11 @@ void set_replay_coasting_vtotal(struct dc_link *link,
void set_replay_defer_update_coasting_vtotal(struct dc_link *link,
enum replay_coasting_vtotal_type type,
uint32_t vtotal);
+void set_replay_frame_skip_number(struct dc_link *link,
+ enum replay_coasting_vtotal_type type,
+ uint32_t coasting_vtotal_refresh_rate_Mhz,
+ uint32_t flicker_free_refresh_rate_Mhz,
+ bool is_defer);
void update_replay_coasting_vtotal_from_defer(struct dc_link *link,
enum replay_coasting_vtotal_type type);
void set_replay_low_rr_full_screen_video_src_vtotal(struct dc_link *link, uint16_t vtotal);
diff --git a/drivers/gpu/drm/amd/include/amd_cper.h b/drivers/gpu/drm/amd/include/amd_cper.h
index 086869264425..a252ee4c7874 100644
--- a/drivers/gpu/drm/amd/include/amd_cper.h
+++ b/drivers/gpu/drm/amd/include/amd_cper.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: MIT */
/*
* Copyright 2025 Advanced Micro Devices, Inc.
*
diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h
index 75efda2969cf..17945094a138 100644
--- a/drivers/gpu/drm/amd/include/amd_shared.h
+++ b/drivers/gpu/drm/amd/include/amd_shared.h
@@ -109,6 +109,7 @@ enum amd_ip_block_type {
AMD_IP_BLOCK_TYPE_VPE,
AMD_IP_BLOCK_TYPE_UMSCH_MM,
AMD_IP_BLOCK_TYPE_ISP,
+ AMD_IP_BLOCK_TYPE_RAS,
AMD_IP_BLOCK_TYPE_NUM,
};
diff --git a/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_d.h b/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_d.h
index 2176548e9203..9778822dd2a0 100644
--- a/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_d.h
+++ b/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_d.h
@@ -60,5 +60,10 @@
#define mmVCE_VCPU_CACHE_SIZE1 0x800C
#define mmVCE_VCPU_CACHE_SIZE2 0x800E
#define mmVCE_VCPU_CNTL 0x8005
+#define mmVCE_VCPU_SCRATCH7 0x8037
+#define mmVCE_FW_REG_STATUS 0x8384
+#define mmVCE_LMI_FW_PERIODIC_CTRL 0x8388
+#define mmVCE_LMI_FW_START_KEYSEL 0x8386
+
#endif
diff --git a/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_sh_mask.h b/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_sh_mask.h
index ea5b26b11cb1..1f82d6f5abde 100644
--- a/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_sh_mask.h
+++ b/drivers/gpu/drm/amd/include/asic_reg/vce/vce_1_0_sh_mask.h
@@ -61,6 +61,8 @@
#define VCE_RB_WPTR__RB_WPTR__SHIFT 0x00000004
#define VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK 0x00000001L
#define VCE_SOFT_RESET__ECPU_SOFT_RESET__SHIFT 0x00000000
+#define VCE_SOFT_RESET__FME_SOFT_RESET_MASK 0x00000004L
+#define VCE_SOFT_RESET__FME_SOFT_RESET__SHIFT 0x00000002
#define VCE_STATUS__JOB_BUSY_MASK 0x00000001L
#define VCE_STATUS__JOB_BUSY__SHIFT 0x00000000
#define VCE_STATUS__UENC_BUSY_MASK 0x00000100L
@@ -95,5 +97,13 @@
#define VCE_VCPU_CNTL__CLK_EN__SHIFT 0x00000000
#define VCE_VCPU_CNTL__RBBM_SOFT_RESET_MASK 0x00040000L
#define VCE_VCPU_CNTL__RBBM_SOFT_RESET__SHIFT 0x00000012
+#define VCE_CLOCK_GATING_A__CGC_DYN_CLOCK_MODE_MASK 0x00010000
+#define VCE_CLOCK_GATING_A__CGC_DYN_CLOCK_MODE_SHIFT 0x00000010
+#define VCE_FW_REG_STATUS__BUSY_MASK 0x0000001
+#define VCE_FW_REG_STATUS__BUSY__SHIFT 0x0000001
+#define VCE_FW_REG_STATUS__PASS_MASK 0x0000008
+#define VCE_FW_REG_STATUS__PASS__SHIFT 0x0000003
+#define VCE_FW_REG_STATUS__DONE_MASK 0x0000800
+#define VCE_FW_REG_STATUS__DONE__SHIFT 0x000000b
#endif
diff --git a/drivers/gpu/drm/amd/include/ivsrcid/vcn/irqsrcs_vcn_5_0.h b/drivers/gpu/drm/amd/include/ivsrcid/vcn/irqsrcs_vcn_5_0.h
index 64b553e7de1a..e7fdcee22a71 100644
--- a/drivers/gpu/drm/amd/include/ivsrcid/vcn/irqsrcs_vcn_5_0.h
+++ b/drivers/gpu/drm/amd/include/ivsrcid/vcn/irqsrcs_vcn_5_0.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 */
+/* SPDX-License-Identifier: MIT */
/*
* Copyright 2024 Advanced Micro Devices, Inc. All rights reserved.
diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index 2b0cdb2a2775..2366e68262e6 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -454,7 +454,7 @@ struct amd_pm_funcs {
bool gate,
int inst);
int (*set_clockgating_by_smu)(void *handle, uint32_t msg_id);
- int (*set_power_limit)(void *handle, uint32_t n);
+ int (*set_power_limit)(void *handle, uint32_t limit_type, uint32_t n);
int (*get_power_limit)(void *handle, uint32_t *limit,
enum pp_power_limit_level pp_limit_level,
enum pp_power_type power_type);
@@ -532,6 +532,110 @@ struct metrics_table_header {
uint8_t content_revision;
};
+enum amdgpu_metrics_attr_id {
+ AMDGPU_METRICS_ATTR_ID_TEMPERATURE_HOTSPOT,
+ AMDGPU_METRICS_ATTR_ID_TEMPERATURE_MEM,
+ AMDGPU_METRICS_ATTR_ID_TEMPERATURE_VRSOC,
+ AMDGPU_METRICS_ATTR_ID_CURR_SOCKET_POWER,
+ AMDGPU_METRICS_ATTR_ID_AVERAGE_GFX_ACTIVITY,
+ AMDGPU_METRICS_ATTR_ID_AVERAGE_UMC_ACTIVITY,
+ AMDGPU_METRICS_ATTR_ID_MEM_MAX_BANDWIDTH,
+ AMDGPU_METRICS_ATTR_ID_ENERGY_ACCUMULATOR,
+ AMDGPU_METRICS_ATTR_ID_SYSTEM_CLOCK_COUNTER,
+ AMDGPU_METRICS_ATTR_ID_ACCUMULATION_COUNTER,
+ AMDGPU_METRICS_ATTR_ID_PROCHOT_RESIDENCY_ACC,
+ AMDGPU_METRICS_ATTR_ID_PPT_RESIDENCY_ACC,
+ AMDGPU_METRICS_ATTR_ID_SOCKET_THM_RESIDENCY_ACC,
+ AMDGPU_METRICS_ATTR_ID_VR_THM_RESIDENCY_ACC,
+ AMDGPU_METRICS_ATTR_ID_HBM_THM_RESIDENCY_ACC,
+ AMDGPU_METRICS_ATTR_ID_GFXCLK_LOCK_STATUS,
+ AMDGPU_METRICS_ATTR_ID_PCIE_LINK_WIDTH,
+ AMDGPU_METRICS_ATTR_ID_PCIE_LINK_SPEED,
+ AMDGPU_METRICS_ATTR_ID_XGMI_LINK_WIDTH,
+ AMDGPU_METRICS_ATTR_ID_XGMI_LINK_SPEED,
+ AMDGPU_METRICS_ATTR_ID_GFX_ACTIVITY_ACC,
+ AMDGPU_METRICS_ATTR_ID_MEM_ACTIVITY_ACC,
+ AMDGPU_METRICS_ATTR_ID_PCIE_BANDWIDTH_ACC,
+ AMDGPU_METRICS_ATTR_ID_PCIE_BANDWIDTH_INST,
+ AMDGPU_METRICS_ATTR_ID_PCIE_L0_TO_RECOV_COUNT_ACC,
+ AMDGPU_METRICS_ATTR_ID_PCIE_REPLAY_COUNT_ACC,
+ AMDGPU_METRICS_ATTR_ID_PCIE_REPLAY_ROVER_COUNT_ACC,
+ AMDGPU_METRICS_ATTR_ID_PCIE_NAK_SENT_COUNT_ACC,
+ AMDGPU_METRICS_ATTR_ID_PCIE_NAK_RCVD_COUNT_ACC,
+ AMDGPU_METRICS_ATTR_ID_XGMI_READ_DATA_ACC,
+ AMDGPU_METRICS_ATTR_ID_XGMI_WRITE_DATA_ACC,
+ AMDGPU_METRICS_ATTR_ID_XGMI_LINK_STATUS,
+ AMDGPU_METRICS_ATTR_ID_FIRMWARE_TIMESTAMP,
+ AMDGPU_METRICS_ATTR_ID_CURRENT_GFXCLK,
+ AMDGPU_METRICS_ATTR_ID_CURRENT_SOCCLK,
+ AMDGPU_METRICS_ATTR_ID_CURRENT_VCLK0,
+ AMDGPU_METRICS_ATTR_ID_CURRENT_DCLK0,
+ AMDGPU_METRICS_ATTR_ID_CURRENT_UCLK,
+ AMDGPU_METRICS_ATTR_ID_NUM_PARTITION,
+ AMDGPU_METRICS_ATTR_ID_PCIE_LC_PERF_OTHER_END_RECOVERY,
+ AMDGPU_METRICS_ATTR_ID_GFX_BUSY_INST,
+ AMDGPU_METRICS_ATTR_ID_JPEG_BUSY,
+ AMDGPU_METRICS_ATTR_ID_VCN_BUSY,
+ AMDGPU_METRICS_ATTR_ID_GFX_BUSY_ACC,
+ AMDGPU_METRICS_ATTR_ID_GFX_BELOW_HOST_LIMIT_PPT_ACC,
+ AMDGPU_METRICS_ATTR_ID_GFX_BELOW_HOST_LIMIT_THM_ACC,
+ AMDGPU_METRICS_ATTR_ID_GFX_LOW_UTILIZATION_ACC,
+ AMDGPU_METRICS_ATTR_ID_GFX_BELOW_HOST_LIMIT_TOTAL_ACC,
+ AMDGPU_METRICS_ATTR_ID_MAX,
+};
+
+enum amdgpu_metrics_attr_type {
+ AMDGPU_METRICS_TYPE_U8,
+ AMDGPU_METRICS_TYPE_S8,
+ AMDGPU_METRICS_TYPE_U16,
+ AMDGPU_METRICS_TYPE_S16,
+ AMDGPU_METRICS_TYPE_U32,
+ AMDGPU_METRICS_TYPE_S32,
+ AMDGPU_METRICS_TYPE_U64,
+ AMDGPU_METRICS_TYPE_S64,
+ AMDGPU_METRICS_TYPE_MAX,
+};
+
+enum amdgpu_metrics_attr_unit {
+ /* None */
+ AMDGPU_METRICS_UNIT_NONE,
+ /* MHz*/
+ AMDGPU_METRICS_UNIT_CLOCK_1,
+ /* Degree Celsius*/
+ AMDGPU_METRICS_UNIT_TEMP_1,
+ /* Watts*/
+ AMDGPU_METRICS_UNIT_POWER_1,
+ /* In nanoseconds*/
+ AMDGPU_METRICS_UNIT_TIME_1,
+ /* In 10 nanoseconds*/
+ AMDGPU_METRICS_UNIT_TIME_2,
+ /* Speed in GT/s */
+ AMDGPU_METRICS_UNIT_SPEED_1,
+ /* Speed in 0.1 GT/s */
+ AMDGPU_METRICS_UNIT_SPEED_2,
+ /* Bandwidth GB/s */
+ AMDGPU_METRICS_UNIT_BW_1,
+ /* Data in KB */
+ AMDGPU_METRICS_UNIT_DATA_1,
+ /* Percentage */
+ AMDGPU_METRICS_UNIT_PERCENT,
+ AMDGPU_METRICS_UNIT_MAX,
+};
+
+#define AMDGPU_METRICS_ATTR_UNIT_MASK 0xFF000000
+#define AMDGPU_METRICS_ATTR_UNIT_SHIFT 24
+#define AMDGPU_METRICS_ATTR_TYPE_MASK 0x00F00000
+#define AMDGPU_METRICS_ATTR_TYPE_SHIFT 20
+#define AMDGPU_METRICS_ATTR_ID_MASK 0x000FFC00
+#define AMDGPU_METRICS_ATTR_ID_SHIFT 10
+#define AMDGPU_METRICS_ATTR_INST_MASK 0x000003FF
+#define AMDGPU_METRICS_ATTR_INST_SHIFT 0
+
+#define AMDGPU_METRICS_ENC_ATTR(unit, type, id, inst) \
+ (((u64)(unit) << AMDGPU_METRICS_ATTR_UNIT_SHIFT) | \
+ ((u64)(type) << AMDGPU_METRICS_ATTR_TYPE_SHIFT) | \
+ ((u64)(id) << AMDGPU_METRICS_ATTR_ID_SHIFT) | (inst))
+
/*
* gpu_metrics_v1_0 is not recommended as it's not naturally aligned.
* Use gpu_metrics_v1_1 or later instead.
@@ -1221,6 +1325,19 @@ struct gpu_metrics_v1_8 {
uint32_t pcie_lc_perf_other_end_recovery;
};
+struct gpu_metrics_attr {
+ /* Field type encoded with AMDGPU_METRICS_ENC_ATTR */
+ uint64_t attr_encoding;
+ /* Attribute value, depends on attr_encoding */
+ void *attr_value;
+};
+
+struct gpu_metrics_v1_9 {
+ struct metrics_table_header common_header;
+ int attr_count;
+ struct gpu_metrics_attr metrics_attrs[];
+};
+
/*
* gpu_metrics_v2_0 is not recommended as it's not naturally aligned.
* Use gpu_metrics_v2_1 or later instead.
@@ -1703,4 +1820,10 @@ struct amdgpu_partition_metrics_v1_0 {
uint64_t gfx_below_host_limit_total_acc[MAX_XCC];
};
+struct amdgpu_partition_metrics_v1_1 {
+ struct metrics_table_header common_header;
+ int attr_count;
+ struct gpu_metrics_attr metrics_attrs[];
+};
+
#endif
diff --git a/drivers/gpu/drm/amd/include/mes_v11_api_def.h b/drivers/gpu/drm/amd/include/mes_v11_api_def.h
index ab1cfc92dbeb..f9629d42ada2 100644
--- a/drivers/gpu/drm/amd/include/mes_v11_api_def.h
+++ b/drivers/gpu/drm/amd/include/mes_v11_api_def.h
@@ -345,7 +345,8 @@ union MESAPI__REMOVE_QUEUE {
uint32_t unmap_kiq_utility_queue : 1;
uint32_t preempt_legacy_gfx_queue : 1;
uint32_t unmap_legacy_queue : 1;
- uint32_t reserved : 28;
+ uint32_t remove_queue_after_reset : 1;
+ uint32_t reserved : 27;
};
struct MES_API_STATUS api_status;
diff --git a/drivers/gpu/drm/amd/include/mes_v12_api_def.h b/drivers/gpu/drm/amd/include/mes_v12_api_def.h
index 69611c7e30e3..2f12cba4eb66 100644
--- a/drivers/gpu/drm/amd/include/mes_v12_api_def.h
+++ b/drivers/gpu/drm/amd/include/mes_v12_api_def.h
@@ -399,7 +399,8 @@ union MESAPI__REMOVE_QUEUE {
uint32_t unmap_kiq_utility_queue : 1;
uint32_t preempt_legacy_gfx_queue : 1;
uint32_t unmap_legacy_queue : 1;
- uint32_t reserved : 28;
+ uint32_t remove_queue_after_reset : 1;
+ uint32_t reserved : 27;
};
struct MES_API_STATUS api_status;
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index 518d07afc7df..79b174e5326d 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -195,24 +195,6 @@ int amdgpu_dpm_set_mp1_state(struct amdgpu_device *adev,
return ret;
}
-int amdgpu_dpm_notify_rlc_state(struct amdgpu_device *adev, bool en)
-{
- int ret = 0;
- const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
-
- if (pp_funcs && pp_funcs->notify_rlc_state) {
- mutex_lock(&adev->pm.mutex);
-
- ret = pp_funcs->notify_rlc_state(
- adev->powerplay.pp_handle,
- en);
-
- mutex_unlock(&adev->pm.mutex);
- }
-
- return ret;
-}
-
int amdgpu_dpm_is_baco_supported(struct amdgpu_device *adev)
{
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
@@ -1205,8 +1187,11 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table)
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
int ret = 0;
- if (!pp_funcs->get_pp_table)
- return 0;
+ if (!table)
+ return -EINVAL;
+
+ if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled)
+ return -EOPNOTSUPP;
mutex_lock(&adev->pm.mutex);
ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle,
@@ -1616,6 +1601,7 @@ int amdgpu_dpm_get_power_limit(struct amdgpu_device *adev,
}
int amdgpu_dpm_set_power_limit(struct amdgpu_device *adev,
+ uint32_t limit_type,
uint32_t limit)
{
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
@@ -1626,7 +1612,7 @@ int amdgpu_dpm_set_power_limit(struct amdgpu_device *adev,
mutex_lock(&adev->pm.mutex);
ret = pp_funcs->set_power_limit(adev->powerplay.pp_handle,
- limit);
+ limit_type, limit);
mutex_unlock(&adev->pm.mutex);
return ret;
@@ -1732,7 +1718,10 @@ int amdgpu_dpm_set_pp_table(struct amdgpu_device *adev,
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
int ret = 0;
- if (!pp_funcs->set_pp_table)
+ if (!buf || !size)
+ return -EINVAL;
+
+ if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled)
return -EOPNOTSUPP;
mutex_lock(&adev->pm.mutex);
@@ -2139,3 +2128,10 @@ ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id,
return ret;
}
+
+const struct ras_smu_drv *amdgpu_dpm_get_ras_smu_driver(struct amdgpu_device *adev)
+{
+ void *pp_handle = adev->powerplay.pp_handle;
+
+ return smu_get_ras_smu_driver(pp_handle);
+}
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index b5fbb0fd1dc0..65296a819e6a 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -108,8 +108,9 @@ const char * const amdgpu_pp_profile_name[] = {
static int amdgpu_pm_dev_state_check(struct amdgpu_device *adev, bool runpm)
{
bool runpm_check = runpm ? adev->in_runpm : false;
+ bool full_init = (adev->init_lvl->level == AMDGPU_INIT_LEVEL_DEFAULT);
- if (amdgpu_in_reset(adev))
+ if (amdgpu_in_reset(adev) || !full_init)
return -EBUSY;
if (adev->in_suspend && !runpm_check)
@@ -173,7 +174,6 @@ static int amdgpu_pm_get_access_if_active(struct amdgpu_device *adev)
*/
static inline void amdgpu_pm_put_access(struct amdgpu_device *adev)
{
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
}
@@ -2506,7 +2506,7 @@ static struct amdgpu_device_attr amdgpu_device_attrs[] = {
AMDGPU_DEVICE_ATTR_RO(pp_num_states, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF),
AMDGPU_DEVICE_ATTR_RO(pp_cur_state, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF),
AMDGPU_DEVICE_ATTR_RW(pp_force_state, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF),
- AMDGPU_DEVICE_ATTR_RW(pp_table, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF),
+ AMDGPU_DEVICE_ATTR_RW(pp_table, ATTR_FLAG_BASIC),
AMDGPU_DEVICE_ATTR_RW(pp_dpm_sclk, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF,
.attr_update = pp_dpm_clk_default_attr_update),
AMDGPU_DEVICE_ATTR_RW(pp_dpm_mclk, ATTR_FLAG_BASIC|ATTR_FLAG_ONEVF,
@@ -2638,6 +2638,15 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_
if (amdgpu_dpm_get_apu_thermal_limit(adev, &limit) ==
-EOPNOTSUPP)
*states = ATTR_STATE_UNSUPPORTED;
+ } else if (DEVICE_ATTR_IS(pp_table)) {
+ int ret;
+ char *tmp = NULL;
+
+ ret = amdgpu_dpm_get_pp_table(adev, &tmp);
+ if (ret == -EOPNOTSUPP || !tmp)
+ *states = ATTR_STATE_UNSUPPORTED;
+ else
+ *states = ATTR_STATE_SUPPORTED;
}
switch (gc_ver) {
@@ -3372,7 +3381,9 @@ static ssize_t amdgpu_hwmon_show_power_label(struct device *dev,
to_sensor_dev_attr(attr)->index == PP_PWR_TYPE_FAST ?
"fastPPT" : "slowPPT");
else
- return sysfs_emit(buf, "PPT\n");
+ return sysfs_emit(buf, "%s\n",
+ to_sensor_dev_attr(attr)->index == PP_PWR_TYPE_FAST ?
+ "PPT1" : "PPT");
}
static ssize_t amdgpu_hwmon_set_power_cap(struct device *dev,
@@ -3390,13 +3401,12 @@ static ssize_t amdgpu_hwmon_set_power_cap(struct device *dev,
return err;
value = value / 1000000; /* convert to Watt */
- value |= limit_type << 24;
err = amdgpu_pm_get_access(adev);
if (err < 0)
return err;
- err = amdgpu_dpm_set_power_limit(adev, value);
+ err = amdgpu_dpm_set_power_limit(adev, limit_type, value);
amdgpu_pm_put_access(adev);
@@ -3578,7 +3588,6 @@ static SENSOR_DEVICE_ATTR(power1_cap_min, S_IRUGO, amdgpu_hwmon_show_power_cap_m
static SENSOR_DEVICE_ATTR(power1_cap, S_IRUGO | S_IWUSR, amdgpu_hwmon_show_power_cap, amdgpu_hwmon_set_power_cap, 0);
static SENSOR_DEVICE_ATTR(power1_cap_default, S_IRUGO, amdgpu_hwmon_show_power_cap_default, NULL, 0);
static SENSOR_DEVICE_ATTR(power1_label, S_IRUGO, amdgpu_hwmon_show_power_label, NULL, 0);
-static SENSOR_DEVICE_ATTR(power2_average, S_IRUGO, amdgpu_hwmon_show_power_avg, NULL, 1);
static SENSOR_DEVICE_ATTR(power2_cap_max, S_IRUGO, amdgpu_hwmon_show_power_cap_max, NULL, 1);
static SENSOR_DEVICE_ATTR(power2_cap_min, S_IRUGO, amdgpu_hwmon_show_power_cap_min, NULL, 1);
static SENSOR_DEVICE_ATTR(power2_cap, S_IRUGO | S_IWUSR, amdgpu_hwmon_show_power_cap, amdgpu_hwmon_set_power_cap, 1);
@@ -3627,7 +3636,6 @@ static struct attribute *hwmon_attributes[] = {
&sensor_dev_attr_power1_cap.dev_attr.attr,
&sensor_dev_attr_power1_cap_default.dev_attr.attr,
&sensor_dev_attr_power1_label.dev_attr.attr,
- &sensor_dev_attr_power2_average.dev_attr.attr,
&sensor_dev_attr_power2_cap_max.dev_attr.attr,
&sensor_dev_attr_power2_cap_min.dev_attr.attr,
&sensor_dev_attr_power2_cap.dev_attr.attr,
@@ -3826,13 +3834,14 @@ static umode_t hwmon_attributes_visible(struct kobject *kobj,
return 0;
/* only Vangogh has fast PPT limit and power labels */
- if (!(gc_ver == IP_VERSION(10, 3, 1)) &&
- (attr == &sensor_dev_attr_power2_average.dev_attr.attr ||
- attr == &sensor_dev_attr_power2_cap_max.dev_attr.attr ||
+ if ((attr == &sensor_dev_attr_power2_cap_max.dev_attr.attr ||
attr == &sensor_dev_attr_power2_cap_min.dev_attr.attr ||
attr == &sensor_dev_attr_power2_cap.dev_attr.attr ||
attr == &sensor_dev_attr_power2_cap_default.dev_attr.attr ||
- attr == &sensor_dev_attr_power2_label.dev_attr.attr))
+ attr == &sensor_dev_attr_power2_label.dev_attr.attr) &&
+ (amdgpu_dpm_get_power_limit(adev, &tmp,
+ PP_PWR_LIMIT_MAX,
+ PP_PWR_TYPE_FAST) == -EOPNOTSUPP))
return 0;
return effective_mode;
@@ -4724,14 +4733,14 @@ int amdgpu_pm_sysfs_init(struct amdgpu_device *adev)
ret = devm_device_add_group(adev->dev,
&amdgpu_pm_policy_attr_group);
if (ret)
- goto err_out0;
+ goto err_out1;
}
if (amdgpu_dpm_is_temp_metrics_supported(adev, SMU_TEMP_METRIC_GPUBOARD)) {
ret = devm_device_add_group(adev->dev,
&amdgpu_board_attr_group);
if (ret)
- goto err_out0;
+ goto err_out1;
if (amdgpu_pm_get_sensor_generic(adev, AMDGPU_PP_SENSOR_MAXNODEPOWERLIMIT,
(void *)&tmp) != -EOPNOTSUPP) {
sysfs_add_file_to_group(&adev->dev->kobj,
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index 65c1d98af26c..aa3f427819a0 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -424,8 +424,6 @@ int amdgpu_dpm_mode1_reset(struct amdgpu_device *adev);
int amdgpu_dpm_set_mp1_state(struct amdgpu_device *adev,
enum pp_mp1_state mp1_state);
-int amdgpu_dpm_notify_rlc_state(struct amdgpu_device *adev, bool en);
-
int amdgpu_dpm_set_gfx_power_up_by_imu(struct amdgpu_device *adev);
int amdgpu_dpm_baco_exit(struct amdgpu_device *adev);
@@ -553,7 +551,7 @@ int amdgpu_dpm_get_power_limit(struct amdgpu_device *adev,
enum pp_power_limit_level pp_limit_level,
enum pp_power_type power_type);
int amdgpu_dpm_set_power_limit(struct amdgpu_device *adev,
- uint32_t limit);
+ uint32_t limit_type, uint32_t limit);
int amdgpu_dpm_is_cclk_dpm_supported(struct amdgpu_device *adev);
int amdgpu_dpm_debugfs_print_current_performance_level(struct amdgpu_device *adev,
struct seq_file *m);
@@ -614,5 +612,6 @@ int amdgpu_dpm_reset_vcn(struct amdgpu_device *adev, uint32_t inst_mask);
bool amdgpu_dpm_reset_vcn_is_supported(struct amdgpu_device *adev);
bool amdgpu_dpm_is_temp_metrics_supported(struct amdgpu_device *adev,
enum smu_temp_metric_type type);
+const struct ras_smu_drv *amdgpu_dpm_get_ras_smu_driver(struct amdgpu_device *adev);
#endif
diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
index 3a9522c17fee..1f539cc65f41 100644
--- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
+++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c
@@ -2558,18 +2558,13 @@ static int si_enable_power_containment(struct amdgpu_device *adev,
if (enable) {
if (!si_should_disable_uvd_powertune(adev, amdgpu_new_state)) {
smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_TDPClampingActive);
- if (smc_result != PPSMC_Result_OK) {
+ if (smc_result != PPSMC_Result_OK)
ret = -EINVAL;
- ni_pi->pc_enabled = false;
- } else {
- ni_pi->pc_enabled = true;
- }
}
} else {
smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_TDPClampingInactive);
if (smc_result != PPSMC_Result_OK)
ret = -EINVAL;
- ni_pi->pc_enabled = false;
}
}
@@ -7051,13 +7046,20 @@ static void si_set_vce_clock(struct amdgpu_device *adev,
if ((old_rps->evclk != new_rps->evclk) ||
(old_rps->ecclk != new_rps->ecclk)) {
/* Turn the clocks on when encoding, off otherwise */
+ dev_dbg(adev->dev, "set VCE clocks: %u, %u\n", new_rps->evclk, new_rps->ecclk);
+
if (new_rps->evclk || new_rps->ecclk) {
- /* Place holder for future VCE1.0 porting to amdgpu
- vce_v1_0_enable_mgcg(adev, false, false);*/
+ amdgpu_asic_set_vce_clocks(adev, new_rps->evclk, new_rps->ecclk);
+ amdgpu_device_ip_set_clockgating_state(
+ adev, AMD_IP_BLOCK_TYPE_VCE, AMD_CG_STATE_UNGATE);
+ amdgpu_device_ip_set_powergating_state(
+ adev, AMD_IP_BLOCK_TYPE_VCE, AMD_PG_STATE_UNGATE);
} else {
- /* Place holder for future VCE1.0 porting to amdgpu
- vce_v1_0_enable_mgcg(adev, true, false);
- amdgpu_asic_set_vce_clocks(adev, new_rps->evclk, new_rps->ecclk);*/
+ amdgpu_device_ip_set_powergating_state(
+ adev, AMD_IP_BLOCK_TYPE_VCE, AMD_PG_STATE_GATE);
+ amdgpu_device_ip_set_clockgating_state(
+ adev, AMD_IP_BLOCK_TYPE_VCE, AMD_CG_STATE_GATE);
+ amdgpu_asic_set_vce_clocks(adev, 0, 0);
}
}
}
@@ -7509,8 +7511,6 @@ static int si_dpm_init(struct amdgpu_device *adev)
pi->pasi = CYPRESS_HASI_DFLT;
pi->vrc = SISLANDS_VRC_DFLT;
- pi->gfx_clock_gating = true;
-
eg_pi->sclk_deep_sleep = true;
si_pi->sclk_deep_sleep_above_low = false;
@@ -7521,7 +7521,6 @@ static int si_dpm_init(struct amdgpu_device *adev)
eg_pi->dynamic_ac_timing = true;
- eg_pi->light_sleep = true;
#if defined(CONFIG_ACPI)
eg_pi->pcie_performance_request =
amdgpu_acpi_is_pcie_performance_request_supported(adev);
@@ -7582,6 +7581,7 @@ static void si_dpm_debugfs_print_current_performance_level(void *handle,
} else {
pl = &ps->performance_levels[current_index];
seq_printf(m, "uvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+ seq_printf(m, "vce evclk: %d ecclk: %d\n", rps->evclk, rps->ecclk);
seq_printf(m, "power level %d sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
}
diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.h b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.h
index 11cb7874a6bb..3aed75fbf913 100644
--- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.h
+++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.h
@@ -38,11 +38,7 @@
#define MC_ARB_DRAM_TIMING2_2 0xa00
#define MC_ARB_DRAM_TIMING2_3 0xa01
-#define MAX_NO_OF_MVDD_VALUES 2
-#define MAX_NO_VREG_STEPS 32
#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
-#define SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE 32
-#define SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
#define RV770_ASI_DFLT 1000
#define CYPRESS_HASI_DFLT 400000
#define PCIE_PERF_REQ_PECI_GEN1 2
@@ -51,11 +47,6 @@
#define RV770_DEFAULT_VCLK_FREQ 53300 /* 10 khz */
#define RV770_DEFAULT_DCLK_FREQ 40000 /* 10 khz */
-#define SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE 16
-
-#define RV770_SMC_TABLE_ADDRESS 0xB000
-#define RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 3
-
#define SMC_STROBE_RATIO 0x0F
#define SMC_STROBE_ENABLE 0x10
@@ -64,27 +55,6 @@
#define SMC_MC_RTT_ENABLE 0x04
#define SMC_MC_STUTTER_EN 0x08
-#define RV770_SMC_VOLTAGEMASK_VDDC 0
-#define RV770_SMC_VOLTAGEMASK_MVDD 1
-#define RV770_SMC_VOLTAGEMASK_VDDCI 2
-#define RV770_SMC_VOLTAGEMASK_MAX 4
-
-#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
-#define NISLANDS_SMC_STROBE_RATIO 0x0F
-#define NISLANDS_SMC_STROBE_ENABLE 0x10
-
-#define NISLANDS_SMC_MC_EDC_RD_FLAG 0x01
-#define NISLANDS_SMC_MC_EDC_WR_FLAG 0x02
-#define NISLANDS_SMC_MC_RTT_ENABLE 0x04
-#define NISLANDS_SMC_MC_STUTTER_EN 0x08
-
-#define MAX_NO_VREG_STEPS 32
-
-#define NISLANDS_SMC_VOLTAGEMASK_VDDC 0
-#define NISLANDS_SMC_VOLTAGEMASK_MVDD 1
-#define NISLANDS_SMC_VOLTAGEMASK_VDDCI 2
-#define NISLANDS_SMC_VOLTAGEMASK_MAX 4
-
#define SISLANDS_MCREGISTERTABLE_INITIAL_SLOT 0
#define SISLANDS_MCREGISTERTABLE_ACPI_SLOT 1
#define SISLANDS_MCREGISTERTABLE_ULV_SLOT 2
@@ -219,32 +189,6 @@ enum si_cac_config_reg_type
SISLANDS_CACCONFIG_MAX
};
-enum si_power_level {
- SI_POWER_LEVEL_LOW = 0,
- SI_POWER_LEVEL_MEDIUM = 1,
- SI_POWER_LEVEL_HIGH = 2,
- SI_POWER_LEVEL_CTXSW = 3,
-};
-
-enum si_td {
- SI_TD_AUTO,
- SI_TD_UP,
- SI_TD_DOWN,
-};
-
-enum si_display_watermark {
- SI_DISPLAY_WATERMARK_LOW = 0,
- SI_DISPLAY_WATERMARK_HIGH = 1,
-};
-
-enum si_display_gap
-{
- SI_PM_DISPLAY_GAP_VBLANK_OR_WM = 0,
- SI_PM_DISPLAY_GAP_VBLANK = 1,
- SI_PM_DISPLAY_GAP_WATERMARK = 2,
- SI_PM_DISPLAY_GAP_IGNORE = 3,
-};
-
extern const struct amdgpu_ip_block_version si_smu_ip_block;
struct ni_leakage_coeffients
@@ -258,56 +202,6 @@ struct ni_leakage_coeffients
u32 t_ref;
};
-struct SMC_Evergreen_MCRegisterAddress
-{
- uint16_t s0;
- uint16_t s1;
-};
-
-typedef struct SMC_Evergreen_MCRegisterAddress SMC_Evergreen_MCRegisterAddress;
-
-struct evergreen_mc_reg_entry {
- u32 mclk_max;
- u32 mc_data[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
-};
-
-struct evergreen_mc_reg_table {
- u8 last;
- u8 num_entries;
- u16 valid_flag;
- struct evergreen_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
- SMC_Evergreen_MCRegisterAddress mc_reg_address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
-};
-
-struct SMC_Evergreen_MCRegisterSet
-{
- uint32_t value[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
-};
-
-typedef struct SMC_Evergreen_MCRegisterSet SMC_Evergreen_MCRegisterSet;
-
-struct SMC_Evergreen_MCRegisters
-{
- uint8_t last;
- uint8_t reserved[3];
- SMC_Evergreen_MCRegisterAddress address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE];
- SMC_Evergreen_MCRegisterSet data[5];
-};
-
-typedef struct SMC_Evergreen_MCRegisters SMC_Evergreen_MCRegisters;
-
-struct SMC_NIslands_MCRegisterSet
-{
- uint32_t value[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
-};
-
-typedef struct SMC_NIslands_MCRegisterSet SMC_NIslands_MCRegisterSet;
-
-struct ni_mc_reg_entry {
- u32 mclk_max;
- u32 mc_data[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
-};
-
struct SMC_NIslands_MCRegisterAddress
{
uint16_t s0;
@@ -316,257 +210,20 @@ struct SMC_NIslands_MCRegisterAddress
typedef struct SMC_NIslands_MCRegisterAddress SMC_NIslands_MCRegisterAddress;
-struct SMC_NIslands_MCRegisters
-{
- uint8_t last;
- uint8_t reserved[3];
- SMC_NIslands_MCRegisterAddress address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
- SMC_NIslands_MCRegisterSet data[SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
-};
-
-typedef struct SMC_NIslands_MCRegisters SMC_NIslands_MCRegisters;
-
-struct evergreen_ulv_param {
- bool supported;
- struct rv7xx_pl *pl;
-};
-
-struct evergreen_arb_registers {
- u32 mc_arb_dram_timing;
- u32 mc_arb_dram_timing2;
- u32 mc_arb_rfsh_rate;
- u32 mc_arb_burst_time;
-};
-
-struct at {
- u32 rlp;
- u32 rmp;
- u32 lhp;
- u32 lmp;
-};
-
-struct ni_clock_registers {
- u32 cg_spll_func_cntl;
- u32 cg_spll_func_cntl_2;
- u32 cg_spll_func_cntl_3;
- u32 cg_spll_func_cntl_4;
- u32 cg_spll_spread_spectrum;
- u32 cg_spll_spread_spectrum_2;
- u32 mclk_pwrmgt_cntl;
- u32 dll_cntl;
- u32 mpll_ad_func_cntl;
- u32 mpll_ad_func_cntl_2;
- u32 mpll_dq_func_cntl;
- u32 mpll_dq_func_cntl_2;
- u32 mpll_ss1;
- u32 mpll_ss2;
-};
-
-struct RV770_SMC_SCLK_VALUE
-{
- uint32_t vCG_SPLL_FUNC_CNTL;
- uint32_t vCG_SPLL_FUNC_CNTL_2;
- uint32_t vCG_SPLL_FUNC_CNTL_3;
- uint32_t vCG_SPLL_SPREAD_SPECTRUM;
- uint32_t vCG_SPLL_SPREAD_SPECTRUM_2;
- uint32_t sclk_value;
-};
-
-typedef struct RV770_SMC_SCLK_VALUE RV770_SMC_SCLK_VALUE;
-
-struct RV770_SMC_MCLK_VALUE
-{
- uint32_t vMPLL_AD_FUNC_CNTL;
- uint32_t vMPLL_AD_FUNC_CNTL_2;
- uint32_t vMPLL_DQ_FUNC_CNTL;
- uint32_t vMPLL_DQ_FUNC_CNTL_2;
- uint32_t vMCLK_PWRMGT_CNTL;
- uint32_t vDLL_CNTL;
- uint32_t vMPLL_SS;
- uint32_t vMPLL_SS2;
- uint32_t mclk_value;
-};
-
-typedef struct RV770_SMC_MCLK_VALUE RV770_SMC_MCLK_VALUE;
-
-
-struct RV730_SMC_MCLK_VALUE
-{
- uint32_t vMCLK_PWRMGT_CNTL;
- uint32_t vDLL_CNTL;
- uint32_t vMPLL_FUNC_CNTL;
- uint32_t vMPLL_FUNC_CNTL2;
- uint32_t vMPLL_FUNC_CNTL3;
- uint32_t vMPLL_SS;
- uint32_t vMPLL_SS2;
- uint32_t mclk_value;
-};
-
-typedef struct RV730_SMC_MCLK_VALUE RV730_SMC_MCLK_VALUE;
-
-struct RV770_SMC_VOLTAGE_VALUE
-{
- uint16_t value;
- uint8_t index;
- uint8_t padding;
-};
-
-typedef struct RV770_SMC_VOLTAGE_VALUE RV770_SMC_VOLTAGE_VALUE;
-
-union RV7XX_SMC_MCLK_VALUE
-{
- RV770_SMC_MCLK_VALUE mclk770;
- RV730_SMC_MCLK_VALUE mclk730;
-};
-
-typedef union RV7XX_SMC_MCLK_VALUE RV7XX_SMC_MCLK_VALUE, *LPRV7XX_SMC_MCLK_VALUE;
-
-struct RV770_SMC_HW_PERFORMANCE_LEVEL
-{
- uint8_t arbValue;
- union{
- uint8_t seqValue;
- uint8_t ACIndex;
- };
- uint8_t displayWatermark;
- uint8_t gen2PCIE;
- uint8_t gen2XSP;
- uint8_t backbias;
- uint8_t strobeMode;
- uint8_t mcFlags;
- uint32_t aT;
- uint32_t bSP;
- RV770_SMC_SCLK_VALUE sclk;
- RV7XX_SMC_MCLK_VALUE mclk;
- RV770_SMC_VOLTAGE_VALUE vddc;
- RV770_SMC_VOLTAGE_VALUE mvdd;
- RV770_SMC_VOLTAGE_VALUE vddci;
- uint8_t reserved1;
- uint8_t reserved2;
- uint8_t stateFlags;
- uint8_t padding;
-};
-
-typedef struct RV770_SMC_HW_PERFORMANCE_LEVEL RV770_SMC_HW_PERFORMANCE_LEVEL;
-
-struct RV770_SMC_SWSTATE
-{
- uint8_t flags;
- uint8_t padding1;
- uint8_t padding2;
- uint8_t padding3;
- RV770_SMC_HW_PERFORMANCE_LEVEL levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
-};
-
-typedef struct RV770_SMC_SWSTATE RV770_SMC_SWSTATE;
-
-struct RV770_SMC_VOLTAGEMASKTABLE
-{
- uint8_t highMask[RV770_SMC_VOLTAGEMASK_MAX];
- uint32_t lowMask[RV770_SMC_VOLTAGEMASK_MAX];
-};
-
-typedef struct RV770_SMC_VOLTAGEMASKTABLE RV770_SMC_VOLTAGEMASKTABLE;
-
-struct RV770_SMC_STATETABLE
-{
- uint8_t thermalProtectType;
- uint8_t systemFlags;
- uint8_t maxVDDCIndexInPPTable;
- uint8_t extraFlags;
- uint8_t highSMIO[MAX_NO_VREG_STEPS];
- uint32_t lowSMIO[MAX_NO_VREG_STEPS];
- RV770_SMC_VOLTAGEMASKTABLE voltageMaskTable;
- RV770_SMC_SWSTATE initialState;
- RV770_SMC_SWSTATE ACPIState;
- RV770_SMC_SWSTATE driverState;
- RV770_SMC_SWSTATE ULVState;
-};
-
-typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE;
-
-struct vddc_table_entry {
- u16 vddc;
- u8 vddc_index;
- u8 high_smio;
- u32 low_smio;
-};
-
-struct rv770_clock_registers {
- u32 cg_spll_func_cntl;
- u32 cg_spll_func_cntl_2;
- u32 cg_spll_func_cntl_3;
- u32 cg_spll_spread_spectrum;
- u32 cg_spll_spread_spectrum_2;
- u32 mpll_ad_func_cntl;
- u32 mpll_ad_func_cntl_2;
- u32 mpll_dq_func_cntl;
- u32 mpll_dq_func_cntl_2;
- u32 mclk_pwrmgt_cntl;
- u32 dll_cntl;
- u32 mpll_ss1;
- u32 mpll_ss2;
-};
-
-struct rv730_clock_registers {
- u32 cg_spll_func_cntl;
- u32 cg_spll_func_cntl_2;
- u32 cg_spll_func_cntl_3;
- u32 cg_spll_spread_spectrum;
- u32 cg_spll_spread_spectrum_2;
- u32 mclk_pwrmgt_cntl;
- u32 dll_cntl;
- u32 mpll_func_cntl;
- u32 mpll_func_cntl2;
- u32 mpll_func_cntl3;
- u32 mpll_ss;
- u32 mpll_ss2;
-};
-
-union r7xx_clock_registers {
- struct rv770_clock_registers rv770;
- struct rv730_clock_registers rv730;
-};
-
struct rv7xx_power_info {
/* flags */
- bool mem_gddr5;
- bool pcie_gen2;
- bool dynamic_pcie_gen2;
- bool acpi_pcie_gen2;
- bool boot_in_gen2;
bool voltage_control; /* vddc */
bool mvdd_control;
bool sclk_ss;
bool mclk_ss;
bool dynamic_ss;
- bool gfx_clock_gating;
- bool mg_clock_gating;
- bool mgcgtssm;
- bool power_gating;
bool thermal_protection;
- bool display_gap;
- bool dcodt;
- bool ulps;
- /* registers */
- union r7xx_clock_registers clk_regs;
- u32 s0_vid_lower_smio_cntl;
/* voltage */
- u32 vddc_mask_low;
- u32 mvdd_mask_low;
u32 mvdd_split_frequency;
- u32 mvdd_low_smio[MAX_NO_OF_MVDD_VALUES];
u16 max_vddc;
u16 max_vddc_in_table;
u16 min_vddc_in_table;
- struct vddc_table_entry vddc_table[MAX_NO_VREG_STEPS];
- u8 valid_vddc_entries;
- /* dc odt */
- u32 mclk_odt_threshold;
- u8 odt_value_0[2];
- u8 odt_value_1[2];
/* stored values */
- u32 boot_sclk;
u16 acpi_vddc;
u32 ref_div;
u32 active_auto_throttle_sources;
@@ -582,17 +239,6 @@ struct rv7xx_power_info {
u32 asi;
u32 pasi;
u32 vrc;
- u32 restricted_levels;
- u32 rlp;
- u32 rmp;
- u32 lhp;
- u32 lmp;
- /* smc offsets */
- u16 state_table_start;
- u16 soft_regs_start;
- u16 sram_end;
- /* scratch structs */
- RV770_SMC_STATETABLE smc_statetable;
};
enum si_pcie_gen {
@@ -611,44 +257,12 @@ struct rv7xx_pl {
enum si_pcie_gen pcie_gen; /* si+ only */
};
-struct rv7xx_ps {
- struct rv7xx_pl high;
- struct rv7xx_pl medium;
- struct rv7xx_pl low;
- bool dc_compatible;
-};
-
struct si_ps {
u16 performance_level_count;
bool dc_compatible;
struct rv7xx_pl performance_levels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
};
-struct ni_mc_reg_table {
- u8 last;
- u8 num_entries;
- u16 valid_flag;
- struct ni_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
- SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
-};
-
-struct ni_cac_data
-{
- struct ni_leakage_coeffients leakage_coefficients;
- u32 i_leakage;
- s32 leakage_minimum_temperature;
- u32 pwr_const;
- u32 dc_cac_value;
- u32 bif_cac_value;
- u32 lkge_pwr;
- u8 mc_wr_weight;
- u8 mc_rd_weight;
- u8 allow_ovrflw;
- u8 num_win_tdp;
- u8 l2num_win_tdp;
- u8 lts_truncate_n;
-};
-
struct evergreen_power_info {
/* must be first! */
struct rv7xx_power_info rv7xx;
@@ -657,203 +271,33 @@ struct evergreen_power_info {
bool dynamic_ac_timing;
bool abm;
bool mcls;
- bool light_sleep;
- bool memory_transition;
bool pcie_performance_request;
- bool pcie_performance_request_registered;
bool sclk_deep_sleep;
- bool dll_default_on;
- bool ls_clock_gating;
bool smu_uvd_hs;
bool uvd_enabled;
/* stored values */
u16 acpi_vddci;
- u8 mvdd_high_index;
- u8 mvdd_low_index;
u32 mclk_edc_wr_enable_threshold;
- struct evergreen_mc_reg_table mc_reg_table;
struct atom_voltage_table vddc_voltage_table;
struct atom_voltage_table vddci_voltage_table;
- struct evergreen_arb_registers bootup_arb_registers;
- struct evergreen_ulv_param ulv;
- struct at ats[2];
- /* smc offsets */
- u16 mc_reg_table_start;
struct amdgpu_ps current_rps;
- struct rv7xx_ps current_ps;
struct amdgpu_ps requested_rps;
- struct rv7xx_ps requested_ps;
-};
-
-struct PP_NIslands_Dpm2PerfLevel
-{
- uint8_t MaxPS;
- uint8_t TgtAct;
- uint8_t MaxPS_StepInc;
- uint8_t MaxPS_StepDec;
- uint8_t PSST;
- uint8_t NearTDPDec;
- uint8_t AboveSafeInc;
- uint8_t BelowSafeInc;
- uint8_t PSDeltaLimit;
- uint8_t PSDeltaWin;
- uint8_t Reserved[6];
-};
-
-typedef struct PP_NIslands_Dpm2PerfLevel PP_NIslands_Dpm2PerfLevel;
-
-struct PP_NIslands_DPM2Parameters
-{
- uint32_t TDPLimit;
- uint32_t NearTDPLimit;
- uint32_t SafePowerLimit;
- uint32_t PowerBoostLimit;
-};
-typedef struct PP_NIslands_DPM2Parameters PP_NIslands_DPM2Parameters;
-
-struct NISLANDS_SMC_SCLK_VALUE
-{
- uint32_t vCG_SPLL_FUNC_CNTL;
- uint32_t vCG_SPLL_FUNC_CNTL_2;
- uint32_t vCG_SPLL_FUNC_CNTL_3;
- uint32_t vCG_SPLL_FUNC_CNTL_4;
- uint32_t vCG_SPLL_SPREAD_SPECTRUM;
- uint32_t vCG_SPLL_SPREAD_SPECTRUM_2;
- uint32_t sclk_value;
-};
-
-typedef struct NISLANDS_SMC_SCLK_VALUE NISLANDS_SMC_SCLK_VALUE;
-
-struct NISLANDS_SMC_MCLK_VALUE
-{
- uint32_t vMPLL_FUNC_CNTL;
- uint32_t vMPLL_FUNC_CNTL_1;
- uint32_t vMPLL_FUNC_CNTL_2;
- uint32_t vMPLL_AD_FUNC_CNTL;
- uint32_t vMPLL_AD_FUNC_CNTL_2;
- uint32_t vMPLL_DQ_FUNC_CNTL;
- uint32_t vMPLL_DQ_FUNC_CNTL_2;
- uint32_t vMCLK_PWRMGT_CNTL;
- uint32_t vDLL_CNTL;
- uint32_t vMPLL_SS;
- uint32_t vMPLL_SS2;
- uint32_t mclk_value;
-};
-
-typedef struct NISLANDS_SMC_MCLK_VALUE NISLANDS_SMC_MCLK_VALUE;
-
-struct NISLANDS_SMC_VOLTAGE_VALUE
-{
- uint16_t value;
- uint8_t index;
- uint8_t padding;
-};
-
-typedef struct NISLANDS_SMC_VOLTAGE_VALUE NISLANDS_SMC_VOLTAGE_VALUE;
-
-struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL
-{
- uint8_t arbValue;
- uint8_t ACIndex;
- uint8_t displayWatermark;
- uint8_t gen2PCIE;
- uint8_t reserved1;
- uint8_t reserved2;
- uint8_t strobeMode;
- uint8_t mcFlags;
- uint32_t aT;
- uint32_t bSP;
- NISLANDS_SMC_SCLK_VALUE sclk;
- NISLANDS_SMC_MCLK_VALUE mclk;
- NISLANDS_SMC_VOLTAGE_VALUE vddc;
- NISLANDS_SMC_VOLTAGE_VALUE mvdd;
- NISLANDS_SMC_VOLTAGE_VALUE vddci;
- NISLANDS_SMC_VOLTAGE_VALUE std_vddc;
- uint32_t powergate_en;
- uint8_t hUp;
- uint8_t hDown;
- uint8_t stateFlags;
- uint8_t arbRefreshState;
- uint32_t SQPowerThrottle;
- uint32_t SQPowerThrottle_2;
- uint32_t reserved[2];
- PP_NIslands_Dpm2PerfLevel dpm2;
-};
-
-typedef struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL NISLANDS_SMC_HW_PERFORMANCE_LEVEL;
-
-struct NISLANDS_SMC_SWSTATE
-{
- uint8_t flags;
- uint8_t levelCount;
- uint8_t padding2;
- uint8_t padding3;
- NISLANDS_SMC_HW_PERFORMANCE_LEVEL levels[];
-};
-
-typedef struct NISLANDS_SMC_SWSTATE NISLANDS_SMC_SWSTATE;
-
-struct NISLANDS_SMC_VOLTAGEMASKTABLE
-{
- uint8_t highMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
- uint32_t lowMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
-};
-
-typedef struct NISLANDS_SMC_VOLTAGEMASKTABLE NISLANDS_SMC_VOLTAGEMASKTABLE;
-
-#define NISLANDS_MAX_NO_VREG_STEPS 32
-
-struct NISLANDS_SMC_STATETABLE
-{
- uint8_t thermalProtectType;
- uint8_t systemFlags;
- uint8_t maxVDDCIndexInPPTable;
- uint8_t extraFlags;
- uint8_t highSMIO[NISLANDS_MAX_NO_VREG_STEPS];
- uint32_t lowSMIO[NISLANDS_MAX_NO_VREG_STEPS];
- NISLANDS_SMC_VOLTAGEMASKTABLE voltageMaskTable;
- PP_NIslands_DPM2Parameters dpm2Params;
- NISLANDS_SMC_SWSTATE initialState;
- NISLANDS_SMC_SWSTATE ACPIState;
- NISLANDS_SMC_SWSTATE ULVState;
- NISLANDS_SMC_SWSTATE driverState;
- NISLANDS_SMC_HW_PERFORMANCE_LEVEL dpmLevels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
};
-typedef struct NISLANDS_SMC_STATETABLE NISLANDS_SMC_STATETABLE;
-
struct ni_power_info {
/* must be first! */
struct evergreen_power_info eg;
- struct ni_clock_registers clock_registers;
- struct ni_mc_reg_table mc_reg_table;
u32 mclk_rtt_mode_threshold;
/* flags */
- bool use_power_boost_limit;
bool support_cac_long_term_average;
bool cac_enabled;
bool cac_configuration_required;
bool driver_calculate_cac_leakage;
- bool pc_enabled;
bool enable_power_containment;
bool enable_cac;
bool enable_sq_ramping;
- /* smc offsets */
- u16 arb_table_start;
- u16 fan_table_start;
- u16 cac_table_start;
- u16 spll_table_start;
- /* CAC stuff */
- struct ni_cac_data cac_data;
- u32 dc_cac_table[NISLANDS_DCCAC_MAX_LEVELS];
- const struct ni_cac_weights *cac_weights;
- u8 lta_window_size;
- u8 lts_truncate;
struct si_ps current_ps;
struct si_ps requested_ps;
- /* scratch structs */
- SMC_NIslands_MCRegisters smc_mc_reg_table;
- NISLANDS_SMC_STATETABLE smc_statetable;
};
struct si_cac_config_reg
@@ -952,7 +396,6 @@ struct si_leakage_voltage
struct si_leakage_voltage_entry entries[SISLANDS_MAX_LEAKAGE_COUNT];
};
-
struct si_ulv_param {
bool supported;
u32 cg_ulv_control;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
index 554492dfa3c0..3aaf3dd71868 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
@@ -20,7 +20,6 @@
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
-#include "pp_debug.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/gfp.h>
@@ -28,12 +27,10 @@
#include <linux/firmware.h>
#include <linux/reboot.h>
#include "amd_shared.h"
-#include "amd_powerplay.h"
#include "power_state.h"
#include "amdgpu.h"
#include "hwmgr.h"
#include "amdgpu_dpm_internal.h"
-#include "amdgpu_display.h"
static const struct amd_pm_funcs pp_dpm_funcs;
@@ -634,9 +631,12 @@ static int pp_dpm_get_pp_table(void *handle, char **table)
{
struct pp_hwmgr *hwmgr = handle;
- if (!hwmgr || !hwmgr->pm_en || !hwmgr->soft_pp_table)
+ if (!hwmgr || !hwmgr->pm_en || !table)
return -EINVAL;
+ if (!hwmgr->soft_pp_table)
+ return -EOPNOTSUPP;
+
*table = (char *)hwmgr->soft_pp_table;
return hwmgr->soft_pp_table_size;
}
@@ -955,7 +955,7 @@ static int pp_dpm_switch_power_profile(void *handle,
return 0;
}
-static int pp_set_power_limit(void *handle, uint32_t limit)
+static int pp_set_power_limit(void *handle, uint32_t limit_type, uint32_t limit)
{
struct pp_hwmgr *hwmgr = handle;
uint32_t max_power_limit;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c
index d2dbd90bb427..0a876c840c79 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/fiji_smumgr.c
@@ -2024,7 +2024,7 @@ static int fiji_init_smc_table(struct pp_hwmgr *hwmgr)
table->VoltageResponseTime = 0;
table->PhaseResponseTime = 0;
table->MemoryThermThrottleEnable = 1;
- table->PCIeBootLinkLevel = 0; /* 0:Gen1 1:Gen2 2:Gen3*/
+ table->PCIeBootLinkLevel = (uint8_t) (data->dpm_table.pcie_speed_table.count);
table->PCIeGenInterval = 1;
table->VRConfig = 0;
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c
index 1f50f1e74c48..aa3ae9b115c4 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/iceland_smumgr.c
@@ -2028,7 +2028,7 @@ static int iceland_init_smc_table(struct pp_hwmgr *hwmgr)
table->VoltageResponseTime = 0;
table->PhaseResponseTime = 0;
table->MemoryThermThrottleEnable = 1;
- table->PCIeBootLinkLevel = 0;
+ table->PCIeBootLinkLevel = (uint8_t) (data->dpm_table.pcie_speed_table.count);
table->PCIeGenInterval = 1;
result = iceland_populate_smc_svi2_config(hwmgr, table);
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c
index ac9ec8257f82..38e19e5cad4d 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/smu10_smumgr.c
@@ -139,7 +139,7 @@ static int smu10_copy_table_from_smc(struct pp_hwmgr *hwmgr,
priv->smu_tables.entry[table_id].table_id,
NULL);
- amdgpu_asic_invalidate_hdp(adev, NULL);
+ amdgpu_hdp_invalidate(adev, NULL);
memcpy(table, (uint8_t *)priv->smu_tables.entry[table_id].table,
priv->smu_tables.entry[table_id].size);
@@ -164,7 +164,7 @@ static int smu10_copy_table_to_smc(struct pp_hwmgr *hwmgr,
memcpy(priv->smu_tables.entry[table_id].table, table,
priv->smu_tables.entry[table_id].size);
- amdgpu_asic_flush_hdp(adev, NULL);
+ amdgpu_hdp_flush(adev, NULL);
smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetDriverDramAddrHigh,
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c
index f9c0f117725d..0bf1bf5528c2 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega10_smumgr.c
@@ -60,7 +60,7 @@ static int vega10_copy_table_from_smc(struct pp_hwmgr *hwmgr,
priv->smu_tables.entry[table_id].table_id,
NULL);
- amdgpu_asic_invalidate_hdp(adev, NULL);
+ amdgpu_hdp_invalidate(adev, NULL);
memcpy(table, priv->smu_tables.entry[table_id].table,
priv->smu_tables.entry[table_id].size);
@@ -90,7 +90,7 @@ static int vega10_copy_table_to_smc(struct pp_hwmgr *hwmgr,
memcpy(priv->smu_tables.entry[table_id].table, table,
priv->smu_tables.entry[table_id].size);
- amdgpu_asic_flush_hdp(adev, NULL);
+ amdgpu_hdp_flush(adev, NULL);
smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetDriverDramAddrHigh,
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c
index d3ff6a831ed5..e2ba593faa5d 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega12_smumgr.c
@@ -68,7 +68,7 @@ static int vega12_copy_table_from_smc(struct pp_hwmgr *hwmgr,
"[CopyTableFromSMC] Attempt to Transfer Table From SMU Failed!",
return -EINVAL);
- amdgpu_asic_invalidate_hdp(adev, NULL);
+ amdgpu_hdp_invalidate(adev, NULL);
memcpy(table, priv->smu_tables.entry[table_id].table,
priv->smu_tables.entry[table_id].size);
@@ -98,7 +98,7 @@ static int vega12_copy_table_to_smc(struct pp_hwmgr *hwmgr,
memcpy(priv->smu_tables.entry[table_id].table, table,
priv->smu_tables.entry[table_id].size);
- amdgpu_asic_flush_hdp(adev, NULL);
+ amdgpu_hdp_flush(adev, NULL);
PP_ASSERT_WITH_CODE(smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetDriverDramAddrHigh,
diff --git a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c
index a5c95b180672..e3515156d26f 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/smumgr/vega20_smumgr.c
@@ -192,7 +192,7 @@ static int vega20_copy_table_from_smc(struct pp_hwmgr *hwmgr,
"[CopyTableFromSMC] Attempt to Transfer Table From SMU Failed!",
return ret);
- amdgpu_asic_invalidate_hdp(adev, NULL);
+ amdgpu_hdp_invalidate(adev, NULL);
memcpy(table, priv->smu_tables.entry[table_id].table,
priv->smu_tables.entry[table_id].size);
@@ -223,7 +223,7 @@ static int vega20_copy_table_to_smc(struct pp_hwmgr *hwmgr,
memcpy(priv->smu_tables.entry[table_id].table, table,
priv->smu_tables.entry[table_id].size);
- amdgpu_asic_flush_hdp(adev, NULL);
+ amdgpu_hdp_flush(adev, NULL);
PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetDriverDramAddrHigh,
@@ -256,7 +256,7 @@ int vega20_set_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
memcpy(priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].table, table,
priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].size);
- amdgpu_asic_flush_hdp(adev, NULL);
+ amdgpu_hdp_flush(adev, NULL);
PP_ASSERT_WITH_CODE((ret = smum_send_msg_to_smc_with_parameter(hwmgr,
PPSMC_MSG_SetDriverDramAddrHigh,
@@ -306,7 +306,7 @@ int vega20_get_activity_monitor_coeff(struct pp_hwmgr *hwmgr,
"[GetActivityMonitor] Attempt to Transfer Table From SMU Failed!",
return ret);
- amdgpu_asic_invalidate_hdp(adev, NULL);
+ amdgpu_hdp_invalidate(adev, NULL);
memcpy(table, priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].table,
priv->smu_tables.entry[TABLE_ACTIVITY_MONITOR_COEFF].size);
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index fb8086859857..f51fa265230b 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -68,7 +68,7 @@ static int smu_handle_task(struct smu_context *smu,
static int smu_reset(struct smu_context *smu);
static int smu_set_fan_speed_pwm(void *handle, u32 speed);
static int smu_set_fan_control_mode(void *handle, u32 value);
-static int smu_set_power_limit(void *handle, uint32_t limit);
+static int smu_set_power_limit(void *handle, uint32_t limit_type, uint32_t limit);
static int smu_set_fan_speed_rpm(void *handle, uint32_t speed);
static int smu_set_gfx_cgpg(struct smu_context *smu, bool enabled);
static int smu_set_mp1_state(void *handle, enum pp_mp1_state mp1_state);
@@ -508,11 +508,14 @@ static void smu_restore_dpm_user_profile(struct smu_context *smu)
/* Enable restore flag */
smu->user_dpm_profile.flags |= SMU_DPM_USER_PROFILE_RESTORE;
- /* set the user dpm power limit */
- if (smu->user_dpm_profile.power_limit) {
- ret = smu_set_power_limit(smu, smu->user_dpm_profile.power_limit);
+ /* set the user dpm power limits */
+ for (int i = SMU_DEFAULT_PPT_LIMIT; i < SMU_LIMIT_TYPE_COUNT; i++) {
+ if (!smu->user_dpm_profile.power_limits[i])
+ continue;
+ ret = smu_set_power_limit(smu, i,
+ smu->user_dpm_profile.power_limits[i]);
if (ret)
- dev_err(smu->adev->dev, "Failed to set power limit value\n");
+ dev_err(smu->adev->dev, "Failed to set %d power limit value\n", i);
}
/* set the user dpm clock configurations */
@@ -609,6 +612,17 @@ bool is_support_cclk_dpm(struct amdgpu_device *adev)
return true;
}
+int amdgpu_smu_ras_send_msg(struct amdgpu_device *adev, enum smu_message_type msg,
+ uint32_t param, uint32_t *read_arg)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ int ret = -EOPNOTSUPP;
+
+ if (smu->ppt_funcs && smu->ppt_funcs->ras_send_msg)
+ ret = smu->ppt_funcs->ras_send_msg(smu, msg, param, read_arg);
+
+ return ret;
+}
static int smu_sys_get_pp_table(void *handle,
char **table)
@@ -620,7 +634,7 @@ static int smu_sys_get_pp_table(void *handle,
return -EOPNOTSUPP;
if (!smu_table->power_play_table && !smu_table->hardcode_pptable)
- return -EINVAL;
+ return -EOPNOTSUPP;
if (smu_table->hardcode_pptable)
*table = smu_table->hardcode_pptable;
@@ -1655,9 +1669,12 @@ static int smu_smc_hw_setup(struct smu_context *smu)
if (adev->in_suspend && smu_is_dpm_running(smu)) {
dev_info(adev->dev, "dpm has been enabled\n");
ret = smu_system_features_control(smu, true);
- if (ret)
+ if (ret) {
dev_err(adev->dev, "Failed system features control!\n");
- return ret;
+ return ret;
+ }
+
+ return smu_enable_thermal_alert(smu);
}
break;
default:
@@ -2040,6 +2057,12 @@ static int smu_disable_dpms(struct smu_context *smu)
smu->is_apu && (amdgpu_in_reset(adev) || adev->in_s0ix))
return 0;
+ /* vangogh s0ix */
+ if ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(11, 5, 0) ||
+ amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(11, 5, 2)) &&
+ adev->in_s0ix)
+ return 0;
+
/*
* For gpu reset, runpm and hibernation through BACO,
* BACO feature has to be kept enabled.
@@ -2225,7 +2248,6 @@ static int smu_resume(struct amdgpu_ip_block *ip_block)
int ret;
struct amdgpu_device *adev = ip_block->adev;
struct smu_context *smu = adev->powerplay.pp_handle;
- struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
if (amdgpu_sriov_multi_vf_mode(adev))
return 0;
@@ -2257,18 +2279,6 @@ static int smu_resume(struct amdgpu_ip_block *ip_block)
adev->pm.dpm_enabled = true;
- if (smu->current_power_limit) {
- ret = smu_set_power_limit(smu, smu->current_power_limit);
- if (ret && ret != -EOPNOTSUPP)
- return ret;
- }
-
- if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL && smu->od_enabled) {
- ret = smu_od_edit_dpm_table(smu, PP_OD_COMMIT_DPM_TABLE, NULL, 0);
- if (ret)
- return ret;
- }
-
dev_info(adev->dev, "SMU is resumed successfully!\n");
return 0;
@@ -2796,6 +2806,17 @@ const struct amdgpu_ip_block_version smu_v14_0_ip_block = {
.funcs = &smu_ip_funcs,
};
+const struct ras_smu_drv *smu_get_ras_smu_driver(void *handle)
+{
+ struct smu_context *smu = (struct smu_context *)handle;
+ const struct ras_smu_drv *tmp = NULL;
+ int ret;
+
+ ret = smu_get_ras_smu_drv(smu, &tmp);
+
+ return ret ? NULL : tmp;
+}
+
static int smu_load_microcode(void *handle)
{
struct smu_context *smu = handle;
@@ -2889,6 +2910,9 @@ int smu_get_power_limit(void *handle,
if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
return -EOPNOTSUPP;
+ if (!limit)
+ return -EINVAL;
+
switch (pp_power_type) {
case PP_PWR_TYPE_SUSTAINED:
limit_type = SMU_DEFAULT_PPT_LIMIT;
@@ -2920,6 +2944,8 @@ int smu_get_power_limit(void *handle,
if (limit_type != SMU_DEFAULT_PPT_LIMIT) {
if (smu->ppt_funcs->get_ppt_limit)
ret = smu->ppt_funcs->get_ppt_limit(smu, limit, limit_type, limit_level);
+ else
+ return -EOPNOTSUPP;
} else {
switch (limit_level) {
case SMU_PPT_LIMIT_CURRENT:
@@ -2958,37 +2984,34 @@ int smu_get_power_limit(void *handle,
return ret;
}
-static int smu_set_power_limit(void *handle, uint32_t limit)
+static int smu_set_power_limit(void *handle, uint32_t limit_type, uint32_t limit)
{
struct smu_context *smu = handle;
- uint32_t limit_type = limit >> 24;
int ret = 0;
if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
return -EOPNOTSUPP;
- limit &= (1<<24)-1;
- if (limit_type != SMU_DEFAULT_PPT_LIMIT)
- if (smu->ppt_funcs->set_power_limit)
- return smu->ppt_funcs->set_power_limit(smu, limit_type, limit);
-
- if ((limit > smu->max_power_limit) || (limit < smu->min_power_limit)) {
- dev_err(smu->adev->dev,
- "New power limit (%d) is out of range [%d,%d]\n",
- limit, smu->min_power_limit, smu->max_power_limit);
- return -EINVAL;
+ if (limit_type == SMU_DEFAULT_PPT_LIMIT) {
+ if (!limit)
+ limit = smu->current_power_limit;
+ if ((limit > smu->max_power_limit) || (limit < smu->min_power_limit)) {
+ dev_err(smu->adev->dev,
+ "New power limit (%d) is out of range [%d,%d]\n",
+ limit, smu->min_power_limit, smu->max_power_limit);
+ return -EINVAL;
+ }
}
- if (!limit)
- limit = smu->current_power_limit;
-
if (smu->ppt_funcs->set_power_limit) {
ret = smu->ppt_funcs->set_power_limit(smu, limit_type, limit);
- if (!ret && !(smu->user_dpm_profile.flags & SMU_DPM_USER_PROFILE_RESTORE))
- smu->user_dpm_profile.power_limit = limit;
+ if (ret)
+ return ret;
+ if (!(smu->user_dpm_profile.flags & SMU_DPM_USER_PROFILE_RESTORE))
+ smu->user_dpm_profile.power_limits[limit_type] = limit;
}
- return ret;
+ return 0;
}
static int smu_print_smuclk_levels(struct smu_context *smu, enum smu_clk_type clk_type, char *buf)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index 582c186d8b62..8815fc70b63b 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -212,6 +212,7 @@ enum smu_power_src_type {
enum smu_ppt_limit_type {
SMU_DEFAULT_PPT_LIMIT = 0,
SMU_FAST_PPT_LIMIT,
+ SMU_LIMIT_TYPE_COUNT,
};
enum smu_ppt_limit_level {
@@ -231,7 +232,7 @@ enum smu_memory_pool_size {
struct smu_user_dpm_profile {
uint32_t fan_mode;
- uint32_t power_limit;
+ uint32_t power_limits[SMU_LIMIT_TYPE_COUNT];
uint32_t fan_speed_pwm;
uint32_t fan_speed_rpm;
uint32_t flags;
@@ -1521,6 +1522,21 @@ struct pptable_funcs {
*/
ssize_t (*get_xcp_metrics)(struct smu_context *smu, int xcp_id,
void *table);
+ /**
+ * @ras_send_msg: Send a message with a parameter from Ras
+ * &msg: Type of message.
+ * &param: Message parameter.
+ * &read_arg: SMU response (optional).
+ */
+ int (*ras_send_msg)(struct smu_context *smu,
+ enum smu_message_type msg, uint32_t param, uint32_t *read_arg);
+
+
+ /**
+ * @get_ras_smu_drv: Get RAS smu driver interface
+ * Return: ras_smu_drv *
+ */
+ int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
};
typedef enum {
@@ -1785,7 +1801,10 @@ int smu_set_pm_policy(struct smu_context *smu, enum pp_pm_policy p_type,
int level);
ssize_t smu_get_pm_policy_info(struct smu_context *smu,
enum pp_pm_policy p_type, char *sysbuf);
+const struct ras_smu_drv *smu_get_ras_smu_driver(void *handle);
+int amdgpu_smu_ras_send_msg(struct amdgpu_device *adev, enum smu_message_type msg,
+ uint32_t param, uint32_t *readarg);
#endif
void smu_feature_cap_set(struct smu_context *smu, enum smu_feature_cap_id fea_id);
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h
index bf6aa9620911..dd30d96e1ca2 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_pmfw.h
@@ -87,7 +87,7 @@ typedef enum {
/*37*/ FEATURE_DVO = 37,
/*38*/ FEATURE_XVMINORPSM_CLKSTOP_DS = 38,
/*39*/ FEATURE_GLOBAL_DPM = 39,
-/*40*/ FEATURE_NODE_POWER_MANAGER = 40,
+/*40*/ FEATURE_HROM_EN = 40,
/*41*/ NUM_FEATURES = 41
} FEATURE_LIST_e;
@@ -189,7 +189,7 @@ typedef enum {
SVI_MAX_TEMP_ENTRIES, // 13
} SVI_TEMP_e;
-#define SMU_METRICS_TABLE_VERSION 0x14
+#define SMU_METRICS_TABLE_VERSION 0x15
#define SMU_SYSTEM_METRICS_TABLE_VERSION 0x1
@@ -367,6 +367,11 @@ typedef struct {
//Node Power Limit
uint32_t MaxNodePowerLimit;
+
+ // PPT1 Configuration
+ uint32_t PPT1Max;
+ uint32_t PPT1Min;
+ uint32_t PPT1Default;
} StaticMetricsTable_t;
#pragma pack(pop)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h
index 4b066c42e0ec..d09b6ae9827e 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v13_0_12_ppsmc.h
@@ -105,23 +105,21 @@
#define PPSMC_MSG_UpdatePccWaitDecMaxStr 0x4C
#define PPSMC_MSG_ResetSDMA 0x4D
#define PPSMC_MSG_GetRasTableVersion 0x4E
-#define PPSMC_MSG_GetRmaStatus 0x4F
-#define PPSMC_MSG_GetErrorCount 0x50
-#define PPSMC_MSG_GetBadPageCount 0x51
-#define PPSMC_MSG_GetBadPageInfo 0x52
-#define PPSMC_MSG_GetBadPagePaAddrLoHi 0x53
-#define PPSMC_MSG_SetTimestampLoHi 0x54
-#define PPSMC_MSG_GetTimestampLoHi 0x55
-#define PPSMC_MSG_GetRasPolicy 0x56
-#define PPSMC_MSG_DumpErrorRecord 0x57
+#define PPSMC_MSG_GetBadPageCount 0x50
+#define PPSMC_MSG_GetBadPageMcaAddress 0x51
+#define PPSMC_MSG_SetTimestamp 0x53
+#define PPSMC_MSG_SetTimestampHi 0x54
+#define PPSMC_MSG_GetTimestamp 0x55
+#define PPSMC_MSG_GetBadPageIpIdLoHi 0x57
#define PPSMC_MSG_EraseRasTable 0x58
#define PPSMC_MSG_GetStaticMetricsTable 0x59
#define PPSMC_MSG_ResetVfArbitersByIndex 0x5A
-#define PPSMC_MSG_GetBadPageSeverity 0x5B
#define PPSMC_MSG_GetSystemMetricsTable 0x5C
#define PPSMC_MSG_GetSystemMetricsVersion 0x5D
#define PPSMC_MSG_ResetVCN 0x5E
-#define PPSMC_Message_Count 0x5F
+#define PPSMC_MSG_SetFastPptLimit 0x5F
+#define PPSMC_MSG_GetFastPptLimit 0x60
+#define PPSMC_Message_Count 0x61
//PPSMC Reset Types for driver msg argument
#define PPSMC_RESET_TYPE_DRIVER_MODE_1_RESET 0x1
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
index 2256c77da636..9b71a8afdd35 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
@@ -279,7 +279,16 @@
__SMU_DUMMY_MAP(ResetSDMA), \
__SMU_DUMMY_MAP(ResetVCN), \
__SMU_DUMMY_MAP(GetStaticMetricsTable), \
- __SMU_DUMMY_MAP(GetSystemMetricsTable),
+ __SMU_DUMMY_MAP(GetSystemMetricsTable), \
+ __SMU_DUMMY_MAP(GetRASTableVersion), \
+ __SMU_DUMMY_MAP(GetBadPageCount), \
+ __SMU_DUMMY_MAP(GetBadPageMcaAddr), \
+ __SMU_DUMMY_MAP(SetTimestamp), \
+ __SMU_DUMMY_MAP(GetTimestamp), \
+ __SMU_DUMMY_MAP(GetBadPageIpid), \
+ __SMU_DUMMY_MAP(EraseRasTable), \
+ __SMU_DUMMY_MAP(SetFastPptLimit), \
+ __SMU_DUMMY_MAP(GetFastPptLimit),
#undef __SMU_DUMMY_MAP
#define __SMU_DUMMY_MAP(type) SMU_MSG_##type
@@ -458,7 +467,8 @@ enum smu_clk_type {
__SMU_DUMMY_MAP(GFX_EDC_XVMIN), \
__SMU_DUMMY_MAP(GFX_DIDT_XVMIN), \
__SMU_DUMMY_MAP(FAN_ABNORMAL), \
- __SMU_DUMMY_MAP(PIT),
+ __SMU_DUMMY_MAP(PIT), \
+ __SMU_DUMMY_MAP(HROM_EN),
#undef __SMU_DUMMY_MAP
#define __SMU_DUMMY_MAP(feature) SMU_FEATURE_##feature##_BIT
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c
index 9548bd3c624b..55401e6b2b0b 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c
@@ -291,11 +291,12 @@ static int cyan_skillfish_print_clk_levels(struct smu_context *smu,
enum smu_clk_type clk_type,
char *buf)
{
- int ret = 0, size = 0;
+ int ret = 0, size = 0, start_offset = 0;
uint32_t cur_value = 0;
int i;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_SCLK:
@@ -353,7 +354,7 @@ static int cyan_skillfish_print_clk_levels(struct smu_context *smu,
return ret;
}
- return size;
+ return size - start_offset;
}
static bool cyan_skillfish_is_dpm_running(struct smu_context *smu)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
index 0028f10ead42..7c9f77124ab2 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c
@@ -1469,7 +1469,7 @@ static int navi10_print_clk_levels(struct smu_context *smu,
enum smu_clk_type clk_type, char *buf)
{
uint16_t *curve_settings;
- int i, levels, size = 0, ret = 0;
+ int i, levels, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
uint32_t freq_values[3] = {0};
uint32_t mark_index = 0;
@@ -1484,6 +1484,7 @@ static int navi10_print_clk_levels(struct smu_context *smu,
uint32_t min_value, max_value;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_GFXCLK:
@@ -1497,11 +1498,11 @@ static int navi10_print_clk_levels(struct smu_context *smu,
case SMU_DCEFCLK:
ret = navi10_get_current_clk_freq_by_table(smu, clk_type, &cur_value);
if (ret)
- return size;
+ return size - start_offset;
ret = smu_v11_0_get_dpm_level_count(smu, clk_type, &count);
if (ret)
- return size;
+ return size - start_offset;
ret = navi10_is_support_fine_grained_dpm(smu, clk_type);
if (ret < 0)
@@ -1511,7 +1512,7 @@ static int navi10_print_clk_levels(struct smu_context *smu,
for (i = 0; i < count; i++) {
ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, i, &value);
if (ret)
- return size;
+ return size - start_offset;
size += sysfs_emit_at(buf, size, "%d: %uMhz %s\n", i, value,
cur_value == value ? "*" : "");
@@ -1519,10 +1520,10 @@ static int navi10_print_clk_levels(struct smu_context *smu,
} else {
ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, 0, &freq_values[0]);
if (ret)
- return size;
+ return size - start_offset;
ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, count - 1, &freq_values[2]);
if (ret)
- return size;
+ return size - start_offset;
freq_values[1] = cur_value;
mark_index = cur_value == freq_values[0] ? 0 :
@@ -1653,7 +1654,7 @@ static int navi10_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static int navi10_force_clk_levels(struct smu_context *smu,
@@ -2888,7 +2889,7 @@ static int navi10_set_dummy_pstates_table_location(struct smu_context *smu)
dummy_table += 0x1000;
}
- amdgpu_asic_flush_hdp(smu->adev, NULL);
+ amdgpu_hdp_flush(smu->adev, NULL);
ret = smu_cmn_send_smc_msg_with_param(smu,
SMU_MSG_SET_DRIVER_DUMMY_TABLE_DRAM_ADDR_HIGH,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
index 31c2c0386b1f..774283ac7827 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c
@@ -1281,7 +1281,7 @@ static int sienna_cichlid_print_clk_levels(struct smu_context *smu,
struct smu_11_0_7_overdrive_table *od_settings = smu->od_settings;
OverDriveTable_t *od_table =
(OverDriveTable_t *)table_context->overdrive_table;
- int i, size = 0, ret = 0;
+ int i, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
uint32_t freq_values[3] = {0};
uint32_t mark_index = 0;
@@ -1289,6 +1289,7 @@ static int sienna_cichlid_print_clk_levels(struct smu_context *smu,
uint32_t min_value, max_value;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_GFXCLK:
@@ -1434,7 +1435,7 @@ static int sienna_cichlid_print_clk_levels(struct smu_context *smu,
}
print_clk_out:
- return size;
+ return size - start_offset;
}
static int sienna_cichlid_force_clk_levels(struct smu_context *smu,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
index 78e4186d06cc..b0d6487171d7 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
@@ -1022,7 +1022,12 @@ int smu_v11_0_enable_thermal_alert(struct smu_context *smu)
int smu_v11_0_disable_thermal_alert(struct smu_context *smu)
{
- return amdgpu_irq_put(smu->adev, &smu->irq_source, 0);
+ int ret = 0;
+
+ if (smu->smu_table.thermal_controller_type)
+ ret = amdgpu_irq_put(smu->adev, &smu->irq_source, 0);
+
+ return ret;
}
static uint16_t convert_to_vddc(uint8_t vid)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
index 2c9869feba61..9626da2dba58 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c
@@ -565,7 +565,7 @@ static int vangogh_print_legacy_clk_levels(struct smu_context *smu,
DpmClocks_t *clk_table = smu->smu_table.clocks_table;
SmuMetrics_legacy_t metrics;
struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
- int i, idx, size = 0, ret = 0;
+ int i, idx, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
bool cur_value_match_level = false;
@@ -576,6 +576,7 @@ static int vangogh_print_legacy_clk_levels(struct smu_context *smu,
return ret;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_SCLK:
@@ -658,7 +659,7 @@ static int vangogh_print_legacy_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static int vangogh_print_clk_levels(struct smu_context *smu,
@@ -666,7 +667,7 @@ static int vangogh_print_clk_levels(struct smu_context *smu,
{
DpmClocks_t *clk_table = smu->smu_table.clocks_table;
SmuMetrics_t metrics;
- int i, idx, size = 0, ret = 0;
+ int i, idx, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
bool cur_value_match_level = false;
uint32_t min, max;
@@ -678,6 +679,7 @@ static int vangogh_print_clk_levels(struct smu_context *smu,
return ret;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_SCLK:
@@ -779,7 +781,7 @@ static int vangogh_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static int vangogh_common_print_clk_levels(struct smu_context *smu,
@@ -2217,6 +2219,9 @@ static int vangogh_post_smu_init(struct smu_context *smu)
uint32_t total_cu = adev->gfx.config.max_cu_per_sh *
adev->gfx.config.max_sh_per_se * adev->gfx.config.max_shader_engines;
+ if (adev->in_s0ix)
+ return 0;
+
/* allow message will be sent after enable message on Vangogh*/
if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_GFXCLK_BIT) &&
(adev->pg_flags & AMD_PG_SUPPORT_GFX_PG)) {
@@ -2308,8 +2313,7 @@ static int vangogh_get_power_limit(struct smu_context *smu,
uint32_t *max_power_limit,
uint32_t *min_power_limit)
{
- struct smu_11_5_power_context *power_context =
- smu->smu_power.power_context;
+ struct smu_11_5_power_context *power_context = smu->smu_power.power_context;
uint32_t ppt_limit;
int ret = 0;
@@ -2345,12 +2349,11 @@ static int vangogh_get_power_limit(struct smu_context *smu,
}
static int vangogh_get_ppt_limit(struct smu_context *smu,
- uint32_t *ppt_limit,
- enum smu_ppt_limit_type type,
- enum smu_ppt_limit_level level)
+ uint32_t *ppt_limit,
+ enum smu_ppt_limit_type type,
+ enum smu_ppt_limit_level level)
{
- struct smu_11_5_power_context *power_context =
- smu->smu_power.power_context;
+ struct smu_11_5_power_context *power_context = smu->smu_power.power_context;
if (!power_context)
return -EOPNOTSUPP;
@@ -2399,7 +2402,6 @@ static int vangogh_set_power_limit(struct smu_context *smu,
smu->current_power_limit = ppt_limit;
break;
case SMU_FAST_PPT_LIMIT:
- ppt_limit &= ~(SMU_FAST_PPT_LIMIT << 24);
if (ppt_limit > power_context->max_fast_ppt_limit) {
dev_err(smu->adev->dev,
"New power limit (%d) is over the max allowed %d\n",
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 3baf20f4c373..eaa9ea162f16 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
@@ -494,7 +494,7 @@ static int renoir_set_fine_grain_gfx_freq_parameters(struct smu_context *smu)
static int renoir_print_clk_levels(struct smu_context *smu,
enum smu_clk_type clk_type, char *buf)
{
- int i, idx, size = 0, ret = 0;
+ int i, idx, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0, min = 0, max = 0;
SmuMetrics_t metrics;
bool cur_value_match_level = false;
@@ -506,6 +506,7 @@ static int renoir_print_clk_levels(struct smu_context *smu,
return ret;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_RANGE:
@@ -550,7 +551,7 @@ static int renoir_print_clk_levels(struct smu_context *smu,
size += sysfs_emit_at(buf, size, "2: %uMhz %s\n", max,
i == 2 ? "*" : "");
}
- return size;
+ return size - start_offset;
case SMU_SOCCLK:
count = NUM_SOCCLK_DPM_LEVELS;
cur_value = metrics.ClockFrequency[CLOCK_SOCCLK];
@@ -607,7 +608,7 @@ static int renoir_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static enum amd_pm_state_type renoir_get_current_power_state(struct smu_context *smu)
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 c1062e5f0393..677781060246 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
@@ -1195,15 +1195,16 @@ static int smu_v13_0_0_print_clk_levels(struct smu_context *smu,
struct smu_13_0_dpm_table *single_dpm_table;
struct smu_13_0_pcie_table *pcie_table;
uint32_t gen_speed, lane_width;
- int i, curr_freq, size = 0;
+ int i, curr_freq, size = 0, start_offset = 0;
int32_t min_value, max_value;
int ret = 0;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
if (amdgpu_ras_intr_triggered()) {
size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
+ return size - start_offset;
}
switch (clk_type) {
@@ -1534,7 +1535,7 @@ static int smu_v13_0_0_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
index cb3fea9e8cf3..9e635f733fbf 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c
@@ -34,6 +34,7 @@
#include "amdgpu_fru_eeprom.h"
#include <linux/pci.h>
#include "smu_cmn.h"
+#include "amdgpu_ras.h"
#undef MP1_Public
#undef smnMP1_FIRMWARE_FLAGS
@@ -58,7 +59,7 @@
#define NUM_JPEG_RINGS_FW 10
#define NUM_JPEG_RINGS_GPU_METRICS(gpu_metrics) \
- (ARRAY_SIZE(gpu_metrics->xcp_stats[0].jpeg_busy) / 4)
+ (ARRAY_SIZE(gpu_metrics->jpeg_busy) / 4)
const struct cmn2asic_mapping smu_v13_0_12_feature_mask_map[SMU_FEATURE_COUNT] = {
SMU_13_0_12_FEA_MAP(SMU_FEATURE_DATA_CALCULATIONS_BIT, FEATURE_DATA_CALCULATION),
@@ -81,6 +82,7 @@ const struct cmn2asic_mapping smu_v13_0_12_feature_mask_map[SMU_FEATURE_COUNT] =
SMU_13_0_12_FEA_MAP(SMU_FEATURE_DS_MPIOCLK_BIT, FEATURE_DS_MPIOCLK),
SMU_13_0_12_FEA_MAP(SMU_FEATURE_DS_MP0CLK_BIT, FEATURE_DS_MP0CLK),
SMU_13_0_12_FEA_MAP(SMU_FEATURE_PIT_BIT, FEATURE_PIT),
+ SMU_13_0_12_FEA_MAP(SMU_FEATURE_HROM_EN_BIT, FEATURE_HROM_EN),
};
const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[SMU_MSG_MAX_COUNT] = {
@@ -139,6 +141,15 @@ const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[SMU_MSG_MAX_COUNT] =
MSG_MAP(ResetVCN, PPSMC_MSG_ResetVCN, 0),
MSG_MAP(GetStaticMetricsTable, PPSMC_MSG_GetStaticMetricsTable, 1),
MSG_MAP(GetSystemMetricsTable, PPSMC_MSG_GetSystemMetricsTable, 1),
+ MSG_MAP(GetRASTableVersion, PPSMC_MSG_GetRasTableVersion, 0),
+ MSG_MAP(GetBadPageCount, PPSMC_MSG_GetBadPageCount, 0),
+ MSG_MAP(GetBadPageMcaAddr, PPSMC_MSG_GetBadPageMcaAddress, 0),
+ MSG_MAP(SetTimestamp, PPSMC_MSG_SetTimestamp, 0),
+ MSG_MAP(GetTimestamp, PPSMC_MSG_GetTimestamp, 0),
+ MSG_MAP(GetBadPageIpid, PPSMC_MSG_GetBadPageIpIdLoHi, 0),
+ MSG_MAP(EraseRasTable, PPSMC_MSG_EraseRasTable, 0),
+ MSG_MAP(SetFastPptLimit, PPSMC_MSG_SetFastPptLimit, 1),
+ MSG_MAP(GetFastPptLimit, PPSMC_MSG_GetFastPptLimit, 1),
};
int smu_v13_0_12_tables_init(struct smu_context *smu)
@@ -345,6 +356,12 @@ int smu_v13_0_12_setup_driver_pptable(struct smu_context *smu)
if (smu_v13_0_6_cap_supported(smu, SMU_CAP(NPM_METRICS)))
pptable->MaxNodePowerLimit =
SMUQ10_ROUND(static_metrics->MaxNodePowerLimit);
+ if (smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT)) &&
+ static_metrics->PPT1Max) {
+ pptable->PPT1Max = static_metrics->PPT1Max;
+ pptable->PPT1Min = static_metrics->PPT1Min;
+ pptable->PPT1Default = static_metrics->PPT1Default;
+ }
smu_v13_0_12_init_xgmi_data(smu, static_metrics);
pptable->Init = true;
}
@@ -449,7 +466,7 @@ static int smu_v13_0_12_get_system_metrics_table(struct smu_context *smu)
return ret;
}
- amdgpu_asic_invalidate_hdp(smu->adev, NULL);
+ amdgpu_hdp_invalidate(smu->adev, NULL);
smu_table_cache_update_time(sys_table, jiffies);
memcpy(sys_table->cache.buffer, table->cpu_addr,
smu_v13_0_12_get_system_metrics_size());
@@ -719,15 +736,14 @@ static ssize_t smu_v13_0_12_get_temp_metrics(struct smu_context *smu,
ssize_t smu_v13_0_12_get_xcp_metrics(struct smu_context *smu, struct amdgpu_xcp *xcp, void *table, void *smu_metrics)
{
const u8 num_jpeg_rings = NUM_JPEG_RINGS_FW;
- struct amdgpu_partition_metrics_v1_0 *xcp_metrics;
+ struct smu_v13_0_6_partition_metrics *xcp_metrics;
struct amdgpu_device *adev = smu->adev;
MetricsTable_t *metrics;
int inst, j, k, idx;
u32 inst_mask;
metrics = (MetricsTable_t *)smu_metrics;
- xcp_metrics = (struct amdgpu_partition_metrics_v1_0 *) table;
- smu_cmn_init_partition_metrics(xcp_metrics, 1, 0);
+ xcp_metrics = (struct smu_v13_0_6_partition_metrics *)table;
amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_VCN, &inst_mask);
idx = 0;
for_each_inst(k, inst_mask) {
@@ -772,22 +788,17 @@ ssize_t smu_v13_0_12_get_xcp_metrics(struct smu_context *smu, struct amdgpu_xcp
return sizeof(*xcp_metrics);
}
-ssize_t smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table, void *smu_metrics)
+void smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table,
+ void *smu_metrics,
+ struct smu_v13_0_6_gpu_metrics *gpu_metrics)
{
- struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_8 *gpu_metrics =
- (struct gpu_metrics_v1_8 *)smu_table->gpu_metrics_table;
- int ret = 0, xcc_id, inst, i, j, k, idx;
struct amdgpu_device *adev = smu->adev;
+ int ret = 0, xcc_id, inst, i, j;
u8 num_jpeg_rings_gpu_metrics;
MetricsTable_t *metrics;
- struct amdgpu_xcp *xcp;
- u32 inst_mask;
metrics = (MetricsTable_t *)smu_metrics;
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 8);
-
gpu_metrics->temperature_hotspot =
SMUQ10_ROUND(metrics->MaxSocketTemperature);
/* Individual HBM stack temperature is not reported */
@@ -877,60 +888,186 @@ ssize_t smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table, void
gpu_metrics->xgmi_link_status[j] = ret;
}
- gpu_metrics->num_partition = adev->xcp_mgr->num_xcps;
-
num_jpeg_rings_gpu_metrics = NUM_JPEG_RINGS_GPU_METRICS(gpu_metrics);
- for_each_xcp(adev->xcp_mgr, xcp, i) {
- amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_VCN, &inst_mask);
- idx = 0;
- for_each_inst(k, inst_mask) {
- /* Both JPEG and VCN has same instances */
- inst = GET_INST(VCN, k);
-
- for (j = 0; j < num_jpeg_rings_gpu_metrics; ++j) {
- gpu_metrics->xcp_stats[i].jpeg_busy
- [(idx * num_jpeg_rings_gpu_metrics) + j] =
- SMUQ10_ROUND(metrics->JpegBusy
- [(inst * NUM_JPEG_RINGS_FW) + j]);
- }
- gpu_metrics->xcp_stats[i].vcn_busy[idx] =
- SMUQ10_ROUND(metrics->VcnBusy[inst]);
- idx++;
+ for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
+ inst = GET_INST(VCN, i);
+
+ for (j = 0; j < num_jpeg_rings_gpu_metrics; ++j) {
+ gpu_metrics->jpeg_busy[(i * num_jpeg_rings_gpu_metrics) +
+ j] =
+ SMUQ10_ROUND(
+ metrics->JpegBusy[(inst *
+ NUM_JPEG_RINGS_FW) +
+ j]);
}
+ gpu_metrics->vcn_busy[i] = SMUQ10_ROUND(metrics->VcnBusy[inst]);
+ }
- amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, &inst_mask);
- idx = 0;
- for_each_inst(k, inst_mask) {
- inst = GET_INST(GC, k);
- gpu_metrics->xcp_stats[i].gfx_busy_inst[idx] =
- SMUQ10_ROUND(metrics->GfxBusy[inst]);
- gpu_metrics->xcp_stats[i].gfx_busy_acc[idx] =
- SMUQ10_ROUND(metrics->GfxBusyAcc[inst]);
- if (smu_v13_0_6_cap_supported(smu, SMU_CAP(HST_LIMIT_METRICS))) {
- gpu_metrics->xcp_stats[i].gfx_below_host_limit_ppt_acc[idx] =
- SMUQ10_ROUND(metrics->GfxclkBelowHostLimitPptAcc[inst]);
- gpu_metrics->xcp_stats[i].gfx_below_host_limit_thm_acc[idx] =
- SMUQ10_ROUND(metrics->GfxclkBelowHostLimitThmAcc[inst]);
- gpu_metrics->xcp_stats[i].gfx_low_utilization_acc[idx] =
- SMUQ10_ROUND(metrics->GfxclkLowUtilizationAcc[inst]);
- gpu_metrics->xcp_stats[i].gfx_below_host_limit_total_acc[idx] =
- SMUQ10_ROUND(metrics->GfxclkBelowHostLimitTotalAcc[inst]);
- }
- idx++;
- }
+ for (i = 0; i < NUM_XCC(adev->gfx.xcc_mask); ++i) {
+ inst = GET_INST(GC, i);
+ gpu_metrics->gfx_busy_inst[i] =
+ SMUQ10_ROUND(metrics->GfxBusy[inst]);
+ gpu_metrics->gfx_busy_acc[i] =
+ SMUQ10_ROUND(metrics->GfxBusyAcc[inst]);
+ if (smu_v13_0_6_cap_supported(smu,
+ SMU_CAP(HST_LIMIT_METRICS))) {
+ gpu_metrics
+ ->gfx_below_host_limit_ppt_acc[i] = SMUQ10_ROUND(
+ metrics->GfxclkBelowHostLimitPptAcc[inst]);
+ gpu_metrics
+ ->gfx_below_host_limit_thm_acc[i] = SMUQ10_ROUND(
+ metrics->GfxclkBelowHostLimitThmAcc[inst]);
+ gpu_metrics->gfx_low_utilization_acc[i] = SMUQ10_ROUND(
+ metrics->GfxclkLowUtilizationAcc[inst]);
+ gpu_metrics->gfx_below_host_limit_total_acc
+ [i] = SMUQ10_ROUND(
+ metrics->GfxclkBelowHostLimitTotalAcc[inst]);
+ };
}
gpu_metrics->xgmi_link_width = metrics->XgmiWidth;
gpu_metrics->xgmi_link_speed = metrics->XgmiBitrate;
gpu_metrics->firmware_timestamp = metrics->Timestamp;
-
- *table = (void *)gpu_metrics;
-
- return sizeof(*gpu_metrics);
}
const struct smu_temp_funcs smu_v13_0_12_temp_funcs = {
.temp_metrics_is_supported = smu_v13_0_12_is_temp_metrics_supported,
.get_temp_metrics = smu_v13_0_12_get_temp_metrics,
};
+
+static int smu_v13_0_12_get_ras_table_version(struct amdgpu_device *adev,
+ uint32_t *table_version)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetRASTableVersion, 0, table_version);
+}
+
+static int smu_v13_0_12_get_badpage_count(struct amdgpu_device *adev, uint32_t *count,
+ uint32_t timeout)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ uint64_t end, now;
+ int ret = 0;
+
+ now = (uint64_t)ktime_to_ms(ktime_get());
+ end = now + timeout;
+ do {
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetBadPageCount, 0, count);
+ /* eeprom is not ready */
+ if (ret != -EBUSY)
+ return ret;
+ mdelay(10);
+ now = (uint64_t)ktime_to_ms(ktime_get());
+ } while (now < end);
+
+ dev_err(adev->dev,
+ "smu get bad page count timeout!\n");
+ return ret;
+}
+
+static int smu_v13_0_12_set_timestamp(struct amdgpu_device *adev, uint64_t timestamp)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_SetTimestamp, (uint32_t)timestamp, 0);
+}
+
+static int smu_v13_0_12_get_timestamp(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *timestamp)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ uint32_t temp;
+ int ret;
+
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetTimestamp, index, &temp);
+ if (!ret)
+ *timestamp = temp;
+
+ return ret;
+}
+
+static int smu_v13_0_12_get_badpage_ipid(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *ipid)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ uint32_t temp_arg, temp_ipid_lo, temp_ipid_high;
+ int ret;
+
+ temp_arg = index | (1 << 16);
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetBadPageIpid, temp_arg, &temp_ipid_lo);
+ if (ret)
+ return ret;
+
+ temp_arg = index | (2 << 16);
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetBadPageIpid, temp_arg, &temp_ipid_high);
+ if (!ret)
+ *ipid = (uint64_t)temp_ipid_high << 32 | temp_ipid_lo;
+ return ret;
+}
+
+static int smu_v13_0_12_erase_ras_table(struct amdgpu_device *adev,
+ uint32_t *result)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+
+ return smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_EraseRasTable, 0, result);
+}
+
+static int smu_v13_0_12_get_badpage_mca_addr(struct amdgpu_device *adev,
+ uint16_t index, uint64_t *mca_addr)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+ uint32_t temp_arg, temp_addr_lo, temp_addr_high;
+ int ret;
+
+ temp_arg = index | (1 << 16);
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetBadPageMcaAddr, temp_arg, &temp_addr_lo);
+ if (ret)
+ return ret;
+
+ temp_arg = index | (2 << 16);
+ ret = smu_cmn_send_smc_msg_with_param(smu,
+ SMU_MSG_GetBadPageMcaAddr, temp_arg, &temp_addr_high);
+ if (!ret)
+ *mca_addr = (uint64_t)temp_addr_high << 32 | temp_addr_lo;
+ return ret;
+}
+
+static const struct ras_eeprom_smu_funcs smu_v13_0_12_eeprom_smu_funcs = {
+ .get_ras_table_version = smu_v13_0_12_get_ras_table_version,
+ .get_badpage_count = smu_v13_0_12_get_badpage_count,
+ .get_badpage_mca_addr = smu_v13_0_12_get_badpage_mca_addr,
+ .set_timestamp = smu_v13_0_12_set_timestamp,
+ .get_timestamp = smu_v13_0_12_get_timestamp,
+ .get_badpage_ipid = smu_v13_0_12_get_badpage_ipid,
+ .erase_ras_table = smu_v13_0_12_erase_ras_table,
+};
+
+static void smu_v13_0_12_ras_smu_feature_flags(struct amdgpu_device *adev, uint64_t *flags)
+{
+ struct smu_context *smu = adev->powerplay.pp_handle;
+
+ if (!flags)
+ return;
+
+ *flags = 0ULL;
+
+ if (smu_v13_0_6_cap_supported(smu, SMU_CAP(RAS_EEPROM)))
+ *flags |= RAS_SMU_FEATURE_BIT__RAS_EEPROM;
+
+}
+
+const struct ras_smu_drv smu_v13_0_12_ras_smu_drv = {
+ .smu_eeprom_funcs = &smu_v13_0_12_eeprom_smu_funcs,
+ .ras_smu_feature_flags = smu_v13_0_12_ras_smu_feature_flags,
+};
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
index b081ae3e8f43..6908f9930f16 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
@@ -497,11 +497,12 @@ static int smu_v13_0_4_get_dpm_level_count(struct smu_context *smu,
static int smu_v13_0_4_print_clk_levels(struct smu_context *smu,
enum smu_clk_type clk_type, char *buf)
{
- int i, idx, size = 0, ret = 0;
+ int i, idx, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
uint32_t min, max;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_SCLK:
@@ -565,7 +566,7 @@ static int smu_v13_0_4_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static int smu_v13_0_4_read_sensor(struct smu_context *smu,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
index f5db181ef489..4576bf008b22 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c
@@ -861,11 +861,12 @@ out:
static int smu_v13_0_5_print_clk_levels(struct smu_context *smu,
enum smu_clk_type clk_type, char *buf)
{
- int i, idx, size = 0, ret = 0;
+ int i, idx, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
uint32_t min = 0, max = 0;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_SCLK:
@@ -928,7 +929,7 @@ static int smu_v13_0_5_print_clk_levels(struct smu_context *smu,
}
print_clk_out:
- return size;
+ return size - start_offset;
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
index 285cf7979693..44e1cd821eec 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
@@ -356,6 +356,9 @@ static void smu_v13_0_12_init_caps(struct smu_context *smu)
if (fw_ver > 0x04560900)
smu_v13_0_6_cap_set(smu, SMU_CAP(VCN_RESET));
+ if (fw_ver >= 0x04560D00)
+ smu_v13_0_6_cap_set(smu, SMU_CAP(FAST_PPT));
+
if (fw_ver >= 0x04560700) {
if (fw_ver >= 0x04560900) {
smu_v13_0_6_cap_set(smu, SMU_CAP(TEMP_METRICS));
@@ -450,7 +453,8 @@ static void smu_v13_0_6_init_caps(struct smu_context *smu)
((pgm == 4) && (fw_ver >= 0x4557000)))
smu_v13_0_6_cap_set(smu, SMU_CAP(SDMA_RESET));
- if ((pgm == 0) && (fw_ver >= 0x00558200))
+ if ((pgm == 0 && fw_ver >= 0x00558200) ||
+ (pgm == 7 && fw_ver >= 0x07551400))
smu_v13_0_6_cap_set(smu, SMU_CAP(VCN_RESET));
}
@@ -548,7 +552,7 @@ static int smu_v13_0_6_tables_init(struct smu_context *smu)
{
struct smu_table_context *smu_table = &smu->smu_table;
struct smu_table *tables = smu_table->tables;
- void *gpu_metrics_table __free(kfree) = NULL;
+ struct smu_v13_0_6_gpu_metrics *gpu_metrics;
void *driver_pptable __free(kfree) = NULL;
void *metrics_table __free(kfree) = NULL;
struct amdgpu_device *adev = smu->adev;
@@ -578,24 +582,28 @@ static int smu_v13_0_6_tables_init(struct smu_context *smu)
return -ENOMEM;
smu_table->metrics_time = 0;
- smu_table->gpu_metrics_table_size = sizeof(struct gpu_metrics_v1_8);
- gpu_metrics_table =
- kzalloc(smu_table->gpu_metrics_table_size, GFP_KERNEL);
- if (!gpu_metrics_table)
- return -ENOMEM;
-
driver_pptable = kzalloc(sizeof(struct PPTable_t), GFP_KERNEL);
if (!driver_pptable)
return -ENOMEM;
+ ret = smu_table_cache_init(smu, SMU_TABLE_SMU_METRICS,
+ sizeof(struct smu_v13_0_6_gpu_metrics), 1);
+ if (ret)
+ return ret;
+
+ gpu_metrics = (struct smu_v13_0_6_gpu_metrics
+ *)(tables[SMU_TABLE_SMU_METRICS].cache.buffer);
+
+ smu_v13_0_6_gpu_metrics_init(gpu_metrics, 1, 9);
if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) ==
IP_VERSION(13, 0, 12)) {
ret = smu_v13_0_12_tables_init(smu);
- if (ret)
+ if (ret) {
+ smu_table_cache_fini(smu, SMU_TABLE_SMU_METRICS);
return ret;
+ }
}
- smu_table->gpu_metrics_table = no_free_ptr(gpu_metrics_table);
smu_table->metrics_table = no_free_ptr(metrics_table);
smu_table->driver_pptable = no_free_ptr(driver_pptable);
@@ -731,6 +739,7 @@ static int smu_v13_0_6_fini_smc_tables(struct smu_context *smu)
{
if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 12))
smu_v13_0_12_tables_fini(smu);
+ smu_table_cache_fini(smu, SMU_TABLE_SMU_METRICS);
return smu_v13_0_fini_smc_tables(smu);
}
@@ -765,7 +774,7 @@ int smu_v13_0_6_get_metrics_table(struct smu_context *smu, void *metrics_table,
return ret;
}
- amdgpu_asic_invalidate_hdp(smu->adev, NULL);
+ amdgpu_hdp_invalidate(smu->adev, NULL);
memcpy(smu_table->metrics_table, table->cpu_addr, table_size);
smu_table->metrics_time = jiffies;
@@ -844,12 +853,23 @@ int smu_v13_0_6_get_static_metrics_table(struct smu_context *smu)
return ret;
}
- amdgpu_asic_invalidate_hdp(smu->adev, NULL);
+ amdgpu_hdp_invalidate(smu->adev, NULL);
memcpy(smu_table->metrics_table, table->cpu_addr, table_size);
return 0;
}
+static void smu_v13_0_6_update_caps(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct PPTable_t *pptable =
+ (struct PPTable_t *)smu_table->driver_pptable;
+
+ if (smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT)) &&
+ !pptable->PPT1Max)
+ smu_v13_0_6_cap_clear(smu, SMU_CAP(FAST_PPT));
+}
+
static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu)
{
struct smu_table_context *smu_table = &smu->smu_table;
@@ -866,8 +886,12 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu)
uint8_t max_width;
if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 12) &&
- smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS)))
- return smu_v13_0_12_setup_driver_pptable(smu);
+ smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS))) {
+ ret = smu_v13_0_12_setup_driver_pptable(smu);
+ if (ret)
+ return ret;
+ goto out;
+ }
/* Store one-time values in driver PPTable */
if (!pptable->Init) {
@@ -947,7 +971,8 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu)
smu_v13_0_6_fill_static_metrics_table(smu, static_metrics);
}
}
-
+out:
+ smu_v13_0_6_update_caps(smu);
return 0;
}
@@ -1393,7 +1418,7 @@ static int smu_v13_0_6_print_clks(struct smu_context *smu, char *buf, int size,
return -EINVAL;
if (curr_clk < SMU_13_0_6_DSCLK_THRESHOLD) {
- size = sysfs_emit_at(buf, size, "S: %uMhz *\n", curr_clk);
+ size += sysfs_emit_at(buf, size, "S: %uMhz *\n", curr_clk);
for (i = 0; i < clocks.num_levels; i++)
size += sysfs_emit_at(buf, size, "%d: %uMhz\n", i,
clocks.data[i].clocks_in_khz /
@@ -1428,7 +1453,7 @@ static int smu_v13_0_6_print_clks(struct smu_context *smu, char *buf, int size,
static int smu_v13_0_6_print_clk_levels(struct smu_context *smu,
enum smu_clk_type type, char *buf)
{
- int now, size = 0;
+ int now, size = 0, start_offset = 0;
int ret = 0;
struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
struct smu_13_0_dpm_table *single_dpm_table;
@@ -1437,10 +1462,11 @@ static int smu_v13_0_6_print_clk_levels(struct smu_context *smu,
uint32_t min_clk, max_clk;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
if (amdgpu_ras_intr_triggered()) {
size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
+ return size - start_offset;
}
dpm_context = smu_dpm->dpm_context;
@@ -1512,9 +1538,13 @@ static int smu_v13_0_6_print_clk_levels(struct smu_context *smu,
single_dpm_table = &(dpm_context->dpm_tables.uclk_table);
- return smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
- now, "mclk");
+ ret = smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
+ now, "mclk");
+ if (ret < 0)
+ return ret;
+ size += ret;
+ break;
case SMU_SOCCLK:
ret = smu_v13_0_6_get_current_clk_freq_by_table(smu, SMU_SOCCLK,
&now);
@@ -1526,9 +1556,13 @@ static int smu_v13_0_6_print_clk_levels(struct smu_context *smu,
single_dpm_table = &(dpm_context->dpm_tables.soc_table);
- return smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
- now, "socclk");
+ ret = smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
+ now, "socclk");
+ if (ret < 0)
+ return ret;
+ size += ret;
+ break;
case SMU_FCLK:
ret = smu_v13_0_6_get_current_clk_freq_by_table(smu, SMU_FCLK,
&now);
@@ -1540,9 +1574,13 @@ static int smu_v13_0_6_print_clk_levels(struct smu_context *smu,
single_dpm_table = &(dpm_context->dpm_tables.fclk_table);
- return smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
- now, "fclk");
+ ret = smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
+ now, "fclk");
+ if (ret < 0)
+ return ret;
+ size += ret;
+ break;
case SMU_VCLK:
ret = smu_v13_0_6_get_current_clk_freq_by_table(smu, SMU_VCLK,
&now);
@@ -1554,9 +1592,13 @@ static int smu_v13_0_6_print_clk_levels(struct smu_context *smu,
single_dpm_table = &(dpm_context->dpm_tables.vclk_table);
- return smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
- now, "vclk");
+ ret = smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
+ now, "vclk");
+ if (ret < 0)
+ return ret;
+ size += ret;
+ break;
case SMU_DCLK:
ret = smu_v13_0_6_get_current_clk_freq_by_table(smu, SMU_DCLK,
&now);
@@ -1568,14 +1610,18 @@ static int smu_v13_0_6_print_clk_levels(struct smu_context *smu,
single_dpm_table = &(dpm_context->dpm_tables.dclk_table);
- return smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
- now, "dclk");
+ ret = smu_v13_0_6_print_clks(smu, buf, size, single_dpm_table,
+ now, "dclk");
+ if (ret < 0)
+ return ret;
+ size += ret;
+ break;
default:
break;
}
- return size;
+ return size - start_offset;
}
static int smu_v13_0_6_upload_dpm_level(struct smu_context *smu, bool max,
@@ -1845,7 +1891,7 @@ static int smu_v13_0_6_get_power_limit(struct smu_context *smu,
if (current_power_limit)
*current_power_limit = power_limit;
if (default_power_limit)
- *default_power_limit = power_limit;
+ *default_power_limit = pptable->MaxSocketPowerLimit;
if (max_power_limit) {
*max_power_limit = pptable->MaxSocketPowerLimit;
@@ -1860,9 +1906,66 @@ static int smu_v13_0_6_set_power_limit(struct smu_context *smu,
enum smu_ppt_limit_type limit_type,
uint32_t limit)
{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct PPTable_t *pptable =
+ (struct PPTable_t *)smu_table->driver_pptable;
+ int ret;
+
+ if (limit_type == SMU_FAST_PPT_LIMIT) {
+ if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT)))
+ return -EOPNOTSUPP;
+ if (limit > pptable->PPT1Max || limit < pptable->PPT1Min) {
+ dev_err(smu->adev->dev,
+ "New power limit (%d) should be between min %d max %d\n",
+ limit, pptable->PPT1Min, pptable->PPT1Max);
+ return -EINVAL;
+ }
+ ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetFastPptLimit,
+ limit, NULL);
+ if (ret)
+ dev_err(smu->adev->dev, "Set fast PPT limit failed!\n");
+ return ret;
+ }
+
return smu_v13_0_set_power_limit(smu, limit_type, limit);
}
+static int smu_v13_0_6_get_ppt_limit(struct smu_context *smu,
+ uint32_t *ppt_limit,
+ enum smu_ppt_limit_type type,
+ enum smu_ppt_limit_level level)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct PPTable_t *pptable =
+ (struct PPTable_t *)smu_table->driver_pptable;
+ int ret = 0;
+
+ if (type == SMU_FAST_PPT_LIMIT) {
+ if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(FAST_PPT)))
+ return -EOPNOTSUPP;
+ switch (level) {
+ case SMU_PPT_LIMIT_MAX:
+ *ppt_limit = pptable->PPT1Max;
+ break;
+ case SMU_PPT_LIMIT_CURRENT:
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetFastPptLimit, ppt_limit);
+ if (ret)
+ dev_err(smu->adev->dev, "Get fast PPT limit failed!\n");
+ break;
+ case SMU_PPT_LIMIT_DEFAULT:
+ *ppt_limit = pptable->PPT1Default;
+ break;
+ case SMU_PPT_LIMIT_MIN:
+ *ppt_limit = pptable->PPT1Min;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return ret;
+ }
+ return -EOPNOTSUPP;
+}
+
static int smu_v13_0_6_irq_process(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
@@ -2383,7 +2486,7 @@ static int smu_v13_0_6_request_i2c_xfer(struct smu_context *smu,
memcpy(table->cpu_addr, table_data, table_size);
/* Flush hdp cache */
- amdgpu_asic_flush_hdp(adev, NULL);
+ amdgpu_hdp_flush(adev, NULL);
ret = smu_cmn_send_smc_msg(smu, SMU_MSG_RequestI2cTransaction,
NULL);
@@ -2627,7 +2730,7 @@ static ssize_t smu_v13_0_6_get_xcp_metrics(struct smu_context *smu, int xcp_id,
{
const u8 num_jpeg_rings = AMDGPU_MAX_JPEG_RINGS_4_0_3;
int version = smu_v13_0_6_get_metrics_version(smu);
- struct amdgpu_partition_metrics_v1_0 *xcp_metrics;
+ struct smu_v13_0_6_partition_metrics *xcp_metrics;
MetricsTableV0_t *metrics_v0 __free(kfree) = NULL;
struct amdgpu_device *adev = smu->adev;
int ret, inst, i, j, k, idx;
@@ -2647,8 +2750,8 @@ static ssize_t smu_v13_0_6_get_xcp_metrics(struct smu_context *smu, int xcp_id,
if (i == adev->xcp_mgr->num_xcps)
return -EINVAL;
- xcp_metrics = (struct amdgpu_partition_metrics_v1_0 *)table;
- smu_cmn_init_partition_metrics(xcp_metrics, 1, 0);
+ xcp_metrics = (struct smu_v13_0_6_partition_metrics *)table;
+ smu_v13_0_6_partition_metrics_init(xcp_metrics, 1, 1);
metrics_v0 = kzalloc(METRICS_TABLE_SIZE, GFP_KERNEL);
if (!metrics_v0)
@@ -2740,18 +2843,16 @@ static ssize_t smu_v13_0_6_get_xcp_metrics(struct smu_context *smu, int xcp_id,
static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table)
{
struct smu_table_context *smu_table = &smu->smu_table;
- struct gpu_metrics_v1_8 *gpu_metrics =
- (struct gpu_metrics_v1_8 *)smu_table->gpu_metrics_table;
+ struct smu_table *tables = smu_table->tables;
+ struct smu_v13_0_6_gpu_metrics *gpu_metrics;
int version = smu_v13_0_6_get_metrics_version(smu);
MetricsTableV0_t *metrics_v0 __free(kfree) = NULL;
- int ret = 0, xcc_id, inst, i, j, k, idx;
struct amdgpu_device *adev = smu->adev;
+ int ret = 0, xcc_id, inst, i, j;
MetricsTableV1_t *metrics_v1;
MetricsTableV2_t *metrics_v2;
- struct amdgpu_xcp *xcp;
u16 link_width_level;
u8 num_jpeg_rings;
- u32 inst_mask;
bool per_inst;
metrics_v0 = kzalloc(METRICS_TABLE_SIZE, GFP_KERNEL);
@@ -2759,16 +2860,20 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table
if (ret)
return ret;
- if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) ==
- IP_VERSION(13, 0, 12) &&
- smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS)))
- return smu_v13_0_12_get_gpu_metrics(smu, table, metrics_v0);
+ metrics_v2 = (MetricsTableV2_t *)metrics_v0;
+ gpu_metrics = (struct smu_v13_0_6_gpu_metrics
+ *)(tables[SMU_TABLE_SMU_METRICS].cache.buffer);
+
+ if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(13, 0, 12) &&
+ smu_v13_0_6_cap_supported(smu, SMU_CAP(STATIC_METRICS))) {
+ smu_v13_0_12_get_gpu_metrics(smu, table, metrics_v0,
+ gpu_metrics);
+ goto fill;
+ }
metrics_v1 = (MetricsTableV1_t *)metrics_v0;
metrics_v2 = (MetricsTableV2_t *)metrics_v0;
- smu_cmn_init_soft_gpu_metrics(gpu_metrics, 1, 8);
-
gpu_metrics->temperature_hotspot =
SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version));
/* Individual HBM stack temperature is not reported */
@@ -2889,55 +2994,49 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table
gpu_metrics->xgmi_link_status[j] = ret;
}
- gpu_metrics->num_partition = adev->xcp_mgr->num_xcps;
-
per_inst = smu_v13_0_6_cap_supported(smu, SMU_CAP(PER_INST_METRICS));
num_jpeg_rings = AMDGPU_MAX_JPEG_RINGS_4_0_3;
- for_each_xcp(adev->xcp_mgr, xcp, i) {
- amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_VCN, &inst_mask);
- idx = 0;
- for_each_inst(k, inst_mask) {
- /* Both JPEG and VCN has same instances */
- inst = GET_INST(VCN, k);
-
- for (j = 0; j < num_jpeg_rings; ++j) {
- gpu_metrics->xcp_stats[i].jpeg_busy
- [(idx * num_jpeg_rings) + j] =
- SMUQ10_ROUND(GET_METRIC_FIELD(JpegBusy, version)
- [(inst * num_jpeg_rings) + j]);
- }
- gpu_metrics->xcp_stats[i].vcn_busy[idx] =
- SMUQ10_ROUND(GET_METRIC_FIELD(VcnBusy, version)[inst]);
- idx++;
-
- }
+ for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) {
+ inst = GET_INST(JPEG, i);
+ for (j = 0; j < num_jpeg_rings; ++j)
+ gpu_metrics->jpeg_busy[(i * num_jpeg_rings) + j] =
+ SMUQ10_ROUND(GET_METRIC_FIELD(
+ JpegBusy,
+ version)[(inst * num_jpeg_rings) + j]);
+ }
+ for (i = 0; i < adev->vcn.num_vcn_inst; ++i) {
+ inst = GET_INST(VCN, i);
+ gpu_metrics->vcn_busy[i] =
+ SMUQ10_ROUND(GET_METRIC_FIELD(VcnBusy, version)[inst]);
+ }
- if (per_inst) {
- amdgpu_xcp_get_inst_details(xcp, AMDGPU_XCP_GFX, &inst_mask);
- idx = 0;
- for_each_inst(k, inst_mask) {
- inst = GET_INST(GC, k);
- gpu_metrics->xcp_stats[i].gfx_busy_inst[idx] =
- SMUQ10_ROUND(GET_GPU_METRIC_FIELD(GfxBusy, version)[inst]);
- gpu_metrics->xcp_stats[i].gfx_busy_acc[idx] =
- SMUQ10_ROUND(GET_GPU_METRIC_FIELD(GfxBusyAcc,
- version)[inst]);
- if (smu_v13_0_6_cap_supported(smu, SMU_CAP(HST_LIMIT_METRICS))) {
- gpu_metrics->xcp_stats[i].gfx_below_host_limit_ppt_acc[idx] =
- SMUQ10_ROUND
- (metrics_v0->GfxclkBelowHostLimitPptAcc[inst]);
- gpu_metrics->xcp_stats[i].gfx_below_host_limit_thm_acc[idx] =
- SMUQ10_ROUND
- (metrics_v0->GfxclkBelowHostLimitThmAcc[inst]);
- gpu_metrics->xcp_stats[i].gfx_low_utilization_acc[idx] =
- SMUQ10_ROUND
- (metrics_v0->GfxclkLowUtilizationAcc[inst]);
- gpu_metrics->xcp_stats[i].gfx_below_host_limit_total_acc[idx] =
- SMUQ10_ROUND
- (metrics_v0->GfxclkBelowHostLimitTotalAcc[inst]);
- }
- idx++;
+ if (per_inst) {
+ for (i = 0; i < NUM_XCC(adev->gfx.xcc_mask); ++i) {
+ inst = GET_INST(GC, i);
+ gpu_metrics->gfx_busy_inst[i] = SMUQ10_ROUND(
+ GET_GPU_METRIC_FIELD(GfxBusy, version)[inst]);
+ gpu_metrics->gfx_busy_acc[i] = SMUQ10_ROUND(
+ GET_GPU_METRIC_FIELD(GfxBusyAcc,
+ version)[inst]);
+ if (smu_v13_0_6_cap_supported(
+ smu, SMU_CAP(HST_LIMIT_METRICS))) {
+ gpu_metrics->gfx_below_host_limit_ppt_acc
+ [i] = SMUQ10_ROUND(
+ metrics_v0->GfxclkBelowHostLimitPptAcc
+ [inst]);
+ gpu_metrics->gfx_below_host_limit_thm_acc
+ [i] = SMUQ10_ROUND(
+ metrics_v0->GfxclkBelowHostLimitThmAcc
+ [inst]);
+ gpu_metrics->gfx_low_utilization_acc
+ [i] = SMUQ10_ROUND(
+ metrics_v0
+ ->GfxclkLowUtilizationAcc[inst]);
+ gpu_metrics->gfx_below_host_limit_total_acc
+ [i] = SMUQ10_ROUND(
+ metrics_v0->GfxclkBelowHostLimitTotalAcc
+ [inst]);
}
}
}
@@ -2947,7 +3046,8 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table
gpu_metrics->firmware_timestamp = GET_METRIC_FIELD(Timestamp, version);
- *table = (void *)gpu_metrics;
+fill:
+ *table = tables[SMU_TABLE_SMU_METRICS].cache.buffer;
return sizeof(*gpu_metrics);
}
@@ -3226,6 +3326,24 @@ static int smu_v13_0_6_reset_vcn(struct smu_context *smu, uint32_t inst_mask)
return ret;
}
+static int smu_v13_0_6_ras_send_msg(struct smu_context *smu, enum smu_message_type msg, uint32_t param, uint32_t *read_arg)
+{
+ int ret;
+
+ switch (msg) {
+ case SMU_MSG_QueryValidMcaCount:
+ case SMU_MSG_QueryValidMcaCeCount:
+ case SMU_MSG_McaBankDumpDW:
+ case SMU_MSG_McaBankCeDumpDW:
+ case SMU_MSG_ClearMcaOnRead:
+ ret = smu_cmn_send_smc_msg_with_param(smu, msg, param, read_arg);
+ break;
+ default:
+ ret = -EPERM;
+ }
+
+ return ret;
+}
static int smu_v13_0_6_post_init(struct smu_context *smu)
{
@@ -3863,6 +3981,29 @@ static void smu_v13_0_6_set_temp_funcs(struct smu_context *smu)
== IP_VERSION(13, 0, 12)) ? &smu_v13_0_12_temp_funcs : NULL;
}
+static int smu_v13_0_6_get_ras_smu_drv(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv)
+{
+ if (!ras_smu_drv)
+ return -EINVAL;
+
+ if (amdgpu_sriov_vf(smu->adev))
+ return -EOPNOTSUPP;
+
+ if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_HROM_EN_BIT))
+ smu_v13_0_6_cap_set(smu, SMU_CAP(RAS_EEPROM));
+
+ switch (amdgpu_ip_version(smu->adev, MP1_HWIP, 0)) {
+ case IP_VERSION(13, 0, 12):
+ *ras_smu_drv = &smu_v13_0_12_ras_smu_drv;
+ break;
+ default:
+ *ras_smu_drv = NULL;
+ break;
+ }
+
+ return 0;
+}
+
static const struct pptable_funcs smu_v13_0_6_ppt_funcs = {
/* init dpm */
.get_allowed_feature_mask = smu_v13_0_6_get_allowed_feature_mask,
@@ -3894,6 +4035,7 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = {
.get_enabled_mask = smu_v13_0_6_get_enabled_mask,
.feature_is_enabled = smu_cmn_feature_is_enabled,
.set_power_limit = smu_v13_0_6_set_power_limit,
+ .get_ppt_limit = smu_v13_0_6_get_ppt_limit,
.set_xgmi_pstate = smu_v13_0_set_xgmi_pstate,
.register_irq_handler = smu_v13_0_6_register_irq_handler,
.enable_thermal_alert = smu_v13_0_enable_thermal_alert,
@@ -3921,6 +4063,8 @@ static const struct pptable_funcs smu_v13_0_6_ppt_funcs = {
.reset_sdma = smu_v13_0_6_reset_sdma,
.dpm_reset_vcn = smu_v13_0_6_reset_vcn,
.post_init = smu_v13_0_6_post_init,
+ .ras_send_msg = smu_v13_0_6_ras_send_msg,
+ .get_ras_smu_drv = smu_v13_0_6_get_ras_smu_drv,
};
void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h
index 7ef5f3e66c27..6cbdd7c5ded9 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.h
@@ -50,6 +50,9 @@ struct PPTable_t {
uint32_t MinLclkDpmRange;
uint64_t PublicSerialNumber_AID;
uint32_t MaxNodePowerLimit;
+ uint32_t PPT1Max;
+ uint32_t PPT1Min;
+ uint32_t PPT1Default;
bool Init;
};
@@ -72,9 +75,18 @@ enum smu_v13_0_6_caps {
SMU_CAP(PLDM_VERSION),
SMU_CAP(TEMP_METRICS),
SMU_CAP(NPM_METRICS),
+ SMU_CAP(RAS_EEPROM),
+ SMU_CAP(FAST_PPT),
SMU_CAP(ALL),
};
+#define SMU_13_0_6_NUM_XGMI_LINKS 8
+#define SMU_13_0_6_MAX_GFX_CLKS 8
+#define SMU_13_0_6_MAX_CLKS 4
+#define SMU_13_0_6_MAX_XCC 8
+#define SMU_13_0_6_MAX_VCN 4
+#define SMU_13_0_6_MAX_JPEG 40
+
extern void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu);
bool smu_v13_0_6_cap_supported(struct smu_context *smu, enum smu_v13_0_6_caps cap);
int smu_v13_0_6_get_static_metrics_table(struct smu_context *smu);
@@ -87,7 +99,6 @@ size_t smu_v13_0_12_get_system_metrics_size(void);
int smu_v13_0_12_setup_driver_pptable(struct smu_context *smu);
int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu,
MetricsMember_t member, uint32_t *value);
-ssize_t smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table, void *smu_metrics);
ssize_t smu_v13_0_12_get_xcp_metrics(struct smu_context *smu,
struct amdgpu_xcp *xcp, void *table,
void *smu_metrics);
@@ -99,4 +110,156 @@ int smu_v13_0_12_get_npm_data(struct smu_context *smu,
extern const struct cmn2asic_mapping smu_v13_0_12_feature_mask_map[];
extern const struct cmn2asic_msg_mapping smu_v13_0_12_message_map[];
extern const struct smu_temp_funcs smu_v13_0_12_temp_funcs;
+extern const struct ras_smu_drv smu_v13_0_12_ras_smu_drv;
+
+#if defined(SWSMU_CODE_LAYER_L2)
+#include "smu_cmn.h"
+
+/* SMUv 13.0.6 GPU metrics*/
+#define SMU_13_0_6_METRICS_FIELDS(SMU_SCALAR, SMU_ARRAY) \
+ SMU_SCALAR(SMU_MATTR(TEMPERATURE_HOTSPOT), SMU_MUNIT(TEMP_1), \
+ SMU_MTYPE(U16), temperature_hotspot); \
+ SMU_SCALAR(SMU_MATTR(TEMPERATURE_MEM), SMU_MUNIT(TEMP_1), \
+ SMU_MTYPE(U16), temperature_mem); \
+ SMU_SCALAR(SMU_MATTR(TEMPERATURE_VRSOC), SMU_MUNIT(TEMP_1), \
+ SMU_MTYPE(U16), temperature_vrsoc); \
+ SMU_SCALAR(SMU_MATTR(CURR_SOCKET_POWER), SMU_MUNIT(POWER_1), \
+ SMU_MTYPE(U16), curr_socket_power); \
+ SMU_SCALAR(SMU_MATTR(AVERAGE_GFX_ACTIVITY), SMU_MUNIT(PERCENT), \
+ SMU_MTYPE(U16), average_gfx_activity); \
+ SMU_SCALAR(SMU_MATTR(AVERAGE_UMC_ACTIVITY), SMU_MUNIT(PERCENT), \
+ SMU_MTYPE(U16), average_umc_activity); \
+ SMU_SCALAR(SMU_MATTR(MEM_MAX_BANDWIDTH), SMU_MUNIT(BW_1), \
+ SMU_MTYPE(U64), mem_max_bandwidth); \
+ SMU_SCALAR(SMU_MATTR(ENERGY_ACCUMULATOR), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), energy_accumulator); \
+ SMU_SCALAR(SMU_MATTR(SYSTEM_CLOCK_COUNTER), SMU_MUNIT(TIME_1), \
+ SMU_MTYPE(U64), system_clock_counter); \
+ SMU_SCALAR(SMU_MATTR(ACCUMULATION_COUNTER), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), accumulation_counter); \
+ SMU_SCALAR(SMU_MATTR(PROCHOT_RESIDENCY_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), prochot_residency_acc); \
+ SMU_SCALAR(SMU_MATTR(PPT_RESIDENCY_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), ppt_residency_acc); \
+ SMU_SCALAR(SMU_MATTR(SOCKET_THM_RESIDENCY_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), socket_thm_residency_acc); \
+ SMU_SCALAR(SMU_MATTR(VR_THM_RESIDENCY_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), vr_thm_residency_acc); \
+ SMU_SCALAR(SMU_MATTR(HBM_THM_RESIDENCY_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), hbm_thm_residency_acc); \
+ SMU_SCALAR(SMU_MATTR(GFXCLK_LOCK_STATUS), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), gfxclk_lock_status); \
+ SMU_SCALAR(SMU_MATTR(PCIE_LINK_WIDTH), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U16), pcie_link_width); \
+ SMU_SCALAR(SMU_MATTR(PCIE_LINK_SPEED), SMU_MUNIT(SPEED_2), \
+ SMU_MTYPE(U16), pcie_link_speed); \
+ SMU_SCALAR(SMU_MATTR(XGMI_LINK_WIDTH), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U16), xgmi_link_width); \
+ SMU_SCALAR(SMU_MATTR(XGMI_LINK_SPEED), SMU_MUNIT(SPEED_1), \
+ SMU_MTYPE(U16), xgmi_link_speed); \
+ SMU_SCALAR(SMU_MATTR(GFX_ACTIVITY_ACC), SMU_MUNIT(PERCENT), \
+ SMU_MTYPE(U32), gfx_activity_acc); \
+ SMU_SCALAR(SMU_MATTR(MEM_ACTIVITY_ACC), SMU_MUNIT(PERCENT), \
+ SMU_MTYPE(U32), mem_activity_acc); \
+ SMU_SCALAR(SMU_MATTR(PCIE_BANDWIDTH_ACC), SMU_MUNIT(PERCENT), \
+ SMU_MTYPE(U64), pcie_bandwidth_acc); \
+ SMU_SCALAR(SMU_MATTR(PCIE_BANDWIDTH_INST), SMU_MUNIT(BW_1), \
+ SMU_MTYPE(U64), pcie_bandwidth_inst); \
+ SMU_SCALAR(SMU_MATTR(PCIE_L0_TO_RECOV_COUNT_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), pcie_l0_to_recov_count_acc); \
+ SMU_SCALAR(SMU_MATTR(PCIE_REPLAY_COUNT_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), pcie_replay_count_acc); \
+ SMU_SCALAR(SMU_MATTR(PCIE_REPLAY_ROVER_COUNT_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), pcie_replay_rover_count_acc); \
+ SMU_SCALAR(SMU_MATTR(PCIE_NAK_SENT_COUNT_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), pcie_nak_sent_count_acc); \
+ SMU_SCALAR(SMU_MATTR(PCIE_NAK_RCVD_COUNT_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U32), pcie_nak_rcvd_count_acc); \
+ SMU_ARRAY(SMU_MATTR(XGMI_READ_DATA_ACC), SMU_MUNIT(DATA_1), \
+ SMU_MTYPE(U64), xgmi_read_data_acc, \
+ SMU_13_0_6_NUM_XGMI_LINKS); \
+ SMU_ARRAY(SMU_MATTR(XGMI_WRITE_DATA_ACC), SMU_MUNIT(DATA_1), \
+ SMU_MTYPE(U64), xgmi_write_data_acc, \
+ SMU_13_0_6_NUM_XGMI_LINKS); \
+ SMU_ARRAY(SMU_MATTR(XGMI_LINK_STATUS), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U16), xgmi_link_status, \
+ SMU_13_0_6_NUM_XGMI_LINKS); \
+ SMU_SCALAR(SMU_MATTR(FIRMWARE_TIMESTAMP), SMU_MUNIT(TIME_2), \
+ SMU_MTYPE(U64), firmware_timestamp); \
+ SMU_ARRAY(SMU_MATTR(CURRENT_GFXCLK), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_gfxclk, SMU_13_0_6_MAX_GFX_CLKS); \
+ SMU_ARRAY(SMU_MATTR(CURRENT_SOCCLK), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_socclk, SMU_13_0_6_MAX_CLKS); \
+ SMU_ARRAY(SMU_MATTR(CURRENT_VCLK0), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_vclk0, SMU_13_0_6_MAX_CLKS); \
+ SMU_ARRAY(SMU_MATTR(CURRENT_DCLK0), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_dclk0, SMU_13_0_6_MAX_CLKS); \
+ SMU_SCALAR(SMU_MATTR(CURRENT_UCLK), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_uclk); \
+ SMU_SCALAR(SMU_MATTR(PCIE_LC_PERF_OTHER_END_RECOVERY), \
+ SMU_MUNIT(NONE), SMU_MTYPE(U32), \
+ pcie_lc_perf_other_end_recovery); \
+ SMU_ARRAY(SMU_MATTR(GFX_BUSY_INST), SMU_MUNIT(PERCENT), \
+ SMU_MTYPE(U32), gfx_busy_inst, SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(JPEG_BUSY), SMU_MUNIT(PERCENT), SMU_MTYPE(U16), \
+ jpeg_busy, SMU_13_0_6_MAX_JPEG); \
+ SMU_ARRAY(SMU_MATTR(VCN_BUSY), SMU_MUNIT(PERCENT), SMU_MTYPE(U16), \
+ vcn_busy, SMU_13_0_6_MAX_VCN); \
+ SMU_ARRAY(SMU_MATTR(GFX_BUSY_ACC), SMU_MUNIT(PERCENT), SMU_MTYPE(U64), \
+ gfx_busy_acc, SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_PPT_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_below_host_limit_ppt_acc, \
+ SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_THM_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_below_host_limit_thm_acc, \
+ SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_LOW_UTILIZATION_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_low_utilization_acc, \
+ SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_TOTAL_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_below_host_limit_total_acc, \
+ SMU_13_0_6_MAX_XCC);
+
+DECLARE_SMU_METRICS_CLASS(smu_v13_0_6_gpu_metrics, SMU_13_0_6_METRICS_FIELDS);
+void smu_v13_0_12_get_gpu_metrics(struct smu_context *smu, void **table,
+ void *smu_metrics,
+ struct smu_v13_0_6_gpu_metrics *gpu_metrics);
+
+#define SMU_13_0_6_PARTITION_METRICS_FIELDS(SMU_SCALAR, SMU_ARRAY) \
+ SMU_ARRAY(SMU_MATTR(CURRENT_GFXCLK), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_gfxclk, SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(CURRENT_SOCCLK), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_socclk, SMU_13_0_6_MAX_CLKS); \
+ SMU_ARRAY(SMU_MATTR(CURRENT_VCLK0), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_vclk0, SMU_13_0_6_MAX_CLKS); \
+ SMU_ARRAY(SMU_MATTR(CURRENT_DCLK0), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_dclk0, SMU_13_0_6_MAX_CLKS); \
+ SMU_SCALAR(SMU_MATTR(CURRENT_UCLK), SMU_MUNIT(CLOCK_1), \
+ SMU_MTYPE(U16), current_uclk); \
+ SMU_ARRAY(SMU_MATTR(GFX_BUSY_INST), SMU_MUNIT(PERCENT), \
+ SMU_MTYPE(U32), gfx_busy_inst, SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(JPEG_BUSY), SMU_MUNIT(PERCENT), SMU_MTYPE(U16), \
+ jpeg_busy, SMU_13_0_6_MAX_JPEG); \
+ SMU_ARRAY(SMU_MATTR(VCN_BUSY), SMU_MUNIT(PERCENT), SMU_MTYPE(U16), \
+ vcn_busy, SMU_13_0_6_MAX_VCN); \
+ SMU_ARRAY(SMU_MATTR(GFX_BUSY_ACC), SMU_MUNIT(PERCENT), SMU_MTYPE(U64), \
+ gfx_busy_acc, SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_PPT_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_below_host_limit_ppt_acc, \
+ SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_THM_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_below_host_limit_thm_acc, \
+ SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_LOW_UTILIZATION_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_low_utilization_acc, \
+ SMU_13_0_6_MAX_XCC); \
+ SMU_ARRAY(SMU_MATTR(GFX_BELOW_HOST_LIMIT_TOTAL_ACC), SMU_MUNIT(NONE), \
+ SMU_MTYPE(U64), gfx_below_host_limit_total_acc, \
+ SMU_13_0_6_MAX_XCC);
+
+DECLARE_SMU_METRICS_CLASS(smu_v13_0_6_partition_metrics,
+ SMU_13_0_6_PARTITION_METRICS_FIELDS);
+
+#endif /* SWSMU_CODE_LAYER_L2 */
+
#endif
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 c96fa5e49ed6..a3fc35b9011e 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
@@ -1184,15 +1184,16 @@ static int smu_v13_0_7_print_clk_levels(struct smu_context *smu,
struct smu_13_0_dpm_table *single_dpm_table;
struct smu_13_0_pcie_table *pcie_table;
uint32_t gen_speed, lane_width;
- int i, curr_freq, size = 0;
+ int i, curr_freq, size = 0, start_offset = 0;
int32_t min_value, max_value;
int ret = 0;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
if (amdgpu_ras_intr_triggered()) {
size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
+ return size - start_offset;
}
switch (clk_type) {
@@ -1523,7 +1524,7 @@ static int smu_v13_0_7_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static int smu_v13_0_7_od_restore_table_single(struct smu_context *smu, long input)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
index 73b4506ef5a8..5d7e671fa3c3 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c
@@ -1041,12 +1041,13 @@ static uint32_t yellow_carp_get_umd_pstate_clk_default(struct smu_context *smu,
static int yellow_carp_print_clk_levels(struct smu_context *smu,
enum smu_clk_type clk_type, char *buf)
{
- int i, idx, size = 0, ret = 0;
+ int i, idx, size = 0, ret = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
uint32_t min, max;
uint32_t clk_limit = 0;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_SCLK:
@@ -1111,7 +1112,7 @@ static int yellow_carp_print_clk_levels(struct smu_context *smu,
}
print_clk_out:
- return size;
+ return size - start_offset;
}
static int yellow_carp_force_clk_levels(struct smu_context *smu,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
index fe00c84b1cc6..b1bd946d8e30 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c
@@ -1132,11 +1132,12 @@ static int smu_v14_0_common_get_dpm_level_count(struct smu_context *smu,
static int smu_v14_0_0_print_clk_levels(struct smu_context *smu,
enum smu_clk_type clk_type, char *buf)
{
- int i, idx, ret = 0, size = 0;
+ int i, idx, ret = 0, size = 0, start_offset = 0;
uint32_t cur_value = 0, value = 0, count = 0;
uint32_t min, max;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
switch (clk_type) {
case SMU_OD_SCLK:
@@ -1202,7 +1203,7 @@ static int smu_v14_0_0_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static int smu_v14_0_0_set_soft_freq_limited_range(struct smu_context *smu,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
index 086501cc5213..2cea688c604f 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
@@ -1056,15 +1056,16 @@ static int smu_v14_0_2_print_clk_levels(struct smu_context *smu,
struct smu_14_0_dpm_table *single_dpm_table;
struct smu_14_0_pcie_table *pcie_table;
uint32_t gen_speed, lane_width;
- int i, curr_freq, size = 0;
+ int i, curr_freq, size = 0, start_offset = 0;
int32_t min_value, max_value;
int ret = 0;
smu_cmn_get_sysfs_buf(&buf, &size);
+ start_offset = size;
if (amdgpu_ras_intr_triggered()) {
size += sysfs_emit_at(buf, size, "unavailable\n");
- return size;
+ return size - start_offset;
}
switch (clk_type) {
@@ -1374,7 +1375,7 @@ static int smu_v14_0_2_print_clk_levels(struct smu_context *smu,
break;
}
- return size;
+ return size - start_offset;
}
static int smu_v14_0_2_force_clk_levels(struct smu_context *smu,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
index f532f7c69259..4040ff926544 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
@@ -164,9 +164,13 @@ static void __smu_cmn_reg_print_error(struct smu_context *smu,
msg_index, param, message);
break;
case SMU_RESP_BUSY_OTHER:
- dev_err_ratelimited(adev->dev,
- "SMU: I'm very busy for your command: index:%d param:0x%08X message:%s",
- msg_index, param, message);
+ /* It is normal for SMU_MSG_GetBadPageCount to return busy
+ * so don't print error at this case.
+ */
+ if (msg != SMU_MSG_GetBadPageCount)
+ dev_err_ratelimited(adev->dev,
+ "SMU: I'm very busy for your command: index:%d param:0x%08X message:%s",
+ msg_index, param, message);
break;
case SMU_RESP_DEBUG_END:
dev_err_ratelimited(adev->dev,
@@ -969,7 +973,7 @@ int smu_cmn_update_table(struct smu_context *smu,
table_index);
uint32_t table_size;
int ret = 0;
- if (!table_data || table_id >= SMU_TABLE_COUNT || table_id < 0)
+ if (!table_data || table_index >= SMU_TABLE_COUNT || table_id < 0)
return -EINVAL;
table_size = smu_table->tables[table_index].size;
@@ -980,7 +984,7 @@ int smu_cmn_update_table(struct smu_context *smu,
* Flush hdp cache: to guard the content seen by
* GPU is consitent with CPU.
*/
- amdgpu_asic_flush_hdp(adev, NULL);
+ amdgpu_hdp_flush(adev, NULL);
}
ret = smu_cmn_send_smc_msg_with_param(smu, drv2smu ?
@@ -992,7 +996,7 @@ int smu_cmn_update_table(struct smu_context *smu,
return ret;
if (!drv2smu) {
- amdgpu_asic_invalidate_hdp(adev, NULL);
+ amdgpu_hdp_invalidate(adev, NULL);
memcpy(table_data, table->cpu_addr, table_size);
}
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
index 0ae91c8b6d72..8d7c4814c68f 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
@@ -202,5 +202,72 @@ void smu_cmn_get_backend_workload_mask(struct smu_context *smu,
u32 workload_mask,
u32 *backend_workload_mask);
+/*SMU gpu metrics */
+
+/* Attribute ID mapping */
+#define SMU_MATTR(X) AMDGPU_METRICS_ATTR_ID_##X
+/* Type ID mapping */
+#define SMU_MTYPE(X) AMDGPU_METRICS_TYPE_##X
+/* Unit ID mapping */
+#define SMU_MUNIT(X) AMDGPU_METRICS_UNIT_##X
+
+/* Map TYPEID to C type */
+#define SMU_CTYPE(TYPEID) SMU_CTYPE_##TYPEID
+
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_U8 u8
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_S8 s8
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_U16 u16
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_S16 s16
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_U32 u32
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_S32 s32
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_U64 u64
+#define SMU_CTYPE_AMDGPU_METRICS_TYPE_S64 s64
+
+/* struct members */
+#define SMU_METRICS_SCALAR(ID, UNIT, TYPEID, NAME) \
+ u64 NAME##_ftype; \
+ SMU_CTYPE(TYPEID) NAME
+
+#define SMU_METRICS_ARRAY(ID, UNIT, TYPEID, NAME, SIZE) \
+ u64 NAME##_ftype; \
+ SMU_CTYPE(TYPEID) NAME[SIZE]
+
+/* Init functions for scalar/array fields - init to 0xFFs */
+#define SMU_METRICS_INIT_SCALAR(ID, UNIT, TYPEID, NAME) \
+ do { \
+ obj->NAME##_ftype = \
+ AMDGPU_METRICS_ENC_ATTR(UNIT, TYPEID, ID, 1); \
+ obj->NAME = (SMU_CTYPE(TYPEID)) ~0; \
+ count++; \
+ } while (0)
+
+#define SMU_METRICS_INIT_ARRAY(ID, UNIT, TYPEID, NAME, SIZE) \
+ do { \
+ obj->NAME##_ftype = \
+ AMDGPU_METRICS_ENC_ATTR(UNIT, TYPEID, ID, SIZE); \
+ memset(obj->NAME, 0xFF, sizeof(obj->NAME)); \
+ count++; \
+ } while (0)
+
+/* Declare Metrics Class and Template object */
+#define DECLARE_SMU_METRICS_CLASS(CLASSNAME, SMU_METRICS_FIELD_LIST) \
+ struct __packed CLASSNAME { \
+ struct metrics_table_header header; \
+ int attr_count; \
+ SMU_METRICS_FIELD_LIST(SMU_METRICS_SCALAR, SMU_METRICS_ARRAY); \
+ }; \
+ static inline void CLASSNAME##_init(struct CLASSNAME *obj, \
+ uint8_t frev, uint8_t crev) \
+ { \
+ int count = 0; \
+ memset(obj, 0xFF, sizeof(*obj)); \
+ obj->header.format_revision = frev; \
+ obj->header.content_revision = crev; \
+ obj->header.structure_size = sizeof(*obj); \
+ SMU_METRICS_FIELD_LIST(SMU_METRICS_INIT_SCALAR, \
+ SMU_METRICS_INIT_ARRAY) \
+ obj->attr_count = count; \
+ }
+
#endif
#endif
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h b/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h
index c09ecf1a68a0..34f6b4b1c3ba 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_internal.h
@@ -100,6 +100,7 @@
#define smu_is_asic_wbrf_supported(smu) smu_ppt_funcs(is_asic_wbrf_supported, false, smu)
#define smu_enable_uclk_shadow(smu, enable) smu_ppt_funcs(enable_uclk_shadow, 0, smu, enable)
#define smu_set_wbrf_exclusion_ranges(smu, freq_band_range) smu_ppt_funcs(set_wbrf_exclusion_ranges, -EOPNOTSUPP, smu, freq_band_range)
+#define smu_get_ras_smu_drv(smu, ras_smu_drv) smu_ppt_funcs(get_ras_smu_drv, -EOPNOTSUPP, smu, ras_smu_drv)
#endif
#endif
diff --git a/drivers/gpu/drm/amd/ras/Makefile b/drivers/gpu/drm/amd/ras/Makefile
new file mode 100644
index 000000000000..bbdaba811d34
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/Makefile
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
+#
+# 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
+# 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.
+
+ifeq ($(AMD_GPU_RAS_MGR),)
+ AMD_GPU_RAS_MGR := ras_mgr
+endif
+
+subdir-ccflags-y += -I$(AMD_GPU_RAS_FULL_PATH)/rascore
+subdir-ccflags-y += -I$(AMD_GPU_RAS_FULL_PATH)/$(AMD_GPU_RAS_MGR)
+
+RAS_LIBS = $(AMD_GPU_RAS_MGR) rascore
+
+AMD_RAS = $(addsuffix /Makefile, $(addprefix $(AMD_GPU_RAS_FULL_PATH)/,$(RAS_LIBS)))
+
+include $(AMD_RAS)
+
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/Makefile b/drivers/gpu/drm/amd/ras/ras_mgr/Makefile
new file mode 100644
index 000000000000..5e5a2cfa4068
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/Makefile
@@ -0,0 +1,33 @@
+# Copyright 2025 Advanced Micro Devices, 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.
+
+RAS_MGR_FILES = amdgpu_ras_sys.o \
+ amdgpu_ras_mgr.o \
+ amdgpu_ras_eeprom_i2c.o \
+ amdgpu_ras_mp1_v13_0.o \
+ amdgpu_ras_cmd.o \
+ amdgpu_ras_process.o \
+ amdgpu_ras_nbio_v7_9.o
+
+
+RAS_MGR = $(addprefix $(AMD_GPU_RAS_PATH)/ras_mgr/, $(RAS_MGR_FILES))
+
+AMD_GPU_RAS_FILES += $(RAS_MGR)
+
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c
new file mode 100644
index 000000000000..78419b7f7729
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 <linux/pci.h>
+#include "amdgpu.h"
+#include "amdgpu_ras.h"
+#include "ras_sys.h"
+#include "amdgpu_ras_cmd.h"
+#include "amdgpu_ras_mgr.h"
+
+/* inject address is 52 bits */
+#define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52)
+
+#define AMDGPU_RAS_TYPE_RASCORE 0x1
+#define AMDGPU_RAS_TYPE_AMDGPU 0x2
+#define AMDGPU_RAS_TYPE_VF 0x3
+
+static int amdgpu_ras_trigger_error_prepare(struct ras_core_context *ras_core,
+ struct ras_cmd_inject_error_req *block_info)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ int ret;
+
+ if (block_info->block_id == TA_RAS_BLOCK__XGMI_WAFL) {
+ if (amdgpu_dpm_set_df_cstate(adev, DF_CSTATE_DISALLOW))
+ RAS_DEV_WARN(adev, "Failed to disallow df cstate");
+
+ ret = amdgpu_dpm_set_pm_policy(adev, PP_PM_POLICY_XGMI_PLPD, XGMI_PLPD_DISALLOW);
+ if (ret && (ret != -EOPNOTSUPP))
+ RAS_DEV_WARN(adev, "Failed to disallow XGMI power down");
+ }
+
+ return 0;
+}
+
+static int amdgpu_ras_trigger_error_end(struct ras_core_context *ras_core,
+ struct ras_cmd_inject_error_req *block_info)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ int ret;
+
+ if (block_info->block_id == TA_RAS_BLOCK__XGMI_WAFL) {
+ if (amdgpu_ras_intr_triggered())
+ return 0;
+
+ ret = amdgpu_dpm_set_pm_policy(adev, PP_PM_POLICY_XGMI_PLPD, XGMI_PLPD_DEFAULT);
+ if (ret && (ret != -EOPNOTSUPP))
+ RAS_DEV_WARN(adev, "Failed to allow XGMI power down");
+
+ if (amdgpu_dpm_set_df_cstate(adev, DF_CSTATE_ALLOW))
+ RAS_DEV_WARN(adev, "Failed to allow df cstate");
+ }
+
+ return 0;
+}
+
+static uint64_t local_addr_to_xgmi_global_addr(struct ras_core_context *ras_core,
+ uint64_t addr)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct amdgpu_xgmi *xgmi = &adev->gmc.xgmi;
+
+ return (addr + xgmi->physical_node_id * xgmi->node_segment_size);
+}
+
+static int amdgpu_ras_inject_error(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct ras_cmd_inject_error_req *req =
+ (struct ras_cmd_inject_error_req *)cmd->input_buff_raw;
+ int ret = RAS_CMD__ERROR_GENERIC;
+
+ if (req->block_id == RAS_BLOCK_ID__UMC) {
+ if (amdgpu_ras_mgr_check_retired_addr(adev, req->address)) {
+ RAS_DEV_WARN(ras_core->dev,
+ "RAS WARN: inject: 0x%llx has already been marked as bad!\n",
+ req->address);
+ return RAS_CMD__ERROR_ACCESS_DENIED;
+ }
+
+ if ((req->address >= adev->gmc.mc_vram_size &&
+ adev->gmc.mc_vram_size) ||
+ (req->address >= RAS_UMC_INJECT_ADDR_LIMIT)) {
+ RAS_DEV_WARN(adev, "RAS WARN: input address 0x%llx is invalid.",
+ req->address);
+ return RAS_CMD__ERROR_INVALID_INPUT_DATA;
+ }
+
+ /* Calculate XGMI relative offset */
+ if (adev->gmc.xgmi.num_physical_nodes > 1 &&
+ req->block_id != RAS_BLOCK_ID__GFX) {
+ req->address = local_addr_to_xgmi_global_addr(ras_core, req->address);
+ }
+ }
+
+ amdgpu_ras_trigger_error_prepare(ras_core, req);
+ ret = rascore_handle_cmd(ras_core, cmd, data);
+ amdgpu_ras_trigger_error_end(ras_core, req);
+ if (ret) {
+ RAS_DEV_ERR(adev, "ras inject block %u failed %d\n", req->block_id, ret);
+ ret = RAS_CMD__ERROR_ACCESS_DENIED;
+ }
+
+
+ return ret;
+}
+
+static int amdgpu_ras_get_ras_safe_fb_addr_ranges(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct ras_cmd_dev_handle *input_data =
+ (struct ras_cmd_dev_handle *)cmd->input_buff_raw;
+ struct ras_cmd_ras_safe_fb_address_ranges_rsp *ranges =
+ (struct ras_cmd_ras_safe_fb_address_ranges_rsp *)cmd->output_buff_raw;
+ struct amdgpu_mem_partition_info *mem_ranges;
+ uint32_t i = 0;
+
+ if (cmd->input_size != sizeof(*input_data))
+ return RAS_CMD__ERROR_INVALID_INPUT_DATA;
+
+ mem_ranges = adev->gmc.mem_partitions;
+ for (i = 0; i < adev->gmc.num_mem_partitions; i++) {
+ ranges->range[i].start = mem_ranges[i].range.fpfn << AMDGPU_GPU_PAGE_SHIFT;
+ ranges->range[i].size = mem_ranges[i].size;
+ ranges->range[i].idx = i;
+ }
+
+ ranges->num_ranges = adev->gmc.num_mem_partitions;
+
+ ranges->version = 0;
+ cmd->output_size = sizeof(struct ras_cmd_ras_safe_fb_address_ranges_rsp);
+
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_translate_fb_address(struct ras_core_context *ras_core,
+ enum ras_fb_addr_type src_type,
+ enum ras_fb_addr_type dest_type,
+ union ras_translate_fb_address *src_addr,
+ union ras_translate_fb_address *dest_addr)
+{
+ uint64_t soc_phy_addr;
+ int ret = RAS_CMD__SUCCESS;
+
+ /* Does not need to be queued as event as this is a SW translation */
+ switch (src_type) {
+ case RAS_FB_ADDR_SOC_PHY:
+ soc_phy_addr = src_addr->soc_phy_addr;
+ break;
+ case RAS_FB_ADDR_BANK:
+ ret = ras_cmd_translate_bank_to_soc_pa(ras_core,
+ src_addr->bank_addr, &soc_phy_addr);
+ if (ret)
+ return RAS_CMD__ERROR_GENERIC;
+ break;
+ default:
+ return RAS_CMD__ERROR_INVALID_CMD;
+ }
+
+ switch (dest_type) {
+ case RAS_FB_ADDR_SOC_PHY:
+ dest_addr->soc_phy_addr = soc_phy_addr;
+ break;
+ case RAS_FB_ADDR_BANK:
+ ret = ras_cmd_translate_soc_pa_to_bank(ras_core,
+ soc_phy_addr, &dest_addr->bank_addr);
+ if (ret)
+ return RAS_CMD__ERROR_GENERIC;
+ break;
+ default:
+ return RAS_CMD__ERROR_INVALID_CMD;
+ }
+
+ return ret;
+}
+
+static int amdgpu_ras_translate_fb_address(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_translate_fb_address_req *req_buff =
+ (struct ras_cmd_translate_fb_address_req *)cmd->input_buff_raw;
+ struct ras_cmd_translate_fb_address_rsp *rsp_buff =
+ (struct ras_cmd_translate_fb_address_rsp *)cmd->output_buff_raw;
+ int ret = RAS_CMD__ERROR_GENERIC;
+
+ if (cmd->input_size != sizeof(struct ras_cmd_translate_fb_address_req))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ if ((req_buff->src_addr_type >= RAS_FB_ADDR_UNKNOWN) ||
+ (req_buff->dest_addr_type >= RAS_FB_ADDR_UNKNOWN))
+ return RAS_CMD__ERROR_INVALID_INPUT_DATA;
+
+ ret = ras_translate_fb_address(ras_core, req_buff->src_addr_type,
+ req_buff->dest_addr_type, &req_buff->trans_addr, &rsp_buff->trans_addr);
+ if (ret)
+ return RAS_CMD__ERROR_GENERIC;
+
+ rsp_buff->version = 0;
+ cmd->output_size = sizeof(struct ras_cmd_translate_fb_address_rsp);
+
+ return RAS_CMD__SUCCESS;
+}
+
+static struct ras_cmd_func_map amdgpu_ras_cmd_maps[] = {
+ {RAS_CMD__INJECT_ERROR, amdgpu_ras_inject_error},
+ {RAS_CMD__GET_SAFE_FB_ADDRESS_RANGES, amdgpu_ras_get_ras_safe_fb_addr_ranges},
+ {RAS_CMD__TRANSLATE_FB_ADDRESS, amdgpu_ras_translate_fb_address},
+};
+
+int amdgpu_ras_handle_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_func_map *ras_cmd = NULL;
+ int i, res;
+
+ for (i = 0; i < ARRAY_SIZE(amdgpu_ras_cmd_maps); i++) {
+ if (cmd->cmd_id == amdgpu_ras_cmd_maps[i].cmd_id) {
+ ras_cmd = &amdgpu_ras_cmd_maps[i];
+ break;
+ }
+ }
+
+ if (ras_cmd)
+ res = ras_cmd->func(ras_core, cmd, NULL);
+ else
+ res = RAS_CMD__ERROR_UKNOWN_CMD;
+
+ return res;
+}
+
+int amdgpu_ras_submit_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd)
+{
+ struct ras_core_context *cmd_core = ras_core;
+ int timeout = 60;
+ int res;
+
+ cmd->cmd_res = RAS_CMD__ERROR_INVALID_CMD;
+ cmd->output_size = 0;
+
+ if (!ras_core_is_enabled(cmd_core))
+ return RAS_CMD__ERROR_ACCESS_DENIED;
+
+ while (ras_core_gpu_in_reset(cmd_core)) {
+ msleep(1000);
+ if (!timeout--)
+ return RAS_CMD__ERROR_TIMEOUT;
+ }
+
+ res = amdgpu_ras_handle_cmd(cmd_core, cmd, NULL);
+ if (res == RAS_CMD__ERROR_UKNOWN_CMD)
+ res = rascore_handle_cmd(cmd_core, cmd, NULL);
+
+ cmd->cmd_res = res;
+
+ if (cmd->output_size > cmd->output_buf_size) {
+ RAS_DEV_ERR(cmd_core->dev,
+ "Output size 0x%x exceeds output buffer size 0x%x!\n",
+ cmd->output_size, cmd->output_buf_size);
+ return RAS_CMD__SUCCESS_EXEED_BUFFER;
+ }
+
+ return RAS_CMD__SUCCESS;
+}
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.h
new file mode 100644
index 000000000000..5973b156cc85
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ */
+
+#ifndef __AMDGPU_RAS_CMD_H__
+#define __AMDGPU_RAS_CMD_H__
+#include "ras.h"
+
+enum amdgpu_ras_cmd_id {
+ RAS_CMD__AMDGPU_BEGIN = RAS_CMD_ID_AMDGPU_START,
+ RAS_CMD__TRANSLATE_MEMORY_FD,
+ RAS_CMD__AMDGPU_SUPPORTED_MAX = RAS_CMD_ID_AMDGPU_END,
+};
+
+struct ras_cmd_translate_memory_fd_req {
+ struct ras_cmd_dev_handle dev;
+ uint32_t type;
+ uint32_t fd;
+ uint64_t address;
+ uint32_t reserved[4];
+};
+
+struct ras_cmd_translate_memory_fd_rsp {
+ uint32_t version;
+ uint32_t padding;
+ uint64_t start;
+ uint64_t size;
+ uint32_t reserved[2];
+};
+
+int amdgpu_ras_handle_cmd(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data);
+int amdgpu_ras_submit_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd);
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c
new file mode 100644
index 000000000000..3ed3ff42b7e1
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * 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
+ * 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.
+ */
+
+#include "amdgpu.h"
+#include "amdgpu_atomfirmware.h"
+#include "amdgpu_ras_eeprom.h"
+#include "amdgpu_ras_mgr.h"
+#include "amdgpu_ras_eeprom_i2c.h"
+#include "ras_eeprom.h"
+
+/* These are memory addresses as would be seen by one or more EEPROM
+ * chips strung on the I2C bus, usually by manipulating pins 1-3 of a
+ * set of EEPROM devices. They form a continuous memory space.
+ *
+ * The I2C device address includes the device type identifier, 1010b,
+ * which is a reserved value and indicates that this is an I2C EEPROM
+ * device. It also includes the top 3 bits of the 19 bit EEPROM memory
+ * address, namely bits 18, 17, and 16. This makes up the 7 bit
+ * address sent on the I2C bus with bit 0 being the direction bit,
+ * which is not represented here, and sent by the hardware directly.
+ *
+ * For instance,
+ * 50h = 1010000b => device type identifier 1010b, bits 18:16 = 000b, address 0.
+ * 54h = 1010100b => --"--, bits 18:16 = 100b, address 40000h.
+ * 56h = 1010110b => --"--, bits 18:16 = 110b, address 60000h.
+ * Depending on the size of the I2C EEPROM device(s), bits 18:16 may
+ * address memory in a device or a device on the I2C bus, depending on
+ * the status of pins 1-3. See top of amdgpu_eeprom.c.
+ *
+ * The RAS table lives either at address 0 or address 40000h of EEPROM.
+ */
+#define EEPROM_I2C_MADDR_0 0x0
+#define EEPROM_I2C_MADDR_4 0x40000
+
+#define MAKE_I2C_ADDR(_aa) ((0xA << 3) | (((_aa) >> 16) & 0xF))
+#define to_amdgpu_ras(x) (container_of(x, struct amdgpu_ras, eeprom_control))
+
+#define EEPROM_PAGE_BITS 8
+#define EEPROM_PAGE_SIZE (1U << EEPROM_PAGE_BITS)
+#define EEPROM_PAGE_MASK (EEPROM_PAGE_SIZE - 1)
+
+#define EEPROM_OFFSET_SIZE 2
+
+static int ras_eeprom_i2c_config(struct ras_core_context *ras_core)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ u8 i2c_addr;
+
+ if (amdgpu_atomfirmware_ras_rom_addr(adev, &i2c_addr)) {
+ /* The address given by VBIOS is an 8-bit, wire-format
+ * address, i.e. the most significant byte.
+ *
+ * Normalize it to a 19-bit EEPROM address. Remove the
+ * device type identifier and make it a 7-bit address;
+ * then make it a 19-bit EEPROM address. See top of
+ * amdgpu_eeprom.c.
+ */
+ i2c_addr = (i2c_addr & 0x0F) >> 1;
+ control->i2c_address = ((u32) i2c_addr) << 16;
+ return 0;
+ }
+
+ switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
+ case IP_VERSION(13, 0, 5):
+ case IP_VERSION(13, 0, 6):
+ case IP_VERSION(13, 0, 10):
+ case IP_VERSION(13, 0, 12):
+ case IP_VERSION(13, 0, 14):
+ control->i2c_address = EEPROM_I2C_MADDR_4;
+ return 0;
+ default:
+ return -ENODATA;
+ }
+ return -ENODATA;
+}
+
+static int ras_eeprom_i2c_xfer(struct ras_core_context *ras_core, u32 eeprom_addr,
+ u8 *eeprom_buf, u32 buf_size, bool read)
+{
+ struct i2c_adapter *i2c_adap = ras_core->ras_eeprom.i2c_adapter;
+ u8 eeprom_offset_buf[EEPROM_OFFSET_SIZE];
+ struct i2c_msg msgs[] = {
+ {
+ .flags = 0,
+ .len = EEPROM_OFFSET_SIZE,
+ .buf = eeprom_offset_buf,
+ },
+ {
+ .flags = read ? I2C_M_RD : 0,
+ },
+ };
+ const u8 *p = eeprom_buf;
+ int r;
+ u16 len;
+
+ for (r = 0; buf_size > 0;
+ buf_size -= len, eeprom_addr += len, eeprom_buf += len) {
+ /* Set the EEPROM address we want to write to/read from.
+ */
+ msgs[0].addr = MAKE_I2C_ADDR(eeprom_addr);
+ msgs[1].addr = msgs[0].addr;
+ msgs[0].buf[0] = (eeprom_addr >> 8) & 0xff;
+ msgs[0].buf[1] = eeprom_addr & 0xff;
+
+ if (!read) {
+ /* Write the maximum amount of data, without
+ * crossing the device's page boundary, as per
+ * its spec. Partial page writes are allowed,
+ * starting at any location within the page,
+ * so long as the page boundary isn't crossed
+ * over (actually the page pointer rolls
+ * over).
+ *
+ * As per the AT24CM02 EEPROM spec, after
+ * writing into a page, the I2C driver should
+ * terminate the transfer, i.e. in
+ * "i2c_transfer()" below, with a STOP
+ * condition, so that the self-timed write
+ * cycle begins. This is implied for the
+ * "i2c_transfer()" abstraction.
+ */
+ len = min(EEPROM_PAGE_SIZE - (eeprom_addr & EEPROM_PAGE_MASK),
+ buf_size);
+ } else {
+ /* Reading from the EEPROM has no limitation
+ * on the number of bytes read from the EEPROM
+ * device--they are simply sequenced out.
+ * Keep in mind that i2c_msg.len is u16 type.
+ */
+ len = min(U16_MAX, buf_size);
+ }
+ msgs[1].len = len;
+ msgs[1].buf = eeprom_buf;
+
+
+ /* This constitutes a START-STOP transaction.
+ */
+ r = i2c_transfer(i2c_adap, msgs, ARRAY_SIZE(msgs));
+ if (r != ARRAY_SIZE(msgs))
+ break;
+
+ if (!read) {
+ /* According to EEPROM specs the length of the
+ * self-writing cycle, tWR (tW), is 10 ms.
+ *
+ * TODO: Use polling on ACK, aka Acknowledge
+ * Polling, to minimize waiting for the
+ * internal write cycle to complete, as it is
+ * usually smaller than tWR (tW).
+ */
+ msleep(10);
+ }
+ }
+
+ return r < 0 ? r : eeprom_buf - p;
+}
+
+const struct ras_eeprom_sys_func amdgpu_ras_eeprom_i2c_sys_func = {
+ .eeprom_i2c_xfer = ras_eeprom_i2c_xfer,
+ .update_eeprom_i2c_config = ras_eeprom_i2c_config,
+};
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.h
new file mode 100644
index 000000000000..3b5878605411
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, 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) 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.
+ */
+#ifndef __AMDGPU_RAS_EEPROM_I2C_H__
+#define __AMDGPU_RAS_EEPROM_I2C_H__
+#include "ras.h"
+
+extern const struct ras_eeprom_sys_func amdgpu_ras_eeprom_i2c_sys_func;
+#endif
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c
new file mode 100644
index 000000000000..afe8135b6258
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c
@@ -0,0 +1,648 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "amdgpu.h"
+#include "amdgpu_reset.h"
+#include "amdgpu_xgmi.h"
+#include "ras_sys.h"
+#include "amdgpu_ras_mgr.h"
+#include "amdgpu_ras_cmd.h"
+#include "amdgpu_ras_process.h"
+#include "amdgpu_ras_eeprom_i2c.h"
+#include "amdgpu_ras_mp1_v13_0.h"
+#include "amdgpu_ras_nbio_v7_9.h"
+
+#define MAX_SOCKET_NUM_PER_HIVE 8
+#define MAX_AID_NUM_PER_SOCKET 4
+#define MAX_XCD_NUM_PER_AID 2
+
+/* typical ECC bad page rate is 1 bad page per 100MB VRAM */
+#define TYPICAL_ECC_BAD_PAGE_RATE (100ULL * SZ_1M)
+
+#define COUNT_BAD_PAGE_THRESHOLD(size) (((size) >> 21) << 4)
+
+/* Reserve 8 physical dram row for possible retirement.
+ * In worst cases, it will lose 8 * 2MB memory in vram domain
+ */
+#define RAS_RESERVED_VRAM_SIZE_DEFAULT (16ULL << 20)
+
+
+static void ras_mgr_init_event_mgr(struct ras_event_manager *mgr)
+{
+ struct ras_event_state *event_state;
+ int i;
+
+ memset(mgr, 0, sizeof(*mgr));
+ atomic64_set(&mgr->seqno, 0);
+
+ for (i = 0; i < ARRAY_SIZE(mgr->event_state); i++) {
+ event_state = &mgr->event_state[i];
+ event_state->last_seqno = RAS_EVENT_INVALID_ID;
+ atomic64_set(&event_state->count, 0);
+ }
+}
+
+static void amdgpu_ras_mgr_init_event_mgr(struct ras_core_context *ras_core)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ struct ras_event_manager *event_mgr;
+ struct amdgpu_hive_info *hive;
+
+ hive = amdgpu_get_xgmi_hive(adev);
+ event_mgr = hive ? &hive->event_mgr : &ras_mgr->ras_event_mgr;
+
+ /* init event manager with node 0 on xgmi system */
+ if (!amdgpu_reset_in_recovery(adev)) {
+ if (!hive || adev->gmc.xgmi.node_id == 0)
+ ras_mgr_init_event_mgr(event_mgr);
+ }
+
+ if (hive)
+ amdgpu_put_xgmi_hive(hive);
+}
+
+static int amdgpu_ras_mgr_init_aca_config(struct amdgpu_device *adev,
+ struct ras_core_config *config)
+{
+ struct ras_aca_config *aca_cfg = &config->aca_cfg;
+
+ aca_cfg->socket_num_per_hive = MAX_SOCKET_NUM_PER_HIVE;
+ aca_cfg->aid_num_per_socket = MAX_AID_NUM_PER_SOCKET;
+ aca_cfg->xcd_num_per_aid = MAX_XCD_NUM_PER_AID;
+
+ return 0;
+}
+
+static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev,
+ struct ras_core_config *config)
+{
+ struct ras_eeprom_config *eeprom_cfg = &config->eeprom_cfg;
+
+ eeprom_cfg->eeprom_sys_fn = &amdgpu_ras_eeprom_i2c_sys_func;
+ eeprom_cfg->eeprom_i2c_adapter = adev->pm.ras_eeprom_i2c_bus;
+ if (eeprom_cfg->eeprom_i2c_adapter) {
+ const struct i2c_adapter_quirks *quirks =
+ ((struct i2c_adapter *)eeprom_cfg->eeprom_i2c_adapter)->quirks;
+
+ if (quirks) {
+ eeprom_cfg->max_i2c_read_len = quirks->max_read_len;
+ eeprom_cfg->max_i2c_write_len = quirks->max_write_len;
+ }
+ }
+
+ /*
+ * amdgpu_bad_page_threshold is used to config
+ * the threshold for the number of bad pages.
+ * -1: Threshold is set to default value
+ * Driver will issue a warning message when threshold is reached
+ * and continue runtime services.
+ * 0: Disable bad page retirement
+ * Driver will not retire bad pages
+ * which is intended for debugging purpose.
+ * -2: Threshold is determined by a formula
+ * that assumes 1 bad page per 100M of local memory.
+ * Driver will continue runtime services when threhold is reached.
+ * 0 < threshold < max number of bad page records in EEPROM,
+ * A user-defined threshold is set
+ * Driver will halt runtime services when this custom threshold is reached.
+ */
+ if (amdgpu_bad_page_threshold == NONSTOP_OVER_THRESHOLD)
+ eeprom_cfg->eeprom_record_threshold_count =
+ div64_u64(adev->gmc.mc_vram_size, TYPICAL_ECC_BAD_PAGE_RATE);
+ else if (amdgpu_bad_page_threshold == WARN_NONSTOP_OVER_THRESHOLD)
+ eeprom_cfg->eeprom_record_threshold_count =
+ COUNT_BAD_PAGE_THRESHOLD(RAS_RESERVED_VRAM_SIZE_DEFAULT);
+ else
+ eeprom_cfg->eeprom_record_threshold_count = amdgpu_bad_page_threshold;
+
+ eeprom_cfg->eeprom_record_threshold_config = amdgpu_bad_page_threshold;
+
+ return 0;
+}
+
+static int amdgpu_ras_mgr_init_mp1_config(struct amdgpu_device *adev,
+ struct ras_core_config *config)
+{
+ struct ras_mp1_config *mp1_cfg = &config->mp1_cfg;
+ int ret = 0;
+
+ switch (config->mp1_ip_version) {
+ case IP_VERSION(13, 0, 6):
+ case IP_VERSION(13, 0, 14):
+ case IP_VERSION(13, 0, 12):
+ mp1_cfg->mp1_sys_fn = &amdgpu_ras_mp1_sys_func_v13_0;
+ break;
+ default:
+ RAS_DEV_ERR(adev,
+ "The mp1(0x%x) ras config is not right!\n",
+ config->mp1_ip_version);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int amdgpu_ras_mgr_init_nbio_config(struct amdgpu_device *adev,
+ struct ras_core_config *config)
+{
+ struct ras_nbio_config *nbio_cfg = &config->nbio_cfg;
+ int ret = 0;
+
+ switch (config->nbio_ip_version) {
+ case IP_VERSION(7, 9, 0):
+ case IP_VERSION(7, 9, 1):
+ nbio_cfg->nbio_sys_fn = &amdgpu_ras_nbio_sys_func_v7_9;
+ break;
+ default:
+ RAS_DEV_ERR(adev,
+ "The nbio(0x%x) ras config is not right!\n",
+ config->nbio_ip_version);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int amdgpu_ras_mgr_get_ras_psp_system_status(struct ras_core_context *ras_core,
+ struct ras_psp_sys_status *status)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct ta_context *context = &adev->psp.ras_context.context;
+
+ status->initialized = context->initialized;
+ status->session_id = context->session_id;
+ status->psp_cmd_mutex = &adev->psp.mutex;
+
+ return 0;
+}
+
+static int amdgpu_ras_mgr_get_ras_ta_init_param(struct ras_core_context *ras_core,
+ struct ras_ta_init_param *ras_ta_param)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ uint32_t nps_mode;
+
+ if (amdgpu_ras_is_poison_mode_supported(adev))
+ ras_ta_param->poison_mode_en = 1;
+
+ if (!adev->gmc.xgmi.connected_to_cpu && !adev->gmc.is_app_apu)
+ ras_ta_param->dgpu_mode = 1;
+
+ ras_ta_param->xcc_mask = adev->gfx.xcc_mask;
+ ras_ta_param->channel_dis_num = hweight32(adev->gmc.m_half_use) * 2;
+
+ ras_ta_param->active_umc_mask = adev->umc.active_mask;
+
+ if (!amdgpu_ras_mgr_get_curr_nps_mode(adev, &nps_mode))
+ ras_ta_param->nps_mode = nps_mode;
+
+ return 0;
+}
+
+const struct ras_psp_sys_func amdgpu_ras_psp_sys_func = {
+ .get_ras_psp_system_status = amdgpu_ras_mgr_get_ras_psp_system_status,
+ .get_ras_ta_init_param = amdgpu_ras_mgr_get_ras_ta_init_param,
+};
+
+static int amdgpu_ras_mgr_init_psp_config(struct amdgpu_device *adev,
+ struct ras_core_config *config)
+{
+ struct ras_psp_config *psp_cfg = &config->psp_cfg;
+
+ psp_cfg->psp_sys_fn = &amdgpu_ras_psp_sys_func;
+
+ return 0;
+}
+
+static int amdgpu_ras_mgr_init_umc_config(struct amdgpu_device *adev,
+ struct ras_core_config *config)
+{
+ struct ras_umc_config *umc_cfg = &config->umc_cfg;
+
+ umc_cfg->umc_vram_type = adev->gmc.vram_type;
+
+ return 0;
+}
+
+static struct ras_core_context *amdgpu_ras_mgr_create_ras_core(struct amdgpu_device *adev)
+{
+ struct ras_core_config init_config;
+
+ memset(&init_config, 0, sizeof(init_config));
+
+ init_config.umc_ip_version = amdgpu_ip_version(adev, UMC_HWIP, 0);
+ init_config.mp1_ip_version = amdgpu_ip_version(adev, MP1_HWIP, 0);
+ init_config.gfx_ip_version = amdgpu_ip_version(adev, GC_HWIP, 0);
+ init_config.nbio_ip_version = amdgpu_ip_version(adev, NBIO_HWIP, 0);
+ init_config.psp_ip_version = amdgpu_ip_version(adev, MP1_HWIP, 0);
+
+ if (init_config.umc_ip_version == IP_VERSION(12, 0, 0) ||
+ init_config.umc_ip_version == IP_VERSION(12, 5, 0))
+ init_config.aca_ip_version = IP_VERSION(1, 0, 0);
+
+ init_config.sys_fn = &amdgpu_ras_sys_fn;
+ init_config.ras_eeprom_supported = true;
+ init_config.poison_supported =
+ amdgpu_ras_is_poison_mode_supported(adev);
+
+ amdgpu_ras_mgr_init_aca_config(adev, &init_config);
+ amdgpu_ras_mgr_init_eeprom_config(adev, &init_config);
+ amdgpu_ras_mgr_init_mp1_config(adev, &init_config);
+ amdgpu_ras_mgr_init_nbio_config(adev, &init_config);
+ amdgpu_ras_mgr_init_psp_config(adev, &init_config);
+ amdgpu_ras_mgr_init_umc_config(adev, &init_config);
+
+ return ras_core_create(&init_config);
+}
+
+static int amdgpu_ras_mgr_sw_init(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ struct amdgpu_ras_mgr *ras_mgr;
+ int ret = 0;
+
+ /* Disabled by default */
+ con->uniras_enabled = false;
+
+ /* Enabled only in debug mode */
+ if (adev->debug_enable_ras_aca) {
+ con->uniras_enabled = true;
+ RAS_DEV_INFO(adev, "Debug amdgpu uniras!");
+ }
+
+ if (!con->uniras_enabled)
+ return 0;
+
+ ras_mgr = kzalloc(sizeof(*ras_mgr), GFP_KERNEL);
+ if (!ras_mgr)
+ return -EINVAL;
+
+ con->ras_mgr = ras_mgr;
+ ras_mgr->adev = adev;
+
+ ras_mgr->ras_core = amdgpu_ras_mgr_create_ras_core(adev);
+ if (!ras_mgr->ras_core) {
+ RAS_DEV_ERR(adev, "Failed to create ras core!\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ras_mgr->ras_core->dev = adev;
+
+ amdgpu_ras_process_init(adev);
+ ras_core_sw_init(ras_mgr->ras_core);
+ amdgpu_ras_mgr_init_event_mgr(ras_mgr->ras_core);
+ return 0;
+
+err:
+ kfree(ras_mgr);
+ return ret;
+}
+
+static int amdgpu_ras_mgr_sw_fini(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ struct amdgpu_ras_mgr *ras_mgr = (struct amdgpu_ras_mgr *)con->ras_mgr;
+
+ if (!con->uniras_enabled)
+ return 0;
+
+ if (!ras_mgr)
+ return 0;
+
+ amdgpu_ras_process_fini(adev);
+ ras_core_sw_fini(ras_mgr->ras_core);
+ ras_core_destroy(ras_mgr->ras_core);
+ ras_mgr->ras_core = NULL;
+
+ kfree(con->ras_mgr);
+ con->ras_mgr = NULL;
+
+ return 0;
+}
+
+static int amdgpu_ras_mgr_hw_init(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ int ret;
+
+ if (!con->uniras_enabled)
+ return 0;
+
+ if (!ras_mgr || !ras_mgr->ras_core)
+ return -EINVAL;
+
+ ret = ras_core_hw_init(ras_mgr->ras_core);
+ if (ret) {
+ RAS_DEV_ERR(adev, "Failed to initialize ras core!\n");
+ return ret;
+ }
+
+ ras_mgr->ras_is_ready = true;
+
+ amdgpu_enable_uniras(adev, true);
+
+ RAS_DEV_INFO(adev, "AMDGPU RAS Is Ready.\n");
+ return 0;
+}
+
+static int amdgpu_ras_mgr_hw_fini(struct amdgpu_ip_block *ip_block)
+{
+ struct amdgpu_device *adev = ip_block->adev;
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!con->uniras_enabled)
+ return 0;
+
+ if (!ras_mgr || !ras_mgr->ras_core)
+ return -EINVAL;
+
+ ras_core_hw_fini(ras_mgr->ras_core);
+
+ ras_mgr->ras_is_ready = false;
+
+ return 0;
+}
+
+struct amdgpu_ras_mgr *amdgpu_ras_mgr_get_context(struct amdgpu_device *adev)
+{
+ if (!adev || !adev->psp.ras_context.ras)
+ return NULL;
+
+ return (struct amdgpu_ras_mgr *)adev->psp.ras_context.ras->ras_mgr;
+}
+
+static const struct amd_ip_funcs __maybe_unused ras_v1_0_ip_funcs = {
+ .name = "ras_v1_0",
+ .sw_init = amdgpu_ras_mgr_sw_init,
+ .sw_fini = amdgpu_ras_mgr_sw_fini,
+ .hw_init = amdgpu_ras_mgr_hw_init,
+ .hw_fini = amdgpu_ras_mgr_hw_fini,
+};
+
+const struct amdgpu_ip_block_version ras_v1_0_ip_block = {
+ .type = AMD_IP_BLOCK_TYPE_RAS,
+ .major = 1,
+ .minor = 0,
+ .rev = 0,
+ .funcs = &ras_v1_0_ip_funcs,
+};
+
+int amdgpu_enable_uniras(struct amdgpu_device *adev, bool enable)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!ras_mgr || !ras_mgr->ras_core)
+ return -EPERM;
+
+ if (amdgpu_sriov_vf(adev))
+ return -EPERM;
+
+ RAS_DEV_INFO(adev, "Enable amdgpu unified ras!");
+ return ras_core_set_status(ras_mgr->ras_core, enable);
+}
+
+bool amdgpu_uniras_enabled(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!ras_mgr || !ras_mgr->ras_core)
+ return false;
+
+ if (amdgpu_sriov_vf(adev))
+ return false;
+
+ return ras_core_is_enabled(ras_mgr->ras_core);
+}
+
+static bool amdgpu_ras_mgr_is_ready(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (ras_mgr && ras_mgr->ras_core && ras_mgr->ras_is_ready &&
+ ras_core_is_ready(ras_mgr->ras_core))
+ return true;
+
+ return false;
+}
+
+int amdgpu_ras_mgr_handle_fatal_interrupt(struct amdgpu_device *adev, void *data)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return -EPERM;
+
+ return ras_core_handle_nbio_irq(ras_mgr->ras_core, data);
+}
+
+uint64_t amdgpu_ras_mgr_gen_ras_event_seqno(struct amdgpu_device *adev,
+ enum ras_seqno_type seqno_type)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ int ret;
+ uint64_t seq_no;
+
+ if (!amdgpu_ras_mgr_is_ready(adev) ||
+ (seqno_type >= RAS_SEQNO_TYPE_COUNT_MAX))
+ return 0;
+
+ seq_no = ras_core_gen_seqno(ras_mgr->ras_core, seqno_type);
+
+ if ((seqno_type == RAS_SEQNO_TYPE_DE) ||
+ (seqno_type == RAS_SEQNO_TYPE_POISON_CONSUMPTION)) {
+ ret = ras_core_put_seqno(ras_mgr->ras_core, seqno_type, seq_no);
+ if (ret)
+ RAS_DEV_WARN(adev, "There are too many ras interrupts!");
+ }
+
+ return seq_no;
+}
+
+int amdgpu_ras_mgr_handle_controller_interrupt(struct amdgpu_device *adev, void *data)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ struct ras_ih_info *ih_info = (struct ras_ih_info *)data;
+ uint64_t seq_no = 0;
+ int ret = 0;
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return -EPERM;
+
+ if (ih_info && (ih_info->block == AMDGPU_RAS_BLOCK__UMC)) {
+ if (ras_mgr->ras_core->poison_supported) {
+ seq_no = amdgpu_ras_mgr_gen_ras_event_seqno(adev, RAS_SEQNO_TYPE_DE);
+ RAS_DEV_INFO(adev,
+ "{%llu} RAS poison is created, no user action is needed.\n",
+ seq_no);
+ }
+
+ ret = amdgpu_ras_process_handle_umc_interrupt(adev, ih_info);
+ } else if (ras_mgr->ras_core->poison_supported) {
+ ret = amdgpu_ras_process_handle_unexpected_interrupt(adev, ih_info);
+ } else {
+ RAS_DEV_WARN(adev,
+ "No RAS interrupt handler for non-UMC block with poison disabled.\n");
+ }
+
+ return ret;
+}
+
+int amdgpu_ras_mgr_handle_consumer_interrupt(struct amdgpu_device *adev, void *data)
+{
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return -EPERM;
+
+ return amdgpu_ras_process_handle_consumption_interrupt(adev, data);
+}
+
+int amdgpu_ras_mgr_update_ras_ecc(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return -EPERM;
+
+ return ras_core_update_ecc_info(ras_mgr->ras_core);
+}
+
+int amdgpu_ras_mgr_reset_gpu(struct amdgpu_device *adev, uint32_t flags)
+{
+ struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return -EPERM;
+
+ con->gpu_reset_flags |= flags;
+ return amdgpu_ras_reset_gpu(adev);
+}
+
+bool amdgpu_ras_mgr_check_eeprom_safety_watermark(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return false;
+
+ return ras_eeprom_check_safety_watermark(ras_mgr->ras_core);
+}
+
+int amdgpu_ras_mgr_get_curr_nps_mode(struct amdgpu_device *adev,
+ uint32_t *nps_mode)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ uint32_t mode;
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return -EINVAL;
+
+ mode = ras_core_get_curr_nps_mode(ras_mgr->ras_core);
+ if (!mode || mode > AMDGPU_NPS8_PARTITION_MODE)
+ return -EINVAL;
+
+ *nps_mode = mode;
+
+ return 0;
+}
+
+bool amdgpu_ras_mgr_check_retired_addr(struct amdgpu_device *adev,
+ uint64_t addr)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return false;
+
+ return ras_umc_check_retired_addr(ras_mgr->ras_core, addr);
+}
+
+bool amdgpu_ras_mgr_is_rma(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!ras_mgr || !ras_mgr->ras_core || !ras_mgr->ras_is_ready)
+ return false;
+
+ return ras_core_gpu_is_rma(ras_mgr->ras_core);
+}
+
+int amdgpu_ras_mgr_handle_ras_cmd(struct amdgpu_device *adev,
+ uint32_t cmd_id, void *input, uint32_t input_size,
+ void *output, uint32_t out_size)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ struct ras_cmd_ctx *cmd_ctx;
+ uint32_t ctx_buf_size = PAGE_SIZE;
+ int ret;
+
+ if (!amdgpu_ras_mgr_is_ready(adev))
+ return -EPERM;
+
+ cmd_ctx = kzalloc(ctx_buf_size, GFP_KERNEL);
+ if (!cmd_ctx)
+ return -ENOMEM;
+
+ cmd_ctx->cmd_id = cmd_id;
+
+ memcpy(cmd_ctx->input_buff_raw, input, input_size);
+ cmd_ctx->input_size = input_size;
+ cmd_ctx->output_buf_size = ctx_buf_size - sizeof(*cmd_ctx);
+
+ ret = amdgpu_ras_submit_cmd(ras_mgr->ras_core, cmd_ctx);
+ if (!ret && !cmd_ctx->cmd_res && output && (out_size == cmd_ctx->output_size))
+ memcpy(output, cmd_ctx->output_buff_raw, cmd_ctx->output_size);
+
+ kfree(cmd_ctx);
+
+ return ret;
+}
+
+int amdgpu_ras_mgr_pre_reset(struct amdgpu_device *adev)
+{
+ if (!amdgpu_ras_mgr_is_ready(adev)) {
+ RAS_DEV_ERR(adev, "Invalid ras suspend!\n");
+ return -EPERM;
+ }
+
+ amdgpu_ras_process_pre_reset(adev);
+ return 0;
+}
+
+int amdgpu_ras_mgr_post_reset(struct amdgpu_device *adev)
+{
+ if (!amdgpu_ras_mgr_is_ready(adev)) {
+ RAS_DEV_ERR(adev, "Invalid ras resume!\n");
+ return -EPERM;
+ }
+
+ amdgpu_ras_process_post_reset(adev);
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h
new file mode 100644
index 000000000000..8fb7eb4b8f13
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2025 Advanced Micro Devices, 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
+ * 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.
+ */
+#ifndef __AMDGPU_RAS_MGR_H__
+#define __AMDGPU_RAS_MGR_H__
+#include "ras.h"
+#include "amdgpu_ras_process.h"
+
+enum ras_ih_type {
+ RAS_IH_NONE,
+ RAS_IH_FROM_BLOCK_CONTROLLER,
+ RAS_IH_FROM_CONSUMER_CLIENT,
+ RAS_IH_FROM_FATAL_ERROR,
+};
+
+struct ras_ih_info {
+ uint32_t block;
+ union {
+ struct amdgpu_iv_entry iv_entry;
+ struct {
+ uint16_t pasid;
+ uint32_t reset;
+ pasid_notify pasid_fn;
+ void *data;
+ };
+ };
+};
+
+struct amdgpu_ras_mgr {
+ struct amdgpu_device *adev;
+ struct ras_core_context *ras_core;
+ struct delayed_work retire_page_dwork;
+ struct ras_event_manager ras_event_mgr;
+ uint64_t last_poison_consumption_seqno;
+ bool ras_is_ready;
+
+ bool is_paused;
+ struct completion ras_event_done;
+};
+
+extern const struct amdgpu_ip_block_version ras_v1_0_ip_block;
+
+struct amdgpu_ras_mgr *amdgpu_ras_mgr_get_context(
+ struct amdgpu_device *adev);
+int amdgpu_enable_uniras(struct amdgpu_device *adev, bool enable);
+bool amdgpu_uniras_enabled(struct amdgpu_device *adev);
+int amdgpu_ras_mgr_handle_fatal_interrupt(struct amdgpu_device *adev, void *data);
+int amdgpu_ras_mgr_handle_controller_interrupt(struct amdgpu_device *adev, void *data);
+int amdgpu_ras_mgr_handle_consumer_interrupt(struct amdgpu_device *adev, void *data);
+int amdgpu_ras_mgr_update_ras_ecc(struct amdgpu_device *adev);
+int amdgpu_ras_mgr_reset_gpu(struct amdgpu_device *adev, uint32_t flags);
+uint64_t amdgpu_ras_mgr_gen_ras_event_seqno(struct amdgpu_device *adev,
+ enum ras_seqno_type seqno_type);
+bool amdgpu_ras_mgr_check_eeprom_safety_watermark(struct amdgpu_device *adev);
+int amdgpu_ras_mgr_get_curr_nps_mode(struct amdgpu_device *adev, uint32_t *nps_mode);
+bool amdgpu_ras_mgr_check_retired_addr(struct amdgpu_device *adev,
+ uint64_t addr);
+bool amdgpu_ras_mgr_is_rma(struct amdgpu_device *adev);
+int amdgpu_ras_mgr_handle_ras_cmd(struct amdgpu_device *adev,
+ uint32_t cmd_id, void *input, uint32_t input_size,
+ void *output, uint32_t out_size);
+int amdgpu_ras_mgr_pre_reset(struct amdgpu_device *adev);
+int amdgpu_ras_mgr_post_reset(struct amdgpu_device *adev);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c
new file mode 100644
index 000000000000..79a51b1603ac
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "amdgpu_smu.h"
+#include "amdgpu_reset.h"
+#include "amdgpu_ras_mp1_v13_0.h"
+
+#define RAS_MP1_MSG_QueryValidMcaCeCount 0x3A
+#define RAS_MP1_MSG_McaBankCeDumpDW 0x3B
+
+static int mp1_v13_0_get_valid_bank_count(struct ras_core_context *ras_core,
+ u32 msg, u32 *count)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ u32 smu_msg;
+ int ret = 0;
+
+ if (!count)
+ return -EINVAL;
+
+ smu_msg = (msg == RAS_MP1_MSG_QueryValidMcaCeCount) ?
+ SMU_MSG_QueryValidMcaCeCount : SMU_MSG_QueryValidMcaCount;
+
+ if (down_read_trylock(&adev->reset_domain->sem)) {
+ ret = amdgpu_smu_ras_send_msg(adev, smu_msg, 0, count);
+ up_read(&adev->reset_domain->sem);
+ } else {
+ ret = -RAS_CORE_GPU_IN_MODE1_RESET;
+ }
+
+ if (ret)
+ *count = 0;
+
+ return ret;
+}
+
+static int mp1_v13_0_dump_valid_bank(struct ras_core_context *ras_core,
+ u32 msg, u32 idx, u32 reg_idx, u64 *val)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ uint32_t data[2] = {0, 0};
+ uint32_t param;
+ int ret = 0;
+ int i, offset;
+ u32 smu_msg = (msg == RAS_MP1_MSG_McaBankCeDumpDW) ?
+ SMU_MSG_McaBankCeDumpDW : SMU_MSG_McaBankDumpDW;
+
+ if (down_read_trylock(&adev->reset_domain->sem)) {
+ offset = reg_idx * 8;
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ param = ((idx & 0xffff) << 16) | ((offset + (i << 2)) & 0xfffc);
+ ret = amdgpu_smu_ras_send_msg(adev, smu_msg, param, &data[i]);
+ if (ret) {
+ RAS_DEV_ERR(adev, "ACA failed to read register[%d], offset:0x%x\n",
+ reg_idx, offset);
+ break;
+ }
+ }
+ up_read(&adev->reset_domain->sem);
+
+ if (!ret)
+ *val = (uint64_t)data[1] << 32 | data[0];
+ } else {
+ ret = -RAS_CORE_GPU_IN_MODE1_RESET;
+ }
+
+ return ret;
+}
+
+const struct ras_mp1_sys_func amdgpu_ras_mp1_sys_func_v13_0 = {
+ .mp1_get_valid_bank_count = mp1_v13_0_get_valid_bank_count,
+ .mp1_dump_valid_bank = mp1_v13_0_dump_valid_bank,
+};
+
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.h
new file mode 100644
index 000000000000..71c614ae1ae4
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ */
+
+#ifndef __AMDGPU_RAS_MP1_V13_0_H__
+#define __AMDGPU_RAS_MP1_V13_0_H__
+#include "ras.h"
+
+extern const struct ras_mp1_sys_func amdgpu_ras_mp1_sys_func_v13_0;
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.c
new file mode 100644
index 000000000000..2783f5875c7c
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "amdgpu_ras_mgr.h"
+#include "amdgpu_ras_nbio_v7_9.h"
+#include "nbio/nbio_7_9_0_offset.h"
+#include "nbio/nbio_7_9_0_sh_mask.h"
+#include "ivsrcid/nbio/irqsrcs_nbif_7_4.h"
+
+static int nbio_v7_9_set_ras_controller_irq_state(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *src,
+ unsigned int type,
+ enum amdgpu_interrupt_state state)
+{
+ /* Dummy function, there is no initialization operation in driver */
+
+ return 0;
+}
+
+static int nbio_v7_9_process_ras_controller_irq(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry)
+{
+ /* By design, the ih cookie for ras_controller_irq should be written
+ * to BIFring instead of general iv ring. However, due to known bif ring
+ * hw bug, it has to be disabled. There is no chance the process function
+ * will be involked. Just left it as a dummy one.
+ */
+ return 0;
+}
+
+static int nbio_v7_9_set_ras_err_event_athub_irq_state(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *src,
+ unsigned int type,
+ enum amdgpu_interrupt_state state)
+{
+ /* Dummy function, there is no initialization operation in driver */
+
+ return 0;
+}
+
+static int nbio_v7_9_process_err_event_athub_irq(struct amdgpu_device *adev,
+ struct amdgpu_irq_src *source,
+ struct amdgpu_iv_entry *entry)
+{
+ /* By design, the ih cookie for err_event_athub_irq should be written
+ * to BIFring instead of general iv ring. However, due to known bif ring
+ * hw bug, it has to be disabled. There is no chance the process function
+ * will be involked. Just left it as a dummy one.
+ */
+ return 0;
+}
+
+static const struct amdgpu_irq_src_funcs nbio_v7_9_ras_controller_irq_funcs = {
+ .set = nbio_v7_9_set_ras_controller_irq_state,
+ .process = nbio_v7_9_process_ras_controller_irq,
+};
+
+static const struct amdgpu_irq_src_funcs nbio_v7_9_ras_err_event_athub_irq_funcs = {
+ .set = nbio_v7_9_set_ras_err_event_athub_irq_state,
+ .process = nbio_v7_9_process_err_event_athub_irq,
+};
+
+static int nbio_v7_9_init_ras_controller_interrupt(struct ras_core_context *ras_core, bool state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ int r;
+
+ /* init the irq funcs */
+ adev->nbio.ras_controller_irq.funcs =
+ &nbio_v7_9_ras_controller_irq_funcs;
+ adev->nbio.ras_controller_irq.num_types = 1;
+
+ /* register ras controller interrupt */
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_BIF,
+ NBIF_7_4__SRCID__RAS_CONTROLLER_INTERRUPT,
+ &adev->nbio.ras_controller_irq);
+
+ return r;
+}
+
+static int nbio_v7_9_init_ras_err_event_athub_interrupt(struct ras_core_context *ras_core,
+ bool state)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ int r;
+
+ /* init the irq funcs */
+ adev->nbio.ras_err_event_athub_irq.funcs =
+ &nbio_v7_9_ras_err_event_athub_irq_funcs;
+ adev->nbio.ras_err_event_athub_irq.num_types = 1;
+
+ /* register ras err event athub interrupt */
+ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_BIF,
+ NBIF_7_4__SRCID__ERREVENT_ATHUB_INTERRUPT,
+ &adev->nbio.ras_err_event_athub_irq);
+
+ return r;
+}
+
+const struct ras_nbio_sys_func amdgpu_ras_nbio_sys_func_v7_9 = {
+ .set_ras_controller_irq_state = nbio_v7_9_init_ras_controller_interrupt,
+ .set_ras_err_event_athub_irq_state = nbio_v7_9_init_ras_err_event_athub_interrupt,
+};
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.h
new file mode 100644
index 000000000000..272259e9a0e7
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_nbio_v7_9.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __AMDGPU_RAS_NBIO_V7_9_H__
+#define __AMDGPU_RAS_NBIO_V7_9_H__
+
+extern const struct ras_nbio_sys_func amdgpu_ras_nbio_sys_func_v7_9;
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.c
new file mode 100644
index 000000000000..5782c007de71
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * 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
+ * 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.
+ */
+
+#include "amdgpu.h"
+#include "amdgpu_reset.h"
+#include "amdgpu_xgmi.h"
+#include "ras_sys.h"
+#include "amdgpu_ras_mgr.h"
+#include "amdgpu_ras_process.h"
+
+#define RAS_MGR_RETIRE_PAGE_INTERVAL 100
+#define RAS_EVENT_PROCESS_TIMEOUT 1200
+
+static void ras_process_retire_page_dwork(struct work_struct *work)
+{
+ struct amdgpu_ras_mgr *ras_mgr =
+ container_of(work, struct amdgpu_ras_mgr, retire_page_dwork.work);
+ struct amdgpu_device *adev = ras_mgr->adev;
+ int ret;
+
+ if (amdgpu_ras_is_rma(adev))
+ return;
+
+ /* If gpu reset is ongoing, delay retiring the bad pages */
+ if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) {
+ schedule_delayed_work(&ras_mgr->retire_page_dwork,
+ msecs_to_jiffies(RAS_MGR_RETIRE_PAGE_INTERVAL * 3));
+ return;
+ }
+
+ ret = ras_umc_handle_bad_pages(ras_mgr->ras_core, NULL);
+ if (!ret)
+ schedule_delayed_work(&ras_mgr->retire_page_dwork,
+ msecs_to_jiffies(RAS_MGR_RETIRE_PAGE_INTERVAL));
+}
+
+int amdgpu_ras_process_init(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ ras_mgr->is_paused = false;
+ init_completion(&ras_mgr->ras_event_done);
+
+ INIT_DELAYED_WORK(&ras_mgr->retire_page_dwork, ras_process_retire_page_dwork);
+
+ return 0;
+}
+
+int amdgpu_ras_process_fini(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ ras_mgr->is_paused = false;
+ /* Save all cached bad pages to eeprom */
+ flush_delayed_work(&ras_mgr->retire_page_dwork);
+ cancel_delayed_work_sync(&ras_mgr->retire_page_dwork);
+ return 0;
+}
+
+int amdgpu_ras_process_handle_umc_interrupt(struct amdgpu_device *adev, void *data)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!ras_mgr->ras_core)
+ return -EINVAL;
+
+ return ras_process_add_interrupt_req(ras_mgr->ras_core, NULL, true);
+}
+
+int amdgpu_ras_process_handle_unexpected_interrupt(struct amdgpu_device *adev, void *data)
+{
+ amdgpu_ras_set_fed(adev, true);
+ return amdgpu_ras_mgr_reset_gpu(adev, AMDGPU_RAS_GPU_RESET_MODE1_RESET);
+}
+
+int amdgpu_ras_process_handle_consumption_interrupt(struct amdgpu_device *adev, void *data)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ struct ras_ih_info *ih_info = (struct ras_ih_info *)data;
+ struct ras_event_req req;
+ uint64_t seqno;
+
+ if (!ih_info)
+ return -EINVAL;
+
+ memset(&req, 0, sizeof(req));
+ req.block = ih_info->block;
+ req.data = ih_info->data;
+ req.pasid = ih_info->pasid;
+ req.pasid_fn = ih_info->pasid_fn;
+ req.reset = ih_info->reset;
+
+ seqno = ras_core_get_seqno(ras_mgr->ras_core,
+ RAS_SEQNO_TYPE_POISON_CONSUMPTION, false);
+
+ /* When the ACA register cannot be read from FW, the poison
+ * consumption seqno in the fifo will not pop up, so it is
+ * necessary to check whether the seqno is the previous seqno.
+ */
+ if (seqno == ras_mgr->last_poison_consumption_seqno) {
+ /* Pop and discard the previous seqno */
+ ras_core_get_seqno(ras_mgr->ras_core,
+ RAS_SEQNO_TYPE_POISON_CONSUMPTION, true);
+ seqno = ras_core_get_seqno(ras_mgr->ras_core,
+ RAS_SEQNO_TYPE_POISON_CONSUMPTION, false);
+ }
+ ras_mgr->last_poison_consumption_seqno = seqno;
+ req.seqno = seqno;
+
+ return ras_process_add_interrupt_req(ras_mgr->ras_core, &req, false);
+}
+
+int amdgpu_ras_process_begin(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (ras_mgr->is_paused)
+ return -EAGAIN;
+
+ reinit_completion(&ras_mgr->ras_event_done);
+ return 0;
+}
+
+int amdgpu_ras_process_end(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ complete(&ras_mgr->ras_event_done);
+ return 0;
+}
+
+int amdgpu_ras_process_pre_reset(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ long rc;
+
+ if (!ras_mgr || !ras_mgr->ras_core)
+ return -EINVAL;
+
+ if (!ras_mgr->ras_core->is_initialized)
+ return -EPERM;
+
+ ras_mgr->is_paused = true;
+
+ /* Wait for RAS event processing to complete */
+ rc = wait_for_completion_interruptible_timeout(&ras_mgr->ras_event_done,
+ msecs_to_jiffies(RAS_EVENT_PROCESS_TIMEOUT));
+ if (rc <= 0)
+ RAS_DEV_WARN(adev, "Waiting for ras process to complete %s\n",
+ rc ? "interrupted" : "timeout");
+
+ flush_delayed_work(&ras_mgr->retire_page_dwork);
+ return 0;
+}
+
+int amdgpu_ras_process_post_reset(struct amdgpu_device *adev)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+
+ if (!ras_mgr || !ras_mgr->ras_core)
+ return -EINVAL;
+
+ if (!ras_mgr->ras_core->is_initialized)
+ return -EPERM;
+
+ ras_mgr->is_paused = false;
+
+ schedule_delayed_work(&ras_mgr->retire_page_dwork, 0);
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.h
new file mode 100644
index 000000000000..d55cdaeac441
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_process.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2025 Advanced Micro Devices, 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
+ * 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.
+ */
+#ifndef __AMDGPU_RAS_PROCESS_H__
+#define __AMDGPU_RAS_PROCESS_H__
+#include "ras_process.h"
+#include "amdgpu_ras_mgr.h"
+
+enum ras_ih_type;
+int amdgpu_ras_process_init(struct amdgpu_device *adev);
+int amdgpu_ras_process_fini(struct amdgpu_device *adev);
+int amdgpu_ras_process_handle_umc_interrupt(struct amdgpu_device *adev,
+ void *data);
+int amdgpu_ras_process_handle_unexpected_interrupt(struct amdgpu_device *adev,
+ void *data);
+int amdgpu_ras_process_handle_consumption_interrupt(struct amdgpu_device *adev,
+ void *data);
+int amdgpu_ras_process_begin(struct amdgpu_device *adev);
+int amdgpu_ras_process_end(struct amdgpu_device *adev);
+int amdgpu_ras_process_pre_reset(struct amdgpu_device *adev);
+int amdgpu_ras_process_post_reset(struct amdgpu_device *adev);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c
new file mode 100644
index 000000000000..45ed8c3b5563
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras_sys.h"
+#include "amdgpu_ras_mgr.h"
+#include "amdgpu_ras.h"
+#include "amdgpu_reset.h"
+
+static int amdgpu_ras_sys_detect_fatal_event(struct ras_core_context *ras_core, void *data)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ int ret;
+ uint64_t seq_no;
+
+ ret = amdgpu_ras_global_ras_isr(adev);
+ if (ret)
+ return ret;
+
+ seq_no = amdgpu_ras_mgr_gen_ras_event_seqno(adev, RAS_SEQNO_TYPE_UE);
+ RAS_DEV_INFO(adev,
+ "{%llu} Uncorrectable hardware error(ERREVENT_ATHUB_INTERRUPT) detected!\n",
+ seq_no);
+
+ return amdgpu_ras_process_handle_unexpected_interrupt(adev, data);
+}
+
+static int amdgpu_ras_sys_poison_consumption_event(struct ras_core_context *ras_core,
+ void *data)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct ras_event_req *req = (struct ras_event_req *)data;
+ pasid_notify pasid_fn;
+
+ if (!req)
+ return -EINVAL;
+
+ if (req->pasid_fn) {
+ pasid_fn = (pasid_notify)req->pasid_fn;
+ pasid_fn(adev, req->pasid, req->data);
+ }
+
+ return 0;
+}
+
+static int amdgpu_ras_sys_gen_seqno(struct ras_core_context *ras_core,
+ enum ras_seqno_type seqno_type, uint64_t *seqno)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev);
+ struct ras_event_manager *event_mgr;
+ struct ras_event_state *event_state;
+ struct amdgpu_hive_info *hive;
+ enum ras_event_type event_type;
+ uint64_t seq_no;
+
+ if (!ras_mgr || !seqno ||
+ (seqno_type >= RAS_SEQNO_TYPE_COUNT_MAX))
+ return -EINVAL;
+
+ switch (seqno_type) {
+ case RAS_SEQNO_TYPE_UE:
+ event_type = RAS_EVENT_TYPE_FATAL;
+ break;
+ case RAS_SEQNO_TYPE_CE:
+ case RAS_SEQNO_TYPE_DE:
+ event_type = RAS_EVENT_TYPE_POISON_CREATION;
+ break;
+ case RAS_SEQNO_TYPE_POISON_CONSUMPTION:
+ event_type = RAS_EVENT_TYPE_POISON_CONSUMPTION;
+ break;
+ default:
+ event_type = RAS_EVENT_TYPE_INVALID;
+ break;
+ }
+
+ hive = amdgpu_get_xgmi_hive(adev);
+ event_mgr = hive ? &hive->event_mgr : &ras_mgr->ras_event_mgr;
+ event_state = &event_mgr->event_state[event_type];
+ if ((event_type == RAS_EVENT_TYPE_FATAL) && amdgpu_ras_in_recovery(adev)) {
+ seq_no = event_state->last_seqno;
+ } else {
+ seq_no = atomic64_inc_return(&event_mgr->seqno);
+ event_state->last_seqno = seq_no;
+ atomic64_inc(&event_state->count);
+ }
+ amdgpu_put_xgmi_hive(hive);
+
+ *seqno = seq_no;
+ return 0;
+
+}
+
+static int amdgpu_ras_sys_event_notifier(struct ras_core_context *ras_core,
+ enum ras_notify_event event_id, void *data)
+{
+ struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(ras_core->dev);
+ int ret = 0;
+
+ switch (event_id) {
+ case RAS_EVENT_ID__BAD_PAGE_DETECTED:
+ schedule_delayed_work(&ras_mgr->retire_page_dwork, 0);
+ break;
+ case RAS_EVENT_ID__POISON_CONSUMPTION:
+ amdgpu_ras_sys_poison_consumption_event(ras_core, data);
+ break;
+ case RAS_EVENT_ID__RESERVE_BAD_PAGE:
+ ret = amdgpu_ras_reserve_page(ras_core->dev, *(uint64_t *)data);
+ break;
+ case RAS_EVENT_ID__FATAL_ERROR_DETECTED:
+ ret = amdgpu_ras_sys_detect_fatal_event(ras_core, data);
+ break;
+ case RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM:
+ ret = amdgpu_dpm_send_hbm_bad_pages_num(ras_core->dev, *(uint32_t *)data);
+ break;
+ case RAS_EVENT_ID__UPDATE_BAD_CHANNEL_BITMAP:
+ ret = amdgpu_dpm_send_hbm_bad_channel_flag(ras_core->dev, *(uint32_t *)data);
+ break;
+ case RAS_EVENT_ID__DEVICE_RMA:
+ ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_RMA, NULL, NULL);
+ ret = amdgpu_dpm_send_rma_reason(ras_core->dev);
+ break;
+ case RAS_EVENT_ID__RESET_GPU:
+ ret = amdgpu_ras_mgr_reset_gpu(ras_core->dev, *(uint32_t *)data);
+ break;
+ case RAS_EVENT_ID__RAS_EVENT_PROC_BEGIN:
+ ret = amdgpu_ras_process_begin(ras_core->dev);
+ break;
+ case RAS_EVENT_ID__RAS_EVENT_PROC_END:
+ ret = amdgpu_ras_process_end(ras_core->dev);
+ break;
+ default:
+ RAS_DEV_WARN(ras_core->dev, "Invalid ras notify event:%d\n", event_id);
+ break;
+ }
+
+ return ret;
+}
+
+static u64 amdgpu_ras_sys_get_utc_second_timestamp(struct ras_core_context *ras_core)
+{
+ return ktime_get_real_seconds();
+}
+
+static int amdgpu_ras_sys_check_gpu_status(struct ras_core_context *ras_core,
+ uint32_t *status)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ uint32_t gpu_status = 0;
+
+ if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev))
+ gpu_status |= RAS_GPU_STATUS__IN_RESET;
+
+ if (amdgpu_sriov_vf(adev))
+ gpu_status |= RAS_GPU_STATUS__IS_VF;
+
+ *status = gpu_status;
+
+ return 0;
+}
+
+static int amdgpu_ras_sys_get_device_system_info(struct ras_core_context *ras_core,
+ struct device_system_info *dev_info)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+
+ dev_info->device_id = adev->pdev->device;
+ dev_info->vendor_id = adev->pdev->vendor;
+ dev_info->socket_id = adev->smuio.funcs->get_socket_id(adev);
+
+ return 0;
+}
+
+static int amdgpu_ras_sys_gpu_reset_lock(struct ras_core_context *ras_core,
+ bool down, bool try)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ int ret = 0;
+
+ if (down && try)
+ ret = down_read_trylock(&adev->reset_domain->sem);
+ else if (down)
+ down_read(&adev->reset_domain->sem);
+ else
+ up_read(&adev->reset_domain->sem);
+
+ return ret;
+}
+
+static bool amdgpu_ras_sys_detect_ras_interrupt(struct ras_core_context *ras_core)
+{
+ return !!atomic_read(&amdgpu_ras_in_intr);
+}
+
+static int amdgpu_ras_sys_get_gpu_mem(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem)
+{
+ struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev;
+ struct psp_context *psp = &adev->psp;
+ struct psp_ring *psp_ring;
+ struct ta_mem_context *mem_ctx;
+
+ if (mem_type == GPU_MEM_TYPE_RAS_PSP_RING) {
+ psp_ring = &psp->km_ring;
+ gpu_mem->mem_bo = adev->firmware.rbuf;
+ gpu_mem->mem_size = psp_ring->ring_size;
+ gpu_mem->mem_mc_addr = psp_ring->ring_mem_mc_addr;
+ gpu_mem->mem_cpu_addr = psp_ring->ring_mem;
+ } else if (mem_type == GPU_MEM_TYPE_RAS_PSP_CMD) {
+ gpu_mem->mem_bo = psp->cmd_buf_bo;
+ gpu_mem->mem_size = PSP_CMD_BUFFER_SIZE;
+ gpu_mem->mem_mc_addr = psp->cmd_buf_mc_addr;
+ gpu_mem->mem_cpu_addr = psp->cmd_buf_mem;
+ } else if (mem_type == GPU_MEM_TYPE_RAS_PSP_FENCE) {
+ gpu_mem->mem_bo = psp->fence_buf_bo;
+ gpu_mem->mem_size = PSP_FENCE_BUFFER_SIZE;
+ gpu_mem->mem_mc_addr = psp->fence_buf_mc_addr;
+ gpu_mem->mem_cpu_addr = psp->fence_buf;
+ } else if (mem_type == GPU_MEM_TYPE_RAS_TA_FW) {
+ gpu_mem->mem_bo = psp->fw_pri_bo;
+ gpu_mem->mem_size = PSP_1_MEG;
+ gpu_mem->mem_mc_addr = psp->fw_pri_mc_addr;
+ gpu_mem->mem_cpu_addr = psp->fw_pri_buf;
+ } else if (mem_type == GPU_MEM_TYPE_RAS_TA_CMD) {
+ mem_ctx = &psp->ras_context.context.mem_context;
+ gpu_mem->mem_bo = mem_ctx->shared_bo;
+ gpu_mem->mem_size = mem_ctx->shared_mem_size;
+ gpu_mem->mem_mc_addr = mem_ctx->shared_mc_addr;
+ gpu_mem->mem_cpu_addr = mem_ctx->shared_buf;
+ } else {
+ return -EINVAL;
+ }
+
+ if (!gpu_mem->mem_bo || !gpu_mem->mem_size ||
+ !gpu_mem->mem_mc_addr || !gpu_mem->mem_cpu_addr) {
+ RAS_DEV_ERR(ras_core->dev, "The ras psp gpu memory is invalid!\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int amdgpu_ras_sys_put_gpu_mem(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem)
+{
+
+ return 0;
+}
+
+const struct ras_sys_func amdgpu_ras_sys_fn = {
+ .ras_notifier = amdgpu_ras_sys_event_notifier,
+ .get_utc_second_timestamp = amdgpu_ras_sys_get_utc_second_timestamp,
+ .gen_seqno = amdgpu_ras_sys_gen_seqno,
+ .check_gpu_status = amdgpu_ras_sys_check_gpu_status,
+ .get_device_system_info = amdgpu_ras_sys_get_device_system_info,
+ .gpu_reset_lock = amdgpu_ras_sys_gpu_reset_lock,
+ .detect_ras_interrupt = amdgpu_ras_sys_detect_ras_interrupt,
+ .get_gpu_mem = amdgpu_ras_sys_get_gpu_mem,
+ .put_gpu_mem = amdgpu_ras_sys_put_gpu_mem,
+};
diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
new file mode 100644
index 000000000000..8156531a7b63
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_SYS_H__
+#define __RAS_SYS_H__
+#include <linux/stdarg.h>
+#include <linux/printk.h>
+#include <linux/dev_printk.h>
+#include <linux/mempool.h>
+#include "amdgpu.h"
+
+#define RAS_DEV_ERR(device, fmt, ...) \
+ do { \
+ if (device) \
+ dev_err(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
+ else \
+ printk(KERN_ERR fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define RAS_DEV_WARN(device, fmt, ...) \
+ do { \
+ if (device) \
+ dev_warn(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
+ else \
+ printk(KERN_WARNING fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define RAS_DEV_INFO(device, fmt, ...) \
+ do { \
+ if (device) \
+ dev_info(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
+ else \
+ printk(KERN_INFO fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define RAS_DEV_DBG(device, fmt, ...) \
+ do { \
+ if (device) \
+ dev_dbg(((struct amdgpu_device *)device)->dev, fmt, ##__VA_ARGS__); \
+ else \
+ printk(KERN_DEBUG fmt, ##__VA_ARGS__); \
+ } while (0)
+
+#define RAS_INFO(fmt, ...) printk(KERN_INFO fmt, ##__VA_ARGS__)
+
+#define RAS_DEV_RREG32_SOC15(dev, ip, inst, reg) \
+({ \
+ struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
+ __RREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg, \
+ 0, ip##_HWIP, inst); \
+})
+
+#define RAS_DEV_WREG32_SOC15(dev, ip, inst, reg, value) \
+({ \
+ struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
+ __WREG32_SOC15_RLC__((adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg), \
+ value, 0, ip##_HWIP, inst); \
+})
+
+/* GET_INST returns the physical instance corresponding to a logical instance */
+#define RAS_GET_INST(dev, ip, inst) \
+({ \
+ struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
+ adev->ip_map.logical_to_dev_inst ? \
+ adev->ip_map.logical_to_dev_inst(adev, ip##_HWIP, inst) : inst; \
+})
+
+#define RAS_GET_MASK(dev, ip, mask) \
+({ \
+ struct amdgpu_device *adev = (struct amdgpu_device *)dev; \
+ (adev->ip_map.logical_to_dev_mask ? \
+ adev->ip_map.logical_to_dev_mask(adev, ip##_HWIP, mask) : mask); \
+})
+
+static inline void *ras_radix_tree_delete_iter(struct radix_tree_root *root, void *iter)
+{
+ return radix_tree_delete(root, ((struct radix_tree_iter *)iter)->index);
+}
+
+static inline long ras_wait_event_interruptible_timeout(void *wq_head,
+ int (*condition)(void *param), void *param, unsigned int timeout)
+{
+ return wait_event_interruptible_timeout(*(wait_queue_head_t *)wq_head,
+ condition(param), timeout);
+}
+
+extern const struct ras_sys_func amdgpu_ras_sys_fn;
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/Makefile b/drivers/gpu/drm/amd/ras/rascore/Makefile
index e69de29bb2d1..e826a1f86424 100644
--- a/drivers/gpu/drm/amd/ras/rascore/Makefile
+++ b/drivers/gpu/drm/amd/ras/rascore/Makefile
@@ -0,0 +1,44 @@
+#
+# Copyright 2025 Advanced Micro Devices, 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.
+#
+RAS_CORE_FILES = ras_core.o \
+ ras_mp1.o \
+ ras_mp1_v13_0.o \
+ ras_aca.o \
+ ras_aca_v1_0.o \
+ ras_eeprom.o \
+ ras_umc.o \
+ ras_umc_v12_0.o \
+ ras_cmd.o \
+ ras_gfx.o \
+ ras_gfx_v9_0.o \
+ ras_process.o \
+ ras_nbio.o \
+ ras_nbio_v7_9.o \
+ ras_log_ring.o \
+ ras_cper.o \
+ ras_psp.o \
+ ras_psp_v13_0.o
+
+
+RAS_CORE = $(addprefix $(AMD_GPU_RAS_PATH)/rascore/,$(RAS_CORE_FILES))
+
+AMD_GPU_RAS_FILES += $(RAS_CORE)
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras.h b/drivers/gpu/drm/amd/ras/rascore/ras.h
new file mode 100644
index 000000000000..3396b2e0949d
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras.h
@@ -0,0 +1,370 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_H__
+#define __RAS_H__
+#include "ras_sys.h"
+#include "ras_umc.h"
+#include "ras_aca.h"
+#include "ras_eeprom.h"
+#include "ras_core_status.h"
+#include "ras_process.h"
+#include "ras_gfx.h"
+#include "ras_cmd.h"
+#include "ras_nbio.h"
+#include "ras_mp1.h"
+#include "ras_psp.h"
+#include "ras_log_ring.h"
+
+#define RAS_HW_ERR "[Hardware Error]: "
+
+#define RAS_GPU_PAGE_SHIFT 12
+#define RAS_ADDR_TO_PFN(addr) ((addr) >> RAS_GPU_PAGE_SHIFT)
+#define RAS_PFN_TO_ADDR(pfn) ((pfn) << RAS_GPU_PAGE_SHIFT)
+
+#define RAS_CORE_RESET_GPU 0x10000
+
+#define GPU_RESET_CAUSE_POISON (RAS_CORE_RESET_GPU | 0x0001)
+#define GPU_RESET_CAUSE_FATAL (RAS_CORE_RESET_GPU | 0x0002)
+#define GPU_RESET_CAUSE_RMA (RAS_CORE_RESET_GPU | 0x0004)
+
+enum ras_block_id {
+ RAS_BLOCK_ID__UMC = 0,
+ RAS_BLOCK_ID__SDMA,
+ RAS_BLOCK_ID__GFX,
+ RAS_BLOCK_ID__MMHUB,
+ RAS_BLOCK_ID__ATHUB,
+ RAS_BLOCK_ID__PCIE_BIF,
+ RAS_BLOCK_ID__HDP,
+ RAS_BLOCK_ID__XGMI_WAFL,
+ RAS_BLOCK_ID__DF,
+ RAS_BLOCK_ID__SMN,
+ RAS_BLOCK_ID__SEM,
+ RAS_BLOCK_ID__MP0,
+ RAS_BLOCK_ID__MP1,
+ RAS_BLOCK_ID__FUSE,
+ RAS_BLOCK_ID__MCA,
+ RAS_BLOCK_ID__VCN,
+ RAS_BLOCK_ID__JPEG,
+ RAS_BLOCK_ID__IH,
+ RAS_BLOCK_ID__MPIO,
+
+ RAS_BLOCK_ID__LAST
+};
+
+enum ras_ecc_err_type {
+ RAS_ECC_ERR__NONE = 0,
+ RAS_ECC_ERR__PARITY = 1,
+ RAS_ECC_ERR__SINGLE_CORRECTABLE = 2,
+ RAS_ECC_ERR__MULTI_UNCORRECTABLE = 4,
+ RAS_ECC_ERR__POISON = 8,
+};
+
+enum ras_err_type {
+ RAS_ERR_TYPE__UE = 0,
+ RAS_ERR_TYPE__CE,
+ RAS_ERR_TYPE__DE,
+ RAS_ERR_TYPE__LAST
+};
+
+enum ras_seqno_type {
+ RAS_SEQNO_TYPE_INVALID = 0,
+ RAS_SEQNO_TYPE_UE,
+ RAS_SEQNO_TYPE_CE,
+ RAS_SEQNO_TYPE_DE,
+ RAS_SEQNO_TYPE_POISON_CONSUMPTION,
+ RAS_SEQNO_TYPE_COUNT_MAX,
+};
+
+enum ras_seqno_fifo {
+ SEQNO_FIFO_INVALID = 0,
+ SEQNO_FIFO_POISON_CREATION,
+ SEQNO_FIFO_POISON_CONSUMPTION,
+ SEQNO_FIFO_COUNT_MAX
+};
+
+enum ras_notify_event {
+ RAS_EVENT_ID__NONE,
+ RAS_EVENT_ID__BAD_PAGE_DETECTED,
+ RAS_EVENT_ID__POISON_CONSUMPTION,
+ RAS_EVENT_ID__RESERVE_BAD_PAGE,
+ RAS_EVENT_ID__DEVICE_RMA,
+ RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM,
+ RAS_EVENT_ID__UPDATE_BAD_CHANNEL_BITMAP,
+ RAS_EVENT_ID__FATAL_ERROR_DETECTED,
+ RAS_EVENT_ID__RESET_GPU,
+ RAS_EVENT_ID__RESET_VF,
+ RAS_EVENT_ID__RAS_EVENT_PROC_BEGIN,
+ RAS_EVENT_ID__RAS_EVENT_PROC_END,
+};
+
+enum ras_gpu_status {
+ RAS_GPU_STATUS__NOT_READY = 0,
+ RAS_GPU_STATUS__READY = 0x1,
+ RAS_GPU_STATUS__IN_RESET = 0x2,
+ RAS_GPU_STATUS__IS_RMA = 0x4,
+ RAS_GPU_STATUS__IS_VF = 0x8,
+};
+
+struct ras_core_context;
+struct ras_bank_ecc;
+struct ras_umc;
+struct ras_aca;
+struct ras_process;
+struct ras_nbio;
+struct ras_log_ring;
+struct ras_psp;
+
+struct ras_mp1_sys_func {
+ int (*mp1_get_valid_bank_count)(struct ras_core_context *ras_core,
+ u32 msg, u32 *count);
+ int (*mp1_dump_valid_bank)(struct ras_core_context *ras_core,
+ u32 msg, u32 idx, u32 reg_idx, u64 *val);
+};
+
+struct ras_eeprom_sys_func {
+ int (*eeprom_i2c_xfer)(struct ras_core_context *ras_core,
+ u32 eeprom_addr, u8 *eeprom_buf, u32 buf_size, bool read);
+ int (*update_eeprom_i2c_config)(struct ras_core_context *ras_core);
+};
+
+struct ras_nbio_sys_func {
+ int (*set_ras_controller_irq_state)(struct ras_core_context *ras_core,
+ bool state);
+ int (*set_ras_err_event_athub_irq_state)(struct ras_core_context *ras_core,
+ bool state);
+};
+
+struct ras_time {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ long tm_year;
+};
+
+struct device_system_info {
+ uint32_t device_id;
+ uint32_t vendor_id;
+ uint32_t socket_id;
+};
+
+enum gpu_mem_type {
+ GPU_MEM_TYPE_DEFAULT,
+ GPU_MEM_TYPE_RAS_PSP_RING,
+ GPU_MEM_TYPE_RAS_PSP_CMD,
+ GPU_MEM_TYPE_RAS_PSP_FENCE,
+ GPU_MEM_TYPE_RAS_TA_FW,
+ GPU_MEM_TYPE_RAS_TA_CMD,
+};
+
+struct ras_psp_sys_func {
+ int (*get_ras_psp_system_status)(struct ras_core_context *ras_core,
+ struct ras_psp_sys_status *status);
+ int (*get_ras_ta_init_param)(struct ras_core_context *ras_core,
+ struct ras_ta_init_param *ras_ta_param);
+};
+
+struct ras_sys_func {
+ int (*gpu_reset_lock)(struct ras_core_context *ras_core,
+ bool down, bool try);
+ int (*check_gpu_status)(struct ras_core_context *ras_core,
+ uint32_t *status);
+ int (*gen_seqno)(struct ras_core_context *ras_core,
+ enum ras_seqno_type seqno_type, uint64_t *seqno);
+ int (*async_handle_ras_event)(struct ras_core_context *ras_core, void *data);
+ int (*ras_notifier)(struct ras_core_context *ras_core,
+ enum ras_notify_event event_id, void *data);
+ u64 (*get_utc_second_timestamp)(struct ras_core_context *ras_core);
+ int (*get_device_system_info)(struct ras_core_context *ras_core,
+ struct device_system_info *dev_info);
+ bool (*detect_ras_interrupt)(struct ras_core_context *ras_core);
+ int (*get_gpu_mem)(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem);
+ int (*put_gpu_mem)(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem);
+};
+
+struct ras_ecc_count {
+ uint64_t new_ce_count;
+ uint64_t total_ce_count;
+ uint64_t new_ue_count;
+ uint64_t total_ue_count;
+ uint64_t new_de_count;
+ uint64_t total_de_count;
+};
+
+struct ras_bank_ecc {
+ uint32_t nps;
+ uint64_t seq_no;
+ uint64_t status;
+ uint64_t ipid;
+ uint64_t addr;
+};
+
+struct ras_bank_ecc_node {
+ struct list_head node;
+ struct ras_bank_ecc ecc;
+};
+
+struct ras_aca_config {
+ u32 socket_num_per_hive;
+ u32 aid_num_per_socket;
+ u32 xcd_num_per_aid;
+};
+
+struct ras_mp1_config {
+ const struct ras_mp1_sys_func *mp1_sys_fn;
+};
+
+struct ras_nbio_config {
+ const struct ras_nbio_sys_func *nbio_sys_fn;
+};
+
+struct ras_psp_config {
+ const struct ras_psp_sys_func *psp_sys_fn;
+};
+
+struct ras_umc_config {
+ uint32_t umc_vram_type;
+};
+
+struct ras_eeprom_config {
+ const struct ras_eeprom_sys_func *eeprom_sys_fn;
+ int eeprom_record_threshold_config;
+ uint32_t eeprom_record_threshold_count;
+ void *eeprom_i2c_adapter;
+ u32 eeprom_i2c_addr;
+ u32 eeprom_i2c_port;
+ u16 max_i2c_read_len;
+ u16 max_i2c_write_len;
+};
+
+struct ras_core_config {
+ u32 aca_ip_version;
+ u32 umc_ip_version;
+ u32 mp1_ip_version;
+ u32 gfx_ip_version;
+ u32 nbio_ip_version;
+ u32 psp_ip_version;
+
+ bool poison_supported;
+ bool ras_eeprom_supported;
+ const struct ras_sys_func *sys_fn;
+
+ struct ras_aca_config aca_cfg;
+ struct ras_mp1_config mp1_cfg;
+ struct ras_nbio_config nbio_cfg;
+ struct ras_psp_config psp_cfg;
+ struct ras_eeprom_config eeprom_cfg;
+ struct ras_umc_config umc_cfg;
+};
+
+struct ras_core_context {
+ void *dev;
+ struct ras_core_config *config;
+ u32 socket_num_per_hive;
+ u32 aid_num_per_socket;
+ u32 xcd_num_per_aid;
+ int max_ue_banks_per_query;
+ int max_ce_banks_per_query;
+ struct ras_aca ras_aca;
+
+ bool ras_eeprom_supported;
+ struct ras_eeprom_control ras_eeprom;
+
+ struct ras_psp ras_psp;
+ struct ras_umc ras_umc;
+ struct ras_nbio ras_nbio;
+ struct ras_gfx ras_gfx;
+ struct ras_mp1 ras_mp1;
+ struct ras_process ras_proc;
+ struct ras_cmd_mgr ras_cmd;
+ struct ras_log_ring ras_log_ring;
+
+ const struct ras_sys_func *sys_fn;
+
+ /* is poison mode supported */
+ bool poison_supported;
+
+ bool is_rma;
+ bool is_initialized;
+
+ struct kfifo de_seqno_fifo;
+ struct kfifo consumption_seqno_fifo;
+ spinlock_t seqno_lock;
+
+ bool ras_core_enabled;
+};
+
+struct ras_core_context *ras_core_create(struct ras_core_config *init_config);
+void ras_core_destroy(struct ras_core_context *ras_core);
+int ras_core_sw_init(struct ras_core_context *ras_core);
+int ras_core_sw_fini(struct ras_core_context *ras_core);
+int ras_core_hw_init(struct ras_core_context *ras_core);
+int ras_core_hw_fini(struct ras_core_context *ras_core);
+bool ras_core_is_ready(struct ras_core_context *ras_core);
+uint64_t ras_core_gen_seqno(struct ras_core_context *ras_core,
+ enum ras_seqno_type seqno_type);
+uint64_t ras_core_get_seqno(struct ras_core_context *ras_core,
+ enum ras_seqno_type seqno_type, bool pop);
+
+int ras_core_put_seqno(struct ras_core_context *ras_core,
+ enum ras_seqno_type seqno_type, uint64_t seqno);
+
+int ras_core_update_ecc_info(struct ras_core_context *ras_core);
+int ras_core_query_block_ecc_data(struct ras_core_context *ras_core,
+ enum ras_block_id block, struct ras_ecc_count *ecc_count);
+
+bool ras_core_gpu_in_reset(struct ras_core_context *ras_core);
+bool ras_core_gpu_is_rma(struct ras_core_context *ras_core);
+bool ras_core_gpu_is_vf(struct ras_core_context *ras_core);
+bool ras_core_handle_nbio_irq(struct ras_core_context *ras_core, void *data);
+int ras_core_handle_fatal_error(struct ras_core_context *ras_core);
+
+uint32_t ras_core_get_curr_nps_mode(struct ras_core_context *ras_core);
+const char *ras_core_get_ras_block_name(enum ras_block_id block_id);
+int ras_core_convert_timestamp_to_time(struct ras_core_context *ras_core,
+ uint64_t timestamp, struct ras_time *tm);
+
+int ras_core_set_status(struct ras_core_context *ras_core, bool enable);
+bool ras_core_is_enabled(struct ras_core_context *ras_core);
+uint64_t ras_core_get_utc_second_timestamp(struct ras_core_context *ras_core);
+int ras_core_translate_soc_pa_and_bank(struct ras_core_context *ras_core,
+ uint64_t *soc_pa, struct umc_bank_addr *bank_addr, bool bank_to_pa);
+bool ras_core_ras_interrupt_detected(struct ras_core_context *ras_core);
+int ras_core_get_gpu_mem(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem);
+int ras_core_put_gpu_mem(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem);
+bool ras_core_check_safety_watermark(struct ras_core_context *ras_core);
+int ras_core_down_trylock_gpu_reset_lock(struct ras_core_context *ras_core);
+void ras_core_down_gpu_reset_lock(struct ras_core_context *ras_core);
+void ras_core_up_gpu_reset_lock(struct ras_core_context *ras_core);
+int ras_core_event_notify(struct ras_core_context *ras_core,
+ enum ras_notify_event event_id, void *data);
+int ras_core_get_device_system_info(struct ras_core_context *ras_core,
+ struct device_system_info *dev_info);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca.c b/drivers/gpu/drm/amd/ras/rascore/ras_aca.c
new file mode 100644
index 000000000000..e433c70d2989
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca.c
@@ -0,0 +1,672 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_aca.h"
+#include "ras_aca_v1_0.h"
+#include "ras_mp1_v13_0.h"
+
+#define ACA_MARK_FATAL_FLAG 0x100
+#define ACA_MARK_UE_READ_FLAG 0x1
+
+#define blk_name(block_id) ras_core_get_ras_block_name(block_id)
+
+static struct aca_regs_dump {
+ const char *name;
+ int reg_idx;
+} aca_regs[] = {
+ {"CONTROL", ACA_REG_IDX__CTL},
+ {"STATUS", ACA_REG_IDX__STATUS},
+ {"ADDR", ACA_REG_IDX__ADDR},
+ {"MISC", ACA_REG_IDX__MISC0},
+ {"CONFIG", ACA_REG_IDX__CONFG},
+ {"IPID", ACA_REG_IDX__IPID},
+ {"SYND", ACA_REG_IDX__SYND},
+ {"DESTAT", ACA_REG_IDX__DESTAT},
+ {"DEADDR", ACA_REG_IDX__DEADDR},
+ {"CONTROL_MASK", ACA_REG_IDX__CTL_MASK},
+};
+
+
+static void aca_report_ecc_info(struct ras_core_context *ras_core,
+ u64 seq_no, u32 blk, u32 skt, u32 aid,
+ struct aca_aid_ecc *aid_ecc,
+ struct aca_bank_ecc *new_ecc)
+{
+ struct aca_ecc_count ecc_count = {0};
+
+ ecc_count.new_ue_count = new_ecc->ue_count;
+ ecc_count.new_de_count = new_ecc->de_count;
+ ecc_count.new_ce_count = new_ecc->ce_count;
+ if (blk == RAS_BLOCK_ID__GFX) {
+ struct aca_ecc_count *xcd_ecc;
+ int xcd_id;
+
+ for (xcd_id = 0; xcd_id < aid_ecc->xcd.xcd_num; xcd_id++) {
+ xcd_ecc = &aid_ecc->xcd.xcd[xcd_id].ecc_err;
+ ecc_count.total_ue_count += xcd_ecc->total_ue_count;
+ ecc_count.total_de_count += xcd_ecc->total_de_count;
+ ecc_count.total_ce_count += xcd_ecc->total_ce_count;
+ }
+ } else {
+ ecc_count.total_ue_count = aid_ecc->ecc_err.total_ue_count;
+ ecc_count.total_de_count = aid_ecc->ecc_err.total_de_count;
+ ecc_count.total_ce_count = aid_ecc->ecc_err.total_ce_count;
+ }
+
+ if (ecc_count.new_ue_count) {
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} socket: %d, die: %d, %u new uncorrectable hardware errors detected in %s block\n",
+ seq_no, skt, aid, ecc_count.new_ue_count, blk_name(blk));
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} socket: %d, die: %d, %u uncorrectable hardware errors detected in total in %s block\n",
+ seq_no, skt, aid, ecc_count.total_ue_count, blk_name(blk));
+ }
+
+ if (ecc_count.new_de_count) {
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} socket: %d, die: %d, %u new %s detected in %s block\n",
+ seq_no, skt, aid, ecc_count.new_de_count,
+ (blk == RAS_BLOCK_ID__UMC) ?
+ "deferred hardware errors" : "poison consumption",
+ blk_name(blk));
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} socket: %d, die: %d, %u %s detected in total in %s block\n",
+ seq_no, skt, aid, ecc_count.total_de_count,
+ (blk == RAS_BLOCK_ID__UMC) ?
+ "deferred hardware errors" : "poison consumption",
+ blk_name(blk));
+ }
+
+ if (ecc_count.new_ce_count) {
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} socket: %d, die: %d, %u new correctable hardware errors detected in %s block\n",
+ seq_no, skt, aid, ecc_count.new_ce_count, blk_name(blk));
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} socket: %d, die: %d, %u correctable hardware errors detected in total in %s block\n",
+ seq_no, skt, aid, ecc_count.total_ce_count, blk_name(blk));
+ }
+}
+
+static void aca_bank_log(struct ras_core_context *ras_core,
+ int idx, int total, struct aca_bank_reg *bank,
+ struct aca_bank_ecc *bank_ecc)
+{
+ int i;
+
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu}" RAS_HW_ERR "Accelerator Check Architecture events logged\n",
+ bank->seq_no);
+ /* plus 1 for output format, e.g: ACA[08/08]: xxxx */
+ for (i = 0; i < ARRAY_SIZE(aca_regs); i++)
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu}" RAS_HW_ERR "ACA[%02d/%02d].%s=0x%016llx\n",
+ bank->seq_no, idx + 1, total,
+ aca_regs[i].name, bank->regs[aca_regs[i].reg_idx]);
+}
+
+static void aca_log_bank_data(struct ras_core_context *ras_core,
+ struct aca_bank_reg *bank, struct aca_bank_ecc *bank_ecc,
+ struct ras_log_batch_tag *batch)
+{
+ if (bank_ecc->ue_count)
+ ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_UE, bank->regs, batch);
+ else if (bank_ecc->de_count)
+ ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_DE, bank->regs, batch);
+ else
+ ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_CE, bank->regs, batch);
+}
+
+static int aca_get_bank_count(struct ras_core_context *ras_core,
+ enum ras_err_type type, u32 *count)
+{
+ return ras_mp1_get_bank_count(ras_core, type, count);
+}
+
+static bool aca_match_bank(struct aca_block *aca_blk, struct aca_bank_reg *bank)
+{
+ const struct aca_bank_hw_ops *bank_ops;
+
+ if (!aca_blk->blk_info)
+ return false;
+
+ bank_ops = &aca_blk->blk_info->bank_ops;
+ if (!bank_ops->bank_match)
+ return false;
+
+ return bank_ops->bank_match(aca_blk, bank);
+}
+
+static int aca_parse_bank(struct ras_core_context *ras_core,
+ struct aca_block *aca_blk,
+ struct aca_bank_reg *bank,
+ struct aca_bank_ecc *ecc)
+{
+ const struct aca_bank_hw_ops *bank_ops = &aca_blk->blk_info->bank_ops;
+
+ if (!bank_ops || !bank_ops->bank_parse)
+ return -RAS_CORE_NOT_SUPPORTED;
+
+ return bank_ops->bank_parse(ras_core, aca_blk, bank, ecc);
+}
+
+static int aca_check_block_ecc_info(struct ras_core_context *ras_core,
+ struct aca_block *aca_blk, struct aca_ecc_info *info)
+{
+ if (info->socket_id >= aca_blk->ecc.socket_num_per_hive) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Socket id (%d) is out of config! max:%u\n",
+ info->socket_id, aca_blk->ecc.socket_num_per_hive);
+ return -ENODATA;
+ }
+
+ if (info->die_id >= aca_blk->ecc.socket[info->socket_id].aid_num) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Die id (%d) is out of config! max:%u\n",
+ info->die_id, aca_blk->ecc.socket[info->socket_id].aid_num);
+ return -ENODATA;
+ }
+
+ if ((aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__GFX) &&
+ (info->xcd_id >=
+ aca_blk->ecc.socket[info->socket_id].aid[info->die_id].xcd.xcd_num)) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Xcd id (%d) is out of config! max:%u\n",
+ info->xcd_id,
+ aca_blk->ecc.socket[info->socket_id].aid[info->die_id].xcd.xcd_num);
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+static int aca_log_bad_bank(struct ras_core_context *ras_core,
+ struct aca_block *aca_blk, struct aca_bank_reg *bank,
+ struct aca_bank_ecc *bank_ecc)
+{
+ struct aca_ecc_info *info;
+ struct aca_ecc_count *ecc_err;
+ struct aca_aid_ecc *aid_ecc;
+ int ret;
+
+ info = &bank_ecc->bank_info;
+
+ ret = aca_check_block_ecc_info(ras_core, aca_blk, info);
+ if (ret)
+ return ret;
+
+ mutex_lock(&ras_core->ras_aca.aca_lock);
+ aid_ecc = &aca_blk->ecc.socket[info->socket_id].aid[info->die_id];
+ if (aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__GFX)
+ ecc_err = &aid_ecc->xcd.xcd[info->xcd_id].ecc_err;
+ else
+ ecc_err = &aid_ecc->ecc_err;
+
+ ecc_err->new_ce_count += bank_ecc->ce_count;
+ ecc_err->total_ce_count += bank_ecc->ce_count;
+ ecc_err->new_ue_count += bank_ecc->ue_count;
+ ecc_err->total_ue_count += bank_ecc->ue_count;
+ ecc_err->new_de_count += bank_ecc->de_count;
+ ecc_err->total_de_count += bank_ecc->de_count;
+ mutex_unlock(&ras_core->ras_aca.aca_lock);
+
+ if ((aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__UMC) &&
+ bank_ecc->de_count) {
+ struct ras_bank_ecc ras_ecc = {0};
+
+ ras_ecc.nps = ras_core_get_curr_nps_mode(ras_core);
+ ras_ecc.addr = bank_ecc->bank_info.addr;
+ ras_ecc.ipid = bank_ecc->bank_info.ipid;
+ ras_ecc.status = bank_ecc->bank_info.status;
+ ras_ecc.seq_no = bank->seq_no;
+
+ if (ras_core_gpu_in_reset(ras_core))
+ ras_umc_log_bad_bank_pending(ras_core, &ras_ecc);
+ else
+ ras_umc_log_bad_bank(ras_core, &ras_ecc);
+ }
+
+ aca_report_ecc_info(ras_core,
+ bank->seq_no, aca_blk->blk_info->ras_block_id, info->socket_id, info->die_id,
+ &aca_blk->ecc.socket[info->socket_id].aid[info->die_id], bank_ecc);
+
+ return 0;
+}
+
+static struct aca_block *aca_get_bank_aca_block(struct ras_core_context *ras_core,
+ struct aca_bank_reg *bank)
+{
+ int i = 0;
+
+ for (i = 0; i < RAS_BLOCK_ID__LAST; i++)
+ if (aca_match_bank(&ras_core->ras_aca.aca_blk[i], bank))
+ return &ras_core->ras_aca.aca_blk[i];
+
+ return NULL;
+}
+
+static int aca_dump_bank(struct ras_core_context *ras_core, u32 ecc_type,
+ int idx, void *data)
+{
+ struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
+ int i, ret, reg_cnt;
+
+ reg_cnt = min_t(int, 16, ARRAY_SIZE(bank->regs));
+ for (i = 0; i < reg_cnt; i++) {
+ ret = ras_mp1_dump_bank(ras_core, ecc_type, idx, i, &bank->regs[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static uint64_t aca_get_bank_seqno(struct ras_core_context *ras_core,
+ enum ras_err_type err_type, struct aca_block *aca_blk,
+ struct aca_bank_ecc *bank_ecc)
+{
+ uint64_t seq_no = 0;
+
+ if (bank_ecc->de_count) {
+ if (aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__UMC)
+ seq_no = ras_core_get_seqno(ras_core, RAS_SEQNO_TYPE_DE, true);
+ else
+ seq_no = ras_core_get_seqno(ras_core,
+ RAS_SEQNO_TYPE_POISON_CONSUMPTION, true);
+ } else if (bank_ecc->ue_count) {
+ seq_no = ras_core_get_seqno(ras_core, RAS_SEQNO_TYPE_UE, true);
+ } else {
+ seq_no = ras_core_get_seqno(ras_core, RAS_SEQNO_TYPE_CE, true);
+ }
+
+ return seq_no;
+}
+
+static bool aca_dup_update_ue_in_fatal(struct ras_core_context *ras_core,
+ u32 ecc_type)
+{
+ struct ras_aca *aca = &ras_core->ras_aca;
+
+ if (ecc_type != RAS_ERR_TYPE__UE)
+ return false;
+
+ if (aca->ue_updated_mark & ACA_MARK_FATAL_FLAG) {
+ if (aca->ue_updated_mark & ACA_MARK_UE_READ_FLAG)
+ return true;
+
+ aca->ue_updated_mark |= ACA_MARK_UE_READ_FLAG;
+ }
+
+ return false;
+}
+
+void ras_aca_mark_fatal_flag(struct ras_core_context *ras_core)
+{
+ struct ras_aca *aca = &ras_core->ras_aca;
+
+ if (!aca)
+ return;
+
+ aca->ue_updated_mark |= ACA_MARK_FATAL_FLAG;
+}
+
+void ras_aca_clear_fatal_flag(struct ras_core_context *ras_core)
+{
+ struct ras_aca *aca = &ras_core->ras_aca;
+
+ if (!aca)
+ return;
+
+ if ((aca->ue_updated_mark & ACA_MARK_FATAL_FLAG) &&
+ (aca->ue_updated_mark & ACA_MARK_UE_READ_FLAG))
+ aca->ue_updated_mark = 0;
+}
+
+static int aca_banks_update(struct ras_core_context *ras_core,
+ u32 ecc_type, void *data)
+{
+ struct aca_bank_reg bank;
+ struct aca_block *aca_blk;
+ struct aca_bank_ecc bank_ecc;
+ struct ras_log_batch_tag *batch_tag = NULL;
+ u32 count = 0;
+ int ret = 0;
+ int i;
+
+ mutex_lock(&ras_core->ras_aca.bank_op_lock);
+
+ if (aca_dup_update_ue_in_fatal(ras_core, ecc_type))
+ goto out;
+
+ ret = aca_get_bank_count(ras_core, ecc_type, &count);
+ if (ret)
+ goto out;
+
+ if (!count)
+ goto out;
+
+ batch_tag = ras_log_ring_create_batch_tag(ras_core);
+ for (i = 0; i < count; i++) {
+ memset(&bank, 0, sizeof(bank));
+ ret = aca_dump_bank(ras_core, ecc_type, i, &bank);
+ if (ret)
+ break;
+
+ bank.ecc_type = ecc_type;
+
+ memset(&bank_ecc, 0, sizeof(bank_ecc));
+ aca_blk = aca_get_bank_aca_block(ras_core, &bank);
+ if (aca_blk)
+ ret = aca_parse_bank(ras_core, aca_blk, &bank, &bank_ecc);
+
+ bank.seq_no = aca_get_bank_seqno(ras_core, ecc_type, aca_blk, &bank_ecc);
+
+ aca_log_bank_data(ras_core, &bank, &bank_ecc, batch_tag);
+ aca_bank_log(ras_core, i, count, &bank, &bank_ecc);
+
+ if (!ret && aca_blk)
+ ret = aca_log_bad_bank(ras_core, aca_blk, &bank, &bank_ecc);
+
+ if (ret)
+ break;
+ }
+ ras_log_ring_destroy_batch_tag(ras_core, batch_tag);
+
+out:
+ mutex_unlock(&ras_core->ras_aca.bank_op_lock);
+ return ret;
+}
+
+int ras_aca_update_ecc(struct ras_core_context *ras_core, u32 type, void *data)
+{
+ /* Update aca bank to aca source error_cache first */
+ return aca_banks_update(ras_core, type, data);
+}
+
+static struct aca_block *ras_aca_get_block_handle(struct ras_core_context *ras_core, uint32_t blk)
+{
+ return &ras_core->ras_aca.aca_blk[blk];
+}
+
+static int ras_aca_clear_block_ecc_count(struct ras_core_context *ras_core, u32 blk)
+{
+ struct aca_block *aca_blk;
+ struct aca_aid_ecc *aid_ecc;
+ int skt, aid, xcd;
+
+ mutex_lock(&ras_core->ras_aca.aca_lock);
+ aca_blk = ras_aca_get_block_handle(ras_core, blk);
+ for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) {
+ for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) {
+ aid_ecc = &aca_blk->ecc.socket[skt].aid[aid];
+ if (blk == RAS_BLOCK_ID__GFX) {
+ for (xcd = 0; xcd < aid_ecc->xcd.xcd_num; xcd++)
+ memset(&aid_ecc->xcd.xcd[xcd],
+ 0, sizeof(struct aca_xcd_ecc));
+ } else {
+ memset(&aid_ecc->ecc_err, 0, sizeof(aid_ecc->ecc_err));
+ }
+ }
+ }
+ mutex_unlock(&ras_core->ras_aca.aca_lock);
+
+ return 0;
+}
+
+int ras_aca_clear_all_blocks_ecc_count(struct ras_core_context *ras_core)
+{
+ enum ras_block_id blk;
+ int ret;
+
+ for (blk = RAS_BLOCK_ID__UMC; blk < RAS_BLOCK_ID__LAST; blk++) {
+ ret = ras_aca_clear_block_ecc_count(ras_core, blk);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+int ras_aca_clear_block_new_ecc_count(struct ras_core_context *ras_core, u32 blk)
+{
+ struct aca_block *aca_blk;
+ int skt, aid, xcd;
+ struct aca_ecc_count *ecc_err;
+ struct aca_aid_ecc *aid_ecc;
+
+ mutex_lock(&ras_core->ras_aca.aca_lock);
+ aca_blk = ras_aca_get_block_handle(ras_core, blk);
+ for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) {
+ for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) {
+ aid_ecc = &aca_blk->ecc.socket[skt].aid[aid];
+ if (blk == RAS_BLOCK_ID__GFX) {
+ for (xcd = 0; xcd < aid_ecc->xcd.xcd_num; xcd++) {
+ ecc_err = &aid_ecc->xcd.xcd[xcd].ecc_err;
+ ecc_err->new_ce_count = 0;
+ ecc_err->new_ue_count = 0;
+ ecc_err->new_de_count = 0;
+ }
+ } else {
+ ecc_err = &aid_ecc->ecc_err;
+ ecc_err->new_ce_count = 0;
+ ecc_err->new_ue_count = 0;
+ ecc_err->new_de_count = 0;
+ }
+ }
+ }
+ mutex_unlock(&ras_core->ras_aca.aca_lock);
+
+ return 0;
+}
+
+static int ras_aca_get_block_each_aid_ecc_count(struct ras_core_context *ras_core,
+ u32 blk, u32 skt, u32 aid, u32 xcd,
+ struct aca_ecc_count *ecc_count)
+{
+ struct aca_block *aca_blk;
+ struct aca_ecc_count *ecc_err;
+
+ aca_blk = ras_aca_get_block_handle(ras_core, blk);
+ if (blk == RAS_BLOCK_ID__GFX)
+ ecc_err = &aca_blk->ecc.socket[skt].aid[aid].xcd.xcd[xcd].ecc_err;
+ else
+ ecc_err = &aca_blk->ecc.socket[skt].aid[aid].ecc_err;
+
+ ecc_count->new_ce_count = ecc_err->new_ce_count;
+ ecc_count->total_ce_count = ecc_err->total_ce_count;
+ ecc_count->new_ue_count = ecc_err->new_ue_count;
+ ecc_count->total_ue_count = ecc_err->total_ue_count;
+ ecc_count->new_de_count = ecc_err->new_de_count;
+ ecc_count->total_de_count = ecc_err->total_de_count;
+
+ return 0;
+}
+
+static inline void _add_ecc_count(struct aca_ecc_count *des, struct aca_ecc_count *src)
+{
+ des->new_ce_count += src->new_ce_count;
+ des->total_ce_count += src->total_ce_count;
+ des->new_ue_count += src->new_ue_count;
+ des->total_ue_count += src->total_ue_count;
+ des->new_de_count += src->new_de_count;
+ des->total_de_count += src->total_de_count;
+}
+
+static const struct ras_aca_ip_func *aca_get_ip_func(
+ struct ras_core_context *ras_core, uint32_t ip_version)
+{
+ switch (ip_version) {
+ case IP_VERSION(1, 0, 0):
+ return &ras_aca_func_v1_0;
+ default:
+ RAS_DEV_ERR(ras_core->dev,
+ "ACA ip version(0x%x) is not supported!\n", ip_version);
+ break;
+ }
+
+ return NULL;
+}
+
+int ras_aca_get_block_ecc_count(struct ras_core_context *ras_core,
+ u32 blk, void *data)
+{
+ struct ras_ecc_count *err_data = (struct ras_ecc_count *)data;
+ struct aca_block *aca_blk;
+ int skt, aid, xcd;
+ struct aca_ecc_count ecc_xcd;
+ struct aca_ecc_count ecc_aid;
+ struct aca_ecc_count ecc;
+
+ if (blk >= RAS_BLOCK_ID__LAST)
+ return -EINVAL;
+
+ if (!err_data)
+ return -EINVAL;
+
+ aca_blk = ras_aca_get_block_handle(ras_core, blk);
+ memset(&ecc, 0, sizeof(ecc));
+
+ mutex_lock(&ras_core->ras_aca.aca_lock);
+ if (blk == RAS_BLOCK_ID__GFX) {
+ for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) {
+ for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) {
+ memset(&ecc_aid, 0, sizeof(ecc_aid));
+ for (xcd = 0;
+ xcd < aca_blk->ecc.socket[skt].aid[aid].xcd.xcd_num;
+ xcd++) {
+ memset(&ecc_xcd, 0, sizeof(ecc_xcd));
+ if (ras_aca_get_block_each_aid_ecc_count(ras_core,
+ blk, skt, aid, xcd, &ecc_xcd))
+ continue;
+ _add_ecc_count(&ecc_aid, &ecc_xcd);
+ }
+ _add_ecc_count(&ecc, &ecc_aid);
+ }
+ }
+ } else {
+ for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) {
+ for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++) {
+ memset(&ecc_aid, 0, sizeof(ecc_aid));
+ if (ras_aca_get_block_each_aid_ecc_count(ras_core,
+ blk, skt, aid, 0, &ecc_aid))
+ continue;
+ _add_ecc_count(&ecc, &ecc_aid);
+ }
+ }
+ }
+
+ err_data->new_ce_count = ecc.new_ce_count;
+ err_data->total_ce_count = ecc.total_ce_count;
+ err_data->new_ue_count = ecc.new_ue_count;
+ err_data->total_ue_count = ecc.total_ue_count;
+ err_data->new_de_count = ecc.new_de_count;
+ err_data->total_de_count = ecc.total_de_count;
+ mutex_unlock(&ras_core->ras_aca.aca_lock);
+
+ return 0;
+}
+
+int ras_aca_sw_init(struct ras_core_context *ras_core)
+{
+ struct ras_aca *ras_aca = &ras_core->ras_aca;
+ struct ras_aca_config *aca_cfg = &ras_core->config->aca_cfg;
+ struct aca_block *aca_blk;
+ uint32_t socket_num_per_hive;
+ uint32_t aid_num_per_socket;
+ uint32_t xcd_num_per_aid;
+ int blk, skt, aid;
+
+ socket_num_per_hive = aca_cfg->socket_num_per_hive;
+ aid_num_per_socket = aca_cfg->aid_num_per_socket;
+ xcd_num_per_aid = aca_cfg->xcd_num_per_aid;
+
+ if (!xcd_num_per_aid || !aid_num_per_socket ||
+ (socket_num_per_hive > MAX_SOCKET_NUM_PER_HIVE) ||
+ (aid_num_per_socket > MAX_AID_NUM_PER_SOCKET) ||
+ (xcd_num_per_aid > MAX_XCD_NUM_PER_AID)) {
+ RAS_DEV_ERR(ras_core->dev, "Invalid ACA system configuration: %d, %d, %d\n",
+ socket_num_per_hive, aid_num_per_socket, xcd_num_per_aid);
+ return -EINVAL;
+ }
+
+ memset(ras_aca, 0, sizeof(*ras_aca));
+
+ for (blk = 0; blk < RAS_BLOCK_ID__LAST; blk++) {
+ aca_blk = &ras_aca->aca_blk[blk];
+ aca_blk->ecc.socket_num_per_hive = socket_num_per_hive;
+ for (skt = 0; skt < aca_blk->ecc.socket_num_per_hive; skt++) {
+ aca_blk->ecc.socket[skt].aid_num = aid_num_per_socket;
+ if (blk == RAS_BLOCK_ID__GFX) {
+ for (aid = 0; aid < aca_blk->ecc.socket[skt].aid_num; aid++)
+ aca_blk->ecc.socket[skt].aid[aid].xcd.xcd_num =
+ xcd_num_per_aid;
+ }
+ }
+ }
+
+ mutex_init(&ras_aca->aca_lock);
+ mutex_init(&ras_aca->bank_op_lock);
+
+ return 0;
+}
+
+int ras_aca_sw_fini(struct ras_core_context *ras_core)
+{
+ struct ras_aca *ras_aca = &ras_core->ras_aca;
+
+ mutex_destroy(&ras_aca->aca_lock);
+ mutex_destroy(&ras_aca->bank_op_lock);
+
+ return 0;
+}
+
+int ras_aca_hw_init(struct ras_core_context *ras_core)
+{
+ struct ras_aca *ras_aca = &ras_core->ras_aca;
+ struct aca_block *aca_blk;
+ const struct ras_aca_ip_func *ip_func;
+ int i;
+
+ ras_aca->aca_ip_version = ras_core->config->aca_ip_version;
+ ip_func = aca_get_ip_func(ras_core, ras_aca->aca_ip_version);
+ if (!ip_func)
+ return -EINVAL;
+
+ for (i = 0; i < ip_func->block_num; i++) {
+ aca_blk = &ras_aca->aca_blk[ip_func->block_info[i]->ras_block_id];
+ aca_blk->blk_info = ip_func->block_info[i];
+ }
+
+ ras_aca->ue_updated_mark = 0;
+
+ return 0;
+}
+
+int ras_aca_hw_fini(struct ras_core_context *ras_core)
+{
+ struct ras_aca *ras_aca = &ras_core->ras_aca;
+
+ ras_aca->ue_updated_mark = 0;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca.h b/drivers/gpu/drm/amd/ras/rascore/ras_aca.h
new file mode 100644
index 000000000000..f61b02a5f0fc
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_ACA_H__
+#define __RAS_ACA_H__
+#include "ras.h"
+
+#define MAX_SOCKET_NUM_PER_HIVE 8
+#define MAX_AID_NUM_PER_SOCKET 4
+#define MAX_XCD_NUM_PER_AID 2
+#define MAX_ACA_RAS_BLOCK 20
+
+#define ACA_ERROR__UE_MASK (0x1 << RAS_ERR_TYPE__UE)
+#define ACA_ERROR__CE_MASK (0x1 << RAS_ERR_TYPE__CE)
+#define ACA_ERROR__DE_MASK (0x1 << RAS_ERR_TYPE__DE)
+
+enum ras_aca_reg_idx {
+ ACA_REG_IDX__CTL = 0,
+ ACA_REG_IDX__STATUS = 1,
+ ACA_REG_IDX__ADDR = 2,
+ ACA_REG_IDX__MISC0 = 3,
+ ACA_REG_IDX__CONFG = 4,
+ ACA_REG_IDX__IPID = 5,
+ ACA_REG_IDX__SYND = 6,
+ ACA_REG_IDX__DESTAT = 8,
+ ACA_REG_IDX__DEADDR = 9,
+ ACA_REG_IDX__CTL_MASK = 10,
+ ACA_REG_MAX_COUNT = 16,
+};
+
+struct ras_core_context;
+struct aca_block;
+
+struct aca_bank_reg {
+ u32 ecc_type;
+ u64 seq_no;
+ u64 regs[ACA_REG_MAX_COUNT];
+};
+
+enum aca_ecc_hwip {
+ ACA_ECC_HWIP__UNKNOWN = -1,
+ ACA_ECC_HWIP__PSP = 0,
+ ACA_ECC_HWIP__UMC,
+ ACA_ECC_HWIP__SMU,
+ ACA_ECC_HWIP__PCS_XGMI,
+ ACA_ECC_HWIP_COUNT,
+};
+
+struct aca_ecc_info {
+ int die_id;
+ int socket_id;
+ int xcd_id;
+ int hwid;
+ int mcatype;
+ uint64_t status;
+ uint64_t ipid;
+ uint64_t addr;
+};
+
+struct aca_bank_ecc {
+ struct aca_ecc_info bank_info;
+ u32 ce_count;
+ u32 ue_count;
+ u32 de_count;
+};
+
+struct aca_ecc_count {
+ u32 new_ce_count;
+ u32 total_ce_count;
+ u32 new_ue_count;
+ u32 total_ue_count;
+ u32 new_de_count;
+ u32 total_de_count;
+};
+
+struct aca_xcd_ecc {
+ struct aca_ecc_count ecc_err;
+};
+
+struct aca_aid_ecc {
+ union {
+ struct aca_xcd {
+ struct aca_xcd_ecc xcd[MAX_XCD_NUM_PER_AID];
+ u32 xcd_num;
+ } xcd;
+ struct aca_ecc_count ecc_err;
+ };
+};
+
+struct aca_socket_ecc {
+ struct aca_aid_ecc aid[MAX_AID_NUM_PER_SOCKET];
+ u32 aid_num;
+};
+
+struct aca_block_ecc {
+ struct aca_socket_ecc socket[MAX_SOCKET_NUM_PER_HIVE];
+ u32 socket_num_per_hive;
+};
+
+struct aca_bank_hw_ops {
+ bool (*bank_match)(struct aca_block *ras_blk, void *data);
+ int (*bank_parse)(struct ras_core_context *ras_core,
+ struct aca_block *aca_blk, void *data, void *buf);
+};
+
+struct aca_block_info {
+ char name[32];
+ u32 ras_block_id;
+ enum aca_ecc_hwip hwip;
+ struct aca_bank_hw_ops bank_ops;
+ u32 mask;
+};
+
+struct aca_block {
+ const struct aca_block_info *blk_info;
+ struct aca_block_ecc ecc;
+};
+
+struct ras_aca_ip_func {
+ uint32_t block_num;
+ const struct aca_block_info **block_info;
+};
+
+struct ras_aca {
+ uint32_t aca_ip_version;
+ const struct ras_aca_ip_func *ip_func;
+ struct mutex aca_lock;
+ struct mutex bank_op_lock;
+ struct aca_block aca_blk[MAX_ACA_RAS_BLOCK];
+ uint32_t ue_updated_mark;
+};
+
+int ras_aca_sw_init(struct ras_core_context *ras_core);
+int ras_aca_sw_fini(struct ras_core_context *ras_core);
+int ras_aca_hw_init(struct ras_core_context *ras_core);
+int ras_aca_hw_fini(struct ras_core_context *ras_core);
+int ras_aca_get_block_ecc_count(struct ras_core_context *ras_core, u32 blk, void *data);
+int ras_aca_clear_block_new_ecc_count(struct ras_core_context *ras_core, u32 blk);
+int ras_aca_clear_all_blocks_ecc_count(struct ras_core_context *ras_core);
+int ras_aca_update_ecc(struct ras_core_context *ras_core, u32 ecc_type, void *data);
+void ras_aca_mark_fatal_flag(struct ras_core_context *ras_core);
+void ras_aca_clear_fatal_flag(struct ras_core_context *ras_core);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c
new file mode 100644
index 000000000000..29df98948703
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_aca.h"
+#include "ras_core_status.h"
+#include "ras_aca_v1_0.h"
+
+struct ras_aca_hwip {
+ int hwid;
+ int mcatype;
+};
+
+static struct ras_aca_hwip aca_hwid_mcatypes[ACA_ECC_HWIP_COUNT] = {
+ [ACA_ECC_HWIP__SMU] = {0x01, 0x01},
+ [ACA_ECC_HWIP__PCS_XGMI] = {0x50, 0x00},
+ [ACA_ECC_HWIP__UMC] = {0x96, 0x00},
+};
+
+static int aca_decode_bank_info(struct aca_block *aca_blk,
+ struct aca_bank_reg *bank, struct aca_ecc_info *info)
+{
+ u64 ipid;
+ u32 instidhi, instidlo;
+
+ ipid = bank->regs[ACA_REG_IDX__IPID];
+ info->hwid = ACA_REG_IPID_HARDWAREID(ipid);
+ info->mcatype = ACA_REG_IPID_MCATYPE(ipid);
+ /*
+ * Unified DieID Format: SAASS. A:AID, S:Socket.
+ * Unified DieID[4:4] = InstanceId[0:0]
+ * Unified DieID[0:3] = InstanceIdHi[0:3]
+ */
+ instidhi = ACA_REG_IPID_INSTANCEIDHI(ipid);
+ instidlo = ACA_REG_IPID_INSTANCEIDLO(ipid);
+ info->die_id = ((instidhi >> 2) & 0x03);
+ info->socket_id = ((instidlo & 0x1) << 2) | (instidhi & 0x03);
+
+ if ((aca_blk->blk_info->hwip == ACA_ECC_HWIP__SMU) &&
+ (aca_blk->blk_info->ras_block_id == RAS_BLOCK_ID__GFX))
+ info->xcd_id =
+ ((instidlo & GENMASK_ULL(31, 1)) == mmSMNAID_XCD0_MCA_SMU) ? 0 : 1;
+
+ return 0;
+}
+
+static bool aca_check_bank_hwip(struct aca_bank_reg *bank, enum aca_ecc_hwip type)
+{
+ struct ras_aca_hwip *hwip;
+ int hwid, mcatype;
+ u64 ipid;
+
+ if (!bank || (type == ACA_ECC_HWIP__UNKNOWN))
+ return false;
+
+ hwip = &aca_hwid_mcatypes[type];
+ if (!hwip->hwid)
+ return false;
+
+ ipid = bank->regs[ACA_REG_IDX__IPID];
+ hwid = ACA_REG_IPID_HARDWAREID(ipid);
+ mcatype = ACA_REG_IPID_MCATYPE(ipid);
+
+ return hwip->hwid == hwid && hwip->mcatype == mcatype;
+}
+
+static bool aca_match_bank_default(struct aca_block *aca_blk, void *data)
+{
+ return aca_check_bank_hwip((struct aca_bank_reg *)data, aca_blk->blk_info->hwip);
+}
+
+static bool aca_match_gfx_bank(struct aca_block *aca_blk, void *data)
+{
+ struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
+ u32 instlo;
+
+ if (!aca_check_bank_hwip(bank, aca_blk->blk_info->hwip))
+ return false;
+
+ instlo = ACA_REG_IPID_INSTANCEIDLO(bank->regs[ACA_REG_IDX__IPID]);
+ instlo &= GENMASK_ULL(31, 1);
+ switch (instlo) {
+ case mmSMNAID_XCD0_MCA_SMU:
+ case mmSMNAID_XCD1_MCA_SMU:
+ case mmSMNXCD_XCD0_MCA_SMU:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool aca_match_sdma_bank(struct aca_block *aca_blk, void *data)
+{
+ struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
+ /* CODE_SDMA0 - CODE_SDMA4, reference to smu driver if header file */
+ static int sdma_err_codes[] = { 33, 34, 35, 36 };
+ u32 instlo;
+ int errcode, i;
+
+ if (!aca_check_bank_hwip(bank, aca_blk->blk_info->hwip))
+ return false;
+
+ instlo = ACA_REG_IPID_INSTANCEIDLO(bank->regs[ACA_REG_IDX__IPID]);
+ instlo &= GENMASK_ULL(31, 1);
+ if (instlo != mmSMNAID_AID0_MCA_SMU)
+ return false;
+
+ errcode = ACA_REG_SYND_ERRORINFORMATION(bank->regs[ACA_REG_IDX__SYND]);
+ errcode &= 0xff;
+
+ /* Check SDMA error codes */
+ for (i = 0; i < ARRAY_SIZE(sdma_err_codes); i++) {
+ if (errcode == sdma_err_codes[i])
+ return true;
+ }
+
+ return false;
+}
+
+static bool aca_match_mmhub_bank(struct aca_block *aca_blk, void *data)
+{
+ struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
+ /* reference to smu driver if header file */
+ const int mmhub_err_codes[] = {
+ 0, 1, 2, 3, 4, /* CODE_DAGB0 - 4 */
+ 5, 6, 7, 8, 9, /* CODE_EA0 - 4 */
+ 10, /* CODE_UTCL2_ROUTER */
+ 11, /* CODE_VML2 */
+ 12, /* CODE_VML2_WALKER */
+ 13, /* CODE_MMCANE */
+ };
+ u32 instlo;
+ int errcode, i;
+
+ if (!aca_check_bank_hwip(bank, aca_blk->blk_info->hwip))
+ return false;
+
+ instlo = ACA_REG_IPID_INSTANCEIDLO(bank->regs[ACA_REG_IDX__IPID]);
+ instlo &= GENMASK_ULL(31, 1);
+ if (instlo != mmSMNAID_AID0_MCA_SMU)
+ return false;
+
+ errcode = ACA_REG_SYND_ERRORINFORMATION(bank->regs[ACA_REG_IDX__SYND]);
+ errcode &= 0xff;
+
+ /* Check MMHUB error codes */
+ for (i = 0; i < ARRAY_SIZE(mmhub_err_codes); i++) {
+ if (errcode == mmhub_err_codes[i])
+ return true;
+ }
+
+ return false;
+}
+
+static bool aca_check_umc_de(struct ras_core_context *ras_core, uint64_t mc_umc_status)
+{
+ return (ras_core->poison_supported &&
+ ACA_REG_STATUS_VAL(mc_umc_status) &&
+ ACA_REG_STATUS_DEFERRED(mc_umc_status));
+}
+
+static bool aca_check_umc_ue(struct ras_core_context *ras_core, uint64_t mc_umc_status)
+{
+ if (aca_check_umc_de(ras_core, mc_umc_status))
+ return false;
+
+ return (ACA_REG_STATUS_VAL(mc_umc_status) &&
+ (ACA_REG_STATUS_PCC(mc_umc_status) ||
+ ACA_REG_STATUS_UC(mc_umc_status) ||
+ ACA_REG_STATUS_TCC(mc_umc_status)));
+}
+
+static bool aca_check_umc_ce(struct ras_core_context *ras_core, uint64_t mc_umc_status)
+{
+ if (aca_check_umc_de(ras_core, mc_umc_status))
+ return false;
+
+ return (ACA_REG_STATUS_VAL(mc_umc_status) &&
+ (ACA_REG_STATUS_CECC(mc_umc_status) ||
+ (ACA_REG_STATUS_UECC(mc_umc_status) &&
+ ACA_REG_STATUS_UC(mc_umc_status) == 0) ||
+ /* Identify data parity error in replay mode */
+ ((ACA_REG_STATUS_ERRORCODEEXT(mc_umc_status) == 0x5 ||
+ ACA_REG_STATUS_ERRORCODEEXT(mc_umc_status) == 0xb) &&
+ !(aca_check_umc_ue(ras_core, mc_umc_status)))));
+}
+
+static int aca_parse_umc_bank(struct ras_core_context *ras_core,
+ struct aca_block *ras_blk, void *data, void *buf)
+{
+ struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
+ struct aca_bank_ecc *ecc = (struct aca_bank_ecc *)buf;
+ struct aca_ecc_info bank_info;
+ uint32_t ext_error_code;
+ uint64_t status0;
+
+ status0 = bank->regs[ACA_REG_IDX__STATUS];
+ if (!ACA_REG_STATUS_VAL(status0))
+ return 0;
+
+ memset(&bank_info, 0, sizeof(bank_info));
+ aca_decode_bank_info(ras_blk, bank, &bank_info);
+ memcpy(&ecc->bank_info, &bank_info, sizeof(bank_info));
+ ecc->bank_info.status = bank->regs[ACA_REG_IDX__STATUS];
+ ecc->bank_info.ipid = bank->regs[ACA_REG_IDX__IPID];
+ ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR];
+
+ ext_error_code = ACA_REG_STATUS_ERRORCODEEXT(status0);
+
+ if (aca_check_umc_de(ras_core, status0))
+ ecc->de_count = 1;
+ else if (aca_check_umc_ue(ras_core, status0))
+ ecc->ue_count = ext_error_code ?
+ 1 : ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]);
+ else if (aca_check_umc_ce(ras_core, status0))
+ ecc->ce_count = ext_error_code ?
+ 1 : ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]);
+
+ return 0;
+}
+
+static bool aca_check_bank_is_de(struct ras_core_context *ras_core,
+ uint64_t status)
+{
+ return (ACA_REG_STATUS_POISON(status) ||
+ ACA_REG_STATUS_DEFERRED(status));
+}
+
+static int aca_parse_bank_default(struct ras_core_context *ras_core,
+ struct aca_block *ras_blk,
+ void *data, void *buf)
+{
+ struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
+ struct aca_bank_ecc *ecc = (struct aca_bank_ecc *)buf;
+ struct aca_ecc_info bank_info;
+ u64 misc0 = bank->regs[ACA_REG_IDX__MISC0];
+ u64 status = bank->regs[ACA_REG_IDX__STATUS];
+
+ memset(&bank_info, 0, sizeof(bank_info));
+ aca_decode_bank_info(ras_blk, bank, &bank_info);
+ memcpy(&ecc->bank_info, &bank_info, sizeof(bank_info));
+ ecc->bank_info.status = status;
+ ecc->bank_info.ipid = bank->regs[ACA_REG_IDX__IPID];
+ ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR];
+
+ if (aca_check_bank_is_de(ras_core, status)) {
+ ecc->de_count = 1;
+ } else {
+ if (bank->ecc_type == RAS_ERR_TYPE__UE)
+ ecc->ue_count = 1;
+ else if (bank->ecc_type == RAS_ERR_TYPE__CE)
+ ecc->ce_count = ACA_REG_MISC0_ERRCNT(misc0);
+ }
+
+ return 0;
+}
+
+static int aca_parse_xgmi_bank(struct ras_core_context *ras_core,
+ struct aca_block *ras_blk,
+ void *data, void *buf)
+{
+ struct aca_bank_reg *bank = (struct aca_bank_reg *)data;
+ struct aca_bank_ecc *ecc = (struct aca_bank_ecc *)buf;
+ struct aca_ecc_info bank_info;
+ u64 status, count;
+ int ext_error_code;
+
+ memset(&bank_info, 0, sizeof(bank_info));
+ aca_decode_bank_info(ras_blk, bank, &bank_info);
+ memcpy(&ecc->bank_info, &bank_info, sizeof(bank_info));
+ ecc->bank_info.status = bank->regs[ACA_REG_IDX__STATUS];
+ ecc->bank_info.ipid = bank->regs[ACA_REG_IDX__IPID];
+ ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR];
+
+ status = bank->regs[ACA_REG_IDX__STATUS];
+ ext_error_code = ACA_REG_STATUS_ERRORCODEEXT(status);
+
+ count = ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]);
+ if (bank->ecc_type == RAS_ERR_TYPE__UE) {
+ if (ext_error_code != 0 && ext_error_code != 9)
+ count = 0ULL;
+ ecc->ue_count = count;
+ } else if (bank->ecc_type == RAS_ERR_TYPE__CE) {
+ count = ext_error_code == 6 ? count : 0ULL;
+ ecc->ce_count = count;
+ }
+
+ return 0;
+}
+
+static const struct aca_block_info aca_v1_0_umc = {
+ .name = "umc",
+ .ras_block_id = RAS_BLOCK_ID__UMC,
+ .hwip = ACA_ECC_HWIP__UMC,
+ .mask = ACA_ERROR__UE_MASK | ACA_ERROR__CE_MASK | ACA_ERROR__DE_MASK,
+ .bank_ops = {
+ .bank_match = aca_match_bank_default,
+ .bank_parse = aca_parse_umc_bank,
+ },
+};
+
+static const struct aca_block_info aca_v1_0_gfx = {
+ .name = "gfx",
+ .ras_block_id = RAS_BLOCK_ID__GFX,
+ .hwip = ACA_ECC_HWIP__SMU,
+ .mask = ACA_ERROR__UE_MASK | ACA_ERROR__CE_MASK,
+ .bank_ops = {
+ .bank_match = aca_match_gfx_bank,
+ .bank_parse = aca_parse_bank_default,
+ },
+};
+
+static const struct aca_block_info aca_v1_0_sdma = {
+ .name = "sdma",
+ .ras_block_id = RAS_BLOCK_ID__SDMA,
+ .hwip = ACA_ECC_HWIP__SMU,
+ .mask = ACA_ERROR__UE_MASK,
+ .bank_ops = {
+ .bank_match = aca_match_sdma_bank,
+ .bank_parse = aca_parse_bank_default,
+ },
+};
+
+static const struct aca_block_info aca_v1_0_mmhub = {
+ .name = "mmhub",
+ .ras_block_id = RAS_BLOCK_ID__MMHUB,
+ .hwip = ACA_ECC_HWIP__SMU,
+ .mask = ACA_ERROR__UE_MASK,
+ .bank_ops = {
+ .bank_match = aca_match_mmhub_bank,
+ .bank_parse = aca_parse_bank_default,
+ },
+};
+
+static const struct aca_block_info aca_v1_0_xgmi = {
+ .name = "xgmi",
+ .ras_block_id = RAS_BLOCK_ID__XGMI_WAFL,
+ .hwip = ACA_ECC_HWIP__PCS_XGMI,
+ .mask = ACA_ERROR__UE_MASK | ACA_ERROR__CE_MASK,
+ .bank_ops = {
+ .bank_match = aca_match_bank_default,
+ .bank_parse = aca_parse_xgmi_bank,
+ },
+};
+
+static const struct aca_block_info *aca_block_info_v1_0[] = {
+ &aca_v1_0_umc,
+ &aca_v1_0_gfx,
+ &aca_v1_0_sdma,
+ &aca_v1_0_mmhub,
+ &aca_v1_0_xgmi,
+};
+
+const struct ras_aca_ip_func ras_aca_func_v1_0 = {
+ .block_num = ARRAY_SIZE(aca_block_info_v1_0),
+ .block_info = aca_block_info_v1_0,
+};
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.h b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.h
new file mode 100644
index 000000000000..40e5d94b037f
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_ACA_V1_0_H__
+#define __RAS_ACA_V1_0_H__
+#include "ras.h"
+
+#define ACA__REG__FIELD(x, h, l) (((x) & GENMASK_ULL(h, l)) >> l)
+#define ACA_REG_STATUS_VAL(x) ACA__REG__FIELD(x, 63, 63)
+#define ACA_REG_STATUS_OVERFLOW(x) ACA__REG__FIELD(x, 62, 62)
+#define ACA_REG_STATUS_UC(x) ACA__REG__FIELD(x, 61, 61)
+#define ACA_REG_STATUS_EN(x) ACA__REG__FIELD(x, 60, 60)
+#define ACA_REG_STATUS_MISCV(x) ACA__REG__FIELD(x, 59, 59)
+#define ACA_REG_STATUS_ADDRV(x) ACA__REG__FIELD(x, 58, 58)
+#define ACA_REG_STATUS_PCC(x) ACA__REG__FIELD(x, 57, 57)
+#define ACA_REG_STATUS_ERRCOREIDVAL(x) ACA__REG__FIELD(x, 56, 56)
+#define ACA_REG_STATUS_TCC(x) ACA__REG__FIELD(x, 55, 55)
+#define ACA_REG_STATUS_SYNDV(x) ACA__REG__FIELD(x, 53, 53)
+#define ACA_REG_STATUS_CECC(x) ACA__REG__FIELD(x, 46, 46)
+#define ACA_REG_STATUS_UECC(x) ACA__REG__FIELD(x, 45, 45)
+#define ACA_REG_STATUS_DEFERRED(x) ACA__REG__FIELD(x, 44, 44)
+#define ACA_REG_STATUS_POISON(x) ACA__REG__FIELD(x, 43, 43)
+#define ACA_REG_STATUS_SCRUB(x) ACA__REG__FIELD(x, 40, 40)
+#define ACA_REG_STATUS_ERRCOREID(x) ACA__REG__FIELD(x, 37, 32)
+#define ACA_REG_STATUS_ADDRLSB(x) ACA__REG__FIELD(x, 29, 24)
+#define ACA_REG_STATUS_ERRORCODEEXT(x) ACA__REG__FIELD(x, 21, 16)
+#define ACA_REG_STATUS_ERRORCODE(x) ACA__REG__FIELD(x, 15, 0)
+
+#define ACA_REG_IPID_MCATYPE(x) ACA__REG__FIELD(x, 63, 48)
+#define ACA_REG_IPID_INSTANCEIDHI(x) ACA__REG__FIELD(x, 47, 44)
+#define ACA_REG_IPID_HARDWAREID(x) ACA__REG__FIELD(x, 43, 32)
+#define ACA_REG_IPID_INSTANCEIDLO(x) ACA__REG__FIELD(x, 31, 0)
+
+#define ACA_REG_MISC0_VALID(x) ACA__REG__FIELD(x, 63, 63)
+#define ACA_REG_MISC0_OVRFLW(x) ACA__REG__FIELD(x, 48, 48)
+#define ACA_REG_MISC0_ERRCNT(x) ACA__REG__FIELD(x, 43, 32)
+
+#define ACA_REG_SYND_ERRORINFORMATION(x) ACA__REG__FIELD(x, 17, 0)
+
+/* NOTE: The following codes refers to the smu header file */
+#define ACA_EXTERROR_CODE_CE 0x3a
+#define ACA_EXTERROR_CODE_FAULT 0x3b
+
+#define mmSMNAID_XCD0_MCA_SMU 0x36430400 /* SMN AID XCD0 */
+#define mmSMNAID_XCD1_MCA_SMU 0x38430400 /* SMN AID XCD1 */
+#define mmSMNXCD_XCD0_MCA_SMU 0x40430400 /* SMN XCD XCD0 */
+#define mmSMNAID_AID0_MCA_SMU 0x03b30400 /* SMN AID AID0 */
+
+extern const struct ras_aca_ip_func ras_aca_func_v1_0;
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_cmd.c b/drivers/gpu/drm/amd/ras/rascore/ras_cmd.c
new file mode 100644
index 000000000000..94e6d7420d94
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_cmd.c
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_cmd.h"
+
+#define RAS_CMD_MAJOR_VERSION 6
+#define RAS_CMD_MINOR_VERSION 0
+#define RAS_CMD_VERSION (((RAS_CMD_MAJOR_VERSION) << 10) | (RAS_CMD_MINOR_VERSION))
+
+static int ras_cmd_add_device(struct ras_core_context *ras_core)
+{
+ INIT_LIST_HEAD(&ras_core->ras_cmd.head);
+ ras_core->ras_cmd.ras_core = ras_core;
+ ras_core->ras_cmd.dev_handle = (uintptr_t)ras_core ^ RAS_CMD_DEV_HANDLE_MAGIC;
+ return 0;
+}
+
+static int ras_cmd_remove_device(struct ras_core_context *ras_core)
+{
+ memset(&ras_core->ras_cmd, 0, sizeof(ras_core->ras_cmd));
+ return 0;
+}
+
+static int ras_get_block_ecc_info(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_block_ecc_info_req *input_data =
+ (struct ras_cmd_block_ecc_info_req *)cmd->input_buff_raw;
+ struct ras_cmd_block_ecc_info_rsp *output_data =
+ (struct ras_cmd_block_ecc_info_rsp *)cmd->output_buff_raw;
+ struct ras_ecc_count err_data;
+ int ret;
+
+ if (cmd->input_size != sizeof(struct ras_cmd_block_ecc_info_req))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ memset(&err_data, 0, sizeof(err_data));
+ ret = ras_aca_get_block_ecc_count(ras_core, input_data->block_id, &err_data);
+ if (ret)
+ return RAS_CMD__ERROR_GENERIC;
+
+ output_data->ce_count = err_data.total_ce_count;
+ output_data->ue_count = err_data.total_ue_count;
+ output_data->de_count = err_data.total_de_count;
+
+ cmd->output_size = sizeof(struct ras_cmd_block_ecc_info_rsp);
+ return RAS_CMD__SUCCESS;
+}
+
+static void ras_cmd_update_bad_page_info(struct ras_cmd_bad_page_record *ras_cmd_record,
+ struct eeprom_umc_record *record)
+{
+ ras_cmd_record->retired_page = record->cur_nps_retired_row_pfn;
+ ras_cmd_record->ts = record->ts;
+ ras_cmd_record->err_type = record->err_type;
+ ras_cmd_record->mem_channel = record->mem_channel;
+ ras_cmd_record->mcumc_id = record->mcumc_id;
+ ras_cmd_record->address = record->address;
+ ras_cmd_record->bank = record->bank;
+ ras_cmd_record->valid = 1;
+}
+
+static int ras_cmd_get_group_bad_pages(struct ras_core_context *ras_core,
+ uint32_t group_index, struct ras_cmd_bad_pages_info_rsp *output_data)
+{
+ struct eeprom_umc_record record;
+ struct ras_cmd_bad_page_record *ras_cmd_record;
+ uint32_t i = 0, bp_cnt = 0, group_cnt = 0;
+
+ output_data->bp_in_group = 0;
+ output_data->group_index = 0;
+
+ bp_cnt = ras_umc_get_badpage_count(ras_core);
+ if (bp_cnt) {
+ output_data->group_index = group_index;
+ group_cnt = bp_cnt / RAS_CMD_MAX_BAD_PAGES_PER_GROUP
+ + ((bp_cnt % RAS_CMD_MAX_BAD_PAGES_PER_GROUP) ? 1 : 0);
+
+ if (group_index >= group_cnt)
+ return RAS_CMD__ERROR_INVALID_INPUT_DATA;
+
+ i = group_index * RAS_CMD_MAX_BAD_PAGES_PER_GROUP;
+ for (;
+ i < bp_cnt && output_data->bp_in_group < RAS_CMD_MAX_BAD_PAGES_PER_GROUP;
+ i++) {
+ if (ras_umc_get_badpage_record(ras_core, i, &record))
+ return RAS_CMD__ERROR_GENERIC;
+
+ ras_cmd_record = &output_data->records[i % RAS_CMD_MAX_BAD_PAGES_PER_GROUP];
+
+ memset(ras_cmd_record, 0, sizeof(*ras_cmd_record));
+ ras_cmd_update_bad_page_info(ras_cmd_record, &record);
+ output_data->bp_in_group++;
+ }
+ }
+ output_data->bp_total_cnt = bp_cnt;
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_cmd_get_bad_pages(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_bad_pages_info_req *input_data =
+ (struct ras_cmd_bad_pages_info_req *)cmd->input_buff_raw;
+ struct ras_cmd_bad_pages_info_rsp *output_data =
+ (struct ras_cmd_bad_pages_info_rsp *)cmd->output_buff_raw;
+ int ret;
+
+ if (cmd->input_size != sizeof(struct ras_cmd_bad_pages_info_req))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ ret = ras_cmd_get_group_bad_pages(ras_core, input_data->group_index, output_data);
+ if (ret)
+ return RAS_CMD__ERROR_GENERIC;
+
+ output_data->version = 0;
+
+ cmd->output_size = sizeof(struct ras_cmd_bad_pages_info_rsp);
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_cmd_clear_bad_page_info(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ if (cmd->input_size != sizeof(struct ras_cmd_dev_handle))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ if (ras_eeprom_reset_table(ras_core))
+ return RAS_CMD__ERROR_GENERIC;
+
+ if (ras_umc_clean_badpage_data(ras_core))
+ return RAS_CMD__ERROR_GENERIC;
+
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_cmd_reset_all_error_counts(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ if (cmd->input_size != sizeof(struct ras_cmd_dev_handle))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ if (ras_aca_clear_all_blocks_ecc_count(ras_core))
+ return RAS_CMD__ERROR_GENERIC;
+
+ if (ras_umc_clear_logged_ecc(ras_core))
+ return RAS_CMD__ERROR_GENERIC;
+
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_cmd_get_cper_snapshot(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_cper_snapshot_rsp *output_data =
+ (struct ras_cmd_cper_snapshot_rsp *)cmd->output_buff_raw;
+ struct ras_log_batch_overview overview;
+
+ if (cmd->input_size != sizeof(struct ras_cmd_cper_snapshot_req))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ ras_log_ring_get_batch_overview(ras_core, &overview);
+
+ output_data->total_cper_num = overview.logged_batch_count;
+ output_data->start_cper_id = overview.first_batch_id;
+ output_data->latest_cper_id = overview.last_batch_id;
+
+ output_data->version = 0;
+
+ cmd->output_size = sizeof(struct ras_cmd_cper_snapshot_rsp);
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_cmd_get_cper_records(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_cper_record_req *req =
+ (struct ras_cmd_cper_record_req *)cmd->input_buff_raw;
+ struct ras_cmd_cper_record_rsp *rsp =
+ (struct ras_cmd_cper_record_rsp *)cmd->output_buff_raw;
+ struct ras_log_info *trace[MAX_RECORD_PER_BATCH] = {0};
+ struct ras_log_batch_overview overview;
+ uint32_t offset = 0, real_data_len = 0;
+ uint64_t batch_id;
+ uint8_t *buffer;
+ int ret = 0, i, count;
+
+ if (cmd->input_size != sizeof(struct ras_cmd_cper_record_req))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ if (!req->buf_size || !req->buf_ptr || !req->cper_num)
+ return RAS_CMD__ERROR_INVALID_INPUT_DATA;
+
+ buffer = kzalloc(req->buf_size, GFP_KERNEL);
+ if (!buffer)
+ return RAS_CMD__ERROR_GENERIC;
+
+ ras_log_ring_get_batch_overview(ras_core, &overview);
+ for (i = 0; i < req->cper_num; i++) {
+ batch_id = req->cper_start_id + i;
+ if (batch_id >= overview.last_batch_id)
+ break;
+
+ count = ras_log_ring_get_batch_records(ras_core, batch_id, trace,
+ ARRAY_SIZE(trace));
+ if (count > 0) {
+ ret = ras_cper_generate_cper(ras_core, trace, count,
+ &buffer[offset], req->buf_size - offset, &real_data_len);
+ if (ret)
+ break;
+
+ offset += real_data_len;
+ }
+ }
+
+ if ((ret && (ret != -ENOMEM)) ||
+ copy_to_user(u64_to_user_ptr(req->buf_ptr), buffer, offset)) {
+ kfree(buffer);
+ return RAS_CMD__ERROR_GENERIC;
+ }
+
+ rsp->real_data_size = offset;
+ rsp->real_cper_num = i;
+ rsp->remain_num = (ret == -ENOMEM) ? (req->cper_num - i) : 0;
+ rsp->version = 0;
+
+ cmd->output_size = sizeof(struct ras_cmd_cper_record_rsp);
+
+ kfree(buffer);
+
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_cmd_get_batch_trace_snapshot(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_batch_trace_snapshot_rsp *rsp =
+ (struct ras_cmd_batch_trace_snapshot_rsp *)cmd->output_buff_raw;
+ struct ras_log_batch_overview overview;
+
+
+ if (cmd->input_size != sizeof(struct ras_cmd_batch_trace_snapshot_req))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ ras_log_ring_get_batch_overview(ras_core, &overview);
+
+ rsp->total_batch_num = overview.logged_batch_count;
+ rsp->start_batch_id = overview.first_batch_id;
+ rsp->latest_batch_id = overview.last_batch_id;
+ rsp->version = 0;
+
+ cmd->output_size = sizeof(struct ras_cmd_batch_trace_snapshot_rsp);
+ return RAS_CMD__SUCCESS;
+}
+
+static int ras_cmd_get_batch_trace_records(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_batch_trace_record_req *input_data =
+ (struct ras_cmd_batch_trace_record_req *)cmd->input_buff_raw;
+ struct ras_cmd_batch_trace_record_rsp *output_data =
+ (struct ras_cmd_batch_trace_record_rsp *)cmd->output_buff_raw;
+ struct ras_log_batch_overview overview;
+ struct ras_log_info *trace_arry[MAX_RECORD_PER_BATCH] = {0};
+ struct ras_log_info *record;
+ int i, j, count = 0, offset = 0;
+ uint64_t id;
+ bool completed = false;
+
+ if (cmd->input_size != sizeof(struct ras_cmd_batch_trace_record_req))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ if ((!input_data->batch_num) || (input_data->batch_num > RAS_CMD_MAX_BATCH_NUM))
+ return RAS_CMD__ERROR_INVALID_INPUT_DATA;
+
+ ras_log_ring_get_batch_overview(ras_core, &overview);
+ if ((input_data->start_batch_id < overview.first_batch_id) ||
+ (input_data->start_batch_id >= overview.last_batch_id))
+ return RAS_CMD__ERROR_INVALID_INPUT_SIZE;
+
+ for (i = 0; i < input_data->batch_num; i++) {
+ id = input_data->start_batch_id + i;
+ if (id >= overview.last_batch_id) {
+ completed = true;
+ break;
+ }
+
+ count = ras_log_ring_get_batch_records(ras_core,
+ id, trace_arry, ARRAY_SIZE(trace_arry));
+ if (count > 0) {
+ if ((offset + count) > RAS_CMD_MAX_TRACE_NUM)
+ break;
+ for (j = 0; j < count; j++) {
+ record = &output_data->records[offset + j];
+ record->seqno = trace_arry[j]->seqno;
+ record->timestamp = trace_arry[j]->timestamp;
+ record->event = trace_arry[j]->event;
+ memcpy(&record->aca_reg,
+ &trace_arry[j]->aca_reg, sizeof(trace_arry[j]->aca_reg));
+ }
+ } else {
+ count = 0;
+ }
+
+ output_data->batchs[i].batch_id = id;
+ output_data->batchs[i].offset = offset;
+ output_data->batchs[i].trace_num = count;
+ offset += count;
+ }
+
+ output_data->start_batch_id = input_data->start_batch_id;
+ output_data->real_batch_num = i;
+ output_data->remain_num = completed ? 0 : (input_data->batch_num - i);
+ output_data->version = 0;
+
+ cmd->output_size = sizeof(struct ras_cmd_batch_trace_record_rsp);
+
+ return RAS_CMD__SUCCESS;
+}
+
+static enum ras_ta_block __get_ras_ta_block(enum ras_block_id block)
+{
+ switch (block) {
+ case RAS_BLOCK_ID__UMC:
+ return RAS_TA_BLOCK__UMC;
+ case RAS_BLOCK_ID__SDMA:
+ return RAS_TA_BLOCK__SDMA;
+ case RAS_BLOCK_ID__GFX:
+ return RAS_TA_BLOCK__GFX;
+ case RAS_BLOCK_ID__MMHUB:
+ return RAS_TA_BLOCK__MMHUB;
+ case RAS_BLOCK_ID__ATHUB:
+ return RAS_TA_BLOCK__ATHUB;
+ case RAS_BLOCK_ID__PCIE_BIF:
+ return RAS_TA_BLOCK__PCIE_BIF;
+ case RAS_BLOCK_ID__HDP:
+ return RAS_TA_BLOCK__HDP;
+ case RAS_BLOCK_ID__XGMI_WAFL:
+ return RAS_TA_BLOCK__XGMI_WAFL;
+ case RAS_BLOCK_ID__DF:
+ return RAS_TA_BLOCK__DF;
+ case RAS_BLOCK_ID__SMN:
+ return RAS_TA_BLOCK__SMN;
+ case RAS_BLOCK_ID__SEM:
+ return RAS_TA_BLOCK__SEM;
+ case RAS_BLOCK_ID__MP0:
+ return RAS_TA_BLOCK__MP0;
+ case RAS_BLOCK_ID__MP1:
+ return RAS_TA_BLOCK__MP1;
+ case RAS_BLOCK_ID__FUSE:
+ return RAS_TA_BLOCK__FUSE;
+ case RAS_BLOCK_ID__MCA:
+ return RAS_TA_BLOCK__MCA;
+ case RAS_BLOCK_ID__VCN:
+ return RAS_TA_BLOCK__VCN;
+ case RAS_BLOCK_ID__JPEG:
+ return RAS_TA_BLOCK__JPEG;
+ default:
+ return RAS_TA_BLOCK__UMC;
+ }
+}
+
+static enum ras_ta_error_type __get_ras_ta_err_type(enum ras_ecc_err_type error)
+{
+ switch (error) {
+ case RAS_ECC_ERR__NONE:
+ return RAS_TA_ERROR__NONE;
+ case RAS_ECC_ERR__PARITY:
+ return RAS_TA_ERROR__PARITY;
+ case RAS_ECC_ERR__SINGLE_CORRECTABLE:
+ return RAS_TA_ERROR__SINGLE_CORRECTABLE;
+ case RAS_ECC_ERR__MULTI_UNCORRECTABLE:
+ return RAS_TA_ERROR__MULTI_UNCORRECTABLE;
+ case RAS_ECC_ERR__POISON:
+ return RAS_TA_ERROR__POISON;
+ default:
+ return RAS_TA_ERROR__NONE;
+ }
+}
+
+static int ras_cmd_inject_error(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_inject_error_req *req =
+ (struct ras_cmd_inject_error_req *)cmd->input_buff_raw;
+ struct ras_cmd_inject_error_rsp *output_data =
+ (struct ras_cmd_inject_error_rsp *)cmd->output_buff_raw;
+ int ret = 0;
+ struct ras_ta_trigger_error_input block_info = {
+ .block_id = __get_ras_ta_block(req->block_id),
+ .sub_block_index = req->subblock_id,
+ .inject_error_type = __get_ras_ta_err_type(req->error_type),
+ .address = req->address,
+ .value = req->method,
+ };
+
+ ret = ras_psp_trigger_error(ras_core, &block_info, req->instance_mask);
+ if (!ret) {
+ output_data->version = 0;
+ output_data->address = block_info.address;
+ cmd->output_size = sizeof(struct ras_cmd_inject_error_rsp);
+ } else {
+ RAS_DEV_ERR(ras_core->dev, "ras inject block %u failed %d\n", req->block_id, ret);
+ ret = RAS_CMD__ERROR_ACCESS_DENIED;
+ }
+
+ return ret;
+}
+
+static struct ras_cmd_func_map ras_cmd_maps[] = {
+ {RAS_CMD__INJECT_ERROR, ras_cmd_inject_error},
+ {RAS_CMD__GET_BLOCK_ECC_STATUS, ras_get_block_ecc_info},
+ {RAS_CMD__GET_BAD_PAGES, ras_cmd_get_bad_pages},
+ {RAS_CMD__CLEAR_BAD_PAGE_INFO, ras_cmd_clear_bad_page_info},
+ {RAS_CMD__RESET_ALL_ERROR_COUNTS, ras_cmd_reset_all_error_counts},
+ {RAS_CMD__GET_CPER_SNAPSHOT, ras_cmd_get_cper_snapshot},
+ {RAS_CMD__GET_CPER_RECORD, ras_cmd_get_cper_records},
+ {RAS_CMD__GET_BATCH_TRACE_SNAPSHOT, ras_cmd_get_batch_trace_snapshot},
+ {RAS_CMD__GET_BATCH_TRACE_RECORD, ras_cmd_get_batch_trace_records},
+};
+
+int rascore_handle_cmd(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data)
+{
+ struct ras_cmd_func_map *ras_cmd = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ras_cmd_maps); i++) {
+ if (cmd->cmd_id == ras_cmd_maps[i].cmd_id) {
+ ras_cmd = &ras_cmd_maps[i];
+ break;
+ }
+ }
+
+ if (!ras_cmd)
+ return RAS_CMD__ERROR_UKNOWN_CMD;
+
+ return ras_cmd->func(ras_core, cmd, data);
+}
+
+int ras_cmd_init(struct ras_core_context *ras_core)
+{
+ return ras_cmd_add_device(ras_core);
+}
+
+int ras_cmd_fini(struct ras_core_context *ras_core)
+{
+ ras_cmd_remove_device(ras_core);
+ return 0;
+}
+
+int ras_cmd_query_interface_info(struct ras_core_context *ras_core,
+ struct ras_query_interface_info_rsp *rsp)
+{
+ rsp->ras_cmd_major_ver = RAS_CMD_MAJOR_VERSION;
+ rsp->ras_cmd_minor_ver = RAS_CMD_MINOR_VERSION;
+
+ return 0;
+}
+
+int ras_cmd_translate_soc_pa_to_bank(struct ras_core_context *ras_core,
+ uint64_t soc_pa, struct ras_fb_bank_addr *bank_addr)
+{
+ struct umc_bank_addr umc_bank = {0};
+ int ret;
+
+ ret = ras_umc_translate_soc_pa_and_bank(ras_core, &soc_pa, &umc_bank, false);
+ if (ret)
+ return RAS_CMD__ERROR_GENERIC;
+
+ bank_addr->stack_id = umc_bank.stack_id;
+ bank_addr->bank_group = umc_bank.bank_group;
+ bank_addr->bank = umc_bank.bank;
+ bank_addr->row = umc_bank.row;
+ bank_addr->column = umc_bank.column;
+ bank_addr->channel = umc_bank.channel;
+ bank_addr->subchannel = umc_bank.subchannel;
+
+ return 0;
+}
+
+int ras_cmd_translate_bank_to_soc_pa(struct ras_core_context *ras_core,
+ struct ras_fb_bank_addr bank_addr, uint64_t *soc_pa)
+{
+ struct umc_bank_addr umc_bank = {0};
+
+ umc_bank.stack_id = bank_addr.stack_id;
+ umc_bank.bank_group = bank_addr.bank_group;
+ umc_bank.bank = bank_addr.bank;
+ umc_bank.row = bank_addr.row;
+ umc_bank.column = bank_addr.column;
+ umc_bank.channel = bank_addr.channel;
+ umc_bank.subchannel = bank_addr.subchannel;
+
+ return ras_umc_translate_soc_pa_and_bank(ras_core, soc_pa, &umc_bank, true);
+}
+
+uint64_t ras_cmd_get_dev_handle(struct ras_core_context *ras_core)
+{
+ return ras_core->ras_cmd.dev_handle;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_cmd.h b/drivers/gpu/drm/amd/ras/rascore/ras_cmd.h
new file mode 100644
index 000000000000..48a0715eb821
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_cmd.h
@@ -0,0 +1,426 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_CMD_H__
+#define __RAS_CMD_H__
+#include "ras.h"
+#include "ras_eeprom.h"
+#include "ras_log_ring.h"
+#include "ras_cper.h"
+
+#define RAS_CMD_DEV_HANDLE_MAGIC 0xFEEDAD00UL
+
+#define RAS_CMD_MAX_IN_SIZE 256
+#define RAS_CMD_MAX_GPU_NUM 32
+#define RAS_CMD_MAX_BAD_PAGES_PER_GROUP 32
+
+/* position of instance value in sub_block_index of
+ * ta_ras_trigger_error_input, the sub block uses lower 12 bits
+ */
+#define RAS_TA_INST_MASK 0xfffff000
+#define RAS_TA_INST_SHIFT 0xc
+
+enum ras_cmd_interface_type {
+ RAS_CMD_INTERFACE_TYPE_NONE,
+ RAS_CMD_INTERFACE_TYPE_AMDGPU,
+ RAS_CMD_INTERFACE_TYPE_VF,
+ RAS_CMD_INTERFACE_TYPE_PF,
+};
+
+enum ras_cmd_id_range {
+ RAS_CMD_ID_COMMON_START = 0,
+ RAS_CMD_ID_COMMON_END = 0x10000,
+ RAS_CMD_ID_AMDGPU_START = RAS_CMD_ID_COMMON_END,
+ RAS_CMD_ID_AMDGPU_END = 0x20000,
+ RAS_CMD_ID_MXGPU_START = RAS_CMD_ID_AMDGPU_END,
+ RAS_CMD_ID_MXGPU_END = 0x30000,
+ RAS_CMD_ID_MXGPU_VF_START = RAS_CMD_ID_MXGPU_END,
+ RAS_CMD_ID_MXGPU_VF_END = 0x40000,
+};
+
+enum ras_cmd_id {
+ RAS_CMD__BEGIN = RAS_CMD_ID_COMMON_START,
+ RAS_CMD__QUERY_INTERFACE_INFO,
+ RAS_CMD__GET_DEVICES_INFO,
+ RAS_CMD__GET_BLOCK_ECC_STATUS,
+ RAS_CMD__INJECT_ERROR,
+ RAS_CMD__GET_BAD_PAGES,
+ RAS_CMD__CLEAR_BAD_PAGE_INFO,
+ RAS_CMD__RESET_ALL_ERROR_COUNTS,
+ RAS_CMD__GET_SAFE_FB_ADDRESS_RANGES,
+ RAS_CMD__TRANSLATE_FB_ADDRESS,
+ RAS_CMD__GET_LINK_TOPOLOGY,
+ RAS_CMD__GET_CPER_SNAPSHOT,
+ RAS_CMD__GET_CPER_RECORD,
+ RAS_CMD__GET_BATCH_TRACE_SNAPSHOT,
+ RAS_CMD__GET_BATCH_TRACE_RECORD,
+ RAS_CMD__SUPPORTED_MAX = RAS_CMD_ID_COMMON_END,
+};
+
+enum ras_cmd_response {
+ RAS_CMD__SUCCESS = 0,
+ RAS_CMD__SUCCESS_EXEED_BUFFER,
+ RAS_CMD__ERROR_UKNOWN_CMD,
+ RAS_CMD__ERROR_INVALID_CMD,
+ RAS_CMD__ERROR_VERSION,
+ RAS_CMD__ERROR_INVALID_INPUT_SIZE,
+ RAS_CMD__ERROR_INVALID_INPUT_DATA,
+ RAS_CMD__ERROR_DRV_INIT_FAIL,
+ RAS_CMD__ERROR_ACCESS_DENIED,
+ RAS_CMD__ERROR_GENERIC,
+ RAS_CMD__ERROR_TIMEOUT,
+};
+
+enum ras_error_type {
+ RAS_TYPE_ERROR__NONE = 0,
+ RAS_TYPE_ERROR__PARITY = 1,
+ RAS_TYPE_ERROR__SINGLE_CORRECTABLE = 2,
+ RAS_TYPE_ERROR__MULTI_UNCORRECTABLE = 4,
+ RAS_TYPE_ERROR__POISON = 8,
+};
+
+struct ras_core_context;
+struct ras_cmd_ctx;
+
+struct ras_cmd_mgr {
+ struct list_head head;
+ struct ras_core_context *ras_core;
+ uint64_t dev_handle;
+};
+
+struct ras_cmd_func_map {
+ uint32_t cmd_id;
+ int (*func)(struct ras_core_context *ras_core,
+ struct ras_cmd_ctx *cmd, void *data);
+};
+
+struct ras_device_bdf {
+ union {
+ struct {
+ uint32_t function : 3;
+ uint32_t device : 5;
+ uint32_t bus : 8;
+ uint32_t domain : 16;
+ };
+ uint32_t u32_all;
+ };
+};
+
+struct ras_cmd_param {
+ uint32_t idx_vf;
+ void *data;
+};
+
+#pragma pack(push, 8)
+struct ras_cmd_ctx {
+ uint32_t magic;
+ union {
+ struct {
+ uint16_t ras_cmd_minor_ver : 10;
+ uint16_t ras_cmd_major_ver : 6;
+ };
+ uint16_t ras_cmd_ver;
+ };
+ union {
+ struct {
+ uint16_t plat_major_ver : 10;
+ uint16_t plat_minor_ver : 6;
+ };
+ uint16_t plat_ver;
+ };
+ uint32_t cmd_id;
+ uint32_t cmd_res;
+ uint32_t input_size;
+ uint32_t output_size;
+ uint32_t output_buf_size;
+ uint32_t reserved[5];
+ uint8_t input_buff_raw[RAS_CMD_MAX_IN_SIZE];
+ uint8_t output_buff_raw[];
+};
+
+struct ras_cmd_dev_handle {
+ uint64_t dev_handle;
+};
+
+struct ras_cmd_block_ecc_info_req {
+ struct ras_cmd_dev_handle dev;
+ uint32_t block_id;
+ uint32_t subblock_id;
+ uint32_t reserved[4];
+};
+
+struct ras_cmd_block_ecc_info_rsp {
+ uint32_t version;
+ uint32_t ce_count;
+ uint32_t ue_count;
+ uint32_t de_count;
+ uint32_t reserved[6];
+};
+
+struct ras_cmd_inject_error_req {
+ struct ras_cmd_dev_handle dev;
+ uint32_t block_id;
+ uint32_t subblock_id;
+ uint64_t address;
+ uint32_t error_type;
+ uint32_t instance_mask;
+ union {
+ struct {
+ /* vf index */
+ uint64_t vf_idx : 6;
+ /* method of error injection. i.e persistent, coherent etc */
+ uint64_t method : 10;
+ uint64_t rsv : 48;
+ };
+ uint64_t value;
+ };
+ uint32_t reserved[8];
+};
+
+struct ras_cmd_inject_error_rsp {
+ uint32_t version;
+ uint32_t reserved[5];
+ uint64_t address;
+};
+
+struct ras_cmd_dev_info {
+ uint64_t dev_handle;
+ uint32_t location_id;
+ uint32_t ecc_enabled;
+ uint32_t ecc_supported;
+ uint32_t vf_num;
+ uint32_t asic_type;
+ uint32_t oam_id;
+ uint32_t reserved[8];
+};
+
+struct ras_cmd_devices_info_rsp {
+ uint32_t version;
+ uint32_t dev_num;
+ uint32_t reserved[6];
+ struct ras_cmd_dev_info devs[RAS_CMD_MAX_GPU_NUM];
+};
+
+struct ras_cmd_bad_page_record {
+ union {
+ uint64_t address;
+ uint64_t offset;
+ };
+ uint64_t retired_page;
+ uint64_t ts;
+
+ uint32_t err_type;
+
+ union {
+ unsigned char bank;
+ unsigned char cu;
+ };
+
+ unsigned char mem_channel;
+ unsigned char mcumc_id;
+
+ unsigned char valid;
+ unsigned char reserved[8];
+};
+
+struct ras_cmd_bad_pages_info_req {
+ struct ras_cmd_dev_handle device;
+ uint32_t group_index;
+ uint32_t reserved[5];
+};
+
+struct ras_cmd_bad_pages_info_rsp {
+ uint32_t version;
+ uint32_t group_index;
+ uint32_t bp_in_group;
+ uint32_t bp_total_cnt;
+ uint32_t reserved[4];
+ struct ras_cmd_bad_page_record records[RAS_CMD_MAX_BAD_PAGES_PER_GROUP];
+};
+
+struct ras_query_interface_info_req {
+ uint32_t reserved[8];
+};
+
+struct ras_query_interface_info_rsp {
+ uint32_t version;
+ uint32_t ras_cmd_major_ver;
+ uint32_t ras_cmd_minor_ver;
+ uint32_t plat_major_ver;
+ uint32_t plat_minor_ver;
+ uint8_t interface_type;
+ uint8_t rsv[3];
+ uint32_t reserved[8];
+};
+
+#define RAS_MAX_NUM_SAFE_RANGES 64
+struct ras_cmd_ras_safe_fb_address_ranges_rsp {
+ uint32_t version;
+ uint32_t num_ranges;
+ uint32_t reserved[4];
+ struct {
+ uint64_t start;
+ uint64_t size;
+ uint32_t idx;
+ uint32_t reserved[3];
+ } range[RAS_MAX_NUM_SAFE_RANGES];
+};
+
+enum ras_fb_addr_type {
+ RAS_FB_ADDR_SOC_PHY, /* SPA */
+ RAS_FB_ADDR_BANK,
+ RAS_FB_ADDR_VF_PHY, /* GPA */
+ RAS_FB_ADDR_UNKNOWN
+};
+
+struct ras_fb_bank_addr {
+ uint32_t stack_id; /* SID */
+ uint32_t bank_group;
+ uint32_t bank;
+ uint32_t row;
+ uint32_t column;
+ uint32_t channel;
+ uint32_t subchannel; /* Also called Pseudochannel (PC) */
+ uint32_t reserved[3];
+};
+
+struct ras_fb_vf_phy_addr {
+ uint32_t vf_idx;
+ uint32_t reserved;
+ uint64_t addr;
+};
+
+union ras_translate_fb_address {
+ struct ras_fb_bank_addr bank_addr;
+ uint64_t soc_phy_addr;
+ struct ras_fb_vf_phy_addr vf_phy_addr;
+};
+
+struct ras_cmd_translate_fb_address_req {
+ struct ras_cmd_dev_handle dev;
+ enum ras_fb_addr_type src_addr_type;
+ enum ras_fb_addr_type dest_addr_type;
+ union ras_translate_fb_address trans_addr;
+};
+
+struct ras_cmd_translate_fb_address_rsp {
+ uint32_t version;
+ uint32_t reserved[5];
+ union ras_translate_fb_address trans_addr;
+};
+
+struct ras_dev_link_topology_req {
+ struct ras_cmd_dev_handle src;
+ struct ras_cmd_dev_handle dst;
+};
+
+struct ras_dev_link_topology_rsp {
+ uint32_t version;
+ uint32_t link_status; /* HW status of the link */
+ uint32_t link_type; /* type of the link */
+ uint32_t num_hops; /* number of hops */
+ uint32_t reserved[8];
+};
+
+struct ras_cmd_cper_snapshot_req {
+ struct ras_cmd_dev_handle dev;
+};
+
+struct ras_cmd_cper_snapshot_rsp {
+ uint32_t version;
+ uint32_t reserved[4];
+ uint32_t total_cper_num;
+ uint64_t start_cper_id;
+ uint64_t latest_cper_id;
+};
+
+struct ras_cmd_cper_record_req {
+ struct ras_cmd_dev_handle dev;
+ uint64_t cper_start_id;
+ uint32_t cper_num;
+ uint32_t buf_size;
+ uint64_t buf_ptr;
+ uint32_t reserved[4];
+};
+
+struct ras_cmd_cper_record_rsp {
+ uint32_t version;
+ uint32_t real_data_size;
+ uint32_t real_cper_num;
+ uint32_t remain_num;
+ uint32_t reserved[4];
+};
+
+struct ras_cmd_batch_trace_snapshot_req {
+ struct ras_cmd_dev_handle dev;
+};
+
+struct ras_cmd_batch_trace_snapshot_rsp {
+ uint32_t version;
+ uint32_t reserved[4];
+ uint32_t total_batch_num;
+ uint64_t start_batch_id;
+ uint64_t latest_batch_id;
+};
+
+struct ras_cmd_batch_trace_record_req {
+ struct ras_cmd_dev_handle dev;
+ uint64_t start_batch_id;
+ uint32_t batch_num;
+ uint32_t reserved[5];
+};
+
+struct batch_ras_trace_info {
+ uint64_t batch_id;
+ uint16_t offset;
+ uint8_t trace_num;
+ uint8_t rsv;
+ uint32_t reserved;
+};
+
+#define RAS_CMD_MAX_BATCH_NUM 300
+#define RAS_CMD_MAX_TRACE_NUM 300
+struct ras_cmd_batch_trace_record_rsp {
+ uint32_t version;
+ uint16_t real_batch_num;
+ uint16_t remain_num;
+ uint64_t start_batch_id;
+ uint32_t reserved[2];
+ struct batch_ras_trace_info batchs[RAS_CMD_MAX_BATCH_NUM];
+ struct ras_log_info records[RAS_CMD_MAX_TRACE_NUM];
+};
+
+#pragma pack(pop)
+
+int ras_cmd_init(struct ras_core_context *ras_core);
+int ras_cmd_fini(struct ras_core_context *ras_core);
+int rascore_handle_cmd(struct ras_core_context *ras_core, struct ras_cmd_ctx *cmd, void *data);
+uint64_t ras_cmd_get_dev_handle(struct ras_core_context *ras_core);
+int ras_cmd_query_interface_info(struct ras_core_context *ras_core,
+ struct ras_query_interface_info_rsp *rsp);
+int ras_cmd_translate_soc_pa_to_bank(struct ras_core_context *ras_core,
+ uint64_t soc_pa, struct ras_fb_bank_addr *bank_addr);
+int ras_cmd_translate_bank_to_soc_pa(struct ras_core_context *ras_core,
+ struct ras_fb_bank_addr bank_addr, uint64_t *soc_pa);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_core.c b/drivers/gpu/drm/amd/ras/rascore/ras_core.c
new file mode 100644
index 000000000000..01122b55c98a
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_core.c
@@ -0,0 +1,603 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_core_status.h"
+
+#define RAS_SEQNO_FIFO_SIZE (128 * sizeof(uint64_t))
+
+#define IS_LEAP_YEAR(x) ((x % 4 == 0 && x % 100 != 0) || x % 400 == 0)
+
+static const char * const ras_block_name[] = {
+ "umc",
+ "sdma",
+ "gfx",
+ "mmhub",
+ "athub",
+ "pcie_bif",
+ "hdp",
+ "xgmi_wafl",
+ "df",
+ "smn",
+ "sem",
+ "mp0",
+ "mp1",
+ "fuse",
+ "mca",
+ "vcn",
+ "jpeg",
+ "ih",
+ "mpio",
+};
+
+const char *ras_core_get_ras_block_name(enum ras_block_id block_id)
+{
+ if (block_id >= ARRAY_SIZE(ras_block_name))
+ return "";
+
+ return ras_block_name[block_id];
+}
+
+int ras_core_convert_timestamp_to_time(struct ras_core_context *ras_core,
+ uint64_t timestamp, struct ras_time *tm)
+{
+ int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ uint64_t month = 0, day = 0, hour = 0, minute = 0, second = 0;
+ uint32_t year = 0;
+ int seconds_per_day = 24 * 60 * 60;
+ int seconds_per_hour = 60 * 60;
+ int seconds_per_minute = 60;
+ int days, remaining_seconds;
+
+ days = div64_u64_rem(timestamp, seconds_per_day, (uint64_t *)&remaining_seconds);
+
+ /* utc_timestamp follows the Unix epoch */
+ year = 1970;
+ while (days >= 365) {
+ if (IS_LEAP_YEAR(year)) {
+ if (days < 366)
+ break;
+ days -= 366;
+ } else {
+ days -= 365;
+ }
+ year++;
+ }
+
+ days_in_month[1] += IS_LEAP_YEAR(year);
+
+ month = 0;
+ while (days >= days_in_month[month]) {
+ days -= days_in_month[month];
+ month++;
+ }
+ month++;
+ day = days + 1;
+
+ if (remaining_seconds) {
+ hour = remaining_seconds / seconds_per_hour;
+ minute = (remaining_seconds % seconds_per_hour) / seconds_per_minute;
+ second = remaining_seconds % seconds_per_minute;
+ }
+
+ tm->tm_year = year;
+ tm->tm_mon = month;
+ tm->tm_mday = day;
+ tm->tm_hour = hour;
+ tm->tm_min = minute;
+ tm->tm_sec = second;
+
+ return 0;
+}
+
+bool ras_core_gpu_in_reset(struct ras_core_context *ras_core)
+{
+ uint32_t status = 0;
+
+ if (ras_core->sys_fn &&
+ ras_core->sys_fn->check_gpu_status)
+ ras_core->sys_fn->check_gpu_status(ras_core, &status);
+
+ return (status & RAS_GPU_STATUS__IN_RESET) ? true : false;
+}
+
+bool ras_core_gpu_is_vf(struct ras_core_context *ras_core)
+{
+ uint32_t status = 0;
+
+ if (ras_core->sys_fn &&
+ ras_core->sys_fn->check_gpu_status)
+ ras_core->sys_fn->check_gpu_status(ras_core, &status);
+
+ return (status & RAS_GPU_STATUS__IS_VF) ? true : false;
+}
+
+bool ras_core_gpu_is_rma(struct ras_core_context *ras_core)
+{
+ if (!ras_core)
+ return false;
+
+ return ras_core->is_rma;
+}
+
+static int ras_core_seqno_fifo_write(struct ras_core_context *ras_core,
+ enum ras_seqno_fifo fifo_type, uint64_t seqno)
+{
+ int ret = 0;
+ struct kfifo *seqno_fifo = NULL;
+
+ if (fifo_type == SEQNO_FIFO_POISON_CREATION)
+ seqno_fifo = &ras_core->de_seqno_fifo;
+ else if (fifo_type == SEQNO_FIFO_POISON_CONSUMPTION)
+ seqno_fifo = &ras_core->consumption_seqno_fifo;
+
+ if (seqno_fifo)
+ ret = kfifo_in_spinlocked(seqno_fifo,
+ &seqno, sizeof(seqno), &ras_core->seqno_lock);
+
+ return ret ? 0 : -EINVAL;
+}
+
+static int ras_core_seqno_fifo_read(struct ras_core_context *ras_core,
+ enum ras_seqno_fifo fifo_type, uint64_t *seqno, bool pop)
+{
+ int ret = 0;
+ struct kfifo *seqno_fifo = NULL;
+
+ if (fifo_type == SEQNO_FIFO_POISON_CREATION)
+ seqno_fifo = &ras_core->de_seqno_fifo;
+ else if (fifo_type == SEQNO_FIFO_POISON_CONSUMPTION)
+ seqno_fifo = &ras_core->consumption_seqno_fifo;
+
+ if (seqno_fifo) {
+ if (pop)
+ ret = kfifo_out_spinlocked(seqno_fifo,
+ seqno, sizeof(*seqno), &ras_core->seqno_lock);
+ else
+ ret = kfifo_out_peek(seqno_fifo, seqno, sizeof(*seqno));
+ }
+
+ return ret ? 0 : -EINVAL;
+}
+
+uint64_t ras_core_gen_seqno(struct ras_core_context *ras_core,
+ enum ras_seqno_type type)
+{
+ uint64_t seqno = 0;
+
+ if (ras_core->sys_fn &&
+ ras_core->sys_fn->gen_seqno)
+ ras_core->sys_fn->gen_seqno(ras_core, type, &seqno);
+
+ return seqno;
+}
+
+int ras_core_put_seqno(struct ras_core_context *ras_core,
+ enum ras_seqno_type seqno_type, uint64_t seqno)
+{
+ int ret = 0;
+
+ if (seqno_type >= RAS_SEQNO_TYPE_COUNT_MAX)
+ return -EINVAL;
+
+ if (seqno_type == RAS_SEQNO_TYPE_DE)
+ ret = ras_core_seqno_fifo_write(ras_core,
+ SEQNO_FIFO_POISON_CREATION, seqno);
+ else if (seqno_type == RAS_SEQNO_TYPE_POISON_CONSUMPTION)
+ ret = ras_core_seqno_fifo_write(ras_core,
+ SEQNO_FIFO_POISON_CONSUMPTION, seqno);
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+uint64_t ras_core_get_seqno(struct ras_core_context *ras_core,
+ enum ras_seqno_type seqno_type, bool pop)
+{
+ uint64_t seq_no;
+ int ret = -ENODATA;
+
+ if (seqno_type >= RAS_SEQNO_TYPE_COUNT_MAX)
+ return 0;
+
+ if (seqno_type == RAS_SEQNO_TYPE_DE)
+ ret = ras_core_seqno_fifo_read(ras_core,
+ SEQNO_FIFO_POISON_CREATION, &seq_no, pop);
+ else if (seqno_type == RAS_SEQNO_TYPE_POISON_CONSUMPTION)
+ ret = ras_core_seqno_fifo_read(ras_core,
+ SEQNO_FIFO_POISON_CONSUMPTION, &seq_no, pop);
+
+ if (ret)
+ seq_no = ras_core_gen_seqno(ras_core, seqno_type);
+
+ return seq_no;
+}
+
+static int ras_core_eeprom_recovery(struct ras_core_context *ras_core)
+{
+ int count;
+ int ret;
+
+ count = ras_eeprom_get_record_count(ras_core);
+ if (!count)
+ return 0;
+
+ /* Avoid bad page to be loaded again after gpu reset */
+ if (ras_umc_get_saved_eeprom_count(ras_core) >= count)
+ return 0;
+
+ ret = ras_umc_load_bad_pages(ras_core);
+ if (ret) {
+ RAS_DEV_ERR(ras_core->dev, "ras_umc_load_bad_pages failed: %d\n", ret);
+ return ret;
+ }
+
+ ras_eeprom_sync_info(ras_core);
+
+ return ret;
+}
+
+struct ras_core_context *ras_core_create(struct ras_core_config *init_config)
+{
+ struct ras_core_context *ras_core;
+ struct ras_core_config *config;
+
+ ras_core = kzalloc(sizeof(*ras_core), GFP_KERNEL);
+ if (!ras_core)
+ return NULL;
+
+ config = kzalloc(sizeof(*config), GFP_KERNEL);
+ if (!config) {
+ kfree(ras_core);
+ return NULL;
+ }
+
+ memcpy(config, init_config, sizeof(*config));
+ ras_core->config = config;
+
+ return ras_core;
+}
+
+void ras_core_destroy(struct ras_core_context *ras_core)
+{
+ if (ras_core)
+ kfree(ras_core->config);
+
+ kfree(ras_core);
+}
+
+int ras_core_sw_init(struct ras_core_context *ras_core)
+{
+ int ret;
+
+ if (!ras_core->config) {
+ RAS_DEV_ERR(ras_core->dev, "No ras core config!\n");
+ return -EINVAL;
+ }
+
+ ras_core->sys_fn = ras_core->config->sys_fn;
+ if (!ras_core->sys_fn)
+ return -EINVAL;
+
+ ret = kfifo_alloc(&ras_core->de_seqno_fifo,
+ RAS_SEQNO_FIFO_SIZE, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ ret = kfifo_alloc(&ras_core->consumption_seqno_fifo,
+ RAS_SEQNO_FIFO_SIZE, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&ras_core->seqno_lock);
+
+ ret = ras_aca_sw_init(ras_core);
+ if (ret)
+ return ret;
+
+ ret = ras_umc_sw_init(ras_core);
+ if (ret)
+ return ret;
+
+ ret = ras_cmd_init(ras_core);
+ if (ret)
+ return ret;
+
+ ret = ras_log_ring_sw_init(ras_core);
+ if (ret)
+ return ret;
+
+ ret = ras_psp_sw_init(ras_core);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int ras_core_sw_fini(struct ras_core_context *ras_core)
+{
+ kfifo_free(&ras_core->de_seqno_fifo);
+ kfifo_free(&ras_core->consumption_seqno_fifo);
+
+ ras_psp_sw_fini(ras_core);
+ ras_log_ring_sw_fini(ras_core);
+ ras_cmd_fini(ras_core);
+ ras_umc_sw_fini(ras_core);
+ ras_aca_sw_fini(ras_core);
+
+ return 0;
+}
+
+int ras_core_hw_init(struct ras_core_context *ras_core)
+{
+ int ret;
+
+ ras_core->ras_eeprom_supported =
+ ras_core->config->ras_eeprom_supported;
+
+ ras_core->poison_supported = ras_core->config->poison_supported;
+
+ ret = ras_psp_hw_init(ras_core);
+ if (ret)
+ return ret;
+
+ ret = ras_aca_hw_init(ras_core);
+ if (ret)
+ goto init_err1;
+
+ ret = ras_mp1_hw_init(ras_core);
+ if (ret)
+ goto init_err2;
+
+ ret = ras_nbio_hw_init(ras_core);
+ if (ret)
+ goto init_err3;
+
+ ret = ras_umc_hw_init(ras_core);
+ if (ret)
+ goto init_err4;
+
+ ret = ras_gfx_hw_init(ras_core);
+ if (ret)
+ goto init_err5;
+
+ ret = ras_eeprom_hw_init(ras_core);
+ if (ret)
+ goto init_err6;
+
+ ret = ras_core_eeprom_recovery(ras_core);
+ if (ret) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to recovery ras core, ret:%d\n", ret);
+ goto init_err6;
+ }
+
+ ret = ras_eeprom_check_storage_status(ras_core);
+ if (ret)
+ goto init_err6;
+
+ ret = ras_process_init(ras_core);
+ if (ret)
+ goto init_err7;
+
+ ras_core->is_initialized = true;
+
+ return 0;
+
+init_err7:
+ ras_eeprom_hw_fini(ras_core);
+init_err6:
+ ras_gfx_hw_fini(ras_core);
+init_err5:
+ ras_umc_hw_fini(ras_core);
+init_err4:
+ ras_nbio_hw_fini(ras_core);
+init_err3:
+ ras_mp1_hw_fini(ras_core);
+init_err2:
+ ras_aca_hw_fini(ras_core);
+init_err1:
+ ras_psp_hw_fini(ras_core);
+ return ret;
+}
+
+int ras_core_hw_fini(struct ras_core_context *ras_core)
+{
+ ras_core->is_initialized = false;
+
+ ras_process_fini(ras_core);
+ ras_eeprom_hw_fini(ras_core);
+ ras_gfx_hw_fini(ras_core);
+ ras_nbio_hw_fini(ras_core);
+ ras_umc_hw_fini(ras_core);
+ ras_mp1_hw_fini(ras_core);
+ ras_aca_hw_fini(ras_core);
+ ras_psp_hw_fini(ras_core);
+
+ return 0;
+}
+
+bool ras_core_handle_nbio_irq(struct ras_core_context *ras_core, void *data)
+{
+ return ras_nbio_handle_irq_error(ras_core, data);
+}
+
+int ras_core_handle_fatal_error(struct ras_core_context *ras_core)
+{
+ int ret = 0;
+
+ ras_aca_mark_fatal_flag(ras_core);
+
+ ret = ras_core_event_notify(ras_core,
+ RAS_EVENT_ID__FATAL_ERROR_DETECTED, NULL);
+
+ return ret;
+}
+
+uint32_t ras_core_get_curr_nps_mode(struct ras_core_context *ras_core)
+{
+ if (ras_core->ras_nbio.ip_func &&
+ ras_core->ras_nbio.ip_func->get_memory_partition_mode)
+ return ras_core->ras_nbio.ip_func->get_memory_partition_mode(ras_core);
+
+ RAS_DEV_ERR(ras_core->dev, "Failed to get gpu memory nps mode!\n");
+ return 0;
+}
+
+int ras_core_update_ecc_info(struct ras_core_context *ras_core)
+{
+ int ret;
+
+ ret = ras_aca_update_ecc(ras_core, RAS_ERR_TYPE__CE, NULL);
+ if (!ret)
+ ret = ras_aca_update_ecc(ras_core, RAS_ERR_TYPE__UE, NULL);
+
+ return ret;
+}
+
+int ras_core_query_block_ecc_data(struct ras_core_context *ras_core,
+ enum ras_block_id block, struct ras_ecc_count *ecc_count)
+{
+ int ret;
+
+ if (!ecc_count || (block >= RAS_BLOCK_ID__LAST) || !ras_core)
+ return -EINVAL;
+
+ ret = ras_aca_get_block_ecc_count(ras_core, block, ecc_count);
+ if (!ret)
+ ras_aca_clear_block_new_ecc_count(ras_core, block);
+
+ return ret;
+}
+
+int ras_core_set_status(struct ras_core_context *ras_core, bool enable)
+{
+ ras_core->ras_core_enabled = enable;
+
+ return 0;
+}
+
+bool ras_core_is_enabled(struct ras_core_context *ras_core)
+{
+ return ras_core->ras_core_enabled;
+}
+
+uint64_t ras_core_get_utc_second_timestamp(struct ras_core_context *ras_core)
+{
+ if (ras_core && ras_core->sys_fn &&
+ ras_core->sys_fn->get_utc_second_timestamp)
+ return ras_core->sys_fn->get_utc_second_timestamp(ras_core);
+
+ RAS_DEV_ERR(ras_core->dev, "Failed to get system timestamp!\n");
+ return 0;
+}
+
+int ras_core_translate_soc_pa_and_bank(struct ras_core_context *ras_core,
+ uint64_t *soc_pa, struct umc_bank_addr *bank_addr, bool bank_to_pa)
+{
+ if (!ras_core || !soc_pa || !bank_addr)
+ return -EINVAL;
+
+ return ras_umc_translate_soc_pa_and_bank(ras_core, soc_pa, bank_addr, bank_to_pa);
+}
+
+bool ras_core_ras_interrupt_detected(struct ras_core_context *ras_core)
+{
+ if (ras_core && ras_core->sys_fn &&
+ ras_core->sys_fn->detect_ras_interrupt)
+ return ras_core->sys_fn->detect_ras_interrupt(ras_core);
+
+ RAS_DEV_ERR(ras_core->dev, "Failed to detect ras interrupt!\n");
+ return false;
+}
+
+int ras_core_get_gpu_mem(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem)
+{
+ if (ras_core->sys_fn && ras_core->sys_fn->get_gpu_mem)
+ return ras_core->sys_fn->get_gpu_mem(ras_core, mem_type, gpu_mem);
+
+ RAS_DEV_ERR(ras_core->dev, "Not config get gpu memory API!\n");
+ return -EACCES;
+}
+
+int ras_core_put_gpu_mem(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem)
+{
+ if (ras_core->sys_fn && ras_core->sys_fn->put_gpu_mem)
+ return ras_core->sys_fn->put_gpu_mem(ras_core, mem_type, gpu_mem);
+
+ RAS_DEV_ERR(ras_core->dev, "Not config put gpu memory API!!\n");
+ return -EACCES;
+}
+
+bool ras_core_is_ready(struct ras_core_context *ras_core)
+{
+ return ras_core ? ras_core->is_initialized : false;
+}
+
+bool ras_core_check_safety_watermark(struct ras_core_context *ras_core)
+{
+ return ras_eeprom_check_safety_watermark(ras_core);
+}
+
+int ras_core_down_trylock_gpu_reset_lock(struct ras_core_context *ras_core)
+{
+ if (ras_core->sys_fn && ras_core->sys_fn->gpu_reset_lock)
+ return ras_core->sys_fn->gpu_reset_lock(ras_core, true, true);
+
+ return 1;
+}
+
+void ras_core_down_gpu_reset_lock(struct ras_core_context *ras_core)
+{
+ if (ras_core->sys_fn && ras_core->sys_fn->gpu_reset_lock)
+ ras_core->sys_fn->gpu_reset_lock(ras_core, true, false);
+}
+
+void ras_core_up_gpu_reset_lock(struct ras_core_context *ras_core)
+{
+ if (ras_core->sys_fn && ras_core->sys_fn->gpu_reset_lock)
+ ras_core->sys_fn->gpu_reset_lock(ras_core, false, false);
+}
+
+int ras_core_event_notify(struct ras_core_context *ras_core,
+ enum ras_notify_event event_id, void *data)
+{
+ if (ras_core && ras_core->sys_fn &&
+ ras_core->sys_fn->ras_notifier)
+ return ras_core->sys_fn->ras_notifier(ras_core, event_id, data);
+
+ return -RAS_CORE_NOT_SUPPORTED;
+}
+
+int ras_core_get_device_system_info(struct ras_core_context *ras_core,
+ struct device_system_info *dev_info)
+{
+ if (ras_core && ras_core->sys_fn &&
+ ras_core->sys_fn->get_device_system_info)
+ return ras_core->sys_fn->get_device_system_info(ras_core, dev_info);
+
+ return -RAS_CORE_NOT_SUPPORTED;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_cper.c b/drivers/gpu/drm/amd/ras/rascore/ras_cper.c
new file mode 100644
index 000000000000..0fc7522b7ab6
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_cper.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_core_status.h"
+#include "ras_log_ring.h"
+#include "ras_cper.h"
+
+static const struct ras_cper_guid MCE = CPER_NOTIFY__MCE;
+static const struct ras_cper_guid CMC = CPER_NOTIFY__CMC;
+static const struct ras_cper_guid BOOT = BOOT__TYPE;
+
+static const struct ras_cper_guid CRASHDUMP = GPU__CRASHDUMP;
+static const struct ras_cper_guid RUNTIME = GPU__NONSTANDARD_ERROR;
+
+static void cper_get_timestamp(struct ras_core_context *ras_core,
+ struct ras_cper_timestamp *timestamp, uint64_t utc_second_timestamp)
+{
+ struct ras_time tm = {0};
+
+ ras_core_convert_timestamp_to_time(ras_core, utc_second_timestamp, &tm);
+ timestamp->seconds = tm.tm_sec;
+ timestamp->minutes = tm.tm_min;
+ timestamp->hours = tm.tm_hour;
+ timestamp->flag = 0;
+ timestamp->day = tm.tm_mday;
+ timestamp->month = tm.tm_mon;
+ timestamp->year = tm.tm_year % 100;
+ timestamp->century = tm.tm_year / 100;
+}
+
+static void fill_section_hdr(struct ras_core_context *ras_core,
+ struct cper_section_hdr *hdr, enum ras_cper_type type,
+ enum ras_cper_severity sev, struct ras_log_info *trace)
+{
+ struct device_system_info dev_info = {0};
+ char record_id[32];
+
+ hdr->signature[0] = 'C';
+ hdr->signature[1] = 'P';
+ hdr->signature[2] = 'E';
+ hdr->signature[3] = 'R';
+ hdr->revision = CPER_HDR__REV_1;
+ hdr->signature_end = 0xFFFFFFFF;
+ hdr->error_severity = (sev == RAS_CPER_SEV_RMA ? RAS_CPER_SEV_FATAL_UE : sev);
+
+ hdr->valid_bits.platform_id = 1;
+ hdr->valid_bits.timestamp = 1;
+
+ ras_core_get_device_system_info(ras_core, &dev_info);
+
+ cper_get_timestamp(ras_core, &hdr->timestamp, trace->timestamp);
+
+ snprintf(record_id, sizeof(record_id), "%d:%llX", dev_info.socket_id,
+ RAS_LOG_SEQNO_TO_BATCH_IDX(trace->seqno));
+ memcpy(hdr->record_id, record_id, 8);
+
+ snprintf(hdr->platform_id, 16, "0x%04X:0x%04X",
+ dev_info.vendor_id, dev_info.device_id);
+ /* pmfw version should be part of creator_id according to CPER spec */
+ snprintf(hdr->creator_id, 16, "%s", CPER_CREATOR_ID__AMDGPU);
+
+ switch (type) {
+ case RAS_CPER_TYPE_BOOT:
+ hdr->notify_type = BOOT;
+ break;
+ case RAS_CPER_TYPE_FATAL:
+ case RAS_CPER_TYPE_RMA:
+ hdr->notify_type = MCE;
+ break;
+ case RAS_CPER_TYPE_RUNTIME:
+ if (sev == RAS_CPER_SEV_NON_FATAL_CE)
+ hdr->notify_type = CMC;
+ else
+ hdr->notify_type = MCE;
+ break;
+ default:
+ RAS_DEV_ERR(ras_core->dev, "Unknown CPER Type\n");
+ break;
+ }
+}
+
+static int fill_section_descriptor(struct ras_core_context *ras_core,
+ struct cper_section_descriptor *descriptor,
+ enum ras_cper_severity sev,
+ struct ras_cper_guid sec_type,
+ uint32_t section_offset,
+ uint32_t section_length)
+{
+ struct device_system_info dev_info = {0};
+
+ descriptor->revision_minor = CPER_SEC__MINOR_REV_1;
+ descriptor->revision_major = CPER_SEC__MAJOR_REV_22;
+ descriptor->sec_offset = section_offset;
+ descriptor->sec_length = section_length;
+ descriptor->valid_bits.fru_text = 1;
+ descriptor->flag_bits.primary = 1;
+ descriptor->severity = (sev == RAS_CPER_SEV_RMA ? RAS_CPER_SEV_FATAL_UE : sev);
+ descriptor->sec_type = sec_type;
+
+ ras_core_get_device_system_info(ras_core, &dev_info);
+
+ snprintf(descriptor->fru_text, 20, "OAM%d", dev_info.socket_id);
+
+ if (sev == RAS_CPER_SEV_RMA)
+ descriptor->flag_bits.exceed_err_threshold = 1;
+
+ if (sev == RAS_CPER_SEV_NON_FATAL_UE)
+ descriptor->flag_bits.latent_err = 1;
+
+ return 0;
+}
+
+static int fill_section_fatal(struct ras_core_context *ras_core,
+ struct cper_section_fatal *fatal, struct ras_log_info *trace)
+{
+ fatal->data.reg_ctx_type = CPER_CTX_TYPE__CRASH;
+ fatal->data.reg_arr_size = sizeof(fatal->data.reg);
+
+ fatal->data.reg.status = trace->aca_reg.regs[RAS_CPER_ACA_REG_STATUS];
+ fatal->data.reg.addr = trace->aca_reg.regs[RAS_CPER_ACA_REG_ADDR];
+ fatal->data.reg.ipid = trace->aca_reg.regs[RAS_CPER_ACA_REG_IPID];
+ fatal->data.reg.synd = trace->aca_reg.regs[RAS_CPER_ACA_REG_SYND];
+
+ return 0;
+}
+
+static int fill_section_runtime(struct ras_core_context *ras_core,
+ struct cper_section_runtime *runtime, struct ras_log_info *trace,
+ enum ras_cper_severity sev)
+{
+ runtime->hdr.valid_bits.err_info_cnt = 1;
+ runtime->hdr.valid_bits.err_context_cnt = 1;
+
+ runtime->descriptor.error_type = RUNTIME;
+ runtime->descriptor.ms_chk_bits.err_type_valid = 1;
+ if (sev == RAS_CPER_SEV_RMA) {
+ runtime->descriptor.valid_bits.ms_chk = 1;
+ runtime->descriptor.ms_chk_bits.err_type = 1;
+ runtime->descriptor.ms_chk_bits.pcc = 1;
+ }
+
+ runtime->reg.reg_ctx_type = CPER_CTX_TYPE__CRASH;
+ runtime->reg.reg_arr_size = sizeof(runtime->reg.reg_dump);
+
+ runtime->reg.reg_dump[RAS_CPER_ACA_REG_CTL] = trace->aca_reg.regs[ACA_REG_IDX__CTL];
+ runtime->reg.reg_dump[RAS_CPER_ACA_REG_STATUS] = trace->aca_reg.regs[ACA_REG_IDX__STATUS];
+ runtime->reg.reg_dump[RAS_CPER_ACA_REG_ADDR] = trace->aca_reg.regs[ACA_REG_IDX__ADDR];
+ runtime->reg.reg_dump[RAS_CPER_ACA_REG_MISC0] = trace->aca_reg.regs[ACA_REG_IDX__MISC0];
+ runtime->reg.reg_dump[RAS_CPER_ACA_REG_CONFIG] = trace->aca_reg.regs[ACA_REG_IDX__CONFG];
+ runtime->reg.reg_dump[RAS_CPER_ACA_REG_IPID] = trace->aca_reg.regs[ACA_REG_IDX__IPID];
+ runtime->reg.reg_dump[RAS_CPER_ACA_REG_SYND] = trace->aca_reg.regs[ACA_REG_IDX__SYND];
+
+ return 0;
+}
+
+static int cper_generate_runtime_record(struct ras_core_context *ras_core,
+ struct cper_section_hdr *hdr, struct ras_log_info **trace_arr, uint32_t arr_num,
+ enum ras_cper_severity sev)
+{
+ struct cper_section_descriptor *descriptor;
+ struct cper_section_runtime *runtime;
+ int i;
+
+ fill_section_hdr(ras_core, hdr, RAS_CPER_TYPE_RUNTIME, sev, trace_arr[0]);
+ hdr->record_length = RAS_HDR_LEN + ((RAS_SEC_DESC_LEN + RAS_NONSTD_SEC_LEN) * arr_num);
+ hdr->sec_cnt = arr_num;
+ for (i = 0; i < arr_num; i++) {
+ descriptor = (struct cper_section_descriptor *)((uint8_t *)hdr +
+ RAS_SEC_DESC_OFFSET(i));
+ runtime = (struct cper_section_runtime *)((uint8_t *)hdr +
+ RAS_NONSTD_SEC_OFFSET(hdr->sec_cnt, i));
+
+ fill_section_descriptor(ras_core, descriptor, sev, RUNTIME,
+ RAS_NONSTD_SEC_OFFSET(hdr->sec_cnt, i),
+ sizeof(struct cper_section_runtime));
+ fill_section_runtime(ras_core, runtime, trace_arr[i], sev);
+ }
+
+ return 0;
+}
+
+static int cper_generate_fatal_record(struct ras_core_context *ras_core,
+ uint8_t *buffer, struct ras_log_info **trace_arr, uint32_t arr_num)
+{
+ struct ras_cper_fatal_record record = {0};
+ int i = 0;
+
+ for (i = 0; i < arr_num; i++) {
+ fill_section_hdr(ras_core, &record.hdr, RAS_CPER_TYPE_FATAL,
+ RAS_CPER_SEV_FATAL_UE, trace_arr[i]);
+ record.hdr.record_length = RAS_HDR_LEN + RAS_SEC_DESC_LEN + RAS_FATAL_SEC_LEN;
+ record.hdr.sec_cnt = 1;
+
+ fill_section_descriptor(ras_core, &record.descriptor, RAS_CPER_SEV_FATAL_UE,
+ CRASHDUMP, offsetof(struct ras_cper_fatal_record, fatal),
+ sizeof(struct cper_section_fatal));
+
+ fill_section_fatal(ras_core, &record.fatal, trace_arr[i]);
+
+ memcpy(buffer + (i * record.hdr.record_length),
+ &record, record.hdr.record_length);
+ }
+
+ return 0;
+}
+
+static int cper_get_record_size(enum ras_cper_type type, uint16_t section_count)
+{
+ int size = 0;
+
+ size += RAS_HDR_LEN;
+ size += (RAS_SEC_DESC_LEN * section_count);
+
+ switch (type) {
+ case RAS_CPER_TYPE_RUNTIME:
+ case RAS_CPER_TYPE_RMA:
+ size += (RAS_NONSTD_SEC_LEN * section_count);
+ break;
+ case RAS_CPER_TYPE_FATAL:
+ size += (RAS_FATAL_SEC_LEN * section_count);
+ size += (RAS_HDR_LEN * (section_count - 1));
+ break;
+ case RAS_CPER_TYPE_BOOT:
+ size += (RAS_BOOT_SEC_LEN * section_count);
+ break;
+ default:
+ /* should never reach here */
+ break;
+ }
+
+ return size;
+}
+
+static enum ras_cper_type cper_ras_log_event_to_cper_type(enum ras_log_event event)
+{
+ switch (event) {
+ case RAS_LOG_EVENT_UE:
+ return RAS_CPER_TYPE_FATAL;
+ case RAS_LOG_EVENT_DE:
+ case RAS_LOG_EVENT_CE:
+ case RAS_LOG_EVENT_POISON_CREATION:
+ case RAS_LOG_EVENT_POISON_CONSUMPTION:
+ return RAS_CPER_TYPE_RUNTIME;
+ case RAS_LOG_EVENT_RMA:
+ return RAS_CPER_TYPE_RMA;
+ default:
+ /* should never reach here */
+ return RAS_CPER_TYPE_RUNTIME;
+ }
+}
+
+int ras_cper_generate_cper(struct ras_core_context *ras_core,
+ struct ras_log_info **trace_list, uint32_t count,
+ uint8_t *buf, uint32_t buf_len, uint32_t *real_data_len)
+{
+ uint8_t *buffer = buf;
+ uint64_t buf_size = buf_len;
+ int record_size, saved_size = 0;
+ struct cper_section_hdr *hdr;
+
+ /* All the batch traces share the same event */
+ record_size = cper_get_record_size(
+ cper_ras_log_event_to_cper_type(trace_list[0]->event), count);
+
+ if ((record_size + saved_size) > buf_size)
+ return -ENOMEM;
+
+ hdr = (struct cper_section_hdr *)(buffer + saved_size);
+
+ switch (trace_list[0]->event) {
+ case RAS_LOG_EVENT_RMA:
+ cper_generate_runtime_record(ras_core, hdr, trace_list, count, RAS_CPER_SEV_RMA);
+ break;
+ case RAS_LOG_EVENT_DE:
+ cper_generate_runtime_record(ras_core,
+ hdr, trace_list, count, RAS_CPER_SEV_NON_FATAL_UE);
+ break;
+ case RAS_LOG_EVENT_CE:
+ cper_generate_runtime_record(ras_core,
+ hdr, trace_list, count, RAS_CPER_SEV_NON_FATAL_CE);
+ break;
+ case RAS_LOG_EVENT_UE:
+ cper_generate_fatal_record(ras_core, buffer + saved_size, trace_list, count);
+ break;
+ default:
+ RAS_DEV_WARN(ras_core->dev, "Unprocessed trace event: %d\n", trace_list[0]->event);
+ break;
+ }
+
+ saved_size += record_size;
+
+ *real_data_len = saved_size;
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_cper.h b/drivers/gpu/drm/amd/ras/rascore/ras_cper.h
new file mode 100644
index 000000000000..076c1883c1ce
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_cper.h
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_CPER_H__
+#define __RAS_CPER_H__
+
+#define CPER_UUID_MAX_SIZE 16
+struct ras_cper_guid {
+ uint8_t b[CPER_UUID_MAX_SIZE];
+};
+
+#define CPER_GUID__INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \
+ ((struct ras_cper_guid) \
+ {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+ (b) & 0xff, ((b) >> 8) & 0xff, \
+ (c) & 0xff, ((c) >> 8) & 0xff, \
+ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }})
+
+#define CPER_HDR__REV_1 (0x100)
+#define CPER_SEC__MINOR_REV_1 (0x01)
+#define CPER_SEC__MAJOR_REV_22 (0x22)
+#define CPER_OAM_MAX_COUNT (8)
+
+#define CPER_CTX_TYPE__CRASH (1)
+#define CPER_CTX_TYPE__BOOT (9)
+
+#define CPER_CREATOR_ID__AMDGPU "amdgpu"
+
+#define CPER_NOTIFY__MCE \
+ CPER_GUID__INIT(0xE8F56FFE, 0x919C, 0x4cc5, 0xBA, 0x88, 0x65, 0xAB, \
+ 0xE1, 0x49, 0x13, 0xBB)
+#define CPER_NOTIFY__CMC \
+ CPER_GUID__INIT(0x2DCE8BB1, 0xBDD7, 0x450e, 0xB9, 0xAD, 0x9C, 0xF4, \
+ 0xEB, 0xD4, 0xF8, 0x90)
+#define BOOT__TYPE \
+ CPER_GUID__INIT(0x3D61A466, 0xAB40, 0x409a, 0xA6, 0x98, 0xF3, 0x62, \
+ 0xD4, 0x64, 0xB3, 0x8F)
+
+#define GPU__CRASHDUMP \
+ CPER_GUID__INIT(0x32AC0C78, 0x2623, 0x48F6, 0xB0, 0xD0, 0x73, 0x65, \
+ 0x72, 0x5F, 0xD6, 0xAE)
+#define GPU__NONSTANDARD_ERROR \
+ CPER_GUID__INIT(0x32AC0C78, 0x2623, 0x48F6, 0x81, 0xA2, 0xAC, 0x69, \
+ 0x17, 0x80, 0x55, 0x1D)
+#define PROC_ERR__SECTION_TYPE \
+ CPER_GUID__INIT(0xDC3EA0B0, 0xA144, 0x4797, 0xB9, 0x5B, 0x53, 0xFA, \
+ 0x24, 0x2B, 0x6E, 0x1D)
+
+enum ras_cper_type {
+ RAS_CPER_TYPE_RUNTIME,
+ RAS_CPER_TYPE_FATAL,
+ RAS_CPER_TYPE_BOOT,
+ RAS_CPER_TYPE_RMA,
+};
+
+enum ras_cper_severity {
+ RAS_CPER_SEV_NON_FATAL_UE = 0,
+ RAS_CPER_SEV_FATAL_UE = 1,
+ RAS_CPER_SEV_NON_FATAL_CE = 2,
+ RAS_CPER_SEV_RMA = 3,
+
+ RAS_CPER_SEV_UNUSED = 10,
+};
+
+enum ras_cper_aca_reg {
+ RAS_CPER_ACA_REG_CTL = 0,
+ RAS_CPER_ACA_REG_STATUS = 1,
+ RAS_CPER_ACA_REG_ADDR = 2,
+ RAS_CPER_ACA_REG_MISC0 = 3,
+ RAS_CPER_ACA_REG_CONFIG = 4,
+ RAS_CPER_ACA_REG_IPID = 5,
+ RAS_CPER_ACA_REG_SYND = 6,
+ RAS_CPER_ACA_REG_DESTAT = 8,
+ RAS_CPER_ACA_REG_DEADDR = 9,
+ RAS_CPER_ACA_REG_MASK = 10,
+
+ RAS_CPER_ACA_REG_COUNT = 16,
+};
+
+#pragma pack(push, 1)
+
+struct ras_cper_timestamp {
+ uint8_t seconds;
+ uint8_t minutes;
+ uint8_t hours;
+ uint8_t flag;
+ uint8_t day;
+ uint8_t month;
+ uint8_t year;
+ uint8_t century;
+};
+
+struct cper_section_hdr {
+ char signature[4]; /* "CPER" */
+ uint16_t revision;
+ uint32_t signature_end; /* 0xFFFFFFFF */
+ uint16_t sec_cnt;
+ enum ras_cper_severity error_severity;
+ union {
+ struct {
+ uint32_t platform_id : 1;
+ uint32_t timestamp : 1;
+ uint32_t partition_id : 1;
+ uint32_t reserved : 29;
+ } valid_bits;
+ uint32_t valid_mask;
+ };
+ uint32_t record_length; /* Total size of CPER Entry */
+ struct ras_cper_timestamp timestamp;
+ char platform_id[16];
+ struct ras_cper_guid partition_id; /* Reserved */
+ char creator_id[16];
+ struct ras_cper_guid notify_type; /* CMC, MCE */
+ char record_id[8]; /* Unique CPER Entry ID */
+ uint32_t flags; /* Reserved */
+ uint64_t persistence_info; /* Reserved */
+ uint8_t reserved[12]; /* Reserved */
+};
+
+struct cper_section_descriptor {
+ uint32_t sec_offset; /* Offset from the start of CPER entry */
+ uint32_t sec_length;
+ uint8_t revision_minor; /* CPER_SEC_MINOR_REV_1 */
+ uint8_t revision_major; /* CPER_SEC_MAJOR_REV_22 */
+ union {
+ struct {
+ uint8_t fru_id : 1;
+ uint8_t fru_text : 1;
+ uint8_t reserved : 6;
+ } valid_bits;
+ uint8_t valid_mask;
+ };
+ uint8_t reserved;
+ union {
+ struct {
+ uint32_t primary : 1;
+ uint32_t reserved1 : 2;
+ uint32_t exceed_err_threshold : 1;
+ uint32_t latent_err : 1;
+ uint32_t reserved2 : 27;
+ } flag_bits;
+ uint32_t flag_mask;
+ };
+ struct ras_cper_guid sec_type;
+ char fru_id[16];
+ enum ras_cper_severity severity;
+ char fru_text[20];
+};
+
+struct runtime_hdr {
+ union {
+ struct {
+ uint64_t apic_id : 1;
+ uint64_t fw_id : 1;
+ uint64_t err_info_cnt : 6;
+ uint64_t err_context_cnt : 6;
+ } valid_bits;
+ uint64_t valid_mask;
+ };
+ uint64_t apic_id;
+ char fw_id[48];
+};
+
+struct runtime_descriptor {
+ struct ras_cper_guid error_type;
+ union {
+ struct {
+ uint64_t ms_chk : 1;
+ uint64_t target_addr_id : 1;
+ uint64_t req_id : 1;
+ uint64_t resp_id : 1;
+ uint64_t instr_ptr : 1;
+ uint64_t reserved : 59;
+ } valid_bits;
+ uint64_t valid_mask;
+ };
+ union {
+ struct {
+ uint64_t err_type_valid : 1;
+ uint64_t pcc_valid : 1;
+ uint64_t uncorr_valid : 1;
+ uint64_t precise_ip_valid : 1;
+ uint64_t restartable_ip_valid : 1;
+ uint64_t overflow_valid : 1;
+ uint64_t reserved1 : 10;
+ uint64_t err_type : 2;
+ uint64_t pcc : 1;
+ uint64_t uncorr : 1;
+ uint64_t precised_ip : 1;
+ uint64_t restartable_ip : 1;
+ uint64_t overflow : 1;
+ uint64_t reserved2 : 41;
+ } ms_chk_bits;
+ uint64_t ms_chk_mask;
+ };
+ uint64_t target_addr_id;
+ uint64_t req_id;
+ uint64_t resp_id;
+ uint64_t instr_ptr;
+};
+
+struct runtime_error_reg {
+ uint16_t reg_ctx_type;
+ uint16_t reg_arr_size;
+ uint32_t msr_addr;
+ uint64_t mm_reg_addr;
+ uint64_t reg_dump[RAS_CPER_ACA_REG_COUNT];
+};
+
+struct cper_section_runtime {
+ struct runtime_hdr hdr;
+ struct runtime_descriptor descriptor;
+ struct runtime_error_reg reg;
+};
+
+struct crashdump_hdr {
+ uint64_t reserved1;
+ uint64_t reserved2;
+ char fw_id[48];
+ uint64_t reserved3[8];
+};
+
+struct fatal_reg_info {
+ uint64_t status;
+ uint64_t addr;
+ uint64_t ipid;
+ uint64_t synd;
+};
+
+struct crashdump_fatal {
+ uint16_t reg_ctx_type;
+ uint16_t reg_arr_size;
+ uint32_t reserved1;
+ uint64_t reserved2;
+ struct fatal_reg_info reg;
+};
+
+struct crashdump_boot {
+ uint16_t reg_ctx_type;
+ uint16_t reg_arr_size;
+ uint32_t reserved1;
+ uint64_t reserved2;
+ uint64_t msg[CPER_OAM_MAX_COUNT];
+};
+
+struct cper_section_fatal {
+ struct crashdump_hdr hdr;
+ struct crashdump_fatal data;
+};
+
+struct cper_section_boot {
+ struct crashdump_hdr hdr;
+ struct crashdump_boot data;
+};
+
+struct ras_cper_fatal_record {
+ struct cper_section_hdr hdr;
+ struct cper_section_descriptor descriptor;
+ struct cper_section_fatal fatal;
+};
+#pragma pack(pop)
+
+#define RAS_HDR_LEN (sizeof(struct cper_section_hdr))
+#define RAS_SEC_DESC_LEN (sizeof(struct cper_sec_desc))
+
+#define RAS_BOOT_SEC_LEN (sizeof(struct cper_sec_crashdump_boot))
+#define RAS_FATAL_SEC_LEN (sizeof(struct cper_sec_crashdump_fatal))
+#define RAS_NONSTD_SEC_LEN (sizeof(struct cper_sec_nonstd_err))
+
+#define RAS_SEC_DESC_OFFSET(idx) (RAS_HDR_LEN + (RAS_SEC_DESC_LEN * idx))
+
+#define RAS_BOOT_SEC_OFFSET(count, idx) \
+ (RAS_HDR_LEN + (RAS_SEC_DESC_LEN * count) + (RAS_BOOT_SEC_LEN * idx))
+#define RAS_FATAL_SEC_OFFSET(count, idx) \
+ (RAS_HDR_LEN + (RAS_SEC_DESC_LEN * count) + (RAS_FATAL_SEC_LEN * idx))
+#define RAS_NONSTD_SEC_OFFSET(count, idx) \
+ (RAS_HDR_LEN + (RAS_SEC_DESC_LEN * count) + (RAS_NONSTD_SEC_LEN * idx))
+
+struct ras_core_context;
+struct ras_log_info;
+int ras_cper_generate_cper(struct ras_core_context *ras_core,
+ struct ras_log_info **trace_list, uint32_t count,
+ uint8_t *buf, uint32_t buf_len, uint32_t *real_data_len);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c
new file mode 100644
index 000000000000..cd6b057bdaf3
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c
@@ -0,0 +1,1339 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras_eeprom.h"
+#include "ras.h"
+
+/* These are memory addresses as would be seen by one or more EEPROM
+ * chips strung on the I2C bus, usually by manipulating pins 1-3 of a
+ * set of EEPROM devices. They form a continuous memory space.
+ *
+ * The I2C device address includes the device type identifier, 1010b,
+ * which is a reserved value and indicates that this is an I2C EEPROM
+ * device. It also includes the top 3 bits of the 19 bit EEPROM memory
+ * address, namely bits 18, 17, and 16. This makes up the 7 bit
+ * address sent on the I2C bus with bit 0 being the direction bit,
+ * which is not represented here, and sent by the hardware directly.
+ *
+ * For instance,
+ * 50h = 1010000b => device type identifier 1010b, bits 18:16 = 000b, address 0.
+ * 54h = 1010100b => --"--, bits 18:16 = 100b, address 40000h.
+ * 56h = 1010110b => --"--, bits 18:16 = 110b, address 60000h.
+ * Depending on the size of the I2C EEPROM device(s), bits 18:16 may
+ * address memory in a device or a device on the I2C bus, depending on
+ * the status of pins 1-3.
+ *
+ * The RAS table lives either at address 0 or address 40000h of EEPROM.
+ */
+#define EEPROM_I2C_MADDR_0 0x0
+#define EEPROM_I2C_MADDR_4 0x40000
+
+#define EEPROM_PAGE_BITS 8
+#define EEPROM_PAGE_SIZE (1U << EEPROM_PAGE_BITS)
+#define EEPROM_PAGE_MASK (EEPROM_PAGE_SIZE - 1)
+
+#define EEPROM_OFFSET_SIZE 2
+#define MAKE_I2C_ADDR(_aa) ((0xA << 3) | (((_aa) >> 16) & 0xF))
+
+/*
+ * The 2 macros bellow represent the actual size in bytes that
+ * those entities occupy in the EEPROM memory.
+ * RAS_TABLE_RECORD_SIZE is different than sizeof(eeprom_umc_record) which
+ * uses uint64 to store 6b fields such as retired_page.
+ */
+#define RAS_TABLE_HEADER_SIZE 20
+#define RAS_TABLE_RECORD_SIZE 24
+
+/* Table hdr is 'AMDR' */
+#define RAS_TABLE_HDR_VAL 0x414d4452
+
+/* Bad GPU tag ‘BADG’ */
+#define RAS_TABLE_HDR_BAD 0x42414447
+
+/*
+ * EEPROM Table structure v1
+ * ---------------------------------
+ * | |
+ * | EEPROM TABLE HEADER |
+ * | ( size 20 Bytes ) |
+ * | |
+ * ---------------------------------
+ * | |
+ * | BAD PAGE RECORD AREA |
+ * | |
+ * ---------------------------------
+ */
+
+/* Assume 2-Mbit size EEPROM and take up the whole space. */
+#define RAS_TBL_SIZE_BYTES (256 * 1024)
+#define RAS_TABLE_START 0
+#define RAS_HDR_START RAS_TABLE_START
+#define RAS_RECORD_START (RAS_HDR_START + RAS_TABLE_HEADER_SIZE)
+#define RAS_MAX_RECORD_COUNT ((RAS_TBL_SIZE_BYTES - RAS_TABLE_HEADER_SIZE) \
+ / RAS_TABLE_RECORD_SIZE)
+
+/*
+ * EEPROM Table structrue v2.1
+ * ---------------------------------
+ * | |
+ * | EEPROM TABLE HEADER |
+ * | ( size 20 Bytes ) |
+ * | |
+ * ---------------------------------
+ * | |
+ * | EEPROM TABLE RAS INFO |
+ * | (available info size 4 Bytes) |
+ * | ( reserved size 252 Bytes ) |
+ * | |
+ * ---------------------------------
+ * | |
+ * | BAD PAGE RECORD AREA |
+ * | |
+ * ---------------------------------
+ */
+
+/* EEPROM Table V2_1 */
+#define RAS_TABLE_V2_1_INFO_SIZE 256
+#define RAS_TABLE_V2_1_INFO_START RAS_TABLE_HEADER_SIZE
+#define RAS_RECORD_START_V2_1 (RAS_HDR_START + RAS_TABLE_HEADER_SIZE + \
+ RAS_TABLE_V2_1_INFO_SIZE)
+#define RAS_MAX_RECORD_COUNT_V2_1 ((RAS_TBL_SIZE_BYTES - RAS_TABLE_HEADER_SIZE - \
+ RAS_TABLE_V2_1_INFO_SIZE) \
+ / RAS_TABLE_RECORD_SIZE)
+
+/* Given a zero-based index of an EEPROM RAS record, yields the EEPROM
+ * offset off of RAS_TABLE_START. That is, this is something you can
+ * add to control->i2c_address, and then tell I2C layer to read
+ * from/write to there. _N is the so called absolute index,
+ * because it starts right after the table header.
+ */
+#define RAS_INDEX_TO_OFFSET(_C, _N) ((_C)->ras_record_offset + \
+ (_N) * RAS_TABLE_RECORD_SIZE)
+
+#define RAS_OFFSET_TO_INDEX(_C, _O) (((_O) - \
+ (_C)->ras_record_offset) / RAS_TABLE_RECORD_SIZE)
+
+/* Given a 0-based relative record index, 0, 1, 2, ..., etc., off
+ * of "fri", return the absolute record index off of the end of
+ * the table header.
+ */
+#define RAS_RI_TO_AI(_C, _I) (((_I) + (_C)->ras_fri) % \
+ (_C)->ras_max_record_count)
+
+#define RAS_NUM_RECS(_tbl_hdr) (((_tbl_hdr)->tbl_size - \
+ RAS_TABLE_HEADER_SIZE) / RAS_TABLE_RECORD_SIZE)
+
+#define RAS_NUM_RECS_V2_1(_tbl_hdr) (((_tbl_hdr)->tbl_size - \
+ RAS_TABLE_HEADER_SIZE - \
+ RAS_TABLE_V2_1_INFO_SIZE) / RAS_TABLE_RECORD_SIZE)
+
+#define to_ras_core_context(x) (container_of(x, struct ras_core_context, ras_eeprom))
+
+static bool __is_ras_eeprom_supported(struct ras_core_context *ras_core)
+{
+ return ras_core->ras_eeprom_supported;
+}
+
+static bool __get_eeprom_i2c_addr(struct ras_core_context *ras_core,
+ struct ras_eeprom_control *control)
+{
+ int ret = -EINVAL;
+
+ if (control->sys_func &&
+ control->sys_func->update_eeprom_i2c_config)
+ ret = control->sys_func->update_eeprom_i2c_config(ras_core);
+ else
+ RAS_DEV_WARN(ras_core->dev,
+ "No eeprom i2c system config!\n");
+
+ return !ret ? true : false;
+}
+
+static int __ras_eeprom_xfer(struct ras_core_context *ras_core, u32 eeprom_addr,
+ u8 *eeprom_buf, u32 buf_size, bool read)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ int ret;
+
+ if (control->sys_func && control->sys_func->eeprom_i2c_xfer) {
+ ret = control->sys_func->eeprom_i2c_xfer(ras_core,
+ eeprom_addr, eeprom_buf, buf_size, read);
+
+ if ((ret > 0) && !read) {
+ /* According to EEPROM specs the length of the
+ * self-writing cycle, tWR (tW), is 10 ms.
+ *
+ * TODO: Use polling on ACK, aka Acknowledge
+ * Polling, to minimize waiting for the
+ * internal write cycle to complete, as it is
+ * usually smaller than tWR (tW).
+ */
+ msleep(10);
+ }
+
+ return ret;
+ }
+
+ RAS_DEV_ERR(ras_core->dev, "Error: No eeprom i2c system xfer function!\n");
+ return -EINVAL;
+}
+
+static int __eeprom_xfer(struct ras_core_context *ras_core, u32 eeprom_addr,
+ u8 *eeprom_buf, u32 buf_size, bool read)
+{
+ u16 limit;
+ u16 ps; /* Partial size */
+ int res = 0, r;
+
+ if (read)
+ limit = ras_core->ras_eeprom.max_read_len;
+ else
+ limit = ras_core->ras_eeprom.max_write_len;
+
+ if (limit && (limit <= EEPROM_OFFSET_SIZE)) {
+ RAS_DEV_ERR(ras_core->dev,
+ "maddr:0x%04X size:0x%02X:quirk max_%s_len must be > %d",
+ eeprom_addr, buf_size,
+ read ? "read" : "write", EEPROM_OFFSET_SIZE);
+ return -EINVAL;
+ }
+
+ ras_core_down_gpu_reset_lock(ras_core);
+
+ if (limit == 0) {
+ res = __ras_eeprom_xfer(ras_core, eeprom_addr,
+ eeprom_buf, buf_size, read);
+ } else {
+ /* The "limit" includes all data bytes sent/received,
+ * which would include the EEPROM_OFFSET_SIZE bytes.
+ * Account for them here.
+ */
+ limit -= EEPROM_OFFSET_SIZE;
+ for ( ; buf_size > 0;
+ buf_size -= ps, eeprom_addr += ps, eeprom_buf += ps) {
+ ps = (buf_size < limit) ? buf_size : limit;
+
+ r = __ras_eeprom_xfer(ras_core, eeprom_addr,
+ eeprom_buf, ps, read);
+ if (r < 0)
+ break;
+
+ res += r;
+ }
+ }
+
+ ras_core_up_gpu_reset_lock(ras_core);
+
+ return res;
+}
+
+static int __eeprom_read(struct ras_core_context *ras_core,
+ u32 eeprom_addr, u8 *eeprom_buf, u32 bytes)
+{
+ return __eeprom_xfer(ras_core, eeprom_addr,
+ eeprom_buf, bytes, true);
+}
+
+static int __eeprom_write(struct ras_core_context *ras_core,
+ u32 eeprom_addr, u8 *eeprom_buf, u32 bytes)
+{
+ return __eeprom_xfer(ras_core, eeprom_addr,
+ eeprom_buf, bytes, false);
+}
+
+static void
+__encode_table_header_to_buf(struct ras_eeprom_table_header *hdr,
+ unsigned char *buf)
+{
+ u32 *pp = (uint32_t *)buf;
+
+ pp[0] = cpu_to_le32(hdr->header);
+ pp[1] = cpu_to_le32(hdr->version);
+ pp[2] = cpu_to_le32(hdr->first_rec_offset);
+ pp[3] = cpu_to_le32(hdr->tbl_size);
+ pp[4] = cpu_to_le32(hdr->checksum);
+}
+
+static void
+__decode_table_header_from_buf(struct ras_eeprom_table_header *hdr,
+ unsigned char *buf)
+{
+ u32 *pp = (uint32_t *)buf;
+
+ hdr->header = le32_to_cpu(pp[0]);
+ hdr->version = le32_to_cpu(pp[1]);
+ hdr->first_rec_offset = le32_to_cpu(pp[2]);
+ hdr->tbl_size = le32_to_cpu(pp[3]);
+ hdr->checksum = le32_to_cpu(pp[4]);
+}
+
+static int __write_table_header(struct ras_eeprom_control *control)
+{
+ u8 buf[RAS_TABLE_HEADER_SIZE];
+ struct ras_core_context *ras_core = to_ras_core_context(control);
+ int res;
+
+ memset(buf, 0, sizeof(buf));
+ __encode_table_header_to_buf(&control->tbl_hdr, buf);
+
+ /* i2c may be unstable in gpu reset */
+ res = __eeprom_write(ras_core,
+ control->i2c_address +
+ control->ras_header_offset,
+ buf, RAS_TABLE_HEADER_SIZE);
+
+ if (res < 0) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to write EEPROM table header:%d\n", res);
+ } else if (res < RAS_TABLE_HEADER_SIZE) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Short write:%d out of %d\n", res, RAS_TABLE_HEADER_SIZE);
+ res = -EIO;
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+static void
+__encode_table_ras_info_to_buf(struct ras_eeprom_table_ras_info *rai,
+ unsigned char *buf)
+{
+ u32 *pp = (uint32_t *)buf;
+ u32 tmp;
+
+ tmp = ((uint32_t)(rai->rma_status) & 0xFF) |
+ (((uint32_t)(rai->health_percent) << 8) & 0xFF00) |
+ (((uint32_t)(rai->ecc_page_threshold) << 16) & 0xFFFF0000);
+ pp[0] = cpu_to_le32(tmp);
+}
+
+static void
+__decode_table_ras_info_from_buf(struct ras_eeprom_table_ras_info *rai,
+ unsigned char *buf)
+{
+ u32 *pp = (uint32_t *)buf;
+ u32 tmp;
+
+ tmp = le32_to_cpu(pp[0]);
+ rai->rma_status = tmp & 0xFF;
+ rai->health_percent = (tmp >> 8) & 0xFF;
+ rai->ecc_page_threshold = (tmp >> 16) & 0xFFFF;
+}
+
+static int __write_table_ras_info(struct ras_eeprom_control *control)
+{
+ struct ras_core_context *ras_core = to_ras_core_context(control);
+ u8 *buf;
+ int res;
+
+ buf = kzalloc(RAS_TABLE_V2_1_INFO_SIZE, GFP_KERNEL);
+ if (!buf) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to alloc buf to write table ras info\n");
+ return -ENOMEM;
+ }
+
+ __encode_table_ras_info_to_buf(&control->tbl_rai, buf);
+
+ /* i2c may be unstable in gpu reset */
+ res = __eeprom_write(ras_core,
+ control->i2c_address +
+ control->ras_info_offset,
+ buf, RAS_TABLE_V2_1_INFO_SIZE);
+
+ if (res < 0) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to write EEPROM table ras info:%d\n", res);
+ } else if (res < RAS_TABLE_V2_1_INFO_SIZE) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Short write:%d out of %d\n", res, RAS_TABLE_V2_1_INFO_SIZE);
+ res = -EIO;
+ } else {
+ res = 0;
+ }
+
+ kfree(buf);
+
+ return res;
+}
+
+static u8 __calc_hdr_byte_sum(const struct ras_eeprom_control *control)
+{
+ int ii;
+ u8 *pp, csum;
+ u32 sz;
+
+ /* Header checksum, skip checksum field in the calculation */
+ sz = sizeof(control->tbl_hdr) - sizeof(control->tbl_hdr.checksum);
+ pp = (u8 *) &control->tbl_hdr;
+ csum = 0;
+ for (ii = 0; ii < sz; ii++, pp++)
+ csum += *pp;
+
+ return csum;
+}
+
+static u8 __calc_ras_info_byte_sum(const struct ras_eeprom_control *control)
+{
+ int ii;
+ u8 *pp, csum;
+ u32 sz;
+
+ sz = sizeof(control->tbl_rai);
+ pp = (u8 *) &control->tbl_rai;
+ csum = 0;
+ for (ii = 0; ii < sz; ii++, pp++)
+ csum += *pp;
+
+ return csum;
+}
+
+static int ras_eeprom_correct_header_tag(
+ struct ras_eeprom_control *control,
+ uint32_t header)
+{
+ struct ras_eeprom_table_header *hdr = &control->tbl_hdr;
+ u8 *hh;
+ int res;
+ u8 csum;
+
+ csum = -hdr->checksum;
+
+ hh = (void *) &hdr->header;
+ csum -= (hh[0] + hh[1] + hh[2] + hh[3]);
+ hh = (void *) &header;
+ csum += hh[0] + hh[1] + hh[2] + hh[3];
+ csum = -csum;
+ mutex_lock(&control->ras_tbl_mutex);
+ hdr->header = header;
+ hdr->checksum = csum;
+ res = __write_table_header(control);
+ mutex_unlock(&control->ras_tbl_mutex);
+
+ return res;
+}
+
+static void ras_set_eeprom_table_version(struct ras_eeprom_control *control)
+{
+ struct ras_eeprom_table_header *hdr = &control->tbl_hdr;
+
+ hdr->version = RAS_TABLE_VER_V3;
+}
+
+int ras_eeprom_reset_table(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ struct ras_eeprom_table_header *hdr = &control->tbl_hdr;
+ struct ras_eeprom_table_ras_info *rai = &control->tbl_rai;
+ u8 csum;
+ int res;
+
+ mutex_lock(&control->ras_tbl_mutex);
+
+ hdr->header = RAS_TABLE_HDR_VAL;
+ ras_set_eeprom_table_version(control);
+
+ if (hdr->version >= RAS_TABLE_VER_V2_1) {
+ hdr->first_rec_offset = RAS_RECORD_START_V2_1;
+ hdr->tbl_size = RAS_TABLE_HEADER_SIZE +
+ RAS_TABLE_V2_1_INFO_SIZE;
+ rai->rma_status = RAS_GPU_HEALTH_USABLE;
+ /**
+ * GPU health represented as a percentage.
+ * 0 means worst health, 100 means fully health.
+ */
+ rai->health_percent = 100;
+ /* ecc_page_threshold = 0 means disable bad page retirement */
+ rai->ecc_page_threshold = control->record_threshold_count;
+ } else {
+ hdr->first_rec_offset = RAS_RECORD_START;
+ hdr->tbl_size = RAS_TABLE_HEADER_SIZE;
+ }
+
+ csum = __calc_hdr_byte_sum(control);
+ if (hdr->version >= RAS_TABLE_VER_V2_1)
+ csum += __calc_ras_info_byte_sum(control);
+ csum = -csum;
+ hdr->checksum = csum;
+ res = __write_table_header(control);
+ if (!res && hdr->version > RAS_TABLE_VER_V1)
+ res = __write_table_ras_info(control);
+
+ control->ras_num_recs = 0;
+ control->ras_fri = 0;
+
+ control->bad_channel_bitmap = 0;
+ ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM,
+ &control->ras_num_recs);
+ ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_CHANNEL_BITMAP,
+ &control->bad_channel_bitmap);
+ control->update_channel_flag = false;
+
+ mutex_unlock(&control->ras_tbl_mutex);
+
+ return res;
+}
+
+static void
+__encode_table_record_to_buf(struct ras_eeprom_control *control,
+ struct eeprom_umc_record *record,
+ unsigned char *buf)
+{
+ __le64 tmp = 0;
+ int i = 0;
+
+ /* Next are all record fields according to EEPROM page spec in LE foramt */
+ buf[i++] = record->err_type;
+
+ buf[i++] = record->bank;
+
+ tmp = cpu_to_le64(record->ts);
+ memcpy(buf + i, &tmp, 8);
+ i += 8;
+
+ tmp = cpu_to_le64((record->offset & 0xffffffffffff));
+ memcpy(buf + i, &tmp, 6);
+ i += 6;
+
+ buf[i++] = record->mem_channel;
+ buf[i++] = record->mcumc_id;
+
+ tmp = cpu_to_le64((record->retired_row_pfn & 0xffffffffffff));
+ memcpy(buf + i, &tmp, 6);
+}
+
+static void
+__decode_table_record_from_buf(struct ras_eeprom_control *control,
+ struct eeprom_umc_record *record,
+ unsigned char *buf)
+{
+ __le64 tmp = 0;
+ int i = 0;
+
+ /* Next are all record fields according to EEPROM page spec in LE foramt */
+ record->err_type = buf[i++];
+
+ record->bank = buf[i++];
+
+ memcpy(&tmp, buf + i, 8);
+ record->ts = le64_to_cpu(tmp);
+ i += 8;
+
+ memcpy(&tmp, buf + i, 6);
+ record->offset = (le64_to_cpu(tmp) & 0xffffffffffff);
+ i += 6;
+
+ record->mem_channel = buf[i++];
+ record->mcumc_id = buf[i++];
+
+ memcpy(&tmp, buf + i, 6);
+ record->retired_row_pfn = (le64_to_cpu(tmp) & 0xffffffffffff);
+}
+
+bool ras_eeprom_check_safety_watermark(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ bool ret = false;
+ int bad_page_count;
+
+ if (!__is_ras_eeprom_supported(ras_core) ||
+ !control->record_threshold_config)
+ return false;
+
+ bad_page_count = ras_umc_get_badpage_count(ras_core);
+ if (control->tbl_hdr.header == RAS_TABLE_HDR_BAD) {
+ if (bad_page_count > control->record_threshold_count)
+ RAS_DEV_WARN(ras_core->dev, "RAS records:%d exceed threshold:%d",
+ bad_page_count, control->record_threshold_count);
+
+ if ((control->record_threshold_config == WARN_NONSTOP_OVER_THRESHOLD) ||
+ (control->record_threshold_config == NONSTOP_OVER_THRESHOLD)) {
+ RAS_DEV_WARN(ras_core->dev,
+ "Please consult AMD Service Action Guide (SAG) for appropriate service procedures.\n");
+ ret = false;
+ } else {
+ ras_core->is_rma = true;
+ RAS_DEV_WARN(ras_core->dev,
+ "Please consider adjusting the customized threshold.\n");
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * __ras_eeprom_write -- write indexed from buffer to EEPROM
+ * @control: pointer to control structure
+ * @buf: pointer to buffer containing data to write
+ * @fri: start writing at this index
+ * @num: number of records to write
+ *
+ * The caller must hold the table mutex in @control.
+ * Return 0 on success, -errno otherwise.
+ */
+static int __ras_eeprom_write(struct ras_eeprom_control *control,
+ u8 *buf, const u32 fri, const u32 num)
+{
+ struct ras_core_context *ras_core = to_ras_core_context(control);
+ u32 buf_size;
+ int res;
+
+ /* i2c may be unstable in gpu reset */
+ buf_size = num * RAS_TABLE_RECORD_SIZE;
+ res = __eeprom_write(ras_core,
+ control->i2c_address + RAS_INDEX_TO_OFFSET(control, fri),
+ buf, buf_size);
+ if (res < 0) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Writing %d EEPROM table records error:%d\n", num, res);
+ } else if (res < buf_size) {
+ /* Short write, return error.*/
+ RAS_DEV_ERR(ras_core->dev,
+ "Wrote %d records out of %d\n",
+ (res/RAS_TABLE_RECORD_SIZE), num);
+ res = -EIO;
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+static int ras_eeprom_append_table(struct ras_eeprom_control *control,
+ struct eeprom_umc_record *record,
+ const u32 num)
+{
+ u32 a, b, i;
+ u8 *buf, *pp;
+ int res;
+
+ buf = kcalloc(num, RAS_TABLE_RECORD_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Encode all of them in one go.
+ */
+ pp = buf;
+ for (i = 0; i < num; i++, pp += RAS_TABLE_RECORD_SIZE) {
+ __encode_table_record_to_buf(control, &record[i], pp);
+
+ /* update bad channel bitmap */
+ if ((record[i].mem_channel < BITS_PER_TYPE(control->bad_channel_bitmap)) &&
+ !(control->bad_channel_bitmap & (1 << record[i].mem_channel))) {
+ control->bad_channel_bitmap |= 1 << record[i].mem_channel;
+ control->update_channel_flag = true;
+ }
+ }
+
+ /* a, first record index to write into.
+ * b, last record index to write into.
+ * a = first index to read (fri) + number of records in the table,
+ * b = a + @num - 1.
+ * Let N = control->ras_max_num_record_count, then we have,
+ * case 0: 0 <= a <= b < N,
+ * just append @num records starting at a;
+ * case 1: 0 <= a < N <= b,
+ * append (N - a) records starting at a, and
+ * append the remainder, b % N + 1, starting at 0.
+ * case 2: 0 <= fri < N <= a <= b, then modulo N we get two subcases,
+ * case 2a: 0 <= a <= b < N
+ * append num records starting at a; and fix fri if b overwrote it,
+ * and since a <= b, if b overwrote it then a must've also,
+ * and if b didn't overwrite it, then a didn't also.
+ * case 2b: 0 <= b < a < N
+ * write num records starting at a, which wraps around 0=N
+ * and overwrite fri unconditionally. Now from case 2a,
+ * this means that b eclipsed fri to overwrite it and wrap
+ * around 0 again, i.e. b = 2N+r pre modulo N, so we unconditionally
+ * set fri = b + 1 (mod N).
+ * Now, since fri is updated in every case, except the trivial case 0,
+ * the number of records present in the table after writing, is,
+ * num_recs - 1 = b - fri (mod N), and we take the positive value,
+ * by adding an arbitrary multiple of N before taking the modulo N
+ * as shown below.
+ */
+ a = control->ras_fri + control->ras_num_recs;
+ b = a + num - 1;
+ if (b < control->ras_max_record_count) {
+ res = __ras_eeprom_write(control, buf, a, num);
+ } else if (a < control->ras_max_record_count) {
+ u32 g0, g1;
+
+ g0 = control->ras_max_record_count - a;
+ g1 = b % control->ras_max_record_count + 1;
+ res = __ras_eeprom_write(control, buf, a, g0);
+ if (res)
+ goto Out;
+ res = __ras_eeprom_write(control,
+ buf + g0 * RAS_TABLE_RECORD_SIZE,
+ 0, g1);
+ if (res)
+ goto Out;
+ if (g1 > control->ras_fri)
+ control->ras_fri = g1 % control->ras_max_record_count;
+ } else {
+ a %= control->ras_max_record_count;
+ b %= control->ras_max_record_count;
+
+ if (a <= b) {
+ /* Note that, b - a + 1 = num. */
+ res = __ras_eeprom_write(control, buf, a, num);
+ if (res)
+ goto Out;
+ if (b >= control->ras_fri)
+ control->ras_fri = (b + 1) % control->ras_max_record_count;
+ } else {
+ u32 g0, g1;
+
+ /* b < a, which means, we write from
+ * a to the end of the table, and from
+ * the start of the table to b.
+ */
+ g0 = control->ras_max_record_count - a;
+ g1 = b + 1;
+ res = __ras_eeprom_write(control, buf, a, g0);
+ if (res)
+ goto Out;
+ res = __ras_eeprom_write(control,
+ buf + g0 * RAS_TABLE_RECORD_SIZE, 0, g1);
+ if (res)
+ goto Out;
+ control->ras_fri = g1 % control->ras_max_record_count;
+ }
+ }
+ control->ras_num_recs = 1 +
+ (control->ras_max_record_count + b - control->ras_fri)
+ % control->ras_max_record_count;
+Out:
+ kfree(buf);
+ return res;
+}
+
+static int ras_eeprom_update_header(struct ras_eeprom_control *control)
+{
+ struct ras_core_context *ras_core = to_ras_core_context(control);
+ int threshold_config = control->record_threshold_config;
+ u8 *buf, *pp, csum;
+ u32 buf_size;
+ int bad_page_count;
+ int res;
+
+ bad_page_count = ras_umc_get_badpage_count(ras_core);
+ /* Modify the header if it exceeds.
+ */
+ if (threshold_config != 0 &&
+ bad_page_count > control->record_threshold_count) {
+ RAS_DEV_WARN(ras_core->dev,
+ "Saved bad pages %d reaches threshold value %d\n",
+ bad_page_count, control->record_threshold_count);
+ control->tbl_hdr.header = RAS_TABLE_HDR_BAD;
+ if (control->tbl_hdr.version >= RAS_TABLE_VER_V2_1) {
+ control->tbl_rai.rma_status = RAS_GPU_RETIRED__ECC_REACH_THRESHOLD;
+ control->tbl_rai.health_percent = 0;
+ }
+
+ if ((threshold_config != WARN_NONSTOP_OVER_THRESHOLD) &&
+ (threshold_config != NONSTOP_OVER_THRESHOLD))
+ ras_core->is_rma = true;
+
+ /* ignore the -ENOTSUPP return value */
+ ras_core_event_notify(ras_core, RAS_EVENT_ID__DEVICE_RMA, NULL);
+ }
+
+ if (control->tbl_hdr.version >= RAS_TABLE_VER_V2_1)
+ control->tbl_hdr.tbl_size = RAS_TABLE_HEADER_SIZE +
+ RAS_TABLE_V2_1_INFO_SIZE +
+ control->ras_num_recs * RAS_TABLE_RECORD_SIZE;
+ else
+ control->tbl_hdr.tbl_size = RAS_TABLE_HEADER_SIZE +
+ control->ras_num_recs * RAS_TABLE_RECORD_SIZE;
+ control->tbl_hdr.checksum = 0;
+
+ buf_size = control->ras_num_recs * RAS_TABLE_RECORD_SIZE;
+ buf = kcalloc(control->ras_num_recs, RAS_TABLE_RECORD_SIZE, GFP_KERNEL);
+ if (!buf) {
+ RAS_DEV_ERR(ras_core->dev,
+ "allocating memory for table of size %d bytes failed\n",
+ control->tbl_hdr.tbl_size);
+ res = -ENOMEM;
+ goto Out;
+ }
+
+ res = __eeprom_read(ras_core,
+ control->i2c_address +
+ control->ras_record_offset,
+ buf, buf_size);
+ if (res < 0) {
+ RAS_DEV_ERR(ras_core->dev,
+ "EEPROM failed reading records:%d\n", res);
+ goto Out;
+ } else if (res < buf_size) {
+ RAS_DEV_ERR(ras_core->dev,
+ "EEPROM read %d out of %d bytes\n", res, buf_size);
+ res = -EIO;
+ goto Out;
+ }
+
+ /**
+ * bad page records have been stored in eeprom,
+ * now calculate gpu health percent
+ */
+ if (threshold_config != 0 &&
+ control->tbl_hdr.version >= RAS_TABLE_VER_V2_1 &&
+ bad_page_count <= control->record_threshold_count)
+ control->tbl_rai.health_percent = ((control->record_threshold_count -
+ bad_page_count) * 100) / control->record_threshold_count;
+
+ /* Recalc the checksum.
+ */
+ csum = 0;
+ for (pp = buf; pp < buf + buf_size; pp++)
+ csum += *pp;
+
+ csum += __calc_hdr_byte_sum(control);
+ if (control->tbl_hdr.version >= RAS_TABLE_VER_V2_1)
+ csum += __calc_ras_info_byte_sum(control);
+ /* avoid sign extension when assigning to "checksum" */
+ csum = -csum;
+ control->tbl_hdr.checksum = csum;
+ res = __write_table_header(control);
+ if (!res && control->tbl_hdr.version > RAS_TABLE_VER_V1)
+ res = __write_table_ras_info(control);
+Out:
+ kfree(buf);
+ return res;
+}
+
+/**
+ * ras_core_eeprom_append -- append records to the EEPROM RAS table
+ * @control: pointer to control structure
+ * @record: array of records to append
+ * @num: number of records in @record array
+ *
+ * Append @num records to the table, calculate the checksum and write
+ * the table back to EEPROM. The maximum number of records that
+ * can be appended is between 1 and control->ras_max_record_count,
+ * regardless of how many records are already stored in the table.
+ *
+ * Return 0 on success or if EEPROM is not supported, -errno on error.
+ */
+int ras_eeprom_append(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, const u32 num)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ int res;
+
+ if (!__is_ras_eeprom_supported(ras_core))
+ return 0;
+
+ if (num == 0) {
+ RAS_DEV_ERR(ras_core->dev, "will not append 0 records\n");
+ return -EINVAL;
+ } else if ((num + control->ras_num_recs) > control->ras_max_record_count) {
+ RAS_DEV_ERR(ras_core->dev,
+ "cannot append %d records than the size of table %d\n",
+ num, control->ras_max_record_count);
+ return -EINVAL;
+ }
+
+ mutex_lock(&control->ras_tbl_mutex);
+ res = ras_eeprom_append_table(control, record, num);
+ if (!res)
+ res = ras_eeprom_update_header(control);
+
+ mutex_unlock(&control->ras_tbl_mutex);
+
+ return res;
+}
+
+/**
+ * __ras_eeprom_read -- read indexed from EEPROM into buffer
+ * @control: pointer to control structure
+ * @buf: pointer to buffer to read into
+ * @fri: first record index, start reading at this index, absolute index
+ * @num: number of records to read
+ *
+ * The caller must hold the table mutex in @control.
+ * Return 0 on success, -errno otherwise.
+ */
+static int __ras_eeprom_read(struct ras_eeprom_control *control,
+ u8 *buf, const u32 fri, const u32 num)
+{
+ struct ras_core_context *ras_core = to_ras_core_context(control);
+ u32 buf_size;
+ int res;
+
+ /* i2c may be unstable in gpu reset */
+ buf_size = num * RAS_TABLE_RECORD_SIZE;
+ res = __eeprom_read(ras_core,
+ control->i2c_address +
+ RAS_INDEX_TO_OFFSET(control, fri),
+ buf, buf_size);
+ if (res < 0) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Reading %d EEPROM table records error:%d\n", num, res);
+ } else if (res < buf_size) {
+ /* Short read, return error.
+ */
+ RAS_DEV_ERR(ras_core->dev,
+ "Read %d records out of %d\n",
+ (res/RAS_TABLE_RECORD_SIZE), num);
+ res = -EIO;
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+int ras_eeprom_read(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, const u32 num)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ int i, res;
+ u8 *buf, *pp;
+ u32 g0, g1;
+
+ if (!__is_ras_eeprom_supported(ras_core))
+ return 0;
+
+ if (num == 0) {
+ RAS_DEV_ERR(ras_core->dev, "will not read 0 records\n");
+ return -EINVAL;
+ } else if (num > control->ras_num_recs) {
+ RAS_DEV_ERR(ras_core->dev,
+ "too many records to read:%d available:%d\n",
+ num, control->ras_num_recs);
+ return -EINVAL;
+ }
+
+ buf = kcalloc(num, RAS_TABLE_RECORD_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* Determine how many records to read, from the first record
+ * index, fri, to the end of the table, and from the beginning
+ * of the table, such that the total number of records is
+ * @num, and we handle wrap around when fri > 0 and
+ * fri + num > RAS_MAX_RECORD_COUNT.
+ *
+ * First we compute the index of the last element
+ * which would be fetched from each region,
+ * g0 is in [fri, fri + num - 1], and
+ * g1 is in [0, RAS_MAX_RECORD_COUNT - 1].
+ * Then, if g0 < RAS_MAX_RECORD_COUNT, the index of
+ * the last element to fetch, we set g0 to _the number_
+ * of elements to fetch, @num, since we know that the last
+ * indexed to be fetched does not exceed the table.
+ *
+ * If, however, g0 >= RAS_MAX_RECORD_COUNT, then
+ * we set g0 to the number of elements to read
+ * until the end of the table, and g1 to the number of
+ * elements to read from the beginning of the table.
+ */
+ g0 = control->ras_fri + num - 1;
+ g1 = g0 % control->ras_max_record_count;
+ if (g0 < control->ras_max_record_count) {
+ g0 = num;
+ g1 = 0;
+ } else {
+ g0 = control->ras_max_record_count - control->ras_fri;
+ g1 += 1;
+ }
+
+ mutex_lock(&control->ras_tbl_mutex);
+ res = __ras_eeprom_read(control, buf, control->ras_fri, g0);
+ if (res)
+ goto Out;
+ if (g1) {
+ res = __ras_eeprom_read(control,
+ buf + g0 * RAS_TABLE_RECORD_SIZE, 0, g1);
+ if (res)
+ goto Out;
+ }
+
+ res = 0;
+
+ /* Read up everything? Then transform.
+ */
+ pp = buf;
+ for (i = 0; i < num; i++, pp += RAS_TABLE_RECORD_SIZE) {
+ __decode_table_record_from_buf(control, &record[i], pp);
+
+ /* update bad channel bitmap */
+ if ((record[i].mem_channel < BITS_PER_TYPE(control->bad_channel_bitmap)) &&
+ !(control->bad_channel_bitmap & (1 << record[i].mem_channel))) {
+ control->bad_channel_bitmap |= 1 << record[i].mem_channel;
+ control->update_channel_flag = true;
+ }
+ }
+Out:
+ kfree(buf);
+ mutex_unlock(&control->ras_tbl_mutex);
+
+ return res;
+}
+
+uint32_t ras_eeprom_max_record_count(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+
+ /* get available eeprom table version first before eeprom table init */
+ ras_set_eeprom_table_version(control);
+
+ if (control->tbl_hdr.version >= RAS_TABLE_VER_V2_1)
+ return RAS_MAX_RECORD_COUNT_V2_1;
+ else
+ return RAS_MAX_RECORD_COUNT;
+}
+
+/**
+ * __verify_ras_table_checksum -- verify the RAS EEPROM table checksum
+ * @control: pointer to control structure
+ *
+ * Check the checksum of the stored in EEPROM RAS table.
+ *
+ * Return 0 if the checksum is correct,
+ * positive if it is not correct, and
+ * -errno on I/O error.
+ */
+static int __verify_ras_table_checksum(struct ras_eeprom_control *control)
+{
+ struct ras_core_context *ras_core = to_ras_core_context(control);
+ int buf_size, res;
+ u8 csum, *buf, *pp;
+
+ if (control->tbl_hdr.version >= RAS_TABLE_VER_V2_1)
+ buf_size = RAS_TABLE_HEADER_SIZE +
+ RAS_TABLE_V2_1_INFO_SIZE +
+ control->ras_num_recs * RAS_TABLE_RECORD_SIZE;
+ else
+ buf_size = RAS_TABLE_HEADER_SIZE +
+ control->ras_num_recs * RAS_TABLE_RECORD_SIZE;
+
+ buf = kzalloc(buf_size, GFP_KERNEL);
+ if (!buf) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Out of memory checking RAS table checksum.\n");
+ return -ENOMEM;
+ }
+
+ res = __eeprom_read(ras_core,
+ control->i2c_address +
+ control->ras_header_offset,
+ buf, buf_size);
+ if (res < buf_size) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Partial read for checksum, res:%d\n", res);
+ /* On partial reads, return -EIO.
+ */
+ if (res >= 0)
+ res = -EIO;
+ goto Out;
+ }
+
+ csum = 0;
+ for (pp = buf; pp < buf + buf_size; pp++)
+ csum += *pp;
+Out:
+ kfree(buf);
+ return res < 0 ? res : csum;
+}
+
+static int __read_table_ras_info(struct ras_eeprom_control *control)
+{
+ struct ras_eeprom_table_ras_info *rai = &control->tbl_rai;
+ struct ras_core_context *ras_core = to_ras_core_context(control);
+ unsigned char *buf;
+ int res;
+
+ buf = kzalloc(RAS_TABLE_V2_1_INFO_SIZE, GFP_KERNEL);
+ if (!buf) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to alloc buf to read EEPROM table ras info\n");
+ return -ENOMEM;
+ }
+
+ /**
+ * EEPROM table V2_1 supports ras info,
+ * read EEPROM table ras info
+ */
+ res = __eeprom_read(ras_core,
+ control->i2c_address + control->ras_info_offset,
+ buf, RAS_TABLE_V2_1_INFO_SIZE);
+ if (res < RAS_TABLE_V2_1_INFO_SIZE) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to read EEPROM table ras info, res:%d\n", res);
+ res = res >= 0 ? -EIO : res;
+ goto Out;
+ }
+
+ __decode_table_ras_info_from_buf(rai, buf);
+
+Out:
+ kfree(buf);
+ return res == RAS_TABLE_V2_1_INFO_SIZE ? 0 : res;
+}
+
+static int __check_ras_table_status(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ unsigned char buf[RAS_TABLE_HEADER_SIZE] = { 0 };
+ struct ras_eeprom_table_header *hdr;
+ int res;
+
+ hdr = &control->tbl_hdr;
+
+ if (!__is_ras_eeprom_supported(ras_core))
+ return 0;
+
+ if (!__get_eeprom_i2c_addr(ras_core, control))
+ return -EINVAL;
+
+ control->ras_header_offset = RAS_HDR_START;
+ control->ras_info_offset = RAS_TABLE_V2_1_INFO_START;
+ mutex_init(&control->ras_tbl_mutex);
+
+ /* Read the table header from EEPROM address */
+ res = __eeprom_read(ras_core,
+ control->i2c_address + control->ras_header_offset,
+ buf, RAS_TABLE_HEADER_SIZE);
+ if (res < RAS_TABLE_HEADER_SIZE) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to read EEPROM table header, res:%d\n", res);
+ return res >= 0 ? -EIO : res;
+ }
+
+ __decode_table_header_from_buf(hdr, buf);
+
+ if (hdr->header != RAS_TABLE_HDR_VAL &&
+ hdr->header != RAS_TABLE_HDR_BAD) {
+ RAS_DEV_INFO(ras_core->dev, "Creating a new EEPROM table");
+ return ras_eeprom_reset_table(ras_core);
+ }
+
+ switch (hdr->version) {
+ case RAS_TABLE_VER_V2_1:
+ case RAS_TABLE_VER_V3:
+ control->ras_num_recs = RAS_NUM_RECS_V2_1(hdr);
+ control->ras_record_offset = RAS_RECORD_START_V2_1;
+ control->ras_max_record_count = RAS_MAX_RECORD_COUNT_V2_1;
+ break;
+ case RAS_TABLE_VER_V1:
+ control->ras_num_recs = RAS_NUM_RECS(hdr);
+ control->ras_record_offset = RAS_RECORD_START;
+ control->ras_max_record_count = RAS_MAX_RECORD_COUNT;
+ break;
+ default:
+ RAS_DEV_ERR(ras_core->dev,
+ "RAS header invalid, unsupported version: %u",
+ hdr->version);
+ return -EINVAL;
+ }
+
+ if (control->ras_num_recs > control->ras_max_record_count) {
+ RAS_DEV_ERR(ras_core->dev,
+ "RAS header invalid, records in header: %u max allowed :%u",
+ control->ras_num_recs, control->ras_max_record_count);
+ return -EINVAL;
+ }
+
+ control->ras_fri = RAS_OFFSET_TO_INDEX(control, hdr->first_rec_offset);
+
+ return 0;
+}
+
+int ras_eeprom_check_storage_status(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ struct ras_eeprom_table_header *hdr;
+ int bad_page_count;
+ int res = 0;
+
+ if (!__is_ras_eeprom_supported(ras_core))
+ return 0;
+
+ if (!__get_eeprom_i2c_addr(ras_core, control))
+ return -EINVAL;
+
+ hdr = &control->tbl_hdr;
+
+ bad_page_count = ras_umc_get_badpage_count(ras_core);
+ if (hdr->header == RAS_TABLE_HDR_VAL) {
+ RAS_DEV_INFO(ras_core->dev,
+ "Found existing EEPROM table with %d records\n",
+ bad_page_count);
+
+ if (hdr->version >= RAS_TABLE_VER_V2_1) {
+ res = __read_table_ras_info(control);
+ if (res)
+ return res;
+ }
+
+ res = __verify_ras_table_checksum(control);
+ if (res)
+ RAS_DEV_ERR(ras_core->dev,
+ "RAS table incorrect checksum or error:%d\n", res);
+
+ /* Warn if we are at 90% of the threshold or above
+ */
+ if (10 * bad_page_count >= 9 * control->record_threshold_count)
+ RAS_DEV_WARN(ras_core->dev,
+ "RAS records:%u exceeds 90%% of threshold:%d\n",
+ bad_page_count,
+ control->record_threshold_count);
+
+ } else if (hdr->header == RAS_TABLE_HDR_BAD &&
+ control->record_threshold_config != 0) {
+ if (hdr->version >= RAS_TABLE_VER_V2_1) {
+ res = __read_table_ras_info(control);
+ if (res)
+ return res;
+ }
+
+ res = __verify_ras_table_checksum(control);
+ if (res)
+ RAS_DEV_ERR(ras_core->dev,
+ "RAS Table incorrect checksum or error:%d\n", res);
+
+ if (control->record_threshold_count >= bad_page_count) {
+ /* This means that, the threshold was increased since
+ * the last time the system was booted, and now,
+ * ras->record_threshold_count - control->num_recs > 0,
+ * so that at least one more record can be saved,
+ * before the page count threshold is reached.
+ */
+ RAS_DEV_INFO(ras_core->dev,
+ "records:%d threshold:%d, resetting RAS table header signature",
+ bad_page_count,
+ control->record_threshold_count);
+ res = ras_eeprom_correct_header_tag(control, RAS_TABLE_HDR_VAL);
+ } else {
+ RAS_DEV_ERR(ras_core->dev, "RAS records:%d exceed threshold:%d",
+ bad_page_count, control->record_threshold_count);
+ if ((control->record_threshold_config == WARN_NONSTOP_OVER_THRESHOLD) ||
+ (control->record_threshold_config == NONSTOP_OVER_THRESHOLD)) {
+ RAS_DEV_WARN(ras_core->dev,
+ "Please consult AMD Service Action Guide (SAG) for appropriate service procedures\n");
+ res = 0;
+ } else {
+ ras_core->is_rma = true;
+ RAS_DEV_ERR(ras_core->dev,
+ "User defined threshold is set, runtime service will be halt when threshold is reached\n");
+ }
+ }
+ }
+
+ return res < 0 ? res : 0;
+}
+
+int ras_eeprom_hw_init(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control;
+ struct ras_eeprom_config *eeprom_cfg;
+
+ if (!ras_core)
+ return -EINVAL;
+
+ ras_core->is_rma = false;
+
+ control = &ras_core->ras_eeprom;
+
+ memset(control, 0, sizeof(*control));
+
+ eeprom_cfg = &ras_core->config->eeprom_cfg;
+ control->record_threshold_config =
+ eeprom_cfg->eeprom_record_threshold_config;
+
+ control->record_threshold_count = ras_eeprom_max_record_count(ras_core);
+ if (eeprom_cfg->eeprom_record_threshold_count <
+ control->record_threshold_count)
+ control->record_threshold_count =
+ eeprom_cfg->eeprom_record_threshold_count;
+
+ control->sys_func = eeprom_cfg->eeprom_sys_fn;
+ control->max_read_len = eeprom_cfg->max_i2c_read_len;
+ control->max_write_len = eeprom_cfg->max_i2c_write_len;
+ control->i2c_adapter = eeprom_cfg->eeprom_i2c_adapter;
+ control->i2c_port = eeprom_cfg->eeprom_i2c_port;
+ control->i2c_address = eeprom_cfg->eeprom_i2c_addr;
+
+ control->update_channel_flag = false;
+
+ return __check_ras_table_status(ras_core);
+}
+
+int ras_eeprom_hw_fini(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control;
+
+ if (!ras_core)
+ return -EINVAL;
+
+ control = &ras_core->ras_eeprom;
+ mutex_destroy(&control->ras_tbl_mutex);
+
+ return 0;
+}
+
+uint32_t ras_eeprom_get_record_count(struct ras_core_context *ras_core)
+{
+ if (!ras_core)
+ return 0;
+
+ return ras_core->ras_eeprom.ras_num_recs;
+}
+
+void ras_eeprom_sync_info(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control;
+
+ if (!ras_core)
+ return;
+
+ control = &ras_core->ras_eeprom;
+ ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM,
+ &control->ras_num_recs);
+ ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_CHANNEL_BITMAP,
+ &control->bad_channel_bitmap);
+}
+
+enum ras_gpu_health_status
+ ras_eeprom_check_gpu_status(struct ras_core_context *ras_core)
+{
+ struct ras_eeprom_control *control = &ras_core->ras_eeprom;
+ struct ras_eeprom_table_ras_info *rai = &control->tbl_rai;
+
+ if (!__is_ras_eeprom_supported(ras_core) ||
+ !control->record_threshold_config)
+ return RAS_GPU_HEALTH_NONE;
+
+ if (control->tbl_hdr.header == RAS_TABLE_HDR_BAD)
+ return RAS_GPU_IN_BAD_STATUS;
+
+ return rai->rma_status;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h
new file mode 100644
index 000000000000..2abe566c18b6
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_EEPROM_H__
+#define __RAS_EEPROM_H__
+#include "ras_sys.h"
+
+#define RAS_TABLE_VER_V1 0x00010000
+#define RAS_TABLE_VER_V2_1 0x00021000
+#define RAS_TABLE_VER_V3 0x00030000
+
+#define NONSTOP_OVER_THRESHOLD -2
+#define WARN_NONSTOP_OVER_THRESHOLD -1
+#define DISABLE_RETIRE_PAGE 0
+
+/*
+ * Bad address pfn : eeprom_umc_record.retired_row_pfn[39:0],
+ * nps mode: eeprom_umc_record.retired_row_pfn[47:40]
+ */
+#define EEPROM_RECORD_UMC_ADDR_MASK 0xFFFFFFFFFFULL
+#define EEPROM_RECORD_UMC_NPS_MASK 0xFF0000000000ULL
+#define EEPROM_RECORD_UMC_NPS_SHIFT 40
+
+#define EEPROM_RECORD_UMC_NPS_MODE(RECORD) \
+ (((RECORD)->retired_row_pfn & EEPROM_RECORD_UMC_NPS_MASK) >> \
+ EEPROM_RECORD_UMC_NPS_SHIFT)
+
+#define EEPROM_RECORD_UMC_ADDR_PFN(RECORD) \
+ ((RECORD)->retired_row_pfn & EEPROM_RECORD_UMC_ADDR_MASK)
+
+#define EEPROM_RECORD_SETUP_UMC_ADDR_AND_NPS(RECORD, ADDR, NPS) \
+do { \
+ uint64_t tmp = (NPS); \
+ tmp = ((tmp << EEPROM_RECORD_UMC_NPS_SHIFT) & EEPROM_RECORD_UMC_NPS_MASK); \
+ tmp |= (ADDR) & EEPROM_RECORD_UMC_ADDR_MASK; \
+ (RECORD)->retired_row_pfn = tmp; \
+} while (0)
+
+enum ras_gpu_health_status {
+ RAS_GPU_HEALTH_NONE = 0,
+ RAS_GPU_HEALTH_USABLE = 1,
+ RAS_GPU_RETIRED__ECC_REACH_THRESHOLD = 2,
+ RAS_GPU_IN_BAD_STATUS = 3,
+};
+
+enum ras_eeprom_err_type {
+ RAS_EEPROM_ERR_NA,
+ RAS_EEPROM_ERR_RECOVERABLE,
+ RAS_EEPROM_ERR_NON_RECOVERABLE,
+ RAS_EEPROM_ERR_COUNT,
+};
+
+struct ras_eeprom_table_header {
+ uint32_t header;
+ uint32_t version;
+ uint32_t first_rec_offset;
+ uint32_t tbl_size;
+ uint32_t checksum;
+} __packed;
+
+struct ras_eeprom_table_ras_info {
+ u8 rma_status;
+ u8 health_percent;
+ u16 ecc_page_threshold;
+ u32 padding[64 - 1];
+} __packed;
+
+struct ras_eeprom_control {
+ struct ras_eeprom_table_header tbl_hdr;
+ struct ras_eeprom_table_ras_info tbl_rai;
+
+ /* record threshold */
+ int record_threshold_config;
+ uint32_t record_threshold_count;
+ bool update_channel_flag;
+
+ const struct ras_eeprom_sys_func *sys_func;
+ void *i2c_adapter;
+ u32 i2c_port;
+ u16 max_read_len;
+ u16 max_write_len;
+
+ /* Base I2C EEPPROM 19-bit memory address,
+ * where the table is located. For more information,
+ * see top of amdgpu_eeprom.c.
+ */
+ u32 i2c_address;
+
+ /* The byte offset off of @i2c_address
+ * where the table header is found,
+ * and where the records start--always
+ * right after the header.
+ */
+ u32 ras_header_offset;
+ u32 ras_info_offset;
+ u32 ras_record_offset;
+
+ /* Number of records in the table.
+ */
+ u32 ras_num_recs;
+
+ /* First record index to read, 0-based.
+ * Range is [0, num_recs-1]. This is
+ * an absolute index, starting right after
+ * the table header.
+ */
+ u32 ras_fri;
+
+ /* Maximum possible number of records
+ * we could store, i.e. the maximum capacity
+ * of the table.
+ */
+ u32 ras_max_record_count;
+
+ /* Protect table access via this mutex.
+ */
+ struct mutex ras_tbl_mutex;
+
+ /* Record channel info which occurred bad pages
+ */
+ u32 bad_channel_bitmap;
+};
+
+/*
+ * Represents single table record. Packed to be easily serialized into byte
+ * stream.
+ */
+struct eeprom_umc_record {
+
+ union {
+ uint64_t address;
+ uint64_t offset;
+ };
+
+ uint64_t retired_row_pfn;
+ uint64_t ts;
+
+ enum ras_eeprom_err_type err_type;
+
+ union {
+ unsigned char bank;
+ unsigned char cu;
+ };
+
+ unsigned char mem_channel;
+ unsigned char mcumc_id;
+
+ /* The following variables will not be saved to eeprom.
+ */
+ uint64_t cur_nps_retired_row_pfn;
+ uint32_t cur_nps_bank;
+ uint32_t cur_nps;
+};
+
+struct ras_core_context;
+int ras_eeprom_hw_init(struct ras_core_context *ras_core);
+int ras_eeprom_hw_fini(struct ras_core_context *ras_core);
+
+int ras_eeprom_reset_table(struct ras_core_context *ras_core);
+
+bool ras_eeprom_check_safety_watermark(struct ras_core_context *ras_core);
+
+int ras_eeprom_read(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *records, const u32 num);
+
+int ras_eeprom_append(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *records, const u32 num);
+
+uint32_t ras_eeprom_max_record_count(struct ras_core_context *ras_core);
+uint32_t ras_eeprom_get_record_count(struct ras_core_context *ras_core);
+void ras_eeprom_sync_info(struct ras_core_context *ras_core);
+
+int ras_eeprom_check_storage_status(struct ras_core_context *ras_core);
+enum ras_gpu_health_status
+ ras_eeprom_check_gpu_status(struct ras_core_context *ras_core);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_gfx.c b/drivers/gpu/drm/amd/ras/rascore/ras_gfx.c
new file mode 100644
index 000000000000..f5ce28777705
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_gfx.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_gfx_v9_0.h"
+#include "ras_gfx.h"
+#include "ras_core_status.h"
+
+static const struct ras_gfx_ip_func *ras_gfx_get_ip_funcs(
+ struct ras_core_context *ras_core, uint32_t ip_version)
+{
+ switch (ip_version) {
+ case IP_VERSION(9, 4, 3):
+ case IP_VERSION(9, 4, 4):
+ case IP_VERSION(9, 5, 0):
+ return &gfx_ras_func_v9_0;
+ default:
+ RAS_DEV_ERR(ras_core->dev,
+ "GFX ip version(0x%x) is not supported!\n", ip_version);
+ break;
+ }
+
+ return NULL;
+}
+
+int ras_gfx_get_ta_subblock(struct ras_core_context *ras_core,
+ uint32_t error_type, uint32_t subblock, uint32_t *ta_subblock)
+{
+ struct ras_gfx *gfx = &ras_core->ras_gfx;
+
+ return gfx->ip_func->get_ta_subblock(ras_core,
+ error_type, subblock, ta_subblock);
+}
+
+int ras_gfx_hw_init(struct ras_core_context *ras_core)
+{
+ struct ras_gfx *gfx = &ras_core->ras_gfx;
+
+ gfx->gfx_ip_version = ras_core->config->gfx_ip_version;
+
+ gfx->ip_func = ras_gfx_get_ip_funcs(ras_core, gfx->gfx_ip_version);
+
+ return gfx->ip_func ? RAS_CORE_OK : -EINVAL;
+}
+
+int ras_gfx_hw_fini(struct ras_core_context *ras_core)
+{
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_gfx.h b/drivers/gpu/drm/amd/ras/rascore/ras_gfx.h
new file mode 100644
index 000000000000..8a42d69fb0ad
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_gfx.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_GFX_H__
+#define __RAS_GFX_H__
+
+struct ras_gfx_ip_func {
+ int (*get_ta_subblock)(struct ras_core_context *ras_core,
+ uint32_t error_type, uint32_t subblock, uint32_t *ta_subblock);
+};
+
+struct ras_gfx {
+ uint32_t gfx_ip_version;
+ const struct ras_gfx_ip_func *ip_func;
+};
+
+int ras_gfx_hw_init(struct ras_core_context *ras_core);
+int ras_gfx_hw_fini(struct ras_core_context *ras_core);
+
+int ras_gfx_get_ta_subblock(struct ras_core_context *ras_core,
+ uint32_t error_type, uint32_t subblock, uint32_t *ta_subblock);
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.c
new file mode 100644
index 000000000000..6213d3f125be
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.c
@@ -0,0 +1,426 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_gfx_v9_0.h"
+#include "ras_core_status.h"
+
+enum ta_gfx_v9_subblock {
+ /*CPC*/
+ TA_GFX_V9__GFX_CPC_INDEX_START = 0,
+ TA_GFX_V9__GFX_CPC_SCRATCH = TA_GFX_V9__GFX_CPC_INDEX_START,
+ TA_GFX_V9__GFX_CPC_UCODE,
+ TA_GFX_V9__GFX_DC_STATE_ME1,
+ TA_GFX_V9__GFX_DC_CSINVOC_ME1,
+ TA_GFX_V9__GFX_DC_RESTORE_ME1,
+ TA_GFX_V9__GFX_DC_STATE_ME2,
+ TA_GFX_V9__GFX_DC_CSINVOC_ME2,
+ TA_GFX_V9__GFX_DC_RESTORE_ME2,
+ TA_GFX_V9__GFX_CPC_INDEX_END = TA_GFX_V9__GFX_DC_RESTORE_ME2,
+ /* CPF*/
+ TA_GFX_V9__GFX_CPF_INDEX_START,
+ TA_GFX_V9__GFX_CPF_ROQ_ME2 = TA_GFX_V9__GFX_CPF_INDEX_START,
+ TA_GFX_V9__GFX_CPF_ROQ_ME1,
+ TA_GFX_V9__GFX_CPF_TAG,
+ TA_GFX_V9__GFX_CPF_INDEX_END = TA_GFX_V9__GFX_CPF_TAG,
+ /* CPG*/
+ TA_GFX_V9__GFX_CPG_INDEX_START,
+ TA_GFX_V9__GFX_CPG_DMA_ROQ = TA_GFX_V9__GFX_CPG_INDEX_START,
+ TA_GFX_V9__GFX_CPG_DMA_TAG,
+ TA_GFX_V9__GFX_CPG_TAG,
+ TA_GFX_V9__GFX_CPG_INDEX_END = TA_GFX_V9__GFX_CPG_TAG,
+ /* GDS*/
+ TA_GFX_V9__GFX_GDS_INDEX_START,
+ TA_GFX_V9__GFX_GDS_MEM = TA_GFX_V9__GFX_GDS_INDEX_START,
+ TA_GFX_V9__GFX_GDS_INPUT_QUEUE,
+ TA_GFX_V9__GFX_GDS_OA_PHY_CMD_RAM_MEM,
+ TA_GFX_V9__GFX_GDS_OA_PHY_DATA_RAM_MEM,
+ TA_GFX_V9__GFX_GDS_OA_PIPE_MEM,
+ TA_GFX_V9__GFX_GDS_INDEX_END = TA_GFX_V9__GFX_GDS_OA_PIPE_MEM,
+ /* SPI*/
+ TA_GFX_V9__GFX_SPI_SR_MEM,
+ /* SQ*/
+ TA_GFX_V9__GFX_SQ_INDEX_START,
+ TA_GFX_V9__GFX_SQ_SGPR = TA_GFX_V9__GFX_SQ_INDEX_START,
+ TA_GFX_V9__GFX_SQ_LDS_D,
+ TA_GFX_V9__GFX_SQ_LDS_I,
+ TA_GFX_V9__GFX_SQ_VGPR, /* VGPR = SP*/
+ TA_GFX_V9__GFX_SQ_INDEX_END = TA_GFX_V9__GFX_SQ_VGPR,
+ /* SQC (3 ranges)*/
+ TA_GFX_V9__GFX_SQC_INDEX_START,
+ /* SQC range 0*/
+ TA_GFX_V9__GFX_SQC_INDEX0_START = TA_GFX_V9__GFX_SQC_INDEX_START,
+ TA_GFX_V9__GFX_SQC_INST_UTCL1_LFIFO =
+ TA_GFX_V9__GFX_SQC_INDEX0_START,
+ TA_GFX_V9__GFX_SQC_DATA_CU0_WRITE_DATA_BUF,
+ TA_GFX_V9__GFX_SQC_DATA_CU0_UTCL1_LFIFO,
+ TA_GFX_V9__GFX_SQC_DATA_CU1_WRITE_DATA_BUF,
+ TA_GFX_V9__GFX_SQC_DATA_CU1_UTCL1_LFIFO,
+ TA_GFX_V9__GFX_SQC_DATA_CU2_WRITE_DATA_BUF,
+ TA_GFX_V9__GFX_SQC_DATA_CU2_UTCL1_LFIFO,
+ TA_GFX_V9__GFX_SQC_INDEX0_END =
+ TA_GFX_V9__GFX_SQC_DATA_CU2_UTCL1_LFIFO,
+ /* SQC range 1*/
+ TA_GFX_V9__GFX_SQC_INDEX1_START,
+ TA_GFX_V9__GFX_SQC_INST_BANKA_TAG_RAM =
+ TA_GFX_V9__GFX_SQC_INDEX1_START,
+ TA_GFX_V9__GFX_SQC_INST_BANKA_UTCL1_MISS_FIFO,
+ TA_GFX_V9__GFX_SQC_INST_BANKA_MISS_FIFO,
+ TA_GFX_V9__GFX_SQC_INST_BANKA_BANK_RAM,
+ TA_GFX_V9__GFX_SQC_DATA_BANKA_TAG_RAM,
+ TA_GFX_V9__GFX_SQC_DATA_BANKA_HIT_FIFO,
+ TA_GFX_V9__GFX_SQC_DATA_BANKA_MISS_FIFO,
+ TA_GFX_V9__GFX_SQC_DATA_BANKA_DIRTY_BIT_RAM,
+ TA_GFX_V9__GFX_SQC_DATA_BANKA_BANK_RAM,
+ TA_GFX_V9__GFX_SQC_INDEX1_END =
+ TA_GFX_V9__GFX_SQC_DATA_BANKA_BANK_RAM,
+ /* SQC range 2*/
+ TA_GFX_V9__GFX_SQC_INDEX2_START,
+ TA_GFX_V9__GFX_SQC_INST_BANKB_TAG_RAM =
+ TA_GFX_V9__GFX_SQC_INDEX2_START,
+ TA_GFX_V9__GFX_SQC_INST_BANKB_UTCL1_MISS_FIFO,
+ TA_GFX_V9__GFX_SQC_INST_BANKB_MISS_FIFO,
+ TA_GFX_V9__GFX_SQC_INST_BANKB_BANK_RAM,
+ TA_GFX_V9__GFX_SQC_DATA_BANKB_TAG_RAM,
+ TA_GFX_V9__GFX_SQC_DATA_BANKB_HIT_FIFO,
+ TA_GFX_V9__GFX_SQC_DATA_BANKB_MISS_FIFO,
+ TA_GFX_V9__GFX_SQC_DATA_BANKB_DIRTY_BIT_RAM,
+ TA_GFX_V9__GFX_SQC_DATA_BANKB_BANK_RAM,
+ TA_GFX_V9__GFX_SQC_INDEX2_END =
+ TA_GFX_V9__GFX_SQC_DATA_BANKB_BANK_RAM,
+ TA_GFX_V9__GFX_SQC_INDEX_END = TA_GFX_V9__GFX_SQC_INDEX2_END,
+ /* TA*/
+ TA_GFX_V9__GFX_TA_INDEX_START,
+ TA_GFX_V9__GFX_TA_FS_DFIFO = TA_GFX_V9__GFX_TA_INDEX_START,
+ TA_GFX_V9__GFX_TA_FS_AFIFO,
+ TA_GFX_V9__GFX_TA_FL_LFIFO,
+ TA_GFX_V9__GFX_TA_FX_LFIFO,
+ TA_GFX_V9__GFX_TA_FS_CFIFO,
+ TA_GFX_V9__GFX_TA_INDEX_END = TA_GFX_V9__GFX_TA_FS_CFIFO,
+ /* TCA*/
+ TA_GFX_V9__GFX_TCA_INDEX_START,
+ TA_GFX_V9__GFX_TCA_HOLE_FIFO = TA_GFX_V9__GFX_TCA_INDEX_START,
+ TA_GFX_V9__GFX_TCA_REQ_FIFO,
+ TA_GFX_V9__GFX_TCA_INDEX_END = TA_GFX_V9__GFX_TCA_REQ_FIFO,
+ /* TCC (5 sub-ranges)*/
+ TA_GFX_V9__GFX_TCC_INDEX_START,
+ /* TCC range 0*/
+ TA_GFX_V9__GFX_TCC_INDEX0_START = TA_GFX_V9__GFX_TCC_INDEX_START,
+ TA_GFX_V9__GFX_TCC_CACHE_DATA = TA_GFX_V9__GFX_TCC_INDEX0_START,
+ TA_GFX_V9__GFX_TCC_CACHE_DATA_BANK_0_1,
+ TA_GFX_V9__GFX_TCC_CACHE_DATA_BANK_1_0,
+ TA_GFX_V9__GFX_TCC_CACHE_DATA_BANK_1_1,
+ TA_GFX_V9__GFX_TCC_CACHE_DIRTY_BANK_0,
+ TA_GFX_V9__GFX_TCC_CACHE_DIRTY_BANK_1,
+ TA_GFX_V9__GFX_TCC_HIGH_RATE_TAG,
+ TA_GFX_V9__GFX_TCC_LOW_RATE_TAG,
+ TA_GFX_V9__GFX_TCC_INDEX0_END = TA_GFX_V9__GFX_TCC_LOW_RATE_TAG,
+ /* TCC range 1*/
+ TA_GFX_V9__GFX_TCC_INDEX1_START,
+ TA_GFX_V9__GFX_TCC_IN_USE_DEC = TA_GFX_V9__GFX_TCC_INDEX1_START,
+ TA_GFX_V9__GFX_TCC_IN_USE_TRANSFER,
+ TA_GFX_V9__GFX_TCC_INDEX1_END =
+ TA_GFX_V9__GFX_TCC_IN_USE_TRANSFER,
+ /* TCC range 2*/
+ TA_GFX_V9__GFX_TCC_INDEX2_START,
+ TA_GFX_V9__GFX_TCC_RETURN_DATA = TA_GFX_V9__GFX_TCC_INDEX2_START,
+ TA_GFX_V9__GFX_TCC_RETURN_CONTROL,
+ TA_GFX_V9__GFX_TCC_UC_ATOMIC_FIFO,
+ TA_GFX_V9__GFX_TCC_WRITE_RETURN,
+ TA_GFX_V9__GFX_TCC_WRITE_CACHE_READ,
+ TA_GFX_V9__GFX_TCC_SRC_FIFO,
+ TA_GFX_V9__GFX_TCC_SRC_FIFO_NEXT_RAM,
+ TA_GFX_V9__GFX_TCC_CACHE_TAG_PROBE_FIFO,
+ TA_GFX_V9__GFX_TCC_INDEX2_END =
+ TA_GFX_V9__GFX_TCC_CACHE_TAG_PROBE_FIFO,
+ /* TCC range 3*/
+ TA_GFX_V9__GFX_TCC_INDEX3_START,
+ TA_GFX_V9__GFX_TCC_LATENCY_FIFO = TA_GFX_V9__GFX_TCC_INDEX3_START,
+ TA_GFX_V9__GFX_TCC_LATENCY_FIFO_NEXT_RAM,
+ TA_GFX_V9__GFX_TCC_INDEX3_END =
+ TA_GFX_V9__GFX_TCC_LATENCY_FIFO_NEXT_RAM,
+ /* TCC range 4*/
+ TA_GFX_V9__GFX_TCC_INDEX4_START,
+ TA_GFX_V9__GFX_TCC_WRRET_TAG_WRITE_RETURN =
+ TA_GFX_V9__GFX_TCC_INDEX4_START,
+ TA_GFX_V9__GFX_TCC_ATOMIC_RETURN_BUFFER,
+ TA_GFX_V9__GFX_TCC_INDEX4_END =
+ TA_GFX_V9__GFX_TCC_ATOMIC_RETURN_BUFFER,
+ TA_GFX_V9__GFX_TCC_INDEX_END = TA_GFX_V9__GFX_TCC_INDEX4_END,
+ /* TCI*/
+ TA_GFX_V9__GFX_TCI_WRITE_RAM,
+ /* TCP*/
+ TA_GFX_V9__GFX_TCP_INDEX_START,
+ TA_GFX_V9__GFX_TCP_CACHE_RAM = TA_GFX_V9__GFX_TCP_INDEX_START,
+ TA_GFX_V9__GFX_TCP_LFIFO_RAM,
+ TA_GFX_V9__GFX_TCP_CMD_FIFO,
+ TA_GFX_V9__GFX_TCP_VM_FIFO,
+ TA_GFX_V9__GFX_TCP_DB_RAM,
+ TA_GFX_V9__GFX_TCP_UTCL1_LFIFO0,
+ TA_GFX_V9__GFX_TCP_UTCL1_LFIFO1,
+ TA_GFX_V9__GFX_TCP_INDEX_END = TA_GFX_V9__GFX_TCP_UTCL1_LFIFO1,
+ /* TD*/
+ TA_GFX_V9__GFX_TD_INDEX_START,
+ TA_GFX_V9__GFX_TD_SS_FIFO_LO = TA_GFX_V9__GFX_TD_INDEX_START,
+ TA_GFX_V9__GFX_TD_SS_FIFO_HI,
+ TA_GFX_V9__GFX_TD_CS_FIFO,
+ TA_GFX_V9__GFX_TD_INDEX_END = TA_GFX_V9__GFX_TD_CS_FIFO,
+ /* EA (3 sub-ranges)*/
+ TA_GFX_V9__GFX_EA_INDEX_START,
+ /* EA range 0*/
+ TA_GFX_V9__GFX_EA_INDEX0_START = TA_GFX_V9__GFX_EA_INDEX_START,
+ TA_GFX_V9__GFX_EA_DRAMRD_CMDMEM = TA_GFX_V9__GFX_EA_INDEX0_START,
+ TA_GFX_V9__GFX_EA_DRAMWR_CMDMEM,
+ TA_GFX_V9__GFX_EA_DRAMWR_DATAMEM,
+ TA_GFX_V9__GFX_EA_RRET_TAGMEM,
+ TA_GFX_V9__GFX_EA_WRET_TAGMEM,
+ TA_GFX_V9__GFX_EA_GMIRD_CMDMEM,
+ TA_GFX_V9__GFX_EA_GMIWR_CMDMEM,
+ TA_GFX_V9__GFX_EA_GMIWR_DATAMEM,
+ TA_GFX_V9__GFX_EA_INDEX0_END = TA_GFX_V9__GFX_EA_GMIWR_DATAMEM,
+ /* EA range 1*/
+ TA_GFX_V9__GFX_EA_INDEX1_START,
+ TA_GFX_V9__GFX_EA_DRAMRD_PAGEMEM = TA_GFX_V9__GFX_EA_INDEX1_START,
+ TA_GFX_V9__GFX_EA_DRAMWR_PAGEMEM,
+ TA_GFX_V9__GFX_EA_IORD_CMDMEM,
+ TA_GFX_V9__GFX_EA_IOWR_CMDMEM,
+ TA_GFX_V9__GFX_EA_IOWR_DATAMEM,
+ TA_GFX_V9__GFX_EA_GMIRD_PAGEMEM,
+ TA_GFX_V9__GFX_EA_GMIWR_PAGEMEM,
+ TA_GFX_V9__GFX_EA_INDEX1_END = TA_GFX_V9__GFX_EA_GMIWR_PAGEMEM,
+ /* EA range 2*/
+ TA_GFX_V9__GFX_EA_INDEX2_START,
+ TA_GFX_V9__GFX_EA_MAM_D0MEM = TA_GFX_V9__GFX_EA_INDEX2_START,
+ TA_GFX_V9__GFX_EA_MAM_D1MEM,
+ TA_GFX_V9__GFX_EA_MAM_D2MEM,
+ TA_GFX_V9__GFX_EA_MAM_D3MEM,
+ TA_GFX_V9__GFX_EA_INDEX2_END = TA_GFX_V9__GFX_EA_MAM_D3MEM,
+ TA_GFX_V9__GFX_EA_INDEX_END = TA_GFX_V9__GFX_EA_INDEX2_END,
+ /* UTC VM L2 bank*/
+ TA_GFX_V9__UTC_VML2_BANK_CACHE,
+ /* UTC VM walker*/
+ TA_GFX_V9__UTC_VML2_WALKER,
+ /* UTC ATC L2 2MB cache*/
+ TA_GFX_V9__UTC_ATCL2_CACHE_2M_BANK,
+ /* UTC ATC L2 4KB cache*/
+ TA_GFX_V9__UTC_ATCL2_CACHE_4K_BANK,
+ TA_GFX_V9__GFX_MAX
+};
+
+struct ras_gfx_subblock_t {
+ unsigned char *name;
+ int ta_subblock;
+ int hw_supported_error_type;
+ int sw_supported_error_type;
+};
+
+#define RAS_GFX_SUB_BLOCK(subblock, a, b, c, d, e, f, g, h) \
+ [RAS_GFX_V9__##subblock] = { \
+ #subblock, \
+ TA_GFX_V9__##subblock, \
+ ((a) | ((b) << 1) | ((c) << 2) | ((d) << 3)), \
+ (((e) << 1) | ((f) << 3) | (g) | ((h) << 2)), \
+ }
+
+const struct ras_gfx_subblock_t ras_gfx_v9_0_subblocks[] = {
+ RAS_GFX_SUB_BLOCK(GFX_CPC_SCRATCH, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_CPC_UCODE, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_DC_STATE_ME1, 1, 0, 0, 1, 0, 0, 1, 0),
+ RAS_GFX_SUB_BLOCK(GFX_DC_CSINVOC_ME1, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_DC_RESTORE_ME1, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_DC_STATE_ME2, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_DC_CSINVOC_ME2, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_DC_RESTORE_ME2, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_CPF_ROQ_ME2, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_CPF_ROQ_ME1, 1, 0, 0, 1, 0, 0, 1, 0),
+ RAS_GFX_SUB_BLOCK(GFX_CPF_TAG, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_CPG_DMA_ROQ, 1, 0, 0, 1, 0, 0, 1, 0),
+ RAS_GFX_SUB_BLOCK(GFX_CPG_DMA_TAG, 0, 1, 1, 1, 0, 1, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_CPG_TAG, 0, 1, 1, 1, 1, 1, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_GDS_MEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_GDS_INPUT_QUEUE, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_GDS_OA_PHY_CMD_RAM_MEM, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_GDS_OA_PHY_DATA_RAM_MEM, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_GDS_OA_PIPE_MEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SPI_SR_MEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQ_SGPR, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQ_LDS_D, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_SQ_LDS_I, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQ_VGPR, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_UTCL1_LFIFO, 0, 1, 1, 1, 0, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_CU0_WRITE_DATA_BUF, 0, 1, 1, 1, 0, 0,
+ 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_CU0_UTCL1_LFIFO, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_CU1_WRITE_DATA_BUF, 0, 1, 1, 1, 0, 0,
+ 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_CU1_UTCL1_LFIFO, 0, 1, 1, 1, 1, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_CU2_WRITE_DATA_BUF, 0, 1, 1, 1, 0, 0,
+ 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_CU2_UTCL1_LFIFO, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKA_TAG_RAM, 0, 1, 1, 1, 1, 0, 0,
+ 1),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKA_UTCL1_MISS_FIFO, 1, 0, 0, 1, 0,
+ 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKA_MISS_FIFO, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKA_BANK_RAM, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKA_TAG_RAM, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKA_HIT_FIFO, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKA_MISS_FIFO, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKA_DIRTY_BIT_RAM, 1, 0, 0, 1, 0, 0,
+ 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKA_BANK_RAM, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKB_TAG_RAM, 0, 1, 1, 1, 1, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKB_UTCL1_MISS_FIFO, 1, 0, 0, 1, 0,
+ 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKB_MISS_FIFO, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_INST_BANKB_BANK_RAM, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKB_TAG_RAM, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKB_HIT_FIFO, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKB_MISS_FIFO, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKB_DIRTY_BIT_RAM, 1, 0, 0, 1, 0, 0,
+ 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_SQC_DATA_BANKB_BANK_RAM, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_TA_FS_DFIFO, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_TA_FS_AFIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TA_FL_LFIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TA_FX_LFIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TA_FS_CFIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCA_HOLE_FIFO, 1, 0, 0, 1, 0, 1, 1, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCA_REQ_FIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_CACHE_DATA, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_CACHE_DATA_BANK_0_1, 0, 1, 1, 1, 1, 0, 0,
+ 1),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_CACHE_DATA_BANK_1_0, 0, 1, 1, 1, 1, 0, 0,
+ 1),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_CACHE_DATA_BANK_1_1, 0, 1, 1, 1, 1, 0, 0,
+ 1),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_CACHE_DIRTY_BANK_0, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_CACHE_DIRTY_BANK_1, 0, 1, 1, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_HIGH_RATE_TAG, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_LOW_RATE_TAG, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_IN_USE_DEC, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_IN_USE_TRANSFER, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_RETURN_DATA, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_RETURN_CONTROL, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_UC_ATOMIC_FIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_WRITE_RETURN, 1, 0, 0, 1, 0, 1, 1, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_WRITE_CACHE_READ, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_SRC_FIFO, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_SRC_FIFO_NEXT_RAM, 1, 0, 0, 1, 0, 0, 1, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_CACHE_TAG_PROBE_FIFO, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_LATENCY_FIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_LATENCY_FIFO_NEXT_RAM, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_WRRET_TAG_WRITE_RETURN, 1, 0, 0, 1, 0, 0,
+ 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCC_ATOMIC_RETURN_BUFFER, 1, 0, 0, 1, 0, 0, 0,
+ 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCI_WRITE_RAM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCP_CACHE_RAM, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_TCP_LFIFO_RAM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCP_CMD_FIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCP_VM_FIFO, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCP_DB_RAM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCP_UTCL1_LFIFO0, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TCP_UTCL1_LFIFO1, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TD_SS_FIFO_LO, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_TD_SS_FIFO_HI, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_TD_CS_FIFO, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_DRAMRD_CMDMEM, 0, 1, 1, 1, 1, 0, 0, 1),
+ RAS_GFX_SUB_BLOCK(GFX_EA_DRAMWR_CMDMEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_DRAMWR_DATAMEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_RRET_TAGMEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_WRET_TAGMEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_GMIRD_CMDMEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_GMIWR_CMDMEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_GMIWR_DATAMEM, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_DRAMRD_PAGEMEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_DRAMWR_PAGEMEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_IORD_CMDMEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_IOWR_CMDMEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_IOWR_DATAMEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_GMIRD_PAGEMEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_GMIWR_PAGEMEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_MAM_D0MEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_MAM_D1MEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_MAM_D2MEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(GFX_EA_MAM_D3MEM, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(UTC_VML2_BANK_CACHE, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(UTC_VML2_WALKER, 0, 1, 1, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(UTC_ATCL2_CACHE_2M_BANK, 1, 0, 0, 1, 0, 0, 0, 0),
+ RAS_GFX_SUB_BLOCK(UTC_ATCL2_CACHE_4K_BANK, 0, 1, 1, 1, 0, 0, 0, 0),
+};
+
+static int gfx_v9_0_get_ta_subblock(struct ras_core_context *ras_core,
+ uint32_t error_type, uint32_t subblock, uint32_t *ta_subblock)
+{
+ const struct ras_gfx_subblock_t *gfx_subblock;
+
+ if (subblock >= ARRAY_SIZE(ras_gfx_v9_0_subblocks))
+ return -EINVAL;
+
+ gfx_subblock = &ras_gfx_v9_0_subblocks[subblock];
+ if (!gfx_subblock->name)
+ return -EPERM;
+
+ if (!(gfx_subblock->hw_supported_error_type & error_type)) {
+ RAS_DEV_ERR(ras_core->dev, "GFX Subblock %s, hardware do not support type 0x%x\n",
+ gfx_subblock->name, error_type);
+ return -EPERM;
+ }
+
+ if (!(gfx_subblock->sw_supported_error_type & error_type)) {
+ RAS_DEV_ERR(ras_core->dev, "GFX Subblock %s, driver do not support type 0x%x\n",
+ gfx_subblock->name, error_type);
+ return -EPERM;
+ }
+
+ *ta_subblock = gfx_subblock->ta_subblock;
+
+ return 0;
+}
+
+const struct ras_gfx_ip_func gfx_ras_func_v9_0 = {
+ .get_ta_subblock = gfx_v9_0_get_ta_subblock,
+};
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.h b/drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.h
new file mode 100644
index 000000000000..659b56619747
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_gfx_v9_0.h
@@ -0,0 +1,259 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_GFX_V9_0_H__
+#define __RAS_GFX_V9_0_H__
+
+enum ras_gfx_v9_subblock {
+ /* CPC */
+ RAS_GFX_V9__GFX_CPC_INDEX_START = 0,
+ RAS_GFX_V9__GFX_CPC_SCRATCH =
+ RAS_GFX_V9__GFX_CPC_INDEX_START,
+ RAS_GFX_V9__GFX_CPC_UCODE,
+ RAS_GFX_V9__GFX_DC_STATE_ME1,
+ RAS_GFX_V9__GFX_DC_CSINVOC_ME1,
+ RAS_GFX_V9__GFX_DC_RESTORE_ME1,
+ RAS_GFX_V9__GFX_DC_STATE_ME2,
+ RAS_GFX_V9__GFX_DC_CSINVOC_ME2,
+ RAS_GFX_V9__GFX_DC_RESTORE_ME2,
+ RAS_GFX_V9__GFX_CPC_INDEX_END =
+ RAS_GFX_V9__GFX_DC_RESTORE_ME2,
+ /* CPF */
+ RAS_GFX_V9__GFX_CPF_INDEX_START,
+ RAS_GFX_V9__GFX_CPF_ROQ_ME2 =
+ RAS_GFX_V9__GFX_CPF_INDEX_START,
+ RAS_GFX_V9__GFX_CPF_ROQ_ME1,
+ RAS_GFX_V9__GFX_CPF_TAG,
+ RAS_GFX_V9__GFX_CPF_INDEX_END = RAS_GFX_V9__GFX_CPF_TAG,
+ /* CPG */
+ RAS_GFX_V9__GFX_CPG_INDEX_START,
+ RAS_GFX_V9__GFX_CPG_DMA_ROQ =
+ RAS_GFX_V9__GFX_CPG_INDEX_START,
+ RAS_GFX_V9__GFX_CPG_DMA_TAG,
+ RAS_GFX_V9__GFX_CPG_TAG,
+ RAS_GFX_V9__GFX_CPG_INDEX_END = RAS_GFX_V9__GFX_CPG_TAG,
+ /* GDS */
+ RAS_GFX_V9__GFX_GDS_INDEX_START,
+ RAS_GFX_V9__GFX_GDS_MEM = RAS_GFX_V9__GFX_GDS_INDEX_START,
+ RAS_GFX_V9__GFX_GDS_INPUT_QUEUE,
+ RAS_GFX_V9__GFX_GDS_OA_PHY_CMD_RAM_MEM,
+ RAS_GFX_V9__GFX_GDS_OA_PHY_DATA_RAM_MEM,
+ RAS_GFX_V9__GFX_GDS_OA_PIPE_MEM,
+ RAS_GFX_V9__GFX_GDS_INDEX_END =
+ RAS_GFX_V9__GFX_GDS_OA_PIPE_MEM,
+ /* SPI */
+ RAS_GFX_V9__GFX_SPI_SR_MEM,
+ /* SQ */
+ RAS_GFX_V9__GFX_SQ_INDEX_START,
+ RAS_GFX_V9__GFX_SQ_SGPR = RAS_GFX_V9__GFX_SQ_INDEX_START,
+ RAS_GFX_V9__GFX_SQ_LDS_D,
+ RAS_GFX_V9__GFX_SQ_LDS_I,
+ RAS_GFX_V9__GFX_SQ_VGPR,
+ RAS_GFX_V9__GFX_SQ_INDEX_END = RAS_GFX_V9__GFX_SQ_VGPR,
+ /* SQC (3 ranges) */
+ RAS_GFX_V9__GFX_SQC_INDEX_START,
+ /* SQC range 0 */
+ RAS_GFX_V9__GFX_SQC_INDEX0_START =
+ RAS_GFX_V9__GFX_SQC_INDEX_START,
+ RAS_GFX_V9__GFX_SQC_INST_UTCL1_LFIFO =
+ RAS_GFX_V9__GFX_SQC_INDEX0_START,
+ RAS_GFX_V9__GFX_SQC_DATA_CU0_WRITE_DATA_BUF,
+ RAS_GFX_V9__GFX_SQC_DATA_CU0_UTCL1_LFIFO,
+ RAS_GFX_V9__GFX_SQC_DATA_CU1_WRITE_DATA_BUF,
+ RAS_GFX_V9__GFX_SQC_DATA_CU1_UTCL1_LFIFO,
+ RAS_GFX_V9__GFX_SQC_DATA_CU2_WRITE_DATA_BUF,
+ RAS_GFX_V9__GFX_SQC_DATA_CU2_UTCL1_LFIFO,
+ RAS_GFX_V9__GFX_SQC_INDEX0_END =
+ RAS_GFX_V9__GFX_SQC_DATA_CU2_UTCL1_LFIFO,
+ /* SQC range 1 */
+ RAS_GFX_V9__GFX_SQC_INDEX1_START,
+ RAS_GFX_V9__GFX_SQC_INST_BANKA_TAG_RAM =
+ RAS_GFX_V9__GFX_SQC_INDEX1_START,
+ RAS_GFX_V9__GFX_SQC_INST_BANKA_UTCL1_MISS_FIFO,
+ RAS_GFX_V9__GFX_SQC_INST_BANKA_MISS_FIFO,
+ RAS_GFX_V9__GFX_SQC_INST_BANKA_BANK_RAM,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKA_TAG_RAM,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKA_HIT_FIFO,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKA_MISS_FIFO,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKA_DIRTY_BIT_RAM,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKA_BANK_RAM,
+ RAS_GFX_V9__GFX_SQC_INDEX1_END =
+ RAS_GFX_V9__GFX_SQC_DATA_BANKA_BANK_RAM,
+ /* SQC range 2 */
+ RAS_GFX_V9__GFX_SQC_INDEX2_START,
+ RAS_GFX_V9__GFX_SQC_INST_BANKB_TAG_RAM =
+ RAS_GFX_V9__GFX_SQC_INDEX2_START,
+ RAS_GFX_V9__GFX_SQC_INST_BANKB_UTCL1_MISS_FIFO,
+ RAS_GFX_V9__GFX_SQC_INST_BANKB_MISS_FIFO,
+ RAS_GFX_V9__GFX_SQC_INST_BANKB_BANK_RAM,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKB_TAG_RAM,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKB_HIT_FIFO,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKB_MISS_FIFO,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKB_DIRTY_BIT_RAM,
+ RAS_GFX_V9__GFX_SQC_DATA_BANKB_BANK_RAM,
+ RAS_GFX_V9__GFX_SQC_INDEX2_END =
+ RAS_GFX_V9__GFX_SQC_DATA_BANKB_BANK_RAM,
+ RAS_GFX_V9__GFX_SQC_INDEX_END =
+ RAS_GFX_V9__GFX_SQC_INDEX2_END,
+ /* TA */
+ RAS_GFX_V9__GFX_TA_INDEX_START,
+ RAS_GFX_V9__GFX_TA_FS_DFIFO =
+ RAS_GFX_V9__GFX_TA_INDEX_START,
+ RAS_GFX_V9__GFX_TA_FS_AFIFO,
+ RAS_GFX_V9__GFX_TA_FL_LFIFO,
+ RAS_GFX_V9__GFX_TA_FX_LFIFO,
+ RAS_GFX_V9__GFX_TA_FS_CFIFO,
+ RAS_GFX_V9__GFX_TA_INDEX_END = RAS_GFX_V9__GFX_TA_FS_CFIFO,
+ /* TCA */
+ RAS_GFX_V9__GFX_TCA_INDEX_START,
+ RAS_GFX_V9__GFX_TCA_HOLE_FIFO =
+ RAS_GFX_V9__GFX_TCA_INDEX_START,
+ RAS_GFX_V9__GFX_TCA_REQ_FIFO,
+ RAS_GFX_V9__GFX_TCA_INDEX_END =
+ RAS_GFX_V9__GFX_TCA_REQ_FIFO,
+ /* TCC (5 sub-ranges) */
+ RAS_GFX_V9__GFX_TCC_INDEX_START,
+ /* TCC range 0 */
+ RAS_GFX_V9__GFX_TCC_INDEX0_START =
+ RAS_GFX_V9__GFX_TCC_INDEX_START,
+ RAS_GFX_V9__GFX_TCC_CACHE_DATA =
+ RAS_GFX_V9__GFX_TCC_INDEX0_START,
+ RAS_GFX_V9__GFX_TCC_CACHE_DATA_BANK_0_1,
+ RAS_GFX_V9__GFX_TCC_CACHE_DATA_BANK_1_0,
+ RAS_GFX_V9__GFX_TCC_CACHE_DATA_BANK_1_1,
+ RAS_GFX_V9__GFX_TCC_CACHE_DIRTY_BANK_0,
+ RAS_GFX_V9__GFX_TCC_CACHE_DIRTY_BANK_1,
+ RAS_GFX_V9__GFX_TCC_HIGH_RATE_TAG,
+ RAS_GFX_V9__GFX_TCC_LOW_RATE_TAG,
+ RAS_GFX_V9__GFX_TCC_INDEX0_END =
+ RAS_GFX_V9__GFX_TCC_LOW_RATE_TAG,
+ /* TCC range 1 */
+ RAS_GFX_V9__GFX_TCC_INDEX1_START,
+ RAS_GFX_V9__GFX_TCC_IN_USE_DEC =
+ RAS_GFX_V9__GFX_TCC_INDEX1_START,
+ RAS_GFX_V9__GFX_TCC_IN_USE_TRANSFER,
+ RAS_GFX_V9__GFX_TCC_INDEX1_END =
+ RAS_GFX_V9__GFX_TCC_IN_USE_TRANSFER,
+ /* TCC range 2 */
+ RAS_GFX_V9__GFX_TCC_INDEX2_START,
+ RAS_GFX_V9__GFX_TCC_RETURN_DATA =
+ RAS_GFX_V9__GFX_TCC_INDEX2_START,
+ RAS_GFX_V9__GFX_TCC_RETURN_CONTROL,
+ RAS_GFX_V9__GFX_TCC_UC_ATOMIC_FIFO,
+ RAS_GFX_V9__GFX_TCC_WRITE_RETURN,
+ RAS_GFX_V9__GFX_TCC_WRITE_CACHE_READ,
+ RAS_GFX_V9__GFX_TCC_SRC_FIFO,
+ RAS_GFX_V9__GFX_TCC_SRC_FIFO_NEXT_RAM,
+ RAS_GFX_V9__GFX_TCC_CACHE_TAG_PROBE_FIFO,
+ RAS_GFX_V9__GFX_TCC_INDEX2_END =
+ RAS_GFX_V9__GFX_TCC_CACHE_TAG_PROBE_FIFO,
+ /* TCC range 3 */
+ RAS_GFX_V9__GFX_TCC_INDEX3_START,
+ RAS_GFX_V9__GFX_TCC_LATENCY_FIFO =
+ RAS_GFX_V9__GFX_TCC_INDEX3_START,
+ RAS_GFX_V9__GFX_TCC_LATENCY_FIFO_NEXT_RAM,
+ RAS_GFX_V9__GFX_TCC_INDEX3_END =
+ RAS_GFX_V9__GFX_TCC_LATENCY_FIFO_NEXT_RAM,
+ /* TCC range 4 */
+ RAS_GFX_V9__GFX_TCC_INDEX4_START,
+ RAS_GFX_V9__GFX_TCC_WRRET_TAG_WRITE_RETURN =
+ RAS_GFX_V9__GFX_TCC_INDEX4_START,
+ RAS_GFX_V9__GFX_TCC_ATOMIC_RETURN_BUFFER,
+ RAS_GFX_V9__GFX_TCC_INDEX4_END =
+ RAS_GFX_V9__GFX_TCC_ATOMIC_RETURN_BUFFER,
+ RAS_GFX_V9__GFX_TCC_INDEX_END =
+ RAS_GFX_V9__GFX_TCC_INDEX4_END,
+ /* TCI */
+ RAS_GFX_V9__GFX_TCI_WRITE_RAM,
+ /* TCP */
+ RAS_GFX_V9__GFX_TCP_INDEX_START,
+ RAS_GFX_V9__GFX_TCP_CACHE_RAM =
+ RAS_GFX_V9__GFX_TCP_INDEX_START,
+ RAS_GFX_V9__GFX_TCP_LFIFO_RAM,
+ RAS_GFX_V9__GFX_TCP_CMD_FIFO,
+ RAS_GFX_V9__GFX_TCP_VM_FIFO,
+ RAS_GFX_V9__GFX_TCP_DB_RAM,
+ RAS_GFX_V9__GFX_TCP_UTCL1_LFIFO0,
+ RAS_GFX_V9__GFX_TCP_UTCL1_LFIFO1,
+ RAS_GFX_V9__GFX_TCP_INDEX_END =
+ RAS_GFX_V9__GFX_TCP_UTCL1_LFIFO1,
+ /* TD */
+ RAS_GFX_V9__GFX_TD_INDEX_START,
+ RAS_GFX_V9__GFX_TD_SS_FIFO_LO =
+ RAS_GFX_V9__GFX_TD_INDEX_START,
+ RAS_GFX_V9__GFX_TD_SS_FIFO_HI,
+ RAS_GFX_V9__GFX_TD_CS_FIFO,
+ RAS_GFX_V9__GFX_TD_INDEX_END = RAS_GFX_V9__GFX_TD_CS_FIFO,
+ /* EA (3 sub-ranges) */
+ RAS_GFX_V9__GFX_EA_INDEX_START,
+ /* EA range 0 */
+ RAS_GFX_V9__GFX_EA_INDEX0_START =
+ RAS_GFX_V9__GFX_EA_INDEX_START,
+ RAS_GFX_V9__GFX_EA_DRAMRD_CMDMEM =
+ RAS_GFX_V9__GFX_EA_INDEX0_START,
+ RAS_GFX_V9__GFX_EA_DRAMWR_CMDMEM,
+ RAS_GFX_V9__GFX_EA_DRAMWR_DATAMEM,
+ RAS_GFX_V9__GFX_EA_RRET_TAGMEM,
+ RAS_GFX_V9__GFX_EA_WRET_TAGMEM,
+ RAS_GFX_V9__GFX_EA_GMIRD_CMDMEM,
+ RAS_GFX_V9__GFX_EA_GMIWR_CMDMEM,
+ RAS_GFX_V9__GFX_EA_GMIWR_DATAMEM,
+ RAS_GFX_V9__GFX_EA_INDEX0_END =
+ RAS_GFX_V9__GFX_EA_GMIWR_DATAMEM,
+ /* EA range 1 */
+ RAS_GFX_V9__GFX_EA_INDEX1_START,
+ RAS_GFX_V9__GFX_EA_DRAMRD_PAGEMEM =
+ RAS_GFX_V9__GFX_EA_INDEX1_START,
+ RAS_GFX_V9__GFX_EA_DRAMWR_PAGEMEM,
+ RAS_GFX_V9__GFX_EA_IORD_CMDMEM,
+ RAS_GFX_V9__GFX_EA_IOWR_CMDMEM,
+ RAS_GFX_V9__GFX_EA_IOWR_DATAMEM,
+ RAS_GFX_V9__GFX_EA_GMIRD_PAGEMEM,
+ RAS_GFX_V9__GFX_EA_GMIWR_PAGEMEM,
+ RAS_GFX_V9__GFX_EA_INDEX1_END =
+ RAS_GFX_V9__GFX_EA_GMIWR_PAGEMEM,
+ /* EA range 2 */
+ RAS_GFX_V9__GFX_EA_INDEX2_START,
+ RAS_GFX_V9__GFX_EA_MAM_D0MEM =
+ RAS_GFX_V9__GFX_EA_INDEX2_START,
+ RAS_GFX_V9__GFX_EA_MAM_D1MEM,
+ RAS_GFX_V9__GFX_EA_MAM_D2MEM,
+ RAS_GFX_V9__GFX_EA_MAM_D3MEM,
+ RAS_GFX_V9__GFX_EA_INDEX2_END =
+ RAS_GFX_V9__GFX_EA_MAM_D3MEM,
+ RAS_GFX_V9__GFX_EA_INDEX_END =
+ RAS_GFX_V9__GFX_EA_INDEX2_END,
+ /* UTC VM L2 bank */
+ RAS_GFX_V9__UTC_VML2_BANK_CACHE,
+ /* UTC VM walker */
+ RAS_GFX_V9__UTC_VML2_WALKER,
+ /* UTC ATC L2 2MB cache */
+ RAS_GFX_V9__UTC_ATCL2_CACHE_2M_BANK,
+ /* UTC ATC L2 4KB cache */
+ RAS_GFX_V9__UTC_ATCL2_CACHE_4K_BANK,
+ RAS_GFX_V9__GFX_MAX
+};
+
+extern const struct ras_gfx_ip_func gfx_ras_func_v9_0;
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_log_ring.c b/drivers/gpu/drm/amd/ras/rascore/ras_log_ring.c
new file mode 100644
index 000000000000..0a838fdcb2f6
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_log_ring.c
@@ -0,0 +1,317 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_core_status.h"
+#include "ras_log_ring.h"
+
+#define RAS_LOG_MAX_QUERY_SIZE 0xC000
+#define RAS_LOG_MEM_TEMP_SIZE 0x200
+#define RAS_LOG_MEMPOOL_SIZE \
+ (RAS_LOG_MAX_QUERY_SIZE + RAS_LOG_MEM_TEMP_SIZE)
+
+#define BATCH_IDX_TO_TREE_IDX(batch_idx, sn) (((batch_idx) << 8) | (sn))
+
+static const uint64_t ras_rma_aca_reg[ACA_REG_MAX_COUNT] = {
+ [ACA_REG_IDX__CTL] = 0x1,
+ [ACA_REG_IDX__STATUS] = 0xB000000000000137,
+ [ACA_REG_IDX__ADDR] = 0x0,
+ [ACA_REG_IDX__MISC0] = 0x0,
+ [ACA_REG_IDX__CONFG] = 0x1ff00000002,
+ [ACA_REG_IDX__IPID] = 0x9600000000,
+ [ACA_REG_IDX__SYND] = 0x0,
+};
+
+static uint64_t ras_log_ring_get_logged_ecc_count(struct ras_core_context *ras_core)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ uint64_t count = 0;
+
+ if (log_ring->logged_ecc_count < 0) {
+ RAS_DEV_WARN(ras_core->dev,
+ "Error: the logged ras count should not less than 0!\n");
+ count = 0;
+ } else {
+ count = log_ring->logged_ecc_count;
+ }
+
+ if (count > RAS_LOG_MEMPOOL_SIZE)
+ RAS_DEV_WARN(ras_core->dev,
+ "Error: the logged ras count is out of range!\n");
+
+ return count;
+}
+
+static int ras_log_ring_add_data(struct ras_core_context *ras_core,
+ struct ras_log_info *log, struct ras_log_batch_tag *batch_tag)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ unsigned long flags = 0;
+ int ret = 0;
+
+ if (batch_tag && (batch_tag->sub_seqno >= MAX_RECORD_PER_BATCH)) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Invalid batch sub seqno:%d, batch:0x%llx\n",
+ batch_tag->sub_seqno, batch_tag->batch_id);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&log_ring->spin_lock, flags);
+ if (batch_tag) {
+ log->seqno =
+ BATCH_IDX_TO_TREE_IDX(batch_tag->batch_id, batch_tag->sub_seqno);
+ batch_tag->sub_seqno++;
+ } else {
+ log->seqno = BATCH_IDX_TO_TREE_IDX(log_ring->mono_upward_batch_id, 0);
+ log_ring->mono_upward_batch_id++;
+ }
+ ret = radix_tree_insert(&log_ring->ras_log_root, log->seqno, log);
+ if (!ret)
+ log_ring->logged_ecc_count++;
+ spin_unlock_irqrestore(&log_ring->spin_lock, flags);
+
+ if (ret) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to add ras log! seqno:0x%llx, ret:%d\n",
+ log->seqno, ret);
+ mempool_free(log, log_ring->ras_log_mempool);
+ }
+
+ return ret;
+}
+
+static int ras_log_ring_delete_data(struct ras_core_context *ras_core, uint32_t count)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ unsigned long flags = 0;
+ uint32_t i = 0, j = 0;
+ uint64_t batch_id, idx;
+ void *data;
+ int ret = -ENODATA;
+
+ if (count > ras_log_ring_get_logged_ecc_count(ras_core))
+ return -EINVAL;
+
+ spin_lock_irqsave(&log_ring->spin_lock, flags);
+ batch_id = log_ring->last_del_batch_id;
+ while (batch_id < log_ring->mono_upward_batch_id) {
+ for (j = 0; j < MAX_RECORD_PER_BATCH; j++) {
+ idx = BATCH_IDX_TO_TREE_IDX(batch_id, j);
+ data = radix_tree_delete(&log_ring->ras_log_root, idx);
+ if (data) {
+ mempool_free(data, log_ring->ras_log_mempool);
+ log_ring->logged_ecc_count--;
+ i++;
+ }
+ }
+ batch_id = ++log_ring->last_del_batch_id;
+ if (i >= count) {
+ ret = 0;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&log_ring->spin_lock, flags);
+
+ return ret;
+}
+
+static void ras_log_ring_clear_log_tree(struct ras_core_context *ras_core)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ uint64_t batch_id, idx;
+ unsigned long flags = 0;
+ void *data;
+ int j;
+
+ if ((log_ring->mono_upward_batch_id <= log_ring->last_del_batch_id) &&
+ !log_ring->logged_ecc_count)
+ return;
+
+ spin_lock_irqsave(&log_ring->spin_lock, flags);
+ batch_id = log_ring->last_del_batch_id;
+ while (batch_id < log_ring->mono_upward_batch_id) {
+ for (j = 0; j < MAX_RECORD_PER_BATCH; j++) {
+ idx = BATCH_IDX_TO_TREE_IDX(batch_id, j);
+ data = radix_tree_delete(&log_ring->ras_log_root, idx);
+ if (data) {
+ mempool_free(data, log_ring->ras_log_mempool);
+ log_ring->logged_ecc_count--;
+ }
+ }
+ batch_id++;
+ }
+ spin_unlock_irqrestore(&log_ring->spin_lock, flags);
+
+}
+
+int ras_log_ring_sw_init(struct ras_core_context *ras_core)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+
+ memset(log_ring, 0, sizeof(*log_ring));
+
+ log_ring->ras_log_mempool = mempool_create_kmalloc_pool(
+ RAS_LOG_MEMPOOL_SIZE, sizeof(struct ras_log_info));
+ if (!log_ring->ras_log_mempool)
+ return -ENOMEM;
+
+ INIT_RADIX_TREE(&log_ring->ras_log_root, GFP_KERNEL);
+
+ spin_lock_init(&log_ring->spin_lock);
+
+ return 0;
+}
+
+int ras_log_ring_sw_fini(struct ras_core_context *ras_core)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+
+ ras_log_ring_clear_log_tree(ras_core);
+ log_ring->logged_ecc_count = 0;
+ log_ring->last_del_batch_id = 0;
+ log_ring->mono_upward_batch_id = 0;
+
+ mempool_destroy(log_ring->ras_log_mempool);
+
+ return 0;
+}
+
+struct ras_log_batch_tag *ras_log_ring_create_batch_tag(struct ras_core_context *ras_core)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ struct ras_log_batch_tag *batch_tag;
+ unsigned long flags = 0;
+
+ batch_tag = kzalloc(sizeof(*batch_tag), GFP_KERNEL);
+ if (!batch_tag)
+ return NULL;
+
+ spin_lock_irqsave(&log_ring->spin_lock, flags);
+ batch_tag->batch_id = log_ring->mono_upward_batch_id;
+ log_ring->mono_upward_batch_id++;
+ spin_unlock_irqrestore(&log_ring->spin_lock, flags);
+
+ batch_tag->sub_seqno = 0;
+ batch_tag->timestamp = ras_core_get_utc_second_timestamp(ras_core);
+ return batch_tag;
+}
+
+void ras_log_ring_destroy_batch_tag(struct ras_core_context *ras_core,
+ struct ras_log_batch_tag *batch_tag)
+{
+ kfree(batch_tag);
+}
+
+void ras_log_ring_add_log_event(struct ras_core_context *ras_core,
+ enum ras_log_event event, void *data, struct ras_log_batch_tag *batch_tag)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ struct device_system_info dev_info = {0};
+ struct ras_log_info *log;
+ uint64_t socket_id;
+ void *obj;
+
+ obj = mempool_alloc_preallocated(log_ring->ras_log_mempool);
+ if (!obj ||
+ (ras_log_ring_get_logged_ecc_count(ras_core) >= RAS_LOG_MEMPOOL_SIZE)) {
+ ras_log_ring_delete_data(ras_core, RAS_LOG_MEM_TEMP_SIZE);
+ if (!obj)
+ obj = mempool_alloc_preallocated(log_ring->ras_log_mempool);
+ }
+
+ if (!obj) {
+ RAS_DEV_ERR(ras_core->dev, "ERROR: Failed to alloc ras log buffer!\n");
+ return;
+ }
+
+ log = (struct ras_log_info *)obj;
+
+ memset(log, 0, sizeof(*log));
+ log->timestamp =
+ batch_tag ? batch_tag->timestamp : ras_core_get_utc_second_timestamp(ras_core);
+ log->event = event;
+
+ if (data)
+ memcpy(&log->aca_reg, data, sizeof(log->aca_reg));
+
+ if (event == RAS_LOG_EVENT_RMA) {
+ memcpy(&log->aca_reg, ras_rma_aca_reg, sizeof(log->aca_reg));
+ ras_core_get_device_system_info(ras_core, &dev_info);
+ socket_id = dev_info.socket_id;
+ log->aca_reg.regs[ACA_REG_IDX__IPID] |= ((socket_id / 4) & 0x01);
+ log->aca_reg.regs[ACA_REG_IDX__IPID] |= (((socket_id % 4) & 0x3) << 44);
+ }
+
+ ras_log_ring_add_data(ras_core, log, batch_tag);
+}
+
+static struct ras_log_info *ras_log_ring_lookup_data(struct ras_core_context *ras_core,
+ uint64_t idx)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ unsigned long flags = 0;
+ void *data;
+
+ spin_lock_irqsave(&log_ring->spin_lock, flags);
+ data = radix_tree_lookup(&log_ring->ras_log_root, idx);
+ spin_unlock_irqrestore(&log_ring->spin_lock, flags);
+
+ return (struct ras_log_info *)data;
+}
+
+int ras_log_ring_get_batch_records(struct ras_core_context *ras_core, uint64_t batch_id,
+ struct ras_log_info **log_arr, uint32_t arr_num)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+ uint32_t i, idx, count = 0;
+ void *data;
+
+ if ((batch_id >= log_ring->mono_upward_batch_id) ||
+ (batch_id < log_ring->last_del_batch_id))
+ return -EINVAL;
+
+ for (i = 0; i < MAX_RECORD_PER_BATCH; i++) {
+ idx = BATCH_IDX_TO_TREE_IDX(batch_id, i);
+ data = ras_log_ring_lookup_data(ras_core, idx);
+ if (data) {
+ log_arr[count++] = data;
+ if (count >= arr_num)
+ break;
+ }
+ }
+
+ return count;
+}
+
+int ras_log_ring_get_batch_overview(struct ras_core_context *ras_core,
+ struct ras_log_batch_overview *overview)
+{
+ struct ras_log_ring *log_ring = &ras_core->ras_log_ring;
+
+ overview->logged_batch_count =
+ log_ring->mono_upward_batch_id - log_ring->last_del_batch_id;
+ overview->last_batch_id = log_ring->mono_upward_batch_id;
+ overview->first_batch_id = log_ring->last_del_batch_id;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_log_ring.h b/drivers/gpu/drm/amd/ras/rascore/ras_log_ring.h
new file mode 100644
index 000000000000..0ff6cc35678d
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_log_ring.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_LOG_RING_H__
+#define __RAS_LOG_RING_H__
+#include "ras_aca.h"
+
+#define MAX_RECORD_PER_BATCH 32
+
+#define RAS_LOG_SEQNO_TO_BATCH_IDX(seqno) ((seqno) >> 8)
+
+enum ras_log_event {
+ RAS_LOG_EVENT_NONE,
+ RAS_LOG_EVENT_UE,
+ RAS_LOG_EVENT_DE,
+ RAS_LOG_EVENT_CE,
+ RAS_LOG_EVENT_POISON_CREATION,
+ RAS_LOG_EVENT_POISON_CONSUMPTION,
+ RAS_LOG_EVENT_RMA,
+ RAS_LOG_EVENT_COUNT_MAX,
+};
+
+struct ras_aca_reg {
+ uint64_t regs[ACA_REG_MAX_COUNT];
+};
+
+struct ras_log_info {
+ uint64_t seqno;
+ uint64_t timestamp;
+ enum ras_log_event event;
+ union {
+ struct ras_aca_reg aca_reg;
+ };
+};
+
+struct ras_log_batch_tag {
+ uint64_t batch_id;
+ uint64_t timestamp;
+ uint32_t sub_seqno;
+};
+
+struct ras_log_ring {
+ void *ras_log_mempool;
+ struct radix_tree_root ras_log_root;
+ spinlock_t spin_lock;
+ uint64_t mono_upward_batch_id;
+ uint64_t last_del_batch_id;
+ int logged_ecc_count;
+};
+
+struct ras_log_batch_overview {
+ uint64_t first_batch_id;
+ uint64_t last_batch_id;
+ uint32_t logged_batch_count;
+};
+
+struct ras_core_context;
+
+int ras_log_ring_sw_init(struct ras_core_context *ras_core);
+int ras_log_ring_sw_fini(struct ras_core_context *ras_core);
+
+struct ras_log_batch_tag *ras_log_ring_create_batch_tag(struct ras_core_context *ras_core);
+void ras_log_ring_destroy_batch_tag(struct ras_core_context *ras_core,
+ struct ras_log_batch_tag *tag);
+void ras_log_ring_add_log_event(struct ras_core_context *ras_core,
+ enum ras_log_event event, void *data, struct ras_log_batch_tag *tag);
+
+int ras_log_ring_get_batch_records(struct ras_core_context *ras_core, uint64_t batch_idx,
+ struct ras_log_info **log_arr, uint32_t arr_num);
+
+int ras_log_ring_get_batch_overview(struct ras_core_context *ras_core,
+ struct ras_log_batch_overview *overview);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c
new file mode 100644
index 000000000000..f3321df85021
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_mp1.h"
+#include "ras_mp1_v13_0.h"
+
+static const struct ras_mp1_ip_func *ras_mp1_get_ip_funcs(
+ struct ras_core_context *ras_core, uint32_t ip_version)
+{
+ switch (ip_version) {
+ case IP_VERSION(13, 0, 6):
+ case IP_VERSION(13, 0, 14):
+ case IP_VERSION(13, 0, 12):
+ return &mp1_ras_func_v13_0;
+ default:
+ RAS_DEV_ERR(ras_core->dev,
+ "MP1 ip version(0x%x) is not supported!\n", ip_version);
+ break;
+ }
+
+ return NULL;
+}
+
+int ras_mp1_get_bank_count(struct ras_core_context *ras_core,
+ enum ras_err_type type, u32 *count)
+{
+ struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+
+ return mp1->ip_func->get_valid_bank_count(ras_core, type, count);
+}
+
+int ras_mp1_dump_bank(struct ras_core_context *ras_core,
+ u32 type, u32 idx, u32 reg_idx, u64 *val)
+{
+ struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+
+ return mp1->ip_func->dump_valid_bank(ras_core, type, idx, reg_idx, val);
+}
+
+int ras_mp1_hw_init(struct ras_core_context *ras_core)
+{
+ struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+
+ mp1->mp1_ip_version = ras_core->config->mp1_ip_version;
+ mp1->sys_func = ras_core->config->mp1_cfg.mp1_sys_fn;
+ if (!mp1->sys_func) {
+ RAS_DEV_ERR(ras_core->dev, "RAS mp1 sys function not configured!\n");
+ return -EINVAL;
+ }
+
+ mp1->ip_func = ras_mp1_get_ip_funcs(ras_core, mp1->mp1_ip_version);
+
+ return mp1->ip_func ? RAS_CORE_OK : -EINVAL;
+}
+
+int ras_mp1_hw_fini(struct ras_core_context *ras_core)
+{
+ return 0;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h
new file mode 100644
index 000000000000..de1d08286f41
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_MP1_H__
+#define __RAS_MP1_H__
+#include "ras.h"
+
+enum ras_err_type;
+struct ras_mp1_ip_func {
+ int (*get_valid_bank_count)(struct ras_core_context *ras_core,
+ enum ras_err_type type, u32 *count);
+ int (*dump_valid_bank)(struct ras_core_context *ras_core,
+ enum ras_err_type type, u32 idx, u32 reg_idx, u64 *val);
+};
+
+struct ras_mp1 {
+ uint32_t mp1_ip_version;
+ const struct ras_mp1_ip_func *ip_func;
+ const struct ras_mp1_sys_func *sys_func;
+};
+
+int ras_mp1_hw_init(struct ras_core_context *ras_core);
+int ras_mp1_hw_fini(struct ras_core_context *ras_core);
+
+int ras_mp1_get_bank_count(struct ras_core_context *ras_core,
+ enum ras_err_type type, u32 *count);
+
+int ras_mp1_dump_bank(struct ras_core_context *ras_core,
+ u32 ecc_type, u32 idx, u32 reg_idx, u64 *val);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c
new file mode 100644
index 000000000000..310d39fc816b
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_mp1.h"
+#include "ras_core_status.h"
+#include "ras_mp1_v13_0.h"
+
+#define RAS_MP1_MSG_QueryValidMcaCount 0x36
+#define RAS_MP1_MSG_McaBankDumpDW 0x37
+#define RAS_MP1_MSG_ClearMcaOnRead 0x39
+#define RAS_MP1_MSG_QueryValidMcaCeCount 0x3A
+#define RAS_MP1_MSG_McaBankCeDumpDW 0x3B
+
+#define MAX_UE_BANKS_PER_QUERY 12
+#define MAX_CE_BANKS_PER_QUERY 12
+
+static int mp1_v13_0_get_bank_count(struct ras_core_context *ras_core,
+ enum ras_err_type type, u32 *count)
+{
+ struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+ const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
+ uint32_t bank_count = 0;
+ u32 msg;
+ int ret;
+
+ if (!count)
+ return -EINVAL;
+
+ if (!sys_func || !sys_func->mp1_get_valid_bank_count)
+ return -RAS_CORE_NOT_SUPPORTED;
+
+ switch (type) {
+ case RAS_ERR_TYPE__UE:
+ msg = RAS_MP1_MSG_QueryValidMcaCount;
+ break;
+ case RAS_ERR_TYPE__CE:
+ case RAS_ERR_TYPE__DE:
+ msg = RAS_MP1_MSG_QueryValidMcaCeCount;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = sys_func->mp1_get_valid_bank_count(ras_core, msg, &bank_count);
+ if (!ret) {
+ if (((type == RAS_ERR_TYPE__UE) && (bank_count >= MAX_UE_BANKS_PER_QUERY)) ||
+ ((type == RAS_ERR_TYPE__CE) && (bank_count >= MAX_CE_BANKS_PER_QUERY)))
+ return -EINVAL;
+
+ *count = bank_count;
+ }
+
+ return ret;
+}
+
+static int mp1_v13_0_dump_bank(struct ras_core_context *ras_core,
+ enum ras_err_type type, u32 idx, u32 reg_idx, u64 *val)
+{
+ struct ras_mp1 *mp1 = &ras_core->ras_mp1;
+ const struct ras_mp1_sys_func *sys_func = mp1->sys_func;
+ u32 msg;
+
+ if (!sys_func || !sys_func->mp1_dump_valid_bank)
+ return -RAS_CORE_NOT_SUPPORTED;
+
+ switch (type) {
+ case RAS_ERR_TYPE__UE:
+ msg = RAS_MP1_MSG_McaBankDumpDW;
+ break;
+ case RAS_ERR_TYPE__CE:
+ case RAS_ERR_TYPE__DE:
+ msg = RAS_MP1_MSG_McaBankCeDumpDW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return sys_func->mp1_dump_valid_bank(ras_core, msg, idx, reg_idx, val);
+}
+
+const struct ras_mp1_ip_func mp1_ras_func_v13_0 = {
+ .get_valid_bank_count = mp1_v13_0_get_bank_count,
+ .dump_valid_bank = mp1_v13_0_dump_bank,
+};
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.h b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.h
new file mode 100644
index 000000000000..2edfdb5f6a75
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_MP1_V13_0_H__
+#define __RAS_MP1_V13_0_H__
+#include "ras_mp1.h"
+
+extern const struct ras_mp1_ip_func mp1_ras_func_v13_0;
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_nbio.c b/drivers/gpu/drm/amd/ras/rascore/ras_nbio.c
new file mode 100644
index 000000000000..bfddd104d548
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_nbio.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_nbio.h"
+#include "ras_nbio_v7_9.h"
+
+static const struct ras_nbio_ip_func *ras_nbio_get_ip_funcs(
+ struct ras_core_context *ras_core, uint32_t ip_version)
+{
+ switch (ip_version) {
+ case IP_VERSION(7, 9, 0):
+ case IP_VERSION(7, 9, 1):
+ return &ras_nbio_v7_9;
+ default:
+ RAS_DEV_ERR(ras_core->dev,
+ "NBIO ip version(0x%x) is not supported!\n", ip_version);
+ break;
+ }
+
+ return NULL;
+}
+
+int ras_nbio_hw_init(struct ras_core_context *ras_core)
+{
+ struct ras_nbio *nbio = &ras_core->ras_nbio;
+
+ nbio->nbio_ip_version = ras_core->config->nbio_ip_version;
+ nbio->sys_func = ras_core->config->nbio_cfg.nbio_sys_fn;
+ if (!nbio->sys_func) {
+ RAS_DEV_ERR(ras_core->dev, "RAS nbio sys function not configured!\n");
+ return -EINVAL;
+ }
+
+ nbio->ip_func = ras_nbio_get_ip_funcs(ras_core, nbio->nbio_ip_version);
+ if (!nbio->ip_func)
+ return -EINVAL;
+
+ if (nbio->sys_func) {
+ if (nbio->sys_func->set_ras_controller_irq_state)
+ nbio->sys_func->set_ras_controller_irq_state(ras_core, true);
+ if (nbio->sys_func->set_ras_err_event_athub_irq_state)
+ nbio->sys_func->set_ras_err_event_athub_irq_state(ras_core, true);
+ }
+
+ return 0;
+}
+
+int ras_nbio_hw_fini(struct ras_core_context *ras_core)
+{
+ struct ras_nbio *nbio = &ras_core->ras_nbio;
+
+ if (nbio->sys_func) {
+ if (nbio->sys_func->set_ras_controller_irq_state)
+ nbio->sys_func->set_ras_controller_irq_state(ras_core, false);
+ if (nbio->sys_func->set_ras_err_event_athub_irq_state)
+ nbio->sys_func->set_ras_err_event_athub_irq_state(ras_core, false);
+ }
+
+ return 0;
+}
+
+bool ras_nbio_handle_irq_error(struct ras_core_context *ras_core, void *data)
+{
+ struct ras_nbio *nbio = &ras_core->ras_nbio;
+
+ if (nbio->ip_func) {
+ if (nbio->ip_func->handle_ras_controller_intr_no_bifring)
+ nbio->ip_func->handle_ras_controller_intr_no_bifring(ras_core);
+ if (nbio->ip_func->handle_ras_err_event_athub_intr_no_bifring)
+ nbio->ip_func->handle_ras_err_event_athub_intr_no_bifring(ras_core);
+ }
+
+ return true;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_nbio.h b/drivers/gpu/drm/amd/ras/rascore/ras_nbio.h
new file mode 100644
index 000000000000..0a1313e59a02
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_nbio.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_NBIO_H__
+#define __RAS_NBIO_H__
+#include "ras.h"
+
+struct ras_core_context;
+
+struct ras_nbio_ip_func {
+ int (*handle_ras_controller_intr_no_bifring)(struct ras_core_context *ras_core);
+ int (*handle_ras_err_event_athub_intr_no_bifring)(struct ras_core_context *ras_core);
+ uint32_t (*get_memory_partition_mode)(struct ras_core_context *ras_core);
+};
+
+struct ras_nbio {
+ uint32_t nbio_ip_version;
+ const struct ras_nbio_ip_func *ip_func;
+ const struct ras_nbio_sys_func *sys_func;
+};
+
+int ras_nbio_hw_init(struct ras_core_context *ras_core);
+int ras_nbio_hw_fini(struct ras_core_context *ras_core);
+bool ras_nbio_handle_irq_error(struct ras_core_context *ras_core, void *data);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.c b/drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.c
new file mode 100644
index 000000000000..f17d708ec668
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_nbio_v7_9.h"
+
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_ATHUB_ERR_EVENT_INTERRUPT_CLEAR__SHIFT 0x12
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_ATHUB_ERR_EVENT_INTERRUPT_CLEAR_MASK 0x00040000L
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_ATHUB_ERR_EVENT_INTERRUPT_STATUS__SHIFT 0x2
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_ATHUB_ERR_EVENT_INTERRUPT_STATUS_MASK 0x00000004L
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_CNTLR_INTERRUPT_CLEAR__SHIFT 0x11
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_CNTLR_INTERRUPT_CLEAR_MASK 0x00020000L
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_CNTLR_INTERRUPT_STATUS__SHIFT 0x1
+#define BIF_BX0_BIF_DOORBELL_INT_CNTL__RAS_CNTLR_INTERRUPT_STATUS_MASK 0x00000002L
+
+#define regBIF_BX0_BIF_DOORBELL_INT_CNTL_BASE_IDX 2
+#define regBIF_BX0_BIF_DOORBELL_INT_CNTL 0x00fe
+
+#define regBIF_BX0_BIF_INTR_CNTL 0x0101
+#define regBIF_BX0_BIF_INTR_CNTL_BASE_IDX 2
+
+/* BIF_BX0_BIF_INTR_CNTL */
+#define BIF_BX0_BIF_INTR_CNTL__RAS_INTR_VEC_SEL__SHIFT 0x0
+#define BIF_BX0_BIF_INTR_CNTL__RAS_INTR_VEC_SEL_MASK 0x00000001L
+
+#define regBIF_BX_PF0_PARTITION_MEM_STATUS 0x0164
+#define regBIF_BX_PF0_PARTITION_MEM_STATUS_BASE_IDX 2
+/* BIF_BX_PF0_PARTITION_MEM_STATUS */
+#define BIF_BX_PF0_PARTITION_MEM_STATUS__CHANGE_STATUE__SHIFT 0x0
+#define BIF_BX_PF0_PARTITION_MEM_STATUS__NPS_MODE__SHIFT 0x4
+#define BIF_BX_PF0_PARTITION_MEM_STATUS__CHANGE_STATUE_MASK 0x0000000FL
+#define BIF_BX_PF0_PARTITION_MEM_STATUS__NPS_MODE_MASK 0x00000FF0L
+
+
+static int nbio_v7_9_handle_ras_controller_intr_no_bifring(struct ras_core_context *ras_core)
+{
+ uint32_t bif_doorbell_intr_cntl = 0;
+
+ bif_doorbell_intr_cntl =
+ RAS_DEV_RREG32_SOC15(ras_core->dev, NBIO, 0, regBIF_BX0_BIF_DOORBELL_INT_CNTL);
+
+ if (REG_GET_FIELD(bif_doorbell_intr_cntl,
+ BIF_BX0_BIF_DOORBELL_INT_CNTL, RAS_CNTLR_INTERRUPT_STATUS)) {
+ /* driver has to clear the interrupt status when bif ring is disabled */
+ bif_doorbell_intr_cntl = REG_SET_FIELD(bif_doorbell_intr_cntl,
+ BIF_BX0_BIF_DOORBELL_INT_CNTL,
+ RAS_CNTLR_INTERRUPT_CLEAR, 1);
+
+ RAS_DEV_WREG32_SOC15(ras_core->dev,
+ NBIO, 0, regBIF_BX0_BIF_DOORBELL_INT_CNTL, bif_doorbell_intr_cntl);
+
+ /* TODO: handle ras controller interrupt */
+ }
+
+ return 0;
+}
+
+static int nbio_v7_9_handle_ras_err_event_athub_intr_no_bifring(struct ras_core_context *ras_core)
+{
+ uint32_t bif_doorbell_intr_cntl = 0;
+ int ret = 0;
+
+ bif_doorbell_intr_cntl =
+ RAS_DEV_RREG32_SOC15(ras_core->dev, NBIO, 0, regBIF_BX0_BIF_DOORBELL_INT_CNTL);
+
+ if (REG_GET_FIELD(bif_doorbell_intr_cntl,
+ BIF_BX0_BIF_DOORBELL_INT_CNTL, RAS_ATHUB_ERR_EVENT_INTERRUPT_STATUS)) {
+ /* driver has to clear the interrupt status when bif ring is disabled */
+ bif_doorbell_intr_cntl = REG_SET_FIELD(bif_doorbell_intr_cntl,
+ BIF_BX0_BIF_DOORBELL_INT_CNTL,
+ RAS_ATHUB_ERR_EVENT_INTERRUPT_CLEAR, 1);
+
+ RAS_DEV_WREG32_SOC15(ras_core->dev,
+ NBIO, 0, regBIF_BX0_BIF_DOORBELL_INT_CNTL, bif_doorbell_intr_cntl);
+
+ ret = ras_core_handle_fatal_error(ras_core);
+ }
+
+ return ret;
+}
+
+static uint32_t nbio_v7_9_get_memory_partition_mode(struct ras_core_context *ras_core)
+{
+ uint32_t mem_status;
+ uint32_t mem_mode;
+
+ mem_status =
+ RAS_DEV_RREG32_SOC15(ras_core->dev, NBIO, 0, regBIF_BX_PF0_PARTITION_MEM_STATUS);
+
+ /* Each bit represents a mode 1-8*/
+ mem_mode = REG_GET_FIELD(mem_status, BIF_BX_PF0_PARTITION_MEM_STATUS, NPS_MODE);
+
+ return ffs(mem_mode);
+}
+
+const struct ras_nbio_ip_func ras_nbio_v7_9 = {
+ .handle_ras_controller_intr_no_bifring =
+ nbio_v7_9_handle_ras_controller_intr_no_bifring,
+ .handle_ras_err_event_athub_intr_no_bifring =
+ nbio_v7_9_handle_ras_err_event_athub_intr_no_bifring,
+ .get_memory_partition_mode = nbio_v7_9_get_memory_partition_mode,
+};
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.h b/drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.h
new file mode 100644
index 000000000000..8711c82a927f
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_nbio_v7_9.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_NBIO_V7_9_H__
+#define __RAS_NBIO_V7_9_H__
+#include "ras_nbio.h"
+
+extern const struct ras_nbio_ip_func ras_nbio_v7_9;
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_process.c b/drivers/gpu/drm/amd/ras/rascore/ras_process.c
new file mode 100644
index 000000000000..3267dcdb169c
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_process.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_process.h"
+
+#define RAS_EVENT_FIFO_SIZE (128 * sizeof(struct ras_event_req))
+
+#define RAS_POLLING_ECC_TIMEOUT 300
+
+static int ras_process_put_event(struct ras_core_context *ras_core,
+ struct ras_event_req *req)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+ int ret;
+
+ ret = kfifo_in_spinlocked(&ras_proc->event_fifo,
+ req, sizeof(*req), &ras_proc->fifo_spinlock);
+ if (!ret) {
+ RAS_DEV_ERR(ras_core->dev, "Poison message fifo is full!\n");
+ return -ENOSPC;
+ }
+
+ return 0;
+}
+
+static int ras_process_add_reset_gpu_event(struct ras_core_context *ras_core,
+ uint32_t reset_cause)
+{
+ struct ras_event_req req = {0};
+
+ req.reset = reset_cause;
+
+ return ras_process_put_event(ras_core, &req);
+}
+
+static int ras_process_get_event(struct ras_core_context *ras_core,
+ struct ras_event_req *req)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+
+ return kfifo_out_spinlocked(&ras_proc->event_fifo,
+ req, sizeof(*req), &ras_proc->fifo_spinlock);
+}
+
+static void ras_process_clear_event_fifo(struct ras_core_context *ras_core)
+{
+ struct ras_event_req req;
+ int ret;
+
+ do {
+ ret = ras_process_get_event(ras_core, &req);
+ } while (ret);
+}
+
+#define AMDGPU_RAS_WAITING_DATA_READY 200
+static int ras_process_umc_event(struct ras_core_context *ras_core,
+ uint32_t event_count)
+{
+ struct ras_ecc_count ecc_data;
+ int ret = 0;
+ uint32_t timeout = 0;
+ uint32_t detected_de_count = 0;
+
+ do {
+ memset(&ecc_data, 0, sizeof(ecc_data));
+ ret = ras_core_update_ecc_info(ras_core);
+ if (ret)
+ return ret;
+
+ ret = ras_core_query_block_ecc_data(ras_core, RAS_BLOCK_ID__UMC, &ecc_data);
+ if (ret)
+ return ret;
+
+ if (ecc_data.new_de_count) {
+ detected_de_count += ecc_data.new_de_count;
+ timeout = 0;
+ } else {
+ if (!timeout && event_count)
+ timeout = AMDGPU_RAS_WAITING_DATA_READY;
+
+ if (timeout) {
+ if (!--timeout)
+ break;
+
+ msleep(1);
+ }
+ }
+ } while (detected_de_count < event_count);
+
+ if (detected_de_count && ras_core_gpu_is_rma(ras_core))
+ ras_process_add_reset_gpu_event(ras_core, GPU_RESET_CAUSE_RMA);
+
+ return 0;
+}
+
+static int ras_process_non_umc_event(struct ras_core_context *ras_core)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+ struct ras_event_req req;
+ uint32_t event_count = kfifo_len(&ras_proc->event_fifo);
+ uint32_t reset_flags = 0;
+ int ret = 0, i;
+
+ for (i = 0; i < event_count; i++) {
+ memset(&req, 0, sizeof(req));
+ ret = ras_process_get_event(ras_core, &req);
+ if (!ret)
+ continue;
+
+ ras_core_event_notify(ras_core,
+ RAS_EVENT_ID__POISON_CONSUMPTION, &req);
+
+ reset_flags |= req.reset;
+
+ if (req.reset == GPU_RESET_CAUSE_RMA)
+ continue;
+
+ if (req.reset)
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} GPU reset for %s RAS poison consumption is issued!\n",
+ req.seqno, ras_core_get_ras_block_name(req.block));
+ else
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} %s RAS poison consumption is issued!\n",
+ req.seqno, ras_core_get_ras_block_name(req.block));
+ }
+
+ if (reset_flags) {
+ ret = ras_core_event_notify(ras_core,
+ RAS_EVENT_ID__RESET_GPU, &reset_flags);
+ if (!ret && (reset_flags & GPU_RESET_CAUSE_RMA))
+ return -RAS_CORE_GPU_IN_MODE1_RESET;
+ }
+
+ return ret;
+}
+
+int ras_process_handle_ras_event(struct ras_core_context *ras_core)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+ uint32_t umc_event_count;
+ int ret;
+
+ ret = ras_core_event_notify(ras_core,
+ RAS_EVENT_ID__RAS_EVENT_PROC_BEGIN, NULL);
+ if (ret)
+ return ret;
+
+ ras_aca_clear_fatal_flag(ras_core);
+ ras_umc_log_pending_bad_bank(ras_core);
+
+ do {
+ umc_event_count = atomic_read(&ras_proc->umc_interrupt_count);
+ ret = ras_process_umc_event(ras_core, umc_event_count);
+ if (ret == -RAS_CORE_GPU_IN_MODE1_RESET)
+ break;
+
+ if (umc_event_count)
+ atomic_sub(umc_event_count, &ras_proc->umc_interrupt_count);
+ } while (atomic_read(&ras_proc->umc_interrupt_count));
+
+ if ((ret != -RAS_CORE_GPU_IN_MODE1_RESET) &&
+ (kfifo_len(&ras_proc->event_fifo)))
+ ret = ras_process_non_umc_event(ras_core);
+
+ if (ret == -RAS_CORE_GPU_IN_MODE1_RESET) {
+ /* Clear poison fifo */
+ ras_process_clear_event_fifo(ras_core);
+ atomic_set(&ras_proc->umc_interrupt_count, 0);
+ }
+
+ ras_core_event_notify(ras_core,
+ RAS_EVENT_ID__RAS_EVENT_PROC_END, NULL);
+ return ret;
+}
+
+static int thread_wait_condition(void *param)
+{
+ struct ras_process *ras_proc = (struct ras_process *)param;
+
+ return (kthread_should_stop() ||
+ atomic_read(&ras_proc->ras_interrupt_req));
+}
+
+static int ras_process_thread(void *context)
+{
+ struct ras_core_context *ras_core = (struct ras_core_context *)context;
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+
+ while (!kthread_should_stop()) {
+ ras_wait_event_interruptible_timeout(&ras_proc->ras_process_wq,
+ thread_wait_condition, ras_proc,
+ msecs_to_jiffies(RAS_POLLING_ECC_TIMEOUT));
+
+ if (kthread_should_stop())
+ break;
+
+ if (!ras_core->is_initialized)
+ continue;
+
+ atomic_set(&ras_proc->ras_interrupt_req, 0);
+
+ if (ras_core_gpu_in_reset(ras_core))
+ continue;
+
+ if (ras_core->sys_fn && ras_core->sys_fn->async_handle_ras_event)
+ ras_core->sys_fn->async_handle_ras_event(ras_core, NULL);
+ else
+ ras_process_handle_ras_event(ras_core);
+ }
+
+ return 0;
+}
+
+int ras_process_init(struct ras_core_context *ras_core)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+ int ret;
+
+ ret = kfifo_alloc(&ras_proc->event_fifo, RAS_EVENT_FIFO_SIZE, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&ras_proc->fifo_spinlock);
+
+ init_waitqueue_head(&ras_proc->ras_process_wq);
+
+ ras_proc->ras_process_thread = kthread_run(ras_process_thread,
+ (void *)ras_core, "ras_process_thread");
+ if (!ras_proc->ras_process_thread) {
+ RAS_DEV_ERR(ras_core->dev, "Failed to create ras_process_thread.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ ras_process_fini(ras_core);
+ return ret;
+}
+
+int ras_process_fini(struct ras_core_context *ras_core)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+
+ if (ras_proc->ras_process_thread) {
+ kthread_stop(ras_proc->ras_process_thread);
+ ras_proc->ras_process_thread = NULL;
+ }
+
+ kfifo_free(&ras_proc->event_fifo);
+
+ return 0;
+}
+
+static int ras_process_add_umc_interrupt_req(struct ras_core_context *ras_core,
+ struct ras_event_req *req)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+
+ atomic_inc(&ras_proc->umc_interrupt_count);
+ atomic_inc(&ras_proc->ras_interrupt_req);
+
+ wake_up(&ras_proc->ras_process_wq);
+ return 0;
+}
+
+static int ras_process_add_non_umc_interrupt_req(struct ras_core_context *ras_core,
+ struct ras_event_req *req)
+{
+ struct ras_process *ras_proc = &ras_core->ras_proc;
+ int ret;
+
+ ret = ras_process_put_event(ras_core, req);
+ if (!ret) {
+ atomic_inc(&ras_proc->ras_interrupt_req);
+ wake_up(&ras_proc->ras_process_wq);
+ }
+
+ return ret;
+}
+
+int ras_process_add_interrupt_req(struct ras_core_context *ras_core,
+ struct ras_event_req *req, bool is_umc)
+{
+ int ret;
+
+ if (!ras_core)
+ return -EINVAL;
+
+ if (!ras_core->is_initialized)
+ return -EPERM;
+
+ if (is_umc)
+ ret = ras_process_add_umc_interrupt_req(ras_core, req);
+ else
+ ret = ras_process_add_non_umc_interrupt_req(ras_core, req);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_process.h b/drivers/gpu/drm/amd/ras/rascore/ras_process.h
new file mode 100644
index 000000000000..28458b50510e
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_process.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_PROCESS_H__
+#define __RAS_PROCESS_H__
+
+struct ras_event_req {
+ uint64_t seqno;
+ uint32_t idx_vf;
+ uint32_t block;
+ uint16_t pasid;
+ uint32_t reset;
+ void *pasid_fn;
+ void *data;
+};
+
+struct ras_process {
+ void *dev;
+ void *ras_process_thread;
+ wait_queue_head_t ras_process_wq;
+ atomic_t ras_interrupt_req;
+ atomic_t umc_interrupt_count;
+ struct kfifo event_fifo;
+ spinlock_t fifo_spinlock;
+};
+
+struct ras_core_context;
+int ras_process_init(struct ras_core_context *ras_core);
+int ras_process_fini(struct ras_core_context *ras_core);
+int ras_process_handle_ras_event(struct ras_core_context *ras_core);
+int ras_process_add_interrupt_req(struct ras_core_context *ras_core,
+ struct ras_event_req *req, bool is_umc);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_psp.c b/drivers/gpu/drm/amd/ras/rascore/ras_psp.c
new file mode 100644
index 000000000000..ccdb42d2dd60
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_psp.c
@@ -0,0 +1,750 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_ta_if.h"
+#include "ras_psp.h"
+#include "ras_psp_v13_0.h"
+
+/* position of instance value in sub_block_index of
+ * ta_ras_trigger_error_input, the sub block uses lower 12 bits
+ */
+#define RAS_TA_INST_MASK 0xfffff000
+#define RAS_TA_INST_SHIFT 0xc
+
+static const struct ras_psp_ip_func *ras_psp_get_ip_funcs(
+ struct ras_core_context *ras_core, uint32_t ip_version)
+{
+ switch (ip_version) {
+ case IP_VERSION(13, 0, 6):
+ case IP_VERSION(13, 0, 14):
+ case IP_VERSION(13, 0, 12):
+ return &ras_psp_v13_0;
+ default:
+ RAS_DEV_ERR(ras_core->dev,
+ "psp ip version(0x%x) is not supported!\n", ip_version);
+ break;
+ }
+
+ return NULL;
+}
+
+static int ras_psp_sync_system_ras_psp_status(struct ras_core_context *ras_core)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+ struct ras_psp_ctx *psp_ctx = &ras_core->ras_psp.psp_ctx;
+ struct ras_psp_sys_status status = {0};
+ int ret;
+
+ if (psp->sys_func && psp->sys_func->get_ras_psp_system_status) {
+ ret = psp->sys_func->get_ras_psp_system_status(ras_core, &status);
+ if (ret)
+ return ret;
+
+ if (status.initialized) {
+ ta_ctx->preload_ras_ta_enabled = true;
+ ta_ctx->ras_ta_initialized = status.initialized;
+ ta_ctx->session_id = status.session_id;
+ }
+
+ psp_ctx->external_mutex = status.psp_cmd_mutex;
+ }
+
+ return 0;
+}
+
+static int ras_psp_get_ras_ta_init_param(struct ras_core_context *ras_core,
+ struct ras_ta_init_param *ras_ta_param)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+
+ if (psp->sys_func && psp->sys_func->get_ras_ta_init_param)
+ return psp->sys_func->get_ras_ta_init_param(ras_core, ras_ta_param);
+
+ RAS_DEV_ERR(ras_core->dev, "Not config get_ras_ta_init_param API!!\n");
+ return -EACCES;
+}
+
+static struct gpu_mem_block *ras_psp_get_gpu_mem(struct ras_core_context *ras_core,
+ enum gpu_mem_type mem_type)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+ struct gpu_mem_block *gpu_mem = NULL;
+ int ret;
+
+ switch (mem_type) {
+ case GPU_MEM_TYPE_RAS_PSP_RING:
+ gpu_mem = &psp->psp_ring.ras_ring_gpu_mem;
+ break;
+ case GPU_MEM_TYPE_RAS_PSP_CMD:
+ gpu_mem = &psp->psp_ctx.psp_cmd_gpu_mem;
+ break;
+ case GPU_MEM_TYPE_RAS_PSP_FENCE:
+ gpu_mem = &psp->psp_ctx.out_fence_gpu_mem;
+ break;
+ case GPU_MEM_TYPE_RAS_TA_FW:
+ gpu_mem = &psp->ta_ctx.fw_gpu_mem;
+ break;
+ case GPU_MEM_TYPE_RAS_TA_CMD:
+ gpu_mem = &psp->ta_ctx.cmd_gpu_mem;
+ break;
+ default:
+ return NULL;
+ }
+
+ if (!gpu_mem->ref_count) {
+ ret = ras_core_get_gpu_mem(ras_core, mem_type, gpu_mem);
+ if (ret)
+ return NULL;
+ gpu_mem->mem_type = mem_type;
+ }
+
+ gpu_mem->ref_count++;
+
+ return gpu_mem;
+}
+
+static int ras_psp_put_gpu_mem(struct ras_core_context *ras_core,
+ struct gpu_mem_block *gpu_mem)
+{
+ if (!gpu_mem)
+ return 0;
+
+ gpu_mem->ref_count--;
+
+ if (gpu_mem->ref_count > 0) {
+ return 0;
+ } else if (gpu_mem->ref_count < 0) {
+ RAS_DEV_WARN(ras_core->dev,
+ "Duplicate free gpu memory %u\n", gpu_mem->mem_type);
+ } else {
+ ras_core_put_gpu_mem(ras_core, gpu_mem->mem_type, gpu_mem);
+ memset(gpu_mem, 0, sizeof(*gpu_mem));
+ }
+
+ return 0;
+}
+
+static void __acquire_psp_cmd_lock(struct ras_core_context *ras_core)
+{
+ struct ras_psp_ctx *psp_ctx = &ras_core->ras_psp.psp_ctx;
+
+ if (psp_ctx->external_mutex)
+ mutex_lock(psp_ctx->external_mutex);
+ else
+ mutex_lock(&psp_ctx->internal_mutex);
+}
+
+static void __release_psp_cmd_lock(struct ras_core_context *ras_core)
+{
+ struct ras_psp_ctx *psp_ctx = &ras_core->ras_psp.psp_ctx;
+
+ if (psp_ctx->external_mutex)
+ mutex_unlock(psp_ctx->external_mutex);
+ else
+ mutex_unlock(&psp_ctx->internal_mutex);
+}
+
+static uint32_t __get_ring_frame_slot(struct ras_core_context *ras_core)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+ uint32_t ras_ring_wptr_dw;
+
+ ras_ring_wptr_dw = psp->ip_func->psp_ras_ring_wptr_get(ras_core);
+
+ return div64_u64((ras_ring_wptr_dw << 2), sizeof(struct psp_gfx_rb_frame));
+}
+
+static int __set_ring_frame_slot(struct ras_core_context *ras_core,
+ uint32_t slot)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+
+ return psp->ip_func->psp_ras_ring_wptr_set(ras_core,
+ (slot * sizeof(struct psp_gfx_rb_frame)) >> 2);
+}
+
+static int write_frame_to_ras_psp_ring(struct ras_core_context *ras_core,
+ struct psp_gfx_rb_frame *frame)
+{
+ struct gpu_mem_block *ring_mem;
+ struct psp_gfx_rb_frame *rb_frame;
+ uint32_t max_frame_slot;
+ uint32_t slot_idx;
+ uint32_t write_flush_read_back = 0;
+ int ret = 0;
+
+ ring_mem = ras_psp_get_gpu_mem(ras_core, GPU_MEM_TYPE_RAS_PSP_RING);
+ if (!ring_mem)
+ return -ENOMEM;
+
+ max_frame_slot =
+ div64_u64(ring_mem->mem_size, sizeof(struct psp_gfx_rb_frame));
+
+ rb_frame =
+ (struct psp_gfx_rb_frame *)ring_mem->mem_cpu_addr;
+
+ slot_idx = __get_ring_frame_slot(ras_core);
+ if (slot_idx >= max_frame_slot)
+ slot_idx = 0;
+
+ memcpy(&rb_frame[slot_idx], frame, sizeof(*frame));
+
+ /* Do a read to force the write of the frame before writing
+ * write pointer.
+ */
+ write_flush_read_back = rb_frame[slot_idx].fence_value;
+ if (write_flush_read_back != frame->fence_value) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to submit ring cmd! cmd:0x%x:0x%x, fence:0x%x:0x%x value:%u, expected:%u\n",
+ rb_frame[slot_idx].cmd_buf_addr_hi,
+ rb_frame[slot_idx].cmd_buf_addr_lo,
+ rb_frame[slot_idx].fence_addr_hi,
+ rb_frame[slot_idx].fence_addr_lo,
+ write_flush_read_back, frame->fence_value);
+ ret = -EACCES;
+ goto err;
+ }
+
+ slot_idx++;
+
+ if (slot_idx >= max_frame_slot)
+ slot_idx = 0;
+
+ __set_ring_frame_slot(ras_core, slot_idx);
+
+err:
+ ras_psp_put_gpu_mem(ras_core, ring_mem);
+ return ret;
+}
+
+static int send_psp_cmd(struct ras_core_context *ras_core,
+ enum psp_gfx_cmd_id gfx_cmd_id, void *cmd_data,
+ uint32_t cmd_size, struct psp_cmd_resp *resp)
+{
+ struct ras_psp_ctx *psp_ctx = &ras_core->ras_psp.psp_ctx;
+ struct gpu_mem_block *psp_cmd_buf = NULL;
+ struct gpu_mem_block *psp_fence_buf = NULL;
+ struct psp_gfx_cmd_resp *gfx_cmd;
+ struct psp_gfx_rb_frame rb_frame;
+ int ret = 0;
+ int timeout = 1000;
+
+ if (!cmd_data || (cmd_size > sizeof(union psp_gfx_commands)) || !resp) {
+ RAS_DEV_ERR(ras_core->dev, "Invalid RAS PSP command, id: %u\n", gfx_cmd_id);
+ return -EINVAL;
+ }
+
+ __acquire_psp_cmd_lock(ras_core);
+
+ psp_cmd_buf = ras_psp_get_gpu_mem(ras_core, GPU_MEM_TYPE_RAS_PSP_CMD);
+ if (!psp_cmd_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ psp_fence_buf = ras_psp_get_gpu_mem(ras_core, GPU_MEM_TYPE_RAS_PSP_FENCE);
+ if (!psp_fence_buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ gfx_cmd = (struct psp_gfx_cmd_resp *)psp_cmd_buf->mem_cpu_addr;
+ memset(gfx_cmd, 0, sizeof(*gfx_cmd));
+ gfx_cmd->cmd_id = gfx_cmd_id;
+ memcpy(&gfx_cmd->cmd, cmd_data, cmd_size);
+
+ psp_ctx->in_fence_value++;
+
+ memset(&rb_frame, 0, sizeof(rb_frame));
+ rb_frame.cmd_buf_addr_hi = upper_32_bits(psp_cmd_buf->mem_mc_addr);
+ rb_frame.cmd_buf_addr_lo = lower_32_bits(psp_cmd_buf->mem_mc_addr);
+ rb_frame.fence_addr_hi = upper_32_bits(psp_fence_buf->mem_mc_addr);
+ rb_frame.fence_addr_lo = lower_32_bits(psp_fence_buf->mem_mc_addr);
+ rb_frame.fence_value = psp_ctx->in_fence_value;
+
+ ret = write_frame_to_ras_psp_ring(ras_core, &rb_frame);
+ if (ret) {
+ psp_ctx->in_fence_value--;
+ goto exit;
+ }
+
+ while (*((uint64_t *)psp_fence_buf->mem_cpu_addr) !=
+ psp_ctx->in_fence_value) {
+ if (--timeout == 0)
+ break;
+ /*
+ * Shouldn't wait for timeout when err_event_athub occurs,
+ * because gpu reset thread triggered and lock resource should
+ * be released for psp resume sequence.
+ */
+ if (ras_core_ras_interrupt_detected(ras_core))
+ break;
+
+ msleep(2);
+ }
+
+ resp->status = gfx_cmd->resp.status;
+ resp->session_id = gfx_cmd->resp.session_id;
+
+exit:
+ ras_psp_put_gpu_mem(ras_core, psp_cmd_buf);
+ ras_psp_put_gpu_mem(ras_core, psp_fence_buf);
+
+ __release_psp_cmd_lock(ras_core);
+
+ return ret;
+}
+
+static void __check_ras_ta_cmd_resp(struct ras_core_context *ras_core,
+ struct ras_ta_cmd *ras_cmd)
+{
+
+ if (ras_cmd->ras_out_message.flags.err_inject_switch_disable_flag) {
+ RAS_DEV_WARN(ras_core->dev, "ECC switch disabled\n");
+ ras_cmd->ras_status = RAS_TA_STATUS__ERROR_RAS_NOT_AVAILABLE;
+ } else if (ras_cmd->ras_out_message.flags.reg_access_failure_flag)
+ RAS_DEV_WARN(ras_core->dev, "RAS internal register access blocked\n");
+
+ switch (ras_cmd->ras_status) {
+ case RAS_TA_STATUS__ERROR_UNSUPPORTED_IP:
+ RAS_DEV_WARN(ras_core->dev,
+ "RAS WARNING: cmd failed due to unsupported ip\n");
+ break;
+ case RAS_TA_STATUS__ERROR_UNSUPPORTED_ERROR_INJ:
+ RAS_DEV_WARN(ras_core->dev,
+ "RAS WARNING: cmd failed due to unsupported error injection\n");
+ break;
+ case RAS_TA_STATUS__SUCCESS:
+ break;
+ case RAS_TA_STATUS__TEE_ERROR_ACCESS_DENIED:
+ if (ras_cmd->cmd_id == RAS_TA_CMD_ID__TRIGGER_ERROR)
+ RAS_DEV_WARN(ras_core->dev,
+ "RAS WARNING: Inject error to critical region is not allowed\n");
+ break;
+ default:
+ RAS_DEV_WARN(ras_core->dev,
+ "RAS WARNING: ras status = 0x%X\n", ras_cmd->ras_status);
+ break;
+ }
+}
+
+static int send_ras_ta_runtime_cmd(struct ras_core_context *ras_core,
+ enum ras_ta_cmd_id cmd_id, void *in, uint32_t in_size,
+ void *out, uint32_t out_size)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+ struct gpu_mem_block *cmd_mem;
+ struct ras_ta_cmd *ras_cmd;
+ struct psp_gfx_cmd_invoke_cmd invoke_cmd = {0};
+ struct psp_cmd_resp resp = {0};
+ int ret = 0;
+
+ if (!in || (in_size > sizeof(union ras_ta_cmd_input)) ||
+ (cmd_id >= MAX_RAS_TA_CMD_ID)) {
+ RAS_DEV_ERR(ras_core->dev, "Invalid RAS TA command, id: %u\n", cmd_id);
+ return -EINVAL;
+ }
+
+ ras_psp_sync_system_ras_psp_status(ras_core);
+
+ cmd_mem = ras_psp_get_gpu_mem(ras_core, GPU_MEM_TYPE_RAS_TA_CMD);
+ if (!cmd_mem)
+ return -ENOMEM;
+
+ if (!ras_core_down_trylock_gpu_reset_lock(ras_core)) {
+ ret = -EACCES;
+ goto out;
+ }
+
+ ras_cmd = (struct ras_ta_cmd *)cmd_mem->mem_cpu_addr;
+
+ mutex_lock(&ta_ctx->ta_mutex);
+
+ memset(ras_cmd, 0, sizeof(*ras_cmd));
+ ras_cmd->cmd_id = cmd_id;
+ memcpy(&ras_cmd->ras_in_message, in, in_size);
+
+ invoke_cmd.ta_cmd_id = cmd_id;
+ invoke_cmd.session_id = ta_ctx->session_id;
+
+ ret = send_psp_cmd(ras_core, GFX_CMD_ID_INVOKE_CMD,
+ &invoke_cmd, sizeof(invoke_cmd), &resp);
+
+ /* If err_event_athub occurs error inject was successful, however
+ * return status from TA is no long reliable
+ */
+ if (ras_core_ras_interrupt_detected(ras_core)) {
+ ret = 0;
+ goto unlock;
+ }
+
+ if (ret || resp.status) {
+ RAS_DEV_ERR(ras_core->dev,
+ "RAS: Failed to send psp cmd! ret:%d, status:%u\n",
+ ret, resp.status);
+ ret = -ESTRPIPE;
+ goto unlock;
+ }
+
+ if (ras_cmd->if_version > RAS_TA_HOST_IF_VER) {
+ RAS_DEV_WARN(ras_core->dev, "RAS: Unsupported Interface\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (!ras_cmd->ras_status && out && out_size)
+ memcpy(out, &ras_cmd->ras_out_message, out_size);
+
+ __check_ras_ta_cmd_resp(ras_core, ras_cmd);
+
+unlock:
+ mutex_unlock(&ta_ctx->ta_mutex);
+ ras_core_up_gpu_reset_lock(ras_core);
+out:
+ ras_psp_put_gpu_mem(ras_core, cmd_mem);
+ return ret;
+}
+
+static int trigger_ras_ta_error(struct ras_core_context *ras_core,
+ struct ras_ta_trigger_error_input *info, uint32_t instance_mask)
+{
+ uint32_t dev_mask = 0;
+
+ switch (info->block_id) {
+ case RAS_TA_BLOCK__GFX:
+ if (ras_gfx_get_ta_subblock(ras_core, info->inject_error_type,
+ info->sub_block_index, &info->sub_block_index))
+ return -EINVAL;
+
+ dev_mask = RAS_GET_MASK(ras_core->dev, GC, instance_mask);
+ break;
+ case RAS_TA_BLOCK__SDMA:
+ dev_mask = RAS_GET_MASK(ras_core->dev, SDMA0, instance_mask);
+ break;
+ case RAS_TA_BLOCK__VCN:
+ case RAS_TA_BLOCK__JPEG:
+ dev_mask = RAS_GET_MASK(ras_core->dev, VCN, instance_mask);
+ break;
+ default:
+ dev_mask = instance_mask;
+ break;
+ }
+
+ /* reuse sub_block_index for backward compatibility */
+ dev_mask <<= RAS_TA_INST_SHIFT;
+ dev_mask &= RAS_TA_INST_MASK;
+ info->sub_block_index |= dev_mask;
+
+ return send_ras_ta_runtime_cmd(ras_core, RAS_TA_CMD_ID__TRIGGER_ERROR,
+ info, sizeof(*info), NULL, 0);
+}
+
+static int send_load_ta_fw_cmd(struct ras_core_context *ras_core,
+ struct ras_ta_ctx *ta_ctx)
+{
+ struct ras_ta_fw_bin *fw_bin = &ta_ctx->fw_bin;
+ struct gpu_mem_block *fw_mem;
+ struct gpu_mem_block *cmd_mem;
+ struct ras_ta_cmd *ta_cmd;
+ struct ras_ta_init_flags *ta_init_flags;
+ struct psp_gfx_cmd_load_ta psp_load_ta_cmd;
+ struct psp_cmd_resp resp = {0};
+ struct ras_ta_image_header *fw_hdr = NULL;
+ int ret;
+
+ fw_mem = ras_psp_get_gpu_mem(ras_core, GPU_MEM_TYPE_RAS_TA_FW);
+ if (!fw_mem)
+ return -ENOMEM;
+
+ cmd_mem = ras_psp_get_gpu_mem(ras_core, GPU_MEM_TYPE_RAS_TA_CMD);
+ if (!cmd_mem) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = ras_psp_get_ras_ta_init_param(ras_core, &ta_ctx->init_param);
+ if (ret)
+ goto err;
+
+ if (!ras_core_down_trylock_gpu_reset_lock(ras_core)) {
+ ret = -EACCES;
+ goto err;
+ }
+
+ /* copy ras ta binary to shared gpu memory */
+ memcpy(fw_mem->mem_cpu_addr, fw_bin->bin_addr, fw_bin->bin_size);
+ fw_mem->mem_size = fw_bin->bin_size;
+
+ /* Initialize ras ta startup parameter */
+ ta_cmd = (struct ras_ta_cmd *)cmd_mem->mem_cpu_addr;
+ ta_init_flags = &ta_cmd->ras_in_message.init_flags;
+
+ ta_init_flags->poison_mode_en = ta_ctx->init_param.poison_mode_en;
+ ta_init_flags->dgpu_mode = ta_ctx->init_param.dgpu_mode;
+ ta_init_flags->xcc_mask = ta_ctx->init_param.xcc_mask;
+ ta_init_flags->channel_dis_num = ta_ctx->init_param.channel_dis_num;
+ ta_init_flags->nps_mode = ta_ctx->init_param.nps_mode;
+ ta_init_flags->active_umc_mask = ta_ctx->init_param.active_umc_mask;
+
+ /* Setup load ras ta command */
+ memset(&psp_load_ta_cmd, 0, sizeof(psp_load_ta_cmd));
+ psp_load_ta_cmd.app_phy_addr_lo = lower_32_bits(fw_mem->mem_mc_addr);
+ psp_load_ta_cmd.app_phy_addr_hi = upper_32_bits(fw_mem->mem_mc_addr);
+ psp_load_ta_cmd.app_len = fw_mem->mem_size;
+ psp_load_ta_cmd.cmd_buf_phy_addr_lo = lower_32_bits(cmd_mem->mem_mc_addr);
+ psp_load_ta_cmd.cmd_buf_phy_addr_hi = upper_32_bits(cmd_mem->mem_mc_addr);
+ psp_load_ta_cmd.cmd_buf_len = cmd_mem->mem_size;
+
+ ret = send_psp_cmd(ras_core, GFX_CMD_ID_LOAD_TA,
+ &psp_load_ta_cmd, sizeof(psp_load_ta_cmd), &resp);
+ if (!ret && !resp.status) {
+ /* Read TA version at FW offset 0x60 if TA version not found*/
+ fw_hdr = (struct ras_ta_image_header *)fw_bin->bin_addr;
+ RAS_DEV_INFO(ras_core->dev, "PSP: RAS TA(version:%X.%X.%X.%X) is loaded.\n",
+ (fw_hdr->image_version >> 24) & 0xFF, (fw_hdr->image_version >> 16) & 0xFF,
+ (fw_hdr->image_version >> 8) & 0xFF, fw_hdr->image_version & 0xFF);
+ ta_ctx->ta_version = fw_hdr->image_version;
+ ta_ctx->session_id = resp.session_id;
+ ta_ctx->ras_ta_initialized = true;
+ } else {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to load RAS TA! ret:%d, status:%d\n", ret, resp.status);
+ }
+
+ ras_core_up_gpu_reset_lock(ras_core);
+
+err:
+ ras_psp_put_gpu_mem(ras_core, fw_mem);
+ ras_psp_put_gpu_mem(ras_core, cmd_mem);
+ return ret;
+}
+
+static int load_ras_ta_firmware(struct ras_core_context *ras_core,
+ struct ras_psp_ta_load *ras_ta_load)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+ struct ras_ta_fw_bin *fw_bin = &ta_ctx->fw_bin;
+ int ret;
+
+ fw_bin->bin_addr = ras_ta_load->bin_addr;
+ fw_bin->bin_size = ras_ta_load->bin_size;
+ fw_bin->fw_version = ras_ta_load->fw_version;
+ fw_bin->feature_version = ras_ta_load->feature_version;
+
+ ret = send_load_ta_fw_cmd(ras_core, ta_ctx);
+ if (!ret) {
+ ras_ta_load->out_session_id = ta_ctx->session_id;
+ ras_ta_load->out_loaded_ta_version = ta_ctx->ta_version;
+ }
+
+ return ret;
+}
+
+static int unload_ras_ta_firmware(struct ras_core_context *ras_core,
+ struct ras_psp_ta_unload *ras_ta_unload)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+ struct psp_gfx_cmd_unload_ta cmd_unload_ta = {0};
+ struct psp_cmd_resp resp = {0};
+ int ret;
+
+ if (!ras_core_down_trylock_gpu_reset_lock(ras_core))
+ return -EACCES;
+
+ cmd_unload_ta.session_id = ta_ctx->session_id;
+ ret = send_psp_cmd(ras_core, GFX_CMD_ID_UNLOAD_TA,
+ &cmd_unload_ta, sizeof(cmd_unload_ta), &resp);
+ if (ret || resp.status) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Failed to unload RAS TA! ret:%d, status:%u\n",
+ ret, resp.status);
+ goto unlock;
+ }
+
+ kfree(ta_ctx->fw_bin.bin_addr);
+ memset(&ta_ctx->fw_bin, 0, sizeof(ta_ctx->fw_bin));
+ ta_ctx->ta_version = 0;
+ ta_ctx->ras_ta_initialized = false;
+ ta_ctx->session_id = 0;
+
+unlock:
+ ras_core_up_gpu_reset_lock(ras_core);
+
+ return ret;
+}
+
+int ras_psp_load_firmware(struct ras_core_context *ras_core,
+ struct ras_psp_ta_load *ras_ta_load)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+ struct ras_psp_ta_unload ras_ta_unload = {0};
+ int ret;
+
+ if (ta_ctx->preload_ras_ta_enabled)
+ return 0;
+
+ if (!ras_ta_load)
+ return -EINVAL;
+
+ if (ta_ctx->ras_ta_initialized) {
+ ras_ta_unload.ras_session_id = ta_ctx->session_id;
+ ret = unload_ras_ta_firmware(ras_core, &ras_ta_unload);
+ if (ret)
+ return ret;
+ }
+
+ return load_ras_ta_firmware(ras_core, ras_ta_load);
+}
+
+int ras_psp_unload_firmware(struct ras_core_context *ras_core,
+ struct ras_psp_ta_unload *ras_ta_unload)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+
+ if (ta_ctx->preload_ras_ta_enabled)
+ return 0;
+
+ if ((!ras_ta_unload) ||
+ (ras_ta_unload->ras_session_id != ta_ctx->session_id))
+ return -EINVAL;
+
+ return unload_ras_ta_firmware(ras_core, ras_ta_unload);
+}
+
+int ras_psp_trigger_error(struct ras_core_context *ras_core,
+ struct ras_ta_trigger_error_input *info, uint32_t instance_mask)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+
+ if (!ta_ctx->preload_ras_ta_enabled && !ta_ctx->ras_ta_initialized) {
+ RAS_DEV_ERR(ras_core->dev, "RAS: ras firmware not initialized!");
+ return -ENOEXEC;
+ }
+
+ if (!info)
+ return -EINVAL;
+
+ return trigger_ras_ta_error(ras_core, info, instance_mask);
+}
+
+int ras_psp_query_address(struct ras_core_context *ras_core,
+ struct ras_ta_query_address_input *addr_in,
+ struct ras_ta_query_address_output *addr_out)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+
+ if (!ta_ctx->preload_ras_ta_enabled &&
+ !ta_ctx->ras_ta_initialized) {
+ RAS_DEV_ERR(ras_core->dev, "RAS: ras firmware not initialized!");
+ return -ENOEXEC;
+ }
+
+ if (!addr_in || !addr_out)
+ return -EINVAL;
+
+ return send_ras_ta_runtime_cmd(ras_core, RAS_TA_CMD_ID__QUERY_ADDRESS,
+ addr_in, sizeof(*addr_in), addr_out, sizeof(*addr_out));
+}
+
+int ras_psp_sw_init(struct ras_core_context *ras_core)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+
+ memset(psp, 0, sizeof(*psp));
+
+ psp->sys_func = ras_core->config->psp_cfg.psp_sys_fn;
+ if (!psp->sys_func) {
+ RAS_DEV_ERR(ras_core->dev, "RAS psp sys function not configured!\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&psp->psp_ctx.internal_mutex);
+ mutex_init(&psp->ta_ctx.ta_mutex);
+
+ return 0;
+}
+
+int ras_psp_sw_fini(struct ras_core_context *ras_core)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+
+ mutex_destroy(&psp->psp_ctx.internal_mutex);
+ mutex_destroy(&psp->ta_ctx.ta_mutex);
+
+ memset(psp, 0, sizeof(*psp));
+
+ return 0;
+}
+
+int ras_psp_hw_init(struct ras_core_context *ras_core)
+{
+ struct ras_psp *psp = &ras_core->ras_psp;
+
+ psp->psp_ip_version = ras_core->config->psp_ip_version;
+
+ psp->ip_func = ras_psp_get_ip_funcs(ras_core, psp->psp_ip_version);
+ if (!psp->ip_func)
+ return -EINVAL;
+
+ /* After GPU reset, the system RAS PSP status may change.
+ * therefore, it is necessary to synchronize the system status again.
+ */
+ ras_psp_sync_system_ras_psp_status(ras_core);
+
+ return 0;
+}
+
+int ras_psp_hw_fini(struct ras_core_context *ras_core)
+{
+ return 0;
+}
+
+bool ras_psp_check_supported_cmd(struct ras_core_context *ras_core,
+ enum ras_ta_cmd_id cmd_id)
+{
+ struct ras_ta_ctx *ta_ctx = &ras_core->ras_psp.ta_ctx;
+ bool ret = false;
+
+ if (!ta_ctx->preload_ras_ta_enabled && !ta_ctx->ras_ta_initialized)
+ return false;
+
+ switch (cmd_id) {
+ case RAS_TA_CMD_ID__QUERY_ADDRESS:
+ /* Currently, querying the address from RAS TA is only supported
+ * when the RAS TA firmware is loaded during driver installation.
+ */
+ if (ta_ctx->preload_ras_ta_enabled)
+ ret = true;
+ break;
+ case RAS_TA_CMD_ID__TRIGGER_ERROR:
+ ret = true;
+ break;
+ default:
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_psp.h b/drivers/gpu/drm/amd/ras/rascore/ras_psp.h
new file mode 100644
index 000000000000..71776fecfd66
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_psp.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_PSP_H__
+#define __RAS_PSP_H__
+#include "ras.h"
+#include "ras_ta_if.h"
+
+struct ras_core_context;
+struct ras_ta_trigger_error_input;
+struct ras_ta_query_address_input;
+struct ras_ta_query_address_output;
+enum ras_ta_cmd_id;
+
+struct ras_ta_image_header {
+ uint32_t reserved1[24];
+ uint32_t image_version; /* [0x60] Off Chip Firmware Version */
+ uint32_t reserved2[39];
+};
+
+struct ras_psp_sys_status {
+ bool initialized;
+ uint32_t session_id;
+ void *psp_cmd_mutex;
+};
+
+struct ras_ta_init_param {
+ uint8_t poison_mode_en;
+ uint8_t dgpu_mode;
+ uint16_t xcc_mask;
+ uint8_t channel_dis_num;
+ uint8_t nps_mode;
+ uint32_t active_umc_mask;
+};
+
+struct gpu_mem_block {
+ uint32_t mem_type;
+ void *mem_bo;
+ uint64_t mem_mc_addr;
+ void *mem_cpu_addr;
+ uint32_t mem_size;
+ int ref_count;
+ void *private;
+};
+
+struct ras_psp_ip_func {
+ uint32_t (*psp_ras_ring_wptr_get)(struct ras_core_context *ras_core);
+ int (*psp_ras_ring_wptr_set)(struct ras_core_context *ras_core, uint32_t wptr);
+};
+
+struct ras_psp_ring {
+ struct gpu_mem_block ras_ring_gpu_mem;
+};
+
+struct psp_cmd_resp {
+ uint32_t status;
+ uint32_t session_id;
+};
+
+struct ras_psp_ctx {
+ void *external_mutex;
+ struct mutex internal_mutex;
+ uint64_t in_fence_value;
+ struct gpu_mem_block psp_cmd_gpu_mem;
+ struct gpu_mem_block out_fence_gpu_mem;
+};
+
+struct ras_ta_fw_bin {
+ uint32_t fw_version;
+ uint32_t feature_version;
+ uint32_t bin_size;
+ uint8_t *bin_addr;
+};
+
+struct ras_ta_ctx {
+ bool preload_ras_ta_enabled;
+ bool ras_ta_initialized;
+ uint32_t session_id;
+ uint32_t resp_status;
+ uint32_t ta_version;
+ struct mutex ta_mutex;
+ struct ras_ta_fw_bin fw_bin;
+ struct ras_ta_init_param init_param;
+ struct gpu_mem_block fw_gpu_mem;
+ struct gpu_mem_block cmd_gpu_mem;
+};
+
+struct ras_psp {
+ uint32_t psp_ip_version;
+ struct ras_psp_ring psp_ring;
+ struct ras_psp_ctx psp_ctx;
+ struct ras_ta_ctx ta_ctx;
+ const struct ras_psp_ip_func *ip_func;
+ const struct ras_psp_sys_func *sys_func;
+};
+
+struct ras_psp_ta_load {
+ uint32_t fw_version;
+ uint32_t feature_version;
+ uint32_t bin_size;
+ uint8_t *bin_addr;
+ uint64_t out_session_id;
+ uint32_t out_loaded_ta_version;
+};
+
+struct ras_psp_ta_unload {
+ uint64_t ras_session_id;
+};
+
+int ras_psp_sw_init(struct ras_core_context *ras_core);
+int ras_psp_sw_fini(struct ras_core_context *ras_core);
+int ras_psp_hw_init(struct ras_core_context *ras_core);
+int ras_psp_hw_fini(struct ras_core_context *ras_core);
+int ras_psp_load_firmware(struct ras_core_context *ras_core,
+ struct ras_psp_ta_load *ras_ta_load);
+int ras_psp_unload_firmware(struct ras_core_context *ras_core,
+ struct ras_psp_ta_unload *ras_ta_unload);
+int ras_psp_trigger_error(struct ras_core_context *ras_core,
+ struct ras_ta_trigger_error_input *info, uint32_t instance_mask);
+int ras_psp_query_address(struct ras_core_context *ras_core,
+ struct ras_ta_query_address_input *addr_in,
+ struct ras_ta_query_address_output *addr_out);
+bool ras_psp_check_supported_cmd(struct ras_core_context *ras_core,
+ enum ras_ta_cmd_id cmd_id);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.c
new file mode 100644
index 000000000000..626cf39b75ac
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_psp_v13_0.h"
+
+#define regMP0_SMN_C2PMSG_67 0x0083
+#define regMP0_SMN_C2PMSG_67_BASE_IDX 0
+
+static uint32_t ras_psp_v13_0_ring_wptr_get(struct ras_core_context *ras_core)
+{
+ return RAS_DEV_RREG32_SOC15(ras_core->dev, MP0, 0, regMP0_SMN_C2PMSG_67);
+}
+
+static int ras_psp_v13_0_ring_wptr_set(struct ras_core_context *ras_core, uint32_t value)
+{
+ RAS_DEV_WREG32_SOC15(ras_core->dev, MP0, 0, regMP0_SMN_C2PMSG_67, value);
+
+ return 0;
+}
+
+const struct ras_psp_ip_func ras_psp_v13_0 = {
+ .psp_ras_ring_wptr_get = ras_psp_v13_0_ring_wptr_get,
+ .psp_ras_ring_wptr_set = ras_psp_v13_0_ring_wptr_set,
+};
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.h b/drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.h
new file mode 100644
index 000000000000..b705ffe38a12
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_psp_v13_0.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_PSP_V13_0_H__
+#define __RAS_PSP_V13_0_H__
+#include "ras_psp.h"
+
+extern const struct ras_psp_ip_func ras_psp_v13_0;
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_ta_if.h b/drivers/gpu/drm/amd/ras/rascore/ras_ta_if.h
new file mode 100644
index 000000000000..0921e36d3274
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_ta_if.h
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef _RAS_TA_IF_H
+#define _RAS_TA_IF_H
+#include "ras.h"
+
+#define RAS_TA_HOST_IF_VER 0
+
+/* Responses have bit 31 set */
+#define RSP_ID_MASK (1U << 31)
+#define RSP_ID(cmdId) (((uint32_t)(cmdId)) | RSP_ID_MASK)
+
+/* invalid node instance value */
+#define RAS_TA_INV_NODE 0xffff
+
+/* RAS related enumerations */
+/**********************************************************/
+enum ras_ta_cmd_id {
+ RAS_TA_CMD_ID__ENABLE_FEATURES = 0,
+ RAS_TA_CMD_ID__DISABLE_FEATURES,
+ RAS_TA_CMD_ID__TRIGGER_ERROR,
+ RAS_TA_CMD_ID__QUERY_BLOCK_INFO,
+ RAS_TA_CMD_ID__QUERY_SUB_BLOCK_INFO,
+ RAS_TA_CMD_ID__QUERY_ADDRESS,
+ MAX_RAS_TA_CMD_ID
+};
+
+enum ras_ta_status {
+ RAS_TA_STATUS__SUCCESS = 0x0000,
+ RAS_TA_STATUS__RESET_NEEDED = 0xA001,
+ RAS_TA_STATUS__ERROR_INVALID_PARAMETER = 0xA002,
+ RAS_TA_STATUS__ERROR_RAS_NOT_AVAILABLE = 0xA003,
+ RAS_TA_STATUS__ERROR_RAS_DUPLICATE_CMD = 0xA004,
+ RAS_TA_STATUS__ERROR_INJECTION_FAILED = 0xA005,
+ RAS_TA_STATUS__ERROR_ASD_READ_WRITE = 0xA006,
+ RAS_TA_STATUS__ERROR_TOGGLE_DF_CSTATE = 0xA007,
+ RAS_TA_STATUS__ERROR_TIMEOUT = 0xA008,
+ RAS_TA_STATUS__ERROR_BLOCK_DISABLED = 0XA009,
+ RAS_TA_STATUS__ERROR_GENERIC = 0xA00A,
+ RAS_TA_STATUS__ERROR_RAS_MMHUB_INIT = 0xA00B,
+ RAS_TA_STATUS__ERROR_GET_DEV_INFO = 0xA00C,
+ RAS_TA_STATUS__ERROR_UNSUPPORTED_DEV = 0xA00D,
+ RAS_TA_STATUS__ERROR_NOT_INITIALIZED = 0xA00E,
+ RAS_TA_STATUS__ERROR_TEE_INTERNAL = 0xA00F,
+ RAS_TA_STATUS__ERROR_UNSUPPORTED_FUNCTION = 0xA010,
+ RAS_TA_STATUS__ERROR_SYS_DRV_REG_ACCESS = 0xA011,
+ RAS_TA_STATUS__ERROR_RAS_READ_WRITE = 0xA012,
+ RAS_TA_STATUS__ERROR_NULL_PTR = 0xA013,
+ RAS_TA_STATUS__ERROR_UNSUPPORTED_IP = 0xA014,
+ RAS_TA_STATUS__ERROR_PCS_STATE_QUIET = 0xA015,
+ RAS_TA_STATUS__ERROR_PCS_STATE_ERROR = 0xA016,
+ RAS_TA_STATUS__ERROR_PCS_STATE_HANG = 0xA017,
+ RAS_TA_STATUS__ERROR_PCS_STATE_UNKNOWN = 0xA018,
+ RAS_TA_STATUS__ERROR_UNSUPPORTED_ERROR_INJ = 0xA019,
+ RAS_TA_STATUS__TEE_ERROR_ACCESS_DENIED = 0xA01A
+};
+
+enum ras_ta_block {
+ RAS_TA_BLOCK__UMC = 0,
+ RAS_TA_BLOCK__SDMA,
+ RAS_TA_BLOCK__GFX,
+ RAS_TA_BLOCK__MMHUB,
+ RAS_TA_BLOCK__ATHUB,
+ RAS_TA_BLOCK__PCIE_BIF,
+ RAS_TA_BLOCK__HDP,
+ RAS_TA_BLOCK__XGMI_WAFL,
+ RAS_TA_BLOCK__DF,
+ RAS_TA_BLOCK__SMN,
+ RAS_TA_BLOCK__SEM,
+ RAS_TA_BLOCK__MP0,
+ RAS_TA_BLOCK__MP1,
+ RAS_TA_BLOCK__FUSE,
+ RAS_TA_BLOCK__MCA,
+ RAS_TA_BLOCK__VCN,
+ RAS_TA_BLOCK__JPEG,
+ RAS_TA_BLOCK__IH,
+ RAS_TA_BLOCK__MPIO,
+ RAS_TA_BLOCK__MMSCH,
+ RAS_TA_NUM_BLOCK_MAX
+};
+
+enum ras_ta_mca_block {
+ RAS_TA_MCA_BLOCK__MP0 = 0,
+ RAS_TA_MCA_BLOCK__MP1 = 1,
+ RAS_TA_MCA_BLOCK__MPIO = 2,
+ RAS_TA_MCA_BLOCK__IOHC = 3,
+ RAS_TA_MCA_NUM_BLOCK_MAX
+};
+
+enum ras_ta_error_type {
+ RAS_TA_ERROR__NONE = 0,
+ RAS_TA_ERROR__PARITY = 1,
+ RAS_TA_ERROR__SINGLE_CORRECTABLE = 2,
+ RAS_TA_ERROR__MULTI_UNCORRECTABLE = 4,
+ RAS_TA_ERROR__POISON = 8,
+};
+
+enum ras_ta_address_type {
+ RAS_TA_MCA_TO_PA,
+ RAS_TA_PA_TO_MCA,
+};
+
+enum ras_ta_nps_mode {
+ RAS_TA_UNKNOWN_MODE = 0,
+ RAS_TA_NPS1_MODE = 1,
+ RAS_TA_NPS2_MODE = 2,
+ RAS_TA_NPS4_MODE = 4,
+ RAS_TA_NPS8_MODE = 8,
+};
+
+/* Input/output structures for RAS commands */
+/**********************************************************/
+
+struct ras_ta_enable_features_input {
+ enum ras_ta_block block_id;
+ enum ras_ta_error_type error_type;
+};
+
+struct ras_ta_disable_features_input {
+ enum ras_ta_block block_id;
+ enum ras_ta_error_type error_type;
+};
+
+struct ras_ta_trigger_error_input {
+ /* ras-block. i.e. umc, gfx */
+ enum ras_ta_block block_id;
+
+ /* type of error. i.e. single_correctable */
+ enum ras_ta_error_type inject_error_type;
+
+ /* mem block. i.e. hbm, sram etc. */
+ uint32_t sub_block_index;
+
+ /* explicit address of error */
+ uint64_t address;
+
+ /* method if error injection. i.e persistent, coherent etc. */
+ uint64_t value;
+};
+
+struct ras_ta_init_flags {
+ uint8_t poison_mode_en;
+ uint8_t dgpu_mode;
+ uint16_t xcc_mask;
+ uint8_t channel_dis_num;
+ uint8_t nps_mode;
+ uint32_t active_umc_mask;
+};
+
+struct ras_ta_mca_addr {
+ uint64_t err_addr;
+ uint32_t ch_inst;
+ uint32_t umc_inst;
+ uint32_t node_inst;
+ uint32_t socket_id;
+};
+
+struct ras_ta_phy_addr {
+ uint64_t pa;
+ uint32_t bank;
+ uint32_t channel_idx;
+};
+
+struct ras_ta_query_address_input {
+ enum ras_ta_address_type addr_type;
+ struct ras_ta_mca_addr ma;
+ struct ras_ta_phy_addr pa;
+};
+
+struct ras_ta_output_flags {
+ uint8_t ras_init_success_flag;
+ uint8_t err_inject_switch_disable_flag;
+ uint8_t reg_access_failure_flag;
+};
+
+struct ras_ta_query_address_output {
+ /* don't use the flags here */
+ struct ras_ta_output_flags flags;
+ struct ras_ta_mca_addr ma;
+ struct ras_ta_phy_addr pa;
+};
+
+/* Common input structure for RAS callbacks */
+/**********************************************************/
+union ras_ta_cmd_input {
+ struct ras_ta_init_flags init_flags;
+ struct ras_ta_enable_features_input enable_features;
+ struct ras_ta_disable_features_input disable_features;
+ struct ras_ta_trigger_error_input trigger_error;
+ struct ras_ta_query_address_input address;
+ uint32_t reserve_pad[256];
+};
+
+union ras_ta_cmd_output {
+ struct ras_ta_output_flags flags;
+ struct ras_ta_query_address_output address;
+ uint32_t reserve_pad[256];
+};
+
+struct ras_ta_cmd {
+ uint32_t cmd_id;
+ uint32_t resp_id;
+ uint32_t ras_status;
+ uint32_t if_version;
+ union ras_ta_cmd_input ras_in_message;
+ union ras_ta_cmd_output ras_out_message;
+};
+
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
new file mode 100644
index 000000000000..4dae64c424a2
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c
@@ -0,0 +1,707 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_umc.h"
+#include "ras_umc_v12_0.h"
+
+#define MAX_ECC_NUM_PER_RETIREMENT 16
+
+/* bad page timestamp format
+ * yy[31:27] mm[26:23] day[22:17] hh[16:12] mm[11:6] ss[5:0]
+ */
+#define EEPROM_TIMESTAMP_MINUTE 6
+#define EEPROM_TIMESTAMP_HOUR 12
+#define EEPROM_TIMESTAMP_DAY 17
+#define EEPROM_TIMESTAMP_MONTH 23
+#define EEPROM_TIMESTAMP_YEAR 27
+
+static uint64_t ras_umc_get_eeprom_timestamp(struct ras_core_context *ras_core)
+{
+ struct ras_time tm = {0};
+ uint64_t utc_timestamp = 0;
+ uint64_t eeprom_timestamp = 0;
+
+ utc_timestamp = ras_core_get_utc_second_timestamp(ras_core);
+ if (!utc_timestamp)
+ return utc_timestamp;
+
+ ras_core_convert_timestamp_to_time(ras_core, utc_timestamp, &tm);
+
+ /* the year range is 2000 ~ 2031, set the year if not in the range */
+ if (tm.tm_year < 2000)
+ tm.tm_year = 2000;
+ if (tm.tm_year > 2031)
+ tm.tm_year = 2031;
+
+ tm.tm_year -= 2000;
+
+ eeprom_timestamp = tm.tm_sec + (tm.tm_min << EEPROM_TIMESTAMP_MINUTE)
+ + (tm.tm_hour << EEPROM_TIMESTAMP_HOUR)
+ + (tm.tm_mday << EEPROM_TIMESTAMP_DAY)
+ + (tm.tm_mon << EEPROM_TIMESTAMP_MONTH)
+ + (tm.tm_year << EEPROM_TIMESTAMP_YEAR);
+ eeprom_timestamp &= 0xffffffff;
+
+ return eeprom_timestamp;
+}
+
+static const struct ras_umc_ip_func *ras_umc_get_ip_func(
+ struct ras_core_context *ras_core, uint32_t ip_version)
+{
+ switch (ip_version) {
+ case IP_VERSION(12, 0, 0):
+ case IP_VERSION(12, 5, 0):
+ return &ras_umc_func_v12_0;
+ default:
+ RAS_DEV_ERR(ras_core->dev,
+ "UMC ip version(0x%x) is not supported!\n", ip_version);
+ break;
+ }
+
+ return NULL;
+}
+
+int ras_umc_psp_convert_ma_to_pa(struct ras_core_context *ras_core,
+ struct umc_mca_addr *in, struct umc_phy_addr *out,
+ uint32_t nps)
+{
+ struct ras_ta_query_address_input addr_in;
+ struct ras_ta_query_address_output addr_out;
+ int ret;
+
+ if (!in)
+ return -EINVAL;
+
+ memset(&addr_in, 0, sizeof(addr_in));
+ memset(&addr_out, 0, sizeof(addr_out));
+
+ addr_in.ma.err_addr = in->err_addr;
+ addr_in.ma.ch_inst = in->ch_inst;
+ addr_in.ma.umc_inst = in->umc_inst;
+ addr_in.ma.node_inst = in->node_inst;
+ addr_in.ma.socket_id = in->socket_id;
+
+ addr_in.addr_type = RAS_TA_MCA_TO_PA;
+
+ ret = ras_psp_query_address(ras_core, &addr_in, &addr_out);
+ if (ret) {
+ RAS_DEV_WARN(ras_core->dev,
+ "Failed to query RAS physical address for 0x%llx, ret:%d",
+ in->err_addr, ret);
+ return -EREMOTEIO;
+ }
+
+ if (out) {
+ out->pa = addr_out.pa.pa;
+ out->bank = addr_out.pa.bank;
+ out->channel_idx = addr_out.pa.channel_idx;
+ }
+
+ return 0;
+}
+
+static int ras_umc_log_ecc(struct ras_core_context *ras_core,
+ unsigned long idx, void *data)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ int ret;
+
+ mutex_lock(&ras_umc->tree_lock);
+ ret = radix_tree_insert(&ras_umc->root, idx, data);
+ if (!ret)
+ radix_tree_tag_set(&ras_umc->root, idx, UMC_ECC_NEW_DETECTED_TAG);
+ mutex_unlock(&ras_umc->tree_lock);
+
+ return ret;
+}
+
+int ras_umc_clear_logged_ecc(struct ras_core_context *ras_core)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ uint64_t buf[8] = {0};
+ void **slot;
+ void *data;
+ void *iter = buf;
+
+ mutex_lock(&ras_umc->tree_lock);
+ radix_tree_for_each_slot(slot, &ras_umc->root, iter, 0) {
+ data = ras_radix_tree_delete_iter(&ras_umc->root, iter);
+ kfree(data);
+ }
+ mutex_unlock(&ras_umc->tree_lock);
+
+ return 0;
+}
+
+static void ras_umc_reserve_eeprom_record(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ uint64_t page_pfn[16];
+ int count = 0, i;
+
+ memset(page_pfn, 0, sizeof(page_pfn));
+ if (ras_umc->ip_func && ras_umc->ip_func->eeprom_record_to_nps_pages) {
+ count = ras_umc->ip_func->eeprom_record_to_nps_pages(ras_core,
+ record, record->cur_nps, page_pfn, ARRAY_SIZE(page_pfn));
+ if (count <= 0) {
+ RAS_DEV_ERR(ras_core->dev,
+ "Fail to convert error address! count:%d\n", count);
+ return;
+ }
+ }
+
+ /* Reserve memory */
+ for (i = 0; i < count; i++)
+ ras_core_event_notify(ras_core,
+ RAS_EVENT_ID__RESERVE_BAD_PAGE, &page_pfn[i]);
+}
+
+/* When gpu reset is ongoing, ecc logging operations will be pended.
+ */
+int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_bank_ecc *bank)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct ras_bank_ecc_node *ecc_node;
+
+ ecc_node = kzalloc(sizeof(*ecc_node), GFP_KERNEL);
+ if (!ecc_node)
+ return -ENOMEM;
+
+ memcpy(&ecc_node->ecc, bank, sizeof(ecc_node->ecc));
+
+ mutex_lock(&ras_umc->pending_ecc_lock);
+ list_add_tail(&ecc_node->node, &ras_umc->pending_ecc_list);
+ mutex_unlock(&ras_umc->pending_ecc_lock);
+
+ return 0;
+}
+
+/* After gpu reset is complete, re-log the pending error banks.
+ */
+int ras_umc_log_pending_bad_bank(struct ras_core_context *ras_core)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct ras_bank_ecc_node *ecc_node, *tmp;
+
+ mutex_lock(&ras_umc->pending_ecc_lock);
+ list_for_each_entry_safe(ecc_node,
+ tmp, &ras_umc->pending_ecc_list, node){
+ if (ecc_node && !ras_umc_log_bad_bank(ras_core, &ecc_node->ecc)) {
+ list_del(&ecc_node->node);
+ kfree(ecc_node);
+ }
+ }
+ mutex_unlock(&ras_umc->pending_ecc_lock);
+
+ return 0;
+}
+
+int ras_umc_log_bad_bank(struct ras_core_context *ras_core, struct ras_bank_ecc *bank)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct eeprom_umc_record umc_rec;
+ struct eeprom_umc_record *err_rec;
+ int ret;
+
+ memset(&umc_rec, 0, sizeof(umc_rec));
+
+ mutex_lock(&ras_umc->bank_log_lock);
+ ret = ras_umc->ip_func->bank_to_eeprom_record(ras_core, bank, &umc_rec);
+ if (ret)
+ goto out;
+
+ err_rec = kzalloc(sizeof(*err_rec), GFP_KERNEL);
+ if (!err_rec) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(err_rec, &umc_rec, sizeof(umc_rec));
+ ret = ras_umc_log_ecc(ras_core, err_rec->cur_nps_retired_row_pfn, err_rec);
+ if (ret) {
+ if (ret == -EEXIST) {
+ RAS_DEV_INFO(ras_core->dev, "The bad pages have been logged before.\n");
+ ret = 0;
+ }
+
+ kfree(err_rec);
+ goto out;
+ }
+
+ ras_umc_reserve_eeprom_record(ras_core, err_rec);
+
+ ret = ras_core_event_notify(ras_core,
+ RAS_EVENT_ID__BAD_PAGE_DETECTED, NULL);
+
+out:
+ mutex_unlock(&ras_umc->bank_log_lock);
+ return ret;
+}
+
+static int ras_umc_get_new_records(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *records, u32 num)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct eeprom_umc_record *entries[MAX_ECC_NUM_PER_RETIREMENT];
+ u32 entry_num = num < MAX_ECC_NUM_PER_RETIREMENT ? num : MAX_ECC_NUM_PER_RETIREMENT;
+ int count = 0;
+ int new_detected, i;
+
+ mutex_lock(&ras_umc->tree_lock);
+ new_detected = radix_tree_gang_lookup_tag(&ras_umc->root, (void **)entries,
+ 0, entry_num, UMC_ECC_NEW_DETECTED_TAG);
+ for (i = 0; i < new_detected; i++) {
+ if (!entries[i])
+ continue;
+
+ memcpy(&records[i], entries[i], sizeof(struct eeprom_umc_record));
+ count++;
+ radix_tree_tag_clear(&ras_umc->root,
+ entries[i]->cur_nps_retired_row_pfn, UMC_ECC_NEW_DETECTED_TAG);
+ }
+ mutex_unlock(&ras_umc->tree_lock);
+
+ return count;
+}
+
+static bool ras_umc_check_retired_record(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, bool from_eeprom)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct eeprom_store_record *data = &ras_umc->umc_err_data.rom_data;
+ uint32_t nps = 0;
+ int i, ret;
+
+ if (from_eeprom) {
+ nps = ras_umc->umc_err_data.umc_nps_mode;
+ if (ras_umc->ip_func && ras_umc->ip_func->eeprom_record_to_nps_record) {
+ ret = ras_umc->ip_func->eeprom_record_to_nps_record(ras_core, record, nps);
+ if (ret)
+ RAS_DEV_WARN(ras_core->dev,
+ "Failed to adjust eeprom record, ret:%d", ret);
+ }
+ return false;
+ }
+
+ for (i = 0; i < data->count; i++) {
+ if ((data->bps[i].retired_row_pfn == record->retired_row_pfn) &&
+ (data->bps[i].cur_nps_retired_row_pfn == record->cur_nps_retired_row_pfn))
+ return true;
+ }
+
+ return false;
+}
+
+/* alloc/realloc bps array */
+static int ras_umc_realloc_err_data_space(struct ras_core_context *ras_core,
+ struct eeprom_store_record *data, int pages)
+{
+ unsigned int old_space = data->count + data->space_left;
+ unsigned int new_space = old_space + pages;
+ unsigned int align_space = ALIGN(new_space, 512);
+ void *bps = kzalloc(align_space * sizeof(*data->bps), GFP_KERNEL);
+
+ if (!bps)
+ return -ENOMEM;
+
+ if (data->bps) {
+ memcpy(bps, data->bps,
+ data->count * sizeof(*data->bps));
+ kfree(data->bps);
+ }
+
+ data->bps = bps;
+ data->space_left += align_space - old_space;
+ return 0;
+}
+
+static int ras_umc_update_eeprom_rom_data(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *bps)
+{
+ struct eeprom_store_record *data = &ras_core->ras_umc.umc_err_data.rom_data;
+
+ if (!data->space_left &&
+ ras_umc_realloc_err_data_space(ras_core, data, 256)) {
+ return -ENOMEM;
+ }
+
+ memcpy(&data->bps[data->count], bps, sizeof(*data->bps));
+ data->count++;
+ data->space_left--;
+ return 0;
+}
+
+static int ras_umc_update_eeprom_ram_data(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *bps)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct eeprom_store_record *data = &ras_umc->umc_err_data.ram_data;
+ uint64_t page_pfn[16];
+ int count = 0, j;
+
+ if (!data->space_left &&
+ ras_umc_realloc_err_data_space(ras_core, data, 256)) {
+ return -ENOMEM;
+ }
+
+ memset(page_pfn, 0, sizeof(page_pfn));
+ if (ras_umc->ip_func && ras_umc->ip_func->eeprom_record_to_nps_pages)
+ count = ras_umc->ip_func->eeprom_record_to_nps_pages(ras_core,
+ bps, bps->cur_nps, page_pfn, ARRAY_SIZE(page_pfn));
+
+ if (count > 0) {
+ for (j = 0; j < count; j++) {
+ bps->cur_nps_retired_row_pfn = page_pfn[j];
+ memcpy(&data->bps[data->count], bps, sizeof(*data->bps));
+ data->count++;
+ data->space_left--;
+ }
+ } else {
+ memcpy(&data->bps[data->count], bps, sizeof(*data->bps));
+ data->count++;
+ data->space_left--;
+ }
+
+ return 0;
+}
+
+/* it deal with vram only. */
+static int ras_umc_add_bad_pages(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *bps,
+ int pages, bool from_eeprom)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct ras_umc_err_data *data = &ras_umc->umc_err_data;
+ int i, ret = 0;
+
+ if (!bps || pages <= 0)
+ return 0;
+
+ mutex_lock(&ras_umc->umc_lock);
+ for (i = 0; i < pages; i++) {
+ if (ras_umc_check_retired_record(ras_core, &bps[i], from_eeprom))
+ continue;
+
+ ret = ras_umc_update_eeprom_rom_data(ras_core, &bps[i]);
+ if (ret)
+ goto out;
+
+ if (data->last_retired_pfn == bps[i].cur_nps_retired_row_pfn)
+ continue;
+
+ data->last_retired_pfn = bps[i].cur_nps_retired_row_pfn;
+
+ if (from_eeprom)
+ ras_umc_reserve_eeprom_record(ras_core, &bps[i]);
+
+ ret = ras_umc_update_eeprom_ram_data(ras_core, &bps[i]);
+ if (ret)
+ goto out;
+ }
+out:
+ mutex_unlock(&ras_umc->umc_lock);
+
+ return ret;
+}
+
+/*
+ * read error record array in eeprom and reserve enough space for
+ * storing new bad pages
+ */
+int ras_umc_load_bad_pages(struct ras_core_context *ras_core)
+{
+ struct eeprom_umc_record *bps;
+ uint32_t ras_num_recs;
+ int ret;
+
+ ras_num_recs = ras_eeprom_get_record_count(ras_core);
+ /* no bad page record, skip eeprom access */
+ if (!ras_num_recs ||
+ ras_core->ras_eeprom.record_threshold_config == DISABLE_RETIRE_PAGE)
+ return 0;
+
+ bps = kcalloc(ras_num_recs, sizeof(*bps), GFP_KERNEL);
+ if (!bps)
+ return -ENOMEM;
+
+ ret = ras_eeprom_read(ras_core, bps, ras_num_recs);
+ if (ret) {
+ RAS_DEV_ERR(ras_core->dev, "Failed to load EEPROM table records!");
+ } else {
+ ras_core->ras_umc.umc_err_data.last_retired_pfn = UMC_INV_MEM_PFN;
+ ret = ras_umc_add_bad_pages(ras_core, bps, ras_num_recs, true);
+ }
+
+ kfree(bps);
+ return ret;
+}
+
+/*
+ * write error record array to eeprom, the function should be
+ * protected by recovery_lock
+ * new_cnt: new added UE count, excluding reserved bad pages, can be NULL
+ */
+static int ras_umc_save_bad_pages(struct ras_core_context *ras_core)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct eeprom_store_record *data = &ras_umc->umc_err_data.rom_data;
+ uint32_t eeprom_record_num;
+ int save_count;
+ int ret = 0;
+
+ if (!data->bps)
+ return 0;
+
+ eeprom_record_num = ras_eeprom_get_record_count(ras_core);
+ mutex_lock(&ras_umc->umc_lock);
+ save_count = data->count - eeprom_record_num;
+ /* only new entries are saved */
+ if (save_count > 0) {
+ if (ras_eeprom_append(ras_core,
+ &data->bps[eeprom_record_num],
+ save_count)) {
+ RAS_DEV_ERR(ras_core->dev, "Failed to save EEPROM table data!");
+ ret = -EIO;
+ goto exit;
+ }
+
+ RAS_DEV_INFO(ras_core->dev, "Saved %d pages to EEPROM table.\n", save_count);
+ }
+
+exit:
+ mutex_unlock(&ras_umc->umc_lock);
+ return ret;
+}
+
+int ras_umc_handle_bad_pages(struct ras_core_context *ras_core, void *data)
+{
+ struct eeprom_umc_record records[MAX_ECC_NUM_PER_RETIREMENT];
+ int count, ret;
+
+ memset(records, 0, sizeof(records));
+ count = ras_umc_get_new_records(ras_core, records, ARRAY_SIZE(records));
+ if (count <= 0)
+ return -ENODATA;
+
+ ret = ras_umc_add_bad_pages(ras_core, records, count, false);
+ if (ret) {
+ RAS_DEV_ERR(ras_core->dev, "Failed to add ras bad page!\n");
+ return -EINVAL;
+ }
+
+ ret = ras_umc_save_bad_pages(ras_core);
+ if (ret) {
+ RAS_DEV_ERR(ras_core->dev, "Failed to save ras bad page\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int ras_umc_sw_init(struct ras_core_context *ras_core)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+
+ memset(ras_umc, 0, sizeof(*ras_umc));
+
+ INIT_LIST_HEAD(&ras_umc->pending_ecc_list);
+
+ INIT_RADIX_TREE(&ras_umc->root, GFP_KERNEL);
+
+ mutex_init(&ras_umc->tree_lock);
+ mutex_init(&ras_umc->pending_ecc_lock);
+ mutex_init(&ras_umc->umc_lock);
+ mutex_init(&ras_umc->bank_log_lock);
+
+ return 0;
+}
+
+int ras_umc_sw_fini(struct ras_core_context *ras_core)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct ras_umc_err_data *umc_err_data = &ras_umc->umc_err_data;
+ struct ras_bank_ecc_node *ecc_node, *tmp;
+
+ mutex_destroy(&ras_umc->umc_lock);
+ mutex_destroy(&ras_umc->bank_log_lock);
+
+ if (umc_err_data->rom_data.bps) {
+ umc_err_data->rom_data.count = 0;
+ kfree(umc_err_data->rom_data.bps);
+ umc_err_data->rom_data.bps = NULL;
+ umc_err_data->rom_data.space_left = 0;
+ }
+
+ if (umc_err_data->ram_data.bps) {
+ umc_err_data->ram_data.count = 0;
+ kfree(umc_err_data->ram_data.bps);
+ umc_err_data->ram_data.bps = NULL;
+ umc_err_data->ram_data.space_left = 0;
+ }
+
+ ras_umc_clear_logged_ecc(ras_core);
+
+ mutex_lock(&ras_umc->pending_ecc_lock);
+ list_for_each_entry_safe(ecc_node,
+ tmp, &ras_umc->pending_ecc_list, node){
+ list_del(&ecc_node->node);
+ kfree(ecc_node);
+ }
+ mutex_unlock(&ras_umc->pending_ecc_lock);
+
+ mutex_destroy(&ras_umc->tree_lock);
+ mutex_destroy(&ras_umc->pending_ecc_lock);
+
+ return 0;
+}
+
+int ras_umc_hw_init(struct ras_core_context *ras_core)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ uint32_t nps;
+
+ nps = ras_core_get_curr_nps_mode(ras_core);
+
+ if (!nps || (nps >= UMC_MEMORY_PARTITION_MODE_UNKNOWN)) {
+ RAS_DEV_ERR(ras_core->dev, "Invalid memory NPS mode: %u!\n", nps);
+ return -ENODATA;
+ }
+
+ ras_umc->umc_err_data.umc_nps_mode = nps;
+
+ ras_umc->umc_vram_type = ras_core->config->umc_cfg.umc_vram_type;
+ if (!ras_umc->umc_vram_type) {
+ RAS_DEV_ERR(ras_core->dev, "Invalid UMC VRAM Type: %u!\n",
+ ras_umc->umc_vram_type);
+ return -ENODATA;
+ }
+
+ ras_umc->umc_ip_version = ras_core->config->umc_ip_version;
+ ras_umc->ip_func = ras_umc_get_ip_func(ras_core, ras_umc->umc_ip_version);
+ if (!ras_umc->ip_func)
+ return -EINVAL;
+
+ return 0;
+}
+
+int ras_umc_hw_fini(struct ras_core_context *ras_core)
+{
+ return 0;
+}
+
+int ras_umc_clean_badpage_data(struct ras_core_context *ras_core)
+{
+ struct ras_umc_err_data *data = &ras_core->ras_umc.umc_err_data;
+
+ mutex_lock(&ras_core->ras_umc.umc_lock);
+
+ kfree(data->rom_data.bps);
+ kfree(data->ram_data.bps);
+
+ memset(data, 0, sizeof(*data));
+ mutex_unlock(&ras_core->ras_umc.umc_lock);
+
+ return 0;
+}
+
+int ras_umc_fill_eeprom_record(struct ras_core_context *ras_core,
+ uint64_t err_addr, uint32_t umc_inst, struct umc_phy_addr *cur_nps_addr,
+ enum umc_memory_partition_mode cur_nps, struct eeprom_umc_record *record)
+{
+ struct eeprom_umc_record *err_rec = record;
+
+ /* Set bad page pfn and nps mode */
+ EEPROM_RECORD_SETUP_UMC_ADDR_AND_NPS(err_rec,
+ RAS_ADDR_TO_PFN(cur_nps_addr->pa), cur_nps);
+
+ err_rec->address = err_addr;
+ err_rec->ts = ras_umc_get_eeprom_timestamp(ras_core);
+ err_rec->err_type = RAS_EEPROM_ERR_NON_RECOVERABLE;
+ err_rec->cu = 0;
+ err_rec->mem_channel = cur_nps_addr->channel_idx;
+ err_rec->mcumc_id = umc_inst;
+ err_rec->cur_nps_retired_row_pfn = RAS_ADDR_TO_PFN(cur_nps_addr->pa);
+ err_rec->cur_nps_bank = cur_nps_addr->bank;
+ err_rec->cur_nps = cur_nps;
+ return 0;
+}
+
+int ras_umc_get_saved_eeprom_count(struct ras_core_context *ras_core)
+{
+ struct ras_umc_err_data *err_data = &ras_core->ras_umc.umc_err_data;
+
+ return err_data->rom_data.count;
+}
+
+int ras_umc_get_badpage_count(struct ras_core_context *ras_core)
+{
+ struct eeprom_store_record *data = &ras_core->ras_umc.umc_err_data.ram_data;
+
+ return data->count;
+}
+
+int ras_umc_get_badpage_record(struct ras_core_context *ras_core, uint32_t index, void *record)
+{
+ struct eeprom_store_record *data = &ras_core->ras_umc.umc_err_data.ram_data;
+
+ if (index >= data->count)
+ return -EINVAL;
+
+ memcpy(record, &data->bps[index], sizeof(struct eeprom_umc_record));
+ return 0;
+}
+
+bool ras_umc_check_retired_addr(struct ras_core_context *ras_core, uint64_t addr)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ struct eeprom_store_record *data = &ras_umc->umc_err_data.ram_data;
+ uint64_t page_pfn = RAS_ADDR_TO_PFN(addr);
+ int i, ret = false;
+
+ mutex_lock(&ras_umc->umc_lock);
+ for (i = 0; i < data->count; i++) {
+ if (data->bps[i].cur_nps_retired_row_pfn == page_pfn) {
+ ret = true;
+ break;
+ }
+ }
+ mutex_unlock(&ras_umc->umc_lock);
+
+ return ret;
+}
+
+int ras_umc_translate_soc_pa_and_bank(struct ras_core_context *ras_core,
+ uint64_t *soc_pa, struct umc_bank_addr *bank_addr, bool bank_to_pa)
+{
+ struct ras_umc *ras_umc = &ras_core->ras_umc;
+ int ret = 0;
+
+ if (bank_to_pa)
+ ret = ras_umc->ip_func->bank_to_soc_pa(ras_core, *bank_addr, soc_pa);
+ else
+ ret = ras_umc->ip_func->soc_pa_to_bank(ras_core, *soc_pa, bank_addr);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
new file mode 100644
index 000000000000..7d9e779d8c4c
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+
+#ifndef __RAS_UMC_H__
+#define __RAS_UMC_H__
+#include "ras.h"
+#include "ras_eeprom.h"
+#include "ras_cmd.h"
+
+#define UMC_VRAM_TYPE_UNKNOWN 0
+#define UMC_VRAM_TYPE_GDDR1 1
+#define UMC_VRAM_TYPE_DDR2 2
+#define UMC_VRAM_TYPE_GDDR3 3
+#define UMC_VRAM_TYPE_GDDR4 4
+#define UMC_VRAM_TYPE_GDDR5 5
+#define UMC_VRAM_TYPE_HBM 6
+#define UMC_VRAM_TYPE_DDR3 7
+#define UMC_VRAM_TYPE_DDR4 8
+#define UMC_VRAM_TYPE_GDDR6 9
+#define UMC_VRAM_TYPE_DDR5 10
+#define UMC_VRAM_TYPE_LPDDR4 11
+#define UMC_VRAM_TYPE_LPDDR5 12
+#define UMC_VRAM_TYPE_HBM3E 13
+
+#define UMC_ECC_NEW_DETECTED_TAG 0x1
+#define UMC_INV_MEM_PFN (0xFFFFFFFFFFFFFFFF)
+
+/* three column bits and one row bit in MCA address flip
+ * in bad page retirement
+ */
+#define UMC_PA_FLIP_BITS_NUM 4
+
+enum umc_memory_partition_mode {
+ UMC_MEMORY_PARTITION_MODE_NONE = 0,
+ UMC_MEMORY_PARTITION_MODE_NPS1 = 1,
+ UMC_MEMORY_PARTITION_MODE_NPS2 = 2,
+ UMC_MEMORY_PARTITION_MODE_NPS3 = 3,
+ UMC_MEMORY_PARTITION_MODE_NPS4 = 4,
+ UMC_MEMORY_PARTITION_MODE_NPS6 = 6,
+ UMC_MEMORY_PARTITION_MODE_NPS8 = 8,
+ UMC_MEMORY_PARTITION_MODE_UNKNOWN
+};
+
+struct ras_core_context;
+struct ras_bank_ecc;
+
+struct umc_flip_bits {
+ uint32_t flip_bits_in_pa[UMC_PA_FLIP_BITS_NUM];
+ uint32_t flip_row_bit;
+ uint32_t r13_in_pa;
+ uint32_t bit_num;
+};
+
+struct umc_mca_addr {
+ uint64_t err_addr;
+ uint32_t ch_inst;
+ uint32_t umc_inst;
+ uint32_t node_inst;
+ uint32_t socket_id;
+};
+
+struct umc_phy_addr {
+ uint64_t pa;
+ uint32_t bank;
+ uint32_t channel_idx;
+};
+
+struct umc_bank_addr {
+ uint32_t stack_id; /* SID */
+ uint32_t bank_group;
+ uint32_t bank;
+ uint32_t row;
+ uint32_t column;
+ uint32_t channel;
+ uint32_t subchannel; /* Also called Pseudochannel (PC) */
+};
+
+struct ras_umc_ip_func {
+ int (*bank_to_eeprom_record)(struct ras_core_context *ras_core,
+ struct ras_bank_ecc *bank, struct eeprom_umc_record *record);
+ int (*eeprom_record_to_nps_record)(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, uint32_t nps);
+ int (*eeprom_record_to_nps_pages)(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, uint32_t nps,
+ uint64_t *pfns, uint32_t num);
+ int (*bank_to_soc_pa)(struct ras_core_context *ras_core,
+ struct umc_bank_addr bank_addr, uint64_t *soc_pa);
+ int (*soc_pa_to_bank)(struct ras_core_context *ras_core,
+ uint64_t soc_pa, struct umc_bank_addr *bank_addr);
+};
+
+struct eeprom_store_record {
+ /* point to data records array */
+ struct eeprom_umc_record *bps;
+ /* the count of entries */
+ int count;
+ /* the space can place new entries */
+ int space_left;
+};
+
+struct ras_umc_err_data {
+ struct eeprom_store_record rom_data;
+ struct eeprom_store_record ram_data;
+ enum umc_memory_partition_mode umc_nps_mode;
+ uint64_t last_retired_pfn;
+};
+
+struct ras_umc {
+ u32 umc_ip_version;
+ u32 umc_vram_type;
+ const struct ras_umc_ip_func *ip_func;
+ struct radix_tree_root root;
+ struct mutex tree_lock;
+ struct mutex umc_lock;
+ struct mutex bank_log_lock;
+ struct mutex pending_ecc_lock;
+ struct ras_umc_err_data umc_err_data;
+ struct list_head pending_ecc_list;
+};
+
+int ras_umc_sw_init(struct ras_core_context *ras);
+int ras_umc_sw_fini(struct ras_core_context *ras);
+int ras_umc_hw_init(struct ras_core_context *ras);
+int ras_umc_hw_fini(struct ras_core_context *ras);
+int ras_umc_psp_convert_ma_to_pa(struct ras_core_context *ras_core,
+ struct umc_mca_addr *in, struct umc_phy_addr *out,
+ uint32_t nps);
+int ras_umc_handle_bad_pages(struct ras_core_context *ras_core, void *data);
+int ras_umc_log_bad_bank(struct ras_core_context *ras, struct ras_bank_ecc *bank);
+int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_bank_ecc *bank);
+int ras_umc_log_pending_bad_bank(struct ras_core_context *ras_core);
+int ras_umc_clear_logged_ecc(struct ras_core_context *ras_core);
+int ras_umc_load_bad_pages(struct ras_core_context *ras_core);
+int ras_umc_get_saved_eeprom_count(struct ras_core_context *ras_core);
+int ras_umc_clean_badpage_data(struct ras_core_context *ras_core);
+int ras_umc_fill_eeprom_record(struct ras_core_context *ras_core,
+ uint64_t err_addr, uint32_t umc_inst, struct umc_phy_addr *cur_nps_addr,
+ enum umc_memory_partition_mode cur_nps, struct eeprom_umc_record *record);
+
+int ras_umc_get_badpage_count(struct ras_core_context *ras_core);
+int ras_umc_get_badpage_record(struct ras_core_context *ras_core, uint32_t index, void *record);
+bool ras_umc_check_retired_addr(struct ras_core_context *ras_core, uint64_t addr);
+int ras_umc_translate_soc_pa_and_bank(struct ras_core_context *ras_core,
+ uint64_t *soc_pa, struct umc_bank_addr *bank_addr, bool bank_to_pa);
+#endif
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c
new file mode 100644
index 000000000000..5d9a11c17a86
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.c
@@ -0,0 +1,511 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2025 Advanced Micro Devices, 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 "ras.h"
+#include "ras_umc.h"
+#include "ras_core_status.h"
+#include "ras_umc_v12_0.h"
+
+#define NumDieInterleaved 4
+
+static const uint32_t umc_v12_0_channel_idx_tbl[]
+ [UMC_V12_0_UMC_INSTANCE_NUM][UMC_V12_0_CHANNEL_INSTANCE_NUM] = {
+ {{3, 7, 11, 15, 2, 6, 10, 14}, {1, 5, 9, 13, 0, 4, 8, 12},
+ {19, 23, 27, 31, 18, 22, 26, 30}, {17, 21, 25, 29, 16, 20, 24, 28}},
+ {{47, 43, 39, 35, 46, 42, 38, 34}, {45, 41, 37, 33, 44, 40, 36, 32},
+ {63, 59, 55, 51, 62, 58, 54, 50}, {61, 57, 53, 49, 60, 56, 52, 48}},
+ {{79, 75, 71, 67, 78, 74, 70, 66}, {77, 73, 69, 65, 76, 72, 68, 64},
+ {95, 91, 87, 83, 94, 90, 86, 82}, {93, 89, 85, 81, 92, 88, 84, 80}},
+ {{99, 103, 107, 111, 98, 102, 106, 110}, {97, 101, 105, 109, 96, 100, 104, 108},
+ {115, 119, 123, 127, 114, 118, 122, 126}, {113, 117, 121, 125, 112, 116, 120, 124}}
+};
+
+/* mapping of MCA error address to normalized address */
+static const uint32_t umc_v12_0_ma2na_mapping[] = {
+ 0, 5, 6, 8, 9, 14, 12, 13,
+ 10, 11, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28,
+ 24, 7, 29, 30,
+};
+
+static bool umc_v12_0_bit_wise_xor(uint32_t val)
+{
+ bool result = 0;
+ int i;
+
+ for (i = 0; i < 32; i++)
+ result = result ^ ((val >> i) & 0x1);
+
+ return result;
+}
+
+static void __get_nps_pa_flip_bits(struct ras_core_context *ras_core,
+ enum umc_memory_partition_mode nps,
+ struct umc_flip_bits *flip_bits)
+{
+ uint32_t vram_type = ras_core->ras_umc.umc_vram_type;
+
+ /* default setting */
+ flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_C2_BIT;
+ flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_C3_BIT;
+ flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_C4_BIT;
+ flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R13_BIT;
+ flip_bits->flip_row_bit = 13;
+ flip_bits->bit_num = 4;
+ flip_bits->r13_in_pa = UMC_V12_0_PA_R13_BIT;
+
+ if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) {
+ flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_CH5_BIT;
+ flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_C2_BIT;
+ flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_B1_BIT;
+ flip_bits->r13_in_pa = UMC_V12_0_PA_R12_BIT;
+ } else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) {
+ flip_bits->flip_bits_in_pa[0] = UMC_V12_0_PA_CH4_BIT;
+ flip_bits->flip_bits_in_pa[1] = UMC_V12_0_PA_CH5_BIT;
+ flip_bits->flip_bits_in_pa[2] = UMC_V12_0_PA_B0_BIT;
+ flip_bits->r13_in_pa = UMC_V12_0_PA_R11_BIT;
+ }
+
+ switch (vram_type) {
+ case UMC_VRAM_TYPE_HBM:
+ /* other nps modes are taken as nps1 */
+ if (nps == UMC_MEMORY_PARTITION_MODE_NPS2)
+ flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R12_BIT;
+ else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4)
+ flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT;
+
+ break;
+ case UMC_VRAM_TYPE_HBM3E:
+ flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R12_BIT;
+ flip_bits->flip_row_bit = 12;
+
+ if (nps == UMC_MEMORY_PARTITION_MODE_NPS2)
+ flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R11_BIT;
+ else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4)
+ flip_bits->flip_bits_in_pa[3] = UMC_V12_0_PA_R10_BIT;
+
+ break;
+ default:
+ RAS_DEV_WARN(ras_core->dev,
+ "Unknown HBM type, set RAS retire flip bits to the value in NPS1 mode.\n");
+ break;
+ }
+}
+
+static uint64_t convert_nps_pa_to_row_pa(struct ras_core_context *ras_core,
+ uint64_t pa, enum umc_memory_partition_mode nps, bool zero_pfn_ok)
+{
+ struct umc_flip_bits flip_bits = {0};
+ uint64_t row_pa;
+ int i;
+
+ __get_nps_pa_flip_bits(ras_core, nps, &flip_bits);
+
+ row_pa = pa;
+ /* clear loop bits in soc physical address */
+ for (i = 0; i < flip_bits.bit_num; i++)
+ row_pa &= ~BIT_ULL(flip_bits.flip_bits_in_pa[i]);
+
+ if (!zero_pfn_ok && !RAS_ADDR_TO_PFN(row_pa))
+ row_pa |= BIT_ULL(flip_bits.flip_bits_in_pa[2]);
+
+ return row_pa;
+}
+
+static int lookup_bad_pages_in_a_row(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, uint32_t nps,
+ uint64_t *pfns, uint32_t num,
+ uint64_t seq_no, bool dump)
+{
+ uint32_t col, col_lower, row, row_lower, idx, row_high;
+ uint64_t soc_pa, row_pa, column, err_addr;
+ uint64_t retired_addr = RAS_PFN_TO_ADDR(record->cur_nps_retired_row_pfn);
+ struct umc_flip_bits flip_bits = {0};
+ uint32_t retire_unit;
+ uint32_t i;
+
+ __get_nps_pa_flip_bits(ras_core, nps, &flip_bits);
+
+ row_pa = convert_nps_pa_to_row_pa(ras_core, retired_addr, nps, true);
+
+ err_addr = record->address;
+ /* get column bit 0 and 1 in mca address */
+ col_lower = (err_addr >> 1) & 0x3ULL;
+ /* MA_R13_BIT will be handled later */
+ row_lower = (err_addr >> UMC_V12_0_MCA_R0_BIT) & 0x1fffULL;
+ row_lower &= ~BIT_ULL(flip_bits.flip_row_bit);
+
+ if (ras_core->ras_gfx.gfx_ip_version >= IP_VERSION(9, 5, 0)) {
+ row_high = (row_pa >> flip_bits.r13_in_pa) & 0x3ULL;
+ /* it's 2.25GB in each channel, from MCA address to PA
+ * [R14 R13] is converted if the two bits value are 0x3,
+ * get them from PA instead of MCA address.
+ */
+ row_lower |= (row_high << 13);
+ }
+
+ idx = 0;
+ row = 0;
+ retire_unit = 0x1 << flip_bits.bit_num;
+ /* loop for all possibilities of retire bits */
+ for (column = 0; column < retire_unit; column++) {
+ soc_pa = row_pa;
+ for (i = 0; i < flip_bits.bit_num; i++)
+ soc_pa |= (((column >> i) & 0x1ULL) << flip_bits.flip_bits_in_pa[i]);
+
+ col = ((column & 0x7) << 2) | col_lower;
+
+ /* add row bit 13 */
+ if (flip_bits.bit_num == UMC_PA_FLIP_BITS_NUM)
+ row = ((column >> 3) << flip_bits.flip_row_bit) | row_lower;
+
+ if (dump)
+ RAS_DEV_INFO(ras_core->dev,
+ "{%llu} Error Address(PA):0x%-10llx Row:0x%-4x Col:0x%-2x Bank:0x%x Channel:0x%x\n",
+ seq_no, soc_pa, row, col,
+ record->cur_nps_bank, record->mem_channel);
+
+
+ if (pfns && (idx < num))
+ pfns[idx++] = RAS_ADDR_TO_PFN(soc_pa);
+ }
+
+ return idx;
+}
+
+static int umc_v12_convert_ma_to_pa(struct ras_core_context *ras_core,
+ struct umc_mca_addr *addr_in, struct umc_phy_addr *addr_out,
+ uint32_t nps)
+{
+ uint32_t i, na_shift;
+ uint64_t soc_pa, na, na_nps;
+ uint32_t bank_hash0, bank_hash1, bank_hash2, bank_hash3, col, row;
+ uint32_t bank0, bank1, bank2, bank3, bank;
+ uint32_t ch_inst = addr_in->ch_inst;
+ uint32_t umc_inst = addr_in->umc_inst;
+ uint32_t node_inst = addr_in->node_inst;
+ uint32_t socket_id = addr_in->socket_id;
+ uint32_t channel_index;
+ uint64_t err_addr = addr_in->err_addr;
+
+ if (node_inst != UMC_INV_AID_NODE) {
+ if (ch_inst >= UMC_V12_0_CHANNEL_INSTANCE_NUM ||
+ umc_inst >= UMC_V12_0_UMC_INSTANCE_NUM ||
+ node_inst >= UMC_V12_0_AID_NUM_MAX ||
+ socket_id >= UMC_V12_0_SOCKET_NUM_MAX)
+ return -EINVAL;
+ } else {
+ if (socket_id >= UMC_V12_0_SOCKET_NUM_MAX ||
+ ch_inst >= UMC_V12_0_TOTAL_CHANNEL_NUM)
+ return -EINVAL;
+ }
+
+ bank_hash0 = (err_addr >> UMC_V12_0_MCA_B0_BIT) & 0x1ULL;
+ bank_hash1 = (err_addr >> UMC_V12_0_MCA_B1_BIT) & 0x1ULL;
+ bank_hash2 = (err_addr >> UMC_V12_0_MCA_B2_BIT) & 0x1ULL;
+ bank_hash3 = (err_addr >> UMC_V12_0_MCA_B3_BIT) & 0x1ULL;
+ col = (err_addr >> 1) & 0x1fULL;
+ row = (err_addr >> 10) & 0x3fffULL;
+
+ /* apply bank hash algorithm */
+ bank0 =
+ bank_hash0 ^ (UMC_V12_0_XOR_EN0 &
+ (umc_v12_0_bit_wise_xor(col & UMC_V12_0_COL_XOR0) ^
+ (umc_v12_0_bit_wise_xor(row & UMC_V12_0_ROW_XOR0))));
+ bank1 =
+ bank_hash1 ^ (UMC_V12_0_XOR_EN1 &
+ (umc_v12_0_bit_wise_xor(col & UMC_V12_0_COL_XOR1) ^
+ (umc_v12_0_bit_wise_xor(row & UMC_V12_0_ROW_XOR1))));
+ bank2 =
+ bank_hash2 ^ (UMC_V12_0_XOR_EN2 &
+ (umc_v12_0_bit_wise_xor(col & UMC_V12_0_COL_XOR2) ^
+ (umc_v12_0_bit_wise_xor(row & UMC_V12_0_ROW_XOR2))));
+ bank3 =
+ bank_hash3 ^ (UMC_V12_0_XOR_EN3 &
+ (umc_v12_0_bit_wise_xor(col & UMC_V12_0_COL_XOR3) ^
+ (umc_v12_0_bit_wise_xor(row & UMC_V12_0_ROW_XOR3))));
+
+ bank = bank0 | (bank1 << 1) | (bank2 << 2) | (bank3 << 3);
+ err_addr &= ~0x3c0ULL;
+ err_addr |= (bank << UMC_V12_0_MCA_B0_BIT);
+
+ na_nps = 0x0;
+ /* convert mca error address to normalized address */
+ for (i = 1; i < ARRAY_SIZE(umc_v12_0_ma2na_mapping); i++)
+ na_nps |= ((err_addr >> i) & 0x1ULL) << umc_v12_0_ma2na_mapping[i];
+
+ if (nps == UMC_MEMORY_PARTITION_MODE_NPS1)
+ na_shift = 8;
+ else if (nps == UMC_MEMORY_PARTITION_MODE_NPS2)
+ na_shift = 9;
+ else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4)
+ na_shift = 10;
+ else if (nps == UMC_MEMORY_PARTITION_MODE_NPS8)
+ na_shift = 11;
+ else
+ return -EINVAL;
+
+ na = ((na_nps >> na_shift) << 8) | (na_nps & 0xff);
+
+ if (node_inst != UMC_INV_AID_NODE)
+ channel_index =
+ umc_v12_0_channel_idx_tbl[node_inst][umc_inst][ch_inst];
+ else {
+ channel_index = ch_inst;
+ node_inst = channel_index /
+ (UMC_V12_0_UMC_INSTANCE_NUM * UMC_V12_0_CHANNEL_INSTANCE_NUM);
+ }
+
+ /* translate umc channel address to soc pa, 3 parts are included */
+ soc_pa = ADDR_OF_32KB_BLOCK(na) |
+ ADDR_OF_256B_BLOCK(channel_index) |
+ OFFSET_IN_256B_BLOCK(na);
+
+ /* calc channel hash based on absolute address */
+ soc_pa += socket_id * SOCKET_LFB_SIZE;
+ /* the umc channel bits are not original values, they are hashed */
+ UMC_V12_0_SET_CHANNEL_HASH(channel_index, soc_pa);
+ /* restore pa */
+ soc_pa -= socket_id * SOCKET_LFB_SIZE;
+
+ /* get some channel bits from na_nps directly and
+ * add nps section offset
+ */
+ if (nps == UMC_MEMORY_PARTITION_MODE_NPS2) {
+ soc_pa &= ~(0x1ULL << UMC_V12_0_PA_CH5_BIT);
+ soc_pa |= ((na_nps & 0x100) << 5);
+ soc_pa += (node_inst >> 1) * (SOCKET_LFB_SIZE >> 1);
+ } else if (nps == UMC_MEMORY_PARTITION_MODE_NPS4) {
+ soc_pa &= ~(0x3ULL << UMC_V12_0_PA_CH4_BIT);
+ soc_pa |= ((na_nps & 0x300) << 4);
+ soc_pa += node_inst * (SOCKET_LFB_SIZE >> 2);
+ } else if (nps == UMC_MEMORY_PARTITION_MODE_NPS8) {
+ soc_pa &= ~(0x7ULL << UMC_V12_0_PA_CH4_BIT);
+ soc_pa |= ((na_nps & 0x700) << 4);
+ soc_pa += node_inst * (SOCKET_LFB_SIZE >> 2) +
+ (channel_index >> 4) * (SOCKET_LFB_SIZE >> 3);
+ }
+
+ addr_out->pa = soc_pa;
+ addr_out->bank = bank;
+ addr_out->channel_idx = channel_index;
+
+ return 0;
+}
+
+static int convert_ma_to_pa(struct ras_core_context *ras_core,
+ struct umc_mca_addr *addr_in, struct umc_phy_addr *addr_out,
+ uint32_t nps)
+{
+ int ret;
+
+ if (ras_psp_check_supported_cmd(ras_core, RAS_TA_CMD_ID__QUERY_ADDRESS))
+ ret = ras_umc_psp_convert_ma_to_pa(ras_core,
+ addr_in, addr_out, nps);
+ else
+ ret = umc_v12_convert_ma_to_pa(ras_core,
+ addr_in, addr_out, nps);
+
+ return ret;
+}
+
+static int convert_bank_to_nps_addr(struct ras_core_context *ras_core,
+ struct ras_bank_ecc *bank, struct umc_phy_addr *pa_addr, uint32_t nps)
+{
+ struct umc_mca_addr addr_in;
+ struct umc_phy_addr addr_out;
+ int ret;
+
+ memset(&addr_in, 0, sizeof(addr_in));
+ memset(&addr_out, 0, sizeof(addr_out));
+
+ addr_in.err_addr = ACA_ADDR_2_ERR_ADDR(bank->addr);
+ addr_in.ch_inst = ACA_IPID_2_UMC_CH(bank->ipid);
+ addr_in.umc_inst = ACA_IPID_2_UMC_INST(bank->ipid);
+ addr_in.node_inst = ACA_IPID_2_DIE_ID(bank->ipid);
+ addr_in.socket_id = ACA_IPID_2_SOCKET_ID(bank->ipid);
+
+ ret = convert_ma_to_pa(ras_core, &addr_in, &addr_out, nps);
+ if (!ret) {
+ pa_addr->pa =
+ convert_nps_pa_to_row_pa(ras_core, addr_out.pa, nps, false);
+ pa_addr->channel_idx = addr_out.channel_idx;
+ pa_addr->bank = addr_out.bank;
+ }
+
+ return ret;
+}
+
+static int umc_v12_0_bank_to_eeprom_record(struct ras_core_context *ras_core,
+ struct ras_bank_ecc *bank, struct eeprom_umc_record *record)
+{
+ struct umc_phy_addr nps_addr;
+ int ret;
+
+ memset(&nps_addr, 0, sizeof(nps_addr));
+
+ ret = convert_bank_to_nps_addr(ras_core, bank,
+ &nps_addr, bank->nps);
+ if (ret)
+ return ret;
+
+ ras_umc_fill_eeprom_record(ras_core,
+ ACA_ADDR_2_ERR_ADDR(bank->addr), ACA_IPID_2_UMC_INST(bank->ipid),
+ &nps_addr, bank->nps, record);
+
+ lookup_bad_pages_in_a_row(ras_core, record,
+ bank->nps, NULL, 0, bank->seq_no, true);
+
+ return 0;
+}
+
+static int convert_eeprom_record_to_nps_addr(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, uint64_t *pa, uint32_t nps)
+{
+ struct device_system_info dev_info = {0};
+ struct umc_mca_addr addr_in;
+ struct umc_phy_addr addr_out;
+ int ret;
+
+ memset(&addr_in, 0, sizeof(addr_in));
+ memset(&addr_out, 0, sizeof(addr_out));
+
+ ras_core_get_device_system_info(ras_core, &dev_info);
+
+ addr_in.err_addr = record->address;
+ addr_in.ch_inst = record->mem_channel;
+ addr_in.umc_inst = record->mcumc_id;
+ addr_in.node_inst = UMC_INV_AID_NODE;
+ addr_in.socket_id = dev_info.socket_id;
+
+ ret = convert_ma_to_pa(ras_core, &addr_in, &addr_out, nps);
+ if (ret)
+ return ret;
+
+ *pa = convert_nps_pa_to_row_pa(ras_core, addr_out.pa, nps, false);
+
+ return 0;
+}
+
+static int umc_v12_0_eeprom_record_to_nps_record(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, uint32_t nps)
+{
+ uint64_t pa = 0;
+ int ret = 0;
+
+ if (nps == EEPROM_RECORD_UMC_NPS_MODE(record)) {
+ record->cur_nps_retired_row_pfn = EEPROM_RECORD_UMC_ADDR_PFN(record);
+ } else {
+ ret = convert_eeprom_record_to_nps_addr(ras_core,
+ record, &pa, nps);
+ if (!ret)
+ record->cur_nps_retired_row_pfn = RAS_ADDR_TO_PFN(pa);
+ }
+
+ record->cur_nps = nps;
+
+ return ret;
+}
+
+static int umc_v12_0_eeprom_record_to_nps_pages(struct ras_core_context *ras_core,
+ struct eeprom_umc_record *record, uint32_t nps,
+ uint64_t *pfns, uint32_t num)
+{
+ return lookup_bad_pages_in_a_row(ras_core,
+ record, nps, pfns, num, 0, false);
+}
+
+static int umc_12_0_soc_pa_to_bank(struct ras_core_context *ras_core,
+ uint64_t soc_pa,
+ struct umc_bank_addr *bank_addr)
+{
+
+ int channel_hashed = 0;
+ int channel_real = 0;
+ int channel_reversed = 0;
+ int i = 0;
+
+ bank_addr->stack_id = UMC_V12_0_SOC_PA_TO_SID(soc_pa);
+ bank_addr->bank_group = 0; /* This is a combination of SID & Bank. Needed?? */
+ bank_addr->bank = UMC_V12_0_SOC_PA_TO_BANK(soc_pa);
+ bank_addr->row = UMC_V12_0_SOC_PA_TO_ROW(soc_pa);
+ bank_addr->column = UMC_V12_0_SOC_PA_TO_COL(soc_pa);
+
+ /* Channel bits 4-6 are hashed. Bruteforce reverse the hash */
+ channel_hashed = (soc_pa >> UMC_V12_0_PA_CH4_BIT) & 0x7;
+
+ for (i = 0; i < 8; i++) {
+ channel_reversed = 0;
+ channel_reversed |= UMC_V12_0_CHANNEL_HASH_CH4((i << 4), soc_pa);
+ channel_reversed |= (UMC_V12_0_CHANNEL_HASH_CH5((i << 4), soc_pa) << 1);
+ channel_reversed |= (UMC_V12_0_CHANNEL_HASH_CH6((i << 4), soc_pa) << 2);
+ if (channel_reversed == channel_hashed)
+ channel_real = ((i << 4)) | ((soc_pa >> UMC_V12_0_PA_CH0_BIT) & 0xf);
+ }
+
+ bank_addr->channel = channel_real;
+ bank_addr->subchannel = UMC_V12_0_SOC_PA_TO_PC(soc_pa);
+
+ return 0;
+}
+
+static int umc_12_0_bank_to_soc_pa(struct ras_core_context *ras_core,
+ struct umc_bank_addr bank_addr,
+ uint64_t *soc_pa)
+{
+ uint64_t na = 0;
+ uint64_t tmp_pa = 0;
+ *soc_pa = 0;
+
+ tmp_pa |= UMC_V12_0_SOC_SID_TO_PA(bank_addr.stack_id);
+ tmp_pa |= UMC_V12_0_SOC_BANK_TO_PA(bank_addr.bank);
+ tmp_pa |= UMC_V12_0_SOC_ROW_TO_PA(bank_addr.row);
+ tmp_pa |= UMC_V12_0_SOC_COL_TO_PA(bank_addr.column);
+ tmp_pa |= UMC_V12_0_SOC_CH_TO_PA(bank_addr.channel);
+ tmp_pa |= UMC_V12_0_SOC_PC_TO_PA(bank_addr.subchannel);
+
+ /* Get the NA */
+ na = ((tmp_pa >> UMC_V12_0_PA_C2_BIT) << UMC_V12_0_NA_C2_BIT);
+ na |= tmp_pa & 0xff;
+
+ /* translate umc channel address to soc pa, 3 parts are included */
+ tmp_pa = ADDR_OF_32KB_BLOCK(na) |
+ ADDR_OF_256B_BLOCK(bank_addr.channel) |
+ OFFSET_IN_256B_BLOCK(na);
+
+ /* the umc channel bits are not original values, they are hashed */
+ UMC_V12_0_SET_CHANNEL_HASH(bank_addr.channel, tmp_pa);
+
+ *soc_pa = tmp_pa;
+
+ return 0;
+}
+
+const struct ras_umc_ip_func ras_umc_func_v12_0 = {
+ .bank_to_eeprom_record = umc_v12_0_bank_to_eeprom_record,
+ .eeprom_record_to_nps_record = umc_v12_0_eeprom_record_to_nps_record,
+ .eeprom_record_to_nps_pages = umc_v12_0_eeprom_record_to_nps_pages,
+ .bank_to_soc_pa = umc_12_0_bank_to_soc_pa,
+ .soc_pa_to_bank = umc_12_0_soc_pa_to_bank,
+};
+
diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h
new file mode 100644
index 000000000000..8a35ad856165
--- /dev/null
+++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h
@@ -0,0 +1,314 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2025 Advanced Micro Devices, 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.
+ *
+ */
+#ifndef __RAS_UMC_V12_0_H__
+#define __RAS_UMC_V12_0_H__
+#include "ras.h"
+
+/* MCA_UMC_UMC0_MCUMC_ADDRT0 */
+#define MCA_UMC_UMC0_MCUMC_ADDRT0__ErrorAddr__SHIFT 0x0
+#define MCA_UMC_UMC0_MCUMC_ADDRT0__Reserved__SHIFT 0x38
+#define MCA_UMC_UMC0_MCUMC_ADDRT0__ErrorAddr_MASK 0x00FFFFFFFFFFFFFFL
+#define MCA_UMC_UMC0_MCUMC_ADDRT0__Reserved_MASK 0xFF00000000000000L
+
+/* MCMP1_IPIDT0 */
+#define MCMP1_IPIDT0__InstanceIdLo__SHIFT 0x0
+#define MCMP1_IPIDT0__HardwareID__SHIFT 0x20
+#define MCMP1_IPIDT0__InstanceIdHi__SHIFT 0x2c
+#define MCMP1_IPIDT0__McaType__SHIFT 0x30
+
+#define MCMP1_IPIDT0__InstanceIdLo_MASK 0x00000000FFFFFFFFL
+#define MCMP1_IPIDT0__HardwareID_MASK 0x00000FFF00000000L
+#define MCMP1_IPIDT0__InstanceIdHi_MASK 0x0000F00000000000L
+#define MCMP1_IPIDT0__McaType_MASK 0xFFFF000000000000L
+
+/* number of umc channel instance with memory map register access */
+#define UMC_V12_0_CHANNEL_INSTANCE_NUM 8
+/* number of umc instance with memory map register access */
+#define UMC_V12_0_UMC_INSTANCE_NUM 4
+
+/* one piece of normalized address is mapped to 8 pieces of physical address */
+#define UMC_V12_0_NA_MAP_PA_NUM 8
+
+/* bank bits in MCA error address */
+#define UMC_V12_0_MCA_B0_BIT 6
+#define UMC_V12_0_MCA_B1_BIT 7
+#define UMC_V12_0_MCA_B2_BIT 8
+#define UMC_V12_0_MCA_B3_BIT 9
+
+/* row bits in MCA address */
+#define UMC_V12_0_MCA_R0_BIT 10
+
+/* Stack ID bits in SOC physical address */
+#define UMC_V12_0_PA_SID1_BIT 37
+#define UMC_V12_0_PA_SID0_BIT 36
+
+/* bank bits in SOC physical address */
+#define UMC_V12_0_PA_B3_BIT 18
+#define UMC_V12_0_PA_B2_BIT 17
+#define UMC_V12_0_PA_B1_BIT 20
+#define UMC_V12_0_PA_B0_BIT 19
+
+/* row bits in SOC physical address */
+#define UMC_V12_0_PA_R13_BIT 35
+#define UMC_V12_0_PA_R12_BIT 34
+#define UMC_V12_0_PA_R11_BIT 33
+#define UMC_V12_0_PA_R10_BIT 32
+#define UMC_V12_0_PA_R9_BIT 31
+#define UMC_V12_0_PA_R8_BIT 30
+#define UMC_V12_0_PA_R7_BIT 29
+#define UMC_V12_0_PA_R6_BIT 28
+#define UMC_V12_0_PA_R5_BIT 27
+#define UMC_V12_0_PA_R4_BIT 26
+#define UMC_V12_0_PA_R3_BIT 25
+#define UMC_V12_0_PA_R2_BIT 24
+#define UMC_V12_0_PA_R1_BIT 23
+#define UMC_V12_0_PA_R0_BIT 22
+
+/* column bits in SOC physical address */
+#define UMC_V12_0_PA_C4_BIT 21
+#define UMC_V12_0_PA_C3_BIT 16
+#define UMC_V12_0_PA_C2_BIT 15
+#define UMC_V12_0_PA_C1_BIT 6
+#define UMC_V12_0_PA_C0_BIT 5
+
+/* channel index bits in SOC physical address */
+#define UMC_V12_0_PA_CH6_BIT 14
+#define UMC_V12_0_PA_CH5_BIT 13
+#define UMC_V12_0_PA_CH4_BIT 12
+#define UMC_V12_0_PA_CH3_BIT 11
+#define UMC_V12_0_PA_CH2_BIT 10
+#define UMC_V12_0_PA_CH1_BIT 9
+#define UMC_V12_0_PA_CH0_BIT 8
+
+/* Pseudochannel index bits in SOC physical address */
+#define UMC_V12_0_PA_PC0_BIT 7
+
+#define UMC_V12_0_NA_C2_BIT 8
+
+#define UMC_V12_0_SOC_PA_TO_SID(pa) \
+ ((((pa >> UMC_V12_0_PA_SID0_BIT) & 0x1ULL) << 0ULL) | \
+ (((pa >> UMC_V12_0_PA_SID1_BIT) & 0x1ULL) << 1ULL))
+
+#define UMC_V12_0_SOC_PA_TO_BANK(pa) \
+ ((((pa >> UMC_V12_0_PA_B0_BIT) & 0x1ULL) << 0ULL) | \
+ (((pa >> UMC_V12_0_PA_B1_BIT) & 0x1ULL) << 1ULL) | \
+ (((pa >> UMC_V12_0_PA_B2_BIT) & 0x1ULL) << 2ULL) | \
+ (((pa >> UMC_V12_0_PA_B3_BIT) & 0x1ULL) << 3ULL))
+
+#define UMC_V12_0_SOC_PA_TO_ROW(pa) \
+ ((((pa >> UMC_V12_0_PA_R0_BIT) & 0x1ULL) << 0ULL) | \
+ (((pa >> UMC_V12_0_PA_R1_BIT) & 0x1ULL) << 1ULL) | \
+ (((pa >> UMC_V12_0_PA_R2_BIT) & 0x1ULL) << 2ULL) | \
+ (((pa >> UMC_V12_0_PA_R3_BIT) & 0x1ULL) << 3ULL) | \
+ (((pa >> UMC_V12_0_PA_R4_BIT) & 0x1ULL) << 4ULL) | \
+ (((pa >> UMC_V12_0_PA_R5_BIT) & 0x1ULL) << 5ULL) | \
+ (((pa >> UMC_V12_0_PA_R6_BIT) & 0x1ULL) << 6ULL) | \
+ (((pa >> UMC_V12_0_PA_R7_BIT) & 0x1ULL) << 7ULL) | \
+ (((pa >> UMC_V12_0_PA_R8_BIT) & 0x1ULL) << 8ULL) | \
+ (((pa >> UMC_V12_0_PA_R9_BIT) & 0x1ULL) << 9ULL) | \
+ (((pa >> UMC_V12_0_PA_R10_BIT) & 0x1ULL) << 10ULL) | \
+ (((pa >> UMC_V12_0_PA_R11_BIT) & 0x1ULL) << 11ULL) | \
+ (((pa >> UMC_V12_0_PA_R12_BIT) & 0x1ULL) << 12ULL) | \
+ (((pa >> UMC_V12_0_PA_R13_BIT) & 0x1ULL) << 13ULL))
+
+#define UMC_V12_0_SOC_PA_TO_COL(pa) \
+ ((((pa >> UMC_V12_0_PA_C0_BIT) & 0x1ULL) << 0ULL) | \
+ (((pa >> UMC_V12_0_PA_C1_BIT) & 0x1ULL) << 1ULL) | \
+ (((pa >> UMC_V12_0_PA_C2_BIT) & 0x1ULL) << 2ULL) | \
+ (((pa >> UMC_V12_0_PA_C3_BIT) & 0x1ULL) << 3ULL) | \
+ (((pa >> UMC_V12_0_PA_C4_BIT) & 0x1ULL) << 4ULL))
+
+#define UMC_V12_0_SOC_PA_TO_CH(pa) \
+ ((((pa >> UMC_V12_0_PA_CH0_BIT) & 0x1ULL) << 0ULL) | \
+ (((pa >> UMC_V12_0_PA_CH1_BIT) & 0x1ULL) << 1ULL) | \
+ (((pa >> UMC_V12_0_PA_CH2_BIT) & 0x1ULL) << 2ULL) | \
+ (((pa >> UMC_V12_0_PA_CH3_BIT) & 0x1ULL) << 3ULL) | \
+ (((pa >> UMC_V12_0_PA_CH4_BIT) & 0x1ULL) << 4ULL) | \
+ (((pa >> UMC_V12_0_PA_CH5_BIT) & 0x1ULL) << 5ULL) | \
+ (((pa >> UMC_V12_0_PA_CH6_BIT) & 0x1ULL) << 6ULL))
+
+#define UMC_V12_0_SOC_PA_TO_PC(pa) (((pa >> UMC_V12_0_PA_PC0_BIT) & 0x1ULL) << 0ULL)
+
+#define UMC_V12_0_SOC_SID_TO_PA(sid) \
+ ((((sid >> 0ULL) & 0x1ULL) << UMC_V12_0_PA_SID0_BIT) | \
+ (((sid >> 1ULL) & 0x1ULL) << UMC_V12_0_PA_SID1_BIT))
+
+#define UMC_V12_0_SOC_BANK_TO_PA(bank) \
+ ((((bank >> 0ULL) & 0x1ULL) << UMC_V12_0_PA_B0_BIT) | \
+ (((bank >> 1ULL) & 0x1ULL) << UMC_V12_0_PA_B1_BIT) | \
+ (((bank >> 2ULL) & 0x1ULL) << UMC_V12_0_PA_B2_BIT) | \
+ (((bank >> 3ULL) & 0x1ULL) << UMC_V12_0_PA_B3_BIT))
+
+#define UMC_V12_0_SOC_ROW_TO_PA(row) \
+ ((((row >> 0ULL) & 0x1ULL) << UMC_V12_0_PA_R0_BIT) | \
+ (((row >> 1ULL) & 0x1ULL) << UMC_V12_0_PA_R1_BIT) | \
+ (((row >> 2ULL) & 0x1ULL) << UMC_V12_0_PA_R2_BIT) | \
+ (((row >> 3ULL) & 0x1ULL) << UMC_V12_0_PA_R3_BIT) | \
+ (((row >> 4ULL) & 0x1ULL) << UMC_V12_0_PA_R4_BIT) | \
+ (((row >> 5ULL) & 0x1ULL) << UMC_V12_0_PA_R5_BIT) | \
+ (((row >> 6ULL) & 0x1ULL) << UMC_V12_0_PA_R6_BIT) | \
+ (((row >> 7ULL) & 0x1ULL) << UMC_V12_0_PA_R7_BIT) | \
+ (((row >> 8ULL) & 0x1ULL) << UMC_V12_0_PA_R8_BIT) | \
+ (((row >> 9ULL) & 0x1ULL) << UMC_V12_0_PA_R9_BIT) | \
+ (((row >> 10ULL) & 0x1ULL) << UMC_V12_0_PA_R10_BIT) | \
+ (((row >> 11ULL) & 0x1ULL) << UMC_V12_0_PA_R11_BIT) | \
+ (((row >> 12ULL) & 0x1ULL) << UMC_V12_0_PA_R12_BIT) | \
+ (((row >> 13ULL) & 0x1ULL) << UMC_V12_0_PA_R13_BIT))
+
+#define UMC_V12_0_SOC_COL_TO_PA(col) \
+ ((((col >> 0ULL) & 0x1ULL) << UMC_V12_0_PA_C0_BIT) | \
+ (((col >> 1ULL) & 0x1ULL) << UMC_V12_0_PA_C1_BIT) | \
+ (((col >> 2ULL) & 0x1ULL) << UMC_V12_0_PA_C2_BIT) | \
+ (((col >> 3ULL) & 0x1ULL) << UMC_V12_0_PA_C3_BIT) | \
+ (((col >> 4ULL) & 0x1ULL) << UMC_V12_0_PA_C4_BIT))
+
+#define UMC_V12_0_SOC_CH_TO_PA(ch) \
+ ((((ch >> 0ULL) & 0x1ULL) << UMC_V12_0_PA_CH0_BIT) | \
+ (((ch >> 1ULL) & 0x1ULL) << UMC_V12_0_PA_CH1_BIT) | \
+ (((ch >> 2ULL) & 0x1ULL) << UMC_V12_0_PA_CH2_BIT) | \
+ (((ch >> 3ULL) & 0x1ULL) << UMC_V12_0_PA_CH3_BIT) | \
+ (((ch >> 4ULL) & 0x1ULL) << UMC_V12_0_PA_CH4_BIT) | \
+ (((ch >> 5ULL) & 0x1ULL) << UMC_V12_0_PA_CH5_BIT) | \
+ (((ch >> 6ULL) & 0x1ULL) << UMC_V12_0_PA_CH6_BIT))
+
+#define UMC_V12_0_SOC_PC_TO_PA(pc) (((pc >> 0ULL) & 0x1ULL) << UMC_V12_0_PA_PC0_BIT)
+
+/* bank hash settings */
+#define UMC_V12_0_XOR_EN0 1
+#define UMC_V12_0_XOR_EN1 1
+#define UMC_V12_0_XOR_EN2 1
+#define UMC_V12_0_XOR_EN3 1
+#define UMC_V12_0_COL_XOR0 0x0
+#define UMC_V12_0_COL_XOR1 0x0
+#define UMC_V12_0_COL_XOR2 0x800
+#define UMC_V12_0_COL_XOR3 0x1000
+#define UMC_V12_0_ROW_XOR0 0x11111
+#define UMC_V12_0_ROW_XOR1 0x22222
+#define UMC_V12_0_ROW_XOR2 0x4444
+#define UMC_V12_0_ROW_XOR3 0x8888
+
+/* channel hash settings */
+#define UMC_V12_0_HASH_4K 0
+#define UMC_V12_0_HASH_64K 1
+#define UMC_V12_0_HASH_2M 1
+#define UMC_V12_0_HASH_1G 1
+#define UMC_V12_0_HASH_1T 1
+
+/* XOR some bits of PA into CH4~CH6 bits (bits 12~14 of PA),
+ * hash bit is only effective when related setting is enabled
+ */
+#define UMC_V12_0_CHANNEL_HASH_CH4(channel_idx, pa) ((((channel_idx) >> 5) & 0x1) ^ \
+ (((pa) >> 20) & 0x1ULL & UMC_V12_0_HASH_64K) ^ \
+ (((pa) >> 27) & 0x1ULL & UMC_V12_0_HASH_2M) ^ \
+ (((pa) >> 34) & 0x1ULL & UMC_V12_0_HASH_1G) ^ \
+ (((pa) >> 41) & 0x1ULL & UMC_V12_0_HASH_1T))
+#define UMC_V12_0_CHANNEL_HASH_CH5(channel_idx, pa) ((((channel_idx) >> 6) & 0x1) ^ \
+ (((pa) >> 21) & 0x1ULL & UMC_V12_0_HASH_64K) ^ \
+ (((pa) >> 28) & 0x1ULL & UMC_V12_0_HASH_2M) ^ \
+ (((pa) >> 35) & 0x1ULL & UMC_V12_0_HASH_1G) ^ \
+ (((pa) >> 42) & 0x1ULL & UMC_V12_0_HASH_1T))
+#define UMC_V12_0_CHANNEL_HASH_CH6(channel_idx, pa) ((((channel_idx) >> 4) & 0x1) ^ \
+ (((pa) >> 19) & 0x1ULL & UMC_V12_0_HASH_64K) ^ \
+ (((pa) >> 26) & 0x1ULL & UMC_V12_0_HASH_2M) ^ \
+ (((pa) >> 33) & 0x1ULL & UMC_V12_0_HASH_1G) ^ \
+ (((pa) >> 40) & 0x1ULL & UMC_V12_0_HASH_1T) ^ \
+ (((pa) >> 47) & 0x1ULL & UMC_V12_0_HASH_1T))
+#define UMC_V12_0_SET_CHANNEL_HASH(channel_idx, pa) do { \
+ (pa) &= ~(0x7ULL << UMC_V12_0_PA_CH4_BIT); \
+ (pa) |= (UMC_V12_0_CHANNEL_HASH_CH4(channel_idx, pa) << UMC_V12_0_PA_CH4_BIT); \
+ (pa) |= (UMC_V12_0_CHANNEL_HASH_CH5(channel_idx, pa) << UMC_V12_0_PA_CH5_BIT); \
+ (pa) |= (UMC_V12_0_CHANNEL_HASH_CH6(channel_idx, pa) << UMC_V12_0_PA_CH6_BIT); \
+ } while (0)
+
+
+/*
+ * (addr / 256) * 4096, the higher 26 bits in ErrorAddr
+ * is the index of 4KB block
+ */
+#define ADDR_OF_4KB_BLOCK(addr) (((addr) & ~0xffULL) << 4)
+/*
+ * (addr / 256) * 8192, the higher 26 bits in ErrorAddr
+ * is the index of 8KB block
+ */
+#define ADDR_OF_8KB_BLOCK(addr) (((addr) & ~0xffULL) << 5)
+/*
+ * (addr / 256) * 32768, the higher 26 bits in ErrorAddr
+ * is the index of 8KB block
+ */
+#define ADDR_OF_32KB_BLOCK(addr) (((addr) & ~0xffULL) << 7)
+/* channel index is the index of 256B block */
+#define ADDR_OF_256B_BLOCK(channel_index) ((channel_index) << 8)
+/* offset in 256B block */
+#define OFFSET_IN_256B_BLOCK(addr) ((addr) & 0xffULL)
+
+
+#define UMC_V12_ADDR_MASK_BAD_COLS(addr) \
+ ((addr) & ~((0x3ULL << UMC_V12_0_PA_C2_BIT) | \
+ (0x1ULL << UMC_V12_0_PA_C4_BIT) | \
+ (0x1ULL << UMC_V12_0_PA_R13_BIT)))
+
+#define ACA_IPID_HI_2_UMC_AID(_ipid_hi) (((_ipid_hi) >> 2) & 0x3)
+#define ACA_IPID_LO_2_UMC_CH(_ipid_lo) \
+ (((((_ipid_lo) >> 20) & 0x1) * 4) + (((_ipid_lo) >> 12) & 0xF))
+#define ACA_IPID_LO_2_UMC_INST(_ipid_lo) (((_ipid_lo) >> 21) & 0x7)
+
+#define ACA_IPID_2_DIE_ID(ipid) ((REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdHi) >> 2) & 0x03)
+#define ACA_IPID_2_UMC_CH(ipid) \
+ (ACA_IPID_LO_2_UMC_CH(REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdLo)))
+
+#define ACA_IPID_2_UMC_INST(ipid) \
+ (ACA_IPID_LO_2_UMC_INST(REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdLo)))
+
+#define ACA_IPID_2_SOCKET_ID(ipid) \
+ (((REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdLo) & 0x1) << 2) | \
+ (REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdHi) & 0x03))
+
+#define ACA_ADDR_2_ERR_ADDR(addr) \
+ REG_GET_FIELD(addr, MCA_UMC_UMC0_MCUMC_ADDRT0, ErrorAddr)
+
+/* R13 bit shift should be considered, double the number */
+#define UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL (UMC_V12_0_NA_MAP_PA_NUM * 2)
+
+
+/* C2, C3, C4, R13, four MCA bits are looped in page retirement */
+#define UMC_V12_0_RETIRE_LOOP_BITS 4
+
+/* invalid node instance value */
+#define UMC_INV_AID_NODE 0xffff
+
+#define UMC_V12_0_AID_NUM_MAX 4
+#define UMC_V12_0_SOCKET_NUM_MAX 8
+
+#define UMC_V12_0_TOTAL_CHANNEL_NUM \
+ (UMC_V12_0_AID_NUM_MAX * UMC_V12_0_UMC_INSTANCE_NUM * UMC_V12_0_CHANNEL_INSTANCE_NUM)
+
+/* one device has 192GB HBM */
+#define SOCKET_LFB_SIZE 0x3000000000ULL
+
+extern const struct ras_umc_ip_func ras_umc_func_v12_0;
+
+int ras_umc_get_badpage_count(struct ras_core_context *ras_core);
+int ras_umc_get_badpage_record(struct ras_core_context *ras_core, uint32_t index, void *record);
+#endif
+
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
index 2ad33559a33a..5a66948ffd24 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c
@@ -111,6 +111,7 @@ komeda_crtc_atomic_check(struct drm_crtc *crtc,
static int
komeda_crtc_prepare(struct komeda_crtc *kcrtc)
{
+ struct drm_device *drm = kcrtc->base.dev;
struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
struct komeda_pipeline *master = kcrtc->master;
struct komeda_crtc_state *kcrtc_st = to_kcrtc_st(kcrtc->base.state);
@@ -128,8 +129,8 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc)
err = mdev->funcs->change_opmode(mdev, new_mode);
if (err) {
- DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
- mdev->dpmode, new_mode);
+ drm_err(drm, "failed to change opmode: 0x%x -> 0x%x.\n,",
+ mdev->dpmode, new_mode);
goto unlock;
}
@@ -142,18 +143,18 @@ komeda_crtc_prepare(struct komeda_crtc *kcrtc)
if (new_mode != KOMEDA_MODE_DUAL_DISP) {
err = clk_set_rate(mdev->aclk, komeda_crtc_get_aclk(kcrtc_st));
if (err)
- DRM_ERROR("failed to set aclk.\n");
+ drm_err(drm, "failed to set aclk.\n");
err = clk_prepare_enable(mdev->aclk);
if (err)
- DRM_ERROR("failed to enable aclk.\n");
+ drm_err(drm, "failed to enable aclk.\n");
}
err = clk_set_rate(master->pxlclk, mode->crtc_clock * 1000);
if (err)
- DRM_ERROR("failed to set pxlclk for pipe%d\n", master->id);
+ drm_err(drm, "failed to set pxlclk for pipe%d\n", master->id);
err = clk_prepare_enable(master->pxlclk);
if (err)
- DRM_ERROR("failed to enable pxl clk for pipe%d.\n", master->id);
+ drm_err(drm, "failed to enable pxl clk for pipe%d.\n", master->id);
unlock:
mutex_unlock(&mdev->lock);
@@ -164,6 +165,7 @@ unlock:
static int
komeda_crtc_unprepare(struct komeda_crtc *kcrtc)
{
+ struct drm_device *drm = kcrtc->base.dev;
struct komeda_dev *mdev = kcrtc->base.dev->dev_private;
struct komeda_pipeline *master = kcrtc->master;
u32 new_mode;
@@ -180,8 +182,8 @@ komeda_crtc_unprepare(struct komeda_crtc *kcrtc)
err = mdev->funcs->change_opmode(mdev, new_mode);
if (err) {
- DRM_ERROR("failed to change opmode: 0x%x -> 0x%x.\n,",
- mdev->dpmode, new_mode);
+ drm_err(drm, "failed to change opmode: 0x%x -> 0x%x.\n,",
+ mdev->dpmode, new_mode);
goto unlock;
}
@@ -200,6 +202,7 @@ unlock:
void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
struct komeda_events *evts)
{
+ struct drm_device *drm = kcrtc->base.dev;
struct drm_crtc *crtc = &kcrtc->base;
u32 events = evts->pipes[kcrtc->master->id];
@@ -212,7 +215,7 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
if (wb_conn)
drm_writeback_signal_completion(&wb_conn->base, 0);
else
- DRM_WARN("CRTC[%d]: EOW happen but no wb_connector.\n",
+ drm_warn(drm, "CRTC[%d]: EOW happen but no wb_connector.\n",
drm_crtc_index(&kcrtc->base));
}
/* will handle it together with the write back support */
@@ -236,7 +239,7 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc,
crtc->state->event = NULL;
drm_crtc_send_vblank_event(crtc, event);
} else {
- DRM_WARN("CRTC[%d]: FLIP happened but no pending commit.\n",
+ drm_warn(drm, "CRTC[%d]: FLIP happened but no pending commit.\n",
drm_crtc_index(&kcrtc->base));
}
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
@@ -309,7 +312,7 @@ komeda_crtc_flush_and_wait_for_flip_done(struct komeda_crtc *kcrtc,
/* wait the flip take affect.*/
if (wait_for_completion_timeout(flip_done, HZ) == 0) {
- DRM_ERROR("wait pipe%d flip done timeout\n", kcrtc->master->id);
+ drm_err(drm, "wait pipe%d flip done timeout\n", kcrtc->master->id);
if (!input_flip_done) {
unsigned long flags;
@@ -562,6 +565,7 @@ static const struct drm_crtc_funcs komeda_crtc_funcs = {
int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
struct komeda_dev *mdev)
{
+ struct drm_device *drm = &kms->base;
struct komeda_crtc *crtc;
struct komeda_pipeline *master;
char str[16];
@@ -581,7 +585,7 @@ int komeda_kms_setup_crtcs(struct komeda_kms_dev *kms,
else
sprintf(str, "None");
- DRM_INFO("CRTC-%d: master(pipe-%d) slave(%s).\n",
+ drm_info(drm, "CRTC-%d: master(pipe-%d) slave(%s).\n",
kms->n_crtcs, master->id, str);
kms->n_crtcs++;
@@ -613,6 +617,7 @@ static int komeda_attach_bridge(struct device *dev,
struct komeda_pipeline *pipe,
struct drm_encoder *encoder)
{
+ struct drm_device *drm = encoder->dev;
struct drm_bridge *bridge;
int err;
@@ -624,7 +629,7 @@ static int komeda_attach_bridge(struct device *dev,
err = drm_bridge_attach(encoder, bridge, NULL, 0);
if (err)
- dev_err(dev, "bridge_attach() failed for pipe: %s\n",
+ drm_err(drm, "bridge_attach() failed for pipe: %s\n",
of_node_full_name(pipe->of_node));
return err;
diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
index 901f938aefe0..3ca461eb0a24 100644
--- a/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
+++ b/drivers/gpu/drm/arm/display/komeda/komeda_framebuffer.c
@@ -9,6 +9,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include "komeda_framebuffer.h"
#include "komeda_dev.h"
diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c
index 806da0aaedf7..4b4a08cb396d 100644
--- a/drivers/gpu/drm/arm/hdlcd_crtc.c
+++ b/drivers/gpu/drm/arm/hdlcd_crtc.c
@@ -22,6 +22,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
index c3179d74f3f5..81d45f2dd6a7 100644
--- a/drivers/gpu/drm/arm/hdlcd_drv.c
+++ b/drivers/gpu/drm/arm/hdlcd_drv.c
@@ -33,6 +33,7 @@
#include <drm/drm_modeset_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c
index bc5f5e9798c3..b765f6c9eea4 100644
--- a/drivers/gpu/drm/arm/malidp_drv.c
+++ b/drivers/gpu/drm/arm/malidp_drv.c
@@ -29,6 +29,7 @@
#include <drm/drm_modeset_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c
index 600af5ad81b1..47733c85d271 100644
--- a/drivers/gpu/drm/arm/malidp_mw.c
+++ b/drivers/gpu/drm/arm/malidp_mw.c
@@ -14,6 +14,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_writeback.h>
diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c
index 87f2e5ee8790..f1a5014bcfa1 100644
--- a/drivers/gpu/drm/arm/malidp_planes.c
+++ b/drivers/gpu/drm/arm/malidp_planes.c
@@ -263,7 +263,7 @@ static int malidp_se_check_scaling(struct malidp_plane *mp,
struct drm_plane_state *state)
{
struct drm_crtc_state *crtc_state =
- drm_atomic_get_existing_crtc_state(state->state, state->crtc);
+ drm_atomic_get_new_crtc_state(state->state, state->crtc);
struct malidp_crtc_state *mc;
u32 src_w, src_h;
int ret;
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 0900e4466ffb..033b19b31f63 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -13,6 +13,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/armada/armada_debugfs.c b/drivers/gpu/drm/armada/armada_debugfs.c
index a763349dd89f..2445365c823f 100644
--- a/drivers/gpu/drm/armada/armada_debugfs.c
+++ b/drivers/gpu/drm/armada/armada_debugfs.c
@@ -12,6 +12,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include "armada_crtc.h"
#include "armada_drm.h"
diff --git a/drivers/gpu/drm/armada/armada_fb.c b/drivers/gpu/drm/armada/armada_fb.c
index aa4289127086..77098928f821 100644
--- a/drivers/gpu/drm/armada/armada_fb.c
+++ b/drivers/gpu/drm/armada/armada_fb.c
@@ -6,6 +6,7 @@
#include <drm/drm_modeset_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include "armada_drm.h"
#include "armada_fb.h"
diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
index cb53cc91bafb..8bbae94804f8 100644
--- a/drivers/gpu/drm/armada/armada_fbdev.c
+++ b/drivers/gpu/drm/armada/armada_fbdev.c
@@ -13,6 +13,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include "armada_crtc.h"
#include "armada_drm.h"
@@ -28,8 +29,6 @@ static void armada_fbdev_fb_destroy(struct fb_info *info)
fbh->fb->funcs->destroy(fbh->fb);
drm_client_release(&fbh->client);
- drm_fb_helper_unprepare(fbh);
- kfree(fbh);
}
static const struct fb_ops armada_fb_ops = {
@@ -45,10 +44,10 @@ int armada_fbdev_driver_fbdev_probe(struct drm_fb_helper *fbh,
struct drm_fb_helper_surface_size *sizes)
{
struct drm_device *dev = fbh->dev;
+ struct fb_info *info = fbh->info;
struct drm_mode_fb_cmd2 mode;
struct armada_framebuffer *dfb;
struct armada_gem_object *obj;
- struct fb_info *info;
int size, ret;
void *ptr;
@@ -92,12 +91,6 @@ int armada_fbdev_driver_fbdev_probe(struct drm_fb_helper *fbh,
if (IS_ERR(dfb))
return PTR_ERR(dfb);
- info = drm_fb_helper_alloc_info(fbh);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto err_fballoc;
- }
-
info->fbops = &armada_fb_ops;
info->fix.smem_start = obj->phys_addr;
info->fix.smem_len = obj->obj.size;
@@ -113,8 +106,4 @@ int armada_fbdev_driver_fbdev_probe(struct drm_fb_helper *fbh,
(unsigned long long)obj->phys_addr);
return 0;
-
- err_fballoc:
- dfb->fb.funcs->destroy(&dfb->fb);
- return ret;
}
diff --git a/drivers/gpu/drm/armada/armada_gem.c b/drivers/gpu/drm/armada/armada_gem.c
index 1a1680d71486..35fcfa0d85ff 100644
--- a/drivers/gpu/drm/armada/armada_gem.c
+++ b/drivers/gpu/drm/armada/armada_gem.c
@@ -10,6 +10,7 @@
#include <drm/armada_drm.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include "armada_drm.h"
#include "armada_gem.h"
diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c
index 3b9bd8ecda13..21fd3b4ba10f 100644
--- a/drivers/gpu/drm/armada/armada_overlay.c
+++ b/drivers/gpu/drm/armada/armada_overlay.c
@@ -12,6 +12,7 @@
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_print.h>
#include "armada_crtc.h"
#include "armada_drm.h"
diff --git a/drivers/gpu/drm/armada/armada_plane.c b/drivers/gpu/drm/armada/armada_plane.c
index cc47c032dbc1..a0326b4f568e 100644
--- a/drivers/gpu/drm/armada/armada_plane.c
+++ b/drivers/gpu/drm/armada/armada_plane.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_print.h>
#include "armada_crtc.h"
#include "armada_drm.h"
@@ -94,12 +95,7 @@ int armada_drm_plane_atomic_check(struct drm_plane *plane,
return 0;
}
- if (state)
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- crtc);
- else
- crtc_state = crtc->state;
-
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
0,
INT_MAX, true, false);
diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile
index 2547613155da..cdbcba3b43ad 100644
--- a/drivers/gpu/drm/ast/Makefile
+++ b/drivers/gpu/drm/ast/Makefile
@@ -6,7 +6,9 @@
ast-y := \
ast_2000.o \
ast_2100.o \
+ ast_2200.o \
ast_2300.o \
+ ast_2400.o \
ast_2500.o \
ast_2600.o \
ast_cursor.o \
@@ -14,7 +16,6 @@ ast-y := \
ast_dp501.o \
ast_dp.o \
ast_drv.o \
- ast_main.o \
ast_mm.o \
ast_mode.o \
ast_post.o \
diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c
index 41c2aa1e425a..fa3bc23ce098 100644
--- a/drivers/gpu/drm/ast/ast_2000.c
+++ b/drivers/gpu/drm/ast/ast_2000.c
@@ -27,6 +27,9 @@
*/
#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
#include "ast_drv.h"
#include "ast_post.h"
@@ -147,3 +150,108 @@ int ast_2000_post(struct ast_device *ast)
return 0;
}
+
+/*
+ * Mode setting
+ */
+
+const struct ast_vbios_dclk_info ast_2000_dclk_table[] = {
+ {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */
+ {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
+ {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
+ {0x76, 0x63, 0x01}, /* 03: VCLK36 */
+ {0xee, 0x67, 0x01}, /* 04: VCLK40 */
+ {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
+ {0xc6, 0x64, 0x01}, /* 06: VCLK50 */
+ {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
+ {0x80, 0x64, 0x00}, /* 08: VCLK65 */
+ {0x7b, 0x63, 0x00}, /* 09: VCLK75 */
+ {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */
+ {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */
+ {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */
+ {0x85, 0x24, 0x00}, /* 0d: VCLK135 */
+ {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */
+ {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */
+ {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
+ {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */
+ {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
+ {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
+ {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
+ {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
+ {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
+ {0x77, 0x58, 0x80}, /* 17: VCLK119 */
+ {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
+ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
+ {0x3b, 0x2c, 0x81}, /* 1a: VCLK118_25 */
+};
+
+/*
+ * Device initialization
+ */
+
+void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post)
+{
+ enum ast_tx_chip tx_chip = AST_TX_NONE;
+ u8 vgacra3;
+
+ /*
+ * VGACRA3 Enhanced Color Mode Register, check if DVO is already
+ * enabled, in that case, assume we have a SIL164 TMDS transmitter
+ *
+ * Don't make that assumption if we the chip wasn't enabled and
+ * is at power-on reset, otherwise we'll incorrectly "detect" a
+ * SIL164 when there is none.
+ */
+ if (!need_post) {
+ vgacra3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xff);
+ if (vgacra3 & AST_IO_VGACRA3_DVO_ENABLED)
+ tx_chip = AST_TX_SIL164;
+ }
+
+ __ast_device_set_tx_chip(ast, tx_chip);
+}
+
+static const struct ast_device_quirks ast_2000_device_quirks = {
+ .crtc_mem_req_threshold_low = 31,
+ .crtc_mem_req_threshold_high = 47,
+};
+
+struct drm_device *ast_2000_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2000_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2000_detect_tx_chip(ast, need_post);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c
index 829e3b8b0d19..05aeb0624d41 100644
--- a/drivers/gpu/drm/ast/ast_2100.c
+++ b/drivers/gpu/drm/ast/ast_2100.c
@@ -27,6 +27,9 @@
*/
#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
#include "ast_drv.h"
#include "ast_post.h"
@@ -386,3 +389,92 @@ int ast_2100_post(struct ast_device *ast)
return 0;
}
+
+/*
+ * Widescreen detection
+ */
+
+/* Try to detect WSXGA+ on Gen2+ */
+bool __ast_2100_detect_wsxga_p(struct ast_device *ast)
+{
+ u8 vgacrd0 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd0);
+
+ if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_BY_BMC))
+ return true;
+ if (vgacrd0 & AST_IO_VGACRD0_IKVM_WIDESCREEN)
+ return true;
+
+ return false;
+}
+
+/* Try to detect WUXGA on Gen2+ */
+bool __ast_2100_detect_wuxga(struct ast_device *ast)
+{
+ u8 vgacrd1;
+
+ if (ast->support_fullhd) {
+ vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1);
+ if (!(vgacrd1 & AST_IO_VGACRD1_SUPPORTS_WUXGA))
+ return true;
+ }
+
+ return false;
+}
+
+static void ast_2100_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast)) {
+ ast->support_wsxga_p = true;
+ if (ast->chip == AST2100)
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2100_device_quirks = {
+ .crtc_mem_req_threshold_low = 47,
+ .crtc_mem_req_threshold_high = 63,
+};
+
+struct drm_device *ast_2100_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2100_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2000_detect_tx_chip(ast, need_post);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ast_2100_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2200.c b/drivers/gpu/drm/ast/ast_2200.c
new file mode 100644
index 000000000000..b64345d11ffa
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2200.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "ast_drv.h"
+
+static void ast_2200_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast)) {
+ ast->support_wsxga_p = true;
+ if (ast->chip == AST2200)
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2200_device_quirks = {
+ .crtc_mem_req_threshold_low = 47,
+ .crtc_mem_req_threshold_high = 63,
+};
+
+struct drm_device *ast_2200_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2200_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2000_detect_tx_chip(ast, need_post);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ast_2200_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
+
diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c
index dc2a32244689..5f50d9f91ffd 100644
--- a/drivers/gpu/drm/ast/ast_2300.c
+++ b/drivers/gpu/drm/ast/ast_2300.c
@@ -27,6 +27,12 @@
*/
#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "ast_drv.h"
#include "ast_post.h"
@@ -1326,3 +1332,132 @@ int ast_2300_post(struct ast_device *ast)
return 0;
}
+
+/*
+ * Device initialization
+ */
+
+void ast_2300_detect_tx_chip(struct ast_device *ast)
+{
+ enum ast_tx_chip tx_chip = AST_TX_NONE;
+ struct drm_device *dev = &ast->base;
+ u8 vgacrd1;
+
+ /*
+ * On AST GEN4+, look at the configuration set by the SoC in
+ * the SOC scratch register #1 bits 11:8 (interestingly marked
+ * as "reserved" in the spec)
+ */
+ vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1,
+ AST_IO_VGACRD1_TX_TYPE_MASK);
+ switch (vgacrd1) {
+ /*
+ * GEN4 to GEN6
+ */
+ case AST_IO_VGACRD1_TX_SIL164_VBIOS:
+ tx_chip = AST_TX_SIL164;
+ break;
+ case AST_IO_VGACRD1_TX_DP501_VBIOS:
+ ast->dp501_fw_addr = drmm_kzalloc(dev, SZ_32K, GFP_KERNEL);
+ if (ast->dp501_fw_addr) {
+ /* backup firmware */
+ if (ast_backup_fw(ast, ast->dp501_fw_addr, SZ_32K)) {
+ drmm_kfree(dev, ast->dp501_fw_addr);
+ ast->dp501_fw_addr = NULL;
+ }
+ }
+ fallthrough;
+ case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW:
+ tx_chip = AST_TX_DP501;
+ break;
+ /*
+ * GEN7+
+ */
+ case AST_IO_VGACRD1_TX_ASTDP:
+ tx_chip = AST_TX_ASTDP;
+ break;
+ /*
+ * Several of the listed TX chips are not explicitly supported
+ * by the ast driver. If these exist in real-world devices, they
+ * are most likely reported as VGA or SIL164 outputs. We warn here
+ * to get bug reports for these devices. If none come in for some
+ * time, we can begin to fail device probing on these values.
+ */
+ case AST_IO_VGACRD1_TX_ITE66121_VBIOS:
+ drm_warn(dev, "ITE IT66121 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
+ break;
+ case AST_IO_VGACRD1_TX_CH7003_VBIOS:
+ drm_warn(dev, "Chrontel CH7003 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
+ break;
+ case AST_IO_VGACRD1_TX_ANX9807_VBIOS:
+ drm_warn(dev, "Analogix ANX9807 detected, 0x%x, Gen%lu\n", vgacrd1, AST_GEN(ast));
+ break;
+ }
+
+ __ast_device_set_tx_chip(ast, tx_chip);
+}
+
+static void ast_2300_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1300) {
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2300_device_quirks = {
+ .crtc_mem_req_threshold_low = 96,
+ .crtc_mem_req_threshold_high = 120,
+};
+
+struct drm_device *ast_2300_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2300_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* map reserved buffer */
+ ast->dp501_fw_buf = NULL;
+ if (ast->vram_size < pci_resource_len(pdev, 0)) {
+ ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0);
+ if (!ast->dp501_fw_buf)
+ drm_info(dev, "failed to map reserved buffer!\n");
+ }
+
+ ast_2300_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2400.c b/drivers/gpu/drm/ast/ast_2400.c
new file mode 100644
index 000000000000..2e6befd24f91
--- /dev/null
+++ b/drivers/gpu/drm/ast/ast_2400.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2012 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
+
+#include "ast_drv.h"
+
+static void ast_2400_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST1400) {
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2400_device_quirks = {
+ .crtc_mem_req_threshold_low = 96,
+ .crtc_mem_req_threshold_high = 120,
+};
+
+struct drm_device *ast_2400_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2400_device_quirks);
+
+ ast->dclk_table = ast_2000_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* map reserved buffer */
+ ast->dp501_fw_buf = NULL;
+ if (ast->vram_size < pci_resource_len(pdev, 0)) {
+ ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0);
+ if (!ast->dp501_fw_buf)
+ drm_info(dev, "failed to map reserved buffer!\n");
+ }
+
+ ast_2400_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c
index 1e541498ea67..2a52af0ded56 100644
--- a/drivers/gpu/drm/ast/ast_2500.c
+++ b/drivers/gpu/drm/ast/ast_2500.c
@@ -27,7 +27,9 @@
*/
#include <linux/delay.h>
+#include <linux/pci.h>
+#include <drm/drm_drv.h>
#include <drm/drm_print.h>
#include "ast_drv.h"
@@ -567,3 +569,107 @@ int ast_2500_post(struct ast_device *ast)
return 0;
}
+
+/*
+ * Mode setting
+ */
+
+const struct ast_vbios_dclk_info ast_2500_dclk_table[] = {
+ {0x2c, 0xe7, 0x03}, /* 00: VCLK25_175 */
+ {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
+ {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
+ {0x76, 0x63, 0x01}, /* 03: VCLK36 */
+ {0xee, 0x67, 0x01}, /* 04: VCLK40 */
+ {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
+ {0xc6, 0x64, 0x01}, /* 06: VCLK50 */
+ {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
+ {0x80, 0x64, 0x00}, /* 08: VCLK65 */
+ {0x7b, 0x63, 0x00}, /* 09: VCLK75 */
+ {0x67, 0x62, 0x00}, /* 0a: VCLK78_75 */
+ {0x7c, 0x62, 0x00}, /* 0b: VCLK94_5 */
+ {0x8e, 0x62, 0x00}, /* 0c: VCLK108 */
+ {0x85, 0x24, 0x00}, /* 0d: VCLK135 */
+ {0x67, 0x22, 0x00}, /* 0e: VCLK157_5 */
+ {0x6a, 0x22, 0x00}, /* 0f: VCLK162 */
+ {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
+ {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */
+ {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
+ {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
+ {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
+ {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
+ {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
+ {0x58, 0x01, 0x42}, /* 17: VCLK119 */
+ {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
+ {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
+ {0x44, 0x20, 0x43}, /* 1a: VCLK118_25 */
+};
+
+/*
+ * Device initialization
+ */
+
+static void ast_2500_detect_widescreen(struct ast_device *ast)
+{
+ if (__ast_2100_detect_wsxga_p(ast) || ast->chip == AST2510) {
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ }
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2500_device_quirks = {
+ .crtc_mem_req_threshold_low = 96,
+ .crtc_mem_req_threshold_high = 120,
+ .crtc_hsync_precatch_needed = true,
+};
+
+struct drm_device *ast_2500_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2500_device_quirks);
+
+ ast->dclk_table = ast_2500_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ if (need_post) {
+ ret = ast_post_gpu(ast);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ /* map reserved buffer */
+ ast->dp501_fw_buf = NULL;
+ if (ast->vram_size < pci_resource_len(pdev, 0)) {
+ ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0);
+ if (!ast->dp501_fw_buf)
+ drm_info(dev, "failed to map reserved buffer!\n");
+ }
+
+ ast_2500_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_2600.c b/drivers/gpu/drm/ast/ast_2600.c
index 8d75a47444f5..dee78fd5b022 100644
--- a/drivers/gpu/drm/ast/ast_2600.c
+++ b/drivers/gpu/drm/ast/ast_2600.c
@@ -26,6 +26,10 @@
* Authors: Dave Airlie <airlied@redhat.com>
*/
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
#include "ast_drv.h"
#include "ast_post.h"
@@ -42,3 +46,71 @@ int ast_2600_post(struct ast_device *ast)
return 0;
}
+
+/*
+ * Device initialization
+ */
+
+static void ast_2600_detect_widescreen(struct ast_device *ast)
+{
+ ast->support_wsxga_p = true;
+ ast->support_fullhd = true;
+ if (__ast_2100_detect_wuxga(ast))
+ ast->support_wuxga = true;
+}
+
+static const struct ast_device_quirks ast_2600_device_quirks = {
+ .crtc_mem_req_threshold_low = 160,
+ .crtc_mem_req_threshold_high = 224,
+ .crtc_hsync_precatch_needed = true,
+ .crtc_hsync_add4_needed = true,
+};
+
+struct drm_device *ast_2600_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post)
+{
+ struct drm_device *dev;
+ struct ast_device *ast;
+ int ret;
+
+ ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
+ if (IS_ERR(ast))
+ return ERR_CAST(ast);
+ dev = &ast->base;
+
+ ast_device_init(ast, chip, config_mode, regs, ioregs, &ast_2600_device_quirks);
+
+ ast->dclk_table = ast_2500_dclk_table;
+
+ ast_2300_detect_tx_chip(ast);
+
+ switch (ast->tx_chip) {
+ case AST_TX_ASTDP:
+ ret = ast_post_gpu(ast);
+ break;
+ default:
+ ret = 0;
+ if (need_post)
+ ret = ast_post_gpu(ast);
+ break;
+ }
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = ast_mm_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ast_2600_detect_widescreen(ast);
+
+ ret = ast_mode_config_init(ast);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return dev;
+}
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index 473faa92d08c..b9a9b050b546 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -37,6 +37,7 @@
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_module.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "ast_drv.h"
@@ -46,6 +47,34 @@ static int ast_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, ast_modeset, int, 0400);
+void ast_device_init(struct ast_device *ast,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ const struct ast_device_quirks *quirks)
+{
+ ast->quirks = quirks;
+ ast->chip = chip;
+ ast->config_mode = config_mode;
+ ast->regs = regs;
+ ast->ioregs = ioregs;
+}
+
+void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip)
+{
+ static const char * const info_str[] = {
+ "analog VGA",
+ "Sil164 TMDS transmitter",
+ "DP501 DisplayPort transmitter",
+ "ASPEED DisplayPort transmitter",
+ };
+
+ drm_info(&ast->base, "Using %s\n", info_str[tx_chip]);
+
+ ast->tx_chip = tx_chip;
+}
+
/*
* DRM driver
*/
@@ -266,7 +295,7 @@ static int ast_detect_chip(struct pci_dev *pdev,
*chip_out = chip;
*config_mode_out = config_mode;
- return 0;
+ return __AST_CHIP_GEN(chip);
}
static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -277,6 +306,7 @@ static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
void __iomem *ioregs;
enum ast_config_mode config_mode;
enum ast_chip chip;
+ unsigned int chip_gen;
struct drm_device *drm;
bool need_post = false;
@@ -349,10 +379,43 @@ static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return ret;
ret = ast_detect_chip(pdev, regs, ioregs, &chip, &config_mode);
- if (ret)
+ if (ret < 0)
return ret;
+ chip_gen = ret;
- drm = ast_device_create(pdev, &ast_driver, chip, config_mode, regs, ioregs, need_post);
+ switch (chip_gen) {
+ case 1:
+ drm = ast_2000_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 2:
+ drm = ast_2100_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 3:
+ drm = ast_2200_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 4:
+ drm = ast_2300_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 5:
+ drm = ast_2400_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 6:
+ drm = ast_2500_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ case 7:
+ drm = ast_2600_device_create(pdev, &ast_driver, chip, config_mode,
+ regs, ioregs, need_post);
+ break;
+ default:
+ dev_err(&pdev->dev, "Gen%d not supported\n", chip_gen);
+ return -ENODEV;
+ }
if (IS_ERR(drm))
return PTR_ERR(drm);
pci_set_drvdata(pdev, drm);
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index c15aef014f69..787e38c6c17d 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -164,9 +164,31 @@ to_ast_connector(struct drm_connector *connector)
* Device
*/
+struct ast_device_quirks {
+ /*
+ * CRTC memory request threshold
+ */
+ unsigned char crtc_mem_req_threshold_low;
+ unsigned char crtc_mem_req_threshold_high;
+
+ /*
+ * Adjust hsync values to load next scanline early. Signalled
+ * by AST2500PreCatchCRT in VBIOS mode flags.
+ */
+ bool crtc_hsync_precatch_needed;
+
+ /*
+ * Workaround for modes with HSync Time that is not a multiple
+ * of 8 (e.g., 1920x1080@60Hz, HSync +44 pixels).
+ */
+ bool crtc_hsync_add4_needed;
+};
+
struct ast_device {
struct drm_device base;
+ const struct ast_device_quirks *quirks;
+
void __iomem *regs;
void __iomem *ioregs;
void __iomem *dp501_fw_buf;
@@ -174,6 +196,8 @@ struct ast_device {
enum ast_config_mode config_mode;
enum ast_chip chip;
+ const struct ast_vbios_dclk_info *dclk_table;
+
void __iomem *vram;
unsigned long vram_base;
unsigned long vram_size;
@@ -217,14 +241,6 @@ static inline struct ast_device *to_ast_device(struct drm_device *dev)
return container_of(dev, struct ast_device, base);
}
-struct drm_device *ast_device_create(struct pci_dev *pdev,
- const struct drm_driver *drv,
- enum ast_chip chip,
- enum ast_config_mode config_mode,
- void __iomem *regs,
- void __iomem *ioregs,
- bool need_post);
-
static inline unsigned long __ast_gen(struct ast_device *ast)
{
return __AST_CHIP_GEN(ast->chip);
@@ -282,13 +298,13 @@ static inline void __ast_write8_i(void __iomem *addr, u32 reg, u8 index, u8 val)
__ast_write8(addr, reg + 1, val);
}
-static inline void __ast_write8_i_masked(void __iomem *addr, u32 reg, u8 index, u8 read_mask,
+static inline void __ast_write8_i_masked(void __iomem *addr, u32 reg, u8 index, u8 preserve_mask,
u8 val)
{
- u8 tmp = __ast_read8_i_masked(addr, reg, index, read_mask);
+ u8 tmp = __ast_read8_i_masked(addr, reg, index, preserve_mask);
- tmp |= val;
- __ast_write8_i(addr, reg, index, tmp);
+ val &= ~preserve_mask;
+ __ast_write8_i(addr, reg, index, tmp | val);
}
static inline u32 ast_read32(struct ast_device *ast, u32 reg)
@@ -415,21 +431,89 @@ struct ast_crtc_state {
int ast_mm_init(struct ast_device *ast);
+/* ast_drv.c */
+void ast_device_init(struct ast_device *ast,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ const struct ast_device_quirks *quirks);
+void __ast_device_set_tx_chip(struct ast_device *ast, enum ast_tx_chip tx_chip);
+
/* ast_2000.c */
int ast_2000_post(struct ast_device *ast);
+extern const struct ast_vbios_dclk_info ast_2000_dclk_table[];
+void ast_2000_detect_tx_chip(struct ast_device *ast, bool need_post);
+struct drm_device *ast_2000_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
/* ast_2100.c */
int ast_2100_post(struct ast_device *ast);
+bool __ast_2100_detect_wsxga_p(struct ast_device *ast);
+bool __ast_2100_detect_wuxga(struct ast_device *ast);
+struct drm_device *ast_2100_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2200.c */
+struct drm_device *ast_2200_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
/* ast_2300.c */
int ast_2300_post(struct ast_device *ast);
+void ast_2300_detect_tx_chip(struct ast_device *ast);
+struct drm_device *ast_2300_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
+
+/* ast_2400.c */
+struct drm_device *ast_2400_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
/* ast_2500.c */
void ast_2500_patch_ahb(void __iomem *regs);
int ast_2500_post(struct ast_device *ast);
+extern const struct ast_vbios_dclk_info ast_2500_dclk_table[];
+struct drm_device *ast_2500_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
/* ast_2600.c */
int ast_2600_post(struct ast_device *ast);
+struct drm_device *ast_2600_device_create(struct pci_dev *pdev,
+ const struct drm_driver *drv,
+ enum ast_chip chip,
+ enum ast_config_mode config_mode,
+ void __iomem *regs,
+ void __iomem *ioregs,
+ bool need_post);
/* ast post */
int ast_post_gpu(struct ast_device *ast);
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c
deleted file mode 100644
index 3eea6a6cdacd..000000000000
--- a/drivers/gpu/drm/ast/ast_main.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2012 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- */
-/*
- * Authors: Dave Airlie <airlied@redhat.com>
- */
-
-#include <linux/of.h>
-#include <linux/pci.h>
-
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_gem.h>
-#include <drm/drm_managed.h>
-
-#include "ast_drv.h"
-
-/* Try to detect WSXGA+ on Gen2+ */
-static bool __ast_2100_detect_wsxga_p(struct ast_device *ast)
-{
- u8 vgacrd0 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd0);
-
- if (!(vgacrd0 & AST_IO_VGACRD0_VRAM_INIT_BY_BMC))
- return true;
- if (vgacrd0 & AST_IO_VGACRD0_IKVM_WIDESCREEN)
- return true;
-
- return false;
-}
-
-/* Try to detect WUXGA on Gen2+ */
-static bool __ast_2100_detect_wuxga(struct ast_device *ast)
-{
- u8 vgacrd1;
-
- if (ast->support_fullhd) {
- vgacrd1 = ast_get_index_reg(ast, AST_IO_VGACRI, 0xd1);
- if (!(vgacrd1 & AST_IO_VGACRD1_SUPPORTS_WUXGA))
- return true;
- }
-
- return false;
-}
-
-static void ast_detect_widescreen(struct ast_device *ast)
-{
- ast->support_wsxga_p = false;
- ast->support_fullhd = false;
- ast->support_wuxga = false;
-
- if (AST_GEN(ast) >= 7) {
- ast->support_wsxga_p = true;
- ast->support_fullhd = true;
- if (__ast_2100_detect_wuxga(ast))
- ast->support_wuxga = true;
- } else if (AST_GEN(ast) >= 6) {
- if (__ast_2100_detect_wsxga_p(ast))
- ast->support_wsxga_p = true;
- else if (ast->chip == AST2510)
- ast->support_wsxga_p = true;
- if (ast->support_wsxga_p)
- ast->support_fullhd = true;
- if (__ast_2100_detect_wuxga(ast))
- ast->support_wuxga = true;
- } else if (AST_GEN(ast) >= 5) {
- if (__ast_2100_detect_wsxga_p(ast))
- ast->support_wsxga_p = true;
- else if (ast->chip == AST1400)
- ast->support_wsxga_p = true;
- if (ast->support_wsxga_p)
- ast->support_fullhd = true;
- if (__ast_2100_detect_wuxga(ast))
- ast->support_wuxga = true;
- } else if (AST_GEN(ast) >= 4) {
- if (__ast_2100_detect_wsxga_p(ast))
- ast->support_wsxga_p = true;
- else if (ast->chip == AST1300)
- ast->support_wsxga_p = true;
- if (ast->support_wsxga_p)
- ast->support_fullhd = true;
- if (__ast_2100_detect_wuxga(ast))
- ast->support_wuxga = true;
- } else if (AST_GEN(ast) >= 3) {
- if (__ast_2100_detect_wsxga_p(ast))
- ast->support_wsxga_p = true;
- if (ast->support_wsxga_p) {
- if (ast->chip == AST2200)
- ast->support_fullhd = true;
- }
- if (__ast_2100_detect_wuxga(ast))
- ast->support_wuxga = true;
- } else if (AST_GEN(ast) >= 2) {
- if (__ast_2100_detect_wsxga_p(ast))
- ast->support_wsxga_p = true;
- if (ast->support_wsxga_p) {
- if (ast->chip == AST2100)
- ast->support_fullhd = true;
- }
- if (__ast_2100_detect_wuxga(ast))
- ast->support_wuxga = true;
- }
-}
-
-static void ast_detect_tx_chip(struct ast_device *ast, bool need_post)
-{
- static const char * const info_str[] = {
- "analog VGA",
- "Sil164 TMDS transmitter",
- "DP501 DisplayPort transmitter",
- "ASPEED DisplayPort transmitter",
- };
-
- struct drm_device *dev = &ast->base;
- u8 vgacra3, vgacrd1;
-
- /* Check 3rd Tx option (digital output afaik) */
- ast->tx_chip = AST_TX_NONE;
-
- if (AST_GEN(ast) <= 3) {
- /*
- * VGACRA3 Enhanced Color Mode Register, check if DVO is already
- * enabled, in that case, assume we have a SIL164 TMDS transmitter
- *
- * Don't make that assumption if we the chip wasn't enabled and
- * is at power-on reset, otherwise we'll incorrectly "detect" a
- * SIL164 when there is none.
- */
- if (!need_post) {
- vgacra3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xff);
- if (vgacra3 & AST_IO_VGACRA3_DVO_ENABLED)
- ast->tx_chip = AST_TX_SIL164;
- }
- } else {
- /*
- * On AST GEN4+, look at the configuration set by the SoC in
- * the SOC scratch register #1 bits 11:8 (interestingly marked
- * as "reserved" in the spec)
- */
- vgacrd1 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1,
- AST_IO_VGACRD1_TX_TYPE_MASK);
- switch (vgacrd1) {
- /*
- * GEN4 to GEN6
- */
- case AST_IO_VGACRD1_TX_SIL164_VBIOS:
- ast->tx_chip = AST_TX_SIL164;
- break;
- case AST_IO_VGACRD1_TX_DP501_VBIOS:
- ast->dp501_fw_addr = drmm_kzalloc(dev, 32*1024, GFP_KERNEL);
- if (ast->dp501_fw_addr) {
- /* backup firmware */
- if (ast_backup_fw(ast, ast->dp501_fw_addr, 32*1024)) {
- drmm_kfree(dev, ast->dp501_fw_addr);
- ast->dp501_fw_addr = NULL;
- }
- }
- fallthrough;
- case AST_IO_VGACRD1_TX_FW_EMBEDDED_FW:
- ast->tx_chip = AST_TX_DP501;
- break;
- /*
- * GEN7+
- */
- case AST_IO_VGACRD1_TX_ASTDP:
- ast->tx_chip = AST_TX_ASTDP;
- break;
- /*
- * Several of the listed TX chips are not explicitly supported
- * by the ast driver. If these exist in real-world devices, they
- * are most likely reported as VGA or SIL164 outputs. We warn here
- * to get bug reports for these devices. If none come in for some
- * time, we can begin to fail device probing on these values.
- */
- case AST_IO_VGACRD1_TX_ITE66121_VBIOS:
- drm_warn(dev, "ITE IT66121 detected, 0x%x, Gen%lu\n",
- vgacrd1, AST_GEN(ast));
- break;
- case AST_IO_VGACRD1_TX_CH7003_VBIOS:
- drm_warn(dev, "Chrontel CH7003 detected, 0x%x, Gen%lu\n",
- vgacrd1, AST_GEN(ast));
- break;
- case AST_IO_VGACRD1_TX_ANX9807_VBIOS:
- drm_warn(dev, "Analogix ANX9807 detected, 0x%x, Gen%lu\n",
- vgacrd1, AST_GEN(ast));
- break;
- }
- }
-
- drm_info(dev, "Using %s\n", info_str[ast->tx_chip]);
-}
-
-struct drm_device *ast_device_create(struct pci_dev *pdev,
- const struct drm_driver *drv,
- enum ast_chip chip,
- enum ast_config_mode config_mode,
- void __iomem *regs,
- void __iomem *ioregs,
- bool need_post)
-{
- struct drm_device *dev;
- struct ast_device *ast;
- int ret;
-
- ast = devm_drm_dev_alloc(&pdev->dev, drv, struct ast_device, base);
- if (IS_ERR(ast))
- return ERR_CAST(ast);
- dev = &ast->base;
-
- ast->chip = chip;
- ast->config_mode = config_mode;
- ast->regs = regs;
- ast->ioregs = ioregs;
-
- ast_detect_tx_chip(ast, need_post);
- switch (ast->tx_chip) {
- case AST_TX_ASTDP:
- ret = ast_post_gpu(ast);
- break;
- default:
- ret = 0;
- if (need_post)
- ret = ast_post_gpu(ast);
- break;
- }
- if (ret)
- return ERR_PTR(ret);
-
- ret = ast_mm_init(ast);
- if (ret)
- return ERR_PTR(ret);
-
- /* map reserved buffer */
- ast->dp501_fw_buf = NULL;
- if (ast->vram_size < pci_resource_len(pdev, 0)) {
- ast->dp501_fw_buf = pci_iomap_range(pdev, 0, ast->vram_size, 0);
- if (!ast->dp501_fw_buf)
- drm_info(dev, "failed to map reserved buffer!\n");
- }
-
- ast_detect_widescreen(ast);
-
- ret = ast_mode_config_init(ast);
- if (ret)
- return ERR_PTR(ret);
-
- return dev;
-}
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 30b011ed0a05..cd08990a10f9 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -43,6 +43,7 @@
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_panic.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "ast_drv.h"
@@ -241,16 +242,15 @@ static void ast_set_std_reg(struct ast_device *ast,
ast_set_index_reg(ast, AST_IO_VGAGRI, i, stdtable->gr[i]);
}
-static void ast_set_crtc_reg(struct ast_device *ast,
- struct drm_display_mode *mode,
+static void ast_set_crtc_reg(struct ast_device *ast, struct drm_display_mode *mode,
const struct ast_vbios_enhtable *vmode)
{
u8 jreg05 = 0, jreg07 = 0, jreg09 = 0, jregAC = 0, jregAD = 0, jregAE = 0;
- u16 temp, precache = 0;
+ u16 temp;
+ unsigned char crtc_hsync_precatch = 0;
- if ((IS_AST_GEN6(ast) || IS_AST_GEN7(ast)) &&
- (vmode->flags & AST2500PreCatchCRT))
- precache = 40;
+ if (ast->quirks->crtc_hsync_precatch_needed && (vmode->flags & AST2500PreCatchCRT))
+ crtc_hsync_precatch = 40;
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x11, 0x7f, 0x00);
@@ -276,12 +276,12 @@ static void ast_set_crtc_reg(struct ast_device *ast,
jregAD |= 0x01; /* HBE D[5] */
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x03, 0xE0, (temp & 0x1f));
- temp = ((mode->crtc_hsync_start-precache) >> 3) - 1;
+ temp = ((mode->crtc_hsync_start - crtc_hsync_precatch) >> 3) - 1;
if (temp & 0x100)
jregAC |= 0x40; /* HRS D[5] */
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x04, 0x00, temp);
- temp = (((mode->crtc_hsync_end-precache) >> 3) - 1) & 0x3f;
+ temp = (((mode->crtc_hsync_end - crtc_hsync_precatch) >> 3) - 1) & 0x3f;
if (temp & 0x20)
jregAD |= 0x04; /* HRE D[5] */
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x05, 0x60, (u8)((temp & 0x1f) | jreg05));
@@ -289,8 +289,7 @@ static void ast_set_crtc_reg(struct ast_device *ast,
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAC, 0x00, jregAC);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAD, 0x00, jregAD);
- // Workaround for HSync Time non octave pixels (1920x1080@60Hz HSync 44 pixels);
- if (IS_AST_GEN7(ast) && (mode->crtc_vdisplay == 1080))
+ if (ast->quirks->crtc_hsync_add4_needed && mode->crtc_vdisplay == 1080)
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x02);
else
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xFC, 0xFD, 0x00);
@@ -348,7 +347,7 @@ static void ast_set_crtc_reg(struct ast_device *ast,
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x09, 0xdf, jreg09);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xAE, 0x00, (jregAE | 0x80));
- if (precache)
+ if (crtc_hsync_precatch)
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x80);
else
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xb6, 0x3f, 0x00);
@@ -370,12 +369,7 @@ static void ast_set_dclk_reg(struct ast_device *ast,
struct drm_display_mode *mode,
const struct ast_vbios_enhtable *vmode)
{
- const struct ast_vbios_dclk_info *clk_info;
-
- if (IS_AST_GEN6(ast) || IS_AST_GEN7(ast))
- clk_info = &dclk_table_ast2500[vmode->dclk_index];
- else
- clk_info = &dclk_table[vmode->dclk_index];
+ const struct ast_vbios_dclk_info *clk_info = &ast->dclk_table[vmode->dclk_index];
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc0, 0x00, clk_info->param1);
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xc1, 0x00, clk_info->param2);
@@ -415,20 +409,11 @@ static void ast_set_color_reg(struct ast_device *ast,
static void ast_set_crtthd_reg(struct ast_device *ast)
{
- /* Set Threshold */
- if (IS_AST_GEN7(ast)) {
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0xe0);
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0xa0);
- } else if (IS_AST_GEN6(ast) || IS_AST_GEN5(ast) || IS_AST_GEN4(ast)) {
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x78);
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x60);
- } else if (IS_AST_GEN3(ast) || IS_AST_GEN2(ast)) {
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x3f);
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x2f);
- } else {
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, 0x2f);
- ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, 0x1f);
- }
+ u8 vgacra6 = ast->quirks->crtc_mem_req_threshold_low;
+ u8 vgacra7 = ast->quirks->crtc_mem_req_threshold_high;
+
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xa7, vgacra7);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0xa6, vgacra6);
}
static void ast_set_sync_reg(struct ast_device *ast,
@@ -572,9 +557,14 @@ static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
ast_set_vbios_color_reg(ast, fb->format, ast_crtc_state->vmode);
}
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
- drm_atomic_for_each_plane_damage(&iter, &damage) {
- ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage);
+ /* if the buffer comes from another device */
+ if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
+ drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_for_each_plane_damage(&iter, &damage) {
+ ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage);
+ }
+
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
}
/*
diff --git a/drivers/gpu/drm/ast/ast_tables.h b/drivers/gpu/drm/ast/ast_tables.h
index f1c9f7e1f1fc..7da5b5c60f41 100644
--- a/drivers/gpu/drm/ast/ast_tables.h
+++ b/drivers/gpu/drm/ast/ast_tables.h
@@ -33,66 +33,6 @@
#define HiCModeIndex 3
#define TrueCModeIndex 4
-static const struct ast_vbios_dclk_info dclk_table[] = {
- {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */
- {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
- {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
- {0x76, 0x63, 0x01}, /* 03: VCLK36 */
- {0xEE, 0x67, 0x01}, /* 04: VCLK40 */
- {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
- {0xC6, 0x64, 0x01}, /* 06: VCLK50 */
- {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
- {0x80, 0x64, 0x00}, /* 08: VCLK65 */
- {0x7B, 0x63, 0x00}, /* 09: VCLK75 */
- {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */
- {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */
- {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */
- {0x85, 0x24, 0x00}, /* 0D: VCLK135 */
- {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */
- {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */
- {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
- {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */
- {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
- {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
- {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
- {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
- {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
- {0x77, 0x58, 0x80}, /* 17: VCLK119 */
- {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
- {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
- {0x3b, 0x2c, 0x81}, /* 1A: VCLK118_25 */
-};
-
-static const struct ast_vbios_dclk_info dclk_table_ast2500[] = {
- {0x2C, 0xE7, 0x03}, /* 00: VCLK25_175 */
- {0x95, 0x62, 0x03}, /* 01: VCLK28_322 */
- {0x67, 0x63, 0x01}, /* 02: VCLK31_5 */
- {0x76, 0x63, 0x01}, /* 03: VCLK36 */
- {0xEE, 0x67, 0x01}, /* 04: VCLK40 */
- {0x82, 0x62, 0x01}, /* 05: VCLK49_5 */
- {0xC6, 0x64, 0x01}, /* 06: VCLK50 */
- {0x94, 0x62, 0x01}, /* 07: VCLK56_25 */
- {0x80, 0x64, 0x00}, /* 08: VCLK65 */
- {0x7B, 0x63, 0x00}, /* 09: VCLK75 */
- {0x67, 0x62, 0x00}, /* 0A: VCLK78_75 */
- {0x7C, 0x62, 0x00}, /* 0B: VCLK94_5 */
- {0x8E, 0x62, 0x00}, /* 0C: VCLK108 */
- {0x85, 0x24, 0x00}, /* 0D: VCLK135 */
- {0x67, 0x22, 0x00}, /* 0E: VCLK157_5 */
- {0x6A, 0x22, 0x00}, /* 0F: VCLK162 */
- {0x4d, 0x4c, 0x80}, /* 10: VCLK154 */
- {0x68, 0x6f, 0x80}, /* 11: VCLK83.5 */
- {0x28, 0x49, 0x80}, /* 12: VCLK106.5 */
- {0x37, 0x49, 0x80}, /* 13: VCLK146.25 */
- {0x1f, 0x45, 0x80}, /* 14: VCLK148.5 */
- {0x47, 0x6c, 0x80}, /* 15: VCLK71 */
- {0x25, 0x65, 0x80}, /* 16: VCLK88.75 */
- {0x58, 0x01, 0x42}, /* 17: VCLK119 */
- {0x32, 0x67, 0x80}, /* 18: VCLK85_5 */
- {0x6a, 0x6d, 0x80}, /* 19: VCLK97_75 */
- {0x44, 0x20, 0x43}, /* 1A: VCLK118_25 */
-};
-
static const struct ast_vbios_stdtable vbios_stdtable[] = {
/* MD_2_3_400 */
{
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 0f7ffb3ced20..e0efc7309b1b 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -20,6 +20,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -215,32 +216,32 @@ static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
!(status & ATMEL_XLCDC_CM),
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register CMSTS timeout\n");
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_XLCDC_SD);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
status & ATMEL_XLCDC_SD,
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register SDSTS timeout\n");
}
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
!(status & ATMEL_HLCDC_DISP),
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register DISPSTS timeout\n");
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
!(status & ATMEL_HLCDC_SYNC),
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register LCDSTS timeout\n");
regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
!(status & ATMEL_HLCDC_PIXEL_CLK),
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register CLKSTS timeout\n");
clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
pinctrl_pm_select_sleep_state(dev->dev);
@@ -269,32 +270,32 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
status & ATMEL_HLCDC_PIXEL_CLK,
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register CLKSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register CLKSTS timeout\n");
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
status & ATMEL_HLCDC_SYNC,
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register LCDSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register LCDSTS timeout\n");
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
status & ATMEL_HLCDC_DISP,
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register DISPSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register DISPSTS timeout\n");
if (crtc->dc->desc->is_xlcdc) {
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_CM);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
status & ATMEL_XLCDC_CM,
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register CMSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register CMSTS timeout\n");
regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_XLCDC_SD);
if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
!(status & ATMEL_XLCDC_SD),
10, 1000))
- dev_warn(dev->dev, "Atmel LCDC status register SDSTS timeout\n");
+ drm_warn(dev, "Atmel LCDC status register SDSTS timeout\n");
}
pm_runtime_put_sync(dev->dev);
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
index fa8ad94e431a..dd70894c8f38 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
@@ -25,6 +25,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -724,19 +725,19 @@ static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev)
ret = atmel_hlcdc_create_outputs(dev);
if (ret) {
- dev_err(dev->dev, "failed to create HLCDC outputs: %d\n", ret);
+ drm_err(dev, "failed to create HLCDC outputs: %d\n", ret);
return ret;
}
ret = atmel_hlcdc_create_planes(dev);
if (ret) {
- dev_err(dev->dev, "failed to create planes: %d\n", ret);
+ drm_err(dev, "failed to create planes: %d\n", ret);
return ret;
}
ret = atmel_hlcdc_crtc_create(dev);
if (ret) {
- dev_err(dev->dev, "failed to create crtc\n");
+ drm_err(dev, "failed to create crtc\n");
return ret;
}
@@ -778,7 +779,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
ret = clk_prepare_enable(dc->hlcdc->periph_clk);
if (ret) {
- dev_err(dev->dev, "failed to enable periph_clk\n");
+ drm_err(dev, "failed to enable periph_clk\n");
return ret;
}
@@ -786,13 +787,13 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
ret = drm_vblank_init(dev, 1);
if (ret < 0) {
- dev_err(dev->dev, "failed to initialize vblank\n");
+ drm_err(dev, "failed to initialize vblank\n");
goto err_periph_clk_disable;
}
ret = atmel_hlcdc_dc_modeset_init(dev);
if (ret < 0) {
- dev_err(dev->dev, "failed to initialize mode setting\n");
+ drm_err(dev, "failed to initialize mode setting\n");
goto err_periph_clk_disable;
}
@@ -802,7 +803,7 @@ static int atmel_hlcdc_dc_load(struct drm_device *dev)
ret = atmel_hlcdc_dc_irq_install(dev, dc->hlcdc->irq);
pm_runtime_put_sync(dev->dev);
if (ret < 0) {
- dev_err(dev->dev, "failed to install IRQ handler\n");
+ drm_err(dev, "failed to install IRQ handler\n");
goto err_periph_clk_disable;
}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
index e1a0bb24b511..53d47f01db0b 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h
@@ -378,7 +378,8 @@ struct atmel_lcdc_dc_ops {
void (*lcdc_update_buffers)(struct atmel_hlcdc_plane *plane,
struct atmel_hlcdc_plane_state *state,
u32 sr, int i);
- void (*lcdc_atomic_disable)(struct atmel_hlcdc_plane *plane);
+ void (*lcdc_atomic_disable)(struct atmel_hlcdc_plane *plane,
+ struct atmel_hlcdc_dc *dc);
void (*lcdc_update_general_settings)(struct atmel_hlcdc_plane *plane,
struct atmel_hlcdc_plane_state *state);
void (*lcdc_atomic_update)(struct atmel_hlcdc_plane *plane,
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
index 50fee6a93964..0b8a86afb096 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -15,6 +15,7 @@
#include <drm/drm_bridge.h>
#include <drm/drm_encoder.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "atmel_hlcdc_dc.h"
@@ -92,7 +93,7 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, int endpoint)
output->bus_fmt = atmel_hlcdc_of_bus_fmt(ep);
of_node_put(ep);
if (output->bus_fmt < 0) {
- dev_err(dev->dev, "endpoint %d: invalid bus width\n", endpoint);
+ drm_err(dev, "endpoint %d: invalid bus width\n", endpoint);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
index 4a7ba0918eca..92132be9823f 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
@@ -16,6 +16,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "atmel_hlcdc_dc.h"
@@ -365,13 +366,34 @@ void atmel_xlcdc_plane_setup_scaler(struct atmel_hlcdc_plane *plane,
xfactor);
/*
- * With YCbCr 4:2:2 and YCbYcr 4:2:0 window resampling, configuration
- * register LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT is half
+ * With YCbCr 4:2:0 window resampling, configuration register
+ * LCDC_HEOCFG25.VXSCFACT and LCDC_HEOCFG27.HXSCFACT values are half
* the value of yfactor and xfactor.
+ *
+ * On the other hand, with YCbCr 4:2:2 window resampling, only the
+ * configuration register LCDC_HEOCFG27.HXSCFACT value is half the value
+ * of the xfactor; the value of LCDC_HEOCFG25.VXSCFACT is yfactor (no
+ * division by 2).
*/
- if (state->base.fb->format->format == DRM_FORMAT_YUV420) {
+ switch (state->base.fb->format->format) {
+ /* YCbCr 4:2:2 */
+ case DRM_FORMAT_YUYV:
+ case DRM_FORMAT_UYVY:
+ case DRM_FORMAT_YVYU:
+ case DRM_FORMAT_VYUY:
+ case DRM_FORMAT_YUV422:
+ case DRM_FORMAT_NV61:
+ xfactor /= 2;
+ break;
+
+ /* YCbCr 4:2:0 */
+ case DRM_FORMAT_YUV420:
+ case DRM_FORMAT_NV21:
yfactor /= 2;
xfactor /= 2;
+ break;
+ default:
+ break;
}
atmel_hlcdc_layer_write_cfg(&plane->layer, desc->layout.scaler_config + 2,
@@ -714,7 +736,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
if (!hstate->base.crtc || WARN_ON(!fb))
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(state, s->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, s->crtc);
mode = &crtc_state->adjusted_mode;
ret = drm_atomic_helper_check_plane_state(s, crtc_state,
@@ -816,7 +838,8 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
return 0;
}
-static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
+static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,
+ struct atmel_hlcdc_dc *dc)
{
/* Disable interrupts */
atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_HLCDC_LAYER_IDR,
@@ -832,7 +855,8 @@ static void atmel_hlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_HLCDC_LAYER_ISR);
}
-static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
+static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane,
+ struct atmel_hlcdc_dc *dc)
{
/* Disable interrupts */
atmel_hlcdc_layer_write_reg(&plane->layer, ATMEL_XLCDC_LAYER_IDR,
@@ -842,6 +866,15 @@ static void atmel_xlcdc_atomic_disable(struct atmel_hlcdc_plane *plane)
atmel_hlcdc_layer_write_reg(&plane->layer,
ATMEL_XLCDC_LAYER_ENR, 0);
+ /*
+ * Updating XLCDC_xxxCFGx, XLCDC_xxxFBA and XLCDC_xxxEN,
+ * (where xxx indicates each layer) requires writing one to the
+ * Update Attribute field for each layer in LCDC_ATTRE register for SAM9X7.
+ */
+ regmap_write(dc->hlcdc->regmap, ATMEL_XLCDC_ATTRE, ATMEL_XLCDC_BASE_UPDATE |
+ ATMEL_XLCDC_OVR1_UPDATE | ATMEL_XLCDC_OVR3_UPDATE |
+ ATMEL_XLCDC_HEO_UPDATE);
+
/* Clear all pending interrupts */
atmel_hlcdc_layer_read_reg(&plane->layer, ATMEL_XLCDC_LAYER_ISR);
}
@@ -852,7 +885,7 @@ static void atmel_hlcdc_plane_atomic_disable(struct drm_plane *p,
struct atmel_hlcdc_plane *plane = drm_plane_to_atmel_hlcdc_plane(p);
struct atmel_hlcdc_dc *dc = plane->base.dev->dev_private;
- dc->desc->ops->lcdc_atomic_disable(plane);
+ dc->desc->ops->lcdc_atomic_disable(plane, dc);
}
static void atmel_hlcdc_atomic_update(struct atmel_hlcdc_plane *plane,
@@ -1034,7 +1067,7 @@ static void atmel_hlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,
if (isr &
(ATMEL_HLCDC_LAYER_OVR_IRQ(0) | ATMEL_HLCDC_LAYER_OVR_IRQ(1) |
ATMEL_HLCDC_LAYER_OVR_IRQ(2)))
- dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
+ drm_dbg(plane->base.dev, "overrun on plane %s\n",
desc->name);
}
@@ -1051,7 +1084,7 @@ static void atmel_xlcdc_irq_dbg(struct atmel_hlcdc_plane *plane,
if (isr &
(ATMEL_XLCDC_LAYER_OVR_IRQ(0) | ATMEL_XLCDC_LAYER_OVR_IRQ(1) |
ATMEL_XLCDC_LAYER_OVR_IRQ(2)))
- dev_dbg(plane->base.dev->dev, "overrun on plane %s\n",
+ drm_dbg(plane->base.dev, "overrun on plane %s\n",
desc->name);
}
@@ -1140,7 +1173,7 @@ static void atmel_hlcdc_plane_reset(struct drm_plane *p)
if (state) {
if (atmel_hlcdc_plane_alloc_dscrs(p, state)) {
kfree(state);
- dev_err(p->dev->dev,
+ drm_err(p->dev,
"Failed to allocate initial plane state\n");
return;
}
diff --git a/drivers/gpu/drm/bridge/imx/Kconfig b/drivers/gpu/drm/bridge/imx/Kconfig
index 9a480c6abb85..b9028a5e5a06 100644
--- a/drivers/gpu/drm/bridge/imx/Kconfig
+++ b/drivers/gpu/drm/bridge/imx/Kconfig
@@ -18,12 +18,23 @@ config DRM_IMX8MP_DW_HDMI_BRIDGE
depends on OF
depends on COMMON_CLK
select DRM_DW_HDMI
+ imply DRM_IMX8MP_HDMI_PAI
imply DRM_IMX8MP_HDMI_PVI
imply PHY_FSL_SAMSUNG_HDMI_PHY
help
Choose this to enable support for the internal HDMI encoder found
on the i.MX8MP SoC.
+config DRM_IMX8MP_HDMI_PAI
+ tristate "Freescale i.MX8MP HDMI PAI bridge support"
+ depends on OF
+ select DRM_DW_HDMI
+ select REGMAP
+ select REGMAP_MMIO
+ help
+ Choose this to enable support for the internal HDMI TX Parallel
+ Audio Interface found on the Freescale i.MX8MP SoC.
+
config DRM_IMX8MP_HDMI_PVI
tristate "Freescale i.MX8MP HDMI PVI bridge support"
depends on OF
diff --git a/drivers/gpu/drm/bridge/imx/Makefile b/drivers/gpu/drm/bridge/imx/Makefile
index dd5d48584806..8d01fda25451 100644
--- a/drivers/gpu/drm/bridge/imx/Makefile
+++ b/drivers/gpu/drm/bridge/imx/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_DRM_IMX_LDB_HELPER) += imx-ldb-helper.o
obj-$(CONFIG_DRM_IMX_LEGACY_BRIDGE) += imx-legacy-bridge.o
obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE) += imx8mp-hdmi-tx.o
+obj-$(CONFIG_DRM_IMX8MP_HDMI_PAI) += imx8mp-hdmi-pai.o
obj-$(CONFIG_DRM_IMX8MP_HDMI_PVI) += imx8mp-hdmi-pvi.o
obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o
obj-$(CONFIG_DRM_IMX8QXP_LDB) += imx8qxp-ldb.o
diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c
new file mode 100644
index 000000000000..8d13a35b206a
--- /dev/null
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pai.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <sound/asoundef.h>
+
+#define HTX_PAI_CTRL 0x00
+#define ENABLE BIT(0)
+
+#define HTX_PAI_CTRL_EXT 0x04
+#define WTMK_HIGH_MASK GENMASK(31, 24)
+#define WTMK_LOW_MASK GENMASK(23, 16)
+#define NUM_CH_MASK GENMASK(10, 8)
+#define WTMK_HIGH(n) FIELD_PREP(WTMK_HIGH_MASK, (n))
+#define WTMK_LOW(n) FIELD_PREP(WTMK_LOW_MASK, (n))
+#define NUM_CH(n) FIELD_PREP(NUM_CH_MASK, (n) - 1)
+
+#define HTX_PAI_FIELD_CTRL 0x08
+#define PRE_SEL GENMASK(28, 24)
+#define D_SEL GENMASK(23, 20)
+#define V_SEL GENMASK(19, 15)
+#define U_SEL GENMASK(14, 10)
+#define C_SEL GENMASK(9, 5)
+#define P_SEL GENMASK(4, 0)
+
+struct imx8mp_hdmi_pai {
+ struct regmap *regmap;
+};
+
+static void imx8mp_hdmi_pai_enable(struct dw_hdmi *dw_hdmi, int channel,
+ int width, int rate, int non_pcm,
+ int iec958)
+{
+ const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi);
+ struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio;
+ int val;
+
+ /* PAI set control extended */
+ val = WTMK_HIGH(3) | WTMK_LOW(3);
+ val |= NUM_CH(channel);
+ regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL_EXT, val);
+
+ /* IEC60958 format */
+ if (iec958) {
+ val = FIELD_PREP_CONST(P_SEL,
+ __bf_shf(IEC958_SUBFRAME_PARITY));
+ val |= FIELD_PREP_CONST(C_SEL,
+ __bf_shf(IEC958_SUBFRAME_CHANNEL_STATUS));
+ val |= FIELD_PREP_CONST(U_SEL,
+ __bf_shf(IEC958_SUBFRAME_USER_DATA));
+ val |= FIELD_PREP_CONST(V_SEL,
+ __bf_shf(IEC958_SUBFRAME_VALIDITY));
+ val |= FIELD_PREP_CONST(D_SEL,
+ __bf_shf(IEC958_SUBFRAME_SAMPLE_24_MASK));
+ val |= FIELD_PREP_CONST(PRE_SEL,
+ __bf_shf(IEC958_SUBFRAME_PREAMBLE_MASK));
+ } else {
+ /*
+ * The allowed PCM widths are 24bit and 32bit, as they are supported
+ * by aud2htx module.
+ * for 24bit, D_SEL = 0, select all the bits.
+ * for 32bit, D_SEL = 8, select 24bit in MSB.
+ */
+ val = FIELD_PREP(D_SEL, width - 24);
+ }
+
+ regmap_write(hdmi_pai->regmap, HTX_PAI_FIELD_CTRL, val);
+
+ /* PAI start running */
+ regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, ENABLE);
+}
+
+static void imx8mp_hdmi_pai_disable(struct dw_hdmi *dw_hdmi)
+{
+ const struct dw_hdmi_plat_data *pdata = dw_hdmi_to_plat_data(dw_hdmi);
+ struct imx8mp_hdmi_pai *hdmi_pai = pdata->priv_audio;
+
+ /* Stop PAI */
+ regmap_write(hdmi_pai->regmap, HTX_PAI_CTRL, 0);
+}
+
+static const struct regmap_config imx8mp_hdmi_pai_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = HTX_PAI_FIELD_CTRL,
+};
+
+static int imx8mp_hdmi_pai_bind(struct device *dev, struct device *master, void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dw_hdmi_plat_data *plat_data = data;
+ struct imx8mp_hdmi_pai *hdmi_pai;
+ struct resource *res;
+ void __iomem *base;
+
+ hdmi_pai = devm_kzalloc(dev, sizeof(*hdmi_pai), GFP_KERNEL);
+ if (!hdmi_pai)
+ return -ENOMEM;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ hdmi_pai->regmap = devm_regmap_init_mmio_clk(dev, "apb", base,
+ &imx8mp_hdmi_pai_regmap_config);
+ if (IS_ERR(hdmi_pai->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(hdmi_pai->regmap);
+ }
+
+ plat_data->enable_audio = imx8mp_hdmi_pai_enable;
+ plat_data->disable_audio = imx8mp_hdmi_pai_disable;
+ plat_data->priv_audio = hdmi_pai;
+
+ return 0;
+}
+
+static const struct component_ops imx8mp_hdmi_pai_ops = {
+ .bind = imx8mp_hdmi_pai_bind,
+};
+
+static int imx8mp_hdmi_pai_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &imx8mp_hdmi_pai_ops);
+}
+
+static void imx8mp_hdmi_pai_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &imx8mp_hdmi_pai_ops);
+}
+
+static const struct of_device_id imx8mp_hdmi_pai_of_table[] = {
+ { .compatible = "fsl,imx8mp-hdmi-pai" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8mp_hdmi_pai_of_table);
+
+static struct platform_driver imx8mp_hdmi_pai_platform_driver = {
+ .probe = imx8mp_hdmi_pai_probe,
+ .remove = imx8mp_hdmi_pai_remove,
+ .driver = {
+ .name = "imx8mp-hdmi-pai",
+ .of_match_table = imx8mp_hdmi_pai_of_table,
+ },
+};
+module_platform_driver(imx8mp_hdmi_pai_platform_driver);
+
+MODULE_DESCRIPTION("i.MX8MP HDMI PAI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c
index 1e7a789ec289..32fd3554e267 100644
--- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c
@@ -5,11 +5,13 @@
*/
#include <linux/clk.h>
+#include <linux/component.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
struct imx8mp_hdmi {
struct dw_hdmi_plat_data plat_data;
@@ -79,10 +81,45 @@ static const struct dw_hdmi_phy_ops imx8mp_hdmi_phy_ops = {
.update_hpd = dw_hdmi_phy_update_hpd,
};
+static int imx8mp_dw_hdmi_bind(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct imx8mp_hdmi *hdmi = dev_get_drvdata(dev);
+ int ret;
+
+ ret = component_bind_all(dev, &hdmi->plat_data);
+ if (ret)
+ return dev_err_probe(dev, ret, "component_bind_all failed!\n");
+
+ hdmi->dw_hdmi = dw_hdmi_probe(pdev, &hdmi->plat_data);
+ if (IS_ERR(hdmi->dw_hdmi)) {
+ component_unbind_all(dev, &hdmi->plat_data);
+ return PTR_ERR(hdmi->dw_hdmi);
+ }
+
+ return 0;
+}
+
+static void imx8mp_dw_hdmi_unbind(struct device *dev)
+{
+ struct imx8mp_hdmi *hdmi = dev_get_drvdata(dev);
+
+ dw_hdmi_remove(hdmi->dw_hdmi);
+
+ component_unbind_all(dev, &hdmi->plat_data);
+}
+
+static const struct component_master_ops imx8mp_dw_hdmi_ops = {
+ .bind = imx8mp_dw_hdmi_bind,
+ .unbind = imx8mp_dw_hdmi_unbind,
+};
+
static int imx8mp_dw_hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_hdmi_plat_data *plat_data;
+ struct component_match *match = NULL;
+ struct device_node *remote;
struct imx8mp_hdmi *hdmi;
hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
@@ -102,20 +139,38 @@ static int imx8mp_dw_hdmi_probe(struct platform_device *pdev)
plat_data->priv_data = hdmi;
plat_data->phy_force_vendor = true;
- hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data);
- if (IS_ERR(hdmi->dw_hdmi))
- return PTR_ERR(hdmi->dw_hdmi);
-
platform_set_drvdata(pdev, hdmi);
+ /* port@2 is for hdmi_pai device */
+ remote = of_graph_get_remote_node(pdev->dev.of_node, 2, 0);
+ if (!remote) {
+ hdmi->dw_hdmi = dw_hdmi_probe(pdev, plat_data);
+ if (IS_ERR(hdmi->dw_hdmi))
+ return PTR_ERR(hdmi->dw_hdmi);
+ } else {
+ drm_of_component_match_add(dev, &match, component_compare_of, remote);
+
+ of_node_put(remote);
+
+ return component_master_add_with_match(dev, &imx8mp_dw_hdmi_ops, match);
+ }
+
return 0;
}
static void imx8mp_dw_hdmi_remove(struct platform_device *pdev)
{
struct imx8mp_hdmi *hdmi = platform_get_drvdata(pdev);
+ struct device_node *remote;
- dw_hdmi_remove(hdmi->dw_hdmi);
+ remote = of_graph_get_remote_node(pdev->dev.of_node, 2, 0);
+ if (remote) {
+ of_node_put(remote);
+
+ component_master_del(&pdev->dev, &imx8mp_dw_hdmi_ops);
+ } else {
+ dw_hdmi_remove(hdmi->dw_hdmi);
+ }
}
static int imx8mp_dw_hdmi_pm_suspend(struct device *dev)
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
index 5d272916e200..122502968927 100644
--- a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
+++ b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c
@@ -683,11 +683,6 @@ static void imx8qxp_ldb_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-static int imx8qxp_ldb_runtime_suspend(struct device *dev)
-{
- return 0;
-}
-
static int imx8qxp_ldb_runtime_resume(struct device *dev)
{
struct imx8qxp_ldb *imx8qxp_ldb = dev_get_drvdata(dev);
@@ -700,7 +695,7 @@ static int imx8qxp_ldb_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops imx8qxp_ldb_pm_ops = {
- RUNTIME_PM_OPS(imx8qxp_ldb_runtime_suspend, imx8qxp_ldb_runtime_resume, NULL)
+ RUNTIME_PM_OPS(NULL, imx8qxp_ldb_runtime_resume, NULL)
};
static const struct of_device_id imx8qxp_ldb_dt_ids[] = {
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index aa7b1dcc5d70..0185f61e6e59 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -287,6 +287,7 @@
enum chip_id {
ID_IT6610,
ID_IT66121,
+ ID_IT66122,
};
struct it66121_chip_info {
@@ -312,7 +313,7 @@ struct it66121_ctx {
u8 swl;
bool auto_cts;
} audio;
- const struct it66121_chip_info *info;
+ enum chip_id id;
};
static const struct regmap_range_cfg it66121_regmap_banks[] = {
@@ -402,7 +403,7 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,
if (ret)
return ret;
- if (ctx->info->id == ID_IT66121) {
+ if (ctx->id == ID_IT66121 || ctx->id == ID_IT66122) {
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
IT66121_AFE_IP_EC1, 0);
if (ret)
@@ -428,7 +429,7 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,
if (ret)
return ret;
- if (ctx->info->id == ID_IT66121) {
+ if (ctx->id == ID_IT66121 || ctx->id == ID_IT66122) {
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG,
IT66121_AFE_IP_EC1,
IT66121_AFE_IP_EC1);
@@ -449,7 +450,7 @@ static int it66121_configure_afe(struct it66121_ctx *ctx,
if (ret)
return ret;
- if (ctx->info->id == ID_IT6610) {
+ if (ctx->id == ID_IT6610) {
ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG,
IT6610_AFE_XP_BYPASS,
IT6610_AFE_XP_BYPASS);
@@ -599,7 +600,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,
if (ret)
return ret;
- if (ctx->info->id == ID_IT66121) {
+ if (ctx->id == ID_IT66121 || ctx->id == ID_IT66122) {
ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
IT66121_CLK_BANK_PWROFF_RCLK, 0);
if (ret)
@@ -748,7 +749,7 @@ static int it66121_bridge_check(struct drm_bridge *bridge,
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
- if (ctx->info->id == ID_IT6610) {
+ if (ctx->id == ID_IT6610) {
/* The IT6610 only supports these settings */
bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH |
DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
@@ -802,7 +803,7 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI))
goto unlock;
- if (ctx->info->id == ID_IT66121 &&
+ if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
IT66121_CLK_BANK_PWROFF_TXCLK,
IT66121_CLK_BANK_PWROFF_TXCLK)) {
@@ -815,7 +816,7 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge,
if (it66121_configure_afe(ctx, adjusted_mode))
goto unlock;
- if (ctx->info->id == ID_IT66121 &&
+ if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
IT66121_CLK_BANK_PWROFF_TXCLK, 0)) {
goto unlock;
@@ -1384,8 +1385,6 @@ static int it66121_audio_startup(struct device *dev, void *data)
int ret;
struct it66121_ctx *ctx = dev_get_drvdata(dev);
- dev_dbg(dev, "%s\n", __func__);
-
mutex_lock(&ctx->lock);
ret = it661221_audio_output_enable(ctx, true);
if (ret)
@@ -1401,8 +1400,6 @@ static void it66121_audio_shutdown(struct device *dev, void *data)
int ret;
struct it66121_ctx *ctx = dev_get_drvdata(dev);
- dev_dbg(dev, "%s\n", __func__);
-
mutex_lock(&ctx->lock);
ret = it661221_audio_output_enable(ctx, false);
if (ret)
@@ -1479,8 +1476,6 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev)
.no_capture_mute = 1,
};
- dev_dbg(dev, "%s\n", __func__);
-
if (!of_property_present(dev->of_node, "#sound-dai-cells")) {
dev_info(dev, "No \"#sound-dai-cells\", no audio\n");
return 0;
@@ -1504,13 +1499,20 @@ static const char * const it66121_supplies[] = {
"vcn33", "vcn18", "vrf12"
};
+static const struct it66121_chip_info it66xx_chip_info[] = {
+ {.id = ID_IT6610, .vid = 0xca00, .pid = 0x0611 },
+ {.id = ID_IT66121, .vid = 0x4954, .pid = 0x0612 },
+ {.id = ID_IT66122, .vid = 0x4954, .pid = 0x0622 },
+};
+
static int it66121_probe(struct i2c_client *client)
{
u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 };
struct device_node *ep;
- int ret;
+ int ret, i;
struct it66121_ctx *ctx;
struct device *dev = &client->dev;
+ const struct it66121_chip_info *chip_info;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(dev, "I2C check functionality failed.\n");
@@ -1528,7 +1530,6 @@ static int it66121_probe(struct i2c_client *client)
ctx->dev = dev;
ctx->client = client;
- ctx->info = i2c_get_match_data(client);
of_property_read_u32(ep, "bus-width", &ctx->bus_width);
of_node_put(ep);
@@ -1574,11 +1575,18 @@ static int it66121_probe(struct i2c_client *client)
revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]);
device_ids[1] &= IT66121_DEVICE_ID1_MASK;
- if ((vendor_ids[1] << 8 | vendor_ids[0]) != ctx->info->vid ||
- (device_ids[1] << 8 | device_ids[0]) != ctx->info->pid) {
- return -ENODEV;
+ for (i = 0; i < ARRAY_SIZE(it66xx_chip_info); i++) {
+ chip_info = &it66xx_chip_info[i];
+ if ((vendor_ids[1] << 8 | vendor_ids[0]) == chip_info->vid &&
+ (device_ids[1] << 8 | device_ids[0]) == chip_info->pid) {
+ ctx->id = chip_info->id;
+ break;
+ }
}
+ if (i == ARRAY_SIZE(it66xx_chip_info))
+ return -ENODEV;
+
ctx->bridge.of_node = dev->of_node;
ctx->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
@@ -1612,28 +1620,18 @@ static void it66121_remove(struct i2c_client *client)
mutex_destroy(&ctx->lock);
}
-static const struct it66121_chip_info it66121_chip_info = {
- .id = ID_IT66121,
- .vid = 0x4954,
- .pid = 0x0612,
-};
-
-static const struct it66121_chip_info it6610_chip_info = {
- .id = ID_IT6610,
- .vid = 0xca00,
- .pid = 0x0611,
-};
-
static const struct of_device_id it66121_dt_match[] = {
- { .compatible = "ite,it66121", &it66121_chip_info },
- { .compatible = "ite,it6610", &it6610_chip_info },
+ { .compatible = "ite,it6610" },
+ { .compatible = "ite,it66121" },
+ { .compatible = "ite,it66122" },
{ }
};
MODULE_DEVICE_TABLE(of, it66121_dt_match);
static const struct i2c_device_id it66121_id[] = {
- { "it66121", (kernel_ulong_t) &it66121_chip_info },
- { "it6610", (kernel_ulong_t) &it6610_chip_info },
+ { .name = "it6610" },
+ { .name = "it66121" },
+ { .name = "it66122" },
{ }
};
MODULE_DEVICE_TABLE(i2c, it66121_id);
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index d537b1d036fb..1f0aba28ad1e 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -179,7 +179,6 @@ struct sii902x {
struct drm_connector connector;
struct gpio_desc *reset_gpio;
struct i2c_mux_core *i2cmux;
- bool sink_is_hdmi;
u32 bus_width;
/*
@@ -315,8 +314,6 @@ static int sii902x_get_modes(struct drm_connector *connector)
drm_edid_free(drm_edid);
}
- sii902x->sink_is_hdmi = connector->display_info.is_hdmi;
-
return num;
}
@@ -342,9 +339,17 @@ static void sii902x_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct sii902x *sii902x = bridge_to_sii902x(bridge);
+ struct drm_connector *connector;
+ u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (connector && connector->display_info.is_hdmi)
+ output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;
mutex_lock(&sii902x->mutex);
+ regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
+ SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);
regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL,
SII902X_AVI_POWER_STATE_MSK,
SII902X_AVI_POWER_STATE_D(0));
@@ -359,16 +364,12 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *adj)
{
struct sii902x *sii902x = bridge_to_sii902x(bridge);
- u8 output_mode = SII902X_SYS_CTRL_OUTPUT_DVI;
struct regmap *regmap = sii902x->regmap;
u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
struct hdmi_avi_infoframe frame;
u16 pixel_clock_10kHz = adj->clock / 10;
int ret;
- if (sii902x->sink_is_hdmi)
- output_mode = SII902X_SYS_CTRL_OUTPUT_HDMI;
-
buf[0] = pixel_clock_10kHz & 0xff;
buf[1] = pixel_clock_10kHz >> 8;
buf[2] = drm_mode_vrefresh(adj);
@@ -384,11 +385,6 @@ static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
mutex_lock(&sii902x->mutex);
- ret = regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
- SII902X_SYS_CTRL_OUTPUT_MODE, output_mode);
- if (ret)
- goto out;
-
ret = regmap_bulk_write(regmap, SII902X_TPI_VIDEO_DATA, buf, 10);
if (ret)
goto out;
diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
index e4d0bc2200f8..2cd1847ba776 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -262,6 +262,16 @@ static const struct of_device_id simple_bridge_match[] = {
.connector_type = DRM_MODE_CONNECTOR_VGA,
},
}, {
+ .compatible = "asl-tek,cs5263",
+ .data = &(const struct simple_bridge_info) {
+ .connector_type = DRM_MODE_CONNECTOR_HDMIA,
+ },
+ }, {
+ .compatible = "parade,ps185hdm",
+ .data = &(const struct simple_bridge_info) {
+ .connector_type = DRM_MODE_CONNECTOR_HDMIA,
+ },
+ }, {
.compatible = "radxa,ra620",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
diff --git a/drivers/gpu/drm/bridge/synopsys/Kconfig b/drivers/gpu/drm/bridge/synopsys/Kconfig
index 2c5e532410de..a46df7583bcf 100644
--- a/drivers/gpu/drm/bridge/synopsys/Kconfig
+++ b/drivers/gpu/drm/bridge/synopsys/Kconfig
@@ -61,6 +61,14 @@ config DRM_DW_HDMI_QP
select DRM_KMS_HELPER
select REGMAP_MMIO
+config DRM_DW_HDMI_QP_CEC
+ bool "Synopsis Designware QP CEC interface"
+ depends on DRM_DW_HDMI_QP
+ select DRM_DISPLAY_HDMI_CEC_HELPER
+ help
+ Support the CEC interface which is part of the Synopsys
+ Designware HDMI QP block.
+
config DRM_DW_MIPI_DSI
tristate
select DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 9bbfe8da3de0..82aaf74e1bc0 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -2049,6 +2049,8 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
bridge->ycbcr_420_allowed = true;
+ devm_drm_bridge_add(dev, bridge);
+
dp->aux.dev = dev;
dp->aux.drm_dev = encoder->dev;
dp->aux.name = dev_name(dev);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
index ab18f9a3bf23..df7a37eb47f4 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-gp-audio.c
@@ -90,6 +90,11 @@ static int audio_hw_params(struct device *dev, void *data,
params->iec.status[0] & IEC958_AES0_NONAUDIO);
dw_hdmi_set_sample_width(dw->data.hdmi, params->sample_width);
+ if (daifmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE)
+ dw_hdmi_set_sample_iec958(dw->data.hdmi, 1);
+ else
+ dw_hdmi_set_sample_iec958(dw->data.hdmi, 0);
+
return 0;
}
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index 39332c57f2c5..fe4c026280f0 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -18,6 +18,7 @@
#include <drm/bridge/dw_hdmi_qp.h>
#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_cec_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -26,6 +27,8 @@
#include <drm/drm_edid.h>
#include <drm/drm_modes.h>
+#include <media/cec.h>
+
#include <sound/hdmi-codec.h>
#include "dw-hdmi-qp.h"
@@ -131,17 +134,34 @@ struct dw_hdmi_qp_i2c {
bool is_segment;
};
+#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
+struct dw_hdmi_qp_cec {
+ struct drm_connector *connector;
+ int irq;
+ u32 addresses;
+ struct cec_msg rx_msg;
+ u8 tx_status;
+ bool tx_done;
+ bool rx_done;
+};
+#endif
+
struct dw_hdmi_qp {
struct drm_bridge bridge;
struct device *dev;
struct dw_hdmi_qp_i2c *i2c;
+#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
+ struct dw_hdmi_qp_cec *cec;
+#endif
+
struct {
const struct dw_hdmi_qp_phy_ops *ops;
void *data;
} phy;
+ unsigned long ref_clk_rate;
struct regmap *regm;
unsigned long tmds_char_rate;
@@ -848,8 +868,9 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
return;
if (connector->display_info.is_hdmi) {
- dev_dbg(hdmi->dev, "%s mode=HDMI rate=%llu\n",
- __func__, conn_state->hdmi.tmds_char_rate);
+ dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u\n", __func__,
+ drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format),
+ conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc);
op_mode = 0;
hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
} else {
@@ -965,6 +986,179 @@ static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
}
}
+#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
+static irqreturn_t dw_hdmi_qp_cec_hardirq(int irq, void *dev_id)
+{
+ struct dw_hdmi_qp *hdmi = dev_id;
+ struct dw_hdmi_qp_cec *cec = hdmi->cec;
+ irqreturn_t ret = IRQ_HANDLED;
+ u32 stat;
+
+ stat = dw_hdmi_qp_read(hdmi, CEC_INT_STATUS);
+ if (stat == 0)
+ return IRQ_NONE;
+
+ dw_hdmi_qp_write(hdmi, stat, CEC_INT_CLEAR);
+
+ if (stat & CEC_STAT_LINE_ERR) {
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_DONE) {
+ cec->tx_status = CEC_TX_STATUS_OK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CEC_STAT_NACK) {
+ cec->tx_status = CEC_TX_STATUS_NACK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (stat & CEC_STAT_EOM) {
+ unsigned int len, i, val;
+
+ val = dw_hdmi_qp_read(hdmi, CEC_RX_COUNT_STATUS);
+ len = (val & 0xf) + 1;
+
+ if (len > sizeof(cec->rx_msg.msg))
+ len = sizeof(cec->rx_msg.msg);
+
+ for (i = 0; i < 4; i++) {
+ val = dw_hdmi_qp_read(hdmi, CEC_RX_DATA3_0 + i * 4);
+ cec->rx_msg.msg[i * 4] = val & 0xff;
+ cec->rx_msg.msg[i * 4 + 1] = (val >> 8) & 0xff;
+ cec->rx_msg.msg[i * 4 + 2] = (val >> 16) & 0xff;
+ cec->rx_msg.msg[i * 4 + 3] = (val >> 24) & 0xff;
+ }
+
+ dw_hdmi_qp_write(hdmi, 1, CEC_LOCK_CONTROL);
+
+ cec->rx_msg.len = len;
+ cec->rx_done = true;
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static irqreturn_t dw_hdmi_qp_cec_thread(int irq, void *dev_id)
+{
+ struct dw_hdmi_qp *hdmi = dev_id;
+ struct dw_hdmi_qp_cec *cec = hdmi->cec;
+
+ if (cec->tx_done) {
+ cec->tx_done = false;
+ drm_connector_hdmi_cec_transmit_attempt_done(cec->connector,
+ cec->tx_status);
+ }
+
+ if (cec->rx_done) {
+ cec->rx_done = false;
+ drm_connector_hdmi_cec_received_msg(cec->connector, &cec->rx_msg);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int dw_hdmi_qp_cec_init(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
+ struct dw_hdmi_qp_cec *cec = hdmi->cec;
+
+ cec->connector = connector;
+
+ dw_hdmi_qp_write(hdmi, 0, CEC_TX_COUNT);
+ dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
+ dw_hdmi_qp_write(hdmi, 0, CEC_INT_MASK_N);
+
+ return devm_request_threaded_irq(hdmi->dev, cec->irq,
+ dw_hdmi_qp_cec_hardirq,
+ dw_hdmi_qp_cec_thread, IRQF_SHARED,
+ dev_name(hdmi->dev), hdmi);
+}
+
+static int dw_hdmi_qp_cec_log_addr(struct drm_bridge *bridge, u8 logical_addr)
+{
+ struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
+ struct dw_hdmi_qp_cec *cec = hdmi->cec;
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ cec->addresses = 0;
+ else
+ cec->addresses |= BIT(logical_addr) | CEC_ADDR_BROADCAST;
+
+ dw_hdmi_qp_write(hdmi, cec->addresses, CEC_ADDR);
+
+ return 0;
+}
+
+static int dw_hdmi_qp_cec_enable(struct drm_bridge *bridge, bool enable)
+{
+ struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
+ unsigned int irqs;
+ u32 swdisable;
+
+ if (!enable) {
+ dw_hdmi_qp_write(hdmi, 0, CEC_INT_MASK_N);
+ dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
+
+ swdisable = dw_hdmi_qp_read(hdmi, GLOBAL_SWDISABLE);
+ swdisable = swdisable | CEC_SWDISABLE;
+ dw_hdmi_qp_write(hdmi, swdisable, GLOBAL_SWDISABLE);
+ } else {
+ swdisable = dw_hdmi_qp_read(hdmi, GLOBAL_SWDISABLE);
+ swdisable = swdisable & ~CEC_SWDISABLE;
+ dw_hdmi_qp_write(hdmi, swdisable, GLOBAL_SWDISABLE);
+
+ dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
+ dw_hdmi_qp_write(hdmi, 1, CEC_LOCK_CONTROL);
+
+ dw_hdmi_qp_cec_log_addr(bridge, CEC_LOG_ADDR_INVALID);
+
+ irqs = CEC_STAT_LINE_ERR | CEC_STAT_NACK | CEC_STAT_EOM |
+ CEC_STAT_DONE;
+ dw_hdmi_qp_write(hdmi, ~0, CEC_INT_CLEAR);
+ dw_hdmi_qp_write(hdmi, irqs, CEC_INT_MASK_N);
+ }
+
+ return 0;
+}
+
+static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct dw_hdmi_qp *hdmi = dw_hdmi_qp_from_bridge(bridge);
+ unsigned int i;
+ u32 val;
+
+ for (i = 0; i < msg->len; i++) {
+ if (!(i % 4))
+ val = msg->msg[i];
+ if ((i % 4) == 1)
+ val |= msg->msg[i] << 8;
+ if ((i % 4) == 2)
+ val |= msg->msg[i] << 16;
+ if ((i % 4) == 3)
+ val |= msg->msg[i] << 24;
+
+ if (i == (msg->len - 1) || (i % 4) == 3)
+ dw_hdmi_qp_write(hdmi, val, CEC_TX_DATA3_0 + (i / 4) * 4);
+ }
+
+ dw_hdmi_qp_write(hdmi, msg->len - 1, CEC_TX_COUNT);
+ dw_hdmi_qp_write(hdmi, CEC_CTRL_START, CEC_TX_CONTROL);
+
+ return 0;
+}
+#else
+#define dw_hdmi_qp_cec_init NULL
+#define dw_hdmi_qp_cec_enable NULL
+#define dw_hdmi_qp_cec_log_addr NULL
+#define dw_hdmi_qp_cec_transmit NULL
+#endif /* CONFIG_DRM_DW_HDMI_QP_CEC */
+
static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@@ -979,6 +1173,10 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.hdmi_audio_startup = dw_hdmi_qp_audio_enable,
.hdmi_audio_shutdown = dw_hdmi_qp_audio_disable,
.hdmi_audio_prepare = dw_hdmi_qp_audio_prepare,
+ .hdmi_cec_init = dw_hdmi_qp_cec_init,
+ .hdmi_cec_enable = dw_hdmi_qp_cec_enable,
+ .hdmi_cec_log_addr = dw_hdmi_qp_cec_log_addr,
+ .hdmi_cec_transmit = dw_hdmi_qp_cec_transmit,
};
static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
@@ -1014,13 +1212,11 @@ static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi)
{
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_0_INT_MASK_N);
dw_hdmi_qp_write(hdmi, 0, MAINUNIT_1_INT_MASK_N);
- dw_hdmi_qp_write(hdmi, 428571429, TIMER_BASE_CONFIG0);
+ dw_hdmi_qp_write(hdmi, hdmi->ref_clk_rate, TIMER_BASE_CONFIG0);
/* Software reset */
dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0);
-
dw_hdmi_qp_write(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
-
dw_hdmi_qp_mod(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
/* Clear DONE and ERROR interrupts */
@@ -1066,6 +1262,13 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
hdmi->phy.ops = plat_data->phy_ops;
hdmi->phy.data = plat_data->phy_data;
+ if (plat_data->ref_clk_rate) {
+ hdmi->ref_clk_rate = plat_data->ref_clk_rate;
+ } else {
+ hdmi->ref_clk_rate = 428571429;
+ dev_warn(dev, "Set ref_clk_rate to vendor default\n");
+ }
+
dw_hdmi_qp_init_hw(hdmi);
ret = devm_request_threaded_irq(dev, plat_data->main_irq,
@@ -1085,6 +1288,12 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
hdmi->bridge.vendor = "Synopsys";
hdmi->bridge.product = "DW HDMI QP TX";
+ if (plat_data->supported_formats)
+ hdmi->bridge.supported_formats = plat_data->supported_formats;
+
+ if (plat_data->max_bpc)
+ hdmi->bridge.max_bpc = plat_data->max_bpc;
+
hdmi->bridge.ddc = dw_hdmi_qp_i2c_adapter(hdmi);
if (IS_ERR(hdmi->bridge.ddc))
return ERR_CAST(hdmi->bridge.ddc);
@@ -1093,6 +1302,22 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
hdmi->bridge.hdmi_audio_dev = dev;
hdmi->bridge.hdmi_audio_dai_port = 1;
+#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
+ if (plat_data->cec_irq) {
+ hdmi->bridge.ops |= DRM_BRIDGE_OP_HDMI_CEC_ADAPTER;
+ hdmi->bridge.hdmi_cec_dev = dev;
+ hdmi->bridge.hdmi_cec_adapter_name = dev_name(dev);
+
+ hdmi->cec = devm_kzalloc(hdmi->dev, sizeof(*hdmi->cec), GFP_KERNEL);
+ if (!hdmi->cec)
+ return ERR_PTR(-ENOMEM);
+
+ hdmi->cec->irq = plat_data->cec_irq;
+ } else {
+ dev_warn(dev, "Disabled CEC support due to missing IRQ\n");
+ }
+#endif
+
ret = devm_drm_bridge_add(dev, &hdmi->bridge);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
index 72987e6c4689..91a15f82e32a 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
@@ -488,9 +488,23 @@
#define AUDPKT_VBIT_OVR0 0xf24
/* CEC Registers */
#define CEC_TX_CONTROL 0x1000
+#define CEC_CTRL_CLEAR BIT(0)
+#define CEC_CTRL_START BIT(0)
#define CEC_STATUS 0x1004
+#define CEC_STAT_DONE BIT(0)
+#define CEC_STAT_NACK BIT(1)
+#define CEC_STAT_ARBLOST BIT(2)
+#define CEC_STAT_LINE_ERR BIT(3)
+#define CEC_STAT_RETRANS_FAIL BIT(4)
+#define CEC_STAT_DISCARD BIT(5)
+#define CEC_STAT_TX_BUSY BIT(8)
+#define CEC_STAT_RX_BUSY BIT(9)
+#define CEC_STAT_DRIVE_ERR BIT(10)
+#define CEC_STAT_EOM BIT(11)
+#define CEC_STAT_NOTIFY_ERR BIT(12)
#define CEC_CONFIG 0x1008
#define CEC_ADDR 0x100c
+#define CEC_ADDR_BROADCAST BIT(15)
#define CEC_TX_COUNT 0x1020
#define CEC_TX_DATA3_0 0x1024
#define CEC_TX_DATA7_4 0x1028
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 206b099a35e9..3b77e73ac0ea 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -177,6 +177,7 @@ struct dw_hdmi {
spinlock_t audio_lock;
struct mutex audio_mutex;
+ unsigned int sample_iec958;
unsigned int sample_non_pcm;
unsigned int sample_width;
unsigned int sample_rate;
@@ -198,6 +199,12 @@ struct dw_hdmi {
enum drm_connector_status last_connector_result;
};
+const struct dw_hdmi_plat_data *dw_hdmi_to_plat_data(struct dw_hdmi *hdmi)
+{
+ return hdmi->plat_data;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_to_plat_data);
+
#define HDMI_IH_PHY_STAT0_RX_SENSE \
(HDMI_IH_PHY_STAT0_RX_SENSE0 | HDMI_IH_PHY_STAT0_RX_SENSE1 | \
HDMI_IH_PHY_STAT0_RX_SENSE2 | HDMI_IH_PHY_STAT0_RX_SENSE3)
@@ -712,6 +719,14 @@ void dw_hdmi_set_sample_non_pcm(struct dw_hdmi *hdmi, unsigned int non_pcm)
}
EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_non_pcm);
+void dw_hdmi_set_sample_iec958(struct dw_hdmi *hdmi, unsigned int iec958)
+{
+ mutex_lock(&hdmi->audio_mutex);
+ hdmi->sample_iec958 = iec958;
+ mutex_unlock(&hdmi->audio_mutex);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_iec958);
+
void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
{
mutex_lock(&hdmi->audio_mutex);
@@ -843,7 +858,8 @@ static void dw_hdmi_gp_audio_enable(struct dw_hdmi *hdmi)
hdmi->channels,
hdmi->sample_width,
hdmi->sample_rate,
- hdmi->sample_non_pcm);
+ hdmi->sample_non_pcm,
+ hdmi->sample_iec958);
}
static void dw_hdmi_gp_audio_disable(struct dw_hdmi *hdmi)
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index ae0d08e5e960..276d05d25ad8 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -106,10 +106,21 @@
#define SN_PWM_EN_INV_REG 0xA5
#define SN_PWM_INV_MASK BIT(0)
#define SN_PWM_EN_MASK BIT(1)
+
+#define SN_IRQ_EN_REG 0xE0
+#define IRQ_EN BIT(0)
+
+#define SN_IRQ_EVENTS_EN_REG 0xE6
+#define HPD_INSERTION_EN BIT(1)
+#define HPD_REMOVAL_EN BIT(2)
+
#define SN_AUX_CMD_STATUS_REG 0xF4
#define AUX_IRQ_STATUS_AUX_RPLY_TOUT BIT(3)
#define AUX_IRQ_STATUS_AUX_SHORT BIT(5)
#define AUX_IRQ_STATUS_NAT_I2C_FAIL BIT(6)
+#define SN_IRQ_STATUS_REG 0xF5
+#define HPD_REMOVAL_STATUS BIT(2)
+#define HPD_INSERTION_STATUS BIT(1)
#define MIN_DSI_CLK_FREQ_MHZ 40
@@ -152,7 +163,9 @@
* @ln_assign: Value to program to the LN_ASSIGN register.
* @ln_polrs: Value for the 4-bit LN_POLRS field of SN_ENH_FRAME_REG.
* @comms_enabled: If true then communication over the aux channel is enabled.
+ * @hpd_enabled: If true then HPD events are enabled.
* @comms_mutex: Protects modification of comms_enabled.
+ * @hpd_mutex: Protects modification of hpd_enabled.
*
* @gchip: If we expose our GPIOs, this is used.
* @gchip_output: A cache of whether we've set GPIOs to output. This
@@ -190,7 +203,9 @@ struct ti_sn65dsi86 {
u8 ln_assign;
u8 ln_polrs;
bool comms_enabled;
+ bool hpd_enabled;
struct mutex comms_mutex;
+ struct mutex hpd_mutex;
#if defined(CONFIG_OF_GPIO)
struct gpio_chip gchip;
@@ -221,6 +236,23 @@ static const struct regmap_config ti_sn65dsi86_regmap_config = {
.max_register = 0xFF,
};
+static int ti_sn65dsi86_read_u8(struct ti_sn65dsi86 *pdata, unsigned int reg,
+ u8 *val)
+{
+ int ret;
+ unsigned int reg_val;
+
+ ret = regmap_read(pdata->regmap, reg, &reg_val);
+ if (ret) {
+ dev_err(pdata->dev, "fail to read raw reg %#x: %d\n",
+ reg, ret);
+ return ret;
+ }
+ *val = (u8)reg_val;
+
+ return 0;
+}
+
static int __maybe_unused ti_sn65dsi86_read_u16(struct ti_sn65dsi86 *pdata,
unsigned int reg, u16 *val)
{
@@ -379,6 +411,7 @@ static void ti_sn65dsi86_disable_comms(struct ti_sn65dsi86 *pdata)
static int __maybe_unused ti_sn65dsi86_resume(struct device *dev)
{
struct ti_sn65dsi86 *pdata = dev_get_drvdata(dev);
+ const struct i2c_client *client = to_i2c_client(pdata->dev);
int ret;
ret = regulator_bulk_enable(SN_REGULATOR_SUPPLY_NUM, pdata->supplies);
@@ -413,6 +446,13 @@ static int __maybe_unused ti_sn65dsi86_resume(struct device *dev)
if (pdata->refclk)
ti_sn65dsi86_enable_comms(pdata, NULL);
+ if (client->irq) {
+ ret = regmap_update_bits(pdata->regmap, SN_IRQ_EN_REG, IRQ_EN,
+ IRQ_EN);
+ if (ret)
+ dev_err(pdata->dev, "Failed to enable IRQ events: %d\n", ret);
+ }
+
return ret;
}
@@ -1211,6 +1251,8 @@ static void ti_sn65dsi86_debugfs_init(struct drm_bridge *bridge, struct dentry *
static void ti_sn_bridge_hpd_enable(struct drm_bridge *bridge)
{
struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+ const struct i2c_client *client = to_i2c_client(pdata->dev);
+ int ret;
/*
* Device needs to be powered on before reading the HPD state
@@ -1219,11 +1261,35 @@ static void ti_sn_bridge_hpd_enable(struct drm_bridge *bridge)
*/
pm_runtime_get_sync(pdata->dev);
+
+ mutex_lock(&pdata->hpd_mutex);
+ pdata->hpd_enabled = true;
+ mutex_unlock(&pdata->hpd_mutex);
+
+ if (client->irq) {
+ ret = regmap_set_bits(pdata->regmap, SN_IRQ_EVENTS_EN_REG,
+ HPD_REMOVAL_EN | HPD_INSERTION_EN);
+ if (ret)
+ dev_err(pdata->dev, "Failed to enable HPD events: %d\n", ret);
+ }
}
static void ti_sn_bridge_hpd_disable(struct drm_bridge *bridge)
{
struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+ const struct i2c_client *client = to_i2c_client(pdata->dev);
+ int ret;
+
+ if (client->irq) {
+ ret = regmap_clear_bits(pdata->regmap, SN_IRQ_EVENTS_EN_REG,
+ HPD_REMOVAL_EN | HPD_INSERTION_EN);
+ if (ret)
+ dev_err(pdata->dev, "Failed to disable HPD events: %d\n", ret);
+ }
+
+ mutex_lock(&pdata->hpd_mutex);
+ pdata->hpd_enabled = false;
+ mutex_unlock(&pdata->hpd_mutex);
pm_runtime_put_autosuspend(pdata->dev);
}
@@ -1309,6 +1375,41 @@ static int ti_sn_bridge_parse_dsi_host(struct ti_sn65dsi86 *pdata)
return 0;
}
+static irqreturn_t ti_sn_bridge_interrupt(int irq, void *private)
+{
+ struct ti_sn65dsi86 *pdata = private;
+ struct drm_device *dev = pdata->bridge.dev;
+ u8 status;
+ int ret;
+ bool hpd_event;
+
+ ret = ti_sn65dsi86_read_u8(pdata, SN_IRQ_STATUS_REG, &status);
+ if (ret) {
+ dev_err(pdata->dev, "Failed to read IRQ status: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ hpd_event = status & (HPD_REMOVAL_STATUS | HPD_INSERTION_STATUS);
+
+ dev_dbg(pdata->dev, "(SN_IRQ_STATUS_REG = %#x)\n", status);
+ if (!status)
+ return IRQ_NONE;
+
+ ret = regmap_write(pdata->regmap, SN_IRQ_STATUS_REG, status);
+ if (ret) {
+ dev_err(pdata->dev, "Failed to clear IRQ status: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ /* Only send the HPD event if we are bound with a device. */
+ mutex_lock(&pdata->hpd_mutex);
+ if (pdata->hpd_enabled && hpd_event)
+ drm_kms_helper_hotplug_event(dev);
+ mutex_unlock(&pdata->hpd_mutex);
+
+ return IRQ_HANDLED;
+}
+
static int ti_sn_bridge_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
@@ -1931,6 +2032,7 @@ static int ti_sn65dsi86_probe(struct i2c_client *client)
dev_set_drvdata(dev, pdata);
pdata->dev = dev;
+ mutex_init(&pdata->hpd_mutex);
mutex_init(&pdata->comms_mutex);
pdata->regmap = devm_regmap_init_i2c(client,
@@ -1971,6 +2073,16 @@ static int ti_sn65dsi86_probe(struct i2c_client *client)
if (strncmp(id_buf, "68ISD ", ARRAY_SIZE(id_buf)))
return dev_err_probe(dev, -EOPNOTSUPP, "unsupported device id\n");
+ if (client->irq) {
+ ret = devm_request_threaded_irq(pdata->dev, client->irq, NULL,
+ ti_sn_bridge_interrupt,
+ IRQF_ONESHOT,
+ dev_name(pdata->dev), pdata);
+
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request interrupt\n");
+ }
+
/*
* Break ourselves up into a collection of aux devices. The only real
* motiviation here is to solve the chicken-and-egg problem of probe
diff --git a/drivers/gpu/drm/ci/gitlab-ci.yml b/drivers/gpu/drm/ci/gitlab-ci.yml
index d502d146b177..56638814bb28 100644
--- a/drivers/gpu/drm/ci/gitlab-ci.yml
+++ b/drivers/gpu/drm/ci/gitlab-ci.yml
@@ -280,7 +280,7 @@ sanity:
GIT_STRATEGY: none
script:
# ci-fairy check-commits --junit-xml=check-commits.xml
- - ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-merge-request.xml
+ # - ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-merge-request.xml
- |
set -eu
image_tags=(
diff --git a/drivers/gpu/drm/clients/drm_client_setup.c b/drivers/gpu/drm/clients/drm_client_setup.c
index 72480db1f00d..515aceac22b1 100644
--- a/drivers/gpu/drm/clients/drm_client_setup.c
+++ b/drivers/gpu/drm/clients/drm_client_setup.c
@@ -13,8 +13,8 @@
static char drm_client_default[16] = CONFIG_DRM_CLIENT_DEFAULT;
module_param_string(active, drm_client_default, sizeof(drm_client_default), 0444);
MODULE_PARM_DESC(active,
- "Choose which drm client to start, default is"
- CONFIG_DRM_CLIENT_DEFAULT "]");
+ "Choose which drm client to start, default is "
+ CONFIG_DRM_CLIENT_DEFAULT);
/**
* drm_client_setup() - Setup in-kernel DRM clients
diff --git a/drivers/gpu/drm/clients/drm_fbdev_client.c b/drivers/gpu/drm/clients/drm_fbdev_client.c
index f894ba52bdb5..28951e392482 100644
--- a/drivers/gpu/drm/clients/drm_fbdev_client.c
+++ b/drivers/gpu/drm/clients/drm_fbdev_client.c
@@ -13,22 +13,36 @@
* struct drm_client_funcs
*/
+static void drm_fbdev_client_free(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+
+ drm_fb_helper_unprepare(fb_helper);
+ kfree(fb_helper);
+}
+
static void drm_fbdev_client_unregister(struct drm_client_dev *client)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
if (fb_helper->info) {
+ /*
+ * Fully probed framebuffer device
+ */
drm_fb_helper_unregister_info(fb_helper);
} else {
+ /*
+ * Partially initialized client, no framebuffer device yet
+ */
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
}
-static int drm_fbdev_client_restore(struct drm_client_dev *client)
+static int drm_fbdev_client_restore(struct drm_client_dev *client, bool force)
{
- drm_fb_helper_lastclose(client->dev);
+ struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+
+ drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
return 0;
}
@@ -62,32 +76,27 @@ err_drm_err:
return ret;
}
-static int drm_fbdev_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_fbdev_client_suspend(struct drm_client_dev *client)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
- if (holds_console_lock)
- drm_fb_helper_set_suspend(fb_helper, true);
- else
- drm_fb_helper_set_suspend_unlocked(fb_helper, true);
+ drm_fb_helper_set_suspend_unlocked(fb_helper, true);
return 0;
}
-static int drm_fbdev_client_resume(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_fbdev_client_resume(struct drm_client_dev *client)
{
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
- if (holds_console_lock)
- drm_fb_helper_set_suspend(fb_helper, false);
- else
- drm_fb_helper_set_suspend_unlocked(fb_helper, false);
+ drm_fb_helper_set_suspend_unlocked(fb_helper, false);
return 0;
}
static const struct drm_client_funcs drm_fbdev_client_funcs = {
.owner = THIS_MODULE,
+ .free = drm_fbdev_client_free,
.unregister = drm_fbdev_client_unregister,
.restore = drm_fbdev_client_restore,
.hotplug = drm_fbdev_client_hotplug,
diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c
index d239f1e3c456..4d3005273b27 100644
--- a/drivers/gpu/drm/clients/drm_log.c
+++ b/drivers/gpu/drm/clients/drm_log.c
@@ -100,7 +100,7 @@ static void drm_log_clear_line(struct drm_log_scanout *scanout, u32 line)
return;
iosys_map_memset(&map, r.y1 * fb->pitches[0], 0, height * fb->pitches[0]);
drm_client_buffer_vunmap_local(scanout->buffer);
- drm_client_framebuffer_flush(scanout->buffer, &r);
+ drm_client_buffer_flush(scanout->buffer, &r);
}
static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
@@ -133,7 +133,7 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s,
if (scanout->line >= scanout->rows)
scanout->line = 0;
drm_client_buffer_vunmap_local(scanout->buffer);
- drm_client_framebuffer_flush(scanout->buffer, &r);
+ drm_client_buffer_flush(scanout->buffer, &r);
}
static void drm_log_draw_new_line(struct drm_log_scanout *scanout,
@@ -204,7 +204,7 @@ static int drm_log_setup_modeset(struct drm_client_dev *client,
if (format == DRM_FORMAT_INVALID)
return -EINVAL;
- scanout->buffer = drm_client_framebuffer_create(client, width, height, format);
+ scanout->buffer = drm_client_buffer_create_dumb(client, width, height, format);
if (IS_ERR(scanout->buffer)) {
drm_warn(client->dev, "drm_log can't create framebuffer %d %d %p4cc\n",
width, height, &format);
@@ -272,7 +272,7 @@ static void drm_log_init_client(struct drm_log *dlog)
err_failed_commit:
for (i = 0; i < n_modeset; i++)
- drm_client_framebuffer_delete(dlog->scanout[i].buffer);
+ drm_client_buffer_delete(dlog->scanout[i].buffer);
err_nomodeset:
kfree(dlog->scanout);
@@ -286,26 +286,45 @@ static void drm_log_free_scanout(struct drm_client_dev *client)
if (dlog->n_scanout) {
for (i = 0; i < dlog->n_scanout; i++)
- drm_client_framebuffer_delete(dlog->scanout[i].buffer);
+ drm_client_buffer_delete(dlog->scanout[i].buffer);
dlog->n_scanout = 0;
kfree(dlog->scanout);
dlog->scanout = NULL;
}
}
-static void drm_log_client_unregister(struct drm_client_dev *client)
+static void drm_log_client_free(struct drm_client_dev *client)
{
struct drm_log *dlog = client_to_drm_log(client);
struct drm_device *dev = client->dev;
+ kfree(dlog);
+
+ drm_dbg(dev, "Unregistered with drm log\n");
+}
+
+static void drm_log_client_unregister(struct drm_client_dev *client)
+{
+ struct drm_log *dlog = client_to_drm_log(client);
+
unregister_console(&dlog->con);
mutex_lock(&dlog->lock);
drm_log_free_scanout(client);
- drm_client_release(client);
mutex_unlock(&dlog->lock);
- kfree(dlog);
- drm_dbg(dev, "Unregistered with drm log\n");
+ drm_client_release(client);
+}
+
+static int drm_log_client_restore(struct drm_client_dev *client, bool force)
+{
+ int ret;
+
+ if (force)
+ ret = drm_client_modeset_commit_locked(client);
+ else
+ ret = drm_client_modeset_commit(client);
+
+ return ret;
}
static int drm_log_client_hotplug(struct drm_client_dev *client)
@@ -319,7 +338,7 @@ static int drm_log_client_hotplug(struct drm_client_dev *client)
return 0;
}
-static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_lock)
+static int drm_log_client_suspend(struct drm_client_dev *client)
{
struct drm_log *dlog = client_to_drm_log(client);
@@ -328,7 +347,7 @@ static int drm_log_client_suspend(struct drm_client_dev *client, bool _console_l
return 0;
}
-static int drm_log_client_resume(struct drm_client_dev *client, bool _console_lock)
+static int drm_log_client_resume(struct drm_client_dev *client)
{
struct drm_log *dlog = client_to_drm_log(client);
@@ -339,7 +358,9 @@ static int drm_log_client_resume(struct drm_client_dev *client, bool _console_lo
static const struct drm_client_funcs drm_log_client_funcs = {
.owner = THIS_MODULE,
+ .free = drm_log_client_free,
.unregister = drm_log_client_unregister,
+ .restore = drm_log_client_restore,
.hotplug = drm_log_client_hotplug,
.suspend = drm_log_client_suspend,
.resume = drm_log_client_resume,
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index baacd21e7341..a2d30cf9e06d 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -137,10 +137,9 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
- struct drm_bridge *bridge;
/* Notify all bridges in the pipeline of hotplug events. */
- drm_for_each_bridge_in_chain(bridge_connector->encoder, bridge) {
+ drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) {
if (bridge->funcs->hpd_notify)
bridge->funcs->hpd_notify(bridge, status);
}
@@ -619,6 +618,20 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
* Bridge Connector Initialisation
*/
+static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+{
+ struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
+
+ drm_bridge_put(bridge_connector->bridge_edid);
+ drm_bridge_put(bridge_connector->bridge_hpd);
+ drm_bridge_put(bridge_connector->bridge_detect);
+ drm_bridge_put(bridge_connector->bridge_modes);
+ drm_bridge_put(bridge_connector->bridge_hdmi);
+ drm_bridge_put(bridge_connector->bridge_hdmi_audio);
+ drm_bridge_put(bridge_connector->bridge_dp_audio);
+ drm_bridge_put(bridge_connector->bridge_hdmi_cec);
+}
+
/**
* drm_bridge_connector_init - Initialise a connector for a chain of bridges
* @drm: the DRM device
@@ -639,7 +652,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
struct drm_bridge_connector *bridge_connector;
struct drm_connector *connector;
struct i2c_adapter *ddc = NULL;
- struct drm_bridge *bridge, *panel_bridge = NULL;
+ struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL;
unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
unsigned int max_bpc = 8;
bool support_hdcp = false;
@@ -650,6 +663,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (!bridge_connector)
return ERR_PTR(-ENOMEM);
+ ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
+ if (ret)
+ return ERR_PTR(ret);
+
bridge_connector->encoder = encoder;
/*
@@ -667,20 +684,28 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
* detection are available, we don't support hotplug detection at all.
*/
connector_type = DRM_MODE_CONNECTOR_Unknown;
- drm_for_each_bridge_in_chain(encoder, bridge) {
+ drm_for_each_bridge_in_chain_scoped(encoder, bridge) {
if (!bridge->interlace_allowed)
connector->interlace_allowed = false;
if (!bridge->ycbcr_420_allowed)
connector->ycbcr_420_allowed = false;
- if (bridge->ops & DRM_BRIDGE_OP_EDID)
- bridge_connector->bridge_edid = bridge;
- if (bridge->ops & DRM_BRIDGE_OP_HPD)
- bridge_connector->bridge_hpd = bridge;
- if (bridge->ops & DRM_BRIDGE_OP_DETECT)
- bridge_connector->bridge_detect = bridge;
- if (bridge->ops & DRM_BRIDGE_OP_MODES)
- bridge_connector->bridge_modes = bridge;
+ if (bridge->ops & DRM_BRIDGE_OP_EDID) {
+ drm_bridge_put(bridge_connector->bridge_edid);
+ bridge_connector->bridge_edid = drm_bridge_get(bridge);
+ }
+ if (bridge->ops & DRM_BRIDGE_OP_HPD) {
+ drm_bridge_put(bridge_connector->bridge_hpd);
+ bridge_connector->bridge_hpd = drm_bridge_get(bridge);
+ }
+ if (bridge->ops & DRM_BRIDGE_OP_DETECT) {
+ drm_bridge_put(bridge_connector->bridge_detect);
+ bridge_connector->bridge_detect = drm_bridge_get(bridge);
+ }
+ if (bridge->ops & DRM_BRIDGE_OP_MODES) {
+ drm_bridge_put(bridge_connector->bridge_modes);
+ bridge_connector->bridge_modes = drm_bridge_get(bridge);
+ }
if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
if (bridge_connector->bridge_hdmi)
return ERR_PTR(-EBUSY);
@@ -688,7 +713,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
!bridge->funcs->hdmi_clear_infoframe)
return ERR_PTR(-EINVAL);
- bridge_connector->bridge_hdmi = bridge;
+ bridge_connector->bridge_hdmi = drm_bridge_get(bridge);
if (bridge->supported_formats)
supported_formats = bridge->supported_formats;
@@ -711,7 +736,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
!bridge->funcs->hdmi_audio_shutdown)
return ERR_PTR(-EINVAL);
- bridge_connector->bridge_hdmi_audio = bridge;
+ bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
@@ -729,21 +754,21 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
!bridge->funcs->dp_audio_shutdown)
return ERR_PTR(-EINVAL);
- bridge_connector->bridge_dp_audio = bridge;
+ bridge_connector->bridge_dp_audio = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
if (bridge_connector->bridge_hdmi_cec)
return ERR_PTR(-EBUSY);
- bridge_connector->bridge_hdmi_cec = bridge;
+ bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
}
if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
if (bridge_connector->bridge_hdmi_cec)
return ERR_PTR(-EBUSY);
- bridge_connector->bridge_hdmi_cec = bridge;
+ bridge_connector->bridge_hdmi_cec = drm_bridge_get(bridge);
if (!bridge->funcs->hdmi_cec_enable ||
!bridge->funcs->hdmi_cec_log_addr ||
@@ -762,8 +787,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (bridge->ddc)
ddc = bridge->ddc;
- if (drm_bridge_is_panel(bridge))
- panel_bridge = bridge;
+ if (drm_bridge_is_panel(bridge)) {
+ drm_bridge_put(panel_bridge);
+ panel_bridge = drm_bridge_get(bridge);
+ }
if (bridge->support_hdcp)
support_hdcp = true;
@@ -818,7 +845,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (bridge_connector->bridge_hdmi_cec &&
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
- bridge = bridge_connector->bridge_hdmi_cec;
+ struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
ret = drmm_connector_hdmi_cec_notifier_register(connector,
NULL,
@@ -829,7 +856,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
if (bridge_connector->bridge_hdmi_cec &&
bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
- bridge = bridge_connector->bridge_hdmi_cec;
+ struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
ret = drmm_connector_hdmi_cec_register(connector,
&drm_bridge_connector_hdmi_cec_funcs,
diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c
index 4aaeae4fa03c..f9fdf19de74a 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -29,6 +29,7 @@
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
@@ -123,6 +124,14 @@ bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
}
EXPORT_SYMBOL(drm_dp_clock_recovery_ok);
+bool drm_dp_post_lt_adj_req_in_progress(const u8 link_status[DP_LINK_STATUS_SIZE])
+{
+ u8 lane_align = dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED);
+
+ return lane_align & DP_POST_LT_ADJ_REQ_IN_PROGRESS;
+}
+EXPORT_SYMBOL(drm_dp_post_lt_adj_req_in_progress);
+
u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
int lane)
{
@@ -2543,6 +2552,10 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
{ OUI(0x00, 0x0C, 0xE7), DEVICE_ID_ANY, false, BIT(DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC) },
/* Apple MacBookPro 2017 15 inch eDP Retina panel reports too low DP_MAX_LINK_RATE */
{ OUI(0x00, 0x10, 0xfa), DEVICE_ID(101, 68, 21, 101, 98, 97), false, BIT(DP_DPCD_QUIRK_CAN_DO_MAX_LINK_RATE_3_24_GBPS) },
+ /* Synaptics Panamera supports only a compressed bpp of 12 above 50% of its max DSC pixel throughput */
+ { OUI(0x90, 0xCC, 0x24), DEVICE_ID('S', 'Y', 'N', 'A', 0x53, 0x22), true, BIT(DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) },
+ { OUI(0x90, 0xCC, 0x24), DEVICE_ID('S', 'Y', 'N', 'A', 0x53, 0x31), true, BIT(DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) },
+ { OUI(0x90, 0xCC, 0x24), DEVICE_ID('S', 'Y', 'N', 'A', 0x53, 0x33), true, BIT(DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) },
};
#undef OUI
@@ -2832,6 +2845,158 @@ int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_S
}
EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);
+/*
+ * See DP Standard v2.1a 2.8.4 Minimum Slices/Display, Table 2-159 and
+ * Appendix L.1 Derivation of Slice Count Requirements.
+ */
+static int dsc_sink_min_slice_throughput(int peak_pixel_rate)
+{
+ if (peak_pixel_rate >= 4800000)
+ return 600000;
+ else if (peak_pixel_rate >= 2700000)
+ return 400000;
+ else
+ return 340000;
+}
+
+/**
+ * drm_dp_dsc_sink_max_slice_throughput() - Get a DSC sink's maximum pixel throughput per slice
+ * @dsc_dpcd: DSC sink's capabilities from DPCD
+ * @peak_pixel_rate: Cumulative peak pixel rate in kHz
+ * @is_rgb_yuv444: The mode is either RGB or YUV444
+ *
+ * Return the DSC sink device's maximum pixel throughput per slice, based on
+ * the device's @dsc_dpcd capabilities, the @peak_pixel_rate of the transferred
+ * stream(s) and whether the output format @is_rgb_yuv444 or yuv422/yuv420.
+ *
+ * Note that @peak_pixel_rate is the total pixel rate transferred to the same
+ * DSC/display sink. For instance to calculate a tile's slice count of an MST
+ * multi-tiled display sink (not considering here the required
+ * rounding/alignment of slice count)::
+ *
+ * @peak_pixel_rate = tile_pixel_rate * tile_count
+ * total_slice_count = @peak_pixel_rate / drm_dp_dsc_sink_max_slice_throughput(@peak_pixel_rate)
+ * tile_slice_count = total_slice_count / tile_count
+ *
+ * Returns:
+ * The maximum pixel throughput per slice supported by the DSC sink device
+ * in kPixels/sec.
+ */
+int drm_dp_dsc_sink_max_slice_throughput(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE],
+ int peak_pixel_rate, bool is_rgb_yuv444)
+{
+ int throughput;
+ int delta = 0;
+ int base;
+
+ throughput = dsc_dpcd[DP_DSC_PEAK_THROUGHPUT - DP_DSC_SUPPORT];
+
+ if (is_rgb_yuv444) {
+ throughput = (throughput & DP_DSC_THROUGHPUT_MODE_0_MASK) >>
+ DP_DSC_THROUGHPUT_MODE_0_SHIFT;
+
+ delta = ((dsc_dpcd[DP_DSC_RC_BUF_BLK_SIZE - DP_DSC_SUPPORT]) &
+ DP_DSC_THROUGHPUT_MODE_0_DELTA_MASK) >>
+ DP_DSC_THROUGHPUT_MODE_0_DELTA_SHIFT; /* in units of 2 MPixels/sec */
+ delta *= 2000;
+ } else {
+ throughput = (throughput & DP_DSC_THROUGHPUT_MODE_1_MASK) >>
+ DP_DSC_THROUGHPUT_MODE_1_SHIFT;
+ }
+
+ switch (throughput) {
+ case 0:
+ return dsc_sink_min_slice_throughput(peak_pixel_rate);
+ case 1:
+ base = 340000;
+ break;
+ case 2 ... 14:
+ base = 400000 + 50000 * (throughput - 2);
+ break;
+ case 15:
+ base = 170000;
+ break;
+ }
+
+ return base + delta;
+}
+EXPORT_SYMBOL(drm_dp_dsc_sink_max_slice_throughput);
+
+static u8 dsc_branch_dpcd_cap(const u8 dpcd[DP_DSC_BRANCH_CAP_SIZE], int reg)
+{
+ return dpcd[reg - DP_DSC_BRANCH_OVERALL_THROUGHPUT_0];
+}
+
+/**
+ * drm_dp_dsc_branch_max_overall_throughput() - Branch device's max overall DSC pixel throughput
+ * @dsc_branch_dpcd: DSC branch capabilities from DPCD
+ * @is_rgb_yuv444: The mode is either RGB or YUV444
+ *
+ * Return the branch device's maximum overall DSC pixel throughput, based on
+ * the device's DPCD DSC branch capabilities, and whether the output
+ * format @is_rgb_yuv444 or yuv422/yuv420.
+ *
+ * Returns:
+ * - 0: The maximum overall throughput capability is not indicated by
+ * the device separately and it must be determined from the per-slice
+ * max throughput (see @drm_dp_dsc_branch_slice_max_throughput())
+ * and the maximum slice count supported by the device.
+ * - > 0: The maximum overall DSC pixel throughput supported by the branch
+ * device in kPixels/sec.
+ */
+int drm_dp_dsc_branch_max_overall_throughput(const u8 dsc_branch_dpcd[DP_DSC_BRANCH_CAP_SIZE],
+ bool is_rgb_yuv444)
+{
+ int throughput;
+
+ if (is_rgb_yuv444)
+ throughput = dsc_branch_dpcd_cap(dsc_branch_dpcd,
+ DP_DSC_BRANCH_OVERALL_THROUGHPUT_0);
+ else
+ throughput = dsc_branch_dpcd_cap(dsc_branch_dpcd,
+ DP_DSC_BRANCH_OVERALL_THROUGHPUT_1);
+
+ switch (throughput) {
+ case 0:
+ return 0;
+ case 1:
+ return 680000;
+ default:
+ return 600000 + 50000 * throughput;
+ }
+}
+EXPORT_SYMBOL(drm_dp_dsc_branch_max_overall_throughput);
+
+/**
+ * drm_dp_dsc_branch_max_line_width() - Branch device's max DSC line width
+ * @dsc_branch_dpcd: DSC branch capabilities from DPCD
+ *
+ * Return the branch device's maximum overall DSC line width, based on
+ * the device's @dsc_branch_dpcd capabilities.
+ *
+ * Returns:
+ * - 0: The maximum line width is not indicated by the device
+ * separately and it must be determined from the maximum
+ * slice count and slice-width supported by the device.
+ * - %-EINVAL: The device indicates an invalid maximum line width
+ * (< 5120 pixels).
+ * - >= 5120: The maximum line width in pixels.
+ */
+int drm_dp_dsc_branch_max_line_width(const u8 dsc_branch_dpcd[DP_DSC_BRANCH_CAP_SIZE])
+{
+ int line_width = dsc_branch_dpcd_cap(dsc_branch_dpcd, DP_DSC_BRANCH_MAX_LINE_WIDTH);
+
+ switch (line_width) {
+ case 0:
+ return 0;
+ case 1 ... 15:
+ return -EINVAL;
+ default:
+ return line_width * 320;
+ }
+}
+EXPORT_SYMBOL(drm_dp_dsc_branch_max_line_width);
+
static int drm_dp_read_lttpr_regs(struct drm_dp_aux *aux,
const u8 dpcd[DP_RECEIVER_CAP_SIZE], int address,
u8 *buf, int buf_size)
@@ -4128,22 +4293,61 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
{
int fxp, fxp_min, fxp_max, fxp_actual, f = 1;
int ret;
- u8 pn, pn_min, pn_max;
+ u8 pn, pn_min, pn_max, bit_count;
if (!bl->aux_set)
return 0;
- ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &pn);
+ ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, &bit_count);
if (ret < 0) {
drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap: %d\n",
aux->name, ret);
return -ENODEV;
}
- pn &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+ bit_count &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+
+ ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min);
+ if (ret < 0) {
+ drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n",
+ aux->name, ret);
+ return -ENODEV;
+ }
+ pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+
+ ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max);
+ if (ret < 0) {
+ drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n",
+ aux->name, ret);
+ return -ENODEV;
+ }
+ pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
+
+ if (unlikely(pn_min > pn_max)) {
+ drm_dbg_kms(aux->drm_dev, "%s: Invalid pwmgen bit count cap min/max returned: %d %d\n",
+ aux->name, pn_min, pn_max);
+ return -EINVAL;
+ }
+
+ /*
+ * Per VESA eDP Spec v1.4b, section 3.3.10.2:
+ * If DP_EDP_PWMGEN_BIT_COUNT is less than DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN,
+ * the sink must use the MIN value as the effective PWM bit count.
+ * Clamp the reported value to the [MIN, MAX] capability range to ensure
+ * correct brightness scaling on compliant eDP panels.
+ * Only enable this logic if the [MIN, MAX] range is valid in regard to Spec.
+ */
+ pn = bit_count;
+ if (bit_count < pn_min)
+ pn = clamp(bit_count, pn_min, pn_max);
+
bl->max = (1 << pn) - 1;
- if (!driver_pwm_freq_hz)
+ if (!driver_pwm_freq_hz) {
+ if (pn != bit_count)
+ goto bit_count_write_back;
+
return 0;
+ }
/*
* Set PWM Frequency divider to match desired frequency provided by the driver.
@@ -4167,21 +4371,6 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
* - FxP is within 25% of desired value.
* Note: 25% is arbitrary value and may need some tweak.
*/
- ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min);
- if (ret < 0) {
- drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap min: %d\n",
- aux->name, ret);
- return 0;
- }
- ret = drm_dp_dpcd_read_byte(aux, DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max);
- if (ret < 0) {
- drm_dbg_kms(aux->drm_dev, "%s: Failed to read pwmgen bit count cap max: %d\n",
- aux->name, ret);
- return 0;
- }
- pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
- pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
-
/* Ensure frequency is within 25% of desired value */
fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
@@ -4199,12 +4388,17 @@ drm_edp_backlight_probe_max(struct drm_dp_aux *aux, struct drm_edp_backlight_inf
break;
}
+bit_count_write_back:
ret = drm_dp_dpcd_write_byte(aux, DP_EDP_PWMGEN_BIT_COUNT, pn);
if (ret < 0) {
drm_dbg_kms(aux->drm_dev, "%s: Failed to write aux pwmgen bit count: %d\n",
aux->name, ret);
return 0;
}
+
+ if (!driver_pwm_freq_hz)
+ return 0;
+
bl->pwmgen_bit_count = pn;
bl->max = (1 << pn) - 1;
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index cd15cf52f0c9..67e095e398a3 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -42,6 +42,7 @@
#include <drm/drm_mode.h>
#include <drm/drm_print.h>
#include <drm/drm_writeback.h>
+#include <drm/drm_colorop.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
@@ -107,6 +108,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state)
kfree(state->connectors);
kfree(state->crtcs);
kfree(state->planes);
+ kfree(state->colorops);
kfree(state->private_objs);
}
EXPORT_SYMBOL(drm_atomic_state_default_release);
@@ -138,6 +140,10 @@ drm_atomic_state_init(struct drm_device *dev, struct drm_atomic_state *state)
sizeof(*state->planes), GFP_KERNEL);
if (!state->planes)
goto fail;
+ state->colorops = kcalloc(dev->mode_config.num_colorop,
+ sizeof(*state->colorops), GFP_KERNEL);
+ if (!state->colorops)
+ goto fail;
/*
* Because drm_atomic_state can be committed asynchronously we need our
@@ -200,6 +206,8 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
drm_dbg_atomic(dev, "Clearing atomic state %p\n", state);
+ state->checked = false;
+
for (i = 0; i < state->num_connector; i++) {
struct drm_connector *connector = state->connectors[i].ptr;
@@ -207,9 +215,9 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
continue;
connector->funcs->atomic_destroy_state(connector,
- state->connectors[i].state);
+ state->connectors[i].state_to_destroy);
state->connectors[i].ptr = NULL;
- state->connectors[i].state = NULL;
+ state->connectors[i].state_to_destroy = NULL;
state->connectors[i].old_state = NULL;
state->connectors[i].new_state = NULL;
drm_connector_put(connector);
@@ -222,10 +230,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
continue;
crtc->funcs->atomic_destroy_state(crtc,
- state->crtcs[i].state);
+ state->crtcs[i].state_to_destroy);
state->crtcs[i].ptr = NULL;
- state->crtcs[i].state = NULL;
+ state->crtcs[i].state_to_destroy = NULL;
state->crtcs[i].old_state = NULL;
state->crtcs[i].new_state = NULL;
@@ -242,20 +250,34 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
continue;
plane->funcs->atomic_destroy_state(plane,
- state->planes[i].state);
+ state->planes[i].state_to_destroy);
state->planes[i].ptr = NULL;
- state->planes[i].state = NULL;
+ state->planes[i].state_to_destroy = NULL;
state->planes[i].old_state = NULL;
state->planes[i].new_state = NULL;
}
+ for (i = 0; i < config->num_colorop; i++) {
+ struct drm_colorop *colorop = state->colorops[i].ptr;
+
+ if (!colorop)
+ continue;
+
+ drm_colorop_atomic_destroy_state(colorop,
+ state->colorops[i].state);
+ state->colorops[i].ptr = NULL;
+ state->colorops[i].state = NULL;
+ state->colorops[i].old_state = NULL;
+ state->colorops[i].new_state = NULL;
+ }
+
for (i = 0; i < state->num_private_objs; i++) {
struct drm_private_obj *obj = state->private_objs[i].ptr;
obj->funcs->atomic_destroy_state(obj,
- state->private_objs[i].state);
+ state->private_objs[i].state_to_destroy);
state->private_objs[i].ptr = NULL;
- state->private_objs[i].state = NULL;
+ state->private_objs[i].state_to_destroy = NULL;
state->private_objs[i].old_state = NULL;
state->private_objs[i].new_state = NULL;
}
@@ -348,8 +370,9 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
struct drm_crtc_state *crtc_state;
WARN_ON(!state->acquire_ctx);
+ drm_WARN_ON(state->dev, state->checked);
- crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (crtc_state)
return crtc_state;
@@ -361,7 +384,7 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state,
if (!crtc_state)
return ERR_PTR(-ENOMEM);
- state->crtcs[index].state = crtc_state;
+ state->crtcs[index].state_to_destroy = crtc_state;
state->crtcs[index].old_state = crtc->state;
state->crtcs[index].new_state = crtc_state;
state->crtcs[index].ptr = crtc;
@@ -480,8 +503,8 @@ static int drm_atomic_connector_check(struct drm_connector *connector,
}
if (state->crtc)
- crtc_state = drm_atomic_get_existing_crtc_state(state->state,
- state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state->state,
+ state->crtc);
if (writeback_job->fb && !crtc_state->active) {
drm_dbg_atomic(connector->dev,
@@ -528,13 +551,14 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
struct drm_plane_state *plane_state;
WARN_ON(!state->acquire_ctx);
+ drm_WARN_ON(state->dev, state->checked);
/* the legacy pointers should never be set */
WARN_ON(plane->fb);
WARN_ON(plane->old_fb);
WARN_ON(plane->crtc);
- plane_state = drm_atomic_get_existing_plane_state(state, plane);
+ plane_state = drm_atomic_get_new_plane_state(state, plane);
if (plane_state)
return plane_state;
@@ -546,7 +570,7 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
if (!plane_state)
return ERR_PTR(-ENOMEM);
- state->planes[index].state = plane_state;
+ state->planes[index].state_to_destroy = plane_state;
state->planes[index].ptr = plane;
state->planes[index].old_state = plane->state;
state->planes[index].new_state = plane_state;
@@ -568,6 +592,55 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state,
}
EXPORT_SYMBOL(drm_atomic_get_plane_state);
+/**
+ * drm_atomic_get_colorop_state - get colorop state
+ * @state: global atomic state object
+ * @colorop: colorop to get state object for
+ *
+ * This function returns the colorop state for the given colorop, allocating it
+ * if needed. It will also grab the relevant plane lock to make sure that the
+ * state is consistent.
+ *
+ * Returns:
+ *
+ * Either the allocated state or the error code encoded into the pointer. When
+ * the error is EDEADLK then the w/w mutex code has detected a deadlock and the
+ * entire atomic sequence must be restarted. All other errors are fatal.
+ */
+struct drm_colorop_state *
+drm_atomic_get_colorop_state(struct drm_atomic_state *state,
+ struct drm_colorop *colorop)
+{
+ int ret, index = drm_colorop_index(colorop);
+ struct drm_colorop_state *colorop_state;
+
+ WARN_ON(!state->acquire_ctx);
+
+ colorop_state = drm_atomic_get_new_colorop_state(state, colorop);
+ if (colorop_state)
+ return colorop_state;
+
+ ret = drm_modeset_lock(&colorop->plane->mutex, state->acquire_ctx);
+ if (ret)
+ return ERR_PTR(ret);
+
+ colorop_state = drm_atomic_helper_colorop_duplicate_state(colorop);
+ if (!colorop_state)
+ return ERR_PTR(-ENOMEM);
+
+ state->colorops[index].state = colorop_state;
+ state->colorops[index].ptr = colorop;
+ state->colorops[index].old_state = colorop->state;
+ state->colorops[index].new_state = colorop_state;
+ colorop_state->state = state;
+
+ drm_dbg_atomic(colorop->dev, "Added [COLOROP:%d:%d] %p state to %p\n",
+ colorop->base.id, colorop->type, colorop_state, state);
+
+ return colorop_state;
+}
+EXPORT_SYMBOL(drm_atomic_get_colorop_state);
+
static bool
plane_switching_crtc(const struct drm_plane_state *old_plane_state,
const struct drm_plane_state *new_plane_state)
@@ -707,6 +780,46 @@ static int drm_atomic_plane_check(const struct drm_plane_state *old_plane_state,
return 0;
}
+static void drm_atomic_colorop_print_state(struct drm_printer *p,
+ const struct drm_colorop_state *state)
+{
+ struct drm_colorop *colorop = state->colorop;
+
+ drm_printf(p, "colorop[%u]:\n", colorop->base.id);
+ drm_printf(p, "\ttype=%s\n", drm_get_colorop_type_name(colorop->type));
+ if (colorop->bypass_property)
+ drm_printf(p, "\tbypass=%u\n", state->bypass);
+
+ switch (colorop->type) {
+ case DRM_COLOROP_1D_CURVE:
+ drm_printf(p, "\tcurve_1d_type=%s\n",
+ drm_get_colorop_curve_1d_type_name(state->curve_1d_type));
+ break;
+ case DRM_COLOROP_1D_LUT:
+ drm_printf(p, "\tsize=%d\n", colorop->size);
+ drm_printf(p, "\tinterpolation=%s\n",
+ drm_get_colorop_lut1d_interpolation_name(colorop->lut1d_interpolation));
+ drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0);
+ break;
+ case DRM_COLOROP_CTM_3X4:
+ drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0);
+ break;
+ case DRM_COLOROP_MULTIPLIER:
+ drm_printf(p, "\tmultiplier=%llu\n", state->multiplier);
+ break;
+ case DRM_COLOROP_3D_LUT:
+ drm_printf(p, "\tsize=%d\n", colorop->size);
+ drm_printf(p, "\tinterpolation=%s\n",
+ drm_get_colorop_lut3d_interpolation_name(colorop->lut3d_interpolation));
+ drm_printf(p, "\tdata blob id=%d\n", state->data ? state->data->base.id : 0);
+ break;
+ default:
+ break;
+ }
+
+ drm_printf(p, "\tnext=%d\n", colorop->next ? colorop->next->base.id : 0);
+}
+
static void drm_atomic_plane_print_state(struct drm_printer *p,
const struct drm_plane_state *state)
{
@@ -728,7 +841,8 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
drm_printf(p, "\tcolor-range=%s\n",
drm_get_color_range_name(state->color_range));
drm_printf(p, "\tcolor_mgmt_changed=%d\n", state->color_mgmt_changed);
-
+ drm_printf(p, "\tcolor-pipeline=%d\n",
+ state->color_pipeline ? state->color_pipeline->base.id : 0);
if (plane->funcs->atomic_print_state)
plane->funcs->atomic_print_state(p, state);
}
@@ -831,14 +945,17 @@ struct drm_private_state *
drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
struct drm_private_obj *obj)
{
- int index, num_objs, i, ret;
+ int index, num_objs, ret;
size_t size;
struct __drm_private_objs_state *arr;
struct drm_private_state *obj_state;
- for (i = 0; i < state->num_private_objs; i++)
- if (obj == state->private_objs[i].ptr)
- return state->private_objs[i].state;
+ WARN_ON(!state->acquire_ctx);
+ drm_WARN_ON(state->dev, state->checked);
+
+ obj_state = drm_atomic_get_new_private_obj_state(state, obj);
+ if (obj_state)
+ return obj_state;
ret = drm_modeset_lock(&obj->lock, state->acquire_ctx);
if (ret)
@@ -858,7 +975,7 @@ drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
if (!obj_state)
return ERR_PTR(-ENOMEM);
- state->private_objs[index].state = obj_state;
+ state->private_objs[index].state_to_destroy = obj_state;
state->private_objs[index].old_state = obj->state;
state->private_objs[index].new_state = obj_state;
state->private_objs[index].ptr = obj;
@@ -1129,6 +1246,7 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
struct drm_connector_state *connector_state;
WARN_ON(!state->acquire_ctx);
+ drm_WARN_ON(state->dev, state->checked);
ret = drm_modeset_lock(&config->connection_mutex, state->acquire_ctx);
if (ret)
@@ -1152,15 +1270,16 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state,
state->num_connector = alloc;
}
- if (state->connectors[index].state)
- return state->connectors[index].state;
+ connector_state = drm_atomic_get_new_connector_state(state, connector);
+ if (connector_state)
+ return connector_state;
connector_state = connector->funcs->atomic_duplicate_state(connector);
if (!connector_state)
return ERR_PTR(-ENOMEM);
drm_connector_get(connector);
- state->connectors[index].state = connector_state;
+ state->connectors[index].state_to_destroy = connector_state;
state->connectors[index].old_state = connector->state;
state->connectors[index].new_state = connector_state;
state->connectors[index].ptr = connector;
@@ -1308,7 +1427,6 @@ drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
struct drm_encoder *encoder)
{
struct drm_bridge_state *bridge_state;
- struct drm_bridge *bridge;
if (!encoder)
return 0;
@@ -1317,7 +1435,7 @@ drm_atomic_add_encoder_bridges(struct drm_atomic_state *state,
"Adding all bridges for [encoder:%d:%s] to %p\n",
encoder->base.id, encoder->name, state);
- drm_for_each_bridge_in_chain(encoder, bridge) {
+ drm_for_each_bridge_in_chain_scoped(encoder, bridge) {
/* Skip bridges that don't implement the atomic state hooks. */
if (!bridge->funcs->atomic_duplicate_state)
continue;
@@ -1438,6 +1556,52 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state,
EXPORT_SYMBOL(drm_atomic_add_affected_planes);
/**
+ * drm_atomic_add_affected_colorops - add colorops for plane
+ * @state: atomic state
+ * @plane: DRM plane
+ *
+ * This function walks the current configuration and adds all colorops
+ * currently used by @plane to the atomic configuration @state. This is useful
+ * when an atomic commit also needs to check all currently enabled colorop on
+ * @plane, e.g. when changing the mode. It's also useful when re-enabling a plane
+ * to avoid special code to force-enable all colorops.
+ *
+ * Since acquiring a colorop state will always also acquire the w/w mutex of the
+ * current plane for that colorop (if there is any) adding all the colorop states for
+ * a plane will not reduce parallelism of atomic updates.
+ *
+ * Returns:
+ * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK
+ * then the w/w mutex code has detected a deadlock and the entire atomic
+ * sequence must be restarted. All other errors are fatal.
+ */
+int
+drm_atomic_add_affected_colorops(struct drm_atomic_state *state,
+ struct drm_plane *plane)
+{
+ struct drm_colorop *colorop;
+ struct drm_colorop_state *colorop_state;
+
+ WARN_ON(!drm_atomic_get_new_plane_state(state, plane));
+
+ drm_dbg_atomic(plane->dev,
+ "Adding all current colorops for [PLANE:%d:%s] to %p\n",
+ plane->base.id, plane->name, state);
+
+ drm_for_each_colorop(colorop, plane->dev) {
+ if (colorop->plane != plane)
+ continue;
+
+ colorop_state = drm_atomic_get_colorop_state(state, colorop);
+ if (IS_ERR(colorop_state))
+ return PTR_ERR(colorop_state);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_atomic_add_affected_colorops);
+
+/**
* drm_atomic_check_only - check whether a given config would work
* @state: atomic configuration to check
*
@@ -1541,6 +1705,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
requested_crtc, affected_crtc);
}
+ state->checked = true;
+
return 0;
}
EXPORT_SYMBOL(drm_atomic_check_only);
@@ -1833,6 +1999,7 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p,
bool take_locks)
{
struct drm_mode_config *config = &dev->mode_config;
+ struct drm_colorop *colorop;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_connector *connector;
@@ -1842,6 +2009,14 @@ static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p,
if (!drm_drv_uses_atomic_modeset(dev))
return;
+ list_for_each_entry(colorop, &config->colorop_list, head) {
+ if (take_locks)
+ drm_modeset_lock(&colorop->plane->mutex, NULL);
+ drm_atomic_colorop_print_state(p, colorop->state);
+ if (take_locks)
+ drm_modeset_unlock(&colorop->plane->mutex);
+ }
+
list_for_each_entry(plane, &config->plane_list, head) {
if (take_locks)
drm_modeset_lock(&plane->mutex, NULL);
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index d5ebe6ea0acb..10adac9397cf 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1831,10 +1831,12 @@ drm_atomic_helper_wait_for_vblanks(struct drm_device *dev,
}
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
+ wait_queue_head_t *queue = drm_crtc_vblank_waitqueue(crtc);
+
if (!(crtc_mask & drm_crtc_mask(crtc)))
continue;
- ret = wait_event_timeout(dev->vblank[i].queue,
+ ret = wait_event_timeout(*queue,
state->crtcs[i].last_vblank_count !=
drm_crtc_vblank_count(crtc),
msecs_to_jiffies(100));
@@ -3182,6 +3184,8 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
struct drm_plane *plane;
struct drm_plane_state *old_plane_state, *new_plane_state;
+ struct drm_colorop *colorop;
+ struct drm_colorop_state *old_colorop_state, *new_colorop_state;
struct drm_crtc_commit *commit;
struct drm_private_obj *obj;
struct drm_private_state *old_obj_state, *new_obj_state;
@@ -3236,7 +3240,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
old_conn_state->state = state;
new_conn_state->state = NULL;
- state->connectors[i].state = old_conn_state;
+ state->connectors[i].state_to_destroy = old_conn_state;
connector->state = new_conn_state;
}
@@ -3246,7 +3250,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
old_crtc_state->state = state;
new_crtc_state->state = NULL;
- state->crtcs[i].state = old_crtc_state;
+ state->crtcs[i].state_to_destroy = old_crtc_state;
crtc->state = new_crtc_state;
if (new_crtc_state->commit) {
@@ -3259,6 +3263,16 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
}
}
+ for_each_oldnew_colorop_in_state(state, colorop, old_colorop_state, new_colorop_state, i) {
+ WARN_ON(colorop->state != old_colorop_state);
+
+ old_colorop_state->state = state;
+ new_colorop_state->state = NULL;
+
+ state->colorops[i].state = old_colorop_state;
+ colorop->state = new_colorop_state;
+ }
+
drm_panic_lock(state->dev, flags);
for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
WARN_ON(plane->state != old_plane_state);
@@ -3266,7 +3280,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
old_plane_state->state = state;
new_plane_state->state = NULL;
- state->planes[i].state = old_plane_state;
+ state->planes[i].state_to_destroy = old_plane_state;
plane->state = new_plane_state;
}
drm_panic_unlock(state->dev, flags);
@@ -3277,7 +3291,7 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state,
old_obj_state->state = state;
new_obj_state->state = NULL;
- state->private_objs[i].state = old_obj_state;
+ state->private_objs[i].state_to_destroy = old_obj_state;
obj->state = new_obj_state;
}
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 7142e163e618..cee6d8fc44ad 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -268,6 +268,11 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state,
plane_state->color_range = val;
}
+ if (plane->color_pipeline_property) {
+ /* default is always NULL, i.e., bypass */
+ plane_state->color_pipeline = NULL;
+ }
+
if (plane->zpos_property) {
if (!drm_object_property_get_default_value(&plane->base,
plane->zpos_property,
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 85dbdaa4a2e2..7320db4b8489 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -35,6 +35,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_writeback.h>
#include <drm/drm_vblank.h>
+#include <drm/drm_colorop.h>
#include <linux/export.h>
#include <linux/dma-fence.h>
@@ -258,6 +259,34 @@ drm_atomic_set_fb_for_plane(struct drm_plane_state *plane_state,
EXPORT_SYMBOL(drm_atomic_set_fb_for_plane);
/**
+ * drm_atomic_set_colorop_for_plane - set colorop for plane
+ * @plane_state: atomic state object for the plane
+ * @colorop: colorop to use for the plane
+ *
+ * Helper function to select the color pipeline on a plane by setting
+ * it to the first drm_colorop element of the pipeline.
+ */
+void
+drm_atomic_set_colorop_for_plane(struct drm_plane_state *plane_state,
+ struct drm_colorop *colorop)
+{
+ struct drm_plane *plane = plane_state->plane;
+
+ if (colorop)
+ drm_dbg_atomic(plane->dev,
+ "Set [COLOROP:%d] for [PLANE:%d:%s] state %p\n",
+ colorop->base.id, plane->base.id, plane->name,
+ plane_state);
+ else
+ drm_dbg_atomic(plane->dev,
+ "Set [NOCOLOROP] for [PLANE:%d:%s] state %p\n",
+ plane->base.id, plane->name, plane_state);
+
+ plane_state->color_pipeline = colorop;
+}
+EXPORT_SYMBOL(drm_atomic_set_colorop_for_plane);
+
+/**
* drm_atomic_set_crtc_for_connector - set CRTC for connector
* @conn_state: atomic state object for the connector
* @crtc: CRTC to use for the connector
@@ -419,6 +448,8 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
set_out_fence_for_crtc(state->state, crtc, fence_ptr);
} else if (property == crtc->scaling_filter_property) {
state->scaling_filter = val;
+ } else if (property == crtc->sharpness_strength_property) {
+ state->sharpness_strength = val;
} else if (crtc->funcs->atomic_set_property) {
return crtc->funcs->atomic_set_property(crtc, state, property, val);
} else {
@@ -456,6 +487,8 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc,
*val = 0;
else if (property == crtc->scaling_filter_property)
*val = state->scaling_filter;
+ else if (property == crtc->sharpness_strength_property)
+ *val = state->sharpness_strength;
else if (crtc->funcs->atomic_get_property)
return crtc->funcs->atomic_get_property(crtc, state, property, val);
else {
@@ -540,6 +573,16 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
state->color_encoding = val;
} else if (property == plane->color_range_property) {
state->color_range = val;
+ } else if (property == plane->color_pipeline_property) {
+ /* find DRM colorop object */
+ struct drm_colorop *colorop = NULL;
+
+ colorop = drm_colorop_find(dev, file_priv, val);
+
+ if (val && !colorop)
+ return -EACCES;
+
+ drm_atomic_set_colorop_for_plane(state, colorop);
} else if (property == config->prop_fb_damage_clips) {
ret = drm_property_replace_blob_from_id(dev,
&state->fb_damage_clips,
@@ -622,6 +665,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
*val = state->color_encoding;
} else if (property == plane->color_range_property) {
*val = state->color_range;
+ } else if (property == plane->color_pipeline_property) {
+ *val = (state->color_pipeline) ? state->color_pipeline->base.id : 0;
} else if (property == config->prop_fb_damage_clips) {
*val = (state->fb_damage_clips) ?
state->fb_damage_clips->base.id : 0;
@@ -644,6 +689,96 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
return 0;
}
+static int drm_atomic_color_set_data_property(struct drm_colorop *colorop,
+ struct drm_colorop_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ ssize_t elem_size = -1;
+ ssize_t size = -1;
+ bool replaced = false;
+
+ switch (colorop->type) {
+ case DRM_COLOROP_1D_LUT:
+ size = colorop->size * sizeof(struct drm_color_lut32);
+ break;
+ case DRM_COLOROP_CTM_3X4:
+ size = sizeof(struct drm_color_ctm_3x4);
+ break;
+ case DRM_COLOROP_3D_LUT:
+ size = colorop->size * colorop->size * colorop->size *
+ sizeof(struct drm_color_lut32);
+ break;
+ default:
+ /* should never get here */
+ return -EINVAL;
+ }
+
+ return drm_property_replace_blob_from_id(colorop->dev,
+ &state->data,
+ val,
+ size,
+ elem_size,
+ &replaced);
+}
+
+static int drm_atomic_colorop_set_property(struct drm_colorop *colorop,
+ struct drm_colorop_state *state,
+ struct drm_file *file_priv,
+ struct drm_property *property,
+ uint64_t val)
+{
+ if (property == colorop->bypass_property) {
+ state->bypass = val;
+ } else if (property == colorop->lut1d_interpolation_property) {
+ colorop->lut1d_interpolation = val;
+ } else if (property == colorop->curve_1d_type_property) {
+ state->curve_1d_type = val;
+ } else if (property == colorop->multiplier_property) {
+ state->multiplier = val;
+ } else if (property == colorop->lut3d_interpolation_property) {
+ colorop->lut3d_interpolation = val;
+ } else if (property == colorop->data_property) {
+ return drm_atomic_color_set_data_property(colorop, state,
+ property, val);
+ } else {
+ drm_dbg_atomic(colorop->dev,
+ "[COLOROP:%d:%d] unknown property [PROP:%d:%s]\n",
+ colorop->base.id, colorop->type,
+ property->base.id, property->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+drm_atomic_colorop_get_property(struct drm_colorop *colorop,
+ const struct drm_colorop_state *state,
+ struct drm_property *property, uint64_t *val)
+{
+ if (property == colorop->type_property)
+ *val = colorop->type;
+ else if (property == colorop->bypass_property)
+ *val = state->bypass;
+ else if (property == colorop->lut1d_interpolation_property)
+ *val = colorop->lut1d_interpolation;
+ else if (property == colorop->curve_1d_type_property)
+ *val = state->curve_1d_type;
+ else if (property == colorop->multiplier_property)
+ *val = state->multiplier;
+ else if (property == colorop->size_property)
+ *val = colorop->size;
+ else if (property == colorop->lut3d_interpolation_property)
+ *val = colorop->lut3d_interpolation;
+ else if (property == colorop->data_property)
+ *val = (state->data) ? state->data->base.id : 0;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static int drm_atomic_set_writeback_fb_for_connector(
struct drm_connector_state *conn_state,
struct drm_framebuffer *fb)
@@ -910,6 +1045,15 @@ int drm_atomic_get_property(struct drm_mode_object *obj,
plane->state, property, val);
break;
}
+ case DRM_MODE_OBJECT_COLOROP: {
+ struct drm_colorop *colorop = obj_to_colorop(obj);
+
+ if (colorop->plane)
+ WARN_ON(!drm_modeset_is_locked(&colorop->plane->mutex));
+
+ ret = drm_atomic_colorop_get_property(colorop, colorop->state, property, val);
+ break;
+ }
default:
drm_dbg_atomic(dev, "[OBJECT:%d] has no properties\n", obj->id);
ret = -EINVAL;
@@ -1107,6 +1251,21 @@ int drm_atomic_set_property(struct drm_atomic_state *state,
ret = drm_atomic_plane_set_property(plane,
plane_state, file_priv,
prop, prop_value);
+
+ break;
+ }
+ case DRM_MODE_OBJECT_COLOROP: {
+ struct drm_colorop *colorop = obj_to_colorop(obj);
+ struct drm_colorop_state *colorop_state;
+
+ colorop_state = drm_atomic_get_colorop_state(state, colorop);
+ if (IS_ERR(colorop_state)) {
+ ret = PTR_ERR(colorop_state);
+ break;
+ }
+
+ ret = drm_atomic_colorop_set_property(colorop, colorop_state,
+ file_priv, prop, prop_value);
break;
}
default:
@@ -1446,6 +1605,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
state->acquire_ctx = &ctx;
state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET);
+ state->plane_color_pipeline = file_priv->plane_color_pipeline;
retry:
copied_objs = 0;
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index d031447eebc9..8f355df883d8 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -197,15 +197,22 @@
* driver.
*/
+/* Protect bridge_list and bridge_lingering_list */
static DEFINE_MUTEX(bridge_lock);
static LIST_HEAD(bridge_list);
+static LIST_HEAD(bridge_lingering_list);
static void __drm_bridge_free(struct kref *kref)
{
struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount);
+ mutex_lock(&bridge_lock);
+ list_del(&bridge->list);
+ mutex_unlock(&bridge_lock);
+
if (bridge->funcs->destroy)
bridge->funcs->destroy(bridge);
+
kfree(bridge->container);
}
@@ -273,6 +280,7 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
return ERR_PTR(-ENOMEM);
bridge = container + offset;
+ INIT_LIST_HEAD(&bridge->list);
bridge->container = container;
bridge->funcs = funcs;
kref_init(&bridge->refcount);
@@ -286,10 +294,13 @@ void *__devm_drm_bridge_alloc(struct device *dev, size_t size, size_t offset,
EXPORT_SYMBOL(__devm_drm_bridge_alloc);
/**
- * drm_bridge_add - add the given bridge to the global bridge list
+ * drm_bridge_add - register a bridge
*
* @bridge: bridge control structure
*
+ * Add the given bridge to the global list of bridges, where they can be
+ * found by users via of_drm_find_bridge().
+ *
* The bridge to be added must have been allocated by
* devm_drm_bridge_alloc().
*/
@@ -300,6 +311,14 @@ void drm_bridge_add(struct drm_bridge *bridge)
drm_bridge_get(bridge);
+ /*
+ * If the bridge was previously added and then removed, it is now
+ * in bridge_lingering_list. Remove it or bridge_lingering_list will be
+ * corrupted when adding this bridge to bridge_list below.
+ */
+ if (!list_empty(&bridge->list))
+ list_del_init(&bridge->list);
+
mutex_init(&bridge->hpd_mutex);
if (bridge->ops & DRM_BRIDGE_OP_HDMI)
@@ -336,14 +355,19 @@ int devm_drm_bridge_add(struct device *dev, struct drm_bridge *bridge)
EXPORT_SYMBOL(devm_drm_bridge_add);
/**
- * drm_bridge_remove - remove the given bridge from the global bridge list
+ * drm_bridge_remove - unregister a bridge
*
* @bridge: bridge control structure
+ *
+ * Remove the given bridge from the global list of registered bridges, so
+ * it won't be found by users via of_drm_find_bridge(), and add it to the
+ * lingering bridge list, to keep track of it until its allocated memory is
+ * eventually freed.
*/
void drm_bridge_remove(struct drm_bridge *bridge)
{
mutex_lock(&bridge_lock);
- list_del_init(&bridge->list);
+ list_move_tail(&bridge->list, &bridge_lingering_list);
mutex_unlock(&bridge_lock);
mutex_destroy(&bridge->hpd_mutex);
@@ -398,6 +422,9 @@ static bool drm_bridge_is_atomic(struct drm_bridge *bridge)
* If non-NULL the previous bridge must be already attached by a call to this
* function.
*
+ * The bridge to be attached must have been previously added by
+ * drm_bridge_add().
+ *
* Note that bridges attached to encoders are auto-detached during encoder
* cleanup in drm_encoder_cleanup(), so drm_bridge_attach() should generally
* *not* be balanced with a drm_bridge_detach() in driver code.
@@ -414,6 +441,12 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
if (!encoder || !bridge)
return -EINVAL;
+ if (!bridge->container)
+ DRM_WARN("DRM bridge corrupted or not allocated by devm_drm_bridge_alloc()\n");
+
+ if (list_empty(&bridge->list))
+ DRM_WARN("Missing drm_bridge_add() before attach\n");
+
drm_bridge_get(bridge);
if (previous && (!previous->dev || previous->encoder != encoder)) {
@@ -1062,12 +1095,12 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge,
struct drm_encoder *encoder = bridge->encoder;
struct drm_bridge_state *last_bridge_state;
unsigned int i, num_out_bus_fmts = 0;
- struct drm_bridge *last_bridge;
u32 *out_bus_fmts;
int ret = 0;
- last_bridge = list_last_entry(&encoder->bridge_chain,
- struct drm_bridge, chain_node);
+ struct drm_bridge *last_bridge __free(drm_bridge_put) =
+ drm_bridge_get(list_last_entry(&encoder->bridge_chain,
+ struct drm_bridge, chain_node));
last_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
last_bridge);
@@ -1121,7 +1154,6 @@ drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct drm_bridge_state *bridge_state, *next_bridge_state;
- struct drm_bridge *next_bridge;
u32 output_flags = 0;
bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
@@ -1130,7 +1162,7 @@ drm_atomic_bridge_propagate_bus_flags(struct drm_bridge *bridge,
if (!bridge_state)
return;
- next_bridge = drm_bridge_get_next_bridge(bridge);
+ struct drm_bridge *next_bridge __free(drm_bridge_put) = drm_bridge_get_next_bridge(bridge);
/*
* Let's try to apply the most common case here, that is, propagate
@@ -1432,17 +1464,20 @@ EXPORT_SYMBOL(devm_drm_put_bridge);
static void drm_bridge_debugfs_show_bridge(struct drm_printer *p,
struct drm_bridge *bridge,
- unsigned int idx)
+ unsigned int idx,
+ bool lingering)
{
drm_printf(p, "bridge[%u]: %ps\n", idx, bridge->funcs);
- drm_printf(p, "\trefcount: %u\n", kref_read(&bridge->refcount));
+ drm_printf(p, "\trefcount: %u%s\n", kref_read(&bridge->refcount),
+ lingering ? " [lingering]" : "");
drm_printf(p, "\ttype: [%d] %s\n",
bridge->type,
drm_get_connector_type_name(bridge->type));
- if (bridge->of_node)
+ /* The OF node could be freed after drm_bridge_remove() */
+ if (bridge->of_node && !lingering)
drm_printf(p, "\tOF: %pOFfc\n", bridge->of_node);
drm_printf(p, "\tops: [0x%x]", bridge->ops);
@@ -1468,7 +1503,10 @@ static int allbridges_show(struct seq_file *m, void *data)
mutex_lock(&bridge_lock);
list_for_each_entry(bridge, &bridge_list, list)
- drm_bridge_debugfs_show_bridge(&p, bridge, idx++);
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false);
+
+ list_for_each_entry(bridge, &bridge_lingering_list, list)
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++, true);
mutex_unlock(&bridge_lock);
@@ -1480,11 +1518,10 @@ static int encoder_bridges_show(struct seq_file *m, void *data)
{
struct drm_encoder *encoder = m->private;
struct drm_printer p = drm_seq_file_printer(m);
- struct drm_bridge *bridge;
unsigned int idx = 0;
- drm_for_each_bridge_in_chain(encoder, bridge)
- drm_bridge_debugfs_show_bridge(&p, bridge, idx++);
+ drm_for_each_bridge_in_chain_scoped(encoder, bridge)
+ drm_bridge_debugfs_show_bridge(&p, bridge, idx++, false);
return 0;
}
diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
index a94061f373de..2f279b46bd2c 100644
--- a/drivers/gpu/drm/drm_buddy.c
+++ b/drivers/gpu/drm/drm_buddy.c
@@ -11,9 +11,19 @@
#include <linux/sizes.h>
#include <drm/drm_buddy.h>
+#include <drm/drm_print.h>
+
+enum drm_buddy_free_tree {
+ DRM_BUDDY_CLEAR_TREE = 0,
+ DRM_BUDDY_DIRTY_TREE,
+ DRM_BUDDY_MAX_FREE_TREES,
+};
static struct kmem_cache *slab_blocks;
+#define for_each_free_tree(tree) \
+ for ((tree) = 0; (tree) < DRM_BUDDY_MAX_FREE_TREES; (tree)++)
+
static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm,
struct drm_buddy_block *parent,
unsigned int order,
@@ -31,6 +41,8 @@ static struct drm_buddy_block *drm_block_alloc(struct drm_buddy *mm,
block->header |= order;
block->parent = parent;
+ RB_CLEAR_NODE(&block->rb);
+
BUG_ON(block->header & DRM_BUDDY_HEADER_UNUSED);
return block;
}
@@ -41,23 +53,64 @@ 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)
+static enum drm_buddy_free_tree
+get_block_tree(struct drm_buddy_block *block)
{
- struct drm_buddy_block *node;
- struct list_head *head;
+ return drm_buddy_block_is_clear(block) ?
+ DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE;
+}
- head = &mm->free_list[drm_buddy_block_order(block)];
- if (list_empty(head)) {
- list_add(&block->link, head);
- return;
- }
+static struct drm_buddy_block *
+rbtree_get_free_block(const struct rb_node *node)
+{
+ return node ? rb_entry(node, struct drm_buddy_block, rb) : NULL;
+}
- list_for_each_entry(node, head, link)
- if (drm_buddy_block_offset(block) < drm_buddy_block_offset(node))
- break;
+static struct drm_buddy_block *
+rbtree_last_free_block(struct rb_root *root)
+{
+ return rbtree_get_free_block(rb_last(root));
+}
- __list_add(&block->link, node->link.prev, &node->link);
+static bool rbtree_is_empty(struct rb_root *root)
+{
+ return RB_EMPTY_ROOT(root);
+}
+
+static bool drm_buddy_block_offset_less(const struct drm_buddy_block *block,
+ const struct drm_buddy_block *node)
+{
+ return drm_buddy_block_offset(block) < drm_buddy_block_offset(node);
+}
+
+static bool rbtree_block_offset_less(struct rb_node *block,
+ const struct rb_node *node)
+{
+ return drm_buddy_block_offset_less(rbtree_get_free_block(block),
+ rbtree_get_free_block(node));
+}
+
+static void rbtree_insert(struct drm_buddy *mm,
+ struct drm_buddy_block *block,
+ enum drm_buddy_free_tree tree)
+{
+ rb_add(&block->rb,
+ &mm->free_trees[tree][drm_buddy_block_order(block)],
+ rbtree_block_offset_less);
+}
+
+static void rbtree_remove(struct drm_buddy *mm,
+ struct drm_buddy_block *block)
+{
+ unsigned int order = drm_buddy_block_order(block);
+ enum drm_buddy_free_tree tree;
+ struct rb_root *root;
+
+ tree = get_block_tree(block);
+ root = &mm->free_trees[tree][order];
+
+ rb_erase(&block->rb, root);
+ RB_CLEAR_NODE(&block->rb);
}
static void clear_reset(struct drm_buddy_block *block)
@@ -70,29 +123,34 @@ static void mark_cleared(struct drm_buddy_block *block)
block->header |= DRM_BUDDY_HEADER_CLEAR;
}
-static void mark_allocated(struct drm_buddy_block *block)
+static void mark_allocated(struct drm_buddy *mm,
+ struct drm_buddy_block *block)
{
block->header &= ~DRM_BUDDY_HEADER_STATE;
block->header |= DRM_BUDDY_ALLOCATED;
- list_del(&block->link);
+ rbtree_remove(mm, block);
}
static void mark_free(struct drm_buddy *mm,
struct drm_buddy_block *block)
{
+ enum drm_buddy_free_tree tree;
+
block->header &= ~DRM_BUDDY_HEADER_STATE;
block->header |= DRM_BUDDY_FREE;
- list_insert_sorted(mm, block);
+ tree = get_block_tree(block);
+ rbtree_insert(mm, block, tree);
}
-static void mark_split(struct drm_buddy_block *block)
+static void mark_split(struct drm_buddy *mm,
+ struct drm_buddy_block *block)
{
block->header &= ~DRM_BUDDY_HEADER_STATE;
block->header |= DRM_BUDDY_SPLIT;
- list_del(&block->link);
+ rbtree_remove(mm, block);
}
static inline bool overlaps(u64 s1, u64 e1, u64 s2, u64 e2)
@@ -148,7 +206,7 @@ static unsigned int __drm_buddy_free(struct drm_buddy *mm,
mark_cleared(parent);
}
- list_del(&buddy->link);
+ rbtree_remove(mm, buddy);
if (force_merge && drm_buddy_block_is_clear(buddy))
mm->clear_avail -= drm_buddy_block_size(mm, buddy);
@@ -169,7 +227,7 @@ static int __force_merge(struct drm_buddy *mm,
u64 end,
unsigned int min_order)
{
- unsigned int order;
+ unsigned int tree, order;
int i;
if (!min_order)
@@ -178,44 +236,48 @@ static int __force_merge(struct drm_buddy *mm,
if (min_order > mm->max_order)
return -EINVAL;
- for (i = min_order - 1; i >= 0; i--) {
- struct drm_buddy_block *block, *prev;
+ for_each_free_tree(tree) {
+ for (i = min_order - 1; i >= 0; i--) {
+ struct rb_node *iter = rb_last(&mm->free_trees[tree][i]);
- list_for_each_entry_safe_reverse(block, prev, &mm->free_list[i], link) {
- struct drm_buddy_block *buddy;
- u64 block_start, block_end;
+ while (iter) {
+ struct drm_buddy_block *block, *buddy;
+ u64 block_start, block_end;
- if (!block->parent)
- continue;
+ block = rbtree_get_free_block(iter);
+ iter = rb_prev(iter);
- block_start = drm_buddy_block_offset(block);
- block_end = block_start + drm_buddy_block_size(mm, block) - 1;
+ if (!block || !block->parent)
+ continue;
- if (!contains(start, end, block_start, block_end))
- continue;
+ block_start = drm_buddy_block_offset(block);
+ block_end = block_start + drm_buddy_block_size(mm, block) - 1;
- buddy = __get_buddy(block);
- if (!drm_buddy_block_is_free(buddy))
- continue;
+ if (!contains(start, end, block_start, block_end))
+ continue;
- WARN_ON(drm_buddy_block_is_clear(block) ==
- drm_buddy_block_is_clear(buddy));
+ buddy = __get_buddy(block);
+ if (!drm_buddy_block_is_free(buddy))
+ continue;
- /*
- * If the prev block is same as buddy, don't access the
- * block in the next iteration as we would free the
- * buddy block as part of the free function.
- */
- if (prev == buddy)
- prev = list_prev_entry(prev, link);
+ WARN_ON(drm_buddy_block_is_clear(block) ==
+ drm_buddy_block_is_clear(buddy));
- list_del(&block->link);
- if (drm_buddy_block_is_clear(block))
- mm->clear_avail -= drm_buddy_block_size(mm, block);
+ /*
+ * Advance to the next node when the current node is the buddy,
+ * as freeing the block will also remove its buddy from the tree.
+ */
+ if (iter == &buddy->rb)
+ iter = rb_prev(iter);
- order = __drm_buddy_free(mm, block, true);
- if (order >= min_order)
- return 0;
+ rbtree_remove(mm, block);
+ if (drm_buddy_block_is_clear(block))
+ mm->clear_avail -= drm_buddy_block_size(mm, block);
+
+ order = __drm_buddy_free(mm, block, true);
+ if (order >= min_order)
+ return 0;
+ }
}
}
@@ -236,8 +298,8 @@ static int __force_merge(struct drm_buddy *mm,
*/
int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
{
- unsigned int i;
- u64 offset;
+ unsigned int i, j, root_count = 0;
+ u64 offset = 0;
if (size < chunk_size)
return -EINVAL;
@@ -258,14 +320,22 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
BUG_ON(mm->max_order > DRM_BUDDY_MAX_ORDER);
- mm->free_list = kmalloc_array(mm->max_order + 1,
- sizeof(struct list_head),
- GFP_KERNEL);
- if (!mm->free_list)
+ mm->free_trees = kmalloc_array(DRM_BUDDY_MAX_FREE_TREES,
+ sizeof(*mm->free_trees),
+ GFP_KERNEL);
+ if (!mm->free_trees)
return -ENOMEM;
- for (i = 0; i <= mm->max_order; ++i)
- INIT_LIST_HEAD(&mm->free_list[i]);
+ for_each_free_tree(i) {
+ mm->free_trees[i] = kmalloc_array(mm->max_order + 1,
+ sizeof(struct rb_root),
+ GFP_KERNEL);
+ if (!mm->free_trees[i])
+ goto out_free_tree;
+
+ for (j = 0; j <= mm->max_order; ++j)
+ mm->free_trees[i][j] = RB_ROOT;
+ }
mm->n_roots = hweight64(size);
@@ -273,10 +343,7 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
sizeof(struct drm_buddy_block *),
GFP_KERNEL);
if (!mm->roots)
- goto out_free_list;
-
- offset = 0;
- i = 0;
+ goto out_free_tree;
/*
* Split into power-of-two blocks, in case we are given a size that is
@@ -296,24 +363,26 @@ int drm_buddy_init(struct drm_buddy *mm, u64 size, u64 chunk_size)
mark_free(mm, root);
- BUG_ON(i > mm->max_order);
+ BUG_ON(root_count > mm->max_order);
BUG_ON(drm_buddy_block_size(mm, root) < chunk_size);
- mm->roots[i] = root;
+ mm->roots[root_count] = root;
offset += root_size;
size -= root_size;
- i++;
+ root_count++;
} while (size);
return 0;
out_free_roots:
- while (i--)
- drm_block_free(mm, mm->roots[i]);
+ while (root_count--)
+ drm_block_free(mm, mm->roots[root_count]);
kfree(mm->roots);
-out_free_list:
- kfree(mm->free_list);
+out_free_tree:
+ while (i--)
+ kfree(mm->free_trees[i]);
+ kfree(mm->free_trees);
return -ENOMEM;
}
EXPORT_SYMBOL(drm_buddy_init);
@@ -323,7 +392,7 @@ EXPORT_SYMBOL(drm_buddy_init);
*
* @mm: DRM buddy manager to free
*
- * Cleanup memory manager resources and the freelist
+ * Cleanup memory manager resources and the freetree
*/
void drm_buddy_fini(struct drm_buddy *mm)
{
@@ -349,8 +418,9 @@ void drm_buddy_fini(struct drm_buddy *mm)
WARN_ON(mm->avail != mm->size);
+ for_each_free_tree(i)
+ kfree(mm->free_trees[i]);
kfree(mm->roots);
- kfree(mm->free_list);
}
EXPORT_SYMBOL(drm_buddy_fini);
@@ -374,8 +444,7 @@ static int split_block(struct drm_buddy *mm,
return -ENOMEM;
}
- mark_free(mm, block->left);
- mark_free(mm, block->right);
+ mark_split(mm, block);
if (drm_buddy_block_is_clear(block)) {
mark_cleared(block->left);
@@ -383,7 +452,8 @@ static int split_block(struct drm_buddy *mm,
clear_reset(block);
}
- mark_split(block);
+ mark_free(mm, block->left);
+ mark_free(mm, block->right);
return 0;
}
@@ -412,10 +482,11 @@ EXPORT_SYMBOL(drm_get_buddy);
* @is_clear: blocks clear state
*
* Reset the clear state based on @is_clear value for each block
- * in the freelist.
+ * in the freetree.
*/
void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear)
{
+ enum drm_buddy_free_tree src_tree, dst_tree;
u64 root_size, size, start;
unsigned int order;
int i;
@@ -430,19 +501,24 @@ void drm_buddy_reset_clear(struct drm_buddy *mm, bool is_clear)
size -= root_size;
}
+ src_tree = is_clear ? DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE;
+ dst_tree = is_clear ? DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE;
+
for (i = 0; i <= mm->max_order; ++i) {
- struct drm_buddy_block *block;
-
- list_for_each_entry_reverse(block, &mm->free_list[i], link) {
- if (is_clear != drm_buddy_block_is_clear(block)) {
- if (is_clear) {
- mark_cleared(block);
- mm->clear_avail += drm_buddy_block_size(mm, block);
- } else {
- clear_reset(block);
- mm->clear_avail -= drm_buddy_block_size(mm, block);
- }
+ struct rb_root *root = &mm->free_trees[src_tree][i];
+ struct drm_buddy_block *block, *tmp;
+
+ rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
+ rbtree_remove(mm, block);
+ if (is_clear) {
+ mark_cleared(block);
+ mm->clear_avail += drm_buddy_block_size(mm, block);
+ } else {
+ clear_reset(block);
+ mm->clear_avail -= drm_buddy_block_size(mm, block);
}
+
+ rbtree_insert(mm, block, dst_tree);
}
}
}
@@ -632,23 +708,17 @@ __drm_buddy_alloc_range_bias(struct drm_buddy *mm,
}
static struct drm_buddy_block *
-get_maxblock(struct drm_buddy *mm, unsigned int order,
- unsigned long flags)
+get_maxblock(struct drm_buddy *mm,
+ unsigned int order,
+ enum drm_buddy_free_tree tree)
{
struct drm_buddy_block *max_block = NULL, *block = NULL;
+ struct rb_root *root;
unsigned int i;
for (i = order; i <= mm->max_order; ++i) {
- struct drm_buddy_block *tmp_block;
-
- list_for_each_entry_reverse(tmp_block, &mm->free_list[i], link) {
- if (block_incompatible(tmp_block, flags))
- continue;
-
- block = tmp_block;
- break;
- }
-
+ root = &mm->free_trees[tree][i];
+ block = rbtree_last_free_block(root);
if (!block)
continue;
@@ -667,46 +737,44 @@ get_maxblock(struct drm_buddy *mm, unsigned int order,
}
static struct drm_buddy_block *
-alloc_from_freelist(struct drm_buddy *mm,
+alloc_from_freetree(struct drm_buddy *mm,
unsigned int order,
unsigned long flags)
{
struct drm_buddy_block *block = NULL;
+ struct rb_root *root;
+ enum drm_buddy_free_tree tree;
unsigned int tmp;
int err;
+ tree = (flags & DRM_BUDDY_CLEAR_ALLOCATION) ?
+ DRM_BUDDY_CLEAR_TREE : DRM_BUDDY_DIRTY_TREE;
+
if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
- block = get_maxblock(mm, order, flags);
+ block = get_maxblock(mm, order, tree);
if (block)
/* Store the obtained block order */
tmp = drm_buddy_block_order(block);
} else {
for (tmp = order; tmp <= mm->max_order; ++tmp) {
- struct drm_buddy_block *tmp_block;
-
- list_for_each_entry_reverse(tmp_block, &mm->free_list[tmp], link) {
- if (block_incompatible(tmp_block, flags))
- continue;
-
- block = tmp_block;
- break;
- }
-
+ /* Get RB tree root for this order and tree */
+ root = &mm->free_trees[tree][tmp];
+ block = rbtree_last_free_block(root);
if (block)
break;
}
}
if (!block) {
- /* Fallback method */
+ /* Try allocating from the other tree */
+ tree = (tree == DRM_BUDDY_CLEAR_TREE) ?
+ DRM_BUDDY_DIRTY_TREE : DRM_BUDDY_CLEAR_TREE;
+
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;
- }
+ root = &mm->free_trees[tree][tmp];
+ block = rbtree_last_free_block(root);
+ if (block)
+ break;
}
if (!block)
@@ -771,7 +839,7 @@ static int __alloc_range(struct drm_buddy *mm,
if (contains(start, end, block_start, block_end)) {
if (drm_buddy_block_is_free(block)) {
- mark_allocated(block);
+ mark_allocated(mm, block);
total_allocated += drm_buddy_block_size(mm, block);
mm->avail -= drm_buddy_block_size(mm, block);
if (drm_buddy_block_is_clear(block))
@@ -849,10 +917,9 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm,
{
u64 rhs_offset, lhs_offset, lhs_size, filled;
struct drm_buddy_block *block;
- struct list_head *list;
+ unsigned int tree, order;
LIST_HEAD(blocks_lhs);
unsigned long pages;
- unsigned int order;
u64 modify_size;
int err;
@@ -862,35 +929,45 @@ static int __alloc_contig_try_harder(struct drm_buddy *mm,
if (order == 0)
return -ENOSPC;
- list = &mm->free_list[order];
- if (list_empty(list))
- return -ENOSPC;
+ for_each_free_tree(tree) {
+ struct rb_root *root;
+ struct rb_node *iter;
+
+ root = &mm->free_trees[tree][order];
+ if (rbtree_is_empty(root))
+ continue;
- list_for_each_entry_reverse(block, list, link) {
- /* Allocate blocks traversing RHS */
- rhs_offset = drm_buddy_block_offset(block);
- err = __drm_buddy_alloc_range(mm, rhs_offset, size,
- &filled, blocks);
- if (!err || err != -ENOSPC)
- return err;
-
- lhs_size = max((size - filled), min_block_size);
- if (!IS_ALIGNED(lhs_size, min_block_size))
- lhs_size = round_up(lhs_size, min_block_size);
-
- /* Allocate blocks traversing LHS */
- lhs_offset = drm_buddy_block_offset(block) - lhs_size;
- err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size,
- NULL, &blocks_lhs);
- if (!err) {
- list_splice(&blocks_lhs, blocks);
- return 0;
- } else if (err != -ENOSPC) {
+ iter = rb_last(root);
+ while (iter) {
+ block = rbtree_get_free_block(iter);
+
+ /* Allocate blocks traversing RHS */
+ rhs_offset = drm_buddy_block_offset(block);
+ err = __drm_buddy_alloc_range(mm, rhs_offset, size,
+ &filled, blocks);
+ if (!err || err != -ENOSPC)
+ return err;
+
+ lhs_size = max((size - filled), min_block_size);
+ if (!IS_ALIGNED(lhs_size, min_block_size))
+ lhs_size = round_up(lhs_size, min_block_size);
+
+ /* Allocate blocks traversing LHS */
+ lhs_offset = drm_buddy_block_offset(block) - lhs_size;
+ err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size,
+ NULL, &blocks_lhs);
+ if (!err) {
+ list_splice(&blocks_lhs, blocks);
+ return 0;
+ } else if (err != -ENOSPC) {
+ drm_buddy_free_list_internal(mm, blocks);
+ return err;
+ }
+ /* Free blocks for the next iteration */
drm_buddy_free_list_internal(mm, blocks);
- return err;
+
+ iter = rb_prev(iter);
}
- /* Free blocks for the next iteration */
- drm_buddy_free_list_internal(mm, blocks);
}
return -ENOSPC;
@@ -976,7 +1053,7 @@ int drm_buddy_block_trim(struct drm_buddy *mm,
list_add(&block->tmp_link, &dfs);
err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL);
if (err) {
- mark_allocated(block);
+ mark_allocated(mm, block);
mm->avail -= drm_buddy_block_size(mm, block);
if (drm_buddy_block_is_clear(block))
mm->clear_avail -= drm_buddy_block_size(mm, block);
@@ -999,8 +1076,8 @@ __drm_buddy_alloc_blocks(struct drm_buddy *mm,
return __drm_buddy_alloc_range_bias(mm, start, end,
order, flags);
else
- /* Allocate from freelist */
- return alloc_from_freelist(mm, order, flags);
+ /* Allocate from freetree */
+ return alloc_from_freetree(mm, order, flags);
}
/**
@@ -1017,8 +1094,8 @@ __drm_buddy_alloc_blocks(struct drm_buddy *mm,
* alloc_range_bias() called on range limitations, which traverses
* the tree and returns the desired block.
*
- * alloc_from_freelist() called when *no* range restrictions
- * are enforced, which picks the block from the freelist.
+ * alloc_from_freetree() called when *no* range restrictions
+ * are enforced, which picks the block from the freetree.
*
* Returns:
* 0 on success, error code on failure.
@@ -1120,7 +1197,7 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm,
}
} while (1);
- mark_allocated(block);
+ mark_allocated(mm, block);
mm->avail -= drm_buddy_block_size(mm, block);
if (drm_buddy_block_is_clear(block))
mm->clear_avail -= drm_buddy_block_size(mm, block);
@@ -1201,12 +1278,18 @@ void drm_buddy_print(struct drm_buddy *mm, struct drm_printer *p)
mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20);
for (order = mm->max_order; order >= 0; order--) {
- struct drm_buddy_block *block;
+ struct drm_buddy_block *block, *tmp;
+ struct rb_root *root;
u64 count = 0, free;
+ unsigned int tree;
- list_for_each_entry(block, &mm->free_list[order], link) {
- BUG_ON(!drm_buddy_block_is_free(block));
- count++;
+ for_each_free_tree(tree) {
+ root = &mm->free_trees[tree][order];
+
+ rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) {
+ BUG_ON(!drm_buddy_block_is_free(block));
+ count++;
+ }
}
drm_printf(p, "order-%2d ", order);
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 3fa38d4ac70b..a82d741e6630 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -11,12 +11,14 @@
#include <linux/slab.h>
#include <drm/drm_client.h>
+#include <drm/drm_client_event.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem.h>
+#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mode.h>
#include <drm/drm_print.h>
@@ -168,29 +170,59 @@ void drm_client_release(struct drm_client_dev *client)
drm_client_modeset_free(client);
drm_client_close(client);
+
+ if (client->funcs && client->funcs->free)
+ client->funcs->free(client);
+
drm_dev_put(dev);
}
EXPORT_SYMBOL(drm_client_release);
-static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
+/**
+ * drm_client_buffer_delete - Delete a client buffer
+ * @buffer: DRM client buffer
+ */
+void drm_client_buffer_delete(struct drm_client_buffer *buffer)
{
- if (buffer->gem) {
- drm_gem_vunmap(buffer->gem, &buffer->map);
- drm_gem_object_put(buffer->gem);
- }
+ struct drm_gem_object *gem;
+ int ret;
+
+ if (!buffer)
+ return;
+
+ gem = buffer->fb->obj[0];
+ drm_gem_vunmap(gem, &buffer->map);
+
+ ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
+ if (ret)
+ drm_err(buffer->client->dev,
+ "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
+
+ drm_gem_object_put(buffer->gem);
kfree(buffer);
}
+EXPORT_SYMBOL(drm_client_buffer_delete);
static struct drm_client_buffer *
drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,
- u32 format, u32 *handle)
+ u32 format, u32 handle, u32 pitch)
{
- const struct drm_format_info *info = drm_format_info(format);
- struct drm_mode_create_dumb dumb_args = { };
+ struct drm_mode_fb_cmd2 fb_req = {
+ .width = width,
+ .height = height,
+ .pixel_format = format,
+ .handles = {
+ handle,
+ },
+ .pitches = {
+ pitch,
+ },
+ };
struct drm_device *dev = client->dev;
struct drm_client_buffer *buffer;
struct drm_gem_object *obj;
+ struct drm_framebuffer *fb;
int ret;
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
@@ -199,28 +231,38 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,
buffer->client = client;
- dumb_args.width = width;
- dumb_args.height = height;
- dumb_args.bpp = drm_format_info_bpp(info, 0);
- ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
- if (ret)
- goto err_delete;
-
- obj = drm_gem_object_lookup(client->file, dumb_args.handle);
+ obj = drm_gem_object_lookup(client->file, handle);
if (!obj) {
ret = -ENOENT;
goto err_delete;
}
- buffer->pitch = dumb_args.pitch;
+ ret = drm_mode_addfb2(dev, &fb_req, client->file);
+ if (ret)
+ goto err_drm_gem_object_put;
+
+ fb = drm_framebuffer_lookup(dev, client->file, fb_req.fb_id);
+ if (drm_WARN_ON(dev, !fb)) {
+ ret = -ENOENT;
+ goto err_drm_mode_rmfb;
+ }
+
+ /* drop the reference we picked up in framebuffer lookup */
+ drm_framebuffer_put(fb);
+
+ strscpy(fb->comm, client->name, TASK_COMM_LEN);
+
buffer->gem = obj;
- *handle = dumb_args.handle;
+ buffer->fb = fb;
return buffer;
+err_drm_mode_rmfb:
+ drm_mode_rmfb(dev, fb_req.fb_id, client->file);
+err_drm_gem_object_put:
+ drm_gem_object_put(obj);
err_delete:
- drm_client_buffer_delete(buffer);
-
+ kfree(buffer);
return ERR_PTR(ret);
}
@@ -247,7 +289,7 @@ err_delete:
int drm_client_buffer_vmap_local(struct drm_client_buffer *buffer,
struct iosys_map *map_copy)
{
- struct drm_gem_object *gem = buffer->gem;
+ struct drm_gem_object *gem = buffer->fb->obj[0];
struct iosys_map *map = &buffer->map;
int ret;
@@ -276,7 +318,7 @@ EXPORT_SYMBOL(drm_client_buffer_vmap_local);
*/
void drm_client_buffer_vunmap_local(struct drm_client_buffer *buffer)
{
- struct drm_gem_object *gem = buffer->gem;
+ struct drm_gem_object *gem = buffer->fb->obj[0];
struct iosys_map *map = &buffer->map;
drm_gem_vunmap_locked(gem, map);
@@ -307,9 +349,10 @@ EXPORT_SYMBOL(drm_client_buffer_vunmap_local);
int drm_client_buffer_vmap(struct drm_client_buffer *buffer,
struct iosys_map *map_copy)
{
+ struct drm_gem_object *gem = buffer->fb->obj[0];
int ret;
- ret = drm_gem_vmap(buffer->gem, &buffer->map);
+ ret = drm_gem_vmap(gem, &buffer->map);
if (ret)
return ret;
*map_copy = buffer->map;
@@ -328,57 +371,14 @@ EXPORT_SYMBOL(drm_client_buffer_vmap);
*/
void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
{
- drm_gem_vunmap(buffer->gem, &buffer->map);
-}
-EXPORT_SYMBOL(drm_client_buffer_vunmap);
+ struct drm_gem_object *gem = buffer->fb->obj[0];
-static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
-{
- int ret;
-
- if (!buffer->fb)
- return;
-
- ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
- if (ret)
- drm_err(buffer->client->dev,
- "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
-
- buffer->fb = NULL;
-}
-
-static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
- u32 width, u32 height, u32 format,
- u32 handle)
-{
- struct drm_client_dev *client = buffer->client;
- struct drm_mode_fb_cmd2 fb_req = { };
- int ret;
-
- fb_req.width = width;
- fb_req.height = height;
- fb_req.pixel_format = format;
- fb_req.handles[0] = handle;
- fb_req.pitches[0] = buffer->pitch;
-
- ret = drm_mode_addfb2(client->dev, &fb_req, client->file);
- if (ret)
- return ret;
-
- buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
- if (WARN_ON(!buffer->fb))
- return -ENOENT;
-
- /* drop the reference we picked up in framebuffer lookup */
- drm_framebuffer_put(buffer->fb);
-
- strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
-
- return 0;
+ drm_gem_vunmap(gem, &buffer->map);
}
+EXPORT_SYMBOL(drm_client_buffer_vunmap);
/**
- * drm_client_framebuffer_create - Create a client framebuffer
+ * drm_client_buffer_create_dumb - Create a client buffer backed by a dumb buffer
* @client: DRM client
* @width: Framebuffer width
* @height: Framebuffer height
@@ -386,24 +386,33 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
*
* This function creates a &drm_client_buffer which consists of a
* &drm_framebuffer backed by a dumb buffer.
- * Call drm_client_framebuffer_delete() to free the buffer.
+ * Call drm_client_buffer_delete() to free the buffer.
*
* Returns:
* Pointer to a client buffer or an error pointer on failure.
*/
struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_buffer_create_dumb(struct drm_client_dev *client, u32 width, u32 height, u32 format)
{
+ const struct drm_format_info *info = drm_format_info(format);
+ struct drm_device *dev = client->dev;
+ struct drm_mode_create_dumb dumb_args = { };
struct drm_client_buffer *buffer;
- u32 handle;
int ret;
- buffer = drm_client_buffer_create(client, width, height, format,
- &handle);
- if (IS_ERR(buffer))
- return buffer;
+ dumb_args.width = width;
+ dumb_args.height = height;
+ dumb_args.bpp = drm_format_info_bpp(info, 0);
+ ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
+ if (ret)
+ return ERR_PTR(ret);
- ret = drm_client_buffer_addfb(buffer, width, height, format, handle);
+ buffer = drm_client_buffer_create(client, width, height, format,
+ dumb_args.handle, dumb_args.pitch);
+ if (IS_ERR(buffer)) {
+ ret = PTR_ERR(buffer);
+ goto err_drm_mode_destroy_dumb;
+ }
/*
* The handle is only needed for creating the framebuffer, destroy it
@@ -411,34 +420,19 @@ drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 heig
* 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);
- }
+ drm_mode_destroy_dumb(client->dev, dumb_args.handle, client->file);
return buffer;
-}
-EXPORT_SYMBOL(drm_client_framebuffer_create);
-
-/**
- * drm_client_framebuffer_delete - Delete a client framebuffer
- * @buffer: DRM client buffer (can be NULL)
- */
-void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
-{
- if (!buffer)
- return;
- drm_client_buffer_rmfb(buffer);
- drm_client_buffer_delete(buffer);
+err_drm_mode_destroy_dumb:
+ drm_mode_destroy_dumb(client->dev, dumb_args.handle, client->file);
+ return ERR_PTR(ret);
}
-EXPORT_SYMBOL(drm_client_framebuffer_delete);
+EXPORT_SYMBOL(drm_client_buffer_create_dumb);
/**
- * drm_client_framebuffer_flush - Manually flush client framebuffer
- * @buffer: DRM client buffer (can be NULL)
+ * drm_client_buffer_flush - Manually flush client buffer
+ * @buffer: DRM client buffer
* @rect: Damage rectangle (if NULL flushes all)
*
* This calls &drm_framebuffer_funcs->dirty (if present) to flush buffer changes
@@ -447,7 +441,7 @@ EXPORT_SYMBOL(drm_client_framebuffer_delete);
* Returns:
* Zero on success or negative error code on failure.
*/
-int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect)
+int drm_client_buffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect)
{
if (!buffer || !buffer->fb || !buffer->fb->funcs->dirty)
return 0;
@@ -467,4 +461,4 @@ int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_re
return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
0, 0, NULL, 0);
}
-EXPORT_SYMBOL(drm_client_framebuffer_flush);
+EXPORT_SYMBOL(drm_client_buffer_flush);
diff --git a/drivers/gpu/drm/drm_client_event.c b/drivers/gpu/drm/drm_client_event.c
index c83196ad8b59..7b3e362f7926 100644
--- a/drivers/gpu/drm/drm_client_event.c
+++ b/drivers/gpu/drm/drm_client_event.c
@@ -39,12 +39,13 @@ void drm_client_dev_unregister(struct drm_device *dev)
mutex_lock(&dev->clientlist_mutex);
list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
list_del(&client->list);
- if (client->funcs && client->funcs->unregister) {
+ /*
+ * Unregistering consumes and frees the client.
+ */
+ if (client->funcs && client->funcs->unregister)
client->funcs->unregister(client);
- } else {
+ else
drm_client_release(client);
- kfree(client);
- }
}
mutex_unlock(&dev->clientlist_mutex);
}
@@ -101,7 +102,7 @@ void drm_client_dev_hotplug(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_client_dev_hotplug);
-void drm_client_dev_restore(struct drm_device *dev)
+void drm_client_dev_restore(struct drm_device *dev, bool force)
{
struct drm_client_dev *client;
int ret;
@@ -114,7 +115,7 @@ void drm_client_dev_restore(struct drm_device *dev)
if (!client->funcs || !client->funcs->restore)
continue;
- ret = client->funcs->restore(client);
+ ret = client->funcs->restore(client, force);
drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
if (!ret) /* The first one to return zero gets the privilege to restore */
break;
@@ -122,7 +123,7 @@ void drm_client_dev_restore(struct drm_device *dev)
mutex_unlock(&dev->clientlist_mutex);
}
-static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_client_suspend(struct drm_client_dev *client)
{
struct drm_device *dev = client->dev;
int ret = 0;
@@ -131,7 +132,7 @@ static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_
return 0;
if (client->funcs && client->funcs->suspend)
- ret = client->funcs->suspend(client, holds_console_lock);
+ ret = client->funcs->suspend(client);
drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
client->suspended = true;
@@ -139,20 +140,20 @@ static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_
return ret;
}
-void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock)
+void drm_client_dev_suspend(struct drm_device *dev)
{
struct drm_client_dev *client;
mutex_lock(&dev->clientlist_mutex);
list_for_each_entry(client, &dev->clientlist, list) {
if (!client->suspended)
- drm_client_suspend(client, holds_console_lock);
+ drm_client_suspend(client);
}
mutex_unlock(&dev->clientlist_mutex);
}
EXPORT_SYMBOL(drm_client_dev_suspend);
-static int drm_client_resume(struct drm_client_dev *client, bool holds_console_lock)
+static int drm_client_resume(struct drm_client_dev *client)
{
struct drm_device *dev = client->dev;
int ret = 0;
@@ -161,7 +162,7 @@ static int drm_client_resume(struct drm_client_dev *client, bool holds_console_l
return 0;
if (client->funcs && client->funcs->resume)
- ret = client->funcs->resume(client, holds_console_lock);
+ ret = client->funcs->resume(client);
drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
client->suspended = false;
@@ -172,14 +173,14 @@ static int drm_client_resume(struct drm_client_dev *client, bool holds_console_l
return ret;
}
-void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock)
+void drm_client_dev_resume(struct drm_device *dev)
{
struct drm_client_dev *client;
mutex_lock(&dev->clientlist_mutex);
list_for_each_entry(client, &dev->clientlist, list) {
if (client->suspended)
- drm_client_resume(client, holds_console_lock);
+ drm_client_resume(client);
}
mutex_unlock(&dev->clientlist_mutex);
}
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c
index 9c2c3b0c8c47..fc4caf7da5fc 100644
--- a/drivers/gpu/drm/drm_client_modeset.c
+++ b/drivers/gpu/drm/drm_client_modeset.c
@@ -1293,6 +1293,50 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode)
}
EXPORT_SYMBOL(drm_client_modeset_dpms);
+/**
+ * drm_client_modeset_wait_for_vblank() - Wait for the next VBLANK to occur
+ * @client: DRM client
+ * @crtc_index: The ndex of the CRTC to wait on
+ *
+ * Block the caller until the given CRTC has seen a VBLANK. Do nothing
+ * if the CRTC is disabled. If there's another DRM master present, fail
+ * with -EBUSY.
+ *
+ * Returns:
+ * 0 on success, or negative error code otherwise.
+ */
+int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned int crtc_index)
+{
+ struct drm_device *dev = client->dev;
+ struct drm_crtc *crtc;
+ int ret;
+
+ /*
+ * Rate-limit update frequency to vblank. If there's a DRM master
+ * present, it could interfere while we're waiting for the vblank
+ * event. Don't wait in this case.
+ */
+ if (!drm_master_internal_acquire(dev))
+ return -EBUSY;
+
+ crtc = client->modesets[crtc_index].crtc;
+
+ /*
+ * Only wait for a vblank event if the CRTC is enabled, otherwise
+ * just don't do anything, not even report an error.
+ */
+ ret = drm_crtc_vblank_get(crtc);
+ if (!ret) {
+ drm_crtc_wait_one_vblank(crtc);
+ drm_crtc_vblank_put(crtc);
+ }
+
+ drm_master_internal_release(dev);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_client_modeset_wait_for_vblank);
+
#ifdef CONFIG_DRM_KUNIT_TEST
#include "tests/drm_client_modeset_test.c"
#endif
diff --git a/drivers/gpu/drm/drm_client_sysrq.c b/drivers/gpu/drm/drm_client_sysrq.c
new file mode 100644
index 000000000000..eea660096f1b
--- /dev/null
+++ b/drivers/gpu/drm/drm_client_sysrq.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+#include <linux/sysrq.h>
+
+#include <drm/drm_client_event.h>
+#include <drm/drm_device.h>
+#include <drm/drm_print.h>
+
+#include "drm_internal.h"
+
+#ifdef CONFIG_MAGIC_SYSRQ
+static LIST_HEAD(drm_client_sysrq_dev_list);
+static DEFINE_MUTEX(drm_client_sysrq_dev_lock);
+
+/* emergency restore, don't bother with error reporting */
+static void drm_client_sysrq_restore_work_fn(struct work_struct *ignored)
+{
+ struct drm_device *dev;
+
+ guard(mutex)(&drm_client_sysrq_dev_lock);
+
+ list_for_each_entry(dev, &drm_client_sysrq_dev_list, client_sysrq_list) {
+ if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+ continue;
+
+ drm_client_dev_restore(dev, true);
+ }
+}
+
+static DECLARE_WORK(drm_client_sysrq_restore_work, drm_client_sysrq_restore_work_fn);
+
+static void drm_client_sysrq_restore_handler(u8 ignored)
+{
+ schedule_work(&drm_client_sysrq_restore_work);
+}
+
+static const struct sysrq_key_op drm_client_sysrq_restore_op = {
+ .handler = drm_client_sysrq_restore_handler,
+ .help_msg = "force-fb(v)",
+ .action_msg = "Restore framebuffer console",
+};
+
+void drm_client_sysrq_register(struct drm_device *dev)
+{
+ guard(mutex)(&drm_client_sysrq_dev_lock);
+
+ if (list_empty(&drm_client_sysrq_dev_list))
+ register_sysrq_key('v', &drm_client_sysrq_restore_op);
+
+ list_add(&dev->client_sysrq_list, &drm_client_sysrq_dev_list);
+}
+
+void drm_client_sysrq_unregister(struct drm_device *dev)
+{
+ guard(mutex)(&drm_client_sysrq_dev_lock);
+
+ /* remove device from global restore list */
+ if (!drm_WARN_ON(dev, list_empty(&dev->client_sysrq_list)))
+ list_del(&dev->client_sysrq_list);
+
+ /* no devices left; unregister key */
+ if (list_empty(&drm_client_sysrq_dev_list))
+ unregister_sysrq_key('v', &drm_client_sysrq_restore_op);
+}
+#endif
diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c
index 131c1c9ae92f..c598b99673fc 100644
--- a/drivers/gpu/drm/drm_color_mgmt.c
+++ b/drivers/gpu/drm/drm_color_mgmt.c
@@ -874,3 +874,46 @@ void drm_crtc_fill_palette_8(struct drm_crtc *crtc, drm_crtc_set_lut_func set_pa
fill_palette_8(crtc, i, set_palette);
}
EXPORT_SYMBOL(drm_crtc_fill_palette_8);
+
+/**
+ * drm_color_lut32_check - check validity of extended lookup table
+ * @lut: property blob containing extended LUT to check
+ * @tests: bitmask of tests to run
+ *
+ * Helper to check whether a userspace-provided extended lookup table is valid and
+ * satisfies hardware requirements. Drivers pass a bitmask indicating which of
+ * the tests in &drm_color_lut_tests should be performed.
+ *
+ * Returns 0 on success, -EINVAL on failure.
+ */
+int drm_color_lut32_check(const struct drm_property_blob *lut, u32 tests)
+{
+ const struct drm_color_lut32 *entry;
+ int i;
+
+ if (!lut || !tests)
+ return 0;
+
+ entry = lut->data;
+ for (i = 0; i < drm_color_lut32_size(lut); i++) {
+ if (tests & DRM_COLOR_LUT_EQUAL_CHANNELS) {
+ if (entry[i].red != entry[i].blue ||
+ entry[i].red != entry[i].green) {
+ DRM_DEBUG_KMS("All LUT entries must have equal r/g/b\n");
+ return -EINVAL;
+ }
+ }
+
+ if (i > 0 && tests & DRM_COLOR_LUT_NON_DECREASING) {
+ if (entry[i].red < entry[i - 1].red ||
+ entry[i].green < entry[i - 1].green ||
+ entry[i].blue < entry[i - 1].blue) {
+ DRM_DEBUG_KMS("LUT entries must never decrease.\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_color_lut32_check);
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
new file mode 100644
index 000000000000..44eb823585d2
--- /dev/null
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (C) 2023 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * 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.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include <drm/drm_colorop.h>
+#include <drm/drm_print.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_plane.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * When userspace signals the &DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE it
+ * should use the COLOR_PIPELINE plane property and associated colorops
+ * for any color operation on the &drm_plane. Setting of all old color
+ * properties, such as COLOR_ENCODING and COLOR_RANGE, will be rejected
+ * and the values of the properties will be ignored.
+ *
+ * Colorops are only advertised and valid for atomic drivers and atomic
+ * userspace that signals the &DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
+ * client cap.
+ *
+ * A colorop represents a single color operation. Colorops are chained
+ * via the NEXT property and make up color pipelines. Color pipelines
+ * are advertised and selected via the COLOR_PIPELINE &drm_plane
+ * property.
+ *
+ * A colorop will be of a certain type, advertised by the read-only TYPE
+ * property. Each type of colorop will advertise a different set of
+ * properties and is programmed in a different manner. Types can be
+ * enumerated 1D curves, 1D LUTs, 3D LUTs, matrices, etc. See the
+ * &drm_colorop_type documentation for information on each type.
+ *
+ * If a colorop advertises the BYPASS property it can be bypassed.
+ *
+ * Information about colorop and color pipeline design decisions can be
+ * found at rfc/color_pipeline.rst, but note that this document will
+ * grow stale over time.
+ */
+
+static const struct drm_prop_enum_list drm_colorop_type_enum_list[] = {
+ { DRM_COLOROP_1D_CURVE, "1D Curve" },
+ { DRM_COLOROP_1D_LUT, "1D LUT" },
+ { DRM_COLOROP_CTM_3X4, "3x4 Matrix"},
+ { DRM_COLOROP_MULTIPLIER, "Multiplier"},
+ { DRM_COLOROP_3D_LUT, "3D LUT"},
+};
+
+static const char * const colorop_curve_1d_type_names[] = {
+ [DRM_COLOROP_1D_CURVE_SRGB_EOTF] = "sRGB EOTF",
+ [DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF] = "sRGB Inverse EOTF",
+ [DRM_COLOROP_1D_CURVE_PQ_125_EOTF] = "PQ 125 EOTF",
+ [DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF] = "PQ 125 Inverse EOTF",
+ [DRM_COLOROP_1D_CURVE_BT2020_INV_OETF] = "BT.2020 Inverse OETF",
+ [DRM_COLOROP_1D_CURVE_BT2020_OETF] = "BT.2020 OETF",
+ [DRM_COLOROP_1D_CURVE_GAMMA22] = "Gamma 2.2",
+ [DRM_COLOROP_1D_CURVE_GAMMA22_INV] = "Gamma 2.2 Inverse",
+};
+
+static const struct drm_prop_enum_list drm_colorop_lut1d_interpolation_list[] = {
+ { DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR, "Linear" },
+};
+
+
+static const struct drm_prop_enum_list drm_colorop_lut3d_interpolation_list[] = {
+ { DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL, "Tetrahedral" },
+};
+
+/* Init Helpers */
+
+static int drm_plane_colorop_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane, enum drm_colorop_type type,
+ uint32_t flags)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_property *prop;
+ int ret = 0;
+
+ ret = drm_mode_object_add(dev, &colorop->base, DRM_MODE_OBJECT_COLOROP);
+ if (ret)
+ return ret;
+
+ colorop->base.properties = &colorop->properties;
+ colorop->dev = dev;
+ colorop->type = type;
+ colorop->plane = plane;
+ colorop->next = NULL;
+
+ list_add_tail(&colorop->head, &config->colorop_list);
+ colorop->index = config->num_colorop++;
+
+ /* add properties */
+
+ /* type */
+ prop = drm_property_create_enum(dev,
+ DRM_MODE_PROP_IMMUTABLE,
+ "TYPE", drm_colorop_type_enum_list,
+ ARRAY_SIZE(drm_colorop_type_enum_list));
+
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->type_property = prop;
+
+ drm_object_attach_property(&colorop->base,
+ colorop->type_property,
+ colorop->type);
+
+ if (flags & DRM_COLOROP_FLAG_ALLOW_BYPASS) {
+ /* bypass */
+ prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
+ "BYPASS");
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->bypass_property = prop;
+ drm_object_attach_property(&colorop->base,
+ colorop->bypass_property,
+ 1);
+ }
+
+ /* next */
+ prop = drm_property_create_object(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ATOMIC,
+ "NEXT", DRM_MODE_OBJECT_COLOROP);
+ if (!prop)
+ return -ENOMEM;
+ colorop->next_property = prop;
+ drm_object_attach_property(&colorop->base,
+ colorop->next_property,
+ 0);
+
+ return ret;
+}
+
+/**
+ * drm_colorop_cleanup - Cleanup a drm_colorop object in color_pipeline
+ *
+ * @colorop: The drm_colorop object to be cleaned
+ */
+void drm_colorop_cleanup(struct drm_colorop *colorop)
+{
+ struct drm_device *dev = colorop->dev;
+ struct drm_mode_config *config = &dev->mode_config;
+
+ list_del(&colorop->head);
+ config->num_colorop--;
+
+ if (colorop->state && colorop->state->data) {
+ drm_property_blob_put(colorop->state->data);
+ colorop->state->data = NULL;
+ }
+
+ kfree(colorop->state);
+}
+EXPORT_SYMBOL(drm_colorop_cleanup);
+
+/**
+ * drm_colorop_pipeline_destroy - Helper for color pipeline destruction
+ *
+ * @dev: - The drm_device containing the drm_planes with the color_pipelines
+ *
+ * Provides a default color pipeline destroy handler for drm_device.
+ */
+void drm_colorop_pipeline_destroy(struct drm_device *dev)
+{
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_colorop *colorop, *next;
+
+ list_for_each_entry_safe(colorop, next, &config->colorop_list, head) {
+ drm_colorop_cleanup(colorop);
+ kfree(colorop);
+ }
+}
+EXPORT_SYMBOL(drm_colorop_pipeline_destroy);
+
+/**
+ * drm_plane_colorop_curve_1d_init - Initialize a DRM_COLOROP_1D_CURVE
+ *
+ * @dev: DRM device
+ * @colorop: The drm_colorop object to initialize
+ * @plane: The associated drm_plane
+ * @supported_tfs: A bitfield of supported drm_plane_colorop_curve_1d_init enum values,
+ * created using BIT(curve_type) and combined with the OR '|'
+ * operator.
+ * @flags: bitmask of misc, see DRM_COLOROP_FLAG_* defines.
+ * @return zero on success, -E value on failure
+ */
+int drm_plane_colorop_curve_1d_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane, u64 supported_tfs, uint32_t flags)
+{
+ struct drm_prop_enum_list enum_list[DRM_COLOROP_1D_CURVE_COUNT];
+ int i, len;
+
+ struct drm_property *prop;
+ int ret;
+
+ if (!supported_tfs) {
+ drm_err(dev,
+ "No supported TFs for new 1D curve colorop on [PLANE:%d:%s]\n",
+ plane->base.id, plane->name);
+ return -EINVAL;
+ }
+
+ if ((supported_tfs & -BIT(DRM_COLOROP_1D_CURVE_COUNT)) != 0) {
+ drm_err(dev, "Unknown TF provided on [PLANE:%d:%s]\n",
+ plane->base.id, plane->name);
+ return -EINVAL;
+ }
+
+ ret = drm_plane_colorop_init(dev, colorop, plane, DRM_COLOROP_1D_CURVE, flags);
+ if (ret)
+ return ret;
+
+ len = 0;
+ for (i = 0; i < DRM_COLOROP_1D_CURVE_COUNT; i++) {
+ if ((supported_tfs & BIT(i)) == 0)
+ continue;
+
+ enum_list[len].type = i;
+ enum_list[len].name = colorop_curve_1d_type_names[i];
+ len++;
+ }
+
+ if (WARN_ON(len <= 0))
+ return -EINVAL;
+
+ /* initialize 1D curve only attribute */
+ prop = drm_property_create_enum(dev, DRM_MODE_PROP_ATOMIC, "CURVE_1D_TYPE",
+ enum_list, len);
+
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->curve_1d_type_property = prop;
+ drm_object_attach_property(&colorop->base, colorop->curve_1d_type_property,
+ enum_list[0].type);
+ drm_colorop_reset(colorop);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_colorop_curve_1d_init);
+
+static int drm_colorop_create_data_prop(struct drm_device *dev, struct drm_colorop *colorop)
+{
+ struct drm_property *prop;
+
+ /* data */
+ prop = drm_property_create(dev, DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
+ "DATA", 0);
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->data_property = prop;
+ drm_object_attach_property(&colorop->base,
+ colorop->data_property,
+ 0);
+
+ return 0;
+}
+
+/**
+ * drm_plane_colorop_curve_1d_lut_init - Initialize a DRM_COLOROP_1D_LUT
+ *
+ * @dev: DRM device
+ * @colorop: The drm_colorop object to initialize
+ * @plane: The associated drm_plane
+ * @lut_size: LUT size supported by driver
+ * @interpolation: 1D LUT interpolation type
+ * @flags: bitmask of misc, see DRM_COLOROP_FLAG_* defines.
+ * @return zero on success, -E value on failure
+ */
+int drm_plane_colorop_curve_1d_lut_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane, uint32_t lut_size,
+ enum drm_colorop_lut1d_interpolation_type interpolation,
+ uint32_t flags)
+{
+ struct drm_property *prop;
+ int ret;
+
+ ret = drm_plane_colorop_init(dev, colorop, plane, DRM_COLOROP_1D_LUT, flags);
+ if (ret)
+ return ret;
+
+ /* initialize 1D LUT only attribute */
+ /* LUT size */
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ATOMIC,
+ "SIZE", 0, UINT_MAX);
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->size_property = prop;
+ drm_object_attach_property(&colorop->base, colorop->size_property, lut_size);
+ colorop->size = lut_size;
+
+ /* interpolation */
+ prop = drm_property_create_enum(dev, 0, "LUT1D_INTERPOLATION",
+ drm_colorop_lut1d_interpolation_list,
+ ARRAY_SIZE(drm_colorop_lut1d_interpolation_list));
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->lut1d_interpolation_property = prop;
+ drm_object_attach_property(&colorop->base, prop, interpolation);
+ colorop->lut1d_interpolation = interpolation;
+
+ /* data */
+ ret = drm_colorop_create_data_prop(dev, colorop);
+ if (ret)
+ return ret;
+
+ drm_colorop_reset(colorop);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_colorop_curve_1d_lut_init);
+
+int drm_plane_colorop_ctm_3x4_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane, uint32_t flags)
+{
+ int ret;
+
+ ret = drm_plane_colorop_init(dev, colorop, plane, DRM_COLOROP_CTM_3X4, flags);
+ if (ret)
+ return ret;
+
+ ret = drm_colorop_create_data_prop(dev, colorop);
+ if (ret)
+ return ret;
+
+ drm_colorop_reset(colorop);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_colorop_ctm_3x4_init);
+
+/**
+ * drm_plane_colorop_mult_init - Initialize a DRM_COLOROP_MULTIPLIER
+ *
+ * @dev: DRM device
+ * @colorop: The drm_colorop object to initialize
+ * @plane: The associated drm_plane
+ * @flags: bitmask of misc, see DRM_COLOROP_FLAG_* defines.
+ * @return zero on success, -E value on failure
+ */
+int drm_plane_colorop_mult_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane, uint32_t flags)
+{
+ struct drm_property *prop;
+ int ret;
+
+ ret = drm_plane_colorop_init(dev, colorop, plane, DRM_COLOROP_MULTIPLIER, flags);
+ if (ret)
+ return ret;
+
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, "MULTIPLIER", 0, U64_MAX);
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->multiplier_property = prop;
+ drm_object_attach_property(&colorop->base, colorop->multiplier_property, 0);
+
+ drm_colorop_reset(colorop);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_colorop_mult_init);
+
+int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *colorop,
+ struct drm_plane *plane,
+ uint32_t lut_size,
+ enum drm_colorop_lut3d_interpolation_type interpolation,
+ uint32_t flags)
+{
+ struct drm_property *prop;
+ int ret;
+
+ ret = drm_plane_colorop_init(dev, colorop, plane, DRM_COLOROP_3D_LUT, flags);
+ if (ret)
+ return ret;
+
+ /* LUT size */
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ATOMIC,
+ "SIZE", 0, UINT_MAX);
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->size_property = prop;
+ drm_object_attach_property(&colorop->base, colorop->size_property, lut_size);
+ colorop->size = lut_size;
+
+ /* interpolation */
+ prop = drm_property_create_enum(dev, 0, "LUT3D_INTERPOLATION",
+ drm_colorop_lut3d_interpolation_list,
+ ARRAY_SIZE(drm_colorop_lut3d_interpolation_list));
+ if (!prop)
+ return -ENOMEM;
+
+ colorop->lut3d_interpolation_property = prop;
+ drm_object_attach_property(&colorop->base, prop, interpolation);
+ colorop->lut3d_interpolation = interpolation;
+
+ /* data */
+ ret = drm_colorop_create_data_prop(dev, colorop);
+ if (ret)
+ return ret;
+
+ drm_colorop_reset(colorop);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_colorop_3dlut_init);
+
+static void __drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop,
+ struct drm_colorop_state *state)
+{
+ memcpy(state, colorop->state, sizeof(*state));
+
+ if (state->data)
+ drm_property_blob_get(state->data);
+
+ state->bypass = true;
+}
+
+struct drm_colorop_state *
+drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop)
+{
+ struct drm_colorop_state *state;
+
+ if (WARN_ON(!colorop->state))
+ return NULL;
+
+ state = kmalloc(sizeof(*state), GFP_KERNEL);
+ if (state)
+ __drm_atomic_helper_colorop_duplicate_state(colorop, state);
+
+ return state;
+}
+
+void drm_colorop_atomic_destroy_state(struct drm_colorop *colorop,
+ struct drm_colorop_state *state)
+{
+ kfree(state);
+}
+
+/**
+ * __drm_colorop_state_reset - resets colorop state to default values
+ * @colorop_state: atomic colorop state, must not be NULL
+ * @colorop: colorop object, must not be NULL
+ *
+ * Initializes the newly allocated @colorop_state with default
+ * values. This is useful for drivers that subclass the CRTC state.
+ */
+static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state,
+ struct drm_colorop *colorop)
+{
+ u64 val;
+
+ colorop_state->colorop = colorop;
+ colorop_state->bypass = true;
+
+ if (colorop->curve_1d_type_property) {
+ drm_object_property_get_default_value(&colorop->base,
+ colorop->curve_1d_type_property,
+ &val);
+ colorop_state->curve_1d_type = val;
+ }
+}
+
+/**
+ * __drm_colorop_reset - reset state on colorop
+ * @colorop: drm colorop
+ * @colorop_state: colorop state to assign
+ *
+ * Initializes the newly allocated @colorop_state and assigns it to
+ * the &drm_crtc->state pointer of @colorop, usually required when
+ * initializing the drivers or when called from the &drm_colorop_funcs.reset
+ * hook.
+ *
+ * This is useful for drivers that subclass the colorop state.
+ */
+static void __drm_colorop_reset(struct drm_colorop *colorop,
+ struct drm_colorop_state *colorop_state)
+{
+ if (colorop_state)
+ __drm_colorop_state_reset(colorop_state, colorop);
+
+ colorop->state = colorop_state;
+}
+
+void drm_colorop_reset(struct drm_colorop *colorop)
+{
+ kfree(colorop->state);
+ colorop->state = kzalloc(sizeof(*colorop->state), GFP_KERNEL);
+
+ if (colorop->state)
+ __drm_colorop_reset(colorop, colorop->state);
+}
+
+static const char * const colorop_type_name[] = {
+ [DRM_COLOROP_1D_CURVE] = "1D Curve",
+ [DRM_COLOROP_1D_LUT] = "1D LUT",
+ [DRM_COLOROP_CTM_3X4] = "3x4 Matrix",
+ [DRM_COLOROP_MULTIPLIER] = "Multiplier",
+ [DRM_COLOROP_3D_LUT] = "3D LUT",
+};
+
+static const char * const colorop_lu3d_interpolation_name[] = {
+ [DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL] = "Tetrahedral",
+};
+
+static const char * const colorop_lut1d_interpolation_name[] = {
+ [DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR] = "Linear",
+};
+
+const char *drm_get_colorop_type_name(enum drm_colorop_type type)
+{
+ if (WARN_ON(type >= ARRAY_SIZE(colorop_type_name)))
+ return "unknown";
+
+ return colorop_type_name[type];
+}
+
+const char *drm_get_colorop_curve_1d_type_name(enum drm_colorop_curve_1d_type type)
+{
+ if (WARN_ON(type >= ARRAY_SIZE(colorop_curve_1d_type_names)))
+ return "unknown";
+
+ return colorop_curve_1d_type_names[type];
+}
+
+/**
+ * drm_get_colorop_lut1d_interpolation_name: return a string for interpolation type
+ * @type: interpolation type to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
+const char *drm_get_colorop_lut1d_interpolation_name(enum drm_colorop_lut1d_interpolation_type type)
+{
+ if (WARN_ON(type >= ARRAY_SIZE(colorop_lut1d_interpolation_name)))
+ return "unknown";
+
+ return colorop_lut1d_interpolation_name[type];
+}
+
+/**
+ * drm_get_colorop_lut3d_interpolation_name - return a string for interpolation type
+ * @type: interpolation type to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
+const char *drm_get_colorop_lut3d_interpolation_name(enum drm_colorop_lut3d_interpolation_type type)
+{
+ if (WARN_ON(type >= ARRAY_SIZE(colorop_lu3d_interpolation_name)))
+ return "unknown";
+
+ return colorop_lu3d_interpolation_name[type];
+}
+
+/**
+ * drm_colorop_set_next_property - sets the next pointer
+ * @colorop: drm colorop
+ * @next: next colorop
+ *
+ * Should be used when constructing the color pipeline
+ */
+void drm_colorop_set_next_property(struct drm_colorop *colorop, struct drm_colorop *next)
+{
+ drm_object_property_set_value(&colorop->base,
+ colorop->next_property,
+ next ? next->base.id : 0);
+ colorop->next = next;
+}
+EXPORT_SYMBOL(drm_colorop_set_next_property);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 272d6254ea47..4d6dc9ebfdb5 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -3439,6 +3439,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
* properties reflect the latest status.
*/
ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic,
+ file_priv->plane_color_pipeline,
(uint32_t __user *)(unsigned long)(out_resp->props_ptr),
(uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
&out_resp->count_props);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 46655339003d..a7797d260f1e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -229,6 +229,25 @@ struct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc)
* Driver's default scaling filter
* Nearest Neighbor:
* Nearest Neighbor scaling filter
+ * SHARPNESS_STRENGTH:
+ * Atomic property for setting the sharpness strength/intensity by userspace.
+ *
+ * The value of this property is set as an integer value ranging
+ * from 0 - 255 where:
+ *
+ * 0: Sharpness feature is disabled(default value).
+ *
+ * 1: Minimum sharpness.
+ *
+ * 255: Maximum sharpness.
+ *
+ * User can gradually increase or decrease the sharpness level and can
+ * set the optimum value depending on content.
+ * This value will be passed to kernel through the UAPI.
+ * The setting of this property does not require modeset.
+ * The sharpness effect takes place post blending on the final composed output.
+ * If the feature is disabled, the content remains same without any sharpening effect
+ * and when this feature is applied, it enhances the clarity of the content.
*/
__printf(6, 0)
@@ -940,6 +959,22 @@ int drm_crtc_create_scaling_filter_property(struct drm_crtc *crtc,
}
EXPORT_SYMBOL(drm_crtc_create_scaling_filter_property);
+int drm_crtc_create_sharpness_strength_property(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_property *prop =
+ drm_property_create_range(dev, 0, "SHARPNESS_STRENGTH", 0, 255);
+
+ if (!prop)
+ return -ENOMEM;
+
+ crtc->sharpness_strength_property = prop;
+ drm_object_attach_property(&crtc->base, prop, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_crtc_create_sharpness_strength_property);
+
/**
* drm_crtc_in_clone_mode - check if the given CRTC state is in clone mode
*
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 89706aa8232f..c09409229644 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -163,6 +163,7 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
void drm_mode_object_unregister(struct drm_device *dev,
struct drm_mode_object *object);
int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
+ bool plane_color_pipeline,
uint32_t __user *prop_ptr,
uint64_t __user *prop_values,
uint32_t *arg_count_props);
diff --git a/drivers/gpu/drm/drm_displayid.c b/drivers/gpu/drm/drm_displayid.c
index b4fd43783c50..58d0bb6d2676 100644
--- a/drivers/gpu/drm/drm_displayid.c
+++ b/drivers/gpu/drm/drm_displayid.c
@@ -9,6 +9,34 @@
#include "drm_crtc_internal.h"
#include "drm_displayid_internal.h"
+enum {
+ QUIRK_IGNORE_CHECKSUM,
+};
+
+struct displayid_quirk {
+ const struct drm_edid_ident ident;
+ u8 quirks;
+};
+
+static const struct displayid_quirk quirks[] = {
+ {
+ .ident = DRM_EDID_IDENT_INIT('C', 'S', 'O', 5142, "MNE007ZA1-5"),
+ .quirks = BIT(QUIRK_IGNORE_CHECKSUM),
+ },
+};
+
+static u8 get_quirks(const struct drm_edid *drm_edid)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(quirks); i++) {
+ if (drm_edid_match(drm_edid, &quirks[i].ident))
+ return quirks[i].quirks;
+ }
+
+ return 0;
+}
+
static const struct displayid_header *
displayid_get_header(const u8 *displayid, int length, int index)
{
@@ -23,7 +51,7 @@ displayid_get_header(const u8 *displayid, int length, int index)
}
static const struct displayid_header *
-validate_displayid(const u8 *displayid, int length, int idx)
+validate_displayid(const u8 *displayid, int length, int idx, bool ignore_checksum)
{
int i, dispid_length;
u8 csum = 0;
@@ -41,33 +69,35 @@ validate_displayid(const u8 *displayid, int length, int idx)
for (i = 0; i < dispid_length; i++)
csum += displayid[idx + i];
if (csum) {
- DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum);
- return ERR_PTR(-EINVAL);
+ DRM_NOTE("DisplayID checksum invalid, remainder is %d%s\n", csum,
+ ignore_checksum ? " (ignoring)" : "");
+
+ if (!ignore_checksum)
+ return ERR_PTR(-EINVAL);
}
return base;
}
-static const u8 *drm_find_displayid_extension(const struct drm_edid *drm_edid,
- int *length, int *idx,
- int *ext_index)
+static const u8 *find_next_displayid_extension(struct displayid_iter *iter)
{
const struct displayid_header *base;
const u8 *displayid;
+ bool ignore_checksum = iter->quirks & BIT(QUIRK_IGNORE_CHECKSUM);
- displayid = drm_edid_find_extension(drm_edid, DISPLAYID_EXT, ext_index);
+ displayid = drm_edid_find_extension(iter->drm_edid, DISPLAYID_EXT, &iter->ext_index);
if (!displayid)
return NULL;
/* EDID extensions block checksum isn't for us */
- *length = EDID_LENGTH - 1;
- *idx = 1;
+ iter->length = EDID_LENGTH - 1;
+ iter->idx = 1;
- base = validate_displayid(displayid, *length, *idx);
+ base = validate_displayid(displayid, iter->length, iter->idx, ignore_checksum);
if (IS_ERR(base))
return NULL;
- *length = *idx + sizeof(*base) + base->bytes;
+ iter->length = iter->idx + sizeof(*base) + base->bytes;
return displayid;
}
@@ -78,6 +108,7 @@ void displayid_iter_edid_begin(const struct drm_edid *drm_edid,
memset(iter, 0, sizeof(*iter));
iter->drm_edid = drm_edid;
+ iter->quirks = get_quirks(drm_edid);
}
static const struct displayid_block *
@@ -126,10 +157,7 @@ __displayid_iter_next(struct displayid_iter *iter)
/* The first section we encounter is the base section */
bool base_section = !iter->section;
- iter->section = drm_find_displayid_extension(iter->drm_edid,
- &iter->length,
- &iter->idx,
- &iter->ext_index);
+ iter->section = find_next_displayid_extension(iter);
if (!iter->section) {
iter->drm_edid = NULL;
return NULL;
diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h
index 957dd0619f5c..5b1b32f73516 100644
--- a/drivers/gpu/drm/drm_displayid_internal.h
+++ b/drivers/gpu/drm/drm_displayid_internal.h
@@ -167,6 +167,8 @@ struct displayid_iter {
u8 version;
u8 primary_use;
+
+ u8 quirks;
};
void displayid_iter_edid_begin(const struct drm_edid *drm_edid,
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 8e3cb08241c8..2915118436ce 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -733,6 +733,7 @@ static int drm_dev_init(struct drm_device *dev,
INIT_LIST_HEAD(&dev->filelist);
INIT_LIST_HEAD(&dev->filelist_internal);
INIT_LIST_HEAD(&dev->clientlist);
+ INIT_LIST_HEAD(&dev->client_sysrq_list);
INIT_LIST_HEAD(&dev->vblank_event_list);
spin_lock_init(&dev->event_lock);
@@ -1100,6 +1101,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
goto err_unload;
}
drm_panic_register(dev);
+ drm_client_sysrq_register(dev);
DRM_INFO("Initialized %s %d.%d.%d for %s on minor %d\n",
driver->name, driver->major, driver->minor,
@@ -1144,6 +1146,7 @@ void drm_dev_unregister(struct drm_device *dev)
{
dev->registered = false;
+ drm_client_sysrq_unregister(dev);
drm_panic_unregister(dev);
drm_client_dev_unregister(dev);
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index 70032bba1c97..e2b62e5fb891 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -25,8 +25,11 @@
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
+#include <drm/drm_dumb_buffers.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_gem.h>
#include <drm/drm_mode.h>
+#include <drm/drm_print.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
@@ -57,6 +60,134 @@
* a hardware-specific ioctl to allocate suitable buffer objects.
*/
+static int drm_mode_align_dumb(struct drm_mode_create_dumb *args,
+ unsigned long hw_pitch_align,
+ unsigned long hw_size_align)
+{
+ u32 pitch = args->pitch;
+ u32 size;
+
+ if (!pitch)
+ return -EINVAL;
+
+ if (hw_pitch_align)
+ pitch = roundup(pitch, hw_pitch_align);
+
+ if (!hw_size_align)
+ hw_size_align = PAGE_SIZE;
+ else if (!IS_ALIGNED(hw_size_align, PAGE_SIZE))
+ return -EINVAL; /* TODO: handle this if necessary */
+
+ if (check_mul_overflow(args->height, pitch, &size))
+ return -EINVAL;
+ size = ALIGN(size, hw_size_align);
+ if (!size)
+ return -EINVAL;
+
+ args->pitch = pitch;
+ args->size = size;
+
+ return 0;
+}
+
+/**
+ * drm_mode_size_dumb - Calculates the scanline and buffer sizes for dumb buffers
+ * @dev: DRM device
+ * @args: Parameters for the dumb buffer
+ * @hw_pitch_align: Hardware scanline alignment in bytes
+ * @hw_size_align: Hardware buffer-size alignment in bytes
+ *
+ * The helper drm_mode_size_dumb() calculates the size of the buffer
+ * allocation and the scanline size for a dumb buffer. Callers have to
+ * set the buffers width, height and color mode in the argument @arg.
+ * The helper validates the correctness of the input and tests for
+ * possible overflows. If successful, it returns the dumb buffer's
+ * required scanline pitch and size in &args.
+ *
+ * The parameter @hw_pitch_align allows the driver to specifies an
+ * alignment for the scanline pitch, if the hardware requires any. The
+ * calculated pitch will be a multiple of the alignment. The parameter
+ * @hw_size_align allows to specify an alignment for buffer sizes. The
+ * provided alignment should represent requirements of the graphics
+ * hardware. drm_mode_size_dumb() handles GEM-related constraints
+ * automatically across all drivers and hardware. For example, the
+ * returned buffer size is always a multiple of PAGE_SIZE, which is
+ * required by mmap().
+ *
+ * Returns:
+ * Zero on success, or a negative error code otherwise.
+ */
+int drm_mode_size_dumb(struct drm_device *dev,
+ struct drm_mode_create_dumb *args,
+ unsigned long hw_pitch_align,
+ unsigned long hw_size_align)
+{
+ u64 pitch = 0;
+ u32 fourcc;
+
+ /*
+ * The scanline pitch depends on the buffer width and the color
+ * format. The latter is specified as a color-mode constant for
+ * which we first have to find the corresponding color format.
+ *
+ * Different color formats can have the same color-mode constant.
+ * For example XRGB8888 and BGRX8888 both have a color mode of 32.
+ * It is possible to use different formats for dumb-buffer allocation
+ * and rendering as long as all involved formats share the same
+ * color-mode constant.
+ */
+ fourcc = drm_driver_color_mode_format(dev, args->bpp);
+ if (fourcc != DRM_FORMAT_INVALID) {
+ const struct drm_format_info *info = drm_format_info(fourcc);
+
+ if (!info)
+ return -EINVAL;
+ pitch = drm_format_info_min_pitch(info, 0, args->width);
+ } else if (args->bpp) {
+ /*
+ * Some userspace throws in arbitrary values for bpp and
+ * relies on the kernel to figure it out. In this case we
+ * fall back to the old method of using bpp directly. The
+ * over-commitment of memory from the rounding is acceptable
+ * for compatibility with legacy userspace. We have a number
+ * of deprecated legacy values that are explicitly supported.
+ */
+ switch (args->bpp) {
+ default:
+ drm_warn_once(dev,
+ "Unknown color mode %u; guessing buffer size.\n",
+ args->bpp);
+ fallthrough;
+ /*
+ * These constants represent various YUV formats supported by
+ * drm_gem_afbc_get_bpp().
+ */
+ case 12: // DRM_FORMAT_YUV420_8BIT
+ case 15: // DRM_FORMAT_YUV420_10BIT
+ case 30: // DRM_FORMAT_VUY101010
+ fallthrough;
+ /*
+ * Used by Mesa and Gstreamer to allocate NV formats and others
+ * as RGB buffers. Technically, XRGB16161616F formats are RGB,
+ * but the dumb buffers are not supposed to be used for anything
+ * beyond 32 bits per pixels.
+ */
+ case 10: // DRM_FORMAT_NV{15,20,30}, DRM_FORMAT_P010
+ case 64: // DRM_FORMAT_{XRGB,XBGR,ARGB,ABGR}16161616F
+ pitch = args->width * DIV_ROUND_UP(args->bpp, SZ_8);
+ break;
+ }
+ }
+
+ if (!pitch || pitch > U32_MAX)
+ return -EINVAL;
+
+ args->pitch = pitch;
+
+ return drm_mode_align_dumb(args, hw_pitch_align, hw_size_align);
+}
+EXPORT_SYMBOL(drm_mode_size_dumb);
+
int drm_mode_create_dumb(struct drm_device *dev,
struct drm_mode_create_dumb *args,
struct drm_file *file_priv)
@@ -99,7 +230,30 @@ int drm_mode_create_dumb(struct drm_device *dev,
int drm_mode_create_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
- return drm_mode_create_dumb(dev, data, file_priv);
+ struct drm_mode_create_dumb *args = data;
+ int err;
+
+ err = drm_mode_create_dumb(dev, args, file_priv);
+ if (err) {
+ args->handle = 0;
+ args->pitch = 0;
+ args->size = 0;
+ }
+ return err;
+}
+
+static int drm_mode_mmap_dumb(struct drm_device *dev, struct drm_mode_map_dumb *args,
+ struct drm_file *file_priv)
+{
+ if (!dev->driver->dumb_create)
+ return -ENOSYS;
+
+ if (dev->driver->dumb_map_offset)
+ return dev->driver->dumb_map_offset(file_priv, dev, args->handle,
+ &args->offset);
+ else
+ return drm_gem_dumb_map_offset(file_priv, dev, args->handle,
+ &args->offset);
}
/**
@@ -120,17 +274,12 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_map_dumb *args = data;
+ int err;
- if (!dev->driver->dumb_create)
- return -ENOSYS;
-
- if (dev->driver->dumb_map_offset)
- return dev->driver->dumb_map_offset(file_priv, dev,
- args->handle,
- &args->offset);
- else
- return drm_gem_dumb_map_offset(file_priv, dev, args->handle,
- &args->offset);
+ err = drm_mode_mmap_dumb(dev, args, file_priv);
+ if (err)
+ args->offset = 0;
+ return err;
}
int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index e2e85345aa9a..26bb7710a462 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -250,6 +250,9 @@ static const struct edid_quirk {
EDID_QUIRK('S', 'V', 'R', 0x1019, BIT(EDID_QUIRK_NON_DESKTOP)),
EDID_QUIRK('A', 'U', 'O', 0x1111, BIT(EDID_QUIRK_NON_DESKTOP)),
+ /* LQ116M1JW10 displays noise when 8 bpc, but display fine as 6 bpc */
+ EDID_QUIRK('S', 'H', 'P', 0x154c, BIT(EDID_QUIRK_FORCE_6BPC)),
+
/*
* @drm_edid_internal_quirk entries end here, following with the
* @drm_edid_quirk entries.
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 11a5b60cb9ce..4a7f72044ab8 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -31,9 +31,6 @@
#include <linux/console.h>
#include <linux/export.h>
-#include <linux/pci.h>
-#include <linux/sysrq.h>
-#include <linux/vga_switcheroo.h>
#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>
@@ -255,6 +252,7 @@ __drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper,
/**
* drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
* @fb_helper: driver-allocated fbdev helper, can be NULL
+ * @force: ignore present DRM master
*
* This helper should be called from fbdev emulation's &drm_client_funcs.restore
* callback. It ensures that the user isn't greeted with a black screen when the
@@ -263,48 +261,12 @@ __drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper,
* Returns:
* 0 on success, or a negative errno code otherwise.
*/
-int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, bool force)
{
- return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false);
+ return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
}
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
-#ifdef CONFIG_MAGIC_SYSRQ
-/* emergency restore, don't bother with error reporting */
-static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
-{
- struct drm_fb_helper *helper;
-
- mutex_lock(&kernel_fb_helper_lock);
- list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
- struct drm_device *dev = helper->dev;
-
- if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
- continue;
-
- mutex_lock(&helper->lock);
- drm_client_modeset_commit_locked(&helper->client);
- mutex_unlock(&helper->lock);
- }
- mutex_unlock(&kernel_fb_helper_lock);
-}
-
-static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
-
-static void drm_fb_helper_sysrq(u8 dummy1)
-{
- schedule_work(&drm_fb_helper_restore_work);
-}
-
-static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
- .handler = drm_fb_helper_sysrq,
- .help_msg = "force-fb(v)",
- .action_msg = "Restore framebuffer console",
-};
-#else
-static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
-#endif
-
static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
{
struct drm_fb_helper *fb_helper = info->par;
@@ -368,6 +330,10 @@ static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper)
unsigned long flags;
int ret;
+ mutex_lock(&helper->lock);
+ drm_client_modeset_wait_for_vblank(&helper->client, 0);
+ mutex_unlock(&helper->lock);
+
if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty))
return;
@@ -491,20 +457,7 @@ int drm_fb_helper_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_fb_helper_init);
-/**
- * drm_fb_helper_alloc_info - allocate fb_info and some of its members
- * @fb_helper: driver-allocated fbdev helper
- *
- * A helper to alloc fb_info and the member cmap. Called by the driver
- * within the struct &drm_driver.fbdev_probe callback function. Drivers do
- * not need to release the allocated fb_info structure themselves, this is
- * automatically done when calling drm_fb_helper_fini().
- *
- * RETURNS:
- * fb_info pointer if things went okay, pointer containing error code
- * otherwise
- */
-struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper)
+static struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper)
{
struct device *dev = fb_helper->dev->dev;
struct fb_info *info;
@@ -531,17 +484,8 @@ err_release:
framebuffer_release(info);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL(drm_fb_helper_alloc_info);
-/**
- * drm_fb_helper_release_info - release fb_info and its members
- * @fb_helper: driver-allocated fbdev helper
- *
- * A helper to release fb_info and the member cmap. Drivers do not
- * need to release the allocated fb_info structure themselves, this is
- * automatically done when calling drm_fb_helper_fini().
- */
-void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper)
+static void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper)
{
struct fb_info *info = fb_helper->info;
@@ -554,7 +498,6 @@ void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
-EXPORT_SYMBOL(drm_fb_helper_release_info);
/**
* drm_fb_helper_unregister_info - unregister fb_info framebuffer device
@@ -566,11 +509,6 @@ EXPORT_SYMBOL(drm_fb_helper_release_info);
*/
void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper)
{
- struct fb_info *info = fb_helper->info;
- struct device *dev = info->device;
-
- if (dev_is_pci(dev))
- vga_switcheroo_client_fb_set(to_pci_dev(dev), NULL);
unregister_framebuffer(fb_helper->info);
}
EXPORT_SYMBOL(drm_fb_helper_unregister_info);
@@ -597,11 +535,8 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
drm_fb_helper_release_info(fb_helper);
mutex_lock(&kernel_fb_helper_lock);
- if (!list_empty(&fb_helper->kernel_fb_list)) {
+ if (!list_empty(&fb_helper->kernel_fb_list))
list_del(&fb_helper->kernel_fb_list);
- if (list_empty(&kernel_fb_helper_list))
- unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
- }
mutex_unlock(&kernel_fb_helper_lock);
if (!fb_helper->client.funcs)
@@ -1068,15 +1003,9 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct drm_fb_helper *fb_helper = info->par;
- struct drm_device *dev = fb_helper->dev;
- struct drm_crtc *crtc;
int ret = 0;
- mutex_lock(&fb_helper->lock);
- if (!drm_master_internal_acquire(dev)) {
- ret = -EBUSY;
- goto unlock;
- }
+ guard(mutex)(&fb_helper->lock);
switch (cmd) {
case FBIO_WAITFORVSYNC:
@@ -1096,28 +1025,12 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
* make. If we're not smart enough here, one should
* just consider switch the userspace to KMS.
*/
- crtc = fb_helper->client.modesets[0].crtc;
-
- /*
- * Only wait for a vblank event if the CRTC is
- * enabled, otherwise just don't do anythintg,
- * not even report an error.
- */
- ret = drm_crtc_vblank_get(crtc);
- if (!ret) {
- drm_crtc_wait_one_vblank(crtc);
- drm_crtc_vblank_put(crtc);
- }
-
- ret = 0;
+ ret = drm_client_modeset_wait_for_vblank(&fb_helper->client, 0);
break;
default:
ret = -ENOTTY;
}
- drm_master_internal_release(dev);
-unlock:
- mutex_unlock(&fb_helper->lock);
return ret;
}
EXPORT_SYMBOL(drm_fb_helper_ioctl);
@@ -1346,9 +1259,9 @@ int drm_fb_helper_set_par(struct fb_info *info)
* the KDSET IOCTL with KD_TEXT, and only after that drops the master
* status when exiting.
*
- * In the past this was caught by drm_fb_helper_lastclose(), but on
- * modern systems where logind always keeps a drm fd open to orchestrate
- * the vt switching, this doesn't work.
+ * In the past this was caught by drm_fb_helper_restore_fbdev_mode_unlocked(),
+ * but on modern systems where logind always keeps a drm fd open to
+ * orchestrate the vt switching, this doesn't work.
*
* To not break the userspace ABI we have this special case here, which
* is only used for the above case. Everything else uses the normal
@@ -1632,7 +1545,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
struct drm_client_dev *client = &fb_helper->client;
struct drm_device *dev = fb_helper->dev;
struct drm_fb_helper_surface_size sizes;
- struct fb_info *info;
int ret;
if (drm_WARN_ON(dev, !dev->driver->fbdev_probe))
@@ -1653,12 +1565,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
strcpy(fb_helper->fb->comm, "[fbcon]");
- info = fb_helper->info;
-
- /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */
- if (dev_is_pci(info->device))
- vga_switcheroo_client_fb_set(to_pci_dev(info->device), info);
-
return 0;
}
@@ -1827,6 +1733,11 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
height = dev->mode_config.max_height;
drm_client_modeset_probe(&fb_helper->client, width, height);
+
+ info = drm_fb_helper_alloc_info(fb_helper);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
ret = drm_fb_helper_single_fb_probe(fb_helper);
if (ret < 0) {
if (ret == -EAGAIN) {
@@ -1835,13 +1746,12 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
}
mutex_unlock(&fb_helper->lock);
- return ret;
+ goto err_drm_fb_helper_release_info;
}
drm_setup_crtcs_fb(fb_helper);
fb_helper->deferred_setup = false;
- info = fb_helper->info;
info->var.pixclock = 0;
/* Need to drop locks to avoid recursive deadlock in
@@ -1857,13 +1767,14 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
info->node, info->fix.id);
mutex_lock(&kernel_fb_helper_lock);
- if (list_empty(&kernel_fb_helper_list))
- register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
-
list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
mutex_unlock(&kernel_fb_helper_lock);
return 0;
+
+err_drm_fb_helper_release_info:
+ drm_fb_helper_release_info(fb_helper);
+ return ret;
}
/**
@@ -1973,16 +1884,3 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
return 0;
}
EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
-
-/**
- * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation
- * @dev: DRM device
- *
- * This function is obsolete. Call drm_fb_helper_restore_fbdev_mode_unlocked()
- * instead.
- */
-void drm_fb_helper_lastclose(struct drm_device *dev)
-{
- drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper);
-}
-EXPORT_SYMBOL(drm_fb_helper_lastclose);
diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c
index 8bd626ef16c7..9412d9fdd74b 100644
--- a/drivers/gpu/drm/drm_fbdev_dma.c
+++ b/drivers/gpu/drm/drm_fbdev_dma.c
@@ -10,6 +10,7 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
/*
* struct fb_ops
@@ -55,10 +56,8 @@ static void drm_fbdev_dma_fb_destroy(struct fb_info *info)
drm_fb_helper_fini(fb_helper);
drm_client_buffer_vunmap(fb_helper->buffer);
- drm_client_framebuffer_delete(fb_helper->buffer);
+ drm_client_buffer_delete(fb_helper->buffer);
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
static const struct fb_ops drm_fbdev_dma_fb_ops = {
@@ -90,10 +89,8 @@ static void drm_fbdev_dma_shadowed_fb_destroy(struct fb_info *info)
vfree(shadow);
drm_client_buffer_vunmap(fb_helper->buffer);
- drm_client_framebuffer_delete(fb_helper->buffer);
+ drm_client_buffer_delete(fb_helper->buffer);
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
static const struct fb_ops drm_fbdev_dma_shadowed_fb_ops = {
@@ -272,9 +269,9 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
{
struct drm_client_dev *client = &fb_helper->client;
struct drm_device *dev = fb_helper->dev;
+ struct fb_info *info = fb_helper->info;
struct drm_client_buffer *buffer;
struct drm_framebuffer *fb;
- struct fb_info *info;
u32 format;
struct iosys_map map;
int ret;
@@ -285,7 +282,7 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp,
sizes->surface_depth);
- buffer = drm_client_framebuffer_create(client, sizes->surface_width,
+ buffer = drm_client_buffer_create_dumb(client, sizes->surface_width,
sizes->surface_height, format);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
@@ -304,12 +301,6 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
fb_helper->buffer = buffer;
fb_helper->fb = fb;
- info = drm_fb_helper_alloc_info(fb_helper);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto err_drm_client_buffer_vunmap;
- }
-
drm_fb_helper_fill_info(info, fb_helper, sizes);
if (fb->funcs->dirty)
@@ -317,18 +308,16 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
else
ret = drm_fbdev_dma_driver_fbdev_probe_tail(fb_helper, sizes);
if (ret)
- goto err_drm_fb_helper_release_info;
+ goto err_drm_client_buffer_vunmap;
return 0;
-err_drm_fb_helper_release_info:
- drm_fb_helper_release_info(fb_helper);
err_drm_client_buffer_vunmap:
fb_helper->fb = NULL;
fb_helper->buffer = NULL;
drm_client_buffer_vunmap(buffer);
err_drm_client_buffer_delete:
- drm_client_framebuffer_delete(buffer);
+ drm_client_buffer_delete(buffer);
return ret;
}
EXPORT_SYMBOL(drm_fbdev_dma_driver_fbdev_probe);
diff --git a/drivers/gpu/drm/drm_fbdev_shmem.c b/drivers/gpu/drm/drm_fbdev_shmem.c
index 1e827bf8b815..458c899b5d4f 100644
--- a/drivers/gpu/drm/drm_fbdev_shmem.c
+++ b/drivers/gpu/drm/drm_fbdev_shmem.c
@@ -9,6 +9,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
/*
* struct fb_ops
@@ -63,10 +64,8 @@ static void drm_fbdev_shmem_fb_destroy(struct fb_info *info)
drm_fb_helper_fini(fb_helper);
drm_client_buffer_vunmap(fb_helper->buffer);
- drm_client_framebuffer_delete(fb_helper->buffer);
+ drm_client_buffer_delete(fb_helper->buffer);
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
static const struct fb_ops drm_fbdev_shmem_fb_ops = {
@@ -136,10 +135,10 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
{
struct drm_client_dev *client = &fb_helper->client;
struct drm_device *dev = fb_helper->dev;
+ struct fb_info *info = fb_helper->info;
struct drm_client_buffer *buffer;
struct drm_gem_shmem_object *shmem;
struct drm_framebuffer *fb;
- struct fb_info *info;
u32 format;
struct iosys_map map;
int ret;
@@ -149,7 +148,7 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
sizes->surface_bpp);
format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp, sizes->surface_depth);
- buffer = drm_client_framebuffer_create(client, sizes->surface_width,
+ buffer = drm_client_buffer_create_dumb(client, sizes->surface_width,
sizes->surface_height, format);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
@@ -169,12 +168,6 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
fb_helper->buffer = buffer;
fb_helper->fb = fb;
- info = drm_fb_helper_alloc_info(fb_helper);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto err_drm_client_buffer_vunmap;
- }
-
drm_fb_helper_fill_info(info, fb_helper, sizes);
info->fbops = &drm_fbdev_shmem_fb_ops;
@@ -195,18 +188,16 @@ int drm_fbdev_shmem_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
info->fbdefio = &fb_helper->fbdefio;
ret = fb_deferred_io_init(info);
if (ret)
- goto err_drm_fb_helper_release_info;
+ goto err_drm_client_buffer_vunmap;
return 0;
-err_drm_fb_helper_release_info:
- drm_fb_helper_release_info(fb_helper);
err_drm_client_buffer_vunmap:
fb_helper->fb = NULL;
fb_helper->buffer = NULL;
drm_client_buffer_vunmap(buffer);
err_drm_client_buffer_delete:
- drm_client_framebuffer_delete(buffer);
+ drm_client_buffer_delete(buffer);
return ret;
}
EXPORT_SYMBOL(drm_fbdev_shmem_driver_fbdev_probe);
diff --git a/drivers/gpu/drm/drm_fbdev_ttm.c b/drivers/gpu/drm/drm_fbdev_ttm.c
index 85feb55bba11..160bc35d8738 100644
--- a/drivers/gpu/drm/drm_fbdev_ttm.c
+++ b/drivers/gpu/drm/drm_fbdev_ttm.c
@@ -50,11 +50,9 @@ static void drm_fbdev_ttm_fb_destroy(struct fb_info *info)
fb_deferred_io_cleanup(info);
drm_fb_helper_fini(fb_helper);
vfree(shadow);
- drm_client_framebuffer_delete(fb_helper->buffer);
+ drm_client_buffer_delete(fb_helper->buffer);
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
static const struct fb_ops drm_fbdev_ttm_fb_ops = {
@@ -176,8 +174,8 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
{
struct drm_client_dev *client = &fb_helper->client;
struct drm_device *dev = fb_helper->dev;
+ struct fb_info *info = fb_helper->info;
struct drm_client_buffer *buffer;
- struct fb_info *info;
size_t screen_size;
void *screen_buffer;
u32 format;
@@ -189,7 +187,7 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp,
sizes->surface_depth);
- buffer = drm_client_framebuffer_create(client, sizes->surface_width,
+ buffer = drm_client_buffer_create_dumb(client, sizes->surface_width,
sizes->surface_height, format);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
@@ -202,13 +200,7 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
screen_buffer = vzalloc(screen_size);
if (!screen_buffer) {
ret = -ENOMEM;
- goto err_drm_client_framebuffer_delete;
- }
-
- info = drm_fb_helper_alloc_info(fb_helper);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto err_vfree;
+ goto err_drm_client_buffer_delete;
}
drm_fb_helper_fill_info(info, fb_helper, sizes);
@@ -227,18 +219,16 @@ int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
info->fbdefio = &fb_helper->fbdefio;
ret = fb_deferred_io_init(info);
if (ret)
- goto err_drm_fb_helper_release_info;
+ goto err_vfree;
return 0;
-err_drm_fb_helper_release_info:
- drm_fb_helper_release_info(fb_helper);
err_vfree:
vfree(screen_buffer);
-err_drm_client_framebuffer_delete:
+err_drm_client_buffer_delete:
fb_helper->fb = NULL;
fb_helper->buffer = NULL;
- drm_client_framebuffer_delete(buffer);
+ drm_client_buffer_delete(buffer);
return ret;
}
EXPORT_SYMBOL(drm_fbdev_ttm_driver_fbdev_probe);
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index eebd1a05ee97..be5e617ceb9f 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -405,7 +405,7 @@ EXPORT_SYMBOL(drm_open);
static void drm_lastclose(struct drm_device *dev)
{
- drm_client_dev_restore(dev);
+ drm_client_dev_restore(dev, false);
if (dev_is_pci(dev->dev))
vga_switcheroo_process_delayed_switch();
diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c
index 006836554cc2..6cddf05c493b 100644
--- a/drivers/gpu/drm/drm_format_helper.c
+++ b/drivers/gpu/drm/drm_format_helper.c
@@ -1165,97 +1165,6 @@ void drm_fb_argb8888_to_argb4444(struct iosys_map *dst, const unsigned int *dst_
}
EXPORT_SYMBOL(drm_fb_argb8888_to_argb4444);
-/**
- * drm_fb_blit - Copy parts of a framebuffer to display memory
- * @dst: Array of display-memory addresses to copy to
- * @dst_pitch: Array of numbers of bytes between the start of two consecutive scanlines
- * within @dst; can be NULL if scanlines are stored next to each other.
- * @dst_format: FOURCC code of the display's color format
- * @src: The framebuffer memory to copy from
- * @fb: The framebuffer to copy from
- * @clip: Clip rectangle area to copy
- * @state: Transform and conversion state
- *
- * This function copies parts of a framebuffer to display memory. If the
- * formats of the display and the framebuffer mismatch, the blit function
- * will attempt to convert between them during the process. The parameters @dst,
- * @dst_pitch and @src refer to arrays. Each array must have at least as many
- * entries as there are planes in @dst_format's format. Each entry stores the
- * value for the format's respective color plane at the same index.
- *
- * This function does not apply clipping on @dst (i.e. the destination is at the
- * top-left corner).
- *
- * Returns:
- * 0 on success, or
- * -EINVAL if the color-format conversion failed, or
- * a negative error code otherwise.
- */
-int drm_fb_blit(struct iosys_map *dst, const unsigned int *dst_pitch, uint32_t dst_format,
- const struct iosys_map *src, const struct drm_framebuffer *fb,
- const struct drm_rect *clip, struct drm_format_conv_state *state)
-{
- uint32_t fb_format = fb->format->format;
-
- if (fb_format == dst_format) {
- drm_fb_memcpy(dst, dst_pitch, src, fb, clip);
- return 0;
- } else if (fb_format == (dst_format | DRM_FORMAT_BIG_ENDIAN)) {
- drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state);
- return 0;
- } else if (fb_format == (dst_format & ~DRM_FORMAT_BIG_ENDIAN)) {
- drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state);
- return 0;
- } else if (fb_format == DRM_FORMAT_XRGB8888) {
- if (dst_format == DRM_FORMAT_RGB565) {
- drm_fb_xrgb8888_to_rgb565(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_XRGB1555) {
- drm_fb_xrgb8888_to_xrgb1555(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_ARGB1555) {
- drm_fb_xrgb8888_to_argb1555(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_RGBA5551) {
- drm_fb_xrgb8888_to_rgba5551(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_RGB888) {
- drm_fb_xrgb8888_to_rgb888(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_BGR888) {
- drm_fb_xrgb8888_to_bgr888(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_ARGB8888) {
- drm_fb_xrgb8888_to_argb8888(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_XBGR8888) {
- drm_fb_xrgb8888_to_xbgr8888(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_ABGR8888) {
- drm_fb_xrgb8888_to_abgr8888(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_XRGB2101010) {
- drm_fb_xrgb8888_to_xrgb2101010(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_ARGB2101010) {
- drm_fb_xrgb8888_to_argb2101010(dst, dst_pitch, src, fb, clip, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_BGRX8888) {
- drm_fb_swab(dst, dst_pitch, src, fb, clip, false, state);
- return 0;
- } else if (dst_format == DRM_FORMAT_RGB332) {
- drm_fb_xrgb8888_to_rgb332(dst, dst_pitch, src, fb, clip, state);
- return 0;
- }
- }
-
- drm_warn_once(fb->dev, "No conversion helper from %p4cc to %p4cc found.\n",
- &fb_format, &dst_format);
-
- return -EINVAL;
-}
-EXPORT_SYMBOL(drm_fb_blit);
-
static void drm_fb_gray8_to_gray2_line(void *dbuf, const void *sbuf, unsigned int pixels)
{
u8 *dbuf8 = dbuf;
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index adbb73f00d68..18e753ade001 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -1048,7 +1048,7 @@ retry:
plane_state->crtc->base.id,
plane_state->crtc->name, fb->base.id);
- crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
ret = drm_atomic_add_affected_connectors(state, plane_state->crtc);
if (ret)
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index f884d155a832..efc79bbf3c73 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -101,10 +101,8 @@ drm_gem_init(struct drm_device *dev)
vma_offset_manager = drmm_kzalloc(dev, sizeof(*vma_offset_manager),
GFP_KERNEL);
- if (!vma_offset_manager) {
- DRM_ERROR("out of memory\n");
+ if (!vma_offset_manager)
return -ENOMEM;
- }
dev->vma_offset_manager = vma_offset_manager;
drm_vma_offset_manager_init(vma_offset_manager,
@@ -785,9 +783,9 @@ static int objects_lookup(struct drm_file *filp, u32 *handle, int count,
int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles,
int count, struct drm_gem_object ***objs_out)
{
- int ret;
- u32 *handles;
struct drm_gem_object **objs;
+ u32 *handles;
+ int ret;
if (!count)
return 0;
@@ -799,20 +797,11 @@ int drm_gem_objects_lookup(struct drm_file *filp, void __user *bo_handles,
*objs_out = objs;
- handles = kvmalloc_array(count, sizeof(u32), GFP_KERNEL);
- if (!handles) {
- ret = -ENOMEM;
- goto out;
- }
-
- if (copy_from_user(handles, bo_handles, count * sizeof(u32))) {
- ret = -EFAULT;
- DRM_DEBUG("Failed to copy in GEM handles\n");
- goto out;
- }
+ handles = vmemdup_array_user(bo_handles, count, sizeof(u32));
+ if (IS_ERR(handles))
+ return PTR_ERR(handles);
ret = objects_lookup(filp, handles, count, objs);
-out:
kvfree(handles);
return ret;
@@ -855,12 +844,13 @@ EXPORT_SYMBOL(drm_gem_object_lookup);
long drm_gem_dma_resv_wait(struct drm_file *filep, u32 handle,
bool wait_all, unsigned long timeout)
{
- long ret;
+ struct drm_device *dev = filep->minor->dev;
struct drm_gem_object *obj;
+ long ret;
obj = drm_gem_object_lookup(filep, handle);
if (!obj) {
- DRM_DEBUG("Failed to look up GEM BO %d\n", handle);
+ drm_dbg_core(dev, "Failed to look up GEM BO %d\n", handle);
return -EINVAL;
}
diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c
index ebf305fb24f0..569d41a65a0b 100644
--- a/drivers/gpu/drm/drm_gem_atomic_helper.c
+++ b/drivers/gpu/drm/drm_gem_atomic_helper.c
@@ -310,8 +310,12 @@ EXPORT_SYMBOL(drm_gem_destroy_shadow_plane_state);
void __drm_gem_reset_shadow_plane(struct drm_plane *plane,
struct drm_shadow_plane_state *shadow_plane_state)
{
- __drm_atomic_helper_plane_reset(plane, &shadow_plane_state->base);
- drm_format_conv_state_init(&shadow_plane_state->fmtcnv_state);
+ if (shadow_plane_state) {
+ __drm_atomic_helper_plane_reset(plane, &shadow_plane_state->base);
+ drm_format_conv_state_init(&shadow_plane_state->fmtcnv_state);
+ } else {
+ __drm_atomic_helper_plane_reset(plane, NULL);
+ }
}
EXPORT_SYMBOL(__drm_gem_reset_shadow_plane);
@@ -334,8 +338,6 @@ void drm_gem_reset_shadow_plane(struct drm_plane *plane)
}
shadow_plane_state = kzalloc(sizeof(*shadow_plane_state), GFP_KERNEL);
- if (!shadow_plane_state)
- return;
__drm_gem_reset_shadow_plane(plane, shadow_plane_state);
}
EXPORT_SYMBOL(drm_gem_reset_shadow_plane);
diff --git a/drivers/gpu/drm/drm_gem_dma_helper.c b/drivers/gpu/drm/drm_gem_dma_helper.c
index 4f0320df858f..12d8307997a0 100644
--- a/drivers/gpu/drm/drm_gem_dma_helper.c
+++ b/drivers/gpu/drm/drm_gem_dma_helper.c
@@ -20,7 +20,9 @@
#include <drm/drm.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_vma_manager.h>
/**
@@ -304,9 +306,11 @@ int drm_gem_dma_dumb_create(struct drm_file *file_priv,
struct drm_mode_create_dumb *args)
{
struct drm_gem_dma_object *dma_obj;
+ int ret;
- args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
- args->size = args->pitch * args->height;
+ ret = drm_mode_size_dumb(drm, args, SZ_8, 0);
+ if (ret)
+ return ret;
dma_obj = drm_gem_dma_create_with_handle(file_priv, drm, args->size,
&args->handle);
@@ -582,7 +586,7 @@ drm_gem_dma_prime_import_sg_table_vmap(struct drm_device *dev,
ret = dma_buf_vmap_unlocked(attach->dmabuf, &map);
if (ret) {
- DRM_ERROR("Failed to vmap PRIME buffer\n");
+ drm_err(dev, "Failed to vmap PRIME buffer\n");
return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
index 4bc89d33df59..9fd4eb02a20f 100644
--- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c
+++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c
@@ -16,6 +16,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include "drm_internal.h"
diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 5d1349c34afd..dc94a27710e5 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -18,6 +18,7 @@
#include <drm/drm.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_prime.h>
#include <drm/drm_print.h>
@@ -48,28 +49,12 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
.vm_ops = &drm_gem_shmem_vm_ops,
};
-static struct drm_gem_shmem_object *
-__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private,
- struct vfsmount *gemfs)
+static int __drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem,
+ size_t size, bool private, struct vfsmount *gemfs)
{
- struct drm_gem_shmem_object *shmem;
- struct drm_gem_object *obj;
+ struct drm_gem_object *obj = &shmem->base;
int ret = 0;
- size = PAGE_ALIGN(size);
-
- if (dev->driver->gem_create_object) {
- obj = dev->driver->gem_create_object(dev, size);
- if (IS_ERR(obj))
- return ERR_CAST(obj);
- shmem = to_drm_gem_shmem_obj(obj);
- } else {
- shmem = kzalloc(sizeof(*shmem), GFP_KERNEL);
- if (!shmem)
- return ERR_PTR(-ENOMEM);
- obj = &shmem->base;
- }
-
if (!obj->funcs)
obj->funcs = &drm_gem_shmem_funcs;
@@ -81,7 +66,7 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private,
}
if (ret) {
drm_gem_private_object_fini(obj);
- goto err_free;
+ return ret;
}
ret = drm_gem_create_mmap_offset(obj);
@@ -102,14 +87,55 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private,
__GFP_RETRY_MAYFAIL | __GFP_NOWARN);
}
- return shmem;
-
+ return 0;
err_release:
drm_gem_object_release(obj);
-err_free:
- kfree(obj);
+ return ret;
+}
- return ERR_PTR(ret);
+/**
+ * drm_gem_shmem_init - Initialize an allocated object.
+ * @dev: DRM device
+ * @obj: The allocated shmem GEM object.
+ *
+ * Returns:
+ * 0 on success, or a negative error code on failure.
+ */
+int drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, size_t size)
+{
+ return __drm_gem_shmem_init(dev, shmem, size, false, NULL);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_init);
+
+static struct drm_gem_shmem_object *
+__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private,
+ struct vfsmount *gemfs)
+{
+ struct drm_gem_shmem_object *shmem;
+ struct drm_gem_object *obj;
+ int ret = 0;
+
+ size = PAGE_ALIGN(size);
+
+ if (dev->driver->gem_create_object) {
+ obj = dev->driver->gem_create_object(dev, size);
+ if (IS_ERR(obj))
+ return ERR_CAST(obj);
+ shmem = to_drm_gem_shmem_obj(obj);
+ } else {
+ shmem = kzalloc(sizeof(*shmem), GFP_KERNEL);
+ if (!shmem)
+ return ERR_PTR(-ENOMEM);
+ obj = &shmem->base;
+ }
+
+ ret = __drm_gem_shmem_init(dev, shmem, size, private, gemfs);
+ if (ret) {
+ kfree(obj);
+ return ERR_PTR(ret);
+ }
+
+ return shmem;
}
/**
* drm_gem_shmem_create - Allocate an object with the given size
@@ -150,13 +176,13 @@ struct drm_gem_shmem_object *drm_gem_shmem_create_with_mnt(struct drm_device *de
EXPORT_SYMBOL_GPL(drm_gem_shmem_create_with_mnt);
/**
- * drm_gem_shmem_free - Free resources associated with a shmem GEM object
- * @shmem: shmem GEM object to free
+ * drm_gem_shmem_release - Release resources associated with a shmem GEM object.
+ * @shmem: shmem GEM object
*
- * This function cleans up the GEM object state and frees the memory used to
- * store the object itself.
+ * This function cleans up the GEM object state, but does not free the memory used to store the
+ * object itself. This function is meant to be a dedicated helper for the Rust GEM bindings.
*/
-void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
+void drm_gem_shmem_release(struct drm_gem_shmem_object *shmem)
{
struct drm_gem_object *obj = &shmem->base;
@@ -183,6 +209,19 @@ void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
}
drm_gem_object_release(obj);
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_release);
+
+/**
+ * drm_gem_shmem_free - Free resources associated with a shmem GEM object
+ * @shmem: shmem GEM object to free
+ *
+ * This function cleans up the GEM object state and frees the memory used to
+ * store the object itself.
+ */
+void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem)
+{
+ drm_gem_shmem_release(shmem);
kfree(shmem);
}
EXPORT_SYMBOL_GPL(drm_gem_shmem_free);
@@ -518,18 +557,11 @@ EXPORT_SYMBOL_GPL(drm_gem_shmem_purge_locked);
int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+ int ret;
- if (!args->pitch || !args->size) {
- args->pitch = min_pitch;
- args->size = PAGE_ALIGN(args->pitch * args->height);
- } else {
- /* ensure sane minimum values */
- if (args->pitch < min_pitch)
- args->pitch = min_pitch;
- if (args->size < args->pitch * args->height)
- args->size = PAGE_ALIGN(args->pitch * args->height);
- }
+ ret = drm_mode_size_dumb(dev, args, SZ_8, 0);
+ if (ret)
+ return ret;
return drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle);
}
diff --git a/drivers/gpu/drm/drm_gem_ttm_helper.c b/drivers/gpu/drm/drm_gem_ttm_helper.c
index 257cca4cb97a..08ff0fadd0b2 100644
--- a/drivers/gpu/drm/drm_gem_ttm_helper.c
+++ b/drivers/gpu/drm/drm_gem_ttm_helper.c
@@ -4,6 +4,7 @@
#include <linux/module.h>
#include <drm/drm_gem_ttm_helper.h>
+#include <drm/drm_print.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_tt.h>
diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c
index b04cde4a60e7..5e5b70518dbe 100644
--- a/drivers/gpu/drm/drm_gem_vram_helper.c
+++ b/drivers/gpu/drm/drm_gem_vram_helper.c
@@ -17,6 +17,7 @@
#include <drm/drm_mode.h>
#include <drm/drm_plane.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/ttm/ttm_range_manager.h>
#include <drm/ttm/ttm_tt.h>
@@ -107,7 +108,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs;
static void drm_gem_vram_cleanup(struct drm_gem_vram_object *gbo)
{
- /* We got here via ttm_bo_put(), which means that the
+ /* We got here via ttm_bo_fini(), which means that the
* TTM buffer object in 'bo' has already been cleaned
* up; only release the GEM object.
*/
@@ -234,11 +235,11 @@ EXPORT_SYMBOL(drm_gem_vram_create);
* drm_gem_vram_put() - Releases a reference to a VRAM-backed GEM object
* @gbo: the GEM VRAM object
*
- * See ttm_bo_put() for more information.
+ * See ttm_bo_fini() for more information.
*/
void drm_gem_vram_put(struct drm_gem_vram_object *gbo)
{
- ttm_bo_put(&gbo->bo);
+ ttm_bo_fini(&gbo->bo);
}
EXPORT_SYMBOL(drm_gem_vram_put);
@@ -859,7 +860,7 @@ static int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev,
ret = ttm_device_init(&vmm->bdev, &bo_driver, dev->dev,
dev->anon_inode->i_mapping,
dev->vma_offset_manager,
- false, true);
+ TTM_ALLOCATION_POOL_USE_DMA32);
if (ret)
return ret;
@@ -967,7 +968,7 @@ drm_vram_helper_mode_valid_internal(struct drm_device *dev,
max_fbpages = (vmm->vram_size / 2) >> PAGE_SHIFT;
- fbsize = mode->hdisplay * mode->vdisplay * max_bpp;
+ fbsize = (u32)mode->hdisplay * mode->vdisplay * max_bpp;
fbpages = DIV_ROUND_UP(fbsize, PAGE_SIZE);
if (fbpages > max_fbpages)
diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c
index cb906765897e..73e550c8ff8c 100644
--- a/drivers/gpu/drm/drm_gpusvm.c
+++ b/drivers/gpu/drm/drm_gpusvm.c
@@ -1363,7 +1363,8 @@ map_pages:
order = drm_gpusvm_hmm_pfn_to_order(pfns[i], i, npages);
if (is_device_private_page(page) ||
is_device_coherent_page(page)) {
- if (zdd != page->zone_device_data && i > 0) {
+ if (!ctx->allow_mixed &&
+ zdd != page->zone_device_data && i > 0) {
err = -EOPNOTSUPP;
goto err_unmap;
}
@@ -1399,7 +1400,8 @@ map_pages:
} else {
dma_addr_t addr;
- if (is_zone_device_page(page) || pagemap) {
+ if (is_zone_device_page(page) ||
+ (pagemap && !ctx->allow_mixed)) {
err = -EOPNOTSUPP;
goto err_unmap;
}
diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c
index af63f4d00315..8a06d296561d 100644
--- a/drivers/gpu/drm/drm_gpuvm.c
+++ b/drivers/gpu/drm/drm_gpuvm.c
@@ -26,6 +26,7 @@
*/
#include <drm/drm_gpuvm.h>
+#include <drm/drm_print.h>
#include <linux/export.h>
#include <linux/interval_tree_generic.h>
@@ -877,6 +878,31 @@ __drm_gpuvm_bo_list_add(struct drm_gpuvm *gpuvm, spinlock_t *lock,
}
/**
+ * drm_gpuvm_bo_is_zombie() - check whether this vm_bo is scheduled for cleanup
+ * @vm_bo: the &drm_gpuvm_bo
+ *
+ * When a vm_bo is scheduled for cleanup using the bo_defer list, it is not
+ * immediately removed from the evict and extobj lists. Therefore, anyone
+ * iterating these lists should skip entries that are being destroyed.
+ *
+ * Checking the refcount without incrementing it is okay as long as the lock
+ * protecting the evict/extobj list is held for as long as you are using the
+ * vm_bo, because even if the refcount hits zero while you are using it, freeing
+ * the vm_bo requires taking the list's lock.
+ *
+ * Zombie entries can be observed on the evict and extobj lists regardless of
+ * whether DRM_GPUVM_RESV_PROTECTED is used, but they remain on the lists for a
+ * longer time when the resv lock is used because we can't take the resv lock
+ * during run_job() in immediate mode, meaning that they need to remain on the
+ * lists until drm_gpuvm_bo_deferred_cleanup() is called.
+ */
+static bool
+drm_gpuvm_bo_is_zombie(struct drm_gpuvm_bo *vm_bo)
+{
+ return !kref_read(&vm_bo->kref);
+}
+
+/**
* drm_gpuvm_bo_list_add() - insert a vm_bo into the given list
* @__vm_bo: the &drm_gpuvm_bo
* @__list_name: the name of the list to insert into
@@ -1081,6 +1107,8 @@ drm_gpuvm_init(struct drm_gpuvm *gpuvm, const char *name,
INIT_LIST_HEAD(&gpuvm->evict.list);
spin_lock_init(&gpuvm->evict.lock);
+ init_llist_head(&gpuvm->bo_defer);
+
kref_init(&gpuvm->kref);
gpuvm->name = name ? name : "unknown";
@@ -1122,6 +1150,8 @@ drm_gpuvm_fini(struct drm_gpuvm *gpuvm)
"Extobj list should be empty.\n");
drm_WARN(gpuvm->drm, !list_empty(&gpuvm->evict.list),
"Evict list should be empty.\n");
+ drm_WARN(gpuvm->drm, !llist_empty(&gpuvm->bo_defer),
+ "VM BO cleanup list should be empty.\n");
drm_gem_object_put(gpuvm->r_obj);
}
@@ -1217,6 +1247,9 @@ drm_gpuvm_prepare_objects_locked(struct drm_gpuvm *gpuvm,
drm_gpuvm_resv_assert_held(gpuvm);
list_for_each_entry(vm_bo, &gpuvm->extobj.list, list.entry.extobj) {
+ if (drm_gpuvm_bo_is_zombie(vm_bo))
+ continue;
+
ret = exec_prepare_obj(exec, vm_bo->obj, num_fences);
if (ret)
break;
@@ -1460,6 +1493,9 @@ drm_gpuvm_validate_locked(struct drm_gpuvm *gpuvm, struct drm_exec *exec)
list_for_each_entry_safe(vm_bo, next, &gpuvm->evict.list,
list.entry.evict) {
+ if (drm_gpuvm_bo_is_zombie(vm_bo))
+ continue;
+
ret = ops->vm_bo_validate(vm_bo, exec);
if (ret)
break;
@@ -1560,6 +1596,7 @@ drm_gpuvm_bo_create(struct drm_gpuvm *gpuvm,
INIT_LIST_HEAD(&vm_bo->list.entry.extobj);
INIT_LIST_HEAD(&vm_bo->list.entry.evict);
+ init_llist_node(&vm_bo->list.entry.bo_defer);
return vm_bo;
}
@@ -1621,6 +1658,126 @@ drm_gpuvm_bo_put(struct drm_gpuvm_bo *vm_bo)
}
EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put);
+/*
+ * drm_gpuvm_bo_into_zombie() - called when the vm_bo becomes a zombie due to
+ * deferred cleanup
+ *
+ * If deferred cleanup is used, then this must be called right after the vm_bo
+ * refcount drops to zero. Must be called with GEM mutex held. After releasing
+ * the GEM mutex, drm_gpuvm_bo_defer_zombie_cleanup() must be called.
+ */
+static void
+drm_gpuvm_bo_into_zombie(struct kref *kref)
+{
+ struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,
+ kref);
+
+ if (!drm_gpuvm_resv_protected(vm_bo->vm)) {
+ drm_gpuvm_bo_list_del(vm_bo, extobj, true);
+ drm_gpuvm_bo_list_del(vm_bo, evict, true);
+ }
+
+ list_del(&vm_bo->list.entry.gem);
+}
+
+/*
+ * drm_gpuvm_bo_defer_zombie_cleanup() - adds a new zombie vm_bo to the
+ * bo_defer list
+ *
+ * Called after drm_gpuvm_bo_into_zombie(). GEM mutex must not be held.
+ *
+ * It's important that the GEM stays alive for the duration in which we hold
+ * the mutex, but the instant we add the vm_bo to bo_defer, another thread
+ * might call drm_gpuvm_bo_deferred_cleanup() and put the GEM. Therefore, to
+ * avoid kfreeing a mutex we are holding, the GEM mutex must be released
+ * *before* calling this function.
+ */
+static void
+drm_gpuvm_bo_defer_zombie_cleanup(struct drm_gpuvm_bo *vm_bo)
+{
+ llist_add(&vm_bo->list.entry.bo_defer, &vm_bo->vm->bo_defer);
+}
+
+static void
+drm_gpuvm_bo_defer_free(struct kref *kref)
+{
+ struct drm_gpuvm_bo *vm_bo = container_of(kref, struct drm_gpuvm_bo,
+ kref);
+
+ drm_gpuvm_bo_into_zombie(kref);
+ mutex_unlock(&vm_bo->obj->gpuva.lock);
+ drm_gpuvm_bo_defer_zombie_cleanup(vm_bo);
+}
+
+/**
+ * drm_gpuvm_bo_put_deferred() - drop a struct drm_gpuvm_bo reference with
+ * deferred cleanup
+ * @vm_bo: the &drm_gpuvm_bo to release the reference of
+ *
+ * This releases a reference to @vm_bo.
+ *
+ * This might take and release the GEMs GPUVA lock. You should call
+ * drm_gpuvm_bo_deferred_cleanup() later to complete the cleanup process.
+ *
+ * Returns: true if vm_bo is being destroyed, false otherwise.
+ */
+bool
+drm_gpuvm_bo_put_deferred(struct drm_gpuvm_bo *vm_bo)
+{
+ if (!vm_bo)
+ return false;
+
+ drm_WARN_ON(vm_bo->vm->drm, !drm_gpuvm_immediate_mode(vm_bo->vm));
+
+ return !!kref_put_mutex(&vm_bo->kref,
+ drm_gpuvm_bo_defer_free,
+ &vm_bo->obj->gpuva.lock);
+}
+EXPORT_SYMBOL_GPL(drm_gpuvm_bo_put_deferred);
+
+/**
+ * drm_gpuvm_bo_deferred_cleanup() - clean up BOs in the deferred list
+ * deferred cleanup
+ * @gpuvm: the VM to clean up
+ *
+ * Cleans up &drm_gpuvm_bo instances in the deferred cleanup list.
+ */
+void
+drm_gpuvm_bo_deferred_cleanup(struct drm_gpuvm *gpuvm)
+{
+ const struct drm_gpuvm_ops *ops = gpuvm->ops;
+ struct drm_gpuvm_bo *vm_bo;
+ struct drm_gem_object *obj;
+ struct llist_node *bo_defer;
+
+ bo_defer = llist_del_all(&gpuvm->bo_defer);
+ if (!bo_defer)
+ return;
+
+ if (drm_gpuvm_resv_protected(gpuvm)) {
+ dma_resv_lock(drm_gpuvm_resv(gpuvm), NULL);
+ llist_for_each_entry(vm_bo, bo_defer, list.entry.bo_defer) {
+ drm_gpuvm_bo_list_del(vm_bo, extobj, false);
+ drm_gpuvm_bo_list_del(vm_bo, evict, false);
+ }
+ dma_resv_unlock(drm_gpuvm_resv(gpuvm));
+ }
+
+ while (bo_defer) {
+ vm_bo = llist_entry(bo_defer, struct drm_gpuvm_bo, list.entry.bo_defer);
+ bo_defer = bo_defer->next;
+ obj = vm_bo->obj;
+ if (ops && ops->vm_bo_free)
+ ops->vm_bo_free(vm_bo);
+ else
+ kfree(vm_bo);
+
+ drm_gpuvm_put(gpuvm);
+ drm_gem_object_put(obj);
+ }
+}
+EXPORT_SYMBOL_GPL(drm_gpuvm_bo_deferred_cleanup);
+
static struct drm_gpuvm_bo *
__drm_gpuvm_bo_find(struct drm_gpuvm *gpuvm,
struct drm_gem_object *obj)
@@ -1949,6 +2106,40 @@ drm_gpuva_unlink(struct drm_gpuva *va)
EXPORT_SYMBOL_GPL(drm_gpuva_unlink);
/**
+ * drm_gpuva_unlink_defer() - unlink a &drm_gpuva with deferred vm_bo cleanup
+ * @va: the &drm_gpuva to unlink
+ *
+ * Similar to drm_gpuva_unlink(), but uses drm_gpuvm_bo_put_deferred() and takes
+ * the lock for the caller.
+ */
+void
+drm_gpuva_unlink_defer(struct drm_gpuva *va)
+{
+ struct drm_gem_object *obj = va->gem.obj;
+ struct drm_gpuvm_bo *vm_bo = va->vm_bo;
+ bool should_defer_bo;
+
+ if (unlikely(!obj))
+ return;
+
+ drm_WARN_ON(vm_bo->vm->drm, !drm_gpuvm_immediate_mode(vm_bo->vm));
+
+ mutex_lock(&obj->gpuva.lock);
+ list_del_init(&va->gem.entry);
+
+ /*
+ * This is drm_gpuvm_bo_put_deferred() except we already hold the mutex.
+ */
+ should_defer_bo = kref_put(&vm_bo->kref, drm_gpuvm_bo_into_zombie);
+ mutex_unlock(&obj->gpuva.lock);
+ if (should_defer_bo)
+ drm_gpuvm_bo_defer_zombie_cleanup(vm_bo);
+
+ va->vm_bo = NULL;
+}
+EXPORT_SYMBOL_GPL(drm_gpuva_unlink_defer);
+
+/**
* drm_gpuva_find_first() - find the first &drm_gpuva in the given range
* @gpuvm: the &drm_gpuvm to search in
* @addr: the &drm_gpuvas address
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 5a3bed48ab1f..f893b1e3a596 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -56,6 +56,17 @@ static inline void drm_client_debugfs_init(struct drm_device *dev)
{ }
#endif
+/* drm_client_sysrq.c */
+#if defined(CONFIG_DRM_CLIENT) && defined(CONFIG_MAGIC_SYSRQ)
+void drm_client_sysrq_register(struct drm_device *dev);
+void drm_client_sysrq_unregister(struct drm_device *dev);
+#else
+static inline void drm_client_sysrq_register(struct drm_device *dev)
+{ }
+static inline void drm_client_sysrq_unregister(struct drm_device *dev)
+{ }
+#endif
+
/* drm_file.c */
extern struct mutex drm_global_mutex;
bool drm_dev_needs_global_mutex(struct drm_device *dev);
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index d8a24875a7ba..ff193155129e 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -373,6 +373,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
return -EINVAL;
file_priv->supports_virtualized_cursor_plane = req->value;
break;
+ case DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE:
+ if (!file_priv->atomic)
+ return -EINVAL;
+ if (req->value > 1)
+ return -EINVAL;
+ file_priv->plane_color_pipeline = req->value;
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c
index e33c78fc8fbd..00482227a9cd 100644
--- a/drivers/gpu/drm/drm_mipi_dbi.c
+++ b/drivers/gpu/drm/drm_mipi_dbi.c
@@ -26,6 +26,7 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modes.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_rect.h>
#include <video/mipi_display.h>
@@ -691,7 +692,7 @@ int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev,
const struct drm_simple_display_pipe_funcs *funcs,
const struct drm_display_mode *mode, unsigned int rotation)
{
- size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
+ size_t bufsize = (u32)mode->vdisplay * mode->hdisplay * sizeof(u16);
dbidev->drm.mode_config.preferred_depth = 16;
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index ca254611b382..6692abe564d3 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -49,6 +49,7 @@
#include <linux/stacktrace.h>
#include <drm/drm_mm.h>
+#include <drm/drm_print.h>
/**
* DOC: Overview
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 25f376869b3a..d12db9b0bab8 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -30,6 +30,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_print.h>
+#include <drm/drm_colorop.h>
#include <linux/dma-resv.h>
#include "drm_crtc_internal.h"
@@ -192,11 +193,15 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
void drm_mode_config_reset(struct drm_device *dev)
{
struct drm_crtc *crtc;
+ struct drm_colorop *colorop;
struct drm_plane *plane;
struct drm_encoder *encoder;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
+ drm_for_each_colorop(colorop, dev)
+ drm_colorop_reset(colorop);
+
drm_for_each_plane(plane, dev)
if (plane->funcs->reset)
plane->funcs->reset(plane);
@@ -437,6 +442,7 @@ int drmm_mode_config_init(struct drm_device *dev)
INIT_LIST_HEAD(&dev->mode_config.property_list);
INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
INIT_LIST_HEAD(&dev->mode_config.plane_list);
+ INIT_LIST_HEAD(&dev->mode_config.colorop_list);
INIT_LIST_HEAD(&dev->mode_config.privobj_list);
idr_init_base(&dev->mode_config.object_idr, 1);
idr_init_base(&dev->mode_config.tile_idr, 1);
@@ -458,6 +464,7 @@ int drmm_mode_config_init(struct drm_device *dev)
dev->mode_config.num_crtc = 0;
dev->mode_config.num_encoder = 0;
dev->mode_config.num_total_plane = 0;
+ dev->mode_config.num_colorop = 0;
if (IS_ENABLED(CONFIG_LOCKDEP)) {
struct drm_modeset_acquire_ctx modeset_ctx;
diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c
index e943205a2394..b45d501b10c8 100644
--- a/drivers/gpu/drm/drm_mode_object.c
+++ b/drivers/gpu/drm/drm_mode_object.c
@@ -28,6 +28,7 @@
#include <drm/drm_device.h>
#include <drm/drm_file.h>
#include <drm/drm_mode_object.h>
+#include <drm/drm_plane.h>
#include <drm/drm_print.h>
#include "drm_crtc_internal.h"
@@ -386,6 +387,7 @@ EXPORT_SYMBOL(drm_object_property_get_default_value);
/* helper for getconnector and getproperties ioctls */
int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
+ bool plane_color_pipeline,
uint32_t __user *prop_ptr,
uint64_t __user *prop_values,
uint32_t *arg_count_props)
@@ -399,6 +401,21 @@ int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
continue;
+ if (plane_color_pipeline && obj->type == DRM_MODE_OBJECT_PLANE) {
+ struct drm_plane *plane = obj_to_plane(obj);
+
+ if (prop == plane->color_encoding_property ||
+ prop == plane->color_range_property)
+ continue;
+ }
+
+ if (!plane_color_pipeline && obj->type == DRM_MODE_OBJECT_PLANE) {
+ struct drm_plane *plane = obj_to_plane(obj);
+
+ if (prop == plane->color_pipeline_property)
+ continue;
+ }
+
if (*arg_count_props > count) {
ret = __drm_object_property_get_value(obj, prop, &val);
if (ret)
@@ -457,6 +474,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
}
ret = drm_mode_object_get_properties(obj, file_priv->atomic,
+ file_priv->plane_color_pipeline,
(uint32_t __user *)(unsigned long)(arg->props_ptr),
(uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
&arg->count_props);
diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c
index 988735560570..a57f6a10ada4 100644
--- a/drivers/gpu/drm/drm_modeset_helper.c
+++ b/drivers/gpu/drm/drm_modeset_helper.c
@@ -203,10 +203,10 @@ int drm_mode_config_helper_suspend(struct drm_device *dev)
if (dev->mode_config.poll_enabled)
drm_kms_helper_poll_disable(dev);
- drm_client_dev_suspend(dev, false);
+ drm_client_dev_suspend(dev);
state = drm_atomic_helper_suspend(dev);
if (IS_ERR(state)) {
- drm_client_dev_resume(dev, false);
+ drm_client_dev_resume(dev);
/*
* Don't enable polling if it was never initialized
@@ -252,7 +252,7 @@ int drm_mode_config_helper_resume(struct drm_device *dev)
DRM_ERROR("Failed to resume (%d)\n", ret);
dev->mode_config.suspend_state = NULL;
- drm_client_dev_resume(dev, false);
+ drm_client_dev_resume(dev);
/*
* Don't enable polling if it is not initialized
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 1d6312fa1429..d4b6ea42db0f 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -174,6 +174,33 @@ static void drm_panic_write_pixel24(void *vaddr, unsigned int offset, u32 color)
*p = color & 0xff;
}
+/*
+ * Special case if the pixel crosses page boundaries
+ */
+static void drm_panic_write_pixel24_xpage(void *vaddr, struct page *next_page,
+ unsigned int offset, u32 color)
+{
+ u8 *vaddr2;
+ u8 *p = vaddr + offset;
+
+ vaddr2 = kmap_local_page_try_from_panic(next_page);
+
+ *p++ = color & 0xff;
+ color >>= 8;
+
+ if (offset == PAGE_SIZE - 1)
+ p = vaddr2;
+
+ *p++ = color & 0xff;
+ color >>= 8;
+
+ if (offset == PAGE_SIZE - 2)
+ p = vaddr2;
+
+ *p = color & 0xff;
+ kunmap_local(vaddr2);
+}
+
static void drm_panic_write_pixel32(void *vaddr, unsigned int offset, u32 color)
{
u32 *p = vaddr + offset;
@@ -231,7 +258,14 @@ static void drm_panic_blit_page(struct page **pages, unsigned int dpitch,
page = new_page;
vaddr = kmap_local_page_try_from_panic(pages[page]);
}
- if (vaddr)
+ if (!vaddr)
+ continue;
+
+ // Special case for 24bit, as a pixel might cross page boundaries
+ if (cpp == 3 && offset + 3 > PAGE_SIZE)
+ drm_panic_write_pixel24_xpage(vaddr, pages[page + 1],
+ offset, fg32);
+ else
drm_panic_write_pixel(vaddr, offset, fg32, cpp);
}
}
@@ -321,7 +355,15 @@ static void drm_panic_fill_page(struct page **pages, unsigned int dpitch,
page = new_page;
vaddr = kmap_local_page_try_from_panic(pages[page]);
}
- drm_panic_write_pixel(vaddr, offset, color, cpp);
+ if (!vaddr)
+ continue;
+
+ // Special case for 24bit, as a pixel might cross page boundaries
+ if (cpp == 3 && offset + 3 > PAGE_SIZE)
+ drm_panic_write_pixel24_xpage(vaddr, pages[page + 1],
+ offset, color);
+ else
+ drm_panic_write_pixel(vaddr, offset, color, cpp);
}
}
if (vaddr)
@@ -429,6 +471,9 @@ static void drm_panic_logo_rect(struct drm_rect *rect, const struct font_desc *f
static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *rect,
const struct font_desc *font, u32 fg_color)
{
+ if (rect->x2 > sb->width || rect->y2 > sb->height)
+ return;
+
if (logo_mono)
drm_panic_blit(sb, rect, logo_mono->data,
DIV_ROUND_UP(drm_rect_width(rect), 8), 1, fg_color);
@@ -477,7 +522,7 @@ static int draw_line_with_wrap(struct drm_scanout_buffer *sb, const struct font_
struct drm_panic_line *line, int yoffset, u32 fg_color)
{
int chars_per_row = sb->width / font->width;
- struct drm_rect r_txt = DRM_RECT_INIT(0, yoffset, sb->width, sb->height);
+ struct drm_rect r_txt = DRM_RECT_INIT(0, yoffset, sb->width, font->height);
struct drm_panic_line line_wrap;
if (line->len > chars_per_row) {
@@ -520,7 +565,7 @@ static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
struct drm_panic_line line;
int yoffset;
- if (!font)
+ if (!font || font->width > sb->width)
return;
yoffset = sb->height - font->height - (sb->height % font->height) / 2;
@@ -733,7 +778,10 @@ static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
pr_debug("QR width %d and scale %d\n", qr_width, scale);
r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale);
- v_margin = (sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg)) / 5;
+ v_margin = sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg);
+ if (v_margin < 0)
+ return -ENOSPC;
+ v_margin /= 5;
drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin);
r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1 + QR_MARGIN * scale,
@@ -746,7 +794,7 @@ static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
/* Fill with the background color, and draw text on top */
drm_panic_fill(sb, &r_screen, bg_color);
- if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr))
+ if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr_canvas))
drm_panic_logo_draw(sb, &r_logo, font, fg_color);
draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c
index 38f82391bfda..d2c211f66c9e 100644
--- a/drivers/gpu/drm/drm_plane.c
+++ b/drivers/gpu/drm/drm_plane.c
@@ -210,7 +210,7 @@ static struct drm_property_blob *create_in_format_blob(struct drm_device *dev,
formats_size = sizeof(__u32) * plane->format_count;
if (WARN_ON(!formats_size)) {
/* 0 formats are never expected */
- return 0;
+ return ERR_PTR(-EINVAL);
}
modifiers_size =
@@ -226,7 +226,7 @@ static struct drm_property_blob *create_in_format_blob(struct drm_device *dev,
blob = drm_property_create_blob(dev, blob_size, NULL);
if (IS_ERR(blob))
- return NULL;
+ return blob;
blob_data = blob->data;
blob_data->version = FORMAT_BLOB_CURRENT;
@@ -1820,3 +1820,62 @@ int drm_plane_add_size_hints_property(struct drm_plane *plane,
return 0;
}
EXPORT_SYMBOL(drm_plane_add_size_hints_property);
+
+/**
+ * drm_plane_create_color_pipeline_property - create a new color pipeline
+ * property
+ *
+ * @plane: drm plane
+ * @pipelines: list of pipelines
+ * @num_pipelines: number of pipelines
+ *
+ * Create the COLOR_PIPELINE plane property to specific color pipelines on
+ * the plane.
+ *
+ * RETURNS:
+ * Zero for success or -errno
+ */
+int drm_plane_create_color_pipeline_property(struct drm_plane *plane,
+ const struct drm_prop_enum_list *pipelines,
+ int num_pipelines)
+{
+ struct drm_prop_enum_list *all_pipelines;
+ struct drm_property *prop;
+ int len = 0;
+ int i;
+
+ all_pipelines = kcalloc(num_pipelines + 1,
+ sizeof(*all_pipelines),
+ GFP_KERNEL);
+
+ if (!all_pipelines) {
+ drm_err(plane->dev, "failed to allocate color pipeline\n");
+ return -ENOMEM;
+ }
+
+ /* Create default Bypass color pipeline */
+ all_pipelines[len].type = 0;
+ all_pipelines[len].name = "Bypass";
+ len++;
+
+ /* Add all other color pipelines */
+ for (i = 0; i < num_pipelines; i++, len++) {
+ all_pipelines[len].type = pipelines[i].type;
+ all_pipelines[len].name = pipelines[i].name;
+ }
+
+ prop = drm_property_create_enum(plane->dev, DRM_MODE_PROP_ATOMIC,
+ "COLOR_PIPELINE",
+ all_pipelines, len);
+ if (IS_ERR(prop)) {
+ kfree(all_pipelines);
+ return PTR_ERR(prop);
+ }
+
+ drm_object_attach_property(&plane->base, prop, 0);
+ plane->color_pipeline_property = prop;
+
+ kfree(all_pipelines);
+ return 0;
+}
+EXPORT_SYMBOL(drm_plane_create_color_pipeline_property);
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 43a10b4af43a..21809a82187b 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -37,6 +37,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include "drm_internal.h"
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 46f59883183d..5c14140cd0c2 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -136,8 +136,17 @@
* vblanks after a timer has expired, which can be configured through the
* ``vblankoffdelay`` module parameter.
*
- * Drivers for hardware without support for vertical-blanking interrupts
- * must not call drm_vblank_init(). For such drivers, atomic helpers will
+ * Drivers for hardware without support for vertical-blanking interrupts can
+ * use DRM vblank timers to send vblank events at the rate of the current
+ * display mode's refresh. While not synchronized to the hardware's
+ * vertical-blanking regions, the timer helps DRM clients and compositors to
+ * adapt their update cycle to the display output. Drivers should set up
+ * vblanking as usual, but call drm_crtc_vblank_start_timer() and
+ * drm_crtc_vblank_cancel_timer() as part of their atomic mode setting.
+ * See also DRM vblank helpers for more information.
+ *
+ * Drivers without support for vertical-blanking interrupts nor timers must
+ * not call drm_vblank_init(). For these drivers, atomic helpers will
* automatically generate fake vblank events as part of the display update.
* This functionality also can be controlled by the driver by enabling and
* disabling struct drm_crtc_state.no_vblank.
@@ -508,6 +517,9 @@ static void drm_vblank_init_release(struct drm_device *dev, void *ptr)
drm_WARN_ON(dev, READ_ONCE(vblank->enabled) &&
drm_core_check_feature(dev, DRIVER_MODESET));
+ if (vblank->vblank_timer.crtc)
+ hrtimer_cancel(&vblank->vblank_timer.timer);
+
drm_vblank_destroy_worker(vblank);
timer_delete_sync(&vblank->disable_timer);
}
@@ -794,10 +806,8 @@ drm_crtc_vblank_helper_get_vblank_timestamp_internal(
ts_vblank_time = ktime_to_timespec64(*vblank_time);
drm_dbg_vbl(dev,
- "crtc %u : v p(%d,%d)@ %lld.%06ld -> %lld.%06ld [e %d us, %d rep]\n",
- pipe, hpos, vpos,
- (u64)ts_etime.tv_sec, ts_etime.tv_nsec / 1000,
- (u64)ts_vblank_time.tv_sec, ts_vblank_time.tv_nsec / 1000,
+ "crtc %u : v p(%d,%d)@ %ptSp -> %ptSp [e %d us, %d rep]\n",
+ pipe, hpos, vpos, &ts_etime, &ts_vblank_time,
duration_ns / 1000, i);
return true;
@@ -1303,7 +1313,7 @@ void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe)
ret = wait_event_timeout(vblank->queue,
last != drm_vblank_count(dev, pipe),
- msecs_to_jiffies(100));
+ msecs_to_jiffies(1000));
drm_WARN(dev, ret == 0, "vblank wait timed out on crtc %i\n", pipe);
@@ -2162,3 +2172,159 @@ err_free:
return ret;
}
+/*
+ * VBLANK timer
+ */
+
+static enum hrtimer_restart drm_vblank_timer_function(struct hrtimer *timer)
+{
+ struct drm_vblank_crtc_timer *vtimer =
+ container_of(timer, struct drm_vblank_crtc_timer, timer);
+ struct drm_crtc *crtc = vtimer->crtc;
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_device *dev = crtc->dev;
+ unsigned long flags;
+ ktime_t interval;
+ u64 ret_overrun;
+ bool succ;
+
+ spin_lock_irqsave(&vtimer->interval_lock, flags);
+ interval = vtimer->interval;
+ spin_unlock_irqrestore(&vtimer->interval_lock, flags);
+
+ if (!interval)
+ return HRTIMER_NORESTART;
+
+ ret_overrun = hrtimer_forward_now(&vtimer->timer, interval);
+ if (ret_overrun != 1)
+ drm_dbg_vbl(dev, "vblank timer overrun\n");
+
+ if (crtc_funcs->handle_vblank_timeout)
+ succ = crtc_funcs->handle_vblank_timeout(crtc);
+ else
+ succ = drm_crtc_handle_vblank(crtc);
+ if (!succ)
+ return HRTIMER_NORESTART;
+
+ return HRTIMER_RESTART;
+}
+
+/**
+ * drm_crtc_vblank_start_timer - Starts the vblank timer on the given CRTC
+ * @crtc: the CRTC
+ *
+ * Drivers should call this function from their CRTC's enable_vblank
+ * function to start a vblank timer. The timer will fire after the duration
+ * of a full frame. drm_crtc_vblank_cancel_timer() disables a running timer.
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise.
+ */
+int drm_crtc_vblank_start_timer(struct drm_crtc *crtc)
+{
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+ struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer;
+ unsigned long flags;
+
+ if (!vtimer->crtc) {
+ /*
+ * Set up the data structures on the first invocation.
+ */
+ vtimer->crtc = crtc;
+ spin_lock_init(&vtimer->interval_lock);
+ hrtimer_setup(&vtimer->timer, drm_vblank_timer_function,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ } else {
+ /*
+ * Timer should not be active. If it is, wait for the
+ * previous cancel operations to finish.
+ */
+ while (hrtimer_active(&vtimer->timer))
+ hrtimer_try_to_cancel(&vtimer->timer);
+ }
+
+ drm_calc_timestamping_constants(crtc, &crtc->mode);
+
+ spin_lock_irqsave(&vtimer->interval_lock, flags);
+ vtimer->interval = ns_to_ktime(vblank->framedur_ns);
+ spin_unlock_irqrestore(&vtimer->interval_lock, flags);
+
+ hrtimer_start(&vtimer->timer, vtimer->interval, HRTIMER_MODE_REL);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_start_timer);
+
+/**
+ * drm_crtc_vblank_cancel_timer - Cancels the given CRTC's vblank timer
+ * @crtc: the CRTC
+ *
+ * Drivers should call this function from their CRTC's disable_vblank
+ * function to stop a vblank timer.
+ */
+void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc)
+{
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+ struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer;
+ unsigned long flags;
+
+ /*
+ * Calling hrtimer_cancel() can result in a deadlock with DRM's
+ * vblank_time_lime_lock and hrtimers' softirq_expiry_lock. So
+ * clear interval and indicate cancellation. The timer function
+ * will cancel itself on the next invocation.
+ */
+
+ spin_lock_irqsave(&vtimer->interval_lock, flags);
+ vtimer->interval = 0;
+ spin_unlock_irqrestore(&vtimer->interval_lock, flags);
+
+ hrtimer_try_to_cancel(&vtimer->timer);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_cancel_timer);
+
+/**
+ * drm_crtc_vblank_get_vblank_timeout - Returns the vblank timeout
+ * @crtc: The CRTC
+ * @vblank_time: Returns the next vblank timestamp
+ *
+ * The helper drm_crtc_vblank_get_vblank_timeout() returns the next vblank
+ * timestamp of the CRTC's vblank timer according to the timer's expiry
+ * time.
+ */
+void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time)
+{
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+ struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer;
+ u64 cur_count;
+ ktime_t cur_time;
+
+ if (!READ_ONCE(vblank->enabled)) {
+ *vblank_time = ktime_get();
+ return;
+ }
+
+ /*
+ * A concurrent vblank timeout could update the expires field before
+ * we compare it with the vblank time. Hence we'd compare the old
+ * expiry time to the new vblank time; deducing the timer had already
+ * expired. Reread until we get consistent values from both fields.
+ */
+ do {
+ cur_count = drm_crtc_vblank_count_and_time(crtc, &cur_time);
+ *vblank_time = READ_ONCE(vtimer->timer.node.expires);
+ } while (cur_count != drm_crtc_vblank_count_and_time(crtc, &cur_time));
+
+ if (drm_WARN_ON(crtc->dev, !ktime_compare(*vblank_time, cur_time)))
+ return; /* Already expired */
+
+ /*
+ * To prevent races we roll the hrtimer forward before we do any
+ * interrupt processing - this is how real hw works (the interrupt
+ * is only generated after all the vblank registers are updated)
+ * and what the vblank core expects. Therefore we need to always
+ * correct the timestamp by one frame.
+ */
+ *vblank_time = ktime_sub(*vblank_time, vtimer->interval);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_get_vblank_timeout);
diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c
new file mode 100644
index 000000000000..a04a6ba1b0ca
--- /dev/null
+++ b/drivers/gpu/drm/drm_vblank_helper.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: MIT
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
+
+/**
+ * DOC: overview
+ *
+ * The vblank helper library provides functions for supporting vertical
+ * blanking in DRM drivers.
+ *
+ * For vblank timers, several callback implementations are available.
+ * Drivers enable support for vblank timers by setting the vblank callbacks
+ * in struct &drm_crtc_funcs to the helpers provided by this library. The
+ * initializer macro DRM_CRTC_VBLANK_TIMER_FUNCS does this conveniently.
+ * The driver further has to send the VBLANK event from its atomic_flush
+ * callback and control vblank from the CRTC's atomic_enable and atomic_disable
+ * callbacks. The callbacks are located in struct &drm_crtc_helper_funcs.
+ * The vblank helper library provides implementations of these callbacks
+ * for drivers without further requirements. The initializer macro
+ * DRM_CRTC_HELPER_VBLANK_FUNCS sets them coveniently.
+ *
+ * Once the driver enables vblank support with drm_vblank_init(), each
+ * CRTC's vblank timer fires according to the programmed display mode. By
+ * default, the vblank timer invokes drm_crtc_handle_vblank(). Drivers with
+ * more specific requirements can set their own handler function in
+ * struct &drm_crtc_helper_funcs.handle_vblank_timeout.
+ */
+
+/*
+ * VBLANK helpers
+ */
+
+/**
+ * drm_crtc_vblank_atomic_flush -
+ * Implements struct &drm_crtc_helper_funcs.atomic_flush
+ * @crtc: The CRTC
+ * @state: The atomic state to apply
+ *
+ * The helper drm_crtc_vblank_atomic_flush() implements atomic_flush of
+ * struct drm_crtc_helper_funcs for CRTCs that only need to send out a
+ * VBLANK event.
+ *
+ * See also struct &drm_crtc_helper_funcs.atomic_flush.
+ */
+void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_pending_vblank_event *event;
+
+ spin_lock_irq(&dev->event_lock);
+
+ event = crtc_state->event;
+ crtc_state->event = NULL;
+
+ if (event) {
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ }
+
+ spin_unlock_irq(&dev->event_lock);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_atomic_flush);
+
+/**
+ * drm_crtc_vblank_atomic_enable - Implements struct &drm_crtc_helper_funcs.atomic_enable
+ * @crtc: The CRTC
+ * @state: The atomic state
+ *
+ * The helper drm_crtc_vblank_atomic_enable() implements atomic_enable
+ * of struct drm_crtc_helper_funcs for CRTCs the only need to enable VBLANKs.
+ *
+ * See also struct &drm_crtc_helper_funcs.atomic_enable.
+ */
+void drm_crtc_vblank_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ drm_crtc_vblank_on(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_atomic_enable);
+
+/**
+ * drm_crtc_vblank_atomic_disable - Implements struct &drm_crtc_helper_funcs.atomic_disable
+ * @crtc: The CRTC
+ * @state: The atomic state
+ *
+ * The helper drm_crtc_vblank_atomic_disable() implements atomic_disable
+ * of struct drm_crtc_helper_funcs for CRTCs the only need to disable VBLANKs.
+ *
+ * See also struct &drm_crtc_funcs.atomic_disable.
+ */
+void drm_crtc_vblank_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ drm_crtc_vblank_off(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_atomic_disable);
+
+/*
+ * VBLANK timer
+ */
+
+/**
+ * drm_crtc_vblank_helper_enable_vblank_timer - Implements struct &drm_crtc_funcs.enable_vblank
+ * @crtc: The CRTC
+ *
+ * The helper drm_crtc_vblank_helper_enable_vblank_timer() implements
+ * enable_vblank of struct drm_crtc_helper_funcs for CRTCs that require
+ * a VBLANK timer. It sets up the timer on the first invocation. The
+ * started timer expires after the current frame duration. See struct
+ * &drm_vblank_crtc.framedur_ns.
+ *
+ * See also struct &drm_crtc_helper_funcs.enable_vblank.
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise.
+ */
+int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc)
+{
+ return drm_crtc_vblank_start_timer(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_helper_enable_vblank_timer);
+
+/**
+ * drm_crtc_vblank_helper_disable_vblank_timer - Implements struct &drm_crtc_funcs.disable_vblank
+ * @crtc: The CRTC
+ *
+ * The helper drm_crtc_vblank_helper_disable_vblank_timer() implements
+ * disable_vblank of struct drm_crtc_funcs for CRTCs that require a
+ * VBLANK timer.
+ *
+ * See also struct &drm_crtc_helper_funcs.disable_vblank.
+ */
+void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc)
+{
+ drm_crtc_vblank_cancel_timer(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_helper_disable_vblank_timer);
+
+/**
+ * drm_crtc_vblank_helper_get_vblank_timestamp_from_timer -
+ * Implements struct &drm_crtc_funcs.get_vblank_timestamp
+ * @crtc: The CRTC
+ * @max_error: Maximum acceptable error
+ * @vblank_time: Returns the next vblank timestamp
+ * @in_vblank_irq: True is called from drm_crtc_handle_vblank()
+ *
+ * The helper drm_crtc_helper_get_vblank_timestamp_from_timer() implements
+ * get_vblank_timestamp of struct drm_crtc_funcs for CRTCs that require a
+ * VBLANK timer. It returns the timestamp according to the timer's expiry
+ * time.
+ *
+ * See also struct &drm_crtc_funcs.get_vblank_timestamp.
+ *
+ * Returns:
+ * True on success, or false otherwise.
+ */
+bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc,
+ int *max_error,
+ ktime_t *vblank_time,
+ bool in_vblank_irq)
+{
+ drm_crtc_vblank_get_vblank_timeout(crtc, vblank_time);
+
+ return true;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_from_timer);
diff --git a/drivers/gpu/drm/drm_vblank_work.c b/drivers/gpu/drm/drm_vblank_work.c
index e4e1873f0e1e..70f0199251ea 100644
--- a/drivers/gpu/drm/drm_vblank_work.c
+++ b/drivers/gpu/drm/drm_vblank_work.c
@@ -244,7 +244,7 @@ EXPORT_SYMBOL(drm_vblank_work_flush);
void drm_vblank_work_flush_all(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
- struct drm_vblank_crtc *vblank = &dev->vblank[drm_crtc_index(crtc)];
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
spin_lock_irq(&dev->event_lock);
wait_event_lock_irq(vblank->work_wait_queue,
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c
index b13a17276d07..ad5e6f7b23f9 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "etnaviv_cmdbuf.h"
#include "etnaviv_gpu.h"
@@ -347,7 +348,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, u32 exec_state,
u32 link_target, link_dwords;
bool switch_context = gpu->exec_state != exec_state;
bool switch_mmu_context = gpu->mmu_context != mmu_context;
- unsigned int new_flush_seq = READ_ONCE(gpu->mmu_context->flush_seq);
+ unsigned int new_flush_seq = READ_ONCE(mmu_context->flush_seq);
bool need_flush = switch_mmu_context || gpu->flush_seq != new_flush_seq;
bool has_blt = !!(gpu->identity.minor_features5 &
chipMinorFeatures5_BLT_ENGINE);
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
index 3e91747ed339..54ceae87b401 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
@@ -17,6 +17,7 @@
#include <drm/drm_ioctl.h>
#include <drm/drm_of.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include "etnaviv_cmdbuf.h"
#include "etnaviv_drv.h"
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index 2f844e82bc46..5d8f3b03d4ae 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <linux/dma-mapping.h>
#include <linux/shmem_fs.h>
#include <linux/spinlock.h>
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
index 76c742328edb..a9611c1a773f 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include <linux/dma-fence-array.h>
#include <linux/file.h>
#include <linux/dma-resv.h>
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
index cf0d9049bcf1..ca0be293f5fe 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c
@@ -16,6 +16,8 @@
#include <linux/reset.h>
#include <linux/thermal.h>
+#include <drm/drm_print.h>
+
#include "etnaviv_cmdbuf.h"
#include "etnaviv_dump.h"
#include "etnaviv_gpu.h"
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c b/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c
index 8665f2658d51..32d710baf17f 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_hwdb.c
@@ -198,6 +198,38 @@ static const struct etnaviv_chip_identity etnaviv_chip_identities[] = {
},
{
.model = 0x8000,
+ .revision = 0x6205,
+ .product_id = 0x80003,
+ .customer_id = 0x15,
+ .eco_id = 0,
+ .stream_count = 16,
+ .register_max = 64,
+ .thread_count = 512,
+ .shader_core_count = 2,
+ .nn_core_count = 2,
+ .vertex_cache_size = 16,
+ .vertex_output_buffer_size = 1024,
+ .pixel_pipes = 1,
+ .instruction_count = 512,
+ .num_constants = 320,
+ .buffer_size = 0,
+ .varyings_count = 16,
+ .features = 0xe0287c8d,
+ .minor_features0 = 0xc1799eff,
+ .minor_features1 = 0xfefbfad9,
+ .minor_features2 = 0xeb9d4fbf,
+ .minor_features3 = 0xedfffced,
+ .minor_features4 = 0xdb0dafc7,
+ .minor_features5 = 0x7b5ac333,
+ .minor_features6 = 0xfcce6000,
+ .minor_features7 = 0x03fbfa6f,
+ .minor_features8 = 0x00ef0ef0,
+ .minor_features9 = 0x0eca703c,
+ .minor_features10 = 0x898048f0,
+ .minor_features11 = 0x00000034,
+ },
+ {
+ .model = 0x8000,
.revision = 0x7120,
.product_id = 0x45080009,
.customer_id = 0x88,
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
index df5192083b20..a992be2ede88 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
@@ -6,6 +6,8 @@
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
+#include <drm/drm_print.h>
+
#include "common.xml.h"
#include "etnaviv_cmdbuf.h"
#include "etnaviv_drv.h"
diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
index b9e206303b48..9ae0fa4667a9 100644
--- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c
@@ -20,6 +20,7 @@
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "exynos_drm_crtc.h"
diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
index b8d9b7251319..bb74b17f9753 100644
--- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c
+++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c
@@ -20,6 +20,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/exynos_drm.h>
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index ddd73e7f26a3..6ecd95bcb0c4 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -14,6 +14,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/exynos_drm.h>
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 93de25b77e68..637927818dfe 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -16,6 +16,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
@@ -42,8 +43,6 @@ static void exynos_drm_fb_destroy(struct fb_info *info)
drm_framebuffer_remove(fb);
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
static const struct fb_ops exynos_drm_fb_ops = {
@@ -59,18 +58,11 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes,
struct exynos_drm_gem *exynos_gem)
{
- struct fb_info *fbi;
+ struct fb_info *fbi = helper->info;
struct drm_framebuffer *fb = helper->fb;
unsigned int size = fb->width * fb->height * fb->format->cpp[0];
unsigned long offset;
- fbi = drm_fb_helper_alloc_info(helper);
- if (IS_ERR(fbi)) {
- DRM_DEV_ERROR(to_dma_dev(helper->dev),
- "failed to allocate fb info.\n");
- return PTR_ERR(fbi);
- }
-
fbi->fbops = &exynos_drm_fb_ops;
drm_fb_helper_fill_info(fbi, helper, sizes);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 205c238cc73a..b6abdc4f2b0a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -23,6 +23,7 @@
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/exynos_drm.h>
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index d32f2474cbaa..2bea107dd960 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -21,6 +21,7 @@
#include <linux/workqueue.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index e3fbb45f37a2..b9b2f000072d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -10,7 +10,9 @@
#include <linux/shmem_fs.h>
#include <linux/module.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/drm_vma_manager.h>
#include <drm/exynos_drm.h>
@@ -329,15 +331,16 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
unsigned int flags;
int ret;
+ ret = drm_mode_size_dumb(dev, args, 0, 0);
+ if (ret)
+ return ret;
+
/*
* allocate memory to be used for framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_CREATE_DUMB command.
*/
- args->pitch = args->width * ((args->bpp + 7) / 8);
- args->size = args->pitch * args->height;
-
if (is_drm_iommu_supported(dev))
flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC;
else
diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
index 03c8490af4f4..008def51225a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c
@@ -22,6 +22,7 @@
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_mode.h>
+#include <drm/drm_print.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index 7c3aa77186d3..67afddd566e2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -9,6 +9,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_crtc.h"
@@ -58,7 +59,7 @@ static void exynos_plane_mode_set(struct exynos_drm_plane_state *exynos_state)
struct drm_plane_state *state = &exynos_state->base;
struct drm_crtc *crtc = state->crtc;
struct drm_crtc_state *crtc_state =
- drm_atomic_get_existing_crtc_state(state->state, crtc);
+ drm_atomic_get_new_crtc_state(state->state, crtc);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
int crtc_x, crtc_y;
unsigned int crtc_w, crtc_h;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index e094b8bbc0f1..64c69dd2966e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -14,6 +14,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index a3670d2eaab2..69dea5049309 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -28,6 +28,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/exynos_drm.h>
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
index 794a87d16f88..a9a341ea6507 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
@@ -15,6 +15,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "fsl_dcu_drm_drv.h"
diff --git a/drivers/gpu/drm/gma500/backlight.c b/drivers/gpu/drm/gma500/backlight.c
index 8711a7a5b8da..c8f1716a12d5 100644
--- a/drivers/gpu/drm/gma500/backlight.c
+++ b/drivers/gpu/drm/gma500/backlight.c
@@ -11,6 +11,8 @@
#include <acpi/video.h>
+#include <drm/drm_print.h>
+
#include "psb_drv.h"
#include "psb_intel_reg.h"
#include "psb_intel_drv.h"
diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c
index 718d45891fc7..fd6ea8998dbe 100644
--- a/drivers/gpu/drm/gma500/cdv_device.c
+++ b/drivers/gpu/drm/gma500/cdv_device.c
@@ -9,6 +9,7 @@
#include <drm/drm.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_print.h>
#include "cdv_device.h"
#include "gma_device.h"
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c
index bbd0abdd8382..5942a9d46b02 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_display.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
@@ -11,6 +11,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include "cdv_device.h"
#include "framebuffer.h"
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index c85143792019..54bf626f0524 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -34,6 +34,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "gma_display.h"
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index f2a3e37ef632..8e93ee0d0ccd 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -31,6 +31,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "cdv_device.h"
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 9276e3676ba0..fbe7fe317393 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -14,6 +14,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "cdv_device.h"
diff --git a/drivers/gpu/drm/gma500/fbdev.c b/drivers/gpu/drm/gma500/fbdev.c
index 32d31e5f5f1a..c26926babc2a 100644
--- a/drivers/gpu/drm/gma500/fbdev.c
+++ b/drivers/gpu/drm/gma500/fbdev.c
@@ -50,48 +50,6 @@ static const struct vm_operations_struct psb_fbdev_vm_ops = {
* struct fb_ops
*/
-#define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16)
-
-static int psb_fbdev_fb_setcolreg(unsigned int regno,
- unsigned int red, unsigned int green,
- unsigned int blue, unsigned int transp,
- struct fb_info *info)
-{
- struct drm_fb_helper *fb_helper = info->par;
- struct drm_framebuffer *fb = fb_helper->fb;
- uint32_t v;
-
- if (!fb)
- return -ENOMEM;
-
- if (regno > 255)
- return 1;
-
- red = CMAP_TOHW(red, info->var.red.length);
- blue = CMAP_TOHW(blue, info->var.blue.length);
- green = CMAP_TOHW(green, info->var.green.length);
- transp = CMAP_TOHW(transp, info->var.transp.length);
-
- v = (red << info->var.red.offset) |
- (green << info->var.green.offset) |
- (blue << info->var.blue.offset) |
- (transp << info->var.transp.offset);
-
- if (regno < 16) {
- switch (fb->format->cpp[0] * 8) {
- case 16:
- ((uint32_t *) info->pseudo_palette)[regno] = v;
- break;
- case 24:
- case 32:
- ((uint32_t *) info->pseudo_palette)[regno] = v;
- break;
- }
- }
-
- return 0;
-}
-
static int psb_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
if (vma->vm_pgoff != 0)
@@ -126,16 +84,12 @@ static void psb_fbdev_fb_destroy(struct fb_info *info)
drm_gem_object_put(obj);
drm_client_release(&fb_helper->client);
-
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
static const struct fb_ops psb_fbdev_fb_ops = {
.owner = THIS_MODULE,
__FB_DEFAULT_IOMEM_OPS_RDWR,
DRM_FB_HELPER_DEFAULT_OPS,
- .fb_setcolreg = psb_fbdev_fb_setcolreg,
__FB_DEFAULT_IOMEM_OPS_DRAW,
.fb_mmap = psb_fbdev_fb_mmap,
.fb_destroy = psb_fbdev_fb_destroy,
@@ -154,7 +108,7 @@ int psb_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
struct drm_device *dev = fb_helper->dev;
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
struct pci_dev *pdev = to_pci_dev(dev->dev);
- struct fb_info *info;
+ struct fb_info *info = fb_helper->info;
struct drm_framebuffer *fb;
struct drm_mode_fb_cmd2 mode_cmd = { };
int size;
@@ -213,12 +167,6 @@ int psb_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
fb_helper->funcs = &psb_fbdev_fb_helper_funcs;
fb_helper->fb = fb;
- info = drm_fb_helper_alloc_info(fb_helper);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto err_drm_framebuffer_unregister_private;
- }
-
info->fbops = &psb_fbdev_fb_ops;
/* Accessed stolen memory directly */
@@ -242,10 +190,6 @@ int psb_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
return 0;
-err_drm_framebuffer_unregister_private:
- drm_framebuffer_unregister_private(fb);
- drm_framebuffer_cleanup(fb);
- kfree(fb);
err_drm_gem_object_put:
drm_gem_object_put(obj);
return ret;
diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
index 4b7627a72637..2e44a2ac2742 100644
--- a/drivers/gpu/drm/gma500/gem.c
+++ b/drivers/gpu/drm/gma500/gem.c
@@ -16,6 +16,7 @@
#include <asm/set_memory.h>
#include <drm/drm.h>
+#include <drm/drm_print.h>
#include <drm/drm_vma_manager.h>
#include "gem.h"
diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c
index d5924ca3ed05..b60720560830 100644
--- a/drivers/gpu/drm/gma500/intel_bios.c
+++ b/drivers/gpu/drm/gma500/intel_bios.c
@@ -8,6 +8,7 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/drm.h>
+#include <drm/drm_print.h>
#include "intel_bios.h"
#include "psb_drv.h"
diff --git a/drivers/gpu/drm/gma500/intel_gmbus.c b/drivers/gpu/drm/gma500/intel_gmbus.c
index ee8b047587f2..2b06ba22f9c6 100644
--- a/drivers/gpu/drm/gma500/intel_gmbus.c
+++ b/drivers/gpu/drm/gma500/intel_gmbus.c
@@ -32,6 +32,8 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <drm/drm_print.h>
+
#include "psb_drv.h"
#include "psb_intel_drv.h"
#include "psb_intel_reg.h"
diff --git a/drivers/gpu/drm/gma500/mid_bios.c b/drivers/gpu/drm/gma500/mid_bios.c
index cba97d7db131..0326f3ddc621 100644
--- a/drivers/gpu/drm/gma500/mid_bios.c
+++ b/drivers/gpu/drm/gma500/mid_bios.c
@@ -12,6 +12,7 @@
*/
#include <drm/drm.h>
+#include <drm/drm_print.h>
#include "mid_bios.h"
#include "psb_drv.h"
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c
index ea9b41af0867..086d14678a8e 100644
--- a/drivers/gpu/drm/gma500/oaktrail_crtc.c
+++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c
@@ -10,6 +10,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include "framebuffer.h"
#include "gem.h"
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
index c0feca58511d..20d027d552c7 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
@@ -30,6 +30,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "psb_drv.h"
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
index 6daa6669ed23..48e8ac560a2a 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
@@ -30,6 +30,9 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+
+#include <drm/drm_print.h>
+
#include "psb_drv.h"
#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg))
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
index 72191d6f0d06..0705ba3813e6 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c
@@ -13,6 +13,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "intel_bios.h"
diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c
index 0c271072af63..5f0daa25b86d 100644
--- a/drivers/gpu/drm/gma500/opregion.c
+++ b/drivers/gpu/drm/gma500/opregion.c
@@ -22,6 +22,9 @@
*
*/
#include <linux/acpi.h>
+
+#include <drm/drm_print.h>
+
#include "psb_drv.h"
#include "psb_irq.h"
#include "psb_intel_reg.h"
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index 85d3557c2eb9..005ab7f5355f 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -25,6 +25,7 @@
#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_pciids.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "framebuffer.h"
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
index ff46e88c4768..1ff2bd23db74 100644
--- a/drivers/gpu/drm/gma500/psb_intel_display.c
+++ b/drivers/gpu/drm/gma500/psb_intel_display.c
@@ -11,6 +11,7 @@
#include <drm/drm_modeset_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include "framebuffer.h"
#include "gem.h"
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index 9ad611b5956e..f8f3c42e67a7 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -13,6 +13,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "intel_bios.h"
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index afda40fc4494..553e7c7d9bb8 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -36,6 +36,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include "psb_drv.h"
#include "psb_intel_drv.h"
diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c
index 7bbb79b0497d..3a946b472064 100644
--- a/drivers/gpu/drm/gma500/psb_irq.c
+++ b/drivers/gpu/drm/gma500/psb_irq.c
@@ -9,6 +9,7 @@
**************************************************************************/
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "power.h"
@@ -249,6 +250,7 @@ static irqreturn_t gma_irq_handler(int irq, void *arg)
void gma_irq_preinstall(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
+ struct drm_crtc *crtc;
unsigned long irqflags;
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
@@ -259,10 +261,15 @@ void gma_irq_preinstall(struct drm_device *dev)
PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE);
PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE);
- if (dev->vblank[0].enabled)
- dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
- if (dev->vblank[1].enabled)
- dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+
+ if (vblank->enabled) {
+ u32 mask = drm_crtc_index(crtc) ? _PSB_VSYNC_PIPEB_FLAG :
+ _PSB_VSYNC_PIPEA_FLAG;
+ dev_priv->vdc_irq_mask |= mask;
+ }
+ }
/* Revisit this area - want per device masks ? */
if (dev_priv->ops->hotplug)
@@ -277,8 +284,8 @@ void gma_irq_preinstall(struct drm_device *dev)
void gma_irq_postinstall(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
+ struct drm_crtc *crtc;
unsigned long irqflags;
- unsigned int i;
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
@@ -291,11 +298,13 @@ void gma_irq_postinstall(struct drm_device *dev)
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
- for (i = 0; i < dev->num_crtcs; ++i) {
- if (dev->vblank[i].enabled)
- gma_enable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+
+ if (vblank->enabled)
+ gma_enable_pipestat(dev_priv, drm_crtc_index(crtc), PIPE_VBLANK_INTERRUPT_ENABLE);
else
- gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
+ gma_disable_pipestat(dev_priv, drm_crtc_index(crtc), PIPE_VBLANK_INTERRUPT_ENABLE);
}
if (dev_priv->ops->hotplug_enable)
@@ -336,8 +345,8 @@ void gma_irq_uninstall(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
struct pci_dev *pdev = to_pci_dev(dev->dev);
+ struct drm_crtc *crtc;
unsigned long irqflags;
- unsigned int i;
if (!dev_priv->irq_enabled)
return;
@@ -349,9 +358,11 @@ void gma_irq_uninstall(struct drm_device *dev)
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
- for (i = 0; i < dev->num_crtcs; ++i) {
- if (dev->vblank[i].enabled)
- gma_disable_pipestat(dev_priv, i, PIPE_VBLANK_INTERRUPT_ENABLE);
+ drm_for_each_crtc(crtc, dev) {
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+
+ if (vblank->enabled)
+ gma_disable_pipestat(dev_priv, drm_crtc_index(crtc), PIPE_VBLANK_INTERRUPT_ENABLE);
}
dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG |
diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c
index 4a15695fa933..1726a3fadff8 100644
--- a/drivers/gpu/drm/gud/gud_connector.c
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -561,11 +561,11 @@ static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_conn
continue; /* not a DRM property */
property = gud_connector_property_lookup(connector, prop);
- if (WARN_ON(IS_ERR(property)))
+ if (drm_WARN_ON(drm, IS_ERR(property)))
continue;
state_val = gud_connector_tv_state_val(prop, &gconn->initial_tv_state);
- if (WARN_ON(IS_ERR(state_val)))
+ if (drm_WARN_ON(drm, IS_ERR(state_val)))
continue;
*state_val = val;
@@ -593,7 +593,7 @@ int gud_connector_fill_properties(struct drm_connector_state *connector_state,
unsigned int *state_val;
state_val = gud_connector_tv_state_val(prop, &connector_state->tv);
- if (WARN_ON_ONCE(IS_ERR(state_val)))
+ if (drm_WARN_ON_ONCE(connector_state->connector->dev, IS_ERR(state_val)))
return PTR_ERR(state_val);
val = *state_val;
@@ -667,7 +667,7 @@ static int gud_connector_create(struct gud_device *gdrm, unsigned int index,
return ret;
}
- if (WARN_ON(connector->index != index))
+ if (drm_WARN_ON(drm, connector->index != index))
return -EINVAL;
if (flags & GUD_CONNECTOR_FLAGS_POLL_STATUS)
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
index b7345c8d823d..42135a48d92e 100644
--- a/drivers/gpu/drm/gud/gud_drv.c
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -249,7 +249,7 @@ int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val)
return gud_usb_set(gdrm, request, 0, &val, sizeof(val));
}
-static int gud_get_properties(struct gud_device *gdrm)
+static int gud_plane_add_properties(struct gud_device *gdrm)
{
struct gud_property_req *properties;
unsigned int i, num_properties;
@@ -463,10 +463,6 @@ static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
return PTR_ERR(gdrm);
drm = &gdrm->drm;
- drm->mode_config.funcs = &gud_mode_config_funcs;
- ret = drmm_mode_config_init(drm);
- if (ret)
- return ret;
gdrm->flags = le32_to_cpu(desc.flags);
gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4;
@@ -483,11 +479,28 @@ static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (ret)
return ret;
+ usb_set_intfdata(intf, gdrm);
+
+ dma_dev = usb_intf_get_dma_device(intf);
+ if (dma_dev) {
+ drm_dev_set_dma_dev(drm, dma_dev);
+ put_device(dma_dev);
+ } else {
+ dev_warn(dev, "buffer sharing not supported"); /* not an error */
+ }
+
+ /* Mode config init */
+ ret = drmm_mode_config_init(drm);
+ if (ret)
+ return ret;
+
drm->mode_config.min_width = le32_to_cpu(desc.min_width);
drm->mode_config.max_width = le32_to_cpu(desc.max_width);
drm->mode_config.min_height = le32_to_cpu(desc.min_height);
drm->mode_config.max_height = le32_to_cpu(desc.max_height);
+ drm->mode_config.funcs = &gud_mode_config_funcs;
+ /* Format init */
formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL);
/* Add room for emulated XRGB8888 */
formats = devm_kmalloc_array(dev, GUD_FORMATS_MAX_NUM + 1, sizeof(*formats), GFP_KERNEL);
@@ -587,6 +600,7 @@ static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
return -ENOMEM;
}
+ /* Pipeline init */
ret = drm_universal_plane_init(drm, &gdrm->plane, 0,
&gud_plane_funcs,
formats, num_formats,
@@ -598,12 +612,9 @@ static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
drm_plane_helper_add(&gdrm->plane, &gud_plane_helper_funcs);
drm_plane_enable_fb_damage_clips(&gdrm->plane);
- devm_kfree(dev, formats);
- devm_kfree(dev, formats_dev);
-
- ret = gud_get_properties(gdrm);
+ ret = gud_plane_add_properties(gdrm);
if (ret) {
- dev_err(dev, "Failed to get properties (error=%d)\n", ret);
+ dev_err(dev, "Failed to add properties (error=%d)\n", ret);
return ret;
}
@@ -621,16 +632,7 @@ static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
}
drm_mode_config_reset(drm);
-
- usb_set_intfdata(intf, gdrm);
-
- dma_dev = usb_intf_get_dma_device(intf);
- if (dma_dev) {
- drm_dev_set_dma_dev(drm, dma_dev);
- put_device(dma_dev);
- } else {
- dev_warn(dev, "buffer sharing not supported"); /* not an error */
- }
+ drm_kms_helper_poll_init(drm);
drm_debugfs_add_file(drm, "stats", gud_stats_debugfs, NULL);
@@ -638,7 +640,8 @@ static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (ret)
return ret;
- drm_kms_helper_poll_init(drm);
+ devm_kfree(dev, formats);
+ devm_kfree(dev, formats_dev);
drm_client_setup(drm, NULL);
diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c
index 54d9aa9998e5..76d77a736d84 100644
--- a/drivers/gpu/drm/gud/gud_pipe.c
+++ b/drivers/gpu/drm/gud/gud_pipe.c
@@ -61,7 +61,7 @@ static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format
size_t len;
void *buf;
- WARN_ON_ONCE(format->char_per_block[0] != 1);
+ drm_WARN_ON_ONCE(fb->dev, format->char_per_block[0] != 1);
/* Start on a byte boundary */
rect->x1 = ALIGN_DOWN(rect->x1, block_width);
@@ -69,7 +69,7 @@ static size_t gud_xrgb8888_to_r124(u8 *dst, const struct drm_format_info *format
height = drm_rect_height(rect);
len = drm_format_info_min_pitch(format, 0, width) * height;
- buf = kmalloc(width * height, GFP_KERNEL);
+ buf = kmalloc_array(height, width, GFP_KERNEL);
if (!buf)
return 0;
@@ -138,7 +138,7 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma
pix = ((r >> 7) << 2) | ((g >> 7) << 1) | (b >> 7);
break;
default:
- WARN_ON_ONCE(1);
+ drm_WARN_ON_ONCE(fb->dev, 1);
return len;
}
@@ -527,7 +527,7 @@ int gud_plane_atomic_check(struct drm_plane *plane,
drm_connector_list_iter_end(&conn_iter);
}
- if (WARN_ON_ONCE(!connector_state))
+ if (drm_WARN_ON_ONCE(plane->dev, !connector_state))
return -ENOENT;
len = struct_size(req, properties,
@@ -539,7 +539,7 @@ int gud_plane_atomic_check(struct drm_plane *plane,
gud_from_display_mode(&req->mode, mode);
req->format = gud_from_fourcc(format->format);
- if (WARN_ON_ONCE(!req->format)) {
+ if (drm_WARN_ON_ONCE(plane->dev, !req->format)) {
ret = -EINVAL;
goto out;
}
@@ -561,7 +561,7 @@ int gud_plane_atomic_check(struct drm_plane *plane,
val = new_plane_state->rotation;
break;
default:
- WARN_ON_ONCE(1);
+ drm_WARN_ON_ONCE(plane->dev, 1);
ret = -EINVAL;
goto out;
}
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
index 45c4eb008ad5..76384b4581bf 100644
--- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
@@ -29,6 +29,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_gem_framebuffer_helper.h>
diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
index 1e1c87be1204..8a11c2df5b88 100644
--- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
+++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
@@ -24,6 +24,7 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
index 0d49f168a919..06b5d96e6eaf 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
@@ -14,6 +14,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "hyperv_drm.h"
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
index 945b9482bcb3..7978f8c8108c 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
@@ -19,6 +19,9 @@
#include <drm/drm_probe_helper.h>
#include <drm/drm_panic.h>
#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#include "hyperv_drm.h"
@@ -111,11 +114,15 @@ static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc,
crtc_state->mode.hdisplay,
crtc_state->mode.vdisplay,
plane_state->fb->pitches[0]);
+
+ drm_crtc_vblank_on(crtc);
}
static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = {
.atomic_check = drm_crtc_helper_atomic_check,
+ .atomic_flush = drm_crtc_vblank_atomic_flush,
.atomic_enable = hyperv_crtc_helper_atomic_enable,
+ .atomic_disable = drm_crtc_vblank_atomic_disable,
};
static const struct drm_crtc_funcs hyperv_crtc_funcs = {
@@ -125,6 +132,7 @@ static const struct drm_crtc_funcs hyperv_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
};
static int hyperv_plane_atomic_check(struct drm_plane *plane,
@@ -321,6 +329,10 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv)
return ret;
}
+ ret = drm_vblank_init(dev, 1);
+ if (ret)
+ return ret;
+
drm_mode_config_reset(dev);
return 0;
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index e58c0c158b3a..4db24050edb0 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -13,6 +13,11 @@ subdir-ccflags-$(CONFIG_DRM_I915_WERROR) += -Werror
# drivers. Define I915 when building i915.
subdir-ccflags-y += -DI915
+# FIXME: Disable tracepoints on i915 for PREEMPT_RT, unfortunately
+# it's an all or nothing flag. You cannot selectively disable
+# only some tracepoints.
+subdir-ccflags-$(CONFIG_PREEMPT_RT) += -DNOTRACE
+
subdir-ccflags-y += -I$(src)
# Please keep these build lists sorted!
@@ -26,6 +31,7 @@ i915-y += \
i915_ioctl.o \
i915_irq.o \
i915_mitigations.o \
+ i915_mmio_range.o \
i915_module.o \
i915_params.o \
i915_pci.o \
@@ -150,6 +156,7 @@ gem-y += \
gem/i915_gem_lmem.o \
gem/i915_gem_mman.o \
gem/i915_gem_object.o \
+ gem/i915_gem_object_frontbuffer.o \
gem/i915_gem_pages.o \
gem/i915_gem_phys.o \
gem/i915_gem_pm.o \
@@ -228,14 +235,18 @@ i915-y += \
display/intel_bios.o \
display/intel_bo.o \
display/intel_bw.o \
+ display/intel_casf.o \
display/intel_cdclk.o \
display/intel_cmtg.o \
display/intel_color.o \
+ display/intel_colorop.o \
+ display/intel_color_pipeline.o \
display/intel_combo_phy.o \
display/intel_connector.o \
display/intel_crtc.o \
display/intel_crtc_state_dump.o \
display/intel_cursor.o \
+ display/intel_dbuf_bw.o \
display/intel_display.o \
display/intel_display_conversion.o \
display/intel_display_driver.o \
@@ -248,6 +259,7 @@ i915-y += \
display/intel_display_rpm.o \
display/intel_display_rps.o \
display/intel_display_snapshot.o \
+ display/intel_display_utils.o \
display/intel_display_wa.o \
display/intel_dmc.o \
display/intel_dmc_wl.o \
@@ -297,9 +309,11 @@ i915-y += \
display/intel_vblank.o \
display/intel_vga.o \
display/intel_wm.o \
+ display/skl_prefill.o \
display/skl_scaler.o \
display/skl_universal_plane.o \
display/skl_watermark.o \
+ display/vlv_clock.o \
display/vlv_sideband.o
i915-$(CONFIG_ACPI) += \
display/intel_acpi.o \
@@ -346,6 +360,7 @@ i915-y += \
display/intel_gmbus.o \
display/intel_hdmi.o \
display/intel_lspcon.o \
+ display/intel_lt_phy.o \
display/intel_lvds.o \
display/intel_panel.o \
display/intel_pfit.o \
@@ -413,7 +428,7 @@ obj-$(CONFIG_DRM_I915_GVT_KVMGT) += kvmgt.o
#
# Enable locally for CONFIG_DRM_I915_WERROR=y. See also scripts/Makefile.build
ifdef CONFIG_DRM_I915_WERROR
- cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none -Werror $<
+ cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(PYTHON3) $(KERNELDOC) -none -Werror $<
endif
# header test
diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c
index aa159f9ce12f..a3ff21b2f69f 100644
--- a/drivers/gpu/drm/i915/display/g4x_dp.c
+++ b/drivers/gpu/drm/i915/display/g4x_dp.c
@@ -11,7 +11,6 @@
#include "g4x_dp.h"
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_audio.h"
#include "intel_backlight.h"
#include "intel_connector.h"
@@ -20,6 +19,7 @@
#include "intel_display_power.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dp_aux.h"
#include "intel_dp_link_training.h"
diff --git a/drivers/gpu/drm/i915/display/hsw_ips.c b/drivers/gpu/drm/i915/display/hsw_ips.c
index 927fe56aec77..008d339d5c21 100644
--- a/drivers/gpu/drm/i915/display/hsw_ips.c
+++ b/drivers/gpu/drm/i915/display/hsw_ips.c
@@ -56,7 +56,7 @@ static void hsw_ips_enable(const struct intel_crtc_state *crtc_state)
* the HW state readout code will complain that the expected
* IPS_CTL value is not the one we read.
*/
- if (intel_de_wait_for_set(display, IPS_CTL, IPS_ENABLE, 50))
+ if (intel_de_wait_for_set_ms(display, IPS_CTL, IPS_ENABLE, 50))
drm_err(display->drm,
"Timed out waiting for IPS enable\n");
}
@@ -78,7 +78,7 @@ bool hsw_ips_disable(const struct intel_crtc_state *crtc_state)
* 42ms timeout value leads to occasional timeouts so use 100ms
* instead.
*/
- if (intel_de_wait_for_clear(display, IPS_CTL, IPS_ENABLE, 100))
+ if (intel_de_wait_for_clear_ms(display, IPS_CTL, IPS_ENABLE, 100))
drm_err(display->drm,
"Timed out waiting for IPS disable\n");
} else {
@@ -191,45 +191,46 @@ bool hsw_crtc_supports_ips(struct intel_crtc *crtc)
static bool hsw_crtc_state_ips_capable(const struct intel_crtc_state *crtc_state)
{
- struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- /* IPS only exists on ULT machines and is tied to pipe A. */
if (!hsw_crtc_supports_ips(crtc))
return false;
- if (!display->params.enable_ips)
- return false;
-
if (crtc_state->pipe_bpp > 24)
return false;
- /*
- * We compare against max which means we must take
- * the increased cdclk requirement into account when
- * calculating the new cdclk.
- *
- * Should measure whether using a lower cdclk w/o IPS
- */
- if (display->platform.broadwell &&
- crtc_state->pixel_rate > display->cdclk.max_cdclk_freq * 95 / 100)
- return false;
-
return true;
}
+static int _hsw_ips_min_cdclk(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ if (display->platform.broadwell)
+ return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95);
+
+ /* no IPS specific limits to worry about */
+ return 0;
+}
+
int hsw_ips_min_cdclk(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
+ int min_cdclk;
- if (!display->platform.broadwell)
+ if (!hsw_crtc_state_ips_capable(crtc_state))
return 0;
- if (!hsw_crtc_state_ips_capable(crtc_state))
+ min_cdclk = _hsw_ips_min_cdclk(crtc_state);
+
+ /*
+ * Do not ask for more than the max CDCLK frequency,
+ * if that is not enough IPS will simply not be used.
+ */
+ if (min_cdclk > display->cdclk.max_cdclk_freq)
return 0;
- /* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */
- return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95);
+ return min_cdclk;
}
int hsw_ips_compute_config(struct intel_atomic_state *state,
@@ -244,6 +245,12 @@ int hsw_ips_compute_config(struct intel_atomic_state *state,
if (!hsw_crtc_state_ips_capable(crtc_state))
return 0;
+ if (_hsw_ips_min_cdclk(crtc_state) > display->cdclk.max_cdclk_freq)
+ return 0;
+
+ if (!display->params.enable_ips)
+ return 0;
+
/*
* When IPS gets enabled, the pipe CRC changes. Since IPS gets
* enabled and disabled dynamically based on package C states,
@@ -257,18 +264,6 @@ int hsw_ips_compute_config(struct intel_atomic_state *state,
if (!(crtc_state->active_planes & ~BIT(PLANE_CURSOR)))
return 0;
- if (display->platform.broadwell) {
- const struct intel_cdclk_state *cdclk_state;
-
- cdclk_state = intel_atomic_get_cdclk_state(state);
- if (IS_ERR(cdclk_state))
- return PTR_ERR(cdclk_state);
-
- /* pixel rate mustn't exceed 95% of cdclk with IPS on BDW */
- if (crtc_state->pixel_rate > intel_cdclk_logical(cdclk_state) * 95 / 100)
- return 0;
- }
-
crtc_state->ips_enabled = true;
return 0;
diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.c b/drivers/gpu/drm/i915/display/i9xx_plane.c
index 407deb5dfb57..51ccc6bd5f21 100644
--- a/drivers/gpu/drm/i915/display/i9xx_plane.c
+++ b/drivers/gpu/drm/i915/display/i9xx_plane.c
@@ -11,7 +11,6 @@
#include <drm/drm_print.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "i9xx_plane.h"
#include "i9xx_plane_regs.h"
#include "intel_atomic.h"
@@ -19,6 +18,7 @@
#include "intel_display_irq.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_fb.h"
#include "intel_fbc.h"
#include "intel_frontbuffer.h"
@@ -754,10 +754,9 @@ static bool i9xx_plane_get_hw_state(struct intel_plane *plane,
static unsigned int
hsw_primary_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
- const struct drm_format_info *info = drm_format_info(pixel_format);
int cpp = info->cpp[0];
/* Limit to 8k pixels to guarantee OFFSET.x doesn't get too big. */
@@ -766,10 +765,9 @@ hsw_primary_max_stride(struct intel_plane *plane,
static unsigned int
ilk_primary_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
- const struct drm_format_info *info = drm_format_info(pixel_format);
int cpp = info->cpp[0];
/* Limit to 4k pixels to guarantee TILEOFF.x doesn't get too big. */
@@ -781,10 +779,9 @@ ilk_primary_max_stride(struct intel_plane *plane,
unsigned int
i965_plane_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
- const struct drm_format_info *info = drm_format_info(pixel_format);
int cpp = info->cpp[0];
/* Limit to 4k pixels to guarantee TILEOFF.x doesn't get too big. */
@@ -796,8 +793,8 @@ i965_plane_max_stride(struct intel_plane *plane,
static unsigned int
i915_plane_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
if (modifier == I915_FORMAT_MOD_X_TILED)
return 8 * 1024;
@@ -807,8 +804,8 @@ i915_plane_max_stride(struct intel_plane *plane,
static unsigned int
i8xx_plane_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
if (plane->i9xx_plane == PLANE_C)
return 4 * 1024;
@@ -1191,10 +1188,8 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc,
val = intel_de_read(display, DSPCNTR(display, i9xx_plane));
if (DISPLAY_VER(display) >= 4) {
- if (val & DISP_TILED) {
- plane_config->tiling = I915_TILING_X;
+ if (val & DISP_TILED)
fb->modifier = I915_FORMAT_MOD_X_TILED;
- }
if (val & DISP_ROTATE_180)
plane_config->rotation = DRM_MODE_ROTATE_180;
@@ -1206,14 +1201,15 @@ i9xx_get_initial_plane_config(struct intel_crtc *crtc,
pixel_format = val & DISP_FORMAT_MASK;
fourcc = i9xx_format_to_fourcc(pixel_format);
- fb->format = drm_format_info(fourcc);
+
+ fb->format = drm_get_format_info(display->drm, fourcc, fb->modifier);
if (display->platform.haswell || display->platform.broadwell) {
offset = intel_de_read(display,
DSPOFFSET(display, i9xx_plane));
base = intel_de_read(display, DSPSURF(display, i9xx_plane)) & DISP_ADDR_MASK;
} else if (DISPLAY_VER(display) >= 4) {
- if (plane_config->tiling)
+ if (fb->modifier == I915_FORMAT_MOD_X_TILED)
offset = intel_de_read(display,
DSPTILEOFF(display, i9xx_plane));
else
diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.h b/drivers/gpu/drm/i915/display/i9xx_plane.h
index 565dab751301..ec78bf4dd35e 100644
--- a/drivers/gpu/drm/i915/display/i9xx_plane.h
+++ b/drivers/gpu/drm/i915/display/i9xx_plane.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
enum pipe;
+struct drm_format_info;
struct drm_framebuffer;
struct intel_crtc;
struct intel_display;
@@ -18,8 +19,8 @@ struct intel_plane_state;
#ifdef I915
unsigned int i965_plane_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation);
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation);
unsigned int vlv_plane_min_alignment(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int colot_plane);
diff --git a/drivers/gpu/drm/i915/display/i9xx_wm.c b/drivers/gpu/drm/i915/display/i9xx_wm.c
index fd3b7b35f351..01f3803fa09f 100644
--- a/drivers/gpu/drm/i915/display/i9xx_wm.c
+++ b/drivers/gpu/drm/i915/display/i9xx_wm.c
@@ -5,6 +5,8 @@
#include <linux/iopoll.h>
+#include <drm/drm_print.h>
+
#include "soc/intel_dram.h"
#include "i915_drv.h"
@@ -2295,12 +2297,11 @@ static void i9xx_update_wm(struct intel_display *display)
crtc = single_enabled_crtc(display);
if (display->platform.i915gm && crtc) {
- struct drm_gem_object *obj;
-
- obj = intel_fb_bo(crtc->base.primary->state->fb);
+ const struct drm_framebuffer *fb =
+ crtc->base.primary->state->fb;
/* self-refresh seems busted with untiled */
- if (!intel_bo_is_tiled(obj))
+ if (fb->modifier == DRM_FORMAT_MOD_LINEAR)
crtc = NULL;
}
diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c
index 37faa8f19f6e..9230792960f2 100644
--- a/drivers/gpu/drm/i915/display/icl_dsi.c
+++ b/drivers/gpu/drm/i915/display/icl_dsi.c
@@ -35,7 +35,6 @@
#include <drm/drm_probe_helper.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "icl_dsi.h"
#include "icl_dsi_regs.h"
#include "intel_atomic.h"
@@ -48,6 +47,7 @@
#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display_regs.h"
+#include "intel_display_utils.h"
#include "intel_dsi.h"
#include "intel_dsi_vbt.h"
#include "intel_panel.h"
@@ -148,9 +148,9 @@ static void wait_for_cmds_dispatched_to_panel(struct intel_encoder *encoder)
for_each_dsi_port(port, intel_dsi->ports) {
dsi_trans = dsi_port_to_transcoder(port);
- ret = intel_de_wait_custom(display, DSI_LP_MSG(dsi_trans),
- LPTX_IN_PROGRESS, 0,
- 20, 0, NULL);
+ ret = intel_de_wait_for_clear_us(display,
+ DSI_LP_MSG(dsi_trans),
+ LPTX_IN_PROGRESS, 20);
if (ret)
drm_err(display->drm, "LPTX bit not cleared\n");
}
@@ -534,9 +534,8 @@ static void gen11_dsi_enable_ddi_buffer(struct intel_encoder *encoder)
for_each_dsi_port(port, intel_dsi->ports) {
intel_de_rmw(display, DDI_BUF_CTL(port), 0, DDI_BUF_CTL_ENABLE);
- ret = intel_de_wait_custom(display, DDI_BUF_CTL(port),
- DDI_BUF_IS_IDLE, 0,
- 500, 0, NULL);
+ ret = intel_de_wait_for_clear_us(display, DDI_BUF_CTL(port),
+ DDI_BUF_IS_IDLE, 500);
if (ret)
drm_err(display->drm, "DDI port:%c buffer idle\n",
port_name(port));
@@ -857,9 +856,9 @@ gen11_dsi_configure_transcoder(struct intel_encoder *encoder,
dsi_trans = dsi_port_to_transcoder(port);
- ret = intel_de_wait_custom(display, DSI_TRANS_FUNC_CONF(dsi_trans),
- LINK_READY, LINK_READY,
- 2500, 0, NULL);
+ ret = intel_de_wait_for_set_us(display,
+ DSI_TRANS_FUNC_CONF(dsi_trans),
+ LINK_READY, 2500);
if (ret)
drm_err(display->drm, "DSI link not ready\n");
}
@@ -1048,8 +1047,8 @@ static void gen11_dsi_enable_transcoder(struct intel_encoder *encoder)
TRANSCONF_ENABLE);
/* wait for transcoder to be enabled */
- if (intel_de_wait_for_set(display, TRANSCONF(display, dsi_trans),
- TRANSCONF_STATE_ENABLE, 10))
+ if (intel_de_wait_for_set_ms(display, TRANSCONF(display, dsi_trans),
+ TRANSCONF_STATE_ENABLE, 10))
drm_err(display->drm,
"DSI transcoder not enabled\n");
}
@@ -1317,8 +1316,8 @@ static void gen11_dsi_disable_transcoder(struct intel_encoder *encoder)
TRANSCONF_ENABLE, 0);
/* wait for transcoder to be disabled */
- if (intel_de_wait_for_clear(display, TRANSCONF(display, dsi_trans),
- TRANSCONF_STATE_ENABLE, 50))
+ if (intel_de_wait_for_clear_ms(display, TRANSCONF(display, dsi_trans),
+ TRANSCONF_STATE_ENABLE, 50))
drm_err(display->drm,
"DSI trancoder not disabled\n");
}
@@ -1358,9 +1357,8 @@ static void gen11_dsi_deconfigure_trancoder(struct intel_encoder *encoder)
tmp &= ~LINK_ULPS_TYPE_LP11;
intel_de_write(display, DSI_LP_MSG(dsi_trans), tmp);
- ret = intel_de_wait_custom(display, DSI_LP_MSG(dsi_trans),
- LINK_IN_ULPS, LINK_IN_ULPS,
- 10, 0, NULL);
+ ret = intel_de_wait_for_set_us(display, DSI_LP_MSG(dsi_trans),
+ LINK_IN_ULPS, 10);
if (ret)
drm_err(display->drm, "DSI link not in ULPS\n");
}
@@ -1395,9 +1393,8 @@ static void gen11_dsi_disable_port(struct intel_encoder *encoder)
for_each_dsi_port(port, intel_dsi->ports) {
intel_de_rmw(display, DDI_BUF_CTL(port), DDI_BUF_CTL_ENABLE, 0);
- ret = intel_de_wait_custom(display, DDI_BUF_CTL(port),
- DDI_BUF_IS_IDLE, DDI_BUF_IS_IDLE,
- 8, 0, NULL);
+ ret = intel_de_wait_for_set_us(display, DDI_BUF_CTL(port),
+ DDI_BUF_IS_IDLE, 8);
if (ret)
drm_err(display->drm,
@@ -1655,7 +1652,7 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder,
if (ret)
return ret;
- crtc_state->dsc.compression_enable = true;
+ intel_dsc_enable_on_crtc(crtc_state);
return 0;
}
diff --git a/drivers/gpu/drm/i915/display/intel_acpi.c b/drivers/gpu/drm/i915/display/intel_acpi.c
index 1addd6288241..68c01932f7b4 100644
--- a/drivers/gpu/drm/i915/display/intel_acpi.c
+++ b/drivers/gpu/drm/i915/display/intel_acpi.c
@@ -11,10 +11,10 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_acpi.h"
#include "intel_display_core.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#define INTEL_DSM_REVISION_ID 1 /* For Calpella anyway... */
#define INTEL_DSM_FN_PLATFORM_MUX_INFO 1 /* No args */
diff --git a/drivers/gpu/drm/i915/display/intel_alpm.c b/drivers/gpu/drm/i915/display/intel_alpm.c
index ed7a7ed486b5..6372f533f65b 100644
--- a/drivers/gpu/drm/i915/display/intel_alpm.c
+++ b/drivers/gpu/drm/i915/display/intel_alpm.c
@@ -49,7 +49,7 @@ void intel_alpm_init(struct intel_dp *intel_dp)
return;
intel_dp->alpm_dpcd = dpcd;
- mutex_init(&intel_dp->alpm_parameters.lock);
+ mutex_init(&intel_dp->alpm.lock);
}
static int get_silence_period_symbols(const struct intel_crtc_state *crtc_state)
@@ -58,43 +58,32 @@ static int get_silence_period_symbols(const struct intel_crtc_state *crtc_state)
1000 / 1000;
}
-static int get_lfps_cycle_min_max_time(const struct intel_crtc_state *crtc_state,
- int *min, int *max)
+static void get_lfps_cycle_min_max_time(const struct intel_crtc_state *crtc_state,
+ int *min, int *max)
{
if (crtc_state->port_clock < 540000) {
*min = 65 * LFPS_CYCLE_COUNT;
*max = 75 * LFPS_CYCLE_COUNT;
- } else if (crtc_state->port_clock <= 810000) {
+ } else {
*min = 140;
*max = 800;
- } else {
- *min = *max = -1;
- return -1;
}
-
- return 0;
}
static int get_lfps_cycle_time(const struct intel_crtc_state *crtc_state)
{
- int tlfps_cycle_min, tlfps_cycle_max, ret;
+ int tlfps_cycle_min, tlfps_cycle_max;
- ret = get_lfps_cycle_min_max_time(crtc_state, &tlfps_cycle_min,
- &tlfps_cycle_max);
- if (ret)
- return ret;
+ get_lfps_cycle_min_max_time(crtc_state, &tlfps_cycle_min,
+ &tlfps_cycle_max);
return tlfps_cycle_min + (tlfps_cycle_max - tlfps_cycle_min) / 2;
}
static int get_lfps_half_cycle_clocks(const struct intel_crtc_state *crtc_state)
{
- int lfps_cycle_time = get_lfps_cycle_time(crtc_state);
-
- if (lfps_cycle_time < 0)
- return -1;
-
- return lfps_cycle_time * crtc_state->port_clock / 1000 / 1000 / (2 * LFPS_CYCLE_COUNT);
+ return get_lfps_cycle_time(crtc_state) * crtc_state->port_clock / 1000 /
+ 1000 / (2 * LFPS_CYCLE_COUNT);
}
/*
@@ -133,7 +122,7 @@ static int _lnl_compute_aux_less_wake_time(const struct intel_crtc_state *crtc_s
static int
_lnl_compute_aux_less_alpm_params(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state)
+ struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(intel_dp);
int aux_less_wake_time, aux_less_wake_lines, silence_period,
@@ -146,8 +135,6 @@ _lnl_compute_aux_less_alpm_params(struct intel_dp *intel_dp,
silence_period = get_silence_period_symbols(crtc_state);
lfps_half_cycle = get_lfps_half_cycle_clocks(crtc_state);
- if (lfps_half_cycle < 0)
- return false;
if (aux_less_wake_lines > ALPM_CTL_AUX_LESS_WAKE_TIME_MASK ||
silence_period > PORT_ALPM_CTL_SILENCE_PERIOD_MASK ||
@@ -157,15 +144,15 @@ _lnl_compute_aux_less_alpm_params(struct intel_dp *intel_dp,
if (display->params.psr_safest_params)
aux_less_wake_lines = ALPM_CTL_AUX_LESS_WAKE_TIME_MASK;
- intel_dp->alpm_parameters.aux_less_wake_lines = aux_less_wake_lines;
- intel_dp->alpm_parameters.silence_period_sym_clocks = silence_period;
- intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms = lfps_half_cycle;
+ crtc_state->alpm_state.aux_less_wake_lines = aux_less_wake_lines;
+ crtc_state->alpm_state.silence_period_sym_clocks = silence_period;
+ crtc_state->alpm_state.lfps_half_cycle_num_of_syms = lfps_half_cycle;
return true;
}
static bool _lnl_compute_alpm_params(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state)
+ struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(intel_dp);
int check_entry_lines;
@@ -186,7 +173,7 @@ static bool _lnl_compute_alpm_params(struct intel_dp *intel_dp,
if (display->params.psr_safest_params)
check_entry_lines = 15;
- intel_dp->alpm_parameters.check_entry_lines = check_entry_lines;
+ crtc_state->alpm_state.check_entry_lines = check_entry_lines;
return true;
}
@@ -217,7 +204,7 @@ static int io_buffer_wake_time(const struct intel_crtc_state *crtc_state)
}
bool intel_alpm_compute_params(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state)
+ struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(intel_dp);
int io_wake_lines, io_wake_time, fast_wake_lines, fast_wake_time;
@@ -255,8 +242,8 @@ bool intel_alpm_compute_params(struct intel_dp *intel_dp,
io_wake_lines = fast_wake_lines = max_wake_lines;
/* According to Bspec lower limit should be set as 7 lines. */
- intel_dp->alpm_parameters.io_wake_lines = max(io_wake_lines, 7);
- intel_dp->alpm_parameters.fast_wake_lines = max(fast_wake_lines, 7);
+ crtc_state->alpm_state.io_wake_lines = max(io_wake_lines, 7);
+ crtc_state->alpm_state.fast_wake_lines = max(fast_wake_lines, 7);
return true;
}
@@ -270,12 +257,12 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
int waketime_in_lines, first_sdp_position;
int context_latency, guardband;
- if (intel_dp->alpm_parameters.lobf_disable_debug) {
+ if (intel_dp->alpm.lobf_disable_debug) {
drm_dbg_kms(display->drm, "LOBF is disabled by debug flag\n");
return;
}
- if (intel_dp->alpm_parameters.sink_alpm_error)
+ if (intel_dp->alpm.sink_alpm_error)
return;
if (!intel_dp_is_edp(intel_dp))
@@ -306,9 +293,9 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
adjusted_mode->crtc_vdisplay - context_latency;
first_sdp_position = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start;
if (intel_alpm_aux_less_wake_supported(intel_dp))
- waketime_in_lines = intel_dp->alpm_parameters.io_wake_lines;
+ waketime_in_lines = crtc_state->alpm_state.io_wake_lines;
else
- waketime_in_lines = intel_dp->alpm_parameters.aux_less_wake_lines;
+ waketime_in_lines = crtc_state->alpm_state.aux_less_wake_lines;
crtc_state->has_lobf = (context_latency + guardband) >
(first_sdp_position + waketime_in_lines);
@@ -325,7 +312,7 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
!crtc_state->has_lobf))
return;
- mutex_lock(&intel_dp->alpm_parameters.lock);
+ mutex_lock(&intel_dp->alpm.lock);
/*
* Panel Replay on eDP is always using ALPM aux less. I.e. no need to
* check panel support at this point.
@@ -334,7 +321,7 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
alpm_ctl = ALPM_CTL_ALPM_ENABLE |
ALPM_CTL_ALPM_AUX_LESS_ENABLE |
ALPM_CTL_AUX_LESS_SLEEP_HOLD_TIME_50_SYMBOLS |
- ALPM_CTL_AUX_LESS_WAKE_TIME(intel_dp->alpm_parameters.aux_less_wake_lines);
+ ALPM_CTL_AUX_LESS_WAKE_TIME(crtc_state->alpm_state.aux_less_wake_lines);
if (intel_dp->as_sdp_supported) {
u32 pr_alpm_ctl = PR_ALPM_CTL_ADAPTIVE_SYNC_SDP_POSITION_T1;
@@ -352,7 +339,7 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
} else {
alpm_ctl = ALPM_CTL_EXTENDED_FAST_WAKE_ENABLE |
- ALPM_CTL_EXTENDED_FAST_WAKE_TIME(intel_dp->alpm_parameters.fast_wake_lines);
+ ALPM_CTL_EXTENDED_FAST_WAKE_TIME(crtc_state->alpm_state.fast_wake_lines);
}
if (crtc_state->has_lobf) {
@@ -360,17 +347,17 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp,
drm_dbg_kms(display->drm, "Link off between frames (LOBF) enabled\n");
}
- alpm_ctl |= ALPM_CTL_ALPM_ENTRY_CHECK(intel_dp->alpm_parameters.check_entry_lines);
+ alpm_ctl |= ALPM_CTL_ALPM_ENTRY_CHECK(crtc_state->alpm_state.check_entry_lines);
intel_de_write(display, ALPM_CTL(display, cpu_transcoder), alpm_ctl);
- mutex_unlock(&intel_dp->alpm_parameters.lock);
+ mutex_unlock(&intel_dp->alpm.lock);
}
void intel_alpm_configure(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state)
{
lnl_alpm_configure(intel_dp, crtc_state);
- intel_dp->alpm_parameters.transcoder = crtc_state->cpu_transcoder;
+ intel_dp->alpm.transcoder = crtc_state->cpu_transcoder;
}
void intel_alpm_port_configure(struct intel_dp *intel_dp,
@@ -388,14 +375,14 @@ void intel_alpm_port_configure(struct intel_dp *intel_dp,
PORT_ALPM_CTL_MAX_PHY_SWING_SETUP(15) |
PORT_ALPM_CTL_MAX_PHY_SWING_HOLD(0) |
PORT_ALPM_CTL_SILENCE_PERIOD(
- intel_dp->alpm_parameters.silence_period_sym_clocks);
+ crtc_state->alpm_state.silence_period_sym_clocks);
lfps_ctl_val = PORT_ALPM_LFPS_CTL_LFPS_CYCLE_COUNT(LFPS_CYCLE_COUNT) |
PORT_ALPM_LFPS_CTL_LFPS_HALF_CYCLE_DURATION(
- intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms) |
+ crtc_state->alpm_state.lfps_half_cycle_num_of_syms) |
PORT_ALPM_LFPS_CTL_FIRST_LFPS_HALF_CYCLE_DURATION(
- intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms) |
+ crtc_state->alpm_state.lfps_half_cycle_num_of_syms) |
PORT_ALPM_LFPS_CTL_LAST_LFPS_HALF_CYCLE_DURATION(
- intel_dp->alpm_parameters.lfps_half_cycle_num_of_syms);
+ crtc_state->alpm_state.lfps_half_cycle_num_of_syms);
}
intel_de_write(display, PORT_ALPM_CTL(port), alpm_ctl_val);
@@ -433,10 +420,10 @@ void intel_alpm_pre_plane_update(struct intel_atomic_state *state,
continue;
if (old_crtc_state->has_lobf) {
- mutex_lock(&intel_dp->alpm_parameters.lock);
+ mutex_lock(&intel_dp->alpm.lock);
intel_de_write(display, ALPM_CTL(display, cpu_transcoder), 0);
drm_dbg_kms(display->drm, "Link off between frames (LOBF) disabled\n");
- mutex_unlock(&intel_dp->alpm_parameters.lock);
+ mutex_unlock(&intel_dp->alpm.lock);
}
}
}
@@ -530,7 +517,7 @@ i915_edp_lobf_debug_get(void *data, u64 *val)
struct intel_connector *connector = data;
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
- *val = intel_dp->alpm_parameters.lobf_disable_debug;
+ *val = intel_dp->alpm.lobf_disable_debug;
return 0;
}
@@ -541,7 +528,7 @@ i915_edp_lobf_debug_set(void *data, u64 val)
struct intel_connector *connector = data;
struct intel_dp *intel_dp = enc_to_intel_dp(connector->encoder);
- intel_dp->alpm_parameters.lobf_disable_debug = val;
+ intel_dp->alpm.lobf_disable_debug = val;
return 0;
}
@@ -569,12 +556,12 @@ void intel_alpm_lobf_debugfs_add(struct intel_connector *connector)
void intel_alpm_disable(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
- enum transcoder cpu_transcoder = intel_dp->alpm_parameters.transcoder;
+ enum transcoder cpu_transcoder = intel_dp->alpm.transcoder;
if (DISPLAY_VER(display) < 20 || !intel_dp->alpm_dpcd)
return;
- mutex_lock(&intel_dp->alpm_parameters.lock);
+ mutex_lock(&intel_dp->alpm.lock);
intel_de_rmw(display, ALPM_CTL(display, cpu_transcoder),
ALPM_CTL_ALPM_ENABLE | ALPM_CTL_LOBF_ENABLE |
@@ -585,7 +572,7 @@ void intel_alpm_disable(struct intel_dp *intel_dp)
PORT_ALPM_CTL_ALPM_AUX_LESS_ENABLE, 0);
drm_dbg_kms(display->drm, "Disabling ALPM\n");
- mutex_unlock(&intel_dp->alpm_parameters.lock);
+ mutex_unlock(&intel_dp->alpm.lock);
}
bool intel_alpm_get_error(struct intel_dp *intel_dp)
diff --git a/drivers/gpu/drm/i915/display/intel_alpm.h b/drivers/gpu/drm/i915/display/intel_alpm.h
index a861c20b5d79..53599b464dea 100644
--- a/drivers/gpu/drm/i915/display/intel_alpm.h
+++ b/drivers/gpu/drm/i915/display/intel_alpm.h
@@ -17,7 +17,7 @@ struct intel_crtc;
void intel_alpm_init(struct intel_dp *intel_dp);
bool intel_alpm_compute_params(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state);
+ struct intel_crtc_state *crtc_state);
void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
diff --git a/drivers/gpu/drm/i915/display/intel_backlight.c b/drivers/gpu/drm/i915/display/intel_backlight.c
index 3b14f929825a..a68fdbd2acb9 100644
--- a/drivers/gpu/drm/i915/display/intel_backlight.c
+++ b/drivers/gpu/drm/i915/display/intel_backlight.c
@@ -13,7 +13,6 @@
#include <drm/drm_print.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_backlight.h"
#include "intel_backlight_regs.h"
#include "intel_connector.h"
@@ -21,6 +20,7 @@
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp_aux_backlight.h"
#include "intel_dsi_dcs_backlight.h"
#include "intel_panel.h"
diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index 3596dce84c28..4b41068e9e35 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -32,15 +32,15 @@
#include <drm/display/drm_dsc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_fixed.h>
+#include <drm/drm_print.h>
#include "soc/intel_rom.h"
-#include "i915_drv.h"
-#include "i915_utils.h"
#include "intel_display.h"
#include "intel_display_core.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_gmbus.h"
#define _INTEL_BIOS_PRIVATE
@@ -3144,7 +3144,6 @@ err_free_rom:
static const struct vbt_header *intel_bios_get_vbt(struct intel_display *display,
size_t *sizep)
{
- struct drm_i915_private *i915 = to_i915(display->drm);
const struct vbt_header *vbt = NULL;
vbt = firmware_get_vbt(display, sizep);
@@ -3158,11 +3157,11 @@ static const struct vbt_header *intel_bios_get_vbt(struct intel_display *display
*/
if (!vbt && display->platform.dgfx)
with_intel_display_rpm(display)
- vbt = oprom_get_vbt(display, intel_rom_spi(i915), sizep, "SPI flash");
+ vbt = oprom_get_vbt(display, intel_rom_spi(display->drm), sizep, "SPI flash");
if (!vbt)
with_intel_display_rpm(display)
- vbt = oprom_get_vbt(display, intel_rom_pci(i915), sizep, "PCI ROM");
+ vbt = oprom_get_vbt(display, intel_rom_pci(display->drm), sizep, "PCI ROM");
return vbt;
}
diff --git a/drivers/gpu/drm/i915/display/intel_bo.c b/drivers/gpu/drm/i915/display/intel_bo.c
index 6ae1374d5c2b..f3687eb63467 100644
--- a/drivers/gpu/drm/i915/display/intel_bo.c
+++ b/drivers/gpu/drm/i915/display/intel_bo.c
@@ -29,11 +29,6 @@ bool intel_bo_is_protected(struct drm_gem_object *obj)
return i915_gem_object_is_protected(to_intel_bo(obj));
}
-void intel_bo_flush_if_display(struct drm_gem_object *obj)
-{
- i915_gem_object_flush_if_display(to_intel_bo(obj));
-}
-
int intel_bo_fb_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
return i915_gem_fb_mmap(to_intel_bo(obj), vma);
@@ -44,15 +39,40 @@ int intel_bo_read_from_page(struct drm_gem_object *obj, u64 offset, void *dst, i
return i915_gem_object_read_from_page(to_intel_bo(obj), offset, dst, size);
}
-struct intel_frontbuffer *intel_bo_get_frontbuffer(struct drm_gem_object *obj)
+struct intel_frontbuffer *intel_bo_frontbuffer_get(struct drm_gem_object *_obj)
+{
+ struct drm_i915_gem_object *obj = to_intel_bo(_obj);
+ struct i915_frontbuffer *front;
+
+ front = i915_gem_object_frontbuffer_get(obj);
+ if (!front)
+ return NULL;
+
+ return &front->base;
+}
+
+void intel_bo_frontbuffer_ref(struct intel_frontbuffer *_front)
{
- return i915_gem_object_get_frontbuffer(to_intel_bo(obj));
+ struct i915_frontbuffer *front =
+ container_of(_front, typeof(*front), base);
+
+ i915_gem_object_frontbuffer_ref(front);
}
-struct intel_frontbuffer *intel_bo_set_frontbuffer(struct drm_gem_object *obj,
- struct intel_frontbuffer *front)
+void intel_bo_frontbuffer_put(struct intel_frontbuffer *_front)
{
- return i915_gem_object_set_frontbuffer(to_intel_bo(obj), front);
+ struct i915_frontbuffer *front =
+ container_of(_front, typeof(*front), base);
+
+ return i915_gem_object_frontbuffer_put(front);
+}
+
+void intel_bo_frontbuffer_flush_for_display(struct intel_frontbuffer *_front)
+{
+ struct i915_frontbuffer *front =
+ container_of(_front, typeof(*front), base);
+
+ i915_gem_object_flush_if_display(front->obj);
}
void intel_bo_describe(struct seq_file *m, struct drm_gem_object *obj)
diff --git a/drivers/gpu/drm/i915/display/intel_bo.h b/drivers/gpu/drm/i915/display/intel_bo.h
index 48d87019e48a..fc05f680dc76 100644
--- a/drivers/gpu/drm/i915/display/intel_bo.h
+++ b/drivers/gpu/drm/i915/display/intel_bo.h
@@ -16,13 +16,13 @@ bool intel_bo_is_tiled(struct drm_gem_object *obj);
bool intel_bo_is_userptr(struct drm_gem_object *obj);
bool intel_bo_is_shmem(struct drm_gem_object *obj);
bool intel_bo_is_protected(struct drm_gem_object *obj);
-void intel_bo_flush_if_display(struct drm_gem_object *obj);
int intel_bo_fb_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
int intel_bo_read_from_page(struct drm_gem_object *obj, u64 offset, void *dst, int size);
-struct intel_frontbuffer *intel_bo_get_frontbuffer(struct drm_gem_object *obj);
-struct intel_frontbuffer *intel_bo_set_frontbuffer(struct drm_gem_object *obj,
- struct intel_frontbuffer *front);
+struct intel_frontbuffer *intel_bo_frontbuffer_get(struct drm_gem_object *obj);
+void intel_bo_frontbuffer_ref(struct intel_frontbuffer *front);
+void intel_bo_frontbuffer_put(struct intel_frontbuffer *front);
+void intel_bo_frontbuffer_flush_for_display(struct intel_frontbuffer *front);
void intel_bo_describe(struct seq_file *m, struct drm_gem_object *obj);
diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c
index ac6da20d9529..1f6461be50ef 100644
--- a/drivers/gpu/drm/i915/display/intel_bw.c
+++ b/drivers/gpu/drm/i915/display/intel_bw.c
@@ -4,31 +4,25 @@
*/
#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_print.h>
#include "soc/intel_dram.h"
#include "i915_drv.h"
#include "i915_reg.h"
-#include "i915_utils.h"
-#include "intel_atomic.h"
#include "intel_bw.h"
-#include "intel_cdclk.h"
+#include "intel_crtc.h"
#include "intel_display_core.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_mchbar_regs.h"
#include "intel_pcode.h"
#include "intel_uncore.h"
#include "skl_watermark.h"
-struct intel_dbuf_bw {
- unsigned int max_bw[I915_MAX_DBUF_SLICES];
- u8 active_planes[I915_MAX_DBUF_SLICES];
-};
-
struct intel_bw_state {
struct intel_global_state base;
- struct intel_dbuf_bw dbuf_bw[I915_MAX_PIPES];
/*
* Contains a bit mask, used to determine, whether correspondent
@@ -811,72 +805,40 @@ void intel_bw_init_hw(struct intel_display *display)
if (!HAS_DISPLAY(display))
return;
- if (DISPLAY_VERx100(display) >= 3002)
- tgl_get_bw_info(display, dram_info, &xe3lpd_3002_sa_info);
- else if (DISPLAY_VER(display) >= 30)
- tgl_get_bw_info(display, dram_info, &xe3lpd_sa_info);
- else if (DISPLAY_VERx100(display) >= 1401 && display->platform.dgfx &&
- dram_info->type == INTEL_DRAM_GDDR_ECC)
- xe2_hpd_get_bw_info(display, dram_info, &xe2_hpd_ecc_sa_info);
- else if (DISPLAY_VERx100(display) >= 1401 && display->platform.dgfx)
- xe2_hpd_get_bw_info(display, dram_info, &xe2_hpd_sa_info);
- else if (DISPLAY_VER(display) >= 14)
+ /*
+ * Starting with Xe3p_LPD, the hardware tells us whether memory has ECC
+ * enabled that would impact display bandwidth. However, so far there
+ * are no instructions in Bspec on how to handle that case. Let's
+ * complain if we ever find such a scenario.
+ */
+ if (DISPLAY_VER(display) >= 35)
+ drm_WARN_ON(display->drm, dram_info->ecc_impacting_de_bw);
+
+ if (DISPLAY_VER(display) >= 30) {
+ if (DISPLAY_VERx100(display) == 3002)
+ tgl_get_bw_info(display, dram_info, &xe3lpd_3002_sa_info);
+ else
+ tgl_get_bw_info(display, dram_info, &xe3lpd_sa_info);
+ } else if (DISPLAY_VERx100(display) >= 1401 && display->platform.dgfx) {
+ if (dram_info->type == INTEL_DRAM_GDDR_ECC)
+ xe2_hpd_get_bw_info(display, dram_info, &xe2_hpd_ecc_sa_info);
+ else
+ xe2_hpd_get_bw_info(display, dram_info, &xe2_hpd_sa_info);
+ } else if (DISPLAY_VER(display) >= 14) {
tgl_get_bw_info(display, dram_info, &mtl_sa_info);
- else if (display->platform.dg2)
+ } else if (display->platform.dg2) {
dg2_get_bw_info(display);
- else if (display->platform.alderlake_p)
+ } else if (display->platform.alderlake_p) {
tgl_get_bw_info(display, dram_info, &adlp_sa_info);
- else if (display->platform.alderlake_s)
+ } else if (display->platform.alderlake_s) {
tgl_get_bw_info(display, dram_info, &adls_sa_info);
- else if (display->platform.rocketlake)
+ } else if (display->platform.rocketlake) {
tgl_get_bw_info(display, dram_info, &rkl_sa_info);
- else if (DISPLAY_VER(display) == 12)
+ } else if (DISPLAY_VER(display) == 12) {
tgl_get_bw_info(display, dram_info, &tgl_sa_info);
- else if (DISPLAY_VER(display) == 11)
+ } else if (DISPLAY_VER(display) == 11) {
icl_get_bw_info(display, dram_info, &icl_sa_info);
-}
-
-static unsigned int intel_bw_crtc_num_active_planes(const struct intel_crtc_state *crtc_state)
-{
- /*
- * We assume cursors are small enough
- * to not not cause bandwidth problems.
- */
- return hweight8(crtc_state->active_planes & ~BIT(PLANE_CURSOR));
-}
-
-static unsigned int intel_bw_crtc_data_rate(const struct intel_crtc_state *crtc_state)
-{
- struct intel_display *display = to_intel_display(crtc_state);
- struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- unsigned int data_rate = 0;
- enum plane_id plane_id;
-
- for_each_plane_id_on_crtc(crtc, plane_id) {
- /*
- * We assume cursors are small enough
- * to not not cause bandwidth problems.
- */
- if (plane_id == PLANE_CURSOR)
- continue;
-
- data_rate += crtc_state->data_rate[plane_id];
-
- if (DISPLAY_VER(display) < 11)
- data_rate += crtc_state->data_rate_y[plane_id];
}
-
- return data_rate;
-}
-
-/* "Maximum Pipe Read Bandwidth" */
-static int intel_bw_crtc_min_cdclk(struct intel_display *display,
- unsigned int data_rate)
-{
- if (DISPLAY_VER(display) < 12)
- return 0;
-
- return DIV_ROUND_UP_ULL(mul_u32_u32(data_rate, 10), 512);
}
static unsigned int intel_bw_num_active_planes(struct intel_display *display,
@@ -894,14 +856,13 @@ static unsigned int intel_bw_num_active_planes(struct intel_display *display,
static unsigned int intel_bw_data_rate(struct intel_display *display,
const struct intel_bw_state *bw_state)
{
- struct drm_i915_private *i915 = to_i915(display->drm);
unsigned int data_rate = 0;
enum pipe pipe;
for_each_pipe(display, pipe)
data_rate += bw_state->data_rate[pipe];
- if (DISPLAY_VER(display) >= 13 && i915_vtd_active(i915))
+ if (DISPLAY_VER(display) >= 13 && intel_display_vtd_active(display))
data_rate = DIV_ROUND_UP(data_rate * 105, 100);
return data_rate;
@@ -1262,223 +1223,6 @@ static int intel_bw_check_qgv_points(struct intel_display *display,
old_bw_state, new_bw_state);
}
-static bool intel_dbuf_bw_changed(struct intel_display *display,
- const struct intel_dbuf_bw *old_dbuf_bw,
- const struct intel_dbuf_bw *new_dbuf_bw)
-{
- enum dbuf_slice slice;
-
- for_each_dbuf_slice(display, slice) {
- if (old_dbuf_bw->max_bw[slice] != new_dbuf_bw->max_bw[slice] ||
- old_dbuf_bw->active_planes[slice] != new_dbuf_bw->active_planes[slice])
- return true;
- }
-
- return false;
-}
-
-static bool intel_bw_state_changed(struct intel_display *display,
- const struct intel_bw_state *old_bw_state,
- const struct intel_bw_state *new_bw_state)
-{
- enum pipe pipe;
-
- for_each_pipe(display, pipe) {
- const struct intel_dbuf_bw *old_dbuf_bw =
- &old_bw_state->dbuf_bw[pipe];
- const struct intel_dbuf_bw *new_dbuf_bw =
- &new_bw_state->dbuf_bw[pipe];
-
- if (intel_dbuf_bw_changed(display, old_dbuf_bw, new_dbuf_bw))
- return true;
-
- if (intel_bw_crtc_min_cdclk(display, old_bw_state->data_rate[pipe]) !=
- intel_bw_crtc_min_cdclk(display, new_bw_state->data_rate[pipe]))
- return true;
- }
-
- return false;
-}
-
-static void skl_plane_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
- struct intel_crtc *crtc,
- enum plane_id plane_id,
- const struct skl_ddb_entry *ddb,
- unsigned int data_rate)
-{
- struct intel_display *display = to_intel_display(crtc);
- unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(display, ddb);
- enum dbuf_slice slice;
-
- /*
- * The arbiter can only really guarantee an
- * equal share of the total bw to each plane.
- */
- for_each_dbuf_slice_in_mask(display, slice, dbuf_mask) {
- dbuf_bw->max_bw[slice] = max(dbuf_bw->max_bw[slice], data_rate);
- dbuf_bw->active_planes[slice] |= BIT(plane_id);
- }
-}
-
-static void skl_crtc_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
- const struct intel_crtc_state *crtc_state)
-{
- struct intel_display *display = to_intel_display(crtc_state);
- struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- enum plane_id plane_id;
-
- memset(dbuf_bw, 0, sizeof(*dbuf_bw));
-
- if (!crtc_state->hw.active)
- return;
-
- for_each_plane_id_on_crtc(crtc, plane_id) {
- /*
- * We assume cursors are small enough
- * to not cause bandwidth problems.
- */
- if (plane_id == PLANE_CURSOR)
- continue;
-
- skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
- &crtc_state->wm.skl.plane_ddb[plane_id],
- crtc_state->data_rate[plane_id]);
-
- if (DISPLAY_VER(display) < 11)
- skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
- &crtc_state->wm.skl.plane_ddb_y[plane_id],
- crtc_state->data_rate[plane_id]);
- }
-}
-
-/* "Maximum Data Buffer Bandwidth" */
-static int
-intel_bw_dbuf_min_cdclk(struct intel_display *display,
- const struct intel_bw_state *bw_state)
-{
- unsigned int total_max_bw = 0;
- enum dbuf_slice slice;
-
- for_each_dbuf_slice(display, slice) {
- int num_active_planes = 0;
- unsigned int max_bw = 0;
- enum pipe pipe;
-
- /*
- * The arbiter can only really guarantee an
- * equal share of the total bw to each plane.
- */
- for_each_pipe(display, pipe) {
- const struct intel_dbuf_bw *dbuf_bw = &bw_state->dbuf_bw[pipe];
-
- max_bw = max(dbuf_bw->max_bw[slice], max_bw);
- num_active_planes += hweight8(dbuf_bw->active_planes[slice]);
- }
- max_bw *= num_active_planes;
-
- total_max_bw = max(total_max_bw, max_bw);
- }
-
- return DIV_ROUND_UP(total_max_bw, 64);
-}
-
-int intel_bw_min_cdclk(struct intel_display *display,
- const struct intel_bw_state *bw_state)
-{
- enum pipe pipe;
- int min_cdclk;
-
- min_cdclk = intel_bw_dbuf_min_cdclk(display, bw_state);
-
- for_each_pipe(display, pipe)
- min_cdclk = max(min_cdclk,
- intel_bw_crtc_min_cdclk(display,
- bw_state->data_rate[pipe]));
-
- return min_cdclk;
-}
-
-int intel_bw_calc_min_cdclk(struct intel_atomic_state *state,
- bool *need_cdclk_calc)
-{
- struct intel_display *display = to_intel_display(state);
- struct intel_bw_state *new_bw_state = NULL;
- const struct intel_bw_state *old_bw_state = NULL;
- const struct intel_cdclk_state *cdclk_state;
- const struct intel_crtc_state *old_crtc_state;
- const struct intel_crtc_state *new_crtc_state;
- int old_min_cdclk, new_min_cdclk;
- struct intel_crtc *crtc;
- int i;
-
- if (DISPLAY_VER(display) < 9)
- return 0;
-
- for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
- new_crtc_state, i) {
- struct intel_dbuf_bw old_dbuf_bw, new_dbuf_bw;
-
- skl_crtc_calc_dbuf_bw(&old_dbuf_bw, old_crtc_state);
- skl_crtc_calc_dbuf_bw(&new_dbuf_bw, new_crtc_state);
-
- if (!intel_dbuf_bw_changed(display, &old_dbuf_bw, &new_dbuf_bw))
- continue;
-
- new_bw_state = intel_atomic_get_bw_state(state);
- if (IS_ERR(new_bw_state))
- return PTR_ERR(new_bw_state);
-
- old_bw_state = intel_atomic_get_old_bw_state(state);
-
- new_bw_state->dbuf_bw[crtc->pipe] = new_dbuf_bw;
- }
-
- if (!old_bw_state)
- return 0;
-
- if (intel_bw_state_changed(display, old_bw_state, new_bw_state)) {
- int ret = intel_atomic_lock_global_state(&new_bw_state->base);
- if (ret)
- return ret;
- }
-
- old_min_cdclk = intel_bw_min_cdclk(display, old_bw_state);
- new_min_cdclk = intel_bw_min_cdclk(display, new_bw_state);
-
- /*
- * No need to check against the cdclk state if
- * the min cdclk doesn't increase.
- *
- * Ie. we only ever increase the cdclk due to bandwidth
- * requirements. This can reduce back and forth
- * display blinking due to constant cdclk changes.
- */
- if (new_min_cdclk <= old_min_cdclk)
- return 0;
-
- cdclk_state = intel_atomic_get_cdclk_state(state);
- if (IS_ERR(cdclk_state))
- return PTR_ERR(cdclk_state);
-
- /*
- * No need to recalculate the cdclk state if
- * the min cdclk doesn't increase.
- *
- * Ie. we only ever increase the cdclk due to bandwidth
- * requirements. This can reduce back and forth
- * display blinking due to constant cdclk changes.
- */
- if (new_min_cdclk <= intel_cdclk_bw_min_cdclk(cdclk_state))
- return 0;
-
- drm_dbg_kms(display->drm,
- "new bandwidth min cdclk (%d kHz) > old min cdclk (%d kHz)\n",
- new_min_cdclk, intel_cdclk_bw_min_cdclk(cdclk_state));
- *need_cdclk_calc = true;
-
- return 0;
-}
-
static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *changed)
{
struct intel_display *display = to_intel_display(state);
@@ -1489,13 +1233,13 @@ static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *chan
for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
new_crtc_state, i) {
unsigned int old_data_rate =
- intel_bw_crtc_data_rate(old_crtc_state);
+ intel_crtc_bw_data_rate(old_crtc_state);
unsigned int new_data_rate =
- intel_bw_crtc_data_rate(new_crtc_state);
+ intel_crtc_bw_data_rate(new_crtc_state);
unsigned int old_active_planes =
- intel_bw_crtc_num_active_planes(old_crtc_state);
+ intel_crtc_bw_num_active_planes(old_crtc_state);
unsigned int new_active_planes =
- intel_bw_crtc_num_active_planes(new_crtc_state);
+ intel_crtc_bw_num_active_planes(new_crtc_state);
struct intel_bw_state *new_bw_state;
/*
@@ -1527,11 +1271,11 @@ static int intel_bw_check_data_rate(struct intel_atomic_state *state, bool *chan
static int intel_bw_modeset_checks(struct intel_atomic_state *state)
{
- struct intel_display *display = to_intel_display(state);
const struct intel_bw_state *old_bw_state;
struct intel_bw_state *new_bw_state;
+ int ret;
- if (DISPLAY_VER(display) < 9)
+ if (!intel_any_crtc_active_changed(state))
return 0;
new_bw_state = intel_atomic_get_bw_state(state);
@@ -1543,13 +1287,9 @@ static int intel_bw_modeset_checks(struct intel_atomic_state *state)
new_bw_state->active_pipes =
intel_calc_active_pipes(state, old_bw_state->active_pipes);
- if (new_bw_state->active_pipes != old_bw_state->active_pipes) {
- int ret;
-
- ret = intel_atomic_lock_global_state(&new_bw_state->base);
- if (ret)
- return ret;
- }
+ ret = intel_atomic_lock_global_state(&new_bw_state->base);
+ if (ret)
+ return ret;
return 0;
}
@@ -1599,7 +1339,7 @@ static int intel_bw_check_sagv_mask(struct intel_atomic_state *state)
return 0;
}
-int intel_bw_atomic_check(struct intel_atomic_state *state, bool any_ms)
+int intel_bw_atomic_check(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
bool changed = false;
@@ -1610,11 +1350,9 @@ int intel_bw_atomic_check(struct intel_atomic_state *state, bool any_ms)
if (DISPLAY_VER(display) < 9)
return 0;
- if (any_ms) {
- ret = intel_bw_modeset_checks(state);
- if (ret)
- return ret;
- }
+ ret = intel_bw_modeset_checks(state);
+ if (ret)
+ return ret;
ret = intel_bw_check_sagv_mask(state);
if (ret)
@@ -1657,9 +1395,9 @@ static void intel_bw_crtc_update(struct intel_bw_state *bw_state,
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
bw_state->data_rate[crtc->pipe] =
- intel_bw_crtc_data_rate(crtc_state);
+ intel_crtc_bw_data_rate(crtc_state);
bw_state->num_active_planes[crtc->pipe] =
- intel_bw_crtc_num_active_planes(crtc_state);
+ intel_crtc_bw_num_active_planes(crtc_state);
drm_dbg_kms(display->drm, "pipe %c data rate %u num active planes %u\n",
pipe_name(crtc->pipe),
@@ -1690,8 +1428,6 @@ void intel_bw_update_hw_state(struct intel_display *display)
if (DISPLAY_VER(display) >= 11)
intel_bw_crtc_update(bw_state, crtc_state);
- skl_crtc_calc_dbuf_bw(&bw_state->dbuf_bw[pipe], crtc_state);
-
/* initially SAGV has been forced off */
bw_state->pipe_sagv_reject |= BIT(pipe);
}
@@ -1709,7 +1445,6 @@ void intel_bw_crtc_disable_noatomic(struct intel_crtc *crtc)
bw_state->data_rate[pipe] = 0;
bw_state->num_active_planes[pipe] = 0;
- memset(&bw_state->dbuf_bw[pipe], 0, sizeof(bw_state->dbuf_bw[pipe]));
}
static struct intel_global_state *
diff --git a/drivers/gpu/drm/i915/display/intel_bw.h b/drivers/gpu/drm/i915/display/intel_bw.h
index d51f50c9d302..99b447388245 100644
--- a/drivers/gpu/drm/i915/display/intel_bw.h
+++ b/drivers/gpu/drm/i915/display/intel_bw.h
@@ -28,11 +28,7 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state);
void intel_bw_init_hw(struct intel_display *display);
int intel_bw_init(struct intel_display *display);
-int intel_bw_atomic_check(struct intel_atomic_state *state, bool any_ms);
-int intel_bw_calc_min_cdclk(struct intel_atomic_state *state,
- bool *need_cdclk_calc);
-int intel_bw_min_cdclk(struct intel_display *display,
- const struct intel_bw_state *bw_state);
+int intel_bw_atomic_check(struct intel_atomic_state *state);
void intel_bw_update_hw_state(struct intel_display *display);
void intel_bw_crtc_disable_noatomic(struct intel_crtc *crtc);
diff --git a/drivers/gpu/drm/i915/display/intel_casf.c b/drivers/gpu/drm/i915/display/intel_casf.c
new file mode 100644
index 000000000000..95339b496f24
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_casf.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: MIT
+/* Copyright © 2025 Intel Corporation */
+
+#include <drm/drm_print.h>
+
+#include "i915_reg.h"
+#include "intel_casf.h"
+#include "intel_casf_regs.h"
+#include "intel_de.h"
+#include "intel_display_regs.h"
+#include "intel_display_types.h"
+#include "skl_scaler.h"
+
+#define MAX_PIXELS_FOR_3_TAP_FILTER (1920 * 1080)
+#define MAX_PIXELS_FOR_5_TAP_FILTER (3840 * 2160)
+
+#define FILTER_COEFF_0_125 125
+#define FILTER_COEFF_0_25 250
+#define FILTER_COEFF_0_5 500
+#define FILTER_COEFF_1_0 1000
+#define FILTER_COEFF_0_0 0
+#define SET_POSITIVE_SIGN(x) ((x) & (~SIGN))
+
+/**
+ * DOC: Content Adaptive Sharpness Filter (CASF)
+ *
+ * Starting from LNL the display engine supports an
+ * adaptive sharpening filter, enhancing the image
+ * quality. The display hardware utilizes the second
+ * pipe scaler for implementing CASF.
+ * If sharpness is being enabled then pipe scaling
+ * cannot be used.
+ * This filter operates on a region of pixels based
+ * on the tap size. Coefficients are used to generate
+ * an alpha value which blends the sharpened image to
+ * original image.
+ */
+
+/* Default LUT values to be loaded one time. */
+static const u16 sharpness_lut[] = {
+ 4095, 2047, 1364, 1022, 816, 678, 579,
+ 504, 444, 397, 357, 323, 293, 268, 244, 224,
+ 204, 187, 170, 154, 139, 125, 111, 98, 85,
+ 73, 60, 48, 36, 24, 12, 0
+};
+
+const u16 filtercoeff_1[] = {
+ FILTER_COEFF_0_0, FILTER_COEFF_0_0, FILTER_COEFF_0_5,
+ FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_0,
+ FILTER_COEFF_0_0,
+};
+
+const u16 filtercoeff_2[] = {
+ FILTER_COEFF_0_0, FILTER_COEFF_0_25, FILTER_COEFF_0_5,
+ FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_25,
+ FILTER_COEFF_0_0,
+};
+
+const u16 filtercoeff_3[] = {
+ FILTER_COEFF_0_125, FILTER_COEFF_0_25, FILTER_COEFF_0_5,
+ FILTER_COEFF_1_0, FILTER_COEFF_0_5, FILTER_COEFF_0_25,
+ FILTER_COEFF_0_125,
+};
+
+static void intel_casf_filter_lut_load(struct intel_crtc *crtc,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int i;
+
+ intel_de_write(display, SHRPLUT_INDEX(crtc->pipe),
+ INDEX_AUTO_INCR | INDEX_VALUE(0));
+
+ for (i = 0; i < ARRAY_SIZE(sharpness_lut); i++)
+ intel_de_write(display, SHRPLUT_DATA(crtc->pipe),
+ sharpness_lut[i]);
+}
+
+void intel_casf_update_strength(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ int win_size;
+
+ intel_de_rmw(display, SHARPNESS_CTL(crtc->pipe), FILTER_STRENGTH_MASK,
+ FILTER_STRENGTH(crtc_state->hw.casf_params.strength));
+
+ win_size = intel_de_read(display, SKL_PS_WIN_SZ(crtc->pipe, 1));
+
+ intel_de_write_fw(display, SKL_PS_WIN_SZ(crtc->pipe, 1), win_size);
+}
+
+static void intel_casf_compute_win_size(struct intel_crtc_state *crtc_state)
+{
+ const struct drm_display_mode *mode = &crtc_state->hw.adjusted_mode;
+ u32 total_pixels = mode->hdisplay * mode->vdisplay;
+
+ if (total_pixels <= MAX_PIXELS_FOR_3_TAP_FILTER)
+ crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_3X3;
+ else if (total_pixels <= MAX_PIXELS_FOR_5_TAP_FILTER)
+ crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_5X5;
+ else
+ crtc_state->hw.casf_params.win_size = SHARPNESS_FILTER_SIZE_7X7;
+}
+
+int intel_casf_compute_config(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ if (!HAS_CASF(display))
+ return 0;
+
+ if (crtc_state->uapi.sharpness_strength == 0) {
+ crtc_state->hw.casf_params.casf_enable = false;
+ crtc_state->hw.casf_params.strength = 0;
+ return 0;
+ }
+
+ crtc_state->hw.casf_params.casf_enable = true;
+
+ /*
+ * HW takes a value in form (1.0 + strength) in 4.4 fixed format.
+ * Strength is from 0.0-14.9375 ie from 0-239.
+ * User can give value from 0-255 but is clamped to 239.
+ * Ex. User gives 85 which is 5.3125 and adding 1.0 gives 6.3125.
+ * 6.3125 in 4.4 format is b01100101 which is equal to 101.
+ * Also 85 + 16 = 101.
+ */
+ crtc_state->hw.casf_params.strength =
+ min(crtc_state->uapi.sharpness_strength, 0xEF) + 0x10;
+
+ intel_casf_compute_win_size(crtc_state);
+
+ intel_casf_scaler_compute_config(crtc_state);
+
+ return 0;
+}
+
+void intel_casf_sharpness_get_config(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ u32 sharp;
+
+ sharp = intel_de_read(display, SHARPNESS_CTL(crtc->pipe));
+ if (sharp & FILTER_EN) {
+ if (drm_WARN_ON(display->drm,
+ REG_FIELD_GET(FILTER_STRENGTH_MASK, sharp) < 16))
+ crtc_state->hw.casf_params.strength = 0;
+ else
+ crtc_state->hw.casf_params.strength =
+ REG_FIELD_GET(FILTER_STRENGTH_MASK, sharp);
+ crtc_state->hw.casf_params.casf_enable = true;
+ crtc_state->hw.casf_params.win_size =
+ REG_FIELD_GET(FILTER_SIZE_MASK, sharp);
+ }
+}
+
+bool intel_casf_needs_scaler(const struct intel_crtc_state *crtc_state)
+{
+ if (crtc_state->hw.casf_params.casf_enable)
+ return true;
+
+ return false;
+}
+
+static int casf_coeff_tap(int i)
+{
+ return i % SCALER_FILTER_NUM_TAPS;
+}
+
+static u32 casf_coeff(struct intel_crtc_state *crtc_state, int t)
+{
+ struct scaler_filter_coeff value;
+ u32 coeff;
+
+ value = crtc_state->hw.casf_params.coeff[t];
+ value.sign = 0;
+
+ coeff = value.sign << 15 | value.exp << 12 | value.mantissa << 3;
+ return coeff;
+}
+
+/*
+ * 17 phase of 7 taps requires 119 coefficients in 60 dwords per set.
+ * To enable casf: program scaler coefficients with the coeffients
+ * that are calculated and stored in hw.casf_params.coeff as per
+ * SCALER_COEFFICIENT_FORMAT
+ */
+static void intel_casf_write_coeff(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ int id = crtc_state->scaler_state.scaler_id;
+ int i;
+
+ if (id != 1) {
+ drm_WARN(display->drm, 0, "Second scaler not enabled\n");
+ return;
+ }
+
+ intel_de_write_fw(display, GLK_PS_COEF_INDEX_SET(crtc->pipe, id, 0),
+ PS_COEF_INDEX_AUTO_INC);
+
+ for (i = 0; i < 17 * SCALER_FILTER_NUM_TAPS; i += 2) {
+ u32 tmp;
+ int t;
+
+ t = casf_coeff_tap(i);
+ tmp = casf_coeff(crtc_state, t);
+
+ t = casf_coeff_tap(i + 1);
+ tmp |= casf_coeff(crtc_state, t) << 16;
+
+ intel_de_write_fw(display, GLK_PS_COEF_DATA_SET(crtc->pipe, id, 0),
+ tmp);
+ }
+}
+
+static void convert_sharpness_coef_binary(struct scaler_filter_coeff *coeff,
+ u16 coefficient)
+{
+ if (coefficient < 25) {
+ coeff->mantissa = (coefficient * 2048) / 100;
+ coeff->exp = 3;
+ } else if (coefficient < 50) {
+ coeff->mantissa = (coefficient * 1024) / 100;
+ coeff->exp = 2;
+ } else if (coefficient < 100) {
+ coeff->mantissa = (coefficient * 512) / 100;
+ coeff->exp = 1;
+ } else {
+ coeff->mantissa = (coefficient * 256) / 100;
+ coeff->exp = 0;
+ }
+}
+
+void intel_casf_scaler_compute_config(struct intel_crtc_state *crtc_state)
+{
+ const u16 *filtercoeff;
+ u16 filter_coeff[SCALER_FILTER_NUM_TAPS];
+ u16 sumcoeff = 0;
+ int i;
+
+ if (crtc_state->hw.casf_params.win_size == 0)
+ filtercoeff = filtercoeff_1;
+ else if (crtc_state->hw.casf_params.win_size == 1)
+ filtercoeff = filtercoeff_2;
+ else
+ filtercoeff = filtercoeff_3;
+
+ for (i = 0; i < SCALER_FILTER_NUM_TAPS; i++)
+ sumcoeff += *(filtercoeff + i);
+
+ for (i = 0; i < SCALER_FILTER_NUM_TAPS; i++) {
+ filter_coeff[i] = (*(filtercoeff + i) * 100 / sumcoeff);
+ convert_sharpness_coef_binary(&crtc_state->hw.casf_params.coeff[i],
+ filter_coeff[i]);
+ }
+}
+
+void intel_casf_enable(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ u32 sharpness_ctl;
+
+ intel_casf_filter_lut_load(crtc, crtc_state);
+
+ intel_casf_write_coeff(crtc_state);
+
+ sharpness_ctl = FILTER_EN | FILTER_STRENGTH(crtc_state->hw.casf_params.strength);
+
+ sharpness_ctl |= crtc_state->hw.casf_params.win_size;
+
+ intel_de_write(display, SHARPNESS_CTL(crtc->pipe), sharpness_ctl);
+
+ skl_scaler_setup_casf(crtc_state);
+}
+
+void intel_casf_disable(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ intel_de_write(display, SKL_PS_CTRL(crtc->pipe, 1), 0);
+ intel_de_write(display, SKL_PS_WIN_POS(crtc->pipe, 1), 0);
+ intel_de_write(display, SHARPNESS_CTL(crtc->pipe), 0);
+ intel_de_write(display, SKL_PS_WIN_SZ(crtc->pipe, 1), 0);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_casf.h b/drivers/gpu/drm/i915/display/intel_casf.h
new file mode 100644
index 000000000000..b3fb0bcb3f5b
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_casf.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __INTEL_CASF_H__
+#define __INTEL_CASF_H__
+
+#include <linux/types.h>
+
+struct intel_crtc_state;
+
+int intel_casf_compute_config(struct intel_crtc_state *crtc_state);
+void intel_casf_update_strength(struct intel_crtc_state *new_crtc_state);
+void intel_casf_sharpness_get_config(struct intel_crtc_state *crtc_state);
+void intel_casf_enable(struct intel_crtc_state *crtc_state);
+void intel_casf_disable(const struct intel_crtc_state *crtc_state);
+void intel_casf_scaler_compute_config(struct intel_crtc_state *crtc_state);
+bool intel_casf_needs_scaler(const struct intel_crtc_state *crtc_state);
+
+#endif /* __INTEL_CASF_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_casf_regs.h b/drivers/gpu/drm/i915/display/intel_casf_regs.h
new file mode 100644
index 000000000000..87803cca510f
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_casf_regs.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __INTEL_CASF_REGS_H__
+#define __INTEL_CASF_REGS_H__
+
+#include "intel_display_reg_defs.h"
+
+#define _SHARPNESS_CTL_A 0x682B0
+#define _SHARPNESS_CTL_B 0x68AB0
+#define SHARPNESS_CTL(pipe) _MMIO_PIPE(pipe, _SHARPNESS_CTL_A, _SHARPNESS_CTL_B)
+#define FILTER_EN REG_BIT(31)
+#define FILTER_STRENGTH_MASK REG_GENMASK(15, 8)
+#define FILTER_STRENGTH(x) REG_FIELD_PREP(FILTER_STRENGTH_MASK, (x))
+#define FILTER_SIZE_MASK REG_GENMASK(1, 0)
+#define SHARPNESS_FILTER_SIZE_3X3 REG_FIELD_PREP(FILTER_SIZE_MASK, 0)
+#define SHARPNESS_FILTER_SIZE_5X5 REG_FIELD_PREP(FILTER_SIZE_MASK, 1)
+#define SHARPNESS_FILTER_SIZE_7X7 REG_FIELD_PREP(FILTER_SIZE_MASK, 2)
+
+#define _SHRPLUT_DATA_A 0x682B8
+#define _SHRPLUT_DATA_B 0x68AB8
+#define SHRPLUT_DATA(pipe) _MMIO_PIPE(pipe, _SHRPLUT_DATA_A, _SHRPLUT_DATA_B)
+
+#define _SHRPLUT_INDEX_A 0x682B4
+#define _SHRPLUT_INDEX_B 0x68AB4
+#define SHRPLUT_INDEX(pipe) _MMIO_PIPE(pipe, _SHRPLUT_INDEX_A, _SHRPLUT_INDEX_B)
+#define INDEX_AUTO_INCR REG_BIT(10)
+#define INDEX_VALUE_MASK REG_GENMASK(4, 0)
+#define INDEX_VALUE(x) REG_FIELD_PREP(INDEX_VALUE_MASK, (x))
+
+#endif /* __INTEL_CASF_REGS__ */
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index 9725eebe5706..37801c744b05 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -26,21 +26,22 @@
#include <linux/time.h>
#include <drm/drm_fixed.h>
+#include <drm/drm_print.h>
#include "soc/intel_dram.h"
#include "hsw_ips.h"
#include "i915_drv.h"
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_audio.h"
-#include "intel_bw.h"
#include "intel_cdclk.h"
#include "intel_crtc.h"
+#include "intel_dbuf_bw.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_mchbar_regs.h"
#include "intel_pci_config.h"
#include "intel_pcode.h"
@@ -49,6 +50,7 @@
#include "intel_vdsc.h"
#include "skl_watermark.h"
#include "skl_watermark_regs.h"
+#include "vlv_clock.h"
#include "vlv_dsi.h"
#include "vlv_sideband.h"
@@ -132,8 +134,8 @@ struct intel_cdclk_state {
*/
struct intel_cdclk_config actual;
- /* minimum acceptable cdclk to satisfy bandwidth requirements */
- int bw_min_cdclk;
+ /* minimum acceptable cdclk to satisfy DBUF bandwidth requirements */
+ int dbuf_bw_min_cdclk;
/* minimum acceptable cdclk for each pipe */
int min_cdclk[I915_MAX_PIPES];
/* minimum acceptable voltage level for each pipe */
@@ -145,6 +147,9 @@ struct intel_cdclk_state {
/* forced minimum cdclk for glk+ audio w/a */
int force_min_cdclk;
+ /* bitmask of enabled pipes */
+ u8 enabled_pipes;
+
/* bitmask of active pipes */
u8 active_pipes;
@@ -563,8 +568,7 @@ static void hsw_get_cdclk(struct intel_display *display,
static int vlv_calc_cdclk(struct intel_display *display, int min_cdclk)
{
- struct drm_i915_private *dev_priv = to_i915(display->drm);
- int freq_320 = (dev_priv->hpll_freq << 1) % 320000 != 0 ?
+ int freq_320 = (vlv_clock_get_hpll_vco(display->drm) << 1) % 320000 != 0 ?
333333 : 320000;
/*
@@ -584,8 +588,6 @@ static int vlv_calc_cdclk(struct intel_display *display, int min_cdclk)
static u8 vlv_calc_voltage_level(struct intel_display *display, int cdclk)
{
- struct drm_i915_private *dev_priv = to_i915(display->drm);
-
if (display->platform.valleyview) {
if (cdclk >= 320000) /* jump to highest voltage for 400MHz too */
return 2;
@@ -599,7 +601,7 @@ static u8 vlv_calc_voltage_level(struct intel_display *display, int cdclk)
* hardware has shown that we just need to write the desired
* CCK divider into the Punit register.
*/
- return DIV_ROUND_CLOSEST(dev_priv->hpll_freq << 1, cdclk) - 1;
+ return DIV_ROUND_CLOSEST(vlv_clock_get_hpll_vco(display->drm) << 1, cdclk) - 1;
}
}
@@ -608,17 +610,12 @@ static void vlv_get_cdclk(struct intel_display *display,
{
u32 val;
- vlv_iosf_sb_get(display->drm, BIT(VLV_IOSF_SB_CCK) | BIT(VLV_IOSF_SB_PUNIT));
-
- cdclk_config->vco = vlv_get_hpll_vco(display->drm);
- cdclk_config->cdclk = vlv_get_cck_clock(display->drm, "cdclk",
- CCK_DISPLAY_CLOCK_CONTROL,
- cdclk_config->vco);
+ cdclk_config->vco = vlv_clock_get_hpll_vco(display->drm);
+ cdclk_config->cdclk = vlv_clock_get_cdclk(display->drm);
+ vlv_punit_get(display->drm);
val = vlv_punit_read(display->drm, PUNIT_REG_DSPSSPM);
-
- vlv_iosf_sb_put(display->drm,
- BIT(VLV_IOSF_SB_CCK) | BIT(VLV_IOSF_SB_PUNIT));
+ vlv_punit_put(display->drm);
if (display->platform.valleyview)
cdclk_config->voltage_level = (val & DSPFREQGUAR_MASK) >>
@@ -630,7 +627,6 @@ static void vlv_get_cdclk(struct intel_display *display,
static void vlv_program_pfi_credits(struct intel_display *display)
{
- struct drm_i915_private *dev_priv = to_i915(display->drm);
unsigned int credits, default_credits;
if (display->platform.cherryview)
@@ -638,7 +634,7 @@ static void vlv_program_pfi_credits(struct intel_display *display)
else
default_credits = PFI_CREDIT(8);
- if (display->cdclk.hw.cdclk >= dev_priv->czclk_freq) {
+ if (display->cdclk.hw.cdclk >= vlv_clock_get_czclk(display->drm)) {
/* CHV suggested value is 31 or 63 */
if (display->platform.cherryview)
credits = PFI_CREDIT_63;
@@ -670,7 +666,6 @@ static void vlv_set_cdclk(struct intel_display *display,
const struct intel_cdclk_config *cdclk_config,
enum pipe pipe)
{
- struct drm_i915_private *dev_priv = to_i915(display->drm);
int cdclk = cdclk_config->cdclk;
u32 val, cmd = cdclk_config->voltage_level;
intel_wakeref_t wakeref;
@@ -715,7 +710,7 @@ static void vlv_set_cdclk(struct intel_display *display,
if (cdclk == 400000) {
u32 divider;
- divider = DIV_ROUND_CLOSEST(dev_priv->hpll_freq << 1,
+ divider = DIV_ROUND_CLOSEST(vlv_clock_get_hpll_vco(display->drm) << 1,
cdclk) - 1;
/* adjust cdclk divider */
@@ -907,9 +902,8 @@ static void bdw_set_cdclk(struct intel_display *display,
* According to the spec, it should be enough to poll for this 1 us.
* However, extensive testing shows that this can take longer.
*/
- ret = intel_de_wait_custom(display, LCPLL_CTL,
- LCPLL_CD_SOURCE_FCLK_DONE, LCPLL_CD_SOURCE_FCLK_DONE,
- 100, 0, NULL);
+ ret = intel_de_wait_for_set_us(display, LCPLL_CTL,
+ LCPLL_CD_SOURCE_FCLK_DONE, 100);
if (ret)
drm_err(display->drm, "Switching to FCLK failed\n");
@@ -919,9 +913,8 @@ static void bdw_set_cdclk(struct intel_display *display,
intel_de_rmw(display, LCPLL_CTL,
LCPLL_CD_SOURCE_FCLK, 0);
- ret = intel_de_wait_custom(display, LCPLL_CTL,
- LCPLL_CD_SOURCE_FCLK_DONE, 0,
- 1, 0, NULL);
+ ret = intel_de_wait_for_clear_us(display, LCPLL_CTL,
+ LCPLL_CD_SOURCE_FCLK_DONE, 1);
if (ret)
drm_err(display->drm, "Switching back to LCPLL failed\n");
@@ -1119,7 +1112,7 @@ static void skl_dpll0_enable(struct intel_display *display, int vco)
intel_de_rmw(display, LCPLL1_CTL,
0, LCPLL_PLL_ENABLE);
- if (intel_de_wait_for_set(display, LCPLL1_CTL, LCPLL_PLL_LOCK, 5))
+ if (intel_de_wait_for_set_ms(display, LCPLL1_CTL, LCPLL_PLL_LOCK, 5))
drm_err(display->drm, "DPLL0 not locked\n");
display->cdclk.hw.vco = vco;
@@ -1133,7 +1126,7 @@ static void skl_dpll0_disable(struct intel_display *display)
intel_de_rmw(display, LCPLL1_CTL,
LCPLL_PLL_ENABLE, 0);
- if (intel_de_wait_for_clear(display, LCPLL1_CTL, LCPLL_PLL_LOCK, 1))
+ if (intel_de_wait_for_clear_ms(display, LCPLL1_CTL, LCPLL_PLL_LOCK, 1))
drm_err(display->drm, "Couldn't disable DPLL0\n");
display->cdclk.hw.vco = 0;
@@ -1540,6 +1533,41 @@ static const struct intel_cdclk_vals xe3lpd_cdclk_table[] = {
{}
};
+static const struct intel_cdclk_vals xe3p_lpd_cdclk_table[] = {
+ { .refclk = 38400, .cdclk = 151200, .ratio = 21, .waveform = 0xa4a4 },
+ { .refclk = 38400, .cdclk = 176400, .ratio = 21, .waveform = 0xaa54 },
+ { .refclk = 38400, .cdclk = 201600, .ratio = 21, .waveform = 0xaaaa },
+ { .refclk = 38400, .cdclk = 226800, .ratio = 21, .waveform = 0xad5a },
+ { .refclk = 38400, .cdclk = 252000, .ratio = 21, .waveform = 0xb6b6 },
+ { .refclk = 38400, .cdclk = 277200, .ratio = 21, .waveform = 0xdbb6 },
+ { .refclk = 38400, .cdclk = 302400, .ratio = 21, .waveform = 0xeeee },
+ { .refclk = 38400, .cdclk = 327600, .ratio = 21, .waveform = 0xf7de },
+ { .refclk = 38400, .cdclk = 352800, .ratio = 21, .waveform = 0xfefe },
+ { .refclk = 38400, .cdclk = 378000, .ratio = 21, .waveform = 0xfffe },
+ { .refclk = 38400, .cdclk = 403200, .ratio = 21, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 422400, .ratio = 22, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 441600, .ratio = 23, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 460800, .ratio = 24, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 480000, .ratio = 25, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 499200, .ratio = 26, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 518400, .ratio = 27, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 537600, .ratio = 28, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 556800, .ratio = 29, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 576000, .ratio = 30, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 595200, .ratio = 31, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 614400, .ratio = 32, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 633600, .ratio = 33, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 652800, .ratio = 34, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 672000, .ratio = 35, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 691200, .ratio = 36, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 710400, .ratio = 37, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 729600, .ratio = 38, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 748800, .ratio = 39, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 768000, .ratio = 40, .waveform = 0xffff },
+ { .refclk = 38400, .cdclk = 787200, .ratio = 41, .waveform = 0xffff },
+ {}
+};
+
static const int cdclk_squash_len = 16;
static int cdclk_squash_divider(u16 waveform)
@@ -1567,7 +1595,7 @@ static int bxt_calc_cdclk(struct intel_display *display, int min_cdclk)
drm_WARN(display->drm, 1,
"Cannot satisfy minimum cdclk %d with refclk %u\n",
min_cdclk, display->cdclk.hw.ref);
- return 0;
+ return display->cdclk.max_cdclk_freq;
}
static int bxt_calc_cdclk_pll_vco(struct intel_display *display, int cdclk)
@@ -1805,8 +1833,8 @@ static void bxt_de_pll_disable(struct intel_display *display)
intel_de_write(display, BXT_DE_PLL_ENABLE, 0);
/* Timeout 200us */
- if (intel_de_wait_for_clear(display,
- BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
+ if (intel_de_wait_for_clear_ms(display,
+ BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
drm_err(display->drm, "timeout waiting for DE PLL unlock\n");
display->cdclk.hw.vco = 0;
@@ -1822,8 +1850,8 @@ static void bxt_de_pll_enable(struct intel_display *display, int vco)
intel_de_write(display, BXT_DE_PLL_ENABLE, BXT_DE_PLL_PLL_ENABLE);
/* Timeout 200us */
- if (intel_de_wait_for_set(display,
- BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
+ if (intel_de_wait_for_set_ms(display,
+ BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
drm_err(display->drm, "timeout waiting for DE PLL lock\n");
display->cdclk.hw.vco = vco;
@@ -1835,7 +1863,7 @@ static void icl_cdclk_pll_disable(struct intel_display *display)
BXT_DE_PLL_PLL_ENABLE, 0);
/* Timeout 200us */
- if (intel_de_wait_for_clear(display, BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
+ if (intel_de_wait_for_clear_ms(display, BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
drm_err(display->drm, "timeout waiting for CDCLK PLL unlock\n");
display->cdclk.hw.vco = 0;
@@ -1853,7 +1881,7 @@ static void icl_cdclk_pll_enable(struct intel_display *display, int vco)
intel_de_write(display, BXT_DE_PLL_ENABLE, val);
/* Timeout 200us */
- if (intel_de_wait_for_set(display, BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
+ if (intel_de_wait_for_set_ms(display, BXT_DE_PLL_ENABLE, BXT_DE_PLL_LOCK, 1))
drm_err(display->drm, "timeout waiting for CDCLK PLL lock\n");
display->cdclk.hw.vco = vco;
@@ -1873,8 +1901,8 @@ static void adlp_cdclk_pll_crawl(struct intel_display *display, int vco)
intel_de_write(display, BXT_DE_PLL_ENABLE, val);
/* Timeout 200us */
- if (intel_de_wait_for_set(display, BXT_DE_PLL_ENABLE,
- BXT_DE_PLL_LOCK | BXT_DE_PLL_FREQ_REQ_ACK, 1))
+ if (intel_de_wait_for_set_ms(display, BXT_DE_PLL_ENABLE,
+ BXT_DE_PLL_LOCK | BXT_DE_PLL_FREQ_REQ_ACK, 1))
drm_err(display->drm, "timeout waiting for FREQ change request ack\n");
val &= ~BXT_DE_PLL_FREQ_REQ;
@@ -2600,6 +2628,12 @@ static void intel_set_cdclk(struct intel_display *display,
}
}
+static bool dg2_power_well_count(struct intel_display *display,
+ const struct intel_cdclk_state *cdclk_state)
+{
+ return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0;
+}
+
static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
@@ -2612,16 +2646,16 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state)
if (!intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual) &&
- new_cdclk_state->active_pipes ==
- old_cdclk_state->active_pipes)
+ dg2_power_well_count(display, old_cdclk_state) ==
+ dg2_power_well_count(display, new_cdclk_state))
return;
/* According to "Sequence Before Frequency Change", voltage level set to 0x3 */
voltage_level = DISPLAY_TO_PCODE_VOLTAGE_MAX;
change_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk;
- update_pipe_count = hweight8(new_cdclk_state->active_pipes) >
- hweight8(old_cdclk_state->active_pipes);
+ update_pipe_count = dg2_power_well_count(display, new_cdclk_state) >
+ dg2_power_well_count(display, old_cdclk_state);
/*
* According to "Sequence Before Frequency Change",
@@ -2639,7 +2673,7 @@ static void intel_cdclk_pcode_pre_notify(struct intel_atomic_state *state)
* no action if it is decreasing, before the change
*/
if (update_pipe_count)
- num_active_pipes = hweight8(new_cdclk_state->active_pipes);
+ num_active_pipes = dg2_power_well_count(display, new_cdclk_state);
intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk,
change_cdclk, update_pipe_count);
@@ -2659,8 +2693,8 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state)
voltage_level = new_cdclk_state->actual.voltage_level;
update_cdclk = new_cdclk_state->actual.cdclk != old_cdclk_state->actual.cdclk;
- update_pipe_count = hweight8(new_cdclk_state->active_pipes) <
- hweight8(old_cdclk_state->active_pipes);
+ update_pipe_count = dg2_power_well_count(display, new_cdclk_state) <
+ dg2_power_well_count(display, old_cdclk_state);
/*
* According to "Sequence After Frequency Change",
@@ -2676,7 +2710,7 @@ static void intel_cdclk_pcode_post_notify(struct intel_atomic_state *state)
* no action if it is increasing, after the change
*/
if (update_pipe_count)
- num_active_pipes = hweight8(new_cdclk_state->active_pipes);
+ num_active_pipes = dg2_power_well_count(display, new_cdclk_state);
intel_pcode_notify(display, voltage_level, num_active_pipes, cdclk,
update_cdclk, update_pipe_count);
@@ -2711,6 +2745,9 @@ intel_set_cdclk_pre_plane_update(struct intel_atomic_state *state)
struct intel_cdclk_config cdclk_config;
enum pipe pipe;
+ if (!new_cdclk_state)
+ return;
+
if (!intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual))
return;
@@ -2763,6 +2800,9 @@ intel_set_cdclk_post_plane_update(struct intel_atomic_state *state)
intel_atomic_get_new_cdclk_state(state);
enum pipe pipe;
+ if (!new_cdclk_state)
+ return;
+
if (!intel_cdclk_changed(&old_cdclk_state->actual,
&new_cdclk_state->actual))
return;
@@ -2800,16 +2840,20 @@ static int intel_cdclk_guardband(struct intel_display *display)
return 90;
}
-static int intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state)
+static int _intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state, int pixel_rate)
{
struct intel_display *display = to_intel_display(crtc_state);
int ppc = intel_cdclk_ppc(display, crtc_state->double_wide);
int guardband = intel_cdclk_guardband(display);
- int pixel_rate = crtc_state->pixel_rate;
return DIV_ROUND_UP(pixel_rate * 100, guardband * ppc);
}
+static int intel_pixel_rate_to_cdclk(const struct intel_crtc_state *crtc_state)
+{
+ return _intel_pixel_rate_to_cdclk(crtc_state, crtc_state->pixel_rate);
+}
+
static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state)
{
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
@@ -2818,12 +2862,12 @@ static int intel_planes_min_cdclk(const struct intel_crtc_state *crtc_state)
int min_cdclk = 0;
for_each_intel_plane_on_crtc(display->drm, crtc, plane)
- min_cdclk = max(min_cdclk, crtc_state->min_cdclk[plane->id]);
+ min_cdclk = max(min_cdclk, crtc_state->plane_min_cdclk[plane->id]);
return min_cdclk;
}
-static int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state)
+int intel_crtc_min_cdclk(const struct intel_crtc_state *crtc_state)
{
int min_cdclk;
@@ -2831,6 +2875,8 @@ static int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_stat
return 0;
min_cdclk = intel_pixel_rate_to_cdclk(crtc_state);
+ min_cdclk = max(min_cdclk, intel_crtc_bw_min_cdclk(crtc_state));
+ min_cdclk = max(min_cdclk, intel_fbc_min_cdclk(crtc_state));
min_cdclk = max(min_cdclk, hsw_ips_min_cdclk(crtc_state));
min_cdclk = max(min_cdclk, intel_audio_min_cdclk(crtc_state));
min_cdclk = max(min_cdclk, vlv_dsi_min_cdclk(crtc_state));
@@ -2840,51 +2886,110 @@ static int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_stat
return min_cdclk;
}
-static int intel_compute_min_cdclk(struct intel_atomic_state *state)
+static int intel_cdclk_update_crtc_min_cdclk(struct intel_atomic_state *state,
+ struct intel_crtc *crtc,
+ int old_min_cdclk, int new_min_cdclk,
+ bool *need_cdclk_calc)
{
struct intel_display *display = to_intel_display(state);
- struct intel_cdclk_state *cdclk_state =
- intel_atomic_get_new_cdclk_state(state);
- const struct intel_bw_state *bw_state;
- struct intel_crtc *crtc;
- struct intel_crtc_state *crtc_state;
- int min_cdclk, i;
- enum pipe pipe;
+ struct intel_cdclk_state *cdclk_state;
+ bool allow_cdclk_decrease = intel_any_crtc_needs_modeset(state);
+ int ret;
- for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
- int ret;
+ if (new_min_cdclk == old_min_cdclk)
+ return 0;
- min_cdclk = intel_crtc_compute_min_cdclk(crtc_state);
- if (min_cdclk < 0)
- return min_cdclk;
+ if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
+ return 0;
- if (cdclk_state->min_cdclk[crtc->pipe] == min_cdclk)
- continue;
+ cdclk_state = intel_atomic_get_cdclk_state(state);
+ if (IS_ERR(cdclk_state))
+ return PTR_ERR(cdclk_state);
- cdclk_state->min_cdclk[crtc->pipe] = min_cdclk;
+ old_min_cdclk = cdclk_state->min_cdclk[crtc->pipe];
- ret = intel_atomic_lock_global_state(&cdclk_state->base);
- if (ret)
- return ret;
- }
+ if (new_min_cdclk == old_min_cdclk)
+ return 0;
+
+ if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
+ return 0;
+
+ cdclk_state->min_cdclk[crtc->pipe] = new_min_cdclk;
- bw_state = intel_atomic_get_new_bw_state(state);
- if (bw_state) {
- min_cdclk = intel_bw_min_cdclk(display, bw_state);
+ ret = intel_atomic_lock_global_state(&cdclk_state->base);
+ if (ret)
+ return ret;
- if (cdclk_state->bw_min_cdclk != min_cdclk) {
- int ret;
+ *need_cdclk_calc = true;
- cdclk_state->bw_min_cdclk = min_cdclk;
+ drm_dbg_kms(display->drm,
+ "[CRTC:%d:%s] min cdclk: %d kHz -> %d kHz\n",
+ crtc->base.base.id, crtc->base.name,
+ old_min_cdclk, new_min_cdclk);
- ret = intel_atomic_lock_global_state(&cdclk_state->base);
- if (ret)
- return ret;
- }
- }
+ return 0;
+}
+
+int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state,
+ int old_min_cdclk, int new_min_cdclk,
+ bool *need_cdclk_calc)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_cdclk_state *cdclk_state;
+ bool allow_cdclk_decrease = intel_any_crtc_needs_modeset(state);
+ int ret;
+
+ if (new_min_cdclk == old_min_cdclk)
+ return 0;
+
+ if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
+ return 0;
+
+ cdclk_state = intel_atomic_get_cdclk_state(state);
+ if (IS_ERR(cdclk_state))
+ return PTR_ERR(cdclk_state);
+
+ old_min_cdclk = cdclk_state->dbuf_bw_min_cdclk;
- min_cdclk = max(cdclk_state->force_min_cdclk,
- cdclk_state->bw_min_cdclk);
+ if (new_min_cdclk == old_min_cdclk)
+ return 0;
+
+ if (!allow_cdclk_decrease && new_min_cdclk < old_min_cdclk)
+ return 0;
+
+ cdclk_state->dbuf_bw_min_cdclk = new_min_cdclk;
+
+ ret = intel_atomic_lock_global_state(&cdclk_state->base);
+ if (ret)
+ return ret;
+
+ *need_cdclk_calc = true;
+
+ drm_dbg_kms(display->drm,
+ "dbuf bandwidth min cdclk: %d kHz -> %d kHz\n",
+ old_min_cdclk, new_min_cdclk);
+
+ return 0;
+}
+
+static bool glk_cdclk_audio_wa_needed(struct intel_display *display,
+ const struct intel_cdclk_state *cdclk_state)
+{
+ return display->platform.geminilake &&
+ cdclk_state->enabled_pipes &&
+ !is_power_of_2(cdclk_state->enabled_pipes);
+}
+
+static int intel_compute_min_cdclk(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_cdclk_state *cdclk_state =
+ intel_atomic_get_new_cdclk_state(state);
+ enum pipe pipe;
+ int min_cdclk;
+
+ min_cdclk = cdclk_state->force_min_cdclk;
+ min_cdclk = max(min_cdclk, cdclk_state->dbuf_bw_min_cdclk);
for_each_pipe(display, pipe)
min_cdclk = max(min_cdclk, cdclk_state->min_cdclk[pipe]);
@@ -2896,8 +3001,7 @@ static int intel_compute_min_cdclk(struct intel_atomic_state *state)
* by changing the cd2x divider (see glk_cdclk_table[]) and
* thus a full modeset won't be needed then.
*/
- if (display->platform.geminilake && cdclk_state->active_pipes &&
- !is_power_of_2(cdclk_state->active_pipes))
+ if (glk_cdclk_audio_wa_needed(display, cdclk_state))
min_cdclk = max(min_cdclk, 2 * 96000);
if (min_cdclk > display->cdclk.max_cdclk_freq) {
@@ -3183,38 +3287,66 @@ intel_atomic_get_cdclk_state(struct intel_atomic_state *state)
return to_intel_cdclk_state(cdclk_state);
}
-int intel_cdclk_atomic_check(struct intel_atomic_state *state,
- bool *need_cdclk_calc)
+static int intel_cdclk_modeset_checks(struct intel_atomic_state *state,
+ bool *need_cdclk_calc)
{
+ struct intel_display *display = to_intel_display(state);
const struct intel_cdclk_state *old_cdclk_state;
- const struct intel_cdclk_state *new_cdclk_state;
- struct intel_plane_state __maybe_unused *plane_state;
- struct intel_plane *plane;
+ struct intel_cdclk_state *new_cdclk_state;
int ret;
- int i;
- /*
- * active_planes bitmask has been updated, and potentially affected
- * planes are part of the state. We can now compute the minimum cdclk
- * for each plane.
- */
- for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
- ret = intel_plane_calc_min_cdclk(state, plane, need_cdclk_calc);
- if (ret)
- return ret;
- }
+ if (!intel_any_crtc_enable_changed(state) &&
+ !intel_any_crtc_active_changed(state))
+ return 0;
+
+ new_cdclk_state = intel_atomic_get_cdclk_state(state);
+ if (IS_ERR(new_cdclk_state))
+ return PTR_ERR(new_cdclk_state);
+
+ old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
+
+ new_cdclk_state->enabled_pipes =
+ intel_calc_enabled_pipes(state, old_cdclk_state->enabled_pipes);
- ret = intel_bw_calc_min_cdclk(state, need_cdclk_calc);
+ new_cdclk_state->active_pipes =
+ intel_calc_active_pipes(state, old_cdclk_state->active_pipes);
+
+ ret = intel_atomic_lock_global_state(&new_cdclk_state->base);
if (ret)
return ret;
- old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
- new_cdclk_state = intel_atomic_get_new_cdclk_state(state);
+ if (!old_cdclk_state->active_pipes != !new_cdclk_state->active_pipes)
+ *need_cdclk_calc = true;
- if (new_cdclk_state &&
- old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk)
+ if (glk_cdclk_audio_wa_needed(display, old_cdclk_state) !=
+ glk_cdclk_audio_wa_needed(display, new_cdclk_state))
*need_cdclk_calc = true;
+ if (dg2_power_well_count(display, old_cdclk_state) !=
+ dg2_power_well_count(display, new_cdclk_state))
+ *need_cdclk_calc = true;
+
+ return 0;
+}
+
+static int intel_crtcs_calc_min_cdclk(struct intel_atomic_state *state,
+ bool *need_cdclk_calc)
+{
+ const struct intel_crtc_state *old_crtc_state;
+ const struct intel_crtc_state *new_crtc_state;
+ struct intel_crtc *crtc;
+ int i, ret;
+
+ for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
+ new_crtc_state, i) {
+ ret = intel_cdclk_update_crtc_min_cdclk(state, crtc,
+ old_crtc_state->min_cdclk,
+ new_crtc_state->min_cdclk,
+ need_cdclk_calc);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -3250,18 +3382,17 @@ static bool intel_cdclk_need_serialize(struct intel_display *display,
const struct intel_cdclk_state *old_cdclk_state,
const struct intel_cdclk_state *new_cdclk_state)
{
- bool power_well_cnt_changed = hweight8(old_cdclk_state->active_pipes) !=
- hweight8(new_cdclk_state->active_pipes);
- bool cdclk_changed = intel_cdclk_changed(&old_cdclk_state->actual,
- &new_cdclk_state->actual);
/*
- * We need to poke hw for gen >= 12, because we notify PCode if
+ * We need to poke hw for DG2, because we notify PCode if
* pipe power well count changes.
*/
- return cdclk_changed || (display->platform.dg2 && power_well_cnt_changed);
+ return intel_cdclk_changed(&old_cdclk_state->actual,
+ &new_cdclk_state->actual) ||
+ dg2_power_well_count(display, old_cdclk_state) !=
+ dg2_power_well_count(display, new_cdclk_state);
}
-int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
+static int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
{
struct intel_display *display = to_intel_display(state);
const struct intel_cdclk_state *old_cdclk_state;
@@ -3275,9 +3406,6 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
- new_cdclk_state->active_pipes =
- intel_calc_active_pipes(state, old_cdclk_state->active_pipes);
-
ret = intel_cdclk_modeset_calc_cdclk(state);
if (ret)
return ret;
@@ -3290,9 +3418,7 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
ret = intel_atomic_serialize_global_state(&new_cdclk_state->base);
if (ret)
return ret;
- } else if (old_cdclk_state->active_pipes != new_cdclk_state->active_pipes ||
- old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk ||
- intel_cdclk_changed(&old_cdclk_state->logical,
+ } else if (intel_cdclk_changed(&old_cdclk_state->logical,
&new_cdclk_state->logical)) {
ret = intel_atomic_lock_global_state(&new_cdclk_state->base);
if (ret)
@@ -3374,14 +3500,55 @@ int intel_modeset_calc_cdclk(struct intel_atomic_state *state)
return 0;
}
+int intel_cdclk_atomic_check(struct intel_atomic_state *state)
+{
+ const struct intel_cdclk_state *old_cdclk_state;
+ struct intel_cdclk_state *new_cdclk_state;
+ bool need_cdclk_calc = false;
+ int ret;
+
+ ret = intel_cdclk_modeset_checks(state, &need_cdclk_calc);
+ if (ret)
+ return ret;
+
+ ret = intel_crtcs_calc_min_cdclk(state, &need_cdclk_calc);
+ if (ret)
+ return ret;
+
+ ret = intel_dbuf_bw_calc_min_cdclk(state, &need_cdclk_calc);
+ if (ret)
+ return ret;
+
+ old_cdclk_state = intel_atomic_get_old_cdclk_state(state);
+ new_cdclk_state = intel_atomic_get_new_cdclk_state(state);
+
+ if (new_cdclk_state &&
+ old_cdclk_state->force_min_cdclk != new_cdclk_state->force_min_cdclk) {
+ ret = intel_atomic_lock_global_state(&new_cdclk_state->base);
+ if (ret)
+ return ret;
+
+ need_cdclk_calc = true;
+ }
+
+ if (need_cdclk_calc) {
+ ret = intel_modeset_calc_cdclk(state);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
void intel_cdclk_update_hw_state(struct intel_display *display)
{
- const struct intel_bw_state *bw_state =
- to_intel_bw_state(display->bw.obj.state);
+ const struct intel_dbuf_bw_state *dbuf_bw_state =
+ to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
struct intel_cdclk_state *cdclk_state =
to_intel_cdclk_state(display->cdclk.obj.state);
struct intel_crtc *crtc;
+ cdclk_state->enabled_pipes = 0;
cdclk_state->active_pipes = 0;
for_each_intel_crtc(display->drm, crtc) {
@@ -3389,14 +3556,16 @@ void intel_cdclk_update_hw_state(struct intel_display *display)
to_intel_crtc_state(crtc->base.state);
enum pipe pipe = crtc->pipe;
+ if (crtc_state->hw.enable)
+ cdclk_state->enabled_pipes |= BIT(pipe);
if (crtc_state->hw.active)
cdclk_state->active_pipes |= BIT(pipe);
- cdclk_state->min_cdclk[pipe] = intel_crtc_compute_min_cdclk(crtc_state);
+ cdclk_state->min_cdclk[pipe] = crtc_state->min_cdclk;
cdclk_state->min_voltage_level[pipe] = crtc_state->min_voltage_level;
}
- cdclk_state->bw_min_cdclk = intel_bw_min_cdclk(display, bw_state);
+ cdclk_state->dbuf_bw_min_cdclk = intel_dbuf_bw_min_cdclk(display, dbuf_bw_state);
}
void intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc)
@@ -3425,7 +3594,9 @@ static int intel_compute_max_dotclk(struct intel_display *display)
*/
void intel_update_max_cdclk(struct intel_display *display)
{
- if (DISPLAY_VERx100(display) >= 3002) {
+ if (DISPLAY_VER(display) >= 35) {
+ display->cdclk.max_cdclk_freq = 787200;
+ } else if (DISPLAY_VERx100(display) >= 3002) {
display->cdclk.max_cdclk_freq = 480000;
} else if (DISPLAY_VER(display) >= 30) {
display->cdclk.max_cdclk_freq = 691200;
@@ -3565,13 +3736,6 @@ static int pch_rawclk(struct intel_display *display)
return (intel_de_read(display, PCH_RAWCLK_FREQ) & RAWCLK_FREQ_MASK) * 1000;
}
-static int vlv_hrawclk(struct intel_display *display)
-{
- /* RAWCLK_FREQ_VLV register updated from power well code */
- return vlv_get_cck_clock_hpll(display->drm, "hrawclk",
- CCK_DISPLAY_REF_CLOCK_CONTROL);
-}
-
static int i9xx_hrawclk(struct intel_display *display)
{
struct drm_i915_private *i915 = to_i915(display->drm);
@@ -3605,7 +3769,7 @@ u32 intel_read_rawclk(struct intel_display *display)
else if (HAS_PCH_SPLIT(display))
freq = pch_rawclk(display);
else if (display->platform.valleyview || display->platform.cherryview)
- freq = vlv_hrawclk(display);
+ freq = vlv_clock_get_hrawclk(display->drm);
else if (DISPLAY_VER(display) >= 3)
freq = i9xx_hrawclk(display);
else
@@ -3783,7 +3947,10 @@ static const struct intel_cdclk_funcs i830_cdclk_funcs = {
*/
void intel_init_cdclk_hooks(struct intel_display *display)
{
- if (DISPLAY_VER(display) >= 30) {
+ if (DISPLAY_VER(display) >= 35) {
+ display->funcs.cdclk = &xe3lpd_cdclk_funcs;
+ display->cdclk.table = xe3p_lpd_cdclk_table;
+ } else if (DISPLAY_VER(display) >= 30) {
display->funcs.cdclk = &xe3lpd_cdclk_funcs;
display->cdclk.table = xe3lpd_cdclk_table;
} else if (DISPLAY_VER(display) >= 20) {
@@ -3897,11 +4064,6 @@ int intel_cdclk_min_cdclk(const struct intel_cdclk_state *cdclk_state, enum pipe
return cdclk_state->min_cdclk[pipe];
}
-int intel_cdclk_bw_min_cdclk(const struct intel_cdclk_state *cdclk_state)
-{
- return cdclk_state->bw_min_cdclk;
-}
-
bool intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state)
{
const struct intel_cdclk_state *new_cdclk_state, *old_cdclk_state;
@@ -3933,3 +4095,75 @@ void intel_cdclk_read_hw(struct intel_display *display)
cdclk_state->actual = display->cdclk.hw;
cdclk_state->logical = display->cdclk.hw;
}
+
+static int calc_cdclk(const struct intel_crtc_state *crtc_state, int min_cdclk)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ if (DISPLAY_VER(display) >= 10 || display->platform.broxton) {
+ return bxt_calc_cdclk(display, min_cdclk);
+ } else if (DISPLAY_VER(display) == 9) {
+ int vco;
+
+ vco = display->cdclk.skl_preferred_vco_freq;
+ if (vco == 0)
+ vco = 8100000;
+
+ return skl_calc_cdclk(min_cdclk, vco);
+ } else if (display->platform.broadwell) {
+ return bdw_calc_cdclk(min_cdclk);
+ } else if (display->platform.cherryview || display->platform.valleyview) {
+ return vlv_calc_cdclk(display, min_cdclk);
+ } else {
+ return display->cdclk.max_cdclk_freq;
+ }
+}
+
+static unsigned int _intel_cdclk_prefill_adj(const struct intel_crtc_state *crtc_state,
+ int clock, int min_cdclk)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int ppc = intel_cdclk_ppc(display, crtc_state->double_wide);
+ int cdclk = calc_cdclk(crtc_state, min_cdclk);
+
+ return min(0x10000, DIV_ROUND_UP_ULL((u64)clock << 16, ppc * cdclk));
+}
+
+unsigned int intel_cdclk_prefill_adjustment(const struct intel_crtc_state *crtc_state)
+{
+ /* FIXME use the actual min_cdclk for the pipe here */
+ return intel_cdclk_prefill_adjustment_worst(crtc_state);
+}
+
+unsigned int intel_cdclk_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state)
+{
+ int clock = crtc_state->hw.pipe_mode.crtc_clock;
+ int min_cdclk;
+
+ /*
+ * FIXME could perhaps consider a few more of the factors
+ * that go the per-crtc min_cdclk. Namely anything that
+ * only changes during full modesets.
+ *
+ * FIXME this assumes 1:1 scaling, but the other _worst() stuff
+ * assumes max downscaling, so the final result will be
+ * unrealistically bad. Figure out where the actual maximum value
+ * lies and use that to compute a more realistic worst case
+ * estimate...
+ */
+ min_cdclk = _intel_pixel_rate_to_cdclk(crtc_state, clock);
+
+ return _intel_cdclk_prefill_adj(crtc_state, clock, min_cdclk);
+}
+
+int intel_cdclk_min_cdclk_for_prefill(const struct intel_crtc_state *crtc_state,
+ unsigned int prefill_lines_unadjusted,
+ unsigned int prefill_lines_available)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ const struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode;
+ int ppc = intel_cdclk_ppc(display, crtc_state->double_wide);
+
+ return DIV_ROUND_UP_ULL(mul_u32_u32(pipe_mode->crtc_clock, prefill_lines_unadjusted),
+ ppc * prefill_lines_available);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.h b/drivers/gpu/drm/i915/display/intel_cdclk.h
index cacee598af0e..1ff7d078b42c 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.h
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.h
@@ -38,16 +38,17 @@ void intel_set_cdclk_post_plane_update(struct intel_atomic_state *state);
void intel_cdclk_dump_config(struct intel_display *display,
const struct intel_cdclk_config *cdclk_config,
const char *context);
-int intel_modeset_calc_cdclk(struct intel_atomic_state *state);
void intel_cdclk_get_cdclk(struct intel_display *display,
struct intel_cdclk_config *cdclk_config);
-int intel_cdclk_atomic_check(struct intel_atomic_state *state,
- bool *need_cdclk_calc);
+int intel_cdclk_atomic_check(struct intel_atomic_state *state);
int intel_cdclk_state_set_joined_mbus(struct intel_atomic_state *state, bool joined_mbus);
struct intel_cdclk_state *
intel_atomic_get_cdclk_state(struct intel_atomic_state *state);
void intel_cdclk_update_hw_state(struct intel_display *display);
void intel_cdclk_crtc_disable_noatomic(struct intel_crtc *crtc);
+int intel_cdclk_update_dbuf_bw_min_cdclk(struct intel_atomic_state *state,
+ int old_min_cdclk, int new_min_cdclk,
+ bool *need_cdclk_calc);
#define to_intel_cdclk_state(global_state) \
container_of_const((global_state), struct intel_cdclk_state, base)
@@ -64,9 +65,16 @@ int intel_cdclk_logical(const struct intel_cdclk_state *cdclk_state);
int intel_cdclk_actual(const struct intel_cdclk_state *cdclk_state);
int intel_cdclk_actual_voltage_level(const struct intel_cdclk_state *cdclk_state);
int intel_cdclk_min_cdclk(const struct intel_cdclk_state *cdclk_state, enum pipe pipe);
-int intel_cdclk_bw_min_cdclk(const struct intel_cdclk_state *cdclk_state);
bool intel_cdclk_pmdemand_needs_update(struct intel_atomic_state *state);
void intel_cdclk_force_min_cdclk(struct intel_cdclk_state *cdclk_state, int force_min_cdclk);
void intel_cdclk_read_hw(struct intel_display *display);
+unsigned int intel_cdclk_prefill_adjustment(const struct intel_crtc_state *crtc_state);
+unsigned int intel_cdclk_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state);
+int intel_cdclk_min_cdclk_for_prefill(const struct intel_crtc_state *crtc_state,
+ unsigned int prefill_lines_unadjusted,
+ unsigned int prefill_lines_available);
+
+int intel_crtc_min_cdclk(const struct intel_crtc_state *crtc_state);
+
#endif /* __INTEL_CDCLK_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c
index 671db6926e4c..e7950655434b 100644
--- a/drivers/gpu/drm/i915/display/intel_color.c
+++ b/drivers/gpu/drm/i915/display/intel_color.c
@@ -24,14 +24,16 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "i9xx_plane_regs.h"
#include "intel_color.h"
#include "intel_color_regs.h"
#include "intel_de.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dsb.h"
#include "intel_vrr.h"
+#include "skl_universal_plane.h"
+#include "skl_universal_plane_regs.h"
struct intel_color_funcs {
int (*color_check)(struct intel_atomic_state *state,
@@ -87,6 +89,14 @@ struct intel_color_funcs {
* Read config other than LUTs and CSCs, before them. Optional.
*/
void (*get_config)(struct intel_crtc_state *crtc_state);
+
+ /* Plane CSC*/
+ void (*load_plane_csc_matrix)(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state);
+
+ /* Plane Pre/Post CSC */
+ void (*load_plane_luts)(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state);
};
#define CTM_COEFF_SIGN (1ULL << 63)
@@ -609,6 +619,8 @@ static u16 ctm_to_twos_complement(u64 coeff, int int_bits, int frac_bits)
if (CTM_COEFF_NEGATIVE(coeff))
c = -c;
+ int_bits = max(int_bits, 1);
+
c = clamp(c, -(s64)BIT(int_bits + frac_bits - 1),
(s64)(BIT(int_bits + frac_bits - 1) - 1));
@@ -1090,18 +1102,19 @@ static void skl_get_config(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- u32 tmp;
crtc_state->gamma_mode = hsw_read_gamma_mode(crtc);
crtc_state->csc_mode = ilk_read_csc_mode(crtc);
- tmp = intel_de_read(display, SKL_BOTTOM_COLOR(crtc->pipe));
+ if (DISPLAY_VER(display) < 35) {
+ u32 tmp = intel_de_read(display, SKL_BOTTOM_COLOR(crtc->pipe));
- if (tmp & SKL_BOTTOM_COLOR_GAMMA_ENABLE)
- crtc_state->gamma_enable = true;
+ if (tmp & SKL_BOTTOM_COLOR_GAMMA_ENABLE)
+ crtc_state->gamma_enable = true;
- if (tmp & SKL_BOTTOM_COLOR_CSC_ENABLE)
- crtc_state->csc_enable = true;
+ if (tmp & SKL_BOTTOM_COLOR_CSC_ENABLE)
+ crtc_state->csc_enable = true;
+ }
}
static void skl_color_commit_arm(struct intel_dsb *dsb,
@@ -2013,7 +2026,7 @@ void intel_color_prepare_commit(struct intel_atomic_state *state,
if (crtc_state->use_dsb && intel_color_uses_chained_dsb(crtc_state)) {
intel_vrr_send_push(crtc_state->dsb_color, crtc_state);
- intel_dsb_wait_vblank_delay(state, crtc_state->dsb_color);
+ intel_dsb_wait_for_delayed_vblank(state, crtc_state->dsb_color);
intel_vrr_check_push_sent(crtc_state->dsb_color, crtc_state);
intel_dsb_interrupt(crtc_state->dsb_color);
}
@@ -3835,6 +3848,266 @@ static void icl_read_luts(struct intel_crtc_state *crtc_state)
}
}
+static void
+xelpd_load_plane_csc_matrix(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+ const struct drm_plane_state *state = &plane_state->uapi;
+ enum pipe pipe = to_intel_plane(state->plane)->pipe;
+ enum plane_id plane = to_intel_plane(state->plane)->id;
+ const struct drm_property_blob *blob = plane_state->hw.ctm;
+ struct drm_color_ctm_3x4 *ctm;
+ const u64 *input;
+ u16 coeffs[9] = {};
+ int i, j;
+
+ if (!icl_is_hdr_plane(display, plane) || !blob)
+ return;
+
+ ctm = blob->data;
+ input = ctm->matrix;
+
+ /*
+ * Convert fixed point S31.32 input to format supported by the
+ * hardware.
+ */
+ for (i = 0, j = 0; i < ARRAY_SIZE(coeffs); i++) {
+ u64 abs_coeff = ((1ULL << 63) - 1) & input[j];
+
+ /*
+ * Clamp input value to min/max supported by
+ * hardware.
+ */
+ abs_coeff = clamp_val(abs_coeff, 0, CTM_COEFF_4_0 - 1);
+
+ /* sign bit */
+ if (CTM_COEFF_NEGATIVE(input[j]))
+ coeffs[i] |= 1 << 15;
+
+ if (abs_coeff < CTM_COEFF_0_125)
+ coeffs[i] |= (3 << 12) |
+ ILK_CSC_COEFF_FP(abs_coeff, 12);
+ else if (abs_coeff < CTM_COEFF_0_25)
+ coeffs[i] |= (2 << 12) |
+ ILK_CSC_COEFF_FP(abs_coeff, 11);
+ else if (abs_coeff < CTM_COEFF_0_5)
+ coeffs[i] |= (1 << 12) |
+ ILK_CSC_COEFF_FP(abs_coeff, 10);
+ else if (abs_coeff < CTM_COEFF_1_0)
+ coeffs[i] |= ILK_CSC_COEFF_FP(abs_coeff, 9);
+ else if (abs_coeff < CTM_COEFF_2_0)
+ coeffs[i] |= (7 << 12) |
+ ILK_CSC_COEFF_FP(abs_coeff, 8);
+ else
+ coeffs[i] |= (6 << 12) |
+ ILK_CSC_COEFF_FP(abs_coeff, 7);
+
+ /* Skip postoffs */
+ if (!((j + 2) % 4))
+ j += 2;
+ else
+ j++;
+ }
+
+ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 0),
+ coeffs[0] << 16 | coeffs[1]);
+ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 1),
+ coeffs[2] << 16);
+
+ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 2),
+ coeffs[3] << 16 | coeffs[4]);
+ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 3),
+ coeffs[5] << 16);
+
+ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 4),
+ coeffs[6] << 16 | coeffs[7]);
+ intel_de_write_dsb(display, dsb, PLANE_CSC_COEFF(pipe, plane, 5),
+ coeffs[8] << 16);
+
+ intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane, 0), 0);
+ intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane, 1), 0);
+ intel_de_write_dsb(display, dsb, PLANE_CSC_PREOFF(pipe, plane, 2), 0);
+
+ /*
+ * Conversion from S31.32 to S0.12. BIT[12] is the signed bit
+ */
+ intel_de_write_dsb(display, dsb,
+ PLANE_CSC_POSTOFF(pipe, plane, 0),
+ ctm_to_twos_complement(input[3], 0, 12));
+ intel_de_write_dsb(display, dsb,
+ PLANE_CSC_POSTOFF(pipe, plane, 1),
+ ctm_to_twos_complement(input[7], 0, 12));
+ intel_de_write_dsb(display, dsb,
+ PLANE_CSC_POSTOFF(pipe, plane, 2),
+ ctm_to_twos_complement(input[11], 0, 12));
+}
+
+static void
+xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+ const struct drm_plane_state *state = &plane_state->uapi;
+ enum pipe pipe = to_intel_plane(state->plane)->pipe;
+ enum plane_id plane = to_intel_plane(state->plane)->id;
+ const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data;
+ u32 i, lut_size;
+
+ if (icl_is_hdr_plane(display, plane)) {
+ lut_size = 128;
+
+ intel_de_write_dsb(display, dsb,
+ PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
+ PLANE_PAL_PREC_AUTO_INCREMENT);
+
+ if (pre_csc_lut) {
+ for (i = 0; i < lut_size; i++) {
+ u32 lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24);
+
+ intel_de_write_dsb(display, dsb,
+ PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ lut_val);
+ }
+
+ /* Program the max register to clamp values > 1.0. */
+ /* TODO: Restrict to 0x7ffffff */
+ do {
+ intel_de_write_dsb(display, dsb,
+ PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ (1 << 24));
+ } while (i++ > 130);
+ } else {
+ for (i = 0; i < lut_size; i++) {
+ u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);
+
+ intel_de_write_dsb(display, dsb,
+ PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
+ }
+
+ do {
+ intel_de_write_dsb(display, dsb,
+ PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ 1 << 24);
+ } while (i++ < 130);
+ }
+
+ intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
+ }
+}
+
+static void
+xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+ const struct drm_plane_state *state = &plane_state->uapi;
+ enum pipe pipe = to_intel_plane(state->plane)->pipe;
+ enum plane_id plane = to_intel_plane(state->plane)->id;
+ const struct drm_color_lut32 *post_csc_lut = plane_state->hw.gamma_lut->data;
+ u32 i, lut_size, lut_val;
+
+ if (icl_is_hdr_plane(display, plane)) {
+ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0),
+ PLANE_PAL_PREC_AUTO_INCREMENT);
+ /* TODO: Add macro */
+ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0),
+ PLANE_PAL_PREC_AUTO_INCREMENT);
+ if (post_csc_lut) {
+ lut_size = 32;
+ for (i = 0; i < lut_size; i++) {
+ lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24);
+
+ intel_de_write_dsb(display, dsb,
+ PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ lut_val);
+ }
+
+ /* Segment 2 */
+ do {
+ intel_de_write_dsb(display, dsb,
+ PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ (1 << 24));
+ } while (i++ < 34);
+ } else {
+ /*TODO: Add for segment 0 */
+ lut_size = 32;
+ for (i = 0; i < lut_size; i++) {
+ u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1);
+
+ intel_de_write_dsb(display, dsb,
+ PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), v);
+ }
+
+ do {
+ intel_de_write_dsb(display, dsb,
+ PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0),
+ 1 << 24);
+ } while (i++ < 34);
+ }
+
+ intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0);
+ intel_de_write_dsb(display, dsb,
+ PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0);
+ }
+}
+
+static void
+xelpd_plane_load_luts(struct intel_dsb *dsb, const struct intel_plane_state *plane_state)
+{
+ if (plane_state->hw.degamma_lut)
+ xelpd_program_plane_pre_csc_lut(dsb, plane_state);
+
+ if (plane_state->hw.gamma_lut)
+ xelpd_program_plane_post_csc_lut(dsb, plane_state);
+}
+
+static u32 glk_3dlut_10(const struct drm_color_lut32 *color)
+{
+ return REG_FIELD_PREP(LUT_3D_DATA_RED_MASK, drm_color_lut32_extract(color->red, 10)) |
+ REG_FIELD_PREP(LUT_3D_DATA_GREEN_MASK, drm_color_lut32_extract(color->green, 10)) |
+ REG_FIELD_PREP(LUT_3D_DATA_BLUE_MASK, drm_color_lut32_extract(color->blue, 10));
+}
+
+static void glk_load_lut_3d(struct intel_dsb *dsb,
+ struct intel_crtc *crtc,
+ const struct drm_property_blob *blob)
+{
+ struct intel_display *display = to_intel_display(crtc->base.dev);
+ const struct drm_color_lut32 *lut = blob->data;
+ int i, lut_size = drm_color_lut32_size(blob);
+ enum pipe pipe = crtc->pipe;
+
+ if (!dsb && intel_de_read(display, LUT_3D_CTL(pipe)) & LUT_3D_READY) {
+ drm_err(display->drm, "[CRTC:%d:%s] 3D LUT not ready, not loading LUTs\n",
+ crtc->base.base.id, crtc->base.name);
+ return;
+ }
+
+ intel_de_write_dsb(display, dsb, LUT_3D_INDEX(pipe), LUT_3D_AUTO_INCREMENT);
+ for (i = 0; i < lut_size; i++)
+ intel_de_write_dsb(display, dsb, LUT_3D_DATA(pipe), glk_3dlut_10(&lut[i]));
+ intel_de_write_dsb(display, dsb, LUT_3D_INDEX(pipe), 0);
+}
+
+static void glk_lut_3d_commit(struct intel_dsb *dsb, struct intel_crtc *crtc, bool enable)
+{
+ struct intel_display *display = to_intel_display(crtc);
+ enum pipe pipe = crtc->pipe;
+ u32 val = 0;
+
+ if (!dsb && intel_de_read(display, LUT_3D_CTL(pipe)) & LUT_3D_READY) {
+ drm_err(display->drm, "[CRTC:%d:%s] 3D LUT not ready, not committing change\n",
+ crtc->base.base.id, crtc->base.name);
+ return;
+ }
+
+ if (enable)
+ val = LUT_3D_ENABLE | LUT_3D_READY | LUT_3D_BIND_PLANE_1;
+
+ intel_de_write_dsb(display, dsb, LUT_3D_CTL(pipe), val);
+}
+
static const struct intel_color_funcs chv_color_funcs = {
.color_check = chv_color_check,
.color_commit_arm = i9xx_color_commit_arm,
@@ -3882,6 +4155,8 @@ static const struct intel_color_funcs tgl_color_funcs = {
.lut_equal = icl_lut_equal,
.read_csc = icl_read_csc,
.get_config = skl_get_config,
+ .load_plane_csc_matrix = xelpd_load_plane_csc_matrix,
+ .load_plane_luts = xelpd_plane_load_luts,
};
static const struct intel_color_funcs icl_color_funcs = {
@@ -3962,6 +4237,67 @@ static const struct intel_color_funcs ilk_color_funcs = {
.get_config = ilk_get_config,
};
+void intel_color_plane_commit_arm(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+ struct intel_crtc *crtc = to_intel_crtc(plane_state->uapi.crtc);
+
+ if (crtc && intel_color_crtc_has_3dlut(display, crtc->pipe))
+ glk_lut_3d_commit(dsb, crtc, !!plane_state->hw.lut_3d);
+}
+
+static void
+intel_color_load_plane_csc_matrix(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+
+ if (display->funcs.color->load_plane_csc_matrix)
+ display->funcs.color->load_plane_csc_matrix(dsb, plane_state);
+}
+
+static void
+intel_color_load_plane_luts(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+
+ if (display->funcs.color->load_plane_luts)
+ display->funcs.color->load_plane_luts(dsb, plane_state);
+}
+
+bool
+intel_color_crtc_has_3dlut(struct intel_display *display, enum pipe pipe)
+{
+ if (DISPLAY_VER(display) >= 12)
+ return pipe == PIPE_A || pipe == PIPE_B;
+ else
+ return false;
+}
+
+static void
+intel_color_load_3dlut(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+ struct intel_crtc *crtc = to_intel_crtc(plane_state->uapi.crtc);
+
+ if (crtc && intel_color_crtc_has_3dlut(display, crtc->pipe))
+ glk_load_lut_3d(dsb, crtc, plane_state->hw.lut_3d);
+}
+
+void intel_color_plane_program_pipeline(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state)
+{
+ if (plane_state->hw.ctm)
+ intel_color_load_plane_csc_matrix(dsb, plane_state);
+ if (plane_state->hw.degamma_lut || plane_state->hw.gamma_lut)
+ intel_color_load_plane_luts(dsb, plane_state);
+ if (plane_state->hw.lut_3d)
+ intel_color_load_3dlut(dsb, plane_state);
+}
+
void intel_color_crtc_init(struct intel_crtc *crtc)
{
struct intel_display *display = to_intel_display(crtc);
diff --git a/drivers/gpu/drm/i915/display/intel_color.h b/drivers/gpu/drm/i915/display/intel_color.h
index bf7a12ce9df0..c21b9bdf7bb8 100644
--- a/drivers/gpu/drm/i915/display/intel_color.h
+++ b/drivers/gpu/drm/i915/display/intel_color.h
@@ -13,7 +13,9 @@ struct intel_crtc_state;
struct intel_crtc;
struct intel_display;
struct intel_dsb;
+struct intel_plane_state;
struct drm_property_blob;
+enum pipe;
void intel_color_init_hooks(struct intel_display *display);
int intel_color_init(struct intel_display *display);
@@ -40,5 +42,9 @@ bool intel_color_lut_equal(const struct intel_crtc_state *crtc_state,
const struct drm_property_blob *blob2,
bool is_pre_csc_lut);
void intel_color_assert_luts(const struct intel_crtc_state *crtc_state);
-
+void intel_color_plane_program_pipeline(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state);
+void intel_color_plane_commit_arm(struct intel_dsb *dsb,
+ const struct intel_plane_state *plane_state);
+bool intel_color_crtc_has_3dlut(struct intel_display *display, enum pipe pipe);
#endif /* __INTEL_COLOR_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_color_pipeline.c b/drivers/gpu/drm/i915/display/intel_color_pipeline.c
new file mode 100644
index 000000000000..942d9b9c93ce
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_color_pipeline.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+#include "intel_color.h"
+#include "intel_colorop.h"
+#include "intel_color_pipeline.h"
+#include "intel_de.h"
+#include "intel_display_types.h"
+#include "skl_universal_plane.h"
+
+#define MAX_COLOR_PIPELINES 1
+#define PLANE_DEGAMMA_SIZE 128
+#define PLANE_GAMMA_SIZE 32
+
+static
+int _intel_color_pipeline_plane_init(struct drm_plane *plane, struct drm_prop_enum_list *list,
+ enum pipe pipe)
+{
+ struct drm_device *dev = plane->dev;
+ struct intel_display *display = to_intel_display(dev);
+ struct drm_colorop *prev_op;
+ struct intel_colorop *colorop;
+ int ret;
+
+ colorop = intel_colorop_create(INTEL_PLANE_CB_PRE_CSC_LUT);
+
+ ret = drm_plane_colorop_curve_1d_lut_init(dev, &colorop->base, plane,
+ PLANE_DEGAMMA_SIZE,
+ DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+
+ if (ret)
+ return ret;
+
+ list->type = colorop->base.base.id;
+ list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", colorop->base.base.id);
+
+ /* TODO: handle failures and clean up */
+ prev_op = &colorop->base;
+
+ if (DISPLAY_VER(display) >= 35 &&
+ intel_color_crtc_has_3dlut(display, pipe) &&
+ plane->type == DRM_PLANE_TYPE_PRIMARY) {
+ colorop = intel_colorop_create(INTEL_PLANE_CB_3DLUT);
+
+ ret = drm_plane_colorop_3dlut_init(dev, &colorop->base, plane, 17,
+ DRM_COLOROP_LUT3D_INTERPOLATION_TETRAHEDRAL,
+ true);
+ if (ret)
+ return ret;
+
+ drm_colorop_set_next_property(prev_op, &colorop->base);
+
+ prev_op = &colorop->base;
+ }
+
+ colorop = intel_colorop_create(INTEL_PLANE_CB_CSC);
+ ret = drm_plane_colorop_ctm_3x4_init(dev, &colorop->base, plane,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ return ret;
+
+ drm_colorop_set_next_property(prev_op, &colorop->base);
+ prev_op = &colorop->base;
+
+ colorop = intel_colorop_create(INTEL_PLANE_CB_POST_CSC_LUT);
+ ret = drm_plane_colorop_curve_1d_lut_init(dev, &colorop->base, plane,
+ PLANE_GAMMA_SIZE,
+ DRM_COLOROP_LUT1D_INTERPOLATION_LINEAR,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ return ret;
+
+ drm_colorop_set_next_property(prev_op, &colorop->base);
+
+ return 0;
+}
+
+int intel_color_pipeline_plane_init(struct drm_plane *plane, enum pipe pipe)
+{
+ struct drm_device *dev = plane->dev;
+ struct intel_display *display = to_intel_display(dev);
+ struct drm_prop_enum_list pipelines[MAX_COLOR_PIPELINES];
+ int len = 0;
+ int ret;
+
+ /* Currently expose pipeline only for HDR planes */
+ if (!icl_is_hdr_plane(display, to_intel_plane(plane)->id))
+ return 0;
+
+ /* Add pipeline consisting of transfer functions */
+ ret = _intel_color_pipeline_plane_init(plane, &pipelines[len], pipe);
+ if (ret)
+ return ret;
+ len++;
+
+ return drm_plane_create_color_pipeline_property(plane, pipelines, len);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_color_pipeline.h b/drivers/gpu/drm/i915/display/intel_color_pipeline.h
new file mode 100644
index 000000000000..a457d306da7f
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_color_pipeline.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __INTEL_COLOR_PIPELINE_H__
+#define __INTEL_COLOR_PIPELINE_H__
+
+struct drm_plane;
+enum pipe;
+
+int intel_color_pipeline_plane_init(struct drm_plane *plane, enum pipe pipe);
+
+#endif /* __INTEL_COLOR_PIPELINE_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_color_regs.h b/drivers/gpu/drm/i915/display/intel_color_regs.h
index 8eb643cfead7..c370b6029369 100644
--- a/drivers/gpu/drm/i915/display/intel_color_regs.h
+++ b/drivers/gpu/drm/i915/display/intel_color_regs.h
@@ -316,4 +316,33 @@
#define SKL_BOTTOM_COLOR_CSC_ENABLE REG_BIT(30)
#define SKL_BOTTOM_COLOR(pipe) _MMIO_PIPE(pipe, _SKL_BOTTOM_COLOR_A, _SKL_BOTTOM_COLOR_B)
+/* 3D LUT */
+#define _LUT_3D_CTL_A 0x490A4
+#define _LUT_3D_CTL_B 0x491A4
+#define LUT_3D_CTL(pipe) _MMIO_PIPE(pipe, _LUT_3D_CTL_A, _LUT_3D_CTL_B)
+#define LUT_3D_ENABLE REG_BIT(31)
+#define LUT_3D_READY REG_BIT(30)
+#define LUT_3D_BINDING_MASK REG_GENMASK(23, 22)
+#define LUT_3D_BIND_PIPE REG_FIELD_PREP(LUT_3D_BINDING_MASK, 0)
+#define LUT_3D_BIND_PLANE_1 REG_FIELD_PREP(LUT_3D_BINDING_MASK, 1)
+#define LUT_3D_BIND_PLANE_2 REG_FIELD_PREP(LUT_3D_BINDING_MASK, 2)
+#define LUT_3D_BIND_PLANE_3 REG_FIELD_PREP(LUT_3D_BINDING_MASK, 3)
+
+#define _LUT_3D_INDEX_A 0x490A8
+#define _LUT_3D_INDEX_B 0x491A8
+#define LUT_3D_INDEX(pipe) _MMIO_PIPE(pipe, _LUT_3D_INDEX_A, _LUT_3D_INDEX_B)
+#define LUT_3D_AUTO_INCREMENT REG_BIT(13)
+#define LUT_3D_INDEX_VALUE_MASK REG_GENMASK(12, 0)
+#define LUT_3D_INDEX_VALUE(x) REG_FIELD_PREP(LUT_3D_INDEX_VALUE_MASK, (x))
+
+#define _LUT_3D_DATA_A 0x490AC
+#define _LUT_3D_DATA_B 0x491AC
+#define LUT_3D_DATA(pipe) _MMIO_PIPE(pipe, _LUT_3D_DATA_A, _LUT_3D_DATA_B)
+#define LUT_3D_DATA_RED_MASK REG_GENMASK(29, 20)
+#define LUT_3D_DATA_GREEN_MASK REG_GENMASK(19, 10)
+#define LUT_3D_DATA_BLUE_MASK REG_GENMASK(9, 0)
+#define LUT_3D_DATA_RED(x) REG_FIELD_PREP(LUT_3D_DATA_RED_MASK, (x))
+#define LUT_3D_DATA_GREEN(x) REG_FIELD_PREP(LUT_3D_DATA_GREEN_MASK, (x))
+#define LUT_3D_DATA_BLUE(x) REG_FIELD_PREP(LUT_3D_DATA_BLUE_MASK, (x))
+
#endif /* __INTEL_COLOR_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_colorop.c b/drivers/gpu/drm/i915/display/intel_colorop.c
new file mode 100644
index 000000000000..f2fc0d8780ce
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_colorop.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+#include "intel_colorop.h"
+
+struct intel_colorop *to_intel_colorop(struct drm_colorop *colorop)
+{
+ return container_of(colorop, struct intel_colorop, base);
+}
+
+struct intel_colorop *intel_colorop_alloc(void)
+{
+ struct intel_colorop *colorop;
+
+ colorop = kzalloc(sizeof(*colorop), GFP_KERNEL);
+ if (!colorop)
+ return ERR_PTR(-ENOMEM);
+
+ return colorop;
+}
+
+struct intel_colorop *intel_colorop_create(enum intel_color_block id)
+{
+ struct intel_colorop *colorop;
+
+ colorop = intel_colorop_alloc();
+
+ if (IS_ERR(colorop))
+ return colorop;
+
+ colorop->id = id;
+
+ return colorop;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_colorop.h b/drivers/gpu/drm/i915/display/intel_colorop.h
new file mode 100644
index 000000000000..21d58eb9f3d0
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_colorop.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __INTEL_COLOROP_H__
+#define __INTEL_COLOROP_H__
+
+#include "intel_display_types.h"
+
+struct intel_colorop *to_intel_colorop(struct drm_colorop *colorop);
+struct intel_colorop *intel_colorop_alloc(void);
+struct intel_colorop *intel_colorop_create(enum intel_color_block id);
+
+#endif /* __INTEL_COLOROP_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_combo_phy.c b/drivers/gpu/drm/i915/display/intel_combo_phy.c
index 112749f97c26..f401558ac14e 100644
--- a/drivers/gpu/drm/i915/display/intel_combo_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_combo_phy.c
@@ -5,12 +5,12 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_combo_phy.h"
#include "intel_combo_phy_regs.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#define for_each_combo_phy(__display, __phy) \
for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++) \
diff --git a/drivers/gpu/drm/i915/display/intel_connector.c b/drivers/gpu/drm/i915/display/intel_connector.c
index 6a55854db5b6..913d90a7a508 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.c
+++ b/drivers/gpu/drm/i915/display/intel_connector.c
@@ -28,10 +28,11 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "i915_drv.h"
-#include "i915_utils.h"
+#include "i915_utils.h" /* for i915_inject_probe_failure() */
#include "intel_connector.h"
#include "intel_display_core.h"
#include "intel_display_debugfs.h"
diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c
index 31e68047f217..82e89cdbe5a5 100644
--- a/drivers/gpu/drm/i915/display/intel_crt.c
+++ b/drivers/gpu/drm/i915/display/intel_crt.c
@@ -498,10 +498,10 @@ static bool ilk_crt_detect_hotplug(struct drm_connector *connector)
intel_de_write(display, crt->adpa_reg, adpa);
- if (intel_de_wait_for_clear(display,
- crt->adpa_reg,
- ADPA_CRT_HOTPLUG_FORCE_TRIGGER,
- 1000))
+ if (intel_de_wait_for_clear_ms(display,
+ crt->adpa_reg,
+ ADPA_CRT_HOTPLUG_FORCE_TRIGGER,
+ 1000))
drm_dbg_kms(display->drm,
"timed out waiting for FORCE_TRIGGER");
@@ -553,8 +553,8 @@ static bool valleyview_crt_detect_hotplug(struct drm_connector *connector)
intel_de_write(display, crt->adpa_reg, adpa);
- if (intel_de_wait_for_clear(display, crt->adpa_reg,
- ADPA_CRT_HOTPLUG_FORCE_TRIGGER, 1000)) {
+ if (intel_de_wait_for_clear_ms(display, crt->adpa_reg,
+ ADPA_CRT_HOTPLUG_FORCE_TRIGGER, 1000)) {
drm_dbg_kms(display->drm,
"timed out waiting for FORCE_TRIGGER");
intel_de_write(display, crt->adpa_reg, save_adpa);
@@ -604,8 +604,8 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
CRT_HOTPLUG_FORCE_DETECT,
CRT_HOTPLUG_FORCE_DETECT);
/* wait for FORCE_DETECT to go off */
- if (intel_de_wait_for_clear(display, PORT_HOTPLUG_EN(display),
- CRT_HOTPLUG_FORCE_DETECT, 1000))
+ if (intel_de_wait_for_clear_ms(display, PORT_HOTPLUG_EN(display),
+ CRT_HOTPLUG_FORCE_DETECT, 1000))
drm_dbg_kms(display->drm,
"timed out waiting for FORCE_DETECT to go off");
}
diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c
index a187db6df2d3..9d2a23c96c61 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc.c
@@ -9,6 +9,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <drm/drm_vblank_work.h>
@@ -84,8 +85,13 @@ u32 intel_crtc_get_vblank_counter(struct intel_crtc *crtc)
if (!crtc->active)
return 0;
- if (!vblank->max_vblank_count)
- return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
+ if (!vblank->max_vblank_count) {
+ /* On preempt-rt we cannot take the vblank spinlock since this function is called from tracepoints */
+ if (IS_ENABLED(CONFIG_PREEMPT_RT))
+ return (u32)drm_crtc_vblank_count(&crtc->base);
+ else
+ return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
+ }
return crtc->base.funcs->get_vblank_counter(&crtc->base);
}
@@ -390,6 +396,9 @@ int intel_crtc_init(struct intel_display *display, enum pipe pipe)
drm_WARN_ON(display->drm, drm_crtc_index(&crtc->base) != crtc->pipe);
+ if (HAS_CASF(display))
+ drm_crtc_create_sharpness_strength_property(&crtc->base);
+
return 0;
fail:
@@ -748,3 +757,89 @@ void intel_pipe_update_end(struct intel_atomic_state *state,
out:
intel_psr_unlock(new_crtc_state);
}
+
+bool intel_crtc_enable_changed(const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state)
+{
+ return old_crtc_state->hw.enable != new_crtc_state->hw.enable;
+}
+
+bool intel_any_crtc_enable_changed(struct intel_atomic_state *state)
+{
+ const struct intel_crtc_state *old_crtc_state, *new_crtc_state;
+ struct intel_crtc *crtc;
+ int i;
+
+ for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
+ new_crtc_state, i) {
+ if (intel_crtc_enable_changed(old_crtc_state, new_crtc_state))
+ return true;
+ }
+
+ return false;
+}
+
+bool intel_crtc_active_changed(const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state)
+{
+ return old_crtc_state->hw.active != new_crtc_state->hw.active;
+}
+
+bool intel_any_crtc_active_changed(struct intel_atomic_state *state)
+{
+ const struct intel_crtc_state *old_crtc_state, *new_crtc_state;
+ struct intel_crtc *crtc;
+ int i;
+
+ for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
+ new_crtc_state, i) {
+ if (intel_crtc_active_changed(old_crtc_state, new_crtc_state))
+ return true;
+ }
+
+ return false;
+}
+
+unsigned int intel_crtc_bw_num_active_planes(const struct intel_crtc_state *crtc_state)
+{
+ /*
+ * We assume cursors are small enough
+ * to not cause bandwidth problems.
+ */
+ return hweight8(crtc_state->active_planes & ~BIT(PLANE_CURSOR));
+}
+
+unsigned int intel_crtc_bw_data_rate(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ unsigned int data_rate = 0;
+ enum plane_id plane_id;
+
+ for_each_plane_id_on_crtc(crtc, plane_id) {
+ /*
+ * We assume cursors are small enough
+ * to not cause bandwidth problems.
+ */
+ if (plane_id == PLANE_CURSOR)
+ continue;
+
+ data_rate += crtc_state->data_rate[plane_id];
+
+ if (DISPLAY_VER(display) < 11)
+ data_rate += crtc_state->data_rate_y[plane_id];
+ }
+
+ return data_rate;
+}
+
+/* "Maximum Pipe Read Bandwidth" */
+int intel_crtc_bw_min_cdclk(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ if (DISPLAY_VER(display) < 12)
+ return 0;
+
+ return DIV_ROUND_UP_ULL(mul_u32_u32(intel_crtc_bw_data_rate(crtc_state), 10), 512);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_crtc.h b/drivers/gpu/drm/i915/display/intel_crtc.h
index 8c14ff8b391e..07917e8a9ae3 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.h
+++ b/drivers/gpu/drm/i915/display/intel_crtc.h
@@ -58,4 +58,15 @@ void intel_wait_for_vblank_if_active(struct intel_display *display,
enum pipe pipe);
void intel_crtc_wait_for_next_vblank(struct intel_crtc *crtc);
+bool intel_any_crtc_enable_changed(struct intel_atomic_state *state);
+bool intel_crtc_enable_changed(const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state);
+bool intel_any_crtc_active_changed(struct intel_atomic_state *state);
+bool intel_crtc_active_changed(const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state);
+
+unsigned int intel_crtc_bw_num_active_planes(const struct intel_crtc_state *crtc_state);
+unsigned int intel_crtc_bw_data_rate(const struct intel_crtc_state *crtc_state);
+int intel_crtc_bw_min_cdclk(const struct intel_crtc_state *crtc_state);
+
#endif
diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c
index 0c7f91046996..c2a6217c2262 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c
@@ -289,10 +289,9 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config,
drm_printf(&p, "scanline offset: %d\n",
intel_crtc_scanline_offset(pipe_config));
- drm_printf(&p, "vblank delay: %d, framestart delay: %d, MSA timing delay: %d\n",
- pipe_config->hw.adjusted_mode.crtc_vblank_start -
- pipe_config->hw.adjusted_mode.crtc_vdisplay,
- pipe_config->framestart_delay, pipe_config->msa_timing_delay);
+ drm_printf(&p, "framestart delay: %d, MSA timing delay: %d, set context latency: %d\n",
+ pipe_config->framestart_delay, pipe_config->msa_timing_delay,
+ pipe_config->set_context_latency);
drm_printf(&p, "vrr: %s, fixed rr: %s, vmin: %d, vmax: %d, flipline: %d, pipeline full: %d, guardband: %d vsync start: %d, vsync end: %d\n",
str_yes_no(pipe_config->vrr.enable),
@@ -313,9 +312,9 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config,
drm_printf(&p, "pipe mode: " DRM_MODE_FMT "\n",
DRM_MODE_ARG(&pipe_config->hw.pipe_mode));
intel_dump_crtc_timings(&p, &pipe_config->hw.pipe_mode);
- drm_printf(&p, "port clock: %d, pipe src: " DRM_RECT_FMT ", pixel rate %d\n",
+ drm_printf(&p, "port clock: %d, pipe src: " DRM_RECT_FMT ", pixel rate %d, min cdclk %d\n",
pipe_config->port_clock, DRM_RECT_ARG(&pipe_config->pipe_src),
- pipe_config->pixel_rate);
+ pipe_config->pixel_rate, pipe_config->min_cdclk);
drm_printf(&p, "linetime: %d, ips linetime: %d\n",
pipe_config->linetime, pipe_config->ips_linetime);
@@ -373,6 +372,11 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config,
intel_vdsc_state_dump(&p, 0, pipe_config);
+ drm_printf(&p, "sharpness strength: %d, sharpness tap size: %d, sharpness enable: %d\n",
+ pipe_config->hw.casf_params.strength,
+ pipe_config->hw.casf_params.win_size,
+ pipe_config->hw.casf_params.casf_enable);
+
dump_planes:
if (!state)
return;
diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c
index d4d181f9dca5..a10b2425b94d 100644
--- a/drivers/gpu/drm/i915/display/intel_cursor.c
+++ b/drivers/gpu/drm/i915/display/intel_cursor.c
@@ -12,13 +12,13 @@
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_cursor.h"
#include "intel_cursor_regs.h"
#include "intel_de.h"
#include "intel_display.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_fb.h"
#include "intel_fb_pin.h"
#include "intel_frontbuffer.h"
@@ -182,8 +182,8 @@ static int intel_check_cursor(struct intel_crtc_state *crtc_state,
static unsigned int
i845_cursor_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
return 2048;
}
@@ -343,8 +343,8 @@ static bool i845_cursor_get_hw_state(struct intel_plane *plane,
static unsigned int
i9xx_cursor_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
return plane->base.dev->mode_config.cursor_width * 4;
}
@@ -662,7 +662,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb,
cntl = plane_state->ctl |
i9xx_cursor_ctl_crtc(crtc_state);
- if (width != height)
+ if (DISPLAY_VER(display) < 14 && width != height)
fbc_ctl = CUR_FBC_EN | CUR_FBC_HEIGHT(height - 1);
base = plane_state->surf;
@@ -1092,3 +1092,23 @@ fail:
return ERR_PTR(ret);
}
+
+void intel_cursor_mode_config_init(struct intel_display *display)
+{
+ struct drm_mode_config *mode_config = &display->drm->mode_config;
+
+ if (display->platform.i845g) {
+ mode_config->cursor_width = 64;
+ mode_config->cursor_height = 1023;
+ } else if (display->platform.i865g) {
+ mode_config->cursor_width = 512;
+ mode_config->cursor_height = 1023;
+ } else if (display->platform.i830 || display->platform.i85x ||
+ display->platform.i915g || display->platform.i915gm) {
+ mode_config->cursor_width = 64;
+ mode_config->cursor_height = 64;
+ } else {
+ mode_config->cursor_width = 256;
+ mode_config->cursor_height = 256;
+ }
+}
diff --git a/drivers/gpu/drm/i915/display/intel_cursor.h b/drivers/gpu/drm/i915/display/intel_cursor.h
index 65a9e7eb88c2..7c269d7381ad 100644
--- a/drivers/gpu/drm/i915/display/intel_cursor.h
+++ b/drivers/gpu/drm/i915/display/intel_cursor.h
@@ -17,4 +17,6 @@ intel_cursor_plane_create(struct intel_display *display,
void intel_cursor_unpin_work(struct kthread_work *base);
+void intel_cursor_mode_config_init(struct intel_display *display);
+
#endif
diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c
index 801235a5bc0a..d98b4cf6b60e 100644
--- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c
@@ -8,7 +8,6 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_alpm.h"
#include "intel_cx0_phy.h"
#include "intel_cx0_phy_regs.h"
@@ -16,16 +15,15 @@
#include "intel_ddi_buf_trans.h"
#include "intel_de.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_hdmi.h"
+#include "intel_lt_phy.h"
#include "intel_panel.h"
#include "intel_psr.h"
#include "intel_snps_hdmi_pll.h"
#include "intel_tc.h"
-#define MB_WRITE_COMMITTED true
-#define MB_WRITE_UNCOMMITTED false
-
#define for_each_cx0_lane_in_mask(__lane_mask, __lane) \
for ((__lane) = 0; (__lane) < 2; (__lane)++) \
for_each_if((__lane_mask) & BIT(__lane))
@@ -39,14 +37,12 @@ bool intel_encoder_is_c10phy(struct intel_encoder *encoder)
struct intel_display *display = to_intel_display(encoder);
enum phy phy = intel_encoder_to_phy(encoder);
- /* PTL doesn't have a PHY connected to PORT B; as such,
- * there will never be a case where PTL uses PHY B.
- * WCL uses PORT A and B with the C10 PHY.
- * Reusing the condition for WCL and extending it for PORT B
- * should not cause any issues for PTL.
- */
- if (display->platform.pantherlake && phy < PHY_C)
- return true;
+ if (display->platform.pantherlake) {
+ if (display->platform.pantherlake_wildcatlake)
+ return phy <= PHY_B;
+ else
+ return phy == PHY_A;
+ }
if ((display->platform.lunarlake || display->platform.meteorlake) && phy < PHY_C)
return true;
@@ -130,8 +126,8 @@ static void intel_cx0_phy_transaction_end(struct intel_encoder *encoder, intel_w
intel_display_power_put(display, POWER_DOMAIN_DC_OFF, wakeref);
}
-static void intel_clear_response_ready_flag(struct intel_encoder *encoder,
- int lane)
+void intel_clear_response_ready_flag(struct intel_encoder *encoder,
+ int lane)
{
struct intel_display *display = to_intel_display(encoder);
@@ -140,7 +136,7 @@ static void intel_clear_response_ready_flag(struct intel_encoder *encoder,
0, XELPDP_PORT_P2M_RESPONSE_READY | XELPDP_PORT_P2M_ERROR_SET);
}
-static void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane)
+void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane)
{
struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port;
@@ -149,9 +145,9 @@ static void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane)
intel_de_write(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
XELPDP_PORT_M2P_TRANSACTION_RESET);
- if (intel_de_wait_for_clear(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
- XELPDP_PORT_M2P_TRANSACTION_RESET,
- XELPDP_MSGBUS_TIMEOUT_SLOW)) {
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
+ XELPDP_PORT_M2P_TRANSACTION_RESET,
+ XELPDP_MSGBUS_TIMEOUT_MS)) {
drm_err_once(display->drm,
"Failed to bring PHY %c to idle.\n",
phy_name(phy));
@@ -161,19 +157,17 @@ static void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane)
intel_clear_response_ready_flag(encoder, lane);
}
-static int intel_cx0_wait_for_ack(struct intel_encoder *encoder,
- int command, int lane, u32 *val)
+int intel_cx0_wait_for_ack(struct intel_encoder *encoder,
+ int command, int lane, u32 *val)
{
struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port;
enum phy phy = intel_encoder_to_phy(encoder);
- if (intel_de_wait_custom(display,
- XELPDP_PORT_P2M_MSGBUS_STATUS(display, port, lane),
- XELPDP_PORT_P2M_RESPONSE_READY,
- XELPDP_PORT_P2M_RESPONSE_READY,
- XELPDP_MSGBUS_TIMEOUT_FAST_US,
- XELPDP_MSGBUS_TIMEOUT_SLOW, val)) {
+ if (intel_de_wait_ms(display, XELPDP_PORT_P2M_MSGBUS_STATUS(display, port, lane),
+ XELPDP_PORT_P2M_RESPONSE_READY,
+ XELPDP_PORT_P2M_RESPONSE_READY,
+ XELPDP_MSGBUS_TIMEOUT_MS, val)) {
drm_dbg_kms(display->drm,
"PHY %c Timeout waiting for message ACK. Status: 0x%x\n",
phy_name(phy), *val);
@@ -218,9 +212,9 @@ static int __intel_cx0_read_once(struct intel_encoder *encoder,
int ack;
u32 val;
- if (intel_de_wait_for_clear(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
- XELPDP_PORT_M2P_TRANSACTION_PENDING,
- XELPDP_MSGBUS_TIMEOUT_SLOW)) {
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
+ XELPDP_PORT_M2P_TRANSACTION_PENDING,
+ XELPDP_MSGBUS_TIMEOUT_MS)) {
drm_dbg_kms(display->drm,
"PHY %c Timeout waiting for previous transaction to complete. Reset the bus and retry.\n", phy_name(phy));
intel_cx0_bus_reset(encoder, lane);
@@ -273,8 +267,7 @@ static u8 __intel_cx0_read(struct intel_encoder *encoder,
return 0;
}
-static u8 intel_cx0_read(struct intel_encoder *encoder,
- u8 lane_mask, u16 addr)
+u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr)
{
int lane = lane_mask_to_lane(lane_mask);
@@ -290,9 +283,9 @@ static int __intel_cx0_write_once(struct intel_encoder *encoder,
int ack;
u32 val;
- if (intel_de_wait_for_clear(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
- XELPDP_PORT_M2P_TRANSACTION_PENDING,
- XELPDP_MSGBUS_TIMEOUT_SLOW)) {
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
+ XELPDP_PORT_M2P_TRANSACTION_PENDING,
+ XELPDP_MSGBUS_TIMEOUT_MS)) {
drm_dbg_kms(display->drm,
"PHY %c Timeout waiting for previous transaction to complete. Resetting the bus.\n", phy_name(phy));
intel_cx0_bus_reset(encoder, lane);
@@ -306,9 +299,9 @@ static int __intel_cx0_write_once(struct intel_encoder *encoder,
XELPDP_PORT_M2P_DATA(data) |
XELPDP_PORT_M2P_ADDRESS(addr));
- if (intel_de_wait_for_clear(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
- XELPDP_PORT_M2P_TRANSACTION_PENDING,
- XELPDP_MSGBUS_TIMEOUT_SLOW)) {
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
+ XELPDP_PORT_M2P_TRANSACTION_PENDING,
+ XELPDP_MSGBUS_TIMEOUT_MS)) {
drm_dbg_kms(display->drm,
"PHY %c Timeout waiting for write to complete. Resetting the bus.\n", phy_name(phy));
intel_cx0_bus_reset(encoder, lane);
@@ -361,8 +354,8 @@ static void __intel_cx0_write(struct intel_encoder *encoder,
"PHY %c Write %04x failed after %d retries.\n", phy_name(phy), addr, i);
}
-static void intel_cx0_write(struct intel_encoder *encoder,
- u8 lane_mask, u16 addr, u8 data, bool committed)
+void intel_cx0_write(struct intel_encoder *encoder,
+ u8 lane_mask, u16 addr, u8 data, bool committed)
{
int lane;
@@ -414,8 +407,8 @@ static void __intel_cx0_rmw(struct intel_encoder *encoder,
__intel_cx0_write(encoder, lane, addr, val, committed);
}
-static void intel_cx0_rmw(struct intel_encoder *encoder,
- u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed)
+void intel_cx0_rmw(struct intel_encoder *encoder,
+ u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed)
{
u8 lane;
@@ -2105,6 +2098,9 @@ static int intel_c10pll_calc_state(struct intel_crtc_state *crtc_state,
return 0;
}
+static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder,
+ const struct intel_c10pll_state *pll_state);
+
static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder,
struct intel_c10pll_state *pll_state)
{
@@ -2129,6 +2125,8 @@ static void intel_c10pll_readout_hw_state(struct intel_encoder *encoder,
pll_state->tx = intel_cx0_read(encoder, lane, PHY_C10_VDR_TX(0));
intel_cx0_phy_transaction_end(encoder, wakeref);
+
+ pll_state->clock = intel_c10pll_calc_port_clock(encoder, pll_state);
}
static void intel_c10_pll_program(struct intel_display *display,
@@ -2587,20 +2585,6 @@ static bool is_dp2(u32 clock)
return false;
}
-static bool is_hdmi_frl(u32 clock)
-{
- switch (clock) {
- case 300000: /* 3 Gbps */
- case 600000: /* 6 Gbps */
- case 800000: /* 8 Gbps */
- case 1000000: /* 10 Gbps */
- case 1200000: /* 12 Gbps */
- return true;
- default:
- return false;
- }
-}
-
static bool intel_c20_protocol_switch_valid(struct intel_encoder *encoder)
{
struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
@@ -2614,7 +2598,7 @@ static int intel_get_c20_custom_width(u32 clock, bool dp)
{
if (dp && is_dp2(clock))
return 2;
- else if (is_hdmi_frl(clock))
+ else if (intel_hdmi_is_frl(clock))
return 1;
else
return 0;
@@ -2626,11 +2610,13 @@ static void intel_c20_pll_program(struct intel_display *display,
bool is_dp, int port_clock)
{
u8 owned_lane_mask = intel_cx0_get_owned_lane_mask(encoder);
+ u8 serdes;
bool cntx;
int i;
/* 1. Read current context selection */
- cntx = intel_cx0_read(encoder, INTEL_CX0_LANE0, PHY_C20_VDR_CUSTOM_SERDES_RATE) & BIT(0);
+ cntx = intel_cx0_read(encoder, INTEL_CX0_LANE0, PHY_C20_VDR_CUSTOM_SERDES_RATE) &
+ PHY_C20_CONTEXT_TOGGLE;
/*
* 2. If there is a protocol switch from HDMI to DP or vice versa, clear
@@ -2700,28 +2686,31 @@ static void intel_c20_pll_program(struct intel_display *display,
MB_WRITE_COMMITTED);
/* 5. For DP or 6. For HDMI */
- if (is_dp) {
- intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE,
- BIT(6) | PHY_C20_CUSTOM_SERDES_MASK,
- BIT(6) | PHY_C20_CUSTOM_SERDES(intel_c20_get_dp_rate(port_clock)),
- MB_WRITE_COMMITTED);
- } else {
- intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE,
- BIT(7) | PHY_C20_CUSTOM_SERDES_MASK,
- is_hdmi_frl(port_clock) ? BIT(7) : 0,
- MB_WRITE_COMMITTED);
+ serdes = 0;
+ if (is_dp)
+ serdes = PHY_C20_IS_DP |
+ PHY_C20_DP_RATE(intel_c20_get_dp_rate(port_clock));
+ else if (intel_hdmi_is_frl(port_clock))
+ serdes = PHY_C20_IS_HDMI_FRL;
- intel_cx0_write(encoder, INTEL_CX0_BOTH_LANES, PHY_C20_VDR_HDMI_RATE,
- intel_c20_get_hdmi_rate(port_clock),
- MB_WRITE_COMMITTED);
- }
+ intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE,
+ PHY_C20_IS_DP | PHY_C20_DP_RATE_MASK | PHY_C20_IS_HDMI_FRL,
+ serdes,
+ MB_WRITE_COMMITTED);
+
+ if (!is_dp)
+ intel_cx0_rmw(encoder, INTEL_CX0_BOTH_LANES, PHY_C20_VDR_HDMI_RATE,
+ PHY_C20_HDMI_RATE_MASK,
+ intel_c20_get_hdmi_rate(port_clock),
+ MB_WRITE_COMMITTED);
/*
* 7. Write Vendor specific registers to toggle context setting to load
* the updated programming toggle context bit
*/
intel_cx0_rmw(encoder, owned_lane_mask, PHY_C20_VDR_CUSTOM_SERDES_RATE,
- BIT(0), cntx ? 0 : 1, MB_WRITE_COMMITTED);
+ PHY_C20_CONTEXT_TOGGLE, cntx ? 0 : PHY_C20_CONTEXT_TOGGLE,
+ MB_WRITE_COMMITTED);
}
static int intel_c10pll_calc_port_clock(struct intel_encoder *encoder,
@@ -2768,7 +2757,7 @@ static void intel_program_port_clock_ctl(struct intel_encoder *encoder,
val |= XELPDP_FORWARD_CLOCK_UNGATE;
- if (!is_dp && is_hdmi_frl(port_clock))
+ if (!is_dp && intel_hdmi_is_frl(port_clock))
val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_DIV18CLK);
else
val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK);
@@ -2808,8 +2797,8 @@ static u32 intel_cx0_get_powerdown_state(u8 lane_mask, u8 state)
return val;
}
-static void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
- u8 lane_mask, u8 state)
+void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
+ u8 lane_mask, u8 state)
{
struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port;
@@ -2823,9 +2812,9 @@ static void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
/* Wait for pending transactions.*/
for_each_cx0_lane_in_mask(lane_mask, lane)
- if (intel_de_wait_for_clear(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
- XELPDP_PORT_M2P_TRANSACTION_PENDING,
- XELPDP_MSGBUS_TIMEOUT_SLOW)) {
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
+ XELPDP_PORT_M2P_TRANSACTION_PENDING,
+ XELPDP_MSGBUS_TIMEOUT_MS)) {
drm_dbg_kms(display->drm,
"PHY %c Timeout waiting for previous transaction to complete. Reset the bus.\n",
phy_name(phy));
@@ -2837,26 +2826,26 @@ static void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
intel_cx0_get_powerdown_update(lane_mask));
/* Update Timeout Value */
- if (intel_de_wait_custom(display, buf_ctl2_reg,
- intel_cx0_get_powerdown_update(lane_mask), 0,
- XELPDP_PORT_POWERDOWN_UPDATE_TIMEOUT_US, 0, NULL))
+ if (intel_de_wait_for_clear_ms(display, buf_ctl2_reg,
+ intel_cx0_get_powerdown_update(lane_mask),
+ XELPDP_PORT_POWERDOWN_UPDATE_TIMEOUT_MS))
drm_warn(display->drm,
- "PHY %c failed to bring out of Lane reset after %dus.\n",
- phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US);
+ "PHY %c failed to bring out of lane reset\n",
+ phy_name(phy));
}
-static void intel_cx0_setup_powerdown(struct intel_encoder *encoder)
+void intel_cx0_setup_powerdown(struct intel_encoder *encoder)
{
struct intel_display *display = to_intel_display(encoder);
enum port port = encoder->port;
intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
XELPDP_POWER_STATE_READY_MASK,
- XELPDP_POWER_STATE_READY(CX0_P2_STATE_READY));
+ XELPDP_POWER_STATE_READY(XELPDP_P2_STATE_READY));
intel_de_rmw(display, XELPDP_PORT_BUF_CTL3(display, port),
XELPDP_POWER_STATE_ACTIVE_MASK |
XELPDP_PLL_LANE_STAGGERING_DELAY_MASK,
- XELPDP_POWER_STATE_ACTIVE(CX0_P0_STATE_ACTIVE) |
+ XELPDP_POWER_STATE_ACTIVE(XELPDP_P0_STATE_ACTIVE) |
XELPDP_PLL_LANE_STAGGERING_DELAY(0));
}
@@ -2898,48 +2887,47 @@ static void intel_cx0_phy_lane_reset(struct intel_encoder *encoder,
XELPDP_LANE_PHY_CURRENT_STATUS(1))
: XELPDP_LANE_PHY_CURRENT_STATUS(0);
- if (intel_de_wait_custom(display, XELPDP_PORT_BUF_CTL1(display, port),
- XELPDP_PORT_BUF_SOC_PHY_READY,
- XELPDP_PORT_BUF_SOC_PHY_READY,
- XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US, 0, NULL))
+ if (intel_de_wait_for_set_us(display, XELPDP_PORT_BUF_CTL1(display, port),
+ XELPDP_PORT_BUF_SOC_PHY_READY,
+ XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US))
drm_warn(display->drm,
- "PHY %c failed to bring out of SOC reset after %dus.\n",
- phy_name(phy), XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US);
+ "PHY %c failed to bring out of SOC reset\n",
+ phy_name(phy));
intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_pipe_reset,
lane_pipe_reset);
- if (intel_de_wait_custom(display, XELPDP_PORT_BUF_CTL2(display, port),
- lane_phy_current_status, lane_phy_current_status,
- XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL))
+ if (intel_de_wait_for_set_us(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_current_status,
+ XELPDP_PORT_RESET_START_TIMEOUT_US))
drm_warn(display->drm,
- "PHY %c failed to bring out of Lane reset after %dus.\n",
- phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US);
+ "PHY %c failed to bring out of lane reset\n",
+ phy_name(phy));
intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
intel_cx0_get_pclk_refclk_request(owned_lane_mask),
intel_cx0_get_pclk_refclk_request(lane_mask));
- if (intel_de_wait_custom(display, XELPDP_PORT_CLOCK_CTL(display, port),
- intel_cx0_get_pclk_refclk_ack(owned_lane_mask),
- intel_cx0_get_pclk_refclk_ack(lane_mask),
- XELPDP_REFCLK_ENABLE_TIMEOUT_US, 0, NULL))
+ if (intel_de_wait_us(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ intel_cx0_get_pclk_refclk_ack(owned_lane_mask),
+ intel_cx0_get_pclk_refclk_ack(lane_mask),
+ XELPDP_REFCLK_ENABLE_TIMEOUT_US, NULL))
drm_warn(display->drm,
- "PHY %c failed to request refclk after %dus.\n",
- phy_name(phy), XELPDP_REFCLK_ENABLE_TIMEOUT_US);
+ "PHY %c failed to request refclk\n",
+ phy_name(phy));
intel_cx0_powerdown_change_sequence(encoder, INTEL_CX0_BOTH_LANES,
- CX0_P2_STATE_RESET);
+ XELPDP_P2_STATE_RESET);
intel_cx0_setup_powerdown(encoder);
intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_pipe_reset, 0);
- if (intel_de_wait_for_clear(display, XELPDP_PORT_BUF_CTL2(display, port),
- lane_phy_current_status,
- XELPDP_PORT_RESET_END_TIMEOUT))
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_current_status,
+ XELPDP_PORT_RESET_END_TIMEOUT_MS))
drm_warn(display->drm,
- "PHY %c failed to bring out of Lane reset after %dms.\n",
- phy_name(phy), XELPDP_PORT_RESET_END_TIMEOUT);
+ "PHY %c failed to bring out of lane reset\n",
+ phy_name(phy));
}
static void intel_cx0_program_phy_lane(struct intel_encoder *encoder, int lane_count,
@@ -3034,7 +3022,7 @@ static void __intel_cx0pll_enable(struct intel_encoder *encoder,
* TODO: For DP alt mode use only one lane.
*/
intel_cx0_powerdown_change_sequence(encoder, INTEL_CX0_BOTH_LANES,
- CX0_P2_STATE_READY);
+ XELPDP_P2_STATE_READY);
/*
* 4. Program PORT_MSGBUS_TIMER register's Message Bus Timer field to 0xA000.
@@ -3074,12 +3062,12 @@ static void __intel_cx0pll_enable(struct intel_encoder *encoder,
intel_cx0_get_pclk_pll_request(maxpclk_lane));
/* 10. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK> == "1". */
- if (intel_de_wait_custom(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
- intel_cx0_get_pclk_pll_ack(INTEL_CX0_BOTH_LANES),
- intel_cx0_get_pclk_pll_ack(maxpclk_lane),
- XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US, 0, NULL))
- drm_warn(display->drm, "Port %c PLL not locked after %dus.\n",
- phy_name(phy), XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US);
+ if (intel_de_wait_us(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
+ intel_cx0_get_pclk_pll_ack(INTEL_CX0_BOTH_LANES),
+ intel_cx0_get_pclk_pll_ack(maxpclk_lane),
+ XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US, NULL))
+ drm_warn(display->drm, "Port %c PLL not locked\n",
+ phy_name(phy));
/*
* 11. Follow the Display Voltage Frequency Switching Sequence After
@@ -3160,8 +3148,8 @@ static int intel_mtl_tbt_clock_select(struct intel_display *display,
}
}
-static void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder,
- const struct intel_crtc_state *crtc_state)
+void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(encoder);
enum phy phy = intel_encoder_to_phy(encoder);
@@ -3198,12 +3186,9 @@ static void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder,
intel_de_write(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), val);
/* 5. Poll on PORT_CLOCK_CTL TBT CLOCK Ack == "1". */
- if (intel_de_wait_custom(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
- XELPDP_TBT_CLOCK_ACK,
- XELPDP_TBT_CLOCK_ACK,
- 100, 0, NULL))
- drm_warn(display->drm,
- "[ENCODER:%d:%s][%c] PHY PLL not locked after 100us.\n",
+ if (intel_de_wait_for_set_us(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
+ XELPDP_TBT_CLOCK_ACK, 100))
+ drm_warn(display->drm, "[ENCODER:%d:%s][%c] PHY PLL not locked\n",
encoder->base.base.id, encoder->base.name, phy_name(phy));
/*
@@ -3275,13 +3260,13 @@ static u8 cx0_power_control_disable_val(struct intel_encoder *encoder)
struct intel_display *display = to_intel_display(encoder);
if (intel_encoder_is_c10phy(encoder))
- return CX0_P2PG_STATE_DISABLE;
+ return XELPDP_P2PG_STATE_DISABLE;
if ((display->platform.battlemage && encoder->port == PORT_A) ||
(DISPLAY_VER(display) >= 30 && encoder->type == INTEL_OUTPUT_EDP))
- return CX0_P2PG_STATE_DISABLE;
+ return XELPDP_P2PG_STATE_DISABLE;
- return CX0_P4PG_STATE_DISABLE;
+ return XELPDP_P4PG_STATE_DISABLE;
}
static void intel_cx0pll_disable(struct intel_encoder *encoder)
@@ -3313,13 +3298,12 @@ static void intel_cx0pll_disable(struct intel_encoder *encoder)
/*
* 5. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN<Lane for maxPCLK**> == "0".
*/
- if (intel_de_wait_custom(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
- intel_cx0_get_pclk_pll_ack(INTEL_CX0_BOTH_LANES) |
- intel_cx0_get_pclk_refclk_ack(INTEL_CX0_BOTH_LANES), 0,
- XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US, 0, NULL))
- drm_warn(display->drm,
- "Port %c PLL not unlocked after %dus.\n",
- phy_name(phy), XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US);
+ if (intel_de_wait_for_clear_us(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
+ intel_cx0_get_pclk_pll_ack(INTEL_CX0_BOTH_LANES) |
+ intel_cx0_get_pclk_refclk_ack(INTEL_CX0_BOTH_LANES),
+ XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US))
+ drm_warn(display->drm, "Port %c PLL not unlocked\n",
+ phy_name(phy));
/*
* 6. Follow the Display Voltage Frequency Switching Sequence After
@@ -3345,7 +3329,7 @@ static bool intel_cx0_pll_is_enabled(struct intel_encoder *encoder)
intel_cx0_get_pclk_pll_request(lane);
}
-static void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder)
+void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder)
{
struct intel_display *display = to_intel_display(encoder);
enum phy phy = intel_encoder_to_phy(encoder);
@@ -3362,10 +3346,9 @@ static void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder)
XELPDP_TBT_CLOCK_REQUEST, 0);
/* 3. Poll on PORT_CLOCK_CTL TBT CLOCK Ack == "0". */
- if (intel_de_wait_custom(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
- XELPDP_TBT_CLOCK_ACK, 0, 10, 0, NULL))
- drm_warn(display->drm,
- "[ENCODER:%d:%s][%c] PHY PLL not unlocked after 10us.\n",
+ if (intel_de_wait_for_clear_us(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
+ XELPDP_TBT_CLOCK_ACK, 10))
+ drm_warn(display->drm, "[ENCODER:%d:%s][%c] PHY PLL not unlocked\n",
encoder->base.base.id, encoder->base.name, phy_name(phy));
/*
@@ -3584,7 +3567,7 @@ void intel_cx0pll_state_verify(struct intel_atomic_state *state,
struct intel_encoder *encoder;
struct intel_cx0pll_state mpll_hw_state = {};
- if (DISPLAY_VER(display) < 14)
+ if (!IS_DISPLAY_VER(display, 14, 30))
return;
if (!new_crtc_state->hw.active)
diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h
index c5a7b529955b..84d334b865f7 100644
--- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h
+++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h
@@ -8,6 +8,9 @@
#include <linux/types.h>
+#define MB_WRITE_COMMITTED true
+#define MB_WRITE_UNCOMMITTED false
+
enum icl_port_dpll_id;
struct intel_atomic_state;
struct intel_c10pll_state;
@@ -19,6 +22,8 @@ struct intel_display;
struct intel_encoder;
struct intel_hdmi;
+void intel_clear_response_ready_flag(struct intel_encoder *encoder,
+ int lane);
bool intel_encoder_is_c10phy(struct intel_encoder *encoder);
void intel_mtl_pll_enable(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
@@ -41,9 +46,25 @@ bool intel_cx0pll_compare_hw_state(const struct intel_cx0pll_state *a,
const struct intel_cx0pll_state *b);
void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
+void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder,
+ u8 lane_mask, u8 state);
+int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock);
+void intel_cx0_setup_powerdown(struct intel_encoder *encoder);
+bool intel_cx0_is_hdmi_frl(u32 clock);
+u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr);
+void intel_cx0_rmw(struct intel_encoder *encoder,
+ u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed);
+void intel_cx0_write(struct intel_encoder *encoder,
+ u8 lane_mask, u16 addr, u8 data, bool committed);
+int intel_cx0_wait_for_ack(struct intel_encoder *encoder,
+ int command, int lane, u32 *val);
+void intel_cx0_bus_reset(struct intel_encoder *encoder, int lane);
int intel_mtl_tbt_calc_port_clock(struct intel_encoder *encoder);
void intel_cx0_pll_power_save_wa(struct intel_display *display);
void intel_lnl_mac_transmit_lfps(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state);
+void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state);
+void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder);
#endif /* __INTEL_CX0_PHY_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h
index 77eae1d845f7..8df5cd5ce418 100644
--- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h
+++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h
@@ -50,6 +50,7 @@
#define XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x1)
#define XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x2)
#define XELPDP_PORT_M2P_COMMAND_READ REG_FIELD_PREP(XELPDP_PORT_M2P_COMMAND_TYPE_MASK, 0x3)
+#define XELPDP_PORT_P2P_TRANSACTION_PENDING REG_BIT(24)
#define XELPDP_PORT_M2P_DATA_MASK REG_GENMASK(23, 16)
#define XELPDP_PORT_M2P_DATA(val) REG_FIELD_PREP(XELPDP_PORT_M2P_DATA_MASK, val)
#define XELPDP_PORT_M2P_TRANSACTION_RESET REG_BIT(15)
@@ -73,14 +74,13 @@
#define XELPDP_PORT_P2M_DATA(val) REG_FIELD_PREP(XELPDP_PORT_P2M_DATA_MASK, val)
#define XELPDP_PORT_P2M_ERROR_SET REG_BIT(15)
-#define XELPDP_MSGBUS_TIMEOUT_SLOW 1
-#define XELPDP_MSGBUS_TIMEOUT_FAST_US 2
+#define XELPDP_MSGBUS_TIMEOUT_MS 1
#define XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US 3200
#define XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US 20
#define XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US 100
#define XELPDP_PORT_RESET_START_TIMEOUT_US 5
-#define XELPDP_PORT_POWERDOWN_UPDATE_TIMEOUT_US 100
-#define XELPDP_PORT_RESET_END_TIMEOUT 15
+#define XELPDP_PORT_POWERDOWN_UPDATE_TIMEOUT_MS 2
+#define XELPDP_PORT_RESET_END_TIMEOUT_MS 15
#define XELPDP_REFCLK_ENABLE_TIMEOUT_US 1
#define _XELPDP_PORT_BUF_CTL1_LN0_A 0x64004
@@ -104,6 +104,8 @@
#define XELPDP_PORT_BUF_PORT_DATA_20BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 1)
#define XELPDP_PORT_BUF_PORT_DATA_40BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 2)
#define XELPDP_PORT_REVERSAL REG_BIT(16)
+#define XE3PLPDP_PHY_MODE_MASK REG_GENMASK(15, 12)
+#define XE3PLPDP_PHY_MODE_DP REG_FIELD_PREP(XE3PLPDP_PHY_MODE_MASK, 0x3)
#define XELPDP_PORT_BUF_IO_SELECT_TBT REG_BIT(11)
#define XELPDP_PORT_BUF_PHY_IDLE REG_BIT(7)
#define XELPDP_TC_PHY_OWNERSHIP REG_BIT(6)
@@ -124,6 +126,7 @@
_XELPDP_PORT_BUF_CTL2(port))
#define XELPDP_LANE_PIPE_RESET(lane) _PICK(lane, REG_BIT(31), REG_BIT(30))
#define XELPDP_LANE_PHY_CURRENT_STATUS(lane) _PICK(lane, REG_BIT(29), REG_BIT(28))
+#define XE3PLPDP_LANE_PHY_PULSE_STATUS(lane) _PICK(lane, REG_BIT(27), REG_BIT(26))
#define XELPDP_LANE_POWERDOWN_UPDATE(lane) _PICK(lane, REG_BIT(25), REG_BIT(24))
#define _XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK REG_GENMASK(23, 20)
#define _XELPDP_LANE0_POWERDOWN_NEW_STATE(val) REG_FIELD_PREP(_XELPDP_LANE0_POWERDOWN_NEW_STATE_MASK, val)
@@ -149,11 +152,12 @@
#define XELPDP_PLL_LANE_STAGGERING_DELAY(val) REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val)
#define XELPDP_POWER_STATE_ACTIVE_MASK REG_GENMASK(3, 0)
#define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val)
-#define CX0_P0_STATE_ACTIVE 0x0
-#define CX0_P2_STATE_READY 0x2
-#define CX0_P2PG_STATE_DISABLE 0x9
-#define CX0_P4PG_STATE_DISABLE 0xC
-#define CX0_P2_STATE_RESET 0x2
+#define XELPDP_P0_STATE_ACTIVE 0x0
+#define XELPDP_P2_STATE_READY 0x2
+#define XE3PLPD_P4_STATE_DISABLE 0x4
+#define XELPDP_P2PG_STATE_DISABLE 0x9
+#define XELPDP_P4PG_STATE_DISABLE 0xC
+#define XELPDP_P2_STATE_RESET 0x2
#define _XELPDP_PORT_MSGBUS_TIMER_LN0_A 0x640d8
#define _XELPDP_PORT_MSGBUS_TIMER_LN0_B 0x641d8
@@ -298,10 +302,14 @@
#define PHY_C20_RD_DATA_L 0xC08
#define PHY_C20_RD_DATA_H 0xC09
#define PHY_C20_VDR_CUSTOM_SERDES_RATE 0xD00
-#define PHY_C20_VDR_HDMI_RATE 0xD01
+#define PHY_C20_IS_HDMI_FRL REG_BIT8(7)
+#define PHY_C20_IS_DP REG_BIT8(6)
+#define PHY_C20_DP_RATE_MASK REG_GENMASK8(4, 1)
+#define PHY_C20_DP_RATE(val) REG_FIELD_PREP8(PHY_C20_DP_RATE_MASK, val)
#define PHY_C20_CONTEXT_TOGGLE REG_BIT8(0)
-#define PHY_C20_CUSTOM_SERDES_MASK REG_GENMASK8(4, 1)
-#define PHY_C20_CUSTOM_SERDES(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_SERDES_MASK, val)
+#define PHY_C20_VDR_HDMI_RATE 0xD01
+#define PHY_C20_HDMI_RATE_MASK REG_GENMASK8(1, 0)
+#define PHY_C20_HDMI_RATE(val) REG_FIELD_PREP8(PHY_C20_HDMI_RATE_MASK, val)
#define PHY_C20_VDR_CUSTOM_WIDTH 0xD02
#define PHY_C20_CUSTOM_WIDTH_MASK REG_GENMASK(1, 0)
#define PHY_C20_CUSTOM_WIDTH(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_WIDTH_MASK, val)
diff --git a/drivers/gpu/drm/i915/display/intel_dbuf_bw.c b/drivers/gpu/drm/i915/display/intel_dbuf_bw.c
new file mode 100644
index 000000000000..8b8894c37f63
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dbuf_bw.c
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <drm/drm_print.h>
+
+#include "intel_dbuf_bw.h"
+#include "intel_display_core.h"
+#include "intel_display_types.h"
+#include "skl_watermark.h"
+
+struct intel_dbuf_bw {
+ unsigned int max_bw[I915_MAX_DBUF_SLICES];
+ u8 active_planes[I915_MAX_DBUF_SLICES];
+};
+
+struct intel_dbuf_bw_state {
+ struct intel_global_state base;
+ struct intel_dbuf_bw dbuf_bw[I915_MAX_PIPES];
+};
+
+struct intel_dbuf_bw_state *to_intel_dbuf_bw_state(struct intel_global_state *obj_state)
+{
+ return container_of(obj_state, struct intel_dbuf_bw_state, base);
+}
+
+struct intel_dbuf_bw_state *
+intel_atomic_get_old_dbuf_bw_state(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_global_state *dbuf_bw_state;
+
+ dbuf_bw_state = intel_atomic_get_old_global_obj_state(state, &display->dbuf_bw.obj);
+
+ return to_intel_dbuf_bw_state(dbuf_bw_state);
+}
+
+struct intel_dbuf_bw_state *
+intel_atomic_get_new_dbuf_bw_state(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_global_state *dbuf_bw_state;
+
+ dbuf_bw_state = intel_atomic_get_new_global_obj_state(state, &display->dbuf_bw.obj);
+
+ return to_intel_dbuf_bw_state(dbuf_bw_state);
+}
+
+struct intel_dbuf_bw_state *
+intel_atomic_get_dbuf_bw_state(struct intel_atomic_state *state)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_global_state *dbuf_bw_state;
+
+ dbuf_bw_state = intel_atomic_get_global_obj_state(state, &display->dbuf_bw.obj);
+ if (IS_ERR(dbuf_bw_state))
+ return ERR_CAST(dbuf_bw_state);
+
+ return to_intel_dbuf_bw_state(dbuf_bw_state);
+}
+
+static bool intel_dbuf_bw_changed(struct intel_display *display,
+ const struct intel_dbuf_bw *old_dbuf_bw,
+ const struct intel_dbuf_bw *new_dbuf_bw)
+{
+ enum dbuf_slice slice;
+
+ for_each_dbuf_slice(display, slice) {
+ if (old_dbuf_bw->max_bw[slice] != new_dbuf_bw->max_bw[slice] ||
+ old_dbuf_bw->active_planes[slice] != new_dbuf_bw->active_planes[slice])
+ return true;
+ }
+
+ return false;
+}
+
+static bool intel_dbuf_bw_state_changed(struct intel_display *display,
+ const struct intel_dbuf_bw_state *old_dbuf_bw_state,
+ const struct intel_dbuf_bw_state *new_dbuf_bw_state)
+{
+ enum pipe pipe;
+
+ for_each_pipe(display, pipe) {
+ const struct intel_dbuf_bw *old_dbuf_bw =
+ &old_dbuf_bw_state->dbuf_bw[pipe];
+ const struct intel_dbuf_bw *new_dbuf_bw =
+ &new_dbuf_bw_state->dbuf_bw[pipe];
+
+ if (intel_dbuf_bw_changed(display, old_dbuf_bw, new_dbuf_bw))
+ return true;
+ }
+
+ return false;
+}
+
+static void skl_plane_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
+ struct intel_crtc *crtc,
+ enum plane_id plane_id,
+ const struct skl_ddb_entry *ddb,
+ unsigned int data_rate)
+{
+ struct intel_display *display = to_intel_display(crtc);
+ unsigned int dbuf_mask = skl_ddb_dbuf_slice_mask(display, ddb);
+ enum dbuf_slice slice;
+
+ /*
+ * The arbiter can only really guarantee an
+ * equal share of the total bw to each plane.
+ */
+ for_each_dbuf_slice_in_mask(display, slice, dbuf_mask) {
+ dbuf_bw->max_bw[slice] = max(dbuf_bw->max_bw[slice], data_rate);
+ dbuf_bw->active_planes[slice] |= BIT(plane_id);
+ }
+}
+
+static void skl_crtc_calc_dbuf_bw(struct intel_dbuf_bw *dbuf_bw,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ enum plane_id plane_id;
+
+ memset(dbuf_bw, 0, sizeof(*dbuf_bw));
+
+ if (!crtc_state->hw.active)
+ return;
+
+ for_each_plane_id_on_crtc(crtc, plane_id) {
+ /*
+ * We assume cursors are small enough
+ * to not cause bandwidth problems.
+ */
+ if (plane_id == PLANE_CURSOR)
+ continue;
+
+ skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
+ &crtc_state->wm.skl.plane_ddb[plane_id],
+ crtc_state->data_rate[plane_id]);
+
+ if (DISPLAY_VER(display) < 11)
+ skl_plane_calc_dbuf_bw(dbuf_bw, crtc, plane_id,
+ &crtc_state->wm.skl.plane_ddb_y[plane_id],
+ crtc_state->data_rate[plane_id]);
+ }
+}
+
+/* "Maximum Data Buffer Bandwidth" */
+int intel_dbuf_bw_min_cdclk(struct intel_display *display,
+ const struct intel_dbuf_bw_state *dbuf_bw_state)
+{
+ unsigned int total_max_bw = 0;
+ enum dbuf_slice slice;
+
+ for_each_dbuf_slice(display, slice) {
+ int num_active_planes = 0;
+ unsigned int max_bw = 0;
+ enum pipe pipe;
+
+ /*
+ * The arbiter can only really guarantee an
+ * equal share of the total bw to each plane.
+ */
+ for_each_pipe(display, pipe) {
+ const struct intel_dbuf_bw *dbuf_bw = &dbuf_bw_state->dbuf_bw[pipe];
+
+ max_bw = max(dbuf_bw->max_bw[slice], max_bw);
+ num_active_planes += hweight8(dbuf_bw->active_planes[slice]);
+ }
+ max_bw *= num_active_planes;
+
+ total_max_bw = max(total_max_bw, max_bw);
+ }
+
+ return DIV_ROUND_UP(total_max_bw, 64);
+}
+
+int intel_dbuf_bw_calc_min_cdclk(struct intel_atomic_state *state,
+ bool *need_cdclk_calc)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_dbuf_bw_state *new_dbuf_bw_state = NULL;
+ const struct intel_dbuf_bw_state *old_dbuf_bw_state = NULL;
+ const struct intel_crtc_state *old_crtc_state;
+ const struct intel_crtc_state *new_crtc_state;
+ struct intel_crtc *crtc;
+ int ret, i;
+
+ if (DISPLAY_VER(display) < 9)
+ return 0;
+
+ for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
+ new_crtc_state, i) {
+ struct intel_dbuf_bw old_dbuf_bw, new_dbuf_bw;
+
+ skl_crtc_calc_dbuf_bw(&old_dbuf_bw, old_crtc_state);
+ skl_crtc_calc_dbuf_bw(&new_dbuf_bw, new_crtc_state);
+
+ if (!intel_dbuf_bw_changed(display, &old_dbuf_bw, &new_dbuf_bw))
+ continue;
+
+ new_dbuf_bw_state = intel_atomic_get_dbuf_bw_state(state);
+ if (IS_ERR(new_dbuf_bw_state))
+ return PTR_ERR(new_dbuf_bw_state);
+
+ old_dbuf_bw_state = intel_atomic_get_old_dbuf_bw_state(state);
+
+ new_dbuf_bw_state->dbuf_bw[crtc->pipe] = new_dbuf_bw;
+ }
+
+ if (!old_dbuf_bw_state)
+ return 0;
+
+ if (intel_dbuf_bw_state_changed(display, old_dbuf_bw_state, new_dbuf_bw_state)) {
+ ret = intel_atomic_lock_global_state(&new_dbuf_bw_state->base);
+ if (ret)
+ return ret;
+ }
+
+ ret = intel_cdclk_update_dbuf_bw_min_cdclk(state,
+ intel_dbuf_bw_min_cdclk(display, old_dbuf_bw_state),
+ intel_dbuf_bw_min_cdclk(display, new_dbuf_bw_state),
+ need_cdclk_calc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void intel_dbuf_bw_update_hw_state(struct intel_display *display)
+{
+ struct intel_dbuf_bw_state *dbuf_bw_state =
+ to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
+ struct intel_crtc *crtc;
+
+ if (DISPLAY_VER(display) < 9)
+ return;
+
+ for_each_intel_crtc(display->drm, crtc) {
+ const struct intel_crtc_state *crtc_state =
+ to_intel_crtc_state(crtc->base.state);
+
+ skl_crtc_calc_dbuf_bw(&dbuf_bw_state->dbuf_bw[crtc->pipe], crtc_state);
+ }
+}
+
+void intel_dbuf_bw_crtc_disable_noatomic(struct intel_crtc *crtc)
+{
+ struct intel_display *display = to_intel_display(crtc);
+ struct intel_dbuf_bw_state *dbuf_bw_state =
+ to_intel_dbuf_bw_state(display->dbuf_bw.obj.state);
+ enum pipe pipe = crtc->pipe;
+
+ if (DISPLAY_VER(display) < 9)
+ return;
+
+ memset(&dbuf_bw_state->dbuf_bw[pipe], 0, sizeof(dbuf_bw_state->dbuf_bw[pipe]));
+}
+
+static struct intel_global_state *
+intel_dbuf_bw_duplicate_state(struct intel_global_obj *obj)
+{
+ struct intel_dbuf_bw_state *state;
+
+ state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ return &state->base;
+}
+
+static void intel_dbuf_bw_destroy_state(struct intel_global_obj *obj,
+ struct intel_global_state *state)
+{
+ kfree(state);
+}
+
+static const struct intel_global_state_funcs intel_dbuf_bw_funcs = {
+ .atomic_duplicate_state = intel_dbuf_bw_duplicate_state,
+ .atomic_destroy_state = intel_dbuf_bw_destroy_state,
+};
+
+int intel_dbuf_bw_init(struct intel_display *display)
+{
+ struct intel_dbuf_bw_state *state;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ intel_atomic_global_obj_init(display, &display->dbuf_bw.obj,
+ &state->base, &intel_dbuf_bw_funcs);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dbuf_bw.h b/drivers/gpu/drm/i915/display/intel_dbuf_bw.h
new file mode 100644
index 000000000000..61875b9d5969
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_dbuf_bw.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __INTEL_DBUF_BW_H__
+#define __INTEL_DBUF_BW_H__
+
+#include <drm/drm_atomic.h>
+
+struct intel_atomic_state;
+struct intel_dbuf_bw_state;
+struct intel_crtc;
+struct intel_display;
+struct intel_global_state;
+
+struct intel_dbuf_bw_state *
+to_intel_dbuf_bw_state(struct intel_global_state *obj_state);
+
+struct intel_dbuf_bw_state *
+intel_atomic_get_old_dbuf_bw_state(struct intel_atomic_state *state);
+
+struct intel_dbuf_bw_state *
+intel_atomic_get_new_dbuf_bw_state(struct intel_atomic_state *state);
+
+struct intel_dbuf_bw_state *
+intel_atomic_get_dbuf_bw_state(struct intel_atomic_state *state);
+
+int intel_dbuf_bw_init(struct intel_display *display);
+int intel_dbuf_bw_calc_min_cdclk(struct intel_atomic_state *state,
+ bool *need_cdclk_calc);
+int intel_dbuf_bw_min_cdclk(struct intel_display *display,
+ const struct intel_dbuf_bw_state *dbuf_bw_state);
+void intel_dbuf_bw_update_hw_state(struct intel_display *display);
+void intel_dbuf_bw_crtc_disable_noatomic(struct intel_crtc *crtc);
+
+#endif /* __INTEL_DBUF_BW_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index c09aa759f4d4..002ccd47856d 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -35,7 +35,6 @@
#include <drm/drm_privacy_screen_consumer.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "icl_dsi.h"
#include "intel_alpm.h"
#include "intel_audio.h"
@@ -53,6 +52,7 @@
#include "intel_display_power.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dkl_phy.h"
#include "intel_dkl_phy_regs.h"
#include "intel_dp.h"
@@ -72,6 +72,7 @@
#include "intel_hotplug.h"
#include "intel_hti.h"
#include "intel_lspcon.h"
+#include "intel_lt_phy.h"
#include "intel_mg_phy_regs.h"
#include "intel_modeset_lock.h"
#include "intel_panel.h"
@@ -209,8 +210,8 @@ void intel_wait_ddi_buf_idle(struct intel_display *display, enum port port)
}
static_assert(DDI_BUF_IS_IDLE == XELPDP_PORT_BUF_PHY_IDLE);
- if (intel_de_wait_for_set(display, intel_ddi_buf_status_reg(display, port),
- DDI_BUF_IS_IDLE, 10))
+ if (intel_de_wait_for_set_ms(display, intel_ddi_buf_status_reg(display, port),
+ DDI_BUF_IS_IDLE, 10))
drm_err(display->drm, "Timeout waiting for DDI BUF %c to get idle\n",
port_name(port));
}
@@ -234,8 +235,8 @@ static void intel_wait_ddi_buf_active(struct intel_encoder *encoder)
}
static_assert(DDI_BUF_IS_IDLE == XELPDP_PORT_BUF_PHY_IDLE);
- if (intel_de_wait_for_clear(display, intel_ddi_buf_status_reg(display, port),
- DDI_BUF_IS_IDLE, 10))
+ if (intel_de_wait_for_clear_ms(display, intel_ddi_buf_status_reg(display, port),
+ DDI_BUF_IS_IDLE, 10))
drm_err(display->drm, "Timeout waiting for DDI BUF %c to get active\n",
port_name(port));
}
@@ -1466,10 +1467,15 @@ static int translate_signal_level(struct intel_dp *intel_dp,
u8 signal_levels)
{
struct intel_display *display = to_intel_display(intel_dp);
+ const u8 *signal_array;
+ size_t array_size;
int i;
- for (i = 0; i < ARRAY_SIZE(index_to_dp_signal_levels); i++) {
- if (index_to_dp_signal_levels[i] == signal_levels)
+ signal_array = index_to_dp_signal_levels;
+ array_size = ARRAY_SIZE(index_to_dp_signal_levels);
+
+ for (i = 0; i < array_size; i++) {
+ if (signal_array[i] == signal_levels)
return i;
}
@@ -2301,8 +2307,8 @@ void intel_ddi_wait_for_act_sent(struct intel_encoder *encoder,
{
struct intel_display *display = to_intel_display(encoder);
- if (intel_de_wait_for_set(display, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_ACT_SENT, 1))
+ if (intel_de_wait_for_set_ms(display, dp_tp_status_reg(encoder, crtc_state),
+ DP_TP_STATUS_ACT_SENT, 1))
drm_err(display->drm, "Timed out waiting for ACT sent\n");
}
@@ -2377,11 +2383,11 @@ int intel_ddi_wait_for_fec_status(struct intel_encoder *encoder,
return 0;
if (enabled)
- ret = intel_de_wait_for_set(display, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_FEC_ENABLE_LIVE, 1);
+ ret = intel_de_wait_for_set_ms(display, dp_tp_status_reg(encoder, crtc_state),
+ DP_TP_STATUS_FEC_ENABLE_LIVE, 1);
else
- ret = intel_de_wait_for_clear(display, dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_FEC_ENABLE_LIVE, 1);
+ ret = intel_de_wait_for_clear_ms(display, dp_tp_status_reg(encoder, crtc_state),
+ DP_TP_STATUS_FEC_ENABLE_LIVE, 1);
if (ret) {
drm_err(display->drm,
@@ -2571,9 +2577,7 @@ mtl_ddi_enable_d2d(struct intel_encoder *encoder)
intel_de_rmw(display, reg, 0, set_bits);
- ret = intel_de_wait_custom(display, reg,
- wait_bits, wait_bits,
- 100, 0, NULL);
+ ret = intel_de_wait_for_set_us(display, reg, wait_bits, 100);
if (ret) {
drm_err(display->drm, "Timeout waiting for D2D Link enable for DDI/PORT_BUF_CTL %c\n",
port_name(port));
@@ -3073,9 +3077,7 @@ mtl_ddi_disable_d2d(struct intel_encoder *encoder)
intel_de_rmw(display, reg, clr_bits, 0);
- ret = intel_de_wait_custom(display, reg,
- wait_bits, 0,
- 100, 0, NULL);
+ ret = intel_de_wait_for_clear_us(display, reg, wait_bits, 100);
if (ret)
drm_err(display->drm, "Timeout waiting for D2D Link disable for DDI/PORT_BUF_CTL %c\n",
port_name(port));
@@ -3862,9 +3864,9 @@ static void intel_ddi_set_idle_link_train(struct intel_dp *intel_dp,
if (port == PORT_A && DISPLAY_VER(display) < 12)
return;
- if (intel_de_wait_for_set(display,
- dp_tp_status_reg(encoder, crtc_state),
- DP_TP_STATUS_IDLE_DONE, 2))
+ if (intel_de_wait_for_set_ms(display,
+ dp_tp_status_reg(encoder, crtc_state),
+ DP_TP_STATUS_IDLE_DONE, 2))
drm_err(display->drm,
"Timed out waiting for DP idle patterns\n");
}
@@ -4240,6 +4242,19 @@ void intel_ddi_get_clock(struct intel_encoder *encoder,
&crtc_state->dpll_hw_state);
}
+static void xe3plpd_ddi_get_config(struct intel_encoder *encoder,
+ struct intel_crtc_state *crtc_state)
+{
+ intel_lt_phy_pll_readout_hw_state(encoder, crtc_state, &crtc_state->dpll_hw_state.ltpll);
+
+ if (crtc_state->dpll_hw_state.ltpll.tbt_mode)
+ crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder);
+ else
+ crtc_state->port_clock =
+ intel_lt_phy_calc_port_clock(encoder, crtc_state);
+ intel_ddi_get_config(encoder, crtc_state);
+}
+
static void mtl_ddi_get_config(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state)
{
@@ -4559,6 +4574,13 @@ static int intel_ddi_compute_config_late(struct intel_encoder *encoder,
struct intel_display *display = to_intel_display(encoder);
struct drm_connector *connector = conn_state->connector;
u8 port_sync_transcoders = 0;
+ int ret = 0;
+
+ if (intel_crtc_has_dp_encoder(crtc_state))
+ ret = intel_dp_compute_config_late(encoder, crtc_state, conn_state);
+
+ if (ret)
+ return ret;
drm_dbg_kms(display->drm, "[ENCODER:%d:%s] [CRTC:%d:%s]\n",
encoder->base.base.id, encoder->base.name,
@@ -5224,7 +5246,12 @@ void intel_ddi_init(struct intel_display *display,
encoder->cloneable = 0;
encoder->pipe_mask = ~0;
- if (DISPLAY_VER(display) >= 14) {
+ if (HAS_LT_PHY(display)) {
+ encoder->enable_clock = intel_xe3plpd_pll_enable;
+ encoder->disable_clock = intel_xe3plpd_pll_disable;
+ encoder->port_pll_type = intel_mtl_port_pll_type;
+ encoder->get_config = xe3plpd_ddi_get_config;
+ } else if (DISPLAY_VER(display) >= 14) {
encoder->enable_clock = intel_mtl_pll_enable;
encoder->disable_clock = intel_mtl_pll_disable;
encoder->port_pll_type = intel_mtl_port_pll_type;
@@ -5289,7 +5316,9 @@ void intel_ddi_init(struct intel_display *display,
encoder->get_config = hsw_ddi_get_config;
}
- if (DISPLAY_VER(display) >= 14) {
+ if (HAS_LT_PHY(display)) {
+ encoder->set_signal_levels = intel_lt_phy_set_signal_levels;
+ } else if (DISPLAY_VER(display) >= 14) {
encoder->set_signal_levels = intel_cx0_phy_set_signal_levels;
} else if (display->platform.dg2) {
encoder->set_signal_levels = intel_snps_phy_set_signal_levels;
diff --git a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c
index a238be5bc455..395dba8c9e4d 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c
@@ -3,13 +3,14 @@
* Copyright © 2020 Intel Corporation
*/
-#include "i915_utils.h"
#include "intel_cx0_phy.h"
#include "intel_ddi.h"
#include "intel_ddi_buf_trans.h"
#include "intel_de.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
+#include "intel_lt_phy.h"
/* HDMI/DVI modes ignore everything but the last 2 items. So we share
* them for both DP and FDI transports, allowing those ports to
@@ -1115,6 +1116,69 @@ static const struct intel_ddi_buf_trans mtl_c20_trans_uhbr = {
.num_entries = ARRAY_SIZE(_mtl_c20_trans_uhbr),
};
+/* DP1.4 */
+static const union intel_ddi_buf_trans_entry _xe3plpd_lt_trans_dp14[] = {
+ { .lt = { 1, 0, 0, 21, 0 } },
+ { .lt = { 1, 1, 0, 24, 3 } },
+ { .lt = { 1, 2, 0, 28, 7 } },
+ { .lt = { 0, 3, 0, 35, 13 } },
+ { .lt = { 1, 1, 0, 27, 0 } },
+ { .lt = { 1, 2, 0, 31, 4 } },
+ { .lt = { 0, 3, 0, 39, 9 } },
+ { .lt = { 1, 2, 0, 35, 0 } },
+ { .lt = { 0, 3, 0, 41, 7 } },
+ { .lt = { 0, 3, 0, 48, 0 } },
+};
+
+/* DP2.1 */
+static const union intel_ddi_buf_trans_entry _xe3plpd_lt_trans_uhbr[] = {
+ { .lt = { 0, 0, 0, 48, 0 } },
+ { .lt = { 0, 0, 0, 43, 5 } },
+ { .lt = { 0, 0, 0, 40, 8 } },
+ { .lt = { 0, 0, 0, 37, 11 } },
+ { .lt = { 0, 0, 0, 33, 15 } },
+ { .lt = { 0, 0, 2, 46, 0 } },
+ { .lt = { 0, 0, 2, 42, 4 } },
+ { .lt = { 0, 0, 2, 38, 8 } },
+ { .lt = { 0, 0, 2, 35, 11 } },
+ { .lt = { 0, 0, 2, 33, 13 } },
+ { .lt = { 0, 0, 4, 44, 0 } },
+ { .lt = { 0, 0, 4, 40, 4 } },
+ { .lt = { 0, 0, 4, 37, 7 } },
+ { .lt = { 0, 0, 4, 33, 11 } },
+ { .lt = { 0, 0, 8, 40, 0 } },
+ { .lt = { 1, 0, 2, 26, 2 } },
+};
+
+/* eDp */
+static const union intel_ddi_buf_trans_entry _xe3plpd_lt_trans_edp[] = {
+ { .lt = { 1, 0, 0, 12, 0 } },
+ { .lt = { 1, 1, 0, 13, 1 } },
+ { .lt = { 1, 2, 0, 15, 3 } },
+ { .lt = { 1, 3, 0, 19, 7 } },
+ { .lt = { 1, 1, 0, 14, 0 } },
+ { .lt = { 1, 2, 0, 16, 2 } },
+ { .lt = { 1, 3, 0, 21, 5 } },
+ { .lt = { 1, 2, 0, 18, 0 } },
+ { .lt = { 1, 3, 0, 22, 4 } },
+ { .lt = { 1, 3, 0, 26, 0 } },
+};
+
+static const struct intel_ddi_buf_trans xe3plpd_lt_trans_dp14 = {
+ .entries = _xe3plpd_lt_trans_dp14,
+ .num_entries = ARRAY_SIZE(_xe3plpd_lt_trans_dp14),
+};
+
+static const struct intel_ddi_buf_trans xe3plpd_lt_trans_uhbr = {
+ .entries = _xe3plpd_lt_trans_uhbr,
+ .num_entries = ARRAY_SIZE(_xe3plpd_lt_trans_uhbr),
+};
+
+static const struct intel_ddi_buf_trans xe3plpd_lt_trans_edp = {
+ .entries = _xe3plpd_lt_trans_edp,
+ .num_entries = ARRAY_SIZE(_xe3plpd_lt_trans_edp),
+};
+
bool is_hobl_buf_trans(const struct intel_ddi_buf_trans *table)
{
return table == &tgl_combo_phy_trans_edp_hbr2_hobl;
@@ -1707,11 +1771,26 @@ mtl_get_c20_buf_trans(struct intel_encoder *encoder,
return intel_get_buf_trans(&mtl_c20_trans_dp14, n_entries);
}
+static const struct intel_ddi_buf_trans *
+xe3plpd_get_lt_buf_trans(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state,
+ int *n_entries)
+{
+ if (intel_crtc_has_dp_encoder(crtc_state) && intel_dp_is_uhbr(crtc_state))
+ return intel_get_buf_trans(&xe3plpd_lt_trans_uhbr, n_entries);
+ else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
+ return intel_get_buf_trans(&xe3plpd_lt_trans_edp, n_entries);
+ else
+ return intel_get_buf_trans(&xe3plpd_lt_trans_dp14, n_entries);
+}
+
void intel_ddi_buf_trans_init(struct intel_encoder *encoder)
{
struct intel_display *display = to_intel_display(encoder);
- if (DISPLAY_VER(display) >= 14) {
+ if (HAS_LT_PHY(display)) {
+ encoder->get_buf_trans = xe3plpd_get_lt_buf_trans;
+ } else if (DISPLAY_VER(display) >= 14) {
if (intel_encoder_is_c10phy(encoder))
encoder->get_buf_trans = mtl_get_c10_buf_trans;
else
diff --git a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.h b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.h
index 29a190390192..cec332090a20 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.h
+++ b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.h
@@ -50,6 +50,14 @@ struct dg2_snps_phy_buf_trans {
u8 post_cursor;
};
+struct xe3plpd_lt_phy_buf_trans {
+ u8 txswing;
+ u8 txswing_level;
+ u8 pre_cursor;
+ u8 main_cursor;
+ u8 post_cursor;
+};
+
union intel_ddi_buf_trans_entry {
struct hsw_ddi_buf_trans hsw;
struct bxt_ddi_buf_trans bxt;
@@ -57,6 +65,7 @@ union intel_ddi_buf_trans_entry {
struct icl_mg_phy_ddi_buf_trans mg;
struct tgl_dkl_phy_ddi_buf_trans dkl;
struct dg2_snps_phy_buf_trans snps;
+ struct xe3plpd_lt_phy_buf_trans lt;
};
struct intel_ddi_buf_trans {
diff --git a/drivers/gpu/drm/i915/display/intel_de.h b/drivers/gpu/drm/i915/display/intel_de.h
index 9ecdcf6b73e4..a7ce3b875e06 100644
--- a/drivers/gpu/drm/i915/display/intel_de.h
+++ b/drivers/gpu/drm/i915/display/intel_de.h
@@ -84,20 +84,13 @@ intel_de_write(struct intel_display *display, i915_reg_t reg, u32 val)
}
static inline u32
-__intel_de_rmw_nowl(struct intel_display *display, i915_reg_t reg,
- u32 clear, u32 set)
-{
- return intel_uncore_rmw(__to_uncore(display), reg, clear, set);
-}
-
-static inline u32
intel_de_rmw(struct intel_display *display, i915_reg_t reg, u32 clear, u32 set)
{
u32 val;
intel_dmc_wl_get(display, reg);
- val = __intel_de_rmw_nowl(display, reg, clear, set);
+ val = intel_uncore_rmw(__to_uncore(display), reg, clear, set);
intel_dmc_wl_put(display, reg);
@@ -105,34 +98,16 @@ intel_de_rmw(struct intel_display *display, i915_reg_t reg, u32 clear, u32 set)
}
static inline int
-__intel_de_wait_for_register_nowl(struct intel_display *display,
- i915_reg_t reg,
- u32 mask, u32 value, unsigned int timeout_ms)
-{
- return intel_wait_for_register(__to_uncore(display), reg, mask,
- value, timeout_ms);
-}
-
-static inline int
-__intel_de_wait_for_register_atomic_nowl(struct intel_display *display,
- i915_reg_t reg,
- u32 mask, u32 value,
- unsigned int fast_timeout_us)
-{
- return __intel_wait_for_register(__to_uncore(display), reg, mask,
- value, fast_timeout_us, 0, NULL);
-}
-
-static inline int
-intel_de_wait(struct intel_display *display, i915_reg_t reg,
- u32 mask, u32 value, unsigned int timeout_ms)
+intel_de_wait_us(struct intel_display *display, i915_reg_t reg,
+ u32 mask, u32 value, unsigned int timeout_us,
+ u32 *out_value)
{
int ret;
intel_dmc_wl_get(display, reg);
- ret = __intel_de_wait_for_register_nowl(display, reg, mask, value,
- timeout_ms);
+ ret = __intel_wait_for_register(__to_uncore(display), reg, mask,
+ value, timeout_us, 0, out_value);
intel_dmc_wl_put(display, reg);
@@ -140,15 +115,16 @@ intel_de_wait(struct intel_display *display, i915_reg_t reg,
}
static inline int
-intel_de_wait_fw(struct intel_display *display, i915_reg_t reg,
- u32 mask, u32 value, unsigned int timeout_ms, u32 *out_value)
+intel_de_wait_ms(struct intel_display *display, i915_reg_t reg,
+ u32 mask, u32 value, unsigned int timeout_ms,
+ u32 *out_value)
{
int ret;
intel_dmc_wl_get(display, reg);
- ret = intel_wait_for_register_fw(__to_uncore(display), reg, mask,
- value, timeout_ms, out_value);
+ ret = __intel_wait_for_register(__to_uncore(display), reg, mask,
+ value, 2, timeout_ms, out_value);
intel_dmc_wl_put(display, reg);
@@ -156,36 +132,49 @@ intel_de_wait_fw(struct intel_display *display, i915_reg_t reg,
}
static inline int
-intel_de_wait_custom(struct intel_display *display, i915_reg_t reg,
- u32 mask, u32 value,
- unsigned int fast_timeout_us,
- unsigned int slow_timeout_ms, u32 *out_value)
+intel_de_wait_fw_ms(struct intel_display *display, i915_reg_t reg,
+ u32 mask, u32 value, unsigned int timeout_ms,
+ u32 *out_value)
{
- int ret;
-
- intel_dmc_wl_get(display, reg);
+ return __intel_wait_for_register_fw(__to_uncore(display), reg, mask,
+ value, 2, timeout_ms, out_value);
+}
- ret = __intel_wait_for_register(__to_uncore(display), reg, mask,
- value,
- fast_timeout_us, slow_timeout_ms, out_value);
+static inline int
+intel_de_wait_fw_us_atomic(struct intel_display *display, i915_reg_t reg,
+ u32 mask, u32 value, unsigned int timeout_us,
+ u32 *out_value)
+{
+ return __intel_wait_for_register_fw(__to_uncore(display), reg, mask,
+ value, timeout_us, 0, out_value);
+}
- intel_dmc_wl_put(display, reg);
+static inline int
+intel_de_wait_for_set_us(struct intel_display *display, i915_reg_t reg,
+ u32 mask, unsigned int timeout_us)
+{
+ return intel_de_wait_us(display, reg, mask, mask, timeout_us, NULL);
+}
- return ret;
+static inline int
+intel_de_wait_for_clear_us(struct intel_display *display, i915_reg_t reg,
+ u32 mask, unsigned int timeout_us)
+{
+ return intel_de_wait_us(display, reg, mask, 0, timeout_us, NULL);
}
static inline int
-intel_de_wait_for_set(struct intel_display *display, i915_reg_t reg,
- u32 mask, unsigned int timeout_ms)
+intel_de_wait_for_set_ms(struct intel_display *display, i915_reg_t reg,
+ u32 mask, unsigned int timeout_ms)
{
- return intel_de_wait(display, reg, mask, mask, timeout_ms);
+ return intel_de_wait_ms(display, reg, mask, mask, timeout_ms, NULL);
}
static inline int
-intel_de_wait_for_clear(struct intel_display *display, i915_reg_t reg,
- u32 mask, unsigned int timeout_ms)
+intel_de_wait_for_clear_ms(struct intel_display *display, i915_reg_t reg,
+ u32 mask, unsigned int timeout_ms)
{
- return intel_de_wait(display, reg, mask, 0, timeout_ms);
+ return intel_de_wait_ms(display, reg, mask, 0, timeout_ms, NULL);
}
/*
@@ -215,6 +204,18 @@ intel_de_write_fw(struct intel_display *display, i915_reg_t reg, u32 val)
}
static inline u32
+intel_de_rmw_fw(struct intel_display *display, i915_reg_t reg, u32 clear, u32 set)
+{
+ u32 old, val;
+
+ old = intel_de_read_fw(display, reg);
+ val = (old & ~clear) | set;
+ intel_de_write_fw(display, reg, val);
+
+ return old;
+}
+
+static inline u32
intel_de_read_notrace(struct intel_display *display, i915_reg_t reg)
{
return intel_uncore_read_notrace(__to_uncore(display), reg);
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 5dca7f96b425..095a319f8bc9 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -41,6 +41,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_fixed.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_rect.h>
#include <drm/drm_vblank.h>
@@ -51,7 +52,6 @@
#include "i915_config.h"
#include "i915_drv.h"
#include "i915_reg.h"
-#include "i915_utils.h"
#include "i9xx_plane.h"
#include "i9xx_plane_regs.h"
#include "i9xx_wm.h"
@@ -60,6 +60,7 @@
#include "intel_audio.h"
#include "intel_bo.h"
#include "intel_bw.h"
+#include "intel_casf.h"
#include "intel_cdclk.h"
#include "intel_clock_gating.h"
#include "intel_color.h"
@@ -76,6 +77,7 @@
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_display_wa.h"
#include "intel_dmc.h"
#include "intel_dp.h"
@@ -99,6 +101,7 @@
#include "intel_hdmi.h"
#include "intel_hotplug.h"
#include "intel_link_bw.h"
+#include "intel_lt_phy.h"
#include "intel_lvds.h"
#include "intel_lvds_regs.h"
#include "intel_modeset_setup.h"
@@ -129,11 +132,9 @@
#include "skl_scaler.h"
#include "skl_universal_plane.h"
#include "skl_watermark.h"
-#include "vlv_dpio_phy_regs.h"
#include "vlv_dsi.h"
#include "vlv_dsi_pll.h"
#include "vlv_dsi_regs.h"
-#include "vlv_sideband.h"
static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state);
static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state);
@@ -141,65 +142,6 @@ static void hsw_set_transconf(const struct intel_crtc_state *crtc_state);
static void bdw_set_pipe_misc(struct intel_dsb *dsb,
const struct intel_crtc_state *crtc_state);
-/* returns HPLL frequency in kHz */
-int vlv_get_hpll_vco(struct drm_device *drm)
-{
- int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };
-
- /* Obtain SKU information */
- hpll_freq = vlv_cck_read(drm, CCK_FUSE_REG) &
- CCK_FUSE_HPLL_FREQ_MASK;
-
- return vco_freq[hpll_freq] * 1000;
-}
-
-int vlv_get_cck_clock(struct drm_device *drm,
- const char *name, u32 reg, int ref_freq)
-{
- u32 val;
- int divider;
-
- val = vlv_cck_read(drm, reg);
- divider = val & CCK_FREQUENCY_VALUES;
-
- drm_WARN(drm, (val & CCK_FREQUENCY_STATUS) !=
- (divider << CCK_FREQUENCY_STATUS_SHIFT),
- "%s change in progress\n", name);
-
- return DIV_ROUND_CLOSEST(ref_freq << 1, divider + 1);
-}
-
-int vlv_get_cck_clock_hpll(struct drm_device *drm,
- const char *name, u32 reg)
-{
- struct drm_i915_private *dev_priv = to_i915(drm);
- int hpll;
-
- vlv_cck_get(drm);
-
- if (dev_priv->hpll_freq == 0)
- dev_priv->hpll_freq = vlv_get_hpll_vco(drm);
-
- hpll = vlv_get_cck_clock(drm, name, reg, dev_priv->hpll_freq);
-
- vlv_cck_put(drm);
-
- return hpll;
-}
-
-void intel_update_czclk(struct intel_display *display)
-{
- struct drm_i915_private *dev_priv = to_i915(display->drm);
-
- if (!display->platform.valleyview && !display->platform.cherryview)
- return;
-
- dev_priv->czclk_freq = vlv_get_cck_clock_hpll(display->drm, "czclk",
- CCK_CZ_CLOCK_CONTROL);
-
- drm_dbg_kms(display->drm, "CZ clock rate: %d kHz\n", dev_priv->czclk_freq);
-}
-
static bool is_hdr_mode(const struct intel_crtc_state *crtc_state)
{
return (crtc_state->active_planes &
@@ -417,8 +359,8 @@ intel_wait_for_pipe_off(const struct intel_crtc_state *old_crtc_state)
enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder;
/* Wait for the Pipe State to go off */
- if (intel_de_wait_for_clear(display, TRANSCONF(display, cpu_transcoder),
- TRANSCONF_STATE_ENABLE, 100))
+ if (intel_de_wait_for_clear_ms(display, TRANSCONF(display, cpu_transcoder),
+ TRANSCONF_STATE_ENABLE, 100))
drm_WARN(display->drm, 1, "pipe_off wait timed out\n");
} else {
intel_wait_for_pipe_scanline_stopped(crtc);
@@ -605,16 +547,13 @@ void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state)
intel_wait_for_pipe_off(old_crtc_state);
}
-u32 intel_plane_fb_max_stride(struct drm_device *drm,
- u32 pixel_format, u64 modifier)
+u32 intel_plane_fb_max_stride(struct intel_display *display,
+ const struct drm_format_info *info,
+ u64 modifier)
{
- struct intel_display *display = to_intel_display(drm);
struct intel_crtc *crtc;
struct intel_plane *plane;
- if (!HAS_DISPLAY(display))
- return 0;
-
/*
* We assume the primary plane for pipe A has
* the highest stride limits of them all,
@@ -626,10 +565,23 @@ u32 intel_plane_fb_max_stride(struct drm_device *drm,
plane = to_intel_plane(crtc->base.primary);
- return plane->max_stride(plane, pixel_format, modifier,
+ return plane->max_stride(plane, info, modifier,
DRM_MODE_ROTATE_0);
}
+u32 intel_dumb_fb_max_stride(struct drm_device *drm,
+ u32 pixel_format, u64 modifier)
+{
+ struct intel_display *display = to_intel_display(drm);
+
+ if (!HAS_DISPLAY(display))
+ return 0;
+
+ return intel_plane_fb_max_stride(display,
+ drm_get_format_info(drm, pixel_format, modifier),
+ modifier);
+}
+
void intel_set_plane_visible(struct intel_crtc_state *crtc_state,
struct intel_plane_state *plane_state,
bool visible)
@@ -891,9 +843,8 @@ static void intel_async_flip_vtd_wa(struct intel_display *display,
static bool needs_async_flip_vtd_wa(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
- struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
- return crtc_state->uapi.async_flip && i915_vtd_active(i915) &&
+ return crtc_state->uapi.async_flip && intel_display_vtd_active(display) &&
(DISPLAY_VER(display) == 9 || display->platform.broadwell ||
display->platform.haswell);
}
@@ -1040,6 +991,24 @@ static bool audio_disabling(const struct intel_crtc_state *old_crtc_state,
memcmp(old_crtc_state->eld, new_crtc_state->eld, MAX_ELD_BYTES) != 0);
}
+static bool intel_casf_enabling(const struct intel_crtc_state *new_crtc_state,
+ const struct intel_crtc_state *old_crtc_state)
+{
+ if (!new_crtc_state->hw.active)
+ return false;
+
+ return is_enabling(hw.casf_params.casf_enable, old_crtc_state, new_crtc_state);
+}
+
+static bool intel_casf_disabling(const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state)
+{
+ if (!new_crtc_state->hw.active)
+ return false;
+
+ return is_disabling(hw.casf_params.casf_enable, old_crtc_state, new_crtc_state);
+}
+
#undef is_disabling
#undef is_enabling
@@ -1195,6 +1164,9 @@ static void intel_pre_plane_update(struct intel_atomic_state *state,
if (audio_disabling(old_crtc_state, new_crtc_state))
intel_encoders_audio_disable(state, crtc);
+ if (intel_casf_disabling(old_crtc_state, new_crtc_state))
+ intel_casf_disable(new_crtc_state);
+
intel_drrs_deactivate(old_crtc_state);
if (hsw_ips_pre_update(state, crtc))
@@ -1642,8 +1614,7 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta
}
intel_set_transcoder_timings(crtc_state);
- if (HAS_VRR(display))
- intel_vrr_set_transcoder_timings(crtc_state);
+ intel_vrr_set_transcoder_timings(crtc_state);
if (cpu_transcoder != TRANSCODER_EDP)
intel_de_write(display, TRANS_MULT(display, cpu_transcoder),
@@ -2422,39 +2393,44 @@ static int intel_crtc_compute_pipe_mode(struct intel_crtc_state *crtc_state)
return 0;
}
-static int intel_crtc_vblank_delay(const struct intel_crtc_state *crtc_state)
+static int intel_crtc_set_context_latency(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
- int vblank_delay = 0;
+ int set_context_latency = 0;
if (!HAS_DSB(display))
return 0;
- vblank_delay = max(vblank_delay, intel_psr_min_vblank_delay(crtc_state));
+ set_context_latency = max(set_context_latency,
+ intel_psr_min_set_context_latency(crtc_state));
- return vblank_delay;
+ return set_context_latency;
}
-static int intel_crtc_compute_vblank_delay(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
+static int intel_crtc_compute_set_context_latency(struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
{
struct intel_display *display = to_intel_display(state);
struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode;
- int vblank_delay, max_vblank_delay;
+ int set_context_latency, max_vblank_delay;
+
+ set_context_latency = intel_crtc_set_context_latency(crtc_state);
- vblank_delay = intel_crtc_vblank_delay(crtc_state);
max_vblank_delay = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start - 1;
- if (vblank_delay > max_vblank_delay) {
- drm_dbg_kms(display->drm, "[CRTC:%d:%s] vblank delay (%d) exceeds max (%d)\n",
- crtc->base.base.id, crtc->base.name, vblank_delay, max_vblank_delay);
+ if (set_context_latency > max_vblank_delay) {
+ drm_dbg_kms(display->drm, "[CRTC:%d:%s] set context latency (%d) exceeds max (%d)\n",
+ crtc->base.base.id, crtc->base.name,
+ set_context_latency,
+ max_vblank_delay);
return -EINVAL;
}
- adjusted_mode->crtc_vblank_start += vblank_delay;
+ crtc_state->set_context_latency = set_context_latency;
+ adjusted_mode->crtc_vblank_start += set_context_latency;
return 0;
}
@@ -2466,11 +2442,11 @@ static int intel_crtc_compute_config(struct intel_atomic_state *state,
intel_atomic_get_new_crtc_state(state, crtc);
int ret;
- ret = intel_crtc_compute_vblank_delay(state, crtc);
+ ret = intel_dpll_crtc_compute_clock(state, crtc);
if (ret)
return ret;
- ret = intel_dpll_crtc_compute_clock(state, crtc);
+ ret = intel_crtc_compute_set_context_latency(state, crtc);
if (ret)
return ret;
@@ -2487,6 +2463,8 @@ static int intel_crtc_compute_config(struct intel_atomic_state *state,
if (crtc_state->has_pch_encoder)
return ilk_fdi_compute_config(crtc, crtc_state);
+ intel_vrr_compute_guardband(crtc_state);
+
return 0;
}
@@ -2678,16 +2656,19 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta
if (DISPLAY_VER(display) >= 13) {
intel_de_write(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
- crtc_vblank_start - crtc_vdisplay);
+ crtc_state->set_context_latency);
/*
* VBLANK_START not used by hw, just clear it
* to make it stand out in register dumps.
*/
crtc_vblank_start = 1;
+ } else if (DISPLAY_VER(display) == 12) {
+ /* VBLANK_START - VACTIVE defines SCL on TGL */
+ crtc_vblank_start = crtc_vdisplay + crtc_state->set_context_latency;
}
- if (DISPLAY_VER(display) >= 4)
+ if (DISPLAY_VER(display) >= 4 && DISPLAY_VER(display) < 35)
intel_de_write(display,
TRANS_VSYNCSHIFT(display, cpu_transcoder),
vsyncshift);
@@ -2768,13 +2749,16 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc
if (DISPLAY_VER(display) >= 13) {
intel_de_write(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder),
- crtc_vblank_start - crtc_vdisplay);
+ crtc_state->set_context_latency);
/*
* VBLANK_START not used by hw, just clear it
* to make it stand out in register dumps.
*/
crtc_vblank_start = 1;
+ } else if (DISPLAY_VER(display) == 12) {
+ /* VBLANK_START - VACTIVE defines SCL on TGL */
+ crtc_vblank_start = crtc_vdisplay + crtc_state->set_context_latency;
}
/*
@@ -2825,7 +2809,7 @@ static bool intel_pipe_is_interlaced(const struct intel_crtc_state *crtc_state)
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
- if (DISPLAY_VER(display) == 2)
+ if (DISPLAY_VER(display) == 2 || DISPLAY_VER(display) >= 35)
return false;
if (DISPLAY_VER(display) >= 9 ||
@@ -2881,11 +2865,24 @@ static void intel_get_transcoder_timings(struct intel_crtc *crtc,
adjusted_mode->crtc_vblank_end += 1;
}
- if (DISPLAY_VER(display) >= 13 && !transcoder_is_dsi(cpu_transcoder))
- adjusted_mode->crtc_vblank_start =
- adjusted_mode->crtc_vdisplay +
+ if (DISPLAY_VER(display) >= 13 && !transcoder_is_dsi(cpu_transcoder)) {
+ pipe_config->set_context_latency =
intel_de_read(display,
TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder));
+ adjusted_mode->crtc_vblank_start =
+ adjusted_mode->crtc_vdisplay +
+ pipe_config->set_context_latency;
+ } else if (DISPLAY_VER(display) == 12) {
+ /*
+ * TGL doesn't have a dedicated register for SCL.
+ * Instead, the hardware derives SCL from the difference between
+ * TRANS_VBLANK.vblank_start and TRANS_VTOTAL.vactive.
+ * To reflect the HW behaviour, readout the value for SCL as
+ * Vblank start - Vactive.
+ */
+ pipe_config->set_context_latency =
+ adjusted_mode->crtc_vblank_start - adjusted_mode->crtc_vdisplay;
+ }
if (DISPLAY_VER(display) >= 30)
pipe_config->min_hblank = intel_de_read(display,
@@ -3203,10 +3200,12 @@ static void hsw_set_transconf(const struct intel_crtc_state *crtc_state)
if (display->platform.haswell && crtc_state->dither)
val |= TRANSCONF_DITHER_EN | TRANSCONF_DITHER_TYPE_SP;
- if (crtc_state->hw.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
- val |= TRANSCONF_INTERLACE_IF_ID_ILK;
- else
- val |= TRANSCONF_INTERLACE_PF_PD_ILK;
+ if (DISPLAY_VER(display) < 35) {
+ if (crtc_state->hw.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE)
+ val |= TRANSCONF_INTERLACE_IF_ID_ILK;
+ else
+ val |= TRANSCONF_INTERLACE_PF_PD_ILK;
+ }
if (display->platform.haswell &&
crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB)
@@ -3952,6 +3951,20 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc,
intel_joiner_get_config(pipe_config);
intel_dsc_get_config(pipe_config);
+ /* intel_vrr_get_config() depends on .framestart_delay */
+ if (!transcoder_is_dsi(pipe_config->cpu_transcoder)) {
+ tmp = intel_de_read(display, CHICKEN_TRANS(display, pipe_config->cpu_transcoder));
+
+ pipe_config->framestart_delay = REG_FIELD_GET(HSW_FRAME_START_DELAY_MASK, tmp) + 1;
+ } else {
+ /* no idea if this is correct */
+ pipe_config->framestart_delay = 1;
+ }
+
+ /*
+ * intel_vrr_get_config() depends on TRANS_SET_CONTEXT_LATENCY
+ * readout done by intel_get_transcoder_timings().
+ */
if (!transcoder_is_dsi(pipe_config->cpu_transcoder) ||
DISPLAY_VER(display) >= 11)
intel_get_transcoder_timings(crtc, pipe_config);
@@ -4003,15 +4016,6 @@ static bool hsw_get_pipe_config(struct intel_crtc *crtc,
pipe_config->pixel_multiplier = 1;
}
- if (!transcoder_is_dsi(pipe_config->cpu_transcoder)) {
- tmp = intel_de_read(display, CHICKEN_TRANS(display, pipe_config->cpu_transcoder));
-
- pipe_config->framestart_delay = REG_FIELD_GET(HSW_FRAME_START_DELAY_MASK, tmp) + 1;
- } else {
- /* no idea if this is correct */
- pipe_config->framestart_delay = 1;
- }
-
out:
intel_display_power_put_all_in_set(display, &crtc->hw_readout_power_domains);
@@ -4258,9 +4262,14 @@ static int intel_crtc_atomic_check(struct intel_atomic_state *state,
return ret;
}
+ ret = intel_casf_compute_config(crtc_state);
+ if (ret)
+ return ret;
+
if (DISPLAY_VER(display) >= 9) {
if (intel_crtc_needs_modeset(crtc_state) ||
- intel_crtc_needs_fastset(crtc_state)) {
+ intel_crtc_needs_fastset(crtc_state) ||
+ intel_casf_needs_scaler(crtc_state)) {
ret = skl_update_scaler_crtc(crtc_state);
if (ret)
return ret;
@@ -4639,7 +4648,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
if (ret)
return ret;
- crtc_state->fec_enable = limits->force_fec_pipes & BIT(crtc->pipe);
+ crtc_state->dsc.compression_enabled_on_link = limits->link_dsc_pipes & BIT(crtc->pipe);
crtc_state->max_link_bpp_x16 = limits->max_bpp_x16[crtc->pipe];
if (crtc_state->pipe_bpp > fxp_q4_to_int(crtc_state->max_link_bpp_x16)) {
@@ -4760,8 +4769,6 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state,
struct drm_connector *connector;
int i;
- intel_vrr_compute_config_late(crtc_state);
-
for_each_new_connector_in_state(&state->base, connector,
conn_state, i) {
struct intel_encoder *encoder =
@@ -4996,9 +5003,33 @@ static bool allow_vblank_delay_fastset(const struct intel_crtc_state *old_crtc_s
* Allow fastboot to fix up vblank delay (handled via LRR
* codepaths), a bit dodgy as the registers aren't
* double buffered but seems to be working more or less...
+ *
+ * Also allow this when the VRR timing generator is always on,
+ * and optimized guardband is used. In such cases,
+ * vblank delay may vary even without inherited state, but it's
+ * still safe as VRR guardband is still same.
*/
- return HAS_LRR(display) && old_crtc_state->inherited &&
- !intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DSI);
+ return HAS_LRR(display) &&
+ (old_crtc_state->inherited || intel_vrr_always_use_vrr_tg(display)) &&
+ !intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DSI);
+}
+
+static void
+pipe_config_lt_phy_pll_mismatch(struct drm_printer *p, bool fastset,
+ const struct intel_crtc *crtc,
+ const char *name,
+ const struct intel_lt_phy_pll_state *a,
+ const struct intel_lt_phy_pll_state *b)
+{
+ struct intel_display *display = to_intel_display(crtc);
+ char *chipname = "LTPHY";
+
+ pipe_config_mismatch(p, fastset, crtc, name, chipname);
+
+ drm_printf(p, "expected:\n");
+ intel_lt_phy_dump_hw_state(display, a);
+ drm_printf(p, "found:\n");
+ intel_lt_phy_dump_hw_state(display, b);
}
bool
@@ -5125,6 +5156,16 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
} \
} while (0)
+#define PIPE_CONF_CHECK_PLL_LT(name) do { \
+ if (!intel_lt_phy_pll_compare_hw_state(&current_config->name, \
+ &pipe_config->name)) { \
+ pipe_config_lt_phy_pll_mismatch(&p, fastset, crtc, __stringify(name), \
+ &current_config->name, \
+ &pipe_config->name); \
+ ret = false; \
+ } \
+} while (0)
+
#define PIPE_CONF_CHECK_TIMINGS(name) do { \
PIPE_CONF_CHECK_I(name.crtc_hdisplay); \
PIPE_CONF_CHECK_I(name.crtc_htotal); \
@@ -5319,6 +5360,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_I(scaler_state.scaler_id);
PIPE_CONF_CHECK_I(pixel_rate);
+ PIPE_CONF_CHECK_BOOL(hw.casf_params.casf_enable);
+ PIPE_CONF_CHECK_I(hw.casf_params.win_size);
+ PIPE_CONF_CHECK_I(hw.casf_params.strength);
PIPE_CONF_CHECK_X(gamma_mode);
if (display->platform.cherryview)
@@ -5349,7 +5393,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_PLL(dpll_hw_state);
/* FIXME convert MTL+ platforms over to dpll_mgr */
- if (DISPLAY_VER(display) >= 14)
+ if (HAS_LT_PHY(display))
+ PIPE_CONF_CHECK_PLL_LT(dpll_hw_state.ltpll);
+ else if (DISPLAY_VER(display) >= 14)
PIPE_CONF_CHECK_PLL_CX0(dpll_hw_state.cx0pll);
PIPE_CONF_CHECK_X(dsi_pll.ctrl);
@@ -5443,6 +5489,8 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
PIPE_CONF_CHECK_I(vrr.guardband);
}
+ PIPE_CONF_CHECK_I(set_context_latency);
+
#undef PIPE_CONF_CHECK_X
#undef PIPE_CONF_CHECK_I
#undef PIPE_CONF_CHECK_LLI
@@ -5689,6 +5737,23 @@ static int hsw_mode_set_planes_workaround(struct intel_atomic_state *state)
return 0;
}
+u8 intel_calc_enabled_pipes(struct intel_atomic_state *state,
+ u8 enabled_pipes)
+{
+ const struct intel_crtc_state *crtc_state;
+ struct intel_crtc *crtc;
+ int i;
+
+ for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
+ if (crtc_state->hw.enable)
+ enabled_pipes |= BIT(crtc->pipe);
+ else
+ enabled_pipes &= ~BIT(crtc->pipe);
+ }
+
+ return enabled_pipes;
+}
+
u8 intel_calc_active_pipes(struct intel_atomic_state *state,
u8 active_pipes)
{
@@ -5718,12 +5783,16 @@ static int intel_modeset_checks(struct intel_atomic_state *state)
return 0;
}
-static bool lrr_params_changed(const struct drm_display_mode *old_adjusted_mode,
- const struct drm_display_mode *new_adjusted_mode)
+static bool lrr_params_changed(const struct intel_crtc_state *old_crtc_state,
+ const struct intel_crtc_state *new_crtc_state)
{
+ const struct drm_display_mode *old_adjusted_mode = &old_crtc_state->hw.adjusted_mode;
+ const struct drm_display_mode *new_adjusted_mode = &new_crtc_state->hw.adjusted_mode;
+
return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start ||
old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end ||
- old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal;
+ old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal ||
+ old_crtc_state->set_context_latency != new_crtc_state->set_context_latency;
}
static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_state,
@@ -5749,8 +5818,7 @@ static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_sta
&new_crtc_state->dp_m_n))
new_crtc_state->update_m_n = false;
- if (!lrr_params_changed(&old_crtc_state->hw.adjusted_mode,
- &new_crtc_state->hw.adjusted_mode))
+ if (!lrr_params_changed(old_crtc_state, new_crtc_state))
new_crtc_state->update_lrr = false;
if (intel_crtc_needs_modeset(new_crtc_state))
@@ -5964,6 +6032,14 @@ static int intel_async_flip_check_uapi(struct intel_atomic_state *state,
return -EINVAL;
}
+ /* FIXME: selective fetch should be disabled for async flips */
+ if (new_crtc_state->enable_psr2_sel_fetch) {
+ drm_dbg_kms(display->drm,
+ "[CRTC:%d:%s] async flip disallowed with PSR2 selective fetch\n",
+ crtc->base.base.id, crtc->base.name);
+ return -EINVAL;
+ }
+
for_each_oldnew_intel_plane_in_state(state, plane, old_plane_state,
new_plane_state, i) {
if (plane->pipe != crtc->pipe)
@@ -6342,7 +6418,6 @@ int intel_atomic_check(struct drm_device *dev,
struct intel_crtc_state *old_crtc_state, *new_crtc_state;
struct intel_crtc *crtc;
int ret, i;
- bool any_ms = false;
if (!intel_display_driver_check_access(display))
return -ENODEV;
@@ -6450,14 +6525,11 @@ int intel_atomic_check(struct drm_device *dev,
if (!intel_crtc_needs_modeset(new_crtc_state))
continue;
- any_ms = true;
-
intel_dpll_release(state, crtc);
}
- if (any_ms && !check_digital_port_conflicts(state)) {
- drm_dbg_kms(display->drm,
- "rejecting conflicting digital port configuration\n");
+ if (intel_any_crtc_needs_modeset(state) && !check_digital_port_conflicts(state)) {
+ drm_dbg_kms(display->drm, "rejecting conflicting digital port configuration\n");
ret = -EINVAL;
goto fail;
}
@@ -6466,29 +6538,25 @@ int intel_atomic_check(struct drm_device *dev,
if (ret)
goto fail;
+ for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i)
+ new_crtc_state->min_cdclk = intel_crtc_min_cdclk(new_crtc_state);
+
ret = intel_compute_global_watermarks(state);
if (ret)
goto fail;
- ret = intel_bw_atomic_check(state, any_ms);
+ ret = intel_bw_atomic_check(state);
if (ret)
goto fail;
- ret = intel_cdclk_atomic_check(state, &any_ms);
+ ret = intel_cdclk_atomic_check(state);
if (ret)
goto fail;
- if (intel_any_crtc_needs_modeset(state))
- any_ms = true;
-
- if (any_ms) {
+ if (intel_any_crtc_needs_modeset(state)) {
ret = intel_modeset_checks(state);
if (ret)
goto fail;
-
- ret = intel_modeset_calc_cdclk(state);
- if (ret)
- return ret;
}
ret = intel_pmdemand_atomic_check(state);
@@ -6739,6 +6807,11 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
intel_vrr_set_transcoder_timings(new_crtc_state);
}
+ if (intel_casf_enabling(new_crtc_state, old_crtc_state))
+ intel_casf_enable(new_crtc_state);
+ else if (new_crtc_state->hw.casf_params.strength != old_crtc_state->hw.casf_params.strength)
+ intel_casf_update_strength(new_crtc_state);
+
intel_fbc_update(state, crtc);
drm_WARN_ON(display->drm, !intel_display_power_is_enabled(display, POWER_DOMAIN_DC_OFF));
@@ -7231,6 +7304,7 @@ static void intel_atomic_dsb_finish(struct intel_atomic_state *state,
struct intel_display *display = to_intel_display(state);
struct intel_crtc_state *new_crtc_state =
intel_atomic_get_new_crtc_state(state, crtc);
+ unsigned int size = new_crtc_state->plane_color_changed ? 8192 : 1024;
if (!new_crtc_state->use_flipq &&
!new_crtc_state->use_dsb &&
@@ -7241,10 +7315,12 @@ static void intel_atomic_dsb_finish(struct intel_atomic_state *state,
* Rough estimate:
* ~64 registers per each plane * 8 planes = 512
* Double that for pipe stuff and other overhead.
+ * ~4913 registers for 3DLUT
+ * ~200 color registers * 3 HDR planes
*/
new_crtc_state->dsb_commit = intel_dsb_prepare(state, crtc, INTEL_DSB_0,
new_crtc_state->use_dsb ||
- new_crtc_state->use_flipq ? 1024 : 16);
+ new_crtc_state->use_flipq ? size : 16);
if (!new_crtc_state->dsb_commit) {
new_crtc_state->use_flipq = false;
new_crtc_state->use_dsb = false;
@@ -7307,7 +7383,7 @@ static void intel_atomic_dsb_finish(struct intel_atomic_state *state,
intel_dsb_wait_vblanks(new_crtc_state->dsb_commit, 1);
intel_vrr_send_push(new_crtc_state->dsb_commit, new_crtc_state);
- intel_dsb_wait_vblank_delay(state, new_crtc_state->dsb_commit);
+ intel_dsb_wait_for_delayed_vblank(state, new_crtc_state->dsb_commit);
intel_vrr_check_push_sent(new_crtc_state->dsb_commit,
new_crtc_state);
intel_dsb_interrupt(new_crtc_state->dsb_commit);
@@ -7397,13 +7473,13 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
*/
intel_pmdemand_pre_plane_update(state);
- if (state->modeset) {
+ if (state->modeset)
drm_atomic_helper_update_legacy_modeset_state(display->drm, &state->base);
- intel_set_cdclk_pre_plane_update(state);
+ intel_set_cdclk_pre_plane_update(state);
+ if (state->modeset)
intel_modeset_verify_disabled(state);
- }
intel_sagv_pre_plane_update(state);
@@ -7516,8 +7592,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
intel_verify_planes(state);
intel_sagv_post_plane_update(state);
- if (state->modeset)
- intel_set_cdclk_post_plane_update(state);
+ intel_set_cdclk_post_plane_update(state);
intel_pmdemand_post_plane_update(state);
drm_atomic_helper_commit_hw_done(&state->base);
@@ -8003,6 +8078,14 @@ enum drm_mode_status intel_mode_valid(struct drm_device *dev,
mode->vtotal > vtotal_max)
return MODE_V_ILLEGAL;
+ /*
+ * WM_LINETIME only goes up to (almost) 64 usec, and also
+ * knowing that the linetime is always bounded will ease the
+ * mind during various calculations.
+ */
+ if (DIV_ROUND_UP(mode->htotal * 1000, mode->clock) > 64)
+ return MODE_H_ILLEGAL;
+
return MODE_OK;
}
@@ -8327,7 +8410,5 @@ void i830_disable_pipe(struct intel_display *display, enum pipe pipe)
bool intel_scanout_needs_vtd_wa(struct intel_display *display)
{
- struct drm_i915_private *i915 = to_i915(display->drm);
-
- return IS_DISPLAY_VER(display, 6, 11) && i915_vtd_active(i915);
+ return IS_DISPLAY_VER(display, 6, 11) && intel_display_vtd_active(display);
}
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 37e2ab301a80..bcc6ccb69d2b 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -34,6 +34,7 @@ struct drm_atomic_state;
struct drm_device;
struct drm_display_mode;
struct drm_encoder;
+struct drm_format_info;
struct drm_modeset_acquire_ctx;
struct intel_atomic_state;
struct intel_crtc;
@@ -394,14 +395,19 @@ enum phy_fia {
i)
int intel_atomic_check(struct drm_device *dev, struct drm_atomic_state *state);
+u8 intel_calc_enabled_pipes(struct intel_atomic_state *state,
+ u8 enabled_pipes);
u8 intel_calc_active_pipes(struct intel_atomic_state *state,
u8 active_pipes);
void intel_link_compute_m_n(u16 bpp, int nlanes,
int pixel_clock, int link_clock,
int bw_overhead,
struct intel_link_m_n *m_n);
-u32 intel_plane_fb_max_stride(struct drm_device *drm,
- u32 pixel_format, u64 modifier);
+u32 intel_plane_fb_max_stride(struct intel_display *display,
+ const struct drm_format_info *info,
+ u64 modifier);
+u32 intel_dumb_fb_max_stride(struct drm_device *drm,
+ u32 pixel_format, u64 modifier);
enum drm_mode_status
intel_mode_valid_max_plane_size(struct intel_display *display,
const struct drm_display_mode *mode,
@@ -435,11 +441,6 @@ void intel_enable_transcoder(const struct intel_crtc_state *new_crtc_state);
void intel_disable_transcoder(const struct intel_crtc_state *old_crtc_state);
void i830_enable_pipe(struct intel_display *display, enum pipe pipe);
void i830_disable_pipe(struct intel_display *display, enum pipe pipe);
-int vlv_get_hpll_vco(struct drm_device *drm);
-int vlv_get_cck_clock(struct drm_device *drm,
- const char *name, u32 reg, int ref_freq);
-int vlv_get_cck_clock_hpll(struct drm_device *drm,
- const char *name, u32 reg);
bool intel_has_pending_fb_unpin(struct intel_display *display);
void intel_encoder_destroy(struct drm_encoder *encoder);
struct drm_display_mode *
@@ -528,7 +529,6 @@ void intel_init_display_hooks(struct intel_display *display);
void intel_setup_outputs(struct intel_display *display);
int intel_initial_commit(struct intel_display *display);
void intel_panel_sanitize_ssc(struct intel_display *display);
-void intel_update_czclk(struct intel_display *display);
enum drm_mode_status intel_mode_valid(struct drm_device *dev,
const struct drm_display_mode *mode);
int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state,
diff --git a/drivers/gpu/drm/i915/display/intel_display_conversion.c b/drivers/gpu/drm/i915/display/intel_display_conversion.c
index d56065f22655..9a47aa38cf82 100644
--- a/drivers/gpu/drm/i915/display/intel_display_conversion.c
+++ b/drivers/gpu/drm/i915/display/intel_display_conversion.c
@@ -1,15 +1,21 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2024 Intel Corporation */
-#include "i915_drv.h"
-#include "intel_display_conversion.h"
+#include <drm/intel/display_member.h>
-static struct intel_display *__i915_to_display(struct drm_i915_private *i915)
-{
- return i915->display;
-}
+#include "intel_display_conversion.h"
struct intel_display *__drm_to_display(struct drm_device *drm)
{
- return __i915_to_display(to_i915(drm));
+ /*
+ * Note: This relies on both struct drm_i915_private and struct
+ * xe_device having the struct drm_device and struct intel_display *
+ * members at the same relative offsets, as defined by struct
+ * __intel_generic_device.
+ *
+ * See also INTEL_DISPLAY_MEMBER_STATIC_ASSERT().
+ */
+ struct __intel_generic_device *d = container_of(drm, struct __intel_generic_device, drm);
+
+ return d->display;
}
diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h
index 8c226406c5cd..9b8414b77c15 100644
--- a/drivers/gpu/drm/i915/display/intel_display_core.h
+++ b/drivers/gpu/drm/i915/display/intel_display_core.h
@@ -41,6 +41,7 @@ struct intel_cdclk_vals;
struct intel_color_funcs;
struct intel_crtc;
struct intel_crtc_state;
+struct intel_display_parent_interface;
struct intel_dmc;
struct intel_dpll_global_funcs;
struct intel_dpll_mgr;
@@ -141,14 +142,13 @@ struct intel_dpll_global {
};
struct intel_frontbuffer_tracking {
+ /* protects busy_bits */
spinlock_t lock;
/*
- * Tracking bits for delayed frontbuffer flushing du to gpu activity or
- * scheduled flips.
+ * Tracking bits for delayed frontbuffer flushing due to gpu activity.
*/
unsigned busy_bits;
- unsigned flip_bits;
};
struct intel_hotplug {
@@ -291,6 +291,9 @@ struct intel_display {
/* Intel PCH: where the south display engine lives */
enum intel_pch pch_type;
+ /* Parent, or core, driver functions exposed to display */
+ const struct intel_display_parent_interface *parent;
+
/* Display functions */
struct {
/* Top level crtc-ish functions */
@@ -370,6 +373,10 @@ struct intel_display {
} dbuf;
struct {
+ struct intel_global_obj obj;
+ } dbuf_bw;
+
+ struct {
/*
* dkl.phy_lock protects against concurrent access of the
* Dekel TypeC PHYs.
@@ -475,7 +482,21 @@ struct intel_display {
struct work_struct vblank_notify_work;
- u32 de_irq_mask[I915_MAX_PIPES];
+ /*
+ * Cached value of VLV/CHV IMR to avoid reads in updating the
+ * bitfield.
+ */
+ u32 vlv_imr_mask;
+ /*
+ * Cached value of gen 5-7 DE IMR to avoid reads in updating the
+ * bitfield.
+ */
+ u32 ilk_de_imr_mask;
+ /*
+ * Cached value of BDW+ DE pipe IMR to avoid reads in updating
+ * the bitfield.
+ */
+ u32 de_pipe_imr_mask[I915_MAX_PIPES];
u32 pipestat_irq_mask[I915_MAX_PIPES];
} irq;
@@ -568,6 +589,11 @@ struct intel_display {
} state;
struct {
+ unsigned int hpll_freq;
+ unsigned int czclk_freq;
+ } vlv_clock;
+
+ struct {
/* ordered wq for modesets */
struct workqueue_struct *modeset;
diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
index 10dddec3796f..9bbfdae8d024 100644
--- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c
+++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c
@@ -12,6 +12,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include "hsw_ips.h"
#include "i915_reg.h"
@@ -47,6 +48,7 @@
#include "intel_psr_regs.h"
#include "intel_vdsc.h"
#include "intel_wm.h"
+#include "intel_tc.h"
static struct intel_display *node_to_intel_display(struct drm_info_node *node)
{
@@ -76,9 +78,6 @@ static int i915_frontbuffer_tracking(struct seq_file *m, void *unused)
seq_printf(m, "FB tracking busy bits: 0x%08x\n",
display->fb_tracking.busy_bits);
- seq_printf(m, "FB tracking flip bits: 0x%08x\n",
- display->fb_tracking.flip_bits);
-
spin_unlock(&display->fb_tracking.lock);
return 0;
@@ -246,6 +245,8 @@ static void intel_connector_info(struct seq_file *m,
{
struct intel_connector *intel_connector = to_intel_connector(connector);
const struct drm_display_mode *mode;
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct intel_digital_port *dig_port = NULL;
seq_printf(m, "[CONNECTOR:%d:%s]: status: %s\n",
connector->base.id, connector->name,
@@ -268,14 +269,19 @@ static void intel_connector_info(struct seq_file *m,
intel_dp_mst_info(m, intel_connector);
else
intel_dp_info(m, intel_connector);
+ dig_port = dp_to_dig_port(intel_attached_dp(intel_connector));
break;
case DRM_MODE_CONNECTOR_HDMIA:
intel_hdmi_info(m, intel_connector);
+ dig_port = hdmi_to_dig_port(intel_attached_hdmi(intel_connector));
break;
default:
break;
}
+ if (dig_port != NULL && intel_encoder_is_tc(&dig_port->base))
+ intel_tc_info(&p, dig_port);
+
intel_hdcp_info(m, intel_connector);
seq_printf(m, "\tmax bpc: %u\n", connector->display_info.bpc);
diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c
index a002bc6ce7b0..1170afaa8680 100644
--- a/drivers/gpu/drm/i915/display/intel_display_device.c
+++ b/drivers/gpu/drm/i915/display/intel_display_device.c
@@ -1404,8 +1404,20 @@ static const struct platform_desc bmg_desc = {
PLATFORM_GROUP(dgfx),
};
+static const u16 wcl_ids[] = {
+ INTEL_WCL_IDS(ID),
+ 0
+};
+
static const struct platform_desc ptl_desc = {
PLATFORM(pantherlake),
+ .subplatforms = (const struct subplatform_desc[]) {
+ {
+ SUBPLATFORM(pantherlake, wildcatlake),
+ .pciidlist = wcl_ids,
+ },
+ {},
+ }
};
__diag_pop();
@@ -1482,6 +1494,7 @@ static const struct {
INTEL_LNL_IDS(INTEL_DISPLAY_DEVICE, &lnl_desc),
INTEL_BMG_IDS(INTEL_DISPLAY_DEVICE, &bmg_desc),
INTEL_PTL_IDS(INTEL_DISPLAY_DEVICE, &ptl_desc),
+ INTEL_WCL_IDS(INTEL_DISPLAY_DEVICE, &ptl_desc),
};
static const struct {
@@ -1494,6 +1507,7 @@ static const struct {
{ 20, 0, &xe2_lpd_display },
{ 30, 0, &xe2_lpd_display },
{ 30, 2, &wcl_display },
+ { 35, 0, &xe2_lpd_display },
};
static const struct intel_display_device_info *
@@ -1634,7 +1648,8 @@ static void display_platforms_or(struct intel_display_platforms *dst,
bitmap_or(dst->bitmap, dst->bitmap, src->bitmap, display_platforms_num_bits());
}
-struct intel_display *intel_display_device_probe(struct pci_dev *pdev)
+struct intel_display *intel_display_device_probe(struct pci_dev *pdev,
+ const struct intel_display_parent_interface *parent)
{
struct intel_display *display;
const struct intel_display_device_info *info;
@@ -1650,6 +1665,8 @@ struct intel_display *intel_display_device_probe(struct pci_dev *pdev)
/* Add drm device backpointer as early as possible. */
display->drm = pci_get_drvdata(pdev);
+ display->parent = parent;
+
intel_display_params_copy(&display->params);
if (has_no_display(pdev)) {
diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h
index f329f1beafef..b559ef43d547 100644
--- a/drivers/gpu/drm/i915/display/intel_display_device.h
+++ b/drivers/gpu/drm/i915/display/intel_display_device.h
@@ -13,6 +13,7 @@
struct drm_printer;
struct intel_display;
+struct intel_display_parent_interface;
struct pci_dev;
/*
@@ -101,7 +102,9 @@ struct pci_dev;
/* Display ver 14.1 (based on GMD ID) */ \
func(battlemage) \
/* Display ver 30 (based on GMD ID) */ \
- func(pantherlake)
+ func(pantherlake) \
+ func(pantherlake_wildcatlake)
+
#define __MEMBER(name) unsigned long name:1;
#define __COUNT(x) 1 +
@@ -140,10 +143,13 @@ struct intel_display_platforms {
func(overlay_needs_physical); \
func(supports_tv);
+#define HAS_128B_Y_TILING(__display) (!(__display)->platform.i915g && !(__display)->platform.i915gm)
#define HAS_4TILE(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14)
#define HAS_ASYNC_FLIPS(__display) (DISPLAY_VER(__display) >= 5)
#define HAS_AS_SDP(__display) (DISPLAY_VER(__display) >= 13)
+#define HAS_AUX_CCS(__display) (IS_DISPLAY_VER(__display, 9, 12) || (__display)->platform.alderlake_p || (__display)->platform.meteorlake)
#define HAS_BIGJOINER(__display) (DISPLAY_VER(__display) >= 11 && HAS_DSC(__display))
+#define HAS_CASF(__display) (DISPLAY_VER(__display) >= 20)
#define HAS_CDCLK_CRAWL(__display) (DISPLAY_INFO(__display)->has_cdclk_crawl)
#define HAS_CDCLK_SQUASH(__display) (DISPLAY_INFO(__display)->has_cdclk_squash)
#define HAS_CMRR(__display) (DISPLAY_VER(__display) >= 20)
@@ -155,7 +161,7 @@ struct intel_display_platforms {
#define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0)
#define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc)
#define HAS_DMC_WAKELOCK(__display) (DISPLAY_VER(__display) >= 20)
-#define HAS_DOUBLE_BUFFERED_M_N(__display) (DISPLAY_VER(__display) >= 9 || (__display)->platform.broadwell)
+#define HAS_DOUBLE_BUFFERED_M_N(__display) (IS_DISPLAY_VER((__display), 9, 14) || (__display)->platform.broadwell)
#define HAS_DOUBLE_BUFFERED_LUT(__display) (DISPLAY_VER(__display) >= 30)
#define HAS_DOUBLE_WIDE(__display) (DISPLAY_VER(__display) < 4)
#define HAS_DP20(__display) ((__display)->platform.dg2 || DISPLAY_VER(__display) >= 14)
@@ -308,7 +314,8 @@ struct intel_display_device_info {
bool intel_display_device_present(struct intel_display *display);
bool intel_display_device_enabled(struct intel_display *display);
-struct intel_display *intel_display_device_probe(struct pci_dev *pdev);
+struct intel_display *intel_display_device_probe(struct pci_dev *pdev,
+ const struct intel_display_parent_interface *parent);
void intel_display_device_remove(struct intel_display *display);
void intel_display_device_info_runtime_init(struct intel_display *display);
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
index cf1c14412abe..7e000ba3e08b 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
@@ -14,11 +14,12 @@
#include <drm/drm_client_event.h>
#include <drm/drm_mode_config.h>
#include <drm/drm_privacy_screen_consumer.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include "i915_drv.h"
-#include "i915_utils.h"
+#include "i915_utils.h" /* for i915_inject_probe_failure() */
#include "i9xx_wm.h"
#include "intel_acpi.h"
#include "intel_atomic.h"
@@ -28,12 +29,15 @@
#include "intel_cdclk.h"
#include "intel_color.h"
#include "intel_crtc.h"
+#include "intel_cursor.h"
+#include "intel_dbuf_bw.h"
#include "intel_display_core.h"
#include "intel_display_debugfs.h"
#include "intel_display_driver.h"
#include "intel_display_irq.h"
#include "intel_display_power.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_display_wa.h"
#include "intel_dkl_phy.h"
#include "intel_dmc.h"
@@ -145,17 +149,7 @@ static void intel_mode_config_init(struct intel_display *display)
mode_config->max_height = 2048;
}
- if (display->platform.i845g || display->platform.i865g) {
- mode_config->cursor_width = display->platform.i845g ? 64 : 512;
- mode_config->cursor_height = 1023;
- } else if (display->platform.i830 || display->platform.i85x ||
- display->platform.i915g || display->platform.i915gm) {
- mode_config->cursor_width = 64;
- mode_config->cursor_height = 64;
- } else {
- mode_config->cursor_width = 256;
- mode_config->cursor_height = 256;
- }
+ intel_cursor_mode_config_init(display);
}
static void intel_mode_config_cleanup(struct intel_display *display)
@@ -285,6 +279,10 @@ int intel_display_driver_probe_noirq(struct intel_display *display)
if (ret)
goto cleanup_wq_unordered;
+ ret = intel_dbuf_bw_init(display);
+ if (ret)
+ goto cleanup_wq_unordered;
+
ret = intel_bw_init(display);
if (ret)
goto cleanup_wq_unordered;
@@ -482,7 +480,6 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
intel_dpll_init(display);
intel_fdi_pll_freq_update(display);
- intel_update_czclk(display);
intel_display_driver_init_hw(display);
intel_dpll_update_ref_clks(display);
diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c
index 123e054affbe..43b27deb4a26 100644
--- a/drivers/gpu/drm/i915/display/intel_display_irq.c
+++ b/drivers/gpu/drm/i915/display/intel_display_irq.c
@@ -3,6 +3,7 @@
* Copyright © 2023 Intel Corporation
*/
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "i915_drv.h"
@@ -140,14 +141,14 @@ void ilk_update_display_irq(struct intel_display *display,
lockdep_assert_held(&display->irq.lock);
drm_WARN_ON(display->drm, enabled_irq_mask & ~interrupt_mask);
- new_val = dev_priv->irq_mask;
+ new_val = display->irq.ilk_de_imr_mask;
new_val &= ~interrupt_mask;
new_val |= (~enabled_irq_mask & interrupt_mask);
- if (new_val != dev_priv->irq_mask &&
+ if (new_val != display->irq.ilk_de_imr_mask &&
!drm_WARN_ON(display->drm, !intel_irqs_enabled(dev_priv))) {
- dev_priv->irq_mask = new_val;
- intel_de_write(display, DEIMR, dev_priv->irq_mask);
+ display->irq.ilk_de_imr_mask = new_val;
+ intel_de_write(display, DEIMR, display->irq.ilk_de_imr_mask);
intel_de_posting_read(display, DEIMR);
}
}
@@ -215,13 +216,13 @@ static void bdw_update_pipe_irq(struct intel_display *display,
if (drm_WARN_ON(display->drm, !intel_irqs_enabled(dev_priv)))
return;
- new_val = display->irq.de_irq_mask[pipe];
+ new_val = display->irq.de_pipe_imr_mask[pipe];
new_val &= ~interrupt_mask;
new_val |= (~enabled_irq_mask & interrupt_mask);
- if (new_val != display->irq.de_irq_mask[pipe]) {
- display->irq.de_irq_mask[pipe] = new_val;
- intel_de_write(display, GEN8_DE_PIPE_IMR(pipe), display->irq.de_irq_mask[pipe]);
+ if (new_val != display->irq.de_pipe_imr_mask[pipe]) {
+ display->irq.de_pipe_imr_mask[pipe] = new_val;
+ intel_de_write(display, GEN8_DE_PIPE_IMR(pipe), display->irq.de_pipe_imr_mask[pipe]);
intel_de_posting_read(display, GEN8_DE_PIPE_IMR(pipe));
}
}
@@ -872,7 +873,7 @@ static void ilk_gtt_fault_irq_handler(struct intel_display *display)
}
}
-void ilk_display_irq_handler(struct intel_display *display, u32 de_iir)
+static void _ilk_display_irq_handler(struct intel_display *display, u32 de_iir)
{
enum pipe pipe;
u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG;
@@ -923,7 +924,7 @@ void ilk_display_irq_handler(struct intel_display *display, u32 de_iir)
ilk_display_rps_irq_handler(display);
}
-void ivb_display_irq_handler(struct intel_display *display, u32 de_iir)
+static void _ivb_display_irq_handler(struct intel_display *display, u32 de_iir)
{
enum pipe pipe;
u32 hotplug_trigger = de_iir & DE_DP_A_HOTPLUG_IVB;
@@ -972,6 +973,53 @@ void ivb_display_irq_handler(struct intel_display *display, u32 de_iir)
}
}
+void ilk_display_irq_master_disable(struct intel_display *display, u32 *de_ier, u32 *sde_ier)
+{
+ /* disable master interrupt before clearing iir */
+ *de_ier = intel_de_read_fw(display, DEIER);
+ intel_de_write_fw(display, DEIER, *de_ier & ~DE_MASTER_IRQ_CONTROL);
+
+ /*
+ * Disable south interrupts. We'll only write to SDEIIR once, so further
+ * interrupts will be stored on its back queue, and then we'll be able
+ * to process them after we restore SDEIER (as soon as we restore it,
+ * we'll get an interrupt if SDEIIR still has something to process due
+ * to its back queue).
+ */
+ if (!HAS_PCH_NOP(display)) {
+ *sde_ier = intel_de_read_fw(display, SDEIER);
+ intel_de_write_fw(display, SDEIER, 0);
+ } else {
+ *sde_ier = 0;
+ }
+}
+
+void ilk_display_irq_master_enable(struct intel_display *display, u32 de_ier, u32 sde_ier)
+{
+ intel_de_write_fw(display, DEIER, de_ier);
+
+ if (sde_ier)
+ intel_de_write_fw(display, SDEIER, sde_ier);
+}
+
+bool ilk_display_irq_handler(struct intel_display *display)
+{
+ u32 de_iir;
+ bool handled = false;
+
+ de_iir = intel_de_read_fw(display, DEIIR);
+ if (de_iir) {
+ intel_de_write_fw(display, DEIIR, de_iir);
+ if (DISPLAY_VER(display) >= 7)
+ _ivb_display_irq_handler(display, de_iir);
+ else
+ _ilk_display_irq_handler(display, de_iir);
+ handled = true;
+ }
+
+ return handled;
+}
+
static u32 gen8_de_port_aux_mask(struct intel_display *display)
{
u32 mask;
@@ -1865,8 +1913,6 @@ void vlv_display_error_irq_handler(struct intel_display *display,
static void _vlv_display_irq_reset(struct intel_display *display)
{
- struct drm_i915_private *dev_priv = to_i915(display->drm);
-
if (display->platform.cherryview)
intel_de_write(display, DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
else
@@ -1881,7 +1927,7 @@ static void _vlv_display_irq_reset(struct intel_display *display)
i9xx_pipestat_irq_reset(display);
intel_display_irq_regs_reset(display, VLV_IRQ_REGS);
- dev_priv->irq_mask = ~0u;
+ display->irq.vlv_imr_mask = ~0u;
}
void vlv_display_irq_reset(struct intel_display *display)
@@ -1902,6 +1948,22 @@ void i9xx_display_irq_reset(struct intel_display *display)
i9xx_pipestat_irq_reset(display);
}
+u32 i9xx_display_irq_enable_mask(struct intel_display *display)
+{
+ u32 enable_mask;
+
+ enable_mask = I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT;
+
+ if (DISPLAY_VER(display) >= 3)
+ enable_mask |= I915_ASLE_INTERRUPT;
+
+ if (HAS_HOTPLUG(display))
+ enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
+
+ return enable_mask;
+}
+
void i915_display_irq_postinstall(struct intel_display *display)
{
/*
@@ -1939,7 +2001,6 @@ static u32 vlv_error_mask(void)
static void _vlv_display_irq_postinstall(struct intel_display *display)
{
- struct drm_i915_private *dev_priv = to_i915(display->drm);
u32 pipestat_mask;
u32 enable_mask;
enum pipe pipe;
@@ -1973,11 +2034,11 @@ static void _vlv_display_irq_postinstall(struct intel_display *display)
enable_mask |= I915_DISPLAY_PIPE_C_EVENT_INTERRUPT |
I915_LPE_PIPE_C_INTERRUPT;
- drm_WARN_ON(display->drm, dev_priv->irq_mask != ~0u);
+ drm_WARN_ON(display->drm, display->irq.vlv_imr_mask != ~0u);
- dev_priv->irq_mask = ~enable_mask;
+ display->irq.vlv_imr_mask = ~enable_mask;
- intel_display_irq_regs_init(display, VLV_IRQ_REGS, dev_priv->irq_mask, enable_mask);
+ intel_display_irq_regs_init(display, VLV_IRQ_REGS, display->irq.vlv_imr_mask, enable_mask);
}
void vlv_display_irq_postinstall(struct intel_display *display)
@@ -1988,7 +2049,7 @@ void vlv_display_irq_postinstall(struct intel_display *display)
spin_unlock_irq(&display->irq.lock);
}
-void ibx_display_irq_reset(struct intel_display *display)
+static void ibx_display_irq_reset(struct intel_display *display)
{
if (HAS_PCH_NOP(display))
return;
@@ -1999,6 +2060,24 @@ void ibx_display_irq_reset(struct intel_display *display)
intel_de_write(display, SERR_INT, 0xffffffff);
}
+void ilk_display_irq_reset(struct intel_display *display)
+{
+ struct intel_uncore *uncore = to_intel_uncore(display->drm);
+
+ gen2_irq_reset(uncore, DE_IRQ_REGS);
+ display->irq.ilk_de_imr_mask = ~0u;
+
+ if (DISPLAY_VER(display) == 7)
+ intel_de_write(display, GEN7_ERR_INT, 0xffffffff);
+
+ if (display->platform.haswell) {
+ intel_de_write(display, EDP_PSR_IMR, 0xffffffff);
+ intel_de_write(display, EDP_PSR_IIR, 0xffffffff);
+ }
+
+ ibx_display_irq_reset(display);
+}
+
void gen8_display_irq_reset(struct intel_display *display)
{
enum pipe pipe;
@@ -2088,8 +2167,8 @@ void gen8_irq_power_well_post_enable(struct intel_display *display,
for_each_pipe_masked(display, pipe, pipe_mask)
intel_display_irq_regs_init(display, GEN8_DE_PIPE_IRQ_REGS(pipe),
- display->irq.de_irq_mask[pipe],
- ~display->irq.de_irq_mask[pipe] | extra_ier);
+ display->irq.de_pipe_imr_mask[pipe],
+ ~display->irq.de_pipe_imr_mask[pipe] | extra_ier);
spin_unlock_irq(&display->irq.lock);
}
@@ -2183,8 +2262,6 @@ out:
void ilk_de_irq_postinstall(struct intel_display *display)
{
- struct drm_i915_private *i915 = to_i915(display->drm);
-
u32 display_mask, extra_mask;
if (DISPLAY_VER(display) >= 7) {
@@ -2216,11 +2293,11 @@ void ilk_de_irq_postinstall(struct intel_display *display)
if (display->platform.ironlake && display->platform.mobile)
extra_mask |= DE_PCU_EVENT;
- i915->irq_mask = ~display_mask;
+ display->irq.ilk_de_imr_mask = ~display_mask;
ibx_irq_postinstall(display);
- intel_display_irq_regs_init(display, DE_IRQ_REGS, i915->irq_mask,
+ intel_display_irq_regs_init(display, DE_IRQ_REGS, display->irq.ilk_de_imr_mask,
display_mask | extra_mask);
}
@@ -2305,12 +2382,12 @@ void gen8_de_irq_postinstall(struct intel_display *display)
}
for_each_pipe(display, pipe) {
- display->irq.de_irq_mask[pipe] = ~de_pipe_masked;
+ display->irq.de_pipe_imr_mask[pipe] = ~de_pipe_masked;
if (intel_display_power_is_enabled(display,
POWER_DOMAIN_PIPE(pipe)))
intel_display_irq_regs_init(display, GEN8_DE_PIPE_IRQ_REGS(pipe),
- display->irq.de_irq_mask[pipe],
+ display->irq.de_pipe_imr_mask[pipe],
de_pipe_enables);
}
diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.h b/drivers/gpu/drm/i915/display/intel_display_irq.h
index c66db3851da4..84acd31948cf 100644
--- a/drivers/gpu/drm/i915/display/intel_display_irq.h
+++ b/drivers/gpu/drm/i915/display/intel_display_irq.h
@@ -47,8 +47,9 @@ void i965_disable_vblank(struct drm_crtc *crtc);
void ilk_disable_vblank(struct drm_crtc *crtc);
void bdw_disable_vblank(struct drm_crtc *crtc);
-void ivb_display_irq_handler(struct intel_display *display, u32 de_iir);
-void ilk_display_irq_handler(struct intel_display *display, u32 de_iir);
+void ilk_display_irq_master_disable(struct intel_display *display, u32 *de_ier, u32 *sde_ier);
+void ilk_display_irq_master_enable(struct intel_display *display, u32 de_ier, u32 sde_ier);
+bool ilk_display_irq_handler(struct intel_display *display);
void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl);
void gen11_display_irq_handler(struct intel_display *display);
@@ -56,11 +57,12 @@ u32 gen11_gu_misc_irq_ack(struct intel_display *display, const u32 master_ctl);
void gen11_gu_misc_irq_handler(struct intel_display *display, const u32 iir);
void i9xx_display_irq_reset(struct intel_display *display);
-void ibx_display_irq_reset(struct intel_display *display);
+void ilk_display_irq_reset(struct intel_display *display);
void vlv_display_irq_reset(struct intel_display *display);
void gen8_display_irq_reset(struct intel_display *display);
void gen11_display_irq_reset(struct intel_display *display);
+u32 i9xx_display_irq_enable_mask(struct intel_display *display);
void i915_display_irq_postinstall(struct intel_display *display);
void i965_display_irq_postinstall(struct intel_display *display);
void vlv_display_irq_postinstall(struct intel_display *display);
diff --git a/drivers/gpu/drm/i915/display/intel_display_jiffies.h b/drivers/gpu/drm/i915/display/intel_display_jiffies.h
new file mode 100644
index 000000000000..c060c567e262
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_display_jiffies.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __INTEL_DISPLAY_JIFFIES_H__
+#define __INTEL_DISPLAY_JIFFIES_H__
+
+#include <linux/jiffies.h>
+
+static inline unsigned long msecs_to_jiffies_timeout(const unsigned int m)
+{
+ unsigned long j = msecs_to_jiffies(m);
+
+ return min_t(unsigned long, MAX_JIFFY_OFFSET, j + 1);
+}
+
+/*
+ * If you need to wait X milliseconds between events A and B, but event B
+ * doesn't happen exactly after event A, you record the timestamp (jiffies) of
+ * when event A happened, then just before event B you call this function and
+ * pass the timestamp as the first argument, and X as the second argument.
+ */
+static inline void
+wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms)
+{
+ unsigned long target_jiffies, tmp_jiffies, remaining_jiffies;
+
+ /*
+ * Don't re-read the value of "jiffies" every time since it may change
+ * behind our back and break the math.
+ */
+ tmp_jiffies = jiffies;
+ target_jiffies = timestamp_jiffies +
+ msecs_to_jiffies_timeout(to_wait_ms);
+
+ if (time_after(target_jiffies, tmp_jiffies)) {
+ remaining_jiffies = target_jiffies - tmp_jiffies;
+ while (remaining_jiffies)
+ remaining_jiffies =
+ schedule_timeout_uninterruptible(remaining_jiffies);
+ }
+}
+
+#endif /* __INTEL_DISPLAY_JIFFIES_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_display_limits.h b/drivers/gpu/drm/i915/display/intel_display_limits.h
index f0fa27e365ab..cb3c9c665c44 100644
--- a/drivers/gpu/drm/i915/display/intel_display_limits.h
+++ b/drivers/gpu/drm/i915/display/intel_display_limits.h
@@ -138,4 +138,13 @@ enum hpd_pin {
HPD_NUM_PINS
};
+enum intel_color_block {
+ INTEL_PLANE_CB_PRE_CSC_LUT,
+ INTEL_PLANE_CB_CSC,
+ INTEL_PLANE_CB_POST_CSC_LUT,
+ INTEL_PLANE_CB_3DLUT,
+
+ INTEL_CB_MAX
+};
+
#endif /* __INTEL_DISPLAY_LIMITS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
index da4babfd6bcb..2a4cc1dcc293 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -6,12 +6,13 @@
#include <linux/iopoll.h>
#include <linux/string_helpers.h>
+#include <drm/drm_print.h>
+
#include "soc/intel_dram.h"
#include "i915_drv.h"
#include "i915_irq.h"
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_backlight_regs.h"
#include "intel_cdclk.h"
#include "intel_clock_gating.h"
@@ -23,6 +24,7 @@
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dmc.h"
#include "intel_mchbar_regs.h"
#include "intel_pch_refclk.h"
@@ -1290,9 +1292,8 @@ static void hsw_disable_lcpll(struct intel_display *display,
val |= LCPLL_CD_SOURCE_FCLK;
intel_de_write(display, LCPLL_CTL, val);
- ret = intel_de_wait_custom(display, LCPLL_CTL,
- LCPLL_CD_SOURCE_FCLK_DONE, LCPLL_CD_SOURCE_FCLK_DONE,
- 1, 0, NULL);
+ ret = intel_de_wait_for_set_us(display, LCPLL_CTL,
+ LCPLL_CD_SOURCE_FCLK_DONE, 1);
if (ret)
drm_err(display->drm, "Switching to FCLK failed\n");
@@ -1303,7 +1304,7 @@ static void hsw_disable_lcpll(struct intel_display *display,
intel_de_write(display, LCPLL_CTL, val);
intel_de_posting_read(display, LCPLL_CTL);
- if (intel_de_wait_for_clear(display, LCPLL_CTL, LCPLL_PLL_LOCK, 1))
+ if (intel_de_wait_for_clear_ms(display, LCPLL_CTL, LCPLL_PLL_LOCK, 1))
drm_err(display->drm, "LCPLL still locked\n");
val = hsw_read_dcomp(display);
@@ -1360,15 +1361,14 @@ static void hsw_restore_lcpll(struct intel_display *display)
val &= ~LCPLL_PLL_DISABLE;
intel_de_write(display, LCPLL_CTL, val);
- if (intel_de_wait_for_set(display, LCPLL_CTL, LCPLL_PLL_LOCK, 5))
+ if (intel_de_wait_for_set_ms(display, LCPLL_CTL, LCPLL_PLL_LOCK, 5))
drm_err(display->drm, "LCPLL not locked yet\n");
if (val & LCPLL_CD_SOURCE_FCLK) {
intel_de_rmw(display, LCPLL_CTL, LCPLL_CD_SOURCE_FCLK, 0);
- ret = intel_de_wait_custom(display, LCPLL_CTL,
- LCPLL_CD_SOURCE_FCLK_DONE, 0,
- 1, 0, NULL);
+ ret = intel_de_wait_for_clear_us(display, LCPLL_CTL,
+ LCPLL_CD_SOURCE_FCLK_DONE, 1);
if (ret)
drm_err(display->drm,
"Switching back to LCPLL failed\n");
@@ -1436,6 +1436,9 @@ static void intel_pch_reset_handshake(struct intel_display *display,
i915_reg_t reg;
u32 reset_bits;
+ if (DISPLAY_VER(display) >= 35)
+ return;
+
if (display->platform.ivybridge) {
reg = GEN7_MSG_CTL;
reset_bits = WAIT_FOR_PCH_FLR_ACK | WAIT_FOR_PCH_RESET_ACK;
diff --git a/drivers/gpu/drm/i915/display/intel_display_power_map.c b/drivers/gpu/drm/i915/display/intel_display_power_map.c
index 39b71fffa2cd..9b49952994ce 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power_map.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power_map.c
@@ -1516,7 +1516,11 @@ static const struct i915_power_well_desc xelpdp_power_wells_main[] = {
.ops = &hsw_power_well_ops,
.irq_pipe_mask = BIT(PIPE_D),
.has_fuses = true,
- }, {
+ },
+};
+
+static const struct i915_power_well_desc xelpdp_power_wells_aux[] = {
+ {
.instances = &I915_PW_INSTANCES(
I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A),
I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B),
@@ -1534,6 +1538,7 @@ static const struct i915_power_well_desc_list xelpdp_power_wells[] = {
I915_PW_DESCRIPTORS(icl_power_wells_pw_1),
I915_PW_DESCRIPTORS(xelpd_power_wells_dc_off),
I915_PW_DESCRIPTORS(xelpdp_power_wells_main),
+ I915_PW_DESCRIPTORS(xelpdp_power_wells_aux),
};
I915_DECL_PW_DOMAINS(xe2lpd_pwdoms_pica_tc,
@@ -1584,6 +1589,7 @@ static const struct i915_power_well_desc_list xe2lpd_power_wells[] = {
I915_PW_DESCRIPTORS(xe2lpd_power_wells_dcoff),
I915_PW_DESCRIPTORS(xelpdp_power_wells_main),
I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica),
+ I915_PW_DESCRIPTORS(xelpdp_power_wells_aux),
};
/*
@@ -1677,16 +1683,6 @@ static const struct i915_power_well_desc xe3lpd_power_wells_main[] = {
.ops = &hsw_power_well_ops,
.irq_pipe_mask = BIT(PIPE_D),
.has_fuses = true,
- }, {
- .instances = &I915_PW_INSTANCES(
- I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A),
- I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B),
- I915_PW("AUX_TC1", &xelpdp_pwdoms_aux_tc1, .xelpdp.aux_ch = AUX_CH_USBC1),
- I915_PW("AUX_TC2", &xelpdp_pwdoms_aux_tc2, .xelpdp.aux_ch = AUX_CH_USBC2),
- I915_PW("AUX_TC3", &xelpdp_pwdoms_aux_tc3, .xelpdp.aux_ch = AUX_CH_USBC3),
- I915_PW("AUX_TC4", &xelpdp_pwdoms_aux_tc4, .xelpdp.aux_ch = AUX_CH_USBC4),
- ),
- .ops = &xelpdp_aux_power_well_ops,
},
};
@@ -1715,6 +1711,7 @@ static const struct i915_power_well_desc_list xe3lpd_power_wells[] = {
I915_PW_DESCRIPTORS(xe3lpd_power_wells_dcoff),
I915_PW_DESCRIPTORS(xe3lpd_power_wells_main),
I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica),
+ I915_PW_DESCRIPTORS(xelpdp_power_wells_aux),
};
static const struct i915_power_well_desc wcl_power_wells_main[] = {
@@ -1751,7 +1748,11 @@ static const struct i915_power_well_desc wcl_power_wells_main[] = {
.ops = &hsw_power_well_ops,
.irq_pipe_mask = BIT(PIPE_C),
.has_fuses = true,
- }, {
+ },
+};
+
+static const struct i915_power_well_desc wcl_power_wells_aux[] = {
+ {
.instances = &I915_PW_INSTANCES(
I915_PW("AUX_A", &icl_pwdoms_aux_a, .xelpdp.aux_ch = AUX_CH_A),
I915_PW("AUX_B", &icl_pwdoms_aux_b, .xelpdp.aux_ch = AUX_CH_B),
@@ -1768,6 +1769,7 @@ static const struct i915_power_well_desc_list wcl_power_wells[] = {
I915_PW_DESCRIPTORS(xe3lpd_power_wells_dcoff),
I915_PW_DESCRIPTORS(wcl_power_wells_main),
I915_PW_DESCRIPTORS(xe2lpd_power_wells_pica),
+ I915_PW_DESCRIPTORS(wcl_power_wells_aux),
};
static void init_power_well_domains(const struct i915_power_well_instance *inst,
diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c
index 5e88b930f5aa..f4f7e73acc87 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power_well.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c
@@ -5,6 +5,8 @@
#include <linux/iopoll.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_irq.h"
#include "i915_reg.h"
@@ -291,8 +293,8 @@ static void hsw_wait_for_power_well_enable(struct intel_display *display,
}
/* Timeout for PW1:10 us, AUX:not specified, other PWs:20 us. */
- if (intel_de_wait_for_set(display, regs->driver,
- HSW_PWR_WELL_CTL_STATE(pw_idx), timeout)) {
+ if (intel_de_wait_for_set_ms(display, regs->driver,
+ HSW_PWR_WELL_CTL_STATE(pw_idx), timeout)) {
drm_dbg_kms(display->drm, "%s power well enable timeout\n",
intel_power_well_name(power_well));
@@ -336,9 +338,9 @@ static void hsw_wait_for_power_well_disable(struct intel_display *display,
*/
reqs = hsw_power_well_requesters(display, regs, pw_idx);
- ret = intel_de_wait_for_clear(display, regs->driver,
- HSW_PWR_WELL_CTL_STATE(pw_idx),
- reqs ? 0 : 1);
+ ret = intel_de_wait_for_clear_ms(display, regs->driver,
+ HSW_PWR_WELL_CTL_STATE(pw_idx),
+ reqs ? 0 : 1);
if (!ret)
return;
@@ -357,8 +359,8 @@ static void gen9_wait_for_power_well_fuses(struct intel_display *display,
{
/* Timeout 5us for PG#0, for other PGs 1us */
drm_WARN_ON(display->drm,
- intel_de_wait_for_set(display, SKL_FUSE_STATUS,
- SKL_FUSE_PG_DIST_STATUS(pg), 1));
+ intel_de_wait_for_set_ms(display, SKL_FUSE_STATUS,
+ SKL_FUSE_PG_DIST_STATUS(pg), 1));
}
static void hsw_power_well_enable(struct intel_display *display,
@@ -1356,6 +1358,7 @@ static void assert_chv_phy_status(struct intel_display *display)
u32 phy_control = display->power.chv_phy_control;
u32 phy_status = 0;
u32 phy_status_mask = 0xffffffff;
+ u32 val;
/*
* The BIOS can leave the PHY is some weird state
@@ -1443,12 +1446,11 @@ static void assert_chv_phy_status(struct intel_display *display)
* The PHY may be busy with some initial calibration and whatnot,
* so the power state can take a while to actually change.
*/
- if (intel_de_wait(display, DISPLAY_PHY_STATUS,
- phy_status_mask, phy_status, 10))
+ if (intel_de_wait_ms(display, DISPLAY_PHY_STATUS,
+ phy_status_mask, phy_status, 10, &val))
drm_err(display->drm,
"Unexpected PHY_STATUS 0x%08x, expected 0x%08x (PHY_CONTROL=0x%08x)\n",
- intel_de_read(display, DISPLAY_PHY_STATUS) & phy_status_mask,
- phy_status, display->power.chv_phy_control);
+ val & phy_status_mask, phy_status, display->power.chv_phy_control);
}
#undef BITS_SET
@@ -1474,8 +1476,8 @@ static void chv_dpio_cmn_power_well_enable(struct intel_display *display,
vlv_set_power_well(display, power_well, true);
/* Poll for phypwrgood signal */
- if (intel_de_wait_for_set(display, DISPLAY_PHY_STATUS,
- PHY_POWERGOOD(phy), 1))
+ if (intel_de_wait_for_set_ms(display, DISPLAY_PHY_STATUS,
+ PHY_POWERGOOD(phy), 1))
drm_err(display->drm, "Display PHY %d is not power up\n",
phy);
@@ -1864,18 +1866,36 @@ static void xelpdp_aux_power_well_enable(struct intel_display *display,
* expected to just wait a fixed 600us after raising the request
* bit.
*/
- usleep_range(600, 1200);
+ if (DISPLAY_VER(display) >= 35) {
+ if (intel_de_wait_for_set_ms(display, XELPDP_DP_AUX_CH_CTL(display, aux_ch),
+ XELPDP_DP_AUX_CH_CTL_POWER_STATUS, 2))
+ drm_warn(display->drm,
+ "Timeout waiting for PHY %c AUX channel power to be up\n",
+ phy_name(phy));
+ } else {
+ usleep_range(600, 1200);
+ }
}
static void xelpdp_aux_power_well_disable(struct intel_display *display,
struct i915_power_well *power_well)
{
enum aux_ch aux_ch = i915_power_well_instance(power_well)->xelpdp.aux_ch;
+ enum phy phy = icl_aux_pw_to_phy(display, power_well);
intel_de_rmw(display, XELPDP_DP_AUX_CH_CTL(display, aux_ch),
XELPDP_DP_AUX_CH_CTL_POWER_REQUEST,
0);
- usleep_range(10, 30);
+
+ if (DISPLAY_VER(display) >= 35) {
+ if (intel_de_wait_for_clear_ms(display, XELPDP_DP_AUX_CH_CTL(display, aux_ch),
+ XELPDP_DP_AUX_CH_CTL_POWER_STATUS, 1))
+ drm_warn(display->drm,
+ "Timeout waiting for PHY %c AUX channel to powerdown\n",
+ phy_name(phy));
+ } else {
+ usleep_range(10, 30);
+ }
}
static bool xelpdp_aux_power_well_enabled(struct intel_display *display,
@@ -1893,8 +1913,8 @@ static void xe2lpd_pica_power_well_enable(struct intel_display *display,
intel_de_write(display, XE2LPD_PICA_PW_CTL,
XE2LPD_PICA_CTL_POWER_REQUEST);
- if (intel_de_wait_for_set(display, XE2LPD_PICA_PW_CTL,
- XE2LPD_PICA_CTL_POWER_STATUS, 1)) {
+ if (intel_de_wait_for_set_ms(display, XE2LPD_PICA_PW_CTL,
+ XE2LPD_PICA_CTL_POWER_STATUS, 1)) {
drm_dbg_kms(display->drm, "pica power well enable timeout\n");
drm_WARN(display->drm, 1, "Power well PICA timeout when enabled");
@@ -1906,8 +1926,8 @@ static void xe2lpd_pica_power_well_disable(struct intel_display *display,
{
intel_de_write(display, XE2LPD_PICA_PW_CTL, 0);
- if (intel_de_wait_for_clear(display, XE2LPD_PICA_PW_CTL,
- XE2LPD_PICA_CTL_POWER_STATUS, 1)) {
+ if (intel_de_wait_for_clear_ms(display, XE2LPD_PICA_PW_CTL,
+ XE2LPD_PICA_CTL_POWER_STATUS, 1)) {
drm_dbg_kms(display->drm, "pica power well disable timeout\n");
drm_WARN(display->drm, 1, "Power well PICA timeout when disabled");
diff --git a/drivers/gpu/drm/i915/display/intel_display_reset.c b/drivers/gpu/drm/i915/display/intel_display_reset.c
index f5f38dca14d7..03e8c68d2913 100644
--- a/drivers/gpu/drm/i915/display/intel_display_reset.c
+++ b/drivers/gpu/drm/i915/display/intel_display_reset.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_print.h>
#include "i915_drv.h"
#include "intel_clock_gating.h"
diff --git a/drivers/gpu/drm/i915/display/intel_display_rpm.c b/drivers/gpu/drm/i915/display/intel_display_rpm.c
index 56c4024201c1..0a331f89b4db 100644
--- a/drivers/gpu/drm/i915/display/intel_display_rpm.c
+++ b/drivers/gpu/drm/i915/display/intel_display_rpm.c
@@ -1,69 +1,62 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
-#include "i915_drv.h"
+#include <drm/intel/display_parent_interface.h>
+
#include "intel_display_core.h"
#include "intel_display_rpm.h"
-#include "intel_runtime_pm.h"
-
-static struct intel_runtime_pm *display_to_rpm(struct intel_display *display)
-{
- struct drm_i915_private *i915 = to_i915(display->drm);
-
- return &i915->runtime_pm;
-}
struct ref_tracker *intel_display_rpm_get_raw(struct intel_display *display)
{
- return intel_runtime_pm_get_raw(display_to_rpm(display));
+ return display->parent->rpm->get_raw(display->drm);
}
void intel_display_rpm_put_raw(struct intel_display *display, struct ref_tracker *wakeref)
{
- intel_runtime_pm_put_raw(display_to_rpm(display), wakeref);
+ display->parent->rpm->put_raw(display->drm, wakeref);
}
struct ref_tracker *intel_display_rpm_get(struct intel_display *display)
{
- return intel_runtime_pm_get(display_to_rpm(display));
+ return display->parent->rpm->get(display->drm);
}
struct ref_tracker *intel_display_rpm_get_if_in_use(struct intel_display *display)
{
- return intel_runtime_pm_get_if_in_use(display_to_rpm(display));
+ return display->parent->rpm->get_if_in_use(display->drm);
}
struct ref_tracker *intel_display_rpm_get_noresume(struct intel_display *display)
{
- return intel_runtime_pm_get_noresume(display_to_rpm(display));
+ return display->parent->rpm->get_noresume(display->drm);
}
void intel_display_rpm_put(struct intel_display *display, struct ref_tracker *wakeref)
{
- intel_runtime_pm_put(display_to_rpm(display), wakeref);
+ display->parent->rpm->put(display->drm, wakeref);
}
void intel_display_rpm_put_unchecked(struct intel_display *display)
{
- intel_runtime_pm_put_unchecked(display_to_rpm(display));
+ display->parent->rpm->put_unchecked(display->drm);
}
bool intel_display_rpm_suspended(struct intel_display *display)
{
- return intel_runtime_pm_suspended(display_to_rpm(display));
+ return display->parent->rpm->suspended(display->drm);
}
void assert_display_rpm_held(struct intel_display *display)
{
- assert_rpm_wakelock_held(display_to_rpm(display));
+ display->parent->rpm->assert_held(display->drm);
}
void intel_display_rpm_assert_block(struct intel_display *display)
{
- disable_rpm_wakeref_asserts(display_to_rpm(display));
+ display->parent->rpm->assert_block(display->drm);
}
void intel_display_rpm_assert_unblock(struct intel_display *display)
{
- enable_rpm_wakeref_asserts(display_to_rpm(display));
+ display->parent->rpm->assert_unblock(display->drm);
}
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 358ab922d7a7..06bf8f7c0989 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -551,7 +551,16 @@ struct intel_connector {
u8 fec_capability;
u8 dsc_hblank_expansion_quirk:1;
+ u8 dsc_throughput_quirk:1;
u8 dsc_decompression_enabled:1;
+
+ struct {
+ struct {
+ int rgb_yuv444;
+ int yuv422_420;
+ } overall_throughput;
+ int max_line_width;
+ } dsc_branch_caps;
} dp;
struct {
@@ -637,6 +646,7 @@ struct intel_plane_state {
enum drm_color_encoding color_encoding;
enum drm_color_range color_range;
enum drm_scaling_filter scaling_filter;
+ struct drm_property_blob *ctm, *degamma_lut, *gamma_lut, *lut_3d;
} hw;
struct i915_vma *ggtt_vma;
@@ -717,7 +727,6 @@ struct intel_initial_plane_config {
struct intel_memory_region *mem;
resource_size_t phys_base;
struct i915_vma *vma;
- unsigned int tiling;
int size;
u32 base;
u8 rotation;
@@ -946,6 +955,26 @@ struct intel_csc_matrix {
u16 postoff[3];
};
+enum intel_panel_replay_dsc_support {
+ INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED,
+ INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY,
+ INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE,
+};
+
+struct scaler_filter_coeff {
+ u16 sign;
+ u16 exp;
+ u16 mantissa;
+};
+
+struct intel_casf {
+ #define SCALER_FILTER_NUM_TAPS 7
+ struct scaler_filter_coeff coeff[SCALER_FILTER_NUM_TAPS];
+ u8 strength;
+ u8 win_size;
+ bool casf_enable;
+};
+
struct intel_crtc_state {
/*
* uapi (drm) state. This is the software state shown to userspace.
@@ -982,6 +1011,7 @@ struct intel_crtc_state {
struct drm_property_blob *degamma_lut, *gamma_lut, *ctm;
struct drm_display_mode mode, pipe_mode, adjusted_mode;
enum drm_scaling_filter scaling_filter;
+ struct intel_casf casf_params;
} hw;
/* actual state of LUTs */
@@ -1124,9 +1154,12 @@ struct intel_crtc_state {
bool has_panel_replay;
bool wm_level_disabled;
bool pkg_c_latency_used;
+ /* Only used for state verification. */
+ enum intel_panel_replay_dsc_support panel_replay_dsc_support;
u32 dc3co_exitline;
u16 su_y_granularity;
u8 active_non_psr_pipes;
+ const char *no_psr_reason;
/*
* Frequency the dpll for the port should run at. Differs from the
@@ -1183,7 +1216,9 @@ struct intel_crtc_state {
struct intel_crtc_wm_state wm;
- int min_cdclk[I915_MAX_PLANES];
+ int min_cdclk;
+
+ int plane_min_cdclk[I915_MAX_PLANES];
/* for packed/planar CbCr */
u32 data_rate[I915_MAX_PLANES];
@@ -1268,6 +1303,8 @@ struct intel_crtc_state {
/* Display Stream compression state */
struct {
+ /* Only used for state computation, not read out from the HW. */
+ bool compression_enabled_on_link;
bool compression_enable;
int num_streams;
/* Compressed Bpp in U6.4 format (first 4 bits for fractional part) */
@@ -1341,6 +1378,23 @@ struct intel_crtc_state {
/* LOBF flag */
bool has_lobf;
+
+ /* W2 window or 'set context latency' lines */
+ u16 set_context_latency;
+
+ struct {
+ u8 io_wake_lines;
+ u8 fast_wake_lines;
+
+ /* LNL and beyond */
+ u8 check_entry_lines;
+ u8 aux_less_wake_lines;
+ u8 silence_period_sym_clocks;
+ u8 lfps_half_cycle_num_of_syms;
+ } alpm_state;
+
+ /* to track changes in plane color blocks */
+ bool plane_color_changed;
};
enum intel_pipe_crc_source {
@@ -1513,8 +1567,8 @@ struct intel_plane {
const struct drm_framebuffer *fb,
int color_plane);
unsigned int (*max_stride)(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation);
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation);
bool (*can_async_flip)(u64 modifier);
/* Write all non-self arming plane registers */
void (*update_noarm)(struct intel_dsb *dsb,
@@ -1679,16 +1733,22 @@ struct intel_psr {
bool source_panel_replay_support;
bool sink_panel_replay_support;
bool sink_panel_replay_su_support;
+ enum intel_panel_replay_dsc_support sink_panel_replay_dsc_support;
bool panel_replay_enabled;
u32 dc3co_exitline;
u32 dc3co_exit_delay;
struct delayed_work dc3co_work;
u8 entry_setup_frames;
+ u8 io_wake_lines;
+ u8 fast_wake_lines;
+
bool link_ok;
bool pkg_c_latency_used;
u8 active_non_psr_pipes;
+
+ const char *no_psr_reason;
};
struct intel_dp {
@@ -1844,19 +1904,12 @@ struct intel_dp {
bool colorimetry_support;
struct {
- u8 io_wake_lines;
- u8 fast_wake_lines;
enum transcoder transcoder;
struct mutex lock;
- /* LNL and beyond */
- u8 check_entry_lines;
- u8 aux_less_wake_lines;
- u8 silence_period_sym_clocks;
- u8 lfps_half_cycle_num_of_syms;
bool lobf_disable_debug;
bool sink_alpm_error;
- } alpm_parameters;
+ } alpm;
u8 alpm_dpcd;
@@ -1936,6 +1989,11 @@ struct intel_dp_mst_encoder {
struct intel_connector *connector;
};
+struct intel_colorop {
+ struct drm_colorop base;
+ enum intel_color_block id;
+};
+
static inline struct intel_encoder *
intel_attached_encoder(struct intel_connector *connector)
{
diff --git a/drivers/gpu/drm/i915/display/intel_display_utils.c b/drivers/gpu/drm/i915/display/intel_display_utils.c
new file mode 100644
index 000000000000..04d010f7c23e
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_display_utils.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: MIT
+/* Copyright © 2025 Intel Corporation */
+
+#include <linux/device.h>
+
+#include <drm/drm_device.h>
+
+#ifdef CONFIG_X86
+#include <asm/hypervisor.h>
+#endif
+
+#include "intel_display_core.h"
+#include "intel_display_utils.h"
+
+bool intel_display_run_as_guest(struct intel_display *display)
+{
+#if IS_ENABLED(CONFIG_X86)
+ return !hypervisor_is_type(X86_HYPER_NATIVE);
+#else
+ /* Not supported yet */
+ return false;
+#endif
+}
+
+bool intel_display_vtd_active(struct intel_display *display)
+{
+ if (device_iommu_mapped(display->drm->dev))
+ return true;
+
+ /* Running as a guest, we assume the host is enforcing VT'd */
+ return intel_display_run_as_guest(display);
+}
diff --git a/drivers/gpu/drm/i915/display/intel_display_utils.h b/drivers/gpu/drm/i915/display/intel_display_utils.h
new file mode 100644
index 000000000000..2a18f160320c
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_display_utils.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __INTEL_DISPLAY_UTILS__
+#define __INTEL_DISPLAY_UTILS__
+
+#include <linux/bug.h>
+#include <linux/types.h>
+
+struct intel_display;
+
+#ifndef MISSING_CASE
+#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \
+ __stringify(x), (long)(x))
+#endif
+
+#ifndef fetch_and_zero
+#define fetch_and_zero(ptr) ({ \
+ typeof(*ptr) __T = *(ptr); \
+ *(ptr) = (typeof(*ptr))0; \
+ __T; \
+})
+#endif
+
+#define KHz(x) (1000 * (x))
+#define MHz(x) KHz(1000 * (x))
+
+bool intel_display_run_as_guest(struct intel_display *display);
+bool intel_display_vtd_active(struct intel_display *display);
+
+#endif /* __INTEL_DISPLAY_UTILS__ */
diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.c b/drivers/gpu/drm/i915/display/intel_display_wa.c
index 31cd2c9cd488..e38e5e87877c 100644
--- a/drivers/gpu/drm/i915/display/intel_display_wa.c
+++ b/drivers/gpu/drm/i915/display/intel_display_wa.c
@@ -49,7 +49,8 @@ void intel_display_wa_apply(struct intel_display *display)
*/
static bool intel_display_needs_wa_16025573575(struct intel_display *display)
{
- return DISPLAY_VERx100(display) == 3000 || DISPLAY_VERx100(display) == 3002;
+ return DISPLAY_VERx100(display) == 3000 || DISPLAY_VERx100(display) == 3002 ||
+ DISPLAY_VERx100(display) == 3500;
}
/*
@@ -67,6 +68,8 @@ bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa,
return intel_display_needs_wa_16025573575(display);
case INTEL_DISPLAY_WA_14011503117:
return DISPLAY_VER(display) == 13;
+ case INTEL_DISPLAY_WA_22014263786:
+ return IS_DISPLAY_VERx100(display, 1100, 1400);
default:
drm_WARN(display->drm, 1, "Missing Wa number: %s\n", name);
break;
diff --git a/drivers/gpu/drm/i915/display/intel_display_wa.h b/drivers/gpu/drm/i915/display/intel_display_wa.h
index abc1df83f066..3644e8e2b724 100644
--- a/drivers/gpu/drm/i915/display/intel_display_wa.h
+++ b/drivers/gpu/drm/i915/display/intel_display_wa.h
@@ -25,6 +25,7 @@ enum intel_display_wa {
INTEL_DISPLAY_WA_16023588340,
INTEL_DISPLAY_WA_16025573575,
INTEL_DISPLAY_WA_14011503117,
+ INTEL_DISPLAY_WA_22014263786,
};
bool __intel_display_wa(struct intel_display *display, enum intel_display_wa wa, const char *name);
diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c
index 77a0199f9ea5..6ebbd97e6351 100644
--- a/drivers/gpu/drm/i915/display/intel_dmc.c
+++ b/drivers/gpu/drm/i915/display/intel_dmc.c
@@ -30,13 +30,13 @@
#include <drm/drm_print.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display_power_well.h"
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dmc.h"
#include "intel_dmc_regs.h"
#include "intel_flipq.h"
@@ -127,6 +127,12 @@ static bool dmc_firmware_param_disabled(struct intel_display *display)
#define DISPLAY_VER13_DMC_MAX_FW_SIZE 0x20000
#define DISPLAY_VER12_DMC_MAX_FW_SIZE ICL_DMC_MAX_FW_SIZE
+#define XE3P_LPD_DMC_PATH DMC_PATH(xe3p_lpd)
+MODULE_FIRMWARE(XE3P_LPD_DMC_PATH);
+
+#define XE3LPD_3002_DMC_PATH DMC_PATH(xe3lpd_3002)
+MODULE_FIRMWARE(XE3LPD_3002_DMC_PATH);
+
#define XE3LPD_DMC_PATH DMC_PATH(xe3lpd)
MODULE_FIRMWARE(XE3LPD_DMC_PATH);
@@ -184,8 +190,13 @@ static const char *dmc_firmware_default(struct intel_display *display, u32 *size
const char *fw_path = NULL;
u32 max_fw_size = 0;
- if (DISPLAY_VERx100(display) == 3002 ||
- DISPLAY_VERx100(display) == 3000) {
+ if (DISPLAY_VERx100(display) == 3500) {
+ fw_path = XE3P_LPD_DMC_PATH;
+ max_fw_size = XE2LPD_DMC_MAX_FW_SIZE;
+ } else if (DISPLAY_VERx100(display) == 3002) {
+ fw_path = XE3LPD_3002_DMC_PATH;
+ max_fw_size = XE2LPD_DMC_MAX_FW_SIZE;
+ } else if (DISPLAY_VERx100(display) == 3000) {
fw_path = XE3LPD_DMC_PATH;
max_fw_size = XE2LPD_DMC_MAX_FW_SIZE;
} else if (DISPLAY_VERx100(display) == 2000) {
@@ -509,10 +520,16 @@ static u32 pipedmc_interrupt_mask(struct intel_display *display)
PIPEDMC_ATS_FAULT;
}
-static u32 dmc_evt_ctl_disable(void)
+static u32 dmc_evt_ctl_disable(u32 dmc_evt_ctl)
{
- return REG_FIELD_PREP(DMC_EVT_CTL_TYPE_MASK,
- DMC_EVT_CTL_TYPE_EDGE_0_1) |
+ /*
+ * DMC_EVT_CTL_ENABLE cannot be cleared once set. Always
+ * configure it based on the original event definition to
+ * avoid mismatches in assert_dmc_loaded().
+ */
+ return (dmc_evt_ctl & DMC_EVT_CTL_ENABLE) |
+ REG_FIELD_PREP(DMC_EVT_CTL_TYPE_MASK,
+ DMC_EVT_CTL_TYPE_EDGE_0_1) |
REG_FIELD_PREP(DMC_EVT_CTL_EVENT_ID_MASK,
DMC_EVENT_FALSE);
}
@@ -546,6 +563,51 @@ static bool is_event_handler(struct intel_display *display,
REG_FIELD_GET(DMC_EVT_CTL_EVENT_ID_MASK, data) == event_id;
}
+static bool fixup_dmc_evt(struct intel_display *display,
+ enum intel_dmc_id dmc_id,
+ i915_reg_t reg_ctl, u32 *data_ctl,
+ i915_reg_t reg_htp, u32 *data_htp)
+{
+ if (!is_dmc_evt_ctl_reg(display, dmc_id, reg_ctl))
+ return false;
+
+ if (!is_dmc_evt_htp_reg(display, dmc_id, reg_htp))
+ return false;
+
+ /* make sure reg_ctl and reg_htp are for the same event */
+ if (i915_mmio_reg_offset(reg_ctl) - i915_mmio_reg_offset(DMC_EVT_CTL(display, dmc_id, 0)) !=
+ i915_mmio_reg_offset(reg_htp) - i915_mmio_reg_offset(DMC_EVT_HTP(display, dmc_id, 0)))
+ return false;
+
+ /*
+ * On ADL-S the HRR event handler is not restored after DC6.
+ * Clear it to zero from the beginning to avoid mismatches later.
+ */
+ if (display->platform.alderlake_s && dmc_id == DMC_FW_MAIN &&
+ is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_A, reg_ctl, *data_ctl)) {
+ *data_ctl = 0;
+ *data_htp = 0;
+ return true;
+ }
+
+ /*
+ * TGL/ADL-S DMC firmware incorrectly uses the undelayed vblank
+ * event for the HRR handler, when it should be using the delayed
+ * vblank event instead. Fixed firmware was never released
+ * so the Windows driver just hacks around it by overriding
+ * the event ID. Do the same.
+ */
+ if ((display->platform.tigerlake || display->platform.alderlake_s) &&
+ is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_A, reg_ctl, *data_ctl)) {
+ *data_ctl &= ~DMC_EVT_CTL_EVENT_ID_MASK;
+ *data_ctl |= REG_FIELD_PREP(DMC_EVT_CTL_EVENT_ID_MASK,
+ MAINDMC_EVENT_VBLANK_DELAYED_A);
+ return true;
+ }
+
+ return false;
+}
+
static bool disable_dmc_evt(struct intel_display *display,
enum intel_dmc_id dmc_id,
i915_reg_t reg, u32 data)
@@ -564,7 +626,7 @@ static bool disable_dmc_evt(struct intel_display *display,
/* also disable the HRR event on the main DMC on TGL/ADLS */
if ((display->platform.tigerlake || display->platform.alderlake_s) &&
- is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_A, reg, data))
+ is_event_handler(display, dmc_id, MAINDMC_EVENT_VBLANK_DELAYED_A, reg, data))
return true;
return false;
@@ -577,7 +639,7 @@ static u32 dmc_mmiodata(struct intel_display *display,
if (disable_dmc_evt(display, dmc_id,
dmc->dmc_info[dmc_id].mmioaddr[i],
dmc->dmc_info[dmc_id].mmiodata[i]))
- return dmc_evt_ctl_disable();
+ return dmc_evt_ctl_disable(dmc->dmc_info[dmc_id].mmiodata[i]);
else
return dmc->dmc_info[dmc_id].mmiodata[i];
}
@@ -636,12 +698,6 @@ static void assert_dmc_loaded(struct intel_display *display,
found = intel_de_read(display, reg);
expected = dmc_mmiodata(display, dmc, dmc_id, i);
- /* once set DMC_EVT_CTL_ENABLE can't be cleared :/ */
- if (is_dmc_evt_ctl_reg(display, dmc_id, reg)) {
- found &= ~DMC_EVT_CTL_ENABLE;
- expected &= ~DMC_EVT_CTL_ENABLE;
- }
-
drm_WARN(display->drm, found != expected,
"DMC %d mmio[%d]/0x%x incorrect (expected 0x%x, current 0x%x)\n",
dmc_id, i, i915_mmio_reg_offset(reg), expected, found);
@@ -662,11 +718,11 @@ static bool need_pipedmc_load_program(struct intel_display *display)
static bool need_pipedmc_load_mmio(struct intel_display *display, enum pipe pipe)
{
/*
- * PTL:
+ * Xe3_LPD/Xe3p_LPD:
* - pipe A/B DMC doesn't need save/restore
* - pipe C/D DMC is in PG0, needs manual save/restore
*/
- if (DISPLAY_VER(display) == 30)
+ if (IS_DISPLAY_VER(display, 30, 35))
return pipe >= PIPE_C;
/*
@@ -794,7 +850,7 @@ static void dmc_configure_event(struct intel_display *display,
if (!is_event_handler(display, dmc_id, event_id, reg, data))
continue;
- intel_de_write(display, reg, enable ? data : dmc_evt_ctl_disable());
+ intel_de_write(display, reg, enable ? data : dmc_evt_ctl_disable(data));
num_handlers++;
}
@@ -1064,9 +1120,32 @@ static u32 parse_dmc_fw_header(struct intel_dmc *dmc,
for (i = 0; i < mmio_count; i++) {
dmc_info->mmioaddr[i] = _MMIO(mmioaddr[i]);
dmc_info->mmiodata[i] = mmiodata[i];
+ }
+
+ for (i = 0; i < mmio_count - 1; i++) {
+ u32 orig_mmiodata[2] = {
+ dmc_info->mmiodata[i],
+ dmc_info->mmiodata[i+1],
+ };
+ if (!fixup_dmc_evt(display, dmc_id,
+ dmc_info->mmioaddr[i], &dmc_info->mmiodata[i],
+ dmc_info->mmioaddr[i+1], &dmc_info->mmiodata[i+1]))
+ continue;
+
+ drm_dbg_kms(display->drm,
+ " mmio[%d]: 0x%x = 0x%x->0x%x (EVT_CTL)\n",
+ i, i915_mmio_reg_offset(dmc_info->mmioaddr[i]),
+ orig_mmiodata[0], dmc_info->mmiodata[i]);
+ drm_dbg_kms(display->drm,
+ " mmio[%d]: 0x%x = 0x%x->0x%x (EVT_HTP)\n",
+ i+1, i915_mmio_reg_offset(dmc_info->mmioaddr[i+1]),
+ orig_mmiodata[1], dmc_info->mmiodata[i+1]);
+ }
+
+ for (i = 0; i < mmio_count; i++) {
drm_dbg_kms(display->drm, " mmio[%d]: 0x%x = 0x%x%s%s\n",
- i, mmioaddr[i], mmiodata[i],
+ i, i915_mmio_reg_offset(dmc_info->mmioaddr[i]), dmc_info->mmiodata[i],
is_dmc_evt_ctl_reg(display, dmc_id, dmc_info->mmioaddr[i]) ? " (EVT_CTL)" :
is_dmc_evt_htp_reg(display, dmc_id, dmc_info->mmioaddr[i]) ? " (EVT_HTP)" : "",
disable_dmc_evt(display, dmc_id, dmc_info->mmioaddr[i],
@@ -1141,7 +1220,7 @@ parse_dmc_fw_package(struct intel_dmc *dmc,
}
num_entries = package_header->num_entries;
- if (WARN_ON(package_header->num_entries > max_entries))
+ if (WARN_ON(num_entries > max_entries))
num_entries = max_entries;
fw_info = (const struct intel_fw_info *)
@@ -1640,14 +1719,14 @@ void intel_pipedmc_irq_handler(struct intel_display *display, enum pipe pipe)
drm_err_ratelimited(display->drm, "[CRTC:%d:%s] PIPEDMC GTT fault\n",
crtc->base.base.id, crtc->base.name);
if (tmp & PIPEDMC_ERROR)
- drm_err(display->drm, "[CRTC:%d:%s]] PIPEDMC error\n",
+ drm_err(display->drm, "[CRTC:%d:%s] PIPEDMC error\n",
crtc->base.base.id, crtc->base.name);
}
int_vector = intel_de_read(display, PIPEDMC_STATUS(pipe)) & PIPEDMC_INT_VECTOR_MASK;
if (tmp == 0 && int_vector != 0)
- drm_err(display->drm, "[CRTC:%d:%s]] PIPEDMC interrupt vector 0x%x\n",
- crtc->base.base.id, crtc->base.name, tmp);
+ drm_err(display->drm, "[CRTC:%d:%s] PIPEDMC interrupt vector 0x%x\n",
+ crtc->base.base.id, crtc->base.name, int_vector);
}
void intel_pipedmc_enable_event(struct intel_crtc *crtc,
diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c
index b3bb89ba34f9..73a3101514f3 100644
--- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c
+++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c
@@ -179,11 +179,11 @@ static void intel_dmc_wl_work(struct work_struct *work)
if (refcount_read(&wl->refcount))
goto out_unlock;
- __intel_de_rmw_nowl(display, DMC_WAKELOCK1_CTL, DMC_WAKELOCK_CTL_REQ, 0);
+ intel_de_rmw_fw(display, DMC_WAKELOCK1_CTL, DMC_WAKELOCK_CTL_REQ, 0);
- if (__intel_de_wait_for_register_atomic_nowl(display, DMC_WAKELOCK1_CTL,
- DMC_WAKELOCK_CTL_ACK, 0,
- DMC_WAKELOCK_CTL_TIMEOUT_US)) {
+ if (intel_de_wait_fw_us_atomic(display, DMC_WAKELOCK1_CTL,
+ DMC_WAKELOCK_CTL_ACK, 0,
+ DMC_WAKELOCK_CTL_TIMEOUT_US, NULL)) {
WARN_RATELIMIT(1, "DMC wakelock release timed out");
goto out_unlock;
}
@@ -207,17 +207,16 @@ static void __intel_dmc_wl_take(struct intel_display *display)
if (wl->taken)
return;
- __intel_de_rmw_nowl(display, DMC_WAKELOCK1_CTL, 0,
- DMC_WAKELOCK_CTL_REQ);
+ intel_de_rmw_fw(display, DMC_WAKELOCK1_CTL, 0, DMC_WAKELOCK_CTL_REQ);
/*
* We need to use the atomic variant of the waiting routine
* because the DMC wakelock is also taken in atomic context.
*/
- if (__intel_de_wait_for_register_atomic_nowl(display, DMC_WAKELOCK1_CTL,
- DMC_WAKELOCK_CTL_ACK,
- DMC_WAKELOCK_CTL_ACK,
- DMC_WAKELOCK_CTL_TIMEOUT_US)) {
+ if (intel_de_wait_fw_us_atomic(display, DMC_WAKELOCK1_CTL,
+ DMC_WAKELOCK_CTL_ACK,
+ DMC_WAKELOCK_CTL_ACK,
+ DMC_WAKELOCK_CTL_TIMEOUT_US, NULL)) {
WARN_RATELIMIT(1, "DMC wakelock ack timed out");
return;
}
@@ -360,7 +359,7 @@ void intel_dmc_wl_enable(struct intel_display *display, u32 dc_state)
* wakelock, because we're just enabling it, so call the
* non-locking version directly here.
*/
- __intel_de_rmw_nowl(display, DMC_WAKELOCK_CFG, 0, DMC_WAKELOCK_CFG_ENABLE);
+ intel_de_rmw_fw(display, DMC_WAKELOCK_CFG, 0, DMC_WAKELOCK_CFG_ENABLE);
wl->enabled = true;
@@ -402,7 +401,7 @@ void intel_dmc_wl_disable(struct intel_display *display)
goto out_unlock;
/* Disable wakelock in DMC */
- __intel_de_rmw_nowl(display, DMC_WAKELOCK_CFG, DMC_WAKELOCK_CFG_ENABLE, 0);
+ intel_de_rmw_fw(display, DMC_WAKELOCK_CFG, DMC_WAKELOCK_CFG_ENABLE, 0);
wl->enabled = false;
@@ -414,7 +413,7 @@ void intel_dmc_wl_disable(struct intel_display *display)
*
* TODO: Get the correct expectation from the hardware team.
*/
- __intel_de_rmw_nowl(display, DMC_WAKELOCK1_CTL, DMC_WAKELOCK_CTL_REQ, 0);
+ intel_de_rmw_fw(display, DMC_WAKELOCK1_CTL, DMC_WAKELOCK_CTL_REQ, 0);
wl->taken = false;
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 2eab591a8ef5..0ec82fcbcf48 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -51,7 +51,6 @@
#include <drm/drm_probe_helper.h>
#include "g4x_dp.h"
-#include "i915_utils.h"
#include "intel_alpm.h"
#include "intel_atomic.h"
#include "intel_audio.h"
@@ -64,6 +63,8 @@
#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display_driver.h"
+#include "intel_display_jiffies.h"
+#include "intel_display_utils.h"
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
@@ -93,14 +94,10 @@
#include "intel_psr.h"
#include "intel_quirks.h"
#include "intel_tc.h"
+#include "intel_vblank.h"
#include "intel_vdsc.h"
#include "intel_vrr.h"
-/* DP DSC throughput values used for slice count calculations KPixels/s */
-#define DP_DSC_PEAK_PIXEL_RATE 2720000
-#define DP_DSC_MAX_ENC_THROUGHPUT_0 340000
-#define DP_DSC_MAX_ENC_THROUGHPUT_1 400000
-
/* Max DSC line buffer depth supported by HW. */
#define INTEL_DP_DSC_MAX_LINE_BUF_DEPTH 13
@@ -1018,13 +1015,43 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector,
struct intel_display *display = to_intel_display(connector);
u8 min_slice_count, i;
int max_slice_width;
+ int tp_rgb_yuv444;
+ int tp_yuv422_420;
- if (mode_clock <= DP_DSC_PEAK_PIXEL_RATE)
- min_slice_count = DIV_ROUND_UP(mode_clock,
- DP_DSC_MAX_ENC_THROUGHPUT_0);
- else
- min_slice_count = DIV_ROUND_UP(mode_clock,
- DP_DSC_MAX_ENC_THROUGHPUT_1);
+ /*
+ * TODO: Use the throughput value specific to the actual RGB/YUV
+ * format of the output.
+ * The RGB/YUV444 throughput value should be always either equal
+ * or smaller than the YUV422/420 value, but let's not depend on
+ * this assumption.
+ */
+ if (mode_clock > max(connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444,
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420))
+ return 0;
+
+ if (mode_hdisplay > connector->dp.dsc_branch_caps.max_line_width)
+ return 0;
+
+ /*
+ * TODO: Pass the total pixel rate of all the streams transferred to
+ * an MST tiled display, calculate the total slice count for all tiles
+ * from this and the per-tile slice count from the total slice count.
+ */
+ tp_rgb_yuv444 = drm_dp_dsc_sink_max_slice_throughput(connector->dp.dsc_dpcd,
+ mode_clock, true);
+ tp_yuv422_420 = drm_dp_dsc_sink_max_slice_throughput(connector->dp.dsc_dpcd,
+ mode_clock, false);
+
+ /*
+ * TODO: Use the throughput value specific to the actual RGB/YUV
+ * format of the output.
+ * For now use the smaller of these, which is ok, potentially
+ * resulting in a higher than required minimum slice count.
+ * The RGB/YUV444 throughput value should be always either equal
+ * or smaller than the YUV422/420 value, but let's not depend on
+ * this assumption.
+ */
+ min_slice_count = DIV_ROUND_UP(mode_clock, min(tp_rgb_yuv444, tp_yuv422_420));
/*
* Due to some DSC engine BW limitations, we need to enable second
@@ -2340,24 +2367,26 @@ static int intel_edp_dsc_compute_pipe_bpp(struct intel_dp *intel_dp,
return 0;
}
-static void intel_dp_fec_compute_config(struct intel_dp *intel_dp,
- struct intel_crtc_state *crtc_state)
+/*
+ * Return whether FEC must be enabled for 8b10b SST or MST links. On 128b132b
+ * links FEC is always enabled implicitly by the HW, so this function returns
+ * false for that case.
+ */
+bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state,
+ bool dsc_enabled_on_crtc)
{
- if (crtc_state->fec_enable)
- return;
+ if (intel_dp_is_uhbr(crtc_state))
+ return false;
/*
* Though eDP v1.5 supports FEC with DSC, unlike DP, it is optional.
* Since, FEC is a bandwidth overhead, continue to not enable it for
* eDP. Until, there is a good reason to do so.
*/
- if (intel_dp_is_edp(intel_dp))
- return;
-
- if (intel_dp_is_uhbr(crtc_state))
- return;
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
+ return false;
- crtc_state->fec_enable = true;
+ return dsc_enabled_on_crtc || intel_dsc_enabled_on_link(crtc_state);
}
int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
@@ -2375,7 +2404,11 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
bool is_mst = intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST);
int ret;
- intel_dp_fec_compute_config(intel_dp, pipe_config);
+ /*
+ * FIXME: set the FEC enabled state once pipe_config->port_clock is
+ * already known, so the UHBR/non-UHBR mode can be determined.
+ */
+ pipe_config->fec_enable = intel_dp_needs_8b10b_fec(pipe_config, true);
if (!intel_dp_dsc_supports_format(connector, pipe_config->output_format))
return -EINVAL;
@@ -2450,7 +2483,8 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
return ret;
}
- pipe_config->dsc.compression_enable = true;
+ intel_dsc_enable_on_crtc(pipe_config);
+
drm_dbg_kms(display->drm, "DP DSC computed with Input Bpp = %d "
"Compressed Bpp = " FXP_Q4_FMT " Slice Count = %d\n",
pipe_config->pipe_bpp,
@@ -2460,6 +2494,40 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
return 0;
}
+static int
+dsc_throughput_quirk_max_bpp_x16(const struct intel_connector *connector,
+ const struct intel_crtc_state *crtc_state)
+{
+ const struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
+
+ if (!connector->dp.dsc_throughput_quirk)
+ return INT_MAX;
+
+ /*
+ * Synaptics Panamera branch devices have a problem decompressing a
+ * stream with a compressed link-bpp higher than 12, if the pixel
+ * clock is higher than ~50 % of the maximum overall throughput
+ * reported by the branch device. Work around this by limiting the
+ * maximum link bpp for such pixel clocks.
+ *
+ * TODO: Use the throughput value specific to the actual RGB/YUV
+ * format of the output, after determining the pixel clock limit for
+ * YUV modes. For now use the smaller of the throughput values, which
+ * may result in limiting the link-bpp value already at a lower than
+ * required mode clock in case of native YUV422/420 output formats.
+ * The RGB/YUV444 throughput value should be always either equal or
+ * smaller than the YUV422/420 value, but let's not depend on this
+ * assumption.
+ */
+ if (adjusted_mode->crtc_clock <
+ min(connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444,
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420) / 2)
+ return INT_MAX;
+
+ return fxp_q4_from_int(12);
+}
+
/*
* Calculate the output link min, max bpp values in limits based on the pipe bpp
* range, crtc_state and dsc mode. Return true on success.
@@ -2491,6 +2559,7 @@ intel_dp_compute_config_link_bpp_limits(struct intel_dp *intel_dp,
} else {
int dsc_src_min_bpp, dsc_sink_min_bpp, dsc_min_bpp;
int dsc_src_max_bpp, dsc_sink_max_bpp, dsc_max_bpp;
+ int throughput_max_bpp_x16;
dsc_src_min_bpp = intel_dp_dsc_min_src_compressed_bpp();
dsc_sink_min_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state);
@@ -2505,6 +2574,19 @@ intel_dp_compute_config_link_bpp_limits(struct intel_dp *intel_dp,
min(dsc_sink_max_bpp, dsc_src_max_bpp) : dsc_src_max_bpp;
max_link_bpp_x16 = min(max_link_bpp_x16, fxp_q4_from_int(dsc_max_bpp));
+
+ throughput_max_bpp_x16 = dsc_throughput_quirk_max_bpp_x16(connector, crtc_state);
+ throughput_max_bpp_x16 = clamp(throughput_max_bpp_x16,
+ limits->link.min_bpp_x16, max_link_bpp_x16);
+ if (throughput_max_bpp_x16 < max_link_bpp_x16) {
+ max_link_bpp_x16 = throughput_max_bpp_x16;
+
+ drm_dbg_kms(display->drm,
+ "[CRTC:%d:%s][CONNECTOR:%d:%s] Decreasing link max bpp to " FXP_Q4_FMT " due to DSC throughput quirk\n",
+ crtc->base.base.id, crtc->base.name,
+ connector->base.base.id, connector->base.name,
+ FXP_Q4_ARGS(max_link_bpp_x16));
+ }
}
limits->link.max_bpp_x16 = max_link_bpp_x16;
@@ -4169,7 +4251,36 @@ static void intel_dp_read_dsc_dpcd(struct drm_dp_aux *aux,
dsc_dpcd);
}
-void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
+static void init_dsc_overall_throughput_limits(struct intel_connector *connector, bool is_branch)
+{
+ u8 branch_caps[DP_DSC_BRANCH_CAP_SIZE];
+ int line_width;
+
+ connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444 = INT_MAX;
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420 = INT_MAX;
+ connector->dp.dsc_branch_caps.max_line_width = INT_MAX;
+
+ if (!is_branch)
+ return;
+
+ if (drm_dp_dpcd_read_data(connector->dp.dsc_decompression_aux,
+ DP_DSC_BRANCH_OVERALL_THROUGHPUT_0, branch_caps,
+ sizeof(branch_caps)) != 0)
+ return;
+
+ connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444 =
+ drm_dp_dsc_branch_max_overall_throughput(branch_caps, true) ? : INT_MAX;
+
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420 =
+ drm_dp_dsc_branch_max_overall_throughput(branch_caps, false) ? : INT_MAX;
+
+ line_width = drm_dp_dsc_branch_max_line_width(branch_caps);
+ connector->dp.dsc_branch_caps.max_line_width = line_width > 0 ? line_width : INT_MAX;
+}
+
+void intel_dp_get_dsc_sink_cap(u8 dpcd_rev,
+ const struct drm_dp_desc *desc, bool is_branch,
+ struct intel_connector *connector)
{
struct intel_display *display = to_intel_display(connector);
@@ -4182,6 +4293,9 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
/* Clear fec_capable to avoid using stale values */
connector->dp.fec_capability = 0;
+ memset(&connector->dp.dsc_branch_caps, 0, sizeof(connector->dp.dsc_branch_caps));
+ connector->dp.dsc_throughput_quirk = false;
+
if (dpcd_rev < DP_DPCD_REV_14)
return;
@@ -4196,6 +4310,19 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
drm_dbg_kms(display->drm, "FEC CAPABILITY: %x\n",
connector->dp.fec_capability);
+
+ if (!(connector->dp.dsc_dpcd[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED))
+ return;
+
+ init_dsc_overall_throughput_limits(connector, is_branch);
+
+ /*
+ * TODO: Move the HW rev check as well to the DRM core quirk table if
+ * that's required after clarifying the list of affected devices.
+ */
+ if (drm_dp_has_quirk(desc, DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) &&
+ desc->ident.hw_rev == 0x10)
+ connector->dp.dsc_throughput_quirk = true;
}
static void intel_edp_get_dsc_sink_cap(u8 edp_dpcd_rev, struct intel_connector *connector)
@@ -4204,6 +4331,9 @@ static void intel_edp_get_dsc_sink_cap(u8 edp_dpcd_rev, struct intel_connector *
return;
intel_dp_read_dsc_dpcd(connector->dp.dsc_decompression_aux, connector->dp.dsc_dpcd);
+
+ if (connector->dp.dsc_dpcd[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED)
+ init_dsc_overall_throughput_limits(connector, false);
}
static void
@@ -4220,6 +4350,7 @@ intel_dp_detect_dsc_caps(struct intel_dp *intel_dp, struct intel_connector *conn
connector);
else
intel_dp_get_dsc_sink_cap(intel_dp->dpcd[DP_DPCD_REV],
+ &intel_dp->desc, drm_dp_is_branch(intel_dp->dpcd),
connector);
}
@@ -5553,7 +5684,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
if (intel_alpm_get_error(intel_dp)) {
intel_alpm_disable(intel_dp);
- intel_dp->alpm_parameters.sink_alpm_error = true;
+ intel_dp->alpm.sink_alpm_error = true;
}
if (intel_dp_test_short_pulse(intel_dp))
@@ -5921,6 +6052,8 @@ intel_dp_detect(struct drm_connector *_connector,
memset(connector->dp.dsc_dpcd, 0, sizeof(connector->dp.dsc_dpcd));
intel_dp->psr.sink_panel_replay_support = false;
intel_dp->psr.sink_panel_replay_su_support = false;
+ intel_dp->psr.sink_panel_replay_dsc_support =
+ INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED;
intel_dp_mst_disconnect(intel_dp);
@@ -6857,3 +6990,81 @@ void intel_dp_mst_resume(struct intel_display *display)
}
}
}
+
+static
+int intel_dp_sdp_compute_config_late(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int guardband = intel_crtc_vblank_length(crtc_state);
+ int min_sdp_guardband = intel_dp_sdp_min_guardband(crtc_state, false);
+
+ if (guardband < min_sdp_guardband) {
+ drm_dbg_kms(display->drm, "guardband %d < min sdp guardband %d\n",
+ guardband, min_sdp_guardband);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int intel_dp_compute_config_late(struct intel_encoder *encoder,
+ struct intel_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ int ret;
+
+ intel_psr_compute_config_late(intel_dp, crtc_state);
+
+ ret = intel_dp_sdp_compute_config_late(crtc_state);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static
+int intel_dp_get_lines_for_sdp(const struct intel_crtc_state *crtc_state, u32 type)
+{
+ switch (type) {
+ case DP_SDP_VSC_EXT_VESA:
+ case DP_SDP_VSC_EXT_CEA:
+ return 10;
+ case HDMI_PACKET_TYPE_GAMUT_METADATA:
+ return 8;
+ case DP_SDP_PPS:
+ return 7;
+ case DP_SDP_ADAPTIVE_SYNC:
+ return crtc_state->vrr.vsync_start + 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int intel_dp_sdp_min_guardband(const struct intel_crtc_state *crtc_state,
+ bool assume_all_enabled)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int sdp_guardband = 0;
+
+ if (assume_all_enabled ||
+ crtc_state->infoframes.enable &
+ intel_hdmi_infoframe_enable(HDMI_PACKET_TYPE_GAMUT_METADATA))
+ sdp_guardband = max(sdp_guardband,
+ intel_dp_get_lines_for_sdp(crtc_state,
+ HDMI_PACKET_TYPE_GAMUT_METADATA));
+
+ if (assume_all_enabled ||
+ crtc_state->dsc.compression_enable)
+ sdp_guardband = max(sdp_guardband,
+ intel_dp_get_lines_for_sdp(crtc_state, DP_SDP_PPS));
+
+ if ((assume_all_enabled && HAS_AS_SDP(display)) ||
+ crtc_state->infoframes.enable & intel_hdmi_infoframe_enable(DP_SDP_ADAPTIVE_SYNC))
+ sdp_guardband = max(sdp_guardband,
+ intel_dp_get_lines_for_sdp(crtc_state, DP_SDP_ADAPTIVE_SYNC));
+
+ return sdp_guardband;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h
index f90cfd1dbbd0..200a8b267f64 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.h
+++ b/drivers/gpu/drm/i915/display/intel_dp.h
@@ -12,6 +12,7 @@ enum intel_output_format;
enum pipe;
enum port;
struct drm_connector_state;
+struct drm_dp_desc;
struct drm_dp_vsc_sdp;
struct drm_encoder;
struct drm_modeset_acquire_ctx;
@@ -72,6 +73,8 @@ void intel_dp_encoder_flush_work(struct drm_encoder *encoder);
int intel_dp_compute_config(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state);
+bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state,
+ bool dsc_enabled_on_crtc);
int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *pipe_config,
struct drm_connector_state *conn_state,
@@ -199,7 +202,9 @@ bool intel_dp_compute_config_limits(struct intel_dp *intel_dp,
bool dsc,
struct link_config_limits *limits);
-void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector);
+void intel_dp_get_dsc_sink_cap(u8 dpcd_rev,
+ const struct drm_dp_desc *desc, bool is_branch,
+ struct intel_connector *connector);
bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder);
bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,
@@ -215,5 +220,10 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state,
int intel_dp_dsc_bpp_step_x16(const struct intel_connector *connector);
void intel_dp_dpcd_set_probe(struct intel_dp *intel_dp, bool force_on_external);
bool intel_dp_in_hdr_mode(const struct drm_connector_state *conn_state);
+int intel_dp_compute_config_late(struct intel_encoder *encoder,
+ struct intel_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state);
+int intel_dp_sdp_min_guardband(const struct intel_crtc_state *crtc_state,
+ bool assume_all_enabled);
#endif /* __INTEL_DP_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_dp_aux.c b/drivers/gpu/drm/i915/display/intel_dp_aux.c
index 829a7c0fbe4f..809799f63e32 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_aux.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_aux.c
@@ -5,9 +5,9 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_de.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dp_aux.h"
#include "intel_dp_aux_regs.h"
@@ -62,9 +62,9 @@ intel_dp_aux_wait_done(struct intel_dp *intel_dp)
u32 status;
int ret;
- ret = intel_de_wait_custom(display, ch_ctl, DP_AUX_CH_CTL_SEND_BUSY,
- 0,
- 2, timeout_ms, &status);
+ ret = intel_de_wait_ms(display, ch_ctl,
+ DP_AUX_CH_CTL_SEND_BUSY, 0,
+ timeout_ms, &status);
if (ret == -ETIMEDOUT)
drm_err(display->drm,
diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
index bd757db85927..14ed0ea22dd3 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c
@@ -782,9 +782,9 @@ intel_dp_mst_hdcp_stream_encryption(struct intel_connector *connector,
return -EINVAL;
/* Wait for encryption confirmation */
- if (intel_de_wait(display, HDCP_STATUS(display, cpu_transcoder, port),
- stream_enc_status, enable ? stream_enc_status : 0,
- HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
+ if (intel_de_wait_ms(display, HDCP_STATUS(display, cpu_transcoder, port),
+ stream_enc_status, enable ? stream_enc_status : 0,
+ HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS, NULL)) {
drm_err(display->drm, "Timed out waiting for transcoder: %s stream encryption %s\n",
transcoder_name(cpu_transcoder), str_enabled_disabled(enable));
return -ETIMEDOUT;
@@ -821,10 +821,10 @@ intel_dp_mst_hdcp2_stream_encryption(struct intel_connector *connector,
return ret;
/* Wait for encryption confirmation */
- if (intel_de_wait(display, HDCP2_STREAM_STATUS(display, cpu_transcoder, pipe),
- STREAM_ENCRYPTION_STATUS,
- enable ? STREAM_ENCRYPTION_STATUS : 0,
- HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
+ if (intel_de_wait_ms(display, HDCP2_STREAM_STATUS(display, cpu_transcoder, pipe),
+ STREAM_ENCRYPTION_STATUS,
+ enable ? STREAM_ENCRYPTION_STATUS : 0,
+ HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS, NULL)) {
drm_err(display->drm, "Timed out waiting for transcoder: %s stream encryption %s\n",
transcoder_name(cpu_transcoder), str_enabled_disabled(enable));
return -ETIMEDOUT;
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
index 27f3716bdc1f..aad5fe14962f 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c
@@ -27,9 +27,10 @@
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_display_core.h"
+#include "intel_display_jiffies.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dp_link_training.h"
#include "intel_encoder.h"
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index 352f7ef29c28..4c0b943fe86f 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -33,7 +33,6 @@
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_audio.h"
#include "intel_connector.h"
@@ -43,6 +42,7 @@
#include "intel_display_driver.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dp_hdcp.h"
#include "intel_dp_link_training.h"
@@ -293,12 +293,22 @@ int intel_dp_mtp_tu_compute_config(struct intel_dp *intel_dp,
mst_stream_update_slots(crtc_state, mst_state);
}
- if (dsc) {
- if (!intel_dp_supports_fec(intel_dp, connector, crtc_state))
- return -EINVAL;
-
- crtc_state->fec_enable = !intel_dp_is_uhbr(crtc_state);
- }
+ /*
+ * NOTE: The following must reset crtc_state->fec_enable for UHBR/DSC
+ * after it was set by intel_dp_dsc_compute_config() ->
+ * intel_dp_needs_8b10b_fec().
+ */
+ crtc_state->fec_enable = intel_dp_needs_8b10b_fec(crtc_state, dsc);
+ /*
+ * If FEC gets enabled only because of another compressed stream, FEC
+ * may not be supported for this uncompressed stream on the whole link
+ * path until the sink DPRX. In this case a downstream branch device
+ * will disable FEC for the uncompressed stream as expected and so the
+ * FEC support doesn't need to be checked for this uncompressed stream.
+ */
+ if (crtc_state->fec_enable && dsc &&
+ !intel_dp_supports_fec(intel_dp, connector, crtc_state))
+ return -EINVAL;
max_dpt_bpp_x16 = fxp_q4_from_int(intel_dp_mst_max_dpt_bpp(crtc_state, dsc));
if (max_dpt_bpp_x16 && max_bpp_x16 > max_dpt_bpp_x16) {
@@ -811,14 +821,14 @@ static u8 get_pipes_downstream_of_mst_port(struct intel_atomic_state *state,
return mask;
}
-static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state,
+static int intel_dp_mst_check_dsc_change(struct intel_atomic_state *state,
struct drm_dp_mst_topology_mgr *mst_mgr,
struct intel_link_bw_limits *limits)
{
struct intel_display *display = to_intel_display(state);
struct intel_crtc *crtc;
u8 mst_pipe_mask;
- u8 fec_pipe_mask = 0;
+ u8 dsc_pipe_mask = 0;
int ret;
mst_pipe_mask = get_pipes_downstream_of_mst_port(state, mst_mgr, NULL);
@@ -831,16 +841,16 @@ static int intel_dp_mst_check_fec_change(struct intel_atomic_state *state,
if (drm_WARN_ON(display->drm, !crtc_state))
return -EINVAL;
- if (crtc_state->fec_enable)
- fec_pipe_mask |= BIT(crtc->pipe);
+ if (intel_dsc_enabled_on_link(crtc_state))
+ dsc_pipe_mask |= BIT(crtc->pipe);
}
- if (!fec_pipe_mask || mst_pipe_mask == fec_pipe_mask)
+ if (!dsc_pipe_mask || mst_pipe_mask == dsc_pipe_mask)
return 0;
- limits->force_fec_pipes |= mst_pipe_mask;
+ limits->link_dsc_pipes |= mst_pipe_mask;
- ret = intel_modeset_pipes_in_mask_early(state, "MST FEC",
+ ret = intel_modeset_pipes_in_mask_early(state, "MST DSC",
mst_pipe_mask);
return ret ? : -EAGAIN;
@@ -894,7 +904,7 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state,
int i;
for_each_new_mst_mgr_in_state(&state->base, mgr, mst_state, i) {
- ret = intel_dp_mst_check_fec_change(state, mgr, limits);
+ ret = intel_dp_mst_check_dsc_change(state, mgr, limits);
if (ret)
return ret;
@@ -1658,6 +1668,7 @@ intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
struct intel_connector *connector)
{
u8 dpcd_caps[DP_RECEIVER_CAP_SIZE];
+ struct drm_dp_desc desc;
if (!connector->dp.dsc_decompression_aux)
return;
@@ -1665,7 +1676,13 @@ intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd_caps) < 0)
return;
- intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV], connector);
+ if (drm_dp_read_desc(connector->dp.dsc_decompression_aux, &desc,
+ drm_dp_is_branch(dpcd_caps)) < 0)
+ return;
+
+ intel_dp_get_dsc_sink_cap(dpcd_caps[DP_DPCD_REV],
+ &desc, drm_dp_is_branch(dpcd_caps),
+ connector);
}
static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector)
diff --git a/drivers/gpu/drm/i915/display/intel_dpio_phy.c b/drivers/gpu/drm/i915/display/intel_dpio_phy.c
index 3f77ad92c156..8027bab2951b 100644
--- a/drivers/gpu/drm/i915/display/intel_dpio_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_dpio_phy.c
@@ -24,13 +24,13 @@
#include <drm/drm_print.h>
#include "bxt_dpio_phy_regs.h"
-#include "i915_utils.h"
#include "intel_ddi.h"
#include "intel_ddi_buf_trans.h"
#include "intel_de.h"
#include "intel_display_power_well.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dpio_phy.h"
#include "vlv_dpio_phy_regs.h"
@@ -390,7 +390,7 @@ static u32 bxt_get_grc(struct intel_display *display, enum dpio_phy phy)
static void bxt_phy_wait_grc_done(struct intel_display *display,
enum dpio_phy phy)
{
- if (intel_de_wait_for_set(display, BXT_PORT_REF_DW3(phy), GRC_DONE, 10))
+ if (intel_de_wait_for_set_ms(display, BXT_PORT_REF_DW3(phy), GRC_DONE, 10))
drm_err(display->drm, "timeout waiting for PHY%d GRC\n", phy);
}
@@ -427,7 +427,7 @@ static void _bxt_dpio_phy_init(struct intel_display *display, enum dpio_phy phy)
* The flag should get set in 100us according to the HW team, but
* use 1ms due to occasional timeouts observed with that.
*/
- if (intel_de_wait_fw(display, BXT_PORT_CL1CM_DW0(phy),
+ if (intel_de_wait_ms(display, BXT_PORT_CL1CM_DW0(phy),
PHY_RESERVED | PHY_POWER_GOOD, PHY_POWER_GOOD, 1, NULL))
drm_err(display->drm, "timeout during PHY%d power on\n",
phy);
@@ -1173,6 +1173,7 @@ void vlv_wait_port_ready(struct intel_encoder *encoder,
struct intel_display *display = to_intel_display(encoder);
u32 port_mask;
i915_reg_t dpll_reg;
+ u32 val;
switch (encoder->port) {
default:
@@ -1193,10 +1194,9 @@ void vlv_wait_port_ready(struct intel_encoder *encoder,
break;
}
- if (intel_de_wait(display, dpll_reg, port_mask, expected_mask, 1000))
+ if (intel_de_wait_ms(display, dpll_reg, port_mask, expected_mask, 1000, &val))
drm_WARN(display->drm, 1,
"timed out waiting for [ENCODER:%d:%s] port ready: got 0x%x, expected 0x%x\n",
encoder->base.base.id, encoder->base.name,
- intel_de_read(display, dpll_reg) & port_mask,
- expected_mask);
+ val & port_mask, expected_mask);
}
diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c
index f969c5399a51..4f1db8493a2e 100644
--- a/drivers/gpu/drm/i915/display/intel_dpll.c
+++ b/drivers/gpu/drm/i915/display/intel_dpll.c
@@ -17,6 +17,7 @@
#include "intel_display_types.h"
#include "intel_dpio_phy.h"
#include "intel_dpll.h"
+#include "intel_lt_phy.h"
#include "intel_lvds.h"
#include "intel_lvds_regs.h"
#include "intel_panel.h"
@@ -1232,6 +1233,28 @@ static int mtl_crtc_compute_clock(struct intel_atomic_state *state,
return 0;
}
+static int xe3plpd_crtc_compute_clock(struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_crtc_state *crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ struct intel_encoder *encoder =
+ intel_get_crtc_new_encoder(state, crtc_state);
+ int ret;
+
+ ret = intel_lt_phy_pll_calc_state(crtc_state, encoder);
+ if (ret)
+ return ret;
+
+ /* TODO: Do the readback via intel_compute_shared_dplls() */
+ crtc_state->port_clock =
+ intel_lt_phy_calc_port_clock(encoder, crtc_state);
+
+ crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state);
+
+ return 0;
+}
+
static int ilk_fb_cb_factor(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
@@ -1691,6 +1714,10 @@ static int i8xx_crtc_compute_clock(struct intel_atomic_state *state,
return 0;
}
+static const struct intel_dpll_global_funcs xe3plpd_dpll_funcs = {
+ .crtc_compute_clock = xe3plpd_crtc_compute_clock,
+};
+
static const struct intel_dpll_global_funcs mtl_dpll_funcs = {
.crtc_compute_clock = mtl_crtc_compute_clock,
};
@@ -1789,7 +1816,9 @@ int intel_dpll_crtc_get_dpll(struct intel_atomic_state *state,
void
intel_dpll_init_clock_hook(struct intel_display *display)
{
- if (DISPLAY_VER(display) >= 14)
+ if (HAS_LT_PHY(display))
+ display->funcs.dpll = &xe3plpd_dpll_funcs;
+ else if (DISPLAY_VER(display) >= 14)
display->funcs.dpll = &mtl_dpll_funcs;
else if (display->platform.dg2)
display->funcs.dpll = &dg2_dpll_funcs;
@@ -1990,7 +2019,7 @@ static void _vlv_enable_pll(const struct intel_crtc_state *crtc_state)
intel_de_posting_read(display, DPLL(display, pipe));
udelay(150);
- if (intel_de_wait_for_set(display, DPLL(display, pipe), DPLL_LOCK_VLV, 1))
+ if (intel_de_wait_for_set_ms(display, DPLL(display, pipe), DPLL_LOCK_VLV, 1))
drm_err(display->drm, "DPLL %d failed to lock\n", pipe);
}
@@ -2136,7 +2165,7 @@ static void _chv_enable_pll(const struct intel_crtc_state *crtc_state)
intel_de_write(display, DPLL(display, pipe), hw_state->dpll);
/* Check PLL is locked */
- if (intel_de_wait_for_set(display, DPLL(display, pipe), DPLL_LOCK_VLV, 1))
+ if (intel_de_wait_for_set_ms(display, DPLL(display, pipe), DPLL_LOCK_VLV, 1))
drm_err(display->drm, "PLL %d failed to lock\n", pipe);
}
diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
index 8ea96cc524a1..9c7cf03cf022 100644
--- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
@@ -27,11 +27,11 @@
#include <drm/drm_print.h>
#include "bxt_dpio_phy_regs.h"
-#include "i915_utils.h"
#include "intel_cx0_phy.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dkl_phy.h"
#include "intel_dkl_phy_regs.h"
#include "intel_dpio_phy.h"
@@ -1395,7 +1395,7 @@ static void skl_ddi_pll_enable(struct intel_display *display,
/* the enable bit is always bit 31 */
intel_de_rmw(display, regs[id].ctl, 0, LCPLL_PLL_ENABLE);
- if (intel_de_wait_for_set(display, DPLL_STATUS, DPLL_LOCK(id), 5))
+ if (intel_de_wait_for_set_ms(display, DPLL_STATUS, DPLL_LOCK(id), 5))
drm_err(display->drm, "DPLL %d not locked\n", id);
}
@@ -2057,9 +2057,9 @@ static void bxt_ddi_pll_enable(struct intel_display *display,
intel_de_rmw(display, BXT_PORT_PLL_ENABLE(port),
0, PORT_PLL_POWER_ENABLE);
- ret = intel_de_wait_custom(display, BXT_PORT_PLL_ENABLE(port),
- PORT_PLL_POWER_STATE, PORT_PLL_POWER_STATE,
- 200, 0, NULL);
+ ret = intel_de_wait_for_set_us(display,
+ BXT_PORT_PLL_ENABLE(port),
+ PORT_PLL_POWER_STATE, 200);
if (ret)
drm_err(display->drm,
"Power state not set for PLL:%d\n", port);
@@ -2122,9 +2122,8 @@ static void bxt_ddi_pll_enable(struct intel_display *display,
intel_de_rmw(display, BXT_PORT_PLL_ENABLE(port), 0, PORT_PLL_ENABLE);
intel_de_posting_read(display, BXT_PORT_PLL_ENABLE(port));
- ret = intel_de_wait_custom(display, BXT_PORT_PLL_ENABLE(port),
- PORT_PLL_LOCK, PORT_PLL_LOCK,
- 200, 0, NULL);
+ ret = intel_de_wait_for_set_us(display, BXT_PORT_PLL_ENABLE(port),
+ PORT_PLL_LOCK, 200);
if (ret)
drm_err(display->drm, "PLL %d not locked\n", port);
@@ -2158,9 +2157,9 @@ static void bxt_ddi_pll_disable(struct intel_display *display,
intel_de_rmw(display, BXT_PORT_PLL_ENABLE(port),
PORT_PLL_POWER_ENABLE, 0);
- ret = intel_de_wait_custom(display, BXT_PORT_PLL_ENABLE(port),
- PORT_PLL_POWER_STATE, 0,
- 200, 0, NULL);
+ ret = intel_de_wait_for_clear_us(display,
+ BXT_PORT_PLL_ENABLE(port),
+ PORT_PLL_POWER_STATE, 200);
if (ret)
drm_err(display->drm,
"Power state not reset for PLL:%d\n", port);
@@ -3921,7 +3920,7 @@ static void icl_pll_power_enable(struct intel_display *display,
* The spec says we need to "wait" but it also says it should be
* immediate.
*/
- if (intel_de_wait_for_set(display, enable_reg, PLL_POWER_STATE, 1))
+ if (intel_de_wait_for_set_ms(display, enable_reg, PLL_POWER_STATE, 1))
drm_err(display->drm, "PLL %d Power not enabled\n",
pll->info->id);
}
@@ -3933,7 +3932,7 @@ static void icl_pll_enable(struct intel_display *display,
intel_de_rmw(display, enable_reg, 0, PLL_ENABLE);
/* Timeout is actually 600us. */
- if (intel_de_wait_for_set(display, enable_reg, PLL_LOCK, 1))
+ if (intel_de_wait_for_set_ms(display, enable_reg, PLL_LOCK, 1))
drm_err(display->drm, "PLL %d not locked\n", pll->info->id);
}
@@ -4046,7 +4045,7 @@ static void icl_pll_disable(struct intel_display *display,
intel_de_rmw(display, enable_reg, PLL_ENABLE, 0);
/* Timeout is actually 1us. */
- if (intel_de_wait_for_clear(display, enable_reg, PLL_LOCK, 1))
+ if (intel_de_wait_for_clear_ms(display, enable_reg, PLL_LOCK, 1))
drm_err(display->drm, "PLL %d locked\n", pll->info->id);
/* DVFS post sequence would be here. See the comment above. */
@@ -4057,7 +4056,7 @@ static void icl_pll_disable(struct intel_display *display,
* The spec says we need to "wait" but it also says it should be
* immediate.
*/
- if (intel_de_wait_for_clear(display, enable_reg, PLL_POWER_STATE, 1))
+ if (intel_de_wait_for_clear_ms(display, enable_reg, PLL_POWER_STATE, 1))
drm_err(display->drm, "PLL %d Power not disabled\n",
pll->info->id);
}
diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h
index f131bdd1c975..6183da90b28d 100644
--- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h
+++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h
@@ -267,6 +267,16 @@ struct intel_cx0pll_state {
bool tbt_mode;
};
+struct intel_lt_phy_pll_state {
+ u32 clock; /* in kHz */
+ u8 addr_msb[13];
+ u8 addr_lsb[13];
+ u8 data[13][4];
+ u8 config[3];
+ bool ssc_enabled;
+ bool tbt_mode;
+};
+
struct intel_dpll_hw_state {
union {
struct i9xx_dpll_hw_state i9xx;
@@ -276,6 +286,7 @@ struct intel_dpll_hw_state {
struct icl_dpll_hw_state icl;
struct intel_mpllb_state mpllb;
struct intel_cx0pll_state cx0pll;
+ struct intel_lt_phy_pll_state ltpll;
};
};
diff --git a/drivers/gpu/drm/i915/display/intel_dpt.c b/drivers/gpu/drm/i915/display/intel_dpt.c
index c0a817018d08..58d953472218 100644
--- a/drivers/gpu/drm/i915/display/intel_dpt.c
+++ b/drivers/gpu/drm/i915/display/intel_dpt.c
@@ -3,6 +3,8 @@
* Copyright © 2021 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_domain.h"
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_lmem.h"
diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c
index dee44d45b668..4ad4efbf9253 100644
--- a/drivers/gpu/drm/i915/display/intel_dsb.c
+++ b/drivers/gpu/drm/i915/display/intel_dsb.c
@@ -115,24 +115,6 @@ static bool pre_commit_is_vrr_active(struct intel_atomic_state *state,
return old_crtc_state->vrr.enable && !intel_crtc_vrr_disabling(state, crtc);
}
-static int dsb_vblank_delay(struct intel_atomic_state *state,
- struct intel_crtc *crtc)
-{
- const struct intel_crtc_state *crtc_state =
- intel_pre_commit_crtc_state(state, crtc);
-
- if (pre_commit_is_vrr_active(state, crtc))
- /*
- * When the push is sent during vblank it will trigger
- * on the next scanline, hence we have up to one extra
- * scanline until the delayed vblank occurs after
- * TRANS_PUSH has been written.
- */
- return intel_vrr_vblank_delay(crtc_state) + 1;
- else
- return intel_mode_vblank_delay(&crtc_state->hw.adjusted_mode);
-}
-
static int dsb_vtotal(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
@@ -723,7 +705,7 @@ void intel_dsb_vblank_evade(struct intel_atomic_state *state,
intel_dsb_emit_wait_dsl(dsb, DSB_OPCODE_WAIT_DSL_OUT, 0, 0);
if (pre_commit_is_vrr_active(state, crtc)) {
- int vblank_delay = intel_vrr_vblank_delay(crtc_state);
+ int vblank_delay = crtc_state->set_context_latency;
end = intel_vrr_vmin_vblank_start(crtc_state);
start = end - vblank_delay - latency;
@@ -815,16 +797,43 @@ void intel_dsb_chain(struct intel_atomic_state *state,
wait_for_vblank ? DSB_WAIT_FOR_VBLANK : 0);
}
-void intel_dsb_wait_vblank_delay(struct intel_atomic_state *state,
- struct intel_dsb *dsb)
+void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
+ struct intel_dsb *dsb)
{
struct intel_crtc *crtc = dsb->crtc;
const struct intel_crtc_state *crtc_state =
intel_pre_commit_crtc_state(state, crtc);
- int usecs = intel_scanlines_to_usecs(&crtc_state->hw.adjusted_mode,
- dsb_vblank_delay(state, crtc));
+ const struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
+ int wait_scanlines;
+
+ if (pre_commit_is_vrr_active(state, crtc)) {
+ /*
+ * If the push happened before the vmin decision boundary
+ * we don't know how far we are from the undelayed vblank.
+ * Wait until we're past the vmin safe window, at which
+ * point we're SCL lines away from the delayed vblank.
+ *
+ * If the push happened after the vmin decision boundary
+ * the hardware itself guarantees that we're SCL lines
+ * away from the delayed vblank, and we won't be inside
+ * the vmin safe window so this extra wait does nothing.
+ */
+ intel_dsb_wait_scanline_out(state, dsb,
+ intel_vrr_safe_window_start(crtc_state),
+ intel_vrr_vmin_safe_window_end(crtc_state));
+ /*
+ * When the push is sent during vblank it will trigger
+ * on the next scanline, hence we have up to one extra
+ * scanline until the delayed vblank occurs after
+ * TRANS_PUSH has been written.
+ */
+ wait_scanlines = crtc_state->set_context_latency + 1;
+ } else {
+ wait_scanlines = intel_mode_vblank_delay(adjusted_mode);
+ }
- intel_dsb_wait_usec(dsb, usecs);
+ intel_dsb_wait_usec(dsb, intel_scanlines_to_usecs(adjusted_mode, wait_scanlines));
}
/**
diff --git a/drivers/gpu/drm/i915/display/intel_dsb.h b/drivers/gpu/drm/i915/display/intel_dsb.h
index c8f4499916eb..2f31f2c1d0c5 100644
--- a/drivers/gpu/drm/i915/display/intel_dsb.h
+++ b/drivers/gpu/drm/i915/display/intel_dsb.h
@@ -48,8 +48,8 @@ void intel_dsb_nonpost_end(struct intel_dsb *dsb);
void intel_dsb_interrupt(struct intel_dsb *dsb);
void intel_dsb_wait_usec(struct intel_dsb *dsb, int count);
void intel_dsb_wait_vblanks(struct intel_dsb *dsb, int count);
-void intel_dsb_wait_vblank_delay(struct intel_atomic_state *state,
- struct intel_dsb *dsb);
+void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state,
+ struct intel_dsb *dsb);
void intel_dsb_wait_scanline_in(struct intel_atomic_state *state,
struct intel_dsb *dsb,
int lower, int upper);
diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
index 23402408e172..4b815ce6b1fe 100644
--- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
+++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
@@ -38,10 +38,10 @@
#include <drm/drm_print.h>
#include <video/mipi_display.h>
-#include "i915_utils.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dsi.h"
#include "intel_dsi_vbt.h"
#include "intel_gmbus_regs.h"
@@ -106,8 +106,8 @@ static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi,
u8 type, flags, seq_port;
u16 len;
enum port port;
-
- drm_dbg_kms(display->drm, "\n");
+ ssize_t ret;
+ bool hs_mode;
flags = *data++;
type = *data++;
@@ -129,45 +129,56 @@ static const u8 *mipi_exec_send_packet(struct intel_dsi *intel_dsi,
goto out;
}
- if ((flags >> MIPI_TRANSFER_MODE_SHIFT) & 1)
+ hs_mode = (flags >> MIPI_TRANSFER_MODE_SHIFT) & 1;
+ if (hs_mode)
dsi_device->mode_flags &= ~MIPI_DSI_MODE_LPM;
else
dsi_device->mode_flags |= MIPI_DSI_MODE_LPM;
dsi_device->channel = (flags >> MIPI_VIRTUAL_CHANNEL_SHIFT) & 3;
+ drm_dbg_kms(display->drm, "DSI packet: Port %c (seq %u), Flags 0x%02x, VC %u, %s, Type 0x%02x, Length %u, Data %*ph\n",
+ port_name(port), seq_port, flags, dsi_device->channel,
+ hs_mode ? "HS" : "LP", type, len, (int)len, data);
+
switch (type) {
case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
- mipi_dsi_generic_write(dsi_device, NULL, 0);
+ ret = mipi_dsi_generic_write(dsi_device, NULL, 0);
break;
case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
- mipi_dsi_generic_write(dsi_device, data, 1);
+ ret = mipi_dsi_generic_write(dsi_device, data, 1);
break;
case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
- mipi_dsi_generic_write(dsi_device, data, 2);
+ ret = mipi_dsi_generic_write(dsi_device, data, 2);
break;
case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
- drm_dbg_kms(display->drm, "Generic Read not yet implemented or used\n");
+ ret = -EOPNOTSUPP;
break;
case MIPI_DSI_GENERIC_LONG_WRITE:
- mipi_dsi_generic_write(dsi_device, data, len);
+ ret = mipi_dsi_generic_write(dsi_device, data, len);
break;
case MIPI_DSI_DCS_SHORT_WRITE:
- mipi_dsi_dcs_write_buffer(dsi_device, data, 1);
+ ret = mipi_dsi_dcs_write_buffer(dsi_device, data, 1);
break;
case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
- mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
+ ret = mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
break;
case MIPI_DSI_DCS_READ:
- drm_dbg_kms(display->drm, "DCS Read not yet implemented or used\n");
+ ret = -EOPNOTSUPP;
break;
case MIPI_DSI_DCS_LONG_WRITE:
- mipi_dsi_dcs_write_buffer(dsi_device, data, len);
+ ret = mipi_dsi_dcs_write_buffer(dsi_device, data, len);
+ break;
+ default:
+ ret = -EINVAL;
break;
}
+ if (ret < 0)
+ drm_err(display->drm, "DSI send packet failed with %pe\n", ERR_PTR(ret));
+
if (DISPLAY_VER(display) < 11)
vlv_dsi_wait_for_fifo_empty(intel_dsi, port);
diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c
index 08b48e36aca6..c2663d6e2c92 100644
--- a/drivers/gpu/drm/i915/display/intel_dvo.c
+++ b/drivers/gpu/drm/i915/display/intel_dvo.c
@@ -34,12 +34,12 @@
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include "i915_utils.h"
#include "intel_connector.h"
#include "intel_de.h"
#include "intel_display_driver.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dvo.h"
#include "intel_dvo_dev.h"
#include "intel_dvo_regs.h"
diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c
index b817ff44c043..b34b4961fe1c 100644
--- a/drivers/gpu/drm/i915/display/intel_fb.c
+++ b/drivers/gpu/drm/i915/display/intel_fb.c
@@ -9,13 +9,13 @@
#include <drm/drm_blend.h>
#include <drm/drm_gem.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
-#include "i915_drv.h"
-#include "i915_utils.h"
#include "intel_bo.h"
#include "intel_display.h"
#include "intel_display_core.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dpt.h"
#include "intel_fb.h"
#include "intel_fb_bo.h"
@@ -547,8 +547,6 @@ static bool plane_has_modifier(struct intel_display *display,
u8 plane_caps,
const struct intel_modifier_desc *md)
{
- struct drm_i915_private *i915 = to_i915(display->drm);
-
if (!IS_DISPLAY_VER(display, md->display_ver.from, md->display_ver.until))
return false;
@@ -560,15 +558,15 @@ static bool plane_has_modifier(struct intel_display *display,
* where supported.
*/
if (intel_fb_is_ccs_modifier(md->modifier) &&
- HAS_FLAT_CCS(i915) != !md->ccs.packed_aux_planes)
+ HAS_AUX_CCS(display) != !!md->ccs.packed_aux_planes)
return false;
if (md->modifier == I915_FORMAT_MOD_4_TILED_BMG_CCS &&
- (GRAPHICS_VER(i915) < 20 || !display->platform.dgfx))
+ (DISPLAY_VER(display) < 14 || !display->platform.dgfx))
return false;
if (md->modifier == I915_FORMAT_MOD_4_TILED_LNL_CCS &&
- (GRAPHICS_VER(i915) < 20 || display->platform.dgfx))
+ (DISPLAY_VER(display) < 20 || display->platform.dgfx))
return false;
return true;
@@ -777,7 +775,6 @@ unsigned int
intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane)
{
struct intel_display *display = to_intel_display(fb->dev);
- struct drm_i915_private *i915 = to_i915(display->drm);
unsigned int cpp = fb->format->cpp[color_plane];
switch (fb->modifier) {
@@ -814,7 +811,7 @@ intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane)
return 64;
fallthrough;
case I915_FORMAT_MOD_Y_TILED:
- if (DISPLAY_VER(display) == 2 || HAS_128_BYTE_Y_TILING(i915))
+ if (HAS_128B_Y_TILING(display))
return 128;
else
return 512;
@@ -1329,7 +1326,7 @@ static bool intel_plane_needs_remap(const struct intel_plane_state *plane_state)
* unclear in Bspec, for now no checking.
*/
stride = intel_fb_pitch(fb, 0, rotation);
- max_stride = plane->max_stride(plane, fb->base.format->format,
+ max_stride = plane->max_stride(plane, fb->base.format,
fb->base.modifier, rotation);
return stride > max_stride;
@@ -1975,7 +1972,8 @@ void intel_add_fb_offsets(int *x, int *y,
static
u32 intel_fb_max_stride(struct intel_display *display,
- u32 pixel_format, u64 modifier)
+ const struct drm_format_info *info,
+ u64 modifier)
{
/*
* Arbitrary limit for gen4+ chosen to match the
@@ -1985,7 +1983,7 @@ u32 intel_fb_max_stride(struct intel_display *display,
*/
if (DISPLAY_VER(display) < 4 || intel_fb_is_ccs_modifier(modifier) ||
intel_fb_modifier_uses_dpt(display, modifier))
- return intel_plane_fb_max_stride(display->drm, pixel_format, modifier);
+ return intel_plane_fb_max_stride(display, info, modifier);
else if (DISPLAY_VER(display) >= 7)
return 256 * 1024;
else
@@ -1999,8 +1997,8 @@ intel_fb_stride_alignment(const struct drm_framebuffer *fb, int color_plane)
unsigned int tile_width;
if (is_surface_linear(fb, color_plane)) {
- unsigned int max_stride = intel_plane_fb_max_stride(display->drm,
- fb->format->format,
+ unsigned int max_stride = intel_plane_fb_max_stride(display,
+ fb->format,
fb->modifier);
/*
@@ -2058,7 +2056,7 @@ static int intel_plane_check_stride(const struct intel_plane_state *plane_state)
/* FIXME other color planes? */
stride = plane_state->view.color_plane[0].mapping_stride;
- max_stride = plane->max_stride(plane, fb->format->format,
+ max_stride = plane->max_stride(plane, fb->format,
fb->modifier, rotation);
if (stride > max_stride) {
@@ -2117,6 +2115,7 @@ static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
intel_frontbuffer_put(intel_fb->frontbuffer);
+ kfree(intel_fb->panic);
kfree(intel_fb);
}
@@ -2196,7 +2195,6 @@ static int intel_user_framebuffer_dirty(struct drm_framebuffer *fb,
return ret;
flush:
- intel_bo_flush_if_display(obj);
intel_frontbuffer_flush(front, ORIGIN_DIRTYFB);
return ret;
}
@@ -2215,39 +2213,45 @@ int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
struct intel_display *display = to_intel_display(obj->dev);
struct drm_framebuffer *fb = &intel_fb->base;
u32 max_stride;
- int ret = -EINVAL;
+ int ret;
int i;
+ intel_fb->panic = intel_panic_alloc();
+ if (!intel_fb->panic)
+ return -ENOMEM;
+
/*
* intel_frontbuffer_get() must be done before
* intel_fb_bo_framebuffer_init() to avoid set_tiling vs. addfb race.
*/
intel_fb->frontbuffer = intel_frontbuffer_get(obj);
- if (!intel_fb->frontbuffer)
- return -ENOMEM;
+ if (!intel_fb->frontbuffer) {
+ ret = -ENOMEM;
+ goto err_free_panic;
+ }
- ret = intel_fb_bo_framebuffer_init(fb, obj, mode_cmd);
+ ret = intel_fb_bo_framebuffer_init(obj, mode_cmd);
if (ret)
goto err_frontbuffer_put;
- ret = -EINVAL;
if (!drm_any_plane_has_format(display->drm,
mode_cmd->pixel_format,
mode_cmd->modifier[0])) {
drm_dbg_kms(display->drm,
"unsupported pixel format %p4cc / modifier 0x%llx\n",
&mode_cmd->pixel_format, mode_cmd->modifier[0]);
+ ret = -EINVAL;
goto err_bo_framebuffer_fini;
}
- max_stride = intel_fb_max_stride(display, mode_cmd->pixel_format,
- mode_cmd->modifier[0]);
+ max_stride = intel_fb_max_stride(display, info, mode_cmd->modifier[0]);
if (mode_cmd->pitches[0] > max_stride) {
drm_dbg_kms(display->drm,
"%s pitch (%u) must be at most %d\n",
mode_cmd->modifier[0] != DRM_FORMAT_MOD_LINEAR ?
"tiled" : "linear",
mode_cmd->pitches[0], max_stride);
+ ret = -EINVAL;
goto err_bo_framebuffer_fini;
}
@@ -2256,6 +2260,7 @@ int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
drm_dbg_kms(display->drm,
"plane 0 offset (0x%08x) must be 0\n",
mode_cmd->offsets[0]);
+ ret = -EINVAL;
goto err_bo_framebuffer_fini;
}
@@ -2266,6 +2271,7 @@ int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
if (mode_cmd->handles[i] != mode_cmd->handles[0]) {
drm_dbg_kms(display->drm, "bad plane %d handle\n", i);
+ ret = -EINVAL;
goto err_bo_framebuffer_fini;
}
@@ -2274,6 +2280,7 @@ int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
drm_dbg_kms(display->drm,
"plane %d pitch (%d) must be at least %u byte aligned\n",
i, fb->pitches[i], stride_alignment);
+ ret = -EINVAL;
goto err_bo_framebuffer_fini;
}
@@ -2284,6 +2291,7 @@ int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
drm_dbg_kms(display->drm,
"ccs aux plane %d pitch (%d) must be %d\n",
i, fb->pitches[i], ccs_aux_stride);
+ ret = -EINVAL;
goto err_bo_framebuffer_fini;
}
}
@@ -2323,6 +2331,9 @@ err_bo_framebuffer_fini:
intel_fb_bo_framebuffer_fini(obj);
err_frontbuffer_put:
intel_frontbuffer_put(intel_fb->frontbuffer);
+err_free_panic:
+ kfree(intel_fb->panic);
+
return ret;
}
@@ -2349,20 +2360,11 @@ intel_user_framebuffer_create(struct drm_device *dev,
struct intel_framebuffer *intel_framebuffer_alloc(void)
{
struct intel_framebuffer *intel_fb;
- struct intel_panic *panic;
intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
if (!intel_fb)
return NULL;
- panic = intel_panic_alloc();
- if (!panic) {
- kfree(intel_fb);
- return NULL;
- }
-
- intel_fb->panic = panic;
-
return intel_fb;
}
diff --git a/drivers/gpu/drm/i915/display/intel_fb_bo.c b/drivers/gpu/drm/i915/display/intel_fb_bo.c
index b0e8b89f7ce8..bfecd73d5fa0 100644
--- a/drivers/gpu/drm/i915/display/intel_fb_bo.c
+++ b/drivers/gpu/drm/i915/display/intel_fb_bo.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_object.h"
@@ -18,8 +19,7 @@ void intel_fb_bo_framebuffer_fini(struct drm_gem_object *obj)
/* Nothing to do for i915 */
}
-int intel_fb_bo_framebuffer_init(struct drm_framebuffer *fb,
- struct drm_gem_object *_obj,
+int intel_fb_bo_framebuffer_init(struct drm_gem_object *_obj,
struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_i915_gem_object *obj = to_intel_bo(_obj);
diff --git a/drivers/gpu/drm/i915/display/intel_fb_bo.h b/drivers/gpu/drm/i915/display/intel_fb_bo.h
index eefcb05a99f0..d775773c6c03 100644
--- a/drivers/gpu/drm/i915/display/intel_fb_bo.h
+++ b/drivers/gpu/drm/i915/display/intel_fb_bo.h
@@ -14,8 +14,7 @@ struct drm_mode_fb_cmd2;
void intel_fb_bo_framebuffer_fini(struct drm_gem_object *obj);
-int intel_fb_bo_framebuffer_init(struct drm_framebuffer *fb,
- struct drm_gem_object *obj,
+int intel_fb_bo_framebuffer_init(struct drm_gem_object *obj,
struct drm_mode_fb_cmd2 *mode_cmd);
struct drm_gem_object *
diff --git a/drivers/gpu/drm/i915/display/intel_fb_pin.c b/drivers/gpu/drm/i915/display/intel_fb_pin.c
index 45af04cb0fb2..7249b784fbba 100644
--- a/drivers/gpu/drm/i915/display/intel_fb_pin.c
+++ b/drivers/gpu/drm/i915/display/intel_fb_pin.c
@@ -7,6 +7,8 @@
* DOC: display pinning helpers
*/
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_domain.h"
#include "gem/i915_gem_object.h"
diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c
index 0d380c825791..437d2fda20a7 100644
--- a/drivers/gpu/drm/i915/display/intel_fbc.c
+++ b/drivers/gpu/drm/i915/display/intel_fbc.c
@@ -43,23 +43,23 @@
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_stolen.h"
#include "gt/intel_gt_types.h"
#include "i915_drv.h"
-#include "i915_utils.h"
#include "i915_vgpu.h"
#include "i915_vma.h"
#include "i9xx_plane_regs.h"
-#include "intel_cdclk.h"
#include "intel_de.h"
#include "intel_display_device.h"
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_trace.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_display_wa.h"
#include "intel_fbc.h"
#include "intel_fbc_regs.h"
@@ -102,7 +102,8 @@ struct intel_fbc {
struct mutex lock;
unsigned int busy_bits;
- struct i915_stolen_fb compressed_fb, compressed_llb;
+ struct intel_stolen_node *compressed_fb;
+ struct intel_stolen_node *compressed_llb;
enum intel_fbc_id id;
@@ -141,15 +142,18 @@ static unsigned int intel_fbc_plane_stride(const struct intel_plane_state *plane
return stride;
}
-static unsigned int intel_fbc_cfb_cpp(void)
+static unsigned int intel_fbc_cfb_cpp(const struct intel_plane_state *plane_state)
{
- return 4; /* FBC always 4 bytes per pixel */
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+ unsigned int cpp = fb->format->cpp[0];
+
+ return max(cpp, 4);
}
/* plane stride based cfb stride in bytes, assuming 1:1 compression limit */
static unsigned int intel_fbc_plane_cfb_stride(const struct intel_plane_state *plane_state)
{
- unsigned int cpp = intel_fbc_cfb_cpp();
+ unsigned int cpp = intel_fbc_cfb_cpp(plane_state);
return intel_fbc_plane_stride(plane_state) * cpp;
}
@@ -203,7 +207,7 @@ static unsigned int intel_fbc_cfb_stride(const struct intel_plane_state *plane_s
struct intel_display *display = to_intel_display(plane_state->uapi.plane->dev);
unsigned int stride = intel_fbc_plane_cfb_stride(plane_state);
unsigned int width = drm_rect_width(&plane_state->uapi.src) >> 16;
- unsigned int cpp = intel_fbc_cfb_cpp();
+ unsigned int cpp = intel_fbc_cfb_cpp(plane_state);
return _intel_fbc_cfb_stride(display, cpp, width, stride);
}
@@ -324,8 +328,8 @@ static void i8xx_fbc_deactivate(struct intel_fbc *fbc)
intel_de_write(display, FBC_CONTROL, fbc_ctl);
/* Wait for compressing bit to clear */
- if (intel_de_wait_for_clear(display, FBC_STATUS,
- FBC_STAT_COMPRESSING, 10)) {
+ if (intel_de_wait_for_clear_ms(display, FBC_STATUS,
+ FBC_STAT_COMPRESSING, 10)) {
drm_dbg_kms(display->drm, "FBC idle timed out\n");
return;
}
@@ -376,20 +380,19 @@ static void i8xx_fbc_nuke(struct intel_fbc *fbc)
static void i8xx_fbc_program_cfb(struct intel_fbc *fbc)
{
struct intel_display *display = fbc->display;
- struct drm_i915_private *i915 = to_i915(display->drm);
drm_WARN_ON(display->drm,
- range_end_overflows_t(u64, i915_gem_stolen_area_address(i915),
- i915_gem_stolen_node_offset(&fbc->compressed_fb),
+ range_end_overflows_t(u64, i915_gem_stolen_area_address(display->drm),
+ i915_gem_stolen_node_offset(fbc->compressed_fb),
U32_MAX));
drm_WARN_ON(display->drm,
- range_end_overflows_t(u64, i915_gem_stolen_area_address(i915),
- i915_gem_stolen_node_offset(&fbc->compressed_llb),
+ range_end_overflows_t(u64, i915_gem_stolen_area_address(display->drm),
+ i915_gem_stolen_node_offset(fbc->compressed_llb),
U32_MAX));
intel_de_write(display, FBC_CFB_BASE,
- i915_gem_stolen_node_address(i915, &fbc->compressed_fb));
+ i915_gem_stolen_node_address(fbc->compressed_fb));
intel_de_write(display, FBC_LL_BASE,
- i915_gem_stolen_node_address(i915, &fbc->compressed_llb));
+ i915_gem_stolen_node_address(fbc->compressed_llb));
}
static const struct intel_fbc_funcs i8xx_fbc_funcs = {
@@ -497,7 +500,7 @@ static void g4x_fbc_program_cfb(struct intel_fbc *fbc)
struct intel_display *display = fbc->display;
intel_de_write(display, DPFC_CB_BASE,
- i915_gem_stolen_node_offset(&fbc->compressed_fb));
+ i915_gem_stolen_node_offset(fbc->compressed_fb));
}
static const struct intel_fbc_funcs g4x_fbc_funcs = {
@@ -566,7 +569,7 @@ static void ilk_fbc_program_cfb(struct intel_fbc *fbc)
struct intel_display *display = fbc->display;
intel_de_write(display, ILK_DPFC_CB_BASE(fbc->id),
- i915_gem_stolen_node_offset(&fbc->compressed_fb));
+ i915_gem_stolen_node_offset(fbc->compressed_fb));
}
static const struct intel_fbc_funcs ilk_fbc_funcs = {
@@ -797,7 +800,6 @@ static u64 intel_fbc_cfb_base_max(struct intel_display *display)
static u64 intel_fbc_stolen_end(struct intel_display *display)
{
- struct drm_i915_private __maybe_unused *i915 = to_i915(display->drm);
u64 end;
/* The FBC hardware for BDW/SKL doesn't have access to the stolen
@@ -806,7 +808,7 @@ static u64 intel_fbc_stolen_end(struct intel_display *display)
* underruns, even if that range is not reserved by the BIOS. */
if (display->platform.broadwell ||
(DISPLAY_VER(display) == 9 && !display->platform.broxton))
- end = i915_gem_stolen_area_size(i915) - 8 * 1024 * 1024;
+ end = i915_gem_stolen_area_size(display->drm) - 8 * 1024 * 1024;
else
end = U64_MAX;
@@ -835,20 +837,19 @@ static int find_compression_limit(struct intel_fbc *fbc,
unsigned int size, int min_limit)
{
struct intel_display *display = fbc->display;
- struct drm_i915_private *i915 = to_i915(display->drm);
u64 end = intel_fbc_stolen_end(display);
int ret, limit = min_limit;
size /= limit;
/* Try to over-allocate to reduce reallocations and fragmentation. */
- ret = i915_gem_stolen_insert_node_in_range(i915, &fbc->compressed_fb,
+ ret = i915_gem_stolen_insert_node_in_range(fbc->compressed_fb,
size <<= 1, 4096, 0, end);
if (ret == 0)
return limit;
for (; limit <= intel_fbc_max_limit(display); limit <<= 1) {
- ret = i915_gem_stolen_insert_node_in_range(i915, &fbc->compressed_fb,
+ ret = i915_gem_stolen_insert_node_in_range(fbc->compressed_fb,
size >>= 1, 4096, 0, end);
if (ret == 0)
return limit;
@@ -861,17 +862,15 @@ static int intel_fbc_alloc_cfb(struct intel_fbc *fbc,
unsigned int size, int min_limit)
{
struct intel_display *display = fbc->display;
- struct drm_i915_private *i915 = to_i915(display->drm);
int ret;
drm_WARN_ON(display->drm,
- i915_gem_stolen_node_allocated(&fbc->compressed_fb));
+ i915_gem_stolen_node_allocated(fbc->compressed_fb));
drm_WARN_ON(display->drm,
- i915_gem_stolen_node_allocated(&fbc->compressed_llb));
+ i915_gem_stolen_node_allocated(fbc->compressed_llb));
if (DISPLAY_VER(display) < 5 && !display->platform.g4x) {
- ret = i915_gem_stolen_insert_node(i915, &fbc->compressed_llb,
- 4096, 4096);
+ ret = i915_gem_stolen_insert_node(fbc->compressed_llb, 4096, 4096);
if (ret)
goto err;
}
@@ -887,14 +886,14 @@ static int intel_fbc_alloc_cfb(struct intel_fbc *fbc,
drm_dbg_kms(display->drm,
"reserved %llu bytes of contiguous stolen space for FBC, limit: %d\n",
- i915_gem_stolen_node_size(&fbc->compressed_fb), fbc->limit);
+ i915_gem_stolen_node_size(fbc->compressed_fb), fbc->limit);
return 0;
err_llb:
- if (i915_gem_stolen_node_allocated(&fbc->compressed_llb))
- i915_gem_stolen_remove_node(i915, &fbc->compressed_llb);
+ if (i915_gem_stolen_node_allocated(fbc->compressed_llb))
+ i915_gem_stolen_remove_node(fbc->compressed_llb);
err:
- if (i915_gem_stolen_initialized(i915))
+ if (i915_gem_stolen_initialized(display->drm))
drm_info_once(display->drm,
"not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size);
return -ENOSPC;
@@ -932,9 +931,12 @@ static void intel_fbc_program_workarounds(struct intel_fbc *fbc)
if (IS_DISPLAY_VER(display, 11, 12))
intel_de_rmw(display, ILK_DPFC_CHICKEN(fbc->id),
0, DPFC_CHICKEN_COMP_DUMMY_PIXEL);
-
- /* Wa_22014263786:icl,jsl,tgl,dg1,rkl,adls,adlp,mtl */
- if (DISPLAY_VER(display) >= 11 && !display->platform.dg2)
+ /*
+ * Wa_22014263786
+ * Fixes: Screen flicker with FBC and Package C state enabled
+ * Workaround: Forced SLB invalidation before start of new frame.
+ */
+ if (intel_display_wa(display, 22014263786))
intel_de_rmw(display, ILK_DPFC_CHICKEN(fbc->id),
0, DPFC_CHICKEN_FORCE_SLB_INVALIDATION);
@@ -945,16 +947,13 @@ static void intel_fbc_program_workarounds(struct intel_fbc *fbc)
static void __intel_fbc_cleanup_cfb(struct intel_fbc *fbc)
{
- struct intel_display *display = fbc->display;
- struct drm_i915_private *i915 = to_i915(display->drm);
-
if (WARN_ON(intel_fbc_hw_is_active(fbc)))
return;
- if (i915_gem_stolen_node_allocated(&fbc->compressed_llb))
- i915_gem_stolen_remove_node(i915, &fbc->compressed_llb);
- if (i915_gem_stolen_node_allocated(&fbc->compressed_fb))
- i915_gem_stolen_remove_node(i915, &fbc->compressed_fb);
+ if (i915_gem_stolen_node_allocated(fbc->compressed_llb))
+ i915_gem_stolen_remove_node(fbc->compressed_llb);
+ if (i915_gem_stolen_node_allocated(fbc->compressed_fb))
+ i915_gem_stolen_remove_node(fbc->compressed_fb);
}
void intel_fbc_cleanup(struct intel_display *display)
@@ -967,6 +966,9 @@ void intel_fbc_cleanup(struct intel_display *display)
__intel_fbc_cleanup_cfb(fbc);
mutex_unlock(&fbc->lock);
+ i915_gem_stolen_node_free(fbc->compressed_fb);
+ i915_gem_stolen_node_free(fbc->compressed_llb);
+
kfree(fbc);
}
}
@@ -1083,11 +1085,57 @@ static bool lnl_fbc_pixel_format_is_valid(const struct intel_plane_state *plane_
}
}
+static bool
+xe3p_lpd_fbc_fp16_format_is_valid(const struct intel_plane_state *plane_state)
+{
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+
+ switch (fb->format->format) {
+ case DRM_FORMAT_ARGB16161616F:
+ case DRM_FORMAT_ABGR16161616F:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool xe3p_lpd_fbc_pixel_format_is_valid(const struct intel_plane_state *plane_state)
+{
+ const struct drm_framebuffer *fb = plane_state->hw.fb;
+
+ if (lnl_fbc_pixel_format_is_valid(plane_state))
+ return true;
+
+ if (xe3p_lpd_fbc_fp16_format_is_valid(plane_state))
+ return true;
+
+ switch (fb->format->format) {
+ case DRM_FORMAT_XRGB16161616:
+ case DRM_FORMAT_XBGR16161616:
+ case DRM_FORMAT_ARGB16161616:
+ case DRM_FORMAT_ABGR16161616:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool
+intel_fbc_is_enable_pixel_normalizer(const struct intel_plane_state *plane_state)
+{
+ struct intel_display *display = to_intel_display(plane_state);
+
+ return DISPLAY_VER(display) >= 35 &&
+ xe3p_lpd_fbc_fp16_format_is_valid(plane_state);
+}
+
static bool pixel_format_is_valid(const struct intel_plane_state *plane_state)
{
struct intel_display *display = to_intel_display(plane_state->uapi.plane->dev);
- if (DISPLAY_VER(display) >= 20)
+ if (DISPLAY_VER(display) >= 35)
+ return xe3p_lpd_fbc_pixel_format_is_valid(plane_state);
+ else if (DISPLAY_VER(display) >= 20)
return lnl_fbc_pixel_format_is_valid(plane_state);
else if (DISPLAY_VER(display) >= 5 || display->platform.g4x)
return g4x_fbc_pixel_format_is_valid(plane_state);
@@ -1355,7 +1403,7 @@ static bool intel_fbc_is_cfb_ok(const struct intel_plane_state *plane_state)
return intel_fbc_min_limit(plane_state) <= fbc->limit &&
intel_fbc_cfb_size(plane_state) <= fbc->limit *
- i915_gem_stolen_node_size(&fbc->compressed_fb);
+ i915_gem_stolen_node_size(fbc->compressed_fb);
}
static bool intel_fbc_is_ok(const struct intel_plane_state *plane_state)
@@ -1421,6 +1469,18 @@ intel_fbc_prepare_dirty_rect(struct intel_atomic_state *state,
}
}
+static int _intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ /* WaFbcExceedCdClockThreshold:hsw,bdw */
+ if (display->platform.haswell || display->platform.broadwell)
+ return DIV_ROUND_UP(crtc_state->pixel_rate * 100, 95);
+
+ /* no FBC specific limits to worry about */
+ return 0;
+}
+
static int intel_fbc_check_plane(struct intel_atomic_state *state,
struct intel_plane *plane)
{
@@ -1436,7 +1496,7 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
if (!fbc)
return 0;
- if (!i915_gem_stolen_initialized(i915)) {
+ if (!i915_gem_stolen_initialized(display->drm)) {
plane_state->no_fbc_reason = "stolen memory not initialised";
return 0;
}
@@ -1462,7 +1522,8 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
}
/* WaFbcTurnOffFbcWhenHyperVisorIsUsed:skl,bxt */
- if (i915_vtd_active(i915) && (display->platform.skylake || display->platform.broxton)) {
+ if (intel_display_vtd_active(display) &&
+ (display->platform.skylake || display->platform.broxton)) {
plane_state->no_fbc_reason = "VT-d enabled";
return 0;
}
@@ -1560,18 +1621,9 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
return 0;
}
- /* WaFbcExceedCdClockThreshold:hsw,bdw */
- if (display->platform.haswell || display->platform.broadwell) {
- const struct intel_cdclk_state *cdclk_state;
-
- cdclk_state = intel_atomic_get_cdclk_state(state);
- if (IS_ERR(cdclk_state))
- return PTR_ERR(cdclk_state);
-
- if (crtc_state->pixel_rate >= intel_cdclk_logical(cdclk_state) * 95 / 100) {
- plane_state->no_fbc_reason = "pixel rate too high";
- return 0;
- }
+ if (_intel_fbc_min_cdclk(crtc_state) > display->cdclk.max_cdclk_freq) {
+ plane_state->no_fbc_reason = "pixel rate too high";
+ return 0;
}
plane_state->no_fbc_reason = NULL;
@@ -1579,6 +1631,27 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state,
return 0;
}
+int intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct intel_plane *plane = to_intel_plane(crtc->base.primary);
+ int min_cdclk;
+
+ if (!plane->fbc)
+ return 0;
+
+ min_cdclk = _intel_fbc_min_cdclk(crtc_state);
+
+ /*
+ * Do not ask for more than the max CDCLK frequency,
+ * if that is not enough FBC will simply not be used.
+ */
+ if (min_cdclk > display->cdclk.max_cdclk_freq)
+ return 0;
+
+ return min_cdclk;
+}
static bool intel_fbc_can_flip_nuke(struct intel_atomic_state *state,
struct intel_crtc *crtc,
@@ -2083,6 +2156,13 @@ static struct intel_fbc *intel_fbc_create(struct intel_display *display,
if (!fbc)
return NULL;
+ fbc->compressed_fb = i915_gem_stolen_node_alloc(display->drm);
+ if (!fbc->compressed_fb)
+ goto err;
+ fbc->compressed_llb = i915_gem_stolen_node_alloc(display->drm);
+ if (!fbc->compressed_llb)
+ goto err;
+
fbc->id = fbc_id;
fbc->display = display;
INIT_WORK(&fbc->underrun_work, intel_fbc_underrun_work_fn);
@@ -2102,6 +2182,13 @@ static struct intel_fbc *intel_fbc_create(struct intel_display *display,
fbc->funcs = &i8xx_fbc_funcs;
return fbc;
+
+err:
+ i915_gem_stolen_node_free(fbc->compressed_llb);
+ i915_gem_stolen_node_free(fbc->compressed_fb);
+ kfree(fbc);
+
+ return NULL;
}
/**
diff --git a/drivers/gpu/drm/i915/display/intel_fbc.h b/drivers/gpu/drm/i915/display/intel_fbc.h
index 0e715cb6b4e6..91424563206a 100644
--- a/drivers/gpu/drm/i915/display/intel_fbc.h
+++ b/drivers/gpu/drm/i915/display/intel_fbc.h
@@ -28,6 +28,7 @@ enum intel_fbc_id {
};
int intel_fbc_atomic_check(struct intel_atomic_state *state);
+int intel_fbc_min_cdclk(const struct intel_crtc_state *crtc_state);
bool intel_fbc_pre_update(struct intel_atomic_state *state,
struct intel_crtc *crtc);
void intel_fbc_post_update(struct intel_atomic_state *state,
@@ -52,5 +53,7 @@ void intel_fbc_prepare_dirty_rect(struct intel_atomic_state *state,
struct intel_crtc *crtc);
void intel_fbc_dirty_rect_update_noarm(struct intel_dsb *dsb,
struct intel_plane *plane);
+bool
+intel_fbc_is_enable_pixel_normalizer(const struct intel_plane_state *plane_state);
#endif /* __INTEL_FBC_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
index 7c4709d58aa3..9cd03e2adeb2 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
@@ -146,8 +146,6 @@ static void intel_fbdev_fb_destroy(struct fb_info *info)
drm_framebuffer_remove(fb_helper->fb);
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
__diag_push();
@@ -207,14 +205,70 @@ static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.fb_set_suspend = intelfb_set_suspend,
};
+static void intel_fbdev_fill_mode_cmd(struct drm_fb_helper_surface_size *sizes,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ /* we don't do packed 24bpp */
+ if (sizes->surface_bpp == 24)
+ sizes->surface_bpp = 32;
+
+ mode_cmd->flags = DRM_MODE_FB_MODIFIERS;
+ mode_cmd->width = sizes->surface_width;
+ mode_cmd->height = sizes->surface_height;
+
+ mode_cmd->pitches[0] = intel_fbdev_fb_pitch_align(mode_cmd->width * DIV_ROUND_UP(sizes->surface_bpp, 8));
+ mode_cmd->pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+ mode_cmd->modifier[0] = DRM_FORMAT_MOD_LINEAR;
+}
+
+static struct intel_framebuffer *
+__intel_fbdev_fb_alloc(struct intel_display *display,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct drm_mode_fb_cmd2 mode_cmd = {};
+ struct drm_framebuffer *fb;
+ struct drm_gem_object *obj;
+ int size;
+
+ intel_fbdev_fill_mode_cmd(sizes, &mode_cmd);
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+ size = PAGE_ALIGN(size);
+
+ obj = intel_fbdev_fb_bo_create(display->drm, size);
+ if (IS_ERR(obj)) {
+ fb = ERR_CAST(obj);
+ goto err;
+ }
+
+ fb = intel_framebuffer_create(obj,
+ drm_get_format_info(display->drm,
+ mode_cmd.pixel_format,
+ mode_cmd.modifier[0]),
+ &mode_cmd);
+ if (IS_ERR(fb)) {
+ intel_fbdev_fb_bo_destroy(obj);
+ goto err;
+ }
+
+ drm_gem_object_put(obj);
+
+ return to_intel_framebuffer(fb);
+
+err:
+ return ERR_CAST(fb);
+
+}
+
int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct intel_display *display = to_intel_display(helper->dev);
struct intel_fbdev *ifbdev = to_intel_fbdev(helper);
struct intel_framebuffer *fb = ifbdev->fb;
+ struct fb_info *info = helper->info;
struct ref_tracker *wakeref;
- struct fb_info *info;
struct i915_vma *vma;
unsigned long flags = 0;
bool prealloc = false;
@@ -237,7 +291,8 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
if (!fb || drm_WARN_ON(display->drm, !intel_fb_bo(&fb->base))) {
drm_dbg_kms(display->drm,
"no BIOS fb, allocating a new one\n");
- fb = intel_fbdev_fb_alloc(helper, sizes);
+
+ fb = __intel_fbdev_fb_alloc(display, sizes);
if (IS_ERR(fb))
return PTR_ERR(fb);
} else {
@@ -263,13 +318,6 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
goto out_unlock;
}
- info = drm_fb_helper_alloc_info(helper);
- if (IS_ERR(info)) {
- drm_err(display->drm, "Failed to allocate fb_info (%pe)\n", info);
- ret = PTR_ERR(info);
- goto out_unpin;
- }
-
helper->funcs = &intel_fb_helper_funcs;
helper->fb = &fb->base;
@@ -277,7 +325,7 @@ int intel_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
obj = intel_fb_bo(&fb->base);
- ret = intel_fbdev_fb_fill_info(display, info, obj, vma);
+ ret = intel_fbdev_fb_fill_info(display->drm, info, obj, vma);
if (ret)
goto out_unpin;
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev_fb.c b/drivers/gpu/drm/i915/display/intel_fbdev_fb.c
index 210aee9ae88b..c3202ba141c5 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev_fb.c
+++ b/drivers/gpu/drm/i915/display/intel_fbdev_fb.c
@@ -3,40 +3,24 @@
* Copyright © 2023 Intel Corporation
*/
-#include <drm/drm_fb_helper.h>
+#include <linux/fb.h>
+
+#include <drm/drm_print.h>
#include "gem/i915_gem_lmem.h"
#include "i915_drv.h"
-#include "intel_display_core.h"
-#include "intel_display_types.h"
-#include "intel_fb.h"
#include "intel_fbdev_fb.h"
-struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes)
+u32 intel_fbdev_fb_pitch_align(u32 stride)
{
- struct intel_display *display = to_intel_display(helper->dev);
- struct drm_i915_private *dev_priv = to_i915(display->drm);
- struct drm_framebuffer *fb;
- struct drm_mode_fb_cmd2 mode_cmd = {};
- struct drm_i915_gem_object *obj;
- int size;
-
- /* we don't do packed 24bpp */
- if (sizes->surface_bpp == 24)
- sizes->surface_bpp = 32;
-
- mode_cmd.width = sizes->surface_width;
- mode_cmd.height = sizes->surface_height;
-
- mode_cmd.pitches[0] = ALIGN(mode_cmd.width *
- DIV_ROUND_UP(sizes->surface_bpp, 8), 64);
- mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
- sizes->surface_depth);
+ return ALIGN(stride, 64);
+}
- size = mode_cmd.pitches[0] * mode_cmd.height;
- size = PAGE_ALIGN(size);
+struct drm_gem_object *intel_fbdev_fb_bo_create(struct drm_device *drm, int size)
+{
+ struct drm_i915_private *dev_priv = to_i915(drm);
+ struct drm_i915_gem_object *obj;
obj = ERR_PTR(-ENODEV);
if (HAS_LMEM(dev_priv)) {
@@ -51,31 +35,29 @@ struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
*
* Also skip stolen on MTL as Wa_22018444074 mitigation.
*/
- if (!display->platform.meteorlake && size * 2 < dev_priv->dsm.usable_size)
+ if (!IS_METEORLAKE(dev_priv) && size * 2 < dev_priv->dsm.usable_size)
obj = i915_gem_object_create_stolen(dev_priv, size);
if (IS_ERR(obj))
obj = i915_gem_object_create_shmem(dev_priv, size);
}
if (IS_ERR(obj)) {
- drm_err(display->drm, "failed to allocate framebuffer (%pe)\n", obj);
+ drm_err(drm, "failed to allocate framebuffer (%pe)\n", obj);
return ERR_PTR(-ENOMEM);
}
- fb = intel_framebuffer_create(intel_bo_to_drm_bo(obj),
- drm_get_format_info(display->drm,
- mode_cmd.pixel_format,
- mode_cmd.modifier[0]),
- &mode_cmd);
- i915_gem_object_put(obj);
+ return &obj->base;
+}
- return to_intel_framebuffer(fb);
+void intel_fbdev_fb_bo_destroy(struct drm_gem_object *obj)
+{
+ drm_gem_object_put(obj);
}
-int intel_fbdev_fb_fill_info(struct intel_display *display, struct fb_info *info,
+int intel_fbdev_fb_fill_info(struct drm_device *drm, struct fb_info *info,
struct drm_gem_object *_obj, struct i915_vma *vma)
{
- struct drm_i915_private *i915 = to_i915(display->drm);
+ struct drm_i915_private *i915 = to_i915(drm);
struct drm_i915_gem_object *obj = to_intel_bo(_obj);
struct i915_gem_ww_ctx ww;
void __iomem *vaddr;
@@ -107,7 +89,7 @@ int intel_fbdev_fb_fill_info(struct intel_display *display, struct fb_info *info
vaddr = i915_vma_pin_iomap(vma);
if (IS_ERR(vaddr)) {
- drm_err(display->drm,
+ drm_err(drm,
"Failed to remap framebuffer into virtual memory (%pe)\n", vaddr);
ret = PTR_ERR(vaddr);
continue;
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev_fb.h b/drivers/gpu/drm/i915/display/intel_fbdev_fb.h
index cb7957272715..fd0b3775dc1f 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev_fb.h
+++ b/drivers/gpu/drm/i915/display/intel_fbdev_fb.h
@@ -6,16 +6,18 @@
#ifndef __INTEL_FBDEV_FB_H__
#define __INTEL_FBDEV_FB_H__
-struct drm_fb_helper;
-struct drm_fb_helper_surface_size;
+#include <linux/types.h>
+
+struct drm_device;
struct drm_gem_object;
+struct drm_mode_fb_cmd2;
struct fb_info;
struct i915_vma;
-struct intel_display;
-struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes);
-int intel_fbdev_fb_fill_info(struct intel_display *display, struct fb_info *info,
+u32 intel_fbdev_fb_pitch_align(u32 stride);
+struct drm_gem_object *intel_fbdev_fb_bo_create(struct drm_device *drm, int size);
+void intel_fbdev_fb_bo_destroy(struct drm_gem_object *obj);
+int intel_fbdev_fb_fill_info(struct drm_device *drm, struct fb_info *info,
struct drm_gem_object *obj, struct i915_vma *vma);
#endif
diff --git a/drivers/gpu/drm/i915/display/intel_fdi.c b/drivers/gpu/drm/i915/display/intel_fdi.c
index 59a36b3a22c1..5bb0090dd5ed 100644
--- a/drivers/gpu/drm/i915/display/intel_fdi.c
+++ b/drivers/gpu/drm/i915/display/intel_fdi.c
@@ -9,13 +9,13 @@
#include <drm/drm_print.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_crtc.h"
#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_fdi.h"
#include "intel_fdi_regs.h"
diff --git a/drivers/gpu/drm/i915/display/intel_flipq.c b/drivers/gpu/drm/i915/display/intel_flipq.c
index 6ab2272ab2df..1e9550cb66a3 100644
--- a/drivers/gpu/drm/i915/display/intel_flipq.c
+++ b/drivers/gpu/drm/i915/display/intel_flipq.c
@@ -7,16 +7,16 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
-#include "intel_step.h"
#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display_core.h"
#include "intel_display_types.h"
-#include "intel_flipq.h"
+#include "intel_display_utils.h"
#include "intel_dmc.h"
#include "intel_dmc_regs.h"
#include "intel_dsb.h"
+#include "intel_flipq.h"
+#include "intel_step.h"
#include "intel_vblank.h"
#include "intel_vrr.h"
@@ -163,10 +163,10 @@ static void intel_flipq_preempt(struct intel_crtc *crtc, bool preempt)
PIPEDMC_FQ_CTRL_PREEMPT, preempt ? PIPEDMC_FQ_CTRL_PREEMPT : 0);
if (preempt &&
- intel_de_wait_for_clear(display,
- PIPEDMC_FQ_STATUS(crtc->pipe),
- PIPEDMC_FQ_STATUS_BUSY,
- intel_flipq_preempt_timeout_ms(display)))
+ intel_de_wait_for_clear_ms(display,
+ PIPEDMC_FQ_STATUS(crtc->pipe),
+ PIPEDMC_FQ_STATUS_BUSY,
+ intel_flipq_preempt_timeout_ms(display)))
drm_err(display->drm, "[CRTC:%d:%s] flip queue preempt timeout\n",
crtc->base.base.id, crtc->base.name);
}
diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
index 73ed28ac9573..03c4978fa5ec 100644
--- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c
+++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c
@@ -56,9 +56,8 @@
*/
#include <drm/drm_gem.h>
+#include <drm/drm_print.h>
-#include "i915_active.h"
-#include "i915_vma.h"
#include "intel_bo.h"
#include "intel_display_trace.h"
#include "intel_display_types.h"
@@ -103,51 +102,6 @@ static void frontbuffer_flush(struct intel_display *display,
}
/**
- * intel_frontbuffer_flip_prepare - prepare asynchronous frontbuffer flip
- * @display: display device
- * @frontbuffer_bits: frontbuffer plane tracking bits
- *
- * This function gets called after scheduling a flip on @obj. The actual
- * frontbuffer flushing will be delayed until completion is signalled with
- * intel_frontbuffer_flip_complete. If an invalidate happens in between this
- * flush will be cancelled.
- *
- * Can be called without any locks held.
- */
-void intel_frontbuffer_flip_prepare(struct intel_display *display,
- unsigned frontbuffer_bits)
-{
- spin_lock(&display->fb_tracking.lock);
- display->fb_tracking.flip_bits |= frontbuffer_bits;
- /* Remove stale busy bits due to the old buffer. */
- display->fb_tracking.busy_bits &= ~frontbuffer_bits;
- spin_unlock(&display->fb_tracking.lock);
-}
-
-/**
- * intel_frontbuffer_flip_complete - complete asynchronous frontbuffer flip
- * @display: display device
- * @frontbuffer_bits: frontbuffer plane tracking bits
- *
- * This function gets called after the flip has been latched and will complete
- * on the next vblank. It will execute the flush if it hasn't been cancelled yet.
- *
- * Can be called without any locks held.
- */
-void intel_frontbuffer_flip_complete(struct intel_display *display,
- unsigned frontbuffer_bits)
-{
- spin_lock(&display->fb_tracking.lock);
- /* Mask any cancelled flips. */
- frontbuffer_bits &= display->fb_tracking.flip_bits;
- display->fb_tracking.flip_bits &= ~frontbuffer_bits;
- spin_unlock(&display->fb_tracking.lock);
-
- if (frontbuffer_bits)
- frontbuffer_flush(display, frontbuffer_bits, ORIGIN_FLIP);
-}
-
-/**
* intel_frontbuffer_flip - synchronous frontbuffer flip
* @display: display device
* @frontbuffer_bits: frontbuffer plane tracking bits
@@ -173,12 +127,11 @@ void __intel_fb_invalidate(struct intel_frontbuffer *front,
enum fb_op_origin origin,
unsigned int frontbuffer_bits)
{
- struct intel_display *display = to_intel_display(front->obj->dev);
+ struct intel_display *display = front->display;
if (origin == ORIGIN_CS) {
spin_lock(&display->fb_tracking.lock);
display->fb_tracking.busy_bits |= frontbuffer_bits;
- display->fb_tracking.flip_bits &= ~frontbuffer_bits;
spin_unlock(&display->fb_tracking.lock);
}
@@ -194,7 +147,10 @@ void __intel_fb_flush(struct intel_frontbuffer *front,
enum fb_op_origin origin,
unsigned int frontbuffer_bits)
{
- struct intel_display *display = to_intel_display(front->obj->dev);
+ struct intel_display *display = front->display;
+
+ if (origin == ORIGIN_DIRTYFB)
+ intel_bo_frontbuffer_flush_for_display(front);
if (origin == ORIGIN_CS) {
spin_lock(&display->fb_tracking.lock);
@@ -208,12 +164,16 @@ void __intel_fb_flush(struct intel_frontbuffer *front,
frontbuffer_flush(display, frontbuffer_bits, origin);
}
+static void intel_frontbuffer_ref(struct intel_frontbuffer *front)
+{
+ intel_bo_frontbuffer_ref(front);
+}
+
static void intel_frontbuffer_flush_work(struct work_struct *work)
{
struct intel_frontbuffer *front =
container_of(work, struct intel_frontbuffer, flush_work);
- intel_bo_flush_if_display(front->obj);
intel_frontbuffer_flush(front, ORIGIN_DIRTYFB);
intel_frontbuffer_put(front);
}
@@ -230,93 +190,31 @@ void intel_frontbuffer_queue_flush(struct intel_frontbuffer *front)
if (!front)
return;
- kref_get(&front->ref);
+ intel_frontbuffer_ref(front);
if (!schedule_work(&front->flush_work))
intel_frontbuffer_put(front);
}
-static int frontbuffer_active(struct i915_active *ref)
+void intel_frontbuffer_init(struct intel_frontbuffer *front, struct drm_device *drm)
{
- struct intel_frontbuffer *front =
- container_of(ref, typeof(*front), write);
-
- kref_get(&front->ref);
- return 0;
+ front->display = to_intel_display(drm);
+ atomic_set(&front->bits, 0);
+ INIT_WORK(&front->flush_work, intel_frontbuffer_flush_work);
}
-static void frontbuffer_retire(struct i915_active *ref)
+void intel_frontbuffer_fini(struct intel_frontbuffer *front)
{
- struct intel_frontbuffer *front =
- container_of(ref, typeof(*front), write);
-
- intel_frontbuffer_flush(front, ORIGIN_CS);
- intel_frontbuffer_put(front);
+ drm_WARN_ON(front->display->drm, atomic_read(&front->bits));
}
-static void frontbuffer_release(struct kref *ref)
- __releases(&to_intel_display(front->obj->dev)->fb_tracking.lock)
+struct intel_frontbuffer *intel_frontbuffer_get(struct drm_gem_object *obj)
{
- struct intel_frontbuffer *ret, *front =
- container_of(ref, typeof(*front), ref);
- struct drm_gem_object *obj = front->obj;
- struct intel_display *display = to_intel_display(obj->dev);
-
- drm_WARN_ON(display->drm, atomic_read(&front->bits));
-
- i915_ggtt_clear_scanout(to_intel_bo(obj));
-
- ret = intel_bo_set_frontbuffer(obj, NULL);
- drm_WARN_ON(display->drm, ret);
- spin_unlock(&display->fb_tracking.lock);
-
- i915_active_fini(&front->write);
-
- drm_gem_object_put(obj);
- kfree_rcu(front, rcu);
-}
-
-struct intel_frontbuffer *
-intel_frontbuffer_get(struct drm_gem_object *obj)
-{
- struct intel_display *display = to_intel_display(obj->dev);
- struct intel_frontbuffer *front, *cur;
-
- front = intel_bo_get_frontbuffer(obj);
- if (front)
- return front;
-
- front = kmalloc(sizeof(*front), GFP_KERNEL);
- if (!front)
- return NULL;
-
- drm_gem_object_get(obj);
-
- front->obj = obj;
- kref_init(&front->ref);
- atomic_set(&front->bits, 0);
- i915_active_init(&front->write,
- frontbuffer_active,
- frontbuffer_retire,
- I915_ACTIVE_RETIRE_SLEEPS);
- INIT_WORK(&front->flush_work, intel_frontbuffer_flush_work);
-
- spin_lock(&display->fb_tracking.lock);
- cur = intel_bo_set_frontbuffer(obj, front);
- spin_unlock(&display->fb_tracking.lock);
-
- if (cur != front) {
- drm_gem_object_put(obj);
- kfree(front);
- }
-
- return cur;
+ return intel_bo_frontbuffer_get(obj);
}
void intel_frontbuffer_put(struct intel_frontbuffer *front)
{
- kref_put_lock(&front->ref,
- frontbuffer_release,
- &to_intel_display(front->obj->dev)->fb_tracking.lock);
+ intel_bo_frontbuffer_put(front);
}
/**
@@ -345,17 +243,13 @@ void intel_frontbuffer_track(struct intel_frontbuffer *old,
BUILD_BUG_ON(I915_MAX_PLANES > INTEL_FRONTBUFFER_BITS_PER_PIPE);
if (old) {
- struct intel_display *display = to_intel_display(old->obj->dev);
-
- drm_WARN_ON(display->drm,
+ drm_WARN_ON(old->display->drm,
!(atomic_read(&old->bits) & frontbuffer_bits));
atomic_andnot(frontbuffer_bits, &old->bits);
}
if (new) {
- struct intel_display *display = to_intel_display(new->obj->dev);
-
- drm_WARN_ON(display->drm,
+ drm_WARN_ON(new->display->drm,
atomic_read(&new->bits) & frontbuffer_bits);
atomic_or(frontbuffer_bits, &new->bits);
}
diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.h b/drivers/gpu/drm/i915/display/intel_frontbuffer.h
index 2fee12eaf9b6..22677acb4c06 100644
--- a/drivers/gpu/drm/i915/display/intel_frontbuffer.h
+++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.h
@@ -26,10 +26,9 @@
#include <linux/atomic.h>
#include <linux/bits.h>
-#include <linux/kref.h>
-
-#include "i915_active_types.h"
+#include <linux/workqueue_types.h>
+struct drm_device;
struct drm_gem_object;
struct intel_display;
@@ -42,12 +41,8 @@ enum fb_op_origin {
};
struct intel_frontbuffer {
- struct kref ref;
+ struct intel_display *display;
atomic_t bits;
- struct i915_active write;
- struct drm_gem_object *obj;
- struct rcu_head rcu;
-
struct work_struct flush_work;
};
@@ -68,10 +63,6 @@ struct intel_frontbuffer {
GENMASK(INTEL_FRONTBUFFER_BITS_PER_PIPE * ((pipe) + 1) - 1, \
INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))
-void intel_frontbuffer_flip_prepare(struct intel_display *display,
- unsigned frontbuffer_bits);
-void intel_frontbuffer_flip_complete(struct intel_display *display,
- unsigned frontbuffer_bits);
void intel_frontbuffer_flip(struct intel_display *display,
unsigned frontbuffer_bits);
@@ -144,4 +135,7 @@ void intel_frontbuffer_track(struct intel_frontbuffer *old,
struct intel_frontbuffer *new,
unsigned int frontbuffer_bits);
+void intel_frontbuffer_init(struct intel_frontbuffer *front, struct drm_device *drm);
+void intel_frontbuffer_fini(struct intel_frontbuffer *front);
+
#endif /* __INTEL_FRONTBUFFER_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c
index 358210adb8f8..795012d7c24c 100644
--- a/drivers/gpu/drm/i915/display/intel_gmbus.c
+++ b/drivers/gpu/drm/i915/display/intel_gmbus.c
@@ -32,6 +32,7 @@
#include <linux/i2c.h>
#include <linux/iopoll.h>
+#include <drm/drm_print.h>
#include <drm/display/drm_hdcp_helper.h>
#include "i915_drv.h"
@@ -448,7 +449,7 @@ gmbus_wait_idle(struct intel_display *display)
add_wait_queue(&display->gmbus.wait_queue, &wait);
intel_de_write_fw(display, GMBUS4(display), irq_enable);
- ret = intel_de_wait_fw(display, GMBUS2(display), GMBUS_ACTIVE, 0, 10, NULL);
+ ret = intel_de_wait_fw_ms(display, GMBUS2(display), GMBUS_ACTIVE, 0, 10, NULL);
intel_de_write_fw(display, GMBUS4(display), 0);
remove_wait_queue(&display->gmbus.wait_queue, &wait);
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c
index 531ee122bf82..5e1a96223a9c 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.c
@@ -19,9 +19,9 @@
#include <drm/intel/i915_component.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_connector.h"
#include "intel_de.h"
+#include "intel_display_jiffies.h"
#include "intel_display_power.h"
#include "intel_display_power_well.h"
#include "intel_display_regs.h"
@@ -410,9 +410,8 @@ static int intel_hdcp_load_keys(struct intel_display *display)
}
/* Wait for the keys to load (500us) */
- ret = intel_de_wait_custom(display, HDCP_KEY_STATUS,
- HDCP_KEY_LOAD_DONE, HDCP_KEY_LOAD_DONE,
- 10, 1, &val);
+ ret = intel_de_wait_ms(display, HDCP_KEY_STATUS, HDCP_KEY_LOAD_DONE,
+ HDCP_KEY_LOAD_DONE, 1, &val);
if (ret)
return ret;
else if (!(val & HDCP_KEY_LOAD_STATUS))
@@ -428,7 +427,7 @@ static int intel_hdcp_load_keys(struct intel_display *display)
static int intel_write_sha_text(struct intel_display *display, u32 sha_text)
{
intel_de_write(display, HDCP_SHA_TEXT, sha_text);
- if (intel_de_wait_for_set(display, HDCP_REP_CTL, HDCP_SHA1_READY, 1)) {
+ if (intel_de_wait_for_set_ms(display, HDCP_REP_CTL, HDCP_SHA1_READY, 1)) {
drm_err(display->drm, "Timed out waiting for SHA1 ready\n");
return -ETIMEDOUT;
}
@@ -707,8 +706,8 @@ int intel_hdcp_validate_v_prime(struct intel_connector *connector,
/* Tell the HW we're done with the hash and wait for it to ACK */
intel_de_write(display, HDCP_REP_CTL,
rep_ctl | HDCP_SHA1_COMPLETE_HASH);
- if (intel_de_wait_for_set(display, HDCP_REP_CTL,
- HDCP_SHA1_COMPLETE, 1)) {
+ if (intel_de_wait_for_set_ms(display, HDCP_REP_CTL,
+ HDCP_SHA1_COMPLETE, 1)) {
drm_err(display->drm, "Timed out waiting for SHA1 complete\n");
return -ETIMEDOUT;
}
@@ -856,9 +855,9 @@ static int intel_hdcp_auth(struct intel_connector *connector)
HDCP_CONF_CAPTURE_AN);
/* Wait for An to be acquired */
- if (intel_de_wait_for_set(display,
- HDCP_STATUS(display, cpu_transcoder, port),
- HDCP_STATUS_AN_READY, 1)) {
+ if (intel_de_wait_for_set_ms(display,
+ HDCP_STATUS(display, cpu_transcoder, port),
+ HDCP_STATUS_AN_READY, 1)) {
drm_err(display->drm, "Timed out waiting for An\n");
return -ETIMEDOUT;
}
@@ -953,10 +952,10 @@ static int intel_hdcp_auth(struct intel_connector *connector)
}
/* Wait for encryption confirmation */
- if (intel_de_wait_for_set(display,
- HDCP_STATUS(display, cpu_transcoder, port),
- HDCP_STATUS_ENC,
- HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
+ if (intel_de_wait_for_set_ms(display,
+ HDCP_STATUS(display, cpu_transcoder, port),
+ HDCP_STATUS_ENC,
+ HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
drm_err(display->drm, "Timed out waiting for encryption\n");
return -ETIMEDOUT;
}
@@ -1013,9 +1012,9 @@ static int _intel_hdcp_disable(struct intel_connector *connector)
hdcp->hdcp_encrypted = false;
intel_de_write(display, HDCP_CONF(display, cpu_transcoder, port), 0);
- if (intel_de_wait_for_clear(display,
- HDCP_STATUS(display, cpu_transcoder, port),
- ~0, HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
+ if (intel_de_wait_for_clear_ms(display,
+ HDCP_STATUS(display, cpu_transcoder, port),
+ ~0, HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS)) {
drm_err(display->drm,
"Failed to disable HDCP, timeout clearing status\n");
return -ETIMEDOUT;
@@ -1940,11 +1939,10 @@ static int hdcp2_enable_encryption(struct intel_connector *connector)
intel_de_rmw(display, HDCP2_CTL(display, cpu_transcoder, port),
0, CTL_LINK_ENCRYPTION_REQ);
- ret = intel_de_wait_for_set(display,
- HDCP2_STATUS(display, cpu_transcoder,
- port),
- LINK_ENCRYPTION_STATUS,
- HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
+ ret = intel_de_wait_for_set_ms(display,
+ HDCP2_STATUS(display, cpu_transcoder, port),
+ LINK_ENCRYPTION_STATUS,
+ HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
dig_port->hdcp.auth_status = true;
return ret;
@@ -1966,11 +1964,10 @@ static int hdcp2_disable_encryption(struct intel_connector *connector)
intel_de_rmw(display, HDCP2_CTL(display, cpu_transcoder, port),
CTL_LINK_ENCRYPTION_REQ, 0);
- ret = intel_de_wait_for_clear(display,
- HDCP2_STATUS(display, cpu_transcoder,
- port),
- LINK_ENCRYPTION_STATUS,
- HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
+ ret = intel_de_wait_for_clear_ms(display,
+ HDCP2_STATUS(display, cpu_transcoder, port),
+ LINK_ENCRYPTION_STATUS,
+ HDCP_ENCRYPT_STATUS_CHANGE_TIMEOUT_MS);
if (ret == -ETIMEDOUT)
drm_dbg_kms(display->drm, "Disable Encryption Timedout");
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp_gsc.c b/drivers/gpu/drm/i915/display/intel_hdcp_gsc.c
index 6a22862d6be1..3e7b480ee9f1 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp_gsc.c
+++ b/drivers/gpu/drm/i915/display/intel_hdcp_gsc.c
@@ -3,13 +3,13 @@
* Copyright 2023, Intel Corporation.
*/
+#include <drm/drm_print.h>
#include <drm/intel/i915_hdcp_interface.h>
#include "gem/i915_gem_region.h"
#include "gt/intel_gt.h"
#include "gt/uc/intel_gsc_uc_heci_cmd_submit.h"
#include "i915_drv.h"
-#include "i915_utils.h"
#include "intel_hdcp_gsc.h"
struct intel_hdcp_gsc_context {
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 4ab7e2e3bfd4..908faf17f93d 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -45,7 +45,6 @@
#include <media/cec-notifier.h>
#include "g4x_hdmi.h"
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_audio.h"
#include "intel_connector.h"
@@ -55,6 +54,7 @@
#include "intel_display_driver.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_gmbus.h"
#include "intel_hdcp.h"
@@ -68,6 +68,20 @@
#include "intel_snps_phy.h"
#include "intel_vrr.h"
+bool intel_hdmi_is_frl(u32 clock)
+{
+ switch (clock) {
+ case 300000: /* 3 Gbps */
+ case 600000: /* 6 Gbps */
+ case 800000: /* 8 Gbps */
+ case 1000000: /* 10 Gbps */
+ case 1200000: /* 12 Gbps */
+ return true;
+ default:
+ return false;
+ }
+}
+
static void
assert_hdmi_port_disabled(struct intel_hdmi *intel_hdmi)
{
@@ -1584,8 +1598,8 @@ bool intel_hdmi_hdcp_check_link_once(struct intel_digital_port *dig_port,
intel_de_write(display, HDCP_RPRIME(display, cpu_transcoder, port), ri.reg);
/* Wait for Ri prime match */
- ret = intel_de_wait_for_set(display, HDCP_STATUS(display, cpu_transcoder, port),
- HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC, 1);
+ ret = intel_de_wait_for_set_ms(display, HDCP_STATUS(display, cpu_transcoder, port),
+ HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC, 1);
if (ret) {
drm_dbg_kms(display->drm, "Ri' mismatch detected (%x)\n",
intel_de_read(display, HDCP_STATUS(display, cpu_transcoder,
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
index dec2ad7dd8a2..be2fad57e4ad 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
@@ -60,6 +60,7 @@ int intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state,
int src_max_slices, int src_max_slice_width,
int hdmi_max_slices, int hdmi_throughput);
int intel_hdmi_dsc_get_slice_height(int vactive);
+bool intel_hdmi_is_frl(u32 clock);
void hsw_write_infoframe(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
diff --git a/drivers/gpu/drm/i915/display/intel_hotplug.c b/drivers/gpu/drm/i915/display/intel_hotplug.c
index 4451a792600a..235706229ffb 100644
--- a/drivers/gpu/drm/i915/display/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/display/intel_hotplug.c
@@ -24,16 +24,17 @@
#include <linux/debugfs.h>
#include <linux/kernel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "i915_drv.h"
#include "i915_irq.h"
-#include "i915_utils.h"
#include "intel_connector.h"
-#include "intel_display_power.h"
#include "intel_display_core.h"
+#include "intel_display_power.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_hdcp.h"
#include "intel_hotplug.h"
diff --git a/drivers/gpu/drm/i915/display/intel_hotplug_irq.c b/drivers/gpu/drm/i915/display/intel_hotplug_irq.c
index 4f72f3fb9af5..46c47b3d6f42 100644
--- a/drivers/gpu/drm/i915/display/intel_hotplug_irq.c
+++ b/drivers/gpu/drm/i915/display/intel_hotplug_irq.c
@@ -6,11 +6,11 @@
#include <drm/drm_print.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_de.h"
#include "intel_display_irq.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp_aux.h"
#include "intel_gmbus.h"
#include "intel_hotplug.h"
@@ -420,6 +420,9 @@ u32 i9xx_hpd_irq_ack(struct intel_display *display)
u32 hotplug_status = 0, hotplug_status_mask;
int i;
+ if (!HAS_HOTPLUG(display))
+ return 0;
+
if (display->platform.g4x ||
display->platform.valleyview || display->platform.cherryview)
hotplug_status_mask = HOTPLUG_INT_STATUS_G4X |
diff --git a/drivers/gpu/drm/i915/display/intel_link_bw.c b/drivers/gpu/drm/i915/display/intel_link_bw.c
index f52dee0ea412..d2862de894fa 100644
--- a/drivers/gpu/drm/i915/display/intel_link_bw.c
+++ b/drivers/gpu/drm/i915/display/intel_link_bw.c
@@ -20,6 +20,7 @@
#include "intel_dp_tunnel.h"
#include "intel_fdi.h"
#include "intel_link_bw.h"
+#include "intel_vdsc.h"
static int get_forced_link_bpp_x16(struct intel_atomic_state *state,
const struct intel_crtc *crtc)
@@ -55,7 +56,7 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
struct intel_display *display = to_intel_display(state);
enum pipe pipe;
- limits->force_fec_pipes = 0;
+ limits->link_dsc_pipes = 0;
limits->bpp_limit_reached_pipes = 0;
for_each_pipe(display, pipe) {
struct intel_crtc *crtc = intel_crtc_for_pipe(display, pipe);
@@ -65,8 +66,8 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
if (state->base.duplicated && crtc_state) {
limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16;
- if (crtc_state->fec_enable)
- limits->force_fec_pipes |= BIT(pipe);
+ if (intel_dsc_enabled_on_link(crtc_state))
+ limits->link_dsc_pipes |= BIT(pipe);
} else {
limits->max_bpp_x16[pipe] = INT_MAX;
}
@@ -265,10 +266,10 @@ assert_link_limit_change_valid(struct intel_display *display,
bool bpps_changed = false;
enum pipe pipe;
- /* FEC can't be forced off after it was forced on. */
+ /* DSC can't be disabled after it was enabled. */
if (drm_WARN_ON(display->drm,
- (old_limits->force_fec_pipes & new_limits->force_fec_pipes) !=
- old_limits->force_fec_pipes))
+ (old_limits->link_dsc_pipes & new_limits->link_dsc_pipes) !=
+ old_limits->link_dsc_pipes))
return false;
for_each_pipe(display, pipe) {
@@ -286,8 +287,8 @@ assert_link_limit_change_valid(struct intel_display *display,
/* At least one limit must change. */
if (drm_WARN_ON(display->drm,
!bpps_changed &&
- new_limits->force_fec_pipes ==
- old_limits->force_fec_pipes))
+ new_limits->link_dsc_pipes ==
+ old_limits->link_dsc_pipes))
return false;
return true;
diff --git a/drivers/gpu/drm/i915/display/intel_link_bw.h b/drivers/gpu/drm/i915/display/intel_link_bw.h
index 95ab7c50c61d..cb18e171037c 100644
--- a/drivers/gpu/drm/i915/display/intel_link_bw.h
+++ b/drivers/gpu/drm/i915/display/intel_link_bw.h
@@ -15,7 +15,7 @@ struct intel_connector;
struct intel_crtc_state;
struct intel_link_bw_limits {
- u8 force_fec_pipes;
+ u8 link_dsc_pipes;
u8 bpp_limit_reached_pipes;
/* in 1/16 bpp units */
int max_bpp_x16[I915_MAX_PIPES];
diff --git a/drivers/gpu/drm/i915/display/intel_lspcon.c b/drivers/gpu/drm/i915/display/intel_lspcon.c
index d56026c4efdd..9ceabbc981a1 100644
--- a/drivers/gpu/drm/i915/display/intel_lspcon.c
+++ b/drivers/gpu/drm/i915/display/intel_lspcon.c
@@ -31,10 +31,10 @@
#include <drm/drm_edid.h>
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_hdmi.h"
#include "intel_lspcon.h"
diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c
new file mode 100644
index 000000000000..a67eb4f7f897
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c
@@ -0,0 +1,2327 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <drm/drm_print.h>
+
+#include "i915_reg.h"
+#include "intel_cx0_phy.h"
+#include "intel_cx0_phy_regs.h"
+#include "intel_ddi.h"
+#include "intel_ddi_buf_trans.h"
+#include "intel_de.h"
+#include "intel_display.h"
+#include "intel_display_types.h"
+#include "intel_display_utils.h"
+#include "intel_dpll_mgr.h"
+#include "intel_hdmi.h"
+#include "intel_lt_phy.h"
+#include "intel_lt_phy_regs.h"
+#include "intel_panel.h"
+#include "intel_psr.h"
+#include "intel_tc.h"
+
+#define for_each_lt_phy_lane_in_mask(__lane_mask, __lane) \
+ for ((__lane) = 0; (__lane) < 2; (__lane)++) \
+ for_each_if((__lane_mask) & BIT(__lane))
+
+#define INTEL_LT_PHY_LANE0 BIT(0)
+#define INTEL_LT_PHY_LANE1 BIT(1)
+#define INTEL_LT_PHY_BOTH_LANES (INTEL_LT_PHY_LANE1 |\
+ INTEL_LT_PHY_LANE0)
+#define MODE_DP 3
+#define Q32_TO_INT(x) ((x) >> 32)
+#define Q32_TO_FRAC(x) ((x) & 0xFFFFFFFF)
+#define DCO_MIN_FREQ_MHZ 11850
+#define REF_CLK_KHZ 38400
+#define TDC_RES_MULTIPLIER 10000000ULL
+
+struct phy_param_t {
+ u32 val;
+ u32 addr;
+};
+
+struct lt_phy_params {
+ struct phy_param_t pll_reg4;
+ struct phy_param_t pll_reg3;
+ struct phy_param_t pll_reg5;
+ struct phy_param_t pll_reg57;
+ struct phy_param_t lf;
+ struct phy_param_t tdc;
+ struct phy_param_t ssc;
+ struct phy_param_t bias2;
+ struct phy_param_t bias_trim;
+ struct phy_param_t dco_med;
+ struct phy_param_t dco_fine;
+ struct phy_param_t ssc_inj;
+ struct phy_param_t surv_bonus;
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_rbr = {
+ .clock = 162000,
+ .config = {
+ 0x83,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x5, 0xa, 0x2a, 0x20 },
+ { 0x80, 0x0, 0x0, 0x0 },
+ { 0x4, 0x4, 0x82, 0x28 },
+ { 0xfa, 0x16, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x5, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x4b, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0a },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr1 = {
+ .clock = 270000,
+ .config = {
+ 0x8b,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x3, 0xca, 0x34, 0xa0 },
+ { 0xe0, 0x0, 0x0, 0x0 },
+ { 0x5, 0x4, 0x81, 0xad },
+ { 0xfa, 0x11, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x7, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x43, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0d },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr2 = {
+ .clock = 540000,
+ .config = {
+ 0x93,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x1, 0x4d, 0x34, 0xa0 },
+ { 0xe0, 0x0, 0x0, 0x0 },
+ { 0xa, 0x4, 0x81, 0xda },
+ { 0xfa, 0x11, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x7, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x43, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0d },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_hbr3 = {
+ .clock = 810000,
+ .config = {
+ 0x9b,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x1, 0x4a, 0x34, 0xa0 },
+ { 0xe0, 0x0, 0x0, 0x0 },
+ { 0x5, 0x4, 0x80, 0xa8 },
+ { 0xfa, 0x11, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x7, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x43, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0d },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr10 = {
+ .clock = 1000000,
+ .config = {
+ 0x43,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x85,
+ 0x85,
+ 0x85,
+ 0x85,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x1, 0xa, 0x20, 0x80 },
+ { 0x6a, 0xaa, 0xaa, 0xab },
+ { 0x0, 0x3, 0x4, 0x94 },
+ { 0xfa, 0x1c, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x4, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x45, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x14, 0x2a, 0x14 },
+ { 0x0, 0x5b, 0xe0, 0x8 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr13_5 = {
+ .clock = 1350000,
+ .config = {
+ 0xcb,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x2, 0x9, 0x2b, 0xe0 },
+ { 0x90, 0x0, 0x0, 0x0 },
+ { 0x8, 0x4, 0x80, 0xe0 },
+ { 0xfa, 0x15, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x6, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x49, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x57, 0xe0, 0x0c },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_dp_uhbr20 = {
+ .clock = 2000000,
+ .config = {
+ 0x53,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x85,
+ 0x85,
+ 0x85,
+ 0x85,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ 0x86,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x1, 0xa, 0x20, 0x80 },
+ { 0x6a, 0xaa, 0xaa, 0xab },
+ { 0x0, 0x3, 0x4, 0x94 },
+ { 0xfa, 0x1c, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x4, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x45, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x14, 0x2a, 0x14 },
+ { 0x0, 0x5b, 0xe0, 0x8 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state * const xe3plpd_lt_dp_tables[] = {
+ &xe3plpd_lt_dp_rbr,
+ &xe3plpd_lt_dp_hbr1,
+ &xe3plpd_lt_dp_hbr2,
+ &xe3plpd_lt_dp_hbr3,
+ &xe3plpd_lt_dp_uhbr10,
+ &xe3plpd_lt_dp_uhbr13_5,
+ &xe3plpd_lt_dp_uhbr20,
+ NULL,
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_2_16 = {
+ .clock = 216000,
+ .config = {
+ 0xa3,
+ 0x2d,
+ 0x1,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x3, 0xca, 0x2a, 0x20 },
+ { 0x80, 0x0, 0x0, 0x0 },
+ { 0x6, 0x4, 0x81, 0xbc },
+ { 0xfa, 0x16, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x5, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x4b, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0a },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_2_43 = {
+ .clock = 243000,
+ .config = {
+ 0xab,
+ 0x2d,
+ 0x1,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x3, 0xca, 0x2f, 0x60 },
+ { 0xb0, 0x0, 0x0, 0x0 },
+ { 0x6, 0x4, 0x81, 0xbc },
+ { 0xfa, 0x13, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x6, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x47, 0x48, 0x0, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0c },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_3_24 = {
+ .clock = 324000,
+ .config = {
+ 0xb3,
+ 0x2d,
+ 0x1,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x2, 0x8a, 0x2a, 0x20 },
+ { 0x80, 0x0, 0x0, 0x0 },
+ { 0x6, 0x4, 0x81, 0x28 },
+ { 0xfa, 0x16, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x5, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x4b, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0a },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_4_32 = {
+ .clock = 432000,
+ .config = {
+ 0xbb,
+ 0x2d,
+ 0x1,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x1, 0x4d, 0x2a, 0x20 },
+ { 0x80, 0x0, 0x0, 0x0 },
+ { 0xc, 0x4, 0x81, 0xbc },
+ { 0xfa, 0x16, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x5, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x4b, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x5b, 0xe0, 0x0a },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_edp_6_75 = {
+ .clock = 675000,
+ .config = {
+ 0xdb,
+ 0x2d,
+ 0x1,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x1, 0x4a, 0x2b, 0xe0 },
+ { 0x90, 0x0, 0x0, 0x0 },
+ { 0x6, 0x4, 0x80, 0xa8 },
+ { 0xfa, 0x15, 0x83, 0x11 },
+ { 0x80, 0x0f, 0xf9, 0x53 },
+ { 0x84, 0x26, 0x6, 0x4 },
+ { 0x0, 0xe0, 0x1, 0x0 },
+ { 0x49, 0x48, 0x0, 0x0 },
+ { 0x27, 0x8, 0x0, 0x0 },
+ { 0x5a, 0x13, 0x29, 0x13 },
+ { 0x0, 0x57, 0xe0, 0x0c },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state * const xe3plpd_lt_edp_tables[] = {
+ &xe3plpd_lt_dp_rbr,
+ &xe3plpd_lt_edp_2_16,
+ &xe3plpd_lt_edp_2_43,
+ &xe3plpd_lt_dp_hbr1,
+ &xe3plpd_lt_edp_3_24,
+ &xe3plpd_lt_edp_4_32,
+ &xe3plpd_lt_dp_hbr2,
+ &xe3plpd_lt_edp_6_75,
+ &xe3plpd_lt_dp_hbr3,
+ NULL,
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_252 = {
+ .clock = 25200,
+ .config = {
+ 0x84,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x0c, 0x15, 0x27, 0x60 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ { 0x8, 0x4, 0x98, 0x28 },
+ { 0x42, 0x0, 0x84, 0x10 },
+ { 0x80, 0x0f, 0xd9, 0xb5 },
+ { 0x86, 0x0, 0x0, 0x0 },
+ { 0x1, 0xa0, 0x1, 0x0 },
+ { 0x4b, 0x0, 0x0, 0x0 },
+ { 0x28, 0x0, 0x0, 0x0 },
+ { 0x0, 0x14, 0x2a, 0x14 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_272 = {
+ .clock = 27200,
+ .config = {
+ 0x84,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x0b, 0x15, 0x26, 0xa0 },
+ { 0x60, 0x0, 0x0, 0x0 },
+ { 0x8, 0x4, 0x96, 0x28 },
+ { 0xfa, 0x0c, 0x84, 0x11 },
+ { 0x80, 0x0f, 0xd9, 0x53 },
+ { 0x86, 0x0, 0x0, 0x0 },
+ { 0x1, 0xa0, 0x1, 0x0 },
+ { 0x4b, 0x0, 0x0, 0x0 },
+ { 0x28, 0x0, 0x0, 0x0 },
+ { 0x0, 0x14, 0x2a, 0x14 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_742p5 = {
+ .clock = 74250,
+ .config = {
+ 0x84,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x4, 0x15, 0x26, 0xa0 },
+ { 0x60, 0x0, 0x0, 0x0 },
+ { 0x8, 0x4, 0x88, 0x28 },
+ { 0xfa, 0x0c, 0x84, 0x11 },
+ { 0x80, 0x0f, 0xd9, 0x53 },
+ { 0x86, 0x0, 0x0, 0x0 },
+ { 0x1, 0xa0, 0x1, 0x0 },
+ { 0x4b, 0x0, 0x0, 0x0 },
+ { 0x28, 0x0, 0x0, 0x0 },
+ { 0x0, 0x14, 0x2a, 0x14 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_1p485 = {
+ .clock = 148500,
+ .config = {
+ 0x84,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x2, 0x15, 0x26, 0xa0 },
+ { 0x60, 0x0, 0x0, 0x0 },
+ { 0x8, 0x4, 0x84, 0x28 },
+ { 0xfa, 0x0c, 0x84, 0x11 },
+ { 0x80, 0x0f, 0xd9, 0x53 },
+ { 0x86, 0x0, 0x0, 0x0 },
+ { 0x1, 0xa0, 0x1, 0x0 },
+ { 0x4b, 0x0, 0x0, 0x0 },
+ { 0x28, 0x0, 0x0, 0x0 },
+ { 0x0, 0x14, 0x2a, 0x14 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state xe3plpd_lt_hdmi_5p94 = {
+ .clock = 594000,
+ .config = {
+ 0x84,
+ 0x2d,
+ 0x0,
+ },
+ .addr_msb = {
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x87,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ 0x88,
+ },
+ .addr_lsb = {
+ 0x10,
+ 0x0c,
+ 0x14,
+ 0xe4,
+ 0x0c,
+ 0x10,
+ 0x14,
+ 0x18,
+ 0x48,
+ 0x40,
+ 0x4c,
+ 0x24,
+ 0x44,
+ },
+ .data = {
+ { 0x0, 0x4c, 0x2, 0x0 },
+ { 0x0, 0x95, 0x26, 0xa0 },
+ { 0x60, 0x0, 0x0, 0x0 },
+ { 0x8, 0x4, 0x81, 0x28 },
+ { 0xfa, 0x0c, 0x84, 0x11 },
+ { 0x80, 0x0f, 0xd9, 0x53 },
+ { 0x86, 0x0, 0x0, 0x0 },
+ { 0x1, 0xa0, 0x1, 0x0 },
+ { 0x4b, 0x0, 0x0, 0x0 },
+ { 0x28, 0x0, 0x0, 0x0 },
+ { 0x0, 0x14, 0x2a, 0x14 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ { 0x0, 0x0, 0x0, 0x0 },
+ },
+};
+
+static const struct intel_lt_phy_pll_state * const xe3plpd_lt_hdmi_tables[] = {
+ &xe3plpd_lt_hdmi_252,
+ &xe3plpd_lt_hdmi_272,
+ &xe3plpd_lt_hdmi_742p5,
+ &xe3plpd_lt_hdmi_1p485,
+ &xe3plpd_lt_hdmi_5p94,
+ NULL,
+};
+
+static u8 intel_lt_phy_get_owned_lane_mask(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ if (!intel_tc_port_in_dp_alt_mode(dig_port))
+ return INTEL_LT_PHY_BOTH_LANES;
+
+ return intel_tc_port_max_lane_count(dig_port) > 2
+ ? INTEL_LT_PHY_BOTH_LANES : INTEL_LT_PHY_LANE0;
+}
+
+static u8 intel_lt_phy_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr)
+{
+ return intel_cx0_read(encoder, lane_mask, addr);
+}
+
+static void intel_lt_phy_write(struct intel_encoder *encoder,
+ u8 lane_mask, u16 addr, u8 data, bool committed)
+{
+ intel_cx0_write(encoder, lane_mask, addr, data, committed);
+}
+
+static void intel_lt_phy_rmw(struct intel_encoder *encoder,
+ u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed)
+{
+ intel_cx0_rmw(encoder, lane_mask, addr, clear, set, committed);
+}
+
+static void intel_lt_phy_clear_status_p2p(struct intel_encoder *encoder,
+ int lane)
+{
+ struct intel_display *display = to_intel_display(encoder);
+
+ intel_de_rmw(display,
+ XE3PLPD_PORT_P2M_MSGBUS_STATUS_P2P(encoder->port, lane),
+ XELPDP_PORT_P2M_RESPONSE_READY, 0);
+}
+
+static void
+assert_dc_off(struct intel_display *display)
+{
+ bool enabled;
+
+ enabled = intel_display_power_is_enabled(display, POWER_DOMAIN_DC_OFF);
+ drm_WARN_ON(display->drm, !enabled);
+}
+
+static int __intel_lt_phy_p2p_write_once(struct intel_encoder *encoder,
+ int lane, u16 addr, u8 data,
+ i915_reg_t mac_reg_addr,
+ u8 expected_mac_val)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ enum port port = encoder->port;
+ enum phy phy = intel_encoder_to_phy(encoder);
+ int ack;
+ u32 val;
+
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
+ XELPDP_PORT_P2P_TRANSACTION_PENDING,
+ XELPDP_MSGBUS_TIMEOUT_MS)) {
+ drm_dbg_kms(display->drm,
+ "PHY %c Timeout waiting for previous transaction to complete. Resetting bus.\n",
+ phy_name(phy));
+ intel_cx0_bus_reset(encoder, lane);
+ return -ETIMEDOUT;
+ }
+
+ intel_de_rmw(display, XELPDP_PORT_P2M_MSGBUS_STATUS(display, port, lane), 0, 0);
+
+ intel_de_write(display, XELPDP_PORT_M2P_MSGBUS_CTL(display, port, lane),
+ XELPDP_PORT_P2P_TRANSACTION_PENDING |
+ XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED |
+ XELPDP_PORT_M2P_DATA(data) |
+ XELPDP_PORT_M2P_ADDRESS(addr));
+
+ ack = intel_cx0_wait_for_ack(encoder, XELPDP_PORT_P2M_COMMAND_WRITE_ACK, lane, &val);
+ if (ack < 0)
+ return ack;
+
+ if (val & XELPDP_PORT_P2M_ERROR_SET) {
+ drm_dbg_kms(display->drm,
+ "PHY %c Error occurred during P2P write command. Status: 0x%x\n",
+ phy_name(phy), val);
+ intel_lt_phy_clear_status_p2p(encoder, lane);
+ intel_cx0_bus_reset(encoder, lane);
+ return -EINVAL;
+ }
+
+ /*
+ * RE-VISIT:
+ * This needs to be added to give PHY time to set everything up this was a requirement
+ * to get the display up and running
+ * This is the time PHY takes to settle down after programming the PHY.
+ */
+ udelay(150);
+ intel_clear_response_ready_flag(encoder, lane);
+ intel_lt_phy_clear_status_p2p(encoder, lane);
+
+ return 0;
+}
+
+static void __intel_lt_phy_p2p_write(struct intel_encoder *encoder,
+ int lane, u16 addr, u8 data,
+ i915_reg_t mac_reg_addr,
+ u8 expected_mac_val)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ enum phy phy = intel_encoder_to_phy(encoder);
+ int i, status;
+
+ assert_dc_off(display);
+
+ /* 3 tries is assumed to be enough to write successfully */
+ for (i = 0; i < 3; i++) {
+ status = __intel_lt_phy_p2p_write_once(encoder, lane, addr, data, mac_reg_addr,
+ expected_mac_val);
+
+ if (status == 0)
+ return;
+ }
+
+ drm_err_once(display->drm,
+ "PHY %c P2P Write %04x failed after %d retries.\n", phy_name(phy), addr, i);
+}
+
+static void intel_lt_phy_p2p_write(struct intel_encoder *encoder,
+ u8 lane_mask, u16 addr, u8 data,
+ i915_reg_t mac_reg_addr,
+ u8 expected_mac_val)
+{
+ int lane;
+
+ for_each_lt_phy_lane_in_mask(lane_mask, lane)
+ __intel_lt_phy_p2p_write(encoder, lane, addr, data, mac_reg_addr, expected_mac_val);
+}
+
+static void
+intel_lt_phy_setup_powerdown(struct intel_encoder *encoder, u8 lane_count)
+{
+ /*
+ * The new PORT_BUF_CTL6 stuff for dc5 entry and exit needs to be handled
+ * by dmc firmware not explicitly mentioned in Bspec. This leaves this
+ * function as a wrapper only but keeping it expecting future changes.
+ */
+ intel_cx0_setup_powerdown(encoder);
+}
+
+static void
+intel_lt_phy_powerdown_change_sequence(struct intel_encoder *encoder,
+ u8 lane_mask, u8 state)
+{
+ intel_cx0_powerdown_change_sequence(encoder, lane_mask, state);
+}
+
+static void
+intel_lt_phy_lane_reset(struct intel_encoder *encoder,
+ u8 lane_count)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ enum port port = encoder->port;
+ enum phy phy = intel_encoder_to_phy(encoder);
+ u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
+ u32 lane_pipe_reset = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? XELPDP_LANE_PIPE_RESET(0) | XELPDP_LANE_PIPE_RESET(1)
+ : XELPDP_LANE_PIPE_RESET(0);
+ u32 lane_phy_current_status = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XELPDP_LANE_PHY_CURRENT_STATUS(0) |
+ XELPDP_LANE_PHY_CURRENT_STATUS(1))
+ : XELPDP_LANE_PHY_CURRENT_STATUS(0);
+ u32 lane_phy_pulse_status = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XE3PLPDP_LANE_PHY_PULSE_STATUS(0) |
+ XE3PLPDP_LANE_PHY_PULSE_STATUS(1))
+ : XE3PLPDP_LANE_PHY_PULSE_STATUS(0);
+
+ intel_de_rmw(display, XE3PLPD_PORT_BUF_CTL5(port),
+ XE3PLPD_MACCLK_RATE_MASK, XE3PLPD_MACCLK_RATE_DEF);
+
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL1(display, port),
+ XE3PLPDP_PHY_MODE_MASK, XE3PLPDP_PHY_MODE_DP);
+
+ intel_lt_phy_setup_powerdown(encoder, lane_count);
+ intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask,
+ XELPDP_P2_STATE_RESET);
+
+ intel_de_rmw(display, XE3PLPD_PORT_BUF_CTL5(port),
+ XE3PLPD_MACCLK_RESET_0, 0);
+
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0));
+
+ if (intel_de_wait_for_set_ms(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_ACK(0),
+ XE3PLPD_MACCLK_TURNON_LATENCY_MS))
+ drm_warn(display->drm, "PHY %c PLL MacCLK assertion ack not done\n",
+ phy_name(phy));
+
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_FORWARD_CLOCK_UNGATE,
+ XELPDP_FORWARD_CLOCK_UNGATE);
+
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_pipe_reset | lane_phy_pulse_status, 0);
+
+ if (intel_de_wait_for_clear_ms(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_current_status,
+ XE3PLPD_RESET_END_LATENCY_MS))
+ drm_warn(display->drm, "PHY %c failed to bring out of lane reset\n",
+ phy_name(phy));
+
+ if (intel_de_wait_for_set_ms(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_pulse_status,
+ XE3PLPD_RATE_CALIB_DONE_LATENCY_MS))
+ drm_warn(display->drm, "PHY %c PLL rate not changed\n",
+ phy_name(phy));
+
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_phy_pulse_status, 0);
+}
+
+static void
+intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state,
+ bool lane_reversal)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ u32 val = 0;
+
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL1(display, encoder->port),
+ XELPDP_PORT_REVERSAL,
+ lane_reversal ? XELPDP_PORT_REVERSAL : 0);
+
+ val |= XELPDP_FORWARD_CLOCK_UNGATE;
+
+ /*
+ * We actually mean MACCLK here and not MAXPCLK when using LT Phy
+ * but since the register bits still remain the same we use
+ * the same definition
+ */
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI) &&
+ intel_hdmi_is_frl(crtc_state->port_clock))
+ val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_DIV18CLK);
+ else
+ val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK);
+
+ /* DP2.0 10G and 20G rates enable MPLLA*/
+ if (crtc_state->port_clock == 1000000 || crtc_state->port_clock == 2000000)
+ val |= XELPDP_SSC_ENABLE_PLLA;
+ else
+ val |= crtc_state->dpll_hw_state.ltpll.ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0;
+
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port),
+ XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE |
+ XELPDP_DDI_CLOCK_SELECT_MASK(display) | XELPDP_SSC_ENABLE_PLLA |
+ XELPDP_SSC_ENABLE_PLLB, val);
+}
+
+static u32 intel_lt_phy_get_dp_clock(u8 rate)
+{
+ switch (rate) {
+ case 0:
+ return 162000;
+ case 1:
+ return 270000;
+ case 2:
+ return 540000;
+ case 3:
+ return 810000;
+ case 4:
+ return 216000;
+ case 5:
+ return 243000;
+ case 6:
+ return 324000;
+ case 7:
+ return 432000;
+ case 8:
+ return 1000000;
+ case 9:
+ return 1350000;
+ case 10:
+ return 2000000;
+ case 11:
+ return 675000;
+ default:
+ MISSING_CASE(rate);
+ return 0;
+ }
+}
+
+static bool
+intel_lt_phy_config_changed(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ u8 val, rate;
+ u32 clock;
+
+ val = intel_lt_phy_read(encoder, INTEL_LT_PHY_LANE0,
+ LT_PHY_VDR_0_CONFIG);
+ rate = REG_FIELD_GET8(LT_PHY_VDR_RATE_ENCODING_MASK, val);
+
+ /*
+ * The only time we do not reconfigure the PLL is when we are
+ * using 1.62 Gbps clock since PHY PLL defaults to that
+ * otherwise we always need to reconfigure it.
+ */
+ if (intel_crtc_has_dp_encoder(crtc_state)) {
+ clock = intel_lt_phy_get_dp_clock(rate);
+ if (crtc_state->port_clock == 1620000 && crtc_state->port_clock == clock)
+ return false;
+ }
+
+ return true;
+}
+
+static intel_wakeref_t intel_lt_phy_transaction_begin(struct intel_encoder *encoder)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ intel_wakeref_t wakeref;
+
+ intel_psr_pause(intel_dp);
+ wakeref = intel_display_power_get(display, POWER_DOMAIN_DC_OFF);
+
+ return wakeref;
+}
+
+static void intel_lt_phy_transaction_end(struct intel_encoder *encoder, intel_wakeref_t wakeref)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ intel_psr_resume(intel_dp);
+ intel_display_power_put(display, POWER_DOMAIN_DC_OFF, wakeref);
+}
+
+static const struct intel_lt_phy_pll_state * const *
+intel_lt_phy_pll_tables_get(struct intel_crtc_state *crtc_state,
+ struct intel_encoder *encoder)
+{
+ if (intel_crtc_has_dp_encoder(crtc_state)) {
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
+ return xe3plpd_lt_edp_tables;
+
+ return xe3plpd_lt_dp_tables;
+ } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) {
+ return xe3plpd_lt_hdmi_tables;
+ }
+
+ MISSING_CASE(encoder->type);
+ return NULL;
+}
+
+static bool
+intel_lt_phy_pll_is_ssc_enabled(struct intel_crtc_state *crtc_state,
+ struct intel_encoder *encoder)
+{
+ struct intel_display *display = to_intel_display(encoder);
+
+ if (intel_crtc_has_dp_encoder(crtc_state)) {
+ if (intel_panel_use_ssc(display)) {
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ return (intel_dp->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
+ }
+ }
+
+ return false;
+}
+
+static u64 mul_q32_u32(u64 a_q32, u32 b)
+{
+ u64 p0, p1, carry, result;
+ u64 x_hi = a_q32 >> 32;
+ u64 x_lo = a_q32 & 0xFFFFFFFFULL;
+
+ p0 = x_lo * (u64)b;
+ p1 = x_hi * (u64)b;
+ carry = p0 >> 32;
+ result = (p1 << 32) + (carry << 32) + (p0 & 0xFFFFFFFFULL);
+
+ return result;
+}
+
+static bool
+calculate_target_dco_and_loop_cnt(u32 frequency_khz, u64 *target_dco_mhz, u32 *loop_cnt)
+{
+ u32 ppm_value = 1;
+ u32 dco_min_freq = DCO_MIN_FREQ_MHZ;
+ u32 dco_max_freq = 16200;
+ u32 dco_min_freq_low = 10000;
+ u32 dco_max_freq_low = 12000;
+ u64 val = 0;
+ u64 refclk_khz = REF_CLK_KHZ;
+ u64 m2div = 0;
+ u64 val_with_frac = 0;
+ u64 ppm = 0;
+ u64 temp0 = 0, temp1, scale;
+ int ppm_cnt, dco_count, y;
+
+ for (ppm_cnt = 0; ppm_cnt < 5; ppm_cnt++) {
+ ppm_value = ppm_cnt == 2 ? 2 : 1;
+ for (dco_count = 0; dco_count < 2; dco_count++) {
+ if (dco_count == 1) {
+ dco_min_freq = dco_min_freq_low;
+ dco_max_freq = dco_max_freq_low;
+ }
+ for (y = 2; y <= 255; y += 2) {
+ val = div64_u64((u64)y * frequency_khz, 200);
+ m2div = div64_u64(((u64)(val) << 32), refclk_khz);
+ m2div = mul_q32_u32(m2div, 500);
+ val_with_frac = mul_q32_u32(m2div, refclk_khz);
+ val_with_frac = div64_u64(val_with_frac, 500);
+ temp1 = Q32_TO_INT(val_with_frac);
+ temp0 = (temp1 > val) ? (temp1 - val) :
+ (val - temp1);
+ ppm = div64_u64(temp0, val);
+ if (temp1 >= dco_min_freq &&
+ temp1 <= dco_max_freq &&
+ ppm < ppm_value) {
+ /* Round to two places */
+ scale = (1ULL << 32) / 100;
+ temp0 = DIV_ROUND_UP_ULL(val_with_frac,
+ scale);
+ *target_dco_mhz = temp0 * scale;
+ *loop_cnt = y;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static void set_phy_vdr_addresses(struct lt_phy_params *p, int pll_type)
+{
+ p->pll_reg4.addr = PLL_REG_ADDR(PLL_REG4_ADDR, pll_type);
+ p->pll_reg3.addr = PLL_REG_ADDR(PLL_REG3_ADDR, pll_type);
+ p->pll_reg5.addr = PLL_REG_ADDR(PLL_REG5_ADDR, pll_type);
+ p->pll_reg57.addr = PLL_REG_ADDR(PLL_REG57_ADDR, pll_type);
+ p->lf.addr = PLL_REG_ADDR(PLL_LF_ADDR, pll_type);
+ p->tdc.addr = PLL_REG_ADDR(PLL_TDC_ADDR, pll_type);
+ p->ssc.addr = PLL_REG_ADDR(PLL_SSC_ADDR, pll_type);
+ p->bias2.addr = PLL_REG_ADDR(PLL_BIAS2_ADDR, pll_type);
+ p->bias_trim.addr = PLL_REG_ADDR(PLL_BIAS_TRIM_ADDR, pll_type);
+ p->dco_med.addr = PLL_REG_ADDR(PLL_DCO_MED_ADDR, pll_type);
+ p->dco_fine.addr = PLL_REG_ADDR(PLL_DCO_FINE_ADDR, pll_type);
+ p->ssc_inj.addr = PLL_REG_ADDR(PLL_SSC_INJ_ADDR, pll_type);
+ p->surv_bonus.addr = PLL_REG_ADDR(PLL_SURV_BONUS_ADDR, pll_type);
+}
+
+static void compute_ssc(struct lt_phy_params *p, u32 ana_cfg)
+{
+ int ssc_stepsize = 0;
+ int ssc_steplen = 0;
+ int ssc_steplog = 0;
+
+ p->ssc.val = (1 << 31) | (ana_cfg << 24) | (ssc_steplog << 16) |
+ (ssc_stepsize << 8) | ssc_steplen;
+}
+
+static void compute_bias2(struct lt_phy_params *p)
+{
+ u32 ssc_en_local = 0;
+ u64 dynctrl_ovrd_en = 0;
+
+ p->bias2.val = (dynctrl_ovrd_en << 31) | (ssc_en_local << 30) |
+ (1 << 23) | (1 << 24) | (32 << 16) | (1 << 8);
+}
+
+static void compute_tdc(struct lt_phy_params *p, u64 tdc_fine)
+{
+ u32 settling_time = 15;
+ u32 bias_ovr_en = 1;
+ u32 coldstart = 1;
+ u32 true_lock = 2;
+ u32 early_lock = 1;
+ u32 lock_ovr_en = 1;
+ u32 lock_thr = tdc_fine ? 3 : 5;
+ u32 unlock_thr = tdc_fine ? 5 : 11;
+
+ p->tdc.val = (u32)((2 << 30) + (settling_time << 16) + (bias_ovr_en << 15) +
+ (lock_ovr_en << 14) + (coldstart << 12) + (true_lock << 10) +
+ (early_lock << 8) + (unlock_thr << 4) + lock_thr);
+}
+
+static void compute_dco_med(struct lt_phy_params *p)
+{
+ u32 cselmed_en = 0;
+ u32 cselmed_dyn_adj = 0;
+ u32 cselmed_ratio = 39;
+ u32 cselmed_thr = 8;
+
+ p->dco_med.val = (cselmed_en << 31) + (cselmed_dyn_adj << 30) +
+ (cselmed_ratio << 24) + (cselmed_thr << 21);
+}
+
+static void compute_dco_fine(struct lt_phy_params *p, u32 dco_12g)
+{
+ u32 dco_fine0_tune_2_0 = 0;
+ u32 dco_fine1_tune_2_0 = 0;
+ u32 dco_fine2_tune_2_0 = 0;
+ u32 dco_fine3_tune_2_0 = 0;
+ u32 dco_dith0_tune_2_0 = 0;
+ u32 dco_dith1_tune_2_0 = 0;
+
+ dco_fine0_tune_2_0 = dco_12g ? 4 : 3;
+ dco_fine1_tune_2_0 = 2;
+ dco_fine2_tune_2_0 = dco_12g ? 2 : 1;
+ dco_fine3_tune_2_0 = 5;
+ dco_dith0_tune_2_0 = dco_12g ? 4 : 3;
+ dco_dith1_tune_2_0 = 2;
+
+ p->dco_fine.val = (dco_dith1_tune_2_0 << 19) +
+ (dco_dith0_tune_2_0 << 16) +
+ (dco_fine3_tune_2_0 << 11) +
+ (dco_fine2_tune_2_0 << 8) +
+ (dco_fine1_tune_2_0 << 3) +
+ dco_fine0_tune_2_0;
+}
+
+int
+intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state,
+ u32 frequency_khz)
+{
+#define DATA_ASSIGN(i, pll_reg) \
+ do { \
+ lt_state->data[i][0] = (u8)((((pll_reg).val) & 0xFF000000) >> 24); \
+ lt_state->data[i][1] = (u8)((((pll_reg).val) & 0x00FF0000) >> 16); \
+ lt_state->data[i][2] = (u8)((((pll_reg).val) & 0x0000FF00) >> 8); \
+ lt_state->data[i][3] = (u8)((((pll_reg).val) & 0x000000FF)); \
+ } while (0)
+#define ADDR_ASSIGN(i, pll_reg) \
+ do { \
+ lt_state->addr_msb[i] = ((pll_reg).addr >> 8) & 0xFF; \
+ lt_state->addr_lsb[i] = (pll_reg).addr & 0xFF; \
+ } while (0)
+
+ bool found = false;
+ struct lt_phy_params p;
+ u32 dco_fmin = DCO_MIN_FREQ_MHZ;
+ u64 refclk_khz = REF_CLK_KHZ;
+ u32 refclk_mhz_int = REF_CLK_KHZ / 1000;
+ u64 m2div = 0;
+ u64 target_dco_mhz = 0;
+ u64 tdc_fine, tdc_targetcnt;
+ u64 feedfwd_gain ,feedfwd_cal_en;
+ u64 tdc_res = 30;
+ u32 prop_coeff;
+ u32 int_coeff;
+ u32 ndiv = 1;
+ u32 m1div = 1, m2div_int, m2div_frac;
+ u32 frac_en;
+ u32 ana_cfg;
+ u32 loop_cnt = 0;
+ u32 gain_ctrl = 2;
+ u32 postdiv = 0;
+ u32 dco_12g = 0;
+ u32 pll_type = 0;
+ u32 d1 = 2, d3 = 5, d4 = 0, d5 = 0;
+ u32 d6 = 0, d6_new = 0;
+ u32 d7, d8 = 0;
+ u32 bonus_7_0 = 0;
+ u32 csel2fo = 11;
+ u32 csel2fo_ovrd_en = 1;
+ u64 temp0, temp1, temp2, temp3;
+
+ p.surv_bonus.val = (bonus_7_0 << 16);
+ p.pll_reg4.val = (refclk_mhz_int << 17) +
+ (ndiv << 9) + (1 << 4);
+ p.bias_trim.val = (csel2fo_ovrd_en << 30) + (csel2fo << 24);
+ p.ssc_inj.val = 0;
+ found = calculate_target_dco_and_loop_cnt(frequency_khz, &target_dco_mhz, &loop_cnt);
+ if (!found)
+ return -EINVAL;
+
+ m2div = div64_u64(target_dco_mhz, (refclk_khz * ndiv * m1div));
+ m2div = mul_q32_u32(m2div, 1000);
+ if (Q32_TO_INT(m2div) > 511)
+ return -EINVAL;
+
+ m2div_int = (u32)Q32_TO_INT(m2div);
+ m2div_frac = (u32)(Q32_TO_FRAC(m2div));
+ frac_en = (m2div_frac > 0) ? 1 : 0;
+
+ if (frac_en > 0)
+ tdc_res = 70;
+ else
+ tdc_res = 36;
+ tdc_fine = tdc_res > 50 ? 1 : 0;
+ temp0 = tdc_res * 40 * 11;
+ temp1 = div64_u64(((4 * TDC_RES_MULTIPLIER) + temp0) * 500, temp0 * refclk_khz);
+ temp2 = div64_u64(temp0 * refclk_khz, 1000);
+ temp3 = div64_u64(((8 * TDC_RES_MULTIPLIER) + temp2), temp2);
+ tdc_targetcnt = tdc_res < 50 ? (int)(temp1) : (int)(temp3);
+ tdc_targetcnt = (int)(tdc_targetcnt / 2);
+ temp0 = mul_q32_u32(target_dco_mhz, tdc_res);
+ temp0 >>= 32;
+ feedfwd_gain = (m2div_frac > 0) ? div64_u64(m1div * TDC_RES_MULTIPLIER, temp0) : 0;
+ feedfwd_cal_en = frac_en;
+
+ temp0 = (u32)Q32_TO_INT(target_dco_mhz);
+ prop_coeff = (temp0 >= dco_fmin) ? 3 : 4;
+ int_coeff = (temp0 >= dco_fmin) ? 7 : 8;
+ ana_cfg = (temp0 >= dco_fmin) ? 8 : 6;
+ dco_12g = (temp0 >= dco_fmin) ? 0 : 1;
+
+ if (temp0 > 12960)
+ d7 = 10;
+ else
+ d7 = 8;
+
+ d8 = loop_cnt / 2;
+ d4 = d8 * 2;
+
+ /* Compute pll_reg3,5,57 & lf */
+ p.pll_reg3.val = (u32)((d4 << 21) + (d3 << 18) + (d1 << 15) + (m2div_int << 5));
+ p.pll_reg5.val = m2div_frac;
+ postdiv = (d5 == 0) ? 9 : d5;
+ d6_new = (d6 == 0) ? 40 : d6;
+ p.pll_reg57.val = (d7 << 24) + (postdiv << 15) + (d8 << 7) + d6_new;
+ p.lf.val = (u32)((frac_en << 31) + (1 << 30) + (frac_en << 29) +
+ (feedfwd_cal_en << 28) + (tdc_fine << 27) +
+ (gain_ctrl << 24) + (feedfwd_gain << 16) +
+ (int_coeff << 12) + (prop_coeff << 8) + tdc_targetcnt);
+
+ compute_ssc(&p, ana_cfg);
+ compute_bias2(&p);
+ compute_tdc(&p, tdc_fine);
+ compute_dco_med(&p);
+ compute_dco_fine(&p, dco_12g);
+
+ pll_type = ((frequency_khz == 10000) || (frequency_khz == 20000) ||
+ (frequency_khz == 2500) || (dco_12g == 1)) ? 0 : 1;
+ set_phy_vdr_addresses(&p, pll_type);
+
+ lt_state->config[0] = 0x84;
+ lt_state->config[1] = 0x2d;
+ ADDR_ASSIGN(0, p.pll_reg4);
+ ADDR_ASSIGN(1, p.pll_reg3);
+ ADDR_ASSIGN(2, p.pll_reg5);
+ ADDR_ASSIGN(3, p.pll_reg57);
+ ADDR_ASSIGN(4, p.lf);
+ ADDR_ASSIGN(5, p.tdc);
+ ADDR_ASSIGN(6, p.ssc);
+ ADDR_ASSIGN(7, p.bias2);
+ ADDR_ASSIGN(8, p.bias_trim);
+ ADDR_ASSIGN(9, p.dco_med);
+ ADDR_ASSIGN(10, p.dco_fine);
+ ADDR_ASSIGN(11, p.ssc_inj);
+ ADDR_ASSIGN(12, p.surv_bonus);
+ DATA_ASSIGN(0, p.pll_reg4);
+ DATA_ASSIGN(1, p.pll_reg3);
+ DATA_ASSIGN(2, p.pll_reg5);
+ DATA_ASSIGN(3, p.pll_reg57);
+ DATA_ASSIGN(4, p.lf);
+ DATA_ASSIGN(5, p.tdc);
+ DATA_ASSIGN(6, p.ssc);
+ DATA_ASSIGN(7, p.bias2);
+ DATA_ASSIGN(8, p.bias_trim);
+ DATA_ASSIGN(9, p.dco_med);
+ DATA_ASSIGN(10, p.dco_fine);
+ DATA_ASSIGN(11, p.ssc_inj);
+ DATA_ASSIGN(12, p.surv_bonus);
+
+ return 0;
+}
+
+static int
+intel_lt_phy_calc_hdmi_port_clock(const struct intel_crtc_state *crtc_state)
+{
+#define REGVAL(i) ( \
+ (lt_state->data[i][3]) | \
+ (lt_state->data[i][2] << 8) | \
+ (lt_state->data[i][1] << 16) | \
+ (lt_state->data[i][0] << 24) \
+)
+
+ struct intel_display *display = to_intel_display(crtc_state);
+ const struct intel_lt_phy_pll_state *lt_state =
+ &crtc_state->dpll_hw_state.ltpll;
+ int clk = 0;
+ u32 d8, pll_reg_5, pll_reg_3, pll_reg_57, m2div_frac, m2div_int;
+ u64 temp0, temp1;
+ /*
+ * The algorithm uses '+' to combine bitfields when
+ * constructing PLL_reg3 and PLL_reg57:
+ * PLL_reg57 = (D7 << 24) + (postdiv << 15) + (D8 << 7) + D6_new;
+ * PLL_reg3 = (D4 << 21) + (D3 << 18) + (D1 << 15) + (m2div_int << 5);
+ *
+ * However, this is likely intended to be a bitwise OR operation,
+ * as each field occupies distinct, non-overlapping bits in the register.
+ *
+ * PLL_reg57 is composed of following fields packed into a 32-bit value:
+ * - D7: max value 10 -> fits in 4 bits -> placed at bits 24-27
+ * - postdiv: max value 9 -> fits in 4 bits -> placed at bits 15-18
+ * - D8: derived from loop_cnt / 2, max 127 -> fits in 7 bits
+ * (though 8 bits are given to it) -> placed at bits 7-14
+ * - D6_new: fits in lower 7 bits -> placed at bits 0-6
+ * PLL_reg57 = (D7 << 24) | (postdiv << 15) | (D8 << 7) | D6_new;
+ *
+ * Similarly, PLL_reg3 is packed as:
+ * - D4: max value 256 -> fits in 9 bits -> placed at bits 21-29
+ * - D3: max value 9 -> fits in 4 bits -> placed at bits 18-21
+ * - D1: max value 2 -> fits in 2 bits -> placed at bits 15-16
+ * - m2div_int: max value 511 -> fits in 9 bits (10 bits allocated)
+ * -> placed at bits 5-14
+ * PLL_reg3 = (D4 << 21) | (D3 << 18) | (D1 << 15) | (m2div_int << 5);
+ */
+ pll_reg_5 = REGVAL(2);
+ pll_reg_3 = REGVAL(1);
+ pll_reg_57 = REGVAL(3);
+ m2div_frac = pll_reg_5;
+
+ /*
+ * From forward algorithm we know
+ * m2div = 2 * m2
+ * val = y * frequency * 5
+ * So now,
+ * frequency = (m2 * 2 * refclk_khz / (d8 * 10))
+ * frequency = (m2div * refclk_khz / (d8 * 10))
+ */
+ d8 = (pll_reg_57 & REG_GENMASK(14, 7)) >> 7;
+ if (d8 == 0) {
+ drm_WARN_ON(display->drm,
+ "Invalid port clock using lowest HDMI portclock\n");
+ return xe3plpd_lt_hdmi_252.clock;
+ }
+ m2div_int = (pll_reg_3 & REG_GENMASK(14, 5)) >> 5;
+ temp0 = ((u64)m2div_frac * REF_CLK_KHZ) >> 32;
+ temp1 = (u64)m2div_int * REF_CLK_KHZ;
+
+ clk = div_u64((temp1 + temp0), d8 * 10);
+
+ return clk;
+}
+
+int
+intel_lt_phy_calc_port_clock(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ int clk;
+ const struct intel_lt_phy_pll_state *lt_state =
+ &crtc_state->dpll_hw_state.ltpll;
+ u8 mode, rate;
+
+ mode = REG_FIELD_GET8(LT_PHY_VDR_MODE_ENCODING_MASK,
+ lt_state->config[0]);
+ /*
+ * For edp/dp read the clock value from the tables
+ * and return the clock as the algorithm used for
+ * calculating the port clock does not exactly matches
+ * with edp/dp clock.
+ */
+ if (mode == MODE_DP) {
+ rate = REG_FIELD_GET8(LT_PHY_VDR_RATE_ENCODING_MASK,
+ lt_state->config[0]);
+ clk = intel_lt_phy_get_dp_clock(rate);
+ } else {
+ clk = intel_lt_phy_calc_hdmi_port_clock(crtc_state);
+ }
+
+ return clk;
+}
+
+int
+intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state,
+ struct intel_encoder *encoder)
+{
+ const struct intel_lt_phy_pll_state * const *tables;
+ int i;
+
+ tables = intel_lt_phy_pll_tables_get(crtc_state, encoder);
+ if (!tables)
+ return -EINVAL;
+
+ for (i = 0; tables[i]; i++) {
+ if (crtc_state->port_clock == tables[i]->clock) {
+ crtc_state->dpll_hw_state.ltpll = *tables[i];
+ if (intel_crtc_has_dp_encoder(crtc_state)) {
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
+ crtc_state->dpll_hw_state.ltpll.config[2] = 1;
+ }
+ crtc_state->dpll_hw_state.ltpll.ssc_enabled =
+ intel_lt_phy_pll_is_ssc_enabled(crtc_state, encoder);
+ return 0;
+ }
+ }
+
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) {
+ return intel_lt_phy_calculate_hdmi_state(&crtc_state->dpll_hw_state.ltpll,
+ crtc_state->port_clock);
+ }
+
+ return -EINVAL;
+}
+
+static void
+intel_lt_phy_program_pll(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
+ int i, j, k;
+
+ intel_lt_phy_write(encoder, owned_lane_mask, LT_PHY_VDR_0_CONFIG,
+ crtc_state->dpll_hw_state.ltpll.config[0], MB_WRITE_COMMITTED);
+ intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_1_CONFIG,
+ crtc_state->dpll_hw_state.ltpll.config[1], MB_WRITE_COMMITTED);
+ intel_lt_phy_write(encoder, owned_lane_mask, LT_PHY_VDR_2_CONFIG,
+ crtc_state->dpll_hw_state.ltpll.config[2], MB_WRITE_COMMITTED);
+
+ for (i = 0; i <= 12; i++) {
+ intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_ADDR_MSB(i),
+ crtc_state->dpll_hw_state.ltpll.addr_msb[i],
+ MB_WRITE_COMMITTED);
+ intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_X_ADDR_LSB(i),
+ crtc_state->dpll_hw_state.ltpll.addr_lsb[i],
+ MB_WRITE_COMMITTED);
+
+ for (j = 3, k = 0; j >= 0; j--, k++)
+ intel_lt_phy_write(encoder, INTEL_LT_PHY_LANE0,
+ LT_PHY_VDR_X_DATAY(i, j),
+ crtc_state->dpll_hw_state.ltpll.data[i][k],
+ MB_WRITE_COMMITTED);
+ }
+}
+
+static void
+intel_lt_phy_enable_disable_tx(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+ bool lane_reversal = dig_port->lane_reversal;
+ u8 lane_count = crtc_state->lane_count;
+ bool is_dp_alt =
+ intel_tc_port_in_dp_alt_mode(dig_port);
+ enum intel_tc_pin_assignment tc_pin =
+ intel_tc_port_get_pin_assignment(dig_port);
+ u8 transmitter_mask = 0;
+
+ /*
+ * We have a two transmitters per lane and total of 2 PHY lanes so a total
+ * of 4 transmitters. We prepare a mask of the lanes that need to be activated
+ * and the transmitter which need to be activated for each lane. TX 0,1 correspond
+ * to LANE0 and TX 2, 3 correspond to LANE1.
+ */
+
+ switch (lane_count) {
+ case 1:
+ transmitter_mask = lane_reversal ? REG_BIT8(3) : REG_BIT8(0);
+ if (is_dp_alt) {
+ if (tc_pin == INTEL_TC_PIN_ASSIGNMENT_D)
+ transmitter_mask = REG_BIT8(0);
+ else
+ transmitter_mask = REG_BIT8(1);
+ }
+ break;
+ case 2:
+ transmitter_mask = lane_reversal ? REG_GENMASK8(3, 2) : REG_GENMASK8(1, 0);
+ if (is_dp_alt)
+ transmitter_mask = REG_GENMASK8(1, 0);
+ break;
+ case 3:
+ transmitter_mask = lane_reversal ? REG_GENMASK8(3, 1) : REG_GENMASK8(2, 0);
+ if (is_dp_alt)
+ transmitter_mask = REG_GENMASK8(2, 0);
+ break;
+ case 4:
+ transmitter_mask = REG_GENMASK8(3, 0);
+ break;
+ default:
+ MISSING_CASE(lane_count);
+ transmitter_mask = REG_GENMASK8(3, 0);
+ break;
+ }
+
+ if (transmitter_mask & BIT(0)) {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_TXY_CTL10(0),
+ LT_PHY_TX_LANE_ENABLE, LT_PHY_TXY_CTL10_MAC(0),
+ LT_PHY_TX_LANE_ENABLE);
+ } else {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_TXY_CTL10(0),
+ 0, LT_PHY_TXY_CTL10_MAC(0), 0);
+ }
+
+ if (transmitter_mask & BIT(1)) {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_TXY_CTL10(1),
+ LT_PHY_TX_LANE_ENABLE, LT_PHY_TXY_CTL10_MAC(1),
+ LT_PHY_TX_LANE_ENABLE);
+ } else {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE0, LT_PHY_TXY_CTL10(1),
+ 0, LT_PHY_TXY_CTL10_MAC(1), 0);
+ }
+
+ if (transmitter_mask & BIT(2)) {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE1, LT_PHY_TXY_CTL10(0),
+ LT_PHY_TX_LANE_ENABLE, LT_PHY_TXY_CTL10_MAC(0),
+ LT_PHY_TX_LANE_ENABLE);
+ } else {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE1, LT_PHY_TXY_CTL10(0),
+ 0, LT_PHY_TXY_CTL10_MAC(0), 0);
+ }
+
+ if (transmitter_mask & BIT(3)) {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE1, LT_PHY_TXY_CTL10(1),
+ LT_PHY_TX_LANE_ENABLE, LT_PHY_TXY_CTL10_MAC(1),
+ LT_PHY_TX_LANE_ENABLE);
+ } else {
+ intel_lt_phy_p2p_write(encoder, INTEL_LT_PHY_LANE1, LT_PHY_TXY_CTL10(1),
+ 0, LT_PHY_TXY_CTL10_MAC(1), 0);
+ }
+}
+
+void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+ bool lane_reversal = dig_port->lane_reversal;
+ u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
+ enum phy phy = intel_encoder_to_phy(encoder);
+ enum port port = encoder->port;
+ intel_wakeref_t wakeref = 0;
+ u32 lane_phy_pulse_status = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XE3PLPDP_LANE_PHY_PULSE_STATUS(0) |
+ XE3PLPDP_LANE_PHY_PULSE_STATUS(1))
+ : XE3PLPDP_LANE_PHY_PULSE_STATUS(0);
+ u8 rate_update;
+
+ wakeref = intel_lt_phy_transaction_begin(encoder);
+
+ /* 1. Enable MacCLK at default 162 MHz frequency. */
+ intel_lt_phy_lane_reset(encoder, crtc_state->lane_count);
+
+ /* 2. Program PORT_CLOCK_CTL register to configure clock muxes, gating, and SSC. */
+ intel_lt_phy_program_port_clock_ctl(encoder, crtc_state, lane_reversal);
+
+ /* 3. Change owned PHY lanes power to Ready state. */
+ intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask,
+ XELPDP_P2_STATE_READY);
+
+ /*
+ * 4. Read the PHY message bus VDR register PHY_VDR_0_Config check enabled PLL type,
+ * encoded rate and encoded mode.
+ */
+ if (intel_lt_phy_config_changed(encoder, crtc_state)) {
+ /*
+ * 5. Program the PHY internal PLL registers over PHY message bus for the desired
+ * frequency and protocol type
+ */
+ intel_lt_phy_program_pll(encoder, crtc_state);
+
+ /* 6. Use the P2P transaction flow */
+ /*
+ * 6.1. Set the PHY VDR register 0xCC4[Rate Control VDR Update] = 1 over PHY message
+ * bus for Owned PHY Lanes.
+ */
+ /*
+ * 6.2. Poll for P2P Transaction Ready = "1" and read the MAC message bus VDR
+ * register at offset 0xC00 for Owned PHY Lanes*.
+ */
+ /* 6.3. Clear P2P transaction Ready bit. */
+ intel_lt_phy_p2p_write(encoder, owned_lane_mask, LT_PHY_RATE_UPDATE,
+ LT_PHY_RATE_CONTROL_VDR_UPDATE, LT_PHY_MAC_VDR,
+ LT_PHY_PCLKIN_GATE);
+
+ /* 7. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 0. */
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0), 0);
+
+ /* 8. Poll for PORT_CLOCK_CTL[PCLK PLL Ack LN0]= 0. */
+ if (intel_de_wait_for_clear_us(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_ACK(0),
+ XE3PLPD_MACCLK_TURNOFF_LATENCY_US))
+ drm_warn(display->drm, "PHY %c PLL MacCLK ack deassertion timeout\n",
+ phy_name(phy));
+
+ /*
+ * 9. Follow the Display Voltage Frequency Switching - Sequence Before Frequency
+ * Change. We handle this step in bxt_set_cdclk().
+ */
+ /* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */
+ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
+ crtc_state->port_clock);
+
+ /* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0));
+
+ /* 12. Poll for PORT_CLOCK_CTL[PCLK PLL Ack LN0]= 1. */
+ if (intel_de_wait_for_set_ms(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_ACK(0),
+ XE3PLPD_MACCLK_TURNON_LATENCY_MS))
+ drm_warn(display->drm, "PHY %c PLL MacCLK ack assertion timeout\n",
+ phy_name(phy));
+
+ /*
+ * 13. Ungate the forward clock by setting
+ * PORT_CLOCK_CTL[Forward Clock Ungate] = 1.
+ */
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_FORWARD_CLOCK_UNGATE,
+ XELPDP_FORWARD_CLOCK_UNGATE);
+
+ /* 14. SW clears PORT_BUF_CTL2 [PHY Pulse Status]. */
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_pulse_status,
+ lane_phy_pulse_status);
+ /*
+ * 15. Clear the PHY VDR register 0xCC4[Rate Control VDR Update] over
+ * PHY message bus for Owned PHY Lanes.
+ */
+ rate_update = intel_lt_phy_read(encoder, INTEL_LT_PHY_LANE0, LT_PHY_RATE_UPDATE);
+ rate_update &= ~LT_PHY_RATE_CONTROL_VDR_UPDATE;
+ intel_lt_phy_write(encoder, owned_lane_mask, LT_PHY_RATE_UPDATE,
+ rate_update, MB_WRITE_COMMITTED);
+
+ /* 16. Poll for PORT_BUF_CTL2 register PHY Pulse Status = 1 for Owned PHY Lanes. */
+ if (intel_de_wait_for_set_ms(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_pulse_status,
+ XE3PLPD_RATE_CALIB_DONE_LATENCY_MS))
+ drm_warn(display->drm, "PHY %c PLL rate not changed\n",
+ phy_name(phy));
+
+ /* 17. SW clears PORT_BUF_CTL2 [PHY Pulse Status]. */
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_pulse_status,
+ lane_phy_pulse_status);
+ } else {
+ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), crtc_state->port_clock);
+ }
+
+ /*
+ * 18. Follow the Display Voltage Frequency Switching - Sequence After Frequency Change.
+ * We handle this step in bxt_set_cdclk()
+ */
+ /* 19. Move the PHY powerdown state to Active and program to enable/disable transmitters */
+ intel_lt_phy_powerdown_change_sequence(encoder, owned_lane_mask,
+ XELPDP_P0_STATE_ACTIVE);
+
+ intel_lt_phy_enable_disable_tx(encoder, crtc_state);
+ intel_lt_phy_transaction_end(encoder, wakeref);
+}
+
+void intel_lt_phy_pll_disable(struct intel_encoder *encoder)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ enum phy phy = intel_encoder_to_phy(encoder);
+ enum port port = encoder->port;
+ intel_wakeref_t wakeref;
+ u8 owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
+ u32 lane_pipe_reset = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XELPDP_LANE_PIPE_RESET(0) |
+ XELPDP_LANE_PIPE_RESET(1))
+ : XELPDP_LANE_PIPE_RESET(0);
+ u32 lane_phy_current_status = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XELPDP_LANE_PHY_CURRENT_STATUS(0) |
+ XELPDP_LANE_PHY_CURRENT_STATUS(1))
+ : XELPDP_LANE_PHY_CURRENT_STATUS(0);
+ u32 lane_phy_pulse_status = owned_lane_mask == INTEL_LT_PHY_BOTH_LANES
+ ? (XE3PLPDP_LANE_PHY_PULSE_STATUS(0) |
+ XE3PLPDP_LANE_PHY_PULSE_STATUS(1))
+ : XE3PLPDP_LANE_PHY_PULSE_STATUS(0);
+
+ wakeref = intel_lt_phy_transaction_begin(encoder);
+
+ /* 1. Clear PORT_BUF_CTL2 [PHY Pulse Status]. */
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_pulse_status,
+ lane_phy_pulse_status);
+
+ /* 2. Set PORT_BUF_CTL2<port> Lane<PHY Lanes Owned> Pipe Reset to 1. */
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port), lane_pipe_reset,
+ lane_pipe_reset);
+
+ /* 3. Poll for PORT_BUF_CTL2<port> Lane<PHY Lanes Owned> PHY Current Status == 1. */
+ if (intel_de_wait_for_set_us(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_current_status,
+ XE3PLPD_RESET_START_LATENCY_US))
+ drm_warn(display->drm, "PHY %c failed to reset lane\n",
+ phy_name(phy));
+
+ /* 4. Clear for PHY pulse status on owned PHY lanes. */
+ intel_de_rmw(display, XELPDP_PORT_BUF_CTL2(display, port),
+ lane_phy_pulse_status,
+ lane_phy_pulse_status);
+
+ /*
+ * 5. Follow the Display Voltage Frequency Switching -
+ * Sequence Before Frequency Change. We handle this step in bxt_set_cdclk().
+ */
+ /* 6. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 0. */
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_REQUEST(0), 0);
+
+ /* 7. Program DDI_CLK_VALFREQ to 0. */
+ intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), 0);
+
+ /* 8. Poll for PORT_CLOCK_CTL[PCLK PLL Ack LN0]= 0. */
+ if (intel_de_wait_for_clear_us(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_LANE_PCLK_PLL_ACK(0),
+ XE3PLPD_MACCLK_TURNOFF_LATENCY_US))
+ drm_warn(display->drm, "PHY %c PLL MacCLK ack deassertion timeout\n",
+ phy_name(phy));
+
+ /*
+ * 9. Follow the Display Voltage Frequency Switching -
+ * Sequence After Frequency Change. We handle this step in bxt_set_cdclk().
+ */
+ /* 10. Program PORT_CLOCK_CTL register to disable and gate clocks. */
+ intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
+ XELPDP_DDI_CLOCK_SELECT_MASK(display) | XELPDP_FORWARD_CLOCK_UNGATE, 0);
+
+ /* 11. Program PORT_BUF_CTL5[MacCLK Reset_0] = 1 to assert MacCLK reset. */
+ intel_de_rmw(display, XE3PLPD_PORT_BUF_CTL5(port),
+ XE3PLPD_MACCLK_RESET_0, XE3PLPD_MACCLK_RESET_0);
+
+ intel_lt_phy_transaction_end(encoder, wakeref);
+}
+
+void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(encoder);
+ const struct intel_ddi_buf_trans *trans;
+ u8 owned_lane_mask;
+ intel_wakeref_t wakeref;
+ int n_entries, ln;
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ if (intel_tc_port_in_tbt_alt_mode(dig_port))
+ return;
+
+ owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
+
+ wakeref = intel_lt_phy_transaction_begin(encoder);
+
+ trans = encoder->get_buf_trans(encoder, crtc_state, &n_entries);
+ if (drm_WARN_ON_ONCE(display->drm, !trans)) {
+ intel_lt_phy_transaction_end(encoder, wakeref);
+ return;
+ }
+
+ for (ln = 0; ln < crtc_state->lane_count; ln++) {
+ int level = intel_ddi_level(encoder, crtc_state, ln);
+ int lane = ln / 2;
+ int tx = ln % 2;
+ u8 lane_mask = lane == 0 ? INTEL_LT_PHY_LANE0 : INTEL_LT_PHY_LANE1;
+
+ if (!(lane_mask & owned_lane_mask))
+ continue;
+
+ intel_lt_phy_rmw(encoder, lane_mask, LT_PHY_TXY_CTL8(tx),
+ LT_PHY_TX_SWING_LEVEL_MASK | LT_PHY_TX_SWING_MASK,
+ LT_PHY_TX_SWING_LEVEL(trans->entries[level].lt.txswing_level) |
+ LT_PHY_TX_SWING(trans->entries[level].lt.txswing),
+ MB_WRITE_COMMITTED);
+
+ intel_lt_phy_rmw(encoder, lane_mask, LT_PHY_TXY_CTL2(tx),
+ LT_PHY_TX_CURSOR_MASK,
+ LT_PHY_TX_CURSOR(trans->entries[level].lt.pre_cursor),
+ MB_WRITE_COMMITTED);
+ intel_lt_phy_rmw(encoder, lane_mask, LT_PHY_TXY_CTL3(tx),
+ LT_PHY_TX_CURSOR_MASK,
+ LT_PHY_TX_CURSOR(trans->entries[level].lt.main_cursor),
+ MB_WRITE_COMMITTED);
+ intel_lt_phy_rmw(encoder, lane_mask, LT_PHY_TXY_CTL4(tx),
+ LT_PHY_TX_CURSOR_MASK,
+ LT_PHY_TX_CURSOR(trans->entries[level].lt.post_cursor),
+ MB_WRITE_COMMITTED);
+ }
+
+ intel_lt_phy_transaction_end(encoder, wakeref);
+}
+
+void intel_lt_phy_dump_hw_state(struct intel_display *display,
+ const struct intel_lt_phy_pll_state *hw_state)
+{
+ int i, j;
+
+ drm_dbg_kms(display->drm, "lt_phy_pll_hw_state:\n");
+ for (i = 0; i < 3; i++) {
+ drm_dbg_kms(display->drm, "config[%d] = 0x%.4x,\n",
+ i, hw_state->config[i]);
+ }
+
+ for (i = 0; i <= 12; i++)
+ for (j = 3; j >= 0; j--)
+ drm_dbg_kms(display->drm, "vdr_data[%d][%d] = 0x%.4x,\n",
+ i, j, hw_state->data[i][j]);
+}
+
+bool
+intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a,
+ const struct intel_lt_phy_pll_state *b)
+{
+ if (memcmp(&a->config, &b->config, sizeof(a->config)) != 0)
+ return false;
+
+ if (memcmp(&a->data, &b->data, sizeof(a->data)) != 0)
+ return false;
+
+ return true;
+}
+
+void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state,
+ struct intel_lt_phy_pll_state *pll_state)
+{
+ u8 owned_lane_mask;
+ u8 lane;
+ intel_wakeref_t wakeref;
+ int i, j, k;
+
+ pll_state->tbt_mode = intel_tc_port_in_tbt_alt_mode(enc_to_dig_port(encoder));
+ if (pll_state->tbt_mode)
+ return;
+
+ owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder);
+ lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1;
+ wakeref = intel_lt_phy_transaction_begin(encoder);
+
+ pll_state->config[0] = intel_lt_phy_read(encoder, lane, LT_PHY_VDR_0_CONFIG);
+ pll_state->config[1] = intel_lt_phy_read(encoder, INTEL_LT_PHY_LANE0, LT_PHY_VDR_1_CONFIG);
+ pll_state->config[2] = intel_lt_phy_read(encoder, lane, LT_PHY_VDR_2_CONFIG);
+
+ for (i = 0; i <= 12; i++) {
+ for (j = 3, k = 0; j >= 0; j--, k++)
+ pll_state->data[i][k] =
+ intel_lt_phy_read(encoder, INTEL_LT_PHY_LANE0,
+ LT_PHY_VDR_X_DATAY(i, j));
+ }
+
+ pll_state->clock =
+ intel_lt_phy_calc_port_clock(encoder, crtc_state);
+ intel_lt_phy_transaction_end(encoder, wakeref);
+}
+
+void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state,
+ struct intel_crtc *crtc)
+{
+ struct intel_display *display = to_intel_display(state);
+ struct intel_digital_port *dig_port;
+ const struct intel_crtc_state *new_crtc_state =
+ intel_atomic_get_new_crtc_state(state, crtc);
+ struct intel_encoder *encoder;
+ struct intel_lt_phy_pll_state pll_hw_state = {};
+ const struct intel_lt_phy_pll_state *pll_sw_state = &new_crtc_state->dpll_hw_state.ltpll;
+ int clock;
+ int i, j;
+
+ if (DISPLAY_VER(display) < 35)
+ return;
+
+ if (!new_crtc_state->hw.active)
+ return;
+
+ /* intel_get_crtc_new_encoder() only works for modeset/fastset commits */
+ if (!intel_crtc_needs_modeset(new_crtc_state) &&
+ !intel_crtc_needs_fastset(new_crtc_state))
+ return;
+
+ encoder = intel_get_crtc_new_encoder(state, new_crtc_state);
+ intel_lt_phy_pll_readout_hw_state(encoder, new_crtc_state, &pll_hw_state);
+ clock = intel_lt_phy_calc_port_clock(encoder, new_crtc_state);
+
+ dig_port = enc_to_dig_port(encoder);
+ if (intel_tc_port_in_tbt_alt_mode(dig_port))
+ return;
+
+ INTEL_DISPLAY_STATE_WARN(display, pll_hw_state.clock != clock,
+ "[CRTC:%d:%s] mismatch in LT PHY: Register CLOCK (expected %d, found %d)",
+ crtc->base.base.id, crtc->base.name,
+ pll_sw_state->clock, pll_hw_state.clock);
+
+ for (i = 0; i < 3; i++) {
+ INTEL_DISPLAY_STATE_WARN(display, pll_hw_state.config[i] != pll_sw_state->config[i],
+ "[CRTC:%d:%s] mismatch in LT PHY PLL CONFIG%d: (expected 0x%04x, found 0x%04x)",
+ crtc->base.base.id, crtc->base.name, i,
+ pll_sw_state->config[i], pll_hw_state.config[i]);
+ }
+
+ for (i = 0; i <= 12; i++) {
+ for (j = 3; j >= 0; j--)
+ INTEL_DISPLAY_STATE_WARN(display,
+ pll_hw_state.data[i][j] !=
+ pll_sw_state->data[i][j],
+ "[CRTC:%d:%s] mismatch in LT PHY PLL DATA[%d][%d]: (expected 0x%04x, found 0x%04x)",
+ crtc->base.base.id, crtc->base.name, i, j,
+ pll_sw_state->data[i][j], pll_hw_state.data[i][j]);
+ }
+}
+
+void intel_xe3plpd_pll_enable(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ if (intel_tc_port_in_tbt_alt_mode(dig_port))
+ intel_mtl_tbt_pll_enable(encoder, crtc_state);
+ else
+ intel_lt_phy_pll_enable(encoder, crtc_state);
+}
+
+void intel_xe3plpd_pll_disable(struct intel_encoder *encoder)
+{
+ struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
+
+ if (intel_tc_port_in_tbt_alt_mode(dig_port))
+ intel_mtl_tbt_pll_disable(encoder);
+ else
+ intel_lt_phy_pll_disable(encoder);
+
+}
diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.h b/drivers/gpu/drm/i915/display/intel_lt_phy.h
new file mode 100644
index 000000000000..b7911acd7dcd
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_lt_phy.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __INTEL_LT_PHY_H__
+#define __INTEL_LT_PHY_H__
+
+#include <linux/types.h>
+
+struct intel_atomic_state;
+struct intel_display;
+struct intel_encoder;
+struct intel_crtc_state;
+struct intel_crtc;
+struct intel_lt_phy_pll_state;
+
+void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state);
+void intel_lt_phy_pll_disable(struct intel_encoder *encoder);
+int
+intel_lt_phy_pll_calc_state(struct intel_crtc_state *crtc_state,
+ struct intel_encoder *encoder);
+int intel_lt_phy_calc_port_clock(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state);
+void intel_lt_phy_set_signal_levels(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state);
+void intel_lt_phy_dump_hw_state(struct intel_display *display,
+ const struct intel_lt_phy_pll_state *hw_state);
+bool
+intel_lt_phy_pll_compare_hw_state(const struct intel_lt_phy_pll_state *a,
+ const struct intel_lt_phy_pll_state *b);
+void intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state,
+ struct intel_lt_phy_pll_state *pll_state);
+void intel_lt_phy_pll_state_verify(struct intel_atomic_state *state,
+ struct intel_crtc *crtc);
+int
+intel_lt_phy_calculate_hdmi_state(struct intel_lt_phy_pll_state *lt_state,
+ u32 frequency_khz);
+void intel_xe3plpd_pll_enable(struct intel_encoder *encoder,
+ const struct intel_crtc_state *crtc_state);
+void intel_xe3plpd_pll_disable(struct intel_encoder *encoder);
+
+#define HAS_LT_PHY(display) (DISPLAY_VER(display) >= 35)
+
+#endif /* __INTEL_LT_PHY_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy_regs.h b/drivers/gpu/drm/i915/display/intel_lt_phy_regs.h
new file mode 100644
index 000000000000..98ccc069a69b
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/intel_lt_phy_regs.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: MIT
+ *
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __INTEL_LT_PHY_REGS_H__
+#define __INTEL_LT_PHY_REGS_H__
+
+#define XE3PLPD_MSGBUS_TIMEOUT_FAST_US 500
+#define XE3PLPD_MACCLK_TURNON_LATENCY_MS 2
+#define XE3PLPD_MACCLK_TURNOFF_LATENCY_US 1
+#define XE3PLPD_RATE_CALIB_DONE_LATENCY_MS 1
+#define XE3PLPD_RESET_START_LATENCY_US 10
+#define XE3PLPD_PWRDN_TO_RDY_LATENCY_US 4
+#define XE3PLPD_RESET_END_LATENCY_MS 2
+
+/* LT Phy MAC Register */
+#define LT_PHY_MAC_VDR _MMIO(0xC00)
+#define LT_PHY_PCLKIN_GATE REG_BIT8(0)
+
+/* LT Phy Pipe Spec Registers */
+#define LT_PHY_TXY_CTL8(idx) (0x408 + (0x200 * (idx)))
+#define LT_PHY_TX_SWING_LEVEL_MASK REG_GENMASK8(7, 4)
+#define LT_PHY_TX_SWING_LEVEL(val) REG_FIELD_PREP8(LT_PHY_TX_SWING_LEVEL_MASK, val)
+#define LT_PHY_TX_SWING_MASK REG_BIT8(3)
+#define LT_PHY_TX_SWING(val) REG_FIELD_PREP8(LT_PHY_TX_SWING_MASK, val)
+
+#define LT_PHY_TXY_CTL2(idx) (0x402 + (0x200 * (idx)))
+#define LT_PHY_TXY_CTL3(idx) (0x403 + (0x200 * (idx)))
+#define LT_PHY_TXY_CTL4(idx) (0x404 + (0x200 * (idx)))
+#define LT_PHY_TX_CURSOR_MASK REG_GENMASK8(5, 0)
+#define LT_PHY_TX_CURSOR(val) REG_FIELD_PREP8(LT_PHY_TX_CURSOR_MASK, val)
+
+#define LT_PHY_TXY_CTL10(idx) (0x40A + (0x200 * (idx)))
+#define LT_PHY_TXY_CTL10_MAC(idx) _MMIO(LT_PHY_TXY_CTL10(idx))
+#define LT_PHY_TX_LANE_ENABLE REG_BIT8(0)
+
+/* LT Phy Vendor Register */
+#define LT_PHY_VDR_0_CONFIG 0xC02
+#define LT_PHY_VDR_DP_PLL_ENABLE REG_BIT(7)
+#define LT_PHY_VDR_1_CONFIG 0xC03
+#define LT_PHY_VDR_RATE_ENCODING_MASK REG_GENMASK8(6, 3)
+#define LT_PHY_VDR_MODE_ENCODING_MASK REG_GENMASK8(2, 0)
+#define LT_PHY_VDR_2_CONFIG 0xCC3
+
+#define LT_PHY_VDR_X_ADDR_MSB(idx) (0xC04 + 0x6 * (idx))
+#define LT_PHY_VDR_X_ADDR_LSB(idx) (0xC05 + 0x6 * (idx))
+
+#define LT_PHY_VDR_X_DATAY(idx, y) ((0xC06 + (3 - (y))) + 0x6 * (idx))
+
+#define LT_PHY_RATE_UPDATE 0xCC4
+#define LT_PHY_RATE_CONTROL_VDR_UPDATE REG_BIT8(0)
+
+#define _XE3PLPD_PORT_BUF_CTL5(idx) _MMIO(_PICK_EVEN_2RANGES(idx, PORT_TC1, \
+ _XELPDP_PORT_BUF_CTL1_LN0_A, \
+ _XELPDP_PORT_BUF_CTL1_LN0_B, \
+ _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \
+ _XELPDP_PORT_BUF_CTL1_LN0_USBC2) \
+ + 0x34)
+#define XE3PLPD_PORT_BUF_CTL5(port) _XE3PLPD_PORT_BUF_CTL5(__xe2lpd_port_idx(port))
+#define XE3PLPD_MACCLK_RESET_0 REG_BIT(11)
+#define XE3PLPD_MACCLK_RATE_MASK REG_GENMASK(4, 0)
+#define XE3PLPD_MACCLK_RATE_DEF REG_FIELD_PREP(XE3PLPD_MACCLK_RATE_MASK, 0x1F)
+
+#define _XE3PLPD_PORT_P2M_MSGBUS_STATUS_P2P(idx, lane) _MMIO(_PICK_EVEN_2RANGES(idx, PORT_TC1, \
+ _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_A, \
+ _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_B, \
+ _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC1, \
+ _XELPDP_PORT_M2P_MSGBUS_CTL_LN0_USBC2) \
+ + 0x60 + (lane) * 0x4)
+#define XE3PLPD_PORT_P2M_MSGBUS_STATUS_P2P(port, lane) _XE3PLPD_PORT_P2M_MSGBUS_STATUS_P2P(__xe2lpd_port_idx(port), \
+ lane)
+#define XE3LPD_PORT_P2M_ADDR_MASK REG_GENMASK(11, 0)
+
+#define PLL_REG4_ADDR 0x8510
+#define PLL_REG3_ADDR 0x850C
+#define PLL_REG5_ADDR 0x8514
+#define PLL_REG57_ADDR 0x85E4
+#define PLL_LF_ADDR 0x860C
+#define PLL_TDC_ADDR 0x8610
+#define PLL_SSC_ADDR 0x8614
+#define PLL_BIAS2_ADDR 0x8618
+#define PLL_BIAS_TRIM_ADDR 0x8648
+#define PLL_DCO_MED_ADDR 0x8640
+#define PLL_DCO_FINE_ADDR 0x864C
+#define PLL_SSC_INJ_ADDR 0x8624
+#define PLL_SURV_BONUS_ADDR 0x8644
+#define PLL_TYPE_OFFSET 0x200
+#define PLL_REG_ADDR(base, pll_type) ((pll_type) ? (base) + PLL_TYPE_OFFSET : (base))
+#endif /* __INTEL_LT_PHY_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c
index 48f4d8ed4f15..89aeb4fb340e 100644
--- a/drivers/gpu/drm/i915/display/intel_lvds.c
+++ b/drivers/gpu/drm/i915/display/intel_lvds.c
@@ -329,7 +329,7 @@ static void intel_enable_lvds(struct intel_atomic_state *state,
intel_de_rmw(display, PP_CONTROL(display, 0), 0, PANEL_POWER_ON);
intel_de_posting_read(display, lvds_encoder->reg);
- if (intel_de_wait_for_set(display, PP_STATUS(display, 0), PP_ON, 5000))
+ if (intel_de_wait_for_set_ms(display, PP_STATUS(display, 0), PP_ON, 5000))
drm_err(display->drm,
"timed out waiting for panel to power on\n");
@@ -345,7 +345,7 @@ static void intel_disable_lvds(struct intel_atomic_state *state,
struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
intel_de_rmw(display, PP_CONTROL(display, 0), PANEL_POWER_ON, 0);
- if (intel_de_wait_for_clear(display, PP_STATUS(display, 0), PP_ON, 1000))
+ if (intel_de_wait_for_clear_ms(display, PP_STATUS(display, 0), PP_ON, 1000))
drm_err(display->drm,
"timed out waiting for panel to power off\n");
@@ -384,7 +384,7 @@ static void intel_lvds_shutdown(struct intel_encoder *encoder)
{
struct intel_display *display = to_intel_display(encoder);
- if (intel_de_wait_for_clear(display, PP_STATUS(display, 0), PP_CYCLE_DELAY_ACTIVE, 5000))
+ if (intel_de_wait_for_clear_ms(display, PP_STATUS(display, 0), PP_CYCLE_DELAY_ACTIVE, 5000))
drm_err(display->drm,
"timed out waiting for panel power cycle delay\n");
}
diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
index 8415f3d703ed..0dcb0597879a 100644
--- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c
+++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
@@ -19,6 +19,7 @@
#include "intel_color.h"
#include "intel_crtc.h"
#include "intel_crtc_state_dump.h"
+#include "intel_dbuf_bw.h"
#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display.h"
@@ -176,6 +177,7 @@ static void intel_crtc_disable_noatomic_complete(struct intel_crtc *crtc)
intel_cdclk_crtc_disable_noatomic(crtc);
skl_wm_crtc_disable_noatomic(crtc);
intel_bw_crtc_disable_noatomic(crtc);
+ intel_dbuf_bw_crtc_disable_noatomic(crtc);
intel_pmdemand_update_port_clock(display, pmdemand_state, pipe, 0);
}
@@ -851,18 +853,23 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
*/
if (plane_state->uapi.visible && plane->min_cdclk) {
if (crtc_state->double_wide || DISPLAY_VER(display) >= 10)
- crtc_state->min_cdclk[plane->id] =
+ crtc_state->plane_min_cdclk[plane->id] =
DIV_ROUND_UP(crtc_state->pixel_rate, 2);
else
- crtc_state->min_cdclk[plane->id] =
+ crtc_state->plane_min_cdclk[plane->id] =
crtc_state->pixel_rate;
}
drm_dbg_kms(display->drm,
"[PLANE:%d:%s] min_cdclk %d kHz\n",
plane->base.base.id, plane->base.name,
- crtc_state->min_cdclk[plane->id]);
+ crtc_state->plane_min_cdclk[plane->id]);
}
+ crtc_state->min_cdclk = intel_crtc_min_cdclk(crtc_state);
+
+ drm_dbg_kms(display->drm, "[CRTC:%d:%s] min_cdclk %d kHz\n",
+ crtc->base.base.id, crtc->base.name, crtc_state->min_cdclk);
+
intel_pmdemand_update_port_clock(display, pmdemand_state, pipe,
crtc_state->port_clock);
}
@@ -872,6 +879,7 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
intel_wm_get_hw_state(display);
intel_bw_update_hw_state(display);
+ intel_dbuf_bw_update_hw_state(display);
intel_cdclk_update_hw_state(display);
intel_pmdemand_init_pmdemand_params(display, pmdemand_state);
diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c
index f2f6b9d9afa1..b361a77cd235 100644
--- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c
+++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c
@@ -16,6 +16,7 @@
#include "intel_display_core.h"
#include "intel_display_types.h"
#include "intel_fdi.h"
+#include "intel_lt_phy.h"
#include "intel_modeset_verify.h"
#include "intel_snps_phy.h"
#include "skl_watermark.h"
@@ -246,6 +247,7 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state,
intel_dpll_state_verify(state, crtc);
intel_mpllb_state_verify(state, crtc);
intel_cx0pll_state_verify(state, crtc);
+ intel_lt_phy_pll_state_verify(state, crtc);
}
void intel_modeset_verify_disabled(struct intel_atomic_state *state)
diff --git a/drivers/gpu/drm/i915/display/intel_overlay.c b/drivers/gpu/drm/i915/display/intel_overlay.c
index 272f9e7af4d4..88eb7ae5765c 100644
--- a/drivers/gpu/drm/i915/display/intel_overlay.c
+++ b/drivers/gpu/drm/i915/display/intel_overlay.c
@@ -27,6 +27,7 @@
*/
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_object_frontbuffer.h"
@@ -307,8 +308,6 @@ static void intel_overlay_flip_prepare(struct intel_overlay *overlay,
intel_frontbuffer_put(overlay->frontbuffer);
overlay->frontbuffer = frontbuffer;
- intel_frontbuffer_flip_prepare(display, INTEL_FRONTBUFFER_OVERLAY(pipe));
-
overlay->old_vma = overlay->vma;
if (vma)
overlay->vma = i915_vma_get(vma);
@@ -365,7 +364,7 @@ static void intel_overlay_release_old_vma(struct intel_overlay *overlay)
if (drm_WARN_ON(display->drm, !vma))
return;
- intel_frontbuffer_flip_complete(display, INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
+ intel_frontbuffer_flip(display, INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
i915_vma_unpin(vma);
i915_vma_put(vma);
@@ -821,8 +820,6 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
goto out_pin_section;
}
- i915_gem_object_flush_frontbuffer(new_bo, ORIGIN_DIRTYFB);
-
if (!overlay->active) {
const struct intel_crtc_state *crtc_state =
overlay->crtc->config;
diff --git a/drivers/gpu/drm/i915/display/intel_pch.c b/drivers/gpu/drm/i915/display/intel_pch.c
index 469e8a3cfb49..65359a36df48 100644
--- a/drivers/gpu/drm/i915/display/intel_pch.c
+++ b/drivers/gpu/drm/i915/display/intel_pch.c
@@ -5,8 +5,8 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_display_core.h"
+#include "intel_display_utils.h"
#include "intel_pch.h"
#define INTEL_PCH_DEVICE_ID_MASK 0xff80
@@ -328,7 +328,7 @@ void intel_pch_detect(struct intel_display *display)
"Display disabled, reverting to NOP PCH\n");
display->pch_type = PCH_NOP;
} else if (!pch) {
- if (i915_run_as_guest() && HAS_DISPLAY(display)) {
+ if (intel_display_run_as_guest(display) && HAS_DISPLAY(display)) {
intel_virt_detect_pch(display, &id, &pch_type);
display->pch_type = pch_type;
} else {
diff --git a/drivers/gpu/drm/i915/display/intel_pch_display.c b/drivers/gpu/drm/i915/display/intel_pch_display.c
index 3456c794e0e7..16619f7be5f8 100644
--- a/drivers/gpu/drm/i915/display/intel_pch_display.c
+++ b/drivers/gpu/drm/i915/display/intel_pch_display.c
@@ -305,7 +305,7 @@ static void ilk_enable_pch_transcoder(const struct intel_crtc_state *crtc_state)
}
intel_de_write(display, reg, val | TRANS_ENABLE);
- if (intel_de_wait_for_set(display, reg, TRANS_STATE_ENABLE, 100))
+ if (intel_de_wait_for_set_ms(display, reg, TRANS_STATE_ENABLE, 100))
drm_err(display->drm, "failed to enable transcoder %c\n",
pipe_name(pipe));
}
@@ -326,7 +326,7 @@ static void ilk_disable_pch_transcoder(struct intel_crtc *crtc)
reg = PCH_TRANSCONF(pipe);
intel_de_rmw(display, reg, TRANS_ENABLE, 0);
/* wait for PCH transcoder off, transcoder state */
- if (intel_de_wait_for_clear(display, reg, TRANS_STATE_ENABLE, 50))
+ if (intel_de_wait_for_clear_ms(display, reg, TRANS_STATE_ENABLE, 50))
drm_err(display->drm, "failed to disable transcoder %c\n",
pipe_name(pipe));
@@ -572,8 +572,8 @@ static void lpt_enable_pch_transcoder(const struct intel_crtc_state *crtc_state)
val |= TRANS_INTERLACE_PROGRESSIVE;
intel_de_write(display, LPT_TRANSCONF, val);
- if (intel_de_wait_for_set(display, LPT_TRANSCONF,
- TRANS_STATE_ENABLE, 100))
+ if (intel_de_wait_for_set_ms(display, LPT_TRANSCONF,
+ TRANS_STATE_ENABLE, 100))
drm_err(display->drm, "Failed to enable PCH transcoder\n");
}
@@ -581,8 +581,8 @@ static void lpt_disable_pch_transcoder(struct intel_display *display)
{
intel_de_rmw(display, LPT_TRANSCONF, TRANS_ENABLE, 0);
/* wait for PCH transcoder off, transcoder state */
- if (intel_de_wait_for_clear(display, LPT_TRANSCONF,
- TRANS_STATE_ENABLE, 50))
+ if (intel_de_wait_for_clear_ms(display, LPT_TRANSCONF,
+ TRANS_STATE_ENABLE, 50))
drm_err(display->drm, "Failed to disable PCH transcoder\n");
/* Workaround: clear timing override bit. */
diff --git a/drivers/gpu/drm/i915/display/intel_pch_refclk.c b/drivers/gpu/drm/i915/display/intel_pch_refclk.c
index 9ae53679a041..9a89bb6dcf65 100644
--- a/drivers/gpu/drm/i915/display/intel_pch_refclk.c
+++ b/drivers/gpu/drm/i915/display/intel_pch_refclk.c
@@ -6,10 +6,10 @@
#include <drm/drm_print.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_panel.h"
#include "intel_pch_refclk.h"
#include "intel_sbi.h"
@@ -21,17 +21,15 @@ static void lpt_fdi_reset_mphy(struct intel_display *display)
intel_de_rmw(display, SOUTH_CHICKEN2, 0, FDI_MPHY_IOSFSB_RESET_CTL);
- ret = intel_de_wait_custom(display, SOUTH_CHICKEN2,
- FDI_MPHY_IOSFSB_RESET_STATUS, FDI_MPHY_IOSFSB_RESET_STATUS,
- 100, 0, NULL);
+ ret = intel_de_wait_for_set_us(display, SOUTH_CHICKEN2,
+ FDI_MPHY_IOSFSB_RESET_STATUS, 100);
if (ret)
drm_err(display->drm, "FDI mPHY reset assert timeout\n");
intel_de_rmw(display, SOUTH_CHICKEN2, FDI_MPHY_IOSFSB_RESET_CTL, 0);
- ret = intel_de_wait_custom(display, SOUTH_CHICKEN2,
- FDI_MPHY_IOSFSB_RESET_STATUS, 0,
- 100, 0, NULL);
+ ret = intel_de_wait_for_clear_us(display, SOUTH_CHICKEN2,
+ FDI_MPHY_IOSFSB_RESET_STATUS, 100);
if (ret)
drm_err(display->drm, "FDI mPHY reset de-assert timeout\n");
}
diff --git a/drivers/gpu/drm/i915/display/intel_pfit.c b/drivers/gpu/drm/i915/display/intel_pfit.c
index 68539e7c2a24..6dda496190e0 100644
--- a/drivers/gpu/drm/i915/display/intel_pfit.c
+++ b/drivers/gpu/drm/i915/display/intel_pfit.c
@@ -5,12 +5,12 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_de.h"
#include "intel_display_core.h"
#include "intel_display_driver.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_lvds_regs.h"
#include "intel_pfit.h"
#include "intel_pfit_regs.h"
diff --git a/drivers/gpu/drm/i915/display/intel_pipe_crc.c b/drivers/gpu/drm/i915/display/intel_pipe_crc.c
index c2b4b2254190..1f27643412f1 100644
--- a/drivers/gpu/drm/i915/display/intel_pipe_crc.c
+++ b/drivers/gpu/drm/i915/display/intel_pipe_crc.c
@@ -28,6 +28,8 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_irq.h"
#include "intel_atomic.h"
diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c
index 2329f09d413d..ab6a58530b39 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_plane.c
@@ -43,12 +43,13 @@
#include <drm/drm_gem.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_panic.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_object.h"
-#include "i915_scheduler_types.h"
#include "i9xx_plane_regs.h"
#include "intel_cdclk.h"
#include "intel_cursor.h"
+#include "intel_colorop.h"
#include "intel_display_rps.h"
#include "intel_display_trace.h"
#include "intel_display_types.h"
@@ -292,64 +293,21 @@ intel_plane_relative_data_rate(const struct intel_crtc_state *crtc_state,
rel_data_rate);
}
-int intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
- struct intel_plane *plane,
- bool *need_cdclk_calc)
+static void intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
+ struct intel_plane *plane)
{
- struct intel_display *display = to_intel_display(plane);
const struct intel_plane_state *plane_state =
intel_atomic_get_new_plane_state(state, plane);
struct intel_crtc *crtc = to_intel_crtc(plane_state->hw.crtc);
- const struct intel_cdclk_state *cdclk_state;
- const struct intel_crtc_state *old_crtc_state;
struct intel_crtc_state *new_crtc_state;
if (!plane_state->uapi.visible || !plane->min_cdclk)
- return 0;
+ return;
- old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
- new_crtc_state->min_cdclk[plane->id] =
+ new_crtc_state->plane_min_cdclk[plane->id] =
plane->min_cdclk(new_crtc_state, plane_state);
-
- /*
- * No need to check against the cdclk state if
- * the min cdclk for the plane doesn't increase.
- *
- * Ie. we only ever increase the cdclk due to plane
- * requirements. This can reduce back and forth
- * display blinking due to constant cdclk changes.
- */
- if (new_crtc_state->min_cdclk[plane->id] <=
- old_crtc_state->min_cdclk[plane->id])
- return 0;
-
- cdclk_state = intel_atomic_get_cdclk_state(state);
- if (IS_ERR(cdclk_state))
- return PTR_ERR(cdclk_state);
-
- /*
- * No need to recalculate the cdclk state if
- * the min cdclk for the pipe doesn't increase.
- *
- * Ie. we only ever increase the cdclk due to plane
- * requirements. This can reduce back and forth
- * display blinking due to constant cdclk changes.
- */
- if (new_crtc_state->min_cdclk[plane->id] <=
- intel_cdclk_min_cdclk(cdclk_state, crtc->pipe))
- return 0;
-
- drm_dbg_kms(display->drm,
- "[PLANE:%d:%s] min cdclk (%d kHz) > [CRTC:%d:%s] min cdclk (%d kHz)\n",
- plane->base.base.id, plane->base.name,
- new_crtc_state->min_cdclk[plane->id],
- crtc->base.base.id, crtc->base.name,
- intel_cdclk_min_cdclk(cdclk_state, crtc->pipe));
- *need_cdclk_calc = true;
-
- return 0;
}
static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state)
@@ -379,6 +337,58 @@ intel_plane_copy_uapi_plane_damage(struct intel_plane_state *new_plane_state,
*damage = drm_plane_state_src(&new_uapi_plane_state->uapi);
}
+static bool
+intel_plane_colorop_replace_blob(struct intel_plane_state *plane_state,
+ struct intel_colorop *intel_colorop,
+ struct drm_property_blob *blob)
+{
+ if (intel_colorop->id == INTEL_PLANE_CB_CSC)
+ return drm_property_replace_blob(&plane_state->hw.ctm, blob);
+ else if (intel_colorop->id == INTEL_PLANE_CB_PRE_CSC_LUT)
+ return drm_property_replace_blob(&plane_state->hw.degamma_lut, blob);
+ else if (intel_colorop->id == INTEL_PLANE_CB_POST_CSC_LUT)
+ return drm_property_replace_blob(&plane_state->hw.gamma_lut, blob);
+ else if (intel_colorop->id == INTEL_PLANE_CB_3DLUT)
+ return drm_property_replace_blob(&plane_state->hw.lut_3d, blob);
+
+ return false;
+}
+
+static void
+intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
+ const struct intel_plane_state *from_plane_state,
+ struct intel_crtc *crtc)
+{
+ struct drm_colorop *iter_colorop, *colorop;
+ struct drm_colorop_state *new_colorop_state;
+ struct drm_atomic_state *state = plane_state->uapi.state;
+ struct intel_colorop *intel_colorop;
+ struct drm_property_blob *blob;
+ struct intel_atomic_state *intel_atomic_state = to_intel_atomic_state(state);
+ struct intel_crtc_state *new_crtc_state = intel_atomic_state ?
+ intel_atomic_get_new_crtc_state(intel_atomic_state, crtc) : NULL;
+ bool changed = false;
+ int i = 0;
+
+ iter_colorop = plane_state->uapi.color_pipeline;
+
+ while (iter_colorop) {
+ for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) {
+ if (new_colorop_state->colorop == iter_colorop) {
+ blob = new_colorop_state->bypass ? NULL : new_colorop_state->data;
+ intel_colorop = to_intel_colorop(colorop);
+ changed |= intel_plane_colorop_replace_blob(plane_state,
+ intel_colorop,
+ blob);
+ }
+ }
+ iter_colorop = iter_colorop->next;
+ }
+
+ if (new_crtc_state && changed)
+ new_crtc_state->plane_color_changed = true;
+}
+
void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
const struct intel_plane_state *from_plane_state,
struct intel_crtc *crtc)
@@ -407,6 +417,8 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi);
plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi);
+
+ intel_plane_color_copy_uapi_to_hw_state(plane_state, from_plane_state, crtc);
}
void intel_plane_copy_hw_state(struct intel_plane_state *plane_state,
@@ -435,7 +447,7 @@ void intel_plane_set_invisible(struct intel_crtc_state *crtc_state,
crtc_state->data_rate_y[plane->id] = 0;
crtc_state->rel_data_rate[plane->id] = 0;
crtc_state->rel_data_rate_y[plane->id] = 0;
- crtc_state->min_cdclk[plane->id] = 0;
+ crtc_state->plane_min_cdclk[plane->id] = 0;
plane_state->uapi.visible = false;
}
@@ -1094,6 +1106,9 @@ int intel_plane_check_src_coordinates(struct intel_plane_state *plane_state)
DISPLAY_VERx100(display) == 3002) &&
src_x % 2 != 0)
hsub = 2;
+
+ if (DISPLAY_VER(display) == 35)
+ vsub = 2;
} else {
hsub = fb->format->hsub;
vsub = fb->format->vsub;
@@ -1172,7 +1187,6 @@ static int
intel_prepare_plane_fb(struct drm_plane *_plane,
struct drm_plane_state *_new_plane_state)
{
- struct i915_sched_attr attr = { .priority = I915_PRIORITY_DISPLAY };
struct intel_plane *plane = to_intel_plane(_plane);
struct intel_display *display = to_intel_display(plane);
struct intel_plane_state *new_plane_state =
@@ -1221,8 +1235,7 @@ intel_prepare_plane_fb(struct drm_plane *_plane,
goto unpin_fb;
if (new_plane_state->uapi.fence) {
- i915_gem_fence_wait_priority(new_plane_state->uapi.fence,
- &attr);
+ i915_gem_fence_wait_priority_display(new_plane_state->uapi.fence);
intel_display_rps_boost_after_vblank(new_plane_state->hw.crtc,
new_plane_state->uapi.fence);
@@ -1746,5 +1759,8 @@ int intel_plane_atomic_check(struct intel_atomic_state *state)
return ret;
}
+ for_each_new_intel_plane_in_state(state, plane, plane_state, i)
+ intel_plane_calc_min_cdclk(state, plane);
+
return 0;
}
diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h
index 8af41ccc0a69..4e99df9de3e8 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.h
+++ b/drivers/gpu/drm/i915/display/intel_plane.h
@@ -69,9 +69,6 @@ int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
struct intel_crtc_state *crtc_state,
const struct intel_plane_state *old_plane_state,
struct intel_plane_state *intel_state);
-int intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
- struct intel_plane *plane,
- bool *need_cdclk_calc);
int intel_plane_check_clipping(struct intel_plane_state *plane_state,
struct intel_crtc_state *crtc_state,
int min_scale, int max_scale,
diff --git a/drivers/gpu/drm/i915/display/intel_plane_initial.c b/drivers/gpu/drm/i915/display/intel_plane_initial.c
index a9f36b1b50c1..a1de1ec564d1 100644
--- a/drivers/gpu/drm/i915/display/intel_plane_initial.c
+++ b/drivers/gpu/drm/i915/display/intel_plane_initial.c
@@ -3,6 +3,8 @@
* Copyright © 2021 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_lmem.h"
#include "gem/i915_gem_region.h"
#include "i915_drv.h"
@@ -131,6 +133,7 @@ initial_plane_vma(struct intel_display *display,
struct drm_mm_node orig_mm = {};
struct i915_vma *vma;
resource_size_t phys_base;
+ unsigned int tiling;
u32 base, size;
u64 pinctl;
@@ -177,17 +180,19 @@ initial_plane_vma(struct intel_display *display,
i915_gem_object_set_cache_coherency(obj, HAS_WT(i915) ?
I915_CACHE_WT : I915_CACHE_NONE);
- switch (plane_config->tiling) {
+ tiling = intel_fb_modifier_to_tiling(plane_config->fb->base.modifier);
+
+ switch (tiling) {
case I915_TILING_NONE:
break;
case I915_TILING_X:
case I915_TILING_Y:
obj->tiling_and_stride =
plane_config->fb->base.pitches[0] |
- plane_config->tiling;
+ tiling;
break;
default:
- MISSING_CASE(plane_config->tiling);
+ MISSING_CASE(tiling);
goto err_obj;
}
@@ -372,7 +377,7 @@ valid_fb:
plane_state->uapi.crtc_w = fb->width;
plane_state->uapi.crtc_h = fb->height;
- if (plane_config->tiling)
+ if (fb->modifier != DRM_FORMAT_MOD_LINEAR)
dev_priv->preserve_bios_swizzle = true;
plane_state->uapi.fb = fb;
diff --git a/drivers/gpu/drm/i915/display/intel_pmdemand.c b/drivers/gpu/drm/i915/display/intel_pmdemand.c
index d806c15db7ce..dc44a7a169c1 100644
--- a/drivers/gpu/drm/i915/display/intel_pmdemand.c
+++ b/drivers/gpu/drm/i915/display/intel_pmdemand.c
@@ -7,13 +7,14 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_bw.h"
#include "intel_cdclk.h"
#include "intel_de.h"
+#include "intel_display_jiffies.h"
#include "intel_display_regs.h"
#include "intel_display_trace.h"
+#include "intel_display_utils.h"
#include "intel_pmdemand.h"
#include "intel_step.h"
#include "skl_watermark.h"
@@ -389,12 +390,12 @@ int intel_pmdemand_atomic_check(struct intel_atomic_state *state)
static bool intel_pmdemand_check_prev_transaction(struct intel_display *display)
{
- return !(intel_de_wait_for_clear(display,
- XELPDP_INITIATE_PMDEMAND_REQUEST(1),
- XELPDP_PMDEMAND_REQ_ENABLE, 10) ||
- intel_de_wait_for_clear(display,
- GEN12_DCPR_STATUS_1,
- XELPDP_PMDEMAND_INFLIGHT_STATUS, 10));
+ return !(intel_de_wait_for_clear_ms(display,
+ XELPDP_INITIATE_PMDEMAND_REQUEST(1),
+ XELPDP_PMDEMAND_REQ_ENABLE, 10) ||
+ intel_de_wait_for_clear_ms(display,
+ GEN12_DCPR_STATUS_1,
+ XELPDP_PMDEMAND_INFLIGHT_STATUS, 10));
}
void
@@ -461,9 +462,9 @@ static void intel_pmdemand_poll(struct intel_display *display)
u32 status;
int ret;
- ret = intel_de_wait_custom(display, XELPDP_INITIATE_PMDEMAND_REQUEST(1),
- XELPDP_PMDEMAND_REQ_ENABLE, 0,
- 50, timeout_ms, &status);
+ ret = intel_de_wait_ms(display, XELPDP_INITIATE_PMDEMAND_REQUEST(1),
+ XELPDP_PMDEMAND_REQ_ENABLE, 0,
+ timeout_ms, &status);
if (ret == -ETIMEDOUT)
drm_err(display->drm,
diff --git a/drivers/gpu/drm/i915/display/intel_pps.c b/drivers/gpu/drm/i915/display/intel_pps.c
index 327e0de86f1e..25692a547764 100644
--- a/drivers/gpu/drm/i915/display/intel_pps.c
+++ b/drivers/gpu/drm/i915/display/intel_pps.c
@@ -10,11 +10,12 @@
#include "g4x_dp.h"
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_de.h"
+#include "intel_display_jiffies.h"
#include "intel_display_power_well.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dpio_phy.h"
#include "intel_dpll.h"
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index 10eb93a34cf2..08bca4573974 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -26,6 +26,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "i915_reg.h"
@@ -39,6 +40,7 @@
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dmc.h"
#include "intel_dp.h"
#include "intel_dp_aux.h"
@@ -50,6 +52,7 @@
#include "intel_snps_phy.h"
#include "intel_step.h"
#include "intel_vblank.h"
+#include "intel_vdsc.h"
#include "intel_vrr.h"
#include "skl_universal_plane.h"
@@ -580,11 +583,53 @@ exit:
intel_dp->psr.su_y_granularity = y;
}
+static enum intel_panel_replay_dsc_support
+compute_pr_dsc_support(struct intel_dp *intel_dp)
+{
+ u8 pr_dsc_mode;
+ u8 val;
+
+ val = intel_dp->pr_dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_CAPABILITY)];
+ pr_dsc_mode = REG_FIELD_GET8(DP_PANEL_REPLAY_DSC_DECODE_CAPABILITY_IN_PR_MASK, val);
+
+ switch (pr_dsc_mode) {
+ case DP_DSC_DECODE_CAPABILITY_IN_PR_FULL_FRAME_ONLY:
+ return INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY;
+ case DP_DSC_DECODE_CAPABILITY_IN_PR_SUPPORTED:
+ return INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE;
+ default:
+ MISSING_CASE(pr_dsc_mode);
+ fallthrough;
+ case DP_DSC_DECODE_CAPABILITY_IN_PR_NOT_SUPPORTED:
+ case DP_DSC_DECODE_CAPABILITY_IN_PR_RESERVED:
+ return INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED;
+ }
+}
+
+static const char *panel_replay_dsc_support_str(enum intel_panel_replay_dsc_support dsc_support)
+{
+ switch (dsc_support) {
+ case INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED:
+ return "not supported";
+ case INTEL_DP_PANEL_REPLAY_DSC_FULL_FRAME_ONLY:
+ return "full frame only";
+ case INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE:
+ return "selective update";
+ default:
+ MISSING_CASE(dsc_support);
+ return "n/a";
+ };
+}
+
static void _panel_replay_init_dpcd(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
int ret;
+ /* TODO: Enable Panel Replay on MST once it's properly implemented. */
+ if (intel_dp->mst_detect == DRM_DP_MST)
+ return;
+
ret = drm_dp_dpcd_read_data(&intel_dp->aux, DP_PANEL_REPLAY_CAP_SUPPORT,
&intel_dp->pr_dpcd, sizeof(intel_dp->pr_dpcd));
if (ret < 0)
@@ -615,10 +660,13 @@ static void _panel_replay_init_dpcd(struct intel_dp *intel_dp)
DP_PANEL_REPLAY_SU_SUPPORT)
intel_dp->psr.sink_panel_replay_su_support = true;
+ intel_dp->psr.sink_panel_replay_dsc_support = compute_pr_dsc_support(intel_dp);
+
drm_dbg_kms(display->drm,
- "Panel replay %sis supported by panel\n",
+ "Panel replay %sis supported by panel (in DSC mode: %s)\n",
intel_dp->psr.sink_panel_replay_su_support ?
- "selective_update " : "");
+ "selective_update " : "",
+ panel_replay_dsc_support_str(intel_dp->psr.sink_panel_replay_dsc_support));
}
static void _psr_init_dpcd(struct intel_dp *intel_dp)
@@ -888,7 +936,8 @@ static bool is_dc5_dc6_blocked(struct intel_dp *intel_dp)
{
struct intel_display *display = to_intel_display(intel_dp);
u32 current_dc_state = intel_display_power_get_current_dc_state(display);
- struct drm_vblank_crtc *vblank = &display->drm->vblank[intel_dp->psr.pipe];
+ struct intel_crtc *crtc = intel_crtc_for_pipe(display, intel_dp->psr.pipe);
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(&crtc->base);
return (current_dc_state != DC_STATE_EN_UPTO_DC5 &&
current_dc_state != DC_STATE_EN_UPTO_DC6) ||
@@ -956,15 +1005,16 @@ static u32 intel_psr2_get_tp_time(struct intel_dp *intel_dp)
return val;
}
-static int psr2_block_count_lines(struct intel_dp *intel_dp)
+static int
+psr2_block_count_lines(u8 io_wake_lines, u8 fast_wake_lines)
{
- return intel_dp->alpm_parameters.io_wake_lines < 9 &&
- intel_dp->alpm_parameters.fast_wake_lines < 9 ? 8 : 12;
+ return io_wake_lines < 9 && fast_wake_lines < 9 ? 8 : 12;
}
static int psr2_block_count(struct intel_dp *intel_dp)
{
- return psr2_block_count_lines(intel_dp) / 4;
+ return psr2_block_count_lines(intel_dp->psr.io_wake_lines,
+ intel_dp->psr.fast_wake_lines) / 4;
}
static u8 frames_before_su_entry(struct intel_dp *intel_dp)
@@ -1059,20 +1109,20 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp)
*/
int tmp;
- tmp = map[intel_dp->alpm_parameters.io_wake_lines -
+ tmp = map[intel_dp->psr.io_wake_lines -
TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES];
val |= TGL_EDP_PSR2_IO_BUFFER_WAKE(tmp + TGL_EDP_PSR2_IO_BUFFER_WAKE_MIN_LINES);
- tmp = map[intel_dp->alpm_parameters.fast_wake_lines - TGL_EDP_PSR2_FAST_WAKE_MIN_LINES];
+ tmp = map[intel_dp->psr.fast_wake_lines - TGL_EDP_PSR2_FAST_WAKE_MIN_LINES];
val |= TGL_EDP_PSR2_FAST_WAKE(tmp + TGL_EDP_PSR2_FAST_WAKE_MIN_LINES);
} else if (DISPLAY_VER(display) >= 20) {
- val |= LNL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->alpm_parameters.io_wake_lines);
+ val |= LNL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->psr.io_wake_lines);
} else if (DISPLAY_VER(display) >= 12) {
- val |= TGL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->alpm_parameters.io_wake_lines);
- val |= TGL_EDP_PSR2_FAST_WAKE(intel_dp->alpm_parameters.fast_wake_lines);
+ val |= TGL_EDP_PSR2_IO_BUFFER_WAKE(intel_dp->psr.io_wake_lines);
+ val |= TGL_EDP_PSR2_FAST_WAKE(intel_dp->psr.fast_wake_lines);
} else if (DISPLAY_VER(display) >= 9) {
- val |= EDP_PSR2_IO_BUFFER_WAKE(intel_dp->alpm_parameters.io_wake_lines);
- val |= EDP_PSR2_FAST_WAKE(intel_dp->alpm_parameters.fast_wake_lines);
+ val |= EDP_PSR2_IO_BUFFER_WAKE(intel_dp->psr.io_wake_lines);
+ val |= EDP_PSR2_FAST_WAKE(intel_dp->psr.fast_wake_lines);
}
if (intel_dp->psr.req_psr2_sdp_prior_scanline)
@@ -1251,12 +1301,6 @@ static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp,
return false;
}
- if (crtc_state->uapi.async_flip) {
- drm_dbg_kms(display->drm,
- "PSR2 sel fetch not enabled, async flip enabled\n");
- return false;
- }
-
return crtc_state->enable_psr2_sel_fetch = true;
}
@@ -1360,22 +1404,54 @@ static int intel_psr_entry_setup_frames(struct intel_dp *intel_dp,
return entry_setup_frames;
}
-static bool wake_lines_fit_into_vblank(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state,
- bool aux_less)
+static
+int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state,
+ bool needs_panel_replay,
+ bool needs_sel_update)
{
- struct intel_display *display = to_intel_display(intel_dp);
- int vblank = crtc_state->hw.adjusted_mode.crtc_vblank_end -
- crtc_state->hw.adjusted_mode.crtc_vblank_start;
- int wake_lines;
+ struct intel_display *display = to_intel_display(crtc_state);
- if (aux_less)
- wake_lines = intel_dp->alpm_parameters.aux_less_wake_lines;
+ if (!crtc_state->has_psr)
+ return 0;
+
+ /* Wa_14015401596 */
+ if (intel_vrr_possible(crtc_state) && IS_DISPLAY_VER(display, 13, 14))
+ return 1;
+
+ /* Rest is for SRD_STATUS needed on LunarLake and onwards */
+ if (DISPLAY_VER(display) < 20)
+ return 0;
+
+ /*
+ * Comment on SRD_STATUS register in Bspec for LunarLake and onwards:
+ *
+ * To deterministically capture the transition of the state machine
+ * going from SRDOFFACK to IDLE, the delayed V. Blank should be at least
+ * one line after the non-delayed V. Blank.
+ *
+ * Legacy TG: TRANS_SET_CONTEXT_LATENCY > 0
+ * VRR TG: TRANS_VRR_CTL[ VRR Guardband ] < (TRANS_VRR_VMAX[ VRR Vmax ]
+ * - TRANS_VTOTAL[ Vertical Active ])
+ *
+ * SRD_STATUS is used only by PSR1 on PantherLake.
+ * SRD_STATUS is used by PSR1 and Panel Replay DP on LunarLake.
+ */
+
+ if (DISPLAY_VER(display) >= 30 && (needs_panel_replay ||
+ needs_sel_update))
+ return 0;
+ else if (DISPLAY_VER(display) < 30 && (needs_sel_update ||
+ intel_crtc_has_type(crtc_state,
+ INTEL_OUTPUT_EDP)))
+ return 0;
else
- wake_lines = DISPLAY_VER(display) < 20 ?
- psr2_block_count_lines(intel_dp) :
- intel_dp->alpm_parameters.io_wake_lines;
+ return 1;
+}
+static bool _wake_lines_fit_into_vblank(const struct intel_crtc_state *crtc_state,
+ int vblank,
+ int wake_lines)
+{
if (crtc_state->req_psr2_sdp_prior_scanline)
vblank -= 1;
@@ -1386,9 +1462,46 @@ static bool wake_lines_fit_into_vblank(struct intel_dp *intel_dp,
return true;
}
+static bool wake_lines_fit_into_vblank(struct intel_dp *intel_dp,
+ const struct intel_crtc_state *crtc_state,
+ bool aux_less,
+ bool needs_panel_replay,
+ bool needs_sel_update)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+ int vblank = crtc_state->hw.adjusted_mode.crtc_vblank_end -
+ crtc_state->hw.adjusted_mode.crtc_vblank_start;
+ int wake_lines;
+ int scl = _intel_psr_min_set_context_latency(crtc_state,
+ needs_panel_replay,
+ needs_sel_update);
+ vblank -= scl;
+
+ if (aux_less)
+ wake_lines = crtc_state->alpm_state.aux_less_wake_lines;
+ else
+ wake_lines = DISPLAY_VER(display) < 20 ?
+ psr2_block_count_lines(crtc_state->alpm_state.io_wake_lines,
+ crtc_state->alpm_state.fast_wake_lines) :
+ crtc_state->alpm_state.io_wake_lines;
+
+ /*
+ * Guardband has not been computed yet, so we conservatively check if the
+ * full vblank duration is sufficient to accommodate wake line requirements
+ * for PSR features like Panel Replay and Selective Update.
+ *
+ * Once the actual guardband is available, a more accurate validation is
+ * performed in intel_psr_compute_config_late(), and PSR features are
+ * disabled if wake lines exceed the available guardband.
+ */
+ return _wake_lines_fit_into_vblank(crtc_state, vblank, wake_lines);
+}
+
static bool alpm_config_valid(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state,
- bool aux_less)
+ struct intel_crtc_state *crtc_state,
+ bool aux_less,
+ bool needs_panel_replay,
+ bool needs_sel_update)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -1398,7 +1511,8 @@ static bool alpm_config_valid(struct intel_dp *intel_dp,
return false;
}
- if (!wake_lines_fit_into_vblank(intel_dp, crtc_state, aux_less)) {
+ if (!wake_lines_fit_into_vblank(intel_dp, crtc_state, aux_less,
+ needs_panel_replay, needs_sel_update)) {
drm_dbg_kms(display->drm,
"PSR2/Panel Replay not enabled, too short vblank time\n");
return false;
@@ -1490,7 +1604,7 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp,
return false;
}
- if (!alpm_config_valid(intel_dp, crtc_state, false))
+ if (!alpm_config_valid(intel_dp, crtc_state, false, false, true))
return false;
if (!crtc_state->enable_psr2_sel_fetch &&
@@ -1535,9 +1649,21 @@ static bool intel_sel_update_config_valid(struct intel_dp *intel_dp,
goto unsupported;
}
- if (crtc_state->has_panel_replay && (DISPLAY_VER(display) < 14 ||
- !intel_dp->psr.sink_panel_replay_su_support))
- goto unsupported;
+ if (crtc_state->has_panel_replay) {
+ if (DISPLAY_VER(display) < 14)
+ goto unsupported;
+
+ if (!intel_dp->psr.sink_panel_replay_su_support)
+ goto unsupported;
+
+ if (intel_dsc_enabled_on_link(crtc_state) &&
+ intel_dp->psr.sink_panel_replay_dsc_support !=
+ INTEL_DP_PANEL_REPLAY_DSC_SELECTIVE_UPDATE) {
+ drm_dbg_kms(display->drm,
+ "Selective update with Panel Replay not enabled because it's not supported with DSC\n");
+ goto unsupported;
+ }
+ }
if (crtc_state->crc_enabled) {
drm_dbg_kms(display->drm,
@@ -1582,6 +1708,7 @@ static bool _psr_compute_config(struct intel_dp *intel_dp,
if (entry_setup_frames >= 0) {
intel_dp->psr.entry_setup_frames = entry_setup_frames;
} else {
+ crtc_state->no_psr_reason = "PSR setup timing not met";
drm_dbg_kms(display->drm,
"PSR condition failed: PSR setup timing not met\n");
return false;
@@ -1592,7 +1719,7 @@ static bool _psr_compute_config(struct intel_dp *intel_dp,
static bool
_panel_replay_compute_config(struct intel_dp *intel_dp,
- const struct intel_crtc_state *crtc_state,
+ struct intel_crtc_state *crtc_state,
const struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(intel_dp);
@@ -1614,6 +1741,14 @@ _panel_replay_compute_config(struct intel_dp *intel_dp,
return false;
}
+ if (intel_dsc_enabled_on_link(crtc_state) &&
+ intel_dp->psr.sink_panel_replay_dsc_support ==
+ INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED) {
+ drm_dbg_kms(display->drm,
+ "Panel Replay not enabled because it's not supported with DSC\n");
+ return false;
+ }
+
if (!intel_dp_is_edp(intel_dp))
return true;
@@ -1641,7 +1776,7 @@ _panel_replay_compute_config(struct intel_dp *intel_dp,
return false;
}
- if (!alpm_config_valid(intel_dp, crtc_state, true))
+ if (!alpm_config_valid(intel_dp, crtc_state, true, true, false))
return false;
return true;
@@ -1656,15 +1791,40 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp,
!crtc_state->has_sel_update);
}
+static
+void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp,
+ struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+ struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state);
+ struct intel_crtc *crtc;
+ u8 active_pipes = 0;
+
+ /* Wa_16025596647 */
+ if (DISPLAY_VER(display) != 20 &&
+ !IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0))
+ return;
+
+ /* Not needed by Panel Replay */
+ if (crtc_state->has_panel_replay)
+ return;
+
+ /* We ignore possible secondary PSR/Panel Replay capable eDP */
+ for_each_intel_crtc(display->drm, crtc)
+ active_pipes |= crtc->active ? BIT(crtc->pipe) : 0;
+
+ active_pipes = intel_calc_active_pipes(state, active_pipes);
+
+ crtc_state->active_non_psr_pipes = active_pipes &
+ ~BIT(to_intel_crtc(crtc_state->uapi.crtc)->pipe);
+}
+
void intel_psr_compute_config(struct intel_dp *intel_dp,
struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct intel_display *display = to_intel_display(intel_dp);
const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
- struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state);
- struct intel_crtc *crtc;
- u8 active_pipes = 0;
if (!psr_global_enabled(intel_dp)) {
drm_dbg_kms(display->drm, "PSR disabled by flag\n");
@@ -1694,6 +1854,8 @@ void intel_psr_compute_config(struct intel_dp *intel_dp,
return;
}
+ /* Only used for state verification. */
+ crtc_state->panel_replay_dsc_support = intel_dp->psr.sink_panel_replay_dsc_support;
crtc_state->has_panel_replay = _panel_replay_compute_config(intel_dp,
crtc_state,
conn_state);
@@ -1705,31 +1867,6 @@ void intel_psr_compute_config(struct intel_dp *intel_dp,
return;
crtc_state->has_sel_update = intel_sel_update_config_valid(intel_dp, crtc_state);
-
- /* Wa_18037818876 */
- if (intel_psr_needs_wa_18037818876(intel_dp, crtc_state)) {
- crtc_state->has_psr = false;
- drm_dbg_kms(display->drm,
- "PSR disabled to workaround PSR FSM hang issue\n");
- }
-
- /* Rest is for Wa_16025596647 */
- if (DISPLAY_VER(display) != 20 &&
- !IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0))
- return;
-
- /* Not needed by Panel Replay */
- if (crtc_state->has_panel_replay)
- return;
-
- /* We ignore possible secondary PSR/Panel Replay capable eDP */
- for_each_intel_crtc(display->drm, crtc)
- active_pipes |= crtc->active ? BIT(crtc->pipe) : 0;
-
- active_pipes = intel_calc_active_pipes(state, active_pipes);
-
- crtc_state->active_non_psr_pipes = active_pipes &
- ~BIT(to_intel_crtc(crtc_state->uapi.crtc)->pipe);
}
void intel_psr_get_config(struct intel_encoder *encoder,
@@ -1813,6 +1950,7 @@ static void intel_psr_activate(struct intel_dp *intel_dp)
hsw_activate_psr1(intel_dp);
intel_dp->psr.active = true;
+ intel_dp->psr.no_psr_reason = NULL;
}
/*
@@ -2022,6 +2160,8 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp,
crtc_state->req_psr2_sdp_prior_scanline;
intel_dp->psr.active_non_psr_pipes = crtc_state->active_non_psr_pipes;
intel_dp->psr.pkg_c_latency_used = crtc_state->pkg_c_latency_used;
+ intel_dp->psr.io_wake_lines = crtc_state->alpm_state.io_wake_lines;
+ intel_dp->psr.fast_wake_lines = crtc_state->alpm_state.fast_wake_lines;
if (!psr_interrupt_error_check(intel_dp))
return;
@@ -2131,8 +2271,8 @@ static void intel_psr_wait_exit_locked(struct intel_dp *intel_dp)
}
/* Wait till PSR is idle */
- if (intel_de_wait_for_clear(display, psr_status,
- psr_status_mask, 2000))
+ if (intel_de_wait_for_clear_ms(display, psr_status,
+ psr_status_mask, 2000))
drm_err(display->drm, "Timed out waiting PSR idle state\n");
}
@@ -2360,50 +2500,17 @@ void intel_psr_trigger_frame_change_event(struct intel_dsb *dsb,
}
/**
- * intel_psr_min_vblank_delay - Minimum vblank delay needed by PSR
+ * intel_psr_min_set_context_latency - Minimum 'set context latency' lines needed by PSR
* @crtc_state: the crtc state
*
- * Return minimum vblank delay needed by PSR.
+ * Return minimum SCL lines/delay needed by PSR.
*/
-int intel_psr_min_vblank_delay(const struct intel_crtc_state *crtc_state)
+int intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state)
{
- struct intel_display *display = to_intel_display(crtc_state);
-
- if (!crtc_state->has_psr)
- return 0;
-
- /* Wa_14015401596 */
- if (intel_vrr_possible(crtc_state) && IS_DISPLAY_VER(display, 13, 14))
- return 1;
- /* Rest is for SRD_STATUS needed on LunarLake and onwards */
- if (DISPLAY_VER(display) < 20)
- return 0;
-
- /*
- * Comment on SRD_STATUS register in Bspec for LunarLake and onwards:
- *
- * To deterministically capture the transition of the state machine
- * going from SRDOFFACK to IDLE, the delayed V. Blank should be at least
- * one line after the non-delayed V. Blank.
- *
- * Legacy TG: TRANS_SET_CONTEXT_LATENCY > 0
- * VRR TG: TRANS_VRR_CTL[ VRR Guardband ] < (TRANS_VRR_VMAX[ VRR Vmax ]
- * - TRANS_VTOTAL[ Vertical Active ])
- *
- * SRD_STATUS is used only by PSR1 on PantherLake.
- * SRD_STATUS is used by PSR1 and Panel Replay DP on LunarLake.
- */
-
- if (DISPLAY_VER(display) >= 30 && (crtc_state->has_panel_replay ||
- crtc_state->has_sel_update))
- return 0;
- else if (DISPLAY_VER(display) < 30 && (crtc_state->has_sel_update ||
- intel_crtc_has_type(crtc_state,
- INTEL_OUTPUT_EDP)))
- return 0;
- else
- return 1;
+ return _intel_psr_min_set_context_latency(crtc_state,
+ crtc_state->has_panel_replay,
+ crtc_state->has_sel_update);
}
static u32 man_trk_ctl_enable_bit_get(struct intel_display *display)
@@ -2925,6 +3032,9 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state,
mutex_lock(&psr->lock);
+ if (!new_crtc_state->has_psr)
+ psr->no_psr_reason = new_crtc_state->no_psr_reason;
+
if (psr->enabled) {
/*
* Reasons to disable:
@@ -2951,6 +3061,20 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state,
}
}
+static void
+verify_panel_replay_dsc_state(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ if (!crtc_state->has_panel_replay)
+ return;
+
+ drm_WARN_ON(display->drm,
+ intel_dsc_enabled_on_link(crtc_state) &&
+ crtc_state->panel_replay_dsc_support ==
+ INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED);
+}
+
void intel_psr_post_plane_update(struct intel_atomic_state *state,
struct intel_crtc *crtc)
{
@@ -2962,6 +3086,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
if (!crtc_state->has_psr)
return;
+ verify_panel_replay_dsc_state(crtc_state);
+
for_each_intel_encoder_mask_with_psr(state->base.dev, encoder,
crtc_state->uapi.encoder_mask) {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
@@ -2973,12 +3099,19 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state,
drm_WARN_ON(display->drm,
psr->enabled && !crtc_state->active_planes);
- keep_disabled |= psr->sink_not_reliable;
- keep_disabled |= !crtc_state->active_planes;
+ if (psr->sink_not_reliable)
+ keep_disabled = true;
+
+ if (!crtc_state->active_planes) {
+ psr->no_psr_reason = "All planes inactive";
+ keep_disabled = true;
+ }
/* Display WA #1136: skl, bxt */
- keep_disabled |= DISPLAY_VER(display) < 11 &&
- crtc_state->wm_level_disabled;
+ if (DISPLAY_VER(display) < 11 && crtc_state->wm_level_disabled) {
+ psr->no_psr_reason = "Workaround #1136 for skl, bxt";
+ keep_disabled = true;
+ }
if (!psr->enabled && !keep_disabled)
intel_psr_enable_locked(intel_dp, crtc_state);
@@ -3027,7 +3160,7 @@ _psr2_ready_for_pipe_update_locked(const struct intel_crtc_state *new_crtc_state
return true;
}
- return intel_de_wait_for_clear(display,
+ return intel_de_wait_for_clear_ms(display,
EDP_PSR2_STATUS(display, cpu_transcoder),
EDP_PSR2_STATUS_STATE_DEEP_SLEEP,
PSR_IDLE_TIMEOUT_MS);
@@ -3047,7 +3180,7 @@ _psr1_ready_for_pipe_update_locked(const struct intel_crtc_state *new_crtc_state
return true;
}
- return intel_de_wait_for_clear(display,
+ return intel_de_wait_for_clear_ms(display,
psr_status_reg(display, cpu_transcoder),
EDP_PSR_STATUS_STATE_MASK,
PSR_IDLE_TIMEOUT_MS);
@@ -3125,7 +3258,7 @@ static bool __psr_wait_for_idle_locked(struct intel_dp *intel_dp)
mutex_unlock(&intel_dp->psr.lock);
- err = intel_de_wait_for_clear(display, reg, mask, 50);
+ err = intel_de_wait_for_clear_ms(display, reg, mask, 50);
if (err)
drm_err(display->drm,
"Timed out waiting for PSR Idle for re-enable\n");
@@ -3991,6 +4124,8 @@ static void intel_psr_sink_capability(struct intel_dp *intel_dp,
seq_printf(m, ", Panel Replay = %s", str_yes_no(psr->sink_panel_replay_support));
seq_printf(m, ", Panel Replay Selective Update = %s",
str_yes_no(psr->sink_panel_replay_su_support));
+ seq_printf(m, ", Panel Replay DSC support = %s",
+ panel_replay_dsc_support_str(psr->sink_panel_replay_dsc_support));
if (intel_dp->pr_dpcd[INTEL_PR_DPCD_INDEX(DP_PANEL_REPLAY_CAP_SUPPORT)] &
DP_PANEL_REPLAY_EARLY_TRANSPORT_SUPPORT)
seq_printf(m, " (Early Transport)");
@@ -4025,6 +4160,8 @@ static void intel_psr_print_mode(struct intel_dp *intel_dp,
region_et = "";
seq_printf(m, "PSR mode: %s%s%s\n", mode, status, region_et);
+ if (psr->no_psr_reason)
+ seq_printf(m, " %s\n", psr->no_psr_reason);
}
static int intel_psr_status(struct seq_file *m, struct intel_dp *intel_dp)
@@ -4322,3 +4459,84 @@ bool intel_psr_needs_alpm_aux_less(struct intel_dp *intel_dp,
{
return intel_dp_is_edp(intel_dp) && crtc_state->has_panel_replay;
}
+
+void intel_psr_compute_config_late(struct intel_dp *intel_dp,
+ struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(intel_dp);
+ int vblank = intel_crtc_vblank_length(crtc_state);
+ int wake_lines;
+
+ if (intel_psr_needs_alpm_aux_less(intel_dp, crtc_state))
+ wake_lines = crtc_state->alpm_state.aux_less_wake_lines;
+ else if (intel_psr_needs_alpm(intel_dp, crtc_state))
+ wake_lines = DISPLAY_VER(display) < 20 ?
+ psr2_block_count_lines(crtc_state->alpm_state.io_wake_lines,
+ crtc_state->alpm_state.fast_wake_lines) :
+ crtc_state->alpm_state.io_wake_lines;
+ else
+ wake_lines = 0;
+
+ /*
+ * Disable the PSR features if wake lines exceed the available vblank.
+ * Though SCL is computed based on these PSR features, it is not reset
+ * even if the PSR features are disabled to avoid changing vblank start
+ * at this stage.
+ */
+ if (wake_lines && !_wake_lines_fit_into_vblank(crtc_state, vblank, wake_lines)) {
+ drm_dbg_kms(display->drm,
+ "Adjusting PSR/PR mode: vblank too short for wake lines = %d\n",
+ wake_lines);
+
+ if (crtc_state->has_panel_replay) {
+ crtc_state->has_panel_replay = false;
+ /*
+ * #TODO : Add fall back to PSR/PSR2
+ * Since panel replay cannot be supported, we can fall back to PSR/PSR2.
+ * This will require calling compute_config for psr and psr2 with check for
+ * actual guardband instead of vblank_length.
+ */
+ crtc_state->has_psr = false;
+ }
+
+ crtc_state->has_sel_update = false;
+ crtc_state->enable_psr2_su_region_et = false;
+ crtc_state->enable_psr2_sel_fetch = false;
+ }
+
+ /* Wa_18037818876 */
+ if (intel_psr_needs_wa_18037818876(intel_dp, crtc_state)) {
+ crtc_state->has_psr = false;
+ drm_dbg_kms(display->drm,
+ "PSR disabled to workaround PSR FSM hang issue\n");
+ }
+
+ intel_psr_set_non_psr_pipes(intel_dp, crtc_state);
+}
+
+int intel_psr_min_guardband(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int psr_min_guardband;
+ int wake_lines;
+
+ if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
+ return 0;
+
+ if (crtc_state->has_panel_replay)
+ wake_lines = crtc_state->alpm_state.aux_less_wake_lines;
+ else if (crtc_state->has_sel_update)
+ wake_lines = DISPLAY_VER(display) < 20 ?
+ psr2_block_count_lines(crtc_state->alpm_state.io_wake_lines,
+ crtc_state->alpm_state.fast_wake_lines) :
+ crtc_state->alpm_state.io_wake_lines;
+ else
+ return 0;
+
+ psr_min_guardband = wake_lines + crtc_state->set_context_latency;
+
+ if (crtc_state->req_psr2_sdp_prior_scanline)
+ psr_min_guardband++;
+
+ return psr_min_guardband;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h
index 077751aa599f..620b35928832 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.h
+++ b/drivers/gpu/drm/i915/display/intel_psr.h
@@ -77,11 +77,14 @@ void intel_psr_unlock(const struct intel_crtc_state *crtc_state);
void intel_psr_trigger_frame_change_event(struct intel_dsb *dsb,
struct intel_atomic_state *state,
struct intel_crtc *crtc);
-int intel_psr_min_vblank_delay(const struct intel_crtc_state *crtc_state);
+int intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state);
void intel_psr_connector_debugfs_add(struct intel_connector *connector);
void intel_psr_debugfs_register(struct intel_display *display);
bool intel_psr_needs_alpm(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state);
bool intel_psr_needs_alpm_aux_less(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state);
+void intel_psr_compute_config_late(struct intel_dp *intel_dp,
+ struct intel_crtc_state *crtc_state);
+int intel_psr_min_guardband(struct intel_crtc_state *crtc_state);
#endif /* __INTEL_PSR_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_qp_tables.c b/drivers/gpu/drm/i915/display/intel_qp_tables.c
index 600c815e37e4..c05d4beb91d8 100644
--- a/drivers/gpu/drm/i915/display/intel_qp_tables.c
+++ b/drivers/gpu/drm/i915/display/intel_qp_tables.c
@@ -5,7 +5,7 @@
#include <drm/display/drm_dsc.h>
-#include "i915_utils.h"
+#include "intel_display_utils.h"
#include "intel_qp_tables.h"
/* from BPP 6 to 24 in steps of 0.5 */
diff --git a/drivers/gpu/drm/i915/display/intel_sbi.c b/drivers/gpu/drm/i915/display/intel_sbi.c
index dfcff924f0ed..b636a0060d39 100644
--- a/drivers/gpu/drm/i915/display/intel_sbi.c
+++ b/drivers/gpu/drm/i915/display/intel_sbi.c
@@ -21,7 +21,8 @@ static int intel_sbi_rw(struct intel_display *display, u16 reg,
lockdep_assert_held(&display->sbi.lock);
- if (intel_de_wait_fw(display, SBI_CTL_STAT, SBI_STATUS_MASK, SBI_STATUS_READY, 100, NULL)) {
+ if (intel_de_wait_fw_ms(display, SBI_CTL_STAT,
+ SBI_STATUS_MASK, SBI_STATUS_READY, 100, NULL)) {
drm_err(display->drm, "timeout waiting for SBI to become ready\n");
return -EBUSY;
}
@@ -37,7 +38,8 @@ static int intel_sbi_rw(struct intel_display *display, u16 reg,
cmd |= SBI_CTL_OP_WR;
intel_de_write_fw(display, SBI_CTL_STAT, cmd | SBI_STATUS_BUSY);
- if (intel_de_wait_fw(display, SBI_CTL_STAT, SBI_STATUS_MASK, SBI_STATUS_READY, 100, &cmd)) {
+ if (intel_de_wait_fw_ms(display, SBI_CTL_STAT,
+ SBI_STATUS_MASK, SBI_STATUS_READY, 100, &cmd)) {
drm_err(display->drm, "timeout waiting for SBI to complete read\n");
return -ETIMEDOUT;
}
diff --git a/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c b/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c
index 7fe6b4a18213..a201edceee10 100644
--- a/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c
+++ b/drivers/gpu/drm/i915/display/intel_snps_hdmi_pll.c
@@ -332,6 +332,8 @@ void intel_snps_hdmi_pll_compute_c10pll(struct intel_c10pll_state *pll_state, u6
c10_curve_1, c10_curve_2, prescaler_divider,
&pll_params);
+ pll_state->clock = pixel_clock;
+
pll_state->tx = 0x10;
pll_state->cmn = 0x1;
pll_state->pll[0] = REG_FIELD_PREP(C10_PLL0_DIV5CLK_EN, pll_params.mpll_div5_en) |
diff --git a/drivers/gpu/drm/i915/display/intel_snps_phy.c b/drivers/gpu/drm/i915/display/intel_snps_phy.c
index b2dd69a11124..295030742294 100644
--- a/drivers/gpu/drm/i915/display/intel_snps_phy.c
+++ b/drivers/gpu/drm/i915/display/intel_snps_phy.c
@@ -7,12 +7,12 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_ddi.h"
#include "intel_ddi_buf_trans.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_snps_hdmi_pll.h"
#include "intel_snps_phy.h"
#include "intel_snps_phy_regs.h"
@@ -42,8 +42,8 @@ void intel_snps_phy_wait_for_calibration(struct intel_display *display)
* which phy was affected and skip setup of the corresponding
* output later.
*/
- if (intel_de_wait_for_clear(display, DG2_PHY_MISC(phy),
- DG2_PHY_DP_TX_ACK_MASK, 25))
+ if (intel_de_wait_for_clear_ms(display, DG2_PHY_MISC(phy),
+ DG2_PHY_DP_TX_ACK_MASK, 25))
display->snps.phy_failed_calibration |= BIT(phy);
}
}
@@ -1863,7 +1863,7 @@ void intel_mpllb_enable(struct intel_encoder *encoder,
* is locked at new settings. This register bit is sampling PHY
* dp_mpllb_state interface signal.
*/
- if (intel_de_wait_for_set(display, enable_reg, PLL_LOCK, 5))
+ if (intel_de_wait_for_set_ms(display, enable_reg, PLL_LOCK, 5))
drm_dbg_kms(display->drm, "Port %c PLL not locked\n", phy_name(phy));
/*
@@ -1903,7 +1903,7 @@ void intel_mpllb_disable(struct intel_encoder *encoder)
* 5. Software polls DPLL_ENABLE [PLL Lock] for PHY acknowledgment
* (dp_txX_ack) that the new transmitter setting request is completed.
*/
- if (intel_de_wait_for_clear(display, enable_reg, PLL_LOCK, 5))
+ if (intel_de_wait_for_clear_ms(display, enable_reg, PLL_LOCK, 5))
drm_err(display->drm, "Port %c PLL not locked\n", phy_name(phy));
/*
diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c
index 75bbaa923204..69b6873a6044 100644
--- a/drivers/gpu/drm/i915/display/intel_sprite.c
+++ b/drivers/gpu/drm/i915/display/intel_sprite.c
@@ -39,10 +39,10 @@
#include <drm/drm_print.h>
#include <drm/drm_rect.h>
-#include "i915_utils.h"
#include "i9xx_plane.h"
#include "intel_de.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_fb.h"
#include "intel_frontbuffer.h"
#include "intel_plane.h"
@@ -958,10 +958,9 @@ static int g4x_sprite_min_cdclk(const struct intel_crtc_state *crtc_state,
static unsigned int
g4x_sprite_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
- const struct drm_format_info *info = drm_format_info(pixel_format);
int cpp = info->cpp[0];
/* Limit to 4k pixels to guarantee TILEOFF.x doesn't get too big. */
@@ -973,10 +972,9 @@ g4x_sprite_max_stride(struct intel_plane *plane,
static unsigned int
hsw_sprite_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
- const struct drm_format_info *info = drm_format_info(pixel_format);
int cpp = info->cpp[0];
/* Limit to 8k pixels to guarantee OFFSET.x doesn't get too big. */
diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c
index c4a5601c5107..1e21fd02685d 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.c
+++ b/drivers/gpu/drm/i915/display/intel_tc.c
@@ -8,7 +8,6 @@
#include <drm/drm_print.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_cx0_phy_regs.h"
#include "intel_ddi.h"
@@ -18,6 +17,7 @@
#include "intel_display_power_map.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dkl_phy_regs.h"
#include "intel_dp.h"
#include "intel_dp_mst.h"
@@ -1076,8 +1076,8 @@ xelpdp_tc_phy_wait_for_tcss_power(struct intel_tc_port *tc, bool enabled)
static void xelpdp_tc_power_request_wa(struct intel_display *display, bool enable)
{
/* check if mailbox is running busy */
- if (intel_de_wait_for_clear(display, TCSS_DISP_MAILBOX_IN_CMD,
- TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY, 10)) {
+ if (intel_de_wait_for_clear_ms(display, TCSS_DISP_MAILBOX_IN_CMD,
+ TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY, 10)) {
drm_dbg_kms(display->drm,
"Timeout waiting for TCSS mailbox run/busy bit to clear\n");
return;
@@ -1089,8 +1089,8 @@ static void xelpdp_tc_power_request_wa(struct intel_display *display, bool enabl
TCSS_DISP_MAILBOX_IN_CMD_DATA(0x1));
/* wait to clear mailbox running busy bit before continuing */
- if (intel_de_wait_for_clear(display, TCSS_DISP_MAILBOX_IN_CMD,
- TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY, 10)) {
+ if (intel_de_wait_for_clear_ms(display, TCSS_DISP_MAILBOX_IN_CMD,
+ TCSS_DISP_MAILBOX_IN_CMD_RUN_BUSY, 10)) {
drm_dbg_kms(display->drm,
"Timeout after writing data to mailbox. Mailbox run/busy bit did not clear\n");
return;
@@ -1703,6 +1703,19 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port,
mutex_unlock(&tc->lock);
}
+void intel_tc_info(struct drm_printer *p, struct intel_digital_port *dig_port)
+{
+ struct intel_tc_port *tc = to_tc_port(dig_port);
+
+ intel_tc_port_lock(dig_port);
+ drm_printf(p, "\tTC Port %s: mode: %s, pin assignment: %c, max lanes: %d\n",
+ tc->port_name,
+ tc_port_mode_name(tc->mode),
+ pin_assignment_name(tc->pin_assignment),
+ tc->max_lane_count);
+ intel_tc_port_unlock(dig_port);
+}
+
/*
* The type-C ports are different because even when they are connected, they may
* not be available/usable by the graphics driver: see the comment on
diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h
index fff8b96e4972..6719aea5bd58 100644
--- a/drivers/gpu/drm/i915/display/intel_tc.h
+++ b/drivers/gpu/drm/i915/display/intel_tc.h
@@ -8,6 +8,7 @@
#include <linux/types.h>
+struct drm_printer;
struct intel_crtc_state;
struct intel_digital_port;
struct intel_encoder;
@@ -113,4 +114,6 @@ void intel_tc_port_cleanup(struct intel_digital_port *dig_port);
bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port);
+void intel_tc_info(struct drm_printer *p, struct intel_digital_port *dig_port);
+
#endif /* __INTEL_TC_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_vblank.c b/drivers/gpu/drm/i915/display/intel_vblank.c
index c15234c1d96e..671f357c6563 100644
--- a/drivers/gpu/drm/i915/display/intel_vblank.c
+++ b/drivers/gpu/drm/i915/display/intel_vblank.c
@@ -5,15 +5,17 @@
#include <linux/iopoll.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "i915_drv.h"
-#include "i915_utils.h"
#include "intel_color.h"
#include "intel_crtc.h"
#include "intel_de.h"
+#include "intel_display_jiffies.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_vblank.h"
#include "intel_vrr.h"
@@ -681,7 +683,7 @@ void intel_vblank_evade_init(const struct intel_crtc_state *old_crtc_state,
else
evade->vblank_start = intel_vrr_vmax_vblank_start(crtc_state);
- vblank_delay = intel_vrr_vblank_delay(crtc_state);
+ vblank_delay = crtc_state->set_context_latency;
} else {
evade->vblank_start = intel_mode_vblank_start(adjusted_mode);
@@ -767,3 +769,13 @@ int intel_vblank_evade(struct intel_vblank_evade_ctx *evade)
return scanline;
}
+
+int intel_crtc_vblank_length(const struct intel_crtc_state *crtc_state)
+{
+ const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
+
+ if (crtc_state->vrr.enable)
+ return crtc_state->vrr.guardband;
+ else
+ return adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vblank_start;
+}
diff --git a/drivers/gpu/drm/i915/display/intel_vblank.h b/drivers/gpu/drm/i915/display/intel_vblank.h
index 21fbb08d61d5..98d04cacd65f 100644
--- a/drivers/gpu/drm/i915/display/intel_vblank.h
+++ b/drivers/gpu/drm/i915/display/intel_vblank.h
@@ -48,4 +48,6 @@ const struct intel_crtc_state *
intel_pre_commit_crtc_state(struct intel_atomic_state *state,
struct intel_crtc *crtc);
+int intel_crtc_vblank_length(const struct intel_crtc_state *crtc_state);
+
#endif /* __INTEL_VBLANK_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.c b/drivers/gpu/drm/i915/display/intel_vdsc.c
index 8e799e225af1..0e727fc5e80c 100644
--- a/drivers/gpu/drm/i915/display/intel_vdsc.c
+++ b/drivers/gpu/drm/i915/display/intel_vdsc.c
@@ -11,10 +11,10 @@
#include <drm/drm_fixed.h>
#include <drm/drm_print.h>
-#include "i915_utils.h"
#include "intel_crtc.h"
#include "intel_de.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dp.h"
#include "intel_dsi.h"
#include "intel_qp_tables.h"
@@ -372,6 +372,22 @@ int intel_dsc_compute_params(struct intel_crtc_state *pipe_config)
return 0;
}
+void intel_dsc_enable_on_crtc(struct intel_crtc_state *crtc_state)
+{
+ crtc_state->dsc.compression_enabled_on_link = true;
+ crtc_state->dsc.compression_enable = true;
+}
+
+bool intel_dsc_enabled_on_link(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ drm_WARN_ON(display->drm, crtc_state->dsc.compression_enable &&
+ !crtc_state->dsc.compression_enabled_on_link);
+
+ return crtc_state->dsc.compression_enabled_on_link;
+}
+
enum intel_display_power_domain
intel_dsc_power_domain(struct intel_crtc *crtc, enum transcoder cpu_transcoder)
{
@@ -1077,3 +1093,11 @@ int intel_vdsc_min_cdclk(const struct intel_crtc_state *crtc_state)
return min_cdclk;
}
+
+unsigned int intel_vdsc_prefill_lines(const struct intel_crtc_state *crtc_state)
+{
+ if (!crtc_state->dsc.compression_enable)
+ return 0;
+
+ return 0x18000; /* 1.5 */
+}
diff --git a/drivers/gpu/drm/i915/display/intel_vdsc.h b/drivers/gpu/drm/i915/display/intel_vdsc.h
index 9e2812f99dd7..99f64ac54b27 100644
--- a/drivers/gpu/drm/i915/display/intel_vdsc.h
+++ b/drivers/gpu/drm/i915/display/intel_vdsc.h
@@ -20,6 +20,8 @@ void intel_uncompressed_joiner_enable(const struct intel_crtc_state *crtc_state)
void intel_dsc_enable(const struct intel_crtc_state *crtc_state);
void intel_dsc_disable(const struct intel_crtc_state *crtc_state);
int intel_dsc_compute_params(struct intel_crtc_state *pipe_config);
+void intel_dsc_enable_on_crtc(struct intel_crtc_state *crtc_state);
+bool intel_dsc_enabled_on_link(const struct intel_crtc_state *crtc_state);
void intel_dsc_get_config(struct intel_crtc_state *crtc_state);
enum intel_display_power_domain
intel_dsc_power_domain(struct intel_crtc *crtc, enum transcoder cpu_transcoder);
@@ -32,5 +34,6 @@ void intel_dsc_dp_pps_write(struct intel_encoder *encoder,
void intel_vdsc_state_dump(struct drm_printer *p, int indent,
const struct intel_crtc_state *crtc_state);
int intel_vdsc_min_cdclk(const struct intel_crtc_state *crtc_state);
+unsigned int intel_vdsc_prefill_lines(const struct intel_crtc_state *crtc_state);
#endif /* __INTEL_VDSC_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c
index 3eed37f271b0..b92c42fde937 100644
--- a/drivers/gpu/drm/i915/display/intel_vrr.c
+++ b/drivers/gpu/drm/i915/display/intel_vrr.c
@@ -10,8 +10,11 @@
#include "intel_display_regs.h"
#include "intel_display_types.h"
#include "intel_dp.h"
+#include "intel_psr.h"
#include "intel_vrr.h"
#include "intel_vrr_regs.h"
+#include "skl_prefill.h"
+#include "skl_watermark.h"
#define FIXED_POINT_PRECISION 100
#define CMRR_PRECISION_TOLERANCE 10
@@ -22,6 +25,9 @@ bool intel_vrr_is_capable(struct intel_connector *connector)
const struct drm_display_info *info = &connector->base.display_info;
struct intel_dp *intel_dp;
+ if (!HAS_VRR(display))
+ return false;
+
/*
* DP Sink is capable of VRR video timings if
* Ignore MSA bit is set in DPCD.
@@ -46,8 +52,7 @@ bool intel_vrr_is_capable(struct intel_connector *connector)
return false;
}
- return HAS_VRR(display) &&
- info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10;
+ return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10;
}
bool intel_vrr_is_in_range(struct intel_connector *connector, int vrefresh)
@@ -79,44 +84,42 @@ intel_vrr_check_modeset(struct intel_atomic_state *state)
}
}
-static int intel_vrr_real_vblank_delay(const struct intel_crtc_state *crtc_state)
-{
- return crtc_state->hw.adjusted_mode.crtc_vblank_start -
- crtc_state->hw.adjusted_mode.crtc_vdisplay;
-}
-
static int intel_vrr_extra_vblank_delay(struct intel_display *display)
{
/*
* On ICL/TGL VRR hardware inserts one extra scanline
* just after vactive, which pushes the vmin decision
- * boundary ahead accordingly. We'll include the extra
- * scanline in our vblank delay estimates to make sure
- * that we never underestimate how long we have until
- * the delayed vblank has passed.
+ * boundary ahead accordingly, and thus reduces the
+ * max guardband length by one scanline.
*/
return DISPLAY_VER(display) < 13 ? 1 : 0;
}
-int intel_vrr_vblank_delay(const struct intel_crtc_state *crtc_state)
+static int intel_vrr_vmin_flipline_offset(struct intel_display *display)
{
- struct intel_display *display = to_intel_display(crtc_state);
-
- return intel_vrr_real_vblank_delay(crtc_state) +
- intel_vrr_extra_vblank_delay(display);
+ /*
+ * ICL/TGL hardware imposes flipline>=vmin+1
+ *
+ * We reduce the vmin value to compensate when programming the
+ * hardware. This approach allows flipline to remain set at the
+ * original value, and thus the frame will have the desired
+ * minimum vtotal.
+ */
+ return DISPLAY_VER(display) < 13 ? 1 : 0;
}
-static int intel_vrr_flipline_offset(struct intel_display *display)
+static int intel_vrr_guardband_to_pipeline_full(const struct intel_crtc_state *crtc_state,
+ int guardband)
{
- /* ICL/TGL hardware imposes flipline>=vmin+1 */
- return DISPLAY_VER(display) < 13 ? 1 : 0;
+ /* hardware imposes one extra scanline somewhere */
+ return guardband - crtc_state->framestart_delay - 1;
}
-static int intel_vrr_vmin_flipline(const struct intel_crtc_state *crtc_state)
+static int intel_vrr_pipeline_full_to_guardband(const struct intel_crtc_state *crtc_state,
+ int pipeline_full)
{
- struct intel_display *display = to_intel_display(crtc_state);
-
- return crtc_state->vrr.vmin + intel_vrr_flipline_offset(display);
+ /* hardware imposes one extra scanline somewhere */
+ return pipeline_full + crtc_state->framestart_delay + 1;
}
/*
@@ -135,48 +138,26 @@ static int intel_vrr_vmin_flipline(const struct intel_crtc_state *crtc_state)
*
* framestart_delay is programmable 1-4.
*/
-static int intel_vrr_vblank_exit_length(const struct intel_crtc_state *crtc_state)
-{
- struct intel_display *display = to_intel_display(crtc_state);
-
- if (DISPLAY_VER(display) >= 13)
- return crtc_state->vrr.guardband;
- else
- /* hardware imposes one extra scanline somewhere */
- return crtc_state->vrr.pipeline_full + crtc_state->framestart_delay + 1;
-}
int intel_vrr_vmin_vtotal(const struct intel_crtc_state *crtc_state)
{
- struct intel_display *display = to_intel_display(crtc_state);
-
/* Min vblank actually determined by flipline */
- if (DISPLAY_VER(display) >= 13)
- return intel_vrr_vmin_flipline(crtc_state);
- else
- return intel_vrr_vmin_flipline(crtc_state) +
- intel_vrr_real_vblank_delay(crtc_state);
+ return crtc_state->vrr.vmin;
}
int intel_vrr_vmax_vtotal(const struct intel_crtc_state *crtc_state)
{
- struct intel_display *display = to_intel_display(crtc_state);
-
- if (DISPLAY_VER(display) >= 13)
- return crtc_state->vrr.vmax;
- else
- return crtc_state->vrr.vmax +
- intel_vrr_real_vblank_delay(crtc_state);
+ return crtc_state->vrr.vmax;
}
int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state)
{
- return intel_vrr_vmin_vtotal(crtc_state) - intel_vrr_vblank_exit_length(crtc_state);
+ return intel_vrr_vmin_vtotal(crtc_state) - crtc_state->vrr.guardband;
}
int intel_vrr_vmax_vblank_start(const struct intel_crtc_state *crtc_state)
{
- return intel_vrr_vmax_vtotal(crtc_state) - intel_vrr_vblank_exit_length(crtc_state);
+ return intel_vrr_vmax_vtotal(crtc_state) - crtc_state->vrr.guardband;
}
static bool
@@ -230,7 +211,6 @@ cmrr_get_vtotal(struct intel_crtc_state *crtc_state, bool video_mode_required)
static
void intel_vrr_compute_cmrr_timings(struct intel_crtc_state *crtc_state)
{
- crtc_state->cmrr.enable = true;
/*
* TODO: Compute precise target refresh rate to determine
* if video_mode_required should be true. Currently set to
@@ -240,52 +220,76 @@ void intel_vrr_compute_cmrr_timings(struct intel_crtc_state *crtc_state)
crtc_state->vrr.vmax = cmrr_get_vtotal(crtc_state, false);
crtc_state->vrr.vmin = crtc_state->vrr.vmax;
crtc_state->vrr.flipline = crtc_state->vrr.vmin;
+
+ crtc_state->cmrr.enable = true;
crtc_state->mode_flags |= I915_MODE_FLAG_VRR;
}
static
-void intel_vrr_compute_vrr_timings(struct intel_crtc_state *crtc_state)
+void intel_vrr_compute_vrr_timings(struct intel_crtc_state *crtc_state,
+ int vmin, int vmax)
{
+ crtc_state->vrr.vmax = vmax;
+ crtc_state->vrr.vmin = vmin;
+ crtc_state->vrr.flipline = crtc_state->vrr.vmin;
+
crtc_state->vrr.enable = true;
crtc_state->mode_flags |= I915_MODE_FLAG_VRR;
}
-/*
- * For fixed refresh rate mode Vmin, Vmax and Flipline all are set to
- * Vtotal value.
- */
static
-int intel_vrr_fixed_rr_vtotal(const struct intel_crtc_state *crtc_state)
+void intel_vrr_compute_fixed_rr_timings(struct intel_crtc_state *crtc_state)
+{
+ /* For fixed rr, vmin = vmax = flipline */
+ crtc_state->vrr.vmax = crtc_state->hw.adjusted_mode.crtc_vtotal;
+ crtc_state->vrr.vmin = crtc_state->vrr.vmax;
+ crtc_state->vrr.flipline = crtc_state->vrr.vmin;
+}
+
+static int intel_vrr_hw_value(const struct intel_crtc_state *crtc_state,
+ int value)
{
struct intel_display *display = to_intel_display(crtc_state);
- int crtc_vtotal = crtc_state->hw.adjusted_mode.crtc_vtotal;
+ /*
+ * On TGL vmin/vmax/flipline also need to be
+ * adjusted by the SCL to maintain correct vtotals.
+ */
if (DISPLAY_VER(display) >= 13)
- return crtc_vtotal;
+ return value;
else
- return crtc_vtotal -
- intel_vrr_real_vblank_delay(crtc_state);
+ return value - crtc_state->set_context_latency;
+}
+
+/*
+ * For fixed refresh rate mode Vmin, Vmax and Flipline all are set to
+ * Vtotal value.
+ */
+static
+int intel_vrr_fixed_rr_hw_vtotal(const struct intel_crtc_state *crtc_state)
+{
+ return intel_vrr_hw_value(crtc_state, crtc_state->hw.adjusted_mode.crtc_vtotal);
}
static
-int intel_vrr_fixed_rr_vmax(const struct intel_crtc_state *crtc_state)
+int intel_vrr_fixed_rr_hw_vmax(const struct intel_crtc_state *crtc_state)
{
- return intel_vrr_fixed_rr_vtotal(crtc_state);
+ return intel_vrr_fixed_rr_hw_vtotal(crtc_state);
}
static
-int intel_vrr_fixed_rr_vmin(const struct intel_crtc_state *crtc_state)
+int intel_vrr_fixed_rr_hw_vmin(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
- return intel_vrr_fixed_rr_vtotal(crtc_state) -
- intel_vrr_flipline_offset(display);
+ return intel_vrr_fixed_rr_hw_vtotal(crtc_state) -
+ intel_vrr_vmin_flipline_offset(display);
}
static
-int intel_vrr_fixed_rr_flipline(const struct intel_crtc_state *crtc_state)
+int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state)
{
- return intel_vrr_fixed_rr_vtotal(crtc_state);
+ return intel_vrr_fixed_rr_hw_vtotal(crtc_state);
}
void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state)
@@ -297,22 +301,11 @@ void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state)
return;
intel_de_write(display, TRANS_VRR_VMIN(display, cpu_transcoder),
- intel_vrr_fixed_rr_vmin(crtc_state) - 1);
+ intel_vrr_fixed_rr_hw_vmin(crtc_state) - 1);
intel_de_write(display, TRANS_VRR_VMAX(display, cpu_transcoder),
- intel_vrr_fixed_rr_vmax(crtc_state) - 1);
+ intel_vrr_fixed_rr_hw_vmax(crtc_state) - 1);
intel_de_write(display, TRANS_VRR_FLIPLINE(display, cpu_transcoder),
- intel_vrr_fixed_rr_flipline(crtc_state) - 1);
-}
-
-static
-void intel_vrr_compute_fixed_rr_timings(struct intel_crtc_state *crtc_state)
-{
- /*
- * For fixed rr, vmin = vmax = flipline.
- * vmin is already set to crtc_vtotal set vmax and flipline the same.
- */
- crtc_state->vrr.vmax = crtc_state->hw.adjusted_mode.crtc_vtotal;
- crtc_state->vrr.flipline = crtc_state->hw.adjusted_mode.crtc_vtotal;
+ intel_vrr_fixed_rr_hw_flipline(crtc_state) - 1);
}
static
@@ -384,60 +377,131 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state,
vmax = vmin;
}
- crtc_state->vrr.vmin = vmin;
- crtc_state->vrr.vmax = vmax;
-
- crtc_state->vrr.flipline = crtc_state->vrr.vmin;
-
if (crtc_state->uapi.vrr_enabled && vmin < vmax)
- intel_vrr_compute_vrr_timings(crtc_state);
+ intel_vrr_compute_vrr_timings(crtc_state, vmin, vmax);
else if (is_cmrr_frac_required(crtc_state) && is_edp)
intel_vrr_compute_cmrr_timings(crtc_state);
else
intel_vrr_compute_fixed_rr_timings(crtc_state);
- /*
- * flipline determines the min vblank length the hardware will
- * generate, and on ICL/TGL flipline>=vmin+1, hence we reduce
- * vmin by one to make sure we can get the actual min vblank length.
- */
- crtc_state->vrr.vmin -= intel_vrr_flipline_offset(display);
-
if (HAS_AS_SDP(display)) {
crtc_state->vrr.vsync_start =
(crtc_state->hw.adjusted_mode.crtc_vtotal -
- crtc_state->hw.adjusted_mode.vsync_start);
+ crtc_state->hw.adjusted_mode.crtc_vsync_start);
crtc_state->vrr.vsync_end =
(crtc_state->hw.adjusted_mode.crtc_vtotal -
- crtc_state->hw.adjusted_mode.vsync_end);
+ crtc_state->hw.adjusted_mode.crtc_vsync_end);
}
}
-void intel_vrr_compute_config_late(struct intel_crtc_state *crtc_state)
+static int
+intel_vrr_max_hw_guardband(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int max_pipeline_full = REG_FIELD_MAX(VRR_CTL_PIPELINE_FULL_MASK);
+
+ if (DISPLAY_VER(display) >= 13)
+ return REG_FIELD_MAX(XELPD_VRR_CTL_VRR_GUARDBAND_MASK);
+ else
+ return intel_vrr_pipeline_full_to_guardband(crtc_state,
+ max_pipeline_full);
+}
+
+static int
+intel_vrr_max_vblank_guardband(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
+ return crtc_state->vrr.vmin -
+ adjusted_mode->crtc_vdisplay -
+ crtc_state->set_context_latency -
+ intel_vrr_extra_vblank_delay(display);
+}
+
+static int
+intel_vrr_max_guardband(struct intel_crtc_state *crtc_state)
+{
+ return min(intel_vrr_max_hw_guardband(crtc_state),
+ intel_vrr_max_vblank_guardband(crtc_state));
+}
+
+static
+int intel_vrr_compute_optimized_guardband(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct skl_prefill_ctx prefill_ctx;
+ int prefill_latency_us;
+ int guardband = 0;
+
+ skl_prefill_init_worst(&prefill_ctx, crtc_state);
+
+ /*
+ * The SoC power controller runs SAGV mutually exclusive with package C states,
+ * so the max of package C and SAGV latencies is used to compute the min prefill guardband.
+ * PM delay = max(sagv_latency, pkgc_max_latency (highest enabled wm level 1 and up))
+ */
+ prefill_latency_us = max(display->sagv.block_time_us,
+ skl_watermark_max_latency(display, 1));
+
+ guardband = skl_prefill_min_guardband(&prefill_ctx,
+ crtc_state,
+ prefill_latency_us);
+
+ if (intel_crtc_has_dp_encoder(crtc_state)) {
+ guardband = max(guardband, intel_psr_min_guardband(crtc_state));
+ guardband = max(guardband, intel_dp_sdp_min_guardband(crtc_state, true));
+ }
+
+ return guardband;
+}
+
+static bool intel_vrr_use_optimized_guardband(const struct intel_crtc_state *crtc_state)
+{
+ /*
+ * #TODO: Enable optimized guardband for HDMI
+ * For HDMI lot of infoframes are transmitted a line or two after vsync.
+ * Since with optimized guardband the double bufferring point is at delayed vblank,
+ * we need to ensure that vsync happens after delayed vblank for the HDMI case.
+ */
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI))
+ return false;
+
+ return true;
+}
+
+void intel_vrr_compute_guardband(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
+ struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode;
+ int guardband;
+
if (!intel_vrr_possible(crtc_state))
return;
- if (DISPLAY_VER(display) >= 13) {
- crtc_state->vrr.guardband =
- crtc_state->vrr.vmin - adjusted_mode->crtc_vblank_start;
- } else {
- /* hardware imposes one extra scanline somewhere */
- crtc_state->vrr.pipeline_full =
- min(255, crtc_state->vrr.vmin - adjusted_mode->crtc_vblank_start -
- crtc_state->framestart_delay - 1);
+ if (intel_vrr_use_optimized_guardband(crtc_state))
+ guardband = intel_vrr_compute_optimized_guardband(crtc_state);
+ else
+ guardband = crtc_state->vrr.vmin - adjusted_mode->crtc_vdisplay;
+
+ crtc_state->vrr.guardband = min(guardband, intel_vrr_max_guardband(crtc_state));
+ if (intel_vrr_always_use_vrr_tg(display)) {
+ adjusted_mode->crtc_vblank_start =
+ adjusted_mode->crtc_vtotal - crtc_state->vrr.guardband;
/*
- * vmin/vmax/flipline also need to be adjusted by
- * the vblank delay to maintain correct vtotals.
+ * pipe_mode has already been derived from the
+ * original adjusted_mode, keep the two in sync.
*/
- crtc_state->vrr.vmin -= intel_vrr_real_vblank_delay(crtc_state);
- crtc_state->vrr.vmax -= intel_vrr_real_vblank_delay(crtc_state);
- crtc_state->vrr.flipline -= intel_vrr_real_vblank_delay(crtc_state);
+ pipe_mode->crtc_vblank_start =
+ adjusted_mode->crtc_vblank_start;
}
+
+ if (DISPLAY_VER(display) < 13)
+ crtc_state->vrr.pipeline_full =
+ intel_vrr_guardband_to_pipeline_full(crtc_state,
+ crtc_state->vrr.guardband);
}
static u32 trans_vrr_ctl(const struct intel_crtc_state *crtc_state)
@@ -461,6 +525,9 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+ if (!HAS_VRR(display))
+ return;
+
/*
* This bit seems to have two meanings depending on the platform:
* TGL: generate VRR "safe window" for DSB vblank waits
@@ -489,7 +556,7 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
intel_vrr_set_fixed_rr_timings(crtc_state);
- if (!intel_vrr_always_use_vrr_tg(display) && !crtc_state->vrr.enable)
+ if (!intel_vrr_always_use_vrr_tg(display))
intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
trans_vrr_ctl(crtc_state));
@@ -498,6 +565,18 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
TRANS_VRR_VSYNC(display, cpu_transcoder),
VRR_VSYNC_END(crtc_state->vrr.vsync_end) |
VRR_VSYNC_START(crtc_state->vrr.vsync_start));
+
+ /*
+ * For BMG and LNL+ onwards the EMP_AS_SDP_TL is used for programming
+ * double buffering point and transmission line for VRR packets for
+ * HDMI2.1/DP/eDP/DP->HDMI2.1 PCON.
+ * Since currently we support VRR only for DP/eDP, so this is programmed
+ * to for Adaptive Sync SDP to Vsync start.
+ */
+ if (DISPLAY_VERx100(display) == 1401 || DISPLAY_VER(display) >= 20)
+ intel_de_write(display,
+ EMP_AS_SDP_TL(display, cpu_transcoder),
+ EMP_AS_SDP_DB_TL(crtc_state->vrr.vsync_start));
}
void intel_vrr_send_push(struct intel_dsb *dsb,
@@ -576,126 +655,128 @@ bool intel_vrr_always_use_vrr_tg(struct intel_display *display)
return false;
}
-static
-void intel_vrr_set_db_point_and_transmission_line(const struct intel_crtc_state *crtc_state)
+static int intel_vrr_hw_vmin(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
- enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
- /*
- * For BMG and LNL+ onwards the EMP_AS_SDP_TL is used for programming
- * double buffering point and transmission line for VRR packets for
- * HDMI2.1/DP/eDP/DP->HDMI2.1 PCON.
- * Since currently we support VRR only for DP/eDP, so this is programmed
- * to for Adaptive Sync SDP to Vsync start.
- */
- if (DISPLAY_VERx100(display) == 1401 || DISPLAY_VER(display) >= 20)
- intel_de_write(display,
- EMP_AS_SDP_TL(display, cpu_transcoder),
- EMP_AS_SDP_DB_TL(crtc_state->vrr.vsync_start));
+ return intel_vrr_hw_value(crtc_state, crtc_state->vrr.vmin) -
+ intel_vrr_vmin_flipline_offset(display);
}
-void intel_vrr_enable(const struct intel_crtc_state *crtc_state)
+static int intel_vrr_hw_vmax(const struct intel_crtc_state *crtc_state)
+{
+ return intel_vrr_hw_value(crtc_state, crtc_state->vrr.vmax);
+}
+
+static int intel_vrr_hw_flipline(const struct intel_crtc_state *crtc_state)
+{
+ return intel_vrr_hw_value(crtc_state, crtc_state->vrr.flipline);
+}
+
+static void intel_vrr_set_vrr_timings(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
- if (!crtc_state->vrr.enable)
- return;
-
intel_de_write(display, TRANS_VRR_VMIN(display, cpu_transcoder),
- crtc_state->vrr.vmin - 1);
+ intel_vrr_hw_vmin(crtc_state) - 1);
intel_de_write(display, TRANS_VRR_VMAX(display, cpu_transcoder),
- crtc_state->vrr.vmax - 1);
+ intel_vrr_hw_vmax(crtc_state) - 1);
intel_de_write(display, TRANS_VRR_FLIPLINE(display, cpu_transcoder),
- crtc_state->vrr.flipline - 1);
+ intel_vrr_hw_flipline(crtc_state) - 1);
+}
- intel_de_write(display, TRANS_PUSH(display, cpu_transcoder),
- TRANS_PUSH_EN);
+static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state,
+ bool cmrr_enable)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
+ u32 vrr_ctl;
- if (!intel_vrr_always_use_vrr_tg(display)) {
- intel_vrr_set_db_point_and_transmission_line(crtc_state);
+ intel_de_write(display, TRANS_PUSH(display, cpu_transcoder), TRANS_PUSH_EN);
- if (crtc_state->cmrr.enable) {
- intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
- VRR_CTL_VRR_ENABLE | VRR_CTL_CMRR_ENABLE |
- trans_vrr_ctl(crtc_state));
- } else {
- intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
- VRR_CTL_VRR_ENABLE | trans_vrr_ctl(crtc_state));
- }
- }
+ vrr_ctl = VRR_CTL_VRR_ENABLE | trans_vrr_ctl(crtc_state);
+
+ /*
+ * FIXME this might be broken as bspec seems to imply that
+ * even VRR_CTL_CMRR_ENABLE is armed by TRANS_CMRR_N_HI
+ * when enabling CMRR (but not when disabling CMRR?).
+ */
+ if (cmrr_enable)
+ vrr_ctl |= VRR_CTL_CMRR_ENABLE;
+
+ intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl);
}
-void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state)
+static void intel_vrr_tg_disable(const struct intel_crtc_state *old_crtc_state)
{
struct intel_display *display = to_intel_display(old_crtc_state);
enum transcoder cpu_transcoder = old_crtc_state->cpu_transcoder;
- if (!old_crtc_state->vrr.enable)
- return;
+ intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
+ trans_vrr_ctl(old_crtc_state));
- if (!intel_vrr_always_use_vrr_tg(display)) {
- intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
- trans_vrr_ctl(old_crtc_state));
- intel_de_wait_for_clear(display,
- TRANS_VRR_STATUS(display, cpu_transcoder),
- VRR_STATUS_VRR_EN_LIVE, 1000);
- intel_de_write(display, TRANS_PUSH(display, cpu_transcoder), 0);
- }
+ if (intel_de_wait_for_clear_ms(display,
+ TRANS_VRR_STATUS(display, cpu_transcoder),
+ VRR_STATUS_VRR_EN_LIVE, 1000))
+ drm_err(display->drm, "Timed out waiting for VRR live status to clear\n");
- intel_vrr_set_fixed_rr_timings(old_crtc_state);
+ intel_de_write(display, TRANS_PUSH(display, cpu_transcoder), 0);
}
-void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state)
+void intel_vrr_enable(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
- enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
- if (!HAS_VRR(display))
+ if (!crtc_state->vrr.enable)
return;
- if (!intel_vrr_possible(crtc_state))
- return;
+ intel_vrr_set_vrr_timings(crtc_state);
- if (!intel_vrr_always_use_vrr_tg(display)) {
- intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
- trans_vrr_ctl(crtc_state));
- return;
- }
+ if (!intel_vrr_always_use_vrr_tg(display))
+ intel_vrr_tg_enable(crtc_state, crtc_state->cmrr.enable);
+}
- intel_de_write(display, TRANS_PUSH(display, cpu_transcoder),
- TRANS_PUSH_EN);
+void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state)
+{
+ struct intel_display *display = to_intel_display(old_crtc_state);
- intel_vrr_set_db_point_and_transmission_line(crtc_state);
+ if (!old_crtc_state->vrr.enable)
+ return;
- intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder),
- VRR_CTL_VRR_ENABLE | trans_vrr_ctl(crtc_state));
+ if (!intel_vrr_always_use_vrr_tg(display))
+ intel_vrr_tg_disable(old_crtc_state);
+
+ intel_vrr_set_fixed_rr_timings(old_crtc_state);
}
-void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state)
+void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
- enum transcoder cpu_transcoder = crtc_state->cpu_transcoder;
-
- if (!HAS_VRR(display))
- return;
if (!intel_vrr_possible(crtc_state))
return;
- intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), 0);
+ if (intel_vrr_always_use_vrr_tg(display))
+ intel_vrr_tg_enable(crtc_state, false);
+}
- intel_de_wait_for_clear(display, TRANS_VRR_STATUS(display, cpu_transcoder),
- VRR_STATUS_VRR_EN_LIVE, 1000);
- intel_de_write(display, TRANS_PUSH(display, cpu_transcoder), 0);
+void intel_vrr_transcoder_disable(const struct intel_crtc_state *old_crtc_state)
+{
+ struct intel_display *display = to_intel_display(old_crtc_state);
+
+ if (!intel_vrr_possible(old_crtc_state))
+ return;
+
+ if (intel_vrr_always_use_vrr_tg(display))
+ intel_vrr_tg_disable(old_crtc_state);
}
bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state)
{
return crtc_state->vrr.flipline &&
crtc_state->vrr.flipline == crtc_state->vrr.vmax &&
- crtc_state->vrr.flipline == intel_vrr_vmin_flipline(crtc_state);
+ crtc_state->vrr.flipline == crtc_state->vrr.vmin;
}
void intel_vrr_get_config(struct intel_crtc_state *crtc_state)
@@ -720,14 +801,20 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state)
TRANS_CMRR_M_HI(display, cpu_transcoder));
}
- if (DISPLAY_VER(display) >= 13)
+ if (DISPLAY_VER(display) >= 13) {
crtc_state->vrr.guardband =
REG_FIELD_GET(XELPD_VRR_CTL_VRR_GUARDBAND_MASK, trans_vrr_ctl);
- else
- if (trans_vrr_ctl & VRR_CTL_PIPELINE_FULL_OVERRIDE)
+ } else {
+ if (trans_vrr_ctl & VRR_CTL_PIPELINE_FULL_OVERRIDE) {
crtc_state->vrr.pipeline_full =
REG_FIELD_GET(VRR_CTL_PIPELINE_FULL_MASK, trans_vrr_ctl);
+ crtc_state->vrr.guardband =
+ intel_vrr_pipeline_full_to_guardband(crtc_state,
+ crtc_state->vrr.pipeline_full);
+ }
+ }
+
if (trans_vrr_ctl & VRR_CTL_FLIP_LINE_EN) {
crtc_state->vrr.flipline = intel_de_read(display,
TRANS_VRR_FLIPLINE(display, cpu_transcoder)) + 1;
@@ -736,6 +823,15 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state)
crtc_state->vrr.vmin = intel_de_read(display,
TRANS_VRR_VMIN(display, cpu_transcoder)) + 1;
+ if (DISPLAY_VER(display) < 13) {
+ /* undo what intel_vrr_hw_value() does when writing the values */
+ crtc_state->vrr.flipline += crtc_state->set_context_latency;
+ crtc_state->vrr.vmax += crtc_state->set_context_latency;
+ crtc_state->vrr.vmin += crtc_state->set_context_latency;
+
+ crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display);
+ }
+
/*
* For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal
* bits are not filled. Since for these platforms TRAN_VMIN is always
@@ -771,4 +867,34 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state)
*/
if (crtc_state->vrr.enable)
crtc_state->mode_flags |= I915_MODE_FLAG_VRR;
+
+ /*
+ * For platforms that always use the VRR timing generator, we overwrite
+ * crtc_vblank_start with vtotal - guardband to reflect the delayed
+ * vblank start. This works for both default and optimized guardband values.
+ * On other platforms, we keep the original value from
+ * intel_get_transcoder_timings() and apply adjustments only in VRR-specific
+ * paths as needed.
+ */
+ if (intel_vrr_always_use_vrr_tg(display))
+ crtc_state->hw.adjusted_mode.crtc_vblank_start =
+ crtc_state->hw.adjusted_mode.crtc_vtotal -
+ crtc_state->vrr.guardband;
+}
+
+int intel_vrr_safe_window_start(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ if (DISPLAY_VER(display) >= 30)
+ return crtc_state->hw.adjusted_mode.crtc_vdisplay -
+ crtc_state->set_context_latency;
+ else
+ return crtc_state->hw.adjusted_mode.crtc_vdisplay;
+}
+
+int intel_vrr_vmin_safe_window_end(const struct intel_crtc_state *crtc_state)
+{
+ return intel_vrr_vmin_vblank_start(crtc_state) -
+ crtc_state->set_context_latency;
}
diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h
index 38bf9996b883..bc9044621635 100644
--- a/drivers/gpu/drm/i915/display/intel_vrr.h
+++ b/drivers/gpu/drm/i915/display/intel_vrr.h
@@ -21,7 +21,7 @@ bool intel_vrr_possible(const struct intel_crtc_state *crtc_state);
void intel_vrr_check_modeset(struct intel_atomic_state *state);
void intel_vrr_compute_config(struct intel_crtc_state *crtc_state,
struct drm_connector_state *conn_state);
-void intel_vrr_compute_config_late(struct intel_crtc_state *crtc_state);
+void intel_vrr_compute_guardband(struct intel_crtc_state *crtc_state);
void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state);
void intel_vrr_enable(const struct intel_crtc_state *crtc_state);
void intel_vrr_send_push(struct intel_dsb *dsb,
@@ -35,11 +35,12 @@ int intel_vrr_vmax_vtotal(const struct intel_crtc_state *crtc_state);
int intel_vrr_vmin_vtotal(const struct intel_crtc_state *crtc_state);
int intel_vrr_vmax_vblank_start(const struct intel_crtc_state *crtc_state);
int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state);
-int intel_vrr_vblank_delay(const struct intel_crtc_state *crtc_state);
bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state);
void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state);
void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state);
void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state);
bool intel_vrr_always_use_vrr_tg(struct intel_display *display);
+int intel_vrr_safe_window_start(const struct intel_crtc_state *crtc_state);
+int intel_vrr_vmin_safe_window_end(const struct intel_crtc_state *crtc_state);
#endif /* __INTEL_VRR_H__ */
diff --git a/drivers/gpu/drm/i915/display/skl_prefill.c b/drivers/gpu/drm/i915/display/skl_prefill.c
new file mode 100644
index 000000000000..4707c2e7127a
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/skl_prefill.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+
+#include <drm/drm_print.h>
+
+#include "intel_cdclk.h"
+#include "intel_display_core.h"
+#include "intel_display_types.h"
+#include "intel_vblank.h"
+#include "intel_vdsc.h"
+#include "skl_prefill.h"
+#include "skl_scaler.h"
+#include "skl_watermark.h"
+
+static unsigned int prefill_usecs_to_lines(const struct intel_crtc_state *crtc_state,
+ unsigned int usecs)
+{
+ const struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode;
+
+ return DIV_ROUND_UP_ULL(mul_u32_u32(pipe_mode->crtc_clock, usecs << 16),
+ pipe_mode->crtc_htotal * 1000);
+}
+
+static void prefill_init(struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state)
+{
+ memset(ctx, 0, sizeof(*ctx));
+
+ ctx->prefill.fixed = crtc_state->framestart_delay << 16;
+
+ /* 20 usec for translation walks/etc. */
+ ctx->prefill.fixed += prefill_usecs_to_lines(crtc_state, 20);
+
+ ctx->prefill.dsc = intel_vdsc_prefill_lines(crtc_state);
+}
+
+static void prefill_init_nocdclk_worst(struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state)
+{
+ prefill_init(ctx, crtc_state);
+
+ ctx->prefill.wm0 = skl_wm0_prefill_lines_worst(crtc_state);
+ ctx->prefill.scaler_1st = skl_scaler_1st_prefill_lines_worst(crtc_state);
+ ctx->prefill.scaler_2nd = skl_scaler_2nd_prefill_lines_worst(crtc_state);
+
+ ctx->adj.scaler_1st = skl_scaler_1st_prefill_adjustment_worst(crtc_state);
+ ctx->adj.scaler_2nd = skl_scaler_2nd_prefill_adjustment_worst(crtc_state);
+}
+
+static void prefill_init_nocdclk(struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state)
+{
+ prefill_init(ctx, crtc_state);
+
+ ctx->prefill.wm0 = skl_wm0_prefill_lines(crtc_state);
+ ctx->prefill.scaler_1st = skl_scaler_1st_prefill_lines(crtc_state);
+ ctx->prefill.scaler_2nd = skl_scaler_2nd_prefill_lines(crtc_state);
+
+ ctx->adj.scaler_1st = skl_scaler_1st_prefill_adjustment(crtc_state);
+ ctx->adj.scaler_2nd = skl_scaler_2nd_prefill_adjustment(crtc_state);
+}
+
+static unsigned int prefill_adjust(unsigned int value, unsigned int factor)
+{
+ return DIV_ROUND_UP_ULL(mul_u32_u32(value, factor), 0x10000);
+}
+
+static unsigned int prefill_lines_nocdclk(const struct skl_prefill_ctx *ctx)
+{
+ unsigned int prefill = 0;
+
+ prefill += ctx->prefill.dsc;
+ prefill = prefill_adjust(prefill, ctx->adj.scaler_2nd);
+
+ prefill += ctx->prefill.scaler_2nd;
+ prefill = prefill_adjust(prefill, ctx->adj.scaler_1st);
+
+ prefill += ctx->prefill.scaler_1st;
+ prefill += ctx->prefill.wm0;
+
+ return prefill;
+}
+
+static unsigned int prefill_lines_cdclk(const struct skl_prefill_ctx *ctx)
+{
+ return prefill_adjust(prefill_lines_nocdclk(ctx), ctx->adj.cdclk);
+}
+
+static unsigned int prefill_lines_full(const struct skl_prefill_ctx *ctx)
+{
+ return ctx->prefill.fixed + prefill_lines_cdclk(ctx);
+}
+
+void skl_prefill_init_worst(struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state)
+{
+ prefill_init_nocdclk_worst(ctx, crtc_state);
+
+ ctx->adj.cdclk = intel_cdclk_prefill_adjustment_worst(crtc_state);
+
+ ctx->prefill.full = prefill_lines_full(ctx);
+}
+
+void skl_prefill_init(struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state)
+{
+ prefill_init_nocdclk(ctx, crtc_state);
+
+ ctx->adj.cdclk = intel_cdclk_prefill_adjustment(crtc_state);
+
+ ctx->prefill.full = prefill_lines_full(ctx);
+}
+
+static unsigned int prefill_lines_with_latency(const struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state,
+ unsigned int latency_us)
+{
+ return ctx->prefill.full + prefill_usecs_to_lines(crtc_state, latency_us);
+}
+
+int skl_prefill_min_guardband(const struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state,
+ unsigned int latency_us)
+{
+ unsigned int prefill = prefill_lines_with_latency(ctx, crtc_state, latency_us);
+
+ return DIV_ROUND_UP(prefill, 0x10000);
+}
+
+static unsigned int prefill_guardband(const struct intel_crtc_state *crtc_state)
+{
+ return intel_crtc_vblank_length(crtc_state) << 16;
+}
+
+bool skl_prefill_vblank_too_short(const struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state,
+ unsigned int latency_us)
+{
+ unsigned int guardband = prefill_guardband(crtc_state);
+ unsigned int prefill = prefill_lines_with_latency(ctx, crtc_state, latency_us);
+
+ return guardband < prefill;
+}
+
+int skl_prefill_min_cdclk(const struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state)
+{
+ unsigned int prefill_unadjusted = prefill_lines_nocdclk(ctx);
+ unsigned int prefill_available = prefill_guardband(crtc_state) - ctx->prefill.fixed;
+
+ return intel_cdclk_min_cdclk_for_prefill(crtc_state, prefill_unadjusted,
+ prefill_available);
+}
diff --git a/drivers/gpu/drm/i915/display/skl_prefill.h b/drivers/gpu/drm/i915/display/skl_prefill.h
new file mode 100644
index 000000000000..028ee19b64ce
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/skl_prefill.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __SKL_PREFILL_H__
+#define __SKL_PREFILL_H__
+
+#include <linux/types.h>
+
+struct intel_crtc_state;
+
+struct skl_prefill_ctx {
+ /* .16 scanlines */
+ struct {
+ unsigned int fixed;
+ unsigned int wm0;
+ unsigned int scaler_1st;
+ unsigned int scaler_2nd;
+ unsigned int dsc;
+ unsigned int full;
+ } prefill;
+
+ /* .16 adjustment factors */
+ struct {
+ unsigned int cdclk;
+ unsigned int scaler_1st;
+ unsigned int scaler_2nd;
+ } adj;
+};
+
+void skl_prefill_init_worst(struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state);
+void skl_prefill_init(struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state);
+
+bool skl_prefill_vblank_too_short(const struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state,
+ unsigned int latency_us);
+int skl_prefill_min_guardband(const struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state,
+ unsigned int latency_us);
+int skl_prefill_min_cdclk(const struct skl_prefill_ctx *ctx,
+ const struct intel_crtc_state *crtc_state);
+
+#endif /* __SKL_PREFILL_H__ */
diff --git a/drivers/gpu/drm/i915/display/skl_scaler.c b/drivers/gpu/drm/i915/display/skl_scaler.c
index c6cccf170ff1..4c4deac7f9c8 100644
--- a/drivers/gpu/drm/i915/display/skl_scaler.c
+++ b/drivers/gpu/drm/i915/display/skl_scaler.c
@@ -5,11 +5,13 @@
#include <drm/drm_print.h>
-#include "i915_utils.h"
+#include "intel_casf.h"
+#include "intel_casf_regs.h"
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_trace.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_display_wa.h"
#include "intel_fb.h"
#include "skl_scaler.h"
@@ -282,7 +284,8 @@ int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state)
drm_rect_width(&crtc_state->pipe_src),
drm_rect_height(&crtc_state->pipe_src),
width, height, NULL, 0,
- crtc_state->pch_pfit.enabled);
+ crtc_state->pch_pfit.enabled ||
+ intel_casf_needs_scaler(crtc_state));
}
/**
@@ -321,7 +324,9 @@ int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
}
static int intel_allocate_scaler(struct intel_crtc_scaler_state *scaler_state,
- struct intel_crtc *crtc)
+ struct intel_crtc *crtc,
+ struct intel_plane_state *plane_state,
+ bool casf_scaler)
{
int i;
@@ -329,6 +334,10 @@ static int intel_allocate_scaler(struct intel_crtc_scaler_state *scaler_state,
if (scaler_state->scalers[i].in_use)
continue;
+ /* CASF needs second scaler */
+ if (!plane_state && casf_scaler && i != 1)
+ continue;
+
scaler_state->scalers[i].in_use = true;
return i;
@@ -379,7 +388,7 @@ static int intel_atomic_setup_scaler(struct intel_crtc_state *crtc_state,
int num_scalers_need, struct intel_crtc *crtc,
const char *name, int idx,
struct intel_plane_state *plane_state,
- int *scaler_id)
+ int *scaler_id, bool casf_scaler)
{
struct intel_display *display = to_intel_display(crtc);
struct intel_crtc_scaler_state *scaler_state = &crtc_state->scaler_state;
@@ -388,7 +397,7 @@ static int intel_atomic_setup_scaler(struct intel_crtc_state *crtc_state,
int vscale = 0;
if (*scaler_id < 0)
- *scaler_id = intel_allocate_scaler(scaler_state, crtc);
+ *scaler_id = intel_allocate_scaler(scaler_state, crtc, plane_state, casf_scaler);
if (drm_WARN(display->drm, *scaler_id < 0,
"Cannot find scaler for %s:%d\n", name, idx))
@@ -520,10 +529,14 @@ static int setup_crtc_scaler(struct intel_atomic_state *state,
struct intel_crtc_scaler_state *scaler_state =
&crtc_state->scaler_state;
+ if (intel_casf_needs_scaler(crtc_state) && crtc_state->pch_pfit.enabled)
+ return -EINVAL;
+
return intel_atomic_setup_scaler(crtc_state,
hweight32(scaler_state->scaler_users),
crtc, "CRTC", crtc->base.base.id,
- NULL, &scaler_state->scaler_id);
+ NULL, &scaler_state->scaler_id,
+ intel_casf_needs_scaler(crtc_state));
}
static int setup_plane_scaler(struct intel_atomic_state *state,
@@ -558,7 +571,8 @@ static int setup_plane_scaler(struct intel_atomic_state *state,
return intel_atomic_setup_scaler(crtc_state,
hweight32(scaler_state->scaler_users),
crtc, "PLANE", plane->base.base.id,
- plane_state, &plane_state->scaler_id);
+ plane_state, &plane_state->scaler_id,
+ false);
}
/**
@@ -738,6 +752,52 @@ static void skl_scaler_setup_filter(struct intel_display *display,
}
}
+#define CASF_SCALER_FILTER_SELECT \
+ (PS_FILTER_PROGRAMMED | \
+ PS_Y_VERT_FILTER_SELECT(0) | \
+ PS_Y_HORZ_FILTER_SELECT(0) | \
+ PS_UV_VERT_FILTER_SELECT(0) | \
+ PS_UV_HORZ_FILTER_SELECT(0))
+
+void skl_scaler_setup_casf(struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ struct intel_display *display = to_intel_display(crtc);
+ struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
+ struct intel_crtc_scaler_state *scaler_state =
+ &crtc_state->scaler_state;
+ struct drm_rect src, dest;
+ int id, width, height;
+ int x = 0, y = 0;
+ enum pipe pipe = crtc->pipe;
+ u32 ps_ctrl;
+
+ width = adjusted_mode->crtc_hdisplay;
+ height = adjusted_mode->crtc_vdisplay;
+
+ drm_rect_init(&dest, x, y, width, height);
+
+ width = drm_rect_width(&dest);
+ height = drm_rect_height(&dest);
+ id = scaler_state->scaler_id;
+
+ drm_rect_init(&src, 0, 0,
+ drm_rect_width(&crtc_state->pipe_src) << 16,
+ drm_rect_height(&crtc_state->pipe_src) << 16);
+
+ trace_intel_pipe_scaler_update_arm(crtc, id, x, y, width, height);
+
+ ps_ctrl = PS_SCALER_EN | PS_BINDING_PIPE | scaler_state->scalers[id].mode |
+ CASF_SCALER_FILTER_SELECT;
+
+ intel_de_write_fw(display, SKL_PS_CTRL(pipe, id), ps_ctrl);
+ intel_de_write_fw(display, SKL_PS_WIN_POS(pipe, id),
+ PS_WIN_XPOS(x) | PS_WIN_YPOS(y));
+ intel_de_write_fw(display, SKL_PS_WIN_SZ(pipe, id),
+ PS_WIN_XSIZE(width) | PS_WIN_YSIZE(height));
+}
+
void skl_pfit_enable(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
@@ -921,16 +981,23 @@ void skl_scaler_get_config(struct intel_crtc_state *crtc_state)
continue;
id = i;
- crtc_state->pch_pfit.enabled = true;
+
+ /* Read CASF regs for second scaler */
+ if (HAS_CASF(display) && id == 1)
+ intel_casf_sharpness_get_config(crtc_state);
+
+ if (!crtc_state->hw.casf_params.casf_enable)
+ crtc_state->pch_pfit.enabled = true;
pos = intel_de_read(display, SKL_PS_WIN_POS(crtc->pipe, i));
size = intel_de_read(display, SKL_PS_WIN_SZ(crtc->pipe, i));
- drm_rect_init(&crtc_state->pch_pfit.dst,
- REG_FIELD_GET(PS_WIN_XPOS_MASK, pos),
- REG_FIELD_GET(PS_WIN_YPOS_MASK, pos),
- REG_FIELD_GET(PS_WIN_XSIZE_MASK, size),
- REG_FIELD_GET(PS_WIN_YSIZE_MASK, size));
+ if (!crtc_state->hw.casf_params.casf_enable)
+ drm_rect_init(&crtc_state->pch_pfit.dst,
+ REG_FIELD_GET(PS_WIN_XPOS_MASK, pos),
+ REG_FIELD_GET(PS_WIN_YPOS_MASK, pos),
+ REG_FIELD_GET(PS_WIN_XSIZE_MASK, size),
+ REG_FIELD_GET(PS_WIN_YSIZE_MASK, size));
scaler_state->scalers[i].in_use = true;
break;
@@ -968,3 +1035,144 @@ void adl_scaler_ecc_unmask(const struct intel_crtc_state *crtc_state)
1);
intel_de_write(display, XELPD_DISPLAY_ERR_FATAL_MASK, 0);
}
+
+unsigned int skl_scaler_1st_prefill_adjustment(const struct intel_crtc_state *crtc_state)
+{
+ /*
+ * FIXME don't have scalers assigned yet
+ * so can't look up the scale factors
+ */
+ return 0x10000;
+}
+
+unsigned int skl_scaler_2nd_prefill_adjustment(const struct intel_crtc_state *crtc_state)
+{
+ /*
+ * FIXME don't have scalers assigned yet
+ * so can't look up the scale factors
+ */
+ return 0x10000;
+}
+
+unsigned int skl_scaler_1st_prefill_lines(const struct intel_crtc_state *crtc_state)
+{
+ const struct intel_crtc_scaler_state *scaler_state =
+ &crtc_state->scaler_state;
+ int num_scalers = hweight32(scaler_state->scaler_users);
+
+ if (num_scalers > 0)
+ return 4 << 16;
+
+ return 0;
+}
+
+unsigned int skl_scaler_2nd_prefill_lines(const struct intel_crtc_state *crtc_state)
+{
+ const struct intel_crtc_scaler_state *scaler_state =
+ &crtc_state->scaler_state;
+ int num_scalers = hweight32(scaler_state->scaler_users);
+
+ if (num_scalers > 1 && crtc_state->pch_pfit.enabled)
+ return 4 << 16;
+
+ return 0;
+}
+
+static unsigned int _skl_scaler_max_scale(const struct intel_crtc_state *crtc_state,
+ unsigned int max_scale)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+
+ /*
+ * Downscaling requires increasing cdclk, so max scale
+ * factor is limited to the max_dotclock/dotclock ratio.
+ *
+ * FIXME find out the max downscale factors properly
+ */
+ return min(max_scale, DIV_ROUND_UP_ULL((u64)display->cdclk.max_dotclk_freq << 16,
+ crtc_state->hw.pipe_mode.crtc_clock));
+}
+
+unsigned int skl_scaler_max_total_scale(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ unsigned int max_scale;
+
+ if (crtc->num_scalers < 1)
+ return 0x10000;
+
+ /* FIXME find out the max downscale factors properly */
+ max_scale = 9 << 16;
+ if (crtc->num_scalers > 1)
+ max_scale *= 9;
+
+ return _skl_scaler_max_scale(crtc_state, max_scale);
+}
+
+unsigned int skl_scaler_max_hscale(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ unsigned int max_scale;
+
+ if (crtc->num_scalers < 1)
+ return 0x10000;
+
+ /* FIXME find out the max downscale factors properly */
+ max_scale = 3 << 16;
+
+ return _skl_scaler_max_scale(crtc_state, max_scale);
+}
+
+unsigned int skl_scaler_max_scale(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+ unsigned int max_scale;
+
+ if (crtc->num_scalers < 1)
+ return 0x10000;
+
+ /* FIXME find out the max downscale factors properly */
+ max_scale = 9 << 16;
+
+ return _skl_scaler_max_scale(crtc_state, max_scale);
+}
+
+unsigned int skl_scaler_1st_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (crtc->num_scalers > 0)
+ return skl_scaler_max_scale(crtc_state);
+ else
+ return 0x10000;
+}
+
+unsigned int skl_scaler_2nd_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (crtc->num_scalers > 1)
+ return skl_scaler_max_scale(crtc_state);
+ else
+ return 0x10000;
+}
+
+unsigned int skl_scaler_1st_prefill_lines_worst(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (crtc->num_scalers > 0)
+ return 4 << 16;
+ else
+ return 0;
+}
+
+unsigned int skl_scaler_2nd_prefill_lines_worst(const struct intel_crtc_state *crtc_state)
+{
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+
+ if (crtc->num_scalers > 1)
+ return 4 << 16;
+ else
+ return 0;
+}
diff --git a/drivers/gpu/drm/i915/display/skl_scaler.h b/drivers/gpu/drm/i915/display/skl_scaler.h
index 12a19016c5f6..7e8d819c019d 100644
--- a/drivers/gpu/drm/i915/display/skl_scaler.h
+++ b/drivers/gpu/drm/i915/display/skl_scaler.h
@@ -36,6 +36,8 @@ void skl_scaler_disable(const struct intel_crtc_state *old_crtc_state);
void skl_scaler_get_config(struct intel_crtc_state *crtc_state);
+void skl_scaler_setup_casf(struct intel_crtc_state *crtc_state);
+
enum drm_mode_status
skl_scaler_mode_valid(struct intel_display *display,
const struct drm_display_mode *mode,
@@ -45,4 +47,19 @@ skl_scaler_mode_valid(struct intel_display *display,
void adl_scaler_ecc_mask(const struct intel_crtc_state *crtc_state);
void adl_scaler_ecc_unmask(const struct intel_crtc_state *crtc_state);
+
+unsigned int skl_scaler_max_total_scale(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_max_scale(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_max_hscale(const struct intel_crtc_state *crtc_state);
+
+unsigned int skl_scaler_1st_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_2nd_prefill_adjustment_worst(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_1st_prefill_lines_worst(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_2nd_prefill_lines_worst(const struct intel_crtc_state *crtc_state);
+
+unsigned int skl_scaler_1st_prefill_adjustment(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_2nd_prefill_adjustment(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_1st_prefill_lines(const struct intel_crtc_state *crtc_state);
+unsigned int skl_scaler_2nd_prefill_lines(const struct intel_crtc_state *crtc_state);
+
#endif
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index e13fb781e7b2..ee8e24497d2c 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -7,15 +7,17 @@
#include <drm/drm_blend.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include "pxp/intel_pxp.h"
-#include "i915_drv.h"
-#include "i915_utils.h"
#include "intel_bo.h"
+#include "intel_color.h"
+#include "intel_color_pipeline.h"
#include "intel_de.h"
#include "intel_display_irq.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dpt.h"
#include "intel_fb.h"
#include "intel_fbc.h"
@@ -24,6 +26,7 @@
#include "intel_plane.h"
#include "intel_psr.h"
#include "intel_psr_regs.h"
+#include "intel_step.h"
#include "skl_scaler.h"
#include "skl_universal_plane.h"
#include "skl_universal_plane_regs.h"
@@ -389,44 +392,19 @@ static int glk_plane_max_width(const struct drm_framebuffer *fb,
}
}
+static int adl_plane_min_width(const struct drm_framebuffer *fb,
+ int color_plane,
+ unsigned int rotation)
+{
+ return 16 / fb->format->cpp[color_plane];
+}
+
static int icl_plane_min_width(const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation)
{
/* Wa_14011264657, Wa_14011050563: gen11+ */
- switch (fb->format->format) {
- case DRM_FORMAT_C8:
- return 18;
- case DRM_FORMAT_RGB565:
- return 10;
- case DRM_FORMAT_XRGB8888:
- case DRM_FORMAT_XBGR8888:
- case DRM_FORMAT_ARGB8888:
- case DRM_FORMAT_ABGR8888:
- case DRM_FORMAT_XRGB2101010:
- case DRM_FORMAT_XBGR2101010:
- case DRM_FORMAT_ARGB2101010:
- case DRM_FORMAT_ABGR2101010:
- case DRM_FORMAT_XVYU2101010:
- case DRM_FORMAT_Y212:
- case DRM_FORMAT_Y216:
- return 6;
- case DRM_FORMAT_NV12:
- return 20;
- case DRM_FORMAT_P010:
- case DRM_FORMAT_P012:
- case DRM_FORMAT_P016:
- return 12;
- case DRM_FORMAT_XRGB16161616F:
- case DRM_FORMAT_XBGR16161616F:
- case DRM_FORMAT_ARGB16161616F:
- case DRM_FORMAT_ABGR16161616F:
- case DRM_FORMAT_XVYU12_16161616:
- case DRM_FORMAT_XVYU16161616:
- return 4;
- default:
- return 1;
- }
+ return 16 / fb->format->cpp[color_plane] + 2;
}
static int xe3_plane_max_width(const struct drm_framebuffer *fb,
@@ -463,6 +441,23 @@ static int skl_plane_max_height(const struct drm_framebuffer *fb,
return 4096;
}
+static enum intel_fbc_id skl_fbc_id_for_pipe(enum pipe pipe)
+{
+ return pipe - PIPE_A + INTEL_FBC_A;
+}
+
+static bool skl_plane_has_fbc(struct intel_display *display,
+ enum intel_fbc_id fbc_id, enum plane_id plane_id)
+{
+ if ((DISPLAY_RUNTIME_INFO(display)->fbc_mask & BIT(fbc_id)) == 0)
+ return false;
+
+ if (DISPLAY_VER(display) >= 20)
+ return icl_is_hdr_plane(display, plane_id);
+ else
+ return plane_id == PLANE_1;
+}
+
static int icl_plane_max_height(const struct drm_framebuffer *fb,
int color_plane,
unsigned int rotation)
@@ -472,12 +467,11 @@ static int icl_plane_max_height(const struct drm_framebuffer *fb,
static unsigned int
plane_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation,
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation,
unsigned int max_pixels,
unsigned int max_bytes)
{
- const struct drm_format_info *info = drm_format_info(pixel_format);
int cpp = info->cpp[0];
if (drm_rotation_90_or_270(rotation))
@@ -488,26 +482,26 @@ plane_max_stride(struct intel_plane *plane,
static unsigned int
adl_plane_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
unsigned int max_pixels = 65536; /* PLANE_OFFSET limit */
unsigned int max_bytes = 128 * 1024;
- return plane_max_stride(plane, pixel_format,
+ return plane_max_stride(plane, info,
modifier, rotation,
max_pixels, max_bytes);
}
static unsigned int
skl_plane_max_stride(struct intel_plane *plane,
- u32 pixel_format, u64 modifier,
- unsigned int rotation)
+ const struct drm_format_info *info,
+ u64 modifier, unsigned int rotation)
{
unsigned int max_pixels = 8192; /* PLANE_OFFSET limit */
unsigned int max_bytes = 32 * 1024;
- return plane_max_stride(plane, pixel_format,
+ return plane_max_stride(plane, info,
modifier, rotation,
max_pixels, max_bytes);
}
@@ -898,6 +892,25 @@ static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
}
+static void x3p_lpd_plane_update_pixel_normalizer(struct intel_dsb *dsb,
+ struct intel_plane *plane,
+ bool enable)
+{
+ struct intel_display *display = to_intel_display(plane);
+ enum intel_fbc_id fbc_id = skl_fbc_id_for_pipe(plane->pipe);
+ u32 val;
+
+ /* Only HDR planes have pixel normalizer and don't matter if no FBC */
+ if (!skl_plane_has_fbc(display, fbc_id, plane->id))
+ return;
+
+ val = enable ? PLANE_PIXEL_NORMALIZE_NORM_FACTOR(PLANE_PIXEL_NORMALIZE_NORM_FACTOR_1_0) |
+ PLANE_PIXEL_NORMALIZE_ENABLE : 0;
+
+ intel_de_write_dsb(display, dsb,
+ PLANE_PIXEL_NORMALIZE(plane->pipe, plane->id), val);
+}
+
static void
icl_plane_disable_arm(struct intel_dsb *dsb,
struct intel_plane *plane,
@@ -913,6 +926,10 @@ icl_plane_disable_arm(struct intel_dsb *dsb,
skl_write_plane_wm(dsb, plane, crtc_state);
icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
+
+ if (DISPLAY_VER(display) >= 35)
+ x3p_lpd_plane_update_pixel_normalizer(dsb, plane, false);
+
intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id), 0);
intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id), 0);
}
@@ -1260,6 +1277,18 @@ static u32 glk_plane_color_ctl(const struct intel_plane_state *plane_state)
if (plane_state->force_black)
plane_color_ctl |= PLANE_COLOR_PLANE_CSC_ENABLE;
+ if (plane_state->hw.degamma_lut)
+ plane_color_ctl |= PLANE_COLOR_PRE_CSC_GAMMA_ENABLE;
+
+ if (plane_state->hw.ctm)
+ plane_color_ctl |= PLANE_COLOR_PLANE_CSC_ENABLE;
+
+ if (plane_state->hw.gamma_lut) {
+ plane_color_ctl &= ~PLANE_COLOR_PLANE_GAMMA_DISABLE;
+ if (drm_color_lut32_size(plane_state->hw.gamma_lut) != 32)
+ plane_color_ctl |= PLANE_COLOR_POST_CSC_GAMMA_MULTSEG_ENABLE;
+ }
+
return plane_color_ctl;
}
@@ -1541,6 +1570,8 @@ icl_plane_update_noarm(struct intel_dsb *dsb,
plane_color_ctl = plane_state->color_ctl |
glk_plane_color_ctl_crtc(crtc_state);
+ intel_color_plane_program_pipeline(dsb, plane_state);
+
/* The scaler will handle the output position */
if (plane_state->scaler_id >= 0) {
crtc_x = 0;
@@ -1572,7 +1603,7 @@ icl_plane_update_noarm(struct intel_dsb *dsb,
}
/* FLAT CCS doesn't need to program AUX_DIST */
- if (!HAS_FLAT_CCS(to_i915(display->drm)) && DISPLAY_VER(display) < 20)
+ if (HAS_AUX_CCS(display))
intel_de_write_dsb(display, dsb, PLANE_AUX_DIST(pipe, plane_id),
skl_plane_aux_dist(plane_state, color_plane));
@@ -1642,6 +1673,16 @@ icl_plane_update_arm(struct intel_dsb *dsb,
icl_plane_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
+ intel_color_plane_commit_arm(dsb, plane_state);
+
+ /*
+ * In order to have FBC for fp16 formats pixel normalizer block must be
+ * active. Check if pixel normalizer block need to be enabled for FBC.
+ * If needed, use normalization factor as 1.0 and enable the block.
+ */
+ if (intel_fbc_is_enable_pixel_normalizer(plane_state))
+ x3p_lpd_plane_update_pixel_normalizer(dsb, plane, true);
+
/*
* The control register self-arms if the plane was previously
* disabled. Try to make the plane enable atomic by writing
@@ -1724,7 +1765,8 @@ static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state,
}
if (rotation & DRM_MODE_REFLECT_X &&
- fb->modifier == DRM_FORMAT_MOD_LINEAR) {
+ fb->modifier == DRM_FORMAT_MOD_LINEAR &&
+ DISPLAY_VER(display) < 35) {
drm_dbg_kms(display->drm,
"[PLANE:%d:%s] horizontal flip is not supported with linear surface formats\n",
plane->base.base.id, plane->base.name);
@@ -1780,8 +1822,7 @@ static int skl_plane_check_fb(const struct intel_crtc_state *crtc_state,
}
/* Y-tiling is not supported in IF-ID Interlace mode */
- if (crtc_state->hw.enable &&
- crtc_state->hw.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE &&
+ if (crtc_state->hw.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE &&
fb->modifier != DRM_FORMAT_MOD_LINEAR &&
fb->modifier != I915_FORMAT_MOD_X_TILED) {
drm_dbg_kms(display->drm,
@@ -1884,6 +1925,14 @@ static int intel_plane_min_width(struct intel_plane *plane,
return 1;
}
+static int intel_plane_min_height(struct intel_plane *plane,
+ const struct drm_framebuffer *fb,
+ int color_plane,
+ unsigned int rotation)
+{
+ return 1;
+}
+
static int intel_plane_max_width(struct intel_plane *plane,
const struct drm_framebuffer *fb,
int color_plane,
@@ -2015,6 +2064,7 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)
int w = drm_rect_width(&plane_state->uapi.src) >> 16;
int h = drm_rect_height(&plane_state->uapi.src) >> 16;
int min_width = intel_plane_min_width(plane, fb, 0, rotation);
+ int min_height = intel_plane_min_height(plane, fb, 0, rotation);
int max_width = intel_plane_max_width(plane, fb, 0, rotation);
int max_height = intel_plane_max_height(plane, fb, 0, rotation);
unsigned int alignment = plane->min_alignment(plane, fb, 0);
@@ -2022,11 +2072,11 @@ 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 || h < 1) {
+ if (w > max_width || w < min_width || h > max_height || h < min_height) {
drm_dbg_kms(display->drm,
- "[PLANE:%d:%s] requested Y/RGB source size %dx%d outside limits (min: %dx1 max: %dx%d)\n",
+ "[PLANE:%d:%s] requested Y/RGB source size %dx%d outside limits (min: %dx%d max: %dx%d)\n",
plane->base.base.id, plane->base.name,
- w, h, min_width, max_width, max_height);
+ w, h, min_width, min_height, max_width, max_height);
return -EINVAL;
}
@@ -2086,6 +2136,8 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
int uv_plane = 1;
int ccs_plane = intel_fb_is_ccs_modifier(fb->modifier) ?
skl_main_to_aux_plane(fb, uv_plane) : 0;
+ int min_width = intel_plane_min_width(plane, fb, uv_plane, rotation);
+ int min_height = intel_plane_min_height(plane, fb, uv_plane, rotation);
int max_width = intel_plane_max_width(plane, fb, uv_plane, rotation);
int max_height = intel_plane_max_height(plane, fb, uv_plane, rotation);
int x = plane_state->uapi.src.x1 >> 17;
@@ -2095,11 +2147,11 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
u32 offset;
/* FIXME not quite sure how/if these apply to the chroma plane */
- if (w > max_width || h > max_height) {
+ if (w > max_width || w < min_width || h > max_height || h < min_height) {
drm_dbg_kms(display->drm,
- "[PLANE:%d:%s] CbCr source size %dx%d too big (limit %dx%d)\n",
+ "[PLANE:%d:%s] requested CbCr source size %dx%d outside limits (min: %dx%d max: %dx%d)\n",
plane->base.base.id, plane->base.name,
- w, h, max_width, max_height);
+ w, h, min_width, min_height, max_width, max_height);
return -EINVAL;
}
@@ -2404,23 +2456,6 @@ void icl_link_nv12_planes(struct intel_plane_state *uv_plane_state,
}
}
-static enum intel_fbc_id skl_fbc_id_for_pipe(enum pipe pipe)
-{
- return pipe - PIPE_A + INTEL_FBC_A;
-}
-
-static bool skl_plane_has_fbc(struct intel_display *display,
- enum intel_fbc_id fbc_id, enum plane_id plane_id)
-{
- if ((DISPLAY_RUNTIME_INFO(display)->fbc_mask & BIT(fbc_id)) == 0)
- return false;
-
- if (DISPLAY_VER(display) >= 20)
- return icl_is_hdr_plane(display, plane_id);
- else
- return plane_id == PLANE_1;
-}
-
static struct intel_fbc *skl_plane_fbc(struct intel_display *display,
enum pipe pipe, enum plane_id plane_id)
{
@@ -2439,13 +2474,10 @@ static bool skl_plane_has_planar(struct intel_display *display,
if (display->platform.skylake || display->platform.broxton)
return false;
- if (DISPLAY_VER(display) == 9 && pipe == PIPE_C)
+ if (pipe == PIPE_C)
return false;
- if (plane_id != PLANE_1 && plane_id != PLANE_2)
- return false;
-
- return true;
+ return plane_id == PLANE_1 || plane_id == PLANE_2;
}
static const u32 *skl_get_plane_formats(struct intel_display *display,
@@ -2461,11 +2493,17 @@ static const u32 *skl_get_plane_formats(struct intel_display *display,
}
}
+static bool glk_plane_has_planar(struct intel_display *display,
+ enum pipe pipe, enum plane_id plane_id)
+{
+ return plane_id == PLANE_1 || plane_id == PLANE_2;
+}
+
static const u32 *glk_get_plane_formats(struct intel_display *display,
enum pipe pipe, enum plane_id plane_id,
int *num_formats)
{
- if (skl_plane_has_planar(display, pipe, plane_id)) {
+ if (glk_plane_has_planar(display, pipe, plane_id)) {
*num_formats = ARRAY_SIZE(glk_planar_formats);
return glk_planar_formats;
} else {
@@ -2705,8 +2743,10 @@ skl_plane_disable_flip_done(struct intel_plane *plane)
static bool skl_plane_has_rc_ccs(struct intel_display *display,
enum pipe pipe, enum plane_id plane_id)
{
- return pipe != PIPE_C &&
- (plane_id == PLANE_1 || plane_id == PLANE_2);
+ if (pipe == PIPE_C)
+ return false;
+
+ return plane_id == PLANE_1 || plane_id == PLANE_2;
}
static u8 skl_plane_caps(struct intel_display *display,
@@ -2834,11 +2874,15 @@ skl_universal_plane_create(struct intel_display *display,
intel_fbc_add_plane(skl_plane_fbc(display, pipe, plane_id), plane);
if (DISPLAY_VER(display) >= 30) {
+ plane->min_width = adl_plane_min_width;
plane->max_width = xe3_plane_max_width;
plane->max_height = icl_plane_max_height;
plane->min_cdclk = icl_plane_min_cdclk;
} else if (DISPLAY_VER(display) >= 11) {
- plane->min_width = icl_plane_min_width;
+ if (DISPLAY_VER(display) >= 14 || display->platform.alderlake_p)
+ plane->min_width = adl_plane_min_width;
+ else
+ plane->min_width = icl_plane_min_width;
if (icl_is_hdr_plane(display, plane_id))
plane->max_width = icl_hdr_plane_max_width;
else
@@ -2930,7 +2974,7 @@ skl_universal_plane_create(struct intel_display *display,
caps = skl_plane_caps(display, pipe, plane_id);
/* FIXME: xe has problems with AUX */
- if (!IS_ENABLED(I915) && !HAS_FLAT_CCS(to_i915(display->drm)))
+ if (!IS_ENABLED(I915) && HAS_AUX_CCS(display))
caps &= ~(INTEL_PLANE_CAP_CCS_RC |
INTEL_PLANE_CAP_CCS_RC_CC |
INTEL_PLANE_CAP_CCS_MC);
@@ -2975,6 +3019,9 @@ skl_universal_plane_create(struct intel_display *display,
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
+ if (DISPLAY_VER(display) >= 12)
+ intel_color_pipeline_plane_init(&plane->base, pipe);
+
drm_plane_create_alpha_property(&plane->base);
drm_plane_create_blend_mode_property(&plane->base,
BIT(DRM_MODE_BLEND_PIXEL_NONE) |
@@ -3057,7 +3104,6 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
fourcc = skl_format_to_fourcc(pixel_format,
val & PLANE_CTL_ORDER_RGBX, alpha);
- fb->format = drm_format_info(fourcc);
tiling = val & PLANE_CTL_TILED_MASK;
switch (tiling) {
@@ -3065,11 +3111,9 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
fb->modifier = DRM_FORMAT_MOD_LINEAR;
break;
case PLANE_CTL_TILED_X:
- plane_config->tiling = I915_TILING_X;
fb->modifier = I915_FORMAT_MOD_X_TILED;
break;
case PLANE_CTL_TILED_Y:
- plane_config->tiling = I915_TILING_Y;
if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE)
if (DISPLAY_VER(display) >= 14)
fb->modifier = I915_FORMAT_MOD_4_TILED_MTL_RC_CCS;
@@ -3110,6 +3154,8 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
goto error;
}
+ fb->format = drm_get_format_info(display->drm, fourcc, fb->modifier);
+
if (!display->params.enable_dpt &&
intel_fb_modifier_uses_dpt(display, fb->modifier)) {
drm_dbg_kms(display->drm, "DPT disabled, skipping initial FB\n");
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane_regs.h b/drivers/gpu/drm/i915/display/skl_universal_plane_regs.h
index ca9fdfbbe57c..6fd4da9f63cf 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane_regs.h
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane_regs.h
@@ -254,6 +254,8 @@
#define PLANE_COLOR_PIPE_CSC_ENABLE REG_BIT(23) /* Pre-ICL */
#define PLANE_COLOR_PLANE_CSC_ENABLE REG_BIT(21) /* ICL+ */
#define PLANE_COLOR_INPUT_CSC_ENABLE REG_BIT(20) /* ICL+ */
+#define PLANE_COLOR_POST_CSC_GAMMA_MULTSEG_ENABLE REG_BIT(15) /* TGL+ */
+#define PLANE_COLOR_PRE_CSC_GAMMA_ENABLE REG_BIT(14)
#define PLANE_COLOR_CSC_MODE_MASK REG_GENMASK(19, 17)
#define PLANE_COLOR_CSC_MODE_BYPASS REG_FIELD_PREP(PLANE_COLOR_CSC_MODE_MASK, 0)
#define PLANE_COLOR_CSC_MODE_YUV601_TO_RGB601 REG_FIELD_PREP(PLANE_COLOR_CSC_MODE_MASK, 1)
@@ -290,6 +292,119 @@
_PLANE_INPUT_CSC_POSTOFF_HI_1_A, _PLANE_INPUT_CSC_POSTOFF_HI_1_B, \
_PLANE_INPUT_CSC_POSTOFF_HI_2_A, _PLANE_INPUT_CSC_POSTOFF_HI_2_B)
+#define _MMIO_PLANE_GAMC(plane, i, a, b) _MMIO(_PIPE(plane, a, b) + (i) * 4)
+
+#define _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_1_A 0x70160
+#define _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_1_B 0x71160
+#define _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_2_A 0x70260
+#define _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_2_B 0x71260
+#define _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_1(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_1_A, \
+ _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_1_B)
+#define _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_2(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_2_A, \
+ _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_2_B)
+#define PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_1(pipe), \
+ _PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH_2(pipe))
+
+#define _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_1_A 0x70164
+#define _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_1_B 0x71164
+#define _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_2_A 0x70264
+#define _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_2_B 0x71264
+#define _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_1(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_1_A, \
+ _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_1_B)
+#define _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_2(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_2_A, \
+ _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_2_B)
+#define PLANE_POST_CSC_GAMC_SEG0_DATA_ENH(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_1(pipe), \
+ _PLANE_POST_CSC_GAMC_SEG0_DATA_ENH_2(pipe))
+
+#define _PLANE_POST_CSC_GAMC_INDEX_ENH_1_A 0x701d8
+#define _PLANE_POST_CSC_GAMC_INDEX_ENH_1_B 0x711d8
+#define _PLANE_POST_CSC_GAMC_INDEX_ENH_2_A 0x702d8
+#define _PLANE_POST_CSC_GAMC_INDEX_ENH_2_B 0x712d8
+#define _PLANE_POST_CSC_GAMC_INDEX_ENH_1(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_INDEX_ENH_1_A, \
+ _PLANE_POST_CSC_GAMC_INDEX_ENH_1_B)
+#define _PLANE_POST_CSC_GAMC_INDEX_ENH_2(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_INDEX_ENH_2_A, \
+ _PLANE_POST_CSC_GAMC_INDEX_ENH_2_B)
+#define PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_POST_CSC_GAMC_INDEX_ENH_1(pipe), \
+ _PLANE_POST_CSC_GAMC_INDEX_ENH_2(pipe))
+
+#define _PLANE_POST_CSC_GAMC_DATA_ENH_1_A 0x701dc
+#define _PLANE_POST_CSC_GAMC_DATA_ENH_1_B 0x711dc
+#define _PLANE_POST_CSC_GAMC_DATA_ENH_2_A 0x702dc
+#define _PLANE_POST_CSC_GAMC_DATA_ENH_2_B 0x712dc
+#define _PLANE_POST_CSC_GAMC_DATA_ENH_1(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_DATA_ENH_1_A, \
+ _PLANE_POST_CSC_GAMC_DATA_ENH_1_B)
+#define _PLANE_POST_CSC_GAMC_DATA_ENH_2(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_DATA_ENH_2_A, \
+ _PLANE_POST_CSC_GAMC_DATA_ENH_2_B)
+#define PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_POST_CSC_GAMC_DATA_ENH_1(pipe), \
+ _PLANE_POST_CSC_GAMC_DATA_ENH_2(pipe))
+
+#define _PLANE_POST_CSC_GAMC_INDEX_1_A 0x704d8
+#define _PLANE_POST_CSC_GAMC_INDEX_1_B 0x714d8
+#define _PLANE_POST_CSC_GAMC_INDEX_2_A 0x705d8
+#define _PLANE_POST_CSC_GAMC_INDEX_2_B 0x715d8
+#define _PLANE_POST_CSC_GAMC_INDEX_1(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_INDEX_1_A, \
+ _PLANE_POST_CSC_GAMC_INDEX_1_B)
+#define _PLANE_POST_CSC_GAMC_INDEX_2(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_INDEX_2_A, \
+ _PLANE_POST_CSC_GAMC_INDEX_2_B)
+#define PLANE_POST_CSC_GAMC_INDEX(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_POST_CSC_GAMC_INDEX_1(pipe), \
+ _PLANE_POST_CSC_GAMC_INDEX_2(pipe))
+
+#define _PLANE_POST_CSC_GAMC_DATA_1_A 0x704dc
+#define _PLANE_POST_CSC_GAMC_DATA_1_B 0x714dc
+#define _PLANE_POST_CSC_GAMC_DATA_2_A 0x705dc
+#define _PLANE_POST_CSC_GAMC_DATA_2_B 0x715dc
+#define _PLANE_POST_CSC_GAMC_DATA_1(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_DATA_1_A, \
+ _PLANE_POST_CSC_GAMC_DATA_1_B)
+#define _PLANE_POST_CSC_GAMC_DATA_2(pipe) _PIPE(pipe, _PLANE_POST_CSC_GAMC_DATA_2_A, \
+ _PLANE_POST_CSC_GAMC_DATA_2_B)
+#define PLANE_POST_CSC_GAMC_DATA(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_POST_CSC_GAMC_DATA_1(pipe), \
+ _PLANE_POST_CSC_GAMC_DATA_2(pipe))
+
+#define _PLANE_PRE_CSC_GAMC_INDEX_ENH_1_A 0x701d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_ENH_1_B 0x711d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_ENH_2_A 0x702d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_ENH_2_B 0x712d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_ENH_1(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_INDEX_ENH_1_A, \
+ _PLANE_PRE_CSC_GAMC_INDEX_ENH_1_B)
+#define _PLANE_PRE_CSC_GAMC_INDEX_ENH_2(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_INDEX_ENH_2_A, \
+ _PLANE_PRE_CSC_GAMC_INDEX_ENH_2_B)
+#define PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_PRE_CSC_GAMC_INDEX_ENH_1(pipe), \
+ _PLANE_PRE_CSC_GAMC_INDEX_ENH_2(pipe))
+#define PLANE_PAL_PREC_AUTO_INCREMENT REG_BIT(10)
+
+#define _PLANE_PRE_CSC_GAMC_DATA_ENH_1_A 0x701d4
+#define _PLANE_PRE_CSC_GAMC_DATA_ENH_1_B 0x711d4
+#define _PLANE_PRE_CSC_GAMC_DATA_ENH_2_A 0x702d4
+#define _PLANE_PRE_CSC_GAMC_DATA_ENH_2_B 0x712d4
+#define _PLANE_PRE_CSC_GAMC_DATA_ENH_1(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_DATA_ENH_1_A, \
+ _PLANE_PRE_CSC_GAMC_DATA_ENH_1_B)
+#define _PLANE_PRE_CSC_GAMC_DATA_ENH_2(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_DATA_ENH_2_A, \
+ _PLANE_PRE_CSC_GAMC_DATA_ENH_2_B)
+#define PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_PRE_CSC_GAMC_DATA_ENH_1(pipe), \
+ _PLANE_PRE_CSC_GAMC_DATA_ENH_2(pipe))
+
+#define _PLANE_PRE_CSC_GAMC_INDEX_1_A 0x704d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_1_B 0x714d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_2_A 0x705d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_2_B 0x715d0
+#define _PLANE_PRE_CSC_GAMC_INDEX_1(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_INDEX_1_A, \
+ _PLANE_PRE_CSC_GAMC_INDEX_1_B)
+#define _PLANE_PRE_CSC_GAMC_INDEX_2(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_INDEX_2_A, \
+ _PLANE_PRE_CSC_GAMC_INDEX_2_B)
+#define PLANE_PRE_CSC_GAMC_INDEX(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_PRE_CSC_GAMC_INDEX_1(pipe), \
+ _PLANE_PRE_CSC_GAMC_INDEX_2(pipe))
+
+#define _PLANE_PRE_CSC_GAMC_DATA_1_A 0x704d4
+#define _PLANE_PRE_CSC_GAMC_DATA_1_B 0x714d4
+#define _PLANE_PRE_CSC_GAMC_DATA_2_A 0x705d4
+#define _PLANE_PRE_CSC_GAMC_DATA_2_B 0x715d4
+#define _PLANE_PRE_CSC_GAMC_DATA_1(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_DATA_1_A, \
+ _PLANE_PRE_CSC_GAMC_DATA_1_B)
+#define _PLANE_PRE_CSC_GAMC_DATA_2(pipe) _PIPE(pipe, _PLANE_PRE_CSC_GAMC_DATA_2_A, \
+ _PLANE_PRE_CSC_GAMC_DATA_2_B)
+#define PLANE_PRE_CSC_GAMC_DATA(pipe, plane, i) _MMIO_PLANE_GAMC(plane, i, _PLANE_PRE_CSC_GAMC_DATA_1(pipe), \
+ _PLANE_PRE_CSC_GAMC_DATA_2(pipe))
+
#define _PLANE_CSC_RY_GY_1_A 0x70210
#define _PLANE_CSC_RY_GY_2_A 0x70310
#define _PLANE_CSC_RY_GY_1_B 0x71210
@@ -324,7 +439,7 @@
#define PLANE_WM_IGNORE_LINES REG_BIT(30)
#define PLANE_WM_AUTO_MIN_ALLOC_EN REG_BIT(29)
#define PLANE_WM_LINES_MASK REG_GENMASK(26, 14)
-#define PLANE_WM_BLOCKS_MASK REG_GENMASK(11, 0)
+#define PLANE_WM_BLOCKS_MASK REG_GENMASK(12, 0)
#define _PLANE_WM_SAGV_1_A 0x70258
#define _PLANE_WM_SAGV_1_B 0x71258
@@ -375,10 +490,10 @@
_PLANE_BUF_CFG_1_A, _PLANE_BUF_CFG_1_B, \
_PLANE_BUF_CFG_2_A, _PLANE_BUF_CFG_2_B)
-/* skl+: 10 bits, icl+ 11 bits, adlp+ 12 bits */
-#define PLANE_BUF_END_MASK REG_GENMASK(27, 16)
+/* skl+: 10 bits, icl+ 11 bits, adlp+ 12 bits, xe3p_lpd 13 bits */
+#define PLANE_BUF_END_MASK REG_GENMASK(28, 16)
#define PLANE_BUF_END(end) REG_FIELD_PREP(PLANE_BUF_END_MASK, (end))
-#define PLANE_BUF_START_MASK REG_GENMASK(11, 0)
+#define PLANE_BUF_START_MASK REG_GENMASK(12, 0)
#define PLANE_BUF_START(start) REG_FIELD_PREP(PLANE_BUF_START_MASK, (start))
#define _PLANE_MIN_BUF_CFG_1_A 0x70274
@@ -389,9 +504,9 @@
_PLANE_MIN_BUF_CFG_1_A, _PLANE_MIN_BUF_CFG_1_B, \
_PLANE_MIN_BUF_CFG_2_A, _PLANE_MIN_BUF_CFG_2_B)
#define PLANE_AUTO_MIN_DBUF_EN REG_BIT(31)
-#define PLANE_MIN_DBUF_BLOCKS_MASK REG_GENMASK(27, 16)
+#define PLANE_MIN_DBUF_BLOCKS_MASK REG_GENMASK(28, 16)
#define PLANE_MIN_DBUF_BLOCKS(val) REG_FIELD_PREP(PLANE_MIN_DBUF_BLOCKS_MASK, (val))
-#define PLANE_INTERIM_DBUF_BLOCKS_MASK REG_GENMASK(11, 0)
+#define PLANE_INTERIM_DBUF_BLOCKS_MASK REG_GENMASK(12, 0)
#define PLANE_INTERIM_DBUF_BLOCKS(val) REG_FIELD_PREP(PLANE_INTERIM_DBUF_BLOCKS_MASK, (val))
/* tgl+ */
@@ -455,4 +570,16 @@
_SEL_FETCH_PLANE_OFFSET_5_A, _SEL_FETCH_PLANE_OFFSET_5_B, \
_SEL_FETCH_PLANE_OFFSET_6_A, _SEL_FETCH_PLANE_OFFSET_6_B)
+#define _PLANE_PIXEL_NORMALIZE_1_A 0x701a8
+#define _PLANE_PIXEL_NORMALIZE_2_A 0x702a8
+#define _PLANE_PIXEL_NORMALIZE_1_B 0x711a8
+#define _PLANE_PIXEL_NORMALIZE_2_B 0x712a8
+#define PLANE_PIXEL_NORMALIZE(pipe, plane) _MMIO_SKL_PLANE((pipe), (plane), \
+ _PLANE_PIXEL_NORMALIZE_1_A, _PLANE_PIXEL_NORMALIZE_1_B, \
+ _PLANE_PIXEL_NORMALIZE_2_A, _PLANE_PIXEL_NORMALIZE_2_B)
+#define PLANE_PIXEL_NORMALIZE_ENABLE REG_BIT(31)
+#define PLANE_PIXEL_NORMALIZE_NORM_FACTOR_MASK REG_GENMASK(15, 0)
+#define PLANE_PIXEL_NORMALIZE_NORM_FACTOR(val) REG_FIELD_PREP(PLANE_PIXEL_NORMALIZE_NORM_FACTOR_MASK, (val))
+#define PLANE_PIXEL_NORMALIZE_NORM_FACTOR_1_0 0x3c00
+
#endif /* __SKL_UNIVERSAL_PLANE_REGS_H__ */
diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c
index d74cbb43ae6f..54e9e0be019d 100644
--- a/drivers/gpu/drm/i915/display/skl_watermark.c
+++ b/drivers/gpu/drm/i915/display/skl_watermark.c
@@ -10,7 +10,6 @@
#include "soc/intel_dram.h"
#include "i915_reg.h"
-#include "i915_utils.h"
#include "i9xx_wm.h"
#include "intel_atomic.h"
#include "intel_bw.h"
@@ -23,12 +22,16 @@
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_fb.h"
#include "intel_fixed.h"
#include "intel_flipq.h"
#include "intel_pcode.h"
#include "intel_plane.h"
+#include "intel_vblank.h"
#include "intel_wm.h"
+#include "skl_prefill.h"
+#include "skl_scaler.h"
#include "skl_universal_plane_regs.h"
#include "skl_watermark.h"
#include "skl_watermark_regs.h"
@@ -632,15 +635,22 @@ skl_cursor_allocation(const struct intel_crtc_state *crtc_state,
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_plane *plane = to_intel_plane(crtc_state->uapi.crtc->cursor);
+ const struct drm_mode_config *mode_config = &display->drm->mode_config;
+ const struct drm_format_info *info;
struct skl_wm_level wm = {};
int ret, min_ddb_alloc = 0;
struct skl_wm_params wp;
+ u64 modifier;
+ u32 format;
int level;
- ret = skl_compute_wm_params(crtc_state, 256,
- drm_format_info(DRM_FORMAT_ARGB8888),
- DRM_FORMAT_MOD_LINEAR,
- DRM_MODE_ROTATE_0,
+ format = DRM_FORMAT_ARGB8888;
+ modifier = DRM_FORMAT_MOD_LINEAR;
+
+ info = drm_get_format_info(display->drm, format, modifier);
+
+ ret = skl_compute_wm_params(crtc_state, mode_config->cursor_width,
+ info, modifier, DRM_MODE_ROTATE_0,
crtc_state->pixel_rate, &wp, 0, 0);
drm_WARN_ON(display->drm, ret);
@@ -1636,26 +1646,11 @@ skl_wm_method2(u32 pixel_rate, u32 pipe_htotal, u32 latency,
return ret;
}
-static uint_fixed_16_16_t
-intel_get_linetime_us(const struct intel_crtc_state *crtc_state)
+static int skl_wm_linetime_us(const struct intel_crtc_state *crtc_state,
+ int pixel_rate)
{
- struct intel_display *display = to_intel_display(crtc_state);
- u32 pixel_rate;
- u32 crtc_htotal;
- uint_fixed_16_16_t linetime_us;
-
- if (!crtc_state->hw.active)
- return u32_to_fixed16(0);
-
- pixel_rate = crtc_state->pixel_rate;
-
- if (drm_WARN_ON(display->drm, pixel_rate == 0))
- return u32_to_fixed16(0);
-
- crtc_htotal = crtc_state->hw.pipe_mode.crtc_htotal;
- linetime_us = div_fixed16(crtc_htotal * 1000, pixel_rate);
-
- return linetime_us;
+ return DIV_ROUND_UP(crtc_state->hw.pipe_mode.crtc_htotal * 1000,
+ pixel_rate);
}
static int
@@ -1743,7 +1738,7 @@ skl_compute_wm_params(const struct intel_crtc_state *crtc_state,
wp->y_tile_minimum = mul_u32_fixed16(wp->y_min_scanlines,
wp->plane_blocks_per_line);
- wp->linetime_us = fixed16_to_u32_round_up(intel_get_linetime_us(crtc_state));
+ wp->linetime_us = skl_wm_linetime_us(crtc_state, plane_pixel_rate);
return 0;
}
@@ -1824,6 +1819,8 @@ static void skl_compute_plane_wm(const struct intel_crtc_state *crtc_state,
if (wp->y_tiled) {
selected_result = max_fixed16(method2, wp->y_tile_minimum);
+ } else if (DISPLAY_VER(display) >= 35) {
+ selected_result = method2;
} else {
if ((wp->cpp * crtc_state->hw.pipe_mode.crtc_htotal /
wp->dbuf_block_size < 1) &&
@@ -1878,18 +1875,21 @@ static void skl_compute_plane_wm(const struct intel_crtc_state *crtc_state,
} else {
blocks++;
}
-
- /*
- * Make sure result blocks for higher latency levels are
- * at least as high as level below the current level.
- * Assumption in DDB algorithm optimization for special
- * cases. Also covers Display WA #1125 for RC.
- */
- if (result_prev->blocks > blocks)
- blocks = result_prev->blocks;
}
}
+ /*
+ * Make sure result blocks for higher latency levels are
+ * at least as high as level below the current level.
+ * Assumption in DDB algorithm optimization for special
+ * cases. Also covers Display WA #1125 for RC.
+ *
+ * Let's always do this as the algorithm can give non
+ * monotonic results on any platform.
+ */
+ blocks = max_t(u32, blocks, result_prev->blocks);
+ lines = max_t(u32, lines, result_prev->lines);
+
if (DISPLAY_VER(display) >= 11) {
if (wp->y_tiled) {
int extra_lines;
@@ -2157,103 +2157,55 @@ static int icl_build_plane_wm(struct intel_crtc_state *crtc_state,
return 0;
}
-static int
-cdclk_prefill_adjustment(const struct intel_crtc_state *crtc_state)
+unsigned int skl_wm0_prefill_lines_worst(const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
- struct intel_atomic_state *state =
- to_intel_atomic_state(crtc_state->uapi.state);
- const struct intel_cdclk_state *cdclk_state;
-
- cdclk_state = intel_atomic_get_cdclk_state(state);
- if (IS_ERR(cdclk_state)) {
- drm_WARN_ON(display->drm, PTR_ERR(cdclk_state));
- return 1;
- }
-
- return min(1, DIV_ROUND_UP(crtc_state->pixel_rate,
- 2 * intel_cdclk_logical(cdclk_state)));
-}
-
-static int
-dsc_prefill_latency(const struct intel_crtc_state *crtc_state)
-{
- struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- const struct intel_crtc_scaler_state *scaler_state =
- &crtc_state->scaler_state;
- int linetime = DIV_ROUND_UP(1000 * crtc_state->hw.adjusted_mode.htotal,
- crtc_state->hw.adjusted_mode.clock);
- int num_scaler_users = hweight32(scaler_state->scaler_users);
- int chroma_downscaling_factor =
- crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420 ? 2 : 1;
- u32 dsc_prefill_latency = 0;
-
- if (!crtc_state->dsc.compression_enable ||
- !num_scaler_users ||
- num_scaler_users > crtc->num_scalers)
- return dsc_prefill_latency;
-
- dsc_prefill_latency = DIV_ROUND_UP(15 * linetime * chroma_downscaling_factor, 10);
-
- for (int i = 0; i < num_scaler_users; i++) {
- u64 hscale_k, vscale_k;
-
- hscale_k = max(1000, mul_u32_u32(scaler_state->scalers[i].hscale, 1000) >> 16);
- vscale_k = max(1000, mul_u32_u32(scaler_state->scalers[i].vscale, 1000) >> 16);
- dsc_prefill_latency = DIV_ROUND_UP_ULL(dsc_prefill_latency * hscale_k * vscale_k,
- 1000000);
- }
-
- dsc_prefill_latency *= cdclk_prefill_adjustment(crtc_state);
+ struct intel_plane *plane = to_intel_plane(crtc_state->uapi.crtc->primary);
+ const struct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode;
+ int ret, pixel_rate, width, level = 0;
+ const struct drm_format_info *info;
+ struct skl_wm_level wm = {};
+ struct skl_wm_params wp;
+ unsigned int latency;
+ u64 modifier;
+ u32 format;
- return intel_usecs_to_scanlines(&crtc_state->hw.adjusted_mode, dsc_prefill_latency);
-}
+ /* only expected to be used for VRR guardband calculation */
+ drm_WARN_ON(display->drm, !HAS_VRR(display));
-static int
-scaler_prefill_latency(const struct intel_crtc_state *crtc_state)
-{
- const struct intel_crtc_scaler_state *scaler_state =
- &crtc_state->scaler_state;
- int num_scaler_users = hweight32(scaler_state->scaler_users);
- int scaler_prefill_latency = 0;
- int linetime = DIV_ROUND_UP(1000 * crtc_state->hw.adjusted_mode.htotal,
- crtc_state->hw.adjusted_mode.clock);
+ /* FIXME rather ugly to pick this by hand but maybe no better way? */
+ format = DRM_FORMAT_XBGR16161616F;
+ if (HAS_4TILE(display))
+ modifier = I915_FORMAT_MOD_4_TILED;
+ else
+ modifier = I915_FORMAT_MOD_Y_TILED;
- if (!num_scaler_users)
- return scaler_prefill_latency;
+ info = drm_get_format_info(display->drm, format, modifier);
- scaler_prefill_latency = 4 * linetime;
+ pixel_rate = DIV_ROUND_UP_ULL(mul_u32_u32(skl_scaler_max_total_scale(crtc_state),
+ pipe_mode->crtc_clock),
+ 0x10000);
- if (num_scaler_users > 1) {
- u64 hscale_k = max(1000, mul_u32_u32(scaler_state->scalers[0].hscale, 1000) >> 16);
- u64 vscale_k = max(1000, mul_u32_u32(scaler_state->scalers[0].vscale, 1000) >> 16);
- int chroma_downscaling_factor =
- crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420 ? 2 : 1;
- int latency;
+ /* FIXME limit to max plane width? */
+ width = DIV_ROUND_UP_ULL(mul_u32_u32(skl_scaler_max_hscale(crtc_state),
+ pipe_mode->crtc_hdisplay),
+ 0x10000);
- latency = DIV_ROUND_UP_ULL((4 * linetime * hscale_k * vscale_k *
- chroma_downscaling_factor), 1000000);
- scaler_prefill_latency += latency;
- }
+ /* FIXME is 90/270 rotation worse than 0/180? */
+ ret = skl_compute_wm_params(crtc_state, width, info,
+ modifier, DRM_MODE_ROTATE_0,
+ pixel_rate, &wp, 0, 1);
+ drm_WARN_ON(display->drm, ret);
- scaler_prefill_latency *= cdclk_prefill_adjustment(crtc_state);
+ latency = skl_wm_latency(display, level, &wp);
- return intel_usecs_to_scanlines(&crtc_state->hw.adjusted_mode, scaler_prefill_latency);
-}
+ skl_compute_plane_wm(crtc_state, plane, level, latency, &wp, &wm, &wm);
-static bool
-skl_is_vblank_too_short(const struct intel_crtc_state *crtc_state,
- int wm0_lines, int latency)
-{
- const struct drm_display_mode *adjusted_mode =
- &crtc_state->hw.adjusted_mode;
+ /* FIXME is this sane? */
+ if (wm.min_ddb_alloc == U16_MAX)
+ wm.lines = skl_wm_max_lines(display);
- return crtc_state->framestart_delay +
- intel_usecs_to_scanlines(adjusted_mode, latency) +
- scaler_prefill_latency(crtc_state) +
- dsc_prefill_latency(crtc_state) +
- wm0_lines >
- adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vblank_start;
+ return wm.lines << 16;
}
static int skl_max_wm0_lines(const struct intel_crtc_state *crtc_state)
@@ -2272,15 +2224,21 @@ static int skl_max_wm0_lines(const struct intel_crtc_state *crtc_state)
return wm0_lines;
}
+unsigned int skl_wm0_prefill_lines(const struct intel_crtc_state *crtc_state)
+{
+ return skl_max_wm0_lines(crtc_state) << 16;
+}
+
/*
* TODO: In case we use PKG_C_LATENCY to allow C-states when the delayed vblank
* size is too small for the package C exit latency we need to notify PSR about
* the scenario to apply Wa_16025596647.
*/
static int skl_max_wm_level_for_vblank(struct intel_crtc_state *crtc_state,
- int wm0_lines)
+ const struct skl_prefill_ctx *ctx)
{
struct intel_display *display = to_intel_display(crtc_state);
+ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
int level;
for (level = display->wm.num_levels - 1; level >= 0; level--) {
@@ -2295,10 +2253,13 @@ static int skl_max_wm_level_for_vblank(struct intel_crtc_state *crtc_state,
if (level == 0)
latency = 0;
- if (!skl_is_vblank_too_short(crtc_state, wm0_lines, latency))
+ if (!skl_prefill_vblank_too_short(ctx, crtc_state, latency))
return level;
}
+ drm_dbg_kms(display->drm, "[CRTC:%d:%s] Not enough time in vblank for prefill\n",
+ crtc->base.base.id, crtc->base.name);
+
return -EINVAL;
}
@@ -2306,14 +2267,15 @@ static int skl_wm_check_vblank(struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
- int wm0_lines, level;
+ struct skl_prefill_ctx ctx;
+ int level;
if (!crtc_state->hw.active)
return 0;
- wm0_lines = skl_max_wm0_lines(crtc_state);
+ skl_prefill_init(&ctx, crtc_state);
- level = skl_max_wm_level_for_vblank(crtc_state, wm0_lines);
+ level = skl_max_wm_level_for_vblank(crtc_state, &ctx);
if (level < 0)
return level;
@@ -2323,6 +2285,13 @@ static int skl_wm_check_vblank(struct intel_crtc_state *crtc_state)
*/
crtc_state->wm_level_disabled = level < display->wm.num_levels - 1;
+ /*
+ * TODO: assert that we are in fact using the maximum guardband
+ * if we end up disabling any WM levels here. Otherwise we clearly
+ * failed in using a realistic worst case prefill estimate when
+ * determining the guardband size.
+ */
+
for (level++; level < display->wm.num_levels; level++) {
enum plane_id plane_id;
@@ -2341,8 +2310,8 @@ static int skl_wm_check_vblank(struct intel_crtc_state *crtc_state)
if (DISPLAY_VER(display) >= 12 &&
display->sagv.block_time_us &&
- skl_is_vblank_too_short(crtc_state, wm0_lines,
- display->sagv.block_time_us)) {
+ skl_prefill_vblank_too_short(&ctx, crtc_state,
+ display->sagv.block_time_us)) {
enum plane_id plane_id;
for_each_plane_id_on_crtc(crtc, plane_id) {
@@ -3174,12 +3143,60 @@ void skl_watermark_ipc_init(struct intel_display *display)
skl_watermark_ipc_update(display);
}
-static void
-adjust_wm_latency(struct intel_display *display,
- u16 wm[], int num_levels, int read_latency)
+static void multiply_wm_latency(struct intel_display *display, int mult)
+{
+ u16 *wm = display->wm.skl_latency;
+ int level, num_levels = display->wm.num_levels;
+
+ for (level = 0; level < num_levels; level++)
+ wm[level] *= mult;
+}
+
+static void increase_wm_latency(struct intel_display *display, int inc)
+{
+ u16 *wm = display->wm.skl_latency;
+ int level, num_levels = display->wm.num_levels;
+
+ wm[0] += inc;
+
+ for (level = 1; level < num_levels; level++) {
+ if (wm[level] == 0)
+ break;
+
+ wm[level] += inc;
+ }
+}
+
+static bool need_16gb_dimm_wa(struct intel_display *display)
{
const struct dram_info *dram_info = intel_dram_info(display->drm);
- int i, level;
+
+ return (display->platform.skylake || display->platform.kabylake ||
+ display->platform.coffeelake || display->platform.cometlake ||
+ DISPLAY_VER(display) == 11) && dram_info->has_16gb_dimms;
+}
+
+static int wm_read_latency(struct intel_display *display)
+{
+ if (DISPLAY_VER(display) >= 14)
+ return 6;
+ else if (DISPLAY_VER(display) >= 12)
+ return 3;
+ else
+ return 2;
+}
+
+static void sanitize_wm_latency(struct intel_display *display)
+{
+ u16 *wm = display->wm.skl_latency;
+ int level, num_levels = display->wm.num_levels;
+
+ /*
+ * Xe3p and beyond should ignore level 0's reported latency and
+ * always apply WaWmMemoryReadLatency logic.
+ */
+ if (DISPLAY_VER(display) >= 35)
+ wm[0] = 0;
/*
* If a level n (n > 1) has a 0us latency, all levels m (m >= n)
@@ -3187,14 +3204,38 @@ adjust_wm_latency(struct intel_display *display,
* of the punit to satisfy this requirement.
*/
for (level = 1; level < num_levels; level++) {
- if (wm[level] == 0) {
- for (i = level + 1; i < num_levels; i++)
- wm[i] = 0;
+ if (wm[level] == 0)
+ break;
+ }
- num_levels = level;
+ for (level = level + 1; level < num_levels; level++)
+ wm[level] = 0;
+}
+
+static void make_wm_latency_monotonic(struct intel_display *display)
+{
+ u16 *wm = display->wm.skl_latency;
+ int level, num_levels = display->wm.num_levels;
+
+ for (level = 1; level < num_levels; level++) {
+ if (wm[level] == 0)
break;
- }
+
+ wm[level] = max(wm[level], wm[level-1]);
}
+}
+
+static void
+adjust_wm_latency(struct intel_display *display)
+{
+ u16 *wm = display->wm.skl_latency;
+
+ if (display->platform.dg2)
+ multiply_wm_latency(display, 2);
+
+ sanitize_wm_latency(display);
+
+ make_wm_latency_monotonic(display);
/*
* WaWmMemoryReadLatency
@@ -3203,24 +3244,22 @@ adjust_wm_latency(struct intel_display *display,
* to add proper adjustment to each valid level we retrieve
* from the punit when level 0 response data is 0us.
*/
- if (wm[0] == 0) {
- for (level = 0; level < num_levels; level++)
- wm[level] += read_latency;
- }
+ if (wm[0] == 0)
+ increase_wm_latency(display, wm_read_latency(display));
/*
- * WA Level-0 adjustment for 16Gb DIMMs: SKL+
+ * WA Level-0 adjustment for 16Gb+ DIMMs: SKL+
* If we could not get dimm info enable this WA to prevent from
- * any underrun. If not able to get DIMM info assume 16Gb DIMM
+ * any underrun. If not able to get DIMM info assume 16Gb+ DIMM
* to avoid any underrun.
*/
- if (!display->platform.dg2 && dram_info->has_16gb_dimms)
- wm[0] += 1;
+ if (need_16gb_dimm_wa(display))
+ increase_wm_latency(display, 1);
}
-static void mtl_read_wm_latency(struct intel_display *display, u16 wm[])
+static void mtl_read_wm_latency(struct intel_display *display)
{
- int num_levels = display->wm.num_levels;
+ u16 *wm = display->wm.skl_latency;
u32 val;
val = intel_de_read(display, MTL_LATENCY_LP0_LP1);
@@ -3234,15 +3273,11 @@ static void mtl_read_wm_latency(struct intel_display *display, u16 wm[])
val = intel_de_read(display, MTL_LATENCY_LP4_LP5);
wm[4] = REG_FIELD_GET(MTL_LATENCY_LEVEL_EVEN_MASK, val);
wm[5] = REG_FIELD_GET(MTL_LATENCY_LEVEL_ODD_MASK, val);
-
- adjust_wm_latency(display, wm, num_levels, 6);
}
-static void skl_read_wm_latency(struct intel_display *display, u16 wm[])
+static void skl_read_wm_latency(struct intel_display *display)
{
- int num_levels = display->wm.num_levels;
- int read_latency = DISPLAY_VER(display) >= 12 ? 3 : 2;
- int mult = display->platform.dg2 ? 2 : 1;
+ u16 *wm = display->wm.skl_latency;
u32 val;
int ret;
@@ -3254,10 +3289,10 @@ static void skl_read_wm_latency(struct intel_display *display, u16 wm[])
return;
}
- wm[0] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_0_4_MASK, val) * mult;
- wm[1] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_1_5_MASK, val) * mult;
- wm[2] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_2_6_MASK, val) * mult;
- wm[3] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_3_7_MASK, val) * mult;
+ wm[0] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_0_4_MASK, val);
+ wm[1] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_1_5_MASK, val);
+ wm[2] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_2_6_MASK, val);
+ wm[3] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_3_7_MASK, val);
/* read the second set of memory latencies[4:7] */
val = 1; /* data0 to be programmed to 1 for second set */
@@ -3267,12 +3302,10 @@ static void skl_read_wm_latency(struct intel_display *display, u16 wm[])
return;
}
- wm[4] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_0_4_MASK, val) * mult;
- wm[5] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_1_5_MASK, val) * mult;
- wm[6] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_2_6_MASK, val) * mult;
- wm[7] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_3_7_MASK, val) * mult;
-
- adjust_wm_latency(display, wm, num_levels, read_latency);
+ wm[4] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_0_4_MASK, val);
+ wm[5] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_1_5_MASK, val);
+ wm[6] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_2_6_MASK, val);
+ wm[7] = REG_FIELD_GET(GEN9_MEM_LATENCY_LEVEL_3_7_MASK, val);
}
static void skl_setup_wm_latency(struct intel_display *display)
@@ -3283,11 +3316,15 @@ static void skl_setup_wm_latency(struct intel_display *display)
display->wm.num_levels = 8;
if (DISPLAY_VER(display) >= 14)
- mtl_read_wm_latency(display, display->wm.skl_latency);
+ mtl_read_wm_latency(display);
else
- skl_read_wm_latency(display, display->wm.skl_latency);
+ skl_read_wm_latency(display);
- intel_print_wm_latency(display, "Gen9 Plane", display->wm.skl_latency);
+ intel_print_wm_latency(display, "original", display->wm.skl_latency);
+
+ adjust_wm_latency(display);
+
+ intel_print_wm_latency(display, "adjusted", display->wm.skl_latency);
}
static struct intel_global_state *intel_dbuf_duplicate_state(struct intel_global_obj *obj)
@@ -3456,7 +3493,10 @@ void intel_dbuf_mdclk_cdclk_ratio_update(struct intel_display *display,
if (!HAS_MBUS_JOINING(display))
return;
- if (DISPLAY_VER(display) >= 20)
+ if (DISPLAY_VER(display) >= 35)
+ intel_de_rmw(display, MBUS_CTL, XE3P_MBUS_TRANSLATION_THROTTLE_MIN_MASK,
+ XE3P_MBUS_TRANSLATION_THROTTLE_MIN(ratio - 1));
+ else if (DISPLAY_VER(display) >= 20)
intel_de_rmw(display, MBUS_CTL, MBUS_TRANSLATION_THROTTLE_MIN_MASK,
MBUS_TRANSLATION_THROTTLE_MIN(ratio - 1));
@@ -3467,9 +3507,14 @@ void intel_dbuf_mdclk_cdclk_ratio_update(struct intel_display *display,
ratio, str_yes_no(joined_mbus));
for_each_dbuf_slice(display, slice)
- intel_de_rmw(display, DBUF_CTL_S(slice),
- DBUF_MIN_TRACKER_STATE_SERVICE_MASK,
- DBUF_MIN_TRACKER_STATE_SERVICE(ratio - 1));
+ if (DISPLAY_VER(display) >= 35)
+ intel_de_rmw(display, DBUF_CTL_S(slice),
+ XE3P_DBUF_MIN_TRACKER_STATE_SERVICE_MASK,
+ XE3P_DBUF_MIN_TRACKER_STATE_SERVICE(ratio - 1));
+ else
+ intel_de_rmw(display, DBUF_CTL_S(slice),
+ DBUF_MIN_TRACKER_STATE_SERVICE_MASK,
+ DBUF_MIN_TRACKER_STATE_SERVICE(ratio - 1));
}
static void intel_dbuf_mdclk_min_tracker_update(struct intel_atomic_state *state)
diff --git a/drivers/gpu/drm/i915/display/skl_watermark.h b/drivers/gpu/drm/i915/display/skl_watermark.h
index 62790816f030..6bc2ec9164bf 100644
--- a/drivers/gpu/drm/i915/display/skl_watermark.h
+++ b/drivers/gpu/drm/i915/display/skl_watermark.h
@@ -79,5 +79,8 @@ void intel_program_dpkgc_latency(struct intel_atomic_state *state);
bool intel_dbuf_pmdemand_needs_update(struct intel_atomic_state *state);
+unsigned int skl_wm0_prefill_lines_worst(const struct intel_crtc_state *crtc_state);
+unsigned int skl_wm0_prefill_lines(const struct intel_crtc_state *crtc_state);
+
#endif /* __SKL_WATERMARK_H__ */
diff --git a/drivers/gpu/drm/i915/display/skl_watermark_regs.h b/drivers/gpu/drm/i915/display/skl_watermark_regs.h
index c5572fc0e847..abf56ac31105 100644
--- a/drivers/gpu/drm/i915/display/skl_watermark_regs.h
+++ b/drivers/gpu/drm/i915/display/skl_watermark_regs.h
@@ -32,16 +32,18 @@
#define MBUS_BBOX_CTL_S1 _MMIO(0x45040)
#define MBUS_BBOX_CTL_S2 _MMIO(0x45044)
-#define MBUS_CTL _MMIO(0x4438C)
-#define MBUS_JOIN REG_BIT(31)
-#define MBUS_HASHING_MODE_MASK REG_BIT(30)
-#define MBUS_HASHING_MODE_2x2 REG_FIELD_PREP(MBUS_HASHING_MODE_MASK, 0)
-#define MBUS_HASHING_MODE_1x4 REG_FIELD_PREP(MBUS_HASHING_MODE_MASK, 1)
-#define MBUS_JOIN_PIPE_SELECT_MASK REG_GENMASK(28, 26)
-#define MBUS_JOIN_PIPE_SELECT(pipe) REG_FIELD_PREP(MBUS_JOIN_PIPE_SELECT_MASK, pipe)
-#define MBUS_JOIN_PIPE_SELECT_NONE MBUS_JOIN_PIPE_SELECT(7)
-#define MBUS_TRANSLATION_THROTTLE_MIN_MASK REG_GENMASK(15, 13)
-#define MBUS_TRANSLATION_THROTTLE_MIN(val) REG_FIELD_PREP(MBUS_TRANSLATION_THROTTLE_MIN_MASK, val)
+#define MBUS_CTL _MMIO(0x4438C)
+#define MBUS_JOIN REG_BIT(31)
+#define MBUS_HASHING_MODE_MASK REG_BIT(30)
+#define MBUS_HASHING_MODE_2x2 REG_FIELD_PREP(MBUS_HASHING_MODE_MASK, 0)
+#define MBUS_HASHING_MODE_1x4 REG_FIELD_PREP(MBUS_HASHING_MODE_MASK, 1)
+#define MBUS_JOIN_PIPE_SELECT_MASK REG_GENMASK(28, 26)
+#define MBUS_JOIN_PIPE_SELECT(pipe) REG_FIELD_PREP(MBUS_JOIN_PIPE_SELECT_MASK, pipe)
+#define MBUS_JOIN_PIPE_SELECT_NONE MBUS_JOIN_PIPE_SELECT(7)
+#define XE3P_MBUS_TRANSLATION_THROTTLE_MIN_MASK REG_GENMASK(16, 13)
+#define XE3P_MBUS_TRANSLATION_THROTTLE_MIN(val) REG_FIELD_PREP(XE3P_MBUS_TRANSLATION_THROTTLE_MIN_MASK, val)
+#define MBUS_TRANSLATION_THROTTLE_MIN_MASK REG_GENMASK(15, 13)
+#define MBUS_TRANSLATION_THROTTLE_MIN(val) REG_FIELD_PREP(MBUS_TRANSLATION_THROTTLE_MIN_MASK, val)
/*
* The below are numbered starting from "S1" on gen11/gen12, but starting
@@ -51,20 +53,22 @@
* way things will be named by the hardware team going forward, plus it's more
* consistent with how most of the rest of our registers are named.
*/
-#define _DBUF_CTL_S0 0x45008
-#define _DBUF_CTL_S1 0x44FE8
-#define _DBUF_CTL_S2 0x44300
-#define _DBUF_CTL_S3 0x44304
-#define DBUF_CTL_S(slice) _MMIO(_PICK(slice, \
- _DBUF_CTL_S0, \
- _DBUF_CTL_S1, \
- _DBUF_CTL_S2, \
- _DBUF_CTL_S3))
-#define DBUF_POWER_REQUEST REG_BIT(31)
-#define DBUF_POWER_STATE REG_BIT(30)
-#define DBUF_TRACKER_STATE_SERVICE_MASK REG_GENMASK(23, 19)
-#define DBUF_TRACKER_STATE_SERVICE(x) REG_FIELD_PREP(DBUF_TRACKER_STATE_SERVICE_MASK, x)
-#define DBUF_MIN_TRACKER_STATE_SERVICE_MASK REG_GENMASK(18, 16) /* ADL-P+ */
+#define _DBUF_CTL_S0 0x45008
+#define _DBUF_CTL_S1 0x44FE8
+#define _DBUF_CTL_S2 0x44300
+#define _DBUF_CTL_S3 0x44304
+#define DBUF_CTL_S(slice) _MMIO(_PICK(slice, \
+ _DBUF_CTL_S0, \
+ _DBUF_CTL_S1, \
+ _DBUF_CTL_S2, \
+ _DBUF_CTL_S3))
+#define DBUF_POWER_REQUEST REG_BIT(31)
+#define DBUF_POWER_STATE REG_BIT(30)
+#define DBUF_TRACKER_STATE_SERVICE_MASK REG_GENMASK(23, 19)
+#define DBUF_TRACKER_STATE_SERVICE(x) REG_FIELD_PREP(DBUF_TRACKER_STATE_SERVICE_MASK, x)
+#define XE3P_DBUF_MIN_TRACKER_STATE_SERVICE_MASK REG_GENMASK(20, 16)
+#define XE3P_DBUF_MIN_TRACKER_STATE_SERVICE(x) REG_FIELD_PREP(XE3P_DBUF_MIN_TRACKER_STATE_SERVICE_MASK, x)
+#define DBUF_MIN_TRACKER_STATE_SERVICE_MASK REG_GENMASK(18, 16) /* ADL-P+ */
#define DBUF_MIN_TRACKER_STATE_SERVICE(x) REG_FIELD_PREP(DBUF_MIN_TRACKER_STATE_SERVICE_MASK, x) /* ADL-P+ */
#define MTL_LATENCY_LP0_LP1 _MMIO(0x45780)
diff --git a/drivers/gpu/drm/i915/display/vlv_clock.c b/drivers/gpu/drm/i915/display/vlv_clock.c
new file mode 100644
index 000000000000..1abdae453514
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/vlv_clock.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: MIT
+/* Copyright © 2025 Intel Corporation */
+
+#include <drm/drm_print.h>
+
+#include "intel_display_core.h"
+#include "intel_display_types.h"
+#include "vlv_clock.h"
+#include "vlv_sideband.h"
+
+/*
+ * FIXME: The caching of hpll_freq and czclk_freq relies on the first calls
+ * occurring at a time when they can actually be read. This appears to be the
+ * case, but is somewhat fragile. Make the initialization explicit at a point
+ * where they can be reliably read.
+ */
+
+/* returns HPLL frequency in kHz */
+int vlv_clock_get_hpll_vco(struct drm_device *drm)
+{
+ struct intel_display *display = to_intel_display(drm);
+ int hpll_freq, vco_freq[] = { 800, 1600, 2000, 2400 };
+
+ if (!display->vlv_clock.hpll_freq) {
+ vlv_cck_get(drm);
+ /* Obtain SKU information */
+ hpll_freq = vlv_cck_read(drm, CCK_FUSE_REG) &
+ CCK_FUSE_HPLL_FREQ_MASK;
+ vlv_cck_put(drm);
+
+ display->vlv_clock.hpll_freq = vco_freq[hpll_freq] * 1000;
+
+ drm_dbg_kms(drm, "HPLL frequency: %d kHz\n", display->vlv_clock.hpll_freq);
+ }
+
+ return display->vlv_clock.hpll_freq;
+}
+
+static int vlv_clock_get_cck(struct drm_device *drm,
+ const char *name, u32 reg, int ref_freq)
+{
+ u32 val;
+ int divider;
+
+ vlv_cck_get(drm);
+ val = vlv_cck_read(drm, reg);
+ vlv_cck_put(drm);
+
+ divider = val & CCK_FREQUENCY_VALUES;
+
+ drm_WARN(drm, (val & CCK_FREQUENCY_STATUS) !=
+ (divider << CCK_FREQUENCY_STATUS_SHIFT),
+ "%s change in progress\n", name);
+
+ return DIV_ROUND_CLOSEST(ref_freq << 1, divider + 1);
+}
+
+int vlv_clock_get_hrawclk(struct drm_device *drm)
+{
+ /* RAWCLK_FREQ_VLV register updated from power well code */
+ return vlv_clock_get_cck(drm, "hrawclk", CCK_DISPLAY_REF_CLOCK_CONTROL,
+ vlv_clock_get_hpll_vco(drm));
+}
+
+int vlv_clock_get_czclk(struct drm_device *drm)
+{
+ struct intel_display *display = to_intel_display(drm);
+
+ if (!display->vlv_clock.czclk_freq) {
+ display->vlv_clock.czclk_freq = vlv_clock_get_cck(drm, "czclk", CCK_CZ_CLOCK_CONTROL,
+ vlv_clock_get_hpll_vco(drm));
+ drm_dbg_kms(drm, "CZ clock rate: %d kHz\n", display->vlv_clock.czclk_freq);
+ }
+
+ return display->vlv_clock.czclk_freq;
+}
+
+int vlv_clock_get_cdclk(struct drm_device *drm)
+{
+ return vlv_clock_get_cck(drm, "cdclk", CCK_DISPLAY_CLOCK_CONTROL,
+ vlv_clock_get_hpll_vco(drm));
+}
+
+int vlv_clock_get_gpll(struct drm_device *drm)
+{
+ return vlv_clock_get_cck(drm, "GPLL ref", CCK_GPLL_CLOCK_CONTROL,
+ vlv_clock_get_czclk(drm));
+}
diff --git a/drivers/gpu/drm/i915/display/vlv_clock.h b/drivers/gpu/drm/i915/display/vlv_clock.h
new file mode 100644
index 000000000000..5742ed3c628d
--- /dev/null
+++ b/drivers/gpu/drm/i915/display/vlv_clock.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __VLV_CLOCK_H__
+#define __VLV_CLOCK_H__
+
+struct drm_device;
+
+#ifdef I915
+int vlv_clock_get_hpll_vco(struct drm_device *drm);
+int vlv_clock_get_hrawclk(struct drm_device *drm);
+int vlv_clock_get_czclk(struct drm_device *drm);
+int vlv_clock_get_cdclk(struct drm_device *drm);
+int vlv_clock_get_gpll(struct drm_device *drm);
+#else
+static inline int vlv_clock_get_hpll_vco(struct drm_device *drm)
+{
+ return 0;
+}
+static inline int vlv_clock_get_hrawclk(struct drm_device *drm)
+{
+ return 0;
+}
+static inline int vlv_clock_get_czclk(struct drm_device *drm)
+{
+ return 0;
+}
+static inline int vlv_clock_get_cdclk(struct drm_device *drm)
+{
+ return 0;
+}
+static inline int vlv_clock_get_gpll(struct drm_device *drm)
+{
+ return 0;
+}
+#endif
+
+#endif /* __VLV_CLOCK_H__ */
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c
index c9a53fde79c4..19bdd8662359 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi.c
@@ -34,7 +34,6 @@
#include <drm/drm_probe_helper.h>
#include "i915_reg.h"
-#include "i915_utils.h"
#include "intel_atomic.h"
#include "intel_backlight.h"
#include "intel_connector.h"
@@ -42,6 +41,7 @@
#include "intel_de.h"
#include "intel_display_regs.h"
#include "intel_display_types.h"
+#include "intel_display_utils.h"
#include "intel_dsi.h"
#include "intel_dsi_vbt.h"
#include "intel_fifo_underrun.h"
@@ -94,8 +94,8 @@ void vlv_dsi_wait_for_fifo_empty(struct intel_dsi *intel_dsi, enum port port)
mask = LP_CTRL_FIFO_EMPTY | HS_CTRL_FIFO_EMPTY |
LP_DATA_FIFO_EMPTY | HS_DATA_FIFO_EMPTY;
- if (intel_de_wait_for_set(display, MIPI_GEN_FIFO_STAT(display, port),
- mask, 100))
+ if (intel_de_wait_for_set_ms(display, MIPI_GEN_FIFO_STAT(display, port),
+ mask, 100))
drm_err(display->drm, "DPI FIFOs are not empty\n");
}
@@ -162,8 +162,8 @@ static ssize_t intel_dsi_host_transfer(struct mipi_dsi_host *host,
/* note: this is never true for reads */
if (packet.payload_length) {
- if (intel_de_wait_for_clear(display, MIPI_GEN_FIFO_STAT(display, port),
- data_mask, 50))
+ if (intel_de_wait_for_clear_ms(display, MIPI_GEN_FIFO_STAT(display, port),
+ data_mask, 50))
drm_err(display->drm,
"Timeout waiting for HS/LP DATA FIFO !full\n");
@@ -176,8 +176,8 @@ static ssize_t intel_dsi_host_transfer(struct mipi_dsi_host *host,
GEN_READ_DATA_AVAIL);
}
- if (intel_de_wait_for_clear(display, MIPI_GEN_FIFO_STAT(display, port),
- ctrl_mask, 50)) {
+ if (intel_de_wait_for_clear_ms(display, MIPI_GEN_FIFO_STAT(display, port),
+ ctrl_mask, 50)) {
drm_err(display->drm,
"Timeout waiting for HS/LP CTRL FIFO !full\n");
}
@@ -188,8 +188,8 @@ static ssize_t intel_dsi_host_transfer(struct mipi_dsi_host *host,
/* ->rx_len is set only for reads */
if (msg->rx_len) {
data_mask = GEN_READ_DATA_AVAIL;
- if (intel_de_wait_for_set(display, MIPI_INTR_STAT(display, port),
- data_mask, 50))
+ if (intel_de_wait_for_set_ms(display, MIPI_INTR_STAT(display, port),
+ data_mask, 50))
drm_err(display->drm,
"Timeout waiting for read data.\n");
@@ -246,7 +246,7 @@ static int dpi_send_cmd(struct intel_dsi *intel_dsi, u32 cmd, bool hs,
intel_de_write(display, MIPI_DPI_CONTROL(display, port), cmd);
mask = SPL_PKT_SENT_INTERRUPT;
- if (intel_de_wait_for_set(display, MIPI_INTR_STAT(display, port), mask, 100))
+ if (intel_de_wait_for_set_ms(display, MIPI_INTR_STAT(display, port), mask, 100))
drm_err(display->drm,
"Video mode command 0x%08x send failed.\n", cmd);
@@ -352,8 +352,8 @@ static bool glk_dsi_enable_io(struct intel_encoder *encoder)
/* Wait for Pwr ACK */
for_each_dsi_port(port, intel_dsi->ports) {
- if (intel_de_wait_for_set(display, MIPI_CTRL(display, port),
- GLK_MIPIIO_PORT_POWERED, 20))
+ if (intel_de_wait_for_set_ms(display, MIPI_CTRL(display, port),
+ GLK_MIPIIO_PORT_POWERED, 20))
drm_err(display->drm, "MIPIO port is powergated\n");
}
@@ -374,8 +374,8 @@ static void glk_dsi_device_ready(struct intel_encoder *encoder)
/* Wait for MIPI PHY status bit to set */
for_each_dsi_port(port, intel_dsi->ports) {
- if (intel_de_wait_for_set(display, MIPI_CTRL(display, port),
- GLK_PHY_STATUS_PORT_READY, 20))
+ if (intel_de_wait_for_set_ms(display, MIPI_CTRL(display, port),
+ GLK_PHY_STATUS_PORT_READY, 20))
drm_err(display->drm, "PHY is not ON\n");
}
@@ -394,8 +394,8 @@ static void glk_dsi_device_ready(struct intel_encoder *encoder)
ULPS_STATE_MASK, ULPS_STATE_ENTER | DEVICE_READY);
/* Wait for ULPS active */
- if (intel_de_wait_for_clear(display, MIPI_CTRL(display, port),
- GLK_ULPS_NOT_ACTIVE, 20))
+ if (intel_de_wait_for_clear_ms(display, MIPI_CTRL(display, port),
+ GLK_ULPS_NOT_ACTIVE, 20))
drm_err(display->drm, "ULPS not active\n");
/* Exit ULPS */
@@ -413,16 +413,16 @@ static void glk_dsi_device_ready(struct intel_encoder *encoder)
/* Wait for Stop state */
for_each_dsi_port(port, intel_dsi->ports) {
- if (intel_de_wait_for_set(display, MIPI_CTRL(display, port),
- GLK_DATA_LANE_STOP_STATE, 20))
+ if (intel_de_wait_for_set_ms(display, MIPI_CTRL(display, port),
+ GLK_DATA_LANE_STOP_STATE, 20))
drm_err(display->drm,
"Date lane not in STOP state\n");
}
/* Wait for AFE LATCH */
for_each_dsi_port(port, intel_dsi->ports) {
- if (intel_de_wait_for_set(display, BXT_MIPI_PORT_CTRL(port),
- AFE_LATCHOUT, 20))
+ if (intel_de_wait_for_set_ms(display, BXT_MIPI_PORT_CTRL(port),
+ AFE_LATCHOUT, 20))
drm_err(display->drm,
"D-PHY not entering LP-11 state\n");
}
@@ -519,15 +519,15 @@ static void glk_dsi_enter_low_power_mode(struct intel_encoder *encoder)
/* Wait for MIPI PHY status bit to unset */
for_each_dsi_port(port, intel_dsi->ports) {
- if (intel_de_wait_for_clear(display, MIPI_CTRL(display, port),
- GLK_PHY_STATUS_PORT_READY, 20))
+ if (intel_de_wait_for_clear_ms(display, MIPI_CTRL(display, port),
+ GLK_PHY_STATUS_PORT_READY, 20))
drm_err(display->drm, "PHY is not turning OFF\n");
}
/* Wait for Pwr ACK bit to unset */
for_each_dsi_port(port, intel_dsi->ports) {
- if (intel_de_wait_for_clear(display, MIPI_CTRL(display, port),
- GLK_MIPIIO_PORT_POWERED, 20))
+ if (intel_de_wait_for_clear_ms(display, MIPI_CTRL(display, port),
+ GLK_MIPIIO_PORT_POWERED, 20))
drm_err(display->drm,
"MIPI IO Port is not powergated\n");
}
@@ -544,8 +544,8 @@ static void glk_dsi_disable_mipi_io(struct intel_encoder *encoder)
/* Wait for MIPI PHY status bit to unset */
for_each_dsi_port(port, intel_dsi->ports) {
- if (intel_de_wait_for_clear(display, MIPI_CTRL(display, port),
- GLK_PHY_STATUS_PORT_READY, 20))
+ if (intel_de_wait_for_clear_ms(display, MIPI_CTRL(display, port),
+ GLK_PHY_STATUS_PORT_READY, 20))
drm_err(display->drm, "PHY is not turning OFF\n");
}
@@ -595,8 +595,8 @@ static void vlv_dsi_clear_device_ready(struct intel_encoder *encoder)
* Port A only. MIPI Port C has no similar bit for checking.
*/
if ((display->platform.broxton || port == PORT_A) &&
- intel_de_wait_for_clear(display, port_ctrl,
- AFE_LATCHOUT, 30))
+ intel_de_wait_for_clear_ms(display, port_ctrl,
+ AFE_LATCHOUT, 30))
drm_err(display->drm, "DSI LP not going Low\n");
/* Disable MIPI PHY transparent latch */
diff --git a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c
index f078b9cda96c..a2da6285890b 100644
--- a/drivers/gpu/drm/i915/display/vlv_dsi_pll.c
+++ b/drivers/gpu/drm/i915/display/vlv_dsi_pll.c
@@ -319,8 +319,8 @@ void bxt_dsi_pll_disable(struct intel_encoder *encoder)
* PLL lock should deassert within 200us.
* Wait up to 1ms before timing out.
*/
- if (intel_de_wait_for_clear(display, BXT_DSI_PLL_ENABLE,
- BXT_DSI_PLL_LOCKED, 1))
+ if (intel_de_wait_for_clear_ms(display, BXT_DSI_PLL_ENABLE,
+ BXT_DSI_PLL_LOCKED, 1))
drm_err(display->drm,
"Timeout waiting for PLL lock deassertion\n");
}
@@ -568,8 +568,8 @@ void bxt_dsi_pll_enable(struct intel_encoder *encoder,
intel_de_rmw(display, BXT_DSI_PLL_ENABLE, 0, BXT_DSI_PLL_DO_ENABLE);
/* Timeout and fail if PLL not locked */
- if (intel_de_wait_for_set(display, BXT_DSI_PLL_ENABLE,
- BXT_DSI_PLL_LOCKED, 1)) {
+ if (intel_de_wait_for_set_ms(display, BXT_DSI_PLL_ENABLE,
+ BXT_DSI_PLL_LOCKED, 1)) {
drm_err(display->drm,
"Timed out waiting for DSI PLL to lock\n");
return;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index ed6599694835..3215ef49c975 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -68,6 +68,7 @@
#include <linux/nospec.h>
#include <drm/drm_cache.h>
+#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include "gt/gen6_ppgtt.h"
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_create.c b/drivers/gpu/drm/i915/gem/i915_gem_create.c
index c3e6a325872d..189ecdd0a9c1 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_create.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_create.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include "display/intel_display.h"
#include "gem/i915_gem_ioctls.h"
@@ -193,8 +194,8 @@ i915_gem_dumb_create(struct drm_file *file,
args->pitch = ALIGN(args->width * cpp, 64);
/* align stride to page size so that we can remap */
- if (args->pitch > intel_plane_fb_max_stride(dev, format,
- DRM_FORMAT_MOD_LINEAR))
+ if (args->pitch > intel_dumb_fb_max_stride(dev, format,
+ DRM_FORMAT_MOD_LINEAR))
args->pitch = ALIGN(args->pitch, 4096);
if (args->pitch < args->width)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 39c7c32e1e74..b057c2fa03a4 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -9,6 +9,7 @@
#include <linux/uaccess.h>
#include <drm/drm_auth.h>
+#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include "gem/i915_gem_ioctls.h"
@@ -142,7 +143,7 @@ enum {
* we want to leave the object where it is and for all the existing relocations
* to match. If the object is given a new address, or if userspace thinks the
* object is elsewhere, we have to parse all the relocation entries and update
- * the addresses. Userspace can set the I915_EXEC_NORELOC flag to hint that
+ * the addresses. Userspace can set the I915_EXEC_NO_RELOC flag to hint that
* all the target addresses in all of its objects match the value in the
* relocation entries and that they all match the presumed offsets given by the
* list of execbuffer objects. Using this knowledge, we know that if we haven't
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
index 75f5b0e871ef..4542135b20d5 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
@@ -16,12 +16,13 @@
#include "i915_gem_evict.h"
#include "i915_gem_gtt.h"
#include "i915_gem_ioctls.h"
-#include "i915_gem_object.h"
#include "i915_gem_mman.h"
+#include "i915_gem_object.h"
+#include "i915_gem_ttm.h"
+#include "i915_jiffies.h"
#include "i915_mm.h"
#include "i915_trace.h"
#include "i915_user_extensions.h"
-#include "i915_gem_ttm.h"
#include "i915_vma.h"
static inline bool
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index 478011e5ecb3..3f6f040c359d 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -27,6 +27,7 @@
#include <linux/sched/mm.h>
#include <drm/drm_cache.h>
+#include <drm/drm_print.h>
#include "display/intel_frontbuffer.h"
#include "pxp/intel_pxp.h"
@@ -476,24 +477,24 @@ static void i915_gem_free_object(struct drm_gem_object *gem_obj)
void __i915_gem_object_flush_frontbuffer(struct drm_i915_gem_object *obj,
enum fb_op_origin origin)
{
- struct intel_frontbuffer *front;
+ struct i915_frontbuffer *front;
- front = i915_gem_object_get_frontbuffer(obj);
+ front = i915_gem_object_frontbuffer_lookup(obj);
if (front) {
- intel_frontbuffer_flush(front, origin);
- intel_frontbuffer_put(front);
+ intel_frontbuffer_flush(&front->base, origin);
+ i915_gem_object_frontbuffer_put(front);
}
}
void __i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj,
enum fb_op_origin origin)
{
- struct intel_frontbuffer *front;
+ struct i915_frontbuffer *front;
- front = i915_gem_object_get_frontbuffer(obj);
+ front = i915_gem_object_frontbuffer_lookup(obj);
if (front) {
- intel_frontbuffer_invalidate(front, origin);
- intel_frontbuffer_put(front);
+ intel_frontbuffer_invalidate(&front->base, origin);
+ i915_gem_object_frontbuffer_put(front);
}
}
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 148034ef504d..8878539c10ed 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -802,6 +802,7 @@ static inline void __start_cpu_write(struct drm_i915_gem_object *obj)
void i915_gem_fence_wait_priority(struct dma_fence *fence,
const struct i915_sched_attr *attr);
+void i915_gem_fence_wait_priority_display(struct dma_fence *fence);
int i915_gem_object_wait(struct drm_i915_gem_object *obj,
unsigned int flags,
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.c
new file mode 100644
index 000000000000..aaa15e7b3f17
--- /dev/null
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: MIT
+/* Copyright © 2025 Intel Corporation */
+
+#include "i915_drv.h"
+#include "i915_gem_object_frontbuffer.h"
+
+static int frontbuffer_active(struct i915_active *ref)
+{
+ struct i915_frontbuffer *front =
+ container_of(ref, typeof(*front), write);
+
+ kref_get(&front->ref);
+ return 0;
+}
+
+static void frontbuffer_retire(struct i915_active *ref)
+{
+ struct i915_frontbuffer *front =
+ container_of(ref, typeof(*front), write);
+
+ intel_frontbuffer_flush(&front->base, ORIGIN_CS);
+ i915_gem_object_frontbuffer_put(front);
+}
+
+struct i915_frontbuffer *
+i915_gem_object_frontbuffer_get(struct drm_i915_gem_object *obj)
+{
+ struct drm_i915_private *i915 = to_i915(obj->base.dev);
+ struct i915_frontbuffer *front, *cur;
+
+ front = i915_gem_object_frontbuffer_lookup(obj);
+ if (front)
+ return front;
+
+ front = kmalloc(sizeof(*front), GFP_KERNEL);
+ if (!front)
+ return NULL;
+
+ intel_frontbuffer_init(&front->base, &i915->drm);
+
+ kref_init(&front->ref);
+ i915_gem_object_get(obj);
+ front->obj = obj;
+
+ i915_active_init(&front->write,
+ frontbuffer_active,
+ frontbuffer_retire,
+ I915_ACTIVE_RETIRE_SLEEPS);
+
+ spin_lock(&i915->frontbuffer_lock);
+ if (rcu_access_pointer(obj->frontbuffer)) {
+ cur = rcu_dereference_protected(obj->frontbuffer, true);
+ kref_get(&cur->ref);
+ } else {
+ cur = front;
+ rcu_assign_pointer(obj->frontbuffer, front);
+ }
+ spin_unlock(&i915->frontbuffer_lock);
+
+ if (cur != front) {
+ i915_gem_object_put(obj);
+ intel_frontbuffer_fini(&front->base);
+ kfree(front);
+ }
+
+ return cur;
+}
+
+void i915_gem_object_frontbuffer_ref(struct i915_frontbuffer *front)
+{
+ kref_get(&front->ref);
+}
+
+static void frontbuffer_release(struct kref *ref)
+ __releases(&i915->frontbuffer_lock)
+{
+ struct i915_frontbuffer *front =
+ container_of(ref, typeof(*front), ref);
+ struct drm_i915_gem_object *obj = front->obj;
+ struct drm_i915_private *i915 = to_i915(obj->base.dev);
+
+ i915_ggtt_clear_scanout(obj);
+
+ RCU_INIT_POINTER(obj->frontbuffer, NULL);
+
+ spin_unlock(&i915->frontbuffer_lock);
+
+ i915_active_fini(&front->write);
+
+ i915_gem_object_put(obj);
+
+ intel_frontbuffer_fini(&front->base);
+
+ kfree_rcu(front, rcu);
+}
+
+void i915_gem_object_frontbuffer_put(struct i915_frontbuffer *front)
+{
+ struct drm_i915_private *i915 = to_i915(front->obj->base.dev);
+
+ kref_put_lock(&front->ref, frontbuffer_release,
+ &i915->frontbuffer_lock);
+}
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h b/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h
index b682969e3a29..2133e29047c5 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_frontbuffer.h
@@ -12,6 +12,14 @@
#include "display/intel_frontbuffer.h"
#include "i915_gem_object_types.h"
+struct i915_frontbuffer {
+ struct intel_frontbuffer base;
+ struct drm_i915_gem_object *obj;
+ struct i915_active write;
+ struct rcu_head rcu;
+ struct kref ref;
+};
+
void __i915_gem_object_flush_frontbuffer(struct drm_i915_gem_object *obj,
enum fb_op_origin origin);
void __i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj,
@@ -33,19 +41,23 @@ i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj,
__i915_gem_object_invalidate_frontbuffer(obj, origin);
}
+struct i915_frontbuffer *i915_gem_object_frontbuffer_get(struct drm_i915_gem_object *obj);
+void i915_gem_object_frontbuffer_ref(struct i915_frontbuffer *front);
+void i915_gem_object_frontbuffer_put(struct i915_frontbuffer *front);
+
/**
- * i915_gem_object_get_frontbuffer - Get the object's frontbuffer
- * @obj: The object whose frontbuffer to get.
+ * i915_gem_object_frontbuffer_lookup - Look up the object's frontbuffer
+ * @obj: The object whose frontbuffer to look up.
*
* Get pointer to object's frontbuffer if such exists. Please note that RCU
* mechanism is used to handle e.g. ongoing removal of frontbuffer pointer.
*
* Return: pointer to object's frontbuffer is such exists or NULL
*/
-static inline struct intel_frontbuffer *
-i915_gem_object_get_frontbuffer(const struct drm_i915_gem_object *obj)
+static inline struct i915_frontbuffer *
+i915_gem_object_frontbuffer_lookup(const struct drm_i915_gem_object *obj)
{
- struct intel_frontbuffer *front;
+ struct i915_frontbuffer *front;
if (likely(!rcu_access_pointer(obj->frontbuffer)))
return NULL;
@@ -62,41 +74,11 @@ i915_gem_object_get_frontbuffer(const struct drm_i915_gem_object *obj)
if (likely(front == rcu_access_pointer(obj->frontbuffer)))
break;
- intel_frontbuffer_put(front);
+ i915_gem_object_frontbuffer_put(front);
} while (1);
rcu_read_unlock();
return front;
}
-/**
- * i915_gem_object_set_frontbuffer - Set the object's frontbuffer
- * @obj: The object whose frontbuffer to set.
- * @front: The frontbuffer to set
- *
- * Set object's frontbuffer pointer. If frontbuffer is already set for the
- * object keep it and return it's pointer to the caller. Please note that RCU
- * mechanism is used to handle e.g. ongoing removal of frontbuffer pointer. This
- * function is protected by i915->display->fb_tracking.lock
- *
- * Return: pointer to frontbuffer which was set.
- */
-static inline struct intel_frontbuffer *
-i915_gem_object_set_frontbuffer(struct drm_i915_gem_object *obj,
- struct intel_frontbuffer *front)
-{
- struct intel_frontbuffer *cur = front;
-
- if (!front) {
- RCU_INIT_POINTER(obj->frontbuffer, NULL);
- } else if (rcu_access_pointer(obj->frontbuffer)) {
- cur = rcu_dereference_protected(obj->frontbuffer, true);
- kref_get(&cur->ref);
- } else {
- rcu_assign_pointer(obj->frontbuffer, front);
- }
-
- return cur;
-}
-
#endif
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
index 64600aa8227f..465ce94aee76 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h
@@ -574,7 +574,7 @@ struct drm_i915_gem_object {
*/
u16 write_domain;
- struct intel_frontbuffer __rcu *frontbuffer;
+ struct i915_frontbuffer __rcu *frontbuffer;
/** Current tiling stride for the object, if it's tiled. */
unsigned int tiling_and_stride;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
index 3f09cbce05bb..c2f8e5f95696 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
@@ -3,9 +3,11 @@
* Copyright © 2014-2016 Intel Corporation
*/
+#include <linux/vmalloc.h>
+
#include <drm/drm_cache.h>
#include <drm/drm_panic.h>
-#include <linux/vmalloc.h>
+#include <drm/drm_print.h>
#include "display/intel_fb.h"
#include "display/intel_display_types.h"
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
index f9e7cab140f8..bc799f182850 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_phys.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
@@ -8,6 +8,7 @@
#include <linux/swap.h>
#include <drm/drm_cache.h>
+#include <drm/drm_print.h>
#include "gt/intel_gt.h"
#include "i915_drv.h"
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
index b9dae15c1d16..26dda55a07ff 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
@@ -441,11 +441,20 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
written = file->f_op->write_iter(&kiocb, &iter);
BUG_ON(written == -EIOCBQUEUED);
- if (written != size)
- return -EIO;
-
+ /*
+ * First, check if write_iter returned a negative error.
+ * If the write failed, return the real error code immediately.
+ * This prevents it from being overwritten by the short write check below.
+ */
if (written < 0)
return written;
+ /*
+ * Check for a short write (written bytes != requested size).
+ * Even if some data was written, return -EIO to indicate that the
+ * write was not fully completed.
+ */
+ if (written != size)
+ return -EIO;
return 0;
}
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
index 7a3e74a6676e..e0d1f369a163 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shrinker.c
@@ -12,6 +12,8 @@
#include <linux/dma-buf.h>
#include <linux/vmalloc.h>
+#include <drm/drm_print.h>
+
#include "gt/intel_gt_requests.h"
#include "gt/intel_gt.h"
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
index 3380151edfc1..f859c99f969b 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
@@ -7,6 +7,7 @@
#include <linux/mutex.h>
#include <drm/drm_mm.h>
+#include <drm/drm_print.h>
#include <drm/intel/i915_drm.h>
#include "gem/i915_gem_lmem.h"
@@ -24,6 +25,11 @@
#include "intel_mchbar_regs.h"
#include "intel_pci_config.h"
+struct intel_stolen_node {
+ struct drm_i915_private *i915;
+ struct drm_mm_node node;
+};
+
/*
* The BIOS typically reserves some of the system's memory for the exclusive
* use of the integrated graphics. This memory is no longer available for
@@ -36,9 +42,9 @@
* for is a boon.
*/
-int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *i915,
- struct drm_mm_node *node, u64 size,
- unsigned alignment, u64 start, u64 end)
+static int __i915_gem_stolen_insert_node_in_range(struct drm_i915_private *i915,
+ struct drm_mm_node *node, u64 size,
+ unsigned int alignment, u64 start, u64 end)
{
int ret;
@@ -58,24 +64,43 @@ int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *i915,
return ret;
}
-int i915_gem_stolen_insert_node(struct drm_i915_private *i915,
- struct drm_mm_node *node, u64 size,
- unsigned alignment)
+int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
+ unsigned int alignment, u64 start, u64 end)
+{
+ return __i915_gem_stolen_insert_node_in_range(node->i915, &node->node,
+ size, alignment,
+ start, end);
+}
+
+static int __i915_gem_stolen_insert_node(struct drm_i915_private *i915,
+ struct drm_mm_node *node, u64 size,
+ unsigned int alignment)
{
- return i915_gem_stolen_insert_node_in_range(i915, node,
- size, alignment,
- I915_GEM_STOLEN_BIAS,
- U64_MAX);
+ return __i915_gem_stolen_insert_node_in_range(i915, node,
+ size, alignment,
+ I915_GEM_STOLEN_BIAS,
+ U64_MAX);
}
-void i915_gem_stolen_remove_node(struct drm_i915_private *i915,
- struct drm_mm_node *node)
+int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size,
+ unsigned int alignment)
+{
+ return __i915_gem_stolen_insert_node(node->i915, &node->node, size, alignment);
+}
+
+static void __i915_gem_stolen_remove_node(struct drm_i915_private *i915,
+ struct drm_mm_node *node)
{
mutex_lock(&i915->mm.stolen_lock);
drm_mm_remove_node(node);
mutex_unlock(&i915->mm.stolen_lock);
}
+void i915_gem_stolen_remove_node(struct intel_stolen_node *node)
+{
+ __i915_gem_stolen_remove_node(node->i915, &node->node);
+}
+
static bool valid_stolen_size(struct drm_i915_private *i915, struct resource *dsm)
{
return (dsm->start != 0 || HAS_LMEMBAR_SMEM_STOLEN(i915)) && dsm->end > dsm->start;
@@ -683,7 +708,7 @@ i915_gem_object_release_stolen(struct drm_i915_gem_object *obj)
struct drm_mm_node *stolen = fetch_and_zero(&obj->stolen);
GEM_BUG_ON(!stolen);
- i915_gem_stolen_remove_node(i915, stolen);
+ __i915_gem_stolen_remove_node(i915, stolen);
kfree(stolen);
i915_gem_object_release_memory_region(obj);
@@ -772,8 +797,8 @@ static int _i915_gem_object_stolen_init(struct intel_memory_region *mem,
ret = drm_mm_reserve_node(&i915->mm.stolen, stolen);
mutex_unlock(&i915->mm.stolen_lock);
} else {
- ret = i915_gem_stolen_insert_node(i915, stolen, size,
- mem->min_page_size);
+ ret = __i915_gem_stolen_insert_node(i915, stolen, size,
+ mem->min_page_size);
}
if (ret)
goto err_free;
@@ -785,7 +810,7 @@ static int _i915_gem_object_stolen_init(struct intel_memory_region *mem,
return 0;
err_remove:
- i915_gem_stolen_remove_node(i915, stolen);
+ __i915_gem_stolen_remove_node(i915, stolen);
err_free:
kfree(stolen);
return ret;
@@ -1000,38 +1025,64 @@ bool i915_gem_object_is_stolen(const struct drm_i915_gem_object *obj)
return obj->ops == &i915_gem_object_stolen_ops;
}
-bool i915_gem_stolen_initialized(const struct drm_i915_private *i915)
+bool i915_gem_stolen_initialized(struct drm_device *drm)
{
+ struct drm_i915_private *i915 = to_i915(drm);
+
return drm_mm_initialized(&i915->mm.stolen);
}
-u64 i915_gem_stolen_area_address(const struct drm_i915_private *i915)
+u64 i915_gem_stolen_area_address(struct drm_device *drm)
{
+ struct drm_i915_private *i915 = to_i915(drm);
+
return i915->dsm.stolen.start;
}
-u64 i915_gem_stolen_area_size(const struct drm_i915_private *i915)
+u64 i915_gem_stolen_area_size(struct drm_device *drm)
{
+ struct drm_i915_private *i915 = to_i915(drm);
+
return resource_size(&i915->dsm.stolen);
}
-u64 i915_gem_stolen_node_address(const struct drm_i915_private *i915,
- const struct drm_mm_node *node)
+u64 i915_gem_stolen_node_address(const struct intel_stolen_node *node)
{
+ struct drm_i915_private *i915 = node->i915;
+
return i915->dsm.stolen.start + i915_gem_stolen_node_offset(node);
}
-bool i915_gem_stolen_node_allocated(const struct drm_mm_node *node)
+bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node)
+{
+ return drm_mm_node_allocated(&node->node);
+}
+
+u64 i915_gem_stolen_node_offset(const struct intel_stolen_node *node)
{
- return drm_mm_node_allocated(node);
+ return node->node.start;
}
-u64 i915_gem_stolen_node_offset(const struct drm_mm_node *node)
+u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node)
{
- return node->start;
+ return node->node.size;
+}
+
+struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm)
+{
+ struct drm_i915_private *i915 = to_i915(drm);
+ struct intel_stolen_node *node;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return NULL;
+
+ node->i915 = i915;
+
+ return node;
}
-u64 i915_gem_stolen_node_size(const struct drm_mm_node *node)
+void i915_gem_stolen_node_free(const struct intel_stolen_node *node)
{
- return node->size;
+ kfree(node);
}
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.h b/drivers/gpu/drm/i915/gem/i915_gem_stolen.h
index dfe0db8bb1b9..7b0386002ed4 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.h
@@ -8,21 +8,17 @@
#include <linux/types.h>
-struct drm_i915_private;
-struct drm_mm_node;
+struct drm_device;
struct drm_i915_gem_object;
+struct drm_i915_private;
+struct intel_stolen_node;
-#define i915_stolen_fb drm_mm_node
-
-int i915_gem_stolen_insert_node(struct drm_i915_private *i915,
- struct drm_mm_node *node, u64 size,
+int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size,
unsigned alignment);
-int i915_gem_stolen_insert_node_in_range(struct drm_i915_private *i915,
- struct drm_mm_node *node, u64 size,
+int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
unsigned alignment, u64 start,
u64 end);
-void i915_gem_stolen_remove_node(struct drm_i915_private *i915,
- struct drm_mm_node *node);
+void i915_gem_stolen_remove_node(struct intel_stolen_node *node);
struct intel_memory_region *
i915_gem_stolen_smem_setup(struct drm_i915_private *i915, u16 type,
u16 instance);
@@ -38,15 +34,17 @@ bool i915_gem_object_is_stolen(const struct drm_i915_gem_object *obj);
#define I915_GEM_STOLEN_BIAS SZ_128K
-bool i915_gem_stolen_initialized(const struct drm_i915_private *i915);
-u64 i915_gem_stolen_area_address(const struct drm_i915_private *i915);
-u64 i915_gem_stolen_area_size(const struct drm_i915_private *i915);
+bool i915_gem_stolen_initialized(struct drm_device *drm);
+u64 i915_gem_stolen_area_address(struct drm_device *drm);
+u64 i915_gem_stolen_area_size(struct drm_device *drm);
+
+u64 i915_gem_stolen_node_address(const struct intel_stolen_node *node);
-u64 i915_gem_stolen_node_address(const struct drm_i915_private *i915,
- const struct drm_mm_node *node);
+bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node);
+u64 i915_gem_stolen_node_offset(const struct intel_stolen_node *node);
+u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node);
-bool i915_gem_stolen_node_allocated(const struct drm_mm_node *node);
-u64 i915_gem_stolen_node_offset(const struct drm_mm_node *node);
-u64 i915_gem_stolen_node_size(const struct drm_mm_node *node);
+struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm);
+void i915_gem_stolen_node_free(const struct intel_stolen_node *node);
#endif /* __I915_GEM_STOLEN_H__ */
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
index 5a296ba3758a..567b97d28d30 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
@@ -145,8 +145,9 @@ i915_tiling_ok(struct drm_i915_gem_object *obj,
return false;
}
- if (GRAPHICS_VER(i915) == 2 ||
- (tiling == I915_TILING_Y && HAS_128_BYTE_Y_TILING(i915)))
+ if (tiling == I915_TILING_Y && HAS_128_BYTE_Y_TILING(i915))
+ tile_width = 128;
+ else if (GRAPHICS_VER(i915) == 2)
tile_width = 128;
else
tile_width = 512;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index 1f4814968868..f65fe86c02b5 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -5,11 +5,13 @@
#include <linux/shmem_fs.h>
+#include <drm/drm_buddy.h>
+#include <drm/drm_print.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_tt.h>
-#include <drm/drm_buddy.h>
#include "i915_drv.h"
+#include "i915_jiffies.h"
#include "i915_ttm_buddy_manager.h"
#include "intel_memory_region.h"
#include "intel_region_ttm.h"
@@ -1029,7 +1031,7 @@ static void i915_ttm_delayed_free(struct drm_i915_gem_object *obj)
{
GEM_BUG_ON(!obj->ttm.created);
- ttm_bo_put(i915_gem_to_ttm(obj));
+ ttm_bo_fini(i915_gem_to_ttm(obj));
}
static vm_fault_t vm_fault_ttm(struct vm_fault *vmf)
@@ -1325,7 +1327,7 @@ int __i915_gem_ttm_object_init(struct intel_memory_region *mem,
* If this function fails, it will call the destructor, but
* our caller still owns the object. So no freeing in the
* destructor until obj->ttm.created is true.
- * Similarly, in delayed_destroy, we can't call ttm_bo_put()
+ * Similarly, in delayed_destroy, we can't call ttm_bo_fini()
* until successful initialization.
*/
ret = ttm_bo_init_reserved(&i915->bdev, i915_gem_to_ttm(obj), bo_type,
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c
index 61596cecce4d..4824f948daed 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c
@@ -3,6 +3,7 @@
* Copyright © 2021 Intel Corporation
*/
+#include <drm/drm_print.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_tt.h>
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
index 307a18eede72..77cc3af3d518 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
@@ -38,6 +38,8 @@
#include <linux/swap.h>
#include <linux/sched/mm.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_gem_ioctls.h"
#include "i915_gem_object.h"
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_wait.c b/drivers/gpu/drm/i915/gem/i915_gem_wait.c
index 54829801d3f7..2893df65c359 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_wait.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_wait.c
@@ -138,6 +138,13 @@ void i915_gem_fence_wait_priority(struct dma_fence *fence,
local_bh_enable(); /* kick the tasklets if queues were reprioritised */
}
+void i915_gem_fence_wait_priority_display(struct dma_fence *fence)
+{
+ struct i915_sched_attr attr = { .priority = I915_PRIORITY_DISPLAY };
+
+ i915_gem_fence_wait_priority(fence, &attr);
+}
+
int
i915_gem_object_wait_priority(struct drm_i915_gem_object *obj,
unsigned int flags,
diff --git a/drivers/gpu/drm/i915/gem/i915_gemfs.c b/drivers/gpu/drm/i915/gem/i915_gemfs.c
index 8f13ec4ff0d0..1f1290214031 100644
--- a/drivers/gpu/drm/i915/gem/i915_gemfs.c
+++ b/drivers/gpu/drm/i915/gem/i915_gemfs.c
@@ -7,6 +7,8 @@
#include <linux/mount.h>
#include <linux/fs_context.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_gemfs.h"
#include "i915_utils.h"
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c
index 539c620364e3..3557e9e6f422 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_client_blt.c
@@ -3,6 +3,8 @@
* Copyright © 2019 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_selftest.h"
#include "display/intel_display_device.h"
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
index eb0158e43417..1330c0b431a7 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -962,13 +962,14 @@ emit_rpcs_query(struct drm_i915_gem_object *obj,
if (IS_ERR(rpcs))
return PTR_ERR(rpcs);
+ i915_gem_ww_ctx_init(&ww, false);
+
batch = i915_vma_instance(rpcs, ce->vm, NULL);
if (IS_ERR(batch)) {
err = PTR_ERR(batch);
goto err_put;
}
- i915_gem_ww_ctx_init(&ww, false);
retry:
err = i915_gem_object_lock(obj, &ww);
if (!err)
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
index 78734c404a6d..0d250d57496a 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -7,6 +7,8 @@
#include <linux/highmem.h>
#include <linux/prime_numbers.h>
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_lmem.h"
#include "gem/i915_gem_region.h"
diff --git a/drivers/gpu/drm/i915/gt/gen2_engine_cs.c b/drivers/gpu/drm/i915/gt/gen2_engine_cs.c
index 8116fd5987e2..8c01fb6d4e7b 100644
--- a/drivers/gpu/drm/i915/gt/gen2_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/gen2_engine_cs.c
@@ -292,15 +292,15 @@ int gen4_emit_bb_start(struct i915_request *rq,
void gen2_irq_enable(struct intel_engine_cs *engine)
{
- engine->i915->irq_mask &= ~engine->irq_enable_mask;
- intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->irq_mask);
+ engine->i915->gen2_imr_mask &= ~engine->irq_enable_mask;
+ intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->gen2_imr_mask);
intel_uncore_posting_read_fw(engine->uncore, GEN2_IMR);
}
void gen2_irq_disable(struct intel_engine_cs *engine)
{
- engine->i915->irq_mask |= engine->irq_enable_mask;
- intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->irq_mask);
+ engine->i915->gen2_imr_mask |= engine->irq_enable_mask;
+ intel_uncore_write(engine->uncore, GEN2_IMR, engine->i915->gen2_imr_mask);
}
void gen5_irq_enable(struct intel_engine_cs *engine)
diff --git a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c
index e9f65f27b53f..071c1cc45257 100644
--- a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c
@@ -3,6 +3,8 @@
* Copyright © 2014 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "gen8_engine_cs.h"
#include "intel_engine_regs.h"
#include "intel_gpu_commands.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
index cc866773ba6f..bf6117d5fc57 100644
--- a/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
+++ b/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
@@ -8,6 +8,8 @@
#include <trace/events/dma_fence.h>
#include <uapi/linux/sched/types.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_trace.h"
#include "intel_breadcrumbs.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
index 8d4bb95f8424..b279878dca29 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_heartbeat.c
@@ -3,7 +3,10 @@
* Copyright © 2019 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
+#include "i915_jiffies.h"
#include "i915_request.h"
#include "intel_context.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_user.c b/drivers/gpu/drm/i915/gt/intel_engine_user.c
index 833987015b8b..be4bbff1a57c 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_user.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_user.c
@@ -7,6 +7,8 @@
#include <linux/list_sort.h>
#include <linux/llist.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "intel_engine.h"
#include "intel_engine_user.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
index 7f389cb0bde4..3df683b0402a 100644
--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
@@ -110,6 +110,8 @@
#include <linux/interrupt.h>
#include <linux/string_helpers.h>
+#include <drm/drm_print.h>
+
#include "gen8_engine_cs.h"
#include "i915_drv.h"
#include "i915_list_util.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c
index 46a5aa4ab9c8..08c4e735481b 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c
@@ -9,6 +9,7 @@
#include <linux/stop_machine.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/intel/i915_drm.h>
#include <drm/intel/intel-gtt.h>
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
index 889e61843ff3..5eda98ebc1ae 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt_fencing.c
@@ -5,6 +5,8 @@
#include <linux/highmem.h>
+#include <drm/drm_print.h>
+
#include "display/intel_display.h"
#include "i915_drv.h"
#include "i915_reg.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt_gmch.c b/drivers/gpu/drm/i915/gt/intel_ggtt_gmch.c
index c5f5f0bdfb2c..cc5d345c5e29 100644
--- a/drivers/gpu/drm/i915/gt/intel_ggtt_gmch.c
+++ b/drivers/gpu/drm/i915/gt/intel_ggtt_gmch.c
@@ -5,6 +5,7 @@
#include "intel_ggtt_gmch.h"
+#include <drm/drm_print.h>
#include <drm/intel/intel-gtt.h>
#include <linux/agp_backend.h>
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_clock_utils.c b/drivers/gpu/drm/i915/gt/intel_gt_clock_utils.c
index 88b147fa5cb1..c90b35881a26 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_clock_utils.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt_clock_utils.c
@@ -205,7 +205,7 @@ static u64 div_u64_roundup(u64 nom, u32 den)
u64 intel_gt_clock_interval_to_ns(const struct intel_gt *gt, u64 count)
{
- return div_u64_roundup(count * NSEC_PER_SEC, gt->clock_frequency);
+ return mul_u64_u32_div(count, NSEC_PER_SEC, gt->clock_frequency);
}
u64 intel_gt_pm_interval_to_ns(const struct intel_gt *gt, u64 count)
@@ -215,7 +215,7 @@ u64 intel_gt_pm_interval_to_ns(const struct intel_gt *gt, u64 count)
u64 intel_gt_ns_to_clock_interval(const struct intel_gt *gt, u64 ns)
{
- return div_u64_roundup(gt->clock_frequency * ns, NSEC_PER_SEC);
+ return mul_u64_u32_div(ns, gt->clock_frequency, NSEC_PER_SEC);
}
u64 intel_gt_ns_to_pm_interval(const struct intel_gt *gt, u64 ns)
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_debugfs.c b/drivers/gpu/drm/i915/gt/intel_gt_debugfs.c
index dcd40b30a96b..bd9abbd6d3d4 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_debugfs.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt_debugfs.c
@@ -5,6 +5,8 @@
#include <linux/debugfs.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "intel_gt.h"
#include "intel_gt_debugfs.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c b/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c
index 87ef85483bae..96411f357f5d 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c
@@ -7,6 +7,8 @@
#include <linux/seq_file.h>
#include <linux/string_helpers.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_reg.h"
#include "intel_gt.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index c481b56fa67d..e8927ad49142 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -3,6 +3,8 @@
* Copyright © 2014 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_lmem.h"
#include "gen8_engine_cs.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_mocs.c b/drivers/gpu/drm/i915/gt/intel_mocs.c
index 5dd8121f4b15..e8d93a657ef6 100644
--- a/drivers/gpu/drm/i915/gt/intel_mocs.c
+++ b/drivers/gpu/drm/i915/gt/intel_mocs.c
@@ -3,6 +3,8 @@
* Copyright © 2015 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "intel_engine.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_rc6.c b/drivers/gpu/drm/i915/gt/intel_rc6.c
index bf38cc5fe872..286d49ecc449 100644
--- a/drivers/gpu/drm/i915/gt/intel_rc6.c
+++ b/drivers/gpu/drm/i915/gt/intel_rc6.c
@@ -6,6 +6,9 @@
#include <linux/pm_runtime.h>
#include <linux/string_helpers.h>
+#include <drm/drm_print.h>
+
+#include "display/vlv_clock.h"
#include "gem/i915_gem_region.h"
#include "i915_drv.h"
#include "i915_reg.h"
@@ -802,7 +805,7 @@ u64 intel_rc6_residency_ns(struct intel_rc6 *rc6, enum intel_rc6_res_type id)
/* On VLV and CHV, residency time is in CZ units rather than 1.28us */
if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) {
mul = 1000000;
- div = i915->czclk_freq;
+ div = vlv_clock_get_czclk(&i915->drm);
overflow_hw = BIT_ULL(40);
time_hw = vlv_residency_raw(uncore, reg);
} else {
diff --git a/drivers/gpu/drm/i915/gt/intel_region_lmem.c b/drivers/gpu/drm/i915/gt/intel_region_lmem.c
index 51bb27e10a4f..a30060fd4429 100644
--- a/drivers/gpu/drm/i915/gt/intel_region_lmem.c
+++ b/drivers/gpu/drm/i915/gt/intel_region_lmem.c
@@ -3,6 +3,8 @@
* Copyright © 2019 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_pci.h"
#include "i915_reg.h"
@@ -18,16 +20,6 @@
#include "gt/intel_gt_regs.h"
#ifdef CONFIG_64BIT
-static void _release_bars(struct pci_dev *pdev)
-{
- int resno;
-
- for (resno = PCI_STD_RESOURCES; resno < PCI_STD_RESOURCE_END; resno++) {
- if (pci_resource_len(pdev, resno))
- pci_release_resource(pdev, resno);
- }
-}
-
static void
_resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size)
{
@@ -35,9 +27,7 @@ _resize_bar(struct drm_i915_private *i915, int resno, resource_size_t size)
int bar_size = pci_rebar_bytes_to_size(size);
int ret;
- _release_bars(pdev);
-
- ret = pci_resize_resource(pdev, resno, bar_size);
+ ret = pci_resize_resource(pdev, resno, bar_size, 0);
if (ret) {
drm_info(&i915->drm, "Failed to resize BAR%d to %dM (%pe)\n",
resno, 1 << bar_size, ERR_PTR(ret));
@@ -61,16 +51,12 @@ static void i915_resize_lmem_bar(struct drm_i915_private *i915, resource_size_t
current_size = roundup_pow_of_two(pci_resource_len(pdev, GEN12_LMEM_BAR));
if (i915->params.lmem_bar_size) {
- u32 bar_sizes;
-
- rebar_size = i915->params.lmem_bar_size *
- (resource_size_t)SZ_1M;
- bar_sizes = pci_rebar_get_possible_sizes(pdev, GEN12_LMEM_BAR);
-
+ rebar_size = i915->params.lmem_bar_size * (resource_size_t)SZ_1M;
if (rebar_size == current_size)
return;
- if (!(bar_sizes & BIT(pci_rebar_bytes_to_size(rebar_size))) ||
+ if (!pci_rebar_size_supported(pdev, GEN12_LMEM_BAR,
+ pci_rebar_bytes_to_size(rebar_size)) ||
rebar_size >= roundup_pow_of_two(lmem_size)) {
rebar_size = lmem_size;
diff --git a/drivers/gpu/drm/i915/gt/intel_renderstate.c b/drivers/gpu/drm/i915/gt/intel_renderstate.c
index 4b56ec3743cf..d53766c288f7 100644
--- a/drivers/gpu/drm/i915/gt/intel_renderstate.c
+++ b/drivers/gpu/drm/i915/gt/intel_renderstate.c
@@ -3,6 +3,8 @@
* Copyright © 2014 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_internal.h"
#include "i915_drv.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_rps.c b/drivers/gpu/drm/i915/gt/intel_rps.c
index 4da94098bd3e..b01c837ab646 100644
--- a/drivers/gpu/drm/i915/gt/intel_rps.c
+++ b/drivers/gpu/drm/i915/gt/intel_rps.c
@@ -7,8 +7,8 @@
#include <drm/intel/i915_drm.h>
-#include "display/intel_display.h"
#include "display/intel_display_rps.h"
+#include "display/vlv_clock.h"
#include "soc/intel_dram.h"
#include "i915_drv.h"
@@ -1690,10 +1690,7 @@ static void vlv_init_gpll_ref_freq(struct intel_rps *rps)
{
struct drm_i915_private *i915 = rps_to_i915(rps);
- rps->gpll_ref_freq =
- vlv_get_cck_clock(&i915->drm, "GPLL ref",
- CCK_GPLL_CLOCK_CONTROL,
- i915->czclk_freq);
+ rps->gpll_ref_freq = vlv_clock_get_gpll(&i915->drm);
drm_dbg(&i915->drm, "GPLL reference freq: %d kHz\n",
rps->gpll_ref_freq);
@@ -1703,13 +1700,13 @@ static void vlv_rps_init(struct intel_rps *rps)
{
struct drm_i915_private *i915 = rps_to_i915(rps);
+ vlv_init_gpll_ref_freq(rps);
+
vlv_iosf_sb_get(&i915->drm,
BIT(VLV_IOSF_SB_PUNIT) |
BIT(VLV_IOSF_SB_NC) |
BIT(VLV_IOSF_SB_CCK));
- vlv_init_gpll_ref_freq(rps);
-
rps->max_freq = vlv_rps_max_freq(rps);
rps->rp0_freq = rps->max_freq;
drm_dbg(&i915->drm, "max GPU freq: %d MHz (%u)\n",
@@ -1737,13 +1734,13 @@ static void chv_rps_init(struct intel_rps *rps)
{
struct drm_i915_private *i915 = rps_to_i915(rps);
+ vlv_init_gpll_ref_freq(rps);
+
vlv_iosf_sb_get(&i915->drm,
BIT(VLV_IOSF_SB_PUNIT) |
BIT(VLV_IOSF_SB_NC) |
BIT(VLV_IOSF_SB_CCK));
- vlv_init_gpll_ref_freq(rps);
-
rps->max_freq = chv_rps_max_freq(rps);
rps->rp0_freq = rps->max_freq;
drm_dbg(&i915->drm, "max GPU freq: %d MHz (%u)\n",
@@ -1780,6 +1777,7 @@ static void vlv_c0_read(struct intel_uncore *uncore, struct intel_rps_ei *ei)
static u32 vlv_wa_c0_ei(struct intel_rps *rps, u32 pm_iir)
{
+ struct drm_i915_private *i915 = rps_to_i915(rps);
struct intel_uncore *uncore = rps_to_uncore(rps);
const struct intel_rps_ei *prev = &rps->ei;
struct intel_rps_ei now;
@@ -1796,7 +1794,7 @@ static u32 vlv_wa_c0_ei(struct intel_rps *rps, u32 pm_iir)
time = ktime_us_delta(now.ktime, prev->ktime);
- time *= rps_to_i915(rps)->czclk_freq;
+ time *= vlv_clock_get_czclk(&i915->drm);
/* Workload can be split between render + media,
* e.g. SwapBuffers being blitted in X after being rendered in
diff --git a/drivers/gpu/drm/i915/gt/intel_sa_media.c b/drivers/gpu/drm/i915/gt/intel_sa_media.c
index 2945526d52d1..fb260d1ec360 100644
--- a/drivers/gpu/drm/i915/gt/intel_sa_media.c
+++ b/drivers/gpu/drm/i915/gt/intel_sa_media.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "i915_drv.h"
#include "gt/intel_gt.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_sseu.c b/drivers/gpu/drm/i915/gt/intel_sseu.c
index 9501d323d0d3..656a499b2706 100644
--- a/drivers/gpu/drm/i915/gt/intel_sseu.c
+++ b/drivers/gpu/drm/i915/gt/intel_sseu.c
@@ -5,6 +5,8 @@
#include <linux/string_helpers.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_perf_types.h"
#include "intel_engine_regs.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c b/drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c
index c2ee5e1826b5..1dc8205bc64d 100644
--- a/drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c
+++ b/drivers/gpu/drm/i915/gt/intel_sseu_debugfs.c
@@ -7,6 +7,8 @@
#include <linux/bitmap.h>
#include <linux/string_helpers.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "intel_gt_debugfs.h"
#include "intel_gt_regs.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_timeline.c b/drivers/gpu/drm/i915/gt/intel_timeline.c
index b9640212d659..843f72829a24 100644
--- a/drivers/gpu/drm/i915/gt/intel_timeline.c
+++ b/drivers/gpu/drm/i915/gt/intel_timeline.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_cache.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_internal.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_tlb.h b/drivers/gpu/drm/i915/gt/intel_tlb.h
index 337327af92ac..ec7612216248 100644
--- a/drivers/gpu/drm/i915/gt/intel_tlb.h
+++ b/drivers/gpu/drm/i915/gt/intel_tlb.h
@@ -18,7 +18,7 @@ void intel_gt_fini_tlb(struct intel_gt *gt);
static inline u32 intel_gt_tlb_seqno(const struct intel_gt *gt)
{
- return seqprop_sequence(&gt->tlb.seqno);
+ return raw_read_seqcount(&gt->tlb.seqno);
}
static inline u32 intel_gt_next_invalidate_tlb_full(const struct intel_gt *gt)
diff --git a/drivers/gpu/drm/i915/gt/intel_wopcm.c b/drivers/gpu/drm/i915/gt/intel_wopcm.c
index 7ebbcc191c2d..1b26ff6488b3 100644
--- a/drivers/gpu/drm/i915/gt/intel_wopcm.c
+++ b/drivers/gpu/drm/i915/gt/intel_wopcm.c
@@ -3,6 +3,8 @@
* Copyright © 2017-2019 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "intel_wopcm.h"
#include "i915_drv.h"
diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c
index 7d486dfa2fc1..ece88c612e27 100644
--- a/drivers/gpu/drm/i915/gt/intel_workarounds.c
+++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c
@@ -5,6 +5,7 @@
#include "i915_drv.h"
#include "i915_reg.h"
+#include "i915_mmio_range.h"
#include "intel_context.h"
#include "intel_engine_pm.h"
#include "intel_engine_regs.h"
@@ -2923,7 +2924,7 @@ void intel_engine_apply_workarounds(struct intel_engine_cs *engine)
wa_list_apply(&engine->wa_list);
}
-static const struct i915_range mcr_ranges_gen8[] = {
+static const struct i915_mmio_range mcr_ranges_gen8[] = {
{ .start = 0x5500, .end = 0x55ff },
{ .start = 0x7000, .end = 0x7fff },
{ .start = 0x9400, .end = 0x97ff },
@@ -2932,7 +2933,7 @@ static const struct i915_range mcr_ranges_gen8[] = {
{},
};
-static const struct i915_range mcr_ranges_gen12[] = {
+static const struct i915_mmio_range mcr_ranges_gen12[] = {
{ .start = 0x8150, .end = 0x815f },
{ .start = 0x9520, .end = 0x955f },
{ .start = 0xb100, .end = 0xb3ff },
@@ -2941,7 +2942,7 @@ static const struct i915_range mcr_ranges_gen12[] = {
{},
};
-static const struct i915_range mcr_ranges_xehp[] = {
+static const struct i915_mmio_range mcr_ranges_xehp[] = {
{ .start = 0x4000, .end = 0x4aff },
{ .start = 0x5200, .end = 0x52ff },
{ .start = 0x5400, .end = 0x7fff },
@@ -2960,7 +2961,7 @@ static const struct i915_range mcr_ranges_xehp[] = {
static bool mcr_range(struct drm_i915_private *i915, u32 offset)
{
- const struct i915_range *mcr_ranges;
+ const struct i915_mmio_range *mcr_ranges;
int i;
if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 55))
diff --git a/drivers/gpu/drm/i915/gt/selftest_context.c b/drivers/gpu/drm/i915/gt/selftest_context.c
index 5eb46700dc4e..ab76703f6e8b 100644
--- a/drivers/gpu/drm/i915/gt/selftest_context.c
+++ b/drivers/gpu/drm/i915/gt/selftest_context.c
@@ -3,6 +3,8 @@
* Copyright © 2019 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_selftest.h"
#include "intel_engine_heartbeat.h"
#include "intel_engine_pm.h"
diff --git a/drivers/gpu/drm/i915/gt/selftest_execlists.c b/drivers/gpu/drm/i915/gt/selftest_execlists.c
index 0454eb1814bb..a06b397b6d42 100644
--- a/drivers/gpu/drm/i915/gt/selftest_execlists.c
+++ b/drivers/gpu/drm/i915/gt/selftest_execlists.c
@@ -5,12 +5,15 @@
#include <linux/prime_numbers.h>
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_pm.h"
#include "gt/intel_engine_heartbeat.h"
#include "gt/intel_reset.h"
#include "gt/selftest_engine_heartbeat.h"
+#include "i915_jiffies.h"
#include "i915_selftest.h"
#include "selftests/i915_random.h"
#include "selftests/igt_flush_test.h"
diff --git a/drivers/gpu/drm/i915/gt/selftest_migrate.c b/drivers/gpu/drm/i915/gt/selftest_migrate.c
index 54bc447efce0..fdf0e9858607 100644
--- a/drivers/gpu/drm/i915/gt/selftest_migrate.c
+++ b/drivers/gpu/drm/i915/gt/selftest_migrate.c
@@ -710,7 +710,14 @@ static int threaded_migrate(struct intel_migrate *migrate,
thread[i].tsk = tsk;
}
- msleep(10 * n_cpus); /* start all threads before we kthread_stop() */
+ /*
+ * Start all threads before we kthread_stop().
+ * In CHV / BXT+VTD environments, where VMA pinning is committed
+ * asynchronously, empirically determined 100ms delay is needed
+ * to avoid stopping threads that may still wait for completion of
+ * intel_ggtt_bind_vma and fail with -ERESTARTSYS when interrupted.
+ */
+ msleep((intel_vm_no_concurrent_access_wa(migrate->context->vm->i915) ? 100 : 10) * n_cpus);
for (i = 0; i < n_cpus; ++i) {
struct task_struct *tsk = thread[i].tsk;
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c b/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c
index 9bd29be7656f..dabb870dcdb1 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c
@@ -3,6 +3,8 @@
* Copyright © 2023 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "gt/intel_context.h"
#include "gt/intel_engine_pm.h"
#include "gt/intel_gpu_commands.h"
diff --git a/drivers/gpu/drm/i915/gvt/aperture_gm.c b/drivers/gpu/drm/i915/gvt/aperture_gm.c
index 62d14f82256f..8cc6e712b0f7 100644
--- a/drivers/gpu/drm/i915/gvt/aperture_gm.c
+++ b/drivers/gpu/drm/i915/gvt/aperture_gm.c
@@ -34,6 +34,8 @@
*
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_reg.h"
#include "gt/intel_ggtt_fencing.h"
diff --git a/drivers/gpu/drm/i915/gvt/cfg_space.c b/drivers/gpu/drm/i915/gvt/cfg_space.c
index 9bafac1eaf48..295a7b5e1d7c 100644
--- a/drivers/gpu/drm/i915/gvt/cfg_space.c
+++ b/drivers/gpu/drm/i915/gvt/cfg_space.c
@@ -31,6 +31,8 @@
*
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "gvt.h"
#include "intel_pci_config.h"
diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c
index d432fdd69833..df04e4ead8ea 100644
--- a/drivers/gpu/drm/i915/gvt/cmd_parser.c
+++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c
@@ -36,6 +36,8 @@
#include <linux/slab.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_reg.h"
#include "display/intel_display_regs.h"
diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c
index 74197e337585..06517d1f07a2 100644
--- a/drivers/gpu/drm/i915/gvt/display.c
+++ b/drivers/gpu/drm/i915/gvt/display.c
@@ -33,6 +33,7 @@
*/
#include <drm/display/drm_dp.h>
+#include <drm/drm_print.h>
#include "i915_drv.h"
#include "i915_reg.h"
diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c
index 4f599af766b0..92506c80322d 100644
--- a/drivers/gpu/drm/i915/gvt/dmabuf.c
+++ b/drivers/gpu/drm/i915/gvt/dmabuf.c
@@ -33,6 +33,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_dmabuf.h"
diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c
index 2031b97de2b7..30e414381af3 100644
--- a/drivers/gpu/drm/i915/gvt/edid.c
+++ b/drivers/gpu/drm/i915/gvt/edid.c
@@ -33,6 +33,7 @@
*/
#include <drm/display/drm_dp.h>
+#include <drm/drm_print.h>
#include "display/intel_dp_aux_regs.h"
#include "display/intel_gmbus.h"
diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c
index ae9b0ded3651..076d9139edc6 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.c
+++ b/drivers/gpu/drm/i915/gvt/gtt.c
@@ -33,6 +33,8 @@
*
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "gvt.h"
#include "i915_pvinfo.h"
diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c
index f446f73f0fe2..36ea12ade849 100644
--- a/drivers/gpu/drm/i915/gvt/handlers.c
+++ b/drivers/gpu/drm/i915/gvt/handlers.c
@@ -37,6 +37,7 @@
*/
#include <drm/display/drm_dp.h>
+#include <drm/drm_print.h>
#include "i915_drv.h"
#include "i915_reg.h"
diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c
index a956da68e6bd..3e66269bc4ee 100644
--- a/drivers/gpu/drm/i915/gvt/interrupt.c
+++ b/drivers/gpu/drm/i915/gvt/interrupt.c
@@ -31,6 +31,8 @@
#include <linux/eventfd.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_reg.h"
#include "display/intel_display_regs.h"
diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c
index 69830a5c49d3..3abc9206f1a8 100644
--- a/drivers/gpu/drm/i915/gvt/kvmgt.c
+++ b/drivers/gpu/drm/i915/gvt/kvmgt.c
@@ -48,6 +48,7 @@
#include <linux/nospec.h>
#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
#include "i915_drv.h"
#include "intel_gvt.h"
@@ -1140,6 +1141,122 @@ static int intel_vgpu_set_irqs(struct intel_vgpu *vgpu, u32 flags,
return func(vgpu, index, start, count, flags, data);
}
+static int intel_vgpu_ioctl_get_region_info(struct vfio_device *vfio_dev,
+ struct vfio_region_info *info,
+ struct vfio_info_cap *caps)
+{
+ struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
+ struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
+ int nr_areas = 1;
+ int cap_type_id;
+ unsigned int i;
+ int ret;
+
+ switch (info->index) {
+ case VFIO_PCI_CONFIG_REGION_INDEX:
+ info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
+ info->size = vgpu->gvt->device_info.cfg_space_size;
+ info->flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ break;
+ case VFIO_PCI_BAR0_REGION_INDEX:
+ info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
+ info->size = vgpu->cfg_space.bar[info->index].size;
+ if (!info->size) {
+ info->flags = 0;
+ break;
+ }
+
+ info->flags = VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ break;
+ case VFIO_PCI_BAR1_REGION_INDEX:
+ info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
+ info->size = 0;
+ info->flags = 0;
+ break;
+ case VFIO_PCI_BAR2_REGION_INDEX:
+ info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
+ info->flags = VFIO_REGION_INFO_FLAG_CAPS |
+ VFIO_REGION_INFO_FLAG_MMAP |
+ VFIO_REGION_INFO_FLAG_READ |
+ VFIO_REGION_INFO_FLAG_WRITE;
+ info->size = gvt_aperture_sz(vgpu->gvt);
+
+ sparse = kzalloc(struct_size(sparse, areas, nr_areas),
+ GFP_KERNEL);
+ if (!sparse)
+ return -ENOMEM;
+
+ sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+ sparse->header.version = 1;
+ sparse->nr_areas = nr_areas;
+ cap_type_id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
+ sparse->areas[0].offset =
+ PAGE_ALIGN(vgpu_aperture_offset(vgpu));
+ sparse->areas[0].size = vgpu_aperture_sz(vgpu);
+ break;
+
+ case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
+ info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
+ info->size = 0;
+ info->flags = 0;
+
+ gvt_dbg_core("get region info bar:%d\n", info->index);
+ break;
+
+ case VFIO_PCI_ROM_REGION_INDEX:
+ case VFIO_PCI_VGA_REGION_INDEX:
+ info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
+ info->size = 0;
+ info->flags = 0;
+
+ gvt_dbg_core("get region info index:%d\n", info->index);
+ break;
+ default: {
+ struct vfio_region_info_cap_type cap_type = {
+ .header.id = VFIO_REGION_INFO_CAP_TYPE,
+ .header.version = 1
+ };
+
+ if (info->index >= VFIO_PCI_NUM_REGIONS + vgpu->num_regions)
+ return -EINVAL;
+ info->index = array_index_nospec(
+ info->index, VFIO_PCI_NUM_REGIONS + vgpu->num_regions);
+
+ i = info->index - VFIO_PCI_NUM_REGIONS;
+
+ info->offset = VFIO_PCI_INDEX_TO_OFFSET(info->index);
+ info->size = vgpu->region[i].size;
+ info->flags = vgpu->region[i].flags;
+
+ cap_type.type = vgpu->region[i].type;
+ cap_type.subtype = vgpu->region[i].subtype;
+
+ ret = vfio_info_add_capability(caps, &cap_type.header,
+ sizeof(cap_type));
+ if (ret)
+ return ret;
+ }
+ }
+
+ if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) && sparse) {
+ ret = -EINVAL;
+ if (cap_type_id == VFIO_REGION_INFO_CAP_SPARSE_MMAP) {
+ ret = vfio_info_add_capability(
+ caps, &sparse->header,
+ struct_size(sparse, areas, sparse->nr_areas));
+ }
+ if (ret) {
+ kfree(sparse);
+ return ret;
+ }
+ }
+
+ kfree(sparse);
+ return 0;
+}
+
static long intel_vgpu_ioctl(struct vfio_device *vfio_dev, unsigned int cmd,
unsigned long arg)
{
@@ -1168,157 +1285,6 @@ static long intel_vgpu_ioctl(struct vfio_device *vfio_dev, unsigned int cmd,
return copy_to_user((void __user *)arg, &info, minsz) ?
-EFAULT : 0;
- } else if (cmd == VFIO_DEVICE_GET_REGION_INFO) {
- struct vfio_region_info info;
- struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
- unsigned int i;
- int ret;
- struct vfio_region_info_cap_sparse_mmap *sparse = NULL;
- int nr_areas = 1;
- int cap_type_id;
-
- minsz = offsetofend(struct vfio_region_info, offset);
-
- if (copy_from_user(&info, (void __user *)arg, minsz))
- return -EFAULT;
-
- if (info.argsz < minsz)
- return -EINVAL;
-
- switch (info.index) {
- case VFIO_PCI_CONFIG_REGION_INDEX:
- info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
- info.size = vgpu->gvt->device_info.cfg_space_size;
- info.flags = VFIO_REGION_INFO_FLAG_READ |
- VFIO_REGION_INFO_FLAG_WRITE;
- break;
- case VFIO_PCI_BAR0_REGION_INDEX:
- info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
- info.size = vgpu->cfg_space.bar[info.index].size;
- if (!info.size) {
- info.flags = 0;
- break;
- }
-
- info.flags = VFIO_REGION_INFO_FLAG_READ |
- VFIO_REGION_INFO_FLAG_WRITE;
- break;
- case VFIO_PCI_BAR1_REGION_INDEX:
- info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
- info.size = 0;
- info.flags = 0;
- break;
- case VFIO_PCI_BAR2_REGION_INDEX:
- info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
- info.flags = VFIO_REGION_INFO_FLAG_CAPS |
- VFIO_REGION_INFO_FLAG_MMAP |
- VFIO_REGION_INFO_FLAG_READ |
- VFIO_REGION_INFO_FLAG_WRITE;
- info.size = gvt_aperture_sz(vgpu->gvt);
-
- sparse = kzalloc(struct_size(sparse, areas, nr_areas),
- GFP_KERNEL);
- if (!sparse)
- return -ENOMEM;
-
- sparse->header.id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
- sparse->header.version = 1;
- sparse->nr_areas = nr_areas;
- cap_type_id = VFIO_REGION_INFO_CAP_SPARSE_MMAP;
- sparse->areas[0].offset =
- PAGE_ALIGN(vgpu_aperture_offset(vgpu));
- sparse->areas[0].size = vgpu_aperture_sz(vgpu);
- break;
-
- case VFIO_PCI_BAR3_REGION_INDEX ... VFIO_PCI_BAR5_REGION_INDEX:
- info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
- info.size = 0;
- info.flags = 0;
-
- gvt_dbg_core("get region info bar:%d\n", info.index);
- break;
-
- case VFIO_PCI_ROM_REGION_INDEX:
- case VFIO_PCI_VGA_REGION_INDEX:
- info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
- info.size = 0;
- info.flags = 0;
-
- gvt_dbg_core("get region info index:%d\n", info.index);
- break;
- default:
- {
- struct vfio_region_info_cap_type cap_type = {
- .header.id = VFIO_REGION_INFO_CAP_TYPE,
- .header.version = 1 };
-
- if (info.index >= VFIO_PCI_NUM_REGIONS +
- vgpu->num_regions)
- return -EINVAL;
- info.index =
- array_index_nospec(info.index,
- VFIO_PCI_NUM_REGIONS +
- vgpu->num_regions);
-
- i = info.index - VFIO_PCI_NUM_REGIONS;
-
- info.offset =
- VFIO_PCI_INDEX_TO_OFFSET(info.index);
- info.size = vgpu->region[i].size;
- info.flags = vgpu->region[i].flags;
-
- cap_type.type = vgpu->region[i].type;
- cap_type.subtype = vgpu->region[i].subtype;
-
- ret = vfio_info_add_capability(&caps,
- &cap_type.header,
- sizeof(cap_type));
- if (ret)
- return ret;
- }
- }
-
- if ((info.flags & VFIO_REGION_INFO_FLAG_CAPS) && sparse) {
- switch (cap_type_id) {
- case VFIO_REGION_INFO_CAP_SPARSE_MMAP:
- ret = vfio_info_add_capability(&caps,
- &sparse->header,
- struct_size(sparse, areas,
- sparse->nr_areas));
- if (ret) {
- kfree(sparse);
- return ret;
- }
- break;
- default:
- kfree(sparse);
- return -EINVAL;
- }
- }
-
- if (caps.size) {
- info.flags |= VFIO_REGION_INFO_FLAG_CAPS;
- if (info.argsz < sizeof(info) + caps.size) {
- info.argsz = sizeof(info) + caps.size;
- info.cap_offset = 0;
- } else {
- vfio_info_cap_shift(&caps, sizeof(info));
- if (copy_to_user((void __user *)arg +
- sizeof(info), caps.buf,
- caps.size)) {
- kfree(caps.buf);
- kfree(sparse);
- return -EFAULT;
- }
- info.cap_offset = sizeof(info);
- }
-
- kfree(caps.buf);
- }
-
- kfree(sparse);
- return copy_to_user((void __user *)arg, &info, minsz) ?
- -EFAULT : 0;
} else if (cmd == VFIO_DEVICE_GET_IRQ_INFO) {
struct vfio_irq_info info;
@@ -1361,21 +1327,27 @@ static long intel_vgpu_ioctl(struct vfio_device *vfio_dev, unsigned int cmd,
if (copy_from_user(&hdr, (void __user *)arg, minsz))
return -EFAULT;
+ if (!is_power_of_2(hdr.flags & VFIO_IRQ_SET_DATA_TYPE_MASK) ||
+ !is_power_of_2(hdr.flags & VFIO_IRQ_SET_ACTION_TYPE_MASK))
+ return -EINVAL;
+
if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) {
int max = intel_vgpu_get_irq_count(vgpu, hdr.index);
+ if (!hdr.count)
+ return -EINVAL;
+
ret = vfio_set_irqs_validate_and_prepare(&hdr, max,
VFIO_PCI_NUM_IRQS, &data_size);
if (ret) {
- gvt_vgpu_err("intel:vfio_set_irqs_validate_and_prepare failed\n");
- return -EINVAL;
- }
- if (data_size) {
- data = memdup_user((void __user *)(arg + minsz),
- data_size);
- if (IS_ERR(data))
- return PTR_ERR(data);
+ gvt_vgpu_err("vfio_set_irqs_validate_and_prepare failed\n");
+ return ret;
}
+
+ data = memdup_user((void __user *)(arg + minsz),
+ data_size);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
}
ret = intel_vgpu_set_irqs(vgpu, hdr.flags, hdr.index,
@@ -1475,6 +1447,7 @@ static const struct vfio_device_ops intel_vgpu_dev_ops = {
.write = intel_vgpu_write,
.mmap = intel_vgpu_mmap,
.ioctl = intel_vgpu_ioctl,
+ .get_region_info_caps = intel_vgpu_ioctl_get_region_info,
.dma_unmap = intel_vgpu_dma_unmap,
.bind_iommufd = vfio_iommufd_emulated_bind,
.unbind_iommufd = vfio_iommufd_emulated_unbind,
diff --git a/drivers/gpu/drm/i915/gvt/mmio.c b/drivers/gpu/drm/i915/gvt/mmio.c
index da1135fa7cda..214eb7effa31 100644
--- a/drivers/gpu/drm/i915/gvt/mmio.c
+++ b/drivers/gpu/drm/i915/gvt/mmio.c
@@ -34,6 +34,9 @@
*/
#include <linux/vmalloc.h>
+
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_reg.h"
#include "display/intel_display_regs.h"
@@ -49,7 +52,7 @@
* @gpa: guest physical address
*
* Returns:
- * Zero on success, negative error code if failed
+ * The MMIO offset of the given GPA
*/
int intel_vgpu_gpa_to_mmio_offset(struct intel_vgpu *vgpu, u64 gpa)
{
@@ -58,7 +61,7 @@ int intel_vgpu_gpa_to_mmio_offset(struct intel_vgpu *vgpu, u64 gpa)
}
#define reg_is_mmio(gvt, reg) \
- (reg >= 0 && reg < gvt->device_info.mmio_size)
+ (reg < gvt->device_info.mmio_size)
#define reg_is_gtt(gvt, reg) \
(reg >= gvt->device_info.gtt_start_offset \
diff --git a/drivers/gpu/drm/i915/gvt/mmio_context.c b/drivers/gpu/drm/i915/gvt/mmio_context.c
index 0b810baad20a..d4e9d485d382 100644
--- a/drivers/gpu/drm/i915/gvt/mmio_context.c
+++ b/drivers/gpu/drm/i915/gvt/mmio_context.c
@@ -33,6 +33,8 @@
*
*/
+#include <drm/drm_print.h>
+
#include "gt/intel_context.h"
#include "gt/intel_engine_regs.h"
#include "gt/intel_gpu_commands.h"
diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 6e87c10bc454..63ad1fed525a 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -35,6 +35,8 @@
#include <linux/kthread.h>
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_pm.h"
#include "gt/intel_context.h"
#include "gt/intel_execlists_submission.h"
diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c
index 11260392234a..c49e4bf95a30 100644
--- a/drivers/gpu/drm/i915/gvt/vgpu.c
+++ b/drivers/gpu/drm/i915/gvt/vgpu.c
@@ -31,6 +31,8 @@
*
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "gvt.h"
#include "i915_pvinfo.h"
diff --git a/drivers/gpu/drm/i915/i915_cmd_parser.c b/drivers/gpu/drm/i915/i915_cmd_parser.c
index 2905df83e180..7654f1be8d3b 100644
--- a/drivers/gpu/drm/i915/i915_cmd_parser.c
+++ b/drivers/gpu/drm/i915/i915_cmd_parser.c
@@ -28,6 +28,7 @@
#include <linux/highmem.h>
#include <drm/drm_cache.h>
+#include <drm/drm_print.h>
#include "gt/intel_engine.h"
#include "gt/intel_engine_regs.h"
diff --git a/drivers/gpu/drm/i915/i915_config.c b/drivers/gpu/drm/i915/i915_config.c
index 24e5bb8a670e..3cb615ffa96d 100644
--- a/drivers/gpu/drm/i915/i915_config.c
+++ b/drivers/gpu/drm/i915/i915_config.c
@@ -6,7 +6,7 @@
#include <linux/kernel.h>
#include "i915_config.h"
-#include "i915_utils.h"
+#include "i915_jiffies.h"
unsigned long
i915_fence_context_timeout(const struct drm_i915_private *i915, u64 context)
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index c2e38d4bcd01..42f6b44f0027 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -32,6 +32,7 @@
#include <linux/string_helpers.h>
#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_context.h"
#include "gt/intel_gt.h"
diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c
index a28c3710c4d5..c97b76771917 100644
--- a/drivers/gpu/drm/i915/i915_driver.c
+++ b/drivers/gpu/drm/i915/i915_driver.c
@@ -46,6 +46,8 @@
#include <drm/drm_ioctl.h>
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
+#include <drm/intel/display_member.h>
+#include <drm/intel/display_parent_interface.h>
#include "display/i9xx_display_sr.h"
#include "display/intel_bw.h"
@@ -737,6 +739,18 @@ static void i915_welcome_messages(struct drm_i915_private *dev_priv)
"DRM_I915_DEBUG_RUNTIME_PM enabled\n");
}
+static const struct intel_display_parent_interface parent = {
+ .rpm = &i915_display_rpm_interface,
+};
+
+const struct intel_display_parent_interface *i915_driver_parent_interface(void)
+{
+ return &parent;
+}
+
+/* Ensure drm and display members are placed properly. */
+INTEL_DISPLAY_MEMBER_STATIC_ASSERT(struct drm_i915_private, drm, display);
+
static struct drm_i915_private *
i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent)
{
@@ -758,7 +772,7 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent)
/* Set up device info and initial runtime info. */
intel_device_info_driver_create(i915, pdev->device, match_info);
- display = intel_display_device_probe(pdev);
+ display = intel_display_device_probe(pdev, &parent);
if (IS_ERR(display))
return ERR_CAST(display);
@@ -978,7 +992,7 @@ void i915_driver_shutdown(struct drm_i915_private *i915)
intel_runtime_pm_disable(&i915->runtime_pm);
intel_power_domains_disable(display);
- drm_client_dev_suspend(&i915->drm, false);
+ drm_client_dev_suspend(&i915->drm);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(&i915->drm);
intel_display_driver_disable_user_access(display);
@@ -1053,7 +1067,6 @@ static int i915_drm_suspend(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_display *display = dev_priv->display;
- struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
pci_power_t opregion_target_state;
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
@@ -1061,14 +1074,12 @@ static int i915_drm_suspend(struct drm_device *dev)
/* We do a lot of poking in a lot of registers, make sure they work
* properly. */
intel_power_domains_disable(display);
- drm_client_dev_suspend(dev, false);
+ drm_client_dev_suspend(dev);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(dev);
intel_display_driver_disable_user_access(display);
}
- pci_save_state(pdev);
-
intel_display_driver_suspend(display);
intel_irq_suspend(dev_priv);
@@ -1103,7 +1114,6 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_display *display = dev_priv->display;
- struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
struct intel_runtime_pm *rpm = &dev_priv->runtime_pm;
struct intel_gt *gt;
int ret, i;
@@ -1124,11 +1134,21 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
if (ret) {
drm_err(&dev_priv->drm, "Suspend complete failed: %d\n", ret);
intel_display_power_resume_early(display);
-
- goto out;
}
- pci_disable_device(pdev);
+ enable_rpm_wakeref_asserts(rpm);
+
+ if (!dev_priv->uncore.user_forcewake_count)
+ intel_runtime_pm_driver_release(rpm);
+
+ return ret;
+}
+
+static int i915_drm_suspend_noirq(struct drm_device *dev, bool hibernation)
+{
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
+
/*
* During hibernation on some platforms the BIOS may try to access
* the device even though it's already in D3 and hang the machine. So
@@ -1140,21 +1160,20 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
* Lenovo Thinkpad X301, X61s, X60, T60, X41
* Fujitsu FSC S7110
* Acer Aspire 1830T
+ *
+ * pci_save_state() prevents drivers/pci from
+ * automagically putting the device into D3.
*/
- if (!(hibernation && GRAPHICS_VER(dev_priv) < 6))
- pci_set_power_state(pdev, PCI_D3hot);
-
-out:
- enable_rpm_wakeref_asserts(rpm);
- if (!dev_priv->uncore.user_forcewake_count)
- intel_runtime_pm_driver_release(rpm);
+ if (hibernation && GRAPHICS_VER(dev_priv) < 6)
+ pci_save_state(pdev);
- return ret;
+ return 0;
}
int i915_driver_suspend_switcheroo(struct drm_i915_private *i915,
pm_message_t state)
{
+ struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
int error;
if (drm_WARN_ON_ONCE(&i915->drm, state.event != PM_EVENT_SUSPEND &&
@@ -1168,7 +1187,14 @@ int i915_driver_suspend_switcheroo(struct drm_i915_private *i915,
if (error)
return error;
- return i915_drm_suspend_late(&i915->drm, false);
+ error = i915_drm_suspend_late(&i915->drm, false);
+ if (error)
+ return error;
+
+ pci_save_state(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+
+ return 0;
}
static int i915_drm_resume(struct drm_device *dev)
@@ -1245,7 +1271,7 @@ static int i915_drm_resume(struct drm_device *dev)
intel_opregion_resume(display);
- drm_client_dev_resume(dev, false);
+ drm_client_dev_resume(dev);
intel_power_domains_enable(display);
@@ -1260,7 +1286,6 @@ static int i915_drm_resume_early(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct intel_display *display = dev_priv->display;
- struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);
struct intel_gt *gt;
int ret, i;
@@ -1274,41 +1299,6 @@ static int i915_drm_resume_early(struct drm_device *dev)
* similar so that power domains can be employed.
*/
- /*
- * Note that we need to set the power state explicitly, since we
- * powered off the device during freeze and the PCI core won't power
- * it back up for us during thaw. Powering off the device during
- * freeze is not a hard requirement though, and during the
- * suspend/resume phases the PCI core makes sure we get here with the
- * device powered on. So in case we change our freeze logic and keep
- * the device powered we can also remove the following set power state
- * call.
- */
- ret = pci_set_power_state(pdev, PCI_D0);
- if (ret) {
- drm_err(&dev_priv->drm,
- "failed to set PCI D0 power state (%d)\n", ret);
- return ret;
- }
-
- /*
- * Note that pci_enable_device() first enables any parent bridge
- * device and only then sets the power state for this device. The
- * bridge enabling is a nop though, since bridge devices are resumed
- * first. The order of enabling power and enabling the device is
- * imposed by the PCI core as described above, so here we preserve the
- * same order for the freeze/thaw phases.
- *
- * TODO: eventually we should remove pci_disable_device() /
- * pci_enable_enable_device() from suspend/resume. Due to how they
- * depend on the device enable refcount we can't anyway depend on them
- * disabling/enabling the device.
- */
- if (pci_enable_device(pdev))
- return -EIO;
-
- pci_set_master(pdev);
-
disable_rpm_wakeref_asserts(&dev_priv->runtime_pm);
ret = vlv_resume_prepare(dev_priv, false);
@@ -1328,11 +1318,18 @@ static int i915_drm_resume_early(struct drm_device *dev)
int i915_driver_resume_switcheroo(struct drm_i915_private *i915)
{
+ struct pci_dev *pdev = to_pci_dev(i915->drm.dev);
int ret;
if (i915->drm.switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
+ ret = pci_set_power_state(pdev, PCI_D0);
+ if (ret)
+ return ret;
+
+ pci_restore_state(pdev);
+
ret = i915_drm_resume_early(&i915->drm);
if (ret)
return ret;
@@ -1389,6 +1386,16 @@ static int i915_pm_suspend_late(struct device *kdev)
return i915_drm_suspend_late(&i915->drm, false);
}
+static int i915_pm_suspend_noirq(struct device *kdev)
+{
+ struct drm_i915_private *i915 = kdev_to_i915(kdev);
+
+ if (i915->drm.switch_power_state == DRM_SWITCH_POWER_OFF)
+ return 0;
+
+ return i915_drm_suspend_noirq(&i915->drm, false);
+}
+
static int i915_pm_poweroff_late(struct device *kdev)
{
struct drm_i915_private *i915 = kdev_to_i915(kdev);
@@ -1399,6 +1406,16 @@ static int i915_pm_poweroff_late(struct device *kdev)
return i915_drm_suspend_late(&i915->drm, true);
}
+static int i915_pm_poweroff_noirq(struct device *kdev)
+{
+ struct drm_i915_private *i915 = kdev_to_i915(kdev);
+
+ if (i915->drm.switch_power_state == DRM_SWITCH_POWER_OFF)
+ return 0;
+
+ return i915_drm_suspend_noirq(&i915->drm, true);
+}
+
static int i915_pm_resume_early(struct device *kdev)
{
struct drm_i915_private *i915 = kdev_to_i915(kdev);
@@ -1664,24 +1681,25 @@ const struct dev_pm_ops i915_pm_ops = {
.prepare = i915_pm_prepare,
.suspend = i915_pm_suspend,
.suspend_late = i915_pm_suspend_late,
+ .suspend_noirq = i915_pm_suspend_noirq,
.resume_early = i915_pm_resume_early,
.resume = i915_pm_resume,
.complete = i915_pm_complete,
/*
* S4 event handlers
- * @freeze, @freeze_late : called (1) before creating the
- * hibernation image [PMSG_FREEZE] and
- * (2) after rebooting, before restoring
- * the image [PMSG_QUIESCE]
- * @thaw, @thaw_early : called (1) after creating the hibernation
- * image, before writing it [PMSG_THAW]
- * and (2) after failing to create or
- * restore the image [PMSG_RECOVER]
- * @poweroff, @poweroff_late: called after writing the hibernation
- * image, before rebooting [PMSG_HIBERNATE]
- * @restore, @restore_early : called after rebooting and restoring the
- * hibernation image [PMSG_RESTORE]
+ * @freeze* : called (1) before creating the
+ * hibernation image [PMSG_FREEZE] and
+ * (2) after rebooting, before restoring
+ * the image [PMSG_QUIESCE]
+ * @thaw* : called (1) after creating the hibernation
+ * image, before writing it [PMSG_THAW]
+ * and (2) after failing to create or
+ * restore the image [PMSG_RECOVER]
+ * @poweroff* : called after writing the hibernation
+ * image, before rebooting [PMSG_HIBERNATE]
+ * @restore* : called after rebooting and restoring the
+ * hibernation image [PMSG_RESTORE]
*/
.freeze = i915_pm_freeze,
.freeze_late = i915_pm_freeze_late,
@@ -1689,6 +1707,7 @@ const struct dev_pm_ops i915_pm_ops = {
.thaw = i915_pm_thaw,
.poweroff = i915_pm_suspend,
.poweroff_late = i915_pm_poweroff_late,
+ .poweroff_noirq = i915_pm_poweroff_noirq,
.restore_early = i915_pm_restore_early,
.restore = i915_pm_restore,
diff --git a/drivers/gpu/drm/i915/i915_driver.h b/drivers/gpu/drm/i915/i915_driver.h
index 1e95ecb2a163..9551519ab429 100644
--- a/drivers/gpu/drm/i915/i915_driver.h
+++ b/drivers/gpu/drm/i915/i915_driver.h
@@ -12,6 +12,7 @@ struct pci_dev;
struct pci_device_id;
struct drm_i915_private;
struct drm_printer;
+struct intel_display_parent_interface;
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
@@ -24,6 +25,7 @@ void i915_driver_shutdown(struct drm_i915_private *i915);
int i915_driver_resume_switcheroo(struct drm_i915_private *i915);
int i915_driver_suspend_switcheroo(struct drm_i915_private *i915, pm_message_t state);
+const struct intel_display_parent_interface *i915_driver_parent_interface(void);
void
i915_print_iommu_status(struct drm_i915_private *i915, struct drm_printer *p);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6a768aad8edd..5381a934a671 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -174,6 +174,7 @@ struct i915_selftest_stash {
struct drm_i915_private {
struct drm_device drm;
+ /* display device data, must be placed after drm device member */
struct intel_display *display;
/* FIXME: Device release actions should all be moved to drmm_ */
@@ -234,14 +235,11 @@ struct drm_i915_private {
/* Sideband mailbox protection */
struct mutex sb_lock;
- /** Cached value of IMR to avoid reads in updating the bitfield */
- u32 irq_mask;
+ /* Cached value of gen 2-4 IMR to avoid reads in updating the bitfield */
+ u32 gen2_imr_mask;
bool preserve_bios_swizzle;
- unsigned int hpll_freq;
- unsigned int czclk_freq;
-
/**
* wq - Driver workqueue for GEM.
*
@@ -313,6 +311,8 @@ struct drm_i915_private {
struct file *mmap_singleton;
} gem;
+ spinlock_t frontbuffer_lock; /* protects obj->frontbuffer (write-side) */
+
struct intel_pxp *pxp;
struct i915_pmu pmu;
@@ -490,16 +490,6 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915,
#define IS_ALDERLAKE_P(i915) IS_PLATFORM(i915, INTEL_ALDERLAKE_P)
#define IS_DG2(i915) IS_PLATFORM(i915, INTEL_DG2)
#define IS_METEORLAKE(i915) IS_PLATFORM(i915, INTEL_METEORLAKE)
-/*
- * Display code shared by i915 and Xe relies on macros like IS_LUNARLAKE,
- * so we need to define these even on platforms that the i915 base driver
- * doesn't support. Ensure the parameter is used in the definition to
- * avoid 'unused variable' warnings when compiling the shared display code
- * for i915.
- */
-#define IS_LUNARLAKE(i915) (0 && i915)
-#define IS_BATTLEMAGE(i915) (0 && i915)
-#define IS_PANTHERLAKE(i915) (0 && i915)
#define IS_ARROWLAKE_H(i915) \
IS_SUBPLATFORM(i915, INTEL_METEORLAKE, INTEL_SUBPLATFORM_ARL_H)
@@ -604,8 +594,7 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915,
/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
* rows, which changed the alignment requirements and fence programming.
*/
-#define HAS_128_BYTE_Y_TILING(i915) (GRAPHICS_VER(i915) != 2 && \
- !(IS_I915G(i915) || IS_I915GM(i915)))
+#define HAS_128_BYTE_Y_TILING(i915) (!IS_I915G(i915) && !IS_I915GM(i915))
#define HAS_RC6(i915) (INTEL_INFO(i915)->has_rc6)
#define HAS_RC6p(i915) (INTEL_INFO(i915)->has_rc6p)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index e14a0c3db999..4c82c9544b93 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -37,6 +37,7 @@
#include <linux/mman.h>
#include <drm/drm_cache.h>
+#include <drm/drm_print.h>
#include <drm/drm_vma_manager.h>
#include "gem/i915_gem_clflush.h"
@@ -1298,6 +1299,8 @@ void i915_gem_init_early(struct drm_i915_private *dev_priv)
{
i915_gem_init__mm(dev_priv);
i915_gem_init__contexts(dev_priv);
+
+ spin_lock_init(&dev_priv->frontbuffer_lock);
}
void i915_gem_cleanup_early(struct drm_i915_private *dev_priv)
diff --git a/drivers/gpu/drm/i915/i915_getparam.c b/drivers/gpu/drm/i915/i915_getparam.c
index 6fcda6d7b5b7..cf47c2491a0a 100644
--- a/drivers/gpu/drm/i915/i915_getparam.c
+++ b/drivers/gpu/drm/i915/i915_getparam.c
@@ -2,6 +2,8 @@
* SPDX-License-Identifier: MIT
*/
+#include <drm/drm_print.h>
+
#include "display/intel_overlay.h"
#include "gem/i915_gem_mman.h"
#include "gt/intel_engine_user.h"
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 8d5da222a187..1898be4ddc8b 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -32,13 +32,12 @@
#include <linux/sysrq.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
-#include "display/intel_display_core.h"
#include "display/intel_display_irq.h"
#include "display/intel_hotplug.h"
#include "display/intel_hotplug_irq.h"
#include "display/intel_lpe_audio.h"
-#include "display/intel_psr_regs.h"
#include "gt/intel_breadcrumbs.h"
#include "gt/intel_gt.h"
@@ -415,7 +414,7 @@ static irqreturn_t ilk_irq_handler(int irq, void *arg)
struct drm_i915_private *i915 = arg;
struct intel_display *display = i915->display;
void __iomem * const regs = intel_uncore_regs(&i915->uncore);
- u32 de_iir, gt_iir, de_ier, sde_ier = 0;
+ u32 gt_iir, de_ier = 0, sde_ier = 0;
irqreturn_t ret = IRQ_NONE;
if (unlikely(!intel_irqs_enabled(i915)))
@@ -424,19 +423,8 @@ static irqreturn_t ilk_irq_handler(int irq, void *arg)
/* IRQs are synced during runtime_suspend, we don't require a wakeref */
disable_rpm_wakeref_asserts(&i915->runtime_pm);
- /* disable master interrupt before clearing iir */
- de_ier = raw_reg_read(regs, DEIER);
- raw_reg_write(regs, DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
-
- /* Disable south interrupts. We'll only write to SDEIIR once, so further
- * interrupts will will be stored on its back queue, and then we'll be
- * able to process them after we restore SDEIER (as soon as we restore
- * it, we'll get an interrupt if SDEIIR still has something to process
- * due to its back queue). */
- if (!HAS_PCH_NOP(display)) {
- sde_ier = raw_reg_read(regs, SDEIER);
- raw_reg_write(regs, SDEIER, 0);
- }
+ /* Disable master and south interrupts */
+ ilk_display_irq_master_disable(display, &de_ier, &sde_ier);
/* Find, clear, then process each source of interrupt */
@@ -450,15 +438,8 @@ static irqreturn_t ilk_irq_handler(int irq, void *arg)
ret = IRQ_HANDLED;
}
- de_iir = raw_reg_read(regs, DEIIR);
- if (de_iir) {
- raw_reg_write(regs, DEIIR, de_iir);
- if (DISPLAY_VER(display) >= 7)
- ivb_display_irq_handler(display, de_iir);
- else
- ilk_display_irq_handler(display, de_iir);
+ if (ilk_display_irq_handler(display))
ret = IRQ_HANDLED;
- }
if (GRAPHICS_VER(i915) >= 6) {
u32 pm_iir = raw_reg_read(regs, GEN6_PMIIR);
@@ -469,9 +450,8 @@ static irqreturn_t ilk_irq_handler(int irq, void *arg)
}
}
- raw_reg_write(regs, DEIER, de_ier);
- if (sde_ier)
- raw_reg_write(regs, SDEIER, sde_ier);
+ /* Re-enable master and south interrupts */
+ ilk_display_irq_master_enable(display, de_ier, sde_ier);
pmu_irq_stats(i915, ret);
@@ -656,22 +636,10 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
static void ilk_irq_reset(struct drm_i915_private *dev_priv)
{
struct intel_display *display = dev_priv->display;
- struct intel_uncore *uncore = &dev_priv->uncore;
-
- gen2_irq_reset(uncore, DE_IRQ_REGS);
- dev_priv->irq_mask = ~0u;
-
- if (GRAPHICS_VER(dev_priv) == 7)
- intel_uncore_write(uncore, GEN7_ERR_INT, 0xffffffff);
-
- if (IS_HASWELL(dev_priv)) {
- intel_uncore_write(uncore, EDP_PSR_IMR, 0xffffffff);
- intel_uncore_write(uncore, EDP_PSR_IIR, 0xffffffff);
- }
+ /* The master interrupt enable is in DEIER, reset display irq first */
+ ilk_display_irq_reset(display);
gen5_gt_irq_reset(to_gt(dev_priv));
-
- ibx_display_irq_reset(display);
}
static void valleyview_irq_reset(struct drm_i915_private *dev_priv)
@@ -826,9 +794,10 @@ static void cherryview_irq_postinstall(struct drm_i915_private *dev_priv)
intel_uncore_posting_read(&dev_priv->uncore, GEN8_MASTER_IRQ);
}
+#define I9XX_HAS_FBC(i915) (IS_I85X(i915) || IS_I865G(i915) || IS_I915GM(i915) || IS_I945GM(i915))
+
static u32 i9xx_error_mask(struct drm_i915_private *i915)
{
- struct intel_display *display = i915->display;
/*
* On gen2/3 FBC generates (seemingly spurious)
* display INVALID_GTT/INVALID_GTT_PTE table errors.
@@ -841,7 +810,7 @@ static u32 i9xx_error_mask(struct drm_i915_private *i915)
* Unfortunately we can't mask off individual PGTBL_ER bits,
* so we just have to mask off all page table errors via EMR.
*/
- if (HAS_FBC(display))
+ if (I9XX_HAS_FBC(i915))
return I915_ERROR_MEMORY_REFRESH;
else
return I915_ERROR_PAGE_TABLE |
@@ -897,7 +866,7 @@ static void i915_irq_reset(struct drm_i915_private *dev_priv)
gen2_error_reset(uncore, GEN2_ERROR_REGS);
gen2_irq_reset(uncore, GEN2_IRQ_REGS);
- dev_priv->irq_mask = ~0u;
+ dev_priv->gen2_imr_mask = ~0u;
}
static void i915_irq_postinstall(struct drm_i915_private *dev_priv)
@@ -908,28 +877,14 @@ static void i915_irq_postinstall(struct drm_i915_private *dev_priv)
gen2_error_init(uncore, GEN2_ERROR_REGS, ~i9xx_error_mask(dev_priv));
- dev_priv->irq_mask =
- ~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
- I915_MASTER_ERROR_INTERRUPT);
+ enable_mask = i9xx_display_irq_enable_mask(display) |
+ I915_MASTER_ERROR_INTERRUPT;
- enable_mask =
- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
- I915_MASTER_ERROR_INTERRUPT |
- I915_USER_INTERRUPT;
-
- if (DISPLAY_VER(display) >= 3) {
- dev_priv->irq_mask &= ~I915_ASLE_INTERRUPT;
- enable_mask |= I915_ASLE_INTERRUPT;
- }
+ dev_priv->gen2_imr_mask = ~enable_mask;
- if (HAS_HOTPLUG(display)) {
- dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT;
- enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
- }
+ enable_mask |= I915_USER_INTERRUPT;
- gen2_irq_init(uncore, GEN2_IRQ_REGS, dev_priv->irq_mask, enable_mask);
+ gen2_irq_init(uncore, GEN2_IRQ_REGS, dev_priv->gen2_imr_mask, enable_mask);
i915_display_irq_postinstall(display);
}
@@ -958,8 +913,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
ret = IRQ_HANDLED;
- if (HAS_HOTPLUG(display) &&
- iir & I915_DISPLAY_PORT_INTERRUPT)
+ if (iir & I915_DISPLAY_PORT_INTERRUPT)
hotplug_status = i9xx_hpd_irq_ack(display);
/* Call regardless, as some status bits might not be
@@ -999,7 +953,7 @@ static void i965_irq_reset(struct drm_i915_private *dev_priv)
gen2_error_reset(uncore, GEN2_ERROR_REGS);
gen2_irq_reset(uncore, GEN2_IRQ_REGS);
- dev_priv->irq_mask = ~0u;
+ dev_priv->gen2_imr_mask = ~0u;
}
static u32 i965_error_mask(struct drm_i915_private *i915)
@@ -1029,25 +983,17 @@ static void i965_irq_postinstall(struct drm_i915_private *dev_priv)
gen2_error_init(uncore, GEN2_ERROR_REGS, ~i965_error_mask(dev_priv));
- dev_priv->irq_mask =
- ~(I915_ASLE_INTERRUPT |
- I915_DISPLAY_PORT_INTERRUPT |
- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
- I915_MASTER_ERROR_INTERRUPT);
-
- enable_mask =
- I915_ASLE_INTERRUPT |
- I915_DISPLAY_PORT_INTERRUPT |
- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
- I915_MASTER_ERROR_INTERRUPT |
- I915_USER_INTERRUPT;
+ enable_mask = i9xx_display_irq_enable_mask(display) |
+ I915_MASTER_ERROR_INTERRUPT;
+
+ dev_priv->gen2_imr_mask = ~enable_mask;
+
+ enable_mask |= I915_USER_INTERRUPT;
if (IS_G4X(dev_priv))
enable_mask |= I915_BSD_USER_INTERRUPT;
- gen2_irq_init(uncore, GEN2_IRQ_REGS, dev_priv->irq_mask, enable_mask);
+ gen2_irq_init(uncore, GEN2_IRQ_REGS, dev_priv->gen2_imr_mask, enable_mask);
i965_display_irq_postinstall(display);
}
diff --git a/drivers/gpu/drm/i915/i915_jiffies.h b/drivers/gpu/drm/i915/i915_jiffies.h
new file mode 100644
index 000000000000..18a4eaea897a
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_jiffies.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: MIT */
+/* Copyright © 2025 Intel Corporation */
+
+#ifndef __I915_JIFFIES_H__
+#define __I915_JIFFIES_H__
+
+#include <linux/jiffies.h>
+
+static inline unsigned long msecs_to_jiffies_timeout(const unsigned int m)
+{
+ unsigned long j = msecs_to_jiffies(m);
+
+ return min_t(unsigned long, MAX_JIFFY_OFFSET, j + 1);
+}
+
+#endif /* __I915_JIFFIES_H__ */
diff --git a/drivers/gpu/drm/i915/i915_mmio_range.c b/drivers/gpu/drm/i915/i915_mmio_range.c
new file mode 100644
index 000000000000..724041e81aa7
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_mmio_range.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "i915_mmio_range.h"
+
+bool i915_mmio_range_table_contains(u32 addr, const struct i915_mmio_range *table)
+{
+ while (table->start || table->end) {
+ if (addr >= table->start && addr <= table->end)
+ return true;
+
+ table++;
+ }
+
+ return false;
+}
diff --git a/drivers/gpu/drm/i915/i915_mmio_range.h b/drivers/gpu/drm/i915/i915_mmio_range.h
new file mode 100644
index 000000000000..f1c7086d3e3c
--- /dev/null
+++ b/drivers/gpu/drm/i915/i915_mmio_range.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef __I915_MMIO_RANGE_H__
+#define __I915_MMIO_RANGE_H__
+
+#include <linux/types.h>
+
+/* Other register ranges (e.g., shadow tables, MCR tables, etc.) */
+struct i915_mmio_range {
+ u32 start;
+ u32 end;
+};
+
+bool i915_mmio_range_table_contains(u32 addr, const struct i915_mmio_range *table);
+
+#endif /* __I915_MMIO_RANGE_H__ */
diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c
index 5862754c662c..5d9c35b5a182 100644
--- a/drivers/gpu/drm/i915/i915_module.c
+++ b/drivers/gpu/drm/i915/i915_module.c
@@ -5,6 +5,7 @@
*/
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "gem/i915_gem_context.h"
#include "gem/i915_gem_object.h"
diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c
index 1658f1246c6f..0b9d9f3f7813 100644
--- a/drivers/gpu/drm/i915/i915_perf.c
+++ b/drivers/gpu/drm/i915/i915_perf.c
@@ -219,6 +219,7 @@
#include "i915_perf.h"
#include "i915_perf_oa_regs.h"
#include "i915_reg.h"
+#include "i915_mmio_range.h"
/* HW requires this to be a power of two, between 128k and 16M, though driver
* is currently generally designed assuming the largest 16M size is used such
@@ -4320,29 +4321,17 @@ static bool gen8_is_valid_flex_addr(struct i915_perf *perf, u32 addr)
return false;
}
-static bool reg_in_range_table(u32 addr, const struct i915_range *table)
-{
- while (table->start || table->end) {
- if (addr >= table->start && addr <= table->end)
- return true;
-
- table++;
- }
-
- return false;
-}
-
#define REG_EQUAL(addr, mmio) \
((addr) == i915_mmio_reg_offset(mmio))
-static const struct i915_range gen7_oa_b_counters[] = {
+static const struct i915_mmio_range gen7_oa_b_counters[] = {
{ .start = 0x2710, .end = 0x272c }, /* OASTARTTRIG[1-8] */
{ .start = 0x2740, .end = 0x275c }, /* OAREPORTTRIG[1-8] */
{ .start = 0x2770, .end = 0x27ac }, /* OACEC[0-7][0-1] */
{}
};
-static const struct i915_range gen12_oa_b_counters[] = {
+static const struct i915_mmio_range gen12_oa_b_counters[] = {
{ .start = 0x2b2c, .end = 0x2b2c }, /* GEN12_OAG_OA_PESS */
{ .start = 0xd900, .end = 0xd91c }, /* GEN12_OAG_OASTARTTRIG[1-8] */
{ .start = 0xd920, .end = 0xd93c }, /* GEN12_OAG_OAREPORTTRIG1[1-8] */
@@ -4353,7 +4342,7 @@ static const struct i915_range gen12_oa_b_counters[] = {
{}
};
-static const struct i915_range mtl_oam_b_counters[] = {
+static const struct i915_mmio_range mtl_oam_b_counters[] = {
{ .start = 0x393000, .end = 0x39301c }, /* GEN12_OAM_STARTTRIG1[1-8] */
{ .start = 0x393020, .end = 0x39303c }, /* GEN12_OAM_REPORTTRIG1[1-8] */
{ .start = 0x393040, .end = 0x39307c }, /* GEN12_OAM_CEC[0-7][0-1] */
@@ -4361,43 +4350,43 @@ static const struct i915_range mtl_oam_b_counters[] = {
{}
};
-static const struct i915_range xehp_oa_b_counters[] = {
+static const struct i915_mmio_range xehp_oa_b_counters[] = {
{ .start = 0xdc48, .end = 0xdc48 }, /* OAA_ENABLE_REG */
{ .start = 0xdd00, .end = 0xdd48 }, /* OAG_LCE0_0 - OAA_LENABLE_REG */
{}
};
-static const struct i915_range gen7_oa_mux_regs[] = {
+static const struct i915_mmio_range gen7_oa_mux_regs[] = {
{ .start = 0x91b8, .end = 0x91cc }, /* OA_PERFCNT[1-2], OA_PERFMATRIX */
{ .start = 0x9800, .end = 0x9888 }, /* MICRO_BP0_0 - NOA_WRITE */
{ .start = 0xe180, .end = 0xe180 }, /* HALF_SLICE_CHICKEN2 */
{}
};
-static const struct i915_range hsw_oa_mux_regs[] = {
+static const struct i915_mmio_range hsw_oa_mux_regs[] = {
{ .start = 0x09e80, .end = 0x09ea4 }, /* HSW_MBVID2_NOA[0-9] */
{ .start = 0x09ec0, .end = 0x09ec0 }, /* HSW_MBVID2_MISR0 */
{ .start = 0x25100, .end = 0x2ff90 },
{}
};
-static const struct i915_range chv_oa_mux_regs[] = {
+static const struct i915_mmio_range chv_oa_mux_regs[] = {
{ .start = 0x182300, .end = 0x1823a4 },
{}
};
-static const struct i915_range gen8_oa_mux_regs[] = {
+static const struct i915_mmio_range gen8_oa_mux_regs[] = {
{ .start = 0x0d00, .end = 0x0d2c }, /* RPM_CONFIG[0-1], NOA_CONFIG[0-8] */
{ .start = 0x20cc, .end = 0x20cc }, /* WAIT_FOR_RC6_EXIT */
{}
};
-static const struct i915_range gen11_oa_mux_regs[] = {
+static const struct i915_mmio_range gen11_oa_mux_regs[] = {
{ .start = 0x91c8, .end = 0x91dc }, /* OA_PERFCNT[3-4] */
{}
};
-static const struct i915_range gen12_oa_mux_regs[] = {
+static const struct i915_mmio_range gen12_oa_mux_regs[] = {
{ .start = 0x0d00, .end = 0x0d04 }, /* RPM_CONFIG[0-1] */
{ .start = 0x0d0c, .end = 0x0d2c }, /* NOA_CONFIG[0-8] */
{ .start = 0x9840, .end = 0x9840 }, /* GDT_CHICKEN_BITS */
@@ -4410,7 +4399,7 @@ static const struct i915_range gen12_oa_mux_regs[] = {
* Ref: 14010536224:
* 0x20cc is repurposed on MTL, so use a separate array for MTL.
*/
-static const struct i915_range mtl_oa_mux_regs[] = {
+static const struct i915_mmio_range mtl_oa_mux_regs[] = {
{ .start = 0x0d00, .end = 0x0d04 }, /* RPM_CONFIG[0-1] */
{ .start = 0x0d0c, .end = 0x0d2c }, /* NOA_CONFIG[0-8] */
{ .start = 0x9840, .end = 0x9840 }, /* GDT_CHICKEN_BITS */
@@ -4421,61 +4410,61 @@ static const struct i915_range mtl_oa_mux_regs[] = {
static bool gen7_is_valid_b_counter_addr(struct i915_perf *perf, u32 addr)
{
- return reg_in_range_table(addr, gen7_oa_b_counters);
+ return i915_mmio_range_table_contains(addr, gen7_oa_b_counters);
}
static bool gen8_is_valid_mux_addr(struct i915_perf *perf, u32 addr)
{
- return reg_in_range_table(addr, gen7_oa_mux_regs) ||
- reg_in_range_table(addr, gen8_oa_mux_regs);
+ return i915_mmio_range_table_contains(addr, gen7_oa_mux_regs) ||
+ i915_mmio_range_table_contains(addr, gen8_oa_mux_regs);
}
static bool gen11_is_valid_mux_addr(struct i915_perf *perf, u32 addr)
{
- return reg_in_range_table(addr, gen7_oa_mux_regs) ||
- reg_in_range_table(addr, gen8_oa_mux_regs) ||
- reg_in_range_table(addr, gen11_oa_mux_regs);
+ return i915_mmio_range_table_contains(addr, gen7_oa_mux_regs) ||
+ i915_mmio_range_table_contains(addr, gen8_oa_mux_regs) ||
+ i915_mmio_range_table_contains(addr, gen11_oa_mux_regs);
}
static bool hsw_is_valid_mux_addr(struct i915_perf *perf, u32 addr)
{
- return reg_in_range_table(addr, gen7_oa_mux_regs) ||
- reg_in_range_table(addr, hsw_oa_mux_regs);
+ return i915_mmio_range_table_contains(addr, gen7_oa_mux_regs) ||
+ i915_mmio_range_table_contains(addr, hsw_oa_mux_regs);
}
static bool chv_is_valid_mux_addr(struct i915_perf *perf, u32 addr)
{
- return reg_in_range_table(addr, gen7_oa_mux_regs) ||
- reg_in_range_table(addr, chv_oa_mux_regs);
+ return i915_mmio_range_table_contains(addr, gen7_oa_mux_regs) ||
+ i915_mmio_range_table_contains(addr, chv_oa_mux_regs);
}
static bool gen12_is_valid_b_counter_addr(struct i915_perf *perf, u32 addr)
{
- return reg_in_range_table(addr, gen12_oa_b_counters);
+ return i915_mmio_range_table_contains(addr, gen12_oa_b_counters);
}
static bool mtl_is_valid_oam_b_counter_addr(struct i915_perf *perf, u32 addr)
{
if (HAS_OAM(perf->i915) &&
GRAPHICS_VER_FULL(perf->i915) >= IP_VER(12, 70))
- return reg_in_range_table(addr, mtl_oam_b_counters);
+ return i915_mmio_range_table_contains(addr, mtl_oam_b_counters);
return false;
}
static bool xehp_is_valid_b_counter_addr(struct i915_perf *perf, u32 addr)
{
- return reg_in_range_table(addr, xehp_oa_b_counters) ||
- reg_in_range_table(addr, gen12_oa_b_counters) ||
+ return i915_mmio_range_table_contains(addr, xehp_oa_b_counters) ||
+ i915_mmio_range_table_contains(addr, gen12_oa_b_counters) ||
mtl_is_valid_oam_b_counter_addr(perf, addr);
}
static bool gen12_is_valid_mux_addr(struct i915_perf *perf, u32 addr)
{
if (GRAPHICS_VER_FULL(perf->i915) >= IP_VER(12, 70))
- return reg_in_range_table(addr, mtl_oa_mux_regs);
+ return i915_mmio_range_table_contains(addr, mtl_oa_mux_regs);
else
- return reg_in_range_table(addr, gen12_oa_mux_regs);
+ return i915_mmio_range_table_contains(addr, gen12_oa_mux_regs);
}
static u32 mask_reg_value(u32 reg, u32 val)
diff --git a/drivers/gpu/drm/i915/i915_pmu.c b/drivers/gpu/drm/i915/i915_pmu.c
index 5bc696bfbb0f..a6697db21c72 100644
--- a/drivers/gpu/drm/i915/i915_pmu.c
+++ b/drivers/gpu/drm/i915/i915_pmu.c
@@ -6,6 +6,8 @@
#include <linux/pm_runtime.h>
+#include <drm/drm_print.h>
+
#include "gt/intel_engine.h"
#include "gt/intel_engine_pm.h"
#include "gt/intel_engine_regs.h"
@@ -895,7 +897,7 @@ static ssize_t i915_pmu_format_show(struct device *dev,
struct i915_str_attribute *eattr;
eattr = container_of(attr, struct i915_str_attribute, attr);
- return sprintf(buf, "%s\n", eattr->str);
+ return sysfs_emit(buf, "%s\n", eattr->str);
}
#define I915_PMU_FORMAT_ATTR(_name, _config) \
@@ -925,7 +927,7 @@ static ssize_t i915_pmu_event_show(struct device *dev,
struct i915_ext_attribute *eattr;
eattr = container_of(attr, struct i915_ext_attribute, attr);
- return sprintf(buf, "config=0x%lx\n", eattr->val);
+ return sysfs_emit(buf, "config=0x%lx\n", eattr->val);
}
#define __event(__counter, __name, __unit) \
diff --git a/drivers/gpu/drm/i915/i915_query.c b/drivers/gpu/drm/i915/i915_query.c
index 14d9ec0ed777..0c55fb6e9727 100644
--- a/drivers/gpu/drm/i915/i915_query.c
+++ b/drivers/gpu/drm/i915/i915_query.c
@@ -6,6 +6,8 @@
#include <linux/nospec.h>
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_perf.h"
#include "i915_query.h"
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 354ef75ef6a5..5bf3b4ab2baa 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1233,6 +1233,7 @@
#define OROM_OFFSET_MASK REG_GENMASK(20, 16)
#define MTL_MEM_SS_INFO_GLOBAL _MMIO(0x45700)
+#define XE3P_ECC_IMPACTING_DE REG_BIT(12)
#define MTL_N_OF_ENABLED_QGV_POINTS_MASK REG_GENMASK(11, 8)
#define MTL_N_OF_POPULATED_CH_MASK REG_GENMASK(7, 4)
#define MTL_DDR_TYPE_MASK REG_GENMASK(3, 0)
diff --git a/drivers/gpu/drm/i915/i915_reg_defs.h b/drivers/gpu/drm/i915/i915_reg_defs.h
index bfe98cb9a038..e81fac8ab51b 100644
--- a/drivers/gpu/drm/i915/i915_reg_defs.h
+++ b/drivers/gpu/drm/i915/i915_reg_defs.h
@@ -174,6 +174,16 @@
*/
#define REG_FIELD_GET8(__mask, __val) ((u8)FIELD_GET(__mask, __val))
+/**
+ * REG_FIELD_MAX() - produce the maximum value representable by a field
+ * @__mask: shifted mask defining the field's length and position
+ *
+ * Local wrapper for FIELD_MAX() to return the maximum bit value that can
+ * be held in the field specified by @_mask, cast to u32 for consistency
+ * with other macros.
+ */
+#define REG_FIELD_MAX(__mask) ((u32)FIELD_MAX(__mask))
+
typedef struct {
u32 reg;
} i915_reg_t;
diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c
index b9a2b2194c8f..4399941236cb 100644
--- a/drivers/gpu/drm/i915/i915_request.c
+++ b/drivers/gpu/drm/i915/i915_request.c
@@ -31,6 +31,8 @@
#include <linux/sched/signal.h>
#include <linux/sched/mm.h>
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_context.h"
#include "gt/intel_breadcrumbs.h"
#include "gt/intel_context.h"
diff --git a/drivers/gpu/drm/i915/i915_switcheroo.c b/drivers/gpu/drm/i915/i915_switcheroo.c
index d5b6d8ab31a2..7e0791024282 100644
--- a/drivers/gpu/drm/i915/i915_switcheroo.c
+++ b/drivers/gpu/drm/i915/i915_switcheroo.c
@@ -5,6 +5,8 @@
#include <linux/vga_switcheroo.h>
+#include <drm/drm_print.h>
+
#include "display/intel_display_device.h"
#include "i915_driver.h"
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 622c66666935..70e0d8615160 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -30,6 +30,8 @@
#include <linux/stat.h>
#include <linux/sysfs.h>
+#include <drm/drm_print.h>
+
#include "gt/intel_gt_regs.h"
#include "gt/intel_rc6.h"
#include "gt/intel_rps.h"
diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
index 942345548bc3..d5c6e6605086 100644
--- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
+++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
@@ -5,11 +5,11 @@
#include <linux/slab.h>
+#include <drm/drm_buddy.h>
+#include <drm/drm_print.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_bo.h>
-#include <drm/drm_buddy.h>
-
#include "i915_ttm_buddy_manager.h"
#include "i915_gem.h"
diff --git a/drivers/gpu/drm/i915/i915_utils.c b/drivers/gpu/drm/i915/i915_utils.c
index 49f7ed413132..89b920ccbccb 100644
--- a/drivers/gpu/drm/i915/i915_utils.c
+++ b/drivers/gpu/drm/i915/i915_utils.c
@@ -6,6 +6,7 @@
#include <linux/device.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "i915_drv.h"
#include "i915_reg.h"
diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index a0c892e4c40d..4f75115b87d6 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -38,8 +38,10 @@
struct drm_i915_private;
+#ifndef MISSING_CASE
#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \
__stringify(x), (long)(x))
+#endif
#if IS_ENABLED(CONFIG_DRM_I915_DEBUG)
@@ -65,11 +67,13 @@ bool i915_error_injected(void);
drm_err(&(i915)->drm, fmt, ##__VA_ARGS__); \
})
+#ifndef fetch_and_zero
#define fetch_and_zero(ptr) ({ \
typeof(*ptr) __T = *(ptr); \
*(ptr) = (typeof(*ptr))0; \
__T; \
})
+#endif
/*
* check_user_mbz: Check that a user value exists and is zero
@@ -100,43 +104,6 @@ static inline bool is_power_of_2_u64(u64 n)
return (n != 0 && ((n & (n - 1)) == 0));
}
-static inline unsigned long msecs_to_jiffies_timeout(const unsigned int m)
-{
- unsigned long j = msecs_to_jiffies(m);
-
- return min_t(unsigned long, MAX_JIFFY_OFFSET, j + 1);
-}
-
-/*
- * If you need to wait X milliseconds between events A and B, but event B
- * doesn't happen exactly after event A, you record the timestamp (jiffies) of
- * when event A happened, then just before event B you call this function and
- * pass the timestamp as the first argument, and X as the second argument.
- */
-static inline void
-wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms)
-{
- unsigned long target_jiffies, tmp_jiffies, remaining_jiffies;
-
- /*
- * Don't re-read the value of "jiffies" every time since it may change
- * behind our back and break the math.
- */
- tmp_jiffies = jiffies;
- target_jiffies = timestamp_jiffies +
- msecs_to_jiffies_timeout(to_wait_ms);
-
- if (time_after(target_jiffies, tmp_jiffies)) {
- remaining_jiffies = target_jiffies - tmp_jiffies;
- while (remaining_jiffies)
- remaining_jiffies =
- schedule_timeout_uninterruptible(remaining_jiffies);
- }
-}
-
-#define KHz(x) (1000 * (x))
-#define MHz(x) KHz(1000 * (x))
-
void add_taint_for_CI(struct drm_i915_private *i915, unsigned int taint);
static inline void __add_taint_for_CI(unsigned int taint)
{
diff --git a/drivers/gpu/drm/i915/i915_vgpu.c b/drivers/gpu/drm/i915/i915_vgpu.c
index c97323973f9b..d29a06ea51a5 100644
--- a/drivers/gpu/drm/i915/i915_vgpu.c
+++ b/drivers/gpu/drm/i915/i915_vgpu.c
@@ -21,6 +21,8 @@
* SOFTWARE.
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_pvinfo.h"
#include "i915_vgpu.h"
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 25e97031d76e..2c0a63664e13 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -24,7 +24,9 @@
#include <linux/sched/mm.h>
#include <linux/dma-fence-array.h>
+
#include <drm/drm_gem.h>
+#include <drm/drm_print.h>
#include "display/intel_fb.h"
#include "display/intel_frontbuffer.h"
@@ -1595,8 +1597,20 @@ err_unlock:
err_vma_res:
i915_vma_resource_free(vma_res);
err_fence:
- if (work)
- dma_fence_work_commit_imm(&work->base);
+ if (work) {
+ /*
+ * When pinning VMA to GGTT on CHV or BXT with VTD enabled,
+ * commit VMA binding asynchronously to avoid risk of lock
+ * inversion among reservation_ww locks held here and
+ * cpu_hotplug_lock acquired from stop_machine(), which we
+ * wrap around GGTT updates when running in those environments.
+ */
+ if (i915_vma_is_ggtt(vma) &&
+ intel_vm_no_concurrent_access_wa(vma->vm->i915))
+ dma_fence_work_commit(&work->base);
+ else
+ dma_fence_work_commit_imm(&work->base);
+ }
err_rpm:
intel_runtime_pm_put(&vma->vm->i915->runtime_pm, wakeref);
@@ -1990,13 +2004,13 @@ int _i915_vma_move_to_active(struct i915_vma *vma,
}
if (flags & EXEC_OBJECT_WRITE) {
- struct intel_frontbuffer *front;
+ struct i915_frontbuffer *front;
- front = i915_gem_object_get_frontbuffer(obj);
+ front = i915_gem_object_frontbuffer_lookup(obj);
if (unlikely(front)) {
- if (intel_frontbuffer_invalidate(front, ORIGIN_CS))
+ if (intel_frontbuffer_invalidate(&front->base, ORIGIN_CS))
i915_active_add_request(&front->write, rq);
- intel_frontbuffer_put(front);
+ i915_gem_object_frontbuffer_put(front);
}
}
diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c
index 467740969431..175a240ac848 100644
--- a/drivers/gpu/drm/i915/intel_clock_gating.c
+++ b/drivers/gpu/drm/i915/intel_clock_gating.c
@@ -25,6 +25,8 @@
*
*/
+#include <drm/drm_print.h>
+
#include "display/i9xx_plane_regs.h"
#include "display/intel_display.h"
#include "display/intel_display_core.h"
diff --git a/drivers/gpu/drm/i915/intel_gvt.c b/drivers/gpu/drm/i915/intel_gvt.c
index dae9dce7d1b3..c3efc3454ec2 100644
--- a/drivers/gpu/drm/i915/intel_gvt.c
+++ b/drivers/gpu/drm/i915/intel_gvt.c
@@ -21,6 +21,8 @@
* SOFTWARE.
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_vgpu.h"
#include "intel_gvt.h"
diff --git a/drivers/gpu/drm/i915/intel_memory_region.c b/drivers/gpu/drm/i915/intel_memory_region.c
index 59bd603e6deb..ce722f20cab1 100644
--- a/drivers/gpu/drm/i915/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/intel_memory_region.c
@@ -5,6 +5,7 @@
#include <linux/prandom.h>
+#include <drm/drm_print.h>
#include <uapi/drm/i915_drm.h>
#include "intel_memory_region.h"
diff --git a/drivers/gpu/drm/i915/intel_pcode.c b/drivers/gpu/drm/i915/intel_pcode.c
index 55ffedad2490..756652b8ec97 100644
--- a/drivers/gpu/drm/i915/intel_pcode.c
+++ b/drivers/gpu/drm/i915/intel_pcode.c
@@ -3,6 +3,8 @@
* Copyright © 2013-2021 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_reg.h"
#include "i915_wait_util.h"
diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c
index 04525d92bec5..47a69aad5c3f 100644
--- a/drivers/gpu/drm/i915/intel_region_ttm.c
+++ b/drivers/gpu/drm/i915/intel_region_ttm.c
@@ -34,7 +34,7 @@ int intel_region_ttm_device_init(struct drm_i915_private *dev_priv)
return ttm_device_init(&dev_priv->bdev, i915_ttm_driver(),
drm->dev, drm->anon_inode->i_mapping,
- drm->vma_offset_manager, false, false);
+ drm->vma_offset_manager, 0);
}
/**
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index 7ce3e6de0c19..d11c2814b787 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -29,6 +29,7 @@
#include <linux/pm_runtime.h>
#include <drm/drm_print.h>
+#include <drm/intel/display_parent_interface.h>
#include "i915_drv.h"
#include "i915_trace.h"
@@ -177,6 +178,82 @@ static intel_wakeref_t __intel_runtime_pm_get(struct intel_runtime_pm *rpm,
return track_intel_runtime_pm_wakeref(rpm);
}
+static struct intel_runtime_pm *drm_to_rpm(const struct drm_device *drm)
+{
+ struct drm_i915_private *i915 = to_i915(drm);
+
+ return &i915->runtime_pm;
+}
+
+static struct ref_tracker *i915_display_rpm_get(const struct drm_device *drm)
+{
+ return intel_runtime_pm_get(drm_to_rpm(drm));
+}
+
+static struct ref_tracker *i915_display_rpm_get_raw(const struct drm_device *drm)
+{
+ return intel_runtime_pm_get_raw(drm_to_rpm(drm));
+}
+
+static struct ref_tracker *i915_display_rpm_get_if_in_use(const struct drm_device *drm)
+{
+ return intel_runtime_pm_get_if_in_use(drm_to_rpm(drm));
+}
+
+static struct ref_tracker *i915_display_rpm_get_noresume(const struct drm_device *drm)
+{
+ return intel_runtime_pm_get_noresume(drm_to_rpm(drm));
+}
+
+static void i915_display_rpm_put(const struct drm_device *drm, struct ref_tracker *wakeref)
+{
+ intel_runtime_pm_put(drm_to_rpm(drm), wakeref);
+}
+
+static void i915_display_rpm_put_raw(const struct drm_device *drm, struct ref_tracker *wakeref)
+{
+ intel_runtime_pm_put_raw(drm_to_rpm(drm), wakeref);
+}
+
+static void i915_display_rpm_put_unchecked(const struct drm_device *drm)
+{
+ intel_runtime_pm_put_unchecked(drm_to_rpm(drm));
+}
+
+static bool i915_display_rpm_suspended(const struct drm_device *drm)
+{
+ return intel_runtime_pm_suspended(drm_to_rpm(drm));
+}
+
+static void i915_display_rpm_assert_held(const struct drm_device *drm)
+{
+ assert_rpm_wakelock_held(drm_to_rpm(drm));
+}
+
+static void i915_display_rpm_assert_block(const struct drm_device *drm)
+{
+ disable_rpm_wakeref_asserts(drm_to_rpm(drm));
+}
+
+static void i915_display_rpm_assert_unblock(const struct drm_device *drm)
+{
+ enable_rpm_wakeref_asserts(drm_to_rpm(drm));
+}
+
+const struct intel_display_rpm_interface i915_display_rpm_interface = {
+ .get = i915_display_rpm_get,
+ .get_raw = i915_display_rpm_get_raw,
+ .get_if_in_use = i915_display_rpm_get_if_in_use,
+ .get_noresume = i915_display_rpm_get_noresume,
+ .put = i915_display_rpm_put,
+ .put_raw = i915_display_rpm_put_raw,
+ .put_unchecked = i915_display_rpm_put_unchecked,
+ .suspended = i915_display_rpm_suspended,
+ .assert_held = i915_display_rpm_assert_held,
+ .assert_block = i915_display_rpm_assert_block,
+ .assert_unblock = i915_display_rpm_assert_unblock
+};
+
/**
* intel_runtime_pm_get_raw - grab a raw runtime pm reference
* @rpm: the intel_runtime_pm structure
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.h b/drivers/gpu/drm/i915/intel_runtime_pm.h
index 7428bd8fa67f..ed6c43b17f9a 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.h
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.h
@@ -14,6 +14,7 @@
struct device;
struct drm_i915_private;
struct drm_printer;
+struct intel_display_rpm_interface;
/*
* This struct helps tracking the state needed for runtime PM, which puts the
@@ -226,4 +227,6 @@ static inline void print_intel_runtime_pm_wakeref(struct intel_runtime_pm *rpm,
}
#endif
+extern const struct intel_display_rpm_interface i915_display_rpm_interface;
+
#endif /* __INTEL_RUNTIME_PM_H__ */
diff --git a/drivers/gpu/drm/i915/intel_step.c b/drivers/gpu/drm/i915/intel_step.c
index 285b96fadfd5..60a2af5307fc 100644
--- a/drivers/gpu/drm/i915/intel_step.c
+++ b/drivers/gpu/drm/i915/intel_step.c
@@ -3,6 +3,8 @@
* Copyright © 2020,2021 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "intel_step.h"
diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c
index 8cb59f8d1f4c..4adeb271fcbf 100644
--- a/drivers/gpu/drm/i915/intel_uncore.c
+++ b/drivers/gpu/drm/i915/intel_uncore.c
@@ -24,6 +24,7 @@
#include <linux/pm_runtime.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "display/intel_display_core.h"
#include "gt/intel_engine_regs.h"
@@ -35,6 +36,7 @@
#include "i915_reg.h"
#include "i915_vgpu.h"
#include "i915_wait_util.h"
+#include "i915_mmio_range.h"
#include "intel_uncore_trace.h"
#define FORCEWAKE_ACK_TIMEOUT_MS 50
@@ -999,7 +1001,7 @@ find_fw_domain(struct intel_uncore *uncore, u32 offset)
* scanned for obvious mistakes or typos by the selftests.
*/
-static const struct i915_range gen8_shadowed_regs[] = {
+static const struct i915_mmio_range gen8_shadowed_regs[] = {
{ .start = 0x2030, .end = 0x2030 },
{ .start = 0xA008, .end = 0xA00C },
{ .start = 0x12030, .end = 0x12030 },
@@ -1007,7 +1009,7 @@ static const struct i915_range gen8_shadowed_regs[] = {
{ .start = 0x22030, .end = 0x22030 },
};
-static const struct i915_range gen11_shadowed_regs[] = {
+static const struct i915_mmio_range gen11_shadowed_regs[] = {
{ .start = 0x2030, .end = 0x2030 },
{ .start = 0x2550, .end = 0x2550 },
{ .start = 0xA008, .end = 0xA00C },
@@ -1034,7 +1036,7 @@ static const struct i915_range gen11_shadowed_regs[] = {
{ .start = 0x1D8510, .end = 0x1D8550 },
};
-static const struct i915_range gen12_shadowed_regs[] = {
+static const struct i915_mmio_range gen12_shadowed_regs[] = {
{ .start = 0x2030, .end = 0x2030 },
{ .start = 0x2510, .end = 0x2550 },
{ .start = 0xA008, .end = 0xA00C },
@@ -1078,7 +1080,7 @@ static const struct i915_range gen12_shadowed_regs[] = {
{ .start = 0x1F8510, .end = 0x1F8550 },
};
-static const struct i915_range dg2_shadowed_regs[] = {
+static const struct i915_mmio_range dg2_shadowed_regs[] = {
{ .start = 0x2030, .end = 0x2030 },
{ .start = 0x2510, .end = 0x2550 },
{ .start = 0xA008, .end = 0xA00C },
@@ -1117,7 +1119,7 @@ static const struct i915_range dg2_shadowed_regs[] = {
{ .start = 0x1F8510, .end = 0x1F8550 },
};
-static const struct i915_range mtl_shadowed_regs[] = {
+static const struct i915_mmio_range mtl_shadowed_regs[] = {
{ .start = 0x2030, .end = 0x2030 },
{ .start = 0x2510, .end = 0x2550 },
{ .start = 0xA008, .end = 0xA00C },
@@ -1135,7 +1137,7 @@ static const struct i915_range mtl_shadowed_regs[] = {
{ .start = 0x22510, .end = 0x22550 },
};
-static const struct i915_range xelpmp_shadowed_regs[] = {
+static const struct i915_mmio_range xelpmp_shadowed_regs[] = {
{ .start = 0x1C0030, .end = 0x1C0030 },
{ .start = 0x1C0510, .end = 0x1C0550 },
{ .start = 0x1C8030, .end = 0x1C8030 },
@@ -1156,7 +1158,7 @@ static const struct i915_range xelpmp_shadowed_regs[] = {
{ .start = 0x38CFD4, .end = 0x38CFDC },
};
-static int mmio_range_cmp(u32 key, const struct i915_range *range)
+static int mmio_range_cmp(u32 key, const struct i915_mmio_range *range)
{
if (key < range->start)
return -1;
diff --git a/drivers/gpu/drm/i915/intel_uncore.h b/drivers/gpu/drm/i915/intel_uncore.h
index 6048b99b96cb..fafc2ca9a237 100644
--- a/drivers/gpu/drm/i915/intel_uncore.h
+++ b/drivers/gpu/drm/i915/intel_uncore.h
@@ -123,12 +123,6 @@ struct intel_forcewake_range {
enum forcewake_domains domains;
};
-/* Other register ranges (e.g., shadow tables, MCR tables, etc.) */
-struct i915_range {
- u32 start;
- u32 end;
-};
-
struct intel_uncore {
void __iomem *regs;
@@ -162,7 +156,7 @@ struct intel_uncore {
* Shadowed registers are special cases where we can safely write
* to the register *without* grabbing forcewake.
*/
- const struct i915_range *shadowed_reg_table;
+ const struct i915_mmio_range *shadowed_reg_table;
unsigned int shadowed_reg_table_entries;
struct notifier_block pmic_bus_access_nb;
diff --git a/drivers/gpu/drm/i915/intel_wakeref.c b/drivers/gpu/drm/i915/intel_wakeref.c
index 7fa194de5d35..b1883dccc22a 100644
--- a/drivers/gpu/drm/i915/intel_wakeref.c
+++ b/drivers/gpu/drm/i915/intel_wakeref.c
@@ -6,6 +6,8 @@
#include <linux/wait_bit.h>
+#include <drm/drm_print.h>
+
#include "intel_runtime_pm.h"
#include "intel_wakeref.h"
#include "i915_drv.h"
diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp.c b/drivers/gpu/drm/i915/pxp/intel_pxp.c
index 27d545c4e6a5..d4b0c76f335b 100644
--- a/drivers/gpu/drm/i915/pxp/intel_pxp.c
+++ b/drivers/gpu/drm/i915/pxp/intel_pxp.c
@@ -5,6 +5,8 @@
#include <linux/workqueue.h>
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_context.h"
#include "gt/intel_context.h"
#include "gt/intel_gt.h"
diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c b/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c
index 75df959b0aa0..2763773e627d 100644
--- a/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c
+++ b/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c
@@ -3,6 +3,8 @@
* Copyright(c) 2023 Intel Corporation.
*/
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_internal.h"
#include "gt/intel_context.h"
diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp_huc.c b/drivers/gpu/drm/i915/pxp/intel_pxp_huc.c
index 0e609547bef8..9fc575a3d0d5 100644
--- a/drivers/gpu/drm/i915/pxp/intel_pxp_huc.c
+++ b/drivers/gpu/drm/i915/pxp/intel_pxp_huc.c
@@ -3,6 +3,8 @@
* Copyright(c) 2021-2022, Intel Corporation. All rights reserved.
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "gem/i915_gem_region.h"
diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp_session.c b/drivers/gpu/drm/i915/pxp/intel_pxp_session.c
index 091c86e03d1a..1e63261b620f 100644
--- a/drivers/gpu/drm/i915/pxp/intel_pxp_session.c
+++ b/drivers/gpu/drm/i915/pxp/intel_pxp_session.c
@@ -3,6 +3,8 @@
* Copyright(c) 2020, Intel Corporation. All rights reserved.
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "intel_pxp.h"
diff --git a/drivers/gpu/drm/i915/selftests/i915_active.c b/drivers/gpu/drm/i915/selftests/i915_active.c
index 0d89d70b9c36..36c3a5460221 100644
--- a/drivers/gpu/drm/i915/selftests/i915_active.c
+++ b/drivers/gpu/drm/i915/selftests/i915_active.c
@@ -7,6 +7,8 @@
#include <linux/kref.h>
#include <linux/string_helpers.h>
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_pm.h"
#include "gt/intel_gt.h"
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index 7ab4c4e60264..0a86e4857539 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -1118,6 +1118,10 @@ static int misaligned_case(struct i915_address_space *vm, struct intel_memory_re
goto err_put;
}
+ /* make sure page_sizes_gtt has been populated before use */
+ if (i915_is_ggtt(vm) && intel_vm_no_concurrent_access_wa(vm->i915))
+ i915_vma_wait_for_bind(vma);
+
expected_vma_size = round_up(size, 1 << (ffs(vma->resource->page_sizes_gtt) - 1));
expected_node_size = expected_vma_size;
diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c
index 48cd617247d1..1260601bda1f 100644
--- a/drivers/gpu/drm/i915/selftests/i915_request.c
+++ b/drivers/gpu/drm/i915/selftests/i915_request.c
@@ -26,6 +26,8 @@
#include <linux/prime_numbers.h>
#include <linux/sort.h>
+#include <drm/drm_print.h>
+
#include "gem/i915_gem_internal.h"
#include "gem/i915_gem_pm.h"
#include "gem/selftests/mock_context.h"
diff --git a/drivers/gpu/drm/i915/selftests/i915_selftest.c b/drivers/gpu/drm/i915/selftests/i915_selftest.c
index 9c276c9d0a75..8460f0a70d04 100644
--- a/drivers/gpu/drm/i915/selftests/i915_selftest.c
+++ b/drivers/gpu/drm/i915/selftests/i915_selftest.c
@@ -30,6 +30,7 @@
#include "i915_driver.h"
#include "i915_drv.h"
+#include "i915_jiffies.h"
#include "i915_selftest.h"
#include "i915_wait_util.h"
#include "igt_flush_test.h"
diff --git a/drivers/gpu/drm/i915/selftests/intel_uncore.c b/drivers/gpu/drm/i915/selftests/intel_uncore.c
index 58bcbdcef563..507bf42a1aaf 100644
--- a/drivers/gpu/drm/i915/selftests/intel_uncore.c
+++ b/drivers/gpu/drm/i915/selftests/intel_uncore.c
@@ -64,7 +64,7 @@ static int intel_fw_table_check(const struct intel_forcewake_range *ranges,
static int intel_shadow_table_check(void)
{
struct {
- const struct i915_range *regs;
+ const struct i915_mmio_range *regs;
unsigned int size;
} range_lists[] = {
{ gen8_shadowed_regs, ARRAY_SIZE(gen8_shadowed_regs) },
@@ -74,7 +74,7 @@ static int intel_shadow_table_check(void)
{ mtl_shadowed_regs, ARRAY_SIZE(mtl_shadowed_regs) },
{ xelpmp_shadowed_regs, ARRAY_SIZE(xelpmp_shadowed_regs) },
};
- const struct i915_range *range;
+ const struct i915_mmio_range *range;
unsigned int i, j;
s32 prev;
diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
index fb8751bd5df0..b59626c4994c 100644
--- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c
+++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c
@@ -33,6 +33,7 @@
#include "gt/intel_gt.h"
#include "gt/intel_gt_requests.h"
#include "gt/mock_engine.h"
+#include "i915_driver.h"
#include "intel_memory_region.h"
#include "intel_region_ttm.h"
@@ -183,7 +184,8 @@ struct drm_i915_private *mock_gem_device(void)
/* Set up device info and initial runtime info. */
intel_device_info_driver_create(i915, pdev->device, &mock_info);
- display = intel_display_device_probe(pdev);
+ /* FIXME: Can we run selftests using a mock device without display? */
+ display = intel_display_device_probe(pdev, i915_driver_parent_interface());
if (IS_ERR(display))
goto err_device;
diff --git a/drivers/gpu/drm/i915/soc/intel_dram.c b/drivers/gpu/drm/i915/soc/intel_dram.c
index edffaed8f9a7..3e588762709a 100644
--- a/drivers/gpu/drm/i915/soc/intel_dram.c
+++ b/drivers/gpu/drm/i915/soc/intel_dram.c
@@ -6,6 +6,7 @@
#include <linux/string_helpers.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "../display/intel_display_core.h" /* FIXME */
@@ -335,7 +336,7 @@ static bool
skl_is_16gb_dimm(const struct dram_dimm_info *dimm)
{
/* Convert total Gb to Gb per DRAM device */
- return dimm->size / (intel_dimm_num_devices(dimm) ?: 1) == 16;
+ return dimm->size / (intel_dimm_num_devices(dimm) ?: 1) >= 16;
}
static void
@@ -354,7 +355,7 @@ skl_dram_get_dimm_info(struct drm_i915_private *i915,
}
drm_dbg_kms(&i915->drm,
- "CH%u DIMM %c size: %u Gb, width: X%u, ranks: %u, 16Gb DIMMs: %s\n",
+ "CH%u DIMM %c size: %u Gb, width: X%u, ranks: %u, 16Gb+ DIMMs: %s\n",
channel, dimm_name, dimm->size, dimm->width, dimm->ranks,
str_yes_no(skl_is_16gb_dimm(dimm)));
}
@@ -384,7 +385,7 @@ skl_dram_get_channel_info(struct drm_i915_private *i915,
ch->is_16gb_dimm = skl_is_16gb_dimm(&ch->dimm_l) ||
skl_is_16gb_dimm(&ch->dimm_s);
- drm_dbg_kms(&i915->drm, "CH%u ranks: %u, 16Gb DIMMs: %s\n",
+ drm_dbg_kms(&i915->drm, "CH%u ranks: %u, 16Gb+ DIMMs: %s\n",
channel, ch->ranks, str_yes_no(ch->is_16gb_dimm));
return 0;
@@ -406,7 +407,7 @@ skl_dram_get_channels_info(struct drm_i915_private *i915, struct dram_info *dram
u32 val;
int ret;
- /* Assume 16Gb DIMMs are present until proven otherwise */
+ /* Assume 16Gb+ DIMMs are present until proven otherwise */
dram_info->has_16gb_dimms = true;
val = intel_uncore_read(&i915->uncore,
@@ -438,7 +439,7 @@ skl_dram_get_channels_info(struct drm_i915_private *i915, struct dram_info *dram
drm_dbg_kms(&i915->drm, "Memory configuration is symmetric? %s\n",
str_yes_no(dram_info->symmetric_memory));
- drm_dbg_kms(&i915->drm, "16Gb DIMMs: %s\n",
+ drm_dbg_kms(&i915->drm, "16Gb+ DIMMs: %s\n",
str_yes_no(dram_info->has_16gb_dimms));
return 0;
@@ -685,6 +686,7 @@ static int gen12_get_dram_info(struct drm_i915_private *i915, struct dram_info *
static int xelpdp_get_dram_info(struct drm_i915_private *i915, struct dram_info *dram_info)
{
+ struct intel_display *display = i915->display;
u32 val = intel_uncore_read(&i915->uncore, MTL_MEM_SS_INFO_GLOBAL);
switch (REG_FIELD_GET(MTL_DDR_TYPE_MASK, val)) {
@@ -723,6 +725,9 @@ static int xelpdp_get_dram_info(struct drm_i915_private *i915, struct dram_info
dram_info->num_qgv_points = REG_FIELD_GET(MTL_N_OF_ENABLED_QGV_POINTS_MASK, val);
/* PSF GV points not supported in D14+ */
+ if (DISPLAY_VER(display) >= 35)
+ dram_info->ecc_impacting_de_bw = REG_FIELD_GET(XE3P_ECC_IMPACTING_DE, val);
+
return 0;
}
diff --git a/drivers/gpu/drm/i915/soc/intel_dram.h b/drivers/gpu/drm/i915/soc/intel_dram.h
index 03a973f1c941..8475ee379daa 100644
--- a/drivers/gpu/drm/i915/soc/intel_dram.h
+++ b/drivers/gpu/drm/i915/soc/intel_dram.h
@@ -30,6 +30,7 @@ struct dram_info {
u8 num_channels;
u8 num_qgv_points;
u8 num_psf_gv_points;
+ bool ecc_impacting_de_bw; /* Only valid from Xe3p_LPD onward. */
bool symmetric_memory;
bool has_16gb_dimms;
};
diff --git a/drivers/gpu/drm/i915/soc/intel_gmch.c b/drivers/gpu/drm/i915/soc/intel_gmch.c
index f210c9655b53..271da30c8290 100644
--- a/drivers/gpu/drm/i915/soc/intel_gmch.c
+++ b/drivers/gpu/drm/i915/soc/intel_gmch.c
@@ -8,6 +8,7 @@
#include <linux/vgaarb.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/intel/i915_drm.h>
#include "../display/intel_display_core.h" /* FIXME */
diff --git a/drivers/gpu/drm/i915/soc/intel_rom.c b/drivers/gpu/drm/i915/soc/intel_rom.c
index 243d98cab8c3..2f17dc856e7f 100644
--- a/drivers/gpu/drm/i915/soc/intel_rom.c
+++ b/drivers/gpu/drm/i915/soc/intel_rom.c
@@ -39,8 +39,9 @@ static u16 spi_read16(struct intel_rom *rom, loff_t offset)
return spi_read32(rom, offset) & 0xffff;
}
-struct intel_rom *intel_rom_spi(struct drm_i915_private *i915)
+struct intel_rom *intel_rom_spi(struct drm_device *drm)
{
+ struct drm_i915_private *i915 = to_i915(drm);
struct intel_rom *rom;
u32 static_region;
@@ -85,7 +86,7 @@ static void pci_free(struct intel_rom *rom)
pci_unmap_rom(rom->pdev, rom->oprom);
}
-struct intel_rom *intel_rom_pci(struct drm_i915_private *i915)
+struct intel_rom *intel_rom_pci(struct drm_device *drm)
{
struct intel_rom *rom;
@@ -93,7 +94,7 @@ struct intel_rom *intel_rom_pci(struct drm_i915_private *i915)
if (!rom)
return NULL;
- rom->pdev = to_pci_dev(i915->drm.dev);
+ rom->pdev = to_pci_dev(drm->dev);
rom->oprom = pci_map_rom(rom->pdev, &rom->size);
if (!rom->oprom) {
diff --git a/drivers/gpu/drm/i915/soc/intel_rom.h b/drivers/gpu/drm/i915/soc/intel_rom.h
index fb2979c8ef7f..4e59a375787e 100644
--- a/drivers/gpu/drm/i915/soc/intel_rom.h
+++ b/drivers/gpu/drm/i915/soc/intel_rom.h
@@ -8,11 +8,11 @@
#include <linux/types.h>
-struct drm_i915_private;
+struct drm_device;
struct intel_rom;
-struct intel_rom *intel_rom_spi(struct drm_i915_private *i915);
-struct intel_rom *intel_rom_pci(struct drm_i915_private *i915);
+struct intel_rom *intel_rom_spi(struct drm_device *drm);
+struct intel_rom *intel_rom_pci(struct drm_device *drm);
u32 intel_rom_read32(struct intel_rom *rom, loff_t offset);
u16 intel_rom_read16(struct intel_rom *rom, loff_t offset);
diff --git a/drivers/gpu/drm/i915/vlv_iosf_sb.c b/drivers/gpu/drm/i915/vlv_iosf_sb.c
index f4b386933141..38a75651b0dc 100644
--- a/drivers/gpu/drm/i915/vlv_iosf_sb.c
+++ b/drivers/gpu/drm/i915/vlv_iosf_sb.c
@@ -3,6 +3,8 @@
* Copyright © 2013-2021 Intel Corporation
*/
+#include <drm/drm_print.h>
+
#include "i915_drv.h"
#include "i915_iosf_mbi.h"
#include "i915_reg.h"
diff --git a/drivers/gpu/drm/imagination/Kconfig b/drivers/gpu/drm/imagination/Kconfig
index 682dd2633d0c..0482bfcefdde 100644
--- a/drivers/gpu/drm/imagination/Kconfig
+++ b/drivers/gpu/drm/imagination/Kconfig
@@ -7,6 +7,7 @@ config DRM_POWERVR
depends on DRM
depends on MMU
depends on PM
+ depends on POWER_SEQUENCING || !POWER_SEQUENCING
select DRM_EXEC
select DRM_GEM_SHMEM_HELPER
select DRM_SCHED
diff --git a/drivers/gpu/drm/imagination/pvr_ccb.c b/drivers/gpu/drm/imagination/pvr_ccb.c
index 2bbdc05a3b97..9294b4ba1de7 100644
--- a/drivers/gpu/drm/imagination/pvr_ccb.c
+++ b/drivers/gpu/drm/imagination/pvr_ccb.c
@@ -10,6 +10,7 @@
#include "pvr_power.h"
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c
index 294b6019b415..78d6b8a0a450 100644
--- a/drivers/gpu/drm/imagination/pvr_device.c
+++ b/drivers/gpu/drm/imagination/pvr_device.c
@@ -48,7 +48,7 @@
*
* Return:
* * 0 on success, or
- * * Any error returned by devm_platform_ioremap_resource().
+ * * Any error returned by devm_platform_get_and_ioremap_resource().
*/
static int
pvr_device_reg_init(struct pvr_device *pvr_dev)
diff --git a/drivers/gpu/drm/imagination/pvr_device.h b/drivers/gpu/drm/imagination/pvr_device.h
index ab8f56ae15df..ec53ff275541 100644
--- a/drivers/gpu/drm/imagination/pvr_device.h
+++ b/drivers/gpu/drm/imagination/pvr_device.h
@@ -146,6 +146,14 @@ struct pvr_device {
*/
struct clk *mem_clk;
+ /**
+ * @power: Optional power domain devices.
+ *
+ * On platforms with more than one power domain for the GPU, they are
+ * stored here in @domain_devs, along with links between them in
+ * @domain_links. The size of @domain_devs is given by @domain_count,
+ * while the size of @domain_links is (2 * @domain_count) - 1.
+ */
struct pvr_device_power {
struct device **domain_devs;
struct device_link **domain_links;
diff --git a/drivers/gpu/drm/imagination/pvr_fw.c b/drivers/gpu/drm/imagination/pvr_fw.c
index b2f8cba77346..779a58fe6ee8 100644
--- a/drivers/gpu/drm/imagination/pvr_fw.c
+++ b/drivers/gpu/drm/imagination/pvr_fw.c
@@ -17,6 +17,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include <drm/drm_mm.h>
+#include <drm/drm_print.h>
#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/math.h>
diff --git a/drivers/gpu/drm/imagination/pvr_fw_meta.c b/drivers/gpu/drm/imagination/pvr_fw_meta.c
index 60db3668ad3c..9ff03bc60a08 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_meta.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_meta.c
@@ -16,6 +16,8 @@
#include <linux/ktime.h>
#include <linux/types.h>
+#include <drm/drm_print.h>
+
#define ROGUE_FW_HEAP_META_SHIFT 25 /* 32 MB */
#define POLL_TIMEOUT_USEC 1000000
diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c
index a1098b521485..8a56952f6730 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_trace.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c
@@ -9,6 +9,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include <linux/build_bug.h>
#include <linux/dcache.h>
diff --git a/drivers/gpu/drm/imagination/pvr_power.c b/drivers/gpu/drm/imagination/pvr_power.c
index c6e7ff9e935d..b9f801c63260 100644
--- a/drivers/gpu/drm/imagination/pvr_power.c
+++ b/drivers/gpu/drm/imagination/pvr_power.c
@@ -10,6 +10,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c
index 3d97990170bf..48e52c5561be 100644
--- a/drivers/gpu/drm/imagination/pvr_vm.c
+++ b/drivers/gpu/drm/imagination/pvr_vm.c
@@ -13,6 +13,7 @@
#include <drm/drm_exec.h>
#include <drm/drm_gem.h>
#include <drm/drm_gpuvm.h>
+#include <drm/drm_print.h>
#include <linux/bug.h>
#include <linux/container_of.h>
diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c
index 86ecc22d0a55..d42f33d6f3fc 100644
--- a/drivers/gpu/drm/imx/dc/dc-ed.c
+++ b/drivers/gpu/drm/imx/dc/dc-ed.c
@@ -15,12 +15,12 @@
#include "dc-pe.h"
#define PIXENGCFG_STATIC 0x8
-#define POWERDOWN BIT(4)
-#define SYNC_MODE BIT(8)
-#define SINGLE 0
#define DIV_MASK GENMASK(23, 16)
#define DIV(x) FIELD_PREP(DIV_MASK, (x))
#define DIV_RESET 0x80
+#define SYNC_MODE BIT(8)
+#define SINGLE 0
+#define POWERDOWN BIT(4)
#define PIXENGCFG_DYNAMIC 0xc
@@ -28,9 +28,9 @@
#define SYNC_TRIGGER BIT(0)
#define STATICCONTROL 0x8
+#define PERFCOUNTMODE BIT(12)
#define KICK_MODE BIT(8)
#define EXTERNAL BIT(8)
-#define PERFCOUNTMODE BIT(12)
#define CONTROL 0xc
#define GAMMAAPPLYENABLE BIT(0)
diff --git a/drivers/gpu/drm/imx/dc/dc-fg.c b/drivers/gpu/drm/imx/dc/dc-fg.c
index 7f6c1852bf72..28f372be9247 100644
--- a/drivers/gpu/drm/imx/dc/dc-fg.c
+++ b/drivers/gpu/drm/imx/dc/dc-fg.c
@@ -56,9 +56,9 @@
#define FGINCTRL 0x5c
#define FGINCTRLPANIC 0x60
-#define FGDM_MASK GENMASK(2, 0)
-#define ENPRIMALPHA BIT(3)
#define ENSECALPHA BIT(4)
+#define ENPRIMALPHA BIT(3)
+#define FGDM_MASK GENMASK(2, 0)
#define FGCCR 0x64
#define CCGREEN(x) FIELD_PREP(GENMASK(19, 10), (x))
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.c b/drivers/gpu/drm/imx/dc/dc-fu.c
index f94c591c8158..1d8f74babef8 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.c
+++ b/drivers/gpu/drm/imx/dc/dc-fu.c
@@ -18,11 +18,11 @@
#define BASEADDRESSAUTOUPDATE(x) FIELD_PREP(BASEADDRESSAUTOUPDATE_MASK, (x))
/* BURSTBUFFERMANAGEMENT */
+#define LINEMODE_MASK BIT(31)
#define SETBURSTLENGTH_MASK GENMASK(12, 8)
#define SETBURSTLENGTH(x) FIELD_PREP(SETBURSTLENGTH_MASK, (x))
#define SETNUMBUFFERS_MASK GENMASK(7, 0)
#define SETNUMBUFFERS(x) FIELD_PREP(SETNUMBUFFERS_MASK, (x))
-#define LINEMODE_MASK BIT(31)
/* SOURCEBUFFERATTRIBUTES */
#define BITSPERPIXEL_MASK GENMASK(21, 16)
@@ -31,20 +31,20 @@
#define STRIDE(x) FIELD_PREP(STRIDE_MASK, (x) - 1)
/* SOURCEBUFFERDIMENSION */
-#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
#define LINECOUNT(x) FIELD_PREP(GENMASK(29, 16), (x))
+#define LINEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
/* LAYEROFFSET */
-#define LAYERXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
#define LAYERYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x))
+#define LAYERXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
/* CLIPWINDOWOFFSET */
-#define CLIPWINDOWXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
#define CLIPWINDOWYOFFSET(x) FIELD_PREP(GENMASK(30, 16), (x))
+#define CLIPWINDOWXOFFSET(x) FIELD_PREP(GENMASK(14, 0), (x))
/* CLIPWINDOWDIMENSIONS */
-#define CLIPWINDOWWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1)
#define CLIPWINDOWHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x) - 1)
+#define CLIPWINDOWWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x) - 1)
enum dc_linemode {
/*
diff --git a/drivers/gpu/drm/imx/dc/dc-fu.h b/drivers/gpu/drm/imx/dc/dc-fu.h
index e016e1ea5b4e..f678de3ca8c0 100644
--- a/drivers/gpu/drm/imx/dc/dc-fu.h
+++ b/drivers/gpu/drm/imx/dc/dc-fu.h
@@ -33,13 +33,13 @@
#define A_SHIFT(x) FIELD_PREP_CONST(GENMASK(4, 0), (x))
/* LAYERPROPERTY */
+#define SOURCEBUFFERENABLE BIT(31)
#define YUVCONVERSIONMODE_MASK GENMASK(18, 17)
#define YUVCONVERSIONMODE(x) FIELD_PREP(YUVCONVERSIONMODE_MASK, (x))
-#define SOURCEBUFFERENABLE BIT(31)
/* FRAMEDIMENSIONS */
-#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
#define FRAMEHEIGHT(x) FIELD_PREP(GENMASK(29, 16), (x))
+#define FRAMEWIDTH(x) FIELD_PREP(GENMASK(13, 0), (x))
/* CONTROL */
#define INPUTSELECT_MASK GENMASK(4, 3)
diff --git a/drivers/gpu/drm/imx/dc/dc-lb.c b/drivers/gpu/drm/imx/dc/dc-lb.c
index 38f966625d38..ca1d714c8d6e 100644
--- a/drivers/gpu/drm/imx/dc/dc-lb.c
+++ b/drivers/gpu/drm/imx/dc/dc-lb.c
@@ -17,12 +17,12 @@
#include "dc-pe.h"
#define PIXENGCFG_DYNAMIC 0x8
-#define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0)
-#define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \
- FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x))
#define PIXENGCFG_DYNAMIC_SEC_SEL_MASK GENMASK(13, 8)
#define PIXENGCFG_DYNAMIC_SEC_SEL(x) \
FIELD_PREP(PIXENGCFG_DYNAMIC_SEC_SEL_MASK, (x))
+#define PIXENGCFG_DYNAMIC_PRIM_SEL_MASK GENMASK(5, 0)
+#define PIXENGCFG_DYNAMIC_PRIM_SEL(x) \
+ FIELD_PREP(PIXENGCFG_DYNAMIC_PRIM_SEL_MASK, (x))
#define STATICCONTROL 0x8
#define SHDTOKSEL_MASK GENMASK(4, 3)
@@ -37,24 +37,24 @@
#define BLENDCONTROL 0x10
#define ALPHA_MASK GENMASK(23, 16)
#define ALPHA(x) FIELD_PREP(ALPHA_MASK, (x))
-#define PRIM_C_BLD_FUNC_MASK GENMASK(2, 0)
-#define PRIM_C_BLD_FUNC(x) \
- FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x))
-#define SEC_C_BLD_FUNC_MASK GENMASK(6, 4)
-#define SEC_C_BLD_FUNC(x) \
- FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x))
-#define PRIM_A_BLD_FUNC_MASK GENMASK(10, 8)
-#define PRIM_A_BLD_FUNC(x) \
- FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x))
#define SEC_A_BLD_FUNC_MASK GENMASK(14, 12)
#define SEC_A_BLD_FUNC(x) \
FIELD_PREP(SEC_A_BLD_FUNC_MASK, (x))
+#define PRIM_A_BLD_FUNC_MASK GENMASK(10, 8)
+#define PRIM_A_BLD_FUNC(x) \
+ FIELD_PREP(PRIM_A_BLD_FUNC_MASK, (x))
+#define SEC_C_BLD_FUNC_MASK GENMASK(6, 4)
+#define SEC_C_BLD_FUNC(x) \
+ FIELD_PREP(SEC_C_BLD_FUNC_MASK, (x))
+#define PRIM_C_BLD_FUNC_MASK GENMASK(2, 0)
+#define PRIM_C_BLD_FUNC(x) \
+ FIELD_PREP(PRIM_C_BLD_FUNC_MASK, (x))
#define POSITION 0x14
-#define XPOS_MASK GENMASK(15, 0)
-#define XPOS(x) FIELD_PREP(XPOS_MASK, (x))
#define YPOS_MASK GENMASK(31, 16)
#define YPOS(x) FIELD_PREP(YPOS_MASK, (x))
+#define XPOS_MASK GENMASK(15, 0)
+#define XPOS(x) FIELD_PREP(XPOS_MASK, (x))
enum dc_lb_blend_func {
DC_LAYERBLEND_BLEND_ZERO,
diff --git a/drivers/gpu/drm/imx/dc/dc-plane.c b/drivers/gpu/drm/imx/dc/dc-plane.c
index d8b946fb90de..e40d5d66c5c1 100644
--- a/drivers/gpu/drm/imx/dc/dc-plane.c
+++ b/drivers/gpu/drm/imx/dc/dc-plane.c
@@ -106,7 +106,7 @@ dc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state)
}
crtc_state =
- drm_atomic_get_existing_crtc_state(state, plane_state->crtc);
+ drm_atomic_get_new_crtc_state(state, plane_state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
diff --git a/drivers/gpu/drm/imx/dcss/dcss-plane.c b/drivers/gpu/drm/imx/dcss/dcss-plane.c
index ab6d32bad756..0b99b407ac0a 100644
--- a/drivers/gpu/drm/imx/dcss/dcss-plane.c
+++ b/drivers/gpu/drm/imx/dcss/dcss-plane.c
@@ -10,6 +10,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "dcss-dev.h"
#include "dcss-kms.h"
@@ -159,8 +160,8 @@ static int dcss_plane_atomic_check(struct drm_plane *plane,
dma_obj = drm_fb_dma_get_gem_obj(fb, 0);
WARN_ON(!dma_obj);
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- new_plane_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state,
+ new_plane_state->crtc);
hdisplay = crtc_state->adjusted_mode.hdisplay;
vdisplay = crtc_state->adjusted_mode.vdisplay;
diff --git a/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c
index 8333c4bf7369..07e5f96202d4 100644
--- a/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c
@@ -278,4 +278,3 @@ MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:dwhdmi-imx");
diff --git a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c
index ec5fd9a01f1e..eddb471119c6 100644
--- a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c
@@ -17,7 +17,9 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_fbdev_dma.h>
+#include <drm/drm_fourcc.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
@@ -141,17 +143,34 @@ static int imx_drm_dumb_create(struct drm_file *file_priv,
struct drm_device *drm,
struct drm_mode_create_dumb *args)
{
- u32 width = args->width;
+ u32 fourcc;
+ u64 pitch_align;
int ret;
- args->width = ALIGN(width, 8);
-
- ret = drm_gem_dma_dumb_create(file_priv, drm, args);
+ /*
+ * Hardware requires the framebuffer width to be aligned to
+ * multiples of 8. The mode-setting code handles this, but
+ * the buffer pitch has to be aligned as well. Set the pitch
+ * alignment accordingly, so that the each scanline fits into
+ * the allocated buffer.
+ */
+ fourcc = drm_driver_color_mode_format(drm, args->bpp);
+ if (fourcc != DRM_FORMAT_INVALID) {
+ const struct drm_format_info *info = drm_format_info(fourcc);
+
+ if (!info)
+ return -EINVAL;
+ pitch_align = drm_format_info_min_pitch(info, 0, 8);
+ } else {
+ pitch_align = DIV_ROUND_UP(args->bpp, SZ_8) * 8;
+ }
+ if (!pitch_align || pitch_align > U32_MAX)
+ return -EINVAL;
+ ret = drm_mode_size_dumb(drm, args, pitch_align, 0);
if (ret)
return ret;
- args->width = width;
- return ret;
+ return drm_gem_dma_dumb_create(file_priv, drm, args);
}
static const struct drm_driver imx_drm_driver = {
diff --git a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c
index 6be7a57ad03d..626d410d9150 100644
--- a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c
+++ b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c
@@ -644,4 +644,3 @@ module_platform_driver(imx_ldb_driver);
MODULE_DESCRIPTION("i.MX LVDS driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/imx/ipuv3/imx-tve.c b/drivers/gpu/drm/imx/ipuv3/imx-tve.c
index c5629e155d25..c5c6e070cc06 100644
--- a/drivers/gpu/drm/imx/ipuv3/imx-tve.c
+++ b/drivers/gpu/drm/imx/ipuv3/imx-tve.c
@@ -368,17 +368,20 @@ static unsigned long clk_tve_di_recalc_rate(struct clk_hw *hw,
return 0;
}
-static long clk_tve_di_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int clk_tve_di_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
unsigned long div;
- div = *prate / rate;
+ div = req->best_parent_rate / req->rate;
if (div >= 4)
- return *prate / 4;
+ req->rate = req->best_parent_rate / 4;
else if (div >= 2)
- return *prate / 2;
- return *prate;
+ req->rate = req->best_parent_rate / 2;
+ else
+ req->rate = req->best_parent_rate;
+
+ return 0;
}
static int clk_tve_di_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -409,7 +412,7 @@ static int clk_tve_di_set_rate(struct clk_hw *hw, unsigned long rate,
}
static const struct clk_ops clk_tve_di_ops = {
- .round_rate = clk_tve_di_round_rate,
+ .determine_rate = clk_tve_di_determine_rate,
.set_rate = clk_tve_di_set_rate,
.recalc_rate = clk_tve_di_recalc_rate,
};
@@ -674,4 +677,3 @@ module_platform_driver(imx_tve_driver);
MODULE_DESCRIPTION("i.MX Television Encoder driver");
MODULE_AUTHOR("Philipp Zabel, Pengutronix");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx-tve");
diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c
index 704c549750f9..db50eccea0ca 100644
--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c
+++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c
@@ -14,6 +14,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <video/imx-ipu-v3.h>
@@ -386,8 +387,7 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
return -EINVAL;
crtc_state =
- drm_atomic_get_existing_crtc_state(state,
- new_state->crtc);
+ drm_atomic_get_new_crtc_state(state, new_state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
index 6d8325c76697..6fbf505d2801 100644
--- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c
+++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
@@ -25,19 +25,18 @@
struct imx_parallel_display_encoder {
struct drm_encoder encoder;
- struct drm_bridge bridge;
- struct imx_parallel_display *pd;
};
struct imx_parallel_display {
struct device *dev;
u32 bus_format;
struct drm_bridge *next_bridge;
+ struct drm_bridge bridge;
};
static inline struct imx_parallel_display *bridge_to_imxpd(struct drm_bridge *b)
{
- return container_of(b, struct imx_parallel_display_encoder, bridge)->pd;
+ return container_of(b, struct imx_parallel_display, bridge);
}
static const u32 imx_pd_bus_fmts[] = {
@@ -134,10 +133,10 @@ static int imx_pd_bridge_atomic_check(struct drm_bridge *bridge,
struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
struct drm_display_info *di = &conn_state->connector->display_info;
struct drm_bridge_state *next_bridge_state = NULL;
- struct drm_bridge *next_bridge;
u32 bus_flags, bus_fmt;
- next_bridge = drm_bridge_get_next_bridge(bridge);
+ struct drm_bridge *next_bridge __free(drm_bridge_put) = drm_bridge_get_next_bridge(bridge);
+
if (next_bridge)
next_bridge_state = drm_atomic_get_new_bridge_state(crtc_state->state,
next_bridge);
@@ -195,15 +194,13 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(imxpd_encoder))
return PTR_ERR(imxpd_encoder);
- imxpd_encoder->pd = imxpd;
encoder = &imxpd_encoder->encoder;
- bridge = &imxpd_encoder->bridge;
+ bridge = &imxpd->bridge;
ret = imx_drm_encoder_parse_of(drm, encoder, imxpd->dev->of_node);
if (ret)
return ret;
- bridge->funcs = &imx_pd_bridge_funcs;
drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
connector = drm_bridge_connector_init(drm, encoder);
@@ -228,9 +225,10 @@ static int imx_pd_probe(struct platform_device *pdev)
u32 bus_format = 0;
const char *fmt;
- imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
- if (!imxpd)
- return -ENOMEM;
+ imxpd = devm_drm_bridge_alloc(dev, struct imx_parallel_display, bridge,
+ &imx_pd_bridge_funcs);
+ if (IS_ERR(imxpd))
+ return PTR_ERR(imxpd);
/* port@1 is the output port */
imxpd->next_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
@@ -258,6 +256,8 @@ static int imx_pd_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, imxpd);
+ devm_drm_bridge_add(dev, &imxpd->bridge);
+
return component_add(dev, &imx_pd_ops);
}
@@ -286,4 +286,3 @@ module_platform_driver(imx_pd_driver);
MODULE_DESCRIPTION("i.MX parallel display driver");
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx-parallel-display");
diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
index 8d6a0bb31c48..e200b40f30fe 100644
--- a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
+++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c
@@ -14,6 +14,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 9db1ceaed518..d3213fbf22be 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -247,8 +247,8 @@ static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc,
struct ingenic_drm_private_state *priv_state;
unsigned int next_id;
- priv_state = ingenic_drm_get_priv_state(priv, state);
- if (WARN_ON(IS_ERR(priv_state)))
+ priv_state = ingenic_drm_get_new_priv_state(priv, state);
+ if (WARN_ON(!priv_state))
return;
/* Set addresses of our DMA descriptor chains */
@@ -340,6 +340,7 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
crtc);
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL;
+ struct ingenic_drm_private_state *priv_state;
if (crtc_state->gamma_lut &&
drm_color_lut_size(crtc_state->gamma_lut) != ARRAY_SIZE(priv->dma_hwdescs->palette)) {
@@ -347,6 +348,11 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc,
return -EINVAL;
}
+ /* We will need the state in atomic_enable, so let's make sure it's part of the state */
+ priv_state = ingenic_drm_get_priv_state(priv, state);
+ if (IS_ERR(priv_state))
+ return PTR_ERR(priv_state);
+
if (drm_atomic_crtc_needs_modeset(crtc_state) && priv->soc_info->has_osd) {
f1_state = drm_atomic_get_plane_state(crtc_state->state,
&priv->f1);
@@ -471,8 +477,7 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane *plane,
if (priv->soc_info->plane_f0_not_working && plane == &priv->f0)
return -EINVAL;
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c
index 26ebf424d63e..32638a713241 100644
--- a/drivers/gpu/drm/ingenic/ingenic-ipu.c
+++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c
@@ -580,7 +580,7 @@ static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane,
if (!crtc)
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
@@ -705,7 +705,7 @@ ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane,
ipu->sharpness = val;
if (state->crtc) {
- crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
diff --git a/drivers/gpu/drm/kmb/kmb_drv.c b/drivers/gpu/drm/kmb/kmb_drv.c
index 32cda134ae3e..7c2eb1152fc2 100644
--- a/drivers/gpu/drm/kmb/kmb_drv.c
+++ b/drivers/gpu/drm/kmb/kmb_drv.c
@@ -20,6 +20,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/kmb/kmb_plane.c b/drivers/gpu/drm/kmb/kmb_plane.c
index 9e0562aa2bcb..a935ff1503cd 100644
--- a/drivers/gpu/drm/kmb/kmb_plane.c
+++ b/drivers/gpu/drm/kmb/kmb_plane.c
@@ -12,6 +12,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "kmb_drv.h"
#include "kmb_plane.h"
@@ -129,8 +130,7 @@ static int kmb_plane_atomic_check(struct drm_plane *plane,
}
can_position = (plane->type == DRM_PLANE_TYPE_OVERLAY);
crtc_state =
- drm_atomic_get_existing_crtc_state(state,
- new_plane_state->crtc);
+ drm_atomic_get_new_crtc_state(state, new_plane_state->crtc);
return drm_atomic_helper_check_plane_state(new_plane_state,
crtc_state,
DRM_PLANE_NO_SCALING,
diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c
index 739e8c6c6d90..9a1e6b9ecbe5 100644
--- a/drivers/gpu/drm/lima/lima_sched.c
+++ b/drivers/gpu/drm/lima/lima_sched.c
@@ -8,6 +8,8 @@
#include <linux/vmalloc.h>
#include <linux/pm_runtime.h>
+#include <drm/drm_print.h>
+
#include "lima_devfreq.h"
#include "lima_drv.h"
#include "lima_sched.h"
diff --git a/drivers/gpu/drm/logicvc/logicvc_layer.c b/drivers/gpu/drm/logicvc/logicvc_layer.c
index 464000aea765..eab4d773f92b 100644
--- a/drivers/gpu/drm/logicvc/logicvc_layer.c
+++ b/drivers/gpu/drm/logicvc/logicvc_layer.c
@@ -96,8 +96,8 @@ static int logicvc_plane_atomic_check(struct drm_plane *drm_plane,
if (!new_state->crtc)
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(new_state->state,
- new_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(new_state->state,
+ new_state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
diff --git a/drivers/gpu/drm/loongson/lsdc_benchmark.c b/drivers/gpu/drm/loongson/lsdc_benchmark.c
index b088646a2ff9..659173381814 100644
--- a/drivers/gpu/drm/loongson/lsdc_benchmark.c
+++ b/drivers/gpu/drm/loongson/lsdc_benchmark.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include "lsdc_benchmark.h"
#include "lsdc_drv.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_crtc.c b/drivers/gpu/drm/loongson/lsdc_crtc.c
index 03958b79f251..a5b7d5c5fd20 100644
--- a/drivers/gpu/drm/loongson/lsdc_crtc.c
+++ b/drivers/gpu/drm/loongson/lsdc_crtc.c
@@ -9,6 +9,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "lsdc_drv.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_debugfs.c b/drivers/gpu/drm/loongson/lsdc_debugfs.c
index b9c2e6b1701f..19aa7ef577de 100644
--- a/drivers/gpu/drm/loongson/lsdc_debugfs.c
+++ b/drivers/gpu/drm/loongson/lsdc_debugfs.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include "lsdc_benchmark.h"
#include "lsdc_drv.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c
index 12193d2a301a..abf5bf68eec2 100644
--- a/drivers/gpu/drm/loongson/lsdc_drv.c
+++ b/drivers/gpu/drm/loongson/lsdc_drv.c
@@ -15,6 +15,7 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/loongson/lsdc_gem.c b/drivers/gpu/drm/loongson/lsdc_gem.c
index a720d8f53209..6372db2d3093 100644
--- a/drivers/gpu/drm/loongson/lsdc_gem.c
+++ b/drivers/gpu/drm/loongson/lsdc_gem.c
@@ -6,9 +6,11 @@
#include <linux/dma-buf.h>
#include <drm/drm_debugfs.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include "lsdc_drv.h"
#include "lsdc_gem.h"
@@ -57,7 +59,7 @@ static void lsdc_gem_object_free(struct drm_gem_object *obj)
struct ttm_buffer_object *tbo = to_ttm_bo(obj);
if (tbo)
- ttm_bo_put(tbo);
+ ttm_bo_fini(tbo);
}
static int lsdc_gem_object_vmap(struct drm_gem_object *obj, struct iosys_map *map)
@@ -204,45 +206,31 @@ int lsdc_dumb_create(struct drm_file *file, struct drm_device *ddev,
const struct lsdc_desc *descp = ldev->descp;
u32 domain = LSDC_GEM_DOMAIN_VRAM;
struct drm_gem_object *gobj;
- size_t size;
- u32 pitch;
- u32 handle;
int ret;
- if (!args->width || !args->height)
- return -EINVAL;
-
- if (args->bpp != 32 && args->bpp != 16)
- return -EINVAL;
-
- pitch = args->width * args->bpp / 8;
- pitch = ALIGN(pitch, descp->pitch_align);
- size = pitch * args->height;
- size = ALIGN(size, PAGE_SIZE);
+ ret = drm_mode_size_dumb(ddev, args, descp->pitch_align, 0);
+ if (ret)
+ return ret;
/* Maximum single bo size allowed is the half vram size available */
- if (size > ldev->vram_size / 2) {
- drm_err(ddev, "Requesting(%zuMiB) failed\n", size >> 20);
+ if (args->size > ldev->vram_size / 2) {
+ drm_err(ddev, "Requesting(%zuMiB) failed\n", (size_t)(args->size >> PAGE_SHIFT));
return -ENOMEM;
}
- gobj = lsdc_gem_object_create(ddev, domain, size, false, NULL, NULL);
+ gobj = lsdc_gem_object_create(ddev, domain, args->size, false, NULL, NULL);
if (IS_ERR(gobj)) {
drm_err(ddev, "Failed to create gem object\n");
return PTR_ERR(gobj);
}
- ret = drm_gem_handle_create(file, gobj, &handle);
+ ret = drm_gem_handle_create(file, gobj, &args->handle);
/* drop reference from allocate, handle holds it now */
drm_gem_object_put(gobj);
if (ret)
return ret;
- args->pitch = pitch;
- args->size = size;
- args->handle = handle;
-
return 0;
}
diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.c b/drivers/gpu/drm/loongson/lsdc_i2c.c
index ce90c25536d2..012b4761c538 100644
--- a/drivers/gpu/drm/loongson/lsdc_i2c.c
+++ b/drivers/gpu/drm/loongson/lsdc_i2c.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "lsdc_drv.h"
#include "lsdc_output.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_irq.c b/drivers/gpu/drm/loongson/lsdc_irq.c
index efdc4d10792d..e8b7cc327f04 100644
--- a/drivers/gpu/drm/loongson/lsdc_irq.c
+++ b/drivers/gpu/drm/loongson/lsdc_irq.c
@@ -3,6 +3,7 @@
* Copyright (C) 2023 Loongson Technology Corporation Limited
*/
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "lsdc_irq.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c
index 600ed4fb0884..ccca67e01fd9 100644
--- a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c
+++ b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c
@@ -5,6 +5,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "lsdc_drv.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c
index 2bd797a9b9ff..aa7daee4c065 100644
--- a/drivers/gpu/drm/loongson/lsdc_output_7a2000.c
+++ b/drivers/gpu/drm/loongson/lsdc_output_7a2000.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "lsdc_drv.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_pixpll.c b/drivers/gpu/drm/loongson/lsdc_pixpll.c
index 2609a2256da4..51b9a032cf43 100644
--- a/drivers/gpu/drm/loongson/lsdc_pixpll.c
+++ b/drivers/gpu/drm/loongson/lsdc_pixpll.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "lsdc_drv.h"
diff --git a/drivers/gpu/drm/loongson/lsdc_plane.c b/drivers/gpu/drm/loongson/lsdc_plane.c
index aa9a97f9c4dc..9675344128d0 100644
--- a/drivers/gpu/drm/loongson/lsdc_plane.c
+++ b/drivers/gpu/drm/loongson/lsdc_plane.c
@@ -9,6 +9,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include "lsdc_drv.h"
#include "lsdc_regs.h"
@@ -196,7 +197,7 @@ static int lsdc_cursor_plane_atomic_async_check(struct drm_plane *plane,
return -EINVAL;
}
- crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
if (!crtc_state->active)
return -EINVAL;
diff --git a/drivers/gpu/drm/loongson/lsdc_ttm.c b/drivers/gpu/drm/loongson/lsdc_ttm.c
index 2e42c6970c9f..5d9075634bf8 100644
--- a/drivers/gpu/drm/loongson/lsdc_ttm.c
+++ b/drivers/gpu/drm/loongson/lsdc_ttm.c
@@ -8,6 +8,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_managed.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include "lsdc_drv.h"
#include "lsdc_ttm.h"
@@ -544,7 +545,8 @@ int lsdc_ttm_init(struct lsdc_device *ldev)
ret = ttm_device_init(&ldev->bdev, &lsdc_bo_driver, ddev->dev,
ddev->anon_inode->i_mapping,
- ddev->vma_offset_manager, false, true);
+ ddev->vma_offset_manager,
+ TTM_ALLOCATION_POOL_USE_DMA32);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/mcde/mcde_clk_div.c b/drivers/gpu/drm/mcde/mcde_clk_div.c
index 3056ac566473..8c5af2677357 100644
--- a/drivers/gpu/drm/mcde/mcde_clk_div.c
+++ b/drivers/gpu/drm/mcde/mcde_clk_div.c
@@ -71,12 +71,15 @@ static int mcde_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
return best_div;
}
-static long mcde_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int mcde_clk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- int div = mcde_clk_div_choose_div(hw, rate, prate, true);
+ int div = mcde_clk_div_choose_div(hw, req->rate,
+ &req->best_parent_rate, true);
- return DIV_ROUND_UP_ULL(*prate, div);
+ req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div);
+
+ return 0;
}
static unsigned long mcde_clk_div_recalc_rate(struct clk_hw *hw,
@@ -132,7 +135,7 @@ static int mcde_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops mcde_clk_div_ops = {
.enable = mcde_clk_div_enable,
.recalc_rate = mcde_clk_div_recalc_rate,
- .round_rate = mcde_clk_div_round_rate,
+ .determine_rate = mcde_clk_div_determine_rate,
.set_rate = mcde_clk_div_set_rate,
};
diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c
index 52043a12a2e8..257a6e84dd58 100644
--- a/drivers/gpu/drm/mcde/mcde_display.c
+++ b/drivers/gpu/drm/mcde/mcde_display.c
@@ -17,6 +17,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index e47debd60619..96188bf9274a 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -30,9 +30,30 @@ config DRM_MEDIATEK_DP
help
DRM/KMS Display Port driver for MediaTek SoCs.
+config DRM_MEDIATEK_HDMI_COMMON
+ tristate
+ depends on DRM_MEDIATEK
+ select DRM_DISPLAY_HDMI_HELPER
+ select DRM_DISPLAY_HELPER
+ select SND_SOC_HDMI_CODEC if SND_SOC
+ help
+ MediaTek SoC HDMI common library
+
config DRM_MEDIATEK_HDMI
tristate "DRM HDMI Support for Mediatek SoCs"
depends on DRM_MEDIATEK
- select SND_SOC_HDMI_CODEC if SND_SOC
+ select DRM_MEDIATEK_HDMI_COMMON
help
DRM/KMS HDMI driver for Mediatek SoCs
+
+config DRM_MEDIATEK_HDMI_V2
+ tristate "DRM HDMI v2 IP support for MediaTek SoCs"
+ depends on DRM_MEDIATEK
+ select DRM_MEDIATEK_HDMI_COMMON
+ help
+ Say yes here to enable support for the HDMIv2 IP and related
+ DDCv2 as found in the MediaTek MT8195, MT8188 SoCs and other
+ variants.
+ This driver can also be built as a module. If so, the HDMIv2
+ module will be called "mtk_hdmi_v2", and the DDCv2 module
+ will be called "mtk_hdmi_ddc_v2".
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 43afd0a26d14..e0ac49b07d50 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -21,8 +21,11 @@ mediatek-drm-y := mtk_crtc.o \
obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
+obj-$(CONFIG_DRM_MEDIATEK_HDMI_COMMON) += mtk_hdmi_common.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_cec.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_hdmi.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_hdmi_ddc.o
+obj-$(CONFIG_DRM_MEDIATEK_HDMI_V2) += mtk_hdmi_v2.o
+obj-$(CONFIG_DRM_MEDIATEK_HDMI_V2) += mtk_hdmi_ddc_v2.o
obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c
index bc7527542fdc..991cdb3d7d5f 100644
--- a/drivers/gpu/drm/mediatek/mtk_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_crtc.c
@@ -16,6 +16,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -283,6 +284,10 @@ static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
unsigned int i;
unsigned long flags;
+ /* release GCE HW usage and start autosuspend */
+ pm_runtime_mark_last_busy(cmdq_cl->chan->mbox->dev);
+ pm_runtime_put_autosuspend(cmdq_cl->chan->mbox->dev);
+
if (data->sta < 0)
return;
@@ -618,6 +623,9 @@ static void mtk_crtc_update_config(struct mtk_crtc *mtk_crtc, bool needs_vblank)
mtk_crtc->config_updating = false;
spin_unlock_irqrestore(&mtk_crtc->config_lock, flags);
+ if (pm_runtime_resume_and_get(mtk_crtc->cmdq_client.chan->mbox->dev) < 0)
+ goto update_config_out;
+
mbox_send_message(mtk_crtc->cmdq_client.chan, cmdq_handle);
mbox_client_txdone(mtk_crtc->cmdq_client.chan, 0);
goto update_config_out;
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
index ac6620e10262..9672ea1f91a2 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.c
@@ -621,15 +621,27 @@ int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev)
return ret;
}
-int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
+static void mtk_ddp_comp_put_device(void *_dev)
+{
+ struct device *dev = _dev;
+
+ put_device(dev);
+}
+
+static void mtk_ddp_comp_clk_put(void *_clk)
+{
+ struct clk *clk = _clk;
+
+ clk_put(clk);
+}
+
+int mtk_ddp_comp_init(struct device *dev, struct device_node *node, struct mtk_ddp_comp *comp,
unsigned int comp_id)
{
struct platform_device *comp_pdev;
enum mtk_ddp_comp_type type;
struct mtk_ddp_comp_dev *priv;
-#if IS_REACHABLE(CONFIG_MTK_CMDQ)
int ret;
-#endif
if (comp_id >= DDP_COMPONENT_DRM_ID_MAX)
return -EINVAL;
@@ -651,6 +663,10 @@ int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
}
comp->dev = &comp_pdev->dev;
+ ret = devm_add_action_or_reset(dev, mtk_ddp_comp_put_device, comp->dev);
+ if (ret)
+ return ret;
+
if (type == MTK_DISP_AAL ||
type == MTK_DISP_BLS ||
type == MTK_DISP_CCORR ||
@@ -666,15 +682,22 @@ int mtk_ddp_comp_init(struct device_node *node, struct mtk_ddp_comp *comp,
type == MTK_DSI)
return 0;
- priv = devm_kzalloc(comp->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- priv->regs = of_iomap(node, 0);
+ priv->regs = devm_of_iomap(dev, node, 0, NULL);
+ if (IS_ERR(priv->regs))
+ return PTR_ERR(priv->regs);
+
priv->clk = of_clk_get(node, 0);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
+ ret = devm_add_action_or_reset(dev, mtk_ddp_comp_clk_put, priv->clk);
+ if (ret)
+ return ret;
+
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(comp->dev, &priv->cmdq_reg, 0);
if (ret)
diff --git a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
index 7289b3dcf22f..3f3d43f4330d 100644
--- a/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_ddp_comp.h
@@ -350,7 +350,7 @@ static inline void mtk_ddp_comp_encoder_index_set(struct mtk_ddp_comp *comp)
int mtk_ddp_comp_get_id(struct device_node *node,
enum mtk_ddp_comp_type comp_type);
int mtk_find_possible_crtcs(struct drm_device *drm, struct device *dev);
-int mtk_ddp_comp_init(struct device_node *comp_node, struct mtk_ddp_comp *comp,
+int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node, struct mtk_ddp_comp *comp,
unsigned int comp_id);
enum mtk_ddp_comp_type mtk_ddp_comp_get_type(unsigned int comp_id);
void mtk_ddp_write(struct cmdq_pkt *cmdq_pkt, unsigned int value,
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c b/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c
index 10d60d2c2a56..6d7bf4afa78d 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c
@@ -80,27 +80,6 @@ void mtk_ccorr_stop(struct device *dev)
writel_relaxed(0x0, ccorr->regs + DISP_CCORR_EN);
}
-/* Converts a DRM S31.32 value to the HW S1.n format. */
-static u16 mtk_ctm_s31_32_to_s1_n(u64 in, u32 n)
-{
- u16 r;
-
- /* Sign bit. */
- r = in & BIT_ULL(63) ? BIT(n + 1) : 0;
-
- if ((in & GENMASK_ULL(62, 33)) > 0) {
- /* identity value 0x100000000 -> 0x400(mt8183), */
- /* identity value 0x100000000 -> 0x800(mt8192), */
- /* if bigger this, set it to max 0x7ff. */
- r |= GENMASK(n, 0);
- } else {
- /* take the n+1 most important bits. */
- r |= (in >> (32 - n)) & GENMASK(n, 0);
- }
-
- return r;
-}
-
void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state)
{
struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev);
@@ -119,7 +98,7 @@ void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state)
input = ctm->matrix;
for (i = 0; i < ARRAY_SIZE(coeffs); i++)
- coeffs[i] = mtk_ctm_s31_32_to_s1_n(input[i], matrix_bits);
+ coeffs[i] = drm_color_ctm_s31_32_to_qm_n(input[i], 2, matrix_bits);
mtk_ddp_write(cmdq_pkt, coeffs[0] << 16 | coeffs[1],
&ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_0);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
index fe97bb97e004..c0af3e3b51d5 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c
@@ -527,6 +527,13 @@ bool mtk_ovl_adaptor_is_comp_present(struct device_node *node)
type == OVL_ADAPTOR_TYPE_PADDING;
}
+static void ovl_adaptor_put_device(void *_dev)
+{
+ struct device *dev = _dev;
+
+ put_device(dev);
+}
+
static int ovl_adaptor_comp_init(struct device *dev, struct component_match **match)
{
struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev);
@@ -560,6 +567,11 @@ static int ovl_adaptor_comp_init(struct device *dev, struct component_match **ma
if (!comp_pdev)
return -EPROBE_DEFER;
+ ret = devm_add_action_or_reset(dev, ovl_adaptor_put_device,
+ &comp_pdev->dev);
+ if (ret)
+ return ret;
+
priv->ovl_adaptor_comp[id] = &comp_pdev->dev;
drm_of_component_match_add(dev, match, component_compare_of, node);
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
index bef6eeb30d3e..b0b1e158600f 100644
--- a/drivers/gpu/drm/mediatek/mtk_dp.c
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -2087,6 +2087,7 @@ static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
len = of_property_count_elems_of_size(endpoint,
"data-lanes", sizeof(u32));
+ of_node_put(endpoint);
if (len < 0 || len > 4 || len == 3) {
dev_err(dev, "invalid data lane size: %d\n", len);
return -EINVAL;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index eb5537f0ac90..a94c51a83261 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -686,10 +686,6 @@ err_free:
for (i = 0; i < private->data->mmsys_dev_num; i++)
private->all_drm_private[i]->drm = NULL;
err_put_dev:
- for (i = 0; i < private->data->mmsys_dev_num; i++) {
- /* For device_find_child in mtk_drm_get_all_priv() */
- put_device(private->all_drm_private[i]->dev);
- }
put_device(private->mutex_dev);
return ret;
}
@@ -697,18 +693,12 @@ err_put_dev:
static void mtk_drm_unbind(struct device *dev)
{
struct mtk_drm_private *private = dev_get_drvdata(dev);
- int i;
/* for multi mmsys dev, unregister drm dev in mmsys master */
if (private->drm_master) {
drm_dev_unregister(private->drm);
mtk_drm_kms_deinit(private->drm);
drm_dev_put(private->drm);
-
- for (i = 0; i < private->data->mmsys_dev_num; i++) {
- /* For device_find_child in mtk_drm_get_all_priv() */
- put_device(private->all_drm_private[i]->dev);
- }
put_device(private->mutex_dev);
}
private->mtk_drm_bound = false;
@@ -1133,7 +1123,7 @@ static int mtk_drm_probe(struct platform_device *pdev)
(void *)private->mmsys_dev,
sizeof(*private->mmsys_dev));
private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR].dev = &ovl_adaptor->dev;
- mtk_ddp_comp_init(NULL, &private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR],
+ mtk_ddp_comp_init(dev, NULL, &private->ddp_comp[DDP_COMPONENT_DRM_OVL_ADAPTOR],
DDP_COMPONENT_DRM_OVL_ADAPTOR);
component_match_add(dev, &match, compare_dev, &ovl_adaptor->dev);
}
@@ -1199,7 +1189,7 @@ static int mtk_drm_probe(struct platform_device *pdev)
node);
}
- ret = mtk_ddp_comp_init(node, &private->ddp_comp[comp_id], comp_id);
+ ret = mtk_ddp_comp_init(dev, node, &private->ddp_comp[comp_id], comp_id);
if (ret) {
of_node_put(node);
goto err_node;
diff --git a/drivers/gpu/drm/mediatek/mtk_gem.c b/drivers/gpu/drm/mediatek/mtk_gem.c
index a172456d1d7b..024cc7e9036c 100644
--- a/drivers/gpu/drm/mediatek/mtk_gem.c
+++ b/drivers/gpu/drm/mediatek/mtk_gem.c
@@ -11,6 +11,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include "mtk_drm_drv.h"
#include "mtk_gem.h"
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
index b766dd5e6c8d..0face4dcaa36 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c
@@ -31,6 +31,7 @@
#include <drm/drm_probe_helper.h>
#include "mtk_cec.h"
+#include "mtk_hdmi_common.h"
#include "mtk_hdmi_regs.h"
#define NCTS_BYTES 7
@@ -43,143 +44,6 @@ enum mtk_hdmi_clk_id {
MTK_HDMI_CLK_COUNT
};
-enum hdmi_aud_input_type {
- HDMI_AUD_INPUT_I2S = 0,
- HDMI_AUD_INPUT_SPDIF,
-};
-
-enum hdmi_aud_i2s_fmt {
- HDMI_I2S_MODE_RJT_24BIT = 0,
- HDMI_I2S_MODE_RJT_16BIT,
- HDMI_I2S_MODE_LJT_24BIT,
- HDMI_I2S_MODE_LJT_16BIT,
- HDMI_I2S_MODE_I2S_24BIT,
- HDMI_I2S_MODE_I2S_16BIT
-};
-
-enum hdmi_aud_mclk {
- HDMI_AUD_MCLK_128FS,
- HDMI_AUD_MCLK_192FS,
- HDMI_AUD_MCLK_256FS,
- HDMI_AUD_MCLK_384FS,
- HDMI_AUD_MCLK_512FS,
- HDMI_AUD_MCLK_768FS,
- HDMI_AUD_MCLK_1152FS,
-};
-
-enum hdmi_aud_channel_type {
- HDMI_AUD_CHAN_TYPE_1_0 = 0,
- HDMI_AUD_CHAN_TYPE_1_1,
- HDMI_AUD_CHAN_TYPE_2_0,
- HDMI_AUD_CHAN_TYPE_2_1,
- HDMI_AUD_CHAN_TYPE_3_0,
- HDMI_AUD_CHAN_TYPE_3_1,
- HDMI_AUD_CHAN_TYPE_4_0,
- HDMI_AUD_CHAN_TYPE_4_1,
- HDMI_AUD_CHAN_TYPE_5_0,
- HDMI_AUD_CHAN_TYPE_5_1,
- HDMI_AUD_CHAN_TYPE_6_0,
- HDMI_AUD_CHAN_TYPE_6_1,
- HDMI_AUD_CHAN_TYPE_7_0,
- HDMI_AUD_CHAN_TYPE_7_1,
- HDMI_AUD_CHAN_TYPE_3_0_LRS,
- HDMI_AUD_CHAN_TYPE_3_1_LRS,
- HDMI_AUD_CHAN_TYPE_4_0_CLRS,
- HDMI_AUD_CHAN_TYPE_4_1_CLRS,
- HDMI_AUD_CHAN_TYPE_6_1_CS,
- HDMI_AUD_CHAN_TYPE_6_1_CH,
- HDMI_AUD_CHAN_TYPE_6_1_OH,
- HDMI_AUD_CHAN_TYPE_6_1_CHR,
- HDMI_AUD_CHAN_TYPE_7_1_LH_RH,
- HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR,
- HDMI_AUD_CHAN_TYPE_7_1_LC_RC,
- HDMI_AUD_CHAN_TYPE_7_1_LW_RW,
- HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD,
- HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS,
- HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS,
- HDMI_AUD_CHAN_TYPE_7_1_CS_CH,
- HDMI_AUD_CHAN_TYPE_7_1_CS_OH,
- HDMI_AUD_CHAN_TYPE_7_1_CS_CHR,
- HDMI_AUD_CHAN_TYPE_7_1_CH_OH,
- HDMI_AUD_CHAN_TYPE_7_1_CH_CHR,
- HDMI_AUD_CHAN_TYPE_7_1_OH_CHR,
- HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR,
- HDMI_AUD_CHAN_TYPE_6_0_CS,
- HDMI_AUD_CHAN_TYPE_6_0_CH,
- HDMI_AUD_CHAN_TYPE_6_0_OH,
- HDMI_AUD_CHAN_TYPE_6_0_CHR,
- HDMI_AUD_CHAN_TYPE_7_0_LH_RH,
- HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR,
- HDMI_AUD_CHAN_TYPE_7_0_LC_RC,
- HDMI_AUD_CHAN_TYPE_7_0_LW_RW,
- HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD,
- HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS,
- HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS,
- HDMI_AUD_CHAN_TYPE_7_0_CS_CH,
- HDMI_AUD_CHAN_TYPE_7_0_CS_OH,
- HDMI_AUD_CHAN_TYPE_7_0_CS_CHR,
- HDMI_AUD_CHAN_TYPE_7_0_CH_OH,
- HDMI_AUD_CHAN_TYPE_7_0_CH_CHR,
- HDMI_AUD_CHAN_TYPE_7_0_OH_CHR,
- HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR,
- HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS,
- HDMI_AUD_CHAN_TYPE_UNKNOWN = 0xFF
-};
-
-enum hdmi_aud_channel_swap_type {
- HDMI_AUD_SWAP_LR,
- HDMI_AUD_SWAP_LFE_CC,
- HDMI_AUD_SWAP_LSRS,
- HDMI_AUD_SWAP_RLS_RRS,
- HDMI_AUD_SWAP_LR_STATUS,
-};
-
-struct hdmi_audio_param {
- enum hdmi_audio_coding_type aud_codec;
- enum hdmi_audio_sample_size aud_sample_size;
- enum hdmi_aud_input_type aud_input_type;
- enum hdmi_aud_i2s_fmt aud_i2s_fmt;
- enum hdmi_aud_mclk aud_mclk;
- enum hdmi_aud_channel_type aud_input_chan_type;
- struct hdmi_codec_params codec_params;
-};
-
-struct mtk_hdmi_conf {
- bool tz_disabled;
- bool cea_modes_only;
- unsigned long max_mode_clock;
-};
-
-struct mtk_hdmi {
- struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
- struct drm_connector *curr_conn;/* current connector (only valid when 'enabled') */
- struct device *dev;
- const struct mtk_hdmi_conf *conf;
- struct phy *phy;
- struct device *cec_dev;
- struct i2c_adapter *ddc_adpt;
- struct clk *clk[MTK_HDMI_CLK_COUNT];
- struct drm_display_mode mode;
- bool dvi_mode;
- struct regmap *sys_regmap;
- unsigned int sys_offset;
- struct regmap *regs;
- struct platform_device *audio_pdev;
- struct hdmi_audio_param aud_param;
- bool audio_enable;
- bool powered;
- bool enabled;
- hdmi_codec_plugged_cb plugged_cb;
- struct device *codec_dev;
- struct mutex update_plugged_status_lock;
-};
-
-static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b)
-{
- return container_of(b, struct mtk_hdmi, bridge);
-}
-
static void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black)
{
regmap_update_bits(hdmi->regs, VIDEO_CFG_4,
@@ -600,88 +464,6 @@ static void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi,
regmap_write(hdmi->regs, GRL_CFG5, val);
}
-struct hdmi_acr_n {
- unsigned int clock;
- unsigned int n[3];
-};
-
-/* Recommended N values from HDMI specification, tables 7-1 to 7-3 */
-static const struct hdmi_acr_n hdmi_rec_n_table[] = {
- /* Clock, N: 32kHz 44.1kHz 48kHz */
- { 25175, { 4576, 7007, 6864 } },
- { 74176, { 11648, 17836, 11648 } },
- { 148352, { 11648, 8918, 5824 } },
- { 296703, { 5824, 4459, 5824 } },
- { 297000, { 3072, 4704, 5120 } },
- { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */
-};
-
-/**
- * hdmi_recommended_n() - Return N value recommended by HDMI specification
- * @freq: audio sample rate in Hz
- * @clock: rounded TMDS clock in kHz
- */
-static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock)
-{
- const struct hdmi_acr_n *recommended;
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) {
- if (clock == hdmi_rec_n_table[i].clock)
- break;
- }
- recommended = hdmi_rec_n_table + i;
-
- switch (freq) {
- case 32000:
- return recommended->n[0];
- case 44100:
- return recommended->n[1];
- case 48000:
- return recommended->n[2];
- case 88200:
- return recommended->n[1] * 2;
- case 96000:
- return recommended->n[2] * 2;
- case 176400:
- return recommended->n[1] * 4;
- case 192000:
- return recommended->n[2] * 4;
- default:
- return (128 * freq) / 1000;
- }
-}
-
-static unsigned int hdmi_mode_clock_to_hz(unsigned int clock)
-{
- switch (clock) {
- case 25175:
- return 25174825; /* 25.2/1.001 MHz */
- case 74176:
- return 74175824; /* 74.25/1.001 MHz */
- case 148352:
- return 148351648; /* 148.5/1.001 MHz */
- case 296703:
- return 296703297; /* 297/1.001 MHz */
- default:
- return clock * 1000;
- }
-}
-
-static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate,
- unsigned int tmds_clock, unsigned int n)
-{
- return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n,
- 128 * audio_sample_rate);
-}
-
-static void mtk_hdmi_get_ncts(unsigned int sample_rate, unsigned int clock,
- unsigned int *n, unsigned int *cts)
-{
- *n = hdmi_recommended_n(sample_rate, clock);
- *cts = hdmi_expected_cts(sample_rate, clock, *n);
-}
-
static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n,
unsigned int cts)
{
@@ -1072,20 +854,6 @@ static const char * const mtk_hdmi_clk_names[MTK_HDMI_CLK_COUNT] = {
[MTK_HDMI_CLK_AUD_SPDIF] = "spdif",
};
-static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi,
- struct device_node *np)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names); i++) {
- hdmi->clk[i] = of_clk_get_by_name(np,
- mtk_hdmi_clk_names[i]);
- if (IS_ERR(hdmi->clk[i]))
- return PTR_ERR(hdmi->clk[i]);
- }
- return 0;
-}
-
static int mtk_hdmi_clk_enable_audio(struct mtk_hdmi *hdmi)
{
int ret;
@@ -1230,13 +998,6 @@ static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge,
return 0;
}
-static bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- return true;
-}
-
static void mtk_hdmi_bridge_atomic_disable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
@@ -1268,28 +1029,6 @@ static void mtk_hdmi_bridge_atomic_post_disable(struct drm_bridge *bridge,
hdmi->powered = false;
}
-static void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adjusted_mode)
-{
- struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
-
- dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n",
- adjusted_mode->name, adjusted_mode->hdisplay);
- dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d",
- adjusted_mode->hsync_start, adjusted_mode->hsync_end,
- adjusted_mode->htotal);
- dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n",
- adjusted_mode->hskew, adjusted_mode->vdisplay);
- dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d",
- adjusted_mode->vsync_start, adjusted_mode->vsync_end,
- adjusted_mode->vtotal);
- dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n",
- adjusted_mode->vscan, adjusted_mode->flags);
-
- drm_mode_copy(&hdmi->mode, adjusted_mode);
-}
-
static void mtk_hdmi_bridge_atomic_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
@@ -1345,169 +1084,10 @@ static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = {
.edid_read = mtk_hdmi_bridge_edid_read,
};
-static int mtk_hdmi_get_cec_dev(struct mtk_hdmi *hdmi, struct device *dev, struct device_node *np)
-{
- struct platform_device *cec_pdev;
- struct device_node *cec_np;
- int ret;
-
- ret = mtk_hdmi_get_all_clk(hdmi, np);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to get clocks\n");
-
- /* The CEC module handles HDMI hotplug detection */
- cec_np = of_get_compatible_child(np->parent, "mediatek,mt8173-cec");
- if (!cec_np)
- return dev_err_probe(dev, -EINVAL, "Failed to find CEC node\n");
-
- cec_pdev = of_find_device_by_node(cec_np);
- if (!cec_pdev) {
- dev_err(hdmi->dev, "Waiting for CEC device %pOF\n",
- cec_np);
- of_node_put(cec_np);
- return -EPROBE_DEFER;
- }
- of_node_put(cec_np);
-
- /*
- * The mediatek,syscon-hdmi property contains a phandle link to the
- * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG
- * registers it contains.
- */
- hdmi->sys_regmap = syscon_regmap_lookup_by_phandle_args(np, "mediatek,syscon-hdmi",
- 1, &hdmi->sys_offset);
- if (IS_ERR(hdmi->sys_regmap))
- return dev_err_probe(dev, PTR_ERR(hdmi->sys_regmap),
- "Failed to get system configuration registers\n");
-
- hdmi->cec_dev = &cec_pdev->dev;
- return 0;
-}
-
-static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi,
- struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct device_node *remote, *i2c_np;
- int ret;
-
- ret = mtk_hdmi_get_all_clk(hdmi, np);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to get clocks\n");
-
- hdmi->regs = device_node_to_regmap(dev->of_node);
- if (IS_ERR(hdmi->regs))
- return PTR_ERR(hdmi->regs);
-
- remote = of_graph_get_remote_node(np, 1, 0);
- if (!remote)
- return -EINVAL;
-
- if (!of_device_is_compatible(remote, "hdmi-connector")) {
- hdmi->next_bridge = of_drm_find_bridge(remote);
- if (!hdmi->next_bridge) {
- dev_err(dev, "Waiting for external bridge\n");
- of_node_put(remote);
- return -EPROBE_DEFER;
- }
- }
-
- i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0);
- of_node_put(remote);
- if (!i2c_np)
- return dev_err_probe(dev, -EINVAL, "No ddc-i2c-bus in connector\n");
-
- hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np);
- of_node_put(i2c_np);
- if (!hdmi->ddc_adpt)
- return dev_err_probe(dev, -EINVAL, "Failed to get ddc i2c adapter by node\n");
-
- ret = mtk_hdmi_get_cec_dev(hdmi, dev, np);
- if (ret)
- return ret;
-
- return 0;
-}
-
/*
* HDMI audio codec callbacks
*/
-static int mtk_hdmi_audio_params(struct mtk_hdmi *hdmi,
- struct hdmi_codec_daifmt *daifmt,
- struct hdmi_codec_params *params)
-{
- struct hdmi_audio_param aud_params = { 0 };
- unsigned int chan = params->cea.channels;
-
- dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
- params->sample_rate, params->sample_width, chan);
-
- if (!hdmi->bridge.encoder)
- return -ENODEV;
-
- switch (chan) {
- case 2:
- aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
- break;
- case 4:
- aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0;
- break;
- case 6:
- aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1;
- break;
- case 8:
- aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1;
- break;
- default:
- dev_err(hdmi->dev, "channel[%d] not supported!\n", chan);
- return -EINVAL;
- }
-
- switch (params->sample_rate) {
- case 32000:
- case 44100:
- case 48000:
- case 88200:
- case 96000:
- case 176400:
- case 192000:
- break;
- default:
- dev_err(hdmi->dev, "rate[%d] not supported!\n",
- params->sample_rate);
- return -EINVAL;
- }
-
- switch (daifmt->fmt) {
- case HDMI_I2S:
- aud_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
- aud_params.aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
- aud_params.aud_input_type = HDMI_AUD_INPUT_I2S;
- aud_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
- aud_params.aud_mclk = HDMI_AUD_MCLK_128FS;
- break;
- case HDMI_SPDIF:
- aud_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
- aud_params.aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
- aud_params.aud_input_type = HDMI_AUD_INPUT_SPDIF;
- break;
- default:
- dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__,
- daifmt->fmt);
- return -EINVAL;
- }
- memcpy(&aud_params.codec_params, params, sizeof(aud_params.codec_params));
- memcpy(&hdmi->aud_param, &aud_params, sizeof(aud_params));
-
- dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n",
- aud_params.aud_codec, aud_params.aud_input_type,
- aud_params.aud_input_chan_type, aud_params.codec_params.sample_rate);
-
- return 0;
-}
-
static int mtk_hdmi_audio_hw_params(struct device *dev, void *data,
struct hdmi_codec_daifmt *daifmt,
struct hdmi_codec_params *params)
@@ -1555,26 +1135,6 @@ mtk_hdmi_audio_mute(struct device *dev, void *data,
return 0;
}
-static int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len)
-{
- struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
-
- if (hdmi->enabled)
- memcpy(buf, hdmi->curr_conn->eld, min(sizeof(hdmi->curr_conn->eld), len));
- else
- memset(buf, 0, len);
- return 0;
-}
-
-static void mtk_hdmi_audio_set_plugged_cb(struct mtk_hdmi *hdmi, hdmi_codec_plugged_cb fn,
- struct device *codec_dev)
-{
- mutex_lock(&hdmi->update_plugged_status_lock);
- hdmi->plugged_cb = fn;
- hdmi->codec_dev = codec_dev;
- mutex_unlock(&hdmi->update_plugged_status_lock);
-}
-
static int mtk_hdmi_audio_hook_plugged_cb(struct device *dev, void *data,
hdmi_codec_plugged_cb fn,
struct device *codec_dev)
@@ -1596,92 +1156,21 @@ static const struct hdmi_codec_ops mtk_hdmi_audio_codec_ops = {
.hook_plugged_cb = mtk_hdmi_audio_hook_plugged_cb,
};
-static void mtk_hdmi_unregister_audio_driver(void *data)
-{
- platform_device_unregister(data);
-}
-
-static int mtk_hdmi_register_audio_driver(struct device *dev)
-{
- struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
- struct hdmi_audio_param *aud_param = &hdmi->aud_param;
- struct hdmi_codec_pdata codec_data = {
- .ops = &mtk_hdmi_audio_codec_ops,
- .max_i2s_channels = 2,
- .i2s = 1,
- .data = hdmi,
- .no_capture_mute = 1,
- };
- int ret;
-
- aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
- aud_param->aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
- aud_param->aud_input_type = HDMI_AUD_INPUT_I2S;
- aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
- aud_param->aud_mclk = HDMI_AUD_MCLK_128FS;
- aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
-
- hdmi->audio_pdev = platform_device_register_data(dev,
- HDMI_CODEC_DRV_NAME,
- PLATFORM_DEVID_AUTO,
- &codec_data,
- sizeof(codec_data));
- if (IS_ERR(hdmi->audio_pdev))
- return PTR_ERR(hdmi->audio_pdev);
-
- ret = devm_add_action_or_reset(dev, mtk_hdmi_unregister_audio_driver,
- hdmi->audio_pdev);
- if (ret)
- return ret;
-
- return 0;
-}
-
static int mtk_hdmi_probe(struct platform_device *pdev)
{
struct mtk_hdmi *hdmi;
- struct device *dev = &pdev->dev;
int ret;
- hdmi = devm_drm_bridge_alloc(dev, struct mtk_hdmi, bridge,
- &mtk_hdmi_bridge_funcs);
+ hdmi = mtk_hdmi_common_probe(pdev);
if (IS_ERR(hdmi))
return PTR_ERR(hdmi);
- hdmi->dev = dev;
- hdmi->conf = of_device_get_match_data(dev);
-
- ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev);
- if (ret)
- return ret;
-
- hdmi->phy = devm_phy_get(dev, "hdmi");
- if (IS_ERR(hdmi->phy))
- return dev_err_probe(dev, PTR_ERR(hdmi->phy),
- "Failed to get HDMI PHY\n");
-
- mutex_init(&hdmi->update_plugged_status_lock);
- platform_set_drvdata(pdev, hdmi);
-
- ret = mtk_hdmi_register_audio_driver(dev);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to register audio driver\n");
-
- hdmi->bridge.of_node = pdev->dev.of_node;
- hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
- | DRM_BRIDGE_OP_HPD;
- hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
- hdmi->bridge.vendor = "MediaTek";
- hdmi->bridge.product = "On-Chip HDMI";
-
- ret = devm_drm_bridge_add(dev, &hdmi->bridge);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to add bridge\n");
+ if (!hdmi->cec_dev)
+ return dev_err_probe(hdmi->dev, -ENODEV, "CEC is required by HDMIv1\n");
ret = mtk_hdmi_clk_enable_audio(hdmi);
if (ret)
- return dev_err_probe(dev, ret,
+ return dev_err_probe(hdmi->dev, ret,
"Failed to enable audio clocks\n");
return 0;
@@ -1712,19 +1201,32 @@ static __maybe_unused int mtk_hdmi_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops, mtk_hdmi_suspend, mtk_hdmi_resume);
+static const struct mtk_hdmi_ver_conf mtk_hdmi_v1_ver_conf = {
+ .bridge_funcs = &mtk_hdmi_bridge_funcs,
+ .codec_ops = &mtk_hdmi_audio_codec_ops,
+ .mtk_hdmi_clock_names = mtk_hdmi_clk_names,
+ .num_clocks = ARRAY_SIZE(mtk_hdmi_clk_names)
+};
+
static const struct mtk_hdmi_conf mtk_hdmi_conf_mt2701 = {
.tz_disabled = true,
+ .ver_conf = &mtk_hdmi_v1_ver_conf
};
static const struct mtk_hdmi_conf mtk_hdmi_conf_mt8167 = {
- .max_mode_clock = 148500,
.cea_modes_only = true,
+ .max_mode_clock = 148500,
+ .ver_conf = &mtk_hdmi_v1_ver_conf
+};
+
+static const struct mtk_hdmi_conf mtk_hdmi_conf_mt8173 = {
+ .ver_conf = &mtk_hdmi_v1_ver_conf
};
static const struct of_device_id mtk_hdmi_of_ids[] = {
{ .compatible = "mediatek,mt2701-hdmi", .data = &mtk_hdmi_conf_mt2701 },
{ .compatible = "mediatek,mt8167-hdmi", .data = &mtk_hdmi_conf_mt8167 },
- { .compatible = "mediatek,mt8173-hdmi" },
+ { .compatible = "mediatek,mt8173-hdmi", .data = &mtk_hdmi_conf_mt8173 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mtk_hdmi_of_ids);
@@ -1744,3 +1246,4 @@ MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>");
MODULE_DESCRIPTION("MediaTek HDMI Driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("DRM_MTK_HDMI_V1");
+MODULE_IMPORT_NS("DRM_MTK_HDMI");
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
new file mode 100644
index 000000000000..e78eb0876f16
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
@@ -0,0 +1,456 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Copyright (c) 2024 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <drm/drm_modes.h>
+#include <linux/device.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/math.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <sound/hdmi-codec.h>
+
+#include "mtk_hdmi_common.h"
+
+struct hdmi_acr_n {
+ unsigned int clock;
+ unsigned int n[3];
+};
+
+/* Recommended N values from HDMI specification, tables 7-1 to 7-3 */
+static const struct hdmi_acr_n hdmi_rec_n_table[] = {
+ /* Clock, N: 32kHz 44.1kHz 48kHz */
+ { 25175, { 4576, 7007, 6864 } },
+ { 74176, { 11648, 17836, 11648 } },
+ { 148352, { 11648, 8918, 5824 } },
+ { 296703, { 5824, 4459, 5824 } },
+ { 297000, { 3072, 4704, 5120 } },
+ { 0, { 4096, 6272, 6144 } }, /* all other TMDS clocks */
+};
+
+/**
+ * hdmi_recommended_n() - Return N value recommended by HDMI specification
+ * @freq: audio sample rate in Hz
+ * @clock: rounded TMDS clock in kHz
+ */
+static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock)
+{
+ const struct hdmi_acr_n *recommended;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) {
+ if (clock == hdmi_rec_n_table[i].clock)
+ break;
+ }
+ recommended = hdmi_rec_n_table + i;
+
+ switch (freq) {
+ case 32000:
+ return recommended->n[0];
+ case 44100:
+ return recommended->n[1];
+ case 48000:
+ return recommended->n[2];
+ case 88200:
+ return recommended->n[1] * 2;
+ case 96000:
+ return recommended->n[2] * 2;
+ case 176400:
+ return recommended->n[1] * 4;
+ case 192000:
+ return recommended->n[2] * 4;
+ default:
+ return (128 * freq) / 1000;
+ }
+}
+
+static unsigned int hdmi_mode_clock_to_hz(unsigned int clock)
+{
+ switch (clock) {
+ case 25175:
+ return 25174825; /* 25.2/1.001 MHz */
+ case 74176:
+ return 74175824; /* 74.25/1.001 MHz */
+ case 148352:
+ return 148351648; /* 148.5/1.001 MHz */
+ case 296703:
+ return 296703297; /* 297/1.001 MHz */
+ default:
+ return clock * 1000;
+ }
+}
+
+static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate,
+ unsigned int tmds_clock, unsigned int n)
+{
+ return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n,
+ 128 * audio_sample_rate);
+}
+
+void mtk_hdmi_get_ncts(unsigned int sample_rate, unsigned int clock,
+ unsigned int *n, unsigned int *cts)
+{
+ *n = hdmi_recommended_n(sample_rate, clock);
+ *cts = hdmi_expected_cts(sample_rate, clock, *n);
+}
+EXPORT_SYMBOL_NS_GPL(mtk_hdmi_get_ncts, "DRM_MTK_HDMI");
+
+int mtk_hdmi_audio_params(struct mtk_hdmi *hdmi,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct hdmi_audio_param aud_params = { 0 };
+ unsigned int chan = params->cea.channels;
+
+ dev_dbg(hdmi->dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
+ params->sample_rate, params->sample_width, chan);
+
+ if (!hdmi->bridge.encoder)
+ return -ENODEV;
+
+ switch (chan) {
+ case 2:
+ aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
+ break;
+ case 4:
+ aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_4_0;
+ break;
+ case 6:
+ aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_5_1;
+ break;
+ case 8:
+ aud_params.aud_input_chan_type = HDMI_AUD_CHAN_TYPE_7_1;
+ break;
+ default:
+ dev_err(hdmi->dev, "channel[%d] not supported!\n", chan);
+ return -EINVAL;
+ }
+
+ switch (params->sample_rate) {
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ break;
+ default:
+ dev_err(hdmi->dev, "rate[%d] not supported!\n",
+ params->sample_rate);
+ return -EINVAL;
+ }
+
+ switch (daifmt->fmt) {
+ case HDMI_I2S:
+ aud_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
+ aud_params.aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+ aud_params.aud_input_type = HDMI_AUD_INPUT_I2S;
+ aud_params.aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
+ aud_params.aud_mclk = HDMI_AUD_MCLK_128FS;
+ break;
+ case HDMI_SPDIF:
+ aud_params.aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
+ aud_params.aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+ aud_params.aud_input_type = HDMI_AUD_INPUT_SPDIF;
+ break;
+ default:
+ dev_err(hdmi->dev, "%s: Invalid DAI format %d\n", __func__,
+ daifmt->fmt);
+ return -EINVAL;
+ }
+ memcpy(&aud_params.codec_params, params, sizeof(aud_params.codec_params));
+ memcpy(&hdmi->aud_param, &aud_params, sizeof(aud_params));
+
+ dev_dbg(hdmi->dev, "codec:%d, input:%d, channel:%d, fs:%d\n",
+ aud_params.aud_codec, aud_params.aud_input_type,
+ aud_params.aud_input_chan_type, aud_params.codec_params.sample_rate);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_hdmi_audio_params, "DRM_MTK_HDMI");
+
+int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ if (hdmi->enabled)
+ memcpy(buf, hdmi->curr_conn->eld, min(sizeof(hdmi->curr_conn->eld), len));
+ else
+ memset(buf, 0, len);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_hdmi_audio_get_eld, "DRM_MTK_HDMI");
+
+void mtk_hdmi_audio_set_plugged_cb(struct mtk_hdmi *hdmi, hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ mutex_lock(&hdmi->update_plugged_status_lock);
+ hdmi->plugged_cb = fn;
+ hdmi->codec_dev = codec_dev;
+ mutex_unlock(&hdmi->update_plugged_status_lock);
+}
+EXPORT_SYMBOL_NS_GPL(mtk_hdmi_audio_set_plugged_cb, "DRM_MTK_HDMI");
+
+static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi, struct device_node *np,
+ const char * const *clock_names, size_t num_clocks)
+{
+ int i;
+
+ for (i = 0; i < num_clocks; i++) {
+ hdmi->clk[i] = of_clk_get_by_name(np, clock_names[i]);
+
+ if (IS_ERR(hdmi->clk[i]))
+ return PTR_ERR(hdmi->clk[i]);
+ }
+
+ return 0;
+}
+
+bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_hdmi_bridge_mode_fixup, "DRM_MTK_HDMI");
+
+void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n",
+ adjusted_mode->name, adjusted_mode->hdisplay);
+ dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d",
+ adjusted_mode->hsync_start, adjusted_mode->hsync_end,
+ adjusted_mode->htotal);
+ dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n",
+ adjusted_mode->hskew, adjusted_mode->vdisplay);
+ dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d",
+ adjusted_mode->vsync_start, adjusted_mode->vsync_end,
+ adjusted_mode->vtotal);
+ dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n",
+ adjusted_mode->vscan, adjusted_mode->flags);
+
+ drm_mode_copy(&hdmi->mode, adjusted_mode);
+}
+EXPORT_SYMBOL_NS_GPL(mtk_hdmi_bridge_mode_set, "DRM_MTK_HDMI");
+
+static void mtk_hdmi_put_device(void *_dev)
+{
+ struct device *dev = _dev;
+
+ put_device(dev);
+}
+
+static int mtk_hdmi_get_cec_dev(struct mtk_hdmi *hdmi, struct device *dev, struct device_node *np)
+{
+ struct platform_device *cec_pdev;
+ struct device_node *cec_np;
+ int ret;
+
+ /* The CEC module handles HDMI hotplug detection */
+ cec_np = of_get_compatible_child(np->parent, "mediatek,mt8173-cec");
+ if (!cec_np)
+ return dev_err_probe(dev, -EOPNOTSUPP, "Failed to find CEC node\n");
+
+ cec_pdev = of_find_device_by_node(cec_np);
+ if (!cec_pdev) {
+ dev_err(hdmi->dev, "Waiting for CEC device %pOF\n", cec_np);
+ of_node_put(cec_np);
+ return -EPROBE_DEFER;
+ }
+ of_node_put(cec_np);
+
+ ret = devm_add_action_or_reset(dev, mtk_hdmi_put_device, &cec_pdev->dev);
+ if (ret)
+ return ret;
+
+ /*
+ * The mediatek,syscon-hdmi property contains a phandle link to the
+ * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG
+ * registers it contains.
+ */
+ hdmi->sys_regmap = syscon_regmap_lookup_by_phandle_args(np, "mediatek,syscon-hdmi",
+ 1, &hdmi->sys_offset);
+ if (IS_ERR(hdmi->sys_regmap))
+ return dev_err_probe(dev, PTR_ERR(hdmi->sys_regmap),
+ "Failed to get system configuration registers\n");
+
+ hdmi->cec_dev = &cec_pdev->dev;
+ return 0;
+}
+
+static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device *pdev,
+ const char * const *clk_names, size_t num_clocks)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct device_node *remote, *i2c_np;
+ int ret;
+
+ ret = mtk_hdmi_get_all_clk(hdmi, np, clk_names, num_clocks);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ hdmi->irq = platform_get_irq(pdev, 0);
+ if (!hdmi->irq)
+ return hdmi->irq;
+
+ hdmi->regs = device_node_to_regmap(dev->of_node);
+ if (IS_ERR(hdmi->regs))
+ return PTR_ERR(hdmi->regs);
+
+ remote = of_graph_get_remote_node(np, 1, 0);
+ if (!remote)
+ return -EINVAL;
+
+ if (!of_device_is_compatible(remote, "hdmi-connector")) {
+ hdmi->next_bridge = of_drm_find_bridge(remote);
+ if (!hdmi->next_bridge) {
+ dev_err(dev, "Waiting for external bridge\n");
+ of_node_put(remote);
+ return -EPROBE_DEFER;
+ }
+ }
+
+ i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0);
+ of_node_put(remote);
+ if (!i2c_np)
+ return dev_err_probe(dev, -EINVAL, "No ddc-i2c-bus in connector\n");
+
+ hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np);
+ of_node_put(i2c_np);
+ if (!hdmi->ddc_adpt)
+ return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get ddc i2c adapter by node\n");
+
+ ret = devm_add_action_or_reset(dev, mtk_hdmi_put_device, &hdmi->ddc_adpt->dev);
+ if (ret)
+ return ret;
+
+ ret = mtk_hdmi_get_cec_dev(hdmi, dev, np);
+ if (ret == -EOPNOTSUPP)
+ dev_info(dev, "CEC support unavailable: node not found\n");
+ else if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void mtk_hdmi_unregister_audio_driver(void *data)
+{
+ platform_device_unregister(data);
+}
+
+static int mtk_hdmi_register_audio_driver(struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+ struct hdmi_audio_param *aud_param = &hdmi->aud_param;
+ struct hdmi_codec_pdata codec_data = {
+ .ops = hdmi->conf->ver_conf->codec_ops,
+ .max_i2s_channels = 2,
+ .i2s = 1,
+ .data = hdmi,
+ .no_capture_mute = 1,
+ };
+ int ret;
+
+ aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
+ aud_param->aud_sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+ aud_param->aud_input_type = HDMI_AUD_INPUT_I2S;
+ aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
+ aud_param->aud_mclk = HDMI_AUD_MCLK_128FS;
+ aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
+
+ hdmi->audio_pdev = platform_device_register_data(dev,
+ HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO,
+ &codec_data,
+ sizeof(codec_data));
+ if (IS_ERR(hdmi->audio_pdev))
+ return PTR_ERR(hdmi->audio_pdev);
+
+ ret = devm_add_action_or_reset(dev, mtk_hdmi_unregister_audio_driver,
+ hdmi->audio_pdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+struct mtk_hdmi *mtk_hdmi_common_probe(struct platform_device *pdev)
+{
+ const struct mtk_hdmi_ver_conf *ver_conf;
+ const struct mtk_hdmi_conf *hdmi_conf;
+ struct device *dev = &pdev->dev;
+ struct mtk_hdmi *hdmi;
+ int ret;
+
+ hdmi_conf = of_device_get_match_data(dev);
+ if (!hdmi_conf)
+ return ERR_PTR(-ENODEV);
+
+ ver_conf = hdmi_conf->ver_conf;
+
+ hdmi = devm_drm_bridge_alloc(dev, struct mtk_hdmi, bridge,
+ ver_conf->bridge_funcs);
+ if (IS_ERR(hdmi))
+ return hdmi;
+
+ hdmi->dev = dev;
+ hdmi->conf = hdmi_conf;
+
+ hdmi->clk = devm_kcalloc(dev, ver_conf->num_clocks, sizeof(*hdmi->clk), GFP_KERNEL);
+ if (!hdmi->clk)
+ return ERR_PTR(-ENOMEM);
+
+ ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev, ver_conf->mtk_hdmi_clock_names,
+ ver_conf->num_clocks);
+ if (ret)
+ return ERR_PTR(ret);
+
+ hdmi->phy = devm_phy_get(dev, "hdmi");
+ if (IS_ERR(hdmi->phy))
+ return dev_err_cast_probe(dev, hdmi->phy, "Failed to get HDMI PHY\n");
+
+ mutex_init(&hdmi->update_plugged_status_lock);
+ platform_set_drvdata(pdev, hdmi);
+
+ ret = mtk_hdmi_register_audio_driver(dev);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret, "Cannot register HDMI Audio driver\n");
+
+ hdmi->bridge.of_node = pdev->dev.of_node;
+ hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+ | DRM_BRIDGE_OP_HPD;
+
+ if (ver_conf->bridge_funcs->hdmi_write_infoframe &&
+ ver_conf->bridge_funcs->hdmi_clear_infoframe)
+ hdmi->bridge.ops |= DRM_BRIDGE_OP_HDMI;
+
+ hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
+ hdmi->bridge.ddc = hdmi->ddc_adpt;
+ hdmi->bridge.vendor = "MediaTek";
+ hdmi->bridge.product = "On-Chip HDMI";
+ hdmi->bridge.interlace_allowed = ver_conf->interlace_allowed;
+
+ ret = devm_drm_bridge_add(dev, &hdmi->bridge);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret, "Failed to add bridge\n");
+
+ return hdmi;
+}
+EXPORT_SYMBOL_NS_GPL(mtk_hdmi_common_probe, "DRM_MTK_HDMI");
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_DESCRIPTION("MediaTek HDMI Common Library");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
new file mode 100644
index 000000000000..de5e064585f8
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2024 Collabora Ltd.
+ */
+
+#ifndef _MTK_HDMI_COMMON_H
+#define _MTK_HDMI_COMMON_H
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#include <sound/hdmi-codec.h>
+
+enum hdmi_aud_input_type {
+ HDMI_AUD_INPUT_I2S = 0,
+ HDMI_AUD_INPUT_SPDIF,
+};
+
+enum hdmi_aud_i2s_fmt {
+ HDMI_I2S_MODE_RJT_24BIT = 0,
+ HDMI_I2S_MODE_RJT_16BIT,
+ HDMI_I2S_MODE_LJT_24BIT,
+ HDMI_I2S_MODE_LJT_16BIT,
+ HDMI_I2S_MODE_I2S_24BIT,
+ HDMI_I2S_MODE_I2S_16BIT
+};
+
+enum hdmi_aud_mclk {
+ HDMI_AUD_MCLK_128FS,
+ HDMI_AUD_MCLK_192FS,
+ HDMI_AUD_MCLK_256FS,
+ HDMI_AUD_MCLK_384FS,
+ HDMI_AUD_MCLK_512FS,
+ HDMI_AUD_MCLK_768FS,
+ HDMI_AUD_MCLK_1152FS,
+};
+
+enum hdmi_aud_channel_type {
+ HDMI_AUD_CHAN_TYPE_1_0 = 0,
+ HDMI_AUD_CHAN_TYPE_1_1,
+ HDMI_AUD_CHAN_TYPE_2_0,
+ HDMI_AUD_CHAN_TYPE_2_1,
+ HDMI_AUD_CHAN_TYPE_3_0,
+ HDMI_AUD_CHAN_TYPE_3_1,
+ HDMI_AUD_CHAN_TYPE_4_0,
+ HDMI_AUD_CHAN_TYPE_4_1,
+ HDMI_AUD_CHAN_TYPE_5_0,
+ HDMI_AUD_CHAN_TYPE_5_1,
+ HDMI_AUD_CHAN_TYPE_6_0,
+ HDMI_AUD_CHAN_TYPE_6_1,
+ HDMI_AUD_CHAN_TYPE_7_0,
+ HDMI_AUD_CHAN_TYPE_7_1,
+ HDMI_AUD_CHAN_TYPE_3_0_LRS,
+ HDMI_AUD_CHAN_TYPE_3_1_LRS,
+ HDMI_AUD_CHAN_TYPE_4_0_CLRS,
+ HDMI_AUD_CHAN_TYPE_4_1_CLRS,
+ HDMI_AUD_CHAN_TYPE_6_1_CS,
+ HDMI_AUD_CHAN_TYPE_6_1_CH,
+ HDMI_AUD_CHAN_TYPE_6_1_OH,
+ HDMI_AUD_CHAN_TYPE_6_1_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_LH_RH,
+ HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_7_1_LC_RC,
+ HDMI_AUD_CHAN_TYPE_7_1_LW_RW,
+ HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD,
+ HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS,
+ HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS,
+ HDMI_AUD_CHAN_TYPE_7_1_CS_CH,
+ HDMI_AUD_CHAN_TYPE_7_1_CS_OH,
+ HDMI_AUD_CHAN_TYPE_7_1_CS_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_CH_OH,
+ HDMI_AUD_CHAN_TYPE_7_1_CH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_OH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_6_0_CS,
+ HDMI_AUD_CHAN_TYPE_6_0_CH,
+ HDMI_AUD_CHAN_TYPE_6_0_OH,
+ HDMI_AUD_CHAN_TYPE_6_0_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_LH_RH,
+ HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_7_0_LC_RC,
+ HDMI_AUD_CHAN_TYPE_7_0_LW_RW,
+ HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD,
+ HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS,
+ HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS,
+ HDMI_AUD_CHAN_TYPE_7_0_CS_CH,
+ HDMI_AUD_CHAN_TYPE_7_0_CS_OH,
+ HDMI_AUD_CHAN_TYPE_7_0_CS_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_CH_OH,
+ HDMI_AUD_CHAN_TYPE_7_0_CH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_OH_CHR,
+ HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR,
+ HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS,
+ HDMI_AUD_CHAN_TYPE_UNKNOWN = 0xFF
+};
+
+enum hdmi_aud_channel_swap_type {
+ HDMI_AUD_SWAP_LR,
+ HDMI_AUD_SWAP_LFE_CC,
+ HDMI_AUD_SWAP_LSRS,
+ HDMI_AUD_SWAP_RLS_RRS,
+ HDMI_AUD_SWAP_LR_STATUS,
+};
+
+struct hdmi_audio_param {
+ enum hdmi_audio_coding_type aud_codec;
+ enum hdmi_audio_sample_size aud_sample_size;
+ enum hdmi_aud_input_type aud_input_type;
+ enum hdmi_aud_i2s_fmt aud_i2s_fmt;
+ enum hdmi_aud_mclk aud_mclk;
+ enum hdmi_aud_channel_type aud_input_chan_type;
+ struct hdmi_codec_params codec_params;
+};
+
+enum hdmi_hpd_state {
+ HDMI_PLUG_OUT = 0,
+ HDMI_PLUG_IN_AND_SINK_POWER_ON,
+ HDMI_PLUG_IN_ONLY,
+};
+
+struct mtk_hdmi_ver_conf {
+ const struct drm_bridge_funcs *bridge_funcs;
+ const struct hdmi_codec_ops *codec_ops;
+ const char * const *mtk_hdmi_clock_names;
+ int num_clocks;
+ bool interlace_allowed;
+};
+
+struct mtk_hdmi_conf {
+ const struct mtk_hdmi_ver_conf *ver_conf;
+ bool tz_disabled;
+ bool cea_modes_only;
+ unsigned long max_mode_clock;
+ u32 reg_hdmi_tx_cfg;
+};
+
+struct mtk_hdmi {
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct drm_connector *curr_conn;/* current connector (only valid when 'enabled') */
+ struct device *dev;
+ const struct mtk_hdmi_conf *conf;
+ struct phy *phy;
+ struct device *cec_dev;
+ struct i2c_adapter *ddc_adpt;
+ struct clk **clk;
+ struct drm_display_mode mode;
+ bool dvi_mode;
+ struct regmap *sys_regmap;
+ unsigned int sys_offset;
+ struct regmap *regs;
+ struct platform_device *audio_pdev;
+ struct hdmi_audio_param aud_param;
+ bool audio_enable;
+ bool powered;
+ bool enabled;
+ unsigned int irq;
+ enum hdmi_hpd_state hpd;
+ hdmi_codec_plugged_cb plugged_cb;
+ struct device *codec_dev;
+ struct mutex update_plugged_status_lock;
+};
+
+static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b)
+{
+ return container_of(b, struct mtk_hdmi, bridge);
+}
+
+
+int mtk_hdmi_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len);
+void mtk_hdmi_audio_set_plugged_cb(struct mtk_hdmi *hdmi, hdmi_codec_plugged_cb fn,
+ struct device *codec_dev);
+int mtk_hdmi_audio_params(struct mtk_hdmi *hdmi, struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params);
+void mtk_hdmi_get_ncts(unsigned int sample_rate, unsigned int clock,
+ unsigned int *n, unsigned int *cts);
+bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode);
+struct mtk_hdmi *mtk_hdmi_common_probe(struct platform_device *pdev);
+#endif /* _MTK_HDMI_COMMON_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
new file mode 100644
index 000000000000..b844e2c10f28
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek HDMI v2 Display Data Channel Driver
+ *
+ * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre, SAS
+ * Copyright (c) 2024 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include <drm/drm_edid.h>
+
+#include "mtk_hdmi_common.h"
+#include "mtk_hdmi_regs_v2.h"
+
+#define DDC2_DLY_CNT 572 /* BIM=208M/(v*4) = 90Khz */
+#define DDC2_DLY_CNT_EDID 832 /* BIM=208M/(v*4) = 62.5Khz */
+#define SI2C_ADDR_READ 0xf4
+#define SCDC_I2C_SLAVE_ADDRESS 0x54
+
+struct mtk_hdmi_ddc {
+ struct device *dev;
+ struct regmap *regs;
+ struct clk *clk;
+ struct i2c_adapter adap;
+};
+
+static int mtk_ddc_check_and_rise_low_bus(struct mtk_hdmi_ddc *ddc)
+{
+ u32 val;
+
+ regmap_read(ddc->regs, HDCP2X_DDCM_STATUS, &val);
+ if (val & DDC_I2C_BUS_LOW) {
+ regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
+ FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_CLOCK_SCL));
+ usleep_range(250, 300);
+ }
+
+ if (val & DDC_I2C_NO_ACK) {
+ u32 ddc_ctrl, hpd_ddc_ctrl, hpd_ddc_status;
+
+ regmap_read(ddc->regs, DDC_CTRL, &ddc_ctrl);
+ regmap_read(ddc->regs, HPD_DDC_CTRL, &hpd_ddc_ctrl);
+ regmap_read(ddc->regs, HPD_DDC_STATUS, &hpd_ddc_status);
+ }
+
+ if (val & DDC_I2C_NO_ACK)
+ return -EIO;
+
+ return 0;
+}
+
+static int mtk_ddc_wr_one(struct mtk_hdmi_ddc *ddc, u16 addr_id,
+ u16 offset_id, u8 *wr_data)
+{
+ u32 val;
+ int ret;
+
+ /* If down, rise bus for write operation */
+ mtk_ddc_check_and_rise_low_bus(ddc);
+
+ regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
+ FIELD_PREP(HPD_DDC_DELAY_CNT, DDC2_DLY_CNT));
+
+ if (wr_data) {
+ regmap_write(ddc->regs, SI2C_CTRL,
+ FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
+ FIELD_PREP(SI2C_WDATA, *wr_data) |
+ SI2C_WR);
+ }
+
+ regmap_write(ddc->regs, DDC_CTRL,
+ FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_WRITE) |
+ FIELD_PREP(DDC_CTRL_DIN_CNT, wr_data == NULL ? 0 : 1) |
+ FIELD_PREP(DDC_CTRL_OFFSET, offset_id) |
+ FIELD_PREP(DDC_CTRL_ADDR, addr_id));
+ usleep_range(1000, 1250);
+
+ ret = regmap_read_poll_timeout(ddc->regs, HPD_DDC_STATUS, val,
+ !(val & DDC_I2C_IN_PROG), 500, 1000);
+ if (ret) {
+ dev_err(ddc->dev, "DDC I2C write timeout\n");
+ return ret;
+ }
+
+ /* The I2C bus might be down after WR operation: rise it again */
+ ret = mtk_ddc_check_and_rise_low_bus(ddc);
+ if (ret) {
+ dev_err(ddc->dev, "Error during write operation: No ACK\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_ddcm_read_hdmi(struct mtk_hdmi_ddc *ddc, u16 uc_dev,
+ u8 addr, u8 *puc_value, u16 data_cnt)
+{
+ u16 dly_cnt, i, uc_idx;
+ u32 rem, temp_length, uc_read_count, val;
+ u64 loop_counter;
+ int ret;
+
+ mtk_ddc_check_and_rise_low_bus(ddc);
+
+ regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
+ FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_CLEAR_FIFO));
+
+ if (data_cnt >= 16) {
+ temp_length = 16;
+ loop_counter = data_cnt;
+
+ rem = do_div(loop_counter, temp_length);
+ if (rem)
+ loop_counter++;
+ } else {
+ temp_length = data_cnt;
+ loop_counter = 1;
+ }
+
+ if (uc_dev >= DDC_ADDR)
+ dly_cnt = DDC2_DLY_CNT_EDID;
+ else
+ dly_cnt = DDC2_DLY_CNT;
+
+ regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
+ FIELD_PREP(HPD_DDC_DELAY_CNT, dly_cnt));
+
+ for (i = 0; i < loop_counter; i++) {
+ rem = data_cnt % 16;
+
+ if (i > 0 && i == (loop_counter - 1) && rem)
+ temp_length = rem;
+
+ /* 0x51 - 0x53: Flow control */
+ if (uc_dev > DDC_ADDR && uc_dev <= 0x53) {
+ regmap_update_bits(ddc->regs, SCDC_CTRL, SCDC_DDC_SEGMENT,
+ FIELD_PREP(SCDC_DDC_SEGMENT, uc_dev - DDC_ADDR));
+
+ regmap_write(ddc->regs, DDC_CTRL,
+ FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ENH_READ_NOACK) |
+ FIELD_PREP(DDC_CTRL_DIN_CNT, temp_length) |
+ FIELD_PREP(DDC_CTRL_OFFSET, addr + i * temp_length) |
+ FIELD_PREP(DDC_CTRL_ADDR, DDC_ADDR));
+ } else {
+ u16 offset;
+
+ if (addr != 0x43)
+ offset = i * 16;
+ else
+ offset = 0;
+
+ regmap_write(ddc->regs, DDC_CTRL,
+ FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_READ_NOACK) |
+ FIELD_PREP(DDC_CTRL_DIN_CNT, temp_length) |
+ FIELD_PREP(DDC_CTRL_OFFSET, addr + offset) |
+ FIELD_PREP(DDC_CTRL_ADDR, uc_dev));
+ }
+ usleep_range(5000, 5500);
+
+ ret = regmap_read_poll_timeout(ddc->regs, HPD_DDC_STATUS, val,
+ !(val & DDC_I2C_IN_PROG), 1000,
+ 500 * (temp_length + 5));
+ if (ret) {
+ dev_err(ddc->dev, "Timeout waiting for DDC I2C\n");
+ return ret;
+ }
+
+ ret = mtk_ddc_check_and_rise_low_bus(ddc);
+ if (ret) {
+ dev_err(ddc->dev, "Error during read operation: No ACK\n");
+ return ret;
+ }
+
+ for (uc_idx = 0; uc_idx < temp_length; uc_idx++) {
+ unsigned int read_idx = i * 16 + uc_idx;
+
+ regmap_write(ddc->regs, SI2C_CTRL,
+ FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
+ SI2C_RD);
+
+ regmap_read(ddc->regs, HPD_DDC_STATUS, &val);
+ puc_value[read_idx] = FIELD_GET(DDC_DATA_OUT, val);
+
+ regmap_write(ddc->regs, SI2C_CTRL,
+ FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
+ SI2C_CONFIRM_READ);
+
+ /*
+ * If HDMI IP gets reset during EDID read, DDC read
+ * operation will fail and its delay counter will be
+ * reset to 400.
+ */
+ regmap_read(ddc->regs, HPD_DDC_CTRL, &val);
+ if (FIELD_GET(HPD_DDC_DELAY_CNT, val) < DDC2_DLY_CNT)
+ return 0;
+
+ uc_read_count = read_idx + 1;
+ }
+ }
+ if (uc_read_count > U8_MAX)
+ dev_warn(ddc->dev, "Invalid read data count %u\n", uc_read_count);
+
+ return uc_read_count;
+}
+
+static int mtk_hdmi_fg_ddc_data_read(struct mtk_hdmi_ddc *ddc, u16 b_dev,
+ u8 data_addr, u16 data_cnt, u8 *pr_data)
+{
+ int read_data_cnt;
+ u16 req_data_cnt;
+
+ if (!data_cnt) {
+ dev_err(ddc->dev, "Invalid DDCM read request\n");
+ return -EINVAL;
+ }
+
+ req_data_cnt = U8_MAX - data_addr + 1;
+ if (req_data_cnt > data_cnt)
+ req_data_cnt = data_cnt;
+
+ regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN);
+
+ read_data_cnt = mtk_ddcm_read_hdmi(ddc, b_dev, data_addr, pr_data, req_data_cnt);
+
+ if (read_data_cnt < 0)
+ return read_data_cnt;
+ else if (read_data_cnt != req_data_cnt)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mtk_hdmi_ddc_fg_data_write(struct mtk_hdmi_ddc *ddc, u16 b_dev,
+ u8 data_addr, u16 data_cnt, u8 *pr_data)
+{
+ int i, ret;
+
+ regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN);
+ /*
+ * In case there is no payload data, just do a single write for the
+ * address only
+ */
+ if (data_cnt == 0)
+ return mtk_ddc_wr_one(ddc, b_dev, data_addr, NULL);
+
+ i = 0;
+ do {
+ ret = mtk_ddc_wr_one(ddc, b_dev, data_addr + i, pr_data + i);
+ if (ret)
+ return ret;
+ } while (++i < data_cnt);
+
+ return 0;
+}
+
+static int mtk_hdmi_ddc_v2_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
+{
+ struct mtk_hdmi_ddc *ddc;
+ u8 offset = 0;
+ int i, ret;
+
+ ddc = adapter->algo_data;
+
+ for (i = 0; i < num; i++) {
+ struct i2c_msg *msg = &msgs[i];
+
+ if (!msg->buf) {
+ dev_err(ddc->dev, "No message buffer\n");
+ return -EINVAL;
+ }
+
+ if (msg->flags & I2C_M_RD) {
+ /*
+ * The underlying DDC hardware always issues a write request
+ * that assigns the read offset as part of the read operation,
+ * therefore, use the `offset` value assigned in the previous
+ * write request from drm_edid
+ */
+ ret = mtk_hdmi_fg_ddc_data_read(ddc, msg->addr, offset,
+ msg->len, &msg->buf[0]);
+ if (ret)
+ return ret;
+ } else {
+ /*
+ * The HW needs the data offset, found in buf[0], in the
+ * DDC_CTRL register, and each byte of data, starting at
+ * buf[1], goes in the SI2C_WDATA register.
+ */
+ ret = mtk_hdmi_ddc_fg_data_write(ddc, msg->addr, msg->buf[0],
+ msg->len - 1, &msg->buf[1]);
+ if (ret)
+ return ret;
+
+ /*
+ * Store the offset value requested by drm_edid or by
+ * scdc to use in subsequent read requests.
+ */
+ if ((msg->addr == DDC_ADDR || msg->addr == SCDC_I2C_SLAVE_ADDRESS) &&
+ msg->len == 1) {
+ offset = msg->buf[0];
+ }
+ }
+ }
+
+ return i;
+}
+
+static u32 mtk_hdmi_ddc_v2_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mtk_hdmi_ddc_v2_algorithm = {
+ .master_xfer = mtk_hdmi_ddc_v2_xfer,
+ .functionality = mtk_hdmi_ddc_v2_func,
+};
+
+static int mtk_hdmi_ddc_v2_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_hdmi_ddc *ddc;
+ int ret;
+
+ ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
+ if (!ddc)
+ return -ENOMEM;
+
+ ddc->dev = dev;
+ ddc->regs = device_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR_OR_NULL(ddc->regs))
+ return dev_err_probe(dev,
+ IS_ERR(ddc->regs) ? PTR_ERR(ddc->regs) : -EINVAL,
+ "Cannot get regmap\n");
+
+ ddc->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(ddc->clk))
+ return dev_err_probe(dev, PTR_ERR(ddc->clk), "Cannot get DDC clock\n");
+
+ strscpy(ddc->adap.name, "mediatek-hdmi-ddc-v2", sizeof(ddc->adap.name));
+ ddc->adap.owner = THIS_MODULE;
+ ddc->adap.algo = &mtk_hdmi_ddc_v2_algorithm;
+ ddc->adap.retries = 3;
+ ddc->adap.dev.of_node = dev->of_node;
+ ddc->adap.algo_data = ddc;
+ ddc->adap.dev.parent = &pdev->dev;
+
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Cannot enable Runtime PM\n");
+
+ pm_runtime_get_sync(dev);
+
+ ret = devm_i2c_add_adapter(dev, &ddc->adap);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Cannot add DDC I2C adapter\n");
+
+ platform_set_drvdata(pdev, ddc);
+ return 0;
+}
+
+static const struct of_device_id mtk_hdmi_ddc_v2_match[] = {
+ { .compatible = "mediatek,mt8195-hdmi-ddc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_v2_match);
+
+struct platform_driver mtk_hdmi_ddc_v2_driver = {
+ .probe = mtk_hdmi_ddc_v2_probe,
+ .driver = {
+ .name = "mediatek-hdmi-ddc-v2",
+ .of_match_table = mtk_hdmi_ddc_v2_match,
+ },
+};
+module_platform_driver(mtk_hdmi_ddc_v2_driver);
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_AUTHOR("Can Zeng <can.zeng@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek HDMIv2 DDC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
new file mode 100644
index 000000000000..521b35c7e14d
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
@@ -0,0 +1,263 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2021 BayLibre, SAS
+ * Copyright (c) 2024 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#ifndef _MTK_HDMI_REGS_H
+#define _MTK_HDMI_REGS_H
+
+/* HDMI_TOP Config */
+#define TOP_CFG00 0x000
+#define HDMI2_ON BIT(2)
+#define HDMI_MODE_HDMI BIT(3)
+#define SCR_ON BIT(4)
+#define TMDS_PACK_MODE GENMASK(9, 8)
+#define TMDS_PACK_MODE_8BPP 0
+#define TMDS_PACK_MODE_10BPP 1
+#define TMDS_PACK_MODE_12BPP 2
+#define TMDS_PACK_MODE_16BPP 3
+#define DEEPCOLOR_PKT_EN BIT(12)
+#define HDMI_ABIST_VIDEO_FORMAT GENMASK(21, 16)
+#define HDMI_ABIST_ENABLE BIT(31)
+#define TOP_CFG01 0x004
+#define CP_SET_MUTE_EN BIT(0)
+#define CP_CLR_MUTE_EN BIT(1)
+#define NULL_PKT_EN BIT(2)
+#define NULL_PKT_VSYNC_HIGH_EN BIT(3)
+
+/* HDMI_TOP Audio: Channel Mapping */
+#define TOP_AUD_MAP 0x00c
+#define SD0_MAP GENMASK(2, 0)
+#define SD1_MAP GENMASK(6, 4)
+#define SD2_MAP GENMASK(10, 8)
+#define SD3_MAP GENMASK(14, 12)
+#define SD4_MAP GENMASK(18, 16)
+#define SD5_MAP GENMASK(22, 20)
+#define SD6_MAP GENMASK(26, 24)
+#define SD7_MAP GENMASK(30, 28)
+
+/* Auxiliary Video Information (AVI) Infoframe */
+#define TOP_AVI_HEADER 0x024
+#define TOP_AVI_PKT00 0x028
+#define TOP_AVI_PKT01 0x02C
+#define TOP_AVI_PKT02 0x030
+#define TOP_AVI_PKT03 0x034
+#define TOP_AVI_PKT04 0x038
+#define TOP_AVI_PKT05 0x03C
+
+/* Audio Interface Infoframe */
+#define TOP_AIF_HEADER 0x040
+#define TOP_AIF_PKT00 0x044
+#define TOP_AIF_PKT01 0x048
+#define TOP_AIF_PKT02 0x04c
+#define TOP_AIF_PKT03 0x050
+
+/* Audio SPDIF Infoframe */
+#define TOP_SPDIF_HEADER 0x054
+#define TOP_SPDIF_PKT00 0x058
+#define TOP_SPDIF_PKT01 0x05c
+#define TOP_SPDIF_PKT02 0x060
+#define TOP_SPDIF_PKT03 0x064
+#define TOP_SPDIF_PKT04 0x068
+#define TOP_SPDIF_PKT05 0x06c
+#define TOP_SPDIF_PKT06 0x070
+#define TOP_SPDIF_PKT07 0x074
+
+/* Infoframes Configuration */
+#define TOP_INFO_EN 0x01c
+#define AVI_EN BIT(0)
+#define SPD_EN BIT(1)
+#define AUD_EN BIT(2)
+#define CP_EN BIT(5)
+#define VSIF_EN BIT(11)
+#define AVI_EN_WR BIT(16)
+#define SPD_EN_WR BIT(17)
+#define AUD_EN_WR BIT(18)
+#define CP_EN_WR BIT(21)
+#define VSIF_EN_WR BIT(27)
+#define TOP_INFO_RPT 0x020
+#define AVI_RPT_EN BIT(0)
+#define SPD_RPT_EN BIT(1)
+#define AUD_RPT_EN BIT(2)
+#define CP_RPT_EN BIT(5)
+#define VSIF_RPT_EN BIT(11)
+
+/* Vendor Specific Infoframe */
+#define TOP_VSIF_HEADER 0x174
+#define TOP_VSIF_PKT00 0x178
+#define TOP_VSIF_PKT01 0x17c
+#define TOP_VSIF_PKT02 0x180
+#define TOP_VSIF_PKT03 0x184
+#define TOP_VSIF_PKT04 0x188
+#define TOP_VSIF_PKT05 0x18c
+#define TOP_VSIF_PKT06 0x190
+#define TOP_VSIF_PKT07 0x194
+
+/* HDMI_TOP Misc */
+#define TOP_MISC_CTLR 0x1a4
+#define DEEP_COLOR_ADD BIT(4)
+
+/* Hardware interrupts */
+#define TOP_INT_STA00 0x1a8
+#define TOP_INT_ENABLE00 0x1b0
+#define HTPLG_R_INT BIT(0)
+#define HTPLG_F_INT BIT(1)
+#define PORD_R_INT BIT(2)
+#define PORD_F_INT BIT(3)
+#define HDMI_VSYNC_INT BIT(4)
+#define HDMI_AUDIO_INT BIT(5)
+#define HDCP2X_RX_REAUTH_REQ_DDCM_INT BIT(25)
+#define TOP_INT_ENABLE01 0x1b4
+#define TOP_INT_CLR00 0x1b8
+#define TOP_INT_CLR01 0x1bc
+
+
+/* Video Mute */
+#define TOP_VMUTE_CFG1 0x1c8
+#define REG_VMUTE_EN BIT(16)
+
+/* HDMI Audio IP */
+#define AIP_CTRL 0x400
+#define CTS_SW_SEL BIT(0)
+#define CTS_REQ_EN BIT(1)
+#define MCLK_EN BIT(2)
+#define NO_MCLK_CTSGEN_SEL BIT(3)
+#define AUD_IN_EN BIT(8)
+#define AUD_SEL_OWRT BIT(9)
+#define SPDIF_EN BIT(13)
+#define HBRA_ON BIT(14)
+#define DSD_EN BIT(15)
+#define I2S_EN GENMASK(19, 16)
+#define HBR_FROM_SPDIF BIT(20)
+#define CTS_CAL_N4 BIT(23)
+#define SPDIF_INTERNAL_MODULE BIT(24)
+#define AIP_N_VAL 0x404
+#define AIP_CTS_SVAL 0x408
+#define AIP_SPDIF_CTRL 0x40c
+#define WR_1UI_LOCK BIT(0)
+#define FS_OVERRIDE_WRITE BIT(1)
+#define WR_2UI_LOCK BIT(2)
+#define MAX_1UI_WRITE GENMASK(15, 8)
+#define MAX_2UI_SPDIF_WRITE GENMASK(23, 16)
+#define MAX_2UI_I2S_HI_WRITE GENMASK(23, 20)
+#define MAX_2UI_I2S_LFE_CC_SWAP BIT(1)
+#define MAX_2UI_I2S_LO_WRITE GENMASK(19, 16)
+#define AUD_ERR_THRESH GENMASK(29, 24)
+#define I2S2DSD_EN BIT(30)
+#define AIP_I2S_CTRL 0x410
+#define FIFO0_MAP GENMASK(1, 0)
+#define FIFO1_MAP GENMASK(3, 2)
+#define FIFO2_MAP GENMASK(5, 4)
+#define FIFO3_MAP GENMASK(7, 6)
+#define I2S_1ST_BIT_NOSHIFT BIT(8)
+#define I2S_DATA_DIR_LSB BIT(9)
+#define JUSTIFY_RIGHT BIT(10)
+#define WS_HIGH BIT(11)
+#define VBIT_COMPRESSED BIT(12)
+#define CBIT_ORDER_SAME BIT(13)
+#define SCK_EDGE_RISE BIT(14)
+#define AIP_I2S_CHST0 0x414
+#define AIP_I2S_CHST1 0x418
+#define AIP_TXCTRL 0x424
+#define RST4AUDIO BIT(0)
+#define RST4AUDIO_FIFO BIT(1)
+#define RST4AUDIO_ACR BIT(2)
+#define AUD_LAYOUT_1 BIT(4)
+#define AUD_MUTE_FIFO_EN BIT(5)
+#define AUD_PACKET_DROP BIT(6)
+#define DSD_MUTE_EN BIT(7)
+#define AIP_TPI_CTRL 0x428
+#define TPI_AUDIO_LOOKUP_EN BIT(2)
+
+/* Video downsampling configuration */
+#define VID_DOWNSAMPLE_CONFIG 0x8d0
+#define C444_C422_CONFIG_ENABLE BIT(0)
+#define C422_C420_CONFIG_ENABLE BIT(4)
+#define C422_C420_CONFIG_BYPASS BIT(5)
+#define C422_C420_CONFIG_OUT_CB_OR_CR BIT(6)
+#define VID_OUT_FORMAT 0x8fc
+#define OUTPUT_FORMAT_DEMUX_420_ENABLE BIT(10)
+
+/* HDCP registers */
+#define HDCP_TOP_CTRL 0xc00
+#define HDCP2X_CTRL_0 0xc20
+#define HDCP2X_EN BIT(0)
+#define HDCP2X_ENCRYPT_EN BIT(7)
+#define HDCP2X_HPD_OVR BIT(10)
+#define HDCP2X_HPD_SW BIT(11)
+#define HDCP2X_POL_CTRL 0xc54
+#define HDCP2X_DIS_POLL_EN BIT(16)
+#define HDCP1X_CTRL 0xcd0
+#define HDCP1X_ENC_EN BIT(6)
+
+/* HDMI DDC registers */
+#define HPD_DDC_CTRL 0xc08
+#define HPD_DDC_DELAY_CNT GENMASK(31, 16)
+#define HPD_DDC_HPD_DBNC_EN BIT(2)
+#define HPD_DDC_PORD_DBNC_EN BIT(3)
+#define DDC_CTRL 0xc10
+#define DDC_CTRL_ADDR GENMASK(7, 1)
+#define DDC_CTRL_OFFSET GENMASK(15, 8)
+#define DDC_CTRL_DIN_CNT GENMASK(25, 16)
+#define DDC_CTRL_CMD GENMASK(31, 28)
+#define SCDC_CTRL 0xc18
+#define SCDC_DDC_SEGMENT GENMASK(15, 8)
+#define HPD_DDC_STATUS 0xc60
+#define HPD_STATE GENMASK(1, 0)
+#define HPD_STATE_CONNECTED 2
+#define HPD_PIN_STA BIT(4)
+#define PORD_PIN_STA BIT(5)
+#define DDC_I2C_IN_PROG BIT(13)
+#define DDC_DATA_OUT GENMASK(23, 16)
+#define SI2C_CTRL 0xcac
+#define SI2C_WR BIT(0)
+#define SI2C_RD BIT(1)
+#define SI2C_CONFIRM_READ BIT(2)
+#define SI2C_WDATA GENMASK(15, 8)
+#define SI2C_ADDR GENMASK(23, 16)
+
+/* HDCP DDC registers */
+#define HDCP2X_DDCM_STATUS 0xc68
+#define DDC_I2C_NO_ACK BIT(10)
+#define DDC_I2C_BUS_LOW BIT(11)
+
+/* HDMI TX registers */
+#define HDMITX_CONFIG_MT8188 0xea0
+#define HDMITX_CONFIG_MT8195 0x900
+#define HDMI_YUV420_MODE BIT(10)
+#define HDMITX_SW_HPD BIT(29)
+#define HDMITX_SW_RSTB BIT(31)
+
+/**
+ * enum mtk_hdmi_ddc_v2_cmds - DDC_CMD register commands
+ * @DDC_CMD_READ_NOACK: Current address read with no ACK on last byte
+ * @DDC_CMD_READ: Current address read with ACK on last byte
+ * @DDC_CMD_SEQ_READ_NOACK: Sequential read with no ACK on last byte
+ * @DDC_CMD_SEQ_READ: Sequential read with ACK on last byte
+ * @DDC_CMD_ENH_READ_NOACK: Enhanced read with no ACK on last byte
+ * @DDC_CMD_ENH_READ: Enhanced read with ACK on last byte
+ * @DDC_CMD_SEQ_WRITE_NOACK: Sequential write ignoring ACK on last byte
+ * @DDC_CMD_SEQ_WRITE: Sequential write requiring ACK on last byte
+ * @DDC_CMD_RSVD: Reserved for future use
+ * @DDC_CMD_CLEAR_FIFO: Clear DDC I2C FIFO
+ * @DDC_CMD_CLOCK_SCL: Start clocking DDC I2C SCL
+ * @DDC_CMD_ABORT_XFER: Abort DDC I2C transaction
+ */
+enum mtk_hdmi_ddc_v2_cmds {
+ DDC_CMD_READ_NOACK = 0x0,
+ DDC_CMD_READ,
+ DDC_CMD_SEQ_READ_NOACK,
+ DDC_CMD_SEQ_READ,
+ DDC_CMD_ENH_READ_NOACK,
+ DDC_CMD_ENH_READ,
+ DDC_CMD_SEQ_WRITE_NOACK,
+ DDC_CMD_SEQ_WRITE = 0x07,
+ DDC_CMD_CLEAR_FIFO = 0x09,
+ DDC_CMD_CLOCK_SCL = 0x0a,
+ DDC_CMD_ABORT_XFER = 0x0f
+};
+
+#endif /* _MTK_HDMI_REGS_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
new file mode 100644
index 000000000000..c272e1e74b7d
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
@@ -0,0 +1,1521 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek HDMI v2 IP driver
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre, SAS
+ * Copyright (c) 2024 Collabora Ltd.
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/suspend.h>
+#include <linux/units.h>
+#include <linux/phy/phy.h>
+
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
+#include <drm/display/drm_scdc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "mtk_hdmi_common.h"
+#include "mtk_hdmi_regs_v2.h"
+
+#define MTK_HDMI_V2_CLOCK_MIN 27000
+#define MTK_HDMI_V2_CLOCK_MAX 594000
+
+#define HPD_PORD_HWIRQS (HTPLG_R_INT | HTPLG_F_INT | PORD_F_INT | PORD_R_INT)
+
+enum mtk_hdmi_v2_clk_id {
+ MTK_HDMI_V2_CLK_HDCP_SEL,
+ MTK_HDMI_V2_CLK_HDCP_24M_SEL,
+ MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI,
+ MTK_HDMI_V2_CLK_HDMI_APB_SEL,
+ MTK_HDMI_V2_CLK_COUNT,
+};
+
+const char *const mtk_hdmi_v2_clk_names[MTK_HDMI_V2_CLK_COUNT] = {
+ [MTK_HDMI_V2_CLK_HDMI_APB_SEL] = "bus",
+ [MTK_HDMI_V2_CLK_HDCP_SEL] = "hdcp",
+ [MTK_HDMI_V2_CLK_HDCP_24M_SEL] = "hdcp24m",
+ [MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI] = "hdmi-split",
+};
+
+static inline void mtk_hdmi_v2_hwirq_disable(struct mtk_hdmi *hdmi)
+{
+ regmap_write(hdmi->regs, TOP_INT_ENABLE00, 0);
+ regmap_write(hdmi->regs, TOP_INT_ENABLE01, 0);
+}
+
+static inline void mtk_hdmi_v2_enable_hpd_pord_irq(struct mtk_hdmi *hdmi, bool enable)
+{
+ if (enable)
+ regmap_set_bits(hdmi->regs, TOP_INT_ENABLE00, HPD_PORD_HWIRQS);
+ else
+ regmap_clear_bits(hdmi->regs, TOP_INT_ENABLE00, HPD_PORD_HWIRQS);
+}
+
+static inline void mtk_hdmi_v2_set_sw_hpd(struct mtk_hdmi *hdmi, bool enable)
+{
+ if (enable) {
+ regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_HPD);
+ regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
+ regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
+ } else {
+ regmap_clear_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
+ regmap_clear_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
+ regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_HPD);
+ }
+}
+
+static inline void mtk_hdmi_v2_enable_scrambling(struct mtk_hdmi *hdmi, bool enable)
+{
+ struct drm_scdc *scdc = &hdmi->curr_conn->display_info.hdmi.scdc;
+
+ if (enable)
+ regmap_set_bits(hdmi->regs, TOP_CFG00, SCR_ON | HDMI2_ON);
+ else
+ regmap_clear_bits(hdmi->regs, TOP_CFG00, SCR_ON | HDMI2_ON);
+
+ if (scdc->supported) {
+ if (scdc->scrambling.supported)
+ drm_scdc_set_scrambling(hdmi->curr_conn, enable);
+ drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, enable);
+ }
+}
+
+static void mtk_hdmi_v2_hw_vid_mute(struct mtk_hdmi *hdmi, bool enable)
+{
+ /* If enabled, sends a black image */
+ if (enable)
+ regmap_set_bits(hdmi->regs, TOP_VMUTE_CFG1, REG_VMUTE_EN);
+ else
+ regmap_clear_bits(hdmi->regs, TOP_VMUTE_CFG1, REG_VMUTE_EN);
+}
+
+static void mtk_hdmi_v2_hw_aud_mute(struct mtk_hdmi *hdmi, bool enable)
+{
+ u32 aip, val;
+
+ if (!enable) {
+ regmap_clear_bits(hdmi->regs, AIP_TXCTRL, AUD_MUTE_FIFO_EN);
+ return;
+ }
+
+ regmap_read(hdmi->regs, AIP_CTRL, &aip);
+
+ val = AUD_MUTE_FIFO_EN;
+ if (aip & DSD_EN)
+ val |= DSD_MUTE_EN;
+
+ regmap_update_bits(hdmi->regs, AIP_TXCTRL, val, val);
+}
+
+static void mtk_hdmi_v2_hw_reset(struct mtk_hdmi *hdmi)
+{
+ regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_RSTB);
+ udelay(5);
+ regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_RSTB);
+}
+
+static inline u32 mtk_hdmi_v2_format_hw_packet(const u8 *buffer, u8 len)
+{
+ unsigned short i;
+ u32 val = 0;
+
+ for (i = 0; i < len; i++)
+ val |= buffer[i] << (i * 8);
+
+ return val;
+}
+
+static void mtk_hdmi_v2_hw_write_audio_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
+{
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
+
+ regmap_write(hdmi->regs, TOP_AIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
+ regmap_write(hdmi->regs, TOP_AIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 3));
+ regmap_write(hdmi->regs, TOP_AIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 2));
+ regmap_write(hdmi->regs, TOP_AIF_PKT02, 0);
+ regmap_write(hdmi->regs, TOP_AIF_PKT03, 0);
+
+ regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
+ regmap_set_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
+}
+
+static void mtk_hdmi_v2_hw_write_avi_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
+{
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
+
+ regmap_write(hdmi->regs, TOP_AVI_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
+ regmap_write(hdmi->regs, TOP_AVI_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
+ regmap_write(hdmi->regs, TOP_AVI_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 3));
+ regmap_write(hdmi->regs, TOP_AVI_PKT02, mtk_hdmi_v2_format_hw_packet(&buffer[10], 4));
+ regmap_write(hdmi->regs, TOP_AVI_PKT03, mtk_hdmi_v2_format_hw_packet(&buffer[14], 3));
+ regmap_write(hdmi->regs, TOP_AVI_PKT04, 0);
+ regmap_write(hdmi->regs, TOP_AVI_PKT05, 0);
+
+ regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
+ regmap_set_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
+}
+
+static void mtk_hdmi_v2_hw_write_spd_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
+{
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
+
+ regmap_write(hdmi->regs, TOP_SPDIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 3));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT02, mtk_hdmi_v2_format_hw_packet(&buffer[10], 4));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT03, mtk_hdmi_v2_format_hw_packet(&buffer[14], 3));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT04, mtk_hdmi_v2_format_hw_packet(&buffer[17], 4));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT05, mtk_hdmi_v2_format_hw_packet(&buffer[21], 3));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT06, mtk_hdmi_v2_format_hw_packet(&buffer[24], 4));
+ regmap_write(hdmi->regs, TOP_SPDIF_PKT07, buffer[28]);
+
+ regmap_set_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
+ regmap_set_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
+}
+
+static void mtk_hdmi_v2_hw_write_vendor_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
+{
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
+
+ regmap_write(hdmi->regs, TOP_VSIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
+ regmap_write(hdmi->regs, TOP_VSIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
+ regmap_write(hdmi->regs, TOP_VSIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 2));
+ regmap_write(hdmi->regs, TOP_VSIF_PKT02, 0);
+ regmap_write(hdmi->regs, TOP_VSIF_PKT03, 0);
+ regmap_write(hdmi->regs, TOP_VSIF_PKT04, 0);
+ regmap_write(hdmi->regs, TOP_VSIF_PKT05, 0);
+ regmap_write(hdmi->regs, TOP_VSIF_PKT06, 0);
+ regmap_write(hdmi->regs, TOP_VSIF_PKT07, 0);
+
+ regmap_set_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
+ regmap_set_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
+}
+
+static void mtk_hdmi_yuv420_downsampling(struct mtk_hdmi *hdmi, bool enable)
+{
+ u32 val;
+
+ regmap_read(hdmi->regs, VID_DOWNSAMPLE_CONFIG, &val);
+
+ if (enable) {
+ regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMI_YUV420_MODE);
+
+ val |= C444_C422_CONFIG_ENABLE | C422_C420_CONFIG_ENABLE;
+ val |= C422_C420_CONFIG_OUT_CB_OR_CR;
+ val &= ~C422_C420_CONFIG_BYPASS;
+ regmap_write(hdmi->regs, VID_DOWNSAMPLE_CONFIG, val);
+
+ regmap_set_bits(hdmi->regs, VID_OUT_FORMAT, OUTPUT_FORMAT_DEMUX_420_ENABLE);
+ } else {
+ regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMI_YUV420_MODE);
+
+ val &= ~(C444_C422_CONFIG_ENABLE | C422_C420_CONFIG_ENABLE);
+ val &= ~C422_C420_CONFIG_OUT_CB_OR_CR;
+ val |= C422_C420_CONFIG_BYPASS;
+ regmap_write(hdmi->regs, VID_DOWNSAMPLE_CONFIG, val);
+
+ regmap_clear_bits(hdmi->regs, VID_OUT_FORMAT, OUTPUT_FORMAT_DEMUX_420_ENABLE);
+ }
+}
+
+static int mtk_hdmi_v2_setup_audio_infoframe(struct mtk_hdmi *hdmi)
+{
+ struct hdmi_codec_params *params = &hdmi->aud_param.codec_params;
+ struct hdmi_audio_infoframe frame;
+ u8 buffer[14];
+ ssize_t ret;
+
+ memcpy(&frame, &params->cea, sizeof(frame));
+
+ ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+ if (ret < 0)
+ return ret;
+
+ mtk_hdmi_v2_hw_write_audio_infoframe(hdmi, buffer);
+
+ return 0;
+}
+
+static inline void mtk_hdmi_v2_hw_gcp_avmute(struct mtk_hdmi *hdmi, bool mute)
+{
+ u32 val;
+
+ regmap_read(hdmi->regs, TOP_CFG01, &val);
+ val &= ~(CP_CLR_MUTE_EN | CP_SET_MUTE_EN);
+
+ if (mute) {
+ val |= CP_SET_MUTE_EN;
+ val &= ~CP_CLR_MUTE_EN;
+ } else {
+ val |= CP_CLR_MUTE_EN;
+ val &= ~CP_SET_MUTE_EN;
+ }
+ regmap_write(hdmi->regs, TOP_CFG01, val);
+
+ regmap_set_bits(hdmi->regs, TOP_INFO_RPT, CP_RPT_EN);
+ regmap_set_bits(hdmi->regs, TOP_INFO_EN, CP_EN | CP_EN_WR);
+}
+
+static void mtk_hdmi_v2_hw_ncts_enable(struct mtk_hdmi *hdmi, bool enable)
+{
+ if (enable)
+ regmap_set_bits(hdmi->regs, AIP_CTRL, CTS_SW_SEL);
+ else
+ regmap_clear_bits(hdmi->regs, AIP_CTRL, CTS_SW_SEL);
+}
+
+static void mtk_hdmi_v2_hw_aud_set_channel_status(struct mtk_hdmi *hdmi)
+{
+ u8 *ch_status = hdmi->aud_param.codec_params.iec.status;
+
+ /* Only the first 5 to 7 bytes of Channel Status contain useful information */
+ regmap_write(hdmi->regs, AIP_I2S_CHST0, mtk_hdmi_v2_format_hw_packet(&ch_status[0], 4));
+ regmap_write(hdmi->regs, AIP_I2S_CHST1, mtk_hdmi_v2_format_hw_packet(&ch_status[4], 3));
+}
+
+static void mtk_hdmi_v2_hw_aud_set_ncts(struct mtk_hdmi *hdmi,
+ unsigned int sample_rate,
+ unsigned int clock)
+{
+ unsigned int n, cts;
+
+ mtk_hdmi_get_ncts(sample_rate, clock, &n, &cts);
+
+ regmap_write(hdmi->regs, AIP_N_VAL, n);
+ regmap_write(hdmi->regs, AIP_CTS_SVAL, cts);
+}
+
+static void mtk_hdmi_v2_hw_aud_enable(struct mtk_hdmi *hdmi, bool enable)
+{
+ if (enable)
+ regmap_clear_bits(hdmi->regs, AIP_TXCTRL, AUD_PACKET_DROP);
+ else
+ regmap_set_bits(hdmi->regs, AIP_TXCTRL, AUD_PACKET_DROP);
+}
+
+static u32 mtk_hdmi_v2_aud_output_channel_map(u8 sd0, u8 sd1, u8 sd2, u8 sd3,
+ u8 sd4, u8 sd5, u8 sd6, u8 sd7)
+{
+ u32 val;
+
+ /*
+ * Each of the Output Channels (0-7) can be mapped to get their input
+ * from any of the available Input Channels (0-7): this function
+ * takes input channel numbers and formats a value that must then
+ * be written to the TOP_AUD_MAP hardware register by the caller.
+ */
+ val = FIELD_PREP(SD0_MAP, sd0) | FIELD_PREP(SD1_MAP, sd1);
+ val |= FIELD_PREP(SD2_MAP, sd2) | FIELD_PREP(SD3_MAP, sd3);
+ val |= FIELD_PREP(SD4_MAP, sd4) | FIELD_PREP(SD5_MAP, sd5);
+ val |= FIELD_PREP(SD6_MAP, sd6) | FIELD_PREP(SD7_MAP, sd7);
+
+ return val;
+}
+
+static void mtk_hdmi_audio_dsd_config(struct mtk_hdmi *hdmi,
+ unsigned char chnum, bool dsd_bypass)
+{
+ u32 channel_map;
+
+ regmap_update_bits(hdmi->regs, AIP_CTRL, SPDIF_EN | DSD_EN | HBRA_ON, DSD_EN);
+ regmap_set_bits(hdmi->regs, AIP_TXCTRL, DSD_MUTE_EN);
+
+ if (dsd_bypass)
+ channel_map = mtk_hdmi_v2_aud_output_channel_map(0, 2, 4, 6, 1, 3, 5, 7);
+ else
+ channel_map = mtk_hdmi_v2_aud_output_channel_map(0, 5, 1, 0, 3, 2, 4, 0);
+
+ regmap_write(hdmi->regs, TOP_AUD_MAP, channel_map);
+ regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, I2S2DSD_EN);
+}
+
+static inline void mtk_hdmi_v2_hw_i2s_fifo_map(struct mtk_hdmi *hdmi, u32 fifo_mapping)
+{
+ regmap_update_bits(hdmi->regs, AIP_I2S_CTRL,
+ FIFO0_MAP | FIFO1_MAP | FIFO2_MAP | FIFO3_MAP, fifo_mapping);
+}
+
+static inline void mtk_hdmi_v2_hw_i2s_ch_number(struct mtk_hdmi *hdmi, u8 chnum)
+{
+ regmap_update_bits(hdmi->regs, AIP_CTRL, I2S_EN, FIELD_PREP(I2S_EN, chnum));
+}
+
+static void mtk_hdmi_v2_hw_i2s_ch_mapping(struct mtk_hdmi *hdmi, u8 chnum, u8 mapping)
+{
+ u32 fifo_map;
+ u8 bdata;
+
+ switch (chnum) {
+ default:
+ case 2:
+ bdata = 0x1;
+ break;
+ case 3:
+ bdata = 0x3;
+ break;
+ case 6:
+ if (mapping == 0x0e) {
+ bdata = 0xf;
+ break;
+ }
+ fallthrough;
+ case 5:
+ bdata = 0x7;
+ break;
+ case 7:
+ case 8:
+ bdata = 0xf;
+ break;
+ }
+
+ /* Assign default FIFO mapping: SD0 to FIFO0, SD1 to FIFO1, etc. */
+ fifo_map = FIELD_PREP(FIFO0_MAP, 0) | FIELD_PREP(FIFO1_MAP, 1);
+ fifo_map |= FIELD_PREP(FIFO2_MAP, 2) | FIELD_PREP(FIFO3_MAP, 3);
+ mtk_hdmi_v2_hw_i2s_fifo_map(hdmi, fifo_map);
+ mtk_hdmi_v2_hw_i2s_ch_number(hdmi, bdata);
+
+ /*
+ * Set HDMI Audio packet layout indicator:
+ * Layout 0 is for two channels
+ * Layout 1 is for up to eight channels
+ */
+ if (chnum == 2)
+ regmap_set_bits(hdmi->regs, AIP_TXCTRL, AUD_LAYOUT_1);
+ else
+ regmap_clear_bits(hdmi->regs, AIP_TXCTRL, AUD_LAYOUT_1);
+}
+
+static void mtk_hdmi_i2s_data_fmt(struct mtk_hdmi *hdmi, unsigned char fmt)
+{
+ u32 val;
+
+ regmap_read(hdmi->regs, AIP_I2S_CTRL, &val);
+ val &= ~(WS_HIGH | I2S_1ST_BIT_NOSHIFT | JUSTIFY_RIGHT);
+
+ switch (fmt) {
+ case HDMI_I2S_MODE_RJT_24BIT:
+ case HDMI_I2S_MODE_RJT_16BIT:
+ val |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT | JUSTIFY_RIGHT);
+ break;
+ case HDMI_I2S_MODE_LJT_24BIT:
+ case HDMI_I2S_MODE_LJT_16BIT:
+ val |= (WS_HIGH | I2S_1ST_BIT_NOSHIFT);
+ break;
+ case HDMI_I2S_MODE_I2S_24BIT:
+ case HDMI_I2S_MODE_I2S_16BIT:
+ default:
+ break;
+ }
+
+ regmap_write(hdmi->regs, AIP_I2S_CTRL, val);
+}
+
+static inline void mtk_hdmi_i2s_sck_edge_rise(struct mtk_hdmi *hdmi, bool rise)
+{
+ if (rise)
+ regmap_set_bits(hdmi->regs, AIP_I2S_CTRL, SCK_EDGE_RISE);
+ else
+ regmap_clear_bits(hdmi->regs, AIP_I2S_CTRL, SCK_EDGE_RISE);
+}
+
+static inline void mtk_hdmi_i2s_cbit_order(struct mtk_hdmi *hdmi, unsigned int cbit)
+{
+ regmap_update_bits(hdmi->regs, AIP_I2S_CTRL, CBIT_ORDER_SAME, cbit);
+}
+
+static inline void mtk_hdmi_i2s_vbit(struct mtk_hdmi *hdmi, unsigned int vbit)
+{
+ /* V bit: 0 for PCM, 1 for Compressed data */
+ regmap_update_bits(hdmi->regs, AIP_I2S_CTRL, VBIT_COMPRESSED, vbit);
+}
+
+static inline void mtk_hdmi_i2s_data_direction(struct mtk_hdmi *hdmi, unsigned int is_lsb)
+{
+ regmap_update_bits(hdmi->regs, AIP_I2S_CTRL, I2S_DATA_DIR_LSB, is_lsb);
+}
+
+static inline void mtk_hdmi_v2_hw_audio_type(struct mtk_hdmi *hdmi, unsigned int spdif_i2s)
+{
+ regmap_update_bits(hdmi->regs, AIP_CTRL, SPDIF_EN, FIELD_PREP(SPDIF_EN, spdif_i2s));
+}
+
+static u8 mtk_hdmi_v2_get_i2s_ch_mapping(struct mtk_hdmi *hdmi, u8 channel_type)
+{
+ switch (channel_type) {
+ case HDMI_AUD_CHAN_TYPE_1_1:
+ case HDMI_AUD_CHAN_TYPE_2_1:
+ return 0x01;
+ case HDMI_AUD_CHAN_TYPE_3_0:
+ return 0x02;
+ case HDMI_AUD_CHAN_TYPE_3_1:
+ return 0x03;
+ case HDMI_AUD_CHAN_TYPE_3_0_LRS:
+ case HDMI_AUD_CHAN_TYPE_4_0:
+ return 0x08;
+ case HDMI_AUD_CHAN_TYPE_5_1:
+ return 0x0b;
+ case HDMI_AUD_CHAN_TYPE_4_1_CLRS:
+ case HDMI_AUD_CHAN_TYPE_6_0:
+ case HDMI_AUD_CHAN_TYPE_6_0_CS:
+ case HDMI_AUD_CHAN_TYPE_6_0_CH:
+ case HDMI_AUD_CHAN_TYPE_6_0_OH:
+ case HDMI_AUD_CHAN_TYPE_6_0_CHR:
+ return 0x0e;
+ case HDMI_AUD_CHAN_TYPE_1_0:
+ case HDMI_AUD_CHAN_TYPE_2_0:
+ case HDMI_AUD_CHAN_TYPE_3_1_LRS:
+ case HDMI_AUD_CHAN_TYPE_4_1:
+ case HDMI_AUD_CHAN_TYPE_5_0:
+ case HDMI_AUD_CHAN_TYPE_4_0_CLRS:
+ case HDMI_AUD_CHAN_TYPE_6_1:
+ case HDMI_AUD_CHAN_TYPE_6_1_CS:
+ case HDMI_AUD_CHAN_TYPE_6_1_CH:
+ case HDMI_AUD_CHAN_TYPE_6_1_OH:
+ case HDMI_AUD_CHAN_TYPE_6_1_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0:
+ case HDMI_AUD_CHAN_TYPE_7_0_LH_RH:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR:
+ case HDMI_AUD_CHAN_TYPE_7_0_LC_RC:
+ case HDMI_AUD_CHAN_TYPE_7_0_LW_RW:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS:
+ case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS:
+ case HDMI_AUD_CHAN_TYPE_7_0_CS_CH:
+ case HDMI_AUD_CHAN_TYPE_7_0_CS_OH:
+ case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0_CH_OH:
+ case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR:
+ case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS:
+ case HDMI_AUD_CHAN_TYPE_7_1:
+ case HDMI_AUD_CHAN_TYPE_7_1_LH_RH:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR:
+ case HDMI_AUD_CHAN_TYPE_7_1_LC_RC:
+ case HDMI_AUD_CHAN_TYPE_7_1_LW_RW:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS:
+ case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS:
+ case HDMI_AUD_CHAN_TYPE_7_1_CS_CH:
+ case HDMI_AUD_CHAN_TYPE_7_1_CS_OH:
+ case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_1_CH_OH:
+ case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR:
+ case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR:
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static inline void mtk_hdmi_v2_hw_i2s_ch_swap(struct mtk_hdmi *hdmi)
+{
+ regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_2UI_I2S_HI_WRITE,
+ FIELD_PREP(MAX_2UI_I2S_HI_WRITE, MAX_2UI_I2S_LFE_CC_SWAP));
+}
+
+static void mtk_hdmi_hbr_config(struct mtk_hdmi *hdmi, bool dsd_bypass)
+{
+ const u32 hbr_mask = SPDIF_EN | DSD_EN | HBRA_ON;
+
+ if (dsd_bypass) {
+ regmap_update_bits(hdmi->regs, AIP_CTRL, hbr_mask, HBRA_ON);
+ regmap_set_bits(hdmi->regs, AIP_CTRL, I2S_EN);
+ } else {
+ regmap_update_bits(hdmi->regs, AIP_CTRL, hbr_mask, SPDIF_EN);
+ regmap_set_bits(hdmi->regs, AIP_CTRL, SPDIF_INTERNAL_MODULE);
+ regmap_set_bits(hdmi->regs, AIP_CTRL, HBR_FROM_SPDIF);
+ regmap_set_bits(hdmi->regs, AIP_CTRL, CTS_CAL_N4);
+ }
+}
+
+static inline void mtk_hdmi_v2_hw_spdif_config(struct mtk_hdmi *hdmi)
+{
+ regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, WR_1UI_LOCK);
+ regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, FS_OVERRIDE_WRITE);
+ regmap_clear_bits(hdmi->regs, AIP_SPDIF_CTRL, WR_2UI_LOCK);
+
+ regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_1UI_WRITE,
+ FIELD_PREP(MAX_1UI_WRITE, 4));
+ regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_2UI_SPDIF_WRITE,
+ FIELD_PREP(MAX_2UI_SPDIF_WRITE, 9));
+ regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, AUD_ERR_THRESH,
+ FIELD_PREP(AUD_ERR_THRESH, 4));
+
+ regmap_set_bits(hdmi->regs, AIP_SPDIF_CTRL, I2S2DSD_EN);
+}
+
+static void mtk_hdmi_v2_aud_set_input(struct mtk_hdmi *hdmi)
+{
+ struct hdmi_audio_param *aud_param = &hdmi->aud_param;
+ struct hdmi_codec_params *codec_params = &aud_param->codec_params;
+ u8 i2s_ch_map;
+ u32 out_ch_map;
+
+ /* Write the default output channel map. CH0 maps to SD0, CH1 maps to SD1, etc */
+ out_ch_map = mtk_hdmi_v2_aud_output_channel_map(0, 1, 2, 3, 4, 5, 6, 7);
+ regmap_write(hdmi->regs, TOP_AUD_MAP, out_ch_map);
+
+ regmap_update_bits(hdmi->regs, AIP_SPDIF_CTRL, MAX_2UI_I2S_HI_WRITE, 0);
+ regmap_clear_bits(hdmi->regs, AIP_CTRL,
+ SPDIF_EN | DSD_EN | HBRA_ON | CTS_CAL_N4 |
+ HBR_FROM_SPDIF | SPDIF_INTERNAL_MODULE);
+ regmap_clear_bits(hdmi->regs, AIP_TXCTRL, DSD_MUTE_EN | AUD_LAYOUT_1);
+
+ if (aud_param->aud_input_type == HDMI_AUD_INPUT_I2S) {
+ switch (aud_param->aud_codec) {
+ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
+ case HDMI_AUDIO_CODING_TYPE_MLP:
+ mtk_hdmi_i2s_data_fmt(hdmi, aud_param->aud_i2s_fmt);
+ mtk_hdmi_hbr_config(hdmi, true);
+ break;
+ case HDMI_AUDIO_CODING_TYPE_DSD:
+ mtk_hdmi_audio_dsd_config(hdmi, codec_params->channels, 0);
+ mtk_hdmi_v2_hw_i2s_ch_mapping(hdmi, codec_params->channels, 1);
+ break;
+ default:
+ mtk_hdmi_i2s_data_fmt(hdmi, aud_param->aud_i2s_fmt);
+ mtk_hdmi_i2s_sck_edge_rise(hdmi, true);
+ mtk_hdmi_i2s_cbit_order(hdmi, CBIT_ORDER_SAME);
+ mtk_hdmi_i2s_vbit(hdmi, 0); /* PCM data */
+ mtk_hdmi_i2s_data_direction(hdmi, 0); /* MSB first */
+ mtk_hdmi_v2_hw_audio_type(hdmi, HDMI_AUD_INPUT_I2S);
+ i2s_ch_map = mtk_hdmi_v2_get_i2s_ch_mapping(hdmi,
+ aud_param->aud_input_chan_type);
+ mtk_hdmi_v2_hw_i2s_ch_mapping(hdmi, codec_params->channels, i2s_ch_map);
+ mtk_hdmi_v2_hw_i2s_ch_swap(hdmi);
+ }
+ } else {
+ if (codec_params->sample_rate == 768000 &&
+ (aud_param->aud_codec == HDMI_AUDIO_CODING_TYPE_DTS_HD ||
+ aud_param->aud_codec == HDMI_AUDIO_CODING_TYPE_MLP)) {
+ mtk_hdmi_hbr_config(hdmi, false);
+ } else {
+ mtk_hdmi_v2_hw_spdif_config(hdmi);
+ mtk_hdmi_v2_hw_i2s_ch_mapping(hdmi, 2, 0);
+ }
+ }
+}
+
+static inline void mtk_hdmi_v2_hw_audio_input_enable(struct mtk_hdmi *hdmi, bool ena)
+{
+ if (ena)
+ regmap_set_bits(hdmi->regs, AIP_CTRL, AUD_IN_EN);
+ else
+ regmap_clear_bits(hdmi->regs, AIP_CTRL, AUD_IN_EN);
+}
+
+static void mtk_hdmi_v2_aip_ctrl_init(struct mtk_hdmi *hdmi)
+{
+ regmap_set_bits(hdmi->regs, AIP_CTRL,
+ AUD_SEL_OWRT | NO_MCLK_CTSGEN_SEL | MCLK_EN | CTS_REQ_EN);
+ regmap_clear_bits(hdmi->regs, AIP_TPI_CTRL, TPI_AUDIO_LOOKUP_EN);
+}
+
+static void mtk_hdmi_v2_audio_reset(struct mtk_hdmi *hdmi, bool reset)
+{
+ const u32 arst_bits = RST4AUDIO | RST4AUDIO_FIFO | RST4AUDIO_ACR;
+
+ if (reset)
+ regmap_set_bits(hdmi->regs, AIP_TXCTRL, arst_bits);
+ else
+ regmap_clear_bits(hdmi->regs, AIP_TXCTRL, arst_bits);
+}
+
+static void mtk_hdmi_v2_aud_output_config(struct mtk_hdmi *hdmi,
+ struct drm_display_mode *display_mode)
+{
+ /* Shut down and reset the HDMI Audio HW to avoid glitching */
+ mtk_hdmi_v2_hw_aud_mute(hdmi, true);
+ mtk_hdmi_v2_hw_aud_enable(hdmi, false);
+ mtk_hdmi_v2_audio_reset(hdmi, true);
+
+ /* Configure the main hardware params and get out of reset */
+ mtk_hdmi_v2_aip_ctrl_init(hdmi);
+ mtk_hdmi_v2_aud_set_input(hdmi);
+ mtk_hdmi_v2_hw_aud_set_channel_status(hdmi);
+ mtk_hdmi_v2_setup_audio_infoframe(hdmi);
+ mtk_hdmi_v2_hw_audio_input_enable(hdmi, true);
+ mtk_hdmi_v2_audio_reset(hdmi, false);
+
+ /* Ignore N/CTS packet transmission requests and configure */
+ mtk_hdmi_v2_hw_ncts_enable(hdmi, false);
+ mtk_hdmi_v2_hw_aud_set_ncts(hdmi, hdmi->aud_param.codec_params.sample_rate,
+ display_mode->clock);
+
+ /* Wait for the HW to apply settings */
+ usleep_range(25, 50);
+
+ /* Hardware is fully configured: enable TX of N/CTS pkts and unmute */
+ mtk_hdmi_v2_hw_ncts_enable(hdmi, true);
+ mtk_hdmi_v2_hw_aud_enable(hdmi, true);
+ mtk_hdmi_v2_hw_aud_mute(hdmi, false);
+}
+
+static void mtk_hdmi_v2_change_video_resolution(struct mtk_hdmi *hdmi,
+ struct drm_connector_state *conn_state)
+{
+ mtk_hdmi_v2_hw_reset(hdmi);
+ mtk_hdmi_v2_set_sw_hpd(hdmi, true);
+ udelay(2);
+
+ regmap_write(hdmi->regs, HDCP_TOP_CTRL, 0);
+
+ /*
+ * Enable HDCP reauthentication interrupt: the HW uses this internally
+ * for the HPD state machine even if HDCP encryption is not enabled.
+ */
+ regmap_set_bits(hdmi->regs, TOP_INT_ENABLE00, HDCP2X_RX_REAUTH_REQ_DDCM_INT);
+
+ /* Enable hotplug and pord interrupts */
+ mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, true);
+
+ /* Force enabling HDCP HPD */
+ regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
+ regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
+
+ /* Set 8 bits per pixel */
+ regmap_update_bits(hdmi->regs, TOP_CFG00, TMDS_PACK_MODE,
+ FIELD_PREP(TMDS_PACK_MODE, TMDS_PACK_MODE_8BPP));
+ /* Disable generating deepcolor packets */
+ regmap_clear_bits(hdmi->regs, TOP_CFG00, DEEPCOLOR_PKT_EN);
+ /* Disable adding deepcolor information to the general packet */
+ regmap_clear_bits(hdmi->regs, TOP_MISC_CTLR, DEEP_COLOR_ADD);
+
+ if (hdmi->curr_conn->display_info.is_hdmi)
+ regmap_set_bits(hdmi->regs, TOP_CFG00, HDMI_MODE_HDMI);
+ else
+ regmap_clear_bits(hdmi->regs, TOP_CFG00, HDMI_MODE_HDMI);
+
+ udelay(5);
+ mtk_hdmi_v2_hw_vid_mute(hdmi, true);
+ mtk_hdmi_v2_hw_aud_mute(hdmi, true);
+ mtk_hdmi_v2_hw_gcp_avmute(hdmi, false);
+
+ regmap_update_bits(hdmi->regs, TOP_CFG01,
+ NULL_PKT_VSYNC_HIGH_EN | NULL_PKT_EN, NULL_PKT_VSYNC_HIGH_EN);
+ usleep_range(100, 150);
+
+ /* Enable scrambling if tmds clock is 340MHz or more */
+ mtk_hdmi_v2_enable_scrambling(hdmi, hdmi->mode.clock >= 340 * KILO);
+
+ switch (conn_state->hdmi.output_format) {
+ default:
+ case HDMI_COLORSPACE_RGB:
+ case HDMI_COLORSPACE_YUV444:
+ /* Disable YUV420 downsampling for RGB and YUV444 */
+ mtk_hdmi_yuv420_downsampling(hdmi, false);
+ break;
+ case HDMI_COLORSPACE_YUV422:
+ /*
+ * YUV420 downsampling is special and needs a bit of setup
+ * so we disable everything there before doing anything else.
+ *
+ * YUV422 downsampling instead just needs one bit to be set.
+ */
+ mtk_hdmi_yuv420_downsampling(hdmi, false);
+ regmap_set_bits(hdmi->regs, VID_DOWNSAMPLE_CONFIG,
+ C444_C422_CONFIG_ENABLE);
+ break;
+ case HDMI_COLORSPACE_YUV420:
+ mtk_hdmi_yuv420_downsampling(hdmi, true);
+ break;
+ };
+}
+
+static void mtk_hdmi_v2_output_set_display_mode(struct mtk_hdmi *hdmi,
+ struct drm_connector_state *conn_state,
+ struct drm_display_mode *mode)
+{
+ union phy_configure_opts opts = {
+ .dp = { .link_rate = hdmi->mode.clock * KILO }
+ };
+ int ret;
+
+ ret = phy_configure(hdmi->phy, &opts);
+ if (ret)
+ dev_err(hdmi->dev, "Setting clock=%d failed: %d", mode->clock, ret);
+
+ mtk_hdmi_v2_change_video_resolution(hdmi, conn_state);
+ mtk_hdmi_v2_aud_output_config(hdmi, mode);
+}
+
+static int mtk_hdmi_v2_clk_enable(struct mtk_hdmi *hdmi)
+{
+ int ret;
+
+ ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_SEL]);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_24M_SEL]);
+ if (ret)
+ goto disable_hdcp_clk;
+
+ ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_HDMI_APB_SEL]);
+ if (ret)
+ goto disable_hdcp_24m_clk;
+
+ ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI]);
+ if (ret)
+ goto disable_bus_clk;
+
+ return 0;
+
+disable_bus_clk:
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDMI_APB_SEL]);
+disable_hdcp_24m_clk:
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_24M_SEL]);
+disable_hdcp_clk:
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_SEL]);
+
+ return ret;
+}
+
+static void mtk_hdmi_v2_clk_disable(struct mtk_hdmi *hdmi)
+{
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI]);
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDMI_APB_SEL]);
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_24M_SEL]);
+ clk_disable_unprepare(hdmi->clk[MTK_HDMI_V2_CLK_HDCP_SEL]);
+}
+
+static enum hdmi_hpd_state mtk_hdmi_v2_hpd_pord_status(struct mtk_hdmi *hdmi)
+{
+ u8 hpd_pin_sta, pord_pin_sta;
+ u32 hpd_status;
+
+ regmap_read(hdmi->regs, HPD_DDC_STATUS, &hpd_status);
+ hpd_pin_sta = FIELD_GET(HPD_PIN_STA, hpd_status);
+ pord_pin_sta = FIELD_GET(PORD_PIN_STA, hpd_status);
+
+ /*
+ * Inform that the cable is plugged in (hpd_pin_sta) so that the
+ * sink can be powered on by switching the 5V VBUS as required by
+ * the HDMI spec for reading EDID and for HDMI Audio registers to
+ * be accessible.
+ *
+ * PORD detection succeeds only when the cable is plugged in and
+ * the sink is powered on: reaching that state means that the
+ * communication with the sink can be started.
+ *
+ * Please note that when the cable is plugged out the HPD pin will
+ * be the first one to fall, while PORD may still be in rise state
+ * for a few more milliseconds, so we decide HDMI_PLUG_OUT without
+ * checking PORD at all (we check only HPD falling for that).
+ */
+ if (hpd_pin_sta && pord_pin_sta)
+ return HDMI_PLUG_IN_AND_SINK_POWER_ON;
+ else if (hpd_pin_sta)
+ return HDMI_PLUG_IN_ONLY;
+ else
+ return HDMI_PLUG_OUT;
+}
+
+static irqreturn_t mtk_hdmi_v2_isr(int irq, void *arg)
+{
+ struct mtk_hdmi *hdmi = arg;
+ unsigned int irq_sta;
+ int ret = IRQ_HANDLED;
+
+ regmap_read(hdmi->regs, TOP_INT_STA00, &irq_sta);
+
+ /* Handle Hotplug Detection interrupts */
+ if (irq_sta & HPD_PORD_HWIRQS) {
+ /*
+ * Disable the HPD/PORD IRQs now and until thread done to
+ * avoid interrupt storm that could happen with bad cables
+ */
+ mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, false);
+ ret = IRQ_WAKE_THREAD;
+
+ /* Clear HPD/PORD irqs to avoid unwanted retriggering */
+ regmap_write(hdmi->regs, TOP_INT_CLR00, HPD_PORD_HWIRQS);
+ regmap_write(hdmi->regs, TOP_INT_CLR00, 0);
+ }
+
+ return ret;
+}
+
+static irqreturn_t __mtk_hdmi_v2_isr_thread(struct mtk_hdmi *hdmi)
+{
+ enum hdmi_hpd_state hpd;
+
+ hpd = mtk_hdmi_v2_hpd_pord_status(hdmi);
+ if (hpd != hdmi->hpd) {
+ struct drm_encoder *encoder = hdmi->bridge.encoder;
+
+ hdmi->hpd = hpd;
+
+ if (encoder && encoder->dev)
+ drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev);
+ }
+
+ mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, true);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_hdmi_v2_isr_thread(int irq, void *arg)
+{
+ struct mtk_hdmi *hdmi = arg;
+
+ /*
+ * Debounce HDMI monitor HPD status.
+ * Empirical testing shows that 30ms is enough wait
+ */
+ msleep(30);
+
+ return __mtk_hdmi_v2_isr_thread(hdmi);
+}
+
+static int mtk_hdmi_v2_enable(struct mtk_hdmi *hdmi)
+{
+ bool was_active = pm_runtime_active(hdmi->dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(hdmi->dev);
+ if (ret) {
+ dev_err(hdmi->dev, "Cannot resume HDMI\n");
+ return ret;
+ }
+
+ ret = mtk_hdmi_v2_clk_enable(hdmi);
+ if (ret) {
+ pm_runtime_put(hdmi->dev);
+ return ret;
+ }
+
+ if (!was_active) {
+ mtk_hdmi_v2_hw_reset(hdmi);
+ mtk_hdmi_v2_set_sw_hpd(hdmi, true);
+ }
+
+ return 0;
+}
+
+static void mtk_hdmi_v2_disable(struct mtk_hdmi *hdmi)
+{
+ mtk_hdmi_v2_clk_disable(hdmi);
+ pm_runtime_put_sync(hdmi->dev);
+}
+
+/*
+ * Bridge callbacks
+ */
+
+static int mtk_hdmi_v2_bridge_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+ int ret;
+
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+ DRM_ERROR("The flag DRM_BRIDGE_ATTACH_NO_CONNECTOR must be supplied\n");
+ return -EINVAL;
+ }
+ if (hdmi->next_bridge) {
+ ret = drm_bridge_attach(encoder, hdmi->next_bridge, bridge, flags);
+ if (ret)
+ return ret;
+ }
+
+ ret = mtk_hdmi_v2_enable(hdmi);
+ if (ret)
+ return ret;
+
+ /* Enable Hotplug and Pord pins internal debouncing */
+ regmap_set_bits(hdmi->regs, HPD_DDC_CTRL,
+ HPD_DDC_HPD_DBNC_EN | HPD_DDC_PORD_DBNC_EN);
+
+ irq_clear_status_flags(hdmi->irq, IRQ_NOAUTOEN);
+ enable_irq(hdmi->irq);
+
+ /*
+ * Check if any HDMI monitor was connected before probing this driver
+ * and/or attaching the bridge, without debouncing: if so, we want to
+ * notify the DRM so that we start outputting an image ASAP.
+ * Note that calling the ISR thread function will also perform a HW
+ * registers write that enables both the HPD and Pord interrupts.
+ */
+ __mtk_hdmi_v2_isr_thread(hdmi);
+
+ mtk_hdmi_v2_disable(hdmi);
+
+ return 0;
+}
+
+static void mtk_hdmi_v2_bridge_detach(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ WARN_ON(pm_runtime_active(hdmi->dev));
+
+ /* The controller is already powered off, just disable irq here */
+ disable_irq(hdmi->irq);
+}
+
+static void mtk_hdmi_v2_handle_plugged_change(struct mtk_hdmi *hdmi, bool plugged)
+{
+ mutex_lock(&hdmi->update_plugged_status_lock);
+ if (hdmi->plugged_cb && hdmi->codec_dev)
+ hdmi->plugged_cb(hdmi->codec_dev, plugged);
+ mutex_unlock(&hdmi->update_plugged_status_lock);
+}
+
+static void mtk_hdmi_v2_bridge_pre_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+ struct drm_connector_state *conn_state;
+ union phy_configure_opts opts = {
+ .dp = { .link_rate = hdmi->mode.clock * KILO }
+ };
+ int ret;
+
+ /* Power on the controller before trying to write to registers */
+ ret = mtk_hdmi_v2_enable(hdmi);
+ if (WARN_ON(ret))
+ return;
+
+ /* Retrieve the connector through the atomic state */
+ hdmi->curr_conn = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+
+ conn_state = drm_atomic_get_new_connector_state(state, hdmi->curr_conn);
+ if (WARN_ON(!conn_state))
+ return;
+
+ /*
+ * Preconfigure the HDMI controller and the HDMI PHY at pre_enable
+ * stage to make sure that this IP is ready and clocked before the
+ * mtk_dpi gets powered on and before it enables the output.
+ */
+ mtk_hdmi_v2_output_set_display_mode(hdmi, conn_state, &hdmi->mode);
+
+ /* Reconfigure phy clock link with appropriate rate */
+ phy_configure(hdmi->phy, &opts);
+
+ /* Power on the PHY here to make sure that DPI_HDMI is clocked */
+ phy_power_on(hdmi->phy);
+
+ hdmi->powered = true;
+}
+
+static void mtk_hdmi_v2_bridge_enable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+ int ret;
+
+ if (WARN_ON(!hdmi->powered))
+ return;
+
+ ret = drm_atomic_helper_connector_hdmi_update_infoframes(hdmi->curr_conn, state);
+ if (ret)
+ dev_err(hdmi->dev, "Could not update infoframes: %d\n", ret);
+
+ mtk_hdmi_v2_hw_vid_mute(hdmi, false);
+
+ /* signal the connect event to audio codec */
+ mtk_hdmi_v2_handle_plugged_change(hdmi, true);
+
+ hdmi->enabled = true;
+}
+
+static void mtk_hdmi_v2_bridge_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ if (!hdmi->enabled)
+ return;
+
+ mtk_hdmi_v2_hw_gcp_avmute(hdmi, true);
+ msleep(50);
+ mtk_hdmi_v2_hw_vid_mute(hdmi, true);
+ mtk_hdmi_v2_hw_aud_mute(hdmi, true);
+ msleep(50);
+
+ hdmi->enabled = false;
+}
+
+static void mtk_hdmi_v2_bridge_post_disable(struct drm_bridge *bridge,
+ struct drm_atomic_state *state)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ if (!hdmi->powered)
+ return;
+
+ phy_power_off(hdmi->phy);
+ hdmi->powered = false;
+
+ /* signal the disconnect event to audio codec */
+ mtk_hdmi_v2_handle_plugged_change(hdmi, false);
+
+ /* Power off */
+ mtk_hdmi_v2_disable(hdmi);
+}
+
+static enum drm_connector_status mtk_hdmi_v2_bridge_detect(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ return hdmi->hpd != HDMI_PLUG_OUT ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static const struct drm_edid *mtk_hdmi_v2_bridge_edid_read(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ return drm_edid_read(connector);
+}
+
+static void mtk_hdmi_v2_hpd_enable(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+ int ret;
+
+ ret = mtk_hdmi_v2_enable(hdmi);
+ if (ret) {
+ dev_err(hdmi->dev, "Cannot power on controller for HPD: %d\n", ret);
+ return;
+ }
+
+ mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, true);
+}
+
+static void mtk_hdmi_v2_hpd_disable(struct drm_bridge *bridge)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ mtk_hdmi_v2_enable_hpd_pord_irq(hdmi, false);
+ mtk_hdmi_v2_disable(hdmi);
+}
+
+static int mtk_hdmi_v2_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate)
+{
+ if (mode->clock < MTK_HDMI_V2_CLOCK_MIN)
+ return MODE_CLOCK_LOW;
+ else if (mode->clock > MTK_HDMI_V2_CLOCK_MAX)
+ return MODE_CLOCK_HIGH;
+ else
+ return MODE_OK;
+}
+
+static int mtk_hdmi_v2_hdmi_clear_infoframe(struct drm_bridge *bridge,
+ enum hdmi_infoframe_type type)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN_WR | AUD_EN);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
+ break;
+ case HDMI_INFOFRAME_TYPE_AVI:
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
+ break;
+ case HDMI_INFOFRAME_TYPE_SPD:
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
+ break;
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
+ regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
+ break;
+ case HDMI_INFOFRAME_TYPE_DRM:
+ default:
+ break;
+ };
+
+ return 0;
+}
+
+static int mtk_hdmi_v2_hdmi_write_infoframe(struct drm_bridge *bridge,
+ enum hdmi_infoframe_type type,
+ const u8 *buffer, size_t len)
+{
+ struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
+
+ switch (type) {
+ case HDMI_INFOFRAME_TYPE_AUDIO:
+ mtk_hdmi_v2_hw_write_audio_infoframe(hdmi, buffer);
+ break;
+ case HDMI_INFOFRAME_TYPE_AVI:
+ mtk_hdmi_v2_hw_write_avi_infoframe(hdmi, buffer);
+ break;
+ case HDMI_INFOFRAME_TYPE_SPD:
+ mtk_hdmi_v2_hw_write_spd_infoframe(hdmi, buffer);
+ break;
+ case HDMI_INFOFRAME_TYPE_VENDOR:
+ mtk_hdmi_v2_hw_write_vendor_infoframe(hdmi, buffer);
+ break;
+ case HDMI_INFOFRAME_TYPE_DRM:
+ default:
+ dev_err(hdmi->dev, "Unsupported HDMI infoframe type %u\n", type);
+ break;
+ };
+
+ return 0;
+}
+
+static int mtk_hdmi_v2_set_abist(struct mtk_hdmi *hdmi, bool enable)
+{
+ struct drm_display_mode *mode = &hdmi->mode;
+ int abist_format = -EINVAL;
+ bool interlaced;
+
+ if (!enable) {
+ regmap_clear_bits(hdmi->regs, TOP_CFG00, HDMI_ABIST_ENABLE);
+ return 0;
+ }
+
+ if (!mode->hdisplay || !mode->vdisplay)
+ return -EINVAL;
+
+ interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+
+ switch (mode->hdisplay) {
+ case 720:
+ if (mode->vdisplay == 480)
+ abist_format = 2;
+ else if (mode->vdisplay == 576)
+ abist_format = 11;
+ break;
+ case 1280:
+ if (mode->vdisplay == 720)
+ abist_format = 3;
+ break;
+ case 1440:
+ if (mode->vdisplay == 480)
+ abist_format = interlaced ? 5 : 9;
+ else if (mode->vdisplay == 576)
+ abist_format = interlaced ? 14 : 18;
+ break;
+ case 1920:
+ if (mode->vdisplay == 1080)
+ abist_format = interlaced ? 4 : 10;
+ break;
+ case 3840:
+ if (mode->vdisplay == 2160)
+ abist_format = 25;
+ break;
+ case 4096:
+ if (mode->vdisplay == 2160)
+ abist_format = 26;
+ break;
+ default:
+ break;
+ }
+ if (abist_format < 0)
+ return abist_format;
+
+ regmap_update_bits(hdmi->regs, TOP_CFG00, HDMI_ABIST_VIDEO_FORMAT,
+ FIELD_PREP(HDMI_ABIST_VIDEO_FORMAT, abist_format));
+ regmap_set_bits(hdmi->regs, TOP_CFG00, HDMI_ABIST_ENABLE);
+ return 0;
+}
+
+static int mtk_hdmi_v2_debug_abist_show(struct seq_file *m, void *arg)
+{
+ struct mtk_hdmi *hdmi = m->private;
+ bool en;
+ u32 val;
+ int ret;
+
+ if (!hdmi)
+ return -EINVAL;
+
+ ret = regmap_read(hdmi->regs, TOP_CFG00, &val);
+ if (ret)
+ return ret;
+
+ en = FIELD_GET(HDMI_ABIST_ENABLE, val);
+
+ seq_printf(m, "HDMI Automated Built-In Self Test: %s\n",
+ en ? "Enabled" : "Disabled");
+
+ return 0;
+}
+
+static ssize_t mtk_hdmi_v2_debug_abist_write(struct file *file,
+ const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ struct seq_file *m = file->private_data;
+ int ret;
+ u32 en;
+
+ if (!m || !m->private || *offp)
+ return -EINVAL;
+
+ ret = kstrtouint_from_user(ubuf, len, 0, &en);
+ if (ret)
+ return ret;
+
+ if (en < 0 || en > 1)
+ return -EINVAL;
+
+ mtk_hdmi_v2_set_abist((struct mtk_hdmi *)m->private, en);
+ return len;
+}
+
+static int mtk_hdmi_v2_debug_abist_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, mtk_hdmi_v2_debug_abist_show, inode->i_private);
+}
+
+static const struct file_operations mtk_hdmi_debug_abist_fops = {
+ .owner = THIS_MODULE,
+ .open = mtk_hdmi_v2_debug_abist_open,
+ .read = seq_read,
+ .write = mtk_hdmi_v2_debug_abist_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void mtk_hdmi_v2_debugfs_init(struct drm_bridge *bridge, struct dentry *root)
+{
+ struct mtk_hdmi *dpi = hdmi_ctx_from_bridge(bridge);
+
+ debugfs_create_file("hdmi_abist", 0640, root, dpi, &mtk_hdmi_debug_abist_fops);
+}
+
+static const struct drm_bridge_funcs mtk_v2_hdmi_bridge_funcs = {
+ .attach = mtk_hdmi_v2_bridge_attach,
+ .detach = mtk_hdmi_v2_bridge_detach,
+ .mode_fixup = mtk_hdmi_bridge_mode_fixup,
+ .mode_set = mtk_hdmi_bridge_mode_set,
+ .atomic_pre_enable = mtk_hdmi_v2_bridge_pre_enable,
+ .atomic_enable = mtk_hdmi_v2_bridge_enable,
+ .atomic_disable = mtk_hdmi_v2_bridge_disable,
+ .atomic_post_disable = mtk_hdmi_v2_bridge_post_disable,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .detect = mtk_hdmi_v2_bridge_detect,
+ .edid_read = mtk_hdmi_v2_bridge_edid_read,
+ .hpd_enable = mtk_hdmi_v2_hpd_enable,
+ .hpd_disable = mtk_hdmi_v2_hpd_disable,
+ .hdmi_tmds_char_rate_valid = mtk_hdmi_v2_hdmi_tmds_char_rate_valid,
+ .hdmi_clear_infoframe = mtk_hdmi_v2_hdmi_clear_infoframe,
+ .hdmi_write_infoframe = mtk_hdmi_v2_hdmi_write_infoframe,
+ .debugfs_init = mtk_hdmi_v2_debugfs_init,
+};
+
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_hdmi_v2_audio_hook_plugged_cb(struct device *dev, void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+ bool plugged;
+
+ if (!hdmi)
+ return -ENODEV;
+
+ mtk_hdmi_audio_set_plugged_cb(hdmi, fn, codec_dev);
+ plugged = (hdmi->hpd == HDMI_PLUG_IN_AND_SINK_POWER_ON);
+ mtk_hdmi_v2_handle_plugged_change(hdmi, plugged);
+
+ return 0;
+}
+
+static int mtk_hdmi_v2_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ if (hdmi->audio_enable) {
+ mtk_hdmi_audio_params(hdmi, daifmt, params);
+ mtk_hdmi_v2_aud_output_config(hdmi, &hdmi->mode);
+ }
+ return 0;
+}
+
+static int mtk_hdmi_v2_audio_startup(struct device *dev, void *data)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ mtk_hdmi_v2_hw_aud_enable(hdmi, true);
+ hdmi->audio_enable = true;
+
+ return 0;
+}
+
+static void mtk_hdmi_v2_audio_shutdown(struct device *dev, void *data)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ hdmi->audio_enable = false;
+ mtk_hdmi_v2_hw_aud_enable(hdmi, false);
+}
+
+static int mtk_hdmi_v2_audio_mute(struct device *dev, void *data, bool enable, int dir)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ mtk_hdmi_v2_hw_aud_mute(hdmi, enable);
+
+ return 0;
+}
+
+static const struct hdmi_codec_ops mtk_hdmi_v2_audio_codec_ops = {
+ .hw_params = mtk_hdmi_v2_audio_hw_params,
+ .audio_startup = mtk_hdmi_v2_audio_startup,
+ .audio_shutdown = mtk_hdmi_v2_audio_shutdown,
+ .mute_stream = mtk_hdmi_v2_audio_mute,
+ .get_eld = mtk_hdmi_audio_get_eld,
+ .hook_plugged_cb = mtk_hdmi_v2_audio_hook_plugged_cb,
+};
+
+static __maybe_unused int mtk_hdmi_v2_suspend(struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ mtk_hdmi_v2_disable(hdmi);
+
+ return 0;
+}
+
+static __maybe_unused int mtk_hdmi_v2_resume(struct device *dev)
+{
+ struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
+
+ return mtk_hdmi_v2_enable(hdmi);
+}
+
+static SIMPLE_DEV_PM_OPS(mtk_hdmi_v2_pm_ops, mtk_hdmi_v2_suspend, mtk_hdmi_v2_resume);
+
+static const struct mtk_hdmi_ver_conf mtk_hdmi_conf_v2 = {
+ .bridge_funcs = &mtk_v2_hdmi_bridge_funcs,
+ .codec_ops = &mtk_hdmi_v2_audio_codec_ops,
+ .mtk_hdmi_clock_names = mtk_hdmi_v2_clk_names,
+ .num_clocks = MTK_HDMI_V2_CLK_COUNT,
+ .interlace_allowed = true,
+};
+
+static const struct mtk_hdmi_conf mtk_hdmi_conf_mt8188 = {
+ .ver_conf = &mtk_hdmi_conf_v2,
+ .reg_hdmi_tx_cfg = HDMITX_CONFIG_MT8188
+};
+
+static const struct mtk_hdmi_conf mtk_hdmi_conf_mt8195 = {
+ .ver_conf = &mtk_hdmi_conf_v2,
+ .reg_hdmi_tx_cfg = HDMITX_CONFIG_MT8195
+};
+
+static int mtk_hdmi_v2_probe(struct platform_device *pdev)
+{
+ struct mtk_hdmi *hdmi;
+ int ret;
+
+ /* Populate HDMI sub-devices if present */
+ ret = devm_of_platform_populate(&pdev->dev);
+ if (ret)
+ return ret;
+
+ hdmi = mtk_hdmi_common_probe(pdev);
+ if (IS_ERR(hdmi))
+ return PTR_ERR(hdmi);
+
+ hdmi->hpd = HDMI_PLUG_OUT;
+
+ /* Disable all HW interrupts at probe stage */
+ mtk_hdmi_v2_hwirq_disable(hdmi);
+
+ /*
+ * In case bootloader leaves HDMI enabled before booting, make
+ * sure that any interrupt that was left is cleared by setting
+ * all bits in the INT_CLR registers for all 32+19 interrupts.
+ */
+ regmap_write(hdmi->regs, TOP_INT_CLR00, GENMASK(31, 0));
+ regmap_write(hdmi->regs, TOP_INT_CLR01, GENMASK(18, 0));
+
+ /* Restore interrupt clearing registers to zero */
+ regmap_write(hdmi->regs, TOP_INT_CLR00, 0);
+ regmap_write(hdmi->regs, TOP_INT_CLR01, 0);
+
+ /*
+ * Install the ISR but keep it disabled: as the interrupts are
+ * being set up in the .bridge_attach() callback which will
+ * enable both the right HW IRQs and the ISR.
+ */
+ irq_set_status_flags(hdmi->irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(&pdev->dev, hdmi->irq, mtk_hdmi_v2_isr,
+ mtk_hdmi_v2_isr_thread,
+ IRQ_TYPE_LEVEL_HIGH,
+ dev_name(&pdev->dev), hdmi);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Cannot request IRQ\n");
+
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Cannot enable Runtime PM\n");
+
+ return 0;
+}
+
+static void mtk_hdmi_v2_remove(struct platform_device *pdev)
+{
+ struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
+
+ i2c_put_adapter(hdmi->ddc_adpt);
+}
+
+static const struct of_device_id mtk_drm_hdmi_v2_of_ids[] = {
+ { .compatible = "mediatek,mt8188-hdmi-tx", .data = &mtk_hdmi_conf_mt8188 },
+ { .compatible = "mediatek,mt8195-hdmi-tx", .data = &mtk_hdmi_conf_mt8195 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_v2_of_ids);
+
+static struct platform_driver mtk_hdmi_v2_driver = {
+ .probe = mtk_hdmi_v2_probe,
+ .remove = mtk_hdmi_v2_remove,
+ .driver = {
+ .name = "mediatek-drm-hdmi-v2",
+ .of_match_table = mtk_drm_hdmi_v2_of_ids,
+ .pm = &mtk_hdmi_v2_pm_ops,
+ },
+};
+module_platform_driver(mtk_hdmi_v2_driver);
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>>");
+MODULE_DESCRIPTION("MediaTek HDMIv2 Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DRM_MTK_HDMI");
diff --git a/drivers/gpu/drm/mediatek/mtk_plane.c b/drivers/gpu/drm/mediatek/mtk_plane.c
index 02349bd44001..5043e0377270 100644
--- a/drivers/gpu/drm/mediatek/mtk_plane.c
+++ b/drivers/gpu/drm/mediatek/mtk_plane.c
@@ -11,6 +11,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <linux/align.h>
#include "mtk_crtc.h"
@@ -21,9 +22,6 @@
static const u64 modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
- DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
- AFBC_FORMAT_MOD_SPLIT |
- AFBC_FORMAT_MOD_SPARSE),
DRM_FORMAT_MOD_INVALID,
};
@@ -71,26 +69,7 @@ static bool mtk_plane_format_mod_supported(struct drm_plane *plane,
uint32_t format,
uint64_t modifier)
{
- if (modifier == DRM_FORMAT_MOD_LINEAR)
- return true;
-
- if (modifier != DRM_FORMAT_MOD_ARM_AFBC(
- AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 |
- AFBC_FORMAT_MOD_SPLIT |
- AFBC_FORMAT_MOD_SPARSE))
- return false;
-
- if (format != DRM_FORMAT_XRGB8888 &&
- format != DRM_FORMAT_ARGB8888 &&
- format != DRM_FORMAT_BGRX8888 &&
- format != DRM_FORMAT_BGRA8888 &&
- format != DRM_FORMAT_ABGR8888 &&
- format != DRM_FORMAT_XBGR8888 &&
- format != DRM_FORMAT_RGB888 &&
- format != DRM_FORMAT_BGR888)
- return false;
-
- return true;
+ return modifier == DRM_FORMAT_MOD_LINEAR;
}
static void mtk_plane_destroy_state(struct drm_plane *plane,
@@ -122,7 +101,8 @@ static int mtk_plane_atomic_async_check(struct drm_plane *plane,
if (ret)
return ret;
- crtc_state = drm_atomic_get_existing_crtc_state(state, new_plane_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state,
+ new_plane_state->crtc);
return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
DRM_PLANE_NO_SCALING,
diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c
index 7f98de38842b..783572b16963 100644
--- a/drivers/gpu/drm/meson/meson_overlay.c
+++ b/drivers/gpu/drm/meson/meson_overlay.c
@@ -16,6 +16,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "meson_overlay.h"
#include "meson_registers.h"
diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c
index b43ac61201f3..f8d0e0874a5d 100644
--- a/drivers/gpu/drm/meson/meson_plane.c
+++ b/drivers/gpu/drm/meson/meson_plane.c
@@ -20,6 +20,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "meson_plane.h"
#include "meson_registers.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 32cd8ac018c0..a32be27c39e8 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -20,6 +20,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_module.h>
#include <drm/drm_pciids.h>
+#include <drm/drm_print.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200.c b/drivers/gpu/drm/mgag200/mgag200_g200.c
index f874e2949840..a5e291b344db 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200.c
@@ -7,6 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh.c b/drivers/gpu/drm/mgag200/mgag200_g200eh.c
index e2305f8e00f8..d2aa931f579d 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200eh.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200eh.c
@@ -7,6 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh3.c b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c
index 11ae76eb081d..7bea7a728f56 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200eh3.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c
@@ -6,6 +6,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh5.c b/drivers/gpu/drm/mgag200/mgag200_g200eh5.c
index e2a2942a80a0..36da6529d74f 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200eh5.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200eh5.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mgag200/mgag200_g200er.c
index 23debc70dc54..8fa8fe943abf 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200er.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c
@@ -7,6 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mgag200/mgag200_g200ev.c
index f8796e2b7a0f..3fadbeb10af9 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200ev.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c
@@ -7,6 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ew3.c b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c
index 31624c9ab7b7..e387a455eae5 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200ew3.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c
@@ -6,6 +6,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c
index e80da12ba1fe..a0ac19ee0353 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200se.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c
@@ -7,6 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200wb.c b/drivers/gpu/drm/mgag200/mgag200_g200wb.c
index a0e7b9ad46cd..d847fa8ded8c 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200wb.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200wb.c
@@ -7,6 +7,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_drv.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_vga.c b/drivers/gpu/drm/mgag200/mgag200_vga.c
index 60568f32736d..b07c1362ddd4 100644
--- a/drivers/gpu/drm/mgag200/mgag200_vga.c
+++ b/drivers/gpu/drm/mgag200/mgag200_vga.c
@@ -2,6 +2,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_ddc.h"
diff --git a/drivers/gpu/drm/mgag200/mgag200_vga_bmc.c b/drivers/gpu/drm/mgag200/mgag200_vga_bmc.c
index a5a3ac108bd5..a855f1734316 100644
--- a/drivers/gpu/drm/mgag200/mgag200_vga_bmc.c
+++ b/drivers/gpu/drm/mgag200/mgag200_vga_bmc.c
@@ -3,6 +3,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "mgag200_ddc.h"
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 0c0dfb25f01b..8aa7d07303fb 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -24,6 +24,7 @@ adreno-y := \
adreno/a6xx_gmu.o \
adreno/a6xx_hfi.o \
adreno/a6xx_preempt.o \
+ adreno/a8xx_gpu.o \
adreno-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o \
@@ -201,6 +202,7 @@ ADRENO_HEADERS = \
generated/a6xx_perfcntrs.xml.h \
generated/a7xx_enums.xml.h \
generated/a7xx_perfcntrs.xml.h \
+ generated/a8xx_enums.xml.h \
generated/a6xx_gmu.xml.h \
generated/adreno_common.xml.h \
generated/adreno_pm4.xml.h \
diff --git a/drivers/gpu/drm/msm/adreno/a2xx_catalog.c b/drivers/gpu/drm/msm/adreno/a2xx_catalog.c
index 5ddd015f930d..e9dbf3ddf89e 100644
--- a/drivers/gpu/drm/msm/adreno/a2xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a2xx_catalog.c
@@ -7,6 +7,7 @@
*/
#include "adreno_gpu.h"
+#include "a2xx_gpu.h"
static const struct adreno_info a2xx_gpus[] = {
{
@@ -19,7 +20,7 @@ static const struct adreno_info a2xx_gpus[] = {
},
.gmem = SZ_256K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a2xx_gpu_init,
+ .funcs = &a2xx_gpu_funcs,
}, { /* a200 on i.mx51 has only 128kib gmem */
.chip_ids = ADRENO_CHIP_IDS(0x02000001),
.family = ADRENO_2XX_GEN1,
@@ -30,7 +31,7 @@ static const struct adreno_info a2xx_gpus[] = {
},
.gmem = SZ_128K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a2xx_gpu_init,
+ .funcs = &a2xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x02020000),
.family = ADRENO_2XX_GEN2,
@@ -41,7 +42,7 @@ static const struct adreno_info a2xx_gpus[] = {
},
.gmem = SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a2xx_gpu_init,
+ .funcs = &a2xx_gpu_funcs,
}
};
DECLARE_ADRENO_GPULIST(a2xx);
diff --git a/drivers/gpu/drm/msm/adreno/a2xx_gpu.c b/drivers/gpu/drm/msm/adreno/a2xx_gpu.c
index ec38db45d8a3..1b1ee14b65cf 100644
--- a/drivers/gpu/drm/msm/adreno/a2xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a2xx_gpu.c
@@ -234,7 +234,7 @@ static int a2xx_hw_init(struct msm_gpu *gpu)
* word (0x20xxxx for A200, 0x220xxx for A220, 0x225xxx for A225).
* Older firmware files, which lack protection support, have 0 instead.
*/
- if (ptr[1] == 0) {
+ if (ptr[1] == 0 && !a2xx_gpu->protection_disabled) {
dev_warn(gpu->dev->dev,
"Legacy firmware detected, disabling protection support\n");
a2xx_gpu->protection_disabled = true;
@@ -486,39 +486,18 @@ static u32 a2xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
return ring->memptrs->rptr;
}
-static const struct adreno_gpu_funcs funcs = {
- .base = {
- .get_param = adreno_get_param,
- .set_param = adreno_set_param,
- .hw_init = a2xx_hw_init,
- .pm_suspend = msm_gpu_pm_suspend,
- .pm_resume = msm_gpu_pm_resume,
- .recover = a2xx_recover,
- .submit = a2xx_submit,
- .active_ring = adreno_active_ring,
- .irq = a2xx_irq,
- .destroy = a2xx_destroy,
-#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
- .show = adreno_show,
-#endif
- .gpu_state_get = a2xx_gpu_state_get,
- .gpu_state_put = adreno_gpu_state_put,
- .create_vm = a2xx_create_vm,
- .get_rptr = a2xx_get_rptr,
- },
-};
-
static const struct msm_gpu_perfcntr perfcntrs[] = {
/* TODO */
};
-struct msm_gpu *a2xx_gpu_init(struct drm_device *dev)
+static struct msm_gpu *a2xx_gpu_init(struct drm_device *dev)
{
struct a2xx_gpu *a2xx_gpu = NULL;
struct adreno_gpu *adreno_gpu;
struct msm_gpu *gpu;
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
+ struct adreno_platform_config *config = pdev->dev.platform_data;
int ret;
if (!pdev) {
@@ -539,7 +518,7 @@ struct msm_gpu *a2xx_gpu_init(struct drm_device *dev)
gpu->perfcntrs = perfcntrs;
gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs);
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, 1);
if (ret)
goto fail;
@@ -558,3 +537,26 @@ fail:
return ERR_PTR(ret);
}
+
+const struct adreno_gpu_funcs a2xx_gpu_funcs = {
+ .base = {
+ .get_param = adreno_get_param,
+ .set_param = adreno_set_param,
+ .hw_init = a2xx_hw_init,
+ .pm_suspend = msm_gpu_pm_suspend,
+ .pm_resume = msm_gpu_pm_resume,
+ .recover = a2xx_recover,
+ .submit = a2xx_submit,
+ .active_ring = adreno_active_ring,
+ .irq = a2xx_irq,
+ .destroy = a2xx_destroy,
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
+ .show = adreno_show,
+#endif
+ .gpu_state_get = a2xx_gpu_state_get,
+ .gpu_state_put = adreno_gpu_state_put,
+ .create_vm = a2xx_create_vm,
+ .get_rptr = a2xx_get_rptr,
+ },
+ .init = a2xx_gpu_init,
+};
diff --git a/drivers/gpu/drm/msm/adreno/a2xx_gpu.h b/drivers/gpu/drm/msm/adreno/a2xx_gpu.h
index 53702f19990f..162ef98951f5 100644
--- a/drivers/gpu/drm/msm/adreno/a2xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a2xx_gpu.h
@@ -19,6 +19,8 @@ struct a2xx_gpu {
};
#define to_a2xx_gpu(x) container_of(x, struct a2xx_gpu, base)
+extern const struct adreno_gpu_funcs a2xx_gpu_funcs;
+
struct msm_mmu *a2xx_gpummu_new(struct device *dev, struct msm_gpu *gpu);
void a2xx_gpummu_params(struct msm_mmu *mmu, dma_addr_t *pt_base,
dma_addr_t *tran_error);
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_catalog.c b/drivers/gpu/drm/msm/adreno/a3xx_catalog.c
index 1498e6532f62..6ae8716fc08a 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_catalog.c
@@ -7,6 +7,7 @@
*/
#include "adreno_gpu.h"
+#include "a3xx_gpu.h"
static const struct adreno_info a3xx_gpus[] = {
{
@@ -18,7 +19,7 @@ static const struct adreno_info a3xx_gpus[] = {
},
.gmem = SZ_128K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a3xx_gpu_init,
+ .funcs = &a3xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x03000520),
.family = ADRENO_3XX,
@@ -29,7 +30,7 @@ static const struct adreno_info a3xx_gpus[] = {
},
.gmem = SZ_256K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a3xx_gpu_init,
+ .funcs = &a3xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x03000600),
.family = ADRENO_3XX,
@@ -40,7 +41,7 @@ static const struct adreno_info a3xx_gpus[] = {
},
.gmem = SZ_128K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a3xx_gpu_init,
+ .funcs = &a3xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x03000620),
.family = ADRENO_3XX,
@@ -51,7 +52,7 @@ static const struct adreno_info a3xx_gpus[] = {
},
.gmem = SZ_128K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a3xx_gpu_init,
+ .funcs = &a3xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(
0x03020000,
@@ -66,7 +67,7 @@ static const struct adreno_info a3xx_gpus[] = {
},
.gmem = SZ_512K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a3xx_gpu_init,
+ .funcs = &a3xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(
0x03030000,
@@ -81,7 +82,7 @@ static const struct adreno_info a3xx_gpus[] = {
},
.gmem = SZ_1M,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a3xx_gpu_init,
+ .funcs = &a3xx_gpu_funcs,
}
};
DECLARE_ADRENO_GPULIST(a3xx);
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
index a956cd79195e..f22d33e99e81 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c
@@ -508,29 +508,6 @@ static u32 a3xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
return ring->memptrs->rptr;
}
-static const struct adreno_gpu_funcs funcs = {
- .base = {
- .get_param = adreno_get_param,
- .set_param = adreno_set_param,
- .hw_init = a3xx_hw_init,
- .pm_suspend = msm_gpu_pm_suspend,
- .pm_resume = msm_gpu_pm_resume,
- .recover = a3xx_recover,
- .submit = a3xx_submit,
- .active_ring = adreno_active_ring,
- .irq = a3xx_irq,
- .destroy = a3xx_destroy,
-#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
- .show = adreno_show,
-#endif
- .gpu_busy = a3xx_gpu_busy,
- .gpu_state_get = a3xx_gpu_state_get,
- .gpu_state_put = adreno_gpu_state_put,
- .create_vm = adreno_create_vm,
- .get_rptr = a3xx_get_rptr,
- },
-};
-
static const struct msm_gpu_perfcntr perfcntrs[] = {
{ REG_A3XX_SP_PERFCOUNTER6_SELECT, REG_A3XX_RBBM_PERFCTR_SP_6_LO,
SP_ALU_ACTIVE_CYCLES, "ALUACTIVE" },
@@ -538,13 +515,14 @@ static const struct msm_gpu_perfcntr perfcntrs[] = {
SP_FS_FULL_ALU_INSTRUCTIONS, "ALUFULL" },
};
-struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
+static struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
{
struct a3xx_gpu *a3xx_gpu = NULL;
struct adreno_gpu *adreno_gpu;
struct msm_gpu *gpu;
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
+ struct adreno_platform_config *config = pdev->dev.platform_data;
struct icc_path *ocmem_icc_path;
struct icc_path *icc_path;
int ret;
@@ -569,7 +547,7 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev)
adreno_gpu->registers = a3xx_registers;
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, 1);
if (ret)
goto fail;
@@ -613,3 +591,27 @@ fail:
return ERR_PTR(ret);
}
+
+const struct adreno_gpu_funcs a3xx_gpu_funcs = {
+ .base = {
+ .get_param = adreno_get_param,
+ .set_param = adreno_set_param,
+ .hw_init = a3xx_hw_init,
+ .pm_suspend = msm_gpu_pm_suspend,
+ .pm_resume = msm_gpu_pm_resume,
+ .recover = a3xx_recover,
+ .submit = a3xx_submit,
+ .active_ring = adreno_active_ring,
+ .irq = a3xx_irq,
+ .destroy = a3xx_destroy,
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
+ .show = adreno_show,
+#endif
+ .gpu_busy = a3xx_gpu_busy,
+ .gpu_state_get = a3xx_gpu_state_get,
+ .gpu_state_put = adreno_gpu_state_put,
+ .create_vm = adreno_create_vm,
+ .get_rptr = a3xx_get_rptr,
+ },
+ .init = a3xx_gpu_init,
+};
diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
index c555fb13e0d7..3d4ec9dbd918 100644
--- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h
@@ -23,4 +23,6 @@ struct a3xx_gpu {
};
#define to_a3xx_gpu(x) container_of(x, struct a3xx_gpu, base)
+extern const struct adreno_gpu_funcs a3xx_gpu_funcs;
+
#endif /* __A3XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/a4xx_catalog.c b/drivers/gpu/drm/msm/adreno/a4xx_catalog.c
index 09f9f228b75e..9192586f7ef0 100644
--- a/drivers/gpu/drm/msm/adreno/a4xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a4xx_catalog.c
@@ -7,6 +7,7 @@
*/
#include "adreno_gpu.h"
+#include "a4xx_gpu.h"
static const struct adreno_info a4xx_gpus[] = {
{
@@ -19,7 +20,7 @@ static const struct adreno_info a4xx_gpus[] = {
},
.gmem = SZ_256K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a4xx_gpu_init,
+ .funcs = &a4xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x04020000),
.family = ADRENO_4XX,
@@ -30,7 +31,7 @@ static const struct adreno_info a4xx_gpus[] = {
},
.gmem = (SZ_1M + SZ_512K),
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a4xx_gpu_init,
+ .funcs = &a4xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x04030002),
.family = ADRENO_4XX,
@@ -41,7 +42,7 @@ static const struct adreno_info a4xx_gpus[] = {
},
.gmem = (SZ_1M + SZ_512K),
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a4xx_gpu_init,
+ .funcs = &a4xx_gpu_funcs,
}
};
DECLARE_ADRENO_GPULIST(a4xx);
diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
index 83f6329accba..db06c06067ae 100644
--- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c
@@ -627,37 +627,14 @@ static u32 a4xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
return ring->memptrs->rptr;
}
-static const struct adreno_gpu_funcs funcs = {
- .base = {
- .get_param = adreno_get_param,
- .set_param = adreno_set_param,
- .hw_init = a4xx_hw_init,
- .pm_suspend = a4xx_pm_suspend,
- .pm_resume = a4xx_pm_resume,
- .recover = a4xx_recover,
- .submit = a4xx_submit,
- .active_ring = adreno_active_ring,
- .irq = a4xx_irq,
- .destroy = a4xx_destroy,
-#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
- .show = adreno_show,
-#endif
- .gpu_busy = a4xx_gpu_busy,
- .gpu_state_get = a4xx_gpu_state_get,
- .gpu_state_put = adreno_gpu_state_put,
- .create_vm = adreno_create_vm,
- .get_rptr = a4xx_get_rptr,
- },
- .get_timestamp = a4xx_get_timestamp,
-};
-
-struct msm_gpu *a4xx_gpu_init(struct drm_device *dev)
+static struct msm_gpu *a4xx_gpu_init(struct drm_device *dev)
{
struct a4xx_gpu *a4xx_gpu = NULL;
struct adreno_gpu *adreno_gpu;
struct msm_gpu *gpu;
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
+ struct adreno_platform_config *config = pdev->dev.platform_data;
struct icc_path *ocmem_icc_path;
struct icc_path *icc_path;
int ret;
@@ -680,7 +657,7 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev)
gpu->perfcntrs = NULL;
gpu->num_perfcntrs = 0;
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, 1);
if (ret)
goto fail;
@@ -726,3 +703,28 @@ fail:
return ERR_PTR(ret);
}
+
+const struct adreno_gpu_funcs a4xx_gpu_funcs = {
+ .base = {
+ .get_param = adreno_get_param,
+ .set_param = adreno_set_param,
+ .hw_init = a4xx_hw_init,
+ .pm_suspend = a4xx_pm_suspend,
+ .pm_resume = a4xx_pm_resume,
+ .recover = a4xx_recover,
+ .submit = a4xx_submit,
+ .active_ring = adreno_active_ring,
+ .irq = a4xx_irq,
+ .destroy = a4xx_destroy,
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
+ .show = adreno_show,
+#endif
+ .gpu_busy = a4xx_gpu_busy,
+ .gpu_state_get = a4xx_gpu_state_get,
+ .gpu_state_put = adreno_gpu_state_put,
+ .create_vm = adreno_create_vm,
+ .get_rptr = a4xx_get_rptr,
+ },
+ .init = a4xx_gpu_init,
+ .get_timestamp = a4xx_get_timestamp,
+};
diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.h b/drivers/gpu/drm/msm/adreno/a4xx_gpu.h
index a01448cba2ea..71b164439f62 100644
--- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.h
@@ -20,4 +20,6 @@ struct a4xx_gpu {
};
#define to_a4xx_gpu(x) container_of(x, struct a4xx_gpu, base)
+extern const struct adreno_gpu_funcs a4xx_gpu_funcs;
+
#endif /* __A4XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
index b48a636d8237..babd320f3b73 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_catalog.c
@@ -7,6 +7,7 @@
*/
#include "adreno_gpu.h"
+#include "a5xx_gpu.h"
static const struct adreno_info a5xx_gpus[] = {
{
@@ -21,7 +22,7 @@ static const struct adreno_info a5xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_TWO_PASS_USE_WFI |
ADRENO_QUIRK_LMLOADKILL_DISABLE,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x05000600),
.family = ADRENO_5XX,
@@ -38,7 +39,7 @@ static const struct adreno_info a5xx_gpus[] = {
.inactive_period = 250,
.quirks = ADRENO_QUIRK_TWO_PASS_USE_WFI |
ADRENO_QUIRK_LMLOADKILL_DISABLE,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
.zapfw = "a506_zap.mdt",
}, {
.chip_ids = ADRENO_CHIP_IDS(0x05000800),
@@ -55,7 +56,7 @@ static const struct adreno_info a5xx_gpus[] = {
*/
.inactive_period = 250,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
.zapfw = "a508_zap.mdt",
}, {
.chip_ids = ADRENO_CHIP_IDS(0x05000900),
@@ -72,7 +73,7 @@ static const struct adreno_info a5xx_gpus[] = {
*/
.inactive_period = 250,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
/* Adreno 509 uses the same ZAP as 512 */
.zapfw = "a512_zap.mdt",
}, {
@@ -89,7 +90,7 @@ static const struct adreno_info a5xx_gpus[] = {
* the GDSC which appears to make it grumpy
*/
.inactive_period = 250,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
}, {
.chip_ids = ADRENO_CHIP_IDS(0x05010200),
.family = ADRENO_5XX,
@@ -105,7 +106,7 @@ static const struct adreno_info a5xx_gpus[] = {
*/
.inactive_period = 250,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
.zapfw = "a512_zap.mdt",
}, {
.chip_ids = ADRENO_CHIP_IDS(
@@ -127,7 +128,7 @@ static const struct adreno_info a5xx_gpus[] = {
.inactive_period = 250,
.quirks = ADRENO_QUIRK_TWO_PASS_USE_WFI |
ADRENO_QUIRK_FAULT_DETECT_MASK,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
.zapfw = "a530_zap.mdt",
}, {
.chip_ids = ADRENO_CHIP_IDS(0x05040001),
@@ -145,7 +146,7 @@ static const struct adreno_info a5xx_gpus[] = {
*/
.inactive_period = 250,
.quirks = ADRENO_QUIRK_LMLOADKILL_DISABLE,
- .init = a5xx_gpu_init,
+ .funcs = &a5xx_gpu_funcs,
.zapfw = "a540_zap.mdt",
}
};
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index 4a04dc43a8e6..56eaff2ee4e4 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -1691,34 +1691,6 @@ static uint32_t a5xx_get_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
return ring->memptrs->rptr = gpu_read(gpu, REG_A5XX_CP_RB_RPTR);
}
-static const struct adreno_gpu_funcs funcs = {
- .base = {
- .get_param = adreno_get_param,
- .set_param = adreno_set_param,
- .hw_init = a5xx_hw_init,
- .ucode_load = a5xx_ucode_load,
- .pm_suspend = a5xx_pm_suspend,
- .pm_resume = a5xx_pm_resume,
- .recover = a5xx_recover,
- .submit = a5xx_submit,
- .active_ring = a5xx_active_ring,
- .irq = a5xx_irq,
- .destroy = a5xx_destroy,
-#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
- .show = a5xx_show,
-#endif
-#if defined(CONFIG_DEBUG_FS)
- .debugfs_init = a5xx_debugfs_init,
-#endif
- .gpu_busy = a5xx_gpu_busy,
- .gpu_state_get = a5xx_gpu_state_get,
- .gpu_state_put = a5xx_gpu_state_put,
- .create_vm = adreno_create_vm,
- .get_rptr = a5xx_get_rptr,
- },
- .get_timestamp = a5xx_get_timestamp,
-};
-
static void check_speed_bin(struct device *dev)
{
struct nvmem_cell *cell;
@@ -1751,7 +1723,7 @@ static void check_speed_bin(struct device *dev)
devm_pm_opp_set_supported_hw(dev, &val, 1);
}
-struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
+static struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
{
struct msm_drm_private *priv = dev->dev_private;
struct platform_device *pdev = priv->gpu_pdev;
@@ -1781,7 +1753,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
if (config->info->revn == 510)
nr_rings = 1;
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, nr_rings);
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, nr_rings);
if (ret) {
a5xx_destroy(&(a5xx_gpu->base.base));
return ERR_PTR(ret);
@@ -1806,3 +1778,32 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
return gpu;
}
+
+const struct adreno_gpu_funcs a5xx_gpu_funcs = {
+ .base = {
+ .get_param = adreno_get_param,
+ .set_param = adreno_set_param,
+ .hw_init = a5xx_hw_init,
+ .ucode_load = a5xx_ucode_load,
+ .pm_suspend = a5xx_pm_suspend,
+ .pm_resume = a5xx_pm_resume,
+ .recover = a5xx_recover,
+ .submit = a5xx_submit,
+ .active_ring = a5xx_active_ring,
+ .irq = a5xx_irq,
+ .destroy = a5xx_destroy,
+#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP)
+ .show = a5xx_show,
+#endif
+#if defined(CONFIG_DEBUG_FS)
+ .debugfs_init = a5xx_debugfs_init,
+#endif
+ .gpu_busy = a5xx_gpu_busy,
+ .gpu_state_get = a5xx_gpu_state_get,
+ .gpu_state_put = a5xx_gpu_state_put,
+ .create_vm = adreno_create_vm,
+ .get_rptr = a5xx_get_rptr,
+ },
+ .init = a5xx_gpu_init,
+ .get_timestamp = a5xx_get_timestamp,
+};
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
index 9c0d701fe4b8..407bb950d350 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h
@@ -133,6 +133,7 @@ struct a5xx_preempt_record {
*/
#define A5XX_PREEMPT_COUNTER_SIZE (16 * 4)
+extern const struct adreno_gpu_funcs a5xx_gpu_funcs;
int a5xx_power_init(struct msm_gpu *gpu);
void a5xx_gpmu_ucode_init(struct msm_gpu *gpu);
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
index 44df6410bce1..29107b362346 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_catalog.c
@@ -672,6 +672,14 @@ static const u32 a690_protect_regs[] = {
};
DECLARE_ADRENO_PROTECT(a690_protect, 48);
+static const struct adreno_reglist a640_gbif[] = {
+ { REG_A6XX_GBIF_QSB_SIDE0, 0x00071620 },
+ { REG_A6XX_GBIF_QSB_SIDE1, 0x00071620 },
+ { REG_A6XX_GBIF_QSB_SIDE2, 0x00071620 },
+ { REG_A6XX_GBIF_QSB_SIDE3, 0x00071620 },
+ { },
+};
+
static const struct adreno_info a6xx_gpus[] = {
{
.chip_ids = ADRENO_CHIP_IDS(0x06010000),
@@ -683,11 +691,12 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = (SZ_128K + SZ_4K),
.quirks = ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gmuwrapper_funcs,
.zapfw = "a610_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a612_hwcg,
.protect = &a630_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x00080000,
},
@@ -706,6 +715,22 @@ static const struct adreno_info a6xx_gpus[] = {
{ 127, 4 },
),
}, {
+ .chip_ids = ADRENO_CHIP_IDS(0x06010200),
+ .family = ADRENO_6XX_GEN1,
+ .fw = {
+ [ADRENO_FW_SQE] = "a630_sqe.fw",
+ [ADRENO_FW_GMU] = "a612_rgmu.bin",
+ },
+ .gmem = (SZ_128K + SZ_4K),
+ .inactive_period = DRM_MSM_INACTIVE_PERIOD,
+ .funcs = &a6xx_gmuwrapper_funcs,
+ .a6xx = &(const struct a6xx_info) {
+ .hwcg = a612_hwcg,
+ .protect = &a630_protect,
+ .gmu_cgc_mode = 0x00000022,
+ .prim_fifo_threshold = 0x00080000,
+ },
+ }, {
.chip_ids = ADRENO_CHIP_IDS(0x06010500),
.family = ADRENO_6XX_GEN1,
.revn = 615,
@@ -716,7 +741,7 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_512K,
.quirks = ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a615_hwcg,
@@ -747,7 +772,7 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_4GB_VA,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mbn",
.a6xx = &(const struct a6xx_info) {
.hwcg = a615_hwcg,
@@ -774,7 +799,7 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_4GB_VA,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
.protect = &a630_protect,
.gmu_cgc_mode = 0x00000222,
@@ -797,7 +822,7 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_512K,
.quirks = ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a615_hwcg,
@@ -822,7 +847,7 @@ static const struct adreno_info a6xx_gpus[] = {
.gmem = SZ_512K,
.quirks = ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a615_hwcg,
@@ -847,7 +872,7 @@ static const struct adreno_info a6xx_gpus[] = {
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a615_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a615_hwcg,
@@ -873,11 +898,12 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a620_zap.mbn",
.a6xx = &(const struct a6xx_info) {
.hwcg = a620_hwcg,
.protect = &a650_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020200,
.prim_fifo_threshold = 0x00010000,
},
@@ -896,10 +922,11 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
.hwcg = a690_hwcg,
.protect = &a650_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020200,
.prim_fifo_threshold = 0x00010000,
.bcms = (const struct a6xx_bcm[]) {
@@ -933,7 +960,7 @@ static const struct adreno_info a6xx_gpus[] = {
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a630_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a630_hwcg,
@@ -953,7 +980,7 @@ static const struct adreno_info a6xx_gpus[] = {
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a640_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a640_hwcg,
@@ -977,11 +1004,12 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a650_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a650_hwcg,
.protect = &a650_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x00300200,
},
@@ -1003,11 +1031,12 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a660_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a660_hwcg,
.protect = &a660_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020000,
.prim_fifo_threshold = 0x00300200,
},
@@ -1022,10 +1051,11 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
.hwcg = a690_hwcg,
.protect = &a660_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020200,
.prim_fifo_threshold = 0x00300200,
},
@@ -1045,11 +1075,12 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a660_zap.mbn",
.a6xx = &(const struct a6xx_info) {
.hwcg = a660_hwcg,
.protect = &a660_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x00200200,
},
@@ -1072,7 +1103,7 @@ static const struct adreno_info a6xx_gpus[] = {
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_4GB_VA,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a640_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a640_hwcg,
@@ -1091,11 +1122,12 @@ static const struct adreno_info a6xx_gpus[] = {
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gpu_funcs,
.zapfw = "a690_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a690_hwcg,
.protect = &a690_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020200,
.prim_fifo_threshold = 0x00800200,
},
@@ -1426,11 +1458,12 @@ static const struct adreno_info a7xx_gpus[] = {
.gmem = SZ_128K,
.inactive_period = DRM_MSM_INACTIVE_PERIOD,
.quirks = ADRENO_QUIRK_HAS_HW_APRIV,
- .init = a6xx_gpu_init,
+ .funcs = &a6xx_gmuwrapper_funcs,
.zapfw = "a702_zap.mbn",
.a6xx = &(const struct a6xx_info) {
.hwcg = a702_hwcg,
.protect = &a650_protect,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020202,
.prim_fifo_threshold = 0x0000c000,
},
@@ -1452,12 +1485,13 @@ static const struct adreno_info a7xx_gpus[] = {
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION,
- .init = a6xx_gpu_init,
+ .funcs = &a7xx_gpu_funcs,
.zapfw = "a730_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a730_hwcg,
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
+ .gbif_cx = a640_gbif,
.gmu_cgc_mode = 0x00020000,
},
.preempt_record_size = 2860 * SZ_1K,
@@ -1473,12 +1507,13 @@ static const struct adreno_info a7xx_gpus[] = {
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION,
- .init = a6xx_gpu_init,
+ .funcs = &a7xx_gpu_funcs,
.zapfw = "a740_zap.mdt",
.a6xx = &(const struct a6xx_info) {
.hwcg = a740_hwcg,
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
+ .gbif_cx = a640_gbif,
.gmu_chipid = 0x7020100,
.gmu_cgc_mode = 0x00020202,
.bcms = (const struct a6xx_bcm[]) {
@@ -1507,12 +1542,13 @@ static const struct adreno_info a7xx_gpus[] = {
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION |
ADRENO_QUIRK_IFPC,
- .init = a6xx_gpu_init,
+ .funcs = &a7xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
.hwcg = a740_hwcg,
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
.ifpc_reglist = &a750_ifpc_reglist,
+ .gbif_cx = a640_gbif,
.gmu_chipid = 0x7050001,
.gmu_cgc_mode = 0x00020202,
.bcms = (const struct a6xx_bcm[]) {
@@ -1548,12 +1584,13 @@ static const struct adreno_info a7xx_gpus[] = {
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION |
ADRENO_QUIRK_IFPC,
- .init = a6xx_gpu_init,
+ .funcs = &a7xx_gpu_funcs,
.zapfw = "gen70900_zap.mbn",
.a6xx = &(const struct a6xx_info) {
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
.ifpc_reglist = &a750_ifpc_reglist,
+ .gbif_cx = a640_gbif,
.gmu_chipid = 0x7090100,
.gmu_cgc_mode = 0x00020202,
.bcms = (const struct a6xx_bcm[]) {
@@ -1581,11 +1618,12 @@ static const struct adreno_info a7xx_gpus[] = {
.quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
ADRENO_QUIRK_HAS_HW_APRIV |
ADRENO_QUIRK_PREEMPTION,
- .init = a6xx_gpu_init,
+ .funcs = &a7xx_gpu_funcs,
.a6xx = &(const struct a6xx_info) {
.hwcg = a740_hwcg,
.protect = &a730_protect,
.pwrup_reglist = &a7xx_pwrup_reglist,
+ .gbif_cx = a640_gbif,
.gmu_chipid = 0x70f0000,
.gmu_cgc_mode = 0x00020222,
.bcms = (const struct a6xx_bcm[]) {
@@ -1612,6 +1650,306 @@ static const struct adreno_info a7xx_gpus[] = {
};
DECLARE_ADRENO_GPULIST(a7xx);
+static const struct adreno_reglist_pipe x285_nonctxt_regs[] = {
+ { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) },
+ { REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00000800, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0x00200000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_VIS_STREAM_CNTL, 0x10010000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0x00000002, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_1, 0x00000003, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_2, 0x00000200, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_3, 0x00500000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_4, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_RB_GC_GMEM_PROTECT, 0x15000000, BIT(PIPE_BR) },
+ { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
+ { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) },
+ { REG_A8XX_RBBM_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_SLICE_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL, 0x00000030, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL2, 0x00000030, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_INTERFACE_HANG_INT_CNTL, 0x0fffffff, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x22122212, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_CGC_P2S_CNTL, 0x00000040, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_CHICKEN_BITS_2, 0x00820800, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_CHICKEN_BITS_3, 0x00300000, BIT(PIPE_NONE) },
+ { REG_A6XX_SP_PERFCTR_SHADER_MASK, 0x0000003f, BIT(PIPE_NONE) },
+ /* Disable CS dead batch merge */
+ { REG_A7XX_SP_HLSQ_DBG_ECO_CNTL_2, BIT(31), BIT(PIPE_NONE) },
+ { REG_A7XX_SP_HLSQ_TIMEOUT_THRESHOLD_DP, 0x00000080, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_READ_SEL, 0x0001ff00, BIT(PIPE_NONE) },
+ { REG_A6XX_TPL1_DBG_ECO_CNTL, 0x10000000, BIT(PIPE_NONE) },
+ /* BIT(26): Disable final clamp for bicubic filtering */
+ { REG_A6XX_TPL1_DBG_ECO_CNTL1, 0x00000720, BIT(PIPE_NONE) },
+ { REG_A6XX_UCHE_MODE_CNTL, 0x80080000, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_CCHE_MODE_CNTL, 0x00001000, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_CCHE_CACHE_WAYS, 0x00000800, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_GBIF_GX_CONFIG, 0x010240e0, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_VARB_IDLE_TIMEOUT, 0x00000020, BIT(PIPE_NONE) },
+ { REG_A7XX_VFD_DBG_ECO_CNTL, 0x00008000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BV_THRESHOLD, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BR_THRESHOLD, 0x00600060, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0x00200020, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_LP_REQ_CNT, 0x00000020, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { },
+};
+
+static const u32 x285_protect_regs[] = {
+ A6XX_PROTECT_RDONLY(0x00008, 0x039b),
+ A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
+ A6XX_PROTECT_NORDWR(0x00440, 0x001f),
+ A6XX_PROTECT_RDONLY(0x00580, 0x005f),
+ A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
+ A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
+ A6XX_PROTECT_RDONLY(0x00759, 0x0026),
+ A6XX_PROTECT_RDONLY(0x00789, 0x0000),
+ A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
+ A6XX_PROTECT_NORDWR(0x00800, 0x0029),
+ A6XX_PROTECT_NORDWR(0x0082c, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00837, 0x00af),
+ A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
+ A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
+ A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
+ A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
+ A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
+ A6XX_PROTECT_RDONLY(0x03cc6, 0x0039),
+ A6XX_PROTECT_NORDWR(0x03d00, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
+ A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
+ A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
+ A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
+ A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
+ A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x0a82e, 0x0000),
+ A6XX_PROTECT_NORDWR(0x0ae00, 0x0006),
+ A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
+ A6XX_PROTECT_NORDWR(0x0ae10, 0x00bf),
+ A6XX_PROTECT_RDONLY(0x0aed0, 0x002f),
+ A6XX_PROTECT_NORDWR(0x0af00, 0x027f),
+ A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
+ A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x18400, 0x003f),
+ A6XX_PROTECT_RDONLY(0x18440, 0x013f),
+ A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x1b400, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
+ A6XX_PROTECT_RDONLY(0x1f878, 0x0507),
+ A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
+ A6XX_PROTECT_NORDWR(0x1fd80, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x27800, 0x007f),
+ A6XX_PROTECT_RDONLY(0x27880, 0x0385),
+ A6XX_PROTECT_NORDWR(0x27882, 0x000a),
+ A6XX_PROTECT_NORDWR(0x27c06, 0x0000),
+};
+
+DECLARE_ADRENO_PROTECT(x285_protect, 64);
+
+static const struct adreno_reglist_pipe a840_nonctxt_regs[] = {
+ { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) },
+ { REG_A8XX_GRAS_DBG_ECO_CNTL, 0x00000800, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_GRAS_TSEFE_DBG_ECO_CNTL, 0x00200000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A6XX_PC_AUTO_VERTEX_STRIDE, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_VIS_STREAM_CNTL, 0x10010000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CONTEXT_SWITCH_STABILIZE_CNTL_1, 0x00000002, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_1, 0x00000003, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_2, 0x00000200, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_3, 0x00500000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_PC_CHICKEN_BITS_4, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ /* Disable Dead Draw Merge scheme on RB-HLSQ */
+ { REG_A6XX_RB_RBP_CNTL, BIT(5), BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A7XX_RB_CCU_CNTL, 0x00000068, BIT(PIPE_BR) },
+ /* Partially enable perf clear, Disable DINT to c/z be data forwarding */
+ { REG_A7XX_RB_CCU_DBG_ECO_CNTL, 0x00002200, BIT(PIPE_BR) },
+ { REG_A8XX_RB_GC_GMEM_PROTECT, 0x12000000, BIT(PIPE_BR) },
+ { REG_A8XX_RB_RESOLVE_PREFETCH_CNTL, 0x00000007, BIT(PIPE_BR) },
+ { REG_A8XX_RB_CMP_DBG_ECO_CNTL, 0x00004000, BIT(PIPE_BR) },
+ { REG_A8XX_RBBM_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_SLICE_NC_MODE_CNTL, 0x00000001, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_POWER_UP_RESET_SW_OVERRIDE, 0x70809060, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_POWER_UP_RESET_SW_BV_OVERRIDE, 0x30000000, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL, 0x00000030, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_WAIT_IDLE_CLOCKS_CNTL2, 0x00000030, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_INTERFACE_HANG_INT_CNTL, 0x0fffffff, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x22122212, BIT(PIPE_NONE) },
+ { REG_A8XX_RBBM_CGC_P2S_CNTL, 0x00000040, BIT(PIPE_NONE) },
+ /* Disable mode_switch optimization in UMAS */
+ { REG_A6XX_SP_CHICKEN_BITS, BIT(24) | BIT(26), BIT(PIPE_NONE) },
+ /* Disable LPAC large-LM mode */
+ { REG_A8XX_SP_SS_CHICKEN_BITS_0, BIT(3), BIT(PIPE_NONE) },
+ /* Disable PS out of order retire */
+ { REG_A7XX_SP_CHICKEN_BITS_2, 0x00c21800, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_CHICKEN_BITS_3, 0x00300000, BIT(PIPE_NONE) },
+ /* Disable SP2TP info attribute */
+ { REG_A8XX_SP_CHICKEN_BITS_4, 0x00000002, BIT(PIPE_NONE) },
+ { REG_A6XX_SP_PERFCTR_SHADER_MASK, 0x0000003f, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_HLSQ_DBG_ECO_CNTL, BIT(14), BIT(PIPE_NONE) },
+ /* Ignore HLSQ shared constant feedback from SP */
+ { REG_A7XX_SP_HLSQ_DBG_ECO_CNTL_1, BIT(17), BIT(PIPE_NONE) },
+ /* Disable CS dead batch merge */
+ { REG_A7XX_SP_HLSQ_DBG_ECO_CNTL_2, BIT(24), BIT(PIPE_NONE) },
+ { REG_A8XX_SP_HLSQ_DBG_ECO_CNTL_3, BIT(7), BIT(PIPE_NONE) },
+ { REG_A7XX_SP_HLSQ_TIMEOUT_THRESHOLD_DP, 0x00000080, BIT(PIPE_NONE) },
+ { REG_A7XX_SP_READ_SEL, 0x0001ff00, BIT(PIPE_NONE) },
+ { REG_A6XX_TPL1_DBG_ECO_CNTL, 0x10100000, BIT(PIPE_NONE) },
+ /* BIT(26): Disable final clamp for bicubic filtering */
+ { REG_A6XX_TPL1_DBG_ECO_CNTL1, 0x04000720, BIT(PIPE_NONE) },
+ { REG_A6XX_UCHE_MODE_CNTL, 0x80080000, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_CCHE_MODE_CNTL, 0x00001000, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_CCHE_CACHE_WAYS, 0x00000800, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_GBIF_GX_CONFIG, 0x010240e0, BIT(PIPE_NONE) },
+ { REG_A8XX_UCHE_VARB_IDLE_TIMEOUT, 0x00000020, BIT(PIPE_NONE) },
+ { REG_A7XX_VFD_DBG_ECO_CNTL, 0x00008000, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BV_THRESHOLD, 0x00500050, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BR_THRESHOLD, 0x00600060, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_BUSY_REQ_CNT, 0x00200020, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VFD_CB_LP_REQ_CNT, 0x00000020, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { REG_A8XX_VPC_FLATSHADE_MODE_CNTL, 0x00000001, BIT(PIPE_BV) | BIT(PIPE_BR) },
+ { },
+};
+
+static const u32 a840_protect_regs[] = {
+ A6XX_PROTECT_RDONLY(0x00008, 0x039b),
+ A6XX_PROTECT_RDONLY(0x003b4, 0x008b),
+ A6XX_PROTECT_NORDWR(0x00440, 0x001f),
+ A6XX_PROTECT_RDONLY(0x00580, 0x005f),
+ A6XX_PROTECT_NORDWR(0x005e0, 0x011f),
+ A6XX_PROTECT_RDONLY(0x0074a, 0x0005),
+ A6XX_PROTECT_RDONLY(0x00759, 0x001b),
+ A6XX_PROTECT_NORDWR(0x00775, 0x000a),
+ A6XX_PROTECT_RDONLY(0x00789, 0x0000),
+ A6XX_PROTECT_RDONLY(0x0078c, 0x0013),
+ A6XX_PROTECT_NORDWR(0x00800, 0x0029),
+ A6XX_PROTECT_NORDWR(0x00837, 0x00af),
+ A6XX_PROTECT_RDONLY(0x008e7, 0x00c9),
+ A6XX_PROTECT_NORDWR(0x008ec, 0x00c3),
+ A6XX_PROTECT_NORDWR(0x009b1, 0x0250),
+ A6XX_PROTECT_NORDWR(0x00c07, 0x0008),
+ A6XX_PROTECT_RDONLY(0x00ce0, 0x0001),
+ A6XX_PROTECT_RDONLY(0x00df0, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00df1, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00e01, 0x0000),
+ A6XX_PROTECT_NORDWR(0x00e03, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x03c00, 0x00c5),
+ A6XX_PROTECT_RDONLY(0x03cc6, 0x0039),
+ A6XX_PROTECT_NORDWR(0x03d00, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x08600, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x08e00, 0x00ff),
+ A6XX_PROTECT_RDONLY(0x08f00, 0x0000),
+ A6XX_PROTECT_NORDWR(0x08f01, 0x01be),
+ A6XX_PROTECT_NORDWR(0x09600, 0x01ff),
+ A6XX_PROTECT_RDONLY(0x0981a, 0x02e5),
+ A6XX_PROTECT_NORDWR(0x09e00, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x0a600, 0x01ff),
+ A6XX_PROTECT_NORDWR(0x0a82e, 0x0000),
+ A6XX_PROTECT_NORDWR(0x0ae00, 0x0000),
+ A6XX_PROTECT_NORDWR(0x0ae02, 0x0004),
+ A6XX_PROTECT_NORDWR(0x0ae08, 0x0006),
+ A6XX_PROTECT_NORDWR(0x0ae10, 0x00bf),
+ A6XX_PROTECT_RDONLY(0x0aed0, 0x002f),
+ A6XX_PROTECT_NORDWR(0x0af00, 0x027f),
+ A6XX_PROTECT_NORDWR(0x0b600, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x0dc00, 0x1fff),
+ A6XX_PROTECT_RDONLY(0x0fc00, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x18400, 0x003f),
+ A6XX_PROTECT_RDONLY(0x18440, 0x013f),
+ A6XX_PROTECT_NORDWR(0x18580, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x1b400, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x1f400, 0x0477),
+ A6XX_PROTECT_RDONLY(0x1f878, 0x0507),
+ A6XX_PROTECT_NORDWR(0x1f930, 0x0329),
+ A6XX_PROTECT_NORDWR(0x1fd80, 0x1fff),
+ A6XX_PROTECT_NORDWR(0x27800, 0x007f),
+ A6XX_PROTECT_RDONLY(0x27880, 0x0385),
+ A6XX_PROTECT_NORDWR(0x27882, 0x0009),
+ A6XX_PROTECT_NORDWR(0x27c06, 0x0000),
+};
+DECLARE_ADRENO_PROTECT(a840_protect, 15);
+
+static const struct adreno_reglist a840_gbif[] = {
+ { REG_A6XX_GBIF_QSB_SIDE0, 0x00071e20 },
+ { REG_A6XX_GBIF_QSB_SIDE1, 0x00071e20 },
+ { REG_A6XX_GBIF_QSB_SIDE2, 0x00071e20 },
+ { REG_A6XX_GBIF_QSB_SIDE3, 0x00071e20 },
+ { REG_A8XX_GBIF_CX_CONFIG, 0x20023000 },
+ { },
+};
+
+static const struct adreno_info a8xx_gpus[] = {
+ {
+ .chip_ids = ADRENO_CHIP_IDS(0x44070001),
+ .family = ADRENO_8XX_GEN2,
+ .fw = {
+ [ADRENO_FW_SQE] = "gen80100_sqe.fw",
+ [ADRENO_FW_GMU] = "gen80100_gmu.bin",
+ },
+ .gmem = 21 * SZ_1M,
+ .inactive_period = DRM_MSM_INACTIVE_PERIOD,
+ .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
+ ADRENO_QUIRK_HAS_HW_APRIV,
+ .funcs = &a8xx_gpu_funcs,
+ .a6xx = &(const struct a6xx_info) {
+ .protect = &x285_protect,
+ .nonctxt_reglist = x285_nonctxt_regs,
+ .gbif_cx = a840_gbif,
+ .max_slices = 4,
+ .gmu_chipid = 0x8010100,
+ .bcms = (const struct a6xx_bcm[]) {
+ { .name = "SH0", .buswidth = 16 },
+ { .name = "MC0", .buswidth = 4 },
+ {
+ .name = "ACV",
+ .fixed = true,
+ .perfmode = BIT(2),
+ .perfmode_bw = 16500000,
+ },
+ { /* sentinel */ },
+ },
+ },
+ }, {
+ .chip_ids = ADRENO_CHIP_IDS(0x44050a01),
+ .family = ADRENO_8XX_GEN2,
+ .fw = {
+ [ADRENO_FW_SQE] = "gen80200_sqe.fw",
+ [ADRENO_FW_GMU] = "gen80200_gmu.bin",
+ [ADRENO_FW_AQE] = "gen80200_aqe.fw",
+ },
+ .gmem = 18 * SZ_1M,
+ .inactive_period = DRM_MSM_INACTIVE_PERIOD,
+ .quirks = ADRENO_QUIRK_HAS_CACHED_COHERENT |
+ ADRENO_QUIRK_HAS_HW_APRIV,
+ .funcs = &a8xx_gpu_funcs,
+ .a6xx = &(const struct a6xx_info) {
+ .protect = &a840_protect,
+ .nonctxt_reglist = a840_nonctxt_regs,
+ .gbif_cx = a840_gbif,
+ .max_slices = 3,
+ .gmu_chipid = 0x8020100,
+ .bcms = (const struct a6xx_bcm[]) {
+ { .name = "SH0", .buswidth = 16 },
+ { .name = "MC0", .buswidth = 4 },
+ {
+ .name = "ACV",
+ .fixed = true,
+ .perfmode = BIT(2),
+ .perfmode_bw = 10687500,
+ },
+ { /* sentinel */ },
+ },
+ },
+ .preempt_record_size = 19708 * SZ_1K,
+ }
+};
+
+DECLARE_ADRENO_GPULIST(a8xx);
+
static inline __always_unused void __build_asserts(void)
{
BUILD_BUG_ON(a630_protect.count > a630_protect.count_max);
@@ -1619,4 +1957,5 @@ static inline __always_unused void __build_asserts(void)
BUILD_BUG_ON(a660_protect.count > a660_protect.count_max);
BUILD_BUG_ON(a690_protect.count > a690_protect.count_max);
BUILD_BUG_ON(a730_protect.count > a730_protect.count_max);
+ BUILD_BUG_ON(a840_protect.count > a840_protect.count_max);
}
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index fc62fef2fed8..5903cd891b49 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -224,14 +224,19 @@ unsigned long a6xx_gmu_get_freq(struct msm_gpu *gpu)
static bool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu)
{
- u32 val;
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
int local = gmu->idle_level;
+ u32 val;
/* SPTP and IFPC both report as IFPC */
if (gmu->idle_level == GMU_IDLE_STATE_SPTP)
local = GMU_IDLE_STATE_IFPC;
- val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE);
+ if (adreno_is_a8xx(adreno_gpu))
+ val = gmu_read(gmu, REG_A8XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE);
+ else
+ val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE);
if (val == local) {
if (gmu->idle_level != GMU_IDLE_STATE_IFPC ||
@@ -269,7 +274,9 @@ static int a6xx_gmu_start(struct a6xx_gmu *gmu)
/* Set the log wptr index
* note: downstream saves the value in poweroff and restores it here
*/
- if (adreno_is_a7xx(adreno_gpu))
+ if (adreno_is_a8xx(adreno_gpu))
+ gmu_write(gmu, REG_A8XX_GMU_GENERAL_9, 0);
+ else if (adreno_is_a7xx(adreno_gpu))
gmu_write(gmu, REG_A7XX_GMU_GENERAL_9, 0);
else
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_PWR_COL_CP_RESP, 0);
@@ -350,12 +357,18 @@ static const struct a6xx_gmu_oob_bits a6xx_gmu_oob_bits[] = {
/* Trigger a OOB (out of band) request to the GMU */
int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
int ret;
u32 val;
int request, ack;
WARN_ON_ONCE(!mutex_is_locked(&gmu->lock));
+ /* Skip OOB calls since RGMU is not enabled */
+ if (adreno_has_rgmu(adreno_gpu))
+ return 0;
+
if (state >= ARRAY_SIZE(a6xx_gmu_oob_bits))
return -EINVAL;
@@ -376,9 +389,23 @@ int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
/* Trigger the equested OOB operation */
gmu_write(gmu, REG_A6XX_GMU_HOST2GMU_INTR_SET, 1 << request);
- /* Wait for the acknowledge interrupt */
- ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val,
- val & (1 << ack), 100, 10000);
+ do {
+ /* Wait for the acknowledge interrupt */
+ ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val,
+ val & (1 << ack), 100, 10000);
+
+ if (!ret)
+ break;
+
+ if (completion_done(&a6xx_gpu->base.fault_coredump_done))
+ break;
+
+ /* We may timeout because the GMU is temporarily wedged from
+ * pending faults from the GPU and we are taking a devcoredump.
+ * Wait until the MMU is resumed and try again.
+ */
+ wait_for_completion(&a6xx_gpu->base.fault_coredump_done);
+ } while (true);
if (ret)
DRM_DEV_ERROR(gmu->dev,
@@ -395,10 +422,16 @@ int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
/* Clear a pending OOB state in the GMU */
void a6xx_gmu_clear_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
int bit;
WARN_ON_ONCE(!mutex_is_locked(&gmu->lock));
+ /* Skip OOB calls since RGMU is not enabled */
+ if (adreno_has_rgmu(adreno_gpu))
+ return;
+
if (state >= ARRAY_SIZE(a6xx_gmu_oob_bits))
return;
@@ -485,17 +518,25 @@ static void a6xx_gemnoc_workaround(struct a6xx_gmu *gmu)
* in the power down sequence not being fully executed. That in turn can
* prevent CX_GDSC from collapsing. Assert Qactive to avoid this.
*/
- if (adreno_is_a621(adreno_gpu) || adreno_is_7c3(adreno_gpu))
- gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, BIT(0));
+ if (adreno_is_a8xx(adreno_gpu))
+ gmu_write(gmu, REG_A8XX_GPU_GMU_CX_GMU_CX_FALNEXT_INTF, BIT(0));
+ else if (adreno_is_a7xx(adreno_gpu) || (adreno_is_a621(adreno_gpu) ||
+ adreno_is_7c3(adreno_gpu)))
+ gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FALNEXT_INTF, BIT(0));
}
/* Let the GMU know that we are about to go into slumber */
static int a6xx_gmu_notify_slumber(struct a6xx_gmu *gmu)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
int ret;
/* Disable the power counter so the GMU isn't busy */
- gmu_write(gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0);
+ if (adreno_is_a8xx(adreno_gpu))
+ gmu_write(gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0);
+ else
+ gmu_write(gmu, REG_A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0);
/* Disable SPTP_PC if the CPU is responsible for it */
if (gmu->idle_level < GMU_IDLE_STATE_SPTP)
@@ -522,10 +563,9 @@ static int a6xx_gmu_notify_slumber(struct a6xx_gmu *gmu)
}
out:
- a6xx_gemnoc_workaround(gmu);
-
/* Put fence into allow mode */
gmu_write(gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
+ a6xx_gemnoc_workaround(gmu);
return ret;
}
@@ -561,16 +601,22 @@ static int a6xx_rpmh_start(struct a6xx_gmu *gmu)
static void a6xx_rpmh_stop(struct a6xx_gmu *gmu)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ u32 bitmask = BIT(16);
int ret;
u32 val;
if (test_and_clear_bit(GMU_STATUS_FW_START, &gmu->status))
return;
+ if (adreno_is_a840(adreno_gpu))
+ bitmask = BIT(30);
+
gmu_write(gmu, REG_A6XX_GMU_RSCC_CONTROL_REQ, 1);
ret = gmu_poll_timeout_rscc(gmu, REG_A6XX_GPU_RSCC_RSC_STATUS0_DRV0,
- val, val & (1 << 16), 100, 10000);
+ val, val & bitmask, 100, 10000);
if (ret)
DRM_DEV_ERROR(gmu->dev, "Unable to power off the GPU RSC\n");
@@ -584,22 +630,24 @@ static inline void pdc_write(void __iomem *ptr, u32 offset, u32 value)
writel(value, ptr + (offset << 2));
}
-static void __iomem *a6xx_gmu_get_mmio(struct platform_device *pdev,
- const char *name);
-
static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu)
{
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct platform_device *pdev = to_platform_device(gmu->dev);
- void __iomem *pdcptr = a6xx_gmu_get_mmio(pdev, "gmu_pdc");
u32 seqmem0_drv0_reg = REG_A6XX_RSCC_SEQ_MEM_0_DRV0;
void __iomem *seqptr = NULL;
uint32_t pdc_address_offset;
+ void __iomem *pdcptr;
bool pdc_in_aop = false;
+ /* On A8x and above, RPMH/PDC configurations are entirely configured in AOP */
+ if (adreno_is_a8xx(adreno_gpu))
+ return;
+
+ pdcptr = devm_platform_ioremap_resource_byname(pdev, "gmu_pdc");
if (IS_ERR(pdcptr))
- goto err;
+ return;
if (adreno_is_a650_family(adreno_gpu) ||
adreno_is_a7xx(adreno_gpu))
@@ -612,9 +660,9 @@ static void a6xx_gmu_rpmh_init(struct a6xx_gmu *gmu)
pdc_address_offset = 0x30080;
if (!pdc_in_aop) {
- seqptr = a6xx_gmu_get_mmio(pdev, "gmu_pdc_seq");
+ seqptr = devm_platform_ioremap_resource_byname(pdev, "gmu_pdc_seq");
if (IS_ERR(seqptr))
- goto err;
+ return;
}
/* Disable SDE clock gating */
@@ -704,12 +752,6 @@ setup_pdc:
/* ensure no writes happen before the uCode is fully written */
wmb();
-
-err:
- if (!IS_ERR_OR_NULL(pdcptr))
- iounmap(pdcptr);
- if (!IS_ERR_OR_NULL(seqptr))
- iounmap(seqptr);
}
/*
@@ -732,7 +774,7 @@ static void a6xx_gmu_power_config(struct a6xx_gmu *gmu)
gmu_write(gmu, REG_A6XX_GMU_DCACHE_CONFIG, 0x1);
/* A7xx knows better by default! */
- if (adreno_is_a7xx(adreno_gpu))
+ if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu))
return;
gmu_write(gmu, REG_A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0x9c40400);
@@ -780,6 +822,9 @@ static bool fw_block_mem(struct a6xx_gmu_bo *bo, const struct block_header *blk)
return true;
}
+#define NEXT_BLK(blk) \
+ ((const struct block_header *)((const char *)(blk) + sizeof(*(blk)) + (blk)->size))
+
static int a6xx_gmu_fw_load(struct a6xx_gmu *gmu)
{
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
@@ -792,7 +837,9 @@ static int a6xx_gmu_fw_load(struct a6xx_gmu *gmu)
u32 itcm_base = 0x00000000;
u32 dtcm_base = 0x00040000;
- if (adreno_is_a650_family(adreno_gpu) || adreno_is_a7xx(adreno_gpu))
+ if (adreno_is_a650_family(adreno_gpu) ||
+ adreno_is_a7xx(adreno_gpu) ||
+ adreno_is_a8xx(adreno_gpu))
dtcm_base = 0x10004000;
if (gmu->legacy) {
@@ -811,7 +858,7 @@ static int a6xx_gmu_fw_load(struct a6xx_gmu *gmu)
for (blk = (const struct block_header *) fw_image->data;
(const u8*) blk < fw_image->data + fw_image->size;
- blk = (const struct block_header *) &blk->data[blk->size >> 2]) {
+ blk = NEXT_BLK(blk)) {
if (blk->size == 0)
continue;
@@ -847,7 +894,9 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
{
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ struct msm_gpu *gpu = &adreno_gpu->base;
const struct a6xx_info *a6xx_info = adreno_gpu->info->a6xx;
+ const struct adreno_reglist *gbif_cx = a6xx_info->gbif_cx;
u32 fence_range_lower, fence_range_upper;
u32 chipid = 0;
int ret;
@@ -856,12 +905,15 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
if (adreno_is_a650_family(adreno_gpu) || adreno_is_a7xx(adreno_gpu)) {
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FALNEXT_INTF, 1);
gmu_write(gmu, REG_A6XX_GPU_GMU_CX_GMU_CX_FAL_INTF, 1);
+ } else if (adreno_is_a8xx(adreno_gpu)) {
+ gmu_write(gmu, REG_A8XX_GPU_GMU_CX_GMU_CX_FALNEXT_INTF, 1);
+ gmu_write(gmu, REG_A8XX_GPU_GMU_CX_GMU_CX_FAL_INTF, 1);
}
/* Turn on TCM (Tightly Coupled Memory) retention */
if (adreno_is_a7xx(adreno_gpu))
a6xx_llc_write(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL, 1);
- else
+ else if (!adreno_is_a8xx(adreno_gpu))
gmu_write(gmu, REG_A6XX_GMU_GENERAL_7, 1);
ret = a6xx_rpmh_start(gmu);
@@ -886,7 +938,10 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_ADDR, gmu->hfi.iova);
gmu_write(gmu, REG_A6XX_GMU_HFI_QTBL_INFO, 1);
- if (adreno_is_a7xx(adreno_gpu)) {
+ if (adreno_is_a8xx(adreno_gpu)) {
+ fence_range_upper = 0x32;
+ fence_range_lower = 0x8c0;
+ } else if (adreno_is_a7xx(adreno_gpu)) {
fence_range_upper = 0x32;
fence_range_lower = 0x8a0;
} else {
@@ -920,7 +975,12 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
chipid |= (adreno_gpu->chip_id << 8) & 0x0f00; /* patchid */
}
- if (adreno_is_a7xx(adreno_gpu)) {
+ if (adreno_is_a8xx(adreno_gpu)) {
+ gmu_write(gmu, REG_A8XX_GMU_GENERAL_10, chipid);
+ gmu_write(gmu, REG_A8XX_GMU_GENERAL_8,
+ (gmu->log.iova & GENMASK(31, 12)) |
+ ((gmu->log.size / SZ_4K - 1) & GENMASK(7, 0)));
+ } else if (adreno_is_a7xx(adreno_gpu)) {
gmu_write(gmu, REG_A7XX_GMU_GENERAL_10, chipid);
gmu_write(gmu, REG_A7XX_GMU_GENERAL_8,
(gmu->log.iova & GENMASK(31, 12)) |
@@ -932,6 +992,15 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
gmu->log.iova | (gmu->log.size / SZ_4K - 1));
}
+ /* For A7x and newer, do the CX GBIF configurations before GMU wake up */
+ for (int i = 0; (gbif_cx && gbif_cx[i].offset); i++)
+ gpu_write(gpu, gbif_cx[i].offset, gbif_cx[i].value);
+
+ if (adreno_is_a8xx(adreno_gpu)) {
+ gpu_write(gpu, REG_A8XX_GBIF_CX_CONFIG, 0x20023000);
+ gmu_write(gmu, REG_A6XX_GMU_MRC_GBIF_QOS_CTRL, 0x33);
+ }
+
/* Set up the lowest idle level on the GMU */
a6xx_gmu_power_config(gmu);
@@ -983,7 +1052,7 @@ static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu)
u32 val, seqmem_off = 0;
/* The second spin of A7xx GPUs messed with some register offsets.. */
- if (adreno_is_a740_family(adreno_gpu))
+ if (adreno_is_a740_family(adreno_gpu) || adreno_is_a8xx(adreno_gpu))
seqmem_off = 4;
/* Make sure there are no outstanding RPMh votes */
@@ -996,7 +1065,7 @@ static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu)
gmu_poll_timeout_rscc(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS + seqmem_off,
val, (val & 1), 100, 1000);
- if (!adreno_is_a740_family(adreno_gpu))
+ if (!adreno_is_a740_family(adreno_gpu) && !adreno_is_a8xx(adreno_gpu))
return;
gmu_poll_timeout_rscc(gmu, REG_A7XX_RSCC_TCS4_DRV0_STATUS + seqmem_off,
@@ -1024,7 +1093,10 @@ static void a6xx_gmu_force_off(struct a6xx_gmu *gmu)
* Turn off keep alive that might have been enabled by the hang
* interrupt
*/
- gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_GMU_PWR_COL_KEEPALIVE, 0);
+ if (adreno_is_a8xx(adreno_gpu))
+ gmu_write(&a6xx_gpu->gmu, REG_A8XX_GMU_GMU_PWR_COL_KEEPALIVE, 0);
+ else
+ gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_GMU_PWR_COL_KEEPALIVE, 0);
/* Flush all the queues */
a6xx_hfi_stop(gmu);
@@ -1050,7 +1122,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, true);
+ adreno_gpu->funcs->bus_halt(adreno_gpu, true);
/* Reset GPU core blocks */
a6xx_gpu_sw_reset(gpu, true);
@@ -1119,6 +1191,9 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
return ret;
}
+ /* Read the slice info on A8x GPUs */
+ a8xx_gpu_get_slice_info(gpu);
+
/* Set the bus quota to a reasonable value for boot */
a6xx_gmu_set_initial_bw(gpu, gmu);
@@ -1128,7 +1203,7 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
enable_irq(gmu->gmu_irq);
/* Check to see if we are doing a cold or warm boot */
- if (adreno_is_a7xx(adreno_gpu)) {
+ if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) {
status = a6xx_llc_read(a6xx_gpu, REG_A7XX_CX_MISC_TCM_RET_CNTL) == 1 ?
GMU_WARM_BOOT : GMU_COLD_BOOT;
} else if (gmu->legacy) {
@@ -1222,7 +1297,7 @@ static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu)
if (ret)
goto force_off;
- a6xx_bus_clear_pending_transactions(adreno_gpu, a6xx_gpu->hung);
+ adreno_gpu->funcs->bus_halt(adreno_gpu, a6xx_gpu->hung);
/* tell the GMU we want to slumber */
ret = a6xx_gmu_notify_slumber(gmu);
@@ -1457,7 +1532,7 @@ static int a6xx_gmu_rpmh_bw_votes_init(struct adreno_gpu *adreno_gpu,
vote = clamp(peak, 1, BCM_TCS_CMD_VOTE_MASK);
/* GMUs on A7xx votes on both x & y */
- if (adreno_is_a7xx(adreno_gpu))
+ if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu))
data[bcm_index] = BCM_TCS_CMD(commit, true, vote, vote);
else
data[bcm_index] = BCM_TCS_CMD(commit, true, 0, vote);
@@ -1489,13 +1564,14 @@ static unsigned int a6xx_gmu_get_arc_level(struct device *dev,
}
static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes,
- unsigned long *freqs, int freqs_count, const char *id)
+ unsigned long *freqs, int freqs_count,
+ const char *pri_id, const char *sec_id)
{
int i, j;
const u16 *pri, *sec;
size_t pri_count, sec_count;
- pri = cmd_db_read_aux_data(id, &pri_count);
+ pri = cmd_db_read_aux_data(pri_id, &pri_count);
if (IS_ERR(pri))
return PTR_ERR(pri);
/*
@@ -1506,13 +1582,7 @@ static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes,
if (!pri_count)
return -EINVAL;
- /*
- * Some targets have a separate gfx mxc rail. So try to read that first and then fall back
- * to regular mx rail if it is missing
- */
- sec = cmd_db_read_aux_data("gmxc.lvl", &sec_count);
- if (IS_ERR(sec) && sec != ERR_PTR(-EPROBE_DEFER))
- sec = cmd_db_read_aux_data("mx.lvl", &sec_count);
+ sec = cmd_db_read_aux_data(sec_id, &sec_count);
if (IS_ERR(sec))
return PTR_ERR(sec);
@@ -1566,6 +1636,57 @@ static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes,
return 0;
}
+static int a6xx_gmu_rpmh_dep_votes_init(struct device *dev, u32 *votes,
+ unsigned long *freqs, int freqs_count)
+{
+ const u16 *mx;
+ size_t count;
+
+ mx = cmd_db_read_aux_data("mx.lvl", &count);
+ if (IS_ERR(mx))
+ return PTR_ERR(mx);
+ /*
+ * The data comes back as an array of unsigned shorts so adjust the
+ * count accordingly
+ */
+ count >>= 1;
+ if (!count)
+ return -EINVAL;
+
+ /* Fix the vote for zero frequency */
+ votes[0] = 0xffffffff;
+
+ /* Construct a vote for rest of the corners */
+ for (int i = 1; i < freqs_count; i++) {
+ unsigned int level = a6xx_gmu_get_arc_level(dev, freqs[i]);
+ u8 j, index = 0;
+
+ /* Get the primary index that matches the arc level */
+ for (j = 0; j < count; j++) {
+ if (mx[j] >= level) {
+ index = j;
+ break;
+ }
+ }
+
+ if (j == count) {
+ DRM_DEV_ERROR(dev,
+ "Mx Level %u not found in the RPMh list\n",
+ level);
+ DRM_DEV_ERROR(dev, "Available levels:\n");
+ for (j = 0; j < count; j++)
+ DRM_DEV_ERROR(dev, " %u\n", mx[j]);
+
+ return -EINVAL;
+ }
+
+ /* Construct the vote */
+ votes[i] = (0x3fff << 14) | (index << 8) | (0xff);
+ }
+
+ return 0;
+}
+
/*
* The GMU votes with the RPMh for itself and on behalf of the GPU but we need
* to construct the list of votes on the CPU and send it over. Query the RPMh
@@ -1580,15 +1701,27 @@ static int a6xx_gmu_rpmh_votes_init(struct a6xx_gmu *gmu)
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
const struct a6xx_info *info = adreno_gpu->info->a6xx;
struct msm_gpu *gpu = &adreno_gpu->base;
+ const char *sec_id;
+ const u16 *gmxc;
int ret;
+ gmxc = cmd_db_read_aux_data("gmxc.lvl", NULL);
+ if (gmxc == ERR_PTR(-EPROBE_DEFER))
+ return -EPROBE_DEFER;
+
+ /* If GMxC is present, prefer that as secondary rail for GX votes */
+ sec_id = IS_ERR_OR_NULL(gmxc) ? "mx.lvl" : "gmxc.lvl";
+
/* Build the GX votes */
ret = a6xx_gmu_rpmh_arc_votes_init(&gpu->pdev->dev, gmu->gx_arc_votes,
- gmu->gpu_freqs, gmu->nr_gpu_freqs, "gfx.lvl");
+ gmu->gpu_freqs, gmu->nr_gpu_freqs, "gfx.lvl", sec_id);
/* Build the CX votes */
ret |= a6xx_gmu_rpmh_arc_votes_init(gmu->dev, gmu->cx_arc_votes,
- gmu->gmu_freqs, gmu->nr_gmu_freqs, "cx.lvl");
+ gmu->gmu_freqs, gmu->nr_gmu_freqs, "cx.lvl", "mx.lvl");
+
+ ret |= a6xx_gmu_rpmh_dep_votes_init(gmu->dev, gmu->dep_arc_votes,
+ gmu->gpu_freqs, gmu->nr_gpu_freqs);
/* Build the interconnect votes */
if (info->bcms && gmu->nr_gpu_bws > 1)
@@ -1792,27 +1925,6 @@ static int a6xx_gmu_clocks_probe(struct a6xx_gmu *gmu)
return 0;
}
-static void __iomem *a6xx_gmu_get_mmio(struct platform_device *pdev,
- const char *name)
-{
- void __iomem *ret;
- struct resource *res = platform_get_resource_byname(pdev,
- IORESOURCE_MEM, name);
-
- if (!res) {
- DRM_DEV_ERROR(&pdev->dev, "Unable to find the %s registers\n", name);
- return ERR_PTR(-EINVAL);
- }
-
- ret = ioremap(res->start, resource_size(res));
- if (!ret) {
- DRM_DEV_ERROR(&pdev->dev, "Unable to map the %s registers\n", name);
- return ERR_PTR(-EINVAL);
- }
-
- return ret;
-}
-
static int a6xx_gmu_get_irq(struct a6xx_gmu *gmu, struct platform_device *pdev,
const char *name, irq_handler_t handler)
{
@@ -1863,7 +1975,6 @@ void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu)
{
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
- struct platform_device *pdev = to_platform_device(gmu->dev);
mutex_lock(&gmu->lock);
if (!gmu->initialized) {
@@ -1892,12 +2003,11 @@ void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu)
qmp_put(gmu->qmp);
iounmap(gmu->mmio);
- if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "rscc"))
- iounmap(gmu->rscc);
gmu->mmio = NULL;
gmu->rscc = NULL;
- if (!adreno_has_gmu_wrapper(adreno_gpu)) {
+ if (!adreno_has_gmu_wrapper(adreno_gpu) &&
+ !adreno_has_rgmu(adreno_gpu)) {
a6xx_gmu_memory_free(gmu);
free_irq(gmu->gmu_irq, gmu);
@@ -1919,10 +2029,38 @@ static int cxpd_notifier_cb(struct notifier_block *nb,
return 0;
}
+static void __iomem *a6xx_gmu_get_mmio(struct platform_device *pdev,
+ const char *name, resource_size_t *start)
+{
+ void __iomem *ret;
+ struct resource *res = platform_get_resource_byname(pdev,
+ IORESOURCE_MEM, name);
+
+ if (!res) {
+ DRM_DEV_ERROR(&pdev->dev, "Unable to find the %s registers\n", name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = ioremap(res->start, resource_size(res));
+ if (!ret) {
+ DRM_DEV_ERROR(&pdev->dev, "Unable to map the %s registers\n", name);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (start)
+ *start = res->start;
+
+ return ret;
+}
+
int a6xx_gmu_wrapper_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
{
struct platform_device *pdev = of_find_device_by_node(node);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ struct msm_gpu *gpu = &adreno_gpu->base;
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
+ resource_size_t start;
+ struct resource *res;
int ret;
if (!pdev)
@@ -1939,13 +2077,29 @@ int a6xx_gmu_wrapper_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
/* Mark legacy for manual SPTPRAC control */
gmu->legacy = true;
+ /* RGMU requires clocks */
+ ret = devm_clk_bulk_get_all(gmu->dev, &gmu->clocks);
+ if (ret < 0)
+ goto err_clk;
+
+ gmu->nr_clocks = ret;
+
/* Map the GMU registers */
- gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu");
+ gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu", &start);
if (IS_ERR(gmu->mmio)) {
ret = PTR_ERR(gmu->mmio);
goto err_mmio;
}
+ res = platform_get_resource_byname(gpu->pdev, IORESOURCE_MEM, "kgsl_3d0_reg_memory");
+ if (!res) {
+ ret = -EINVAL;
+ goto err_mmio;
+ }
+
+ /* Identify gmu base offset from gpu base address */
+ gmu->mmio_offset = (u32)(start - res->start);
+
gmu->cxpd = dev_pm_domain_attach_by_name(gmu->dev, "cx");
if (IS_ERR(gmu->cxpd)) {
ret = PTR_ERR(gmu->cxpd);
@@ -1978,6 +2132,7 @@ detach_cxpd:
err_mmio:
iounmap(gmu->mmio);
+err_clk:
/* Drop reference taken in of_find_device_by_node */
put_device(gmu->dev);
@@ -1986,10 +2141,13 @@ err_mmio:
int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
{
+ struct platform_device *pdev = of_find_device_by_node(node);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ struct msm_gpu *gpu = &adreno_gpu->base;
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
- struct platform_device *pdev = of_find_device_by_node(node);
struct device_link *link;
+ resource_size_t start;
+ struct resource *res;
int ret;
if (!pdev)
@@ -2025,13 +2183,14 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
*/
gmu->dummy.size = SZ_4K;
if (adreno_is_a660_family(adreno_gpu) ||
- adreno_is_a7xx(adreno_gpu)) {
+ adreno_is_a7xx(adreno_gpu) ||
+ adreno_is_a8xx(adreno_gpu)) {
ret = a6xx_gmu_memory_alloc(gmu, &gmu->debug, SZ_4K * 7,
0x60400000, "debug");
if (ret)
goto err_memory;
- gmu->dummy.size = SZ_8K;
+ gmu->dummy.size = SZ_16K;
}
/* Allocate memory for the GMU dummy page */
@@ -2042,7 +2201,8 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
/* Note that a650 family also includes a660 family: */
if (adreno_is_a650_family(adreno_gpu) ||
- adreno_is_a7xx(adreno_gpu)) {
+ adreno_is_a7xx(adreno_gpu) ||
+ adreno_is_a8xx(adreno_gpu)) {
ret = a6xx_gmu_memory_alloc(gmu, &gmu->icache,
SZ_16M - SZ_16K, 0x04000, "icache");
if (ret)
@@ -2084,19 +2244,30 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
goto err_memory;
/* Map the GMU registers */
- gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu");
+ gmu->mmio = a6xx_gmu_get_mmio(pdev, "gmu", &start);
if (IS_ERR(gmu->mmio)) {
ret = PTR_ERR(gmu->mmio);
goto err_memory;
}
+ res = platform_get_resource_byname(gpu->pdev, IORESOURCE_MEM, "kgsl_3d0_reg_memory");
+ if (!res) {
+ ret = -EINVAL;
+ goto err_mmio;
+ }
+
+ /* Identify gmu base offset from gpu base address */
+ gmu->mmio_offset = (u32)(start - res->start);
+
if (adreno_is_a650_family(adreno_gpu) ||
adreno_is_a7xx(adreno_gpu)) {
- gmu->rscc = a6xx_gmu_get_mmio(pdev, "rscc");
+ gmu->rscc = devm_platform_ioremap_resource_byname(pdev, "rscc");
if (IS_ERR(gmu->rscc)) {
ret = -ENODEV;
goto err_mmio;
}
+ } else if (adreno_is_a8xx(adreno_gpu)) {
+ gmu->rscc = gmu->mmio + 0x19000;
} else {
gmu->rscc = gmu->mmio + 0x23000;
}
@@ -2170,8 +2341,6 @@ detach_cxpd:
err_mmio:
iounmap(gmu->mmio);
- if (platform_get_resource_byname(pdev, IORESOURCE_MEM, "rscc"))
- iounmap(gmu->rscc);
free_irq(gmu->gmu_irq, gmu);
free_irq(gmu->hfi_irq, gmu);
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h
index 06cfc294016f..2af074c8e8cf 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h
@@ -19,8 +19,8 @@ struct a6xx_gmu_bo {
u64 iova;
};
-#define GMU_MAX_GX_FREQS 16
-#define GMU_MAX_CX_FREQS 4
+#define GMU_MAX_GX_FREQS 32
+#define GMU_MAX_CX_FREQS 6
#define GMU_MAX_BCMS 3
struct a6xx_bcm {
@@ -68,6 +68,7 @@ struct a6xx_gmu {
struct drm_gpuvm *vm;
void __iomem *mmio;
+ u32 mmio_offset;
void __iomem *rscc;
int hfi_irq;
@@ -96,6 +97,7 @@ struct a6xx_gmu {
int nr_gpu_freqs;
unsigned long gpu_freqs[GMU_MAX_GX_FREQS];
u32 gx_arc_votes[GMU_MAX_GX_FREQS];
+ u32 dep_arc_votes[GMU_MAX_GX_FREQS];
struct a6xx_hfi_acd_table acd_table;
int nr_gpu_bws;
@@ -130,20 +132,23 @@ struct a6xx_gmu {
unsigned long status;
};
+#define GMU_BYTE_OFFSET(gmu, offset) (((offset) << 2) - (gmu)->mmio_offset)
+
static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset)
{
- return readl(gmu->mmio + (offset << 2));
+ /* The 'offset' is based on GPU's start address. Adjust it */
+ return readl(gmu->mmio + GMU_BYTE_OFFSET(gmu, offset));
}
static inline void gmu_write(struct a6xx_gmu *gmu, u32 offset, u32 value)
{
- writel(value, gmu->mmio + (offset << 2));
+ writel(value, gmu->mmio + GMU_BYTE_OFFSET(gmu, offset));
}
static inline void
gmu_write_bulk(struct a6xx_gmu *gmu, u32 offset, const u32 *data, u32 size)
{
- memcpy_toio(gmu->mmio + (offset << 2), data, size);
+ memcpy_toio(gmu->mmio + GMU_BYTE_OFFSET(gmu, offset), data, size);
wmb();
}
@@ -160,17 +165,17 @@ static inline u64 gmu_read64(struct a6xx_gmu *gmu, u32 lo, u32 hi)
{
u64 val;
- val = (u64) readl(gmu->mmio + (lo << 2));
- val |= ((u64) readl(gmu->mmio + (hi << 2)) << 32);
+ val = gmu_read(gmu, lo);
+ val |= ((u64) gmu_read(gmu, hi) << 32);
return val;
}
#define gmu_poll_timeout(gmu, addr, val, cond, interval, timeout) \
- readl_poll_timeout((gmu)->mmio + ((addr) << 2), val, cond, \
- interval, timeout)
+ readl_poll_timeout((gmu)->mmio + (GMU_BYTE_OFFSET(gmu, addr)), val, \
+ cond, interval, timeout)
#define gmu_poll_timeout_atomic(gmu, addr, val, cond, interval, timeout) \
- readl_poll_timeout_atomic((gmu)->mmio + ((addr) << 2), val, cond, \
+ readl_poll_timeout_atomic((gmu)->mmio + (GMU_BYTE_OFFSET(gmu, addr)), val, cond, \
interval, timeout)
static inline u32 gmu_read_rscc(struct a6xx_gmu *gmu, u32 offset)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index b8f8ae940b55..0200a7e71cdf 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -157,7 +157,7 @@ static void update_shadow_rptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
}
}
-static void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
+void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
@@ -224,7 +224,7 @@ static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu,
OUT_RING(ring, submit->seqno - 1);
OUT_PKT7(ring, CP_THREAD_CONTROL, 1);
- OUT_RING(ring, CP_SET_THREAD_BOTH);
+ OUT_RING(ring, CP_THREAD_CONTROL_0_SYNC_THREADS | CP_SET_THREAD_BOTH);
/* Reset state used to synchronize BR and BV */
OUT_PKT7(ring, CP_RESET_CONTEXT_STATE, 1);
@@ -235,18 +235,31 @@ static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu,
CP_RESET_CONTEXT_STATE_0_RESET_GLOBAL_LOCAL_TS);
OUT_PKT7(ring, CP_THREAD_CONTROL, 1);
- OUT_RING(ring, CP_SET_THREAD_BR);
+ OUT_RING(ring, CP_THREAD_CONTROL_0_SYNC_THREADS | CP_SET_THREAD_BOTH);
+
+ OUT_PKT7(ring, CP_EVENT_WRITE, 1);
+ OUT_RING(ring, LRZ_FLUSH_INVALIDATE);
+
+ OUT_PKT7(ring, CP_THREAD_CONTROL, 1);
+ OUT_RING(ring, CP_THREAD_CONTROL_0_SYNC_THREADS | CP_SET_THREAD_BR);
}
if (!sysprof) {
- if (!adreno_is_a7xx(adreno_gpu)) {
+ if (!(adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu))) {
/* Turn off protected mode to write to special registers */
OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
OUT_RING(ring, 0);
}
- OUT_PKT4(ring, REG_A6XX_RBBM_PERFCTR_SRAM_INIT_CMD, 1);
- OUT_RING(ring, 1);
+ if (adreno_is_a8xx(adreno_gpu)) {
+ OUT_PKT4(ring, REG_A8XX_RBBM_PERFCTR_SRAM_INIT_CMD, 1);
+ OUT_RING(ring, 1);
+ OUT_PKT4(ring, REG_A8XX_RBBM_SLICE_PERFCTR_SRAM_INIT_CMD, 1);
+ OUT_RING(ring, 1);
+ } else {
+ OUT_PKT4(ring, REG_A6XX_RBBM_PERFCTR_SRAM_INIT_CMD, 1);
+ OUT_RING(ring, 1);
+ }
}
/* Execute the table update */
@@ -275,7 +288,7 @@ static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu,
* to make sure BV doesn't race ahead while BR is still switching
* pagetables.
*/
- if (adreno_is_a7xx(&a6xx_gpu->base)) {
+ if (adreno_is_a7xx(&a6xx_gpu->base) || adreno_is_a8xx(&a6xx_gpu->base)) {
OUT_PKT7(ring, CP_THREAD_CONTROL, 1);
OUT_RING(ring, CP_THREAD_CONTROL_0_SYNC_THREADS | CP_SET_THREAD_BR);
}
@@ -289,20 +302,22 @@ static void a6xx_set_pagetable(struct a6xx_gpu *a6xx_gpu,
OUT_RING(ring, CACHE_INVALIDATE);
if (!sysprof) {
+ u32 reg_status = adreno_is_a8xx(adreno_gpu) ?
+ REG_A8XX_RBBM_PERFCTR_SRAM_INIT_STATUS :
+ REG_A6XX_RBBM_PERFCTR_SRAM_INIT_STATUS;
/*
* Wait for SRAM clear after the pgtable update, so the
* two can happen in parallel:
*/
OUT_PKT7(ring, CP_WAIT_REG_MEM, 6);
OUT_RING(ring, CP_WAIT_REG_MEM_0_FUNCTION(WRITE_EQ));
- OUT_RING(ring, CP_WAIT_REG_MEM_POLL_ADDR_LO(
- REG_A6XX_RBBM_PERFCTR_SRAM_INIT_STATUS));
+ OUT_RING(ring, CP_WAIT_REG_MEM_POLL_ADDR_LO(reg_status));
OUT_RING(ring, CP_WAIT_REG_MEM_POLL_ADDR_HI(0));
OUT_RING(ring, CP_WAIT_REG_MEM_3_REF(0x1));
OUT_RING(ring, CP_WAIT_REG_MEM_4_MASK(0x1));
OUT_RING(ring, CP_WAIT_REG_MEM_5_DELAY_LOOP_CYCLES(0));
- if (!adreno_is_a7xx(adreno_gpu)) {
+ if (!(adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu))) {
/* Re-enable protected mode: */
OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1);
OUT_RING(ring, 1);
@@ -375,7 +390,7 @@ static void a6xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
rbmemptr_stats(ring, index, alwayson_end));
/* Write the fence to the scratch register */
- OUT_PKT4(ring, REG_A6XX_CP_SCRATCH_REG(2), 1);
+ OUT_PKT4(ring, REG_A6XX_CP_SCRATCH(2), 1);
OUT_RING(ring, submit->seqno);
/*
@@ -440,6 +455,7 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct msm_ringbuffer *ring = submit->ring;
+ u32 rbbm_perfctr_cp0, cp_always_on_counter;
unsigned int i, ibs = 0;
adreno_check_and_reenable_stall(adreno_gpu);
@@ -460,10 +476,16 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
if (gpu->nr_rings > 1)
a6xx_emit_set_pseudo_reg(ring, a6xx_gpu, submit->queue);
- get_stats_counter(ring, REG_A7XX_RBBM_PERFCTR_CP(0),
- rbmemptr_stats(ring, index, cpcycles_start));
- get_stats_counter(ring, REG_A6XX_CP_ALWAYS_ON_COUNTER,
- rbmemptr_stats(ring, index, alwayson_start));
+ if (adreno_is_a8xx(adreno_gpu)) {
+ rbbm_perfctr_cp0 = REG_A8XX_RBBM_PERFCTR_CP(0);
+ cp_always_on_counter = REG_A8XX_CP_ALWAYS_ON_COUNTER;
+ } else {
+ rbbm_perfctr_cp0 = REG_A7XX_RBBM_PERFCTR_CP(0);
+ cp_always_on_counter = REG_A6XX_CP_ALWAYS_ON_COUNTER;
+ }
+
+ get_stats_counter(ring, rbbm_perfctr_cp0, rbmemptr_stats(ring, index, cpcycles_start));
+ get_stats_counter(ring, cp_always_on_counter, rbmemptr_stats(ring, index, alwayson_start));
OUT_PKT7(ring, CP_THREAD_CONTROL, 1);
OUT_RING(ring, CP_SET_THREAD_BOTH);
@@ -510,14 +532,17 @@ static void a7xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit)
OUT_RING(ring, 0x00e); /* IB1LIST end */
}
- get_stats_counter(ring, REG_A7XX_RBBM_PERFCTR_CP(0),
- rbmemptr_stats(ring, index, cpcycles_end));
- get_stats_counter(ring, REG_A6XX_CP_ALWAYS_ON_COUNTER,
- rbmemptr_stats(ring, index, alwayson_end));
+ get_stats_counter(ring, rbbm_perfctr_cp0, rbmemptr_stats(ring, index, cpcycles_end));
+ get_stats_counter(ring, cp_always_on_counter, rbmemptr_stats(ring, index, alwayson_end));
/* Write the fence to the scratch register */
- OUT_PKT4(ring, REG_A6XX_CP_SCRATCH_REG(2), 1);
- OUT_RING(ring, submit->seqno);
+ if (adreno_is_a8xx(adreno_gpu)) {
+ OUT_PKT4(ring, REG_A8XX_CP_SCRATCH_GLOBAL(2), 1);
+ OUT_RING(ring, submit->seqno);
+ } else {
+ OUT_PKT4(ring, REG_A6XX_CP_SCRATCH(2), 1);
+ OUT_RING(ring, submit->seqno);
+ }
OUT_PKT7(ring, CP_THREAD_CONTROL, 1);
OUT_RING(ring, CP_SET_THREAD_BR);
@@ -612,15 +637,26 @@ static void a6xx_set_hwcg(struct msm_gpu *gpu, bool state)
if (adreno_is_a630(adreno_gpu))
clock_cntl_on = 0x8aa8aa02;
- else if (adreno_is_a610(adreno_gpu))
+ else if (adreno_is_a610(adreno_gpu) || adreno_is_a612(adreno_gpu))
clock_cntl_on = 0xaaa8aa82;
else if (adreno_is_a702(adreno_gpu))
clock_cntl_on = 0xaaaaaa82;
else
clock_cntl_on = 0x8aa8aa82;
- cgc_delay = adreno_is_a615_family(adreno_gpu) ? 0x111 : 0x10111;
- cgc_hyst = adreno_is_a615_family(adreno_gpu) ? 0x555 : 0x5555;
+ if (adreno_is_a612(adreno_gpu))
+ cgc_delay = 0x11;
+ else if (adreno_is_a615_family(adreno_gpu))
+ cgc_delay = 0x111;
+ else
+ cgc_delay = 0x10111;
+
+ if (adreno_is_a612(adreno_gpu))
+ cgc_hyst = 0x55;
+ else if (adreno_is_a615_family(adreno_gpu))
+ cgc_hyst = 0x555;
+ else
+ cgc_hyst = 0x5555;
gmu_write(&a6xx_gpu->gmu, REG_A6XX_GPU_GMU_AO_GMU_CGC_MODE_CNTL,
state ? adreno_gpu->info->a6xx->gmu_cgc_mode : 0);
@@ -706,14 +742,20 @@ static int a6xx_calc_ubwc_config(struct adreno_gpu *gpu)
/* Copy the data into the internal struct to drop the const qualifier (temporarily) */
*cfg = *common_cfg;
- cfg->ubwc_swizzle = 0x6;
- cfg->highest_bank_bit = 15;
+ /* Use common config as is for A8x */
+ if (!adreno_is_a8xx(gpu)) {
+ cfg->ubwc_swizzle = 0x6;
+ cfg->highest_bank_bit = 15;
+ }
if (adreno_is_a610(gpu)) {
cfg->highest_bank_bit = 13;
cfg->ubwc_swizzle = 0x7;
}
+ if (adreno_is_a612(gpu))
+ cfg->highest_bank_bit = 14;
+
if (adreno_is_a618(gpu))
cfg->highest_bank_bit = 14;
@@ -993,7 +1035,7 @@ static bool a6xx_ucode_check_version(struct a6xx_gpu *a6xx_gpu,
return false;
/* A7xx is safe! */
- if (adreno_is_a7xx(adreno_gpu) || adreno_is_a702(adreno_gpu))
+ if (adreno_is_a7xx(adreno_gpu) || adreno_is_a702(adreno_gpu) || adreno_is_a8xx(adreno_gpu))
return true;
/*
@@ -1076,6 +1118,23 @@ static int a6xx_ucode_load(struct msm_gpu *gpu)
}
}
+ if (!a6xx_gpu->aqe_bo && adreno_gpu->fw[ADRENO_FW_AQE]) {
+ a6xx_gpu->aqe_bo = adreno_fw_create_bo(gpu,
+ adreno_gpu->fw[ADRENO_FW_AQE], &a6xx_gpu->aqe_iova);
+
+ if (IS_ERR(a6xx_gpu->aqe_bo)) {
+ int ret = PTR_ERR(a6xx_gpu->aqe_bo);
+
+ a6xx_gpu->aqe_bo = NULL;
+ DRM_DEV_ERROR(&gpu->pdev->dev,
+ "Could not allocate AQE ucode: %d\n", ret);
+
+ return ret;
+ }
+
+ msm_gem_object_set_name(a6xx_gpu->aqe_bo, "aqefw");
+ }
+
/*
* Expanded APRIV and targets that support WHERE_AM_I both need a
* privileged buffer to store the RPTR shadow
@@ -1107,7 +1166,7 @@ static int a6xx_ucode_load(struct msm_gpu *gpu)
return 0;
}
-static int a6xx_zap_shader_init(struct msm_gpu *gpu)
+int a6xx_zap_shader_init(struct msm_gpu *gpu)
{
static bool loaded;
int ret;
@@ -1220,17 +1279,20 @@ static int hw_init(struct msm_gpu *gpu)
/* enable hardware clockgating */
a6xx_set_hwcg(gpu, true);
- /* VBIF/GBIF start*/
- if (adreno_is_a610_family(adreno_gpu) ||
- adreno_is_a640_family(adreno_gpu) ||
- adreno_is_a650_family(adreno_gpu) ||
- adreno_is_a7xx(adreno_gpu)) {
+ /* For gmuwrapper implementations, do the VBIF/GBIF CX configuration here */
+ if (adreno_is_a610_family(adreno_gpu)) {
gpu_write(gpu, REG_A6XX_GBIF_QSB_SIDE0, 0x00071620);
gpu_write(gpu, REG_A6XX_GBIF_QSB_SIDE1, 0x00071620);
gpu_write(gpu, REG_A6XX_GBIF_QSB_SIDE2, 0x00071620);
gpu_write(gpu, REG_A6XX_GBIF_QSB_SIDE3, 0x00071620);
- gpu_write(gpu, REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL,
- adreno_is_a7xx(adreno_gpu) ? 0x2120212 : 0x3);
+ }
+
+ if (adreno_is_a610_family(adreno_gpu) ||
+ adreno_is_a640_family(adreno_gpu) ||
+ adreno_is_a650_family(adreno_gpu)) {
+ gpu_write(gpu, REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x3);
+ } else if (adreno_is_a7xx(adreno_gpu)) {
+ gpu_write(gpu, REG_A6XX_RBBM_GBIF_CLIENT_QOS_CNTL, 0x2120212);
} else {
gpu_write(gpu, REG_A6XX_RBBM_VBIF_CLIENT_QOS_CNTL, 0x3);
}
@@ -1285,10 +1347,10 @@ static int hw_init(struct msm_gpu *gpu)
}
if (adreno_is_a660_family(adreno_gpu))
- gpu_write(gpu, REG_A6XX_CP_LPAC_PROG_FIFO_SIZE, 0x00000020);
+ gpu_write(gpu, REG_A7XX_CP_LPAC_PROG_FIFO_SIZE, 0x00000020);
/* Setting the mem pool size */
- if (adreno_is_a610(adreno_gpu)) {
+ if (adreno_is_a610(adreno_gpu) || adreno_is_a612(adreno_gpu)) {
gpu_write(gpu, REG_A6XX_CP_MEM_POOL_SIZE, 48);
gpu_write(gpu, REG_A6XX_CP_MEM_POOL_DBG_ADDR, 47);
} else if (adreno_is_a702(adreno_gpu)) {
@@ -1321,7 +1383,8 @@ static int hw_init(struct msm_gpu *gpu)
a6xx_set_ubwc_config(gpu);
/* Enable fault detection */
- if (adreno_is_a730(adreno_gpu) ||
+ if (adreno_is_a612(adreno_gpu) ||
+ adreno_is_a730(adreno_gpu) ||
adreno_is_a740_family(adreno_gpu))
gpu_write(gpu, REG_A6XX_RBBM_INTERFACE_HANG_INT_CNTL, (1 << 30) | 0xcfffff);
else if (adreno_is_a690(adreno_gpu))
@@ -1540,7 +1603,7 @@ static void a6xx_recover(struct msm_gpu *gpu)
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
- int i, active_submits;
+ int active_submits;
adreno_dump_info(gpu);
@@ -1548,10 +1611,6 @@ static void a6xx_recover(struct msm_gpu *gpu)
/* Sometimes crashstate capture is skipped, so SQE should be halted here again */
gpu_write(gpu, REG_A6XX_CP_SQE_CNTL, 3);
- for (i = 0; i < 8; i++)
- DRM_DEV_INFO(&gpu->pdev->dev, "CP_SCRATCH_REG%d: %u\n", i,
- gpu_read(gpu, REG_A6XX_CP_SCRATCH_REG(i)));
-
if (hang_debug)
a6xx_dump(gpu);
@@ -1576,9 +1635,9 @@ static void a6xx_recover(struct msm_gpu *gpu)
*/
gpu->active_submits = 0;
- if (adreno_has_gmu_wrapper(adreno_gpu)) {
+ if (adreno_has_gmu_wrapper(adreno_gpu) || adreno_has_rgmu(adreno_gpu)) {
/* Drain the outstanding traffic on memory buses */
- a6xx_bus_clear_pending_transactions(adreno_gpu, true);
+ adreno_gpu->funcs->bus_halt(adreno_gpu, true);
/* Reset the GPU to a clean state */
a6xx_gpu_sw_reset(gpu, true);
@@ -1737,10 +1796,10 @@ static int a6xx_fault_handler(void *arg, unsigned long iova, int flags, void *da
const char *block = "unknown";
u32 scratch[] = {
- gpu_read(gpu, REG_A6XX_CP_SCRATCH_REG(4)),
- gpu_read(gpu, REG_A6XX_CP_SCRATCH_REG(5)),
- gpu_read(gpu, REG_A6XX_CP_SCRATCH_REG(6)),
- gpu_read(gpu, REG_A6XX_CP_SCRATCH_REG(7)),
+ gpu_read(gpu, REG_A6XX_CP_SCRATCH(4)),
+ gpu_read(gpu, REG_A6XX_CP_SCRATCH(5)),
+ gpu_read(gpu, REG_A6XX_CP_SCRATCH(6)),
+ gpu_read(gpu, REG_A6XX_CP_SCRATCH(7)),
};
if (info)
@@ -2072,7 +2131,7 @@ static int a7xx_cx_mem_init(struct a6xx_gpu *a6xx_gpu)
u32 fuse_val;
int ret;
- if (adreno_is_a750(adreno_gpu)) {
+ if (adreno_is_a750(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) {
/*
* Assume that if qcom scm isn't available, that whatever
* replacement allows writing the fuse register ourselves.
@@ -2098,9 +2157,9 @@ static int a7xx_cx_mem_init(struct a6xx_gpu *a6xx_gpu)
return ret;
/*
- * On a750 raytracing may be disabled by the firmware, find out
- * whether that's the case. The scm call above sets the fuse
- * register.
+ * On A7XX_GEN3 and newer, raytracing may be disabled by the
+ * firmware, find out whether that's the case. The scm call
+ * above sets the fuse register.
*/
fuse_val = a6xx_llc_read(a6xx_gpu,
REG_A7XX_CX_MISC_SW_FUSE_VALUE);
@@ -2161,7 +2220,7 @@ void a6xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_
void a6xx_gpu_sw_reset(struct msm_gpu *gpu, bool assert)
{
/* 11nm chips (e.g. ones with A610) have hw issues with the reset line! */
- if (adreno_is_a610(to_adreno_gpu(gpu)))
+ if (adreno_is_a610(to_adreno_gpu(gpu)) || adreno_is_a8xx(to_adreno_gpu(gpu)))
return;
gpu_write(gpu, REG_A6XX_RBBM_SW_RESET_CMD, assert);
@@ -2192,7 +2251,12 @@ static int a6xx_gmu_pm_resume(struct msm_gpu *gpu)
msm_devfreq_resume(gpu);
- adreno_is_a7xx(adreno_gpu) ? a7xx_llc_activate(a6xx_gpu) : a6xx_llc_activate(a6xx_gpu);
+ if (adreno_is_a8xx(adreno_gpu))
+ a8xx_llc_activate(a6xx_gpu);
+ else if (adreno_is_a7xx(adreno_gpu))
+ a7xx_llc_activate(a6xx_gpu);
+ else
+ a6xx_llc_activate(a6xx_gpu);
return ret;
}
@@ -2229,6 +2293,12 @@ static int a6xx_pm_resume(struct msm_gpu *gpu)
if (ret)
goto err_bulk_clk;
+ ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks);
+ if (ret) {
+ clk_bulk_disable_unprepare(gpu->nr_clocks, gpu->grp_clks);
+ goto err_bulk_clk;
+ }
+
if (adreno_is_a619_holi(adreno_gpu))
a6xx_sptprac_enable(gmu);
@@ -2242,8 +2312,10 @@ err_bulk_clk:
err_set_opp:
mutex_unlock(&a6xx_gpu->gmu.lock);
- if (!ret)
+ if (!ret) {
msm_devfreq_resume(gpu);
+ a6xx_llc_activate(a6xx_gpu);
+ }
return ret;
}
@@ -2284,17 +2356,20 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu)
trace_msm_gpu_suspend(0);
+ a6xx_llc_deactivate(a6xx_gpu);
+
msm_devfreq_suspend(gpu);
mutex_lock(&a6xx_gpu->gmu.lock);
/* Drain the outstanding traffic on memory buses */
- a6xx_bus_clear_pending_transactions(adreno_gpu, true);
+ adreno_gpu->funcs->bus_halt(adreno_gpu, true);
if (adreno_is_a619_holi(adreno_gpu))
a6xx_sptprac_disable(gmu);
clk_bulk_disable_unprepare(gpu->nr_clocks, gpu->grp_clks);
+ clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks);
pm_runtime_put_sync(gmu->gxpd);
dev_pm_opp_set_opp(&gpu->pdev->dev, NULL);
@@ -2345,6 +2420,11 @@ static void a6xx_destroy(struct msm_gpu *gpu)
drm_gem_object_put(a6xx_gpu->sqe_bo);
}
+ if (a6xx_gpu->aqe_bo) {
+ msm_gem_unpin_iova(a6xx_gpu->aqe_bo, gpu->vm);
+ drm_gem_object_put(a6xx_gpu->aqe_bo);
+ }
+
if (a6xx_gpu->shadow_bo) {
msm_gem_unpin_iova(a6xx_gpu->shadow_bo, gpu->vm);
drm_gem_object_put(a6xx_gpu->shadow_bo);
@@ -2527,7 +2607,105 @@ static int a6xx_set_supported_hw(struct device *dev, const struct adreno_info *i
return 0;
}
-static const struct adreno_gpu_funcs funcs = {
+static struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
+{
+ struct msm_drm_private *priv = dev->dev_private;
+ struct platform_device *pdev = priv->gpu_pdev;
+ struct adreno_platform_config *config = pdev->dev.platform_data;
+ struct device_node *node;
+ struct a6xx_gpu *a6xx_gpu;
+ struct adreno_gpu *adreno_gpu;
+ struct msm_gpu *gpu;
+ extern int enable_preemption;
+ bool is_a7xx;
+ int ret, nr_rings = 1;
+
+ a6xx_gpu = kzalloc(sizeof(*a6xx_gpu), GFP_KERNEL);
+ if (!a6xx_gpu)
+ return ERR_PTR(-ENOMEM);
+
+ adreno_gpu = &a6xx_gpu->base;
+ gpu = &adreno_gpu->base;
+
+ mutex_init(&a6xx_gpu->gmu.lock);
+
+ adreno_gpu->registers = NULL;
+
+ /* Check if there is a GMU phandle and set it up */
+ node = of_parse_phandle(pdev->dev.of_node, "qcom,gmu", 0);
+ /* FIXME: How do we gracefully handle this? */
+ BUG_ON(!node);
+
+ adreno_gpu->gmu_is_wrapper = of_device_is_compatible(node, "qcom,adreno-gmu-wrapper");
+
+ adreno_gpu->base.hw_apriv =
+ !!(config->info->quirks & ADRENO_QUIRK_HAS_HW_APRIV);
+
+ /* gpu->info only gets assigned in adreno_gpu_init(). A8x is included intentionally */
+ is_a7xx = config->info->family >= ADRENO_7XX_GEN1;
+
+ a6xx_llc_slices_init(pdev, a6xx_gpu, is_a7xx);
+
+ ret = a6xx_set_supported_hw(&pdev->dev, config->info);
+ if (ret) {
+ a6xx_llc_slices_destroy(a6xx_gpu);
+ kfree(a6xx_gpu);
+ return ERR_PTR(ret);
+ }
+
+ if ((enable_preemption == 1) || (enable_preemption == -1 &&
+ (config->info->quirks & ADRENO_QUIRK_PREEMPTION)))
+ nr_rings = 4;
+
+ ret = adreno_gpu_init(dev, pdev, adreno_gpu, config->info->funcs, nr_rings);
+ if (ret) {
+ a6xx_destroy(&(a6xx_gpu->base.base));
+ return ERR_PTR(ret);
+ }
+
+ /*
+ * For now only clamp to idle freq for devices where this is known not
+ * to cause power supply issues:
+ */
+ if (adreno_is_a618(adreno_gpu) || adreno_is_7c3(adreno_gpu))
+ priv->gpu_clamp_to_idle = true;
+
+ if (adreno_has_gmu_wrapper(adreno_gpu) || adreno_has_rgmu(adreno_gpu))
+ ret = a6xx_gmu_wrapper_init(a6xx_gpu, node);
+ else
+ ret = a6xx_gmu_init(a6xx_gpu, node);
+ of_node_put(node);
+ if (ret) {
+ a6xx_destroy(&(a6xx_gpu->base.base));
+ return ERR_PTR(ret);
+ }
+
+ if (adreno_is_a7xx(adreno_gpu) || adreno_is_a8xx(adreno_gpu)) {
+ ret = a7xx_cx_mem_init(a6xx_gpu);
+ if (ret) {
+ a6xx_destroy(&(a6xx_gpu->base.base));
+ return ERR_PTR(ret);
+ }
+ }
+
+ adreno_gpu->uche_trap_base = 0x1fffffffff000ull;
+
+ msm_mmu_set_fault_handler(to_msm_vm(gpu->vm)->mmu, gpu,
+ adreno_gpu->funcs->mmu_fault_handler);
+
+ ret = a6xx_calc_ubwc_config(adreno_gpu);
+ if (ret) {
+ a6xx_destroy(&(a6xx_gpu->base.base));
+ return ERR_PTR(ret);
+ }
+
+ /* Set up the preemption specific bits and pieces for each ringbuffer */
+ a6xx_preempt_init(gpu);
+
+ return gpu;
+}
+
+const struct adreno_gpu_funcs a6xx_gpu_funcs = {
.base = {
.get_param = adreno_get_param,
.set_param = adreno_set_param,
@@ -2554,12 +2732,14 @@ static const struct adreno_gpu_funcs funcs = {
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
.progress = a6xx_progress,
- .sysprof_setup = a6xx_gmu_sysprof_setup,
},
+ .init = a6xx_gpu_init,
.get_timestamp = a6xx_gmu_get_timestamp,
+ .bus_halt = a6xx_bus_clear_pending_transactions,
+ .mmu_fault_handler = a6xx_fault_handler,
};
-static const struct adreno_gpu_funcs funcs_gmuwrapper = {
+const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs = {
.base = {
.get_param = adreno_get_param,
.set_param = adreno_set_param,
@@ -2585,10 +2765,13 @@ static const struct adreno_gpu_funcs funcs_gmuwrapper = {
.get_rptr = a6xx_get_rptr,
.progress = a6xx_progress,
},
+ .init = a6xx_gpu_init,
.get_timestamp = a6xx_get_timestamp,
+ .bus_halt = a6xx_bus_clear_pending_transactions,
+ .mmu_fault_handler = a6xx_fault_handler,
};
-static const struct adreno_gpu_funcs funcs_a7xx = {
+const struct adreno_gpu_funcs a7xx_gpu_funcs = {
.base = {
.get_param = adreno_get_param,
.set_param = adreno_set_param,
@@ -2615,111 +2798,36 @@ static const struct adreno_gpu_funcs funcs_a7xx = {
.create_private_vm = a6xx_create_private_vm,
.get_rptr = a6xx_get_rptr,
.progress = a6xx_progress,
- .sysprof_setup = a6xx_gmu_sysprof_setup,
},
+ .init = a6xx_gpu_init,
.get_timestamp = a6xx_gmu_get_timestamp,
+ .bus_halt = a6xx_bus_clear_pending_transactions,
+ .mmu_fault_handler = a6xx_fault_handler,
};
-struct msm_gpu *a6xx_gpu_init(struct drm_device *dev)
-{
- struct msm_drm_private *priv = dev->dev_private;
- struct platform_device *pdev = priv->gpu_pdev;
- struct adreno_platform_config *config = pdev->dev.platform_data;
- struct device_node *node;
- struct a6xx_gpu *a6xx_gpu;
- struct adreno_gpu *adreno_gpu;
- struct msm_gpu *gpu;
- extern int enable_preemption;
- bool is_a7xx;
- int ret;
-
- a6xx_gpu = kzalloc(sizeof(*a6xx_gpu), GFP_KERNEL);
- if (!a6xx_gpu)
- return ERR_PTR(-ENOMEM);
-
- adreno_gpu = &a6xx_gpu->base;
- gpu = &adreno_gpu->base;
-
- mutex_init(&a6xx_gpu->gmu.lock);
-
- adreno_gpu->registers = NULL;
-
- /* Check if there is a GMU phandle and set it up */
- node = of_parse_phandle(pdev->dev.of_node, "qcom,gmu", 0);
- /* FIXME: How do we gracefully handle this? */
- BUG_ON(!node);
-
- adreno_gpu->gmu_is_wrapper = of_device_is_compatible(node, "qcom,adreno-gmu-wrapper");
-
- adreno_gpu->base.hw_apriv =
- !!(config->info->quirks & ADRENO_QUIRK_HAS_HW_APRIV);
-
- /* gpu->info only gets assigned in adreno_gpu_init() */
- is_a7xx = config->info->family == ADRENO_7XX_GEN1 ||
- config->info->family == ADRENO_7XX_GEN2 ||
- config->info->family == ADRENO_7XX_GEN3;
-
- a6xx_llc_slices_init(pdev, a6xx_gpu, is_a7xx);
-
- ret = a6xx_set_supported_hw(&pdev->dev, config->info);
- if (ret) {
- a6xx_llc_slices_destroy(a6xx_gpu);
- kfree(a6xx_gpu);
- return ERR_PTR(ret);
- }
-
- if ((enable_preemption == 1) || (enable_preemption == -1 &&
- (config->info->quirks & ADRENO_QUIRK_PREEMPTION)))
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs_a7xx, 4);
- else if (is_a7xx)
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs_a7xx, 1);
- else if (adreno_has_gmu_wrapper(adreno_gpu))
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs_gmuwrapper, 1);
- else
- ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1);
- if (ret) {
- a6xx_destroy(&(a6xx_gpu->base.base));
- return ERR_PTR(ret);
- }
-
- /*
- * For now only clamp to idle freq for devices where this is known not
- * to cause power supply issues:
- */
- if (adreno_is_a618(adreno_gpu) || adreno_is_7c3(adreno_gpu))
- priv->gpu_clamp_to_idle = true;
-
- if (adreno_has_gmu_wrapper(adreno_gpu))
- ret = a6xx_gmu_wrapper_init(a6xx_gpu, node);
- else
- ret = a6xx_gmu_init(a6xx_gpu, node);
- of_node_put(node);
- if (ret) {
- a6xx_destroy(&(a6xx_gpu->base.base));
- return ERR_PTR(ret);
- }
-
- if (adreno_is_a7xx(adreno_gpu)) {
- ret = a7xx_cx_mem_init(a6xx_gpu);
- if (ret) {
- a6xx_destroy(&(a6xx_gpu->base.base));
- return ERR_PTR(ret);
- }
- }
-
- adreno_gpu->uche_trap_base = 0x1fffffffff000ull;
-
- msm_mmu_set_fault_handler(to_msm_vm(gpu->vm)->mmu, gpu,
- a6xx_fault_handler);
-
- ret = a6xx_calc_ubwc_config(adreno_gpu);
- if (ret) {
- a6xx_destroy(&(a6xx_gpu->base.base));
- return ERR_PTR(ret);
- }
-
- /* Set up the preemption specific bits and pieces for each ringbuffer */
- a6xx_preempt_init(gpu);
-
- return gpu;
-}
+const struct adreno_gpu_funcs a8xx_gpu_funcs = {
+ .base = {
+ .get_param = adreno_get_param,
+ .set_param = adreno_set_param,
+ .hw_init = a8xx_hw_init,
+ .ucode_load = a6xx_ucode_load,
+ .pm_suspend = a6xx_gmu_pm_suspend,
+ .pm_resume = a6xx_gmu_pm_resume,
+ .recover = a8xx_recover,
+ .submit = a7xx_submit,
+ .active_ring = a6xx_active_ring,
+ .irq = a8xx_irq,
+ .destroy = a6xx_destroy,
+ .gpu_busy = a8xx_gpu_busy,
+ .gpu_get_freq = a6xx_gmu_get_freq,
+ .gpu_set_freq = a6xx_gpu_set_freq,
+ .create_vm = a6xx_create_vm,
+ .create_private_vm = a6xx_create_private_vm,
+ .get_rptr = a6xx_get_rptr,
+ .progress = a8xx_progress,
+ },
+ .init = a6xx_gpu_init,
+ .get_timestamp = a8xx_gmu_get_timestamp,
+ .bus_halt = a8xx_bus_clear_pending_transactions,
+ .mmu_fault_handler = a8xx_fault_handler,
+};
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index 0b17d36c36a9..6820216ec5fc 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -46,6 +46,9 @@ struct a6xx_info {
const struct adreno_protect *protect;
const struct adreno_reglist_list *pwrup_reglist;
const struct adreno_reglist_list *ifpc_reglist;
+ const struct adreno_reglist *gbif_cx;
+ const struct adreno_reglist_pipe *nonctxt_reglist;
+ u32 max_slices;
u32 gmu_chipid;
u32 gmu_cgc_mode;
u32 prim_fifo_threshold;
@@ -57,6 +60,8 @@ struct a6xx_gpu {
struct drm_gem_object *sqe_bo;
uint64_t sqe_iova;
+ struct drm_gem_object *aqe_bo;
+ uint64_t aqe_iova;
struct msm_ringbuffer *cur_ring;
struct msm_ringbuffer *next_ring;
@@ -101,6 +106,11 @@ struct a6xx_gpu {
void *htw_llc_slice;
bool have_mmu500;
bool hung;
+
+ u32 cached_aperture;
+ spinlock_t aperture_lock;
+
+ u32 slice_mask;
};
#define to_a6xx_gpu(x) container_of(x, struct a6xx_gpu, base)
@@ -216,6 +226,11 @@ struct a7xx_cp_smmu_info {
#define A6XX_PROTECT_RDONLY(_reg, _len) \
((((_len) & 0x3FFF) << 18) | ((_reg) & 0x3FFFF))
+extern const struct adreno_gpu_funcs a6xx_gpu_funcs;
+extern const struct adreno_gpu_funcs a6xx_gmuwrapper_funcs;
+extern const struct adreno_gpu_funcs a7xx_gpu_funcs;
+extern const struct adreno_gpu_funcs a8xx_gpu_funcs;
+
static inline bool a6xx_has_gbif(struct adreno_gpu *gpu)
{
if(adreno_is_a630(gpu))
@@ -298,5 +313,19 @@ int a6xx_gpu_state_put(struct msm_gpu_state *state);
void a6xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_off);
void a6xx_gpu_sw_reset(struct msm_gpu *gpu, bool assert);
int a6xx_fenced_write(struct a6xx_gpu *gpu, u32 offset, u64 value, u32 mask, bool is_64b);
-
+void a6xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
+int a6xx_zap_shader_init(struct msm_gpu *gpu);
+
+void a8xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_off);
+int a8xx_fault_handler(void *arg, unsigned long iova, int flags, void *data);
+void a8xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
+int a8xx_gmu_get_timestamp(struct msm_gpu *gpu, uint64_t *value);
+u64 a8xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate);
+int a8xx_gpu_feature_probe(struct msm_gpu *gpu);
+void a8xx_gpu_get_slice_info(struct msm_gpu *gpu);
+int a8xx_hw_init(struct msm_gpu *gpu);
+irqreturn_t a8xx_irq(struct msm_gpu *gpu);
+void a8xx_llc_activate(struct a6xx_gpu *a6xx_gpu);
+bool a8xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring);
+void a8xx_recover(struct msm_gpu *gpu);
#endif /* __A6XX_GPU_H__ */
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c
index 4c7f3c642f6a..d2d6b2fd3cba 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.c
@@ -1255,7 +1255,7 @@ static void a6xx_get_gmu_registers(struct msm_gpu *gpu,
return;
/* Set the fence to ALLOW mode so we can access the registers */
- gpu_write(gpu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
+ gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
_a6xx_get_gmu_registers(gpu, a6xx_state, &a6xx_gmu_reglist[2],
&a6xx_state->gmu_registers[3], false);
@@ -1596,7 +1596,8 @@ struct msm_gpu_state *a6xx_gpu_state_get(struct msm_gpu *gpu)
/* Get the generic state from the adreno core */
adreno_gpu_state_get(gpu, &a6xx_state->base);
- if (!adreno_has_gmu_wrapper(adreno_gpu)) {
+ if (!adreno_has_gmu_wrapper(adreno_gpu) &&
+ !adreno_has_rgmu(adreno_gpu)) {
a6xx_get_gmu_registers(gpu, a6xx_state);
a6xx_state->gmu_log = a6xx_snapshot_gmu_bo(a6xx_state, &a6xx_gpu->gmu.log);
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h
index 1c18499b60bb..b49d8427b59e 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu_state.h
@@ -71,8 +71,8 @@ static const struct a6xx_cluster {
u32 sel_val;
} a6xx_clusters[] = {
CLUSTER(CLUSTER_GRAS, a6xx_gras_cluster, 0, 0),
- CLUSTER(CLUSTER_PS, a6xx_ps_cluster_rac, REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD, 0x0),
- CLUSTER(CLUSTER_PS, a6xx_ps_cluster_rbp, REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD, 0x9),
+ CLUSTER(CLUSTER_PS, a6xx_ps_cluster_rac, REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD, 0x0),
+ CLUSTER(CLUSTER_PS, a6xx_ps_cluster_rbp, REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD, 0x9),
CLUSTER(CLUSTER_PS, a6xx_ps_cluster, 0, 0),
CLUSTER(CLUSTER_FE, a6xx_fe_cluster, 0, 0),
CLUSTER(CLUSTER_PC_VS, a6xx_pc_vs_cluster, 0, 0),
@@ -303,8 +303,8 @@ static const u32 a660_registers[] = {
static const struct a6xx_registers a6xx_reglist[] = {
REGS(a6xx_registers, 0, 0),
REGS(a660_registers, 0, 0),
- REGS(a6xx_rb_rac_registers, REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD, 0),
- REGS(a6xx_rb_rbp_registers, REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD, 9),
+ REGS(a6xx_rb_rac_registers, REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD, 0),
+ REGS(a6xx_rb_rbp_registers, REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD, 9),
};
static const u32 a6xx_ahb_registers[] = {
@@ -343,48 +343,48 @@ static const struct a6xx_registers a6xx_gbif_reglist =
static const u32 a6xx_gmu_gx_registers[] = {
/* GMU GX */
- 0x0000, 0x0000, 0x0010, 0x0013, 0x0016, 0x0016, 0x0018, 0x001b,
- 0x001e, 0x001e, 0x0020, 0x0023, 0x0026, 0x0026, 0x0028, 0x002b,
- 0x002e, 0x002e, 0x0030, 0x0033, 0x0036, 0x0036, 0x0038, 0x003b,
- 0x003e, 0x003e, 0x0040, 0x0043, 0x0046, 0x0046, 0x0080, 0x0084,
- 0x0100, 0x012b, 0x0140, 0x0140,
+ 0x1a800, 0x1a800, 0x1a810, 0x1a813, 0x1a816, 0x1a816, 0x1a818, 0x1a81b,
+ 0x1a81e, 0x1a81e, 0x1a820, 0x1a823, 0x1a826, 0x1a826, 0x1a828, 0x1a82b,
+ 0x1a82e, 0x1a82e, 0x1a830, 0x1a833, 0x1a836, 0x1a836, 0x1a838, 0x1a83b,
+ 0x1a83e, 0x1a83e, 0x1a840, 0x1a843, 0x1a846, 0x1a846, 0x1a880, 0x1a884,
+ 0x1a900, 0x1a92b, 0x1a940, 0x1a940,
};
static const u32 a6xx_gmu_cx_registers[] = {
/* GMU CX */
- 0x4c00, 0x4c07, 0x4c10, 0x4c12, 0x4d00, 0x4d00, 0x4d07, 0x4d0a,
- 0x5000, 0x5004, 0x5007, 0x5008, 0x500b, 0x500c, 0x500f, 0x501c,
- 0x5024, 0x502a, 0x502d, 0x5030, 0x5040, 0x5053, 0x5087, 0x5089,
- 0x50a0, 0x50a2, 0x50a4, 0x50af, 0x50c0, 0x50c3, 0x50d0, 0x50d0,
- 0x50e4, 0x50e4, 0x50e8, 0x50ec, 0x5100, 0x5103, 0x5140, 0x5140,
- 0x5142, 0x5144, 0x514c, 0x514d, 0x514f, 0x5151, 0x5154, 0x5154,
- 0x5157, 0x5158, 0x515d, 0x515d, 0x5162, 0x5162, 0x5164, 0x5165,
- 0x5180, 0x5186, 0x5190, 0x519e, 0x51c0, 0x51c0, 0x51c5, 0x51cc,
- 0x51e0, 0x51e2, 0x51f0, 0x51f0, 0x5200, 0x5201,
+ 0x1f400, 0x1f407, 0x1f410, 0x1f412, 0x1f500, 0x1f500, 0x1f507, 0x1f50a,
+ 0x1f800, 0x1f804, 0x1f807, 0x1f808, 0x1f80b, 0x1f80c, 0x1f80f, 0x1f81c,
+ 0x1f824, 0x1f82a, 0x1f82d, 0x1f830, 0x1f840, 0x1f853, 0x1f887, 0x1f889,
+ 0x1f8a0, 0x1f8a2, 0x1f8a4, 0x1f8af, 0x1f8c0, 0x1f8c3, 0x1f8d0, 0x1f8d0,
+ 0x1f8e4, 0x1f8e4, 0x1f8e8, 0x1f8ec, 0x1f900, 0x1f903, 0x1f940, 0x1f940,
+ 0x1f942, 0x1f944, 0x1f94c, 0x1f94d, 0x1f94f, 0x1f951, 0x1f954, 0x1f954,
+ 0x1f957, 0x1f958, 0x1f95d, 0x1f95d, 0x1f962, 0x1f962, 0x1f964, 0x1f965,
+ 0x1f980, 0x1f986, 0x1f990, 0x1f99e, 0x1f9c0, 0x1f9c0, 0x1f9c5, 0x1f9cc,
+ 0x1f9e0, 0x1f9e2, 0x1f9f0, 0x1f9f0, 0x1fa00, 0x1fa01,
/* GMU AO */
- 0x9300, 0x9316, 0x9400, 0x9400,
+ 0x23b00, 0x23b16, 0x23c00, 0x23c00,
};
static const u32 a6xx_gmu_gpucc_registers[] = {
/* GPU CC */
- 0x9800, 0x9812, 0x9840, 0x9852, 0x9c00, 0x9c04, 0x9c07, 0x9c0b,
- 0x9c15, 0x9c1c, 0x9c1e, 0x9c2d, 0x9c3c, 0x9c3d, 0x9c3f, 0x9c40,
- 0x9c42, 0x9c49, 0x9c58, 0x9c5a, 0x9d40, 0x9d5e, 0xa000, 0xa002,
- 0xa400, 0xa402, 0xac00, 0xac02, 0xb000, 0xb002, 0xb400, 0xb402,
- 0xb800, 0xb802,
+ 0x24000, 0x24012, 0x24040, 0x24052, 0x24400, 0x24404, 0x24407, 0x2440b,
+ 0x24415, 0x2441c, 0x2441e, 0x2442d, 0x2443c, 0x2443d, 0x2443f, 0x24440,
+ 0x24442, 0x24449, 0x24458, 0x2445a, 0x24540, 0x2455e, 0x24800, 0x24802,
+ 0x24c00, 0x24c02, 0x25400, 0x25402, 0x25800, 0x25802, 0x25c00, 0x25c02,
+ 0x26000, 0x26002,
/* GPU CC ACD */
- 0xbc00, 0xbc16, 0xbc20, 0xbc27,
+ 0x26400, 0x26416, 0x26420, 0x26427,
};
static const u32 a621_gmu_gpucc_registers[] = {
/* GPU CC */
- 0x9800, 0x980e, 0x9c00, 0x9c0e, 0xb000, 0xb004, 0xb400, 0xb404,
- 0xb800, 0xb804, 0xbc00, 0xbc05, 0xbc14, 0xbc1d, 0xbc2a, 0xbc30,
- 0xbc32, 0xbc32, 0xbc41, 0xbc55, 0xbc66, 0xbc68, 0xbc78, 0xbc7a,
- 0xbc89, 0xbc8a, 0xbc9c, 0xbc9e, 0xbca0, 0xbca3, 0xbcb3, 0xbcb5,
- 0xbcc5, 0xbcc7, 0xbcd6, 0xbcd8, 0xbce8, 0xbce9, 0xbcf9, 0xbcfc,
- 0xbd0b, 0xbd0c, 0xbd1c, 0xbd1e, 0xbd40, 0xbd70, 0xbe00, 0xbe16,
- 0xbe20, 0xbe2d,
+ 0x24000, 0x2400e, 0x24400, 0x2440e, 0x25800, 0x25804, 0x25c00, 0x25c04,
+ 0x26000, 0x26004, 0x26400, 0x26405, 0x26414, 0x2641d, 0x2642a, 0x26430,
+ 0x26432, 0x26432, 0x26441, 0x26455, 0x26466, 0x26468, 0x26478, 0x2647a,
+ 0x26489, 0x2648a, 0x2649c, 0x2649e, 0x264a0, 0x264a3, 0x264b3, 0x264b5,
+ 0x264c5, 0x264c7, 0x264d6, 0x264d8, 0x264e8, 0x264e9, 0x264f9, 0x264fc,
+ 0x2650b, 0x2650c, 0x2651c, 0x2651e, 0x26540, 0x26570, 0x26600, 0x26616,
+ 0x26620, 0x2662d,
};
static const u32 a6xx_gmu_cx_rscc_registers[] = {
@@ -575,7 +575,7 @@ struct gen7_sptp_cluster_registers {
/* statetype: SP block state type for the cluster */
enum a7xx_statetype_id statetype;
/* pipe_id: Pipe identifier */
- enum a7xx_pipe pipe_id;
+ enum adreno_pipe pipe_id;
/* context_id: Context identifier */
int context_id;
/* location_id: Location identifier */
@@ -801,10 +801,10 @@ static const char *a7xx_statetype_names[] = {
};
static const char *a7xx_pipe_names[] = {
- A7XX_NAME(A7XX_PIPE_NONE),
- A7XX_NAME(A7XX_PIPE_BR),
- A7XX_NAME(A7XX_PIPE_BV),
- A7XX_NAME(A7XX_PIPE_LPAC),
+ A7XX_NAME(PIPE_NONE),
+ A7XX_NAME(PIPE_BR),
+ A7XX_NAME(PIPE_BV),
+ A7XX_NAME(PIPE_LPAC),
};
static const char *a7xx_cluster_names[] = {
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
index 550de6ad68ef..53cfdf4e6c34 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c
@@ -23,6 +23,7 @@ static const char * const a6xx_hfi_msg_id[] = {
HFI_MSG_ID(HFI_H2F_MSG_START),
HFI_MSG_ID(HFI_H2F_FEATURE_CTRL),
HFI_MSG_ID(HFI_H2F_MSG_CORE_FW_START),
+ HFI_MSG_ID(HFI_H2F_MSG_TABLE),
HFI_MSG_ID(HFI_H2F_MSG_GX_BW_PERF_VOTE),
HFI_MSG_ID(HFI_H2F_MSG_PREPARE_SLUMBER),
};
@@ -105,10 +106,25 @@ static int a6xx_hfi_wait_for_msg_interrupt(struct a6xx_gmu *gmu, u32 id, u32 seq
{
int ret;
u32 val;
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+
+ do {
+ /* Wait for a response */
+ ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val,
+ val & A6XX_GMU_GMU2HOST_INTR_INFO_MSGQ, 100, 1000000);
+
+ if (!ret)
+ break;
- /* Wait for a response */
- ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_GMU2HOST_INTR_INFO, val,
- val & A6XX_GMU_GMU2HOST_INTR_INFO_MSGQ, 100, 1000000);
+ if (completion_done(&a6xx_gpu->base.fault_coredump_done))
+ break;
+
+ /* We may timeout because the GMU is temporarily wedged from
+ * pending faults from the GPU and we are taking a devcoredump.
+ * Wait until the MMU is resumed and try again.
+ */
+ wait_for_completion(&a6xx_gpu->base.fault_coredump_done);
+ } while (true);
if (ret) {
DRM_DEV_ERROR(gmu->dev,
@@ -255,11 +271,63 @@ static int a6xx_hfi_send_perf_table_v1(struct a6xx_gmu *gmu)
NULL, 0);
}
+static int a8xx_hfi_send_perf_table(struct a6xx_gmu *gmu)
+{
+ unsigned int num_gx_votes = 3, num_cx_votes = 2;
+ struct a6xx_hfi_table_entry *entry;
+ struct a6xx_hfi_table *tbl;
+ int ret, i;
+ u32 size;
+
+ size = sizeof(*tbl) + (2 * sizeof(tbl->entry[0])) +
+ (gmu->nr_gpu_freqs * num_gx_votes * sizeof(gmu->gx_arc_votes[0])) +
+ (gmu->nr_gmu_freqs * num_cx_votes * sizeof(gmu->cx_arc_votes[0]));
+ tbl = kzalloc(size, GFP_KERNEL);
+ tbl->type = HFI_TABLE_GPU_PERF;
+
+ /* First fill GX votes */
+ entry = &tbl->entry[0];
+ entry->count = gmu->nr_gpu_freqs;
+ entry->stride = num_gx_votes;
+
+ for (i = 0; i < gmu->nr_gpu_freqs; i++) {
+ unsigned int base = i * entry->stride;
+
+ entry->data[base+0] = gmu->gx_arc_votes[i];
+ entry->data[base+1] = gmu->dep_arc_votes[i];
+ entry->data[base+2] = gmu->gpu_freqs[i] / 1000;
+ }
+
+ /* Then fill CX votes */
+ entry = (struct a6xx_hfi_table_entry *)
+ &tbl->entry[0].data[gmu->nr_gpu_freqs * num_gx_votes];
+
+ entry->count = gmu->nr_gmu_freqs;
+ entry->stride = num_cx_votes;
+
+ for (i = 0; i < gmu->nr_gmu_freqs; i++) {
+ unsigned int base = i * entry->stride;
+
+ entry->data[base] = gmu->cx_arc_votes[i];
+ entry->data[base+1] = gmu->gmu_freqs[i] / 1000;
+ }
+
+ ret = a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_TABLE, tbl, size, NULL, 0);
+
+ kfree(tbl);
+ return ret;
+}
+
static int a6xx_hfi_send_perf_table(struct a6xx_gmu *gmu)
{
+ struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct a6xx_hfi_msg_perf_table msg = { 0 };
int i;
+ if (adreno_is_a8xx(adreno_gpu))
+ return a8xx_hfi_send_perf_table(gmu);
+
msg.num_gpu_levels = gmu->nr_gpu_freqs;
msg.num_gmu_levels = gmu->nr_gmu_freqs;
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h
index 653ef720e2da..6f9f74a0bc85 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h
@@ -185,6 +185,23 @@ struct a6xx_hfi_msg_core_fw_start {
u32 handle;
};
+#define HFI_H2F_MSG_TABLE 15
+
+struct a6xx_hfi_table_entry {
+ u32 count;
+ u32 stride;
+ u32 data[];
+};
+
+struct a6xx_hfi_table {
+ u32 header;
+ u32 version;
+ u32 type;
+#define HFI_TABLE_BW_VOTE 0
+#define HFI_TABLE_GPU_PERF 1
+ struct a6xx_hfi_table_entry entry[];
+};
+
#define HFI_H2F_MSG_GX_BW_PERF_VOTE 30
struct a6xx_hfi_gx_bw_perf_vote_cmd {
diff --git a/drivers/gpu/drm/msm/adreno/a8xx_gpu.c b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
new file mode 100644
index 000000000000..30de078e9dfd
--- /dev/null
+++ b/drivers/gpu/drm/msm/adreno/a8xx_gpu.c
@@ -0,0 +1,1201 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
+
+
+#include "msm_gem.h"
+#include "msm_mmu.h"
+#include "msm_gpu_trace.h"
+#include "a6xx_gpu.h"
+#include "a6xx_gmu.xml.h"
+
+#include <linux/bitfield.h>
+#include <linux/devfreq.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/pm_domain.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+
+#define GPU_PAS_ID 13
+
+static void a8xx_aperture_slice_set(struct msm_gpu *gpu, enum adreno_pipe pipe, u32 slice)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ u32 val;
+
+ val = A8XX_CP_APERTURE_CNTL_HOST_PIPEID(pipe) | A8XX_CP_APERTURE_CNTL_HOST_SLICEID(slice);
+
+ if (a6xx_gpu->cached_aperture == val)
+ return;
+
+ gpu_write(gpu, REG_A8XX_CP_APERTURE_CNTL_HOST, val);
+
+ a6xx_gpu->cached_aperture = val;
+}
+
+static void a8xx_aperture_acquire(struct msm_gpu *gpu, enum adreno_pipe pipe, unsigned long *flags)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+
+ spin_lock_irqsave(&a6xx_gpu->aperture_lock, *flags);
+
+ a8xx_aperture_slice_set(gpu, pipe, 0);
+}
+
+static void a8xx_aperture_release(struct msm_gpu *gpu, unsigned long flags)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+
+ spin_unlock_irqrestore(&a6xx_gpu->aperture_lock, flags);
+}
+
+static void a8xx_aperture_clear(struct msm_gpu *gpu)
+{
+ unsigned long flags;
+
+ a8xx_aperture_acquire(gpu, PIPE_NONE, &flags);
+ a8xx_aperture_release(gpu, flags);
+}
+
+static void a8xx_write_pipe(struct msm_gpu *gpu, enum adreno_pipe pipe, u32 offset, u32 data)
+{
+ unsigned long flags;
+
+ a8xx_aperture_acquire(gpu, pipe, &flags);
+ gpu_write(gpu, offset, data);
+ a8xx_aperture_release(gpu, flags);
+}
+
+static u32 a8xx_read_pipe_slice(struct msm_gpu *gpu, enum adreno_pipe pipe, u32 slice, u32 offset)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&a6xx_gpu->aperture_lock, flags);
+ a8xx_aperture_slice_set(gpu, pipe, slice);
+ val = gpu_read(gpu, offset);
+ spin_unlock_irqrestore(&a6xx_gpu->aperture_lock, flags);
+
+ return val;
+}
+
+void a8xx_gpu_get_slice_info(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ const struct a6xx_info *info = adreno_gpu->info->a6xx;
+ u32 slice_mask;
+
+ if (adreno_gpu->info->family < ADRENO_8XX_GEN1)
+ return;
+
+ if (a6xx_gpu->slice_mask)
+ return;
+
+ slice_mask = GENMASK(info->max_slices - 1, 0);
+
+ /* GEN1 doesn't support partial slice configurations */
+ if (adreno_gpu->info->family == ADRENO_8XX_GEN1) {
+ a6xx_gpu->slice_mask = slice_mask;
+ return;
+ }
+
+ slice_mask &= a6xx_llc_read(a6xx_gpu,
+ REG_A8XX_CX_MISC_SLICE_ENABLE_FINAL);
+
+ a6xx_gpu->slice_mask = slice_mask;
+
+ /* Chip ID depends on the number of slices available. So update it */
+ adreno_gpu->chip_id |= FIELD_PREP(GENMASK(7, 4), hweight32(slice_mask));
+}
+
+static u32 a8xx_get_first_slice(struct a6xx_gpu *a6xx_gpu)
+{
+ return ffs(a6xx_gpu->slice_mask) - 1;
+}
+
+static inline bool _a8xx_check_idle(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+
+ /* Check that the GMU is idle */
+ if (!a6xx_gmu_isidle(&a6xx_gpu->gmu))
+ return false;
+
+ /* Check that the CX master is idle */
+ if (gpu_read(gpu, REG_A8XX_RBBM_STATUS) &
+ ~A8XX_RBBM_STATUS_CP_AHB_BUSY_CX_MASTER)
+ return false;
+
+ return !(gpu_read(gpu, REG_A8XX_RBBM_INT_0_STATUS) &
+ A6XX_RBBM_INT_0_MASK_RBBM_HANG_DETECT);
+}
+
+static bool a8xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
+{
+ /* wait for CP to drain ringbuffer: */
+ if (!adreno_idle(gpu, ring))
+ return false;
+
+ if (spin_until(_a8xx_check_idle(gpu))) {
+ DRM_ERROR(
+ "%s: %ps: timeout waiting for GPU to idle: status %8.8X irq %8.8X rptr/wptr %d/%d\n",
+ gpu->name, __builtin_return_address(0),
+ gpu_read(gpu, REG_A8XX_RBBM_STATUS),
+ gpu_read(gpu, REG_A8XX_RBBM_INT_0_STATUS),
+ gpu_read(gpu, REG_A6XX_CP_RB_RPTR),
+ gpu_read(gpu, REG_A6XX_CP_RB_WPTR));
+ return false;
+ }
+
+ return true;
+}
+
+void a8xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ uint32_t wptr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ring->preempt_lock, flags);
+
+ /* Copy the shadow to the actual register */
+ ring->cur = ring->next;
+
+ /* Make sure to wrap wptr if we need to */
+ wptr = get_wptr(ring);
+
+ /* Update HW if this is the current ring and we are not in preempt*/
+ if (!a6xx_in_preempt(a6xx_gpu)) {
+ if (a6xx_gpu->cur_ring == ring)
+ gpu_write(gpu, REG_A6XX_CP_RB_WPTR, wptr);
+ else
+ ring->restore_wptr = true;
+ } else {
+ ring->restore_wptr = true;
+ }
+
+ spin_unlock_irqrestore(&ring->preempt_lock, flags);
+}
+
+static void a8xx_set_hwcg(struct msm_gpu *gpu, bool state)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
+ u32 val;
+
+ if (adreno_is_x285(adreno_gpu) && state)
+ gpu_write(gpu, REG_A8XX_RBBM_CGC_0_PC, 0x00000702);
+
+ gmu_write(gmu, REG_A6XX_GPU_GMU_AO_GMU_CGC_MODE_CNTL,
+ state ? adreno_gpu->info->a6xx->gmu_cgc_mode : 0);
+ gmu_write(gmu, REG_A6XX_GPU_GMU_AO_GMU_CGC_DELAY_CNTL,
+ state ? 0x110111 : 0);
+ gmu_write(gmu, REG_A6XX_GPU_GMU_AO_GMU_CGC_HYST_CNTL,
+ state ? 0x55555 : 0);
+
+ gpu_write(gpu, REG_A8XX_RBBM_CLOCK_CNTL_GLOBAL, 1);
+ gpu_write(gpu, REG_A8XX_RBBM_CGC_GLOBAL_LOAD_CMD, !!state);
+
+ if (state) {
+ gpu_write(gpu, REG_A8XX_RBBM_CGC_P2S_TRIG_CMD, 1);
+
+ if (gpu_poll_timeout(gpu, REG_A8XX_RBBM_CGC_P2S_STATUS, val,
+ val & A8XX_RBBM_CGC_P2S_STATUS_TXDONE, 1, 10)) {
+ dev_err(&gpu->pdev->dev, "RBBM_CGC_P2S_STATUS TXDONE Poll failed\n");
+ return;
+ }
+
+ gpu_write(gpu, REG_A8XX_RBBM_CLOCK_CNTL_GLOBAL, 0);
+ } else {
+ /*
+ * GMU enables clk gating in GBIF during boot up. So,
+ * override that here when hwcg feature is disabled
+ */
+ gpu_rmw(gpu, REG_A8XX_GBIF_CX_CONFIG, BIT(0), 0);
+ }
+}
+
+static void a8xx_set_cp_protect(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ const struct adreno_protect *protect = adreno_gpu->info->a6xx->protect;
+ u32 cntl, final_cfg;
+ unsigned int i;
+
+ cntl = A8XX_CP_PROTECT_CNTL_PIPE_ACCESS_PROT_EN |
+ A8XX_CP_PROTECT_CNTL_PIPE_ACCESS_FAULT_ON_VIOL_EN |
+ A8XX_CP_PROTECT_CNTL_PIPE_LAST_SPAN_INF_RANGE |
+ A8XX_CP_PROTECT_CNTL_PIPE_HALT_SQE_RANGE__MASK;
+ /*
+ * Enable access protection to privileged registers, fault on an access
+ * protect violation and select the last span to protect from the start
+ * address all the way to the end of the register address space
+ */
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_CP_PROTECT_CNTL_PIPE, cntl);
+ a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_CP_PROTECT_CNTL_PIPE, cntl);
+
+ a8xx_aperture_clear(gpu);
+
+ for (i = 0; i < protect->count; i++) {
+ /* Intentionally skip writing to some registers */
+ if (protect->regs[i]) {
+ gpu_write(gpu, REG_A8XX_CP_PROTECT_GLOBAL(i), protect->regs[i]);
+ final_cfg = protect->regs[i];
+ }
+ }
+
+ /*
+ * Last span feature is only supported on PIPE specific register.
+ * So update those here
+ */
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_CP_PROTECT_PIPE(protect->count_max), final_cfg);
+ a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_CP_PROTECT_PIPE(protect->count_max), final_cfg);
+
+ a8xx_aperture_clear(gpu);
+}
+
+static void a8xx_set_ubwc_config(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ const struct qcom_ubwc_cfg_data *cfg = adreno_gpu->ubwc_config;
+ u32 level2_swizzling_dis = !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL2);
+ u32 level3_swizzling_dis = !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL3);
+ bool rgba8888_lossless = false, fp16compoptdis = false;
+ bool yuvnotcomptofc = false, min_acc_len_64b = false;
+ bool rgb565_predicator = false, amsbc = false;
+ bool ubwc_mode = qcom_ubwc_get_ubwc_mode(cfg);
+ u32 ubwc_version = cfg->ubwc_enc_version;
+ u32 hbb, hbb_hi, hbb_lo, mode = 1;
+ u8 uavflagprd_inv = 2;
+
+ switch (ubwc_version) {
+ case UBWC_5_0:
+ amsbc = true;
+ rgb565_predicator = true;
+ mode = 4;
+ break;
+ case UBWC_4_0:
+ amsbc = true;
+ rgb565_predicator = true;
+ fp16compoptdis = true;
+ rgba8888_lossless = true;
+ mode = 2;
+ break;
+ case UBWC_3_0:
+ amsbc = true;
+ mode = 1;
+ break;
+ default:
+ dev_err(&gpu->pdev->dev, "Unknown UBWC version: 0x%x\n", ubwc_version);
+ break;
+ }
+
+ /*
+ * We subtract 13 from the highest bank bit (13 is the minimum value
+ * allowed by hw) and write the lowest two bits of the remaining value
+ * as hbb_lo and the one above it as hbb_hi to the hardware.
+ */
+ WARN_ON(cfg->highest_bank_bit < 13);
+ hbb = cfg->highest_bank_bit - 13;
+ hbb_hi = hbb >> 2;
+ hbb_lo = hbb & 3;
+ a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_GRAS_NC_MODE_CNTL, hbb << 5);
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_GRAS_NC_MODE_CNTL, hbb << 5);
+
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_RB_CCU_NC_MODE_CNTL,
+ yuvnotcomptofc << 6 |
+ hbb_hi << 3 |
+ hbb_lo << 1);
+
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_RB_CMP_NC_MODE_CNTL,
+ mode << 15 |
+ yuvnotcomptofc << 6 |
+ rgba8888_lossless << 4 |
+ fp16compoptdis << 3 |
+ rgb565_predicator << 2 |
+ amsbc << 1 |
+ min_acc_len_64b);
+
+ a8xx_aperture_clear(gpu);
+
+ gpu_write(gpu, REG_A6XX_SP_NC_MODE_CNTL,
+ level3_swizzling_dis << 13 |
+ level2_swizzling_dis << 12 |
+ hbb_hi << 10 |
+ uavflagprd_inv << 4 |
+ min_acc_len_64b << 3 |
+ hbb_lo << 1 | ubwc_mode);
+
+ gpu_write(gpu, REG_A6XX_TPL1_NC_MODE_CNTL,
+ level3_swizzling_dis << 7 |
+ level2_swizzling_dis << 6 |
+ hbb_hi << 4 |
+ min_acc_len_64b << 3 |
+ hbb_lo << 1 | ubwc_mode);
+}
+
+static void a8xx_nonctxt_config(struct msm_gpu *gpu, u32 *gmem_protect)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ const struct a6xx_info *info = adreno_gpu->info->a6xx;
+ const struct adreno_reglist_pipe *regs = info->nonctxt_reglist;
+ unsigned int pipe_id, i;
+ unsigned long flags;
+
+ for (pipe_id = PIPE_NONE; pipe_id <= PIPE_DDE_BV; pipe_id++) {
+ /* We don't have support for LPAC yet */
+ if (pipe_id == PIPE_LPAC)
+ continue;
+
+ a8xx_aperture_acquire(gpu, pipe_id, &flags);
+
+ for (i = 0; regs[i].offset; i++) {
+ if (!(BIT(pipe_id) & regs[i].pipe))
+ continue;
+
+ if (regs[i].offset == REG_A8XX_RB_GC_GMEM_PROTECT)
+ *gmem_protect = regs[i].value;
+
+ gpu_write(gpu, regs[i].offset, regs[i].value);
+ }
+
+ a8xx_aperture_release(gpu, flags);
+ }
+
+ a8xx_aperture_clear(gpu);
+}
+
+static int a8xx_cp_init(struct msm_gpu *gpu)
+{
+ struct msm_ringbuffer *ring = gpu->rb[0];
+ u32 mask;
+
+ /* Disable concurrent binning before sending CP init */
+ OUT_PKT7(ring, CP_THREAD_CONTROL, 1);
+ OUT_RING(ring, BIT(27));
+
+ OUT_PKT7(ring, CP_ME_INIT, 4);
+
+ /* Use multiple HW contexts */
+ mask = BIT(0);
+
+ /* Enable error detection */
+ mask |= BIT(1);
+
+ /* Set default reset state */
+ mask |= BIT(3);
+
+ /* Disable save/restore of performance counters across preemption */
+ mask |= BIT(6);
+
+ OUT_RING(ring, mask);
+
+ /* Enable multiple hardware contexts */
+ OUT_RING(ring, 0x00000003);
+
+ /* Enable error detection */
+ OUT_RING(ring, 0x20000000);
+
+ /* Operation mode mask */
+ OUT_RING(ring, 0x00000002);
+
+ a6xx_flush(gpu, ring);
+ return a8xx_idle(gpu, ring) ? 0 : -EINVAL;
+}
+
+#define A8XX_INT_MASK \
+ (A6XX_RBBM_INT_0_MASK_CP_AHB_ERROR | \
+ A6XX_RBBM_INT_0_MASK_RBBM_ATB_ASYNCFIFO_OVERFLOW | \
+ A6XX_RBBM_INT_0_MASK_RBBM_GPC_ERROR | \
+ A6XX_RBBM_INT_0_MASK_CP_SW | \
+ A6XX_RBBM_INT_0_MASK_CP_HW_ERROR | \
+ A6XX_RBBM_INT_0_MASK_PM4CPINTERRUPT | \
+ A6XX_RBBM_INT_0_MASK_CP_RB_DONE_TS | \
+ A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS | \
+ A6XX_RBBM_INT_0_MASK_RBBM_ATB_BUS_OVERFLOW | \
+ A6XX_RBBM_INT_0_MASK_RBBM_HANG_DETECT | \
+ A6XX_RBBM_INT_0_MASK_UCHE_OOB_ACCESS | \
+ A6XX_RBBM_INT_0_MASK_UCHE_TRAP_INTR | \
+ A6XX_RBBM_INT_0_MASK_TSBWRITEERROR | \
+ A6XX_RBBM_INT_0_MASK_SWFUSEVIOLATION)
+
+#define A8XX_APRIV_MASK \
+ (A8XX_CP_APRIV_CNTL_PIPE_ICACHE | \
+ A8XX_CP_APRIV_CNTL_PIPE_RBFETCH | \
+ A8XX_CP_APRIV_CNTL_PIPE_RBPRIVLEVEL | \
+ A8XX_CP_APRIV_CNTL_PIPE_RBRPWB)
+
+#define A8XX_BR_APRIV_MASK \
+ (A8XX_APRIV_MASK | \
+ A8XX_CP_APRIV_CNTL_PIPE_CDREAD | \
+ A8XX_CP_APRIV_CNTL_PIPE_CDWRITE)
+
+#define A8XX_CP_GLOBAL_INT_MASK \
+ (A8XX_CP_GLOBAL_INT_MASK_HWFAULTBR | \
+ A8XX_CP_GLOBAL_INT_MASK_HWFAULTBV | \
+ A8XX_CP_GLOBAL_INT_MASK_HWFAULTLPAC | \
+ A8XX_CP_GLOBAL_INT_MASK_HWFAULTAQE0 | \
+ A8XX_CP_GLOBAL_INT_MASK_HWFAULTAQE1 | \
+ A8XX_CP_GLOBAL_INT_MASK_HWFAULTDDEBR | \
+ A8XX_CP_GLOBAL_INT_MASK_HWFAULTDDEBV | \
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTBR | \
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTBV | \
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTLPAC | \
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTAQE0 | \
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTAQE1 | \
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTDDEBR | \
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTDDEBV)
+
+#define A8XX_CP_INTERRUPT_STATUS_MASK_PIPE \
+ (A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_CSFRBWRAP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_CSFIB1WRAP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_CSFIB2WRAP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_CSFIB3WRAP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_CSFSDSWRAP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_CSFMRBWRAP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_CSFVSDWRAP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_OPCODEERROR | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_VSDPARITYERROR | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_REGISTERPROTECTIONERROR | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_ILLEGALINSTRUCTION | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_SMMUFAULT | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_VBIFRESPCLIENT| \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_VBIFRESPTYPE | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_VBIFRESPREAD | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_VBIFRESP | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_RTWROVF | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_LRZRTWROVF | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_LRZRTREFCNTOVF | \
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE_LRZRTCLRRESMISS)
+
+#define A8XX_CP_HW_FAULT_STATUS_MASK_PIPE \
+ (A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_CSFRBFAULT | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_CSFIB1FAULT | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_CSFIB2FAULT | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_CSFIB3FAULT | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_CSFSDSFAULT | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_CSFMRBFAULT | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_CSFVSDFAULT | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_SQEREADBURSTOVF | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_EVENTENGINEOVF | \
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE_UCODEERROR)
+
+static int hw_init(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
+ unsigned int pipe_id, i;
+ u32 gmem_protect = 0;
+ u64 gmem_range_min;
+ int ret;
+
+ ret = a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET);
+ if (ret)
+ return ret;
+
+ /* Clear the cached value to force aperture configuration next time */
+ a6xx_gpu->cached_aperture = UINT_MAX;
+ a8xx_aperture_clear(gpu);
+
+ /* Clear GBIF halt in case GX domain was not collapsed */
+ gpu_write(gpu, REG_A6XX_GBIF_HALT, 0);
+ gpu_read(gpu, REG_A6XX_GBIF_HALT);
+
+ gpu_write(gpu, REG_A8XX_RBBM_GBIF_HALT, 0);
+ gpu_read(gpu, REG_A8XX_RBBM_GBIF_HALT);
+
+ gpu_write(gpu, REG_A6XX_RBBM_SECVID_TSB_CNTL, 0);
+
+ /*
+ * Disable the trusted memory range - we don't actually supported secure
+ * memory rendering at this point in time and we don't want to block off
+ * part of the virtual memory space.
+ */
+ gpu_write64(gpu, REG_A6XX_RBBM_SECVID_TSB_TRUSTED_BASE, 0x00000000);
+ gpu_write(gpu, REG_A6XX_RBBM_SECVID_TSB_TRUSTED_SIZE, 0x00000000);
+
+ /* Make all blocks contribute to the GPU BUSY perf counter */
+ gpu_write(gpu, REG_A8XX_RBBM_PERFCTR_GPU_BUSY_MASKED, 0xffffffff);
+
+ /* Setup GMEM Range in UCHE */
+ gmem_range_min = SZ_64M;
+ /* Set the GMEM VA range [0x100000:0x100000 + gpu->gmem - 1] */
+ gpu_write64(gpu, REG_A8XX_UCHE_CCHE_GC_GMEM_RANGE_MIN, gmem_range_min);
+ gpu_write64(gpu, REG_A8XX_SP_HLSQ_GC_GMEM_RANGE_MIN, gmem_range_min);
+
+ /* Setup UCHE Trap region */
+ gpu_write64(gpu, REG_A8XX_UCHE_TRAP_BASE, adreno_gpu->uche_trap_base);
+ gpu_write64(gpu, REG_A8XX_UCHE_WRITE_THRU_BASE, adreno_gpu->uche_trap_base);
+ gpu_write64(gpu, REG_A8XX_UCHE_CCHE_TRAP_BASE, adreno_gpu->uche_trap_base);
+ gpu_write64(gpu, REG_A8XX_UCHE_CCHE_WRITE_THRU_BASE, adreno_gpu->uche_trap_base);
+
+ /* Turn on performance counters */
+ gpu_write(gpu, REG_A8XX_RBBM_PERFCTR_CNTL, 0x1);
+ gpu_write(gpu, REG_A8XX_RBBM_SLICE_PERFCTR_CNTL, 0x1);
+
+ /* Turn on the IFPC counter (countable 4 on XOCLK1) */
+ gmu_write(&a6xx_gpu->gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_SELECT_XOCLK_1,
+ FIELD_PREP(GENMASK(7, 0), 0x4));
+
+ /* Select CP0 to always count cycles */
+ gpu_write(gpu, REG_A8XX_CP_PERFCTR_CP_SEL(0), 1);
+
+ a8xx_set_ubwc_config(gpu);
+
+ /* Set weights for bicubic filtering */
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(0), 0);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(1), 0x3fe05ff4);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(2), 0x3fa0ebee);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(3), 0x3f5193ed);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(4), 0x3f0243f0);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(5), 0x00000000);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(6), 0x3fd093e8);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(7), 0x3f4133dc);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(8), 0x3ea1dfdb);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(9), 0x3e0283e0);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(10), 0x0000ac2b);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(11), 0x0000f01d);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(12), 0x00114412);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(13), 0x0021980a);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(14), 0x0051ec05);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(15), 0x0000380e);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(16), 0x3ff09001);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(17), 0x3fc10bfa);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(18), 0x3f9193f7);
+ gpu_write(gpu, REG_A8XX_TPL1_BICUBIC_WEIGHTS_TABLE(19), 0x3f7227f7);
+
+ gpu_write(gpu, REG_A8XX_UCHE_CLIENT_PF, BIT(7) | 0x1);
+
+ a8xx_nonctxt_config(gpu, &gmem_protect);
+
+ /* Enable fault detection */
+ gpu_write(gpu, REG_A8XX_RBBM_INTERFACE_HANG_INT_CNTL, BIT(30) | 0xcfffff);
+ gpu_write(gpu, REG_A8XX_RBBM_SLICE_INTERFACE_HANG_INT_CNTL, BIT(30));
+
+ /* Set up the CX GMU counter 0 to count busy ticks */
+ gmu_write(gmu, REG_A6XX_GPU_GMU_AO_GPU_CX_BUSY_MASK, 0xff000000);
+
+ /* Enable the power counter */
+ gmu_rmw(gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_SELECT_XOCLK_0, 0xff, BIT(5));
+ gmu_write(gmu, REG_A8XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 1);
+
+ /* Protect registers from the CP */
+ a8xx_set_cp_protect(gpu);
+
+ /* Enable the GMEM save/restore feature for preemption */
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE, 1);
+
+ for (pipe_id = PIPE_BR; pipe_id <= PIPE_DDE_BV; pipe_id++) {
+ u32 apriv_mask = A8XX_APRIV_MASK;
+ unsigned long flags;
+
+ if (pipe_id == PIPE_LPAC)
+ continue;
+
+ if (pipe_id == PIPE_BR)
+ apriv_mask = A8XX_BR_APRIV_MASK;
+
+ a8xx_aperture_acquire(gpu, pipe_id, &flags);
+ gpu_write(gpu, REG_A8XX_CP_APRIV_CNTL_PIPE, apriv_mask);
+ gpu_write(gpu, REG_A8XX_CP_INTERRUPT_STATUS_MASK_PIPE,
+ A8XX_CP_INTERRUPT_STATUS_MASK_PIPE);
+ gpu_write(gpu, REG_A8XX_CP_HW_FAULT_STATUS_MASK_PIPE,
+ A8XX_CP_HW_FAULT_STATUS_MASK_PIPE);
+ a8xx_aperture_release(gpu, flags);
+ }
+
+ a8xx_aperture_clear(gpu);
+
+ /* Enable interrupts */
+ gpu_write(gpu, REG_A8XX_CP_INTERRUPT_STATUS_MASK_GLOBAL, A8XX_CP_GLOBAL_INT_MASK);
+ gpu_write(gpu, REG_A8XX_RBBM_INT_0_MASK, A8XX_INT_MASK);
+
+ ret = adreno_hw_init(gpu);
+ if (ret)
+ goto out;
+
+ gpu_write64(gpu, REG_A8XX_CP_SQE_INSTR_BASE, a6xx_gpu->sqe_iova);
+ if (a6xx_gpu->aqe_iova)
+ gpu_write64(gpu, REG_A8XX_CP_AQE_INSTR_BASE_0, a6xx_gpu->aqe_iova);
+
+ /* Set the ringbuffer address */
+ gpu_write64(gpu, REG_A6XX_CP_RB_BASE, gpu->rb[0]->iova);
+ gpu_write(gpu, REG_A6XX_CP_RB_CNTL, MSM_GPU_RB_CNTL_DEFAULT);
+
+ /* Configure the RPTR shadow if needed: */
+ gpu_write64(gpu, REG_A6XX_CP_RB_RPTR_ADDR, shadowptr(a6xx_gpu, gpu->rb[0]));
+ gpu_write64(gpu, REG_A8XX_CP_RB_RPTR_ADDR_BV, rbmemptr(gpu->rb[0], bv_rptr));
+
+ for (i = 0; i < gpu->nr_rings; i++)
+ a6xx_gpu->shadow[i] = 0;
+
+ /* Always come up on rb 0 */
+ a6xx_gpu->cur_ring = gpu->rb[0];
+
+ for (i = 0; i < gpu->nr_rings; i++)
+ gpu->rb[i]->cur_ctx_seqno = 0;
+
+ /* Enable the SQE_to start the CP engine */
+ gpu_write(gpu, REG_A8XX_CP_SQE_CNTL, 1);
+
+ ret = a8xx_cp_init(gpu);
+ if (ret)
+ goto out;
+
+ /*
+ * Try to load a zap shader into the secure world. If successful
+ * we can use the CP to switch out of secure mode. If not then we
+ * have no resource but to try to switch ourselves out manually. If we
+ * guessed wrong then access to the RBBM_SECVID_TRUST_CNTL register will
+ * be blocked and a permissions violation will soon follow.
+ */
+ ret = a6xx_zap_shader_init(gpu);
+ if (!ret) {
+ OUT_PKT7(gpu->rb[0], CP_SET_SECURE_MODE, 1);
+ OUT_RING(gpu->rb[0], 0x00000000);
+
+ a6xx_flush(gpu, gpu->rb[0]);
+ if (!a8xx_idle(gpu, gpu->rb[0]))
+ return -EINVAL;
+ } else if (ret == -ENODEV) {
+ /*
+ * This device does not use zap shader (but print a warning
+ * just in case someone got their dt wrong.. hopefully they
+ * have a debug UART to realize the error of their ways...
+ * if you mess this up you are about to crash horribly)
+ */
+ dev_warn_once(gpu->dev->dev,
+ "Zap shader not enabled - using SECVID_TRUST_CNTL instead\n");
+ gpu_write(gpu, REG_A6XX_RBBM_SECVID_TRUST_CNTL, 0x0);
+ ret = 0;
+ } else {
+ return ret;
+ }
+
+ /*
+ * GMEM_PROTECT register should be programmed after GPU is transitioned to
+ * non-secure mode
+ */
+ a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_RB_GC_GMEM_PROTECT, gmem_protect);
+ WARN_ON(!gmem_protect);
+ a8xx_aperture_clear(gpu);
+
+ /* Enable hardware clockgating */
+ a8xx_set_hwcg(gpu, true);
+out:
+ /*
+ * Tell the GMU that we are done touching the GPU and it can start power
+ * management
+ */
+ a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_GPU_SET);
+
+ return ret;
+}
+
+int a8xx_hw_init(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ int ret;
+
+ mutex_lock(&a6xx_gpu->gmu.lock);
+ ret = hw_init(gpu);
+ mutex_unlock(&a6xx_gpu->gmu.lock);
+
+ return ret;
+}
+
+static void a8xx_dump(struct msm_gpu *gpu)
+{
+ DRM_DEV_INFO(&gpu->pdev->dev, "status: %08x\n", gpu_read(gpu, REG_A8XX_RBBM_STATUS));
+ adreno_dump(gpu);
+}
+
+void a8xx_recover(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
+ int active_submits;
+
+ adreno_dump_info(gpu);
+
+ if (hang_debug)
+ a8xx_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_A8XX_CP_SQE_CNTL, 3);
+
+ pm_runtime_dont_use_autosuspend(&gpu->pdev->dev);
+
+ /* active_submit won't change until we make a submission */
+ mutex_lock(&gpu->active_lock);
+ active_submits = gpu->active_submits;
+
+ /*
+ * Temporarily clear active_submits count to silence a WARN() in the
+ * runtime suspend cb
+ */
+ gpu->active_submits = 0;
+
+ reinit_completion(&gmu->pd_gate);
+ dev_pm_genpd_add_notifier(gmu->cxpd, &gmu->pd_nb);
+ dev_pm_genpd_synced_poweroff(gmu->cxpd);
+
+ /* Drop the rpm refcount from active submits */
+ if (active_submits)
+ pm_runtime_put(&gpu->pdev->dev);
+
+ /* And the final one from recover worker */
+ pm_runtime_put_sync(&gpu->pdev->dev);
+
+ if (!wait_for_completion_timeout(&gmu->pd_gate, msecs_to_jiffies(1000)))
+ DRM_DEV_ERROR(&gpu->pdev->dev, "cx gdsc didn't collapse\n");
+
+ dev_pm_genpd_remove_notifier(gmu->cxpd);
+
+ pm_runtime_use_autosuspend(&gpu->pdev->dev);
+
+ if (active_submits)
+ pm_runtime_get(&gpu->pdev->dev);
+
+ pm_runtime_get_sync(&gpu->pdev->dev);
+
+ gpu->active_submits = active_submits;
+ mutex_unlock(&gpu->active_lock);
+
+ msm_gpu_hw_init(gpu);
+ a6xx_gpu->hung = false;
+}
+
+static const char *a8xx_uche_fault_block(struct msm_gpu *gpu, u32 mid)
+{
+ static const char * const uche_clients[] = {
+ "BR_VFD", "BR_SP", "BR_VSC", "BR_VPC", "BR_HLSQ", "BR_PC", "BR_LRZ", "BR_TP",
+ "BV_VFD", "BV_SP", "BV_VSC", "BV_VPC", "BV_HLSQ", "BV_PC", "BV_LRZ", "BV_TP",
+ "STCHE",
+ };
+ static const char * const uche_clients_lpac[] = {
+ "-", "SP_LPAC", "-", "-", "HLSQ_LPAC", "-", "-", "TP_LPAC",
+ };
+ u32 val;
+
+ /*
+ * The source of the data depends on the mid ID read from FSYNR1.
+ * and the client ID read from the UCHE block
+ */
+ val = gpu_read(gpu, REG_A8XX_UCHE_CLIENT_PF);
+
+ val &= GENMASK(6, 0);
+
+ /* mid=3 refers to BR or BV */
+ if (mid == 3) {
+ if (val < ARRAY_SIZE(uche_clients))
+ return uche_clients[val];
+ else
+ return "UCHE";
+ }
+
+ /* mid=8 refers to LPAC */
+ if (mid == 8) {
+ if (val < ARRAY_SIZE(uche_clients_lpac))
+ return uche_clients_lpac[val];
+ else
+ return "UCHE_LPAC";
+ }
+
+ return "Unknown";
+}
+
+static const char *a8xx_fault_block(struct msm_gpu *gpu, u32 id)
+{
+ switch (id) {
+ case 0x0:
+ return "CP";
+ case 0x1:
+ return "UCHE: Unknown";
+ case 0x2:
+ return "UCHE_LPAC: Unknown";
+ case 0x3:
+ case 0x8:
+ return a8xx_uche_fault_block(gpu, id);
+ case 0x4:
+ return "CCU";
+ case 0x5:
+ return "Flag cache";
+ case 0x6:
+ return "PREFETCH";
+ case 0x7:
+ return "GMU";
+ case 0x9:
+ return "UCHE_HPAC";
+ }
+
+ return "Unknown";
+}
+
+int a8xx_fault_handler(void *arg, unsigned long iova, int flags, void *data)
+{
+ struct msm_gpu *gpu = arg;
+ struct adreno_smmu_fault_info *info = data;
+ const char *block = "unknown";
+
+ u32 scratch[] = {
+ gpu_read(gpu, REG_A8XX_CP_SCRATCH_GLOBAL(0)),
+ gpu_read(gpu, REG_A8XX_CP_SCRATCH_GLOBAL(1)),
+ gpu_read(gpu, REG_A8XX_CP_SCRATCH_GLOBAL(2)),
+ gpu_read(gpu, REG_A8XX_CP_SCRATCH_GLOBAL(3)),
+ };
+
+ if (info)
+ block = a8xx_fault_block(gpu, info->fsynr1 & 0xff);
+
+ return adreno_fault_handler(gpu, iova, flags, info, block, scratch);
+}
+
+static void a8xx_cp_hw_err_irq(struct msm_gpu *gpu)
+{
+ u32 status = gpu_read(gpu, REG_A8XX_CP_INTERRUPT_STATUS_GLOBAL);
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ u32 slice = a8xx_get_first_slice(a6xx_gpu);
+ u32 hw_fault_mask = GENMASK(6, 0);
+ u32 sw_fault_mask = GENMASK(22, 16);
+ u32 pipe = 0;
+
+ dev_err_ratelimited(&gpu->pdev->dev, "CP Fault Global INT status: 0x%x\n", status);
+
+ if (status & (A8XX_CP_GLOBAL_INT_MASK_HWFAULTBR |
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTBR))
+ pipe |= BIT(PIPE_BR);
+
+ if (status & (A8XX_CP_GLOBAL_INT_MASK_HWFAULTBV |
+ A8XX_CP_GLOBAL_INT_MASK_SWFAULTBV))
+ pipe |= BIT(PIPE_BV);
+
+ if (!pipe) {
+ dev_err_ratelimited(&gpu->pdev->dev, "CP Fault Unknown pipe\n");
+ goto out;
+ }
+
+ for (unsigned int pipe_id = PIPE_NONE; pipe_id <= PIPE_DDE_BV; pipe_id++) {
+ if (!(BIT(pipe_id) & pipe))
+ continue;
+
+ if (hw_fault_mask & status) {
+ status = a8xx_read_pipe_slice(gpu, pipe_id, slice,
+ REG_A8XX_CP_HW_FAULT_STATUS_PIPE);
+ dev_err_ratelimited(&gpu->pdev->dev,
+ "CP HW FAULT pipe: %u status: 0x%x\n", pipe_id, status);
+ }
+
+ if (sw_fault_mask & status) {
+ status = a8xx_read_pipe_slice(gpu, pipe_id, slice,
+ REG_A8XX_CP_INTERRUPT_STATUS_PIPE);
+ dev_err_ratelimited(&gpu->pdev->dev,
+ "CP SW FAULT pipe: %u status: 0x%x\n", pipe_id, status);
+
+ if (status & BIT(8)) {
+ a8xx_write_pipe(gpu, pipe_id, REG_A8XX_CP_SQE_STAT_ADDR_PIPE, 1);
+ status = a8xx_read_pipe_slice(gpu, pipe_id, slice,
+ REG_A8XX_CP_SQE_STAT_DATA_PIPE);
+ dev_err_ratelimited(&gpu->pdev->dev,
+ "CP Opcode error, opcode=0x%x\n", status);
+ }
+
+ if (status & BIT(10)) {
+ status = a8xx_read_pipe_slice(gpu, pipe_id, slice,
+ REG_A8XX_CP_PROTECT_STATUS_PIPE);
+ dev_err_ratelimited(&gpu->pdev->dev,
+ "CP REG PROTECT error, status=0x%x\n", status);
+ }
+ }
+ }
+
+out:
+ /* Turn off interrupts to avoid triggering recovery again */
+ a8xx_aperture_clear(gpu);
+ gpu_write(gpu, REG_A8XX_CP_INTERRUPT_STATUS_MASK_GLOBAL, 0);
+ gpu_write(gpu, REG_A8XX_RBBM_INT_0_MASK, 0);
+
+ kthread_queue_work(gpu->worker, &gpu->recover_work);
+}
+
+static u32 gpu_periph_read(struct msm_gpu *gpu, u32 dbg_offset)
+{
+ gpu_write(gpu, REG_A8XX_CP_SQE_UCODE_DBG_ADDR_PIPE, dbg_offset);
+
+ return gpu_read(gpu, REG_A8XX_CP_SQE_UCODE_DBG_DATA_PIPE);
+}
+
+static u64 gpu_periph_read64(struct msm_gpu *gpu, u32 dbg_offset)
+{
+ u64 lo, hi;
+
+ lo = gpu_periph_read(gpu, dbg_offset);
+ hi = gpu_periph_read(gpu, dbg_offset + 1);
+
+ return (hi << 32) | lo;
+}
+
+#define CP_PERIPH_IB1_BASE_LO 0x7005
+#define CP_PERIPH_IB1_BASE_HI 0x7006
+#define CP_PERIPH_IB1_SIZE 0x7007
+#define CP_PERIPH_IB1_OFFSET 0x7008
+#define CP_PERIPH_IB2_BASE_LO 0x7009
+#define CP_PERIPH_IB2_BASE_HI 0x700a
+#define CP_PERIPH_IB2_SIZE 0x700b
+#define CP_PERIPH_IB2_OFFSET 0x700c
+#define CP_PERIPH_IB3_BASE_LO 0x700d
+#define CP_PERIPH_IB3_BASE_HI 0x700e
+#define CP_PERIPH_IB3_SIZE 0x700f
+#define CP_PERIPH_IB3_OFFSET 0x7010
+
+static void a8xx_fault_detect_irq(struct msm_gpu *gpu)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ struct msm_ringbuffer *ring = gpu->funcs->active_ring(gpu);
+ unsigned long flags;
+
+ /*
+ * If stalled on SMMU fault, we could trip the GPU's hang detection,
+ * but the fault handler will trigger the devcore dump, and we want
+ * to otherwise resume normally rather than killing the submit, so
+ * just bail.
+ */
+ if (gpu_read(gpu, REG_A8XX_RBBM_MISC_STATUS) & A8XX_RBBM_MISC_STATUS_SMMU_STALLED_ON_FAULT)
+ return;
+
+ /*
+ * Force the GPU to stay on until after we finish
+ * collecting information
+ */
+ if (!adreno_has_gmu_wrapper(adreno_gpu))
+ gmu_write(&a6xx_gpu->gmu, REG_A6XX_GMU_GMU_PWR_COL_KEEPALIVE, 1);
+
+ DRM_DEV_ERROR(&gpu->pdev->dev,
+ "gpu fault ring %d fence %x status %8.8X gfx_status %8.8X\n",
+ ring ? ring->id : -1, ring ? ring->fctx->last_fence : 0,
+ gpu_read(gpu, REG_A8XX_RBBM_STATUS), gpu_read(gpu, REG_A8XX_RBBM_GFX_STATUS));
+
+ a8xx_aperture_acquire(gpu, PIPE_BR, &flags);
+
+ DRM_DEV_ERROR(&gpu->pdev->dev,
+ "BR: status %8.8X rb %4.4x/%4.4x ib1 %16.16llX/%4.4x ib2 %16.16llX/%4.4x ib3 %16.16llX/%4.4x\n",
+ gpu_read(gpu, REG_A8XX_RBBM_GFX_BR_STATUS),
+ gpu_read(gpu, REG_A6XX_CP_RB_RPTR),
+ gpu_read(gpu, REG_A6XX_CP_RB_WPTR),
+ gpu_periph_read64(gpu, CP_PERIPH_IB1_BASE_LO),
+ gpu_periph_read(gpu, CP_PERIPH_IB1_OFFSET),
+ gpu_periph_read64(gpu, CP_PERIPH_IB2_BASE_LO),
+ gpu_periph_read(gpu, CP_PERIPH_IB2_OFFSET),
+ gpu_periph_read64(gpu, CP_PERIPH_IB3_BASE_LO),
+ gpu_periph_read(gpu, CP_PERIPH_IB3_OFFSET));
+
+ a8xx_aperture_release(gpu, flags);
+ a8xx_aperture_acquire(gpu, PIPE_BV, &flags);
+
+ DRM_DEV_ERROR(&gpu->pdev->dev,
+ "BV: status %8.8X rb %4.4x/%4.4x ib1 %16.16llX/%4.4x ib2 %16.16llX/%4.4x ib3 %16.16llX/%4.4x\n",
+ gpu_read(gpu, REG_A8XX_RBBM_GFX_BV_STATUS),
+ gpu_read(gpu, REG_A8XX_CP_RB_RPTR_BV),
+ gpu_read(gpu, REG_A6XX_CP_RB_WPTR),
+ gpu_periph_read64(gpu, CP_PERIPH_IB1_BASE_LO),
+ gpu_periph_read(gpu, CP_PERIPH_IB1_OFFSET),
+ gpu_periph_read64(gpu, CP_PERIPH_IB2_BASE_LO),
+ gpu_periph_read(gpu, CP_PERIPH_IB2_OFFSET),
+ gpu_periph_read64(gpu, CP_PERIPH_IB3_BASE_LO),
+ gpu_periph_read(gpu, CP_PERIPH_IB3_OFFSET));
+
+ a8xx_aperture_release(gpu, flags);
+ a8xx_aperture_clear(gpu);
+
+ /* Turn off the hangcheck timer to keep it from bothering us */
+ timer_delete(&gpu->hangcheck_timer);
+
+ kthread_queue_work(gpu->worker, &gpu->recover_work);
+}
+
+static void a8xx_sw_fuse_violation_irq(struct msm_gpu *gpu)
+{
+ u32 status;
+
+ status = gpu_read(gpu, REG_A8XX_RBBM_SW_FUSE_INT_STATUS);
+ gpu_write(gpu, REG_A8XX_RBBM_SW_FUSE_INT_MASK, 0);
+
+ dev_err_ratelimited(&gpu->pdev->dev, "SW fuse violation status=%8.8x\n", status);
+
+ /*
+ * Ignore FASTBLEND violations, because the HW will silently fall back
+ * to legacy blending.
+ */
+ if (status & (A7XX_CX_MISC_SW_FUSE_VALUE_RAYTRACING |
+ A7XX_CX_MISC_SW_FUSE_VALUE_LPAC)) {
+ timer_delete(&gpu->hangcheck_timer);
+
+ kthread_queue_work(gpu->worker, &gpu->recover_work);
+ }
+}
+
+irqreturn_t a8xx_irq(struct msm_gpu *gpu)
+{
+ struct msm_drm_private *priv = gpu->dev->dev_private;
+ u32 status = gpu_read(gpu, REG_A8XX_RBBM_INT_0_STATUS);
+
+ gpu_write(gpu, REG_A8XX_RBBM_INT_CLEAR_CMD, status);
+
+ if (priv->disable_err_irq)
+ status &= A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS;
+
+ if (status & A6XX_RBBM_INT_0_MASK_RBBM_HANG_DETECT)
+ a8xx_fault_detect_irq(gpu);
+
+ if (status & A6XX_RBBM_INT_0_MASK_CP_AHB_ERROR) {
+ u32 rl0, rl1;
+
+ rl0 = gpu_read(gpu, REG_A8XX_CP_RL_ERROR_DETAILS_0);
+ rl1 = gpu_read(gpu, REG_A8XX_CP_RL_ERROR_DETAILS_1);
+ dev_err_ratelimited(&gpu->pdev->dev,
+ "CP | AHB bus error RL_ERROR_0: %x, RL_ERROR_1: %x\n", rl0, rl1);
+ }
+
+ if (status & A6XX_RBBM_INT_0_MASK_CP_HW_ERROR)
+ a8xx_cp_hw_err_irq(gpu);
+
+ if (status & A6XX_RBBM_INT_0_MASK_RBBM_ATB_ASYNCFIFO_OVERFLOW)
+ dev_err_ratelimited(&gpu->pdev->dev, "RBBM | ATB ASYNC overflow\n");
+
+ if (status & A6XX_RBBM_INT_0_MASK_RBBM_ATB_BUS_OVERFLOW)
+ dev_err_ratelimited(&gpu->pdev->dev, "RBBM | ATB bus overflow\n");
+
+ if (status & A6XX_RBBM_INT_0_MASK_UCHE_OOB_ACCESS)
+ dev_err_ratelimited(&gpu->pdev->dev, "UCHE | Out of bounds access\n");
+
+ if (status & A6XX_RBBM_INT_0_MASK_UCHE_TRAP_INTR)
+ dev_err_ratelimited(&gpu->pdev->dev, "UCHE | Trap interrupt\n");
+
+ if (status & A6XX_RBBM_INT_0_MASK_SWFUSEVIOLATION)
+ a8xx_sw_fuse_violation_irq(gpu);
+
+ if (status & A6XX_RBBM_INT_0_MASK_CP_CACHE_FLUSH_TS) {
+ msm_gpu_retire(gpu);
+ a6xx_preempt_trigger(gpu);
+ }
+
+ if (status & A6XX_RBBM_INT_0_MASK_CP_SW)
+ a6xx_preempt_irq(gpu);
+
+ return IRQ_HANDLED;
+}
+
+void a8xx_llc_activate(struct a6xx_gpu *a6xx_gpu)
+{
+ struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
+ struct msm_gpu *gpu = &adreno_gpu->base;
+
+ if (!llcc_slice_activate(a6xx_gpu->llc_slice)) {
+ u32 gpu_scid = llcc_get_slice_id(a6xx_gpu->llc_slice);
+
+ gpu_scid &= GENMASK(5, 0);
+
+ gpu_write(gpu, REG_A6XX_GBIF_SCACHE_CNTL1,
+ FIELD_PREP(GENMASK(29, 24), gpu_scid) |
+ FIELD_PREP(GENMASK(23, 18), gpu_scid) |
+ FIELD_PREP(GENMASK(17, 12), gpu_scid) |
+ FIELD_PREP(GENMASK(11, 6), gpu_scid) |
+ FIELD_PREP(GENMASK(5, 0), gpu_scid));
+
+ gpu_write(gpu, REG_A6XX_GBIF_SCACHE_CNTL0,
+ FIELD_PREP(GENMASK(27, 22), gpu_scid) |
+ FIELD_PREP(GENMASK(21, 16), gpu_scid) |
+ FIELD_PREP(GENMASK(15, 10), gpu_scid) |
+ BIT(8));
+ }
+
+ llcc_slice_activate(a6xx_gpu->htw_llc_slice);
+}
+
+#define GBIF_CLIENT_HALT_MASK BIT(0)
+#define GBIF_ARB_HALT_MASK BIT(1)
+#define VBIF_XIN_HALT_CTRL0_MASK GENMASK(3, 0)
+#define VBIF_RESET_ACK_MASK 0xF0
+#define GPR0_GBIF_HALT_REQUEST 0x1E0
+
+void a8xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu, bool gx_off)
+{
+ struct msm_gpu *gpu = &adreno_gpu->base;
+
+ if (gx_off) {
+ /* Halt the gx side of GBIF */
+ gpu_write(gpu, REG_A8XX_RBBM_GBIF_HALT, 1);
+ spin_until(gpu_read(gpu, REG_A8XX_RBBM_GBIF_HALT_ACK) & 1);
+ }
+
+ /* Halt new client requests on GBIF */
+ gpu_write(gpu, REG_A6XX_GBIF_HALT, GBIF_CLIENT_HALT_MASK);
+ spin_until((gpu_read(gpu, REG_A6XX_GBIF_HALT_ACK) &
+ (GBIF_CLIENT_HALT_MASK)) == GBIF_CLIENT_HALT_MASK);
+
+ /* Halt all AXI requests on GBIF */
+ gpu_write(gpu, REG_A6XX_GBIF_HALT, GBIF_ARB_HALT_MASK);
+ spin_until((gpu_read(gpu, REG_A6XX_GBIF_HALT_ACK) &
+ (GBIF_ARB_HALT_MASK)) == GBIF_ARB_HALT_MASK);
+
+ /* The GBIF halt needs to be explicitly cleared */
+ gpu_write(gpu, REG_A6XX_GBIF_HALT, 0x0);
+}
+
+int a8xx_gmu_get_timestamp(struct msm_gpu *gpu, uint64_t *value)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+
+ mutex_lock(&a6xx_gpu->gmu.lock);
+
+ /* Force the GPU power on so we can read this register */
+ a6xx_gmu_set_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET);
+
+ *value = gpu_read64(gpu, REG_A8XX_CP_ALWAYS_ON_COUNTER);
+
+ a6xx_gmu_clear_oob(&a6xx_gpu->gmu, GMU_OOB_PERFCOUNTER_SET);
+
+ mutex_unlock(&a6xx_gpu->gmu.lock);
+
+ return 0;
+}
+
+u64 a8xx_gpu_busy(struct msm_gpu *gpu, unsigned long *out_sample_rate)
+{
+ struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
+ u64 busy_cycles;
+
+ /* 19.2MHz */
+ *out_sample_rate = 19200000;
+
+ busy_cycles = gmu_read64(&a6xx_gpu->gmu,
+ REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L,
+ REG_A8XX_GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H);
+
+ return busy_cycles;
+}
+
+bool a8xx_progress(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
+{
+ return true;
+}
diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c
index 28f744f3caf7..554d746f115b 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_device.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_device.c
@@ -34,6 +34,7 @@ extern const struct adreno_gpulist a4xx_gpulist;
extern const struct adreno_gpulist a5xx_gpulist;
extern const struct adreno_gpulist a6xx_gpulist;
extern const struct adreno_gpulist a7xx_gpulist;
+extern const struct adreno_gpulist a8xx_gpulist;
static const struct adreno_gpulist *gpulists[] = {
&a2xx_gpulist,
@@ -42,6 +43,7 @@ static const struct adreno_gpulist *gpulists[] = {
&a5xx_gpulist,
&a6xx_gpulist,
&a7xx_gpulist,
+ &a8xx_gpulist,
};
static const struct adreno_info *adreno_info(uint32_t chip_id)
@@ -235,7 +237,7 @@ static int adreno_bind(struct device *dev, struct device *master, void *data)
priv->has_cached_coherent =
!!(info->quirks & ADRENO_QUIRK_HAS_CACHED_COHERENT);
- gpu = info->init(drm);
+ gpu = info->funcs->init(drm);
if (IS_ERR(gpu)) {
dev_warn(drm->dev, "failed to load adreno gpu\n");
return PTR_ERR(gpu);
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gen7_0_0_snapshot.h b/drivers/gpu/drm/msm/adreno/adreno_gen7_0_0_snapshot.h
index 04b49d385f9d..d513e03fef08 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gen7_0_0_snapshot.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gen7_0_0_snapshot.h
@@ -82,85 +82,85 @@ static const u32 gen7_0_0_debugbus_blocks[] = {
};
static const struct gen7_shader_block gen7_0_0_shader_blocks[] = {
- {A7XX_TP0_TMO_DATA, 0x200, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_TP0_SMO_DATA, 0x80, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_TP0_MIPMAP_BASE_DATA, 0x3c0, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_DATA_1, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_0_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_1_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_2_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_3_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_4_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_5_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_6_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_7_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_CB_RAM, 0x390, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_TAG, 0x90, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_DATA_2, 0x200, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_TMO_TAG, 0x80, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_SMO_TAG, 0x80, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_STATE_DATA, 0x40, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_HWAVE_RAM, 0x100, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_L0_INST_BUF, 0x50, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_8_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_9_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_10_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_11_DATA, 0x800, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_12_DATA, 0x200, 4, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM, 0x300, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM, 0x300, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM, 0x300, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM, 0x800, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM_1, 0x200, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x64, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x64, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x64, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x64, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_1, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_STPROC_META, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_DATAPATH_META, 0x20, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_FRONTEND_META, 0x40, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_FRONTEND_META, 0x40, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_FRONTEND_META, 0x40, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INDIRECT_META, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_TP0_TMO_DATA, 0x200, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_TP0_SMO_DATA, 0x80, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_TP0_MIPMAP_BASE_DATA, 0x3c0, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_DATA_1, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_0_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_1_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_2_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_3_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_4_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_5_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_6_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_7_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_CB_RAM, 0x390, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_TAG, 0x90, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_DATA_2, 0x200, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_TMO_TAG, 0x80, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_SMO_TAG, 0x80, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_STATE_DATA, 0x40, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_HWAVE_RAM, 0x100, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_L0_INST_BUF, 0x50, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_8_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_9_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_10_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_11_DATA, 0x800, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_12_DATA, 0x200, 4, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM, 0x300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM, 0x300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM, 0x300, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM, 0x800, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM_1, 0x200, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x64, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x64, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x64, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x64, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_1, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_STPROC_META, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_DATAPATH_META, 0x20, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_FRONTEND_META, 0x40, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_FRONTEND_META, 0x40, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_FRONTEND_META, 0x40, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INDIRECT_META, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
};
static const u32 gen7_0_0_pre_crashdumper_gpu_registers[] = {
@@ -303,7 +303,7 @@ static const u32 gen7_0_0_noncontext_rb_rbp_pipe_br_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_noncontext_rb_rbp_pipe_br_registers), 8));
-/* Block: GRAS Cluster: A7XX_CLUSTER_GRAS Pipeline: A7XX_PIPE_BR */
+/* Block: GRAS Cluster: A7XX_CLUSTER_GRAS Pipeline: PIPE_BR */
static const u32 gen7_0_0_gras_cluster_gras_pipe_br_registers[] = {
0x08000, 0x08008, 0x08010, 0x08092, 0x08094, 0x08099, 0x0809b, 0x0809d,
0x080a0, 0x080a7, 0x080af, 0x080f1, 0x080f4, 0x080f6, 0x080f8, 0x080fa,
@@ -313,7 +313,7 @@ static const u32 gen7_0_0_gras_cluster_gras_pipe_br_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_gras_cluster_gras_pipe_br_registers), 8));
-/* Block: GRAS Cluster: A7XX_CLUSTER_GRAS Pipeline: A7XX_PIPE_BV */
+/* Block: GRAS Cluster: A7XX_CLUSTER_GRAS Pipeline: PIPE_BV */
static const u32 gen7_0_0_gras_cluster_gras_pipe_bv_registers[] = {
0x08000, 0x08008, 0x08010, 0x08092, 0x08094, 0x08099, 0x0809b, 0x0809d,
0x080a0, 0x080a7, 0x080af, 0x080f1, 0x080f4, 0x080f6, 0x080f8, 0x080fa,
@@ -323,7 +323,7 @@ static const u32 gen7_0_0_gras_cluster_gras_pipe_bv_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_gras_cluster_gras_pipe_bv_registers), 8));
-/* Block: PC Cluster: A7XX_CLUSTER_FE Pipeline: A7XX_PIPE_BR */
+/* Block: PC Cluster: A7XX_CLUSTER_FE Pipeline: PIPE_BR */
static const u32 gen7_0_0_pc_cluster_fe_pipe_br_registers[] = {
0x09800, 0x09804, 0x09806, 0x0980a, 0x09810, 0x09811, 0x09884, 0x09886,
0x09b00, 0x09b08,
@@ -331,7 +331,7 @@ static const u32 gen7_0_0_pc_cluster_fe_pipe_br_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_pc_cluster_fe_pipe_br_registers), 8));
-/* Block: PC Cluster: A7XX_CLUSTER_FE Pipeline: A7XX_PIPE_BV */
+/* Block: PC Cluster: A7XX_CLUSTER_FE Pipeline: PIPE_BV */
static const u32 gen7_0_0_pc_cluster_fe_pipe_bv_registers[] = {
0x09800, 0x09804, 0x09806, 0x0980a, 0x09810, 0x09811, 0x09884, 0x09886,
0x09b00, 0x09b08,
@@ -339,7 +339,7 @@ static const u32 gen7_0_0_pc_cluster_fe_pipe_bv_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_pc_cluster_fe_pipe_bv_registers), 8));
-/* Block: RB_RAC Cluster: A7XX_CLUSTER_PS Pipeline: A7XX_PIPE_BR */
+/* Block: RB_RAC Cluster: A7XX_CLUSTER_PS Pipeline: PIPE_BR */
static const u32 gen7_0_0_rb_rac_cluster_ps_pipe_br_registers[] = {
0x08802, 0x08802, 0x08804, 0x08806, 0x08809, 0x0880a, 0x0880e, 0x08811,
0x08818, 0x0881e, 0x08821, 0x08821, 0x08823, 0x08826, 0x08829, 0x08829,
@@ -355,7 +355,7 @@ static const u32 gen7_0_0_rb_rac_cluster_ps_pipe_br_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_rb_rac_cluster_ps_pipe_br_registers), 8));
-/* Block: RB_RBP Cluster: A7XX_CLUSTER_PS Pipeline: A7XX_PIPE_BR */
+/* Block: RB_RBP Cluster: A7XX_CLUSTER_PS Pipeline: PIPE_BR */
static const u32 gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers[] = {
0x08800, 0x08801, 0x08803, 0x08803, 0x0880b, 0x0880d, 0x08812, 0x08812,
0x08820, 0x08820, 0x08822, 0x08822, 0x08827, 0x08828, 0x0882a, 0x0882a,
@@ -370,7 +370,7 @@ static const u32 gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BR Location: HLSQ_STATE */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BR Location: HLSQ_STATE */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers[] = {
0x0a980, 0x0a980, 0x0a982, 0x0a984, 0x0a99e, 0x0a99e, 0x0a9a7, 0x0a9a7,
0x0a9aa, 0x0a9aa, 0x0a9ae, 0x0a9b0, 0x0a9b3, 0x0a9b5, 0x0a9ba, 0x0a9ba,
@@ -381,7 +381,7 @@ static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_LPAC Location: HLSQ_STATE */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_LPAC Location: HLSQ_STATE */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers[] = {
0x0a9b0, 0x0a9b0, 0x0a9b3, 0x0a9b5, 0x0a9ba, 0x0a9ba, 0x0a9bc, 0x0a9bc,
0x0a9c4, 0x0a9c4, 0x0a9cd, 0x0a9cd, 0x0a9e2, 0x0a9e3, 0x0a9e6, 0x0a9fc,
@@ -390,21 +390,21 @@ static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BR Location: HLSQ_DP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BR Location: HLSQ_DP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers[] = {
0x0a9b1, 0x0a9b1, 0x0a9c6, 0x0a9cb, 0x0a9d4, 0x0a9df,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_LPAC Location: HLSQ_DP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_LPAC Location: HLSQ_DP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_dp_registers[] = {
0x0a9b1, 0x0a9b1, 0x0a9d4, 0x0a9df,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_dp_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BR Location: SP_TOP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BR Location: SP_TOP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers[] = {
0x0a980, 0x0a980, 0x0a982, 0x0a984, 0x0a99e, 0x0a9a2, 0x0a9a7, 0x0a9a8,
0x0a9aa, 0x0a9aa, 0x0a9ae, 0x0a9ae, 0x0a9b0, 0x0a9b1, 0x0a9b3, 0x0a9b5,
@@ -414,7 +414,7 @@ static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_LPAC Location: SP_TOP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_LPAC Location: SP_TOP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_lpac_sp_top_registers[] = {
0x0a9b0, 0x0a9b1, 0x0a9b3, 0x0a9b5, 0x0a9ba, 0x0a9bc, 0x0a9e2, 0x0a9e3,
0x0a9e6, 0x0a9f9, 0x0aa00, 0x0aa00, 0x0ab00, 0x0ab00,
@@ -422,7 +422,7 @@ static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_lpac_sp_top_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_lpac_sp_top_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BR Location: uSPTP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BR Location: uSPTP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers[] = {
0x0a980, 0x0a982, 0x0a985, 0x0a9a6, 0x0a9a8, 0x0a9a9, 0x0a9ab, 0x0a9ae,
0x0a9b0, 0x0a9b3, 0x0a9b6, 0x0a9b9, 0x0a9bb, 0x0a9bf, 0x0a9c2, 0x0a9c3,
@@ -432,7 +432,7 @@ static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_LPAC Location: uSPTP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_LPAC Location: uSPTP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers[] = {
0x0a9b0, 0x0a9b3, 0x0a9b6, 0x0a9b9, 0x0a9bb, 0x0a9be, 0x0a9c2, 0x0a9c3,
0x0a9cd, 0x0a9cd, 0x0a9d0, 0x0a9d3, 0x0aa31, 0x0aa31, 0x0ab00, 0x0ab01,
@@ -440,7 +440,7 @@ static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BR Location: HLSQ_STATE */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BR Location: HLSQ_STATE */
static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers[] = {
0x0a800, 0x0a800, 0x0a81b, 0x0a81d, 0x0a822, 0x0a822, 0x0a824, 0x0a824,
0x0a827, 0x0a82a, 0x0a830, 0x0a830, 0x0a833, 0x0a835, 0x0a83a, 0x0a83a,
@@ -453,7 +453,7 @@ static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BV Location: HLSQ_STATE */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BV Location: HLSQ_STATE */
static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_bv_hlsq_state_registers[] = {
0x0a800, 0x0a800, 0x0a81b, 0x0a81d, 0x0a822, 0x0a822, 0x0a824, 0x0a824,
0x0a827, 0x0a82a, 0x0a830, 0x0a830, 0x0a833, 0x0a835, 0x0a83a, 0x0a83a,
@@ -466,7 +466,7 @@ static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_bv_hlsq_state_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_vs_pipe_bv_hlsq_state_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BR Location: SP_TOP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BR Location: SP_TOP */
static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_br_sp_top_registers[] = {
0x0a800, 0x0a800, 0x0a81c, 0x0a81d, 0x0a822, 0x0a824, 0x0a830, 0x0a831,
0x0a834, 0x0a835, 0x0a83a, 0x0a83c, 0x0a840, 0x0a840, 0x0a85c, 0x0a85d,
@@ -477,7 +477,7 @@ static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_br_sp_top_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_vs_pipe_br_sp_top_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BV Location: SP_TOP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BV Location: SP_TOP */
static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_bv_sp_top_registers[] = {
0x0a800, 0x0a800, 0x0a81c, 0x0a81d, 0x0a822, 0x0a824, 0x0a830, 0x0a831,
0x0a834, 0x0a835, 0x0a83a, 0x0a83c, 0x0a840, 0x0a840, 0x0a85c, 0x0a85d,
@@ -488,7 +488,7 @@ static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_bv_sp_top_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_vs_pipe_bv_sp_top_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BR Location: uSPTP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BR Location: uSPTP */
static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_br_usptp_registers[] = {
0x0a800, 0x0a81b, 0x0a81e, 0x0a821, 0x0a823, 0x0a827, 0x0a830, 0x0a833,
0x0a836, 0x0a839, 0x0a83b, 0x0a85b, 0x0a85e, 0x0a861, 0x0a863, 0x0a867,
@@ -498,7 +498,7 @@ static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_br_usptp_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_vs_pipe_br_usptp_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BV Location: uSPTP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BV Location: uSPTP */
static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_bv_usptp_registers[] = {
0x0a800, 0x0a81b, 0x0a81e, 0x0a821, 0x0a823, 0x0a827, 0x0a830, 0x0a833,
0x0a836, 0x0a839, 0x0a83b, 0x0a85b, 0x0a85e, 0x0a861, 0x0a863, 0x0a867,
@@ -508,7 +508,7 @@ static const u32 gen7_0_0_sp_cluster_sp_vs_pipe_bv_usptp_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_vs_pipe_bv_usptp_registers), 8));
-/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BR */
+/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BR */
static const u32 gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers[] = {
0x0b180, 0x0b183, 0x0b190, 0x0b195, 0x0b2c0, 0x0b2d5, 0x0b300, 0x0b307,
0x0b309, 0x0b309, 0x0b310, 0x0b310,
@@ -516,35 +516,35 @@ static const u32 gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BV Location: HLSQ_STATE */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BV Location: HLSQ_STATE */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_bv_hlsq_state_registers[] = {
0x0ab00, 0x0ab02, 0x0ab0a, 0x0ab1b, 0x0ab20, 0x0ab20, 0x0ab40, 0x0abbf,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_bv_hlsq_state_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BV Location: SP_TOP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BV Location: SP_TOP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_bv_sp_top_registers[] = {
0x0ab00, 0x0ab00, 0x0ab02, 0x0ab02, 0x0ab0a, 0x0ab1b, 0x0ab20, 0x0ab20,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_bv_sp_top_registers), 8));
-/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BV Location: uSPTP */
+/* Block: SP Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BV Location: uSPTP */
static const u32 gen7_0_0_sp_cluster_sp_ps_pipe_bv_usptp_registers[] = {
0x0ab00, 0x0ab02, 0x0ab21, 0x0ab22, 0x0ab40, 0x0abbf,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_cluster_sp_ps_pipe_bv_usptp_registers), 8));
-/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_BV */
+/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_BV */
static const u32 gen7_0_0_tpl1_cluster_sp_ps_pipe_bv_registers[] = {
0x0b300, 0x0b307, 0x0b309, 0x0b309, 0x0b310, 0x0b310,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_cluster_sp_ps_pipe_bv_registers), 8));
-/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_PS Pipeline: A7XX_PIPE_LPAC */
+/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_PS Pipeline: PIPE_LPAC */
static const u32 gen7_0_0_tpl1_cluster_sp_ps_pipe_lpac_registers[] = {
0x0b180, 0x0b181, 0x0b300, 0x0b301, 0x0b307, 0x0b307, 0x0b309, 0x0b309,
0x0b310, 0x0b310,
@@ -552,84 +552,84 @@ static const u32 gen7_0_0_tpl1_cluster_sp_ps_pipe_lpac_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_cluster_sp_ps_pipe_lpac_registers), 8));
-/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BR */
+/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BR */
static const u32 gen7_0_0_tpl1_cluster_sp_vs_pipe_br_registers[] = {
0x0b300, 0x0b307, 0x0b309, 0x0b309, 0x0b310, 0x0b310,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_cluster_sp_vs_pipe_br_registers), 8));
-/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_VS Pipeline: A7XX_PIPE_BV */
+/* Block: TPL1 Cluster: A7XX_CLUSTER_SP_VS Pipeline: PIPE_BV */
static const u32 gen7_0_0_tpl1_cluster_sp_vs_pipe_bv_registers[] = {
0x0b300, 0x0b307, 0x0b309, 0x0b309, 0x0b310, 0x0b310,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_cluster_sp_vs_pipe_bv_registers), 8));
-/* Block: VFD Cluster: A7XX_CLUSTER_FE Pipeline: A7XX_PIPE_BR */
+/* Block: VFD Cluster: A7XX_CLUSTER_FE Pipeline: PIPE_BR */
static const u32 gen7_0_0_vfd_cluster_fe_pipe_br_registers[] = {
0x0a000, 0x0a009, 0x0a00e, 0x0a0ef,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vfd_cluster_fe_pipe_br_registers), 8));
-/* Block: VFD Cluster: A7XX_CLUSTER_FE Pipeline: A7XX_PIPE_BV */
+/* Block: VFD Cluster: A7XX_CLUSTER_FE Pipeline: PIPE_BV */
static const u32 gen7_0_0_vfd_cluster_fe_pipe_bv_registers[] = {
0x0a000, 0x0a009, 0x0a00e, 0x0a0ef,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vfd_cluster_fe_pipe_bv_registers), 8));
-/* Block: VPC Cluster: A7XX_CLUSTER_FE Pipeline: A7XX_PIPE_BR */
+/* Block: VPC Cluster: A7XX_CLUSTER_FE Pipeline: PIPE_BR */
static const u32 gen7_0_0_vpc_cluster_fe_pipe_br_registers[] = {
0x09300, 0x09307,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vpc_cluster_fe_pipe_br_registers), 8));
-/* Block: VPC Cluster: A7XX_CLUSTER_FE Pipeline: A7XX_PIPE_BV */
+/* Block: VPC Cluster: A7XX_CLUSTER_FE Pipeline: PIPE_BV */
static const u32 gen7_0_0_vpc_cluster_fe_pipe_bv_registers[] = {
0x09300, 0x09307,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vpc_cluster_fe_pipe_bv_registers), 8));
-/* Block: VPC Cluster: A7XX_CLUSTER_PC_VS Pipeline: A7XX_PIPE_BR */
+/* Block: VPC Cluster: A7XX_CLUSTER_PC_VS Pipeline: PIPE_BR */
static const u32 gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers[] = {
0x09101, 0x0910c, 0x09300, 0x09307,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers), 8));
-/* Block: VPC Cluster: A7XX_CLUSTER_PC_VS Pipeline: A7XX_PIPE_BV */
+/* Block: VPC Cluster: A7XX_CLUSTER_PC_VS Pipeline: PIPE_BV */
static const u32 gen7_0_0_vpc_cluster_pc_vs_pipe_bv_registers[] = {
0x09101, 0x0910c, 0x09300, 0x09307,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vpc_cluster_pc_vs_pipe_bv_registers), 8));
-/* Block: VPC Cluster: A7XX_CLUSTER_VPC_PS Pipeline: A7XX_PIPE_BR */
+/* Block: VPC Cluster: A7XX_CLUSTER_VPC_PS Pipeline: PIPE_BR */
static const u32 gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers[] = {
0x09200, 0x0920f, 0x09212, 0x09216, 0x09218, 0x09236, 0x09300, 0x09307,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers), 8));
-/* Block: VPC Cluster: A7XX_CLUSTER_VPC_PS Pipeline: A7XX_PIPE_BV */
+/* Block: VPC Cluster: A7XX_CLUSTER_VPC_PS Pipeline: PIPE_BV */
static const u32 gen7_0_0_vpc_cluster_vpc_ps_pipe_bv_registers[] = {
0x09200, 0x0920f, 0x09212, 0x09216, 0x09218, 0x09236, 0x09300, 0x09307,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_vpc_cluster_vpc_ps_pipe_bv_registers), 8));
-/* Block: SP Cluster: noncontext Pipeline: A7XX_PIPE_BR Location: HLSQ_STATE */
+/* Block: SP Cluster: noncontext Pipeline: PIPE_BR Location: HLSQ_STATE */
static const u32 gen7_0_0_sp_noncontext_pipe_br_hlsq_state_registers[] = {
0x0ae52, 0x0ae52, 0x0ae60, 0x0ae67, 0x0ae69, 0x0ae73,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_noncontext_pipe_br_hlsq_state_registers), 8));
-/* Block: SP Cluster: noncontext Pipeline: A7XX_PIPE_BR Location: SP_TOP */
+/* Block: SP Cluster: noncontext Pipeline: PIPE_BR Location: SP_TOP */
static const u32 gen7_0_0_sp_noncontext_pipe_br_sp_top_registers[] = {
0x0ae00, 0x0ae00, 0x0ae02, 0x0ae04, 0x0ae06, 0x0ae09, 0x0ae0c, 0x0ae0c,
0x0ae0f, 0x0ae0f, 0x0ae28, 0x0ae2b, 0x0ae35, 0x0ae35, 0x0ae3a, 0x0ae3f,
@@ -638,7 +638,7 @@ static const u32 gen7_0_0_sp_noncontext_pipe_br_sp_top_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_noncontext_pipe_br_sp_top_registers), 8));
-/* Block: SP Cluster: noncontext Pipeline: A7XX_PIPE_BR Location: uSPTP */
+/* Block: SP Cluster: noncontext Pipeline: PIPE_BR Location: uSPTP */
static const u32 gen7_0_0_sp_noncontext_pipe_br_usptp_registers[] = {
0x0ae00, 0x0ae00, 0x0ae02, 0x0ae04, 0x0ae06, 0x0ae09, 0x0ae0c, 0x0ae0c,
0x0ae0f, 0x0ae0f, 0x0ae30, 0x0ae32, 0x0ae35, 0x0ae35, 0x0ae3a, 0x0ae3b,
@@ -647,28 +647,28 @@ static const u32 gen7_0_0_sp_noncontext_pipe_br_usptp_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_noncontext_pipe_br_usptp_registers), 8));
-/* Block: SP Cluster: noncontext Pipeline: A7XX_PIPE_LPAC Location: HLSQ_STATE */
+/* Block: SP Cluster: noncontext Pipeline: PIPE_LPAC Location: HLSQ_STATE */
static const u32 gen7_0_0_sp_noncontext_pipe_lpac_hlsq_state_registers[] = {
0x0af88, 0x0af8a,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_noncontext_pipe_lpac_hlsq_state_registers), 8));
-/* Block: SP Cluster: noncontext Pipeline: A7XX_PIPE_LPAC Location: SP_TOP */
+/* Block: SP Cluster: noncontext Pipeline: PIPE_LPAC Location: SP_TOP */
static const u32 gen7_0_0_sp_noncontext_pipe_lpac_sp_top_registers[] = {
0x0af80, 0x0af84,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_noncontext_pipe_lpac_sp_top_registers), 8));
-/* Block: SP Cluster: noncontext Pipeline: A7XX_PIPE_LPAC Location: uSPTP */
+/* Block: SP Cluster: noncontext Pipeline: PIPE_LPAC Location: uSPTP */
static const u32 gen7_0_0_sp_noncontext_pipe_lpac_usptp_registers[] = {
0x0af80, 0x0af84, 0x0af90, 0x0af92,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_sp_noncontext_pipe_lpac_usptp_registers), 8));
-/* Block: TPl1 Cluster: noncontext Pipeline: A7XX_PIPE_NONE */
+/* Block: TPl1 Cluster: noncontext Pipeline: PIPE_NONE */
static const u32 gen7_0_0_tpl1_noncontext_pipe_none_registers[] = {
0x0b600, 0x0b600, 0x0b602, 0x0b602, 0x0b604, 0x0b604, 0x0b608, 0x0b60c,
0x0b60f, 0x0b621, 0x0b630, 0x0b633,
@@ -676,14 +676,14 @@ static const u32 gen7_0_0_tpl1_noncontext_pipe_none_registers[] = {
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_noncontext_pipe_none_registers), 8));
-/* Block: TPl1 Cluster: noncontext Pipeline: A7XX_PIPE_BR */
+/* Block: TPl1 Cluster: noncontext Pipeline: PIPE_BR */
static const u32 gen7_0_0_tpl1_noncontext_pipe_br_registers[] = {
0x0b600, 0x0b600,
UINT_MAX, UINT_MAX,
};
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_noncontext_pipe_br_registers), 8));
-/* Block: TPl1 Cluster: noncontext Pipeline: A7XX_PIPE_LPAC */
+/* Block: TPl1 Cluster: noncontext Pipeline: PIPE_LPAC */
static const u32 gen7_0_0_tpl1_noncontext_pipe_lpac_registers[] = {
0x0b780, 0x0b780,
UINT_MAX, UINT_MAX,
@@ -691,184 +691,184 @@ static const u32 gen7_0_0_tpl1_noncontext_pipe_lpac_registers[] = {
static_assert(IS_ALIGNED(sizeof(gen7_0_0_tpl1_noncontext_pipe_lpac_registers), 8));
static const struct gen7_sel_reg gen7_0_0_rb_rac_sel = {
- .host_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_HOST,
- .cd_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD,
+ .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST,
+ .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD,
.val = 0x0,
};
static const struct gen7_sel_reg gen7_0_0_rb_rbp_sel = {
- .host_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_HOST,
- .cd_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD,
+ .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST,
+ .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD,
.val = 0x9,
};
static const struct gen7_cluster_registers gen7_0_0_clusters[] = {
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_0_0_noncontext_pipe_br_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BV, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BV, STATE_NON_CONTEXT,
gen7_0_0_noncontext_pipe_bv_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_LPAC, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_LPAC, STATE_NON_CONTEXT,
gen7_0_0_noncontext_pipe_lpac_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_0_0_noncontext_rb_rac_pipe_br_registers, &gen7_0_0_rb_rac_sel, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_0_0_noncontext_rb_rbp_pipe_br_registers, &gen7_0_0_rb_rbp_sel, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_gras_cluster_gras_pipe_br_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_GRAS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_gras_cluster_gras_pipe_bv_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_gras_cluster_gras_pipe_br_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_GRAS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_gras_cluster_gras_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_pc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_pc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_pc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_pc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_rb_rac_cluster_ps_pipe_br_registers, &gen7_0_0_rb_rac_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_rb_rac_cluster_ps_pipe_br_registers, &gen7_0_0_rb_rac_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers, &gen7_0_0_rb_rbp_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers, &gen7_0_0_rb_rbp_sel, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vfd_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vfd_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vfd_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vfd_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PC_VS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_pc_vs_pipe_bv_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PC_VS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_pc_vs_pipe_bv_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_vpc_ps_pipe_bv_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_vpc_ps_pipe_bv_registers, },
};
static const struct gen7_sptp_cluster_registers gen7_0_0_sptp_clusters[] = {
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_0_0_sp_noncontext_pipe_br_hlsq_state_registers, 0xae00 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_0_0_sp_noncontext_pipe_br_sp_top_registers, 0xae00 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_sp_noncontext_pipe_br_usptp_registers, 0xae00 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_HLSQ_STATE,
gen7_0_0_sp_noncontext_pipe_lpac_hlsq_state_registers, 0xaf80 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_SP_TOP,
gen7_0_0_sp_noncontext_pipe_lpac_sp_top_registers, 0xaf80 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_0_0_sp_noncontext_pipe_lpac_usptp_registers, 0xaf80 },
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_tpl1_noncontext_pipe_br_registers, 0xb600 },
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_0_0_tpl1_noncontext_pipe_lpac_registers, 0xb780 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_ps_pipe_lpac_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_vs_pipe_bv_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_vs_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_vs_pipe_bv_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_vs_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_vs_pipe_bv_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_HLSQ_STATE,
gen7_0_0_sp_cluster_sp_vs_pipe_bv_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_vs_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_SP_TOP,
gen7_0_0_sp_cluster_sp_vs_pipe_bv_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_vs_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_USPTP,
gen7_0_0_sp_cluster_sp_vs_pipe_bv_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_lpac_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_bv_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_bv_registers, 0xb000 },
};
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gen7_2_0_snapshot.h b/drivers/gpu/drm/msm/adreno/adreno_gen7_2_0_snapshot.h
index 772652eb61f3..7897622ea6f7 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gen7_2_0_snapshot.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gen7_2_0_snapshot.h
@@ -96,87 +96,87 @@ static const u32 gen7_2_0_debugbus_blocks[] = {
};
static const struct gen7_shader_block gen7_2_0_shader_blocks[] = {
- {A7XX_TP0_TMO_DATA, 0x200, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_TP0_SMO_DATA, 0x80, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_TP0_MIPMAP_BASE_DATA, 0x3c0, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_DATA_1, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_0_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_1_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_2_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_3_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_4_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_5_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_6_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_7_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_CB_RAM, 0x390, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_13_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_14_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_TAG, 0xc0, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_INST_DATA_2, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_TMO_TAG, 0x80, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_SMO_TAG, 0x80, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_STATE_DATA, 0x40, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_HWAVE_RAM, 0x100, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_L0_INST_BUF, 0x50, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_8_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_9_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_10_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_11_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_SP_LB_12_DATA, 0x800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP},
- {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM, 0x300, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM, 0x300, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM, 0x180, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM, 0x200, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM_1, 0x1c0, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM, 0x200, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x64, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x38, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x64, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x10, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INST_RAM_1, 0x800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_STPROC_META, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_DATAPATH_META, 0x20, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_FRONTEND_META, 0x80, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_FRONTEND_META, 0x80, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_FRONTEND_META, 0x80, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_INDIRECT_META, 0x10, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE},
- {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_TP0_TMO_DATA, 0x200, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_TP0_SMO_DATA, 0x80, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_TP0_MIPMAP_BASE_DATA, 0x3c0, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_DATA_1, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_0_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_1_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_2_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_3_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_4_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_5_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_6_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_7_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_CB_RAM, 0x390, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_13_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_14_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_TAG, 0xc0, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_INST_DATA_2, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_TMO_TAG, 0x80, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_SMO_TAG, 0x80, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_STATE_DATA, 0x40, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_HWAVE_RAM, 0x100, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_L0_INST_BUF, 0x50, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_8_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_9_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_10_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_11_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_SP_LB_12_DATA, 0x800, 6, 2, PIPE_BR, A7XX_USPTP},
+ {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x300, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM, 0x300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM, 0x1c0, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM, 0x300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM, 0x180, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x40, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x40, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x10, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM, 0x280, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM, 0x200, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM_1, 0x1c0, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM, 0x800, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM, 0x200, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x10, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_TAG, 0x80, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x64, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x38, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x64, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x10, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x800, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x800, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INST_RAM_1, 0x800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_STPROC_META, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BV_BE_META, 0x10, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_DATAPATH_META, 0x20, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_FRONTEND_META, 0x80, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_FRONTEND_META, 0x80, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_FRONTEND_META, 0x80, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_INDIRECT_META, 0x10, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, PIPE_BR, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, PIPE_BV, A7XX_HLSQ_STATE},
+ {A7XX_HLSQ_BACKEND_META, 0x40, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE},
};
static const u32 gen7_2_0_gpu_registers[] = {
@@ -478,182 +478,182 @@ static const u32 gen7_2_0_sp_noncontext_pipe_lpac_hlsq_state_registers[] = {
static_assert(IS_ALIGNED(sizeof(gen7_2_0_sp_noncontext_pipe_lpac_hlsq_state_registers), 8));
static const struct gen7_sel_reg gen7_2_0_rb_rac_sel = {
- .host_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_HOST,
- .cd_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD,
+ .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST,
+ .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD,
.val = 0x0,
};
static const struct gen7_sel_reg gen7_2_0_rb_rbp_sel = {
- .host_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_HOST,
- .cd_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD,
+ .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST,
+ .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD,
.val = 0x9,
};
static const struct gen7_cluster_registers gen7_2_0_clusters[] = {
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_2_0_noncontext_pipe_br_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BV, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BV, STATE_NON_CONTEXT,
gen7_2_0_noncontext_pipe_bv_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_LPAC, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_LPAC, STATE_NON_CONTEXT,
gen7_0_0_noncontext_pipe_lpac_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_2_0_noncontext_rb_rac_pipe_br_registers, &gen7_2_0_rb_rac_sel, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_2_0_noncontext_rb_rbp_pipe_br_registers, &gen7_2_0_rb_rbp_sel, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_2_0_gras_cluster_gras_pipe_br_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_GRAS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_2_0_gras_cluster_gras_pipe_bv_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_2_0_gras_cluster_gras_pipe_br_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_GRAS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_2_0_gras_cluster_gras_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_pc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_pc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_pc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_pc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_2_0_rb_rac_cluster_ps_pipe_br_registers, &gen7_2_0_rb_rac_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_2_0_rb_rac_cluster_ps_pipe_br_registers, &gen7_2_0_rb_rac_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers, &gen7_2_0_rb_rbp_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_rb_rbp_cluster_ps_pipe_br_registers, &gen7_2_0_rb_rbp_sel, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vfd_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vfd_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vfd_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vfd_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_fe_pipe_br_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_fe_pipe_bv_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PC_VS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_pc_vs_pipe_bv_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_pc_vs_pipe_br_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PC_VS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_pc_vs_pipe_bv_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_0_0_vpc_cluster_vpc_ps_pipe_bv_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_vpc_ps_pipe_br_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_0_0_vpc_cluster_vpc_ps_pipe_bv_registers, },
};
static const struct gen7_sptp_cluster_registers gen7_2_0_sptp_clusters[] = {
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_0_0_sp_noncontext_pipe_br_hlsq_state_registers, 0xae00 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_0_0_sp_noncontext_pipe_br_sp_top_registers, 0xae00 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_sp_noncontext_pipe_br_usptp_registers, 0xae00 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_HLSQ_STATE,
gen7_2_0_sp_noncontext_pipe_lpac_hlsq_state_registers, 0xaf80 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_SP_TOP,
gen7_0_0_sp_noncontext_pipe_lpac_sp_top_registers, 0xaf80 },
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_0_0_sp_noncontext_pipe_lpac_usptp_registers, 0xaf80 },
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_tpl1_noncontext_pipe_br_registers, 0xb600 },
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_NONE, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_NONE, 0, A7XX_USPTP,
gen7_0_0_tpl1_noncontext_pipe_none_registers, 0xb600 },
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_0_0_tpl1_noncontext_pipe_lpac_registers, 0xb780 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_2_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE,
gen7_2_0_sp_cluster_sp_ps_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_br_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_ps_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_HLSQ_STATE,
gen7_2_0_sp_cluster_sp_ps_pipe_lpac_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_HLSQ_DP,
gen7_0_0_sp_cluster_sp_ps_pipe_lpac_hlsq_dp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_ps_pipe_lpac_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_ps_pipe_lpac_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_2_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_HLSQ_STATE,
gen7_2_0_sp_cluster_sp_vs_pipe_bv_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_vs_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_vs_pipe_bv_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_vs_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_vs_pipe_bv_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE,
gen7_2_0_sp_cluster_sp_vs_pipe_br_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_HLSQ_STATE,
gen7_2_0_sp_cluster_sp_vs_pipe_bv_hlsq_state_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_vs_pipe_br_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_SP_TOP,
gen7_2_0_sp_cluster_sp_vs_pipe_bv_sp_top_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_vs_pipe_br_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_USPTP,
gen7_2_0_sp_cluster_sp_vs_pipe_bv_usptp_registers, 0xa800 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_TP0_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_ps_pipe_lpac_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_bv_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_br_registers, 0xb000 },
- { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_TP0_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_USPTP,
gen7_0_0_tpl1_cluster_sp_vs_pipe_bv_registers, 0xb000 },
};
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gen7_9_0_snapshot.h b/drivers/gpu/drm/msm/adreno/adreno_gen7_9_0_snapshot.h
index 0956dfca1f05..20125d1aa21d 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gen7_9_0_snapshot.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gen7_9_0_snapshot.h
@@ -118,97 +118,97 @@ static const u32 gen7_9_0_cx_debugbus_blocks[] = {
};
static const struct gen7_shader_block gen7_9_0_shader_blocks[] = {
- { A7XX_TP0_TMO_DATA, 0x0200, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_TP0_SMO_DATA, 0x0080, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_TP0_MIPMAP_BASE_DATA, 0x03C0, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_INST_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_INST_DATA_1, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_0_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_1_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_2_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_3_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_4_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_5_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_6_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_7_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_CB_RAM, 0x0390, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_13_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_14_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_INST_TAG, 0x00C0, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_INST_DATA_2, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_TMO_TAG, 0x0080, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_SMO_TAG, 0x0080, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_STATE_DATA, 0x0040, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_HWAVE_RAM, 0x0100, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_L0_INST_BUF, 0x0050, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_8_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_9_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_10_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_11_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_SP_LB_12_DATA, 0x0800, 6, 2, A7XX_PIPE_BR, A7XX_USPTP },
- { A7XX_HLSQ_DATAPATH_DSTR_META, 0x0010, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_DATAPATH_DSTR_META, 0x0010, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_L2STC_TAG_RAM, 0x0200, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_L2STC_INFO_CMD, 0x0474, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x0080, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x0080, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG, 0x0080, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x0400, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x0400, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM, 0x0400, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CVS_RAM, 0x01C0, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CVS_RAM, 0x01C0, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CPS_RAM, 0x0300, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CPS_RAM, 0x0180, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x0040, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x0040, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x0040, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x0040, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x0010, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x0010, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x0010, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x0010, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CVS_MISC_RAM, 0x0540, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CVS_MISC_RAM, 0x0540, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CPS_MISC_RAM, 0x0640, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CPS_MISC_RAM, 0x00B0, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CPS_MISC_RAM_1, 0x0800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM, 0x0800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM, 0x0800, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM, 0x0200, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x0800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x0800, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x0800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x0800, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x0050, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x0050, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x0050, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x0008, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM_TAG, 0x0014, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM_TAG, 0x0010, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM_TAG, 0x0004, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x0040, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x0040, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x0040, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x0020, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_LOCAL_MISC_RAM, 0x03C0, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_LOCAL_MISC_RAM, 0x0280, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_LOCAL_MISC_RAM, 0x0050, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_LOCAL_MISC_RAM_TAG, 0x0010, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_GFX_LOCAL_MISC_RAM_TAG, 0x0008, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM_1, 0x0800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_STPROC_META, 0x0010, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_BV_BE_META, 0x0018, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_BV_BE_META, 0x0018, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INST_RAM_2, 0x0800, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_DATAPATH_META, 0x0020, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_FRONTEND_META, 0x0080, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_FRONTEND_META, 0x0080, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_FRONTEND_META, 0x0080, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_INDIRECT_META, 0x0010, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_BACKEND_META, 0x0040, 1, 1, A7XX_PIPE_BR, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_BACKEND_META, 0x0040, 1, 1, A7XX_PIPE_BV, A7XX_HLSQ_STATE },
- { A7XX_HLSQ_BACKEND_META, 0x0040, 1, 1, A7XX_PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_TP0_TMO_DATA, 0x0200, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_TP0_SMO_DATA, 0x0080, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_TP0_MIPMAP_BASE_DATA, 0x03C0, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_INST_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_INST_DATA_1, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_0_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_1_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_2_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_3_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_4_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_5_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_6_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_7_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_CB_RAM, 0x0390, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_13_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_14_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_INST_TAG, 0x00C0, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_INST_DATA_2, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_TMO_TAG, 0x0080, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_SMO_TAG, 0x0080, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_STATE_DATA, 0x0040, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_HWAVE_RAM, 0x0100, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_L0_INST_BUF, 0x0050, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_8_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_9_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_10_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_11_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_SP_LB_12_DATA, 0x0800, 6, 2, PIPE_BR, A7XX_USPTP },
+ { A7XX_HLSQ_DATAPATH_DSTR_META, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_DATAPATH_DSTR_META, 0x0010, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_L2STC_TAG_RAM, 0x0200, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_L2STC_INFO_CMD, 0x0474, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x0080, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG, 0x0080, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG, 0x0080, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x0400, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM, 0x0400, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM, 0x0400, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CVS_RAM, 0x01C0, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CVS_RAM, 0x01C0, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CPS_RAM, 0x0300, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CPS_RAM, 0x0180, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CVS_RAM_TAG, 0x0040, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CHUNK_CPS_RAM_TAG, 0x0040, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_ICB_CVS_CB_BASE_TAG, 0x0010, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_ICB_CPS_CB_BASE_TAG, 0x0010, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CVS_MISC_RAM, 0x0540, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CVS_MISC_RAM, 0x0540, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CPS_MISC_RAM, 0x0640, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CPS_MISC_RAM, 0x00B0, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CPS_MISC_RAM_1, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM, 0x0800, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM, 0x0200, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CVS_CONST_RAM, 0x0800, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CPS_CONST_RAM, 0x0800, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x0050, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CVS_MISC_RAM_TAG, 0x0050, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x0050, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_CPS_MISC_RAM_TAG, 0x0008, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM_TAG, 0x0014, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM_TAG, 0x0010, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM_TAG, 0x0004, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CVS_CONST_RAM_TAG, 0x0040, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_CPS_CONST_RAM_TAG, 0x0020, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_LOCAL_MISC_RAM, 0x03C0, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_LOCAL_MISC_RAM, 0x0280, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_LOCAL_MISC_RAM, 0x0050, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_LOCAL_MISC_RAM_TAG, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_GFX_LOCAL_MISC_RAM_TAG, 0x0008, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM_1, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_STPROC_META, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_BV_BE_META, 0x0018, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_BV_BE_META, 0x0018, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INST_RAM_2, 0x0800, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_DATAPATH_META, 0x0020, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_FRONTEND_META, 0x0080, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_FRONTEND_META, 0x0080, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_FRONTEND_META, 0x0080, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_INDIRECT_META, 0x0010, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_BACKEND_META, 0x0040, 1, 1, PIPE_BR, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_BACKEND_META, 0x0040, 1, 1, PIPE_BV, A7XX_HLSQ_STATE },
+ { A7XX_HLSQ_BACKEND_META, 0x0040, 1, 1, PIPE_LPAC, A7XX_HLSQ_STATE },
};
/*
@@ -226,7 +226,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_pre_crashdumper_gpu_registers), 8));
* Block : ['BROADCAST', 'CP', 'GRAS', 'GXCLKCTL']
* Block : ['PC', 'RBBM', 'RDVM', 'UCHE']
* Block : ['VFD', 'VPC', 'VSC']
- * Pipeline: A7XX_PIPE_NONE
+ * Pipeline: PIPE_NONE
* pairs : 196 (Regs:1778)
*/
static const u32 gen7_9_0_gpu_registers[] = {
@@ -290,7 +290,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_gxclkctl_registers), 8));
/*
* Block : ['GMUAO', 'GMUCX', 'GMUCX_RAM']
- * Pipeline: A7XX_PIPE_NONE
+ * Pipeline: PIPE_NONE
* pairs : 134 (Regs:429)
*/
static const u32 gen7_9_0_gmu_registers[] = {
@@ -334,7 +334,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_gmu_registers), 8));
/*
* Block : ['GMUGX']
- * Pipeline: A7XX_PIPE_NONE
+ * Pipeline: PIPE_NONE
* pairs : 44 (Regs:454)
*/
static const u32 gen7_9_0_gmugx_registers[] = {
@@ -355,7 +355,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_gmugx_registers), 8));
/*
* Block : ['CX_MISC']
- * Pipeline: A7XX_PIPE_NONE
+ * Pipeline: PIPE_NONE
* pairs : 7 (Regs:56)
*/
static const u32 gen7_9_0_cx_misc_registers[] = {
@@ -367,7 +367,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_cx_misc_registers), 8));
/*
* Block : ['DBGC']
- * Pipeline: A7XX_PIPE_NONE
+ * Pipeline: PIPE_NONE
* pairs : 19 (Regs:155)
*/
static const u32 gen7_9_0_dbgc_registers[] = {
@@ -382,7 +382,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_dbgc_registers), 8));
/*
* Block : ['CX_DBGC']
- * Pipeline: A7XX_PIPE_NONE
+ * Pipeline: PIPE_NONE
* pairs : 7 (Regs:75)
*/
static const u32 gen7_9_0_cx_dbgc_registers[] = {
@@ -396,7 +396,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_cx_dbgc_registers), 8));
* Block : ['BROADCAST', 'CP', 'CX_DBGC', 'CX_MISC', 'DBGC', 'GBIF']
* Block : ['GMUAO', 'GMUCX', 'GMUGX', 'GRAS', 'GXCLKCTL', 'PC']
* Block : ['RBBM', 'RDVM', 'UCHE', 'VFD', 'VPC', 'VSC']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* pairs : 29 (Regs:573)
*/
@@ -417,7 +417,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_pipe_br_registers), 8));
* Block : ['BROADCAST', 'CP', 'CX_DBGC', 'CX_MISC', 'DBGC', 'GBIF']
* Block : ['GMUAO', 'GMUCX', 'GMUGX', 'GRAS', 'GXCLKCTL', 'PC']
* Block : ['RBBM', 'RDVM', 'UCHE', 'VFD', 'VPC', 'VSC']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_NONE
* pairs : 29 (Regs:573)
*/
@@ -438,7 +438,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_pipe_bv_registers), 8));
* Block : ['BROADCAST', 'CP', 'CX_DBGC', 'CX_MISC', 'DBGC', 'GBIF']
* Block : ['GMUAO', 'GMUCX', 'GMUGX', 'GRAS', 'GXCLKCTL', 'PC']
* Block : ['RBBM', 'RDVM', 'UCHE', 'VFD', 'VPC', 'VSC']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_NONE
* pairs : 2 (Regs:7)
*/
@@ -450,7 +450,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_pipe_lpac_registers), 8));
/*
* Block : ['RB']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* pairs : 5 (Regs:37)
*/
@@ -463,7 +463,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_rb_pipe_br_rac_registers),
/*
* Block : ['RB']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* pairs : 15 (Regs:66)
*/
@@ -478,7 +478,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_rb_pipe_br_rbp_registers),
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_HLSQ_STATE
* pairs : 4 (Regs:28)
@@ -491,7 +491,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_sp_pipe_br_hlsq_state_regis
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_SP_TOP
* pairs : 10 (Regs:61)
@@ -506,7 +506,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_sp_pipe_br_sp_top_registers
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_USPTP
* pairs : 12 (Regs:62)
@@ -521,7 +521,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_sp_pipe_br_usptp_registers)
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_HLSQ_DP_STR
* pairs : 2 (Regs:5)
@@ -534,7 +534,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_sp_pipe_br_hlsq_dp_str_regi
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_HLSQ_STATE
* pairs : 1 (Regs:5)
@@ -547,7 +547,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_sp_pipe_lpac_hlsq_state_reg
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_SP_TOP
* pairs : 1 (Regs:6)
@@ -560,7 +560,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_sp_pipe_lpac_sp_top_registe
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_USPTP
* pairs : 2 (Regs:9)
@@ -573,7 +573,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_sp_pipe_lpac_usptp_register
/*
* Block : ['TPL1']
- * Pipeline: A7XX_PIPE_NONE
+ * Pipeline: PIPE_NONE
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_USPTP
* pairs : 5 (Regs:29)
@@ -587,7 +587,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_tpl1_pipe_none_usptp_regist
/*
* Block : ['TPL1']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_USPTP
* pairs : 1 (Regs:1)
@@ -600,7 +600,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_tpl1_pipe_br_usptp_register
/*
* Block : ['TPL1']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_NONE
* Location: A7XX_USPTP
* pairs : 1 (Regs:1)
@@ -613,7 +613,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_non_context_tpl1_pipe_lpac_usptp_regist
/*
* Block : ['GRAS']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_GRAS
* pairs : 14 (Regs:293)
*/
@@ -628,7 +628,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_gras_pipe_br_cluster_gras_registers), 8
/*
* Block : ['GRAS']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_GRAS
* pairs : 14 (Regs:293)
*/
@@ -643,7 +643,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_gras_pipe_bv_cluster_gras_registers), 8
/*
* Block : ['PC']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_FE
* pairs : 6 (Regs:31)
*/
@@ -656,7 +656,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_pc_pipe_br_cluster_fe_registers), 8));
/*
* Block : ['PC']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_FE
* pairs : 6 (Regs:31)
*/
@@ -669,7 +669,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_pc_pipe_bv_cluster_fe_registers), 8));
/*
* Block : ['VFD']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_FE
* pairs : 2 (Regs:236)
*/
@@ -681,7 +681,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vfd_pipe_br_cluster_fe_registers), 8));
/*
* Block : ['VFD']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_FE
* pairs : 2 (Regs:236)
*/
@@ -693,7 +693,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vfd_pipe_bv_cluster_fe_registers), 8));
/*
* Block : ['VPC']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_FE
* pairs : 2 (Regs:18)
*/
@@ -705,7 +705,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vpc_pipe_br_cluster_fe_registers), 8));
/*
* Block : ['VPC']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_PC_VS
* pairs : 3 (Regs:30)
*/
@@ -717,7 +717,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vpc_pipe_br_cluster_pc_vs_registers), 8
/*
* Block : ['VPC']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_VPC_PS
* pairs : 5 (Regs:76)
*/
@@ -730,7 +730,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vpc_pipe_br_cluster_vpc_ps_registers),
/*
* Block : ['VPC']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_FE
* pairs : 2 (Regs:18)
*/
@@ -742,7 +742,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vpc_pipe_bv_cluster_fe_registers), 8));
/*
* Block : ['VPC']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_PC_VS
* pairs : 3 (Regs:30)
*/
@@ -754,7 +754,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vpc_pipe_bv_cluster_pc_vs_registers), 8
/*
* Block : ['VPC']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_VPC_PS
* pairs : 5 (Regs:76)
*/
@@ -767,7 +767,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_vpc_pipe_bv_cluster_vpc_ps_registers),
/*
* Block : ['RB']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_PS
* pairs : 39 (Regs:133)
*/
@@ -788,7 +788,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_rb_pipe_br_cluster_ps_rac_registers), 8
/*
* Block : ['RB']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_PS
* pairs : 34 (Regs:100)
*/
@@ -808,7 +808,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_rb_pipe_br_cluster_ps_rbp_registers), 8
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_HLSQ_STATE
* pairs : 29 (Regs:215)
@@ -828,7 +828,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_vs_hlsq_state_reg
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_SP_TOP
* pairs : 22 (Regs:73)
@@ -846,7 +846,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_vs_sp_top_registe
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_USPTP
* pairs : 16 (Regs:269)
@@ -862,7 +862,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_vs_usptp_register
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_HLSQ_STATE
* pairs : 21 (Regs:334)
@@ -880,7 +880,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_state_reg
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_HLSQ_DP
* pairs : 3 (Regs:19)
@@ -893,7 +893,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_regist
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_SP_TOP
* pairs : 18 (Regs:77)
@@ -910,7 +910,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_ps_sp_top_registe
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_USPTP
* pairs : 17 (Regs:333)
@@ -927,7 +927,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_ps_usptp_register
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_HLSQ_DP_STR
* pairs : 1 (Regs:6)
@@ -940,7 +940,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_str_re
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_HLSQ_STATE
* pairs : 28 (Regs:213)
@@ -959,7 +959,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_bv_cluster_sp_vs_hlsq_state_reg
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_SP_TOP
* pairs : 21 (Regs:71)
@@ -977,7 +977,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_bv_cluster_sp_vs_sp_top_registe
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_USPTP
* pairs : 16 (Regs:266)
@@ -993,7 +993,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_bv_cluster_sp_vs_usptp_register
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_HLSQ_STATE
* pairs : 14 (Regs:299)
@@ -1009,7 +1009,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_lpac_cluster_sp_ps_hlsq_state_r
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_HLSQ_DP
* pairs : 2 (Regs:13)
@@ -1022,7 +1022,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_lpac_cluster_sp_ps_hlsq_dp_regi
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_SP_TOP
* pairs : 9 (Regs:34)
@@ -1037,7 +1037,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_lpac_cluster_sp_ps_sp_top_regis
/*
* Block : ['SP']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_USPTP
* pairs : 11 (Regs:279)
@@ -1052,7 +1052,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_sp_pipe_lpac_cluster_sp_ps_usptp_regist
/*
* Block : ['TPL1']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_USPTP
* pairs : 3 (Regs:10)
@@ -1065,7 +1065,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_tpl1_pipe_br_cluster_sp_vs_usptp_regist
/*
* Block : ['TPL1']
- * Pipeline: A7XX_PIPE_BR
+ * Pipeline: PIPE_BR
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_USPTP
* pairs : 6 (Regs:42)
@@ -1079,7 +1079,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_tpl1_pipe_br_cluster_sp_ps_usptp_regist
/*
* Block : ['TPL1']
- * Pipeline: A7XX_PIPE_BV
+ * Pipeline: PIPE_BV
* Cluster : A7XX_CLUSTER_SP_VS
* Location: A7XX_USPTP
* pairs : 3 (Regs:10)
@@ -1092,7 +1092,7 @@ static_assert(IS_ALIGNED(sizeof(gen7_9_0_tpl1_pipe_bv_cluster_sp_vs_usptp_regist
/*
* Block : ['TPL1']
- * Pipeline: A7XX_PIPE_LPAC
+ * Pipeline: PIPE_LPAC
* Cluster : A7XX_CLUSTER_SP_PS
* Location: A7XX_USPTP
* pairs : 5 (Regs:7)
@@ -1105,192 +1105,192 @@ static const u32 gen7_9_0_tpl1_pipe_lpac_cluster_sp_ps_usptp_registers[] = {
static_assert(IS_ALIGNED(sizeof(gen7_9_0_tpl1_pipe_lpac_cluster_sp_ps_usptp_registers), 8));
static const struct gen7_sel_reg gen7_9_0_rb_rac_sel = {
- .host_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_HOST,
- .cd_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD,
+ .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST,
+ .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD,
.val = 0,
};
static const struct gen7_sel_reg gen7_9_0_rb_rbp_sel = {
- .host_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_HOST,
- .cd_reg = REG_A6XX_RB_RB_SUB_BLOCK_SEL_CNTL_CD,
+ .host_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_HOST,
+ .cd_reg = REG_A6XX_RB_SUB_BLOCK_SEL_CNTL_CD,
.val = 0x9,
};
static const struct gen7_cluster_registers gen7_9_0_clusters[] = {
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_9_0_non_context_pipe_br_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BV, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BV, STATE_NON_CONTEXT,
gen7_9_0_non_context_pipe_bv_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_LPAC, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_LPAC, STATE_NON_CONTEXT,
gen7_9_0_non_context_pipe_lpac_registers, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_9_0_non_context_rb_pipe_br_rac_registers, &gen7_9_0_rb_rac_sel, },
- { A7XX_CLUSTER_NONE, A7XX_PIPE_BR, STATE_NON_CONTEXT,
+ { A7XX_CLUSTER_NONE, PIPE_BR, STATE_NON_CONTEXT,
gen7_9_0_non_context_rb_pipe_br_rbp_registers, &gen7_9_0_rb_rbp_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_rb_pipe_br_cluster_ps_rac_registers, &gen7_9_0_rb_rac_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_rb_pipe_br_cluster_ps_rac_registers, &gen7_9_0_rb_rac_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_rb_pipe_br_cluster_ps_rbp_registers, &gen7_9_0_rb_rbp_sel, },
- { A7XX_CLUSTER_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_rb_pipe_br_cluster_ps_rbp_registers, &gen7_9_0_rb_rbp_sel, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_gras_pipe_br_cluster_gras_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_GRAS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_gras_pipe_br_cluster_gras_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_GRAS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_9_0_gras_pipe_bv_cluster_gras_registers, },
- { A7XX_CLUSTER_GRAS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_GRAS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_9_0_gras_pipe_bv_cluster_gras_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_pc_pipe_br_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_pc_pipe_br_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_9_0_pc_pipe_bv_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_9_0_pc_pipe_bv_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_vfd_pipe_br_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_vfd_pipe_br_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_9_0_vfd_pipe_bv_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_9_0_vfd_pipe_bv_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_vpc_pipe_br_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_vpc_pipe_br_cluster_fe_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_vpc_pipe_br_cluster_pc_vs_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PC_VS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_vpc_pipe_br_cluster_pc_vs_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_0,
gen7_9_0_vpc_pipe_br_cluster_vpc_ps_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BR, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BR, STATE_FORCE_CTXT_1,
gen7_9_0_vpc_pipe_br_cluster_vpc_ps_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_9_0_vpc_pipe_bv_cluster_fe_registers, },
- { A7XX_CLUSTER_FE, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_FE, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_9_0_vpc_pipe_bv_cluster_fe_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_PC_VS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_9_0_vpc_pipe_bv_cluster_pc_vs_registers, },
- { A7XX_CLUSTER_PC_VS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_PC_VS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_9_0_vpc_pipe_bv_cluster_pc_vs_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BV, STATE_FORCE_CTXT_0,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BV, STATE_FORCE_CTXT_0,
gen7_9_0_vpc_pipe_bv_cluster_vpc_ps_registers, },
- { A7XX_CLUSTER_VPC_PS, A7XX_PIPE_BV, STATE_FORCE_CTXT_1,
+ { A7XX_CLUSTER_VPC_PS, PIPE_BV, STATE_FORCE_CTXT_1,
gen7_9_0_vpc_pipe_bv_cluster_vpc_ps_registers, },
};
static const struct gen7_sptp_cluster_registers gen7_9_0_sptp_clusters[] = {
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_9_0_non_context_sp_pipe_br_hlsq_state_registers, 0xae00},
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_9_0_non_context_sp_pipe_br_sp_top_registers, 0xae00},
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_9_0_non_context_sp_pipe_br_usptp_registers, 0xae00},
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_DP_STR,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_BR, 0, A7XX_HLSQ_DP_STR,
gen7_9_0_non_context_sp_pipe_br_hlsq_dp_str_registers, 0xae00},
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_HLSQ_STATE,
gen7_9_0_non_context_sp_pipe_lpac_hlsq_state_registers, 0xaf80},
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_SP_TOP,
gen7_9_0_non_context_sp_pipe_lpac_sp_top_registers, 0xaf80},
- { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_SP_NCTX_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_9_0_non_context_sp_pipe_lpac_usptp_registers, 0xaf80},
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_NONE, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_NONE, 0, A7XX_USPTP,
gen7_9_0_non_context_tpl1_pipe_none_usptp_registers, 0xb600},
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_9_0_non_context_tpl1_pipe_br_usptp_registers, 0xb600},
- { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_NONE, A7XX_TP0_NCTX_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_9_0_non_context_tpl1_pipe_lpac_usptp_registers, 0xb780},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_9_0_sp_pipe_br_cluster_sp_vs_hlsq_state_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_9_0_sp_pipe_br_cluster_sp_vs_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_9_0_sp_pipe_br_cluster_sp_vs_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_HLSQ_STATE,
gen7_9_0_sp_pipe_bv_cluster_sp_vs_hlsq_state_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_SP_TOP,
gen7_9_0_sp_pipe_bv_cluster_sp_vs_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_USPTP,
gen7_9_0_sp_pipe_bv_cluster_sp_vs_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE,
gen7_9_0_sp_pipe_br_cluster_sp_vs_hlsq_state_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_SP_TOP,
gen7_9_0_sp_pipe_br_cluster_sp_vs_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_9_0_sp_pipe_br_cluster_sp_vs_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_HLSQ_STATE,
gen7_9_0_sp_pipe_bv_cluster_sp_vs_hlsq_state_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_SP_TOP,
gen7_9_0_sp_pipe_bv_cluster_sp_vs_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_USPTP,
gen7_9_0_sp_pipe_bv_cluster_sp_vs_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_STATE,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_state_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_DP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_SP_TOP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_HLSQ_DP_STR,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_HLSQ_DP_STR,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_str_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_HLSQ_STATE,
gen7_9_0_sp_pipe_lpac_cluster_sp_ps_hlsq_state_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_HLSQ_DP,
gen7_9_0_sp_pipe_lpac_cluster_sp_ps_hlsq_dp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_SP_TOP,
gen7_9_0_sp_pipe_lpac_cluster_sp_ps_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_9_0_sp_pipe_lpac_cluster_sp_ps_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_STATE,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_STATE,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_state_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_DP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_SP_TOP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_HLSQ_DP_STR,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_HLSQ_DP_STR,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_str_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_HLSQ_DP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_SP_TOP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_HLSQ_DP_STR,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_HLSQ_DP_STR,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_str_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_HLSQ_DP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_HLSQ_DP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_SP_TOP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_SP_TOP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_sp_top_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP,
gen7_9_0_sp_pipe_br_cluster_sp_ps_usptp_registers, 0xa800},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_HLSQ_DP_STR,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_HLSQ_DP_STR,
gen7_9_0_sp_pipe_br_cluster_sp_ps_hlsq_dp_str_registers, 0xa800},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_9_0_tpl1_pipe_br_cluster_sp_vs_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, A7XX_PIPE_BV, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX0_3D_CVS_REG, PIPE_BV, 0, A7XX_USPTP,
gen7_9_0_tpl1_pipe_bv_cluster_sp_vs_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_9_0_tpl1_pipe_br_cluster_sp_vs_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, A7XX_PIPE_BV, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_VS, A7XX_SP_CTX1_3D_CVS_REG, PIPE_BV, 1, A7XX_USPTP,
gen7_9_0_tpl1_pipe_bv_cluster_sp_vs_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_BR, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_BR, 0, A7XX_USPTP,
gen7_9_0_tpl1_pipe_br_cluster_sp_ps_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, A7XX_PIPE_LPAC, 0, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX0_3D_CPS_REG, PIPE_LPAC, 0, A7XX_USPTP,
gen7_9_0_tpl1_pipe_lpac_cluster_sp_ps_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, A7XX_PIPE_BR, 1, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX1_3D_CPS_REG, PIPE_BR, 1, A7XX_USPTP,
gen7_9_0_tpl1_pipe_br_cluster_sp_ps_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, A7XX_PIPE_BR, 2, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX2_3D_CPS_REG, PIPE_BR, 2, A7XX_USPTP,
gen7_9_0_tpl1_pipe_br_cluster_sp_ps_usptp_registers, 0xb000},
- { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, A7XX_PIPE_BR, 3, A7XX_USPTP,
+ { A7XX_CLUSTER_SP_PS, A7XX_SP_CTX3_3D_CPS_REG, PIPE_BR, 3, A7XX_USPTP,
gen7_9_0_tpl1_pipe_br_cluster_sp_ps_usptp_registers, 0xb000},
};
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index afaa3cfefd35..1c80909e63ca 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -284,6 +284,7 @@ int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags,
struct adreno_smmu_fault_info *info, const char *block,
u32 scratch[4])
{
+ struct adreno_gpu *adreno_gpu = container_of(gpu, struct adreno_gpu, base);
struct msm_drm_private *priv = gpu->dev->dev_private;
struct msm_mmu *mmu = to_msm_vm(gpu->vm)->mmu;
const char *type = "UNKNOWN";
@@ -336,6 +337,11 @@ int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags,
/* Turn off the hangcheck timer to keep it from bothering us */
timer_delete(&gpu->hangcheck_timer);
+ /* Let any concurrent GMU transactions know that the MMU may be
+ * blocked for a while and they should wait on us.
+ */
+ reinit_completion(&adreno_gpu->fault_coredump_done);
+
fault_info.ttbr0 = info->ttbr0;
fault_info.iova = iova;
fault_info.flags = flags;
@@ -343,18 +349,13 @@ int adreno_fault_handler(struct msm_gpu *gpu, unsigned long iova, int flags,
fault_info.block = block;
msm_gpu_fault_crashstate_capture(gpu, &fault_info);
+
+ complete_all(&adreno_gpu->fault_coredump_done);
}
return 0;
}
-static bool
-adreno_smmu_has_prr(struct msm_gpu *gpu)
-{
- struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(&gpu->pdev->dev);
- return adreno_smmu && adreno_smmu->set_prr_addr;
-}
-
int adreno_get_param(struct msm_gpu *gpu, struct msm_context *ctx,
uint32_t param, uint64_t *value, uint32_t *len)
{
@@ -1196,6 +1197,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
/* Only handle the core clock when GMU is not in use (or is absent). */
if (adreno_has_gmu_wrapper(adreno_gpu) ||
+ adreno_has_rgmu(adreno_gpu) ||
adreno_gpu->info->family < ADRENO_6XX_GEN1) {
/*
* This can only be done before devm_pm_opp_of_add_table(), or
@@ -1229,6 +1231,9 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev,
if (ret)
return ret;
+ init_completion(&adreno_gpu->fault_coredump_done);
+ complete_all(&adreno_gpu->fault_coredump_done);
+
pm_runtime_set_autosuspend_delay(dev,
adreno_gpu->info->inactive_period);
pm_runtime_use_autosuspend(dev);
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 390fa6720d9b..0f8d3de97636 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -27,6 +27,7 @@ enum {
ADRENO_FW_PFP = 1,
ADRENO_FW_GMU = 1, /* a6xx */
ADRENO_FW_GPMU = 2,
+ ADRENO_FW_AQE = 3,
ADRENO_FW_MAX,
};
@@ -50,6 +51,8 @@ enum adreno_family {
ADRENO_7XX_GEN1, /* a730 family */
ADRENO_7XX_GEN2, /* a740 family */
ADRENO_7XX_GEN3, /* a750 family */
+ ADRENO_8XX_GEN1, /* a830 family */
+ ADRENO_8XX_GEN2, /* a840 family */
};
#define ADRENO_QUIRK_TWO_PASS_USE_WFI BIT(0)
@@ -71,9 +74,14 @@ enum adreno_family {
(((_c) >> 8) & 0xff), \
((_c) & 0xff)
+struct adreno_gpu;
+
struct adreno_gpu_funcs {
struct msm_gpu_funcs base;
+ struct msm_gpu *(*init)(struct drm_device *dev);
int (*get_timestamp)(struct msm_gpu *gpu, uint64_t *value);
+ void (*bus_halt)(struct adreno_gpu *adreno_gpu, bool gx_off);
+ int (*mmu_fault_handler)(void *arg, unsigned long iova, int flags, void *data);
};
struct adreno_reglist {
@@ -81,6 +89,13 @@ struct adreno_reglist {
u32 value;
};
+/* Reglist with pipe information */
+struct adreno_reglist_pipe {
+ u32 offset;
+ u32 value;
+ u32 pipe;
+};
+
struct adreno_speedbin {
uint16_t fuse;
uint16_t speedbin;
@@ -101,7 +116,7 @@ struct adreno_info {
const char *fw[ADRENO_FW_MAX];
uint32_t gmem;
u64 quirks;
- struct msm_gpu *(*init)(struct drm_device *dev);
+ const struct adreno_gpu_funcs *funcs;
const char *zapfw;
u32 inactive_period;
union {
@@ -180,6 +195,8 @@ struct adreno_gpu {
uint16_t speedbin;
const struct adreno_gpu_funcs *funcs;
+ struct completion fault_coredump_done;
+
/* interesting register offsets to dump: */
const unsigned int *registers;
@@ -392,6 +409,16 @@ static inline int adreno_is_a610(const struct adreno_gpu *gpu)
return adreno_is_revn(gpu, 610);
}
+static inline int adreno_is_a612(const struct adreno_gpu *gpu)
+{
+ return gpu->info->chip_ids[0] == 0x06010200;
+}
+
+static inline bool adreno_has_rgmu(const struct adreno_gpu *gpu)
+{
+ return adreno_is_a612(gpu);
+}
+
static inline int adreno_is_a618(const struct adreno_gpu *gpu)
{
return adreno_is_revn(gpu, 618);
@@ -466,9 +493,9 @@ static inline int adreno_is_a610_family(const struct adreno_gpu *gpu)
{
if (WARN_ON_ONCE(!gpu->info))
return false;
-
- /* TODO: A612 */
- return adreno_is_a610(gpu) || adreno_is_a702(gpu);
+ return adreno_is_a610(gpu) ||
+ adreno_is_a612(gpu) ||
+ adreno_is_a702(gpu);
}
/* TODO: 615/616 */
@@ -548,6 +575,21 @@ static inline int adreno_is_a7xx(struct adreno_gpu *gpu)
adreno_is_a740_family(gpu);
}
+static inline int adreno_is_a8xx(struct adreno_gpu *gpu)
+{
+ return gpu->info->family >= ADRENO_8XX_GEN1;
+}
+
+static inline int adreno_is_x285(struct adreno_gpu *gpu)
+{
+ return gpu->info->chip_ids[0] == 0x44070001;
+}
+
+static inline int adreno_is_a840(struct adreno_gpu *gpu)
+{
+ return gpu->info->chip_ids[0] == 0x44050a01;
+}
+
/* Put vm_start above 32b to catch issues with not setting xyz_BASE_HI */
#define ADRENO_VM_START 0x100000000ULL
u64 adreno_private_vm_size(struct msm_gpu *gpu);
@@ -673,12 +715,6 @@ OUT_PKT7(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt)
OUT_RING(ring, PKT7(opcode, cnt));
}
-struct msm_gpu *a2xx_gpu_init(struct drm_device *dev);
-struct msm_gpu *a3xx_gpu_init(struct drm_device *dev);
-struct msm_gpu *a4xx_gpu_init(struct drm_device *dev);
-struct msm_gpu *a5xx_gpu_init(struct drm_device *dev);
-struct msm_gpu *a6xx_gpu_init(struct drm_device *dev);
-
static inline uint32_t get_wptr(struct msm_ringbuffer *ring)
{
return (ring->cur - ring->start) % (MSM_GPU_RINGBUFFER_SZ >> 2);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h
new file mode 100644
index 000000000000..13bb43ba67d3
--- /dev/null
+++ b/drivers/gpu/drm/msm/disp/dpu1/catalog/dpu_12_2_glymur.h
@@ -0,0 +1,541 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#ifndef _DPU_12_2_GLYMUR_H
+#define _DPU_12_2_GLYMUR_H
+
+static const struct dpu_caps glymur_dpu_caps = {
+ .max_mixer_width = DEFAULT_DPU_OUTPUT_LINE_WIDTH,
+ .max_mixer_blendstages = 0xb,
+ .has_src_split = true,
+ .has_dim_layer = true,
+ .has_idle_pc = true,
+ .has_3d_merge = true,
+ .max_linewidth = 8192,
+ .pixel_ram_size = DEFAULT_PIXEL_RAM_SIZE,
+};
+
+static const struct dpu_mdp_cfg glymur_mdp = {
+ .name = "top_0",
+ .base = 0, .len = 0x494,
+ .clk_ctrls = {
+ [DPU_CLK_CTRL_REG_DMA] = { .reg_off = 0x2bc, .bit_off = 20 },
+ },
+};
+
+static const struct dpu_ctl_cfg glymur_ctl[] = {
+ {
+ .name = "ctl_0", .id = CTL_0,
+ .base = 0x15000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 9),
+ }, {
+ .name = "ctl_1", .id = CTL_1,
+ .base = 0x16000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 10),
+ }, {
+ .name = "ctl_2", .id = CTL_2,
+ .base = 0x17000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 11),
+ }, {
+ .name = "ctl_3", .id = CTL_3,
+ .base = 0x18000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 12),
+ }, {
+ .name = "ctl_4", .id = CTL_4,
+ .base = 0x19000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 13),
+ }, {
+ .name = "ctl_5", .id = CTL_5,
+ .base = 0x1a000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 23),
+ }, {
+ .name = "ctl_6", .id = CTL_6,
+ .base = 0x1b000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 14),
+ }, {
+ .name = "ctl_7", .id = CTL_7,
+ .base = 0x1c000, .len = 0x1000,
+ .intr_start = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 15),
+ },
+};
+
+static const struct dpu_sspp_cfg glymur_sspp[] = {
+ {
+ .name = "sspp_0", .id = SSPP_VIG0,
+ .base = 0x4000, .len = 0x344,
+ .features = VIG_SDM845_MASK_SDMA,
+ .sblk = &dpu_vig_sblk_qseed3_3_4,
+ .xin_id = 0,
+ .type = SSPP_TYPE_VIG,
+ }, {
+ .name = "sspp_1", .id = SSPP_VIG1,
+ .base = 0x6000, .len = 0x344,
+ .features = VIG_SDM845_MASK_SDMA,
+ .sblk = &dpu_vig_sblk_qseed3_3_4,
+ .xin_id = 4,
+ .type = SSPP_TYPE_VIG,
+ }, {
+ .name = "sspp_2", .id = SSPP_VIG2,
+ .base = 0x8000, .len = 0x344,
+ .features = VIG_SDM845_MASK_SDMA,
+ .sblk = &dpu_vig_sblk_qseed3_3_4,
+ .xin_id = 8,
+ .type = SSPP_TYPE_VIG,
+ }, {
+ .name = "sspp_3", .id = SSPP_VIG3,
+ .base = 0xa000, .len = 0x344,
+ .features = VIG_SDM845_MASK_SDMA,
+ .sblk = &dpu_vig_sblk_qseed3_3_4,
+ .xin_id = 12,
+ .type = SSPP_TYPE_VIG,
+ }, {
+ .name = "sspp_8", .id = SSPP_DMA0,
+ .base = 0x24000, .len = 0x344,
+ .features = DMA_SDM845_MASK_SDMA,
+ .sblk = &dpu_dma_sblk,
+ .xin_id = 1,
+ .type = SSPP_TYPE_DMA,
+ }, {
+ .name = "sspp_9", .id = SSPP_DMA1,
+ .base = 0x26000, .len = 0x344,
+ .features = DMA_SDM845_MASK_SDMA,
+ .sblk = &dpu_dma_sblk,
+ .xin_id = 5,
+ .type = SSPP_TYPE_DMA,
+ }, {
+ .name = "sspp_10", .id = SSPP_DMA2,
+ .base = 0x28000, .len = 0x344,
+ .features = DMA_SDM845_MASK_SDMA,
+ .sblk = &dpu_dma_sblk,
+ .xin_id = 9,
+ .type = SSPP_TYPE_DMA,
+ }, {
+ .name = "sspp_11", .id = SSPP_DMA3,
+ .base = 0x2a000, .len = 0x344,
+ .features = DMA_SDM845_MASK_SDMA,
+ .sblk = &dpu_dma_sblk,
+ .xin_id = 13,
+ .type = SSPP_TYPE_DMA,
+ }, {
+ .name = "sspp_12", .id = SSPP_DMA4,
+ .base = 0x2c000, .len = 0x344,
+ .features = DMA_CURSOR_SDM845_MASK_SDMA,
+ .sblk = &dpu_dma_sblk,
+ .xin_id = 14,
+ .type = SSPP_TYPE_DMA,
+ }, {
+ .name = "sspp_13", .id = SSPP_DMA5,
+ .base = 0x2e000, .len = 0x344,
+ .features = DMA_CURSOR_SDM845_MASK_SDMA,
+ .sblk = &dpu_dma_sblk,
+ .xin_id = 15,
+ .type = SSPP_TYPE_DMA,
+ },
+};
+
+static const struct dpu_lm_cfg glymur_lm[] = {
+ {
+ .name = "lm_0", .id = LM_0,
+ .base = 0x44000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_1,
+ .pingpong = PINGPONG_0,
+ .dspp = DSPP_0,
+ }, {
+ .name = "lm_1", .id = LM_1,
+ .base = 0x45000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_0,
+ .pingpong = PINGPONG_1,
+ .dspp = DSPP_1,
+ }, {
+ .name = "lm_2", .id = LM_2,
+ .base = 0x46000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_3,
+ .pingpong = PINGPONG_2,
+ .dspp = DSPP_2,
+ }, {
+ .name = "lm_3", .id = LM_3,
+ .base = 0x47000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_2,
+ .pingpong = PINGPONG_3,
+ .dspp = DSPP_3,
+ }, {
+ .name = "lm_4", .id = LM_4,
+ .base = 0x48000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_5,
+ .pingpong = PINGPONG_4,
+ }, {
+ .name = "lm_5", .id = LM_5,
+ .base = 0x49000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_4,
+ .pingpong = PINGPONG_5,
+ }, {
+ .name = "lm_6", .id = LM_6,
+ .base = 0x4a000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_7,
+ .pingpong = PINGPONG_6,
+ }, {
+ .name = "lm_7", .id = LM_7,
+ .base = 0x4b000, .len = 0x400,
+ .features = MIXER_MSM8998_MASK,
+ .sblk = &sm8750_lm_sblk,
+ .lm_pair = LM_6,
+ .pingpong = PINGPONG_7,
+ },
+};
+
+static const struct dpu_dspp_cfg glymur_dspp[] = {
+ {
+ .name = "dspp_0", .id = DSPP_0,
+ .base = 0x54000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ }, {
+ .name = "dspp_1", .id = DSPP_1,
+ .base = 0x56000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ }, {
+ .name = "dspp_2", .id = DSPP_2,
+ .base = 0x58000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ }, {
+ .name = "dspp_3", .id = DSPP_3,
+ .base = 0x5a000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ }, {
+ .name = "dspp_4", .id = DSPP_4,
+ .base = 0x5c000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ }, {
+ .name = "dspp_5", .id = DSPP_5,
+ .base = 0x5e000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ }, {
+ .name = "dspp_6", .id = DSPP_6,
+ .base = 0x60000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ }, {
+ .name = "dspp_7", .id = DSPP_7,
+ .base = 0x62000, .len = 0x1800,
+ .sblk = &sm8750_dspp_sblk,
+ },
+};
+
+static const struct dpu_pingpong_cfg glymur_pp[] = {
+ {
+ .name = "pingpong_0", .id = PINGPONG_0,
+ .base = 0x69000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_0,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 8),
+ }, {
+ .name = "pingpong_1", .id = PINGPONG_1,
+ .base = 0x6a000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_0,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 9),
+ }, {
+ .name = "pingpong_2", .id = PINGPONG_2,
+ .base = 0x6b000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_1,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 10),
+ }, {
+ .name = "pingpong_3", .id = PINGPONG_3,
+ .base = 0x6c000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_1,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 11),
+ }, {
+ .name = "pingpong_4", .id = PINGPONG_4,
+ .base = 0x6d000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_2,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 30),
+ }, {
+ .name = "pingpong_5", .id = PINGPONG_5,
+ .base = 0x6e000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_2,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 31),
+ }, {
+ .name = "pingpong_6", .id = PINGPONG_6,
+ .base = 0x6f000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_3,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 20),
+ }, {
+ .name = "pingpong_7", .id = PINGPONG_7,
+ .base = 0x70000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_3,
+ .intr_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR2, 21),
+ }, {
+ .name = "pingpong_cwb_0", .id = PINGPONG_CWB_0,
+ .base = 0x66000, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_4,
+ }, {
+ .name = "pingpong_cwb_1", .id = PINGPONG_CWB_1,
+ .base = 0x66400, .len = 0,
+ .sblk = &sc7280_pp_sblk,
+ .merge_3d = MERGE_3D_4,
+ },
+};
+
+static const struct dpu_merge_3d_cfg glymur_merge_3d[] = {
+ {
+ .name = "merge_3d_0", .id = MERGE_3D_0,
+ .base = 0x4e000, .len = 0x1c,
+ }, {
+ .name = "merge_3d_1", .id = MERGE_3D_1,
+ .base = 0x4f000, .len = 0x1c,
+ }, {
+ .name = "merge_3d_2", .id = MERGE_3D_2,
+ .base = 0x50000, .len = 0x1c,
+ }, {
+ .name = "merge_3d_3", .id = MERGE_3D_3,
+ .base = 0x51000, .len = 0x1c,
+ },
+};
+
+/*
+ * NOTE: Each display compression engine (DCE) contains dual hard
+ * slice DSC encoders so both share same base address but with
+ * its own different sub block address.
+ */
+static const struct dpu_dsc_cfg glymur_dsc[] = {
+ {
+ .name = "dce_0_0", .id = DSC_0,
+ .base = 0x80000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_0,
+ }, {
+ .name = "dce_0_1", .id = DSC_1,
+ .base = 0x80000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_1,
+ }, {
+ .name = "dce_1_0", .id = DSC_2,
+ .base = 0x81000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_0,
+ }, {
+ .name = "dce_1_1", .id = DSC_3,
+ .base = 0x81000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_1,
+ }, {
+ .name = "dce_2_0", .id = DSC_4,
+ .base = 0x82000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_0,
+ }, {
+ .name = "dce_2_1", .id = DSC_5,
+ .base = 0x82000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_1,
+ }, {
+ .name = "dce_3_0", .id = DSC_6,
+ .base = 0x83000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_0,
+ }, {
+ .name = "dce_3_1", .id = DSC_7,
+ .base = 0x83000, .len = 0x8,
+ .features = BIT(DPU_DSC_NATIVE_42x_EN),
+ .sblk = &sm8750_dsc_sblk_1,
+ },
+
+};
+
+static const struct dpu_wb_cfg glymur_wb[] = {
+ {
+ .name = "wb_2", .id = WB_2,
+ .base = 0x65000, .len = 0x2c8,
+ .features = WB_SDM845_MASK,
+ .format_list = wb2_formats_rgb_yuv,
+ .num_formats = ARRAY_SIZE(wb2_formats_rgb_yuv),
+ .xin_id = 6,
+ .vbif_idx = VBIF_RT,
+ .maxlinewidth = 4096,
+ .intr_wb_done = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 4),
+ },
+};
+
+static const struct dpu_cwb_cfg glymur_cwb[] = {
+ {
+ .name = "cwb_0", .id = CWB_0,
+ .base = 0x66200, .len = 0x20,
+ },
+ {
+ .name = "cwb_1", .id = CWB_1,
+ .base = 0x66600, .len = 0x20,
+ },
+ {
+ .name = "cwb_2", .id = CWB_2,
+ .base = 0x7e200, .len = 0x20,
+ },
+ {
+ .name = "cwb_3", .id = CWB_3,
+ .base = 0x7e600, .len = 0x20,
+ },
+};
+
+static const struct dpu_intf_cfg glymur_intf[] = {
+ {
+ .name = "intf_0", .id = INTF_0,
+ .base = 0x34000, .len = 0x400,
+ .type = INTF_DP,
+ .controller_id = MSM_DP_CONTROLLER_0,
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 24),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 25),
+ }, {
+ .name = "intf_1", .id = INTF_1,
+ .base = 0x35000, .len = 0x400,
+ .type = INTF_DSI,
+ .controller_id = MSM_DSI_CONTROLLER_0,
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 26),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 27),
+ .intr_tear_rd_ptr = DPU_IRQ_IDX(MDP_INTF1_TEAR_INTR, 2),
+ }, {
+ .name = "intf_2", .id = INTF_2,
+ .base = 0x36000, .len = 0x400,
+ .type = INTF_DSI,
+ .controller_id = MSM_DSI_CONTROLLER_1,
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 28),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 29),
+ .intr_tear_rd_ptr = DPU_IRQ_IDX(MDP_INTF2_TEAR_INTR, 2),
+ }, {
+ .name = "intf_3", .id = INTF_3,
+ .base = 0x37000, .len = 0x400,
+ .type = INTF_NONE,
+ .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 30),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 31),
+ }, {
+ .name = "intf_4", .id = INTF_4,
+ .base = 0x38000, .len = 0x400,
+ .type = INTF_DP,
+ .controller_id = MSM_DP_CONTROLLER_1,
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 20),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 21),
+ }, {
+ .name = "intf_5", .id = INTF_5,
+ .base = 0x39000, .len = 0x400,
+ .type = INTF_DP,
+ .controller_id = MSM_DP_CONTROLLER_3,
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 22),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 23),
+ }, {
+ .name = "intf_6", .id = INTF_6,
+ .base = 0x3A000, .len = 0x400,
+ .type = INTF_DP,
+ .controller_id = MSM_DP_CONTROLLER_2,
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 17),
+ }, {
+ .name = "intf_7", .id = INTF_7,
+ .base = 0x3b000, .len = 0x400,
+ .type = INTF_NONE,
+ .controller_id = MSM_DP_CONTROLLER_2, /* pair with intf_6 for DP MST */
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 18),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 19),
+ }, {
+ .name = "intf_8", .id = INTF_8,
+ .base = 0x3c000, .len = 0x400,
+ .type = INTF_NONE,
+ .controller_id = MSM_DP_CONTROLLER_1, /* pair with intf_4 for DP MST */
+ .prog_fetch_lines_worst_case = 24,
+ .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 12),
+ .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 13),
+ },
+};
+
+static const struct dpu_perf_cfg glymur_perf_data = {
+ .max_bw_low = 18900000,
+ .max_bw_high = 28500000,
+ .min_core_ib = 2500000,
+ .min_llcc_ib = 0,
+ .min_dram_ib = 800000,
+ .min_prefill_lines = 35,
+ .danger_lut_tbl = {0x3ffff, 0x3ffff, 0x0},
+ .safe_lut_tbl = {0xfe00, 0xfe00, 0xffff},
+ .qos_lut_tbl = {
+ {.nentry = ARRAY_SIZE(sc7180_qos_linear),
+ .entries = sc7180_qos_linear
+ },
+ {.nentry = ARRAY_SIZE(sc7180_qos_macrotile),
+ .entries = sc7180_qos_macrotile
+ },
+ {.nentry = ARRAY_SIZE(sc7180_qos_nrt),
+ .entries = sc7180_qos_nrt
+ },
+ /* TODO: macrotile-qseed is different from macrotile */
+ },
+ .cdp_cfg = {
+ {.rd_enable = 1, .wr_enable = 1},
+ {.rd_enable = 1, .wr_enable = 0}
+ },
+ .clk_inefficiency_factor = 105,
+ .bw_inefficiency_factor = 120,
+};
+
+static const struct dpu_mdss_version glymur_mdss_ver = {
+ .core_major_ver = 12,
+ .core_minor_ver = 2,
+};
+
+const struct dpu_mdss_cfg dpu_glymur_cfg = {
+ .mdss_ver = &glymur_mdss_ver,
+ .caps = &glymur_dpu_caps,
+ .mdp = &glymur_mdp,
+ .cdm = &dpu_cdm_5_x,
+ .ctl_count = ARRAY_SIZE(glymur_ctl),
+ .ctl = glymur_ctl,
+ .sspp_count = ARRAY_SIZE(glymur_sspp),
+ .sspp = glymur_sspp,
+ .mixer_count = ARRAY_SIZE(glymur_lm),
+ .mixer = glymur_lm,
+ .dspp_count = ARRAY_SIZE(glymur_dspp),
+ .dspp = glymur_dspp,
+ .pingpong_count = ARRAY_SIZE(glymur_pp),
+ .pingpong = glymur_pp,
+ .dsc_count = ARRAY_SIZE(glymur_dsc),
+ .dsc = glymur_dsc,
+ .merge_3d_count = ARRAY_SIZE(glymur_merge_3d),
+ .merge_3d = glymur_merge_3d,
+ .wb_count = ARRAY_SIZE(glymur_wb),
+ .wb = glymur_wb,
+ .cwb_count = ARRAY_SIZE(glymur_cwb),
+ .cwb = sm8650_cwb,
+ .intf_count = ARRAY_SIZE(glymur_intf),
+ .intf = glymur_intf,
+ .vbif_count = ARRAY_SIZE(sm8650_vbif),
+ .vbif = sm8650_vbif,
+ .perf = &glymur_perf_data,
+};
+
+#endif
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
index 4b970a59deaf..c39f1908ea65 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
@@ -200,7 +200,7 @@ static int dpu_crtc_get_lm_crc(struct drm_crtc *crtc,
struct dpu_crtc_state *crtc_state)
{
struct dpu_crtc_mixer *m;
- u32 crcs[CRTC_DUAL_MIXERS];
+ u32 crcs[CRTC_QUAD_MIXERS];
int rc = 0;
int i;
@@ -400,7 +400,7 @@ static void _dpu_crtc_program_lm_output_roi(struct drm_crtc *crtc)
static void _dpu_crtc_blend_setup_pipe(struct drm_crtc *crtc,
struct drm_plane *plane,
struct dpu_crtc_mixer *mixer,
- u32 num_mixers,
+ u32 lms_in_pair,
enum dpu_stage stage,
const struct msm_format *format,
uint64_t modifier,
@@ -419,7 +419,7 @@ static void _dpu_crtc_blend_setup_pipe(struct drm_crtc *crtc,
trace_dpu_crtc_setup_mixer(DRMID(crtc), DRMID(plane),
state, to_dpu_plane_state(state), stage_idx,
- format->pixel_format,
+ format->pixel_format, pipe,
modifier);
DRM_DEBUG_ATOMIC("crtc %d stage:%d - plane %d sspp %d fb %d multirect_idx %d\n",
@@ -434,7 +434,7 @@ static void _dpu_crtc_blend_setup_pipe(struct drm_crtc *crtc,
stage_cfg->multirect_index[stage][stage_idx] = pipe->multirect_index;
/* blend config update */
- for (lm_idx = 0; lm_idx < num_mixers; lm_idx++)
+ for (lm_idx = 0; lm_idx < lms_in_pair; lm_idx++)
mixer[lm_idx].lm_ctl->ops.update_pending_flush_sspp(mixer[lm_idx].lm_ctl, sspp_idx);
}
@@ -449,7 +449,7 @@ static void _dpu_crtc_blend_setup_mixer(struct drm_crtc *crtc,
struct dpu_plane_state *pstate = NULL;
const struct msm_format *format;
struct dpu_hw_ctl *ctl = mixer->lm_ctl;
- u32 lm_idx;
+ u32 lm_idx, stage, i, pipe_idx, head_pipe_in_stage, lms_in_pair;
bool bg_alpha_enable = false;
DECLARE_BITMAP(active_fetch, SSPP_MAX);
DECLARE_BITMAP(active_pipes, SSPP_MAX);
@@ -472,22 +472,25 @@ static void _dpu_crtc_blend_setup_mixer(struct drm_crtc *crtc,
if (pstate->stage == DPU_STAGE_BASE && format->alpha_enable)
bg_alpha_enable = true;
- set_bit(pstate->pipe.sspp->idx, active_fetch);
- set_bit(pstate->pipe.sspp->idx, active_pipes);
- _dpu_crtc_blend_setup_pipe(crtc, plane,
- mixer, cstate->num_mixers,
- pstate->stage,
- format, fb ? fb->modifier : 0,
- &pstate->pipe, 0, stage_cfg);
-
- if (pstate->r_pipe.sspp) {
- set_bit(pstate->r_pipe.sspp->idx, active_fetch);
- set_bit(pstate->r_pipe.sspp->idx, active_pipes);
- _dpu_crtc_blend_setup_pipe(crtc, plane,
- mixer, cstate->num_mixers,
- pstate->stage,
- format, fb ? fb->modifier : 0,
- &pstate->r_pipe, 1, stage_cfg);
+ /* loop pipe per mixer pair with config in stage structure */
+ for (stage = 0; stage < STAGES_PER_PLANE; stage++) {
+ head_pipe_in_stage = stage * PIPES_PER_STAGE;
+ for (i = 0; i < PIPES_PER_STAGE; i++) {
+ pipe_idx = i + head_pipe_in_stage;
+ if (!pstate->pipe[pipe_idx].sspp)
+ continue;
+ lms_in_pair = min(cstate->num_mixers - (stage * PIPES_PER_STAGE),
+ PIPES_PER_STAGE);
+ set_bit(pstate->pipe[pipe_idx].sspp->idx, active_fetch);
+ set_bit(pstate->pipe[pipe_idx].sspp->idx, active_pipes);
+ _dpu_crtc_blend_setup_pipe(crtc, plane,
+ &mixer[head_pipe_in_stage],
+ lms_in_pair,
+ pstate->stage,
+ format, fb ? fb->modifier : 0,
+ &pstate->pipe[pipe_idx], i,
+ &stage_cfg[stage]);
+ }
}
/* blend config update */
@@ -523,7 +526,7 @@ static void _dpu_crtc_blend_setup(struct drm_crtc *crtc)
struct dpu_crtc_mixer *mixer = cstate->mixers;
struct dpu_hw_ctl *ctl;
struct dpu_hw_mixer *lm;
- struct dpu_hw_stage_cfg stage_cfg;
+ struct dpu_hw_stage_cfg stage_cfg[STAGES_PER_PLANE];
DECLARE_BITMAP(active_lms, LM_MAX);
int i;
@@ -544,10 +547,10 @@ static void _dpu_crtc_blend_setup(struct drm_crtc *crtc)
}
/* initialize stage cfg */
- memset(&stage_cfg, 0, sizeof(struct dpu_hw_stage_cfg));
+ memset(&stage_cfg, 0, sizeof(stage_cfg));
memset(active_lms, 0, sizeof(active_lms));
- _dpu_crtc_blend_setup_mixer(crtc, dpu_crtc, mixer, &stage_cfg);
+ _dpu_crtc_blend_setup_mixer(crtc, dpu_crtc, mixer, stage_cfg);
for (i = 0; i < cstate->num_mixers; i++) {
ctl = mixer[i].lm_ctl;
@@ -568,13 +571,17 @@ static void _dpu_crtc_blend_setup(struct drm_crtc *crtc)
mixer[i].mixer_op_mode,
ctl->idx - CTL_0);
+ /*
+ * call dpu_hw_ctl_setup_blendstage() to blend layers per stage cfg.
+ * stage data is shared between PIPES_PER_STAGE pipes.
+ */
if (ctl->ops.setup_blendstage)
ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx,
- &stage_cfg);
+ &stage_cfg[i / PIPES_PER_STAGE]);
if (lm->ops.setup_blendstage)
lm->ops.setup_blendstage(lm, mixer[i].hw_lm->idx,
- &stage_cfg);
+ &stage_cfg[i / PIPES_PER_STAGE]);
}
}
@@ -1310,7 +1317,7 @@ done:
return ret;
}
-#define MAX_CHANNELS_PER_CRTC 2
+#define MAX_CHANNELS_PER_CRTC PIPES_PER_PLANE
#define MAX_HDISPLAY_SPLIT 1080
static struct msm_display_topology dpu_crtc_get_topology(
@@ -1321,6 +1328,7 @@ static struct msm_display_topology dpu_crtc_get_topology(
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
struct msm_display_topology topology = {0};
struct drm_encoder *drm_enc;
+ u32 num_rt_intf;
drm_for_each_encoder_mask(drm_enc, crtc->dev, crtc_state->encoder_mask)
dpu_encoder_update_topology(drm_enc, &topology, crtc_state->state,
@@ -1334,11 +1342,14 @@ static struct msm_display_topology dpu_crtc_get_topology(
* Dual display
* 2 LM, 2 INTF ( Split display using 2 interfaces)
*
+ * If DSC is enabled, try to use 4:4:2 topology if there is enough
+ * resource. Otherwise, use 2:2:2 topology.
+ *
* Single display
* 1 LM, 1 INTF
* 2 LM, 1 INTF (stream merge to support high resolution interfaces)
*
- * If DSC is enabled, use 2 LMs for 2:2:1 topology
+ * If DSC is enabled, use 2:2:1 topology
*
* Add dspps to the reservation requirements if ctm is requested
*
@@ -1350,14 +1361,23 @@ static struct msm_display_topology dpu_crtc_get_topology(
* (mode->hdisplay > MAX_HDISPLAY_SPLIT) check.
*/
- if (topology.num_intf == 2 && !topology.cwb_enabled)
- topology.num_lm = 2;
- else if (topology.num_dsc == 2)
+ num_rt_intf = topology.num_intf;
+ if (topology.cwb_enabled)
+ num_rt_intf--;
+
+ if (topology.num_dsc) {
+ if (dpu_kms->catalog->dsc_count >= num_rt_intf * 2)
+ topology.num_dsc = num_rt_intf * 2;
+ else
+ topology.num_dsc = num_rt_intf;
+ topology.num_lm = topology.num_dsc;
+ } else if (num_rt_intf == 2) {
topology.num_lm = 2;
- else if (dpu_kms->catalog->caps->has_3d_merge)
+ } else if (dpu_kms->catalog->caps->has_3d_merge) {
topology.num_lm = (mode->hdisplay > MAX_HDISPLAY_SPLIT) ? 2 : 1;
- else
+ } else {
topology.num_lm = 1;
+ }
if (crtc_state->ctm)
topology.num_dspp = topology.num_lm;
@@ -1545,6 +1565,9 @@ static enum drm_mode_status dpu_crtc_mode_valid(struct drm_crtc *crtc,
adjusted_mode_clk = dpu_core_perf_adjusted_mode_clk(mode->clock,
dpu_kms->perf.perf_cfg);
+ if (dpu_kms->catalog->caps->has_3d_merge)
+ adjusted_mode_clk /= 2;
+
/*
* The given mode, adjusted for the perf clock factor, should not exceed
* the max core clock rate
@@ -1597,6 +1620,17 @@ int dpu_crtc_vblank(struct drm_crtc *crtc, bool en)
return 0;
}
+/**
+ * dpu_crtc_get_num_lm - Get mixer number in this CRTC pipeline
+ * @state: Pointer to drm crtc state object
+ */
+unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state)
+{
+ struct dpu_crtc_state *cstate = to_dpu_crtc_state(state);
+
+ return cstate->num_mixers;
+}
+
#ifdef CONFIG_DEBUG_FS
static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
{
@@ -1679,15 +1713,15 @@ static int _dpu_debugfs_status_show(struct seq_file *s, void *data)
seq_printf(s, "\tdst x:%4d dst_y:%4d dst_w:%4d dst_h:%4d\n",
state->crtc_x, state->crtc_y, state->crtc_w,
state->crtc_h);
- seq_printf(s, "\tsspp[0]:%s\n",
- pstate->pipe.sspp->cap->name);
- seq_printf(s, "\tmultirect[0]: mode: %d index: %d\n",
- pstate->pipe.multirect_mode, pstate->pipe.multirect_index);
- if (pstate->r_pipe.sspp) {
- seq_printf(s, "\tsspp[1]:%s\n",
- pstate->r_pipe.sspp->cap->name);
- seq_printf(s, "\tmultirect[1]: mode: %d index: %d\n",
- pstate->r_pipe.multirect_mode, pstate->r_pipe.multirect_index);
+
+ for (i = 0; i < PIPES_PER_PLANE; i++) {
+ if (!pstate->pipe[i].sspp)
+ continue;
+ seq_printf(s, "\tsspp[%d]:%s\n",
+ i, pstate->pipe[i].sspp->cap->name);
+ seq_printf(s, "\tmultirect[%d]: mode: %d index: %d\n",
+ i, pstate->pipe[i].multirect_mode,
+ pstate->pipe[i].multirect_index);
}
seq_puts(s, "\n");
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
index 94392b9b9245..455073c7025b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.h
@@ -210,7 +210,7 @@ struct dpu_crtc_state {
bool bw_control;
bool bw_split_vote;
- struct drm_rect lm_bounds[CRTC_DUAL_MIXERS];
+ struct drm_rect lm_bounds[CRTC_QUAD_MIXERS];
uint64_t input_fence_timeout_ns;
@@ -218,10 +218,10 @@ struct dpu_crtc_state {
/* HW Resources reserved for the crtc */
u32 num_mixers;
- struct dpu_crtc_mixer mixers[CRTC_DUAL_MIXERS];
+ struct dpu_crtc_mixer mixers[CRTC_QUAD_MIXERS];
u32 num_ctls;
- struct dpu_hw_ctl *hw_ctls[CRTC_DUAL_MIXERS];
+ struct dpu_hw_ctl *hw_ctls[CRTC_QUAD_MIXERS];
enum dpu_crtc_crc_source crc_source;
int crc_frame_skip_count;
@@ -267,4 +267,6 @@ static inline enum dpu_crtc_client_type dpu_crtc_get_client_type(
void dpu_crtc_frame_event_cb(struct drm_crtc *crtc, u32 event);
+unsigned int dpu_crtc_get_num_lm(const struct drm_crtc_state *state);
+
#endif /* _DPU_CRTC_H_ */
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
index 258edaa18fc0..d1cfe81a3373 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder.c
@@ -55,7 +55,8 @@
#define MAX_PHYS_ENCODERS_PER_VIRTUAL \
(MAX_H_TILES_PER_DISPLAY * NUM_PHYS_ENCODER_TYPES)
-#define MAX_CHANNELS_PER_ENC 2
+#define MAX_CHANNELS_PER_ENC 4
+#define MAX_CWB_PER_ENC 2
#define IDLE_SHORT_TIMEOUT 1
@@ -182,7 +183,7 @@ struct dpu_encoder_virt {
struct dpu_encoder_phys *cur_master;
struct dpu_encoder_phys *cur_slave;
struct dpu_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
- struct dpu_hw_cwb *hw_cwb[MAX_CHANNELS_PER_ENC];
+ struct dpu_hw_cwb *hw_cwb[MAX_CWB_PER_ENC];
struct dpu_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
unsigned int dsc_mask;
@@ -660,7 +661,6 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
struct dpu_encoder_virt *dpu_enc = to_dpu_encoder_virt(drm_enc);
struct msm_drm_private *priv = dpu_enc->base.dev->dev_private;
struct msm_display_info *disp_info = &dpu_enc->disp_info;
- struct dpu_kms *dpu_kms = to_dpu_kms(priv->kms);
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct drm_framebuffer *fb;
@@ -674,22 +674,12 @@ void dpu_encoder_update_topology(struct drm_encoder *drm_enc,
dsc = dpu_encoder_get_dsc_config(drm_enc);
- /* We only support 2 DSC mode (with 2 LM and 1 INTF) */
- if (dsc) {
- /*
- * Use 2 DSC encoders, 2 layer mixers and 1 or 2 interfaces
- * when Display Stream Compression (DSC) is enabled,
- * and when enough DSC blocks are available.
- * This is power-optimal and can drive up to (including) 4k
- * screens.
- */
- WARN(topology->num_intf > 2,
- "DSC topology cannot support more than 2 interfaces\n");
- if (topology->num_intf >= 2 || dpu_kms->catalog->dsc_count >= 2)
- topology->num_dsc = 2;
- else
- topology->num_dsc = 1;
- }
+ /*
+ * Set DSC number as 1 to mark the enabled status, will be adjusted
+ * in dpu_crtc_get_topology()
+ */
+ if (dsc)
+ topology->num_dsc = 1;
connector = drm_atomic_get_new_connector_for_encoder(state, drm_enc);
if (!connector)
@@ -1160,7 +1150,7 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
struct dpu_hw_blk *hw_ctl[MAX_CHANNELS_PER_ENC];
struct dpu_hw_blk *hw_dsc[MAX_CHANNELS_PER_ENC];
struct dpu_hw_blk *hw_cwb[MAX_CHANNELS_PER_ENC];
- int num_ctl, num_pp, num_dsc;
+ int num_ctl, num_pp, num_dsc, num_pp_per_intf;
int num_cwb = 0;
bool is_cwb_encoder;
unsigned int dsc_mask = 0;
@@ -1239,10 +1229,16 @@ static void dpu_encoder_virt_atomic_mode_set(struct drm_encoder *drm_enc,
dpu_enc->cur_master->hw_cdm = hw_cdm ? to_dpu_hw_cdm(hw_cdm) : NULL;
}
+ /*
+ * There may be 4 PP and 2 INTF for quad pipe case, so INTF is not
+ * mapped to PP 1:1. Let's calculate the stride with pipe/INTF
+ */
+ num_pp_per_intf = num_pp / dpu_enc->num_phys_encs;
+
for (i = 0; i < dpu_enc->num_phys_encs; i++) {
struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
- phys->hw_pp = dpu_enc->hw_pp[i];
+ phys->hw_pp = dpu_enc->hw_pp[num_pp_per_intf * i];
if (!phys->hw_pp) {
DPU_ERROR_ENC(dpu_enc,
"no pp block assigned at idx: %d\n", i);
@@ -2171,15 +2167,12 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc)
static void dpu_encoder_helper_reset_mixers(struct dpu_encoder_phys *phys_enc)
{
- struct dpu_hw_mixer_cfg mixer;
int i, num_lm;
struct dpu_global_state *global_state;
- struct dpu_hw_blk *hw_lm[2];
- struct dpu_hw_mixer *hw_mixer[2];
+ struct dpu_hw_blk *hw_lm[MAX_CHANNELS_PER_ENC];
+ struct dpu_hw_mixer *hw_mixer[MAX_CHANNELS_PER_ENC];
struct dpu_hw_ctl *ctl = phys_enc->hw_ctl;
- memset(&mixer, 0, sizeof(mixer));
-
/* reset all mixers for this encoder */
if (ctl->ops.clear_all_blendstages)
ctl->ops.clear_all_blendstages(ctl);
@@ -2383,7 +2376,7 @@ void dpu_encoder_helper_phys_setup_cwb(struct dpu_encoder_phys *phys_enc,
*/
cwb_cfg.input = INPUT_MODE_LM_OUT;
- for (int i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+ for (int i = 0; i < MAX_CWB_PER_ENC; i++) {
hw_cwb = dpu_enc->hw_cwb[i];
if (!hw_cwb)
continue;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
index 61b22d949454..09395d7910ac 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys.h
@@ -302,7 +302,7 @@ static inline enum dpu_3d_blend_mode dpu_encoder_helper_get_3d_blend_mode(
/* Use merge_3d unless DSC MERGE topology is used */
if (phys_enc->split_role == ENC_ROLE_SOLO &&
- dpu_cstate->num_mixers == CRTC_DUAL_MIXERS &&
+ (dpu_cstate->num_mixers != 1) &&
!dpu_encoder_use_dsc_merge(phys_enc->parent))
return BLEND_3D_H_ROW_INT;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
index 6641455c4ec6..23bb39b471b7 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c
@@ -267,8 +267,8 @@ static const u32 wb2_formats_rgb_yuv[] = {
.base = 0x200, .len = 0xa0,}, \
.csc_blk = {.name = "csc", \
.base = 0x320, .len = 0x100,}, \
- .format_list = plane_formats_yuv, \
- .num_formats = ARRAY_SIZE(plane_formats_yuv), \
+ .format_list = plane_formats, \
+ .num_formats = ARRAY_SIZE(plane_formats), \
.rotation_cfg = NULL, \
}
@@ -726,3 +726,4 @@ static const struct dpu_qos_lut_entry sc7180_qos_nrt[] = {
#include "catalog/dpu_10_0_sm8650.h"
#include "catalog/dpu_12_0_sm8750.h"
+#include "catalog/dpu_12_2_glymur.h"
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
index f0768f54e9b3..336757103b5a 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h
@@ -24,7 +24,7 @@
#define DPU_MAX_IMG_WIDTH 0x3fff
#define DPU_MAX_IMG_HEIGHT 0x3fff
-#define CRTC_DUAL_MIXERS 2
+#define CRTC_QUAD_MIXERS 4
#define MAX_XIN_COUNT 16
@@ -749,6 +749,7 @@ struct dpu_mdss_cfg {
const struct dpu_format_extended *vig_formats;
};
+extern const struct dpu_mdss_cfg dpu_glymur_cfg;
extern const struct dpu_mdss_cfg dpu_msm8917_cfg;
extern const struct dpu_mdss_cfg dpu_msm8937_cfg;
extern const struct dpu_mdss_cfg dpu_msm8953_cfg;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
index b7013c9822d2..cc7cc6f6f7cd 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_dsc.h
@@ -71,12 +71,6 @@ struct dpu_hw_dsc *dpu_hw_dsc_init_1_2(struct drm_device *dev,
const struct dpu_dsc_cfg *cfg,
void __iomem *addr);
-/**
- * dpu_hw_dsc_destroy - destroys dsc driver context
- * @dsc: Pointer to dsc driver context returned by dpu_hw_dsc_init
- */
-void dpu_hw_dsc_destroy(struct dpu_hw_dsc *dsc);
-
static inline struct dpu_hw_dsc *to_dpu_hw_dsc(struct dpu_hw_blk *hw)
{
return container_of(hw, struct dpu_hw_dsc, base);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
index 175639c8bfbb..31451241f083 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h
@@ -34,7 +34,9 @@
#define DPU_MAX_PLANES 4
#endif
+#define STAGES_PER_PLANE 2
#define PIPES_PER_STAGE 2
+#define PIPES_PER_PLANE (PIPES_PER_STAGE * STAGES_PER_PLANE)
#ifndef DPU_MAX_DE_CURVES
#define DPU_MAX_DE_CURVES 3
#endif
@@ -149,6 +151,10 @@ enum dpu_dspp {
DSPP_1,
DSPP_2,
DSPP_3,
+ DSPP_4,
+ DSPP_5,
+ DSPP_6,
+ DSPP_7,
DSPP_MAX
};
@@ -159,6 +165,8 @@ enum dpu_ctl {
CTL_3,
CTL_4,
CTL_5,
+ CTL_6,
+ CTL_7,
CTL_MAX
};
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 4e5a8ecd31f7..f4c9767c418d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -1505,6 +1505,7 @@ static const struct dev_pm_ops dpu_pm_ops = {
};
static const struct of_device_id dpu_dt_match[] = {
+ { .compatible = "qcom,glymur-dpu", .data = &dpu_glymur_cfg, },
{ .compatible = "qcom,msm8917-mdp5", .data = &dpu_msm8917_cfg, },
{ .compatible = "qcom,msm8937-mdp5", .data = &dpu_msm8937_cfg, },
{ .compatible = "qcom,msm8953-mdp5", .data = &dpu_msm8953_cfg, },
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
index f54cf0faa1c7..d07a6ab6e7ee 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
@@ -500,13 +500,15 @@ static void _dpu_plane_setup_pixel_ext(struct dpu_hw_scaler3_cfg *scale_cfg,
int i;
for (i = 0; i < DPU_MAX_PLANES; i++) {
+ uint32_t w = src_w, h = src_h;
+
if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) {
- src_w /= chroma_subsmpl_h;
- src_h /= chroma_subsmpl_v;
+ w /= chroma_subsmpl_h;
+ h /= chroma_subsmpl_v;
}
- pixel_ext->num_ext_pxls_top[i] = src_h;
- pixel_ext->num_ext_pxls_left[i] = src_w;
+ pixel_ext->num_ext_pxls_top[i] = h;
+ pixel_ext->num_ext_pxls_left[i] = w;
}
}
@@ -620,6 +622,7 @@ static void _dpu_plane_color_fill(struct dpu_plane *pdpu,
struct msm_drm_private *priv = plane->dev->dev_private;
struct dpu_plane_state *pstate = to_dpu_plane_state(plane->state);
u32 fill_color = (color & 0xFFFFFF) | ((alpha & 0xFF) << 24);
+ int i;
DPU_DEBUG_PLANE(pdpu, "\n");
@@ -633,12 +636,13 @@ static void _dpu_plane_color_fill(struct dpu_plane *pdpu,
return;
/* update sspp */
- _dpu_plane_color_fill_pipe(pstate, &pstate->pipe, &pstate->pipe_cfg.dst_rect,
- fill_color, fmt);
-
- if (pstate->r_pipe.sspp)
- _dpu_plane_color_fill_pipe(pstate, &pstate->r_pipe, &pstate->r_pipe_cfg.dst_rect,
+ for (i = 0; i < PIPES_PER_PLANE; i++) {
+ if (!pstate->pipe[i].sspp)
+ continue;
+ _dpu_plane_color_fill_pipe(pstate, &pstate->pipe[i],
+ &pstate->pipe_cfg[i].dst_rect,
fill_color, fmt);
+ }
}
static int dpu_plane_prepare_fb(struct drm_plane *plane,
@@ -740,7 +744,7 @@ static int dpu_plane_atomic_check_pipe(struct dpu_plane *pdpu,
* We already have verified scaling against platform limitations.
* Now check if the SSPP supports scaling at all.
*/
- if (!sblk->scaler_blk.len &&
+ if (!(sblk->scaler_blk.len && pipe->sspp->ops.setup_scaler) &&
((drm_rect_width(&new_plane_state->src) >> 16 !=
drm_rect_width(&new_plane_state->dst)) ||
(drm_rect_height(&new_plane_state->src) >> 16 !=
@@ -820,10 +824,14 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);
u64 max_mdp_clk_rate = kms->perf.max_core_clk_rate;
struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
- struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
- struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;
+ struct dpu_sw_pipe_cfg *pipe_cfg;
+ struct dpu_sw_pipe_cfg *r_pipe_cfg;
+ struct dpu_sw_pipe_cfg init_pipe_cfg;
struct drm_rect fb_rect = { 0 };
+ const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
uint32_t max_linewidth;
+ u32 num_lm;
+ int stage_id, num_stages;
min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO);
max_scale = MAX_DOWNSCALE_RATIO << 16;
@@ -846,10 +854,10 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
return -EINVAL;
}
- /* state->src is 16.16, src_rect is not */
- drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
+ num_lm = dpu_crtc_get_num_lm(crtc_state);
- pipe_cfg->dst_rect = new_plane_state->dst;
+ /* state->src is 16.16, src_rect is not */
+ drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src);
fb_rect.x2 = new_plane_state->fb->width;
fb_rect.y2 = new_plane_state->fb->height;
@@ -874,35 +882,94 @@ static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane,
max_linewidth = pdpu->catalog->caps->max_linewidth;
- drm_rect_rotate(&pipe_cfg->src_rect,
+ drm_rect_rotate(&init_pipe_cfg.src_rect,
new_plane_state->fb->width, new_plane_state->fb->height,
new_plane_state->rotation);
- if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
- _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {
- if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
- DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
- DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
- return -E2BIG;
+ /*
+ * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair
+ * configs for left and right half screen in case of 4:4:2 topology.
+ * But we may have 2 rect to split wide plane that exceeds limit with 1
+ * config for 2:2:1. So need to handle both wide plane splitting, and
+ * two halves of screen splitting for quad-pipe case. Check dest
+ * rectangle left/right clipping first, then check wide rectangle
+ * splitting in every half next.
+ */
+ num_stages = (num_lm + 1) / 2;
+ /* iterate mixer configs for this plane, to separate left/right with the id */
+ for (stage_id = 0; stage_id < num_stages; stage_id++) {
+ struct drm_rect mixer_rect = {
+ .x1 = stage_id * mode->hdisplay / num_stages,
+ .y1 = 0,
+ .x2 = (stage_id + 1) * mode->hdisplay / num_stages,
+ .y2 = mode->vdisplay
+ };
+ int cfg_idx = stage_id * PIPES_PER_STAGE;
+
+ pipe_cfg = &pstate->pipe_cfg[cfg_idx];
+ r_pipe_cfg = &pstate->pipe_cfg[cfg_idx + 1];
+
+ drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src);
+ pipe_cfg->dst_rect = new_plane_state->dst;
+
+ DPU_DEBUG_PLANE(pdpu, "checking src " DRM_RECT_FMT
+ " vs clip window " DRM_RECT_FMT "\n",
+ DRM_RECT_ARG(&pipe_cfg->src_rect),
+ DRM_RECT_ARG(&mixer_rect));
+
+ /*
+ * If this plane does not fall into mixer rect, check next
+ * mixer rect.
+ */
+ if (!drm_rect_clip_scaled(&pipe_cfg->src_rect,
+ &pipe_cfg->dst_rect,
+ &mixer_rect)) {
+ memset(pipe_cfg, 0, 2 * sizeof(struct dpu_sw_pipe_cfg));
+
+ continue;
}
- *r_pipe_cfg = *pipe_cfg;
- pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1;
- pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1;
- r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2;
- r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2;
- } else {
- memset(r_pipe_cfg, 0, sizeof(*r_pipe_cfg));
- }
+ pipe_cfg->dst_rect.x1 -= mixer_rect.x1;
+ pipe_cfg->dst_rect.x2 -= mixer_rect.x1;
+
+ DPU_DEBUG_PLANE(pdpu, "Got clip src:" DRM_RECT_FMT " dst: " DRM_RECT_FMT "\n",
+ DRM_RECT_ARG(&pipe_cfg->src_rect), DRM_RECT_ARG(&pipe_cfg->dst_rect));
+
+ /* Split wide rect into 2 rect */
+ if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||
+ _dpu_plane_calc_clk(mode, pipe_cfg) > max_mdp_clk_rate) {
+
+ if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {
+ DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
+ DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);
+ return -E2BIG;
+ }
+
+ memcpy(r_pipe_cfg, pipe_cfg, sizeof(struct dpu_sw_pipe_cfg));
+ pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1;
+ pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1;
+ r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2;
+ r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2;
+ DPU_DEBUG_PLANE(pdpu, "Split wide plane into:"
+ DRM_RECT_FMT " and " DRM_RECT_FMT "\n",
+ DRM_RECT_ARG(&pipe_cfg->src_rect),
+ DRM_RECT_ARG(&r_pipe_cfg->src_rect));
+ } else {
+ memset(r_pipe_cfg, 0, sizeof(struct dpu_sw_pipe_cfg));
+ }
- drm_rect_rotate_inv(&pipe_cfg->src_rect,
- new_plane_state->fb->width, new_plane_state->fb->height,
- new_plane_state->rotation);
- if (drm_rect_width(&r_pipe_cfg->src_rect) != 0)
- drm_rect_rotate_inv(&r_pipe_cfg->src_rect,
- new_plane_state->fb->width, new_plane_state->fb->height,
+ drm_rect_rotate_inv(&pipe_cfg->src_rect,
+ new_plane_state->fb->width,
+ new_plane_state->fb->height,
new_plane_state->rotation);
+ if (drm_rect_width(&r_pipe_cfg->src_rect) != 0)
+ drm_rect_rotate_inv(&r_pipe_cfg->src_rect,
+ new_plane_state->fb->width,
+ new_plane_state->fb->height,
+ new_plane_state->rotation);
+ }
+
pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state);
return 0;
@@ -952,6 +1019,23 @@ static int dpu_plane_is_multirect_parallel_capable(struct dpu_hw_sspp *sspp,
dpu_plane_is_parallel_capable(pipe_cfg, fmt, max_linewidth);
}
+static bool dpu_plane_get_single_pipe_in_stage(struct dpu_plane_state *pstate,
+ struct dpu_sw_pipe **single_pipe,
+ struct dpu_sw_pipe_cfg **single_pipe_cfg,
+ int stage_index)
+{
+ int pipe_idx;
+
+ pipe_idx = stage_index * PIPES_PER_STAGE;
+ if (drm_rect_width(&pstate->pipe_cfg[pipe_idx].src_rect) != 0 &&
+ drm_rect_width(&pstate->pipe_cfg[pipe_idx + 1].src_rect) == 0) {
+ *single_pipe = &pstate->pipe[pipe_idx];
+ *single_pipe_cfg = &pstate->pipe_cfg[pipe_idx];
+ return true;
+ }
+
+ return false;
+}
static int dpu_plane_atomic_check_sspp(struct drm_plane *plane,
struct drm_atomic_state *state,
@@ -961,20 +1045,17 @@ static int dpu_plane_atomic_check_sspp(struct drm_plane *plane,
drm_atomic_get_new_plane_state(state, plane);
struct dpu_plane *pdpu = to_dpu_plane(plane);
struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
- struct dpu_sw_pipe *pipe = &pstate->pipe;
- struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
- struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
- struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;
- int ret = 0;
-
- ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg,
- &crtc_state->adjusted_mode,
- new_plane_state);
- if (ret)
- return ret;
+ struct dpu_sw_pipe *pipe;
+ struct dpu_sw_pipe_cfg *pipe_cfg;
+ int ret = 0, i;
- if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) {
- ret = dpu_plane_atomic_check_pipe(pdpu, r_pipe, r_pipe_cfg,
+ for (i = 0; i < PIPES_PER_PLANE; i++) {
+ pipe = &pstate->pipe[i];
+ pipe_cfg = &pstate->pipe_cfg[i];
+ if (!drm_rect_width(&pipe_cfg->src_rect))
+ continue;
+ DPU_DEBUG_PLANE(pdpu, "pipe %d is in use, validate it\n", i);
+ ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg,
&crtc_state->adjusted_mode,
new_plane_state);
if (ret)
@@ -1017,17 +1098,20 @@ static bool dpu_plane_try_multirect_parallel(struct dpu_sw_pipe *pipe, struct dp
static int dpu_plane_try_multirect_shared(struct dpu_plane_state *pstate,
struct dpu_plane_state *prev_adjacent_pstate,
const struct msm_format *fmt,
- uint32_t max_linewidth)
+ uint32_t max_linewidth, int stage_index)
{
- struct dpu_sw_pipe *pipe = &pstate->pipe;
- struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
- struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
- struct dpu_sw_pipe *prev_pipe = &prev_adjacent_pstate->pipe;
- struct dpu_sw_pipe_cfg *prev_pipe_cfg = &prev_adjacent_pstate->pipe_cfg;
+ struct dpu_sw_pipe *pipe, *prev_pipe;
+ struct dpu_sw_pipe_cfg *pipe_cfg, *prev_pipe_cfg;
const struct msm_format *prev_fmt = msm_framebuffer_format(prev_adjacent_pstate->base.fb);
u16 max_tile_height = 1;
- if (prev_adjacent_pstate->r_pipe.sspp != NULL ||
+ if (!dpu_plane_get_single_pipe_in_stage(pstate, &pipe,
+ &pipe_cfg, stage_index))
+ return false;
+
+ if (!dpu_plane_get_single_pipe_in_stage(prev_adjacent_pstate,
+ &prev_pipe, &prev_pipe_cfg,
+ stage_index) ||
prev_pipe->multirect_mode != DPU_SSPP_MULTIRECT_NONE)
return false;
@@ -1042,11 +1126,6 @@ static int dpu_plane_try_multirect_shared(struct dpu_plane_state *pstate,
if (MSM_FORMAT_IS_UBWC(prev_fmt))
max_tile_height = max(max_tile_height, prev_fmt->tile_height);
- r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;
- r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
-
- r_pipe->sspp = NULL;
-
if (dpu_plane_is_parallel_capable(pipe_cfg, fmt, max_linewidth) &&
dpu_plane_is_parallel_capable(prev_pipe_cfg, prev_fmt, max_linewidth) &&
(pipe_cfg->dst_rect.x1 >= prev_pipe_cfg->dst_rect.x2 ||
@@ -1087,10 +1166,10 @@ static int dpu_plane_atomic_check(struct drm_plane *plane,
struct dpu_plane *pdpu = to_dpu_plane(plane);
struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
- struct dpu_sw_pipe *pipe = &pstate->pipe;
- struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
- struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
- struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;
+ struct dpu_sw_pipe *pipe = &pstate->pipe[0];
+ struct dpu_sw_pipe *r_pipe = &pstate->pipe[1];
+ struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg[0];
+ struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->pipe_cfg[1];
const struct drm_crtc_state *crtc_state = NULL;
uint32_t max_linewidth = dpu_kms->catalog->caps->max_linewidth;
@@ -1134,7 +1213,7 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane,
drm_atomic_get_old_plane_state(state, plane);
struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state);
struct drm_crtc_state *crtc_state = NULL;
- int ret;
+ int ret, i;
if (IS_ERR(plane_state))
return PTR_ERR(plane_state);
@@ -1152,8 +1231,8 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane,
* resources are freed by dpu_crtc_assign_plane_resources(),
* but clean them here.
*/
- pstate->pipe.sspp = NULL;
- pstate->r_pipe.sspp = NULL;
+ for (i = 0; i < PIPES_PER_PLANE; i++)
+ pstate->pipe[i].sspp = NULL;
return 0;
}
@@ -1175,37 +1254,72 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane,
return 0;
}
+static int dpu_plane_assign_resource_in_stage(struct dpu_sw_pipe *pipe,
+ struct dpu_sw_pipe_cfg *pipe_cfg,
+ struct drm_plane_state *plane_state,
+ struct dpu_global_state *global_state,
+ struct drm_crtc *crtc,
+ struct dpu_rm_sspp_requirements *reqs)
+{
+ struct drm_plane *plane = plane_state->plane;
+ struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
+ struct dpu_sw_pipe *r_pipe = pipe + 1;
+ struct dpu_sw_pipe_cfg *r_pipe_cfg = pipe_cfg + 1;
+
+ if (drm_rect_width(&pipe_cfg->src_rect) == 0)
+ return 0;
+
+ pipe->sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, reqs);
+ if (!pipe->sspp)
+ return -ENODEV;
+ pipe->multirect_index = DPU_SSPP_RECT_SOLO;
+ pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
+
+ if (drm_rect_width(&r_pipe_cfg->src_rect) == 0)
+ return 0;
+
+ if (dpu_plane_try_multirect_parallel(pipe, pipe_cfg, r_pipe, r_pipe_cfg,
+ pipe->sspp,
+ msm_framebuffer_format(plane_state->fb),
+ dpu_kms->catalog->caps->max_linewidth))
+ return 0;
+
+ r_pipe->sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, reqs);
+ if (!r_pipe->sspp)
+ return -ENODEV;
+ r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;
+ r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
+
+ return 0;
+}
+
static int dpu_plane_virtual_assign_resources(struct drm_crtc *crtc,
struct dpu_global_state *global_state,
struct drm_atomic_state *state,
struct drm_plane_state *plane_state,
- struct drm_plane_state *prev_adjacent_plane_state)
+ struct drm_plane_state **prev_adjacent_plane_state)
{
const struct drm_crtc_state *crtc_state = NULL;
struct drm_plane *plane = plane_state->plane;
struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
struct dpu_rm_sspp_requirements reqs;
- struct dpu_plane_state *pstate, *prev_adjacent_pstate;
+ struct dpu_plane_state *pstate, *prev_adjacent_pstate[STAGES_PER_PLANE];
struct dpu_sw_pipe *pipe;
- struct dpu_sw_pipe *r_pipe;
struct dpu_sw_pipe_cfg *pipe_cfg;
- struct dpu_sw_pipe_cfg *r_pipe_cfg;
const struct msm_format *fmt;
+ int i, ret;
if (plane_state->crtc)
crtc_state = drm_atomic_get_new_crtc_state(state,
plane_state->crtc);
pstate = to_dpu_plane_state(plane_state);
- prev_adjacent_pstate = prev_adjacent_plane_state ?
- to_dpu_plane_state(prev_adjacent_plane_state) : NULL;
- pipe = &pstate->pipe;
- r_pipe = &pstate->r_pipe;
- pipe_cfg = &pstate->pipe_cfg;
- r_pipe_cfg = &pstate->r_pipe_cfg;
-
- pipe->sspp = NULL;
- r_pipe->sspp = NULL;
+ for (i = 0; i < STAGES_PER_PLANE; i++)
+ prev_adjacent_pstate[i] = prev_adjacent_plane_state[i] ?
+ to_dpu_plane_state(prev_adjacent_plane_state[i]) : NULL;
+
+ for (i = 0; i < PIPES_PER_PLANE; i++)
+ pstate->pipe[i].sspp = NULL;
if (!plane_state->fb)
return -EINVAL;
@@ -1217,42 +1331,24 @@ static int dpu_plane_virtual_assign_resources(struct drm_crtc *crtc,
reqs.rot90 = drm_rotation_90_or_270(plane_state->rotation);
- if (drm_rect_width(&r_pipe_cfg->src_rect) == 0) {
- if (!prev_adjacent_pstate ||
- !dpu_plane_try_multirect_shared(pstate, prev_adjacent_pstate, fmt,
- dpu_kms->catalog->caps->max_linewidth)) {
- pipe->sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, &reqs);
- if (!pipe->sspp)
- return -ENODEV;
-
- r_pipe->sspp = NULL;
+ for (i = 0; i < STAGES_PER_PLANE; i++) {
+ if (prev_adjacent_pstate[i] &&
+ dpu_plane_try_multirect_shared(pstate, prev_adjacent_pstate[i], fmt,
+ dpu_kms->catalog->caps->max_linewidth,
+ i))
+ continue;
- pipe->multirect_index = DPU_SSPP_RECT_SOLO;
- pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
+ if (dpu_plane_get_single_pipe_in_stage(pstate, &pipe, &pipe_cfg, i))
+ prev_adjacent_plane_state[i] = plane_state;
- r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;
- r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
- }
- } else {
- pipe->sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, &reqs);
- if (!pipe->sspp)
- return -ENODEV;
-
- if (!dpu_plane_try_multirect_parallel(pipe, pipe_cfg, r_pipe, r_pipe_cfg,
- pipe->sspp,
- msm_framebuffer_format(plane_state->fb),
- dpu_kms->catalog->caps->max_linewidth)) {
- /* multirect is not possible, use two SSPP blocks */
- r_pipe->sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, &reqs);
- if (!r_pipe->sspp)
- return -ENODEV;
-
- pipe->multirect_index = DPU_SSPP_RECT_SOLO;
- pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
-
- r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;
- r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
- }
+ pipe = &pstate->pipe[i * PIPES_PER_STAGE];
+ pipe_cfg = &pstate->pipe_cfg[i * PIPES_PER_STAGE];
+ ret = dpu_plane_assign_resource_in_stage(pipe, pipe_cfg,
+ plane_state,
+ global_state,
+ crtc, &reqs);
+ if (ret)
+ return ret;
}
return dpu_plane_atomic_check_sspp(plane, state, crtc_state);
@@ -1265,7 +1361,7 @@ int dpu_assign_plane_resources(struct dpu_global_state *global_state,
unsigned int num_planes)
{
unsigned int i;
- struct drm_plane_state *prev_adjacent_plane_state = NULL;
+ struct drm_plane_state *prev_adjacent_plane_state[STAGES_PER_PLANE] = { NULL };
for (i = 0; i < num_planes; i++) {
struct drm_plane_state *plane_state = states[i];
@@ -1278,9 +1374,7 @@ int dpu_assign_plane_resources(struct dpu_global_state *global_state,
state, plane_state,
prev_adjacent_plane_state);
if (ret)
- break;
-
- prev_adjacent_plane_state = plane_state;
+ return ret;
}
return 0;
@@ -1316,6 +1410,7 @@ void dpu_plane_flush(struct drm_plane *plane)
{
struct dpu_plane *pdpu;
struct dpu_plane_state *pstate;
+ int i;
if (!plane || !plane->state) {
DPU_ERROR("invalid plane\n");
@@ -1336,8 +1431,8 @@ void dpu_plane_flush(struct drm_plane *plane)
/* force 100% alpha */
_dpu_plane_color_fill(pdpu, pdpu->color_fill, 0xFF);
else {
- dpu_plane_flush_csc(pdpu, &pstate->pipe);
- dpu_plane_flush_csc(pdpu, &pstate->r_pipe);
+ for (i = 0; i < PIPES_PER_PLANE; i++)
+ dpu_plane_flush_csc(pdpu, &pstate->pipe[i]);
}
/* flag h/w flush complete */
@@ -1438,15 +1533,12 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane,
struct dpu_plane *pdpu = to_dpu_plane(plane);
struct drm_plane_state *state = plane->state;
struct dpu_plane_state *pstate = to_dpu_plane_state(state);
- struct dpu_sw_pipe *pipe = &pstate->pipe;
- struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
struct drm_crtc *crtc = state->crtc;
struct drm_framebuffer *fb = state->fb;
bool is_rt_pipe;
const struct msm_format *fmt =
msm_framebuffer_format(fb);
- struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
- struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;
+ int i;
pstate->pending = true;
@@ -1461,12 +1553,11 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane,
crtc->base.id, DRM_RECT_ARG(&state->dst),
&fmt->pixel_format, MSM_FORMAT_IS_UBWC(fmt));
- dpu_plane_sspp_update_pipe(plane, pipe, pipe_cfg, fmt,
- drm_mode_vrefresh(&crtc->mode),
- &pstate->layout);
-
- if (r_pipe->sspp) {
- dpu_plane_sspp_update_pipe(plane, r_pipe, r_pipe_cfg, fmt,
+ for (i = 0; i < PIPES_PER_PLANE; i++) {
+ if (!drm_rect_width(&pstate->pipe_cfg[i].src_rect))
+ continue;
+ dpu_plane_sspp_update_pipe(plane, &pstate->pipe[i],
+ &pstate->pipe_cfg[i], fmt,
drm_mode_vrefresh(&crtc->mode),
&pstate->layout);
}
@@ -1474,15 +1565,17 @@ static void dpu_plane_sspp_atomic_update(struct drm_plane *plane,
if (pstate->needs_qos_remap)
pstate->needs_qos_remap = false;
- pstate->plane_fetch_bw = _dpu_plane_calc_bw(pdpu->catalog, fmt,
- &crtc->mode, pipe_cfg);
-
- pstate->plane_clk = _dpu_plane_calc_clk(&crtc->mode, pipe_cfg);
-
- if (r_pipe->sspp) {
- pstate->plane_fetch_bw += _dpu_plane_calc_bw(pdpu->catalog, fmt, &crtc->mode, r_pipe_cfg);
+ pstate->plane_fetch_bw = 0;
+ pstate->plane_clk = 0;
+ for (i = 0; i < PIPES_PER_PLANE; i++) {
+ if (!drm_rect_width(&pstate->pipe_cfg[i].src_rect))
+ continue;
+ pstate->plane_fetch_bw += _dpu_plane_calc_bw(pdpu->catalog, fmt,
+ &crtc->mode, &pstate->pipe_cfg[i]);
- pstate->plane_clk = max(pstate->plane_clk, _dpu_plane_calc_clk(&crtc->mode, r_pipe_cfg));
+ pstate->plane_clk = max(pstate->plane_clk,
+ _dpu_plane_calc_clk(&crtc->mode,
+ &pstate->pipe_cfg[i]));
}
}
@@ -1490,17 +1583,28 @@ static void _dpu_plane_atomic_disable(struct drm_plane *plane)
{
struct drm_plane_state *state = plane->state;
struct dpu_plane_state *pstate = to_dpu_plane_state(state);
- struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
+ struct dpu_sw_pipe *pipe;
+ int i;
+
+ for (i = 0; i < PIPES_PER_PLANE; i += 1) {
+ pipe = &pstate->pipe[i];
+ if (!pipe->sspp)
+ continue;
- trace_dpu_plane_disable(DRMID(plane), false,
- pstate->pipe.multirect_mode);
+ trace_dpu_plane_disable(DRMID(plane), false,
+ pstate->pipe[i].multirect_mode);
- if (r_pipe->sspp) {
- r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;
- r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
+ if (i % PIPES_PER_STAGE == 0)
+ continue;
- if (r_pipe->sspp->ops.setup_multirect)
- r_pipe->sspp->ops.setup_multirect(r_pipe);
+ /*
+ * clear multirect for the right pipe so that the SSPP
+ * can be further reused in the solo mode
+ */
+ pipe->multirect_index = DPU_SSPP_RECT_SOLO;
+ pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
+ if (pipe->sspp->ops.setup_multirect)
+ pipe->sspp->ops.setup_multirect(pipe);
}
pstate->pending = true;
@@ -1595,31 +1699,26 @@ static void dpu_plane_atomic_print_state(struct drm_printer *p,
const struct drm_plane_state *state)
{
const struct dpu_plane_state *pstate = to_dpu_plane_state(state);
- const struct dpu_sw_pipe *pipe = &pstate->pipe;
- const struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;
- const struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;
- const struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;
+ const struct dpu_sw_pipe *pipe;
+ const struct dpu_sw_pipe_cfg *pipe_cfg;
+ int i;
drm_printf(p, "\tstage=%d\n", pstate->stage);
- if (pipe->sspp) {
- drm_printf(p, "\tsspp[0]=%s\n", pipe->sspp->cap->name);
- drm_printf(p, "\tmultirect_mode[0]=%s\n",
+ for (i = 0; i < PIPES_PER_PLANE; i++) {
+ pipe = &pstate->pipe[i];
+ if (!pipe->sspp)
+ continue;
+ pipe_cfg = &pstate->pipe_cfg[i];
+ drm_printf(p, "\tsspp[%d]=%s\n", i, pipe->sspp->cap->name);
+ drm_printf(p, "\tmultirect_mode[%d]=%s\n", i,
dpu_get_multirect_mode(pipe->multirect_mode));
- drm_printf(p, "\tmultirect_index[0]=%s\n",
+ drm_printf(p, "\tmultirect_index[%d]=%s\n", i,
dpu_get_multirect_index(pipe->multirect_index));
- drm_printf(p, "\tsrc[0]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&pipe_cfg->src_rect));
- drm_printf(p, "\tdst[0]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&pipe_cfg->dst_rect));
- }
-
- if (r_pipe->sspp) {
- drm_printf(p, "\tsspp[1]=%s\n", r_pipe->sspp->cap->name);
- drm_printf(p, "\tmultirect_mode[1]=%s\n",
- dpu_get_multirect_mode(r_pipe->multirect_mode));
- drm_printf(p, "\tmultirect_index[1]=%s\n",
- dpu_get_multirect_index(r_pipe->multirect_index));
- drm_printf(p, "\tsrc[1]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&r_pipe_cfg->src_rect));
- drm_printf(p, "\tdst[1]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&r_pipe_cfg->dst_rect));
+ drm_printf(p, "\tsrc[%d]=" DRM_RECT_FMT "\n", i,
+ DRM_RECT_ARG(&pipe_cfg->src_rect));
+ drm_printf(p, "\tdst[%d]=" DRM_RECT_FMT "\n", i,
+ DRM_RECT_ARG(&pipe_cfg->dst_rect));
}
}
@@ -1657,14 +1756,17 @@ void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable)
struct dpu_plane *pdpu = to_dpu_plane(plane);
struct dpu_plane_state *pstate = to_dpu_plane_state(plane->state);
struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
+ int i;
if (!pdpu->is_rt_pipe)
return;
pm_runtime_get_sync(&dpu_kms->pdev->dev);
- _dpu_plane_set_qos_ctrl(plane, &pstate->pipe, enable);
- if (pstate->r_pipe.sspp)
- _dpu_plane_set_qos_ctrl(plane, &pstate->r_pipe, enable);
+ for (i = 0; i < PIPES_PER_PLANE; i++) {
+ if (!pstate->pipe[i].sspp)
+ continue;
+ _dpu_plane_set_qos_ctrl(plane, &pstate->pipe[i], enable);
+ }
pm_runtime_put_sync(&dpu_kms->pdev->dev);
}
#endif
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
index a3a6e9028333..1ef5a041b8ac 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h
@@ -17,10 +17,8 @@
/**
* struct dpu_plane_state: Define dpu extension of drm plane state object
* @base: base drm plane state object
- * @pipe: software pipe description
- * @r_pipe: software pipe description of the second pipe
- * @pipe_cfg: software pipe configuration
- * @r_pipe_cfg: software pipe configuration for the second pipe
+ * @pipe: software pipe description array
+ * @pipe_cfg: software pipe configuration array
* @stage: assigned by crtc blender
* @needs_qos_remap: qos remap settings need to be updated
* @multirect_index: index of the rectangle of SSPP
@@ -33,10 +31,8 @@
*/
struct dpu_plane_state {
struct drm_plane_state base;
- struct dpu_sw_pipe pipe;
- struct dpu_sw_pipe r_pipe;
- struct dpu_sw_pipe_cfg pipe_cfg;
- struct dpu_sw_pipe_cfg r_pipe_cfg;
+ struct dpu_sw_pipe pipe[PIPES_PER_PLANE];
+ struct dpu_sw_pipe_cfg pipe_cfg[PIPES_PER_PLANE];
enum dpu_stage stage;
bool needs_qos_remap;
bool pending;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
index 2c77c74fac0f..f6568ed8375f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c
@@ -374,7 +374,11 @@ static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
if (!rm->mixer_blks[i])
continue;
- lm_count = 0;
+ /*
+ * Reset lm_count to an even index. This will drop the previous
+ * primary mixer if failed to find its peer.
+ */
+ lm_count &= ~1;
lm_idx[lm_count] = i;
if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state,
@@ -842,7 +846,7 @@ struct dpu_hw_sspp *dpu_rm_reserve_sspp(struct dpu_rm *rm,
if (!reqs->scale && !reqs->yuv)
hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_DMA);
- if (!hw_sspp && reqs->scale)
+ if (!hw_sspp && !reqs->yuv)
hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_RGB);
if (!hw_sspp)
hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_VIG);
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h
index 5307cbc2007c..cb24ad2a6d8d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_trace.h
@@ -651,9 +651,9 @@ TRACE_EVENT(dpu_crtc_setup_mixer,
TP_PROTO(uint32_t crtc_id, uint32_t plane_id,
struct drm_plane_state *state, struct dpu_plane_state *pstate,
uint32_t stage_idx, uint32_t pixel_format,
- uint64_t modifier),
+ struct dpu_sw_pipe *pipe, uint64_t modifier),
TP_ARGS(crtc_id, plane_id, state, pstate, stage_idx,
- pixel_format, modifier),
+ pixel_format, pipe, modifier),
TP_STRUCT__entry(
__field( uint32_t, crtc_id )
__field( uint32_t, plane_id )
@@ -676,9 +676,9 @@ TRACE_EVENT(dpu_crtc_setup_mixer,
__entry->dst_rect = drm_plane_state_dest(state);
__entry->stage_idx = stage_idx;
__entry->stage = pstate->stage;
- __entry->sspp = pstate->pipe.sspp->idx;
- __entry->multirect_idx = pstate->pipe.multirect_index;
- __entry->multirect_mode = pstate->pipe.multirect_mode;
+ __entry->sspp = pipe->sspp->idx;
+ __entry->multirect_idx = pipe->multirect_index;
+ __entry->multirect_mode = pipe->multirect_mode;
__entry->pixel_format = pixel_format;
__entry->modifier = modifier;
),
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
index cd73468e369a..7545c0293efb 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
@@ -72,6 +72,9 @@ static int dpu_wb_conn_atomic_check(struct drm_connector *connector,
DPU_ERROR("invalid fb w=%d, maxlinewidth=%u\n",
fb->width, dpu_wb_conn->maxlinewidth);
return -EINVAL;
+ } else if (fb->modifier != DRM_FORMAT_MOD_LINEAR) {
+ DPU_ERROR("unsupported fb modifier:%#llx\n", fb->modifier);
+ return -EINVAL;
}
return drm_atomic_helper_check_wb_connector_state(conn_state->connector, conn_state->state);
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
index da53ca88251e..e8066f9fd534 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_crtc.c
@@ -527,13 +527,14 @@ static void mdp4_crtc_wait_for_flush_done(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
struct mdp4_kms *mdp4_kms = get_kms(crtc);
+ wait_queue_head_t *queue = drm_crtc_vblank_waitqueue(crtc);
int ret;
ret = drm_crtc_vblank_get(crtc);
if (ret)
return;
- ret = wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue,
+ ret = wait_event_timeout(*queue,
!(mdp4_read(mdp4_kms, REG_MDP4_OVERLAY_FLUSH) &
mdp4_crtc->flushed_mask),
msecs_to_jiffies(50));
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
index 4c4900a7beda..373ae7d9bf01 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
@@ -1234,6 +1234,7 @@ static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc)
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state);
struct mdp5_ctl *ctl = mdp5_cstate->ctl;
+ wait_queue_head_t *queue = drm_crtc_vblank_waitqueue(crtc);
int ret;
/* Should not call this function if crtc is disabled. */
@@ -1244,7 +1245,7 @@ static void mdp5_crtc_wait_for_flush_done(struct drm_crtc *crtc)
if (ret)
return;
- ret = wait_event_timeout(dev->vblank[drm_crtc_index(crtc)].queue,
+ ret = wait_event_timeout(*queue,
((mdp5_ctl_get_commit_status(ctl) &
mdp5_crtc->flushed_mask) == 0),
msecs_to_jiffies(50));
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
index 7c790406d533..4ca183fb61a9 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
@@ -336,8 +336,7 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane,
if (!crtc)
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
@@ -373,8 +372,8 @@ static int mdp5_plane_atomic_async_check(struct drm_plane *plane,
int min_scale, max_scale;
int ret;
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- new_plane_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state,
+ new_plane_state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot.h b/drivers/gpu/drm/msm/disp/msm_disp_snapshot.h
index b5f452bd7ada..53bd1dcde15f 100644
--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot.h
+++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot.h
@@ -38,6 +38,7 @@
* struct msm_disp_state - structure to store current dpu state
* @dev: device pointer
* @drm_dev: drm device pointer
+ * @blocks: list head for hardware state blocks
* @atomic_state: atomic state duplicated at the time of the error
* @time: timestamp at which the coredump was captured
*/
@@ -55,7 +56,7 @@ struct msm_disp_state {
/**
* struct msm_disp_state_block - structure to store each hardware block state
* @name: name of the block
- * @drm_dev: handle to the linked list head
+ * @node: handle to the linked list head
* @size: size of the register space of this hardware block
* @state: array holding the register dump of this hardware block
* @base_addr: starting address of this hardware block's register space
@@ -88,8 +89,9 @@ void msm_disp_snapshot_destroy(struct drm_device *drm_dev);
* msm_disp_snapshot_state_sync - synchronously snapshot display state
* @kms: the kms object
*
- * Returns state or error
+ * Returns: state or error
*
+ * Context:
* Must be called with &kms->dump_mutex held
*/
struct msm_disp_state *msm_disp_snapshot_state_sync(struct msm_kms *kms);
@@ -97,7 +99,7 @@ struct msm_disp_state *msm_disp_snapshot_state_sync(struct msm_kms *kms);
/**
* msm_disp_snapshot_state - trigger to dump the display snapshot
* @drm_dev: handle to drm device
-
+ *
* Returns: none
*/
void msm_disp_snapshot_state(struct drm_device *drm_dev);
@@ -114,7 +116,7 @@ void msm_disp_state_print(struct msm_disp_state *disp_state, struct drm_printer
/**
* msm_disp_snapshot_capture_state - utility to capture atomic state and hw registers
* @disp_state: handle to msm_disp_state struct
-
+ *
* Returns: none
*/
void msm_disp_snapshot_capture_state(struct msm_disp_state *disp_state);
@@ -122,7 +124,7 @@ void msm_disp_snapshot_capture_state(struct msm_disp_state *disp_state);
/**
* msm_disp_state_free - free the memory after the coredump has been read
* @data: handle to struct msm_disp_state
-
+ *
* Returns: none
*/
void msm_disp_state_free(void *data);
@@ -130,7 +132,6 @@ void msm_disp_state_free(void *data);
/**
* msm_disp_snapshot_add_block - add a hardware block with its register dump
* @disp_state: handle to struct msm_disp_state
- * @name: name of the hardware block
* @len: size of the register space of the hardware block
* @base_addr: starting address of the register space of the hardware block
* @fmt: format in which the block names need to be printed
diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
index 071bcdea80f7..19b470968f4d 100644
--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
+++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot_util.c
@@ -82,8 +82,7 @@ void msm_disp_state_print(struct msm_disp_state *state, struct drm_printer *p)
drm_printf(p, "kernel: " UTS_RELEASE "\n");
drm_printf(p, "module: " KBUILD_MODNAME "\n");
drm_printf(p, "dpu devcoredump\n");
- drm_printf(p, "time: %lld.%09ld\n",
- state->time.tv_sec, state->time.tv_nsec);
+ drm_printf(p, "time: %ptSp\n", &state->time);
list_for_each_entry_safe(block, tmp, &state->blocks, node) {
drm_printf(p, "====================%s================\n", block->name);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index c42fd2c17a32..cbcc7c2f0ffc 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -423,13 +423,13 @@ static void msm_dp_ctrl_config_ctrl(struct msm_dp_ctrl_private *ctrl)
static void msm_dp_ctrl_lane_mapping(struct msm_dp_ctrl_private *ctrl)
{
- u32 ln_0 = 0, ln_1 = 1, ln_2 = 2, ln_3 = 3; /* One-to-One mapping */
+ u32 *lane_map = ctrl->link->lane_map;
u32 ln_mapping;
- ln_mapping = ln_0 << LANE0_MAPPING_SHIFT;
- ln_mapping |= ln_1 << LANE1_MAPPING_SHIFT;
- ln_mapping |= ln_2 << LANE2_MAPPING_SHIFT;
- ln_mapping |= ln_3 << LANE3_MAPPING_SHIFT;
+ ln_mapping = lane_map[0] << LANE0_MAPPING_SHIFT;
+ ln_mapping |= lane_map[1] << LANE1_MAPPING_SHIFT;
+ ln_mapping |= lane_map[2] << LANE2_MAPPING_SHIFT;
+ ln_mapping |= lane_map[3] << LANE3_MAPPING_SHIFT;
msm_dp_write_link(ctrl, REG_DP_LOGICAL2PHYSICAL_LANE_MAPPING,
ln_mapping);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index d87d47cc7ec3..9bd9cd5c1e03 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -130,6 +130,14 @@ struct msm_dp_desc {
bool wide_bus_supported;
};
+static const struct msm_dp_desc msm_dp_desc_glymur[] = {
+ { .io_start = 0x0af54000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true },
+ { .io_start = 0x0af5c000, .id = MSM_DP_CONTROLLER_1, .wide_bus_supported = true },
+ { .io_start = 0x0af64000, .id = MSM_DP_CONTROLLER_2, .wide_bus_supported = true },
+ { .io_start = 0x0af6c000, .id = MSM_DP_CONTROLLER_3, .wide_bus_supported = true },
+ {}
+};
+
static const struct msm_dp_desc msm_dp_desc_sa8775p[] = {
{ .io_start = 0x0af54000, .id = MSM_DP_CONTROLLER_0, .wide_bus_supported = true },
{ .io_start = 0x0af5c000, .id = MSM_DP_CONTROLLER_1, .wide_bus_supported = true },
@@ -187,6 +195,7 @@ static const struct msm_dp_desc msm_dp_desc_x1e80100[] = {
};
static const struct of_device_id msm_dp_dt_match[] = {
+ { .compatible = "qcom,glymur-dp", .data = &msm_dp_desc_glymur },
{ .compatible = "qcom,sa8775p-dp", .data = &msm_dp_desc_sa8775p },
{ .compatible = "qcom,sc7180-dp", .data = &msm_dp_desc_sc7180 },
{ .compatible = "qcom,sc7280-dp", .data = &msm_dp_desc_sc7280 },
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
index 66e1bbd80db3..34a91e194a12 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.c
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -6,12 +6,14 @@
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
#include <drm/drm_device.h>
+#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include "dp_reg.h"
#include "dp_link.h"
#include "dp_panel.h"
+#define DP_LINK_RATE_HBR2 540000 /* kbytes */
#define DP_TEST_REQUEST_MASK 0x7F
enum audio_sample_rate {
@@ -1210,10 +1212,121 @@ u32 msm_dp_link_get_test_bits_depth(struct msm_dp_link *msm_dp_link, u32 bpp)
return tbd;
}
+static u32 msm_dp_link_link_frequencies(struct device_node *of_node)
+{
+ struct device_node *endpoint;
+ u64 frequency = 0;
+ int cnt;
+
+ endpoint = of_graph_get_endpoint_by_regs(of_node, 1, 0); /* port@1 */
+ if (!endpoint)
+ return 0;
+
+ cnt = of_property_count_u64_elems(endpoint, "link-frequencies");
+
+ if (cnt > 0)
+ of_property_read_u64_index(endpoint, "link-frequencies",
+ cnt - 1, &frequency);
+ of_node_put(endpoint);
+
+ do_div(frequency,
+ 10 * /* from symbol rate to link rate */
+ 1000); /* kbytes */
+
+ return frequency;
+}
+
+/*
+ * Always populate msm_dp_link->lane_map with 4 lanes.
+ * - Use DTS "data-lanes" if present; otherwise fall back to default mapping.
+ * - For partial definitions, fill remaining entries with unused lanes in
+ * ascending order.
+ */
+static int msm_dp_link_lane_map(struct device *dev, struct msm_dp_link *msm_dp_link)
+{
+ struct device_node *of_node = dev->of_node;
+ struct device_node *endpoint;
+ int cnt = msm_dp_link->max_dp_lanes;
+ u32 tmp[DP_MAX_NUM_DP_LANES];
+ u32 map[DP_MAX_NUM_DP_LANES] = {0, 1, 2, 3}; /* default 1:1 mapping */
+ bool used[DP_MAX_NUM_DP_LANES] = {false};
+ int i, j = 0, ret = -EINVAL;
+
+ endpoint = of_graph_get_endpoint_by_regs(of_node, 1, -1);
+ if (endpoint) {
+ ret = of_property_read_u32_array(endpoint, "data-lanes", tmp, cnt);
+ if (ret)
+ dev_dbg(dev, "endpoint data-lanes read failed (ret=%d)\n", ret);
+ }
+
+ if (ret) {
+ ret = of_property_read_u32_array(of_node, "data-lanes", tmp, cnt);
+ if (ret) {
+ dev_info(dev, "data-lanes not defined, set to default\n");
+ goto out;
+ }
+ }
+
+ for (i = 0; i < cnt; i++) {
+ if (tmp[i] >= DP_MAX_NUM_DP_LANES) {
+ dev_err(dev, "data-lanes[%d]=%u out of range\n", i, tmp[i]);
+ return -EINVAL;
+ }
+ used[tmp[i]] = true;
+ map[i] = tmp[i];
+ }
+
+ /* Fill the remaining entries with unused physical lanes (ascending) */
+ for (i = cnt; i < DP_MAX_NUM_DP_LANES && j < DP_MAX_NUM_DP_LANES; j++) {
+ if (!used[j])
+ map[i++] = j;
+ }
+
+out:
+ if (endpoint)
+ of_node_put(endpoint);
+
+ dev_dbg(dev, "data-lanes count %d <%d %d %d %d>\n", cnt, map[0], map[1], map[2], map[3]);
+ memcpy(msm_dp_link->lane_map, map, sizeof(map));
+ return 0;
+}
+
+static int msm_dp_link_parse_dt(struct device *dev, struct msm_dp_link *msm_dp_link)
+{
+ struct device_node *of_node = dev->of_node;
+ int cnt;
+
+ /*
+ * data-lanes is the property of msm_dp_out endpoint
+ */
+ cnt = drm_of_get_data_lanes_count_ep(of_node, 1, 0, 1, DP_MAX_NUM_DP_LANES);
+ if (cnt < 0) {
+ /* legacy code, data-lanes is the property of mdss_dp node */
+ cnt = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES);
+ }
+
+ if (cnt > 0)
+ msm_dp_link->max_dp_lanes = cnt;
+ else
+ msm_dp_link->max_dp_lanes = DP_MAX_NUM_DP_LANES; /* 4 lanes */
+
+ if (msm_dp_link_lane_map(dev, msm_dp_link)) {
+ dev_err(dev, "failed to parse data-lanes\n");
+ return -EINVAL;
+ }
+
+ msm_dp_link->max_dp_link_rate = msm_dp_link_link_frequencies(of_node);
+ if (!msm_dp_link->max_dp_link_rate)
+ msm_dp_link->max_dp_link_rate = DP_LINK_RATE_HBR2;
+
+ return 0;
+}
+
struct msm_dp_link *msm_dp_link_get(struct device *dev, struct drm_dp_aux *aux)
{
struct msm_dp_link_private *link;
struct msm_dp_link *msm_dp_link;
+ int ret;
if (!dev || !aux) {
DRM_ERROR("invalid input\n");
@@ -1229,5 +1342,9 @@ struct msm_dp_link *msm_dp_link_get(struct device *dev, struct drm_dp_aux *aux)
mutex_init(&link->psm_mutex);
msm_dp_link = &link->msm_dp_link;
+ ret = msm_dp_link_parse_dt(dev, msm_dp_link);
+ if (ret)
+ return ERR_PTR(ret);
+
return msm_dp_link;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
index ba47c6d19fbf..b1eb2de6d2a7 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.h
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -12,6 +12,7 @@
#define DS_PORT_STATUS_CHANGED 0x200
#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF
#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
+#define DP_MAX_NUM_DP_LANES 4
struct msm_dp_link_info {
unsigned char revision;
@@ -72,6 +73,10 @@ struct msm_dp_link {
struct msm_dp_link_test_audio test_audio;
struct msm_dp_link_phy_params phy_params;
struct msm_dp_link_info link_params;
+
+ u32 lane_map[DP_MAX_NUM_DP_LANES];
+ u32 max_dp_lanes;
+ u32 max_dp_link_rate;
};
/**
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 15b7f6c7146e..ad5d55bf009d 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -16,9 +16,6 @@
#define DP_INTF_CONFIG_DATABUS_WIDEN BIT(4)
-#define DP_MAX_NUM_DP_LANES 4
-#define DP_LINK_RATE_HBR2 540000 /* kbytes */
-
struct msm_dp_panel_private {
struct device *dev;
struct drm_device *drm_dev;
@@ -91,6 +88,7 @@ static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel)
int rc, max_lttpr_lanes, max_lttpr_rate;
struct msm_dp_panel_private *panel;
struct msm_dp_link_info *link_info;
+ struct msm_dp_link *link;
u8 *dpcd, major, minor;
panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
@@ -105,16 +103,20 @@ static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel)
major = (link_info->revision >> 4) & 0x0f;
minor = link_info->revision & 0x0f;
+ link = panel->link;
+ drm_dbg_dp(panel->drm_dev, "max_lanes=%d max_link_rate=%d\n",
+ link->max_dp_lanes, link->max_dp_link_rate);
+
link_info->rate = drm_dp_max_link_rate(dpcd);
link_info->num_lanes = drm_dp_max_lane_count(dpcd);
/* Limit data lanes from data-lanes of endpoint property of dtsi */
- if (link_info->num_lanes > msm_dp_panel->max_dp_lanes)
- link_info->num_lanes = msm_dp_panel->max_dp_lanes;
+ if (link_info->num_lanes > link->max_dp_lanes)
+ link_info->num_lanes = link->max_dp_lanes;
/* Limit link rate from link-frequencies of endpoint property of dtsi */
- if (link_info->rate > msm_dp_panel->max_dp_link_rate)
- link_info->rate = msm_dp_panel->max_dp_link_rate;
+ if (link_info->rate > link->max_dp_link_rate)
+ link_info->rate = link->max_dp_link_rate;
/* Limit data lanes from LTTPR capabilities, if any */
max_lttpr_lanes = drm_dp_lttpr_max_lane_count(panel->link->lttpr_common_caps);
@@ -173,9 +175,6 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel,
panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
- drm_dbg_dp(panel->drm_dev, "max_lanes=%d max_link_rate=%d\n",
- msm_dp_panel->max_dp_lanes, msm_dp_panel->max_dp_link_rate);
-
rc = msm_dp_panel_read_dpcd(msm_dp_panel);
if (rc) {
DRM_ERROR("read dpcd failed %d\n", rc);
@@ -648,60 +647,6 @@ int msm_dp_panel_init_panel_info(struct msm_dp_panel *msm_dp_panel)
return 0;
}
-static u32 msm_dp_panel_link_frequencies(struct device_node *of_node)
-{
- struct device_node *endpoint;
- u64 frequency = 0;
- int cnt;
-
- endpoint = of_graph_get_endpoint_by_regs(of_node, 1, 0); /* port@1 */
- if (!endpoint)
- return 0;
-
- cnt = of_property_count_u64_elems(endpoint, "link-frequencies");
-
- if (cnt > 0)
- of_property_read_u64_index(endpoint, "link-frequencies",
- cnt - 1, &frequency);
- of_node_put(endpoint);
-
- do_div(frequency,
- 10 * /* from symbol rate to link rate */
- 1000); /* kbytes */
-
- return frequency;
-}
-
-static int msm_dp_panel_parse_dt(struct msm_dp_panel *msm_dp_panel)
-{
- struct msm_dp_panel_private *panel;
- struct device_node *of_node;
- int cnt;
-
- panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel);
- of_node = panel->dev->of_node;
-
- /*
- * data-lanes is the property of msm_dp_out endpoint
- */
- cnt = drm_of_get_data_lanes_count_ep(of_node, 1, 0, 1, DP_MAX_NUM_DP_LANES);
- if (cnt < 0) {
- /* legacy code, data-lanes is the property of mdss_dp node */
- cnt = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES);
- }
-
- if (cnt > 0)
- msm_dp_panel->max_dp_lanes = cnt;
- else
- msm_dp_panel->max_dp_lanes = DP_MAX_NUM_DP_LANES; /* 4 lanes */
-
- msm_dp_panel->max_dp_link_rate = msm_dp_panel_link_frequencies(of_node);
- if (!msm_dp_panel->max_dp_link_rate)
- msm_dp_panel->max_dp_link_rate = DP_LINK_RATE_HBR2;
-
- return 0;
-}
-
struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux,
struct msm_dp_link *link,
void __iomem *link_base,
@@ -709,7 +654,6 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux
{
struct msm_dp_panel_private *panel;
struct msm_dp_panel *msm_dp_panel;
- int ret;
if (!dev || !aux || !link) {
DRM_ERROR("invalid input\n");
@@ -729,10 +673,6 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux
msm_dp_panel = &panel->msm_dp_panel;
msm_dp_panel->max_bw_code = DP_LINK_BW_8_1;
- ret = msm_dp_panel_parse_dt(msm_dp_panel);
- if (ret)
- return ERR_PTR(ret);
-
return msm_dp_panel;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index d2cf401506dc..921a296852d4 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -41,9 +41,6 @@ struct msm_dp_panel {
bool vsc_sdp_supported;
u32 hw_revision;
- u32 max_dp_lanes;
- u32 max_dp_link_rate;
-
u32 max_bw_code;
};
diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
index e391505fdaf0..3cbf08231492 100644
--- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h
@@ -109,7 +109,6 @@ struct msm_dsi_phy {
struct msm_dsi_dphy_timing timing;
const struct msm_dsi_phy_cfg *cfg;
void *tuning_cfg;
- void *pll_data;
enum msm_dsi_phy_usecase usecase;
bool regulator_ldo_mode;
diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c
index 32f06edd21a9..c5e1d2016bcc 100644
--- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c
+++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c
@@ -426,11 +426,8 @@ static void dsi_pll_enable_pll_bias(struct dsi_pll_7nm *pll)
u32 data;
spin_lock_irqsave(&pll->pll_enable_lock, flags);
- if (pll->pll_enable_cnt++) {
- spin_unlock_irqrestore(&pll->pll_enable_lock, flags);
- WARN_ON(pll->pll_enable_cnt == INT_MAX);
- return;
- }
+ pll->pll_enable_cnt++;
+ WARN_ON(pll->pll_enable_cnt == INT_MAX);
data = readl(pll->phy->base + REG_DSI_7nm_PHY_CMN_CTRL_0);
data |= DSI_7nm_PHY_CMN_CTRL_0_PLL_SHUTDOWNB;
@@ -876,7 +873,6 @@ static int dsi_pll_7nm_init(struct msm_dsi_phy *phy)
spin_lock_init(&pll_7nm->pll_enable_lock);
pll_7nm->phy = phy;
- phy->pll_data = pll_7nm;
ret = pll_7nm_register(pll_7nm, phy->provided_clocks->hws);
if (ret) {
@@ -965,10 +961,8 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy,
u32 const delay_us = 5;
u32 const timeout_us = 1000;
struct msm_dsi_dphy_timing *timing = &phy->timing;
- struct dsi_pll_7nm *pll = phy->pll_data;
void __iomem *base = phy->base;
bool less_than_1500_mhz;
- unsigned long flags;
u32 vreg_ctrl_0, vreg_ctrl_1, lane_ctrl0;
u32 glbl_pemph_ctrl_0;
u32 glbl_str_swi_cal_sel_ctrl, glbl_hstx_str_ctrl_0;
@@ -1090,13 +1084,10 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy,
glbl_rescode_bot_ctrl = 0x3c;
}
- spin_lock_irqsave(&pll->pll_enable_lock, flags);
- pll->pll_enable_cnt = 1;
/* de-assert digital and pll power down */
data = DSI_7nm_PHY_CMN_CTRL_0_DIGTOP_PWRDN_B |
DSI_7nm_PHY_CMN_CTRL_0_PLL_SHUTDOWNB;
writel(data, base + REG_DSI_7nm_PHY_CMN_CTRL_0);
- spin_unlock_irqrestore(&pll->pll_enable_lock, flags);
/* Assert PLL core reset */
writel(0x00, base + REG_DSI_7nm_PHY_CMN_PLL_CNTRL);
@@ -1209,9 +1200,7 @@ static bool dsi_7nm_set_continuous_clock(struct msm_dsi_phy *phy, bool enable)
static void dsi_7nm_phy_disable(struct msm_dsi_phy *phy)
{
- struct dsi_pll_7nm *pll = phy->pll_data;
void __iomem *base = phy->base;
- unsigned long flags;
u32 data;
DBG("");
@@ -1238,11 +1227,8 @@ static void dsi_7nm_phy_disable(struct msm_dsi_phy *phy)
writel(data, base + REG_DSI_7nm_PHY_CMN_CTRL_0);
writel(0, base + REG_DSI_7nm_PHY_CMN_LANE_CTRL0);
- spin_lock_irqsave(&pll->pll_enable_lock, flags);
- pll->pll_enable_cnt = 0;
/* Turn off all PHY blocks */
writel(0x00, base + REG_DSI_7nm_PHY_CMN_CTRL_0);
- spin_unlock_irqrestore(&pll->pll_enable_lock, flags);
/* make sure phy is turned off */
wmb();
diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
index b5969374d53f..fd19995b12b5 100644
--- a/drivers/gpu/drm/msm/msm_fbdev.c
+++ b/drivers/gpu/drm/msm/msm_fbdev.c
@@ -52,8 +52,6 @@ static void msm_fbdev_fb_destroy(struct fb_info *info)
drm_framebuffer_remove(fb);
drm_client_release(&helper->client);
- drm_fb_helper_unprepare(helper);
- kfree(helper);
}
static const struct fb_ops msm_fb_ops = {
@@ -93,9 +91,9 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
{
struct drm_device *dev = helper->dev;
struct msm_drm_private *priv = dev->dev_private;
+ struct fb_info *fbi = helper->info;
struct drm_framebuffer *fb = NULL;
struct drm_gem_object *bo;
- struct fb_info *fbi = NULL;
uint64_t paddr;
uint32_t format;
int ret, pitch;
@@ -128,13 +126,6 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
goto fail;
}
- fbi = drm_fb_helper_alloc_info(helper);
- if (IS_ERR(fbi)) {
- DRM_DEV_ERROR(dev->dev, "failed to allocate fb info\n");
- ret = PTR_ERR(fbi);
- goto fail;
- }
-
DBG("fbi=%p, dev=%p", fbi, dev);
helper->funcs = &msm_fbdev_helper_funcs;
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index 07d8cdd6bb2e..017411a0bf45 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -10,8 +10,10 @@
#include <linux/shmem_fs.h>
#include <linux/dma-buf.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_prime.h>
#include <drm/drm_file.h>
+#include <drm/drm_fourcc.h>
#include <trace/events/gpu_mem.h>
@@ -698,8 +700,32 @@ void msm_gem_unpin_iova(struct drm_gem_object *obj, struct drm_gpuvm *vm)
int msm_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- args->pitch = align_pitch(args->width, args->bpp);
- args->size = PAGE_ALIGN(args->pitch * args->height);
+ u32 fourcc;
+ u64 pitch_align;
+ int ret;
+
+ /*
+ * Adreno needs pitch aligned to 32 pixels. Compute the number
+ * of bytes for a block of 32 pixels at the given color format.
+ * Use the result as pitch alignment.
+ */
+ fourcc = drm_driver_color_mode_format(dev, args->bpp);
+ if (fourcc != DRM_FORMAT_INVALID) {
+ const struct drm_format_info *info;
+
+ info = drm_format_info(fourcc);
+ if (!info)
+ return -EINVAL;
+ pitch_align = drm_format_info_min_pitch(info, 0, 32);
+ } else {
+ pitch_align = round_up(args->width, 32) * DIV_ROUND_UP(args->bpp, SZ_8);
+ }
+ if (!pitch_align || pitch_align > U32_MAX)
+ return -EINVAL;
+ ret = drm_mode_size_dumb(dev, args, pitch_align, 0);
+ if (ret)
+ return ret;
+
return msm_gem_new_handle(dev, file, args->size,
MSM_BO_SCANOUT | MSM_BO_WC, &args->handle, "dumb");
}
@@ -1120,12 +1146,16 @@ static void msm_gem_free_object(struct drm_gem_object *obj)
put_pages(obj);
}
- if (obj->resv != &obj->_resv) {
+ /*
+ * In error paths, we could end up here before msm_gem_new_handle()
+ * has changed obj->resv to point to the shared resv. In this case,
+ * we don't want to drop a ref to the shared r_obj that we haven't
+ * taken yet.
+ */
+ if ((msm_obj->flags & MSM_BO_NO_SHARE) && (obj->resv != &obj->_resv)) {
struct drm_gem_object *r_obj =
container_of(obj->resv, struct drm_gem_object, _resv);
- WARN_ON(!(msm_obj->flags & MSM_BO_NO_SHARE));
-
/* Drop reference we hold to shared resv obj: */
drm_gem_object_put(r_obj);
}
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index 3ab3b27134f9..75d9f3574370 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -414,6 +414,11 @@ static void submit_attach_object_fences(struct msm_gem_submit *submit)
submit->user_fence,
DMA_RESV_USAGE_BOOKKEEP,
DMA_RESV_USAGE_BOOKKEEP);
+
+ last_fence = vm->last_fence;
+ vm->last_fence = dma_fence_unwrap_merge(submit->user_fence, last_fence);
+ dma_fence_put(last_fence);
+
return;
}
@@ -427,10 +432,6 @@ static void submit_attach_object_fences(struct msm_gem_submit *submit)
dma_resv_add_fence(obj->resv, submit->user_fence,
DMA_RESV_USAGE_READ);
}
-
- last_fence = vm->last_fence;
- vm->last_fence = dma_fence_unwrap_merge(submit->user_fence, last_fence);
- dma_fence_put(last_fence);
}
static int submit_bo(struct msm_gem_submit *submit, uint32_t idx,
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 8316af1723c2..71d5238437eb 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -462,15 +462,20 @@ struct op_arg {
bool kept;
};
-static void
+static int
vm_op_enqueue(struct op_arg *arg, struct msm_vm_op _op)
{
struct msm_vm_op *op = kmalloc(sizeof(*op), GFP_KERNEL);
+ if (!op)
+ return -ENOMEM;
+
*op = _op;
list_add_tail(&op->node, &arg->job->vm_ops);
if (op->obj)
drm_gem_object_get(op->obj);
+
+ return 0;
}
static struct drm_gpuva *
@@ -489,6 +494,7 @@ msm_gem_vm_sm_step_map(struct drm_gpuva_op *op, void *_arg)
struct drm_gpuva *vma;
struct sg_table *sgt;
unsigned prot;
+ int ret;
if (arg->kept)
return 0;
@@ -500,8 +506,6 @@ msm_gem_vm_sm_step_map(struct drm_gpuva_op *op, void *_arg)
vm_dbg("%p:%p:%p: %016llx %016llx", vma->vm, vma, vma->gem.obj,
vma->va.addr, vma->va.range);
- vma->flags = ((struct op_arg *)arg)->flags;
-
if (obj) {
sgt = to_msm_bo(obj)->sgt;
prot = msm_gem_prot(obj);
@@ -510,7 +514,7 @@ msm_gem_vm_sm_step_map(struct drm_gpuva_op *op, void *_arg)
prot = IOMMU_READ | IOMMU_WRITE;
}
- vm_op_enqueue(arg, (struct msm_vm_op){
+ ret = vm_op_enqueue(arg, (struct msm_vm_op){
.op = MSM_VM_OP_MAP,
.map = {
.sgt = sgt,
@@ -523,6 +527,10 @@ msm_gem_vm_sm_step_map(struct drm_gpuva_op *op, void *_arg)
.obj = vma->gem.obj,
});
+ if (ret)
+ return ret;
+
+ vma->flags = ((struct op_arg *)arg)->flags;
to_msm_vma(vma)->mapped = true;
return 0;
@@ -538,6 +546,7 @@ msm_gem_vm_sm_step_remap(struct drm_gpuva_op *op, void *arg)
struct drm_gpuvm_bo *vm_bo = orig_vma->vm_bo;
bool mapped = to_msm_vma(orig_vma)->mapped;
unsigned flags;
+ int ret;
vm_dbg("orig_vma: %p:%p:%p: %016llx %016llx", vm, orig_vma,
orig_vma->gem.obj, orig_vma->va.addr, orig_vma->va.range);
@@ -547,7 +556,7 @@ msm_gem_vm_sm_step_remap(struct drm_gpuva_op *op, void *arg)
drm_gpuva_op_remap_to_unmap_range(&op->remap, &unmap_start, &unmap_range);
- vm_op_enqueue(arg, (struct msm_vm_op){
+ ret = vm_op_enqueue(arg, (struct msm_vm_op){
.op = MSM_VM_OP_UNMAP,
.unmap = {
.iova = unmap_start,
@@ -557,6 +566,9 @@ msm_gem_vm_sm_step_remap(struct drm_gpuva_op *op, void *arg)
.obj = orig_vma->gem.obj,
});
+ if (ret)
+ return ret;
+
/*
* Part of this GEM obj is still mapped, but we're going to kill the
* existing VMA and replace it with one or two new ones (ie. two if
@@ -618,6 +630,7 @@ msm_gem_vm_sm_step_unmap(struct drm_gpuva_op *op, void *_arg)
struct msm_vm_bind_job *job = arg->job;
struct drm_gpuva *vma = op->unmap.va;
struct msm_gem_vma *msm_vma = to_msm_vma(vma);
+ int ret;
vm_dbg("%p:%p:%p: %016llx %016llx", vma->vm, vma, vma->gem.obj,
vma->va.addr, vma->va.range);
@@ -650,7 +663,7 @@ msm_gem_vm_sm_step_unmap(struct drm_gpuva_op *op, void *_arg)
if (!msm_vma->mapped)
goto out_close;
- vm_op_enqueue(arg, (struct msm_vm_op){
+ ret = vm_op_enqueue(arg, (struct msm_vm_op){
.op = MSM_VM_OP_UNMAP,
.unmap = {
.iova = vma->va.addr,
@@ -660,6 +673,9 @@ msm_gem_vm_sm_step_unmap(struct drm_gpuva_op *op, void *_arg)
.obj = vma->gem.obj,
});
+ if (ret)
+ return ret;
+
msm_vma->mapped = false;
out_close:
@@ -971,6 +987,7 @@ static int
lookup_op(struct msm_vm_bind_job *job, const struct drm_msm_vm_bind_op *op)
{
struct drm_device *dev = job->vm->drm;
+ struct msm_drm_private *priv = dev->dev_private;
int i = job->nr_ops++;
int ret = 0;
@@ -1017,6 +1034,11 @@ lookup_op(struct msm_vm_bind_job *job, const struct drm_msm_vm_bind_op *op)
break;
}
+ if ((op->op == MSM_VM_BIND_OP_MAP_NULL) &&
+ !adreno_smmu_has_prr(priv->gpu)) {
+ ret = UERR(EINVAL, dev, "PRR not supported\n");
+ }
+
return ret;
}
@@ -1421,7 +1443,7 @@ msm_ioctl_vm_bind(struct drm_device *dev, void *data, struct drm_file *file)
* Maybe we could allow just UNMAP ops? OTOH userspace should just
* immediately close the device file and all will be torn down.
*/
- if (to_msm_vm(ctx->vm)->unusable)
+ if (to_msm_vm(msm_context_vm(dev, ctx))->unusable)
return UERR(EPIPE, dev, "context is unusable");
/*
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 17759abc46d7..995549d0bbbc 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -197,8 +197,7 @@ static ssize_t msm_gpu_devcoredump_read(char *buffer, loff_t offset,
drm_printf(&p, "---\n");
drm_printf(&p, "kernel: " UTS_RELEASE "\n");
drm_printf(&p, "module: " KBUILD_MODNAME "\n");
- drm_printf(&p, "time: %lld.%09ld\n",
- state->time.tv_sec, state->time.tv_nsec);
+ drm_printf(&p, "time: %ptSp\n", &state->time);
if (state->comm)
drm_printf(&p, "comm: %s\n", state->comm);
if (state->cmd)
@@ -287,16 +286,17 @@ static void crashstate_get_bos(struct msm_gpu_state *state, struct msm_gem_submi
state->bos = kcalloc(cnt, sizeof(struct msm_gpu_state_bo), GFP_KERNEL);
- drm_gpuvm_for_each_va (vma, submit->vm) {
- bool dump = rd_full || (vma->flags & MSM_VMA_DUMP);
+ if (state->bos)
+ drm_gpuvm_for_each_va(vma, submit->vm) {
+ bool dump = rd_full || (vma->flags & MSM_VMA_DUMP);
- /* Skip MAP_NULL/PRR VMAs: */
- if (!vma->gem.obj)
- continue;
+ /* Skip MAP_NULL/PRR VMAs: */
+ if (!vma->gem.obj)
+ continue;
- msm_gpu_crashstate_get_bo(state, vma->gem.obj, vma->va.addr,
- dump, vma->gem.offset, vma->va.range);
- }
+ msm_gpu_crashstate_get_bo(state, vma->gem.obj, vma->va.addr,
+ dump, vma->gem.offset, vma->va.range);
+ }
drm_exec_fini(&exec);
} else {
@@ -348,6 +348,10 @@ static void crashstate_get_vm_logs(struct msm_gpu_state *state, struct msm_gem_v
state->vm_logs = kmalloc_array(
state->nr_vm_logs, sizeof(vm->log[0]), GFP_KERNEL);
+ if (!state->vm_logs) {
+ state->nr_vm_logs = 0;
+ }
+
for (int i = 0; i < state->nr_vm_logs; i++) {
int idx = (i + first) & vm_log_mask;
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index a597f2bee30b..2894fc118485 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -299,6 +299,17 @@ static inline struct msm_gpu *dev_to_gpu(struct device *dev)
return container_of(adreno_smmu, struct msm_gpu, adreno_smmu);
}
+static inline bool
+adreno_smmu_has_prr(struct msm_gpu *gpu)
+{
+ struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(&gpu->pdev->dev);
+
+ if (!adreno_smmu)
+ return false;
+
+ return adreno_smmu && adreno_smmu->set_prr_addr;
+}
+
/* It turns out that all targets use the same ringbuffer size */
#define MSM_GPU_RINGBUFFER_SZ SZ_32K
#define MSM_GPU_RINGBUFFER_BLKSIZE 32
diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c
index 0e18619f96cb..a188617653e8 100644
--- a/drivers/gpu/drm/msm/msm_iommu.c
+++ b/drivers/gpu/drm/msm/msm_iommu.c
@@ -338,6 +338,8 @@ msm_iommu_pagetable_prealloc_allocate(struct msm_mmu *mmu, struct msm_mmu_preall
ret = kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, p->count, p->pages);
if (ret != p->count) {
+ kfree(p->pages);
+ p->pages = NULL;
p->count = ret;
return -ENOMEM;
}
@@ -351,6 +353,9 @@ msm_iommu_pagetable_prealloc_cleanup(struct msm_mmu *mmu, struct msm_mmu_preallo
struct kmem_cache *pt_cache = get_pt_cache(mmu);
uint32_t remaining_pt_count = p->count - p->ptr;
+ if (!p->pages)
+ return;
+
if (p->count > 0)
trace_msm_mmu_prealloc_cleanup(p->count, remaining_pt_count);
diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c
index 2d0e3e784c04..bf9a33e925ac 100644
--- a/drivers/gpu/drm/msm/msm_mdss.c
+++ b/drivers/gpu/drm/msm/msm_mdss.c
@@ -553,8 +553,10 @@ static const struct msm_mdss_data data_153k6 = {
static const struct of_device_id mdss_dt_match[] = {
{ .compatible = "qcom,mdss", .data = &data_153k6 },
+ { .compatible = "qcom,glymur-mdss", .data = &data_57k },
{ .compatible = "qcom,msm8998-mdss", .data = &data_76k8 },
{ .compatible = "qcom,qcm2290-mdss", .data = &data_76k8 },
+ { .compatible = "qcom,qcs8300-mdss", .data = &data_74k },
{ .compatible = "qcom,sa8775p-mdss", .data = &data_74k },
{ .compatible = "qcom,sar2130p-mdss", .data = &data_74k },
{ .compatible = "qcom,sdm670-mdss", .data = &data_76k8 },
diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml
index 9459b6038217..3941e7510754 100644
--- a/drivers/gpu/drm/msm/registers/adreno/a6xx.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/a6xx.xml
@@ -7,9 +7,11 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<import file="adreno/adreno_pm4.xml"/>
<import file="adreno/a6xx_enums.xml"/>
<import file="adreno/a7xx_enums.xml"/>
+<import file="adreno/a8xx_enums.xml"/>
<import file="adreno/a6xx_perfcntrs.xml"/>
<import file="adreno/a7xx_perfcntrs.xml"/>
<import file="adreno/a6xx_descriptors.xml"/>
+<import file="adreno/a8xx_descriptors.xml"/>
<!--
Each register that is actually being used by driver should have "usage" defined,
@@ -84,29 +86,134 @@ by a particular renderpass/blit.
<bitfield name="CP_ILLEGAL_INSTR_ERROR_BV" pos="17" type="boolean" variants="A7XX-"/>
</bitset>
+ <bitset name="A8XX_CP_GLOBAL_INT_MASK" inline="no" varset="chip">
+ <bitfield name="HWFAULTBR" pos="0" type="boolean"/>
+ <bitfield name="HWFAULTBV" pos="1" type="boolean"/>
+ <bitfield name="HWFAULTLPAC" pos="2" type="boolean"/>
+ <bitfield name="HWFAULTAQE0" pos="3" type="boolean"/>
+ <bitfield name="HWFAULTAQE1" pos="4" type="boolean"/>
+ <bitfield name="HWFAULTDDEBR" pos="5" type="boolean"/>
+ <bitfield name="HWFAULTDDEBV" pos="6" type="boolean"/>
+ <bitfield name="SWFAULTBR" pos="16" type="boolean"/>
+ <bitfield name="SWFAULTBV" pos="17" type="boolean"/>
+ <bitfield name="SWFAULTLPAC" pos="18" type="boolean"/>
+ <bitfield name="SWFAULTAQE0" pos="19" type="boolean"/>
+ <bitfield name="SWFAULTAQE1" pos="20" type="boolean"/>
+ <bitfield name="SWFAULTDDEBR" pos="21" type="boolean"/>
+ <bitfield name="SWFAULTDDEBV" pos="22" type="boolean"/>
+ </bitset>
+
+ <bitset name="A8XX_CP_INTERRUPT_STATUS_MASK_PIPE" inline="no" varset="chip">
+ <bitfield name="CSFRBWRAP" pos="0" type="boolean"/>
+ <bitfield name="CSFIB1WRAP" pos="1" type="boolean"/>
+ <bitfield name="CSFIB2WRAP" pos="2" type="boolean"/>
+ <bitfield name="CSFIB3WRAP" pos="3" type="boolean"/>
+ <bitfield name="CSFSDSWRAP" pos="4" type="boolean"/>
+ <bitfield name="CSFMRBWRAP" pos="5" type="boolean"/>
+ <bitfield name="CSFVSDWRAP" pos="6" type="boolean"/>
+ <bitfield name="OPCODEERROR" pos="8" type="boolean"/>
+ <bitfield name="VSDPARITYERROR" pos="9" type="boolean"/>
+ <bitfield name="REGISTERPROTECTIONERROR" pos="10" type="boolean"/>
+ <bitfield name="ILLEGALINSTRUCTION" pos="11" type="boolean"/>
+ <bitfield name="SMMUFAULT" pos="12" type="boolean"/>
+ <bitfield name="VBIFRESPCLIENT" pos="13" type="boolean"/>
+ <bitfield name="VBIFRESPTYPE" pos="19" type="boolean"/>
+ <bitfield name="VBIFRESPREAD" pos="21" type="boolean"/>
+ <bitfield name="VBIFRESP" pos="22" type="boolean"/>
+ <bitfield name="RTWROVF" pos="23" type="boolean"/>
+ <bitfield name="LRZRTWROVF" pos="24" type="boolean"/>
+ <bitfield name="LRZRTREFCNTOVF" pos="25" type="boolean"/>
+ <bitfield name="LRZRTCLRRESMISS" pos="26" type="boolean"/>
+ </bitset>
+
+ <bitset name="A8XX_CP_HW_FAULT_STATUS_MASK_PIPE" inline="no" varset="chip">
+ <bitfield name="CSFRBFAULT" pos="0" type="boolean"/>
+ <bitfield name="CSFIB1FAULT" pos="1" type="boolean"/>
+ <bitfield name="CSFIB2FAULT" pos="2" type="boolean"/>
+ <bitfield name="CSFIB3FAULT" pos="3" type="boolean"/>
+ <bitfield name="CSFSDSFAULT" pos="4" type="boolean"/>
+ <bitfield name="CSFMRBFAULT" pos="5" type="boolean"/>
+ <bitfield name="CSFVSDFAULT" pos="6" type="boolean"/>
+ <bitfield name="SQEREADBURSTOVF" pos="8" type="boolean"/>
+ <bitfield name="EVENTENGINEOVF" pos="9" type="boolean"/>
+ <bitfield name="UCODEERROR" pos="10" type="boolean"/>
+ </bitset>
+
<reg64 offset="0x0800" name="CP_RB_BASE"/>
<reg32 offset="0x0802" name="CP_RB_CNTL"/>
+ <reg32 offset="0x0803" name="CP_RB_RPTR_WR" variants="A7XX-"/>
<reg64 offset="0x0804" name="CP_RB_RPTR_ADDR"/>
<reg32 offset="0x0806" name="CP_RB_RPTR"/>
<reg32 offset="0x0807" name="CP_RB_WPTR"/>
- <reg32 offset="0x0808" name="CP_SQE_CNTL"/>
- <reg32 offset="0x0812" name="CP_CP2GMU_STATUS">
+ <reg32 offset="0x0808" name="CP_RB_RPTR_ADDR_BV" variants="A8XX-"/>
+ <reg32 offset="0x080a" name="CP_RB_RPTR_BV" variants="A8XX-"/>
+ <reg64 offset="0x080b" name="CP_RB_BASE_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x080d" name="CP_RB_CNTL_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x080e" name="CP_RB_RPTR_WR_LPAC" variants="A8XX-"/>
+ <reg64 offset="0x080f" name="CP_RB_RPTR_ADDR_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x0811" name="CP_RB_RPTR_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x0812" name="CP_RB_WPTR_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x0814" name="CP_SMMU_STREAM_ID_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x0808" name="CP_SQE_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0815" name="CP_SQE_CNTL" variants="A8XX-"/>
+ <reg64 offset="0x0816" name="CP_SQE_INSTR_BASE" variants="A8XX-"/>
+ <reg64 offset="0x0818" name="CP_AQE_INSTR_BASE_0" variants="A8XX-"/>
+ <reg64 offset="0x081a" name="CP_AQE_INSTR_BASE_1" variants="A8XX-"/>
+ <reg32 offset="0x0812" name="CP_CP2GMU_STATUS" variants="A6XX-A7XX">
+ <!-- Note, layout defined by microcode -->
+ <bitfield name="IFPC" pos="0" type="boolean"/>
+ </reg32>
+ <reg32 offset="0x0822" name="CP_CP2GMU_STATUS" variants="A8XX-">
<bitfield name="IFPC" pos="0" type="boolean"/>
</reg32>
- <reg32 offset="0x0821" name="CP_HW_FAULT"/>
- <reg32 offset="0x0823" name="CP_INTERRUPT_STATUS" type="A6XX_CP_INT"/>
- <reg32 offset="0x0824" name="CP_PROTECT_STATUS"/>
- <reg32 offset="0x0825" name="CP_STATUS_1"/>
- <reg64 offset="0x0830" name="CP_SQE_INSTR_BASE"/>
- <reg32 offset="0x0840" name="CP_MISC_CNTL"/>
- <reg32 offset="0x0844" name="CP_APRIV_CNTL">
+ <reg32 offset="0x0821" name="CP_HW_FAULT" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0823" name="CP_INTERRUPT_STATUS" type="A6XX_CP_INT" variants="A6XX-A7XX"/>
+
+ <bitset name="a6xx_cp_protect_status" inline="yes">
+ <bitfield name="ADDR" low="0" high="17"/>
+ <bitfield name="READ" pos="20" type="boolean"/>
+ <bitfield name="CP_HALTED" pos="21" type="boolean"/>
+ <bitfield name="ACCESS_VIOLATION" pos="22" type="boolean"/>
+ </bitset>
+
+ <reg32 offset="0x0824" name="CP_PROTECT_STATUS" type="a6xx_cp_protect_status" variants="A6XX-A7XX"/>
+ <reg32 offset="0x084f" name="CP_PROTECT_STATUS_PIPE" type="a6xx_cp_protect_status" variants="A8XX-"/>
+ <reg32 offset="0x0825" name="CP_STATUS_1" variants="A6XX-A7XX"/>
+
+ <reg32 offset="0x0825" name="CP_SEMAPHORE_REG_0" variants="A8XX-"/>
+ <array offset="0x082a" name="CP_SCRATCH_GLOBAL" stride="1" length="4" variants="A8XX-">
+ <reg32 offset="0x0" name="REG"/>
+ </array>
+ <array offset="0x0830" name="CP_SCRATCH_PIPE" stride="1" length="5" variants="A8XX-">
+ <reg32 offset="0x0" name="REG"/>
+ </array>
+
+ <reg32 offset="0x0840" name="CP_RL_ERROR_DETAILS_0" variants="A8XX-"/>
+ <reg32 offset="0x0841" name="CP_RL_ERROR_DETAILS_1" variants="A8XX-"/>
+
+ <reg64 offset="0x0830" name="CP_SQE_INSTR_BASE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0840" name="CP_MISC_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x084c" name="CP_MISC_CNTL" variants="A8XX-"/>
+
+ <reg32 offset="0x08b0" name="CP_SQE_ICACHE_CNTL_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08b1" name="CP_SQE_DCACHE_CNTL_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08b3" name="CP_HW_FAULT_STATUS_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08b4" name="CP_HW_FAULT_STATUS_MASK_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08b5" name="CP_INTERRUPT_STATUS_GLOBAL" type="A8XX_CP_GLOBAL_INT_MASK" variants="A8XX-"/>
+ <reg32 offset="0x08b6" name="CP_INTERRUPT_STATUS_MASK_GLOBAL" type="A8XX_CP_GLOBAL_INT_MASK" variants="A8XX-"/>
+ <reg32 offset="0x08b7" name="CP_INTERRUPT_STATUS_PIPE" type="A8XX_CP_INTERRUPT_STATUS_MASK_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08b8" name="CP_INTERRUPT_STATUS_MASK_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08b9" name="CP_PIPE_STATUS_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08ba" name="CP_GPU_BATCH_ID_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08bb" name="CP_SQE_STATUS_PIPE" variants="A8XX-"/>
+
+ <bitset name="a6xx_cp_apriv_cntl" inline="yes">
<!-- Crashdumper writes -->
<bitfield pos="6" name="CDWRITE" type="boolean"/>
<!-- Crashdumper reads -->
<bitfield pos="5" name="CDREAD" type="boolean"/>
-
- <!-- 4 is unknown -->
-
+ <!-- CP Scratch reg copy to mem -->
+ <bitfield pos="4" name="SCRATCHWT" type="boolean"/>
<!-- RPTR shadow writes -->
<bitfield pos="3" name="RBRPWB" type="boolean"/>
<!-- Memory accesses from PM4 packets in the ringbuffer -->
@@ -115,11 +222,16 @@ by a particular renderpass/blit.
<bitfield pos="1" name="RBFETCH" type="boolean"/>
<!-- Instruction cache fetches -->
<bitfield pos="0" name="ICACHE" type="boolean"/>
- </reg32>
+ </bitset>
+
+ <reg32 offset="0x0844" name="CP_APRIV_CNTL" type="a6xx_cp_apriv_cntl" variants="A6XX-A7XX"/>
+ <reg32 offset="0x084d" name="CP_APRIV_CNTL_PIPE" type="a6xx_cp_apriv_cntl" variants="A8XX-"/>
+
<!-- Preemptions taking longer than this threshold increment PERF_CP_LONG_PREEMPTIONS: -->
- <reg32 offset="0x08C0" name="CP_PREEMPT_THRESHOLD"/>
+ <reg32 offset="0x08c0" name="CP_PREEMPT_THRESHOLD" variants="A6XX-A7XX"/>
+ <reg32 offset="0x08ec" name="CP_PREEMPT_THRESHOLD" variants="A8XX-"/>
<!-- all the threshold values seem to be in units of quad-dwords: -->
- <reg32 offset="0x08C1" name="CP_ROQ_THRESHOLDS_1">
+ <reg32 offset="0x08c1" name="CP_ROQ_THRESHOLDS_1" variants="A6XX">
<doc>
b0..7 identifies where MRB data starts (and RB data ends)
b8.15 identifies where VSD data starts (and MRB data ends)
@@ -131,7 +243,7 @@ by a particular renderpass/blit.
<bitfield name="IB1_START" low="16" high="23" shr="2"/>
<bitfield name="IB2_START" low="24" high="31" shr="2"/>
</reg32>
- <reg32 offset="0x08C2" name="CP_ROQ_THRESHOLDS_2">
+ <reg32 offset="0x08c2" name="CP_ROQ_THRESHOLDS_2" variants="A6XX">
<doc>
low bits identify where CP_SET_DRAW_STATE stateobj
processing starts (and IB2 data ends). I'm guessing
@@ -147,176 +259,293 @@ by a particular renderpass/blit.
<!-- total ROQ size: -->
<bitfield name="ROQ_SIZE" low="16" high="31" shr="2"/>
</reg32>
- <reg32 offset="0x08C3" name="CP_MEM_POOL_SIZE"/>
- <reg32 offset="0x0841" name="CP_CHICKEN_DBG"/>
- <reg32 offset="0x0842" name="CP_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
- <reg32 offset="0x0843" name="CP_DBG_ECO_CNTL"/>
- <reg32 offset="0x084F" name="CP_PROTECT_CNTL">
+ <reg32 offset="0x08C3" name="CP_MEM_POOL_SIZE" variants="A6XX"/>
+ <reg32 offset="0x0841" name="CP_CHICKEN_DBG" variants="A6XX-A7XX"/>
+ <reg32 offset="0x08b2" name="CP_CHICKEN_DBG_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x0842" name="CP_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
+ <reg32 offset="0x0843" name="CP_DBG_ECO_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x084b" name="CP_DBG_ECO_CNTL" variants="A8XX-"/>
+
+ <bitset name="a6xx_cp_protect_cntl" inline="yes">
<bitfield pos="3" name="LAST_SPAN_INF_RANGE" type="boolean"/>
<bitfield pos="1" name="ACCESS_FAULT_ON_VIOL_EN" type="boolean"/>
<bitfield pos="0" name="ACCESS_PROT_EN" type="boolean"/>
- </reg32>
+ </bitset>
- <array offset="0x0883" name="CP_SCRATCH" stride="1" length="8">
+ <reg32 offset="0x084f" name="CP_PROTECT_CNTL" type="a6xx_cp_protect_cntl" variants="A6XX-A7XX"/>
+ <bitset name="a8xx_cp_protect_cntl" inline="yes">
+ <bitfield name="HALT_SQE_RANGE" low="16" high="31"/>
+ <bitfield name="LAST_SPAN_INF_RANGE" pos="3" type="boolean"/>
+ <bitfield name="ACCESS_FAULT_ON_VIOL_EN" pos="1" type="boolean"/>
+ <bitfield name="ACCESS_PROT_EN" pos="0" type="boolean"/>
+ </bitset>
+
+ <reg32 offset="0x084e" name="CP_PROTECT_CNTL_PIPE" type="a8xx_cp_protect_cntl" variants="A8XX-"/>
+
+ <array offset="0x0883" name="CP_SCRATCH" stride="1" length="8" variants="A6XX-A7XX">
<reg32 offset="0x0" name="REG" type="uint"/>
</array>
- <array offset="0x0850" name="CP_PROTECT" stride="1" length="32">
+ <array offset="0x0850" name="CP_PROTECT" stride="1" length="32" variants="A6XX-A7XX">
+ <reg32 offset="0x0" name="REG" type="a6x_cp_protect"/>
+ </array>
+ <array offset="0x0850" name="CP_PROTECT_GLOBAL" stride="1" length="64" variants="A8XX-">
+ <reg32 offset="0x0" name="REG" type="a6x_cp_protect"/>
+ </array>
+ <array offset="0x08a0" name="CP_PROTECT_PIPE" stride="1" length="16" variants="A8XX-">
<reg32 offset="0x0" name="REG" type="a6x_cp_protect"/>
</array>
- <reg32 offset="0x08A0" name="CP_CONTEXT_SWITCH_CNTL">
+ <bitset name="a6xx_cp_context_switch_cntl" inline="yes">
<bitfield name="STOP" pos="0" type="boolean"/>
<bitfield name="LEVEL" low="6" high="7"/>
<bitfield name="USES_GMEM" pos="8" type="boolean"/>
<bitfield name="SKIP_SAVE_RESTORE" pos="9" type="boolean"/>
- </reg32>
- <reg64 offset="0x08A1" name="CP_CONTEXT_SWITCH_SMMU_INFO"/>
- <reg64 offset="0x08A3" name="CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR"/>
- <reg64 offset="0x08A5" name="CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR"/>
- <reg64 offset="0x08A7" name="CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR"/>
- <reg32 offset="0x08ab" name="CP_CONTEXT_SWITCH_LEVEL_STATUS" variants="A7XX-"/>
- <array offset="0x08D0" name="CP_PERFCTR_CP_SEL" stride="1" length="14"/>
- <array offset="0x08e0" name="CP_BV_PERFCTR_CP_SEL" stride="1" length="7" variants="A7XX-"/>
- <reg64 offset="0x0900" name="CP_CRASH_DUMP_SCRIPT_BASE"/>
- <reg32 offset="0x0902" name="CP_CRASH_DUMP_CNTL"/>
- <reg32 offset="0x0903" name="CP_CRASH_DUMP_STATUS"/>
- <reg32 offset="0x0908" name="CP_SQE_STAT_ADDR"/>
- <reg32 offset="0x0909" name="CP_SQE_STAT_DATA"/>
- <reg32 offset="0x090A" name="CP_DRAW_STATE_ADDR"/>
- <reg32 offset="0x090B" name="CP_DRAW_STATE_DATA"/>
- <reg32 offset="0x090C" name="CP_ROQ_DBG_ADDR"/>
- <reg32 offset="0x090D" name="CP_ROQ_DBG_DATA"/>
- <reg32 offset="0x090E" name="CP_MEM_POOL_DBG_ADDR"/>
- <reg32 offset="0x090F" name="CP_MEM_POOL_DBG_DATA"/>
- <reg32 offset="0x0910" name="CP_SQE_UCODE_DBG_ADDR"/>
- <reg32 offset="0x0911" name="CP_SQE_UCODE_DBG_DATA"/>
- <reg64 offset="0x0928" name="CP_IB1_BASE"/>
- <reg32 offset="0x092A" name="CP_IB1_REM_SIZE"/>
- <reg64 offset="0x092B" name="CP_IB2_BASE"/>
- <reg32 offset="0x092D" name="CP_IB2_REM_SIZE"/>
+ </bitset>
+
+ <reg32 offset="0x08a0" name="CP_CONTEXT_SWITCH_CNTL" type="a6xx_cp_context_switch_cntl" variants="A6XX-A7XX"/>
+ <reg32 offset="0x08c0" name="CP_CONTEXT_SWITCH_CNTL" type="a6xx_cp_context_switch_cntl" variants="A8XX-"/>
+
+ <reg64 offset="0x08a1" name="CP_CONTEXT_SWITCH_SMMU_INFO" variants="A6XX-A7XX"/>
+ <reg64 offset="0x08a3" name="CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR" variants="A6XX-A7XX"/>
+ <reg64 offset="0x08a5" name="CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR" variants="A6XX-A7XX"/>
+ <reg64 offset="0x08a7" name="CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR" variants="A6XX-A7XX"/>
+ <reg32 offset="0x08ab" name="CP_CONTEXT_SWITCH_LEVEL_STATUS" variants="A7XX"/>
+
+ <reg64 offset="0x08c1" name="CP_CONTEXT_SWITCH_SMMU_INFO" variants="A8XX-"/>
+ <reg64 offset="0x08c3" name="CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR" variants="A8XX-"/>
+ <reg64 offset="0x08c5" name="CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR" variants="A8XX-"/>
+ <reg64 offset="0x08c7" name="CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR" variants="A8XX-"/>
+ <reg32 offset="0x08cb" name="CP_CONTEXT_SWITCH_LEVEL_STATUS" variants="A8XX-"/>
+
+ <array offset="0x08d0" name="CP_PERFCTR_CP_SEL" stride="1" length="14" variants="A6XX-A7XX"/>
+ <array offset="0x08d0" name="CP_PERFCTR_CP_SEL" stride="1" length="21" variants="A8XX-"/>
+ <array offset="0x08e0" name="CP_BV_PERFCTR_CP_SEL" stride="1" length="7" variants="A7XX"/>
+ <reg64 offset="0x0900" name="CP_CRASH_DUMP_SCRIPT_BASE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0902" name="CP_CRASH_DUMP_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0903" name="CP_CRASH_DUMP_STATUS" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0842" name="CP_CRASH_DUMP_SCRIPT_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0844" name="CP_CRASH_DUMP_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x0845" name="CP_CRASH_DUMP_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0908" name="CP_SQE_STAT_ADDR" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0909" name="CP_SQE_STAT_DATA" variants="A6XX-A7XX"/>
+ <reg32 offset="0x090a" name="CP_DRAW_STATE_ADDR" variants="A6XX-A7XX"/>
+ <reg32 offset="0x090b" name="CP_DRAW_STATE_DATA" variants="A6XX-A7XX"/>
+ <reg32 offset="0x090c" name="CP_ROQ_DBG_ADDR" variants="A6XX-A7XX"/>
+ <reg32 offset="0x090d" name="CP_ROQ_DBG_DATA" variants="A6XX-A7XX"/>
+ <reg32 offset="0x090e" name="CP_MEM_POOL_DBG_ADDR" variants="A6XX-A7XX"/>
+ <reg32 offset="0x090f" name="CP_MEM_POOL_DBG_DATA" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0910" name="CP_SQE_UCODE_DBG_ADDR" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0911" name="CP_SQE_UCODE_DBG_DATA" variants="A6XX-A7XX"/>
+
+ <reg32 offset="0x08f0" name="CP_SQE_STAT_ADDR_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f1" name="CP_SQE_STAT_DATA_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f2" name="CP_DRAW_STATE_ADDR_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f3" name="CP_DRAW_STATE_DATA_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f4" name="CP_ROQ_DBG_ADDR_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f5" name="CP_ROQ_DBG_DATA_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f6" name="CP_MEM_POOL_DBG_ADDR_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f7" name="CP_MEM_POOL_DBG_DATA_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f8" name="CP_SQE_UCODE_DBG_ADDR_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08f9" name="CP_SQE_UCODE_DBG_DATA_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08fa" name="CP_RESOURCE_TABLE_DBG_ADDR_BV" variants="A8XX-"/>
+ <reg32 offset="0x08fb" name="CP_RESOURCE_TABLE_DBG_DATA_BV" variants="A8XX-"/>
+ <reg32 offset="0x08fc" name="CP_FIFO_DBG_ADDR_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x08fd" name="CP_FIFO_DBG_DATA_LPAC" variants="A8XX-"/>
+ <reg32 offset="0x08fe" name="CP_FIFO_DBG_ADDR_DDE_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x08ff" name="CP_FIFO_DBG_DATA_DDE_PIPE" variants="A8XX-"/>
+
+ <reg32 offset="0x0b00" name="CP_SLICE_MEM_POOL_DBG_ADDR_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x0b01" name="CP_SLICE_MEM_POOL_DBG_DATA_PIPE" variants="A8XX-"/>
+ <reg32 offset="0x0b93" name="CP_SLICE_CHICKEN_DBG_PIPE" variants="A8XX-"/>
+
+ <reg64 offset="0x0928" name="CP_IB1_BASE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x092a" name="CP_IB1_REM_SIZE" variants="A6XX-A7XX"/>
+ <reg64 offset="0x092b" name="CP_IB2_BASE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x092d" name="CP_IB2_REM_SIZE" variants="A6XX-A7XX"/>
<!-- SDS == CP_SET_DRAW_STATE: -->
- <reg64 offset="0x092e" name="CP_SDS_BASE"/>
- <reg32 offset="0x0930" name="CP_SDS_REM_SIZE"/>
+ <reg64 offset="0x092e" name="CP_SDS_BASE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0930" name="CP_SDS_REM_SIZE" variants="A6XX-A7XX"/>
<!-- MRB == MEM_READ_ADDR/$addr in SQE firmware -->
- <reg64 offset="0x0931" name="CP_MRB_BASE"/>
- <reg32 offset="0x0933" name="CP_MRB_REM_SIZE"/>
+ <reg64 offset="0x0931" name="CP_MRB_BASE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0933" name="CP_MRB_REM_SIZE" variants="A6XX-A7XX"/>
<!--
VSD == Visibility Stream Decode
This is used by CP to read the draw stream and skip empty draws
-->
- <reg64 offset="0x0934" name="CP_VSD_BASE"/>
+ <reg64 offset="0x0934" name="CP_VSD_BASE" variants="A6XX-A7XX"/>
+
+ <reg64 offset="0x0900" name="CP_IB1_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0902" name="CP_IB1_REM_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x0903" name="CP_IB1_INIT_SIZE" variants="A8XX-"/>
+ <reg64 offset="0x0904" name="CP_IB2_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0906" name="CP_IB2_REM_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x0907" name="CP_IB2_INIT_SIZE" variants="A8XX-"/>
+ <reg64 offset="0x0908" name="CP_IB3_BASE" variants="A8XX-"/>
+ <reg32 offset="0x090a" name="CP_IB3_REM_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x090b" name="CP_IB3_INIT_SIZE" variants="A8XX-"/>
+ <reg64 offset="0x090c" name="CP_SDS_BASE" variants="A8XX-"/>
+ <reg32 offset="0x090e" name="CP_SDS_REM_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x090f" name="CP_SDS_INIT_SIZE" variants="A8XX-"/>
+ <reg64 offset="0x0910" name="CP_MRB_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0912" name="CP_MRB_REM_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x0913" name="CP_MRB_INIT_SIZE" variants="A8XX-"/>
+ <reg64 offset="0x0914" name="CP_VSD_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0916" name="CP_VSD_REM_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x0917" name="CP_VSD_INIT_SIZE" variants="A8XX-"/>
<bitset name="a6xx_roq_status" inline="yes">
<bitfield name="RPTR" low="0" high="9"/>
<bitfield name="WPTR" low="16" high="25"/>
</bitset>
- <reg32 offset="0x0939" name="CP_ROQ_RB_STATUS" type="a6xx_roq_status"/>
- <reg32 offset="0x093a" name="CP_ROQ_IB1_STATUS" type="a6xx_roq_status"/>
- <reg32 offset="0x093b" name="CP_ROQ_IB2_STATUS" type="a6xx_roq_status"/>
- <reg32 offset="0x093c" name="CP_ROQ_SDS_STATUS" type="a6xx_roq_status"/>
- <reg32 offset="0x093d" name="CP_ROQ_MRB_STATUS" type="a6xx_roq_status"/>
- <reg32 offset="0x093e" name="CP_ROQ_VSD_STATUS" type="a6xx_roq_status"/>
-
- <reg32 offset="0x0943" name="CP_IB1_INIT_SIZE"/>
- <reg32 offset="0x0944" name="CP_IB2_INIT_SIZE"/>
- <reg32 offset="0x0945" name="CP_SDS_INIT_SIZE"/>
- <reg32 offset="0x0946" name="CP_MRB_INIT_SIZE"/>
- <reg32 offset="0x0947" name="CP_VSD_INIT_SIZE"/>
-
- <reg32 offset="0x0948" name="CP_ROQ_AVAIL_RB">
+ <reg32 offset="0x0939" name="CP_ROQ_RB_STATUS" type="a6xx_roq_status" variants="A6XX-A7XX"/>
+ <reg32 offset="0x093a" name="CP_ROQ_IB1_STATUS" type="a6xx_roq_status" variants="A6XX-A7XX"/>
+ <reg32 offset="0x093b" name="CP_ROQ_IB2_STATUS" type="a6xx_roq_status" variants="A6XX-A7XX"/>
+ <reg32 offset="0x093c" name="CP_ROQ_SDS_STATUS" type="a6xx_roq_status" variants="A6XX-A7XX"/>
+ <reg32 offset="0x093d" name="CP_ROQ_MRB_STATUS" type="a6xx_roq_status" variants="A6XX-A7XX"/>
+ <reg32 offset="0x093e" name="CP_ROQ_VSD_STATUS" type="a6xx_roq_status" variants="A6XX-A7XX"/>
+
+ <reg32 offset="0x0920" name="CP_ROQ_RB_STATUS" type="a6xx_roq_status" variants="A8XX-"/>
+ <reg32 offset="0x0921" name="CP_ROQ_IB1_STATUS" type="a6xx_roq_status" variants="A8XX-"/>
+ <reg32 offset="0x0922" name="CP_ROQ_IB2_STATUS" type="a6xx_roq_status" variants="A8XX-"/>
+ <reg32 offset="0x0923" name="CP_ROQ_IB3_STATUS" type="a6xx_roq_status" variants="A8XX-"/>
+ <reg32 offset="0x0924" name="CP_ROQ_SDS_STATUS" type="a6xx_roq_status" variants="A8XX-"/>
+ <reg32 offset="0x0925" name="CP_ROQ_MRB_STATUS" type="a6xx_roq_status" variants="A8XX-"/>
+ <reg32 offset="0x0926" name="CP_ROQ_VSD_STATUS" type="a6xx_roq_status" variants="A8XX-"/>
+
+ <reg32 offset="0x0943" name="CP_IB1_INIT_SIZE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0944" name="CP_IB2_INIT_SIZE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0945" name="CP_SDS_INIT_SIZE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0946" name="CP_MRB_INIT_SIZE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0947" name="CP_VSD_INIT_SIZE" variants="A6XX-A7XX"/>
+
+ <bitset name="a6xx_cp_roq_avail" inline="yes">
<doc>number of remaining dwords incl current dword being consumed?</doc>
<bitfield name="REM" low="16" high="31"/>
- </reg32>
- <reg32 offset="0x0949" name="CP_ROQ_AVAIL_IB1">
- <doc>number of remaining dwords incl current dword being consumed?</doc>
- <bitfield name="REM" low="16" high="31"/>
- </reg32>
- <reg32 offset="0x094a" name="CP_ROQ_AVAIL_IB2">
+ </bitset>
+
+ <reg32 offset="0x0948" name="CP_ROQ_AVAIL_RB" type="a6xx_cp_roq_avail" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0949" name="CP_ROQ_AVAIL_IB1" type="a6xx_cp_roq_avail" variants="A6XX-A7XX"/>
+ <reg32 offset="0x094a" name="CP_ROQ_AVAIL_IB2" type="a6xx_cp_roq_avail" variants="A6XX-A7XX"/>
+ <reg32 offset="0x094b" name="CP_ROQ_AVAIL_SDS" type="a6xx_cp_roq_avail" variants="A6XX-A7XX"/>
+ <reg32 offset="0x094c" name="CP_ROQ_AVAIL_MRB" type="a6xx_cp_roq_avail" variants="A6XX-A7XX"/>
+ <reg32 offset="0x094d" name="CP_ROQ_AVAIL_VSD" type="a6xx_cp_roq_avail" variants="A6XX-A7XX"/>
+
+ <reg32 offset="0x0918" name="CP_ROQ_AVAIL_RB" type="a6xx_cp_roq_avail" variants="A8XX-"/>
+ <reg32 offset="0x0919" name="CP_ROQ_AVAIL_IB1" type="a6xx_cp_roq_avail" variants="A8XX-"/>
+ <reg32 offset="0x091a" name="CP_ROQ_AVAIL_IB2" type="a6xx_cp_roq_avail" variants="A8XX-"/>
+ <reg32 offset="0x091b" name="CP_ROQ_AVAIL_IB3" type="a6xx_cp_roq_avail" variants="A8XX-"/>
+ <reg32 offset="0x091c" name="CP_ROQ_AVAIL_SDS" type="a6xx_cp_roq_avail" variants="A8XX-"/>
+ <reg32 offset="0x091d" name="CP_ROQ_AVAIL_MRB" type="a6xx_cp_roq_avail" variants="A8XX-"/>
+ <reg32 offset="0x091e" name="CP_ROQ_AVAIL_VSD" type="a6xx_cp_roq_avail" variants="A8XX-"/>
+
+ <bitset name="a7xx_aperture_cntl" inline="yes">
+ <bitfield name="PIPE" low="12" high="13" type="adreno_pipe"/>
+ <bitfield name="CLUSTER" low="8" high="10" type="a7xx_cluster"/>
+ <bitfield name="CONTEXT" low="4" high="5"/>
+ </bitset>
+ <reg64 offset="0x0980" name="CP_ALWAYS_ON_COUNTER" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0982" name="CP_ALWAYS_ON_CONTEXT" variants="A6XX-A7XX"/>
+ <reg64 offset="0x08e7" name="CP_ALWAYS_ON_COUNTER" variants="A8XX-"/>
+ <reg64 offset="0x08e9" name="CP_ALWAYS_ON_CONTEXT" variants="A8XX-"/>
+ <reg32 offset="0x098d" name="CP_AHB_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0838" name="CP_AHB_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x0A00" name="CP_APERTURE_CNTL_HOST" variants="A6XX"/>
+ <reg32 offset="0x0A00" name="CP_APERTURE_CNTL_HOST" type="a7xx_aperture_cntl" variants="A7XX"/>
+ <reg32 offset="0x0A01" name="CP_APERTURE_CNTL_SQE" variants="A6XX"/>
+ <reg32 offset="0x0A03" name="CP_APERTURE_CNTL_CD" variants="A6XX"/>
+ <reg32 offset="0x0A03" name="CP_APERTURE_CNTL_CD" type="a7xx_aperture_cntl" variants="A7XX"/>
+
+ <array offset="0x0a9c" name="CP_RESERVED_REG" stride="1" length="4" variants="A7XX"/>
+ <array offset="0x0958" name="CP_RESERVED_REG" stride="1" length="4" variants="A8XX-"/>
+
+ <bitset name="a8xx_aperture_cntl" inline="yes">
+ <bitfield name="CONTEXTID3D" low="4" high="5"/>
+ <bitfield name="CLUSTERID" low="8" high="11"/>
+ <bitfield name="PIPEID" low="12" high="15"/>
+ <bitfield name="SLICEID" low="16" high="18"/>
+ <bitfield name="USESLICEID" pos="23" type="boolean"/>
+ </bitset>
+
+ <reg32 offset="0x081c" name="CP_APERTURE_CNTL_HOST" type="a8xx_aperture_cntl" variants="A8XX-"/>
+ <reg32 offset="0x081d" name="CP_APERTURE_CNTL_GMU" type="a8xx_aperture_cntl" variants="A8XX-"/>
+ <reg32 offset="0x081e" name="CP_APERTURE_CNTL_CD" type="a8xx_aperture_cntl" variants="A8XX-"/>
+
+ <reg32 offset="0x0a61" name="CP_BV_PROTECT_STATUS" variants="A7XX"/>
+ <reg32 offset="0x0a64" name="CP_BV_HW_FAULT" variants="A7XX"/>
+ <reg32 offset="0x0a66" name="CP_BV_RB_RPTR" variants="A7XX"/>
+ <reg64 offset="0x0a6d" name="CP_BV_IB1_BASE" variants="A7XX"/>
+ <reg32 offset="0x0a70" name="CP_BV_IB1_REM_SIZE" variants="A7XX"/>
+ <reg64 offset="0x0a71" name="CP_BV_IB2_BASE" variants="A7XX"/>
+ <reg32 offset="0x0a74" name="CP_BV_IB2_REM_SIZE" variants="A7XX"/>
+ <reg32 offset="0x0a81" name="CP_BV_DRAW_STATE_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0a82" name="CP_BV_DRAW_STATE_DATA" variants="A7XX"/>
+ <reg32 offset="0x0a83" name="CP_BV_ROQ_DBG_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0a84" name="CP_BV_ROQ_DBG_DATA" variants="A7XX"/>
+ <reg32 offset="0x0a85" name="CP_BV_SQE_UCODE_DBG_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0a86" name="CP_BV_SQE_UCODE_DBG_DATA" variants="A7XX"/>
+ <reg32 offset="0x0a87" name="CP_BV_SQE_STAT_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0a88" name="CP_BV_SQE_STAT_DATA" variants="A7XX"/>
+
+ <reg32 offset="0x0a8f" name="CP_BV_ROQ_AVAIL_RB" variants="A7XX">
<doc>number of remaining dwords incl current dword being consumed?</doc>
<bitfield name="REM" low="16" high="31"/>
</reg32>
- <reg32 offset="0x094b" name="CP_ROQ_AVAIL_SDS">
+ <reg32 offset="0x0a90" name="CP_BV_ROQ_AVAIL_IB1" variants="A7XX">
<doc>number of remaining dwords incl current dword being consumed?</doc>
<bitfield name="REM" low="16" high="31"/>
</reg32>
- <reg32 offset="0x094c" name="CP_ROQ_AVAIL_MRB">
- <doc>number of dwords that have already been read but haven't been consumed by $addr</doc>
- <bitfield name="REM" low="16" high="31"/>
- </reg32>
- <reg32 offset="0x094d" name="CP_ROQ_AVAIL_VSD">
+ <reg32 offset="0x0a91" name="CP_BV_ROQ_AVAIL_IB2" variants="A7XX">
<doc>number of remaining dwords incl current dword being consumed?</doc>
<bitfield name="REM" low="16" high="31"/>
</reg32>
- <bitset name="a7xx_aperture_cntl" inline="yes">
- <bitfield name="PIPE" low="12" high="13" type="a7xx_pipe"/>
- <bitfield name="CLUSTER" low="8" high="10" type="a7xx_cluster"/>
- <bitfield name="CONTEXT" low="4" high="5"/>
- </bitset>
- <reg64 offset="0x0980" name="CP_ALWAYS_ON_COUNTER"/>
- <reg32 offset="0x098D" name="CP_AHB_CNTL"/>
- <reg32 offset="0x0A00" name="CP_APERTURE_CNTL_HOST" variants="A6XX"/>
- <reg32 offset="0x0A00" name="CP_APERTURE_CNTL_HOST" type="a7xx_aperture_cntl" variants="A7XX-"/>
- <reg32 offset="0x0A01" name="CP_APERTURE_CNTL_SQE" variants="A6XX"/>
- <reg32 offset="0x0A03" name="CP_APERTURE_CNTL_CD" variants="A6XX"/>
- <reg32 offset="0x0A03" name="CP_APERTURE_CNTL_CD" type="a7xx_aperture_cntl" variants="A7XX-"/>
-
- <reg32 offset="0x0a61" name="CP_BV_PROTECT_STATUS" variants="A7XX-"/>
- <reg32 offset="0x0a64" name="CP_BV_HW_FAULT" variants="A7XX-"/>
- <reg32 offset="0x0a81" name="CP_BV_DRAW_STATE_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0a82" name="CP_BV_DRAW_STATE_DATA" variants="A7XX-"/>
- <reg32 offset="0x0a83" name="CP_BV_ROQ_DBG_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0a84" name="CP_BV_ROQ_DBG_DATA" variants="A7XX-"/>
- <reg32 offset="0x0a85" name="CP_BV_SQE_UCODE_DBG_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0a86" name="CP_BV_SQE_UCODE_DBG_DATA" variants="A7XX-"/>
- <reg32 offset="0x0a87" name="CP_BV_SQE_STAT_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0a88" name="CP_BV_SQE_STAT_DATA" variants="A7XX-"/>
- <reg32 offset="0x0a96" name="CP_BV_MEM_POOL_DBG_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0a97" name="CP_BV_MEM_POOL_DBG_DATA" variants="A7XX-"/>
- <reg64 offset="0x0a98" name="CP_BV_RB_RPTR_ADDR" variants="A7XX-"/>
-
- <reg32 offset="0x0a9a" name="CP_RESOURCE_TABLE_DBG_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0a9b" name="CP_RESOURCE_TABLE_DBG_DATA" variants="A7XX-"/>
- <reg32 offset="0x0ad0" name="CP_BV_APRIV_CNTL" variants="A7XX-"/>
- <reg32 offset="0x0ada" name="CP_BV_CHICKEN_DBG" variants="A7XX-"/>
-
- <reg32 offset="0x0b0a" name="CP_LPAC_DRAW_STATE_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0b0b" name="CP_LPAC_DRAW_STATE_DATA" variants="A7XX-"/>
- <reg32 offset="0x0b0c" name="CP_LPAC_ROQ_DBG_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0b27" name="CP_SQE_AC_UCODE_DBG_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0b28" name="CP_SQE_AC_UCODE_DBG_DATA" variants="A7XX-"/>
- <reg32 offset="0x0b29" name="CP_SQE_AC_STAT_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0b2a" name="CP_SQE_AC_STAT_DATA" variants="A7XX-"/>
-
- <reg32 offset="0x0b31" name="CP_LPAC_APRIV_CNTL" variants="A7XX-"/>
- <reg32 offset="0x0B34" name="CP_LPAC_PROG_FIFO_SIZE"/>
- <reg32 offset="0x0b35" name="CP_LPAC_ROQ_DBG_DATA" variants="A7XX-"/>
- <reg32 offset="0x0b36" name="CP_LPAC_FIFO_DBG_DATA" variants="A7XX-"/>
- <reg32 offset="0x0b40" name="CP_LPAC_FIFO_DBG_ADDR" variants="A7XX-"/>
- <reg32 offset="0x0b81" name="CP_LPAC_SQE_CNTL"/>
- <reg64 offset="0x0b82" name="CP_LPAC_SQE_INSTR_BASE"/>
-
- <reg64 offset="0x0b70" name="CP_AQE_INSTR_BASE_0" variants="A7XX-"/>
- <reg64 offset="0x0b72" name="CP_AQE_INSTR_BASE_1" variants="A7XX-"/>
- <reg32 offset="0x0b78" name="CP_AQE_APRIV_CNTL" variants="A7XX-"/>
-
- <reg32 offset="0x0ba8" name="CP_AQE_ROQ_DBG_ADDR_0" variants="A7XX-"/>
- <reg32 offset="0x0ba9" name="CP_AQE_ROQ_DBG_ADDR_1" variants="A7XX-"/>
- <reg32 offset="0x0bac" name="CP_AQE_ROQ_DBG_DATA_0" variants="A7XX-"/>
- <reg32 offset="0x0bad" name="CP_AQE_ROQ_DBG_DATA_1" variants="A7XX-"/>
- <reg32 offset="0x0bb0" name="CP_AQE_UCODE_DBG_ADDR_0" variants="A7XX-"/>
- <reg32 offset="0x0bb1" name="CP_AQE_UCODE_DBG_ADDR_1" variants="A7XX-"/>
- <reg32 offset="0x0bb4" name="CP_AQE_UCODE_DBG_DATA_0" variants="A7XX-"/>
- <reg32 offset="0x0bb5" name="CP_AQE_UCODE_DBG_DATA_1" variants="A7XX-"/>
- <reg32 offset="0x0bb8" name="CP_AQE_STAT_ADDR_0" variants="A7XX-"/>
- <reg32 offset="0x0bb9" name="CP_AQE_STAT_ADDR_1" variants="A7XX-"/>
- <reg32 offset="0x0bbc" name="CP_AQE_STAT_DATA_0" variants="A7XX-"/>
- <reg32 offset="0x0bbd" name="CP_AQE_STAT_DATA_1" variants="A7XX-"/>
-
- <reg32 offset="0x0C01" name="VSC_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
- <reg32 offset="0x0018" name="RBBM_GPR0_CNTL"/>
- <reg32 offset="0x0201" name="RBBM_INT_0_STATUS" type="A6XX_RBBM_INT_0_MASK"/>
- <reg32 offset="0x0210" name="RBBM_STATUS">
+ <reg32 offset="0x0a96" name="CP_BV_MEM_POOL_DBG_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0a97" name="CP_BV_MEM_POOL_DBG_DATA" variants="A7XX"/>
+ <reg64 offset="0x0a98" name="CP_BV_RB_RPTR_ADDR" variants="A7XX"/>
+
+ <reg32 offset="0x0a9a" name="CP_RESOURCE_TABLE_DBG_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0a9b" name="CP_RESOURCE_TABLE_DBG_DATA" variants="A7XX"/>
+ <reg32 offset="0x0ad0" name="CP_BV_APRIV_CNTL" variants="A7XX"/>
+ <reg32 offset="0x0ada" name="CP_BV_CHICKEN_DBG" variants="A7XX"/>
+
+ <reg32 offset="0x0b0a" name="CP_LPAC_DRAW_STATE_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0b0b" name="CP_LPAC_DRAW_STATE_DATA" variants="A7XX"/>
+ <reg32 offset="0x0b0c" name="CP_LPAC_ROQ_DBG_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0b27" name="CP_SQE_AC_UCODE_DBG_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0b28" name="CP_SQE_AC_UCODE_DBG_DATA" variants="A7XX"/>
+ <reg32 offset="0x0b29" name="CP_SQE_AC_STAT_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0b2a" name="CP_SQE_AC_STAT_DATA" variants="A7XX"/>
+
+ <reg32 offset="0x0b31" name="CP_LPAC_APRIV_CNTL" variants="A7XX"/>
+ <reg32 offset="0x0b34" name="CP_LPAC_PROG_FIFO_SIZE" variants="A7XX"/>
+ <reg32 offset="0x0b35" name="CP_LPAC_ROQ_DBG_DATA" variants="A7XX"/>
+ <reg32 offset="0x0b36" name="CP_LPAC_FIFO_DBG_DATA" variants="A7XX"/>
+ <reg32 offset="0x0b40" name="CP_LPAC_FIFO_DBG_ADDR" variants="A7XX"/>
+ <reg32 offset="0x0b81" name="CP_LPAC_SQE_CNTL" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0b82" name="CP_LPAC_SQE_INSTR_BASE" variants="A6XX-A7XX"/>
+
+ <reg64 offset="0x0b70" name="CP_AQE_INSTR_BASE_0" variants="A7XX"/>
+ <reg64 offset="0x0b72" name="CP_AQE_INSTR_BASE_1" variants="A7XX"/>
+ <reg32 offset="0x0b78" name="CP_AQE_APRIV_CNTL" variants="A7XX"/>
+
+ <reg32 offset="0x0ba8" name="CP_AQE_ROQ_DBG_ADDR_0" variants="A7XX"/>
+ <reg32 offset="0x0ba9" name="CP_AQE_ROQ_DBG_ADDR_1" variants="A7XX"/>
+ <reg32 offset="0x0bac" name="CP_AQE_ROQ_DBG_DATA_0" variants="A7XX"/>
+ <reg32 offset="0x0bad" name="CP_AQE_ROQ_DBG_DATA_1" variants="A7XX"/>
+ <reg32 offset="0x0bb0" name="CP_AQE_UCODE_DBG_ADDR_0" variants="A7XX"/>
+ <reg32 offset="0x0bb1" name="CP_AQE_UCODE_DBG_ADDR_1" variants="A7XX"/>
+ <reg32 offset="0x0bb4" name="CP_AQE_UCODE_DBG_DATA_0" variants="A7XX"/>
+ <reg32 offset="0x0bb5" name="CP_AQE_UCODE_DBG_DATA_1" variants="A7XX"/>
+ <reg32 offset="0x0bb8" name="CP_AQE_STAT_ADDR_0" variants="A7XX"/>
+ <reg32 offset="0x0bb9" name="CP_AQE_STAT_ADDR_1" variants="A7XX"/>
+ <reg32 offset="0x0bbc" name="CP_AQE_STAT_DATA_0" variants="A7XX"/>
+ <reg32 offset="0x0bbd" name="CP_AQE_STAT_DATA_1" variants="A7XX"/>
+
+ <reg32 offset="0x0C01" name="VSC_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
+ <reg32 offset="0x0018" name="RBBM_GPR0_CNTL" variants="A6XX"/>
+ <reg32 offset="0x0201" name="RBBM_INT_0_STATUS" type="A6XX_RBBM_INT_0_MASK" variants="A6XX-A7XX"/>
+ <reg32 offset="0x006a" name="RBBM_INT_0_STATUS" type="A6XX_RBBM_INT_0_MASK" variants="A8XX-"/>
+ <reg32 offset="0x0210" name="RBBM_STATUS" variants="A6XX-A7XX">
<bitfield pos="23" name="GPU_BUSY_IGN_AHB" type="boolean"/>
<bitfield pos="22" name="GPU_BUSY_IGN_AHB_CP" type="boolean"/>
<bitfield pos="21" name="HLSQ_BUSY" type="boolean"/>
@@ -342,22 +571,59 @@ by a particular renderpass/blit.
<bitfield pos="1" name="CP_AHB_BUSY_CP_MASTER" type="boolean"/>
<bitfield pos="0" name="CP_AHB_BUSY_CX_MASTER" type="boolean"/>
</reg32>
- <reg32 offset="0x0211" name="RBBM_STATUS1"/>
- <reg32 offset="0x0212" name="RBBM_STATUS2"/>
- <reg32 offset="0x0213" name="RBBM_STATUS3">
+ <reg32 offset="0x0211" name="RBBM_STATUS1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0212" name="RBBM_STATUS2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0213" name="RBBM_STATUS3" variants="A6XX-A7XX">
<bitfield pos="24" name="SMMU_STALLED_ON_FAULT" type="boolean"/>
</reg32>
- <reg32 offset="0x0215" name="RBBM_VBIF_GX_RESET_STATUS"/>
- <reg32 offset="0x0260" name="RBBM_CLOCK_MODE_CP" variants="A7XX-"/>
- <reg32 offset="0x0284" name="RBBM_CLOCK_MODE_BV_LRZ" variants="A7XX-"/>
- <reg32 offset="0x0285" name="RBBM_CLOCK_MODE_BV_GRAS" variants="A7XX-"/>
- <reg32 offset="0x0286" name="RBBM_CLOCK_MODE2_GRAS" variants="A7XX-"/>
- <reg32 offset="0x0287" name="RBBM_CLOCK_MODE_BV_VFD" variants="A7XX-"/>
- <reg32 offset="0x0288" name="RBBM_CLOCK_MODE_BV_GPC" variants="A7XX-"/>
+ <reg32 offset="0x012" name="RBBM_STATUS" variants="A8XX-">
+ <bitfield pos="23" name="GPU_BUSY_IGN_AHB" type="boolean"/>
+ <bitfield pos="22" name="GPU_BUSY_IGN_AHB_CP" type="boolean"/>
+ <bitfield pos="21" name="SLICE_BUSY_IGN_CP" type="boolean"/>
+ <bitfield pos="20" name="CP_SLICE_BUSY" type="boolean"/>
+ <bitfield pos="19" name="UNSLICE_BUSY_IGN_AHB" type="boolean"/>
+ <bitfield pos="18" name="UNSLICE_BUSY_IGN_AHB_CP" type="boolean"/>
+ <bitfield pos="17" name="CP_SLICE_RL_BUSY" type="boolean"/>
+ <bitfield pos="14" name="UNSLICE_TOP_BUSY" type="boolean"/>
+ <bitfield pos="13" name="UFC_BUSY" type="boolean"/>
+ <bitfield pos="12" name="HLSQ_BUSY" type="boolean"/>
+ <bitfield pos="11" name="VSC_BUSY" type="boolean"/>
+ <bitfield pos="10" name="UCHE_BUSY" type="boolean"/>
+ <bitfield pos="9" name="VPC_BUSY" type="boolean"/>
+ <bitfield pos="8" name="PC_BUSY" type="boolean"/>
+ <bitfield pos="7" name="CMP_BUSY" type="boolean"/>
+ <bitfield pos="6" name="DCMP_BUSY" type="boolean"/>
+ <bitfield pos="5" name="VBIF_GX_BUSY" type="boolean"/>
+ <bitfield pos="4" name="DBGC_PERF_BUSY" type="boolean"/>
+ <bitfield pos="3" name="GFX_DBGC_BUSY" type="boolean"/>
+ <bitfield pos="2" name="CP_BUSY" type="boolean"/>
+ <bitfield pos="1" name="CP_AHB_BUSY_CP_MASTER" type="boolean"/>
+ <bitfield pos="0" name="CP_AHB_BUSY_CX_MASTER" type="boolean"/>
+ </reg32>
+ <reg32 offset="0x013" name="RBBM_STATUS1" variants="A8XX-"/>
+ <reg32 offset="0x015" name="RBBM_GFX_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x016" name="RBBM_GFX_STATUS1" variants="A8XX-"/>
+ <reg32 offset="0x018" name="RBBM_LPAC_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x01a" name="RBBM_GFX_BR_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x01c" name="RBBM_GFX_BV_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x01e" name="RBBM_MISC_STATUS" variants="A8XX-">
+ <bitfield pos="0" name="SMMU_STALLED_ON_FAULT" type="boolean"/>
+ </reg32>
+
+ <reg32 offset="0x0215" name="RBBM_VBIF_GX_RESET_STATUS" variants="A6XX"/>
- <reg32 offset="0x02c0" name="RBBM_SW_FUSE_INT_STATUS" variants="A7XX-"/>
- <reg32 offset="0x02c1" name="RBBM_SW_FUSE_INT_MASK" variants="A7XX-"/>
+ <reg32 offset="0x0260" name="RBBM_CLOCK_MODE_CP" variants="A7XX"/>
+ <reg32 offset="0x0284" name="RBBM_CLOCK_MODE_BV_LRZ" variants="A7XX"/>
+ <reg32 offset="0x0285" name="RBBM_CLOCK_MODE_BV_GRAS" variants="A7XX"/>
+ <reg32 offset="0x0286" name="RBBM_CLOCK_MODE2_GRAS" variants="A7XX"/>
+ <reg32 offset="0x0287" name="RBBM_CLOCK_MODE_BV_VFD" variants="A7XX"/>
+ <reg32 offset="0x0288" name="RBBM_CLOCK_MODE_BV_GPC" variants="A7XX"/>
+
+ <reg32 offset="0x02c0" name="RBBM_SW_FUSE_INT_STATUS" variants="A7XX"/>
+ <reg32 offset="0x02c1" name="RBBM_SW_FUSE_INT_MASK" variants="A7XX"/>
+ <reg32 offset="0x0071" name="RBBM_SW_FUSE_INT_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0072" name="RBBM_SW_FUSE_INT_MASK" variants="A8XX-"/>
<array offset="0x0400" name="RBBM_PERFCTR_CP" stride="2" length="14" variants="A6XX"/>
<array offset="0x041c" name="RBBM_PERFCTR_RBBM" stride="2" length="4" variants="A6XX"/>
@@ -376,49 +642,96 @@ by a particular renderpass/blit.
<array offset="0x04ea" name="RBBM_PERFCTR_LRZ" stride="2" length="4" variants="A6XX"/>
<array offset="0x04f2" name="RBBM_PERFCTR_CMP" stride="2" length="4" variants="A6XX"/>
- <array offset="0x0300" name="RBBM_PERFCTR_CP" stride="2" length="14" variants="A7XX-"/>
- <array offset="0x031c" name="RBBM_PERFCTR_RBBM" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x0324" name="RBBM_PERFCTR_PC" stride="2" length="8" variants="A7XX-"/>
- <array offset="0x0334" name="RBBM_PERFCTR_VFD" stride="2" length="8" variants="A7XX-"/>
- <array offset="0x0344" name="RBBM_PERFCTR_HLSQ" stride="2" length="6" variants="A7XX-"/>
- <array offset="0x0350" name="RBBM_PERFCTR_VPC" stride="2" length="6" variants="A7XX-"/>
- <array offset="0x035c" name="RBBM_PERFCTR_CCU" stride="2" length="5" variants="A7XX-"/>
- <array offset="0x0366" name="RBBM_PERFCTR_TSE" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x036e" name="RBBM_PERFCTR_RAS" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x0376" name="RBBM_PERFCTR_UCHE" stride="2" length="12" variants="A7XX-"/>
- <array offset="0x038e" name="RBBM_PERFCTR_TP" stride="2" length="12" variants="A7XX-"/>
- <array offset="0x03a6" name="RBBM_PERFCTR_SP" stride="2" length="24" variants="A7XX-"/>
- <array offset="0x03d6" name="RBBM_PERFCTR_RB" stride="2" length="8" variants="A7XX-"/>
- <array offset="0x03e6" name="RBBM_PERFCTR_VSC" stride="2" length="2" variants="A7XX-"/>
- <array offset="0x03ea" name="RBBM_PERFCTR_LRZ" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x03f2" name="RBBM_PERFCTR_CMP" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x03fa" name="RBBM_PERFCTR_UFC" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x0410" name="RBBM_PERFCTR2_HLSQ" stride="2" length="6" variants="A7XX-"/>
- <array offset="0x041c" name="RBBM_PERFCTR2_CP" stride="2" length="7" variants="A7XX-"/>
- <array offset="0x042a" name="RBBM_PERFCTR2_SP" stride="2" length="12" variants="A7XX-"/>
- <array offset="0x0442" name="RBBM_PERFCTR2_TP" stride="2" length="6" variants="A7XX-"/>
- <array offset="0x044e" name="RBBM_PERFCTR2_UFC" stride="2" length="2" variants="A7XX-"/>
- <array offset="0x0460" name="RBBM_PERFCTR_BV_PC" stride="2" length="8" variants="A7XX-"/>
- <array offset="0x0470" name="RBBM_PERFCTR_BV_VFD" stride="2" length="8" variants="A7XX-"/>
- <array offset="0x0480" name="RBBM_PERFCTR_BV_VPC" stride="2" length="6" variants="A7XX-"/>
- <array offset="0x048c" name="RBBM_PERFCTR_BV_TSE" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x0494" name="RBBM_PERFCTR_BV_RAS" stride="2" length="4" variants="A7XX-"/>
- <array offset="0x049c" name="RBBM_PERFCTR_BV_LRZ" stride="2" length="4" variants="A7XX-"/>
-
- <reg32 offset="0x0500" name="RBBM_PERFCTR_CNTL"/>
- <reg32 offset="0x0501" name="RBBM_PERFCTR_LOAD_CMD0"/>
- <reg32 offset="0x0502" name="RBBM_PERFCTR_LOAD_CMD1"/>
- <reg32 offset="0x0503" name="RBBM_PERFCTR_LOAD_CMD2"/>
- <reg32 offset="0x0504" name="RBBM_PERFCTR_LOAD_CMD3"/>
- <reg32 offset="0x0505" name="RBBM_PERFCTR_LOAD_VALUE_LO"/>
- <reg32 offset="0x0506" name="RBBM_PERFCTR_LOAD_VALUE_HI"/>
- <array offset="0x0507" name="RBBM_PERFCTR_RBBM_SEL" stride="1" length="4"/>
- <reg32 offset="0x050B" name="RBBM_PERFCTR_GPU_BUSY_MASKED"/>
- <reg32 offset="0x050e" name="RBBM_PERFCTR_SRAM_INIT_CMD"/>
- <reg32 offset="0x050f" name="RBBM_PERFCTR_SRAM_INIT_STATUS"/>
- <reg32 offset="0x0533" name="RBBM_ISDB_CNT"/>
- <reg32 offset="0x0534" name="RBBM_NC_MODE_CNTL"/>
- <reg32 offset="0x0535" name="RBBM_SNAPSHOT_STATUS" variants="A7XX-"/>
+ <array offset="0x0300" name="RBBM_PERFCTR_CP" stride="2" length="14" variants="A7XX"/>
+ <array offset="0x031c" name="RBBM_PERFCTR_RBBM" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x0324" name="RBBM_PERFCTR_PC" stride="2" length="8" variants="A7XX"/>
+ <array offset="0x0334" name="RBBM_PERFCTR_VFD" stride="2" length="8" variants="A7XX"/>
+ <array offset="0x0344" name="RBBM_PERFCTR_HLSQ" stride="2" length="6" variants="A7XX"/>
+ <array offset="0x0350" name="RBBM_PERFCTR_VPC" stride="2" length="6" variants="A7XX"/>
+ <array offset="0x035c" name="RBBM_PERFCTR_CCU" stride="2" length="5" variants="A7XX"/>
+ <array offset="0x0366" name="RBBM_PERFCTR_TSE" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x036e" name="RBBM_PERFCTR_RAS" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x0376" name="RBBM_PERFCTR_UCHE" stride="2" length="12" variants="A7XX"/>
+ <array offset="0x038e" name="RBBM_PERFCTR_TP" stride="2" length="12" variants="A7XX"/>
+ <array offset="0x03a6" name="RBBM_PERFCTR_SP" stride="2" length="24" variants="A7XX"/>
+ <array offset="0x03d6" name="RBBM_PERFCTR_RB" stride="2" length="8" variants="A7XX"/>
+ <array offset="0x03e6" name="RBBM_PERFCTR_VSC" stride="2" length="2" variants="A7XX"/>
+ <array offset="0x03ea" name="RBBM_PERFCTR_LRZ" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x03f2" name="RBBM_PERFCTR_CMP" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x03fa" name="RBBM_PERFCTR_UFC" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x0410" name="RBBM_PERFCTR2_HLSQ" stride="2" length="6" variants="A7XX"/>
+ <array offset="0x041c" name="RBBM_PERFCTR2_CP" stride="2" length="7" variants="A7XX"/>
+ <array offset="0x042a" name="RBBM_PERFCTR2_SP" stride="2" length="12" variants="A7XX"/>
+ <array offset="0x0442" name="RBBM_PERFCTR2_TP" stride="2" length="6" variants="A7XX"/>
+ <array offset="0x044e" name="RBBM_PERFCTR2_UFC" stride="2" length="2" variants="A7XX"/>
+ <array offset="0x0460" name="RBBM_PERFCTR_BV_PC" stride="2" length="8" variants="A7XX"/>
+ <array offset="0x0470" name="RBBM_PERFCTR_BV_VFD" stride="2" length="8" variants="A7XX"/>
+ <array offset="0x0480" name="RBBM_PERFCTR_BV_VPC" stride="2" length="6" variants="A7XX"/>
+ <array offset="0x048c" name="RBBM_PERFCTR_BV_TSE" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x0494" name="RBBM_PERFCTR_BV_RAS" stride="2" length="4" variants="A7XX"/>
+ <array offset="0x049c" name="RBBM_PERFCTR_BV_LRZ" stride="2" length="4" variants="A7XX"/>
+
+ <array offset="0x01b0" name="RBBM_PERFCTR_CP" stride="2" length="14" variants="A8XX"/>
+ <array offset="0x01cc" name="RBBM_PERFCTR_RBBM" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x01d4" name="RBBM_PERFCTR_PC" stride="2" length="8" variants="A8XX"/>
+ <array offset="0x01e4" name="RBBM_PERFCTR_VFD" stride="2" length="8" variants="A8XX"/>
+ <array offset="0x01f4" name="RBBM_PERFCTR_HLSQ" stride="2" length="6" variants="A8XX"/>
+ <array offset="0x0200" name="RBBM_PERFCTR_VPC" stride="2" length="6" variants="A8XX"/>
+ <array offset="0x020c" name="RBBM_PERFCTR_CCU" stride="2" length="5" variants="A8XX"/>
+ <array offset="0x0216" name="RBBM_PERFCTR_TSE" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x021e" name="RBBM_PERFCTR_RAS" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x0226" name="RBBM_PERFCTR_UCHE" stride="2" length="24" variants="A8XX"/>
+ <array offset="0x0256" name="RBBM_PERFCTR_TP" stride="2" length="12" variants="A8XX"/>
+ <array offset="0x026e" name="RBBM_PERFCTR_SP" stride="2" length="24" variants="A8XX"/>
+ <array offset="0x029e" name="RBBM_PERFCTR_RB" stride="2" length="8" variants="A8XX"/>
+ <array offset="0x02ae" name="RBBM_PERFCTR_VSC" stride="2" length="2" variants="A8XX"/>
+ <array offset="0x02b2" name="RBBM_PERFCTR_LRZ" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x02ba" name="RBBM_PERFCTR_CMP" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x02c2" name="RBBM_PERFCTR_UFC" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x02e2" name="RBBM_PERFCTR2_HLSQ" stride="2" length="6" variants="A8XX"/>
+ <array offset="0x02ee" name="RBBM_PERFCTR2_CP" stride="2" length="7" variants="A8XX"/>
+ <array offset="0x02fc" name="RBBM_PERFCTR2_SP" stride="2" length="12" variants="A8XX"/>
+ <array offset="0x0314" name="RBBM_PERFCTR2_TP" stride="2" length="8" variants="A8XX"/>
+ <array offset="0x0324" name="RBBM_PERFCTR2_UFC" stride="2" length="2" variants="A8XX"/>
+ <array offset="0x0328" name="RBBM_PERFCTR_BV_PC" stride="2" length="8" variants="A8XX"/>
+ <array offset="0x0338" name="RBBM_PERFCTR_BV_VFD" stride="2" length="8" variants="A8XX"/>
+ <array offset="0x0348" name="RBBM_PERFCTR_BV_VPC" stride="2" length="6" variants="A8XX"/>
+ <array offset="0x0354" name="RBBM_PERFCTR_BV_TSE" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x035c" name="RBBM_PERFCTR_BV_RAS" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x0364" name="RBBM_PERFCTR_BV_LRZ" stride="2" length="4" variants="A8XX"/>
+ <array offset="0x036c" name="RBBM_PERFCTR_BV_CCU" stride="2" length="3" variants="A8XX"/>
+ <array offset="0x0372" name="RBBM_PERFCTR_BV_RB" stride="2" length="6" variants="A8XX"/>
+
+ <reg32 offset="0x0500" name="RBBM_PERFCTR_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0460" name="RBBM_PERFCTR_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x0501" name="RBBM_PERFCTR_LOAD_CMD0" variants="A6XX"/>
+ <reg32 offset="0x0502" name="RBBM_PERFCTR_LOAD_CMD1" variants="A6XX"/>
+ <reg32 offset="0x0503" name="RBBM_PERFCTR_LOAD_CMD2" variants="A6XX"/>
+ <reg32 offset="0x0504" name="RBBM_PERFCTR_LOAD_CMD3" variants="A6XX"/>
+ <reg64 offset="0x0505" name="RBBM_PERFCTR_LOAD_VALUE" variants="A6XX"/>
+ <array offset="0x0507" name="RBBM_PERFCTR_RBBM_SEL" stride="1" length="4" variants="A6XX-A7XX"/>
+ <array offset="0x0441" name="RBBM_PERFCTR_RBBM_SEL" stride="1" length="4" variants="A8XX-"/>
+ <reg32 offset="0x050B" name="RBBM_PERFCTR_GPU_BUSY_MASKED" variants="A6XX-A7XX"/>
+ <reg32 offset="0x019e" name="RBBM_PERFCTR_GPU_BUSY_MASKED" variants="A8XX-"/>
+ <reg32 offset="0x050e" name="RBBM_PERFCTR_SRAM_INIT_CMD" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0449" name="RBBM_PERFCTR_SRAM_INIT_CMD" variants="A8XX-"/>
+ <reg32 offset="0x050f" name="RBBM_PERFCTR_SRAM_INIT_STATUS" variants="A6XX-A7XX"/>
+ <reg32 offset="0x019f" name="RBBM_PERFCTR_SRAM_INIT_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x01a1" name="RBBM_PERFCTR_FLUSH_HOST_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x044c" name="RBBM_PERFCTR_FLUSH_HOST_CMD" variants="A8XX-"/>
+ <reg32 offset="0x0533" name="RBBM_ISDB_CNT" variants="A6XX-A7XX"/>
+ <reg32 offset="0x002d" name="RBBM_ISDB_CNT" variants="A8XX-"/>
+ <reg32 offset="0x0534" name="RBBM_NC_MODE_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0440" name="RBBM_NC_MODE_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x0535" name="RBBM_SNAPSHOT_STATUS" variants="A7XX"/>
+ <reg32 offset="0x002e" name="RBBM_SNAPSHOT_STATUS" variants="A8XX-"/>
+
+ <reg32 offset="0x500" name="RBBM_SLICE_PERFCTR_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x58f" name="RBBM_SLICE_INTERFACE_HANG_INT_CNTL" variants="A8XX-"/>
+ <array offset="0x5e0" name="RBBM_SLICE_PERFCTR_RBBM_SEL" stride="1" length="4" variants="A8XX-"/>
+ <reg32 offset="0x5e8" name="RBBM_SLICE_PERFCTR_SRAM_INIT_CMD" variants="A8XX-"/>
+ <reg32 offset="0x5eb" name="RBBM_SLICE_PERFCTR_FLUSH_HOST_CMD" variants="A8XX-"/>
+ <reg32 offset="0x5ec" name="RBBM_SLICE_NC_MODE_CNTL" variants="A8XX-"/>
<!---
This block of registers aren't tied to perf counters. They
@@ -426,170 +739,211 @@ by a particular renderpass/blit.
vertices in, number of primnitives assembled etc.
-->
- <reg64 offset="0x0540" name="RBBM_PIPESTAT_IAVERTICES"/>
- <reg64 offset="0x0542" name="RBBM_PIPESTAT_IAPRIMITIVES"/>
- <reg64 offset="0x0544" name="RBBM_PIPESTAT_VSINVOCATIONS"/>
- <reg64 offset="0x0546" name="RBBM_PIPESTAT_HSINVOCATIONS"/>
- <reg64 offset="0x0548" name="RBBM_PIPESTAT_DSINVOCATIONS"/>
- <reg64 offset="0x054a" name="RBBM_PIPESTAT_GSINVOCATIONS"/>
- <reg64 offset="0x054c" name="RBBM_PIPESTAT_GSPRIMITIVES"/>
- <reg64 offset="0x054e" name="RBBM_PIPESTAT_CINVOCATIONS"/>
- <reg64 offset="0x0550" name="RBBM_PIPESTAT_CPRIMITIVES"/>
- <reg64 offset="0x0552" name="RBBM_PIPESTAT_PSINVOCATIONS"/>
- <reg64 offset="0x0554" name="RBBM_PIPESTAT_CSINVOCATIONS"/>
+ <reg64 offset="0x0540" name="RBBM_PIPESTAT_IAVERTICES" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0542" name="RBBM_PIPESTAT_IAPRIMITIVES" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0544" name="RBBM_PIPESTAT_VSINVOCATIONS" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0546" name="RBBM_PIPESTAT_HSINVOCATIONS" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0548" name="RBBM_PIPESTAT_DSINVOCATIONS" variants="A6XX-A7XX"/>
+ <reg64 offset="0x054a" name="RBBM_PIPESTAT_GSINVOCATIONS" variants="A6XX-A7XX"/>
+ <reg64 offset="0x054c" name="RBBM_PIPESTAT_GSPRIMITIVES" variants="A6XX-A7XX"/>
+ <reg64 offset="0x054e" name="RBBM_PIPESTAT_CINVOCATIONS" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0550" name="RBBM_PIPESTAT_CPRIMITIVES" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0552" name="RBBM_PIPESTAT_PSINVOCATIONS" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0554" name="RBBM_PIPESTAT_CSINVOCATIONS" variants="A6XX-A7XX"/>
+
+ <reg64 offset="0x0380" name="RBBM_PIPESTAT_IAVERTICES" variants="A8XX-"/>
+ <reg64 offset="0x0382" name="RBBM_PIPESTAT_IAPRIMITIVES" variants="A8XX-"/>
+ <reg64 offset="0x0384" name="RBBM_PIPESTAT_VSINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x0386" name="RBBM_PIPESTAT_GSINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x0388" name="RBBM_PIPESTAT_GSPRIMITIVES" variants="A8XX-"/>
+ <reg64 offset="0x038a" name="RBBM_PIPESTAT_CINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x038c" name="RBBM_PIPESTAT_CPRIMITIVES" variants="A8XX-"/>
+ <reg64 offset="0x038e" name="RBBM_PIPESTAT_PSINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x0390" name="RBBM_PIPESTAT_HSINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x0392" name="RBBM_PIPESTAT_DSINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x0394" name="RBBM_PIPESTAT_CSINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x0396" name="RBBM_PIPESTAT_ASINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x0398" name="RBBM_PIPESTAT_MSINVOCATIONS" variants="A8XX-"/>
+ <reg64 offset="0x039a" name="RBBM_PIPESTAT_MSPRIMITIVES" variants="A8XX-"/>
<reg32 offset="0xF400" name="RBBM_SECVID_TRUST_CNTL"/>
<reg64 offset="0xF800" name="RBBM_SECVID_TSB_TRUSTED_BASE"/>
<reg32 offset="0xF802" name="RBBM_SECVID_TSB_TRUSTED_SIZE"/>
<reg32 offset="0xF803" name="RBBM_SECVID_TSB_CNTL"/>
- <reg32 offset="0xF810" name="RBBM_SECVID_TSB_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
+ <reg32 offset="0xF810" name="RBBM_SECVID_TSB_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
<reg64 offset="0xfc00" name="RBBM_SECVID_TSB_STATUS" variants="A7XX-"/>
- <reg32 offset="0x00010" name="RBBM_VBIF_CLIENT_QOS_CNTL"/>
- <reg32 offset="0x00011" name="RBBM_GBIF_CLIENT_QOS_CNTL"/>
- <reg32 offset="0x00016" name="RBBM_GBIF_HALT"/>
- <reg32 offset="0x00017" name="RBBM_GBIF_HALT_ACK"/>
- <reg32 offset="0x0001c" name="RBBM_WAIT_FOR_GPU_IDLE_CMD">
+ <reg32 offset="0x00010" name="RBBM_VBIF_CLIENT_QOS_CNTL" variants="A6XX"/>
+ <reg32 offset="0x00011" name="RBBM_GBIF_CLIENT_QOS_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00008" name="RBBM_GBIF_CLIENT_QOS_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x00016" name="RBBM_GBIF_HALT" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0000a" name="RBBM_GBIF_HALT" variants="A8XX-"/>
+ <reg32 offset="0x00017" name="RBBM_GBIF_HALT_ACK" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0000b" name="RBBM_GBIF_HALT_ACK" variants="A8XX-"/>
+ <reg32 offset="0x0001c" name="RBBM_WAIT_FOR_GPU_IDLE_CMD" variants="A6XX">
<bitfield pos="0" name="WAIT_GPU_IDLE" type="boolean"/>
</reg32>
- <reg32 offset="0x00016" name="RBBM_GBIF_HALT" variants="A7XX-"/>
- <reg32 offset="0x00017" name="RBBM_GBIF_HALT_ACK" variants="A7XX-"/>
- <reg32 offset="0x0001f" name="RBBM_INTERFACE_HANG_INT_CNTL"/>
- <reg32 offset="0x00037" name="RBBM_INT_CLEAR_CMD" type="A6XX_RBBM_INT_0_MASK"/>
- <reg32 offset="0x00038" name="RBBM_INT_0_MASK" type="A6XX_RBBM_INT_0_MASK"/>
- <reg32 offset="0x0003a" name="RBBM_INT_2_MASK" variants="A7XX-"/>
- <reg32 offset="0x00042" name="RBBM_SP_HYST_CNT"/>
- <reg32 offset="0x00043" name="RBBM_SW_RESET_CMD"/>
- <reg32 offset="0x00044" name="RBBM_RAC_THRESHOLD_CNT"/>
- <reg32 offset="0x00045" name="RBBM_BLOCK_SW_RESET_CMD"/>
- <reg32 offset="0x00046" name="RBBM_BLOCK_SW_RESET_CMD2"/>
- <reg32 offset="0x000ad" name="RBBM_CLOCK_CNTL_GLOBAL" variants="A7XX-"/>
- <reg32 offset="0x000ae" name="RBBM_CLOCK_CNTL"/>
- <reg32 offset="0x000b0" name="RBBM_CLOCK_CNTL_SP0"/>
- <reg32 offset="0x000b1" name="RBBM_CLOCK_CNTL_SP1"/>
- <reg32 offset="0x000b2" name="RBBM_CLOCK_CNTL_SP2"/>
- <reg32 offset="0x000b3" name="RBBM_CLOCK_CNTL_SP3"/>
- <reg32 offset="0x000b4" name="RBBM_CLOCK_CNTL2_SP0"/>
- <reg32 offset="0x000b5" name="RBBM_CLOCK_CNTL2_SP1"/>
- <reg32 offset="0x000b6" name="RBBM_CLOCK_CNTL2_SP2"/>
- <reg32 offset="0x000b7" name="RBBM_CLOCK_CNTL2_SP3"/>
- <reg32 offset="0x000b8" name="RBBM_CLOCK_DELAY_SP0"/>
- <reg32 offset="0x000b9" name="RBBM_CLOCK_DELAY_SP1"/>
- <reg32 offset="0x000ba" name="RBBM_CLOCK_DELAY_SP2"/>
- <reg32 offset="0x000bb" name="RBBM_CLOCK_DELAY_SP3"/>
- <reg32 offset="0x000bc" name="RBBM_CLOCK_HYST_SP0"/>
- <reg32 offset="0x000bd" name="RBBM_CLOCK_HYST_SP1"/>
- <reg32 offset="0x000be" name="RBBM_CLOCK_HYST_SP2"/>
- <reg32 offset="0x000bf" name="RBBM_CLOCK_HYST_SP3"/>
- <reg32 offset="0x000c0" name="RBBM_CLOCK_CNTL_TP0"/>
- <reg32 offset="0x000c1" name="RBBM_CLOCK_CNTL_TP1"/>
- <reg32 offset="0x000c2" name="RBBM_CLOCK_CNTL_TP2"/>
- <reg32 offset="0x000c3" name="RBBM_CLOCK_CNTL_TP3"/>
- <reg32 offset="0x000c4" name="RBBM_CLOCK_CNTL2_TP0"/>
- <reg32 offset="0x000c5" name="RBBM_CLOCK_CNTL2_TP1"/>
- <reg32 offset="0x000c6" name="RBBM_CLOCK_CNTL2_TP2"/>
- <reg32 offset="0x000c7" name="RBBM_CLOCK_CNTL2_TP3"/>
- <reg32 offset="0x000c8" name="RBBM_CLOCK_CNTL3_TP0"/>
- <reg32 offset="0x000c9" name="RBBM_CLOCK_CNTL3_TP1"/>
- <reg32 offset="0x000ca" name="RBBM_CLOCK_CNTL3_TP2"/>
- <reg32 offset="0x000cb" name="RBBM_CLOCK_CNTL3_TP3"/>
- <reg32 offset="0x000cc" name="RBBM_CLOCK_CNTL4_TP0"/>
- <reg32 offset="0x000cd" name="RBBM_CLOCK_CNTL4_TP1"/>
- <reg32 offset="0x000ce" name="RBBM_CLOCK_CNTL4_TP2"/>
- <reg32 offset="0x000cf" name="RBBM_CLOCK_CNTL4_TP3"/>
- <reg32 offset="0x000d0" name="RBBM_CLOCK_DELAY_TP0"/>
- <reg32 offset="0x000d1" name="RBBM_CLOCK_DELAY_TP1"/>
- <reg32 offset="0x000d2" name="RBBM_CLOCK_DELAY_TP2"/>
- <reg32 offset="0x000d3" name="RBBM_CLOCK_DELAY_TP3"/>
- <reg32 offset="0x000d4" name="RBBM_CLOCK_DELAY2_TP0"/>
- <reg32 offset="0x000d5" name="RBBM_CLOCK_DELAY2_TP1"/>
- <reg32 offset="0x000d6" name="RBBM_CLOCK_DELAY2_TP2"/>
- <reg32 offset="0x000d7" name="RBBM_CLOCK_DELAY2_TP3"/>
- <reg32 offset="0x000d8" name="RBBM_CLOCK_DELAY3_TP0"/>
- <reg32 offset="0x000d9" name="RBBM_CLOCK_DELAY3_TP1"/>
- <reg32 offset="0x000da" name="RBBM_CLOCK_DELAY3_TP2"/>
- <reg32 offset="0x000db" name="RBBM_CLOCK_DELAY3_TP3"/>
- <reg32 offset="0x000dc" name="RBBM_CLOCK_DELAY4_TP0"/>
- <reg32 offset="0x000dd" name="RBBM_CLOCK_DELAY4_TP1"/>
- <reg32 offset="0x000de" name="RBBM_CLOCK_DELAY4_TP2"/>
- <reg32 offset="0x000df" name="RBBM_CLOCK_DELAY4_TP3"/>
- <reg32 offset="0x000e0" name="RBBM_CLOCK_HYST_TP0"/>
- <reg32 offset="0x000e1" name="RBBM_CLOCK_HYST_TP1"/>
- <reg32 offset="0x000e2" name="RBBM_CLOCK_HYST_TP2"/>
- <reg32 offset="0x000e3" name="RBBM_CLOCK_HYST_TP3"/>
- <reg32 offset="0x000e4" name="RBBM_CLOCK_HYST2_TP0"/>
- <reg32 offset="0x000e5" name="RBBM_CLOCK_HYST2_TP1"/>
- <reg32 offset="0x000e6" name="RBBM_CLOCK_HYST2_TP2"/>
- <reg32 offset="0x000e7" name="RBBM_CLOCK_HYST2_TP3"/>
- <reg32 offset="0x000e8" name="RBBM_CLOCK_HYST3_TP0"/>
- <reg32 offset="0x000e9" name="RBBM_CLOCK_HYST3_TP1"/>
- <reg32 offset="0x000ea" name="RBBM_CLOCK_HYST3_TP2"/>
- <reg32 offset="0x000eb" name="RBBM_CLOCK_HYST3_TP3"/>
- <reg32 offset="0x000ec" name="RBBM_CLOCK_HYST4_TP0"/>
- <reg32 offset="0x000ed" name="RBBM_CLOCK_HYST4_TP1"/>
- <reg32 offset="0x000ee" name="RBBM_CLOCK_HYST4_TP2"/>
- <reg32 offset="0x000ef" name="RBBM_CLOCK_HYST4_TP3"/>
- <reg32 offset="0x000f0" name="RBBM_CLOCK_CNTL_RB0"/>
- <reg32 offset="0x000f1" name="RBBM_CLOCK_CNTL_RB1"/>
- <reg32 offset="0x000f2" name="RBBM_CLOCK_CNTL_RB2"/>
- <reg32 offset="0x000f3" name="RBBM_CLOCK_CNTL_RB3"/>
- <reg32 offset="0x000f4" name="RBBM_CLOCK_CNTL2_RB0"/>
- <reg32 offset="0x000f5" name="RBBM_CLOCK_CNTL2_RB1"/>
- <reg32 offset="0x000f6" name="RBBM_CLOCK_CNTL2_RB2"/>
- <reg32 offset="0x000f7" name="RBBM_CLOCK_CNTL2_RB3"/>
- <reg32 offset="0x000f8" name="RBBM_CLOCK_CNTL_CCU0"/>
- <reg32 offset="0x000f9" name="RBBM_CLOCK_CNTL_CCU1"/>
- <reg32 offset="0x000fa" name="RBBM_CLOCK_CNTL_CCU2"/>
- <reg32 offset="0x000fb" name="RBBM_CLOCK_CNTL_CCU3"/>
- <reg32 offset="0x00100" name="RBBM_CLOCK_HYST_RB_CCU0"/>
- <reg32 offset="0x00101" name="RBBM_CLOCK_HYST_RB_CCU1"/>
- <reg32 offset="0x00102" name="RBBM_CLOCK_HYST_RB_CCU2"/>
- <reg32 offset="0x00103" name="RBBM_CLOCK_HYST_RB_CCU3"/>
- <reg32 offset="0x00104" name="RBBM_CLOCK_CNTL_RAC"/>
- <reg32 offset="0x00105" name="RBBM_CLOCK_CNTL2_RAC"/>
- <reg32 offset="0x00106" name="RBBM_CLOCK_DELAY_RAC"/>
- <reg32 offset="0x00107" name="RBBM_CLOCK_HYST_RAC"/>
- <reg32 offset="0x00108" name="RBBM_CLOCK_CNTL_TSE_RAS_RBBM"/>
- <reg32 offset="0x00109" name="RBBM_CLOCK_DELAY_TSE_RAS_RBBM"/>
- <reg32 offset="0x0010a" name="RBBM_CLOCK_HYST_TSE_RAS_RBBM"/>
- <reg32 offset="0x0010b" name="RBBM_CLOCK_CNTL_UCHE"/>
- <reg32 offset="0x0010c" name="RBBM_CLOCK_CNTL2_UCHE"/>
- <reg32 offset="0x0010d" name="RBBM_CLOCK_CNTL3_UCHE"/>
- <reg32 offset="0x0010e" name="RBBM_CLOCK_CNTL4_UCHE"/>
- <reg32 offset="0x0010f" name="RBBM_CLOCK_DELAY_UCHE"/>
- <reg32 offset="0x00110" name="RBBM_CLOCK_HYST_UCHE"/>
- <reg32 offset="0x00111" name="RBBM_CLOCK_MODE_VFD"/>
- <reg32 offset="0x00112" name="RBBM_CLOCK_DELAY_VFD"/>
- <reg32 offset="0x00113" name="RBBM_CLOCK_HYST_VFD"/>
- <reg32 offset="0x00114" name="RBBM_CLOCK_MODE_GPC"/>
- <reg32 offset="0x00115" name="RBBM_CLOCK_DELAY_GPC"/>
- <reg32 offset="0x00116" name="RBBM_CLOCK_HYST_GPC"/>
- <reg32 offset="0x00117" name="RBBM_CLOCK_DELAY_HLSQ_2"/>
- <reg32 offset="0x00118" name="RBBM_CLOCK_CNTL_GMU_GX"/>
- <reg32 offset="0x00119" name="RBBM_CLOCK_DELAY_GMU_GX"/>
- <reg32 offset="0x0011a" name="RBBM_CLOCK_HYST_GMU_GX"/>
- <reg32 offset="0x0011b" name="RBBM_CLOCK_MODE_HLSQ"/>
- <reg32 offset="0x0011c" name="RBBM_CLOCK_DELAY_HLSQ"/>
- <reg32 offset="0x0011d" name="RBBM_CLOCK_HYST_HLSQ"/>
- <reg32 offset="0x0011e" name="RBBM_CGC_GLOBAL_LOAD_CMD" variants="A7XX-"/>
- <reg32 offset="0x0011f" name="RBBM_CGC_P2S_TRIG_CMD" variants="A7XX-"/>
- <reg32 offset="0x00120" name="RBBM_CLOCK_CNTL_TEX_FCHE"/>
- <reg32 offset="0x00121" name="RBBM_CLOCK_DELAY_TEX_FCHE"/>
+ <reg32 offset="0x01a" name="RBBM_WAIT_IDLE_CLOCKS_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x01b" name="RBBM_WAIT_IDLE_CLOCKS_CNTL2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x010" name="RBBM_WAIT_IDLE_CLOCKS_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x011" name="RBBM_WAIT_IDLE_CLOCKS_CNTL2" variants="A8XX-"/>
+
+ <reg32 offset="0x0001f" name="RBBM_INTERFACE_HANG_INT_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0002f" name="RBBM_INTERFACE_HANG_INT_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x00037" name="RBBM_INT_CLEAR_CMD" type="A6XX_RBBM_INT_0_MASK" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00061" name="RBBM_INT_CLEAR_CMD" type="A6XX_RBBM_INT_0_MASK" variants="A8XX-"/>
+ <reg32 offset="0x00038" name="RBBM_INT_0_MASK" type="A6XX_RBBM_INT_0_MASK" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00062" name="RBBM_INT_0_MASK" type="A6XX_RBBM_INT_0_MASK" variants="A8XX-"/>
+ <reg32 offset="0x0003a" name="RBBM_INT_2_MASK" variants="A7XX"/>
+ <reg32 offset="0x00064" name="RBBM_INT_2_MASK" variants="A8XX-"/>
+ <reg32 offset="0x00042" name="RBBM_SP_HYST_CNT" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00043" name="RBBM_SW_RESET_CMD" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00073" name="RBBM_SW_RESET_CMD" variants="A8XX-"/>
+ <reg32 offset="0x00044" name="RBBM_RAC_THRESHOLD_CNT" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00029" name="RBBM_RAC_THRESHOLD_CNT" variants="A8XX-"/>
+ <reg32 offset="0x00045" name="RBBM_BLOCK_SW_RESET_CMD" variants="A6XX"/>
+ <reg32 offset="0x00046" name="RBBM_BLOCK_SW_RESET_CMD2" variants="A6XX"/>
+ <reg32 offset="0x000ad" name="RBBM_CLOCK_CNTL_GLOBAL" variants="A7XX"/>
+ <reg32 offset="0x0009a" name="RBBM_CLOCK_CNTL_GLOBAL" variants="A8XX-"/>
+ <reg32 offset="0x07d" name="RBBM_POWER_UP_RESET_SW_OVERRIDE" variants="A8XX-"/>
+ <reg32 offset="0x07e" name="RBBM_POWER_UP_RESET_SW_BV_OVERRIDE" variants="A8XX-"/>
+ <reg32 offset="0x000ae" name="RBBM_CLOCK_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b0" name="RBBM_CLOCK_CNTL_SP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b1" name="RBBM_CLOCK_CNTL_SP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b2" name="RBBM_CLOCK_CNTL_SP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b3" name="RBBM_CLOCK_CNTL_SP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b4" name="RBBM_CLOCK_CNTL2_SP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b5" name="RBBM_CLOCK_CNTL2_SP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b6" name="RBBM_CLOCK_CNTL2_SP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b7" name="RBBM_CLOCK_CNTL2_SP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b8" name="RBBM_CLOCK_DELAY_SP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000b9" name="RBBM_CLOCK_DELAY_SP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ba" name="RBBM_CLOCK_DELAY_SP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000bb" name="RBBM_CLOCK_DELAY_SP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000bc" name="RBBM_CLOCK_HYST_SP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000bd" name="RBBM_CLOCK_HYST_SP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000be" name="RBBM_CLOCK_HYST_SP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000bf" name="RBBM_CLOCK_HYST_SP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c0" name="RBBM_CLOCK_CNTL_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c1" name="RBBM_CLOCK_CNTL_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c2" name="RBBM_CLOCK_CNTL_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c3" name="RBBM_CLOCK_CNTL_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c4" name="RBBM_CLOCK_CNTL2_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c5" name="RBBM_CLOCK_CNTL2_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c6" name="RBBM_CLOCK_CNTL2_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c7" name="RBBM_CLOCK_CNTL2_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c8" name="RBBM_CLOCK_CNTL3_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000c9" name="RBBM_CLOCK_CNTL3_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ca" name="RBBM_CLOCK_CNTL3_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000cb" name="RBBM_CLOCK_CNTL3_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000cc" name="RBBM_CLOCK_CNTL4_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000cd" name="RBBM_CLOCK_CNTL4_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ce" name="RBBM_CLOCK_CNTL4_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000cf" name="RBBM_CLOCK_CNTL4_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d0" name="RBBM_CLOCK_DELAY_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d1" name="RBBM_CLOCK_DELAY_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d2" name="RBBM_CLOCK_DELAY_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d3" name="RBBM_CLOCK_DELAY_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d4" name="RBBM_CLOCK_DELAY2_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d5" name="RBBM_CLOCK_DELAY2_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d6" name="RBBM_CLOCK_DELAY2_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d7" name="RBBM_CLOCK_DELAY2_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d8" name="RBBM_CLOCK_DELAY3_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000d9" name="RBBM_CLOCK_DELAY3_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000da" name="RBBM_CLOCK_DELAY3_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000db" name="RBBM_CLOCK_DELAY3_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000dc" name="RBBM_CLOCK_DELAY4_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000dd" name="RBBM_CLOCK_DELAY4_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000de" name="RBBM_CLOCK_DELAY4_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000df" name="RBBM_CLOCK_DELAY4_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e0" name="RBBM_CLOCK_HYST_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e1" name="RBBM_CLOCK_HYST_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e2" name="RBBM_CLOCK_HYST_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e3" name="RBBM_CLOCK_HYST_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e4" name="RBBM_CLOCK_HYST2_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e5" name="RBBM_CLOCK_HYST2_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e6" name="RBBM_CLOCK_HYST2_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e7" name="RBBM_CLOCK_HYST2_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e8" name="RBBM_CLOCK_HYST3_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000e9" name="RBBM_CLOCK_HYST3_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ea" name="RBBM_CLOCK_HYST3_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000eb" name="RBBM_CLOCK_HYST3_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ec" name="RBBM_CLOCK_HYST4_TP0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ed" name="RBBM_CLOCK_HYST4_TP1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ee" name="RBBM_CLOCK_HYST4_TP2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000ef" name="RBBM_CLOCK_HYST4_TP3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f0" name="RBBM_CLOCK_CNTL_RB0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f1" name="RBBM_CLOCK_CNTL_RB1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f2" name="RBBM_CLOCK_CNTL_RB2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f3" name="RBBM_CLOCK_CNTL_RB3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f4" name="RBBM_CLOCK_CNTL2_RB0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f5" name="RBBM_CLOCK_CNTL2_RB1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f6" name="RBBM_CLOCK_CNTL2_RB2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f7" name="RBBM_CLOCK_CNTL2_RB3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f8" name="RBBM_CLOCK_CNTL_CCU0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000f9" name="RBBM_CLOCK_CNTL_CCU1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000fa" name="RBBM_CLOCK_CNTL_CCU2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x000fb" name="RBBM_CLOCK_CNTL_CCU3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00100" name="RBBM_CLOCK_HYST_RB_CCU0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00101" name="RBBM_CLOCK_HYST_RB_CCU1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00102" name="RBBM_CLOCK_HYST_RB_CCU2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00103" name="RBBM_CLOCK_HYST_RB_CCU3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00104" name="RBBM_CLOCK_CNTL_RAC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00105" name="RBBM_CLOCK_CNTL2_RAC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00106" name="RBBM_CLOCK_DELAY_RAC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00107" name="RBBM_CLOCK_HYST_RAC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00108" name="RBBM_CLOCK_CNTL_TSE_RAS_RBBM" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00109" name="RBBM_CLOCK_DELAY_TSE_RAS_RBBM" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0010a" name="RBBM_CLOCK_HYST_TSE_RAS_RBBM" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0010b" name="RBBM_CLOCK_CNTL_UCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0010c" name="RBBM_CLOCK_CNTL2_UCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0010d" name="RBBM_CLOCK_CNTL3_UCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0010e" name="RBBM_CLOCK_CNTL4_UCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0010f" name="RBBM_CLOCK_DELAY_UCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00110" name="RBBM_CLOCK_HYST_UCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00111" name="RBBM_CLOCK_MODE_VFD" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00112" name="RBBM_CLOCK_DELAY_VFD" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00113" name="RBBM_CLOCK_HYST_VFD" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00114" name="RBBM_CLOCK_MODE_GPC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00115" name="RBBM_CLOCK_DELAY_GPC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00116" name="RBBM_CLOCK_HYST_GPC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00117" name="RBBM_CLOCK_DELAY_HLSQ_2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00118" name="RBBM_CLOCK_CNTL_GMU_GX" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00119" name="RBBM_CLOCK_DELAY_GMU_GX" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0011a" name="RBBM_CLOCK_HYST_GMU_GX" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0011b" name="RBBM_CLOCK_MODE_HLSQ" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0011c" name="RBBM_CLOCK_DELAY_HLSQ" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0011d" name="RBBM_CLOCK_HYST_HLSQ" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0011e" name="RBBM_CGC_GLOBAL_LOAD_CMD" variants="A7XX"/>
+ <reg32 offset="0x0009b" name="RBBM_CGC_GLOBAL_LOAD_CMD" variants="A8XX-"/>
+ <reg32 offset="0x0011f" name="RBBM_CGC_P2S_TRIG_CMD" variants="A7XX"/>
+ <reg32 offset="0x0009c" name="RBBM_CGC_P2S_TRIG_CMD" variants="A8XX-"/>
+ <reg32 offset="0x00120" name="RBBM_CGC_P2S_CNTL" variants="A7XX"/>
+ <reg32 offset="0x0009d" name="RBBM_CGC_P2S_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x00120" name="RBBM_CLOCK_CNTL_TEX_FCHE" variants="A6XX"/>
+ <reg32 offset="0x00121" name="RBBM_CLOCK_DELAY_TEX_FCHE" variants="A6XX-A7XX"/>
<reg32 offset="0x00122" name="RBBM_CLOCK_HYST_TEX_FCHE" variants="A6XX"/>
- <reg32 offset="0x00122" name="RBBM_CGC_P2S_STATUS" variants="A7XX-">
+ <reg32 offset="0x00122" name="RBBM_CGC_P2S_STATUS" variants="A7XX">
<bitfield name="TXDONE" pos="0" type="boolean"/>
</reg32>
- <reg32 offset="0x00123" name="RBBM_CLOCK_CNTL_FCHE"/>
- <reg32 offset="0x00124" name="RBBM_CLOCK_DELAY_FCHE"/>
- <reg32 offset="0x00125" name="RBBM_CLOCK_HYST_FCHE"/>
- <reg32 offset="0x00126" name="RBBM_CLOCK_CNTL_MHUB"/>
- <reg32 offset="0x00127" name="RBBM_CLOCK_DELAY_MHUB"/>
- <reg32 offset="0x00128" name="RBBM_CLOCK_HYST_MHUB"/>
- <reg32 offset="0x00129" name="RBBM_CLOCK_DELAY_GLC"/>
- <reg32 offset="0x0012a" name="RBBM_CLOCK_HYST_GLC"/>
- <reg32 offset="0x0012b" name="RBBM_CLOCK_CNTL_GLC"/>
- <reg32 offset="0x0012f" name="RBBM_CLOCK_HYST2_VFD" variants="A7XX-"/>
- <reg32 offset="0x005ff" name="RBBM_LPAC_GBIF_CLIENT_QOS_CNTL"/>
+ <reg32 offset="0x09f" name="RBBM_CGC_P2S_STATUS" variants="A8XX-">
+ <bitfield name="TXDONE" pos="0" type="boolean"/>
+ </reg32>
+ <reg32 offset="0x00123" name="RBBM_CLOCK_CNTL_FCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00124" name="RBBM_CLOCK_DELAY_FCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00125" name="RBBM_CLOCK_HYST_FCHE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00126" name="RBBM_CLOCK_CNTL_MHUB" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00127" name="RBBM_CLOCK_DELAY_MHUB" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00128" name="RBBM_CLOCK_HYST_MHUB" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00129" name="RBBM_CLOCK_DELAY_GLC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0012a" name="RBBM_CLOCK_HYST_GLC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0012b" name="RBBM_CLOCK_CNTL_GLC" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0012f" name="RBBM_CLOCK_HYST2_VFD" variants="A7XX"/>
+ <reg32 offset="0x00195" name="RBBM_CGC_0_PC" variants="A7XX"/>
+ <reg32 offset="0x0010b" name="RBBM_CGC_0_PC" variants="A8XX-"/>
+
+ <reg32 offset="0x005ff" name="RBBM_LPAC_GBIF_CLIENT_QOS_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x00009" name="RBBM_LPAC_GBIF_CLIENT_QOS_CNTL" variants="A8XX-"/>
<reg32 offset="0x0600" name="DBGC_CFG_DBGBUS_SEL_A"/>
<reg32 offset="0x0601" name="DBGC_CFG_DBGBUS_SEL_B"/>
@@ -610,6 +964,8 @@ by a particular renderpass/blit.
<reg32 offset="0x0605" name="DBGC_CFG_DBGBUS_CNTLM">
<bitfield high="27" low="24" name="ENABLE"/>
</reg32>
+ <reg32 offset="0x0606" name="DBGC_CFG_DBGBUS_OPL"/>
+ <reg32 offset="0x0607" name="DBGC_CFG_DBGBUS_OPE"/>
<reg32 offset="0x0608" name="DBGC_CFG_DBGBUS_IVTL_0"/>
<reg32 offset="0x0609" name="DBGC_CFG_DBGBUS_IVTL_1"/>
<reg32 offset="0x060a" name="DBGC_CFG_DBGBUS_IVTL_2"/>
@@ -638,72 +994,276 @@ by a particular renderpass/blit.
<bitfield high="27" low="24" name="BYTEL14"/>
<bitfield high="31" low="28" name="BYTEL15"/>
</reg32>
+ <reg32 offset="0x0612" name="DBGC_CFG_DBGBUS_IVTE_0"/>
+ <reg32 offset="0x0613" name="DBGC_CFG_DBGBUS_IVTE_1"/>
+ <reg32 offset="0x0614" name="DBGC_CFG_DBGBUS_IVTE_2"/>
+ <reg32 offset="0x0615" name="DBGC_CFG_DBGBUS_IVTE_3"/>
+ <reg32 offset="0x0616" name="DBGC_CFG_DBGBUS_MASKE_0"/>
+ <reg32 offset="0x0617" name="DBGC_CFG_DBGBUS_MASKE_1"/>
+ <reg32 offset="0x0618" name="DBGC_CFG_DBGBUS_MASKE_2"/>
+ <reg32 offset="0x0619" name="DBGC_CFG_DBGBUS_MASKE_3"/>
+ <reg32 offset="0x061a" name="DBGC_CFG_DBGBUS_NIBBLEE"/>
+ <reg32 offset="0x061b" name="DBGC_CFG_DBGBUS_PTRC0"/>
+ <reg32 offset="0x061c" name="DBGC_CFG_DBGBUS_PTRC1"/>
+ <reg32 offset="0x061d" name="DBGC_CFG_DBGBUS_LOADREG"/>
+ <reg32 offset="0x061e" name="DBGC_CFG_DBGBUS_IDX"/>
+ <reg32 offset="0x061f" name="DBGC_CFG_DBGBUS_CLRC"/>
+ <reg32 offset="0x0620" name="DBGC_CFG_DBGBUS_LOADIVT"/>
+ <reg32 offset="0x0621" name="DBGC_VBIF_DBG_CNTL"/>
+ <reg32 offset="0x0622" name="DBGC_DBG_LO_HI_GPIO"/>
+ <reg32 offset="0x0623" name="DBGC_EXT_TRACE_BUS_CNTL"/>
+ <reg32 offset="0x0624" name="DBGC_READ_AHB_THROUGH_DBG"/>
+ <reg32 offset="0x0625" name="DBGC_CFG_DBGBUS_EVENT_LOGIC"/>
+ <reg32 offset="0x0626" name="DBGC_CFG_DBGBUS_OVER"/>
+ <reg32 offset="0x0627" name="DBGC_CFG_DBGBUS_COUNT0"/>
+ <reg32 offset="0x0628" name="DBGC_CFG_DBGBUS_COUNT1"/>
+ <reg32 offset="0x0629" name="DBGC_CFG_DBGBUS_COUNT2"/>
+ <reg32 offset="0x062a" name="DBGC_CFG_DBGBUS_COUNT3"/>
+ <reg32 offset="0x062b" name="DBGC_CFG_DBGBUS_COUNT4"/>
+ <reg32 offset="0x062c" name="DBGC_CFG_DBGBUS_COUNT5"/>
+ <reg32 offset="0x062d" name="DBGC_CFG_DBGBUS_TRACE_ADDR"/>
+ <reg32 offset="0x062e" name="DBGC_CFG_DBGBUS_TRACE_BUF0"/>
<reg32 offset="0x062f" name="DBGC_CFG_DBGBUS_TRACE_BUF1"/>
<reg32 offset="0x0630" name="DBGC_CFG_DBGBUS_TRACE_BUF2"/>
- <array offset="0x0CD8" name="VSC_PERFCTR_VSC_SEL" stride="1" length="2" variants="A6XX"/>
- <reg32 offset="0x0CD8" name="VSC_UNKNOWN_0CD8" variants="A7XX">
- <doc>
- Set to true when binning, isn't changed afterwards
- </doc>
- <bitfield name="BINNING" pos="0" type="boolean"/>
- </reg32>
+ <reg32 offset="0x0631" name="DBGC_CFG_DBGBUS_TRACE_BUF3"/>
+ <reg32 offset="0x0632" name="DBGC_CFG_DBGBUS_TRACE_BUF4"/>
+ <reg32 offset="0x0633" name="DBGC_CFG_DBGBUS_MISR0"/>
+ <reg32 offset="0x0634" name="DBGC_CFG_DBGBUS_MISR1"/>
+ <reg32 offset="0x0635" name="DBGC_EVT_CFG"/>
+ <reg32 offset="0x0636" name="DBGC_EVT_INTF_SEL_0"/>
+ <reg32 offset="0x0637" name="DBGC_EVT_INTF_SEL_1"/>
+ <reg32 offset="0x0638" name="DBGC_EVT_SLICE_CFG"/>
+ <reg32 offset="0x0639" name="DBGC_QDSS_TIMESTAMP_0"/>
+ <reg32 offset="0x063a" name="DBGC_QDSS_TIMESTAMP_1"/>
+ <reg32 offset="0x063b" name="DBGC_ECO_CNTL"/>
+ <reg32 offset="0x063c" name="DBGC_AHB_DBG_CNTL"/>
+ <reg32 offset="0x063d" name="DBGC_EVT_INTF_SEL_2"/>
+ <reg32 offset="0x0640" name="DBGC_CFG_DBGBUS_PONG_SEL_A"/>
+ <reg32 offset="0x0641" name="DBGC_CFG_DBGBUS_PONG_SEL_B"/>
+ <reg32 offset="0x0642" name="DBGC_CFG_DBGBUS_PONG_SEL_C"/>
+ <reg32 offset="0x0643" name="DBGC_CFG_DBGBUS_PONG_SEL_D"/>
+ <reg32 offset="0x0644" name="DBGC_CFG_DBGBUS_MISC_MODE"/>
+ <reg32 offset="0x0650" name="DBGC_EVT_INTF_SEL_3_0"/>
+ <reg32 offset="0x0651" name="DBGC_EVT_INTF_SEL_3_1"/>
+ <reg32 offset="0x0652" name="DBGC_EVT_INTF_SEL_3_2"/>
+ <reg32 offset="0x0653" name="DBGC_EVT_INTF_SEL_3_3"/>
+ <reg32 offset="0x0654" name="DBGC_EVT_INTF_SEL_3_4"/>
+ <reg32 offset="0x0655" name="DBGC_EVT_INTF_SEL_3_5"/>
+ <reg32 offset="0x0660" name="DBGC_TRACE_BUFFER_STATUS"/>
+ <reg32 offset="0x0661" name="DBGC_TRACE_BUFFER_CMD"/>
+ <reg32 offset="0x0662" name="DBGC_DBG_TRACE_BUFFER_RD_ADDR"/>
+ <reg32 offset="0x0663" name="DBGC_DBG_TRACE_BUFFER_RD_DATA"/>
+ <reg32 offset="0x0664" name="DBGC_TRACE_BUFFER_ATB_RD_STATUS"/>
+ <reg32 offset="0x0665" name="DBGC_SMMU_FAULT_BLOCK_HALT_CFG"/>
+ <reg32 offset="0x0666" name="DBGC_DBG_LOPC_SB_RD_ADDR"/>
+ <reg32 offset="0x0667" name="DBGC_DBG_LOPC_SB_RD_DATA"/>
+ <reg32 offset="0x0668" name="DBGC_DBG_LOPC_SB_WR_ADDR"/>
+ <reg32 offset="0x0669" name="DBGC_DBG_LOPC_SB_WR_DATA"/>
+ <reg32 offset="0x066a" name="DBGC_INTERRUPT_STATUS"/>
+ <reg64 offset="0x0680" name="DBGC_GBIF_DBG_BASE"/>
+ <reg32 offset="0x0682" name="DBGC_GBIF_DBG_BUFF_SIZE"/>
+ <reg32 offset="0x0683" name="DBGC_GBIF_DBG_CNTL"/>
+ <reg32 offset="0x0684" name="DBGC_GBIF_DBG_CMD"/>
+ <reg32 offset="0x0685" name="DBGC_GBIF_DBG_STATUS"/>
+
+ <reg32 offset="0x0700" name="DBGC_SCOPE_PERF_COUNTER_CFG_US" variants="A8XX-"/>
+ <reg32 offset="0x0701" name="DBGC_CFG_PERF_TRIG_CLUSTER_FE_US" variants="A8XX-"/>
+ <reg32 offset="0x0702" name="DBGC_CFG_PERF_TRIG_CLUSTER_VPC_US" variants="A8XX-"/>
+ <reg32 offset="0x0703" name="DBGC_CFG_PERF_TRIG_CLUSTER_SP_VS_US" variants="A8XX-"/>
+ <reg32 offset="0x0704" name="DBGC_CFG_PERF_TRIG_CLUSTER_SP_PS_US" variants="A8XX-"/>
+ <reg32 offset="0x0707" name="DBGC_CFG_PERF_TRIG_CLUSTER_NONE_US" variants="A8XX-"/>
+ <reg32 offset="0x0708" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_FE_US" variants="A8XX-"/>
+ <reg32 offset="0x0709" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_VPC_US" variants="A8XX-"/>
+ <reg32 offset="0x070a" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_SP_VS_US" variants="A8XX-"/>
+ <reg32 offset="0x070f" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_NONE_US" variants="A8XX-"/>
+ <reg32 offset="0x0710" name="DBGC_CFG_PERF_COUNTER_SEL_FE_US" variants="A8XX-"/>
+ <reg32 offset="0x0711" name="DBGC_CFG_PERF_COUNTER_SEL_FE_US_1" variants="A8XX-"/>
+ <reg32 offset="0x0712" name="DBGC_CFG_PERF_COUNTER_SEL_FE_US_2" variants="A8XX-"/>
+ <reg32 offset="0x0713" name="DBGC_CFG_PERF_COUNTER_SEL_VPC_US" variants="A8XX-"/>
+ <reg32 offset="0x0714" name="DBGC_CFG_PERF_COUNTER_SEL_VPC_US_1" variants="A8XX-"/>
+ <reg32 offset="0x0715" name="DBGC_CFG_PERF_COUNTER_SEL_SP_VS_US" variants="A8XX-"/>
+ <reg32 offset="0x0716" name="DBGC_CFG_PERF_COUNTER_SEL_SP_PS_US" variants="A8XX-"/>
+ <reg32 offset="0x0720" name="DBGC_CFG_PERF_COUNTER_SEL_NONE_US" variants="A8XX-"/>
+ <reg32 offset="0x0721" name="DBGC_CFG_PERF_COUNTER_SEL_NONE_US_1" variants="A8XX-"/>
+ <reg32 offset="0x0722" name="DBGC_CFG_BV_PERF_COUNTER_SEL_FE_US" variants="A8XX-"/>
+ <reg32 offset="0x0723" name="DBGC_CFG_BV_PERF_COUNTER_SEL_FE_US_1" variants="A8XX-"/>
+ <reg32 offset="0x0724" name="DBGC_CFG_BV_PERF_COUNTER_SEL_FE_US_2" variants="A8XX-"/>
+ <reg32 offset="0x0730" name="DBGC_CFG_BV_PERF_COUNTER_SEL_VPC_US" variants="A8XX-"/>
+ <reg32 offset="0x0731" name="DBGC_CFG_BV_PERF_COUNTER_SEL_VPC_US_1" variants="A8XX-"/>
+ <reg32 offset="0x0732" name="DBGC_CFG_BV_PERF_COUNTER_SEL_SP_VS_US" variants="A8XX-"/>
+ <reg32 offset="0x0740" name="DBGC_CFG_BV_PERF_COUNTER_SEL_NONE_US" variants="A8XX-"/>
+ <reg32 offset="0x0742" name="DBGC_CFG_PERF_TIMESTAMP_TRIG_SEL_US" variants="A8XX-"/>
+ <reg32 offset="0x0743" name="DBGC_CFG_BV_PERF_TIMESTAMP_TRIG_SEL_US" variants="A8XX-"/>
+ <reg64 offset="0x0744" name="DBGC_CFG_GBIF_BR_PERF_CNTR_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0746" name="DBGC_CFG_GBIF_BR_BUFFER_SIZE" variants="A8XX-"/>
+ <reg64 offset="0x0747" name="DBGC_CFG_GBIF_BV_PERF_CNTR_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0749" name="DBGC_CFG_GBIF_BV_BUFFER_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x074a" name="DBGC_CFG_GBIF_QOS_CTRL" variants="A8XX-"/>
+ <reg32 offset="0x0750" name="DBGC_GBIF_BR_PERF_CNTR_WRITE_POINTER" variants="A8XX-"/>
+ <reg32 offset="0x0751" name="DBGC_GBIF_BV_PERF_CNTR_WRITE_POINTER" variants="A8XX-"/>
+ <reg32 offset="0x0752" name="DBGC_PERF_COUNTER_FE_LOCAL_BATCH_ID" variants="A8XX-"/>
+ <reg32 offset="0x0753" name="DBGC_CFG_PERF_WAIT_IDLE_CLOCKS_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x0754" name="DBGC_PERF_COUNTER_SCOPING_CMD_US" variants="A8XX-"/>
+ <reg32 offset="0x0755" name="DBGC_PERF_SKEW_BUFFER_INIT_CMD" variants="A8XX-"/>
+ <reg32 offset="0x0759" name="DBGC_LOPC_INTERRUPT_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x075a" name="DBGC_LOPC_BUFFER_PTR_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x075b" name="DBGC_PERF_SCOPING_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x075c" name="DBGC_PERF_COUNTER_PKT_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0760" name="DBGC_GC_LIVE_MBX_PKT_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0761" name="DBGC_GC_ALW_MBX_PKT_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0762" name="DBGC_AO_CNTR_LO_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0763" name="DBGC_AO_CNTR_HI_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0770" name="DBGC_LOPC_GC_SB_DEPTH_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0780" name="DBGC_LPAC_SCOPE_PERF_COUNTER_CFG_US" variants="A8XX-"/>
+ <reg32 offset="0x0781" name="DBGC_CFG_PERF_TRIG_LPAC_US" variants="A8XX-"/>
+ <reg32 offset="0x0782" name="DBGC_CFG_PERF_COUNTER_SEL_LPAC_US" variants="A8XX-"/>
+ <reg32 offset="0x0783" name="DBGC_CFG_PERF_COUNTER_SEL_LPAC_US_1" variants="A8XX-"/>
+ <reg32 offset="0x0784" name="DBGC_CFG_PERF_COUNTER_SEL_LPAC_US_2" variants="A8XX-"/>
+ <reg32 offset="0x0785" name="DBGC_CFG_PERF_TIMESTAMP_TRIG_SEL_LPAC_US" variants="A8XX-"/>
+ <reg64 offset="0x0786" name="DBGC_CFG_GBIF_LPAC_PERF_CNTR_BASE" variants="A8XX-"/>
+ <reg32 offset="0x0788" name="DBGC_CFG_GBIF_LPAC_BUFFER_SIZE" variants="A8XX-"/>
+ <reg32 offset="0x0789" name="DBGC_GBIF_LPAC_PERF_CNTR_WRITE_POINTER" variants="A8XX-"/>
+ <reg32 offset="0x078a" name="DBGC_CFG_LPAC_PERF_WAIT_IDLE_CLOCKS_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x078b" name="DBGC_LPAC_PERF_COUNTER_SCOPING_CMD_US" variants="A8XX-"/>
+ <reg32 offset="0x078c" name="DBGC_LPAC_MBX_PKT_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x078d" name="DBGC_LPAC_PERF_SCOPING_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x0790" name="DBGC_LOPC_LPAC_SB_DEPTH_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x07a0" name="DBGC_SCOPE_PERF_COUNTER_CFG_S" variants="A8XX-"/>
+ <reg32 offset="0x07a1" name="DBGC_CFG_PERF_TRIG_CLUSTER_FE_S" variants="A8XX-"/>
+ <reg32 offset="0x07a2" name="DBGC_CFG_PERF_TRIG_CLUSTER_SP_VS" variants="A8XX-"/>
+ <reg32 offset="0x07a3" name="DBGC_CFG_PERF_TRIG_CLUSTER_VPC_VS" variants="A8XX-"/>
+ <reg32 offset="0x07a4" name="DBGC_CFG_PERF_TRIG_CLUSTER_GRAS" variants="A8XX-"/>
+ <reg32 offset="0x07a5" name="DBGC_CFG_PERF_TRIG_CLUSTER_SP_PS" variants="A8XX-"/>
+ <reg32 offset="0x07a6" name="DBGC_CFG_PERF_TRIG_CLUSTER_VPC_PS" variants="A8XX-"/>
+ <reg32 offset="0x07a7" name="DBGC_CFG_PERF_TRIG_CLUSTER_PS" variants="A8XX-"/>
+ <reg32 offset="0x07a8" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_FE_S" variants="A8XX-"/>
+ <reg32 offset="0x07a9" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_SP_VS" variants="A8XX-"/>
+ <reg32 offset="0x07aa" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_VPC_VS" variants="A8XX-"/>
+ <reg32 offset="0x07ab" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_GRAS" variants="A8XX-"/>
+ <reg32 offset="0x07ac" name="DBGC_CFG_BV_PERF_TRIG_CLUSTER_VPC_PS" variants="A8XX-"/>
+ <reg32 offset="0x07ad" name="DBGC_CFG_PERF_COUNTER_SEL_FE_S" variants="A8XX-"/>
+ <reg32 offset="0x07ae" name="DBGC_CFG_PERF_COUNTER_SEL_FE_S_1" variants="A8XX-"/>
+ <reg32 offset="0x07af" name="DBGC_CFG_PERF_COUNTER_SEL_FE_S_2" variants="A8XX-"/>
+ <reg32 offset="0x07b0" name="DBGC_CFG_PERF_COUNTER_SEL_FE_S_3" variants="A8XX-"/>
+ <reg32 offset="0x07b1" name="DBGC_CFG_PERF_COUNTER_SEL_SP_VS" variants="A8XX-"/>
+ <reg32 offset="0x07b2" name="DBGC_CFG_PERF_COUNTER_SEL_SP_VS_1" variants="A8XX-"/>
+ <reg32 offset="0x07b3" name="DBGC_CFG_PERF_COUNTER_SEL_SP_VS_2" variants="A8XX-"/>
+ <reg32 offset="0x07b4" name="DBGC_CFG_PERF_COUNTER_SEL_SP_VS_3" variants="A8XX-"/>
+ <reg32 offset="0x07b5" name="DBGC_CFG_PERF_COUNTER_SEL_VPC_VS" variants="A8XX-"/>
+ <reg32 offset="0x07b6" name="DBGC_CFG_PERF_COUNTER_SEL_VPC_VS_1" variants="A8XX-"/>
+ <reg32 offset="0x07b7" name="DBGC_CFG_PERF_COUNTER_SEL_GRAS" variants="A8XX-"/>
+ <reg32 offset="0x07b8" name="DBGC_CFG_PERF_COUNTER_SEL_GRAS_1" variants="A8XX-"/>
+ <reg32 offset="0x07b9" name="DBGC_CFG_PERF_COUNTER_SEL_GRAS_2" variants="A8XX-"/>
+ <reg32 offset="0x07ba" name="DBGC_CFG_PERF_COUNTER_SEL_SP_PS" variants="A8XX-"/>
+ <reg32 offset="0x07bb" name="DBGC_CFG_PERF_COUNTER_SEL_SP_PS_1" variants="A8XX-"/>
+ <reg32 offset="0x07bc" name="DBGC_CFG_PERF_COUNTER_SEL_SP_PS_2" variants="A8XX-"/>
+ <reg32 offset="0x07bd" name="DBGC_CFG_PERF_COUNTER_SEL_SP_PS_3" variants="A8XX-"/>
+ <reg32 offset="0x07be" name="DBGC_CFG_PERF_COUNTER_SEL_VPC_PS" variants="A8XX-"/>
+ <reg32 offset="0x07bf" name="DBGC_CFG_PERF_COUNTER_SEL_VPC_PS_1" variants="A8XX-"/>
+ <reg32 offset="0x07c0" name="DBGC_CFG_PERF_COUNTER_SEL_PS" variants="A8XX-"/>
+ <reg32 offset="0x07c1" name="DBGC_CFG_PERF_COUNTER_SEL_PS_1" variants="A8XX-"/>
+ <reg32 offset="0x07c2" name="DBGC_CFG_PERF_COUNTER_SEL_PS_2" variants="A8XX-"/>
+ <reg32 offset="0x07c3" name="DBGC_CFG_PERF_COUNTER_SEL_PS_3" variants="A8XX-"/>
+ <reg32 offset="0x07c4" name="DBGC_CFG_PERF_TIMESTAMP_TRIG_SEL_S" variants="A8XX-"/>
+ <reg32 offset="0x07c5" name="DBGC_CFG_BV_PERF_COUNTER_SEL_FE_S" variants="A8XX-"/>
+ <reg32 offset="0x07c6" name="DBGC_CFG_BV_PERF_COUNTER_SEL_FE_S_1" variants="A8XX-"/>
+ <reg32 offset="0x07c7" name="DBGC_CFG_BV_PERF_COUNTER_SEL_FE_S_2" variants="A8XX-"/>
+ <reg32 offset="0x07c8" name="DBGC_CFG_BV_PERF_COUNTER_SEL_FE_S_3" variants="A8XX-"/>
+ <reg32 offset="0x07c9" name="DBGC_CFG_BV_PERF_COUNTER_SEL_SP_VS" variants="A8XX-"/>
+ <reg32 offset="0x07ca" name="DBGC_CFG_BV_PERF_COUNTER_SEL_SP_VS_1" variants="A8XX-"/>
+ <reg32 offset="0x07cb" name="DBGC_CFG_BV_PERF_COUNTER_SEL_SP_VS_2" variants="A8XX-"/>
+ <reg32 offset="0x07cc" name="DBGC_CFG_BV_PERF_COUNTER_SEL_SP_VS_3" variants="A8XX-"/>
+ <reg32 offset="0x07cd" name="DBGC_CFG_BV_PERF_COUNTER_SEL_VPC_VS" variants="A8XX-"/>
+ <reg32 offset="0x07ce" name="DBGC_CFG_BV_PERF_COUNTER_SEL_VPC_VS_1" variants="A8XX-"/>
+ <reg32 offset="0x07cf" name="DBGC_CFG_BV_PERF_COUNTER_SEL_GRAS" variants="A8XX-"/>
+ <reg32 offset="0x07d0" name="DBGC_CFG_BV_PERF_COUNTER_SEL_GRAS_1" variants="A8XX-"/>
+ <reg32 offset="0x07d1" name="DBGC_CFG_BV_PERF_COUNTER_SEL_GRAS_2" variants="A8XX-"/>
+ <reg32 offset="0x07d2" name="DBGC_CFG_BV_PERF_COUNTER_SEL_VPC_PS" variants="A8XX-"/>
+ <reg32 offset="0x07d3" name="DBGC_CFG_BV_PERF_COUNTER_SEL_VPC_PS_1" variants="A8XX-"/>
+ <reg32 offset="0x07d4" name="DBGC_CFG_BV_PERF_TIMESTAMP_TRIG_SEL_S" variants="A8XX-"/>
+ <reg32 offset="0x07d5" name="DBGC_PERF_COUNTER_SCOPING_CMD_S" variants="A8XX-"/>
+ <reg32 offset="0x07e0" name="DBGC_LPAC_SCOPE_PERF_COUNTER_CFG_S" variants="A8XX-"/>
+ <reg32 offset="0x07e1" name="DBGC_CFG_PERF_TRIG_LPAC_S" variants="A8XX-"/>
+ <reg32 offset="0x07e2" name="DBGC_CFG_PERF_COUNTER_SEL_LPAC_S" variants="A8XX-"/>
+ <reg32 offset="0x07e3" name="DBGC_CFG_PERF_COUNTER_SEL_LPAC_S_1" variants="A8XX-"/>
+ <reg32 offset="0x07e4" name="DBGC_CFG_PERF_COUNTER_SEL_LPAC_S_2" variants="A8XX-"/>
+ <reg32 offset="0x07e5" name="DBGC_CFG_PERF_TIMESTAMP_TRIG_SEL_LPAC_S" variants="A8XX-"/>
+ <reg32 offset="0x07e6" name="DBGC_LPAC_PERF_COUNTER_SCOPING_CMD_S" variants="A8XX-"/>
+
+ <array offset="0x0CD8" name="VSC_PERFCTR_VSC_SEL" stride="1" length="2"/>
<reg32 offset="0xC800" name="HLSQ_DBG_AHB_READ_APERTURE"/>
<reg32 offset="0xD000" name="HLSQ_DBG_READ_SEL"/>
- <reg32 offset="0x0E00" name="UCHE_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
+ <reg32 offset="0x0E00" name="UCHE_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
<reg32 offset="0x0E01" name="UCHE_MODE_CNTL"/>
- <reg64 offset="0x0E05" name="UCHE_WRITE_RANGE_MAX"/>
- <reg64 offset="0x0E07" name="UCHE_WRITE_THRU_BASE"/>
- <reg64 offset="0x0E09" name="UCHE_TRAP_BASE"/>
- <reg64 offset="0x0E0B" name="UCHE_GMEM_RANGE_MIN"/>
- <reg64 offset="0x0E0D" name="UCHE_GMEM_RANGE_MAX"/>
- <reg32 offset="0x0E17" name="UCHE_CACHE_WAYS" usage="cmd"/>
+ <reg64 offset="0x0E05" name="UCHE_WRITE_RANGE_MAX" variants="A6XX"/>
+ <reg64 offset="0x0E07" name="UCHE_WRITE_THRU_BASE" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0E06" name="UCHE_WRITE_THRU_BASE" variants="A8XX-"/>
+ <reg64 offset="0x0E09" name="UCHE_TRAP_BASE" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0E08" name="UCHE_TRAP_BASE" variants="A8XX-"/>
+ <reg64 offset="0x0E0B" name="UCHE_GMEM_RANGE_MIN" variants="A6XX-A7XX"/>
+ <reg64 offset="0x0E0D" name="UCHE_GMEM_RANGE_MAX" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0e17" name="UCHE_CACHE_WAYS" variants="A6XX-A7XX" usage="init"/>
+ <reg32 offset="0x0e04" name="UCHE_CACHE_WAYS" variants="A8XX-"/>
<reg32 offset="0x0E18" name="UCHE_FILTER_CNTL"/>
- <reg32 offset="0x0E19" name="UCHE_CLIENT_PF" usage="cmd">
+ <reg32 offset="0x0e19" name="UCHE_CLIENT_PF" variants="A6XX-A7XX" usage="init">
<bitfield high="7" low="0" name="PERFSEL"/>
</reg32>
- <array offset="0x0E1C" name="UCHE_PERFCTR_UCHE_SEL" stride="1" length="12"/>
- <reg32 offset="0x0e3a" name="UCHE_GBIF_GX_CONFIG"/>
- <reg32 offset="0x0e3c" name="UCHE_CMDQ_CONFIG"/>
+ <array offset="0x0e1c" name="UCHE_PERFCTR_UCHE_SEL" stride="1" length="12" variants="A6XX-A7XX"/>
+ <array offset="0x0e20" name="UCHE_PERFCTR_UCHE_SEL" stride="1" length="24" variants="A8XX-"/>
+ <reg32 offset="0x0e3a" name="UCHE_GBIF_GX_CONFIG" variants="A6XX-A7XX"/>
+ <reg32 offset="0x0e12" name="UCHE_GBIF_GX_CONFIG" variants="A8XX-"/>
+ <reg32 offset="0x0e3c" name="UCHE_CMDQ_CONFIG" variants="A6XX-A7XX"/>
- <reg32 offset="0x3000" name="VBIF_VERSION"/>
- <reg32 offset="0x3001" name="VBIF_CLKON">
+ <reg32 offset="0x0f01" name="UCHE_CCHE_MODE_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x0f02" name="UCHE_CCHE_CACHE_WAYS" variants="A8XX-"/>
+ <reg64 offset="0x0f04" name="UCHE_CCHE_WRITE_THRU_BASE" variants="A8XX-"/>
+ <reg64 offset="0x0f06" name="UCHE_CCHE_TRAP_BASE" variants="A8XX-"/>
+ <reg64 offset="0x0f08" name="UCHE_CCHE_GC_GMEM_RANGE_MIN" variants="A8XX-"/>
+ <reg64 offset="0x0f0a" name="UCHE_CCHE_LPAC_GMEM_RANGE_MIN" variants="A8XX-"/>
+ <reg32 offset="0x0f0c" name="UCHE_CCHE_HW_DBG_CNTL" variants="A8XX-"/>
+
+ <!-- VBIF only existed on early a6xx, and was later replaced with GBIF -->
+
+ <reg32 offset="0x3000" name="VBIF_VERSION" variants="A6XX"/>
+ <reg32 offset="0x3001" name="VBIF_CLKON" variants="A6XX">
<bitfield pos="1" name="FORCE_ON_TESTBUS" type="boolean"/>
</reg32>
- <reg32 offset="0x302A" name="VBIF_GATE_OFF_WRREQ_EN"/>
- <reg32 offset="0x3080" name="VBIF_XIN_HALT_CTRL0"/>
- <reg32 offset="0x3081" name="VBIF_XIN_HALT_CTRL1"/>
- <reg32 offset="0x3084" name="VBIF_TEST_BUS_OUT_CTRL"/>
- <reg32 offset="0x3085" name="VBIF_TEST_BUS1_CTRL0"/>
- <reg32 offset="0x3086" name="VBIF_TEST_BUS1_CTRL1">
+ <reg32 offset="0x302A" name="VBIF_GATE_OFF_WRREQ_EN" variants="A6XX"/>
+ <reg32 offset="0x3080" name="VBIF_XIN_HALT_CTRL0" variants="A6XX"/>
+ <reg32 offset="0x3081" name="VBIF_XIN_HALT_CTRL1" variants="A6XX"/>
+ <reg32 offset="0x3084" name="VBIF_TEST_BUS_OUT_CTRL" variants="A6XX"/>
+ <reg32 offset="0x3085" name="VBIF_TEST_BUS1_CTRL0" variants="A6XX"/>
+ <reg32 offset="0x3086" name="VBIF_TEST_BUS1_CTRL1" variants="A6XX">
<bitfield low="0" high="3" name="DATA_SEL"/>
</reg32>
- <reg32 offset="0x3087" name="VBIF_TEST_BUS2_CTRL0"/>
- <reg32 offset="0x3088" name="VBIF_TEST_BUS2_CTRL1">
+ <reg32 offset="0x3087" name="VBIF_TEST_BUS2_CTRL0" variants="A6XX"/>
+ <reg32 offset="0x3088" name="VBIF_TEST_BUS2_CTRL1" variants="A6XX">
<bitfield low="0" high="8" name="DATA_SEL"/>
</reg32>
- <reg32 offset="0x308c" name="VBIF_TEST_BUS_OUT"/>
- <reg32 offset="0x30d0" name="VBIF_PERF_CNT_SEL0"/>
- <reg32 offset="0x30d1" name="VBIF_PERF_CNT_SEL1"/>
- <reg32 offset="0x30d2" name="VBIF_PERF_CNT_SEL2"/>
- <reg32 offset="0x30d3" name="VBIF_PERF_CNT_SEL3"/>
- <reg32 offset="0x30d8" name="VBIF_PERF_CNT_LOW0"/>
- <reg32 offset="0x30d9" name="VBIF_PERF_CNT_LOW1"/>
- <reg32 offset="0x30da" name="VBIF_PERF_CNT_LOW2"/>
- <reg32 offset="0x30db" name="VBIF_PERF_CNT_LOW3"/>
- <reg32 offset="0x30e0" name="VBIF_PERF_CNT_HIGH0"/>
- <reg32 offset="0x30e1" name="VBIF_PERF_CNT_HIGH1"/>
- <reg32 offset="0x30e2" name="VBIF_PERF_CNT_HIGH2"/>
- <reg32 offset="0x30e3" name="VBIF_PERF_CNT_HIGH3"/>
- <reg32 offset="0x3100" name="VBIF_PERF_PWR_CNT_EN0"/>
- <reg32 offset="0x3101" name="VBIF_PERF_PWR_CNT_EN1"/>
- <reg32 offset="0x3102" name="VBIF_PERF_PWR_CNT_EN2"/>
- <reg32 offset="0x3110" name="VBIF_PERF_PWR_CNT_LOW0"/>
- <reg32 offset="0x3111" name="VBIF_PERF_PWR_CNT_LOW1"/>
- <reg32 offset="0x3112" name="VBIF_PERF_PWR_CNT_LOW2"/>
- <reg32 offset="0x3118" name="VBIF_PERF_PWR_CNT_HIGH0"/>
- <reg32 offset="0x3119" name="VBIF_PERF_PWR_CNT_HIGH1"/>
- <reg32 offset="0x311a" name="VBIF_PERF_PWR_CNT_HIGH2"/>
-
+ <reg32 offset="0x308c" name="VBIF_TEST_BUS_OUT" variants="A6XX"/>
+ <reg32 offset="0x30d0" name="VBIF_PERF_CNT_SEL0" variants="A6XX"/>
+ <reg32 offset="0x30d1" name="VBIF_PERF_CNT_SEL1" variants="A6XX"/>
+ <reg32 offset="0x30d2" name="VBIF_PERF_CNT_SEL2" variants="A6XX"/>
+ <reg32 offset="0x30d3" name="VBIF_PERF_CNT_SEL3" variants="A6XX"/>
+ <reg32 offset="0x30d8" name="VBIF_PERF_CNT_LOW0" variants="A6XX"/>
+ <reg32 offset="0x30d9" name="VBIF_PERF_CNT_LOW1" variants="A6XX"/>
+ <reg32 offset="0x30da" name="VBIF_PERF_CNT_LOW2" variants="A6XX"/>
+ <reg32 offset="0x30db" name="VBIF_PERF_CNT_LOW3" variants="A6XX"/>
+ <reg32 offset="0x30e0" name="VBIF_PERF_CNT_HIGH0" variants="A6XX"/>
+ <reg32 offset="0x30e1" name="VBIF_PERF_CNT_HIGH1" variants="A6XX"/>
+ <reg32 offset="0x30e2" name="VBIF_PERF_CNT_HIGH2" variants="A6XX"/>
+ <reg32 offset="0x30e3" name="VBIF_PERF_CNT_HIGH3" variants="A6XX"/>
+ <reg32 offset="0x3100" name="VBIF_PERF_PWR_CNT_EN0" variants="A6XX"/>
+ <reg32 offset="0x3101" name="VBIF_PERF_PWR_CNT_EN1" variants="A6XX"/>
+ <reg32 offset="0x3102" name="VBIF_PERF_PWR_CNT_EN2" variants="A6XX"/>
+ <reg32 offset="0x3110" name="VBIF_PERF_PWR_CNT_LOW0" variants="A6XX"/>
+ <reg32 offset="0x3111" name="VBIF_PERF_PWR_CNT_LOW1" variants="A6XX"/>
+ <reg32 offset="0x3112" name="VBIF_PERF_PWR_CNT_LOW2" variants="A6XX"/>
+ <reg32 offset="0x3118" name="VBIF_PERF_PWR_CNT_HIGH0" variants="A6XX"/>
+ <reg32 offset="0x3119" name="VBIF_PERF_PWR_CNT_HIGH1" variants="A6XX"/>
+ <reg32 offset="0x311a" name="VBIF_PERF_PWR_CNT_HIGH2" variants="A6XX"/>
+
+ <reg32 offset="0x3c00" name="GBIF_CX_CONFIG" variants="A8XX-"/>
<reg32 offset="0x3c01" name="GBIF_SCACHE_CNTL0"/>
<reg32 offset="0x3c02" name="GBIF_SCACHE_CNTL1"/>
<reg32 offset="0x3c03" name="GBIF_QSB_SIDE0"/>
@@ -712,30 +1272,66 @@ by a particular renderpass/blit.
<reg32 offset="0x3c06" name="GBIF_QSB_SIDE3"/>
<reg32 offset="0x3c45" name="GBIF_HALT"/>
<reg32 offset="0x3c46" name="GBIF_HALT_ACK"/>
+ <reg32 offset="0x3c49" name="GBIF_REINIT_ENABLE" variants="A8XX-"/>
+ <reg32 offset="0x3c4a" name="GBIF_REINIT_DONE" variants="A8XX-"/>
<reg32 offset="0x3cc0" name="GBIF_PERF_PWR_CNT_EN"/>
<reg32 offset="0x3cc1" name="GBIF_PERF_PWR_CNT_CLR"/>
<reg32 offset="0x3cc2" name="GBIF_PERF_CNT_SEL"/>
- <reg32 offset="0x3cc3" name="GBIF_PERF_PWR_CNT_SEL"/>
- <reg32 offset="0x3cc4" name="GBIF_PERF_CNT_LOW0"/>
- <reg32 offset="0x3cc5" name="GBIF_PERF_CNT_LOW1"/>
- <reg32 offset="0x3cc6" name="GBIF_PERF_CNT_LOW2"/>
- <reg32 offset="0x3cc7" name="GBIF_PERF_CNT_LOW3"/>
- <reg32 offset="0x3cc8" name="GBIF_PERF_CNT_HIGH0"/>
- <reg32 offset="0x3cc9" name="GBIF_PERF_CNT_HIGH1"/>
- <reg32 offset="0x3cca" name="GBIF_PERF_CNT_HIGH2"/>
- <reg32 offset="0x3ccb" name="GBIF_PERF_CNT_HIGH3"/>
- <reg32 offset="0x3ccc" name="GBIF_PWR_CNT_LOW0"/>
- <reg32 offset="0x3ccd" name="GBIF_PWR_CNT_LOW1"/>
- <reg32 offset="0x3cce" name="GBIF_PWR_CNT_LOW2"/>
- <reg32 offset="0x3ccf" name="GBIF_PWR_CNT_HIGH0"/>
- <reg32 offset="0x3cd0" name="GBIF_PWR_CNT_HIGH1"/>
- <reg32 offset="0x3cd1" name="GBIF_PWR_CNT_HIGH2"/>
+ <reg32 offset="0x3cc3" name="GBIF_PERF_CNT_SEL_1" variants="A8XX-"/>
+
+ <reg32 offset="0x3cc3" name="GBIF_PERF_PWR_CNT_SEL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cc4" name="GBIF_PERF_CNT_LOW0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cc5" name="GBIF_PERF_CNT_LOW1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cc6" name="GBIF_PERF_CNT_LOW2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cc7" name="GBIF_PERF_CNT_LOW3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cc8" name="GBIF_PERF_CNT_HIGH0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cc9" name="GBIF_PERF_CNT_HIGH1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cca" name="GBIF_PERF_CNT_HIGH2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3ccb" name="GBIF_PERF_CNT_HIGH3" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3ccc" name="GBIF_PWR_CNT_LOW0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3ccd" name="GBIF_PWR_CNT_LOW1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cce" name="GBIF_PWR_CNT_LOW2" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3ccf" name="GBIF_PWR_CNT_HIGH0" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cd0" name="GBIF_PWR_CNT_HIGH1" variants="A6XX-A7XX"/>
+ <reg32 offset="0x3cd1" name="GBIF_PWR_CNT_HIGH2" variants="A6XX-A7XX"/>
+
+ <reg32 offset="0x3cc4" name="GBIF_PWR_CNT_SEL" variants="A8XX"/>
+ <reg32 offset="0x3cc6" name="GBIF_PERF_CNT_LO_0" variants="A8XX"/>
+ <reg32 offset="0x3cc7" name="GBIF_PERF_CNT_HI_0" variants="A8XX"/>
+ <reg32 offset="0x3cc8" name="GBIF_PERF_CNT_LO_1" variants="A8XX"/>
+ <reg32 offset="0x3cc9" name="GBIF_PERF_CNT_HI_1" variants="A8XX"/>
+ <reg32 offset="0x3cca" name="GBIF_PERF_CNT_LO_2" variants="A8XX"/>
+ <reg32 offset="0x3ccb" name="GBIF_PERF_CNT_HI_2" variants="A8XX"/>
+ <reg32 offset="0x3ccc" name="GBIF_PERF_CNT_LO_3" variants="A8XX"/>
+ <reg32 offset="0x3ccd" name="GBIF_PERF_CNT_HI_3" variants="A8XX"/>
+ <reg32 offset="0x3cce" name="GBIF_PERF_CNT_LO_4" variants="A8XX"/>
+ <reg32 offset="0x3ccf" name="GBIF_PERF_CNT_HI_4" variants="A8XX"/>
+ <reg32 offset="0x3cd0" name="GBIF_PERF_CNT_LO_5" variants="A8XX"/>
+ <reg32 offset="0x3cd1" name="GBIF_PERF_CNT_HI_5" variants="A8XX"/>
+ <reg32 offset="0x3cd2" name="GBIF_PERF_CNT_LO_6" variants="A8XX"/>
+ <reg32 offset="0x3cd3" name="GBIF_PERF_CNT_HI_6" variants="A8XX"/>
+ <reg32 offset="0x3cd4" name="GBIF_PERF_CNT_LO_7" variants="A8XX"/>
+ <reg32 offset="0x3cd5" name="GBIF_PERF_CNT_HI_7" variants="A8XX"/>
+ <reg32 offset="0x3ce0" name="GBIF_PWR_CNT_LO_0" variants="A8XX"/>
+ <reg32 offset="0x3ce1" name="GBIF_PWR_CNT_LO_1" variants="A8XX"/>
+ <reg32 offset="0x3ce2" name="GBIF_PWR_CNT_LO_2" variants="A8XX"/>
+ <reg32 offset="0x3ce3" name="GBIF_PWR_CNT_HI_0" variants="A8XX"/>
+ <reg32 offset="0x3ce4" name="GBIF_PWR_CNT_HI_1" variants="A8XX"/>
+ <reg32 offset="0x3ce5" name="GBIF_PWR_CNT_HI_2" variants="A8XX"/>
<reg32 offset="0x0c00" name="VSC_DBG_ECO_CNTL"/>
- <reg32 offset="0x0c02" name="VSC_BIN_SIZE" usage="rp_blit">
- <bitfield name="WIDTH" low="0" high="7" shr="5" type="uint"/>
- <bitfield name="HEIGHT" low="8" high="16" shr="4" type="uint"/>
+ <reg32 offset="0x0df0" name="VSC_KMD_DBG_ECO_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x0c02" name="VSC_BIN_SIZE" usage="rp_blit" variants="A6XX-A7XX">
+ <bitfield name="BINW" low="0" high="7" shr="5" type="uint"/>
+ <bitfield name="BINH" low="8" high="16" shr="4" type="uint"/>
</reg32>
+
+ <bitset name="a8xx_bin_size" inline="yes">
+ <bitfield name="BINW" low="0" high="9" shr="5" type="uint"/>
+ <bitfield name="BINH" low="16" high="26" shr="4" type="uint"/>
+ </bitset>
+
+ <reg32 offset="0x0c02" name="VSC_BIN_SIZE" type="a8xx_bin_size" usage="rp_blit" variants="A8XX"/>
<reg64 offset="0x0c03" name="VSC_SIZE_BASE" type="waddress" usage="cmd"/>
<reg32 offset="0x0c06" name="VSC_EXPANDED_BIN_CNTL" usage="rp_blit">
<bitfield name="NX" low="1" high="10" type="uint"/>
@@ -803,10 +1399,14 @@ by a particular renderpass/blit.
<reg32 offset="0x0d08" name="VSC_UNKNOWN_0D08" variants="A7XX-" usage="rp_blit"/>
- <reg32 offset="0x0E10" name="UCHE_UNKNOWN_0E10" variants="A7XX-" usage="cmd"/>
- <reg32 offset="0x0E11" name="UCHE_UNKNOWN_0E11" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0x0e10" name="UCHE_UNKNOWN_0E10" variants="A7XX" usage="init"/>
+ <reg32 offset="0x0e10" name="UCHE_VARB_IDLE_TIMEOUT" variants="A8XX-"/>
+ <reg32 offset="0x0e11" name="UCHE_UNKNOWN_0E11" variants="A7XX" usage="init"/>
+ <reg32 offset="0x0e11" name="UCHE_CLIENT_PF" variants="A8XX-"/>
<!-- always 0x03200000 ? -->
- <reg32 offset="0x0e12" name="UCHE_UNKNOWN_0E12" usage="cmd"/>
+ <reg32 offset="0x0e12" name="UCHE_UNKNOWN_0E12" variants="A6XX-A7XX" usage="init"/>
+ <reg32 offset="0x0e15" name="UCHE_DBG_ECO_CNTL_0" variants="A8XX-"/>
+ <reg32 offset="0x0e16" name="UCHE_HW_DBG_CNTL" variants="A8XX-"/>
<!-- adreno_reg_xy has 15 bits per coordinate, but a6xx registers only have 14 -->
<bitset name="a6xx_reg_xy" inline="yes">
@@ -829,6 +1429,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8000" name="GRAS_CL_CNTL" type="a6xx_gras_cl_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8200" name="GRAS_CL_CNTL" type="a6xx_gras_cl_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_xs_clip_cull_distance" inline="yes">
<bitfield name="CLIP_MASK" low="0" high="7"/>
@@ -839,6 +1440,18 @@ by a particular renderpass/blit.
<reg32 offset="0x8003" name="GRAS_CL_GS_CLIP_CULL_DISTANCE" type="a6xx_gras_xs_clip_cull_distance" usage="rp_blit" variants="A6XX-A7XX" />
<reg32 offset="0x8004" name="GRAS_CL_ARRAY_SIZE" low="0" high="10" type="uint" usage="rp_blit" variants="A6XX-A7XX" />
+ <reg32 offset="0x8201" name="GRAS_CL_VS_CLIP_CULL_DISTANCE" type="a6xx_gras_xs_clip_cull_distance" usage="rp_blit" variants="A8XX" />
+ <reg32 offset="0x8202" name="GRAS_CL_DS_CLIP_CULL_DISTANCE" type="a6xx_gras_xs_clip_cull_distance" usage="rp_blit" variants="A8XX" />
+ <reg32 offset="0x8203" name="GRAS_CL_GS_CLIP_CULL_DISTANCE" type="a6xx_gras_xs_clip_cull_distance" usage="rp_blit" variants="A8XX" />
+ <reg32 offset="0x8204" name="GRAS_CL_ARRAY_SIZE" low="0" high="10" type="uint" usage="rp_blit" variants="A8XX" />
+
+ <reg32 offset="0x8228" name="GRAS_UNKNOWN_8228" variants="A8XX-"/>
+ <reg32 offset="0x8229" name="GRAS_UNKNOWN_8229" variants="A8XX-"/>
+ <reg32 offset="0x822a" name="GRAS_UNKNOWN_822A" variants="A8XX-"/>
+ <reg32 offset="0x822b" name="GRAS_UNKNOWN_822B" variants="A8XX-"/>
+ <reg32 offset="0x822c" name="GRAS_UNKNOWN_822C" variants="A8XX-"/>
+ <reg32 offset="0x822d" name="GRAS_UNKNOWN_822D" variants="A8XX-"/>
+
<bitset name="a6xx_gras_cl_interp_cntl" inline="yes">
<!-- see also RB_INTERP_CNTL -->
<bitfield name="IJ_PERSP_PIXEL" pos="0" type="boolean"/>
@@ -853,6 +1466,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8005" name="GRAS_CL_INTERP_CNTL" type="a6xx_gras_cl_interp_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8080" name="GRAS_CL_INTERP_CNTL" type="a6xx_gras_cl_interp_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_cl_guardband_clip_adj" inline="true">
<bitfield name="HORZ" low="0" high="8" type="uint"/>
@@ -860,9 +1474,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8006" name="GRAS_CL_GUARDBAND_CLIP_ADJ" type="a6xx_gras_cl_guardband_clip_adj" variants="A6XX-A7XX" usage="rp_blit"/>
-
- <!-- Something connected to depth-stencil attachment size -->
- <reg32 offset="0x8007" name="GRAS_UNKNOWN_8007" variants="A7XX-" usage="rp_blit"/>
+ <reg32 offset="0x8205" name="GRAS_CL_GUARDBAND_CLIP_ADJ" type="a6xx_gras_cl_guardband_clip_adj" variants="A8XX-" usage="rp_blit"/>
<!-- the scale/offset is per view, with up to 6 views -->
<bitset name="a6xx_gras_bin_foveat" inline="yes">
@@ -887,6 +1499,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8008" name="GRAS_BIN_FOVEAT" type="a6xx_gras_bin_foveat" variants="A7XX" usage="cmd"/>
+ <reg32 offset="0x8206" name="GRAS_BIN_FOVEAT" type="a6xx_gras_bin_foveat" variants="A8XX-" usage="cmd"/>
<reg32 offset="0x8009" name="GRAS_BIN_FOVEAT_OFFSET_0" variants="A7XX-" usage="cmd">
<bitfield name="XOFFSET_0" low="0" high="9" shr="2" type="uint"/>
@@ -921,10 +1534,23 @@ by a particular renderpass/blit.
<reg32 offset="5" name="ZSCALE" type="float"/>
</array>
+ <array offset="0x82d0" name="GRAS_CL_VIEWPORT" stride="6" length="16" variants="A8XX-" usage="rp_blit">
+ <reg32 offset="0" name="XOFFSET" type="float"/>
+ <reg32 offset="1" name="XSCALE" type="float"/>
+ <reg32 offset="2" name="YOFFSET" type="float"/>
+ <reg32 offset="3" name="YSCALE" type="float"/>
+ <reg32 offset="4" name="ZOFFSET" type="float"/>
+ <reg32 offset="5" name="ZSCALE" type="float"/>
+ </array>
+
<array offset="0x8070" name="GRAS_CL_VIEWPORT_ZCLAMP" stride="2" length="16" variants="A6XX-A7XX" usage="rp_blit">
<reg32 offset="0" name="MIN" type="float"/>
<reg32 offset="1" name="MAX" type="float"/>
</array>
+ <array offset="0x80c0" name="GRAS_CL_VIEWPORT_ZCLAMP" stride="2" length="16" variants="A8XX-" usage="rp_blit">
+ <reg32 offset="0" name="MIN" type="float"/>
+ <reg32 offset="1" name="MAX" type="float"/>
+ </array>
<bitset name="a6xx_gras_su_cntl" varset="chip">
<bitfield name="CULL_FRONT" pos="0" type="boolean"/>
@@ -951,6 +1577,13 @@ by a particular renderpass/blit.
<bitfield name="UNK20" low="20" high="22" variants="A6XX-A7XX"/>
</bitset>
<reg32 offset="0x8090" name="GRAS_SU_CNTL" type="a6xx_gras_su_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8209" name="GRAS_SU_CNTL" type="a6xx_gras_su_cntl" variants="A8XX-" usage="rp_blit"/>
+
+ <!-- Fields moved from GRAS_SU_CNTL on earlier gens: -->
+ <reg32 offset="0x820c" name="GRAS_SU_STEREO_CNTL" variants="A8XX-" usage="rp_blit">
+ <bitfield name="RENDERTARGETINDEXINCR" pos="18" type="boolean"/>
+ <bitfield name="VIEWPORTINDEXINCR" pos="19" type="boolean"/>
+ </reg32>
<bitset name="a6xx_gras_su_point_minmax" inline="yes">
<bitfield name="MIN" low="0" high="15" type="ufixed" radix="4"/>
@@ -958,25 +1591,31 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8091" name="GRAS_SU_POINT_MINMAX" type="a6xx_gras_su_point_minmax" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x820a" name="GRAS_SU_POINT_MINMAX" type="a6xx_gras_su_point_minmax" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0x8092" name="GRAS_SU_POINT_SIZE" low="0" high="15" type="fixed" radix="4" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x820b" name="GRAS_SU_POINT_SIZE" low="0" high="15" type="fixed" radix="4" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_su_depth_cntl" inline="yes">
<bitfield name="Z_TEST_ENABLE" pos="0" type="boolean"/>
</bitset>
<reg32 offset="0x8114" name="GRAS_SU_DEPTH_CNTL" variants="A6XX-A7XX" type="a6xx_gras_su_depth_cntl" usage="rp_blit"/>
+ <reg32 offset="0x8086" name="GRAS_SU_DEPTH_CNTL" variants="A8XX-" type="a6xx_gras_su_depth_cntl" usage="rp_blit"/>
<bitset name="a6xx_gras_su_stencil_cntl" inline="yes">
<bitfield name="STENCIL_ENABLE" pos="0" type="boolean"/>
</bitset>
<reg32 offset="0x8115" name="GRAS_SU_STENCIL_CNTL" type="a6xx_gras_su_stencil_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8087" name="GRAS_SU_STENCIL_CNTL" type="a6xx_gras_su_stencil_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_su_render_cntl" inline="yes">
<bitfield name="FS_DISABLE" pos="7" type="boolean"/>
</bitset>
<reg32 offset="0x8116" name="GRAS_SU_RENDER_CNTL" type="a6xx_gras_su_render_cntl" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8088" name="GRAS_SU_RENDER_CNTL" type="a6xx_gras_su_render_cntl" variants="A8XX-" usage="rp_blit"/>
<!-- 0x8093 invalid -->
<bitset name="a6xx_depth_plane_cntl" inline="yes">
@@ -984,16 +1623,25 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8094" name="GRAS_SU_DEPTH_PLANE_CNTL" type="a6xx_depth_plane_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8089" name="GRAS_SU_DEPTH_PLANE_CNTL" type="a6xx_depth_plane_cntl" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0x8095" name="GRAS_SU_POLY_OFFSET_SCALE" type="float" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x808a" name="GRAS_SU_POLY_OFFSET_SCALE" type="float" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0x8096" name="GRAS_SU_POLY_OFFSET_OFFSET" type="float" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x808b" name="GRAS_SU_POLY_OFFSET_OFFSET" type="float" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0x8097" name="GRAS_SU_POLY_OFFSET_OFFSET_CLAMP" type="float" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x808c" name="GRAS_SU_POLY_OFFSET_OFFSET_CLAMP" type="float" variants="A8XX-" usage="rp_blit"/>
+
<bitset name="a6xx_depth_buffer_info" inline="yes">
<bitfield name="DEPTH_FORMAT" low="0" high="2" type="a6xx_depth_format"/>
- <bitfield name="UNK3" pos="3"/>
+ <bitfield name="READ_ONLY" pos="3" type="boolean"/>
</bitset>
<!-- duplicates RB_DEPTH_BUFFER_INFO: -->
<reg32 offset="0x8098" name="GRAS_SU_DEPTH_BUFFER_INFO" type="a6xx_depth_buffer_info" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x808d" name="GRAS_SU_DEPTH_BUFFER_INFO" type="a6xx_depth_buffer_info" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_su_conservative_ras_cntl" inline="yes">
<bitfield name="CONSERVATIVERASEN" pos="0" type="boolean"/>
@@ -1008,6 +1656,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8099" name="GRAS_SU_CONSERVATIVE_RAS_CNTL" type="a6xx_gras_su_conservative_ras_cntl" variants="A6XX-A7XX" usage="cmd"/>
+ <reg32 offset="0x820d" name="GRAS_SU_CONSERVATIVE_RAS_CNTL" type="a6xx_gras_su_conservative_ras_cntl" variants="A8XX-" usage="cmd"/>
<reg32 offset="0x809a" name="GRAS_SU_PATH_RENDERING_CNTL">
<bitfield name="UNK0" pos="0" type="boolean"/>
@@ -1022,10 +1671,16 @@ by a particular renderpass/blit.
<reg32 offset="0x809c" name="GRAS_SU_GS_SIV_CNTL" type="a6xx_gras_us_xs_siv_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x809d" name="GRAS_SU_DS_SIV_CNTL" type="a6xx_gras_us_xs_siv_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x820e" name="GRAS_SU_VS_SIV_CNTL" type="a6xx_gras_us_xs_siv_cntl" variants="A8XX" usage="rp_blit"/>
+ <reg32 offset="0x820f" name="GRAS_SU_GS_SIV_CNTL" type="a6xx_gras_us_xs_siv_cntl" variants="A8XX" usage="rp_blit"/>
+ <reg32 offset="0x8210" name="GRAS_SU_DS_SIV_CNTL" type="a6xx_gras_us_xs_siv_cntl" variants="A8XX" usage="rp_blit"/>
+
<bitset name="a6xx_rast_cntl" inline="yes">
<bitfield name="MODE" low="0" high="1" type="a6xx_polygon_mode"/>
</bitset>
+ <reg32 offset="0x8211" name="GRAS_RAST_CNTL" type="a6xx_rast_cntl" variants="A8XX-" usage="rp_blit"/>
+
<enum name="a6xx_sequenced_thread_dist">
<value value="0x0" name="DIST_SCREEN_COORD"/>
<value value="0x1" name="DIST_ALL_TO_RB0"/>
@@ -1073,7 +1728,6 @@ by a particular renderpass/blit.
</enum>
<bitset name="a6xx_gras_sc_cntl" inline="yes">
- <bitfield name="CCUSINGLECACHELINESIZE" low="0" high="2"/>
<bitfield name="SINGLE_PRIM_MODE" low="3" high="4" type="a6xx_single_prim_mode"/>
<bitfield name="RASTER_MODE" pos="5" type="a6xx_raster_mode"/>
<bitfield name="RASTER_DIRECTION" low="6" high="7" type="a6xx_raster_direction"/>
@@ -1084,7 +1738,10 @@ by a particular renderpass/blit.
<bitfield name="EARLYVIZOUTEN" pos="12" type="boolean"/>
</bitset>
- <reg32 offset="0x80a0" name="GRAS_SC_CNTL" type="a6xx_gras_sc_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x80a0" name="GRAS_SC_CNTL" type="a6xx_gras_sc_cntl" variants="A6XX-A7XX" usage="rp_blit">
+ <bitfield name="CCUSINGLECACHELINESIZE" low="0" high="2" variants="A6XX-A7XX"/>
+ </reg32>
+ <reg32 offset="0x8230" name="GRAS_SC_CNTL" type="a6xx_gras_sc_cntl" variants="A8XX-" usage="rp_blit"/>
<enum name="a6xx_render_mode">
<value value="0x0" name="RENDERING_PASS"/>
@@ -1123,6 +1780,28 @@ by a particular renderpass/blit.
<reg32 offset="0x80a1" name="GRAS_SC_BIN_CNTL" type="a6xx_bin_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <!-- Common fields for RB_CNTL and GRAS_SC_BIN_CNTL -->
+ <bitset name="a8xx_bin_cntl" inline="yes">
+ <bitfield name="BINW" low="0" high="9" shr="5" type="uint"/>
+ <bitfield name="BINH" low="16" high="26" shr="4" type="uint"/>
+ <bitfield name="RENDER_MODE" low="11" high="13" type="a6xx_render_mode"/>
+ <doc>
+ Allows draws that don't have GRAS_LRZ_CNTL.LRZ_WRITE but have
+ GRAS_LRZ_CNTL.ENABLE to contribute to LRZ during RENDERING pass.
+ In sysmem mode GRAS_LRZ_CNTL.LRZ_WRITE is not considered.
+ </doc>
+ <bitfield name="LRZ_FEEDBACK_ZMODE_MASK" low="28" high="30" type="a6xx_lrz_feedback_mask"/>
+ <doc>Disable LRZ feedback writes</doc>
+ <bitfield name="FORCE_LRZ_WRITE_DIS" pos="31" type="boolean"/>
+ </bitset>
+
+ <reg32 offset="0x8231" name="GRAS_SC_BIN_CNTL" type="a8xx_bin_cntl" variants="A8XX-" usage="rp_blit">
+ <bitfield name="CONS_VIS_IN_BINNING" pos="10" type="boolean"/>
+ <bitfield name="FORCE_BI_DIR_LRZ_DISABLE" pos="14" type="boolean"/>
+ <bitfield name="FORCE_LRZ_DIS" pos="15" type="boolean"/>
+ <bitfield name="BIN_VRS_DIS" pos="27" type="boolean"/>
+ </reg32>
+
<bitset name="a6xx_gras_sc_ras_msaa_cntl" inline="yes">
<bitfield name="SAMPLES" low="0" high="1" type="a3xx_msaa_samples"/>
<bitfield name="UNK2" pos="2"/>
@@ -1130,6 +1809,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x80a2" name="GRAS_SC_RAS_MSAA_CNTL" type="a6xx_gras_sc_ras_msaa_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8232" name="GRAS_SC_RAS_MSAA_CNTL" type="a6xx_gras_sc_ras_msaa_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_sc_dest_msaa_cntl" inline="yes">
<bitfield name="SAMPLES" low="0" high="1" type="a3xx_msaa_samples"/>
@@ -1137,6 +1817,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x80a3" name="GRAS_SC_DEST_MSAA_CNTL" type="a6xx_gras_sc_dest_msaa_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8233" name="GRAS_SC_DEST_MSAA_CNTL" type="a6xx_gras_sc_dest_msaa_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_msaa_sample_pos_cntl" inline="yes">
<bitfield name="UNK0" pos="0"/>
@@ -1158,13 +1839,21 @@ by a particular renderpass/blit.
<reg32 offset="0x80a5" name="GRAS_SC_PROGRAMMABLE_MSAA_POS_0" type="a6xx_programmable_msaa_pos" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x80a6" name="GRAS_SC_PROGRAMMABLE_MSAA_POS_1" type="a6xx_programmable_msaa_pos" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8237" name="GRAS_SC_MSAA_SAMPLE_POS_CNTL" type="a6xx_msaa_sample_pos_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8238" name="GRAS_SC_PROGRAMMABLE_MSAA_POS_0" type="a6xx_programmable_msaa_pos" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8239" name="GRAS_SC_PROGRAMMABLE_MSAA_POS_1" type="a6xx_programmable_msaa_pos" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x823a" name="GRAS_SC_PROGRAMMABLE_MSAA_POS_2" type="a6xx_programmable_msaa_pos" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x823b" name="GRAS_SC_PROGRAMMABLE_MSAA_POS_3" type="a6xx_programmable_msaa_pos" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0x80a7" name="GRAS_ROTATION_CNTL" variants="A7XX" usage="cmd"/>
+ <reg32 offset="0x8207" name="GRAS_ROTATION_CNTL" variants="A8XX-" usage="cmd"/>
<bitset name="a6xx_screen_scissor_cntl" inline="yes">
<bitfield name="SCISSOR_DISABLE" pos="0" type="boolean"/>
</bitset>
<reg32 offset="0x80af" name="GRAS_SC_SCREEN_SCISSOR_CNTL" type="a6xx_screen_scissor_cntl" variants="A6XX-A7XX" pos="0" usage="cmd"/>
+ <reg32 offset="0x8234" name="GRAS_SC_SCREEN_SCISSOR_CNTL" type="a6xx_screen_scissor_cntl" variants="A8XX-" pos="0" usage="cmd"/>
<bitset name="a6xx_scissor_xy" inline="yes">
<bitfield name="X" low="0" high="15" type="uint"/>
@@ -1176,14 +1865,26 @@ by a particular renderpass/blit.
<reg32 offset="1" name="BR" type="a6xx_scissor_xy"/>
</array>
+ <array offset="0x8240" name="GRAS_SC_SCREEN_SCISSOR" stride="2" length="16" variants="A8XX-" usage="rp_blit">
+ <reg32 offset="0" name="TL" type="a6xx_scissor_xy"/>
+ <reg32 offset="1" name="BR" type="a6xx_scissor_xy"/>
+ </array>
+
<array offset="0x80d0" name="GRAS_SC_VIEWPORT_SCISSOR" stride="2" length="16" variants="A6XX-A7XX" usage="rp_blit">
<reg32 offset="0" name="TL" type="a6xx_scissor_xy"/>
<reg32 offset="1" name="BR" type="a6xx_scissor_xy"/>
</array>
+ <array offset="0x8270" name="GRAS_SC_VIEWPORT_SCISSOR" stride="2" length="16" variants="A8XX-" usage="rp_blit">
+ <reg32 offset="0" name="TL" type="a6xx_scissor_xy"/>
+ <reg32 offset="1" name="BR" type="a6xx_scissor_xy"/>
+ </array>
<reg32 offset="0x80f0" name="GRAS_SC_WINDOW_SCISSOR_TL" type="a6xx_reg_xy" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x80f1" name="GRAS_SC_WINDOW_SCISSOR_BR" type="a6xx_reg_xy" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8235" name="GRAS_SC_WINDOW_SCISSOR_TL" type="a6xx_reg_xy" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8236" name="GRAS_SC_WINDOW_SCISSOR_BR" type="a6xx_reg_xy" variants="A8XX-" usage="rp_blit"/>
+
<enum name="a6xx_fsr_combiner">
<value value="0" name="FSR_COMBINER_OP_KEEP"/>
<value value="1" name="FSR_COMBINER_OP_REPLACE"/>
@@ -1203,6 +1904,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x80f4" name="GRAS_VRS_CONFIG" type="a6xx_gras_vrs_config" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8208" name="GRAS_VRS_CONFIG" type="a6xx_gras_vrs_config" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_quality_buffer_info" inline="yes">
<bitfield name="LAYERED" pos="0" type="boolean"/>
@@ -1210,6 +1912,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x80f5" name="GRAS_QUALITY_BUFFER_INFO" type="a6xx_gras_quality_buffer_info" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x808e" name="GRAS_QUALITY_BUFFER_INFO" type="a6xx_gras_quality_buffer_info" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_gras_quality_buffer_dimension" inline="yes">
<bitfield name="WIDTH" low="0" high="15" type="uint"/>
@@ -1217,8 +1920,10 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x80f6" name="GRAS_QUALITY_BUFFER_DIMENSION" type="a6xx_gras_quality_buffer_dimension" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x808f" name="GRAS_QUALITY_BUFFER_DIMENSION" type="a6xx_gras_quality_buffer_dimension" variants="A8XX-" usage="rp_blit"/>
<reg64 offset="0x80f8" name="GRAS_QUALITY_BUFFER_BASE" variants="A7XX" type="waddress" usage="rp_blit"/>
+ <reg64 offset="0x8090" name="GRAS_QUALITY_BUFFER_BASE" variants="A8XX-" type="waddress" usage="rp_blit"/>
<bitset name="a6xx_gras_quality_buffer_pitch" inline="yes">
<bitfield name="PITCH" shr="6" low="0" high="7" type="uint"/>
@@ -1226,6 +1931,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x80fa" name="GRAS_QUALITY_BUFFER_PITCH" type="a6xx_gras_quality_buffer_pitch" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8092" name="GRAS_QUALITY_BUFFER_PITCH" type="a6xx_gras_quality_buffer_pitch" variants="A8XX-" usage="rp_blit"/>
<enum name="a6xx_lrz_dir_status">
<value value="0x1" name="LRZ_DIR_LE"/>
@@ -1244,7 +1950,6 @@ by a particular renderpass/blit.
- 0.0 if GREATER
- 1.0 if LESS
</doc>
- <bitfield name="FC_ENABLE" pos="3" type="boolean" variants="A6XX"/>
<!-- set when depth-test + depth-write enabled -->
<bitfield name="Z_WRITE_ENABLE" pos="4" type="boolean"/>
<bitfield name="Z_BOUNDS_ENABLE" pos="5" type="boolean"/>
@@ -1258,11 +1963,27 @@ by a particular renderpass/blit.
Disable LRZ based on previous direction and the current one.
If DIR_WRITE is not enabled - there is no write to direction buffer.
</doc>
- <bitfield name="DISABLE_ON_WRONG_DIR" pos="9" type="boolean" variants="A6XX"/>
<bitfield name="Z_FUNC" low="11" high="13" type="adreno_compare_func" variants="A7XX-"/>
</bitset>
- <reg32 offset="0x8100" name="GRAS_LRZ_CNTL" type="a6xx_gras_lrz_cntl" usage="rp_blit" variants="A6XX-A7XX"/>
+ <reg32 offset="0x8100" name="GRAS_LRZ_CNTL" type="a6xx_gras_lrz_cntl" usage="rp_blit" variants="A6XX">
+ <bitfield name="FC_ENABLE" pos="3" type="boolean" variants="A6XX"/>
+ <bitfield name="DISABLE_ON_WRONG_DIR" pos="9" type="boolean" variants="A6XX"/>
+ </reg32>
+ <reg32 offset="0x8100" name="GRAS_LRZ_CNTL" type="a6xx_gras_lrz_cntl" usage="rp_blit" variants="A7XX"/>
+ <reg32 offset="0x8212" name="GRAS_LRZ_CNTL" type="a6xx_gras_lrz_cntl" usage="rp_blit" variants="A8XX-"/>
+
+ <reg32 offset="0x8007" name="GRAS_LRZ_CB_CNTL" variants="A7XX" usage="rp_blit">
+ <doc>
+ The total size of the LRZ image array (not including
+ fast clear buffer), used as a stride for double
+ buffering used with concurrent binning.
+ </doc>
+ <bitfield name="DOUBLE_BUFFER_STRIDE" low="8" high="31" shr="8"/>
+ </reg32>
+ <reg32 offset="0x8101" name="GRAS_LRZ_CB_CNTL" usage="rp_blit" variants="A8XX-">
+ <bitfield name="DOUBLE_BUFFER_PITCH" low="8" high="31" shr="8"/>
+ </reg32>
<enum name="a6xx_fragcoord_sample_mode">
<value value="0" name="FRAGCOORD_CENTER"/>
@@ -1275,14 +1996,17 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8101" name="GRAS_LRZ_PS_INPUT_CNTL" type="a6xx_gras_lrz_ps_input_cntl" usage="rp_blit" variants="A6XX-A7XX"/>
+ <reg32 offset="0x8102" name="GRAS_LRZ_PS_INPUT_CNTL" type="a6xx_gras_lrz_ps_input_cntl" usage="rp_blit" variants="A8XX-"/>
<bitset name="a6xx_gras_lrz_mrt_buffer_info_0" inline="yes">
<bitfield name="COLOR_FORMAT" low="0" high="7" type="a6xx_format"/>
</bitset>
<reg32 offset="0x8102" name="GRAS_LRZ_MRT_BUFFER_INFO_0" type="a6xx_gras_lrz_mrt_buffer_info_0" usage="rp_blit" variants="A6XX-A7XX"/>
+ <reg32 offset="0x8103" name="GRAS_LRZ_MRT_BUFFER_INFO_0" type="a6xx_gras_lrz_mrt_buffer_info_0" usage="rp_blit" variants="A8XX-"/>
<reg64 offset="0x8103" name="GRAS_LRZ_BUFFER_BASE" align="256" type="waddress" usage="rp_blit" variants="A6XX-A7XX"/>
+ <reg64 offset="0x8104" name="GRAS_LRZ_BUFFER_BASE" align="256" type="waddress" usage="rp_blit" variants="A8XX-"/>
<bitset name="a6xx_gras_lrz_buffer_pitch" inline="yes">
<bitfield name="PITCH" low="0" high="7" shr="5" type="uint"/>
@@ -1290,6 +2014,9 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x8105" name="GRAS_LRZ_BUFFER_PITCH" type="a6xx_gras_lrz_buffer_pitch" usage="rp_blit" variants="A6XX-A7XX"/>
+ <reg32 offset="0x8108" name="GRAS_LRZ_BUFFER_PITCH" type="a6xx_gras_lrz_buffer_pitch" usage="rp_blit" variants="A8XX-"/>
+
+ <reg32 offset="0x810e" name="GRAS_LRZ_BUFFER_STRIDE" usage="rp_blit" low="0" high="16" shr="12" variants="A8XX-"/>
<!--
The LRZ "fast clear" buffer is initialized to zero's by blob, and
@@ -1346,22 +2073,32 @@ by a particular renderpass/blit.
<!-- 0x810c-0x810f invalid -->
+ <reg32 offset="0x8110" name="GRAS_LRZ_BUFFER_SLICE_PITCH" low="0" high="31" shr="8" type="uint" variants="A8XX-"/>
+
<reg32 offset="0x8110" name="GRAS_MODE_CNTL" low="0" high="1" variants="A6XX-A7XX" usage="cmd"/>
+ <reg32 offset="0x8213" name="GRAS_MODE_CNTL" low="0" high="1" variants="A8XX-" usage="cmd"/>
<!-- A bit tentative but it's a color and it is followed by LRZ_CLEAR -->
<reg32 offset="0x8111" name="GRAS_LRZ_DEPTH_CLEAR" type="float" variants="A7XX"/>
+ <reg32 offset="0x810d" name="GRAS_LRZ_DEPTH_CLEAR" type="float" variants="A8XX-"/>
- <bitset name="a6xx_gras_lrz_depth_buffer_info" inline="yes">
- <bitfield name="DEPTH_FORMAT" low="0" high="2" type="a6xx_depth_format"/>
- <bitfield name="UNK3" pos="3"/>
- </bitset>
-
- <reg32 offset="0x8113" name="GRAS_LRZ_DEPTH_BUFFER_INFO" type="a6xx_gras_lrz_depth_buffer_info" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8113" name="GRAS_LRZ_DEPTH_BUFFER_INFO" type="a6xx_depth_buffer_info" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x810f" name="GRAS_LRZ_DEPTH_BUFFER_INFO" type="a6xx_depth_buffer_info" variants="A8XX" usage="rp_blit"/>
<doc>LUT used to convert quality buffer values to HW shading rate values. An array of 4-bit values.</doc>
- <array offset="0x8120" name="GRAS_LRZ_QUALITY_LOOKUP_TABLE" variants="A7XX-" stride="1" length="2"/>
+ <array offset="0x8120" name="GRAS_LRZ_QUALITY_LOOKUP_TABLE" variants="A7XX" stride="1" length="2"/>
+ <array offset="0x8130" name="GRAS_LRZ_QUALITY_LOOKUP_TABLE" variants="A8XX-" stride="1" length="2"/>
- <!-- 0x8112-0x83ff invalid -->
+ <reg32 offset="0x810c" name="GRAS_LRZ_COLOR_COMP_MASK" variants="A8XX-">
+ <bitfield name="MRT0" low="0" high="3"/>
+ <bitfield name="MRT1" low="4" high="7"/>
+ <bitfield name="MRT2" low="8" high="11"/>
+ <bitfield name="MRT3" low="12" high="15"/>
+ <bitfield name="MRT4" low="16" high="19"/>
+ <bitfield name="MRT5" low="20" high="23"/>
+ <bitfield name="MRT6" low="24" high="27"/>
+ <bitfield name="MRT7" low="28" high="31"/>
+ </reg32>
<enum name="a6xx_rotation">
<value value="0x0" name="ROTATE_0"/>
@@ -1372,7 +2109,7 @@ by a particular renderpass/blit.
<value value="0x5" name="ROTATE_VFLIP"/>
</enum>
- <bitset name="a6xx_a2d_bit_cntl" inline="yes">
+ <bitset name="a6xx_a2d_blt_cntl" inline="yes">
<bitfield name="ROTATE" low="0" high="2" type="a6xx_rotation"/>
<bitfield name="OVERWRITEEN" pos="3" type="boolean"/>
<bitfield name="UNK4" low="4" high="6"/>
@@ -1391,7 +2128,7 @@ by a particular renderpass/blit.
<bitfield name="COPY" pos="30" type="boolean" variants="A7XX-"/>
</bitset>
- <reg32 offset="0x8400" name="GRAS_A2D_BLT_CNTL" type="a6xx_a2d_bit_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8400" name="GRAS_A2D_BLT_CNTL" type="a6xx_a2d_blt_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<!-- note: the low 8 bits for src coords are valid, probably fixed point
it would be a bit weird though, since we subtract 1 from BR coords
apparently signed, gallium driver uses negative coords and it works?
@@ -1408,16 +2145,35 @@ by a particular renderpass/blit.
<reg32 offset="0x840a" name="GRAS_A2D_SCISSOR_TL" type="a6xx_reg_xy" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x840b" name="GRAS_A2D_SCISSOR_BR" type="a6xx_reg_xy" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x8500" name="GRAS_A2D_BLT_CNTL" type="a6xx_a2d_blt_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8501" name="GRAS_A2D_SRC_XMIN" low="8" high="24" type="int" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8502" name="GRAS_A2D_SRC_XMAX" low="8" high="24" type="int" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8503" name="GRAS_A2D_SRC_YMIN" low="8" high="24" type="int" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8504" name="GRAS_A2D_SRC_YMAX" low="8" high="24" type="int" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8505" name="GRAS_A2D_DEST_TL" type="a6xx_reg_xy" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8506" name="GRAS_A2D_DEST_BR" type="a6xx_reg_xy" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8507" name="GRAS_A2D_SCISSOR_TL" type="a6xx_reg_xy" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x8508" name="GRAS_A2D_SCISSOR_BR" type="a6xx_reg_xy" variants="A8XX-" usage="rp_blit"/>
+
<!-- always 0x880 ? (and 0 in a640/a650 traces?) -->
- <reg32 offset="0x8600" name="GRAS_DBG_ECO_CNTL" usage="cmd">
+ <reg32 offset="0x8600" name="GRAS_DBG_ECO_CNTL" usage="init" variants="A6XX-A7XX">
<bitfield name="UNK7" pos="7" type="boolean"/>
<bitfield name="LRZCACHELOCKDIS" pos="11" type="boolean"/>
</reg32>
- <reg32 offset="0x8601" name="GRAS_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode"/>
- <reg32 offset="0x8602" name="GRAS_NC_MODE_CNTL" variants="A7XX-"/>
- <array offset="0x8610" name="GRAS_PERFCTR_TSE_SEL" stride="1" length="4"/>
- <array offset="0x8614" name="GRAS_PERFCTR_RAS_SEL" stride="1" length="4"/>
- <array offset="0x8618" name="GRAS_PERFCTR_LRZ_SEL" stride="1" length="4"/>
+ <reg32 offset="0x8600" name="GRAS_TSEFE_DBG_ECO_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x8702" name="GRAS_DBG_ECO_CNTL" variants="A8XX"/>
+ <reg32 offset="0x8601" name="GRAS_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode" variants="A6XX"/>
+ <reg32 offset="0x8602" name="GRAS_NC_MODE_CNTL" variants="A7XX"/>
+ <reg32 offset="0x8700" name="GRAS_NC_MODE_CNTL" variants="A8XX-"/>
+ <array offset="0x8610" name="GRAS_PERFCTR_TSE_SEL" stride="1" length="4" variants="A6XX-A7XX"/>
+ <array offset="0x8614" name="GRAS_PERFCTR_RAS_SEL" stride="1" length="4" variants="A6XX-A7XX"/>
+ <array offset="0x8618" name="GRAS_PERFCTR_LRZ_SEL" stride="1" length="4" variants="A6XX-A7XX"/>
+
+ <array offset="0x8610" name="GRAS_PERFCTR_TSEFE_SEL" stride="1" length="6" variants="A8XX-"/>
+ <array offset="0x8710" name="GRAS_PERFCTR_TSE_SEL" stride="1" length="6" variants="A8XX-"/>
+ <array offset="0x8720" name="GRAS_PERFCTR_RAS_SEL" stride="1" length="4" variants="A8XX-"/>
+ <array offset="0x8730" name="GRAS_PERFCTR_LRZ_SEL" stride="1" length="6" variants="A8XX-"/>
+
<!-- note 0x8620-0x87ff are not all invalid
(in particular, 0x8631/0x8632 have 0x3fff3fff mask and would be xy coords)
@@ -1425,6 +2181,7 @@ by a particular renderpass/blit.
<!-- same as GRAS_BIN_CONTROL, but without bit 27: -->
<reg32 offset="0x8800" name="RB_CNTL" variants="A6XX-A7XX" type="a6xx_bin_cntl" usage="rp_blit"/>
+ <reg32 offset="0x8800" name="RB_CNTL" variants="A8XX-" type="a8xx_bin_cntl" usage="rp_blit"/>
<reg32 offset="0x8801" name="RB_RENDER_CNTL" variants="A6XX" usage="rp_blit">
<bitfield name="CCUSINGLECACHELINESIZE" low="3" high="5"/>
@@ -1462,7 +2219,8 @@ by a particular renderpass/blit.
<reg32 offset="0x8804" name="RB_MSAA_SAMPLE_POS_CNTL" type="a6xx_msaa_sample_pos_cntl" usage="rp_blit"/>
<reg32 offset="0x8805" name="RB_PROGRAMMABLE_MSAA_POS_0" type="a6xx_programmable_msaa_pos" usage="rp_blit"/>
<reg32 offset="0x8806" name="RB_PROGRAMMABLE_MSAA_POS_1" type="a6xx_programmable_msaa_pos" usage="rp_blit"/>
- <!-- 0x8807-0x8808 invalid -->
+ <reg32 offset="0x8807" name="RB_PROGRAMMABLE_MSAA_POS_2" type="a6xx_programmable_msaa_pos" usage="rp_blit" variants="A8XX-"/>
+ <reg32 offset="0x8808" name="RB_PROGRAMMABLE_MSAA_POS_3" type="a6xx_programmable_msaa_pos" usage="rp_blit" variants="A8XX-"/>
<!--
note: maybe not actually called RB_RENDER_CONTROLn (since RB_RENDER_CNTL
name comes from kernel and is probably right)
@@ -1476,7 +2234,7 @@ by a particular renderpass/blit.
<bitfield name="IJ_LINEAR_CENTROID" pos="4" type="boolean"/>
<bitfield name="IJ_LINEAR_SAMPLE" pos="5" type="boolean"/>
<bitfield name="COORD_MASK" low="6" high="9" type="hex"/>
- <bitfield name="UNK10" pos="10" type="boolean"/>
+ <bitfield name="INTERP_EN" pos="10" type="boolean"/>
</reg32>
<reg32 offset="0x880a" name="RB_PS_INPUT_CNTL" usage="rp_blit">
<!-- enable bits for various FS sysvalue regs: -->
@@ -1534,8 +2292,32 @@ by a particular renderpass/blit.
<reg32 offset="0x8810" name="RB_PS_SAMPLEFREQ_CNTL" usage="rp_blit">
<bitfield name="PER_SAMP_MODE" pos="0" type="boolean"/>
</reg32>
- <reg32 offset="0x8811" name="RB_UNKNOWN_8811" low="4" high="6" usage="cmd"/>
- <reg32 offset="0x8812" name="RB_UNKNOWN_8812" variants="A7XX-" usage="rp_blit"/>
+ <reg32 offset="0x8811" name="RB_MODE_CNTL" low="4" high="6" usage="cmd"/>
+ <reg32 offset="0x8812" name="RB_BUFFER_CNTL" variants="A7XX-" usage="rp_blit">
+ <bitfield name="Z_SYSMEM" pos="0" type="boolean"/>
+ <bitfield name="S_SYSMEM" pos="1" type="boolean"/>
+ <bitfield name="RT0_SYSMEM" pos="2" type="boolean"/>
+ <bitfield name="RT1_SYSMEM" pos="3" type="boolean"/>
+ <bitfield name="RT2_SYSMEM" pos="4" type="boolean"/>
+ <bitfield name="RT3_SYSMEM" pos="5" type="boolean"/>
+ <bitfield name="RT4_SYSMEM" pos="6" type="boolean"/>
+ <bitfield name="RT5_SYSMEM" pos="7" type="boolean"/>
+ <bitfield name="RT6_SYSMEM" pos="8" type="boolean"/>
+ <bitfield name="RT7_SYSMEM" pos="9" type="boolean"/>
+ <bitfield name="Z_FULL_IN_GMEM" pos="10" type="boolean" variants="A8XX-"/>
+ <bitfield name="S_FULL_IN_GMEM" pos="11" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT0_FULL_IN_GMEM" pos="12" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT1_FULL_IN_GMEM" pos="13" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT2_FULL_IN_GMEM" pos="14" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT3_FULL_IN_GMEM" pos="15" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT4_FULL_IN_GMEM" pos="16" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT5_FULL_IN_GMEM" pos="17" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT6_FULL_IN_GMEM" pos="18" type="boolean" variants="A8XX-"/>
+ <bitfield name="RT7_FULL_IN_GMEM" pos="19" type="boolean" variants="A8XX-"/>
+ </reg32>
+
+ <reg32 offset="0x8816" name="RB_RESOLVE_CR_CNTL" variants="A8XX-" usage="rp_blit"/>
+
<!-- 0x8813-0x8817 invalid -->
<!-- always 0x0 ? -->
<reg32 offset="0x8818" name="RB_UNKNOWN_8818" low="0" high="6" usage="cmd"/>
@@ -1546,11 +2328,17 @@ by a particular renderpass/blit.
<reg32 offset="0x881c" name="RB_UNKNOWN_881C" usage="cmd"/>
<reg32 offset="0x881d" name="RB_UNKNOWN_881D" usage="cmd"/>
<reg32 offset="0x881e" name="RB_UNKNOWN_881E" usage="cmd"/>
- <!-- 0x881f invalid -->
+
+ <!-- Duplicates fields from SP_PS_CNTL_0 -->
+ <reg32 offset="0x881f" name="RB_PS_CNTL" variants="A8XX-" usage="rp_blit">
+ <bitfield name="PIXLODENABLE" pos="0" type="boolean"/>
+ <bitfield name="LODPIXMASK" pos="1" type="boolean"/>
+ </reg32>
+
<array offset="0x8820" name="RB_MRT" stride="8" length="8" usage="rp_blit">
<reg32 offset="0x0" name="CONTROL">
- <bitfield name="BLEND" pos="0" type="boolean"/>
- <bitfield name="BLEND2" pos="1" type="boolean"/>
+ <bitfield name="COLOR_BLEND_EN" pos="0" type="boolean"/>
+ <bitfield name="ALPHA_BLEND_EN" pos="1" type="boolean"/>
<bitfield name="ROP_ENABLE" pos="2" type="boolean"/>
<bitfield name="ROP_CODE" low="3" high="6" type="a3xx_rop_code"/>
<bitfield name="COMPONENT_ENABLE" low="7" high="10" type="hex"/>
@@ -1613,7 +2401,9 @@ by a particular renderpass/blit.
<bitfield name="ALPHA_TO_ONE" pos="11" type="boolean"/>
<bitfield name="SAMPLE_MASK" low="16" high="31"/>
</reg32>
- <!-- 0x8866-0x886f invalid -->
+ <reg32 offset="0x8866" name="RB_LB_PARAM_LIMIT" variants="A8XX-" usage="rp_blit">
+ <bitfield name="PRIMALLOCTHRESHOLD" low="0" high="2" type="uint"/>
+ </reg32>
<reg32 offset="0x8870" name="RB_DEPTH_PLANE_CNTL" type="a6xx_depth_plane_cntl" usage="rp_blit"/>
<reg32 offset="0x8871" name="RB_DEPTH_CNTL" usage="rp_blit">
@@ -1627,14 +2417,15 @@ by a particular renderpass/blit.
</doc>
<bitfield name="Z_READ_ENABLE" pos="6" type="boolean"/>
<bitfield name="Z_BOUNDS_ENABLE" pos="7" type="boolean"/>
+ <!-- clamp shader depth out to [0, 1] (instead of RB_VIEWPORT_ZCLAMP_MIN/MAX)-->
+ <bitfield name="O_DEPTH_01_CLAMP_EN" pos="8" type="boolean" variants="A8XX-"/>
</reg32>
<!-- duplicates GRAS_SU_DEPTH_BUFFER_INFO: -->
<reg32 offset="0x8872" name="RB_DEPTH_BUFFER_INFO" variants="A6XX" type="a6xx_depth_buffer_info" usage="rp_blit"/>
<!-- first 4 bits duplicates GRAS_SU_DEPTH_BUFFER_INFO -->
- <reg32 offset="0x8872" name="RB_DEPTH_BUFFER_INFO" variants="A7XX-" usage="rp_blit">
- <bitfield name="DEPTH_FORMAT" low="0" high="2" type="a6xx_depth_format"/>
- <bitfield name="UNK3" low="3" high="4"/>
+ <reg32 offset="0x8872" name="RB_DEPTH_BUFFER_INFO" type="a6xx_depth_buffer_info" variants="A7XX-" usage="rp_blit">
+ <bitfield name="PRT" low="3" high="4"/>
<bitfield name="TILEMODE" low="5" high="6" type="a6xx_tile_mode"/>
<bitfield name="LOSSLESSCOMPEN" pos="7" type="boolean"/>
</reg32>
@@ -1702,12 +2493,22 @@ by a particular renderpass/blit.
<reg32 offset="0x8898" name="RB_LRZ_CNTL" usage="rp_blit">
<bitfield name="ENABLE" pos="0" type="boolean"/>
</reg32>
- <reg32 offset="0x8899" name="RB_UNKNOWN_8899" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0x8899" name="RB_LRZ_CNTL2" variants="A7XX-" usage="cmd">
+ <bitfield name="ENABLE_BIDIRECTIONAL_LRZ" pos="0" type="boolean"/>
+ </reg32>
<!-- 0x8899-0x88bf invalid -->
<!-- clamps depth value for depth test/write -->
<reg32 offset="0x88c0" name="RB_VIEWPORT_ZCLAMP_MIN" type="float" usage="rp_blit" variants="A6XX-A7XX"/>
<reg32 offset="0x88c1" name="RB_VIEWPORT_ZCLAMP_MAX" type="float" usage="rp_blit" variants="A6XX-A7XX"/>
+<!-- todo allow type="float" on an <array/> -->
+ <array offset="0x88b0" name="RB_VIEWPORT_ZCLAMP_MIN" stride="1" length="16" usage="rp_blit" variants="A8XX-">
+ <reg32 offset="0" name="REG" type="float"/>
+ </array>
+ <array offset="0x88c0" name="RB_VIEWPORT_ZCLAMP_MAX" stride="1" length="16" usage="rp_blit" variants="A8XX-">
+ <reg32 offset="0" name="REG" type="float"/>
+ </array>
+
<!-- 0x88c2-0x88cf invalid-->
<reg32 offset="0x88d0" name="RB_RESOLVE_CNTL_0" usage="rp_blit">
<bitfield name="UNK0" low="0" high="12"/>
@@ -1720,6 +2521,11 @@ by a particular renderpass/blit.
<bitfield name="BINW" low="0" high="5" shr="5" type="uint"/>
<bitfield name="BINH" low="8" high="14" shr="4" type="uint"/>
</reg32>
+
+ <reg32 offset="0x88d3" name="RB_RESOLVE_CNTL_3" type="a8xx_bin_size" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x88f0" name="RB_RESOLVE_CNTL_4" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x88f1" name="RB_RESOLVE_CNTL_5" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0x88d4" name="RB_RESOLVE_WINDOW_OFFSET" type="a6xx_reg_xy" usage="rp_blit"/>
<reg32 offset="0x88d5" name="RB_RESOLVE_GMEM_BUFFER_INFO" usage="rp_blit">
<bitfield name="SAMPLES" low="3" high="4" type="a3xx_msaa_samples"/>
@@ -1798,8 +2604,10 @@ by a particular renderpass/blit.
<value value="0x1" name="CCU_CACHE_SIZE_HALF"/>
<value value="0x2" name="CCU_CACHE_SIZE_QUARTER"/>
<value value="0x3" name="CCU_CACHE_SIZE_EIGHTH"/>
+ <!-- for DEPTH_CACHE_SIZE 3 == THREE_QUARTER from KNP -->
+ <value value="0x3" name="CCU_CACHE_SIZE_THREE_QUARTER"/>
</enum>
- <reg32 offset="0x88e5" name="RB_CCU_CACHE_CNTL" variants="A7XX-" usage="cmd">
+ <reg32 offset="0x88e5" name="RB_CCU_CACHE_CNTL" variants="A7XX" usage="cmd">
<bitfield name="DEPTH_OFFSET_HI" pos="0" type="hex"/>
<bitfield name="COLOR_OFFSET_HI" pos="2" type="hex"/>
<bitfield name="DEPTH_CACHE_SIZE" low="10" high="11" type="a6xx_ccu_cache_size"/>
@@ -1814,12 +2622,30 @@ by a particular renderpass/blit.
-->
<bitfield name="COLOR_OFFSET" low="23" high="31" shr="12" type="hex"/>
</reg32>
- <!-- 0x88e6-0x88ef invalid -->
+
+ <reg32 offset="0x88e5" name="RB_CCU_CACHE_CNTL" variants="A8XX-" usage="cmd">
+ <!--
+ For color cache, full is 128KB per CCU. For depth cache,
+ full is 256KB per CCU.
+
+ For attr/pos caches (see VPC_{ATTR,POS,BV_POS}_BUF_GMEM_SIZE),
+ the sizes are per CCU
+ -->
+ <bitfield name="COLOR_OFFSET" low="0" high="13" shr="12" type="hex"/>
+ <bitfield name="COLOR_CACHE_SIZE" low="14" high="15" type="a6xx_ccu_cache_size"/>
+ <bitfield name="DEPTH_OFFSET" low="16" high="29" shr="12" type="hex"/>
+ <bitfield name="DEPTH_CACHE_SIZE" low="30" high="31" type="a6xx_ccu_cache_size"/>
+ </reg32>
+
+ <reg32 offset="0x88e6" name="RB_RESOLVE_GMEM_BUFFER_CNTL" variants="A8XX-">
+ <bitfield name="FULL_IN_GMEM" pos="0" type="boolean"/>
+ </reg32>
+
<!-- always 0x0 ? -->
- <reg32 offset="0x88f0" name="RB_UNKNOWN_88F0" low="0" high="11" usage="cmd"/>
+ <reg32 offset="0x88f0" name="RB_UNKNOWN_88F0" low="0" high="11" variants="A6XX" usage="cmd"/>
<!-- could be for separate stencil? (or may not be a flag buffer at all) -->
- <reg64 offset="0x88f1" name="RB_UNK_FLAG_BUFFER_BASE" type="waddress" align="64"/>
- <reg32 offset="0x88f3" name="RB_UNK_FLAG_BUFFER_PITCH" type="a6xx_flag_buffer_pitch"/>
+ <reg64 offset="0x88f1" name="RB_UNK_FLAG_BUFFER_BASE" type="waddress" align="64" variants="A6XX"/>
+ <reg32 offset="0x88f3" name="RB_UNK_FLAG_BUFFER_PITCH" type="a6xx_flag_buffer_pitch" variants="A6XX"/>
<reg32 offset="0x88f4" name="RB_VRS_CONFIG" usage="rp_blit">
<bitfield name="UNK2" pos="2" type="boolean"/>
@@ -1849,9 +2675,19 @@ by a particular renderpass/blit.
the address is specified through CP_EVENT_WRITE7::WRITE_SAMPLE_COUNT.
</doc>
<reg64 offset="0x8927" name="RB_SAMPLE_COUNTER_BASE" type="waddress" align="16" usage="cmd"/>
- <!-- 0x8929-0x89ff invalid -->
- <!-- TODO: there are some registers in the 0x8a00-0x8bff range -->
+ <bitset name="a8xx_gmem_dimension" inline="yes">
+ <bitfield name="WIDTH" low="0" high="14" type="uint"/>
+ <bitfield name="HEIGHT" low="16" high="30" type="uint"/>
+ </bitset>
+
+ <reg32 offset="0x8813" name="RB_DEPTH_GMEM_DIMENSION" type="a8xx_gmem_dimension" variants="A8XX-"/>
+ <reg32 offset="0x8814" name="RB_STENCIL_GMEM_DIMENSION" type="a8xx_gmem_dimension" variants="A8XX-"/>
+ <reg32 offset="0x8815" name="RB_RESOLVE_GMEM_DIMENSION" type="a8xx_gmem_dimension" variants="A8XX-"/>
+
+ <array offset="0x8930" name="RB_MRT_GMEM_DIMENSION" variants="A8XX-" stride="1" length="8">
+ <reg32 offset="0" name="REG" type="a8xx_gmem_dimension"/>
+ </array>
<!--
These show up in a6xx gen3+ but so far haven't found an example of
@@ -1862,7 +2698,7 @@ by a particular renderpass/blit.
<reg32 offset="0x8a20" name="RB_UNKNOWN_8A20" variants="A6XX" usage="rp_blit"/>
<reg32 offset="0x8a30" name="RB_UNKNOWN_8A30" variants="A6XX" usage="rp_blit"/>
- <reg32 offset="0x8c00" name="RB_A2D_BLT_CNTL" type="a6xx_a2d_bit_cntl" usage="rp_blit"/>
+ <reg32 offset="0x8c00" name="RB_A2D_BLT_CNTL" type="a6xx_a2d_blt_cntl" usage="rp_blit"/>
<reg32 offset="0x8c01" name="RB_A2D_PIXEL_CNTL" low="0" high="31" usage="rp_blit"/>
<bitset name="a6xx_a2d_src_texture_info" inline="yes">
@@ -1921,10 +2757,10 @@ by a particular renderpass/blit.
<!-- 0x8c35-0x8dff invalid -->
<!-- always 0x1 ? either doesn't exist for a650 or write-only: -->
- <reg32 offset="0x8e01" name="RB_UNKNOWN_8E01" usage="cmd"/>
+ <reg32 offset="0x8e01" name="RB_RBP_CNTL" usage="cmd"/>
<!-- 0x8e00-0x8e03 invalid -->
<reg32 offset="0x8e04" name="RB_DBG_ECO_CNTL" usage="cmd"/> <!-- TODO: valid mask 0xfffffeff -->
- <reg32 offset="0x8e05" name="RB_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode"/>
+ <reg32 offset="0x8e05" name="RB_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode" variants="A6XX"/>
<!-- 0x02080000 in GMEM, zero otherwise? -->
<reg32 offset="0x8e06" name="RB_CCU_DBG_ECO_CNTL" variants="A7XX-" usage="cmd"/>
@@ -1963,7 +2799,7 @@ by a particular renderpass/blit.
<bitfield name="CONCURRENT_UNRESOLVE_MODE" low="5" high="6" type="a7xx_concurrent_unresolve_mode"/>
<!-- rest of the bits were moved to RB_CCU_CACHE_CNTL -->
</reg32>
- <reg32 offset="0x8e08" name="RB_NC_MODE_CNTL">
+ <reg32 offset="0x8e08" name="RB_NC_MODE_CNTL" variants="A6XX-A7XX">
<bitfield name="MODE" pos="0" type="boolean"/>
<bitfield name="LOWER_BIT" low="1" high="2" type="uint"/>
<bitfield name="MIN_ACCESS_LENGTH" pos="3" type="boolean"/> <!-- true=64b false=32b -->
@@ -1972,26 +2808,40 @@ by a particular renderpass/blit.
<bitfield name="RGB565_PREDICATOR" pos="11" type="boolean"/>
<bitfield name="UNK12" low="12" high="13"/>
</reg32>
- <reg32 offset="0x8e09" name="RB_UNKNOWN_8E09" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0x8e08" name="RB_CCU_NC_MODE_CNTL" variants="A8XX-"/>
+
+ <reg32 offset="0x8e09" name="RB_UNKNOWN_8E09" variants="A7XX" usage="cmd"/>
+ <reg32 offset="0x8e09" name="RB_GC_GMEM_PROTECT" variants="A8XX-"/>
+ <reg32 offset="0x8e0a" name="RB_LPAC_GMEM_PROTECT" variants="A8XX-"/>
<!-- 0x8e09-0x8e0f invalid -->
<array offset="0x8e10" name="RB_PERFCTR_RB_SEL" stride="1" length="8"/>
<array offset="0x8e18" name="RB_PERFCTR_CCU_SEL" stride="1" length="5"/>
<!-- 0x8e1d-0x8e1f invalid -->
<!-- 0x8e20-0x8e25 more perfcntr sel? -->
<!-- 0x8e26-0x8e27 invalid -->
- <reg32 offset="0x8e28" name="RB_CMP_DBG_ECO_CNTL"/>
+
+ <reg32 offset="0x8f00" name="RB_CMP_NC_MODE_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x8f01" name="RB_RESOLVE_PREFETCH_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x8f02" name="RB_CMP_DBG_ECO_CNTL" variants="A8XX-"/>
+
+ <reg32 offset="0x8f03" name="RB_UNSLICE_STATUS" variants="A8XX-"/>
+ <reg32 offset="0x8e28" name="RB_CMP_DBG_ECO_CNTL" variants="A6XX-A7XX"/>
<!-- 0x8e29-0x8e2b invalid -->
- <array offset="0x8e2c" name="RB_PERFCTR_CMP_SEL" stride="1" length="4"/>
- <array offset="0x8e30" name="RB_PERFCTR_UFC_SEL" stride="1" length="6" variants="A7XX-"/>
- <reg32 offset="0x8e3b" name="RB_RB_SUB_BLOCK_SEL_CNTL_HOST"/>
- <reg32 offset="0x8e3d" name="RB_RB_SUB_BLOCK_SEL_CNTL_CD"/>
+ <array offset="0x8e2c" name="RB_PERFCTR_CMP_SEL" stride="1" length="4" variants="A6XX-A7XX"/>
+ <array offset="0x8e30" name="RB_PERFCTR_UFC_SEL" stride="1" length="6" variants="A7XX"/>
+ <array offset="0x8f04" name="RB_PERFCTR_CMP_SEL" stride="1" length="4" variants="A8XX-"/>
+ <array offset="0x8f10" name="RB_PERFCTR_UFC_SEL" stride="1" length="6" variants="A8XX-"/>
+ <reg32 offset="0x8e3b" name="RB_SUB_BLOCK_SEL_CNTL_HOST"/>
+ <reg32 offset="0x8e3d" name="RB_SUB_BLOCK_SEL_CNTL_CD"/>
+ <reg32 offset="0x8f29" name="RB_UFC_DBG_CNTL" variants="A8XX-"/>
<!-- 0x8e3e-0x8e4f invalid -->
<!-- GMEM save/restore for preemption: -->
<reg32 offset="0x8e50" name="RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ENABLE" pos="0" type="boolean"/>
<!-- address for GMEM save/restore? -->
<reg32 offset="0x8e51" name="RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE_ADDR" type="waddress" align="1"/>
- <!-- 0x8e53-0x8e7f invalid -->
- <reg32 offset="0x8e79" name="RB_UNKNOWN_8E79" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0x8e77" name="RB_SLICE_UFC_PREFETCH_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x8e78" name="RB_SLICE_UFC_DBG_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x8e79" name="RB_UNKNOWN_8E79" variants="A7XX" usage="init"/>
<!-- 0x8e80-0x8e83 are valid -->
<!-- 0x8e84-0x90ff invalid -->
@@ -2014,6 +2864,10 @@ by a particular renderpass/blit.
<reg32 offset="0x9102" name="VPC_GS_CLIP_CULL_CNTL" type="a6xx_vpc_xs_clip_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x9103" name="VPC_DS_CLIP_CULL_CNTL" type="a6xx_vpc_xs_clip_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9307" name="VPC_VS_CLIP_CULL_CNTL" type="a6xx_vpc_xs_clip_cntl" variants="A8XX" usage="rp_blit"/>
+ <reg32 offset="0x9308" name="VPC_GS_CLIP_CULL_CNTL" type="a6xx_vpc_xs_clip_cntl" variants="A8XX" usage="rp_blit"/>
+ <reg32 offset="0x9309" name="VPC_DS_CLIP_CULL_CNTL" type="a6xx_vpc_xs_clip_cntl" variants="A8XX" usage="rp_blit"/>
+
<reg32 offset="0x9311" name="VPC_VS_CLIP_CULL_CNTL_V2" type="a6xx_vpc_xs_clip_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x9312" name="VPC_GS_CLIP_CULL_CNTL_V2" type="a6xx_vpc_xs_clip_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x9313" name="VPC_DS_CLIP_CULL_CNTL_V2" type="a6xx_vpc_xs_clip_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
@@ -2028,6 +2882,9 @@ by a particular renderpass/blit.
<reg32 offset="0x9105" name="VPC_GS_SIV_CNTL" type="a6xx_vpc_xs_siv_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x9106" name="VPC_DS_SIV_CNTL" type="a6xx_vpc_xs_siv_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x930a" name="VPC_VS_SIV_CNTL" type="a6xx_vpc_xs_siv_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x930b" name="VPC_GS_SIV_CNTL" type="a6xx_vpc_xs_siv_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x930c" name="VPC_DS_SIV_CNTL" type="a6xx_vpc_xs_siv_cntl" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x9314" name="VPC_VS_SIV_CNTL_V2" type="a6xx_vpc_xs_siv_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x9315" name="VPC_GS_SIV_CNTL_V2" type="a6xx_vpc_xs_siv_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
@@ -2042,6 +2899,7 @@ by a particular renderpass/blit.
<reg32 offset="0x9980" name="VPC_RAST_STREAM_CNTL" type="a6xx_vpc_rast_stream_cntl" variants="A6XX" usage="rp_blit"/>
<reg32 offset="0x9107" name="VPC_RAST_STREAM_CNTL" type="a6xx_vpc_rast_stream_cntl" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x930d" name="VPC_RAST_STREAM_CNTL" type="a6xx_vpc_rast_stream_cntl" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x9317" name="VPC_RAST_STREAM_CNTL_V2" type="a6xx_vpc_rast_stream_cntl" variants="A7XX" usage="rp_blit"/>
<reg32 offset="0x9107" name="VPC_UNKNOWN_9107" variants="A6XX" usage="rp_blit">
@@ -2051,6 +2909,7 @@ by a particular renderpass/blit.
</reg32>
<reg32 offset="0x9108" name="VPC_RAST_CNTL" type="a6xx_rast_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x930e" name="VPC_RAST_CNTL" type="a6xx_rast_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_pc_cntl" inline="yes">
<bitfield name="PRIMITIVE_RESTART" pos="0" type="boolean"/>
<bitfield name="PROVOKING_VTX_LAST" pos="1" type="boolean"/>
@@ -2091,9 +2950,13 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9109" name="VPC_PC_CNTL" type="a6xx_pc_cntl" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x930f" name="VPC_PC_CNTL" type="a6xx_pc_cntl" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x910a" name="VPC_GS_PARAM_0" type="a6xx_gs_param_0" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x90c0" name="VPC_GS_PARAM_0" type="a6xx_gs_param_0" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x910b" name="VPC_STEREO_RENDERING_VIEWMASK" type="hex" low="0" high="15" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x90c1" name="VPC_STEREO_RENDERING_VIEWMASK" type="hex" low="0" high="15" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x910c" name="VPC_STEREO_RENDERING_CNTL" type="a6xx_stereo_rendering_cntl" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x931a" name="VPC_STEREO_RENDERING_CNTL" type="a6xx_stereo_rendering_cntl" variants="A8XX-" usage="rp_blit"/>
<enum name="a6xx_varying_interp_mode">
<value value="0" name="INTERP_SMOOTH"/>
@@ -2119,6 +2982,15 @@ by a particular renderpass/blit.
<reg32 offset="0x0" name="MODE"/>
</array>
+ <array offset="0x9240" name="VPC_VARYING_INTERP_MODE" stride="1" length="8" variants="A8XX-" usage="rp_blit">
+ <doc>Packed array of a6xx_varying_interp_mode</doc>
+ <reg32 offset="0x0" name="MODE"/>
+ </array>
+ <array offset="0x9248" name="VPC_VARYING_REPLACE_MODE" stride="1" length="8" variants="A8XX-" usage="rp_blit">
+ <doc>Packed array of a6xx_varying_ps_repl_mode</doc>
+ <reg32 offset="0x0" name="MODE"/>
+ </array>
+
<!-- always 0x0 -->
<reg32 offset="0x9210" name="VPC_UNKNOWN_9210" low="0" high="31" variants="A6XX" usage="cmd"/>
<reg32 offset="0x9211" name="VPC_UNKNOWN_9211" low="0" high="31" variants="A6XX" usage="cmd"/>
@@ -2128,6 +3000,11 @@ by a particular renderpass/blit.
<reg32 offset="0" name="DISABLE"/>
</array>
+ <array offset="0x9252" name="VPC_VARYING_LM_TRANSFER_CNTL" stride="1" length="4" variants="A8XX-" usage="rp_blit">
+ <!-- one bit per varying component: -->
+ <reg32 offset="0" name="DISABLE"/>
+ </array>
+
<bitset name="a6xx_vpc_so_mapping_wptr" inline="yes">
<!--
Choose which DWORD to write to. There is an array of
@@ -2158,6 +3035,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9216" name="VPC_SO_MAPPING_WPTR" type="a6xx_vpc_so_mapping_wptr" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9180" name="VPC_SO_MAPPING_WPTR" type="a6xx_vpc_so_mapping_wptr" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_vpc_so_mapping_port" inline="yes">
<bitfield name="A_BUF" low="0" high="1" type="uint"/>
@@ -2170,8 +3048,10 @@ by a particular renderpass/blit.
<!-- special register, write multiple times to load SO program (not readable) -->
<reg32 offset="0x9217" name="VPC_SO_MAPPING_PORT" type="a6xx_vpc_so_mapping_port" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9181" name="VPC_SO_MAPPING_PORT" type="a6xx_vpc_so_mapping_port" variants="A8XX-" usage="rp_blit"/>
<reg64 offset="0x9218" name="VPC_SO_QUERY_BASE" type="waddress" align="32" variants="A6XX-A7XX" usage="cmd"/>
+ <reg64 offset="0x9182" name="VPC_SO_QUERY_BASE" type="waddress" align="32" variants="A8XX-" usage="cmd"/>
<array offset="0x921a" name="VPC_SO" stride="7" length="4" variants="A6XX-A7XX" usage="cmd">
<reg64 offset="0" name="BUFFER_BASE" type="waddress" align="32"/>
@@ -2181,13 +3061,23 @@ by a particular renderpass/blit.
<reg64 offset="5" name="FLUSH_BASE" type="waddress" align="32"/>
</array>
+ <array offset="0x9184" name="VPC_SO" stride="7" length="4" variants="A8XX-" usage="cmd">
+ <reg64 offset="0" name="BUFFER_BASE" type="waddress" align="32"/>
+ <reg32 offset="2" name="BUFFER_SIZE" low="2" high="31" shr="2"/>
+ <reg32 offset="3" name="BUFFER_STRIDE" low="0" high="9" shr="2"/>
+ <reg32 offset="4" name="BUFFER_OFFSET" low="2" high="31" shr="2"/>
+ <reg64 offset="5" name="FLUSH_BASE" type="waddress" align="32"/>
+ </array>
+
<bitset name="a6xx_vpc_replace_mode_cntl" inline="yes">
<bitfield name="INVERT" pos="0" type="boolean"/>
</bitset>
<reg32 offset="0x9236" name="VPC_REPLACE_MODE_CNTL" type="a6xx_vpc_replace_mode_cntl" variants="A6XX-A7XX" usage="cmd"/>
+ <reg32 offset="0x9310" name="VPC_REPLACE_MODE_CNTL" type="a6xx_vpc_replace_mode_cntl" variants="A8XX-" usage="cmd"/>
<reg32 offset="0x9300" name="VPC_ROTATION_CNTL" low="0" high="2" variants="A6XX-A7XX" usage="cmd"/>
+ <reg32 offset="0x9312" name="VPC_ROTATION_CNTL" low="0" high="2" variants="A8XX-" usage="cmd"/>
<bitset name="a6xx_vpc_xs_cntl" inline="yes">
<doc>
@@ -2211,6 +3101,10 @@ by a particular renderpass/blit.
<reg32 offset="0x9302" name="VPC_GS_CNTL" type="a6xx_vpc_xs_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x9303" name="VPC_DS_CNTL" type="a6xx_vpc_xs_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9300" name="VPC_VS_CNTL" type="a6xx_vpc_xs_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x9301" name="VPC_GS_CNTL" type="a6xx_vpc_xs_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x9302" name="VPC_DS_CNTL" type="a6xx_vpc_xs_cntl" variants="A8XX-" usage="rp_blit"/>
+
<bitset name="a6xx_vpc_ps_cntl" inline="yes">
<bitfield name="NUMNONPOSVAR" low="0" high="7" type="uint"/>
<!-- for fixed-function (i.e. no GS) gl_PrimitiveID in FS -->
@@ -2231,6 +3125,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9304" name="VPC_PS_CNTL" type="a6xx_vpc_ps_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9303" name="VPC_PS_CNTL" type="a6xx_vpc_ps_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_vpc_so_cntl" inline="yes">
<!--
@@ -2244,40 +3139,73 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9305" name="VPC_SO_CNTL" type="a6xx_vpc_so_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9304" name="VPC_SO_CNTL" type="a6xx_vpc_so_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_so_override" inline="yes">
<bitfield name="DISABLE" pos="0" type="boolean"/>
</bitset>
<reg32 offset="0x9306" name="VPC_SO_OVERRIDE" type="a6xx_so_override" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9305" name="VPC_SO_OVERRIDE" type="a6xx_so_override" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x9807" name="PC_DGEN_SO_OVERRIDE" type="a6xx_so_override" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b0a" name="PC_DGEN_SO_OVERRIDE" type="a6xx_so_override" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x9307" name="VPC_PS_RAST_CNTL" type="a6xx_rast_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9306" name="VPC_PS_RAST_CNTL" type="a6xx_rast_cntl" variants="A8XX-" usage="rp_blit"/>
- <reg32 offset="0x9308" name="VPC_ATTR_BUF_GMEM_SIZE" variants="A7XX" type="uint" usage="rp_blit"/>
- <reg32 offset="0x9309" name="VPC_ATTR_BUF_GMEM_BASE" variants="A7XX" type="uint" usage="rp_blit"/>
+ <reg32 offset="0x9308" name="VPC_ATTR_BUF_GMEM_SIZE" variants="A7XX" type="uint" usage="cmd"/>
+ <reg32 offset="0x9309" name="VPC_ATTR_BUF_GMEM_BASE" variants="A7XX" type="hex" usage="cmd"/>
- <reg32 offset="0x9b09" name="PC_ATTR_BUF_GMEM_SIZE" variants="A7XX" type="uint" usage="rp_blit"/>
+ <reg32 offset="0x9314" name="VPC_ATTR_BUF_GMEM_SIZE" variants="A8XX-" type="uint" usage="cmd"/>
+ <reg32 offset="0x9315" name="VPC_ATTR_BUF_GMEM_BASE" variants="A8XX-" type="hex" usage="cmd"/>
+
+ <reg32 offset="0x9316" name="VPC_POS_BUF_GMEM_SIZE" variants="A8XX-" type="uint" usage="cmd"/>
+ <reg32 offset="0x9317" name="VPC_POS_BUF_GMEM_BASE" variants="A8XX-" type="hex" usage="cmd"/>
+
+ <reg32 offset="0x9318" name="VPC_BV_POS_BUF_GMEM_SIZE" variants="A8XX-" type="uint" usage="cmd"/>
+ <reg32 offset="0x9319" name="VPC_BV_POS_BUF_GMEM_BASE" variants="A8XX-" type="hex" usage="cmd"/>
+
+ <reg32 offset="0x9b09" name="PC_ATTR_BUF_GMEM_SIZE" variants="A7XX" type="uint" usage="cmd"/>
+ <reg32 offset="0x9b16" name="PC_ATTR_BUF_GMEM_SIZE" variants="A8XX-" type="uint" usage="cmd"/>
+
+ <reg32 offset="0x9b17" name="PC_POS_BUF_GMEM_SIZE" variants="A8XX-" type="uint" usage="cmd"/>
+ <reg32 offset="0x9b18" name="PC_BV_POS_BUF_GMEM_SIZE" variants="A8XX-" type="uint" usage="cmd"/>
<reg32 offset="0x930a" name="VPC_UNKNOWN_930A" variants="A7XX"/>
+ <reg32 offset="0x9313" name="VPC_UNKNOWN_9313" variants="A8XX-"/>
+ <reg32 offset="0x9e17" name="PC_UNKNOWN_9E17" variants="A8XX-"/>
+
<reg32 offset="0x960a" name="VPC_FLATSHADE_MODE_CNTL" variants="A7XX"/>
+ <reg32 offset="0x9741" name="VPC_FLATSHADE_MODE_CNTL" variants="A8XX-"/>
<!-- 0x9307-0x95ff invalid -->
<!-- TODO: 0x9600-0x97ff range -->
- <reg32 offset="0x9600" name="VPC_DBG_ECO_CNTL" usage="cmd"/> <!-- always 0x0 ? TODO: 0x1fbf37ff valid mask -->
- <reg32 offset="0x9601" name="VPC_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode" usage="cmd"/>
- <reg32 offset="0x9602" name="VPC_UNKNOWN_9602" pos="0" usage="cmd"/> <!-- always 0x0 ? -->
- <reg32 offset="0x9603" name="VPC_UNKNOWN_9603" low="0" high="26"/>
+ <reg32 offset="0x9600" name="VPC_DBG_ECO_CNTL" variants="A6XX-A7XX" usage="cmd"/> <!-- always 0x0 ? TODO: 0x1fbf37ff valid mask -->
+ <reg32 offset="0x9601" name="VPC_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode" usage="cmd" variants="A6XX"/>
+ <reg32 offset="0x9680" name="VPC_DBG_ECO_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x9604" name="VPC_DBG_ECO_CNTL_2" variants="A8XX-"/>
+ <reg32 offset="0x9742" name="VPC_DBG_ECO_CNTL_1" variants="A8XX-"/>
+ <reg32 offset="0x9745" name="VPC_DBG_ECO_CNTL_3" variants="A8XX-"/>
+ <reg32 offset="0x9602" name="VPC_LB_MODE_CNTL" pos="0" variants="A6XX-A7XX" usage="init"/> <!-- always 0x0 ? -->
+ <reg32 offset="0x9740" name="VPC_LB_MODE_CNTL" pos="0" variants="A8XX-"/>
+ <reg32 offset="0x9603" name="VPC_STATUS" low="0" high="26" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9600" name="VPC_STATUS" low="0" high="26" variants="A8XX-"/>
<array offset="0x9604" name="VPC_PERFCTR_VPC_SEL" stride="1" length="6" variants="A6XX"/>
- <array offset="0x960b" name="VPC_PERFCTR_VPC_SEL" stride="1" length="12" variants="A7XX-"/>
- <!-- 0x960a-0x9623 invalid -->
- <!-- TODO: regs from 0x9624-0x963a -->
- <!-- 0x963b-0x97ff invalid -->
+ <array offset="0x960b" name="VPC_PERFCTR_VPC_SEL" stride="1" length="12" variants="A7XX"/>
+ <array offset="0x9670" name="VPC_PERFCTR_VPC_SEL_2" stride="1" length="12" variants="A8XX-"/>
+ <array offset="0x9690" name="VPC_PERFCTR_VPC_SEL" stride="1" length="12" variants="A8XX-"/>
+ <array offset="0x9750" name="VPC_PERFCTR_VPC_SEL_1" stride="1" length="12" variants="A8XX-"/>
+
+ <reg64 offset="0x9634" name="VPC_CONTEXT_SWITCH_SO_SAVE_ADDR" type="waddress" variants="A6XX-A7XX"/>
+ <reg64 offset="0x9602" name="VPC_CONTEXT_SWITCH_SO_SAVE_ADDR" type="waddress" variants="A8XX-"/>
+
+ <reg32 offset="0x980b" name="PC_UNKNOWN_980B" variants="A8XX-"/>
<reg32 offset="0x9800" name="PC_HS_PARAM_0" low="0" high="5" type="uint" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b10" name="PC_HS_PARAM_0" low="0" high="5" type="uint" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_pc_hs_param_1" inline="yes">
<bitfield name="SIZE" low="0" high="10" type="uint"/>
@@ -2285,6 +3213,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9801" name="PC_HS_PARAM_1" type="a6xx_pc_hs_param_1" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b11" name="PC_HS_PARAM_1" type="a6xx_pc_hs_param_1" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_pc_ds_param" inline="yes">
<bitfield name="SPACING" low="0" high="1" type="a6xx_tess_spacing"/>
@@ -2292,10 +3221,13 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9802" name="PC_DS_PARAM" type="a6xx_pc_ds_param" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b12" name="PC_DS_PARAM" type="a6xx_pc_ds_param" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x9803" name="PC_RESTART_INDEX" low="0" high="31" type="uint" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b15" name="PC_RESTART_INDEX" low="0" high="31" type="uint" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x9804" name="PC_MODE_CNTL" low="0" high="7" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b00" name="PC_MODE_CNTL" low="0" high="14" variants="A8XX" usage="rp_blit"/>
<reg32 offset="0x9805" name="PC_POWER_CNTL" low="0" high="2" usage="rp_blit"/>
@@ -2304,6 +3236,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9806" name="PC_PS_CNTL" type="a6xx_pc_ps_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b06" name="PC_PS_CNTL" type="a6xx_pc_ps_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_pc_dgen_so_cntl" inline="yes">
<bitfield name="STREAM_ENABLE" low="15" high="18" type="hex"/>
@@ -2311,12 +3244,19 @@ by a particular renderpass/blit.
<!-- New in a6xx gen3+ -->
<reg32 offset="0x9808" name="PC_DGEN_SO_CNTL" type="a6xx_pc_dgen_so_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b0b" name="PC_DGEN_SO_CNTL" type="a6xx_pc_dgen_so_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_pc_dgen_su_conservative_ras_cntl" inline="yes">
<bitfield name="CONSERVATIVERASEN" pos="0" type="boolean"/>
</bitset>
<reg32 offset="0x980a" name="PC_DGEN_SU_CONSERVATIVE_RAS_CNTL" type="a6xx_pc_dgen_su_conservative_ras_cntl" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9b08" name="PC_DGEN_SU_CONSERVATIVE_RAS_CNTL" type="a6xx_pc_dgen_su_conservative_ras_cntl" variants="A8XX-"/>
+
+ <reg32 offset="0x9b0c" name="PC_VS_INPUT_CNTL" variants="A8XX-" usage="rp_blit">
+ <bitfield name="INSTR_CNT" low="0" high="5" type="uint"/>
+ <bitfield name="SIDEBAND_CNT" low="6" high="8" type="uint"/>
+ </reg32>
<!-- 0x9840 - 0x9842 are not readable -->
<bitset name="a6xx_draw_initiator" inline="yes">
@@ -2326,6 +3266,9 @@ by a particular renderpass/blit.
<reg32 offset="0x9840" name="PC_DRAW_INITIATOR" type="a6xx_draw_initiator" variants="A6XX-A7XX"/>
<reg32 offset="0x9841" name="PC_KERNEL_INITIATOR" type="a6xx_draw_initiator" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9800" name="PC_DRAW_INITIATOR" type="a6xx_draw_initiator" variants="A8XX-"/>
+ <reg32 offset="0x9801" name="PC_KERNEL_INITIATOR" type="a6xx_draw_initiator" variants="A8XX-"/>
+
<bitset name="a6xx_event_initiator" inline="yes">
<!-- I think only the low bit is actually used? -->
<bitfield name="STATE_ID" low="16" high="23"/>
@@ -2333,6 +3276,7 @@ by a particular renderpass/blit.
</bitset>
<reg32 offset="0x9842" name="PC_EVENT_INITIATOR" type="a6xx_event_initiator" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9802" name="PC_EVENT_INITIATOR" type="a6xx_event_initiator" variants="A8XX-"/>
<!--
0x9880 written in a lot of places by SQE, same value gets written
@@ -2345,19 +3289,23 @@ by a particular renderpass/blit.
<reg32 offset="0x9981" name="PC_DGEN_RAST_CNTL" type="a6xx_rast_cntl" variants="A6XX" usage="rp_blit"/>
<reg32 offset="0x9809" name="PC_DGEN_RAST_CNTL" type="a6xx_rast_cntl" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9812" name="PC_DGEN_RAST_CNTL" type="a6xx_rast_cntl" variants="A8XX" usage="rp_blit"/>
<!-- Both are a750+.
Probably needed to correctly overlap execution of several draws.
-->
<reg32 offset="0x9885" name="PC_HS_BUFFER_SIZE" variants="A7XX" usage="cmd"/>
+ <reg32 offset="0x9814" name="PC_HS_BUFFER_SIZE" variants="A8XX-" usage="cmd"/>
<!-- Blob adds a bit more space {0x10, 0x20, 0x30, 0x40} bytes, but the meaning of
this additional space is not known.
-->
<reg32 offset="0x9886" name="PC_TF_BUFFER_SIZE" variants="A7XX" usage="cmd"/>
+ <reg32 offset="0x9815" name="PC_TF_BUFFER_SIZE" variants="A8XX-" usage="cmd"/>
<!-- 0x9982-0x9aff invalid -->
<reg32 offset="0x9b00" name="PC_CNTL" type="a6xx_pc_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b01" name="PC_CNTL" type="a6xx_pc_cntl" variants="A8XX-" usage="rp_blit"/>
<bitset name="a6xx_pc_xs_cntl" inline="yes">
<doc>
@@ -2381,7 +3329,13 @@ by a particular renderpass/blit.
<reg32 offset="0x9b03" name="PC_HS_CNTL" type="a6xx_pc_xs_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
<reg32 offset="0x9b04" name="PC_DS_CNTL" type="a6xx_pc_xs_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b02" name="PC_VS_CNTL" type="a6xx_pc_xs_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x9b03" name="PC_GS_CNTL" type="a6xx_pc_xs_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x9b04" name="PC_HS_CNTL" type="a6xx_pc_xs_cntl" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0x9b05" name="PC_DS_CNTL" type="a6xx_pc_xs_cntl" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0x9b05" name="PC_GS_PARAM_0" type="a6xx_gs_param_0" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b13" name="PC_GS_PARAM_0" type="a6xx_gs_param_0" variants="A8XX-" usage="rp_blit"/>
<reg32 offset="0x9b06" name="PC_PRIMITIVE_CNTL_6" variants="A6XX" usage="rp_blit">
<doc>
@@ -2391,24 +3345,37 @@ by a particular renderpass/blit.
</reg32>
<reg32 offset="0x9b07" name="PC_STEREO_RENDERING_CNTL" type="a6xx_stereo_rendering_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b09" name="PC_STEREO_RENDERING_CNTL" type="a6xx_stereo_rendering_cntl" variants="A8XX-" usage="rp_blit"/>
<!-- mask of enabled views, doesn't exist on A630 -->
<reg32 offset="0x9b08" name="PC_STEREO_RENDERING_VIEWMASK" type="hex" low="0" high="15" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0x9b0d" name="PC_STEREO_RENDERING_VIEWMASK" type="hex" low="0" high="15" variants="A8XX-" usage="rp_blit"/>
<!-- 0x9b09-0x9bff invalid -->
<reg32 offset="0x9c00" name="PC_2D_EVENT_CMD">
<!-- special register (but note first 8 bits can be written/read) -->
<bitfield name="EVENT" low="0" high="6" type="vgt_event_type"/>
<bitfield name="STATE_ID" low="8" high="15"/>
</reg32>
- <!-- 0x9c01-0x9dff invalid -->
- <!-- TODO: 0x9e00-0xa000 range incomplete -->
- <reg32 offset="0x9e00" name="PC_DBG_ECO_CNTL"/>
- <reg32 offset="0x9e01" name="PC_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
+
+ <reg32 offset="0x9e50" name="PC_CHICKEN_BITS_1" variants="A8XX-"/>
+ <reg32 offset="0x9f20" name="PC_CHICKEN_BITS_2" variants="A8XX-"/>
+ <reg32 offset="0x9e22" name="PC_CHICKEN_BITS_3" variants="A8XX-"/>
+ <reg32 offset="0x9e23" name="PC_CHICKEN_BITS_4" variants="A8XX-"/>
+ <reg32 offset="0x9f23" name="PC_CHICKEN_BITS_5" variants="A8XX-"/>
+
+ <reg32 offset="0x9e00" name="PC_DBG_ECO_CNTL" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9e53" name="PC_DBG_ECO_CNTL" variants="A8XX-"/>
+ <reg32 offset="0x9e01" name="PC_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
<reg64 offset="0x9e04" name="PC_DMA_BASE" type="address" variants="A6XX-A7XX"/>
<reg32 offset="0x9e06" name="PC_DMA_OFFSET" type="uint" variants="A6XX-A7XX"/>
<reg32 offset="0x9e07" name="PC_DMA_SIZE" type="uint" variants="A6XX-A7XX"/>
+ <reg64 offset="0x9e06" name="PC_DMA_BASE" type="address" variants="A8XX-"/>
+ <reg32 offset="0x9e08" name="PC_DMA_OFFSET" type="uint" variants="A8XX-"/>
+ <reg32 offset="0x9e09" name="PC_DMA_SIZE" type="uint" variants="A8XX-"/>
+
<reg64 offset="0x9e08" name="PC_TESS_BASE" variants="A6XX" type="waddress" align="32" usage="cmd"/>
<reg64 offset="0x9810" name="PC_TESS_BASE" variants="A7XX" type="waddress" align="32" usage="cmd"/>
+ <reg64 offset="0x9816" name="PC_TESS_BASE" variants="A8XX-" type="waddress" align="32" usage="cmd"/>
<reg32 offset="0x9e0b" name="PC_DRAWCALL_CNTL" type="vgt_draw_initiator_a4xx" variants="A6XX-A7XX">
<doc>
@@ -2419,6 +3386,10 @@ by a particular renderpass/blit.
<reg32 offset="0x9e0c" name="PC_DRAWCALL_INSTANCE_NUM" type="uint" variants="A6XX-A7XX"/>
<reg32 offset="0x9e0d" name="PC_DRAWCALL_SIZE" type="uint" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9e00" name="PC_DRAWCALL_CNTL" type="vgt_draw_initiator_a4xx" variants="A8XX-"/>
+ <reg32 offset="0x9e01" name="PC_DRAWCALL_INSTANCE_NUM" type="uint" variants="A8XX-"/>
+ <reg32 offset="0x9e02" name="PC_DRAWCALL_SIZE" type="uint" variants="A8XX-"/>
+
<!-- These match the contents of CP_SET_BIN_DATA (not written directly) -->
<bitset name="a6xx_pc_vis_stream_cntl" inline="yes">
<bitfield name="UNK0" low="0" high="15"/>
@@ -2430,20 +3401,30 @@ by a particular renderpass/blit.
<reg64 offset="0x9e12" name="PC_PVIS_STREAM_BIN_BASE" type="waddress" align="32" variants="A6XX-A7XX"/>
<reg64 offset="0x9e14" name="PC_DVIS_STREAM_BIN_BASE" type="waddress" align="32" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9e0a" name="PC_AUTO_VERTEX_STRIDE"/>
+ <reg32 offset="0x9e0d" name="PC_VIS_STREAM_CNTL" type="a6xx_pc_vis_stream_cntl" variants="A8XX-"/>
+ <reg64 offset="0x9e0e" name="PC_PVIS_STREAM_BIN_BASE" type="waddress" align="32" variants="A8XX-"/>
+ <reg64 offset="0x9e10" name="PC_DVIS_STREAM_BIN_BASE" type="waddress" align="32" variants="A8XX-"/>
+
<bitset name="a6xx_pc_drawcall_cntl_override" inline="yes">
<doc>Written by CP_SET_VISIBILITY_OVERRIDE handler</doc>
<bitfield name="OVERRIDE" pos="0" type="boolean"/>
</bitset>
<reg32 offset="0x9e1c" name="PC_DRAWCALL_CNTL_OVERRIDE" type="a6xx_pc_drawcall_cntl_override" variants="A6XX-A7XX"/>
+ <reg32 offset="0x9e04" name="PC_DRAWCALL_CNTL_OVERRIDE" type="a6xx_pc_drawcall_cntl_override" variants="A8XX-"/>
- <reg32 offset="0x9e24" name="PC_UNKNOWN_9E24" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0x9e24" name="PC_UNKNOWN_9E24" variants="A7XX-" usage="init"/>
<array offset="0x9e34" name="PC_PERFCTR_PC_SEL" stride="1" length="8" variants="A6XX"/>
- <array offset="0x9e42" name="PC_PERFCTR_PC_SEL" stride="1" length="16" variants="A7XX-"/>
+ <array offset="0x9e42" name="PC_PERFCTR_PC_SEL" stride="1" length="16" variants="A7XX"/>
+ <array offset="0x9e30" name="PC_PERFCTR_PC_SEL" stride="1" length="16" variants="A8XX-"/>
+ <array offset="0x9f00" name="PC_SLICE_PERFCTR_PC_SEL" stride="1" length="16" variants="A8XX-"/>
<!-- always 0x0 -->
- <reg32 offset="0x9e72" name="PC_UNKNOWN_9E72" usage="cmd"/>
+ <reg32 offset="0x9e72" name="PC_CONTEXT_SWITCH_GFX_PREEMPTION_MODE" variants="A6XX-A7XX" usage="init"/>
+ <reg32 offset="0x9e63" name="PC_CONTEXT_SWITCH_GFX_PREEMPTION_MODE" variants="A8XX-"/>
+ <reg32 offset="0x9e64" name="PC_CONTEXT_SWITCH_STABILIZE_CNTL_1" variants="A8XX-"/>
<reg32 offset="0xa000" name="VFD_CNTL_0" usage="rp_blit">
<bitfield name="FETCH_CNT" low="0" high="5" type="uint"/>
@@ -2530,11 +3511,15 @@ by a particular renderpass/blit.
<reg32 offset="0xa0f8" name="VFD_POWER_CNTL" low="0" high="2" usage="rp_blit"/>
- <reg32 offset="0xa600" name="VFD_DBG_ECO_CNTL" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0xa600" name="VFD_DBG_ECO_CNTL" variants="A7XX-" usage="init"/>
- <reg32 offset="0xa601" name="VFD_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
+ <reg32 offset="0xa601" name="VFD_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
<array offset="0xa610" name="VFD_PERFCTR_VFD_SEL" stride="1" length="8" variants="A6XX"/>
<array offset="0xa610" name="VFD_PERFCTR_VFD_SEL" stride="1" length="16" variants="A7XX-"/>
+ <reg32 offset="0xa639" name="VFD_CB_BV_THRESHOLD" variants="A8XX-"/>
+ <reg32 offset="0xa63a" name="VFD_CB_BR_THRESHOLD" variants="A8XX-"/>
+ <reg32 offset="0xa63b" name="VFD_CB_BUSY_REQ_CNT" variants="A8XX-"/>
+ <reg32 offset="0xa63c" name="VFD_CB_LP_REQ_CNT" variants="A8XX-"/>
<!--
Note: this seems to always be paired with another bit in another
@@ -2593,8 +3578,6 @@ by a particular renderpass/blit.
<bitset name="a6xx_sp_xs_output_cntl" inline="yes">
<!-- # of VS outputs including pos/psize -->
<bitfield name="OUT" low="0" high="5" type="uint"/>
- <!-- FLAGS_REGID only for GS -->
- <bitfield name="FLAGS_REGID" low="6" high="13" type="a3xx_regid"/>
</bitset>
<reg32 offset="0xa800" name="SP_VS_CNTL_0" type="a6xx_sp_xs_cntl_0" usage="rp_blit">
@@ -2720,6 +3703,15 @@ by a particular renderpass/blit.
<bitfield name="OFFSET" low="0" high="18" shr="11"/>
</bitset>
+ <bitset name="a6xx_sp_xs_hysteresis" inline="yes">
+ <doc>Same on a6xx/a7xx, UMD should not need to write this</doc>
+ </bitset>
+
+ <bitset name="a8xx_sp_xs_hysteresis" inline="yes">
+ <doc>UMD needs to write in some cases</doc>
+ <!-- seen 0x400, 0xc00, 0x1000, 0x1c00, 0x1000, 0x2000, 0x3000 -->
+ </bitset>
+
<reg32 offset="0xa81b" name="SP_VS_PROGRAM_COUNTER_OFFSET" type="uint" usage="rp_blit"/>
<reg64 offset="0xa81c" name="SP_VS_BASE" type="address" align="32" usage="rp_blit"/>
<reg32 offset="0xa81e" name="SP_VS_PVT_MEM_PARAM" type="a6xx_sp_xs_pvt_mem_param" usage="rp_blit"/>
@@ -2729,6 +3721,8 @@ by a particular renderpass/blit.
<reg32 offset="0xa823" name="SP_VS_CONFIG" type="a6xx_sp_xs_config" usage="rp_blit"/>
<reg32 offset="0xa824" name="SP_VS_INSTR_SIZE" low="0" high="27" type="uint" usage="rp_blit"/>
<reg32 offset="0xa825" name="SP_VS_PVT_MEM_STACK_OFFSET" type="a6xx_sp_xs_pvt_mem_stack_offset" usage="rp_blit"/>
+ <reg32 offset="0xa826" name="SP_VS_HYSTERESIS" type="a6xx_sp_xs_hysteresis" variants="A6XX-A7XX"/>
+ <reg32 offset="0xa826" name="SP_VS_HYSTERESIS" type="a8xx_sp_xs_hysteresis" variants="A8XX-"/>
<reg32 offset="0xa82d" name="SP_VS_VGS_CNTL" variants="A7XX-" usage="cmd"/>
<reg32 offset="0xa830" name="SP_HS_CNTL_0" type="a6xx_sp_xs_cntl_0" usage="rp_blit">
@@ -2754,6 +3748,8 @@ by a particular renderpass/blit.
<reg32 offset="0xa83b" name="SP_HS_CONFIG" type="a6xx_sp_xs_config" usage="rp_blit"/>
<reg32 offset="0xa83c" name="SP_HS_INSTR_SIZE" low="0" high="27" type="uint" usage="rp_blit"/>
<reg32 offset="0xa83d" name="SP_HS_PVT_MEM_STACK_OFFSET" type="a6xx_sp_xs_pvt_mem_stack_offset" usage="rp_blit"/>
+ <reg32 offset="0xa83e" name="SP_HS_HYSTERESIS" type="a6xx_sp_xs_hysteresis" variants="A6XX-A7XX"/>
+ <reg32 offset="0xa83e" name="SP_HS_HYSTERESIS" type="a8xx_sp_xs_hysteresis" variants="A8XX-"/>
<reg32 offset="0xa82f" name="SP_HS_VGS_CNTL" variants="A7XX-" usage="cmd"/>
<reg32 offset="0xa840" name="SP_DS_CNTL_0" type="a6xx_sp_xs_cntl_0" usage="rp_blit">
@@ -2791,6 +3787,8 @@ by a particular renderpass/blit.
<reg32 offset="0xa863" name="SP_DS_CONFIG" type="a6xx_sp_xs_config" usage="rp_blit"/>
<reg32 offset="0xa864" name="SP_DS_INSTR_SIZE" low="0" high="27" type="uint" usage="rp_blit"/>
<reg32 offset="0xa865" name="SP_DS_PVT_MEM_STACK_OFFSET" type="a6xx_sp_xs_pvt_mem_stack_offset" usage="rp_blit"/>
+ <reg32 offset="0xa866" name="SP_DS_HYSTERESIS" type="a6xx_sp_xs_hysteresis" variants="A6XX-A7XX"/>
+ <reg32 offset="0xa866" name="SP_DS_HYSTERESIS" type="a8xx_sp_xs_hysteresis" variants="A8XX-"/>
<reg32 offset="0xa868" name="SP_DS_VGS_CNTL" variants="A7XX-" usage="cmd"/>
<reg32 offset="0xa870" name="SP_GS_CNTL_0" type="a6xx_sp_xs_cntl_0" usage="rp_blit">
@@ -2814,7 +3812,10 @@ by a particular renderpass/blit.
<reg32 offset="0xa872" name="SP_GS_BOOLEAN_CF_MASK" type="hex" usage="rp_blit"/>
<!-- TODO: exact same layout as 0xa802-0xa81a -->
- <reg32 offset="0xa873" name="SP_GS_OUTPUT_CNTL" type="a6xx_sp_xs_output_cntl" usage="rp_blit"/>
+ <reg32 offset="0xa873" name="SP_GS_OUTPUT_CNTL" type="a6xx_sp_xs_output_cntl" usage="rp_blit">
+ <!-- FLAGS_REGID only for GS -->
+ <bitfield name="FLAGS_REGID" low="6" high="13" type="a3xx_regid"/>
+ </reg32>
<array offset="0xa874" name="SP_GS_OUTPUT" stride="1" length="16" usage="rp_blit">
<reg32 offset="0x0" name="REG">
<bitfield name="A_REGID" low="0" high="7" type="a3xx_regid"/>
@@ -2843,6 +3844,8 @@ by a particular renderpass/blit.
<reg32 offset="0xa894" name="SP_GS_CONFIG" type="a6xx_sp_xs_config" usage="rp_blit"/>
<reg32 offset="0xa895" name="SP_GS_INSTR_SIZE" low="0" high="27" type="uint" usage="rp_blit"/>
<reg32 offset="0xa896" name="SP_GS_PVT_MEM_STACK_OFFSET" type="a6xx_sp_xs_pvt_mem_stack_offset" usage="rp_blit"/>
+ <reg32 offset="0xa897" name="SP_GS_HYSTERESIS" type="a6xx_sp_xs_hysteresis" variants="A6XX-A7XX"/>
+ <reg32 offset="0xa897" name="SP_GS_HYSTERESIS" type="a8xx_sp_xs_hysteresis" variants="A8XX-"/>
<reg32 offset="0xa899" name="SP_GS_VGS_CNTL" variants="A7XX-" usage="cmd"/>
<reg64 offset="0xa8a0" name="SP_VS_SAMPLER_BASE" type="address" align="16" usage="cmd"/>
@@ -2886,13 +3889,19 @@ by a particular renderpass/blit.
<reg64 offset="0xa986" name="SP_PS_PVT_MEM_BASE" type="waddress" align="32" usage="rp_blit"/>
<reg32 offset="0xa988" name="SP_PS_PVT_MEM_SIZE" type="a6xx_sp_xs_pvt_mem_size" usage="rp_blit"/>
- <reg32 offset="0xa989" name="SP_BLEND_CNTL" usage="rp_blit">
+ <bitset name="a6xx_sp_blend_cntl" inline="yes">
<!-- per-mrt enable bit -->
<bitfield name="ENABLE_BLEND" low="0" high="7"/>
- <bitfield name="UNK8" pos="8" type="boolean"/>
+ <bitfield name="INDEPENDENT_BLEND_EN" pos="8" type="boolean"/>
<bitfield name="DUAL_COLOR_IN_ENABLE" pos="9" type="boolean"/>
<bitfield name="ALPHA_TO_COVERAGE" pos="10" type="boolean"/>
+ </bitset>
+
+ <reg32 offset="0xa989" name="SP_BLEND_CNTL" type="a6xx_sp_blend_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
+ <reg32 offset="0xa989" name="SP_BLEND_CNTL" type="a6xx_sp_blend_cntl" variants="A8XX-" usage="rp_blit">
+ <bitfield name="ALPHA_TO_ONE" pos="11" type="boolean" variants="A8XX-"/>
</reg32>
+
<reg32 offset="0xa98a" name="SP_SRGB_CNTL" usage="rp_blit">
<!-- Same as RB_SRGB_CNTL -->
<bitfield name="SRGB_MRT0" pos="0" type="boolean"/>
@@ -2993,13 +4002,11 @@ by a particular renderpass/blit.
<reg32 offset="0xa9a7" name="SP_PS_TSIZE" low="0" high="7" type="uint" usage="rp_blit"/>
<reg32 offset="0xa9a8" name="SP_UNKNOWN_A9A8" low="0" high="16" usage="cmd"/> <!-- always 0x0 ? -->
<reg32 offset="0xa9a9" name="SP_PS_PVT_MEM_STACK_OFFSET" type="a6xx_sp_xs_pvt_mem_stack_offset" usage="rp_blit"/>
- <reg32 offset="0xa9ab" name="SP_PS_UNKNOWN_A9AB" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0xa9ab" name="SP_PS_HYSTERESIS" type="a6xx_sp_xs_hysteresis" variants="A6XX-A7XX"/>
+ <reg32 offset="0xa9ab" name="SP_PS_HYSTERESIS" type="a8xx_sp_xs_hysteresis" variants="A8XX-"/>
<!-- TODO: unknown bool register at 0xa9aa, likely same as 0xa8c0-0xa8c3 but for FS -->
-
-
-
<reg32 offset="0xa9b0" name="SP_CS_CNTL_0" type="a6xx_sp_xs_cntl_0" usage="cmd">
<bitfield name="THREADSIZE" pos="20" type="a6xx_threadsize"/>
<!-- seems to make SP use less concurrent threads when possible? -->
@@ -3036,6 +4043,7 @@ by a particular renderpass/blit.
must be at least the actual CONSTLEN.
</doc>
</bitfield>
+ <bitfield name="ALT_LM_ENCODE" pos="26" type="boolean"/>
</reg32>
<reg32 offset="0xa9b2" name="SP_CS_BOOLEAN_CF_MASK" type="hex" usage="cmd"/>
<reg32 offset="0xa9b3" name="SP_CS_PROGRAM_COUNTER_OFFSET" type="uint" usage="cmd"/>
@@ -3047,7 +4055,8 @@ by a particular renderpass/blit.
<reg32 offset="0xa9bb" name="SP_CS_CONFIG" type="a6xx_sp_xs_config" usage="cmd"/>
<reg32 offset="0xa9bc" name="SP_CS_INSTR_SIZE" low="0" high="27" type="uint" usage="cmd"/>
<reg32 offset="0xa9bd" name="SP_CS_PVT_MEM_STACK_OFFSET" type="a6xx_sp_xs_pvt_mem_stack_offset" usage="cmd"/>
- <reg32 offset="0xa9be" name="SP_CS_UNKNOWN_A9BE" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0xa9be" name="SP_CS_HYSTERESIS" type="a6xx_sp_xs_hysteresis" variants="A6XX-A7XX"/>
+ <reg32 offset="0xa9be" name="SP_CS_HYSTERESIS" type="a8xx_sp_xs_hysteresis" variants="A8XX-"/>
<reg32 offset="0xa9c5" name="SP_CS_VGS_CNTL" variants="A7XX-" usage="cmd"/>
<!-- new in a6xx gen4, matches SP_CS_CONST_CONFIG_0 -->
@@ -3158,6 +4167,18 @@ by a particular renderpass/blit.
<bitfield name="RT7" low="28" high="31"/>
</reg32>
+ <array offset="0xaa04" name="SP_MRT_BLEND_CNTL" stride="1" length="8" variants="A8XX-">
+ <reg32 offset="0" name="REG">
+ <bitfield name="COLOR_BLEND_EN" pos="0" type="boolean"/>
+ <bitfield name="ALPHA_BLEND_EN" pos="1" type="boolean"/>
+ <bitfield name="COMPONENT_WRITE_MASK" low="7" high="10"/>
+ </reg32>
+ </array>
+
+ <reg32 offset="0xaa0c" name="SP_ALPHA_TEST_CNTL" variants="A8XX-">
+ <bitfield name="ALPHA_TEST" pos="8" type="boolean"/>
+ </reg32>
+
<reg32 offset="0xaaf2" name="SP_UNKNOWN_AAF2" type="uint" usage="cmd"/>
<!--
@@ -3182,15 +4203,19 @@ by a particular renderpass/blit.
-->
<bitfield name="CONSTANT_DEMOTION_ENABLE" pos="0" type="boolean"/>
<bitfield name="ISAMMODE" low="1" high="2" type="a6xx_isam_mode"/>
- <bitfield name="SHARED_CONSTS_ENABLE" pos="3" type="boolean"/> <!-- see HLSQ_SHARED_CONSTS -->
+ <bitfield name="SHARED_CONSTS_ENABLE" pos="3" type="boolean"/> <!-- see SP_SHARED_CONSTANT -->
</reg32>
<reg32 offset="0xab01" name="SP_UNKNOWN_AB01" variants="A7XX-" usage="cmd"/>
- <reg32 offset="0xab02" name="SP_UNKNOWN_AB02" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0xab02" name="SP_HLSQ_MODE_CNTL" variants="A7XX-" usage="cmd">
+ <bitfield name="SHARED_CONSTS_ENABLE" pos="0" type="boolean"/> <!-- see SP_SHARED_CONSTANT -->
+ </reg32>
<reg32 offset="0xab04" name="SP_PS_CONFIG" type="a6xx_sp_xs_config" usage="rp_blit"/>
<reg32 offset="0xab05" name="SP_PS_INSTR_SIZE" low="0" high="27" type="uint" usage="rp_blit"/>
+ <reg32 offset="0xab06" name="SP_BIN_SIZE" type="a8xx_bin_size" variants="A8XX-" usage="rp_blit"/>
+
<array offset="0xab10" name="SP_GFX_BINDLESS_BASE" stride="2" length="5" variants="A6XX" usage="rp_blit">
<reg64 offset="0" name="DESCRIPTOR" variants="A6XX">
<bitfield name="DESC_SIZE" low="0" high="1" type="a6xx_bindless_descriptor_size"/>
@@ -3210,9 +4235,12 @@ by a particular renderpass/blit.
-->
<reg64 offset="0xab1a" name="SP_GFX_UAV_BASE" type="address" align="16" usage="cmd"/>
<reg32 offset="0xab20" name="SP_GFX_USIZE" low="0" high="6" type="uint" variants="A6XX-A7XX" usage="cmd"/>
+ <reg32 offset="0xab09" name="SP_GFX_USIZE" low="0" high="6" type="uint" variants="A8XX-" usage="cmd"/>
<reg32 offset="0xab22" name="SP_UNKNOWN_AB22" variants="A7XX" usage="cmd"/>
+ <reg32 offset="0xab23" name="SP_UNKNOWN_AB23" variants="A8XX-"/>
+
<enum name="a6xx_sp_a2d_output_ifmt_type">
<value name="OUTPUT_IFMT_2D_FLOAT" value="0"/>
<value name="OUTPUT_IFMT_2D_SINT" value="1"/>
@@ -3234,22 +4262,27 @@ by a particular renderpass/blit.
<reg32 offset="0xacc0" name="SP_A2D_OUTPUT_INFO" type="a6xx_sp_a2d_output_info" variants="A6XX" usage="rp_blit"/>
<reg32 offset="0xa9bf" name="SP_A2D_OUTPUT_INFO" type="a6xx_sp_a2d_output_info" variants="A7XX-" usage="rp_blit"/>
- <reg32 offset="0xae00" name="SP_DBG_ECO_CNTL" usage="cmd"/>
- <reg32 offset="0xae01" name="SP_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode"/>
+ <reg32 offset="0xae00" name="SP_DBG_ECO_CNTL" usage="init"/>
+ <reg32 offset="0xae01" name="SP_ADDR_MODE_CNTL" pos="0" type="a5xx_address_mode" variants="A6XX"/>
+ <reg32 offset="0xae01" name="SP_SHADER_PROFILING" variants="A8XX-"/>
<reg32 offset="0xae02" name="SP_NC_MODE_CNTL">
<!-- TODO: valid bits 0x3c3f, see kernel -->
</reg32>
- <reg32 offset="0xae03" name="SP_CHICKEN_BITS" usage="cmd"/>
- <reg32 offset="0xae04" name="SP_NC_MODE_CNTL_2" usage="cmd">
+ <reg32 offset="0xae03" name="SP_CHICKEN_BITS" usage="init"/>
+ <reg32 offset="0xae04" name="SP_NC_MODE_CNTL_2" usage="init">
<bitfield name="F16_NO_INF" pos="3" type="boolean"/>
</reg32>
- <reg32 offset="0xae06" name="SP_UNKNOWN_AE06" variants="A7XX-" usage="cmd"/>
- <reg32 offset="0xae08" name="SP_CHICKEN_BITS_1" variants="A7XX-" usage="cmd"/>
- <reg32 offset="0xae09" name="SP_CHICKEN_BITS_2" variants="A7XX-" usage="cmd"/>
- <reg32 offset="0xae0a" name="SP_CHICKEN_BITS_3" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0xae05" name="SP_SS_CHICKEN_BITS_0" variants="A8XX-"/>
+ <reg32 offset="0xae06" name="SP_ISDB_CNTL" variants="A7XX-" usage="init"/>
+ <reg32 offset="0xae07" name="SP_PERFCTR_CNTL"/>
+ <reg32 offset="0xae08" name="SP_CHICKEN_BITS_1" variants="A7XX-" usage="init"/>
+ <reg32 offset="0xae09" name="SP_CHICKEN_BITS_2" variants="A7XX-" usage="init"/>
+ <reg32 offset="0xae0a" name="SP_CHICKEN_BITS_3" variants="A7XX-" usage="init"/>
+ <reg32 offset="0xae0b" name="SP_CHICKEN_BITS_4" variants="A8XX-"/>
+ <reg32 offset="0xae0c" name="SP_STATUS"/>
- <reg32 offset="0xae0f" name="SP_PERFCTR_SHADER_MASK" usage="cmd">
+ <reg32 offset="0xae0f" name="SP_PERFCTR_SHADER_MASK" usage="init">
<!-- some perfcntrs are affected by a per-stage enable bit
(PERF_SP_ALU_WORKING_CYCLES for example)
TODO: verify position of HS/DS/GS bits -->
@@ -3260,24 +4293,47 @@ by a particular renderpass/blit.
<bitfield name="FS" pos="4" type="boolean"/>
<bitfield name="CS" pos="5" type="boolean"/>
</reg32>
- <array offset="0xae10" name="SP_PERFCTR_SP_SEL" stride="1" length="24"/>
+ <array offset="0xae10" name="SP_PERFCTR_SP_SEL" stride="1" length="24" variants="A6XX"/>
<array offset="0xae60" name="SP_PERFCTR_HLSQ_SEL" stride="1" length="6" variants="A7XX-"/>
- <reg32 offset="0xae6a" name="SP_UNKNOWN_AE6A" variants="A7XX-" usage="cmd"/>
- <reg32 offset="0xae6b" name="SP_UNKNOWN_AE6B" variants="A7XX-" usage="cmd"/>
- <reg32 offset="0xae6c" name="SP_HLSQ_DBG_ECO_CNTL" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0xae6a" name="SP_UNKNOWN_AE6A" variants="A7XX-" usage="init"/>
+ <reg32 offset="0xae6b" name="SP_HLSQ_TIMEOUT_THRESHOLD_DP" variants="A7XX-" usage="init"/>
+ <reg32 offset="0xae6c" name="SP_HLSQ_DBG_ECO_CNTL" variants="A7XX-" usage="init"/>
<reg32 offset="0xae6d" name="SP_READ_SEL" variants="A7XX-">
+ <bitfield name="CONTEXT" low="26" high="30"/>
+ <bitfield name="SLICE" low="21" high="25"/>
<bitfield name="LOCATION" low="18" high="20" type="a7xx_state_location"/>
- <bitfield name="PIPE" low="16" high="17" type="a7xx_pipe"/>
+ <bitfield name="PIPE" low="16" high="17" type="adreno_pipe"/>
<bitfield name="STATETYPE" low="8" high="15" type="a7xx_statetype_id"/>
<bitfield name="USPTP" low="4" high="7"/>
<bitfield name="SPTP" low="0" high="3"/>
</reg32>
<reg32 offset="0xae71" name="SP_DBG_CNTL" variants="A7XX-"/>
- <reg32 offset="0xae73" name="SP_UNKNOWN_AE73" variants="A7XX-" usage="cmd"/>
+ <reg32 offset="0xae73" name="SP_HLSQ_DBG_ECO_CNTL_1" variants="A7XX-"/>
+ <reg32 offset="0xae74" name="SP_HLSQ_DBG_ECO_CNTL_2" variants="A7XX-"/>
+ <reg32 offset="0xae76" name="SP_HLSQ_DBG_ECO_CNTL_3" variants="A8XX-"/>
<array offset="0xae80" name="SP_PERFCTR_SP_SEL" stride="1" length="36" variants="A7XX-"/>
<!-- TODO: there are 4 more percntr select registers (0xae28-0xae2b) -->
<!-- TODO: there are a few unknown registers in the 0xae30-0xae52 range -->
- <reg32 offset="0xbe22" name="SP_CONTEXT_SWITCH_GFX_PREEMPTION_SAFE_MODE"/>
+ <reg32 offset="0xae52" name="SP_CONTEXT_SWITCH_GFX_PREEMPTION_SAFE_MODE"/>
+
+ <reg64 offset="0xae10" name="SP_HLSQ_GC_GMEM_RANGE_MIN" variants="A8XX-"/>
+ <reg64 offset="0xae12" name="SP_HLSQ_LPAC_GMEM_RANGE_MIN" variants="A8XX-"/>
+ <reg32 offset="0xae15" name="SP_LPAC_CPI_STATUS" variants="A8XX-"/>
+ <reg32 offset="0xae16" name="SP_LPAC_DBG_STATUS" variants="A8XX-"/>
+ <reg32 offset="0xae17" name="SP_LPAC_ISDB_BATCH_COUNT" variants="A8XX-"/>
+ <reg32 offset="0xae18" name="SP_LPAC_ISDB_BATCH_COUNT_INCR_EN" variants="A8XX-"/>
+ <reg32 offset="0xae19" name="SP_LPAC_ISDB_BATCH_COUNT_SHADERS" variants="A8XX-"/>
+ <reg32 offset="0xae30" name="SP_ISDB_BATCH_COUNT" variants="A7XX-"/>
+ <reg32 offset="0xae31" name="SP_ISDB_BATCH_COUNT_INCR_EN" variants="A7XX-"/>
+ <reg32 offset="0xae32" name="SP_ISDB_BATCH_COUNT_SHADERS" variants="A7XX-"/>
+ <reg32 offset="0xae35" name="SP_ISDB_DEBUG_CONFIG" variants="A7XX-"/>
+
+ <reg32 offset="0xae3a" name="SP_SELF_THROTTLE_CONTROL" variants="A7XX-"/>
+ <reg32 offset="0xae3b" name="SP_DISPATCH_CNTL" variants="A7XX-"/>
+ <reg64 offset="0xae3c" name="SP_SW_DEBUG_ADDR" variants="A7XX-"/>
+ <reg64 offset="0xae3e" name="SP_ISDB_DEBUG_ADDR" variants="A7XX-"/>
+
+ <array offset="0xaec0" name="SP_PERFCTR_HLSQ_SEL_2_0" stride="1" length="6" variants="A7XX-"/>
<!--
The downstream kernel calls the debug cluster of registers
@@ -3285,12 +4341,15 @@ by a particular renderpass/blit.
color base for compute shaders.
-->
<reg64 offset="0xb180" name="TPL1_CS_BORDER_COLOR_BASE" type="address" align="128" usage="cmd"/>
- <reg32 offset="0xb182" name="SP_UNKNOWN_B182" low="0" high="2" usage="cmd"/>
- <reg32 offset="0xb183" name="SP_UNKNOWN_B183" low="0" high="23" usage="cmd"/>
+ <reg32 offset="0xb182" name="TPL1_PS_ROTATION_CNTL" low="0" high="2" usage="cmd"/>
+ <reg32 offset="0xb183" name="TPL1_PS_SWIZZLE_CNTL" low="0" high="23" usage="cmd"/>
<reg32 offset="0xb190" name="SP_UNKNOWN_B190"/>
<reg32 offset="0xb191" name="SP_UNKNOWN_B191"/>
+ <reg32 offset="0xb2d6" name="TPL1_A2D_BIN_SIZE" type="a8xx_bin_size" variants="A8XX-" usage="rp_blit"/>
+ <reg32 offset="0xb2d7" name="TPL1_A2D_FILTER_CNTL" variants="A8XX-" usage="rp_blit"/>
+
<reg32 offset="0xb300" name="TPL1_RAS_MSAA_CNTL" usage="rp_blit">
<bitfield name="SAMPLES" low="0" high="1" type="a3xx_msaa_samples"/>
<bitfield name="UNK2" low="2" high="3"/>
@@ -3303,10 +4362,12 @@ by a particular renderpass/blit.
<!-- looks to work in the same way as a5xx: -->
<reg64 offset="0xb302" name="TPL1_GFX_BORDER_COLOR_BASE" type="address" align="128" usage="cmd"/>
<reg32 offset="0xb304" name="TPL1_MSAA_SAMPLE_POS_CNTL" type="a6xx_msaa_sample_pos_cntl" variants="A6XX-A7XX" usage="rp_blit"/>
- <reg32 offset="0xb305" name="TPL1_PROGRAMMABLE_MSAA_POS_0" type="a6xx_programmable_msaa_pos" usage="rp_blit"/>
- <reg32 offset="0xb306" name="TPL1_PROGRAMMABLE_MSAA_POS_1" type="a6xx_programmable_msaa_pos" usage="rp_blit"/>
+ <reg32 offset="0xb305" name="TPL1_PROGRAMMABLE_MSAA_POS_0" type="a6xx_programmable_msaa_pos" usage="rp_blit" variants="A6XX-A7XX" />
+ <reg32 offset="0xb306" name="TPL1_PROGRAMMABLE_MSAA_POS_1" type="a6xx_programmable_msaa_pos" usage="rp_blit" variants="A6XX-A7XX" />
<reg32 offset="0xb307" name="TPL1_WINDOW_OFFSET" type="a6xx_reg_xy" usage="rp_blit"/>
+ <reg32 offset="0xb304" name="TPL1_BIN_SIZE" type="a8xx_bin_size" variants="A8XX-" usage="rp_blit"/>
+
<enum name="a6xx_coord_round">
<value value="0" name="COORD_TRUNCATE"/>
<value value="1" name="COORD_ROUND_NEAREST_EVEN"/>
@@ -3315,13 +4376,17 @@ by a particular renderpass/blit.
<enum name="a6xx_nearest_mode">
<value value="0" name="ROUND_CLAMP_TRUNCATE"/>
<value value="1" name="CLAMP_ROUND_TRUNCATE"/>
+ <value value="2" name="ROUND_FLOAT_TO_INT"/> <!-- only ARRAYCOORDROUNDMODE -->
</enum>
<reg32 offset="0xb309" name="TPL1_MODE_CNTL" usage="cmd">
<bitfield name="ISAMMODE" low="0" high="1" type="a6xx_isam_mode"/>
<bitfield name="TEXCOORDROUNDMODE" pos="2" type="a6xx_coord_round"/>
+ <bitfield name="ARRAYCOORDROUNDMODE" low="3" high="4" type="a6xx_coord_round"/>
<bitfield name="NEARESTMIPSNAP" pos="5" type="a6xx_nearest_mode"/>
+ <bitfield name="SAMPLEREPLICATE" pos="6" type="boolean"/>
<bitfield name="DESTDATATYPEOVERRIDE" pos="7" type="boolean"/>
+ <bitfield name="PACK_SAMP_REDUCED_PRECISION" pos="8" type="boolean"/>
</reg32>
<reg32 offset="0xb310" name="SP_UNKNOWN_B310" variants="A7XX-" usage="cmd"/>
@@ -3387,11 +4452,12 @@ by a particular renderpass/blit.
<bitfield name="TYPE" low="29" high="31" type="a6xx_tex_type"/>
</reg32>
<reg32 offset="0xab21" name="SP_WINDOW_OFFSET" type="a6xx_reg_xy" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0xab07" name="SP_WINDOW_OFFSET" type="a6xx_reg_xy" variants="A8XX-" usage="rp_blit"/>
<!-- always 0x100000 or 0x1000000? -->
- <reg32 offset="0xb600" name="TPL1_DBG_ECO_CNTL" low="0" high="25" usage="cmd"/>
- <reg32 offset="0xb601" name="TPL1_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
- <reg32 offset="0xb602" name="TPL1_DBG_ECO_CNTL1" usage="cmd">
+ <reg32 offset="0xb600" name="TPL1_DBG_ECO_CNTL" low="0" high="25" usage="init"/>
+ <reg32 offset="0xb601" name="TPL1_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
+ <reg32 offset="0xb602" name="TPL1_DBG_ECO_CNTL1" usage="init">
<!-- Affects UBWC in some way, if BLIT_OP_SCALE is done with this bit set
and if other blit is done without it - UBWC image may be copied incorrectly.
-->
@@ -3404,7 +4470,7 @@ by a particular renderpass/blit.
<bitfield name="UPPER_BIT" pos="4" type="uint"/>
<bitfield name="UNK6" low="6" high="7"/>
</reg32>
- <reg32 offset="0xb605" name="TPL1_UNKNOWN_B605" low="0" high="7" type="uint" variants="A6XX" usage="cmd"/> <!-- always 0x0 or 0x44 ? -->
+ <reg32 offset="0xb605" name="TPL1_UNKNOWN_B605" low="0" high="7" type="uint" variants="A6XX" usage="init"/> <!-- always 0x0 or 0x44 ? -->
<array offset="0xb608" name="TPL1_BICUBIC_WEIGHTS_TABLE" stride="1" length="5" variants="A6XX">
<reg32 offset="0" name="REG" low="0" high="29"/>
@@ -3414,8 +4480,13 @@ by a particular renderpass/blit.
<reg32 offset="0" name="REG" low="0" high="29" usage="cmd"/>
</array>
+ <array offset="0xb606" name="TPL1_BICUBIC_WEIGHTS_TABLE" stride="1" length="25" variants="A8XX">
+ <reg32 offset="0" name="REG" low="0" high="29"/>
+ </array>
+
<array offset="0xb610" name="TPL1_PERFCTR_TP_SEL" stride="1" length="12" variants="A6XX"/>
<array offset="0xb610" name="TPL1_PERFCTR_TP_SEL" stride="1" length="18" variants="A7XX"/>
+ <array offset="0xb620" name="TPL1_PERFCTR_TP_SEL" stride="1" length="20" variants="A8XX"/>
<!-- TODO: 4 more perfcntr sel at 0xb620 ? -->
@@ -3458,10 +4529,8 @@ by a particular renderpass/blit.
<reg32 offset="0xa9ae" name="SP_PS_CNTL_1" variants="A7XX-" usage="rp_blit">
<bitfield name="SYSVAL_REGS_COUNT" low="0" high="7" type="uint"/>
- <!-- UNK8 is set on a730/a740 -->
- <bitfield name="UNK8" pos="8" type="boolean"/>
- <!-- UNK9 is set on a750 -->
- <bitfield name="UNK9" pos="9" type="boolean"/>
+ <bitfield name="DEFER_WAVE_ALLOC_DIS" pos="8" type="boolean"/>
+ <bitfield name="EVICT_BUF_MODE" low="9" high="10"/>
</reg32>
<reg32 offset="0xb820" name="HLSQ_LOAD_STATE_GEOM_CMD"/>
@@ -3512,9 +4581,12 @@ by a particular renderpass/blit.
<reg32 offset="0xb985" type="a6xx_sp_reg_prog_id_2" name="SP_REG_PROG_ID_2" variants="A6XX" usage="rp_blit"/>
<reg32 offset="0xb986" type="a6xx_sp_reg_prog_id_3" name="SP_REG_PROG_ID_3" variants="A6XX" usage="rp_blit"/>
<reg32 offset="0xb987" name="SP_CS_CONST_CONFIG" type="a6xx_xs_const_config" variants="A6XX" usage="cmd"/>
- <reg32 offset="0xa9c6" type="a6xx_sp_ps_wave_cntl" name="SP_PS_WAVE_CNTL" variants="A7XX-" usage="rp_blit"/>
+ <reg32 offset="0xa9c6" type="a6xx_sp_ps_wave_cntl" name="SP_PS_WAVE_CNTL" variants="A7XX" usage="rp_blit"/>
+ <reg32 offset="0xa9c6" name="SP_PS_WAVE_CNTL" variants="A8XX-" usage="rp_blit">
+ <bitfield name="VARYINGS" pos="1" type="boolean"/>
+ </reg32>
<reg32 offset="0xa9c7" name="SP_LB_PARAM_LIMIT" low="0" high="2" variants="A7XX-" usage="rp_blit">
- <bitfield name="PRIMALLOCTHRESHOLD" low="0" high="2" type="uint"/>
+ <bitfield name="PRIMALLOCTHRESHOLD" low="0" high="2" type="uint"/>
</reg32>
<reg32 offset="0xa9c8" name="SP_REG_PROG_ID_0" variants="A7XX-" usage="rp_blit">
<bitfield name="FACEREGID" low="0" high="7" type="a3xx_regid"/>
@@ -3718,7 +4790,7 @@ by a particular renderpass/blit.
<bitfield name="EVENT" low="0" high="6" type="vgt_event_type"/>
</reg32>
- <reg32 offset="0xab1f" name="SP_UPDATE_CNTL" variants="A7XX-" usage="cmd">
+ <reg32 offset="0xab1f" name="SP_UPDATE_CNTL" variants="A7XX" usage="cmd">
<doc>
This register clears pending loads queued up by
CP_LOAD_STATE6. Each bit resets a particular kind(s) of
@@ -3741,10 +4813,30 @@ by a particular renderpass/blit.
<bitfield name="GFX_BINDLESS" low="17" high="24" type="hex"/>
</reg32>
+ <reg32 offset="0xab1f" name="SP_UPDATE_CNTL" variants="A8XX" usage="cmd">
+ <doc>
+ This register clears pending loads queued up by
+ CP_LOAD_STATE6. Each bit resets a particular kind(s) of
+ CP_LOAD_STATE6.
+ </doc>
+
+ <!-- per-stage state: shader, non-bindless UBO, textures, and samplers -->
+ <bitfield name="VS_STATE" pos="0" type="boolean"/>
+ <bitfield name="HS_STATE" pos="1" type="boolean"/>
+ <bitfield name="DS_STATE" pos="2" type="boolean"/>
+ <bitfield name="GS_STATE" pos="3" type="boolean"/>
+ <bitfield name="FS_STATE" pos="4" type="boolean"/>
+ <bitfield name="CS_STATE" pos="5" type="boolean"/>
+ </reg32>
+
+ <reg32 offset="0xa9c0" name="SP_CS_BINDLESS_INVALIDATE"/>
+ <reg32 offset="0xab08" name="SP_GFX_BINDLESS_INVALIDATE"/>
+
<reg32 offset="0xbb10" name="SP_PS_CONST_CONFIG" type="a6xx_xs_const_config" variants="A6XX" usage="rp_blit"/>
<reg32 offset="0xab03" name="SP_PS_CONST_CONFIG" type="a6xx_xs_const_config" variants="A7XX-" usage="rp_blit"/>
<array offset="0xab40" name="SP_SHARED_CONSTANT_GFX" stride="1" length="64" variants="A7XX"/>
+ <array offset="0xab30" name="SP_SHARED_CONSTANT_GFX" stride="1" length="128" variants="A8XX-"/>
<reg32 offset="0xbb11" name="HLSQ_SHARED_CONSTS" variants="A6XX" usage="cmd">
<doc>
@@ -3781,15 +4873,15 @@ by a particular renderpass/blit.
<bitfield name="EVENT" low="0" high="6" type="vgt_event_type"/>
</reg32>
- <reg32 offset="0xbe00" name="HLSQ_UNKNOWN_BE00" variants="A6XX" usage="cmd"/> <!-- all bits valid except bit 29 -->
- <reg32 offset="0xbe01" name="HLSQ_UNKNOWN_BE01" low="4" high="6" variants="A6XX" usage="cmd"/>
- <reg32 offset="0xbe04" name="HLSQ_DBG_ECO_CNTL" variants="A6XX" usage="cmd"/>
- <reg32 offset="0xbe05" name="HLSQ_ADDR_MODE_CNTL" type="a5xx_address_mode"/>
+ <reg32 offset="0xbe00" name="HLSQ_UNKNOWN_BE00" variants="A6XX" usage="init"/> <!-- all bits valid except bit 29 -->
+ <reg32 offset="0xbe01" name="HLSQ_UNKNOWN_BE01" low="4" high="6" variants="A6XX" usage="init"/>
+ <reg32 offset="0xbe04" name="HLSQ_DBG_ECO_CNTL" variants="A6XX" usage="init"/>
+ <reg32 offset="0xbe05" name="HLSQ_ADDR_MODE_CNTL" type="a5xx_address_mode" variants="A6XX"/>
<reg32 offset="0xbe08" name="HLSQ_UNKNOWN_BE08" low="0" high="15"/>
<array offset="0xbe10" name="HLSQ_PERFCTR_HLSQ_SEL" stride="1" length="6"/>
<!-- TODO: some valid registers between 0xbe20 and 0xbe33 -->
- <reg32 offset="0xbe22" name="HLSQ_CONTEXT_SWITCH_GFX_PREEMPTION_SAFE_MODE"/>
+ <reg32 offset="0xbe22" name="HLSQ_CONTEXT_SWITCH_GFX_PREEMPTION_SAFE_MODE" variants="A6XX"/>
<reg32 offset="0xc000" name="SP_AHB_READ_APERTURE" variants="A7XX-"/>
@@ -3918,6 +5010,7 @@ by a particular renderpass/blit.
<reg32 offset="0x0001" name="SYSTEM_CACHE_CNTL_0"/>
<reg32 offset="0x0002" name="SYSTEM_CACHE_CNTL_1"/>
<reg32 offset="0x0039" name="CX_MISC_TCM_RET_CNTL" variants="A7XX-"/>
+ <reg32 offset="0x0087" name="CX_MISC_SLICE_ENABLE_FINAL" variants="A8XX"/>
<reg32 offset="0x0400" name="CX_MISC_SW_FUSE_VALUE" variants="A7XX-">
<bitfield pos="0" name="FASTBLEND" type="boolean"/>
<bitfield pos="1" name="LPAC" type="boolean"/>
diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml
index 4e42f055b85f..81538831dc19 100644
--- a/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_enums.xml
@@ -303,7 +303,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
</enum>
<!--
-Used in a6xx_a2d_bit_cntl.. the value mostly seems to correlate to the
+Used in a6xx_a2d_blt_cntl.. the value mostly seems to correlate to the
component type/size, so I think it relates to internal format used for
blending? The one exception is that 16b unorm and 32b float use the
same value... maybe 16b unorm is uncommon enough that it was just easier
diff --git a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
index b15a242d974d..c4e00b1263cd 100644
--- a/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/a6xx_gmu.xml
@@ -40,56 +40,62 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<bitfield name="IRQ_MASK_BIT" pos="0" />
</bitset>
- <reg32 offset="0x80" name="GPU_GMU_GX_SPTPRAC_CLOCK_CONTROL"/>
- <reg32 offset="0x81" name="GMU_GX_SPTPRAC_POWER_CONTROL"/>
- <reg32 offset="0xc00" name="GMU_CM3_ITCM_START"/>
- <reg32 offset="0x1c00" name="GMU_CM3_DTCM_START"/>
- <reg32 offset="0x23f0" name="GMU_NMI_CONTROL_STATUS"/>
- <reg32 offset="0x23f8" name="GMU_BOOT_SLUMBER_OPTION"/>
- <reg32 offset="0x23f9" name="GMU_GX_VOTE_IDX"/>
- <reg32 offset="0x23fa" name="GMU_MX_VOTE_IDX"/>
- <reg32 offset="0x23fc" name="GMU_DCVS_ACK_OPTION"/>
- <reg32 offset="0x23fd" name="GMU_DCVS_PERF_SETTING"/>
- <reg32 offset="0x23fe" name="GMU_DCVS_BW_SETTING"/>
- <reg32 offset="0x23ff" name="GMU_DCVS_RETURN"/>
- <reg32 offset="0x2bf8" name="GMU_CORE_FW_VERSION">
+ <reg32 offset="0x1a880" name="GPU_GMU_GX_SPTPRAC_CLOCK_CONTROL"/>
+ <reg32 offset="0x1a881" name="GMU_GX_SPTPRAC_POWER_CONTROL"/>
+ <reg32 offset="0x1b400" name="GMU_CM3_ITCM_START"/>
+ <reg32 offset="0x1c400" name="GMU_CM3_DTCM_START"/>
+ <reg32 offset="0x1cbf0" name="GMU_NMI_CONTROL_STATUS"/>
+ <reg32 offset="0x1cbf8" name="GMU_BOOT_SLUMBER_OPTION"/>
+ <reg32 offset="0x1cbf9" name="GMU_GX_VOTE_IDX"/>
+ <reg32 offset="0x1cbfa" name="GMU_MX_VOTE_IDX"/>
+ <reg32 offset="0x1cbfc" name="GMU_DCVS_ACK_OPTION"/>
+ <reg32 offset="0x1cbfd" name="GMU_DCVS_PERF_SETTING"/>
+ <reg32 offset="0x1cbfe" name="GMU_DCVS_BW_SETTING"/>
+ <reg32 offset="0x1cbff" name="GMU_DCVS_RETURN"/>
+ <reg32 offset="0x1d3f8" name="GMU_CORE_FW_VERSION">
<bitfield name="MAJOR" low="28" high="31"/>
<bitfield name="MINOR" low="16" high="27"/>
<bitfield name="STEP" low="0" high="15"/>
</reg32>
- <reg32 offset="0x4c00" name="GMU_ICACHE_CONFIG"/>
- <reg32 offset="0x4c01" name="GMU_DCACHE_CONFIG"/>
- <reg32 offset="0x4c0f" name="GMU_SYS_BUS_CONFIG"/>
- <reg32 offset="0x5000" name="GMU_CM3_SYSRESET"/>
- <reg32 offset="0x5001" name="GMU_CM3_BOOT_CONFIG"/>
- <reg32 offset="0x501a" name="GMU_CM3_FW_BUSY"/>
- <reg32 offset="0x501c" name="GMU_CM3_FW_INIT_RESULT"/>
- <reg32 offset="0x502d" name="GMU_CM3_CFG"/>
- <reg32 offset="0x5040" name="GMU_CX_GMU_POWER_COUNTER_ENABLE"/>
- <reg32 offset="0x5041" name="GMU_CX_GMU_POWER_COUNTER_SELECT_0"/>
- <reg32 offset="0x5042" name="GMU_CX_GMU_POWER_COUNTER_SELECT_1"/>
- <reg32 offset="0x5044" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L"/>
- <reg32 offset="0x5045" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H"/>
- <reg32 offset="0x5046" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_1_L"/>
- <reg32 offset="0x5047" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_1_H"/>
- <reg32 offset="0x5048" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_2_L"/>
- <reg32 offset="0x5049" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_2_H"/>
- <reg32 offset="0x504a" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_3_L"/>
- <reg32 offset="0x504b" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_3_H"/>
- <reg32 offset="0x504c" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L"/>
- <reg32 offset="0x504d" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_4_H"/>
- <reg32 offset="0x504e" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_5_L"/>
- <reg32 offset="0x504f" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_5_H"/>
- <reg32 offset="0x50c0" name="GMU_PWR_COL_INTER_FRAME_CTRL">
+ <reg32 offset="0x1f400" name="GMU_ICACHE_CONFIG"/>
+ <reg32 offset="0x1f401" name="GMU_DCACHE_CONFIG"/>
+ <reg32 offset="0x1f40f" name="GMU_SYS_BUS_CONFIG"/>
+ <reg32 offset="0x1f50b" name="GMU_MRC_GBIF_QOS_CTRL"/>
+ <reg32 offset="0x1f800" name="GMU_CM3_SYSRESET"/>
+ <reg32 offset="0x1f801" name="GMU_CM3_BOOT_CONFIG"/>
+ <reg32 offset="0x1f81a" name="GMU_CM3_FW_BUSY"/>
+ <reg32 offset="0x1f81c" name="GMU_CM3_FW_INIT_RESULT"/>
+ <reg32 offset="0x1f82d" name="GMU_CM3_CFG"/>
+ <reg32 offset="0x1f840" name="GMU_CX_GMU_POWER_COUNTER_ENABLE"/>
+ <reg32 offset="0x1fc10" name="GMU_CX_GMU_POWER_COUNTER_ENABLE" variants="A8XX"/>
+ <reg32 offset="0x1f841" name="GMU_CX_GMU_POWER_COUNTER_SELECT_0"/>
+ <reg32 offset="0x1f842" name="GMU_CX_GMU_POWER_COUNTER_SELECT_1"/>
+ <reg32 offset="0x1fc40" name="GMU_CX_GMU_POWER_COUNTER_SELECT_XOCLK_0" variants="A8XX-"/>
+ <reg32 offset="0x1fc41" name="GMU_CX_GMU_POWER_COUNTER_SELECT_XOCLK_1" variants="A8XX-"/>
+ <reg32 offset="0x1f844" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L"/>
+ <reg32 offset="0x1fca0" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_0_L" variants="A8XX-"/>
+ <reg32 offset="0x1f845" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H"/>
+ <reg32 offset="0x1fca1" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_0_H" variants="A8XX-"/>
+ <reg32 offset="0x1f846" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_1_L"/>
+ <reg32 offset="0x1f847" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_1_H"/>
+ <reg32 offset="0x1f848" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_2_L"/>
+ <reg32 offset="0x1f849" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_2_H"/>
+ <reg32 offset="0x1f84a" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_3_L"/>
+ <reg32 offset="0x1f84b" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_3_H"/>
+ <reg32 offset="0x1f84c" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_4_L"/>
+ <reg32 offset="0x1f84d" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_4_H"/>
+ <reg32 offset="0x1f84e" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_5_L"/>
+ <reg32 offset="0x1f84f" name="GMU_CX_GMU_POWER_COUNTER_XOCLK_5_H"/>
+ <reg32 offset="0x1f8c0" name="GMU_PWR_COL_INTER_FRAME_CTRL">
<bitfield name="IFPC_ENABLE" pos="0" type="boolean"/>
<bitfield name="HM_POWER_COLLAPSE_ENABLE" pos="1" type="boolean"/>
<bitfield name="SPTPRAC_POWER_CONTROL_ENABLE" pos="2" type="boolean"/>
<bitfield name="NUM_PASS_SKIPS" low="10" high="13"/>
<bitfield name="MIN_PASS_LENGTH" low="14" high="31"/>
</reg32>
- <reg32 offset="0x50c1" name="GMU_PWR_COL_INTER_FRAME_HYST"/>
- <reg32 offset="0x50c2" name="GMU_PWR_COL_SPTPRAC_HYST"/>
- <reg32 offset="0x50d0" name="GMU_SPTPRAC_PWR_CLK_STATUS">
+ <reg32 offset="0x1f8c1" name="GMU_PWR_COL_INTER_FRAME_HYST"/>
+ <reg32 offset="0x1f8c2" name="GMU_PWR_COL_SPTPRAC_HYST"/>
+ <reg32 offset="0x1f8d0" name="GMU_SPTPRAC_PWR_CLK_STATUS" variants="A6XX">
<bitfield name="SPTPRAC_GDSC_POWERING_OFF" pos="0" type="boolean"/>
<bitfield name="SPTPRAC_GDSC_POWERING_ON" pos="1" type="boolean"/>
<bitfield name="SPTPRAC_GDSC_POWER_OFF" pos="2" type="boolean"/>
@@ -99,15 +105,19 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<bitfield name="GX_HM_GDSC_POWER_OFF" pos="6" type="boolean"/>
<bitfield name="GX_HM_CLK_OFF" pos="7" type="boolean"/>
</reg32>
- <reg32 offset="0x50d0" name="GMU_SPTPRAC_PWR_CLK_STATUS" variants="A7XX">
+ <reg32 offset="0x1f8d0" name="GMU_SPTPRAC_PWR_CLK_STATUS" variants="A7XX">
<bitfield name="GX_HM_GDSC_POWER_OFF" pos="0" type="boolean"/>
<bitfield name="GX_HM_CLK_OFF" pos="1" type="boolean"/>
</reg32>
- <reg32 offset="0x50e4" name="GMU_GPU_NAP_CTRL">
+ <reg32 offset="0x1f7e8" name="GMU_PWR_CLK_STATUS" variants="A8XX-">
+ <bitfield name="GX_HM_GDSC_POWER_OFF" pos="0" type="boolean"/>
+ <bitfield name="GX_HM_CLK_OFF" pos="1" type="boolean"/>
+ </reg32>
+ <reg32 offset="0x1f8e4" name="GMU_GPU_NAP_CTRL">
<bitfield name="HW_NAP_ENABLE" pos="0"/>
<bitfield name="SID" low="4" high="8"/>
</reg32>
- <reg32 offset="0x50e8" name="GMU_RPMH_CTRL">
+ <reg32 offset="0x1f8e8" name="GMU_RPMH_CTRL">
<bitfield name="RPMH_INTERFACE_ENABLE" pos="0" type="boolean"/>
<bitfield name="LLC_VOTE_ENABLE" pos="4" type="boolean"/>
<bitfield name="DDR_VOTE_ENABLE" pos="8" type="boolean"/>
@@ -119,71 +129,84 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<bitfield name="CX_MIN_VOTE_ENABLE" pos="14" type="boolean"/>
<bitfield name="GFX_MIN_VOTE_ENABLE" pos="15" type="boolean"/>
</reg32>
- <reg32 offset="0x50e9" name="GMU_RPMH_HYST_CTRL"/>
- <reg32 offset="0x50ec" name="GPU_GMU_CX_GMU_RPMH_POWER_STATE"/>
- <reg32 offset="0x50f0" name="GPU_GMU_CX_GMU_CX_FAL_INTF"/>
- <reg32 offset="0x50f1" name="GPU_GMU_CX_GMU_CX_FALNEXT_INTF"/>
- <reg32 offset="0x5100" name="GPU_GMU_CX_GMU_PWR_COL_CP_MSG"/>
- <reg32 offset="0x5101" name="GPU_GMU_CX_GMU_PWR_COL_CP_RESP"/>
- <reg32 offset="0x51f0" name="GMU_BOOT_KMD_LM_HANDSHAKE"/>
- <reg32 offset="0x5157" name="GMU_LLM_GLM_SLEEP_CTRL"/>
- <reg32 offset="0x5158" name="GMU_LLM_GLM_SLEEP_STATUS"/>
- <reg32 offset="0x5088" name="GMU_ALWAYS_ON_COUNTER_L"/>
- <reg32 offset="0x5089" name="GMU_ALWAYS_ON_COUNTER_H"/>
- <reg32 offset="0x50c3" name="GMU_GMU_PWR_COL_KEEPALIVE"/>
- <reg32 offset="0x50c4" name="GMU_PWR_COL_PREEMPT_KEEPALIVE"/>
- <reg32 offset="0x5180" name="GMU_HFI_CTRL_STATUS"/>
- <reg32 offset="0x5181" name="GMU_HFI_VERSION_INFO"/>
- <reg32 offset="0x5182" name="GMU_HFI_SFR_ADDR"/>
- <reg32 offset="0x5183" name="GMU_HFI_MMAP_ADDR"/>
- <reg32 offset="0x5184" name="GMU_HFI_QTBL_INFO"/>
- <reg32 offset="0x5185" name="GMU_HFI_QTBL_ADDR"/>
- <reg32 offset="0x5186" name="GMU_HFI_CTRL_INIT"/>
- <reg32 offset="0x5190" name="GMU_GMU2HOST_INTR_SET"/>
- <reg32 offset="0x5191" name="GMU_GMU2HOST_INTR_CLR"/>
- <reg32 offset="0x5192" name="GMU_GMU2HOST_INTR_INFO">
+ <reg32 offset="0x1f8e9" name="GMU_RPMH_HYST_CTRL"/>
+ <reg32 offset="0x1f8ec" name="GPU_GMU_CX_GMU_RPMH_POWER_STATE" variants="A6XX"/>
+ <reg32 offset="0x1f7e9" name="GPU_GMU_CX_GMU_RPMH_POWER_STATE" variants="A8XX-"/>
+ <reg32 offset="0x1f8f0" name="GPU_GMU_CX_GMU_CX_FAL_INTF" variants="A6XX"/>
+ <reg32 offset="0x1f7ec" name="GPU_GMU_CX_GMU_CX_FAL_INTF" variants="A8XX-"/>
+ <reg32 offset="0x1f8f1" name="GPU_GMU_CX_GMU_CX_FALNEXT_INTF" variants="A6XX"/>
+ <reg32 offset="0x1f7ed" name="GPU_GMU_CX_GMU_CX_FALNEXT_INTF" variants="A8XX-"/>
+ <reg32 offset="0x1f900" name="GPU_GMU_CX_GMU_PWR_COL_CP_MSG"/>
+ <reg32 offset="0x1f901" name="GPU_GMU_CX_GMU_PWR_COL_CP_RESP"/>
+ <reg32 offset="0x1f9f0" name="GMU_BOOT_KMD_LM_HANDSHAKE"/>
+ <reg32 offset="0x1f957" name="GMU_LLM_GLM_SLEEP_CTRL"/>
+ <reg32 offset="0x1f958" name="GMU_LLM_GLM_SLEEP_STATUS"/>
+ <reg32 offset="0x1f888" name="GMU_ALWAYS_ON_COUNTER_L"/>
+ <reg32 offset="0x1f889" name="GMU_ALWAYS_ON_COUNTER_H"/>
+ <reg32 offset="0x1f8c3" name="GMU_GMU_PWR_COL_KEEPALIVE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x1f7e4" name="GMU_GMU_PWR_COL_KEEPALIVE" variants="A8XX-"/>
+ <reg32 offset="0x1f8c4" name="GMU_PWR_COL_PREEMPT_KEEPALIVE" variants="A6XX-A7XX"/>
+ <reg32 offset="0x1f7e5" name="GMU_PWR_COL_PREEMPT_KEEPALIVE" variants="A8XX-"/>
+ <reg32 offset="0x1f980" name="GMU_HFI_CTRL_STATUS"/>
+ <reg32 offset="0x1f981" name="GMU_HFI_VERSION_INFO"/>
+ <reg32 offset="0x1f982" name="GMU_HFI_SFR_ADDR"/>
+ <reg32 offset="0x1f983" name="GMU_HFI_MMAP_ADDR"/>
+ <reg32 offset="0x1f984" name="GMU_HFI_QTBL_INFO"/>
+ <reg32 offset="0x1f985" name="GMU_HFI_QTBL_ADDR"/>
+ <reg32 offset="0x1f986" name="GMU_HFI_CTRL_INIT"/>
+ <reg32 offset="0x1f990" name="GMU_GMU2HOST_INTR_SET"/>
+ <reg32 offset="0x1f991" name="GMU_GMU2HOST_INTR_CLR"/>
+ <reg32 offset="0x1f992" name="GMU_GMU2HOST_INTR_INFO">
<bitfield name="MSGQ" pos="0" type="boolean"/>
<bitfield name="CM3_FAULT" pos="23" type="boolean"/>
</reg32>
- <reg32 offset="0x5193" name="GMU_GMU2HOST_INTR_MASK"/>
- <reg32 offset="0x5194" name="GMU_HOST2GMU_INTR_SET"/>
- <reg32 offset="0x5195" name="GMU_HOST2GMU_INTR_CLR"/>
- <reg32 offset="0x5196" name="GMU_HOST2GMU_INTR_RAW_INFO"/>
- <reg32 offset="0x5197" name="GMU_HOST2GMU_INTR_EN_0"/>
- <reg32 offset="0x5198" name="GMU_HOST2GMU_INTR_EN_1"/>
- <reg32 offset="0x5199" name="GMU_HOST2GMU_INTR_EN_2"/>
- <reg32 offset="0x519a" name="GMU_HOST2GMU_INTR_EN_3"/>
- <reg32 offset="0x519b" name="GMU_HOST2GMU_INTR_INFO_0"/>
- <reg32 offset="0x519c" name="GMU_HOST2GMU_INTR_INFO_1"/>
- <reg32 offset="0x519d" name="GMU_HOST2GMU_INTR_INFO_2"/>
- <reg32 offset="0x519e" name="GMU_HOST2GMU_INTR_INFO_3"/>
- <reg32 offset="0x51c5" name="GMU_GENERAL_0"/>
- <reg32 offset="0x51c6" name="GMU_GENERAL_1"/>
- <reg32 offset="0x51cb" name="GMU_GENERAL_6"/>
- <reg32 offset="0x51cc" name="GMU_GENERAL_7"/>
- <reg32 offset="0x51cd" name="GMU_GENERAL_8" variants="A7XX"/>
- <reg32 offset="0x51ce" name="GMU_GENERAL_9" variants="A7XX"/>
- <reg32 offset="0x51cf" name="GMU_GENERAL_10" variants="A7XX"/>
- <reg32 offset="0x515d" name="GMU_ISENSE_CTRL"/>
- <reg32 offset="0x8920" name="GPU_CS_ENABLE_REG"/>
- <reg32 offset="0x515d" name="GPU_GMU_CX_GMU_ISENSE_CTRL"/>
- <reg32 offset="0x8578" name="GPU_CS_AMP_CALIBRATION_CONTROL3"/>
- <reg32 offset="0x8558" name="GPU_CS_AMP_CALIBRATION_CONTROL2"/>
- <reg32 offset="0x8580" name="GPU_CS_A_SENSOR_CTRL_0"/>
- <reg32 offset="0x27ada" name="GPU_CS_A_SENSOR_CTRL_2"/>
- <reg32 offset="0x881a" name="GPU_CS_SENSOR_GENERAL_STATUS"/>
- <reg32 offset="0x8957" name="GPU_CS_AMP_CALIBRATION_CONTROL1"/>
- <reg32 offset="0x881a" name="GPU_CS_SENSOR_GENERAL_STATUS"/>
- <reg32 offset="0x881d" name="GPU_CS_AMP_CALIBRATION_STATUS1_0"/>
- <reg32 offset="0x881f" name="GPU_CS_AMP_CALIBRATION_STATUS1_2"/>
- <reg32 offset="0x8821" name="GPU_CS_AMP_CALIBRATION_STATUS1_4"/>
- <reg32 offset="0x8965" name="GPU_CS_AMP_CALIBRATION_DONE"/>
- <reg32 offset="0x896d" name="GPU_CS_AMP_PERIOD_CTRL"/>
- <reg32 offset="0x8965" name="GPU_CS_AMP_CALIBRATION_DONE"/>
- <reg32 offset="0x514d" name="GPU_GMU_CX_GMU_PWR_THRESHOLD"/>
- <reg32 offset="0x9303" name="GMU_AO_INTERRUPT_EN"/>
- <reg32 offset="0x9304" name="GMU_AO_HOST_INTERRUPT_CLR"/>
- <reg32 offset="0x9305" name="GMU_AO_HOST_INTERRUPT_STATUS">
+ <reg32 offset="0x1f993" name="GMU_GMU2HOST_INTR_MASK"/>
+ <reg32 offset="0x1f994" name="GMU_HOST2GMU_INTR_SET"/>
+ <reg32 offset="0x1f995" name="GMU_HOST2GMU_INTR_CLR"/>
+ <reg32 offset="0x1f996" name="GMU_HOST2GMU_INTR_RAW_INFO"/>
+ <reg32 offset="0x1f997" name="GMU_HOST2GMU_INTR_EN_0"/>
+ <reg32 offset="0x1f998" name="GMU_HOST2GMU_INTR_EN_1"/>
+ <reg32 offset="0x1f999" name="GMU_HOST2GMU_INTR_EN_2"/>
+ <reg32 offset="0x1f99a" name="GMU_HOST2GMU_INTR_EN_3"/>
+ <reg32 offset="0x1f99b" name="GMU_HOST2GMU_INTR_INFO_0"/>
+ <reg32 offset="0x1f99c" name="GMU_HOST2GMU_INTR_INFO_1"/>
+ <reg32 offset="0x1f99d" name="GMU_HOST2GMU_INTR_INFO_2"/>
+ <reg32 offset="0x1f99e" name="GMU_HOST2GMU_INTR_INFO_3"/>
+ <reg32 offset="0x1f9c5" name="GMU_GENERAL_0"/>
+ <reg32 offset="0x1f9c6" name="GMU_GENERAL_1"/>
+ <reg32 offset="0x1f9cb" name="GMU_GENERAL_6"/>
+ <reg32 offset="0x1f9cc" name="GMU_GENERAL_7"/>
+ <reg32 offset="0x1f9cd" name="GMU_GENERAL_8" variants="A7XX"/>
+ <reg32 offset="0x1f9ce" name="GMU_GENERAL_9" variants="A7XX"/>
+ <reg32 offset="0x1f9cf" name="GMU_GENERAL_10" variants="A7XX"/>
+ <reg32 offset="0x1f9c0" name="GMU_GENERAL_0" variants="A8XX"/>
+ <reg32 offset="0x1f9c1" name="GMU_GENERAL_1" variants="A8XX"/>
+ <reg32 offset="0x1f9c6" name="GMU_GENERAL_6" variants="A8XX"/>
+ <reg32 offset="0x1f9c7" name="GMU_GENERAL_7" variants="A8XX"/>
+ <reg32 offset="0x1f9c8" name="GMU_GENERAL_8" variants="A8XX"/>
+ <reg32 offset="0x1f9c9" name="GMU_GENERAL_9" variants="A8XX"/>
+ <reg32 offset="0x1f9ca" name="GMU_GENERAL_10" variants="A8XX"/>
+ <reg32 offset="0x1f9cb" name="GMU_GENERAL_11" variants="A8XX"/>
+ <reg32 offset="0x1f95d" name="GMU_ISENSE_CTRL"/>
+ <reg32 offset="0x23120" name="GPU_CS_ENABLE_REG"/>
+ <reg32 offset="0x1f95d" name="GPU_GMU_CX_GMU_ISENSE_CTRL"/>
+ <reg32 offset="0x22d78" name="GPU_CS_AMP_CALIBRATION_CONTROL3"/>
+ <reg32 offset="0x22d58" name="GPU_CS_AMP_CALIBRATION_CONTROL2"/>
+ <reg32 offset="0x22d80" name="GPU_CS_A_SENSOR_CTRL_0"/>
+ <reg32 offset="0x422da" name="GPU_CS_A_SENSOR_CTRL_2"/>
+ <reg32 offset="0x2301a" name="GPU_CS_SENSOR_GENERAL_STATUS"/>
+ <reg32 offset="0x23157" name="GPU_CS_AMP_CALIBRATION_CONTROL1"/>
+ <reg32 offset="0x2301a" name="GPU_CS_SENSOR_GENERAL_STATUS"/>
+ <reg32 offset="0x2301d" name="GPU_CS_AMP_CALIBRATION_STATUS1_0"/>
+ <reg32 offset="0x2301f" name="GPU_CS_AMP_CALIBRATION_STATUS1_2"/>
+ <reg32 offset="0x23021" name="GPU_CS_AMP_CALIBRATION_STATUS1_4"/>
+ <reg32 offset="0x23165" name="GPU_CS_AMP_CALIBRATION_DONE"/>
+ <reg32 offset="0x2316d" name="GPU_CS_AMP_PERIOD_CTRL"/>
+ <reg32 offset="0x23165" name="GPU_CS_AMP_CALIBRATION_DONE"/>
+ <reg32 offset="0x1f94d" name="GPU_GMU_CX_GMU_PWR_THRESHOLD"/>
+ <reg32 offset="0x23b03" name="GMU_AO_INTERRUPT_EN"/>
+ <reg32 offset="0x23b04" name="GMU_AO_HOST_INTERRUPT_CLR"/>
+ <reg32 offset="0x23b05" name="GMU_AO_HOST_INTERRUPT_STATUS">
<bitfield name="WDOG_BITE" pos="0" type="boolean"/>
<bitfield name="RSCC_COMP" pos="1" type="boolean"/>
<bitfield name="VDROOP" pos="2" type="boolean"/>
@@ -191,27 +214,27 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<bitfield name="DBD_WAKEUP" pos="4" type="boolean"/>
<bitfield name="HOST_AHB_BUS_ERROR" pos="5" type="boolean"/>
</reg32>
- <reg32 offset="0x9306" name="GMU_AO_HOST_INTERRUPT_MASK"/>
- <reg32 offset="0x9309" name="GPU_GMU_AO_GMU_CGC_MODE_CNTL"/>
- <reg32 offset="0x930a" name="GPU_GMU_AO_GMU_CGC_DELAY_CNTL"/>
- <reg32 offset="0x930b" name="GPU_GMU_AO_GMU_CGC_HYST_CNTL"/>
- <reg32 offset="0x930c" name="GPU_GMU_AO_GPU_CX_BUSY_STATUS">
+ <reg32 offset="0x23b06" name="GMU_AO_HOST_INTERRUPT_MASK"/>
+ <reg32 offset="0x23b09" name="GPU_GMU_AO_GMU_CGC_MODE_CNTL"/>
+ <reg32 offset="0x23b0a" name="GPU_GMU_AO_GMU_CGC_DELAY_CNTL"/>
+ <reg32 offset="0x23b0b" name="GPU_GMU_AO_GMU_CGC_HYST_CNTL"/>
+ <reg32 offset="0x23b0c" name="GPU_GMU_AO_GPU_CX_BUSY_STATUS">
<bitfield name = "GPUBUSYIGNAHB" pos="23" type="boolean"/>
</reg32>
- <reg32 offset="0x930d" name="GPU_GMU_AO_GPU_CX_BUSY_STATUS2"/>
- <reg32 offset="0x930e" name="GPU_GMU_AO_GPU_CX_BUSY_MASK"/>
- <reg32 offset="0x9310" name="GMU_AO_AHB_FENCE_CTRL"/>
- <reg32 offset="0x9313" name="GMU_AHB_FENCE_STATUS"/>
- <reg32 offset="0x9314" name="GMU_AHB_FENCE_STATUS_CLR"/>
- <reg32 offset="0x9315" name="GMU_RBBM_INT_UNMASKED_STATUS"/>
- <reg32 offset="0x9316" name="GMU_AO_SPARE_CNTL"/>
- <reg32 offset="0x9307" name="GMU_RSCC_CONTROL_REQ"/>
- <reg32 offset="0x9308" name="GMU_RSCC_CONTROL_ACK"/>
- <reg32 offset="0x9311" name="GMU_AHB_FENCE_RANGE_0"/>
- <reg32 offset="0x9312" name="GMU_AHB_FENCE_RANGE_1"/>
- <reg32 offset="0x9c03" name="GPU_CC_GX_GDSCR"/>
- <reg32 offset="0x9d42" name="GPU_CC_GX_DOMAIN_MISC"/>
- <reg32 offset="0xc001" name="GPU_CPR_FSM_CTL"/>
+ <reg32 offset="0x23b0d" name="GPU_GMU_AO_GPU_CX_BUSY_STATUS2"/>
+ <reg32 offset="0x23b0e" name="GPU_GMU_AO_GPU_CX_BUSY_MASK"/>
+ <reg32 offset="0x23b10" name="GMU_AO_AHB_FENCE_CTRL"/>
+ <reg32 offset="0x23b13" name="GMU_AHB_FENCE_STATUS"/>
+ <reg32 offset="0x23b14" name="GMU_AHB_FENCE_STATUS_CLR"/>
+ <reg32 offset="0x23b15" name="GMU_RBBM_INT_UNMASKED_STATUS"/>
+ <reg32 offset="0x23b16" name="GMU_AO_SPARE_CNTL"/>
+ <reg32 offset="0x23b07" name="GMU_RSCC_CONTROL_REQ"/>
+ <reg32 offset="0x23b08" name="GMU_RSCC_CONTROL_ACK"/>
+ <reg32 offset="0x23b11" name="GMU_AHB_FENCE_RANGE_0"/>
+ <reg32 offset="0x23b12" name="GMU_AHB_FENCE_RANGE_1"/>
+ <reg32 offset="0x24403" name="GPU_CC_GX_GDSCR"/>
+ <reg32 offset="0x24542" name="GPU_CC_GX_DOMAIN_MISC"/>
+ <reg32 offset="0x26801" name="GPU_CPR_FSM_CTL"/>
<!-- starts at offset 0x8c00 on most gpus -->
<reg32 offset="0x0004" name="GPU_RSCC_RSC_STATUS0_DRV0"/>
@@ -233,12 +256,12 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<reg32 offset="0x03ee" name="RSCC_TCS1_DRV0_STATUS"/>
<reg32 offset="0x0496" name="RSCC_TCS2_DRV0_STATUS"/>
<reg32 offset="0x053e" name="RSCC_TCS3_DRV0_STATUS"/>
- <reg32 offset="0x05e6" name="RSCC_TCS4_DRV0_STATUS" variants="A7XX"/>
- <reg32 offset="0x068e" name="RSCC_TCS5_DRV0_STATUS" variants="A7XX"/>
- <reg32 offset="0x0736" name="RSCC_TCS6_DRV0_STATUS" variants="A7XX"/>
- <reg32 offset="0x07de" name="RSCC_TCS7_DRV0_STATUS" variants="A7XX"/>
- <reg32 offset="0x0886" name="RSCC_TCS8_DRV0_STATUS" variants="A7XX"/>
- <reg32 offset="0x092e" name="RSCC_TCS9_DRV0_STATUS" variants="A7XX"/>
+ <reg32 offset="0x05e6" name="RSCC_TCS4_DRV0_STATUS" variants="A7XX-"/>
+ <reg32 offset="0x068e" name="RSCC_TCS5_DRV0_STATUS" variants="A7XX-"/>
+ <reg32 offset="0x0736" name="RSCC_TCS6_DRV0_STATUS" variants="A7XX-"/>
+ <reg32 offset="0x07de" name="RSCC_TCS7_DRV0_STATUS" variants="A7XX-"/>
+ <reg32 offset="0x0886" name="RSCC_TCS8_DRV0_STATUS" variants="A7XX-"/>
+ <reg32 offset="0x092e" name="RSCC_TCS9_DRV0_STATUS" variants="A7XX-"/>
</domain>
</database>
diff --git a/drivers/gpu/drm/msm/registers/adreno/a7xx_enums.xml b/drivers/gpu/drm/msm/registers/adreno/a7xx_enums.xml
index 661b0dd0f675..8d195ee5d284 100644
--- a/drivers/gpu/drm/msm/registers/adreno/a7xx_enums.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/a7xx_enums.xml
@@ -93,13 +93,6 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value value="4" name="A7XX_HLSQ_DP_STR"/>
</enum>
-<enum name="a7xx_pipe">
- <value value="0" name="A7XX_PIPE_NONE"/>
- <value value="1" name="A7XX_PIPE_BR"/>
- <value value="2" name="A7XX_PIPE_BV"/>
- <value value="3" name="A7XX_PIPE_LPAC"/>
-</enum>
-
<enum name="a7xx_cluster">
<value value="0" name="A7XX_CLUSTER_NONE"/>
<value value="1" name="A7XX_CLUSTER_FE"/>
diff --git a/drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml b/drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml
new file mode 100644
index 000000000000..edcbdb3b6921
--- /dev/null
+++ b/drivers/gpu/drm/msm/registers/adreno/a8xx_descriptors.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<database xmlns="http://nouveau.freedesktop.org/"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
+<import file="freedreno_copyright.xml"/>
+<import file="adreno/adreno_common.xml"/>
+<import file="adreno/adreno_pm4.xml"/>
+<import file="adreno/a6xx_enums.xml"/>
+<import file="adreno/a8xx_enums.xml"/>
+
+<domain name="A8XX_TEX_SAMP" width="32">
+ <doc>Texture sampler dwords</doc>
+ <reg32 offset="0" name="0">
+ <bitfield name="MIPFILTER_LINEAR_NEAR" pos="0" type="boolean"/>
+ <bitfield name="MIPMAPING_DIS" pos="1" type="boolean"/>
+ <bitfield name="XY_MAG" low="2" high="3" type="a6xx_tex_filter"/>
+ <bitfield name="XY_MIN" low="4" high="5" type="a6xx_tex_filter"/>
+ <bitfield name="WRAP_S" low="6" high="8" type="a6xx_tex_clamp"/>
+ <bitfield name="WRAP_T" low="9" high="11" type="a6xx_tex_clamp"/>
+ <bitfield name="WRAP_R" low="12" high="14" type="a6xx_tex_clamp"/>
+ <bitfield name="MSAA_BOX_FILTERING" pos="15" type="boolean"/>
+ <bitfield name="LOD_BIAS" low="16" high="28" type="fixed" radix="8"/>
+ <bitfield name="ANISO" low="29" high="31" type="a6xx_tex_aniso"/>
+ </reg32>
+ <reg32 offset="1" name="1">
+ <bitfield name="MAX_LOD" low="0" high="11" type="ufixed" radix="8"/>
+ <bitfield name="MIN_LOD" low="12" high="23" type="ufixed" radix="8"/>
+ <bitfield name="REDUCTION_MODE" low="24" high="25" type="a6xx_reduction_mode"/>
+ <bitfield name="COMPARE_FUNC" low="26" high="28" type="adreno_compare_func"/>
+ <bitfield name="CHROMA_LINEAR" pos="29" type="boolean"/>
+ <bitfield name="CUBEMAPSEAMLESSFILTOFF" pos="30" type="boolean"/>
+ <bitfield name="UNNORM_COORDS" pos="31" type="boolean"/>
+ </reg32>
+ <reg32 offset="2" name="2">
+ <bitfield name="FASTBORDERCOLOREN" pos="0" type="boolean"/>
+ <bitfield name="FASTBORDERCOLOR" low="1" high="2" type="a6xx_fast_border_color"/>
+ <bitfield name="BCOLOR" low="7" high="31"/>
+ </reg32>
+ <reg32 offset="3" name="3"/>
+</domain>
+
+<domain name="A8XX_TEX_MEMOBJ" width="32" varset="chip">
+ <doc>Texture memobj dwords</doc>
+ <reg32 offset="0" name="0">
+ <bitfield name="BASE_LO" low="6" high="31" shr="6"/>
+ </reg32>
+ <reg32 offset="1" name="1">
+ <bitfield name="BASE_HI" low="0" high="16"/>
+ <bitfield name="TYPE" low="17" high="19" type="a6xx_tex_type"/>
+ <bitfield name="DEPTH" low="20" high="31" type="uint"/>
+ </reg32>
+ <reg32 offset="2" name="2">
+ <bitfield name="WIDTH" low="0" high="14" type="uint"/>
+ <bitfield name="HEIGHT" low="15" high="29" type="uint"/>
+ <bitfield name="SAMPLES" low="30" high="31" type="a3xx_msaa_samples"/>
+ </reg32>
+ <reg32 offset="3" name="3">
+ <bitfield name="FMT" low="0" high="7" type="a6xx_format"/>
+ <bitfield name="SWAP" low="8" high="9" type="a3xx_color_swap"/>
+ <bitfield name="SWIZ_X" low="10" high="12" type="a8xx_tex_swiz"/>
+ <bitfield name="SWIZ_Y" low="13" high="15" type="a8xx_tex_swiz"/>
+ <bitfield name="SWIZ_Z" low="16" high="18" type="a8xx_tex_swiz"/>
+ <bitfield name="SWIZ_W" low="19" high="21" type="a8xx_tex_swiz"/>
+ </reg32>
+ <reg32 offset="4" name="4">
+ <bitfield name="TILE_MODE" low="0" high="1" type="a6xx_tile_mode"/>
+ <bitfield name="FLAG" pos="2" type="boolean"/>
+ <bitfield name="PRT_EN" pos="3" type="boolean"/>
+ <bitfield name="TILE_ALL" pos="4" type="boolean"/>
+ <bitfield name="SRGB" pos="5" type="boolean"/>
+ <bitfield name="FLAG_LO" low="6" high="31" shr="6"/>
+ <!-- For multiplanar: -->
+ <bitfield name="BASE_U_LO" low="6" high="31" shr="6"/>
+ </reg32>
+ <reg32 offset="5" name="5">
+ <bitfield name="FLAG_HI" low="0" high="16"/>
+ <!-- For multiplanar: -->
+ <bitfield name="BASE_U_HI" low="0" high="16"/>
+ <bitfield name="FLAG_BUFFER_PITCH" low="17" high="24" shr="6" type="uint"/>
+ <bitfield name="ALL_SAMPLES_CENTER" pos="29" type="boolean"/>
+ <bitfield name="MUTABLEEN" pos="31" type="boolean"/>
+ </reg32>
+ <reg32 offset="6" name="6">
+ <bitfield name="TEX_LINE_OFFSET" low="0" high="23" type="uint"/> <!-- PITCH -->
+ <bitfield name="MIN_LINE_OFFSET" low="24" high="27" type="uint"/> <!-- PITCHALIGN -->
+ <bitfield name="MIPLVLS" low="28" high="31" type="uint"/>
+ </reg32>
+ <reg32 offset="7" name="7">
+ <bitfield name="ARRAY_SLICE_OFFSET" low="0" high="22" shr="12" type="uint"/> <!-- ARRAY_PITCH -->
+ <bitfield name="ASO_UNIT" pos="23"/> <!-- 4KB or 32B ? -->
+ <bitfield name="MIN_ARRAY_SLIZE_OFFSET" low="24" high="27" shr="12"/> <!-- MIN_LAYERSZ -->
+ <bitfield name="GMEM_TILING_FALLBACK_EN" pos="28" type="boolean"/>
+ <bitfield name="CORNER_BASED_EN" pos="30" type="boolean"/>
+ <bitfield name="GMEM_FULL_SURF" pos="31" type="boolean"/>
+ <!-- For multiplanar. This overlaps other single-planar fields: -->
+ <bitfield name="UV_OFFSET_H" low="24" high="25" type="ufixed" radix="2"/> <!-- CHROMA_MIDPOINT_X -->
+ <bitfield name="UV_OFFSET_V" low="26" high="27" type="ufixed" radix="2"/> <!-- CHROMA_MIDPOINT_Y -->
+ </reg32>
+ <reg32 offset="8" name="8">
+ <bitfield name="FLAG_ARRAY_PITCH" low="0" high="14" shr="12" type="uint"/> <!-- FLAG_BUFFER_ARRAY_PITCH -->
+ <!-- log2 size of the first level, required for mipmapping -->
+ <bitfield name="FLAG_BUFFER_LOGW" low="24" high="27" type="uint"/>
+ <bitfield name="FLAG_BUFFER_LOGH" low="28" high="31" type="uint"/>
+ <!-- For multiplanar. This overlaps other single-planar fields: -->
+ <bitfield name="BASE_V_LO" low="6" high="31" shr="6"/>
+ </reg32>
+ <reg32 offset="9" name="9">
+ <bitfield name="MIN_LOD_CLAMP" low="19" high="30" type="ufixed" radix="8"/>
+ <!-- For multiplanar, this overlaps other fields: -->
+ <bitfield name="BASE_V_HI" low="0" high="16"/>
+ <bitfield name="UV_PITCH" low="17" high="26"/> <!-- PLANE_PITCH -->
+ </reg32>
+ <reg32 offset="10" name="10"/>
+ <reg32 offset="11" name="11"/>
+ <reg32 offset="12" name="12"/>
+ <reg32 offset="13" name="13"/>
+ <reg32 offset="14" name="14"/>
+ <reg32 offset="15" name="15"/>
+</domain>
+
+</database>
diff --git a/drivers/gpu/drm/msm/registers/adreno/a8xx_enums.xml b/drivers/gpu/drm/msm/registers/adreno/a8xx_enums.xml
new file mode 100644
index 000000000000..c842db8c78d6
--- /dev/null
+++ b/drivers/gpu/drm/msm/registers/adreno/a8xx_enums.xml
@@ -0,0 +1,299 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<database xmlns="http://nouveau.freedesktop.org/"
+xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
+<import file="freedreno_copyright.xml"/>
+<import file="adreno/adreno_common.xml"/>
+<import file="adreno/adreno_pm4.xml"/>
+
+<enum name="a8xx_statetype_id">
+ <value value="0" name="A8XX_TP0_NCTX_REG"/>
+ <value value="1" name="A8XX_TP0_CTX0_3D_CVS_REG"/>
+ <value value="2" name="A8XX_TP0_CTX0_3D_CPS_REG"/>
+ <value value="3" name="A8XX_TP0_CTX1_3D_CVS_REG"/>
+ <value value="4" name="A8XX_TP0_CTX1_3D_CPS_REG"/>
+ <value value="5" name="A8XX_TP0_CTX2_3D_CPS_REG"/>
+ <value value="6" name="A8XX_TP0_CTX3_3D_CPS_REG"/>
+ <value value="9" name="A8XX_TP0_TMO_DATA"/>
+ <value value="10" name="A8XX_TP0_SMO_DATA"/>
+ <value value="11" name="A8XX_TP0_MIPMAP_BASE_DATA"/>
+ <value value="12" name="A8XX_TP_3D_CVS_REG"/>
+ <value value="13" name="A8XX_TP_3D_CPS_REG"/>
+ <value value="16" name="A8XX_SP_3D_CVS_REG"/>
+ <value value="17" name="A8XX_SP_3D_CPS_REG"/>
+ <value value="22" name="A8XX_SP_LB_DATA_RAM"/>
+ <value value="23" name="A8XX_SP_INST_DATA_RAM"/>
+ <value value="24" name="A8XX_SP_STH"/>
+ <value value="25" name="A8XX_SP_EVQ"/>
+ <value value="26" name="A8XX_SP_CONSMNG"/>
+ <value value="30" name="A8XX_HLSQ_INST_DATA_RAM"/>
+ <value value="31" name="A8XX_SP_INST_DATA_3"/>
+ <value value="32" name="A8XX_SP_NCTX_REG"/>
+ <value value="33" name="A8XX_SP_CTX0_3D_CVS_REG"/>
+ <value value="34" name="A8XX_SP_CTX0_3D_CPS_REG"/>
+ <value value="35" name="A8XX_SP_CTX1_3D_CVS_REG"/>
+ <value value="36" name="A8XX_SP_CTX1_3D_CPS_REG"/>
+ <value value="37" name="A8XX_SP_CTX2_3D_CPS_REG"/>
+ <value value="38" name="A8XX_SP_CTX3_3D_CPS_REG"/>
+ <value value="39" name="A8XX_SP_INST_DATA"/>
+ <value value="40" name="A8XX_SP_INST_DATA_1"/>
+ <value value="41" name="A8XX_SP_LB_0_DATA"/>
+ <value value="42" name="A8XX_SP_LB_1_DATA"/>
+ <value value="43" name="A8XX_SP_LB_2_DATA"/>
+ <value value="44" name="A8XX_SP_LB_3_DATA"/>
+ <value value="45" name="A8XX_SP_LB_4_DATA"/>
+ <value value="46" name="A8XX_SP_LB_5_DATA"/>
+ <value value="47" name="A8XX_SP_LB_6_DATA"/>
+ <value value="48" name="A8XX_SP_LB_7_DATA"/>
+ <value value="49" name="A8XX_SP_CB_RAM"/>
+ <value value="50" name="A8XX_SP_LB_13_DATA"/>
+ <value value="51" name="A8XX_SP_LB_14_DATA"/>
+ <value value="52" name="A8XX_SP_INST_TAG"/>
+ <value value="53" name="A8XX_SP_INST_DATA_2"/>
+ <value value="54" name="A8XX_SP_TMO_TAG"/>
+ <value value="55" name="A8XX_SP_SMO_TAG"/>
+ <value value="56" name="A8XX_SP_STATE_DATA"/>
+ <value value="57" name="A8XX_SP_HWAVE_RAM"/>
+ <value value="58" name="A8XX_SP_L0_INST_BUF"/>
+ <value value="59" name="A8XX_SP_LB_8_DATA"/>
+ <value value="60" name="A8XX_SP_LB_9_DATA"/>
+ <value value="61" name="A8XX_SP_LB_10_DATA"/>
+ <value value="62" name="A8XX_SP_LB_11_DATA"/>
+ <value value="63" name="A8XX_SP_LB_12_DATA"/>
+ <value value="64" name="A8XX_HLSQ_DATAPATH_DSTR_META"/>
+ <value value="65" name="A8XX_HLSQ_DESC_REMAP_META"/>
+ <value value="66" name="A8XX_HLSQ_SLICE_TOP_META"/>
+ <value value="67" name="A8XX_HLSQ_L2STC_TAG_RAM"/>
+ <value value="68" name="A8XX_HLSQ_L2STC_INFO_CMD"/>
+ <value value="69" name="A8XX_HLSQ_CVS_BE_CTXT_BUF_RAM_TAG"/>
+ <value value="70" name="A8XX_HLSQ_CPS_BE_CTXT_BUF_RAM_TAG"/>
+ <value value="71" name="A8XX_HLSQ_GFX_CVS_BE_CTXT_BUF_RAM"/>
+ <value value="72" name="A8XX_HLSQ_GFX_CPS_BE_CTXT_BUF_RAM"/>
+ <value value="73" name="A8XX_HLSQ_CHUNK_CVS_RAM"/>
+ <value value="74" name="A8XX_HLSQ_CHUNK_CPS_RAM"/>
+ <value value="75" name="A8XX_HLSQ_CHUNK_CVS_RAM_TAG"/>
+ <value value="76" name="A8XX_HLSQ_CHUNK_CPS_RAM_TAG"/>
+ <value value="77" name="A8XX_HLSQ_ICB_CVS_CB_BASE_TAG"/>
+ <value value="78" name="A8XX_HLSQ_ICB_CPS_CB_BASE_TAG"/>
+ <value value="79" name="A8XX_HLSQ_CVS_MISC_RAM"/>
+ <value value="80" name="A8XX_HLSQ_CPS_MISC_RAM"/>
+ <value value="81" name="A8XX_HLSQ_CPS_MISC_RAM_1"/>
+ <value value="82" name="A8XX_HLSQ_INST_RAM"/>
+ <value value="83" name="A8XX_HLSQ_GFX_CVS_CONST_RAM"/>
+ <value value="84" name="A8XX_HLSQ_GFX_CPS_CONST_RAM"/>
+ <value value="85" name="A8XX_HLSQ_CVS_MISC_RAM_TAG"/>
+ <value value="86" name="A8XX_HLSQ_CPS_MISC_RAM_TAG"/>
+ <value value="87" name="A8XX_HLSQ_INST_RAM_TAG"/>
+ <value value="88" name="A8XX_HLSQ_GFX_CVS_CONST_RAM_TAG"/>
+ <value value="89" name="A8XX_HLSQ_GFX_CPS_CONST_RAM_TAG"/>
+ <value value="90" name="A8XX_HLSQ_GFX_LOCAL_MISC_RAM"/>
+ <value value="91" name="A8XX_HLSQ_GFX_LOCAL_MISC_RAM_TAG"/>
+ <value value="92" name="A8XX_HLSQ_INST_RAM_1"/>
+ <value value="93" name="A8XX_HLSQ_STPROC_META"/>
+ <value value="94" name="A8XX_HLSQ_SLICE_BACKEND_META"/>
+ <value value="95" name="A8XX_HLSQ_INST_RAM_2"/>
+ <value value="96" name="A8XX_HLSQ_DATAPATH_META"/>
+ <value value="97" name="A8XX_HLSQ_FRONTEND_META"/>
+ <value value="98" name="A8XX_HLSQ_INDIRECT_META"/>
+ <value value="99" name="A8XX_HLSQ_BACKEND_META"/>
+</enum>
+
+<enum name="a8xx_state_location">
+ <value value="0" name="A8XX_HLSQ_STATE"/>
+ <value value="1" name="A8XX_HLSQ_DP"/>
+ <value value="2" name="A8XX_SP_TOP"/>
+ <value value="3" name="A8XX_USPTP"/>
+ <value value="4" name="A8XX_HLSQ_DP_STR"/>
+</enum>
+
+<enum name="a8xx_cluster">
+ <value value="0" name="A8XX_CLUSTER_NONE"/>
+ <value value="1" name="A8XX_CLUSTER_FE_US"/>
+ <value value="2" name="A8XX_CLUSTER_FE_S"/>
+ <value value="3" name="A8XX_CLUSTER_SP_VS"/>
+ <value value="4" name="A8XX_CLUSTER_VPC_VS"/>
+ <value value="5" name="A8XX_CLUSTER_VPC_US"/>
+ <value value="6" name="A8XX_CLUSTER_GRAS"/>
+ <value value="7" name="A8XX_CLUSTER_SP_PS"/>
+ <value value="8" name="A8XX_CLUSTER_VPC_PS"/>
+ <value value="9" name="A8XX_CLUSTER_PS"/>
+</enum>
+
+<enum name="a8xx_debugbus_id">
+ <value value="1" name="A8XX_DEBUGBUS_GBIF_CX_GC_US_I_0"/>
+ <value value="2" name="A8XX_DEBUGBUS_GMU_CX_GC_US_I_0"/>
+ <value value="3" name="A8XX_DEBUGBUS_CX_GC_US_I_0"/>
+ <value value="8" name="A8XX_DEBUGBUS_GBIF_GX_GC_US_I_0"/>
+ <value value="9" name="A8XX_DEBUGBUS_GMU_GX_GC_US_I_0"/>
+ <value value="10" name="A8XX_DEBUGBUS_DBGC_GC_US_I_0"/>
+ <value value="11" name="A8XX_DEBUGBUS_RBBM_GC_US_I_0"/>
+ <value value="12" name="A8XX_DEBUGBUS_LARC_GC_US_I_0"/>
+ <value value="13" name="A8XX_DEBUGBUS_COM_GC_US_I_0"/>
+ <value value="14" name="A8XX_DEBUGBUS_HLSQ_GC_US_I_0"/>
+ <value value="15" name="A8XX_DEBUGBUS_CGC_GC_US_I_0"/>
+ <value value="20" name="A8XX_DEBUGBUS_VSC_GC_US_I_0_0"/>
+ <value value="21" name="A8XX_DEBUGBUS_VSC_GC_US_I_0_1"/>
+ <value value="24" name="A8XX_DEBUGBUS_UFC_GC_US_I_0"/>
+ <value value="25" name="A8XX_DEBUGBUS_UFC_GC_US_I_1"/>
+ <value value="40" name="A8XX_DEBUGBUS_CP_GC_US_I_0_0"/>
+ <value value="41" name="A8XX_DEBUGBUS_CP_GC_US_I_0_1"/>
+ <value value="42" name="A8XX_DEBUGBUS_CP_GC_US_I_0_2"/>
+ <value value="56" name="A8XX_DEBUGBUS_PC_BR_US_I_0"/>
+ <value value="57" name="A8XX_DEBUGBUS_PC_BV_US_I_0"/>
+ <value value="58" name="A8XX_DEBUGBUS_GPC_BR_US_I_0"/>
+ <value value="59" name="A8XX_DEBUGBUS_GPC_BV_US_I_0"/>
+ <value value="60" name="A8XX_DEBUGBUS_VPC_BR_US_I_0"/>
+ <value value="61" name="A8XX_DEBUGBUS_VPC_BV_US_I_0"/>
+ <value value="80" name="A8XX_DEBUGBUS_UCHE_WRAPPER_GC_US_I_0"/>
+ <value value="81" name="A8XX_DEBUGBUS_UCHE_GC_US_I_0"/>
+ <value value="82" name="A8XX_DEBUGBUS_UCHE_GC_US_I_1"/>
+ <value value="83" name="A8XX_DEBUGBUS_UCHE_GC_US_I_0_1"/>
+ <value value="84" name="A8XX_DEBUGBUS_UCHE_GC_US_I_1_1"/>
+ <value value="128" name="A8XX_DEBUGBUS_CP_GC_S_0_I_0"/>
+ <value value="129" name="A8XX_DEBUGBUS_PC_BR_S_0_I_0"/>
+ <value value="130" name="A8XX_DEBUGBUS_PC_BV_S_0_I_0"/>
+ <value value="131" name="A8XX_DEBUGBUS_TESS_GC_S_0_I_0"/>
+ <value value="132" name="A8XX_DEBUGBUS_TSEFE_GC_S_0_I_0"/>
+ <value value="133" name="A8XX_DEBUGBUS_TSEBE_GC_S_0_I_0"/>
+ <value value="134" name="A8XX_DEBUGBUS_RAS_GC_S_0_I_0"/>
+ <value value="135" name="A8XX_DEBUGBUS_LRZ_BR_S_0_I_0"/>
+ <value value="136" name="A8XX_DEBUGBUS_LRZ_BV_S_0_I_0"/>
+ <value value="137" name="A8XX_DEBUGBUS_VFDP_GC_S_0_I_0"/>
+ <value value="138" name="A8XX_DEBUGBUS_GPC_BR_S_0_I_0"/>
+ <value value="139" name="A8XX_DEBUGBUS_GPC_BV_S_0_I_0"/>
+ <value value="140" name="A8XX_DEBUGBUS_VPCFE_BR_S_0_I_0"/>
+ <value value="141" name="A8XX_DEBUGBUS_VPCFE_BV_S_0_I_0"/>
+ <value value="142" name="A8XX_DEBUGBUS_VPCBE_BR_S_0_I_0"/>
+ <value value="143" name="A8XX_DEBUGBUS_VPCBE_BV_S_0_I_0"/>
+ <value value="144" name="A8XX_DEBUGBUS_CCHE_GC_S_0_I_0"/>
+ <value value="145" name="A8XX_DEBUGBUS_DBGC_GC_S_0_I_0"/>
+ <value value="146" name="A8XX_DEBUGBUS_LARC_GC_S_0_I_0"/>
+ <value value="147" name="A8XX_DEBUGBUS_RBBM_GC_S_0_I_0"/>
+ <value value="148" name="A8XX_DEBUGBUS_CCRE_GC_S_0_I_0"/>
+ <value value="149" name="A8XX_DEBUGBUS_CGC_GC_S_0_I_0"/>
+ <value value="150" name="A8XX_DEBUGBUS_GMU_GC_S_0_I_0"/>
+ <value value="151" name="A8XX_DEBUGBUS_SLICE_GC_S_0_I_0"/>
+ <value value="152" name="A8XX_DEBUGBUS_HLSQ_SPTP_STAR_GC_S_0_I_0"/>
+ <value value="160" name="A8XX_DEBUGBUS_USP_GC_S_0_I_0"/>
+ <value value="161" name="A8XX_DEBUGBUS_USP_GC_S_0_I_1"/>
+ <value value="166" name="A8XX_DEBUGBUS_USPTP_GC_S_0_I_0"/>
+ <value value="167" name="A8XX_DEBUGBUS_USPTP_GC_S_0_I_1"/>
+ <value value="168" name="A8XX_DEBUGBUS_USPTP_GC_S_0_I_2"/>
+ <value value="169" name="A8XX_DEBUGBUS_USPTP_GC_S_0_I_3"/>
+ <value value="178" name="A8XX_DEBUGBUS_TP_GC_S_0_I_0"/>
+ <value value="179" name="A8XX_DEBUGBUS_TP_GC_S_0_I_1"/>
+ <value value="180" name="A8XX_DEBUGBUS_TP_GC_S_0_I_2"/>
+ <value value="181" name="A8XX_DEBUGBUS_TP_GC_S_0_I_3"/>
+ <value value="190" name="A8XX_DEBUGBUS_RB_GC_S_0_I_0"/>
+ <value value="191" name="A8XX_DEBUGBUS_RB_GC_S_0_I_1"/>
+ <value value="196" name="A8XX_DEBUGBUS_CCU_GC_S_0_I_0"/>
+ <value value="197" name="A8XX_DEBUGBUS_CCU_GC_S_0_I_1"/>
+ <value value="202" name="A8XX_DEBUGBUS_HLSQ_GC_S_0_I_0"/>
+ <value value="203" name="A8XX_DEBUGBUS_HLSQ_GC_S_0_I_1"/>
+ <value value="208" name="A8XX_DEBUGBUS_VFD_GC_S_0_I_0"/>
+ <value value="209" name="A8XX_DEBUGBUS_VFD_GC_S_0_I_1"/>
+ <value value="256" name="A8XX_DEBUGBUS_CP_GC_S_1_I_0"/>
+ <value value="257" name="A8XX_DEBUGBUS_PC_BR_S_1_I_0"/>
+ <value value="258" name="A8XX_DEBUGBUS_PC_BV_S_1_I_0"/>
+ <value value="259" name="A8XX_DEBUGBUS_TESS_GC_S_1_I_0"/>
+ <value value="260" name="A8XX_DEBUGBUS_TSEFE_GC_S_1_I_0"/>
+ <value value="261" name="A8XX_DEBUGBUS_TSEBE_GC_S_1_I_0"/>
+ <value value="262" name="A8XX_DEBUGBUS_RAS_GC_S_1_I_0"/>
+ <value value="263" name="A8XX_DEBUGBUS_LRZ_BR_S_1_I_0"/>
+ <value value="264" name="A8XX_DEBUGBUS_LRZ_BV_S_1_I_0"/>
+ <value value="265" name="A8XX_DEBUGBUS_VFDP_GC_S_1_I_0"/>
+ <value value="266" name="A8XX_DEBUGBUS_GPC_BR_S_1_I_0"/>
+ <value value="267" name="A8XX_DEBUGBUS_GPC_BV_S_1_I_0"/>
+ <value value="268" name="A8XX_DEBUGBUS_VPCFE_BR_S_1_I_0"/>
+ <value value="269" name="A8XX_DEBUGBUS_VPCFE_BV_S_1_I_0"/>
+ <value value="270" name="A8XX_DEBUGBUS_VPCBE_BR_S_1_I_0"/>
+ <value value="271" name="A8XX_DEBUGBUS_VPCBE_BV_S_1_I_0"/>
+ <value value="272" name="A8XX_DEBUGBUS_CCHE_GC_S_1_I_0"/>
+ <value value="273" name="A8XX_DEBUGBUS_DBGC_GC_S_1_I_0"/>
+ <value value="274" name="A8XX_DEBUGBUS_LARC_GC_S_1_I_0"/>
+ <value value="275" name="A8XX_DEBUGBUS_RBBM_GC_S_1_I_0"/>
+ <value value="276" name="A8XX_DEBUGBUS_CCRE_GC_S_1_I_0"/>
+ <value value="277" name="A8XX_DEBUGBUS_CGC_GC_S_1_I_0"/>
+ <value value="278" name="A8XX_DEBUGBUS_GMU_GC_S_1_I_0"/>
+ <value value="279" name="A8XX_DEBUGBUS_SLICE_GC_S_1_I_0"/>
+ <value value="280" name="A8XX_DEBUGBUS_HLSQ_SPTP_STAR_GC_S_1_I_0"/>
+ <value value="288" name="A8XX_DEBUGBUS_USP_GC_S_1_I_0"/>
+ <value value="289" name="A8XX_DEBUGBUS_USP_GC_S_1_I_1"/>
+ <value value="294" name="A8XX_DEBUGBUS_USPTP_GC_S_1_I_0"/>
+ <value value="295" name="A8XX_DEBUGBUS_USPTP_GC_S_1_I_1"/>
+ <value value="296" name="A8XX_DEBUGBUS_USPTP_GC_S_1_I_2"/>
+ <value value="297" name="A8XX_DEBUGBUS_USPTP_GC_S_1_I_3"/>
+ <value value="306" name="A8XX_DEBUGBUS_TP_GC_S_1_I_0"/>
+ <value value="307" name="A8XX_DEBUGBUS_TP_GC_S_1_I_1"/>
+ <value value="308" name="A8XX_DEBUGBUS_TP_GC_S_1_I_2"/>
+ <value value="309" name="A8XX_DEBUGBUS_TP_GC_S_1_I_3"/>
+ <value value="318" name="A8XX_DEBUGBUS_RB_GC_S_1_I_0"/>
+ <value value="319" name="A8XX_DEBUGBUS_RB_GC_S_1_I_1"/>
+ <value value="324" name="A8XX_DEBUGBUS_CCU_GC_S_1_I_0"/>
+ <value value="325" name="A8XX_DEBUGBUS_CCU_GC_S_1_I_1"/>
+ <value value="330" name="A8XX_DEBUGBUS_HLSQ_GC_S_1_I_0"/>
+ <value value="331" name="A8XX_DEBUGBUS_HLSQ_GC_S_1_I_1"/>
+ <value value="336" name="A8XX_DEBUGBUS_VFD_GC_S_1_I_0"/>
+ <value value="337" name="A8XX_DEBUGBUS_VFD_GC_S_1_I_1"/>
+ <value value="384" name="A8XX_DEBUGBUS_CP_GC_S_2_I_0"/>
+ <value value="385" name="A8XX_DEBUGBUS_PC_BR_S_2_I_0"/>
+ <value value="386" name="A8XX_DEBUGBUS_PC_BV_S_2_I_0"/>
+ <value value="387" name="A8XX_DEBUGBUS_TESS_GC_S_2_I_0"/>
+ <value value="388" name="A8XX_DEBUGBUS_TSEFE_GC_S_2_I_0"/>
+ <value value="389" name="A8XX_DEBUGBUS_TSEBE_GC_S_2_I_0"/>
+ <value value="390" name="A8XX_DEBUGBUS_RAS_GC_S_2_I_0"/>
+ <value value="391" name="A8XX_DEBUGBUS_LRZ_BR_S_2_I_0"/>
+ <value value="392" name="A8XX_DEBUGBUS_LRZ_BV_S_2_I_0"/>
+ <value value="393" name="A8XX_DEBUGBUS_VFDP_GC_S_2_I_0"/>
+ <value value="394" name="A8XX_DEBUGBUS_GPC_BR_S_2_I_0"/>
+ <value value="395" name="A8XX_DEBUGBUS_GPC_BV_S_2_I_0"/>
+ <value value="396" name="A8XX_DEBUGBUS_VPCFE_BR_S_2_I_0"/>
+ <value value="397" name="A8XX_DEBUGBUS_VPCFE_BV_S_2_I_0"/>
+ <value value="398" name="A8XX_DEBUGBUS_VPCBE_BR_S_2_I_0"/>
+ <value value="399" name="A8XX_DEBUGBUS_VPCBE_BV_S_2_I_0"/>
+ <value value="400" name="A8XX_DEBUGBUS_CCHE_GC_S_2_I_0"/>
+ <value value="401" name="A8XX_DEBUGBUS_DBGC_GC_S_2_I_0"/>
+ <value value="402" name="A8XX_DEBUGBUS_LARC_GC_S_2_I_0"/>
+ <value value="403" name="A8XX_DEBUGBUS_RBBM_GC_S_2_I_0"/>
+ <value value="404" name="A8XX_DEBUGBUS_CCRE_GC_S_2_I_0"/>
+ <value value="405" name="A8XX_DEBUGBUS_CGC_GC_S_2_I_0"/>
+ <value value="406" name="A8XX_DEBUGBUS_GMU_GC_S_2_I_0"/>
+ <value value="407" name="A8XX_DEBUGBUS_SLICE_GC_S_2_I_0"/>
+ <value value="408" name="A8XX_DEBUGBUS_HLSQ_SPTP_STAR_GC_S_2_I_0"/>
+ <value value="416" name="A8XX_DEBUGBUS_USP_GC_S_2_I_0"/>
+ <value value="417" name="A8XX_DEBUGBUS_USP_GC_S_2_I_1"/>
+ <value value="422" name="A8XX_DEBUGBUS_USPTP_GC_S_2_I_0"/>
+ <value value="423" name="A8XX_DEBUGBUS_USPTP_GC_S_2_I_1"/>
+ <value value="424" name="A8XX_DEBUGBUS_USPTP_GC_S_2_I_2"/>
+ <value value="425" name="A8XX_DEBUGBUS_USPTP_GC_S_2_I_3"/>
+ <value value="434" name="A8XX_DEBUGBUS_TP_GC_S_2_I_0"/>
+ <value value="435" name="A8XX_DEBUGBUS_TP_GC_S_2_I_1"/>
+ <value value="436" name="A8XX_DEBUGBUS_TP_GC_S_2_I_2"/>
+ <value value="437" name="A8XX_DEBUGBUS_TP_GC_S_2_I_3"/>
+ <value value="446" name="A8XX_DEBUGBUS_RB_GC_S_2_I_0"/>
+ <value value="447" name="A8XX_DEBUGBUS_RB_GC_S_2_I_1"/>
+ <value value="452" name="A8XX_DEBUGBUS_CCU_GC_S_2_I_0"/>
+ <value value="453" name="A8XX_DEBUGBUS_CCU_GC_S_2_I_1"/>
+ <value value="458" name="A8XX_DEBUGBUS_HLSQ_GC_S_2_I_0"/>
+ <value value="459" name="A8XX_DEBUGBUS_HLSQ_GC_S_2_I_1"/>
+ <value value="464" name="A8XX_DEBUGBUS_VFD_GC_S_2_I_0"/>
+ <value value="465" name="A8XX_DEBUGBUS_VFD_GC_S_2_I_1"/>
+</enum>
+
+<enum name="a8xx_usptp_id">
+ <value value="0" name="A8XX_uSPTP0"/>
+ <value value="1" name="A8XX_uSPTP1"/>
+ <value value="15" name="A8XX_SPTOP"/>
+</enum>
+
+<enum name="a8xx_tex_swiz">
+ <value name="A8XX_SWIZ_IDENTITY" value="0"/>
+ <value name="A8XX_SWIZ_ZERO" value="1"/>
+ <value name="A8XX_SWIZ_ONE" value="2"/>
+ <value name="A8XX_SWIZ_X" value="3"/>
+ <value name="A8XX_SWIZ_Y" value="4"/>
+ <value name="A8XX_SWIZ_Z" value="5"/>
+ <value name="A8XX_SWIZ_W" value="6"/>
+</enum>
+
+</database>
diff --git a/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml b/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml
index 218ec8bb966e..79d204f1e400 100644
--- a/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/adreno_common.xml
@@ -11,6 +11,7 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="A5XX" value="5"/>
<value name="A6XX" value="6"/>
<value name="A7XX" value="7"/>
+ <value name="A8XX" value="8"/>
</enum>
<enum name="adreno_pa_su_sc_draw">
@@ -397,4 +398,15 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value value="0x7" name="TEX_PREFETCH_UNK7"/>
</enum>
+<enum name="adreno_pipe">
+ <value value="0" name="PIPE_NONE"/>
+ <value value="1" name="PIPE_BR"/>
+ <value value="2" name="PIPE_BV"/>
+ <value value="3" name="PIPE_LPAC"/>
+ <value value="4" name="PIPE_AQE0"/>
+ <value value="5" name="PIPE_AQE1"/>
+ <value value="6" name="PIPE_DDE_BR"/>
+ <value value="7" name="PIPE_DDE_BV"/>
+</enum>
+
</database>
diff --git a/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml b/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml
index 0e10e1c6d263..51e9c94f5e37 100644
--- a/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml
+++ b/drivers/gpu/drm/msm/registers/adreno/adreno_pm4.xml
@@ -6,103 +6,102 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<import file="adreno/adreno_common.xml"/>
<enum name="vgt_event_type" varset="chip">
- <value name="VS_DEALLOC" value="0"/>
- <value name="PS_DEALLOC" value="1" variants="A2XX-A6XX"/>
- <value name="VS_DONE_TS" value="2"/>
- <value name="PS_DONE_TS" value="3"/>
+ <value name="VS_DEALLOC" value="0x00" variants="A2XX-A5XX"/>
+ <value name="PS_DEALLOC" value="0x01" variants="A2XX-A5XX"/>
+ <value name="VS_DONE_TS" value="0x02" variants="A2XX-A5XX"/>
+ <value name="PS_DONE_TS" value="0x03" variants="A2XX-A5XX"/>
<doc>
Flushes dirty data from UCHE, and also writes a GPU timestamp to
the address if one is provided.
</doc>
- <value name="CACHE_FLUSH_TS" value="4"/>
- <value name="CONTEXT_DONE" value="5"/>
- <value name="CACHE_FLUSH" value="6" variants="A2XX-A4XX"/>
- <value name="VIZQUERY_START" value="7" variants="A2XX"/>
- <value name="HLSQ_FLUSH" value="7" variants="A3XX-A4XX"/>
- <value name="VIZQUERY_END" value="8" variants="A2XX"/>
- <value name="SC_WAIT_WC" value="9" variants="A2XX"/>
- <value name="WRITE_PRIMITIVE_COUNTS" value="9" variants="A6XX-"/>
- <value name="START_PRIMITIVE_CTRS" value="11" variants="A6XX-"/>
- <value name="STOP_PRIMITIVE_CTRS" value="12" variants="A6XX-"/>
+ <value name="CACHE_FLUSH_TS" value="0x04"/>
+ <value name="CONTEXT_DONE" value="0x05"/>
+ <value name="CACHE_FLUSH" value="0x06" variants="A2XX-A4XX"/>
+ <value name="VIZQUERY_START" value="0x07" variants="A2XX"/>
+ <value name="HLSQ_FLUSH" value="0x07" variants="A3XX-A4XX"/>
+ <value name="VIZQUERY_END" value="0x08" variants="A2XX"/>
+ <value name="SC_WAIT_WC" value="0x09" variants="A2XX"/>
+ <value name="WRITE_PRIMITIVE_COUNTS" value="0x09" variants="A6XX-"/>
+ <value name="START_PRIMITIVE_CTRS" value="0x0b" variants="A6XX-"/>
+ <value name="STOP_PRIMITIVE_CTRS" value="0x0c" variants="A6XX-"/>
<!-- Not sure that these 4 events don't have the same meaning as on A5XX+ -->
- <value name="RST_PIX_CNT" value="13" variants="A2XX-A4XX"/>
- <value name="RST_VTX_CNT" value="14" variants="A2XX-A4XX"/>
- <value name="TILE_FLUSH" value="15" variants="A2XX-A4XX"/>
- <value name="STAT_EVENT" value="16" variants="A2XX-A4XX"/>
- <value name="CACHE_FLUSH_AND_INV_TS_EVENT" value="20" variants="A2XX-A4XX"/>
+ <value name="RST_PIX_CNT" value="0x0d" variants="A2XX-A4XX"/>
+ <value name="RST_VTX_CNT" value="0x0e" variants="A2XX-A4XX"/>
+ <value name="TILE_FLUSH" value="0x0f" variants="A2XX-A4XX"/>
+ <value name="STAT_EVENT" value="0x10" variants="A2XX-A4XX"/>
+ <value name="CACHE_FLUSH_AND_INV_TS_EVENT" value="0x14" variants="A2XX-A4XX"/>
<doc>
If A6XX_RB_SAMPLE_COUNTER_CNTL.copy is true, writes OQ Z passed
sample counts to RB_SAMPLE_COUNTER_BASE. This writes to main
memory, skipping UCHE.
</doc>
- <value name="ZPASS_DONE" value="21"/>
- <value name="CACHE_FLUSH_AND_INV_EVENT" value="22" variants="A2XX"/>
+ <value name="ZPASS_DONE" value="0x15"/>
+ <value name="CACHE_FLUSH_AND_INV_EVENT" value="0x16" variants="A2XX"/>
<doc>
Writes the GPU timestamp to the address that follows, once RB
access and flushes are complete.
</doc>
- <value name="RB_DONE_TS" value="22" variants="A3XX-"/>
+ <value name="RB_DONE_TS" value="0x16" variants="A3XX-"/>
- <value name="PERFCOUNTER_START" value="23" variants="A2XX-A4XX"/>
- <value name="PERFCOUNTER_STOP" value="24" variants="A2XX-A4XX"/>
- <value name="VS_FETCH_DONE" value="27"/>
- <value name="FACENESS_FLUSH" value="28" variants="A2XX-A4XX"/>
+ <value name="PERFCOUNTER_START" value="0x17" variants="A2XX-A4XX"/>
+ <value name="PERFCOUNTER_STOP" value="0x18" variants="A2XX-A4XX"/>
+ <value name="VS_FETCH_DONE" value="0x1b" variants="A2XX-A5XX"/>
+ <value name="FACENESS_FLUSH" value="0x1c" variants="A2XX-A4XX"/>
<!-- a5xx events -->
- <value name="WT_DONE_TS" value="8" variants="A5XX-"/>
- <value name="START_FRAGMENT_CTRS" value="13" variants="A5XX-"/>
- <value name="STOP_FRAGMENT_CTRS" value="14" variants="A5XX-"/>
- <value name="START_COMPUTE_CTRS" value="15" variants="A5XX-"/>
- <value name="STOP_COMPUTE_CTRS" value="16" variants="A5XX-"/>
- <value name="FLUSH_SO_0" value="17" variants="A5XX-"/>
- <value name="FLUSH_SO_1" value="18" variants="A5XX-"/>
- <value name="FLUSH_SO_2" value="19" variants="A5XX-"/>
- <value name="FLUSH_SO_3" value="20" variants="A5XX-"/>
+ <value name="WT_DONE_TS" value="0x08" variants="A5XX-A6XX"/>
+ <value name="START_FRAGMENT_CTRS" value="0x0d" variants="A5XX-"/>
+ <value name="STOP_FRAGMENT_CTRS" value="0x0e" variants="A5XX-"/>
+ <value name="START_COMPUTE_CTRS" value="0x0f" variants="A5XX-"/>
+ <value name="STOP_COMPUTE_CTRS" value="0x10" variants="A5XX-"/>
+ <value name="FLUSH_SO_0" value="0x11" variants="A5XX-"/>
+ <value name="FLUSH_SO_1" value="0x12" variants="A5XX-"/>
+ <value name="FLUSH_SO_2" value="0x13" variants="A5XX-"/>
+ <value name="FLUSH_SO_3" value="0x14" variants="A5XX-"/>
<doc>
Invalidates depth attachment data from the CCU. We assume this
happens in the last stage.
</doc>
- <value name="PC_CCU_INVALIDATE_DEPTH" value="24" variants="A5XX-"/>
+ <value name="PC_CCU_INVALIDATE_DEPTH" value="0x18" variants="A5XX-A6XX"/>
<doc>
Invalidates color attachment data from the CCU. We assume this
happens in the last stage.
</doc>
- <value name="PC_CCU_INVALIDATE_COLOR" value="25" variants="A5XX-"/>
+ <value name="PC_CCU_INVALIDATE_COLOR" value="0x19" variants="A5XX-A6XX"/>
<doc>
Flushes the small cache used by CP_EVENT_WRITE::BLIT (which,
along with its registers, would be better named RESOLVE).
</doc>
- <value name="PC_CCU_RESOLVE_TS" value="26" variants="A6XX"/>
+ <value name="PC_CCU_RESOLVE_TS" value="0x1a" variants="A6XX"/>
<doc>
Flushes depth attachment data from the CCU. We assume this
happens in the last stage.
</doc>
- <value name="PC_CCU_FLUSH_DEPTH_TS" value="28" variants="A5XX-"/>
+ <value name="PC_CCU_FLUSH_DEPTH_TS" value="0x1c" variants="A5XX-A6XX"/>
<doc>
Flushes color attachment data from the CCU. We assume this
happens in the last stage.
</doc>
- <value name="PC_CCU_FLUSH_COLOR_TS" value="29" variants="A5XX-"/>
+ <value name="PC_CCU_FLUSH_COLOR_TS" value="0x1d" variants="A5XX-A6XX"/>
<doc>
- 2D blit to resolve GMEM to system memory (skipping CCU) at the
- end of a render pass. Compare to CP_BLIT's BLIT_OP_SCALE for
- more general blitting.
+ Triggers a resolve (GMEM to sysmem) or unresolve (sysmem to
+ GMEM) or clear blit, depending on CCU programming.
</doc>
- <value name="BLIT" value="30" variants="A5XX-"/>
+ <value name="CCU_RESOLVE" value="0x1e" variants="A5XX-"/>
<doc>
Flip between the primary and secondary LRZ buffers. This is used
for concurrent binning, so that BV can write to one buffer while
BR reads from the other.
</doc>
- <value name="LRZ_FLIP_BUFFER" value="36" variants="A7XX"/>
+ <value name="LRZ_FLIP_BUFFER" value="0x24" variants="A7XX-"/>
<doc>
Clears based on GRAS_LRZ_CNTL configuration, could clear
@@ -115,44 +114,46 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
CUR_DIR_UNSET = 0x3
Clear of direction means setting the direction to CUR_DIR_UNSET.
</doc>
- <value name="LRZ_CLEAR" value="37" variants="A5XX-"/>
-
- <value name="LRZ_FLUSH" value="38" variants="A5XX-"/>
- <value name="BLIT_OP_FILL_2D" value="39" variants="A5XX-"/>
- <value name="BLIT_OP_COPY_2D" value="40" variants="A5XX-A6XX"/>
- <value name="LRZ_CACHE_INVALIDATE" value="40" variants="A7XX"/>
- <value name="LRZ_Q_CACHE_INVALIDATE" value="41" variants="A7XX"/>
- <value name="BLIT_OP_SCALE_2D" value="42" variants="A5XX-"/>
- <value name="CONTEXT_DONE_2D" value="43" variants="A5XX-"/>
- <value name="VSC_BINNING_START" value="44" variants="A5XX-"/>
- <value name="VSC_BINNING_END" value="45" variants="A5XX-"/>
+ <value name="LRZ_CLEAR" value="0x25" variants="A5XX-"/>
+
+ <value name="LRZ_FLUSH_INVALIDATE" value="0x26" variants="A5XX-A6XX"/>
+ <value name="LRZ_CACHE_FLUSH" value="0x26" variants="A7XX-"/>
+ <value name="BLIT_OP_FILL_2D" value="0x27" variants="A5XX-A6XX"/>
+ <value name="BLIT_OP_COPY_2D" value="0x28" variants="A5XX-A6XX"/>
+ <value name="LRZ_CACHE_INVALIDATE" value="0x28" variants="A7XX-"/>
+ <value name="LRZ_Q_CACHE_INVALIDATE" value="0x29" variants="A7XX-"/>
+ <value name="BLIT_OP_SCALE_2D" value="0x2a" variants="A5XX-"/>
+ <value name="CONTEXT_DONE_2D" value="0x2b" variants="A5XX-"/>
+ <value name="VSC_BINNING_START" value="0x2c" variants="A5XX-"/>
+ <value name="VSC_BINNING_END" value="0x2d" variants="A5XX-"/>
<!-- a6xx events -->
<doc>
Invalidates UCHE.
</doc>
- <value name="CACHE_INVALIDATE" value="49" variants="A6XX"/>
+ <value name="CACHE_INVALIDATE" value="0x31" variants="A6XX"/>
- <value name="LABEL" value="63" variants="A6XX-"/>
+ <value name="DEBUG_LABEL" value="0x3f" variants="A6XX-"/>
<!-- note, some of these are the same as a6xx, just named differently -->
<doc> Doesn't seem to do anything </doc>
- <value name="DUMMY_EVENT" value="1" variants="A7XX"/>
- <value name="CCU_INVALIDATE_DEPTH" value="24" variants="A7XX"/>
- <value name="CCU_INVALIDATE_COLOR" value="25" variants="A7XX"/>
- <value name="CCU_RESOLVE_CLEAN" value="26" variants="A7XX"/>
- <value name="CCU_FLUSH_DEPTH" value="28" variants="A7XX"/>
- <value name="CCU_FLUSH_COLOR" value="29" variants="A7XX"/>
- <value name="CCU_RESOLVE" value="30" variants="A7XX"/>
- <value name="CCU_END_RESOLVE_GROUP" value="31" variants="A7XX"/>
- <value name="CCU_CLEAN_DEPTH" value="32" variants="A7XX"/>
- <value name="CCU_CLEAN_COLOR" value="33" variants="A7XX"/>
- <value name="CACHE_RESET" value="48" variants="A7XX"/>
- <value name="CACHE_CLEAN" value="49" variants="A7XX"/>
+ <value name="DUMMY_EVENT" value="0x01" variants="A7XX-"/>
+ <value name="CCU_INVALIDATE_DEPTH" value="0x18" variants="A7XX-"/>
+ <value name="CCU_INVALIDATE_COLOR" value="0x19" variants="A7XX-"/>
+ <value name="CCU_RESOLVE_CLEAN" value="0x1a" variants="A7XX-"/>
+ <value name="CCU_FLUSH_DEPTH" value="0x1c" variants="A7XX-"/>
+ <value name="CCU_FLUSH_COLOR" value="0x1d" variants="A7XX-"/>
+ <value name="CCU_END_RESOLVE_GROUP" value="0x1f" variants="A7XX-"/>
+ <value name="CCU_CLEAN_DEPTH" value="0x20" variants="A7XX-"/>
+ <value name="CCU_CLEAN_COLOR" value="0x21" variants="A7XX-"/>
+ <value name="CACHE_RESET" value="0x30" variants="A7XX-"/>
+ <value name="CACHE_CLEAN" value="0x31" variants="A7XX-"/>
<!-- TODO: deal with name conflicts with other gens -->
- <value name="CACHE_FLUSH7" value="50" variants="A7XX"/>
- <value name="CACHE_INVALIDATE7" value="51" variants="A7XX"/>
+ <value name="CACHE_FLUSH7" value="0x32" variants="A7XX-"/>
+ <value name="CACHE_INVALIDATE7" value="0x33" variants="A7XX-"/>
+ <value name="DEPTH_BUFFER_FLIP" value="0x3d" variants="A8XX-"/>
+ <value name="CCH_FAST_CLEAR_CLEAN" value="0x1b" variants="A8XX-"/>
</enum>
<enum name="pc_di_primtype">
@@ -310,11 +311,11 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="CP_EVENT_WRITE" value="0x46" variants="A2XX-A6XX"/>
<value name="CP_EVENT_WRITE7" value="0x46" variants="A7XX-"/>
<doc>generate a VS|PS_done event</doc>
- <value name="CP_EVENT_WRITE_SHD" value="0x58"/>
+ <value name="CP_EVENT_WRITE_SHD" value="0x58" variants="A2XX"/>
<doc>generate a cache flush done event</doc>
- <value name="CP_EVENT_WRITE_CFL" value="0x59"/>
+ <value name="CP_EVENT_WRITE_CFL" value="0x59" variants="A2XX"/>
<doc>generate a z_pass done event</doc>
- <value name="CP_EVENT_WRITE_ZPD" value="0x5b"/>
+ <value name="CP_EVENT_WRITE_ZPD" value="0x5b" variants="A2XX"/>
<doc>
not sure the real name, but this seems to be what is used for
opencl, instead of CP_DRAW_INDX..
@@ -335,9 +336,9 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<doc>load constant into chip and to memory</doc>
<value name="CP_SET_CONSTANT" value="0x2d" variants="A2XX"/>
<doc>load sequencer instruction memory (pointer-based)</doc>
- <value name="CP_IM_LOAD" value="0x27"/>
+ <value name="CP_IM_LOAD" value="0x27" variants="A2XX"/>
<doc>load sequencer instruction memory (code embedded in packet)</doc>
- <value name="CP_IM_LOAD_IMMEDIATE" value="0x2b"/>
+ <value name="CP_IM_LOAD_IMMEDIATE" value="0x2b" variants="A2XX"/>
<doc>load constants from a location in memory</doc>
<value name="CP_LOAD_CONSTANT_CONTEXT" value="0x2e" variants="A2XX"/>
<doc>selective invalidation of state pointers</doc>
@@ -662,6 +663,12 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<value name="CP_CCHE_INVALIDATE" value="0x3a" variants="A7XX-"/>
<value name="CP_SCOPE_CNTL" value="0x6c" variants="A7XX-"/>
+
+ <value name="CP_SKIP_IB_MODE" value="0x27" variants="A7XX-"/>
+
+ <value name="CP_MEMORY_MAP_UPDATE" value="0x58" variants="A8XX-"/>
+
+ <value name="CP_BARRIER" value="0x59" variants="A8XX-"/>
</enum>
@@ -1800,49 +1807,73 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
<value value="6" name="RM6_BIN_RESOLVE"/>
<value value="7" name="RM6_BIN_RENDER_END"/>
<value value="8" name="RM6_COMPUTE"/>
- <value value="0xc" name="RM6_BLIT2DSCALE"/> <!-- no-op (at least on current sqe fw) -->
+ <value value="12" name="RM6_BLIT2DSCALE"/> <!-- no-op (at least on current sqe fw) -->
<!--
These values come from a6xx_set_marker() in the
downstream kernel, and they can only be set by the kernel
-->
- <value value="0xd" name="RM6_IB1LIST_START"/>
- <value value="0xe" name="RM6_IB1LIST_END"/>
+ <value value="13" name="RM6_IB1LIST_START"/>
+ <value value="14" name="RM6_IB1LIST_END"/>
+ <value value="15" name="RM7_BIN_VISIBILITY_END"/>
+
+ <!-- new in a8xx: -->
+ <value value="32" name="RM8_DEPTH_PASS_START"/>
+ <value value="33" name="RM8_DEPTH_PASS_END"/>
+ <value value="34" name="RM8_SET_RENDER_TARGET"/>
+ <value value="35" name="RM8_PGMEM_ON"/>
+ <value value="36" name="RM8_PGMEM_OFF"/>
</enum>
- <reg32 offset="0" name="0">
- <!-- if b8 is set, the low bits are interpreted differently (and b4 ignored) -->
- <bitfield name="MARKER_MODE" pos="8" type="set_marker_mode" addvariant="yes"/>
-
- <bitfield name="MODE" low="0" high="3" type="a6xx_marker" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
- <!-- used by preemption to determine if GMEM needs to be saved or not -->
- <bitfield name="USES_GMEM" pos="4" type="boolean" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
-
- <bitfield name="IFPC_MODE" pos="0" type="a6xx_ifpc_mode" varset="set_marker_mode" variants="SET_IFPC_MODE"/>
-
- <!--
- CP_SET_MARKER is used with these bits to create a
- critical section around a workaround for ray tracing.
- The workaround happens after BVH building, and appears
- to invalidate the RTU's BVH node cache. It makes sure
- that only one of BR/BV/LPAC is executing the
- workaround at a time, and no draws using RT on BV/LPAC
- are executing while the workaround is executed on BR (or
- vice versa, that no draws on BV/BR using RT are executed
- while the workaround executes on LPAC), by
- hooking subsequent CP_EVENT_WRITE/CP_DRAW_*/CP_EXEC_CS.
- The blob usage is:
-
- CP_SET_MARKER(RT_WA_START)
- ... workaround here ...
- CP_SET_MARKER(RT_WA_END)
- ...
- CP_SET_MARKER(SHADER_USES_RT)
- CP_DRAW_INDX(...) or CP_EXEC_CS(...)
- -->
- <bitfield name="SHADER_USES_RT" pos="9" type="boolean" variants="A7XX-"/>
- <bitfield name="RT_WA_START" pos="10" type="boolean" variants="A7XX-"/>
- <bitfield name="RT_WA_END" pos="11" type="boolean" variants="A7XX-"/>
- </reg32>
+ <stripe varset="chip" variants="A6XX-A7XX">
+ <reg32 offset="0" name="0">
+ <!-- if b8 is set, the low bits are interpreted differently (and b4 ignored) -->
+ <bitfield name="MARKER_MODE" pos="8" type="set_marker_mode" addvariant="yes"/>
+
+
+ <bitfield name="MODE" low="0" high="3" type="a6xx_marker" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
+ <!-- used by preemption to determine if GMEM needs to be saved or not -->
+ <bitfield name="USES_GMEM" pos="4" type="boolean" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
+
+
+ <bitfield name="IFPC_MODE" pos="0" type="a6xx_ifpc_mode" varset="set_marker_mode" variants="SET_IFPC_MODE"/>
+
+
+ <!--
+ CP_SET_MARKER is used with these bits to create a
+ critical section around a workaround for ray tracing.
+ The workaround happens after BVH building, and appears
+ to invalidate the RTU's BVH node cache. It makes sure
+ that only one of BR/BV/LPAC is executing the
+ workaround at a time, and no draws using RT on BV/LPAC
+ are executing while the workaround is executed on BR (or
+ vice versa, that no draws on BV/BR using RT are executed
+ while the workaround executes on LPAC), by
+ hooking subsequent CP_EVENT_WRITE/CP_DRAW_*/CP_EXEC_CS.
+ The blob usage is:
+
+
+ CP_SET_MARKER(RT_WA_START)
+ ... workaround here ...
+ CP_SET_MARKER(RT_WA_END)
+ ...
+ CP_SET_MARKER(SHADER_USES_RT)
+ CP_DRAW_INDX(...) or CP_EXEC_CS(...)
+ -->
+ <bitfield name="SHADER_USES_RT" pos="9" type="boolean" variants="A7XX-"/>
+ <bitfield name="RT_WA_START" pos="10" type="boolean" variants="A7XX-"/>
+ <bitfield name="RT_WA_END" pos="11" type="boolean" variants="A7XX-"/>
+ </reg32>
+ </stripe>
+ <stripe varset="chip" variants="A8XX-">
+ <reg32 offset="0" name="0">
+ <!-- if b8 is set, the low bits are interpreted differently (and b4 ignored) -->
+ <bitfield name="MARKER_MODE" pos="8" type="set_marker_mode" addvariant="yes"/>
+ <bitfield name="MODE" low="0" high="6" type="a6xx_marker" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
+ <bitfield name="USES_GMEM" pos="7" type="boolean" varset="set_marker_mode" variants="SET_RENDER_MODE"/>
+ <bitfield name="IFPC_MODE" pos="0" type="a6xx_ifpc_mode" varset="set_marker_mode" variants="SET_IFPC_MODE"/>
+ <!-- idk if the RT w/a fields apply to a8xx as well -->
+ </reg32>
+ </stripe>
</domain>
<domain name="CP_SET_PSEUDO_REG" width="32" varset="chip" prefix="chip" variants="A6XX-">
@@ -2066,6 +2097,14 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
payload *and* skipsaverestore is set. This is
expected to restore static register values not
saved when skipsaverestore is set.
+
+ On BV, a skipsaverestore preemption is triggered
+ and this preamble type is executed whenever a
+ CP_THREAD_CONTROL that synchronizes threads
+ happens. This can be explicitly via
+ SYNC_THREADS, or implicitly when the value of
+ CONCURRENT_BIN_DISABLE changes from the previous
+ thread control.
</doc>
</value>
<value name="POSTAMBLE_AMBLE_TYPE" value="2">
@@ -2308,5 +2347,99 @@ opcode: CP_LOAD_STATE4 (30) (4 dwords)
</reg32>
</domain>
+<domain name="CP_RESOURCE_LIST" width="32">
+ <doc>
+ A7xx introduces the "resource table" which is managed by
+ CP_RESOURCE_LIST. It is used to synchronize BR and BV access
+ to resources such as LRZ buffers.
+
+ The resource table consists of resources that are in-use by BR.
+ Each "resource" has a base address, which is
+ usually a pointer but is treated by the HW as an opaque handle,
+ a read/write bit, and a timestamp when it was last used.
+ Resources are removed from the table upon event completion when
+ a special CP_EVENT_WRITE::CLEAR_RENDER_RESOURCE bit is set, which
+ will remove all resources with a timestamp up to the current
+ timestamp.
+
+ CP_RESOURCE_LIST first specifies a list of BV resources. For
+ each BV resource, the HW will check if there is a corresponding
+ BR resource in the table, and if at least one of the BV and BR
+ resources is marked WRITE then it will stall until the BR
+ resource is removed.
+
+ It then specifies a list of BR resources. These will be added to
+ the resource table, unless there is an overflow in which case
+ the designated overflow register will have bit 0 set. Overflow
+ should cause the next binning pass to stall until BR is done,
+ effectively disabling concurrent binning.
+
+ CP_RESOURCE_LIST must be executed by BV. BR resources are added
+ by BV and removed by BR.
+
+ There is a separate table for "LRZ resources." These behave a
+ bit differently: specifying an LRZ resource via BV_RES_LRZ
+ stalls on any matching resource existing and then adds it to the
+ table, making it both a BV and BR resource in one. There is a
+ separate CLEAR_LRZ_RESOURCE bit for removing resources from the
+ LRZ table, and it only removes one resource given by a base
+ address passed to CP_EVENT_WRITE. Therefore timestamps are
+ unnecessary.
+ </doc>
+ <reg32 offset="0" name="BV_COUNT" type="uint"/>
+ <doc>
+ What follows is a list of CP_BV_RESOURCE and then CP_RESOURCE_LIST_BR.
+ </doc>
+</domain>
+
+<domain name="CP_BV_RESOURCE" width="32">
+ <doc>
+ BV resources don't go in the table. Instead CP waits until any
+ corresponding BR resources with the same base pointer are
+ finished before the packet completes.
+ </doc>
+ <enum name="cp_bv_resource_encoding">
+ <value value="0" name="BV_RES_DIRECT"/>
+ <doc>
+ INDIRECT resources are encoded as a 32b offset + 3b
+ bindless base selector. The offset is added to the given
+ BINDLESS_BASE pseudoregister and then the 64b value
+ fetched there is used as the pointer.
+ </doc>
+ <value value="1" name="BV_RES_INDIRECT_READ"/>
+ <value value="2" name="BV_RES_LRZ"/>
+ <value value="3" name="BV_RES_INDIRECT_WRITE"/>
+ </enum>
+ <reg64 offset="0" name="0">
+ <bitfield name="BASE_ADDR" low="1" high="61" shr="1" type="address"/>
+ <bitfield name="WRITE" pos="0" type="boolean"/>
+ <bitfield name="ENCODING" low="62" high="63" type="cp_bv_resource_encoding"/>
+ </reg64>
+</domain>
+
+<domain name="CP_RESOURCE_LIST_BR" width="32">
+ <reg32 offset="0" name="0">
+ <bitfield name="BR_COUNT" low="0" high="23" type="uint"/>
+ <bitfield name="OVERFLOW_ONCHIP_ADDR" low="24" high="26"/>
+ <bitfield name="OVERFLOW" pos="31" type="boolean"/>
+ </reg32>
+ <doc>
+ What follows is a list of CP_BR_RESOURCE.
+ </doc>
+</domain>
+
+<domain name="CP_BR_RESOURCE" width="32">
+ <enum name="cp_br_resource_encoding">
+ <value value="0" name="BR_RES_DIRECT"/>
+ <value value="2" name="BR_RES_INDIRECT_READ"/>
+ <value value="3" name="BR_RES_INDIRECT_WRITE"/> <!-- set WRITE bit -->
+ </enum>
+ <reg64 offset="0" name="0">
+ <bitfield name="BASE_ADDR" low="1" high="61" shr="1" type="address"/>
+ <bitfield name="WRITE" pos="0" type="boolean"/>
+ <bitfield name="ENCODING" low="62" high="63" type="cp_br_resource_encoding"/>
+ </reg64>
+</domain>
+
</database>
diff --git a/drivers/gpu/drm/msm/registers/gen_header.py b/drivers/gpu/drm/msm/registers/gen_header.py
index 1d603dadfabd..2acad951f1e2 100644
--- a/drivers/gpu/drm/msm/registers/gen_header.py
+++ b/drivers/gpu/drm/msm/registers/gen_header.py
@@ -189,12 +189,13 @@ class Bitset(object):
print(" return (struct fd_reg_pair) {")
print(" .reg = (uint32_t)%s," % reg.reg_offset())
print(" .value =")
+ cast = "(uint64_t)" if reg.bit_size == 64 else ""
for f in self.fields:
if f.type in [ "address", "waddress" ]:
continue
else:
type, val = f.ctype("fields.%s" % field_name(reg, f))
- print(" (%-40s << %2d) |" % (val, f.low))
+ print(" (%s%-40s << %2d) |" % (cast, val, f.low))
value_name = "dword"
if reg.bit_size == 64:
value_name = "qword"
@@ -264,10 +265,11 @@ class Bitset(object):
(prefix, prefix, prefix, skip))
- def dump(self, is_deprecated, prefix=None):
+ def dump(self, is_deprecated, prefix=None, reg=None):
if prefix is None:
prefix = self.name
- if self.reg and self.reg.bit_size == 64:
+ reg64 = reg and self.reg and self.reg.bit_size == 64
+ if reg64:
print("static inline uint32_t %s_LO(uint32_t val)\n{" % prefix)
print("\treturn val;\n}")
print("static inline uint32_t %s_HI(uint32_t val)\n{" % prefix)
@@ -283,14 +285,17 @@ class Bitset(object):
elif f.type == "boolean" or (f.type is None and f.low == f.high):
tab_to("#define %s" % name, "0x%08x" % (1 << f.low))
else:
- tab_to("#define %s__MASK" % name, "0x%08x" % mask(f.low, f.high))
+ typespec = "ull" if reg64 else "u"
+ tab_to("#define %s__MASK" % name, "0x%08x%s" % (mask(f.low, f.high), typespec))
tab_to("#define %s__SHIFT" % name, "%d" % f.low)
type, val = f.ctype("val")
+ ret_type = "uint64_t" if reg64 else "uint32_t"
+ cast = "(uint64_t)" if reg64 else ""
- print("static inline uint32_t %s(%s val)\n{" % (name, type))
+ print("static inline %s %s(%s val)\n{" % (ret_type, name, type))
if f.shr > 0:
print("\tassert(!(val & 0x%x));" % mask(0, f.shr - 1))
- print("\treturn ((%s) << %s__SHIFT) & %s__MASK;\n}" % (val, name, name))
+ print("\treturn (%s(%s) << %s__SHIFT) & %s__MASK;\n}" % (cast, val, name, name))
print()
class Array(object):
@@ -437,7 +442,7 @@ class Reg(object):
print("static inline%s uint32_t REG_%s(%s) { return 0x%08x + %s; }" % (depcrstr, self.full_name, proto, offset, strides))
if self.bitset.inline:
- self.bitset.dump(is_deprecated, self.full_name)
+ self.bitset.dump(is_deprecated, self.full_name, self)
print("")
def dump_pack_struct(self, is_deprecated):
diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c
index 1c3b33be6c40..72eb0de46b54 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_kms.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c
@@ -26,6 +26,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "lcdif_drv.h"
diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
index 7ed2516b6de0..8cac0a275b7d 100644
--- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c
+++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c
@@ -26,6 +26,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "mxsfb_drv.h"
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
index c88776d1e784..3b5757aed9c8 100644
--- a/drivers/gpu/drm/nouveau/Kconfig
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -28,6 +28,7 @@ config DRM_NOUVEAU
select THERMAL if ACPI && X86
select ACPI_VIDEO if ACPI && X86
select SND_HDA_COMPONENT if SND_HDA_CORE
+ select PM_DEVFREQ if ARCH_TEGRA
help
Choose this option for open-source NVIDIA support.
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index e97e39abf3a2..12b1dba8e05d 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -2867,7 +2867,9 @@ nv50_display_create(struct drm_device *dev)
}
/* Assign the correct format modifiers */
- if (disp->disp->object.oclass >= TU102_DISP)
+ if (disp->disp->object.oclass >= GB202_DISP)
+ nouveau_display(dev)->format_modifiers = wndwca7e_modifiers;
+ else if (disp->disp->object.oclass >= TU102_DISP)
nouveau_display(dev)->format_modifiers = wndwc57e_modifiers;
else
if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h
index 15f9242b72ac..5d998f0319dc 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.h
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h
@@ -104,4 +104,5 @@ struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder);
extern const u64 disp50xx_modifiers[];
extern const u64 disp90xx_modifiers[];
extern const u64 wndwc57e_modifiers[];
+extern const u64 wndwca7e_modifiers[];
#endif
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
index e2c55f4b9c5a..ef9e410babbf 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
@@ -786,13 +786,14 @@ nv50_wndw_destroy(struct drm_plane *plane)
}
/* This function assumes the format has already been validated against the plane
- * and the modifier was validated against the device-wides modifier list at FB
+ * and the modifier was validated against the device-wide modifier list at FB
* creation time.
*/
static bool nv50_plane_format_mod_supported(struct drm_plane *plane,
u32 format, u64 modifier)
{
struct nouveau_drm *drm = nouveau_drm(plane->dev);
+ const struct drm_format_info *info = drm_format_info(format);
uint8_t i;
/* All chipsets can display all formats in linear layout */
@@ -800,13 +801,32 @@ static bool nv50_plane_format_mod_supported(struct drm_plane *plane,
return true;
if (drm->client.device.info.chipset < 0xc0) {
- const struct drm_format_info *info = drm_format_info(format);
const uint8_t kind = (modifier >> 12) & 0xff;
if (!format) return false;
for (i = 0; i < info->num_planes; i++)
if ((info->cpp[i] != 4) && kind != 0x70) return false;
+ } else if (drm->client.device.info.chipset >= 0x1b2) {
+ const uint8_t slayout = ((modifier >> 22) & 0x1) |
+ ((modifier >> 25) & 0x6);
+
+ if (!format)
+ return false;
+
+ /*
+ * Note in practice this implies only formats where cpp is equal
+ * for each plane, or >= 4 for all planes, are supported.
+ */
+ for (i = 0; i < info->num_planes; i++) {
+ if (((info->cpp[i] == 2) && slayout != 3) ||
+ ((info->cpp[i] == 1) && slayout != 2) ||
+ ((info->cpp[i] >= 4) && slayout != 1))
+ return false;
+
+ /* 24-bit not supported. It has yet another layout */
+ WARN_ON(info->cpp[i] == 3);
+ }
}
return true;
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c b/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c
index 0d8e9a9d1a57..2cec8cfbd546 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndwca7e.c
@@ -179,6 +179,39 @@ wndwca7e_ntfy_set(struct nv50_wndw *wndw, struct nv50_wndw_atom *asyw)
return 0;
}
+/****************************************************************
+ * Log2(block height) ----------------------------+ *
+ * Page Kind ----------------------------------+ | *
+ * Gob Height/Page Kind Generation ------+ | | *
+ * Sector layout -------+ | | | *
+ * Compression ------+ | | | | */
+const u64 wndwca7e_modifiers[] = { /* | | | | | */
+ /* 4cpp+ modifiers */
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 0),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 1),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 2),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 3),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 4),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 1, 2, 0x06, 5),
+ /* 1cpp/8bpp modifiers */
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 2, 2, 0x06, 0),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 2, 2, 0x06, 1),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 2, 2, 0x06, 2),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 2, 2, 0x06, 3),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 2, 2, 0x06, 4),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 2, 2, 0x06, 5),
+ /* 2cpp/16bpp modifiers */
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 3, 2, 0x06, 0),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 3, 2, 0x06, 1),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 3, 2, 0x06, 2),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 3, 2, 0x06, 3),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 3, 2, 0x06, 4),
+ DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, 3, 2, 0x06, 5),
+ /* All formats support linear */
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID
+};
+
static const struct nv50_wndw_func
wndwca7e = {
.acquire = wndwc37e_acquire,
diff --git a/drivers/gpu/drm/nouveau/include/nvfw/hs.h b/drivers/gpu/drm/nouveau/include/nvfw/hs.h
index 8b58b668fc0c..c78ab11ec3ac 100644
--- a/drivers/gpu/drm/nouveau/include/nvfw/hs.h
+++ b/drivers/gpu/drm/nouveau/include/nvfw/hs.h
@@ -52,7 +52,9 @@ struct nvfw_hs_load_header_v2 {
struct {
u32 offset;
u32 size;
- } app[];
+ u32 data_offset;
+ u32 data_size;
+ } app[] __counted_by(num_apps);
};
const struct nvfw_hs_load_header_v2 *nvfw_hs_load_header_v2(struct nvkm_subdev *, const void *);
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h b/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h
index 22f74fc88cd7..57bc542780bb 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/core/tegra.h
@@ -9,6 +9,8 @@ struct nvkm_device_tegra {
struct nvkm_device device;
struct platform_device *pdev;
+ void __iomem *regs;
+
struct reset_control *rst;
struct clk *clk;
struct clk *clk_ref;
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h
index d5d8877064a7..6a09d397c651 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/clk.h
@@ -134,4 +134,5 @@ int gf100_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct
int gk104_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **);
int gk20a_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **);
int gm20b_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **);
+int gp10b_clk_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_clk **);
#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h
index d59fd12268b9..6c26beeb427f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.h
@@ -57,7 +57,7 @@ nouveau_bo(struct ttm_buffer_object *bo)
static inline void
nouveau_bo_fini(struct nouveau_bo *bo)
{
- ttm_bo_put(&bo->bo);
+ ttm_bo_fini(&bo->bo);
}
extern struct ttm_device_funcs nouveau_bo_driver;
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 805d0a87aa54..00515623a2cc 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -30,6 +30,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_client_event.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
@@ -764,7 +765,7 @@ nouveau_display_suspend(struct drm_device *dev, bool runtime)
{
struct nouveau_display *disp = nouveau_display(dev);
- drm_client_dev_suspend(dev, false);
+ drm_client_dev_suspend(dev);
if (drm_drv_uses_atomic_modeset(dev)) {
if (!runtime) {
@@ -795,7 +796,7 @@ nouveau_display_resume(struct drm_device *dev, bool runtime)
}
}
- drm_client_dev_resume(dev, false);
+ drm_client_dev_resume(dev);
}
int
@@ -807,9 +808,9 @@ nouveau_display_dumb_create(struct drm_file *file_priv, struct drm_device *dev,
uint32_t domain;
int ret;
- args->pitch = roundup(args->width * (args->bpp / 8), 256);
- args->size = args->pitch * args->height;
- args->size = roundup(args->size, PAGE_SIZE);
+ ret = drm_mode_size_dumb(dev, args, SZ_256, 0);
+ if (ret)
+ return ret;
/* Use VRAM if there is any ; otherwise fallback to system memory */
if (nouveau_drm(dev)->client.device.info.ram_size != 0)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 55abc510067b..0e409414f44d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -10,7 +10,7 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 4
-#define DRIVER_PATCHLEVEL 0
+#define DRIVER_PATCHLEVEL 1
/*
* 1.1.1:
@@ -35,6 +35,8 @@
* programs that get directly linked with NVKM.
* 1.3.1:
* - implemented limited ABI16/NVIF interop
+ * 1.4.1:
+ * - add variable page sizes and compression for Turing+
*/
#include <linux/notifier.h>
@@ -49,6 +51,7 @@
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_placement.h>
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 690e10fbf0bd..395d92ab6271 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -87,7 +87,7 @@ nouveau_gem_object_del(struct drm_gem_object *gem)
return;
}
- ttm_bo_put(&nvbo->bo);
+ ttm_bo_fini(&nvbo->bo);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_platform.c b/drivers/gpu/drm/nouveau/nouveau_platform.c
index 8d5853deeee4..9fd351273236 100644
--- a/drivers/gpu/drm/nouveau/nouveau_platform.c
+++ b/drivers/gpu/drm/nouveau/nouveau_platform.c
@@ -21,6 +21,8 @@
*/
#include "nouveau_platform.h"
+#include <nvkm/subdev/clk/gk20a_devfreq.h>
+
static int nouveau_platform_probe(struct platform_device *pdev)
{
const struct nvkm_device_tegra_func *func;
@@ -40,6 +42,21 @@ static void nouveau_platform_remove(struct platform_device *pdev)
nouveau_drm_device_remove(drm);
}
+#ifdef CONFIG_PM_SLEEP
+static int nouveau_platform_suspend(struct device *dev)
+{
+ return gk20a_devfreq_suspend(dev);
+}
+
+static int nouveau_platform_resume(struct device *dev)
+{
+ return gk20a_devfreq_resume(dev);
+}
+
+static SIMPLE_DEV_PM_OPS(nouveau_pm_ops, nouveau_platform_suspend,
+ nouveau_platform_resume);
+#endif
+
#if IS_ENABLED(CONFIG_OF)
static const struct nvkm_device_tegra_func gk20a_platform_data = {
.iommu_bit = 34,
@@ -81,6 +98,9 @@ struct platform_driver nouveau_platform_driver = {
.driver = {
.name = "nouveau",
.of_match_table = of_match_ptr(nouveau_platform_match),
+#ifdef CONFIG_PM_SLEEP
+ .pm = &nouveau_pm_ops,
+#endif
},
.probe = nouveau_platform_probe,
.remove = nouveau_platform_remove,
diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c
index e60f7892f5ce..a7bf539e5d86 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sched.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sched.c
@@ -482,6 +482,17 @@ nouveau_sched_create(struct nouveau_sched **psched, struct nouveau_drm *drm,
return 0;
}
+static bool
+nouveau_sched_job_list_empty(struct nouveau_sched *sched)
+{
+ bool empty;
+
+ spin_lock(&sched->job.list.lock);
+ empty = list_empty(&sched->job.list.head);
+ spin_unlock(&sched->job.list.lock);
+
+ return empty;
+}
static void
nouveau_sched_fini(struct nouveau_sched *sched)
@@ -489,8 +500,7 @@ nouveau_sched_fini(struct nouveau_sched *sched)
struct drm_gpu_scheduler *drm_sched = &sched->base;
struct drm_sched_entity *entity = &sched->entity;
- rmb(); /* for list_empty to work without lock */
- wait_event(sched->job.wq, list_empty(&sched->job.list.head));
+ wait_event(sched->job.wq, nouveau_sched_job_list_empty(sched));
drm_sched_entity_fini(entity);
drm_sched_fini(drm_sched);
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
index 7d2436e5d50d..0a55babdf667 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -302,8 +302,10 @@ nouveau_ttm_init(struct nouveau_drm *drm)
ret = ttm_device_init(&drm->ttm.bdev, &nouveau_bo_driver, drm->dev->dev,
dev->anon_inode->i_mapping,
dev->vma_offset_manager,
- drm_need_swiotlb(drm->client.mmu.dmabits),
- drm->client.mmu.dmabits <= 32);
+ (drm_need_swiotlb(drm->client.mmu.dmabits) ?
+ TTM_ALLOCATION_POOL_USE_DMA_ALLOC : 0) |
+ (drm->client.mmu.dmabits <= 32 ?
+ TTM_ALLOCATION_POOL_USE_DMA32 : 0));
if (ret) {
NV_ERROR(drm, "error initialising bo driver, %d\n", ret);
return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
index 79eefdfd08a2..f10809115c56 100644
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c
@@ -107,34 +107,34 @@ nouveau_uvmm_vmm_sparse_unref(struct nouveau_uvmm *uvmm,
static int
nouveau_uvmm_vmm_get(struct nouveau_uvmm *uvmm,
- u64 addr, u64 range)
+ u64 addr, u64 range, u8 page_shift)
{
struct nvif_vmm *vmm = &uvmm->vmm.vmm;
- return nvif_vmm_raw_get(vmm, addr, range, PAGE_SHIFT);
+ return nvif_vmm_raw_get(vmm, addr, range, page_shift);
}
static int
nouveau_uvmm_vmm_put(struct nouveau_uvmm *uvmm,
- u64 addr, u64 range)
+ u64 addr, u64 range, u8 page_shift)
{
struct nvif_vmm *vmm = &uvmm->vmm.vmm;
- return nvif_vmm_raw_put(vmm, addr, range, PAGE_SHIFT);
+ return nvif_vmm_raw_put(vmm, addr, range, page_shift);
}
static int
nouveau_uvmm_vmm_unmap(struct nouveau_uvmm *uvmm,
- u64 addr, u64 range, bool sparse)
+ u64 addr, u64 range, u8 page_shift, bool sparse)
{
struct nvif_vmm *vmm = &uvmm->vmm.vmm;
- return nvif_vmm_raw_unmap(vmm, addr, range, PAGE_SHIFT, sparse);
+ return nvif_vmm_raw_unmap(vmm, addr, range, page_shift, sparse);
}
static int
nouveau_uvmm_vmm_map(struct nouveau_uvmm *uvmm,
- u64 addr, u64 range,
+ u64 addr, u64 range, u8 page_shift,
u64 bo_offset, u8 kind,
struct nouveau_mem *mem)
{
@@ -163,7 +163,7 @@ nouveau_uvmm_vmm_map(struct nouveau_uvmm *uvmm,
return -ENOSYS;
}
- return nvif_vmm_raw_map(vmm, addr, range, PAGE_SHIFT,
+ return nvif_vmm_raw_map(vmm, addr, range, page_shift,
&args, argc,
&mem->mem, bo_offset);
}
@@ -182,8 +182,9 @@ nouveau_uvma_vmm_put(struct nouveau_uvma *uvma)
{
u64 addr = uvma->va.va.addr;
u64 range = uvma->va.va.range;
+ u8 page_shift = uvma->page_shift;
- return nouveau_uvmm_vmm_put(to_uvmm(uvma), addr, range);
+ return nouveau_uvmm_vmm_put(to_uvmm(uvma), addr, range, page_shift);
}
static int
@@ -193,9 +194,11 @@ nouveau_uvma_map(struct nouveau_uvma *uvma,
u64 addr = uvma->va.va.addr;
u64 offset = uvma->va.gem.offset;
u64 range = uvma->va.va.range;
+ u8 page_shift = uvma->page_shift;
return nouveau_uvmm_vmm_map(to_uvmm(uvma), addr, range,
- offset, uvma->kind, mem);
+ page_shift, offset, uvma->kind,
+ mem);
}
static int
@@ -203,12 +206,13 @@ nouveau_uvma_unmap(struct nouveau_uvma *uvma)
{
u64 addr = uvma->va.va.addr;
u64 range = uvma->va.va.range;
+ u8 page_shift = uvma->page_shift;
bool sparse = !!uvma->region;
if (drm_gpuva_invalidated(&uvma->va))
return 0;
- return nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, sparse);
+ return nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, page_shift, sparse);
}
static int
@@ -450,6 +454,62 @@ op_unmap_prepare_unwind(struct drm_gpuva *va)
drm_gpuva_insert(va->vm, va);
}
+static bool
+op_map_aligned_to_page_shift(const struct drm_gpuva_op_map *op, u8 page_shift)
+{
+ u64 non_page_bits = (1ULL << page_shift) - 1;
+
+ return (op->va.addr & non_page_bits) == 0 &&
+ (op->va.range & non_page_bits) == 0 &&
+ (op->gem.offset & non_page_bits) == 0;
+}
+
+static u8
+select_page_shift(struct nouveau_uvmm *uvmm, struct drm_gpuva_op_map *op)
+{
+ struct nouveau_bo *nvbo = nouveau_gem_object(op->gem.obj);
+
+ /* nouveau_bo_fixup_align() guarantees that the page size will be aligned
+ * for most cases, but it can't handle cases where userspace allocates with
+ * a size and then binds with a smaller granularity. So in order to avoid
+ * breaking old userspace, we need to ensure that the VA is actually
+ * aligned before using it, and if it isn't, then we downgrade to the first
+ * granularity that will fit, which is optimal from a correctness and
+ * performance perspective.
+ */
+ if (op_map_aligned_to_page_shift(op, nvbo->page))
+ return nvbo->page;
+
+ struct nouveau_mem *mem = nouveau_mem(nvbo->bo.resource);
+ struct nvif_vmm *vmm = &uvmm->vmm.vmm;
+ int i;
+
+ /* If the given granularity doesn't fit, let's find one that will fit. */
+ for (i = 0; i < vmm->page_nr; i++) {
+ /* Ignore anything that is bigger or identical to the BO preference. */
+ if (vmm->page[i].shift >= nvbo->page)
+ continue;
+
+ /* Skip incompatible domains. */
+ if ((mem->mem.type & NVIF_MEM_VRAM) && !vmm->page[i].vram)
+ continue;
+ if ((mem->mem.type & NVIF_MEM_HOST) &&
+ (!vmm->page[i].host || vmm->page[i].shift > PAGE_SHIFT))
+ continue;
+
+ /* If it fits, return the proposed shift. */
+ if (op_map_aligned_to_page_shift(op, vmm->page[i].shift))
+ return vmm->page[i].shift;
+ }
+
+ /* If we get here then nothing can reconcile the requirements. This should never
+ * happen.
+ */
+ drm_WARN_ONCE(op->gem.obj->dev, 1, "Could not find an appropriate page size.\n");
+
+ return PAGE_SHIFT;
+}
+
static void
nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
struct nouveau_uvma_prealloc *new,
@@ -501,7 +561,8 @@ nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
if (vmm_get_range)
nouveau_uvmm_vmm_put(uvmm, vmm_get_start,
- vmm_get_range);
+ vmm_get_range,
+ select_page_shift(uvmm, &op->map));
break;
}
case DRM_GPUVA_OP_REMAP: {
@@ -528,6 +589,7 @@ nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
u64 ustart = va->va.addr;
u64 urange = va->va.range;
u64 uend = ustart + urange;
+ u8 page_shift = uvma_from_va(va)->page_shift;
/* Nothing to do for mappings we merge with. */
if (uend == vmm_get_start ||
@@ -538,7 +600,8 @@ nouveau_uvmm_sm_prepare_unwind(struct nouveau_uvmm *uvmm,
u64 vmm_get_range = ustart - vmm_get_start;
nouveau_uvmm_vmm_put(uvmm, vmm_get_start,
- vmm_get_range);
+ vmm_get_range,
+ page_shift);
}
vmm_get_start = uend;
break;
@@ -592,6 +655,7 @@ op_map_prepare(struct nouveau_uvmm *uvmm,
uvma->region = args->region;
uvma->kind = args->kind;
+ uvma->page_shift = select_page_shift(uvmm, op);
drm_gpuva_map(&uvmm->base, &uvma->va, op);
@@ -633,7 +697,8 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
if (vmm_get_range) {
ret = nouveau_uvmm_vmm_get(uvmm, vmm_get_start,
- vmm_get_range);
+ vmm_get_range,
+ new->map->page_shift);
if (ret) {
op_map_prepare_unwind(new->map);
goto unwind;
@@ -689,6 +754,7 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
u64 ustart = va->va.addr;
u64 urange = va->va.range;
u64 uend = ustart + urange;
+ u8 page_shift = uvma_from_va(va)->page_shift;
op_unmap_prepare(u);
@@ -704,7 +770,7 @@ nouveau_uvmm_sm_prepare(struct nouveau_uvmm *uvmm,
u64 vmm_get_range = ustart - vmm_get_start;
ret = nouveau_uvmm_vmm_get(uvmm, vmm_get_start,
- vmm_get_range);
+ vmm_get_range, page_shift);
if (ret) {
op_unmap_prepare_unwind(va);
goto unwind;
@@ -799,10 +865,11 @@ op_unmap_range(struct drm_gpuva_op_unmap *u,
u64 addr, u64 range)
{
struct nouveau_uvma *uvma = uvma_from_va(u->va);
+ u8 page_shift = uvma->page_shift;
bool sparse = !!uvma->region;
if (!drm_gpuva_invalidated(u->va))
- nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, sparse);
+ nouveau_uvmm_vmm_unmap(to_uvmm(uvma), addr, range, page_shift, sparse);
}
static void
@@ -882,6 +949,7 @@ nouveau_uvmm_sm_cleanup(struct nouveau_uvmm *uvmm,
struct drm_gpuva_op_map *n = r->next;
struct drm_gpuva *va = r->unmap->va;
struct nouveau_uvma *uvma = uvma_from_va(va);
+ u8 page_shift = uvma->page_shift;
if (unmap) {
u64 addr = va->va.addr;
@@ -893,7 +961,7 @@ nouveau_uvmm_sm_cleanup(struct nouveau_uvmm *uvmm,
if (n)
end = n->va.addr;
- nouveau_uvmm_vmm_put(uvmm, addr, end - addr);
+ nouveau_uvmm_vmm_put(uvmm, addr, end - addr, page_shift);
}
nouveau_uvma_gem_put(uvma);
diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.h b/drivers/gpu/drm/nouveau/nouveau_uvmm.h
index 9d3c348581eb..51925711ae90 100644
--- a/drivers/gpu/drm/nouveau/nouveau_uvmm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.h
@@ -33,6 +33,7 @@ struct nouveau_uvma {
struct nouveau_uvma_region *region;
u8 kind;
+ u8 page_shift;
};
#define uvmm_from_gpuvm(x) container_of((x), struct nouveau_uvmm, base)
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index 3375a59ebf1a..2517b65d8faa 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -2280,6 +2280,7 @@ nv13b_chipset = {
.acr = { 0x00000001, gp10b_acr_new },
.bar = { 0x00000001, gm20b_bar_new },
.bus = { 0x00000001, gf100_bus_new },
+ .clk = { 0x00000001, gp10b_clk_new },
.fault = { 0x00000001, gp10b_fault_new },
.fb = { 0x00000001, gp10b_fb_new },
.fuse = { 0x00000001, gm107_fuse_new },
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
index 114e50ca1827..03aa6f09ec89 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
@@ -259,6 +259,10 @@ nvkm_device_tegra_new(const struct nvkm_device_tegra_func *func,
tdev->func = func;
tdev->pdev = pdev;
+ tdev->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(tdev->regs))
+ return PTR_ERR(tdev->regs);
+
if (func->require_vdd) {
tdev->vdd = devm_regulator_get(&pdev->dev, "vdd");
if (IS_ERR(tdev->vdd)) {
diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/fw.c b/drivers/gpu/drm/nouveau/nvkm/falcon/fw.c
index cac6d64ab67d..4e8b3f1c7e25 100644
--- a/drivers/gpu/drm/nouveau/nvkm/falcon/fw.c
+++ b/drivers/gpu/drm/nouveau/nvkm/falcon/fw.c
@@ -159,6 +159,8 @@ nvkm_falcon_fw_dtor(struct nvkm_falcon_fw *fw)
nvkm_memory_unref(&fw->inst);
nvkm_falcon_fw_dtor_sigs(fw);
nvkm_firmware_dtor(&fw->fw);
+ kfree(fw->boot);
+ fw->boot = NULL;
}
static const struct nvkm_firmware_func
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild
index dcecd499d8df..be8f3283ee16 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/Kbuild
@@ -10,6 +10,8 @@ nvkm-y += nvkm/subdev/clk/gf100.o
nvkm-y += nvkm/subdev/clk/gk104.o
nvkm-y += nvkm/subdev/clk/gk20a.o
nvkm-y += nvkm/subdev/clk/gm20b.o
+nvkm-y += nvkm/subdev/clk/gp10b.o
+nvkm-$(CONFIG_PM_DEVFREQ) += nvkm/subdev/clk/gk20a_devfreq.o
nvkm-y += nvkm/subdev/clk/pllnv04.o
nvkm-y += nvkm/subdev/clk/pllgt215.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c
index d573fb0917fc..65f5d0f1f3bf 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.c
@@ -23,6 +23,7 @@
*
*/
#include "priv.h"
+#include "gk20a_devfreq.h"
#include "gk20a.h"
#include <core/tegra.h>
@@ -589,6 +590,10 @@ gk20a_clk_init(struct nvkm_clk *base)
return ret;
}
+ ret = gk20a_devfreq_init(base, &clk->devfreq);
+ if (ret)
+ return ret;
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h
index 286413ff4a9e..ea5b0bab4cce 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a.h
@@ -118,6 +118,7 @@ struct gk20a_clk {
const struct gk20a_clk_pllg_params *params;
struct gk20a_pll pll;
u32 parent_rate;
+ struct gk20a_devfreq *devfreq;
u32 (*div_to_pl)(u32);
u32 (*pl_to_div)(u32);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c
new file mode 100644
index 000000000000..41003cbcdbfa
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: MIT
+#include <linux/clk.h>
+#include <linux/math64.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+
+#include <drm/drm_managed.h>
+
+#include <subdev/clk.h>
+
+#include "nouveau_drv.h"
+#include "nouveau_chan.h"
+#include "priv.h"
+#include "gk20a_devfreq.h"
+#include "gk20a.h"
+#include "gp10b.h"
+
+#define PMU_BUSY_CYCLES_NORM_MAX 1000U
+
+#define PWR_PMU_IDLE_COUNTER_TOTAL 0U
+#define PWR_PMU_IDLE_COUNTER_BUSY 4U
+
+#define PWR_PMU_IDLE_COUNT_REG_OFFSET 0x0010A508U
+#define PWR_PMU_IDLE_COUNT_REG_SIZE 16U
+#define PWR_PMU_IDLE_COUNT_MASK 0x7FFFFFFFU
+#define PWR_PMU_IDLE_COUNT_RESET_VALUE (0x1U << 31U)
+
+#define PWR_PMU_IDLE_INTR_REG_OFFSET 0x0010A9E8U
+#define PWR_PMU_IDLE_INTR_ENABLE_VALUE 0U
+
+#define PWR_PMU_IDLE_INTR_STATUS_REG_OFFSET 0x0010A9ECU
+#define PWR_PMU_IDLE_INTR_STATUS_MASK 0x00000001U
+#define PWR_PMU_IDLE_INTR_STATUS_RESET_VALUE 0x1U
+
+#define PWR_PMU_IDLE_THRESHOLD_REG_OFFSET 0x0010A8A0U
+#define PWR_PMU_IDLE_THRESHOLD_REG_SIZE 4U
+#define PWR_PMU_IDLE_THRESHOLD_MAX_VALUE 0x7FFFFFFFU
+
+#define PWR_PMU_IDLE_CTRL_REG_OFFSET 0x0010A50CU
+#define PWR_PMU_IDLE_CTRL_REG_SIZE 16U
+#define PWR_PMU_IDLE_CTRL_VALUE_MASK 0x3U
+#define PWR_PMU_IDLE_CTRL_VALUE_BUSY 0x2U
+#define PWR_PMU_IDLE_CTRL_VALUE_ALWAYS 0x3U
+#define PWR_PMU_IDLE_CTRL_FILTER_MASK (0x1U << 2)
+#define PWR_PMU_IDLE_CTRL_FILTER_DISABLED 0x0U
+
+#define PWR_PMU_IDLE_MASK_REG_OFFSET 0x0010A504U
+#define PWR_PMU_IDLE_MASK_REG_SIZE 16U
+#define PWM_PMU_IDLE_MASK_GR_ENABLED 0x1U
+#define PWM_PMU_IDLE_MASK_CE_2_ENABLED 0x200000U
+
+/**
+ * struct gk20a_devfreq - Device frequency management
+ */
+struct gk20a_devfreq {
+ /** @devfreq: devfreq device. */
+ struct devfreq *devfreq;
+
+ /** @regs: Device registers. */
+ void __iomem *regs;
+
+ /** @gov_data: Governor data. */
+ struct devfreq_simple_ondemand_data gov_data;
+
+ /** @busy_time: Busy time. */
+ ktime_t busy_time;
+
+ /** @total_time: Total time. */
+ ktime_t total_time;
+
+ /** @time_last_update: Last update time. */
+ ktime_t time_last_update;
+};
+
+static struct gk20a_devfreq *dev_to_gk20a_devfreq(struct device *dev)
+{
+ struct nouveau_drm *drm = dev_get_drvdata(dev);
+ struct nvkm_subdev *subdev = nvkm_device_subdev(drm->nvkm, NVKM_SUBDEV_CLK, 0);
+ struct nvkm_clk *base = nvkm_clk(subdev);
+
+ switch (drm->nvkm->chipset) {
+ case 0x13b: return gp10b_clk(base)->devfreq; break;
+ default: return gk20a_clk(base)->devfreq; break;
+ }
+}
+
+static void gk20a_pmu_init_perfmon_counter(struct gk20a_devfreq *gdevfreq)
+{
+ u32 data;
+
+ // Set pmu idle intr status bit on total counter overflow
+ writel(PWR_PMU_IDLE_INTR_ENABLE_VALUE,
+ gdevfreq->regs + PWR_PMU_IDLE_INTR_REG_OFFSET);
+
+ writel(PWR_PMU_IDLE_THRESHOLD_MAX_VALUE,
+ gdevfreq->regs + PWR_PMU_IDLE_THRESHOLD_REG_OFFSET +
+ (PWR_PMU_IDLE_COUNTER_TOTAL * PWR_PMU_IDLE_THRESHOLD_REG_SIZE));
+
+ // Setup counter for total cycles
+ data = readl(gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET +
+ (PWR_PMU_IDLE_COUNTER_TOTAL * PWR_PMU_IDLE_CTRL_REG_SIZE));
+ data &= ~(PWR_PMU_IDLE_CTRL_VALUE_MASK | PWR_PMU_IDLE_CTRL_FILTER_MASK);
+ data |= PWR_PMU_IDLE_CTRL_VALUE_ALWAYS | PWR_PMU_IDLE_CTRL_FILTER_DISABLED;
+ writel(data, gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET +
+ (PWR_PMU_IDLE_COUNTER_TOTAL * PWR_PMU_IDLE_CTRL_REG_SIZE));
+
+ // Setup counter for busy cycles
+ writel(PWM_PMU_IDLE_MASK_GR_ENABLED | PWM_PMU_IDLE_MASK_CE_2_ENABLED,
+ gdevfreq->regs + PWR_PMU_IDLE_MASK_REG_OFFSET +
+ (PWR_PMU_IDLE_COUNTER_BUSY * PWR_PMU_IDLE_MASK_REG_SIZE));
+
+ data = readl(gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET +
+ (PWR_PMU_IDLE_COUNTER_BUSY * PWR_PMU_IDLE_CTRL_REG_SIZE));
+ data &= ~(PWR_PMU_IDLE_CTRL_VALUE_MASK | PWR_PMU_IDLE_CTRL_FILTER_MASK);
+ data |= PWR_PMU_IDLE_CTRL_VALUE_BUSY | PWR_PMU_IDLE_CTRL_FILTER_DISABLED;
+ writel(data, gdevfreq->regs + PWR_PMU_IDLE_CTRL_REG_OFFSET +
+ (PWR_PMU_IDLE_COUNTER_BUSY * PWR_PMU_IDLE_CTRL_REG_SIZE));
+}
+
+static u32 gk20a_pmu_read_idle_counter(struct gk20a_devfreq *gdevfreq, u32 counter_id)
+{
+ u32 ret;
+
+ ret = readl(gdevfreq->regs + PWR_PMU_IDLE_COUNT_REG_OFFSET +
+ (counter_id * PWR_PMU_IDLE_COUNT_REG_SIZE));
+
+ return ret & PWR_PMU_IDLE_COUNT_MASK;
+}
+
+static void gk20a_pmu_reset_idle_counter(struct gk20a_devfreq *gdevfreq, u32 counter_id)
+{
+ writel(PWR_PMU_IDLE_COUNT_RESET_VALUE, gdevfreq->regs + PWR_PMU_IDLE_COUNT_REG_OFFSET +
+ (counter_id * PWR_PMU_IDLE_COUNT_REG_SIZE));
+}
+
+static u32 gk20a_pmu_read_idle_intr_status(struct gk20a_devfreq *gdevfreq)
+{
+ u32 ret;
+
+ ret = readl(gdevfreq->regs + PWR_PMU_IDLE_INTR_STATUS_REG_OFFSET);
+
+ return ret & PWR_PMU_IDLE_INTR_STATUS_MASK;
+}
+
+static void gk20a_pmu_clear_idle_intr_status(struct gk20a_devfreq *gdevfreq)
+{
+ writel(PWR_PMU_IDLE_INTR_STATUS_RESET_VALUE,
+ gdevfreq->regs + PWR_PMU_IDLE_INTR_STATUS_REG_OFFSET);
+}
+
+static void gk20a_devfreq_update_utilization(struct gk20a_devfreq *gdevfreq)
+{
+ ktime_t now, last;
+ u64 busy_cycles, total_cycles;
+ u32 norm, intr_status;
+
+ now = ktime_get();
+ last = gdevfreq->time_last_update;
+ gdevfreq->total_time = ktime_us_delta(now, last);
+
+ busy_cycles = gk20a_pmu_read_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_BUSY);
+ total_cycles = gk20a_pmu_read_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_TOTAL);
+ intr_status = gk20a_pmu_read_idle_intr_status(gdevfreq);
+
+ gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_BUSY);
+ gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_TOTAL);
+
+ if (intr_status != 0UL) {
+ norm = PMU_BUSY_CYCLES_NORM_MAX;
+ gk20a_pmu_clear_idle_intr_status(gdevfreq);
+ } else if (total_cycles == 0ULL || busy_cycles > total_cycles) {
+ norm = PMU_BUSY_CYCLES_NORM_MAX;
+ } else {
+ norm = (u32)div64_u64(busy_cycles * PMU_BUSY_CYCLES_NORM_MAX,
+ total_cycles);
+ }
+
+ gdevfreq->busy_time = div_u64(gdevfreq->total_time * norm, PMU_BUSY_CYCLES_NORM_MAX);
+ gdevfreq->time_last_update = now;
+}
+
+static int gk20a_devfreq_target(struct device *dev, unsigned long *freq,
+ u32 flags)
+{
+ struct nouveau_drm *drm = dev_get_drvdata(dev);
+ struct nvkm_subdev *subdev = nvkm_device_subdev(drm->nvkm, NVKM_SUBDEV_CLK, 0);
+ struct nvkm_clk *base = nvkm_clk(subdev);
+ struct nvkm_pstate *pstates = base->func->pstates;
+ int nr_pstates = base->func->nr_pstates;
+ int i, ret;
+
+ for (i = 0; i < nr_pstates - 1; i++)
+ if (pstates[i].base.domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV >= *freq)
+ break;
+
+ ret = nvkm_clk_ustate(base, pstates[i].pstate, 0);
+ ret |= nvkm_clk_ustate(base, pstates[i].pstate, 1);
+ if (ret) {
+ nvkm_error(subdev, "cannot update clock\n");
+ return ret;
+ }
+
+ *freq = pstates[i].base.domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV;
+
+ return 0;
+}
+
+static int gk20a_devfreq_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+ struct nouveau_drm *drm = dev_get_drvdata(dev);
+ struct nvkm_subdev *subdev = nvkm_device_subdev(drm->nvkm, NVKM_SUBDEV_CLK, 0);
+ struct nvkm_clk *base = nvkm_clk(subdev);
+
+ *freq = nvkm_clk_read(base, nv_clk_src_gpc) * GK20A_CLK_GPC_MDIV;
+
+ return 0;
+}
+
+static void gk20a_devfreq_reset(struct gk20a_devfreq *gdevfreq)
+{
+ gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_BUSY);
+ gk20a_pmu_reset_idle_counter(gdevfreq, PWR_PMU_IDLE_COUNTER_TOTAL);
+ gk20a_pmu_clear_idle_intr_status(gdevfreq);
+
+ gdevfreq->busy_time = 0;
+ gdevfreq->total_time = 0;
+ gdevfreq->time_last_update = ktime_get();
+}
+
+static int gk20a_devfreq_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *status)
+{
+ struct nouveau_drm *drm = dev_get_drvdata(dev);
+ struct gk20a_devfreq *gdevfreq = dev_to_gk20a_devfreq(dev);
+
+ gk20a_devfreq_get_cur_freq(dev, &status->current_frequency);
+
+ gk20a_devfreq_update_utilization(gdevfreq);
+
+ status->busy_time = ktime_to_ns(gdevfreq->busy_time);
+ status->total_time = ktime_to_ns(gdevfreq->total_time);
+
+ gk20a_devfreq_reset(gdevfreq);
+
+ NV_DEBUG(drm, "busy %lu total %lu %lu %% freq %lu MHz\n",
+ status->busy_time, status->total_time,
+ status->busy_time / (status->total_time / 100),
+ status->current_frequency / 1000 / 1000);
+
+ return 0;
+}
+
+static struct devfreq_dev_profile gk20a_devfreq_profile = {
+ .timer = DEVFREQ_TIMER_DELAYED,
+ .polling_ms = 50,
+ .target = gk20a_devfreq_target,
+ .get_cur_freq = gk20a_devfreq_get_cur_freq,
+ .get_dev_status = gk20a_devfreq_get_dev_status,
+};
+
+int gk20a_devfreq_init(struct nvkm_clk *base, struct gk20a_devfreq **gdevfreq)
+{
+ struct nvkm_device *device = base->subdev.device;
+ struct nouveau_drm *drm = dev_get_drvdata(device->dev);
+ struct nvkm_device_tegra *tdev = device->func->tegra(device);
+ struct nvkm_pstate *pstates = base->func->pstates;
+ int nr_pstates = base->func->nr_pstates;
+ struct gk20a_devfreq *new_gdevfreq;
+ int i;
+
+ new_gdevfreq = drmm_kzalloc(drm->dev, sizeof(struct gk20a_devfreq), GFP_KERNEL);
+ if (!new_gdevfreq)
+ return -ENOMEM;
+
+ new_gdevfreq->regs = tdev->regs;
+
+ for (i = 0; i < nr_pstates; i++)
+ dev_pm_opp_add(base->subdev.device->dev,
+ pstates[i].base.domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV, 0);
+
+ gk20a_pmu_init_perfmon_counter(new_gdevfreq);
+ gk20a_devfreq_reset(new_gdevfreq);
+
+ gk20a_devfreq_profile.initial_freq =
+ nvkm_clk_read(base, nv_clk_src_gpc) * GK20A_CLK_GPC_MDIV;
+
+ new_gdevfreq->gov_data.upthreshold = 45;
+ new_gdevfreq->gov_data.downdifferential = 5;
+
+ new_gdevfreq->devfreq = devm_devfreq_add_device(device->dev,
+ &gk20a_devfreq_profile,
+ DEVFREQ_GOV_SIMPLE_ONDEMAND,
+ &new_gdevfreq->gov_data);
+ if (IS_ERR(new_gdevfreq->devfreq))
+ return PTR_ERR(new_gdevfreq->devfreq);
+
+ *gdevfreq = new_gdevfreq;
+
+ return 0;
+}
+
+int gk20a_devfreq_resume(struct device *dev)
+{
+ struct gk20a_devfreq *gdevfreq = dev_to_gk20a_devfreq(dev);
+
+ if (!gdevfreq || !gdevfreq->devfreq)
+ return 0;
+
+ return devfreq_resume_device(gdevfreq->devfreq);
+}
+
+int gk20a_devfreq_suspend(struct device *dev)
+{
+ struct gk20a_devfreq *gdevfreq = dev_to_gk20a_devfreq(dev);
+
+ if (!gdevfreq || !gdevfreq->devfreq)
+ return 0;
+
+ return devfreq_suspend_device(gdevfreq->devfreq);
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h
new file mode 100644
index 000000000000..5b7ca8a7a5cd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gk20a_devfreq.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: MIT */
+#ifndef __GK20A_DEVFREQ_H__
+#define __GK20A_DEVFREQ_H__
+
+#include <linux/devfreq.h>
+
+struct gk20a_devfreq;
+
+#if defined(CONFIG_PM_DEVFREQ)
+int gk20a_devfreq_init(struct nvkm_clk *base, struct gk20a_devfreq **devfreq);
+
+int gk20a_devfreq_resume(struct device *dev);
+int gk20a_devfreq_suspend(struct device *dev);
+#else
+static inline int gk20a_devfreq_init(struct nvkm_clk *base, struct gk20a_devfreq **devfreq)
+{
+ return 0;
+}
+
+static inline int gk20a_devfreq_resume(struct device dev) { return 0; }
+static inline int gk20a_devfreq_suspend(struct device *dev) { return 0; }
+#endif /* CONFIG_PM_DEVFREQ */
+
+#endif /* __GK20A_DEVFREQ_H__ */
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c
index 7c33542f651b..fa8ca53acbd1 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c
@@ -27,6 +27,7 @@
#include <core/tegra.h>
#include "priv.h"
+#include "gk20a_devfreq.h"
#include "gk20a.h"
#define GPCPLL_CFG_SYNC_MODE BIT(2)
@@ -869,6 +870,10 @@ gm20b_clk_init(struct nvkm_clk *base)
return ret;
}
+ ret = gk20a_devfreq_init(base, &clk->devfreq);
+ if (ret)
+ return ret;
+
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c
new file mode 100644
index 000000000000..492b62c0ee96
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: MIT
+#include <subdev/clk.h>
+#include <subdev/timer.h>
+#include <core/device.h>
+#include <core/tegra.h>
+
+#include "priv.h"
+#include "gk20a_devfreq.h"
+#include "gk20a.h"
+#include "gp10b.h"
+
+static int
+gp10b_clk_init(struct nvkm_clk *base)
+{
+ struct gp10b_clk *clk = gp10b_clk(base);
+ struct nvkm_subdev *subdev = &clk->base.subdev;
+ int ret;
+
+ /* Start with the highest frequency, matching the BPMP default */
+ base->func->calc(base, &base->func->pstates[base->func->nr_pstates - 1].base);
+ ret = base->func->prog(base);
+ if (ret) {
+ nvkm_error(subdev, "cannot initialize clock\n");
+ return ret;
+ }
+
+ ret = gk20a_devfreq_init(base, &clk->devfreq);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int
+gp10b_clk_read(struct nvkm_clk *base, enum nv_clk_src src)
+{
+ struct gp10b_clk *clk = gp10b_clk(base);
+ struct nvkm_subdev *subdev = &clk->base.subdev;
+
+ switch (src) {
+ case nv_clk_src_gpc:
+ return clk_get_rate(clk->clk) / GK20A_CLK_GPC_MDIV;
+ default:
+ nvkm_error(subdev, "invalid clock source %d\n", src);
+ return -EINVAL;
+ }
+}
+
+static int
+gp10b_clk_calc(struct nvkm_clk *base, struct nvkm_cstate *cstate)
+{
+ struct gp10b_clk *clk = gp10b_clk(base);
+ u32 target_rate = cstate->domain[nv_clk_src_gpc] * GK20A_CLK_GPC_MDIV;
+
+ clk->new_rate = clk_round_rate(clk->clk, target_rate) / GK20A_CLK_GPC_MDIV;
+
+ return 0;
+}
+
+static int
+gp10b_clk_prog(struct nvkm_clk *base)
+{
+ struct gp10b_clk *clk = gp10b_clk(base);
+ int ret;
+
+ ret = clk_set_rate(clk->clk, clk->new_rate * GK20A_CLK_GPC_MDIV);
+ if (ret < 0)
+ return ret;
+
+ clk->rate = clk_get_rate(clk->clk) / GK20A_CLK_GPC_MDIV;
+
+ return 0;
+}
+
+static struct nvkm_pstate
+gp10b_pstates[] = {
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 114750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 216750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 318750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 420750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 522750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 624750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 726750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 828750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 930750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 1032750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 1134750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 1236750,
+ },
+ },
+ {
+ .base = {
+ .domain[nv_clk_src_gpc] = 1300500,
+ },
+ },
+};
+
+static const struct nvkm_clk_func
+gp10b_clk = {
+ .init = gp10b_clk_init,
+ .read = gp10b_clk_read,
+ .calc = gp10b_clk_calc,
+ .prog = gp10b_clk_prog,
+ .tidy = gk20a_clk_tidy,
+ .pstates = gp10b_pstates,
+ .nr_pstates = ARRAY_SIZE(gp10b_pstates),
+ .domains = {
+ { nv_clk_src_gpc, 0xff, 0, "core", GK20A_CLK_GPC_MDIV },
+ { nv_clk_src_max }
+ }
+};
+
+int
+gp10b_clk_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
+ struct nvkm_clk **pclk)
+{
+ struct nvkm_device_tegra *tdev = device->func->tegra(device);
+ const struct nvkm_clk_func *func = &gp10b_clk;
+ struct gp10b_clk *clk;
+ int ret, i;
+
+ clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return -ENOMEM;
+ *pclk = &clk->base;
+ clk->clk = tdev->clk;
+
+ /* Finish initializing the pstates */
+ for (i = 0; i < func->nr_pstates; i++) {
+ INIT_LIST_HEAD(&func->pstates[i].list);
+ func->pstates[i].pstate = i + 1;
+ }
+
+ ret = nvkm_clk_ctor(func, device, type, inst, true, &clk->base);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h
new file mode 100644
index 000000000000..178e3bcdbbf7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gp10b.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+#ifndef __NVKM_CLK_GP10B_H__
+#define __NVKM_CLK_GP10B_H__
+
+struct gp10b_clk {
+ /* currently applied parameters */
+ struct nvkm_clk base;
+ struct gk20a_devfreq *devfreq;
+ struct clk *clk;
+ u32 rate;
+
+ /* new parameters to apply */
+ u32 new_rate;
+};
+
+#define gp10b_clk(p) container_of((p), struct gp10b_clk, base)
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c
index 8a286a9349ac..7ce1b65e2c1c 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/base.c
@@ -279,7 +279,7 @@ nvkm_fb_ctor(const struct nvkm_fb_func *func, struct nvkm_device *device,
mutex_init(&fb->tags.mutex);
if (func->sysmem.flush_page_init) {
- fb->sysmem.flush_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ fb->sysmem.flush_page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
if (!fb->sysmem.flush_page)
return -ENOMEM;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb100.c
index 1c78c8853617..170776cc82fb 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb100.c
@@ -15,6 +15,9 @@ gb100_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
const u32 hshub = DRF_LO(NV_PFB_HSHUB0);
struct nvkm_device *device = fb->subdev.device;
+ // Ensure that the address is within hardware limits
+ WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(52));
+
nvkm_wr32(device, hshub + NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_HI, addr_hi);
nvkm_wr32(device, hshub + NV_PFB_HSHUB_PCIE_FLUSH_SYSMEM_ADDR_LO, addr_lo);
nvkm_wr32(device, hshub + NV_PFB_HSHUB_EG_PCIE_FLUSH_SYSMEM_ADDR_HI, addr_hi);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb202.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb202.c
index 848505026d02..a21bf19e1041 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb202.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gb202.c
@@ -13,6 +13,9 @@ gb202_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
struct nvkm_device *device = fb->subdev.device;
const u64 addr = fb->sysmem.flush_page_addr;
+ // Ensure that the address is within hardware limits
+ WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(52));
+
nvkm_wr32(device, NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_HI, upper_32_bits(addr));
nvkm_wr32(device, NV_PFB_FBHUB0_PCIE_FLUSH_SYSMEM_ADDR_LO, lower_32_bits(addr));
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c
index 07db9b397ac1..64281a09fb39 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gf100.c
@@ -80,6 +80,9 @@ gf100_fb_init_page(struct nvkm_fb *fb)
void
gf100_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
{
+ // Ensure that the address can actually fit in the register
+ WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(40));
+
nvkm_wr32(fb->subdev.device, 0x100c10, fb->sysmem.flush_page_addr >> 8);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gh100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gh100.c
index 2d8c51f882d5..8c9394048f25 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gh100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gh100.c
@@ -13,6 +13,9 @@ gh100_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
const u64 addr = fb->sysmem.flush_page_addr >> NV_PFB_NISO_FLUSH_SYSMEM_ADDR_SHIFT;
struct nvkm_device *device = fb->subdev.device;
+ // Ensure that the address is within hardware limits
+ WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(52));
+
nvkm_wr32(device, NV_PFB_FBHUB_PCIE_FLUSH_SYSMEM_ADDR_HI, upper_32_bits(addr));
nvkm_wr32(device, NV_PFB_FBHUB_PCIE_FLUSH_SYSMEM_ADDR_LO, lower_32_bits(addr));
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c
index a6efbd913c13..076d968b7297 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/nv50.c
@@ -214,6 +214,9 @@ nv50_fb_tags(struct nvkm_fb *base)
static void
nv50_fb_sysmem_flush_page_init(struct nvkm_fb *fb)
{
+ // Ensure that the address can actually fit in the register
+ WARN_ON(fb->sysmem.flush_page_addr > DMA_BIT_MASK(40));
+
nvkm_wr32(fb->subdev.device, 0x100c08, fb->sysmem.flush_page_addr >> 8);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c
index 851fd847a2a9..ed15a4475181 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c
@@ -21,9 +21,7 @@
*/
#include "vmm.h"
-#include <core/client.h>
#include <subdev/fb.h>
-#include <subdev/ltc.h>
#include <subdev/timer.h>
#include <engine/gr.h>
@@ -111,13 +109,33 @@ gp100_vmm_pgt_pfn(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
nvkm_done(pt->memory);
}
+static inline u64
+gp100_vmm_comptag_nr(u64 size)
+{
+ return size >> 16; /* One comptag per 64KiB VRAM. */
+}
+
+static inline u64
+gp100_vmm_pte_comptagline_base(u64 addr)
+{
+ /* RM allocates enough comptags for all of VRAM, so use a 1:1 mapping. */
+ return (1 + gp100_vmm_comptag_nr(addr)) << 36; /* NV_MMU_VER2_PTE_COMPTAGLINE */
+}
+
+static inline u64
+gp100_vmm_pte_comptagline_incr(u32 page_size)
+{
+ return gp100_vmm_comptag_nr(page_size) << 36; /* NV_MMU_VER2_PTE_COMPTAGLINE */
+}
+
static inline void
gp100_vmm_pgt_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
u32 ptei, u32 ptes, struct nvkm_vmm_map *map, u64 addr)
{
u64 data = (addr >> 4) | map->type;
- map->type += ptes * map->ctag;
+ if (map->ctag)
+ data |= gp100_vmm_pte_comptagline_base(addr);
while (ptes--) {
VMM_WO064(pt, vmm, ptei++ * 8, data);
@@ -142,7 +160,6 @@ gp100_vmm_pgt_dma(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
while (ptes--) {
const u64 data = (*map->dma++ >> 4) | map->type;
VMM_WO064(pt, vmm, ptei++ * 8, data);
- map->type += map->ctag;
}
nvkm_done(pt->memory);
return;
@@ -200,7 +217,8 @@ gp100_vmm_pd0_pte(struct nvkm_vmm *vmm, struct nvkm_mmu_pt *pt,
{
u64 data = (addr >> 4) | map->type;
- map->type += ptes * map->ctag;
+ if (map->ctag)
+ data |= gp100_vmm_pte_comptagline_base(addr);
while (ptes--) {
VMM_WO128(pt, vmm, ptei++ * 0x10, data, 0ULL);
@@ -411,8 +429,6 @@ gp100_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc,
struct gp100_vmm_map_vn vn;
struct gp100_vmm_map_v0 v0;
} *args = argv;
- struct nvkm_device *device = vmm->mmu->subdev.device;
- struct nvkm_memory *memory = map->memory;
u8 kind, kind_inv, priv, ro, vol;
int kindn, aper, ret = -ENOSYS;
const u8 *kindm;
@@ -449,29 +465,24 @@ gp100_vmm_valid(struct nvkm_vmm *vmm, void *argv, u32 argc,
return -EINVAL;
}
+ /* Handle compression. */
if (kindm[kind] != kind) {
- u64 tags = nvkm_memory_size(memory) >> 16;
- if (aper != 0 || !(page->type & NVKM_VMM_PAGE_COMP)) {
- VMM_DEBUG(vmm, "comp %d %02x", aper, page->type);
- return -EINVAL;
- }
-
- if (!map->no_comp) {
- ret = nvkm_memory_tags_get(memory, device, tags,
- nvkm_ltc_tags_clear,
- &map->tags);
- if (ret) {
- VMM_DEBUG(vmm, "comp %d", ret);
- return ret;
+ struct nvkm_device *device = vmm->mmu->subdev.device;
+
+ /* Compression is only supported when using GSP-RM, as
+ * PMU firmware is required in order to initialise the
+ * compbit backing store.
+ */
+ if (nvkm_gsp_rm(device->gsp)) {
+ /* Turing GPUs require PTE_COMPTAGLINE to be filled,
+ * in addition to specifying a compressed kind.
+ */
+ if (device->card_type < GA100) {
+ map->ctag = gp100_vmm_pte_comptagline_incr(1 << map->page->shift);
+ map->next |= map->ctag;
}
- }
-
- if (!map->no_comp && map->tags->mn) {
- tags = map->tags->mn->offset + (map->offset >> 16);
- map->ctag |= ((1ULL << page->shift) >> 16) << 36;
- map->type |= tags << 36;
- map->next |= map->ctag;
} else {
+ /* Revert to non-compressed kind. */
kind = kindm[kind];
}
}
@@ -592,8 +603,8 @@ gp100_vmm = {
{ 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx },
{ 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx },
{ 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx },
- { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SVxC },
- { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SVxC },
+ { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SVxx },
+ { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SVxx },
{ 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SVHx },
{}
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c
index e081239afe58..5791d134962b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp10b.c
@@ -34,8 +34,8 @@ gp10b_vmm = {
{ 47, &gp100_vmm_desc_16[4], NVKM_VMM_PAGE_Sxxx },
{ 38, &gp100_vmm_desc_16[3], NVKM_VMM_PAGE_Sxxx },
{ 29, &gp100_vmm_desc_16[2], NVKM_VMM_PAGE_Sxxx },
- { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SxHC },
- { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SxHC },
+ { 21, &gp100_vmm_desc_16[1], NVKM_VMM_PAGE_SxHx },
+ { 16, &gp100_vmm_desc_16[0], NVKM_VMM_PAGE_SxHx },
{ 12, &gp100_vmm_desc_12[0], NVKM_VMM_PAGE_SxHx },
{}
}
diff --git a/drivers/gpu/drm/nova/Kconfig b/drivers/gpu/drm/nova/Kconfig
index cca6a3fea879..3e637ad7b5ba 100644
--- a/drivers/gpu/drm/nova/Kconfig
+++ b/drivers/gpu/drm/nova/Kconfig
@@ -1,9 +1,11 @@
config DRM_NOVA
tristate "Nova DRM driver"
+ depends on 64BIT
depends on DRM=y
depends on PCI
depends on RUST
select AUXILIARY_BUS
+ select NOVA_CORE
default n
help
Choose this if you want to build the Nova DRM driver for Nvidia
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index 63ddc5127f7b..1c2a1920c0a6 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -10,6 +10,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mode.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "omap_drv.h"
diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c
index a3d470468e5b..9edc1b3f9f95 100644
--- a/drivers/gpu/drm/omapdrm/omap_debugfs.c
+++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c
@@ -11,6 +11,7 @@
#include <drm/drm_file.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include "omap_drv.h"
#include "omap_dmm_tiler.h"
diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
index 3fff32c000a6..bbe427ab43c1 100644
--- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
+++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
@@ -26,6 +26,8 @@
#include <linux/vmalloc.h>
#include <linux/wait.h>
+#include <drm/drm_print.h>
+
#include "omap_dmm_tiler.h"
#include "omap_dmm_priv.h"
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 794267f0f007..1b96343226a5 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -19,6 +19,7 @@
#include <drm/drm_ioctl.h>
#include <drm/drm_panel.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c
index 4dd05bc732da..195715b162e3 100644
--- a/drivers/gpu/drm/omapdrm/omap_encoder.c
+++ b/drivers/gpu/drm/omapdrm/omap_encoder.c
@@ -77,7 +77,6 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
struct omap_dss_device *output = omap_encoder->output;
struct drm_device *dev = encoder->dev;
struct drm_connector *connector;
- struct drm_bridge *bridge;
struct videomode vm = { 0 };
u32 bus_flags;
@@ -97,8 +96,7 @@ static void omap_encoder_mode_set(struct drm_encoder *encoder,
*
* A better solution is to use DRM's bus-flags through the whole driver.
*/
- for (bridge = output->bridge; bridge;
- bridge = drm_bridge_get_next_bridge(bridge)) {
+ drm_for_each_bridge_in_chain_from(output->bridge, bridge) {
if (!bridge->timings)
continue;
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
index bb3105556f19..b8c249ec1891 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -12,6 +12,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include "omap_dmm_tiler.h"
#include "omap_drv.h"
diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
index 948af7ec1130..ca3fb186bf19 100644
--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
+++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
@@ -15,6 +15,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/drm_util.h>
#include "omap_drv.h"
@@ -103,8 +104,6 @@ static void omap_fbdev_fb_destroy(struct fb_info *info)
drm_framebuffer_remove(fb);
drm_client_release(&helper->client);
- drm_fb_helper_unprepare(helper);
- kfree(helper);
}
/*
@@ -155,9 +154,9 @@ int omap_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
struct drm_device *dev = helper->dev;
struct omap_drm_private *priv = dev->dev_private;
struct omap_fbdev *fbdev = priv->fbdev;
+ struct fb_info *fbi = helper->info;
struct drm_framebuffer *fb = NULL;
union omap_gem_size gsize;
- struct fb_info *fbi = NULL;
struct drm_mode_fb_cmd2 mode_cmd = {0};
struct drm_gem_object *bo;
dma_addr_t dma_addr;
@@ -226,13 +225,6 @@ int omap_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
goto fail;
}
- fbi = drm_fb_helper_alloc_info(helper);
- if (IS_ERR(fbi)) {
- dev_err(dev->dev, "failed to allocate fb info\n");
- ret = PTR_ERR(fbi);
- goto fail;
- }
-
DBG("fbi=%p, dev=%p", fbi, dev);
helper->funcs = &omap_fbdev_helper_funcs;
diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c
index 381552bfb409..71e79f53489a 100644
--- a/drivers/gpu/drm/omapdrm/omap_gem.c
+++ b/drivers/gpu/drm/omapdrm/omap_gem.c
@@ -10,7 +10,9 @@
#include <linux/spinlock.h>
#include <linux/vmalloc.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/drm_vma_manager.h>
#include "omap_drv.h"
@@ -580,15 +582,13 @@ static int omap_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struc
int omap_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
- union omap_gem_size gsize;
-
- args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
-
- args->size = PAGE_ALIGN(args->pitch * args->height);
+ union omap_gem_size gsize = { };
+ int ret;
- gsize = (union omap_gem_size){
- .bytes = args->size,
- };
+ ret = drm_mode_size_dumb(dev, args, SZ_8, 0);
+ if (ret)
+ return ret;
+ gsize.bytes = args->size;
return omap_gem_new_handle(dev, file, gsize,
OMAP_BO_SCANOUT | OMAP_BO_WC, &args->handle);
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c
index a6f0bbc879d2..943c5307da00 100644
--- a/drivers/gpu/drm/omapdrm/omap_irq.c
+++ b/drivers/gpu/drm/omapdrm/omap_irq.c
@@ -5,6 +5,7 @@
*/
#include <drm/drm_vblank.h>
+#include <drm/drm_print.h>
#include "omap_drv.h"
diff --git a/drivers/gpu/drm/omapdrm/omap_overlay.c b/drivers/gpu/drm/omapdrm/omap_overlay.c
index fb97c74386f2..6fb7510cbebb 100644
--- a/drivers/gpu/drm/omapdrm/omap_overlay.c
+++ b/drivers/gpu/drm/omapdrm/omap_overlay.c
@@ -6,6 +6,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_print.h>
#include "omap_dmm_tiler.h"
#include "omap_drv.h"
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index 24a2ded08b45..f9698890c989 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -10,6 +10,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include "omap_dmm_tiler.h"
#include "omap_drv.h"
@@ -229,7 +230,7 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
if (!crtc)
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
/* we should have a crtc state if the plane is attached to a crtc */
if (WARN_ON(!crtc_state))
return 0;
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 407c5f6a268b..76f6af819037 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -408,6 +408,19 @@ config DRM_PANEL_LG_LB035Q02
(found on the Gumstix Overo Palo35 board). To compile this driver as
a module, choose M here.
+config DRM_PANEL_LG_LD070WX3
+ tristate "LG LD070WX3 MIPI DSI panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select VIDEOMODE_HELPERS
+ help
+ Say Y here if you want to enable support for the LD070WX3 MIPI DSI
+ panel found in the NVIDIA Tegra Note 7 tablet.
+
+ To compile this driver as a module, choose M here: the module will
+ be called panel-lg-ld070wx3.
+
config DRM_PANEL_LG_LG4573
tristate "LG4573 RGB/SPI panel"
depends on OF && SPI
@@ -801,6 +814,19 @@ config DRM_PANEL_SAMSUNG_S6D7AA0
select DRM_MIPI_DSI
select VIDEOMODE_HELPERS
+config DRM_PANEL_SAMSUNG_S6E3FC2X01
+ tristate "Samsung S6E3FC2X01 DSI panel controller"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select VIDEOMODE_HELPERS
+ help
+ Say Y or M here if you want to enable support for the
+ Samsung S6E3FC2 DDIC and connected MIPI DSI panel.
+ Currently supported panels:
+
+ Samsung AMS641RW (found in the OnePlus 6T smartphone)
+
config DRM_PANEL_SAMSUNG_S6E3HA2
tristate "Samsung S6E3HA2 DSI video mode panel"
depends on OF
@@ -868,16 +894,17 @@ config DRM_PANEL_SAMSUNG_S6E8AA5X01_AMS561RA01
DSI protocol with 4 lanes.
config DRM_PANEL_SAMSUNG_SOFEF00
- tristate "Samsung sofef00/s6e3fc2x01 OnePlus 6/6T DSI cmd mode panels"
+ tristate "Samsung SOFEF00 DSI panel controller"
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
select VIDEOMODE_HELPERS
help
Say Y or M here if you want to enable support for the Samsung AMOLED
- command mode panels found in the OnePlus 6/6T smartphones.
+ panel SOFEF00 DDIC and connected panel.
+ Currently supported panels:
- The panels are 2280x1080@60Hz and 2340x1080@60Hz respectively
+ Samsung AMS628NW01 (found in OnePlus 6, 1080x2280@60Hz)
config DRM_PANEL_SEIKO_43WVF1G
tristate "Seiko 43WVF1G panel"
@@ -888,6 +915,21 @@ config DRM_PANEL_SEIKO_43WVF1G
Say Y here if you want to enable support for the Seiko
43WVF1G controller for 800x480 LCD panels
+config DRM_PANEL_SHARP_LQ079L1SX01
+ tristate "Sharp LQ079L1SX01 panel"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ select VIDEOMODE_HELPERS
+ help
+ Say Y here if you want to enable support for Sharp LQ079L1SX01
+ TFT-LCD modules. The panel has a 2560x1600 resolution and uses
+ 24 bit RGB per pixel. It provides a dual MIPI DSI interface to
+ the host.
+
+ To compile this driver as a module, choose M here: the module
+ will be called panel-sharp-lq079l1sx01.
+
config DRM_PANEL_SHARP_LQ101R1SX01
tristate "Sharp LQ101R1SX01 panel"
depends on OF
@@ -1045,6 +1087,17 @@ config DRM_PANEL_SYNAPTICS_R63353
Say Y if you want to enable support for panels based on the
Synaptics R63353 controller.
+config DRM_PANEL_SYNAPTICS_TDDI
+ tristate "Synaptics TDDI display panels"
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y if you want to enable support for the Synaptics TDDI display
+ panels. There are multiple MIPI DSI panels manufactured under the TDDI
+ namesake, with varying resolutions and data lanes. They also have a
+ built-in LED backlight and a touch controller.
+
config DRM_PANEL_TDO_TL070WSH30
tristate "TDO TL070WSH30 DSI panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 3615a761b44f..b9562a6fdcb3 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W) += panel-leadtek-ltk050h3146w.o
obj-$(CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829) += panel-leadtek-ltk500hd1829.o
obj-$(CONFIG_DRM_PANEL_LINCOLNTECH_LCD197) += panel-lincolntech-lcd197.o
obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
+obj-$(CONFIG_DRM_PANEL_LG_LD070WX3) += panel-lg-ld070wx3.o
obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
@@ -79,6 +80,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D27A1) += panel-samsung-s6d27a1.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D7AA0) += panel-samsung-s6d7aa0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3FA7) += panel-samsung-s6e3fa7.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3FC2X01) += panel-samsung-s6e3fc2x01.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2) += panel-samsung-s6e3ha2.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E3HA8) += panel-samsung-s6e3ha8.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03) += panel-samsung-s6e63j0x03.o
@@ -91,6 +93,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA5X01_AMS561RA01) += panel-samsung-s6e8aa5x01-ams561ra01.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
+obj-$(CONFIG_DRM_PANEL_SHARP_LQ079L1SX01) += panel-sharp-lq079l1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS043T1LE01) += panel-sharp-ls043t1le01.o
@@ -100,6 +103,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
obj-$(CONFIG_DRM_PANEL_SUMMIT) += panel-summit.o
obj-$(CONFIG_DRM_PANEL_SYNAPTICS_R63353) += panel-synaptics-r63353.o
+obj-$(CONFIG_DRM_PANEL_SYNAPTICS_TDDI) += panel-synaptics-tddi.o
obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 62435e3cd9f4..415b894890ad 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -1888,6 +1888,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1ea5, &delay_200_500_e50, "B116XAK01.6"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x203d, &delay_200_500_e50, "B140HTN02.0"),
+ EDP_PANEL_ENTRY('A', 'U', 'O', 0x205c, &delay_200_500_e50, "B116XAN02.0"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x208d, &delay_200_500_e50, "B140HTN02.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"),
@@ -1909,6 +1910,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x8bba, &delay_200_500_e50, "B140UAN08.5"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0xa199, &delay_200_500_e50, "B116XAN06.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0xa7b3, &delay_200_500_e50, "B140UAN04.4"),
+ EDP_PANEL_ENTRY('A', 'U', 'O', 0xb7a9, &delay_200_500_e50, "B140HAK03.3"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0xc4b4, &delay_200_500_e50, "B116XAT04.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0xc9a8, &delay_200_500_e50, "B140QAN08.H"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0xcdba, &delay_200_500_e50, "B140UAX01.2"),
@@ -1963,6 +1965,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a3e, &delay_200_500_e80_d50, "NV116WHM-N49"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a6a, &delay_200_500_e80, "NV140WUM-N44"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a84, &delay_200_500_e50, "NV133WUM-T01"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ac5, &delay_200_500_e50, "NV116WHM-N4C"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ae8, &delay_200_500_e50_p2e80, "NV140WUM-N41"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0b09, &delay_200_500_e50_po2e200, "NV140FHM-NZ"),
@@ -1974,6 +1977,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c20, &delay_200_500_e80, "NT140FHM-N47"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c93, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cb6, &delay_200_500_e200, "NT116WHM-N44"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cf2, &delay_200_500_e200, "NV156FHM-N4S"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cf6, &delay_200_500_e200, "NV140WUM-N64"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d45, &delay_200_500_e80, "NV116WHM-N4B"),
@@ -2007,10 +2011,12 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1441, &delay_200_500_e80_d50, "N140JCA-ELK"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x144f, &delay_200_500_e80_d50, "N140HGA-EA1"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x1468, &delay_200_500_e80, "N140HGA-EA1"),
+ EDP_PANEL_ENTRY('C', 'M', 'N', 0x148f, &delay_200_500_e80, "N140HCA-EAC"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14a8, &delay_200_500_e80, "N140JCA-ELP"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d6, &delay_200_500_e80_d50, "N140BGA-EA4"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x14e5, &delay_200_500_e80_d50, "N140HGA-EA1"),
+ EDP_PANEL_ENTRY('C', 'M', 'N', 0x1565, &delay_200_500_e80, "N156HCA-EAB"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x162b, &delay_200_500_e80_d50, "N160JCE-ELL"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x7402, &delay_200_500_e200_d50, "N116BCA-EAK"),
@@ -2022,10 +2028,12 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1104, &delay_200_500_e50_d100, "MNB601LS1-4"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x143f, &delay_200_500_e50, "MNE007QS3-6"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1448, &delay_200_500_e50, "MNE007QS3-7"),
+ EDP_PANEL_ENTRY('C', 'S', 'W', 0x144b, &delay_200_500_e80, "MNE001BS1-4"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1457, &delay_80_500_e80_p2e200, "MNE007QS3-8"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1462, &delay_200_500_e50, "MNE007QS5-2"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1468, &delay_200_500_e50, "MNE007QB2-2"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"),
+ EDP_PANEL_ENTRY('C', 'S', 'W', 0x1519, &delay_200_500_e80_d50, "MNF601BS1-3"),
EDP_PANEL_ENTRY('E', 'T', 'C', 0x0000, &delay_50_500_e200_d200_po2e335, "LP079QX1-SP0V"),
@@ -2046,6 +2054,8 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('K', 'D', 'B', 0x1212, &delay_200_500_e50, "KD116N0930A16"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x1707, &delay_200_150_e50, "KD116N2130B12"),
+ EDP_PANEL_ENTRY('K', 'D', 'C', 0x0110, &delay_200_500_e50, "KD116N3730A07"),
+ EDP_PANEL_ENTRY('K', 'D', 'C', 0x0397, &delay_200_500_e50, "KD116N3730A12"),
EDP_PANEL_ENTRY('K', 'D', 'C', 0x044f, &delay_200_500_e50, "KD116N9-30NH-F3"),
EDP_PANEL_ENTRY('K', 'D', 'C', 0x05f1, &delay_200_500_e80_d50, "KD116N5-30NV-G7"),
EDP_PANEL_ENTRY('K', 'D', 'C', 0x0809, &delay_200_500_e50, "KD116N2930A15"),
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
index ad4993b2f92a..947b47841b01 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
@@ -100,7 +100,7 @@ static const struct ili9881c_instr lhr050h41_init[] = {
ILI9881C_COMMAND_INSTR(0x13, 0x00),
ILI9881C_COMMAND_INSTR(0x14, 0x00),
ILI9881C_COMMAND_INSTR(0x15, 0x00),
- ILI9881C_COMMAND_INSTR(0x16, 0x0C),
+ ILI9881C_COMMAND_INSTR(0x16, 0x0c),
ILI9881C_COMMAND_INSTR(0x17, 0x00),
ILI9881C_COMMAND_INSTR(0x18, 0x00),
ILI9881C_COMMAND_INSTR(0x19, 0x00),
@@ -108,7 +108,7 @@ static const struct ili9881c_instr lhr050h41_init[] = {
ILI9881C_COMMAND_INSTR(0x1b, 0x00),
ILI9881C_COMMAND_INSTR(0x1c, 0x00),
ILI9881C_COMMAND_INSTR(0x1d, 0x00),
- ILI9881C_COMMAND_INSTR(0x1e, 0xC0),
+ ILI9881C_COMMAND_INSTR(0x1e, 0xc0),
ILI9881C_COMMAND_INSTR(0x1f, 0x80),
ILI9881C_COMMAND_INSTR(0x20, 0x04),
ILI9881C_COMMAND_INSTR(0x21, 0x01),
@@ -134,7 +134,7 @@ static const struct ili9881c_instr lhr050h41_init[] = {
ILI9881C_COMMAND_INSTR(0x35, 0x00),
ILI9881C_COMMAND_INSTR(0x36, 0x00),
ILI9881C_COMMAND_INSTR(0x37, 0x00),
- ILI9881C_COMMAND_INSTR(0x38, 0x3C),
+ ILI9881C_COMMAND_INSTR(0x38, 0x3c),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
ILI9881C_COMMAND_INSTR(0x3a, 0x00),
ILI9881C_COMMAND_INSTR(0x3b, 0x00),
@@ -173,11 +173,11 @@ static const struct ili9881c_instr lhr050h41_init[] = {
ILI9881C_COMMAND_INSTR(0x67, 0x02),
ILI9881C_COMMAND_INSTR(0x68, 0x02),
ILI9881C_COMMAND_INSTR(0x69, 0x02),
- ILI9881C_COMMAND_INSTR(0x6a, 0x0C),
+ ILI9881C_COMMAND_INSTR(0x6a, 0x0c),
ILI9881C_COMMAND_INSTR(0x6b, 0x02),
- ILI9881C_COMMAND_INSTR(0x6c, 0x0F),
- ILI9881C_COMMAND_INSTR(0x6d, 0x0E),
- ILI9881C_COMMAND_INSTR(0x6e, 0x0D),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x6d, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x0d),
ILI9881C_COMMAND_INSTR(0x6f, 0x06),
ILI9881C_COMMAND_INSTR(0x70, 0x07),
ILI9881C_COMMAND_INSTR(0x71, 0x02),
@@ -195,74 +195,74 @@ static const struct ili9881c_instr lhr050h41_init[] = {
ILI9881C_COMMAND_INSTR(0x7d, 0x02),
ILI9881C_COMMAND_INSTR(0x7e, 0x02),
ILI9881C_COMMAND_INSTR(0x7f, 0x02),
- ILI9881C_COMMAND_INSTR(0x80, 0x0C),
+ ILI9881C_COMMAND_INSTR(0x80, 0x0c),
ILI9881C_COMMAND_INSTR(0x81, 0x02),
- ILI9881C_COMMAND_INSTR(0x82, 0x0F),
- ILI9881C_COMMAND_INSTR(0x83, 0x0E),
- ILI9881C_COMMAND_INSTR(0x84, 0x0D),
+ ILI9881C_COMMAND_INSTR(0x82, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x83, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x84, 0x0d),
ILI9881C_COMMAND_INSTR(0x85, 0x06),
ILI9881C_COMMAND_INSTR(0x86, 0x07),
ILI9881C_COMMAND_INSTR(0x87, 0x02),
ILI9881C_COMMAND_INSTR(0x88, 0x02),
ILI9881C_COMMAND_INSTR(0x89, 0x02),
- ILI9881C_COMMAND_INSTR(0x8A, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(4),
- ILI9881C_COMMAND_INSTR(0x6C, 0x15),
- ILI9881C_COMMAND_INSTR(0x6E, 0x22),
- ILI9881C_COMMAND_INSTR(0x6F, 0x33),
- ILI9881C_COMMAND_INSTR(0x3A, 0xA4),
- ILI9881C_COMMAND_INSTR(0x8D, 0x0D),
- ILI9881C_COMMAND_INSTR(0x87, 0xBA),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x15),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x22),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x33),
+ ILI9881C_COMMAND_INSTR(0x3a, 0xa4),
+ ILI9881C_COMMAND_INSTR(0x8d, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x87, 0xba),
ILI9881C_COMMAND_INSTR(0x26, 0x76),
- ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
+ ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
ILI9881C_SWITCH_PAGE_INSTR(1),
- ILI9881C_COMMAND_INSTR(0x22, 0x0A),
- ILI9881C_COMMAND_INSTR(0x53, 0xDC),
- ILI9881C_COMMAND_INSTR(0x55, 0xA7),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x53, 0xdc),
+ ILI9881C_COMMAND_INSTR(0x55, 0xa7),
ILI9881C_COMMAND_INSTR(0x50, 0x78),
ILI9881C_COMMAND_INSTR(0x51, 0x78),
ILI9881C_COMMAND_INSTR(0x31, 0x02),
ILI9881C_COMMAND_INSTR(0x60, 0x14),
- ILI9881C_COMMAND_INSTR(0xA0, 0x2A),
- ILI9881C_COMMAND_INSTR(0xA1, 0x39),
- ILI9881C_COMMAND_INSTR(0xA2, 0x46),
- ILI9881C_COMMAND_INSTR(0xA3, 0x0e),
- ILI9881C_COMMAND_INSTR(0xA4, 0x12),
- ILI9881C_COMMAND_INSTR(0xA5, 0x25),
- ILI9881C_COMMAND_INSTR(0xA6, 0x19),
- ILI9881C_COMMAND_INSTR(0xA7, 0x1d),
- ILI9881C_COMMAND_INSTR(0xA8, 0xa6),
- ILI9881C_COMMAND_INSTR(0xA9, 0x1C),
- ILI9881C_COMMAND_INSTR(0xAA, 0x29),
- ILI9881C_COMMAND_INSTR(0xAB, 0x85),
- ILI9881C_COMMAND_INSTR(0xAC, 0x1C),
- ILI9881C_COMMAND_INSTR(0xAD, 0x1B),
- ILI9881C_COMMAND_INSTR(0xAE, 0x51),
- ILI9881C_COMMAND_INSTR(0xAF, 0x22),
- ILI9881C_COMMAND_INSTR(0xB0, 0x2d),
- ILI9881C_COMMAND_INSTR(0xB1, 0x4f),
- ILI9881C_COMMAND_INSTR(0xB2, 0x59),
- ILI9881C_COMMAND_INSTR(0xB3, 0x3F),
- ILI9881C_COMMAND_INSTR(0xC0, 0x2A),
- ILI9881C_COMMAND_INSTR(0xC1, 0x3a),
- ILI9881C_COMMAND_INSTR(0xC2, 0x45),
- ILI9881C_COMMAND_INSTR(0xC3, 0x0e),
- ILI9881C_COMMAND_INSTR(0xC4, 0x11),
- ILI9881C_COMMAND_INSTR(0xC5, 0x24),
- ILI9881C_COMMAND_INSTR(0xC6, 0x1a),
- ILI9881C_COMMAND_INSTR(0xC7, 0x1c),
- ILI9881C_COMMAND_INSTR(0xC8, 0xaa),
- ILI9881C_COMMAND_INSTR(0xC9, 0x1C),
- ILI9881C_COMMAND_INSTR(0xCA, 0x29),
- ILI9881C_COMMAND_INSTR(0xCB, 0x96),
- ILI9881C_COMMAND_INSTR(0xCC, 0x1C),
- ILI9881C_COMMAND_INSTR(0xCD, 0x1B),
- ILI9881C_COMMAND_INSTR(0xCE, 0x51),
- ILI9881C_COMMAND_INSTR(0xCF, 0x22),
- ILI9881C_COMMAND_INSTR(0xD0, 0x2b),
- ILI9881C_COMMAND_INSTR(0xD1, 0x4b),
- ILI9881C_COMMAND_INSTR(0xD2, 0x59),
- ILI9881C_COMMAND_INSTR(0xD3, 0x3F),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x39),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x46),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x0e),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x12),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x25),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x19),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa8, 0xa6),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x29),
+ ILI9881C_COMMAND_INSTR(0xab, 0x85),
+ ILI9881C_COMMAND_INSTR(0xac, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xad, 0x1b),
+ ILI9881C_COMMAND_INSTR(0xae, 0x51),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x22),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x2d),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x4f),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x59),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x3f),
+ ILI9881C_COMMAND_INSTR(0xc0, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x3a),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x45),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x0e),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x11),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x24),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xc8, 0xaa),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xca, 0x29),
+ ILI9881C_COMMAND_INSTR(0xcb, 0x96),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x1b),
+ ILI9881C_COMMAND_INSTR(0xce, 0x51),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x22),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x2b),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x4b),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x59),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x3f),
};
static const struct ili9881c_instr k101_im2byl02_init[] = {
@@ -276,12 +276,12 @@ static const struct ili9881c_instr k101_im2byl02_init[] = {
ILI9881C_COMMAND_INSTR(0x07, 0x00),
ILI9881C_COMMAND_INSTR(0x08, 0x00),
ILI9881C_COMMAND_INSTR(0x09, 0x00),
- ILI9881C_COMMAND_INSTR(0x0A, 0x01),
- ILI9881C_COMMAND_INSTR(0x0B, 0x01),
- ILI9881C_COMMAND_INSTR(0x0C, 0x00),
- ILI9881C_COMMAND_INSTR(0x0D, 0x01),
- ILI9881C_COMMAND_INSTR(0x0E, 0x01),
- ILI9881C_COMMAND_INSTR(0x0F, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0a, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0b, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0d, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0e, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0f, 0x00),
ILI9881C_COMMAND_INSTR(0x10, 0x00),
ILI9881C_COMMAND_INSTR(0x11, 0x00),
ILI9881C_COMMAND_INSTR(0x12, 0x00),
@@ -292,12 +292,12 @@ static const struct ili9881c_instr k101_im2byl02_init[] = {
ILI9881C_COMMAND_INSTR(0x17, 0x00),
ILI9881C_COMMAND_INSTR(0x18, 0x00),
ILI9881C_COMMAND_INSTR(0x19, 0x00),
- ILI9881C_COMMAND_INSTR(0x1A, 0x00),
- ILI9881C_COMMAND_INSTR(0x1B, 0x00),
- ILI9881C_COMMAND_INSTR(0x1C, 0x00),
- ILI9881C_COMMAND_INSTR(0x1D, 0x00),
- ILI9881C_COMMAND_INSTR(0x1E, 0x40),
- ILI9881C_COMMAND_INSTR(0x1F, 0xC0),
+ ILI9881C_COMMAND_INSTR(0x1a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1e, 0x40),
+ ILI9881C_COMMAND_INSTR(0x1f, 0xc0),
ILI9881C_COMMAND_INSTR(0x20, 0x06),
ILI9881C_COMMAND_INSTR(0x21, 0x01),
ILI9881C_COMMAND_INSTR(0x22, 0x06),
@@ -306,14 +306,14 @@ static const struct ili9881c_instr k101_im2byl02_init[] = {
ILI9881C_COMMAND_INSTR(0x25, 0x88),
ILI9881C_COMMAND_INSTR(0x26, 0x00),
ILI9881C_COMMAND_INSTR(0x27, 0x00),
- ILI9881C_COMMAND_INSTR(0x28, 0x3B),
+ ILI9881C_COMMAND_INSTR(0x28, 0x3b),
ILI9881C_COMMAND_INSTR(0x29, 0x03),
- ILI9881C_COMMAND_INSTR(0x2A, 0x00),
- ILI9881C_COMMAND_INSTR(0x2B, 0x00),
- ILI9881C_COMMAND_INSTR(0x2C, 0x00),
- ILI9881C_COMMAND_INSTR(0x2D, 0x00),
- ILI9881C_COMMAND_INSTR(0x2E, 0x00),
- ILI9881C_COMMAND_INSTR(0x2F, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2f, 0x00),
ILI9881C_COMMAND_INSTR(0x30, 0x00),
ILI9881C_COMMAND_INSTR(0x31, 0x00),
ILI9881C_COMMAND_INSTR(0x32, 0x00),
@@ -324,12 +324,12 @@ static const struct ili9881c_instr k101_im2byl02_init[] = {
ILI9881C_COMMAND_INSTR(0x37, 0x00),
ILI9881C_COMMAND_INSTR(0x38, 0x00),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
- ILI9881C_COMMAND_INSTR(0x3A, 0x00),
- ILI9881C_COMMAND_INSTR(0x3B, 0x00),
- ILI9881C_COMMAND_INSTR(0x3C, 0x00),
- ILI9881C_COMMAND_INSTR(0x3D, 0x00),
- ILI9881C_COMMAND_INSTR(0x3E, 0x00),
- ILI9881C_COMMAND_INSTR(0x3F, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3f, 0x00),
ILI9881C_COMMAND_INSTR(0x40, 0x00),
ILI9881C_COMMAND_INSTR(0x41, 0x00),
ILI9881C_COMMAND_INSTR(0x42, 0x00),
@@ -340,17 +340,17 @@ static const struct ili9881c_instr k101_im2byl02_init[] = {
ILI9881C_COMMAND_INSTR(0x52, 0x45),
ILI9881C_COMMAND_INSTR(0x53, 0x67),
ILI9881C_COMMAND_INSTR(0x54, 0x89),
- ILI9881C_COMMAND_INSTR(0x55, 0xAB),
+ ILI9881C_COMMAND_INSTR(0x55, 0xab),
ILI9881C_COMMAND_INSTR(0x56, 0x01),
ILI9881C_COMMAND_INSTR(0x57, 0x23),
ILI9881C_COMMAND_INSTR(0x58, 0x45),
ILI9881C_COMMAND_INSTR(0x59, 0x67),
- ILI9881C_COMMAND_INSTR(0x5A, 0x89),
- ILI9881C_COMMAND_INSTR(0x5B, 0xAB),
- ILI9881C_COMMAND_INSTR(0x5C, 0xCD),
- ILI9881C_COMMAND_INSTR(0x5D, 0xEF),
- ILI9881C_COMMAND_INSTR(0x5E, 0x00),
- ILI9881C_COMMAND_INSTR(0x5F, 0x01),
+ ILI9881C_COMMAND_INSTR(0x5a, 0x89),
+ ILI9881C_COMMAND_INSTR(0x5b, 0xab),
+ ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
+ ILI9881C_COMMAND_INSTR(0x5d, 0xef),
+ ILI9881C_COMMAND_INSTR(0x5e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x5f, 0x01),
ILI9881C_COMMAND_INSTR(0x60, 0x01),
ILI9881C_COMMAND_INSTR(0x61, 0x06),
ILI9881C_COMMAND_INSTR(0x62, 0x06),
@@ -361,101 +361,101 @@ static const struct ili9881c_instr k101_im2byl02_init[] = {
ILI9881C_COMMAND_INSTR(0x67, 0x02),
ILI9881C_COMMAND_INSTR(0x68, 0x02),
ILI9881C_COMMAND_INSTR(0x69, 0x05),
- ILI9881C_COMMAND_INSTR(0x6A, 0x05),
- ILI9881C_COMMAND_INSTR(0x6B, 0x02),
- ILI9881C_COMMAND_INSTR(0x6C, 0x0D),
- ILI9881C_COMMAND_INSTR(0x6D, 0x0D),
- ILI9881C_COMMAND_INSTR(0x6E, 0x0C),
- ILI9881C_COMMAND_INSTR(0x6F, 0x0C),
- ILI9881C_COMMAND_INSTR(0x70, 0x0F),
- ILI9881C_COMMAND_INSTR(0x71, 0x0F),
- ILI9881C_COMMAND_INSTR(0x72, 0x0E),
- ILI9881C_COMMAND_INSTR(0x73, 0x0E),
+ ILI9881C_COMMAND_INSTR(0x6a, 0x05),
+ ILI9881C_COMMAND_INSTR(0x6b, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x6d, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x70, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x71, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x72, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x73, 0x0e),
ILI9881C_COMMAND_INSTR(0x74, 0x02),
ILI9881C_COMMAND_INSTR(0x75, 0x01),
ILI9881C_COMMAND_INSTR(0x76, 0x01),
ILI9881C_COMMAND_INSTR(0x77, 0x06),
ILI9881C_COMMAND_INSTR(0x78, 0x06),
ILI9881C_COMMAND_INSTR(0x79, 0x07),
- ILI9881C_COMMAND_INSTR(0x7A, 0x07),
- ILI9881C_COMMAND_INSTR(0x7B, 0x00),
- ILI9881C_COMMAND_INSTR(0x7C, 0x00),
- ILI9881C_COMMAND_INSTR(0x7D, 0x02),
- ILI9881C_COMMAND_INSTR(0x7E, 0x02),
- ILI9881C_COMMAND_INSTR(0x7F, 0x05),
+ ILI9881C_COMMAND_INSTR(0x7a, 0x07),
+ ILI9881C_COMMAND_INSTR(0x7b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x7c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x7d, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7f, 0x05),
ILI9881C_COMMAND_INSTR(0x80, 0x05),
ILI9881C_COMMAND_INSTR(0x81, 0x02),
- ILI9881C_COMMAND_INSTR(0x82, 0x0D),
- ILI9881C_COMMAND_INSTR(0x83, 0x0D),
- ILI9881C_COMMAND_INSTR(0x84, 0x0C),
- ILI9881C_COMMAND_INSTR(0x85, 0x0C),
- ILI9881C_COMMAND_INSTR(0x86, 0x0F),
- ILI9881C_COMMAND_INSTR(0x87, 0x0F),
- ILI9881C_COMMAND_INSTR(0x88, 0x0E),
- ILI9881C_COMMAND_INSTR(0x89, 0x0E),
- ILI9881C_COMMAND_INSTR(0x8A, 0x02),
+ ILI9881C_COMMAND_INSTR(0x82, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x83, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x84, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x85, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x86, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x87, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x88, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x89, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(4),
- ILI9881C_COMMAND_INSTR(0x3B, 0xC0), /* ILI4003D sel */
- ILI9881C_COMMAND_INSTR(0x6C, 0x15), /* Set VCORE voltage = 1.5V */
- ILI9881C_COMMAND_INSTR(0x6E, 0x2A), /* di_pwr_reg=0 for power mode 2A, VGH clamp 18V */
- ILI9881C_COMMAND_INSTR(0x6F, 0x33), /* pumping ratio VGH=5x VGL=-3x */
- ILI9881C_COMMAND_INSTR(0x8D, 0x1B), /* VGL clamp -10V */
- ILI9881C_COMMAND_INSTR(0x87, 0xBA), /* ESD */
- ILI9881C_COMMAND_INSTR(0x3A, 0x24), /* POWER SAVING */
+ ILI9881C_COMMAND_INSTR(0x3b, 0xc0), /* ILI4003D sel */
+ ILI9881C_COMMAND_INSTR(0x6c, 0x15), /* Set VCORE voltage = 1.5V */
+ ILI9881C_COMMAND_INSTR(0x6e, 0x2a), /* di_pwr_reg=0 for power mode 2A, VGH clamp 18V */
+ ILI9881C_COMMAND_INSTR(0x6f, 0x33), /* pumping ratio VGH=5x VGL=-3x */
+ ILI9881C_COMMAND_INSTR(0x8d, 0x1b), /* VGL clamp -10V */
+ ILI9881C_COMMAND_INSTR(0x87, 0xba), /* ESD */
+ ILI9881C_COMMAND_INSTR(0x3a, 0x24), /* POWER SAVING */
ILI9881C_COMMAND_INSTR(0x26, 0x76),
- ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
+ ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
ILI9881C_SWITCH_PAGE_INSTR(1),
- ILI9881C_COMMAND_INSTR(0x22, 0x0A), /* BGR, SS */
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a), /* BGR, SS */
ILI9881C_COMMAND_INSTR(0x31, 0x00), /* Zigzag type3 inversion */
ILI9881C_COMMAND_INSTR(0x40, 0x53), /* ILI4003D sel */
ILI9881C_COMMAND_INSTR(0x43, 0x66),
- ILI9881C_COMMAND_INSTR(0x53, 0x4C),
+ ILI9881C_COMMAND_INSTR(0x53, 0x4c),
ILI9881C_COMMAND_INSTR(0x50, 0x87),
ILI9881C_COMMAND_INSTR(0x51, 0x82),
ILI9881C_COMMAND_INSTR(0x60, 0x15),
ILI9881C_COMMAND_INSTR(0x61, 0x01),
- ILI9881C_COMMAND_INSTR(0x62, 0x0C),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0c),
ILI9881C_COMMAND_INSTR(0x63, 0x00),
- ILI9881C_COMMAND_INSTR(0xA0, 0x00),
- ILI9881C_COMMAND_INSTR(0xA1, 0x13), /* VP251 */
- ILI9881C_COMMAND_INSTR(0xA2, 0x23), /* VP247 */
- ILI9881C_COMMAND_INSTR(0xA3, 0x14), /* VP243 */
- ILI9881C_COMMAND_INSTR(0xA4, 0x16), /* VP239 */
- ILI9881C_COMMAND_INSTR(0xA5, 0x29), /* VP231 */
- ILI9881C_COMMAND_INSTR(0xA6, 0x1E), /* VP219 */
- ILI9881C_COMMAND_INSTR(0xA7, 0x1D), /* VP203 */
- ILI9881C_COMMAND_INSTR(0xA8, 0x86), /* VP175 */
- ILI9881C_COMMAND_INSTR(0xA9, 0x1E), /* VP144 */
- ILI9881C_COMMAND_INSTR(0xAA, 0x29), /* VP111 */
- ILI9881C_COMMAND_INSTR(0xAB, 0x74), /* VP80 */
- ILI9881C_COMMAND_INSTR(0xAC, 0x19), /* VP52 */
- ILI9881C_COMMAND_INSTR(0xAD, 0x17), /* VP36 */
- ILI9881C_COMMAND_INSTR(0xAE, 0x4B), /* VP24 */
- ILI9881C_COMMAND_INSTR(0xAF, 0x20), /* VP16 */
- ILI9881C_COMMAND_INSTR(0xB0, 0x26), /* VP12 */
- ILI9881C_COMMAND_INSTR(0xB1, 0x4C), /* VP8 */
- ILI9881C_COMMAND_INSTR(0xB2, 0x5D), /* VP4 */
- ILI9881C_COMMAND_INSTR(0xB3, 0x3F), /* VP0 */
- ILI9881C_COMMAND_INSTR(0xC0, 0x00), /* VN255 GAMMA N */
- ILI9881C_COMMAND_INSTR(0xC1, 0x13), /* VN251 */
- ILI9881C_COMMAND_INSTR(0xC2, 0x23), /* VN247 */
- ILI9881C_COMMAND_INSTR(0xC3, 0x14), /* VN243 */
- ILI9881C_COMMAND_INSTR(0xC4, 0x16), /* VN239 */
- ILI9881C_COMMAND_INSTR(0xC5, 0x29), /* VN231 */
- ILI9881C_COMMAND_INSTR(0xC6, 0x1E), /* VN219 */
- ILI9881C_COMMAND_INSTR(0xC7, 0x1D), /* VN203 */
- ILI9881C_COMMAND_INSTR(0xC8, 0x86), /* VN175 */
- ILI9881C_COMMAND_INSTR(0xC9, 0x1E), /* VN144 */
- ILI9881C_COMMAND_INSTR(0xCA, 0x29), /* VN111 */
- ILI9881C_COMMAND_INSTR(0xCB, 0x74), /* VN80 */
- ILI9881C_COMMAND_INSTR(0xCC, 0x19), /* VN52 */
- ILI9881C_COMMAND_INSTR(0xCD, 0x17), /* VN36 */
- ILI9881C_COMMAND_INSTR(0xCE, 0x4B), /* VN24 */
- ILI9881C_COMMAND_INSTR(0xCF, 0x20), /* VN16 */
- ILI9881C_COMMAND_INSTR(0xD0, 0x26), /* VN12 */
- ILI9881C_COMMAND_INSTR(0xD1, 0x4C), /* VN8 */
- ILI9881C_COMMAND_INSTR(0xD2, 0x5D), /* VN4 */
- ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */
+ ILI9881C_COMMAND_INSTR(0xa0, 0x00),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x13), /* VP251 */
+ ILI9881C_COMMAND_INSTR(0xa2, 0x23), /* VP247 */
+ ILI9881C_COMMAND_INSTR(0xa3, 0x14), /* VP243 */
+ ILI9881C_COMMAND_INSTR(0xa4, 0x16), /* VP239 */
+ ILI9881C_COMMAND_INSTR(0xa5, 0x29), /* VP231 */
+ ILI9881C_COMMAND_INSTR(0xa6, 0x1e), /* VP219 */
+ ILI9881C_COMMAND_INSTR(0xa7, 0x1d), /* VP203 */
+ ILI9881C_COMMAND_INSTR(0xa8, 0x86), /* VP175 */
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1e), /* VP144 */
+ ILI9881C_COMMAND_INSTR(0xaa, 0x29), /* VP111 */
+ ILI9881C_COMMAND_INSTR(0xab, 0x74), /* VP80 */
+ ILI9881C_COMMAND_INSTR(0xac, 0x19), /* VP52 */
+ ILI9881C_COMMAND_INSTR(0xad, 0x17), /* VP36 */
+ ILI9881C_COMMAND_INSTR(0xae, 0x4b), /* VP24 */
+ ILI9881C_COMMAND_INSTR(0xaf, 0x20), /* VP16 */
+ ILI9881C_COMMAND_INSTR(0xb0, 0x26), /* VP12 */
+ ILI9881C_COMMAND_INSTR(0xb1, 0x4c), /* VP8 */
+ ILI9881C_COMMAND_INSTR(0xb2, 0x5d), /* VP4 */
+ ILI9881C_COMMAND_INSTR(0xb3, 0x3f), /* VP0 */
+ ILI9881C_COMMAND_INSTR(0xc0, 0x00), /* VN255 GAMMA N */
+ ILI9881C_COMMAND_INSTR(0xc1, 0x13), /* VN251 */
+ ILI9881C_COMMAND_INSTR(0xc2, 0x23), /* VN247 */
+ ILI9881C_COMMAND_INSTR(0xc3, 0x14), /* VN243 */
+ ILI9881C_COMMAND_INSTR(0xc4, 0x16), /* VN239 */
+ ILI9881C_COMMAND_INSTR(0xc5, 0x29), /* VN231 */
+ ILI9881C_COMMAND_INSTR(0xc6, 0x1e), /* VN219 */
+ ILI9881C_COMMAND_INSTR(0xc7, 0x1d), /* VN203 */
+ ILI9881C_COMMAND_INSTR(0xc8, 0x86), /* VN175 */
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1e), /* VN144 */
+ ILI9881C_COMMAND_INSTR(0xca, 0x29), /* VN111 */
+ ILI9881C_COMMAND_INSTR(0xcb, 0x74), /* VN80 */
+ ILI9881C_COMMAND_INSTR(0xcc, 0x19), /* VN52 */
+ ILI9881C_COMMAND_INSTR(0xcd, 0x17), /* VN36 */
+ ILI9881C_COMMAND_INSTR(0xce, 0x4b), /* VN24 */
+ ILI9881C_COMMAND_INSTR(0xcf, 0x20), /* VN16 */
+ ILI9881C_COMMAND_INSTR(0xd0, 0x26), /* VN12 */
+ ILI9881C_COMMAND_INSTR(0xd1, 0x4c), /* VN8 */
+ ILI9881C_COMMAND_INSTR(0xd2, 0x5d), /* VN4 */
+ ILI9881C_COMMAND_INSTR(0xd3, 0x3f), /* VN0 */
};
static const struct ili9881c_instr kd050hdfia020_init[] = {
@@ -517,7 +517,7 @@ static const struct ili9881c_instr kd050hdfia020_init[] = {
ILI9881C_COMMAND_INSTR(0x35, 0x00),
ILI9881C_COMMAND_INSTR(0x36, 0x00),
ILI9881C_COMMAND_INSTR(0x37, 0x00),
- ILI9881C_COMMAND_INSTR(0x38, 0x3C),
+ ILI9881C_COMMAND_INSTR(0x38, 0x3c),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
ILI9881C_COMMAND_INSTR(0x3a, 0x40),
ILI9881C_COMMAND_INSTR(0x3b, 0x40),
@@ -549,10 +549,10 @@ static const struct ili9881c_instr kd050hdfia020_init[] = {
ILI9881C_COMMAND_INSTR(0x60, 0x00),
ILI9881C_COMMAND_INSTR(0x61, 0x15),
ILI9881C_COMMAND_INSTR(0x62, 0x14),
- ILI9881C_COMMAND_INSTR(0x63, 0x0E),
- ILI9881C_COMMAND_INSTR(0x64, 0x0F),
- ILI9881C_COMMAND_INSTR(0x65, 0x0C),
- ILI9881C_COMMAND_INSTR(0x66, 0x0D),
+ ILI9881C_COMMAND_INSTR(0x63, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x64, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x65, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x66, 0x0d),
ILI9881C_COMMAND_INSTR(0x67, 0x06),
ILI9881C_COMMAND_INSTR(0x68, 0x02),
ILI9881C_COMMAND_INSTR(0x69, 0x07),
@@ -571,10 +571,10 @@ static const struct ili9881c_instr kd050hdfia020_init[] = {
ILI9881C_COMMAND_INSTR(0x76, 0x00),
ILI9881C_COMMAND_INSTR(0x77, 0x14),
ILI9881C_COMMAND_INSTR(0x78, 0x15),
- ILI9881C_COMMAND_INSTR(0x79, 0x0E),
- ILI9881C_COMMAND_INSTR(0x7a, 0x0F),
- ILI9881C_COMMAND_INSTR(0x7b, 0x0C),
- ILI9881C_COMMAND_INSTR(0x7c, 0x0D),
+ ILI9881C_COMMAND_INSTR(0x79, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x7a, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x7b, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x7c, 0x0d),
ILI9881C_COMMAND_INSTR(0x7d, 0x06),
ILI9881C_COMMAND_INSTR(0x7e, 0x02),
ILI9881C_COMMAND_INSTR(0x7f, 0x07),
@@ -587,71 +587,71 @@ static const struct ili9881c_instr kd050hdfia020_init[] = {
ILI9881C_COMMAND_INSTR(0x87, 0x02),
ILI9881C_COMMAND_INSTR(0x88, 0x02),
ILI9881C_COMMAND_INSTR(0x89, 0x02),
- ILI9881C_COMMAND_INSTR(0x8A, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(0x4),
- ILI9881C_COMMAND_INSTR(0x6C, 0x15),
- ILI9881C_COMMAND_INSTR(0x6E, 0x2A),
- ILI9881C_COMMAND_INSTR(0x6F, 0x33),
- ILI9881C_COMMAND_INSTR(0x3A, 0x94),
- ILI9881C_COMMAND_INSTR(0x8D, 0x15),
- ILI9881C_COMMAND_INSTR(0x87, 0xBA),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x15),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x2a),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x33),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x94),
+ ILI9881C_COMMAND_INSTR(0x8d, 0x15),
+ ILI9881C_COMMAND_INSTR(0x87, 0xba),
ILI9881C_COMMAND_INSTR(0x26, 0x76),
- ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
- ILI9881C_COMMAND_INSTR(0xB5, 0x06),
+ ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
+ ILI9881C_COMMAND_INSTR(0xb5, 0x06),
ILI9881C_SWITCH_PAGE_INSTR(0x1),
- ILI9881C_COMMAND_INSTR(0x22, 0x0A),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
ILI9881C_COMMAND_INSTR(0x31, 0x00),
ILI9881C_COMMAND_INSTR(0x53, 0x90),
- ILI9881C_COMMAND_INSTR(0x55, 0xA2),
- ILI9881C_COMMAND_INSTR(0x50, 0xB7),
- ILI9881C_COMMAND_INSTR(0x51, 0xB7),
+ ILI9881C_COMMAND_INSTR(0x55, 0xa2),
+ ILI9881C_COMMAND_INSTR(0x50, 0xb7),
+ ILI9881C_COMMAND_INSTR(0x51, 0xb7),
ILI9881C_COMMAND_INSTR(0x60, 0x22),
ILI9881C_COMMAND_INSTR(0x61, 0x00),
ILI9881C_COMMAND_INSTR(0x62, 0x19),
ILI9881C_COMMAND_INSTR(0x63, 0x10),
- ILI9881C_COMMAND_INSTR(0xA0, 0x08),
- ILI9881C_COMMAND_INSTR(0xA1, 0x1A),
- ILI9881C_COMMAND_INSTR(0xA2, 0x27),
- ILI9881C_COMMAND_INSTR(0xA3, 0x15),
- ILI9881C_COMMAND_INSTR(0xA4, 0x17),
- ILI9881C_COMMAND_INSTR(0xA5, 0x2A),
- ILI9881C_COMMAND_INSTR(0xA6, 0x1E),
- ILI9881C_COMMAND_INSTR(0xA7, 0x1F),
- ILI9881C_COMMAND_INSTR(0xA8, 0x8B),
- ILI9881C_COMMAND_INSTR(0xA9, 0x1B),
- ILI9881C_COMMAND_INSTR(0xAA, 0x27),
- ILI9881C_COMMAND_INSTR(0xAB, 0x78),
- ILI9881C_COMMAND_INSTR(0xAC, 0x18),
- ILI9881C_COMMAND_INSTR(0xAD, 0x18),
- ILI9881C_COMMAND_INSTR(0xAE, 0x4C),
- ILI9881C_COMMAND_INSTR(0xAF, 0x21),
- ILI9881C_COMMAND_INSTR(0xB0, 0x27),
- ILI9881C_COMMAND_INSTR(0xB1, 0x54),
- ILI9881C_COMMAND_INSTR(0xB2, 0x67),
- ILI9881C_COMMAND_INSTR(0xB3, 0x39),
- ILI9881C_COMMAND_INSTR(0xC0, 0x08),
- ILI9881C_COMMAND_INSTR(0xC1, 0x1A),
- ILI9881C_COMMAND_INSTR(0xC2, 0x27),
- ILI9881C_COMMAND_INSTR(0xC3, 0x15),
- ILI9881C_COMMAND_INSTR(0xC4, 0x17),
- ILI9881C_COMMAND_INSTR(0xC5, 0x2A),
- ILI9881C_COMMAND_INSTR(0xC6, 0x1E),
- ILI9881C_COMMAND_INSTR(0xC7, 0x1F),
- ILI9881C_COMMAND_INSTR(0xC8, 0x8B),
- ILI9881C_COMMAND_INSTR(0xC9, 0x1B),
- ILI9881C_COMMAND_INSTR(0xCA, 0x27),
- ILI9881C_COMMAND_INSTR(0xCB, 0x78),
- ILI9881C_COMMAND_INSTR(0xCC, 0x18),
- ILI9881C_COMMAND_INSTR(0xCD, 0x18),
- ILI9881C_COMMAND_INSTR(0xCE, 0x4C),
- ILI9881C_COMMAND_INSTR(0xCF, 0x21),
- ILI9881C_COMMAND_INSTR(0xD0, 0x27),
- ILI9881C_COMMAND_INSTR(0xD1, 0x54),
- ILI9881C_COMMAND_INSTR(0xD2, 0x67),
- ILI9881C_COMMAND_INSTR(0xD3, 0x39),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x27),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x15),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x17),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x1e),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x1f),
+ ILI9881C_COMMAND_INSTR(0xa8, 0x8b),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1b),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x27),
+ ILI9881C_COMMAND_INSTR(0xab, 0x78),
+ ILI9881C_COMMAND_INSTR(0xac, 0x18),
+ ILI9881C_COMMAND_INSTR(0xad, 0x18),
+ ILI9881C_COMMAND_INSTR(0xae, 0x4c),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x21),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x54),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x67),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x39),
+ ILI9881C_COMMAND_INSTR(0xc0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x27),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x15),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x17),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x1e),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x1f),
+ ILI9881C_COMMAND_INSTR(0xc8, 0x8b),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1b),
+ ILI9881C_COMMAND_INSTR(0xca, 0x27),
+ ILI9881C_COMMAND_INSTR(0xcb, 0x78),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x18),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x18),
+ ILI9881C_COMMAND_INSTR(0xce, 0x4c),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x21),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x54),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x67),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x39),
ILI9881C_SWITCH_PAGE_INSTR(0),
ILI9881C_COMMAND_INSTR(0x35, 0x00),
- ILI9881C_COMMAND_INSTR(0x3A, 0x7),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x7),
};
static const struct ili9881c_instr tl050hdv35_init[] = {
@@ -696,7 +696,7 @@ static const struct ili9881c_instr tl050hdv35_init[] = {
ILI9881C_COMMAND_INSTR(0x35, 0x00),
ILI9881C_COMMAND_INSTR(0x36, 0x00),
ILI9881C_COMMAND_INSTR(0x37, 0x00),
- ILI9881C_COMMAND_INSTR(0x38, 0x3C),
+ ILI9881C_COMMAND_INSTR(0x38, 0x3c),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
ILI9881C_COMMAND_INSTR(0x3a, 0x40),
ILI9881C_COMMAND_INSTR(0x3b, 0x40),
@@ -750,7 +750,7 @@ static const struct ili9881c_instr tl050hdv35_init[] = {
ILI9881C_COMMAND_INSTR(0x7f, 0x07),
ILI9881C_COMMAND_INSTR(0x88, 0x02),
ILI9881C_COMMAND_INSTR(0x89, 0x02),
- ILI9881C_COMMAND_INSTR(0x8A, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(4),
ILI9881C_COMMAND_INSTR(0x38, 0x01),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
@@ -820,6 +820,204 @@ static const struct ili9881c_instr tl050hdv35_init[] = {
ILI9881C_COMMAND_INSTR(0xd3, 0x39),
};
+static const struct ili9881c_instr w552946aaa_init[] = {
+ ILI9881C_SWITCH_PAGE_INSTR(3),
+ ILI9881C_COMMAND_INSTR(0x01, 0x00),
+ ILI9881C_COMMAND_INSTR(0x02, 0x00),
+ ILI9881C_COMMAND_INSTR(0x03, 0x53),
+ ILI9881C_COMMAND_INSTR(0x04, 0x53),
+ ILI9881C_COMMAND_INSTR(0x05, 0x13),
+ ILI9881C_COMMAND_INSTR(0x06, 0x04),
+ ILI9881C_COMMAND_INSTR(0x07, 0x02),
+ ILI9881C_COMMAND_INSTR(0x08, 0x02),
+ ILI9881C_COMMAND_INSTR(0x09, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0f, 0x00),
+ ILI9881C_COMMAND_INSTR(0x10, 0x00),
+ ILI9881C_COMMAND_INSTR(0x11, 0x00),
+ ILI9881C_COMMAND_INSTR(0x12, 0x00),
+ ILI9881C_COMMAND_INSTR(0x13, 0x00),
+ ILI9881C_COMMAND_INSTR(0x14, 0x00),
+ ILI9881C_COMMAND_INSTR(0x15, 0x08),
+ ILI9881C_COMMAND_INSTR(0x16, 0x10),
+ ILI9881C_COMMAND_INSTR(0x17, 0x00),
+ ILI9881C_COMMAND_INSTR(0x18, 0x08),
+ ILI9881C_COMMAND_INSTR(0x19, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1e, 0xc0),
+ ILI9881C_COMMAND_INSTR(0x1f, 0x80),
+ ILI9881C_COMMAND_INSTR(0x20, 0x02),
+ ILI9881C_COMMAND_INSTR(0x21, 0x09),
+ ILI9881C_COMMAND_INSTR(0x22, 0x00),
+ ILI9881C_COMMAND_INSTR(0x23, 0x00),
+ ILI9881C_COMMAND_INSTR(0x24, 0x00),
+ ILI9881C_COMMAND_INSTR(0x25, 0x00),
+ ILI9881C_COMMAND_INSTR(0x26, 0x00),
+ ILI9881C_COMMAND_INSTR(0x27, 0x00),
+ ILI9881C_COMMAND_INSTR(0x28, 0x55),
+ ILI9881C_COMMAND_INSTR(0x29, 0x03),
+ ILI9881C_COMMAND_INSTR(0x2a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2f, 0x00),
+ ILI9881C_COMMAND_INSTR(0x30, 0x00),
+ ILI9881C_COMMAND_INSTR(0x31, 0x00),
+ ILI9881C_COMMAND_INSTR(0x32, 0x00),
+ ILI9881C_COMMAND_INSTR(0x33, 0x00),
+ ILI9881C_COMMAND_INSTR(0x34, 0x04),
+ ILI9881C_COMMAND_INSTR(0x35, 0x05),
+ ILI9881C_COMMAND_INSTR(0x36, 0x05),
+ ILI9881C_COMMAND_INSTR(0x37, 0x00),
+ ILI9881C_COMMAND_INSTR(0x38, 0x3c),
+ ILI9881C_COMMAND_INSTR(0x39, 0x35),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3b, 0x40),
+ ILI9881C_COMMAND_INSTR(0x3c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3f, 0x00),
+ ILI9881C_COMMAND_INSTR(0x40, 0x00),
+ ILI9881C_COMMAND_INSTR(0x41, 0x88),
+ ILI9881C_COMMAND_INSTR(0x42, 0x00),
+ ILI9881C_COMMAND_INSTR(0x43, 0x00),
+ ILI9881C_COMMAND_INSTR(0x44, 0x1f),
+ ILI9881C_COMMAND_INSTR(0x50, 0x01),
+ ILI9881C_COMMAND_INSTR(0x51, 0x23),
+ ILI9881C_COMMAND_INSTR(0x52, 0x45),
+ ILI9881C_COMMAND_INSTR(0x53, 0x67),
+ ILI9881C_COMMAND_INSTR(0x54, 0x89),
+ ILI9881C_COMMAND_INSTR(0x55, 0xab),
+ ILI9881C_COMMAND_INSTR(0x56, 0x01),
+ ILI9881C_COMMAND_INSTR(0x57, 0x23),
+ ILI9881C_COMMAND_INSTR(0x58, 0x45),
+ ILI9881C_COMMAND_INSTR(0x59, 0x67),
+ ILI9881C_COMMAND_INSTR(0x5a, 0x89),
+ ILI9881C_COMMAND_INSTR(0x5b, 0xab),
+ ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
+ ILI9881C_COMMAND_INSTR(0x5d, 0xef),
+ ILI9881C_COMMAND_INSTR(0x5e, 0x03),
+ ILI9881C_COMMAND_INSTR(0x5f, 0x14),
+ ILI9881C_COMMAND_INSTR(0x60, 0x15),
+ ILI9881C_COMMAND_INSTR(0x61, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x63, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x64, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x65, 0x10),
+ ILI9881C_COMMAND_INSTR(0x66, 0x11),
+ ILI9881C_COMMAND_INSTR(0x67, 0x08),
+ ILI9881C_COMMAND_INSTR(0x68, 0x02),
+ ILI9881C_COMMAND_INSTR(0x69, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x6a, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6b, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6d, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x02),
+ ILI9881C_COMMAND_INSTR(0x70, 0x02),
+ ILI9881C_COMMAND_INSTR(0x71, 0x02),
+ ILI9881C_COMMAND_INSTR(0x72, 0x06),
+ ILI9881C_COMMAND_INSTR(0x73, 0x02),
+ ILI9881C_COMMAND_INSTR(0x74, 0x02),
+ ILI9881C_COMMAND_INSTR(0x75, 0x14),
+ ILI9881C_COMMAND_INSTR(0x76, 0x15),
+ ILI9881C_COMMAND_INSTR(0x77, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x78, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x79, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x7a, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x7b, 0x11),
+ ILI9881C_COMMAND_INSTR(0x7c, 0x10),
+ ILI9881C_COMMAND_INSTR(0x7d, 0x06),
+ ILI9881C_COMMAND_INSTR(0x7e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7f, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x80, 0x02),
+ ILI9881C_COMMAND_INSTR(0x81, 0x02),
+ ILI9881C_COMMAND_INSTR(0x82, 0x02),
+ ILI9881C_COMMAND_INSTR(0x83, 0x02),
+ ILI9881C_COMMAND_INSTR(0x84, 0x02),
+ ILI9881C_COMMAND_INSTR(0x85, 0x02),
+ ILI9881C_COMMAND_INSTR(0x86, 0x02),
+ ILI9881C_COMMAND_INSTR(0x87, 0x02),
+ ILI9881C_COMMAND_INSTR(0x88, 0x08),
+ ILI9881C_COMMAND_INSTR(0x89, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
+ ILI9881C_SWITCH_PAGE_INSTR(4),
+ ILI9881C_COMMAND_INSTR(0x00, 0x80),
+ ILI9881C_COMMAND_INSTR(0x70, 0x00),
+ ILI9881C_COMMAND_INSTR(0x71, 0x00),
+ ILI9881C_COMMAND_INSTR(0x66, 0xfe),
+ ILI9881C_COMMAND_INSTR(0x82, 0x15),
+ ILI9881C_COMMAND_INSTR(0x84, 0x15),
+ ILI9881C_COMMAND_INSTR(0x85, 0x15),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x24),
+ ILI9881C_COMMAND_INSTR(0x32, 0xac),
+ ILI9881C_COMMAND_INSTR(0x8c, 0x80),
+ ILI9881C_COMMAND_INSTR(0x3c, 0xf5),
+ ILI9881C_COMMAND_INSTR(0x88, 0x33),
+ ILI9881C_SWITCH_PAGE_INSTR(1),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x31, 0x00),
+ ILI9881C_COMMAND_INSTR(0x53, 0x78),
+ ILI9881C_COMMAND_INSTR(0x55, 0x7b),
+ ILI9881C_COMMAND_INSTR(0x60, 0x20),
+ ILI9881C_COMMAND_INSTR(0x61, 0x00),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x63, 0x00),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x00),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x10),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x13),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x26),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa8, 0x67),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x29),
+ ILI9881C_COMMAND_INSTR(0xab, 0x5b),
+ ILI9881C_COMMAND_INSTR(0xac, 0x26),
+ ILI9881C_COMMAND_INSTR(0xad, 0x28),
+ ILI9881C_COMMAND_INSTR(0xae, 0x5c),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x30),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x31),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x32),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x00),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x2e),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x32),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x00),
+ ILI9881C_COMMAND_INSTR(0xb6, 0x02),
+ ILI9881C_COMMAND_INSTR(0xb7, 0x03),
+ ILI9881C_COMMAND_INSTR(0xc0, 0x00),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x10),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x13),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x26),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xc8, 0x67),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xca, 0x29),
+ ILI9881C_COMMAND_INSTR(0xcb, 0x5b),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x26),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x28),
+ ILI9881C_COMMAND_INSTR(0xce, 0x5c),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x30),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x31),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x2e),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x32),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x00),
+ ILI9881C_SWITCH_PAGE_INSTR(0),
+};
+
static const struct ili9881c_instr w552946ab_init[] = {
ILI9881C_SWITCH_PAGE_INSTR(3),
ILI9881C_COMMAND_INSTR(0x01, 0x00),
@@ -831,12 +1029,12 @@ static const struct ili9881c_instr w552946ab_init[] = {
ILI9881C_COMMAND_INSTR(0x07, 0x02),
ILI9881C_COMMAND_INSTR(0x08, 0x02),
ILI9881C_COMMAND_INSTR(0x09, 0x00),
- ILI9881C_COMMAND_INSTR(0x0A, 0x00),
- ILI9881C_COMMAND_INSTR(0x0B, 0x00),
- ILI9881C_COMMAND_INSTR(0x0C, 0x00),
- ILI9881C_COMMAND_INSTR(0x0D, 0x00),
- ILI9881C_COMMAND_INSTR(0x0E, 0x00),
- ILI9881C_COMMAND_INSTR(0x0F, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0f, 0x00),
ILI9881C_COMMAND_INSTR(0x10, 0x00),
ILI9881C_COMMAND_INSTR(0x11, 0x00),
@@ -848,12 +1046,12 @@ static const struct ili9881c_instr w552946ab_init[] = {
ILI9881C_COMMAND_INSTR(0x17, 0x00),
ILI9881C_COMMAND_INSTR(0x18, 0x08),
ILI9881C_COMMAND_INSTR(0x19, 0x00),
- ILI9881C_COMMAND_INSTR(0x1A, 0x00),
- ILI9881C_COMMAND_INSTR(0x1B, 0x00),
- ILI9881C_COMMAND_INSTR(0x1C, 0x00),
- ILI9881C_COMMAND_INSTR(0x1D, 0x00),
- ILI9881C_COMMAND_INSTR(0x1E, 0xC0),
- ILI9881C_COMMAND_INSTR(0x1F, 0x80),
+ ILI9881C_COMMAND_INSTR(0x1a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1e, 0xc0),
+ ILI9881C_COMMAND_INSTR(0x1f, 0x80),
ILI9881C_COMMAND_INSTR(0x20, 0x02),
ILI9881C_COMMAND_INSTR(0x21, 0x09),
@@ -865,12 +1063,12 @@ static const struct ili9881c_instr w552946ab_init[] = {
ILI9881C_COMMAND_INSTR(0x27, 0x00),
ILI9881C_COMMAND_INSTR(0x28, 0x55),
ILI9881C_COMMAND_INSTR(0x29, 0x03),
- ILI9881C_COMMAND_INSTR(0x2A, 0x00),
- ILI9881C_COMMAND_INSTR(0x2B, 0x00),
- ILI9881C_COMMAND_INSTR(0x2C, 0x00),
- ILI9881C_COMMAND_INSTR(0x2D, 0x00),
- ILI9881C_COMMAND_INSTR(0x2E, 0x00),
- ILI9881C_COMMAND_INSTR(0x2F, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2f, 0x00),
ILI9881C_COMMAND_INSTR(0x30, 0x00),
ILI9881C_COMMAND_INSTR(0x31, 0x00),
@@ -880,54 +1078,54 @@ static const struct ili9881c_instr w552946ab_init[] = {
ILI9881C_COMMAND_INSTR(0x35, 0x05),
ILI9881C_COMMAND_INSTR(0x36, 0x05),
ILI9881C_COMMAND_INSTR(0x37, 0x00),
- ILI9881C_COMMAND_INSTR(0x38, 0x3C),
+ ILI9881C_COMMAND_INSTR(0x38, 0x3c),
ILI9881C_COMMAND_INSTR(0x39, 0x35),
- ILI9881C_COMMAND_INSTR(0x3A, 0x00),
- ILI9881C_COMMAND_INSTR(0x3B, 0x40),
- ILI9881C_COMMAND_INSTR(0x3C, 0x00),
- ILI9881C_COMMAND_INSTR(0x3D, 0x00),
- ILI9881C_COMMAND_INSTR(0x3E, 0x00),
- ILI9881C_COMMAND_INSTR(0x3F, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3b, 0x40),
+ ILI9881C_COMMAND_INSTR(0x3c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3f, 0x00),
ILI9881C_COMMAND_INSTR(0x40, 0x00),
ILI9881C_COMMAND_INSTR(0x41, 0x88),
ILI9881C_COMMAND_INSTR(0x42, 0x00),
ILI9881C_COMMAND_INSTR(0x43, 0x00),
- ILI9881C_COMMAND_INSTR(0x44, 0x1F),
+ ILI9881C_COMMAND_INSTR(0x44, 0x1f),
ILI9881C_COMMAND_INSTR(0x50, 0x01),
ILI9881C_COMMAND_INSTR(0x51, 0x23),
ILI9881C_COMMAND_INSTR(0x52, 0x45),
ILI9881C_COMMAND_INSTR(0x53, 0x67),
ILI9881C_COMMAND_INSTR(0x54, 0x89),
- ILI9881C_COMMAND_INSTR(0x55, 0xaB),
+ ILI9881C_COMMAND_INSTR(0x55, 0xab),
ILI9881C_COMMAND_INSTR(0x56, 0x01),
ILI9881C_COMMAND_INSTR(0x57, 0x23),
ILI9881C_COMMAND_INSTR(0x58, 0x45),
ILI9881C_COMMAND_INSTR(0x59, 0x67),
- ILI9881C_COMMAND_INSTR(0x5A, 0x89),
- ILI9881C_COMMAND_INSTR(0x5B, 0xAB),
- ILI9881C_COMMAND_INSTR(0x5C, 0xCD),
- ILI9881C_COMMAND_INSTR(0x5D, 0xEF),
- ILI9881C_COMMAND_INSTR(0x5E, 0x03),
- ILI9881C_COMMAND_INSTR(0x5F, 0x14),
+ ILI9881C_COMMAND_INSTR(0x5a, 0x89),
+ ILI9881C_COMMAND_INSTR(0x5b, 0xab),
+ ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
+ ILI9881C_COMMAND_INSTR(0x5d, 0xef),
+ ILI9881C_COMMAND_INSTR(0x5e, 0x03),
+ ILI9881C_COMMAND_INSTR(0x5f, 0x14),
ILI9881C_COMMAND_INSTR(0x60, 0x15),
- ILI9881C_COMMAND_INSTR(0x61, 0x0C),
- ILI9881C_COMMAND_INSTR(0x62, 0x0D),
- ILI9881C_COMMAND_INSTR(0x63, 0x0E),
- ILI9881C_COMMAND_INSTR(0x64, 0x0F),
+ ILI9881C_COMMAND_INSTR(0x61, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x63, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x64, 0x0f),
ILI9881C_COMMAND_INSTR(0x65, 0x10),
ILI9881C_COMMAND_INSTR(0x66, 0x11),
ILI9881C_COMMAND_INSTR(0x67, 0x08),
ILI9881C_COMMAND_INSTR(0x68, 0x02),
- ILI9881C_COMMAND_INSTR(0x69, 0x0A),
- ILI9881C_COMMAND_INSTR(0x6A, 0x02),
- ILI9881C_COMMAND_INSTR(0x6B, 0x02),
- ILI9881C_COMMAND_INSTR(0x6C, 0x02),
- ILI9881C_COMMAND_INSTR(0x6D, 0x02),
- ILI9881C_COMMAND_INSTR(0x6E, 0x02),
- ILI9881C_COMMAND_INSTR(0x6F, 0x02),
+ ILI9881C_COMMAND_INSTR(0x69, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x6a, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6b, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6d, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x02),
ILI9881C_COMMAND_INSTR(0x70, 0x02),
ILI9881C_COMMAND_INSTR(0x71, 0x02),
@@ -936,15 +1134,15 @@ static const struct ili9881c_instr w552946ab_init[] = {
ILI9881C_COMMAND_INSTR(0x74, 0x02),
ILI9881C_COMMAND_INSTR(0x75, 0x14),
ILI9881C_COMMAND_INSTR(0x76, 0x15),
- ILI9881C_COMMAND_INSTR(0x77, 0x0F),
- ILI9881C_COMMAND_INSTR(0x78, 0x0E),
- ILI9881C_COMMAND_INSTR(0x79, 0x0D),
- ILI9881C_COMMAND_INSTR(0x7A, 0x0C),
- ILI9881C_COMMAND_INSTR(0x7B, 0x11),
- ILI9881C_COMMAND_INSTR(0x7C, 0x10),
- ILI9881C_COMMAND_INSTR(0x7D, 0x06),
- ILI9881C_COMMAND_INSTR(0x7E, 0x02),
- ILI9881C_COMMAND_INSTR(0x7F, 0x0A),
+ ILI9881C_COMMAND_INSTR(0x77, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x78, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x79, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x7a, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x7b, 0x11),
+ ILI9881C_COMMAND_INSTR(0x7c, 0x10),
+ ILI9881C_COMMAND_INSTR(0x7d, 0x06),
+ ILI9881C_COMMAND_INSTR(0x7e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7f, 0x0a),
ILI9881C_COMMAND_INSTR(0x80, 0x02),
ILI9881C_COMMAND_INSTR(0x81, 0x02),
@@ -956,74 +1154,74 @@ static const struct ili9881c_instr w552946ab_init[] = {
ILI9881C_COMMAND_INSTR(0x87, 0x02),
ILI9881C_COMMAND_INSTR(0x88, 0x08),
ILI9881C_COMMAND_INSTR(0x89, 0x02),
- ILI9881C_COMMAND_INSTR(0x8A, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(4),
ILI9881C_COMMAND_INSTR(0x00, 0x80),
ILI9881C_COMMAND_INSTR(0x70, 0x00),
ILI9881C_COMMAND_INSTR(0x71, 0x00),
- ILI9881C_COMMAND_INSTR(0x66, 0xFE),
+ ILI9881C_COMMAND_INSTR(0x66, 0xfe),
ILI9881C_COMMAND_INSTR(0x82, 0x15),
ILI9881C_COMMAND_INSTR(0x84, 0x15),
ILI9881C_COMMAND_INSTR(0x85, 0x15),
ILI9881C_COMMAND_INSTR(0x3a, 0x24),
- ILI9881C_COMMAND_INSTR(0x32, 0xAC),
- ILI9881C_COMMAND_INSTR(0x8C, 0x80),
- ILI9881C_COMMAND_INSTR(0x3C, 0xF5),
+ ILI9881C_COMMAND_INSTR(0x32, 0xac),
+ ILI9881C_COMMAND_INSTR(0x8c, 0x80),
+ ILI9881C_COMMAND_INSTR(0x3c, 0xf5),
ILI9881C_COMMAND_INSTR(0x88, 0x33),
ILI9881C_SWITCH_PAGE_INSTR(1),
- ILI9881C_COMMAND_INSTR(0x22, 0x0A),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
ILI9881C_COMMAND_INSTR(0x31, 0x00),
ILI9881C_COMMAND_INSTR(0x53, 0x78),
- ILI9881C_COMMAND_INSTR(0x50, 0x5B),
- ILI9881C_COMMAND_INSTR(0x51, 0x5B),
+ ILI9881C_COMMAND_INSTR(0x50, 0x5b),
+ ILI9881C_COMMAND_INSTR(0x51, 0x5b),
ILI9881C_COMMAND_INSTR(0x60, 0x20),
ILI9881C_COMMAND_INSTR(0x61, 0x00),
- ILI9881C_COMMAND_INSTR(0x62, 0x0D),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0d),
ILI9881C_COMMAND_INSTR(0x63, 0x00),
- ILI9881C_COMMAND_INSTR(0xA0, 0x00),
- ILI9881C_COMMAND_INSTR(0xA1, 0x10),
- ILI9881C_COMMAND_INSTR(0xA2, 0x1C),
- ILI9881C_COMMAND_INSTR(0xA3, 0x13),
- ILI9881C_COMMAND_INSTR(0xA4, 0x15),
- ILI9881C_COMMAND_INSTR(0xA5, 0x26),
- ILI9881C_COMMAND_INSTR(0xA6, 0x1A),
- ILI9881C_COMMAND_INSTR(0xA7, 0x1D),
- ILI9881C_COMMAND_INSTR(0xA8, 0x67),
- ILI9881C_COMMAND_INSTR(0xA9, 0x1C),
- ILI9881C_COMMAND_INSTR(0xAA, 0x29),
- ILI9881C_COMMAND_INSTR(0xAB, 0x5B),
- ILI9881C_COMMAND_INSTR(0xAC, 0x26),
- ILI9881C_COMMAND_INSTR(0xAD, 0x28),
- ILI9881C_COMMAND_INSTR(0xAE, 0x5C),
- ILI9881C_COMMAND_INSTR(0xAF, 0x30),
- ILI9881C_COMMAND_INSTR(0xB0, 0x31),
- ILI9881C_COMMAND_INSTR(0xB1, 0x2E),
- ILI9881C_COMMAND_INSTR(0xB2, 0x32),
- ILI9881C_COMMAND_INSTR(0xB3, 0x00),
-
- ILI9881C_COMMAND_INSTR(0xC0, 0x00),
- ILI9881C_COMMAND_INSTR(0xC1, 0x10),
- ILI9881C_COMMAND_INSTR(0xC2, 0x1C),
- ILI9881C_COMMAND_INSTR(0xC3, 0x13),
- ILI9881C_COMMAND_INSTR(0xC4, 0x15),
- ILI9881C_COMMAND_INSTR(0xC5, 0x26),
- ILI9881C_COMMAND_INSTR(0xC6, 0x1A),
- ILI9881C_COMMAND_INSTR(0xC7, 0x1D),
- ILI9881C_COMMAND_INSTR(0xC8, 0x67),
- ILI9881C_COMMAND_INSTR(0xC9, 0x1C),
- ILI9881C_COMMAND_INSTR(0xCA, 0x29),
- ILI9881C_COMMAND_INSTR(0xCB, 0x5B),
- ILI9881C_COMMAND_INSTR(0xCC, 0x26),
- ILI9881C_COMMAND_INSTR(0xCD, 0x28),
- ILI9881C_COMMAND_INSTR(0xCE, 0x5C),
- ILI9881C_COMMAND_INSTR(0xCF, 0x30),
- ILI9881C_COMMAND_INSTR(0xD0, 0x31),
- ILI9881C_COMMAND_INSTR(0xD1, 0x2E),
- ILI9881C_COMMAND_INSTR(0xD2, 0x32),
- ILI9881C_COMMAND_INSTR(0xD3, 0x00),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x00),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x10),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x13),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x26),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa8, 0x67),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x29),
+ ILI9881C_COMMAND_INSTR(0xab, 0x5b),
+ ILI9881C_COMMAND_INSTR(0xac, 0x26),
+ ILI9881C_COMMAND_INSTR(0xad, 0x28),
+ ILI9881C_COMMAND_INSTR(0xae, 0x5c),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x30),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x31),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x2e),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x32),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x00),
+
+ ILI9881C_COMMAND_INSTR(0xc0, 0x00),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x10),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x13),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x26),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xc8, 0x67),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xca, 0x29),
+ ILI9881C_COMMAND_INSTR(0xcb, 0x5b),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x26),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x28),
+ ILI9881C_COMMAND_INSTR(0xce, 0x5c),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x30),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x31),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x2e),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x32),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x00),
ILI9881C_SWITCH_PAGE_INSTR(0),
};
@@ -1032,10 +1230,10 @@ static const struct ili9881c_instr am8001280g_init[] = {
ILI9881C_COMMAND_INSTR(0x01, 0x00),
ILI9881C_COMMAND_INSTR(0x02, 0x00),
ILI9881C_COMMAND_INSTR(0x03, 0x73),
- ILI9881C_COMMAND_INSTR(0x04, 0xD3),
+ ILI9881C_COMMAND_INSTR(0x04, 0xd3),
ILI9881C_COMMAND_INSTR(0x05, 0x00),
- ILI9881C_COMMAND_INSTR(0x06, 0x0A),
- ILI9881C_COMMAND_INSTR(0x07, 0x0E),
+ ILI9881C_COMMAND_INSTR(0x06, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x07, 0x0e),
ILI9881C_COMMAND_INSTR(0x08, 0x00),
ILI9881C_COMMAND_INSTR(0x09, 0x01),
ILI9881C_COMMAND_INSTR(0x0a, 0x01),
@@ -1117,10 +1315,10 @@ static const struct ili9881c_instr am8001280g_init[] = {
ILI9881C_COMMAND_INSTR(0x5f, 0x02),
ILI9881C_COMMAND_INSTR(0x60, 0x00),
ILI9881C_COMMAND_INSTR(0x61, 0x01),
- ILI9881C_COMMAND_INSTR(0x62, 0x0D),
- ILI9881C_COMMAND_INSTR(0x63, 0x0C),
- ILI9881C_COMMAND_INSTR(0x64, 0x0F),
- ILI9881C_COMMAND_INSTR(0x65, 0x0E),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x63, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x64, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x65, 0x0e),
ILI9881C_COMMAND_INSTR(0x66, 0x06),
ILI9881C_COMMAND_INSTR(0x67, 0x07),
ILI9881C_COMMAND_INSTR(0x68, 0x02),
@@ -1139,10 +1337,10 @@ static const struct ili9881c_instr am8001280g_init[] = {
ILI9881C_COMMAND_INSTR(0x75, 0x02),
ILI9881C_COMMAND_INSTR(0x76, 0x00),
ILI9881C_COMMAND_INSTR(0x77, 0x01),
- ILI9881C_COMMAND_INSTR(0x78, 0x0D),
- ILI9881C_COMMAND_INSTR(0x79, 0x0C),
- ILI9881C_COMMAND_INSTR(0x7a, 0x0F),
- ILI9881C_COMMAND_INSTR(0x7b, 0x0E),
+ ILI9881C_COMMAND_INSTR(0x78, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x79, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x7a, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x7b, 0x0e),
ILI9881C_COMMAND_INSTR(0x7c, 0x06),
ILI9881C_COMMAND_INSTR(0x7d, 0x07),
ILI9881C_COMMAND_INSTR(0x7e, 0x02),
@@ -1157,7 +1355,7 @@ static const struct ili9881c_instr am8001280g_init[] = {
ILI9881C_COMMAND_INSTR(0x87, 0x02),
ILI9881C_COMMAND_INSTR(0x88, 0x02),
ILI9881C_COMMAND_INSTR(0x89, 0x02),
- ILI9881C_COMMAND_INSTR(0x8A, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(4),
ILI9881C_COMMAND_INSTR(0x6c, 0x15),
@@ -1170,60 +1368,248 @@ static const struct ili9881c_instr am8001280g_init[] = {
ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
ILI9881C_SWITCH_PAGE_INSTR(1),
- ILI9881C_COMMAND_INSTR(0x22, 0x0A),
- ILI9881C_COMMAND_INSTR(0x31, 0x0B),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x31, 0x0b),
ILI9881C_COMMAND_INSTR(0x50, 0xa5),
ILI9881C_COMMAND_INSTR(0x51, 0xa0),
ILI9881C_COMMAND_INSTR(0x53, 0x70),
- ILI9881C_COMMAND_INSTR(0x55, 0x7A),
+ ILI9881C_COMMAND_INSTR(0x55, 0x7a),
ILI9881C_COMMAND_INSTR(0x60, 0x14),
- ILI9881C_COMMAND_INSTR(0xA0, 0x00),
- ILI9881C_COMMAND_INSTR(0xA1, 0x53),
- ILI9881C_COMMAND_INSTR(0xA2, 0x50),
- ILI9881C_COMMAND_INSTR(0xA3, 0x20),
- ILI9881C_COMMAND_INSTR(0xA4, 0x27),
- ILI9881C_COMMAND_INSTR(0xA5, 0x33),
- ILI9881C_COMMAND_INSTR(0xA6, 0x25),
- ILI9881C_COMMAND_INSTR(0xA7, 0x25),
- ILI9881C_COMMAND_INSTR(0xA8, 0xD4),
- ILI9881C_COMMAND_INSTR(0xA9, 0x1A),
- ILI9881C_COMMAND_INSTR(0xAA, 0x2B),
- ILI9881C_COMMAND_INSTR(0xAB, 0xB5),
- ILI9881C_COMMAND_INSTR(0xAC, 0x19),
- ILI9881C_COMMAND_INSTR(0xAD, 0x18),
- ILI9881C_COMMAND_INSTR(0xAE, 0x53),
- ILI9881C_COMMAND_INSTR(0xAF, 0x1A),
- ILI9881C_COMMAND_INSTR(0xB0, 0x25),
- ILI9881C_COMMAND_INSTR(0xB1, 0x62),
- ILI9881C_COMMAND_INSTR(0xB2, 0x6A),
- ILI9881C_COMMAND_INSTR(0xB3, 0x31),
-
- ILI9881C_COMMAND_INSTR(0xC0, 0x00),
- ILI9881C_COMMAND_INSTR(0xC1, 0x53),
- ILI9881C_COMMAND_INSTR(0xC2, 0x50),
- ILI9881C_COMMAND_INSTR(0xC3, 0x20),
- ILI9881C_COMMAND_INSTR(0xC4, 0x27),
- ILI9881C_COMMAND_INSTR(0xC5, 0x33),
- ILI9881C_COMMAND_INSTR(0xC6, 0x25),
- ILI9881C_COMMAND_INSTR(0xC7, 0x25),
- ILI9881C_COMMAND_INSTR(0xC8, 0xD4),
- ILI9881C_COMMAND_INSTR(0xC9, 0x1A),
- ILI9881C_COMMAND_INSTR(0xCA, 0x2B),
- ILI9881C_COMMAND_INSTR(0xCB, 0xB5),
- ILI9881C_COMMAND_INSTR(0xCC, 0x19),
- ILI9881C_COMMAND_INSTR(0xCD, 0x18),
- ILI9881C_COMMAND_INSTR(0xCE, 0x53),
- ILI9881C_COMMAND_INSTR(0xCF, 0x1A),
- ILI9881C_COMMAND_INSTR(0xD0, 0x25),
- ILI9881C_COMMAND_INSTR(0xD1, 0x62),
- ILI9881C_COMMAND_INSTR(0xD2, 0x6A),
- ILI9881C_COMMAND_INSTR(0xD3, 0x31),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x00),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x53),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x50),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x20),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x27),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x33),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x25),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x25),
+ ILI9881C_COMMAND_INSTR(0xa8, 0xd4),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x2b),
+ ILI9881C_COMMAND_INSTR(0xab, 0xb5),
+ ILI9881C_COMMAND_INSTR(0xac, 0x19),
+ ILI9881C_COMMAND_INSTR(0xad, 0x18),
+ ILI9881C_COMMAND_INSTR(0xae, 0x53),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x25),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x62),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x6a),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x31),
+
+ ILI9881C_COMMAND_INSTR(0xc0, 0x00),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x53),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x50),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x20),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x27),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x33),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x25),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x25),
+ ILI9881C_COMMAND_INSTR(0xc8, 0xd4),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xca, 0x2b),
+ ILI9881C_COMMAND_INSTR(0xcb, 0xb5),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x19),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x18),
+ ILI9881C_COMMAND_INSTR(0xce, 0x53),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x25),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x62),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x6a),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x31),
ILI9881C_SWITCH_PAGE_INSTR(0),
ILI9881C_COMMAND_INSTR(MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c),
ILI9881C_COMMAND_INSTR(MIPI_DCS_WRITE_POWER_SAVE, 0x00),
};
+static const struct ili9881c_instr rpi_5inch_init[] = {
+ ILI9881C_SWITCH_PAGE_INSTR(3),
+ ILI9881C_COMMAND_INSTR(0x01, 0x00),
+ ILI9881C_COMMAND_INSTR(0x02, 0x00),
+ ILI9881C_COMMAND_INSTR(0x03, 0x73),
+ ILI9881C_COMMAND_INSTR(0x04, 0x73),
+ ILI9881C_COMMAND_INSTR(0x05, 0x00),
+ ILI9881C_COMMAND_INSTR(0x06, 0x06),
+ ILI9881C_COMMAND_INSTR(0x07, 0x02),
+ ILI9881C_COMMAND_INSTR(0x08, 0x00),
+ ILI9881C_COMMAND_INSTR(0x09, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0a, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0b, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0c, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0d, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0e, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0f, 0x01),
+ ILI9881C_COMMAND_INSTR(0x10, 0x01),
+ ILI9881C_COMMAND_INSTR(0x11, 0x00),
+ ILI9881C_COMMAND_INSTR(0x12, 0x00),
+ ILI9881C_COMMAND_INSTR(0x13, 0x01),
+ ILI9881C_COMMAND_INSTR(0x14, 0x00),
+ ILI9881C_COMMAND_INSTR(0x15, 0x00),
+ ILI9881C_COMMAND_INSTR(0x16, 0x00),
+ ILI9881C_COMMAND_INSTR(0x17, 0x00),
+ ILI9881C_COMMAND_INSTR(0x18, 0x00),
+ ILI9881C_COMMAND_INSTR(0x19, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1e, 0xc0),
+ ILI9881C_COMMAND_INSTR(0x1f, 0x80),
+ ILI9881C_COMMAND_INSTR(0x20, 0x04),
+ ILI9881C_COMMAND_INSTR(0x21, 0x03),
+ ILI9881C_COMMAND_INSTR(0x22, 0x00),
+ ILI9881C_COMMAND_INSTR(0x23, 0x00),
+ ILI9881C_COMMAND_INSTR(0x24, 0x00),
+ ILI9881C_COMMAND_INSTR(0x25, 0x00),
+ ILI9881C_COMMAND_INSTR(0x26, 0x00),
+ ILI9881C_COMMAND_INSTR(0x27, 0x00),
+ ILI9881C_COMMAND_INSTR(0x28, 0x33),
+ ILI9881C_COMMAND_INSTR(0x29, 0x03),
+ ILI9881C_COMMAND_INSTR(0x2a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2f, 0x00),
+ ILI9881C_COMMAND_INSTR(0x30, 0x00),
+ ILI9881C_COMMAND_INSTR(0x31, 0x00),
+ ILI9881C_COMMAND_INSTR(0x32, 0x00),
+ ILI9881C_COMMAND_INSTR(0x33, 0x00),
+ ILI9881C_COMMAND_INSTR(0x34, 0x03),
+ ILI9881C_COMMAND_INSTR(0x35, 0x00),
+ ILI9881C_COMMAND_INSTR(0x36, 0x03),
+ ILI9881C_COMMAND_INSTR(0x37, 0x00),
+ ILI9881C_COMMAND_INSTR(0x38, 0x00),
+ ILI9881C_COMMAND_INSTR(0x39, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3f, 0x00),
+ ILI9881C_COMMAND_INSTR(0x40, 0x00),
+ ILI9881C_COMMAND_INSTR(0x41, 0x00),
+ ILI9881C_COMMAND_INSTR(0x42, 0x00),
+ ILI9881C_COMMAND_INSTR(0x43, 0x00),
+ ILI9881C_COMMAND_INSTR(0x44, 0x00),
+ ILI9881C_COMMAND_INSTR(0x50, 0x01),
+ ILI9881C_COMMAND_INSTR(0x51, 0x23),
+ ILI9881C_COMMAND_INSTR(0x52, 0x45),
+ ILI9881C_COMMAND_INSTR(0x53, 0x67),
+ ILI9881C_COMMAND_INSTR(0x54, 0x89),
+ ILI9881C_COMMAND_INSTR(0x55, 0xab),
+ ILI9881C_COMMAND_INSTR(0x56, 0x01),
+ ILI9881C_COMMAND_INSTR(0x57, 0x23),
+ ILI9881C_COMMAND_INSTR(0x58, 0x45),
+ ILI9881C_COMMAND_INSTR(0x59, 0x67),
+ ILI9881C_COMMAND_INSTR(0x5a, 0x89),
+ ILI9881C_COMMAND_INSTR(0x5b, 0xab),
+ ILI9881C_COMMAND_INSTR(0x5c, 0xcd),
+ ILI9881C_COMMAND_INSTR(0x5d, 0xef),
+ ILI9881C_COMMAND_INSTR(0x5e, 0x10),
+ ILI9881C_COMMAND_INSTR(0x5f, 0x09),
+ ILI9881C_COMMAND_INSTR(0x60, 0x08),
+ ILI9881C_COMMAND_INSTR(0x61, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x63, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x64, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x65, 0x02),
+ ILI9881C_COMMAND_INSTR(0x66, 0x02),
+ ILI9881C_COMMAND_INSTR(0x67, 0x02),
+ ILI9881C_COMMAND_INSTR(0x68, 0x02),
+ ILI9881C_COMMAND_INSTR(0x69, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6a, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6b, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6d, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x02),
+ ILI9881C_COMMAND_INSTR(0x70, 0x02),
+ ILI9881C_COMMAND_INSTR(0x71, 0x06),
+ ILI9881C_COMMAND_INSTR(0x72, 0x07),
+ ILI9881C_COMMAND_INSTR(0x73, 0x02),
+ ILI9881C_COMMAND_INSTR(0x74, 0x02),
+ ILI9881C_COMMAND_INSTR(0x75, 0x06),
+ ILI9881C_COMMAND_INSTR(0x76, 0x07),
+ ILI9881C_COMMAND_INSTR(0x77, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x78, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x79, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x7a, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x7b, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7c, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7d, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7f, 0x02),
+ ILI9881C_COMMAND_INSTR(0x80, 0x02),
+ ILI9881C_COMMAND_INSTR(0x81, 0x02),
+ ILI9881C_COMMAND_INSTR(0x82, 0x02),
+ ILI9881C_COMMAND_INSTR(0x83, 0x02),
+ ILI9881C_COMMAND_INSTR(0x84, 0x02),
+ ILI9881C_COMMAND_INSTR(0x85, 0x02),
+ ILI9881C_COMMAND_INSTR(0x86, 0x02),
+ ILI9881C_COMMAND_INSTR(0x87, 0x09),
+ ILI9881C_COMMAND_INSTR(0x88, 0x08),
+ ILI9881C_COMMAND_INSTR(0x89, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
+ ILI9881C_SWITCH_PAGE_INSTR(4),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x15),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x2a),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x57),
+ ILI9881C_COMMAND_INSTR(0x3a, 0xa4),
+ ILI9881C_COMMAND_INSTR(0x8d, 0x1a),
+ ILI9881C_COMMAND_INSTR(0x87, 0xba),
+ ILI9881C_COMMAND_INSTR(0x26, 0x76),
+ ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
+ ILI9881C_SWITCH_PAGE_INSTR(1),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x31, 0x00),
+ ILI9881C_COMMAND_INSTR(0x53, 0x35),
+ ILI9881C_COMMAND_INSTR(0x55, 0x50),
+ ILI9881C_COMMAND_INSTR(0x50, 0xaf),
+ ILI9881C_COMMAND_INSTR(0x51, 0xaf),
+ ILI9881C_COMMAND_INSTR(0x60, 0x14),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x2c),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x14),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x19),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x2e),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x22),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x23),
+ ILI9881C_COMMAND_INSTR(0xa8, 0x97),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1e),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x29),
+ ILI9881C_COMMAND_INSTR(0xab, 0x7b),
+ ILI9881C_COMMAND_INSTR(0xac, 0x18),
+ ILI9881C_COMMAND_INSTR(0xad, 0x17),
+ ILI9881C_COMMAND_INSTR(0xae, 0x4b),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x1f),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x52),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x63),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x39),
+ ILI9881C_COMMAND_INSTR(0xc0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x2c),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x14),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x19),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x2e),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x22),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x23),
+ ILI9881C_COMMAND_INSTR(0xc8, 0x97),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1e),
+ ILI9881C_COMMAND_INSTR(0xca, 0x29),
+ ILI9881C_COMMAND_INSTR(0xcb, 0x7b),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x18),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x17),
+ ILI9881C_COMMAND_INSTR(0xce, 0x4b),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x1f),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x52),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x63),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x39),
+};
+
static const struct ili9881c_instr rpi_7inch_init[] = {
ILI9881C_SWITCH_PAGE_INSTR(3),
ILI9881C_COMMAND_INSTR(0x01, 0x00),
@@ -1352,22 +1738,22 @@ static const struct ili9881c_instr rpi_7inch_init[] = {
ILI9881C_COMMAND_INSTR(0x87, 0x02),
ILI9881C_COMMAND_INSTR(0x88, 0x02),
ILI9881C_COMMAND_INSTR(0x89, 0x02),
- ILI9881C_COMMAND_INSTR(0x8A, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
ILI9881C_SWITCH_PAGE_INSTR(4),
- ILI9881C_COMMAND_INSTR(0x6C, 0x15),
- ILI9881C_COMMAND_INSTR(0x6E, 0x2A),
- ILI9881C_COMMAND_INSTR(0x6F, 0x33),
- ILI9881C_COMMAND_INSTR(0x3B, 0x98),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x15),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x2a),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x33),
+ ILI9881C_COMMAND_INSTR(0x3b, 0x98),
ILI9881C_COMMAND_INSTR(0x3a, 0x94),
- ILI9881C_COMMAND_INSTR(0x8D, 0x14),
- ILI9881C_COMMAND_INSTR(0x87, 0xBA),
+ ILI9881C_COMMAND_INSTR(0x8d, 0x14),
+ ILI9881C_COMMAND_INSTR(0x87, 0xba),
ILI9881C_COMMAND_INSTR(0x26, 0x76),
- ILI9881C_COMMAND_INSTR(0xB2, 0xD1),
- ILI9881C_COMMAND_INSTR(0xB5, 0x06),
+ ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
+ ILI9881C_COMMAND_INSTR(0xb5, 0x06),
ILI9881C_COMMAND_INSTR(0x38, 0x01),
ILI9881C_COMMAND_INSTR(0x39, 0x00),
ILI9881C_SWITCH_PAGE_INSTR(1),
- ILI9881C_COMMAND_INSTR(0x22, 0x0A),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
ILI9881C_COMMAND_INSTR(0x31, 0x00),
ILI9881C_COMMAND_INSTR(0x53, 0x7d),
ILI9881C_COMMAND_INSTR(0x55, 0x8f),
@@ -1375,46 +1761,46 @@ static const struct ili9881c_instr rpi_7inch_init[] = {
ILI9881C_COMMAND_INSTR(0x50, 0x96),
ILI9881C_COMMAND_INSTR(0x51, 0x96),
ILI9881C_COMMAND_INSTR(0x60, 0x23),
- ILI9881C_COMMAND_INSTR(0xA0, 0x08),
- ILI9881C_COMMAND_INSTR(0xA1, 0x1d),
- ILI9881C_COMMAND_INSTR(0xA2, 0x2a),
- ILI9881C_COMMAND_INSTR(0xA3, 0x10),
- ILI9881C_COMMAND_INSTR(0xA4, 0x15),
- ILI9881C_COMMAND_INSTR(0xA5, 0x28),
- ILI9881C_COMMAND_INSTR(0xA6, 0x1c),
- ILI9881C_COMMAND_INSTR(0xA7, 0x1d),
- ILI9881C_COMMAND_INSTR(0xA8, 0x7e),
- ILI9881C_COMMAND_INSTR(0xA9, 0x1d),
- ILI9881C_COMMAND_INSTR(0xAA, 0x29),
- ILI9881C_COMMAND_INSTR(0xAB, 0x6b),
- ILI9881C_COMMAND_INSTR(0xAC, 0x1a),
- ILI9881C_COMMAND_INSTR(0xAD, 0x18),
- ILI9881C_COMMAND_INSTR(0xAE, 0x4b),
- ILI9881C_COMMAND_INSTR(0xAF, 0x20),
- ILI9881C_COMMAND_INSTR(0xB0, 0x27),
- ILI9881C_COMMAND_INSTR(0xB1, 0x50),
- ILI9881C_COMMAND_INSTR(0xB2, 0x64),
- ILI9881C_COMMAND_INSTR(0xB3, 0x39),
- ILI9881C_COMMAND_INSTR(0xC0, 0x08),
- ILI9881C_COMMAND_INSTR(0xC1, 0x1d),
- ILI9881C_COMMAND_INSTR(0xC2, 0x2a),
- ILI9881C_COMMAND_INSTR(0xC3, 0x10),
- ILI9881C_COMMAND_INSTR(0xC4, 0x15),
- ILI9881C_COMMAND_INSTR(0xC5, 0x28),
- ILI9881C_COMMAND_INSTR(0xC6, 0x1c),
- ILI9881C_COMMAND_INSTR(0xC7, 0x1d),
- ILI9881C_COMMAND_INSTR(0xC8, 0x7e),
- ILI9881C_COMMAND_INSTR(0xC9, 0x1d),
- ILI9881C_COMMAND_INSTR(0xCA, 0x29),
- ILI9881C_COMMAND_INSTR(0xCB, 0x6b),
- ILI9881C_COMMAND_INSTR(0xCC, 0x1a),
- ILI9881C_COMMAND_INSTR(0xCD, 0x18),
- ILI9881C_COMMAND_INSTR(0xCE, 0x4b),
- ILI9881C_COMMAND_INSTR(0xCF, 0x20),
- ILI9881C_COMMAND_INSTR(0xD0, 0x27),
- ILI9881C_COMMAND_INSTR(0xD1, 0x50),
- ILI9881C_COMMAND_INSTR(0xD2, 0x64),
- ILI9881C_COMMAND_INSTR(0xD3, 0x39),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x10),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x28),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa8, 0x7e),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x29),
+ ILI9881C_COMMAND_INSTR(0xab, 0x6b),
+ ILI9881C_COMMAND_INSTR(0xac, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xad, 0x18),
+ ILI9881C_COMMAND_INSTR(0xae, 0x4b),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x20),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x50),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x64),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x39),
+ ILI9881C_COMMAND_INSTR(0xc0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x10),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x28),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xc8, 0x7e),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xca, 0x29),
+ ILI9881C_COMMAND_INSTR(0xcb, 0x6b),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x18),
+ ILI9881C_COMMAND_INSTR(0xce, 0x4b),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x20),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x50),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x64),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x39),
};
static const struct ili9881c_instr bsd1218_a101kl68_init[] = {
@@ -1772,6 +2158,23 @@ static const struct drm_display_mode tl050hdv35_default_mode = {
.height_mm = 110,
};
+static const struct drm_display_mode w552946aaa_default_mode = {
+ .clock = 65000,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 52,
+ .hsync_end = 720 + 52 + 8,
+ .htotal = 720 + 52 + 8 + 48,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 16,
+ .vsync_end = 1280 + 16 + 6,
+ .vtotal = 1280 + 16 + 6 + 15,
+
+ .width_mm = 68,
+ .height_mm = 121,
+};
+
static const struct drm_display_mode w552946aba_default_mode = {
.clock = 64000,
@@ -1806,6 +2209,23 @@ static const struct drm_display_mode am8001280g_default_mode = {
.height_mm = 151,
};
+static const struct drm_display_mode rpi_5inch_default_mode = {
+ .clock = 83333,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 110,
+ .hsync_end = 720 + 110 + 12,
+ .htotal = 720 + 110 + 12 + 95,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 100,
+ .vsync_end = 1280 + 100 + 2,
+ .vtotal = 1280 + 100 + 2 + 100,
+
+ .width_mm = 62,
+ .height_mm = 110,
+};
+
static const struct drm_display_mode rpi_7inch_default_mode = {
.clock = 83330,
@@ -1983,6 +2403,15 @@ static const struct ili9881c_desc tl050hdv35_desc = {
.default_address_mode = 0x03,
};
+static const struct ili9881c_desc w552946aaa_desc = {
+ .init = w552946aaa_init,
+ .init_length = ARRAY_SIZE(w552946aaa_init),
+ .mode = &w552946aaa_default_mode,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET,
+ .lanes = 2,
+};
+
static const struct ili9881c_desc w552946aba_desc = {
.init = w552946ab_init,
.init_length = ARRAY_SIZE(w552946ab_init),
@@ -2000,6 +2429,14 @@ static const struct ili9881c_desc am8001280g_desc = {
MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM,
};
+static const struct ili9881c_desc rpi_5inch_desc = {
+ .init = rpi_5inch_init,
+ .init_length = ARRAY_SIZE(rpi_5inch_init),
+ .mode = &rpi_5inch_default_mode,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM,
+ .lanes = 2,
+};
+
static const struct ili9881c_desc rpi_7inch_desc = {
.init = rpi_7inch_init,
.init_length = ARRAY_SIZE(rpi_7inch_init),
@@ -2023,8 +2460,10 @@ static const struct of_device_id ili9881c_of_match[] = {
{ .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc },
{ .compatible = "startek,kd050hdfia020", .data = &kd050hdfia020_desc },
{ .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc },
+ { .compatible = "wanchanglong,w552946aaa", .data = &w552946aaa_desc },
{ .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc },
{ .compatible = "ampire,am8001280g", .data = &am8001280g_desc },
+ { .compatible = "raspberrypi,dsi-5inch", &rpi_5inch_desc },
{ .compatible = "raspberrypi,dsi-7inch", &rpi_7inch_desc },
{ }
};
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c
index 85c7059be214..c52f20863fc7 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c
@@ -61,6 +61,13 @@ struct ili9882t {
mipi_dsi_dcs_write_seq_multi(ctx, ILI9882T_DCS_SWITCH_PAGE, \
0x98, 0x82, (page))
+/* IL79900A-specific commands, add new commands as you decode them */
+#define IL79900A_DCS_SWITCH_PAGE 0xFF
+
+#define il79900a_switch_page(ctx, page) \
+ mipi_dsi_dcs_write_seq_multi(ctx, IL79900A_DCS_SWITCH_PAGE, \
+ 0x5a, 0xa5, (page))
+
static int starry_ili9882t_init(struct ili9882t *ili)
{
struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi };
@@ -413,6 +420,38 @@ static int starry_ili9882t_init(struct ili9882t *ili)
return ctx.accum_err;
};
+static int tianma_il79900a_init(struct ili9882t *ili)
+{
+ struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi };
+
+ mipi_dsi_usleep_range(&ctx, 5000, 5100);
+
+ il79900a_switch_page(&ctx, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0x3e, 0x62);
+
+ il79900a_switch_page(&ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0x1b, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0x5d, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0x5e, 0x40);
+
+ il79900a_switch_page(&ctx, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0X29, 0x00);
+
+ il79900a_switch_page(&ctx, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0x92, 0x22);
+
+ il79900a_switch_page(&ctx, 0x00);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&ctx);
+
+ mipi_dsi_msleep(&ctx, 120);
+
+ mipi_dsi_dcs_set_display_on_multi(&ctx);
+
+ mipi_dsi_msleep(&ctx, 80);
+
+ return 0;
+};
+
static inline struct ili9882t *to_ili9882t(struct drm_panel *panel)
{
return container_of(panel, struct ili9882t, base);
@@ -529,6 +568,19 @@ static const struct drm_display_mode starry_ili9882t_default_mode = {
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
+static const struct drm_display_mode tianma_il79900a_default_mode = {
+ .clock = 264355,
+ .hdisplay = 1600,
+ .hsync_start = 1600 + 20,
+ .hsync_end = 1600 + 20 + 4,
+ .htotal = 1600 + 20 + 4 + 20,
+ .vdisplay = 2560,
+ .vsync_start = 2560 + 82,
+ .vsync_end = 2560 + 82 + 2,
+ .vtotal = 2560 + 82 + 2 + 36,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
static const struct panel_desc starry_ili9882t_desc = {
.modes = &starry_ili9882t_default_mode,
.bpc = 8,
@@ -543,6 +595,20 @@ static const struct panel_desc starry_ili9882t_desc = {
.init = starry_ili9882t_init,
};
+static const struct panel_desc tianma_tl121bvms07_desc = {
+ .modes = &tianma_il79900a_default_mode,
+ .bpc = 8,
+ .size = {
+ .width_mm = 163,
+ .height_mm = 260,
+ },
+ .lanes = 3,
+ .format = MIPI_DSI_FMT_RGB888,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_LPM,
+ .init = tianma_il79900a_init,
+};
+
static int ili9882t_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
@@ -680,6 +746,9 @@ static const struct of_device_id ili9882t_of_match[] = {
{ .compatible = "starry,ili9882t",
.data = &starry_ili9882t_desc
},
+ { .compatible = "tianma,tl121bvms07-00",
+ .data = &tianma_tl121bvms07_desc
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ili9882t_of_match);
diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
index 5c2530598ddb..aa05316dc57b 100644
--- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
+++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
@@ -1132,22 +1132,19 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
dsi->lanes = desc->lanes;
jadard->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(jadard->reset)) {
- DRM_DEV_ERROR(&dsi->dev, "failed to get our reset GPIO\n");
- return PTR_ERR(jadard->reset);
- }
+ if (IS_ERR(jadard->reset))
+ return dev_err_probe(&dsi->dev, PTR_ERR(jadard->reset),
+ "failed to get our reset GPIO\n");
jadard->vdd = devm_regulator_get(dev, "vdd");
- if (IS_ERR(jadard->vdd)) {
- DRM_DEV_ERROR(&dsi->dev, "failed to get vdd regulator\n");
- return PTR_ERR(jadard->vdd);
- }
+ if (IS_ERR(jadard->vdd))
+ return dev_err_probe(&dsi->dev, PTR_ERR(jadard->vdd),
+ "failed to get vdd regulator\n");
jadard->vccio = devm_regulator_get(dev, "vccio");
- if (IS_ERR(jadard->vccio)) {
- DRM_DEV_ERROR(&dsi->dev, "failed to get vccio regulator\n");
- return PTR_ERR(jadard->vccio);
- }
+ if (IS_ERR(jadard->vccio))
+ return dev_err_probe(&dsi->dev, PTR_ERR(jadard->vccio),
+ "failed to get vccio regulator\n");
ret = of_drm_get_panel_orientation(dev->of_node, &jadard->orientation);
if (ret < 0)
diff --git a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c
index 2fc7b0779b37..893af9b16756 100644
--- a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c
+++ b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c
@@ -359,7 +359,7 @@ static int kingdisplay_panel_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
- MIPI_DSI_MODE_LPM;
+ MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
kingdisplay = devm_drm_panel_alloc(&dsi->dev, __typeof(*kingdisplay), base,
&kingdisplay_panel_funcs,
diff --git a/drivers/gpu/drm/panel/panel-lg-ld070wx3.c b/drivers/gpu/drm/panel/panel-lg-ld070wx3.c
new file mode 100644
index 000000000000..00cbfc5518a5
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lg-ld070wx3.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/array_size.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+static const struct regulator_bulk_data lg_ld070wx3_supplies[] = {
+ { .supply = "vdd" }, { .supply = "vcc" },
+};
+
+struct lg_ld070wx3 {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+
+ struct regulator_bulk_data *supplies;
+};
+
+static inline struct lg_ld070wx3 *to_lg_ld070wx3(struct drm_panel *panel)
+{
+ return container_of(panel, struct lg_ld070wx3, panel);
+}
+
+static int lg_ld070wx3_prepare(struct drm_panel *panel)
+{
+ struct lg_ld070wx3 *priv = to_lg_ld070wx3(panel);
+ struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
+ struct device *dev = panel->dev;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(lg_ld070wx3_supplies), priv->supplies);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable power supplies: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * According to spec delay between enabling supply is 0,
+ * for regulators to reach required voltage ~5ms needed.
+ * MIPI interface signal for setup requires additional
+ * 110ms which in total results in 115ms.
+ */
+ mdelay(115);
+
+ mipi_dsi_dcs_soft_reset_multi(&ctx);
+ mipi_dsi_msleep(&ctx, 20);
+
+ /* Differential input impedance selection */
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0xae, 0x0b);
+
+ /* Enter test mode 1 and 2*/
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0xee, 0xea);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0xef, 0x5f);
+
+ /* Increased MIPI CLK driving ability */
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0xf2, 0x68);
+
+ /* Exit test mode 1 and 2 */
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0xee, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&ctx, 0xef, 0x00);
+
+ return ctx.accum_err;
+}
+
+static int lg_ld070wx3_unprepare(struct drm_panel *panel)
+{
+ struct lg_ld070wx3 *priv = to_lg_ld070wx3(panel);
+ struct mipi_dsi_multi_context ctx = { .dsi = priv->dsi };
+
+ mipi_dsi_dcs_enter_sleep_mode_multi(&ctx);
+
+ msleep(50);
+
+ regulator_bulk_disable(ARRAY_SIZE(lg_ld070wx3_supplies), priv->supplies);
+
+ /* power supply must be off for at least 1s after panel disable */
+ msleep(1000);
+
+ return 0;
+}
+
+static const struct drm_display_mode lg_ld070wx3_mode = {
+ .clock = (800 + 32 + 48 + 8) * (1280 + 5 + 3 + 1) * 60 / 1000,
+ .hdisplay = 800,
+ .hsync_start = 800 + 32,
+ .hsync_end = 800 + 32 + 48,
+ .htotal = 800 + 32 + 48 + 8,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 5,
+ .vsync_end = 1280 + 5 + 3,
+ .vtotal = 1280 + 5 + 3 + 1,
+ .width_mm = 94,
+ .height_mm = 151,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int lg_ld070wx3_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector, &lg_ld070wx3_mode);
+}
+
+static const struct drm_panel_funcs lg_ld070wx3_panel_funcs = {
+ .prepare = lg_ld070wx3_prepare,
+ .unprepare = lg_ld070wx3_unprepare,
+ .get_modes = lg_ld070wx3_get_modes,
+};
+
+static int lg_ld070wx3_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct lg_ld070wx3 *priv;
+ int ret;
+
+ priv = devm_drm_panel_alloc(dev, struct lg_ld070wx3, panel,
+ &lg_ld070wx3_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(lg_ld070wx3_supplies),
+ lg_ld070wx3_supplies, &priv->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get supplies\n");
+
+ priv->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, priv);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
+
+ ret = drm_panel_of_backlight(&priv->panel);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get backlight\n");
+
+ drm_panel_add(&priv->panel);
+
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0) {
+ drm_panel_remove(&priv->panel);
+ return dev_err_probe(dev, ret, "failed to attach to DSI host\n");
+ }
+
+ return 0;
+}
+
+static void lg_ld070wx3_remove(struct mipi_dsi_device *dsi)
+{
+ struct lg_ld070wx3 *priv = mipi_dsi_get_drvdata(dsi);
+
+ drm_panel_remove(&priv->panel);
+}
+
+static const struct of_device_id lg_ld070wx3_of_match[] = {
+ { .compatible = "lg,ld070wx3-sl01" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lg_ld070wx3_of_match);
+
+static struct mipi_dsi_driver lg_ld070wx3_driver = {
+ .driver = {
+ .name = "panel-lg-ld070wx3",
+ .of_match_table = lg_ld070wx3_of_match,
+ },
+ .probe = lg_ld070wx3_probe,
+ .remove = lg_ld070wx3_remove,
+};
+module_mipi_dsi_driver(lg_ld070wx3_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("LG LD070WX3-SL01 DSI panel driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c
index 0db9cadd868e..18130bc14201 100644
--- a/drivers/gpu/drm/panel/panel-newvision-nv3052c.c
+++ b/drivers/gpu/drm/panel/panel-newvision-nv3052c.c
@@ -43,59 +43,12 @@ struct nv3052c {
struct gpio_desc *reset_gpio;
};
-static const struct nv3052c_reg ltk035c5444t_panel_regs[] = {
- // EXTC Command set enable, select page 1
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 },
- // Mostly unknown registers
- { 0xe3, 0x00 },
- { 0x40, 0x00 },
- { 0x03, 0x40 },
- { 0x04, 0x00 },
- { 0x05, 0x03 },
- { 0x08, 0x00 },
- { 0x09, 0x07 },
- { 0x0a, 0x01 },
- { 0x0b, 0x32 },
- { 0x0c, 0x32 },
- { 0x0d, 0x0b },
- { 0x0e, 0x00 },
- { 0x23, 0xa0 },
- { 0x24, 0x0c },
- { 0x25, 0x06 },
- { 0x26, 0x14 },
- { 0x27, 0x14 },
- { 0x38, 0xcc }, // VCOM_ADJ1
- { 0x39, 0xd7 }, // VCOM_ADJ2
- { 0x3a, 0x4a }, // VCOM_ADJ3
- { 0x28, 0x40 },
- { 0x29, 0x01 },
- { 0x2a, 0xdf },
- { 0x49, 0x3c },
- { 0x91, 0x77 }, // EXTPW_CTRL2
- { 0x92, 0x77 }, // EXTPW_CTRL3
- { 0xa0, 0x55 },
- { 0xa1, 0x50 },
- { 0xa4, 0x9c },
- { 0xa7, 0x02 },
- { 0xa8, 0x01 },
- { 0xa9, 0x01 },
- { 0xaa, 0xfc },
- { 0xab, 0x28 },
- { 0xac, 0x06 },
- { 0xad, 0x06 },
- { 0xae, 0x06 },
- { 0xaf, 0x03 },
- { 0xb0, 0x08 },
- { 0xb1, 0x26 },
- { 0xb2, 0x28 },
- { 0xb3, 0x28 },
- { 0xb4, 0x33 },
- { 0xb5, 0x08 },
- { 0xb6, 0x26 },
- { 0xb7, 0x08 },
- { 0xb8, 0x26 },
- { 0xf0, 0x00 },
- { 0xf6, 0xc0 },
+/*
+ * Common initialization registers for all currently
+ * supported displays. Mostly seem to be related
+ * to Gamma correction curves and output pad mappings.
+ */
+static const struct nv3052c_reg common_init_regs[] = {
// EXTC Command set enable, select page 2
{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
// Set gray scale voltage to adjust gamma
@@ -215,7 +168,7 @@ static const struct nv3052c_reg ltk035c5444t_panel_regs[] = {
{ 0xa0, 0x01 }, // PANELU2D33
// EXTC Command set enable, select page 2
{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
- // Unknown registers
+ // Page 2 register values (0x01..0x10) are same for nv3051d and nv3052c
{ 0x01, 0x01 },
{ 0x02, 0xda },
{ 0x03, 0xba },
@@ -236,6 +189,62 @@ static const struct nv3052c_reg ltk035c5444t_panel_regs[] = {
{ 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 },
// Display Access Control
{ 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0
+
+};
+
+static const struct nv3052c_reg ltk035c5444t_panel_regs[] = {
+ // EXTC Command set enable, select page 1
+ { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x01 },
+ // Mostly unknown registers
+ { 0xe3, 0x00 },
+ { 0x40, 0x00 },
+ { 0x03, 0x40 },
+ { 0x04, 0x00 },
+ { 0x05, 0x03 },
+ { 0x08, 0x00 },
+ { 0x09, 0x07 },
+ { 0x0a, 0x01 },
+ { 0x0b, 0x32 },
+ { 0x0c, 0x32 },
+ { 0x0d, 0x0b },
+ { 0x0e, 0x00 },
+ { 0x23, 0xa0 },
+ { 0x24, 0x0c },
+ { 0x25, 0x06 },
+ { 0x26, 0x14 },
+ { 0x27, 0x14 },
+ { 0x38, 0xcc }, // VCOM_ADJ1
+ { 0x39, 0xd7 }, // VCOM_ADJ2
+ { 0x3a, 0x4a }, // VCOM_ADJ3
+ { 0x28, 0x40 },
+ { 0x29, 0x01 },
+ { 0x2a, 0xdf },
+ { 0x49, 0x3c },
+ { 0x91, 0x77 }, // EXTPW_CTRL2
+ { 0x92, 0x77 }, // EXTPW_CTRL3
+ { 0xa0, 0x55 },
+ { 0xa1, 0x50 },
+ { 0xa4, 0x9c },
+ { 0xa7, 0x02 },
+ { 0xa8, 0x01 },
+ { 0xa9, 0x01 },
+ { 0xaa, 0xfc },
+ { 0xab, 0x28 },
+ { 0xac, 0x06 },
+ { 0xad, 0x06 },
+ { 0xae, 0x06 },
+ { 0xaf, 0x03 },
+ { 0xb0, 0x08 },
+ { 0xb1, 0x26 },
+ { 0xb2, 0x28 },
+ { 0xb3, 0x28 },
+ { 0xb4, 0x33 },
+ { 0xb5, 0x08 },
+ { 0xb6, 0x26 },
+ { 0xb7, 0x08 },
+ { 0xb8, 0x26 },
+ { 0xf0, 0x00 },
+ { 0xf6, 0xc0 },
};
static const struct nv3052c_reg fs035vg158_panel_regs[] = {
@@ -291,146 +300,6 @@ static const struct nv3052c_reg fs035vg158_panel_regs[] = {
{ 0xb8, 0x26 },
{ 0xf0, 0x00 },
{ 0xf6, 0xc0 },
- // EXTC Command set enable, select page 0
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
- // Set gray scale voltage to adjust gamma
- { 0xb0, 0x0b }, // PGAMVR0
- { 0xb1, 0x16 }, // PGAMVR1
- { 0xb2, 0x17 }, // PGAMVR2
- { 0xb3, 0x2c }, // PGAMVR3
- { 0xb4, 0x32 }, // PGAMVR4
- { 0xb5, 0x3b }, // PGAMVR5
- { 0xb6, 0x29 }, // PGAMPR0
- { 0xb7, 0x40 }, // PGAMPR1
- { 0xb8, 0x0d }, // PGAMPK0
- { 0xb9, 0x05 }, // PGAMPK1
- { 0xba, 0x12 }, // PGAMPK2
- { 0xbb, 0x10 }, // PGAMPK3
- { 0xbc, 0x12 }, // PGAMPK4
- { 0xbd, 0x15 }, // PGAMPK5
- { 0xbe, 0x19 }, // PGAMPK6
- { 0xbf, 0x0e }, // PGAMPK7
- { 0xc0, 0x16 }, // PGAMPK8
- { 0xc1, 0x0a }, // PGAMPK9
- // Set gray scale voltage to adjust gamma
- { 0xd0, 0x0c }, // NGAMVR0
- { 0xd1, 0x17 }, // NGAMVR0
- { 0xd2, 0x14 }, // NGAMVR1
- { 0xd3, 0x2e }, // NGAMVR2
- { 0xd4, 0x32 }, // NGAMVR3
- { 0xd5, 0x3c }, // NGAMVR4
- { 0xd6, 0x22 }, // NGAMPR0
- { 0xd7, 0x3d }, // NGAMPR1
- { 0xd8, 0x0d }, // NGAMPK0
- { 0xd9, 0x07 }, // NGAMPK1
- { 0xda, 0x13 }, // NGAMPK2
- { 0xdb, 0x13 }, // NGAMPK3
- { 0xdc, 0x11 }, // NGAMPK4
- { 0xdd, 0x15 }, // NGAMPK5
- { 0xde, 0x19 }, // NGAMPK6
- { 0xdf, 0x10 }, // NGAMPK7
- { 0xe0, 0x17 }, // NGAMPK8
- { 0xe1, 0x0a }, // NGAMPK9
- // EXTC Command set enable, select page 3
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 },
- // Set various timing settings
- { 0x00, 0x2a }, // GIP_VST_1
- { 0x01, 0x2a }, // GIP_VST_2
- { 0x02, 0x2a }, // GIP_VST_3
- { 0x03, 0x2a }, // GIP_VST_4
- { 0x04, 0x61 }, // GIP_VST_5
- { 0x05, 0x80 }, // GIP_VST_6
- { 0x06, 0xc7 }, // GIP_VST_7
- { 0x07, 0x01 }, // GIP_VST_8
- { 0x08, 0x03 }, // GIP_VST_9
- { 0x09, 0x04 }, // GIP_VST_10
- { 0x70, 0x22 }, // GIP_ECLK1
- { 0x71, 0x80 }, // GIP_ECLK2
- { 0x30, 0x2a }, // GIP_CLK_1
- { 0x31, 0x2a }, // GIP_CLK_2
- { 0x32, 0x2a }, // GIP_CLK_3
- { 0x33, 0x2a }, // GIP_CLK_4
- { 0x34, 0x61 }, // GIP_CLK_5
- { 0x35, 0xc5 }, // GIP_CLK_6
- { 0x36, 0x80 }, // GIP_CLK_7
- { 0x37, 0x23 }, // GIP_CLK_8
- { 0x40, 0x03 }, // GIP_CLKA_1
- { 0x41, 0x04 }, // GIP_CLKA_2
- { 0x42, 0x05 }, // GIP_CLKA_3
- { 0x43, 0x06 }, // GIP_CLKA_4
- { 0x44, 0x11 }, // GIP_CLKA_5
- { 0x45, 0xe8 }, // GIP_CLKA_6
- { 0x46, 0xe9 }, // GIP_CLKA_7
- { 0x47, 0x11 }, // GIP_CLKA_8
- { 0x48, 0xea }, // GIP_CLKA_9
- { 0x49, 0xeb }, // GIP_CLKA_10
- { 0x50, 0x07 }, // GIP_CLKB_1
- { 0x51, 0x08 }, // GIP_CLKB_2
- { 0x52, 0x09 }, // GIP_CLKB_3
- { 0x53, 0x0a }, // GIP_CLKB_4
- { 0x54, 0x11 }, // GIP_CLKB_5
- { 0x55, 0xec }, // GIP_CLKB_6
- { 0x56, 0xed }, // GIP_CLKB_7
- { 0x57, 0x11 }, // GIP_CLKB_8
- { 0x58, 0xef }, // GIP_CLKB_9
- { 0x59, 0xf0 }, // GIP_CLKB_10
- // Map internal GOA signals to GOA output pad
- { 0xb1, 0x01 }, // PANELD2U2
- { 0xb4, 0x15 }, // PANELD2U5
- { 0xb5, 0x16 }, // PANELD2U6
- { 0xb6, 0x09 }, // PANELD2U7
- { 0xb7, 0x0f }, // PANELD2U8
- { 0xb8, 0x0d }, // PANELD2U9
- { 0xb9, 0x0b }, // PANELD2U10
- { 0xba, 0x00 }, // PANELD2U11
- { 0xc7, 0x02 }, // PANELD2U24
- { 0xca, 0x17 }, // PANELD2U27
- { 0xcb, 0x18 }, // PANELD2U28
- { 0xcc, 0x0a }, // PANELD2U29
- { 0xcd, 0x10 }, // PANELD2U30
- { 0xce, 0x0e }, // PANELD2U31
- { 0xcf, 0x0c }, // PANELD2U32
- { 0xd0, 0x00 }, // PANELD2U33
- // Map internal GOA signals to GOA output pad
- { 0x81, 0x00 }, // PANELU2D2
- { 0x84, 0x15 }, // PANELU2D5
- { 0x85, 0x16 }, // PANELU2D6
- { 0x86, 0x10 }, // PANELU2D7
- { 0x87, 0x0a }, // PANELU2D8
- { 0x88, 0x0c }, // PANELU2D9
- { 0x89, 0x0e }, // PANELU2D10
- { 0x8a, 0x02 }, // PANELU2D11
- { 0x97, 0x00 }, // PANELU2D24
- { 0x9a, 0x17 }, // PANELU2D27
- { 0x9b, 0x18 }, // PANELU2D28
- { 0x9c, 0x0f }, // PANELU2D29
- { 0x9d, 0x09 }, // PANELU2D30
- { 0x9e, 0x0b }, // PANELU2D31
- { 0x9f, 0x0d }, // PANELU2D32
- { 0xa0, 0x01 }, // PANELU2D33
- // EXTC Command set enable, select page 2
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
- // Unknown registers
- { 0x01, 0x01 },
- { 0x02, 0xda },
- { 0x03, 0xba },
- { 0x04, 0xa8 },
- { 0x05, 0x9a },
- { 0x06, 0x70 },
- { 0x07, 0xff },
- { 0x08, 0x91 },
- { 0x09, 0x90 },
- { 0x0a, 0xff },
- { 0x0b, 0x8f },
- { 0x0c, 0x60 },
- { 0x0d, 0x58 },
- { 0x0e, 0x48 },
- { 0x0f, 0x38 },
- { 0x10, 0x2b },
- // EXTC Command set enable, select page 0
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 },
- // Display Access Control
- { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0
};
@@ -487,146 +356,6 @@ static const struct nv3052c_reg wl_355608_a8_panel_regs[] = {
{ 0xb8, 0x26 },
{ 0xf0, 0x00 },
{ 0xf6, 0xc0 },
- // EXTC Command set enable, select page 2
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
- // Set gray scale voltage to adjust gamma
- { 0xb0, 0x0b }, // PGAMVR0
- { 0xb1, 0x16 }, // PGAMVR1
- { 0xb2, 0x17 }, // PGAMVR2
- { 0xb3, 0x2c }, // PGAMVR3
- { 0xb4, 0x32 }, // PGAMVR4
- { 0xb5, 0x3b }, // PGAMVR5
- { 0xb6, 0x29 }, // PGAMPR0
- { 0xb7, 0x40 }, // PGAMPR1
- { 0xb8, 0x0d }, // PGAMPK0
- { 0xb9, 0x05 }, // PGAMPK1
- { 0xba, 0x12 }, // PGAMPK2
- { 0xbb, 0x10 }, // PGAMPK3
- { 0xbc, 0x12 }, // PGAMPK4
- { 0xbd, 0x15 }, // PGAMPK5
- { 0xbe, 0x19 }, // PGAMPK6
- { 0xbf, 0x0e }, // PGAMPK7
- { 0xc0, 0x16 }, // PGAMPK8
- { 0xc1, 0x0a }, // PGAMPK9
- // Set gray scale voltage to adjust gamma
- { 0xd0, 0x0c }, // NGAMVR0
- { 0xd1, 0x17 }, // NGAMVR0
- { 0xd2, 0x14 }, // NGAMVR1
- { 0xd3, 0x2e }, // NGAMVR2
- { 0xd4, 0x32 }, // NGAMVR3
- { 0xd5, 0x3c }, // NGAMVR4
- { 0xd6, 0x22 }, // NGAMPR0
- { 0xd7, 0x3d }, // NGAMPR1
- { 0xd8, 0x0d }, // NGAMPK0
- { 0xd9, 0x07 }, // NGAMPK1
- { 0xda, 0x13 }, // NGAMPK2
- { 0xdb, 0x13 }, // NGAMPK3
- { 0xdc, 0x11 }, // NGAMPK4
- { 0xdd, 0x15 }, // NGAMPK5
- { 0xde, 0x19 }, // NGAMPK6
- { 0xdf, 0x10 }, // NGAMPK7
- { 0xe0, 0x17 }, // NGAMPK8
- { 0xe1, 0x0a }, // NGAMPK9
- // EXTC Command set enable, select page 3
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x03 },
- // Set various timing settings
- { 0x00, 0x2a }, // GIP_VST_1
- { 0x01, 0x2a }, // GIP_VST_2
- { 0x02, 0x2a }, // GIP_VST_3
- { 0x03, 0x2a }, // GIP_VST_4
- { 0x04, 0x61 }, // GIP_VST_5
- { 0x05, 0x80 }, // GIP_VST_6
- { 0x06, 0xc7 }, // GIP_VST_7
- { 0x07, 0x01 }, // GIP_VST_8
- { 0x08, 0x03 }, // GIP_VST_9
- { 0x09, 0x04 }, // GIP_VST_10
- { 0x70, 0x22 }, // GIP_ECLK1
- { 0x71, 0x80 }, // GIP_ECLK2
- { 0x30, 0x2a }, // GIP_CLK_1
- { 0x31, 0x2a }, // GIP_CLK_2
- { 0x32, 0x2a }, // GIP_CLK_3
- { 0x33, 0x2a }, // GIP_CLK_4
- { 0x34, 0x61 }, // GIP_CLK_5
- { 0x35, 0xc5 }, // GIP_CLK_6
- { 0x36, 0x80 }, // GIP_CLK_7
- { 0x37, 0x23 }, // GIP_CLK_8
- { 0x40, 0x03 }, // GIP_CLKA_1
- { 0x41, 0x04 }, // GIP_CLKA_2
- { 0x42, 0x05 }, // GIP_CLKA_3
- { 0x43, 0x06 }, // GIP_CLKA_4
- { 0x44, 0x11 }, // GIP_CLKA_5
- { 0x45, 0xe8 }, // GIP_CLKA_6
- { 0x46, 0xe9 }, // GIP_CLKA_7
- { 0x47, 0x11 }, // GIP_CLKA_8
- { 0x48, 0xea }, // GIP_CLKA_9
- { 0x49, 0xeb }, // GIP_CLKA_10
- { 0x50, 0x07 }, // GIP_CLKB_1
- { 0x51, 0x08 }, // GIP_CLKB_2
- { 0x52, 0x09 }, // GIP_CLKB_3
- { 0x53, 0x0a }, // GIP_CLKB_4
- { 0x54, 0x11 }, // GIP_CLKB_5
- { 0x55, 0xec }, // GIP_CLKB_6
- { 0x56, 0xed }, // GIP_CLKB_7
- { 0x57, 0x11 }, // GIP_CLKB_8
- { 0x58, 0xef }, // GIP_CLKB_9
- { 0x59, 0xf0 }, // GIP_CLKB_10
- // Map internal GOA signals to GOA output pad
- { 0xb1, 0x01 }, // PANELD2U2
- { 0xb4, 0x15 }, // PANELD2U5
- { 0xb5, 0x16 }, // PANELD2U6
- { 0xb6, 0x09 }, // PANELD2U7
- { 0xb7, 0x0f }, // PANELD2U8
- { 0xb8, 0x0d }, // PANELD2U9
- { 0xb9, 0x0b }, // PANELD2U10
- { 0xba, 0x00 }, // PANELD2U11
- { 0xc7, 0x02 }, // PANELD2U24
- { 0xca, 0x17 }, // PANELD2U27
- { 0xcb, 0x18 }, // PANELD2U28
- { 0xcc, 0x0a }, // PANELD2U29
- { 0xcd, 0x10 }, // PANELD2U30
- { 0xce, 0x0e }, // PANELD2U31
- { 0xcf, 0x0c }, // PANELD2U32
- { 0xd0, 0x00 }, // PANELD2U33
- // Map internal GOA signals to GOA output pad
- { 0x81, 0x00 }, // PANELU2D2
- { 0x84, 0x15 }, // PANELU2D5
- { 0x85, 0x16 }, // PANELU2D6
- { 0x86, 0x10 }, // PANELU2D7
- { 0x87, 0x0a }, // PANELU2D8
- { 0x88, 0x0c }, // PANELU2D9
- { 0x89, 0x0e }, // PANELU2D10
- { 0x8a, 0x02 }, // PANELU2D11
- { 0x97, 0x00 }, // PANELU2D24
- { 0x9a, 0x17 }, // PANELU2D27
- { 0x9b, 0x18 }, // PANELU2D28
- { 0x9c, 0x0f }, // PANELU2D29
- { 0x9d, 0x09 }, // PANELU2D30
- { 0x9e, 0x0b }, // PANELU2D31
- { 0x9f, 0x0d }, // PANELU2D32
- { 0xa0, 0x01 }, // PANELU2D33
- // EXTC Command set enable, select page 2
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x02 },
- // Unknown registers
- { 0x01, 0x01 },
- { 0x02, 0xda },
- { 0x03, 0xba },
- { 0x04, 0xa8 },
- { 0x05, 0x9a },
- { 0x06, 0x70 },
- { 0x07, 0xff },
- { 0x08, 0x91 },
- { 0x09, 0x90 },
- { 0x0a, 0xff },
- { 0x0b, 0x8f },
- { 0x0c, 0x60 },
- { 0x0d, 0x58 },
- { 0x0e, 0x48 },
- { 0x0f, 0x38 },
- { 0x10, 0x2b },
- // EXTC Command set enable, select page 0
- { 0xff, 0x30 }, { 0xff, 0x52 }, { 0xff, 0x00 },
- // Display Access Control
- { 0x36, 0x0a }, // bgr = 1, ss = 1, gs = 0
};
static inline struct nv3052c *to_nv3052c(struct drm_panel *panel)
@@ -655,6 +384,7 @@ static int nv3052c_prepare(struct drm_panel *panel)
gpiod_set_value_cansleep(priv->reset_gpio, 0);
usleep_range(5000, 20000);
+ /* Apply panel-specific initialization registers */
for (i = 0; i < panel_regs_len; i++) {
err = mipi_dbi_command(dbi, panel_regs[i].cmd,
panel_regs[i].val);
@@ -665,6 +395,16 @@ static int nv3052c_prepare(struct drm_panel *panel)
}
}
+ /* Apply common initialization registers */
+ for (i = 0; i < ARRAY_SIZE(common_init_regs); i++) {
+ err = mipi_dbi_command(dbi, common_init_regs[i].cmd,
+ common_init_regs[i].val);
+ if (err) {
+ dev_err(priv->dev, "Unable to set register: %d\n", err);
+ goto err_disable_regulator;
+ }
+ }
+
err = mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
if (err) {
dev_err(priv->dev, "Unable to exit sleep mode: %d\n", err);
diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
index ad35d0fb0a16..c3fbc459c7e0 100644
--- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
+++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c
@@ -54,9 +54,9 @@ static int rb070d30_panel_prepare(struct drm_panel *panel)
}
msleep(20);
- gpiod_set_value(ctx->gpios.power, 1);
+ gpiod_set_value_cansleep(ctx->gpios.power, 1);
msleep(20);
- gpiod_set_value(ctx->gpios.reset, 1);
+ gpiod_set_value_cansleep(ctx->gpios.reset, 1);
msleep(20);
return 0;
}
@@ -65,8 +65,8 @@ static int rb070d30_panel_unprepare(struct drm_panel *panel)
{
struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel);
- gpiod_set_value(ctx->gpios.reset, 0);
- gpiod_set_value(ctx->gpios.power, 0);
+ gpiod_set_value_cansleep(ctx->gpios.reset, 0);
+ gpiod_set_value_cansleep(ctx->gpios.power, 0);
regulator_disable(ctx->supply);
return 0;
diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c b/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c
new file mode 100644
index 000000000000..e63080204af7
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Nia Espera <a5b6@riseup.net>
+ * Copyright (c) 2025 David Heidelberg <david@ixit.cz>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/swab.h>
+#include <linux/backlight.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#define MCS_ELVSS_ON 0xb1
+
+struct samsung_s6e3fc2x01 {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+};
+
+static const struct regulator_bulk_data s6e3fc2x01_supplies[] = {
+ { .supply = "vddio" },
+ { .supply = "vci" },
+ { .supply = "poc" },
+};
+
+static inline
+struct samsung_s6e3fc2x01 *to_samsung_s6e3fc2x01(struct drm_panel *panel)
+{
+ return container_of(panel, struct samsung_s6e3fc2x01, panel);
+}
+
+#define s6e3fc2x01_test_key_on_lvl1(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x9f, 0xa5, 0xa5)
+#define s6e3fc2x01_test_key_off_lvl1(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0x9f, 0x5a, 0x5a)
+#define s6e3fc2x01_test_key_on_lvl2(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
+#define s6e3fc2x01_test_key_off_lvl2(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
+#define s6e3fc2x01_test_key_on_lvl3(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x5a, 0x5a)
+#define s6e3fc2x01_test_key_off_lvl3(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0xa5, 0xa5)
+
+static void s6e3fc2x01_reset(struct samsung_s6e3fc2x01 *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(5000, 6000);
+}
+
+static int s6e3fc2x01_on(struct samsung_s6e3fc2x01 *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ s6e3fc2x01_test_key_on_lvl1(&dsi_ctx);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x0a);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ s6e3fc2x01_test_key_off_lvl1(&dsi_ctx);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xcd, 0x01);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+
+ mipi_dsi_usleep_range(&dsi_ctx, 15000, 16000);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x0f);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ s6e3fc2x01_test_key_on_lvl1(&dsi_ctx);
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ s6e3fc2x01_test_key_off_lvl1(&dsi_ctx);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xeb, 0x17,
+ 0x41, 0x92,
+ 0x0e, 0x10,
+ 0x82, 0x5a);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+
+ /* Column & Page Address Setting */
+ mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0x0000, 0x0437);
+ mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0x0000, 0x0923);
+
+ /* Horizontal & Vertical sync Setting */
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe8, 0x10, 0x30);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+
+ s6e3fc2x01_test_key_on_lvl3(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe3, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed, 0x67);
+ s6e3fc2x01_test_key_off_lvl3(&dsi_ctx);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb7, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb7, 0x12);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+ mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ELVSS_ON, 0x00, 0x01);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb3, 0x00, 0xc1);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x78);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x90);
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ELVSS_ON, 0xc6, 0x00, 0x00,
+ 0x21, 0xed, 0x02, 0x08, 0x06, 0xc1, 0x27,
+ 0xfc, 0xdc, 0xe4, 0x00, 0xd9, 0xe6, 0xe7,
+ 0x00, 0xfc, 0xff, 0xea);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MCS_ELVSS_ON, 0x00, 0x00);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ return dsi_ctx.accum_err;
+}
+
+static int s6e3fc2x01_enable(struct drm_panel *panel)
+{
+ struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ s6e3fc2x01_test_key_on_lvl1(&dsi_ctx);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ s6e3fc2x01_test_key_off_lvl1(&dsi_ctx);
+
+ return dsi_ctx.accum_err;
+}
+
+static int s6e3fc2x01_off(struct samsung_s6e3fc2x01 *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ s6e3fc2x01_test_key_on_lvl1(&dsi_ctx);
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+
+ mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 16000, 17000);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x82);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 16000, 17000);
+
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+
+ s6e3fc2x01_test_key_off_lvl1(&dsi_ctx);
+
+ s6e3fc2x01_test_key_on_lvl2(&dsi_ctx);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf4, 0x01);
+ s6e3fc2x01_test_key_off_lvl2(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 160);
+
+ return dsi_ctx.accum_err;
+}
+
+static int s6e3fc2x01_disable(struct drm_panel *panel)
+{
+ struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel);
+
+ s6e3fc2x01_off(ctx);
+
+ return 0;
+}
+
+static int s6e3fc2x01_prepare(struct drm_panel *panel)
+{
+ struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(s6e3fc2x01_supplies), ctx->supplies);
+ if (ret < 0)
+ return ret;
+
+ s6e3fc2x01_reset(ctx);
+
+ ret = s6e3fc2x01_on(ctx);
+ if (ret < 0) {
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(s6e3fc2x01_supplies), ctx->supplies);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int s6e3fc2x01_unprepare(struct drm_panel *panel)
+{
+ struct samsung_s6e3fc2x01 *ctx = to_samsung_s6e3fc2x01(panel);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(s6e3fc2x01_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static const struct drm_display_mode ams641rw_mode = {
+ .clock = (1080 + 72 + 16 + 36) * (2340 + 32 + 4 + 18) * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 72,
+ .hsync_end = 1080 + 72 + 16,
+ .htotal = 1080 + 72 + 16 + 36,
+ .vdisplay = 2340,
+ .vsync_start = 2340 + 32,
+ .vsync_end = 2340 + 32 + 4,
+ .vtotal = 2340 + 32 + 4 + 18,
+ .width_mm = 68,
+ .height_mm = 145,
+};
+
+static int s6e3fc2x01_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector, &ams641rw_mode);
+}
+
+static const struct drm_panel_funcs samsung_s6e3fc2x01_panel_funcs = {
+ .prepare = s6e3fc2x01_prepare,
+ .enable = s6e3fc2x01_enable,
+ .disable = s6e3fc2x01_disable,
+ .unprepare = s6e3fc2x01_unprepare,
+ .get_modes = s6e3fc2x01_get_modes,
+};
+
+static int s6e3fc2x01_panel_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int err;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ err = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+ if (err < 0)
+ return err;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static const struct backlight_ops s6e3fc2x01_panel_bl_ops = {
+ .update_status = s6e3fc2x01_panel_bl_update_status,
+};
+
+static struct backlight_device *
+s6e3fc2x01_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_PLATFORM,
+ .brightness = 512,
+ .max_brightness = 1023,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &s6e3fc2x01_panel_bl_ops, &props);
+}
+
+static int s6e3fc2x01_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct samsung_s6e3fc2x01 *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct samsung_s6e3fc2x01, panel,
+ &samsung_s6e3fc2x01_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = devm_regulator_bulk_get_const(dev,
+ ARRAY_SIZE(s6e3fc2x01_supplies),
+ s6e3fc2x01_supplies,
+ &ctx->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+
+ /* keep the display on for flicker-free experience */
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
+
+ ctx->panel.prepare_prev_first = true;
+
+ ctx->panel.backlight = s6e3fc2x01_create_backlight(dsi);
+ if (IS_ERR(ctx->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
+ "Failed to create backlight\n");
+
+ drm_panel_add(&ctx->panel);
+
+ ret = mipi_dsi_attach(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
+ drm_panel_remove(&ctx->panel);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void s6e3fc2x01_remove(struct mipi_dsi_device *dsi)
+{
+ struct samsung_s6e3fc2x01 *ctx = mipi_dsi_get_drvdata(dsi);
+ int ret;
+
+ ret = mipi_dsi_detach(dsi);
+ if (ret < 0)
+ dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct of_device_id s6e3fc2x01_of_match[] = {
+ { .compatible = "samsung,s6e3fc2x01-ams641rw", .data = &ams641rw_mode },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, s6e3fc2x01_of_match);
+
+static struct mipi_dsi_driver s6e3fc2x01_driver = {
+ .probe = s6e3fc2x01_probe,
+ .remove = s6e3fc2x01_remove,
+ .driver = {
+ .name = "panel-samsung-s6e3fc2x01",
+ .of_match_table = s6e3fc2x01_of_match,
+ },
+};
+module_mipi_dsi_driver(s6e3fc2x01_driver);
+
+MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
+MODULE_DESCRIPTION("DRM driver for Samsung S6E3FC2X01 DDIC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
index 064258217d50..e00a497a7c96 100644
--- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c
+++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c
@@ -16,20 +16,32 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
struct sofef00_panel {
struct drm_panel panel;
struct mipi_dsi_device *dsi;
- struct regulator *supply;
+ struct regulator_bulk_data *supplies;
struct gpio_desc *reset_gpio;
};
+static const struct regulator_bulk_data sofef00_supplies[] = {
+ { .supply = "vddio" },
+ { .supply = "vci" },
+ { .supply = "poc" },
+};
+
static inline
struct sofef00_panel *to_sofef00_panel(struct drm_panel *panel)
{
return container_of(panel, struct sofef00_panel, panel);
}
+#define sofef00_test_key_on_lvl2(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
+#define sofef00_test_key_off_lvl2(ctx) \
+ mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
+
static void sofef00_panel_reset(struct sofef00_panel *ctx)
{
gpiod_set_value_cansleep(ctx->reset_gpio, 0);
@@ -50,18 +62,26 @@ static int sofef00_panel_on(struct sofef00_panel *ctx)
mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000);
- mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
-
+ sofef00_test_key_on_lvl2(&dsi_ctx);
mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ sofef00_test_key_off_lvl2(&dsi_ctx);
- mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
- mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ sofef00_test_key_on_lvl2(&dsi_ctx);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x07);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0x12);
- mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+ sofef00_test_key_off_lvl2(&dsi_ctx);
+
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+ return dsi_ctx.accum_err;
+}
+
+static int sofef00_enable(struct drm_panel *panel)
+{
+ struct sofef00_panel *ctx = to_sofef00_panel(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
return dsi_ctx.accum_err;
@@ -72,8 +92,6 @@ static int sofef00_panel_off(struct sofef00_panel *ctx)
struct mipi_dsi_device *dsi = ctx->dsi;
struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
- dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
-
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
mipi_dsi_msleep(&dsi_ctx, 40);
@@ -86,70 +104,70 @@ static int sofef00_panel_off(struct sofef00_panel *ctx)
static int sofef00_panel_prepare(struct drm_panel *panel)
{
struct sofef00_panel *ctx = to_sofef00_panel(panel);
- struct device *dev = &ctx->dsi->dev;
int ret;
- ret = regulator_enable(ctx->supply);
- if (ret < 0) {
- dev_err(dev, "Failed to enable regulator: %d\n", ret);
+ ret = regulator_bulk_enable(ARRAY_SIZE(sofef00_supplies), ctx->supplies);
+ if (ret < 0)
return ret;
- }
sofef00_panel_reset(ctx);
ret = sofef00_panel_on(ctx);
if (ret < 0) {
gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(sofef00_supplies), ctx->supplies);
return ret;
}
return 0;
}
-static int sofef00_panel_unprepare(struct drm_panel *panel)
+static int sofef00_disable(struct drm_panel *panel)
{
struct sofef00_panel *ctx = to_sofef00_panel(panel);
sofef00_panel_off(ctx);
- regulator_disable(ctx->supply);
return 0;
}
-static const struct drm_display_mode enchilada_panel_mode = {
+static int sofef00_panel_unprepare(struct drm_panel *panel)
+{
+ struct sofef00_panel *ctx = to_sofef00_panel(panel);
+
+ regulator_bulk_disable(ARRAY_SIZE(sofef00_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static const struct drm_display_mode ams628nw01_panel_mode = {
.clock = (1080 + 112 + 16 + 36) * (2280 + 36 + 8 + 12) * 60 / 1000,
+
.hdisplay = 1080,
.hsync_start = 1080 + 112,
.hsync_end = 1080 + 112 + 16,
.htotal = 1080 + 112 + 16 + 36,
+
.vdisplay = 2280,
.vsync_start = 2280 + 36,
.vsync_end = 2280 + 36 + 8,
.vtotal = 2280 + 36 + 8 + 12,
+
.width_mm = 68,
.height_mm = 145,
+
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
static int sofef00_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector)
{
- struct drm_display_mode *mode;
-
- mode = drm_mode_duplicate(connector->dev, &enchilada_panel_mode);
- if (!mode)
- return -ENOMEM;
-
- drm_mode_set_name(mode);
-
- mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
- connector->display_info.width_mm = mode->width_mm;
- connector->display_info.height_mm = mode->height_mm;
- drm_mode_probed_add(connector, mode);
-
- return 1;
+ return drm_connector_helper_get_modes_fixed(connector, &ams628nw01_panel_mode);
}
static const struct drm_panel_funcs sofef00_panel_panel_funcs = {
.prepare = sofef00_panel_prepare,
+ .enable = sofef00_enable,
+ .disable = sofef00_disable,
.unprepare = sofef00_panel_unprepare,
.get_modes = sofef00_panel_get_modes,
};
@@ -160,10 +178,14 @@ static int sofef00_panel_bl_update_status(struct backlight_device *bl)
int err;
u16 brightness = (u16)backlight_get_brightness(bl);
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
err = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
if (err < 0)
return err;
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
return 0;
}
@@ -177,7 +199,7 @@ sofef00_create_backlight(struct mipi_dsi_device *dsi)
struct device *dev = &dsi->dev;
const struct backlight_properties props = {
.type = BACKLIGHT_PLATFORM,
- .brightness = 1023,
+ .brightness = 512,
.max_brightness = 1023,
};
@@ -197,10 +219,12 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi)
if (IS_ERR(ctx))
return PTR_ERR(ctx);
- ctx->supply = devm_regulator_get(dev, "vddio");
- if (IS_ERR(ctx->supply))
- return dev_err_probe(dev, PTR_ERR(ctx->supply),
- "Failed to get vddio regulator\n");
+ ret = devm_regulator_bulk_get_const(dev,
+ ARRAY_SIZE(sofef00_supplies),
+ sofef00_supplies,
+ &ctx->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(ctx->reset_gpio))
@@ -212,6 +236,10 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi)
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM;
+
+ ctx->panel.prepare_prev_first = true;
ctx->panel.backlight = sofef00_create_backlight(dsi);
if (IS_ERR(ctx->panel.backlight))
@@ -243,7 +271,8 @@ static void sofef00_panel_remove(struct mipi_dsi_device *dsi)
}
static const struct of_device_id sofef00_panel_of_match[] = {
- { .compatible = "samsung,sofef00" },
+ { .compatible = "samsung,sofef00" }, /* legacy */
+ { .compatible = "samsung,sofef00-ams628nw01" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sofef00_panel_of_match);
@@ -252,7 +281,7 @@ static struct mipi_dsi_driver sofef00_panel_driver = {
.probe = sofef00_panel_probe,
.remove = sofef00_panel_remove,
.driver = {
- .name = "panel-oneplus6",
+ .name = "panel-samsung-sofef00",
.of_match_table = sofef00_panel_of_match,
},
};
@@ -260,5 +289,5 @@ static struct mipi_dsi_driver sofef00_panel_driver = {
module_mipi_dsi_driver(sofef00_panel_driver);
MODULE_AUTHOR("Casey Connolly <casey.connolly@linaro.org>");
-MODULE_DESCRIPTION("DRM driver for Samsung AMOLED DSI panels found in OnePlus 6/6T phones");
+MODULE_DESCRIPTION("DRM driver for Samsung SOFEF00 DDIC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c
new file mode 100644
index 000000000000..8c00fde1c4a9
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sharp-lq079l1sx01.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2016 XiaoMi, Inc.
+ * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+static const struct regulator_bulk_data sharp_supplies[] = {
+ { .supply = "avdd" }, { .supply = "vddio" },
+ { .supply = "vsp" }, { .supply = "vsn" },
+};
+
+struct sharp_panel {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi[2];
+
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data *supplies;
+
+ const struct drm_display_mode *mode;
+};
+
+static inline struct sharp_panel *to_sharp_panel(struct drm_panel *panel)
+{
+ return container_of(panel, struct sharp_panel, panel);
+}
+
+static void sharp_panel_reset(struct sharp_panel *sharp)
+{
+ gpiod_set_value_cansleep(sharp->reset_gpio, 1);
+ usleep_range(2000, 3000);
+ gpiod_set_value_cansleep(sharp->reset_gpio, 0);
+ usleep_range(2000, 3000);
+}
+
+static int sharp_panel_prepare(struct drm_panel *panel)
+{
+ struct sharp_panel *sharp = to_sharp_panel(panel);
+ struct device *dev = panel->dev;
+ struct mipi_dsi_device *dsi0 = sharp->dsi[0];
+ struct mipi_dsi_device *dsi1 = sharp->dsi[1];
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL };
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(sharp_supplies), sharp->supplies);
+ if (ret) {
+ dev_err(dev, "error enabling regulators (%d)\n", ret);
+ return ret;
+ }
+
+ msleep(24);
+
+ if (sharp->reset_gpio)
+ sharp_panel_reset(sharp);
+
+ msleep(32);
+
+ mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_EXIT_SLEEP_MODE);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1,
+ MIPI_DCS_SET_DISPLAY_BRIGHTNESS, 0xff);
+ mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1,
+ MIPI_DCS_WRITE_POWER_SAVE, 0x01);
+ mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1,
+ MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x2c);
+
+ mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_ON);
+
+ return 0;
+}
+
+static int sharp_panel_unprepare(struct drm_panel *panel)
+{
+ struct sharp_panel *sharp = to_sharp_panel(panel);
+ struct mipi_dsi_device *dsi0 = sharp->dsi[0];
+ struct mipi_dsi_device *dsi1 = sharp->dsi[1];
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = NULL };
+
+ mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_SET_DISPLAY_OFF);
+ mipi_dsi_msleep(&dsi_ctx, 100);
+ mipi_dsi_dual_dcs_write_seq_multi(&dsi_ctx, dsi0, dsi1, MIPI_DCS_ENTER_SLEEP_MODE);
+ mipi_dsi_msleep(&dsi_ctx, 150);
+
+ if (sharp->reset_gpio)
+ gpiod_set_value_cansleep(sharp->reset_gpio, 1);
+
+ return regulator_bulk_disable(ARRAY_SIZE(sharp_supplies), sharp->supplies);
+}
+
+static const struct drm_display_mode default_mode = {
+ .clock = (1536 + 136 + 28 + 28) * (2048 + 14 + 8 + 2) * 60 / 1000,
+ .hdisplay = 1536,
+ .hsync_start = 1536 + 136,
+ .hsync_end = 1536 + 136 + 28,
+ .htotal = 1536 + 136 + 28 + 28,
+ .vdisplay = 2048,
+ .vsync_start = 2048 + 14,
+ .vsync_end = 2048 + 14 + 8,
+ .vtotal = 2048 + 14 + 8 + 2,
+ .width_mm = 120,
+ .height_mm = 160,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int sharp_panel_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector, &default_mode);
+}
+
+static const struct drm_panel_funcs sharp_panel_funcs = {
+ .unprepare = sharp_panel_unprepare,
+ .prepare = sharp_panel_prepare,
+ .get_modes = sharp_panel_get_modes,
+};
+
+static int sharp_panel_probe(struct mipi_dsi_device *dsi)
+{
+ const struct mipi_dsi_device_info info = { "sharp-link1", 0, NULL };
+ struct device *dev = &dsi->dev;
+ struct device_node *dsi_r;
+ struct mipi_dsi_host *dsi_r_host;
+ struct sharp_panel *sharp;
+ int i, ret;
+
+ sharp = devm_drm_panel_alloc(dev, struct sharp_panel, panel,
+ &sharp_panel_funcs, DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(sharp))
+ return PTR_ERR(sharp);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(sharp_supplies),
+ sharp_supplies, &sharp->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get supplies\n");
+
+ sharp->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(sharp->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(sharp->reset_gpio),
+ "failed to get reset GPIO\n");
+
+ /* Panel is always connected to two DSI hosts, DSI0 is left, DSI1 is right */
+ dsi_r = of_graph_get_remote_node(dsi->dev.of_node, 1, -1);
+ if (!dsi_r)
+ return dev_err_probe(dev, -ENODEV, "failed to find second DSI host node\n");
+
+ dsi_r_host = of_find_mipi_dsi_host_by_node(dsi_r);
+ of_node_put(dsi_r);
+ if (!dsi_r_host)
+ return dev_err_probe(dev, -EPROBE_DEFER, "cannot get secondary DSI host\n");
+
+ sharp->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi_r_host, &info);
+ if (IS_ERR(sharp->dsi[1]))
+ return dev_err_probe(dev, PTR_ERR(sharp->dsi[1]),
+ "second link registration failed\n");
+
+ sharp->dsi[0] = dsi;
+ mipi_dsi_set_drvdata(dsi, sharp);
+
+ ret = drm_panel_of_backlight(&sharp->panel);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get backlight\n");
+
+ drm_panel_add(&sharp->panel);
+
+ for (i = 0; i < ARRAY_SIZE(sharp->dsi); i++) {
+ if (!sharp->dsi[i])
+ continue;
+
+ sharp->dsi[i]->lanes = 4;
+ sharp->dsi[i]->format = MIPI_DSI_FMT_RGB888;
+ sharp->dsi[i]->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
+
+ ret = devm_mipi_dsi_attach(dev, sharp->dsi[i]);
+ if (ret < 0) {
+ drm_panel_remove(&sharp->panel);
+ return dev_err_probe(dev, ret, "failed to attach to DSI%d\n", i);
+ }
+ }
+
+ return 0;
+}
+
+static void sharp_panel_remove(struct mipi_dsi_device *dsi)
+{
+ struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi);
+
+ drm_panel_remove(&sharp->panel);
+}
+
+static const struct of_device_id sharp_of_match[] = {
+ { .compatible = "sharp,lq079l1sx01" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sharp_of_match);
+
+static struct mipi_dsi_driver sharp_panel_driver = {
+ .driver = {
+ .name = "panel-sharp-lq079l1sx01",
+ .of_match_table = sharp_of_match,
+ },
+ .probe = sharp_panel_probe,
+ .remove = sharp_panel_remove,
+};
+module_mipi_dsi_driver(sharp_panel_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("Sharp LQ079L1SX01 panel driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 0019de93be1b..b26b682826bc 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -2889,6 +2889,38 @@ static const struct panel_desc innolux_zj070na_01p = {
},
};
+static const struct display_timing jutouch_jt101tm023_timing = {
+ .pixelclock = { 66300000, 72400000, 78900000 },
+ .hactive = { 1280, 1280, 1280 },
+ .hfront_porch = { 12, 72, 132 },
+ .hback_porch = { 88, 88, 88 },
+ .hsync_len = { 10, 10, 48 },
+ .vactive = { 800, 800, 800 },
+ .vfront_porch = { 1, 15, 49 },
+ .vback_porch = { 23, 23, 23 },
+ .vsync_len = { 5, 6, 13 },
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW |
+ DISPLAY_FLAGS_DE_HIGH,
+};
+
+static const struct panel_desc jutouch_jt101tm023 = {
+ .timings = &jutouch_jt101tm023_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 217,
+ .height = 136,
+ },
+ .delay = {
+ .enable = 50,
+ .disable = 50,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+
static const struct display_timing koe_tx14d24vm1bpa_timing = {
.pixelclock = { 5580000, 5850000, 6200000 },
.hactive = { 320, 320, 320 },
@@ -4074,6 +4106,30 @@ static const struct panel_desc qishenglong_gopher2b_lcd = {
.connector_type = DRM_MODE_CONNECTOR_DPI,
};
+static const struct display_timing raystar_rff500f_awh_dnn_timing = {
+ .pixelclock = { 23000000, 25000000, 27000000 },
+ .hactive = { 800, 800, 800 },
+ .hback_porch = { 4, 8, 48 },
+ .hfront_porch = { 4, 8, 48 },
+ .hsync_len = { 2, 4, 8 },
+ .vactive = { 480, 480, 480 },
+ .vback_porch = { 4, 8, 12 },
+ .vfront_porch = { 4, 8, 12 },
+ .vsync_len = { 2, 4, 8 },
+};
+
+static const struct panel_desc raystar_rff500f_awh_dnn = {
+ .timings = &raystar_rff500f_awh_dnn_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 108,
+ .height = 65,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct display_timing rocktech_rk043fn48h_timing = {
.pixelclock = { 6000000, 9000000, 12000000 },
.hactive = { 480, 480, 480 },
@@ -4191,6 +4247,37 @@ static const struct panel_desc samsung_ltl101al01 = {
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
+static const struct display_timing samsung_ltl106al01_timing = {
+ .pixelclock = { 71980000, 71980000, 71980000 },
+ .hactive = { 1366, 1366, 1366 },
+ .hfront_porch = { 56, 56, 56 },
+ .hback_porch = { 106, 106, 106 },
+ .hsync_len = { 14, 14, 14 },
+ .vactive = { 768, 768, 768 },
+ .vfront_porch = { 3, 3, 3 },
+ .vback_porch = { 6, 6, 6 },
+ .vsync_len = { 1, 1, 1 },
+ .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW,
+};
+
+static const struct panel_desc samsung_ltl106al01 = {
+ .timings = &samsung_ltl106al01_timing,
+ .num_timings = 1,
+ .bpc = 8,
+ .size = {
+ .width = 235,
+ .height = 132,
+ },
+ .delay = {
+ .prepare = 5,
+ .enable = 10,
+ .disable = 10,
+ .unprepare = 5,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct drm_display_mode samsung_ltn101nt05_mode = {
.clock = 54030,
.hdisplay = 1024,
@@ -5209,6 +5296,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "innolux,zj070na-01p",
.data = &innolux_zj070na_01p,
}, {
+ .compatible = "jutouch,jt101tm023",
+ .data = &jutouch_jt101tm023,
+ }, {
.compatible = "koe,tx14d24vm1bpa",
.data = &koe_tx14d24vm1bpa,
}, {
@@ -5344,6 +5434,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "qishenglong,gopher2b-lcd",
.data = &qishenglong_gopher2b_lcd,
}, {
+ .compatible = "raystar,rff500f-awh-dnn",
+ .data = &raystar_rff500f_awh_dnn,
+ }, {
.compatible = "rocktech,rk043fn48h",
.data = &rocktech_rk043fn48h,
}, {
@@ -5356,6 +5449,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "samsung,ltl101al01",
.data = &samsung_ltl101al01,
}, {
+ .compatible = "samsung,ltl106al01",
+ .data = &samsung_ltl106al01,
+ }, {
.compatible = "samsung,ltn101nt05",
.data = &samsung_ltn101nt05,
}, {
@@ -5565,34 +5661,6 @@ static const struct panel_desc_dsi boe_tv080wum_nl0 = {
.lanes = 4,
};
-static const struct drm_display_mode lg_ld070wx3_sl01_mode = {
- .clock = 71000,
- .hdisplay = 800,
- .hsync_start = 800 + 32,
- .hsync_end = 800 + 32 + 1,
- .htotal = 800 + 32 + 1 + 57,
- .vdisplay = 1280,
- .vsync_start = 1280 + 28,
- .vsync_end = 1280 + 28 + 1,
- .vtotal = 1280 + 28 + 1 + 14,
-};
-
-static const struct panel_desc_dsi lg_ld070wx3_sl01 = {
- .desc = {
- .modes = &lg_ld070wx3_sl01_mode,
- .num_modes = 1,
- .bpc = 8,
- .size = {
- .width = 94,
- .height = 151,
- },
- .connector_type = DRM_MODE_CONNECTOR_DSI,
- },
- .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_CLOCK_NON_CONTINUOUS,
- .format = MIPI_DSI_FMT_RGB888,
- .lanes = 4,
-};
-
static const struct drm_display_mode lg_lh500wx1_sd03_mode = {
.clock = 67000,
.hdisplay = 720,
@@ -5717,9 +5785,6 @@ static const struct of_device_id dsi_of_match[] = {
.compatible = "boe,tv080wum-nl0",
.data = &boe_tv080wum_nl0
}, {
- .compatible = "lg,ld070wx3-sl01",
- .data = &lg_ld070wx3_sl01
- }, {
.compatible = "lg,lh500wx1-sd03",
.data = &lg_lh500wx1_sd03
}, {
diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
index 04d91929eedd..d5f821d6b23c 100644
--- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
+++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c
@@ -249,6 +249,11 @@ static const struct drm_display_mode default_mode = {
.flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
};
+/*
+ * The mode data for this panel has been reverse engineered without access
+ * to the panel datasheet / manual. Using DRM_MODE_FLAG_PHSYNC like all
+ * other panels results in garbage data on the display.
+ */
static const struct drm_display_mode t28cp45tn89_mode = {
.clock = 6008,
.hdisplay = 240,
@@ -261,7 +266,7 @@ static const struct drm_display_mode t28cp45tn89_mode = {
.vtotal = 320 + 8 + 4 + 4,
.width_mm = 43,
.height_mm = 57,
- .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC,
+ .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC,
};
static const struct drm_display_mode et028013dma_mode = {
diff --git a/drivers/gpu/drm/panel/panel-synaptics-tddi.c b/drivers/gpu/drm/panel/panel-synaptics-tddi.c
new file mode 100644
index 000000000000..0aea1854710e
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-synaptics-tddi.c
@@ -0,0 +1,277 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics TDDI display panel driver.
+ *
+ * Copyright (C) 2025 Kaustabh Chakraborty <kauschluss@disroot.org>
+ */
+
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct tddi_panel_data {
+ u8 lanes;
+ /* wait timings for panel enable */
+ u8 delay_ms_sleep_exit;
+ u8 delay_ms_display_on;
+ /* wait timings for panel disable */
+ u8 delay_ms_display_off;
+ u8 delay_ms_sleep_enter;
+};
+
+struct tddi_ctx {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct drm_display_mode mode;
+ struct backlight_device *backlight;
+ const struct tddi_panel_data *data;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *backlight_gpio;
+};
+
+static const struct regulator_bulk_data tddi_supplies[] = {
+ { .supply = "vio" },
+ { .supply = "vsn" },
+ { .supply = "vsp" },
+};
+
+static inline struct tddi_ctx *to_tddi_ctx(struct drm_panel *panel)
+{
+ return container_of(panel, struct tddi_ctx, panel);
+}
+
+static int tddi_update_status(struct backlight_device *backlight)
+{
+ struct tddi_ctx *ctx = bl_get_data(backlight);
+ struct mipi_dsi_multi_context dsi = { .dsi = ctx->dsi };
+ u8 brightness = backlight_get_brightness(backlight);
+
+ if (!ctx->panel.enabled)
+ return 0;
+
+ mipi_dsi_dcs_set_display_brightness_multi(&dsi, brightness);
+
+ return dsi.accum_err;
+}
+
+static int tddi_prepare(struct drm_panel *panel)
+{
+ struct tddi_ctx *ctx = to_tddi_ctx(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tddi_supplies), ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+
+ gpiod_set_value_cansleep(ctx->backlight_gpio, 0);
+ usleep_range(5000, 6000);
+
+ return 0;
+}
+
+static int tddi_unprepare(struct drm_panel *panel)
+{
+ struct tddi_ctx *ctx = to_tddi_ctx(panel);
+
+ gpiod_set_value_cansleep(ctx->backlight_gpio, 1);
+ usleep_range(5000, 6000);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(5000, 6000);
+
+ regulator_bulk_disable(ARRAY_SIZE(tddi_supplies), ctx->supplies);
+
+ return 0;
+}
+
+static int tddi_enable(struct drm_panel *panel)
+{
+ struct tddi_ctx *ctx = to_tddi_ctx(panel);
+ struct mipi_dsi_multi_context dsi = { .dsi = ctx->dsi };
+ u8 brightness = ctx->backlight->props.brightness;
+
+ mipi_dsi_dcs_write_seq_multi(&dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x0c);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi);
+ mipi_dsi_msleep(&dsi, ctx->data->delay_ms_sleep_exit);
+
+ /* sync the panel with the backlight's brightness level */
+ mipi_dsi_dcs_set_display_brightness_multi(&dsi, brightness);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi);
+ mipi_dsi_msleep(&dsi, ctx->data->delay_ms_display_on);
+
+ return dsi.accum_err;
+};
+
+static int tddi_disable(struct drm_panel *panel)
+{
+ struct tddi_ctx *ctx = to_tddi_ctx(panel);
+ struct mipi_dsi_multi_context dsi = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi);
+ mipi_dsi_msleep(&dsi, ctx->data->delay_ms_display_off);
+
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi);
+ mipi_dsi_msleep(&dsi, ctx->data->delay_ms_sleep_enter);
+
+ return dsi.accum_err;
+}
+
+static int tddi_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct tddi_ctx *ctx = to_tddi_ctx(panel);
+
+ return drm_connector_helper_get_modes_fixed(connector, &ctx->mode);
+}
+
+static const struct backlight_ops tddi_bl_ops = {
+ .update_status = tddi_update_status,
+};
+
+static const struct backlight_properties tddi_bl_props = {
+ .type = BACKLIGHT_PLATFORM,
+ .brightness = 255,
+ .max_brightness = 255,
+};
+
+static const struct drm_panel_funcs tddi_drm_panel_funcs = {
+ .prepare = tddi_prepare,
+ .unprepare = tddi_unprepare,
+ .enable = tddi_enable,
+ .disable = tddi_disable,
+ .get_modes = tddi_get_modes,
+};
+
+static int tddi_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct tddi_ctx *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct tddi_ctx, panel,
+ &tddi_drm_panel_funcs, DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ctx->data = of_device_get_match_data(dev);
+
+ ctx->dsi = dsi;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(tddi_supplies),
+ tddi_supplies, &ctx->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get regulators\n");
+
+ ctx->backlight_gpio = devm_gpiod_get_optional(dev, "backlight", GPIOD_ASIS);
+ if (IS_ERR(ctx->backlight_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->backlight_gpio),
+ "failed to get backlight-gpios\n");
+
+ ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "failed to get reset-gpios\n");
+
+ ret = of_get_drm_panel_display_mode(dev->of_node, &ctx->mode, NULL);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to get panel timings\n");
+
+ ctx->backlight = devm_backlight_device_register(dev, dev_name(dev), dev,
+ ctx, &tddi_bl_ops,
+ &tddi_bl_props);
+ if (IS_ERR(ctx->backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->backlight),
+ "failed to register backlight device");
+
+ dsi->lanes = ctx->data->lanes;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+ MIPI_DSI_MODE_VIDEO_NO_HFP;
+
+ ctx->panel.prepare_prev_first = true;
+ drm_panel_add(&ctx->panel);
+
+ ret = devm_mipi_dsi_attach(dev, dsi);
+ if (ret < 0) {
+ drm_panel_remove(&ctx->panel);
+ return dev_err_probe(dev, ret, "failed to attach to DSI host\n");
+ }
+
+ return 0;
+}
+
+static void tddi_remove(struct mipi_dsi_device *dsi)
+{
+ struct tddi_ctx *ctx = mipi_dsi_get_drvdata(dsi);
+
+ drm_panel_remove(&ctx->panel);
+}
+
+static const struct tddi_panel_data td4101_panel_data = {
+ .lanes = 2,
+ /* wait timings for panel enable */
+ .delay_ms_sleep_exit = 100,
+ .delay_ms_display_on = 0,
+ /* wait timings for panel disable */
+ .delay_ms_display_off = 20,
+ .delay_ms_sleep_enter = 90,
+};
+
+static const struct tddi_panel_data td4300_panel_data = {
+ .lanes = 4,
+ /* wait timings for panel enable */
+ .delay_ms_sleep_exit = 100,
+ .delay_ms_display_on = 0,
+ /* wait timings for panel disable */
+ .delay_ms_display_off = 0,
+ .delay_ms_sleep_enter = 0,
+};
+
+static const struct of_device_id tddi_of_device_id[] = {
+ {
+ .compatible = "syna,td4101-panel",
+ .data = &td4101_panel_data,
+ }, {
+ .compatible = "syna,td4300-panel",
+ .data = &td4300_panel_data,
+ }, { }
+};
+MODULE_DEVICE_TABLE(of, tddi_of_device_id);
+
+static struct mipi_dsi_driver tddi_dsi_driver = {
+ .probe = tddi_probe,
+ .remove = tddi_remove,
+ .driver = {
+ .name = "panel-synaptics-tddi",
+ .of_match_table = tddi_of_device_id,
+ },
+};
+module_mipi_dsi_driver(tddi_dsi_driver);
+
+MODULE_AUTHOR("Kaustabh Chakraborty <kauschluss@disroot.org>");
+MODULE_DESCRIPTION("Synaptics TDDI Display Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c
index 909c280eab1f..e5e688cf98fd 100644
--- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c
+++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c
@@ -3,6 +3,7 @@
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*/
+#include <linux/backlight.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/property.h>
@@ -20,6 +21,8 @@ struct visionox_rm69299_panel_desc {
const struct drm_display_mode *mode;
const u8 *init_seq;
unsigned int init_seq_len;
+ int max_brightness;
+ int initial_brightness;
};
struct visionox_rm69299 {
@@ -192,7 +195,7 @@ static int visionox_rm69299_unprepare(struct drm_panel *panel)
struct visionox_rm69299 *ctx = panel_to_ctx(panel);
struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
- ctx->dsi->mode_flags = 0;
+ ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
@@ -247,7 +250,7 @@ static const struct drm_display_mode visionox_rm69299_1080x2248_60hz = {
};
static const struct drm_display_mode visionox_rm69299_1080x2160_60hz = {
- .clock = 158695,
+ .clock = (2160 + 8 + 4 + 4) * (1080 + 26 + 2 + 36) * 60 / 1000,
.hdisplay = 1080,
.hsync_start = 1080 + 26,
.hsync_end = 1080 + 26 + 2,
@@ -285,6 +288,63 @@ static const struct drm_panel_funcs visionox_rm69299_drm_funcs = {
.get_modes = visionox_rm69299_get_modes,
};
+static int visionox_rm69299_bl_get_brightness(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return brightness;
+}
+
+static int visionox_rm69299_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static const struct backlight_ops visionox_rm69299_bl_ops = {
+ .update_status = visionox_rm69299_bl_update_status,
+ .get_brightness = visionox_rm69299_bl_get_brightness,
+};
+
+static struct backlight_device *
+visionox_rm69299_create_backlight(struct visionox_rm69299 *ctx)
+{
+ struct device *dev = &ctx->dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = ctx->desc->initial_brightness,
+ .max_brightness = ctx->desc->max_brightness,
+ };
+
+ if (!ctx->desc->max_brightness)
+ return 0;
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, ctx->dsi,
+ &visionox_rm69299_bl_ops,
+ &props);
+}
+
static int visionox_rm69299_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
@@ -316,6 +376,11 @@ static int visionox_rm69299_probe(struct mipi_dsi_device *dsi)
return PTR_ERR(ctx->reset_gpio);
}
+ ctx->panel.backlight = visionox_rm69299_create_backlight(ctx);
+ if (IS_ERR(ctx->panel.backlight))
+ return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
+ "Failed to create backlight\n");
+
drm_panel_add(&ctx->panel);
dsi->lanes = 4;
@@ -353,6 +418,8 @@ const struct visionox_rm69299_panel_desc visionox_rm69299_shift_desc = {
.mode = &visionox_rm69299_1080x2160_60hz,
.init_seq = (const u8 *)visionox_rm69299_1080x2160_60hz_init_seq,
.init_seq_len = ARRAY_SIZE(visionox_rm69299_1080x2160_60hz_init_seq),
+ .max_brightness = 255,
+ .initial_brightness = 50,
};
static const struct of_device_id visionox_rm69299_of_match[] = {
diff --git a/drivers/gpu/drm/panfrost/panfrost_devfreq.c b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
index 5d0dce10336b..b51c30778811 100644
--- a/drivers/gpu/drm/panfrost/panfrost_devfreq.c
+++ b/drivers/gpu/drm/panfrost/panfrost_devfreq.c
@@ -8,6 +8,8 @@
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
+#include <drm/drm_print.h>
+
#include "panfrost_device.h"
#include "panfrost_devfreq.h"
@@ -74,7 +76,7 @@ static int panfrost_devfreq_get_dev_status(struct device *dev,
spin_unlock_irqrestore(&pfdevfreq->lock, irqflags);
- dev_dbg(pfdev->dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
+ dev_dbg(pfdev->base.dev, "busy %lu total %lu %lu %% freq %lu MHz\n",
status->busy_time, status->total_time,
status->busy_time / (status->total_time / 100),
status->current_frequency / 1000 / 1000);
@@ -119,7 +121,7 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
int ret;
struct dev_pm_opp *opp;
unsigned long cur_freq;
- struct device *dev = &pfdev->pdev->dev;
+ struct device *dev = pfdev->base.dev;
struct devfreq *devfreq;
struct thermal_cooling_device *cooling;
struct panfrost_devfreq *pfdevfreq = &pfdev->pfdevfreq;
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index 04bec27449cb..c61b97af120c 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -20,9 +20,9 @@
static int panfrost_reset_init(struct panfrost_device *pfdev)
{
- pfdev->rstc = devm_reset_control_array_get_optional_exclusive(pfdev->dev);
+ pfdev->rstc = devm_reset_control_array_get_optional_exclusive(pfdev->base.dev);
if (IS_ERR(pfdev->rstc)) {
- dev_err(pfdev->dev, "get reset failed %ld\n", PTR_ERR(pfdev->rstc));
+ dev_err(pfdev->base.dev, "get reset failed %ld\n", PTR_ERR(pfdev->rstc));
return PTR_ERR(pfdev->rstc);
}
@@ -39,22 +39,22 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
int err;
unsigned long rate;
- pfdev->clock = devm_clk_get(pfdev->dev, NULL);
+ pfdev->clock = devm_clk_get(pfdev->base.dev, NULL);
if (IS_ERR(pfdev->clock)) {
- dev_err(pfdev->dev, "get clock failed %ld\n", PTR_ERR(pfdev->clock));
+ dev_err(pfdev->base.dev, "get clock failed %ld\n", PTR_ERR(pfdev->clock));
return PTR_ERR(pfdev->clock);
}
rate = clk_get_rate(pfdev->clock);
- dev_info(pfdev->dev, "clock rate = %lu\n", rate);
+ dev_info(pfdev->base.dev, "clock rate = %lu\n", rate);
err = clk_prepare_enable(pfdev->clock);
if (err)
return err;
- pfdev->bus_clock = devm_clk_get_optional(pfdev->dev, "bus");
+ pfdev->bus_clock = devm_clk_get_optional(pfdev->base.dev, "bus");
if (IS_ERR(pfdev->bus_clock)) {
- dev_err(pfdev->dev, "get bus_clock failed %ld\n",
+ dev_err(pfdev->base.dev, "get bus_clock failed %ld\n",
PTR_ERR(pfdev->bus_clock));
err = PTR_ERR(pfdev->bus_clock);
goto disable_clock;
@@ -62,7 +62,7 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
if (pfdev->bus_clock) {
rate = clk_get_rate(pfdev->bus_clock);
- dev_info(pfdev->dev, "bus_clock rate = %lu\n", rate);
+ dev_info(pfdev->base.dev, "bus_clock rate = %lu\n", rate);
err = clk_prepare_enable(pfdev->bus_clock);
if (err)
@@ -87,7 +87,7 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)
{
int ret, i;
- pfdev->regulators = devm_kcalloc(pfdev->dev, pfdev->comp->num_supplies,
+ pfdev->regulators = devm_kcalloc(pfdev->base.dev, pfdev->comp->num_supplies,
sizeof(*pfdev->regulators),
GFP_KERNEL);
if (!pfdev->regulators)
@@ -96,12 +96,12 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)
for (i = 0; i < pfdev->comp->num_supplies; i++)
pfdev->regulators[i].supply = pfdev->comp->supply_names[i];
- ret = devm_regulator_bulk_get(pfdev->dev,
+ ret = devm_regulator_bulk_get(pfdev->base.dev,
pfdev->comp->num_supplies,
pfdev->regulators);
if (ret < 0) {
if (ret != -EPROBE_DEFER)
- dev_err(pfdev->dev, "failed to get regulators: %d\n",
+ dev_err(pfdev->base.dev, "failed to get regulators: %d\n",
ret);
return ret;
}
@@ -109,7 +109,7 @@ static int panfrost_regulator_init(struct panfrost_device *pfdev)
ret = regulator_bulk_enable(pfdev->comp->num_supplies,
pfdev->regulators);
if (ret < 0) {
- dev_err(pfdev->dev, "failed to enable regulators: %d\n", ret);
+ dev_err(pfdev->base.dev, "failed to enable regulators: %d\n", ret);
return ret;
}
@@ -144,7 +144,7 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
int err;
int i, num_domains;
- num_domains = of_count_phandle_with_args(pfdev->dev->of_node,
+ num_domains = of_count_phandle_with_args(pfdev->base.dev->of_node,
"power-domains",
"#power-domain-cells");
@@ -156,7 +156,7 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
return 0;
if (num_domains != pfdev->comp->num_pm_domains) {
- dev_err(pfdev->dev,
+ dev_err(pfdev->base.dev,
"Incorrect number of power domains: %d provided, %d needed\n",
num_domains, pfdev->comp->num_pm_domains);
return -EINVAL;
@@ -168,20 +168,21 @@ static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
for (i = 0; i < num_domains; i++) {
pfdev->pm_domain_devs[i] =
- dev_pm_domain_attach_by_name(pfdev->dev,
- pfdev->comp->pm_domain_names[i]);
+ dev_pm_domain_attach_by_name(pfdev->base.dev,
+ pfdev->comp->pm_domain_names[i]);
if (IS_ERR_OR_NULL(pfdev->pm_domain_devs[i])) {
err = PTR_ERR(pfdev->pm_domain_devs[i]) ? : -ENODATA;
pfdev->pm_domain_devs[i] = NULL;
- dev_err(pfdev->dev,
+ dev_err(pfdev->base.dev,
"failed to get pm-domain %s(%d): %d\n",
pfdev->comp->pm_domain_names[i], i, err);
goto err;
}
- pfdev->pm_domain_links[i] = device_link_add(pfdev->dev,
- pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME |
- DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE);
+ pfdev->pm_domain_links[i] =
+ device_link_add(pfdev->base.dev,
+ pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE);
if (!pfdev->pm_domain_links[i]) {
dev_err(pfdev->pm_domain_devs[i],
"adding device link failed!\n");
@@ -220,20 +221,20 @@ int panfrost_device_init(struct panfrost_device *pfdev)
err = panfrost_reset_init(pfdev);
if (err) {
- dev_err(pfdev->dev, "reset init failed %d\n", err);
+ dev_err(pfdev->base.dev, "reset init failed %d\n", err);
goto out_pm_domain;
}
err = panfrost_clk_init(pfdev);
if (err) {
- dev_err(pfdev->dev, "clk init failed %d\n", err);
+ dev_err(pfdev->base.dev, "clk init failed %d\n", err);
goto out_reset;
}
err = panfrost_devfreq_init(pfdev);
if (err) {
if (err != -EPROBE_DEFER)
- dev_err(pfdev->dev, "devfreq init failed %d\n", err);
+ dev_err(pfdev->base.dev, "devfreq init failed %d\n", err);
goto out_clk;
}
@@ -244,7 +245,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
goto out_devfreq;
}
- pfdev->iomem = devm_platform_ioremap_resource(pfdev->pdev, 0);
+ pfdev->iomem = devm_platform_ioremap_resource(to_platform_device(pfdev->base.dev), 0);
if (IS_ERR(pfdev->iomem)) {
err = PTR_ERR(pfdev->iomem);
goto out_regulator;
@@ -258,7 +259,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
if (err)
goto out_gpu;
- err = panfrost_job_init(pfdev);
+ err = panfrost_jm_init(pfdev);
if (err)
goto out_mmu;
@@ -268,7 +269,7 @@ int panfrost_device_init(struct panfrost_device *pfdev)
return 0;
out_job:
- panfrost_job_fini(pfdev);
+ panfrost_jm_fini(pfdev);
out_mmu:
panfrost_mmu_fini(pfdev);
out_gpu:
@@ -289,7 +290,7 @@ out_pm_domain:
void panfrost_device_fini(struct panfrost_device *pfdev)
{
panfrost_perfcnt_fini(pfdev);
- panfrost_job_fini(pfdev);
+ panfrost_jm_fini(pfdev);
panfrost_mmu_fini(pfdev);
panfrost_gpu_fini(pfdev);
panfrost_devfreq_fini(pfdev);
@@ -399,13 +400,16 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev,
return false;
}
-void panfrost_device_reset(struct panfrost_device *pfdev)
+void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int)
{
panfrost_gpu_soft_reset(pfdev);
panfrost_gpu_power_on(pfdev);
panfrost_mmu_reset(pfdev);
- panfrost_job_enable_interrupts(pfdev);
+
+ panfrost_jm_reset_interrupts(pfdev);
+ if (enable_job_int)
+ panfrost_jm_enable_interrupts(pfdev);
}
static int panfrost_device_runtime_resume(struct device *dev)
@@ -429,7 +433,7 @@ static int panfrost_device_runtime_resume(struct device *dev)
}
}
- panfrost_device_reset(pfdev);
+ panfrost_device_reset(pfdev, true);
panfrost_devfreq_resume(pfdev);
return 0;
@@ -447,11 +451,11 @@ static int panfrost_device_runtime_suspend(struct device *dev)
{
struct panfrost_device *pfdev = dev_get_drvdata(dev);
- if (!panfrost_job_is_idle(pfdev))
+ if (!panfrost_jm_is_idle(pfdev))
return -EBUSY;
panfrost_devfreq_suspend(pfdev);
- panfrost_job_suspend_irq(pfdev);
+ panfrost_jm_suspend_irq(pfdev);
panfrost_mmu_suspend_irq(pfdev);
panfrost_gpu_suspend_irq(pfdev);
panfrost_gpu_power_off(pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 077525a3ad68..e61c4329fd07 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -10,11 +10,13 @@
#include <linux/pm.h>
#include <linux/regulator/consumer.h>
#include <linux/spinlock.h>
+#include <drm/drm_auth.h>
#include <drm/drm_device.h>
#include <drm/drm_mm.h>
#include <drm/gpu_scheduler.h>
#include "panfrost_devfreq.h"
+#include "panfrost_job.h"
struct panfrost_device;
struct panfrost_mmu;
@@ -22,9 +24,12 @@ struct panfrost_job_slot;
struct panfrost_job;
struct panfrost_perfcnt;
-#define NUM_JOB_SLOTS 3
#define MAX_PM_DOMAINS 5
+#define ALL_JS_INT_MASK \
+ (GENMASK(16 + NUM_JOB_SLOTS - 1, 16) | \
+ GENMASK(NUM_JOB_SLOTS - 1, 0))
+
enum panfrost_drv_comp_bits {
PANFROST_COMP_BIT_GPU,
PANFROST_COMP_BIT_JOB,
@@ -123,9 +128,7 @@ struct panfrost_device_debugfs {
};
struct panfrost_device {
- struct device *dev;
- struct drm_device *ddev;
- struct platform_device *pdev;
+ struct drm_device base;
int gpu_irq;
int mmu_irq;
@@ -144,7 +147,6 @@ struct panfrost_device {
DECLARE_BITMAP(is_suspended, PANFROST_COMP_BIT_MAX);
spinlock_t as_lock;
- unsigned long as_in_use_mask;
unsigned long as_alloc_mask;
unsigned long as_faulty_mask;
struct list_head as_lru_list;
@@ -206,16 +208,22 @@ struct panfrost_engine_usage {
struct panfrost_file_priv {
struct panfrost_device *pfdev;
- struct drm_sched_entity sched_entity[NUM_JOB_SLOTS];
+ struct xarray jm_ctxs;
struct panfrost_mmu *mmu;
struct panfrost_engine_usage engine_usage;
};
+static inline bool panfrost_high_prio_allowed(struct drm_file *file)
+{
+ /* Higher priorities require CAP_SYS_NICE or DRM_MASTER */
+ return (capable(CAP_SYS_NICE) || drm_is_current_master(file));
+}
+
static inline struct panfrost_device *to_panfrost_device(struct drm_device *ddev)
{
- return ddev->dev_private;
+ return container_of(ddev, struct panfrost_device, base);
}
static inline int panfrost_model_cmp(struct panfrost_device *pfdev, s32 id)
@@ -241,7 +249,7 @@ int panfrost_unstable_ioctl_check(void);
int panfrost_device_init(struct panfrost_device *pfdev);
void panfrost_device_fini(struct panfrost_device *pfdev);
-void panfrost_device_reset(struct panfrost_device *pfdev);
+void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int);
extern const struct dev_pm_ops panfrost_pm_ops;
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 1ea6c509a5d5..7d8c7c337606 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -16,6 +16,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_ioctl.h>
+#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include <drm/drm_utils.h>
@@ -36,7 +37,7 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
{
int ret;
- ret = pm_runtime_resume_and_get(pfdev->dev);
+ ret = pm_runtime_resume_and_get(pfdev->base.dev);
if (ret)
return ret;
@@ -44,14 +45,14 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev,
*arg = panfrost_timestamp_read(pfdev);
panfrost_cycle_counter_put(pfdev);
- pm_runtime_put(pfdev->dev);
+ pm_runtime_put(pfdev->base.dev);
return 0;
}
static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct drm_file *file)
{
struct drm_panfrost_get_param *param = data;
- struct panfrost_device *pfdev = ddev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(ddev);
int ret;
if (param->pad != 0)
@@ -109,6 +110,14 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct
#endif
break;
+ case DRM_PANFROST_PARAM_ALLOWED_JM_CTX_PRIORITIES:
+ param->value = BIT(PANFROST_JM_CTX_PRIORITY_LOW) |
+ BIT(PANFROST_JM_CTX_PRIORITY_MEDIUM);
+
+ if (panfrost_high_prio_allowed(file))
+ param->value |= BIT(PANFROST_JM_CTX_PRIORITY_HIGH);
+ break;
+
default:
return -EINVAL;
}
@@ -275,13 +284,17 @@ fail:
static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
struct drm_file *file)
{
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
struct panfrost_file_priv *file_priv = file->driver_priv;
struct drm_panfrost_submit *args = data;
struct drm_syncobj *sync_out = NULL;
+ struct panfrost_jm_ctx *jm_ctx;
struct panfrost_job *job;
int ret = 0, slot;
+ if (args->pad)
+ return -EINVAL;
+
if (!args->jc)
return -EINVAL;
@@ -294,10 +307,16 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
return -ENODEV;
}
+ jm_ctx = panfrost_jm_ctx_from_handle(file, args->jm_ctx_handle);
+ if (!jm_ctx) {
+ ret = -EINVAL;
+ goto out_put_syncout;
+ }
+
job = kzalloc(sizeof(*job), GFP_KERNEL);
if (!job) {
ret = -ENOMEM;
- goto out_put_syncout;
+ goto out_put_jm_ctx;
}
kref_init(&job->refcount);
@@ -307,12 +326,13 @@ static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
job->requirements = args->requirements;
job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev);
job->mmu = file_priv->mmu;
+ job->ctx = panfrost_jm_ctx_get(jm_ctx);
job->engine_usage = &file_priv->engine_usage;
slot = panfrost_job_get_slot(job);
ret = drm_sched_job_init(&job->base,
- &file_priv->sched_entity[slot],
+ &jm_ctx->slot_entity[slot],
1, NULL, file->client_id);
if (ret)
goto out_put_job;
@@ -338,6 +358,8 @@ out_cleanup_job:
drm_sched_job_cleanup(&job->base);
out_put_job:
panfrost_job_put(job);
+out_put_jm_ctx:
+ panfrost_jm_ctx_put(jm_ctx);
out_put_syncout:
if (sync_out)
drm_syncobj_put(sync_out);
@@ -436,7 +458,7 @@ static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
{
struct panfrost_file_priv *priv = file_priv->driver_priv;
struct drm_panfrost_madvise *args = data;
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
struct drm_gem_object *gem_obj;
struct panfrost_gem_object *bo;
int ret = 0;
@@ -536,6 +558,27 @@ err_put_obj:
return ret;
}
+static int panfrost_ioctl_jm_ctx_create(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ return panfrost_jm_ctx_create(file, data);
+}
+
+static int panfrost_ioctl_jm_ctx_destroy(struct drm_device *dev, void *data,
+ struct drm_file *file)
+{
+ const struct drm_panfrost_jm_ctx_destroy *args = data;
+
+ if (args->pad)
+ return -EINVAL;
+
+ /* We can't destroy the default context created when the file is opened. */
+ if (!args->handle)
+ return -EINVAL;
+
+ return panfrost_jm_ctx_destroy(file, args->handle);
+}
+
int panfrost_unstable_ioctl_check(void)
{
if (!unstable_ioctls)
@@ -548,7 +591,7 @@ static int
panfrost_open(struct drm_device *dev, struct drm_file *file)
{
int ret;
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
struct panfrost_file_priv *panfrost_priv;
panfrost_priv = kzalloc(sizeof(*panfrost_priv), GFP_KERNEL);
@@ -564,7 +607,7 @@ panfrost_open(struct drm_device *dev, struct drm_file *file)
goto err_free;
}
- ret = panfrost_job_open(panfrost_priv);
+ ret = panfrost_jm_open(file);
if (ret)
goto err_job;
@@ -583,7 +626,7 @@ panfrost_postclose(struct drm_device *dev, struct drm_file *file)
struct panfrost_file_priv *panfrost_priv = file->driver_priv;
panfrost_perfcnt_close(file);
- panfrost_job_close(panfrost_priv);
+ panfrost_jm_close(file);
panfrost_mmu_ctx_put(panfrost_priv->mmu);
kfree(panfrost_priv);
@@ -603,6 +646,8 @@ static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = {
PANFROST_IOCTL(PERFCNT_DUMP, perfcnt_dump, DRM_RENDER_ALLOW),
PANFROST_IOCTL(MADVISE, madvise, DRM_RENDER_ALLOW),
PANFROST_IOCTL(SET_LABEL_BO, set_label_bo, DRM_RENDER_ALLOW),
+ PANFROST_IOCTL(JM_CTX_CREATE, jm_ctx_create, DRM_RENDER_ALLOW),
+ PANFROST_IOCTL(JM_CTX_DESTROY, jm_ctx_destroy, DRM_RENDER_ALLOW),
};
static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev,
@@ -624,30 +669,25 @@ static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev,
* job spent on the GPU.
*/
- static const char * const engine_names[] = {
- "fragment", "vertex-tiler", "compute-only"
- };
-
- BUILD_BUG_ON(ARRAY_SIZE(engine_names) != NUM_JOB_SLOTS);
-
for (i = 0; i < NUM_JOB_SLOTS - 1; i++) {
if (pfdev->profile_mode) {
drm_printf(p, "drm-engine-%s:\t%llu ns\n",
- engine_names[i], panfrost_priv->engine_usage.elapsed_ns[i]);
+ panfrost_engine_names[i],
+ panfrost_priv->engine_usage.elapsed_ns[i]);
drm_printf(p, "drm-cycles-%s:\t%llu\n",
- engine_names[i], panfrost_priv->engine_usage.cycles[i]);
+ panfrost_engine_names[i],
+ panfrost_priv->engine_usage.cycles[i]);
}
drm_printf(p, "drm-maxfreq-%s:\t%lu Hz\n",
- engine_names[i], pfdev->pfdevfreq.fast_rate);
+ panfrost_engine_names[i], pfdev->pfdevfreq.fast_rate);
drm_printf(p, "drm-curfreq-%s:\t%lu Hz\n",
- engine_names[i], pfdev->pfdevfreq.current_frequency);
+ panfrost_engine_names[i], pfdev->pfdevfreq.current_frequency);
}
}
static void panfrost_show_fdinfo(struct drm_printer *p, struct drm_file *file)
{
- struct drm_device *dev = file->minor->dev;
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(file->minor->dev);
panfrost_gpu_show_fdinfo(pfdev, file->driver_priv, p);
@@ -664,16 +704,57 @@ static const struct file_operations panfrost_drm_driver_fops = {
static int panthor_gems_show(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev);
panfrost_gem_debugfs_print_bos(pfdev, m);
return 0;
}
+static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle,
+ struct seq_file *m)
+{
+ struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev;
+ const char *prio = "UNKNOWN";
+
+ static const char * const prios[] = {
+ [DRM_SCHED_PRIORITY_HIGH] = "HIGH",
+ [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL",
+ [DRM_SCHED_PRIORITY_LOW] = "LOW",
+ };
+
+ if (jm_ctx->slot_entity[0].priority !=
+ jm_ctx->slot_entity[1].priority)
+ drm_warn(ddev, "Slot priorities should be the same in a single context");
+
+ if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios))
+ prio = prios[jm_ctx->slot_entity[0].priority];
+
+ seq_printf(m, " JM context %u: priority %s\n", handle, prio);
+}
+
+static int show_file_jm_ctxs(struct panfrost_file_priv *pfile,
+ struct seq_file *m)
+{
+ struct panfrost_jm_ctx *jm_ctx;
+ unsigned long i;
+
+ xa_lock(&pfile->jm_ctxs);
+ xa_for_each(&pfile->jm_ctxs, i, jm_ctx) {
+ jm_ctx = panfrost_jm_ctx_get(jm_ctx);
+ xa_unlock(&pfile->jm_ctxs);
+ show_panfrost_jm_ctx(jm_ctx, i, m);
+ panfrost_jm_ctx_put(jm_ctx);
+ xa_lock(&pfile->jm_ctxs);
+ }
+ xa_unlock(&pfile->jm_ctxs);
+
+ return 0;
+}
+
static struct drm_info_list panthor_debugfs_list[] = {
- {"gems", panthor_gems_show, 0, NULL},
+ {"gems",
+ panthor_gems_show, 0, NULL},
};
static int panthor_gems_debugfs_init(struct drm_minor *minor)
@@ -685,9 +766,64 @@ static int panthor_gems_debugfs_init(struct drm_minor *minor)
return 0;
}
+static int show_each_file(struct seq_file *m, void *arg)
+{
+ struct drm_info_node *node = (struct drm_info_node *)m->private;
+ struct drm_device *ddev = node->minor->dev;
+ int (*show)(struct panfrost_file_priv *, struct seq_file *) =
+ node->info_ent->data;
+ struct drm_file *file;
+ int ret;
+
+ ret = mutex_lock_interruptible(&ddev->filelist_mutex);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(file, &ddev->filelist, lhead) {
+ struct task_struct *task;
+ struct panfrost_file_priv *pfile = file->driver_priv;
+ struct pid *pid;
+
+ /*
+ * Although we have a valid reference on file->pid, that does
+ * not guarantee that the task_struct who called get_pid() is
+ * still alive (e.g. get_pid(current) => fork() => exit()).
+ * Therefore, we need to protect this ->comm access using RCU.
+ */
+ rcu_read_lock();
+ pid = rcu_dereference(file->pid);
+ task = pid_task(pid, PIDTYPE_TGID);
+ seq_printf(m, "client_id %8llu pid %8d command %s:\n",
+ file->client_id, pid_nr(pid),
+ task ? task->comm : "<unknown>");
+ rcu_read_unlock();
+
+ ret = show(pfile, m);
+ if (ret < 0)
+ break;
+
+ seq_puts(m, "\n");
+ }
+
+ mutex_unlock(&ddev->filelist_mutex);
+ return ret;
+}
+
+static struct drm_info_list panfrost_sched_debugfs_list[] = {
+ { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs },
+};
+
+static void panfrost_sched_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panfrost_sched_debugfs_list,
+ ARRAY_SIZE(panfrost_sched_debugfs_list),
+ minor->debugfs_root, minor);
+}
+
static void panfrost_debugfs_init(struct drm_minor *minor)
{
panthor_gems_debugfs_init(minor);
+ panfrost_sched_debugfs_init(minor);
}
#endif
@@ -699,6 +835,8 @@ static void panfrost_debugfs_init(struct drm_minor *minor)
* - 1.3 - adds JD_REQ_CYCLE_COUNT job requirement for SUBMIT
* - adds SYSTEM_TIMESTAMP and SYSTEM_TIMESTAMP_FREQUENCY queries
* - 1.4 - adds SET_LABEL_BO
+ * - 1.5 - adds JM_CTX_{CREATE,DESTROY} ioctls and extend SUBMIT to allow
+ * context creation with configurable priorities/affinity
*/
static const struct drm_driver panfrost_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
@@ -711,7 +849,7 @@ static const struct drm_driver panfrost_drm_driver = {
.name = "panfrost",
.desc = "panfrost DRM",
.major = 1,
- .minor = 4,
+ .minor = 5,
.gem_create_object = panfrost_gem_create_object,
.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,
@@ -723,15 +861,12 @@ static const struct drm_driver panfrost_drm_driver = {
static int panfrost_probe(struct platform_device *pdev)
{
struct panfrost_device *pfdev;
- struct drm_device *ddev;
int err;
- pfdev = devm_kzalloc(&pdev->dev, sizeof(*pfdev), GFP_KERNEL);
- if (!pfdev)
- return -ENOMEM;
-
- pfdev->pdev = pdev;
- pfdev->dev = &pdev->dev;
+ pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver,
+ struct panfrost_device, base);
+ if (IS_ERR(pfdev))
+ return PTR_ERR(pfdev);
platform_set_drvdata(pdev, pfdev);
@@ -741,14 +876,6 @@ static int panfrost_probe(struct platform_device *pdev)
pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
- /* Allocate and initialize the DRM device. */
- ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev);
- if (IS_ERR(ddev))
- return PTR_ERR(ddev);
-
- ddev->dev_private = pfdev;
- pfdev->ddev = ddev;
-
mutex_init(&pfdev->shrinker_lock);
INIT_LIST_HEAD(&pfdev->shrinker_list);
@@ -759,51 +886,47 @@ static int panfrost_probe(struct platform_device *pdev)
goto err_out0;
}
- pm_runtime_set_active(pfdev->dev);
- pm_runtime_mark_last_busy(pfdev->dev);
- pm_runtime_enable(pfdev->dev);
- pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */
- pm_runtime_use_autosuspend(pfdev->dev);
+ pm_runtime_set_active(pfdev->base.dev);
+ pm_runtime_mark_last_busy(pfdev->base.dev);
+ pm_runtime_enable(pfdev->base.dev);
+ pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */
+ pm_runtime_use_autosuspend(pfdev->base.dev);
/*
* Register the DRM device with the core and the connectors with
* sysfs
*/
- err = drm_dev_register(ddev, 0);
+ err = drm_dev_register(&pfdev->base, 0);
if (err < 0)
goto err_out1;
- err = panfrost_gem_shrinker_init(ddev);
+ err = panfrost_gem_shrinker_init(&pfdev->base);
if (err)
goto err_out2;
return 0;
err_out2:
- drm_dev_unregister(ddev);
+ drm_dev_unregister(&pfdev->base);
err_out1:
- pm_runtime_disable(pfdev->dev);
+ pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->dev);
+ pm_runtime_set_suspended(pfdev->base.dev);
err_out0:
- drm_dev_put(ddev);
return err;
}
static void panfrost_remove(struct platform_device *pdev)
{
struct panfrost_device *pfdev = platform_get_drvdata(pdev);
- struct drm_device *ddev = pfdev->ddev;
- drm_dev_unregister(ddev);
- panfrost_gem_shrinker_cleanup(ddev);
+ drm_dev_unregister(&pfdev->base);
+ panfrost_gem_shrinker_cleanup(&pfdev->base);
- pm_runtime_get_sync(pfdev->dev);
- pm_runtime_disable(pfdev->dev);
+ pm_runtime_get_sync(pfdev->base.dev);
+ pm_runtime_disable(pfdev->base.dev);
panfrost_device_fini(pfdev);
- pm_runtime_set_suspended(pfdev->dev);
-
- drm_dev_put(ddev);
+ pm_runtime_set_suspended(pfdev->base.dev);
}
static ssize_t profiling_show(struct device *dev,
diff --git a/drivers/gpu/drm/panfrost/panfrost_dump.c b/drivers/gpu/drm/panfrost/panfrost_dump.c
index 4042afe2fbf4..3ed6c902d0a1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_dump.c
+++ b/drivers/gpu/drm/panfrost/panfrost_dump.c
@@ -163,7 +163,7 @@ void panfrost_core_dump(struct panfrost_job *job)
iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN |
__GFP_NORETRY);
if (!iter.start) {
- dev_warn(pfdev->dev, "failed to allocate devcoredump file\n");
+ dev_warn(pfdev->base.dev, "failed to allocate devcoredump file\n");
return;
}
@@ -204,14 +204,14 @@ void panfrost_core_dump(struct panfrost_job *job)
mapping = job->mappings[i];
if (!bo->base.sgt) {
- dev_err(pfdev->dev, "Panfrost Dump: BO has no sgt, cannot dump\n");
+ dev_err(pfdev->base.dev, "Panfrost Dump: BO has no sgt, cannot dump\n");
iter.hdr->bomap.valid = 0;
goto dump_header;
}
ret = drm_gem_vmap(&bo->base.base, &map);
if (ret) {
- dev_err(pfdev->dev, "Panfrost Dump: couldn't map Buffer Object\n");
+ dev_err(pfdev->base.dev, "Panfrost Dump: couldn't map Buffer Object\n");
iter.hdr->bomap.valid = 0;
goto dump_header;
}
@@ -237,5 +237,5 @@ dump_header: panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_BO, iter.data +
}
panfrost_core_dump_header(&iter, PANFROSTDUMP_BUF_TRAILER, iter.data);
- dev_coredumpv(pfdev->dev, iter.start, iter.data - iter.start, GFP_KERNEL);
+ dev_coredumpv(pfdev->base.dev, iter.start, iter.data - iter.start, GFP_KERNEL);
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 85d6289a6eda..8041b65c6609 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -8,6 +8,7 @@
#include <linux/dma-mapping.h>
#include <drm/panfrost_drm.h>
+#include <drm/drm_print.h>
#include "panfrost_device.h"
#include "panfrost_gem.h"
#include "panfrost_mmu.h"
@@ -26,7 +27,7 @@ static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev,
static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo)
{
- struct panfrost_device *pfdev = bo->base.base.dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(bo->base.base.dev);
if (list_empty(&bo->debugfs.node))
return;
@@ -48,7 +49,7 @@ static void panfrost_gem_debugfs_bo_rm(struct panfrost_gem_object *bo) {}
static void panfrost_gem_free_object(struct drm_gem_object *obj)
{
struct panfrost_gem_object *bo = to_panfrost_bo(obj);
- struct panfrost_device *pfdev = obj->dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
/*
* Make sure the BO is no longer inserted in the shrinker list before
@@ -76,7 +77,7 @@ static void panfrost_gem_free_object(struct drm_gem_object *obj)
for (i = 0; i < n_sgt; i++) {
if (bo->sgts[i].sgl) {
- dma_unmap_sgtable(pfdev->dev, &bo->sgts[i],
+ dma_unmap_sgtable(pfdev->base.dev, &bo->sgts[i],
DMA_BIDIRECTIONAL, 0);
sg_free_table(&bo->sgts[i]);
}
@@ -284,7 +285,7 @@ static const struct drm_gem_object_funcs panfrost_gem_funcs = {
*/
struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size)
{
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
struct panfrost_gem_object *obj;
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
index 02b60ea1433a..2fe967a90bcb 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c
@@ -97,7 +97,7 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
*/
int panfrost_gem_shrinker_init(struct drm_device *dev)
{
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
pfdev->shrinker = shrinker_alloc(0, "drm-panfrost");
if (!pfdev->shrinker)
@@ -120,7 +120,7 @@ int panfrost_gem_shrinker_init(struct drm_device *dev)
*/
void panfrost_gem_shrinker_cleanup(struct drm_device *dev)
{
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
if (pfdev->shrinker)
shrinker_free(pfdev->shrinker);
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 174e190ba40f..483d278eb154 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -12,6 +12,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <drm/drm_print.h>
+
#include "panfrost_device.h"
#include "panfrost_features.h"
#include "panfrost_issues.h"
@@ -36,12 +38,12 @@ static irqreturn_t panfrost_gpu_irq_handler(int irq, void *data)
u64 address = (u64) gpu_read(pfdev, GPU_FAULT_ADDRESS_HI) << 32;
address |= gpu_read(pfdev, GPU_FAULT_ADDRESS_LO);
- dev_warn(pfdev->dev, "GPU Fault 0x%08x (%s) at 0x%016llx\n",
+ dev_warn(pfdev->base.dev, "GPU Fault 0x%08x (%s) at 0x%016llx\n",
fault_status, panfrost_exception_name(fault_status & 0xFF),
address);
if (state & GPU_IRQ_MULTIPLE_FAULT)
- dev_warn(pfdev->dev, "There were multiple GPU faults - some have not been reported\n");
+ dev_warn(pfdev->base.dev, "There were multiple GPU faults - some have not been reported\n");
gpu_write(pfdev, GPU_INT_MASK, 0);
}
@@ -72,13 +74,13 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
val, val & GPU_IRQ_RESET_COMPLETED, 10, 10000);
if (ret) {
- dev_err(pfdev->dev, "gpu soft reset timed out, attempting hard reset\n");
+ dev_err(pfdev->base.dev, "gpu soft reset timed out, attempting hard reset\n");
gpu_write(pfdev, GPU_CMD, GPU_CMD_HARD_RESET);
ret = readl_relaxed_poll_timeout(pfdev->iomem + GPU_INT_RAWSTAT, val,
val & GPU_IRQ_RESET_COMPLETED, 100, 10000);
if (ret) {
- dev_err(pfdev->dev, "gpu hard reset timed out\n");
+ dev_err(pfdev->base.dev, "gpu hard reset timed out\n");
return ret;
}
}
@@ -95,7 +97,7 @@ int panfrost_gpu_soft_reset(struct panfrost_device *pfdev)
* All in-flight jobs should have released their cycle
* counter references upon reset, but let us make sure
*/
- if (drm_WARN_ON(pfdev->ddev, atomic_read(&pfdev->cycle_counter.use_count) != 0))
+ if (drm_WARN_ON(&pfdev->base, atomic_read(&pfdev->cycle_counter.use_count) != 0))
atomic_set(&pfdev->cycle_counter.use_count, 0);
return 0;
@@ -240,9 +242,10 @@ static const struct panfrost_model gpu_models[] = {
/* MediaTek MT8188 Mali-G57 MC3 */
GPU_MODEL(g57, 0x9093,
GPU_REV(g57, 0, 0)),
+ {0},
};
-static void panfrost_gpu_init_features(struct panfrost_device *pfdev)
+static int panfrost_gpu_init_features(struct panfrost_device *pfdev)
{
u32 gpu_id, num_js, major, minor, status, rev;
const char *name = "unknown";
@@ -327,16 +330,22 @@ static void panfrost_gpu_init_features(struct panfrost_device *pfdev)
break;
}
+ if (!model->name) {
+ dev_err(pfdev->base.dev, "GPU model not found: mali-%s id rev %#x %#x\n",
+ name, gpu_id, rev);
+ return -ENODEV;
+ }
+
bitmap_from_u64(pfdev->features.hw_features, hw_feat);
bitmap_from_u64(pfdev->features.hw_issues, hw_issues);
- dev_info(pfdev->dev, "mali-%s id 0x%x major 0x%x minor 0x%x status 0x%x",
+ dev_info(pfdev->base.dev, "mali-%s id 0x%x major 0x%x minor 0x%x status 0x%x",
name, gpu_id, major, minor, status);
- dev_info(pfdev->dev, "features: %64pb, issues: %64pb",
+ dev_info(pfdev->base.dev, "features: %64pb, issues: %64pb",
pfdev->features.hw_features,
pfdev->features.hw_issues);
- dev_info(pfdev->dev, "Features: L2:0x%08x Shader:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x JS:0x%x",
+ dev_info(pfdev->base.dev, "Features: L2:0x%08x Shader:0x%08x Tiler:0x%08x Mem:0x%0x MMU:0x%08x AS:0x%x JS:0x%x",
pfdev->features.l2_features,
pfdev->features.core_features,
pfdev->features.tiler_features,
@@ -345,8 +354,10 @@ static void panfrost_gpu_init_features(struct panfrost_device *pfdev)
pfdev->features.as_present,
pfdev->features.js_present);
- dev_info(pfdev->dev, "shader_present=0x%0llx l2_present=0x%0llx",
+ dev_info(pfdev->base.dev, "shader_present=0x%0llx l2_present=0x%0llx",
pfdev->features.shader_present, pfdev->features.l2_present);
+
+ return 0;
}
void panfrost_cycle_counter_get(struct panfrost_device *pfdev)
@@ -411,7 +422,7 @@ static u64 panfrost_get_core_mask(struct panfrost_device *pfdev)
*/
core_mask = ~(pfdev->features.l2_present - 1) &
(pfdev->features.l2_present - 2);
- dev_info_once(pfdev->dev, "using only 1st core group (%lu cores from %lu)\n",
+ dev_info_once(pfdev->base.dev, "using only 1st core group (%lu cores from %lu)\n",
hweight64(core_mask),
hweight64(pfdev->features.shader_present));
@@ -432,7 +443,7 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev)
val, val == (pfdev->features.l2_present & core_mask),
10, 20000);
if (ret)
- dev_err(pfdev->dev, "error powering up gpu L2");
+ dev_err(pfdev->base.dev, "error powering up gpu L2");
gpu_write(pfdev, SHADER_PWRON_LO,
pfdev->features.shader_present & core_mask);
@@ -440,13 +451,13 @@ void panfrost_gpu_power_on(struct panfrost_device *pfdev)
val, val == (pfdev->features.shader_present & core_mask),
10, 20000);
if (ret)
- dev_err(pfdev->dev, "error powering up gpu shader");
+ dev_err(pfdev->base.dev, "error powering up gpu shader");
gpu_write(pfdev, TILER_PWRON_LO, pfdev->features.tiler_present);
ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_READY_LO,
val, val == pfdev->features.tiler_present, 10, 1000);
if (ret)
- dev_err(pfdev->dev, "error powering up gpu tiler");
+ dev_err(pfdev->base.dev, "error powering up gpu tiler");
}
void panfrost_gpu_power_off(struct panfrost_device *pfdev)
@@ -458,19 +469,19 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
ret = readl_relaxed_poll_timeout(pfdev->iomem + SHADER_PWRTRANS_LO,
val, !val, 1, 2000);
if (ret)
- dev_err(pfdev->dev, "shader power transition timeout");
+ dev_err(pfdev->base.dev, "shader power transition timeout");
gpu_write(pfdev, TILER_PWROFF_LO, pfdev->features.tiler_present);
ret = readl_relaxed_poll_timeout(pfdev->iomem + TILER_PWRTRANS_LO,
val, !val, 1, 2000);
if (ret)
- dev_err(pfdev->dev, "tiler power transition timeout");
+ dev_err(pfdev->base.dev, "tiler power transition timeout");
gpu_write(pfdev, L2_PWROFF_LO, pfdev->features.l2_present);
ret = readl_poll_timeout(pfdev->iomem + L2_PWRTRANS_LO,
val, !val, 0, 2000);
if (ret)
- dev_err(pfdev->dev, "l2 power transition timeout");
+ dev_err(pfdev->base.dev, "l2 power transition timeout");
}
void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev)
@@ -489,23 +500,26 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
if (err)
return err;
- panfrost_gpu_init_features(pfdev);
+ err = panfrost_gpu_init_features(pfdev);
+ if (err)
+ return err;
- err = dma_set_mask_and_coherent(pfdev->dev,
- DMA_BIT_MASK(FIELD_GET(0xff00, pfdev->features.mmu_features)));
+ err = dma_set_mask_and_coherent(pfdev->base.dev,
+ DMA_BIT_MASK(FIELD_GET(0xff00,
+ pfdev->features.mmu_features)));
if (err)
return err;
- dma_set_max_seg_size(pfdev->dev, UINT_MAX);
+ dma_set_max_seg_size(pfdev->base.dev, UINT_MAX);
- pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
+ pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "gpu");
if (pfdev->gpu_irq < 0)
return pfdev->gpu_irq;
- err = devm_request_irq(pfdev->dev, pfdev->gpu_irq, panfrost_gpu_irq_handler,
+ err = devm_request_irq(pfdev->base.dev, pfdev->gpu_irq, panfrost_gpu_irq_handler,
IRQF_SHARED, KBUILD_MODNAME "-gpu", pfdev);
if (err) {
- dev_err(pfdev->dev, "failed to request gpu irq");
+ dev_err(pfdev->base.dev, "failed to request gpu irq");
return err;
}
@@ -525,9 +539,9 @@ u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev)
if (panfrost_has_hw_feature(pfdev, HW_FEATURE_FLUSH_REDUCTION)) {
/* Flush reduction only makes sense when the GPU is kept powered on between jobs */
- if (pm_runtime_get_if_in_use(pfdev->dev)) {
+ if (pm_runtime_get_if_in_use(pfdev->base.dev)) {
flush_id = gpu_read(pfdev, GPU_LATEST_FLUSH_ID);
- pm_runtime_put(pfdev->dev);
+ pm_runtime_put(pfdev->base.dev);
return flush_id;
}
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index 82acabb21b27..11894a6b9fcc 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -22,11 +22,16 @@
#include "panfrost_mmu.h"
#include "panfrost_dump.h"
+#define MAX_JM_CTX_PER_FILE 64
#define JOB_TIMEOUT_MS 500
#define job_write(dev, reg, data) writel(data, dev->iomem + (reg))
#define job_read(dev, reg) readl(dev->iomem + (reg))
+const char * const panfrost_engine_names[] = {
+ "fragment", "vertex-tiler", "compute-only"
+};
+
struct panfrost_queue_state {
struct drm_gpu_scheduler sched;
u64 fence_context;
@@ -94,7 +99,7 @@ static struct dma_fence *panfrost_fence_create(struct panfrost_device *pfdev, in
if (!fence)
return ERR_PTR(-ENOMEM);
- fence->dev = pfdev->ddev;
+ fence->dev = &pfdev->base;
fence->queue = js_num;
fence->seqno = ++js->queue[js_num].emit_seqno;
dma_fence_init(&fence->base, &panfrost_fence_ops, &js->job_lock,
@@ -195,7 +200,7 @@ panfrost_enqueue_job(struct panfrost_device *pfdev, int slot,
return 1;
}
-static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
+static int panfrost_job_hw_submit(struct panfrost_job *job, int js)
{
struct panfrost_device *pfdev = job->pfdev;
unsigned int subslot;
@@ -203,17 +208,22 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
u64 jc_head = job->jc;
int ret;
- panfrost_devfreq_record_busy(&pfdev->pfdevfreq);
-
- ret = pm_runtime_get_sync(pfdev->dev);
+ ret = pm_runtime_get_sync(pfdev->base.dev);
if (ret < 0)
- return;
+ goto err_hwsubmit;
if (WARN_ON(job_read(pfdev, JS_COMMAND_NEXT(js)))) {
- return;
+ ret = -EINVAL;
+ goto err_hwsubmit;
}
- cfg = panfrost_mmu_as_get(pfdev, job->mmu);
+ ret = panfrost_mmu_as_get(pfdev, job->mmu);
+ if (ret < 0)
+ goto err_hwsubmit;
+
+ cfg = ret;
+
+ panfrost_devfreq_record_busy(&pfdev->pfdevfreq);
job_write(pfdev, JS_HEAD_NEXT_LO(js), lower_32_bits(jc_head));
job_write(pfdev, JS_HEAD_NEXT_HI(js), upper_32_bits(jc_head));
@@ -256,11 +266,17 @@ static void panfrost_job_hw_submit(struct panfrost_job *job, int js)
}
job_write(pfdev, JS_COMMAND_NEXT(js), JS_COMMAND_START);
- dev_dbg(pfdev->dev,
+ dev_dbg(pfdev->base.dev,
"JS: Submitting atom %p to js[%d][%d] with head=0x%llx AS %d",
job, js, subslot, jc_head, cfg & 0xf);
}
spin_unlock(&pfdev->js->job_lock);
+
+ return 0;
+
+err_hwsubmit:
+ pm_runtime_put_autosuspend(pfdev->base.dev);
+ return ret;
}
static int panfrost_acquire_object_fences(struct drm_gem_object **bos,
@@ -359,6 +375,7 @@ static void panfrost_job_cleanup(struct kref *ref)
kvfree(job->bos);
}
+ panfrost_jm_ctx_put(job->ctx);
kfree(job);
}
@@ -382,6 +399,10 @@ static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job)
struct panfrost_device *pfdev = job->pfdev;
int slot = panfrost_job_get_slot(job);
struct dma_fence *fence = NULL;
+ int ret;
+
+ if (job->ctx->destroyed)
+ return ERR_PTR(-ECANCELED);
if (unlikely(job->base.s_fence->finished.error))
return NULL;
@@ -400,27 +421,27 @@ static struct dma_fence *panfrost_job_run(struct drm_sched_job *sched_job)
dma_fence_put(job->done_fence);
job->done_fence = dma_fence_get(fence);
- panfrost_job_hw_submit(job, slot);
+ ret = panfrost_job_hw_submit(job, slot);
+ if (ret) {
+ dma_fence_put(fence);
+ return ERR_PTR(ret);
+ }
return fence;
}
-void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
+void panfrost_jm_reset_interrupts(struct panfrost_device *pfdev)
{
- int j;
- u32 irq_mask = 0;
+ job_write(pfdev, JOB_INT_CLEAR, ALL_JS_INT_MASK);
+}
+void panfrost_jm_enable_interrupts(struct panfrost_device *pfdev)
+{
clear_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
-
- for (j = 0; j < NUM_JOB_SLOTS; j++) {
- irq_mask |= MK_JS_MASK(j);
- }
-
- job_write(pfdev, JOB_INT_CLEAR, irq_mask);
- job_write(pfdev, JOB_INT_MASK, irq_mask);
+ job_write(pfdev, JOB_INT_MASK, ALL_JS_INT_MASK);
}
-void panfrost_job_suspend_irq(struct panfrost_device *pfdev)
+void panfrost_jm_suspend_irq(struct panfrost_device *pfdev)
{
set_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
@@ -437,12 +458,12 @@ static void panfrost_job_handle_err(struct panfrost_device *pfdev,
bool signal_fence = true;
if (!panfrost_exception_is_fault(js_status)) {
- dev_dbg(pfdev->dev, "js event, js=%d, status=%s, head=0x%x, tail=0x%x",
+ dev_dbg(pfdev->base.dev, "js event, js=%d, status=%s, head=0x%x, tail=0x%x",
js, exception_name,
job_read(pfdev, JS_HEAD_LO(js)),
job_read(pfdev, JS_TAIL_LO(js)));
} else {
- dev_err(pfdev->dev, "js fault, js=%d, status=%s, head=0x%x, tail=0x%x",
+ dev_err(pfdev->base.dev, "js fault, js=%d, status=%s, head=0x%x, tail=0x%x",
js, exception_name,
job_read(pfdev, JS_HEAD_LO(js)),
job_read(pfdev, JS_TAIL_LO(js)));
@@ -474,7 +495,7 @@ static void panfrost_job_handle_err(struct panfrost_device *pfdev,
if (signal_fence)
dma_fence_signal_locked(job->done_fence);
- pm_runtime_put_autosuspend(pfdev->dev);
+ pm_runtime_put_autosuspend(pfdev->base.dev);
if (panfrost_exception_needs_reset(pfdev, js_status)) {
atomic_set(&pfdev->reset.pending, 1);
@@ -482,8 +503,8 @@ static void panfrost_job_handle_err(struct panfrost_device *pfdev,
}
}
-static void panfrost_job_handle_done(struct panfrost_device *pfdev,
- struct panfrost_job *job)
+static void panfrost_jm_handle_done(struct panfrost_device *pfdev,
+ struct panfrost_job *job)
{
/* Set ->jc to 0 to avoid re-submitting an already finished job (can
* happen when we receive the DONE interrupt while doing a GPU reset).
@@ -493,10 +514,10 @@ static void panfrost_job_handle_done(struct panfrost_device *pfdev,
panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
dma_fence_signal_locked(job->done_fence);
- pm_runtime_put_autosuspend(pfdev->dev);
+ pm_runtime_put_autosuspend(pfdev->base.dev);
}
-static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
+static void panfrost_jm_handle_irq(struct panfrost_device *pfdev, u32 status)
{
struct panfrost_job *done[NUM_JOB_SLOTS][2] = {};
struct panfrost_job *failed[NUM_JOB_SLOTS] = {};
@@ -571,7 +592,7 @@ static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
}
for (i = 0; i < ARRAY_SIZE(done[0]) && done[j][i]; i++)
- panfrost_job_handle_done(pfdev, done[j][i]);
+ panfrost_jm_handle_done(pfdev, done[j][i]);
}
/* And finally we requeue jobs that were waiting in the second slot
@@ -589,7 +610,7 @@ static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
struct panfrost_job *canceled = panfrost_dequeue_job(pfdev, j);
dma_fence_set_error(canceled->done_fence, -ECANCELED);
- panfrost_job_handle_done(pfdev, canceled);
+ panfrost_jm_handle_done(pfdev, canceled);
} else if (!atomic_read(&pfdev->reset.pending)) {
/* Requeue the job we removed if no reset is pending */
job_write(pfdev, JS_COMMAND_NEXT(j), JS_COMMAND_START);
@@ -597,15 +618,15 @@ static void panfrost_job_handle_irq(struct panfrost_device *pfdev, u32 status)
}
}
-static void panfrost_job_handle_irqs(struct panfrost_device *pfdev)
+static void panfrost_jm_handle_irqs(struct panfrost_device *pfdev)
{
u32 status = job_read(pfdev, JOB_INT_RAWSTAT);
while (status) {
- pm_runtime_mark_last_busy(pfdev->dev);
+ pm_runtime_mark_last_busy(pfdev->base.dev);
spin_lock(&pfdev->js->job_lock);
- panfrost_job_handle_irq(pfdev, status);
+ panfrost_jm_handle_irq(pfdev, status);
spin_unlock(&pfdev->js->job_lock);
status = job_read(pfdev, JOB_INT_RAWSTAT);
}
@@ -683,10 +704,10 @@ panfrost_reset(struct panfrost_device *pfdev,
10, 10000);
if (ret)
- dev_err(pfdev->dev, "Soft-stop failed\n");
+ dev_err(pfdev->base.dev, "Soft-stop failed\n");
/* Handle the remaining interrupts before we reset. */
- panfrost_job_handle_irqs(pfdev);
+ panfrost_jm_handle_irqs(pfdev);
/* Remaining interrupts have been handled, but we might still have
* stuck jobs. Let's make sure the PM counters stay balanced by
@@ -701,7 +722,7 @@ panfrost_reset(struct panfrost_device *pfdev,
if (pfdev->jobs[i][j]->requirements & PANFROST_JD_REQ_CYCLE_COUNT ||
pfdev->jobs[i][j]->is_profiled)
panfrost_cycle_counter_put(pfdev->jobs[i][j]->pfdev);
- pm_runtime_put_noidle(pfdev->dev);
+ pm_runtime_put_noidle(pfdev->base.dev);
panfrost_devfreq_record_idle(&pfdev->pfdevfreq);
}
}
@@ -709,12 +730,7 @@ panfrost_reset(struct panfrost_device *pfdev,
spin_unlock(&pfdev->js->job_lock);
/* Proceed with reset now. */
- panfrost_device_reset(pfdev);
-
- /* panfrost_device_reset() unmasks job interrupts, but we want to
- * keep them masked a bit longer.
- */
- job_write(pfdev, JOB_INT_MASK, 0);
+ panfrost_device_reset(pfdev, false);
/* GPU has been reset, we can clear the reset pending bit. */
atomic_set(&pfdev->reset.pending, 0);
@@ -736,9 +752,7 @@ panfrost_reset(struct panfrost_device *pfdev,
drm_sched_start(&pfdev->js->queue[i].sched, 0);
/* Re-enable job interrupts now that everything has been restarted. */
- job_write(pfdev, JOB_INT_MASK,
- GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
- GENMASK(NUM_JOB_SLOTS - 1, 0));
+ panfrost_jm_enable_interrupts(pfdev);
dma_fence_end_signalling(cookie);
}
@@ -769,11 +783,11 @@ static enum drm_gpu_sched_stat panfrost_job_timedout(struct drm_sched_job
synchronize_irq(pfdev->js->irq);
if (dma_fence_is_signaled(job->done_fence)) {
- dev_warn(pfdev->dev, "unexpectedly high interrupt latency\n");
+ dev_warn(pfdev->base.dev, "unexpectedly high interrupt latency\n");
return DRM_GPU_SCHED_STAT_NO_HANG;
}
- dev_err(pfdev->dev, "gpu sched timeout, js=%d, config=0x%x, status=0x%x, head=0x%x, tail=0x%x, sched_job=%p",
+ dev_err(pfdev->base.dev, "gpu sched timeout, js=%d, config=0x%x, status=0x%x, head=0x%x, tail=0x%x, sched_job=%p",
js,
job_read(pfdev, JS_CONFIG(js)),
job_read(pfdev, JS_STATUS(js)),
@@ -803,22 +817,20 @@ static const struct drm_sched_backend_ops panfrost_sched_ops = {
.free_job = panfrost_job_free
};
-static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
+static irqreturn_t panfrost_jm_irq_handler_thread(int irq, void *data)
{
struct panfrost_device *pfdev = data;
- panfrost_job_handle_irqs(pfdev);
+ panfrost_jm_handle_irqs(pfdev);
/* Enable interrupts only if we're not about to get suspended */
if (!test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended))
- job_write(pfdev, JOB_INT_MASK,
- GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
- GENMASK(NUM_JOB_SLOTS - 1, 0));
+ job_write(pfdev, JOB_INT_MASK, ALL_JS_INT_MASK);
return IRQ_HANDLED;
}
-static irqreturn_t panfrost_job_irq_handler(int irq, void *data)
+static irqreturn_t panfrost_jm_irq_handler(int irq, void *data)
{
struct panfrost_device *pfdev = data;
u32 status;
@@ -834,19 +846,20 @@ static irqreturn_t panfrost_job_irq_handler(int irq, void *data)
return IRQ_WAKE_THREAD;
}
-int panfrost_job_init(struct panfrost_device *pfdev)
+int panfrost_jm_init(struct panfrost_device *pfdev)
{
struct drm_sched_init_args args = {
.ops = &panfrost_sched_ops,
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 2,
.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
- .name = "pan_js",
- .dev = pfdev->dev,
+ .dev = pfdev->base.dev,
};
struct panfrost_job_slot *js;
int ret, j;
+ BUILD_BUG_ON(ARRAY_SIZE(panfrost_engine_names) != NUM_JOB_SLOTS);
+
/* All GPUs have two entries per queue, but without jobchain
* disambiguation stopping the right job in the close path is tricky,
* so let's just advertise one entry in that case.
@@ -854,24 +867,25 @@ int panfrost_job_init(struct panfrost_device *pfdev)
if (!panfrost_has_hw_feature(pfdev, HW_FEATURE_JOBCHAIN_DISAMBIGUATION))
args.credit_limit = 1;
- pfdev->js = js = devm_kzalloc(pfdev->dev, sizeof(*js), GFP_KERNEL);
+ js = devm_kzalloc(pfdev->base.dev, sizeof(*js), GFP_KERNEL);
if (!js)
return -ENOMEM;
+ pfdev->js = js;
INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
spin_lock_init(&js->job_lock);
- js->irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "job");
+ js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job");
if (js->irq < 0)
return js->irq;
- ret = devm_request_threaded_irq(pfdev->dev, js->irq,
- panfrost_job_irq_handler,
- panfrost_job_irq_handler_thread,
+ ret = devm_request_threaded_irq(pfdev->base.dev, js->irq,
+ panfrost_jm_irq_handler,
+ panfrost_jm_irq_handler_thread,
IRQF_SHARED, KBUILD_MODNAME "-job",
pfdev);
if (ret) {
- dev_err(pfdev->dev, "failed to request job irq");
+ dev_err(pfdev->base.dev, "failed to request job irq");
return ret;
}
@@ -882,15 +896,17 @@ int panfrost_job_init(struct panfrost_device *pfdev)
for (j = 0; j < NUM_JOB_SLOTS; j++) {
js->queue[j].fence_context = dma_fence_context_alloc(1);
+ args.name = panfrost_engine_names[j];
ret = drm_sched_init(&js->queue[j].sched, &args);
if (ret) {
- dev_err(pfdev->dev, "Failed to create scheduler: %d.", ret);
+ dev_err(pfdev->base.dev, "Failed to create scheduler: %d.", ret);
goto err_sched;
}
}
- panfrost_job_enable_interrupts(pfdev);
+ panfrost_jm_reset_interrupts(pfdev);
+ panfrost_jm_enable_interrupts(pfdev);
return 0;
@@ -902,7 +918,7 @@ err_sched:
return ret;
}
-void panfrost_job_fini(struct panfrost_device *pfdev)
+void panfrost_jm_fini(struct panfrost_device *pfdev)
{
struct panfrost_job_slot *js = pfdev->js;
int j;
@@ -917,39 +933,176 @@ void panfrost_job_fini(struct panfrost_device *pfdev)
destroy_workqueue(pfdev->reset.wq);
}
-int panfrost_job_open(struct panfrost_file_priv *panfrost_priv)
+int panfrost_jm_open(struct drm_file *file)
+{
+ struct panfrost_file_priv *panfrost_priv = file->driver_priv;
+ int ret;
+
+ struct drm_panfrost_jm_ctx_create default_jm_ctx = {
+ .priority = PANFROST_JM_CTX_PRIORITY_MEDIUM,
+ };
+
+ xa_init_flags(&panfrost_priv->jm_ctxs, XA_FLAGS_ALLOC);
+
+ ret = panfrost_jm_ctx_create(file, &default_jm_ctx);
+ if (ret)
+ return ret;
+
+ /* We expect the default context to be assigned handle 0. */
+ if (WARN_ON(default_jm_ctx.handle))
+ return -EINVAL;
+
+ return 0;
+}
+
+void panfrost_jm_close(struct drm_file *file)
+{
+ struct panfrost_file_priv *panfrost_priv = file->driver_priv;
+ struct panfrost_jm_ctx *jm_ctx;
+ unsigned long i;
+
+ xa_for_each(&panfrost_priv->jm_ctxs, i, jm_ctx)
+ panfrost_jm_ctx_destroy(file, i);
+
+ xa_destroy(&panfrost_priv->jm_ctxs);
+}
+
+int panfrost_jm_is_idle(struct panfrost_device *pfdev)
{
- struct panfrost_device *pfdev = panfrost_priv->pfdev;
struct panfrost_job_slot *js = pfdev->js;
- struct drm_gpu_scheduler *sched;
- int ret, i;
+ int i;
for (i = 0; i < NUM_JOB_SLOTS; i++) {
- sched = &js->queue[i].sched;
- ret = drm_sched_entity_init(&panfrost_priv->sched_entity[i],
- DRM_SCHED_PRIORITY_NORMAL, &sched,
- 1, NULL);
- if (WARN_ON(ret))
- return ret;
+ /* If there are any jobs in the HW queue, we're not idle */
+ if (atomic_read(&js->queue[i].sched.credit_count))
+ return false;
+ }
+
+ return true;
+}
+
+static void panfrost_jm_ctx_release(struct kref *kref)
+{
+ struct panfrost_jm_ctx *jm_ctx = container_of(kref, struct panfrost_jm_ctx, refcnt);
+
+ WARN_ON(!jm_ctx->destroyed);
+
+ for (u32 i = 0; i < ARRAY_SIZE(jm_ctx->slot_entity); i++)
+ drm_sched_entity_destroy(&jm_ctx->slot_entity[i]);
+
+ kfree(jm_ctx);
+}
+
+void
+panfrost_jm_ctx_put(struct panfrost_jm_ctx *jm_ctx)
+{
+ if (jm_ctx)
+ kref_put(&jm_ctx->refcnt, panfrost_jm_ctx_release);
+}
+
+struct panfrost_jm_ctx *
+panfrost_jm_ctx_get(struct panfrost_jm_ctx *jm_ctx)
+{
+ if (jm_ctx)
+ kref_get(&jm_ctx->refcnt);
+
+ return jm_ctx;
+}
+
+struct panfrost_jm_ctx *
+panfrost_jm_ctx_from_handle(struct drm_file *file, u32 handle)
+{
+ struct panfrost_file_priv *priv = file->driver_priv;
+ struct panfrost_jm_ctx *jm_ctx;
+
+ xa_lock(&priv->jm_ctxs);
+ jm_ctx = panfrost_jm_ctx_get(xa_load(&priv->jm_ctxs, handle));
+ xa_unlock(&priv->jm_ctxs);
+
+ return jm_ctx;
+}
+
+static int jm_ctx_prio_to_drm_sched_prio(struct drm_file *file,
+ enum drm_panfrost_jm_ctx_priority in,
+ enum drm_sched_priority *out)
+{
+ switch (in) {
+ case PANFROST_JM_CTX_PRIORITY_LOW:
+ *out = DRM_SCHED_PRIORITY_LOW;
+ return 0;
+ case PANFROST_JM_CTX_PRIORITY_MEDIUM:
+ *out = DRM_SCHED_PRIORITY_NORMAL;
+ return 0;
+ case PANFROST_JM_CTX_PRIORITY_HIGH:
+ if (!panfrost_high_prio_allowed(file))
+ return -EACCES;
+
+ *out = DRM_SCHED_PRIORITY_HIGH;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+int panfrost_jm_ctx_create(struct drm_file *file,
+ struct drm_panfrost_jm_ctx_create *args)
+{
+ struct panfrost_file_priv *priv = file->driver_priv;
+ struct panfrost_device *pfdev = priv->pfdev;
+ enum drm_sched_priority sched_prio;
+ struct panfrost_jm_ctx *jm_ctx;
+ int ret;
+
+ jm_ctx = kzalloc(sizeof(*jm_ctx), GFP_KERNEL);
+ if (!jm_ctx)
+ return -ENOMEM;
+
+ kref_init(&jm_ctx->refcnt);
+
+ ret = jm_ctx_prio_to_drm_sched_prio(file, args->priority, &sched_prio);
+ if (ret)
+ goto err_put_jm_ctx;
+
+ for (u32 i = 0; i < NUM_JOB_SLOTS; i++) {
+ struct drm_gpu_scheduler *sched = &pfdev->js->queue[i].sched;
+
+ ret = drm_sched_entity_init(&jm_ctx->slot_entity[i], sched_prio,
+ &sched, 1, NULL);
+ if (ret)
+ goto err_put_jm_ctx;
}
+
+ ret = xa_alloc(&priv->jm_ctxs, &args->handle, jm_ctx,
+ XA_LIMIT(0, MAX_JM_CTX_PER_FILE), GFP_KERNEL);
+ if (ret)
+ goto err_put_jm_ctx;
+
return 0;
+
+err_put_jm_ctx:
+ jm_ctx->destroyed = true;
+ panfrost_jm_ctx_put(jm_ctx);
+ return ret;
}
-void panfrost_job_close(struct panfrost_file_priv *panfrost_priv)
+int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle)
{
- struct panfrost_device *pfdev = panfrost_priv->pfdev;
- int i;
+ struct panfrost_file_priv *priv = file->driver_priv;
+ struct panfrost_device *pfdev = priv->pfdev;
+ struct panfrost_jm_ctx *jm_ctx;
- for (i = 0; i < NUM_JOB_SLOTS; i++)
- drm_sched_entity_destroy(&panfrost_priv->sched_entity[i]);
+ jm_ctx = xa_erase(&priv->jm_ctxs, handle);
+ if (!jm_ctx)
+ return -EINVAL;
+
+ jm_ctx->destroyed = true;
/* Kill in-flight jobs */
spin_lock(&pfdev->js->job_lock);
- for (i = 0; i < NUM_JOB_SLOTS; i++) {
- struct drm_sched_entity *entity = &panfrost_priv->sched_entity[i];
- int j;
+ for (u32 i = 0; i < ARRAY_SIZE(jm_ctx->slot_entity); i++) {
+ struct drm_sched_entity *entity = &jm_ctx->slot_entity[i];
- for (j = ARRAY_SIZE(pfdev->jobs[0]) - 1; j >= 0; j--) {
+ for (int j = ARRAY_SIZE(pfdev->jobs[0]) - 1; j >= 0; j--) {
struct panfrost_job *job = pfdev->jobs[i][j];
u32 cmd;
@@ -980,18 +1133,7 @@ void panfrost_job_close(struct panfrost_file_priv *panfrost_priv)
}
}
spin_unlock(&pfdev->js->job_lock);
-}
-
-int panfrost_job_is_idle(struct panfrost_device *pfdev)
-{
- struct panfrost_job_slot *js = pfdev->js;
- int i;
-
- for (i = 0; i < NUM_JOB_SLOTS; i++) {
- /* If there are any jobs in the HW queue, we're not idle */
- if (atomic_read(&js->queue[i].sched.credit_count))
- return false;
- }
- return true;
+ panfrost_jm_ctx_put(jm_ctx);
+ return 0;
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
index ec581b97852b..c3f57e41a571 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.h
+++ b/drivers/gpu/drm/panfrost/panfrost_job.h
@@ -18,6 +18,7 @@ struct panfrost_job {
struct panfrost_device *pfdev;
struct panfrost_mmu *mmu;
+ struct panfrost_jm_ctx *ctx;
/* Fence to be signaled by IRQ handler when the job is complete. */
struct dma_fence *done_fence;
@@ -39,15 +40,38 @@ struct panfrost_job {
u64 start_cycles;
};
-int panfrost_job_init(struct panfrost_device *pfdev);
-void panfrost_job_fini(struct panfrost_device *pfdev);
-int panfrost_job_open(struct panfrost_file_priv *panfrost_priv);
-void panfrost_job_close(struct panfrost_file_priv *panfrost_priv);
+struct panfrost_js_ctx {
+ struct drm_sched_entity sched_entity;
+ bool enabled;
+};
+
+#define NUM_JOB_SLOTS 3
+
+struct panfrost_jm_ctx {
+ struct kref refcnt;
+ bool destroyed;
+ struct drm_sched_entity slot_entity[NUM_JOB_SLOTS];
+};
+
+extern const char * const panfrost_engine_names[];
+
+int panfrost_jm_ctx_create(struct drm_file *file,
+ struct drm_panfrost_jm_ctx_create *args);
+int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle);
+void panfrost_jm_ctx_put(struct panfrost_jm_ctx *jm_ctx);
+struct panfrost_jm_ctx *panfrost_jm_ctx_get(struct panfrost_jm_ctx *jm_ctx);
+struct panfrost_jm_ctx *panfrost_jm_ctx_from_handle(struct drm_file *file, u32 handle);
+
+int panfrost_jm_init(struct panfrost_device *pfdev);
+void panfrost_jm_fini(struct panfrost_device *pfdev);
+int panfrost_jm_open(struct drm_file *file);
+void panfrost_jm_close(struct drm_file *file);
+void panfrost_jm_reset_interrupts(struct panfrost_device *pfdev);
+void panfrost_jm_enable_interrupts(struct panfrost_device *pfdev);
+void panfrost_jm_suspend_irq(struct panfrost_device *pfdev);
+int panfrost_jm_is_idle(struct panfrost_device *pfdev);
int panfrost_job_get_slot(struct panfrost_job *job);
int panfrost_job_push(struct panfrost_job *job);
void panfrost_job_put(struct panfrost_job *job);
-void panfrost_job_enable_interrupts(struct panfrost_device *pfdev);
-void panfrost_job_suspend_irq(struct panfrost_device *pfdev);
-int panfrost_job_is_idle(struct panfrost_device *pfdev);
#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index f6b91c052cfb..8f3b7a7b6ad0 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -2,6 +2,7 @@
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
#include <drm/panfrost_drm.h>
+#include <drm/drm_print.h>
#include <linux/atomic.h>
#include <linux/bitfield.h>
@@ -81,7 +82,7 @@ static int wait_ready(struct panfrost_device *pfdev, u32 as_nr)
if (ret) {
/* The GPU hung, let's trigger a reset */
panfrost_device_schedule_reset(pfdev);
- dev_err(pfdev->dev, "AS_ACTIVE bit stuck\n");
+ dev_err(pfdev->base.dev, "AS_ACTIVE bit stuck\n");
}
return ret;
@@ -222,7 +223,7 @@ static int mmu_cfg_init_aarch64_4k(struct panfrost_mmu *mmu)
struct io_pgtable_cfg *pgtbl_cfg = &mmu->pgtbl_cfg;
struct panfrost_device *pfdev = mmu->pfdev;
- if (drm_WARN_ON(pfdev->ddev, pgtbl_cfg->arm_lpae_s1_cfg.ttbr &
+ if (drm_WARN_ON(&pfdev->base, pgtbl_cfg->arm_lpae_s1_cfg.ttbr &
~AS_TRANSTAB_AARCH64_4K_ADDR_MASK))
return -EINVAL;
@@ -253,12 +254,12 @@ static int panfrost_mmu_cfg_init(struct panfrost_mmu *mmu,
return mmu_cfg_init_mali_lpae(mmu);
default:
/* This should never happen */
- drm_WARN(pfdev->ddev, 1, "Invalid pgtable format");
+ drm_WARN(&pfdev->base, 1, "Invalid pgtable format");
return -EINVAL;
}
}
-u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
+int panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
{
int as;
@@ -300,7 +301,10 @@ u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
if (!atomic_read(&lru_mmu->as_count))
break;
}
- WARN_ON(&lru_mmu->list == &pfdev->as_lru_list);
+ if (WARN_ON(&lru_mmu->list == &pfdev->as_lru_list)) {
+ as = -EBUSY;
+ goto out;
+ }
list_del_init(&lru_mmu->list);
as = lru_mmu->as;
@@ -315,7 +319,9 @@ u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu)
atomic_set(&mmu->as_count, 1);
list_add(&mmu->list, &pfdev->as_lru_list);
- dev_dbg(pfdev->dev, "Assigned AS%d to mmu %p, alloc_mask=%lx", as, mmu, pfdev->as_alloc_mask);
+ dev_dbg(pfdev->base.dev,
+ "Assigned AS%d to mmu %p, alloc_mask=%lx",
+ as, mmu, pfdev->as_alloc_mask);
panfrost_mmu_enable(pfdev, mmu);
@@ -381,13 +387,30 @@ static void panfrost_mmu_flush_range(struct panfrost_device *pfdev,
if (mmu->as < 0)
return;
- pm_runtime_get_noresume(pfdev->dev);
+ pm_runtime_get_noresume(pfdev->base.dev);
/* Flush the PTs only if we're already awake */
- if (pm_runtime_active(pfdev->dev))
+ if (pm_runtime_active(pfdev->base.dev))
mmu_hw_do_operation(pfdev, mmu, iova, size, AS_COMMAND_FLUSH_PT);
- pm_runtime_put_autosuspend(pfdev->dev);
+ pm_runtime_put_autosuspend(pfdev->base.dev);
+}
+
+static void mmu_unmap_range(struct panfrost_mmu *mmu, u64 iova, size_t len)
+{
+ struct io_pgtable_ops *ops = mmu->pgtbl_ops;
+ size_t pgsize, unmapped_len = 0;
+ size_t unmapped_page, pgcount;
+
+ while (unmapped_len < len) {
+ pgsize = get_pgsize(iova, len - unmapped_len, &pgcount);
+
+ unmapped_page = ops->unmap_pages(ops, iova, pgsize, pgcount, NULL);
+ WARN_ON(unmapped_page != pgsize * pgcount);
+
+ iova += pgsize * pgcount;
+ unmapped_len += pgsize * pgcount;
+ }
}
static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
@@ -396,22 +419,30 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
unsigned int count;
struct scatterlist *sgl;
struct io_pgtable_ops *ops = mmu->pgtbl_ops;
+ size_t total_mapped = 0;
u64 start_iova = iova;
+ int ret;
for_each_sgtable_dma_sg(sgt, sgl, count) {
unsigned long paddr = sg_dma_address(sgl);
size_t len = sg_dma_len(sgl);
- dev_dbg(pfdev->dev, "map: as=%d, iova=%llx, paddr=%lx, len=%zx", mmu->as, iova, paddr, len);
+ dev_dbg(pfdev->base.dev,
+ "map: as=%d, iova=%llx, paddr=%lx, len=%zx",
+ mmu->as, iova, paddr, len);
while (len) {
size_t pgcount, mapped = 0;
size_t pgsize = get_pgsize(iova | paddr, len, &pgcount);
- ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot,
+ ret = ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot,
GFP_KERNEL, &mapped);
+ if (ret)
+ goto err_unmap_pages;
+
/* Don't get stuck if things have gone wrong */
mapped = max(mapped, pgsize);
+ total_mapped += mapped;
iova += mapped;
paddr += mapped;
len -= mapped;
@@ -421,6 +452,10 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
panfrost_mmu_flush_range(pfdev, mmu, start_iova, iova - start_iova);
return 0;
+
+err_unmap_pages:
+ mmu_unmap_range(mmu, start_iova, total_mapped);
+ return ret;
}
int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
@@ -431,6 +466,7 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
struct panfrost_device *pfdev = to_panfrost_device(obj->dev);
struct sg_table *sgt;
int prot = IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE;
+ int ret;
if (WARN_ON(mapping->active))
return 0;
@@ -442,11 +478,18 @@ int panfrost_mmu_map(struct panfrost_gem_mapping *mapping)
if (WARN_ON(IS_ERR(sgt)))
return PTR_ERR(sgt);
- mmu_map_sg(pfdev, mapping->mmu, mapping->mmnode.start << PAGE_SHIFT,
- prot, sgt);
+ ret = mmu_map_sg(pfdev, mapping->mmu, mapping->mmnode.start << PAGE_SHIFT,
+ prot, sgt);
+ if (ret)
+ goto err_put_pages;
+
mapping->active = true;
return 0;
+
+err_put_pages:
+ drm_gem_shmem_put_pages_locked(shmem);
+ return ret;
}
void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
@@ -462,7 +505,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
if (WARN_ON(!mapping->active))
return;
- dev_dbg(pfdev->dev, "unmap: as=%d, iova=%llx, len=%zx",
+ dev_dbg(pfdev->base.dev, "unmap: as=%d, iova=%llx, len=%zx",
mapping->mmu->as, iova, len);
while (unmapped_len < len) {
@@ -559,7 +602,7 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
bo = bomapping->obj;
if (!bo->is_heap) {
- dev_WARN(pfdev->dev, "matching BO is not heap type (GPU VA = %llx)",
+ dev_WARN(pfdev->base.dev, "matching BO is not heap type (GPU VA = %llx)",
bomapping->mmnode.start << PAGE_SHIFT);
ret = -EINVAL;
goto err_bo;
@@ -595,10 +638,12 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
refcount_set(&bo->base.pages_use_count, 1);
} else {
pages = bo->base.pages;
- if (pages[page_offset]) {
- /* Pages are already mapped, bail out. */
- goto out;
- }
+ }
+
+ sgt = &bo->sgts[page_offset / (SZ_2M / PAGE_SIZE)];
+ if (sgt->sgl) {
+ /* Pages are already mapped, bail out. */
+ goto out;
}
mapping = bo->base.base.filp->f_mapping;
@@ -620,23 +665,24 @@ static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
}
}
- sgt = &bo->sgts[page_offset / (SZ_2M / PAGE_SIZE)];
ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
NUM_FAULT_PAGES, 0, SZ_2M, GFP_KERNEL);
if (ret)
goto err_unlock;
- ret = dma_map_sgtable(pfdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
+ ret = dma_map_sgtable(pfdev->base.dev, sgt, DMA_BIDIRECTIONAL, 0);
if (ret)
goto err_map;
- mmu_map_sg(pfdev, bomapping->mmu, addr,
- IOMMU_WRITE | IOMMU_READ | IOMMU_CACHE | IOMMU_NOEXEC, sgt);
+ ret = mmu_map_sg(pfdev, bomapping->mmu, addr,
+ IOMMU_WRITE | IOMMU_READ | IOMMU_CACHE | IOMMU_NOEXEC, sgt);
+ if (ret)
+ goto err_mmu_map_sg;
bomapping->active = true;
bo->heap_rss_size += SZ_2M;
- dev_dbg(pfdev->dev, "mapped page fault @ AS%d %llx", as, addr);
+ dev_dbg(pfdev->base.dev, "mapped page fault @ AS%d %llx", as, addr);
out:
dma_resv_unlock(obj->resv);
@@ -645,6 +691,8 @@ out:
return 0;
+err_mmu_map_sg:
+ dma_unmap_sgtable(pfdev->base.dev, sgt, DMA_BIDIRECTIONAL, 0);
err_map:
sg_free_table(sgt);
err_unlock:
@@ -662,13 +710,12 @@ static void panfrost_mmu_release_ctx(struct kref *kref)
spin_lock(&pfdev->as_lock);
if (mmu->as >= 0) {
- pm_runtime_get_noresume(pfdev->dev);
- if (pm_runtime_active(pfdev->dev))
+ pm_runtime_get_noresume(pfdev->base.dev);
+ if (pm_runtime_active(pfdev->base.dev))
panfrost_mmu_disable(pfdev, mmu->as);
- pm_runtime_put_autosuspend(pfdev->dev);
+ pm_runtime_put_autosuspend(pfdev->base.dev);
clear_bit(mmu->as, &pfdev->as_alloc_mask);
- clear_bit(mmu->as, &pfdev->as_in_use_mask);
list_del(&mmu->list);
}
spin_unlock(&pfdev->as_lock);
@@ -726,7 +773,7 @@ struct panfrost_mmu *panfrost_mmu_ctx_create(struct panfrost_device *pfdev)
if (pfdev->comp->gpu_quirks & BIT(GPU_QUIRK_FORCE_AARCH64_PGTABLE)) {
if (!panfrost_has_hw_feature(pfdev, HW_FEATURE_AARCH64_MMU)) {
- dev_err_once(pfdev->dev,
+ dev_err_once(pfdev->base.dev,
"AARCH64_4K page table not supported\n");
return ERR_PTR(-EINVAL);
}
@@ -755,7 +802,7 @@ struct panfrost_mmu *panfrost_mmu_ctx_create(struct panfrost_device *pfdev)
.oas = pa_bits,
.coherent_walk = pfdev->coherent,
.tlb = &mmu_tlb_ops,
- .iommu_dev = pfdev->dev,
+ .iommu_dev = pfdev->base.dev,
};
mmu->pgtbl_ops = alloc_io_pgtable_ops(fmt, &mmu->pgtbl_cfg, mmu);
@@ -848,7 +895,7 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
if (ret) {
/* terminal fault, print info about the fault */
- dev_err(pfdev->dev,
+ dev_err(pfdev->base.dev,
"Unhandled Page fault in AS%d at VA 0x%016llX\n"
"Reason: %s\n"
"raw fault status: 0x%X\n"
@@ -896,18 +943,18 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
{
int err;
- pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
+ pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "mmu");
if (pfdev->mmu_irq < 0)
return pfdev->mmu_irq;
- err = devm_request_threaded_irq(pfdev->dev, pfdev->mmu_irq,
+ err = devm_request_threaded_irq(pfdev->base.dev, pfdev->mmu_irq,
panfrost_mmu_irq_handler,
panfrost_mmu_irq_handler_thread,
IRQF_SHARED, KBUILD_MODNAME "-mmu",
pfdev);
if (err) {
- dev_err(pfdev->dev, "failed to request mmu irq");
+ dev_err(pfdev->base.dev, "failed to request mmu irq");
return err;
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.h b/drivers/gpu/drm/panfrost/panfrost_mmu.h
index 022a9a74a114..27c3c65ed074 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.h
@@ -4,6 +4,7 @@
#ifndef __PANFROST_MMU_H__
#define __PANFROST_MMU_H__
+struct panfrost_device;
struct panfrost_gem_mapping;
struct panfrost_file_priv;
struct panfrost_mmu;
@@ -16,7 +17,7 @@ void panfrost_mmu_fini(struct panfrost_device *pfdev);
void panfrost_mmu_reset(struct panfrost_device *pfdev);
void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev);
-u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
+int panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
struct panfrost_mmu *panfrost_mmu_ctx_get(struct panfrost_mmu *mmu);
diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
index 0dd62e8b2fa7..7020c0192e18 100644
--- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
+++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c
@@ -84,11 +84,11 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
else if (perfcnt->user)
return -EBUSY;
- ret = pm_runtime_get_sync(pfdev->dev);
+ ret = pm_runtime_get_sync(pfdev->base.dev);
if (ret < 0)
goto err_put_pm;
- bo = drm_gem_shmem_create(pfdev->ddev, perfcnt->bosize);
+ bo = drm_gem_shmem_create(&pfdev->base, perfcnt->bosize);
if (IS_ERR(bo)) {
ret = PTR_ERR(bo);
goto err_put_pm;
@@ -130,9 +130,11 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
goto err_vunmap;
}
- perfcnt->user = user;
+ ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
+ if (ret < 0)
+ goto err_vunmap;
- as = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu);
+ as = ret;
cfg = GPU_PERFCNT_CFG_AS(as) |
GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL);
@@ -164,6 +166,8 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev,
/* The BO ref is retained by the mapping. */
drm_gem_object_put(&bo->base);
+ perfcnt->user = user;
+
return 0;
err_vunmap:
@@ -175,7 +179,7 @@ err_close_bo:
err_put_bo:
drm_gem_object_put(&bo->base);
err_put_pm:
- pm_runtime_put(pfdev->dev);
+ pm_runtime_put(pfdev->base.dev);
return ret;
}
@@ -203,7 +207,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu);
panfrost_gem_mapping_put(perfcnt->mapping);
perfcnt->mapping = NULL;
- pm_runtime_put_autosuspend(pfdev->dev);
+ pm_runtime_put_autosuspend(pfdev->base.dev);
return 0;
}
@@ -211,7 +215,7 @@ static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev,
int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
struct drm_panfrost_perfcnt_enable *req = data;
int ret;
@@ -238,7 +242,7 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data,
int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
- struct panfrost_device *pfdev = dev->dev_private;
+ struct panfrost_device *pfdev = to_panfrost_device(dev);
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
struct drm_panfrost_perfcnt_dump *req = data;
void __user *user_ptr = (void __user *)(uintptr_t)req->buf_ptr;
@@ -273,12 +277,12 @@ void panfrost_perfcnt_close(struct drm_file *file_priv)
struct panfrost_device *pfdev = pfile->pfdev;
struct panfrost_perfcnt *perfcnt = pfdev->perfcnt;
- pm_runtime_get_sync(pfdev->dev);
+ pm_runtime_get_sync(pfdev->base.dev);
mutex_lock(&perfcnt->lock);
if (perfcnt->user == pfile)
panfrost_perfcnt_disable_locked(pfdev, file_priv);
mutex_unlock(&perfcnt->lock);
- pm_runtime_put_autosuspend(pfdev->dev);
+ pm_runtime_put_autosuspend(pfdev->base.dev);
}
int panfrost_perfcnt_init(struct panfrost_device *pfdev)
@@ -316,7 +320,7 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev)
COUNTERS_PER_BLOCK * BYTES_PER_COUNTER;
}
- perfcnt = devm_kzalloc(pfdev->dev, sizeof(*perfcnt), GFP_KERNEL);
+ perfcnt = devm_kzalloc(pfdev->base.dev, sizeof(*perfcnt), GFP_KERNEL);
if (!perfcnt)
return -ENOMEM;
diff --git a/drivers/gpu/drm/panthor/Makefile b/drivers/gpu/drm/panthor/Makefile
index 02db21748c12..753a32c446df 100644
--- a/drivers/gpu/drm/panthor/Makefile
+++ b/drivers/gpu/drm/panthor/Makefile
@@ -10,6 +10,7 @@ panthor-y := \
panthor_heap.o \
panthor_hw.o \
panthor_mmu.o \
+ panthor_pwr.o \
panthor_sched.o
obj-$(CONFIG_DRM_PANTHOR) += panthor.o
diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.c b/drivers/gpu/drm/panthor/panthor_devfreq.c
index 3686515d368d..2249b41ca4af 100644
--- a/drivers/gpu/drm/panthor/panthor_devfreq.c
+++ b/drivers/gpu/drm/panthor/panthor_devfreq.c
@@ -8,6 +8,7 @@
#include <linux/pm_opp.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "panthor_devfreq.h"
#include "panthor_device.h"
@@ -62,7 +63,6 @@ static void panthor_devfreq_update_utilization(struct panthor_devfreq *pdevfreq)
static int panthor_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
- struct panthor_device *ptdev = dev_get_drvdata(dev);
struct dev_pm_opp *opp;
int err;
@@ -72,8 +72,6 @@ static int panthor_devfreq_target(struct device *dev, unsigned long *freq,
dev_pm_opp_put(opp);
err = dev_pm_opp_set_rate(dev, *freq);
- if (!err)
- ptdev->current_frequency = *freq;
return err;
}
@@ -115,11 +113,21 @@ static int panthor_devfreq_get_dev_status(struct device *dev,
return 0;
}
+static int panthor_devfreq_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+ struct panthor_device *ptdev = dev_get_drvdata(dev);
+
+ *freq = clk_get_rate(ptdev->clks.core);
+
+ return 0;
+}
+
static struct devfreq_dev_profile panthor_devfreq_profile = {
.timer = DEVFREQ_TIMER_DELAYED,
.polling_ms = 50, /* ~3 frames */
.target = panthor_devfreq_target,
.get_dev_status = panthor_devfreq_get_dev_status,
+ .get_cur_freq = panthor_devfreq_get_cur_freq,
};
int panthor_devfreq_init(struct panthor_device *ptdev)
@@ -134,6 +142,7 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
struct thermal_cooling_device *cooling;
struct device *dev = ptdev->base.dev;
struct panthor_devfreq *pdevfreq;
+ struct opp_table *table;
struct dev_pm_opp *opp;
unsigned long cur_freq;
unsigned long freq = ULONG_MAX;
@@ -145,18 +154,30 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
ptdev->devfreq = pdevfreq;
- ret = devm_pm_opp_set_regulators(dev, reg_names);
- if (ret) {
- if (ret != -EPROBE_DEFER)
- DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n");
-
- return ret;
+ /*
+ * The power domain associated with the GPU may have already added an
+ * OPP table, complete with OPPs, as part of the platform bus
+ * initialization. If this is the case, the power domain is in charge of
+ * also controlling the performance, with a set_performance callback.
+ * Only add a new OPP table from DT if there isn't such a table present
+ * already.
+ */
+ table = dev_pm_opp_get_opp_table(dev);
+ if (IS_ERR_OR_NULL(table)) {
+ ret = devm_pm_opp_set_regulators(dev, reg_names);
+ if (ret && ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ DRM_DEV_ERROR(dev, "Couldn't set OPP regulators\n");
+ return ret;
+ }
+
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret)
+ return ret;
+ } else {
+ dev_pm_opp_put_opp_table(table);
}
- ret = devm_pm_opp_of_add_table(dev);
- if (ret)
- return ret;
-
spin_lock_init(&pdevfreq->lock);
panthor_devfreq_reset(pdevfreq);
@@ -198,7 +219,6 @@ int panthor_devfreq_init(struct panthor_device *ptdev)
return PTR_ERR(opp);
panthor_devfreq_profile.initial_freq = cur_freq;
- ptdev->current_frequency = cur_freq;
/*
* Set the recommend OPP this will enable and configure the regulator
@@ -296,3 +316,19 @@ void panthor_devfreq_record_idle(struct panthor_device *ptdev)
spin_unlock_irqrestore(&pdevfreq->lock, irqflags);
}
+
+unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev)
+{
+ struct panthor_devfreq *pdevfreq = ptdev->devfreq;
+ unsigned long freq = 0;
+ int ret;
+
+ if (!pdevfreq->devfreq)
+ return 0;
+
+ ret = pdevfreq->devfreq->profile->get_cur_freq(ptdev->base.dev, &freq);
+ if (ret)
+ return 0;
+
+ return freq;
+}
diff --git a/drivers/gpu/drm/panthor/panthor_devfreq.h b/drivers/gpu/drm/panthor/panthor_devfreq.h
index b7631de695f7..f8e29e02f66c 100644
--- a/drivers/gpu/drm/panthor/panthor_devfreq.h
+++ b/drivers/gpu/drm/panthor/panthor_devfreq.h
@@ -18,4 +18,6 @@ void panthor_devfreq_suspend(struct panthor_device *ptdev);
void panthor_devfreq_record_busy(struct panthor_device *ptdev);
void panthor_devfreq_record_idle(struct panthor_device *ptdev);
+unsigned long panthor_devfreq_get_freq(struct panthor_device *ptdev);
+
#endif /* __PANTHOR_DEVFREQ_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 81df49880bd8..e133b1e0ad6d 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -13,6 +13,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "panthor_devfreq.h"
#include "panthor_device.h"
@@ -20,6 +21,7 @@
#include "panthor_gpu.h"
#include "panthor_hw.h"
#include "panthor_mmu.h"
+#include "panthor_pwr.h"
#include "panthor_regs.h"
#include "panthor_sched.h"
@@ -65,6 +67,16 @@ static int panthor_clk_init(struct panthor_device *ptdev)
return 0;
}
+static int panthor_init_power(struct device *dev)
+{
+ struct dev_pm_domain_list *pd_list = NULL;
+
+ if (dev->pm_domain)
+ return 0;
+
+ return devm_pm_domain_attach_list(dev, NULL, &pd_list);
+}
+
void panthor_device_unplug(struct panthor_device *ptdev)
{
/* This function can be called from two different path: the reset work
@@ -83,6 +95,8 @@ void panthor_device_unplug(struct panthor_device *ptdev)
return;
}
+ drm_WARN_ON(&ptdev->base, pm_runtime_get_sync(ptdev->base.dev) < 0);
+
/* Call drm_dev_unplug() so any access to HW blocks happening after
* that point get rejected.
*/
@@ -93,8 +107,6 @@ void panthor_device_unplug(struct panthor_device *ptdev)
*/
mutex_unlock(&ptdev->unplug.lock);
- drm_WARN_ON(&ptdev->base, pm_runtime_get_sync(ptdev->base.dev) < 0);
-
/* Now, try to cleanly shutdown the GPU before the device resources
* get reclaimed.
*/
@@ -102,6 +114,7 @@ void panthor_device_unplug(struct panthor_device *ptdev)
panthor_fw_unplug(ptdev);
panthor_mmu_unplug(ptdev);
panthor_gpu_unplug(ptdev);
+ panthor_pwr_unplug(ptdev);
pm_runtime_dont_use_autosuspend(ptdev->base.dev);
pm_runtime_put_sync_suspend(ptdev->base.dev);
@@ -120,7 +133,7 @@ static void panthor_device_reset_cleanup(struct drm_device *ddev, void *data)
{
struct panthor_device *ptdev = container_of(ddev, struct panthor_device, base);
- cancel_work_sync(&ptdev->reset.work);
+ disable_work_sync(&ptdev->reset.work);
destroy_workqueue(ptdev->reset.wq);
}
@@ -141,8 +154,8 @@ static void panthor_device_reset_work(struct work_struct *work)
panthor_sched_pre_reset(ptdev);
panthor_fw_pre_reset(ptdev, true);
panthor_mmu_pre_reset(ptdev);
- panthor_gpu_soft_reset(ptdev);
- panthor_gpu_l2_power_on(ptdev);
+ panthor_hw_soft_reset(ptdev);
+ panthor_hw_l2_power_on(ptdev);
panthor_mmu_post_reset(ptdev);
ret = panthor_fw_post_reset(ptdev);
atomic_set(&ptdev->reset.pending, 0);
@@ -172,6 +185,8 @@ int panthor_device_init(struct panthor_device *ptdev)
struct page *p;
int ret;
+ ptdev->soc_data = of_device_get_match_data(ptdev->base.dev);
+
init_completion(&ptdev->unplug.done);
ret = drmm_mutex_init(&ptdev->base, &ptdev->unplug.lock);
if (ret)
@@ -219,6 +234,12 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
return ret;
+ ret = panthor_init_power(ptdev->base.dev);
+ if (ret < 0) {
+ drm_err(&ptdev->base, "init power domains failed, ret=%d", ret);
+ return ret;
+ }
+
ret = panthor_devfreq_init(ptdev);
if (ret)
return ret;
@@ -249,10 +270,14 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
goto err_rpm_put;
- ret = panthor_gpu_init(ptdev);
+ ret = panthor_pwr_init(ptdev);
if (ret)
goto err_rpm_put;
+ ret = panthor_gpu_init(ptdev);
+ if (ret)
+ goto err_unplug_pwr;
+
ret = panthor_gpu_coherency_init(ptdev);
if (ret)
goto err_unplug_gpu;
@@ -293,6 +318,9 @@ err_unplug_mmu:
err_unplug_gpu:
panthor_gpu_unplug(ptdev);
+err_unplug_pwr:
+ panthor_pwr_unplug(ptdev);
+
err_rpm_put:
pm_runtime_put_sync_suspend(ptdev->base.dev);
return ret;
@@ -446,6 +474,7 @@ static int panthor_device_resume_hw_components(struct panthor_device *ptdev)
{
int ret;
+ panthor_pwr_resume(ptdev);
panthor_gpu_resume(ptdev);
panthor_mmu_resume(ptdev);
@@ -455,6 +484,7 @@ static int panthor_device_resume_hw_components(struct panthor_device *ptdev)
panthor_mmu_suspend(ptdev);
panthor_gpu_suspend(ptdev);
+ panthor_pwr_suspend(ptdev);
return ret;
}
@@ -568,6 +598,7 @@ int panthor_device_suspend(struct device *dev)
panthor_fw_suspend(ptdev);
panthor_mmu_suspend(ptdev);
panthor_gpu_suspend(ptdev);
+ panthor_pwr_suspend(ptdev);
drm_dev_exit(cookie);
}
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index 4fc7cf2aeed5..f35e52b9546a 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -24,14 +24,27 @@ struct panthor_device;
struct panthor_gpu;
struct panthor_group_pool;
struct panthor_heap_pool;
+struct panthor_hw;
struct panthor_job;
struct panthor_mmu;
struct panthor_fw;
struct panthor_perfcnt;
+struct panthor_pwr;
struct panthor_vm;
struct panthor_vm_pool;
/**
+ * struct panthor_soc_data - Panthor SoC Data
+ */
+struct panthor_soc_data {
+ /** @asn_hash_enable: True if GPU_L2_CONFIG_ASN_HASH_ENABLE must be set. */
+ bool asn_hash_enable;
+
+ /** @asn_hash: ASN_HASH values when asn_hash_enable is true. */
+ u32 asn_hash[3];
+};
+
+/**
* enum panthor_device_pm_state - PM state
*/
enum panthor_device_pm_state {
@@ -93,6 +106,9 @@ struct panthor_device {
/** @base: Base drm_device. */
struct drm_device base;
+ /** @soc_data: Optional SoC data. */
+ const struct panthor_soc_data *soc_data;
+
/** @phys_addr: Physical address of the iomem region. */
phys_addr_t phys_addr;
@@ -120,6 +136,12 @@ struct panthor_device {
/** @csif_info: Command stream interface information. */
struct drm_panthor_csif_info csif_info;
+ /** @hw: GPU-specific data. */
+ struct panthor_hw *hw;
+
+ /** @pwr: Power control management data. */
+ struct panthor_pwr *pwr;
+
/** @gpu: GPU management data. */
struct panthor_gpu *gpu;
@@ -200,9 +222,6 @@ struct panthor_device {
/** @profile_mask: User-set profiling flags for job accounting. */
u32 profile_mask;
- /** @current_frequency: Device clock frequency at present. Set by DVFS*/
- unsigned long current_frequency;
-
/** @fast_rate: Maximum device clock frequency. Set by DVFS */
unsigned long fast_rate;
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 4c202fc5ce05..d1d4c50da5bf 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -20,11 +20,13 @@
#include <drm/drm_drv.h>
#include <drm/drm_exec.h>
#include <drm/drm_ioctl.h>
+#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include <drm/drm_utils.h>
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
+#include "panthor_devfreq.h"
#include "panthor_device.h"
#include "panthor_fw.h"
#include "panthor_gem.h"
@@ -1105,7 +1107,7 @@ static int panthor_ioctl_group_create(struct drm_device *ddev, void *data,
if (ret)
goto out;
- ret = panthor_group_create(pfile, args, queue_args);
+ ret = panthor_group_create(pfile, args, queue_args, file->client_id);
if (ret < 0)
goto out;
args->group_handle = ret;
@@ -1519,7 +1521,8 @@ static void panthor_gpu_show_fdinfo(struct panthor_device *ptdev,
drm_printf(p, "drm-cycles-panthor:\t%llu\n", pfile->stats.cycles);
drm_printf(p, "drm-maxfreq-panthor:\t%lu Hz\n", ptdev->fast_rate);
- drm_printf(p, "drm-curfreq-panthor:\t%lu Hz\n", ptdev->current_frequency);
+ drm_printf(p, "drm-curfreq-panthor:\t%lu Hz\n",
+ panthor_devfreq_get_freq(ptdev));
}
static void panthor_show_internal_memory_stats(struct drm_printer *p, struct drm_file *file)
@@ -1682,7 +1685,13 @@ static struct attribute *panthor_attrs[] = {
ATTRIBUTE_GROUPS(panthor);
+static const struct panthor_soc_data soc_data_mediatek_mt8196 = {
+ .asn_hash_enable = true,
+ .asn_hash = { 0xb, 0xe, 0x0, },
+};
+
static const struct of_device_id dt_match[] = {
+ { .compatible = "mediatek,mt8196-mali", .data = &soc_data_mediatek_mt8196, },
{ .compatible = "rockchip,rk3588-mali" },
{ .compatible = "arm,mali-valhall-csf" },
{}
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index df767e82148a..1a5e3c1a27fb 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -16,11 +16,13 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "panthor_device.h"
#include "panthor_fw.h"
#include "panthor_gem.h"
#include "panthor_gpu.h"
+#include "panthor_hw.h"
#include "panthor_mmu.h"
#include "panthor_regs.h"
#include "panthor_sched.h"
@@ -32,6 +34,7 @@
#define PROGRESS_TIMEOUT_SCALE_SHIFT 10
#define IDLE_HYSTERESIS_US 800
#define PWROFF_HYSTERESIS_US 10000
+#define MCU_HALT_TIMEOUT_US (1ULL * USEC_PER_SEC)
/**
* struct panthor_fw_binary_hdr - Firmware binary header.
@@ -316,6 +319,49 @@ panthor_fw_get_cs_iface(struct panthor_device *ptdev, u32 csg_slot, u32 cs_slot)
return &ptdev->fw->iface.streams[csg_slot][cs_slot];
}
+static bool panthor_fw_has_glb_state(struct panthor_device *ptdev)
+{
+ struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
+
+ return glb_iface->control->version >= CSF_IFACE_VERSION(4, 1, 0);
+}
+
+static bool panthor_fw_has_64bit_ep_req(struct panthor_device *ptdev)
+{
+ struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
+
+ return glb_iface->control->version >= CSF_IFACE_VERSION(4, 0, 0);
+}
+
+u64 panthor_fw_csg_endpoint_req_get(struct panthor_device *ptdev,
+ struct panthor_fw_csg_iface *csg_iface)
+{
+ if (panthor_fw_has_64bit_ep_req(ptdev))
+ return csg_iface->input->endpoint_req2;
+ else
+ return csg_iface->input->endpoint_req;
+}
+
+void panthor_fw_csg_endpoint_req_set(struct panthor_device *ptdev,
+ struct panthor_fw_csg_iface *csg_iface, u64 value)
+{
+ if (panthor_fw_has_64bit_ep_req(ptdev))
+ csg_iface->input->endpoint_req2 = value;
+ else
+ csg_iface->input->endpoint_req = lower_32_bits(value);
+}
+
+void panthor_fw_csg_endpoint_req_update(struct panthor_device *ptdev,
+ struct panthor_fw_csg_iface *csg_iface, u64 value,
+ u64 mask)
+{
+ if (panthor_fw_has_64bit_ep_req(ptdev))
+ panthor_fw_update_reqs64(csg_iface, endpoint_req2, value, mask);
+ else
+ panthor_fw_update_reqs(csg_iface, endpoint_req, lower_32_bits(value),
+ lower_32_bits(mask));
+}
+
/**
* panthor_fw_conv_timeout() - Convert a timeout into a cycle-count
* @ptdev: Device.
@@ -995,6 +1041,9 @@ static void panthor_fw_init_global_iface(struct panthor_device *ptdev)
GLB_IDLE_EN |
GLB_IDLE;
+ if (panthor_fw_has_glb_state(ptdev))
+ glb_iface->input->ack_irq_mask |= GLB_STATE_MASK;
+
panthor_fw_update_reqs(glb_iface, req, GLB_IDLE_EN, GLB_IDLE_EN);
panthor_fw_toggle_reqs(glb_iface, req, ack,
GLB_CFG_ALLOC_EN |
@@ -1068,6 +1117,54 @@ static void panthor_fw_stop(struct panthor_device *ptdev)
drm_err(&ptdev->base, "Failed to stop MCU");
}
+static bool panthor_fw_mcu_halted(struct panthor_device *ptdev)
+{
+ struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
+ bool halted;
+
+ halted = gpu_read(ptdev, MCU_STATUS) == MCU_STATUS_HALT;
+
+ if (panthor_fw_has_glb_state(ptdev))
+ halted &= (GLB_STATE_GET(glb_iface->output->ack) == GLB_STATE_HALT);
+
+ return halted;
+}
+
+static void panthor_fw_halt_mcu(struct panthor_device *ptdev)
+{
+ struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
+
+ if (panthor_fw_has_glb_state(ptdev))
+ panthor_fw_update_reqs(glb_iface, req, GLB_STATE(GLB_STATE_HALT), GLB_STATE_MASK);
+ else
+ panthor_fw_update_reqs(glb_iface, req, GLB_HALT, GLB_HALT);
+
+ gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1);
+}
+
+static bool panthor_fw_wait_mcu_halted(struct panthor_device *ptdev)
+{
+ bool halted = false;
+
+ if (read_poll_timeout_atomic(panthor_fw_mcu_halted, halted, halted, 10,
+ MCU_HALT_TIMEOUT_US, 0, ptdev)) {
+ drm_warn(&ptdev->base, "Timed out waiting for MCU to halt");
+ return false;
+ }
+
+ return true;
+}
+
+static void panthor_fw_mcu_set_active(struct panthor_device *ptdev)
+{
+ struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
+
+ if (panthor_fw_has_glb_state(ptdev))
+ panthor_fw_update_reqs(glb_iface, req, GLB_STATE(GLB_STATE_ACTIVE), GLB_STATE_MASK);
+ else
+ panthor_fw_update_reqs(glb_iface, req, 0, GLB_HALT);
+}
+
/**
* panthor_fw_pre_reset() - Call before a reset.
* @ptdev: Device.
@@ -1084,19 +1181,13 @@ void panthor_fw_pre_reset(struct panthor_device *ptdev, bool on_hang)
ptdev->reset.fast = false;
if (!on_hang) {
- struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
- u32 status;
-
- panthor_fw_update_reqs(glb_iface, req, GLB_HALT, GLB_HALT);
- gpu_write(ptdev, CSF_DOORBELL(CSF_GLB_DOORBELL_ID), 1);
- if (!gpu_read_poll_timeout(ptdev, MCU_STATUS, status,
- status == MCU_STATUS_HALT, 10,
- 100000)) {
- ptdev->reset.fast = true;
- } else {
+ panthor_fw_halt_mcu(ptdev);
+ if (!panthor_fw_wait_mcu_halted(ptdev))
drm_warn(&ptdev->base, "Failed to cleanly suspend MCU");
- }
+ else
+ ptdev->reset.fast = true;
}
+ panthor_fw_stop(ptdev);
panthor_job_irq_suspend(&ptdev->fw->irq);
panthor_fw_stop(ptdev);
@@ -1125,14 +1216,14 @@ int panthor_fw_post_reset(struct panthor_device *ptdev)
*/
panthor_reload_fw_sections(ptdev, true);
} else {
- /* The FW detects 0 -> 1 transitions. Make sure we reset
- * the HALT bit before the FW is rebooted.
+ /*
+ * If the FW was previously successfully halted in the pre-reset
+ * operation, we need to transition it to active again before
+ * the FW is rebooted.
* This is not needed on a slow reset because FW sections are
* re-initialized.
*/
- struct panthor_fw_global_iface *glb_iface = panthor_fw_get_glb_iface(ptdev);
-
- panthor_fw_update_reqs(glb_iface, req, 0, GLB_HALT);
+ panthor_fw_mcu_set_active(ptdev);
}
ret = panthor_fw_start(ptdev);
@@ -1163,13 +1254,17 @@ void panthor_fw_unplug(struct panthor_device *ptdev)
{
struct panthor_fw_section *section;
- cancel_delayed_work_sync(&ptdev->fw->watchdog.ping_work);
+ disable_delayed_work_sync(&ptdev->fw->watchdog.ping_work);
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) {
/* Make sure the IRQ handler cannot be called after that point. */
if (ptdev->fw->irq.irq)
panthor_job_irq_suspend(&ptdev->fw->irq);
+ panthor_fw_halt_mcu(ptdev);
+ if (!panthor_fw_wait_mcu_halted(ptdev))
+ drm_warn(&ptdev->base, "Failed to halt MCU on unplug");
+
panthor_fw_stop(ptdev);
}
@@ -1185,7 +1280,7 @@ void panthor_fw_unplug(struct panthor_device *ptdev)
ptdev->fw->vm = NULL;
if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev))
- panthor_gpu_power_off(ptdev, L2, ptdev->gpu_info.l2_present, 20000);
+ panthor_hw_l2_power_off(ptdev);
}
/**
@@ -1364,7 +1459,7 @@ int panthor_fw_init(struct panthor_device *ptdev)
return ret;
}
- ret = panthor_gpu_l2_power_on(ptdev);
+ ret = panthor_hw_l2_power_on(ptdev);
if (ret)
return ret;
@@ -1408,3 +1503,4 @@ MODULE_FIRMWARE("arm/mali/arch10.12/mali_csffw.bin");
MODULE_FIRMWARE("arm/mali/arch11.8/mali_csffw.bin");
MODULE_FIRMWARE("arm/mali/arch12.8/mali_csffw.bin");
MODULE_FIRMWARE("arm/mali/arch13.8/mali_csffw.bin");
+MODULE_FIRMWARE("arm/mali/arch14.8/mali_csffw.bin");
diff --git a/drivers/gpu/drm/panthor/panthor_fw.h b/drivers/gpu/drm/panthor/panthor_fw.h
index 6598d96c6d2a..fbdc21469ba3 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.h
+++ b/drivers/gpu/drm/panthor/panthor_fw.h
@@ -167,10 +167,11 @@ struct panthor_fw_csg_input_iface {
#define CSG_EP_REQ_TILER(x) (((x) << 16) & GENMASK(19, 16))
#define CSG_EP_REQ_EXCL_COMPUTE BIT(20)
#define CSG_EP_REQ_EXCL_FRAGMENT BIT(21)
-#define CSG_EP_REQ_PRIORITY(x) (((x) << 28) & GENMASK(31, 28))
#define CSG_EP_REQ_PRIORITY_MASK GENMASK(31, 28)
+#define CSG_EP_REQ_PRIORITY(x) (((x) << 28) & CSG_EP_REQ_PRIORITY_MASK)
+#define CSG_EP_REQ_PRIORITY_GET(x) (((x) & CSG_EP_REQ_PRIORITY_MASK) >> 28)
u32 endpoint_req;
- u32 reserved2[2];
+ u64 endpoint_req2;
u64 suspend_buf;
u64 protm_suspend_buf;
u32 config;
@@ -214,6 +215,13 @@ struct panthor_fw_global_input_iface {
#define GLB_FWCFG_UPDATE BIT(9)
#define GLB_IDLE_EN BIT(10)
#define GLB_SLEEP BIT(12)
+#define GLB_STATE_MASK GENMASK(14, 12)
+#define GLB_STATE_ACTIVE 0
+#define GLB_STATE_HALT 1
+#define GLB_STATE_SLEEP 2
+#define GLB_STATE_SUSPEND 3
+#define GLB_STATE(x) (((x) << 12) & GLB_STATE_MASK)
+#define GLB_STATE_GET(x) (((x) & GLB_STATE_MASK) >> 12)
#define GLB_INACTIVE_COMPUTE BIT(20)
#define GLB_INACTIVE_FRAGMENT BIT(21)
#define GLB_INACTIVE_TILER BIT(22)
@@ -457,6 +465,16 @@ struct panthor_fw_global_iface {
spin_unlock(&(__iface)->lock); \
} while (0)
+#define panthor_fw_update_reqs64(__iface, __in_reg, __val, __mask) \
+ do { \
+ u64 __cur_val, __new_val; \
+ spin_lock(&(__iface)->lock); \
+ __cur_val = READ_ONCE((__iface)->input->__in_reg); \
+ __new_val = (__cur_val & ~(__mask)) | ((__val) & (__mask)); \
+ WRITE_ONCE((__iface)->input->__in_reg, __new_val); \
+ spin_unlock(&(__iface)->lock); \
+ } while (0)
+
struct panthor_fw_global_iface *
panthor_fw_get_glb_iface(struct panthor_device *ptdev);
@@ -466,6 +484,16 @@ panthor_fw_get_csg_iface(struct panthor_device *ptdev, u32 csg_slot);
struct panthor_fw_cs_iface *
panthor_fw_get_cs_iface(struct panthor_device *ptdev, u32 csg_slot, u32 cs_slot);
+u64 panthor_fw_csg_endpoint_req_get(struct panthor_device *ptdev,
+ struct panthor_fw_csg_iface *csg_iface);
+
+void panthor_fw_csg_endpoint_req_set(struct panthor_device *ptdev,
+ struct panthor_fw_csg_iface *csg_iface, u64 value);
+
+void panthor_fw_csg_endpoint_req_update(struct panthor_device *ptdev,
+ struct panthor_fw_csg_iface *csg_iface, u64 value,
+ u64 mask);
+
int panthor_fw_csg_wait_acks(struct panthor_device *ptdev, u32 csg_id, u32 req_mask,
u32 *acked, u32 timeout_ms);
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index 156c7a0b62a2..fbde78db270a 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/slab.h>
+#include <drm/drm_print.h>
#include <drm/panthor_drm.h>
#include "panthor_device.h"
@@ -86,7 +87,6 @@ static void panthor_gem_free_object(struct drm_gem_object *obj)
void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo)
{
struct panthor_vm *vm;
- int ret;
if (IS_ERR_OR_NULL(bo))
return;
@@ -94,18 +94,11 @@ void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo)
vm = bo->vm;
panthor_kernel_bo_vunmap(bo);
- if (drm_WARN_ON(bo->obj->dev,
- to_panthor_bo(bo->obj)->exclusive_vm_root_gem != panthor_vm_root_gem(vm)))
- goto out_free_bo;
-
- ret = panthor_vm_unmap_range(vm, bo->va_node.start, bo->va_node.size);
- if (ret)
- goto out_free_bo;
-
+ drm_WARN_ON(bo->obj->dev,
+ to_panthor_bo(bo->obj)->exclusive_vm_root_gem != panthor_vm_root_gem(vm));
+ panthor_vm_unmap_range(vm, bo->va_node.start, bo->va_node.size);
panthor_vm_free_va(vm, &bo->va_node);
drm_gem_object_put(bo->obj);
-
-out_free_bo:
panthor_vm_put(vm);
kfree(bo);
}
@@ -152,6 +145,9 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
bo = to_panthor_bo(&obj->base);
kbo->obj = &obj->base;
bo->flags = bo_flags;
+ bo->exclusive_vm_root_gem = panthor_vm_root_gem(vm);
+ drm_gem_object_get(bo->exclusive_vm_root_gem);
+ bo->base.base.resv = bo->exclusive_vm_root_gem->resv;
if (vm == panthor_fw_vm(ptdev))
debug_flags |= PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED;
@@ -175,9 +171,6 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
goto err_free_va;
kbo->vm = panthor_vm_get(vm);
- bo->exclusive_vm_root_gem = panthor_vm_root_gem(vm);
- drm_gem_object_get(bo->exclusive_vm_root_gem);
- bo->base.base.resv = bo->exclusive_vm_root_gem->resv;
return kbo;
err_free_va:
@@ -288,6 +281,23 @@ panthor_gem_create_with_handle(struct drm_file *file,
panthor_gem_debugfs_set_usage_flags(bo, 0);
+ /* If this is a write-combine mapping, we query the sgt to force a CPU
+ * cache flush (dma_map_sgtable() is called when the sgt is created).
+ * This ensures the zero-ing is visible to any uncached mapping created
+ * by vmap/mmap.
+ * FIXME: Ideally this should be done when pages are allocated, not at
+ * BO creation time.
+ */
+ if (shmem->map_wc) {
+ struct sg_table *sgt;
+
+ sgt = drm_gem_shmem_get_pages_sgt(shmem);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto out_put_gem;
+ }
+ }
+
/*
* Allocate an id of idr table where the obj is registered
* and handle has the id what user can see.
@@ -296,6 +306,7 @@ panthor_gem_create_with_handle(struct drm_file *file,
if (!ret)
*size = bo->base.base.size;
+out_put_gem:
/* drop reference from allocate - handle holds it now. */
drm_gem_object_put(&shmem->base);
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index db69449a5be0..06b231b2460a 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -15,9 +15,11 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "panthor_device.h"
#include "panthor_gpu.h"
+#include "panthor_hw.h"
#include "panthor_regs.h"
/**
@@ -52,6 +54,28 @@ static void panthor_gpu_coherency_set(struct panthor_device *ptdev)
ptdev->coherent ? GPU_COHERENCY_PROT_BIT(ACE_LITE) : GPU_COHERENCY_NONE);
}
+static void panthor_gpu_l2_config_set(struct panthor_device *ptdev)
+{
+ const struct panthor_soc_data *data = ptdev->soc_data;
+ u32 l2_config;
+ u32 i;
+
+ if (!data || !data->asn_hash_enable)
+ return;
+
+ if (GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) < 11) {
+ drm_err(&ptdev->base, "Custom ASN hash not supported by the device");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->asn_hash); i++)
+ gpu_write(ptdev, GPU_ASN_HASH(i), data->asn_hash[i]);
+
+ l2_config = gpu_read(ptdev, GPU_L2_CONFIG);
+ l2_config |= GPU_L2_CONFIG_ASN_HASH_ENABLE;
+ gpu_write(ptdev, GPU_L2_CONFIG, l2_config);
+}
+
static void panthor_gpu_irq_handler(struct panthor_device *ptdev, u32 status)
{
gpu_write(ptdev, GPU_INT_CLEAR, status);
@@ -218,6 +242,11 @@ int panthor_gpu_block_power_on(struct panthor_device *ptdev,
return 0;
}
+void panthor_gpu_l2_power_off(struct panthor_device *ptdev)
+{
+ panthor_gpu_power_off(ptdev, L2, ptdev->gpu_info.l2_present, 20000);
+}
+
/**
* panthor_gpu_l2_power_on() - Power-on the L2-cache
* @ptdev: Device.
@@ -241,8 +270,9 @@ int panthor_gpu_l2_power_on(struct panthor_device *ptdev)
hweight64(ptdev->gpu_info.shader_present));
}
- /* Set the desired coherency mode before the power up of L2 */
+ /* Set the desired coherency mode and L2 config before the power up of L2 */
panthor_gpu_coherency_set(ptdev);
+ panthor_gpu_l2_config_set(ptdev);
return panthor_gpu_power_on(ptdev, L2, 1, 20000);
}
@@ -344,9 +374,9 @@ void panthor_gpu_suspend(struct panthor_device *ptdev)
{
/* On a fast reset, simply power down the L2. */
if (!ptdev->reset.fast)
- panthor_gpu_soft_reset(ptdev);
+ panthor_hw_soft_reset(ptdev);
else
- panthor_gpu_power_off(ptdev, L2, 1, 20000);
+ panthor_hw_l2_power_off(ptdev);
panthor_gpu_irq_suspend(&ptdev->gpu->irq);
}
@@ -361,6 +391,6 @@ void panthor_gpu_suspend(struct panthor_device *ptdev)
void panthor_gpu_resume(struct panthor_device *ptdev)
{
panthor_gpu_irq_resume(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK);
- panthor_gpu_l2_power_on(ptdev);
+ panthor_hw_l2_power_on(ptdev);
}
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.h b/drivers/gpu/drm/panthor/panthor_gpu.h
index 7c17a8c06858..12e66f48ced1 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.h
+++ b/drivers/gpu/drm/panthor/panthor_gpu.h
@@ -46,6 +46,7 @@ int panthor_gpu_block_power_off(struct panthor_device *ptdev,
type ## _PWRTRANS, \
mask, timeout_us)
+void panthor_gpu_l2_power_off(struct panthor_device *ptdev);
int panthor_gpu_l2_power_on(struct panthor_device *ptdev);
int panthor_gpu_flush_caches(struct panthor_device *ptdev,
u32 l2, u32 lsc, u32 other);
diff --git a/drivers/gpu/drm/panthor/panthor_heap.c b/drivers/gpu/drm/panthor/panthor_heap.c
index d236e9ceade4..0b6ff4c0a11b 100644
--- a/drivers/gpu/drm/panthor/panthor_heap.c
+++ b/drivers/gpu/drm/panthor/panthor_heap.c
@@ -4,6 +4,7 @@
#include <linux/iosys-map.h>
#include <linux/rwsem.h>
+#include <drm/drm_print.h>
#include <drm/panthor_drm.h>
#include "panthor_device.h"
diff --git a/drivers/gpu/drm/panthor/panthor_hw.c b/drivers/gpu/drm/panthor/panthor_hw.c
index 4f2858114e5e..87ebb7ae42c4 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.c
+++ b/drivers/gpu/drm/panthor/panthor_hw.c
@@ -1,13 +1,58 @@
// SPDX-License-Identifier: GPL-2.0 or MIT
/* Copyright 2025 ARM Limited. All rights reserved. */
+#include <drm/drm_print.h>
+
#include "panthor_device.h"
+#include "panthor_gpu.h"
#include "panthor_hw.h"
+#include "panthor_pwr.h"
#include "panthor_regs.h"
#define GPU_PROD_ID_MAKE(arch_major, prod_major) \
(((arch_major) << 24) | (prod_major))
+/** struct panthor_hw_entry - HW arch major to panthor_hw binding entry */
+struct panthor_hw_entry {
+ /** @arch_min: Minimum supported architecture major value (inclusive) */
+ u8 arch_min;
+
+ /** @arch_max: Maximum supported architecture major value (inclusive) */
+ u8 arch_max;
+
+ /** @hwdev: Pointer to panthor_hw structure */
+ struct panthor_hw *hwdev;
+};
+
+static struct panthor_hw panthor_hw_arch_v10 = {
+ .ops = {
+ .soft_reset = panthor_gpu_soft_reset,
+ .l2_power_off = panthor_gpu_l2_power_off,
+ .l2_power_on = panthor_gpu_l2_power_on,
+ },
+};
+
+static struct panthor_hw panthor_hw_arch_v14 = {
+ .ops = {
+ .soft_reset = panthor_pwr_reset_soft,
+ .l2_power_off = panthor_pwr_l2_power_off,
+ .l2_power_on = panthor_pwr_l2_power_on,
+ },
+};
+
+static struct panthor_hw_entry panthor_hw_match[] = {
+ {
+ .arch_min = 10,
+ .arch_max = 13,
+ .hwdev = &panthor_hw_arch_v10,
+ },
+ {
+ .arch_min = 14,
+ .arch_max = 14,
+ .hwdev = &panthor_hw_arch_v14,
+ },
+};
+
static char *get_gpu_model_name(struct panthor_device *ptdev)
{
const u32 gpu_id = ptdev->gpu_info.gpu_id;
@@ -53,6 +98,12 @@ static char *get_gpu_model_name(struct panthor_device *ptdev)
fallthrough;
case GPU_PROD_ID_MAKE(13, 1):
return "Mali-G625";
+ case GPU_PROD_ID_MAKE(14, 0):
+ return "Mali-G1-Ultra";
+ case GPU_PROD_ID_MAKE(14, 1):
+ return "Mali-G1-Premium";
+ case GPU_PROD_ID_MAKE(14, 3):
+ return "Mali-G1-Pro";
}
return "(Unknown Mali GPU)";
@@ -62,7 +113,6 @@ static void panthor_gpu_info_init(struct panthor_device *ptdev)
{
unsigned int i;
- ptdev->gpu_info.gpu_id = gpu_read(ptdev, GPU_ID);
ptdev->gpu_info.csf_id = gpu_read(ptdev, GPU_CSF_ID);
ptdev->gpu_info.gpu_rev = gpu_read(ptdev, GPU_REVID);
ptdev->gpu_info.core_features = gpu_read(ptdev, GPU_CORE_FEATURES);
@@ -80,12 +130,19 @@ static void panthor_gpu_info_init(struct panthor_device *ptdev)
ptdev->gpu_info.as_present = gpu_read(ptdev, GPU_AS_PRESENT);
- ptdev->gpu_info.shader_present = gpu_read64(ptdev, GPU_SHADER_PRESENT);
- ptdev->gpu_info.tiler_present = gpu_read64(ptdev, GPU_TILER_PRESENT);
- ptdev->gpu_info.l2_present = gpu_read64(ptdev, GPU_L2_PRESENT);
-
/* Introduced in arch 11.x */
ptdev->gpu_info.gpu_features = gpu_read64(ptdev, GPU_FEATURES);
+
+ if (panthor_hw_has_pwr_ctrl(ptdev)) {
+ /* Introduced in arch 14.x */
+ ptdev->gpu_info.l2_present = gpu_read64(ptdev, PWR_L2_PRESENT);
+ ptdev->gpu_info.tiler_present = gpu_read64(ptdev, PWR_TILER_PRESENT);
+ ptdev->gpu_info.shader_present = gpu_read64(ptdev, PWR_SHADER_PRESENT);
+ } else {
+ ptdev->gpu_info.shader_present = gpu_read64(ptdev, GPU_SHADER_PRESENT);
+ ptdev->gpu_info.tiler_present = gpu_read64(ptdev, GPU_TILER_PRESENT);
+ ptdev->gpu_info.l2_present = gpu_read64(ptdev, GPU_L2_PRESENT);
+ }
}
static void panthor_hw_info_init(struct panthor_device *ptdev)
@@ -117,8 +174,50 @@ static void panthor_hw_info_init(struct panthor_device *ptdev)
ptdev->gpu_info.tiler_present);
}
+static int panthor_hw_bind_device(struct panthor_device *ptdev)
+{
+ struct panthor_hw *hdev = NULL;
+ const u32 arch_major = GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id);
+ int i = 0;
+
+ for (i = 0; i < ARRAY_SIZE(panthor_hw_match); i++) {
+ struct panthor_hw_entry *entry = &panthor_hw_match[i];
+
+ if (arch_major >= entry->arch_min && arch_major <= entry->arch_max) {
+ hdev = entry->hwdev;
+ break;
+ }
+ }
+
+ if (!hdev)
+ return -EOPNOTSUPP;
+
+ ptdev->hw = hdev;
+
+ return 0;
+}
+
+static int panthor_hw_gpu_id_init(struct panthor_device *ptdev)
+{
+ ptdev->gpu_info.gpu_id = gpu_read(ptdev, GPU_ID);
+ if (!ptdev->gpu_info.gpu_id)
+ return -ENXIO;
+
+ return 0;
+}
+
int panthor_hw_init(struct panthor_device *ptdev)
{
+ int ret = 0;
+
+ ret = panthor_hw_gpu_id_init(ptdev);
+ if (ret)
+ return ret;
+
+ ret = panthor_hw_bind_device(ptdev);
+ if (ret)
+ return ret;
+
panthor_hw_info_init(ptdev);
return 0;
diff --git a/drivers/gpu/drm/panthor/panthor_hw.h b/drivers/gpu/drm/panthor/panthor_hw.h
index 0af6acc6aa6a..56c68c1e9c26 100644
--- a/drivers/gpu/drm/panthor/panthor_hw.h
+++ b/drivers/gpu/drm/panthor/panthor_hw.h
@@ -4,8 +4,53 @@
#ifndef __PANTHOR_HW_H__
#define __PANTHOR_HW_H__
-struct panthor_device;
+#include "panthor_device.h"
+#include "panthor_regs.h"
+
+/**
+ * struct panthor_hw_ops - HW operations that are specific to a GPU
+ */
+struct panthor_hw_ops {
+ /** @soft_reset: Soft reset function pointer */
+ int (*soft_reset)(struct panthor_device *ptdev);
+
+ /** @l2_power_off: L2 power off function pointer */
+ void (*l2_power_off)(struct panthor_device *ptdev);
+
+ /** @l2_power_on: L2 power on function pointer */
+ int (*l2_power_on)(struct panthor_device *ptdev);
+};
+
+/**
+ * struct panthor_hw - GPU specific register mapping and functions
+ */
+struct panthor_hw {
+ /** @features: Bitmap containing panthor_hw_feature */
+
+ /** @ops: Panthor HW specific operations */
+ struct panthor_hw_ops ops;
+};
int panthor_hw_init(struct panthor_device *ptdev);
+static inline int panthor_hw_soft_reset(struct panthor_device *ptdev)
+{
+ return ptdev->hw->ops.soft_reset(ptdev);
+}
+
+static inline int panthor_hw_l2_power_on(struct panthor_device *ptdev)
+{
+ return ptdev->hw->ops.l2_power_on(ptdev);
+}
+
+static inline void panthor_hw_l2_power_off(struct panthor_device *ptdev)
+{
+ ptdev->hw->ops.l2_power_off(ptdev);
+}
+
+static inline bool panthor_hw_has_pwr_ctrl(struct panthor_device *ptdev)
+{
+ return GPU_ARCH_MAJOR(ptdev->gpu_info.gpu_id) >= 14;
+}
+
#endif /* __PANTHOR_HW_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 6dec4354e378..d4839d282689 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -7,6 +7,7 @@
#include <drm/drm_exec.h>
#include <drm/drm_gpuvm.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
@@ -181,20 +182,6 @@ struct panthor_vm_op_ctx {
u64 range;
} va;
- /**
- * @returned_vmas: List of panthor_vma objects returned after a VM operation.
- *
- * For unmap operations, this will contain all VMAs that were covered by the
- * specified VA range.
- *
- * For map operations, this will contain all VMAs that previously mapped to
- * the specified VA range.
- *
- * Those VMAs, and the resources they point to will be released as part of
- * the op_ctx cleanup operation.
- */
- struct list_head returned_vmas;
-
/** @map: Fields specific to a map operation. */
struct {
/** @map.vm_bo: Buffer object to map. */
@@ -917,10 +904,9 @@ static int panthor_vm_unmap_pages(struct panthor_vm *vm, u64 iova, u64 size)
{
struct panthor_device *ptdev = vm->ptdev;
struct io_pgtable_ops *ops = vm->pgtbl_ops;
+ u64 start_iova = iova;
u64 offset = 0;
- drm_dbg(&ptdev->base, "unmap: as=%d, iova=%llx, len=%llx", vm->as.id, iova, size);
-
while (offset < size) {
size_t unmapped_sz = 0, pgcount;
size_t pgsize = get_pgsize(iova + offset, size - offset, &pgcount);
@@ -935,6 +921,12 @@ static int panthor_vm_unmap_pages(struct panthor_vm *vm, u64 iova, u64 size)
panthor_vm_flush_range(vm, iova, offset + unmapped_sz);
return -EINVAL;
}
+
+ drm_dbg(&ptdev->base,
+ "unmap: as=%d, iova=0x%llx, sz=%llu, va=0x%llx, pgcnt=%zu, pgsz=%zu",
+ vm->as.id, start_iova, size, iova + offset,
+ unmapped_sz / pgsize, pgsize);
+
offset += unmapped_sz;
}
@@ -950,6 +942,7 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot,
struct scatterlist *sgl;
struct io_pgtable_ops *ops = vm->pgtbl_ops;
u64 start_iova = iova;
+ u64 start_size = size;
int ret;
if (!size)
@@ -969,15 +962,18 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot,
len = min_t(size_t, len, size);
size -= len;
- drm_dbg(&ptdev->base, "map: as=%d, iova=%llx, paddr=%pad, len=%zx",
- vm->as.id, iova, &paddr, len);
-
while (len) {
size_t pgcount, mapped = 0;
size_t pgsize = get_pgsize(iova | paddr, len, &pgcount);
ret = ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot,
GFP_KERNEL, &mapped);
+
+ drm_dbg(&ptdev->base,
+ "map: as=%d, iova=0x%llx, sz=%llu, va=0x%llx, pa=%pad, pgcnt=%zu, pgsz=%zu",
+ vm->as.id, start_iova, start_size, iova, &paddr,
+ mapped / pgsize, pgsize);
+
iova += mapped;
paddr += mapped;
len -= mapped;
@@ -1081,47 +1077,18 @@ void panthor_vm_free_va(struct panthor_vm *vm, struct drm_mm_node *va_node)
mutex_unlock(&vm->mm_lock);
}
-static void panthor_vm_bo_put(struct drm_gpuvm_bo *vm_bo)
+static void panthor_vm_bo_free(struct drm_gpuvm_bo *vm_bo)
{
struct panthor_gem_object *bo = to_panthor_bo(vm_bo->obj);
- struct drm_gpuvm *vm = vm_bo->vm;
- bool unpin;
- /* We must retain the GEM before calling drm_gpuvm_bo_put(),
- * otherwise the mutex might be destroyed while we hold it.
- * Same goes for the VM, since we take the VM resv lock.
- */
- drm_gem_object_get(&bo->base.base);
- drm_gpuvm_get(vm);
-
- /* We take the resv lock to protect against concurrent accesses to the
- * gpuvm evicted/extobj lists that are modified in
- * drm_gpuvm_bo_destroy(), which is called if drm_gpuvm_bo_put()
- * releases sthe last vm_bo reference.
- * We take the BO GPUVA list lock to protect the vm_bo removal from the
- * GEM vm_bo list.
- */
- dma_resv_lock(drm_gpuvm_resv(vm), NULL);
- mutex_lock(&bo->base.base.gpuva.lock);
- unpin = drm_gpuvm_bo_put(vm_bo);
- mutex_unlock(&bo->base.base.gpuva.lock);
- dma_resv_unlock(drm_gpuvm_resv(vm));
-
- /* If the vm_bo object was destroyed, release the pin reference that
- * was hold by this object.
- */
- if (unpin && !drm_gem_is_imported(&bo->base.base))
+ if (!drm_gem_is_imported(&bo->base.base))
drm_gem_shmem_unpin(&bo->base);
-
- drm_gpuvm_put(vm);
- drm_gem_object_put(&bo->base.base);
+ kfree(vm_bo);
}
static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx,
struct panthor_vm *vm)
{
- struct panthor_vma *vma, *tmp_vma;
-
u32 remaining_pt_count = op_ctx->rsvd_page_tables.count -
op_ctx->rsvd_page_tables.ptr;
@@ -1134,16 +1101,26 @@ static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx,
kfree(op_ctx->rsvd_page_tables.pages);
if (op_ctx->map.vm_bo)
- panthor_vm_bo_put(op_ctx->map.vm_bo);
+ drm_gpuvm_bo_put_deferred(op_ctx->map.vm_bo);
for (u32 i = 0; i < ARRAY_SIZE(op_ctx->preallocated_vmas); i++)
kfree(op_ctx->preallocated_vmas[i]);
- list_for_each_entry_safe(vma, tmp_vma, &op_ctx->returned_vmas, node) {
- list_del(&vma->node);
- panthor_vm_bo_put(vma->base.vm_bo);
- kfree(vma);
+ drm_gpuvm_bo_deferred_cleanup(&vm->base);
+}
+
+static void
+panthor_vm_op_ctx_return_vma(struct panthor_vm_op_ctx *op_ctx,
+ struct panthor_vma *vma)
+{
+ for (u32 i = 0; i < ARRAY_SIZE(op_ctx->preallocated_vmas); i++) {
+ if (!op_ctx->preallocated_vmas[i]) {
+ op_ctx->preallocated_vmas[i] = vma;
+ return;
+ }
}
+
+ WARN_ON_ONCE(1);
}
static struct panthor_vma *
@@ -1175,10 +1152,14 @@ panthor_vm_op_ctx_prealloc_vmas(struct panthor_vm_op_ctx *op_ctx)
break;
case DRM_PANTHOR_VM_BIND_OP_TYPE_UNMAP:
- /* Partial unmaps might trigger a remap with either a prev or a next VA,
- * but not both.
+ /* Two VMAs can be needed for an unmap, as an unmap can happen
+ * in the middle of a drm_gpuva, requiring a remap with both
+ * prev & next VA. Or an unmap can span more than one drm_gpuva
+ * where the first and last ones are covered partially, requring
+ * a remap for the first with a prev VA and remap for the last
+ * with a next VA.
*/
- vma_count = 1;
+ vma_count = 2;
break;
default:
@@ -1232,7 +1213,6 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
return -EINVAL;
memset(op_ctx, 0, sizeof(*op_ctx));
- INIT_LIST_HEAD(&op_ctx->returned_vmas);
op_ctx->flags = flags;
op_ctx->va.range = size;
op_ctx->va.addr = va;
@@ -1243,7 +1223,9 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
if (!drm_gem_is_imported(&bo->base.base)) {
/* Pre-reserve the BO pages, so the map operation doesn't have to
- * allocate.
+ * allocate. This pin is dropped in panthor_vm_bo_free(), so
+ * once we have successfully called drm_gpuvm_bo_create(),
+ * GPUVM will take care of dropping the pin for us.
*/
ret = drm_gem_shmem_pin(&bo->base);
if (ret)
@@ -1282,16 +1264,6 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
mutex_unlock(&bo->base.base.gpuva.lock);
dma_resv_unlock(panthor_vm_resv(vm));
- /* If the a vm_bo for this <VM,BO> combination exists, it already
- * retains a pin ref, and we can release the one we took earlier.
- *
- * If our pre-allocated vm_bo is picked, it now retains the pin ref,
- * which will be released in panthor_vm_bo_put().
- */
- if (preallocated_vm_bo != op_ctx->map.vm_bo &&
- !drm_gem_is_imported(&bo->base.base))
- drm_gem_shmem_unpin(&bo->base);
-
op_ctx->map.bo_offset = offset;
/* L1, L2 and L3 page tables.
@@ -1339,7 +1311,6 @@ static int panthor_vm_prepare_unmap_op_ctx(struct panthor_vm_op_ctx *op_ctx,
int ret;
memset(op_ctx, 0, sizeof(*op_ctx));
- INIT_LIST_HEAD(&op_ctx->returned_vmas);
op_ctx->va.range = size;
op_ctx->va.addr = va;
op_ctx->flags = DRM_PANTHOR_VM_BIND_OP_TYPE_UNMAP;
@@ -1387,7 +1358,6 @@ static void panthor_vm_prepare_sync_only_op_ctx(struct panthor_vm_op_ctx *op_ctx
struct panthor_vm *vm)
{
memset(op_ctx, 0, sizeof(*op_ctx));
- INIT_LIST_HEAD(&op_ctx->returned_vmas);
op_ctx->flags = DRM_PANTHOR_VM_BIND_OP_TYPE_SYNC_ONLY;
}
@@ -2033,26 +2003,13 @@ static void panthor_vma_link(struct panthor_vm *vm,
mutex_lock(&bo->base.base.gpuva.lock);
drm_gpuva_link(&vma->base, vm_bo);
- drm_WARN_ON(&vm->ptdev->base, drm_gpuvm_bo_put(vm_bo));
mutex_unlock(&bo->base.base.gpuva.lock);
}
-static void panthor_vma_unlink(struct panthor_vm *vm,
- struct panthor_vma *vma)
+static void panthor_vma_unlink(struct panthor_vma *vma)
{
- struct panthor_gem_object *bo = to_panthor_bo(vma->base.gem.obj);
- struct drm_gpuvm_bo *vm_bo = drm_gpuvm_bo_get(vma->base.vm_bo);
-
- mutex_lock(&bo->base.base.gpuva.lock);
- drm_gpuva_unlink(&vma->base);
- mutex_unlock(&bo->base.base.gpuva.lock);
-
- /* drm_gpuva_unlink() release the vm_bo, but we manually retained it
- * when entering this function, so we can implement deferred VMA
- * destruction. Re-assign it here.
- */
- vma->base.vm_bo = vm_bo;
- list_add_tail(&vma->node, &vm->op_ctx->returned_vmas);
+ drm_gpuva_unlink_defer(&vma->base);
+ kfree(vma);
}
static void panthor_vma_init(struct panthor_vma *vma, u32 flags)
@@ -2081,15 +2038,17 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
ret = panthor_vm_map_pages(vm, op->map.va.addr, flags_to_prot(vma->flags),
op_ctx->map.sgt, op->map.gem.offset,
op->map.va.range);
- if (ret)
+ if (ret) {
+ panthor_vm_op_ctx_return_vma(op_ctx, vma);
return ret;
+ }
- /* Ref owned by the mapping now, clear the obj field so we don't release the
- * pinning/obj ref behind GPUVA's back.
- */
drm_gpuva_map(&vm->base, &vma->base, &op->map);
panthor_vma_link(vm, vma, op_ctx->map.vm_bo);
+
+ drm_gpuvm_bo_put_deferred(op_ctx->map.vm_bo);
op_ctx->map.vm_bo = NULL;
+
return 0;
}
@@ -2128,16 +2087,14 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
* owned by the old mapping which will be released when this
* mapping is destroyed, we need to grab a ref here.
*/
- panthor_vma_link(vm, prev_vma,
- drm_gpuvm_bo_get(op->remap.unmap->va->vm_bo));
+ panthor_vma_link(vm, prev_vma, op->remap.unmap->va->vm_bo);
}
if (next_vma) {
- panthor_vma_link(vm, next_vma,
- drm_gpuvm_bo_get(op->remap.unmap->va->vm_bo));
+ panthor_vma_link(vm, next_vma, op->remap.unmap->va->vm_bo);
}
- panthor_vma_unlink(vm, unmap_vma);
+ panthor_vma_unlink(unmap_vma);
return 0;
}
@@ -2154,12 +2111,13 @@ static int panthor_gpuva_sm_step_unmap(struct drm_gpuva_op *op,
return ret;
drm_gpuva_unmap(&op->unmap);
- panthor_vma_unlink(vm, unmap_vma);
+ panthor_vma_unlink(unmap_vma);
return 0;
}
static const struct drm_gpuvm_ops panthor_gpuvm_ops = {
.vm_free = panthor_vm_free,
+ .vm_bo_free = panthor_vm_bo_free,
.sm_step_map = panthor_gpuva_sm_step_map,
.sm_step_remap = panthor_gpuva_sm_step_remap,
.sm_step_unmap = panthor_gpuva_sm_step_unmap,
diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c
new file mode 100644
index 000000000000..57cfc7ce715b
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_pwr.c
@@ -0,0 +1,549 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+/* Copyright 2025 ARM Limited. All rights reserved. */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/cleanup.h>
+#include <linux/iopoll.h>
+#include <linux/wait.h>
+
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+
+#include "panthor_device.h"
+#include "panthor_hw.h"
+#include "panthor_pwr.h"
+#include "panthor_regs.h"
+
+#define PWR_INTERRUPTS_MASK \
+ (PWR_IRQ_POWER_CHANGED_SINGLE | \
+ PWR_IRQ_POWER_CHANGED_ALL | \
+ PWR_IRQ_DELEGATION_CHANGED | \
+ PWR_IRQ_RESET_COMPLETED | \
+ PWR_IRQ_RETRACT_COMPLETED | \
+ PWR_IRQ_INSPECT_COMPLETED | \
+ PWR_IRQ_COMMAND_NOT_ALLOWED | \
+ PWR_IRQ_COMMAND_INVALID)
+
+#define PWR_ALL_CORES_MASK GENMASK_U64(63, 0)
+
+#define PWR_DOMAIN_MAX_BITS 16
+
+#define PWR_TRANSITION_TIMEOUT_US (2ULL * USEC_PER_SEC)
+
+#define PWR_RETRACT_TIMEOUT_US (2ULL * USEC_PER_MSEC)
+
+#define PWR_RESET_TIMEOUT_MS 500
+
+/**
+ * struct panthor_pwr - PWR_CONTROL block management data.
+ */
+struct panthor_pwr {
+ /** @irq: PWR irq. */
+ struct panthor_irq irq;
+
+ /** @reqs_lock: Lock protecting access to pending_reqs. */
+ spinlock_t reqs_lock;
+
+ /** @pending_reqs: Pending PWR requests. */
+ u32 pending_reqs;
+
+ /** @reqs_acked: PWR request wait queue. */
+ wait_queue_head_t reqs_acked;
+};
+
+static void panthor_pwr_irq_handler(struct panthor_device *ptdev, u32 status)
+{
+ spin_lock(&ptdev->pwr->reqs_lock);
+ gpu_write(ptdev, PWR_INT_CLEAR, status);
+
+ if (unlikely(status & PWR_IRQ_COMMAND_NOT_ALLOWED))
+ drm_err(&ptdev->base, "PWR_IRQ: COMMAND_NOT_ALLOWED");
+
+ if (unlikely(status & PWR_IRQ_COMMAND_INVALID))
+ drm_err(&ptdev->base, "PWR_IRQ: COMMAND_INVALID");
+
+ if (status & ptdev->pwr->pending_reqs) {
+ ptdev->pwr->pending_reqs &= ~status;
+ wake_up_all(&ptdev->pwr->reqs_acked);
+ }
+ spin_unlock(&ptdev->pwr->reqs_lock);
+}
+PANTHOR_IRQ_HANDLER(pwr, PWR, panthor_pwr_irq_handler);
+
+static void panthor_pwr_write_command(struct panthor_device *ptdev, u32 command, u64 args)
+{
+ if (args)
+ gpu_write64(ptdev, PWR_CMDARG, args);
+
+ gpu_write(ptdev, PWR_COMMAND, command);
+}
+
+static bool reset_irq_raised(struct panthor_device *ptdev)
+{
+ return gpu_read(ptdev, PWR_INT_RAWSTAT) & PWR_IRQ_RESET_COMPLETED;
+}
+
+static bool reset_pending(struct panthor_device *ptdev)
+{
+ return (ptdev->pwr->pending_reqs & PWR_IRQ_RESET_COMPLETED);
+}
+
+static int panthor_pwr_reset(struct panthor_device *ptdev, u32 reset_cmd)
+{
+ scoped_guard(spinlock_irqsave, &ptdev->pwr->reqs_lock) {
+ if (reset_pending(ptdev)) {
+ drm_WARN(&ptdev->base, 1, "Reset already pending");
+ } else {
+ ptdev->pwr->pending_reqs |= PWR_IRQ_RESET_COMPLETED;
+ gpu_write(ptdev, PWR_INT_CLEAR, PWR_IRQ_RESET_COMPLETED);
+ panthor_pwr_write_command(ptdev, reset_cmd, 0);
+ }
+ }
+
+ if (!wait_event_timeout(ptdev->pwr->reqs_acked, !reset_pending(ptdev),
+ msecs_to_jiffies(PWR_RESET_TIMEOUT_MS))) {
+ guard(spinlock_irqsave)(&ptdev->pwr->reqs_lock);
+
+ if (reset_pending(ptdev) && !reset_irq_raised(ptdev)) {
+ drm_err(&ptdev->base, "RESET timed out (0x%x)", reset_cmd);
+ return -ETIMEDOUT;
+ }
+
+ ptdev->pwr->pending_reqs &= ~PWR_IRQ_RESET_COMPLETED;
+ }
+
+ return 0;
+}
+
+static const char *get_domain_name(u8 domain)
+{
+ switch (domain) {
+ case PWR_COMMAND_DOMAIN_L2:
+ return "L2";
+ case PWR_COMMAND_DOMAIN_TILER:
+ return "Tiler";
+ case PWR_COMMAND_DOMAIN_SHADER:
+ return "Shader";
+ case PWR_COMMAND_DOMAIN_BASE:
+ return "Base";
+ case PWR_COMMAND_DOMAIN_STACK:
+ return "Stack";
+ }
+ return "Unknown";
+}
+
+static u32 get_domain_base(u8 domain)
+{
+ switch (domain) {
+ case PWR_COMMAND_DOMAIN_L2:
+ return PWR_L2_PRESENT;
+ case PWR_COMMAND_DOMAIN_TILER:
+ return PWR_TILER_PRESENT;
+ case PWR_COMMAND_DOMAIN_SHADER:
+ return PWR_SHADER_PRESENT;
+ case PWR_COMMAND_DOMAIN_BASE:
+ return PWR_BASE_PRESENT;
+ case PWR_COMMAND_DOMAIN_STACK:
+ return PWR_STACK_PRESENT;
+ }
+ return 0;
+}
+
+static u32 get_domain_ready_reg(u32 domain)
+{
+ return get_domain_base(domain) + (PWR_L2_READY - PWR_L2_PRESENT);
+}
+
+static u32 get_domain_pwrtrans_reg(u32 domain)
+{
+ return get_domain_base(domain) + (PWR_L2_PWRTRANS - PWR_L2_PRESENT);
+}
+
+static bool is_valid_domain(u32 domain)
+{
+ return get_domain_base(domain) != 0;
+}
+
+static bool has_rtu(struct panthor_device *ptdev)
+{
+ return ptdev->gpu_info.gpu_features & GPU_FEATURES_RAY_TRAVERSAL;
+}
+
+static u8 get_domain_subdomain(struct panthor_device *ptdev, u32 domain)
+{
+ if (domain == PWR_COMMAND_DOMAIN_SHADER && has_rtu(ptdev))
+ return PWR_COMMAND_SUBDOMAIN_RTU;
+
+ return 0;
+}
+
+static int panthor_pwr_domain_wait_transition(struct panthor_device *ptdev, u32 domain,
+ u32 timeout_us)
+{
+ u32 pwrtrans_reg = get_domain_pwrtrans_reg(domain);
+ u64 val;
+ int ret = 0;
+
+ ret = gpu_read64_poll_timeout(ptdev, pwrtrans_reg, val, !(PWR_ALL_CORES_MASK & val), 100,
+ timeout_us);
+ if (ret) {
+ drm_err(&ptdev->base, "%s domain power in transition, pwrtrans(0x%llx)",
+ get_domain_name(domain), val);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void panthor_pwr_debug_info_show(struct panthor_device *ptdev)
+{
+ drm_info(&ptdev->base, "GPU_FEATURES: 0x%016llx", gpu_read64(ptdev, GPU_FEATURES));
+ drm_info(&ptdev->base, "PWR_STATUS: 0x%016llx", gpu_read64(ptdev, PWR_STATUS));
+ drm_info(&ptdev->base, "L2_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_L2_PRESENT));
+ drm_info(&ptdev->base, "L2_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_L2_PWRTRANS));
+ drm_info(&ptdev->base, "L2_READY: 0x%016llx", gpu_read64(ptdev, PWR_L2_READY));
+ drm_info(&ptdev->base, "TILER_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_TILER_PRESENT));
+ drm_info(&ptdev->base, "TILER_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_TILER_PWRTRANS));
+ drm_info(&ptdev->base, "TILER_READY: 0x%016llx", gpu_read64(ptdev, PWR_TILER_READY));
+ drm_info(&ptdev->base, "SHADER_PRESENT: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_PRESENT));
+ drm_info(&ptdev->base, "SHADER_PWRTRANS: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_PWRTRANS));
+ drm_info(&ptdev->base, "SHADER_READY: 0x%016llx", gpu_read64(ptdev, PWR_SHADER_READY));
+}
+
+static int panthor_pwr_domain_transition(struct panthor_device *ptdev, u32 cmd, u32 domain,
+ u64 mask, u32 timeout_us)
+{
+ u32 ready_reg = get_domain_ready_reg(domain);
+ u32 pwr_cmd = PWR_COMMAND_DEF(cmd, domain, get_domain_subdomain(ptdev, domain));
+ u64 expected_val = 0;
+ u64 val;
+ int ret = 0;
+
+ if (drm_WARN_ON(&ptdev->base, !is_valid_domain(domain)))
+ return -EINVAL;
+
+ switch (cmd) {
+ case PWR_COMMAND_POWER_DOWN:
+ expected_val = 0;
+ break;
+ case PWR_COMMAND_POWER_UP:
+ expected_val = mask;
+ break;
+ default:
+ drm_err(&ptdev->base, "Invalid power domain transition command (0x%x)", cmd);
+ return -EINVAL;
+ }
+
+ ret = panthor_pwr_domain_wait_transition(ptdev, domain, timeout_us);
+ if (ret)
+ return ret;
+
+ /* domain already in target state, return early */
+ if ((gpu_read64(ptdev, ready_reg) & mask) == expected_val)
+ return 0;
+
+ panthor_pwr_write_command(ptdev, pwr_cmd, mask);
+
+ ret = gpu_read64_poll_timeout(ptdev, ready_reg, val, (mask & val) == expected_val, 100,
+ timeout_us);
+ if (ret) {
+ drm_err(&ptdev->base,
+ "timeout waiting on %s power domain transition, cmd(0x%x), arg(0x%llx)",
+ get_domain_name(domain), pwr_cmd, mask);
+ panthor_pwr_debug_info_show(ptdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define panthor_pwr_domain_power_off(__ptdev, __domain, __mask, __timeout_us) \
+ panthor_pwr_domain_transition(__ptdev, PWR_COMMAND_POWER_DOWN, __domain, __mask, \
+ __timeout_us)
+
+#define panthor_pwr_domain_power_on(__ptdev, __domain, __mask, __timeout_us) \
+ panthor_pwr_domain_transition(__ptdev, PWR_COMMAND_POWER_UP, __domain, __mask, __timeout_us)
+
+/**
+ * retract_domain() - Retract control of a domain from MCU
+ * @ptdev: Device.
+ * @domain: Domain to retract the control
+ *
+ * Retracting L2 domain is not expected since it won't be delegated.
+ *
+ * Return: 0 on success or retracted already.
+ * -EPERM if domain is L2.
+ * A negative error code otherwise.
+ */
+static int retract_domain(struct panthor_device *ptdev, u32 domain)
+{
+ const u32 pwr_cmd = PWR_COMMAND_DEF(PWR_COMMAND_RETRACT, domain, 0);
+ const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS);
+ const u64 delegated_mask = PWR_STATUS_DOMAIN_DELEGATED(domain);
+ const u64 allow_mask = PWR_STATUS_DOMAIN_ALLOWED(domain);
+ u64 val;
+ int ret;
+
+ if (drm_WARN_ON(&ptdev->base, domain == PWR_COMMAND_DOMAIN_L2))
+ return -EPERM;
+
+ ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val, !(PWR_STATUS_RETRACT_PENDING & val),
+ 0, PWR_RETRACT_TIMEOUT_US);
+ if (ret) {
+ drm_err(&ptdev->base, "%s domain retract pending", get_domain_name(domain));
+ return ret;
+ }
+
+ if (!(pwr_status & delegated_mask)) {
+ drm_dbg(&ptdev->base, "%s domain already retracted", get_domain_name(domain));
+ return 0;
+ }
+
+ panthor_pwr_write_command(ptdev, pwr_cmd, 0);
+
+ /*
+ * On successful retraction
+ * allow-flag will be set with delegated-flag being cleared.
+ */
+ ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val,
+ ((delegated_mask | allow_mask) & val) == allow_mask, 10,
+ PWR_TRANSITION_TIMEOUT_US);
+ if (ret) {
+ drm_err(&ptdev->base, "Retracting %s domain timeout, cmd(0x%x)",
+ get_domain_name(domain), pwr_cmd);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * delegate_domain() - Delegate control of a domain to MCU
+ * @ptdev: Device.
+ * @domain: Domain to delegate the control
+ *
+ * Delegating L2 domain is prohibited.
+ *
+ * Return:
+ * * 0 on success or delegated already.
+ * * -EPERM if domain is L2.
+ * * A negative error code otherwise.
+ */
+static int delegate_domain(struct panthor_device *ptdev, u32 domain)
+{
+ const u32 pwr_cmd = PWR_COMMAND_DEF(PWR_COMMAND_DELEGATE, domain, 0);
+ const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS);
+ const u64 allow_mask = PWR_STATUS_DOMAIN_ALLOWED(domain);
+ const u64 delegated_mask = PWR_STATUS_DOMAIN_DELEGATED(domain);
+ u64 val;
+ int ret;
+
+ if (drm_WARN_ON(&ptdev->base, domain == PWR_COMMAND_DOMAIN_L2))
+ return -EPERM;
+
+ /* Already delegated, exit early */
+ if (pwr_status & delegated_mask)
+ return 0;
+
+ /* Check if the command is allowed before delegating. */
+ if (!(pwr_status & allow_mask)) {
+ drm_warn(&ptdev->base, "Delegating %s domain not allowed", get_domain_name(domain));
+ return -EPERM;
+ }
+
+ ret = panthor_pwr_domain_wait_transition(ptdev, domain, PWR_TRANSITION_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ panthor_pwr_write_command(ptdev, pwr_cmd, 0);
+
+ /*
+ * On successful delegation
+ * allow-flag will be cleared with delegated-flag being set.
+ */
+ ret = gpu_read64_poll_timeout(ptdev, PWR_STATUS, val,
+ ((delegated_mask | allow_mask) & val) == delegated_mask,
+ 10, PWR_TRANSITION_TIMEOUT_US);
+ if (ret) {
+ drm_err(&ptdev->base, "Delegating %s domain timeout, cmd(0x%x)",
+ get_domain_name(domain), pwr_cmd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int panthor_pwr_delegate_domains(struct panthor_device *ptdev)
+{
+ int ret;
+
+ if (!ptdev->pwr)
+ return 0;
+
+ ret = delegate_domain(ptdev, PWR_COMMAND_DOMAIN_SHADER);
+ if (ret)
+ return ret;
+
+ ret = delegate_domain(ptdev, PWR_COMMAND_DOMAIN_TILER);
+ if (ret)
+ goto err_retract_shader;
+
+ return 0;
+
+err_retract_shader:
+ retract_domain(ptdev, PWR_COMMAND_DOMAIN_SHADER);
+
+ return ret;
+}
+
+/**
+ * panthor_pwr_domain_force_off - Forcefully power down a domain.
+ * @ptdev: Device.
+ * @domain: Domain to forcefully power down.
+ *
+ * This function will attempt to retract and power off the requested power
+ * domain. However, if retraction fails, the operation is aborted. If power off
+ * fails, the domain will remain retracted and under the host control.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static int panthor_pwr_domain_force_off(struct panthor_device *ptdev, u32 domain)
+{
+ const u64 domain_ready = gpu_read64(ptdev, get_domain_ready_reg(domain));
+ int ret;
+
+ /* Domain already powered down, early exit. */
+ if (!domain_ready)
+ return 0;
+
+ /* Domain has to be in host control to issue power off command. */
+ ret = retract_domain(ptdev, domain);
+ if (ret)
+ return ret;
+
+ return panthor_pwr_domain_power_off(ptdev, domain, domain_ready, PWR_TRANSITION_TIMEOUT_US);
+}
+
+void panthor_pwr_unplug(struct panthor_device *ptdev)
+{
+ unsigned long flags;
+
+ if (!ptdev->pwr)
+ return;
+
+ /* Make sure the IRQ handler is not running after that point. */
+ panthor_pwr_irq_suspend(&ptdev->pwr->irq);
+
+ /* Wake-up all waiters. */
+ spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags);
+ ptdev->pwr->pending_reqs = 0;
+ wake_up_all(&ptdev->pwr->reqs_acked);
+ spin_unlock_irqrestore(&ptdev->pwr->reqs_lock, flags);
+}
+
+int panthor_pwr_init(struct panthor_device *ptdev)
+{
+ struct panthor_pwr *pwr;
+ int err, irq;
+
+ if (!panthor_hw_has_pwr_ctrl(ptdev))
+ return 0;
+
+ pwr = drmm_kzalloc(&ptdev->base, sizeof(*pwr), GFP_KERNEL);
+ if (!pwr)
+ return -ENOMEM;
+
+ spin_lock_init(&pwr->reqs_lock);
+ init_waitqueue_head(&pwr->reqs_acked);
+ ptdev->pwr = pwr;
+
+ irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "gpu");
+ if (irq < 0)
+ return irq;
+
+ err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int panthor_pwr_reset_soft(struct panthor_device *ptdev)
+{
+ if (!(gpu_read64(ptdev, PWR_STATUS) & PWR_STATUS_ALLOW_SOFT_RESET)) {
+ drm_err(&ptdev->base, "RESET_SOFT not allowed");
+ return -EOPNOTSUPP;
+ }
+
+ return panthor_pwr_reset(ptdev, PWR_COMMAND_RESET_SOFT);
+}
+
+void panthor_pwr_l2_power_off(struct panthor_device *ptdev)
+{
+ const u64 l2_allow_mask = PWR_STATUS_DOMAIN_ALLOWED(PWR_COMMAND_DOMAIN_L2);
+ const u64 pwr_status = gpu_read64(ptdev, PWR_STATUS);
+
+ /* Abort if L2 power off constraints are not satisfied */
+ if (!(pwr_status & l2_allow_mask)) {
+ drm_warn(&ptdev->base, "Power off L2 domain not allowed");
+ return;
+ }
+
+ /* It is expected that when halting the MCU, it would power down its
+ * delegated domains. However, an unresponsive or hung MCU may not do
+ * so, which is why we need to check and retract the domains back into
+ * host control to be powered down in the right order before powering
+ * down the L2.
+ */
+ if (panthor_pwr_domain_force_off(ptdev, PWR_COMMAND_DOMAIN_TILER))
+ return;
+
+ if (panthor_pwr_domain_force_off(ptdev, PWR_COMMAND_DOMAIN_SHADER))
+ return;
+
+ panthor_pwr_domain_power_off(ptdev, PWR_COMMAND_DOMAIN_L2, ptdev->gpu_info.l2_present,
+ PWR_TRANSITION_TIMEOUT_US);
+}
+
+int panthor_pwr_l2_power_on(struct panthor_device *ptdev)
+{
+ const u32 pwr_status = gpu_read64(ptdev, PWR_STATUS);
+ const u32 l2_allow_mask = PWR_STATUS_DOMAIN_ALLOWED(PWR_COMMAND_DOMAIN_L2);
+ int ret;
+
+ if ((pwr_status & l2_allow_mask) == 0) {
+ drm_warn(&ptdev->base, "Power on L2 domain not allowed");
+ return -EPERM;
+ }
+
+ ret = panthor_pwr_domain_power_on(ptdev, PWR_COMMAND_DOMAIN_L2, ptdev->gpu_info.l2_present,
+ PWR_TRANSITION_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ /* Delegate control of the shader and tiler power domains to the MCU as
+ * it can better manage which shader/tiler cores need to be powered up
+ * or can be powered down based on currently running jobs.
+ *
+ * If the shader and tiler domains are already delegated to the MCU,
+ * this call would just return early.
+ */
+ return panthor_pwr_delegate_domains(ptdev);
+}
+
+void panthor_pwr_suspend(struct panthor_device *ptdev)
+{
+ if (!ptdev->pwr)
+ return;
+
+ panthor_pwr_irq_suspend(&ptdev->pwr->irq);
+}
+
+void panthor_pwr_resume(struct panthor_device *ptdev)
+{
+ if (!ptdev->pwr)
+ return;
+
+ panthor_pwr_irq_resume(&ptdev->pwr->irq, PWR_INTERRUPTS_MASK);
+}
diff --git a/drivers/gpu/drm/panthor/panthor_pwr.h b/drivers/gpu/drm/panthor/panthor_pwr.h
new file mode 100644
index 000000000000..adf1f6136abc
--- /dev/null
+++ b/drivers/gpu/drm/panthor/panthor_pwr.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+/* Copyright 2025 ARM Limited. All rights reserved. */
+
+#ifndef __PANTHOR_PWR_H__
+#define __PANTHOR_PWR_H__
+
+struct panthor_device;
+
+void panthor_pwr_unplug(struct panthor_device *ptdev);
+
+int panthor_pwr_init(struct panthor_device *ptdev);
+
+int panthor_pwr_reset_soft(struct panthor_device *ptdev);
+
+void panthor_pwr_l2_power_off(struct panthor_device *ptdev);
+
+int panthor_pwr_l2_power_on(struct panthor_device *ptdev);
+
+void panthor_pwr_suspend(struct panthor_device *ptdev);
+
+void panthor_pwr_resume(struct panthor_device *ptdev);
+
+#endif /* __PANTHOR_PWR_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_regs.h b/drivers/gpu/drm/panthor/panthor_regs.h
index 8bee76d01bf8..08bf06c452d6 100644
--- a/drivers/gpu/drm/panthor/panthor_regs.h
+++ b/drivers/gpu/drm/panthor/panthor_regs.h
@@ -64,6 +64,8 @@
#define GPU_FAULT_STATUS 0x3C
#define GPU_FAULT_ADDR 0x40
+#define GPU_L2_CONFIG 0x48
+#define GPU_L2_CONFIG_ASN_HASH_ENABLE BIT(24)
#define GPU_PWR_KEY 0x50
#define GPU_PWR_KEY_UNLOCK 0x2968A819
@@ -72,6 +74,7 @@
#define GPU_FEATURES 0x60
#define GPU_FEATURES_RAY_INTERSECTION BIT(2)
+#define GPU_FEATURES_RAY_TRAVERSAL BIT(5)
#define GPU_TIMESTAMP_OFFSET 0x88
#define GPU_CYCLE_COUNT 0x90
@@ -110,6 +113,8 @@
#define GPU_REVID 0x280
+#define GPU_ASN_HASH(n) (0x2C0 + ((n) * 4))
+
#define GPU_COHERENCY_FEATURES 0x300
#define GPU_COHERENCY_PROT_BIT(name) BIT(GPU_COHERENCY_ ## name)
@@ -205,4 +210,82 @@
#define CSF_DOORBELL(i) (0x80000 + ((i) * 0x10000))
#define CSF_GLB_DOORBELL_ID 0
+/* PWR Control registers */
+
+#define PWR_CONTROL_BASE 0x800
+#define PWR_CTRL_REG(x) (PWR_CONTROL_BASE + (x))
+
+#define PWR_INT_RAWSTAT PWR_CTRL_REG(0x0)
+#define PWR_INT_CLEAR PWR_CTRL_REG(0x4)
+#define PWR_INT_MASK PWR_CTRL_REG(0x8)
+#define PWR_INT_STAT PWR_CTRL_REG(0xc)
+#define PWR_IRQ_POWER_CHANGED_SINGLE BIT(0)
+#define PWR_IRQ_POWER_CHANGED_ALL BIT(1)
+#define PWR_IRQ_DELEGATION_CHANGED BIT(2)
+#define PWR_IRQ_RESET_COMPLETED BIT(3)
+#define PWR_IRQ_RETRACT_COMPLETED BIT(4)
+#define PWR_IRQ_INSPECT_COMPLETED BIT(5)
+#define PWR_IRQ_COMMAND_NOT_ALLOWED BIT(30)
+#define PWR_IRQ_COMMAND_INVALID BIT(31)
+
+#define PWR_STATUS PWR_CTRL_REG(0x20)
+#define PWR_STATUS_ALLOW_L2 BIT_U64(0)
+#define PWR_STATUS_ALLOW_TILER BIT_U64(1)
+#define PWR_STATUS_ALLOW_SHADER BIT_U64(8)
+#define PWR_STATUS_ALLOW_BASE BIT_U64(14)
+#define PWR_STATUS_ALLOW_STACK BIT_U64(15)
+#define PWR_STATUS_DOMAIN_ALLOWED(x) BIT_U64(x)
+#define PWR_STATUS_DELEGATED_L2 BIT_U64(16)
+#define PWR_STATUS_DELEGATED_TILER BIT_U64(17)
+#define PWR_STATUS_DELEGATED_SHADER BIT_U64(24)
+#define PWR_STATUS_DELEGATED_BASE BIT_U64(30)
+#define PWR_STATUS_DELEGATED_STACK BIT_U64(31)
+#define PWR_STATUS_DELEGATED_SHIFT 16
+#define PWR_STATUS_DOMAIN_DELEGATED(x) BIT_U64((x) + PWR_STATUS_DELEGATED_SHIFT)
+#define PWR_STATUS_ALLOW_SOFT_RESET BIT_U64(33)
+#define PWR_STATUS_ALLOW_FAST_RESET BIT_U64(34)
+#define PWR_STATUS_POWER_PENDING BIT_U64(41)
+#define PWR_STATUS_RESET_PENDING BIT_U64(42)
+#define PWR_STATUS_RETRACT_PENDING BIT_U64(43)
+#define PWR_STATUS_INSPECT_PENDING BIT_U64(44)
+
+#define PWR_COMMAND PWR_CTRL_REG(0x28)
+#define PWR_COMMAND_POWER_UP 0x10
+#define PWR_COMMAND_POWER_DOWN 0x11
+#define PWR_COMMAND_DELEGATE 0x20
+#define PWR_COMMAND_RETRACT 0x21
+#define PWR_COMMAND_RESET_SOFT 0x31
+#define PWR_COMMAND_RESET_FAST 0x32
+#define PWR_COMMAND_INSPECT 0xF0
+#define PWR_COMMAND_DOMAIN_L2 0
+#define PWR_COMMAND_DOMAIN_TILER 1
+#define PWR_COMMAND_DOMAIN_SHADER 8
+#define PWR_COMMAND_DOMAIN_BASE 14
+#define PWR_COMMAND_DOMAIN_STACK 15
+#define PWR_COMMAND_SUBDOMAIN_RTU BIT(0)
+#define PWR_COMMAND_DEF(cmd, domain, subdomain) \
+ (((subdomain) << 16) | ((domain) << 8) | (cmd))
+
+#define PWR_CMDARG PWR_CTRL_REG(0x30)
+
+#define PWR_L2_PRESENT PWR_CTRL_REG(0x100)
+#define PWR_L2_READY PWR_CTRL_REG(0x108)
+#define PWR_L2_PWRTRANS PWR_CTRL_REG(0x110)
+#define PWR_L2_PWRACTIVE PWR_CTRL_REG(0x118)
+#define PWR_TILER_PRESENT PWR_CTRL_REG(0x140)
+#define PWR_TILER_READY PWR_CTRL_REG(0x148)
+#define PWR_TILER_PWRTRANS PWR_CTRL_REG(0x150)
+#define PWR_TILER_PWRACTIVE PWR_CTRL_REG(0x158)
+#define PWR_SHADER_PRESENT PWR_CTRL_REG(0x200)
+#define PWR_SHADER_READY PWR_CTRL_REG(0x208)
+#define PWR_SHADER_PWRTRANS PWR_CTRL_REG(0x210)
+#define PWR_SHADER_PWRACTIVE PWR_CTRL_REG(0x218)
+#define PWR_BASE_PRESENT PWR_CTRL_REG(0x380)
+#define PWR_BASE_READY PWR_CTRL_REG(0x388)
+#define PWR_BASE_PWRTRANS PWR_CTRL_REG(0x390)
+#define PWR_BASE_PWRACTIVE PWR_CTRL_REG(0x398)
+#define PWR_STACK_PRESENT PWR_CTRL_REG(0x3c0)
+#define PWR_STACK_READY PWR_CTRL_REG(0x3c8)
+#define PWR_STACK_PWRTRANS PWR_CTRL_REG(0x3d0)
+
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 3d1f57e3990f..b834123a6560 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -5,6 +5,7 @@
#include <drm/drm_exec.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
@@ -360,17 +361,23 @@ struct panthor_queue {
/** @entity: DRM scheduling entity used for this queue. */
struct drm_sched_entity entity;
- /**
- * @remaining_time: Time remaining before the job timeout expires.
- *
- * The job timeout is suspended when the queue is not scheduled by the
- * FW. Every time we suspend the timer, we need to save the remaining
- * time so we can restore it later on.
- */
- unsigned long remaining_time;
+ /** @name: DRM scheduler name for this queue. */
+ char *name;
- /** @timeout_suspended: True if the job timeout was suspended. */
- bool timeout_suspended;
+ /** @timeout: Queue timeout related fields. */
+ struct {
+ /** @timeout.work: Work executed when a queue timeout occurs. */
+ struct delayed_work work;
+
+ /**
+ * @timeout.remaining: Time remaining before a queue timeout.
+ *
+ * When the timer is running, this value is set to MAX_SCHEDULE_TIMEOUT.
+ * When the timer is suspended, it's set to the time remaining when the
+ * timer was suspended.
+ */
+ unsigned long remaining;
+ } timeout;
/**
* @doorbell_id: Doorbell assigned to this queue.
@@ -895,11 +902,18 @@ static void group_free_queue(struct panthor_group *group, struct panthor_queue *
if (IS_ERR_OR_NULL(queue))
return;
- drm_sched_entity_destroy(&queue->entity);
+ /* This should have been disabled before that point. */
+ drm_WARN_ON(&group->ptdev->base,
+ disable_delayed_work_sync(&queue->timeout.work));
+
+ if (queue->entity.fence_context)
+ drm_sched_entity_destroy(&queue->entity);
if (queue->scheduler.ops)
drm_sched_fini(&queue->scheduler);
+ kfree(queue->name);
+
panthor_queue_put_syncwait_obj(queue);
panthor_kernel_bo_destroy(queue->ringbuf);
@@ -1039,6 +1053,115 @@ group_unbind_locked(struct panthor_group *group)
return 0;
}
+static bool
+group_is_idle(struct panthor_group *group)
+{
+ struct panthor_device *ptdev = group->ptdev;
+ u32 inactive_queues;
+
+ if (group->csg_id >= 0)
+ return ptdev->scheduler->csg_slots[group->csg_id].idle;
+
+ inactive_queues = group->idle_queues | group->blocked_queues;
+ return hweight32(inactive_queues) == group->queue_count;
+}
+
+static void
+queue_reset_timeout_locked(struct panthor_queue *queue)
+{
+ lockdep_assert_held(&queue->fence_ctx.lock);
+
+ if (queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT) {
+ mod_delayed_work(queue->scheduler.timeout_wq,
+ &queue->timeout.work,
+ msecs_to_jiffies(JOB_TIMEOUT_MS));
+ }
+}
+
+static bool
+group_can_run(struct panthor_group *group)
+{
+ return group->state != PANTHOR_CS_GROUP_TERMINATED &&
+ group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
+ !group->destroyed && group->fatal_queues == 0 &&
+ !group->timedout;
+}
+
+static bool
+queue_timeout_is_suspended(struct panthor_queue *queue)
+{
+ /* When running, the remaining time is set to MAX_SCHEDULE_TIMEOUT. */
+ return queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT;
+}
+
+static void
+queue_suspend_timeout_locked(struct panthor_queue *queue)
+{
+ unsigned long qtimeout, now;
+ struct panthor_group *group;
+ struct panthor_job *job;
+ bool timer_was_active;
+
+ lockdep_assert_held(&queue->fence_ctx.lock);
+
+ /* Already suspended, nothing to do. */
+ if (queue_timeout_is_suspended(queue))
+ return;
+
+ job = list_first_entry_or_null(&queue->fence_ctx.in_flight_jobs,
+ struct panthor_job, node);
+ group = job ? job->group : NULL;
+
+ /* If the queue is blocked and the group is idle, we want the timer to
+ * keep running because the group can't be unblocked by other queues,
+ * so it has to come from an external source, and we want to timebox
+ * this external signalling.
+ */
+ if (group && group_can_run(group) &&
+ (group->blocked_queues & BIT(job->queue_idx)) &&
+ group_is_idle(group))
+ return;
+
+ now = jiffies;
+ qtimeout = queue->timeout.work.timer.expires;
+
+ /* Cancel the timer. */
+ timer_was_active = cancel_delayed_work(&queue->timeout.work);
+ if (!timer_was_active || !job)
+ queue->timeout.remaining = msecs_to_jiffies(JOB_TIMEOUT_MS);
+ else if (time_after(qtimeout, now))
+ queue->timeout.remaining = qtimeout - now;
+ else
+ queue->timeout.remaining = 0;
+
+ if (WARN_ON_ONCE(queue->timeout.remaining > msecs_to_jiffies(JOB_TIMEOUT_MS)))
+ queue->timeout.remaining = msecs_to_jiffies(JOB_TIMEOUT_MS);
+}
+
+static void
+queue_suspend_timeout(struct panthor_queue *queue)
+{
+ spin_lock(&queue->fence_ctx.lock);
+ queue_suspend_timeout_locked(queue);
+ spin_unlock(&queue->fence_ctx.lock);
+}
+
+static void
+queue_resume_timeout(struct panthor_queue *queue)
+{
+ spin_lock(&queue->fence_ctx.lock);
+
+ if (queue_timeout_is_suspended(queue)) {
+ mod_delayed_work(queue->scheduler.timeout_wq,
+ &queue->timeout.work,
+ queue->timeout.remaining);
+
+ queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT;
+ }
+
+ spin_unlock(&queue->fence_ctx.lock);
+}
+
/**
* cs_slot_prog_locked() - Program a queue slot
* @ptdev: Device.
@@ -1077,10 +1200,8 @@ cs_slot_prog_locked(struct panthor_device *ptdev, u32 csg_id, u32 cs_id)
CS_IDLE_EMPTY |
CS_STATE_MASK |
CS_EXTRACT_EVENT);
- if (queue->iface.input->insert != queue->iface.input->extract && queue->timeout_suspended) {
- drm_sched_resume_timeout(&queue->scheduler, queue->remaining_time);
- queue->timeout_suspended = false;
- }
+ if (queue->iface.input->insert != queue->iface.input->extract)
+ queue_resume_timeout(queue);
}
/**
@@ -1107,14 +1228,7 @@ cs_slot_reset_locked(struct panthor_device *ptdev, u32 csg_id, u32 cs_id)
CS_STATE_STOP,
CS_STATE_MASK);
- /* If the queue is blocked, we want to keep the timeout running, so
- * we can detect unbounded waits and kill the group when that happens.
- */
- if (!(group->blocked_queues & BIT(cs_id)) && !queue->timeout_suspended) {
- queue->remaining_time = drm_sched_suspend_timeout(&queue->scheduler);
- queue->timeout_suspended = true;
- WARN_ON(queue->remaining_time > msecs_to_jiffies(JOB_TIMEOUT_MS));
- }
+ queue_suspend_timeout(queue);
return 0;
}
@@ -1133,11 +1247,13 @@ csg_slot_sync_priority_locked(struct panthor_device *ptdev, u32 csg_id)
{
struct panthor_csg_slot *csg_slot = &ptdev->scheduler->csg_slots[csg_id];
struct panthor_fw_csg_iface *csg_iface;
+ u64 endpoint_req;
lockdep_assert_held(&ptdev->scheduler->lock);
csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
- csg_slot->priority = (csg_iface->input->endpoint_req & CSG_EP_REQ_PRIORITY_MASK) >> 28;
+ endpoint_req = panthor_fw_csg_endpoint_req_get(ptdev, csg_iface);
+ csg_slot->priority = CSG_EP_REQ_PRIORITY_GET(endpoint_req);
}
/**
@@ -1297,6 +1413,7 @@ csg_slot_prog_locked(struct panthor_device *ptdev, u32 csg_id, u32 priority)
struct panthor_csg_slot *csg_slot;
struct panthor_group *group;
u32 queue_mask = 0, i;
+ u64 endpoint_req;
lockdep_assert_held(&ptdev->scheduler->lock);
@@ -1323,10 +1440,12 @@ csg_slot_prog_locked(struct panthor_device *ptdev, u32 csg_id, u32 priority)
csg_iface->input->allow_compute = group->compute_core_mask;
csg_iface->input->allow_fragment = group->fragment_core_mask;
csg_iface->input->allow_other = group->tiler_core_mask;
- csg_iface->input->endpoint_req = CSG_EP_REQ_COMPUTE(group->max_compute_cores) |
- CSG_EP_REQ_FRAGMENT(group->max_fragment_cores) |
- CSG_EP_REQ_TILER(group->max_tiler_cores) |
- CSG_EP_REQ_PRIORITY(priority);
+ endpoint_req = CSG_EP_REQ_COMPUTE(group->max_compute_cores) |
+ CSG_EP_REQ_FRAGMENT(group->max_fragment_cores) |
+ CSG_EP_REQ_TILER(group->max_tiler_cores) |
+ CSG_EP_REQ_PRIORITY(priority);
+ panthor_fw_csg_endpoint_req_set(ptdev, csg_iface, endpoint_req);
+
csg_iface->input->config = panthor_vm_as(group->vm);
if (group->suspend_buf)
@@ -1411,7 +1530,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev,
fault = cs_iface->output->fault;
info = cs_iface->output->fault_info;
- if (queue && CS_EXCEPTION_TYPE(fault) == DRM_PANTHOR_EXCEPTION_CS_INHERIT_FAULT) {
+ if (queue) {
u64 cs_extract = queue->iface.output->extract;
struct panthor_job *job;
@@ -1909,28 +2028,6 @@ tick_ctx_is_full(const struct panthor_scheduler *sched,
return ctx->group_count == sched->csg_slot_count;
}
-static bool
-group_is_idle(struct panthor_group *group)
-{
- struct panthor_device *ptdev = group->ptdev;
- u32 inactive_queues;
-
- if (group->csg_id >= 0)
- return ptdev->scheduler->csg_slots[group->csg_id].idle;
-
- inactive_queues = group->idle_queues | group->blocked_queues;
- return hweight32(inactive_queues) == group->queue_count;
-}
-
-static bool
-group_can_run(struct panthor_group *group)
-{
- return group->state != PANTHOR_CS_GROUP_TERMINATED &&
- group->state != PANTHOR_CS_GROUP_UNKNOWN_STATE &&
- !group->destroyed && group->fatal_queues == 0 &&
- !group->timedout;
-}
-
static void
tick_ctx_pick_groups_from_list(const struct panthor_scheduler *sched,
struct panthor_sched_tick_ctx *ctx,
@@ -2224,9 +2321,9 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
continue;
}
- panthor_fw_update_reqs(csg_iface, endpoint_req,
- CSG_EP_REQ_PRIORITY(new_csg_prio),
- CSG_EP_REQ_PRIORITY_MASK);
+ panthor_fw_csg_endpoint_req_update(ptdev, csg_iface,
+ CSG_EP_REQ_PRIORITY(new_csg_prio),
+ CSG_EP_REQ_PRIORITY_MASK);
csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
CSG_ENDPOINT_CONFIG);
@@ -2612,6 +2709,7 @@ static void group_schedule_locked(struct panthor_group *group, u32 queue_mask)
static void queue_stop(struct panthor_queue *queue,
struct panthor_job *bad_job)
{
+ disable_delayed_work_sync(&queue->timeout.work);
drm_sched_stop(&queue->scheduler, bad_job ? &bad_job->base : NULL);
}
@@ -2623,6 +2721,7 @@ static void queue_start(struct panthor_queue *queue)
list_for_each_entry(job, &queue->scheduler.pending_list, base.list)
job->base.s_fence->parent = dma_fence_get(job->done_fence);
+ enable_delayed_work(&queue->timeout.work);
drm_sched_start(&queue->scheduler, 0);
}
@@ -2689,7 +2788,6 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
{
struct panthor_scheduler *sched = ptdev->scheduler;
struct panthor_csg_slots_upd_ctx upd_ctx;
- struct panthor_group *group;
u32 suspended_slots;
u32 i;
@@ -2743,13 +2841,23 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
while (slot_mask) {
u32 csg_id = ffs(slot_mask) - 1;
struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id];
+ struct panthor_group *group = csg_slot->group;
/* Terminate command timedout, but the soft-reset will
* automatically terminate all active groups, so let's
* force the state to halted here.
*/
- if (csg_slot->group->state != PANTHOR_CS_GROUP_TERMINATED)
- csg_slot->group->state = PANTHOR_CS_GROUP_TERMINATED;
+ if (group->state != PANTHOR_CS_GROUP_TERMINATED) {
+ group->state = PANTHOR_CS_GROUP_TERMINATED;
+
+ /* Reset the queue slots manually if the termination
+ * request failed.
+ */
+ for (i = 0; i < group->queue_count; i++) {
+ if (group->queues[i])
+ cs_slot_reset_locked(ptdev, csg_id, i);
+ }
+ }
slot_mask &= ~BIT(csg_id);
}
}
@@ -2779,8 +2887,8 @@ void panthor_sched_suspend(struct panthor_device *ptdev)
for (i = 0; i < sched->csg_slot_count; i++) {
struct panthor_csg_slot *csg_slot = &sched->csg_slots[i];
+ struct panthor_group *group = csg_slot->group;
- group = csg_slot->group;
if (!group)
continue;
@@ -2909,35 +3017,47 @@ void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile)
xa_unlock(&gpool->xa);
}
-static void group_sync_upd_work(struct work_struct *work)
+static bool queue_check_job_completion(struct panthor_queue *queue)
{
- struct panthor_group *group =
- container_of(work, struct panthor_group, sync_upd_work);
+ struct panthor_syncobj_64b *syncobj = NULL;
struct panthor_job *job, *job_tmp;
+ bool cookie, progress = false;
LIST_HEAD(done_jobs);
- u32 queue_idx;
- bool cookie;
cookie = dma_fence_begin_signalling();
- for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) {
- struct panthor_queue *queue = group->queues[queue_idx];
- struct panthor_syncobj_64b *syncobj;
+ spin_lock(&queue->fence_ctx.lock);
+ list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
+ if (!syncobj) {
+ struct panthor_group *group = job->group;
- if (!queue)
- continue;
+ syncobj = group->syncobjs->kmap +
+ (job->queue_idx * sizeof(*syncobj));
+ }
- syncobj = group->syncobjs->kmap + (queue_idx * sizeof(*syncobj));
+ if (syncobj->seqno < job->done_fence->seqno)
+ break;
- spin_lock(&queue->fence_ctx.lock);
- list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) {
- if (syncobj->seqno < job->done_fence->seqno)
- break;
+ list_move_tail(&job->node, &done_jobs);
+ dma_fence_signal_locked(job->done_fence);
+ }
- list_move_tail(&job->node, &done_jobs);
- dma_fence_signal_locked(job->done_fence);
- }
- spin_unlock(&queue->fence_ctx.lock);
+ if (list_empty(&queue->fence_ctx.in_flight_jobs)) {
+ /* If we have no job left, we cancel the timer, and reset remaining
+ * time to its default so it can be restarted next time
+ * queue_resume_timeout() is called.
+ */
+ queue_suspend_timeout_locked(queue);
+
+ /* If there's no job pending, we consider it progress to avoid a
+ * spurious timeout if the timeout handler and the sync update
+ * handler raced.
+ */
+ progress = true;
+ } else if (!list_empty(&done_jobs)) {
+ queue_reset_timeout_locked(queue);
+ progress = true;
}
+ spin_unlock(&queue->fence_ctx.lock);
dma_fence_end_signalling(cookie);
list_for_each_entry_safe(job, job_tmp, &done_jobs, node) {
@@ -2947,6 +3067,27 @@ static void group_sync_upd_work(struct work_struct *work)
panthor_job_put(&job->base);
}
+ return progress;
+}
+
+static void group_sync_upd_work(struct work_struct *work)
+{
+ struct panthor_group *group =
+ container_of(work, struct panthor_group, sync_upd_work);
+ u32 queue_idx;
+ bool cookie;
+
+ cookie = dma_fence_begin_signalling();
+ for (queue_idx = 0; queue_idx < group->queue_count; queue_idx++) {
+ struct panthor_queue *queue = group->queues[queue_idx];
+
+ if (!queue)
+ continue;
+
+ queue_check_job_completion(queue);
+ }
+ dma_fence_end_signalling(cookie);
+
group_put(group);
}
@@ -3194,17 +3335,6 @@ queue_run_job(struct drm_sched_job *sched_job)
queue->iface.input->insert = job->ringbuf.end;
if (group->csg_id < 0) {
- /* If the queue is blocked, we want to keep the timeout running, so we
- * can detect unbounded waits and kill the group when that happens.
- * Otherwise, we suspend the timeout so the time we spend waiting for
- * a CSG slot is not counted.
- */
- if (!(group->blocked_queues & BIT(job->queue_idx)) &&
- !queue->timeout_suspended) {
- queue->remaining_time = drm_sched_suspend_timeout(&queue->scheduler);
- queue->timeout_suspended = true;
- }
-
group_schedule_locked(group, BIT(job->queue_idx));
} else {
gpu_write(ptdev, CSF_DOORBELL(queue->doorbell_id), 1);
@@ -3213,6 +3343,7 @@ queue_run_job(struct drm_sched_job *sched_job)
pm_runtime_get(ptdev->base.dev);
sched->pm.has_ref = true;
}
+ queue_resume_timeout(queue);
panthor_devfreq_record_busy(sched->ptdev);
}
@@ -3262,7 +3393,6 @@ queue_timedout_job(struct drm_sched_job *sched_job)
mutex_unlock(&sched->lock);
queue_start(queue);
-
return DRM_GPU_SCHED_STAT_RESET;
}
@@ -3305,11 +3435,23 @@ static u32 calc_profiling_ringbuf_num_slots(struct panthor_device *ptdev,
return DIV_ROUND_UP(cs_ringbuf_size, min_profiled_job_instrs * sizeof(u64));
}
+static void queue_timeout_work(struct work_struct *work)
+{
+ struct panthor_queue *queue = container_of(work, struct panthor_queue,
+ timeout.work.work);
+ bool progress;
+
+ progress = queue_check_job_completion(queue);
+ if (!progress)
+ drm_sched_fault(&queue->scheduler);
+}
+
static struct panthor_queue *
group_create_queue(struct panthor_group *group,
- const struct drm_panthor_queue_create *args)
+ const struct drm_panthor_queue_create *args,
+ u64 drm_client_id, u32 gid, u32 qid)
{
- const struct drm_sched_init_args sched_args = {
+ struct drm_sched_init_args sched_args = {
.ops = &panthor_queue_sched_ops,
.submit_wq = group->ptdev->scheduler->wq,
.num_rqs = 1,
@@ -3320,9 +3462,8 @@ group_create_queue(struct panthor_group *group,
* their profiling status.
*/
.credit_limit = args->ringbuf_size / sizeof(u64),
- .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
+ .timeout = MAX_SCHEDULE_TIMEOUT,
.timeout_wq = group->ptdev->reset.wq,
- .name = "panthor-queue",
.dev = group->ptdev->base.dev,
};
struct drm_gpu_scheduler *drm_sched;
@@ -3343,6 +3484,8 @@ group_create_queue(struct panthor_group *group,
if (!queue)
return ERR_PTR(-ENOMEM);
+ queue->timeout.remaining = msecs_to_jiffies(JOB_TIMEOUT_MS);
+ INIT_DELAYED_WORK(&queue->timeout.work, queue_timeout_work);
queue->fence_ctx.id = dma_fence_context_alloc(1);
spin_lock_init(&queue->fence_ctx.lock);
INIT_LIST_HEAD(&queue->fence_ctx.in_flight_jobs);
@@ -3397,12 +3540,23 @@ group_create_queue(struct panthor_group *group,
if (ret)
goto err_free_queue;
+ /* assign a unique name */
+ queue->name = kasprintf(GFP_KERNEL, "panthor-queue-%llu-%u-%u", drm_client_id, gid, qid);
+ if (!queue->name) {
+ ret = -ENOMEM;
+ goto err_free_queue;
+ }
+
+ sched_args.name = queue->name;
+
ret = drm_sched_init(&queue->scheduler, &sched_args);
if (ret)
goto err_free_queue;
drm_sched = &queue->scheduler;
ret = drm_sched_entity_init(&queue->entity, 0, &drm_sched, 1, NULL);
+ if (ret)
+ goto err_free_queue;
return queue;
@@ -3446,7 +3600,8 @@ static void add_group_kbo_sizes(struct panthor_device *ptdev,
int panthor_group_create(struct panthor_file *pfile,
const struct drm_panthor_group_create *group_args,
- const struct drm_panthor_queue_create *queue_args)
+ const struct drm_panthor_queue_create *queue_args,
+ u64 drm_client_id)
{
struct panthor_device *ptdev = pfile->ptdev;
struct panthor_group_pool *gpool = pfile->groups;
@@ -3539,12 +3694,16 @@ int panthor_group_create(struct panthor_file *pfile,
memset(group->syncobjs->kmap, 0,
group_args->queues.count * sizeof(struct panthor_syncobj_64b));
+ ret = xa_alloc(&gpool->xa, &gid, group, XA_LIMIT(1, MAX_GROUPS_PER_POOL), GFP_KERNEL);
+ if (ret)
+ goto err_put_group;
+
for (i = 0; i < group_args->queues.count; i++) {
- group->queues[i] = group_create_queue(group, &queue_args[i]);
+ group->queues[i] = group_create_queue(group, &queue_args[i], drm_client_id, gid, i);
if (IS_ERR(group->queues[i])) {
ret = PTR_ERR(group->queues[i]);
group->queues[i] = NULL;
- goto err_put_group;
+ goto err_erase_gid;
}
group->queue_count++;
@@ -3552,10 +3711,6 @@ int panthor_group_create(struct panthor_file *pfile,
group->idle_queues = GENMASK(group->queue_count - 1, 0);
- ret = xa_alloc(&gpool->xa, &gid, group, XA_LIMIT(1, MAX_GROUPS_PER_POOL), GFP_KERNEL);
- if (ret)
- goto err_put_group;
-
mutex_lock(&sched->reset.lock);
if (atomic_read(&sched->reset.in_progress)) {
panthor_group_stop(group);
@@ -3574,6 +3729,9 @@ int panthor_group_create(struct panthor_file *pfile,
return gid;
+err_erase_gid:
+ xa_erase(&gpool->xa, gid);
+
err_put_group:
group_put(group);
return ret;
@@ -3855,7 +4013,9 @@ void panthor_sched_unplug(struct panthor_device *ptdev)
{
struct panthor_scheduler *sched = ptdev->scheduler;
- cancel_delayed_work_sync(&sched->tick_work);
+ disable_delayed_work_sync(&sched->tick_work);
+ disable_work_sync(&sched->fw_events_work);
+ disable_work_sync(&sched->sync_upd_work);
mutex_lock(&sched->lock);
if (sched->pm.has_ref) {
@@ -3873,8 +4033,6 @@ static void panthor_sched_fini(struct drm_device *ddev, void *res)
if (!sched || !sched->csg_slot_count)
return;
- cancel_delayed_work_sync(&sched->tick_work);
-
if (sched->wq)
destroy_workqueue(sched->wq);
diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h
index 742b0b4ff3a3..f4a475aa34c0 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.h
+++ b/drivers/gpu/drm/panthor/panthor_sched.h
@@ -21,7 +21,8 @@ struct panthor_job;
int panthor_group_create(struct panthor_file *pfile,
const struct drm_panthor_group_create *group_args,
- const struct drm_panthor_queue_create *queue_args);
+ const struct drm_panthor_queue_create *queue_args,
+ u64 drm_client_id);
int panthor_group_destroy(struct panthor_file *pfile, u32 group_handle);
int panthor_group_get_state(struct panthor_file *pfile,
struct drm_panthor_group_get_state *get_state);
diff --git a/drivers/gpu/drm/pl111/pl111_display.c b/drivers/gpu/drm/pl111/pl111_display.c
index b9fe926a49e8..3a9661b9b1fc 100644
--- a/drivers/gpu/drm/pl111/pl111_display.c
+++ b/drivers/gpu/drm/pl111/pl111_display.c
@@ -20,6 +20,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "pl111_drm.h"
@@ -473,12 +474,15 @@ static int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate,
return best_div;
}
-static long pl111_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int pl111_clk_div_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- int div = pl111_clk_div_choose_div(hw, rate, prate, true);
+ int div = pl111_clk_div_choose_div(hw, req->rate,
+ &req->best_parent_rate, true);
- return DIV_ROUND_UP_ULL(*prate, div);
+ req->rate = DIV_ROUND_UP_ULL(req->best_parent_rate, div);
+
+ return 0;
}
static unsigned long pl111_clk_div_recalc_rate(struct clk_hw *hw,
@@ -528,7 +532,7 @@ static int pl111_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops pl111_clk_div_ops = {
.recalc_rate = pl111_clk_div_recalc_rate,
- .round_rate = pl111_clk_div_round_rate,
+ .determine_rate = pl111_clk_div_determine_rate,
.set_rate = pl111_clk_div_set_rate,
};
diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c
index d6ea01f3797b..2e3200db2f39 100644
--- a/drivers/gpu/drm/qxl/qxl_cmd.c
+++ b/drivers/gpu/drm/qxl/qxl_cmd.c
@@ -27,6 +27,7 @@
#include <linux/delay.h>
+#include <drm/drm_print.h>
#include <drm/drm_util.h>
#include "qxl_drv.h"
diff --git a/drivers/gpu/drm/qxl/qxl_debugfs.c b/drivers/gpu/drm/qxl/qxl_debugfs.c
index 2d9ed3b94574..b66b14b08b61 100644
--- a/drivers/gpu/drm/qxl/qxl_debugfs.c
+++ b/drivers/gpu/drm/qxl/qxl_debugfs.c
@@ -30,6 +30,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include "qxl_drv.h"
#include "qxl_object.h"
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index ae7e572b1b4a..a134820aac58 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -34,9 +34,12 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#include "qxl_drv.h"
#include "qxl_object.h"
@@ -382,7 +385,25 @@ static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc,
static void qxl_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
+ struct drm_device *dev = crtc->dev;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_pending_vblank_event *event;
+
qxl_crtc_update_monitors_config(crtc, "flush");
+
+ spin_lock_irq(&dev->event_lock);
+
+ event = crtc_state->event;
+ crtc_state->event = NULL;
+
+ if (event) {
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ }
+
+ spin_unlock_irq(&dev->event_lock);
}
static void qxl_crtc_destroy(struct drm_crtc *crtc)
@@ -401,6 +422,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
};
static int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb,
@@ -455,11 +477,15 @@ static void qxl_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
qxl_crtc_update_monitors_config(crtc, "enable");
+
+ drm_crtc_vblank_on(crtc);
}
static void qxl_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
+ drm_crtc_vblank_off(crtc);
+
qxl_crtc_update_monitors_config(crtc, "disable");
}
@@ -1276,6 +1302,10 @@ int qxl_modeset_init(struct qxl_device *qdev)
qxl_display_read_client_monitors_config(qdev);
+ ret = drm_vblank_init(&qdev->ddev, qxl_num_crtc);
+ if (ret)
+ return ret;
+
drm_mode_config_reset(&qdev->ddev);
return 0;
}
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index 417061ae59eb..2bbb1168a3ff 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -44,6 +44,7 @@
#include <drm/drm_module.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "qxl_object.h"
diff --git a/drivers/gpu/drm/qxl/qxl_gem.c b/drivers/gpu/drm/qxl/qxl_gem.c
index fc5e3763c359..4939b57a2a48 100644
--- a/drivers/gpu/drm/qxl/qxl_gem.c
+++ b/drivers/gpu/drm/qxl/qxl_gem.c
@@ -24,6 +24,7 @@
*/
#include <drm/drm.h>
+#include <drm/drm_print.h>
#include "qxl_drv.h"
#include "qxl_object.h"
@@ -39,7 +40,7 @@ void qxl_gem_object_free(struct drm_gem_object *gobj)
qxl_surface_evict(qdev, qobj, false);
tbo = &qobj->tbo;
- ttm_bo_put(tbo);
+ ttm_bo_fini(tbo);
}
int qxl_gem_object_create(struct qxl_device *qdev, int size,
diff --git a/drivers/gpu/drm/qxl/qxl_image.c b/drivers/gpu/drm/qxl/qxl_image.c
index ffff54e5fb31..3cc45997533d 100644
--- a/drivers/gpu/drm/qxl/qxl_image.c
+++ b/drivers/gpu/drm/qxl/qxl_image.c
@@ -26,6 +26,8 @@
#include <linux/gfp.h>
#include <linux/slab.h>
+#include <drm/drm_print.h>
+
#include "qxl_drv.h"
#include "qxl_object.h"
diff --git a/drivers/gpu/drm/qxl/qxl_ioctl.c b/drivers/gpu/drm/qxl/qxl_ioctl.c
index 506ae1f5e099..336cbff26089 100644
--- a/drivers/gpu/drm/qxl/qxl_ioctl.c
+++ b/drivers/gpu/drm/qxl/qxl_ioctl.c
@@ -26,6 +26,8 @@
#include <linux/pci.h>
#include <linux/uaccess.h>
+#include <drm/drm_print.h>
+
#include "qxl_drv.h"
#include "qxl_object.h"
diff --git a/drivers/gpu/drm/qxl/qxl_irq.c b/drivers/gpu/drm/qxl/qxl_irq.c
index 665278ee3b6d..4018bcf808e5 100644
--- a/drivers/gpu/drm/qxl/qxl_irq.c
+++ b/drivers/gpu/drm/qxl/qxl_irq.c
@@ -26,6 +26,7 @@
#include <linux/pci.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "qxl_drv.h"
diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c
index dc3828db1991..461b7ab9ad5c 100644
--- a/drivers/gpu/drm/qxl/qxl_kms.c
+++ b/drivers/gpu/drm/qxl/qxl_kms.c
@@ -28,6 +28,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "qxl_drv.h"
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index 05204a6a3fa8..7b3c9a6016db 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -22,6 +22,8 @@
#include <linux/delay.h>
+#include <drm/drm_print.h>
+
#include <trace/events/dma_fence.h>
#include "qxl_drv.h"
diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c
index 765a144cea14..1a40590077dd 100644
--- a/drivers/gpu/drm/qxl/qxl_ttm.c
+++ b/drivers/gpu/drm/qxl/qxl_ttm.c
@@ -28,6 +28,7 @@
#include <drm/drm.h>
#include <drm/drm_file.h>
#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include <drm/qxl_drm.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_placement.h>
@@ -196,7 +197,7 @@ int qxl_ttm_init(struct qxl_device *qdev)
r = ttm_device_init(&qdev->mman.bdev, &qxl_bo_driver, NULL,
qdev->ddev.anon_inode->i_mapping,
qdev->ddev.vma_offset_manager,
- false, false);
+ 0);
if (r) {
DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
return r;
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 63c47585afbc..527b9d19d730 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -80,6 +80,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_audio_component.h>
#include <drm/drm_suballoc.h>
+#include <drm/drm_print.h>
#include "radeon_family.h"
#include "radeon_mode.h"
diff --git a/drivers/gpu/drm/radeon/radeon_acpi.c b/drivers/gpu/drm/radeon/radeon_acpi.c
index 22ce61bdfc06..08f8ba4fd148 100644
--- a/drivers/gpu/drm/radeon/radeon_acpi.c
+++ b/drivers/gpu/drm/radeon/radeon_acpi.c
@@ -408,7 +408,6 @@ static int radeon_atif_handler(struct radeon_device *rdev,
pm_runtime_get_sync(rdev_to_drm(rdev)->dev);
/* Just fire off a uevent and let userspace tell us what to do */
drm_helper_hpd_irq_event(rdev_to_drm(rdev));
- pm_runtime_mark_last_busy(rdev_to_drm(rdev)->dev);
pm_runtime_put_autosuspend(rdev_to_drm(rdev)->dev);
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 9f6a3df951ba..012d8b2295b8 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -875,10 +875,8 @@ radeon_lvds_detect(struct drm_connector *connector, bool force)
radeon_connector_update_scratch_regs(connector, ret);
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
@@ -1066,10 +1064,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
radeon_connector_update_scratch_regs(connector, ret);
out:
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
@@ -1154,10 +1150,8 @@ radeon_tv_detect(struct drm_connector *connector, bool force)
ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false);
radeon_connector_update_scratch_regs(connector, ret);
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
@@ -1402,10 +1396,8 @@ out:
}
exit:
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
@@ -1714,10 +1706,8 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
}
out:
- if (!drm_kms_helper_is_poll_worker()) {
- pm_runtime_mark_last_busy(connector->dev->dev);
+ if (!drm_kms_helper_is_poll_worker())
pm_runtime_put_autosuspend(connector->dev->dev);
- }
return ret;
}
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 9e35b14e2bf0..60afaa8e56b4 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1635,7 +1635,7 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend,
}
if (notify_clients)
- drm_client_dev_suspend(dev, false);
+ drm_client_dev_suspend(dev);
return 0;
}
@@ -1739,7 +1739,7 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool notify_clients)
radeon_pm_compute_clocks(rdev);
if (notify_clients)
- drm_client_dev_resume(dev, false);
+ drm_client_dev_resume(dev);
return 0;
}
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 351b9dfcdad8..35fb99bcd9a7 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -644,8 +644,6 @@ radeon_crtc_set_config(struct drm_mode_set *set,
if (crtc->enabled)
active = true;
- pm_runtime_mark_last_busy(dev->dev);
-
rdev = dev->dev_private;
/* if we have active crtcs and we don't have a power ref,
take the current one */
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 88e821d67af7..87fd6255c114 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -241,12 +241,12 @@ module_param_named(uvd, radeon_uvd, int, 0444);
MODULE_PARM_DESC(vce, "vce enable/disable vce support (1 = enable, 0 = disable)");
module_param_named(vce, radeon_vce, int, 0444);
-int radeon_si_support = 1;
-MODULE_PARM_DESC(si_support, "SI support (1 = enabled (default), 0 = disabled)");
+int radeon_si_support = -1;
+MODULE_PARM_DESC(si_support, "SI support (1 = enabled, 0 = disabled, -1 = default)");
module_param_named(si_support, radeon_si_support, int, 0444);
-int radeon_cik_support = 1;
-MODULE_PARM_DESC(cik_support, "CIK support (1 = enabled (default), 0 = disabled)");
+int radeon_cik_support = -1;
+MODULE_PARM_DESC(cik_support, "CIK support (1 = enabled, 0 = disabled, -1 = default)");
module_param_named(cik_support, radeon_cik_support, int, 0444);
static const struct pci_device_id pciidlist[] = {
@@ -256,12 +256,60 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
static const struct drm_driver kms_driver;
+static bool radeon_support_enabled(struct device *dev,
+ const enum radeon_family family)
+{
+ const char *gen;
+ int module_param = -1;
+ bool amdgpu_support_built = IS_ENABLED(CONFIG_DRM_AMDGPU);
+ bool support_by_default = true;
+
+ switch (family) {
+ case CHIP_TAHITI:
+ case CHIP_PITCAIRN:
+ case CHIP_VERDE:
+ case CHIP_OLAND:
+ case CHIP_HAINAN:
+ gen = "SI";
+ module_param = radeon_si_support;
+ amdgpu_support_built &= IS_ENABLED(CONFIG_DRM_AMDGPU_SI);
+ support_by_default = false;
+ break;
+
+ case CHIP_BONAIRE:
+ case CHIP_HAWAII:
+ support_by_default = false;
+ fallthrough;
+ case CHIP_KAVERI:
+ case CHIP_KABINI:
+ case CHIP_MULLINS:
+ gen = "CIK";
+ module_param = radeon_cik_support;
+ amdgpu_support_built &= IS_ENABLED(CONFIG_DRM_AMDGPU_CIK);
+ break;
+
+ default:
+ /* All other chips are supported by radeon only */
+ return true;
+ }
+
+ if ((module_param == -1 && (support_by_default || !amdgpu_support_built)) ||
+ module_param == 1)
+ return true;
+
+ if (!module_param)
+ dev_info(dev, "%s support disabled by module param\n", gen);
+
+ return false;
+}
+
static int radeon_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
unsigned long flags = 0;
struct drm_device *ddev;
struct radeon_device *rdev;
+ struct device *dev = &pdev->dev;
const struct drm_format_info *format;
int ret;
@@ -270,30 +318,8 @@ static int radeon_pci_probe(struct pci_dev *pdev,
flags = ent->driver_data;
- if (!radeon_si_support) {
- switch (flags & RADEON_FAMILY_MASK) {
- case CHIP_TAHITI:
- case CHIP_PITCAIRN:
- case CHIP_VERDE:
- case CHIP_OLAND:
- case CHIP_HAINAN:
- dev_info(&pdev->dev,
- "SI support disabled by module param\n");
- return -ENODEV;
- }
- }
- if (!radeon_cik_support) {
- switch (flags & RADEON_FAMILY_MASK) {
- case CHIP_KAVERI:
- case CHIP_BONAIRE:
- case CHIP_HAWAII:
- case CHIP_KABINI:
- case CHIP_MULLINS:
- dev_info(&pdev->dev,
- "CIK support disabled by module param\n");
- return -ENODEV;
- }
- }
+ if (!radeon_support_enabled(dev, flags & RADEON_FAMILY_MASK))
+ return -ENODEV;
if (vga_switcheroo_client_probe_defer(pdev))
return -EPROBE_DEFER;
@@ -303,28 +329,28 @@ static int radeon_pci_probe(struct pci_dev *pdev,
if (ret)
return ret;
- rdev = devm_drm_dev_alloc(&pdev->dev, &kms_driver, typeof(*rdev), ddev);
+ rdev = devm_drm_dev_alloc(dev, &kms_driver, typeof(*rdev), ddev);
if (IS_ERR(rdev))
return PTR_ERR(rdev);
- rdev->dev = &pdev->dev;
+ rdev->dev = dev;
rdev->pdev = pdev;
ddev = rdev_to_drm(rdev);
ddev->dev_private = rdev;
ret = pci_enable_device(pdev);
if (ret)
- goto err_free;
+ return ret;
pci_set_drvdata(pdev, ddev);
ret = radeon_driver_load_kms(ddev, flags);
if (ret)
- goto err_agp;
+ goto err;
ret = drm_dev_register(ddev, flags);
if (ret)
- goto err_agp;
+ goto err;
if (rdev->mc.real_vram_size <= (8 * 1024 * 1024))
format = drm_format_info(DRM_FORMAT_C8);
@@ -337,30 +363,14 @@ static int radeon_pci_probe(struct pci_dev *pdev,
return 0;
-err_agp:
+err:
pci_disable_device(pdev);
-err_free:
- drm_dev_put(ddev);
return ret;
}
static void
-radeon_pci_remove(struct pci_dev *pdev)
-{
- struct drm_device *dev = pci_get_drvdata(pdev);
-
- drm_put_dev(dev);
-}
-
-static void
radeon_pci_shutdown(struct pci_dev *pdev)
{
- /* if we are running in a VM, make sure the device
- * torn down properly on reboot/shutdown
- */
- if (radeon_device_is_virtual())
- radeon_pci_remove(pdev);
-
#if defined(CONFIG_PPC64) || defined(CONFIG_MACH_LOONGSON64)
/*
* Some adapters need to be suspended before a
@@ -477,7 +487,6 @@ static int radeon_pmops_runtime_idle(struct device *dev)
}
}
- pm_runtime_mark_last_busy(dev);
pm_runtime_autosuspend(dev);
/* we don't want the main rpm_idle to call suspend - we want to autosuspend */
return 1;
@@ -499,7 +508,6 @@ long radeon_drm_ioctl(struct file *filp,
ret = drm_ioctl(filp, cmd, arg);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return ret;
}
@@ -613,7 +621,6 @@ static struct pci_driver radeon_kms_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.probe = radeon_pci_probe,
- .remove = radeon_pci_remove,
.shutdown = radeon_pci_shutdown,
.driver.pm = &radeon_pm_ops,
};
diff --git a/drivers/gpu/drm/radeon/radeon_fbdev.c b/drivers/gpu/drm/radeon/radeon_fbdev.c
index dc81b0c2dbff..fd083aaa91bb 100644
--- a/drivers/gpu/drm/radeon/radeon_fbdev.c
+++ b/drivers/gpu/drm/radeon/radeon_fbdev.c
@@ -154,7 +154,6 @@ static int radeon_fbdev_fb_open(struct fb_info *info, int user)
return 0;
err_pm_runtime_mark_last_busy:
- pm_runtime_mark_last_busy(rdev_to_drm(rdev)->dev);
pm_runtime_put_autosuspend(rdev_to_drm(rdev)->dev);
return ret;
}
@@ -164,7 +163,6 @@ static int radeon_fbdev_fb_release(struct fb_info *info, int user)
struct drm_fb_helper *fb_helper = info->par;
struct radeon_device *rdev = fb_helper->dev->dev_private;
- pm_runtime_mark_last_busy(rdev_to_drm(rdev)->dev);
pm_runtime_put_autosuspend(rdev_to_drm(rdev)->dev);
return 0;
@@ -184,8 +182,6 @@ static void radeon_fbdev_fb_destroy(struct fb_info *info)
radeon_fbdev_destroy_pinned_object(gobj);
drm_client_release(&fb_helper->client);
- drm_fb_helper_unprepare(fb_helper);
- kfree(fb_helper);
}
static const struct fb_ops radeon_fbdev_fb_ops = {
@@ -206,7 +202,7 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
struct radeon_device *rdev = fb_helper->dev->dev_private;
const struct drm_format_info *format_info;
struct drm_mode_fb_cmd2 mode_cmd = { };
- struct fb_info *info;
+ struct fb_info *info = fb_helper->info;
struct drm_gem_object *gobj;
struct radeon_bo *rbo;
struct drm_framebuffer *fb;
@@ -247,13 +243,6 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
fb_helper->funcs = &radeon_fbdev_fb_helper_funcs;
fb_helper->fb = fb;
- /* okay we have an object now allocate the framebuffer */
- info = drm_fb_helper_alloc_info(fb_helper);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto err_drm_framebuffer_unregister_private;
- }
-
info->fbops = &radeon_fbdev_fb_ops;
/* radeon resume is fragile and needs a vt switch to help it along */
@@ -279,10 +268,6 @@ int radeon_fbdev_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
return 0;
-err_drm_framebuffer_unregister_private:
- fb_helper->fb = NULL;
- drm_framebuffer_unregister_private(fb);
- drm_framebuffer_cleanup(fb);
err_kfree:
kfree(fb);
err_radeon_fbdev_destroy_pinned_object:
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 5b5b54e876d4..167d6f122b8e 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -360,13 +360,6 @@ static bool radeon_fence_is_signaled(struct dma_fence *f)
if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq)
return true;
- if (down_read_trylock(&rdev->exclusive_lock)) {
- radeon_fence_process(rdev, ring);
- up_read(&rdev->exclusive_lock);
-
- if (atomic64_read(&rdev->fence_drv[ring].last_seq) >= seq)
- return true;
- }
return false;
}
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index f86773f3db20..18ca1bcfd2f9 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -86,7 +86,7 @@ static void radeon_gem_object_free(struct drm_gem_object *gobj)
if (robj) {
radeon_mn_unregister(robj);
- ttm_bo_put(&robj->tbo);
+ ttm_bo_fini(&robj->tbo);
}
}
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 645e33bf7947..7cbe02ffb193 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -84,7 +84,6 @@ void radeon_driver_unload_kms(struct drm_device *dev)
rdev->agp = NULL;
done_free:
- kfree(rdev);
dev->dev_private = NULL;
}
@@ -170,7 +169,6 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
pm_runtime_set_autosuspend_delay(dev->dev, 5000);
pm_runtime_set_active(dev->dev);
pm_runtime_allow(dev->dev);
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
}
@@ -677,7 +675,6 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
file_priv->driver_priv = fpriv;
}
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return 0;
@@ -687,7 +684,6 @@ err_fpriv:
kfree(fpriv);
err_suspend:
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
return r;
}
@@ -737,7 +733,6 @@ void radeon_driver_postclose_kms(struct drm_device *dev,
kfree(fpriv);
file_priv->driver_priv = NULL;
}
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
}
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index 616d25c8c2de..695ac32f7535 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -683,8 +683,10 @@ int radeon_ttm_init(struct radeon_device *rdev)
r = ttm_device_init(&rdev->mman.bdev, &radeon_bo_driver, rdev->dev,
rdev_to_drm(rdev)->anon_inode->i_mapping,
rdev_to_drm(rdev)->vma_offset_manager,
- rdev->need_swiotlb,
- dma_addressing_limited(&rdev->pdev->dev));
+ (rdev->need_swiotlb ?
+ TTM_ALLOCATION_POOL_USE_DMA_ALLOC : 0) |
+ (dma_addressing_limited(&rdev->pdev->dev) ?
+ TTM_ALLOCATION_POOL_USE_DMA32 : 0));
if (r) {
DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
return r;
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
index 7e175dbfd892..2e2906ab750b 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_crtc.c
@@ -17,6 +17,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "rcar_cmm.h"
@@ -993,7 +994,7 @@ static void rcar_du_crtc_cleanup(struct drm_crtc *crtc)
rcar_du_crtc_crc_cleanup(rcrtc);
- return drm_crtc_cleanup(crtc);
+ drm_crtc_cleanup(crtc);
}
static void rcar_du_crtc_reset(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
index d948ff3594c4..031d07f4508e 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c
@@ -24,6 +24,7 @@
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "rcar_du_drv.h"
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c
index 216219accfd9..6294443f6068 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c
@@ -11,6 +11,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
@@ -407,8 +408,8 @@ int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
struct rcar_du_device *rcdu = to_rcar_du_device(dev);
- unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
unsigned int align;
+ int ret;
/*
* The R8A7779 DU requires a 16 pixels pitch alignment as documented,
@@ -419,7 +420,9 @@ int rcar_du_dumb_create(struct drm_file *file, struct drm_device *dev,
else
align = 16 * args->bpp / 8;
- args->pitch = roundup(min_pitch, align);
+ ret = drm_mode_size_dumb(dev, args, align, 0);
+ if (ret)
+ return ret;
return drm_gem_dma_dumb_create_internal(file, dev, args);
}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
index 5c73a513f678..9413b76d0bfc 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c
@@ -5,6 +5,7 @@
* Copyright (C) 2020 Renesas Electronics Corporation
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -71,6 +72,7 @@ struct rcar_mipi_dsi {
} clocks;
enum mipi_dsi_pixel_format format;
+ unsigned long mode_flags;
unsigned int num_data_lanes;
unsigned int lanes;
};
@@ -316,8 +318,8 @@ rcar_mipi_dsi_post_init_phtw_v4h(struct rcar_mipi_dsi *dsi,
WRITE_PHTW(0x01020100, 0x00000180);
ret = read_poll_timeout(rcar_mipi_dsi_read, status,
- status & PHTR_TEST, 2000, 10000, false,
- dsi, PHTR);
+ status & PHTR_TESTDOUT_TEST,
+ 2000, 10000, false, dsi, PHTR);
if (ret < 0) {
dev_err(dsi->dev, "failed to test PHTR\n");
return ret;
@@ -457,29 +459,43 @@ static void rcar_mipi_dsi_set_display_timing(struct rcar_mipi_dsi *dsi,
u32 vprmset4r;
/* Configuration for Pixel Stream and Packet Header */
- if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 24)
+ switch (mipi_dsi_pixel_format_to_bpp(dsi->format)) {
+ case 24:
rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB24);
- else if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 18)
+ break;
+ case 18:
rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB18);
- else if (mipi_dsi_pixel_format_to_bpp(dsi->format) == 16)
+ break;
+ case 16:
rcar_mipi_dsi_write(dsi, TXVMPSPHSETR, TXVMPSPHSETR_DT_RGB16);
- else {
+ break;
+ default:
dev_warn(dsi->dev, "unsupported format");
return;
}
/* Configuration for Blanking sequence and Input Pixel */
- setr = TXVMSETR_HSABPEN_EN | TXVMSETR_HBPBPEN_EN
- | TXVMSETR_HFPBPEN_EN | TXVMSETR_SYNSEQ_PULSES
- | TXVMSETR_PIXWDTH | TXVMSETR_VSTPM;
+ setr = TXVMSETR_PIXWDTH | TXVMSETR_VSTPM;
+
+ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
+ setr |= TXVMSETR_SYNSEQ_EVENTS;
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP))
+ setr |= TXVMSETR_HFPBPEN;
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP))
+ setr |= TXVMSETR_HBPBPEN;
+ if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA))
+ setr |= TXVMSETR_HSABPEN;
+ }
+
rcar_mipi_dsi_write(dsi, TXVMSETR, setr);
- /* Configuration for Video Parameters */
- vprmset0r = (mode->flags & DRM_MODE_FLAG_PVSYNC ?
- TXVMVPRMSET0R_VSPOL_HIG : TXVMVPRMSET0R_VSPOL_LOW)
- | (mode->flags & DRM_MODE_FLAG_PHSYNC ?
- TXVMVPRMSET0R_HSPOL_HIG : TXVMVPRMSET0R_HSPOL_LOW)
- | TXVMVPRMSET0R_CSPC_RGB | TXVMVPRMSET0R_BPP_24;
+ /* Configuration for Video Parameters, input is always RGB888 */
+ vprmset0r = TXVMVPRMSET0R_BPP_24;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ vprmset0r |= TXVMVPRMSET0R_VSPOL_LOW;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ vprmset0r |= TXVMVPRMSET0R_HSPOL_LOW;
vprmset1r = TXVMVPRMSET1R_VACTIVE(mode->vdisplay)
| TXVMVPRMSET1R_VSA(mode->vsync_end - mode->vsync_start);
@@ -620,6 +636,7 @@ static int rcar_mipi_dsi_startup(struct rcar_mipi_dsi *dsi,
vclkset = VCLKSET_CKEN;
rcar_mipi_dsi_write(dsi, VCLKSET, vclkset);
+ /* Output is always RGB, never YCbCr */
if (dsi_format == 24)
vclkset |= VCLKSET_BPP_24;
else if (dsi_format == 18)
@@ -631,7 +648,7 @@ static int rcar_mipi_dsi_startup(struct rcar_mipi_dsi *dsi,
return -EINVAL;
}
- vclkset |= VCLKSET_COLOR_RGB | VCLKSET_LANE(dsi->lanes - 1);
+ vclkset |= VCLKSET_LANE(dsi->lanes - 1);
switch (dsi->info->model) {
case RCAR_DSI_V3U:
@@ -911,6 +928,7 @@ static int rcar_mipi_dsi_host_attach(struct mipi_dsi_host *host,
dsi->lanes = device->lanes;
dsi->format = device->format;
+ dsi->mode_flags = device->mode_flags;
dsi->next_bridge = devm_drm_of_get_bridge(dsi->dev, dsi->dev->of_node,
1, 0);
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
index 76521276e2af..b6fb58c2f9f6 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi_regs.h
@@ -9,292 +9,311 @@
#define __RCAR_MIPI_DSI_REGS_H__
#define LINKSR 0x010
-#define LINKSR_LPBUSY (1 << 1)
-#define LINKSR_HSBUSY (1 << 0)
+#define LINKSR_LPBUSY BIT_U32(1)
+#define LINKSR_HSBUSY BIT_U32(0)
#define TXSETR 0x100
-#define TXSETR_LANECNT_MASK (0x3 << 0)
+#define TXSETR_LANECNT_MASK GENMASK_U32(1, 0)
/*
* DSI Command Transfer Registers
*/
#define TXCMSETR 0x110
-#define TXCMSETR_SPDTYP (1 << 8) /* 0:HS 1:LP */
-#define TXCMSETR_LPPDACC (1 << 0)
+#define TXCMSETR_SPDTYP BIT_U32(8) /* 0:HS 1:LP */
+#define TXCMSETR_LPPDACC BIT_U32(0)
#define TXCMCR 0x120
-#define TXCMCR_BTATYP (1 << 2)
-#define TXCMCR_BTAREQ (1 << 1)
-#define TXCMCR_TXREQ (1 << 0)
+#define TXCMCR_BTATYP BIT_U32(2)
+#define TXCMCR_BTAREQ BIT_U32(1)
+#define TXCMCR_TXREQ BIT_U32(0)
#define TXCMSR 0x130
-#define TXCMSR_CLSNERR (1 << 18)
-#define TXCMSR_AXIERR (1 << 16)
-#define TXCMSR_TXREQEND (1 << 0)
+#define TXCMSR_CLSNERR BIT_U32(18)
+#define TXCMSR_AXIERR BIT_U32(16)
+#define TXCMSR_TXREQEND BIT_U32(0)
#define TXCMSCR 0x134
-#define TXCMSCR_CLSNERR (1 << 18)
-#define TXCMSCR_AXIERR (1 << 16)
-#define TXCMSCR_TXREQEND (1 << 0)
+#define TXCMSCR_CLSNERR BIT_U32(18)
+#define TXCMSCR_AXIERR BIT_U32(16)
+#define TXCMSCR_TXREQEND BIT_U32(0)
#define TXCMIER 0x138
-#define TXCMIER_CLSNERR (1 << 18)
-#define TXCMIER_AXIERR (1 << 16)
-#define TXCMIER_TXREQEND (1 << 0)
+#define TXCMIER_CLSNERR BIT_U32(18)
+#define TXCMIER_AXIERR BIT_U32(16)
+#define TXCMIER_TXREQEND BIT_U32(0)
#define TXCMADDRSET0R 0x140
#define TXCMPHDR 0x150
-#define TXCMPHDR_FMT (1 << 24) /* 0:SP 1:LP */
-#define TXCMPHDR_VC(n) (((n) & 0x3) << 22)
-#define TXCMPHDR_DT(n) (((n) & 0x3f) << 16)
-#define TXCMPHDR_DATA1(n) (((n) & 0xff) << 8)
-#define TXCMPHDR_DATA0(n) (((n) & 0xff) << 0)
+#define TXCMPHDR_FMT BIT_U32(24) /* 0:SP 1:LP */
+#define TXCMPHDR_VC_MASK GENMASK_U32(23, 22)
+#define TXCMPHDR_VC(n) FIELD_PREP(TXCMPHDR_VC_MASK, (n))
+#define TXCMPHDR_DT_MASK GENMASK_U32(21, 16)
+#define TXCMPHDR_DT(n) FIELD_PREP(TXCMPHDR_DT_MASK, (n))
+#define TXCMPHDR_DATA1_MASK GENMASK_U32(15, 8)
+#define TXCMPHDR_DATA1(n) FIELD_PREP(TXCMPHDR_DATA1_MASK, (n))
+#define TXCMPHDR_DATA0_MASK GENMASK_U32(7, 0)
+#define TXCMPHDR_DATA0(n) FIELD_PREP(TXCMPHDR_DATA0_MASK, (n))
#define TXCMPPD0R 0x160
#define TXCMPPD1R 0x164
#define TXCMPPD2R 0x168
#define TXCMPPD3R 0x16c
#define RXSETR 0x200
-#define RXSETR_CRCEN (((n) & 0xf) << 24)
-#define RXSETR_ECCEN (((n) & 0xf) << 16)
+#define RXSETR_CRCEN_MASK GENMASK_U32(27, 24)
+#define RXSETR_ECCEN_MASK GENMASK_U32(19, 16)
#define RXPSETR 0x210
-#define RXPSETR_LPPDACC (1 << 0)
+#define RXPSETR_LPPDACC BIT_U32(0)
#define RXPSR 0x220
-#define RXPSR_ECCERR1B (1 << 28)
-#define RXPSR_UEXTRGERR (1 << 25)
-#define RXPSR_RESPTOERR (1 << 24)
-#define RXPSR_OVRERR (1 << 23)
-#define RXPSR_AXIERR (1 << 22)
-#define RXPSR_CRCERR (1 << 21)
-#define RXPSR_WCERR (1 << 20)
-#define RXPSR_UEXDTERR (1 << 19)
-#define RXPSR_UEXPKTERR (1 << 18)
-#define RXPSR_ECCERR (1 << 17)
-#define RXPSR_MLFERR (1 << 16)
-#define RXPSR_RCVACK (1 << 14)
-#define RXPSR_RCVEOT (1 << 10)
-#define RXPSR_RCVAKE (1 << 9)
-#define RXPSR_RCVRESP (1 << 8)
-#define RXPSR_BTAREQEND (1 << 0)
+#define RXPSR_ECCERR1B BIT_U32(28)
+#define RXPSR_UEXTRGERR BIT_U32(25)
+#define RXPSR_RESPTOERR BIT_U32(24)
+#define RXPSR_OVRERR BIT_U32(23)
+#define RXPSR_AXIERR BIT_U32(22)
+#define RXPSR_CRCERR BIT_U32(21)
+#define RXPSR_WCERR BIT_U32(20)
+#define RXPSR_UEXDTERR BIT_U32(19)
+#define RXPSR_UEXPKTERR BIT_U32(18)
+#define RXPSR_ECCERR BIT_U32(17)
+#define RXPSR_MLFERR BIT_U32(16)
+#define RXPSR_RCVACK BIT_U32(14)
+#define RXPSR_RCVEOT BIT_U32(10)
+#define RXPSR_RCVAKE BIT_U32(9)
+#define RXPSR_RCVRESP BIT_U32(8)
+#define RXPSR_BTAREQEND BIT_U32(0)
#define RXPSCR 0x224
-#define RXPSCR_ECCERR1B (1 << 28)
-#define RXPSCR_UEXTRGERR (1 << 25)
-#define RXPSCR_RESPTOERR (1 << 24)
-#define RXPSCR_OVRERR (1 << 23)
-#define RXPSCR_AXIERR (1 << 22)
-#define RXPSCR_CRCERR (1 << 21)
-#define RXPSCR_WCERR (1 << 20)
-#define RXPSCR_UEXDTERR (1 << 19)
-#define RXPSCR_UEXPKTERR (1 << 18)
-#define RXPSCR_ECCERR (1 << 17)
-#define RXPSCR_MLFERR (1 << 16)
-#define RXPSCR_RCVACK (1 << 14)
-#define RXPSCR_RCVEOT (1 << 10)
-#define RXPSCR_RCVAKE (1 << 9)
-#define RXPSCR_RCVRESP (1 << 8)
-#define RXPSCR_BTAREQEND (1 << 0)
+#define RXPSCR_ECCERR1B BIT_U32(28)
+#define RXPSCR_UEXTRGERR BIT_U32(25)
+#define RXPSCR_RESPTOERR BIT_U32(24)
+#define RXPSCR_OVRERR BIT_U32(23)
+#define RXPSCR_AXIERR BIT_U32(22)
+#define RXPSCR_CRCERR BIT_U32(21)
+#define RXPSCR_WCERR BIT_U32(20)
+#define RXPSCR_UEXDTERR BIT_U32(19)
+#define RXPSCR_UEXPKTERR BIT_U32(18)
+#define RXPSCR_ECCERR BIT_U32(17)
+#define RXPSCR_MLFERR BIT_U32(16)
+#define RXPSCR_RCVACK BIT_U32(14)
+#define RXPSCR_RCVEOT BIT_U32(10)
+#define RXPSCR_RCVAKE BIT_U32(9)
+#define RXPSCR_RCVRESP BIT_U32(8)
+#define RXPSCR_BTAREQEND BIT_U32(0)
#define RXPIER 0x228
-#define RXPIER_ECCERR1B (1 << 28)
-#define RXPIER_UEXTRGERR (1 << 25)
-#define RXPIER_RESPTOERR (1 << 24)
-#define RXPIER_OVRERR (1 << 23)
-#define RXPIER_AXIERR (1 << 22)
-#define RXPIER_CRCERR (1 << 21)
-#define RXPIER_WCERR (1 << 20)
-#define RXPIER_UEXDTERR (1 << 19)
-#define RXPIER_UEXPKTERR (1 << 18)
-#define RXPIER_ECCERR (1 << 17)
-#define RXPIER_MLFERR (1 << 16)
-#define RXPIER_RCVACK (1 << 14)
-#define RXPIER_RCVEOT (1 << 10)
-#define RXPIER_RCVAKE (1 << 9)
-#define RXPIER_RCVRESP (1 << 8)
-#define RXPIER_BTAREQEND (1 << 0)
+#define RXPIER_ECCERR1B BIT_U32(28)
+#define RXPIER_UEXTRGERR BIT_U32(25)
+#define RXPIER_RESPTOERR BIT_U32(24)
+#define RXPIER_OVRERR BIT_U32(23)
+#define RXPIER_AXIERR BIT_U32(22)
+#define RXPIER_CRCERR BIT_U32(21)
+#define RXPIER_WCERR BIT_U32(20)
+#define RXPIER_UEXDTERR BIT_U32(19)
+#define RXPIER_UEXPKTERR BIT_U32(18)
+#define RXPIER_ECCERR BIT_U32(17)
+#define RXPIER_MLFERR BIT_U32(16)
+#define RXPIER_RCVACK BIT_U32(14)
+#define RXPIER_RCVEOT BIT_U32(10)
+#define RXPIER_RCVAKE BIT_U32(9)
+#define RXPIER_RCVRESP BIT_U32(8)
+#define RXPIER_BTAREQEND BIT_U32(0)
#define RXPADDRSET0R 0x230
#define RXPSIZESETR 0x238
-#define RXPSIZESETR_SIZE(n) (((n) & 0xf) << 3)
+#define RXPSIZESETR_SIZE_MASK GENMASK_U32(6, 3)
#define RXPHDR 0x240
-#define RXPHDR_FMT (1 << 24) /* 0:SP 1:LP */
-#define RXPHDR_VC(n) (((n) & 0x3) << 22)
-#define RXPHDR_DT(n) (((n) & 0x3f) << 16)
-#define RXPHDR_DATA1(n) (((n) & 0xff) << 8)
-#define RXPHDR_DATA0(n) (((n) & 0xff) << 0)
+#define RXPHDR_FMT BIT_U32(24) /* 0:SP 1:LP */
+#define RXPHDR_VC_MASK GENMASK_U32(23, 22)
+#define RXPHDR_DT_MASK GENMASK_U32(21, 16)
+#define RXPHDR_DATA1_MASK GENMASK_U32(15, 8)
+#define RXPHDR_DATA0_MASK GENMASK_U32(7, 0)
#define RXPPD0R 0x250
#define RXPPD1R 0x254
#define RXPPD2R 0x258
#define RXPPD3R 0x25c
#define AKEPR 0x300
-#define AKEPR_VC(n) (((n) & 0x3) << 22)
-#define AKEPR_DT(n) (((n) & 0x3f) << 16)
-#define AKEPR_ERRRPT(n) (((n) & 0xffff) << 0)
+#define AKEPR_VC_MASK GENMASK_U32(23, 22)
+#define AKEPR_DT_MASK GENMASK_U32(21, 16)
+#define AKEPR_ERRRPT_MASK GENMASK_U32(15, 0)
#define RXRESPTOSETR 0x400
#define TACR 0x500
#define TASR 0x510
#define TASCR 0x514
#define TAIER 0x518
#define TOSR 0x610
-#define TOSR_TATO (1 << 2)
-#define TOSR_LRXHTO (1 << 1)
-#define TOSR_HRXTO (1 << 0)
+#define TOSR_TATO BIT_U32(2)
+#define TOSR_LRXHTO BIT_U32(1)
+#define TOSR_HRXTO BIT_U32(0)
#define TOSCR 0x614
-#define TOSCR_TATO (1 << 2)
-#define TOSCR_LRXHTO (1 << 1)
-#define TOSCR_HRXTO (1 << 0)
+#define TOSCR_TATO BIT_U32(2)
+#define TOSCR_LRXHTO BIT_U32(1)
+#define TOSCR_HRXTO BIT_U32(0)
/*
* Video Mode Register
*/
#define TXVMSETR 0x180
-#define TXVMSETR_SYNSEQ_PULSES (0 << 16)
-#define TXVMSETR_SYNSEQ_EVENTS (1 << 16)
-#define TXVMSETR_VSTPM (1 << 15)
-#define TXVMSETR_PIXWDTH (1 << 8)
-#define TXVMSETR_VSEN_EN (1 << 4)
-#define TXVMSETR_VSEN_DIS (0 << 4)
-#define TXVMSETR_HFPBPEN_EN (1 << 2)
-#define TXVMSETR_HFPBPEN_DIS (0 << 2)
-#define TXVMSETR_HBPBPEN_EN (1 << 1)
-#define TXVMSETR_HBPBPEN_DIS (0 << 1)
-#define TXVMSETR_HSABPEN_EN (1 << 0)
-#define TXVMSETR_HSABPEN_DIS (0 << 0)
+#define TXVMSETR_SYNSEQ_EVENTS BIT_U32(16) /* 0:Pulses 1:Events */
+#define TXVMSETR_VSTPM BIT_U32(15)
+#define TXVMSETR_PIXWDTH_MASK GENMASK_U32(10, 8)
+#define TXVMSETR_PIXWDTH BIT_U32(8) /* Only allowed value */
+#define TXVMSETR_VSEN BIT_U32(4)
+#define TXVMSETR_HFPBPEN BIT_U32(2)
+#define TXVMSETR_HBPBPEN BIT_U32(1)
+#define TXVMSETR_HSABPEN BIT_U32(0)
#define TXVMCR 0x190
-#define TXVMCR_VFCLR (1 << 12)
-#define TXVMCR_EN_VIDEO (1 << 0)
+#define TXVMCR_VFCLR BIT_U32(12)
+#define TXVMCR_EN_VIDEO BIT_U32(0)
#define TXVMSR 0x1a0
-#define TXVMSR_STR (1 << 16)
-#define TXVMSR_VFRDY (1 << 12)
-#define TXVMSR_ACT (1 << 8)
-#define TXVMSR_RDY (1 << 0)
+#define TXVMSR_STR BIT_U32(16)
+#define TXVMSR_VFRDY BIT_U32(12)
+#define TXVMSR_ACT BIT_U32(8)
+#define TXVMSR_RDY BIT_U32(0)
#define TXVMSCR 0x1a4
-#define TXVMSCR_STR (1 << 16)
+#define TXVMSCR_STR BIT_U32(16)
#define TXVMPSPHSETR 0x1c0
-#define TXVMPSPHSETR_DT_RGB16 (0x0e << 16)
-#define TXVMPSPHSETR_DT_RGB18 (0x1e << 16)
-#define TXVMPSPHSETR_DT_RGB18_LS (0x2e << 16)
-#define TXVMPSPHSETR_DT_RGB24 (0x3e << 16)
-#define TXVMPSPHSETR_DT_YCBCR16 (0x2c << 16)
+#define TXVMPSPHSETR_DT_MASK (0x3f << 16)
+#define TXVMPSPHSETR_DT_RGB16 FIELD_PREP(TXVMPSPHSETR_DT_MASK, 0x0e)
+#define TXVMPSPHSETR_DT_RGB18 FIELD_PREP(TXVMPSPHSETR_DT_MASK, 0x1e)
+#define TXVMPSPHSETR_DT_RGB18_LS FIELD_PREP(TXVMPSPHSETR_DT_MASK, 0x2e)
+#define TXVMPSPHSETR_DT_RGB24 FIELD_PREP(TXVMPSPHSETR_DT_MASK, 0x3e)
+#define TXVMPSPHSETR_DT_YCBCR16 FIELD_PREP(TXVMPSPHSETR_DT_MASK, 0x2c)
#define TXVMVPRMSET0R 0x1d0
-#define TXVMVPRMSET0R_HSPOL_HIG (0 << 17)
-#define TXVMVPRMSET0R_HSPOL_LOW (1 << 17)
-#define TXVMVPRMSET0R_VSPOL_HIG (0 << 16)
-#define TXVMVPRMSET0R_VSPOL_LOW (1 << 16)
-#define TXVMVPRMSET0R_CSPC_RGB (0 << 4)
-#define TXVMVPRMSET0R_CSPC_YCbCr (1 << 4)
-#define TXVMVPRMSET0R_BPP_16 (0 << 0)
-#define TXVMVPRMSET0R_BPP_18 (1 << 0)
-#define TXVMVPRMSET0R_BPP_24 (2 << 0)
+#define TXVMVPRMSET0R_HSPOL_LOW BIT_U32(17) /* 0:High 1:Low */
+#define TXVMVPRMSET0R_VSPOL_LOW BIT_U32(16) /* 0:High 1:Low */
+#define TXVMVPRMSET0R_CSPC_YCbCr BIT_U32(4) /* 0:RGB 1:YCbCr */
+#define TXVMVPRMSET0R_BPP_MASK GENMASK_U32(2, 0)
+#define TXVMVPRMSET0R_BPP_16 FIELD_PREP(TXVMVPRMSET0R_BPP_MASK, 0)
+#define TXVMVPRMSET0R_BPP_18 FIELD_PREP(TXVMVPRMSET0R_BPP_MASK, 1)
+#define TXVMVPRMSET0R_BPP_24 FIELD_PREP(TXVMVPRMSET0R_BPP_MASK, 2)
#define TXVMVPRMSET1R 0x1d4
-#define TXVMVPRMSET1R_VACTIVE(x) (((x) & 0x7fff) << 16)
-#define TXVMVPRMSET1R_VSA(x) (((x) & 0xfff) << 0)
+#define TXVMVPRMSET1R_VACTIVE_MASK GENMASK_U32(30, 16)
+#define TXVMVPRMSET1R_VACTIVE(n) FIELD_PREP(TXVMVPRMSET1R_VACTIVE_MASK, (n))
+#define TXVMVPRMSET1R_VSA_MASK GENMASK_U32(11, 0)
+#define TXVMVPRMSET1R_VSA(n) FIELD_PREP(TXVMVPRMSET1R_VSA_MASK, (n))
#define TXVMVPRMSET2R 0x1d8
-#define TXVMVPRMSET2R_VFP(x) (((x) & 0x1fff) << 16)
-#define TXVMVPRMSET2R_VBP(x) (((x) & 0x1fff) << 0)
+#define TXVMVPRMSET2R_VFP_MASK GENMASK_U32(28, 16)
+#define TXVMVPRMSET2R_VFP(n) FIELD_PREP(TXVMVPRMSET2R_VFP_MASK, (n))
+#define TXVMVPRMSET2R_VBP_MASK GENMASK_U32(12, 0)
+#define TXVMVPRMSET2R_VBP(n) FIELD_PREP(TXVMVPRMSET2R_VBP_MASK, (n))
#define TXVMVPRMSET3R 0x1dc
-#define TXVMVPRMSET3R_HACTIVE(x) (((x) & 0x7fff) << 16)
-#define TXVMVPRMSET3R_HSA(x) (((x) & 0xfff) << 0)
+#define TXVMVPRMSET3R_HACTIVE_MASK GENMASK_U32(30, 16)
+#define TXVMVPRMSET3R_HACTIVE(n) FIELD_PREP(TXVMVPRMSET3R_HACTIVE_MASK, (n))
+#define TXVMVPRMSET3R_HSA_MASK GENMASK_U32(11, 0)
+#define TXVMVPRMSET3R_HSA(n) FIELD_PREP(TXVMVPRMSET3R_HSA_MASK, (n))
#define TXVMVPRMSET4R 0x1e0
-#define TXVMVPRMSET4R_HFP(x) (((x) & 0x1fff) << 16)
-#define TXVMVPRMSET4R_HBP(x) (((x) & 0x1fff) << 0)
+#define TXVMVPRMSET4R_HFP_MASK GENMASK_U32(28, 16)
+#define TXVMVPRMSET4R_HFP(n) FIELD_PREP(TXVMVPRMSET4R_HFP_MASK, (n))
+#define TXVMVPRMSET4R_HBP_MASK GENMASK_U32(12, 0)
+#define TXVMVPRMSET4R_HBP(n) FIELD_PREP(TXVMVPRMSET4R_HBP_MASK, (n))
/*
* PHY-Protocol Interface (PPI) Registers
*/
#define PPISETR 0x700
-#define PPISETR_DLEN_MASK (0xf << 0)
-#define PPISETR_CLEN (1 << 8)
+#define PPISETR_DLEN_MASK GENMASK_U32(3, 0)
+#define PPISETR_CLEN BIT_U32(8)
#define PPICLCR 0x710
-#define PPICLCR_TXREQHS (1 << 8)
-#define PPICLCR_TXULPSEXT (1 << 1)
-#define PPICLCR_TXULPSCLK (1 << 0)
+#define PPICLCR_TXREQHS BIT_U32(8)
+#define PPICLCR_TXULPSEXT BIT_U32(1)
+#define PPICLCR_TXULPSCLK BIT_U32(0)
#define PPICLSR 0x720
-#define PPICLSR_HSTOLP (1 << 27)
-#define PPICLSR_TOHS (1 << 26)
-#define PPICLSR_STPST (1 << 0)
+#define PPICLSR_HSTOLP BIT_U32(27)
+#define PPICLSR_TOHS BIT_U32(26)
+#define PPICLSR_STPST BIT_U32(0)
#define PPICLSCR 0x724
-#define PPICLSCR_HSTOLP (1 << 27)
-#define PPICLSCR_TOHS (1 << 26)
+#define PPICLSCR_HSTOLP BIT_U32(27)
+#define PPICLSCR_TOHS BIT_U32(26)
#define PPIDL0SR 0x740
-#define PPIDL0SR_DIR (1 << 10)
-#define PPIDL0SR_STPST (1 << 6)
+#define PPIDL0SR_DIR BIT_U32(10)
+#define PPIDL0SR_STPST BIT_U32(6)
#define PPIDLSR 0x760
-#define PPIDLSR_STPST (0xf << 0)
+#define PPIDLSR_STPST GENMASK_U32(3, 0)
/*
* Clocks registers
*/
#define LPCLKSET 0x1000
-#define LPCLKSET_CKEN (1 << 8)
-#define LPCLKSET_LPCLKDIV(x) (((x) & 0x3f) << 0)
+#define LPCLKSET_CKEN BIT_U32(8)
+#define LPCLKSET_LPCLKDIV_MASK GENMASK_U32(5, 0)
#define CFGCLKSET 0x1004
-#define CFGCLKSET_CKEN (1 << 8)
-#define CFGCLKSET_CFGCLKDIV(x) (((x) & 0x3f) << 0)
+#define CFGCLKSET_CKEN BIT_U32(8)
+#define CFGCLKSET_CFGCLKDIV_MASK GENMASK_U32(5, 0)
#define DOTCLKDIV 0x1008
-#define DOTCLKDIV_CKEN (1 << 8)
-#define DOTCLKDIV_DOTCLKDIV(x) (((x) & 0x3f) << 0)
+#define DOTCLKDIV_CKEN BIT_U32(8)
+#define DOTCLKDIV_DOTCLKDIV_MASK GENMASK_U32(5, 0)
#define VCLKSET 0x100c
-#define VCLKSET_CKEN (1 << 16)
-#define VCLKSET_COLOR_RGB (0 << 8)
-#define VCLKSET_COLOR_YCC (1 << 8)
-#define VCLKSET_DIV_V3U(x) (((x) & 0x3) << 4)
-#define VCLKSET_DIV_V4H(x) (((x) & 0x7) << 4)
-#define VCLKSET_BPP_16 (0 << 2)
-#define VCLKSET_BPP_18 (1 << 2)
-#define VCLKSET_BPP_18L (2 << 2)
-#define VCLKSET_BPP_24 (3 << 2)
-#define VCLKSET_LANE(x) (((x) & 0x3) << 0)
+#define VCLKSET_CKEN BIT_U32(16)
+#define VCLKSET_COLOR_YCC BIT_U32(8) /* 0:RGB 1:YCbCr */
+#define VCLKSET_DIV_V3U_MASK GENMASK_U32(5, 4)
+#define VCLKSET_DIV_V3U(n) FIELD_PREP(VCLKSET_DIV_V3U_MASK, (n))
+#define VCLKSET_DIV_V4H_MASK GENMASK_U32(6, 4)
+#define VCLKSET_DIV_V4H(n) FIELD_PREP(VCLKSET_DIV_V4H_MASK, (n))
+#define VCLKSET_BPP_MASK GENMASK_U32(3, 2)
+#define VCLKSET_BPP_16 FIELD_PREP(VCLKSET_BPP_MASK, 0)
+#define VCLKSET_BPP_18 FIELD_PREP(VCLKSET_BPP_MASK, 1)
+#define VCLKSET_BPP_18L FIELD_PREP(VCLKSET_BPP_MASK, 2)
+#define VCLKSET_BPP_24 FIELD_PREP(VCLKSET_BPP_MASK, 3)
+#define VCLKSET_LANE_MASK GENMASK_U32(1, 0)
+#define VCLKSET_LANE(n) FIELD_PREP(VCLKSET_LANE_MASK, (n))
#define VCLKEN 0x1010
-#define VCLKEN_CKEN (1 << 0)
+#define VCLKEN_CKEN BIT_U32(0)
#define PHYSETUP 0x1014
-#define PHYSETUP_HSFREQRANGE(x) (((x) & 0x7f) << 16)
-#define PHYSETUP_HSFREQRANGE_MASK (0x7f << 16)
-#define PHYSETUP_CFGCLKFREQRANGE(x) (((x) & 0x3f) << 8)
-#define PHYSETUP_SHUTDOWNZ (1 << 1)
-#define PHYSETUP_RSTZ (1 << 0)
+#define PHYSETUP_HSFREQRANGE_MASK GENMASK_U32(22, 16)
+#define PHYSETUP_HSFREQRANGE(n) FIELD_PREP(PHYSETUP_HSFREQRANGE_MASK, (n))
+#define PHYSETUP_CFGCLKFREQRANGE_MASK GENMASK_U32(13, 8)
+#define PHYSETUP_SHUTDOWNZ BIT_U32(1)
+#define PHYSETUP_RSTZ BIT_U32(0)
#define CLOCKSET1 0x101c
-#define CLOCKSET1_LOCK_PHY (1 << 17)
-#define CLOCKSET1_CLKSEL (1 << 8)
-#define CLOCKSET1_CLKINSEL_EXTAL (0 << 2)
-#define CLOCKSET1_CLKINSEL_DIG (1 << 2)
-#define CLOCKSET1_CLKINSEL_DU (1 << 3)
-#define CLOCKSET1_SHADOW_CLEAR (1 << 1)
-#define CLOCKSET1_UPDATEPLL (1 << 0)
+#define CLOCKSET1_LOCK_PHY BIT_U32(17)
+#define CLOCKSET1_CLKSEL BIT_U32(8)
+#define CLOCKSET1_CLKINSEL_MASK GENMASK_U32(3, 2)
+#define CLOCKSET1_CLKINSEL_EXTAL FIELD_PREP(CLOCKSET1_CLKINSEL_MASK, 0)
+#define CLOCKSET1_CLKINSEL_DIG FIELD_PREP(CLOCKSET1_CLKINSEL_MASK, 1)
+#define CLOCKSET1_CLKINSEL_DU FIELD_PREP(CLOCKSET1_CLKINSEL_MASK, 2)
+#define CLOCKSET1_SHADOW_CLEAR BIT_U32(1)
+#define CLOCKSET1_UPDATEPLL BIT_U32(0)
#define CLOCKSET2 0x1020
-#define CLOCKSET2_M(x) (((x) & 0xfff) << 16)
-#define CLOCKSET2_VCO_CNTRL(x) (((x) & 0x3f) << 8)
-#define CLOCKSET2_N(x) (((x) & 0xf) << 0)
+#define CLOCKSET2_M_MASK GENMASK_U32(27, 16)
+#define CLOCKSET2_M(n) FIELD_PREP(CLOCKSET2_M_MASK, (n))
+#define CLOCKSET2_VCO_CNTRL_MASK GENMASK_U32(13, 8)
+#define CLOCKSET2_VCO_CNTRL(n) FIELD_PREP(CLOCKSET2_VCO_CNTRL_MASK, (n))
+#define CLOCKSET2_N_MASK GENMASK_U32(3, 0)
+#define CLOCKSET2_N(n) FIELD_PREP(CLOCKSET2_N_MASK, (n))
#define CLOCKSET3 0x1024
-#define CLOCKSET3_PROP_CNTRL(x) (((x) & 0x3f) << 24)
-#define CLOCKSET3_INT_CNTRL(x) (((x) & 0x3f) << 16)
-#define CLOCKSET3_CPBIAS_CNTRL(x) (((x) & 0x7f) << 8)
-#define CLOCKSET3_GMP_CNTRL(x) (((x) & 0x3) << 0)
+#define CLOCKSET3_PROP_CNTRL_MASK GENMASK_U32(29, 24)
+#define CLOCKSET3_PROP_CNTRL(n) FIELD_PREP(CLOCKSET3_PROP_CNTRL_MASK, (n))
+#define CLOCKSET3_INT_CNTRL_MASK GENMASK_U32(21, 16)
+#define CLOCKSET3_INT_CNTRL(n) FIELD_PREP(CLOCKSET3_INT_CNTRL_MASK, (n))
+#define CLOCKSET3_CPBIAS_CNTRL_MASK GENMASK_U32(14, 8)
+#define CLOCKSET3_CPBIAS_CNTRL(n) FIELD_PREP(CLOCKSET3_CPBIAS_CNTRL_MASK, (n))
+#define CLOCKSET3_GMP_CNTRL_MASK GENMASK_U32(1, 0)
+#define CLOCKSET3_GMP_CNTRL(n) FIELD_PREP(CLOCKSET3_GMP_CNTRL_MASK, (n))
#define PHTW 0x1034
-#define PHTW_DWEN (1 << 24)
-#define PHTW_TESTDIN_DATA(x) (((x) & 0xff) << 16)
-#define PHTW_CWEN (1 << 8)
-#define PHTW_TESTDIN_CODE(x) (((x) & 0xff) << 0)
+#define PHTW_DWEN BIT_U32(24)
+#define PHTW_TESTDIN_DATA_MASK GENMASK_U32(23, 16)
+#define PHTW_CWEN BIT_U32(8)
+#define PHTW_TESTDIN_CODE_MASK GENMASK_U32(7, 0)
#define PHTR 0x1038
-#define PHTR_TEST (1 << 16)
+#define PHTR_TESTDOUT GENMASK_U32(23, 16)
+#define PHTR_TESTDOUT_TEST BIT_U32(16)
#define PHTC 0x103c
-#define PHTC_TESTCLR (1 << 0)
+#define PHTC_TESTCLR BIT_U32(0)
#endif /* __RCAR_MIPI_DSI_REGS_H__ */
diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig
index e57536fd6f4d..7f2ef7137ae5 100644
--- a/drivers/gpu/drm/renesas/rz-du/Kconfig
+++ b/drivers/gpu/drm/renesas/rz-du/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
config DRM_RZG2L_DU
tristate "DRM Support for RZ/G2L Display Unit"
- depends on ARCH_RZG2L || COMPILE_TEST
+ depends on ARCH_RENESAS || COMPILE_TEST
depends on DRM && OF
depends on VIDEO_RENESAS_VSP1
select DRM_CLIENT_SELECTION
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
index e1aa6a719529..0fef33a5a089 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
@@ -17,6 +17,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "rzg2l_du_drv.h"
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index d30f0983a53a..fdab71d51e2a 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -28,6 +28,7 @@
#include <drm/bridge/analogix_dp.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -330,38 +331,29 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
struct device_node *np = dev->of_node;
dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
- if (IS_ERR(dp->grf)) {
- DRM_DEV_ERROR(dev, "failed to get rockchip,grf property\n");
- return PTR_ERR(dp->grf);
- }
+ if (IS_ERR(dp->grf))
+ return dev_err_probe(dev, PTR_ERR(dp->grf),
+ "failed to get rockchip,grf property\n");
- dp->grfclk = devm_clk_get(dev, "grf");
- if (PTR_ERR(dp->grfclk) == -ENOENT) {
- dp->grfclk = NULL;
- } else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (IS_ERR(dp->grfclk)) {
- DRM_DEV_ERROR(dev, "failed to get grf clock\n");
- return PTR_ERR(dp->grfclk);
- }
+ dp->grfclk = devm_clk_get_optional(dev, "grf");
+ if (IS_ERR(dp->grfclk))
+ return dev_err_probe(dev, PTR_ERR(dp->grfclk),
+ "failed to get grf clock\n");
dp->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(dp->pclk)) {
- DRM_DEV_ERROR(dev, "failed to get pclk property\n");
- return PTR_ERR(dp->pclk);
- }
+ if (IS_ERR(dp->pclk))
+ return dev_err_probe(dev, PTR_ERR(dp->pclk),
+ "failed to get pclk property\n");
dp->rst = devm_reset_control_get(dev, "dp");
- if (IS_ERR(dp->rst)) {
- DRM_DEV_ERROR(dev, "failed to get dp reset control\n");
- return PTR_ERR(dp->rst);
- }
+ if (IS_ERR(dp->rst))
+ return dev_err_probe(dev, PTR_ERR(dp->rst),
+ "failed to get dp reset control\n");
dp->apbrst = devm_reset_control_get_optional(dev, "apb");
- if (IS_ERR(dp->apbrst)) {
- DRM_DEV_ERROR(dev, "failed to get apb reset control\n");
- return PTR_ERR(dp->apbrst);
- }
+ if (IS_ERR(dp->apbrst))
+ return dev_err_probe(dev, PTR_ERR(dp->apbrst),
+ "failed to get apb reset control\n");
return 0;
}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index b7e3f5dcf8d5..177e30445ee8 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -21,6 +21,7 @@
#include <drm/drm_bridge_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index 924fb1d3ece2..0dc3804051a9 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -11,6 +11,8 @@
#include <linux/iopoll.h>
#include <linux/reset.h>
+#include <drm/drm_print.h>
+
#include "cdn-dp-core.h"
#include "cdn-dp-reg.h"
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 5523911b990d..2dad6b7b61b2 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -24,6 +24,7 @@
#include <drm/bridge/dw_mipi_dsi.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "rockchip_drm_drv.h"
@@ -163,6 +164,11 @@
#define RK3288_DSI0_LCDC_SEL BIT(6)
#define RK3288_DSI1_LCDC_SEL BIT(9)
+#define RK3368_GRF_SOC_CON7 0x41c
+#define RK3368_DSI_FORCETXSTOPMODE (0xf << 7)
+#define RK3368_DSI_FORCERXMODE BIT(6)
+#define RK3368_DSI_TURNDISABLE BIT(5)
+
#define RK3399_GRF_SOC_CON20 0x6250
#define RK3399_DSI0_LCDC_SEL BIT(0)
#define RK3399_DSI1_LCDC_SEL BIT(4)
@@ -1528,6 +1534,18 @@ static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = {
{ /* sentinel */ }
};
+static const struct rockchip_dw_dsi_chip_data rk3368_chip_data[] = {
+ {
+ .reg = 0xff960000,
+ .lanecfg1_grf_reg = RK3368_GRF_SOC_CON7,
+ .lanecfg1 = FIELD_PREP_WM16_CONST((RK3368_DSI_TURNDISABLE |
+ RK3368_DSI_FORCETXSTOPMODE |
+ RK3368_DSI_FORCERXMODE), 0),
+ .max_data_lanes = 4,
+ },
+ { /* sentinel */ }
+};
+
static int rk3399_dphy_tx1rx1_init(struct phy *phy)
{
struct dw_mipi_dsi_rockchip *dsi = phy_get_drvdata(phy);
@@ -1688,6 +1706,9 @@ static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = {
.compatible = "rockchip,rk3288-mipi-dsi",
.data = &rk3288_chip_data,
}, {
+ .compatible = "rockchip,rk3368-mipi-dsi",
+ .data = &rk3368_chip_data,
+ }, {
.compatible = "rockchip,rk3399-mipi-dsi",
.data = &rk3399_chip_data,
}, {
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 7b613997bb50..727cdf768161 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -361,7 +361,7 @@ static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
regmap_write(hdmi->regmap, RK3228_GRF_SOC_CON2,
FIELD_PREP_WM16(RK3228_HDMI_SDAIN_MSK, 1) |
- FIELD_PREP_WM16(RK3328_HDMI_SCLIN_MSK, 1));
+ FIELD_PREP_WM16(RK3228_HDMI_SCLIN_MSK, 1));
}
static enum drm_connector_status
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index ed6e8f036f4b..c9fe6aa3e3e3 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/phy/phy.h>
+#include <linux/phy/phy-hdmi.h>
#include <linux/regmap.h>
#include <linux/workqueue.h>
@@ -38,21 +39,16 @@
#define RK3576_HDMI_HDCP14_MEM_EN BIT(15)
#define RK3576_VO0_GRF_SOC_CON8 0x0020
-#define RK3576_COLOR_FORMAT_MASK (0xf << 4)
-#define RK3576_COLOR_DEPTH_MASK (0xf << 8)
-#define RK3576_RGB (0 << 4)
-#define RK3576_YUV422 (0x1 << 4)
-#define RK3576_YUV444 (0x2 << 4)
-#define RK3576_YUV420 (0x3 << 4)
-#define RK3576_8BPC (0x0 << 8)
-#define RK3576_10BPC (0x6 << 8)
+#define RK3576_COLOR_DEPTH_MASK GENMASK(11, 8)
+#define RK3576_8BPC 0x0
+#define RK3576_10BPC 0x6
+#define RK3576_COLOR_FORMAT_MASK GENMASK(7, 4)
+#define RK3576_RGB 0x9
+#define RK3576_YUV422 0x1
+#define RK3576_YUV444 0x2
+#define RK3576_YUV420 0x3
#define RK3576_CECIN_MASK BIT(3)
-#define RK3576_VO0_GRF_SOC_CON12 0x0030
-#define RK3576_GRF_OSDA_DLYN (0xf << 12)
-#define RK3576_GRF_OSDA_DIV (0x7f << 1)
-#define RK3576_GRF_OSDA_DLY_EN BIT(0)
-
#define RK3576_VO0_GRF_SOC_CON14 0x0038
#define RK3576_I2S_SEL_MASK BIT(0)
#define RK3576_SPDIF_SEL_MASK BIT(1)
@@ -74,6 +70,12 @@
#define RK3588_HDMI1_LEVEL_INT BIT(24)
#define RK3588_GRF_VO1_CON3 0x000c
#define RK3588_GRF_VO1_CON6 0x0018
+#define RK3588_COLOR_DEPTH_MASK GENMASK(7, 4)
+#define RK3588_8BPC 0x0
+#define RK3588_10BPC 0x6
+#define RK3588_COLOR_FORMAT_MASK GENMASK(3, 0)
+#define RK3588_RGB 0x0
+#define RK3588_YUV420 0x3
#define RK3588_SCLIN_MASK BIT(9)
#define RK3588_SDAIN_MASK BIT(10)
#define RK3588_MODE_MASK BIT(11)
@@ -92,14 +94,16 @@ struct rockchip_hdmi_qp {
struct rockchip_encoder encoder;
struct dw_hdmi_qp *hdmi;
struct phy *phy;
- struct gpio_desc *enable_gpio;
+ struct gpio_desc *frl_enable_gpio;
struct delayed_work hpd_work;
int port_id;
const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops;
+ unsigned long long tmds_char_rate;
};
struct rockchip_hdmi_qp_ctrl_ops {
void (*io_init)(struct rockchip_hdmi_qp *hdmi);
+ void (*enc_init)(struct rockchip_hdmi_qp *hdmi, struct rockchip_crtc_state *state);
irqreturn_t (*irq_callback)(int irq, void *dev_id);
irqreturn_t (*hardirq_callback)(int irq, void *dev_id);
};
@@ -115,23 +119,15 @@ static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
{
struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
struct drm_crtc *crtc = encoder->crtc;
- unsigned long long rate;
/* Unconditionally switch to TMDS as FRL is not yet supported */
- gpiod_set_value(hdmi->enable_gpio, 1);
-
- if (crtc && crtc->state) {
- rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
- 8, HDMI_COLORSPACE_RGB);
- /*
- * FIXME: Temporary workaround to pass pixel clock rate
- * to the PHY driver until phy_configure_opts_hdmi
- * becomes available in the PHY API. See also the related
- * comment in rk_hdptx_phy_power_on() from
- * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
- */
- phy_set_bus_width(hdmi->phy, div_u64(rate, 100));
- }
+ gpiod_set_value(hdmi->frl_enable_gpio, 0);
+
+ if (!crtc || !crtc->state)
+ return;
+
+ if (hdmi->ctrl_ops->enc_init)
+ hdmi->ctrl_ops->enc_init(hdmi, to_rockchip_crtc_state(crtc->state));
}
static int
@@ -139,12 +135,29 @@ dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
+ struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ union phy_configure_opts phy_cfg = {};
+ int ret;
+
+ if (hdmi->tmds_char_rate == conn_state->hdmi.tmds_char_rate &&
+ s->output_bpc == conn_state->hdmi.output_bpc)
+ return 0;
+
+ phy_cfg.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+ phy_cfg.hdmi.bpc = conn_state->hdmi.output_bpc;
+
+ ret = phy_configure(hdmi->phy, &phy_cfg);
+ if (!ret) {
+ hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+ s->output_bpc = conn_state->hdmi.output_bpc;
+ } else {
+ dev_err(hdmi->dev, "Failed to configure phy: %d\n", ret);
+ }
- s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
- s->output_type = DRM_MODE_CONNECTOR_HDMIA;
-
- return 0;
+ return ret;
}
static const struct
@@ -375,15 +388,45 @@ static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
}
+static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi,
+ struct rockchip_crtc_state *state)
+{
+ u32 val;
+
+ if (state->output_bpc == 10)
+ val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_10BPC);
+ else
+ val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_8BPC);
+
+ regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON8, val);
+}
+
+static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi,
+ struct rockchip_crtc_state *state)
+{
+ u32 val;
+
+ if (state->output_bpc == 10)
+ val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_10BPC);
+ else
+ val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_8BPC);
+
+ regmap_write(hdmi->vo_regmap,
+ hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
+ val);
+}
+
static const struct rockchip_hdmi_qp_ctrl_ops rk3576_hdmi_ctrl_ops = {
.io_init = dw_hdmi_qp_rk3576_io_init,
- .irq_callback = dw_hdmi_qp_rk3576_irq,
+ .enc_init = dw_hdmi_qp_rk3576_enc_init,
+ .irq_callback = dw_hdmi_qp_rk3576_irq,
.hardirq_callback = dw_hdmi_qp_rk3576_hardirq,
};
static const struct rockchip_hdmi_qp_ctrl_ops rk3588_hdmi_ctrl_ops = {
.io_init = dw_hdmi_qp_rk3588_io_init,
- .irq_callback = dw_hdmi_qp_rk3588_irq,
+ .enc_init = dw_hdmi_qp_rk3588_enc_init,
+ .irq_callback = dw_hdmi_qp_rk3588_irq,
.hardirq_callback = dw_hdmi_qp_rk3588_hardirq,
};
@@ -429,14 +472,15 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
void *data)
{
struct platform_device *pdev = to_platform_device(dev);
+ struct dw_hdmi_qp_plat_data plat_data = {};
const struct rockchip_hdmi_qp_cfg *cfg;
- struct dw_hdmi_qp_plat_data plat_data;
struct drm_device *drm = data;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct rockchip_hdmi_qp *hdmi;
struct resource *res;
struct clk_bulk_data *clks;
+ struct clk *ref_clk;
int ret, irq, i;
if (!pdev->dev.of_node)
@@ -455,10 +499,8 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
return -ENODEV;
if (!cfg->ctrl_ops || !cfg->ctrl_ops->io_init ||
- !cfg->ctrl_ops->irq_callback || !cfg->ctrl_ops->hardirq_callback) {
- dev_err(dev, "Missing platform ctrl ops\n");
- return -ENODEV;
- }
+ !cfg->ctrl_ops->irq_callback || !cfg->ctrl_ops->hardirq_callback)
+ return dev_err_probe(dev, -ENODEV, "Missing platform ctrl ops\n");
hdmi->ctrl_ops = cfg->ctrl_ops;
hdmi->dev = &pdev->dev;
@@ -471,13 +513,13 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
break;
}
}
- if (hdmi->port_id < 0) {
- dev_err(hdmi->dev, "Failed to match HDMI port ID\n");
- return hdmi->port_id;
- }
+ if (hdmi->port_id < 0)
+ return dev_err_probe(hdmi->dev, hdmi->port_id,
+ "Failed to match HDMI port ID\n");
plat_data.phy_ops = cfg->phy_ops;
plat_data.phy_data = hdmi;
+ plat_data.max_bpc = 10;
encoder = &hdmi->encoder.encoder;
encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
@@ -495,39 +537,38 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,grf");
- if (IS_ERR(hdmi->regmap)) {
- dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
- return PTR_ERR(hdmi->regmap);
- }
+ if (IS_ERR(hdmi->regmap))
+ return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->regmap),
+ "Unable to get rockchip,grf\n");
hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"rockchip,vo-grf");
- if (IS_ERR(hdmi->vo_regmap)) {
- dev_err(hdmi->dev, "Unable to get rockchip,vo-grf\n");
- return PTR_ERR(hdmi->vo_regmap);
- }
+ if (IS_ERR(hdmi->vo_regmap))
+ return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->vo_regmap),
+ "Unable to get rockchip,vo-grf\n");
ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks);
- if (ret < 0) {
- dev_err(hdmi->dev, "Failed to get clocks: %d\n", ret);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(hdmi->dev, ret, "Failed to get clocks\n");
- hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
- GPIOD_OUT_HIGH);
- if (IS_ERR(hdmi->enable_gpio)) {
- ret = PTR_ERR(hdmi->enable_gpio);
- dev_err(hdmi->dev, "Failed to request enable GPIO: %d\n", ret);
- return ret;
- }
+ ref_clk = clk_get(hdmi->dev, "ref");
+ if (IS_ERR(ref_clk))
+ return dev_err_probe(hdmi->dev, PTR_ERR(ref_clk),
+ "Failed to get ref clock\n");
+
+ plat_data.ref_clk_rate = clk_get_rate(ref_clk);
+ clk_put(ref_clk);
+
+ hdmi->frl_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "frl-enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(hdmi->frl_enable_gpio))
+ return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->frl_enable_gpio),
+ "Failed to request FRL enable GPIO\n");
hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
- if (IS_ERR(hdmi->phy)) {
- ret = PTR_ERR(hdmi->phy);
- if (ret != -EPROBE_DEFER)
- dev_err(hdmi->dev, "failed to get phy: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(hdmi->phy))
+ return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->phy),
+ "Failed to get phy\n");
cfg->ctrl_ops->io_init(hdmi);
@@ -537,6 +578,10 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
if (plat_data.main_irq < 0)
return plat_data.main_irq;
+ plat_data.cec_irq = platform_get_irq_byname(pdev, "cec");
+ if (plat_data.cec_irq < 0)
+ return plat_data.cec_irq;
+
irq = platform_get_irq_byname(pdev, "hpd");
if (irq < 0)
return irq;
@@ -556,17 +601,15 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
if (IS_ERR(hdmi->hdmi)) {
- ret = PTR_ERR(hdmi->hdmi);
drm_encoder_cleanup(encoder);
- return ret;
+ return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmi),
+ "Failed to bind dw-hdmi-qp");
}
connector = drm_bridge_connector_init(drm, encoder);
- if (IS_ERR(connector)) {
- ret = PTR_ERR(connector);
- dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(connector))
+ return dev_err_probe(hdmi->dev, PTR_ERR(connector),
+ "Failed to init bridge connector\n");
return drm_connector_attach_encoder(connector, encoder);
}
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index f24827dc1421..9f7a8cf0ab44 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -22,6 +22,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
index ae4a5ac2299a..997429115068 100644
--- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c
+++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c
@@ -10,6 +10,7 @@
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index eb77bde9f628..3099408e9d05 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -22,6 +22,7 @@
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -96,6 +97,9 @@ void rockchip_drm_dma_init_device(struct drm_device *drm_dev,
private->iommu_dev = ERR_PTR(-ENODEV);
else if (!private->iommu_dev)
private->iommu_dev = dev;
+
+ if (!IS_ERR(private->iommu_dev))
+ drm_dev_set_dma_dev(drm_dev, private->iommu_dev);
}
static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 6330b883efc3..df9a8bff2e22 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -9,10 +9,12 @@
#include <linux/vmalloc.h>
#include <drm/drm.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/drm_vma_manager.h>
#include "rockchip_drm_drv.h"
@@ -403,13 +405,12 @@ int rockchip_gem_dumb_create(struct drm_file *file_priv,
struct drm_mode_create_dumb *args)
{
struct rockchip_gem_object *rk_obj;
- int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+ int ret;
- /*
- * align to 64 bytes since Mali requires it.
- */
- args->pitch = ALIGN(min_pitch, 64);
- args->size = args->pitch * args->height;
+ /* 64-byte alignment required by Mali */
+ ret = drm_mode_size_dumb(dev, args, SZ_64, 0);
+ if (ret)
+ return ret;
rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
&args->handle);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index ba6b0528d1e5..ad4ab894391a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -27,6 +27,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_self_refresh_helper.h>
#include <drm/drm_vblank.h>
@@ -826,8 +827,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
if (!crtc || WARN_ON(!fb))
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
@@ -1092,7 +1092,8 @@ static int vop_plane_atomic_async_check(struct drm_plane *plane,
if (!plane->state->fb)
return -EINVAL;
- crtc_state = drm_atomic_get_existing_crtc_state(state, new_plane_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state,
+ new_plane_state->crtc);
/* Special case for asynchronous cursor updates. */
if (!crtc_state)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index 7ec7bea5e38e..498df0ce4680 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -29,6 +29,7 @@
#include <drm/drm_flip_work.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -101,7 +102,7 @@ enum vop2_afbc_format {
VOP2_AFBC_FMT_INVALID = -1,
};
-#define VOP2_MAX_DCLK_RATE 600000000
+#define VOP2_MAX_DCLK_RATE 600000000UL
/*
* bus-format types.
@@ -1003,6 +1004,8 @@ static int vop2_plane_atomic_check(struct drm_plane *plane,
struct drm_rect *src = &pstate->src;
int min_scale = FRAC_16_16(1, 8);
int max_scale = FRAC_16_16(8, 1);
+ int src_x, src_w, src_h;
+ int dest_w, dest_h;
int format;
int ret;
@@ -1013,7 +1016,7 @@ static int vop2_plane_atomic_check(struct drm_plane *plane,
vop2 = vp->vop2;
vop2_data = vop2->data;
- cstate = drm_atomic_get_existing_crtc_state(pstate->state, crtc);
+ cstate = drm_atomic_get_new_crtc_state(pstate->state, crtc);
if (WARN_ON(!cstate))
return -EINVAL;
@@ -1030,22 +1033,25 @@ static int vop2_plane_atomic_check(struct drm_plane *plane,
if (format < 0)
return format;
- if (drm_rect_width(src) >> 16 < 4 || drm_rect_height(src) >> 16 < 4 ||
- drm_rect_width(dest) < 4 || drm_rect_height(dest) < 4) {
- drm_err(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
- drm_rect_width(src) >> 16, drm_rect_height(src) >> 16,
- drm_rect_width(dest), drm_rect_height(dest));
- pstate->visible = false;
- return 0;
+ /* Co-ordinates have now been clipped */
+ src_x = src->x1 >> 16;
+ src_w = drm_rect_width(src) >> 16;
+ src_h = drm_rect_height(src) >> 16;
+ dest_w = drm_rect_width(dest);
+ dest_h = drm_rect_height(dest);
+
+ if (src_w < 4 || src_h < 4 || dest_w < 4 || dest_h < 4) {
+ drm_dbg_kms(vop2->drm, "Invalid size: %dx%d->%dx%d, min size is 4x4\n",
+ src_w, src_h, dest_w, dest_h);
+ return -EINVAL;
}
- if (drm_rect_width(src) >> 16 > vop2_data->max_input.width ||
- drm_rect_height(src) >> 16 > vop2_data->max_input.height) {
- drm_err(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
- drm_rect_width(src) >> 16,
- drm_rect_height(src) >> 16,
- vop2_data->max_input.width,
- vop2_data->max_input.height);
+ if (src_w > vop2_data->max_input.width ||
+ src_h > vop2_data->max_input.height) {
+ drm_dbg_kms(vop2->drm, "Invalid source: %dx%d. max input: %dx%d\n",
+ src_w, src_h,
+ vop2_data->max_input.width,
+ vop2_data->max_input.height);
return -EINVAL;
}
@@ -1053,8 +1059,8 @@ static int vop2_plane_atomic_check(struct drm_plane *plane,
* Src.x1 can be odd when do clip, but yuv plane start point
* need align with 2 pixel.
*/
- if (fb->format->is_yuv && ((pstate->src.x1 >> 16) % 2)) {
- drm_err(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
+ if (fb->format->is_yuv && src_x % 2) {
+ drm_dbg_kms(vop2->drm, "Invalid Source: Yuv format not support odd xpos\n");
return -EINVAL;
}
@@ -1140,7 +1146,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
struct vop2 *vop2 = win->vop2;
struct drm_framebuffer *fb = pstate->fb;
u32 bpp = vop2_get_bpp(fb->format);
- u32 actual_w, actual_h, dsp_w, dsp_h;
+ u32 src_w, src_h, dsp_w, dsp_h;
u32 act_info, dsp_info;
u32 format;
u32 afbc_format;
@@ -1204,8 +1210,8 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
uv_mst = rk_obj->dma_addr + offset + fb->offsets[1];
}
- actual_w = drm_rect_width(src) >> 16;
- actual_h = drm_rect_height(src) >> 16;
+ src_w = drm_rect_width(src) >> 16;
+ src_h = drm_rect_height(src) >> 16;
dsp_w = drm_rect_width(dest);
if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
@@ -1215,7 +1221,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
dsp_w = adjusted_mode->hdisplay - dest->x1;
if (dsp_w < 4)
dsp_w = 4;
- actual_w = dsp_w * actual_w / drm_rect_width(dest);
+ src_w = dsp_w * src_w / drm_rect_width(dest);
}
dsp_h = drm_rect_height(dest);
@@ -1227,35 +1233,35 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
dsp_h = adjusted_mode->vdisplay - dest->y1;
if (dsp_h < 4)
dsp_h = 4;
- actual_h = dsp_h * actual_h / drm_rect_height(dest);
+ src_h = dsp_h * src_h / drm_rect_height(dest);
}
/*
* This is workaround solution for IC design:
- * esmart can't support scale down when actual_w % 16 == 1.
+ * esmart can't support scale down when src_w % 16 == 1.
*/
if (!(win->data->feature & WIN_FEATURE_AFBDC)) {
- if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
+ if (src_w > dsp_w && (src_w & 0xf) == 1) {
drm_dbg_kms(vop2->drm, "vp%d %s act_w[%d] MODE 16 == 1\n",
- vp->id, win->data->name, actual_w);
- actual_w -= 1;
+ vp->id, win->data->name, src_w);
+ src_w -= 1;
}
}
- if (afbc_en && actual_w % 4) {
- drm_dbg_kms(vop2->drm, "vp%d %s actual_w[%d] not 4 pixel aligned\n",
- vp->id, win->data->name, actual_w);
- actual_w = ALIGN_DOWN(actual_w, 4);
+ if (afbc_en && src_w % 4) {
+ drm_dbg_kms(vop2->drm, "vp%d %s src_w[%d] not 4 pixel aligned\n",
+ vp->id, win->data->name, src_w);
+ src_w = ALIGN_DOWN(src_w, 4);
}
- act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
+ act_info = (src_h - 1) << 16 | ((src_w - 1) & 0xffff);
dsp_info = (dsp_h - 1) << 16 | ((dsp_w - 1) & 0xffff);
format = vop2_convert_format(fb->format->format);
half_block_en = vop2_half_block_enable(pstate);
drm_dbg(vop2->drm, "vp%d update %s[%dx%d->%dx%d@%dx%d] fmt[%p4cc_%s] addr[%pad]\n",
- vp->id, win->data->name, actual_w, actual_h, dsp_w, dsp_h,
+ vp->id, win->data->name, src_w, src_h, dsp_w, dsp_h,
dest->x1, dest->y1,
&fb->format->format,
afbc_en ? "AFBC" : "", &yrgb_mst);
@@ -1284,7 +1290,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
if (fb->modifier & AFBC_FORMAT_MOD_YTR)
afbc_format |= (1 << 4);
- afbc_tile_num = ALIGN(actual_w, block_w) / block_w;
+ afbc_tile_num = ALIGN(src_w, block_w) / block_w;
/*
* AFBC pic_vir_width is count by pixel, this is different
@@ -1362,8 +1368,8 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
if (rotate_90 || rotate_270) {
act_info = swahw32(act_info);
- actual_w = drm_rect_height(src) >> 16;
- actual_h = drm_rect_width(src) >> 16;
+ src_w = drm_rect_height(src) >> 16;
+ src_h = drm_rect_width(src) >> 16;
}
vop2_win_write(win, VOP2_WIN_FORMAT, format);
@@ -1379,7 +1385,7 @@ static void vop2_plane_atomic_update(struct drm_plane *plane,
vop2_win_write(win, VOP2_WIN_UV_MST, uv_mst);
}
- vop2_setup_scale(vop2, win, actual_w, actual_h, dsp_w, dsp_h, fb->format->format);
+ vop2_setup_scale(vop2, win, src_w, src_h, dsp_w, dsp_h, fb->format->format);
if (!vop2_cluster_window(win))
vop2_plane_setup_color_key(plane, 0);
vop2_win_write(win, VOP2_WIN_ACT_INFO, act_info);
@@ -1737,36 +1743,42 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc,
* Switch to HDMI PHY PLL as DCLK source for display modes up
* to 4K@60Hz, if available, otherwise keep using the system CRU.
*/
- if ((vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) && clock <= VOP2_MAX_DCLK_RATE) {
- drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
- struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
-
- if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) {
- if (!vop2->pll_hdmiphy0)
+ if (vop2->pll_hdmiphy0 || vop2->pll_hdmiphy1) {
+ unsigned long max_dclk = DIV_ROUND_CLOSEST_ULL(VOP2_MAX_DCLK_RATE * 8,
+ vcstate->output_bpc);
+ if (clock <= max_dclk) {
+ drm_for_each_encoder_mask(encoder, crtc->dev, crtc_state->encoder_mask) {
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+ if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI0) {
+ if (!vop2->pll_hdmiphy0)
+ break;
+
+ if (!vp->dclk_src)
+ vp->dclk_src = clk_get_parent(vp->dclk);
+
+ ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0);
+ if (ret < 0)
+ drm_warn(vop2->drm,
+ "Could not switch to HDMI0 PHY PLL: %d\n",
+ ret);
break;
+ }
- if (!vp->dclk_src)
- vp->dclk_src = clk_get_parent(vp->dclk);
+ if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) {
+ if (!vop2->pll_hdmiphy1)
+ break;
- ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy0);
- if (ret < 0)
- drm_warn(vop2->drm,
- "Could not switch to HDMI0 PHY PLL: %d\n", ret);
- break;
- }
+ if (!vp->dclk_src)
+ vp->dclk_src = clk_get_parent(vp->dclk);
- if (rkencoder->crtc_endpoint_id == ROCKCHIP_VOP2_EP_HDMI1) {
- if (!vop2->pll_hdmiphy1)
+ ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1);
+ if (ret < 0)
+ drm_warn(vop2->drm,
+ "Could not switch to HDMI1 PHY PLL: %d\n",
+ ret);
break;
-
- if (!vp->dclk_src)
- vp->dclk_src = clk_get_parent(vp->dclk);
-
- ret = clk_set_parent(vp->dclk, vop2->pll_hdmiphy1);
- if (ret < 0)
- drm_warn(vop2->drm,
- "Could not switch to HDMI1 PHY PLL: %d\n", ret);
- break;
+ }
}
}
}
@@ -2647,6 +2659,12 @@ static int vop2_bind(struct device *dev, struct device *master, void *data)
if (IS_ERR(vop2->map))
return PTR_ERR(vop2->map);
+ /* Set the bounds for framebuffer creation */
+ drm->mode_config.min_width = 4;
+ drm->mode_config.min_height = 4;
+ drm->mode_config.max_width = vop2_data->max_input.width;
+ drm->mode_config.max_height = vop2_data->max_input.height;
+
ret = vop2_win_init(vop2);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index 2411260db51d..75f898a10cbc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -22,6 +22,7 @@
#include <drm/drm_bridge_connector.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
index 811020665120..5c0c6e2cc28d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -15,6 +15,7 @@
#include <drm/drm_bridge_connector.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
index 38c49030c7ab..cd8380f0eddc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -1369,6 +1369,25 @@ static const struct vop2_regs_dump rk3588_regs_dump[] = {
},
};
+/*
+ * phys_id is used to identify a main window(Cluster Win/Smart Win, not
+ * include the sub win of a cluster or the multi area) that can do overlay
+ * in main overlay stage.
+ */
+static struct vop2_win *vop2_find_win_by_phys_id(struct vop2 *vop2, uint8_t phys_id)
+{
+ struct vop2_win *win;
+ int i;
+
+ for (i = 0; i < vop2->data->win_size; i++) {
+ win = &vop2->win[i];
+ if (win->data->phys_id == phys_id)
+ return win;
+ }
+
+ return NULL;
+}
+
static unsigned long rk3568_set_intf_mux(struct vop2_video_port *vp, int id, u32 polflags)
{
struct vop2 *vop2 = vp->vop2;
@@ -1842,15 +1861,31 @@ static void vop2_parse_alpha(struct vop2_alpha_config *alpha_config,
alpha->dst_alpha_ctrl.bits.factor_mode = ALPHA_SRC_INVERSE;
}
-static int vop2_find_start_mixer_id_for_vp(struct vop2 *vop2, u8 port_id)
+static int vop2_find_start_mixer_id_for_vp(struct vop2_video_port *vp)
{
- struct vop2_video_port *vp;
- int used_layer = 0;
+ struct vop2 *vop2 = vp->vop2;
+ struct vop2_win *win;
+ u32 layer_sel = vop2->old_layer_sel;
+ u32 used_layer = 0;
+ unsigned long win_mask = vp->win_mask;
+ unsigned long phys_id;
+ bool match;
int i;
- for (i = 0; i < port_id; i++) {
- vp = &vop2->vps[i];
- used_layer += hweight32(vp->win_mask);
+ for (i = 0; i < 31; i += 4) {
+ match = false;
+ for_each_set_bit(phys_id, &win_mask, ROCKCHIP_VOP2_ESMART3) {
+ win = vop2_find_win_by_phys_id(vop2, phys_id);
+ if (win->data->layer_sel_id[vp->id] == ((layer_sel >> i) & 0xf)) {
+ match = true;
+ break;
+ }
+ }
+
+ if (!match)
+ used_layer += 1;
+ else
+ break;
}
return used_layer;
@@ -1935,7 +1970,7 @@ static void vop2_setup_alpha(struct vop2_video_port *vp)
u32 dst_global_alpha = DRM_BLEND_ALPHA_OPAQUE;
if (vop2->version <= VOP_VERSION_RK3588)
- mixer_id = vop2_find_start_mixer_id_for_vp(vop2, vp->id);
+ mixer_id = vop2_find_start_mixer_id_for_vp(vp);
else
mixer_id = 0;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index d1f788763318..219f8c2fa88e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -880,6 +880,7 @@ static const struct vop_data rk3368_vop = {
.win = rk3368_vop_win_data,
.win_size = ARRAY_SIZE(rk3368_vop_win_data),
.max_output = { 4096, 2160 },
+ .lut_size = 1024,
};
static const struct vop_intr rk3366_vop_intr = {
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index 5a4697f636f2..fe174a4857be 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -70,6 +70,7 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
entity->guilty = guilty;
entity->num_sched_list = num_sched_list;
entity->priority = priority;
+ entity->last_user = current->group_leader;
/*
* It's perfectly valid to initialize an entity without having a valid
* scheduler attached. It's just not valid to use the scheduler before it
@@ -172,26 +173,15 @@ int drm_sched_entity_error(struct drm_sched_entity *entity)
}
EXPORT_SYMBOL(drm_sched_entity_error);
+static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
+ struct dma_fence_cb *cb);
+
static void drm_sched_entity_kill_jobs_work(struct work_struct *wrk)
{
struct drm_sched_job *job = container_of(wrk, typeof(*job), work);
-
- drm_sched_fence_scheduled(job->s_fence, NULL);
- drm_sched_fence_finished(job->s_fence, -ESRCH);
- WARN_ON(job->s_fence->parent);
- job->sched->ops->free_job(job);
-}
-
-/* Signal the scheduler finished fence when the entity in question is killed. */
-static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
- struct dma_fence_cb *cb)
-{
- struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
- finish_cb);
+ struct dma_fence *f;
unsigned long index;
- dma_fence_put(f);
-
/* Wait for all dependencies to avoid data corruptions */
xa_for_each(&job->dependencies, index, f) {
struct drm_sched_fence *s_fence = to_drm_sched_fence(f);
@@ -219,6 +209,21 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
dma_fence_put(f);
}
+ drm_sched_fence_scheduled(job->s_fence, NULL);
+ drm_sched_fence_finished(job->s_fence, -ESRCH);
+ WARN_ON(job->s_fence->parent);
+ job->sched->ops->free_job(job);
+}
+
+/* Signal the scheduler finished fence when the entity in question is killed. */
+static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
+ struct dma_fence_cb *cb)
+{
+ struct drm_sched_job *job = container_of(cb, struct drm_sched_job,
+ finish_cb);
+
+ dma_fence_put(f);
+
INIT_WORK(&job->work, drm_sched_entity_kill_jobs_work);
schedule_work(&job->work);
}
@@ -302,7 +307,7 @@ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
/* For a killed process disallow further enqueueing of jobs. */
last_user = cmpxchg(&entity->last_user, current->group_leader, NULL);
- if ((!last_user || last_user == current->group_leader) &&
+ if (last_user == current->group_leader &&
(current->flags & PF_EXITING) && (current->exit_code == SIGKILL))
drm_sched_entity_kill(entity);
@@ -552,10 +557,11 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
drm_sched_rq_remove_entity(entity->rq, entity);
entity->rq = rq;
}
- spin_unlock(&entity->lock);
if (entity->num_sched_list == 1)
entity->sched_list = NULL;
+
+ spin_unlock(&entity->lock);
}
/**
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index c39f0245e3a9..1d4f1b822e7b 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -1237,8 +1237,13 @@ static void drm_sched_run_job_work(struct work_struct *w)
/* Find entity with a ready job */
entity = drm_sched_select_entity(sched);
- if (!entity)
- return; /* No more work */
+ if (!entity) {
+ /*
+ * Either no more work to do, or the next ready job needs more
+ * credits than the scheduler has currently available.
+ */
+ return;
+ }
sched_job = drm_sched_entity_pop_job(entity);
if (!sched_job) {
@@ -1315,7 +1320,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
sched->name = args->name;
sched->timeout = args->timeout;
sched->hang_limit = args->hang_limit;
- sched->timeout_wq = args->timeout_wq ? args->timeout_wq : system_wq;
+ sched->timeout_wq = args->timeout_wq ? args->timeout_wq : system_percpu_wq;
sched->score = args->score ? args->score : &sched->_score;
sched->dev = args->dev;
@@ -1420,7 +1425,7 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched)
struct drm_sched_rq *rq = sched->sched_rq[i];
spin_lock(&rq->lock);
- list_for_each_entry(s_entity, &rq->entities, list)
+ list_for_each_entry(s_entity, &rq->entities, list) {
/*
* Prevents reinsertion and marks job_queue as idle,
* it will be removed from the rq in drm_sched_entity_fini()
@@ -1441,8 +1446,15 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched)
* For now, this remains a potential race in all
* drivers that keep entities alive for longer than
* the scheduler.
+ *
+ * The READ_ONCE() is there to make the lockless read
+ * (warning about the lockless write below) slightly
+ * less broken...
*/
+ if (!READ_ONCE(s_entity->stopped))
+ dev_warn(sched->dev, "Tearing down scheduler with active entities!\n");
s_entity->stopped = true;
+ }
spin_unlock(&rq->lock);
kfree(sched->sched_rq[i]);
}
diff --git a/drivers/gpu/drm/scheduler/tests/sched_tests.h b/drivers/gpu/drm/scheduler/tests/sched_tests.h
index 7f31d35780cc..553d45abd057 100644
--- a/drivers/gpu/drm/scheduler/tests/sched_tests.h
+++ b/drivers/gpu/drm/scheduler/tests/sched_tests.h
@@ -31,9 +31,8 @@
*
* @base: DRM scheduler base class
* @test: Backpointer to owning the kunit test case
- * @lock: Lock to protect the simulated @hw_timeline, @job_list and @done_list
+ * @lock: Lock to protect the simulated @hw_timeline and @job_list
* @job_list: List of jobs submitted to the mock GPU
- * @done_list: List of jobs completed by the mock GPU
* @hw_timeline: Simulated hardware timeline has a @context, @next_seqno and
* @cur_seqno for implementing a struct dma_fence signaling the
* simulated job completion.
diff --git a/drivers/gpu/drm/sitronix/st7571-i2c.c b/drivers/gpu/drm/sitronix/st7571-i2c.c
index a6c4a6738ded..4e73c8b415d6 100644
--- a/drivers/gpu/drm/sitronix/st7571-i2c.c
+++ b/drivers/gpu/drm/sitronix/st7571-i2c.c
@@ -263,6 +263,7 @@ static int st7571_fb_clear_screen(struct st7571_device *st7571)
u32 npixels = st7571->ncols * round_up(st7571->nlines, ST7571_PAGE_HEIGHT) * st7571->bpp;
char pixelvalue = 0x00;
+ st7571_set_position(st7571, 0, 0);
for (int i = 0; i < npixels; i++)
regmap_bulk_write(st7571->regmap, ST7571_DATA_MODE, &pixelvalue, 1);
@@ -321,7 +322,7 @@ static void st7571_prepare_buffer_grayscale(struct st7571_device *st7571,
size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4;
memcpy(st7571->hwbuf, vmap->vaddr, size);
break;
- };
+ }
}
static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
diff --git a/drivers/gpu/drm/sitronix/st7586.c b/drivers/gpu/drm/sitronix/st7586.c
index a29672d84ede..b57ebf37a664 100644
--- a/drivers/gpu/drm/sitronix/st7586.c
+++ b/drivers/gpu/drm/sitronix/st7586.c
@@ -25,6 +25,7 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_print.h>
#include <drm/drm_rect.h>
/* controller-specific commands */
diff --git a/drivers/gpu/drm/sitronix/st7735r.c b/drivers/gpu/drm/sitronix/st7735r.c
index 1d60f6e5b3bc..c1f8228495f6 100644
--- a/drivers/gpu/drm/sitronix/st7735r.c
+++ b/drivers/gpu/drm/sitronix/st7735r.c
@@ -24,6 +24,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_print.h>
#define ST7735R_FRMCTR1 0xb1
#define ST7735R_FRMCTR2 0xb2
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index eec43d1a5595..96cf39320137 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -33,6 +33,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_modes.h>
#include <drm/drm_rect.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "ssd130x.h"
@@ -1016,15 +1017,9 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb,
dst_pitch = DIV_ROUND_UP(drm_rect_width(rect), 8);
- ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
- if (ret)
- return ret;
-
iosys_map_set_vaddr(&dst, buf);
drm_fb_xrgb8888_to_mono(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
- drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
-
ssd130x_update_rect(ssd130x, rect, buf, data_array);
return ret;
@@ -1048,15 +1043,9 @@ static int ssd132x_fb_blit_rect(struct drm_framebuffer *fb,
dst_pitch = drm_rect_width(rect);
- ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
- if (ret)
- return ret;
-
iosys_map_set_vaddr(&dst, buf);
drm_fb_xrgb8888_to_gray8(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
- drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
-
ssd132x_update_rect(ssd130x, rect, buf, data_array);
return ret;
@@ -1078,15 +1067,9 @@ static int ssd133x_fb_blit_rect(struct drm_framebuffer *fb,
dst_pitch = drm_format_info_min_pitch(fi, 0, drm_rect_width(rect));
- ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
- if (ret)
- return ret;
-
iosys_map_set_vaddr(&dst, data_array);
drm_fb_xrgb8888_to_rgb332(&dst, &dst_pitch, vmap, fb, rect, fmtcnv_state);
- drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
-
ssd133x_update_rect(ssd130x, rect, data_array, dst_pitch);
return ret;
@@ -1232,6 +1215,9 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane,
if (!drm_dev_enter(drm, &idx))
return;
+ if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE))
+ goto out_drm_dev_exit;
+
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
@@ -1245,6 +1231,9 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane,
&shadow_plane_state->fmtcnv_state);
}
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+
+out_drm_dev_exit:
drm_dev_exit(idx);
}
@@ -1267,6 +1256,9 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane,
if (!drm_dev_enter(drm, &idx))
return;
+ if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE))
+ goto out_drm_dev_exit;
+
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
@@ -1280,6 +1272,9 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane,
&shadow_plane_state->fmtcnv_state);
}
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+
+out_drm_dev_exit:
drm_dev_exit(idx);
}
@@ -1301,6 +1296,9 @@ static void ssd133x_primary_plane_atomic_update(struct drm_plane *plane,
if (!drm_dev_enter(drm, &idx))
return;
+ if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE))
+ goto out_drm_dev_exit;
+
drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
@@ -1313,6 +1311,9 @@ static void ssd133x_primary_plane_atomic_update(struct drm_plane *plane,
&shadow_plane_state->fmtcnv_state);
}
+ drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+
+out_drm_dev_exit:
drm_dev_exit(idx);
}
@@ -1393,7 +1394,7 @@ static void ssd130x_primary_plane_reset(struct drm_plane *plane)
{
struct ssd130x_plane_state *ssd130x_state;
- WARN_ON(plane->state);
+ drm_WARN_ON_ONCE(plane->dev, plane->state);
ssd130x_state = kzalloc(sizeof(*ssd130x_state), GFP_KERNEL);
if (!ssd130x_state)
@@ -1408,7 +1409,7 @@ static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_
struct ssd130x_plane_state *old_ssd130x_state;
struct ssd130x_plane_state *ssd130x_state;
- if (WARN_ON(!plane->state))
+ if (drm_WARN_ON_ONCE(plane->dev, !plane->state))
return NULL;
old_ssd130x_state = to_ssd130x_plane_state(plane->state);
@@ -1473,15 +1474,7 @@ static enum drm_mode_status ssd130x_crtc_mode_valid(struct drm_crtc *crtc,
{
struct ssd130x_device *ssd130x = drm_to_ssd130x(crtc->dev);
- if (mode->hdisplay != ssd130x->mode.hdisplay &&
- mode->vdisplay != ssd130x->mode.vdisplay)
- return MODE_ONE_SIZE;
- else if (mode->hdisplay != ssd130x->mode.hdisplay)
- return MODE_ONE_WIDTH;
- else if (mode->vdisplay != ssd130x->mode.vdisplay)
- return MODE_ONE_HEIGHT;
-
- return MODE_OK;
+ return drm_crtc_helper_mode_valid_fixed(crtc, mode, &ssd130x->mode);
}
static int ssd130x_crtc_atomic_check(struct drm_crtc *crtc,
@@ -1498,7 +1491,7 @@ static int ssd130x_crtc_atomic_check(struct drm_crtc *crtc,
if (ret)
return ret;
- ssd130x_state->data_array = kmalloc(ssd130x->width * pages, GFP_KERNEL);
+ ssd130x_state->data_array = kmalloc_array(ssd130x->width, pages, GFP_KERNEL);
if (!ssd130x_state->data_array)
return -ENOMEM;
@@ -1519,7 +1512,7 @@ static int ssd132x_crtc_atomic_check(struct drm_crtc *crtc,
if (ret)
return ret;
- ssd130x_state->data_array = kmalloc(columns * ssd130x->height, GFP_KERNEL);
+ ssd130x_state->data_array = kmalloc_array(columns, ssd130x->height, GFP_KERNEL);
if (!ssd130x_state->data_array)
return -ENOMEM;
@@ -1546,7 +1539,7 @@ static int ssd133x_crtc_atomic_check(struct drm_crtc *crtc,
pitch = drm_format_info_min_pitch(fi, 0, ssd130x->width);
- ssd130x_state->data_array = kmalloc(pitch * ssd130x->height, GFP_KERNEL);
+ ssd130x_state->data_array = kmalloc_array(pitch, ssd130x->height, GFP_KERNEL);
if (!ssd130x_state->data_array)
return -ENOMEM;
@@ -1558,7 +1551,7 @@ static void ssd130x_crtc_reset(struct drm_crtc *crtc)
{
struct ssd130x_crtc_state *ssd130x_state;
- WARN_ON(crtc->state);
+ drm_WARN_ON_ONCE(crtc->dev, crtc->state);
ssd130x_state = kzalloc(sizeof(*ssd130x_state), GFP_KERNEL);
if (!ssd130x_state)
@@ -1572,7 +1565,7 @@ static struct drm_crtc_state *ssd130x_crtc_duplicate_state(struct drm_crtc *crtc
struct ssd130x_crtc_state *old_ssd130x_state;
struct ssd130x_crtc_state *ssd130x_state;
- if (WARN_ON(!crtc->state))
+ if (drm_WARN_ON_ONCE(crtc->dev, !crtc->state))
return NULL;
old_ssd130x_state = to_ssd130x_crtc_state(crtc->state);
@@ -1740,20 +1733,8 @@ static const struct drm_encoder_funcs ssd130x_encoder_funcs = {
static int ssd130x_connector_get_modes(struct drm_connector *connector)
{
struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev);
- struct drm_display_mode *mode;
- struct device *dev = ssd130x->dev;
-
- mode = drm_mode_duplicate(connector->dev, &ssd130x->mode);
- if (!mode) {
- dev_err(dev, "Failed to duplicated mode\n");
- return 0;
- }
-
- drm_mode_probed_add(connector, mode);
- drm_set_preferred_mode(connector, mode->hdisplay, mode->vdisplay);
- /* There is only a single mode */
- return 1;
+ return drm_connector_helper_get_modes_fixed(connector, &ssd130x->mode);
}
static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = {
@@ -1887,10 +1868,14 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
mode->type = DRM_MODE_TYPE_DRIVER;
mode->clock = 1;
- mode->hdisplay = mode->htotal = ssd130x->width;
- mode->hsync_start = mode->hsync_end = ssd130x->width;
- mode->vdisplay = mode->vtotal = ssd130x->height;
- mode->vsync_start = mode->vsync_end = ssd130x->height;
+ mode->hdisplay = ssd130x->width;
+ mode->htotal = ssd130x->width;
+ mode->hsync_start = ssd130x->width;
+ mode->hsync_end = ssd130x->width;
+ mode->vdisplay = ssd130x->height;
+ mode->vtotal = ssd130x->height;
+ mode->vsync_start = ssd130x->height;
+ mode->vsync_end = ssd130x->height;
mode->width_mm = 27;
mode->height_mm = 27;
diff --git a/drivers/gpu/drm/sti/sti_cursor.c b/drivers/gpu/drm/sti/sti_cursor.c
index c59fcb4dca32..4e12a465be7f 100644
--- a/drivers/gpu/drm/sti/sti_cursor.c
+++ b/drivers/gpu/drm/sti/sti_cursor.c
@@ -14,6 +14,7 @@
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "sti_compositor.h"
#include "sti_cursor.h"
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
index 5e9332df21df..f16345f01065 100644
--- a/drivers/gpu/drm/sti/sti_drv.c
+++ b/drivers/gpu/drm/sti/sti_drv.c
@@ -22,6 +22,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "sti_drv.h"
@@ -231,23 +232,15 @@ static const struct component_master_ops sti_ops = {
static int sti_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *node = dev->of_node;
- struct device_node *child_np;
- struct component_match *match = NULL;
+ int ret;
- dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
devm_of_platform_populate(dev);
- child_np = of_get_next_available_child(node, NULL);
-
- while (child_np) {
- drm_of_component_match_add(dev, &match, component_compare_of,
- child_np);
- child_np = of_get_next_available_child(node, child_np);
- }
-
- return component_master_add_with_match(dev, &sti_ops, match);
+ return drm_of_component_probe(dev, component_compare_of, &sti_ops);
}
static void sti_platform_remove(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/sti/sti_gdp.c b/drivers/gpu/drm/sti/sti_gdp.c
index f046f5f7ad25..1e5aa8c30645 100644
--- a/drivers/gpu/drm/sti/sti_gdp.c
+++ b/drivers/gpu/drm/sti/sti_gdp.c
@@ -16,6 +16,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "sti_compositor.h"
#include "sti_gdp.h"
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
index 2c015f563de9..b7397827889c 100644
--- a/drivers/gpu/drm/sti/sti_hda.c
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -779,6 +779,8 @@ static int sti_hda_probe(struct platform_device *pdev)
return PTR_ERR(hda->clk_hddac);
}
+ drm_bridge_add(&hda->bridge);
+
platform_set_drvdata(pdev, hda);
return component_add(&pdev->dev, &sti_hda_ops);
@@ -786,7 +788,10 @@ static int sti_hda_probe(struct platform_device *pdev)
static void sti_hda_remove(struct platform_device *pdev)
{
+ struct sti_hda *hda = platform_get_drvdata(pdev);
+
component_del(&pdev->dev, &sti_hda_ops);
+ drm_bridge_remove(&hda->bridge);
}
static const struct of_device_id hda_of_match[] = {
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index 4e7c3d78b2b9..f8222e60b1e0 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -1459,6 +1459,7 @@ static int sti_hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, hdmi);
+ drm_bridge_add(&hdmi->bridge);
return component_add(&pdev->dev, &sti_hdmi_ops);
release_adapter:
@@ -1475,6 +1476,7 @@ static void sti_hdmi_remove(struct platform_device *pdev)
if (hdmi->audio_pdev)
platform_device_unregister(hdmi->audio_pdev);
component_del(&pdev->dev, &sti_hdmi_ops);
+ drm_bridge_remove(&hdmi->bridge);
}
struct platform_driver sti_hdmi_driver = {
diff --git a/drivers/gpu/drm/sti/sti_hqvdp.c b/drivers/gpu/drm/sti/sti_hqvdp.c
index b76606e9a82d..57ef4ba3554e 100644
--- a/drivers/gpu/drm/sti/sti_hqvdp.c
+++ b/drivers/gpu/drm/sti/sti_hqvdp.c
@@ -20,6 +20,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "sti_compositor.h"
#include "sti_drv.h"
diff --git a/drivers/gpu/drm/sti/sti_plane.c b/drivers/gpu/drm/sti/sti_plane.c
index 29e669ccec5b..948f947b5cad 100644
--- a/drivers/gpu/drm/sti/sti_plane.c
+++ b/drivers/gpu/drm/sti/sti_plane.c
@@ -12,6 +12,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include "sti_compositor.h"
#include "sti_drv.h"
diff --git a/drivers/gpu/drm/sti/sti_vtg.c b/drivers/gpu/drm/sti/sti_vtg.c
index ee81691b3203..ce6bc7e7b135 100644
--- a/drivers/gpu/drm/sti/sti_vtg.c
+++ b/drivers/gpu/drm/sti/sti_vtg.c
@@ -143,12 +143,17 @@ struct sti_vtg {
struct sti_vtg *of_vtg_find(struct device_node *np)
{
struct platform_device *pdev;
+ struct sti_vtg *vtg;
pdev = of_find_device_by_node(np);
if (!pdev)
return NULL;
- return (struct sti_vtg *)platform_get_drvdata(pdev);
+ vtg = platform_get_drvdata(pdev);
+
+ put_device(&pdev->dev);
+
+ return vtg;
}
static void vtg_reset(struct sti_vtg *vtg)
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c
index ab00d1a6140c..56d53ac3082d 100644
--- a/drivers/gpu/drm/stm/drv.c
+++ b/drivers/gpu/drm/stm/drv.c
@@ -25,6 +25,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_module.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_managed.h>
diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
index 2c7bc064bc66..58eae6804cc8 100644
--- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
+++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
@@ -274,8 +274,8 @@ static unsigned long dw_mipi_dsi_clk_recalc_rate(struct clk_hw *hw,
return (unsigned long)pll_out_khz * 1000;
}
-static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int dw_mipi_dsi_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct dw_mipi_dsi_stm *dsi = clk_to_dw_mipi_dsi_stm(hw);
unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
@@ -283,14 +283,14 @@ static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
DRM_DEBUG_DRIVER("\n");
- pll_in_khz = (unsigned int)(*parent_rate / 1000);
+ pll_in_khz = (unsigned int)(req->best_parent_rate / 1000);
/* Compute best pll parameters */
idf = 0;
ndiv = 0;
odf = 0;
- ret = dsi_pll_get_params(dsi, pll_in_khz, rate / 1000,
+ ret = dsi_pll_get_params(dsi, pll_in_khz, req->rate / 1000,
&idf, &ndiv, &odf);
if (ret)
DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
@@ -298,7 +298,9 @@ static long dw_mipi_dsi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
/* Get the adjusted pll out value */
pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
- return pll_out_khz * 1000;
+ req->rate = pll_out_khz * 1000;
+
+ return 0;
}
static int dw_mipi_dsi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -351,7 +353,7 @@ static const struct clk_ops dw_mipi_dsi_stm_clk_ops = {
.disable = dw_mipi_dsi_clk_disable,
.is_enabled = dw_mipi_dsi_clk_is_enabled,
.recalc_rate = dw_mipi_dsi_clk_recalc_rate,
- .round_rate = dw_mipi_dsi_clk_round_rate,
+ .determine_rate = dw_mipi_dsi_clk_determine_rate,
.set_rate = dw_mipi_dsi_clk_set_rate,
};
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index d1501e86a5b1..f7e847cfa38f 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -34,6 +34,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c
index 07788e8d3d83..fe38c0984b2b 100644
--- a/drivers/gpu/drm/stm/lvds.c
+++ b/drivers/gpu/drm/stm/lvds.c
@@ -682,8 +682,8 @@ static unsigned long lvds_pixel_clk_recalc_rate(struct clk_hw *hw,
return (unsigned long)lvds->pixel_clock_rate;
}
-static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int lvds_pixel_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct stm_lvds *lvds = container_of(hw, struct stm_lvds, lvds_ck_px);
unsigned int pll_in_khz, bdiv = 0, mdiv = 0, ndiv = 0;
@@ -703,7 +703,7 @@ static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate,
mode = list_first_entry(&connector->modes,
struct drm_display_mode, head);
- pll_in_khz = (unsigned int)(*parent_rate / 1000);
+ pll_in_khz = (unsigned int)(req->best_parent_rate / 1000);
if (lvds_is_dual_link(lvds->link_type))
multiplier = 2;
@@ -719,14 +719,16 @@ static long lvds_pixel_clk_round_rate(struct clk_hw *hw, unsigned long rate,
lvds->pixel_clock_rate = (unsigned long)pll_get_clkout_khz(pll_in_khz, bdiv, mdiv, ndiv)
* 1000 * multiplier / 7;
- return lvds->pixel_clock_rate;
+ req->rate = lvds->pixel_clock_rate;
+
+ return 0;
}
static const struct clk_ops lvds_pixel_clk_ops = {
.enable = lvds_pixel_clk_enable,
.disable = lvds_pixel_clk_disable,
.recalc_rate = lvds_pixel_clk_recalc_rate,
- .round_rate = lvds_pixel_clk_round_rate,
+ .determine_rate = lvds_pixel_clk_determine_rate,
};
static const struct clk_init_data clk_data = {
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
index 2dded3b828df..40405a52a073 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -23,6 +23,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "sun4i_backend.h"
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index c11dfb2739fa..8a409eee1dca 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -22,6 +22,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_module.h>
#include <drm/drm_of.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c
index 5ab1604f12dd..5e9c4b97c84c 100644
--- a/drivers/gpu/drm/sun4i/sun4i_frontend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c
@@ -19,6 +19,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
#include "sun4i_drv.h"
#include "sun4i_frontend.h"
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
index 12430b9d4e93..b1beadb9bb59 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
@@ -59,13 +59,15 @@ static unsigned long sun4i_ddc_calc_divider(unsigned long rate,
return best_rate;
}
-static long sun4i_ddc_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int sun4i_ddc_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct sun4i_ddc *ddc = hw_to_ddc(hw);
- return sun4i_ddc_calc_divider(rate, *prate, ddc->pre_div,
- ddc->m_offset, NULL, NULL);
+ req->rate = sun4i_ddc_calc_divider(req->rate, req->best_parent_rate,
+ ddc->pre_div, ddc->m_offset, NULL, NULL);
+
+ return 0;
}
static unsigned long sun4i_ddc_recalc_rate(struct clk_hw *hw,
@@ -101,7 +103,7 @@ static int sun4i_ddc_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops sun4i_ddc_ops = {
.recalc_rate = sun4i_ddc_recalc_rate,
- .round_rate = sun4i_ddc_round_rate,
+ .determine_rate = sun4i_ddc_determine_rate,
.set_rate = sun4i_ddc_set_rate,
};
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c b/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c
index 03d7de1911cd..4afb12bd5281 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon_dclk.c
@@ -67,8 +67,8 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
return parent_rate / val;
}
-static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int sun4i_dclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct sun4i_dclk *dclk = hw_to_dclk(hw);
struct sun4i_tcon *tcon = dclk->tcon;
@@ -77,7 +77,7 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
int i;
for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) {
- u64 ideal = (u64)rate * i;
+ u64 ideal = (u64)req->rate * i;
unsigned long rounded;
/*
@@ -99,17 +99,19 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
goto out;
}
- if (abs(rate - rounded / i) <
- abs(rate - best_parent / best_div)) {
+ if (abs(req->rate - rounded / i) <
+ abs(req->rate - best_parent / best_div)) {
best_parent = rounded;
best_div = i;
}
}
out:
- *parent_rate = best_parent;
+ req->best_parent_rate = best_parent;
- return best_parent / best_div;
+ req->rate = best_parent / best_div;
+
+ return 0;
}
static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -155,7 +157,7 @@ static const struct clk_ops sun4i_dclk_ops = {
.is_enabled = sun4i_dclk_is_enabled,
.recalc_rate = sun4i_dclk_recalc_rate,
- .round_rate = sun4i_dclk_round_rate,
+ .determine_rate = sun4i_dclk_determine_rate,
.set_rate = sun4i_dclk_set_rate,
.get_phase = sun4i_dclk_get_phase,
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.c b/drivers/gpu/drm/sun4i/sun8i_csc.c
index c100d29b1a89..ce81c12f511d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.c
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.c
@@ -3,11 +3,20 @@
* Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
*/
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
#include <drm/drm_print.h>
#include "sun8i_csc.h"
#include "sun8i_mixer.h"
+enum sun8i_csc_mode {
+ SUN8I_CSC_MODE_OFF,
+ SUN8I_CSC_MODE_YUV2RGB,
+ SUN8I_CSC_MODE_YVU2RGB,
+};
+
static const u32 ccsc_base[][2] = {
[CCSC_MIXER0_LAYOUT] = {CCSC00_OFFSET, CCSC01_OFFSET},
[CCSC_MIXER1_LAYOUT] = {CCSC10_OFFSET, CCSC11_OFFSET},
@@ -107,23 +116,28 @@ static const u32 yuv2rgb_de3[2][3][12] = {
},
};
-static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
- enum sun8i_csc_mode mode,
- enum drm_color_encoding encoding,
- enum drm_color_range range)
+static void sun8i_csc_setup(struct regmap *map, u32 base,
+ enum sun8i_csc_mode mode,
+ enum drm_color_encoding encoding,
+ enum drm_color_range range)
{
+ u32 base_reg, val;
const u32 *table;
- u32 base_reg;
int i;
table = yuv2rgb[range][encoding];
switch (mode) {
+ case SUN8I_CSC_MODE_OFF:
+ val = 0;
+ break;
case SUN8I_CSC_MODE_YUV2RGB:
+ val = SUN8I_CSC_CTRL_EN;
base_reg = SUN8I_CSC_COEFF(base, 0);
regmap_bulk_write(map, base_reg, table, 12);
break;
case SUN8I_CSC_MODE_YVU2RGB:
+ val = SUN8I_CSC_CTRL_EN;
for (i = 0; i < 12; i++) {
if ((i & 3) == 1)
base_reg = SUN8I_CSC_COEFF(base, i + 1);
@@ -135,28 +149,37 @@ static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
}
break;
default:
+ val = 0;
DRM_WARN("Wrong CSC mode specified.\n");
return;
}
+
+ regmap_write(map, SUN8I_CSC_CTRL(base), val);
}
-static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
- enum sun8i_csc_mode mode,
- enum drm_color_encoding encoding,
- enum drm_color_range range)
+static void sun8i_de3_ccsc_setup(struct regmap *map, int layer,
+ enum sun8i_csc_mode mode,
+ enum drm_color_encoding encoding,
+ enum drm_color_range range)
{
+ u32 addr, val, mask;
const u32 *table;
- u32 addr;
int i;
+ mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
table = yuv2rgb_de3[range][encoding];
switch (mode) {
+ case SUN8I_CSC_MODE_OFF:
+ val = 0;
+ break;
case SUN8I_CSC_MODE_YUV2RGB:
+ val = mask;
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0);
regmap_bulk_write(map, addr, table, 12);
break;
case SUN8I_CSC_MODE_YVU2RGB:
+ val = mask;
for (i = 0; i < 12; i++) {
if ((i & 3) == 1)
addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE,
@@ -173,67 +196,53 @@ static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
}
break;
default:
+ val = 0;
DRM_WARN("Wrong CSC mode specified.\n");
return;
}
-}
-
-static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable)
-{
- u32 val;
-
- if (enable)
- val = SUN8I_CSC_CTRL_EN;
- else
- val = 0;
-
- regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val);
-}
-
-static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable)
-{
- u32 val, mask;
-
- mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
-
- if (enable)
- val = mask;
- else
- val = 0;
regmap_update_bits(map, SUN50I_MIXER_BLEND_CSC_CTL(DE3_BLD_BASE),
mask, val);
}
-void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
- enum sun8i_csc_mode mode,
- enum drm_color_encoding encoding,
- enum drm_color_range range)
+static u32 sun8i_csc_get_mode(struct drm_plane_state *state)
{
- u32 base;
+ const struct drm_format_info *format;
- if (mixer->cfg->de_type == SUN8I_MIXER_DE3) {
- sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer,
- mode, encoding, range);
- return;
- }
+ if (!state->crtc || !state->visible)
+ return SUN8I_CSC_MODE_OFF;
- base = ccsc_base[mixer->cfg->ccsc][layer];
+ format = state->fb->format;
+ if (!format->is_yuv)
+ return SUN8I_CSC_MODE_OFF;
- sun8i_csc_set_coefficients(mixer->engine.regs, base,
- mode, encoding, range);
+ switch (format->format) {
+ case DRM_FORMAT_YVU411:
+ case DRM_FORMAT_YVU420:
+ case DRM_FORMAT_YVU422:
+ case DRM_FORMAT_YVU444:
+ return SUN8I_CSC_MODE_YVU2RGB;
+ default:
+ return SUN8I_CSC_MODE_YUV2RGB;
+ }
}
-void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable)
+void sun8i_csc_config(struct sun8i_layer *layer,
+ struct drm_plane_state *state)
{
+ u32 mode = sun8i_csc_get_mode(state);
u32 base;
- if (mixer->cfg->de_type == SUN8I_MIXER_DE3) {
- sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable);
+ if (layer->cfg->de_type == SUN8I_MIXER_DE3) {
+ sun8i_de3_ccsc_setup(layer->regs, layer->channel,
+ mode, state->color_encoding,
+ state->color_range);
return;
}
- base = ccsc_base[mixer->cfg->ccsc][layer];
+ base = ccsc_base[layer->cfg->ccsc][layer->channel];
- sun8i_csc_enable(mixer->engine.regs, base, enable);
+ sun8i_csc_setup(layer->regs, base,
+ mode, state->color_encoding,
+ state->color_range);
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_csc.h b/drivers/gpu/drm/sun4i/sun8i_csc.h
index 828b86fd0cab..2a4b79599610 100644
--- a/drivers/gpu/drm/sun4i/sun8i_csc.h
+++ b/drivers/gpu/drm/sun4i/sun8i_csc.h
@@ -8,7 +8,8 @@
#include <drm/drm_color_mgmt.h>
-struct sun8i_mixer;
+struct drm_plane_state;
+struct sun8i_layer;
/* VI channel CSC units offsets */
#define CCSC00_OFFSET 0xAA050
@@ -22,16 +23,7 @@ struct sun8i_mixer;
#define SUN8I_CSC_CTRL_EN BIT(0)
-enum sun8i_csc_mode {
- SUN8I_CSC_MODE_OFF,
- SUN8I_CSC_MODE_YUV2RGB,
- SUN8I_CSC_MODE_YVU2RGB,
-};
-
-void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
- enum sun8i_csc_mode mode,
- enum drm_color_encoding encoding,
- enum drm_color_range range);
-void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable);
+void sun8i_csc_config(struct sun8i_layer *layer,
+ struct drm_plane_state *state);
#endif
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c
index 31a8409b98f4..ce9c155bfad7 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c
@@ -21,6 +21,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "sun4i_drv.h"
@@ -250,24 +251,6 @@ int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format)
return -EINVAL;
}
-static void sun8i_layer_enable(struct sun8i_layer *layer, bool enable)
-{
- u32 ch_base = sun8i_channel_base(layer->mixer, layer->channel);
- u32 val, reg, mask;
-
- if (layer->type == SUN8I_LAYER_TYPE_UI) {
- val = enable ? SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN : 0;
- mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
- reg = SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay);
- } else {
- val = enable ? SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN : 0;
- mask = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
- reg = SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay);
- }
-
- regmap_update_bits(layer->mixer->engine.regs, reg, mask, val);
-}
-
static void sun8i_mixer_commit(struct sunxi_engine *engine,
struct drm_crtc *crtc,
struct drm_atomic_state *state)
@@ -283,10 +266,10 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine,
drm_for_each_plane(plane, state->dev) {
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
+ int w, h, x, y, zpos;
bool enable;
- int zpos;
- if (!(plane->possible_crtcs & drm_crtc_mask(crtc)) || layer->mixer != mixer)
+ if (!(plane->possible_crtcs & drm_crtc_mask(crtc)))
continue;
plane_state = drm_atomic_get_new_plane_state(state, plane);
@@ -295,23 +278,28 @@ static void sun8i_mixer_commit(struct sunxi_engine *engine,
enable = plane_state->crtc && plane_state->visible;
zpos = plane_state->normalized_zpos;
+ x = plane_state->dst.x1;
+ y = plane_state->dst.y1;
+ w = drm_rect_width(&plane_state->dst);
+ h = drm_rect_height(&plane_state->dst);
- DRM_DEBUG_DRIVER(" plane %d: chan=%d ovl=%d en=%d zpos=%d\n",
- plane->base.id, layer->channel, layer->overlay,
- enable, zpos);
-
- /*
- * We always update the layer enable bit, because it can clear
- * spontaneously for unknown reasons.
- */
- sun8i_layer_enable(layer, enable);
+ DRM_DEBUG_DRIVER(" plane %d: chan=%d ovl=%d en=%d zpos=%d x=%d y=%d w=%d h=%d\n",
+ plane->base.id, layer->index, layer->overlay,
+ enable, zpos, x, y, w, h);
if (!enable)
continue;
/* Route layer to pipe based on zpos */
- route |= layer->channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
+ route |= layer->index << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
pipe_en |= SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
+
+ regmap_write(bld_regs,
+ SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
+ SUN8I_MIXER_COORD(x, y));
+ regmap_write(bld_regs,
+ SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
+ SUN8I_MIXER_SIZE(w, h));
}
regmap_write(bld_regs, SUN8I_MIXER_BLEND_ROUTE(bld_base), route);
@@ -328,18 +316,30 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
{
struct drm_plane **planes;
struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine);
+ int plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
+ enum drm_plane_type type;
+ unsigned int phy_index;
int i;
- planes = devm_kcalloc(drm->dev,
- mixer->cfg->vi_num + mixer->cfg->ui_num + 1,
- sizeof(*planes), GFP_KERNEL);
+ planes = devm_kcalloc(drm->dev, plane_cnt, sizeof(*planes), GFP_KERNEL);
if (!planes)
return ERR_PTR(-ENOMEM);
for (i = 0; i < mixer->cfg->vi_num; i++) {
struct sun8i_layer *layer;
- layer = sun8i_vi_layer_init_one(drm, mixer, i);
+ if (i == 0 && !mixer->cfg->ui_num)
+ type = DRM_PLANE_TYPE_PRIMARY;
+ else
+ type = DRM_PLANE_TYPE_OVERLAY;
+
+ phy_index = i;
+ if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
+ phy_index = mixer->cfg->map[i];
+
+ layer = sun8i_vi_layer_init_one(drm, type, mixer->engine.regs,
+ i, phy_index, plane_cnt,
+ &mixer->cfg->lay_cfg);
if (IS_ERR(layer)) {
dev_err(drm->dev,
"Couldn't initialize overlay plane\n");
@@ -350,16 +350,28 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm,
}
for (i = 0; i < mixer->cfg->ui_num; i++) {
+ unsigned int index = mixer->cfg->vi_num + i;
struct sun8i_layer *layer;
- layer = sun8i_ui_layer_init_one(drm, mixer, i);
+ if (i == 0)
+ type = DRM_PLANE_TYPE_PRIMARY;
+ else
+ type = DRM_PLANE_TYPE_OVERLAY;
+
+ phy_index = index;
+ if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
+ phy_index = mixer->cfg->map[index];
+
+ layer = sun8i_ui_layer_init_one(drm, type, mixer->engine.regs,
+ index, phy_index, plane_cnt,
+ &mixer->cfg->lay_cfg);
if (IS_ERR(layer)) {
dev_err(drm->dev, "Couldn't initialize %s plane\n",
i ? "overlay" : "primary");
return ERR_CAST(layer);
}
- planes[mixer->cfg->vi_num + i] = &layer->plane;
+ planes[index] = &layer->plane;
}
return planes;
@@ -692,119 +704,173 @@ static void sun8i_mixer_remove(struct platform_device *pdev)
}
static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = {
- .ccsc = CCSC_MIXER0_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER0_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0xf,
+ .scanline_yuv = 2048,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
- .scaler_mask = 0xf,
- .scanline_yuv = 2048,
.ui_num = 3,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = {
- .ccsc = CCSC_MIXER1_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER1_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0x3,
+ .scanline_yuv = 2048,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
- .scaler_mask = 0x3,
- .scanline_yuv = 2048,
.ui_num = 1,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = {
- .ccsc = CCSC_MIXER0_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER0_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0xf,
+ .scanline_yuv = 2048,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 432000000,
- .scaler_mask = 0xf,
- .scanline_yuv = 2048,
.ui_num = 3,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = {
- .ccsc = CCSC_MIXER0_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER0_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0xf,
+ .scanline_yuv = 2048,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 297000000,
- .scaler_mask = 0xf,
- .scanline_yuv = 2048,
.ui_num = 3,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = {
- .ccsc = CCSC_MIXER1_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER1_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0x3,
+ .scanline_yuv = 2048,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 297000000,
- .scaler_mask = 0x3,
- .scanline_yuv = 2048,
.ui_num = 1,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = {
- .de_type = SUN8I_MIXER_DE2,
- .vi_num = 2,
- .ui_num = 1,
- .scaler_mask = 0x3,
- .scanline_yuv = 2048,
- .ccsc = CCSC_MIXER0_LAYOUT,
- .mod_rate = 150000000,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER0_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 2,
+ .scaler_mask = 0x3,
+ .scanline_yuv = 2048,
+ },
+ .de_type = SUN8I_MIXER_DE2,
+ .mod_rate = 150000000,
+ .vi_num = 2,
+ .ui_num = 1,
};
static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = {
- .ccsc = CCSC_D1_MIXER0_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_D1_MIXER0_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0x3,
+ .scanline_yuv = 2048,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 297000000,
- .scaler_mask = 0x3,
- .scanline_yuv = 2048,
.ui_num = 1,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = {
- .ccsc = CCSC_MIXER1_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER1_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0x1,
+ .scanline_yuv = 1024,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 297000000,
- .scaler_mask = 0x1,
- .scanline_yuv = 1024,
.ui_num = 0,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = {
- .ccsc = CCSC_MIXER0_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER0_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0xf,
+ .scanline_yuv = 4096,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 297000000,
- .scaler_mask = 0xf,
- .scanline_yuv = 4096,
.ui_num = 3,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = {
- .ccsc = CCSC_MIXER1_LAYOUT,
+ .lay_cfg = {
+ .ccsc = CCSC_MIXER1_LAYOUT,
+ .de_type = SUN8I_MIXER_DE2,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0x3,
+ .scanline_yuv = 2048,
+ .de2_fcc_alpha = 1,
+ },
.de_type = SUN8I_MIXER_DE2,
.mod_rate = 297000000,
- .scaler_mask = 0x3,
- .scanline_yuv = 2048,
.ui_num = 1,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = {
- .ccsc = CCSC_MIXER0_LAYOUT,
+ .lay_cfg = {
+ .de_type = SUN8I_MIXER_DE3,
+ .vi_scaler_num = 1,
+ .scaler_mask = 0xf,
+ .scanline_yuv = 4096,
+ },
.de_type = SUN8I_MIXER_DE3,
.mod_rate = 600000000,
- .scaler_mask = 0xf,
- .scanline_yuv = 4096,
.ui_num = 3,
.vi_num = 1,
};
static const struct sun8i_mixer_cfg sun50i_h616_mixer0_cfg = {
- .ccsc = CCSC_MIXER0_LAYOUT,
+ .lay_cfg = {
+ .de_type = SUN8I_MIXER_DE33,
+ .scaler_mask = 0xf,
+ .scanline_yuv = 4096,
+ },
.de_type = SUN8I_MIXER_DE33,
.mod_rate = 600000000,
- .scaler_mask = 0xf,
- .scanline_yuv = 4096,
.ui_num = 3,
.vi_num = 1,
.map = {0, 6, 7, 8},
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.h b/drivers/gpu/drm/sun4i/sun8i_mixer.h
index a1c1cbccc654..e2f83301aae8 100644
--- a/drivers/gpu/drm/sun4i/sun8i_mixer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_mixer.h
@@ -39,6 +39,9 @@
#define DE3_CH_BASE 0x1000
#define DE3_CH_SIZE 0x0800
+#define DE33_CH_BASE 0x1000
+#define DE33_CH_SIZE 0x20000
+
#define SUN8I_MIXER_BLEND_PIPE_CTL(base) ((base) + 0)
#define SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, x) ((base) + 0x4 + 0x10 * (x))
#define SUN8I_MIXER_BLEND_ATTR_INSIZE(base, x) ((base) + 0x8 + 0x10 * (x))
@@ -161,29 +164,45 @@ enum sun8i_mixer_type {
};
/**
- * struct sun8i_mixer_cfg - mixer HW configuration
- * @vi_num: number of VI channels
- * @ui_num: number of UI channels
+ * struct sun8i_layer_cfg - layer configuration
+ * @vi_scaler_num: Number of VI scalers. Used on DE2 and DE3.
* @scaler_mask: bitmask which tells which channel supports scaling
* First, scaler supports for VI channels is defined and after that, scaler
* support for UI channels. For example, if mixer has 2 VI channels without
* scaler and 2 UI channels with scaler, bitmask would be 0xC.
* @ccsc: select set of CCSC base addresses from the enumeration above.
- * @mod_rate: module clock rate that needs to be set in order to have
- * a functional block.
* @de_type: sun8i_mixer_type enum representing the display engine generation.
* @scaline_yuv: size of a scanline for VI scaler for YUV formats.
- * @map: channel map for DE variants processing YUV separately (DE33)
+ * @de2_fcc_alpha: use FCC for missing DE2 VI alpha capability
+ * Most DE2 cores has FCC. If number of VI planes is one, enable this.
*/
-struct sun8i_mixer_cfg {
- int vi_num;
- int ui_num;
+struct sun8i_layer_cfg {
+ unsigned int vi_scaler_num;
int scaler_mask;
int ccsc;
- unsigned long mod_rate;
unsigned int de_type;
unsigned int scanline_yuv;
- unsigned int map[6];
+ unsigned int de2_fcc_alpha : 1;
+};
+
+/**
+ * struct sun8i_mixer_cfg - mixer HW configuration
+ * @lay_cfg: layer configuration
+ * @vi_num: number of VI channels
+ * @ui_num: number of UI channels
+ * @de_type: sun8i_mixer_type enum representing the display engine generation.
+ * @mod_rate: module clock rate that needs to be set in order to have
+ * a functional block.
+ * @map: channel map for DE variants processing YUV separately (DE33)
+ */
+
+struct sun8i_mixer_cfg {
+ struct sun8i_layer_cfg lay_cfg;
+ int vi_num;
+ int ui_num;
+ unsigned int de_type;
+ unsigned long mod_rate;
+ unsigned int map[6];
};
struct sun8i_mixer {
@@ -206,11 +225,13 @@ enum {
};
struct sun8i_layer {
- struct drm_plane plane;
- struct sun8i_mixer *mixer;
- int type;
- int channel;
- int overlay;
+ struct drm_plane plane;
+ int type;
+ int index;
+ int channel;
+ int overlay;
+ struct regmap *regs;
+ const struct sun8i_layer_cfg *cfg;
};
static inline struct sun8i_layer *
@@ -239,14 +260,14 @@ sun8i_blender_regmap(struct sun8i_mixer *mixer)
}
static inline u32
-sun8i_channel_base(struct sun8i_mixer *mixer, int channel)
+sun8i_channel_base(struct sun8i_layer *layer)
{
- if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
- return mixer->cfg->map[channel] * 0x20000 + DE2_CH_SIZE;
- else if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
- return DE3_CH_BASE + channel * DE3_CH_SIZE;
+ if (layer->cfg->de_type == SUN8I_MIXER_DE33)
+ return DE33_CH_BASE + layer->channel * DE33_CH_SIZE;
+ else if (layer->cfg->de_type == SUN8I_MIXER_DE3)
+ return DE3_CH_BASE + layer->channel * DE3_CH_SIZE;
else
- return DE2_CH_BASE + channel * DE2_CH_SIZE;
+ return DE2_CH_BASE + layer->channel * DE2_CH_SIZE;
}
int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format);
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
index f97be0040aab..f08f6da55dd0 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c
@@ -18,6 +18,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "sun8i_mixer.h"
@@ -25,44 +26,49 @@
#include "sun8i_ui_scaler.h"
#include "sun8i_vi_scaler.h"
-static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane)
+static void sun8i_ui_layer_disable(struct sun8i_layer *layer)
{
- u32 mask, val, ch_base;
+ u32 ch_base = sun8i_channel_base(layer);
- ch_base = sun8i_channel_base(mixer, channel);
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay), 0);
+}
- mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK |
- SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK;
+static void sun8i_ui_layer_update_attributes(struct sun8i_layer *layer,
+ struct drm_plane *plane)
+{
+ struct drm_plane_state *state = plane->state;
+ const struct drm_format_info *fmt;
+ u32 val, ch_base, hw_fmt;
- val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8);
+ ch_base = sun8i_channel_base(layer);
+ fmt = state->fb->format;
+ sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
- val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
+ val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(state->alpha >> 8);
+ val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
+ val |= hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
+ val |= SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
- regmap_update_bits(mixer->engine.regs,
- SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
- mask, val);
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, layer->overlay), val);
}
-static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane,
- unsigned int zpos)
+static void sun8i_ui_layer_update_coord(struct sun8i_layer *layer,
+ struct drm_plane *plane)
{
struct drm_plane_state *state = plane->state;
u32 src_w, src_h, dst_w, dst_h;
- struct regmap *bld_regs;
- u32 bld_base, ch_base;
u32 outsize, insize;
u32 hphase, vphase;
+ u32 ch_base;
DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
- channel, overlay);
+ layer->channel, layer->overlay);
- bld_base = sun8i_blender_base(mixer);
- bld_regs = sun8i_blender_regmap(mixer);
- ch_base = sun8i_channel_base(mixer, channel);
+ ch_base = sun8i_channel_base(layer);
src_w = drm_rect_width(&state->src) >> 16;
src_h = drm_rect_height(&state->src) >> 16;
@@ -79,10 +85,10 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
state->src.x1 >> 16, state->src.y1 >> 16);
DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
- regmap_write(mixer->engine.regs,
- SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay),
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, layer->overlay),
insize);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base),
insize);
@@ -94,67 +100,27 @@ static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
hscale = state->src_w / state->crtc_w;
vscale = state->src_h / state->crtc_h;
- if (mixer->cfg->de_type == SUN8I_MIXER_DE33) {
- sun8i_vi_scaler_setup(mixer, channel, src_w, src_h,
- dst_w, dst_h, hscale, vscale,
- hphase, vphase,
+ if (layer->cfg->de_type == SUN8I_MIXER_DE33) {
+ sun8i_vi_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
+ hscale, vscale, hphase, vphase,
state->fb->format);
- sun8i_vi_scaler_enable(mixer, channel, true);
+ sun8i_vi_scaler_enable(layer, true);
} else {
- sun8i_ui_scaler_setup(mixer, channel, src_w, src_h,
- dst_w, dst_h, hscale, vscale,
- hphase, vphase);
- sun8i_ui_scaler_enable(mixer, channel, true);
+ sun8i_ui_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
+ hscale, vscale, hphase, vphase);
+ sun8i_ui_scaler_enable(layer, true);
}
} else {
DRM_DEBUG_DRIVER("HW scaling is not needed\n");
- if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
- sun8i_vi_scaler_enable(mixer, channel, false);
+ if (layer->cfg->de_type == SUN8I_MIXER_DE33)
+ sun8i_vi_scaler_enable(layer, false);
else
- sun8i_ui_scaler_enable(mixer, channel, false);
+ sun8i_ui_scaler_enable(layer, false);
}
-
- /* Set base coordinates */
- DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
- state->dst.x1, state->dst.y1);
- DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
- regmap_write(bld_regs,
- SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
- SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
- regmap_write(bld_regs,
- SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
- outsize);
-
- return 0;
}
-static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane)
-{
- struct drm_plane_state *state = plane->state;
- const struct drm_format_info *fmt;
- u32 val, ch_base, hw_fmt;
- int ret;
-
- ch_base = sun8i_channel_base(mixer, channel);
-
- fmt = state->fb->format;
- ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
- if (ret || fmt->is_yuv) {
- DRM_DEBUG_DRIVER("Invalid format\n");
- return -EINVAL;
- }
-
- val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
- regmap_update_bits(mixer->engine.regs,
- SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
- SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
-
- return 0;
-}
-
-static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane)
+static void sun8i_ui_layer_update_buffer(struct sun8i_layer *layer,
+ struct drm_plane *plane)
{
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
@@ -163,7 +129,7 @@ static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
u32 ch_base;
int bpp;
- ch_base = sun8i_channel_base(mixer, channel);
+ ch_base = sun8i_channel_base(layer);
/* Get the physical address of the buffer in memory */
gem = drm_fb_dma_get_gem_obj(fb, 0);
@@ -180,17 +146,15 @@ static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
/* Set the line width */
DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
- regmap_write(mixer->engine.regs,
- SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay),
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, layer->overlay),
fb->pitches[0]);
DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &dma_addr);
- regmap_write(mixer->engine.regs,
- SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay),
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, layer->overlay),
lower_32_bits(dma_addr));
-
- return 0;
}
static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
@@ -201,20 +165,28 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
struct drm_crtc *crtc = new_plane_state->crtc;
struct drm_crtc_state *crtc_state;
- int min_scale, max_scale;
+ const struct drm_format_info *fmt;
+ int min_scale, max_scale, ret;
+ u32 hw_fmt;
if (!crtc)
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
+ fmt = new_plane_state->fb->format;
+ ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
+ if (ret || fmt->is_yuv) {
+ DRM_DEBUG_DRIVER("Invalid plane format\n");
+ return -EINVAL;
+ }
+
min_scale = DRM_PLANE_NO_SCALING;
max_scale = DRM_PLANE_NO_SCALING;
- if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
+ if (layer->cfg->scaler_mask & BIT(layer->channel)) {
min_scale = SUN8I_UI_SCALER_SCALE_MIN;
max_scale = SUN8I_UI_SCALER_SCALE_MAX;
}
@@ -232,20 +204,15 @@ static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
- unsigned int zpos = new_state->normalized_zpos;
- struct sun8i_mixer *mixer = layer->mixer;
- if (!new_state->crtc || !new_state->visible)
+ if (!new_state->crtc || !new_state->visible) {
+ sun8i_ui_layer_disable(layer);
return;
+ }
- sun8i_ui_layer_update_coord(mixer, layer->channel,
- layer->overlay, plane, zpos);
- sun8i_ui_layer_update_alpha(mixer, layer->channel,
- layer->overlay, plane);
- sun8i_ui_layer_update_formats(mixer, layer->channel,
- layer->overlay, plane);
- sun8i_ui_layer_update_buffer(mixer, layer->channel,
- layer->overlay, plane);
+ sun8i_ui_layer_update_attributes(layer, plane);
+ sun8i_ui_layer_update_coord(layer, plane);
+ sun8i_ui_layer_update_buffer(layer, plane);
}
static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
@@ -291,21 +258,25 @@ static const uint64_t sun8i_layer_modifiers[] = {
};
struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
- struct sun8i_mixer *mixer,
- int index)
+ enum drm_plane_type type,
+ struct regmap *regs,
+ int index, int phy_index,
+ int plane_cnt,
+ const struct sun8i_layer_cfg *cfg)
{
- enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
- int channel = mixer->cfg->vi_num + index;
struct sun8i_layer *layer;
- unsigned int plane_cnt;
int ret;
layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
if (!layer)
return ERR_PTR(-ENOMEM);
- if (index == 0)
- type = DRM_PLANE_TYPE_PRIMARY;
+ layer->type = SUN8I_LAYER_TYPE_UI;
+ layer->index = index;
+ layer->channel = phy_index;
+ layer->overlay = 0;
+ layer->regs = regs;
+ layer->cfg = cfg;
/* possible crtcs are set later */
ret = drm_universal_plane_init(drm, &layer->plane, 0,
@@ -318,15 +289,13 @@ struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
return ERR_PTR(ret);
}
- plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
-
ret = drm_plane_create_alpha_property(&layer->plane);
if (ret) {
dev_err(drm->dev, "Couldn't add alpha property\n");
return ERR_PTR(ret);
}
- ret = drm_plane_create_zpos_property(&layer->plane, channel,
+ ret = drm_plane_create_zpos_property(&layer->plane, index,
0, plane_cnt - 1);
if (ret) {
dev_err(drm->dev, "Couldn't add zpos property\n");
@@ -334,10 +303,6 @@ struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
}
drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
- layer->mixer = mixer;
- layer->type = SUN8I_LAYER_TYPE_UI;
- layer->channel = channel;
- layer->overlay = 0;
return layer;
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h
index 83892f6ff211..1581ffc6d4e5 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.h
@@ -50,6 +50,9 @@ struct sun8i_mixer;
struct sun8i_layer;
struct sun8i_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
- struct sun8i_mixer *mixer,
- int index);
+ enum drm_plane_type type,
+ struct regmap *regs,
+ int index, int phy_index,
+ int plane_cnt,
+ const struct sun8i_layer_cfg *cfg);
#endif /* _SUN8I_UI_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c
index 8b7a58e27517..a178da8f532a 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.c
@@ -89,18 +89,18 @@ static const u32 lan2coefftab16[240] = {
0x0b1c1603, 0x0d1c1502, 0x0e1d1401, 0x0f1d1301,
};
-static u32 sun8i_ui_scaler_base(struct sun8i_mixer *mixer, int channel)
+static u32 sun8i_ui_scaler_base(struct sun8i_layer *layer)
{
- int vi_num = mixer->cfg->vi_num;
+ int offset = layer->cfg->vi_scaler_num;
- if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
+ if (layer->cfg->de_type == SUN8I_MIXER_DE3)
return DE3_VI_SCALER_UNIT_BASE +
- DE3_VI_SCALER_UNIT_SIZE * vi_num +
- DE3_UI_SCALER_UNIT_SIZE * (channel - vi_num);
+ DE3_VI_SCALER_UNIT_SIZE * offset +
+ DE3_UI_SCALER_UNIT_SIZE * (layer->channel - offset);
else
return DE2_VI_SCALER_UNIT_BASE +
- DE2_VI_SCALER_UNIT_SIZE * vi_num +
- DE2_UI_SCALER_UNIT_SIZE * (channel - vi_num);
+ DE2_VI_SCALER_UNIT_SIZE * offset +
+ DE2_UI_SCALER_UNIT_SIZE * (layer->channel - offset);
}
static int sun8i_ui_scaler_coef_index(unsigned int step)
@@ -127,14 +127,11 @@ static int sun8i_ui_scaler_coef_index(unsigned int step)
}
}
-void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
+void sun8i_ui_scaler_enable(struct sun8i_layer *layer, bool enable)
{
u32 val, base;
- if (WARN_ON(layer < mixer->cfg->vi_num))
- return;
-
- base = sun8i_ui_scaler_base(mixer, layer);
+ base = sun8i_ui_scaler_base(layer);
if (enable)
val = SUN8I_SCALER_GSU_CTRL_EN |
@@ -142,10 +139,10 @@ void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
else
val = 0;
- regmap_write(mixer->engine.regs, SUN8I_SCALER_GSU_CTRL(base), val);
+ regmap_write(layer->regs, SUN8I_SCALER_GSU_CTRL(base), val);
}
-void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
+void sun8i_ui_scaler_setup(struct sun8i_layer *layer,
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
u32 hscale, u32 vscale, u32 hphase, u32 vphase)
{
@@ -153,10 +150,7 @@ void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
int i, offset;
u32 base;
- if (WARN_ON(layer < mixer->cfg->vi_num))
- return;
-
- base = sun8i_ui_scaler_base(mixer, layer);
+ base = sun8i_ui_scaler_base(layer);
hphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16;
vphase <<= SUN8I_UI_SCALER_PHASE_FRAC - 16;
@@ -166,22 +160,22 @@ void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
insize = SUN8I_UI_SCALER_SIZE(src_w, src_h);
outsize = SUN8I_UI_SCALER_SIZE(dst_w, dst_h);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_GSU_OUTSIZE(base), outsize);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_GSU_INSIZE(base), insize);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_GSU_HSTEP(base), hscale);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_GSU_VSTEP(base), vscale);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_GSU_HPHASE(base), hphase);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_GSU_VPHASE(base), vphase);
offset = sun8i_ui_scaler_coef_index(hscale) *
SUN8I_UI_SCALER_COEFF_COUNT;
for (i = 0; i < SUN8I_UI_SCALER_COEFF_COUNT; i++)
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_GSU_HCOEFF(base, i),
lan2coefftab16[offset + i]);
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h
index 1ef4bd6f2718..872d88a58e7e 100644
--- a/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h
+++ b/drivers/gpu/drm/sun4i/sun8i_ui_scaler.h
@@ -35,8 +35,8 @@
#define SUN8I_SCALER_GSU_CTRL_EN BIT(0)
#define SUN8I_SCALER_GSU_CTRL_COEFF_RDY BIT(4)
-void sun8i_ui_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable);
-void sun8i_ui_scaler_setup(struct sun8i_mixer *mixer, int layer,
+void sun8i_ui_scaler_enable(struct sun8i_layer *layer, bool enable);
+void sun8i_ui_scaler_setup(struct sun8i_layer *layer,
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
u32 hscale, u32 vscale, u32 hphase, u32 vphase);
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
index a09ee4097537..ca3ab59e108d 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c
@@ -11,64 +11,74 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+#include "sun4i_crtc.h"
#include "sun8i_csc.h"
#include "sun8i_mixer.h"
#include "sun8i_vi_layer.h"
#include "sun8i_vi_scaler.h"
-static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane)
+static void sun8i_vi_layer_disable(struct sun8i_layer *layer)
{
- u32 mask, val, ch_base;
+ u32 ch_base = sun8i_channel_base(layer);
- ch_base = sun8i_channel_base(mixer, channel);
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay), 0);
+}
- if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
- mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
- SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
- val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
- (plane->state->alpha >> 8);
+static void sun8i_vi_layer_update_attributes(struct sun8i_layer *layer,
+ struct drm_plane *plane)
+{
+ struct drm_plane_state *state = plane->state;
+ const struct drm_format_info *fmt;
+ u32 val, ch_base, hw_fmt;
- val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
+ ch_base = sun8i_channel_base(layer);
+ fmt = state->fb->format;
+ sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
+
+ val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
+ if (!fmt->is_yuv)
+ val |= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
+ val |= SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
+ if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
+ val |= SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(state->alpha >> 8);
+ val |= (state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
+ }
+
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, layer->overlay), val);
- regmap_update_bits(mixer->engine.regs,
- SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
- overlay),
- mask, val);
- } else if (mixer->cfg->vi_num == 1) {
- regmap_update_bits(mixer->engine.regs,
- SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
- SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
- SUN8I_MIXER_FCC_GLOBAL_ALPHA
- (plane->state->alpha >> 8));
+ if (layer->cfg->de2_fcc_alpha) {
+ regmap_write(layer->regs,
+ SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
+ SUN8I_MIXER_FCC_GLOBAL_ALPHA(state->alpha >> 8));
}
}
-static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane,
- unsigned int zpos)
+static void sun8i_vi_layer_update_coord(struct sun8i_layer *layer,
+ struct drm_plane *plane)
{
struct drm_plane_state *state = plane->state;
+ struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(state->crtc);
+ struct sun8i_mixer *mixer = engine_to_sun8i_mixer(scrtc->engine);
const struct drm_format_info *format = state->fb->format;
u32 src_w, src_h, dst_w, dst_h;
- struct regmap *bld_regs;
- u32 bld_base, ch_base;
u32 outsize, insize;
u32 hphase, vphase;
u32 hn = 0, hm = 0;
u32 vn = 0, vm = 0;
bool subsampled;
+ u32 ch_base;
DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
- channel, overlay);
+ layer->channel, layer->overlay);
- bld_base = sun8i_blender_base(mixer);
- bld_regs = sun8i_blender_regmap(mixer);
- ch_base = sun8i_channel_base(mixer, channel);
+ ch_base = sun8i_channel_base(layer);
src_w = drm_rect_width(&state->src) >> 16;
src_h = drm_rect_height(&state->src) >> 16;
@@ -105,10 +115,10 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
(state->src.x1 >> 16) & ~(format->hsub - 1),
(state->src.y1 >> 16) & ~(format->vsub - 1));
DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
- regmap_write(mixer->engine.regs,
- SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
+ regmap_write(layer->regs,
+ SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, layer->overlay),
insize);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
insize);
@@ -143,7 +153,7 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
}
/* it seems that every RGB scaler has buffer for 2048 pixels */
- scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
+ scanline = subsampled ? layer->cfg->scanline_yuv : 2048;
if (src_w > scanline) {
DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
@@ -155,108 +165,34 @@ static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
hscale = (src_w << 16) / dst_w;
vscale = (src_h << 16) / dst_h;
- sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
- dst_h, hscale, vscale, hphase, vphase,
- format);
- sun8i_vi_scaler_enable(mixer, channel, true);
+ sun8i_vi_scaler_setup(layer, src_w, src_h, dst_w, dst_h,
+ hscale, vscale, hphase, vphase, format);
+ sun8i_vi_scaler_enable(layer, true);
} else {
DRM_DEBUG_DRIVER("HW scaling is not needed\n");
- sun8i_vi_scaler_enable(mixer, channel, false);
+ sun8i_vi_scaler_enable(layer, false);
}
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
SUN8I_MIXER_CHAN_VI_DS_M(hm));
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
SUN8I_MIXER_CHAN_VI_DS_N(hn) |
SUN8I_MIXER_CHAN_VI_DS_M(hm));
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
SUN8I_MIXER_CHAN_VI_DS_M(vm));
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
SUN8I_MIXER_CHAN_VI_DS_N(vn) |
SUN8I_MIXER_CHAN_VI_DS_M(vm));
-
- /* Set base coordinates */
- DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
- state->dst.x1, state->dst.y1);
- DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
- regmap_write(bld_regs,
- SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
- SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
- regmap_write(bld_regs,
- SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
- outsize);
-
- return 0;
-}
-
-static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
-{
- if (!format->is_yuv)
- return SUN8I_CSC_MODE_OFF;
-
- switch (format->format) {
- case DRM_FORMAT_YVU411:
- case DRM_FORMAT_YVU420:
- case DRM_FORMAT_YVU422:
- case DRM_FORMAT_YVU444:
- return SUN8I_CSC_MODE_YVU2RGB;
- default:
- return SUN8I_CSC_MODE_YUV2RGB;
- }
-}
-
-static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane)
-{
- struct drm_plane_state *state = plane->state;
- u32 val, ch_base, csc_mode, hw_fmt;
- const struct drm_format_info *fmt;
- int ret;
-
- ch_base = sun8i_channel_base(mixer, channel);
-
- fmt = state->fb->format;
- ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
- if (ret) {
- DRM_DEBUG_DRIVER("Invalid format\n");
- return ret;
- }
-
- val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
- regmap_update_bits(mixer->engine.regs,
- SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
- SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
-
- csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
- if (csc_mode != SUN8I_CSC_MODE_OFF) {
- sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
- state->color_encoding,
- state->color_range);
- sun8i_csc_enable_ccsc(mixer, channel, true);
- } else {
- sun8i_csc_enable_ccsc(mixer, channel, false);
- }
-
- if (!fmt->is_yuv)
- val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
- else
- val = 0;
-
- regmap_update_bits(mixer->engine.regs,
- SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
- SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
-
- return 0;
}
-static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
- int overlay, struct drm_plane *plane)
+static void sun8i_vi_layer_update_buffer(struct sun8i_layer *layer,
+ struct drm_plane *plane)
{
struct drm_plane_state *state = plane->state;
struct drm_framebuffer *fb = state->fb;
@@ -267,7 +203,7 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
u32 ch_base;
int i;
- ch_base = sun8i_channel_base(mixer, channel);
+ ch_base = sun8i_channel_base(layer);
/* Adjust x and y to be dividable by subsampling factor */
src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
@@ -297,21 +233,19 @@ static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
/* Set the line width */
DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
i + 1, fb->pitches[i]);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
- overlay, i),
+ layer->overlay, i),
fb->pitches[i]);
DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
i + 1, &dma_addr);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
- overlay, i),
+ layer->overlay, i),
lower_32_bits(dma_addr));
}
-
- return 0;
}
static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
@@ -322,20 +256,28 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
struct drm_crtc *crtc = new_plane_state->crtc;
struct drm_crtc_state *crtc_state;
- int min_scale, max_scale;
+ const struct drm_format_info *fmt;
+ int min_scale, max_scale, ret;
+ u32 hw_fmt;
if (!crtc)
return 0;
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
+ fmt = new_plane_state->fb->format;
+ ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
+ if (ret) {
+ DRM_DEBUG_DRIVER("Invalid plane format\n");
+ return ret;
+ }
+
min_scale = DRM_PLANE_NO_SCALING;
max_scale = DRM_PLANE_NO_SCALING;
- if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
+ if (layer->cfg->scaler_mask & BIT(layer->channel)) {
min_scale = SUN8I_VI_SCALER_SCALE_MIN;
max_scale = SUN8I_VI_SCALER_SCALE_MAX;
}
@@ -352,20 +294,16 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
plane);
struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
- unsigned int zpos = new_state->normalized_zpos;
- struct sun8i_mixer *mixer = layer->mixer;
- if (!new_state->crtc || !new_state->visible)
+ if (!new_state->crtc || !new_state->visible) {
+ sun8i_vi_layer_disable(layer);
return;
+ }
- sun8i_vi_layer_update_coord(mixer, layer->channel,
- layer->overlay, plane, zpos);
- sun8i_vi_layer_update_alpha(mixer, layer->channel,
- layer->overlay, plane);
- sun8i_vi_layer_update_formats(mixer, layer->channel,
- layer->overlay, plane);
- sun8i_vi_layer_update_buffer(mixer, layer->channel,
- layer->overlay, plane);
+ sun8i_vi_layer_update_attributes(layer, plane);
+ sun8i_vi_layer_update_coord(layer, plane);
+ sun8i_csc_config(layer, new_state);
+ sun8i_vi_layer_update_buffer(layer, plane);
}
static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
@@ -471,12 +409,14 @@ static const uint64_t sun8i_layer_modifiers[] = {
};
struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
- struct sun8i_mixer *mixer,
- int index)
+ enum drm_plane_type type,
+ struct regmap *regs,
+ int index, int phy_index,
+ int plane_cnt,
+ const struct sun8i_layer_cfg *cfg)
{
- enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
u32 supported_encodings, supported_ranges;
- unsigned int plane_cnt, format_count;
+ unsigned int format_count;
struct sun8i_layer *layer;
const u32 *formats;
int ret;
@@ -485,7 +425,14 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
if (!layer)
return ERR_PTR(-ENOMEM);
- if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
+ layer->type = SUN8I_LAYER_TYPE_VI;
+ layer->index = index;
+ layer->channel = phy_index;
+ layer->overlay = 0;
+ layer->regs = regs;
+ layer->cfg = cfg;
+
+ if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
formats = sun8i_vi_layer_de3_formats;
format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
} else {
@@ -493,9 +440,6 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
}
- if (!mixer->cfg->ui_num && index == 0)
- type = DRM_PLANE_TYPE_PRIMARY;
-
/* possible crtcs are set later */
ret = drm_universal_plane_init(drm, &layer->plane, 0,
&sun8i_vi_layer_funcs,
@@ -507,9 +451,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
return ERR_PTR(ret);
}
- plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
-
- if (mixer->cfg->vi_num == 1 || mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
+ if (layer->cfg->de2_fcc_alpha || layer->cfg->de_type >= SUN8I_MIXER_DE3) {
ret = drm_plane_create_alpha_property(&layer->plane);
if (ret) {
dev_err(drm->dev, "Couldn't add alpha property\n");
@@ -526,7 +468,7 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
BIT(DRM_COLOR_YCBCR_BT709);
- if (mixer->cfg->de_type >= SUN8I_MIXER_DE3)
+ if (layer->cfg->de_type >= SUN8I_MIXER_DE3)
supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
@@ -543,10 +485,6 @@ struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
}
drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
- layer->mixer = mixer;
- layer->type = SUN8I_LAYER_TYPE_VI;
- layer->channel = index;
- layer->overlay = 0;
return layer;
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.h b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h
index 655440cdc78f..29cc5573691f 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.h
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.h
@@ -55,6 +55,9 @@ struct sun8i_mixer;
struct sun8i_layer;
struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
- struct sun8i_mixer *mixer,
- int index);
+ enum drm_plane_type type,
+ struct regmap *regs,
+ int index, int phy_index,
+ int plane_cnt,
+ const struct sun8i_layer_cfg *cfg);
#endif /* _SUN8I_VI_LAYER_H_ */
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
index 82df6244af88..3dec4eeb1ba2 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.c
@@ -833,16 +833,17 @@ static const u32 bicubic4coefftab32[480] = {
0x1012110d, 0x1012110d, 0x1013110c, 0x1013110c,
};
-static u32 sun8i_vi_scaler_base(struct sun8i_mixer *mixer, int channel)
+static u32 sun8i_vi_scaler_base(struct sun8i_layer *layer)
{
- if (mixer->cfg->de_type == SUN8I_MIXER_DE33)
- return sun8i_channel_base(mixer, channel) + 0x3000;
- else if (mixer->cfg->de_type == SUN8I_MIXER_DE3)
+ if (layer->cfg->de_type == SUN8I_MIXER_DE33)
+ return DE33_VI_SCALER_UNIT_BASE +
+ DE33_CH_SIZE * layer->channel;
+ else if (layer->cfg->de_type == SUN8I_MIXER_DE3)
return DE3_VI_SCALER_UNIT_BASE +
- DE3_VI_SCALER_UNIT_SIZE * channel;
+ DE3_VI_SCALER_UNIT_SIZE * layer->channel;
else
return DE2_VI_SCALER_UNIT_BASE +
- DE2_VI_SCALER_UNIT_SIZE * channel;
+ DE2_VI_SCALER_UNIT_SIZE * layer->channel;
}
static int sun8i_vi_scaler_coef_index(unsigned int step)
@@ -909,11 +910,11 @@ static void sun8i_vi_scaler_set_coeff(struct regmap *map, u32 base,
}
}
-void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
+void sun8i_vi_scaler_enable(struct sun8i_layer *layer, bool enable)
{
u32 val, base;
- base = sun8i_vi_scaler_base(mixer, layer);
+ base = sun8i_vi_scaler_base(layer);
if (enable)
val = SUN8I_SCALER_VSU_CTRL_EN |
@@ -921,11 +922,11 @@ void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable)
else
val = 0;
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_CTRL(base), val);
}
-void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
+void sun8i_vi_scaler_setup(struct sun8i_layer *layer,
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
u32 hscale, u32 vscale, u32 hphase, u32 vphase,
const struct drm_format_info *format)
@@ -934,7 +935,7 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
u32 insize, outsize;
u32 base;
- base = sun8i_vi_scaler_base(mixer, layer);
+ base = sun8i_vi_scaler_base(layer);
hphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16;
vphase <<= SUN8I_VI_SCALER_PHASE_FRAC - 16;
@@ -958,7 +959,7 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
cvphase = vphase;
}
- if (mixer->cfg->de_type >= SUN8I_MIXER_DE3) {
+ if (layer->cfg->de_type >= SUN8I_MIXER_DE3) {
u32 val;
if (format->hsub == 1 && format->vsub == 1)
@@ -966,36 +967,36 @@ void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
else
val = SUN50I_SCALER_VSU_SCALE_MODE_NORMAL;
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN50I_SCALER_VSU_SCALE_MODE(base), val);
}
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_OUTSIZE(base), outsize);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_YINSIZE(base), insize);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_YHSTEP(base), hscale);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_YVSTEP(base), vscale);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_YHPHASE(base), hphase);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_YVPHASE(base), vphase);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_CINSIZE(base),
SUN8I_VI_SCALER_SIZE(src_w / format->hsub,
src_h / format->vsub));
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_CHSTEP(base),
hscale / format->hsub);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_CVSTEP(base),
vscale / format->vsub);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_CHPHASE(base), chphase);
- regmap_write(mixer->engine.regs,
+ regmap_write(layer->regs,
SUN8I_SCALER_VSU_CVPHASE(base), cvphase);
- sun8i_vi_scaler_set_coeff(mixer->engine.regs, base,
+ sun8i_vi_scaler_set_coeff(layer->regs, base,
hscale, vscale, format);
}
diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
index 68f6593b369a..245fe2f431c3 100644
--- a/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
+++ b/drivers/gpu/drm/sun4i/sun8i_vi_scaler.h
@@ -18,6 +18,8 @@
#define DE3_VI_SCALER_UNIT_BASE 0x20000
#define DE3_VI_SCALER_UNIT_SIZE 0x08000
+#define DE33_VI_SCALER_UNIT_BASE 0x4000
+
/* this two macros assumes 16 fractional bits which is standard in DRM */
#define SUN8I_VI_SCALER_SCALE_MIN 1
#define SUN8I_VI_SCALER_SCALE_MAX ((1UL << 20) - 1)
@@ -69,8 +71,8 @@
#define SUN50I_SCALER_VSU_ANGLE_SHIFT(x) (((x) << 16) & 0xF)
#define SUN50I_SCALER_VSU_ANGLE_OFFSET(x) ((x) & 0xFF)
-void sun8i_vi_scaler_enable(struct sun8i_mixer *mixer, int layer, bool enable);
-void sun8i_vi_scaler_setup(struct sun8i_mixer *mixer, int layer,
+void sun8i_vi_scaler_enable(struct sun8i_layer *layer, bool enable);
+void sun8i_vi_scaler_setup(struct sun8i_layer *layer,
u32 src_w, u32 src_h, u32 dst_w, u32 dst_h,
u32 hscale, u32 vscale, u32 hphase, u32 vphase,
const struct drm_format_info *format);
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
index 89633e30ca62..da670d7eeb2e 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
@@ -10,12 +10,19 @@
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
+#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_modes.h>
struct drm_format_info;
struct drm_scanout_buffer;
struct screen_info;
+typedef void (*drm_sysfb_blit_func)(struct iosys_map *, const unsigned int *,
+ const struct iosys_map *,
+ const struct drm_framebuffer *,
+ const struct drm_rect *,
+ struct drm_format_conv_state *);
+
/*
* Input parsing
*/
@@ -93,10 +100,25 @@ static inline struct drm_sysfb_device *to_drm_sysfb_device(struct drm_device *de
* Plane
*/
+struct drm_sysfb_plane_state {
+ struct drm_shadow_plane_state base;
+
+ /* transfers framebuffer data to scanout buffer in CRTC format */
+ drm_sysfb_blit_func blit_to_crtc;
+};
+
+static inline struct drm_sysfb_plane_state *
+to_drm_sysfb_plane_state(struct drm_plane_state *base)
+{
+ return container_of(to_drm_shadow_plane_state(base), struct drm_sysfb_plane_state, base);
+}
+
size_t drm_sysfb_build_fourcc_list(struct drm_device *dev,
const u32 *native_fourccs, size_t native_nfourccs,
u32 *fourccs_out, size_t nfourccs_out);
+int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane,
+ struct drm_plane_state *plane_state);
int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *new_state);
void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane,
@@ -114,16 +136,24 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
DRM_FORMAT_MOD_INVALID
#define DRM_SYSFB_PLANE_HELPER_FUNCS \
- DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, \
+ .begin_fb_access = drm_sysfb_plane_helper_begin_fb_access, \
+ .end_fb_access = drm_gem_end_shadow_fb_access, \
.atomic_check = drm_sysfb_plane_helper_atomic_check, \
.atomic_update = drm_sysfb_plane_helper_atomic_update, \
.atomic_disable = drm_sysfb_plane_helper_atomic_disable, \
.get_scanout_buffer = drm_sysfb_plane_helper_get_scanout_buffer
+void drm_sysfb_plane_reset(struct drm_plane *plane);
+struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane);
+void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *plane_state);
+
#define DRM_SYSFB_PLANE_FUNCS \
+ .reset = drm_sysfb_plane_reset, \
.update_plane = drm_atomic_helper_update_plane, \
.disable_plane = drm_atomic_helper_disable_plane, \
- DRM_GEM_SHADOW_PLANE_FUNCS
+ .atomic_duplicate_state = drm_sysfb_plane_atomic_duplicate_state, \
+ .atomic_destroy_state = drm_sysfb_plane_atomic_destroy_state
/*
* CRTC
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
index ddb4a7523ee6..6214b7709b37 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
@@ -11,7 +11,6 @@
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_panic.h>
#include <drm/drm_print.h>
@@ -185,6 +184,104 @@ size_t drm_sysfb_build_fourcc_list(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_sysfb_build_fourcc_list);
+static void drm_sysfb_plane_state_destroy(struct drm_sysfb_plane_state *sysfb_plane_state)
+{
+ __drm_gem_destroy_shadow_plane_state(&sysfb_plane_state->base);
+
+ kfree(sysfb_plane_state);
+}
+
+static void drm_sysfb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch,
+ const struct iosys_map *src, const struct drm_framebuffer *fb,
+ const struct drm_rect *clip, struct drm_format_conv_state *state)
+{
+ drm_fb_memcpy(dst, dst_pitch, src, fb, clip);
+}
+
+static drm_sysfb_blit_func drm_sysfb_get_blit_func(u32 dst_format, u32 src_format)
+{
+ if (src_format == dst_format) {
+ return drm_sysfb_memcpy;
+ } else if (src_format == DRM_FORMAT_XRGB8888) {
+ switch (dst_format) {
+ case DRM_FORMAT_RGB565:
+ return drm_fb_xrgb8888_to_rgb565;
+ case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN:
+ return drm_fb_xrgb8888_to_rgb565be;
+ case DRM_FORMAT_XRGB1555:
+ return drm_fb_xrgb8888_to_xrgb1555;
+ case DRM_FORMAT_ARGB1555:
+ return drm_fb_xrgb8888_to_argb1555;
+ case DRM_FORMAT_RGBA5551:
+ return drm_fb_xrgb8888_to_rgba5551;
+ case DRM_FORMAT_RGB888:
+ return drm_fb_xrgb8888_to_rgb888;
+ case DRM_FORMAT_BGR888:
+ return drm_fb_xrgb8888_to_bgr888;
+ case DRM_FORMAT_ARGB8888:
+ return drm_fb_xrgb8888_to_argb8888;
+ case DRM_FORMAT_XBGR8888:
+ return drm_fb_xrgb8888_to_xbgr8888;
+ case DRM_FORMAT_ABGR8888:
+ return drm_fb_xrgb8888_to_abgr8888;
+ case DRM_FORMAT_XRGB2101010:
+ return drm_fb_xrgb8888_to_xrgb2101010;
+ case DRM_FORMAT_ARGB2101010:
+ return drm_fb_xrgb8888_to_argb2101010;
+ case DRM_FORMAT_BGRX8888:
+ return drm_fb_xrgb8888_to_bgrx8888;
+ case DRM_FORMAT_RGB332:
+ return drm_fb_xrgb8888_to_rgb332;
+ }
+ }
+
+ return NULL;
+}
+
+int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane,
+ struct drm_plane_state *plane_state)
+{
+ struct drm_device *dev = plane->dev;
+ struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state);
+ struct drm_framebuffer *fb = plane_state->fb;
+ struct drm_crtc_state *crtc_state;
+ struct drm_sysfb_crtc_state *sysfb_crtc_state;
+ drm_sysfb_blit_func blit_to_crtc;
+ int ret;
+
+ ret = drm_gem_begin_shadow_fb_access(plane, plane_state);
+ if (ret)
+ return ret;
+
+ if (!fb)
+ return 0;
+
+ ret = -EINVAL;
+
+ crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, plane_state->crtc);
+ if (drm_WARN_ON_ONCE(dev, !crtc_state))
+ goto err_drm_gem_end_shadow_fb_access;
+ sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
+
+ if (drm_WARN_ON_ONCE(dev, !sysfb_crtc_state->format))
+ goto err_drm_gem_end_shadow_fb_access;
+ blit_to_crtc = drm_sysfb_get_blit_func(sysfb_crtc_state->format->format,
+ fb->format->format);
+ if (!blit_to_crtc) {
+ drm_warn_once(dev, "No blit helper from %p4cc to %p4cc found.\n",
+ &fb->format->format, &sysfb_crtc_state->format->format);
+ goto err_drm_gem_end_shadow_fb_access;
+ }
+ sysfb_plane_state->blit_to_crtc = blit_to_crtc;
+
+ return 0;
+
+err_drm_gem_end_shadow_fb_access:
+ drm_gem_end_shadow_fb_access(plane, plane_state);
+ return ret;
+}
+EXPORT_SYMBOL(drm_sysfb_plane_helper_begin_fb_access);
+
int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *new_state)
{
@@ -235,12 +332,14 @@ void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_at
struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
- struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+ struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state);
+ struct drm_shadow_plane_state *shadow_plane_state = &sysfb_plane_state->base;
struct drm_framebuffer *fb = plane_state->fb;
unsigned int dst_pitch = sysfb->fb_pitch;
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
const struct drm_format_info *dst_format = sysfb_crtc_state->format;
+ drm_sysfb_blit_func blit_to_crtc = sysfb_plane_state->blit_to_crtc;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
int ret, idx;
@@ -261,8 +360,8 @@ void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_at
continue;
iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
- drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
- &damage, &shadow_plane_state->fmtcnv_state);
+ blit_to_crtc(&dst, &dst_pitch, shadow_plane_state->data, fb, &damage,
+ &shadow_plane_state->fmtcnv_state);
}
drm_dev_exit(idx);
@@ -321,6 +420,52 @@ int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
}
EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
+void drm_sysfb_plane_reset(struct drm_plane *plane)
+{
+ struct drm_sysfb_plane_state *sysfb_plane_state;
+
+ if (plane->state)
+ drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane->state));
+
+ sysfb_plane_state = kzalloc(sizeof(*sysfb_plane_state), GFP_KERNEL);
+ if (sysfb_plane_state)
+ __drm_gem_reset_shadow_plane(plane, &sysfb_plane_state->base);
+ else
+ __drm_gem_reset_shadow_plane(plane, NULL);
+}
+EXPORT_SYMBOL(drm_sysfb_plane_reset);
+
+struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane)
+{
+ struct drm_device *dev = plane->dev;
+ struct drm_plane_state *plane_state = plane->state;
+ struct drm_sysfb_plane_state *sysfb_plane_state;
+ struct drm_sysfb_plane_state *new_sysfb_plane_state;
+ struct drm_shadow_plane_state *new_shadow_plane_state;
+
+ if (drm_WARN_ON(dev, !plane_state))
+ return NULL;
+ sysfb_plane_state = to_drm_sysfb_plane_state(plane_state);
+
+ new_sysfb_plane_state = kzalloc(sizeof(*new_sysfb_plane_state), GFP_KERNEL);
+ if (!new_sysfb_plane_state)
+ return NULL;
+ new_shadow_plane_state = &new_sysfb_plane_state->base;
+
+ __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);
+ new_sysfb_plane_state->blit_to_crtc = sysfb_plane_state->blit_to_crtc;
+
+ return &new_shadow_plane_state->base;
+}
+EXPORT_SYMBOL(drm_sysfb_plane_atomic_duplicate_state);
+
+void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane,
+ struct drm_plane_state *plane_state)
+{
+ drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane_state));
+}
+EXPORT_SYMBOL(drm_sysfb_plane_atomic_destroy_state);
+
/*
* CRTC
*/
diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c
index 1883c4a8604c..1b683d55d6ea 100644
--- a/drivers/gpu/drm/sysfb/efidrm.c
+++ b/drivers/gpu/drm/sysfb/efidrm.c
@@ -21,6 +21,7 @@
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <video/edid.h>
diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c
index 8d8ab39c5f36..d38ba70f4e0d 100644
--- a/drivers/gpu/drm/sysfb/ofdrm.c
+++ b/drivers/gpu/drm/sysfb/ofdrm.c
@@ -21,6 +21,7 @@
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "drm_sysfb_helper.h"
diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c
index 0358164a623c..7a95d2dacd9d 100644
--- a/drivers/gpu/drm/sysfb/simpledrm.c
+++ b/drivers/gpu/drm/sysfb/simpledrm.c
@@ -2,8 +2,9 @@
#include <linux/aperture.h>
#include <linux/clk.h>
-#include <linux/of_clk.h>
#include <linux/minmax.h>
+#include <linux/of_address.h>
+#include <linux/of_clk.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_data/simplefb.h>
#include <linux/platform_device.h>
@@ -24,6 +25,7 @@
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "drm_sysfb_helper.h"
diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c
index 16a4b52d45c6..7b7b5ba26317 100644
--- a/drivers/gpu/drm/sysfb/vesadrm.c
+++ b/drivers/gpu/drm/sysfb/vesadrm.c
@@ -22,6 +22,7 @@
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <video/edid.h>
@@ -295,7 +296,8 @@ static int vesadrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs vesadrm_primary_plane_helper_funcs = {
- DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .begin_fb_access = drm_sysfb_plane_helper_begin_fb_access,
+ .end_fb_access = drm_gem_end_shadow_fb_access,
.atomic_check = vesadrm_primary_plane_helper_atomic_check,
.atomic_update = drm_sysfb_plane_helper_atomic_update,
.atomic_disable = drm_sysfb_plane_helper_atomic_disable,
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index 6fc4b504e786..e399b40d64a1 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -25,6 +25,7 @@ tegra-drm-y := \
falcon.o \
vic.o \
nvdec.o \
+ nvjpg.o \
riscv.o
tegra-drm-y += trace.o
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 59d5c1ba145a..01e9d5011dd8 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -27,6 +27,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "dc.h"
@@ -1033,7 +1034,7 @@ static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_a
int min_scale, max_scale;
int err;
- crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
@@ -3148,6 +3149,7 @@ static int tegra_dc_couple(struct tegra_dc *dc)
dc->client.parent = &parent->client;
dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion));
+ put_device(companion);
}
return 0;
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 4596073fe28f..1d18d43292dc 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -22,6 +22,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
@@ -1383,6 +1384,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra210-sor1", },
{ .compatible = "nvidia,tegra210-vic", },
{ .compatible = "nvidia,tegra210-nvdec", },
+ { .compatible = "nvidia,tegra210-nvjpg", },
{ .compatible = "nvidia,tegra186-display", },
{ .compatible = "nvidia,tegra186-dc", },
{ .compatible = "nvidia,tegra186-sor", },
@@ -1421,6 +1423,7 @@ static struct platform_driver * const drivers[] = {
&tegra_gr3d_driver,
&tegra_vic_driver,
&tegra_nvdec_driver,
+ &tegra_nvjpg_driver,
};
static int __init host1x_drm_init(void)
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 1dd3670f37db..ae68b03d8483 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -214,5 +214,6 @@ extern struct platform_driver tegra_gr2d_driver;
extern struct platform_driver tegra_gr3d_driver;
extern struct platform_driver tegra_vic_driver;
extern struct platform_driver tegra_nvdec_driver;
+extern struct platform_driver tegra_nvjpg_driver;
#endif /* HOST1X_DRM_H */
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index b5089b772267..175f5f9937b0 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -22,6 +22,7 @@
#include <drm/drm_file.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "dc.h"
@@ -545,12 +546,19 @@ static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
/* horizontal back porch */
hbp = (mode->htotal - mode->hsync_end) * mul / div;
- if ((dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0)
- hbp += hsw;
-
/* horizontal front porch */
hfp = (mode->hsync_start - mode->hdisplay) * mul / div;
+ if (dsi->master || dsi->slave) {
+ hact /= 2;
+ hsw /= 2;
+ hbp /= 2;
+ hfp /= 2;
+ }
+
+ if ((dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0)
+ hbp += hsw;
+
/* subtract packet overhead */
hsw -= 10;
hbp -= 14;
@@ -560,11 +568,6 @@ static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3);
tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5);
tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7);
-
- /* set SOL delay (for non-burst mode only) */
- tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY);
-
- /* TODO: implement ganged mode */
} else {
u16 bytes;
@@ -586,29 +589,28 @@ static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe,
value = MIPI_DCS_WRITE_MEMORY_START << 8 |
MIPI_DCS_WRITE_MEMORY_CONTINUE;
tegra_dsi_writel(dsi, value, DSI_DCS_CMDS);
+ }
- /* set SOL delay */
- if (dsi->master || dsi->slave) {
- unsigned long delay, bclk, bclk_ganged;
- unsigned int lanes = state->lanes;
-
- /* SOL to valid, valid to FIFO and FIFO write delay */
- delay = 4 + 4 + 2;
- delay = DIV_ROUND_UP(delay * mul, div * lanes);
- /* FIFO read delay */
- delay = delay + 6;
-
- bclk = DIV_ROUND_UP(mode->htotal * mul, div * lanes);
- bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes);
- value = bclk - bclk_ganged + delay + 20;
- } else {
- /* TODO: revisit for non-ganged mode */
- value = 8 * mul / div;
- }
+ /* set SOL delay */
+ if (dsi->master || dsi->slave) {
+ unsigned long delay, bclk, bclk_ganged;
+ unsigned int lanes = state->lanes;
+
+ /* SOL to valid, valid to FIFO and FIFO write delay */
+ delay = 4 + 4 + 2;
+ delay = DIV_ROUND_UP(delay * mul, div * lanes);
+ /* FIFO read delay */
+ delay = delay + 6;
- tegra_dsi_writel(dsi, value, DSI_SOL_DELAY);
+ bclk = DIV_ROUND_UP(mode->htotal * mul, div * lanes);
+ bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes);
+ value = bclk - bclk_ganged + delay + 20;
+ } else {
+ value = 8 * mul / div;
}
+ tegra_dsi_writel(dsi, value, DSI_SOL_DELAY);
+
if (dsi->slave) {
tegra_dsi_configure(dsi->slave, pipe, mode);
@@ -913,15 +915,6 @@ static void tegra_dsi_encoder_enable(struct drm_encoder *encoder)
u32 value;
int err;
- /* If the bootloader enabled DSI it needs to be disabled
- * in order for the panel initialization commands to be
- * properly sent.
- */
- value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL);
-
- if (value & DSI_POWER_CONTROL_ENABLE)
- tegra_dsi_disable(dsi);
-
err = tegra_dsi_prepare(dsi);
if (err < 0) {
dev_err(dsi->dev, "failed to prepare: %d\n", err);
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index dd041089f797..1cef8c5cac50 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -13,6 +13,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include "drm.h"
#include "gem.h"
diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c
index 1b70f5e164af..8f40882aa76e 100644
--- a/drivers/gpu/drm/tegra/fbdev.c
+++ b/drivers/gpu/drm/tegra/fbdev.c
@@ -53,8 +53,6 @@ static void tegra_fbdev_fb_destroy(struct fb_info *info)
drm_framebuffer_remove(fb);
drm_client_release(&helper->client);
- drm_fb_helper_unprepare(helper);
- kfree(helper);
}
static const struct fb_ops tegra_fb_ops = {
@@ -75,10 +73,10 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
struct tegra_drm *tegra = helper->dev->dev_private;
struct drm_device *drm = helper->dev;
struct drm_mode_fb_cmd2 cmd = { 0 };
+ struct fb_info *info = helper->info;
unsigned int bytes_per_pixel;
struct drm_framebuffer *fb;
unsigned long offset;
- struct fb_info *info;
struct tegra_bo *bo;
size_t size;
int err;
@@ -99,13 +97,6 @@ int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
if (IS_ERR(bo))
return PTR_ERR(bo);
- info = drm_fb_helper_alloc_info(helper);
- if (IS_ERR(info)) {
- dev_err(drm->dev, "failed to allocate framebuffer info\n");
- drm_gem_object_put(&bo->gem);
- return PTR_ERR(info);
- }
-
fb = tegra_fb_alloc(drm,
drm_get_format_info(drm, cmd.pixel_format, cmd.modifier[0]),
&cmd, &bo, 1);
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index 8ede07fb7a21..6b14f1e919eb 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -16,6 +16,7 @@
#include <linux/vmalloc.h>
#include <drm/drm_drv.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_prime.h>
#include "drm.h"
@@ -542,12 +543,13 @@ void tegra_bo_free_object(struct drm_gem_object *gem)
int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm,
struct drm_mode_create_dumb *args)
{
- unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
struct tegra_drm *tegra = drm->dev_private;
struct tegra_bo *bo;
+ int ret;
- args->pitch = round_up(min_pitch, tegra->pitch_align);
- args->size = args->pitch * args->height;
+ ret = drm_mode_size_dumb(drm, args, tegra->pitch_align, 0);
+ if (ret)
+ return ret;
bo = tegra_bo_create_with_handle(file, drm, args->size, 0,
&args->handle);
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 8cd2969e7d4b..0adcd4244a42 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -28,6 +28,7 @@
#include <drm/drm_eld.h>
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -658,7 +659,7 @@ static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data,
{
const u8 *ptr = data;
unsigned long offset;
- size_t i, j;
+ size_t i;
u32 value;
switch (ptr[0]) {
@@ -691,7 +692,7 @@ static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data,
* - subpack_low: bytes 0 - 3
* - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00)
*/
- for (i = 3, j = 0; i < size; i += 7, j += 8) {
+ for (i = 3; i < size; i += 7) {
size_t rem = size - i, num = min_t(size_t, rem, 4);
value = tegra_hdmi_subpack(&ptr[i], num);
diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c
index 8f779f23dc09..c924ffba4094 100644
--- a/drivers/gpu/drm/tegra/hub.c
+++ b/drivers/gpu/drm/tegra/hub.c
@@ -20,6 +20,7 @@
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "drm.h"
diff --git a/drivers/gpu/drm/tegra/nvjpg.c b/drivers/gpu/drm/tegra/nvjpg.c
new file mode 100644
index 000000000000..94503fd0d52d
--- /dev/null
+++ b/drivers/gpu/drm/tegra/nvjpg.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/host1x.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "drm.h"
+#include "falcon.h"
+
+struct nvjpg_config {
+ const char *firmware;
+ unsigned int version;
+};
+
+struct nvjpg {
+ struct falcon falcon;
+
+ void __iomem *regs;
+ struct tegra_drm_client client;
+ struct device *dev;
+ struct clk *clk;
+
+ /* Platform configuration */
+ const struct nvjpg_config *config;
+};
+
+static inline struct nvjpg *to_nvjpg(struct tegra_drm_client *client)
+{
+ return container_of(client, struct nvjpg, client);
+}
+
+static int nvjpg_init(struct host1x_client *client)
+{
+ struct tegra_drm_client *drm = host1x_to_drm_client(client);
+ struct drm_device *dev = dev_get_drvdata(client->host);
+ struct tegra_drm *tegra = dev->dev_private;
+ struct nvjpg *nvjpg = to_nvjpg(drm);
+ int err;
+
+ err = host1x_client_iommu_attach(client);
+ if (err < 0 && err != -ENODEV) {
+ dev_err(nvjpg->dev, "failed to attach to domain: %d\n", err);
+ return err;
+ }
+
+ err = tegra_drm_register_client(tegra, drm);
+ if (err < 0)
+ goto detach;
+
+ /*
+ * Inherit the DMA parameters (such as maximum segment size) from the
+ * parent host1x device.
+ */
+ client->dev->dma_parms = client->host->dma_parms;
+
+ return 0;
+
+detach:
+ host1x_client_iommu_detach(client);
+
+ return err;
+}
+
+static int nvjpg_exit(struct host1x_client *client)
+{
+ struct tegra_drm_client *drm = host1x_to_drm_client(client);
+ struct drm_device *dev = dev_get_drvdata(client->host);
+ struct tegra_drm *tegra = dev->dev_private;
+ struct nvjpg *nvjpg = to_nvjpg(drm);
+ int err;
+
+ /* avoid a dangling pointer just in case this disappears */
+ client->dev->dma_parms = NULL;
+
+ err = tegra_drm_unregister_client(tegra, drm);
+ if (err < 0)
+ return err;
+
+ pm_runtime_dont_use_autosuspend(client->dev);
+ pm_runtime_force_suspend(client->dev);
+
+ host1x_client_iommu_detach(client);
+
+ if (client->group) {
+ dma_unmap_single(nvjpg->dev, nvjpg->falcon.firmware.phys,
+ nvjpg->falcon.firmware.size, DMA_TO_DEVICE);
+ tegra_drm_free(tegra, nvjpg->falcon.firmware.size,
+ nvjpg->falcon.firmware.virt,
+ nvjpg->falcon.firmware.iova);
+ } else {
+ dma_free_coherent(nvjpg->dev, nvjpg->falcon.firmware.size,
+ nvjpg->falcon.firmware.virt,
+ nvjpg->falcon.firmware.iova);
+ }
+
+ return 0;
+}
+
+static const struct host1x_client_ops nvjpg_client_ops = {
+ .init = nvjpg_init,
+ .exit = nvjpg_exit,
+};
+
+static int nvjpg_load_falcon_firmware(struct nvjpg *nvjpg)
+{
+ struct host1x_client *client = &nvjpg->client.base;
+ struct tegra_drm *tegra = nvjpg->client.drm;
+ dma_addr_t iova;
+ size_t size;
+ void *virt;
+ int err;
+
+ if (nvjpg->falcon.firmware.virt)
+ return 0;
+
+ err = falcon_read_firmware(&nvjpg->falcon, nvjpg->config->firmware);
+ if (err < 0)
+ return err;
+
+ size = nvjpg->falcon.firmware.size;
+
+ if (!client->group) {
+ virt = dma_alloc_coherent(nvjpg->dev, size, &iova, GFP_KERNEL);
+ if (!virt)
+ return -ENOMEM;
+ } else {
+ virt = tegra_drm_alloc(tegra, size, &iova);
+ if (IS_ERR(virt))
+ return PTR_ERR(virt);
+ }
+
+ nvjpg->falcon.firmware.virt = virt;
+ nvjpg->falcon.firmware.iova = iova;
+
+ err = falcon_load_firmware(&nvjpg->falcon);
+ if (err < 0)
+ goto cleanup;
+
+ /*
+ * In this case we have received an IOVA from the shared domain, so we
+ * need to make sure to get the physical address so that the DMA API
+ * knows what memory pages to flush the cache for.
+ */
+ if (client->group) {
+ dma_addr_t phys;
+
+ phys = dma_map_single(nvjpg->dev, virt, size, DMA_TO_DEVICE);
+
+ err = dma_mapping_error(nvjpg->dev, phys);
+ if (err < 0)
+ goto cleanup;
+
+ nvjpg->falcon.firmware.phys = phys;
+ }
+
+ return 0;
+
+cleanup:
+ if (!client->group)
+ dma_free_coherent(nvjpg->dev, size, virt, iova);
+ else
+ tegra_drm_free(tegra, size, virt, iova);
+
+ return err;
+}
+
+static __maybe_unused int nvjpg_runtime_resume(struct device *dev)
+{
+ struct nvjpg *nvjpg = dev_get_drvdata(dev);
+ int err;
+
+ err = clk_prepare_enable(nvjpg->clk);
+ if (err < 0)
+ return err;
+
+ usleep_range(20, 30);
+
+ err = nvjpg_load_falcon_firmware(nvjpg);
+ if (err < 0)
+ goto disable_clk;
+
+ err = falcon_boot(&nvjpg->falcon);
+ if (err < 0)
+ goto disable_clk;
+
+ return 0;
+
+disable_clk:
+ clk_disable_unprepare(nvjpg->clk);
+ return err;
+}
+
+static __maybe_unused int nvjpg_runtime_suspend(struct device *dev)
+{
+ struct nvjpg *nvjpg = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(nvjpg->clk);
+
+ return 0;
+}
+
+static int nvjpg_can_use_memory_ctx(struct tegra_drm_client *client, bool *supported)
+{
+ *supported = false;
+
+ return 0;
+}
+
+static const struct tegra_drm_client_ops nvjpg_ops = {
+ .get_streamid_offset = NULL,
+ .can_use_memory_ctx = nvjpg_can_use_memory_ctx,
+};
+
+#define NVIDIA_TEGRA_210_NVJPG_FIRMWARE "nvidia/tegra210/nvjpg.bin"
+
+static const struct nvjpg_config tegra210_nvjpg_config = {
+ .firmware = NVIDIA_TEGRA_210_NVJPG_FIRMWARE,
+ .version = 0x21,
+};
+
+static const struct of_device_id tegra_nvjpg_of_match[] = {
+ { .compatible = "nvidia,tegra210-nvjpg", .data = &tegra210_nvjpg_config },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_nvjpg_of_match);
+
+static int nvjpg_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nvjpg *nvjpg;
+ int err;
+
+ /* inherit DMA mask from host1x parent */
+ err = dma_coerce_mask_and_coherent(dev, *dev->parent->dma_mask);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
+ return err;
+ }
+
+ nvjpg = devm_kzalloc(dev, sizeof(*nvjpg), GFP_KERNEL);
+ if (!nvjpg)
+ return -ENOMEM;
+
+ nvjpg->config = of_device_get_match_data(dev);
+
+ nvjpg->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(nvjpg->regs))
+ return PTR_ERR(nvjpg->regs);
+
+ nvjpg->clk = devm_clk_get(dev, "nvjpg");
+ if (IS_ERR(nvjpg->clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(nvjpg->clk);
+ }
+
+ err = clk_set_rate(nvjpg->clk, ULONG_MAX);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to set clock rate\n");
+ return err;
+ }
+
+ nvjpg->falcon.dev = dev;
+ nvjpg->falcon.regs = nvjpg->regs;
+
+ err = falcon_init(&nvjpg->falcon);
+ if (err < 0)
+ return err;
+
+ platform_set_drvdata(pdev, nvjpg);
+
+ INIT_LIST_HEAD(&nvjpg->client.base.list);
+ nvjpg->client.base.ops = &nvjpg_client_ops;
+ nvjpg->client.base.dev = dev;
+ nvjpg->client.base.class = HOST1X_CLASS_NVJPG;
+ nvjpg->dev = dev;
+
+ INIT_LIST_HEAD(&nvjpg->client.list);
+ nvjpg->client.version = nvjpg->config->version;
+ nvjpg->client.ops = &nvjpg_ops;
+
+ err = host1x_client_register(&nvjpg->client.base);
+ if (err < 0) {
+ dev_err(dev, "failed to register host1x client: %d\n", err);
+ goto exit_falcon;
+ }
+
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ devm_pm_runtime_enable(dev);
+
+ return 0;
+
+exit_falcon:
+ falcon_exit(&nvjpg->falcon);
+
+ return err;
+}
+
+static void nvjpg_remove(struct platform_device *pdev)
+{
+ struct nvjpg *nvjpg = platform_get_drvdata(pdev);
+
+ host1x_client_unregister(&nvjpg->client.base);
+ falcon_exit(&nvjpg->falcon);
+}
+
+static const struct dev_pm_ops nvjpg_pm_ops = {
+ RUNTIME_PM_OPS(nvjpg_runtime_suspend, nvjpg_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+struct platform_driver tegra_nvjpg_driver = {
+ .driver = {
+ .name = "tegra-nvjpg",
+ .of_match_table = tegra_nvjpg_of_match,
+ .pm = &nvjpg_pm_ops
+ },
+ .probe = nvjpg_probe,
+ .remove = nvjpg_remove,
+};
+
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC)
+MODULE_FIRMWARE(NVIDIA_TEGRA_210_NVJPG_FIRMWARE);
+#endif
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 21f3dfdcc5c9..4023cb5998f1 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -24,6 +24,7 @@
#include <drm/drm_eld.h>
#include <drm/drm_file.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_simple_kms_helper.h>
#include "dc.h"
@@ -1864,7 +1865,7 @@ static void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor,
{
const u8 *ptr = data;
unsigned long offset;
- size_t i, j;
+ size_t i;
u32 value;
switch (ptr[0]) {
@@ -1897,7 +1898,7 @@ static void tegra_sor_hdmi_write_infopack(struct tegra_sor *sor,
* - subpack_low: bytes 0 - 3
* - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00)
*/
- for (i = 3, j = 0; i < size; i += 7, j += 8) {
+ for (i = 3; i < size; i += 7) {
size_t rem = size - i, num = min_t(size_t, rem, 4);
value = tegra_sor_hdmi_subpack(&ptr[i], num);
diff --git a/drivers/gpu/drm/tegra/uapi.c b/drivers/gpu/drm/tegra/uapi.c
index 5adab6b22916..d0b6a1fa6efa 100644
--- a/drivers/gpu/drm/tegra/uapi.c
+++ b/drivers/gpu/drm/tegra/uapi.c
@@ -114,9 +114,12 @@ int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_
if (err)
goto put_channel;
- if (supported)
+ if (supported) {
+ struct pid *pid = get_task_pid(current, PIDTYPE_TGID);
context->memory_context = host1x_memory_context_alloc(
- host, client->base.dev, get_task_pid(current, PIDTYPE_TGID));
+ host, client->base.dev, pid);
+ put_pid(pid);
+ }
if (IS_ERR(context->memory_context)) {
if (PTR_ERR(context->memory_context) != -EOPNOTSUPP) {
diff --git a/drivers/gpu/drm/tests/.kunitconfig b/drivers/gpu/drm/tests/.kunitconfig
index 6ec04b4c979d..5be8e71f45d5 100644
--- a/drivers/gpu/drm/tests/.kunitconfig
+++ b/drivers/gpu/drm/tests/.kunitconfig
@@ -1,3 +1,5 @@
CONFIG_KUNIT=y
CONFIG_DRM=y
+CONFIG_DRM_VKMS=y
+CONFIG_DRM_FBDEV_EMULATION=y
CONFIG_DRM_KUNIT_TEST=y
diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index c0e952293ad0..87d5d5f9332a 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_plane_helper_test.o \
drm_probe_helper_test.o \
drm_rect_test.o \
- drm_sysfb_modeset_test.o
+ drm_sysfb_modeset_test.o \
+ drm_fixp_test.o
CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN)
diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c b/drivers/gpu/drm/tests/drm_buddy_test.c
index 7a0e523651f0..5f40b5343bd8 100644
--- a/drivers/gpu/drm/tests/drm_buddy_test.c
+++ b/drivers/gpu/drm/tests/drm_buddy_test.c
@@ -21,6 +21,110 @@ static inline u64 get_size(int order, u64 chunk_size)
return (1 << order) * chunk_size;
}
+static void drm_test_buddy_fragmentation_performance(struct kunit *test)
+{
+ struct drm_buddy_block *block, *tmp;
+ int num_blocks, i, ret, count = 0;
+ LIST_HEAD(allocated_blocks);
+ unsigned long elapsed_ms;
+ LIST_HEAD(reverse_list);
+ LIST_HEAD(test_blocks);
+ LIST_HEAD(clear_list);
+ LIST_HEAD(dirty_list);
+ LIST_HEAD(free_list);
+ struct drm_buddy mm;
+ u64 mm_size = SZ_4G;
+ ktime_t start, end;
+
+ /*
+ * Allocation under severe fragmentation
+ *
+ * Create severe fragmentation by allocating the entire 4 GiB address space
+ * as tiny 8 KiB blocks but forcing a 64 KiB alignment. The resulting pattern
+ * leaves many scattered holes. Split the allocations into two groups and
+ * return them with different flags to block coalescing, then repeatedly
+ * allocate and free 64 KiB blocks while timing the loop. This stresses how
+ * quickly the allocator can satisfy larger, aligned requests from a pool of
+ * highly fragmented space.
+ */
+ KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, SZ_4K),
+ "buddy_init failed\n");
+
+ num_blocks = mm_size / SZ_64K;
+
+ start = ktime_get();
+ /* Allocate with maximum fragmentation - 8K blocks with 64K alignment */
+ for (i = 0; i < num_blocks; i++)
+ KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_64K,
+ &allocated_blocks, 0),
+ "buddy_alloc hit an error size=%u\n", SZ_8K);
+
+ list_for_each_entry_safe(block, tmp, &allocated_blocks, link) {
+ if (count % 4 == 0 || count % 4 == 3)
+ list_move_tail(&block->link, &clear_list);
+ else
+ list_move_tail(&block->link, &dirty_list);
+ count++;
+ }
+
+ /* Free with different flags to ensure no coalescing */
+ drm_buddy_free_list(&mm, &clear_list, DRM_BUDDY_CLEARED);
+ drm_buddy_free_list(&mm, &dirty_list, 0);
+
+ for (i = 0; i < num_blocks; i++)
+ KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, SZ_64K, SZ_64K,
+ &test_blocks, 0),
+ "buddy_alloc hit an error size=%u\n", SZ_64K);
+ drm_buddy_free_list(&mm, &test_blocks, 0);
+
+ end = ktime_get();
+ elapsed_ms = ktime_to_ms(ktime_sub(end, start));
+
+ kunit_info(test, "Fragmented allocation took %lu ms\n", elapsed_ms);
+
+ drm_buddy_fini(&mm);
+
+ /*
+ * Reverse free order under fragmentation
+ *
+ * Construct a fragmented 4 GiB space by allocating every 8 KiB block with
+ * 64 KiB alignment, creating a dense scatter of small regions. Half of the
+ * blocks are selectively freed to form sparse gaps, while the remaining
+ * allocations are preserved, reordered in reverse, and released back with
+ * the cleared flag. This models a pathological reverse-ordered free pattern
+ * and measures how quickly the allocator can merge and reclaim space when
+ * deallocation occurs in the opposite order of allocation, exposing the
+ * cost difference between a linear freelist scan and an ordered tree lookup.
+ */
+ ret = drm_buddy_init(&mm, mm_size, SZ_4K);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ start = ktime_get();
+ /* Allocate maximum fragmentation */
+ for (i = 0; i < num_blocks; i++)
+ KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, 0, mm_size, SZ_8K, SZ_64K,
+ &allocated_blocks, 0),
+ "buddy_alloc hit an error size=%u\n", SZ_8K);
+
+ list_for_each_entry_safe(block, tmp, &allocated_blocks, link) {
+ if (count % 2 == 0)
+ list_move_tail(&block->link, &free_list);
+ count++;
+ }
+ drm_buddy_free_list(&mm, &free_list, DRM_BUDDY_CLEARED);
+
+ list_for_each_entry_safe_reverse(block, tmp, &allocated_blocks, link)
+ list_move(&block->link, &reverse_list);
+ drm_buddy_free_list(&mm, &reverse_list, DRM_BUDDY_CLEARED);
+
+ end = ktime_get();
+ elapsed_ms = ktime_to_ms(ktime_sub(end, start));
+
+ kunit_info(test, "Reverse-ordered free took %lu ms\n", elapsed_ms);
+
+ drm_buddy_fini(&mm);
+}
+
static void drm_test_buddy_alloc_range_bias(struct kunit *test)
{
u32 mm_size, size, ps, bias_size, bias_start, bias_end, bias_rem;
@@ -772,6 +876,7 @@ static struct kunit_case drm_buddy_tests[] = {
KUNIT_CASE(drm_test_buddy_alloc_contiguous),
KUNIT_CASE(drm_test_buddy_alloc_clear),
KUNIT_CASE(drm_test_buddy_alloc_range_bias),
+ KUNIT_CASE(drm_test_buddy_fragmentation_performance),
{}
};
diff --git a/drivers/gpu/drm/tests/drm_fixp_test.c b/drivers/gpu/drm/tests/drm_fixp_test.c
new file mode 100644
index 000000000000..dd77fdedb2a9
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_fixp_test.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2022 Advanced Micro Devices, Inc.
+ */
+
+#include <kunit/test.h>
+#include <drm/drm_fixed.h>
+
+static void drm_test_sm2fixp(struct kunit *test)
+{
+ KUNIT_EXPECT_EQ(test, 0x7fffffffffffffffll, ((1ull << 63) - 1));
+
+ /* 1 */
+ KUNIT_EXPECT_EQ(test, drm_int2fixp(1), drm_sm2fixp(1ull << DRM_FIXED_POINT));
+
+ /* -1 */
+ KUNIT_EXPECT_EQ(test, drm_int2fixp(-1),
+ drm_sm2fixp((1ull << 63) | (1ull << DRM_FIXED_POINT)));
+
+ /* 0.5 */
+ KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(1, 2),
+ drm_sm2fixp(1ull << (DRM_FIXED_POINT - 1)));
+
+ /* -0.5 */
+ KUNIT_EXPECT_EQ(test, drm_fixp_from_fraction(-1, 2),
+ drm_sm2fixp((1ull << 63) | (1ull << (DRM_FIXED_POINT - 1))));
+}
+
+static void drm_test_int2fixp(struct kunit *test)
+{
+ /* 1 */
+ KUNIT_EXPECT_EQ(test, 1ll << 32, drm_int2fixp(1));
+
+ /* -1 */
+ KUNIT_EXPECT_EQ(test, -(1ll << 32), drm_int2fixp(-1));
+
+ /* 1 + (-1) = 0 */
+ KUNIT_EXPECT_EQ(test, 0, drm_int2fixp(1) + drm_int2fixp(-1));
+
+ /* 1 / 2 */
+ KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(1, 2));
+
+ /* -0.5 */
+ KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(-1, 2));
+
+ /* (1 / 2) + (-1) = 0.5 */
+ KUNIT_EXPECT_EQ(test, 1ll << 31, drm_fixp_from_fraction(-1, 2) + drm_int2fixp(1));
+
+ /* (1 / 2) - 1) = 0.5 */
+ KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) + drm_int2fixp(-1));
+
+ /* (1 / 2) - 1) = 0.5 */
+ KUNIT_EXPECT_EQ(test, -(1ll << 31), drm_fixp_from_fraction(1, 2) - drm_int2fixp(1));
+}
+
+static struct kunit_case drm_fixp_tests[] = {
+ KUNIT_CASE(drm_test_int2fixp),
+ KUNIT_CASE(drm_test_sm2fixp),
+ { }
+};
+
+static struct kunit_suite drm_fixp_test_suite = {
+ .name = "drm_fixp",
+ .test_cases = drm_fixp_tests,
+};
+
+kunit_test_suite(drm_fixp_test_suite);
+
+MODULE_AUTHOR("AMD");
+MODULE_LICENSE("Dual MIT/GPL");
+MODULE_DESCRIPTION("Unit tests for drm_fixed.h");
diff --git a/drivers/gpu/drm/tests/drm_mm_test.c b/drivers/gpu/drm/tests/drm_mm_test.c
index 6174d0929020..aec9eccdeae9 100644
--- a/drivers/gpu/drm/tests/drm_mm_test.c
+++ b/drivers/gpu/drm/tests/drm_mm_test.c
@@ -14,6 +14,7 @@
#include <linux/ktime.h>
#include <drm/drm_mm.h>
+#include <drm/drm_print.h>
#include "../lib/drm_random.h"
diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c
index da89fd01c337..8f81eb560b9e 100644
--- a/drivers/gpu/drm/tidss/tidss_crtc.c
+++ b/drivers/gpu/drm/tidss/tidss_crtc.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "tidss_crtc.h"
@@ -94,8 +95,6 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_display_mode *mode;
enum drm_mode_status ok;
- dev_dbg(ddev->dev, "%s\n", __func__);
-
if (!crtc_state->enable)
return 0;
@@ -103,7 +102,7 @@ static int tidss_crtc_atomic_check(struct drm_crtc *crtc,
ok = dispc_vp_mode_valid(dispc, hw_videoport, mode);
if (ok != MODE_OK) {
- dev_dbg(ddev->dev, "%s: bad mode: %ux%u pclk %u kHz\n",
+ drm_dbg(ddev, "%s: bad mode: %ux%u pclk %u kHz\n",
__func__, mode->hdisplay, mode->vdisplay, mode->clock);
return -EINVAL;
}
@@ -172,7 +171,7 @@ static void tidss_crtc_atomic_flush(struct drm_crtc *crtc,
struct tidss_device *tidss = to_tidss(ddev);
unsigned long flags;
- dev_dbg(ddev->dev, "%s: %s is %sactive, %s modeset, event %p\n",
+ drm_dbg(ddev, "%s: %s is %sactive, %s modeset, event %p\n",
__func__, crtc->name, crtc->state->active ? "" : "not ",
drm_atomic_crtc_needs_modeset(crtc->state) ? "needs" : "doesn't need",
crtc->state->event);
@@ -244,11 +243,15 @@ static void tidss_crtc_atomic_enable(struct drm_crtc *crtc,
dispc_vp_prepare(tidss->dispc, tcrtc->hw_videoport, crtc->state);
- dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport, crtc->state);
-
spin_lock_irqsave(&ddev->event_lock, flags);
+ dispc_vp_enable(tidss->dispc, tcrtc->hw_videoport);
+
if (crtc->state->event) {
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+
+ vblank->time = ktime_get();
+
drm_crtc_send_vblank_event(crtc, crtc->state->event);
crtc->state->event = NULL;
}
@@ -328,8 +331,6 @@ static int tidss_crtc_enable_vblank(struct drm_crtc *crtc)
struct drm_device *ddev = crtc->dev;
struct tidss_device *tidss = to_tidss(ddev);
- dev_dbg(ddev->dev, "%s\n", __func__);
-
tidss_runtime_get(tidss);
tidss_irq_enable_vblank(crtc);
@@ -342,29 +343,34 @@ static void tidss_crtc_disable_vblank(struct drm_crtc *crtc)
struct drm_device *ddev = crtc->dev;
struct tidss_device *tidss = to_tidss(ddev);
- dev_dbg(ddev->dev, "%s\n", __func__);
-
tidss_irq_disable_vblank(crtc);
tidss_runtime_put(tidss);
}
+static void tidss_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct tidss_crtc_state *tstate = to_tidss_crtc_state(state);
+
+ __drm_atomic_helper_crtc_destroy_state(&tstate->base);
+ kfree(tstate);
+}
+
static void tidss_crtc_reset(struct drm_crtc *crtc)
{
- struct tidss_crtc_state *tcrtc;
+ struct tidss_crtc_state *tstate;
if (crtc->state)
- __drm_atomic_helper_crtc_destroy_state(crtc->state);
-
- kfree(crtc->state);
+ tidss_crtc_destroy_state(crtc, crtc->state);
- tcrtc = kzalloc(sizeof(*tcrtc), GFP_KERNEL);
- if (!tcrtc) {
+ tstate = kzalloc(sizeof(*tstate), GFP_KERNEL);
+ if (!tstate) {
crtc->state = NULL;
return;
}
- __drm_atomic_helper_crtc_reset(crtc, &tcrtc->base);
+ __drm_atomic_helper_crtc_reset(crtc, &tstate->base);
}
static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc)
@@ -404,7 +410,7 @@ static const struct drm_crtc_funcs tidss_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = tidss_crtc_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .atomic_destroy_state = tidss_crtc_destroy_state,
.enable_vblank = tidss_crtc_enable_vblank,
.disable_vblank = tidss_crtc_disable_vblank,
};
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index 7c8c15a5c39b..58d5eb033bdb 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -27,6 +27,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include "tidss_crtc.h"
#include "tidss_dispc.h"
@@ -57,12 +58,6 @@ static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
};
const struct dispc_features dispc_k2g_feats = {
- .min_pclk_khz = 4375,
-
- .max_pclk_khz = {
- [DISPC_VP_DPI] = 150000,
- },
-
/*
* XXX According TRM the RGB input buffer width up to 2560 should
* work on 3 taps, but in practice it only works up to 1280.
@@ -145,11 +140,6 @@ static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
};
const struct dispc_features dispc_am65x_feats = {
- .max_pclk_khz = {
- [DISPC_VP_DPI] = 165000,
- [DISPC_VP_OLDI_AM65X] = 165000,
- },
-
.scaling = {
.in_width_max_5tap_rgb = 1280,
.in_width_max_3tap_rgb = 2560,
@@ -245,11 +235,6 @@ static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
};
const struct dispc_features dispc_j721e_feats = {
- .max_pclk_khz = {
- [DISPC_VP_DPI] = 170000,
- [DISPC_VP_INTERNAL] = 600000,
- },
-
.scaling = {
.in_width_max_5tap_rgb = 2048,
.in_width_max_3tap_rgb = 4096,
@@ -316,11 +301,6 @@ const struct dispc_features dispc_j721e_feats = {
};
const struct dispc_features dispc_am625_feats = {
- .max_pclk_khz = {
- [DISPC_VP_DPI] = 165000,
- [DISPC_VP_INTERNAL] = 170000,
- },
-
.scaling = {
.in_width_max_5tap_rgb = 1280,
.in_width_max_3tap_rgb = 2560,
@@ -377,15 +357,6 @@ const struct dispc_features dispc_am625_feats = {
};
const struct dispc_features dispc_am62a7_feats = {
- /*
- * if the code reaches dispc_mode_valid with VP1,
- * it should return MODE_BAD.
- */
- .max_pclk_khz = {
- [DISPC_VP_TIED_OFF] = 0,
- [DISPC_VP_DPI] = 165000,
- },
-
.scaling = {
.in_width_max_5tap_rgb = 1280,
.in_width_max_3tap_rgb = 2560,
@@ -442,10 +413,6 @@ const struct dispc_features dispc_am62a7_feats = {
};
const struct dispc_features dispc_am62l_feats = {
- .max_pclk_khz = {
- [DISPC_VP_DPI] = 165000,
- },
-
.subrev = DISPC_AM62L,
.common = "common",
@@ -1051,20 +1018,22 @@ struct dispc_bus_format *dispc_vp_find_bus_fmt(struct dispc_device *dispc,
int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport,
const struct drm_crtc_state *state)
{
+ struct tidss_device *tidss = dispc->tidss;
+ struct drm_device *dev = &tidss->ddev;
const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state);
const struct dispc_bus_format *fmt;
fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format,
tstate->bus_flags);
if (!fmt) {
- dev_dbg(dispc->dev, "%s: Unsupported bus format: %u\n",
+ drm_dbg(dev, "%s: Unsupported bus format: %u\n",
__func__, tstate->bus_format);
return -EINVAL;
}
if (dispc->feat->vp_bus_type[hw_videoport] != DISPC_VP_OLDI_AM65X &&
fmt->is_oldi_fmt) {
- dev_dbg(dispc->dev, "%s: %s is not OLDI-port\n",
+ drm_dbg(dev, "%s: %s is not OLDI-port\n",
__func__, dispc->feat->vp_name[hw_videoport]);
return -EINVAL;
}
@@ -1161,6 +1130,9 @@ void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport,
{
const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state);
const struct dispc_bus_format *fmt;
+ const struct drm_display_mode *mode = &state->adjusted_mode;
+ bool align, onoff, rf, ieo, ipc, ihs, ivs;
+ u32 hsw, hfp, hbp, vsw, vfp, vbp;
fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format,
tstate->bus_flags);
@@ -1173,22 +1145,6 @@ void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport,
dispc_enable_am65x_oldi(dispc, hw_videoport, fmt);
}
-}
-
-void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport,
- const struct drm_crtc_state *state)
-{
- const struct drm_display_mode *mode = &state->adjusted_mode;
- const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state);
- bool align, onoff, rf, ieo, ipc, ihs, ivs;
- const struct dispc_bus_format *fmt;
- u32 hsw, hfp, hbp, vsw, vfp, vbp;
-
- fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format,
- tstate->bus_flags);
-
- if (WARN_ON(!fmt))
- return;
dispc_set_num_datalines(dispc, hw_videoport, fmt->data_width);
@@ -1244,7 +1200,10 @@ void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport,
mode->crtc_hdisplay - 1) |
FIELD_PREP(DISPC_VP_SIZE_SCREEN_VDISPLAY_MASK,
mode->crtc_vdisplay - 1));
+}
+void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport)
+{
VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1,
DISPC_VP_CONTROL_ENABLE_MASK);
}
@@ -1331,33 +1290,61 @@ static void dispc_vp_set_default_color(struct dispc_device *dispc,
DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
}
+/*
+ * Calculate the percentage difference between the requested pixel clock rate
+ * and the effective rate resulting from calculating the clock divider value.
+ */
+unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
+{
+ int r = rate / 100, rr = real_rate / 100;
+
+ return (unsigned int)(abs(((rr - r) * 100) / r));
+}
+
+static int check_pixel_clock(struct dispc_device *dispc, u32 hw_videoport,
+ unsigned long clock)
+{
+ unsigned long round_clock;
+
+ /*
+ * For VP's with external clocking, clock operations must be
+ * delegated to respective driver, so we skip the check here.
+ */
+ if (dispc->tidss->is_ext_vp_clk[hw_videoport])
+ return 0;
+
+ round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
+ /*
+ * To keep the check consistent with dispc_vp_set_clk_rate(), we
+ * use the same 5% check here.
+ */
+ if (dispc_pclk_diff(clock, round_clock) > 5)
+ return -EINVAL;
+
+ return 0;
+}
+
enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
u32 hw_videoport,
const struct drm_display_mode *mode)
{
u32 hsw, hfp, hbp, vsw, vfp, vbp;
enum dispc_vp_bus_type bus_type;
- int max_pclk;
bus_type = dispc->feat->vp_bus_type[hw_videoport];
- max_pclk = dispc->feat->max_pclk_khz[bus_type];
-
- if (WARN_ON(max_pclk == 0))
+ if (WARN_ON(bus_type == DISPC_VP_TIED_OFF))
return MODE_BAD;
- if (mode->clock < dispc->feat->min_pclk_khz)
- return MODE_CLOCK_LOW;
-
- if (mode->clock > max_pclk)
- return MODE_CLOCK_HIGH;
-
if (mode->hdisplay > 4096)
return MODE_BAD;
if (mode->vdisplay > 4096)
return MODE_BAD;
+ if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
+ return MODE_CLOCK_RANGE;
+
/* TODO: add interlace support */
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;
@@ -1421,17 +1408,6 @@ void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport)
clk_disable_unprepare(dispc->vp_clk[hw_videoport]);
}
-/*
- * Calculate the percentage difference between the requested pixel clock rate
- * and the effective rate resulting from calculating the clock divider value.
- */
-unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
-{
- int r = rate / 100, rr = real_rate / 100;
-
- return (unsigned int)(abs(((rr - r) * 100) / r));
-}
-
int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
unsigned long rate)
{
@@ -2849,8 +2825,6 @@ int dispc_runtime_resume(struct dispc_device *dispc)
void dispc_remove(struct tidss_device *tidss)
{
- dev_dbg(tidss->dev, "%s\n", __func__);
-
tidss->dispc = NULL;
}
@@ -2992,8 +2966,6 @@ int dispc_init(struct tidss_device *tidss)
unsigned int i, num_fourccs;
int r = 0;
- dev_dbg(dev, "%s\n", __func__);
-
feat = tidss->feat;
if (feat->subrev != DISPC_K2G) {
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
index 60c1b400eb89..739d211d0018 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.h
+++ b/drivers/gpu/drm/tidss/tidss_dispc.h
@@ -77,9 +77,6 @@ enum dispc_dss_subrevision {
};
struct dispc_features {
- int min_pclk_khz;
- int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
-
struct dispc_features_scaling scaling;
enum dispc_dss_subrevision subrev;
@@ -119,8 +116,7 @@ void dispc_ovr_enable_layer(struct dispc_device *dispc,
void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport,
const struct drm_crtc_state *state);
-void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport,
- const struct drm_crtc_state *state);
+void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport);
void dispc_vp_disable(struct dispc_device *dispc, u32 hw_videoport);
void dispc_vp_unprepare(struct dispc_device *dispc, u32 hw_videoport);
bool dispc_vp_go_busy(struct dispc_device *dispc, u32 hw_videoport);
diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c
index 27d9a8fd541f..1c8cc18bc53c 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.c
+++ b/drivers/gpu/drm/tidss/tidss_drv.c
@@ -33,8 +33,6 @@ int tidss_runtime_get(struct tidss_device *tidss)
{
int r;
- dev_dbg(tidss->dev, "%s\n", __func__);
-
r = pm_runtime_resume_and_get(tidss->dev);
WARN_ON(r < 0);
return r;
@@ -44,8 +42,6 @@ void tidss_runtime_put(struct tidss_device *tidss)
{
int r;
- dev_dbg(tidss->dev, "%s\n", __func__);
-
pm_runtime_mark_last_busy(tidss->dev);
r = pm_runtime_put_autosuspend(tidss->dev);
@@ -56,8 +52,6 @@ static int __maybe_unused tidss_pm_runtime_suspend(struct device *dev)
{
struct tidss_device *tidss = dev_get_drvdata(dev);
- dev_dbg(dev, "%s\n", __func__);
-
return dispc_runtime_suspend(tidss->dispc);
}
@@ -66,8 +60,6 @@ static int __maybe_unused tidss_pm_runtime_resume(struct device *dev)
struct tidss_device *tidss = dev_get_drvdata(dev);
int r;
- dev_dbg(dev, "%s\n", __func__);
-
r = dispc_runtime_resume(tidss->dispc);
if (r)
return r;
@@ -79,8 +71,6 @@ static int __maybe_unused tidss_suspend(struct device *dev)
{
struct tidss_device *tidss = dev_get_drvdata(dev);
- dev_dbg(dev, "%s\n", __func__);
-
return drm_mode_config_helper_suspend(&tidss->ddev);
}
@@ -88,8 +78,6 @@ static int __maybe_unused tidss_resume(struct device *dev)
{
struct tidss_device *tidss = dev_get_drvdata(dev);
- dev_dbg(dev, "%s\n", __func__);
-
return drm_mode_config_helper_resume(&tidss->ddev);
}
@@ -127,8 +115,6 @@ static int tidss_probe(struct platform_device *pdev)
int ret;
int irq;
- dev_dbg(dev, "%s\n", __func__);
-
tidss = devm_drm_dev_alloc(&pdev->dev, &tidss_driver,
struct tidss_device, ddev);
if (IS_ERR(tidss))
@@ -228,8 +214,6 @@ static void tidss_remove(struct platform_device *pdev)
struct tidss_device *tidss = platform_get_drvdata(pdev);
struct drm_device *ddev = &tidss->ddev;
- dev_dbg(dev, "%s\n", __func__);
-
drm_dev_unregister(ddev);
drm_atomic_helper_shutdown(ddev);
diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
index 84454a4855d1..e1c1f41d8b4b 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.h
+++ b/drivers/gpu/drm/tidss/tidss_drv.h
@@ -24,6 +24,8 @@ struct tidss_device {
const struct dispc_features *feat;
struct dispc_device *dispc;
+ bool is_ext_vp_clk[TIDSS_MAX_PORTS];
+
unsigned int num_crtcs;
struct drm_crtc *crtcs[TIDSS_MAX_PORTS];
diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c
index c34eb90cddbe..86eb5d97410b 100644
--- a/drivers/gpu/drm/tidss/tidss_kms.c
+++ b/drivers/gpu/drm/tidss/tidss_kms.c
@@ -24,8 +24,6 @@ static void tidss_atomic_commit_tail(struct drm_atomic_state *old_state)
struct drm_device *ddev = old_state->dev;
struct tidss_device *tidss = to_tidss(ddev);
- dev_dbg(ddev->dev, "%s\n", __func__);
-
tidss_runtime_get(tidss);
drm_atomic_helper_commit_modeset_disables(ddev, old_state);
@@ -245,8 +243,6 @@ int tidss_modeset_init(struct tidss_device *tidss)
struct drm_device *ddev = &tidss->ddev;
int ret;
- dev_dbg(tidss->dev, "%s\n", __func__);
-
ret = drmm_mode_config_init(ddev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c
index 7688251beba2..17c535bfa057 100644
--- a/drivers/gpu/drm/tidss/tidss_oldi.c
+++ b/drivers/gpu/drm/tidss/tidss_oldi.c
@@ -309,6 +309,25 @@ static u32 *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
return input_fmts;
}
+static enum drm_mode_status
+tidss_oldi_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
+ unsigned long round_clock;
+
+ round_clock = clk_round_rate(oldi->serial, mode->clock * 7 * 1000);
+ /*
+ * To keep the check consistent with dispc_vp_set_clk_rate(),
+ * we use the same 5% check here.
+ */
+ if (dispc_pclk_diff(mode->clock * 7 * 1000, round_clock) > 5)
+ return -EINVAL;
+
+ return 0;
+}
+
static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
.attach = tidss_oldi_bridge_attach,
.atomic_pre_enable = tidss_oldi_atomic_pre_enable,
@@ -317,6 +336,7 @@ static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
.atomic_reset = drm_atomic_helper_bridge_reset,
+ .mode_valid = tidss_oldi_mode_valid,
};
static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
@@ -430,6 +450,7 @@ void tidss_oldi_deinit(struct tidss_device *tidss)
for (int i = 0; i < tidss->num_oldis; i++) {
if (tidss->oldis[i]) {
drm_bridge_remove(&tidss->oldis[i]->bridge);
+ tidss->is_ext_vp_clk[tidss->oldis[i]->parent_vp] = false;
tidss->oldis[i] = NULL;
}
}
@@ -580,6 +601,7 @@ int tidss_oldi_init(struct tidss_device *tidss)
oldi->bridge.timings = &default_tidss_oldi_timings;
tidss->oldis[tidss->num_oldis++] = oldi;
+ tidss->is_ext_vp_clk[oldi->parent_vp] = true;
oldi->tidss = tidss;
drm_bridge_add(&oldi->bridge);
diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c
index 142ae81951a0..bd10bc1b9961 100644
--- a/drivers/gpu/drm/tidss/tidss_plane.c
+++ b/drivers/gpu/drm/tidss/tidss_plane.c
@@ -42,8 +42,6 @@ static int tidss_plane_atomic_check(struct drm_plane *plane,
u32 hw_videoport;
int ret;
- dev_dbg(ddev->dev, "%s\n", __func__);
-
if (!new_plane_state->crtc) {
/*
* The visible field is not reset by the DRM core but only
@@ -124,8 +122,6 @@ static void tidss_plane_atomic_update(struct drm_plane *plane,
plane);
u32 hw_videoport;
- dev_dbg(ddev->dev, "%s\n", __func__);
-
if (!new_state->visible) {
dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false);
return;
@@ -143,8 +139,6 @@ static void tidss_plane_atomic_enable(struct drm_plane *plane,
struct tidss_device *tidss = to_tidss(ddev);
struct tidss_plane *tplane = to_tidss_plane(plane);
- dev_dbg(ddev->dev, "%s\n", __func__);
-
dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, true);
}
@@ -155,8 +149,6 @@ static void tidss_plane_atomic_disable(struct drm_plane *plane,
struct tidss_device *tidss = to_tidss(ddev);
struct tidss_plane *tplane = to_tidss_plane(plane);
- dev_dbg(ddev->dev, "%s\n", __func__);
-
dispc_plane_enable(tidss->dispc, tplane->hw_plane_id, false);
}
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index b5f60b2b2d0e..5718d9d83a49 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -676,14 +676,7 @@ static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc,
if (!crtc_state->active)
return 0;
- if (state->planes[0].ptr != crtc->primary ||
- state->planes[0].state == NULL ||
- state->planes[0].state->crtc != crtc) {
- dev_dbg(crtc->dev->dev, "CRTC primary plane must be present");
- return -EINVAL;
- }
-
- return 0;
+ return drm_atomic_helper_check_crtc_primary_plane(crtc_state);
}
static int tilcdc_crtc_enable_vblank(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_plane.c b/drivers/gpu/drm/tilcdc/tilcdc_plane.c
index cf77a8ce7398..aa72ca679598 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_plane.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_plane.c
@@ -42,8 +42,7 @@ static int tilcdc_plane_atomic_check(struct drm_plane *plane,
return -EINVAL;
}
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- new_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
/* we should have a crtc state if the plane is attached to a crtc */
if (WARN_ON(!crtc_state))
return 0;
diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig
index 7d9e85e932d7..f0e72d4b6a47 100644
--- a/drivers/gpu/drm/tiny/Kconfig
+++ b/drivers/gpu/drm/tiny/Kconfig
@@ -85,6 +85,7 @@ config DRM_PANEL_MIPI_DBI
config DRM_PIXPAPER
tristate "DRM support for PIXPAPER display panels"
depends on DRM && SPI
+ depends on MMU
select DRM_CLIENT_SELECTION
select DRM_GEM_SHMEM_HELPER
select DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c
index d2d5e9f1269f..222e4ae1abbd 100644
--- a/drivers/gpu/drm/tiny/bochs.c
+++ b/drivers/gpu/drm/tiny/bochs.c
@@ -21,7 +21,10 @@
#include <drm/drm_module.h>
#include <drm/drm_panic.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#include <video/vga.h>
@@ -526,6 +529,7 @@ static void bochs_crtc_helper_atomic_enable(struct drm_crtc *crtc,
struct bochs_device *bochs = to_bochs_device(crtc->dev);
bochs_hw_blank(bochs, false);
+ drm_crtc_vblank_on(crtc);
}
static void bochs_crtc_helper_atomic_disable(struct drm_crtc *crtc,
@@ -533,12 +537,14 @@ static void bochs_crtc_helper_atomic_disable(struct drm_crtc *crtc,
{
struct bochs_device *bochs = to_bochs_device(crtc->dev);
+ drm_crtc_vblank_off(crtc);
bochs_hw_blank(bochs, true);
}
static const struct drm_crtc_helper_funcs bochs_crtc_helper_funcs = {
.mode_set_nofb = bochs_crtc_helper_mode_set_nofb,
.atomic_check = bochs_crtc_helper_atomic_check,
+ .atomic_flush = drm_crtc_vblank_atomic_flush,
.atomic_enable = bochs_crtc_helper_atomic_enable,
.atomic_disable = bochs_crtc_helper_atomic_disable,
};
@@ -550,6 +556,7 @@ static const struct drm_crtc_funcs bochs_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
};
static const struct drm_encoder_funcs bochs_encoder_funcs = {
@@ -670,6 +677,10 @@ static int bochs_kms_init(struct bochs_device *bochs)
drm_connector_attach_edid_property(connector);
drm_connector_attach_encoder(connector, encoder);
+ ret = drm_vblank_init(dev, 1);
+ if (ret)
+ return ret;
+
drm_mode_config_reset(dev);
return 0;
diff --git a/drivers/gpu/drm/tiny/cirrus-qemu.c b/drivers/gpu/drm/tiny/cirrus-qemu.c
index 97a93adc5669..9ba0eab489bb 100644
--- a/drivers/gpu/drm/tiny/cirrus-qemu.c
+++ b/drivers/gpu/drm/tiny/cirrus-qemu.c
@@ -44,7 +44,10 @@
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_module.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#define DRIVER_NAME "cirrus-qemu"
#define DRIVER_DESC "qemu cirrus vga"
@@ -404,11 +407,15 @@ static void cirrus_crtc_helper_atomic_enable(struct drm_crtc *crtc,
#endif
drm_dev_exit(idx);
+
+ drm_crtc_vblank_on(crtc);
}
static const struct drm_crtc_helper_funcs cirrus_crtc_helper_funcs = {
.atomic_check = cirrus_crtc_helper_atomic_check,
+ .atomic_flush = drm_crtc_vblank_atomic_flush,
.atomic_enable = cirrus_crtc_helper_atomic_enable,
+ .atomic_disable = drm_crtc_vblank_atomic_disable,
};
static const struct drm_crtc_funcs cirrus_crtc_funcs = {
@@ -418,6 +425,7 @@ static const struct drm_crtc_funcs cirrus_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
};
static const struct drm_encoder_funcs cirrus_encoder_funcs = {
@@ -493,6 +501,10 @@ static int cirrus_pipe_init(struct cirrus_device *cirrus)
if (ret)
return ret;
+ ret = drm_vblank_init(dev, 1);
+ if (ret)
+ return ret;
+
return 0;
}
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index fb0004166f4a..d73dfebb4353 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -25,6 +25,7 @@
#include <drm/drm_ioctl.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c
index df263818f45f..9f26aaca0bfa 100644
--- a/drivers/gpu/drm/tiny/hx8357d.c
+++ b/drivers/gpu/drm/tiny/hx8357d.c
@@ -25,6 +25,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include <video/mipi_display.h>
#define HX8357D_SETOSC 0xb0
diff --git a/drivers/gpu/drm/tiny/ili9163.c b/drivers/gpu/drm/tiny/ili9163.c
index 62cadf5e033d..7c154c008344 100644
--- a/drivers/gpu/drm/tiny/ili9163.c
+++ b/drivers/gpu/drm/tiny/ili9163.c
@@ -15,6 +15,7 @@
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include <video/mipi_display.h>
diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c
index 6de44ff69b51..d32538b1eb09 100644
--- a/drivers/gpu/drm/tiny/ili9225.c
+++ b/drivers/gpu/drm/tiny/ili9225.c
@@ -29,6 +29,7 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
+#include <drm/drm_print.h>
#include <drm/drm_rect.h>
#define ILI9225_DRIVER_READ_CODE 0x00
diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c
index e55029433509..2ab750cba505 100644
--- a/drivers/gpu/drm/tiny/ili9341.c
+++ b/drivers/gpu/drm/tiny/ili9341.c
@@ -24,6 +24,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include <video/mipi_display.h>
#define ILI9341_FRMCTR1 0xb1
diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c
index 093661c771a0..1e411a0f4567 100644
--- a/drivers/gpu/drm/tiny/ili9486.c
+++ b/drivers/gpu/drm/tiny/ili9486.c
@@ -23,6 +23,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#define ILI9486_ITFCTR1 0xb0
#define ILI9486_PWCTRL1 0xc2
diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c
index b6b4664908ae..a063eff77624 100644
--- a/drivers/gpu/drm/tiny/mi0283qt.c
+++ b/drivers/gpu/drm/tiny/mi0283qt.c
@@ -22,6 +22,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include <video/mipi_display.h>
#define ILI9341_FRMCTR1 0xb1
diff --git a/drivers/gpu/drm/tiny/panel-mipi-dbi.c b/drivers/gpu/drm/tiny/panel-mipi-dbi.c
index 23914a9f7fd3..82dfa169f762 100644
--- a/drivers/gpu/drm/tiny/panel-mipi-dbi.c
+++ b/drivers/gpu/drm/tiny/panel-mipi-dbi.c
@@ -25,6 +25,7 @@
#include <drm/drm_mipi_dbi.h>
#include <drm/drm_modes.h>
#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
#include <video/mipi_display.h>
diff --git a/drivers/gpu/drm/tiny/pixpaper.c b/drivers/gpu/drm/tiny/pixpaper.c
index 32598fb2fee7..df3ec42edd57 100644
--- a/drivers/gpu/drm/tiny/pixpaper.c
+++ b/drivers/gpu/drm/tiny/pixpaper.c
@@ -17,6 +17,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
/*
diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c
index 4824f863fdba..c8270591afc7 100644
--- a/drivers/gpu/drm/tiny/repaper.c
+++ b/drivers/gpu/drm/tiny/repaper.c
@@ -36,6 +36,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_modes.h>
#include <drm/drm_rect.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_test.c
index 6c77550c51af..d468f8322072 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_bo_test.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_bo_test.c
@@ -251,7 +251,7 @@ static void ttm_bo_unreserve_basic(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -290,7 +290,7 @@ static void ttm_bo_unreserve_pinned(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -342,7 +342,7 @@ static void ttm_bo_unreserve_bulk(struct kunit *test)
resv = kunit_kzalloc(test, sizeof(*resv), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, resv);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -379,7 +379,7 @@ static void ttm_bo_unreserve_bulk(struct kunit *test)
dma_resv_fini(resv);
}
-static void ttm_bo_put_basic(struct kunit *test)
+static void ttm_bo_fini_basic(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_buffer_object *bo;
@@ -394,7 +394,7 @@ static void ttm_bo_put_basic(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -410,7 +410,7 @@ static void ttm_bo_put_basic(struct kunit *test)
dma_resv_unlock(bo->base.resv);
KUNIT_EXPECT_EQ(test, err, 0);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static const char *mock_name(struct dma_fence *f)
@@ -423,7 +423,7 @@ static const struct dma_fence_ops mock_fence_ops = {
.get_timeline_name = mock_name,
};
-static void ttm_bo_put_shared_resv(struct kunit *test)
+static void ttm_bo_fini_shared_resv(struct kunit *test)
{
struct ttm_test_devices *priv = test->priv;
struct ttm_buffer_object *bo;
@@ -437,7 +437,7 @@ static void ttm_bo_put_shared_resv(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -463,7 +463,7 @@ static void ttm_bo_put_shared_resv(struct kunit *test)
bo->type = ttm_bo_type_device;
bo->base.resv = external_resv;
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static void ttm_bo_pin_basic(struct kunit *test)
@@ -477,7 +477,7 @@ static void ttm_bo_pin_basic(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -512,7 +512,7 @@ static void ttm_bo_pin_unpin_resource(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -563,7 +563,7 @@ static void ttm_bo_multiple_pin_one_unpin(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
priv->ttm_dev = ttm_dev;
@@ -616,8 +616,8 @@ static struct kunit_case ttm_bo_test_cases[] = {
KUNIT_CASE(ttm_bo_unreserve_basic),
KUNIT_CASE(ttm_bo_unreserve_pinned),
KUNIT_CASE(ttm_bo_unreserve_bulk),
- KUNIT_CASE(ttm_bo_put_basic),
- KUNIT_CASE(ttm_bo_put_shared_resv),
+ KUNIT_CASE(ttm_bo_fini_basic),
+ KUNIT_CASE(ttm_bo_fini_shared_resv),
KUNIT_CASE(ttm_bo_pin_basic),
KUNIT_CASE(ttm_bo_pin_unpin_resource),
KUNIT_CASE(ttm_bo_multiple_pin_one_unpin),
diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
index 1bcc67977f48..2eda87882e65 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c
@@ -144,7 +144,7 @@ static void ttm_bo_init_reserved_sys_man(struct kunit *test)
drm_mm_node_allocated(&bo->base.vma_node.vm_node));
ttm_resource_free(bo, &bo->resource);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static void ttm_bo_init_reserved_mock_man(struct kunit *test)
@@ -186,7 +186,7 @@ static void ttm_bo_init_reserved_mock_man(struct kunit *test)
drm_mm_node_allocated(&bo->base.vma_node.vm_node));
ttm_resource_free(bo, &bo->resource);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
}
@@ -221,7 +221,7 @@ static void ttm_bo_init_reserved_resv(struct kunit *test)
KUNIT_EXPECT_PTR_EQ(test, bo->base.resv, &resv);
ttm_resource_free(bo, &bo->resource);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static void ttm_bo_validate_basic(struct kunit *test)
@@ -265,7 +265,7 @@ static void ttm_bo_validate_basic(struct kunit *test)
KUNIT_EXPECT_EQ(test, bo->resource->placement,
DRM_BUDDY_TOPDOWN_ALLOCATION);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
ttm_mock_manager_fini(priv->ttm_dev, snd_mem);
}
@@ -292,7 +292,7 @@ static void ttm_bo_validate_invalid_placement(struct kunit *test)
KUNIT_EXPECT_EQ(test, err, -ENOMEM);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static void ttm_bo_validate_failed_alloc(struct kunit *test)
@@ -321,7 +321,7 @@ static void ttm_bo_validate_failed_alloc(struct kunit *test)
KUNIT_EXPECT_EQ(test, err, -ENOMEM);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
ttm_bad_manager_fini(priv->ttm_dev, mem_type);
}
@@ -353,7 +353,7 @@ static void ttm_bo_validate_pinned(struct kunit *test)
ttm_bo_unpin(bo);
dma_resv_unlock(bo->base.resv);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static const struct ttm_bo_validate_test_case ttm_mem_type_cases[] = {
@@ -403,7 +403,7 @@ static void ttm_bo_validate_same_placement(struct kunit *test)
KUNIT_EXPECT_EQ(test, err, 0);
KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, 0);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
if (params->mem_type != TTM_PL_SYSTEM)
ttm_mock_manager_fini(priv->ttm_dev, params->mem_type);
@@ -452,7 +452,7 @@ static void ttm_bo_validate_busy_placement(struct kunit *test)
KUNIT_EXPECT_EQ(test, bo->resource->mem_type, snd_mem);
KUNIT_ASSERT_TRUE(test, list_is_singular(&man->lru[bo->priority]));
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
ttm_bad_manager_fini(priv->ttm_dev, fst_mem);
ttm_mock_manager_fini(priv->ttm_dev, snd_mem);
}
@@ -495,7 +495,7 @@ static void ttm_bo_validate_multihop(struct kunit *test)
KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, size * 2);
KUNIT_EXPECT_EQ(test, bo->resource->mem_type, final_mem);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
ttm_mock_manager_fini(priv->ttm_dev, fst_mem);
ttm_mock_manager_fini(priv->ttm_dev, tmp_mem);
@@ -567,7 +567,7 @@ static void ttm_bo_validate_no_placement_signaled(struct kunit *test)
KUNIT_ASSERT_TRUE(test, flags & TTM_TT_FLAG_ZERO_ALLOC);
}
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static int threaded_dma_resv_signal(void *arg)
@@ -635,7 +635,7 @@ static void ttm_bo_validate_no_placement_not_signaled(struct kunit *test)
/* Make sure we have an idle object at this point */
dma_resv_wait_timeout(bo->base.resv, usage, false, MAX_SCHEDULE_TIMEOUT);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static void ttm_bo_validate_move_fence_signaled(struct kunit *test)
@@ -652,7 +652,7 @@ static void ttm_bo_validate_move_fence_signaled(struct kunit *test)
int err;
man = ttm_manager_type(priv->ttm_dev, mem_type);
- man->move = dma_fence_get_stub();
+ man->eviction_fences[0] = dma_fence_get_stub();
bo = ttm_bo_kunit_init(test, test->priv, size, NULL);
bo->type = bo_type;
@@ -668,8 +668,8 @@ static void ttm_bo_validate_move_fence_signaled(struct kunit *test)
KUNIT_EXPECT_EQ(test, bo->resource->mem_type, mem_type);
KUNIT_EXPECT_EQ(test, ctx.bytes_moved, size);
- ttm_bo_put(bo);
- dma_fence_put(man->move);
+ ttm_bo_fini(bo);
+ dma_fence_put(man->eviction_fences[0]);
}
static const struct ttm_bo_validate_test_case ttm_bo_validate_wait_cases[] = {
@@ -733,9 +733,9 @@ static void ttm_bo_validate_move_fence_not_signaled(struct kunit *test)
spin_lock_init(&fence_lock);
man = ttm_manager_type(priv->ttm_dev, fst_mem);
- man->move = alloc_mock_fence(test);
+ man->eviction_fences[0] = alloc_mock_fence(test);
- task = kthread_create(threaded_fence_signal, man->move, "move-fence-signal");
+ task = kthread_create(threaded_fence_signal, man->eviction_fences[0], "move-fence-signal");
if (IS_ERR(task))
KUNIT_FAIL(test, "Couldn't create move fence signal task\n");
@@ -743,7 +743,8 @@ static void ttm_bo_validate_move_fence_not_signaled(struct kunit *test)
err = ttm_bo_validate(bo, placement_val, &ctx_val);
dma_resv_unlock(bo->base.resv);
- dma_fence_wait_timeout(man->move, false, MAX_SCHEDULE_TIMEOUT);
+ dma_fence_wait_timeout(man->eviction_fences[0], false, MAX_SCHEDULE_TIMEOUT);
+ man->eviction_fences[0] = NULL;
KUNIT_EXPECT_EQ(test, err, 0);
KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, size);
@@ -753,7 +754,7 @@ static void ttm_bo_validate_move_fence_not_signaled(struct kunit *test)
else
KUNIT_EXPECT_EQ(test, bo->resource->mem_type, fst_mem);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
ttm_mock_manager_fini(priv->ttm_dev, fst_mem);
ttm_mock_manager_fini(priv->ttm_dev, snd_mem);
}
@@ -807,8 +808,8 @@ static void ttm_bo_validate_happy_evict(struct kunit *test)
KUNIT_EXPECT_EQ(test, bos[1].resource->mem_type, mem_type);
for (i = 0; i < bo_no; i++)
- ttm_bo_put(&bos[i]);
- ttm_bo_put(bo_val);
+ ttm_bo_fini(&bos[i]);
+ ttm_bo_fini(bo_val);
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
@@ -852,12 +853,12 @@ static void ttm_bo_validate_all_pinned_evict(struct kunit *test)
KUNIT_EXPECT_EQ(test, err, -ENOMEM);
- ttm_bo_put(bo_small);
+ ttm_bo_fini(bo_small);
ttm_bo_reserve(bo_big, false, false, NULL);
ttm_bo_unpin(bo_big);
dma_resv_unlock(bo_big->base.resv);
- ttm_bo_put(bo_big);
+ ttm_bo_fini(bo_big);
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
@@ -916,13 +917,13 @@ static void ttm_bo_validate_allowed_only_evict(struct kunit *test)
KUNIT_EXPECT_EQ(test, bo_evictable->resource->mem_type, mem_type_evict);
KUNIT_EXPECT_EQ(test, ctx_val.bytes_moved, size * 2 + BO_SIZE);
- ttm_bo_put(bo);
- ttm_bo_put(bo_evictable);
+ ttm_bo_fini(bo);
+ ttm_bo_fini(bo_evictable);
ttm_bo_reserve(bo_pinned, false, false, NULL);
ttm_bo_unpin(bo_pinned);
dma_resv_unlock(bo_pinned->base.resv);
- ttm_bo_put(bo_pinned);
+ ttm_bo_fini(bo_pinned);
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
ttm_mock_manager_fini(priv->ttm_dev, mem_multihop);
@@ -973,8 +974,8 @@ static void ttm_bo_validate_deleted_evict(struct kunit *test)
KUNIT_EXPECT_NULL(test, bo_big->ttm);
KUNIT_EXPECT_NULL(test, bo_big->resource);
- ttm_bo_put(bo_small);
- ttm_bo_put(bo_big);
+ ttm_bo_fini(bo_small);
+ ttm_bo_fini(bo_big);
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
}
@@ -995,7 +996,7 @@ static void ttm_bo_validate_busy_domain_evict(struct kunit *test)
*/
ttm_device_fini(priv->ttm_dev);
- err = ttm_device_kunit_init_bad_evict(test->priv, priv->ttm_dev, false, false);
+ err = ttm_device_kunit_init_bad_evict(test->priv, priv->ttm_dev);
KUNIT_ASSERT_EQ(test, err, 0);
ttm_mock_manager_init(priv->ttm_dev, mem_type, MANAGER_SIZE);
@@ -1025,8 +1026,8 @@ static void ttm_bo_validate_busy_domain_evict(struct kunit *test)
KUNIT_EXPECT_EQ(test, bo_init->resource->mem_type, mem_type);
KUNIT_EXPECT_NULL(test, bo_val->resource);
- ttm_bo_put(bo_init);
- ttm_bo_put(bo_val);
+ ttm_bo_fini(bo_init);
+ ttm_bo_fini(bo_val);
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
ttm_bad_manager_fini(priv->ttm_dev, mem_type_evict);
@@ -1070,8 +1071,8 @@ static void ttm_bo_validate_evict_gutting(struct kunit *test)
KUNIT_ASSERT_NULL(test, bo_evict->resource);
KUNIT_ASSERT_TRUE(test, bo_evict->ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC);
- ttm_bo_put(bo_evict);
- ttm_bo_put(bo);
+ ttm_bo_fini(bo_evict);
+ ttm_bo_fini(bo);
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
}
@@ -1128,9 +1129,9 @@ static void ttm_bo_validate_recrusive_evict(struct kunit *test)
ttm_mock_manager_fini(priv->ttm_dev, mem_type);
ttm_mock_manager_fini(priv->ttm_dev, mem_type_evict);
- ttm_bo_put(bo_val);
- ttm_bo_put(bo_tt);
- ttm_bo_put(bo_mock);
+ ttm_bo_fini(bo_val);
+ ttm_bo_fini(bo_tt);
+ ttm_bo_fini(bo_mock);
}
static struct kunit_case ttm_bo_validate_test_cases[] = {
diff --git a/drivers/gpu/drm/ttm/tests/ttm_device_test.c b/drivers/gpu/drm/ttm/tests/ttm_device_test.c
index 1621903818e5..2d55ad34fe48 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_device_test.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_device_test.c
@@ -7,11 +7,11 @@
#include <drm/ttm/ttm_placement.h>
#include "ttm_kunit_helpers.h"
+#include "../ttm_pool_internal.h"
struct ttm_device_test_case {
const char *description;
- bool use_dma_alloc;
- bool use_dma32;
+ unsigned int alloc_flags;
bool pools_init_expected;
};
@@ -25,7 +25,7 @@ static void ttm_device_init_basic(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_EXPECT_PTR_EQ(test, ttm_dev->funcs, &ttm_dev_funcs);
@@ -55,7 +55,7 @@ static void ttm_device_init_multiple(struct kunit *test)
KUNIT_ASSERT_NOT_NULL(test, ttm_devs);
for (i = 0; i < num_dev; i++) {
- err = ttm_device_kunit_init(priv, &ttm_devs[i], false, false);
+ err = ttm_device_kunit_init(priv, &ttm_devs[i], 0);
KUNIT_ASSERT_EQ(test, err, 0);
KUNIT_EXPECT_PTR_EQ(test, ttm_devs[i].dev_mapping,
@@ -81,7 +81,7 @@ static void ttm_device_fini_basic(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
man = ttm_manager_type(ttm_dev, TTM_PL_SYSTEM);
@@ -109,7 +109,7 @@ static void ttm_device_init_no_vma_man(struct kunit *test)
vma_man = drm->vma_offset_manager;
drm->vma_offset_manager = NULL;
- err = ttm_device_kunit_init(priv, ttm_dev, false, false);
+ err = ttm_device_kunit_init(priv, ttm_dev, 0);
KUNIT_EXPECT_EQ(test, err, -EINVAL);
/* Bring the manager back for a graceful cleanup */
@@ -119,26 +119,22 @@ static void ttm_device_init_no_vma_man(struct kunit *test)
static const struct ttm_device_test_case ttm_device_cases[] = {
{
.description = "No DMA allocations, no DMA32 required",
- .use_dma_alloc = false,
- .use_dma32 = false,
.pools_init_expected = false,
},
{
.description = "DMA allocations, DMA32 required",
- .use_dma_alloc = true,
- .use_dma32 = true,
+ .alloc_flags = TTM_ALLOCATION_POOL_USE_DMA_ALLOC |
+ TTM_ALLOCATION_POOL_USE_DMA32,
.pools_init_expected = true,
},
{
.description = "No DMA allocations, DMA32 required",
- .use_dma_alloc = false,
- .use_dma32 = true,
+ .alloc_flags = TTM_ALLOCATION_POOL_USE_DMA32,
.pools_init_expected = false,
},
{
.description = "DMA allocations, no DMA32 required",
- .use_dma_alloc = true,
- .use_dma32 = false,
+ .alloc_flags = TTM_ALLOCATION_POOL_USE_DMA_ALLOC,
.pools_init_expected = true,
},
};
@@ -162,16 +158,13 @@ static void ttm_device_init_pools(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(priv, ttm_dev,
- params->use_dma_alloc,
- params->use_dma32);
+ err = ttm_device_kunit_init(priv, ttm_dev, params->alloc_flags);
KUNIT_ASSERT_EQ(test, err, 0);
pool = &ttm_dev->pool;
KUNIT_ASSERT_NOT_NULL(test, pool);
KUNIT_EXPECT_PTR_EQ(test, pool->dev, priv->dev);
- KUNIT_EXPECT_EQ(test, pool->use_dma_alloc, params->use_dma_alloc);
- KUNIT_EXPECT_EQ(test, pool->use_dma32, params->use_dma32);
+ KUNIT_EXPECT_EQ(test, pool->alloc_flags, params->alloc_flags);
if (params->pools_init_expected) {
for (int i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
@@ -181,7 +174,7 @@ static void ttm_device_init_pools(struct kunit *test)
KUNIT_EXPECT_EQ(test, pt.caching, i);
KUNIT_EXPECT_EQ(test, pt.order, j);
- if (params->use_dma_alloc)
+ if (ttm_pool_uses_dma_alloc(pool))
KUNIT_ASSERT_FALSE(test,
list_empty(&pt.pages));
}
diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
index 7aaf0d1395ff..7b533e4e1e04 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.c
@@ -117,8 +117,7 @@ static void bad_evict_flags(struct ttm_buffer_object *bo,
static int ttm_device_kunit_init_with_funcs(struct ttm_test_devices *priv,
struct ttm_device *ttm,
- bool use_dma_alloc,
- bool use_dma32,
+ unsigned int alloc_flags,
struct ttm_device_funcs *funcs)
{
struct drm_device *drm = priv->drm;
@@ -127,7 +126,7 @@ static int ttm_device_kunit_init_with_funcs(struct ttm_test_devices *priv,
err = ttm_device_init(ttm, funcs, drm->dev,
drm->anon_inode->i_mapping,
drm->vma_offset_manager,
- use_dma_alloc, use_dma32);
+ alloc_flags);
return err;
}
@@ -143,11 +142,10 @@ EXPORT_SYMBOL_GPL(ttm_dev_funcs);
int ttm_device_kunit_init(struct ttm_test_devices *priv,
struct ttm_device *ttm,
- bool use_dma_alloc,
- bool use_dma32)
+ unsigned int alloc_flags)
{
- return ttm_device_kunit_init_with_funcs(priv, ttm, use_dma_alloc,
- use_dma32, &ttm_dev_funcs);
+ return ttm_device_kunit_init_with_funcs(priv, ttm, alloc_flags,
+ &ttm_dev_funcs);
}
EXPORT_SYMBOL_GPL(ttm_device_kunit_init);
@@ -161,12 +159,10 @@ struct ttm_device_funcs ttm_dev_funcs_bad_evict = {
EXPORT_SYMBOL_GPL(ttm_dev_funcs_bad_evict);
int ttm_device_kunit_init_bad_evict(struct ttm_test_devices *priv,
- struct ttm_device *ttm,
- bool use_dma_alloc,
- bool use_dma32)
+ struct ttm_device *ttm)
{
- return ttm_device_kunit_init_with_funcs(priv, ttm, use_dma_alloc,
- use_dma32, &ttm_dev_funcs_bad_evict);
+ return ttm_device_kunit_init_with_funcs(priv, ttm, 0,
+ &ttm_dev_funcs_bad_evict);
}
EXPORT_SYMBOL_GPL(ttm_device_kunit_init_bad_evict);
@@ -252,7 +248,7 @@ struct ttm_test_devices *ttm_test_devices_all(struct kunit *test)
ttm_dev = kunit_kzalloc(test, sizeof(*ttm_dev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ttm_dev);
- err = ttm_device_kunit_init(devs, ttm_dev, false, false);
+ err = ttm_device_kunit_init(devs, ttm_dev, 0);
KUNIT_ASSERT_EQ(test, err, 0);
devs->ttm_dev = ttm_dev;
diff --git a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.h b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.h
index c7da23232ffa..f8402b979d05 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.h
+++ b/drivers/gpu/drm/ttm/tests/ttm_kunit_helpers.h
@@ -28,12 +28,9 @@ struct ttm_test_devices {
/* Building blocks for test-specific init functions */
int ttm_device_kunit_init(struct ttm_test_devices *priv,
struct ttm_device *ttm,
- bool use_dma_alloc,
- bool use_dma32);
+ unsigned int alloc_flags);
int ttm_device_kunit_init_bad_evict(struct ttm_test_devices *priv,
- struct ttm_device *ttm,
- bool use_dma_alloc,
- bool use_dma32);
+ struct ttm_device *ttm);
struct ttm_buffer_object *ttm_bo_kunit_init(struct kunit *test,
struct ttm_test_devices *devs,
size_t size,
diff --git a/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c b/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c
index d7eb6471f2ed..dd395229e388 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_mock_manager.c
@@ -4,6 +4,7 @@
*/
#include <linux/export.h>
+#include <linux/module.h>
#include <drm/ttm/ttm_resource.h>
#include <drm/ttm/ttm_device.h>
diff --git a/drivers/gpu/drm/ttm/tests/ttm_pool_test.c b/drivers/gpu/drm/ttm/tests/ttm_pool_test.c
index 8ade53371f72..11c92bd75779 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_pool_test.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_pool_test.c
@@ -8,11 +8,12 @@
#include <drm/ttm/ttm_pool.h>
#include "ttm_kunit_helpers.h"
+#include "../ttm_pool_internal.h"
struct ttm_pool_test_case {
const char *description;
unsigned int order;
- bool use_dma_alloc;
+ unsigned int alloc_flags;
};
struct ttm_pool_test_priv {
@@ -86,7 +87,7 @@ static struct ttm_pool *ttm_pool_pre_populated(struct kunit *test,
pool = kunit_kzalloc(test, sizeof(*pool), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, pool);
- ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, true, false);
+ ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, TTM_ALLOCATION_POOL_USE_DMA_ALLOC);
err = ttm_pool_alloc(pool, tt, &simple_ctx);
KUNIT_ASSERT_EQ(test, err, 0);
@@ -113,12 +114,12 @@ static const struct ttm_pool_test_case ttm_pool_basic_cases[] = {
{
.description = "One page, with coherent DMA mappings enabled",
.order = 0,
- .use_dma_alloc = true,
+ .alloc_flags = TTM_ALLOCATION_POOL_USE_DMA_ALLOC,
},
{
.description = "Above the allocation limit, with coherent DMA mappings enabled",
.order = MAX_PAGE_ORDER + 1,
- .use_dma_alloc = true,
+ .alloc_flags = TTM_ALLOCATION_POOL_USE_DMA_ALLOC,
},
};
@@ -150,12 +151,11 @@ static void ttm_pool_alloc_basic(struct kunit *test)
pool = kunit_kzalloc(test, sizeof(*pool), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, pool);
- ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, params->use_dma_alloc,
- false);
+ ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, params->alloc_flags);
KUNIT_ASSERT_PTR_EQ(test, pool->dev, devs->dev);
KUNIT_ASSERT_EQ(test, pool->nid, NUMA_NO_NODE);
- KUNIT_ASSERT_EQ(test, pool->use_dma_alloc, params->use_dma_alloc);
+ KUNIT_ASSERT_EQ(test, pool->alloc_flags, params->alloc_flags);
err = ttm_pool_alloc(pool, tt, &simple_ctx);
KUNIT_ASSERT_EQ(test, err, 0);
@@ -165,14 +165,14 @@ static void ttm_pool_alloc_basic(struct kunit *test)
last_page = tt->pages[tt->num_pages - 1];
if (params->order <= MAX_PAGE_ORDER) {
- if (params->use_dma_alloc) {
+ if (ttm_pool_uses_dma_alloc(pool)) {
KUNIT_ASSERT_NOT_NULL(test, (void *)fst_page->private);
KUNIT_ASSERT_NOT_NULL(test, (void *)last_page->private);
} else {
KUNIT_ASSERT_EQ(test, fst_page->private, params->order);
}
} else {
- if (params->use_dma_alloc) {
+ if (ttm_pool_uses_dma_alloc(pool)) {
KUNIT_ASSERT_NOT_NULL(test, (void *)fst_page->private);
KUNIT_ASSERT_NULL(test, (void *)last_page->private);
} else {
@@ -218,7 +218,7 @@ static void ttm_pool_alloc_basic_dma_addr(struct kunit *test)
pool = kunit_kzalloc(test, sizeof(*pool), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, pool);
- ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, true, false);
+ ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, TTM_ALLOCATION_POOL_USE_DMA_ALLOC);
err = ttm_pool_alloc(pool, tt, &simple_ctx);
KUNIT_ASSERT_EQ(test, err, 0);
@@ -348,7 +348,7 @@ static void ttm_pool_free_dma_alloc(struct kunit *test)
pool = kunit_kzalloc(test, sizeof(*pool), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, pool);
- ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, true, false);
+ ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, TTM_ALLOCATION_POOL_USE_DMA_ALLOC);
ttm_pool_alloc(pool, tt, &simple_ctx);
pt = &pool->caching[caching].orders[order];
@@ -379,7 +379,7 @@ static void ttm_pool_free_no_dma_alloc(struct kunit *test)
pool = kunit_kzalloc(test, sizeof(*pool), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, pool);
- ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, false, false);
+ ttm_pool_init(pool, devs->dev, NUMA_NO_NODE, 0);
ttm_pool_alloc(pool, tt, &simple_ctx);
pt = &pool->caching[caching].orders[order];
diff --git a/drivers/gpu/drm/ttm/tests/ttm_resource_test.c b/drivers/gpu/drm/ttm/tests/ttm_resource_test.c
index e6ea2bd01f07..c0e4e35e0442 100644
--- a/drivers/gpu/drm/ttm/tests/ttm_resource_test.c
+++ b/drivers/gpu/drm/ttm/tests/ttm_resource_test.c
@@ -207,6 +207,7 @@ static void ttm_resource_manager_init_basic(struct kunit *test)
struct ttm_resource_test_priv *priv = test->priv;
struct ttm_resource_manager *man;
size_t size = SZ_16K;
+ int i;
man = kunit_kzalloc(test, sizeof(*man), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, man);
@@ -216,8 +217,8 @@ static void ttm_resource_manager_init_basic(struct kunit *test)
KUNIT_ASSERT_PTR_EQ(test, man->bdev, priv->devs->ttm_dev);
KUNIT_ASSERT_EQ(test, man->size, size);
KUNIT_ASSERT_EQ(test, man->usage, 0);
- KUNIT_ASSERT_NULL(test, man->move);
- KUNIT_ASSERT_NOT_NULL(test, &man->move_lock);
+ for (i = 0; i < TTM_NUM_MOVE_FENCES; i++)
+ KUNIT_ASSERT_NULL(test, man->eviction_fences[i]);
for (int i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
KUNIT_ASSERT_TRUE(test, list_empty(&man->lru[i]));
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 29423ceeec5c..bd27607f8076 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -31,6 +31,8 @@
#define pr_fmt(fmt) "[TTM] " fmt
+#include <drm/drm_print.h>
+#include <drm/ttm/ttm_allocation.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_placement.h>
#include <drm/ttm/ttm_tt.h>
@@ -318,18 +320,17 @@ static void ttm_bo_release(struct kref *kref)
bo->destroy(bo);
}
-/**
- * ttm_bo_put
- *
- * @bo: The buffer object.
- *
- * Unreference a buffer object.
- */
+/* TODO: remove! */
void ttm_bo_put(struct ttm_buffer_object *bo)
{
kref_put(&bo->kref, ttm_bo_release);
}
-EXPORT_SYMBOL(ttm_bo_put);
+
+void ttm_bo_fini(struct ttm_buffer_object *bo)
+{
+ ttm_bo_put(bo);
+}
+EXPORT_SYMBOL(ttm_bo_fini);
static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo,
struct ttm_operation_ctx *ctx,
@@ -658,34 +659,35 @@ void ttm_bo_unpin(struct ttm_buffer_object *bo)
EXPORT_SYMBOL(ttm_bo_unpin);
/*
- * Add the last move fence to the BO as kernel dependency and reserve a new
- * fence slot.
+ * Add the pipelined eviction fencesto the BO as kernel dependency and reserve new
+ * fence slots.
*/
-static int ttm_bo_add_move_fence(struct ttm_buffer_object *bo,
- struct ttm_resource_manager *man,
- bool no_wait_gpu)
+static int ttm_bo_add_pipelined_eviction_fences(struct ttm_buffer_object *bo,
+ struct ttm_resource_manager *man,
+ bool no_wait_gpu)
{
struct dma_fence *fence;
- int ret;
+ int i;
- spin_lock(&man->move_lock);
- fence = dma_fence_get(man->move);
- spin_unlock(&man->move_lock);
-
- if (!fence)
- return 0;
+ spin_lock(&man->eviction_lock);
+ for (i = 0; i < TTM_NUM_MOVE_FENCES; i++) {
+ fence = man->eviction_fences[i];
+ if (!fence)
+ continue;
- if (no_wait_gpu) {
- ret = dma_fence_is_signaled(fence) ? 0 : -EBUSY;
- dma_fence_put(fence);
- return ret;
+ if (no_wait_gpu) {
+ if (!dma_fence_is_signaled(fence)) {
+ spin_unlock(&man->eviction_lock);
+ return -EBUSY;
+ }
+ } else {
+ dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL);
+ }
}
+ spin_unlock(&man->eviction_lock);
- dma_resv_add_fence(bo->base.resv, fence, DMA_RESV_USAGE_KERNEL);
-
- ret = dma_resv_reserve_fences(bo->base.resv, 1);
- dma_fence_put(fence);
- return ret;
+ /* TODO: this call should be removed. */
+ return dma_resv_reserve_fences(bo->base.resv, 1);
}
/**
@@ -718,7 +720,7 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
int i, ret;
ticket = dma_resv_locking_ctx(bo->base.resv);
- ret = dma_resv_reserve_fences(bo->base.resv, 1);
+ ret = dma_resv_reserve_fences(bo->base.resv, TTM_NUM_MOVE_FENCES);
if (unlikely(ret))
return ret;
@@ -757,7 +759,7 @@ static int ttm_bo_alloc_resource(struct ttm_buffer_object *bo,
return ret;
}
- ret = ttm_bo_add_move_fence(bo, man, ctx->no_wait_gpu);
+ ret = ttm_bo_add_pipelined_eviction_fences(bo, man, ctx->no_wait_gpu);
if (unlikely(ret)) {
ttm_resource_free(bo, res);
if (ret == -EBUSY)
@@ -878,7 +880,8 @@ bounce:
/* For backward compatibility with userspace */
if (ret == -ENOSPC)
- return -ENOMEM;
+ return bo->bdev->alloc_flags & TTM_ALLOCATION_PROPAGATE_ENOSPC ?
+ ret : -ENOMEM;
/*
* We might need to add a TTM.
diff --git a/drivers/gpu/drm/ttm/ttm_bo_internal.h b/drivers/gpu/drm/ttm/ttm_bo_internal.h
index 9d8b747a34db..e0d48eac74b0 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_internal.h
+++ b/drivers/gpu/drm/ttm/ttm_bo_internal.h
@@ -55,4 +55,6 @@ ttm_bo_get_unless_zero(struct ttm_buffer_object *bo)
return bo;
}
+void ttm_bo_put(struct ttm_buffer_object *bo);
+
#endif
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index acbbca9d5c92..2ff35d55e462 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -258,7 +258,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo,
ret = dma_resv_trylock(&fbo->base.base._resv);
WARN_ON(!ret);
- ret = dma_resv_reserve_fences(&fbo->base.base._resv, 1);
+ ret = dma_resv_reserve_fences(&fbo->base.base._resv, TTM_NUM_MOVE_FENCES);
if (ret) {
dma_resv_unlock(&fbo->base.base._resv);
kfree(fbo);
@@ -646,20 +646,44 @@ static void ttm_bo_move_pipeline_evict(struct ttm_buffer_object *bo,
{
struct ttm_device *bdev = bo->bdev;
struct ttm_resource_manager *from;
+ struct dma_fence *tmp;
+ int i;
from = ttm_manager_type(bdev, bo->resource->mem_type);
/**
* BO doesn't have a TTM we need to bind/unbind. Just remember
- * this eviction and free up the allocation
+ * this eviction and free up the allocation.
+ * The fence will be saved in the first free slot or in the slot
+ * already used to store a fence from the same context. Since
+ * drivers can't use more than TTM_NUM_MOVE_FENCES contexts for
+ * evictions we should always find a slot to use.
*/
- spin_lock(&from->move_lock);
- if (!from->move || dma_fence_is_later(fence, from->move)) {
- dma_fence_put(from->move);
- from->move = dma_fence_get(fence);
+ spin_lock(&from->eviction_lock);
+ for (i = 0; i < TTM_NUM_MOVE_FENCES; i++) {
+ tmp = from->eviction_fences[i];
+ if (!tmp)
+ break;
+ if (fence->context != tmp->context)
+ continue;
+ if (dma_fence_is_later(fence, tmp)) {
+ dma_fence_put(tmp);
+ break;
+ }
+ goto unlock;
+ }
+ if (i < TTM_NUM_MOVE_FENCES) {
+ from->eviction_fences[i] = dma_fence_get(fence);
+ } else {
+ WARN(1, "not enough fence slots for all fence contexts");
+ spin_unlock(&from->eviction_lock);
+ dma_fence_wait(fence, false);
+ goto end;
}
- spin_unlock(&from->move_lock);
+unlock:
+ spin_unlock(&from->eviction_lock);
+end:
ttm_resource_free(bo, &bo->resource);
}
diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c
index c3e2fcbdd2cc..9a51afaf0749 100644
--- a/drivers/gpu/drm/ttm/ttm_device.c
+++ b/drivers/gpu/drm/ttm/ttm_device.c
@@ -31,6 +31,7 @@
#include <linux/export.h>
#include <linux/mm.h>
+#include <drm/ttm/ttm_allocation.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_device.h>
#include <drm/ttm/ttm_tt.h>
@@ -198,8 +199,7 @@ EXPORT_SYMBOL(ttm_device_swapout);
* @dev: The core kernel device pointer for DMA mappings and allocations.
* @mapping: The address space to use for this bo.
* @vma_manager: A pointer to a vma manager.
- * @use_dma_alloc: If coherent DMA allocation API should be used.
- * @use_dma32: If we should use GFP_DMA32 for device memory allocations.
+ * @alloc_flags: TTM_ALLOCATION_* flags.
*
* Initializes a struct ttm_device:
* Returns:
@@ -208,7 +208,7 @@ EXPORT_SYMBOL(ttm_device_swapout);
int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *funcs,
struct device *dev, struct address_space *mapping,
struct drm_vma_offset_manager *vma_manager,
- bool use_dma_alloc, bool use_dma32)
+ unsigned int alloc_flags)
{
struct ttm_global *glob = &ttm_glob;
int ret, nid;
@@ -227,6 +227,7 @@ int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *func
return -ENOMEM;
}
+ bdev->alloc_flags = alloc_flags;
bdev->funcs = funcs;
ttm_sys_man_init(bdev);
@@ -236,7 +237,7 @@ int ttm_device_init(struct ttm_device *bdev, const struct ttm_device_funcs *func
else
nid = NUMA_NO_NODE;
- ttm_pool_init(&bdev->pool, dev, nid, use_dma_alloc, use_dma32);
+ ttm_pool_init(&bdev->pool, dev, nid, alloc_flags);
bdev->vma_manager = vma_manager;
spin_lock_init(&bdev->lru_lock);
diff --git a/drivers/gpu/drm/ttm/ttm_module.c b/drivers/gpu/drm/ttm/ttm_module.c
index b3fffe7b5062..aa137ead5cc5 100644
--- a/drivers/gpu/drm/ttm/ttm_module.c
+++ b/drivers/gpu/drm/ttm/ttm_module.c
@@ -74,7 +74,8 @@ pgprot_t ttm_prot_from_caching(enum ttm_caching caching, pgprot_t tmp)
#endif /* CONFIG_UML */
#endif /* __i386__ || __x86_64__ */
#if defined(__ia64__) || defined(__arm__) || defined(__aarch64__) || \
- defined(__powerpc__) || defined(__mips__) || defined(__loongarch__)
+ defined(__powerpc__) || defined(__mips__) || defined(__loongarch__) || \
+ defined(__riscv)
if (caching == ttm_write_combined)
tmp = pgprot_writecombine(tmp);
else
diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c
index baf27c70a419..18b6db015619 100644
--- a/drivers/gpu/drm/ttm/ttm_pool.c
+++ b/drivers/gpu/drm/ttm/ttm_pool.c
@@ -48,6 +48,7 @@
#include <drm/ttm/ttm_bo.h>
#include "ttm_module.h"
+#include "ttm_pool_internal.h"
#ifdef CONFIG_FAULT_INJECTION
#include <linux/fault-inject.h>
@@ -135,6 +136,7 @@ static DECLARE_RWSEM(pool_shrink_rwsem);
static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags,
unsigned int order)
{
+ const unsigned int beneficial_order = ttm_pool_beneficial_order(pool);
unsigned long attr = DMA_ATTR_FORCE_CONTIGUOUS;
struct ttm_pool_dma *dma;
struct page *p;
@@ -148,7 +150,14 @@ static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags,
gfp_flags |= __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN |
__GFP_THISNODE;
- if (!pool->use_dma_alloc) {
+ /*
+ * Do not add latency to the allocation path for allocations orders
+ * device tolds us do not bring them additional performance gains.
+ */
+ if (beneficial_order && order > beneficial_order)
+ gfp_flags &= ~__GFP_DIRECT_RECLAIM;
+
+ if (!ttm_pool_uses_dma_alloc(pool)) {
p = alloc_pages_node(pool->nid, gfp_flags, order);
if (p)
p->private = order;
@@ -200,7 +209,7 @@ static void ttm_pool_free_page(struct ttm_pool *pool, enum ttm_caching caching,
set_pages_wb(p, 1 << order);
#endif
- if (!pool || !pool->use_dma_alloc) {
+ if (!pool || !ttm_pool_uses_dma_alloc(pool)) {
__free_pages(p, order);
return;
}
@@ -243,7 +252,7 @@ static int ttm_pool_map(struct ttm_pool *pool, unsigned int order,
{
dma_addr_t addr;
- if (pool->use_dma_alloc) {
+ if (ttm_pool_uses_dma_alloc(pool)) {
struct ttm_pool_dma *dma = (void *)p->private;
addr = dma->addr;
@@ -265,7 +274,7 @@ static void ttm_pool_unmap(struct ttm_pool *pool, dma_addr_t dma_addr,
unsigned int num_pages)
{
/* Unmapped while freeing the page */
- if (pool->use_dma_alloc)
+ if (ttm_pool_uses_dma_alloc(pool))
return;
dma_unmap_page(pool->dev, dma_addr, (long)num_pages << PAGE_SHIFT,
@@ -339,7 +348,7 @@ static struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool,
enum ttm_caching caching,
unsigned int order)
{
- if (pool->use_dma_alloc)
+ if (ttm_pool_uses_dma_alloc(pool))
return &pool->caching[caching].orders[order];
#ifdef CONFIG_X86
@@ -348,7 +357,7 @@ static struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool,
if (pool->nid != NUMA_NO_NODE)
return &pool->caching[caching].orders[order];
- if (pool->use_dma32)
+ if (ttm_pool_uses_dma32(pool))
return &global_dma32_write_combined[order];
return &global_write_combined[order];
@@ -356,7 +365,7 @@ static struct ttm_pool_type *ttm_pool_select_type(struct ttm_pool *pool,
if (pool->nid != NUMA_NO_NODE)
return &pool->caching[caching].orders[order];
- if (pool->use_dma32)
+ if (ttm_pool_uses_dma32(pool))
return &global_dma32_uncached[order];
return &global_uncached[order];
@@ -396,7 +405,7 @@ static unsigned int ttm_pool_shrink(void)
/* Return the allocation order based for a page */
static unsigned int ttm_pool_page_order(struct ttm_pool *pool, struct page *p)
{
- if (pool->use_dma_alloc) {
+ if (ttm_pool_uses_dma_alloc(pool)) {
struct ttm_pool_dma *dma = (void *)p->private;
return dma->vaddr & ~PAGE_MASK;
@@ -719,7 +728,7 @@ static int __ttm_pool_alloc(struct ttm_pool *pool, struct ttm_tt *tt,
if (ctx->gfp_retry_mayfail)
gfp_flags |= __GFP_RETRY_MAYFAIL;
- if (pool->use_dma32)
+ if (ttm_pool_uses_dma32(pool))
gfp_flags |= GFP_DMA32;
else
gfp_flags |= GFP_HIGHUSER;
@@ -977,7 +986,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
return -EINVAL;
if ((!ttm_backup_bytes_avail() && !flags->purge) ||
- pool->use_dma_alloc || ttm_tt_is_backed_up(tt))
+ ttm_pool_uses_dma_alloc(pool) || ttm_tt_is_backed_up(tt))
return -EBUSY;
#ifdef CONFIG_X86
@@ -1014,7 +1023,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
if (flags->purge)
return shrunken;
- if (pool->use_dma32)
+ if (ttm_pool_uses_dma32(pool))
gfp = GFP_DMA32;
else
gfp = GFP_HIGHUSER;
@@ -1058,22 +1067,20 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
* @pool: the pool to initialize
* @dev: device for DMA allocations and mappings
* @nid: NUMA node to use for allocations
- * @use_dma_alloc: true if coherent DMA alloc should be used
- * @use_dma32: true if GFP_DMA32 should be used
+ * @alloc_flags: TTM_ALLOCATION_POOL_* flags
*
* Initialize the pool and its pool types.
*/
void ttm_pool_init(struct ttm_pool *pool, struct device *dev,
- int nid, bool use_dma_alloc, bool use_dma32)
+ int nid, unsigned int alloc_flags)
{
unsigned int i, j;
- WARN_ON(!dev && use_dma_alloc);
+ WARN_ON(!dev && ttm_pool_uses_dma_alloc(pool));
pool->dev = dev;
pool->nid = nid;
- pool->use_dma_alloc = use_dma_alloc;
- pool->use_dma32 = use_dma32;
+ pool->alloc_flags = alloc_flags;
for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
for (j = 0; j < NR_PAGE_ORDERS; ++j) {
@@ -1239,7 +1246,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m)
{
unsigned int i;
- if (!pool->use_dma_alloc && pool->nid == NUMA_NO_NODE) {
+ if (!ttm_pool_uses_dma_alloc(pool) && pool->nid == NUMA_NO_NODE) {
seq_puts(m, "unused\n");
return 0;
}
@@ -1250,7 +1257,7 @@ int ttm_pool_debugfs(struct ttm_pool *pool, struct seq_file *m)
for (i = 0; i < TTM_NUM_CACHING_TYPES; ++i) {
if (!ttm_pool_select_type(pool, i, 0))
continue;
- if (pool->use_dma_alloc)
+ if (ttm_pool_uses_dma_alloc(pool))
seq_puts(m, "DMA ");
else
seq_printf(m, "N%d ", pool->nid);
diff --git a/drivers/gpu/drm/ttm/ttm_pool_internal.h b/drivers/gpu/drm/ttm/ttm_pool_internal.h
new file mode 100644
index 000000000000..82c4b7e56a99
--- /dev/null
+++ b/drivers/gpu/drm/ttm/ttm_pool_internal.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/* Copyright (c) 2025 Valve Corporation */
+
+#ifndef _TTM_POOL_INTERNAL_H_
+#define _TTM_POOL_INTERNAL_H_
+
+#include <drm/ttm/ttm_allocation.h>
+#include <drm/ttm/ttm_pool.h>
+
+static inline bool ttm_pool_uses_dma_alloc(struct ttm_pool *pool)
+{
+ return pool->alloc_flags & TTM_ALLOCATION_POOL_USE_DMA_ALLOC;
+}
+
+static inline bool ttm_pool_uses_dma32(struct ttm_pool *pool)
+{
+ return pool->alloc_flags & TTM_ALLOCATION_POOL_USE_DMA32;
+}
+
+static inline bool ttm_pool_beneficial_order(struct ttm_pool *pool)
+{
+ return pool->alloc_flags & 0xff;
+}
+
+#endif
diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c
index e2c82ad07eb4..f5aa29dc6ec0 100644
--- a/drivers/gpu/drm/ttm/ttm_resource.c
+++ b/drivers/gpu/drm/ttm/ttm_resource.c
@@ -34,6 +34,7 @@
#include <drm/ttm/ttm_resource.h>
#include <drm/ttm/ttm_tt.h>
+#include <drm/drm_print.h>
#include <drm/drm_util.h>
/* Detach the cursor from the bulk move list*/
@@ -523,14 +524,15 @@ void ttm_resource_manager_init(struct ttm_resource_manager *man,
{
unsigned i;
- spin_lock_init(&man->move_lock);
man->bdev = bdev;
man->size = size;
man->usage = 0;
for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i)
INIT_LIST_HEAD(&man->lru[i]);
- man->move = NULL;
+ spin_lock_init(&man->eviction_lock);
+ for (i = 0; i < TTM_NUM_MOVE_FENCES; i++)
+ man->eviction_fences[i] = NULL;
}
EXPORT_SYMBOL(ttm_resource_manager_init);
@@ -551,7 +553,7 @@ int ttm_resource_manager_evict_all(struct ttm_device *bdev,
.no_wait_gpu = false,
};
struct dma_fence *fence;
- int ret;
+ int ret, i;
do {
ret = ttm_bo_evict_first(bdev, man, &ctx);
@@ -561,18 +563,24 @@ int ttm_resource_manager_evict_all(struct ttm_device *bdev,
if (ret && ret != -ENOENT)
return ret;
- spin_lock(&man->move_lock);
- fence = dma_fence_get(man->move);
- spin_unlock(&man->move_lock);
-
- if (fence) {
- ret = dma_fence_wait(fence, false);
- dma_fence_put(fence);
- if (ret)
- return ret;
+ ret = 0;
+
+ spin_lock(&man->eviction_lock);
+ for (i = 0; i < TTM_NUM_MOVE_FENCES; i++) {
+ fence = man->eviction_fences[i];
+ if (fence && !dma_fence_is_signaled(fence)) {
+ dma_fence_get(fence);
+ spin_unlock(&man->eviction_lock);
+ ret = dma_fence_wait(fence, false);
+ dma_fence_put(fence);
+ if (ret)
+ return ret;
+ spin_lock(&man->eviction_lock);
+ }
}
+ spin_unlock(&man->eviction_lock);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(ttm_resource_manager_evict_all);
@@ -587,6 +595,9 @@ uint64_t ttm_resource_manager_usage(struct ttm_resource_manager *man)
{
uint64_t usage;
+ if (WARN_ON_ONCE(!man->bdev))
+ return 0;
+
spin_lock(&man->bdev->lru_lock);
usage = man->usage;
spin_unlock(&man->bdev->lru_lock);
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index 506e257dfba8..611d20ab966d 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -40,12 +40,14 @@
#include <linux/shmem_fs.h>
#include <drm/drm_cache.h>
#include <drm/drm_device.h>
+#include <drm/drm_print.h>
#include <drm/drm_util.h>
#include <drm/ttm/ttm_backup.h>
#include <drm/ttm/ttm_bo.h>
#include <drm/ttm/ttm_tt.h>
#include "ttm_module.h"
+#include "ttm_pool_internal.h"
static unsigned long ttm_pages_limit;
@@ -93,7 +95,8 @@ int ttm_tt_create(struct ttm_buffer_object *bo, bool zero_alloc)
* mapped TT pages need to be decrypted or otherwise the drivers
* will end up sending encrypted mem to the gpu.
*/
- if (bdev->pool.use_dma_alloc && cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
+ if (ttm_pool_uses_dma_alloc(&bdev->pool) &&
+ cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)) {
page_flags |= TTM_TT_FLAG_DECRYPTED;
drm_info_once(ddev, "TT memory decryption enabled.");
}
@@ -378,7 +381,7 @@ int ttm_tt_populate(struct ttm_device *bdev,
if (!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL)) {
atomic_long_add(ttm->num_pages, &ttm_pages_allocated);
- if (bdev->pool.use_dma32)
+ if (ttm_pool_uses_dma32(&bdev->pool))
atomic_long_add(ttm->num_pages,
&ttm_dma32_pages_allocated);
}
@@ -416,7 +419,7 @@ int ttm_tt_populate(struct ttm_device *bdev,
error:
if (!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL)) {
atomic_long_sub(ttm->num_pages, &ttm_pages_allocated);
- if (bdev->pool.use_dma32)
+ if (ttm_pool_uses_dma32(&bdev->pool))
atomic_long_sub(ttm->num_pages,
&ttm_dma32_pages_allocated);
}
@@ -439,7 +442,7 @@ void ttm_tt_unpopulate(struct ttm_device *bdev, struct ttm_tt *ttm)
if (!(ttm->page_flags & TTM_TT_FLAG_EXTERNAL)) {
atomic_long_sub(ttm->num_pages, &ttm_pages_allocated);
- if (bdev->pool.use_dma32)
+ if (ttm_pool_uses_dma32(&bdev->pool))
atomic_long_sub(ttm->num_pages,
&ttm_dma32_pages_allocated);
}
diff --git a/drivers/gpu/drm/tve200/tve200_display.c b/drivers/gpu/drm/tve200/tve200_display.c
index 37bdd976ae59..26b6c65ef6fd 100644
--- a/drivers/gpu/drm/tve200/tve200_display.c
+++ b/drivers/gpu/drm/tve200/tve200_display.c
@@ -21,6 +21,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include "tve200_drm.h"
diff --git a/drivers/gpu/drm/udl/udl_edid.c b/drivers/gpu/drm/udl/udl_edid.c
index 12f48ae17073..af4cff2a7c51 100644
--- a/drivers/gpu/drm/udl/udl_edid.c
+++ b/drivers/gpu/drm/udl/udl_edid.c
@@ -4,6 +4,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
#include "udl_drv.h"
#include "udl_edid.h"
diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c
index c41476ddde68..d9547f5117b9 100644
--- a/drivers/gpu/drm/v3d/v3d_bo.c
+++ b/drivers/gpu/drm/v3d/v3d_bo.c
@@ -18,6 +18,8 @@
#include <linux/dma-buf.h>
#include <linux/vmalloc.h>
+#include <drm/drm_print.h>
+
#include "v3d_drv.h"
#include "uapi/drm/v3d_drm.h"
diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c
index 7e789e181af0..89f24eec62a7 100644
--- a/drivers/gpu/drm/v3d/v3d_debugfs.c
+++ b/drivers/gpu/drm/v3d/v3d_debugfs.c
@@ -8,6 +8,7 @@
#include <linux/string_helpers.h>
#include <drm/drm_debugfs.h>
+#include <drm/drm_print.h>
#include "v3d_drv.h"
#include "v3d_regs.h"
diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c
index c5a3bbbc74c5..e8a46c8bad8a 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -25,6 +25,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <uapi/drm/v3d_drm.h>
#include "v3d_drv.h"
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index bb110d35f749..5a180dc6c452 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -11,6 +11,7 @@
#include <linux/uaccess.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "v3d_drv.h"
#include "v3d_regs.h"
diff --git a/drivers/gpu/drm/v3d/v3d_gemfs.c b/drivers/gpu/drm/v3d/v3d_gemfs.c
index c1a30166c099..bf351fc0d488 100644
--- a/drivers/gpu/drm/v3d/v3d_gemfs.c
+++ b/drivers/gpu/drm/v3d/v3d_gemfs.c
@@ -5,6 +5,8 @@
#include <linux/mount.h>
#include <linux/fs_context.h>
+#include <drm/drm_print.h>
+
#include "v3d_drv.h"
void v3d_gemfs_init(struct v3d_dev *v3d)
diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c
index 31ecc5b4ba5a..b55880fd6c50 100644
--- a/drivers/gpu/drm/v3d/v3d_irq.c
+++ b/drivers/gpu/drm/v3d/v3d_irq.c
@@ -16,6 +16,8 @@
#include <linux/platform_device.h>
#include <linux/sched/clock.h>
+#include <drm/drm_print.h>
+
#include "v3d_drv.h"
#include "v3d_regs.h"
#include "v3d_trace.h"
diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index 0ec06bfbbebb..0867250db7a6 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -21,6 +21,7 @@
#include <linux/sched/clock.h>
#include <linux/kthread.h>
+#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include "v3d_drv.h"
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index f3652e90683c..7de5a95ee7ca 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -4,6 +4,7 @@
* Copyright (C) 2023 Raspberry Pi
*/
+#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include "v3d_drv.h"
diff --git a/drivers/gpu/drm/vboxvideo/vbox_irq.c b/drivers/gpu/drm/vboxvideo/vbox_irq.c
index 903a6c48ee8b..37c66668df57 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_irq.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_irq.c
@@ -12,6 +12,7 @@
#include <linux/pci.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "vbox_drv.h"
diff --git a/drivers/gpu/drm/vboxvideo/vbox_main.c b/drivers/gpu/drm/vboxvideo/vbox_main.c
index 7f686a0190e6..aa6664542b20 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_main.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_main.c
@@ -12,6 +12,7 @@
#include <linux/vbox_err.h>
#include <drm/drm_damage_helper.h>
+#include <drm/drm_print.h>
#include "vbox_drv.h"
#include "vboxvideo_guest.h"
diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c
index 9ff3bade9795..d363c3f0afdf 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_mode.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c
@@ -22,6 +22,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include "hgsmi_channels.h"
@@ -262,8 +263,8 @@ static int vbox_primary_atomic_check(struct drm_plane *plane,
struct drm_crtc_state *crtc_state = NULL;
if (new_state->crtc) {
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- new_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state,
+ new_state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
}
@@ -344,8 +345,8 @@ static int vbox_cursor_atomic_check(struct drm_plane *plane,
int ret;
if (new_state->crtc) {
- crtc_state = drm_atomic_get_existing_crtc_state(state,
- new_state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state,
+ new_state->crtc);
if (WARN_ON(!crtc_state))
return -EINVAL;
}
diff --git a/drivers/gpu/drm/vboxvideo/vbox_ttm.c b/drivers/gpu/drm/vboxvideo/vbox_ttm.c
index dc24c2172fd4..19bf8d023dc8 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_ttm.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_ttm.c
@@ -8,6 +8,7 @@
*/
#include <linux/pci.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include "vbox_drv.h"
int vbox_mm_init(struct vbox_private *vbox)
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
index 123ab0ce1781..bb8c40be3250 100644
--- a/drivers/gpu/drm/vc4/Kconfig
+++ b/drivers/gpu/drm/vc4/Kconfig
@@ -35,6 +35,7 @@ config DRM_VC4_HDMI_CEC
bool "Broadcom VC4 HDMI CEC Support"
depends on DRM_VC4
select CEC_CORE
+ select DRM_DISPLAY_HDMI_CEC_HELPER
help
Choose this option if you have a Broadcom VC4 GPU
and want to use CEC.
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 4aaa587be3a5..46b4474ac41d 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -19,6 +19,7 @@
#include <linux/dma-buf.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include "vc4_drv.h"
#include "uapi/drm/vc4_drm.h"
diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
index fac624a663ea..e765904e13f3 100644
--- a/drivers/gpu/drm/vc4/vc4_debugfs.c
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -4,6 +4,7 @@
*/
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include <linux/seq_file.h>
#include <linux/circ_buf.h>
diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 960550c166d9..2afc88394d64 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -17,6 +17,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <linux/clk.h>
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index c7cb1e3a6434..3846996f9028 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -36,6 +36,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 458e5d987964..deeeaebc702f 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -36,6 +36,7 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index 255e5817618e..ab16164b5eda 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -30,6 +30,7 @@
#include <linux/dma-fence-array.h>
#include <drm/drm_exec.h>
+#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
#include "uapi/drm/vc4_drm.h"
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 07c91b450f93..1798d1156d10 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -32,12 +32,14 @@
*/
#include <drm/display/drm_hdmi_audio_helper.h>
+#include <drm/display/drm_hdmi_cec_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/display/drm_scdc_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <linux/clk.h>
@@ -375,14 +377,6 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
drm_atomic_helper_connector_hdmi_hotplug(connector, status);
- if (status == connector_status_disconnected) {
- cec_phys_addr_invalidate(vc4_hdmi->cec_adap);
- return;
- }
-
- cec_s_phys_addr(vc4_hdmi->cec_adap,
- connector->display_info.source_physical_address, false);
-
if (status != connector_status_connected)
return;
@@ -2384,8 +2378,8 @@ static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
struct vc4_hdmi *vc4_hdmi = priv;
if (vc4_hdmi->cec_rx_msg.len)
- cec_received_msg(vc4_hdmi->cec_adap,
- &vc4_hdmi->cec_rx_msg);
+ drm_connector_hdmi_cec_received_msg(&vc4_hdmi->connector,
+ &vc4_hdmi->cec_rx_msg);
return IRQ_HANDLED;
}
@@ -2395,15 +2389,17 @@ static irqreturn_t vc4_cec_irq_handler_tx_thread(int irq, void *priv)
struct vc4_hdmi *vc4_hdmi = priv;
if (vc4_hdmi->cec_tx_ok) {
- cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_OK,
- 0, 0, 0, 0);
+ drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector,
+ CEC_TX_STATUS_OK,
+ 0, 0, 0, 0);
} else {
/*
* This CEC implementation makes 1 retry, so if we
* get a NACK, then that means it made 2 attempts.
*/
- cec_transmit_done(vc4_hdmi->cec_adap, CEC_TX_STATUS_NACK,
- 0, 2, 0, 0);
+ drm_connector_hdmi_cec_transmit_done(&vc4_hdmi->connector,
+ CEC_TX_STATUS_NACK,
+ 0, 2, 0, 0);
}
return IRQ_HANDLED;
}
@@ -2560,9 +2556,9 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
return ret;
}
-static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
+static int vc4_hdmi_cec_enable(struct drm_connector *connector)
{
- struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *drm = vc4_hdmi->connector.dev;
/* clock period in microseconds */
const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
@@ -2627,9 +2623,9 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
return 0;
}
-static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
+static int vc4_hdmi_cec_disable(struct drm_connector *connector)
{
- struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *drm = vc4_hdmi->connector.dev;
unsigned long flags;
int idx;
@@ -2663,17 +2659,17 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
return 0;
}
-static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
+static int vc4_hdmi_cec_adap_enable(struct drm_connector *connector, bool enable)
{
if (enable)
- return vc4_hdmi_cec_enable(adap);
+ return vc4_hdmi_cec_enable(connector);
else
- return vc4_hdmi_cec_disable(adap);
+ return vc4_hdmi_cec_disable(connector);
}
-static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+static int vc4_hdmi_cec_adap_log_addr(struct drm_connector *connector, u8 log_addr)
{
- struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *drm = vc4_hdmi->connector.dev;
unsigned long flags;
int idx;
@@ -2699,10 +2695,10 @@ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
return 0;
}
-static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+static int vc4_hdmi_cec_adap_transmit(struct drm_connector *connector, u8 attempts,
u32 signal_free_time, struct cec_msg *msg)
{
- struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct drm_device *dev = vc4_hdmi->connector.dev;
unsigned long flags;
u32 val;
@@ -2745,84 +2741,65 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
return 0;
}
-static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = {
- .adap_enable = vc4_hdmi_cec_adap_enable,
- .adap_log_addr = vc4_hdmi_cec_adap_log_addr,
- .adap_transmit = vc4_hdmi_cec_adap_transmit,
-};
-
-static void vc4_hdmi_cec_release(void *ptr)
-{
- struct vc4_hdmi *vc4_hdmi = ptr;
-
- cec_unregister_adapter(vc4_hdmi->cec_adap);
- vc4_hdmi->cec_adap = NULL;
-}
-
-static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
+static int vc4_hdmi_cec_init(struct drm_connector *connector)
{
- struct cec_connector_info conn_info;
+ struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
struct platform_device *pdev = vc4_hdmi->pdev;
struct device *dev = &pdev->dev;
int ret;
- if (!of_property_present(dev->of_node, "interrupts")) {
- dev_warn(dev, "'interrupts' DT property is missing, no CEC\n");
- return 0;
- }
-
- vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
- vc4_hdmi,
- vc4_hdmi->variant->card_name,
- CEC_CAP_DEFAULTS |
- CEC_CAP_CONNECTOR_INFO, 1);
- ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
- if (ret < 0)
- return ret;
-
- cec_fill_conn_info_from_drm(&conn_info, &vc4_hdmi->connector);
- cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info);
-
if (vc4_hdmi->variant->external_irq_controller) {
ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-rx"),
vc4_cec_irq_handler_rx_bare,
vc4_cec_irq_handler_rx_thread, 0,
"vc4 hdmi cec rx", vc4_hdmi);
if (ret)
- goto err_delete_cec_adap;
+ return ret;
ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-tx"),
vc4_cec_irq_handler_tx_bare,
vc4_cec_irq_handler_tx_thread, 0,
"vc4 hdmi cec tx", vc4_hdmi);
if (ret)
- goto err_delete_cec_adap;
+ return ret;
} else {
ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
vc4_cec_irq_handler,
vc4_cec_irq_handler_thread, 0,
"vc4 hdmi cec", vc4_hdmi);
if (ret)
- goto err_delete_cec_adap;
+ return ret;
}
- ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev);
- if (ret < 0)
- goto err_delete_cec_adap;
+ return 0;
+}
+
+static const struct drm_connector_hdmi_cec_funcs vc4_hdmi_cec_funcs = {
+ .init = vc4_hdmi_cec_init,
+ .enable = vc4_hdmi_cec_adap_enable,
+ .log_addr = vc4_hdmi_cec_adap_log_addr,
+ .transmit = vc4_hdmi_cec_adap_transmit,
+};
+
+static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi)
+{
+ struct platform_device *pdev = vc4_hdmi->pdev;
+ struct device *dev = &pdev->dev;
+
+ if (!of_property_present(dev->of_node, "interrupts")) {
+ dev_warn(dev, "'interrupts' DT property is missing, no CEC\n");
+ return 0;
+ }
/*
- * NOTE: Strictly speaking, we should probably use a DRM-managed
- * registration there to avoid removing the CEC adapter by the
- * time the DRM driver doesn't have any user anymore.
+ * NOTE: the CEC adapter will be unregistered by drmm cleanup from
+ * drm_managed_release(), which is called from drm_dev_release()
+ * during device unbind.
*
* However, the CEC framework already cleans up the CEC adapter
* only when the last user has closed its file descriptor, so we
* don't need to handle it in DRM.
*
- * By the time the device-managed hook is executed, we will give
- * up our reference to the CEC adapter and therefore don't
- * really care when it's actually freed.
- *
* There's still a problematic sequence: if we unregister our
* CEC adapter, but the userspace keeps a handle on the CEC
* adapter but not the DRM device for some reason. In such a
@@ -2833,19 +2810,14 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
* the CEC framework already handles this too, by calling
* cec_is_registered() in cec_ioctl() and cec_poll().
*/
- ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi);
- if (ret)
- return ret;
-
- return 0;
-
-err_delete_cec_adap:
- cec_delete_adapter(vc4_hdmi->cec_adap);
-
- return ret;
+ return drmm_connector_hdmi_cec_register(&vc4_hdmi->connector,
+ &vc4_hdmi_cec_funcs,
+ vc4_hdmi->variant->card_name,
+ 1,
+ &pdev->dev);
}
#else
-static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
+static int vc4_hdmi_cec_register(struct vc4_hdmi *vc4_hdmi)
{
return 0;
}
@@ -3250,7 +3222,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
if (ret)
goto err_put_runtime_pm;
- ret = vc4_hdmi_cec_init(vc4_hdmi);
+ ret = vc4_hdmi_cec_register(vc4_hdmi);
if (ret)
goto err_put_runtime_pm;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index a31157c99bee..8d069718df00 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -147,7 +147,6 @@ struct vc4_hdmi {
*/
bool disable_wifi_frequencies;
- struct cec_adapter *cec_adap;
struct cec_msg cec_rx_msg;
bool cec_tx_ok;
bool cec_irq_was_rx;
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 4811d794001f..ee8d0738501b 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -26,6 +26,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <soc/bcm2835/raspberrypi-firmware.h>
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index 69b399f3b802..63e88f90eef7 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -48,6 +48,7 @@
#include <linux/platform_device.h>
#include <drm/drm_drv.h>
+#include <drm/drm_print.h>
#include "vc4_drv.h"
#include "vc4_regs.h"
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index 8f983edb81ff..e563c1210937 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -19,6 +19,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/vc4/vc4_perfmon.c b/drivers/gpu/drm/vc4/vc4_perfmon.c
index f1342f917cf7..1ac80c0b258f 100644
--- a/drivers/gpu/drm/vc4/vc4_perfmon.c
+++ b/drivers/gpu/drm/vc4/vc4_perfmon.c
@@ -9,6 +9,8 @@
* The V3D block provides 16 hardware counters which can count various events.
*/
+#include <drm/drm_print.h>
+
#include "vc4_drv.h"
#include "vc4_regs.h"
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index 056d344c5411..f00d4076ba07 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -24,6 +24,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_print.h>
#include "uapi/drm/vc4_drm.h"
@@ -497,8 +498,7 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
u32 v_subsample = fb->format->vsub;
int ret;
- crtc_state = drm_atomic_get_existing_crtc_state(state->state,
- state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
if (!crtc_state) {
DRM_DEBUG_KMS("Invalid crtc state\n");
return -EINVAL;
@@ -875,8 +875,7 @@ static void vc4_plane_calc_load(struct drm_plane_state *state)
unsigned int vscale_factor;
vc4_state = to_vc4_plane_state(state);
- crtc_state = drm_atomic_get_existing_crtc_state(state->state,
- state->crtc);
+ crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
vrefresh = drm_mode_vrefresh(&crtc_state->adjusted_mode);
/* The HVS is able to process 2 pixels/cycle when scaling the source,
diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c
index 14079853338e..edc471e71c0e 100644
--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
@@ -35,6 +35,8 @@
* actually fairly low.
*/
+#include <drm/drm_print.h>
+
#include "uapi/drm/vc4_drm.h"
#include "vc4_drv.h"
#include "vc4_packet.h"
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 42acac05fe47..9082902100e4 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -21,6 +21,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_writeback.h>
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index bb09df5000bd..3ffe09bc89d2 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -10,6 +10,8 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <drm/drm_print.h>
+
#include "vc4_drv.h"
#include "vc4_regs.h"
diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c
index 1e7bdda55698..545c4c3608f5 100644
--- a/drivers/gpu/drm/vc4/vc4_validate.c
+++ b/drivers/gpu/drm/vc4/vc4_validate.c
@@ -43,6 +43,8 @@
* to use) happens.
*/
+#include <drm/drm_print.h>
+
#include "uapi/drm/vc4_drm.h"
#include "vc4_drv.h"
#include "vc4_packet.h"
diff --git a/drivers/gpu/drm/vc4/vc4_validate_shaders.c b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
index 2d74e786914c..b50b6cdac3f4 100644
--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
+++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
@@ -41,6 +41,8 @@
* this validation is only performed at BO creation time.
*/
+#include <drm/drm_print.h>
+
#include "vc4_drv.h"
#include "vc4_qpu_defines.h"
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 06d702e879b0..b84fad2a5b23 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -17,6 +17,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <linux/clk.h>
diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c
index fd76730fd38c..07db319c3d7f 100644
--- a/drivers/gpu/drm/vgem/vgem_fence.c
+++ b/drivers/gpu/drm/vgem/vgem_fence.c
@@ -79,7 +79,7 @@ static struct dma_fence *vgem_fence_create(struct vgem_file *vfile,
dma_fence_init(&fence->base, &vgem_fence_ops, &fence->lock,
dma_fence_context_alloc(1), 1);
- timer_setup(&fence->timer, vgem_fence_timeout, 0);
+ timer_setup(&fence->timer, vgem_fence_timeout, TIMER_IRQSAFE);
/* We force the fence to expire within 10s to prevent driver hangs */
mod_timer(&fence->timer, jiffies + VGEM_FENCE_TIMEOUT);
diff --git a/drivers/gpu/drm/virtio/virtgpu_debugfs.c b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
index 853dd9aa397e..3a68a16b58ae 100644
--- a/drivers/gpu/drm/virtio/virtgpu_debugfs.c
+++ b/drivers/gpu/drm/virtio/virtgpu_debugfs.c
@@ -27,6 +27,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include "virtgpu_drv.h"
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index c3315935d8bc..6a962c1d6e95 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -30,8 +30,11 @@
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#include "virtgpu_drv.h"
@@ -55,6 +58,7 @@ static const struct drm_crtc_funcs virtio_gpu_crtc_funcs = {
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
};
static const struct drm_framebuffer_funcs virtio_gpu_fb_funcs = {
@@ -99,6 +103,7 @@ static void virtio_gpu_crtc_mode_set_nofb(struct drm_crtc *crtc)
static void virtio_gpu_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
+ drm_crtc_vblank_on(crtc);
}
static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc,
@@ -108,6 +113,8 @@ static void virtio_gpu_crtc_atomic_disable(struct drm_crtc *crtc,
struct virtio_gpu_device *vgdev = dev->dev_private;
struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
+ drm_crtc_vblank_off(crtc);
+
virtio_gpu_cmd_set_scanout(vgdev, output->index, 0, 0, 0, 0, 0);
virtio_gpu_notify(vgdev);
}
@@ -121,9 +128,10 @@ static int virtio_gpu_crtc_atomic_check(struct drm_crtc *crtc,
static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
- struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
- crtc);
+ struct drm_device *dev = crtc->dev;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
struct virtio_gpu_output *output = drm_crtc_to_virtio_gpu_output(crtc);
+ struct drm_pending_vblank_event *event;
/*
* virtio-gpu can't do modeset and plane update operations
@@ -133,6 +141,20 @@ static void virtio_gpu_crtc_atomic_flush(struct drm_crtc *crtc,
*/
if (drm_atomic_crtc_needs_modeset(crtc_state))
output->needs_modeset = true;
+
+ spin_lock_irq(&dev->event_lock);
+
+ event = crtc_state->event;
+ crtc_state->event = NULL;
+
+ if (event) {
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ }
+
+ spin_unlock_irq(&dev->event_lock);
}
static const struct drm_crtc_helper_funcs virtio_gpu_crtc_helper_funcs = {
@@ -257,6 +279,7 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
struct drm_encoder *encoder = &output->enc;
struct drm_crtc *crtc = &output->crtc;
struct drm_plane *primary, *cursor;
+ int ret;
output->index = index;
if (index == 0) {
@@ -271,8 +294,10 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index)
cursor = virtio_gpu_plane_init(vgdev, DRM_PLANE_TYPE_CURSOR, index);
if (IS_ERR(cursor))
return PTR_ERR(cursor);
- drm_crtc_init_with_planes(dev, crtc, primary, cursor,
- &virtio_gpu_crtc_funcs, NULL);
+ ret = drm_crtc_init_with_planes(dev, crtc, primary, cursor,
+ &virtio_gpu_crtc_funcs, NULL);
+ if (ret)
+ return ret;
drm_crtc_helper_add(crtc, &virtio_gpu_crtc_helper_funcs);
drm_connector_init(dev, connector, &virtio_gpu_connector_funcs,
@@ -356,6 +381,10 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
for (i = 0 ; i < vgdev->num_scanouts; ++i)
vgdev_output_init(vgdev, i);
+ ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts);
+ if (ret)
+ return ret;
+
drm_mode_config_reset(vgdev->ddev);
return 0;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 71c6ccad4b99..a5ce96fb8a1d 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -39,6 +39,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include "virtgpu_drv.h"
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 1c15cbf326b7..f3594695bb82 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -29,6 +29,7 @@
#include <drm/drm_file.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "virtgpu_drv.h"
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index e6363c887500..4270bfede7b9 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -26,6 +26,8 @@
#include <linux/dma-mapping.h>
#include <linux/moduleparam.h>
+#include <drm/drm_print.h>
+
#include "virtgpu_drv.h"
static int virtio_gpu_virglrenderer_workaround = 1;
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 29e4b458ae57..a7863f8ee4ee 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -30,6 +30,7 @@
#include <linux/virtio_dma_buf.h>
#include <drm/drm_managed.h>
#include <drm/drm_panic.h>
+#include <drm/drm_print.h>
#include "virtgpu_drv.h"
diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c
index 8181b22b9b46..0c194b4e9488 100644
--- a/drivers/gpu/drm/virtio/virtgpu_vq.c
+++ b/drivers/gpu/drm/virtio/virtgpu_vq.c
@@ -32,6 +32,7 @@
#include <linux/virtio_ring.h>
#include <drm/drm_edid.h>
+#include <drm/drm_print.h>
#include "virtgpu_drv.h"
#include "virtgpu_trace.h"
diff --git a/drivers/gpu/drm/vkms/Kconfig b/drivers/gpu/drm/vkms/Kconfig
index 3c02f928ffe6..3977bbb99f7d 100644
--- a/drivers/gpu/drm/vkms/Kconfig
+++ b/drivers/gpu/drm/vkms/Kconfig
@@ -7,6 +7,7 @@ config DRM_VKMS
select DRM_KMS_HELPER
select DRM_GEM_SHMEM_HELPER
select CRC32
+ select CONFIGFS_FS
default n
help
Virtual Kernel Mode-Setting (VKMS) is used for testing or for
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index d657865e573f..9bb264091c38 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -8,7 +8,10 @@ vkms-y := \
vkms_composer.o \
vkms_writeback.o \
vkms_connector.o \
- vkms_config.o
+ vkms_config.o \
+ vkms_configfs.o \
+ vkms_colorop.o \
+ vkms_luts.o
obj-$(CONFIG_DRM_VKMS) += vkms.o
obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += tests/
diff --git a/drivers/gpu/drm/vkms/tests/Makefile b/drivers/gpu/drm/vkms/tests/Makefile
index 5750f0bd9d40..d4d9ba8d4c54 100644
--- a/drivers/gpu/drm/vkms/tests/Makefile
+++ b/drivers/gpu/drm/vkms/tests/Makefile
@@ -2,6 +2,7 @@
vkms-kunit-tests-y := \
vkms_config_test.o \
- vkms_format_test.o
+ vkms_format_test.o \
+ vkms_color_test.o
obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += vkms-kunit-tests.o
diff --git a/drivers/gpu/drm/vkms/tests/vkms_color_test.c b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
new file mode 100644
index 000000000000..1a1c7cac2f15
--- /dev/null
+++ b/drivers/gpu/drm/vkms/tests/vkms_color_test.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <kunit/test.h>
+
+#include <drm/drm_fixed.h>
+#include <drm/drm_mode.h>
+#include "../vkms_composer.h"
+#include "../vkms_drv.h"
+#include "../vkms_luts.h"
+
+#define TEST_LUT_SIZE 16
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+static struct drm_color_lut test_linear_array[TEST_LUT_SIZE] = {
+ { 0x0, 0x0, 0x0, 0 },
+ { 0x1111, 0x1111, 0x1111, 0 },
+ { 0x2222, 0x2222, 0x2222, 0 },
+ { 0x3333, 0x3333, 0x3333, 0 },
+ { 0x4444, 0x4444, 0x4444, 0 },
+ { 0x5555, 0x5555, 0x5555, 0 },
+ { 0x6666, 0x6666, 0x6666, 0 },
+ { 0x7777, 0x7777, 0x7777, 0 },
+ { 0x8888, 0x8888, 0x8888, 0 },
+ { 0x9999, 0x9999, 0x9999, 0 },
+ { 0xaaaa, 0xaaaa, 0xaaaa, 0 },
+ { 0xbbbb, 0xbbbb, 0xbbbb, 0 },
+ { 0xcccc, 0xcccc, 0xcccc, 0 },
+ { 0xdddd, 0xdddd, 0xdddd, 0 },
+ { 0xeeee, 0xeeee, 0xeeee, 0 },
+ { 0xffff, 0xffff, 0xffff, 0 },
+};
+
+/* lerp test parameters */
+struct vkms_color_test_lerp_params {
+ s64 t;
+ __u16 a;
+ __u16 b;
+ __u16 expected;
+};
+
+/* lerp test cases */
+static const struct vkms_color_test_lerp_params color_test_lerp_cases[] = {
+ /* Half-way round down */
+ { 0x80000000 - 1, 0x0, 0x10, 0x8 },
+ { 0x80000000 - 1, 0x1, 0x10, 0x8 }, /* Odd a */
+ { 0x80000000 - 1, 0x1, 0xf, 0x8 }, /* Odd b */
+ { 0x80000000 - 1, 0x10, 0x10, 0x10 }, /* b = a */
+ { 0x80000000 - 1, 0x10, 0x11, 0x10 }, /* b = a + 1*/
+ /* Half-way round up */
+ { 0x80000000, 0x0, 0x10, 0x8 },
+ { 0x80000000, 0x1, 0x10, 0x9 }, /* Odd a */
+ { 0x80000000, 0x1, 0xf, 0x8 }, /* Odd b */
+ { 0x80000000, 0x10, 0x10, 0x10 }, /* b = a */
+ { 0x80000000, 0x10, 0x11, 0x11 }, /* b = a + 1*/
+ /* t = 0.0 */
+ { 0x0, 0x0, 0x10, 0x0 },
+ { 0x0, 0x1, 0x10, 0x1 }, /* Odd a */
+ { 0x0, 0x1, 0xf, 0x1 }, /* Odd b */
+ { 0x0, 0x10, 0x10, 0x10 }, /* b = a */
+ { 0x0, 0x10, 0x11, 0x10 }, /* b = a + 1*/
+ /* t = 1.0 */
+ { 0x100000000, 0x0, 0x10, 0x10 },
+ { 0x100000000, 0x1, 0x10, 0x10 }, /* Odd a */
+ { 0x100000000, 0x1, 0xf, 0xf }, /* Odd b */
+ { 0x100000000, 0x10, 0x10, 0x10 }, /* b = a */
+ { 0x100000000, 0x10, 0x11, 0x11 }, /* b = a + 1*/
+ /* t = 0.0 + 1 */
+ { 0x0 + 1, 0x0, 0x10, 0x0 },
+ { 0x0 + 1, 0x1, 0x10, 0x1 }, /* Odd a */
+ { 0x0 + 1, 0x1, 0xf, 0x1 }, /* Odd b */
+ { 0x0 + 1, 0x10, 0x10, 0x10 }, /* b = a */
+ { 0x0 + 1, 0x10, 0x11, 0x10 }, /* b = a + 1*/
+ /* t = 1.0 - 1 */
+ { 0x100000000 - 1, 0x0, 0x10, 0x10 },
+ { 0x100000000 - 1, 0x1, 0x10, 0x10 }, /* Odd a */
+ { 0x100000000 - 1, 0x1, 0xf, 0xf }, /* Odd b */
+ { 0x100000000 - 1, 0x10, 0x10, 0x10 }, /* b = a */
+ { 0x100000000 - 1, 0x10, 0x11, 0x11 }, /* b = a + 1*/
+ /* t chosen to verify the flipping point of result a (or b) to a+1 (or b-1) */
+ { 0x80000000 - 1, 0x0, 0x1, 0x0 },
+ { 0x80000000, 0x0, 0x1, 0x1 },
+};
+
+static const struct vkms_color_lut test_linear_lut = {
+ .base = test_linear_array,
+ .lut_length = TEST_LUT_SIZE,
+ .channel_value2index_ratio = 0xf000fll
+};
+
+static void vkms_color_test_get_lut_index(struct kunit *test)
+{
+ s64 lut_index;
+ int i;
+
+ lut_index = get_lut_index(&test_linear_lut, test_linear_array[0].red);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int(lut_index), 0);
+
+ for (i = 0; i < TEST_LUT_SIZE; i++) {
+ lut_index = get_lut_index(&test_linear_lut, test_linear_array[i].red);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(lut_index), i);
+ }
+
+ KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_eotf, 0x0)), 0x0);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x0)), 0x0);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x101)), 0x1);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0x202)), 0x2);
+
+ KUNIT_EXPECT_EQ(test, drm_fixp2int(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x0)), 0x0);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x101)), 0x1);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_inv_eotf, 0x202)), 0x2);
+
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0xfefe)), 0xfe);
+ KUNIT_EXPECT_EQ(test, drm_fixp2int_ceil(get_lut_index(&srgb_eotf, 0xffff)), 0xff);
+}
+
+static void vkms_color_test_lerp(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(color_test_lerp_cases); i++) {
+ const struct vkms_color_test_lerp_params *params = &color_test_lerp_cases[i];
+
+ KUNIT_EXPECT_EQ(test, lerp_u16(params->a, params->b, params->t), params->expected);
+ }
+}
+
+static void vkms_color_test_linear(struct kunit *test)
+{
+ for (int i = 0; i < LUT_SIZE; i++) {
+ int linear = apply_lut_to_channel_value(&linear_eotf, i * 0x101, LUT_RED);
+
+ KUNIT_EXPECT_EQ(test, DIV_ROUND_CLOSEST(linear, 0x101), i);
+ }
+}
+
+static void vkms_color_srgb_inv_srgb(struct kunit *test)
+{
+ u16 srgb, final;
+
+ for (int i = 0; i < LUT_SIZE; i++) {
+ srgb = apply_lut_to_channel_value(&srgb_eotf, i * 0x101, LUT_RED);
+ final = apply_lut_to_channel_value(&srgb_inv_eotf, srgb, LUT_RED);
+
+ KUNIT_EXPECT_GE(test, final / 0x101, i - 1);
+ KUNIT_EXPECT_LE(test, final / 0x101, i + 1);
+ }
+}
+
+#define FIXPT_HALF (DRM_FIXED_ONE >> 1)
+#define FIXPT_QUARTER (DRM_FIXED_ONE >> 2)
+
+static const struct drm_color_ctm_3x4 test_matrix_3x4_50_desat = { {
+ FIXPT_HALF, FIXPT_QUARTER, FIXPT_QUARTER, 0,
+ FIXPT_QUARTER, FIXPT_HALF, FIXPT_QUARTER, 0,
+ FIXPT_QUARTER, FIXPT_QUARTER, FIXPT_HALF, 0
+} };
+
+static void vkms_color_ctm_3x4_50_desat(struct kunit *test)
+{
+ struct pixel_argb_s32 ref, out;
+
+ /* full white */
+ ref.a = 0xffff;
+ ref.r = 0xffff;
+ ref.g = 0xffff;
+ ref.b = 0xffff;
+
+ memcpy(&out, &ref, sizeof(out));
+ apply_3x4_matrix(&out, &test_matrix_3x4_50_desat);
+
+ KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out));
+
+ /* full black */
+ ref.a = 0xffff;
+ ref.r = 0x0;
+ ref.g = 0x0;
+ ref.b = 0x0;
+
+ memcpy(&out, &ref, sizeof(out));
+ apply_3x4_matrix(&out, &test_matrix_3x4_50_desat);
+
+ KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out));
+
+ /* 50% grey */
+ ref.a = 0xffff;
+ ref.r = 0x8000;
+ ref.g = 0x8000;
+ ref.b = 0x8000;
+
+ memcpy(&out, &ref, sizeof(out));
+ apply_3x4_matrix(&out, &test_matrix_3x4_50_desat);
+
+ KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out));
+
+ /* full red to 50% desat */
+ ref.a = 0xffff;
+ ref.r = 0x8000;
+ ref.g = 0x4000;
+ ref.b = 0x4000;
+
+ out.a = 0xffff;
+ out.r = 0xffff;
+ out.g = 0x0;
+ out.b = 0x0;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_50_desat);
+
+ KUNIT_EXPECT_MEMEQ(test, &ref, &out, sizeof(out));
+}
+
+/*
+ * BT.709 encoding matrix
+ *
+ * Values printed from within IGT when converting
+ * igt_matrix_3x4_bt709_enc to the fixed-point format expected
+ * by DRM/KMS.
+ */
+static const struct drm_color_ctm_3x4 test_matrix_3x4_bt709_enc = { {
+ 0x00000000366cf400ull, 0x00000000b7175900ull, 0x0000000127bb300ull, 0,
+ 0x800000001993b3a0ull, 0x800000005609fe80ull, 0x000000006f9db200ull, 0,
+ 0x000000009d70a400ull, 0x800000008f011100ull, 0x800000000e6f9330ull, 0
+} };
+
+static void vkms_color_ctm_3x4_bt709(struct kunit *test)
+{
+ struct pixel_argb_s32 out;
+
+ /* full white to bt709 */
+ out.a = 0xffff;
+ out.r = 0xffff;
+ out.g = 0xffff;
+ out.b = 0xffff;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 255 */
+ KUNIT_EXPECT_GT(test, out.r, 0xfe00);
+ KUNIT_EXPECT_LT(test, out.r, 0x10000);
+
+ /* U 0 */
+ KUNIT_EXPECT_LT(test, out.g, 0x0100);
+
+ /* V 0 */
+ KUNIT_EXPECT_LT(test, out.b, 0x0100);
+
+ /* full black to bt709 */
+ out.a = 0xffff;
+ out.r = 0x0;
+ out.g = 0x0;
+ out.b = 0x0;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 0 */
+ KUNIT_EXPECT_LT(test, out.r, 0x100);
+
+ /* U 0 */
+ KUNIT_EXPECT_LT(test, out.g, 0x0100);
+
+ /* V 0 */
+ KUNIT_EXPECT_LT(test, out.b, 0x0100);
+
+ /* gray to bt709 */
+ out.a = 0xffff;
+ out.r = 0x7fff;
+ out.g = 0x7fff;
+ out.b = 0x7fff;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 127 */
+ KUNIT_EXPECT_GT(test, out.r, 0x7e00);
+ KUNIT_EXPECT_LT(test, out.r, 0x8000);
+
+ /* U 0 */
+ KUNIT_EXPECT_LT(test, out.g, 0x0100);
+
+ /* V 0 */
+ KUNIT_EXPECT_LT(test, out.b, 0x0100);
+
+ /* == red 255 - bt709 enc == */
+ out.a = 0xffff;
+ out.r = 0xffff;
+ out.g = 0x0;
+ out.b = 0x0;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 54 */
+ KUNIT_EXPECT_GT(test, out.r, 0x3500);
+ KUNIT_EXPECT_LT(test, out.r, 0x3700);
+
+ /* U 0 */
+ KUNIT_EXPECT_LT(test, out.g, 0x0100);
+
+ /* V 157 */
+ KUNIT_EXPECT_GT(test, out.b, 0x9C00);
+ KUNIT_EXPECT_LT(test, out.b, 0x9E00);
+
+ /* == green 255 - bt709 enc == */
+ out.a = 0xffff;
+ out.r = 0x0;
+ out.g = 0xffff;
+ out.b = 0x0;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 182 */
+ KUNIT_EXPECT_GT(test, out.r, 0xB500);
+ KUNIT_EXPECT_LT(test, out.r, 0xB780); /* laxed by half*/
+
+ /* U 0 */
+ KUNIT_EXPECT_LT(test, out.g, 0x0100);
+
+ /* V 0 */
+ KUNIT_EXPECT_LT(test, out.b, 0x0100);
+
+ /* == blue 255 - bt709 enc == */
+ out.a = 0xffff;
+ out.r = 0x0;
+ out.g = 0x0;
+ out.b = 0xffff;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 18 */
+ KUNIT_EXPECT_GT(test, out.r, 0x1100);
+ KUNIT_EXPECT_LT(test, out.r, 0x1300);
+
+ /* U 111 */
+ KUNIT_EXPECT_GT(test, out.g, 0x6E00);
+ KUNIT_EXPECT_LT(test, out.g, 0x7000);
+
+ /* V 0 */
+ KUNIT_EXPECT_LT(test, out.b, 0x0100);
+
+ /* == red 140 - bt709 enc == */
+ out.a = 0xffff;
+ out.r = 0x8c8c;
+ out.g = 0x0;
+ out.b = 0x0;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 30 */
+ KUNIT_EXPECT_GT(test, out.r, 0x1D00);
+ KUNIT_EXPECT_LT(test, out.r, 0x1F00);
+
+ /* U 0 */
+ KUNIT_EXPECT_LT(test, out.g, 0x100);
+
+ /* V 87 */
+ KUNIT_EXPECT_GT(test, out.b, 0x5600);
+ KUNIT_EXPECT_LT(test, out.b, 0x5800);
+
+ /* == green 140 - bt709 enc == */
+ out.a = 0xffff;
+ out.r = 0x0;
+ out.g = 0x8c8c;
+ out.b = 0x0;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 30 */
+ KUNIT_EXPECT_GT(test, out.r, 0x6400);
+ KUNIT_EXPECT_LT(test, out.r, 0x6600);
+
+ /* U 0 */
+ KUNIT_EXPECT_LT(test, out.g, 0x100);
+
+ /* V 0 */
+ KUNIT_EXPECT_LT(test, out.b, 0x100);
+
+ /* == blue 140 - bt709 enc == */
+ out.a = 0xffff;
+ out.r = 0x0;
+ out.g = 0x0;
+ out.b = 0x8c8c;
+
+ apply_3x4_matrix(&out, &test_matrix_3x4_bt709_enc);
+
+ /* Y 30 */
+ KUNIT_EXPECT_GT(test, out.r, 0x900);
+ KUNIT_EXPECT_LT(test, out.r, 0xB00);
+
+ /* U 61 */
+ KUNIT_EXPECT_GT(test, out.g, 0x3C00);
+ KUNIT_EXPECT_LT(test, out.g, 0x3E00);
+
+ /* V 0 */
+ KUNIT_EXPECT_LT(test, out.b, 0x100);
+}
+
+static struct kunit_case vkms_color_test_cases[] = {
+ KUNIT_CASE(vkms_color_test_get_lut_index),
+ KUNIT_CASE(vkms_color_test_lerp),
+ KUNIT_CASE(vkms_color_test_linear),
+ KUNIT_CASE(vkms_color_srgb_inv_srgb),
+ KUNIT_CASE(vkms_color_ctm_3x4_50_desat),
+ KUNIT_CASE(vkms_color_ctm_3x4_bt709),
+ {}
+};
+
+static struct kunit_suite vkms_color_test_suite = {
+ .name = "vkms-color",
+ .test_cases = vkms_color_test_cases,
+};
+
+kunit_test_suite(vkms_color_test_suite);
+
+MODULE_DESCRIPTION("Kunit test for VKMS LUT handling");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/vkms/tests/vkms_config_test.c b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
index b0d78a81d2df..1e4ea1863420 100644
--- a/drivers/gpu/drm/vkms/tests/vkms_config_test.c
+++ b/drivers/gpu/drm/vkms/tests/vkms_config_test.c
@@ -83,6 +83,7 @@ struct default_config_case {
bool enable_cursor;
bool enable_writeback;
bool enable_overlay;
+ bool enable_plane_pipeline;
};
static void vkms_config_test_empty_config(struct kunit *test)
@@ -108,14 +109,22 @@ static void vkms_config_test_empty_config(struct kunit *test)
}
static struct default_config_case default_config_cases[] = {
- { false, false, false },
- { true, false, false },
- { true, true, false },
- { true, false, true },
- { false, true, false },
- { false, true, true },
- { false, false, true },
- { true, true, true },
+ { false, false, false, false },
+ { true, false, false, false },
+ { true, true, false, false },
+ { true, false, true, false },
+ { false, true, false, false },
+ { false, true, true, false },
+ { false, false, true, false },
+ { true, true, true, false },
+ { false, false, false, true },
+ { true, false, false, true },
+ { true, true, false, true },
+ { true, false, true, true },
+ { false, true, false, true },
+ { false, true, true, true },
+ { false, false, true, true },
+ { true, true, true, true },
};
KUNIT_ARRAY_PARAM(default_config, default_config_cases, NULL);
@@ -132,11 +141,15 @@ static void vkms_config_test_default_config(struct kunit *test)
config = vkms_config_default_create(params->enable_cursor,
params->enable_writeback,
- params->enable_overlay);
+ params->enable_overlay,
+ params->enable_plane_pipeline);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
/* Planes */
vkms_config_for_each_plane(config, plane_cfg) {
+ KUNIT_EXPECT_EQ(test,
+ vkms_config_plane_get_default_pipeline(plane_cfg),
+ params->enable_plane_pipeline);
switch (vkms_config_plane_get_type(plane_cfg)) {
case DRM_PLANE_TYPE_PRIMARY:
n_primaries++;
@@ -368,7 +381,7 @@ static void vkms_config_test_invalid_plane_number(struct kunit *test)
struct vkms_config_plane *plane_cfg;
int n;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
/* Invalid: No planes */
@@ -393,7 +406,7 @@ static void vkms_config_test_valid_plane_type(struct kunit *test)
struct vkms_config_encoder *encoder_cfg;
int err;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
plane_cfg = get_first_plane(config);
@@ -474,7 +487,7 @@ static void vkms_config_test_valid_plane_possible_crtcs(struct kunit *test)
struct vkms_config_plane *plane_cfg;
struct vkms_config_crtc *crtc_cfg;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
plane_cfg = get_first_plane(config);
@@ -493,7 +506,7 @@ static void vkms_config_test_invalid_crtc_number(struct kunit *test)
struct vkms_config_crtc *crtc_cfg;
int n;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
/* Invalid: No CRTCs */
@@ -516,7 +529,7 @@ static void vkms_config_test_invalid_encoder_number(struct kunit *test)
struct vkms_config_encoder *encoder_cfg;
int n;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
/* Invalid: No encoders */
@@ -541,7 +554,7 @@ static void vkms_config_test_valid_encoder_possible_crtcs(struct kunit *test)
struct vkms_config_encoder *encoder_cfg;
int err;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
crtc_cfg1 = get_first_crtc(config);
@@ -587,7 +600,7 @@ static void vkms_config_test_invalid_connector_number(struct kunit *test)
struct vkms_config_connector *connector_cfg;
int n;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
/* Invalid: No connectors */
@@ -610,7 +623,7 @@ static void vkms_config_test_valid_connector_possible_encoders(struct kunit *tes
struct vkms_config_encoder *encoder_cfg;
struct vkms_config_connector *connector_cfg;
- config = vkms_config_default_create(false, false, false);
+ config = vkms_config_default_create(false, false, false, false);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
encoder_cfg = get_first_encoder(config);
@@ -957,6 +970,29 @@ static void vkms_config_test_connector_get_possible_encoders(struct kunit *test)
vkms_config_destroy(config);
}
+static void vkms_config_test_connector_status(struct kunit *test)
+{
+ struct vkms_config *config;
+ struct vkms_config_connector *connector_cfg;
+ enum drm_connector_status status;
+
+ config = vkms_config_create("test");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config);
+
+ connector_cfg = vkms_config_create_connector(config);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector_cfg);
+
+ status = vkms_config_connector_get_status(connector_cfg);
+ KUNIT_EXPECT_EQ(test, status, connector_status_connected);
+
+ vkms_config_connector_set_status(connector_cfg,
+ connector_status_disconnected);
+ status = vkms_config_connector_get_status(connector_cfg);
+ KUNIT_EXPECT_EQ(test, status, connector_status_disconnected);
+
+ vkms_config_destroy(config);
+}
+
static struct kunit_case vkms_config_test_cases[] = {
KUNIT_CASE(vkms_config_test_empty_config),
KUNIT_CASE_PARAM(vkms_config_test_default_config,
@@ -978,6 +1014,7 @@ static struct kunit_case vkms_config_test_cases[] = {
KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs),
KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs),
KUNIT_CASE(vkms_config_test_connector_get_possible_encoders),
+ KUNIT_CASE(vkms_config_test_connector_status),
{}
};
diff --git a/drivers/gpu/drm/vkms/vkms_colorop.c b/drivers/gpu/drm/vkms/vkms_colorop.c
new file mode 100644
index 000000000000..5c3ffc78aea0
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_colorop.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <linux/slab.h>
+#include <drm/drm_colorop.h>
+#include <drm/drm_print.h>
+#include <drm/drm_property.h>
+#include <drm/drm_plane.h>
+
+#include "vkms_drv.h"
+
+static const u64 supported_tfs =
+ BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
+ BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF);
+
+#define MAX_COLOR_PIPELINE_OPS 4
+
+static int vkms_initialize_color_pipeline(struct drm_plane *plane, struct drm_prop_enum_list *list)
+{
+ struct drm_colorop *ops[MAX_COLOR_PIPELINE_OPS];
+ struct drm_device *dev = plane->dev;
+ int ret;
+ int i = 0, j = 0;
+
+ memset(ops, 0, sizeof(ops));
+
+ /* 1st op: 1d curve */
+ ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL);
+ if (!ops[i]) {
+ drm_err(dev, "KMS: Failed to allocate colorop\n");
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ list->type = ops[i]->base.id;
+ list->name = kasprintf(GFP_KERNEL, "Color Pipeline %d", ops[i]->base.id);
+
+ i++;
+
+ /* 2nd op: 3x4 matrix */
+ ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL);
+ if (!ops[i]) {
+ drm_err(dev, "KMS: Failed to allocate colorop\n");
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane, DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i - 1], ops[i]);
+
+ i++;
+
+ /* 3rd op: 3x4 matrix */
+ ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL);
+ if (!ops[i]) {
+ drm_err(dev, "KMS: Failed to allocate colorop\n");
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_ctm_3x4_init(dev, ops[i], plane, DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i - 1], ops[i]);
+
+ i++;
+
+ /* 4th op: 1d curve */
+ ops[i] = kzalloc(sizeof(*ops[i]), GFP_KERNEL);
+ if (!ops[i]) {
+ drm_err(dev, "KMS: Failed to allocate colorop\n");
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ ret = drm_plane_colorop_curve_1d_init(dev, ops[i], plane, supported_tfs,
+ DRM_COLOROP_FLAG_ALLOW_BYPASS);
+ if (ret)
+ goto cleanup;
+
+ drm_colorop_set_next_property(ops[i - 1], ops[i]);
+
+ return 0;
+
+cleanup:
+ for (j = 0; j < i; j++) {
+ if (ops[j]) {
+ drm_colorop_cleanup(ops[j]);
+ kfree(ops[j]);
+ }
+ }
+
+ return ret;
+}
+
+int vkms_initialize_colorops(struct drm_plane *plane)
+{
+ struct drm_prop_enum_list pipeline;
+ int ret;
+
+ /* Add color pipeline */
+ ret = vkms_initialize_color_pipeline(plane, &pipeline);
+ if (ret)
+ return ret;
+
+ /* Create COLOR_PIPELINE property and attach */
+ ret = drm_plane_create_color_pipeline_property(plane, &pipeline, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index fa269d279e25..3cf3f26e0d8e 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -8,10 +8,13 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_fixed.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
#include <linux/minmax.h>
+#include <kunit/visibility.h>
-#include "vkms_drv.h"
+#include "vkms_composer.h"
+#include "vkms_luts.h"
static u16 pre_mul_blend_channel(u16 src, u16 dst, u16 alpha)
{
@@ -60,7 +63,7 @@ static void fill_background(const struct pixel_argb_u16 *background_color,
}
// lerp(a, b, t) = a + (b - a) * t
-static u16 lerp_u16(u16 a, u16 b, s64 t)
+VISIBLE_IF_KUNIT u16 lerp_u16(u16 a, u16 b, s64 t)
{
s64 a_fp = drm_int2fixp(a);
s64 b_fp = drm_int2fixp(b);
@@ -69,27 +72,18 @@ static u16 lerp_u16(u16 a, u16 b, s64 t)
return drm_fixp2int_round(a_fp + delta);
}
+EXPORT_SYMBOL_IF_KUNIT(lerp_u16);
-static s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
+VISIBLE_IF_KUNIT s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value)
{
s64 color_channel_fp = drm_int2fixp(channel_value);
return drm_fixp_mul(color_channel_fp, lut->channel_value2index_ratio);
}
+EXPORT_SYMBOL_IF_KUNIT(get_lut_index);
-/*
- * This enum is related to the positions of the variables inside
- * `struct drm_color_lut`, so the order of both needs to be the same.
- */
-enum lut_channel {
- LUT_RED = 0,
- LUT_GREEN,
- LUT_BLUE,
- LUT_RESERVED
-};
-
-static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
- enum lut_channel channel)
+VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
+ enum lut_channel channel)
{
s64 lut_index = get_lut_index(lut, channel_value);
u16 *floor_lut_value, *ceil_lut_value;
@@ -114,6 +108,8 @@ static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 chan
return lerp_u16(floor_channel_value, ceil_channel_value,
lut_index & DRM_FIXED_DECIMAL_MASK);
}
+EXPORT_SYMBOL_IF_KUNIT(apply_lut_to_channel_value);
+
static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buffer *output_buffer)
{
@@ -132,6 +128,112 @@ static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buff
}
}
+VISIBLE_IF_KUNIT void apply_3x4_matrix(struct pixel_argb_s32 *pixel,
+ const struct drm_color_ctm_3x4 *matrix)
+{
+ s64 rf, gf, bf;
+ s64 r, g, b;
+
+ r = drm_int2fixp(pixel->r);
+ g = drm_int2fixp(pixel->g);
+ b = drm_int2fixp(pixel->b);
+
+ rf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[0]), r) +
+ drm_fixp_mul(drm_sm2fixp(matrix->matrix[1]), g) +
+ drm_fixp_mul(drm_sm2fixp(matrix->matrix[2]), b) +
+ drm_sm2fixp(matrix->matrix[3]);
+
+ gf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[4]), r) +
+ drm_fixp_mul(drm_sm2fixp(matrix->matrix[5]), g) +
+ drm_fixp_mul(drm_sm2fixp(matrix->matrix[6]), b) +
+ drm_sm2fixp(matrix->matrix[7]);
+
+ bf = drm_fixp_mul(drm_sm2fixp(matrix->matrix[8]), r) +
+ drm_fixp_mul(drm_sm2fixp(matrix->matrix[9]), g) +
+ drm_fixp_mul(drm_sm2fixp(matrix->matrix[10]), b) +
+ drm_sm2fixp(matrix->matrix[11]);
+
+ pixel->r = drm_fixp2int_round(rf);
+ pixel->g = drm_fixp2int_round(gf);
+ pixel->b = drm_fixp2int_round(bf);
+}
+EXPORT_SYMBOL_IF_KUNIT(apply_3x4_matrix);
+
+static void apply_colorop(struct pixel_argb_s32 *pixel, struct drm_colorop *colorop)
+{
+ struct drm_colorop_state *colorop_state = colorop->state;
+ struct drm_device *dev = colorop->dev;
+
+ if (colorop->type == DRM_COLOROP_1D_CURVE) {
+ switch (colorop_state->curve_1d_type) {
+ case DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF:
+ pixel->r = apply_lut_to_channel_value(&srgb_inv_eotf, pixel->r, LUT_RED);
+ pixel->g = apply_lut_to_channel_value(&srgb_inv_eotf, pixel->g, LUT_GREEN);
+ pixel->b = apply_lut_to_channel_value(&srgb_inv_eotf, pixel->b, LUT_BLUE);
+ break;
+ case DRM_COLOROP_1D_CURVE_SRGB_EOTF:
+ pixel->r = apply_lut_to_channel_value(&srgb_eotf, pixel->r, LUT_RED);
+ pixel->g = apply_lut_to_channel_value(&srgb_eotf, pixel->g, LUT_GREEN);
+ pixel->b = apply_lut_to_channel_value(&srgb_eotf, pixel->b, LUT_BLUE);
+ break;
+ default:
+ drm_WARN_ONCE(dev, true,
+ "unknown colorop 1D curve type %d\n",
+ colorop_state->curve_1d_type);
+ break;
+ }
+ } else if (colorop->type == DRM_COLOROP_CTM_3X4) {
+ if (colorop_state->data)
+ apply_3x4_matrix(pixel,
+ (struct drm_color_ctm_3x4 *)colorop_state->data->data);
+ }
+}
+
+static void pre_blend_color_transform(const struct vkms_plane_state *plane_state,
+ struct line_buffer *output_buffer)
+{
+ struct pixel_argb_s32 pixel;
+
+ for (size_t x = 0; x < output_buffer->n_pixels; x++) {
+ struct drm_colorop *colorop = plane_state->base.base.color_pipeline;
+
+ /*
+ * Some operations, such as applying a BT709 encoding matrix,
+ * followed by a decoding matrix, require that we preserve
+ * values above 1.0 and below 0.0 until the end of the pipeline.
+ *
+ * Pack the 16-bit UNORM values into s32 to give us head-room to
+ * avoid clipping until we're at the end of the pipeline. Clip
+ * intentionally at the end of the pipeline before packing
+ * UNORM values back into u16.
+ */
+ pixel.a = output_buffer->pixels[x].a;
+ pixel.r = output_buffer->pixels[x].r;
+ pixel.g = output_buffer->pixels[x].g;
+ pixel.b = output_buffer->pixels[x].b;
+
+ while (colorop) {
+ struct drm_colorop_state *colorop_state;
+
+ colorop_state = colorop->state;
+
+ if (!colorop_state)
+ return;
+
+ if (!colorop_state->bypass)
+ apply_colorop(&pixel, colorop);
+
+ colorop = colorop->next;
+ }
+
+ /* clamp values */
+ output_buffer->pixels[x].a = clamp_val(pixel.a, 0, 0xffff);
+ output_buffer->pixels[x].r = clamp_val(pixel.r, 0, 0xffff);
+ output_buffer->pixels[x].g = clamp_val(pixel.g, 0, 0xffff);
+ output_buffer->pixels[x].b = clamp_val(pixel.b, 0, 0xffff);
+ }
+}
+
/**
* direction_for_rotation() - Get the correct reading direction for a given rotation
*
@@ -347,7 +449,7 @@ static void blend_line(struct vkms_plane_state *current_plane, int y,
*/
current_plane->pixel_read_line(current_plane, src_x_start, src_y_start, direction,
pixel_count, &stage_buffer->pixels[dst_x_start]);
-
+ pre_blend_color_transform(current_plane, stage_buffer);
pre_mul_alpha_blend(stage_buffer, output_buffer,
dst_x_start, pixel_count);
}
diff --git a/drivers/gpu/drm/vkms/vkms_composer.h b/drivers/gpu/drm/vkms/vkms_composer.h
new file mode 100644
index 000000000000..04dd5646f672
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_composer.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _VKMS_COMPOSER_H_
+#define _VKMS_COMPOSER_H_
+
+#include <kunit/visibility.h>
+#include "vkms_drv.h"
+
+/*
+ * This enum is related to the positions of the variables inside
+ * `struct drm_color_lut`, so the order of both needs to be the same.
+ */
+enum lut_channel {
+ LUT_RED = 0,
+ LUT_GREEN,
+ LUT_BLUE,
+ LUT_RESERVED
+};
+
+#if IS_ENABLED(CONFIG_KUNIT)
+u16 lerp_u16(u16 a, u16 b, s64 t);
+s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value);
+u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
+ enum lut_channel channel);
+void apply_3x4_matrix(struct pixel_argb_s32 *pixel, const struct drm_color_ctm_3x4 *matrix);
+#endif
+
+#endif /* _VKMS_COMPOSER_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_config.c b/drivers/gpu/drm/vkms/vkms_config.c
index a1df5659b0fb..8788df9edb7c 100644
--- a/drivers/gpu/drm/vkms/vkms_config.c
+++ b/drivers/gpu/drm/vkms/vkms_config.c
@@ -33,7 +33,8 @@ EXPORT_SYMBOL_IF_KUNIT(vkms_config_create);
struct vkms_config *vkms_config_default_create(bool enable_cursor,
bool enable_writeback,
- bool enable_overlay)
+ bool enable_overlay,
+ bool enable_plane_pipeline)
{
struct vkms_config *config;
struct vkms_config_plane *plane_cfg;
@@ -58,6 +59,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
goto err_alloc;
+ vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
if (enable_overlay) {
for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
@@ -67,6 +69,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
vkms_config_plane_set_type(plane_cfg,
DRM_PLANE_TYPE_OVERLAY);
+ vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
goto err_alloc;
@@ -79,6 +82,7 @@ struct vkms_config *vkms_config_default_create(bool enable_cursor,
goto err_alloc;
vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
+ vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
goto err_alloc;
@@ -361,8 +365,11 @@ static int vkms_config_show(struct seq_file *m, void *data)
vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg)
seq_puts(m, "encoder\n");
- vkms_config_for_each_connector(vkmsdev->config, connector_cfg)
- seq_puts(m, "connector\n");
+ vkms_config_for_each_connector(vkmsdev->config, connector_cfg) {
+ seq_puts(m, "connector:\n");
+ seq_printf(m, "\tstatus=%d\n",
+ vkms_config_connector_get_status(connector_cfg));
+ }
return 0;
}
@@ -386,6 +393,7 @@ struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)
return ERR_PTR(-ENOMEM);
plane_cfg->config = config;
+ plane_cfg->default_pipeline = false;
vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
xa_init_flags(&plane_cfg->possible_crtcs, XA_FLAGS_ALLOC);
@@ -588,6 +596,7 @@ struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *c
return ERR_PTR(-ENOMEM);
connector_cfg->config = config;
+ connector_cfg->status = connector_status_connected;
xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC);
list_add_tail(&connector_cfg->link, &config->connectors);
diff --git a/drivers/gpu/drm/vkms/vkms_config.h b/drivers/gpu/drm/vkms/vkms_config.h
index 0118e3f99706..8f7f286a4bdd 100644
--- a/drivers/gpu/drm/vkms/vkms_config.h
+++ b/drivers/gpu/drm/vkms/vkms_config.h
@@ -7,6 +7,8 @@
#include <linux/types.h>
#include <linux/xarray.h>
+#include <drm/drm_connector.h>
+
#include "vkms_drv.h"
/**
@@ -47,6 +49,7 @@ struct vkms_config_plane {
enum drm_plane_type type;
struct xarray possible_crtcs;
+ bool default_pipeline;
/* Internal usage */
struct vkms_plane *plane;
@@ -99,6 +102,7 @@ struct vkms_config_encoder {
*
* @link: Link to the others connector in vkms_config
* @config: The vkms_config this connector belongs to
+ * @status: Status (connected, disconnected...) of the connector
* @possible_encoders: Array of encoders that can be used with this connector
* @connector: Internal usage. This pointer should never be considered as valid.
* It can be used to store a temporary reference to a VKMS connector
@@ -109,6 +113,7 @@ struct vkms_config_connector {
struct list_head link;
struct vkms_config *config;
+ enum drm_connector_status status;
struct xarray possible_encoders;
/* Internal usage */
@@ -199,7 +204,8 @@ struct vkms_config *vkms_config_create(const char *dev_name);
*/
struct vkms_config *vkms_config_default_create(bool enable_cursor,
bool enable_writeback,
- bool enable_overlay);
+ bool enable_overlay,
+ bool enable_plane_pipeline);
/**
* vkms_config_destroy() - Free a VKMS configuration
@@ -285,6 +291,30 @@ vkms_config_plane_set_type(struct vkms_config_plane *plane_cfg,
}
/**
+ * vkms_config_plane_get_default_pipeline() - Return if the plane will
+ * be created with the default pipeline
+ * @plane_cfg: Plane to get the information from
+ */
+static inline bool
+vkms_config_plane_get_default_pipeline(struct vkms_config_plane *plane_cfg)
+{
+ return plane_cfg->default_pipeline;
+}
+
+/**
+ * vkms_config_plane_set_default_pipeline() - Set if the plane will
+ * be created with the default pipeline
+ * @plane_cfg: Plane to configure the pipeline
+ * @default_pipeline: New default pipeline value
+ */
+static inline void
+vkms_config_plane_set_default_pipeline(struct vkms_config_plane *plane_cfg,
+ bool default_pipeline)
+{
+ plane_cfg->default_pipeline = default_pipeline;
+}
+
+/**
* vkms_config_plane_attach_crtc - Attach a plane to a CRTC
* @plane_cfg: Plane to attach
* @crtc_cfg: CRTC to attach @plane_cfg to
@@ -434,4 +464,26 @@ int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connect
void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
struct vkms_config_encoder *encoder_cfg);
+/**
+ * vkms_config_connector_get_status() - Return the status of the connector
+ * @connector_cfg: Connector to get the status from
+ */
+static inline enum drm_connector_status
+vkms_config_connector_get_status(struct vkms_config_connector *connector_cfg)
+{
+ return connector_cfg->status;
+}
+
+/**
+ * vkms_config_connector_set_status() - Set the status of the connector
+ * @connector_cfg: Connector to set the status to
+ * @status: New connector status
+ */
+static inline void
+vkms_config_connector_set_status(struct vkms_config_connector *connector_cfg,
+ enum drm_connector_status status)
+{
+ connector_cfg->status = status;
+}
+
#endif /* _VKMS_CONFIG_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_configfs.c b/drivers/gpu/drm/vkms/vkms_configfs.c
new file mode 100644
index 000000000000..506666e21c91
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_configfs.c
@@ -0,0 +1,843 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/cleanup.h>
+#include <linux/configfs.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "vkms_drv.h"
+#include "vkms_config.h"
+#include "vkms_configfs.h"
+#include "vkms_connector.h"
+
+/* To avoid registering configfs more than once or unregistering on error */
+static bool is_configfs_registered;
+
+/**
+ * struct vkms_configfs_device - Configfs representation of a VKMS device
+ *
+ * @group: Top level configuration group that represents a VKMS device.
+ * Initialized when a new directory is created under "/config/vkms/"
+ * @planes_group: Default subgroup of @group at "/config/vkms/planes"
+ * @crtcs_group: Default subgroup of @group at "/config/vkms/crtcs"
+ * @encoders_group: Default subgroup of @group at "/config/vkms/encoders"
+ * @connectors_group: Default subgroup of @group at "/config/vkms/connectors"
+ * @lock: Lock used to project concurrent access to the configuration attributes
+ * @config: Protected by @lock. Configuration of the VKMS device
+ * @enabled: Protected by @lock. The device is created or destroyed when this
+ * option changes
+ */
+struct vkms_configfs_device {
+ struct config_group group;
+ struct config_group planes_group;
+ struct config_group crtcs_group;
+ struct config_group encoders_group;
+ struct config_group connectors_group;
+
+ struct mutex lock;
+ struct vkms_config *config;
+ bool enabled;
+};
+
+/**
+ * struct vkms_configfs_plane - Configfs representation of a plane
+ *
+ * @group: Top level configuration group that represents a plane.
+ * Initialized when a new directory is created under "/config/vkms/planes"
+ * @possible_crtcs_group: Default subgroup of @group at "plane/possible_crtcs"
+ * @dev: The vkms_configfs_device this plane belongs to
+ * @config: Configuration of the VKMS plane
+ */
+struct vkms_configfs_plane {
+ struct config_group group;
+ struct config_group possible_crtcs_group;
+ struct vkms_configfs_device *dev;
+ struct vkms_config_plane *config;
+};
+
+/**
+ * struct vkms_configfs_crtc - Configfs representation of a CRTC
+ *
+ * @group: Top level configuration group that represents a CRTC.
+ * Initialized when a new directory is created under "/config/vkms/crtcs"
+ * @dev: The vkms_configfs_device this CRTC belongs to
+ * @config: Configuration of the VKMS CRTC
+ */
+struct vkms_configfs_crtc {
+ struct config_group group;
+ struct vkms_configfs_device *dev;
+ struct vkms_config_crtc *config;
+};
+
+/**
+ * struct vkms_configfs_encoder - Configfs representation of a encoder
+ *
+ * @group: Top level configuration group that represents a encoder.
+ * Initialized when a new directory is created under "/config/vkms/encoders"
+ * @possible_crtcs_group: Default subgroup of @group at "encoder/possible_crtcs"
+ * @dev: The vkms_configfs_device this encoder belongs to
+ * @config: Configuration of the VKMS encoder
+ */
+struct vkms_configfs_encoder {
+ struct config_group group;
+ struct config_group possible_crtcs_group;
+ struct vkms_configfs_device *dev;
+ struct vkms_config_encoder *config;
+};
+
+/**
+ * struct vkms_configfs_connector - Configfs representation of a connector
+ *
+ * @group: Top level configuration group that represents a connector.
+ * Initialized when a new directory is created under "/config/vkms/connectors"
+ * @possible_encoders_group: Default subgroup of @group at
+ * "connector/possible_encoders"
+ * @dev: The vkms_configfs_device this connector belongs to
+ * @config: Configuration of the VKMS connector
+ */
+struct vkms_configfs_connector {
+ struct config_group group;
+ struct config_group possible_encoders_group;
+ struct vkms_configfs_device *dev;
+ struct vkms_config_connector *config;
+};
+
+#define device_item_to_vkms_configfs_device(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_device, \
+ group)
+
+#define child_group_to_vkms_configfs_device(group) \
+ device_item_to_vkms_configfs_device((&(group)->cg_item)->ci_parent)
+
+#define plane_item_to_vkms_configfs_plane(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_plane, group)
+
+#define plane_possible_crtcs_item_to_vkms_configfs_plane(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_plane, \
+ possible_crtcs_group)
+
+#define crtc_item_to_vkms_configfs_crtc(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_crtc, group)
+
+#define encoder_item_to_vkms_configfs_encoder(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_encoder, \
+ group)
+
+#define encoder_possible_crtcs_item_to_vkms_configfs_encoder(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_encoder, \
+ possible_crtcs_group)
+
+#define connector_item_to_vkms_configfs_connector(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_connector, \
+ group)
+
+#define connector_possible_encoders_item_to_vkms_configfs_connector(item) \
+ container_of(to_config_group((item)), struct vkms_configfs_connector, \
+ possible_encoders_group)
+
+static ssize_t crtc_writeback_show(struct config_item *item, char *page)
+{
+ struct vkms_configfs_crtc *crtc;
+ bool writeback;
+
+ crtc = crtc_item_to_vkms_configfs_crtc(item);
+
+ scoped_guard(mutex, &crtc->dev->lock)
+ writeback = vkms_config_crtc_get_writeback(crtc->config);
+
+ return sprintf(page, "%d\n", writeback);
+}
+
+static ssize_t crtc_writeback_store(struct config_item *item, const char *page,
+ size_t count)
+{
+ struct vkms_configfs_crtc *crtc;
+ bool writeback;
+
+ crtc = crtc_item_to_vkms_configfs_crtc(item);
+
+ if (kstrtobool(page, &writeback))
+ return -EINVAL;
+
+ scoped_guard(mutex, &crtc->dev->lock) {
+ if (crtc->dev->enabled)
+ return -EBUSY;
+
+ vkms_config_crtc_set_writeback(crtc->config, writeback);
+ }
+
+ return (ssize_t)count;
+}
+
+CONFIGFS_ATTR(crtc_, writeback);
+
+static struct configfs_attribute *crtc_item_attrs[] = {
+ &crtc_attr_writeback,
+ NULL,
+};
+
+static void crtc_release(struct config_item *item)
+{
+ struct vkms_configfs_crtc *crtc;
+ struct mutex *lock;
+
+ crtc = crtc_item_to_vkms_configfs_crtc(item);
+ lock = &crtc->dev->lock;
+
+ scoped_guard(mutex, lock) {
+ vkms_config_destroy_crtc(crtc->dev->config, crtc->config);
+ kfree(crtc);
+ }
+}
+
+static struct configfs_item_operations crtc_item_operations = {
+ .release = &crtc_release,
+};
+
+static const struct config_item_type crtc_item_type = {
+ .ct_attrs = crtc_item_attrs,
+ .ct_item_ops = &crtc_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *make_crtc_group(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs_device *dev;
+ struct vkms_configfs_crtc *crtc;
+ int ret;
+
+ dev = child_group_to_vkms_configfs_device(group);
+
+ scoped_guard(mutex, &dev->lock) {
+ if (dev->enabled)
+ return ERR_PTR(-EBUSY);
+
+ crtc = kzalloc(sizeof(*crtc), GFP_KERNEL);
+ if (!crtc)
+ return ERR_PTR(-ENOMEM);
+
+ crtc->dev = dev;
+
+ crtc->config = vkms_config_create_crtc(dev->config);
+ if (IS_ERR(crtc->config)) {
+ ret = PTR_ERR(crtc->config);
+ kfree(crtc);
+ return ERR_PTR(ret);
+ }
+
+ config_group_init_type_name(&crtc->group, name, &crtc_item_type);
+ }
+
+ return &crtc->group;
+}
+
+static struct configfs_group_operations crtcs_group_operations = {
+ .make_group = &make_crtc_group,
+};
+
+static const struct config_item_type crtc_group_type = {
+ .ct_group_ops = &crtcs_group_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static int plane_possible_crtcs_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_configfs_plane *plane;
+ struct vkms_configfs_crtc *crtc;
+ int ret;
+
+ if (target->ci_type != &crtc_item_type)
+ return -EINVAL;
+
+ plane = plane_possible_crtcs_item_to_vkms_configfs_plane(src);
+ crtc = crtc_item_to_vkms_configfs_crtc(target);
+
+ scoped_guard(mutex, &plane->dev->lock) {
+ if (plane->dev->enabled)
+ return -EBUSY;
+
+ ret = vkms_config_plane_attach_crtc(plane->config, crtc->config);
+ }
+
+ return ret;
+}
+
+static void plane_possible_crtcs_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_configfs_plane *plane;
+ struct vkms_configfs_crtc *crtc;
+
+ plane = plane_possible_crtcs_item_to_vkms_configfs_plane(src);
+ crtc = crtc_item_to_vkms_configfs_crtc(target);
+
+ scoped_guard(mutex, &plane->dev->lock)
+ vkms_config_plane_detach_crtc(plane->config, crtc->config);
+}
+
+static struct configfs_item_operations plane_possible_crtcs_item_operations = {
+ .allow_link = plane_possible_crtcs_allow_link,
+ .drop_link = plane_possible_crtcs_drop_link,
+};
+
+static const struct config_item_type plane_possible_crtcs_group_type = {
+ .ct_item_ops = &plane_possible_crtcs_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static ssize_t plane_type_show(struct config_item *item, char *page)
+{
+ struct vkms_configfs_plane *plane;
+ enum drm_plane_type type;
+
+ plane = plane_item_to_vkms_configfs_plane(item);
+
+ scoped_guard(mutex, &plane->dev->lock)
+ type = vkms_config_plane_get_type(plane->config);
+
+ return sprintf(page, "%u", type);
+}
+
+static ssize_t plane_type_store(struct config_item *item, const char *page,
+ size_t count)
+{
+ struct vkms_configfs_plane *plane;
+ enum drm_plane_type type;
+
+ plane = plane_item_to_vkms_configfs_plane(item);
+
+ if (kstrtouint(page, 10, &type))
+ return -EINVAL;
+
+ if (type != DRM_PLANE_TYPE_OVERLAY && type != DRM_PLANE_TYPE_PRIMARY &&
+ type != DRM_PLANE_TYPE_CURSOR)
+ return -EINVAL;
+
+ scoped_guard(mutex, &plane->dev->lock) {
+ if (plane->dev->enabled)
+ return -EBUSY;
+
+ vkms_config_plane_set_type(plane->config, type);
+ }
+
+ return (ssize_t)count;
+}
+
+CONFIGFS_ATTR(plane_, type);
+
+static struct configfs_attribute *plane_item_attrs[] = {
+ &plane_attr_type,
+ NULL,
+};
+
+static void plane_release(struct config_item *item)
+{
+ struct vkms_configfs_plane *plane;
+ struct mutex *lock;
+
+ plane = plane_item_to_vkms_configfs_plane(item);
+ lock = &plane->dev->lock;
+
+ scoped_guard(mutex, lock) {
+ vkms_config_destroy_plane(plane->config);
+ kfree(plane);
+ }
+}
+
+static struct configfs_item_operations plane_item_operations = {
+ .release = &plane_release,
+};
+
+static const struct config_item_type plane_item_type = {
+ .ct_attrs = plane_item_attrs,
+ .ct_item_ops = &plane_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *make_plane_group(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs_device *dev;
+ struct vkms_configfs_plane *plane;
+ int ret;
+
+ dev = child_group_to_vkms_configfs_device(group);
+
+ scoped_guard(mutex, &dev->lock) {
+ if (dev->enabled)
+ return ERR_PTR(-EBUSY);
+
+ plane = kzalloc(sizeof(*plane), GFP_KERNEL);
+ if (!plane)
+ return ERR_PTR(-ENOMEM);
+
+ plane->dev = dev;
+
+ plane->config = vkms_config_create_plane(dev->config);
+ if (IS_ERR(plane->config)) {
+ ret = PTR_ERR(plane->config);
+ kfree(plane);
+ return ERR_PTR(ret);
+ }
+
+ config_group_init_type_name(&plane->group, name, &plane_item_type);
+
+ config_group_init_type_name(&plane->possible_crtcs_group,
+ "possible_crtcs",
+ &plane_possible_crtcs_group_type);
+ configfs_add_default_group(&plane->possible_crtcs_group,
+ &plane->group);
+ }
+
+ return &plane->group;
+}
+
+static struct configfs_group_operations planes_group_operations = {
+ .make_group = &make_plane_group,
+};
+
+static const struct config_item_type plane_group_type = {
+ .ct_group_ops = &planes_group_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static int encoder_possible_crtcs_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_configfs_encoder *encoder;
+ struct vkms_configfs_crtc *crtc;
+ int ret;
+
+ if (target->ci_type != &crtc_item_type)
+ return -EINVAL;
+
+ encoder = encoder_possible_crtcs_item_to_vkms_configfs_encoder(src);
+ crtc = crtc_item_to_vkms_configfs_crtc(target);
+
+ scoped_guard(mutex, &encoder->dev->lock) {
+ if (encoder->dev->enabled)
+ return -EBUSY;
+
+ ret = vkms_config_encoder_attach_crtc(encoder->config, crtc->config);
+ }
+
+ return ret;
+}
+
+static void encoder_possible_crtcs_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_configfs_encoder *encoder;
+ struct vkms_configfs_crtc *crtc;
+
+ encoder = encoder_possible_crtcs_item_to_vkms_configfs_encoder(src);
+ crtc = crtc_item_to_vkms_configfs_crtc(target);
+
+ scoped_guard(mutex, &encoder->dev->lock)
+ vkms_config_encoder_detach_crtc(encoder->config, crtc->config);
+}
+
+static struct configfs_item_operations encoder_possible_crtcs_item_operations = {
+ .allow_link = encoder_possible_crtcs_allow_link,
+ .drop_link = encoder_possible_crtcs_drop_link,
+};
+
+static const struct config_item_type encoder_possible_crtcs_group_type = {
+ .ct_item_ops = &encoder_possible_crtcs_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static void encoder_release(struct config_item *item)
+{
+ struct vkms_configfs_encoder *encoder;
+ struct mutex *lock;
+
+ encoder = encoder_item_to_vkms_configfs_encoder(item);
+ lock = &encoder->dev->lock;
+
+ scoped_guard(mutex, lock) {
+ vkms_config_destroy_encoder(encoder->dev->config, encoder->config);
+ kfree(encoder);
+ }
+}
+
+static struct configfs_item_operations encoder_item_operations = {
+ .release = &encoder_release,
+};
+
+static const struct config_item_type encoder_item_type = {
+ .ct_item_ops = &encoder_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *make_encoder_group(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs_device *dev;
+ struct vkms_configfs_encoder *encoder;
+ int ret;
+
+ dev = child_group_to_vkms_configfs_device(group);
+
+ scoped_guard(mutex, &dev->lock) {
+ if (dev->enabled)
+ return ERR_PTR(-EBUSY);
+
+ encoder = kzalloc(sizeof(*encoder), GFP_KERNEL);
+ if (!encoder)
+ return ERR_PTR(-ENOMEM);
+
+ encoder->dev = dev;
+
+ encoder->config = vkms_config_create_encoder(dev->config);
+ if (IS_ERR(encoder->config)) {
+ ret = PTR_ERR(encoder->config);
+ kfree(encoder);
+ return ERR_PTR(ret);
+ }
+
+ config_group_init_type_name(&encoder->group, name,
+ &encoder_item_type);
+
+ config_group_init_type_name(&encoder->possible_crtcs_group,
+ "possible_crtcs",
+ &encoder_possible_crtcs_group_type);
+ configfs_add_default_group(&encoder->possible_crtcs_group,
+ &encoder->group);
+ }
+
+ return &encoder->group;
+}
+
+static struct configfs_group_operations encoders_group_operations = {
+ .make_group = &make_encoder_group,
+};
+
+static const struct config_item_type encoder_group_type = {
+ .ct_group_ops = &encoders_group_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static ssize_t connector_status_show(struct config_item *item, char *page)
+{
+ struct vkms_configfs_connector *connector;
+ enum drm_connector_status status;
+
+ connector = connector_item_to_vkms_configfs_connector(item);
+
+ scoped_guard(mutex, &connector->dev->lock)
+ status = vkms_config_connector_get_status(connector->config);
+
+ return sprintf(page, "%u", status);
+}
+
+static ssize_t connector_status_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ struct vkms_configfs_connector *connector;
+ enum drm_connector_status status;
+
+ connector = connector_item_to_vkms_configfs_connector(item);
+
+ if (kstrtouint(page, 10, &status))
+ return -EINVAL;
+
+ if (status != connector_status_connected &&
+ status != connector_status_disconnected &&
+ status != connector_status_unknown)
+ return -EINVAL;
+
+ scoped_guard(mutex, &connector->dev->lock) {
+ vkms_config_connector_set_status(connector->config, status);
+
+ if (connector->dev->enabled)
+ vkms_trigger_connector_hotplug(connector->dev->config->dev);
+ }
+
+ return (ssize_t)count;
+}
+
+CONFIGFS_ATTR(connector_, status);
+
+static struct configfs_attribute *connector_item_attrs[] = {
+ &connector_attr_status,
+ NULL,
+};
+
+static void connector_release(struct config_item *item)
+{
+ struct vkms_configfs_connector *connector;
+ struct mutex *lock;
+
+ connector = connector_item_to_vkms_configfs_connector(item);
+ lock = &connector->dev->lock;
+
+ scoped_guard(mutex, lock) {
+ vkms_config_destroy_connector(connector->config);
+ kfree(connector);
+ }
+}
+
+static struct configfs_item_operations connector_item_operations = {
+ .release = &connector_release,
+};
+
+static const struct config_item_type connector_item_type = {
+ .ct_attrs = connector_item_attrs,
+ .ct_item_ops = &connector_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static int connector_possible_encoders_allow_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_configfs_connector *connector;
+ struct vkms_configfs_encoder *encoder;
+ int ret;
+
+ if (target->ci_type != &encoder_item_type)
+ return -EINVAL;
+
+ connector = connector_possible_encoders_item_to_vkms_configfs_connector(src);
+ encoder = encoder_item_to_vkms_configfs_encoder(target);
+
+ scoped_guard(mutex, &connector->dev->lock) {
+ if (connector->dev->enabled)
+ return -EBUSY;
+
+ ret = vkms_config_connector_attach_encoder(connector->config,
+ encoder->config);
+ }
+
+ return ret;
+}
+
+static void connector_possible_encoders_drop_link(struct config_item *src,
+ struct config_item *target)
+{
+ struct vkms_configfs_connector *connector;
+ struct vkms_configfs_encoder *encoder;
+
+ connector = connector_possible_encoders_item_to_vkms_configfs_connector(src);
+ encoder = encoder_item_to_vkms_configfs_encoder(target);
+
+ scoped_guard(mutex, &connector->dev->lock) {
+ vkms_config_connector_detach_encoder(connector->config,
+ encoder->config);
+ }
+}
+
+static struct configfs_item_operations connector_possible_encoders_item_operations = {
+ .allow_link = connector_possible_encoders_allow_link,
+ .drop_link = connector_possible_encoders_drop_link,
+};
+
+static const struct config_item_type connector_possible_encoders_group_type = {
+ .ct_item_ops = &connector_possible_encoders_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *make_connector_group(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs_device *dev;
+ struct vkms_configfs_connector *connector;
+ int ret;
+
+ dev = child_group_to_vkms_configfs_device(group);
+
+ scoped_guard(mutex, &dev->lock) {
+ if (dev->enabled)
+ return ERR_PTR(-EBUSY);
+
+ connector = kzalloc(sizeof(*connector), GFP_KERNEL);
+ if (!connector)
+ return ERR_PTR(-ENOMEM);
+
+ connector->dev = dev;
+
+ connector->config = vkms_config_create_connector(dev->config);
+ if (IS_ERR(connector->config)) {
+ ret = PTR_ERR(connector->config);
+ kfree(connector);
+ return ERR_PTR(ret);
+ }
+
+ config_group_init_type_name(&connector->group, name,
+ &connector_item_type);
+
+ config_group_init_type_name(&connector->possible_encoders_group,
+ "possible_encoders",
+ &connector_possible_encoders_group_type);
+ configfs_add_default_group(&connector->possible_encoders_group,
+ &connector->group);
+ }
+
+ return &connector->group;
+}
+
+static struct configfs_group_operations connectors_group_operations = {
+ .make_group = &make_connector_group,
+};
+
+static const struct config_item_type connector_group_type = {
+ .ct_group_ops = &connectors_group_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static ssize_t device_enabled_show(struct config_item *item, char *page)
+{
+ struct vkms_configfs_device *dev;
+ bool enabled;
+
+ dev = device_item_to_vkms_configfs_device(item);
+
+ scoped_guard(mutex, &dev->lock)
+ enabled = dev->enabled;
+
+ return sprintf(page, "%d\n", enabled);
+}
+
+static ssize_t device_enabled_store(struct config_item *item, const char *page,
+ size_t count)
+{
+ struct vkms_configfs_device *dev;
+ bool enabled;
+ int ret = 0;
+
+ dev = device_item_to_vkms_configfs_device(item);
+
+ if (kstrtobool(page, &enabled))
+ return -EINVAL;
+
+ scoped_guard(mutex, &dev->lock) {
+ if (!dev->enabled && enabled) {
+ if (!vkms_config_is_valid(dev->config))
+ return -EINVAL;
+
+ ret = vkms_create(dev->config);
+ if (ret)
+ return ret;
+ } else if (dev->enabled && !enabled) {
+ vkms_destroy(dev->config);
+ }
+
+ dev->enabled = enabled;
+ }
+
+ return (ssize_t)count;
+}
+
+CONFIGFS_ATTR(device_, enabled);
+
+static struct configfs_attribute *device_item_attrs[] = {
+ &device_attr_enabled,
+ NULL,
+};
+
+static void device_release(struct config_item *item)
+{
+ struct vkms_configfs_device *dev;
+
+ dev = device_item_to_vkms_configfs_device(item);
+
+ if (dev->enabled)
+ vkms_destroy(dev->config);
+
+ mutex_destroy(&dev->lock);
+ vkms_config_destroy(dev->config);
+ kfree(dev);
+}
+
+static struct configfs_item_operations device_item_operations = {
+ .release = &device_release,
+};
+
+static const struct config_item_type device_item_type = {
+ .ct_attrs = device_item_attrs,
+ .ct_item_ops = &device_item_operations,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *make_device_group(struct config_group *group,
+ const char *name)
+{
+ struct vkms_configfs_device *dev;
+ int ret;
+
+ if (strcmp(name, DEFAULT_DEVICE_NAME) == 0)
+ return ERR_PTR(-EINVAL);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ dev->config = vkms_config_create(name);
+ if (IS_ERR(dev->config)) {
+ ret = PTR_ERR(dev->config);
+ kfree(dev);
+ return ERR_PTR(ret);
+ }
+
+ config_group_init_type_name(&dev->group, name, &device_item_type);
+ mutex_init(&dev->lock);
+
+ config_group_init_type_name(&dev->planes_group, "planes",
+ &plane_group_type);
+ configfs_add_default_group(&dev->planes_group, &dev->group);
+
+ config_group_init_type_name(&dev->crtcs_group, "crtcs",
+ &crtc_group_type);
+ configfs_add_default_group(&dev->crtcs_group, &dev->group);
+
+ config_group_init_type_name(&dev->encoders_group, "encoders",
+ &encoder_group_type);
+ configfs_add_default_group(&dev->encoders_group, &dev->group);
+
+ config_group_init_type_name(&dev->connectors_group, "connectors",
+ &connector_group_type);
+ configfs_add_default_group(&dev->connectors_group, &dev->group);
+
+ return &dev->group;
+}
+
+static struct configfs_group_operations device_group_ops = {
+ .make_group = &make_device_group,
+};
+
+static const struct config_item_type device_group_type = {
+ .ct_group_ops = &device_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem vkms_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_name = "vkms",
+ .ci_type = &device_group_type,
+ },
+ },
+ .su_mutex = __MUTEX_INITIALIZER(vkms_subsys.su_mutex),
+};
+
+int vkms_configfs_register(void)
+{
+ int ret;
+
+ if (is_configfs_registered)
+ return 0;
+
+ config_group_init(&vkms_subsys.su_group);
+ ret = configfs_register_subsystem(&vkms_subsys);
+
+ is_configfs_registered = ret == 0;
+
+ return ret;
+}
+
+void vkms_configfs_unregister(void)
+{
+ if (is_configfs_registered)
+ configfs_unregister_subsystem(&vkms_subsys);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_configfs.h b/drivers/gpu/drm/vkms/vkms_configfs.h
new file mode 100644
index 000000000000..e9020b0043db
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_configfs.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef _VKMS_CONFIGFS_H_
+#define _VKMS_CONFIGFS_H_
+
+int vkms_configfs_register(void);
+void vkms_configfs_unregister(void);
+
+#endif /* _VKMS_CONFIGFS_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_connector.c b/drivers/gpu/drm/vkms/vkms_connector.c
index 48b10cba322a..b0a6b212d3f4 100644
--- a/drivers/gpu/drm/vkms/vkms_connector.c
+++ b/drivers/gpu/drm/vkms/vkms_connector.c
@@ -5,9 +5,37 @@
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
+#include "vkms_config.h"
#include "vkms_connector.h"
+static enum drm_connector_status vkms_connector_detect(struct drm_connector *connector,
+ bool force)
+{
+ struct drm_device *dev = connector->dev;
+ struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
+ struct vkms_connector *vkms_connector;
+ enum drm_connector_status status;
+ struct vkms_config_connector *connector_cfg;
+
+ vkms_connector = drm_connector_to_vkms_connector(connector);
+
+ /*
+ * The connector configuration might not exist if its configfs directory
+ * was deleted. Therefore, use the configuration if present or keep the
+ * current status if we can not access it anymore.
+ */
+ status = connector->status;
+
+ vkms_config_for_each_connector(vkmsdev->config, connector_cfg) {
+ if (connector_cfg->connector == vkms_connector)
+ status = vkms_config_connector_get_status(connector_cfg);
+ }
+
+ return status;
+}
+
static const struct drm_connector_funcs vkms_connector_funcs = {
+ .detect = vkms_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -59,3 +87,10 @@ struct vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev)
return connector;
}
+
+void vkms_trigger_connector_hotplug(struct vkms_device *vkmsdev)
+{
+ struct drm_device *dev = &vkmsdev->drm;
+
+ drm_kms_helper_hotplug_event(dev);
+}
diff --git a/drivers/gpu/drm/vkms/vkms_connector.h b/drivers/gpu/drm/vkms/vkms_connector.h
index c9149c1b7af0..ed312f4eff3a 100644
--- a/drivers/gpu/drm/vkms/vkms_connector.h
+++ b/drivers/gpu/drm/vkms/vkms_connector.h
@@ -5,6 +5,9 @@
#include "vkms_drv.h"
+#define drm_connector_to_vkms_connector(target) \
+ container_of(target, struct vkms_connector, base)
+
/**
* struct vkms_connector - VKMS custom type wrapping around the DRM connector
*
@@ -23,4 +26,10 @@ struct vkms_connector {
*/
struct vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev);
+/**
+ * vkms_trigger_connector_hotplug() - Update the device's connectors status
+ * @vkmsdev: VKMS device to update
+ */
+void vkms_trigger_connector_hotplug(struct vkms_device *vkmsdev);
+
#endif /* _VKMS_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index e60573e0f3e9..9a7db1d51022 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -5,27 +5,21 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#include "vkms_drv.h"
-static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
+static bool vkms_crtc_handle_vblank_timeout(struct drm_crtc *crtc)
{
- struct vkms_output *output = container_of(timer, struct vkms_output,
- vblank_hrtimer);
- struct drm_crtc *crtc = &output->crtc;
+ struct vkms_output *output = drm_crtc_to_vkms_output(crtc);
struct vkms_crtc_state *state;
- u64 ret_overrun;
bool ret, fence_cookie;
fence_cookie = dma_fence_begin_signalling();
- ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer,
- output->period_ns);
- if (ret_overrun != 1)
- pr_warn("%s: vblank timer overrun\n", __func__);
-
spin_lock(&output->lock);
ret = drm_crtc_handle_vblank(crtc);
if (!ret)
@@ -57,55 +51,6 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
dma_fence_end_signalling(fence_cookie);
- return HRTIMER_RESTART;
-}
-
-static int vkms_enable_vblank(struct drm_crtc *crtc)
-{
- struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
- struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
-
- hrtimer_setup(&out->vblank_hrtimer, &vkms_vblank_simulate, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- out->period_ns = ktime_set(0, vblank->framedur_ns);
- hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL);
-
- return 0;
-}
-
-static void vkms_disable_vblank(struct drm_crtc *crtc)
-{
- struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
-
- hrtimer_cancel(&out->vblank_hrtimer);
-}
-
-static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
- int *max_error, ktime_t *vblank_time,
- bool in_vblank_irq)
-{
- struct vkms_output *output = drm_crtc_to_vkms_output(crtc);
- struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
-
- if (!READ_ONCE(vblank->enabled)) {
- *vblank_time = ktime_get();
- return true;
- }
-
- *vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
-
- if (WARN_ON(*vblank_time == vblank->time))
- return true;
-
- /*
- * To prevent races we roll the hrtimer forward before we do any
- * interrupt processing - this is how real hw works (the interrupt is
- * only generated after all the vblank registers are updated) and what
- * the vblank core expects. Therefore we need to always correct the
- * timestampe by one frame.
- */
- *vblank_time -= output->period_ns;
-
return true;
}
@@ -159,9 +104,7 @@ static const struct drm_crtc_funcs vkms_crtc_funcs = {
.reset = vkms_atomic_crtc_reset,
.atomic_duplicate_state = vkms_atomic_crtc_duplicate_state,
.atomic_destroy_state = vkms_atomic_crtc_destroy_state,
- .enable_vblank = vkms_enable_vblank,
- .disable_vblank = vkms_disable_vblank,
- .get_vblank_timestamp = vkms_get_vblank_timestamp,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
.get_crc_sources = vkms_get_crc_sources,
.set_crc_source = vkms_set_crc_source,
.verify_crc_source = vkms_verify_crc_source,
@@ -185,7 +128,7 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
return ret;
drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
- plane_state = drm_atomic_get_existing_plane_state(crtc_state->state, plane);
+ plane_state = drm_atomic_get_new_plane_state(crtc_state->state, plane);
WARN_ON(!plane_state);
if (!plane_state->visible)
@@ -201,7 +144,7 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
i = 0;
drm_for_each_plane_mask(plane, crtc->dev, crtc_state->plane_mask) {
- plane_state = drm_atomic_get_existing_plane_state(crtc_state->state, plane);
+ plane_state = drm_atomic_get_new_plane_state(crtc_state->state, plane);
if (!plane_state->visible)
continue;
@@ -213,18 +156,6 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}
-static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
- struct drm_atomic_state *state)
-{
- drm_crtc_vblank_on(crtc);
-}
-
-static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
- struct drm_atomic_state *state)
-{
- drm_crtc_vblank_off(crtc);
-}
-
static void vkms_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_state *state)
__acquires(&vkms_output->lock)
@@ -265,8 +196,9 @@ static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
.atomic_check = vkms_crtc_atomic_check,
.atomic_begin = vkms_crtc_atomic_begin,
.atomic_flush = vkms_crtc_atomic_flush,
- .atomic_enable = vkms_crtc_atomic_enable,
- .atomic_disable = vkms_crtc_atomic_disable,
+ .atomic_enable = drm_crtc_vblank_atomic_enable,
+ .atomic_disable = drm_crtc_vblank_atomic_disable,
+ .handle_vblank_timeout = vkms_crtc_handle_vblank_timeout,
};
struct vkms_output *vkms_crtc_init(struct drm_device *dev, struct drm_plane *primary,
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index e8472d9b6e3b..dd1402f43773 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -23,11 +23,13 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_vblank.h>
#include "vkms_config.h"
+#include "vkms_configfs.h"
#include "vkms_drv.h"
#define DRIVER_NAME "vkms"
@@ -49,6 +51,14 @@ static bool enable_overlay;
module_param_named(enable_overlay, enable_overlay, bool, 0444);
MODULE_PARM_DESC(enable_overlay, "Enable/Disable overlay support");
+static bool enable_plane_pipeline;
+module_param_named(enable_plane_pipeline, enable_plane_pipeline, bool, 0444);
+MODULE_PARM_DESC(enable_plane_pipeline, "Enable/Disable plane pipeline support");
+
+static bool create_default_dev = true;
+module_param_named(create_default_dev, create_default_dev, bool, 0444);
+MODULE_PARM_DESC(create_default_dev, "Create or not the default VKMS device");
+
DEFINE_DRM_GEM_FOPS(vkms_driver_fops);
static void vkms_atomic_commit_tail(struct drm_atomic_state *old_state)
@@ -146,7 +156,7 @@ static int vkms_modeset_init(struct vkms_device *vkmsdev)
return vkms_output_init(vkmsdev);
}
-static int vkms_create(struct vkms_config *config)
+int vkms_create(struct vkms_config *config)
{
int ret;
struct faux_device *fdev;
@@ -214,7 +224,15 @@ static int __init vkms_init(void)
int ret;
struct vkms_config *config;
- config = vkms_config_default_create(enable_cursor, enable_writeback, enable_overlay);
+ ret = vkms_configfs_register();
+ if (ret)
+ return ret;
+
+ if (!create_default_dev)
+ return 0;
+
+ config = vkms_config_default_create(enable_cursor, enable_writeback,
+ enable_overlay, enable_plane_pipeline);
if (IS_ERR(config))
return PTR_ERR(config);
@@ -229,7 +247,7 @@ static int __init vkms_init(void)
return 0;
}
-static void vkms_destroy(struct vkms_config *config)
+void vkms_destroy(struct vkms_config *config)
{
struct faux_device *fdev;
@@ -240,6 +258,7 @@ static void vkms_destroy(struct vkms_config *config)
fdev = config->dev->faux_dev;
+ drm_colorop_pipeline_destroy(&config->dev->drm);
drm_dev_unregister(&config->dev->drm);
drm_atomic_helper_shutdown(&config->dev->drm);
devres_release_group(&fdev->dev, NULL);
@@ -250,6 +269,8 @@ static void vkms_destroy(struct vkms_config *config)
static void __exit vkms_exit(void)
{
+ vkms_configfs_unregister();
+
if (!default_config)
return;
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 8013c31efe3b..0933e4ce0ff0 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -45,6 +45,10 @@ struct vkms_frame_info {
unsigned int rotation;
};
+struct pixel_argb_s32 {
+ s32 a, r, g, b;
+};
+
/**
* struct pixel_argb_u16 - Internal representation of a pixel color.
* @a: Alpha component value, stored in 16 bits, without padding, using
@@ -215,8 +219,6 @@ struct vkms_output {
struct drm_crtc crtc;
struct drm_writeback_connector wb_connector;
struct drm_encoder wb_encoder;
- struct hrtimer vblank_hrtimer;
- ktime_t period_ns;
struct workqueue_struct *composer_workq;
spinlock_t lock;
@@ -227,6 +229,7 @@ struct vkms_output {
};
struct vkms_config;
+struct vkms_config_plane;
/**
* struct vkms_device - Description of a VKMS device
@@ -259,6 +262,26 @@ struct vkms_device {
container_of(target, struct vkms_plane_state, base.base)
/**
+ * vkms_create() - Create a device from a configuration
+ * @config: Config used to configure the new device
+ *
+ * A pointer to the created vkms_device is stored in @config
+ *
+ * Returns:
+ * 0 on success or an error.
+ */
+int vkms_create(struct vkms_config *config);
+
+/**
+ * vkms_destroy() - Destroy a device
+ * @config: Config from which the device was created
+ *
+ * The device is completely removed, but the @config is not freed. It can be
+ * reused or destroyed with vkms_config_destroy().
+ */
+void vkms_destroy(struct vkms_config *config);
+
+/**
* vkms_crtc_init() - Initialize a CRTC for VKMS
* @dev: DRM device associated with the VKMS buffer
* @crtc: uninitialized CRTC device
@@ -280,10 +303,10 @@ int vkms_output_init(struct vkms_device *vkmsdev);
* vkms_plane_init() - Initialize a plane
*
* @vkmsdev: VKMS device containing the plane
- * @type: type of plane to initialize
+ * @plane_cfg: plane configuration
*/
struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev,
- enum drm_plane_type type);
+ struct vkms_config_plane *plane_cfg);
/* CRC Support */
const char *const *vkms_get_crc_sources(struct drm_crtc *crtc,
@@ -300,4 +323,7 @@ void vkms_writeback_row(struct vkms_writeback_job *wb, const struct line_buffer
/* Writeback */
int vkms_enable_writeback_connector(struct vkms_device *vkmsdev, struct vkms_output *vkms_out);
+/* Colorops */
+int vkms_initialize_colorops(struct drm_plane *plane);
+
#endif /* _VKMS_DRV_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_luts.c b/drivers/gpu/drm/vkms/vkms_luts.c
new file mode 100644
index 000000000000..82cb792f10d8
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_luts.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <drm/drm_mode.h>
+
+#include "vkms_drv.h"
+#include "vkms_luts.h"
+
+/*
+ * These luts were generated with a LUT generated based on
+ * skia's transfer function code. The LUT generator can be
+ * found at
+ * https://gitlab.freedesktop.org/hwentland/lutgen
+ */
+
+static struct drm_color_lut linear_array[LUT_SIZE] = {
+ { 0x0, 0x0, 0x0, 0 },
+ { 0x101, 0x101, 0x101, 0 },
+ { 0x202, 0x202, 0x202, 0 },
+ { 0x303, 0x303, 0x303, 0 },
+ { 0x404, 0x404, 0x404, 0 },
+ { 0x505, 0x505, 0x505, 0 },
+ { 0x606, 0x606, 0x606, 0 },
+ { 0x707, 0x707, 0x707, 0 },
+ { 0x808, 0x808, 0x808, 0 },
+ { 0x909, 0x909, 0x909, 0 },
+ { 0xa0a, 0xa0a, 0xa0a, 0 },
+ { 0xb0b, 0xb0b, 0xb0b, 0 },
+ { 0xc0c, 0xc0c, 0xc0c, 0 },
+ { 0xd0d, 0xd0d, 0xd0d, 0 },
+ { 0xe0e, 0xe0e, 0xe0e, 0 },
+ { 0xf0f, 0xf0f, 0xf0f, 0 },
+ { 0x1010, 0x1010, 0x1010, 0 },
+ { 0x1111, 0x1111, 0x1111, 0 },
+ { 0x1212, 0x1212, 0x1212, 0 },
+ { 0x1313, 0x1313, 0x1313, 0 },
+ { 0x1414, 0x1414, 0x1414, 0 },
+ { 0x1515, 0x1515, 0x1515, 0 },
+ { 0x1616, 0x1616, 0x1616, 0 },
+ { 0x1717, 0x1717, 0x1717, 0 },
+ { 0x1818, 0x1818, 0x1818, 0 },
+ { 0x1919, 0x1919, 0x1919, 0 },
+ { 0x1a1a, 0x1a1a, 0x1a1a, 0 },
+ { 0x1b1b, 0x1b1b, 0x1b1b, 0 },
+ { 0x1c1c, 0x1c1c, 0x1c1c, 0 },
+ { 0x1d1d, 0x1d1d, 0x1d1d, 0 },
+ { 0x1e1e, 0x1e1e, 0x1e1e, 0 },
+ { 0x1f1f, 0x1f1f, 0x1f1f, 0 },
+ { 0x2020, 0x2020, 0x2020, 0 },
+ { 0x2121, 0x2121, 0x2121, 0 },
+ { 0x2222, 0x2222, 0x2222, 0 },
+ { 0x2323, 0x2323, 0x2323, 0 },
+ { 0x2424, 0x2424, 0x2424, 0 },
+ { 0x2525, 0x2525, 0x2525, 0 },
+ { 0x2626, 0x2626, 0x2626, 0 },
+ { 0x2727, 0x2727, 0x2727, 0 },
+ { 0x2828, 0x2828, 0x2828, 0 },
+ { 0x2929, 0x2929, 0x2929, 0 },
+ { 0x2a2a, 0x2a2a, 0x2a2a, 0 },
+ { 0x2b2b, 0x2b2b, 0x2b2b, 0 },
+ { 0x2c2c, 0x2c2c, 0x2c2c, 0 },
+ { 0x2d2d, 0x2d2d, 0x2d2d, 0 },
+ { 0x2e2e, 0x2e2e, 0x2e2e, 0 },
+ { 0x2f2f, 0x2f2f, 0x2f2f, 0 },
+ { 0x3030, 0x3030, 0x3030, 0 },
+ { 0x3131, 0x3131, 0x3131, 0 },
+ { 0x3232, 0x3232, 0x3232, 0 },
+ { 0x3333, 0x3333, 0x3333, 0 },
+ { 0x3434, 0x3434, 0x3434, 0 },
+ { 0x3535, 0x3535, 0x3535, 0 },
+ { 0x3636, 0x3636, 0x3636, 0 },
+ { 0x3737, 0x3737, 0x3737, 0 },
+ { 0x3838, 0x3838, 0x3838, 0 },
+ { 0x3939, 0x3939, 0x3939, 0 },
+ { 0x3a3a, 0x3a3a, 0x3a3a, 0 },
+ { 0x3b3b, 0x3b3b, 0x3b3b, 0 },
+ { 0x3c3c, 0x3c3c, 0x3c3c, 0 },
+ { 0x3d3d, 0x3d3d, 0x3d3d, 0 },
+ { 0x3e3e, 0x3e3e, 0x3e3e, 0 },
+ { 0x3f3f, 0x3f3f, 0x3f3f, 0 },
+ { 0x4040, 0x4040, 0x4040, 0 },
+ { 0x4141, 0x4141, 0x4141, 0 },
+ { 0x4242, 0x4242, 0x4242, 0 },
+ { 0x4343, 0x4343, 0x4343, 0 },
+ { 0x4444, 0x4444, 0x4444, 0 },
+ { 0x4545, 0x4545, 0x4545, 0 },
+ { 0x4646, 0x4646, 0x4646, 0 },
+ { 0x4747, 0x4747, 0x4747, 0 },
+ { 0x4848, 0x4848, 0x4848, 0 },
+ { 0x4949, 0x4949, 0x4949, 0 },
+ { 0x4a4a, 0x4a4a, 0x4a4a, 0 },
+ { 0x4b4b, 0x4b4b, 0x4b4b, 0 },
+ { 0x4c4c, 0x4c4c, 0x4c4c, 0 },
+ { 0x4d4d, 0x4d4d, 0x4d4d, 0 },
+ { 0x4e4e, 0x4e4e, 0x4e4e, 0 },
+ { 0x4f4f, 0x4f4f, 0x4f4f, 0 },
+ { 0x5050, 0x5050, 0x5050, 0 },
+ { 0x5151, 0x5151, 0x5151, 0 },
+ { 0x5252, 0x5252, 0x5252, 0 },
+ { 0x5353, 0x5353, 0x5353, 0 },
+ { 0x5454, 0x5454, 0x5454, 0 },
+ { 0x5555, 0x5555, 0x5555, 0 },
+ { 0x5656, 0x5656, 0x5656, 0 },
+ { 0x5757, 0x5757, 0x5757, 0 },
+ { 0x5858, 0x5858, 0x5858, 0 },
+ { 0x5959, 0x5959, 0x5959, 0 },
+ { 0x5a5a, 0x5a5a, 0x5a5a, 0 },
+ { 0x5b5b, 0x5b5b, 0x5b5b, 0 },
+ { 0x5c5c, 0x5c5c, 0x5c5c, 0 },
+ { 0x5d5d, 0x5d5d, 0x5d5d, 0 },
+ { 0x5e5e, 0x5e5e, 0x5e5e, 0 },
+ { 0x5f5f, 0x5f5f, 0x5f5f, 0 },
+ { 0x6060, 0x6060, 0x6060, 0 },
+ { 0x6161, 0x6161, 0x6161, 0 },
+ { 0x6262, 0x6262, 0x6262, 0 },
+ { 0x6363, 0x6363, 0x6363, 0 },
+ { 0x6464, 0x6464, 0x6464, 0 },
+ { 0x6565, 0x6565, 0x6565, 0 },
+ { 0x6666, 0x6666, 0x6666, 0 },
+ { 0x6767, 0x6767, 0x6767, 0 },
+ { 0x6868, 0x6868, 0x6868, 0 },
+ { 0x6969, 0x6969, 0x6969, 0 },
+ { 0x6a6a, 0x6a6a, 0x6a6a, 0 },
+ { 0x6b6b, 0x6b6b, 0x6b6b, 0 },
+ { 0x6c6c, 0x6c6c, 0x6c6c, 0 },
+ { 0x6d6d, 0x6d6d, 0x6d6d, 0 },
+ { 0x6e6e, 0x6e6e, 0x6e6e, 0 },
+ { 0x6f6f, 0x6f6f, 0x6f6f, 0 },
+ { 0x7070, 0x7070, 0x7070, 0 },
+ { 0x7171, 0x7171, 0x7171, 0 },
+ { 0x7272, 0x7272, 0x7272, 0 },
+ { 0x7373, 0x7373, 0x7373, 0 },
+ { 0x7474, 0x7474, 0x7474, 0 },
+ { 0x7575, 0x7575, 0x7575, 0 },
+ { 0x7676, 0x7676, 0x7676, 0 },
+ { 0x7777, 0x7777, 0x7777, 0 },
+ { 0x7878, 0x7878, 0x7878, 0 },
+ { 0x7979, 0x7979, 0x7979, 0 },
+ { 0x7a7a, 0x7a7a, 0x7a7a, 0 },
+ { 0x7b7b, 0x7b7b, 0x7b7b, 0 },
+ { 0x7c7c, 0x7c7c, 0x7c7c, 0 },
+ { 0x7d7d, 0x7d7d, 0x7d7d, 0 },
+ { 0x7e7e, 0x7e7e, 0x7e7e, 0 },
+ { 0x7f7f, 0x7f7f, 0x7f7f, 0 },
+ { 0x8080, 0x8080, 0x8080, 0 },
+ { 0x8181, 0x8181, 0x8181, 0 },
+ { 0x8282, 0x8282, 0x8282, 0 },
+ { 0x8383, 0x8383, 0x8383, 0 },
+ { 0x8484, 0x8484, 0x8484, 0 },
+ { 0x8585, 0x8585, 0x8585, 0 },
+ { 0x8686, 0x8686, 0x8686, 0 },
+ { 0x8787, 0x8787, 0x8787, 0 },
+ { 0x8888, 0x8888, 0x8888, 0 },
+ { 0x8989, 0x8989, 0x8989, 0 },
+ { 0x8a8a, 0x8a8a, 0x8a8a, 0 },
+ { 0x8b8b, 0x8b8b, 0x8b8b, 0 },
+ { 0x8c8c, 0x8c8c, 0x8c8c, 0 },
+ { 0x8d8d, 0x8d8d, 0x8d8d, 0 },
+ { 0x8e8e, 0x8e8e, 0x8e8e, 0 },
+ { 0x8f8f, 0x8f8f, 0x8f8f, 0 },
+ { 0x9090, 0x9090, 0x9090, 0 },
+ { 0x9191, 0x9191, 0x9191, 0 },
+ { 0x9292, 0x9292, 0x9292, 0 },
+ { 0x9393, 0x9393, 0x9393, 0 },
+ { 0x9494, 0x9494, 0x9494, 0 },
+ { 0x9595, 0x9595, 0x9595, 0 },
+ { 0x9696, 0x9696, 0x9696, 0 },
+ { 0x9797, 0x9797, 0x9797, 0 },
+ { 0x9898, 0x9898, 0x9898, 0 },
+ { 0x9999, 0x9999, 0x9999, 0 },
+ { 0x9a9a, 0x9a9a, 0x9a9a, 0 },
+ { 0x9b9b, 0x9b9b, 0x9b9b, 0 },
+ { 0x9c9c, 0x9c9c, 0x9c9c, 0 },
+ { 0x9d9d, 0x9d9d, 0x9d9d, 0 },
+ { 0x9e9e, 0x9e9e, 0x9e9e, 0 },
+ { 0x9f9f, 0x9f9f, 0x9f9f, 0 },
+ { 0xa0a0, 0xa0a0, 0xa0a0, 0 },
+ { 0xa1a1, 0xa1a1, 0xa1a1, 0 },
+ { 0xa2a2, 0xa2a2, 0xa2a2, 0 },
+ { 0xa3a3, 0xa3a3, 0xa3a3, 0 },
+ { 0xa4a4, 0xa4a4, 0xa4a4, 0 },
+ { 0xa5a5, 0xa5a5, 0xa5a5, 0 },
+ { 0xa6a6, 0xa6a6, 0xa6a6, 0 },
+ { 0xa7a7, 0xa7a7, 0xa7a7, 0 },
+ { 0xa8a8, 0xa8a8, 0xa8a8, 0 },
+ { 0xa9a9, 0xa9a9, 0xa9a9, 0 },
+ { 0xaaaa, 0xaaaa, 0xaaaa, 0 },
+ { 0xabab, 0xabab, 0xabab, 0 },
+ { 0xacac, 0xacac, 0xacac, 0 },
+ { 0xadad, 0xadad, 0xadad, 0 },
+ { 0xaeae, 0xaeae, 0xaeae, 0 },
+ { 0xafaf, 0xafaf, 0xafaf, 0 },
+ { 0xb0b0, 0xb0b0, 0xb0b0, 0 },
+ { 0xb1b1, 0xb1b1, 0xb1b1, 0 },
+ { 0xb2b2, 0xb2b2, 0xb2b2, 0 },
+ { 0xb3b3, 0xb3b3, 0xb3b3, 0 },
+ { 0xb4b4, 0xb4b4, 0xb4b4, 0 },
+ { 0xb5b5, 0xb5b5, 0xb5b5, 0 },
+ { 0xb6b6, 0xb6b6, 0xb6b6, 0 },
+ { 0xb7b7, 0xb7b7, 0xb7b7, 0 },
+ { 0xb8b8, 0xb8b8, 0xb8b8, 0 },
+ { 0xb9b9, 0xb9b9, 0xb9b9, 0 },
+ { 0xbaba, 0xbaba, 0xbaba, 0 },
+ { 0xbbbb, 0xbbbb, 0xbbbb, 0 },
+ { 0xbcbc, 0xbcbc, 0xbcbc, 0 },
+ { 0xbdbd, 0xbdbd, 0xbdbd, 0 },
+ { 0xbebe, 0xbebe, 0xbebe, 0 },
+ { 0xbfbf, 0xbfbf, 0xbfbf, 0 },
+ { 0xc0c0, 0xc0c0, 0xc0c0, 0 },
+ { 0xc1c1, 0xc1c1, 0xc1c1, 0 },
+ { 0xc2c2, 0xc2c2, 0xc2c2, 0 },
+ { 0xc3c3, 0xc3c3, 0xc3c3, 0 },
+ { 0xc4c4, 0xc4c4, 0xc4c4, 0 },
+ { 0xc5c5, 0xc5c5, 0xc5c5, 0 },
+ { 0xc6c6, 0xc6c6, 0xc6c6, 0 },
+ { 0xc7c7, 0xc7c7, 0xc7c7, 0 },
+ { 0xc8c8, 0xc8c8, 0xc8c8, 0 },
+ { 0xc9c9, 0xc9c9, 0xc9c9, 0 },
+ { 0xcaca, 0xcaca, 0xcaca, 0 },
+ { 0xcbcb, 0xcbcb, 0xcbcb, 0 },
+ { 0xcccc, 0xcccc, 0xcccc, 0 },
+ { 0xcdcd, 0xcdcd, 0xcdcd, 0 },
+ { 0xcece, 0xcece, 0xcece, 0 },
+ { 0xcfcf, 0xcfcf, 0xcfcf, 0 },
+ { 0xd0d0, 0xd0d0, 0xd0d0, 0 },
+ { 0xd1d1, 0xd1d1, 0xd1d1, 0 },
+ { 0xd2d2, 0xd2d2, 0xd2d2, 0 },
+ { 0xd3d3, 0xd3d3, 0xd3d3, 0 },
+ { 0xd4d4, 0xd4d4, 0xd4d4, 0 },
+ { 0xd5d5, 0xd5d5, 0xd5d5, 0 },
+ { 0xd6d6, 0xd6d6, 0xd6d6, 0 },
+ { 0xd7d7, 0xd7d7, 0xd7d7, 0 },
+ { 0xd8d8, 0xd8d8, 0xd8d8, 0 },
+ { 0xd9d9, 0xd9d9, 0xd9d9, 0 },
+ { 0xdada, 0xdada, 0xdada, 0 },
+ { 0xdbdb, 0xdbdb, 0xdbdb, 0 },
+ { 0xdcdc, 0xdcdc, 0xdcdc, 0 },
+ { 0xdddd, 0xdddd, 0xdddd, 0 },
+ { 0xdede, 0xdede, 0xdede, 0 },
+ { 0xdfdf, 0xdfdf, 0xdfdf, 0 },
+ { 0xe0e0, 0xe0e0, 0xe0e0, 0 },
+ { 0xe1e1, 0xe1e1, 0xe1e1, 0 },
+ { 0xe2e2, 0xe2e2, 0xe2e2, 0 },
+ { 0xe3e3, 0xe3e3, 0xe3e3, 0 },
+ { 0xe4e4, 0xe4e4, 0xe4e4, 0 },
+ { 0xe5e5, 0xe5e5, 0xe5e5, 0 },
+ { 0xe6e6, 0xe6e6, 0xe6e6, 0 },
+ { 0xe7e7, 0xe7e7, 0xe7e7, 0 },
+ { 0xe8e8, 0xe8e8, 0xe8e8, 0 },
+ { 0xe9e9, 0xe9e9, 0xe9e9, 0 },
+ { 0xeaea, 0xeaea, 0xeaea, 0 },
+ { 0xebeb, 0xebeb, 0xebeb, 0 },
+ { 0xecec, 0xecec, 0xecec, 0 },
+ { 0xeded, 0xeded, 0xeded, 0 },
+ { 0xeeee, 0xeeee, 0xeeee, 0 },
+ { 0xefef, 0xefef, 0xefef, 0 },
+ { 0xf0f0, 0xf0f0, 0xf0f0, 0 },
+ { 0xf1f1, 0xf1f1, 0xf1f1, 0 },
+ { 0xf2f2, 0xf2f2, 0xf2f2, 0 },
+ { 0xf3f3, 0xf3f3, 0xf3f3, 0 },
+ { 0xf4f4, 0xf4f4, 0xf4f4, 0 },
+ { 0xf5f5, 0xf5f5, 0xf5f5, 0 },
+ { 0xf6f6, 0xf6f6, 0xf6f6, 0 },
+ { 0xf7f7, 0xf7f7, 0xf7f7, 0 },
+ { 0xf8f8, 0xf8f8, 0xf8f8, 0 },
+ { 0xf9f9, 0xf9f9, 0xf9f9, 0 },
+ { 0xfafa, 0xfafa, 0xfafa, 0 },
+ { 0xfbfb, 0xfbfb, 0xfbfb, 0 },
+ { 0xfcfc, 0xfcfc, 0xfcfc, 0 },
+ { 0xfdfd, 0xfdfd, 0xfdfd, 0 },
+ { 0xfefe, 0xfefe, 0xfefe, 0 },
+ { 0xffff, 0xffff, 0xffff, 0 },
+};
+
+const struct vkms_color_lut linear_eotf = {
+ .base = linear_array,
+ .lut_length = LUT_SIZE,
+ .channel_value2index_ratio = 0xff00ffll
+};
+EXPORT_SYMBOL(linear_eotf);
+
+static struct drm_color_lut srgb_array[LUT_SIZE] = {
+ { 0x0, 0x0, 0x0, 0 },
+ { 0x13, 0x13, 0x13, 0 },
+ { 0x27, 0x27, 0x27, 0 },
+ { 0x3b, 0x3b, 0x3b, 0 },
+ { 0x4f, 0x4f, 0x4f, 0 },
+ { 0x63, 0x63, 0x63, 0 },
+ { 0x77, 0x77, 0x77, 0 },
+ { 0x8b, 0x8b, 0x8b, 0 },
+ { 0x9f, 0x9f, 0x9f, 0 },
+ { 0xb3, 0xb3, 0xb3, 0 },
+ { 0xc6, 0xc6, 0xc6, 0 },
+ { 0xdb, 0xdb, 0xdb, 0 },
+ { 0xf0, 0xf0, 0xf0, 0 },
+ { 0x107, 0x107, 0x107, 0 },
+ { 0x11f, 0x11f, 0x11f, 0 },
+ { 0x139, 0x139, 0x139, 0 },
+ { 0x153, 0x153, 0x153, 0 },
+ { 0x16f, 0x16f, 0x16f, 0 },
+ { 0x18c, 0x18c, 0x18c, 0 },
+ { 0x1aa, 0x1aa, 0x1aa, 0 },
+ { 0x1ca, 0x1ca, 0x1ca, 0 },
+ { 0x1eb, 0x1eb, 0x1eb, 0 },
+ { 0x20d, 0x20d, 0x20d, 0 },
+ { 0x231, 0x231, 0x231, 0 },
+ { 0x256, 0x256, 0x256, 0 },
+ { 0x27d, 0x27d, 0x27d, 0 },
+ { 0x2a4, 0x2a4, 0x2a4, 0 },
+ { 0x2ce, 0x2ce, 0x2ce, 0 },
+ { 0x2f9, 0x2f9, 0x2f9, 0 },
+ { 0x325, 0x325, 0x325, 0 },
+ { 0x352, 0x352, 0x352, 0 },
+ { 0x381, 0x381, 0x381, 0 },
+ { 0x3b2, 0x3b2, 0x3b2, 0 },
+ { 0x3e4, 0x3e4, 0x3e4, 0 },
+ { 0x418, 0x418, 0x418, 0 },
+ { 0x44d, 0x44d, 0x44d, 0 },
+ { 0x484, 0x484, 0x484, 0 },
+ { 0x4bc, 0x4bc, 0x4bc, 0 },
+ { 0x4f6, 0x4f6, 0x4f6, 0 },
+ { 0x531, 0x531, 0x531, 0 },
+ { 0x56e, 0x56e, 0x56e, 0 },
+ { 0x5ad, 0x5ad, 0x5ad, 0 },
+ { 0x5ed, 0x5ed, 0x5ed, 0 },
+ { 0x62f, 0x62f, 0x62f, 0 },
+ { 0x672, 0x672, 0x672, 0 },
+ { 0x6b7, 0x6b7, 0x6b7, 0 },
+ { 0x6fe, 0x6fe, 0x6fe, 0 },
+ { 0x746, 0x746, 0x746, 0 },
+ { 0x791, 0x791, 0x791, 0 },
+ { 0x7dc, 0x7dc, 0x7dc, 0 },
+ { 0x82a, 0x82a, 0x82a, 0 },
+ { 0x879, 0x879, 0x879, 0 },
+ { 0x8ca, 0x8ca, 0x8ca, 0 },
+ { 0x91d, 0x91d, 0x91d, 0 },
+ { 0x971, 0x971, 0x971, 0 },
+ { 0x9c7, 0x9c7, 0x9c7, 0 },
+ { 0xa1f, 0xa1f, 0xa1f, 0 },
+ { 0xa79, 0xa79, 0xa79, 0 },
+ { 0xad4, 0xad4, 0xad4, 0 },
+ { 0xb32, 0xb32, 0xb32, 0 },
+ { 0xb91, 0xb91, 0xb91, 0 },
+ { 0xbf2, 0xbf2, 0xbf2, 0 },
+ { 0xc54, 0xc54, 0xc54, 0 },
+ { 0xcb9, 0xcb9, 0xcb9, 0 },
+ { 0xd1f, 0xd1f, 0xd1f, 0 },
+ { 0xd88, 0xd88, 0xd88, 0 },
+ { 0xdf2, 0xdf2, 0xdf2, 0 },
+ { 0xe5e, 0xe5e, 0xe5e, 0 },
+ { 0xecc, 0xecc, 0xecc, 0 },
+ { 0xf3c, 0xf3c, 0xf3c, 0 },
+ { 0xfad, 0xfad, 0xfad, 0 },
+ { 0x1021, 0x1021, 0x1021, 0 },
+ { 0x1096, 0x1096, 0x1096, 0 },
+ { 0x110e, 0x110e, 0x110e, 0 },
+ { 0x1187, 0x1187, 0x1187, 0 },
+ { 0x1203, 0x1203, 0x1203, 0 },
+ { 0x1280, 0x1280, 0x1280, 0 },
+ { 0x12ff, 0x12ff, 0x12ff, 0 },
+ { 0x1380, 0x1380, 0x1380, 0 },
+ { 0x1404, 0x1404, 0x1404, 0 },
+ { 0x1489, 0x1489, 0x1489, 0 },
+ { 0x1510, 0x1510, 0x1510, 0 },
+ { 0x1599, 0x1599, 0x1599, 0 },
+ { 0x1624, 0x1624, 0x1624, 0 },
+ { 0x16b2, 0x16b2, 0x16b2, 0 },
+ { 0x1741, 0x1741, 0x1741, 0 },
+ { 0x17d2, 0x17d2, 0x17d2, 0 },
+ { 0x1865, 0x1865, 0x1865, 0 },
+ { 0x18fb, 0x18fb, 0x18fb, 0 },
+ { 0x1992, 0x1992, 0x1992, 0 },
+ { 0x1a2c, 0x1a2c, 0x1a2c, 0 },
+ { 0x1ac8, 0x1ac8, 0x1ac8, 0 },
+ { 0x1b65, 0x1b65, 0x1b65, 0 },
+ { 0x1c05, 0x1c05, 0x1c05, 0 },
+ { 0x1ca7, 0x1ca7, 0x1ca7, 0 },
+ { 0x1d4b, 0x1d4b, 0x1d4b, 0 },
+ { 0x1df1, 0x1df1, 0x1df1, 0 },
+ { 0x1e99, 0x1e99, 0x1e99, 0 },
+ { 0x1f44, 0x1f44, 0x1f44, 0 },
+ { 0x1ff0, 0x1ff0, 0x1ff0, 0 },
+ { 0x209f, 0x209f, 0x209f, 0 },
+ { 0x2150, 0x2150, 0x2150, 0 },
+ { 0x2203, 0x2203, 0x2203, 0 },
+ { 0x22b8, 0x22b8, 0x22b8, 0 },
+ { 0x2370, 0x2370, 0x2370, 0 },
+ { 0x2429, 0x2429, 0x2429, 0 },
+ { 0x24e5, 0x24e5, 0x24e5, 0 },
+ { 0x25a3, 0x25a3, 0x25a3, 0 },
+ { 0x2663, 0x2663, 0x2663, 0 },
+ { 0x2726, 0x2726, 0x2726, 0 },
+ { 0x27ea, 0x27ea, 0x27ea, 0 },
+ { 0x28b1, 0x28b1, 0x28b1, 0 },
+ { 0x297a, 0x297a, 0x297a, 0 },
+ { 0x2a45, 0x2a45, 0x2a45, 0 },
+ { 0x2b13, 0x2b13, 0x2b13, 0 },
+ { 0x2be3, 0x2be3, 0x2be3, 0 },
+ { 0x2cb5, 0x2cb5, 0x2cb5, 0 },
+ { 0x2d89, 0x2d89, 0x2d89, 0 },
+ { 0x2e60, 0x2e60, 0x2e60, 0 },
+ { 0x2f39, 0x2f39, 0x2f39, 0 },
+ { 0x3014, 0x3014, 0x3014, 0 },
+ { 0x30f2, 0x30f2, 0x30f2, 0 },
+ { 0x31d2, 0x31d2, 0x31d2, 0 },
+ { 0x32b4, 0x32b4, 0x32b4, 0 },
+ { 0x3398, 0x3398, 0x3398, 0 },
+ { 0x347f, 0x347f, 0x347f, 0 },
+ { 0x3569, 0x3569, 0x3569, 0 },
+ { 0x3654, 0x3654, 0x3654, 0 },
+ { 0x3742, 0x3742, 0x3742, 0 },
+ { 0x3832, 0x3832, 0x3832, 0 },
+ { 0x3925, 0x3925, 0x3925, 0 },
+ { 0x3a1a, 0x3a1a, 0x3a1a, 0 },
+ { 0x3b11, 0x3b11, 0x3b11, 0 },
+ { 0x3c0b, 0x3c0b, 0x3c0b, 0 },
+ { 0x3d07, 0x3d07, 0x3d07, 0 },
+ { 0x3e05, 0x3e05, 0x3e05, 0 },
+ { 0x3f06, 0x3f06, 0x3f06, 0 },
+ { 0x400a, 0x400a, 0x400a, 0 },
+ { 0x410f, 0x410f, 0x410f, 0 },
+ { 0x4218, 0x4218, 0x4218, 0 },
+ { 0x4322, 0x4322, 0x4322, 0 },
+ { 0x442f, 0x442f, 0x442f, 0 },
+ { 0x453f, 0x453f, 0x453f, 0 },
+ { 0x4650, 0x4650, 0x4650, 0 },
+ { 0x4765, 0x4765, 0x4765, 0 },
+ { 0x487c, 0x487c, 0x487c, 0 },
+ { 0x4995, 0x4995, 0x4995, 0 },
+ { 0x4ab1, 0x4ab1, 0x4ab1, 0 },
+ { 0x4bcf, 0x4bcf, 0x4bcf, 0 },
+ { 0x4cf0, 0x4cf0, 0x4cf0, 0 },
+ { 0x4e13, 0x4e13, 0x4e13, 0 },
+ { 0x4f39, 0x4f39, 0x4f39, 0 },
+ { 0x5061, 0x5061, 0x5061, 0 },
+ { 0x518b, 0x518b, 0x518b, 0 },
+ { 0x52b9, 0x52b9, 0x52b9, 0 },
+ { 0x53e8, 0x53e8, 0x53e8, 0 },
+ { 0x551b, 0x551b, 0x551b, 0 },
+ { 0x5650, 0x5650, 0x5650, 0 },
+ { 0x5787, 0x5787, 0x5787, 0 },
+ { 0x58c1, 0x58c1, 0x58c1, 0 },
+ { 0x59fd, 0x59fd, 0x59fd, 0 },
+ { 0x5b3c, 0x5b3c, 0x5b3c, 0 },
+ { 0x5c7e, 0x5c7e, 0x5c7e, 0 },
+ { 0x5dc2, 0x5dc2, 0x5dc2, 0 },
+ { 0x5f09, 0x5f09, 0x5f09, 0 },
+ { 0x6052, 0x6052, 0x6052, 0 },
+ { 0x619e, 0x619e, 0x619e, 0 },
+ { 0x62ec, 0x62ec, 0x62ec, 0 },
+ { 0x643d, 0x643d, 0x643d, 0 },
+ { 0x6591, 0x6591, 0x6591, 0 },
+ { 0x66e7, 0x66e7, 0x66e7, 0 },
+ { 0x6840, 0x6840, 0x6840, 0 },
+ { 0x699b, 0x699b, 0x699b, 0 },
+ { 0x6afa, 0x6afa, 0x6afa, 0 },
+ { 0x6c5a, 0x6c5a, 0x6c5a, 0 },
+ { 0x6dbe, 0x6dbe, 0x6dbe, 0 },
+ { 0x6f24, 0x6f24, 0x6f24, 0 },
+ { 0x708c, 0x708c, 0x708c, 0 },
+ { 0x71f8, 0x71f8, 0x71f8, 0 },
+ { 0x7366, 0x7366, 0x7366, 0 },
+ { 0x74d6, 0x74d6, 0x74d6, 0 },
+ { 0x764a, 0x764a, 0x764a, 0 },
+ { 0x77c0, 0x77c0, 0x77c0, 0 },
+ { 0x7938, 0x7938, 0x7938, 0 },
+ { 0x7ab4, 0x7ab4, 0x7ab4, 0 },
+ { 0x7c32, 0x7c32, 0x7c32, 0 },
+ { 0x7db3, 0x7db3, 0x7db3, 0 },
+ { 0x7f36, 0x7f36, 0x7f36, 0 },
+ { 0x80bc, 0x80bc, 0x80bc, 0 },
+ { 0x8245, 0x8245, 0x8245, 0 },
+ { 0x83d1, 0x83d1, 0x83d1, 0 },
+ { 0x855f, 0x855f, 0x855f, 0 },
+ { 0x86f0, 0x86f0, 0x86f0, 0 },
+ { 0x8884, 0x8884, 0x8884, 0 },
+ { 0x8a1a, 0x8a1a, 0x8a1a, 0 },
+ { 0x8bb4, 0x8bb4, 0x8bb4, 0 },
+ { 0x8d50, 0x8d50, 0x8d50, 0 },
+ { 0x8eee, 0x8eee, 0x8eee, 0 },
+ { 0x9090, 0x9090, 0x9090, 0 },
+ { 0x9234, 0x9234, 0x9234, 0 },
+ { 0x93db, 0x93db, 0x93db, 0 },
+ { 0x9585, 0x9585, 0x9585, 0 },
+ { 0x9732, 0x9732, 0x9732, 0 },
+ { 0x98e1, 0x98e1, 0x98e1, 0 },
+ { 0x9a93, 0x9a93, 0x9a93, 0 },
+ { 0x9c48, 0x9c48, 0x9c48, 0 },
+ { 0x9e00, 0x9e00, 0x9e00, 0 },
+ { 0x9fbb, 0x9fbb, 0x9fbb, 0 },
+ { 0xa178, 0xa178, 0xa178, 0 },
+ { 0xa338, 0xa338, 0xa338, 0 },
+ { 0xa4fb, 0xa4fb, 0xa4fb, 0 },
+ { 0xa6c1, 0xa6c1, 0xa6c1, 0 },
+ { 0xa88a, 0xa88a, 0xa88a, 0 },
+ { 0xaa56, 0xaa56, 0xaa56, 0 },
+ { 0xac24, 0xac24, 0xac24, 0 },
+ { 0xadf5, 0xadf5, 0xadf5, 0 },
+ { 0xafc9, 0xafc9, 0xafc9, 0 },
+ { 0xb1a0, 0xb1a0, 0xb1a0, 0 },
+ { 0xb37a, 0xb37a, 0xb37a, 0 },
+ { 0xb557, 0xb557, 0xb557, 0 },
+ { 0xb736, 0xb736, 0xb736, 0 },
+ { 0xb919, 0xb919, 0xb919, 0 },
+ { 0xbafe, 0xbafe, 0xbafe, 0 },
+ { 0xbce6, 0xbce6, 0xbce6, 0 },
+ { 0xbed2, 0xbed2, 0xbed2, 0 },
+ { 0xc0c0, 0xc0c0, 0xc0c0, 0 },
+ { 0xc2b0, 0xc2b0, 0xc2b0, 0 },
+ { 0xc4a4, 0xc4a4, 0xc4a4, 0 },
+ { 0xc69b, 0xc69b, 0xc69b, 0 },
+ { 0xc895, 0xc895, 0xc895, 0 },
+ { 0xca91, 0xca91, 0xca91, 0 },
+ { 0xcc91, 0xcc91, 0xcc91, 0 },
+ { 0xce93, 0xce93, 0xce93, 0 },
+ { 0xd098, 0xd098, 0xd098, 0 },
+ { 0xd2a1, 0xd2a1, 0xd2a1, 0 },
+ { 0xd4ac, 0xd4ac, 0xd4ac, 0 },
+ { 0xd6ba, 0xd6ba, 0xd6ba, 0 },
+ { 0xd8cb, 0xd8cb, 0xd8cb, 0 },
+ { 0xdadf, 0xdadf, 0xdadf, 0 },
+ { 0xdcf7, 0xdcf7, 0xdcf7, 0 },
+ { 0xdf11, 0xdf11, 0xdf11, 0 },
+ { 0xe12e, 0xe12e, 0xe12e, 0 },
+ { 0xe34e, 0xe34e, 0xe34e, 0 },
+ { 0xe571, 0xe571, 0xe571, 0 },
+ { 0xe796, 0xe796, 0xe796, 0 },
+ { 0xe9bf, 0xe9bf, 0xe9bf, 0 },
+ { 0xebeb, 0xebeb, 0xebeb, 0 },
+ { 0xee1a, 0xee1a, 0xee1a, 0 },
+ { 0xf04c, 0xf04c, 0xf04c, 0 },
+ { 0xf281, 0xf281, 0xf281, 0 },
+ { 0xf4b9, 0xf4b9, 0xf4b9, 0 },
+ { 0xf6f4, 0xf6f4, 0xf6f4, 0 },
+ { 0xf932, 0xf932, 0xf932, 0 },
+ { 0xfb73, 0xfb73, 0xfb73, 0 },
+ { 0xfdb7, 0xfdb7, 0xfdb7, 0 },
+ { 0xffff, 0xffff, 0xffff, 0 },
+};
+
+const struct vkms_color_lut srgb_eotf = {
+ .base = srgb_array,
+ .lut_length = LUT_SIZE,
+ .channel_value2index_ratio = 0xff00ffll
+};
+EXPORT_SYMBOL(srgb_eotf);
+
+static struct drm_color_lut srgb_inv_array[LUT_SIZE] = {
+ { 0x0, 0x0, 0x0, 0 },
+ { 0xcc2, 0xcc2, 0xcc2, 0 },
+ { 0x15be, 0x15be, 0x15be, 0 },
+ { 0x1c56, 0x1c56, 0x1c56, 0 },
+ { 0x21bd, 0x21bd, 0x21bd, 0 },
+ { 0x2666, 0x2666, 0x2666, 0 },
+ { 0x2a8a, 0x2a8a, 0x2a8a, 0 },
+ { 0x2e4c, 0x2e4c, 0x2e4c, 0 },
+ { 0x31c0, 0x31c0, 0x31c0, 0 },
+ { 0x34f6, 0x34f6, 0x34f6, 0 },
+ { 0x37f9, 0x37f9, 0x37f9, 0 },
+ { 0x3acf, 0x3acf, 0x3acf, 0 },
+ { 0x3d80, 0x3d80, 0x3d80, 0 },
+ { 0x4010, 0x4010, 0x4010, 0 },
+ { 0x4284, 0x4284, 0x4284, 0 },
+ { 0x44dd, 0x44dd, 0x44dd, 0 },
+ { 0x4720, 0x4720, 0x4720, 0 },
+ { 0x494e, 0x494e, 0x494e, 0 },
+ { 0x4b69, 0x4b69, 0x4b69, 0 },
+ { 0x4d73, 0x4d73, 0x4d73, 0 },
+ { 0x4f6e, 0x4f6e, 0x4f6e, 0 },
+ { 0x5159, 0x5159, 0x5159, 0 },
+ { 0x5337, 0x5337, 0x5337, 0 },
+ { 0x5509, 0x5509, 0x5509, 0 },
+ { 0x56cf, 0x56cf, 0x56cf, 0 },
+ { 0x588a, 0x588a, 0x588a, 0 },
+ { 0x5a3b, 0x5a3b, 0x5a3b, 0 },
+ { 0x5be2, 0x5be2, 0x5be2, 0 },
+ { 0x5d80, 0x5d80, 0x5d80, 0 },
+ { 0x5f16, 0x5f16, 0x5f16, 0 },
+ { 0x60a4, 0x60a4, 0x60a4, 0 },
+ { 0x6229, 0x6229, 0x6229, 0 },
+ { 0x63a8, 0x63a8, 0x63a8, 0 },
+ { 0x6520, 0x6520, 0x6520, 0 },
+ { 0x6691, 0x6691, 0x6691, 0 },
+ { 0x67fc, 0x67fc, 0x67fc, 0 },
+ { 0x6961, 0x6961, 0x6961, 0 },
+ { 0x6ac0, 0x6ac0, 0x6ac0, 0 },
+ { 0x6c19, 0x6c19, 0x6c19, 0 },
+ { 0x6d6e, 0x6d6e, 0x6d6e, 0 },
+ { 0x6ebd, 0x6ebd, 0x6ebd, 0 },
+ { 0x7008, 0x7008, 0x7008, 0 },
+ { 0x714d, 0x714d, 0x714d, 0 },
+ { 0x728f, 0x728f, 0x728f, 0 },
+ { 0x73cc, 0x73cc, 0x73cc, 0 },
+ { 0x7504, 0x7504, 0x7504, 0 },
+ { 0x7639, 0x7639, 0x7639, 0 },
+ { 0x776a, 0x776a, 0x776a, 0 },
+ { 0x7897, 0x7897, 0x7897, 0 },
+ { 0x79c1, 0x79c1, 0x79c1, 0 },
+ { 0x7ae7, 0x7ae7, 0x7ae7, 0 },
+ { 0x7c09, 0x7c09, 0x7c09, 0 },
+ { 0x7d28, 0x7d28, 0x7d28, 0 },
+ { 0x7e44, 0x7e44, 0x7e44, 0 },
+ { 0x7f5d, 0x7f5d, 0x7f5d, 0 },
+ { 0x8073, 0x8073, 0x8073, 0 },
+ { 0x8186, 0x8186, 0x8186, 0 },
+ { 0x8296, 0x8296, 0x8296, 0 },
+ { 0x83a4, 0x83a4, 0x83a4, 0 },
+ { 0x84ae, 0x84ae, 0x84ae, 0 },
+ { 0x85b6, 0x85b6, 0x85b6, 0 },
+ { 0x86bc, 0x86bc, 0x86bc, 0 },
+ { 0x87bf, 0x87bf, 0x87bf, 0 },
+ { 0x88bf, 0x88bf, 0x88bf, 0 },
+ { 0x89be, 0x89be, 0x89be, 0 },
+ { 0x8ab9, 0x8ab9, 0x8ab9, 0 },
+ { 0x8bb3, 0x8bb3, 0x8bb3, 0 },
+ { 0x8cab, 0x8cab, 0x8cab, 0 },
+ { 0x8da0, 0x8da0, 0x8da0, 0 },
+ { 0x8e93, 0x8e93, 0x8e93, 0 },
+ { 0x8f84, 0x8f84, 0x8f84, 0 },
+ { 0x9073, 0x9073, 0x9073, 0 },
+ { 0x9161, 0x9161, 0x9161, 0 },
+ { 0x924c, 0x924c, 0x924c, 0 },
+ { 0x9335, 0x9335, 0x9335, 0 },
+ { 0x941d, 0x941d, 0x941d, 0 },
+ { 0x9503, 0x9503, 0x9503, 0 },
+ { 0x95e7, 0x95e7, 0x95e7, 0 },
+ { 0x96c9, 0x96c9, 0x96c9, 0 },
+ { 0x97aa, 0x97aa, 0x97aa, 0 },
+ { 0x9889, 0x9889, 0x9889, 0 },
+ { 0x9966, 0x9966, 0x9966, 0 },
+ { 0x9a42, 0x9a42, 0x9a42, 0 },
+ { 0x9b1c, 0x9b1c, 0x9b1c, 0 },
+ { 0x9bf5, 0x9bf5, 0x9bf5, 0 },
+ { 0x9ccc, 0x9ccc, 0x9ccc, 0 },
+ { 0x9da1, 0x9da1, 0x9da1, 0 },
+ { 0x9e76, 0x9e76, 0x9e76, 0 },
+ { 0x9f49, 0x9f49, 0x9f49, 0 },
+ { 0xa01a, 0xa01a, 0xa01a, 0 },
+ { 0xa0ea, 0xa0ea, 0xa0ea, 0 },
+ { 0xa1b9, 0xa1b9, 0xa1b9, 0 },
+ { 0xa286, 0xa286, 0xa286, 0 },
+ { 0xa352, 0xa352, 0xa352, 0 },
+ { 0xa41d, 0xa41d, 0xa41d, 0 },
+ { 0xa4e7, 0xa4e7, 0xa4e7, 0 },
+ { 0xa5af, 0xa5af, 0xa5af, 0 },
+ { 0xa676, 0xa676, 0xa676, 0 },
+ { 0xa73c, 0xa73c, 0xa73c, 0 },
+ { 0xa801, 0xa801, 0xa801, 0 },
+ { 0xa8c5, 0xa8c5, 0xa8c5, 0 },
+ { 0xa987, 0xa987, 0xa987, 0 },
+ { 0xaa48, 0xaa48, 0xaa48, 0 },
+ { 0xab09, 0xab09, 0xab09, 0 },
+ { 0xabc8, 0xabc8, 0xabc8, 0 },
+ { 0xac86, 0xac86, 0xac86, 0 },
+ { 0xad43, 0xad43, 0xad43, 0 },
+ { 0xadff, 0xadff, 0xadff, 0 },
+ { 0xaeba, 0xaeba, 0xaeba, 0 },
+ { 0xaf74, 0xaf74, 0xaf74, 0 },
+ { 0xb02d, 0xb02d, 0xb02d, 0 },
+ { 0xb0e5, 0xb0e5, 0xb0e5, 0 },
+ { 0xb19c, 0xb19c, 0xb19c, 0 },
+ { 0xb252, 0xb252, 0xb252, 0 },
+ { 0xb307, 0xb307, 0xb307, 0 },
+ { 0xb3bb, 0xb3bb, 0xb3bb, 0 },
+ { 0xb46f, 0xb46f, 0xb46f, 0 },
+ { 0xb521, 0xb521, 0xb521, 0 },
+ { 0xb5d3, 0xb5d3, 0xb5d3, 0 },
+ { 0xb683, 0xb683, 0xb683, 0 },
+ { 0xb733, 0xb733, 0xb733, 0 },
+ { 0xb7e2, 0xb7e2, 0xb7e2, 0 },
+ { 0xb890, 0xb890, 0xb890, 0 },
+ { 0xb93d, 0xb93d, 0xb93d, 0 },
+ { 0xb9ea, 0xb9ea, 0xb9ea, 0 },
+ { 0xba96, 0xba96, 0xba96, 0 },
+ { 0xbb40, 0xbb40, 0xbb40, 0 },
+ { 0xbbea, 0xbbea, 0xbbea, 0 },
+ { 0xbc94, 0xbc94, 0xbc94, 0 },
+ { 0xbd3c, 0xbd3c, 0xbd3c, 0 },
+ { 0xbde4, 0xbde4, 0xbde4, 0 },
+ { 0xbe8b, 0xbe8b, 0xbe8b, 0 },
+ { 0xbf31, 0xbf31, 0xbf31, 0 },
+ { 0xbfd7, 0xbfd7, 0xbfd7, 0 },
+ { 0xc07b, 0xc07b, 0xc07b, 0 },
+ { 0xc120, 0xc120, 0xc120, 0 },
+ { 0xc1c3, 0xc1c3, 0xc1c3, 0 },
+ { 0xc266, 0xc266, 0xc266, 0 },
+ { 0xc308, 0xc308, 0xc308, 0 },
+ { 0xc3a9, 0xc3a9, 0xc3a9, 0 },
+ { 0xc449, 0xc449, 0xc449, 0 },
+ { 0xc4e9, 0xc4e9, 0xc4e9, 0 },
+ { 0xc589, 0xc589, 0xc589, 0 },
+ { 0xc627, 0xc627, 0xc627, 0 },
+ { 0xc6c5, 0xc6c5, 0xc6c5, 0 },
+ { 0xc763, 0xc763, 0xc763, 0 },
+ { 0xc7ff, 0xc7ff, 0xc7ff, 0 },
+ { 0xc89b, 0xc89b, 0xc89b, 0 },
+ { 0xc937, 0xc937, 0xc937, 0 },
+ { 0xc9d2, 0xc9d2, 0xc9d2, 0 },
+ { 0xca6c, 0xca6c, 0xca6c, 0 },
+ { 0xcb06, 0xcb06, 0xcb06, 0 },
+ { 0xcb9f, 0xcb9f, 0xcb9f, 0 },
+ { 0xcc37, 0xcc37, 0xcc37, 0 },
+ { 0xcccf, 0xcccf, 0xcccf, 0 },
+ { 0xcd66, 0xcd66, 0xcd66, 0 },
+ { 0xcdfd, 0xcdfd, 0xcdfd, 0 },
+ { 0xce93, 0xce93, 0xce93, 0 },
+ { 0xcf29, 0xcf29, 0xcf29, 0 },
+ { 0xcfbe, 0xcfbe, 0xcfbe, 0 },
+ { 0xd053, 0xd053, 0xd053, 0 },
+ { 0xd0e7, 0xd0e7, 0xd0e7, 0 },
+ { 0xd17a, 0xd17a, 0xd17a, 0 },
+ { 0xd20d, 0xd20d, 0xd20d, 0 },
+ { 0xd2a0, 0xd2a0, 0xd2a0, 0 },
+ { 0xd331, 0xd331, 0xd331, 0 },
+ { 0xd3c3, 0xd3c3, 0xd3c3, 0 },
+ { 0xd454, 0xd454, 0xd454, 0 },
+ { 0xd4e4, 0xd4e4, 0xd4e4, 0 },
+ { 0xd574, 0xd574, 0xd574, 0 },
+ { 0xd603, 0xd603, 0xd603, 0 },
+ { 0xd692, 0xd692, 0xd692, 0 },
+ { 0xd720, 0xd720, 0xd720, 0 },
+ { 0xd7ae, 0xd7ae, 0xd7ae, 0 },
+ { 0xd83c, 0xd83c, 0xd83c, 0 },
+ { 0xd8c9, 0xd8c9, 0xd8c9, 0 },
+ { 0xd955, 0xd955, 0xd955, 0 },
+ { 0xd9e1, 0xd9e1, 0xd9e1, 0 },
+ { 0xda6d, 0xda6d, 0xda6d, 0 },
+ { 0xdaf8, 0xdaf8, 0xdaf8, 0 },
+ { 0xdb83, 0xdb83, 0xdb83, 0 },
+ { 0xdc0d, 0xdc0d, 0xdc0d, 0 },
+ { 0xdc97, 0xdc97, 0xdc97, 0 },
+ { 0xdd20, 0xdd20, 0xdd20, 0 },
+ { 0xdda9, 0xdda9, 0xdda9, 0 },
+ { 0xde31, 0xde31, 0xde31, 0 },
+ { 0xdeb9, 0xdeb9, 0xdeb9, 0 },
+ { 0xdf41, 0xdf41, 0xdf41, 0 },
+ { 0xdfc8, 0xdfc8, 0xdfc8, 0 },
+ { 0xe04f, 0xe04f, 0xe04f, 0 },
+ { 0xe0d5, 0xe0d5, 0xe0d5, 0 },
+ { 0xe15b, 0xe15b, 0xe15b, 0 },
+ { 0xe1e0, 0xe1e0, 0xe1e0, 0 },
+ { 0xe266, 0xe266, 0xe266, 0 },
+ { 0xe2ea, 0xe2ea, 0xe2ea, 0 },
+ { 0xe36f, 0xe36f, 0xe36f, 0 },
+ { 0xe3f3, 0xe3f3, 0xe3f3, 0 },
+ { 0xe476, 0xe476, 0xe476, 0 },
+ { 0xe4f9, 0xe4f9, 0xe4f9, 0 },
+ { 0xe57c, 0xe57c, 0xe57c, 0 },
+ { 0xe5fe, 0xe5fe, 0xe5fe, 0 },
+ { 0xe680, 0xe680, 0xe680, 0 },
+ { 0xe702, 0xe702, 0xe702, 0 },
+ { 0xe783, 0xe783, 0xe783, 0 },
+ { 0xe804, 0xe804, 0xe804, 0 },
+ { 0xe884, 0xe884, 0xe884, 0 },
+ { 0xe905, 0xe905, 0xe905, 0 },
+ { 0xe984, 0xe984, 0xe984, 0 },
+ { 0xea04, 0xea04, 0xea04, 0 },
+ { 0xea83, 0xea83, 0xea83, 0 },
+ { 0xeb02, 0xeb02, 0xeb02, 0 },
+ { 0xeb80, 0xeb80, 0xeb80, 0 },
+ { 0xebfe, 0xebfe, 0xebfe, 0 },
+ { 0xec7b, 0xec7b, 0xec7b, 0 },
+ { 0xecf9, 0xecf9, 0xecf9, 0 },
+ { 0xed76, 0xed76, 0xed76, 0 },
+ { 0xedf2, 0xedf2, 0xedf2, 0 },
+ { 0xee6f, 0xee6f, 0xee6f, 0 },
+ { 0xeeeb, 0xeeeb, 0xeeeb, 0 },
+ { 0xef66, 0xef66, 0xef66, 0 },
+ { 0xefe2, 0xefe2, 0xefe2, 0 },
+ { 0xf05d, 0xf05d, 0xf05d, 0 },
+ { 0xf0d7, 0xf0d7, 0xf0d7, 0 },
+ { 0xf152, 0xf152, 0xf152, 0 },
+ { 0xf1cc, 0xf1cc, 0xf1cc, 0 },
+ { 0xf245, 0xf245, 0xf245, 0 },
+ { 0xf2bf, 0xf2bf, 0xf2bf, 0 },
+ { 0xf338, 0xf338, 0xf338, 0 },
+ { 0xf3b0, 0xf3b0, 0xf3b0, 0 },
+ { 0xf429, 0xf429, 0xf429, 0 },
+ { 0xf4a1, 0xf4a1, 0xf4a1, 0 },
+ { 0xf519, 0xf519, 0xf519, 0 },
+ { 0xf590, 0xf590, 0xf590, 0 },
+ { 0xf608, 0xf608, 0xf608, 0 },
+ { 0xf67e, 0xf67e, 0xf67e, 0 },
+ { 0xf6f5, 0xf6f5, 0xf6f5, 0 },
+ { 0xf76b, 0xf76b, 0xf76b, 0 },
+ { 0xf7e1, 0xf7e1, 0xf7e1, 0 },
+ { 0xf857, 0xf857, 0xf857, 0 },
+ { 0xf8cd, 0xf8cd, 0xf8cd, 0 },
+ { 0xf942, 0xf942, 0xf942, 0 },
+ { 0xf9b7, 0xf9b7, 0xf9b7, 0 },
+ { 0xfa2b, 0xfa2b, 0xfa2b, 0 },
+ { 0xfaa0, 0xfaa0, 0xfaa0, 0 },
+ { 0xfb14, 0xfb14, 0xfb14, 0 },
+ { 0xfb88, 0xfb88, 0xfb88, 0 },
+ { 0xfbfb, 0xfbfb, 0xfbfb, 0 },
+ { 0xfc6e, 0xfc6e, 0xfc6e, 0 },
+ { 0xfce1, 0xfce1, 0xfce1, 0 },
+ { 0xfd54, 0xfd54, 0xfd54, 0 },
+ { 0xfdc6, 0xfdc6, 0xfdc6, 0 },
+ { 0xfe39, 0xfe39, 0xfe39, 0 },
+ { 0xfeaa, 0xfeaa, 0xfeaa, 0 },
+ { 0xff1c, 0xff1c, 0xff1c, 0 },
+ { 0xff8d, 0xff8d, 0xff8d, 0 },
+ { 0xffff, 0xffff, 0xffff, 0 },
+};
+
+const struct vkms_color_lut srgb_inv_eotf = {
+ .base = srgb_inv_array,
+ .lut_length = LUT_SIZE,
+ .channel_value2index_ratio = 0xff00ffll
+};
+EXPORT_SYMBOL(srgb_inv_eotf);
diff --git a/drivers/gpu/drm/vkms/vkms_luts.h b/drivers/gpu/drm/vkms/vkms_luts.h
new file mode 100644
index 000000000000..925a4a7b84e2
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_luts.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _VKMS_LUTS_H_
+#define _VKMS_LUTS_H_
+
+#define LUT_SIZE 256
+
+extern const struct vkms_color_lut linear_eotf;
+extern const struct vkms_color_lut srgb_eotf;
+extern const struct vkms_color_lut srgb_inv_eotf;
+
+#endif /* _VKMS_LUTS_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_output.c b/drivers/gpu/drm/vkms/vkms_output.c
index 2ee3749e2b28..86ce07a617f5 100644
--- a/drivers/gpu/drm/vkms/vkms_output.c
+++ b/drivers/gpu/drm/vkms/vkms_output.c
@@ -4,6 +4,7 @@
#include "vkms_connector.h"
#include "vkms_drv.h"
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
int vkms_output_init(struct vkms_device *vkmsdev)
{
@@ -19,11 +20,7 @@ int vkms_output_init(struct vkms_device *vkmsdev)
return -EINVAL;
vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
- enum drm_plane_type type;
-
- type = vkms_config_plane_get_type(plane_cfg);
-
- plane_cfg->plane = vkms_plane_init(vkmsdev, type);
+ plane_cfg->plane = vkms_plane_init(vkmsdev, plane_cfg);
if (IS_ERR(plane_cfg->plane)) {
DRM_DEV_ERROR(dev->dev, "Failed to init vkms plane\n");
return PTR_ERR(plane_cfg->plane);
diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c
index e592e47a5736..19fe6acad306 100644
--- a/drivers/gpu/drm/vkms/vkms_plane.c
+++ b/drivers/gpu/drm/vkms/vkms_plane.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0+
+#include "vkms_config.h"
#include <linux/iosys-map.h>
#include <drm/drm_atomic.h>
@@ -8,6 +9,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include "vkms_drv.h"
#include "vkms_formats.h"
@@ -217,7 +219,7 @@ static const struct drm_plane_helper_funcs vkms_plane_helper_funcs = {
};
struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev,
- enum drm_plane_type type)
+ struct vkms_config_plane *plane_cfg)
{
struct drm_device *dev = &vkmsdev->drm;
struct vkms_plane *plane;
@@ -225,7 +227,8 @@ struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev,
plane = drmm_universal_plane_alloc(dev, struct vkms_plane, base, 0,
&vkms_plane_funcs,
vkms_formats, ARRAY_SIZE(vkms_formats),
- NULL, type, NULL);
+ NULL, vkms_config_plane_get_type(plane_cfg),
+ NULL);
if (IS_ERR(plane))
return plane;
@@ -243,5 +246,8 @@ struct vkms_plane *vkms_plane_init(struct vkms_device *vkmsdev,
DRM_COLOR_YCBCR_BT601,
DRM_COLOR_YCBCR_FULL_RANGE);
+ if (vkms_config_plane_get_default_pipeline(plane_cfg))
+ vkms_initialize_colorops(&plane->base);
+
return plane;
}
diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c
index 45d69a3b85f6..097ae1f0a230 100644
--- a/drivers/gpu/drm/vkms/vkms_writeback.c
+++ b/drivers/gpu/drm/vkms/vkms_writeback.c
@@ -6,6 +6,7 @@
#include <drm/drm_edid.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_writeback.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
index 718832b08d96..c46f17ba7236 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
@@ -100,8 +100,10 @@ vmw_cursor_update_type(struct vmw_private *vmw, struct vmw_plane_state *vps)
if (vmw->has_mob) {
if ((vmw->capabilities2 & SVGA_CAP2_CURSOR_MOB) != 0)
return VMW_CURSOR_UPDATE_MOB;
+ else
+ return VMW_CURSOR_UPDATE_GB_ONLY;
}
-
+ drm_warn_once(&vmw->drm, "Unknown Cursor Type!\n");
return VMW_CURSOR_UPDATE_NONE;
}
@@ -139,6 +141,7 @@ static u32 vmw_cursor_mob_size(enum vmw_cursor_update_type update_type,
{
switch (update_type) {
case VMW_CURSOR_UPDATE_LEGACY:
+ case VMW_CURSOR_UPDATE_GB_ONLY:
case VMW_CURSOR_UPDATE_NONE:
return 0;
case VMW_CURSOR_UPDATE_MOB:
@@ -623,6 +626,7 @@ int vmw_cursor_plane_prepare_fb(struct drm_plane *plane,
if (!surface || vps->cursor.legacy.id == surface->snooper.id)
vps->cursor.update_type = VMW_CURSOR_UPDATE_NONE;
break;
+ case VMW_CURSOR_UPDATE_GB_ONLY:
case VMW_CURSOR_UPDATE_MOB: {
bo = vmw_user_object_buffer(&vps->uo);
if (bo) {
@@ -737,6 +741,7 @@ void
vmw_cursor_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
+ struct vmw_bo *bo;
struct drm_plane_state *new_state =
drm_atomic_get_new_plane_state(state, plane);
struct drm_plane_state *old_state =
@@ -762,6 +767,15 @@ vmw_cursor_plane_atomic_update(struct drm_plane *plane,
case VMW_CURSOR_UPDATE_MOB:
vmw_cursor_update_mob(dev_priv, vps);
break;
+ case VMW_CURSOR_UPDATE_GB_ONLY:
+ bo = vmw_user_object_buffer(&vps->uo);
+ if (bo)
+ vmw_send_define_cursor_cmd(dev_priv, bo->map.virtual,
+ vps->base.crtc_w,
+ vps->base.crtc_h,
+ vps->base.hotspot_x,
+ vps->base.hotspot_y);
+ break;
case VMW_CURSOR_UPDATE_NONE:
/* do nothing */
break;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h
index 40694925a70e..0c2cc0699b0d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.h
@@ -33,6 +33,7 @@ static const u32 __maybe_unused vmw_cursor_plane_formats[] = {
enum vmw_cursor_update_type {
VMW_CURSOR_UPDATE_NONE = 0,
VMW_CURSOR_UPDATE_LEGACY,
+ VMW_CURSOR_UPDATE_GB_ONLY,
VMW_CURSOR_UPDATE_MOB,
};
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 8ff958d119be..599052d07ae8 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -1023,8 +1023,8 @@ static int vmw_driver_load(struct vmw_private *dev_priv, u32 pci_id)
dev_priv->drm.dev,
dev_priv->drm.anon_inode->i_mapping,
dev_priv->drm.vma_offset_manager,
- dev_priv->map_mode == vmw_dma_alloc_coherent,
- false);
+ (dev_priv->map_mode == vmw_dma_alloc_coherent) ?
+ TTM_ALLOCATION_POOL_USE_DMA_ALLOC : 0);
if (unlikely(ret != 0)) {
drm_err(&dev_priv->drm,
"Failed initializing TTM buffer object driver.\n");
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index eda5b6f8f4c4..f2abaf1bda6a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -16,6 +16,7 @@
#include <drm/drm_auth.h>
#include <drm/drm_device.h>
#include <drm/drm_file.h>
+#include <drm/drm_print.h>
#include <drm/drm_rect.h>
#include <drm/ttm/ttm_execbuf_util.h>
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index d539f25b5fbe..3057f8baa7d2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -3668,6 +3668,11 @@ static int vmw_cmd_check(struct vmw_private *dev_priv,
cmd_id = header->id;
+ if (header->size > SVGA_CMD_MAX_DATASIZE) {
+ VMW_DEBUG_USER("SVGA3D command: %d is too big.\n",
+ cmd_id + SVGA_3D_CMD_BASE);
+ return -E2BIG;
+ }
*size = header->size + sizeof(SVGA3dCmdHeader);
cmd_id -= SVGA_3D_CMD_BASE;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c
index eedf1fe60be7..39f8c46550c2 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c
@@ -37,7 +37,7 @@ static void vmw_gem_object_free(struct drm_gem_object *gobj)
{
struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(gobj);
if (bo)
- ttm_bo_put(bo);
+ ttm_bo_fini(bo);
}
static int vmw_gem_object_open(struct drm_gem_object *obj,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 54ea1b513950..d32ce1cb579e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -553,6 +553,9 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
memcpy(&vfbs->uo, uo, sizeof(vfbs->uo));
vmw_user_object_ref(&vfbs->uo);
+ if (vfbs->uo.buffer)
+ vfbs->base.base.obj[0] = &vfbs->uo.buffer->tbo.base;
+
*out = &vfbs->base;
ret = drm_framebuffer_init(dev, &vfbs->base.base,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
index 7de20e56082c..fd4e76486f2d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
@@ -32,22 +32,22 @@ enum vmw_bo_dirty_method {
/**
* struct vmw_bo_dirty - Dirty information for buffer objects
+ * @ref_count: Reference count for this structure. Must be first member!
* @start: First currently dirty bit
* @end: Last currently dirty bit + 1
* @method: The currently used dirty method
* @change_count: Number of consecutive method change triggers
- * @ref_count: Reference count for this structure
* @bitmap_size: The size of the bitmap in bits. Typically equal to the
* nuber of pages in the bo.
* @bitmap: A bitmap where each bit represents a page. A set bit means a
* dirty page.
*/
struct vmw_bo_dirty {
+ struct kref ref_count;
unsigned long start;
unsigned long end;
enum vmw_bo_dirty_method method;
unsigned int change_count;
- unsigned int ref_count;
unsigned long bitmap_size;
unsigned long bitmap[];
};
@@ -221,7 +221,7 @@ int vmw_bo_dirty_add(struct vmw_bo *vbo)
int ret;
if (dirty) {
- dirty->ref_count++;
+ kref_get(&dirty->ref_count);
return 0;
}
@@ -235,7 +235,7 @@ int vmw_bo_dirty_add(struct vmw_bo *vbo)
dirty->bitmap_size = num_pages;
dirty->start = dirty->bitmap_size;
dirty->end = 0;
- dirty->ref_count = 1;
+ kref_init(&dirty->ref_count);
if (num_pages < PAGE_SIZE / sizeof(pte_t)) {
dirty->method = VMW_BO_DIRTY_PAGETABLE;
} else {
@@ -274,10 +274,8 @@ void vmw_bo_dirty_release(struct vmw_bo *vbo)
{
struct vmw_bo_dirty *dirty = vbo->dirty;
- if (dirty && --dirty->ref_count == 0) {
- kvfree(dirty);
+ if (dirty && kref_put(&dirty->ref_count, (void *)kvfree))
vbo->dirty = NULL;
- }
}
/**
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index 7e281c3c6bc5..c4ac9b47e23a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -15,6 +15,7 @@
#include "vmw_surface_cache.h"
#include "device_include/svga3d_surfacedefs.h"
+#include <drm/drm_dumb_buffers.h>
#include <drm/ttm/ttm_placement.h>
#define SVGA3D_FLAGS_64(upper32, lower32) (((uint64_t)upper32 << 32) | lower32)
@@ -2267,23 +2268,9 @@ int vmw_dumb_create(struct drm_file *file_priv,
* contents is going to be rendered guest side.
*/
if (!dev_priv->has_mob || !vmw_supports_3d(dev_priv)) {
- int cpp = DIV_ROUND_UP(args->bpp, 8);
-
- switch (cpp) {
- case 1: /* DRM_FORMAT_C8 */
- case 2: /* DRM_FORMAT_RGB565 */
- case 4: /* DRM_FORMAT_XRGB8888 */
- break;
- default:
- /*
- * Dumb buffers don't allow anything else.
- * This is tested via IGT's dumb_buffers
- */
- return -EINVAL;
- }
-
- args->pitch = args->width * cpp;
- args->size = ALIGN(args->pitch * args->height, PAGE_SIZE);
+ ret = drm_mode_size_dumb(dev, args, 0, 0);
+ if (ret)
+ return ret;
ret = vmw_gem_object_create_with_handle(dev_priv, file_priv,
args->size, &args->handle,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
index aec774fa4d7b..5abd7f5ad2db 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
@@ -247,9 +247,8 @@ vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct vmw_private *vmw = vmw_priv(dev);
- unsigned int pipe = crtc->index;
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
if (!vmw->vkms_enabled)
return false;
@@ -281,8 +280,7 @@ vmw_vkms_enable_vblank(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct vmw_private *vmw = vmw_priv(dev);
- unsigned int pipe = drm_crtc_index(crtc);
- struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
if (!vmw->vkms_enabled)
diff --git a/drivers/gpu/drm/xe/Kconfig b/drivers/gpu/drm/xe/Kconfig
index 7219f6b884b6..4b288eb3f5b0 100644
--- a/drivers/gpu/drm/xe/Kconfig
+++ b/drivers/gpu/drm/xe/Kconfig
@@ -13,7 +13,6 @@ config DRM_XE
select TMPFS
select DRM_BUDDY
select DRM_CLIENT_SELECTION
- select DRM_EXEC
select DRM_KMS_HELPER
select DRM_KUNIT_TEST_HELPERS if DRM_XE_KUNIT_TEST != n
select DRM_PANEL
diff --git a/drivers/gpu/drm/xe/Kconfig.debug b/drivers/gpu/drm/xe/Kconfig.debug
index 87902b4bd6d3..01227c77f6d7 100644
--- a/drivers/gpu/drm/xe/Kconfig.debug
+++ b/drivers/gpu/drm/xe/Kconfig.debug
@@ -40,23 +40,23 @@ config DRM_XE_DEBUG_VM
If in doubt, say "N".
-config DRM_XE_DEBUG_MEMIRQ
- bool "Enable extra memirq debugging"
+config DRM_XE_DEBUG_SRIOV
+ bool "Enable extra SR-IOV debugging"
default n
+ imply DRM_XE_DEBUG_MEMIRQ
help
- Choose this option to enable additional debugging info for
- memory based interrupts.
+ Enable extra SR-IOV debugging info.
Recommended for driver developers only.
If in doubt, say "N".
-config DRM_XE_DEBUG_SRIOV
- bool "Enable extra SR-IOV debugging"
+config DRM_XE_DEBUG_MEMIRQ
+ bool "Enable extra memirq debugging"
default n
- select DRM_XE_DEBUG_MEMIRQ
help
- Enable extra SR-IOV debugging info.
+ Choose this option to enable additional debugging info for
+ memory based interrupts.
Recommended for driver developers only.
diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index d9c6cf0f189e..62be4a5227e4 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -58,7 +58,6 @@ xe-y += xe_bb.o \
xe_gt_freq.o \
xe_gt_idle.o \
xe_gt_mcr.o \
- xe_gt_pagefault.o \
xe_gt_sysfs.o \
xe_gt_throttle.o \
xe_gt_topology.o \
@@ -73,6 +72,7 @@ xe-y += xe_bb.o \
xe_guc_id_mgr.o \
xe_guc_klv_helpers.o \
xe_guc_log.o \
+ xe_guc_pagefault.o \
xe_guc_pc.o \
xe_guc_submit.o \
xe_guc_tlb_inval.o \
@@ -94,6 +94,7 @@ xe-y += xe_bb.o \
xe_nvm.o \
xe_oa.o \
xe_observation.o \
+ xe_pagefault.o \
xe_pat.o \
xe_pci.o \
xe_pcode.o \
@@ -173,8 +174,19 @@ xe-$(CONFIG_PCI_IOV) += \
xe_lmtt_2l.o \
xe_lmtt_ml.o \
xe_pci_sriov.o \
+ xe_sriov_packet.o \
xe_sriov_pf.o \
- xe_sriov_pf_service.o
+ xe_sriov_pf_control.o \
+ xe_sriov_pf_debugfs.o \
+ xe_sriov_pf_migration.o \
+ xe_sriov_pf_provision.o \
+ xe_sriov_pf_service.o \
+ xe_sriov_pf_sysfs.o \
+ xe_tile_sriov_pf_debugfs.o
+
+ifdef CONFIG_XE_VFIO_PCI
+ xe-$(CONFIG_PCI_IOV) += xe_sriov_vfio.o
+endif
# include helpers for tests even when XE is built-in
ifdef CONFIG_DRM_XE_KUNIT_TEST
@@ -201,7 +213,6 @@ $(obj)/i915-display/%.o: $(srctree)/drivers/gpu/drm/i915/display/%.c FORCE
# Display code specific to xe
xe-$(CONFIG_DRM_XE_DISPLAY) += \
display/ext/i915_irq.o \
- display/ext/i915_utils.o \
display/intel_bo.o \
display/intel_fb_bo.o \
display/intel_fbdev_fb.o \
@@ -214,6 +225,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
display/xe_hdcp_gsc.o \
display/xe_panic.o \
display/xe_plane_initial.o \
+ display/xe_stolen.o \
display/xe_tdf.o
# SOC code shared with i915
@@ -230,15 +242,19 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_backlight.o \
i915-display/intel_bios.o \
i915-display/intel_bw.o \
+ i915-display/intel_casf.o \
i915-display/intel_cdclk.o \
i915-display/intel_cmtg.o \
i915-display/intel_color.o \
+ i915-display/intel_colorop.o \
+ i915-display/intel_color_pipeline.o \
i915-display/intel_combo_phy.o \
i915-display/intel_connector.o \
i915-display/intel_crtc.o \
i915-display/intel_crtc_state_dump.o \
i915-display/intel_cursor.o \
i915-display/intel_cx0_phy.o \
+ i915-display/intel_dbuf_bw.o \
i915-display/intel_ddi.o \
i915-display/intel_ddi_buf_trans.o \
i915-display/intel_display.o \
@@ -250,7 +266,9 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_display_power.o \
i915-display/intel_display_power_map.o \
i915-display/intel_display_power_well.o \
+ i915-display/intel_display_rpm.o \
i915-display/intel_display_trace.o \
+ i915-display/intel_display_utils.o \
i915-display/intel_display_wa.o \
i915-display/intel_dkl_phy.o \
i915-display/intel_dmc.o \
@@ -287,6 +305,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_hti.o \
i915-display/intel_link_bw.o \
i915-display/intel_lspcon.o \
+ i915-display/intel_lt_phy.o \
i915-display/intel_modeset_lock.o \
i915-display/intel_modeset_setup.o \
i915-display/intel_modeset_verify.o \
@@ -307,6 +326,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
i915-display/intel_vga.o \
i915-display/intel_vrr.o \
i915-display/intel_wm.o \
+ i915-display/skl_prefill.o \
i915-display/skl_scaler.o \
i915-display/skl_universal_plane.o \
i915-display/skl_watermark.o
diff --git a/drivers/gpu/drm/xe/abi/guc_actions_abi.h b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
index 31090c69dfbe..47756e4674a1 100644
--- a/drivers/gpu/drm/xe/abi/guc_actions_abi.h
+++ b/drivers/gpu/drm/xe/abi/guc_actions_abi.h
@@ -196,14 +196,6 @@ enum xe_guc_register_context_multi_lrc_param_offsets {
XE_GUC_REGISTER_CONTEXT_MULTI_LRC_MSG_MIN_LEN = 11,
};
-enum xe_guc_context_wq_item_offsets {
- XE_GUC_CONTEXT_WQ_HEADER_DATA_0_TYPE_LEN = 0,
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_1_CTX_DESC_LOW,
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_2_GUCCTX_RINGTAIL_FREEZEPOCS,
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_3_WI_FENCE_ID,
- XE_GUC_CONTEXT_WQ_EL_CHILD_LIST_DATA_4_RINGTAIL,
-};
-
enum xe_guc_report_status {
XE_GUC_REPORT_STATUS_UNKNOWN = 0x0,
XE_GUC_REPORT_STATUS_ACKED = 0x1,
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h
index 8a048980ea38..0548b2e0316f 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_object.h
@@ -5,10 +5,8 @@
#define __I915_GEM_OBJECT_H__
struct dma_fence;
-struct i915_sched_attr;
-static inline void i915_gem_fence_wait_priority(struct dma_fence *fence,
- const struct i915_sched_attr *attr)
+static inline void i915_gem_fence_wait_priority_display(struct dma_fence *fence)
{
}
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h
index f097fc6d5127..48e3256ba37e 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/gem/i915_gem_stolen.h
@@ -6,80 +6,35 @@
#ifndef _I915_GEM_STOLEN_H_
#define _I915_GEM_STOLEN_H_
-#include "xe_ttm_stolen_mgr.h"
-#include "xe_res_cursor.h"
-#include "xe_validation.h"
-
-struct xe_bo;
-
-struct i915_stolen_fb {
- struct xe_bo *bo;
-};
-
-static inline int i915_gem_stolen_insert_node_in_range(struct xe_device *xe,
- struct i915_stolen_fb *fb,
- u32 size, u32 align,
- u32 start, u32 end)
-{
- struct xe_bo *bo;
- int err = 0;
- u32 flags = XE_BO_FLAG_PINNED | XE_BO_FLAG_STOLEN;
-
- if (start < SZ_4K)
- start = SZ_4K;
-
- if (align) {
- size = ALIGN(size, align);
- start = ALIGN(start, align);
- }
-
- bo = xe_bo_create_pin_range_novm(xe, xe_device_get_root_tile(xe),
- size, start, end, ttm_bo_type_kernel, flags);
- if (IS_ERR(bo)) {
- err = PTR_ERR(bo);
- bo = NULL;
- return err;
- }
-
- fb->bo = bo;
-
- return err;
-}
-
-static inline int i915_gem_stolen_insert_node(struct xe_device *xe,
- struct i915_stolen_fb *fb,
- u32 size, u32 align)
-{
- /* Not used on xe */
- BUG_ON(1);
- return -ENODEV;
-}
-
-static inline void i915_gem_stolen_remove_node(struct xe_device *xe,
- struct i915_stolen_fb *fb)
-{
- xe_bo_unpin_map_no_vm(fb->bo);
- fb->bo = NULL;
-}
-
-#define i915_gem_stolen_initialized(xe) (!!ttm_manager_type(&(xe)->ttm, XE_PL_STOLEN))
-#define i915_gem_stolen_node_allocated(fb) (!!((fb)->bo))
-
-static inline u32 i915_gem_stolen_node_offset(struct i915_stolen_fb *fb)
-{
- struct xe_res_cursor res;
-
- xe_res_first(fb->bo->ttm.resource, 0, 4096, &res);
- return res.start;
-}
-
-/* Used for < gen4. These are not supported by Xe */
-#define i915_gem_stolen_area_address(xe) (!WARN_ON(1))
-/* Used for gen9 specific WA. Gen9 is not supported by Xe */
-#define i915_gem_stolen_area_size(xe) (!WARN_ON(1))
-
-#define i915_gem_stolen_node_address(xe, fb) (xe_ttm_stolen_gpu_offset(xe) + \
- i915_gem_stolen_node_offset(fb))
-#define i915_gem_stolen_node_size(fb) ((u64)((fb)->bo->ttm.base.size))
+#include <linux/types.h>
+
+struct drm_device;
+struct intel_stolen_node;
+
+int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
+ unsigned int align, u64 start, u64 end);
+
+int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size,
+ unsigned int align);
+
+void i915_gem_stolen_remove_node(struct intel_stolen_node *node);
+
+bool i915_gem_stolen_initialized(struct drm_device *drm);
+
+bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node);
+
+u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node);
+
+u64 i915_gem_stolen_area_address(struct drm_device *drm);
+
+u64 i915_gem_stolen_area_size(struct drm_device *drm);
+
+u64 i915_gem_stolen_node_address(struct intel_stolen_node *node);
+
+u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node);
+
+struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm);
+
+void i915_gem_stolen_node_free(const struct intel_stolen_node *node);
#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
index b8269391bc69..3e79a74ff7de 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_drv.h
@@ -12,7 +12,6 @@
#include <drm/drm_drv.h>
-#include "xe_device.h" /* for xe_device_has_flat_ccs() */
#include "xe_device_types.h"
static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
@@ -35,7 +34,4 @@ static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
#define IS_MOBILE(xe) (xe && 0)
-#define HAS_FLAT_CCS(xe) (xe_device_has_flat_ccs(xe))
-#define HAS_128_BYTE_Y_TILING(xe) (xe || 1)
-
#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_scheduler_types.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_scheduler_types.h
deleted file mode 100644
index c11130440d31..000000000000
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_scheduler_types.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* Copyright © 2025 Intel Corporation */
-
-#ifndef __I915_SCHEDULER_TYPES_H__
-#define __I915_SCHEDULER_TYPES_H__
-
-#define I915_PRIORITY_DISPLAY 0
-
-struct i915_sched_attr {
- int priority;
-};
-
-#endif
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h
index 1d7c4360e5c0..bcd441dc0fce 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_utils.h
@@ -3,4 +3,11 @@
* Copyright © 2023 Intel Corporation
*/
-#include "../../i915/i915_utils.h"
+/* for soc/ */
+#ifndef MISSING_CASE
+#define MISSING_CASE(x) WARN(1, "Missing case (%s == %ld)\n", \
+ __stringify(x), (long)(x))
+#endif
+
+/* for a couple of users under i915/display */
+#define i915_inject_probe_failure(unused) ((unused) && 0)
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
index 4465c40f8134..b17e3bab23d5 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/i915_vma.h
@@ -26,8 +26,6 @@ struct i915_vma {
struct xe_ggtt_node *node;
};
-#define i915_ggtt_clear_scanout(bo) do { } while (0)
-
#define i915_vma_fence_id(vma) -1
static inline u32 i915_ggtt_offset(const struct i915_vma *vma)
diff --git a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
index d012f02bc84f..d93ddacdf743 100644
--- a/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
+++ b/drivers/gpu/drm/xe/compat-i915-headers/intel_uncore.h
@@ -91,27 +91,6 @@ static inline u32 intel_uncore_rmw(struct intel_uncore *uncore,
return xe_mmio_rmw32(__compat_uncore_to_mmio(uncore), reg, clear, set);
}
-static inline int intel_wait_for_register(struct intel_uncore *uncore,
- i915_reg_t i915_reg, u32 mask,
- u32 value, unsigned int timeout)
-{
- struct xe_reg reg = XE_REG(i915_mmio_reg_offset(i915_reg));
-
- return xe_mmio_wait32(__compat_uncore_to_mmio(uncore), reg, mask, value,
- timeout * USEC_PER_MSEC, NULL, false);
-}
-
-static inline int intel_wait_for_register_fw(struct intel_uncore *uncore,
- i915_reg_t i915_reg, u32 mask,
- u32 value, unsigned int timeout,
- u32 *out_value)
-{
- struct xe_reg reg = XE_REG(i915_mmio_reg_offset(i915_reg));
-
- return xe_mmio_wait32(__compat_uncore_to_mmio(uncore), reg, mask, value,
- timeout * USEC_PER_MSEC, out_value, false);
-}
-
static inline int
__intel_wait_for_register(struct intel_uncore *uncore, i915_reg_t i915_reg,
u32 mask, u32 value, unsigned int fast_timeout_us,
@@ -133,6 +112,16 @@ __intel_wait_for_register(struct intel_uncore *uncore, i915_reg_t i915_reg,
out_value, atomic);
}
+static inline int
+__intel_wait_for_register_fw(struct intel_uncore *uncore, i915_reg_t i915_reg,
+ u32 mask, u32 value, unsigned int fast_timeout_us,
+ unsigned int slow_timeout_ms, u32 *out_value)
+{
+ return __intel_wait_for_register(uncore, i915_reg, mask, value,
+ fast_timeout_us, slow_timeout_ms,
+ out_value);
+}
+
static inline u32 intel_uncore_read_fw(struct intel_uncore *uncore,
i915_reg_t i915_reg)
{
diff --git a/drivers/gpu/drm/xe/display/ext/i915_utils.c b/drivers/gpu/drm/xe/display/ext/i915_utils.c
deleted file mode 100644
index 1421c2a7b64d..000000000000
--- a/drivers/gpu/drm/xe/display/ext/i915_utils.c
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2023 Intel Corporation
- */
-
-#include "i915_drv.h"
-#include "i915_utils.h"
-
-bool i915_vtd_active(struct drm_i915_private *i915)
-{
- if (device_iommu_mapped(i915->drm.dev))
- return true;
-
- /* Running as a guest, we assume the host is enforcing VT'd */
- return i915_run_as_guest();
-}
-
-#if IS_ENABLED(CONFIG_DRM_I915_DEBUG)
-
-/* i915 specific, just put here for shutting it up */
-int __i915_inject_probe_error(struct drm_i915_private *i915, int err,
- const char *func, int line)
-{
- return 0;
-}
-
-#endif
diff --git a/drivers/gpu/drm/xe/display/intel_bo.c b/drivers/gpu/drm/xe/display/intel_bo.c
index 27437c22bd70..bad2243b9114 100644
--- a/drivers/gpu/drm/xe/display/intel_bo.c
+++ b/drivers/gpu/drm/xe/display/intel_bo.c
@@ -5,6 +5,7 @@
#include "xe_bo.h"
#include "intel_bo.h"
+#include "intel_frontbuffer.h"
bool intel_bo_is_tiled(struct drm_gem_object *obj)
{
@@ -28,10 +29,6 @@ bool intel_bo_is_protected(struct drm_gem_object *obj)
return xe_bo_is_protected(gem_to_xe_bo(obj));
}
-void intel_bo_flush_if_display(struct drm_gem_object *obj)
-{
-}
-
int intel_bo_fb_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
{
return drm_gem_prime_mmap(obj, vma);
@@ -44,15 +41,60 @@ int intel_bo_read_from_page(struct drm_gem_object *obj, u64 offset, void *dst, i
return xe_bo_read(bo, offset, dst, size);
}
-struct intel_frontbuffer *intel_bo_get_frontbuffer(struct drm_gem_object *obj)
+struct xe_frontbuffer {
+ struct intel_frontbuffer base;
+ struct drm_gem_object *obj;
+ struct kref ref;
+};
+
+struct intel_frontbuffer *intel_bo_frontbuffer_get(struct drm_gem_object *obj)
+{
+ struct xe_frontbuffer *front;
+
+ front = kmalloc(sizeof(*front), GFP_KERNEL);
+ if (!front)
+ return NULL;
+
+ intel_frontbuffer_init(&front->base, obj->dev);
+
+ kref_init(&front->ref);
+
+ drm_gem_object_get(obj);
+ front->obj = obj;
+
+ return &front->base;
+}
+
+void intel_bo_frontbuffer_ref(struct intel_frontbuffer *_front)
{
- return NULL;
+ struct xe_frontbuffer *front =
+ container_of(_front, typeof(*front), base);
+
+ kref_get(&front->ref);
+}
+
+static void frontbuffer_release(struct kref *ref)
+{
+ struct xe_frontbuffer *front =
+ container_of(ref, typeof(*front), ref);
+
+ intel_frontbuffer_fini(&front->base);
+
+ drm_gem_object_put(front->obj);
+
+ kfree(front);
+}
+
+void intel_bo_frontbuffer_put(struct intel_frontbuffer *_front)
+{
+ struct xe_frontbuffer *front =
+ container_of(_front, typeof(*front), base);
+
+ kref_put(&front->ref, frontbuffer_release);
}
-struct intel_frontbuffer *intel_bo_set_frontbuffer(struct drm_gem_object *obj,
- struct intel_frontbuffer *front)
+void intel_bo_frontbuffer_flush_for_display(struct intel_frontbuffer *front)
{
- return front;
}
void intel_bo_describe(struct seq_file *m, struct drm_gem_object *obj)
diff --git a/drivers/gpu/drm/xe/display/intel_fb_bo.c b/drivers/gpu/drm/xe/display/intel_fb_bo.c
index ebdb22c9499d..db8b1a27b4de 100644
--- a/drivers/gpu/drm/xe/display/intel_fb_bo.c
+++ b/drivers/gpu/drm/xe/display/intel_fb_bo.c
@@ -24,8 +24,7 @@ void intel_fb_bo_framebuffer_fini(struct drm_gem_object *obj)
xe_bo_put(bo);
}
-int intel_fb_bo_framebuffer_init(struct drm_framebuffer *fb,
- struct drm_gem_object *obj,
+int intel_fb_bo_framebuffer_init(struct drm_gem_object *obj,
struct drm_mode_fb_cmd2 *mode_cmd)
{
struct xe_bo *bo = gem_to_xe_bo(obj);
diff --git a/drivers/gpu/drm/xe/display/intel_fbdev_fb.c b/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
index 8ea9a472113c..7ad76022cb14 100644
--- a/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
+++ b/drivers/gpu/drm/xe/display/intel_fbdev_fb.c
@@ -3,45 +3,34 @@
* Copyright © 2023 Intel Corporation
*/
-#include <drm/drm_fb_helper.h>
+#include <linux/fb.h>
-#include "intel_display_core.h"
-#include "intel_display_types.h"
-#include "intel_fb.h"
#include "intel_fbdev_fb.h"
#include "xe_bo.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_wa.h"
-#include <generated/xe_wa_oob.h>
+#include <generated/xe_device_wa_oob.h>
-struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes)
+/*
+ * FIXME: There shouldn't be any reason to have XE_PAGE_SIZE stride
+ * alignment. The same 64 as i915 uses should be fine, and we shouldn't need to
+ * have driver specific values. However, dropping the stride alignment to 64
+ * leads to underflowing the bo pin count in the atomic cleanup work.
+ */
+u32 intel_fbdev_fb_pitch_align(u32 stride)
{
- struct drm_framebuffer *fb;
- struct drm_device *dev = helper->dev;
- struct xe_device *xe = to_xe_device(dev);
- struct drm_mode_fb_cmd2 mode_cmd = {};
- struct xe_bo *obj;
- int size;
-
- /* we don't do packed 24bpp */
- if (sizes->surface_bpp == 24)
- sizes->surface_bpp = 32;
-
- mode_cmd.width = sizes->surface_width;
- mode_cmd.height = sizes->surface_height;
+ return ALIGN(stride, XE_PAGE_SIZE);
+}
- mode_cmd.pitches[0] = ALIGN(mode_cmd.width *
- DIV_ROUND_UP(sizes->surface_bpp, 8), XE_PAGE_SIZE);
- mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
- sizes->surface_depth);
+struct drm_gem_object *intel_fbdev_fb_bo_create(struct drm_device *drm, int size)
+{
+ struct xe_device *xe = to_xe_device(drm);
+ struct xe_bo *obj;
- size = mode_cmd.pitches[0] * mode_cmd.height;
- size = PAGE_ALIGN(size);
obj = ERR_PTR(-ENODEV);
- if (!IS_DGFX(xe) && !XE_GT_WA(xe_root_mmio_gt(xe), 22019338487_display)) {
+ if (!IS_DGFX(xe) && !XE_DEVICE_WA(xe, 22019338487_display)) {
obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe),
size,
ttm_bo_type_kernel, XE_BO_FLAG_SCANOUT |
@@ -62,33 +51,22 @@ struct intel_framebuffer *intel_fbdev_fb_alloc(struct drm_fb_helper *helper,
if (IS_ERR(obj)) {
drm_err(&xe->drm, "failed to allocate framebuffer (%pe)\n", obj);
- fb = ERR_PTR(-ENOMEM);
- goto err;
- }
-
- fb = intel_framebuffer_create(&obj->ttm.base,
- drm_get_format_info(dev,
- mode_cmd.pixel_format,
- mode_cmd.modifier[0]),
- &mode_cmd);
- if (IS_ERR(fb)) {
- xe_bo_unpin_map_no_vm(obj);
- goto err;
+ return ERR_PTR(-ENOMEM);
}
- drm_gem_object_put(&obj->ttm.base);
-
- return to_intel_framebuffer(fb);
+ return &obj->ttm.base;
+}
-err:
- return ERR_CAST(fb);
+void intel_fbdev_fb_bo_destroy(struct drm_gem_object *obj)
+{
+ xe_bo_unpin_map_no_vm(gem_to_xe_bo(obj));
}
-int intel_fbdev_fb_fill_info(struct intel_display *display, struct fb_info *info,
+int intel_fbdev_fb_fill_info(struct drm_device *drm, struct fb_info *info,
struct drm_gem_object *_obj, struct i915_vma *vma)
{
struct xe_bo *obj = gem_to_xe_bo(_obj);
- struct pci_dev *pdev = to_pci_dev(display->drm->dev);
+ struct pci_dev *pdev = to_pci_dev(drm->dev);
if (!(obj->flags & XE_BO_FLAG_SYSTEM)) {
if (obj->flags & XE_BO_FLAG_STOLEN)
diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c
index 19e691fccf8c..8b0afa270216 100644
--- a/drivers/gpu/drm/xe/display/xe_display.c
+++ b/drivers/gpu/drm/xe/display/xe_display.c
@@ -13,6 +13,8 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
+#include <drm/intel/display_member.h>
+#include <drm/intel/display_parent_interface.h>
#include <uapi/drm/xe_drm.h>
#include "soc/intel_dram.h"
@@ -33,8 +35,12 @@
#include "intel_hotplug.h"
#include "intel_opregion.h"
#include "skl_watermark.h"
+#include "xe_display_rpm.h"
#include "xe_module.h"
+/* Ensure drm and display members are placed properly. */
+INTEL_DISPLAY_MEMBER_STATIC_ASSERT(struct xe_device, drm, display);
+
/* Xe device functions */
/**
@@ -223,15 +229,14 @@ void xe_display_irq_reset(struct xe_device *xe)
gen11_display_irq_reset(display);
}
-void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt)
+void xe_display_irq_postinstall(struct xe_device *xe)
{
struct intel_display *display = xe->display;
if (!xe->info.probe_display)
return;
- if (gt->info.id == XE_GT0)
- gen11_de_irq_postinstall(display);
+ gen11_de_irq_postinstall(display);
}
static bool suspend_to_idle(void)
@@ -324,7 +329,7 @@ void xe_display_pm_suspend(struct xe_device *xe)
* properly.
*/
intel_power_domains_disable(display);
- drm_client_dev_suspend(&xe->drm, false);
+ drm_client_dev_suspend(&xe->drm);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(&xe->drm);
@@ -356,7 +361,7 @@ void xe_display_pm_shutdown(struct xe_device *xe)
return;
intel_power_domains_disable(display);
- drm_client_dev_suspend(&xe->drm, false);
+ drm_client_dev_suspend(&xe->drm);
if (intel_display_device_present(display)) {
drm_kms_helper_poll_disable(&xe->drm);
@@ -481,7 +486,7 @@ void xe_display_pm_resume(struct xe_device *xe)
intel_opregion_resume(display);
- drm_client_dev_resume(&xe->drm, false);
+ drm_client_dev_resume(&xe->drm);
intel_power_domains_enable(display);
}
@@ -511,6 +516,10 @@ static void display_device_remove(struct drm_device *dev, void *arg)
intel_display_device_remove(display);
}
+static const struct intel_display_parent_interface parent = {
+ .rpm = &xe_display_rpm_interface,
+};
+
/**
* xe_display_probe - probe display and create display struct
* @xe: XE device instance
@@ -531,7 +540,7 @@ int xe_display_probe(struct xe_device *xe)
if (!xe->info.probe_display)
goto no_display;
- display = intel_display_device_probe(pdev);
+ display = intel_display_device_probe(pdev, &parent);
if (IS_ERR(display))
return PTR_ERR(display);
diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h
index e533aa4750bc..76db95c25f7e 100644
--- a/drivers/gpu/drm/xe/display/xe_display.h
+++ b/drivers/gpu/drm/xe/display/xe_display.h
@@ -26,7 +26,7 @@ void xe_display_unregister(struct xe_device *xe);
void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl);
void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir);
void xe_display_irq_reset(struct xe_device *xe);
-void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt);
+void xe_display_irq_postinstall(struct xe_device *xe);
void xe_display_pm_suspend(struct xe_device *xe);
void xe_display_pm_shutdown(struct xe_device *xe);
@@ -55,7 +55,7 @@ static inline void xe_display_unregister(struct xe_device *xe) {}
static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {}
static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {}
static inline void xe_display_irq_reset(struct xe_device *xe) {}
-static inline void xe_display_irq_postinstall(struct xe_device *xe, struct xe_gt *gt) {}
+static inline void xe_display_irq_postinstall(struct xe_device *xe) {}
static inline void xe_display_pm_suspend(struct xe_device *xe) {}
static inline void xe_display_pm_shutdown(struct xe_device *xe) {}
diff --git a/drivers/gpu/drm/xe/display/xe_display_rpm.c b/drivers/gpu/drm/xe/display/xe_display_rpm.c
index 3825376e98cc..340f65884812 100644
--- a/drivers/gpu/drm/xe/display/xe_display_rpm.c
+++ b/drivers/gpu/drm/xe/display/xe_display_rpm.c
@@ -1,73 +1,74 @@
// SPDX-License-Identifier: MIT
/* Copyright © 2025 Intel Corporation */
+#include <drm/intel/display_parent_interface.h>
+
#include "intel_display_core.h"
#include "intel_display_rpm.h"
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_pm.h"
-static struct xe_device *display_to_xe(struct intel_display *display)
-{
- return to_xe_device(display->drm);
-}
-
-struct ref_tracker *intel_display_rpm_get_raw(struct intel_display *display)
+static struct ref_tracker *xe_display_rpm_get(const struct drm_device *drm)
{
- return intel_display_rpm_get(display);
+ return xe_pm_runtime_resume_and_get(to_xe_device(drm)) ? INTEL_WAKEREF_DEF : NULL;
}
-void intel_display_rpm_put_raw(struct intel_display *display, struct ref_tracker *wakeref)
+static struct ref_tracker *xe_display_rpm_get_if_in_use(const struct drm_device *drm)
{
- intel_display_rpm_put(display, wakeref);
+ return xe_pm_runtime_get_if_in_use(to_xe_device(drm)) ? INTEL_WAKEREF_DEF : NULL;
}
-struct ref_tracker *intel_display_rpm_get(struct intel_display *display)
+static struct ref_tracker *xe_display_rpm_get_noresume(const struct drm_device *drm)
{
- return xe_pm_runtime_resume_and_get(display_to_xe(display)) ? INTEL_WAKEREF_DEF : NULL;
-}
-
-struct ref_tracker *intel_display_rpm_get_if_in_use(struct intel_display *display)
-{
- return xe_pm_runtime_get_if_in_use(display_to_xe(display)) ? INTEL_WAKEREF_DEF : NULL;
-}
-
-struct ref_tracker *intel_display_rpm_get_noresume(struct intel_display *display)
-{
- xe_pm_runtime_get_noresume(display_to_xe(display));
+ xe_pm_runtime_get_noresume(to_xe_device(drm));
return INTEL_WAKEREF_DEF;
}
-void intel_display_rpm_put(struct intel_display *display, struct ref_tracker *wakeref)
+static void xe_display_rpm_put(const struct drm_device *drm, struct ref_tracker *wakeref)
{
if (wakeref)
- xe_pm_runtime_put(display_to_xe(display));
+ xe_pm_runtime_put(to_xe_device(drm));
}
-void intel_display_rpm_put_unchecked(struct intel_display *display)
+static void xe_display_rpm_put_unchecked(const struct drm_device *drm)
{
- xe_pm_runtime_put(display_to_xe(display));
+ xe_pm_runtime_put(to_xe_device(drm));
}
-bool intel_display_rpm_suspended(struct intel_display *display)
+static bool xe_display_rpm_suspended(const struct drm_device *drm)
{
- struct xe_device *xe = display_to_xe(display);
+ struct xe_device *xe = to_xe_device(drm);
return pm_runtime_suspended(xe->drm.dev);
}
-void assert_display_rpm_held(struct intel_display *display)
+static void xe_display_rpm_assert_held(const struct drm_device *drm)
{
/* FIXME */
}
-void intel_display_rpm_assert_block(struct intel_display *display)
+static void xe_display_rpm_assert_block(const struct drm_device *drm)
{
/* FIXME */
}
-void intel_display_rpm_assert_unblock(struct intel_display *display)
+static void xe_display_rpm_assert_unblock(const struct drm_device *drm)
{
/* FIXME */
}
+
+const struct intel_display_rpm_interface xe_display_rpm_interface = {
+ .get = xe_display_rpm_get,
+ .get_raw = xe_display_rpm_get,
+ .get_if_in_use = xe_display_rpm_get_if_in_use,
+ .get_noresume = xe_display_rpm_get_noresume,
+ .put = xe_display_rpm_put,
+ .put_raw = xe_display_rpm_put,
+ .put_unchecked = xe_display_rpm_put_unchecked,
+ .suspended = xe_display_rpm_suspended,
+ .assert_held = xe_display_rpm_assert_held,
+ .assert_block = xe_display_rpm_assert_block,
+ .assert_unblock = xe_display_rpm_assert_unblock
+};
diff --git a/drivers/gpu/drm/xe/display/xe_display_rpm.h b/drivers/gpu/drm/xe/display/xe_display_rpm.h
new file mode 100644
index 000000000000..0bf9d31e87c1
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_display_rpm.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_DISPLAY_RPM_H_
+#define _XE_DISPLAY_RPM_H_
+
+extern const struct intel_display_rpm_interface xe_display_rpm_interface;
+
+#endif /* _XE_DISPLAY_RPM_H_ */
diff --git a/drivers/gpu/drm/xe/display/xe_display_wa.c b/drivers/gpu/drm/xe/display/xe_display_wa.c
index 8ada1cbcb16c..2aa1b8c03411 100644
--- a/drivers/gpu/drm/xe/display/xe_display_wa.c
+++ b/drivers/gpu/drm/xe/display/xe_display_wa.c
@@ -13,6 +13,7 @@
bool intel_display_needs_wa_16023588340(struct intel_display *display)
{
struct xe_device *xe = to_xe_device(display->drm);
+ struct xe_gt *wa_gt = xe_root_mmio_gt(xe);
- return XE_GT_WA(xe_root_mmio_gt(xe), 16023588340);
+ return wa_gt && XE_GT_WA(wa_gt, 16023588340);
}
diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c
index f32b23338331..df663286092a 100644
--- a/drivers/gpu/drm/xe/display/xe_panic.c
+++ b/drivers/gpu/drm/xe/display/xe_panic.c
@@ -8,20 +8,23 @@
#include "intel_fb.h"
#include "intel_panic.h"
#include "xe_bo.h"
+#include "xe_res_cursor.h"
struct intel_panic {
- struct page **pages;
+ struct xe_res_cursor res;
+ struct iosys_map vmap;
+
int page;
- void *vaddr;
};
static void xe_panic_kunmap(struct intel_panic *panic)
{
- if (panic->vaddr) {
- drm_clflush_virt_range(panic->vaddr, PAGE_SIZE);
- kunmap_local(panic->vaddr);
- panic->vaddr = NULL;
+ if (!panic->vmap.is_iomem && iosys_map_is_set(&panic->vmap)) {
+ drm_clflush_virt_range(panic->vmap.vaddr, PAGE_SIZE);
+ kunmap_local(panic->vmap.vaddr);
}
+ iosys_map_clear(&panic->vmap);
+ panic->page = -1;
}
/*
@@ -46,15 +49,29 @@ static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int
new_page = offset >> PAGE_SHIFT;
offset = offset % PAGE_SIZE;
if (new_page != panic->page) {
- xe_panic_kunmap(panic);
+ if (xe_bo_is_vram(bo)) {
+ /* Display is always mapped on root tile */
+ struct xe_vram_region *vram = xe_bo_device(bo)->mem.vram;
+
+ if (panic->page < 0 || new_page < panic->page) {
+ xe_res_first(bo->ttm.resource, new_page * PAGE_SIZE,
+ bo->ttm.base.size - new_page * PAGE_SIZE, &panic->res);
+ } else {
+ xe_res_next(&panic->res, PAGE_SIZE * (new_page - panic->page));
+ }
+ iosys_map_set_vaddr_iomem(&panic->vmap,
+ vram->mapping + panic->res.start);
+ } else {
+ xe_panic_kunmap(panic);
+ iosys_map_set_vaddr(&panic->vmap,
+ ttm_bo_kmap_try_from_panic(&bo->ttm,
+ new_page));
+ }
panic->page = new_page;
- panic->vaddr = ttm_bo_kmap_try_from_panic(&bo->ttm,
- panic->page);
- }
- if (panic->vaddr) {
- u32 *pix = panic->vaddr + offset;
- *pix = color;
}
+
+ if (iosys_map_is_set(&panic->vmap))
+ iosys_map_wr(&panic->vmap, offset, u32, color);
}
struct intel_panic *intel_panic_alloc(void)
@@ -68,6 +85,12 @@ struct intel_panic *intel_panic_alloc(void)
int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
{
+ struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private;
+ struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base));
+
+ if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo))
+ return -ENODEV;
+
panic->page = -1;
sb->set_pixel = xe_panic_page_set_pixel;
return 0;
@@ -76,5 +99,4 @@ int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb)
void intel_panic_finish(struct intel_panic *panic)
{
xe_panic_kunmap(panic);
- panic->page = -1;
}
diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
index 94f00def811b..12d25c5290fd 100644
--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
+++ b/drivers/gpu/drm/xe/display/xe_plane_initial.c
@@ -25,7 +25,7 @@
#include "xe_vram_types.h"
#include "xe_wa.h"
-#include <generated/xe_wa_oob.h>
+#include <generated/xe_device_wa_oob.h>
void intel_plane_initial_vblank_wait(struct intel_crtc *crtc)
{
@@ -123,7 +123,7 @@ initial_plane_bo(struct xe_device *xe,
phys_base = base;
flags |= XE_BO_FLAG_STOLEN;
- if (XE_GT_WA(xe_root_mmio_gt(xe), 22019338487_display))
+ if (XE_DEVICE_WA(xe, 22019338487_display))
return NULL;
/*
diff --git a/drivers/gpu/drm/xe/display/xe_stolen.c b/drivers/gpu/drm/xe/display/xe_stolen.c
new file mode 100644
index 000000000000..9f04ba36e930
--- /dev/null
+++ b/drivers/gpu/drm/xe/display/xe_stolen.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: MIT
+/* Copyright © 2025 Intel Corporation */
+
+#include "gem/i915_gem_stolen.h"
+#include "xe_res_cursor.h"
+#include "xe_ttm_stolen_mgr.h"
+#include "xe_validation.h"
+
+struct intel_stolen_node {
+ struct xe_device *xe;
+ struct xe_bo *bo;
+};
+
+int i915_gem_stolen_insert_node_in_range(struct intel_stolen_node *node, u64 size,
+ unsigned int align, u64 start, u64 end)
+{
+ struct xe_device *xe = node->xe;
+
+ struct xe_bo *bo;
+ int err = 0;
+ u32 flags = XE_BO_FLAG_PINNED | XE_BO_FLAG_STOLEN;
+
+ if (start < SZ_4K)
+ start = SZ_4K;
+
+ if (align) {
+ size = ALIGN(size, align);
+ start = ALIGN(start, align);
+ }
+
+ bo = xe_bo_create_pin_range_novm(xe, xe_device_get_root_tile(xe),
+ size, start, end, ttm_bo_type_kernel, flags);
+ if (IS_ERR(bo)) {
+ err = PTR_ERR(bo);
+ bo = NULL;
+ return err;
+ }
+
+ node->bo = bo;
+
+ return err;
+}
+
+int i915_gem_stolen_insert_node(struct intel_stolen_node *node, u64 size, unsigned int align)
+{
+ /* Not used on xe */
+ WARN_ON(1);
+
+ return -ENODEV;
+}
+
+void i915_gem_stolen_remove_node(struct intel_stolen_node *node)
+{
+ xe_bo_unpin_map_no_vm(node->bo);
+ node->bo = NULL;
+}
+
+bool i915_gem_stolen_initialized(struct drm_device *drm)
+{
+ struct xe_device *xe = to_xe_device(drm);
+
+ return ttm_manager_type(&xe->ttm, XE_PL_STOLEN);
+}
+
+bool i915_gem_stolen_node_allocated(const struct intel_stolen_node *node)
+{
+ return node->bo;
+}
+
+u32 i915_gem_stolen_node_offset(struct intel_stolen_node *node)
+{
+ struct xe_res_cursor res;
+
+ xe_res_first(node->bo->ttm.resource, 0, 4096, &res);
+ return res.start;
+}
+
+/* Used for < gen4. These are not supported by Xe */
+u64 i915_gem_stolen_area_address(struct drm_device *drm)
+{
+ WARN_ON(1);
+
+ return 0;
+}
+
+/* Used for gen9 specific WA. Gen9 is not supported by Xe */
+u64 i915_gem_stolen_area_size(struct drm_device *drm)
+{
+ WARN_ON(1);
+
+ return 0;
+}
+
+u64 i915_gem_stolen_node_address(struct intel_stolen_node *node)
+{
+ struct xe_device *xe = node->xe;
+
+ return xe_ttm_stolen_gpu_offset(xe) + i915_gem_stolen_node_offset(node);
+}
+
+u64 i915_gem_stolen_node_size(const struct intel_stolen_node *node)
+{
+ return node->bo->ttm.base.size;
+}
+
+struct intel_stolen_node *i915_gem_stolen_node_alloc(struct drm_device *drm)
+{
+ struct xe_device *xe = to_xe_device(drm);
+ struct intel_stolen_node *node;
+
+ node = kzalloc(sizeof(*node), GFP_KERNEL);
+ if (!node)
+ return NULL;
+
+ node->xe = xe;
+
+ return node;
+}
+
+void i915_gem_stolen_node_free(const struct intel_stolen_node *node)
+{
+ kfree(node);
+}
diff --git a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
index 8cfcd3360896..5d41ca297447 100644
--- a/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
+++ b/drivers/gpu/drm/xe/instructions/xe_gpu_commands.h
@@ -31,6 +31,12 @@
#define XY_FAST_COPY_BLT_D1_DST_TILE4 REG_BIT(30)
#define XE2_XY_FAST_COPY_BLT_MOCS_INDEX_MASK GENMASK(23, 20)
+#define MEM_COPY_CMD (2 << 29 | 0x5a << 22 | 0x8)
+#define MEM_COPY_PAGE_COPY_MODE REG_BIT(19)
+#define MEM_COPY_MATRIX_COPY REG_BIT(17)
+#define MEM_COPY_SRC_MOCS_INDEX_MASK GENMASK(31, 28)
+#define MEM_COPY_DST_MOCS_INDEX_MASK GENMASK(6, 3)
+
#define PVC_MEM_SET_CMD (2 << 29 | 0x5b << 22)
#define PVC_MEM_SET_CMD_LEN_DW 7
#define PVC_MEM_SET_MATRIX REG_BIT(17)
diff --git a/drivers/gpu/drm/xe/regs/xe_engine_regs.h b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
index f4c3e1187a00..68172b0248a6 100644
--- a/drivers/gpu/drm/xe/regs/xe_engine_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_engine_regs.h
@@ -141,6 +141,8 @@
#define INHIBIT_SWITCH_UNTIL_PREEMPTED REG_BIT(31)
#define IDLE_DELAY REG_GENMASK(20, 0)
+#define RING_CURRENT_LRCA(base) XE_REG((base) + 0x240)
+
#define RING_CONTEXT_CONTROL(base) XE_REG((base) + 0x244, XE_REG_OPTION_MASKED)
#define CTX_CTRL_PXP_ENABLE REG_BIT(10)
#define CTX_CTRL_OAC_CONTEXT_ENABLE REG_BIT(8)
@@ -153,6 +155,8 @@
#define GFX_DISABLE_LEGACY_MODE REG_BIT(3)
#define GFX_MSIX_INTERRUPT_ENABLE REG_BIT(13)
+#define RING_CSMQDEBUG(base) XE_REG((base) + 0x2b0)
+
#define RING_TIMESTAMP(base) XE_REG((base) + 0x358)
#define RING_TIMESTAMP_UDW(base) XE_REG((base) + 0x358 + 4)
diff --git a/drivers/gpu/drm/xe/regs/xe_gt_regs.h b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
index 51f2a03847f9..917a088c28f2 100644
--- a/drivers/gpu/drm/xe/regs/xe_gt_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_gt_regs.h
@@ -37,6 +37,12 @@
#define GMD_ID XE_REG(0xd8c)
#define GMD_ID_ARCH_MASK REG_GENMASK(31, 22)
#define GMD_ID_RELEASE_MASK REG_GENMASK(21, 14)
+/*
+ * Spec defines these bits as "Reserved", but then make them assume some
+ * meaning that depends on the ARCH. To avoid any confusion, call them
+ * SUBIP_FLAG_MASK.
+ */
+#define GMD_ID_SUBIP_FLAG_MASK REG_GENMASK(13, 6)
#define GMD_ID_REVID REG_GENMASK(5, 0)
#define FORCEWAKE_ACK_GSC XE_REG(0xdf8)
@@ -95,7 +101,6 @@
#define XE2_LMEM_CFG XE_REG(0x48b0)
-#define XEHP_TILE_ADDR_RANGE(_idx) XE_REG_MCR(0x4900 + (_idx) * 4)
#define XEHP_FLAT_CCS_BASE_ADDR XE_REG_MCR(0x4910)
#define XEHP_FLAT_CCS_PTR REG_GENMASK(31, 8)
@@ -168,6 +173,7 @@
#define XEHP_SLICE_COMMON_ECO_CHICKEN1 XE_REG_MCR(0x731c, XE_REG_OPTION_MASKED)
#define MSC_MSAA_REODER_BUF_BYPASS_DISABLE REG_BIT(14)
+#define FAST_CLEAR_VALIGN_FIX REG_BIT(13)
#define XE2LPM_CCCHKNREG1 XE_REG(0x82a8)
@@ -239,6 +245,9 @@
#define XE2_GT_GEOMETRY_DSS_1 XE_REG(0x9150)
#define XE2_GT_GEOMETRY_DSS_2 XE_REG(0x9154)
+#define SERVICE_COPY_ENABLE XE_REG(0x9170)
+#define FUSE_SERVICE_COPY_ENABLE_MASK REG_GENMASK(7, 0)
+
#define GDRST XE_REG(0x941c)
#define GRDOM_GUC REG_BIT(3)
#define GRDOM_FULL REG_BIT(0)
@@ -346,10 +355,6 @@
#define VDN_HCP_POWERGATE_ENABLE(n) REG_BIT(3 + 2 * (n))
#define VDN_MFXVDENC_POWERGATE_ENABLE(n) REG_BIT(4 + 2 * (n))
-#define CTC_MODE XE_REG(0xa26c)
-#define CTC_SHIFT_PARAMETER_MASK REG_GENMASK(2, 1)
-#define CTC_SOURCE_DIVIDE_LOGIC REG_BIT(0)
-
#define FORCEWAKE_RENDER XE_REG(0xa278)
#define POWERGATE_DOMAIN_STATUS XE_REG(0xa2a0)
@@ -545,6 +550,9 @@
#define SARB_CHICKEN1 XE_REG_MCR(0xe90c)
#define COMP_CKN_IN REG_GENMASK(30, 29)
+#define MAIN_GAMCTRL_MODE XE_REG(0xef00)
+#define MAIN_GAMCTRL_QUEUE_SELECT REG_BIT(0)
+
#define RCU_MODE XE_REG(0x14800, XE_REG_OPTION_MASKED)
#define RCU_MODE_FIXED_SLICE_CCS_MODE REG_BIT(1)
#define RCU_MODE_CCS_ENABLE REG_BIT(0)
@@ -581,6 +589,7 @@
#define GT_GFX_RC6 XE_REG(0x138108)
#define GT0_PERF_LIMIT_REASONS XE_REG(0x1381a8)
+/* Common performance limit reason bits - available on all platforms */
#define GT0_PERF_LIMIT_REASONS_MASK 0xde3
#define PROCHOT_MASK REG_BIT(0)
#define THERMAL_LIMIT_MASK REG_BIT(1)
@@ -590,6 +599,18 @@
#define POWER_LIMIT_4_MASK REG_BIT(8)
#define POWER_LIMIT_1_MASK REG_BIT(10)
#define POWER_LIMIT_2_MASK REG_BIT(11)
+/* Platform-specific performance limit reason bits - for Crescent Island */
+#define CRI_PERF_LIMIT_REASONS_MASK 0xfdff
+#define SOC_THERMAL_LIMIT_MASK REG_BIT(1)
+#define MEM_THERMAL_MASK REG_BIT(2)
+#define VR_THERMAL_MASK REG_BIT(3)
+#define ICCMAX_MASK REG_BIT(4)
+#define SOC_AVG_THERMAL_MASK REG_BIT(6)
+#define FASTVMODE_MASK REG_BIT(7)
+#define PSYS_PL1_MASK REG_BIT(12)
+#define PSYS_PL2_MASK REG_BIT(13)
+#define P0_FREQ_MASK REG_BIT(14)
+#define PSYS_CRIT_MASK REG_BIT(15)
#define GT_PERF_STATUS XE_REG(0x1381b4)
#define VOLTAGE_MASK REG_GENMASK(10, 0)
diff --git a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
index af781c8e4a80..f2e455e2bfe4 100644
--- a/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_i2c_regs.h
@@ -14,6 +14,9 @@
#define REG_SG_REMAP_ADDR_PREFIX XE_REG(SOC_BASE + 0x0164)
#define REG_SG_REMAP_ADDR_POSTFIX XE_REG(SOC_BASE + 0x0168)
+#define I2C_BRIDGE_PCICFGCTL XE_REG(I2C_BRIDGE_OFFSET + 0x200)
+#define ACPI_INTR_EN REG_BIT(1)
+
#define I2C_CONFIG_CMD XE_REG(I2C_CONFIG_SPACE_OFFSET + PCI_COMMAND)
#define I2C_CONFIG_PMCSR XE_REG(I2C_CONFIG_SPACE_OFFSET + 0x84)
diff --git a/drivers/gpu/drm/xe/regs/xe_irq_regs.h b/drivers/gpu/drm/xe/regs/xe_irq_regs.h
index 7c2a3a140142..2f97662d958d 100644
--- a/drivers/gpu/drm/xe/regs/xe_irq_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_irq_regs.h
@@ -65,7 +65,10 @@
#define BCS_RSVD_INTR_MASK XE_REG(0x1900a0, XE_REG_OPTION_VF)
#define VCS0_VCS1_INTR_MASK XE_REG(0x1900a8, XE_REG_OPTION_VF)
#define VCS2_VCS3_INTR_MASK XE_REG(0x1900ac, XE_REG_OPTION_VF)
+#define VCS4_VCS5_INTR_MASK XE_REG(0x1900b0, XE_REG_OPTION_VF)
+#define VCS6_VCS7_INTR_MASK XE_REG(0x1900b4, XE_REG_OPTION_VF)
#define VECS0_VECS1_INTR_MASK XE_REG(0x1900d0, XE_REG_OPTION_VF)
+#define VECS2_VECS3_INTR_MASK XE_REG(0x1900d4, XE_REG_OPTION_VF)
#define HECI2_RSVD_INTR_MASK XE_REG(0x1900e4)
#define GUC_SG_INTR_MASK XE_REG(0x1900e8, XE_REG_OPTION_VF)
#define GPM_WGBOXPERF_INTR_MASK XE_REG(0x1900ec, XE_REG_OPTION_VF)
@@ -80,9 +83,10 @@
#define GT_WAIT_SEMAPHORE_INTERRUPT REG_BIT(11)
#define GT_CONTEXT_SWITCH_INTERRUPT REG_BIT(8)
#define GSC_ER_COMPLETE REG_BIT(5)
-#define GT_RENDER_PIPECTL_NOTIFY_INTERRUPT REG_BIT(4)
+#define GT_FLUSH_COMPLETE_INTERRUPT REG_BIT(4)
#define GT_CS_MASTER_ERROR_INTERRUPT REG_BIT(3)
-#define GT_RENDER_USER_INTERRUPT REG_BIT(0)
+#define GT_COMPUTE_WALKER_INTERRUPT REG_BIT(2)
+#define GT_MI_USER_INTERRUPT REG_BIT(0)
/* irqs for OTHER_KCR_INSTANCE */
#define KCR_PXP_STATE_TERMINATED_INTERRUPT REG_BIT(1)
diff --git a/drivers/gpu/drm/xe/regs/xe_pmt.h b/drivers/gpu/drm/xe/regs/xe_pmt.h
index 264e9baf949c..0f79c0714454 100644
--- a/drivers/gpu/drm/xe/regs/xe_pmt.h
+++ b/drivers/gpu/drm/xe/regs/xe_pmt.h
@@ -24,6 +24,7 @@
#define BMG_MODS_RESIDENCY_OFFSET (0x4D0)
#define BMG_G2_RESIDENCY_OFFSET (0x530)
#define BMG_G6_RESIDENCY_OFFSET (0x538)
+#define BMG_G7_RESIDENCY_OFFSET (0x4B0)
#define BMG_G8_RESIDENCY_OFFSET (0x540)
#define BMG_G10_RESIDENCY_OFFSET (0x548)
diff --git a/drivers/gpu/drm/xe/regs/xe_regs.h b/drivers/gpu/drm/xe/regs/xe_regs.h
index 1926b4044314..ad93c57edd17 100644
--- a/drivers/gpu/drm/xe/regs/xe_regs.h
+++ b/drivers/gpu/drm/xe/regs/xe_regs.h
@@ -40,6 +40,8 @@
#define STOLEN_RESERVED XE_REG(0x1082c0)
#define WOPCM_SIZE_MASK REG_GENMASK64(9, 7)
+#define SG_TILE_ADDR_RANGE(_idx) XE_REG(0x1083a0 + (_idx) * 4)
+
#define MTL_RP_STATE_CAP XE_REG(0x138000)
#define MTL_GT_RPA_FREQUENCY XE_REG(0x138008)
diff --git a/drivers/gpu/drm/xe/tests/xe_dma_buf.c b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
index a7e548a2bdfb..5df98de5ba3c 100644
--- a/drivers/gpu/drm/xe/tests/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/tests/xe_dma_buf.c
@@ -31,6 +31,7 @@ static void check_residency(struct kunit *test, struct xe_bo *exported,
struct drm_exec *exec)
{
struct dma_buf_test_params *params = to_dma_buf_test_params(test->priv);
+ struct dma_buf_attachment *attach;
u32 mem_type;
int ret;
@@ -46,7 +47,7 @@ static void check_residency(struct kunit *test, struct xe_bo *exported,
mem_type = XE_PL_TT;
else if (params->force_different_devices && !is_dynamic(params) &&
(params->mem_mask & XE_BO_FLAG_SYSTEM))
- /* Pin migrated to TT */
+ /* Pin migrated to TT on non-dynamic attachments. */
mem_type = XE_PL_TT;
if (!xe_bo_is_mem_type(exported, mem_type)) {
@@ -88,6 +89,18 @@ static void check_residency(struct kunit *test, struct xe_bo *exported,
KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, mem_type));
+ /* Check that we can pin without migrating. */
+ attach = list_first_entry_or_null(&dmabuf->attachments, typeof(*attach), node);
+ if (attach) {
+ int err = dma_buf_pin(attach);
+
+ if (!err) {
+ KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(exported, mem_type));
+ dma_buf_unpin(attach);
+ }
+ KUNIT_EXPECT_EQ(test, err, 0);
+ }
+
if (params->force_different_devices)
KUNIT_EXPECT_TRUE(test, xe_bo_is_mem_type(imported, XE_PL_TT));
else
@@ -150,7 +163,7 @@ static void xe_test_dmabuf_import_same_driver(struct xe_device *xe)
xe_bo_lock(import_bo, false);
err = xe_bo_validate(import_bo, NULL, false, exec);
- /* Pinning in VRAM is not allowed. */
+ /* Pinning in VRAM is not allowed for non-dynamic attachments */
if (!is_dynamic(params) &&
params->force_different_devices &&
!(params->mem_mask & XE_BO_FLAG_SYSTEM))
diff --git a/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_config_kunit.c b/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_config_kunit.c
new file mode 100644
index 000000000000..42bfc4bcfbcf
--- /dev/null
+++ b/drivers/gpu/drm/xe/tests/xe_gt_sriov_pf_config_kunit.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0 AND MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <kunit/static_stub.h>
+#include <kunit/test.h>
+#include <kunit/test-bug.h>
+
+#include "xe_kunit_helpers.h"
+#include "xe_pci_test.h"
+
+#define TEST_MAX_VFS 63
+
+static void pf_set_admin_mode(struct xe_device *xe, bool enable)
+{
+ /* should match logic of xe_sriov_pf_admin_only() */
+ xe->info.probe_display = !enable;
+ KUNIT_EXPECT_EQ(kunit_get_current_test(), enable, xe_sriov_pf_admin_only(xe));
+}
+
+static const void *num_vfs_gen_param(struct kunit *test, const void *prev, char *desc)
+{
+ unsigned long next = 1 + (unsigned long)prev;
+
+ if (next > TEST_MAX_VFS)
+ return NULL;
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%lu VF%s",
+ next, str_plural(next));
+ return (void *)next;
+}
+
+static int pf_gt_config_test_init(struct kunit *test)
+{
+ struct xe_pci_fake_data fake = {
+ .sriov_mode = XE_SRIOV_MODE_PF,
+ .platform = XE_TIGERLAKE, /* any random platform with SR-IOV */
+ .subplatform = XE_SUBPLATFORM_NONE,
+ };
+ struct xe_device *xe;
+ struct xe_gt *gt;
+
+ test->priv = &fake;
+ xe_kunit_helper_xe_device_test_init(test);
+
+ xe = test->priv;
+ KUNIT_ASSERT_TRUE(test, IS_SRIOV_PF(xe));
+
+ gt = xe_root_mmio_gt(xe);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gt);
+ test->priv = gt;
+
+ /* pretend it can support up to 63 VFs */
+ xe->sriov.pf.device_total_vfs = TEST_MAX_VFS;
+ xe->sriov.pf.driver_max_vfs = TEST_MAX_VFS;
+ KUNIT_ASSERT_EQ(test, xe_sriov_pf_get_totalvfs(xe), 63);
+
+ pf_set_admin_mode(xe, false);
+ KUNIT_ASSERT_EQ(test, xe_sriov_init(xe), 0);
+
+ /* more sanity checks */
+ KUNIT_EXPECT_EQ(test, GUC_ID_MAX + 1, SZ_64K);
+ KUNIT_EXPECT_EQ(test, GUC_NUM_DOORBELLS, SZ_256);
+
+ return 0;
+}
+
+static void fair_contexts_1vf(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ struct xe_device *xe = gt_to_xe(gt);
+
+ pf_set_admin_mode(xe, false);
+ KUNIT_ASSERT_FALSE(test, xe_sriov_pf_admin_only(xe));
+ KUNIT_EXPECT_EQ(test, SZ_32K, pf_profile_fair_ctxs(gt, 1));
+
+ pf_set_admin_mode(xe, true);
+ KUNIT_ASSERT_TRUE(test, xe_sriov_pf_admin_only(xe));
+ KUNIT_EXPECT_EQ(test, SZ_64K - SZ_1K, pf_profile_fair_ctxs(gt, 1));
+}
+
+static void fair_contexts(struct kunit *test)
+{
+ unsigned int num_vfs = (unsigned long)test->param_value;
+ struct xe_gt *gt = test->priv;
+ struct xe_device *xe = gt_to_xe(gt);
+
+ pf_set_admin_mode(xe, false);
+ KUNIT_ASSERT_FALSE(test, xe_sriov_pf_admin_only(xe));
+
+ KUNIT_EXPECT_TRUE(test, is_power_of_2(pf_profile_fair_ctxs(gt, num_vfs)));
+ KUNIT_EXPECT_GT(test, GUC_ID_MAX, num_vfs * pf_profile_fair_ctxs(gt, num_vfs));
+
+ if (num_vfs > 31)
+ KUNIT_ASSERT_EQ(test, SZ_1K, pf_profile_fair_ctxs(gt, num_vfs));
+ else if (num_vfs > 15)
+ KUNIT_ASSERT_EQ(test, SZ_2K, pf_profile_fair_ctxs(gt, num_vfs));
+ else if (num_vfs > 7)
+ KUNIT_ASSERT_EQ(test, SZ_4K, pf_profile_fair_ctxs(gt, num_vfs));
+ else if (num_vfs > 3)
+ KUNIT_ASSERT_EQ(test, SZ_8K, pf_profile_fair_ctxs(gt, num_vfs));
+ else if (num_vfs > 1)
+ KUNIT_ASSERT_EQ(test, SZ_16K, pf_profile_fair_ctxs(gt, num_vfs));
+ else
+ KUNIT_ASSERT_EQ(test, SZ_32K, pf_profile_fair_ctxs(gt, num_vfs));
+}
+
+static void fair_doorbells_1vf(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ struct xe_device *xe = gt_to_xe(gt);
+
+ pf_set_admin_mode(xe, false);
+ KUNIT_ASSERT_FALSE(test, xe_sriov_pf_admin_only(xe));
+ KUNIT_EXPECT_EQ(test, 128, pf_profile_fair_dbs(gt, 1));
+
+ pf_set_admin_mode(xe, true);
+ KUNIT_ASSERT_TRUE(test, xe_sriov_pf_admin_only(xe));
+ KUNIT_EXPECT_EQ(test, 240, pf_profile_fair_dbs(gt, 1));
+}
+
+static void fair_doorbells(struct kunit *test)
+{
+ unsigned int num_vfs = (unsigned long)test->param_value;
+ struct xe_gt *gt = test->priv;
+ struct xe_device *xe = gt_to_xe(gt);
+
+ pf_set_admin_mode(xe, false);
+ KUNIT_ASSERT_FALSE(test, xe_sriov_pf_admin_only(xe));
+
+ KUNIT_EXPECT_TRUE(test, is_power_of_2(pf_profile_fair_dbs(gt, num_vfs)));
+ KUNIT_EXPECT_GE(test, GUC_NUM_DOORBELLS, (num_vfs + 1) * pf_profile_fair_dbs(gt, num_vfs));
+
+ if (num_vfs > 31)
+ KUNIT_ASSERT_EQ(test, SZ_4, pf_profile_fair_dbs(gt, num_vfs));
+ else if (num_vfs > 15)
+ KUNIT_ASSERT_EQ(test, SZ_8, pf_profile_fair_dbs(gt, num_vfs));
+ else if (num_vfs > 7)
+ KUNIT_ASSERT_EQ(test, SZ_16, pf_profile_fair_dbs(gt, num_vfs));
+ else if (num_vfs > 3)
+ KUNIT_ASSERT_EQ(test, SZ_32, pf_profile_fair_dbs(gt, num_vfs));
+ else if (num_vfs > 1)
+ KUNIT_ASSERT_EQ(test, SZ_64, pf_profile_fair_dbs(gt, num_vfs));
+ else
+ KUNIT_ASSERT_EQ(test, SZ_128, pf_profile_fair_dbs(gt, num_vfs));
+}
+
+static void fair_ggtt_1vf(struct kunit *test)
+{
+ struct xe_gt *gt = test->priv;
+ struct xe_device *xe = gt_to_xe(gt);
+
+ pf_set_admin_mode(xe, false);
+ KUNIT_ASSERT_FALSE(test, xe_sriov_pf_admin_only(xe));
+ KUNIT_EXPECT_EQ(test, SZ_2G, pf_profile_fair_ggtt(gt, 1));
+
+ pf_set_admin_mode(xe, true);
+ KUNIT_ASSERT_TRUE(test, xe_sriov_pf_admin_only(xe));
+ KUNIT_EXPECT_EQ(test, SZ_2G + SZ_1G + SZ_512M, pf_profile_fair_ggtt(gt, 1));
+}
+
+static void fair_ggtt(struct kunit *test)
+{
+ unsigned int num_vfs = (unsigned long)test->param_value;
+ struct xe_gt *gt = test->priv;
+ struct xe_device *xe = gt_to_xe(gt);
+ u64 alignment = pf_get_ggtt_alignment(gt);
+ u64 shareable = SZ_2G + SZ_1G + SZ_512M;
+
+ pf_set_admin_mode(xe, false);
+ KUNIT_ASSERT_FALSE(test, xe_sriov_pf_admin_only(xe));
+
+ KUNIT_EXPECT_TRUE(test, IS_ALIGNED(pf_profile_fair_ggtt(gt, num_vfs), alignment));
+ KUNIT_EXPECT_GE(test, shareable, num_vfs * pf_profile_fair_ggtt(gt, num_vfs));
+
+ if (num_vfs > 56)
+ KUNIT_ASSERT_EQ(test, SZ_64M - SZ_8M, pf_profile_fair_ggtt(gt, num_vfs));
+ else if (num_vfs > 28)
+ KUNIT_ASSERT_EQ(test, SZ_64M, pf_profile_fair_ggtt(gt, num_vfs));
+ else if (num_vfs > 14)
+ KUNIT_ASSERT_EQ(test, SZ_128M, pf_profile_fair_ggtt(gt, num_vfs));
+ else if (num_vfs > 7)
+ KUNIT_ASSERT_EQ(test, SZ_256M, pf_profile_fair_ggtt(gt, num_vfs));
+ else if (num_vfs > 3)
+ KUNIT_ASSERT_EQ(test, SZ_512M, pf_profile_fair_ggtt(gt, num_vfs));
+ else if (num_vfs > 1)
+ KUNIT_ASSERT_EQ(test, SZ_1G, pf_profile_fair_ggtt(gt, num_vfs));
+ else
+ KUNIT_ASSERT_EQ(test, SZ_2G, pf_profile_fair_ggtt(gt, num_vfs));
+}
+
+static struct kunit_case pf_gt_config_test_cases[] = {
+ KUNIT_CASE(fair_contexts_1vf),
+ KUNIT_CASE(fair_doorbells_1vf),
+ KUNIT_CASE(fair_ggtt_1vf),
+ KUNIT_CASE_PARAM(fair_contexts, num_vfs_gen_param),
+ KUNIT_CASE_PARAM(fair_doorbells, num_vfs_gen_param),
+ KUNIT_CASE_PARAM(fair_ggtt, num_vfs_gen_param),
+ {}
+};
+
+static struct kunit_suite pf_gt_config_suite = {
+ .name = "pf_gt_config",
+ .test_cases = pf_gt_config_test_cases,
+ .init = pf_gt_config_test_init,
+};
+
+kunit_test_suite(pf_gt_config_suite);
diff --git a/drivers/gpu/drm/xe/tests/xe_mocs.c b/drivers/gpu/drm/xe/tests/xe_mocs.c
index 0e502feaca81..6bb278167aaf 100644
--- a/drivers/gpu/drm/xe/tests/xe_mocs.c
+++ b/drivers/gpu/drm/xe/tests/xe_mocs.c
@@ -49,7 +49,7 @@ static void read_l3cc_table(struct xe_gt *gt,
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
xe_force_wake_put(gt_to_fw(gt), fw_ref);
- KUNIT_ASSERT_TRUE_MSG(test, true, "Forcewake Failed.\n");
+ KUNIT_FAIL_AND_ABORT(test, "Forcewake Failed.\n");
}
for (i = 0; i < info->num_mocs_regs; i++) {
diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c
index 663a79ec960d..f3179b31f13e 100644
--- a/drivers/gpu/drm/xe/tests/xe_pci.c
+++ b/drivers/gpu/drm/xe/tests/xe_pci.c
@@ -311,8 +311,8 @@ const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc
}
EXPORT_SYMBOL_IF_KUNIT(xe_pci_id_gen_param);
-static void fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
- u32 *ver, u32 *revid)
+static int fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
+ u32 *ver, u32 *revid)
{
struct kunit *test = kunit_get_current_test();
struct xe_pci_fake_data *data = test->priv;
@@ -324,6 +324,8 @@ static void fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type,
*ver = data->graphics_verx100;
*revid = xe_step_to_gmdid(data->step.graphics);
}
+
+ return 0;
}
static void fake_xe_info_probe_tile_count(struct xe_device *xe)
diff --git a/drivers/gpu/drm/xe/tests/xe_pci_test.c b/drivers/gpu/drm/xe/tests/xe_pci_test.c
index 37b344df2dc3..4d10a7e2b570 100644
--- a/drivers/gpu/drm/xe/tests/xe_pci_test.c
+++ b/drivers/gpu/drm/xe/tests/xe_pci_test.c
@@ -44,21 +44,27 @@ static void check_media_ip(struct kunit *test)
KUNIT_ASSERT_EQ(test, mask, 0);
}
-static void check_platform_gt_count(struct kunit *test)
+static void check_platform_desc(struct kunit *test)
{
const struct pci_device_id *pci = test->param_value;
const struct xe_device_desc *desc =
(const struct xe_device_desc *)pci->driver_data;
- int max_gt = desc->max_gt_per_tile;
- KUNIT_ASSERT_GT(test, max_gt, 0);
- KUNIT_ASSERT_LE(test, max_gt, XE_MAX_GT_PER_TILE);
+ KUNIT_EXPECT_GT(test, desc->dma_mask_size, 0);
+
+ KUNIT_EXPECT_GT(test, (unsigned int)desc->max_gt_per_tile, 0);
+ KUNIT_EXPECT_LE(test, (unsigned int)desc->max_gt_per_tile, XE_MAX_GT_PER_TILE);
+
+ KUNIT_EXPECT_GT(test, desc->va_bits, 0);
+ KUNIT_EXPECT_LE(test, desc->va_bits, 64);
+
+ KUNIT_EXPECT_GT(test, desc->vm_max_level, 0);
}
static struct kunit_case xe_pci_tests[] = {
KUNIT_CASE_PARAM(check_graphics_ip, xe_pci_graphics_ip_gen_param),
KUNIT_CASE_PARAM(check_media_ip, xe_pci_media_ip_gen_param),
- KUNIT_CASE_PARAM(check_platform_gt_count, xe_pci_id_gen_param),
+ KUNIT_CASE_PARAM(check_platform_desc, xe_pci_id_gen_param),
{}
};
diff --git a/drivers/gpu/drm/xe/tests/xe_rtp_test.c b/drivers/gpu/drm/xe/tests/xe_rtp_test.c
index b0254b014fe4..d2255a59e58f 100644
--- a/drivers/gpu/drm/xe/tests/xe_rtp_test.c
+++ b/drivers/gpu/drm/xe/tests/xe_rtp_test.c
@@ -48,12 +48,14 @@ struct rtp_test_case {
const struct xe_rtp_entry *entries;
};
-static bool match_yes(const struct xe_gt *gt, const struct xe_hw_engine *hwe)
+static bool match_yes(const struct xe_device *xe, const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe)
{
return true;
}
-static bool match_no(const struct xe_gt *gt, const struct xe_hw_engine *hwe)
+static bool match_no(const struct xe_device *xe, const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe)
{
return false;
}
diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c
index 4410e28dee54..b0bd31d14bb9 100644
--- a/drivers/gpu/drm/xe/xe_bo.c
+++ b/drivers/gpu/drm/xe/xe_bo.c
@@ -9,6 +9,7 @@
#include <linux/nospec.h>
#include <drm/drm_drv.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_gem_ttm_helper.h>
#include <drm/drm_managed.h>
#include <drm/ttm/ttm_backup.h>
@@ -34,6 +35,7 @@
#include "xe_res_cursor.h"
#include "xe_shrinker.h"
#include "xe_sriov_vf_ccs.h"
+#include "xe_tile.h"
#include "xe_trace_bo.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_vm.h"
@@ -81,6 +83,10 @@ static struct ttm_placement tt_placement = {
.placement = tt_placement_flags,
};
+#define for_each_set_bo_vram_flag(bit__, bo_flags__) \
+ for (unsigned int __bit_tmp = BIT(0); __bit_tmp <= XE_BO_FLAG_VRAM_MASK; __bit_tmp <<= 1) \
+ for_each_if(((bit__) = __bit_tmp) & (bo_flags__) & XE_BO_FLAG_VRAM_MASK)
+
bool mem_type_is_vram(u32 mem_type)
{
return mem_type >= XE_PL_VRAM0 && mem_type != XE_PL_STOLEN;
@@ -213,6 +219,27 @@ static bool force_contiguous(u32 bo_flags)
bo_flags & XE_BO_FLAG_PINNED;
}
+static u8 vram_bo_flag_to_tile_id(struct xe_device *xe, u32 vram_bo_flag)
+{
+ xe_assert(xe, vram_bo_flag & XE_BO_FLAG_VRAM_MASK);
+ xe_assert(xe, (vram_bo_flag & (vram_bo_flag - 1)) == 0);
+
+ return __ffs(vram_bo_flag >> (__ffs(XE_BO_FLAG_VRAM0) - 1)) - 1;
+}
+
+static u32 bo_vram_flags_to_vram_placement(struct xe_device *xe, u32 bo_flags, u32 vram_flag,
+ enum ttm_bo_type type)
+{
+ u8 tile_id = vram_bo_flag_to_tile_id(xe, vram_flag);
+
+ xe_assert(xe, tile_id < xe->info.tile_count);
+
+ if (type == ttm_bo_type_kernel && !(bo_flags & XE_BO_FLAG_FORCE_USER_VRAM))
+ return xe->tiles[tile_id].mem.kernel_vram->placement;
+ else
+ return xe->tiles[tile_id].mem.vram->placement;
+}
+
static void add_vram(struct xe_device *xe, struct xe_bo *bo,
struct ttm_place *places, u32 bo_flags, u32 mem_type, u32 *c)
{
@@ -245,12 +272,15 @@ static void add_vram(struct xe_device *xe, struct xe_bo *bo,
}
static void try_add_vram(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags, u32 *c)
+ u32 bo_flags, enum ttm_bo_type type, u32 *c)
{
- if (bo_flags & XE_BO_FLAG_VRAM0)
- add_vram(xe, bo, bo->placements, bo_flags, XE_PL_VRAM0, c);
- if (bo_flags & XE_BO_FLAG_VRAM1)
- add_vram(xe, bo, bo->placements, bo_flags, XE_PL_VRAM1, c);
+ u32 vram_flag;
+
+ for_each_set_bo_vram_flag(vram_flag, bo_flags) {
+ u32 pl = bo_vram_flags_to_vram_placement(xe, bo_flags, vram_flag, type);
+
+ add_vram(xe, bo, bo->placements, bo_flags, pl, c);
+ }
}
static void try_add_stolen(struct xe_device *xe, struct xe_bo *bo,
@@ -269,11 +299,11 @@ static void try_add_stolen(struct xe_device *xe, struct xe_bo *bo,
}
static int __xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags)
+ u32 bo_flags, enum ttm_bo_type type)
{
u32 c = 0;
- try_add_vram(xe, bo, bo_flags, &c);
+ try_add_vram(xe, bo, bo_flags, type, &c);
try_add_system(xe, bo, bo_flags, &c);
try_add_stolen(xe, bo, bo_flags, &c);
@@ -289,10 +319,10 @@ static int __xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
}
int xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags)
+ u32 bo_flags, enum ttm_bo_type type)
{
xe_bo_assert_held(bo);
- return __xe_bo_placement_for_flags(xe, bo, bo_flags);
+ return __xe_bo_placement_for_flags(xe, bo, bo_flags, type);
}
static void xe_evict_flags(struct ttm_buffer_object *tbo,
@@ -580,6 +610,23 @@ static bool xe_ttm_resource_visible(struct ttm_resource *mem)
return vres->used_visible_size == mem->size;
}
+/**
+ * xe_bo_is_visible_vram - check if BO is placed entirely in visible VRAM.
+ * @bo: The BO
+ *
+ * This function checks whether a given BO resides entirely in memory visible from the CPU
+ *
+ * Returns: true if the BO is entirely visible, false otherwise.
+ *
+ */
+bool xe_bo_is_visible_vram(struct xe_bo *bo)
+{
+ if (drm_WARN_ON(bo->ttm.base.dev, !xe_bo_is_vram(bo)))
+ return false;
+
+ return xe_ttm_resource_visible(bo->ttm.resource);
+}
+
static int xe_ttm_io_mem_reserve(struct ttm_device *bdev,
struct ttm_resource *mem)
{
@@ -1605,7 +1652,7 @@ static int xe_ttm_access_memory(struct ttm_buffer_object *ttm_bo,
if (!mem_type_is_vram(ttm_bo->resource->mem_type))
return -EIO;
- if (!xe_ttm_resource_visible(ttm_bo->resource) || len >= SZ_16K) {
+ if (!xe_bo_is_visible_vram(bo) || len >= SZ_16K) {
struct xe_migrate *migrate =
mem_type_to_migrate(xe, ttm_bo->resource->mem_type);
@@ -1708,7 +1755,7 @@ static void xe_gem_object_free(struct drm_gem_object *obj)
* refcount directly if needed.
*/
__xe_bo_vunmap(gem_to_xe_bo(obj));
- ttm_bo_put(container_of(obj, struct ttm_buffer_object, base));
+ ttm_bo_fini(container_of(obj, struct ttm_buffer_object, base));
}
static void xe_gem_object_close(struct drm_gem_object *obj,
@@ -2075,7 +2122,7 @@ void xe_bo_free(struct xe_bo *bo)
* if the function should allocate a new one.
* @tile: The tile to select for migration of this bo, and the tile used for
* GGTT binding if any. Only to be non-NULL for ttm_bo_type_kernel bos.
- * @resv: Pointer to a locked shared reservation object to use fo this bo,
+ * @resv: Pointer to a locked shared reservation object to use for this bo,
* or NULL for the xe_bo to use its own.
* @bulk: The bulk move to use for LRU bumping, or NULL for external bos.
* @size: The storage size to use for the bo.
@@ -2164,7 +2211,7 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
xe_validation_assert_exec(xe, exec, &bo->ttm.base);
if (!(flags & XE_BO_FLAG_FIXED_PLACEMENT)) {
- err = __xe_bo_placement_for_flags(xe, bo, bo->flags);
+ err = __xe_bo_placement_for_flags(xe, bo, bo->flags, type);
if (WARN_ON(err)) {
xe_ttm_bo_destroy(&bo->ttm);
return ERR_PTR(err);
@@ -2222,34 +2269,37 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
}
static int __xe_bo_fixed_placement(struct xe_device *xe,
- struct xe_bo *bo,
+ struct xe_bo *bo, enum ttm_bo_type type,
u32 flags,
u64 start, u64 end, u64 size)
{
struct ttm_place *place = bo->placements;
+ u32 vram_flag, vram_stolen_flags;
+
+ /*
+ * to allow fixed placement in GGTT of a VF, post-migration fixups would have to
+ * include selecting a new fixed offset and shifting the page ranges for it
+ */
+ xe_assert(xe, !IS_SRIOV_VF(xe) || !(bo->flags & XE_BO_FLAG_GGTT));
if (flags & (XE_BO_FLAG_USER | XE_BO_FLAG_SYSTEM))
return -EINVAL;
+ vram_flag = flags & XE_BO_FLAG_VRAM_MASK;
+ vram_stolen_flags = (flags & (XE_BO_FLAG_STOLEN)) | vram_flag;
+
+ /* check if more than one VRAM/STOLEN flag is set */
+ if (hweight32(vram_stolen_flags) > 1)
+ return -EINVAL;
+
place->flags = TTM_PL_FLAG_CONTIGUOUS;
place->fpfn = start >> PAGE_SHIFT;
place->lpfn = end >> PAGE_SHIFT;
- switch (flags & (XE_BO_FLAG_STOLEN | XE_BO_FLAG_VRAM_MASK)) {
- case XE_BO_FLAG_VRAM0:
- place->mem_type = XE_PL_VRAM0;
- break;
- case XE_BO_FLAG_VRAM1:
- place->mem_type = XE_PL_VRAM1;
- break;
- case XE_BO_FLAG_STOLEN:
+ if (flags & XE_BO_FLAG_STOLEN)
place->mem_type = XE_PL_STOLEN;
- break;
-
- default:
- /* 0 or multiple of the above set */
- return -EINVAL;
- }
+ else
+ place->mem_type = bo_vram_flags_to_vram_placement(xe, flags, vram_flag, type);
bo->placement = (struct ttm_placement) {
.num_placement = 1,
@@ -2278,7 +2328,7 @@ __xe_bo_create_locked(struct xe_device *xe,
return bo;
flags |= XE_BO_FLAG_FIXED_PLACEMENT;
- err = __xe_bo_fixed_placement(xe, bo, flags, start, end, size);
+ err = __xe_bo_fixed_placement(xe, bo, type, flags, start, end, size);
if (err) {
xe_bo_free(bo);
return ERR_PTR(err);
@@ -2602,7 +2652,7 @@ struct xe_bo *xe_bo_create_pin_map(struct xe_device *xe, struct xe_tile *tile,
* @size: The storage size to use for the bo.
* @type: The TTM buffer object type.
* @flags: XE_BO_FLAG_ flags.
- * @intr: Whether to execut any waits for backing store interruptible.
+ * @intr: Whether to execute any waits for backing store interruptible.
*
* Create a pinned and mapped bo. The bo will be external and not associated
* with a VM.
@@ -3577,14 +3627,13 @@ int xe_bo_dumb_create(struct drm_file *file_priv,
struct xe_device *xe = to_xe_device(dev);
struct xe_bo *bo;
uint32_t handle;
- int cpp = DIV_ROUND_UP(args->bpp, 8);
int err;
u32 page_size = max_t(u32, PAGE_SIZE,
xe->info.vram_flags & XE_VRAM_FLAGS_NEED64K ? SZ_64K : SZ_4K);
- args->pitch = ALIGN(args->width * cpp, 64);
- args->size = ALIGN(mul_u32_u32(args->pitch, args->height),
- page_size);
+ err = drm_mode_size_dumb(dev, args, SZ_64, page_size);
+ if (err)
+ return err;
bo = xe_bo_create_user(xe, NULL, args->size,
DRM_XE_GEM_CPU_CACHING_WC,
diff --git a/drivers/gpu/drm/xe/xe_bo.h b/drivers/gpu/drm/xe/xe_bo.h
index a77af42b5f9e..911d5b90461a 100644
--- a/drivers/gpu/drm/xe/xe_bo.h
+++ b/drivers/gpu/drm/xe/xe_bo.h
@@ -49,6 +49,7 @@
#define XE_BO_FLAG_GGTT2 BIT(22)
#define XE_BO_FLAG_GGTT3 BIT(23)
#define XE_BO_FLAG_CPU_ADDR_MIRROR BIT(24)
+#define XE_BO_FLAG_FORCE_USER_VRAM BIT(25)
/* this one is trigger internally only */
#define XE_BO_FLAG_INTERNAL_TEST BIT(30)
@@ -122,7 +123,7 @@ struct xe_bo *xe_managed_bo_create_from_data(struct xe_device *xe, struct xe_til
int xe_managed_bo_reinit_in_vram(struct xe_device *xe, struct xe_tile *tile, struct xe_bo **src);
int xe_bo_placement_for_flags(struct xe_device *xe, struct xe_bo *bo,
- u32 bo_flags);
+ u32 bo_flags, enum ttm_bo_type type);
static inline struct xe_bo *ttm_to_xe_bo(const struct ttm_buffer_object *bo)
{
@@ -273,6 +274,7 @@ int xe_bo_read(struct xe_bo *bo, u64 offset, void *dst, int size);
bool mem_type_is_vram(u32 mem_type);
bool xe_bo_is_vram(struct xe_bo *bo);
+bool xe_bo_is_visible_vram(struct xe_bo *bo);
bool xe_bo_is_stolen(struct xe_bo *bo);
bool xe_bo_is_stolen_devmem(struct xe_bo *bo);
bool xe_bo_is_vm_bound(struct xe_bo *bo);
diff --git a/drivers/gpu/drm/xe/xe_bo_doc.h b/drivers/gpu/drm/xe/xe_bo_doc.h
index 25a884c64bf1..401e7dd26ef3 100644
--- a/drivers/gpu/drm/xe/xe_bo_doc.h
+++ b/drivers/gpu/drm/xe/xe_bo_doc.h
@@ -12,7 +12,7 @@
* BO management
* =============
*
- * TTM manages (placement, eviction, etc...) all BOs in XE.
+ * TTM manages (placement, eviction, etc...) all BOs in Xe.
*
* BO creation
* ===========
@@ -29,7 +29,7 @@
* a kernel BO (e.g. engine state, memory for page tables, etc...). These BOs
* are typically mapped in the GGTT (any kernel BOs aside memory for page tables
* are in the GGTT), are pinned (can't move or be evicted at runtime), have a
- * vmap (XE can access the memory via xe_map layer) and have contiguous physical
+ * vmap (Xe can access the memory via xe_map layer) and have contiguous physical
* memory.
*
* More details of why kernel BOs are pinned and contiguous below.
@@ -40,7 +40,7 @@
* A user BO is created via the DRM_IOCTL_XE_GEM_CREATE IOCTL. Once it is
* created the BO can be mmap'd (via DRM_IOCTL_XE_GEM_MMAP_OFFSET) for user
* access and it can be bound for GPU access (via DRM_IOCTL_XE_VM_BIND). All
- * user BOs are evictable and user BOs are never pinned by XE. The allocation of
+ * user BOs are evictable and user BOs are never pinned by Xe. The allocation of
* the backing store can be deferred from creation time until first use which is
* either mmap, bind, or pagefault.
*
@@ -84,7 +84,7 @@
* ====================
*
* All eviction (or in other words, moving a BO from one memory location to
- * another) is routed through TTM with a callback into XE.
+ * another) is routed through TTM with a callback into Xe.
*
* Runtime eviction
* ----------------
diff --git a/drivers/gpu/drm/xe/xe_bo_evict.c b/drivers/gpu/drm/xe/xe_bo_evict.c
index bc5b4c5fab81..7661fca7f278 100644
--- a/drivers/gpu/drm/xe/xe_bo_evict.c
+++ b/drivers/gpu/drm/xe/xe_bo_evict.c
@@ -73,6 +73,11 @@ int xe_bo_notifier_prepare_all_pinned(struct xe_device *xe)
&xe->pinned.late.kernel_bo_present,
xe_bo_notifier_prepare_pinned);
+ if (!ret)
+ ret = xe_bo_apply_to_pinned(xe, &xe->pinned.late.external,
+ &xe->pinned.late.external,
+ xe_bo_notifier_prepare_pinned);
+
return ret;
}
@@ -93,6 +98,10 @@ void xe_bo_notifier_unprepare_all_pinned(struct xe_device *xe)
(void)xe_bo_apply_to_pinned(xe, &xe->pinned.late.kernel_bo_present,
&xe->pinned.late.kernel_bo_present,
xe_bo_notifier_unprepare_pinned);
+
+ (void)xe_bo_apply_to_pinned(xe, &xe->pinned.late.external,
+ &xe->pinned.late.external,
+ xe_bo_notifier_unprepare_pinned);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_configfs.c b/drivers/gpu/drm/xe/xe_configfs.c
index 139663423185..9f6251b1008b 100644
--- a/drivers/gpu/drm/xe/xe_configfs.c
+++ b/drivers/gpu/drm/xe/xe_configfs.c
@@ -15,9 +15,11 @@
#include "instructions/xe_mi_commands.h"
#include "xe_configfs.h"
+#include "xe_gt_types.h"
#include "xe_hw_engine_types.h"
#include "xe_module.h"
#include "xe_pci_types.h"
+#include "xe_sriov_types.h"
/**
* DOC: Xe Configfs
@@ -25,7 +27,7 @@
* Overview
* ========
*
- * Configfs is a filesystem-based manager of kernel objects. XE KMD registers a
+ * Configfs is a filesystem-based manager of kernel objects. Xe KMD registers a
* configfs subsystem called ``xe`` that creates a directory in the mounted
* configfs directory. The user can create devices under this directory and
* configure them as necessary. See Documentation/filesystems/configfs.rst for
@@ -56,6 +58,7 @@
* :
* └── 0000:03:00.0
* ├── survivability_mode
+ * ├── gt_types_allowed
* ├── engines_allowed
* └── enable_psmi
*
@@ -79,6 +82,44 @@
*
* This attribute can only be set before binding to the device.
*
+ * Allowed GT types:
+ * -----------------
+ *
+ * Allow only specific types of GTs to be detected and initialized by the
+ * driver. Any combination of GT types can be enabled/disabled, although
+ * some settings will cause the device to fail to probe.
+ *
+ * Writes support both comma- and newline-separated input format. Reads
+ * will always return one GT type per line. "primary" and "media" are the
+ * GT type names supported by this interface.
+ *
+ * This attribute can only be set before binding to the device.
+ *
+ * Examples:
+ *
+ * Allow both primary and media GTs to be initialized and used. This matches
+ * the driver's default behavior::
+ *
+ * # echo 'primary,media' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
+ * Allow only the primary GT of each tile to be initialized and used,
+ * effectively disabling the media GT if it exists on the platform::
+ *
+ * # echo 'primary' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
+ * Allow only the media GT of each tile to be initialized and used,
+ * effectively disabling the primary GT. **This configuration will cause
+ * device probe failure on all current platforms, but may be allowed on
+ * igpu platforms in the future**::
+ *
+ * # echo 'media' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
+ * Disable all GTs. Only other GPU IP (such as display) is potentially usable.
+ * **This configuration will cause device probe failure on all current
+ * platforms, but may be allowed on igpu platforms in the future**::
+ *
+ * # echo '' > /sys/kernel/config/xe/0000:03:00.0/gt_types_allowed
+ *
* Allowed engines:
* ----------------
*
@@ -169,6 +210,32 @@
* Currently this is implemented only for post and mid context restore and
* these attributes can only be set before binding to the device.
*
+ * Max SR-IOV Virtual Functions
+ * ----------------------------
+ *
+ * This config allows to limit number of the Virtual Functions (VFs) that can
+ * be managed by the Physical Function (PF) driver, where value 0 disables the
+ * PF mode (no VFs).
+ *
+ * The default max_vfs config value is taken from the max_vfs modparam.
+ *
+ * How to enable PF with support with unlimited (up to HW limit) number of VFs::
+ *
+ * # echo unlimited > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
+ * # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
+ *
+ * How to enable PF with support up to 3 VFs::
+ *
+ * # echo 3 > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
+ * # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
+ *
+ * How to disable PF mode and always run as native::
+ *
+ * # echo 0 > /sys/kernel/config/xe/0000:00:02.0/sriov/max_vfs
+ * # echo 0000:00:02.0 > /sys/bus/pci/drivers/xe/bind
+ *
+ * This setting only takes effect when probing the device.
+ *
* Remove devices
* ==============
*
@@ -185,30 +252,44 @@ struct wa_bb {
struct xe_config_group_device {
struct config_group group;
+ struct config_group sriov;
struct xe_config_device {
+ u64 gt_types_allowed;
u64 engines_allowed;
struct wa_bb ctx_restore_post_bb[XE_ENGINE_CLASS_MAX];
struct wa_bb ctx_restore_mid_bb[XE_ENGINE_CLASS_MAX];
bool survivability_mode;
bool enable_psmi;
+ struct {
+ unsigned int max_vfs;
+ } sriov;
} config;
/* protects attributes */
struct mutex lock;
/* matching descriptor */
const struct xe_device_desc *desc;
+ /* tentative SR-IOV mode */
+ enum xe_sriov_mode mode;
};
static const struct xe_config_device device_defaults = {
+ .gt_types_allowed = U64_MAX,
.engines_allowed = U64_MAX,
.survivability_mode = false,
.enable_psmi = false,
+ .sriov = {
+ .max_vfs = UINT_MAX,
+ },
};
static void set_device_defaults(struct xe_config_device *config)
{
*config = device_defaults;
+#ifdef CONFIG_PCI_IOV
+ config->sriov.max_vfs = xe_modparam.max_vfs;
+#endif
}
struct engine_info {
@@ -230,6 +311,14 @@ static const struct engine_info engine_info[] = {
{ .cls = "gsccs", .mask = XE_HW_ENGINE_GSCCS_MASK, .engine_class = XE_ENGINE_CLASS_OTHER },
};
+static const struct {
+ const char *name;
+ enum xe_gt_type type;
+} gt_types[] = {
+ { .name = "primary", .type = XE_GT_TYPE_MAIN },
+ { .name = "media", .type = XE_GT_TYPE_MEDIA },
+};
+
static struct xe_config_group_device *to_xe_config_group_device(struct config_item *item)
{
return container_of(to_config_group(item), struct xe_config_group_device, group);
@@ -292,6 +381,57 @@ static ssize_t survivability_mode_store(struct config_item *item, const char *pa
return len;
}
+static ssize_t gt_types_allowed_show(struct config_item *item, char *page)
+{
+ struct xe_config_device *dev = to_xe_config_device(item);
+ char *p = page;
+
+ for (size_t i = 0; i < ARRAY_SIZE(gt_types); i++)
+ if (dev->gt_types_allowed & BIT_ULL(gt_types[i].type))
+ p += sprintf(p, "%s\n", gt_types[i].name);
+
+ return p - page;
+}
+
+static ssize_t gt_types_allowed_store(struct config_item *item, const char *page,
+ size_t len)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item);
+ char *buf __free(kfree) = kstrdup(page, GFP_KERNEL);
+ char *p = buf;
+ u64 typemask = 0;
+
+ if (!buf)
+ return -ENOMEM;
+
+ while (p) {
+ char *typename = strsep(&p, ",\n");
+ bool matched = false;
+
+ if (typename[0] == '\0')
+ continue;
+
+ for (size_t i = 0; i < ARRAY_SIZE(gt_types); i++) {
+ if (strcmp(typename, gt_types[i].name) == 0) {
+ typemask |= BIT(gt_types[i].type);
+ matched = true;
+ break;
+ }
+ }
+
+ if (!matched)
+ return -EINVAL;
+ }
+
+ guard(mutex)(&dev->lock);
+ if (is_bound(dev))
+ return -EBUSY;
+
+ dev->config.gt_types_allowed = typemask;
+
+ return len;
+}
+
static ssize_t engines_allowed_show(struct config_item *item, char *page)
{
struct xe_config_device *dev = to_xe_config_device(item);
@@ -672,6 +812,7 @@ CONFIGFS_ATTR(, ctx_restore_mid_bb);
CONFIGFS_ATTR(, ctx_restore_post_bb);
CONFIGFS_ATTR(, enable_psmi);
CONFIGFS_ATTR(, engines_allowed);
+CONFIGFS_ATTR(, gt_types_allowed);
CONFIGFS_ATTR(, survivability_mode);
static struct configfs_attribute *xe_config_device_attrs[] = {
@@ -679,6 +820,7 @@ static struct configfs_attribute *xe_config_device_attrs[] = {
&attr_ctx_restore_post_bb,
&attr_enable_psmi,
&attr_engines_allowed,
+ &attr_gt_types_allowed,
&attr_survivability_mode,
NULL,
};
@@ -721,6 +863,68 @@ static const struct config_item_type xe_config_device_type = {
.ct_owner = THIS_MODULE,
};
+static ssize_t sriov_max_vfs_show(struct config_item *item, char *page)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
+
+ guard(mutex)(&dev->lock);
+
+ if (dev->config.sriov.max_vfs == UINT_MAX)
+ return sprintf(page, "%s\n", "unlimited");
+ else
+ return sprintf(page, "%u\n", dev->config.sriov.max_vfs);
+}
+
+static ssize_t sriov_max_vfs_store(struct config_item *item, const char *page, size_t len)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
+ unsigned int max_vfs;
+ int ret;
+
+ guard(mutex)(&dev->lock);
+
+ if (is_bound(dev))
+ return -EBUSY;
+
+ ret = kstrtouint(page, 0, &max_vfs);
+ if (ret) {
+ if (!sysfs_streq(page, "unlimited"))
+ return ret;
+ max_vfs = UINT_MAX;
+ }
+
+ dev->config.sriov.max_vfs = max_vfs;
+ return len;
+}
+
+CONFIGFS_ATTR(sriov_, max_vfs);
+
+static struct configfs_attribute *xe_config_sriov_attrs[] = {
+ &sriov_attr_max_vfs,
+ NULL,
+};
+
+static bool xe_config_sriov_is_visible(struct config_item *item,
+ struct configfs_attribute *attr, int n)
+{
+ struct xe_config_group_device *dev = to_xe_config_group_device(item->ci_parent);
+
+ if (attr == &sriov_attr_max_vfs && dev->mode != XE_SRIOV_MODE_PF)
+ return false;
+
+ return true;
+}
+
+static struct configfs_group_operations xe_config_sriov_group_ops = {
+ .is_visible = xe_config_sriov_is_visible,
+};
+
+static const struct config_item_type xe_config_sriov_type = {
+ .ct_owner = THIS_MODULE,
+ .ct_group_ops = &xe_config_sriov_group_ops,
+ .ct_attrs = xe_config_sriov_attrs,
+};
+
static const struct xe_device_desc *xe_match_desc(struct pci_dev *pdev)
{
struct device_driver *driver = driver_find("xe", &pci_bus_type);
@@ -746,6 +950,7 @@ static struct config_group *xe_config_make_device_group(struct config_group *gro
unsigned int domain, bus, slot, function;
struct xe_config_group_device *dev;
const struct xe_device_desc *match;
+ enum xe_sriov_mode mode;
struct pci_dev *pdev;
char canonical[16];
int vfnumber = 0;
@@ -762,6 +967,9 @@ static struct config_group *xe_config_make_device_group(struct config_group *gro
return ERR_PTR(-EINVAL);
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(slot, function));
+ mode = pdev ? dev_is_pf(&pdev->dev) ?
+ XE_SRIOV_MODE_PF : XE_SRIOV_MODE_NONE : XE_SRIOV_MODE_VF;
+
if (!pdev && function)
pdev = pci_get_domain_bus_and_slot(domain, bus, PCI_DEVFN(slot, 0));
if (!pdev && slot)
@@ -796,9 +1004,15 @@ static struct config_group *xe_config_make_device_group(struct config_group *gro
return ERR_PTR(-ENOMEM);
dev->desc = match;
+ dev->mode = match->has_sriov ? mode : XE_SRIOV_MODE_NONE;
+
set_device_defaults(&dev->config);
config_group_init_type_name(&dev->group, name, &xe_config_device_type);
+ if (dev->mode != XE_SRIOV_MODE_NONE) {
+ config_group_init_type_name(&dev->sriov, "sriov", &xe_config_sriov_type);
+ configfs_add_default_group(&dev->sriov, &dev->group);
+ }
mutex_init(&dev->lock);
@@ -846,6 +1060,7 @@ static void dump_custom_dev_config(struct pci_dev *pdev,
dev->config.attr_); \
} while (0)
+ PRI_CUSTOM_ATTR("%llx", gt_types_allowed);
PRI_CUSTOM_ATTR("%llx", engines_allowed);
PRI_CUSTOM_ATTR("%d", enable_psmi);
PRI_CUSTOM_ATTR("%d", survivability_mode);
@@ -896,6 +1111,44 @@ bool xe_configfs_get_survivability_mode(struct pci_dev *pdev)
return mode;
}
+static u64 get_gt_types_allowed(struct pci_dev *pdev)
+{
+ struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
+ u64 mask;
+
+ if (!dev)
+ return device_defaults.gt_types_allowed;
+
+ mask = dev->config.gt_types_allowed;
+ config_group_put(&dev->group);
+
+ return mask;
+}
+
+/**
+ * xe_configfs_primary_gt_allowed - determine whether primary GTs are supported
+ * @pdev: pci device
+ *
+ * Return: True if primary GTs are enabled, false if they have been disabled via
+ * configfs.
+ */
+bool xe_configfs_primary_gt_allowed(struct pci_dev *pdev)
+{
+ return get_gt_types_allowed(pdev) & BIT_ULL(XE_GT_TYPE_MAIN);
+}
+
+/**
+ * xe_configfs_media_gt_allowed - determine whether media GTs are supported
+ * @pdev: pci device
+ *
+ * Return: True if the media GTs are enabled, false if they have been disabled
+ * via configfs.
+ */
+bool xe_configfs_media_gt_allowed(struct pci_dev *pdev)
+{
+ return get_gt_types_allowed(pdev) & BIT_ULL(XE_GT_TYPE_MEDIA);
+}
+
/**
* xe_configfs_get_engines_allowed - get engine allowed mask from configfs
* @pdev: pci device
@@ -988,6 +1241,34 @@ u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev,
return len;
}
+#ifdef CONFIG_PCI_IOV
+/**
+ * xe_configfs_get_max_vfs() - Get number of VFs that could be managed
+ * @pdev: the &pci_dev device
+ *
+ * Find the configfs group that belongs to the PCI device and return maximum
+ * number of Virtual Functions (VFs) that could be managed by this device.
+ * If configfs group is not present, use value of max_vfs module parameter.
+ *
+ * Return: maximum number of VFs that could be managed.
+ */
+unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev)
+{
+ struct xe_config_group_device *dev = find_xe_config_group_device(pdev);
+ unsigned int max_vfs;
+
+ if (!dev)
+ return xe_modparam.max_vfs;
+
+ scoped_guard(mutex, &dev->lock)
+ max_vfs = dev->config.sriov.max_vfs;
+
+ config_group_put(&dev->group);
+
+ return max_vfs;
+}
+#endif
+
int __init xe_configfs_init(void)
{
int ret;
diff --git a/drivers/gpu/drm/xe/xe_configfs.h b/drivers/gpu/drm/xe/xe_configfs.h
index c61e0e47ed94..fed57be0b90e 100644
--- a/drivers/gpu/drm/xe/xe_configfs.h
+++ b/drivers/gpu/drm/xe/xe_configfs.h
@@ -17,23 +17,31 @@ int xe_configfs_init(void);
void xe_configfs_exit(void);
void xe_configfs_check_device(struct pci_dev *pdev);
bool xe_configfs_get_survivability_mode(struct pci_dev *pdev);
+bool xe_configfs_primary_gt_allowed(struct pci_dev *pdev);
+bool xe_configfs_media_gt_allowed(struct pci_dev *pdev);
u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev);
bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev);
u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs);
u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs);
+#ifdef CONFIG_PCI_IOV
+unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev);
+#endif
#else
static inline int xe_configfs_init(void) { return 0; }
static inline void xe_configfs_exit(void) { }
static inline void xe_configfs_check_device(struct pci_dev *pdev) { }
static inline bool xe_configfs_get_survivability_mode(struct pci_dev *pdev) { return false; }
+static inline bool xe_configfs_primary_gt_allowed(struct pci_dev *pdev) { return true; }
+static inline bool xe_configfs_media_gt_allowed(struct pci_dev *pdev) { return true; }
static inline u64 xe_configfs_get_engines_allowed(struct pci_dev *pdev) { return U64_MAX; }
static inline bool xe_configfs_get_psmi_enabled(struct pci_dev *pdev) { return false; }
static inline u32 xe_configfs_get_ctx_restore_mid_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs) { return 0; }
static inline u32 xe_configfs_get_ctx_restore_post_bb(struct pci_dev *pdev, enum xe_engine_class,
const u32 **cs) { return 0; }
+static inline unsigned int xe_configfs_get_max_vfs(struct pci_dev *pdev) { return UINT_MAX; }
#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c
index cd977dbd1ef6..e91da9589c5f 100644
--- a/drivers/gpu/drm/xe/xe_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_debugfs.c
@@ -23,12 +23,12 @@
#include "xe_psmi.h"
#include "xe_pxp_debugfs.h"
#include "xe_sriov.h"
-#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_debugfs.h"
#include "xe_sriov_vf.h"
#include "xe_step.h"
#include "xe_tile_debugfs.h"
-#include "xe_wa.h"
#include "xe_vsec.h"
+#include "xe_wa.h"
#ifdef CONFIG_DRM_XE_DEBUG
#include "xe_bo_evict.h"
@@ -142,6 +142,7 @@ static int dgfx_pkg_residencies_show(struct seq_file *m, void *data)
} residencies[] = {
{BMG_G2_RESIDENCY_OFFSET, "Package G2"},
{BMG_G6_RESIDENCY_OFFSET, "Package G6"},
+ {BMG_G7_RESIDENCY_OFFSET, "Package G7"},
{BMG_G8_RESIDENCY_OFFSET, "Package G8"},
{BMG_G10_RESIDENCY_OFFSET, "Package G10"},
{BMG_MODS_RESIDENCY_OFFSET, "Package ModS"}
@@ -349,17 +350,14 @@ static ssize_t disable_late_binding_set(struct file *f, const char __user *ubuf,
{
struct xe_device *xe = file_inode(f)->i_private;
struct xe_late_bind *late_bind = &xe->late_bind;
- u32 uval;
- ssize_t ret;
+ bool val;
+ int ret;
- ret = kstrtouint_from_user(ubuf, size, sizeof(uval), &uval);
+ ret = kstrtobool_from_user(ubuf, size, &val);
if (ret)
return ret;
- if (uval > 1)
- return -EINVAL;
-
- late_bind->disable = !!uval;
+ late_bind->disable = val;
return size;
}
diff --git a/drivers/gpu/drm/xe/xe_devcoredump.c b/drivers/gpu/drm/xe/xe_devcoredump.c
index 203e3038cc81..d444eda65ca6 100644
--- a/drivers/gpu/drm/xe/xe_devcoredump.c
+++ b/drivers/gpu/drm/xe/xe_devcoredump.c
@@ -106,9 +106,9 @@ static ssize_t __xe_devcoredump_read(char *buffer, ssize_t count,
drm_puts(&p, "module: " KBUILD_MODNAME "\n");
ts = ktime_to_timespec64(ss->snapshot_time);
- drm_printf(&p, "Snapshot time: %lld.%09ld\n", ts.tv_sec, ts.tv_nsec);
+ drm_printf(&p, "Snapshot time: %ptSp\n", &ts);
ts = ktime_to_timespec64(ss->boot_time);
- drm_printf(&p, "Uptime: %lld.%09ld\n", ts.tv_sec, ts.tv_nsec);
+ drm_printf(&p, "Uptime: %ptSp\n", &ts);
drm_printf(&p, "Process: %s [%d]\n", ss->process_name, ss->pid);
xe_device_snapshot_print(xe, &p);
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 34d33965eac2..c7d373c70f0f 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -8,6 +8,7 @@
#include <linux/aperture.h>
#include <linux/delay.h>
#include <linux/fault-inject.h>
+#include <linux/iopoll.h>
#include <linux/units.h>
#include <drm/drm_atomic_helper.h>
@@ -51,6 +52,7 @@
#include "xe_nvm.h"
#include "xe_oa.h"
#include "xe_observation.h"
+#include "xe_pagefault.h"
#include "xe_pat.h"
#include "xe_pcode.h"
#include "xe_pm.h"
@@ -436,7 +438,7 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
err = ttm_device_init(&xe->ttm, &xe_ttm_funcs, xe->drm.dev,
xe->drm.anon_inode->i_mapping,
- xe->drm.vma_offset_manager, false, false);
+ xe->drm.vma_offset_manager, 0);
if (WARN_ON(err))
goto err;
@@ -630,16 +632,22 @@ mask_err:
return err;
}
-static bool verify_lmem_ready(struct xe_device *xe)
+static int lmem_initializing(struct xe_device *xe)
{
- u32 val = xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) & LMEM_INIT;
+ if (xe_mmio_read32(xe_root_tile_mmio(xe), GU_CNTL) & LMEM_INIT)
+ return 0;
+
+ if (signal_pending(current))
+ return -EINTR;
- return !!val;
+ return 1;
}
static int wait_for_lmem_ready(struct xe_device *xe)
{
- unsigned long timeout, start;
+ const unsigned long TIMEOUT_SEC = 60;
+ unsigned long prev_jiffies;
+ int initializing;
if (!IS_DGFX(xe))
return 0;
@@ -647,39 +655,35 @@ static int wait_for_lmem_ready(struct xe_device *xe)
if (IS_SRIOV_VF(xe))
return 0;
- if (verify_lmem_ready(xe))
+ if (!lmem_initializing(xe))
return 0;
drm_dbg(&xe->drm, "Waiting for lmem initialization\n");
+ prev_jiffies = jiffies;
- start = jiffies;
- timeout = start + secs_to_jiffies(60); /* 60 sec! */
-
- do {
- if (signal_pending(current))
- return -EINTR;
-
- /*
- * The boot firmware initializes local memory and
- * assesses its health. If memory training fails,
- * the punit will have been instructed to keep the GT powered
- * down.we won't be able to communicate with it
- *
- * If the status check is done before punit updates the register,
- * it can lead to the system being unusable.
- * use a timeout and defer the probe to prevent this.
- */
- if (time_after(jiffies, timeout)) {
- drm_dbg(&xe->drm, "lmem not initialized by firmware\n");
- return -EPROBE_DEFER;
- }
-
- msleep(20);
-
- } while (!verify_lmem_ready(xe));
+ /*
+ * The boot firmware initializes local memory and
+ * assesses its health. If memory training fails,
+ * the punit will have been instructed to keep the GT powered
+ * down.we won't be able to communicate with it
+ *
+ * If the status check is done before punit updates the register,
+ * it can lead to the system being unusable.
+ * use a timeout and defer the probe to prevent this.
+ */
+ poll_timeout_us(initializing = lmem_initializing(xe),
+ initializing <= 0,
+ 20 * USEC_PER_MSEC, TIMEOUT_SEC * USEC_PER_SEC, true);
+ if (initializing < 0)
+ return initializing;
+
+ if (initializing) {
+ drm_dbg(&xe->drm, "lmem not initialized by firmware\n");
+ return -EPROBE_DEFER;
+ }
drm_dbg(&xe->drm, "lmem ready after %ums",
- jiffies_to_msecs(jiffies - start));
+ jiffies_to_msecs(jiffies - prev_jiffies));
return 0;
}
@@ -779,6 +783,8 @@ static int probe_has_flat_ccs(struct xe_device *xe)
return 0;
gt = xe_root_mmio_gt(xe);
+ if (!gt)
+ return 0;
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
@@ -891,6 +897,10 @@ int xe_device_probe(struct xe_device *xe)
return err;
}
+ err = xe_pagefault_init(xe);
+ if (err)
+ return err;
+
if (xe->tiles->media_gt &&
XE_GT_WA(xe->tiles->media_gt, 15015404425_disable))
XE_DEVICE_WA_DISABLE(xe, 15015404425);
@@ -988,16 +998,16 @@ void xe_device_shutdown(struct xe_device *xe)
drm_dbg(&xe->drm, "Shutting down device\n");
- if (xe_driver_flr_disabled(xe)) {
- xe_display_pm_shutdown(xe);
+ xe_display_pm_shutdown(xe);
+
+ xe_irq_suspend(xe);
- xe_irq_suspend(xe);
+ for_each_gt(gt, xe, id)
+ xe_gt_shutdown(gt);
- for_each_gt(gt, xe, id)
- xe_gt_shutdown(gt);
+ xe_display_pm_shutdown_late(xe);
- xe_display_pm_shutdown_late(xe);
- } else {
+ if (!xe_driver_flr_disabled(xe)) {
/* BOOM! */
__xe_driver_flr(xe);
}
@@ -1059,6 +1069,8 @@ void xe_device_l2_flush(struct xe_device *xe)
unsigned int fw_ref;
gt = xe_root_mmio_gt(xe);
+ if (!gt)
+ return;
if (!XE_GT_WA(gt, 16023588340))
return;
@@ -1104,6 +1116,9 @@ void xe_device_td_flush(struct xe_device *xe)
return;
root_gt = xe_root_mmio_gt(xe);
+ if (!root_gt)
+ return;
+
if (XE_GT_WA(root_gt, 16023588340)) {
/* A transient flush is not sufficient: flush the L2 */
xe_device_l2_flush(xe);
@@ -1207,7 +1222,7 @@ static void xe_device_wedged_fini(struct drm_device *drm, void *arg)
*
* /sys/bus/pci/devices/<device>/survivability_mode
*
- * - Admin/userpsace consumer can use firmware flashing tools like fwupd to flash
+ * - Admin/userspace consumer can use firmware flashing tools like fwupd to flash
* firmware and restore device to normal operation.
*/
diff --git a/drivers/gpu/drm/xe/xe_device_sysfs.c b/drivers/gpu/drm/xe/xe_device_sysfs.c
index c5151c86a98a..ec9c06b06fb5 100644
--- a/drivers/gpu/drm/xe/xe_device_sysfs.c
+++ b/drivers/gpu/drm/xe/xe_device_sysfs.c
@@ -38,13 +38,8 @@ vram_d3cold_threshold_show(struct device *dev,
{
struct pci_dev *pdev = to_pci_dev(dev);
struct xe_device *xe = pdev_to_xe_device(pdev);
- int ret;
-
- xe_pm_runtime_get(xe);
- ret = sysfs_emit(buf, "%d\n", xe->d3cold.vram_threshold);
- xe_pm_runtime_put(xe);
- return ret;
+ return sysfs_emit(buf, "%d\n", xe->d3cold.vram_threshold);
}
static ssize_t
@@ -173,11 +168,8 @@ static umode_t late_bind_attr_is_visible(struct kobject *kobj,
u32 cap = 0;
int ret;
- xe_pm_runtime_get(xe);
-
ret = xe_pcode_read(root, PCODE_MBOX(PCODE_LATE_BINDING, GET_CAPABILITY_STATUS, 0),
&cap, NULL);
- xe_pm_runtime_put(xe);
if (ret)
return 0;
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 74d7af830b85..0b2fa7c56d38 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -18,6 +18,7 @@
#include "xe_lmtt_types.h"
#include "xe_memirq_types.h"
#include "xe_oa_types.h"
+#include "xe_pagefault_types.h"
#include "xe_platform_types.h"
#include "xe_pmu_types.h"
#include "xe_pt_types.h"
@@ -27,6 +28,7 @@
#include "xe_sriov_vf_ccs_types.h"
#include "xe_step_types.h"
#include "xe_survivability_mode_types.h"
+#include "xe_tile_sriov_vf_types.h"
#include "xe_validation.h"
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
@@ -158,7 +160,15 @@ struct xe_tile {
/** @mem: memory management info for tile */
struct {
/**
- * @mem.vram: VRAM info for tile.
+ * @mem.kernel_vram: kernel-dedicated VRAM info for tile.
+ *
+ * Although VRAM is associated with a specific tile, it can
+ * still be accessed by all tiles' GTs.
+ */
+ struct xe_vram_region *kernel_vram;
+
+ /**
+ * @mem.vram: general purpose VRAM info for tile.
*
* Although VRAM is associated with a specific tile, it can
* still be accessed by all tiles' GTs.
@@ -185,6 +195,8 @@ struct xe_tile {
struct {
/** @sriov.vf.ggtt_balloon: GGTT regions excluded from use. */
struct xe_ggtt_node *ggtt_balloon[2];
+ /** @sriov.vf.self_config: VF configuration data */
+ struct xe_tile_sriov_vf_selfconfig self_config;
} vf;
} sriov;
@@ -211,12 +223,17 @@ struct xe_tile {
};
/**
- * struct xe_device - Top level struct of XE device
+ * struct xe_device - Top level struct of Xe device
*/
struct xe_device {
/** @drm: drm device */
struct drm_device drm;
+#if IS_ENABLED(CONFIG_DRM_XE_DISPLAY)
+ /** @display: display device data, must be placed after drm device member */
+ struct intel_display *display;
+#endif
+
/** @devcoredump: device coredump */
struct xe_devcoredump devcoredump;
@@ -234,9 +251,9 @@ struct xe_device {
u32 media_verx100;
/** @info.mem_region_mask: mask of valid memory regions */
u32 mem_region_mask;
- /** @info.platform: XE platform enum */
+ /** @info.platform: Xe platform enum */
enum xe_platform platform;
- /** @info.subplatform: XE subplatform enum */
+ /** @info.subplatform: Xe subplatform enum */
enum xe_subplatform subplatform;
/** @info.devid: device ID */
u16 devid;
@@ -289,6 +306,8 @@ struct xe_device {
* pcode mailbox commands.
*/
u8 has_mbx_power_limits:1;
+ /** @info.has_mem_copy_instr: Device supports MEM_COPY instruction */
+ u8 has_mem_copy_instr:1;
/** @info.has_pxp: Device has PXP support */
u8 has_pxp:1;
/** @info.has_range_tlb_inval: Has range based TLB invalidations */
@@ -318,6 +337,8 @@ struct xe_device {
u8 skip_mtcfg:1;
/** @info.skip_pcode: skip access to PCODE uC */
u8 skip_pcode:1;
+ /** @info.needs_shared_vf_gt_wq: needs shared GT WQ on VF */
+ u8 needs_shared_vf_gt_wq:1;
} info;
/** @wa_active: keep track of active workarounds */
@@ -398,6 +419,16 @@ struct xe_device {
u32 next_asid;
/** @usm.lock: protects UM state */
struct rw_semaphore lock;
+ /** @usm.pf_wq: page fault work queue, unbound, high priority */
+ struct workqueue_struct *pf_wq;
+ /*
+ * We pick 4 here because, in the current implementation, it
+ * yields the best bandwidth utilization of the kernel paging
+ * engine.
+ */
+#define XE_PAGEFAULT_QUEUE_COUNT 4
+ /** @usm.pf_queue: Page fault queues */
+ struct xe_pagefault_queue pf_queue[XE_PAGEFAULT_QUEUE_COUNT];
} usm;
/** @pinned: pinned BO state */
@@ -617,8 +648,6 @@ struct xe_device {
* drm_i915_private during build. After cleanup these should go away,
* migrating to the right sub-structs
*/
- struct intel_display *display;
-
const struct dram_info *dram_info;
/*
@@ -627,26 +656,14 @@ struct xe_device {
*/
u32 edram_size_mb;
- /* To shut up runtime pm macros.. */
- struct xe_runtime_pm {} runtime_pm;
-
- /* only to allow build, not used functionally */
- u32 irq_mask;
-
struct intel_uncore {
spinlock_t lock;
} uncore;
-
- /* only to allow build, not used functionally */
- struct {
- unsigned int hpll_freq;
- unsigned int czclk_freq;
- };
#endif
};
/**
- * struct xe_file - file handle for XE driver
+ * struct xe_file - file handle for Xe driver
*/
struct xe_file {
/** @xe: xe DEVICE **/
diff --git a/drivers/gpu/drm/xe/xe_device_wa_oob.rules b/drivers/gpu/drm/xe/xe_device_wa_oob.rules
index 3a0c4ccc4224..55ba01bc8f38 100644
--- a/drivers/gpu/drm/xe/xe_device_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_device_wa_oob.rules
@@ -1,2 +1,5 @@
+22010954014 PLATFORM(DG2)
15015404425 PLATFORM(LUNARLAKE)
PLATFORM(PANTHERLAKE)
+22019338487_display PLATFORM(LUNARLAKE)
+14022085890 SUBPLATFORM(BATTLEMAGE, G21)
diff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c
index a7d67725c3ee..54e42960daad 100644
--- a/drivers/gpu/drm/xe/xe_dma_buf.c
+++ b/drivers/gpu/drm/xe/xe_dma_buf.c
@@ -48,32 +48,43 @@ static void xe_dma_buf_detach(struct dma_buf *dmabuf,
static int xe_dma_buf_pin(struct dma_buf_attachment *attach)
{
- struct drm_gem_object *obj = attach->dmabuf->priv;
+ struct dma_buf *dmabuf = attach->dmabuf;
+ struct drm_gem_object *obj = dmabuf->priv;
struct xe_bo *bo = gem_to_xe_bo(obj);
struct xe_device *xe = xe_bo_device(bo);
struct drm_exec *exec = XE_VALIDATION_UNSUPPORTED;
+ bool allow_vram = true;
int ret;
- /*
- * For now only support pinning in TT memory, for two reasons:
- * 1) Avoid pinning in a placement not accessible to some importers.
- * 2) Pinning in VRAM requires PIN accounting which is a to-do.
- */
- if (xe_bo_is_pinned(bo) && !xe_bo_is_mem_type(bo, XE_PL_TT)) {
+ if (!IS_ENABLED(CONFIG_DMABUF_MOVE_NOTIFY)) {
+ allow_vram = false;
+ } else {
+ list_for_each_entry(attach, &dmabuf->attachments, node) {
+ if (!attach->peer2peer) {
+ allow_vram = false;
+ break;
+ }
+ }
+ }
+
+ if (xe_bo_is_pinned(bo) && !xe_bo_is_mem_type(bo, XE_PL_TT) &&
+ !(xe_bo_is_vram(bo) && allow_vram)) {
drm_dbg(&xe->drm, "Can't migrate pinned bo for dma-buf pin.\n");
return -EINVAL;
}
- ret = xe_bo_migrate(bo, XE_PL_TT, NULL, exec);
- if (ret) {
- if (ret != -EINTR && ret != -ERESTARTSYS)
- drm_dbg(&xe->drm,
- "Failed migrating dma-buf to TT memory: %pe\n",
- ERR_PTR(ret));
- return ret;
+ if (!allow_vram) {
+ ret = xe_bo_migrate(bo, XE_PL_TT, NULL, exec);
+ if (ret) {
+ if (ret != -EINTR && ret != -ERESTARTSYS)
+ drm_dbg(&xe->drm,
+ "Failed migrating dma-buf to TT memory: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
}
- ret = xe_bo_pin_external(bo, true, exec);
+ ret = xe_bo_pin_external(bo, !allow_vram, exec);
xe_assert(xe, !ret);
return 0;
diff --git a/drivers/gpu/drm/xe/xe_eu_stall.c b/drivers/gpu/drm/xe/xe_eu_stall.c
index f5cfdf29fde3..97dfb7945b7a 100644
--- a/drivers/gpu/drm/xe/xe_eu_stall.c
+++ b/drivers/gpu/drm/xe/xe_eu_stall.c
@@ -49,6 +49,7 @@ struct xe_eu_stall_data_stream {
wait_queue_head_t poll_wq;
size_t data_record_size;
size_t per_xecore_buf_size;
+ unsigned int fw_ref;
struct xe_gt *gt;
struct xe_bo *bo;
@@ -124,6 +125,27 @@ struct xe_eu_stall_data_xe2 {
__u64 unused[6];
} __packed;
+/*
+ * EU stall data format for Xe3p arch GPUs.
+ */
+struct xe_eu_stall_data_xe3p {
+ __u64 ip_addr:61; /* Bits 0 to 60 */
+ __u64 tdr_count:8; /* Bits 61 to 68 */
+ __u64 other_count:8; /* Bits 69 to 76 */
+ __u64 control_count:8; /* Bits 77 to 84 */
+ __u64 pipestall_count:8; /* Bits 85 to 92 */
+ __u64 send_count:8; /* Bits 93 to 100 */
+ __u64 dist_acc_count:8; /* Bits 101 to 108 */
+ __u64 sbid_count:8; /* Bits 109 to 116 */
+ __u64 sync_count:8; /* Bits 117 to 124 */
+ __u64 inst_fetch_count:8; /* Bits 125 to 132 */
+ __u64 active_count:8; /* Bits 133 to 140 */
+ __u64 ex_id:3; /* Bits 141 to 143 */
+ __u64 end_flag:1; /* Bit 144 */
+ __u64 unused_bits:47;
+ __u64 unused[5];
+} __packed;
+
const u64 eu_stall_sampling_rates[] = {251, 251 * 2, 251 * 3, 251 * 4, 251 * 5, 251 * 6, 251 * 7};
/**
@@ -167,10 +189,13 @@ size_t xe_eu_stall_data_record_size(struct xe_device *xe)
{
size_t record_size = 0;
- if (xe->info.platform == XE_PVC)
- record_size = sizeof(struct xe_eu_stall_data_pvc);
+ if (GRAPHICS_VER(xe) >= 35)
+ record_size = sizeof(struct xe_eu_stall_data_xe3p);
else if (GRAPHICS_VER(xe) >= 20)
record_size = sizeof(struct xe_eu_stall_data_xe2);
+ else if (xe->info.platform == XE_PVC)
+ record_size = sizeof(struct xe_eu_stall_data_pvc);
+
xe_assert(xe, is_power_of_2(record_size));
@@ -636,13 +661,12 @@ static int xe_eu_stall_stream_enable(struct xe_eu_stall_data_stream *stream)
struct per_xecore_buf *xecore_buf;
struct xe_gt *gt = stream->gt;
u16 group, instance;
- unsigned int fw_ref;
int xecore;
/* Take runtime pm ref and forcewake to disable RC6 */
xe_pm_runtime_get(gt_to_xe(gt));
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_RENDER);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FW_RENDER)) {
+ stream->fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_RENDER);
+ if (!xe_force_wake_ref_has_domain(stream->fw_ref, XE_FW_RENDER)) {
xe_gt_err(gt, "Failed to get RENDER forcewake\n");
xe_pm_runtime_put(gt_to_xe(gt));
return -ETIMEDOUT;
@@ -808,7 +832,7 @@ static int xe_eu_stall_disable_locked(struct xe_eu_stall_data_stream *stream)
xe_gt_mcr_multicast_write(gt, ROW_CHICKEN2,
_MASKED_BIT_DISABLE(DISABLE_DOP_GATING));
- xe_force_wake_put(gt_to_fw(gt), XE_FW_RENDER);
+ xe_force_wake_put(gt_to_fw(gt), stream->fw_ref);
xe_pm_runtime_put(gt_to_xe(gt));
return 0;
diff --git a/drivers/gpu/drm/xe/xe_exec.c b/drivers/gpu/drm/xe/xe_exec.c
index 7715e74bb945..4d81210e41f5 100644
--- a/drivers/gpu/drm/xe/xe_exec.c
+++ b/drivers/gpu/drm/xe/xe_exec.c
@@ -16,10 +16,12 @@
#include "xe_exec_queue.h"
#include "xe_hw_engine_group.h"
#include "xe_macros.h"
+#include "xe_pm.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
#include "xe_sync.h"
#include "xe_svm.h"
+#include "xe_trace.h"
#include "xe_vm.h"
/**
@@ -32,7 +34,7 @@
* - Binding at exec time
* - Flow controlling the ring at exec time
*
- * In XE we avoid all of this complication by not allowing a BO list to be
+ * In Xe we avoid all of this complication by not allowing a BO list to be
* passed into an exec, using the dma-buf implicit sync uAPI, have binds as
* separate operations, and using the DRM scheduler to flow control the ring.
* Let's deep dive on each of these.
@@ -123,7 +125,7 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
struct xe_validation_ctx ctx;
struct xe_sched_job *job;
struct xe_vm *vm;
- bool write_locked, skip_retry = false;
+ bool write_locked;
int err = 0;
struct xe_hw_engine_group *group;
enum xe_hw_engine_group_execution_mode mode, previous_mode;
@@ -153,6 +155,12 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
goto err_exec_queue;
}
+ if (atomic_read(&q->job_cnt) >= XE_MAX_JOB_COUNT_PER_EXEC_QUEUE) {
+ trace_xe_exec_queue_reach_max_job_count(q, XE_MAX_JOB_COUNT_PER_EXEC_QUEUE);
+ err = -EAGAIN;
+ goto err_exec_queue;
+ }
+
if (args->num_syncs) {
syncs = kcalloc(args->num_syncs, sizeof(*syncs), GFP_KERNEL);
if (!syncs) {
@@ -165,7 +173,8 @@ int xe_exec_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
- &syncs_user[num_syncs], SYNC_PARSE_FLAG_EXEC |
+ &syncs_user[num_syncs], NULL, 0,
+ SYNC_PARSE_FLAG_EXEC |
(xe_vm_in_lr_mode(vm) ?
SYNC_PARSE_FLAG_LR_MODE : 0));
if (err)
@@ -247,7 +256,7 @@ retry:
* on task freezing during suspend / hibernate, the call will
* return -ERESTARTSYS and the IOCTL will be rerun.
*/
- err = wait_for_completion_interruptible(&xe->pm_block);
+ err = xe_pm_block_on_suspend(xe);
if (err)
goto err_unlock_list;
@@ -265,12 +274,6 @@ retry:
goto err_exec;
}
- if (xe_exec_queue_is_lr(q) && xe_exec_queue_ring_full(q)) {
- err = -EWOULDBLOCK; /* Aliased to -EAGAIN */
- skip_retry = true;
- goto err_exec;
- }
-
if (xe_exec_queue_uses_pxp(q)) {
err = xe_vm_validate_protected(q->vm);
if (err)
@@ -299,10 +302,6 @@ retry:
goto err_put_job;
if (!xe_vm_in_lr_mode(vm)) {
- err = xe_sched_job_last_fence_add_dep(job, vm);
- if (err)
- goto err_put_job;
-
err = xe_svm_notifier_lock_interruptible(vm);
if (err)
goto err_put_job;
@@ -327,8 +326,6 @@ retry:
xe_sched_job_init_user_fence(job, &syncs[i]);
}
- if (xe_exec_queue_is_lr(q))
- q->ring_ops->emit_job(job);
if (!xe_vm_in_lr_mode(vm))
xe_exec_queue_last_fence_set(q, vm, &job->drm.s_fence->finished);
xe_sched_job_push(job);
@@ -354,7 +351,7 @@ err_exec:
xe_validation_ctx_fini(&ctx);
err_unlock_list:
up_read(&vm->lock);
- if (err == -EAGAIN && !skip_retry)
+ if (err == -EAGAIN)
goto retry;
err_hw_exec_mode:
if (mode == EXEC_MODE_DMA_FENCE)
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.c b/drivers/gpu/drm/xe/xe_exec_queue.c
index 37b2b93b73d6..8724f8de67e2 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
@@ -10,11 +10,13 @@
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
+#include <drm/drm_syncobj.h>
#include <uapi/drm/xe_drm.h>
#include "xe_dep_scheduler.h"
#include "xe_device.h"
#include "xe_gt.h"
+#include "xe_gt_sriov_vf.h"
#include "xe_hw_engine_class_sysfs.h"
#include "xe_hw_engine_group.h"
#include "xe_hw_fence.h"
@@ -28,6 +30,29 @@
#include "xe_vm.h"
#include "xe_pxp.h"
+/**
+ * DOC: Execution Queue
+ *
+ * An Execution queue is an interface for the HW context of execution.
+ * The user creates an execution queue, submits the GPU jobs through those
+ * queues and in the end destroys them.
+ *
+ * Execution queues can also be created by XeKMD itself for driver internal
+ * operations like object migration etc.
+ *
+ * An execution queue is associated with a specified HW engine or a group of
+ * engines (belonging to the same tile and engine class) and any GPU job
+ * submitted on the queue will be run on one of these engines.
+ *
+ * An execution queue is tied to an address space (VM). It holds a reference
+ * of the associated VM and the underlying Logical Ring Context/s (LRC/s)
+ * until the queue is destroyed.
+ *
+ * The execution queue sits on top of the submission backend. It opaquely
+ * handles the GuC and Execlist backends whichever the platform uses, and
+ * the ring operations the different engine classes support.
+ */
+
enum xe_exec_queue_sched_prop {
XE_EXEC_QUEUE_JOB_TIMEOUT = 0,
XE_EXEC_QUEUE_TIMESLICE = 1,
@@ -160,7 +185,7 @@ static struct xe_exec_queue *__xe_exec_queue_alloc(struct xe_device *xe,
return q;
}
-static int __xe_exec_queue_init(struct xe_exec_queue *q)
+static int __xe_exec_queue_init(struct xe_exec_queue *q, u32 exec_queue_flags)
{
int i, err;
u32 flags = 0;
@@ -179,17 +204,37 @@ static int __xe_exec_queue_init(struct xe_exec_queue *q)
flags |= XE_LRC_CREATE_RUNALONE;
}
+ if (!(exec_queue_flags & EXEC_QUEUE_FLAG_KERNEL))
+ flags |= XE_LRC_CREATE_USER_CTX;
+
+ err = q->ops->init(q);
+ if (err)
+ return err;
+
+ /*
+ * This must occur after q->ops->init to avoid race conditions during VF
+ * post-migration recovery, as the fixups for the LRC GGTT addresses
+ * depend on the queue being present in the backend tracking structure.
+ *
+ * In addition to above, we must wait on inflight GGTT changes to avoid
+ * writing out stale values here. Such wait provides a solid solution
+ * (without a race) only if the function can detect migration instantly
+ * from the moment vCPU resumes execution.
+ */
for (i = 0; i < q->width; ++i) {
- q->lrc[i] = xe_lrc_create(q->hwe, q->vm, SZ_16K, q->msix_vec, flags);
- if (IS_ERR(q->lrc[i])) {
- err = PTR_ERR(q->lrc[i]);
+ struct xe_lrc *lrc;
+
+ xe_gt_sriov_vf_wait_valid_ggtt(q->gt);
+ lrc = xe_lrc_create(q->hwe, q->vm, xe_lrc_ring_size(),
+ q->msix_vec, flags);
+ if (IS_ERR(lrc)) {
+ err = PTR_ERR(lrc);
goto err_lrc;
}
- }
- err = q->ops->init(q);
- if (err)
- goto err_lrc;
+ /* Pairs with READ_ONCE to xe_exec_queue_contexts_hwsp_rebase */
+ WRITE_ONCE(q->lrc[i], lrc);
+ }
return 0;
@@ -225,7 +270,7 @@ struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *v
if (IS_ERR(q))
return q;
- err = __xe_exec_queue_init(q);
+ err = __xe_exec_queue_init(q, flags);
if (err)
goto err_post_alloc;
@@ -324,6 +369,16 @@ struct xe_exec_queue *xe_exec_queue_create_bind(struct xe_device *xe,
}
xe_vm_put(migrate_vm);
+ if (!IS_ERR(q)) {
+ int err = drm_syncobj_create(&q->ufence_syncobj,
+ DRM_SYNCOBJ_CREATE_SIGNALED,
+ NULL);
+ if (err) {
+ xe_exec_queue_put(q);
+ return ERR_PTR(err);
+ }
+ }
+
return q;
}
ALLOW_ERROR_INJECTION(xe_exec_queue_create_bind, ERRNO);
@@ -332,11 +387,20 @@ void xe_exec_queue_destroy(struct kref *ref)
{
struct xe_exec_queue *q = container_of(ref, struct xe_exec_queue, refcount);
struct xe_exec_queue *eq, *next;
+ int i;
+
+ xe_assert(gt_to_xe(q->gt), atomic_read(&q->job_cnt) == 0);
+
+ if (q->ufence_syncobj)
+ drm_syncobj_put(q->ufence_syncobj);
if (xe_exec_queue_uses_pxp(q))
xe_pxp_exec_queue_remove(gt_to_xe(q->gt)->pxp, q);
xe_exec_queue_last_fence_put_unlocked(q);
+ for_each_tlb_inval(i)
+ xe_exec_queue_tlb_inval_last_fence_put_unlocked(q, i);
+
if (!(q->flags & EXEC_QUEUE_FLAG_BIND_ENGINE_CHILD)) {
list_for_each_entry_safe(eq, next, &q->multi_gt_list,
multi_gt_link)
@@ -824,25 +888,6 @@ bool xe_exec_queue_is_lr(struct xe_exec_queue *q)
!(q->flags & EXEC_QUEUE_FLAG_VM);
}
-static s32 xe_exec_queue_num_job_inflight(struct xe_exec_queue *q)
-{
- return q->lrc[0]->fence_ctx.next_seqno - xe_lrc_seqno(q->lrc[0]) - 1;
-}
-
-/**
- * xe_exec_queue_ring_full() - Whether an exec_queue's ring is full
- * @q: The exec_queue
- *
- * Return: True if the exec_queue's ring is full, false otherwise.
- */
-bool xe_exec_queue_ring_full(struct xe_exec_queue *q)
-{
- struct xe_lrc *lrc = q->lrc[0];
- s32 max_job = lrc->ring.size / MAX_JOB_SIZE_BYTES;
-
- return xe_exec_queue_num_job_inflight(q) >= max_job;
-}
-
/**
* xe_exec_queue_is_idle() - Whether an exec_queue is idle.
* @q: The exec_queue
@@ -973,7 +1018,9 @@ int xe_exec_queue_destroy_ioctl(struct drm_device *dev, void *data,
static void xe_exec_queue_last_fence_lockdep_assert(struct xe_exec_queue *q,
struct xe_vm *vm)
{
- if (q->flags & EXEC_QUEUE_FLAG_VM) {
+ if (q->flags & EXEC_QUEUE_FLAG_MIGRATE) {
+ xe_migrate_job_lock_assert(q);
+ } else if (q->flags & EXEC_QUEUE_FLAG_VM) {
lockdep_assert_held(&vm->lock);
} else {
xe_vm_assert_held(vm);
@@ -1072,32 +1119,104 @@ void xe_exec_queue_last_fence_set(struct xe_exec_queue *q, struct xe_vm *vm,
struct dma_fence *fence)
{
xe_exec_queue_last_fence_lockdep_assert(q, vm);
+ xe_assert(vm->xe, !dma_fence_is_container(fence));
xe_exec_queue_last_fence_put(q, vm);
q->last_fence = dma_fence_get(fence);
}
/**
- * xe_exec_queue_last_fence_test_dep - Test last fence dependency of queue
+ * xe_exec_queue_tlb_inval_last_fence_put() - Drop ref to last TLB invalidation fence
* @q: The exec queue
- * @vm: The VM the engine does a bind or exec for
+ * @vm: The VM the engine does a bind for
+ * @type: Either primary or media GT
+ */
+void xe_exec_queue_tlb_inval_last_fence_put(struct xe_exec_queue *q,
+ struct xe_vm *vm,
+ unsigned int type)
+{
+ xe_exec_queue_last_fence_lockdep_assert(q, vm);
+ xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
+ type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+
+ xe_exec_queue_tlb_inval_last_fence_put_unlocked(q, type);
+}
+
+/**
+ * xe_exec_queue_tlb_inval_last_fence_put_unlocked() - Drop ref to last TLB
+ * invalidation fence unlocked
+ * @q: The exec queue
+ * @type: Either primary or media GT
*
- * Returns:
- * -ETIME if there exists an unsignalled last fence dependency, zero otherwise.
+ * Only safe to be called from xe_exec_queue_destroy().
*/
-int xe_exec_queue_last_fence_test_dep(struct xe_exec_queue *q, struct xe_vm *vm)
+void xe_exec_queue_tlb_inval_last_fence_put_unlocked(struct xe_exec_queue *q,
+ unsigned int type)
+{
+ xe_assert(q->vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
+ type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+
+ dma_fence_put(q->tlb_inval[type].last_fence);
+ q->tlb_inval[type].last_fence = NULL;
+}
+
+/**
+ * xe_exec_queue_tlb_inval_last_fence_get() - Get last fence for TLB invalidation
+ * @q: The exec queue
+ * @vm: The VM the engine does a bind for
+ * @type: Either primary or media GT
+ *
+ * Get last fence, takes a ref
+ *
+ * Returns: last fence if not signaled, dma fence stub if signaled
+ */
+struct dma_fence *xe_exec_queue_tlb_inval_last_fence_get(struct xe_exec_queue *q,
+ struct xe_vm *vm,
+ unsigned int type)
{
struct dma_fence *fence;
- int err = 0;
- fence = xe_exec_queue_last_fence_get(q, vm);
- if (fence) {
- err = test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) ?
- 0 : -ETIME;
- dma_fence_put(fence);
- }
+ xe_exec_queue_last_fence_lockdep_assert(q, vm);
+ xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
+ type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+ xe_assert(vm->xe, q->flags & (EXEC_QUEUE_FLAG_VM |
+ EXEC_QUEUE_FLAG_MIGRATE));
- return err;
+ if (q->tlb_inval[type].last_fence &&
+ test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
+ &q->tlb_inval[type].last_fence->flags))
+ xe_exec_queue_tlb_inval_last_fence_put(q, vm, type);
+
+ fence = q->tlb_inval[type].last_fence ?: dma_fence_get_stub();
+ dma_fence_get(fence);
+ return fence;
+}
+
+/**
+ * xe_exec_queue_tlb_inval_last_fence_set() - Set last fence for TLB invalidation
+ * @q: The exec queue
+ * @vm: The VM the engine does a bind for
+ * @fence: The fence
+ * @type: Either primary or media GT
+ *
+ * Set the last fence for the tlb invalidation type on the queue. Increases
+ * reference count for fence, when closing queue
+ * xe_exec_queue_tlb_inval_last_fence_put should be called.
+ */
+void xe_exec_queue_tlb_inval_last_fence_set(struct xe_exec_queue *q,
+ struct xe_vm *vm,
+ struct dma_fence *fence,
+ unsigned int type)
+{
+ xe_exec_queue_last_fence_lockdep_assert(q, vm);
+ xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
+ type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+ xe_assert(vm->xe, q->flags & (EXEC_QUEUE_FLAG_VM |
+ EXEC_QUEUE_FLAG_MIGRATE));
+ xe_assert(vm->xe, !dma_fence_is_container(fence));
+
+ xe_exec_queue_tlb_inval_last_fence_put(q, vm, type);
+ q->tlb_inval[type].last_fence = dma_fence_get(fence);
}
/**
@@ -1114,36 +1233,19 @@ int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch)
int err = 0;
for (i = 0; i < q->width; ++i) {
- xe_lrc_update_memirq_regs_with_address(q->lrc[i], q->hwe, scratch);
- xe_lrc_update_hwctx_regs_with_address(q->lrc[i]);
- err = xe_lrc_setup_wa_bb_with_scratch(q->lrc[i], q->hwe, scratch);
+ struct xe_lrc *lrc;
+
+ /* Pairs with WRITE_ONCE in __xe_exec_queue_init */
+ lrc = READ_ONCE(q->lrc[i]);
+ if (!lrc)
+ continue;
+
+ xe_lrc_update_memirq_regs_with_address(lrc, q->hwe, scratch);
+ xe_lrc_update_hwctx_regs_with_address(lrc);
+ err = xe_lrc_setup_wa_bb_with_scratch(lrc, q->hwe, scratch);
if (err)
break;
}
return err;
}
-
-/**
- * xe_exec_queue_jobs_ring_restore - Re-emit ring commands of requests pending on given queue.
- * @q: the &xe_exec_queue struct instance
- */
-void xe_exec_queue_jobs_ring_restore(struct xe_exec_queue *q)
-{
- struct xe_gpu_scheduler *sched = &q->guc->sched;
- struct xe_sched_job *job;
-
- /*
- * This routine is used within VF migration recovery. This means
- * using the lock here introduces a restriction: we cannot wait
- * for any GFX HW response while the lock is taken.
- */
- spin_lock(&sched->base.job_list_lock);
- list_for_each_entry(job, &sched->base.pending_list, drm.list) {
- if (xe_sched_job_is_error(job))
- continue;
-
- q->ring_ops->emit_job(job);
- }
- spin_unlock(&sched->base.job_list_lock);
-}
diff --git a/drivers/gpu/drm/xe/xe_exec_queue.h b/drivers/gpu/drm/xe/xe_exec_queue.h
index 15ec852e7f7e..fda4d4f9bda8 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue.h
@@ -14,6 +14,10 @@ struct drm_file;
struct xe_device;
struct xe_file;
+#define for_each_tlb_inval(__i) \
+ for (__i = XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT; \
+ __i <= XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT; ++__i)
+
struct xe_exec_queue *xe_exec_queue_create(struct xe_device *xe, struct xe_vm *vm,
u32 logical_mask, u16 width,
struct xe_hw_engine *hw_engine, u32 flags,
@@ -64,8 +68,6 @@ static inline bool xe_exec_queue_uses_pxp(struct xe_exec_queue *q)
bool xe_exec_queue_is_lr(struct xe_exec_queue *q);
-bool xe_exec_queue_ring_full(struct xe_exec_queue *q);
-
bool xe_exec_queue_is_idle(struct xe_exec_queue *q);
void xe_exec_queue_kill(struct xe_exec_queue *q);
@@ -86,13 +88,27 @@ struct dma_fence *xe_exec_queue_last_fence_get_for_resume(struct xe_exec_queue *
struct xe_vm *vm);
void xe_exec_queue_last_fence_set(struct xe_exec_queue *e, struct xe_vm *vm,
struct dma_fence *fence);
-int xe_exec_queue_last_fence_test_dep(struct xe_exec_queue *q,
- struct xe_vm *vm);
+
+void xe_exec_queue_tlb_inval_last_fence_put(struct xe_exec_queue *q,
+ struct xe_vm *vm,
+ unsigned int type);
+
+void xe_exec_queue_tlb_inval_last_fence_put_unlocked(struct xe_exec_queue *q,
+ unsigned int type);
+
+struct dma_fence *xe_exec_queue_tlb_inval_last_fence_get(struct xe_exec_queue *q,
+ struct xe_vm *vm,
+ unsigned int type);
+
+void xe_exec_queue_tlb_inval_last_fence_set(struct xe_exec_queue *q,
+ struct xe_vm *vm,
+ struct dma_fence *fence,
+ unsigned int type);
+
void xe_exec_queue_update_run_ticks(struct xe_exec_queue *q);
int xe_exec_queue_contexts_hwsp_rebase(struct xe_exec_queue *q, void *scratch);
-void xe_exec_queue_jobs_ring_restore(struct xe_exec_queue *q);
-
struct xe_lrc *xe_exec_queue_lrc(struct xe_exec_queue *q);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h
index 27b76cf9da89..771ffe35cd0c 100644
--- a/drivers/gpu/drm/xe/xe_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h
@@ -15,6 +15,7 @@
#include "xe_hw_fence_types.h"
#include "xe_lrc_types.h"
+struct drm_syncobj;
struct xe_execlist_exec_queue;
struct xe_gt;
struct xe_guc_exec_queue;
@@ -145,6 +146,11 @@ struct xe_exec_queue {
* dependency scheduler
*/
struct xe_dep_scheduler *dep_scheduler;
+ /**
+ * @last_fence: last fence for tlb invalidation, protected by
+ * vm->lock in write mode
+ */
+ struct dma_fence *last_fence;
} tlb_inval[XE_EXEC_QUEUE_TLB_INVAL_COUNT];
/** @pxp: PXP info tracking */
@@ -155,6 +161,12 @@ struct xe_exec_queue {
struct list_head link;
} pxp;
+ /** @ufence_syncobj: User fence syncobj */
+ struct drm_syncobj *ufence_syncobj;
+
+ /** @ufence_timeline_value: User fence timeline value */
+ u64 ufence_timeline_value;
+
/** @ops: submission backend exec queue operations */
const struct xe_exec_queue_ops *ops;
@@ -162,6 +174,11 @@ struct xe_exec_queue {
const struct xe_ring_ops *ring_ops;
/** @entity: DRM sched entity for this exec queue (1 to 1 relationship) */
struct drm_sched_entity *entity;
+
+#define XE_MAX_JOB_COUNT_PER_EXEC_QUEUE 1000
+ /** @job_cnt: number of drm jobs in this exec queue */
+ atomic_t job_cnt;
+
/**
* @tlb_flush_seqno: The seqno of the last rebind tlb flush performed
* Protected by @vm's resv. Unused if @vm == NULL.
@@ -207,6 +224,9 @@ struct xe_exec_queue_ops {
* call after suspend. In dma-fencing path thus must return within a
* reasonable amount of time. -ETIME return shall indicate an error
* waiting for suspend resulting in associated VM getting killed.
+ * -EAGAIN return indicates the wait should be tried again, if the wait
+ * is within a work item, the work item should be requeued as deadlock
+ * avoidance mechanism.
*/
int (*suspend_wait)(struct xe_exec_queue *q);
/**
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index f83d421ac9d3..769d05517f93 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -339,7 +339,7 @@ static int execlist_exec_queue_init(struct xe_exec_queue *q)
const struct drm_sched_init_args args = {
.ops = &drm_sched_ops,
.num_rqs = 1,
- .credit_limit = q->lrc[0]->ring.size / MAX_JOB_SIZE_BYTES,
+ .credit_limit = xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES,
.hang_limit = XE_SCHED_HANG_LIMIT,
.timeout = XE_SCHED_JOB_TIMEOUT,
.name = q->hwe->name,
diff --git a/drivers/gpu/drm/xe/xe_force_wake_types.h b/drivers/gpu/drm/xe/xe_force_wake_types.h
index 899fbbcb3ea9..14b7b86e801b 100644
--- a/drivers/gpu/drm/xe/xe_force_wake_types.h
+++ b/drivers/gpu/drm/xe/xe_force_wake_types.h
@@ -52,7 +52,22 @@ enum xe_force_wake_domains {
};
/**
- * struct xe_force_wake_domain - XE force wake domains
+ * struct xe_force_wake_domain - Xe force wake power domain
+ *
+ * Represents an individual device-internal power domain. The driver must
+ * ensure the power domain is awake before accessing registers or other
+ * hardware functionality that is part of the power domain. Since different
+ * driver threads may access hardware units simultaneously, a reference count
+ * is used to ensure that the domain remains awake as long as any software
+ * is using the part of the hardware covered by the power domain.
+ *
+ * Hardware provides a register interface to allow the driver to request
+ * wake/sleep of power domains, although in most cases the actual action of
+ * powering the hardware up/down is handled by firmware (and may be subject to
+ * requirements and constraints outside of the driver's visibility) so the
+ * driver needs to wait for an acknowledgment that a wake request has been
+ * acted upon before accessing the parts of the hardware that reside within the
+ * power domain.
*/
struct xe_force_wake_domain {
/** @id: domain force wake id */
@@ -70,7 +85,14 @@ struct xe_force_wake_domain {
};
/**
- * struct xe_force_wake - XE force wake
+ * struct xe_force_wake - Xe force wake collection
+ *
+ * Represents a collection of related power domains (struct
+ * xe_force_wake_domain) associated with a subunit of the device.
+ *
+ * Currently only used for GT power domains (where the term "forcewake" is used
+ * in the hardware documentation), although the interface could be extended to
+ * power wells in other parts of the hardware in the future.
*/
struct xe_force_wake {
/** @gt: back pointers to GT */
diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c
index 7fdd0a97a628..ef481b334af4 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.c
+++ b/drivers/gpu/drm/xe/xe_ggtt.c
@@ -107,10 +107,23 @@ static unsigned int probe_gsm_size(struct pci_dev *pdev)
static void ggtt_update_access_counter(struct xe_ggtt *ggtt)
{
struct xe_tile *tile = ggtt->tile;
- struct xe_gt *affected_gt = XE_GT_WA(tile->primary_gt, 22019338487) ?
- tile->primary_gt : tile->media_gt;
- struct xe_mmio *mmio = &affected_gt->mmio;
- u32 max_gtt_writes = XE_GT_WA(ggtt->tile->primary_gt, 22019338487) ? 1100 : 63;
+ struct xe_gt *affected_gt;
+ u32 max_gtt_writes;
+
+ if (tile->primary_gt && XE_GT_WA(tile->primary_gt, 22019338487)) {
+ affected_gt = tile->primary_gt;
+ max_gtt_writes = 1100;
+
+ /* Only expected to apply to primary GT on dgpu platforms */
+ xe_tile_assert(tile, IS_DGFX(tile_to_xe(tile)));
+ } else {
+ affected_gt = tile->media_gt;
+ max_gtt_writes = 63;
+
+ /* Only expected to apply to media GT on igpu platforms */
+ xe_tile_assert(tile, !IS_DGFX(tile_to_xe(tile)));
+ }
+
/*
* Wa_22019338487: GMD_ID is a RO register, a dummy write forces gunit
* to wait for completion of prior GTT writes before letting this through.
@@ -119,7 +132,7 @@ static void ggtt_update_access_counter(struct xe_ggtt *ggtt)
lockdep_assert_held(&ggtt->lock);
if ((++ggtt->access_count % max_gtt_writes) == 0) {
- xe_mmio_write32(mmio, GMD_ID, 0x0);
+ xe_mmio_write32(&affected_gt->mmio, GMD_ID, 0x0);
ggtt->access_count = 0;
}
}
@@ -138,6 +151,14 @@ static void xe_ggtt_set_pte_and_flush(struct xe_ggtt *ggtt, u64 addr, u64 pte)
ggtt_update_access_counter(ggtt);
}
+static u64 xe_ggtt_get_pte(struct xe_ggtt *ggtt, u64 addr)
+{
+ xe_tile_assert(ggtt->tile, !(addr & XE_PTE_MASK));
+ xe_tile_assert(ggtt->tile, addr < ggtt->size);
+
+ return readq(&ggtt->gsm[addr >> XE_PTE_SHIFT]);
+}
+
static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
{
u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[XE_CACHE_WB];
@@ -159,6 +180,16 @@ static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
}
}
+static void primelockdep(struct xe_ggtt *ggtt)
+{
+ if (!IS_ENABLED(CONFIG_LOCKDEP))
+ return;
+
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&ggtt->lock);
+ fs_reclaim_release(GFP_KERNEL);
+}
+
/**
* xe_ggtt_alloc - Allocate a GGTT for a given &xe_tile
* @tile: &xe_tile
@@ -169,9 +200,19 @@ static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
*/
struct xe_ggtt *xe_ggtt_alloc(struct xe_tile *tile)
{
- struct xe_ggtt *ggtt = drmm_kzalloc(&tile_to_xe(tile)->drm, sizeof(*ggtt), GFP_KERNEL);
- if (ggtt)
- ggtt->tile = tile;
+ struct xe_device *xe = tile_to_xe(tile);
+ struct xe_ggtt *ggtt;
+
+ ggtt = drmm_kzalloc(&xe->drm, sizeof(*ggtt), GFP_KERNEL);
+ if (!ggtt)
+ return NULL;
+
+ if (drmm_mutex_init(&xe->drm, &ggtt->lock))
+ return NULL;
+
+ primelockdep(ggtt);
+ ggtt->tile = tile;
+
return ggtt;
}
@@ -180,7 +221,6 @@ static void ggtt_fini_early(struct drm_device *drm, void *arg)
struct xe_ggtt *ggtt = arg;
destroy_workqueue(ggtt->wq);
- mutex_destroy(&ggtt->lock);
drm_mm_takedown(&ggtt->mm);
}
@@ -198,37 +238,28 @@ void xe_ggtt_might_lock(struct xe_ggtt *ggtt)
}
#endif
-static void primelockdep(struct xe_ggtt *ggtt)
-{
- if (!IS_ENABLED(CONFIG_LOCKDEP))
- return;
-
- fs_reclaim_acquire(GFP_KERNEL);
- might_lock(&ggtt->lock);
- fs_reclaim_release(GFP_KERNEL);
-}
-
static const struct xe_ggtt_pt_ops xelp_pt_ops = {
.pte_encode_flags = xelp_ggtt_pte_flags,
.ggtt_set_pte = xe_ggtt_set_pte,
+ .ggtt_get_pte = xe_ggtt_get_pte,
};
static const struct xe_ggtt_pt_ops xelpg_pt_ops = {
.pte_encode_flags = xelpg_ggtt_pte_flags,
.ggtt_set_pte = xe_ggtt_set_pte,
+ .ggtt_get_pte = xe_ggtt_get_pte,
};
static const struct xe_ggtt_pt_ops xelpg_pt_wa_ops = {
.pte_encode_flags = xelpg_ggtt_pte_flags,
.ggtt_set_pte = xe_ggtt_set_pte_and_flush,
+ .ggtt_get_pte = xe_ggtt_get_pte,
};
static void __xe_ggtt_init_early(struct xe_ggtt *ggtt, u32 reserved)
{
drm_mm_init(&ggtt->mm, reserved,
ggtt->size - reserved);
- mutex_init(&ggtt->lock);
- primelockdep(ggtt);
}
int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size)
@@ -284,14 +315,17 @@ int xe_ggtt_init_early(struct xe_ggtt *ggtt)
ggtt->size = GUC_GGTT_TOP;
if (GRAPHICS_VERx100(xe) >= 1270)
- ggtt->pt_ops = (ggtt->tile->media_gt &&
- XE_GT_WA(ggtt->tile->media_gt, 22019338487)) ||
- XE_GT_WA(ggtt->tile->primary_gt, 22019338487) ?
- &xelpg_pt_wa_ops : &xelpg_pt_ops;
+ ggtt->pt_ops =
+ (ggtt->tile->media_gt && XE_GT_WA(ggtt->tile->media_gt, 22019338487)) ||
+ (ggtt->tile->primary_gt && XE_GT_WA(ggtt->tile->primary_gt, 22019338487)) ?
+ &xelpg_pt_wa_ops : &xelpg_pt_ops;
else
ggtt->pt_ops = &xelp_pt_ops;
ggtt->wq = alloc_workqueue("xe-ggtt-wq", 0, WQ_MEM_RECLAIM);
+ if (!ggtt->wq)
+ return -ENOMEM;
+
__xe_ggtt_init_early(ggtt, xe_wopcm_size(xe));
err = drmm_add_action_or_reset(&xe->drm, ggtt_fini_early, ggtt);
@@ -675,6 +709,20 @@ bool xe_ggtt_node_allocated(const struct xe_ggtt_node *node)
}
/**
+ * xe_ggtt_node_pt_size() - Get the size of page table entries needed to map a GGTT node.
+ * @node: the &xe_ggtt_node
+ *
+ * Return: GGTT node page table entries size in bytes.
+ */
+size_t xe_ggtt_node_pt_size(const struct xe_ggtt_node *node)
+{
+ if (!node)
+ return 0;
+
+ return node->base.size / XE_PAGE_SIZE * sizeof(u64);
+}
+
+/**
* xe_ggtt_map_bo - Map the BO into GGTT
* @ggtt: the &xe_ggtt where node will be mapped
* @node: the &xe_ggtt_node where this BO is mapped
@@ -907,6 +955,85 @@ void xe_ggtt_assign(const struct xe_ggtt_node *node, u16 vfid)
xe_ggtt_assign_locked(node->ggtt, &node->base, vfid);
mutex_unlock(&node->ggtt->lock);
}
+
+/**
+ * xe_ggtt_node_save() - Save a &xe_ggtt_node to a buffer.
+ * @node: the &xe_ggtt_node to be saved
+ * @dst: destination buffer
+ * @size: destination buffer size in bytes
+ * @vfid: VF identifier
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_ggtt_node_save(struct xe_ggtt_node *node, void *dst, size_t size, u16 vfid)
+{
+ struct xe_ggtt *ggtt;
+ u64 start, end;
+ u64 *buf = dst;
+ u64 pte;
+
+ if (!node)
+ return -ENOENT;
+
+ guard(mutex)(&node->ggtt->lock);
+
+ if (xe_ggtt_node_pt_size(node) != size)
+ return -EINVAL;
+
+ ggtt = node->ggtt;
+ start = node->base.start;
+ end = start + node->base.size - 1;
+
+ while (start < end) {
+ pte = ggtt->pt_ops->ggtt_get_pte(ggtt, start);
+ if (vfid != u64_get_bits(pte, GGTT_PTE_VFID))
+ return -EPERM;
+
+ *buf++ = u64_replace_bits(pte, 0, GGTT_PTE_VFID);
+ start += XE_PAGE_SIZE;
+ }
+
+ return 0;
+}
+
+/**
+ * xe_ggtt_node_load() - Load a &xe_ggtt_node from a buffer.
+ * @node: the &xe_ggtt_node to be loaded
+ * @src: source buffer
+ * @size: source buffer size in bytes
+ * @vfid: VF identifier
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_ggtt_node_load(struct xe_ggtt_node *node, const void *src, size_t size, u16 vfid)
+{
+ u64 vfid_pte = xe_encode_vfid_pte(vfid);
+ const u64 *buf = src;
+ struct xe_ggtt *ggtt;
+ u64 start, end;
+
+ if (!node)
+ return -ENOENT;
+
+ guard(mutex)(&node->ggtt->lock);
+
+ if (xe_ggtt_node_pt_size(node) != size)
+ return -EINVAL;
+
+ ggtt = node->ggtt;
+ start = node->base.start;
+ end = start + node->base.size - 1;
+
+ while (start < end) {
+ vfid_pte = u64_replace_bits(*buf++, vfid, GGTT_PTE_VFID);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, start, vfid_pte);
+ start += XE_PAGE_SIZE;
+ }
+ xe_ggtt_invalidate(ggtt);
+
+ return 0;
+}
+
#endif
/**
diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h
index 75fc7a1efea7..93fea4b6079c 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.h
+++ b/drivers/gpu/drm/xe/xe_ggtt.h
@@ -29,6 +29,7 @@ int xe_ggtt_node_insert_locked(struct xe_ggtt_node *node,
u32 size, u32 align, u32 mm_flags);
void xe_ggtt_node_remove(struct xe_ggtt_node *node, bool invalidate);
bool xe_ggtt_node_allocated(const struct xe_ggtt_node *node);
+size_t xe_ggtt_node_pt_size(const struct xe_ggtt_node *node);
void xe_ggtt_map_bo(struct xe_ggtt *ggtt, struct xe_ggtt_node *node,
struct xe_bo *bo, u16 pat_index);
void xe_ggtt_map_bo_unlocked(struct xe_ggtt *ggtt, struct xe_bo *bo);
@@ -43,6 +44,8 @@ u64 xe_ggtt_print_holes(struct xe_ggtt *ggtt, u64 alignment, struct drm_printer
#ifdef CONFIG_PCI_IOV
void xe_ggtt_assign(const struct xe_ggtt_node *node, u16 vfid);
+int xe_ggtt_node_save(struct xe_ggtt_node *node, void *dst, size_t size, u16 vfid);
+int xe_ggtt_node_load(struct xe_ggtt_node *node, const void *src, size_t size, u16 vfid);
#endif
#ifndef CONFIG_LOCKDEP
diff --git a/drivers/gpu/drm/xe/xe_ggtt_types.h b/drivers/gpu/drm/xe/xe_ggtt_types.h
index c5e999d58ff2..dacd796f8184 100644
--- a/drivers/gpu/drm/xe/xe_ggtt_types.h
+++ b/drivers/gpu/drm/xe/xe_ggtt_types.h
@@ -78,6 +78,8 @@ struct xe_ggtt_pt_ops {
u64 (*pte_encode_flags)(struct xe_bo *bo, u16 pat_index);
/** @ggtt_set_pte: Directly write into GGTT's PTE */
void (*ggtt_set_pte)(struct xe_ggtt *ggtt, u64 addr, u64 pte);
+ /** @ggtt_get_pte: Directly read from GGTT's PTE */
+ u64 (*ggtt_get_pte)(struct xe_ggtt *ggtt, u64 addr);
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
index 455ccaf17314..f91e06d03511 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
@@ -101,19 +101,6 @@ void xe_sched_submission_stop(struct xe_gpu_scheduler *sched)
cancel_work_sync(&sched->work_process_msg);
}
-/**
- * xe_sched_submission_stop_async - Stop further runs of submission tasks on a scheduler.
- * @sched: the &xe_gpu_scheduler struct instance
- *
- * This call disables further runs of scheduling work queue. It does not wait
- * for any in-progress runs to finish, only makes sure no further runs happen
- * afterwards.
- */
-void xe_sched_submission_stop_async(struct xe_gpu_scheduler *sched)
-{
- drm_sched_wqueue_stop(&sched->base);
-}
-
void xe_sched_submission_resume_tdr(struct xe_gpu_scheduler *sched)
{
drm_sched_resume_timeout(&sched->base, sched->base.timeout);
@@ -135,3 +122,17 @@ void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
list_add_tail(&msg->link, &sched->msgs);
xe_sched_process_msg_queue(sched);
}
+
+/**
+ * xe_sched_add_msg_head() - Xe GPU scheduler add message to head of list
+ * @sched: Xe GPU scheduler
+ * @msg: Message to add
+ */
+void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched,
+ struct xe_sched_msg *msg)
+{
+ lockdep_assert_held(&sched->base.job_list_lock);
+
+ list_add(&msg->link, &sched->msgs);
+ xe_sched_process_msg_queue(sched);
+}
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.h b/drivers/gpu/drm/xe/xe_gpu_scheduler.h
index e548b2aed95a..c7a77a3a9681 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.h
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.h
@@ -7,7 +7,7 @@
#define _XE_GPU_SCHEDULER_H_
#include "xe_gpu_scheduler_types.h"
-#include "xe_sched_job_types.h"
+#include "xe_sched_job.h"
int xe_sched_init(struct xe_gpu_scheduler *sched,
const struct drm_sched_backend_ops *ops,
@@ -21,7 +21,6 @@ void xe_sched_fini(struct xe_gpu_scheduler *sched);
void xe_sched_submission_start(struct xe_gpu_scheduler *sched);
void xe_sched_submission_stop(struct xe_gpu_scheduler *sched);
-void xe_sched_submission_stop_async(struct xe_gpu_scheduler *sched);
void xe_sched_submission_resume_tdr(struct xe_gpu_scheduler *sched);
@@ -29,6 +28,8 @@ void xe_sched_add_msg(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg);
void xe_sched_add_msg_locked(struct xe_gpu_scheduler *sched,
struct xe_sched_msg *msg);
+void xe_sched_add_msg_head(struct xe_gpu_scheduler *sched,
+ struct xe_sched_msg *msg);
static inline void xe_sched_msg_lock(struct xe_gpu_scheduler *sched)
{
@@ -53,12 +54,14 @@ static inline void xe_sched_tdr_queue_imm(struct xe_gpu_scheduler *sched)
static inline void xe_sched_resubmit_jobs(struct xe_gpu_scheduler *sched)
{
struct drm_sched_job *s_job;
+ bool restore_replay = false;
list_for_each_entry(s_job, &sched->base.pending_list, list) {
struct drm_sched_fence *s_fence = s_job->s_fence;
struct dma_fence *hw_fence = s_fence->parent;
- if (hw_fence && !dma_fence_is_signaled(hw_fence))
+ restore_replay |= to_xe_sched_job(s_job)->restore_replay;
+ if (restore_replay || (hw_fence && !dma_fence_is_signaled(hw_fence)))
sched->base.ops->run_job(s_job);
}
}
@@ -77,17 +80,30 @@ static inline void xe_sched_add_pending_job(struct xe_gpu_scheduler *sched,
spin_unlock(&sched->base.job_list_lock);
}
+/**
+ * xe_sched_first_pending_job() - Find first pending job which is unsignaled
+ * @sched: Xe GPU scheduler
+ *
+ * Return first unsignaled job in pending list or NULL
+ */
static inline
struct xe_sched_job *xe_sched_first_pending_job(struct xe_gpu_scheduler *sched)
{
- struct xe_sched_job *job;
+ struct xe_sched_job *job, *r_job = NULL;
spin_lock(&sched->base.job_list_lock);
- job = list_first_entry_or_null(&sched->base.pending_list,
- struct xe_sched_job, drm.list);
+ list_for_each_entry(job, &sched->base.pending_list, drm.list) {
+ struct drm_sched_fence *s_fence = job->drm.s_fence;
+ struct dma_fence *hw_fence = s_fence->parent;
+
+ if (hw_fence && !dma_fence_is_signaled(hw_fence)) {
+ r_job = job;
+ break;
+ }
+ }
spin_unlock(&sched->base.job_list_lock);
- return job;
+ return r_job;
}
static inline int
diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c
index 83d61bf8ec62..dd69cb834f8e 100644
--- a/drivers/gpu/drm/xe/xe_gsc.c
+++ b/drivers/gpu/drm/xe/xe_gsc.c
@@ -266,7 +266,7 @@ static int gsc_upload_and_init(struct xe_gsc *gsc)
unsigned int fw_ref;
int ret;
- if (XE_GT_WA(tile->primary_gt, 14018094691)) {
+ if (tile->primary_gt && XE_GT_WA(tile->primary_gt, 14018094691)) {
fw_ref = xe_force_wake_get(gt_to_fw(tile->primary_gt), XE_FORCEWAKE_ALL);
/*
@@ -281,7 +281,7 @@ static int gsc_upload_and_init(struct xe_gsc *gsc)
ret = gsc_upload(gsc);
- if (XE_GT_WA(tile->primary_gt, 14018094691))
+ if (tile->primary_gt && XE_GT_WA(tile->primary_gt, 14018094691))
xe_force_wake_put(gt_to_fw(tile->primary_gt), fw_ref);
if (ret)
diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
index 3e0ad7e5b5df..dbb5e7a9bc6a 100644
--- a/drivers/gpu/drm/xe/xe_gt.c
+++ b/drivers/gpu/drm/xe/xe_gt.c
@@ -32,7 +32,6 @@
#include "xe_gt_freq.h"
#include "xe_gt_idle.h"
#include "xe_gt_mcr.h"
-#include "xe_gt_pagefault.h"
#include "xe_gt_printk.h"
#include "xe_gt_sriov_pf.h"
#include "xe_gt_sriov_vf.h"
@@ -49,6 +48,7 @@
#include "xe_map.h"
#include "xe_migrate.h"
#include "xe_mmio.h"
+#include "xe_pagefault.h"
#include "xe_pat.h"
#include "xe_pm.h"
#include "xe_mocs.h"
@@ -65,29 +65,29 @@
#include "xe_wa.h"
#include "xe_wopcm.h"
-static void gt_fini(struct drm_device *drm, void *arg)
-{
- struct xe_gt *gt = arg;
-
- destroy_workqueue(gt->ordered_wq);
-}
-
struct xe_gt *xe_gt_alloc(struct xe_tile *tile)
{
+ struct xe_device *xe = tile_to_xe(tile);
+ struct drm_device *drm = &xe->drm;
+ bool shared_wq = xe->info.needs_shared_vf_gt_wq && tile->primary_gt &&
+ IS_SRIOV_VF(xe);
+ struct workqueue_struct *ordered_wq;
struct xe_gt *gt;
- int err;
- gt = drmm_kzalloc(&tile_to_xe(tile)->drm, sizeof(*gt), GFP_KERNEL);
+ gt = drmm_kzalloc(drm, sizeof(*gt), GFP_KERNEL);
if (!gt)
return ERR_PTR(-ENOMEM);
gt->tile = tile;
- gt->ordered_wq = alloc_ordered_workqueue("gt-ordered-wq",
- WQ_MEM_RECLAIM);
+ if (shared_wq && tile->primary_gt->ordered_wq)
+ ordered_wq = tile->primary_gt->ordered_wq;
+ else
+ ordered_wq = drmm_alloc_ordered_workqueue(drm, "gt-ordered-wq",
+ WQ_MEM_RECLAIM);
+ if (IS_ERR(ordered_wq))
+ return ERR_CAST(ordered_wq);
- err = drmm_add_action_or_reset(&gt_to_xe(gt)->drm, gt_fini, gt);
- if (err)
- return ERR_PTR(err);
+ gt->ordered_wq = ordered_wq;
return gt;
}
@@ -398,6 +398,12 @@ int xe_gt_init_early(struct xe_gt *gt)
return err;
}
+ if (IS_SRIOV_VF(gt_to_xe(gt))) {
+ err = xe_gt_sriov_vf_init_early(gt);
+ if (err)
+ return err;
+ }
+
xe_reg_sr_init(&gt->reg_sr, "GT", gt_to_xe(gt));
err = xe_wa_gt_init(gt);
@@ -583,10 +589,8 @@ static int gt_init_with_all_forcewake(struct xe_gt *gt)
if (IS_SRIOV_PF(gt_to_xe(gt)) && xe_gt_is_main_type(gt))
xe_lmtt_init_hw(&gt_to_tile(gt)->sriov.pf.lmtt);
- if (IS_SRIOV_PF(gt_to_xe(gt))) {
- xe_gt_sriov_pf_init(gt);
+ if (IS_SRIOV_PF(gt_to_xe(gt)))
xe_gt_sriov_pf_init_hw(gt);
- }
xe_force_wake_put(gt_to_fw(gt), fw_ref);
@@ -603,6 +607,13 @@ static void xe_gt_fini(void *arg)
struct xe_gt *gt = arg;
int i;
+ if (disable_work_sync(&gt->reset.worker))
+ /*
+ * If gt_reset_worker was halted from executing, take care of
+ * releasing the rpm reference here.
+ */
+ xe_pm_runtime_put(gt_to_xe(gt));
+
for (i = 0; i < XE_ENGINE_CLASS_MAX; ++i)
xe_hw_fence_irq_finish(&gt->fence_irq[i]);
@@ -633,10 +644,6 @@ int xe_gt_init(struct xe_gt *gt)
if (err)
return err;
- err = xe_gt_pagefault_init(gt);
- if (err)
- return err;
-
err = xe_gt_idle_init(&gt->gtidle);
if (err)
return err;
@@ -657,6 +664,12 @@ int xe_gt_init(struct xe_gt *gt)
if (err)
return err;
+ if (IS_SRIOV_VF(gt_to_xe(gt))) {
+ err = xe_gt_sriov_vf_init(gt);
+ if (err)
+ return err;
+ }
+
return 0;
}
@@ -803,31 +816,21 @@ static int do_gt_restart(struct xe_gt *gt)
return 0;
}
-static int gt_wait_reset_unblock(struct xe_gt *gt)
-{
- return xe_guc_wait_reset_unblock(&gt->uc.guc);
-}
-
-static int gt_reset(struct xe_gt *gt)
+static void gt_reset_worker(struct work_struct *w)
{
+ struct xe_gt *gt = container_of(w, typeof(*gt), reset.worker);
unsigned int fw_ref;
int err;
if (xe_device_wedged(gt_to_xe(gt)))
- return -ECANCELED;
+ goto err_pm_put;
/* We only support GT resets with GuC submission */
if (!xe_device_uc_enabled(gt_to_xe(gt)))
- return -ENODEV;
+ goto err_pm_put;
xe_gt_info(gt, "reset started\n");
- err = gt_wait_reset_unblock(gt);
- if (!err)
- xe_gt_warn(gt, "reset block failed to get lifted");
-
- xe_pm_runtime_get(gt_to_xe(gt));
-
if (xe_fault_inject_gt_reset()) {
err = -ECANCELED;
goto err_fail;
@@ -846,7 +849,7 @@ static int gt_reset(struct xe_gt *gt)
xe_uc_gucrc_disable(&gt->uc);
xe_uc_stop_prepare(&gt->uc);
- xe_gt_pagefault_reset(gt);
+ xe_pagefault_reset(gt_to_xe(gt), gt);
xe_uc_stop(&gt->uc);
@@ -861,29 +864,23 @@ static int gt_reset(struct xe_gt *gt)
goto err_out;
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+
+ /* Pair with get while enqueueing the work in xe_gt_reset_async() */
xe_pm_runtime_put(gt_to_xe(gt));
xe_gt_info(gt, "reset done\n");
- return 0;
+ return;
err_out:
xe_force_wake_put(gt_to_fw(gt), fw_ref);
XE_WARN_ON(xe_uc_start(&gt->uc));
+
err_fail:
xe_gt_err(gt, "reset failed (%pe)\n", ERR_PTR(err));
-
xe_device_declare_wedged(gt_to_xe(gt));
+err_pm_put:
xe_pm_runtime_put(gt_to_xe(gt));
-
- return err;
-}
-
-static void gt_reset_worker(struct work_struct *w)
-{
- struct xe_gt *gt = container_of(w, typeof(*gt), reset.worker);
-
- gt_reset(gt);
}
void xe_gt_reset_async(struct xe_gt *gt)
@@ -895,7 +892,11 @@ void xe_gt_reset_async(struct xe_gt *gt)
return;
xe_gt_info(gt, "reset queued\n");
- queue_work(gt->ordered_wq, &gt->reset.worker);
+
+ /* Pair with put in gt_reset_worker() if work is enqueued */
+ xe_pm_runtime_get_noresume(gt_to_xe(gt));
+ if (!queue_work(gt->ordered_wq, &gt->reset.worker))
+ xe_pm_runtime_put(gt_to_xe(gt));
}
void xe_gt_suspend_prepare(struct xe_gt *gt)
diff --git a/drivers/gpu/drm/xe/xe_gt.h b/drivers/gpu/drm/xe/xe_gt.h
index 41880979f4de..9d710049da45 100644
--- a/drivers/gpu/drm/xe/xe_gt.h
+++ b/drivers/gpu/drm/xe/xe_gt.h
@@ -12,6 +12,7 @@
#include "xe_device.h"
#include "xe_device_types.h"
+#include "xe_gt_sriov_vf.h"
#include "xe_hw_engine.h"
#define for_each_hw_engine(hwe__, gt__, id__) \
@@ -21,6 +22,12 @@
#define CCS_MASK(gt) (((gt)->info.engine_mask & XE_HW_ENGINE_CCS_MASK) >> XE_HW_ENGINE_CCS0)
+#define GT_VER(gt) ({ \
+ typeof(gt) gt_ = (gt); \
+ struct xe_device *xe = gt_to_xe(gt_); \
+ xe_gt_is_media_type(gt_) ? MEDIA_VER(xe) : GRAPHICS_VER(xe); \
+})
+
extern struct fault_attr gt_reset_failure;
static inline bool xe_fault_inject_gt_reset(void)
{
@@ -124,4 +131,16 @@ static inline bool xe_gt_is_usm_hwe(struct xe_gt *gt, struct xe_hw_engine *hwe)
hwe->instance == gt->usm.reserved_bcs_instance;
}
+/**
+ * xe_gt_recovery_pending() - GT recovery pending
+ * @gt: the &xe_gt
+ *
+ * Return: True if GT recovery in pending, False otherwise
+ */
+static inline bool xe_gt_recovery_pending(struct xe_gt *gt)
+{
+ return IS_SRIOV_VF(gt_to_xe(gt)) &&
+ xe_gt_sriov_vf_recovery_pending(gt);
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_clock.c b/drivers/gpu/drm/xe/xe_gt_clock.c
index 4f011d1573c6..bfc25c46f798 100644
--- a/drivers/gpu/drm/xe/xe_gt_clock.c
+++ b/drivers/gpu/drm/xe/xe_gt_clock.c
@@ -55,30 +55,11 @@ static void read_crystal_clock(struct xe_gt *gt, u32 rpm_config_reg, u32 *freq,
}
}
-static void check_ctc_mode(struct xe_gt *gt)
-{
- /*
- * CTC_MODE[0] = 1 is definitely not supported for Xe2 and later
- * platforms. In theory it could be a valid setting for pre-Xe2
- * platforms, but there's no documentation on how to properly handle
- * this case. Reading TIMESTAMP_OVERRIDE, as the driver attempted in
- * the past has been confirmed as incorrect by the hardware architects.
- *
- * For now just warn if we ever encounter hardware in the wild that
- * has this setting and move on as if it hadn't been set.
- */
- if (xe_mmio_read32(&gt->mmio, CTC_MODE) & CTC_SOURCE_DIVIDE_LOGIC)
- xe_gt_warn(gt, "CTC_MODE[0] is set; this is unexpected and undocumented\n");
-}
-
int xe_gt_clock_init(struct xe_gt *gt)
{
u32 freq;
u32 c0;
- if (!IS_SRIOV_VF(gt_to_xe(gt)))
- check_ctc_mode(gt);
-
c0 = xe_mmio_read32(&gt->mmio, RPM_CONFIG0);
read_crystal_clock(gt, c0, &freq, &gt->info.timestamp_base);
@@ -93,11 +74,6 @@ int xe_gt_clock_init(struct xe_gt *gt)
return 0;
}
-static u64 div_u64_roundup(u64 n, u32 d)
-{
- return div_u64(n + d - 1, d);
-}
-
/**
* xe_gt_clock_interval_to_ms - Convert sampled GT clock ticks to msec
*
@@ -108,5 +84,5 @@ static u64 div_u64_roundup(u64 n, u32 d)
*/
u64 xe_gt_clock_interval_to_ms(struct xe_gt *gt, u64 count)
{
- return div_u64_roundup(count * MSEC_PER_SEC, gt->info.reference_clock);
+ return mul_u64_u32_div(count, MSEC_PER_SEC, gt->info.reference_clock);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c
index f253e2df4907..e4fd632f43cf 100644
--- a/drivers/gpu/drm/xe/xe_gt_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c
@@ -12,7 +12,6 @@
#include "xe_device.h"
#include "xe_force_wake.h"
-#include "xe_ggtt.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_idle.h"
@@ -36,6 +35,11 @@
#include "xe_uc_debugfs.h"
#include "xe_wa.h"
+static struct xe_gt *node_to_gt(struct drm_info_node *node)
+{
+ return node->dent->d_parent->d_inode->i_private;
+}
+
/**
* xe_gt_debugfs_simple_show - A show callback for struct drm_info_list
* @m: the &seq_file
@@ -78,8 +82,7 @@ int xe_gt_debugfs_simple_show(struct seq_file *m, void *data)
{
struct drm_printer p = drm_seq_file_printer(m);
struct drm_info_node *node = m->private;
- struct dentry *parent = node->dent->d_parent;
- struct xe_gt *gt = parent->d_inode->i_private;
+ struct xe_gt *gt = node_to_gt(node);
int (*print)(struct xe_gt *, struct drm_printer *) = node->info_ent->data;
if (WARN_ON(!print))
@@ -88,15 +91,36 @@ int xe_gt_debugfs_simple_show(struct seq_file *m, void *data)
return print(gt, &p);
}
-static int hw_engines(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_gt_debugfs_show_with_rpm - A show callback for struct drm_info_list
+ * @m: the &seq_file
+ * @data: data used by the drm debugfs helpers
+ *
+ * Similar to xe_gt_debugfs_simple_show() but implicitly takes a RPM ref.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_debugfs_show_with_rpm(struct seq_file *m, void *data)
{
+ struct drm_info_node *node = m->private;
+ struct xe_gt *gt = node_to_gt(node);
struct xe_device *xe = gt_to_xe(gt);
+ int ret;
+
+ xe_pm_runtime_get(xe);
+ ret = xe_gt_debugfs_simple_show(m, data);
+ xe_pm_runtime_put(xe);
+
+ return ret;
+}
+
+static int hw_engines(struct xe_gt *gt, struct drm_printer *p)
+{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
unsigned int fw_ref;
int ret = 0;
- xe_pm_runtime_get(xe);
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
ret = -ETIMEDOUT;
@@ -108,58 +132,21 @@ static int hw_engines(struct xe_gt *gt, struct drm_printer *p)
fw_put:
xe_force_wake_put(gt_to_fw(gt), fw_ref);
- xe_pm_runtime_put(xe);
-
- return ret;
-}
-
-static int powergate_info(struct xe_gt *gt, struct drm_printer *p)
-{
- int ret;
-
- xe_pm_runtime_get(gt_to_xe(gt));
- ret = xe_gt_idle_pg_print(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
return ret;
}
-static int topology(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_gt_topology_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
static int steering(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_gt_mcr_steering_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
-static int ggtt(struct xe_gt *gt, struct drm_printer *p)
-{
- int ret;
-
- xe_pm_runtime_get(gt_to_xe(gt));
- ret = xe_ggtt_dump(gt_to_tile(gt)->mem.ggtt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return ret;
-}
-
static int register_save_restore(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
- xe_pm_runtime_get(gt_to_xe(gt));
-
xe_reg_sr_dump(&gt->reg_sr, p);
drm_printf(p, "\n");
@@ -177,98 +164,42 @@ static int register_save_restore(struct xe_gt *gt, struct drm_printer *p)
for_each_hw_engine(hwe, gt, id)
xe_reg_whitelist_dump(&hwe->reg_whitelist, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int workarounds(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_wa_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int tunings(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_tuning_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int pat(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_pat_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return 0;
-}
-
-static int mocs(struct xe_gt *gt, struct drm_printer *p)
-{
- xe_pm_runtime_get(gt_to_xe(gt));
- xe_mocs_dump(gt, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int rcs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_RENDER);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int ccs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_COMPUTE);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int bcs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_COPY);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int vcs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_VIDEO_DECODE);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int vecs_default_lrc(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_lrc_dump_default(p, gt, XE_ENGINE_CLASS_VIDEO_ENHANCE);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
static int hwconfig(struct xe_gt *gt, struct drm_printer *p)
{
- xe_pm_runtime_get(gt_to_xe(gt));
xe_guc_hwconfig_dump(&gt->uc.guc, p);
- xe_pm_runtime_put(gt_to_xe(gt));
-
return 0;
}
@@ -278,26 +209,26 @@ static int hwconfig(struct xe_gt *gt, struct drm_printer *p)
* - without access to the PF specific data
*/
static const struct drm_info_list vf_safe_debugfs_list[] = {
- {"topology", .show = xe_gt_debugfs_simple_show, .data = topology},
- {"ggtt", .show = xe_gt_debugfs_simple_show, .data = ggtt},
- {"register-save-restore", .show = xe_gt_debugfs_simple_show, .data = register_save_restore},
- {"workarounds", .show = xe_gt_debugfs_simple_show, .data = workarounds},
- {"tunings", .show = xe_gt_debugfs_simple_show, .data = tunings},
- {"default_lrc_rcs", .show = xe_gt_debugfs_simple_show, .data = rcs_default_lrc},
- {"default_lrc_ccs", .show = xe_gt_debugfs_simple_show, .data = ccs_default_lrc},
- {"default_lrc_bcs", .show = xe_gt_debugfs_simple_show, .data = bcs_default_lrc},
- {"default_lrc_vcs", .show = xe_gt_debugfs_simple_show, .data = vcs_default_lrc},
- {"default_lrc_vecs", .show = xe_gt_debugfs_simple_show, .data = vecs_default_lrc},
- {"hwconfig", .show = xe_gt_debugfs_simple_show, .data = hwconfig},
+ { "topology", .show = xe_gt_debugfs_show_with_rpm, .data = xe_gt_topology_dump },
+ { "register-save-restore",
+ .show = xe_gt_debugfs_show_with_rpm, .data = register_save_restore },
+ { "workarounds", .show = xe_gt_debugfs_show_with_rpm, .data = xe_wa_gt_dump },
+ { "tunings", .show = xe_gt_debugfs_show_with_rpm, .data = xe_tuning_dump },
+ { "default_lrc_rcs", .show = xe_gt_debugfs_show_with_rpm, .data = rcs_default_lrc },
+ { "default_lrc_ccs", .show = xe_gt_debugfs_show_with_rpm, .data = ccs_default_lrc },
+ { "default_lrc_bcs", .show = xe_gt_debugfs_show_with_rpm, .data = bcs_default_lrc },
+ { "default_lrc_vcs", .show = xe_gt_debugfs_show_with_rpm, .data = vcs_default_lrc },
+ { "default_lrc_vecs", .show = xe_gt_debugfs_show_with_rpm, .data = vecs_default_lrc },
+ { "hwconfig", .show = xe_gt_debugfs_show_with_rpm, .data = hwconfig },
};
/* everything else should be added here */
static const struct drm_info_list pf_only_debugfs_list[] = {
- {"hw_engines", .show = xe_gt_debugfs_simple_show, .data = hw_engines},
- {"mocs", .show = xe_gt_debugfs_simple_show, .data = mocs},
- {"pat", .show = xe_gt_debugfs_simple_show, .data = pat},
- {"powergate_info", .show = xe_gt_debugfs_simple_show, .data = powergate_info},
- {"steering", .show = xe_gt_debugfs_simple_show, .data = steering},
+ { "hw_engines", .show = xe_gt_debugfs_show_with_rpm, .data = hw_engines },
+ { "mocs", .show = xe_gt_debugfs_show_with_rpm, .data = xe_mocs_dump },
+ { "pat", .show = xe_gt_debugfs_show_with_rpm, .data = xe_pat_dump },
+ { "powergate_info", .show = xe_gt_debugfs_show_with_rpm, .data = xe_gt_idle_pg_print },
+ { "steering", .show = xe_gt_debugfs_show_with_rpm, .data = steering },
};
static ssize_t write_to_gt_call(const char __user *userbuf, size_t count, loff_t *ppos,
diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.h b/drivers/gpu/drm/xe/xe_gt_debugfs.h
index 05a6cc93c78c..32ee3264051b 100644
--- a/drivers/gpu/drm/xe/xe_gt_debugfs.h
+++ b/drivers/gpu/drm/xe/xe_gt_debugfs.h
@@ -11,5 +11,6 @@ struct xe_gt;
void xe_gt_debugfs_register(struct xe_gt *gt);
int xe_gt_debugfs_simple_show(struct seq_file *m, void *data);
+int xe_gt_debugfs_show_with_rpm(struct seq_file *m, void *data);
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_freq.c b/drivers/gpu/drm/xe/xe_gt_freq.c
index 4ff1b6b58d6b..849ea6c86e8e 100644
--- a/drivers/gpu/drm/xe/xe_gt_freq.c
+++ b/drivers/gpu/drm/xe/xe_gt_freq.c
@@ -29,24 +29,26 @@
* PCODE is the ultimate decision maker of the actual running frequency, based
* on thermal and other running conditions.
*
- * Xe's Freq provides a sysfs API for frequency management:
+ * Xe's Freq provides a sysfs API for frequency management under
+ * ``<device>/tile#/gt#/freq0/`` directory.
*
- * device/tile#/gt#/freq0/<item>_freq *read-only* files:
+ * **Read-only** attributes:
*
- * - act_freq: The actual resolved frequency decided by PCODE.
- * - cur_freq: The current one requested by GuC PC to the PCODE.
- * - rpn_freq: The Render Performance (RP) N level, which is the minimal one.
- * - rpa_freq: The Render Performance (RP) A level, which is the achiveable one.
- * Calculated by PCODE at runtime based on multiple running conditions
- * - rpe_freq: The Render Performance (RP) E level, which is the efficient one.
- * Calculated by PCODE at runtime based on multiple running conditions
- * - rp0_freq: The Render Performance (RP) 0 level, which is the maximum one.
+ * - ``act_freq``: The actual resolved frequency decided by PCODE.
+ * - ``cur_freq``: The current one requested by GuC PC to the PCODE.
+ * - ``rpn_freq``: The Render Performance (RP) N level, which is the minimal one.
+ * - ``rpa_freq``: The Render Performance (RP) A level, which is the achievable one.
+ * Calculated by PCODE at runtime based on multiple running conditions
+ * - ``rpe_freq``: The Render Performance (RP) E level, which is the efficient one.
+ * Calculated by PCODE at runtime based on multiple running conditions
+ * - ``rp0_freq``: The Render Performance (RP) 0 level, which is the maximum one.
*
- * device/tile#/gt#/freq0/<item>_freq *read-write* files:
+ * **Read-write** attributes:
*
- * - min_freq: Min frequency request.
- * - max_freq: Max frequency request.
- * If max <= min, then freq_min becomes a fixed frequency request.
+ * - ``min_freq``: Min frequency request.
+ * - ``max_freq``: Max frequency request.
+ * If max <= min, then freq_min becomes a fixed frequency
+ * request.
*/
static struct xe_guc_pc *
@@ -99,13 +101,8 @@ static ssize_t rp0_freq_show(struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct xe_guc_pc *pc = dev_to_pc(dev);
- u32 freq;
- xe_pm_runtime_get(dev_to_xe(dev));
- freq = xe_guc_pc_get_rp0_freq(pc);
- xe_pm_runtime_put(dev_to_xe(dev));
-
- return sysfs_emit(buf, "%d\n", freq);
+ return sysfs_emit(buf, "%d\n", xe_guc_pc_get_rp0_freq(pc));
}
static struct kobj_attribute attr_rp0_freq = __ATTR_RO(rp0_freq);
diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c
index 8fb1cae91724..164010860664 100644
--- a/drivers/gpu/drm/xe/xe_gt_mcr.c
+++ b/drivers/gpu/drm/xe/xe_gt_mcr.c
@@ -169,6 +169,15 @@ static const struct xe_mmio_range xelpg_dss_steering_table[] = {
{},
};
+static const struct xe_mmio_range xe3p_xpc_xecore_steering_table[] = {
+ { 0x008140, 0x00817F }, /* SLICE, XeCore, SLICE */
+ { 0x009480, 0x00955F }, /* SLICE, XeCore */
+ { 0x00D800, 0x00D87F }, /* SLICE */
+ { 0x00DC00, 0x00E9FF }, /* SLICE, rsvd, XeCore, rsvd, XeCore, rsvd, XeCore */
+ { 0x013000, 0x0135FF }, /* XeCore, SLICE */
+ {},
+};
+
static const struct xe_mmio_range xelpmp_oaddrm_steering_table[] = {
{ 0x393200, 0x39323F },
{ 0x393400, 0x3934FF },
@@ -236,21 +245,60 @@ static const struct xe_mmio_range xe2lpm_instance0_steering_table[] = {
};
static const struct xe_mmio_range xe3lpm_instance0_steering_table[] = {
- { 0x384000, 0x3847DF }, /* GAM, rsvd, GAM */
+ { 0x384000, 0x3841FF }, /* GAM */
+ { 0x384400, 0x3847DF }, /* GAM */
{ 0x384900, 0x384AFF }, /* GAM */
{ 0x389560, 0x3895FF }, /* MEDIAINF */
{ 0x38B600, 0x38B8FF }, /* L3BANK */
{ 0x38C800, 0x38D07F }, /* GAM, MEDIAINF */
- { 0x38D0D0, 0x38F0FF }, /* MEDIAINF, GAM */
+ { 0x38D0D0, 0x38F0FF }, /* MEDIAINF, rsvd, GAM */
{ 0x393C00, 0x393C7F }, /* MEDIAINF */
{},
};
+/*
+ * Different "GAM" ranges have different rules; GAMWKRS, STLB, and GAMREQSTRM
+ * range subtypes need to be steered to (1,0), while all other GAM subtypes
+ * are steered to (0,0) and are included in the "INSTANCE0" table farther
+ * down.
+ */
+static const struct xe_mmio_range xe3p_xpc_gam_grp1_steering_table[] = {
+ { 0x004000, 0x004AFF }, /* GAMREQSTRM, rsvd, STLB, GAMWKRS, GAMREQSTRM */
+ { 0x00F100, 0x00FFFF }, /* GAMWKRS */
+ {},
+};
+
+static const struct xe_mmio_range xe3p_xpc_node_steering_table[] = {
+ { 0x00B000, 0x00B0FF },
+ { 0x00D880, 0x00D8FF },
+ {},
+};
+
+static const struct xe_mmio_range xe3p_xpc_instance0_steering_table[] = {
+ { 0x00B500, 0x00B6FF }, /* PSMI */
+ { 0x00C800, 0x00CFFF }, /* GAMCTRL */
+ { 0x00F000, 0x00F0FF }, /* GAMCTRL */
+ {},
+};
+
static void init_steering_l3bank(struct xe_gt *gt)
{
+ struct xe_device *xe = gt_to_xe(gt);
struct xe_mmio *mmio = &gt->mmio;
- if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 1270) {
+ if (GRAPHICS_VER(xe) >= 35) {
+ unsigned int first_bank = xe_l3_bank_mask_ffs(gt->fuse_topo.l3_bank_mask);
+ const int banks_per_node = 4;
+ unsigned int node = first_bank / banks_per_node;
+
+ /* L3BANK ranges place node in grpID, bank in instanceid */
+ gt->steering[L3BANK].group_target = node;
+ gt->steering[L3BANK].instance_target = first_bank % banks_per_node;
+
+ /* NODE ranges split the node across grpid and instanceid */
+ gt->steering[NODE].group_target = node >> 1;
+ gt->steering[NODE].instance_target = node & 1;
+ } else if (GRAPHICS_VERx100(xe) >= 1270) {
u32 mslice_mask = REG_FIELD_GET(MEML3_EN_MASK,
xe_mmio_read32(mmio, MIRROR_FUSE3));
u32 bank_mask = REG_FIELD_GET(GT_L3_EXC_MASK,
@@ -263,7 +311,7 @@ static void init_steering_l3bank(struct xe_gt *gt)
gt->steering[L3BANK].group_target = __ffs(mslice_mask);
gt->steering[L3BANK].instance_target =
bank_mask & BIT(0) ? 0 : 2;
- } else if (gt_to_xe(gt)->info.platform == XE_DG2) {
+ } else if (xe->info.platform == XE_DG2) {
u32 mslice_mask = REG_FIELD_GET(MEML3_EN_MASK,
xe_mmio_read32(mmio, MIRROR_FUSE3));
u32 bank = __ffs(mslice_mask) * 8;
@@ -418,16 +466,24 @@ static void init_steering_sqidi_psmi(struct xe_gt *gt)
gt->steering[SQIDI_PSMI].instance_target = select & 0x1;
}
+static void init_steering_gam1(struct xe_gt *gt)
+{
+ gt->steering[GAM1].group_target = 1;
+ gt->steering[GAM1].instance_target = 0;
+}
+
static const struct {
const char *name;
void (*init)(struct xe_gt *gt);
} xe_steering_types[] = {
[L3BANK] = { "L3BANK", init_steering_l3bank },
+ [NODE] = { "NODE", NULL }, /* initialized by l3bank init */
[MSLICE] = { "MSLICE", init_steering_mslice },
[LNCF] = { "LNCF", NULL }, /* initialized by mslice init */
- [DSS] = { "DSS", init_steering_dss },
+ [DSS] = { "DSS / XeCore", init_steering_dss },
[OADDRM] = { "OADDRM / GPMXMT", init_steering_oaddrm },
[SQIDI_PSMI] = { "SQIDI_PSMI", init_steering_sqidi_psmi },
+ [GAM1] = { "GAMWKRS / STLB / GAMREQSTRM", init_steering_gam1 },
[INSTANCE0] = { "INSTANCE 0", NULL },
[IMPLICIT_STEERING] = { "IMPLICIT", NULL },
};
@@ -466,7 +522,19 @@ void xe_gt_mcr_init_early(struct xe_gt *gt)
gt->steering[OADDRM].ranges = xelpmp_oaddrm_steering_table;
}
} else {
- if (GRAPHICS_VER(xe) >= 20) {
+ if (GRAPHICS_VERx100(xe) == 3511) {
+ /*
+ * TODO: there are some ranges in bspec with missing
+ * termination: [0x00B000, 0x00B0FF] and
+ * [0x00D880, 0x00D8FF] (NODE); [0x00B100, 0x00B3FF]
+ * (L3BANK). Update them here once bspec is updated.
+ */
+ gt->steering[DSS].ranges = xe3p_xpc_xecore_steering_table;
+ gt->steering[GAM1].ranges = xe3p_xpc_gam_grp1_steering_table;
+ gt->steering[INSTANCE0].ranges = xe3p_xpc_instance0_steering_table;
+ gt->steering[L3BANK].ranges = xelpg_l3bank_steering_table;
+ gt->steering[NODE].ranges = xe3p_xpc_node_steering_table;
+ } else if (GRAPHICS_VER(xe) >= 20) {
gt->steering[DSS].ranges = xe2lpg_dss_steering_table;
gt->steering[SQIDI_PSMI].ranges = xe2lpg_sqidi_psmi_steering_table;
gt->steering[INSTANCE0].ranges = xe2lpg_instance0_steering_table;
diff --git a/drivers/gpu/drm/xe/xe_gt_pagefault.c b/drivers/gpu/drm/xe/xe_gt_pagefault.c
deleted file mode 100644
index a054d6010ae0..000000000000
--- a/drivers/gpu/drm/xe/xe_gt_pagefault.c
+++ /dev/null
@@ -1,679 +0,0 @@
-// SPDX-License-Identifier: MIT
-/*
- * Copyright © 2022 Intel Corporation
- */
-
-#include "xe_gt_pagefault.h"
-
-#include <linux/bitfield.h>
-#include <linux/circ_buf.h>
-
-#include <drm/drm_exec.h>
-#include <drm/drm_managed.h>
-
-#include "abi/guc_actions_abi.h"
-#include "xe_bo.h"
-#include "xe_gt.h"
-#include "xe_gt_printk.h"
-#include "xe_gt_stats.h"
-#include "xe_guc.h"
-#include "xe_guc_ct.h"
-#include "xe_migrate.h"
-#include "xe_svm.h"
-#include "xe_trace_bo.h"
-#include "xe_vm.h"
-#include "xe_vram_types.h"
-
-struct pagefault {
- u64 page_addr;
- u32 asid;
- u16 pdata;
- u8 vfid;
- u8 access_type;
- u8 fault_type;
- u8 fault_level;
- u8 engine_class;
- u8 engine_instance;
- u8 fault_unsuccessful;
- bool trva_fault;
-};
-
-enum access_type {
- ACCESS_TYPE_READ = 0,
- ACCESS_TYPE_WRITE = 1,
- ACCESS_TYPE_ATOMIC = 2,
- ACCESS_TYPE_RESERVED = 3,
-};
-
-enum fault_type {
- NOT_PRESENT = 0,
- WRITE_ACCESS_VIOLATION = 1,
- ATOMIC_ACCESS_VIOLATION = 2,
-};
-
-struct acc {
- u64 va_range_base;
- u32 asid;
- u32 sub_granularity;
- u8 granularity;
- u8 vfid;
- u8 access_type;
- u8 engine_class;
- u8 engine_instance;
-};
-
-static bool access_is_atomic(enum access_type access_type)
-{
- return access_type == ACCESS_TYPE_ATOMIC;
-}
-
-static bool vma_is_valid(struct xe_tile *tile, struct xe_vma *vma)
-{
- return xe_vm_has_valid_gpu_mapping(tile, vma->tile_present,
- vma->tile_invalidated);
-}
-
-static int xe_pf_begin(struct drm_exec *exec, struct xe_vma *vma,
- bool need_vram_move, struct xe_vram_region *vram)
-{
- struct xe_bo *bo = xe_vma_bo(vma);
- struct xe_vm *vm = xe_vma_vm(vma);
- int err;
-
- err = xe_vm_lock_vma(exec, vma);
- if (err)
- return err;
-
- if (!bo)
- return 0;
-
- return need_vram_move ? xe_bo_migrate(bo, vram->placement, NULL, exec) :
- xe_bo_validate(bo, vm, true, exec);
-}
-
-static int handle_vma_pagefault(struct xe_gt *gt, struct xe_vma *vma,
- bool atomic)
-{
- struct xe_vm *vm = xe_vma_vm(vma);
- struct xe_tile *tile = gt_to_tile(gt);
- struct xe_validation_ctx ctx;
- struct drm_exec exec;
- struct dma_fence *fence;
- int err, needs_vram;
-
- lockdep_assert_held_write(&vm->lock);
-
- needs_vram = xe_vma_need_vram_for_atomic(vm->xe, vma, atomic);
- if (needs_vram < 0 || (needs_vram && xe_vma_is_userptr(vma)))
- return needs_vram < 0 ? needs_vram : -EACCES;
-
- xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_COUNT, 1);
- xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_KB, xe_vma_size(vma) / 1024);
-
- trace_xe_vma_pagefault(vma);
-
- /* Check if VMA is valid, opportunistic check only */
- if (vma_is_valid(tile, vma) && !atomic)
- return 0;
-
-retry_userptr:
- if (xe_vma_is_userptr(vma) &&
- xe_vma_userptr_check_repin(to_userptr_vma(vma))) {
- struct xe_userptr_vma *uvma = to_userptr_vma(vma);
-
- err = xe_vma_userptr_pin_pages(uvma);
- if (err)
- return err;
- }
-
- /* Lock VM and BOs dma-resv */
- xe_validation_ctx_init(&ctx, &vm->xe->val, &exec, (struct xe_val_flags) {});
- drm_exec_until_all_locked(&exec) {
- err = xe_pf_begin(&exec, vma, needs_vram == 1, tile->mem.vram);
- drm_exec_retry_on_contention(&exec);
- xe_validation_retry_on_oom(&ctx, &err);
- if (err)
- goto unlock_dma_resv;
-
- /* Bind VMA only to the GT that has faulted */
- trace_xe_vma_pf_bind(vma);
- xe_vm_set_validation_exec(vm, &exec);
- fence = xe_vma_rebind(vm, vma, BIT(tile->id));
- xe_vm_set_validation_exec(vm, NULL);
- if (IS_ERR(fence)) {
- err = PTR_ERR(fence);
- xe_validation_retry_on_oom(&ctx, &err);
- goto unlock_dma_resv;
- }
- }
-
- dma_fence_wait(fence, false);
- dma_fence_put(fence);
-
-unlock_dma_resv:
- xe_validation_ctx_fini(&ctx);
- if (err == -EAGAIN)
- goto retry_userptr;
-
- return err;
-}
-
-static struct xe_vm *asid_to_vm(struct xe_device *xe, u32 asid)
-{
- struct xe_vm *vm;
-
- down_read(&xe->usm.lock);
- vm = xa_load(&xe->usm.asid_to_vm, asid);
- if (vm && xe_vm_in_fault_mode(vm))
- xe_vm_get(vm);
- else
- vm = ERR_PTR(-EINVAL);
- up_read(&xe->usm.lock);
-
- return vm;
-}
-
-static int handle_pagefault(struct xe_gt *gt, struct pagefault *pf)
-{
- struct xe_device *xe = gt_to_xe(gt);
- struct xe_vm *vm;
- struct xe_vma *vma = NULL;
- int err;
- bool atomic;
-
- /* SW isn't expected to handle TRTT faults */
- if (pf->trva_fault)
- return -EFAULT;
-
- vm = asid_to_vm(xe, pf->asid);
- if (IS_ERR(vm))
- return PTR_ERR(vm);
-
- /*
- * TODO: Change to read lock? Using write lock for simplicity.
- */
- down_write(&vm->lock);
-
- if (xe_vm_is_closed(vm)) {
- err = -ENOENT;
- goto unlock_vm;
- }
-
- vma = xe_vm_find_vma_by_addr(vm, pf->page_addr);
- if (!vma) {
- err = -EINVAL;
- goto unlock_vm;
- }
-
- atomic = access_is_atomic(pf->access_type);
-
- if (xe_vma_is_cpu_addr_mirror(vma))
- err = xe_svm_handle_pagefault(vm, vma, gt,
- pf->page_addr, atomic);
- else
- err = handle_vma_pagefault(gt, vma, atomic);
-
-unlock_vm:
- if (!err)
- vm->usm.last_fault_vma = vma;
- up_write(&vm->lock);
- xe_vm_put(vm);
-
- return err;
-}
-
-static int send_pagefault_reply(struct xe_guc *guc,
- struct xe_guc_pagefault_reply *reply)
-{
- u32 action[] = {
- XE_GUC_ACTION_PAGE_FAULT_RES_DESC,
- reply->dw0,
- reply->dw1,
- };
-
- return xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
-}
-
-static void print_pagefault(struct xe_gt *gt, struct pagefault *pf)
-{
- xe_gt_dbg(gt, "\n\tASID: %d\n"
- "\tVFID: %d\n"
- "\tPDATA: 0x%04x\n"
- "\tFaulted Address: 0x%08x%08x\n"
- "\tFaultType: %d\n"
- "\tAccessType: %d\n"
- "\tFaultLevel: %d\n"
- "\tEngineClass: %d %s\n"
- "\tEngineInstance: %d\n",
- pf->asid, pf->vfid, pf->pdata, upper_32_bits(pf->page_addr),
- lower_32_bits(pf->page_addr),
- pf->fault_type, pf->access_type, pf->fault_level,
- pf->engine_class, xe_hw_engine_class_to_str(pf->engine_class),
- pf->engine_instance);
-}
-
-#define PF_MSG_LEN_DW 4
-
-static bool get_pagefault(struct pf_queue *pf_queue, struct pagefault *pf)
-{
- const struct xe_guc_pagefault_desc *desc;
- bool ret = false;
-
- spin_lock_irq(&pf_queue->lock);
- if (pf_queue->tail != pf_queue->head) {
- desc = (const struct xe_guc_pagefault_desc *)
- (pf_queue->data + pf_queue->tail);
-
- pf->fault_level = FIELD_GET(PFD_FAULT_LEVEL, desc->dw0);
- pf->trva_fault = FIELD_GET(XE2_PFD_TRVA_FAULT, desc->dw0);
- pf->engine_class = FIELD_GET(PFD_ENG_CLASS, desc->dw0);
- pf->engine_instance = FIELD_GET(PFD_ENG_INSTANCE, desc->dw0);
- pf->pdata = FIELD_GET(PFD_PDATA_HI, desc->dw1) <<
- PFD_PDATA_HI_SHIFT;
- pf->pdata |= FIELD_GET(PFD_PDATA_LO, desc->dw0);
- pf->asid = FIELD_GET(PFD_ASID, desc->dw1);
- pf->vfid = FIELD_GET(PFD_VFID, desc->dw2);
- pf->access_type = FIELD_GET(PFD_ACCESS_TYPE, desc->dw2);
- pf->fault_type = FIELD_GET(PFD_FAULT_TYPE, desc->dw2);
- pf->page_addr = (u64)(FIELD_GET(PFD_VIRTUAL_ADDR_HI, desc->dw3)) <<
- PFD_VIRTUAL_ADDR_HI_SHIFT;
- pf->page_addr |= FIELD_GET(PFD_VIRTUAL_ADDR_LO, desc->dw2) <<
- PFD_VIRTUAL_ADDR_LO_SHIFT;
-
- pf_queue->tail = (pf_queue->tail + PF_MSG_LEN_DW) %
- pf_queue->num_dw;
- ret = true;
- }
- spin_unlock_irq(&pf_queue->lock);
-
- return ret;
-}
-
-static bool pf_queue_full(struct pf_queue *pf_queue)
-{
- lockdep_assert_held(&pf_queue->lock);
-
- return CIRC_SPACE(pf_queue->head, pf_queue->tail,
- pf_queue->num_dw) <=
- PF_MSG_LEN_DW;
-}
-
-int xe_guc_pagefault_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_gt *gt = guc_to_gt(guc);
- struct pf_queue *pf_queue;
- unsigned long flags;
- u32 asid;
- bool full;
-
- if (unlikely(len != PF_MSG_LEN_DW))
- return -EPROTO;
-
- asid = FIELD_GET(PFD_ASID, msg[1]);
- pf_queue = gt->usm.pf_queue + (asid % NUM_PF_QUEUE);
-
- /*
- * The below logic doesn't work unless PF_QUEUE_NUM_DW % PF_MSG_LEN_DW == 0
- */
- xe_gt_assert(gt, !(pf_queue->num_dw % PF_MSG_LEN_DW));
-
- spin_lock_irqsave(&pf_queue->lock, flags);
- full = pf_queue_full(pf_queue);
- if (!full) {
- memcpy(pf_queue->data + pf_queue->head, msg, len * sizeof(u32));
- pf_queue->head = (pf_queue->head + len) %
- pf_queue->num_dw;
- queue_work(gt->usm.pf_wq, &pf_queue->worker);
- } else {
- xe_gt_warn(gt, "PageFault Queue full, shouldn't be possible\n");
- }
- spin_unlock_irqrestore(&pf_queue->lock, flags);
-
- return full ? -ENOSPC : 0;
-}
-
-#define USM_QUEUE_MAX_RUNTIME_MS 20
-
-static void pf_queue_work_func(struct work_struct *w)
-{
- struct pf_queue *pf_queue = container_of(w, struct pf_queue, worker);
- struct xe_gt *gt = pf_queue->gt;
- struct xe_guc_pagefault_reply reply = {};
- struct pagefault pf = {};
- unsigned long threshold;
- int ret;
-
- threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
-
- while (get_pagefault(pf_queue, &pf)) {
- ret = handle_pagefault(gt, &pf);
- if (unlikely(ret)) {
- print_pagefault(gt, &pf);
- pf.fault_unsuccessful = 1;
- xe_gt_dbg(gt, "Fault response: Unsuccessful %pe\n", ERR_PTR(ret));
- }
-
- reply.dw0 = FIELD_PREP(PFR_VALID, 1) |
- FIELD_PREP(PFR_SUCCESS, pf.fault_unsuccessful) |
- FIELD_PREP(PFR_REPLY, PFR_ACCESS) |
- FIELD_PREP(PFR_DESC_TYPE, FAULT_RESPONSE_DESC) |
- FIELD_PREP(PFR_ASID, pf.asid);
-
- reply.dw1 = FIELD_PREP(PFR_VFID, pf.vfid) |
- FIELD_PREP(PFR_ENG_INSTANCE, pf.engine_instance) |
- FIELD_PREP(PFR_ENG_CLASS, pf.engine_class) |
- FIELD_PREP(PFR_PDATA, pf.pdata);
-
- send_pagefault_reply(&gt->uc.guc, &reply);
-
- if (time_after(jiffies, threshold) &&
- pf_queue->tail != pf_queue->head) {
- queue_work(gt->usm.pf_wq, w);
- break;
- }
- }
-}
-
-static void acc_queue_work_func(struct work_struct *w);
-
-static void pagefault_fini(void *arg)
-{
- struct xe_gt *gt = arg;
- struct xe_device *xe = gt_to_xe(gt);
-
- if (!xe->info.has_usm)
- return;
-
- destroy_workqueue(gt->usm.acc_wq);
- destroy_workqueue(gt->usm.pf_wq);
-}
-
-static int xe_alloc_pf_queue(struct xe_gt *gt, struct pf_queue *pf_queue)
-{
- struct xe_device *xe = gt_to_xe(gt);
- xe_dss_mask_t all_dss;
- int num_dss, num_eus;
-
- bitmap_or(all_dss, gt->fuse_topo.g_dss_mask, gt->fuse_topo.c_dss_mask,
- XE_MAX_DSS_FUSE_BITS);
-
- num_dss = bitmap_weight(all_dss, XE_MAX_DSS_FUSE_BITS);
- num_eus = bitmap_weight(gt->fuse_topo.eu_mask_per_dss,
- XE_MAX_EU_FUSE_BITS) * num_dss;
-
- /*
- * user can issue separate page faults per EU and per CS
- *
- * XXX: Multiplier required as compute UMD are getting PF queue errors
- * without it. Follow on why this multiplier is required.
- */
-#define PF_MULTIPLIER 8
- pf_queue->num_dw =
- (num_eus + XE_NUM_HW_ENGINES) * PF_MSG_LEN_DW * PF_MULTIPLIER;
- pf_queue->num_dw = roundup_pow_of_two(pf_queue->num_dw);
-#undef PF_MULTIPLIER
-
- pf_queue->gt = gt;
- pf_queue->data = devm_kcalloc(xe->drm.dev, pf_queue->num_dw,
- sizeof(u32), GFP_KERNEL);
- if (!pf_queue->data)
- return -ENOMEM;
-
- spin_lock_init(&pf_queue->lock);
- INIT_WORK(&pf_queue->worker, pf_queue_work_func);
-
- return 0;
-}
-
-int xe_gt_pagefault_init(struct xe_gt *gt)
-{
- struct xe_device *xe = gt_to_xe(gt);
- int i, ret = 0;
-
- if (!xe->info.has_usm)
- return 0;
-
- for (i = 0; i < NUM_PF_QUEUE; ++i) {
- ret = xe_alloc_pf_queue(gt, &gt->usm.pf_queue[i]);
- if (ret)
- return ret;
- }
- for (i = 0; i < NUM_ACC_QUEUE; ++i) {
- gt->usm.acc_queue[i].gt = gt;
- spin_lock_init(&gt->usm.acc_queue[i].lock);
- INIT_WORK(&gt->usm.acc_queue[i].worker, acc_queue_work_func);
- }
-
- gt->usm.pf_wq = alloc_workqueue("xe_gt_page_fault_work_queue",
- WQ_UNBOUND | WQ_HIGHPRI, NUM_PF_QUEUE);
- if (!gt->usm.pf_wq)
- return -ENOMEM;
-
- gt->usm.acc_wq = alloc_workqueue("xe_gt_access_counter_work_queue",
- WQ_UNBOUND | WQ_HIGHPRI,
- NUM_ACC_QUEUE);
- if (!gt->usm.acc_wq) {
- destroy_workqueue(gt->usm.pf_wq);
- return -ENOMEM;
- }
-
- return devm_add_action_or_reset(xe->drm.dev, pagefault_fini, gt);
-}
-
-void xe_gt_pagefault_reset(struct xe_gt *gt)
-{
- struct xe_device *xe = gt_to_xe(gt);
- int i;
-
- if (!xe->info.has_usm)
- return;
-
- for (i = 0; i < NUM_PF_QUEUE; ++i) {
- spin_lock_irq(&gt->usm.pf_queue[i].lock);
- gt->usm.pf_queue[i].head = 0;
- gt->usm.pf_queue[i].tail = 0;
- spin_unlock_irq(&gt->usm.pf_queue[i].lock);
- }
-
- for (i = 0; i < NUM_ACC_QUEUE; ++i) {
- spin_lock(&gt->usm.acc_queue[i].lock);
- gt->usm.acc_queue[i].head = 0;
- gt->usm.acc_queue[i].tail = 0;
- spin_unlock(&gt->usm.acc_queue[i].lock);
- }
-}
-
-static int granularity_in_byte(int val)
-{
- switch (val) {
- case 0:
- return SZ_128K;
- case 1:
- return SZ_2M;
- case 2:
- return SZ_16M;
- case 3:
- return SZ_64M;
- default:
- return 0;
- }
-}
-
-static int sub_granularity_in_byte(int val)
-{
- return (granularity_in_byte(val) / 32);
-}
-
-static void print_acc(struct xe_gt *gt, struct acc *acc)
-{
- xe_gt_warn(gt, "Access counter request:\n"
- "\tType: %s\n"
- "\tASID: %d\n"
- "\tVFID: %d\n"
- "\tEngine: %d:%d\n"
- "\tGranularity: 0x%x KB Region/ %d KB sub-granularity\n"
- "\tSub_Granularity Vector: 0x%08x\n"
- "\tVA Range base: 0x%016llx\n",
- acc->access_type ? "AC_NTFY_VAL" : "AC_TRIG_VAL",
- acc->asid, acc->vfid, acc->engine_class, acc->engine_instance,
- granularity_in_byte(acc->granularity) / SZ_1K,
- sub_granularity_in_byte(acc->granularity) / SZ_1K,
- acc->sub_granularity, acc->va_range_base);
-}
-
-static struct xe_vma *get_acc_vma(struct xe_vm *vm, struct acc *acc)
-{
- u64 page_va = acc->va_range_base + (ffs(acc->sub_granularity) - 1) *
- sub_granularity_in_byte(acc->granularity);
-
- return xe_vm_find_overlapping_vma(vm, page_va, SZ_4K);
-}
-
-static int handle_acc(struct xe_gt *gt, struct acc *acc)
-{
- struct xe_device *xe = gt_to_xe(gt);
- struct xe_tile *tile = gt_to_tile(gt);
- struct xe_validation_ctx ctx;
- struct drm_exec exec;
- struct xe_vm *vm;
- struct xe_vma *vma;
- int ret = 0;
-
- /* We only support ACC_TRIGGER at the moment */
- if (acc->access_type != ACC_TRIGGER)
- return -EINVAL;
-
- vm = asid_to_vm(xe, acc->asid);
- if (IS_ERR(vm))
- return PTR_ERR(vm);
-
- down_read(&vm->lock);
-
- /* Lookup VMA */
- vma = get_acc_vma(vm, acc);
- if (!vma) {
- ret = -EINVAL;
- goto unlock_vm;
- }
-
- trace_xe_vma_acc(vma);
-
- /* Userptr or null can't be migrated, nothing to do */
- if (xe_vma_has_no_bo(vma))
- goto unlock_vm;
-
- /* Lock VM and BOs dma-resv */
- xe_validation_ctx_init(&ctx, &vm->xe->val, &exec, (struct xe_val_flags) {});
- drm_exec_until_all_locked(&exec) {
- ret = xe_pf_begin(&exec, vma, IS_DGFX(vm->xe), tile->mem.vram);
- drm_exec_retry_on_contention(&exec);
- xe_validation_retry_on_oom(&ctx, &ret);
- }
-
- xe_validation_ctx_fini(&ctx);
-unlock_vm:
- up_read(&vm->lock);
- xe_vm_put(vm);
-
- return ret;
-}
-
-#define make_u64(hi__, low__) ((u64)(hi__) << 32 | (u64)(low__))
-
-#define ACC_MSG_LEN_DW 4
-
-static bool get_acc(struct acc_queue *acc_queue, struct acc *acc)
-{
- const struct xe_guc_acc_desc *desc;
- bool ret = false;
-
- spin_lock(&acc_queue->lock);
- if (acc_queue->tail != acc_queue->head) {
- desc = (const struct xe_guc_acc_desc *)
- (acc_queue->data + acc_queue->tail);
-
- acc->granularity = FIELD_GET(ACC_GRANULARITY, desc->dw2);
- acc->sub_granularity = FIELD_GET(ACC_SUBG_HI, desc->dw1) << 31 |
- FIELD_GET(ACC_SUBG_LO, desc->dw0);
- acc->engine_class = FIELD_GET(ACC_ENG_CLASS, desc->dw1);
- acc->engine_instance = FIELD_GET(ACC_ENG_INSTANCE, desc->dw1);
- acc->asid = FIELD_GET(ACC_ASID, desc->dw1);
- acc->vfid = FIELD_GET(ACC_VFID, desc->dw2);
- acc->access_type = FIELD_GET(ACC_TYPE, desc->dw0);
- acc->va_range_base = make_u64(desc->dw3 & ACC_VIRTUAL_ADDR_RANGE_HI,
- desc->dw2 & ACC_VIRTUAL_ADDR_RANGE_LO);
-
- acc_queue->tail = (acc_queue->tail + ACC_MSG_LEN_DW) %
- ACC_QUEUE_NUM_DW;
- ret = true;
- }
- spin_unlock(&acc_queue->lock);
-
- return ret;
-}
-
-static void acc_queue_work_func(struct work_struct *w)
-{
- struct acc_queue *acc_queue = container_of(w, struct acc_queue, worker);
- struct xe_gt *gt = acc_queue->gt;
- struct acc acc = {};
- unsigned long threshold;
- int ret;
-
- threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
-
- while (get_acc(acc_queue, &acc)) {
- ret = handle_acc(gt, &acc);
- if (unlikely(ret)) {
- print_acc(gt, &acc);
- xe_gt_warn(gt, "ACC: Unsuccessful %pe\n", ERR_PTR(ret));
- }
-
- if (time_after(jiffies, threshold) &&
- acc_queue->tail != acc_queue->head) {
- queue_work(gt->usm.acc_wq, w);
- break;
- }
- }
-}
-
-static bool acc_queue_full(struct acc_queue *acc_queue)
-{
- lockdep_assert_held(&acc_queue->lock);
-
- return CIRC_SPACE(acc_queue->head, acc_queue->tail, ACC_QUEUE_NUM_DW) <=
- ACC_MSG_LEN_DW;
-}
-
-int xe_guc_access_counter_notify_handler(struct xe_guc *guc, u32 *msg, u32 len)
-{
- struct xe_gt *gt = guc_to_gt(guc);
- struct acc_queue *acc_queue;
- u32 asid;
- bool full;
-
- /*
- * The below logic doesn't work unless ACC_QUEUE_NUM_DW % ACC_MSG_LEN_DW == 0
- */
- BUILD_BUG_ON(ACC_QUEUE_NUM_DW % ACC_MSG_LEN_DW);
-
- if (unlikely(len != ACC_MSG_LEN_DW))
- return -EPROTO;
-
- asid = FIELD_GET(ACC_ASID, msg[1]);
- acc_queue = &gt->usm.acc_queue[asid % NUM_ACC_QUEUE];
-
- spin_lock(&acc_queue->lock);
- full = acc_queue_full(acc_queue);
- if (!full) {
- memcpy(acc_queue->data + acc_queue->head, msg,
- len * sizeof(u32));
- acc_queue->head = (acc_queue->head + len) % ACC_QUEUE_NUM_DW;
- queue_work(gt->usm.acc_wq, &acc_queue->worker);
- } else {
- xe_gt_warn(gt, "ACC Queue full, dropping ACC\n");
- }
- spin_unlock(&acc_queue->lock);
-
- return full ? -ENOSPC : 0;
-}
diff --git a/drivers/gpu/drm/xe/xe_gt_pagefault.h b/drivers/gpu/drm/xe/xe_gt_pagefault.h
deleted file mode 100644
index 839c065a5e4c..000000000000
--- a/drivers/gpu/drm/xe/xe_gt_pagefault.h
+++ /dev/null
@@ -1,19 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/*
- * Copyright © 2022 Intel Corporation
- */
-
-#ifndef _XE_GT_PAGEFAULT_H_
-#define _XE_GT_PAGEFAULT_H_
-
-#include <linux/types.h>
-
-struct xe_gt;
-struct xe_guc;
-
-int xe_gt_pagefault_init(struct xe_gt *gt);
-void xe_gt_pagefault_reset(struct xe_gt *gt);
-int xe_guc_pagefault_handler(struct xe_guc *guc, u32 *msg, u32 len);
-int xe_guc_access_counter_notify_handler(struct xe_guc *guc, u32 *msg, u32 len);
-
-#endif /* _XE_GT_PAGEFAULT_ */
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
index c4dda87b47cc..0714c758b9c1 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf.c
@@ -158,39 +158,19 @@ void xe_gt_sriov_pf_init_hw(struct xe_gt *gt)
xe_gt_sriov_pf_service_update(gt);
}
-static u32 pf_get_vf_regs_stride(struct xe_device *xe)
-{
- return GRAPHICS_VERx100(xe) > 1200 ? 0x400 : 0x1000;
-}
-
-static struct xe_reg xe_reg_vf_to_pf(struct xe_reg vf_reg, unsigned int vfid, u32 stride)
-{
- struct xe_reg pf_reg = vf_reg;
-
- pf_reg.vf = 0;
- pf_reg.addr += stride * vfid;
-
- return pf_reg;
-}
-
static void pf_clear_vf_scratch_regs(struct xe_gt *gt, unsigned int vfid)
{
- u32 stride = pf_get_vf_regs_stride(gt_to_xe(gt));
- struct xe_reg scratch;
- int n, count;
+ struct xe_mmio mmio;
+ int n;
+
+ xe_mmio_init_vf_view(&mmio, &gt->mmio, vfid);
if (xe_gt_is_media_type(gt)) {
- count = MED_VF_SW_FLAG_COUNT;
- for (n = 0; n < count; n++) {
- scratch = xe_reg_vf_to_pf(MED_VF_SW_FLAG(n), vfid, stride);
- xe_mmio_write32(&gt->mmio, scratch, 0);
- }
+ for (n = 0; n < MED_VF_SW_FLAG_COUNT; n++)
+ xe_mmio_write32(&mmio, MED_VF_SW_FLAG(n), 0);
} else {
- count = VF_SW_FLAG_COUNT;
- for (n = 0; n < count; n++) {
- scratch = xe_reg_vf_to_pf(VF_SW_FLAG(n), vfid, stride);
- xe_mmio_write32(&gt->mmio, scratch, 0);
- }
+ for (n = 0; n < VF_SW_FLAG_COUNT; n++)
+ xe_mmio_write32(&mmio, VF_SW_FLAG(n), 0);
}
}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
index 6344b5205c08..59c5c6b4d994 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.c
@@ -9,6 +9,7 @@
#include "abi/guc_actions_sriov_abi.h"
#include "abi/guc_klvs_abi.h"
+#include "regs/xe_gtt_defs.h"
#include "regs/xe_guc_regs.h"
#include "xe_bo.h"
@@ -697,6 +698,22 @@ static u64 pf_estimate_fair_ggtt(struct xe_gt *gt, unsigned int num_vfs)
return fair;
}
+static u64 pf_profile_fair_ggtt(struct xe_gt *gt, unsigned int num_vfs)
+{
+ bool admin_only_pf = xe_sriov_pf_admin_only(gt_to_xe(gt));
+ u64 shareable = ALIGN_DOWN(GUC_GGTT_TOP, SZ_512M);
+ u64 alignment = pf_get_ggtt_alignment(gt);
+
+ if (admin_only_pf && num_vfs == 1)
+ return ALIGN_DOWN(shareable, alignment);
+
+ /* need to hardcode due to ~512M of GGTT being reserved */
+ if (num_vfs > 56)
+ return SZ_64M - SZ_8M;
+
+ return rounddown_pow_of_two(div_u64(shareable, num_vfs));
+}
+
/**
* xe_gt_sriov_pf_config_set_fair_ggtt - Provision many VFs with fair GGTT.
* @gt: the &xe_gt (can't be media)
@@ -710,6 +727,7 @@ static u64 pf_estimate_fair_ggtt(struct xe_gt *gt, unsigned int num_vfs)
int xe_gt_sriov_pf_config_set_fair_ggtt(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
+ u64 profile = pf_profile_fair_ggtt(gt, num_vfs);
u64 fair;
xe_gt_assert(gt, vfid);
@@ -723,9 +741,71 @@ int xe_gt_sriov_pf_config_set_fair_ggtt(struct xe_gt *gt, unsigned int vfid,
if (!fair)
return -ENOSPC;
+ fair = min(fair, profile);
+ if (fair < profile)
+ xe_gt_sriov_info(gt, "Using non-profile provisioning (%s %llu vs %llu)\n",
+ "GGTT", fair, profile);
+
return xe_gt_sriov_pf_config_bulk_set_ggtt(gt, vfid, num_vfs, fair);
}
+/**
+ * xe_gt_sriov_pf_config_ggtt_save() - Save a VF provisioned GGTT data into a buffer.
+ * @gt: the &xe_gt
+ * @vfid: VF identifier (can't be 0)
+ * @buf: the GGTT data destination buffer (or NULL to query the buf size)
+ * @size: the size of the buffer (or 0 to query the buf size)
+ *
+ * This function can only be called on PF.
+ *
+ * Return: size of the buffer needed to save GGTT data if querying,
+ * 0 on successful save or a negative error code on failure.
+ */
+ssize_t xe_gt_sriov_pf_config_ggtt_save(struct xe_gt *gt, unsigned int vfid,
+ void *buf, size_t size)
+{
+ struct xe_ggtt_node *node;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid);
+ xe_gt_assert(gt, !(!buf ^ !size));
+
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ node = pf_pick_vf_config(gt, vfid)->ggtt_region;
+
+ if (!buf)
+ return xe_ggtt_node_pt_size(node);
+
+ return xe_ggtt_node_save(node, buf, size, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_config_ggtt_restore() - Restore a VF provisioned GGTT data from a buffer.
+ * @gt: the &xe_gt
+ * @vfid: VF identifier (can't be 0)
+ * @buf: the GGTT data source buffer
+ * @size: the size of the buffer
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_ggtt_restore(struct xe_gt *gt, unsigned int vfid,
+ const void *buf, size_t size)
+{
+ struct xe_ggtt_node *node;
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid);
+
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ node = pf_pick_vf_config(gt, vfid)->ggtt_region;
+
+ return xe_ggtt_node_load(node, buf, size, vfid);
+}
+
static u32 pf_get_min_spare_ctxs(struct xe_gt *gt)
{
/* XXX: preliminary */
@@ -924,7 +1004,8 @@ static int pf_config_bulk_set_u32_done(struct xe_gt *gt, unsigned int first, uns
const char *what, const char *(*unit)(u32),
unsigned int last, int err)
{
- xe_gt_assert(gt, first);
+ char name[8];
+
xe_gt_assert(gt, num_vfs);
xe_gt_assert(gt, first <= last);
@@ -932,8 +1013,9 @@ static int pf_config_bulk_set_u32_done(struct xe_gt *gt, unsigned int first, uns
return pf_config_set_u32_done(gt, first, value, get(gt, first), what, unit, err);
if (unlikely(err)) {
- xe_gt_sriov_notice(gt, "Failed to bulk provision VF%u..VF%u with %s\n",
- first, first + num_vfs - 1, what);
+ xe_gt_sriov_notice(gt, "Failed to bulk provision %s..VF%u with %s\n",
+ xe_sriov_function_name(first, name, sizeof(name)),
+ first + num_vfs - 1, what);
if (last > first)
pf_config_bulk_set_u32_done(gt, first, last - first, value,
get, what, unit, last, 0);
@@ -942,8 +1024,9 @@ static int pf_config_bulk_set_u32_done(struct xe_gt *gt, unsigned int first, uns
/* pick actual value from first VF - bulk provisioning shall be equal across all VFs */
value = get(gt, first);
- xe_gt_sriov_info(gt, "VF%u..VF%u provisioned with %u%s %s\n",
- first, first + num_vfs - 1, value, unit(value), what);
+ xe_gt_sriov_info(gt, "%s..VF%u provisioned with %u%s %s\n",
+ xe_sriov_function_name(first, name, sizeof(name)),
+ first + num_vfs - 1, value, unit(value), what);
return 0;
}
@@ -982,6 +1065,16 @@ int xe_gt_sriov_pf_config_bulk_set_ctxs(struct xe_gt *gt, unsigned int vfid,
"GuC context IDs", no_unit, n, err);
}
+static u32 pf_profile_fair_ctxs(struct xe_gt *gt, unsigned int num_vfs)
+{
+ bool admin_only_pf = xe_sriov_pf_admin_only(gt_to_xe(gt));
+
+ if (admin_only_pf && num_vfs == 1)
+ return ALIGN_DOWN(GUC_ID_MAX, SZ_1K);
+
+ return rounddown_pow_of_two(GUC_ID_MAX / num_vfs);
+}
+
static u32 pf_estimate_fair_ctxs(struct xe_gt *gt, unsigned int num_vfs)
{
struct xe_guc_id_mgr *idm = &gt->uc.guc.submission_state.idm;
@@ -1014,6 +1107,7 @@ static u32 pf_estimate_fair_ctxs(struct xe_gt *gt, unsigned int num_vfs)
int xe_gt_sriov_pf_config_set_fair_ctxs(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
+ u32 profile = pf_profile_fair_ctxs(gt, num_vfs);
u32 fair;
xe_gt_assert(gt, vfid);
@@ -1026,6 +1120,11 @@ int xe_gt_sriov_pf_config_set_fair_ctxs(struct xe_gt *gt, unsigned int vfid,
if (!fair)
return -ENOSPC;
+ fair = min(fair, profile);
+ if (fair < profile)
+ xe_gt_sriov_info(gt, "Using non-profile provisioning (%s %u vs %u)\n",
+ "GuC context IDs", fair, profile);
+
return xe_gt_sriov_pf_config_bulk_set_ctxs(gt, vfid, num_vfs, fair);
}
@@ -1230,6 +1329,17 @@ int xe_gt_sriov_pf_config_bulk_set_dbs(struct xe_gt *gt, unsigned int vfid,
"GuC doorbell IDs", no_unit, n, err);
}
+static u32 pf_profile_fair_dbs(struct xe_gt *gt, unsigned int num_vfs)
+{
+ bool admin_only_pf = xe_sriov_pf_admin_only(gt_to_xe(gt));
+
+ /* XXX: preliminary */
+ if (admin_only_pf && num_vfs == 1)
+ return GUC_NUM_DOORBELLS - SZ_16;
+
+ return rounddown_pow_of_two(GUC_NUM_DOORBELLS / (num_vfs + 1));
+}
+
static u32 pf_estimate_fair_dbs(struct xe_gt *gt, unsigned int num_vfs)
{
struct xe_guc_db_mgr *dbm = &gt->uc.guc.dbm;
@@ -1262,6 +1372,7 @@ static u32 pf_estimate_fair_dbs(struct xe_gt *gt, unsigned int num_vfs)
int xe_gt_sriov_pf_config_set_fair_dbs(struct xe_gt *gt, unsigned int vfid,
unsigned int num_vfs)
{
+ u32 profile = pf_profile_fair_dbs(gt, num_vfs);
u32 fair;
xe_gt_assert(gt, vfid);
@@ -1274,6 +1385,11 @@ int xe_gt_sriov_pf_config_set_fair_dbs(struct xe_gt *gt, unsigned int vfid,
if (!fair)
return -ENOSPC;
+ fair = min(fair, profile);
+ if (fair < profile)
+ xe_gt_sriov_info(gt, "Using non-profile provisioning (%s %u vs %u)\n",
+ "GuC doorbell IDs", fair, profile);
+
return xe_gt_sriov_pf_config_bulk_set_dbs(gt, vfid, num_vfs, fair);
}
@@ -1484,7 +1600,8 @@ static int pf_provision_vf_lmem(struct xe_gt *gt, unsigned int vfid, u64 size)
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_NEEDS_2M |
XE_BO_FLAG_PINNED |
- XE_BO_FLAG_PINNED_LATE_RESTORE);
+ XE_BO_FLAG_PINNED_LATE_RESTORE |
+ XE_BO_FLAG_FORCE_USER_VRAM);
if (IS_ERR(bo))
return PTR_ERR(bo);
@@ -1547,7 +1664,8 @@ int xe_gt_sriov_pf_config_set_lmem(struct xe_gt *gt, unsigned int vfid, u64 size
{
int err;
- xe_gt_assert(gt, xe_device_has_lmtt(gt_to_xe(gt)));
+ if (!xe_device_has_lmtt(gt_to_xe(gt)))
+ return -EPERM;
mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
if (vfid)
@@ -1597,6 +1715,32 @@ int xe_gt_sriov_pf_config_bulk_set_lmem(struct xe_gt *gt, unsigned int vfid,
"LMEM", n, err);
}
+static struct xe_bo *pf_get_vf_config_lmem_obj(struct xe_gt *gt, unsigned int vfid)
+{
+ struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
+
+ return config->lmem_obj;
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_lmem_obj() - Take a reference to the struct &xe_bo backing VF LMEM.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ *
+ * This function can only be called on PF.
+ * The caller is responsible for calling xe_bo_put() on the returned object.
+ *
+ * Return: pointer to struct &xe_bo backing VF LMEM (if any).
+ */
+struct xe_bo *xe_gt_sriov_pf_config_get_lmem_obj(struct xe_gt *gt, unsigned int vfid)
+{
+ xe_gt_assert(gt, vfid);
+
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ return xe_bo_get(pf_get_vf_config_lmem_obj(gt, vfid));
+}
+
static u64 pf_query_free_lmem(struct xe_gt *gt)
{
struct xe_tile *tile = gt->tile;
@@ -1722,7 +1866,7 @@ static int pf_provision_exec_quantum(struct xe_gt *gt, unsigned int vfid,
return 0;
}
-static int pf_get_exec_quantum(struct xe_gt *gt, unsigned int vfid)
+static u32 pf_get_exec_quantum(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
@@ -1730,47 +1874,107 @@ static int pf_get_exec_quantum(struct xe_gt *gt, unsigned int vfid)
}
/**
- * xe_gt_sriov_pf_config_set_exec_quantum - Configure execution quantum for the VF.
+ * xe_gt_sriov_pf_config_set_exec_quantum_locked() - Configure PF/VF execution quantum.
* @gt: the &xe_gt
- * @vfid: the VF identifier
+ * @vfid: the PF or VF identifier
* @exec_quantum: requested execution quantum in milliseconds (0 is infinity)
*
- * This function can only be called on PF.
+ * This function can only be called on PF with the master mutex hold.
+ * It will log the provisioned value or an error in case of the failure.
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_gt_sriov_pf_config_set_exec_quantum(struct xe_gt *gt, unsigned int vfid,
- u32 exec_quantum)
+int xe_gt_sriov_pf_config_set_exec_quantum_locked(struct xe_gt *gt, unsigned int vfid,
+ u32 exec_quantum)
{
int err;
- mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
err = pf_provision_exec_quantum(gt, vfid, exec_quantum);
- mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u32_done(gt, vfid, exec_quantum,
- xe_gt_sriov_pf_config_get_exec_quantum(gt, vfid),
+ pf_get_exec_quantum(gt, vfid),
"execution quantum", exec_quantum_unit, err);
}
/**
- * xe_gt_sriov_pf_config_get_exec_quantum - Get VF's execution quantum.
+ * xe_gt_sriov_pf_config_set_exec_quantum() - Configure PF/VF execution quantum.
* @gt: the &xe_gt
- * @vfid: the VF identifier
+ * @vfid: the PF or VF identifier
+ * @exec_quantum: requested execution quantum in milliseconds (0 is infinity)
*
* This function can only be called on PF.
+ * It will log the provisioned value or an error in case of the failure.
*
- * Return: VF's (or PF's) execution quantum in milliseconds.
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_set_exec_quantum(struct xe_gt *gt, unsigned int vfid,
+ u32 exec_quantum)
+{
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ return xe_gt_sriov_pf_config_set_exec_quantum_locked(gt, vfid, exec_quantum);
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_exec_quantum_locked() - Get PF/VF execution quantum.
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
+ *
+ * This function can only be called on PF with the master mutex hold.
+ *
+ * Return: execution quantum in milliseconds (or 0 if infinity).
+ */
+u32 xe_gt_sriov_pf_config_get_exec_quantum_locked(struct xe_gt *gt, unsigned int vfid)
+{
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
+ return pf_get_exec_quantum(gt, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_exec_quantum() - Get PF/VF execution quantum.
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
+ *
+ * This function can only be called on PF.
+ *
+ * Return: execution quantum in milliseconds (or 0 if infinity).
*/
u32 xe_gt_sriov_pf_config_get_exec_quantum(struct xe_gt *gt, unsigned int vfid)
{
- u32 exec_quantum;
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
- mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
- exec_quantum = pf_get_exec_quantum(gt, vfid);
- mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
+ return pf_get_exec_quantum(gt, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked() - Configure EQ for PF and VFs.
+ * @gt: the &xe_gt to configure
+ * @exec_quantum: requested execution quantum in milliseconds (0 is infinity)
+ *
+ * This function can only be called on PF with the master mutex hold.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(struct xe_gt *gt, u32 exec_quantum)
+{
+ unsigned int totalvfs = xe_gt_sriov_pf_get_totalvfs(gt);
+ unsigned int n;
+ int err = 0;
- return exec_quantum;
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
+ for (n = 0; n <= totalvfs; n++) {
+ err = pf_provision_exec_quantum(gt, VFID(n), exec_quantum);
+ if (err)
+ break;
+ }
+
+ return pf_config_bulk_set_u32_done(gt, 0, 1 + totalvfs, exec_quantum,
+ pf_get_exec_quantum, "execution quantum",
+ exec_quantum_unit, n, err);
}
static const char *preempt_timeout_unit(u32 preempt_timeout)
@@ -1793,7 +1997,7 @@ static int pf_provision_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
return 0;
}
-static int pf_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid)
+static u32 pf_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_config *config = pf_pick_vf_config(gt, vfid);
@@ -1801,47 +2005,106 @@ static int pf_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid)
}
/**
- * xe_gt_sriov_pf_config_set_preempt_timeout - Configure preemption timeout for the VF.
+ * xe_gt_sriov_pf_config_set_preempt_timeout_locked() - Configure PF/VF preemption timeout.
* @gt: the &xe_gt
- * @vfid: the VF identifier
+ * @vfid: the PF or VF identifier
* @preempt_timeout: requested preemption timeout in microseconds (0 is infinity)
*
- * This function can only be called on PF.
+ * This function can only be called on PF with the master mutex hold.
+ * It will log the provisioned value or an error in case of the failure.
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
- u32 preempt_timeout)
+int xe_gt_sriov_pf_config_set_preempt_timeout_locked(struct xe_gt *gt, unsigned int vfid,
+ u32 preempt_timeout)
{
int err;
- mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
err = pf_provision_preempt_timeout(gt, vfid, preempt_timeout);
- mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
return pf_config_set_u32_done(gt, vfid, preempt_timeout,
- xe_gt_sriov_pf_config_get_preempt_timeout(gt, vfid),
+ pf_get_preempt_timeout(gt, vfid),
"preemption timeout", preempt_timeout_unit, err);
}
/**
- * xe_gt_sriov_pf_config_get_preempt_timeout - Get VF's preemption timeout.
+ * xe_gt_sriov_pf_config_set_preempt_timeout() - Configure PF/VF preemption timeout.
* @gt: the &xe_gt
- * @vfid: the VF identifier
+ * @vfid: the PF or VF identifier
+ * @preempt_timeout: requested preemption timeout in microseconds (0 is infinity)
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
+ u32 preempt_timeout)
+{
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
+
+ return xe_gt_sriov_pf_config_set_preempt_timeout_locked(gt, vfid, preempt_timeout);
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_preempt_timeout_locked() - Get PF/VF preemption timeout.
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
+ *
+ * This function can only be called on PF with the master mutex hold.
+ *
+ * Return: preemption timeout in microseconds (or 0 if infinity).
+ */
+u32 xe_gt_sriov_pf_config_get_preempt_timeout_locked(struct xe_gt *gt, unsigned int vfid)
+{
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
+ return pf_get_preempt_timeout(gt, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_config_get_preempt_timeout() - Get PF/VF preemption timeout.
+ * @gt: the &xe_gt
+ * @vfid: the PF or VF identifier
*
* This function can only be called on PF.
*
- * Return: VF's (or PF's) preemption timeout in microseconds.
+ * Return: preemption timeout in microseconds (or 0 if infinity).
*/
u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid)
{
- u32 preempt_timeout;
+ guard(mutex)(xe_gt_sriov_pf_master_mutex(gt));
- mutex_lock(xe_gt_sriov_pf_master_mutex(gt));
- preempt_timeout = pf_get_preempt_timeout(gt, vfid);
- mutex_unlock(xe_gt_sriov_pf_master_mutex(gt));
+ return pf_get_preempt_timeout(gt, vfid);
+}
- return preempt_timeout;
+/**
+ * xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked() - Configure PT for PF and VFs.
+ * @gt: the &xe_gt to configure
+ * @preempt_timeout: requested preemption timeout in microseconds (0 is infinity)
+ *
+ * This function can only be called on PF with the master mutex hold.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(struct xe_gt *gt, u32 preempt_timeout)
+{
+ unsigned int totalvfs = xe_gt_sriov_pf_get_totalvfs(gt);
+ unsigned int n;
+ int err = 0;
+
+ lockdep_assert_held(xe_gt_sriov_pf_master_mutex(gt));
+
+ for (n = 0; n <= totalvfs; n++) {
+ err = pf_provision_preempt_timeout(gt, VFID(n), preempt_timeout);
+ if (err)
+ break;
+ }
+
+ return pf_config_bulk_set_u32_done(gt, 0, 1 + totalvfs, preempt_timeout,
+ pf_get_preempt_timeout, "preemption timeout",
+ preempt_timeout_unit, n, err);
}
static const char *sched_priority_unit(u32 priority)
@@ -2669,3 +2932,7 @@ int xe_gt_sriov_pf_config_print_available_ggtt(struct xe_gt *gt, struct drm_prin
return 0;
}
+
+#if IS_BUILTIN(CONFIG_DRM_XE_KUNIT_TEST)
+#include "tests/xe_gt_sriov_pf_config_kunit.c"
+#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
index 513e6512a575..4975730423d7 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_config.h
@@ -36,14 +36,25 @@ int xe_gt_sriov_pf_config_set_lmem(struct xe_gt *gt, unsigned int vfid, u64 size
int xe_gt_sriov_pf_config_set_fair_lmem(struct xe_gt *gt, unsigned int vfid, unsigned int num_vfs);
int xe_gt_sriov_pf_config_bulk_set_lmem(struct xe_gt *gt, unsigned int vfid, unsigned int num_vfs,
u64 size);
+struct xe_bo *xe_gt_sriov_pf_config_get_lmem_obj(struct xe_gt *gt, unsigned int vfid);
u32 xe_gt_sriov_pf_config_get_exec_quantum(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_config_set_exec_quantum(struct xe_gt *gt, unsigned int vfid, u32 exec_quantum);
+u32 xe_gt_sriov_pf_config_get_exec_quantum_locked(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_config_set_exec_quantum_locked(struct xe_gt *gt, unsigned int vfid,
+ u32 exec_quantum);
+int xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(struct xe_gt *gt, u32 exec_quantum);
+
u32 xe_gt_sriov_pf_config_get_preempt_timeout(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_config_set_preempt_timeout(struct xe_gt *gt, unsigned int vfid,
u32 preempt_timeout);
+u32 xe_gt_sriov_pf_config_get_preempt_timeout_locked(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_config_set_preempt_timeout_locked(struct xe_gt *gt, unsigned int vfid,
+ u32 preempt_timeout);
+int xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(struct xe_gt *gt, u32 preempt_timeout);
+
u32 xe_gt_sriov_pf_config_get_sched_priority(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_config_set_sched_priority(struct xe_gt *gt, unsigned int vfid, u32 priority);
@@ -61,6 +72,11 @@ ssize_t xe_gt_sriov_pf_config_save(struct xe_gt *gt, unsigned int vfid, void *bu
int xe_gt_sriov_pf_config_restore(struct xe_gt *gt, unsigned int vfid,
const void *buf, size_t size);
+ssize_t xe_gt_sriov_pf_config_ggtt_save(struct xe_gt *gt, unsigned int vfid,
+ void *buf, size_t size);
+int xe_gt_sriov_pf_config_ggtt_restore(struct xe_gt *gt, unsigned int vfid,
+ const void *buf, size_t size);
+
bool xe_gt_sriov_pf_config_is_empty(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_config_init(struct xe_gt *gt);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
index 4f7fff892bc0..bf48b05797de 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.c
@@ -18,6 +18,10 @@
#include "xe_gt_sriov_printk.h"
#include "xe_guc_ct.h"
#include "xe_sriov.h"
+#include "xe_sriov_packet.h"
+#include "xe_sriov_packet_types.h"
+#include "xe_sriov_pf_control.h"
+#include "xe_sriov_pf_migration.h"
#include "xe_sriov_pf_service.h"
#include "xe_tile.h"
@@ -170,6 +174,7 @@ static const char *control_bit_to_string(enum xe_gt_sriov_control_bits bit)
CASE2STR(FLR_SEND_START);
CASE2STR(FLR_WAIT_GUC);
CASE2STR(FLR_GUC_DONE);
+ CASE2STR(FLR_SYNC);
CASE2STR(FLR_RESET_CONFIG);
CASE2STR(FLR_RESET_DATA);
CASE2STR(FLR_RESET_MMIO);
@@ -179,9 +184,20 @@ static const char *control_bit_to_string(enum xe_gt_sriov_control_bits bit)
CASE2STR(PAUSE_SEND_PAUSE);
CASE2STR(PAUSE_WAIT_GUC);
CASE2STR(PAUSE_GUC_DONE);
- CASE2STR(PAUSE_SAVE_GUC);
CASE2STR(PAUSE_FAILED);
CASE2STR(PAUSED);
+ CASE2STR(SAVE_WIP);
+ CASE2STR(SAVE_PROCESS_DATA);
+ CASE2STR(SAVE_WAIT_DATA);
+ CASE2STR(SAVE_DATA_DONE);
+ CASE2STR(SAVE_FAILED);
+ CASE2STR(SAVED);
+ CASE2STR(RESTORE_WIP);
+ CASE2STR(RESTORE_PROCESS_DATA);
+ CASE2STR(RESTORE_WAIT_DATA);
+ CASE2STR(RESTORE_DATA_DONE);
+ CASE2STR(RESTORE_FAILED);
+ CASE2STR(RESTORED);
CASE2STR(RESUME_WIP);
CASE2STR(RESUME_SEND_RESUME);
CASE2STR(RESUME_FAILED);
@@ -206,6 +222,8 @@ static unsigned long pf_get_default_timeout(enum xe_gt_sriov_control_bits bit)
case XE_GT_SRIOV_STATE_FLR_WIP:
case XE_GT_SRIOV_STATE_FLR_RESET_CONFIG:
return 5 * HZ;
+ case XE_GT_SRIOV_STATE_RESTORE_WIP:
+ return 20 * HZ;
default:
return HZ;
}
@@ -223,7 +241,7 @@ static unsigned long *pf_peek_vf_state(struct xe_gt *gt, unsigned int vfid)
{
struct xe_gt_sriov_control_state *cs = pf_pick_vf_control(gt, vfid);
- return &cs->state;
+ return cs->state;
}
static bool pf_check_vf_state(struct xe_gt *gt, unsigned int vfid,
@@ -271,12 +289,19 @@ static bool pf_expect_vf_not_state(struct xe_gt *gt, unsigned int vfid,
return result;
}
+static void pf_track_vf_state(struct xe_gt *gt, unsigned int vfid,
+ enum xe_gt_sriov_control_bits bit,
+ const char *what)
+{
+ xe_gt_sriov_dbg_verbose(gt, "VF%u state %s(%d) %s\n",
+ vfid, control_bit_to_string(bit), bit, what);
+}
+
static bool pf_enter_vf_state(struct xe_gt *gt, unsigned int vfid,
enum xe_gt_sriov_control_bits bit)
{
if (!test_and_set_bit(bit, pf_peek_vf_state(gt, vfid))) {
- xe_gt_sriov_dbg_verbose(gt, "VF%u state %s(%d) enter\n",
- vfid, control_bit_to_string(bit), bit);
+ pf_track_vf_state(gt, vfid, bit, "enter");
return true;
}
return false;
@@ -286,8 +311,7 @@ static bool pf_exit_vf_state(struct xe_gt *gt, unsigned int vfid,
enum xe_gt_sriov_control_bits bit)
{
if (test_and_clear_bit(bit, pf_peek_vf_state(gt, vfid))) {
- xe_gt_sriov_dbg_verbose(gt, "VF%u state %s(%d) exit\n",
- vfid, control_bit_to_string(bit), bit);
+ pf_track_vf_state(gt, vfid, bit, "exit");
return true;
}
return false;
@@ -321,6 +345,8 @@ static void pf_exit_vf_mismatch(struct xe_gt *gt, unsigned int vfid)
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_FAILED);
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESUME_FAILED);
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_FAILED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_FAILED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_FAILED);
}
#define pf_enter_vf_state_machine_bug(gt, vfid) ({ \
@@ -351,6 +377,8 @@ static void pf_queue_vf(struct xe_gt *gt, unsigned int vfid)
static void pf_exit_vf_flr_wip(struct xe_gt *gt, unsigned int vfid);
static void pf_exit_vf_stop_wip(struct xe_gt *gt, unsigned int vfid);
+static void pf_exit_vf_save_wip(struct xe_gt *gt, unsigned int vfid);
+static void pf_exit_vf_restore_wip(struct xe_gt *gt, unsigned int vfid);
static void pf_exit_vf_pause_wip(struct xe_gt *gt, unsigned int vfid);
static void pf_exit_vf_resume_wip(struct xe_gt *gt, unsigned int vfid);
@@ -372,6 +400,8 @@ static void pf_exit_vf_wip(struct xe_gt *gt, unsigned int vfid)
pf_exit_vf_flr_wip(gt, vfid);
pf_exit_vf_stop_wip(gt, vfid);
+ pf_exit_vf_save_wip(gt, vfid);
+ pf_exit_vf_restore_wip(gt, vfid);
pf_exit_vf_pause_wip(gt, vfid);
pf_exit_vf_resume_wip(gt, vfid);
@@ -391,6 +421,8 @@ static void pf_enter_vf_ready(struct xe_gt *gt, unsigned int vfid)
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED);
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_STOPPED);
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESUMED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORED);
pf_exit_vf_mismatch(gt, vfid);
pf_exit_vf_wip(gt, vfid);
}
@@ -421,8 +453,7 @@ static void pf_enter_vf_ready(struct xe_gt *gt, unsigned int vfid)
* : PAUSE_GUC_DONE o-----restart
* : | :
* : | o---<--busy :
- * : v / / :
- * : PAUSE_SAVE_GUC :
+ * : / :
* : / :
* : / :
* :....o..............o...............o...........:
@@ -442,7 +473,6 @@ static void pf_exit_vf_pause_wip(struct xe_gt *gt, unsigned int vfid)
pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_SEND_PAUSE);
pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC);
pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_GUC_DONE);
- pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_SAVE_GUC);
}
}
@@ -473,41 +503,12 @@ static void pf_enter_vf_pause_rejected(struct xe_gt *gt, unsigned int vfid)
pf_enter_vf_pause_failed(gt, vfid);
}
-static void pf_enter_vf_pause_save_guc(struct xe_gt *gt, unsigned int vfid)
-{
- if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_SAVE_GUC))
- pf_enter_vf_state_machine_bug(gt, vfid);
-}
-
-static bool pf_exit_vf_pause_save_guc(struct xe_gt *gt, unsigned int vfid)
-{
- int err;
-
- if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_SAVE_GUC))
- return false;
-
- err = xe_gt_sriov_pf_migration_save_guc_state(gt, vfid);
- if (err) {
- /* retry if busy */
- if (err == -EBUSY) {
- pf_enter_vf_pause_save_guc(gt, vfid);
- return true;
- }
- /* give up on error */
- if (err == -EIO)
- pf_enter_vf_mismatch(gt, vfid);
- }
-
- pf_enter_vf_pause_completed(gt, vfid);
- return true;
-}
-
static bool pf_exit_vf_pause_guc_done(struct xe_gt *gt, unsigned int vfid)
{
if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSE_GUC_DONE))
return false;
- pf_enter_vf_pause_save_guc(gt, vfid);
+ pf_enter_vf_pause_completed(gt, vfid);
return true;
}
@@ -616,7 +617,7 @@ int xe_gt_sriov_pf_control_pause_vf(struct xe_gt *gt, unsigned int vfid)
}
if (pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED)) {
- xe_gt_sriov_info(gt, "VF%u paused!\n", vfid);
+ xe_gt_sriov_dbg(gt, "VF%u paused!\n", vfid);
return 0;
}
@@ -667,6 +668,8 @@ static void pf_enter_vf_resumed(struct xe_gt *gt, unsigned int vfid)
{
pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESUMED);
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORED);
pf_exit_vf_mismatch(gt, vfid);
pf_exit_vf_wip(gt, vfid);
}
@@ -745,6 +748,16 @@ int xe_gt_sriov_pf_control_resume_vf(struct xe_gt *gt, unsigned int vfid)
return -EPERM;
}
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WIP)) {
+ xe_gt_sriov_dbg(gt, "VF%u save is in progress!\n", vfid);
+ return -EBUSY;
+ }
+
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WIP)) {
+ xe_gt_sriov_dbg(gt, "VF%u restore is in progress!\n", vfid);
+ return -EBUSY;
+ }
+
if (!pf_enter_vf_resume_wip(gt, vfid)) {
xe_gt_sriov_dbg(gt, "VF%u resume already in progress!\n", vfid);
return -EALREADY;
@@ -755,7 +768,7 @@ int xe_gt_sriov_pf_control_resume_vf(struct xe_gt *gt, unsigned int vfid)
return err;
if (pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESUMED)) {
- xe_gt_sriov_info(gt, "VF%u resumed!\n", vfid);
+ xe_gt_sriov_dbg(gt, "VF%u resumed!\n", vfid);
return 0;
}
@@ -769,6 +782,562 @@ int xe_gt_sriov_pf_control_resume_vf(struct xe_gt *gt, unsigned int vfid)
}
/**
+ * DOC: The VF SAVE state machine
+ *
+ * SAVE extends the PAUSED state.
+ *
+ * The VF SAVE state machine looks like::
+ *
+ * ....PAUSED....................................................
+ * : :
+ * : (O)<---------o :
+ * : | \ :
+ * : save (SAVED) (SAVE_FAILED) :
+ * : | ^ ^ :
+ * : | | | :
+ * : ....V...............o...........o......SAVE_WIP......... :
+ * : : | | | : :
+ * : : | empty | : :
+ * : : | | | : :
+ * : : | | | : :
+ * : : | DATA_DONE | : :
+ * : : | ^ | : :
+ * : : | | error : :
+ * : : | no_data / : :
+ * : : | / / : :
+ * : : | / / : :
+ * : : | / / : :
+ * : : o---------->PROCESS_DATA<----consume : :
+ * : : \ \ : :
+ * : : \ \ : :
+ * : : \ \ : :
+ * : : ring_full----->WAIT_DATA : :
+ * : : : :
+ * : :......................................................: :
+ * :............................................................:
+ *
+ * For the full state machine view, see `The VF state machine`_.
+ */
+
+static void pf_exit_vf_save_wip(struct xe_gt *gt, unsigned int vfid)
+{
+ if (pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WIP)) {
+ xe_gt_sriov_pf_migration_ring_free(gt, vfid);
+
+ pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_PROCESS_DATA);
+ pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WAIT_DATA);
+ pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_DATA_DONE);
+ }
+}
+
+static void pf_enter_vf_saved(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVED))
+ pf_enter_vf_state_machine_bug(gt, vfid);
+
+ xe_gt_sriov_dbg(gt, "VF%u saved!\n", vfid);
+
+ pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED);
+ pf_exit_vf_mismatch(gt, vfid);
+ pf_exit_vf_wip(gt, vfid);
+}
+
+static void pf_enter_vf_save_failed(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_FAILED))
+ pf_enter_vf_state_machine_bug(gt, vfid);
+
+ wake_up_all(xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid));
+
+ pf_exit_vf_wip(gt, vfid);
+}
+
+static int pf_handle_vf_save_data(struct xe_gt *gt, unsigned int vfid)
+{
+ int ret;
+
+ if (xe_gt_sriov_pf_migration_save_data_pending(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_GUC)) {
+ ret = xe_gt_sriov_pf_migration_guc_save(gt, vfid);
+ if (ret)
+ return ret;
+
+ xe_gt_sriov_pf_migration_save_data_complete(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_GUC);
+
+ return -EAGAIN;
+ }
+
+ if (xe_gt_sriov_pf_migration_save_data_pending(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_GGTT)) {
+ ret = xe_gt_sriov_pf_migration_ggtt_save(gt, vfid);
+ if (ret)
+ return ret;
+
+ xe_gt_sriov_pf_migration_save_data_complete(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_GGTT);
+
+ return -EAGAIN;
+ }
+
+ if (xe_gt_sriov_pf_migration_save_data_pending(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_MMIO)) {
+ ret = xe_gt_sriov_pf_migration_mmio_save(gt, vfid);
+ if (ret)
+ return ret;
+
+ xe_gt_sriov_pf_migration_save_data_complete(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_MMIO);
+
+ return -EAGAIN;
+ }
+
+ if (xe_gt_sriov_pf_migration_save_data_pending(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_VRAM)) {
+ ret = xe_gt_sriov_pf_migration_vram_save(gt, vfid);
+ if (ret == -EAGAIN)
+ return -EAGAIN;
+ else if (ret)
+ return ret;
+
+ xe_gt_sriov_pf_migration_save_data_complete(gt, vfid,
+ XE_SRIOV_PACKET_TYPE_VRAM);
+
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static bool pf_handle_vf_save(struct xe_gt *gt, unsigned int vfid)
+{
+ int ret;
+
+ if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_PROCESS_DATA))
+ return false;
+
+ if (xe_gt_sriov_pf_migration_ring_full(gt, vfid)) {
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WAIT_DATA);
+ return true;
+ }
+
+ ret = pf_handle_vf_save_data(gt, vfid);
+ if (ret == -EAGAIN)
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_PROCESS_DATA);
+ else if (ret)
+ pf_enter_vf_save_failed(gt, vfid);
+ else
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_DATA_DONE);
+
+ return true;
+}
+
+static void pf_exit_vf_save_wait_data(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WAIT_DATA))
+ return;
+
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_PROCESS_DATA);
+ pf_queue_vf(gt, vfid);
+}
+
+static bool pf_enter_vf_save_wip(struct xe_gt *gt, unsigned int vfid)
+{
+ if (pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WIP)) {
+ xe_gt_sriov_pf_migration_save_init(gt, vfid);
+ pf_enter_vf_wip(gt, vfid);
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_PROCESS_DATA);
+ pf_queue_vf(gt, vfid);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * xe_gt_sriov_pf_control_check_save_data_done() - Check if all save migration data was produced.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: true if all migration data was produced, false otherwise.
+ */
+bool xe_gt_sriov_pf_control_check_save_data_done(struct xe_gt *gt, unsigned int vfid)
+{
+ return pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_DATA_DONE);
+}
+
+/**
+ * xe_gt_sriov_pf_control_check_save_failed() - Check if save processing has failed.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: true if save processing failed, false otherwise.
+ */
+bool xe_gt_sriov_pf_control_check_save_failed(struct xe_gt *gt, unsigned int vfid)
+{
+ return pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_FAILED);
+}
+
+/**
+ * xe_gt_sriov_pf_control_process_save_data() - Queue VF save migration data processing.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_process_save_data(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_expect_vf_not_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_FAILED))
+ return -EIO;
+
+ pf_exit_vf_save_wait_data(gt, vfid);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_trigger_save_vf() - Start an SR-IOV VF migration data save sequence.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_trigger_save_vf(struct xe_gt *gt, unsigned int vfid)
+{
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_STOPPED)) {
+ xe_gt_sriov_dbg(gt, "VF%u is stopped!\n", vfid);
+ return -EPERM;
+ }
+
+ if (!pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED)) {
+ xe_gt_sriov_dbg(gt, "VF%u is not paused!\n", vfid);
+ return -EPERM;
+ }
+
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WIP)) {
+ xe_gt_sriov_dbg(gt, "VF%u restore is in progress!\n", vfid);
+ return -EBUSY;
+ }
+
+ if (!pf_enter_vf_save_wip(gt, vfid)) {
+ xe_gt_sriov_dbg(gt, "VF%u save already in progress!\n", vfid);
+ return -EALREADY;
+ }
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_finish_save_vf() - Complete a VF migration data save sequence.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_finish_save_vf(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_DATA_DONE)) {
+ xe_gt_sriov_err(gt, "VF%u save is still in progress!\n", vfid);
+ return -EIO;
+ }
+
+ pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_DATA_DONE);
+ pf_enter_vf_saved(gt, vfid);
+
+ return 0;
+}
+
+/**
+ * DOC: The VF RESTORE state machine
+ *
+ * RESTORE extends the PAUSED state.
+ *
+ * The VF RESTORE state machine looks like::
+ *
+ * ....PAUSED....................................................
+ * : :
+ * : (O)<---------o :
+ * : | \ :
+ * : restore (RESTORED) (RESTORE_FAILED) :
+ * : | ^ ^ :
+ * : | | | :
+ * : ....V...............o...........o......RESTORE_WIP...... :
+ * : : | | | : :
+ * : : | empty | : :
+ * : : | | | : :
+ * : : | | | : :
+ * : : | DATA_DONE | : :
+ * : : | ^ | : :
+ * : : | | error : :
+ * : : | trailer / : :
+ * : : | / / : :
+ * : : | / / : :
+ * : : | / / : :
+ * : : o---------->PROCESS_DATA<----produce : :
+ * : : \ \ : :
+ * : : \ \ : :
+ * : : \ \ : :
+ * : : ring_empty---->WAIT_DATA : :
+ * : : : :
+ * : :......................................................: :
+ * :............................................................:
+ *
+ * For the full state machine view, see `The VF state machine`_.
+ */
+
+static void pf_exit_vf_restore_wip(struct xe_gt *gt, unsigned int vfid)
+{
+ if (pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WIP)) {
+ xe_gt_sriov_pf_migration_ring_free(gt, vfid);
+
+ pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_PROCESS_DATA);
+ pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WAIT_DATA);
+ pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_DATA_DONE);
+ }
+}
+
+static void pf_enter_vf_restored(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORED))
+ pf_enter_vf_state_machine_bug(gt, vfid);
+
+ xe_gt_sriov_dbg(gt, "VF%u restored!\n", vfid);
+
+ pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED);
+ pf_exit_vf_mismatch(gt, vfid);
+ pf_exit_vf_wip(gt, vfid);
+}
+
+static void pf_enter_vf_restore_failed(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_FAILED))
+ pf_enter_vf_state_machine_bug(gt, vfid);
+
+ wake_up_all(xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid));
+
+ pf_exit_vf_wip(gt, vfid);
+}
+
+static int pf_handle_vf_restore_data(struct xe_gt *gt, unsigned int vfid)
+{
+ struct xe_sriov_packet *data = xe_gt_sriov_pf_migration_restore_consume(gt, vfid);
+ int ret = 0;
+
+ switch (data->hdr.type) {
+ case XE_SRIOV_PACKET_TYPE_GGTT:
+ ret = xe_gt_sriov_pf_migration_ggtt_restore(gt, vfid, data);
+ break;
+ case XE_SRIOV_PACKET_TYPE_MMIO:
+ ret = xe_gt_sriov_pf_migration_mmio_restore(gt, vfid, data);
+ break;
+ case XE_SRIOV_PACKET_TYPE_GUC:
+ ret = xe_gt_sriov_pf_migration_guc_restore(gt, vfid, data);
+ break;
+ case XE_SRIOV_PACKET_TYPE_VRAM:
+ ret = xe_gt_sriov_pf_migration_vram_restore(gt, vfid, data);
+ break;
+ default:
+ xe_gt_sriov_notice(gt, "Skipping VF%u unknown data type: %d\n",
+ vfid, data->hdr.type);
+ break;
+ }
+
+ xe_sriov_packet_free(data);
+
+ return ret;
+}
+
+static bool pf_handle_vf_restore(struct xe_gt *gt, unsigned int vfid)
+{
+ int ret;
+
+ if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_PROCESS_DATA))
+ return false;
+
+ if (xe_gt_sriov_pf_migration_ring_empty(gt, vfid)) {
+ if (pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_DATA_DONE))
+ pf_enter_vf_restored(gt, vfid);
+ else
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WAIT_DATA);
+
+ return true;
+ }
+
+ ret = pf_handle_vf_restore_data(gt, vfid);
+ if (ret)
+ pf_enter_vf_restore_failed(gt, vfid);
+ else
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_PROCESS_DATA);
+
+ return true;
+}
+
+static void pf_exit_vf_restore_wait_data(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WAIT_DATA))
+ return;
+
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_PROCESS_DATA);
+ pf_queue_vf(gt, vfid);
+}
+
+static bool pf_enter_vf_restore_wip(struct xe_gt *gt, unsigned int vfid)
+{
+ if (pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WIP)) {
+ pf_enter_vf_wip(gt, vfid);
+ pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_PROCESS_DATA);
+ pf_queue_vf(gt, vfid);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * xe_gt_sriov_pf_control_check_restore_failed() - Check if restore processing has failed.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: true if restore processing failed, false otherwise.
+ */
+bool xe_gt_sriov_pf_control_check_restore_failed(struct xe_gt *gt, unsigned int vfid)
+{
+ return pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_FAILED);
+}
+
+/**
+ * xe_gt_sriov_pf_control_restore_data_done() - Indicate the end of VF migration data stream.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_restore_data_done(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_DATA_DONE)) {
+ pf_enter_vf_state_machine_bug(gt, vfid);
+ return -EIO;
+ }
+
+ return xe_gt_sriov_pf_control_process_restore_data(gt, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_control_process_restore_data() - Queue VF restore migration data processing.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_process_restore_data(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_expect_vf_not_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_FAILED)) {
+ xe_gt_sriov_pf_migration_ring_free(gt, vfid);
+ return -EIO;
+ }
+
+ pf_exit_vf_restore_wait_data(gt, vfid);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_trigger restore_vf() - Start an SR-IOV VF migration data restore sequence.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_trigger_restore_vf(struct xe_gt *gt, unsigned int vfid)
+{
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_STOPPED)) {
+ xe_gt_sriov_dbg(gt, "VF%u is stopped!\n", vfid);
+ return -EPERM;
+ }
+
+ if (!pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED)) {
+ xe_gt_sriov_dbg(gt, "VF%u is not paused!\n", vfid);
+ return -EPERM;
+ }
+
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WIP)) {
+ xe_gt_sriov_dbg(gt, "VF%u save is in progress!\n", vfid);
+ return -EBUSY;
+ }
+
+ if (!pf_enter_vf_restore_wip(gt, vfid)) {
+ xe_gt_sriov_dbg(gt, "VF%u restore already in progress!\n", vfid);
+ return -EALREADY;
+ }
+
+ return 0;
+}
+
+static int pf_wait_vf_restore_done(struct xe_gt *gt, unsigned int vfid)
+{
+ unsigned long timeout = pf_get_default_timeout(XE_GT_SRIOV_STATE_RESTORE_WIP);
+ int err;
+
+ err = pf_wait_vf_wip_done(gt, vfid, timeout);
+ if (err) {
+ xe_gt_sriov_notice(gt, "VF%u RESTORE didn't finish in %u ms (%pe)\n",
+ vfid, jiffies_to_msecs(timeout), ERR_PTR(err));
+ return err;
+ }
+
+ if (!pf_expect_vf_not_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_FAILED))
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_finish_restore_vf() - Complete a VF migration data restore sequence.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_finish_restore_vf(struct xe_gt *gt, unsigned int vfid)
+{
+ int ret;
+
+ ret = pf_wait_vf_restore_done(gt, vfid);
+ if (ret)
+ return ret;
+
+ if (!pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORED)) {
+ pf_enter_vf_mismatch(gt, vfid);
+ return -EIO;
+ }
+
+ pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED);
+
+ return 0;
+}
+
+/**
* DOC: The VF STOP state machine
*
* The VF STOP state machine looks like::
@@ -809,6 +1378,8 @@ static void pf_enter_vf_stopped(struct xe_gt *gt, unsigned int vfid)
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESUMED);
pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_PAUSED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVED);
+ pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORED);
pf_exit_vf_mismatch(gt, vfid);
pf_exit_vf_wip(gt, vfid);
}
@@ -896,7 +1467,7 @@ int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid)
return err;
if (pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_STOPPED)) {
- xe_gt_sriov_info(gt, "VF%u stopped!\n", vfid);
+ xe_gt_sriov_dbg(gt, "VF%u stopped!\n", vfid);
return 0;
}
@@ -934,6 +1505,10 @@ int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid)
* : v : | |
* : FLR_GUC_DONE : | |
* : | : | |
+ * : | o--<--sync : | |
+ * : |/ / : | |
+ * : FLR_SYNC--o : | |
+ * : | : | |
* : FLR_RESET_CONFIG---failed--->-----------o--------+-----------o
* : | : | |
* : FLR_RESET_DATA : | |
@@ -985,6 +1560,8 @@ static void pf_exit_vf_flr_wip(struct xe_gt *gt, unsigned int vfid)
pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_GUC_DONE);
pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_WAIT_GUC);
pf_escape_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SEND_START);
+
+ xe_sriov_pf_control_sync_flr(gt_to_xe(gt), vfid);
}
}
@@ -1141,12 +1718,38 @@ static bool pf_exit_vf_flr_send_start(struct xe_gt *gt, unsigned int vfid)
return true;
}
+static bool pf_exit_vf_flr_sync(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC))
+ return false;
+
+ pf_enter_vf_flr_reset_config(gt, vfid);
+ return true;
+}
+
+static void pf_enter_vf_flr_sync(struct xe_gt *gt, unsigned int vfid)
+{
+ int ret;
+
+ if (!pf_enter_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC))
+ pf_enter_vf_state_machine_bug(gt, vfid);
+
+ ret = xe_sriov_pf_control_sync_flr(gt_to_xe(gt), vfid);
+ if (ret < 0) {
+ xe_gt_sriov_dbg_verbose(gt, "FLR checkpoint %pe\n", ERR_PTR(ret));
+ pf_expect_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC);
+ } else {
+ xe_gt_sriov_dbg_verbose(gt, "FLR checkpoint pass\n");
+ pf_expect_vf_not_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC);
+ }
+}
+
static bool pf_exit_vf_flr_guc_done(struct xe_gt *gt, unsigned int vfid)
{
if (!pf_exit_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_GUC_DONE))
return false;
- pf_enter_vf_flr_reset_config(gt, vfid);
+ pf_enter_vf_flr_sync(gt, vfid);
return true;
}
@@ -1167,10 +1770,52 @@ static void pf_enter_vf_flr_guc_done(struct xe_gt *gt, unsigned int vfid)
*/
int xe_gt_sriov_pf_control_trigger_flr(struct xe_gt *gt, unsigned int vfid)
{
+ pf_enter_vf_flr_wip(gt, vfid);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_sync_flr() - Synchronize on the VF FLR checkpoint.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ * @sync: if true it will allow to exit the checkpoint
+ *
+ * Return: non-zero if FLR checkpoint has been reached, zero if the is no FLR
+ * in progress, or a negative error code on the FLR busy or failed.
+ */
+int xe_gt_sriov_pf_control_sync_flr(struct xe_gt *gt, unsigned int vfid, bool sync)
+{
+ if (sync && pf_exit_vf_flr_sync(gt, vfid))
+ return 1;
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_SYNC))
+ return 1;
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_WIP))
+ return -EBUSY;
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_FAILED))
+ return -EIO;
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_control_wait_flr() - Wait for a VF FLR to complete.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_control_wait_flr(struct xe_gt *gt, unsigned int vfid)
+{
unsigned long timeout = pf_get_default_timeout(XE_GT_SRIOV_STATE_FLR_WIP);
int err;
- pf_enter_vf_flr_wip(gt, vfid);
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_FAILED))
+ return -EIO;
+
+ if (!pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_FLR_WIP))
+ return 0;
err = pf_wait_vf_wip_done(gt, vfid, timeout);
if (err) {
@@ -1378,7 +2023,22 @@ static bool pf_process_vf_state_machine(struct xe_gt *gt, unsigned int vfid)
if (pf_exit_vf_pause_guc_done(gt, vfid))
return true;
- if (pf_exit_vf_pause_save_guc(gt, vfid))
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_SAVE_WAIT_DATA)) {
+ xe_gt_sriov_dbg_verbose(gt, "VF%u in %s\n", vfid,
+ control_bit_to_string(XE_GT_SRIOV_STATE_SAVE_WAIT_DATA));
+ return false;
+ }
+
+ if (pf_handle_vf_save(gt, vfid))
+ return true;
+
+ if (pf_check_vf_state(gt, vfid, XE_GT_SRIOV_STATE_RESTORE_WAIT_DATA)) {
+ xe_gt_sriov_dbg_verbose(gt, "VF%u in %s\n", vfid,
+ control_bit_to_string(XE_GT_SRIOV_STATE_RESTORE_WAIT_DATA));
+ return false;
+ }
+
+ if (pf_handle_vf_restore(gt, vfid))
return true;
if (pf_exit_vf_resume_send_resume(gt, vfid))
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
index c85e64f099cc..c36c8767f3ad 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control.h
@@ -16,8 +16,20 @@ void xe_gt_sriov_pf_control_restart(struct xe_gt *gt);
int xe_gt_sriov_pf_control_pause_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_resume_vf(struct xe_gt *gt, unsigned int vfid);
+bool xe_gt_sriov_pf_control_check_save_data_done(struct xe_gt *gt, unsigned int vfid);
+bool xe_gt_sriov_pf_control_check_save_failed(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_process_save_data(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_trigger_save_vf(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_finish_save_vf(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_restore_data_done(struct xe_gt *gt, unsigned int vfid);
+bool xe_gt_sriov_pf_control_check_restore_failed(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_process_restore_data(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_trigger_restore_vf(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_finish_restore_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_stop_vf(struct xe_gt *gt, unsigned int vfid);
int xe_gt_sriov_pf_control_trigger_flr(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_control_sync_flr(struct xe_gt *gt, unsigned int vfid, bool sync);
+int xe_gt_sriov_pf_control_wait_flr(struct xe_gt *gt, unsigned int vfid);
#ifdef CONFIG_PCI_IOV
int xe_gt_sriov_pf_control_process_guc2pf(struct xe_gt *gt, const u32 *msg, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h
index f02f941b4ad2..6027ba05a7f2 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_control_types.h
@@ -18,6 +18,7 @@
* @XE_GT_SRIOV_STATE_FLR_SEND_START: indicates that the PF wants to send a FLR START command.
* @XE_GT_SRIOV_STATE_FLR_WAIT_GUC: indicates that the PF awaits for a response from the GuC.
* @XE_GT_SRIOV_STATE_FLR_GUC_DONE: indicates that the PF has received a response from the GuC.
+ * @XE_GT_SRIOV_STATE_FLR_SYNC: indicates that the PF awaits to synchronize with other GuCs.
* @XE_GT_SRIOV_STATE_FLR_RESET_CONFIG: indicates that the PF needs to clear VF's resources.
* @XE_GT_SRIOV_STATE_FLR_RESET_DATA: indicates that the PF needs to clear VF's data.
* @XE_GT_SRIOV_STATE_FLR_RESET_MMIO: indicates that the PF needs to reset VF's registers.
@@ -27,9 +28,20 @@
* @XE_GT_SRIOV_STATE_PAUSE_SEND_PAUSE: indicates that the PF is about to send a PAUSE command.
* @XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC: indicates that the PF awaits for a response from the GuC.
* @XE_GT_SRIOV_STATE_PAUSE_GUC_DONE: indicates that the PF has received a response from the GuC.
- * @XE_GT_SRIOV_STATE_PAUSE_SAVE_GUC: indicates that the PF needs to save the VF GuC state.
* @XE_GT_SRIOV_STATE_PAUSE_FAILED: indicates that a VF pause operation has failed.
* @XE_GT_SRIOV_STATE_PAUSED: indicates that the VF is paused.
+ * @XE_GT_SRIOV_STATE_SAVE_WIP: indicates that VF save operation is in progress.
+ * @XE_GT_SRIOV_STATE_SAVE_PROCESS_DATA: indicates that VF migration data is being produced.
+ * @XE_GT_SRIOV_STATE_SAVE_WAIT_DATA: indicates that PF awaits for space in migration data ring.
+ * @XE_GT_SRIOV_STATE_SAVE_DATA_DONE: indicates that all migration data was produced by Xe.
+ * @XE_GT_SRIOV_STATE_SAVE_FAILED: indicates that VF save operation has failed.
+ * @XE_GT_SRIOV_STATE_SAVED: indicates that VF data is saved.
+ * @XE_GT_SRIOV_STATE_RESTORE_WIP: indicates that VF restore operation is in progress.
+ * @XE_GT_SRIOV_STATE_RESTORE_PROCESS_DATA: indicates that VF migration data is being consumed.
+ * @XE_GT_SRIOV_STATE_RESTORE_WAIT_DATA: indicates that PF awaits for data in migration data ring.
+ * @XE_GT_SRIOV_STATE_RESTORE_DATA_DONE: indicates that all migration data was produced by the user.
+ * @XE_GT_SRIOV_STATE_RESTORE_FAILED: indicates that VF restore operation has failed.
+ * @XE_GT_SRIOV_STATE_RESTORED: indicates that VF data is restored.
* @XE_GT_SRIOV_STATE_RESUME_WIP: indicates the a VF resume operation is in progress.
* @XE_GT_SRIOV_STATE_RESUME_SEND_RESUME: indicates that the PF is about to send RESUME command.
* @XE_GT_SRIOV_STATE_RESUME_FAILED: indicates that a VF resume operation has failed.
@@ -47,6 +59,7 @@ enum xe_gt_sriov_control_bits {
XE_GT_SRIOV_STATE_FLR_SEND_START,
XE_GT_SRIOV_STATE_FLR_WAIT_GUC,
XE_GT_SRIOV_STATE_FLR_GUC_DONE,
+ XE_GT_SRIOV_STATE_FLR_SYNC,
XE_GT_SRIOV_STATE_FLR_RESET_CONFIG,
XE_GT_SRIOV_STATE_FLR_RESET_DATA,
XE_GT_SRIOV_STATE_FLR_RESET_MMIO,
@@ -57,10 +70,23 @@ enum xe_gt_sriov_control_bits {
XE_GT_SRIOV_STATE_PAUSE_SEND_PAUSE,
XE_GT_SRIOV_STATE_PAUSE_WAIT_GUC,
XE_GT_SRIOV_STATE_PAUSE_GUC_DONE,
- XE_GT_SRIOV_STATE_PAUSE_SAVE_GUC,
XE_GT_SRIOV_STATE_PAUSE_FAILED,
XE_GT_SRIOV_STATE_PAUSED,
+ XE_GT_SRIOV_STATE_SAVE_WIP,
+ XE_GT_SRIOV_STATE_SAVE_PROCESS_DATA,
+ XE_GT_SRIOV_STATE_SAVE_WAIT_DATA,
+ XE_GT_SRIOV_STATE_SAVE_DATA_DONE,
+ XE_GT_SRIOV_STATE_SAVE_FAILED,
+ XE_GT_SRIOV_STATE_SAVED,
+
+ XE_GT_SRIOV_STATE_RESTORE_WIP,
+ XE_GT_SRIOV_STATE_RESTORE_PROCESS_DATA,
+ XE_GT_SRIOV_STATE_RESTORE_WAIT_DATA,
+ XE_GT_SRIOV_STATE_RESTORE_DATA_DONE,
+ XE_GT_SRIOV_STATE_RESTORE_FAILED,
+ XE_GT_SRIOV_STATE_RESTORED,
+
XE_GT_SRIOV_STATE_RESUME_WIP,
XE_GT_SRIOV_STATE_RESUME_SEND_RESUME,
XE_GT_SRIOV_STATE_RESUME_FAILED,
@@ -71,9 +97,11 @@ enum xe_gt_sriov_control_bits {
XE_GT_SRIOV_STATE_STOP_FAILED,
XE_GT_SRIOV_STATE_STOPPED,
- XE_GT_SRIOV_STATE_MISMATCH = BITS_PER_LONG - 1,
+ XE_GT_SRIOV_STATE_MISMATCH, /* always keep as last */
};
+#define XE_GT_SRIOV_NUM_STATES (XE_GT_SRIOV_STATE_MISMATCH + 1)
+
/**
* struct xe_gt_sriov_control_state - GT-level per-VF control state.
*
@@ -81,7 +109,7 @@ enum xe_gt_sriov_control_bits {
*/
struct xe_gt_sriov_control_state {
/** @state: VF state bits */
- unsigned long state;
+ DECLARE_BITMAP(state, XE_GT_SRIOV_NUM_STATES);
/** @done: completion of async operations */
struct completion done;
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
index 3ed245e04d0c..5278ea4fd655 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.c
@@ -23,14 +23,25 @@
#include "xe_gt_sriov_pf_service.h"
#include "xe_pm.h"
#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_provision.h"
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0 # d_inode->i_private = gt
- * │   ├── pf # d_inode->i_private = gt
- * │   ├── vf1 # d_inode->i_private = VFID(1)
- * :   :
- * │   ├── vfN # d_inode->i_private = VFID(N)
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1 # d_inode->i_private = (xe_gt*)
+ * │ │ ├── tile1
+ * │ │ │ :
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1 # d_inode->i_private = (xe_gt*)
+ * │ │ ├── tile1
+ * │ │ │ :
+ * : :
+ * │ ├── vfN # d_inode->i_private = VFID(N)
*/
static void *extract_priv(struct dentry *d)
@@ -40,26 +51,31 @@ static void *extract_priv(struct dentry *d)
static struct xe_gt *extract_gt(struct dentry *d)
{
- return extract_priv(d->d_parent);
+ return extract_priv(d);
+}
+
+static struct xe_device *extract_xe(struct dentry *d)
+{
+ return extract_priv(d->d_parent->d_parent->d_parent);
}
static unsigned int extract_vfid(struct dentry *d)
{
- return extract_priv(d) == extract_gt(d) ? PFID : (uintptr_t)extract_priv(d);
+ void *priv = extract_priv(d->d_parent->d_parent);
+
+ return priv == extract_xe(d) ? PFID : (uintptr_t)priv;
}
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── contexts_provisioned
- * │   │   ├── doorbells_provisioned
- * │   │   ├── runtime_registers
- * │   │   ├── negotiated_versions
- * │   │   ├── adverse_events
- * ├── gt1
- * │   ├── pf
- * │   │   ├── ...
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── contexts_provisioned
+ * ├── doorbells_provisioned
+ * ├── runtime_registers
+ * ├── adverse_events
*/
static const struct drm_info_list pf_info[] = {
@@ -86,48 +102,14 @@ static const struct drm_info_list pf_info[] = {
};
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── ggtt_available
- * │   │   ├── ggtt_provisioned
- */
-
-static const struct drm_info_list pf_ggtt_info[] = {
- {
- "ggtt_available",
- .show = xe_gt_debugfs_simple_show,
- .data = xe_gt_sriov_pf_config_print_available_ggtt,
- },
- {
- "ggtt_provisioned",
- .show = xe_gt_debugfs_simple_show,
- .data = xe_gt_sriov_pf_config_print_ggtt,
- },
-};
-
-/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── lmem_provisioned
- */
-
-static const struct drm_info_list pf_lmem_info[] = {
- {
- "lmem_provisioned",
- .show = xe_gt_debugfs_simple_show,
- .data = xe_gt_sriov_pf_config_print_lmem,
- },
-};
-
-/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── reset_engine
- * │   │   ├── sample_period
- * │   │   ├── sched_if_idle
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── reset_engine
+ * ├── sample_period
+ * ├── sched_if_idle
*/
#define DEFINE_SRIOV_GT_POLICY_DEBUGFS_ATTRIBUTE(POLICY, TYPE, FORMAT) \
@@ -143,6 +125,8 @@ static int POLICY##_set(void *data, u64 val) \
\
xe_pm_runtime_get(xe); \
err = xe_gt_sriov_pf_policy_set_##POLICY(gt, val); \
+ if (!err) \
+ xe_sriov_pf_provision_set_custom_mode(xe); \
xe_pm_runtime_put(xe); \
\
return err; \
@@ -173,24 +157,24 @@ static void pf_add_policy_attrs(struct xe_gt *gt, struct dentry *parent)
}
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── ggtt_spare
- * │   │   ├── lmem_spare
- * │   │   ├── doorbells_spare
- * │   │   ├── contexts_spare
- * │   │   ├── exec_quantum_ms
- * │   │   ├── preempt_timeout_us
- * │   │   ├── sched_priority
- * │   ├── vf1
- * │   │   ├── ggtt_quota
- * │   │   ├── lmem_quota
- * │   │   ├── doorbells_quota
- * │   │   ├── contexts_quota
- * │   │   ├── exec_quantum_ms
- * │   │   ├── preempt_timeout_us
- * │   │   ├── sched_priority
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * │ ├── tile0
+ * │ : ├── gt0
+ * │ : ├── doorbells_spare
+ * │ ├── contexts_spare
+ * │ ├── exec_quantum_ms
+ * │ ├── preempt_timeout_us
+ * │ ├── sched_priority
+ * ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── doorbells_quota
+ * ├── contexts_quota
+ * ├── exec_quantum_ms
+ * ├── preempt_timeout_us
+ * ├── sched_priority
*/
#define DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(CONFIG, TYPE, FORMAT) \
@@ -208,6 +192,8 @@ static int CONFIG##_set(void *data, u64 val) \
xe_pm_runtime_get(xe); \
err = xe_sriov_pf_wait_ready(xe) ?: \
xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \
+ if (!err) \
+ xe_sriov_pf_provision_set_custom_mode(xe); \
xe_pm_runtime_put(xe); \
\
return err; \
@@ -224,8 +210,6 @@ static int CONFIG##_get(void *data, u64 *val) \
\
DEFINE_DEBUGFS_ATTRIBUTE(CONFIG##_fops, CONFIG##_get, CONFIG##_set, FORMAT)
-DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, u64, "%llu\n");
-DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(lmem, u64, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(ctxs, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(dbs, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(exec_quantum, u32, "%llu\n");
@@ -233,22 +217,26 @@ DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(preempt_timeout, u32, "%llu\n");
DEFINE_SRIOV_GT_CONFIG_DEBUGFS_ATTRIBUTE(sched_priority, u32, "%llu\n");
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- * │   │   ├── threshold_cat_error_count
- * │   │   ├── threshold_doorbell_time_us
- * │   │   ├── threshold_engine_reset_count
- * │   │   ├── threshold_guc_time_us
- * │   │   ├── threshold_irq_time_us
- * │   │   ├── threshold_page_fault_count
- * │   ├── vf1
- * │   │   ├── threshold_cat_error_count
- * │   │   ├── threshold_doorbell_time_us
- * │   │   ├── threshold_engine_reset_count
- * │   │   ├── threshold_guc_time_us
- * │   │   ├── threshold_irq_time_us
- * │   │   ├── threshold_page_fault_count
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * │ ├── tile0
+ * │ : ├── gt0
+ * │ : ├── threshold_cat_error_count
+ * │ ├── threshold_doorbell_time_us
+ * │ ├── threshold_engine_reset_count
+ * │ ├── threshold_guc_time_us
+ * │ ├── threshold_irq_time_us
+ * │ ├── threshold_page_fault_count
+ * ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── threshold_cat_error_count
+ * ├── threshold_doorbell_time_us
+ * ├── threshold_engine_reset_count
+ * ├── threshold_guc_time_us
+ * ├── threshold_irq_time_us
+ * ├── threshold_page_fault_count
*/
static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index index)
@@ -263,6 +251,8 @@ static int set_threshold(void *data, u64 val, enum xe_guc_klv_threshold_index in
xe_pm_runtime_get(xe);
err = xe_gt_sriov_pf_config_set_threshold(gt, vfid, index, val);
+ if (!err)
+ xe_sriov_pf_provision_set_custom_mode(xe);
xe_pm_runtime_put(xe);
return err;
@@ -302,13 +292,6 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne
xe_gt_assert(gt, gt == extract_gt(parent));
xe_gt_assert(gt, vfid == extract_vfid(parent));
- if (xe_gt_is_main_type(gt)) {
- debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
- 0644, parent, parent, &ggtt_fops);
- if (xe_device_has_lmtt(gt_to_xe(gt)))
- debugfs_create_file_unsafe(vfid ? "lmem_quota" : "lmem_spare",
- 0644, parent, parent, &lmem_fops);
- }
debugfs_create_file_unsafe(vfid ? "doorbells_quota" : "doorbells_spare",
0644, parent, parent, &dbs_fops);
debugfs_create_file_unsafe(vfid ? "contexts_quota" : "contexts_spare",
@@ -329,10 +312,12 @@ static void pf_add_config_attrs(struct xe_gt *gt, struct dentry *parent, unsigne
}
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   │   ├── control { stop, pause, resume }
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── control { stop, pause, resume }
*/
static const struct {
@@ -342,9 +327,6 @@ static const struct {
{ "stop", xe_gt_sriov_pf_control_stop_vf },
{ "pause", xe_gt_sriov_pf_control_pause_vf },
{ "resume", xe_gt_sriov_pf_control_resume_vf },
-#ifdef CONFIG_DRM_XE_DEBUG_SRIOV
- { "restore!", xe_gt_sriov_pf_migration_restore_guc_state },
-#endif
};
static ssize_t control_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
@@ -409,58 +391,27 @@ static const struct file_operations control_ops = {
};
/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   │   ├── guc_state
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── vf1
+ * : ├── tile0
+ * : ├── gt0
+ * : ├── config_blob
*/
-static ssize_t guc_state_read(struct file *file, char __user *buf,
- size_t count, loff_t *pos)
-{
- struct dentry *dent = file_dentry(file);
- struct dentry *parent = dent->d_parent;
- struct xe_gt *gt = extract_gt(parent);
- unsigned int vfid = extract_vfid(parent);
- return xe_gt_sriov_pf_migration_read_guc_state(gt, vfid, buf, count, pos);
-}
-
-static ssize_t guc_state_write(struct file *file, const char __user *buf,
- size_t count, loff_t *pos)
-{
- struct dentry *dent = file_dentry(file);
- struct dentry *parent = dent->d_parent;
- struct xe_gt *gt = extract_gt(parent);
- unsigned int vfid = extract_vfid(parent);
-
- if (*pos)
- return -EINVAL;
-
- return xe_gt_sriov_pf_migration_write_guc_state(gt, vfid, buf, count);
-}
-
-static const struct file_operations guc_state_ops = {
- .owner = THIS_MODULE,
- .read = guc_state_read,
- .write = guc_state_write,
- .llseek = default_llseek,
+struct config_blob_data {
+ size_t size;
+ u8 blob[];
};
-/*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   │   ├── config_blob
- */
-static ssize_t config_blob_read(struct file *file, char __user *buf,
- size_t count, loff_t *pos)
+static int config_blob_open(struct inode *inode, struct file *file)
{
struct dentry *dent = file_dentry(file);
struct dentry *parent = dent->d_parent;
struct xe_gt *gt = extract_gt(parent);
unsigned int vfid = extract_vfid(parent);
+ struct config_blob_data *cbd;
ssize_t ret;
- void *tmp;
ret = xe_gt_sriov_pf_config_save(gt, vfid, NULL, 0);
if (!ret)
@@ -468,16 +419,27 @@ static ssize_t config_blob_read(struct file *file, char __user *buf,
if (ret < 0)
return ret;
- tmp = kzalloc(ret, GFP_KERNEL);
- if (!tmp)
+ cbd = kzalloc(struct_size(cbd, blob, ret), GFP_KERNEL);
+ if (!cbd)
return -ENOMEM;
- ret = xe_gt_sriov_pf_config_save(gt, vfid, tmp, ret);
- if (ret > 0)
- ret = simple_read_from_buffer(buf, count, pos, tmp, ret);
+ ret = xe_gt_sriov_pf_config_save(gt, vfid, cbd->blob, ret);
+ if (ret < 0) {
+ kfree(cbd);
+ return ret;
+ }
+
+ cbd->size = ret;
+ file->private_data = cbd;
+ return nonseekable_open(inode, file);
+}
- kfree(tmp);
- return ret;
+static ssize_t config_blob_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct config_blob_data *cbd = file->private_data;
+
+ return simple_read_from_buffer(buf, count, pos, cbd->blob, cbd->size);
}
static ssize_t config_blob_write(struct file *file, const char __user *buf,
@@ -514,80 +476,147 @@ static ssize_t config_blob_write(struct file *file, const char __user *buf,
return ret;
}
+static int config_blob_release(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+ return 0;
+}
+
static const struct file_operations config_blob_ops = {
.owner = THIS_MODULE,
+ .open = config_blob_open,
.read = config_blob_read,
.write = config_blob_write,
- .llseek = default_llseek,
+ .release = config_blob_release,
};
-/**
- * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
- * @gt: the &xe_gt to register
- * @root: the &dentry that represents the GT directory
- *
- * Register SR-IOV PF entries that are GT related and must be shown under GT debugfs.
- */
-void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root)
+static void pf_add_compat_attrs(struct xe_gt *gt, struct dentry *dent, unsigned int vfid)
{
struct xe_device *xe = gt_to_xe(gt);
- struct drm_minor *minor = xe->drm.primary;
- int n, totalvfs = xe_sriov_pf_get_totalvfs(xe);
- struct dentry *pfdentry;
- struct dentry *vfdentry;
- char buf[14]; /* should be enough up to "vf%u\0" for 2^32 - 1 */
-
- xe_gt_assert(gt, IS_SRIOV_PF(xe));
- xe_gt_assert(gt, root->d_inode->i_private == gt);
- /*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── pf
- */
- pfdentry = debugfs_create_dir("pf", root);
- if (IS_ERR(pfdentry))
+ if (!xe_gt_is_main_type(gt))
return;
- pfdentry->d_inode->i_private = gt;
-
- drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), pfdentry, minor);
- if (xe_gt_is_main_type(gt)) {
- drm_debugfs_create_files(pf_ggtt_info,
- ARRAY_SIZE(pf_ggtt_info),
- pfdentry, minor);
- if (xe_device_has_lmtt(gt_to_xe(gt)))
- drm_debugfs_create_files(pf_lmem_info,
- ARRAY_SIZE(pf_lmem_info),
- pfdentry, minor);
+
+ if (vfid) {
+ debugfs_create_symlink("ggtt_quota", dent, "../ggtt_quota");
+ if (xe_device_has_lmtt(xe))
+ debugfs_create_symlink("lmem_quota", dent, "../vram_quota");
+ } else {
+ debugfs_create_symlink("ggtt_spare", dent, "../ggtt_spare");
+ debugfs_create_symlink("ggtt_available", dent, "../ggtt_available");
+ debugfs_create_symlink("ggtt_provisioned", dent, "../ggtt_provisioned");
+ if (xe_device_has_lmtt(xe)) {
+ debugfs_create_symlink("lmem_spare", dent, "../vram_spare");
+ debugfs_create_symlink("lmem_provisioned", dent, "../vram_provisioned");
+ }
}
+}
- pf_add_policy_attrs(gt, pfdentry);
- pf_add_config_attrs(gt, pfdentry, PFID);
-
- for (n = 1; n <= totalvfs; n++) {
- /*
- * /sys/kernel/debug/dri/0/
- * ├── gt0
- * │   ├── vf1
- * │   ├── vf2
- */
- snprintf(buf, sizeof(buf), "vf%u", n);
- vfdentry = debugfs_create_dir(buf, root);
- if (IS_ERR(vfdentry))
- break;
- vfdentry->d_inode->i_private = (void *)(uintptr_t)n;
+static void pf_populate_gt(struct xe_gt *gt, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ struct drm_minor *minor = xe->drm.primary;
- pf_add_config_attrs(gt, vfdentry, VFID(n));
- debugfs_create_file("control", 0600, vfdentry, NULL, &control_ops);
+ if (vfid) {
+ pf_add_config_attrs(gt, dent, vfid);
+
+ debugfs_create_file("control", 0600, dent, NULL, &control_ops);
/* for testing/debugging purposes only! */
if (IS_ENABLED(CONFIG_DRM_XE_DEBUG)) {
- debugfs_create_file("guc_state",
- IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
- vfdentry, NULL, &guc_state_ops);
debugfs_create_file("config_blob",
IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) ? 0600 : 0400,
- vfdentry, NULL, &config_blob_ops);
+ dent, NULL, &config_blob_ops);
}
+
+ } else {
+ pf_add_config_attrs(gt, dent, PFID);
+ pf_add_policy_attrs(gt, dent);
+
+ drm_debugfs_create_files(pf_info, ARRAY_SIZE(pf_info), dent, minor);
}
+
+ /* for backward compatibility only */
+ pf_add_compat_attrs(gt, dent, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_debugfs_populate() - Create SR-IOV GT-level debugfs directories and files.
+ * @gt: the &xe_gt to register
+ * @parent: the parent &dentry that represents a &xe_tile
+ * @vfid: the VF identifier
+ *
+ * Add to the @parent directory new debugfs directory that will represent a @gt and
+ * populate it with GT files that are related to the SR-IOV @vfid function.
+ *
+ * This function can only be called on PF.
+ */
+void xe_gt_sriov_pf_debugfs_populate(struct xe_gt *gt, struct dentry *parent, unsigned int vfid)
+{
+ struct dentry *dent;
+ char name[8]; /* should be enough up to "gt%u\0" for 2^8 - 1 */
+
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, extract_priv(parent) == gt->tile);
+ xe_gt_assert(gt, extract_priv(parent->d_parent) == gt_to_xe(gt) ||
+ (uintptr_t)extract_priv(parent->d_parent) == vfid);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── pf
+ * │ │ ├── tile0 # parent
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1
+ * │ │ : :
+ * │ ├── vf1
+ * │ │ ├── tile0 # parent
+ * │ │ │ ├── gt0 # d_inode->i_private = (xe_gt*)
+ * │ │ │ ├── gt1
+ * │ : : :
+ */
+ snprintf(name, sizeof(name), "gt%u", gt->info.id);
+ dent = debugfs_create_dir(name, parent);
+ if (IS_ERR(dent))
+ return;
+ dent->d_inode->i_private = gt;
+
+ xe_gt_assert(gt, extract_gt(dent) == gt);
+ xe_gt_assert(gt, extract_vfid(dent) == vfid);
+
+ pf_populate_gt(gt, dent, vfid);
+}
+
+static void pf_add_links(struct xe_gt *gt, struct dentry *dent)
+{
+ unsigned int totalvfs = xe_gt_sriov_pf_get_totalvfs(gt);
+ unsigned int vfid;
+ char name[16]; /* should be more than enough for "vf%u\0" and VFID(UINT_MAX) */
+ char symlink[64]; /* should be more enough for "../../sriov/vf%u/tile%u/gt%u\0" */
+
+ for (vfid = 0; vfid <= totalvfs; vfid++) {
+ if (vfid)
+ snprintf(name, sizeof(name), "vf%u", vfid);
+ else
+ snprintf(name, sizeof(name), "pf");
+ snprintf(symlink, sizeof(symlink), "../../sriov/%s/tile%u/gt%u",
+ name, gt->tile->id, gt->info.id);
+ debugfs_create_symlink(name, dent, symlink);
+ }
+}
+
+/**
+ * xe_gt_sriov_pf_debugfs_register - Register SR-IOV PF specific entries in GT debugfs.
+ * @gt: the &xe_gt to register
+ * @dent: the &dentry that represents the GT directory
+ *
+ * Instead of actual files, create symlinks for PF and each VF to their GT specific
+ * attributes that should be already exposed in the dedicated debugfs SR-IOV tree.
+ */
+void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *dent)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, dent->d_inode->i_private == gt);
+
+ pf_add_links(gt, dent);
}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
index 038cc8ddc244..82ff3b7f0532 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_debugfs.h
@@ -11,6 +11,7 @@ struct dentry;
#ifdef CONFIG_PCI_IOV
void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root);
+void xe_gt_sriov_pf_debugfs_populate(struct xe_gt *gt, struct dentry *parent, unsigned int vfid);
#else
static inline void xe_gt_sriov_pf_debugfs_register(struct xe_gt *gt, struct dentry *root) { }
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c
index 44cc612b0a75..3174a8dee779 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.c
@@ -5,14 +5,150 @@
#include <drm/drm_managed.h>
+#include "regs/xe_guc_regs.h"
+
#include "abi/guc_actions_sriov_abi.h"
#include "xe_bo.h"
+#include "xe_ggtt.h"
+#include "xe_gt.h"
+#include "xe_gt_sriov_pf.h"
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_gt_sriov_pf_control.h"
#include "xe_gt_sriov_pf_helpers.h"
#include "xe_gt_sriov_pf_migration.h"
#include "xe_gt_sriov_printk.h"
#include "xe_guc.h"
+#include "xe_guc_buf.h"
#include "xe_guc_ct.h"
+#include "xe_migrate.h"
+#include "xe_mmio.h"
#include "xe_sriov.h"
+#include "xe_sriov_packet.h"
+#include "xe_sriov_packet_types.h"
+#include "xe_sriov_pf_migration.h"
+
+#define XE_GT_SRIOV_PF_MIGRATION_RING_SIZE 5
+
+static struct xe_gt_sriov_migration_data *pf_pick_gt_migration(struct xe_gt *gt, unsigned int vfid)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid != PFID);
+ xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
+
+ return &gt->sriov.pf.vfs[vfid].migration;
+}
+
+static void pf_dump_mig_data(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data,
+ const char *what)
+{
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
+ struct drm_printer p = xe_gt_dbg_printer(gt);
+
+ drm_printf(&p, "VF%u %s (%llu bytes)\n", vfid, what, data->hdr.size);
+ drm_print_hex_dump(&p, "mig_hdr: ", (void *)&data->hdr, sizeof(data->hdr));
+ drm_print_hex_dump(&p, "mig_data: ", data->vaddr, min(SZ_64, data->hdr.size));
+ }
+}
+
+static ssize_t pf_migration_ggtt_size(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!xe_gt_is_main_type(gt))
+ return 0;
+
+ return xe_gt_sriov_pf_config_ggtt_save(gt, vfid, NULL, 0);
+}
+
+static int pf_save_vf_ggtt_mig_data(struct xe_gt *gt, unsigned int vfid)
+{
+ struct xe_sriov_packet *data;
+ size_t size;
+ int ret;
+
+ size = pf_migration_ggtt_size(gt, vfid);
+ xe_gt_assert(gt, size);
+
+ data = xe_sriov_packet_alloc(gt_to_xe(gt));
+ if (!data)
+ return -ENOMEM;
+
+ ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id,
+ XE_SRIOV_PACKET_TYPE_GGTT, 0, size);
+ if (ret)
+ goto fail;
+
+ ret = xe_gt_sriov_pf_config_ggtt_save(gt, vfid, data->vaddr, size);
+ if (ret)
+ goto fail;
+
+ pf_dump_mig_data(gt, vfid, data, "GGTT data save");
+
+ ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data);
+ if (ret)
+ goto fail;
+
+ return 0;
+
+fail:
+ xe_sriov_packet_free(data);
+ xe_gt_sriov_err(gt, "Failed to save VF%u GGTT data (%pe)\n", vfid, ERR_PTR(ret));
+ return ret;
+}
+
+static int pf_restore_vf_ggtt_mig_data(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ int ret;
+
+ pf_dump_mig_data(gt, vfid, data, "GGTT data restore");
+
+ ret = xe_gt_sriov_pf_config_ggtt_restore(gt, vfid, data->vaddr, data->hdr.size);
+ if (ret) {
+ xe_gt_sriov_err(gt, "Failed to restore VF%u GGTT data (%pe)\n",
+ vfid, ERR_PTR(ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_migration_ggtt_save() - Save VF GGTT migration data.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_ggtt_save(struct xe_gt *gt, unsigned int vfid)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid != PFID);
+ xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
+
+ return pf_save_vf_ggtt_mig_data(gt, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_migration_ggtt_restore() - Restore VF GGTT migration data.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ * @data: the &xe_sriov_packet containing migration data
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_ggtt_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid != PFID);
+ xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
+
+ return pf_restore_vf_ggtt_mig_data(gt, vfid, data);
+}
/* Return: number of dwords saved/restored/required or a negative error code on failure */
static int guc_action_vf_save_restore(struct xe_guc *guc, u32 vfid, u32 opcode,
@@ -33,7 +169,7 @@ static int guc_action_vf_save_restore(struct xe_guc *guc, u32 vfid, u32 opcode,
}
/* Return: size of the state in dwords or a negative error code on failure */
-static int pf_send_guc_query_vf_state_size(struct xe_gt *gt, unsigned int vfid)
+static int pf_send_guc_query_vf_mig_data_size(struct xe_gt *gt, unsigned int vfid)
{
int ret;
@@ -42,353 +178,856 @@ static int pf_send_guc_query_vf_state_size(struct xe_gt *gt, unsigned int vfid)
}
/* Return: number of state dwords saved or a negative error code on failure */
-static int pf_send_guc_save_vf_state(struct xe_gt *gt, unsigned int vfid,
- void *buff, size_t size)
+static int pf_send_guc_save_vf_mig_data(struct xe_gt *gt, unsigned int vfid,
+ void *dst, size_t size)
{
const int ndwords = size / sizeof(u32);
- struct xe_tile *tile = gt_to_tile(gt);
- struct xe_device *xe = tile_to_xe(tile);
struct xe_guc *guc = &gt->uc.guc;
- struct xe_bo *bo;
+ CLASS(xe_guc_buf, buf)(&guc->buf, ndwords);
int ret;
xe_gt_assert(gt, size % sizeof(u32) == 0);
xe_gt_assert(gt, size == ndwords * sizeof(u32));
- bo = xe_bo_create_pin_map_novm(xe, tile,
- ALIGN(size, PAGE_SIZE),
- ttm_bo_type_kernel,
- XE_BO_FLAG_SYSTEM |
- XE_BO_FLAG_GGTT |
- XE_BO_FLAG_GGTT_INVALIDATE, false);
- if (IS_ERR(bo))
- return PTR_ERR(bo);
+ if (!xe_guc_buf_is_valid(buf))
+ return -ENOBUFS;
+
+ /* FW expects this buffer to be zero-initialized */
+ memset(xe_guc_buf_cpu_ptr(buf), 0, size);
ret = guc_action_vf_save_restore(guc, vfid, GUC_PF_OPCODE_VF_SAVE,
- xe_bo_ggtt_addr(bo), ndwords);
+ xe_guc_buf_flush(buf), ndwords);
if (!ret)
ret = -ENODATA;
else if (ret > ndwords)
ret = -EPROTO;
else if (ret > 0)
- xe_map_memcpy_from(xe, buff, &bo->vmap, 0, ret * sizeof(u32));
+ memcpy(dst, xe_guc_buf_sync_read(buf), ret * sizeof(u32));
- xe_bo_unpin_map_no_vm(bo);
return ret;
}
/* Return: number of state dwords restored or a negative error code on failure */
-static int pf_send_guc_restore_vf_state(struct xe_gt *gt, unsigned int vfid,
- const void *buff, size_t size)
+static int pf_send_guc_restore_vf_mig_data(struct xe_gt *gt, unsigned int vfid,
+ const void *src, size_t size)
{
const int ndwords = size / sizeof(u32);
- struct xe_tile *tile = gt_to_tile(gt);
- struct xe_device *xe = tile_to_xe(tile);
struct xe_guc *guc = &gt->uc.guc;
- struct xe_bo *bo;
+ CLASS(xe_guc_buf_from_data, buf)(&guc->buf, src, size);
int ret;
xe_gt_assert(gt, size % sizeof(u32) == 0);
xe_gt_assert(gt, size == ndwords * sizeof(u32));
- bo = xe_bo_create_pin_map_novm(xe, tile,
- ALIGN(size, PAGE_SIZE),
- ttm_bo_type_kernel,
- XE_BO_FLAG_SYSTEM |
- XE_BO_FLAG_GGTT |
- XE_BO_FLAG_GGTT_INVALIDATE, false);
- if (IS_ERR(bo))
- return PTR_ERR(bo);
-
- xe_map_memcpy_to(xe, &bo->vmap, 0, buff, size);
+ if (!xe_guc_buf_is_valid(buf))
+ return -ENOBUFS;
ret = guc_action_vf_save_restore(guc, vfid, GUC_PF_OPCODE_VF_RESTORE,
- xe_bo_ggtt_addr(bo), ndwords);
+ xe_guc_buf_flush(buf), ndwords);
if (!ret)
ret = -ENODATA;
else if (ret > ndwords)
ret = -EPROTO;
- xe_bo_unpin_map_no_vm(bo);
return ret;
}
static bool pf_migration_supported(struct xe_gt *gt)
{
- xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
- return gt->sriov.pf.migration.supported;
+ return xe_sriov_pf_migration_supported(gt_to_xe(gt));
}
-static struct mutex *pf_migration_mutex(struct xe_gt *gt)
+static int pf_save_vf_guc_mig_data(struct xe_gt *gt, unsigned int vfid)
{
- xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
- return &gt->sriov.pf.migration.snapshot_lock;
+ struct xe_sriov_packet *data;
+ size_t size;
+ int ret;
+
+ ret = pf_send_guc_query_vf_mig_data_size(gt, vfid);
+ if (ret < 0)
+ goto fail;
+
+ size = ret * sizeof(u32);
+
+ data = xe_sriov_packet_alloc(gt_to_xe(gt));
+ if (!data) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id,
+ XE_SRIOV_PACKET_TYPE_GUC, 0, size);
+ if (ret)
+ goto fail_free;
+
+ ret = pf_send_guc_save_vf_mig_data(gt, vfid, data->vaddr, size);
+ if (ret < 0)
+ goto fail_free;
+ size = ret * sizeof(u32);
+ xe_gt_assert(gt, size);
+ xe_gt_assert(gt, size <= data->hdr.size);
+ data->hdr.size = size;
+ data->remaining = size;
+
+ pf_dump_mig_data(gt, vfid, data, "GuC data save");
+
+ ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data);
+ if (ret)
+ goto fail_free;
+
+ return 0;
+
+fail_free:
+ xe_sriov_packet_free(data);
+fail:
+ xe_gt_sriov_err(gt, "Failed to save VF%u GuC data (%pe)\n",
+ vfid, ERR_PTR(ret));
+ return ret;
}
-static struct xe_gt_sriov_state_snapshot *pf_pick_vf_snapshot(struct xe_gt *gt,
- unsigned int vfid)
+static ssize_t pf_migration_guc_size(struct xe_gt *gt, unsigned int vfid)
+{
+ ssize_t size;
+
+ if (!pf_migration_supported(gt))
+ return -ENOPKG;
+
+ size = pf_send_guc_query_vf_mig_data_size(gt, vfid);
+ if (size >= 0)
+ size *= sizeof(u32);
+
+ return size;
+}
+
+/**
+ * xe_gt_sriov_pf_migration_guc_save() - Save VF GuC migration data.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_guc_save(struct xe_gt *gt, unsigned int vfid)
{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
- lockdep_assert_held(pf_migration_mutex(gt));
- return &gt->sriov.pf.vfs[vfid].snapshot;
+ if (!pf_migration_supported(gt))
+ return -ENOPKG;
+
+ return pf_save_vf_guc_mig_data(gt, vfid);
}
-static unsigned int pf_snapshot_index(struct xe_gt *gt, struct xe_gt_sriov_state_snapshot *snapshot)
+static int pf_restore_vf_guc_state(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
{
- return container_of(snapshot, struct xe_gt_sriov_metadata, snapshot) - gt->sriov.pf.vfs;
+ int ret;
+
+ xe_gt_assert(gt, data->hdr.size);
+
+ pf_dump_mig_data(gt, vfid, data, "GuC data restore");
+
+ ret = pf_send_guc_restore_vf_mig_data(gt, vfid, data->vaddr, data->hdr.size);
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ xe_gt_sriov_err(gt, "Failed to restore VF%u GuC data (%pe)\n",
+ vfid, ERR_PTR(ret));
+ return ret;
}
-static void pf_free_guc_state(struct xe_gt *gt, struct xe_gt_sriov_state_snapshot *snapshot)
+/**
+ * xe_gt_sriov_pf_migration_guc_restore() - Restore VF GuC migration data.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ * @data: the &xe_sriov_packet containing migration data
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_guc_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
{
- struct xe_device *xe = gt_to_xe(gt);
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid != PFID);
+ xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
- drmm_kfree(&xe->drm, snapshot->guc.buff);
- snapshot->guc.buff = NULL;
- snapshot->guc.size = 0;
+ if (!pf_migration_supported(gt))
+ return -ENOPKG;
+
+ return pf_restore_vf_guc_state(gt, vfid, data);
}
-static int pf_alloc_guc_state(struct xe_gt *gt,
- struct xe_gt_sriov_state_snapshot *snapshot,
- size_t size)
+static ssize_t pf_migration_mmio_size(struct xe_gt *gt, unsigned int vfid)
{
- struct xe_device *xe = gt_to_xe(gt);
- void *p;
-
- pf_free_guc_state(gt, snapshot);
+ if (xe_gt_is_media_type(gt))
+ return MED_VF_SW_FLAG_COUNT * sizeof(u32);
+ else
+ return VF_SW_FLAG_COUNT * sizeof(u32);
+}
- if (!size)
- return -ENODATA;
+static int pf_migration_mmio_save(struct xe_gt *gt, unsigned int vfid, void *buf, size_t size)
+{
+ struct xe_mmio mmio;
+ u32 *regs = buf;
+ int n;
- if (size % sizeof(u32))
+ if (size != pf_migration_mmio_size(gt, vfid))
return -EINVAL;
- if (size > SZ_2M)
- return -EFBIG;
+ xe_mmio_init_vf_view(&mmio, &gt->mmio, vfid);
- p = drmm_kzalloc(&xe->drm, size, GFP_KERNEL);
- if (!p)
- return -ENOMEM;
+ if (xe_gt_is_media_type(gt))
+ for (n = 0; n < MED_VF_SW_FLAG_COUNT; n++)
+ regs[n] = xe_mmio_read32(&gt->mmio, MED_VF_SW_FLAG(n));
+ else
+ for (n = 0; n < VF_SW_FLAG_COUNT; n++)
+ regs[n] = xe_mmio_read32(&gt->mmio, VF_SW_FLAG(n));
- snapshot->guc.buff = p;
- snapshot->guc.size = size;
return 0;
}
-static void pf_dump_guc_state(struct xe_gt *gt, struct xe_gt_sriov_state_snapshot *snapshot)
+static int pf_migration_mmio_restore(struct xe_gt *gt, unsigned int vfid,
+ const void *buf, size_t size)
{
- if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV)) {
- unsigned int vfid __maybe_unused = pf_snapshot_index(gt, snapshot);
+ const u32 *regs = buf;
+ struct xe_mmio mmio;
+ int n;
- xe_gt_sriov_dbg_verbose(gt, "VF%u GuC state is %zu dwords:\n",
- vfid, snapshot->guc.size / sizeof(u32));
- print_hex_dump_bytes("state: ", DUMP_PREFIX_OFFSET,
- snapshot->guc.buff, min(SZ_64, snapshot->guc.size));
- }
+ if (size != pf_migration_mmio_size(gt, vfid))
+ return -EINVAL;
+
+ xe_mmio_init_vf_view(&mmio, &gt->mmio, vfid);
+
+ if (xe_gt_is_media_type(gt))
+ for (n = 0; n < MED_VF_SW_FLAG_COUNT; n++)
+ xe_mmio_write32(&gt->mmio, MED_VF_SW_FLAG(n), regs[n]);
+ else
+ for (n = 0; n < VF_SW_FLAG_COUNT; n++)
+ xe_mmio_write32(&gt->mmio, VF_SW_FLAG(n), regs[n]);
+
+ return 0;
}
-static int pf_save_vf_guc_state(struct xe_gt *gt, unsigned int vfid)
+static int pf_save_vf_mmio_mig_data(struct xe_gt *gt, unsigned int vfid)
{
- struct xe_gt_sriov_state_snapshot *snapshot = pf_pick_vf_snapshot(gt, vfid);
+ struct xe_sriov_packet *data;
size_t size;
int ret;
- ret = pf_send_guc_query_vf_state_size(gt, vfid);
- if (ret < 0)
+ size = pf_migration_mmio_size(gt, vfid);
+ xe_gt_assert(gt, size);
+
+ data = xe_sriov_packet_alloc(gt_to_xe(gt));
+ if (!data)
+ return -ENOMEM;
+
+ ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id,
+ XE_SRIOV_PACKET_TYPE_MMIO, 0, size);
+ if (ret)
goto fail;
- size = ret * sizeof(u32);
- xe_gt_sriov_dbg_verbose(gt, "VF%u state size is %d dwords (%zu bytes)\n", vfid, ret, size);
- ret = pf_alloc_guc_state(gt, snapshot, size);
- if (ret < 0)
+ ret = pf_migration_mmio_save(gt, vfid, data->vaddr, size);
+ if (ret)
goto fail;
- ret = pf_send_guc_save_vf_state(gt, vfid, snapshot->guc.buff, size);
- if (ret < 0)
+ pf_dump_mig_data(gt, vfid, data, "MMIO data save");
+
+ ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data);
+ if (ret)
goto fail;
- size = ret * sizeof(u32);
- xe_gt_assert(gt, size);
- xe_gt_assert(gt, size <= snapshot->guc.size);
- snapshot->guc.size = size;
- pf_dump_guc_state(gt, snapshot);
return 0;
fail:
- xe_gt_sriov_dbg(gt, "Unable to save VF%u state (%pe)\n", vfid, ERR_PTR(ret));
- pf_free_guc_state(gt, snapshot);
+ xe_sriov_packet_free(data);
+ xe_gt_sriov_err(gt, "Failed to save VF%u MMIO data (%pe)\n", vfid, ERR_PTR(ret));
return ret;
}
+static int pf_restore_vf_mmio_mig_data(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ int ret;
+
+ pf_dump_mig_data(gt, vfid, data, "MMIO data restore");
+
+ ret = pf_migration_mmio_restore(gt, vfid, data->vaddr, data->hdr.size);
+ if (ret) {
+ xe_gt_sriov_err(gt, "Failed to restore VF%u MMIO data (%pe)\n",
+ vfid, ERR_PTR(ret));
+
+ return ret;
+ }
+
+ return 0;
+}
+
/**
- * xe_gt_sriov_pf_migration_save_guc_state() - Take a GuC VF state snapshot.
+ * xe_gt_sriov_pf_migration_mmio_save() - Save VF MMIO migration data.
* @gt: the &xe_gt
- * @vfid: the VF identifier
+ * @vfid: the VF identifier (can't be 0)
*
* This function is for PF only.
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_gt_sriov_pf_migration_save_guc_state(struct xe_gt *gt, unsigned int vfid)
+int xe_gt_sriov_pf_migration_mmio_save(struct xe_gt *gt, unsigned int vfid)
{
- int err;
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid != PFID);
+ xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
+ return pf_save_vf_mmio_mig_data(gt, vfid);
+}
+
+/**
+ * xe_gt_sriov_pf_migration_mmio_restore() - Restore VF MMIO migration data.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ * @data: the &xe_sriov_packet containing migration data
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_mmio_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
- if (!pf_migration_supported(gt))
- return -ENOPKG;
+ return pf_restore_vf_mmio_mig_data(gt, vfid, data);
+}
- mutex_lock(pf_migration_mutex(gt));
- err = pf_save_vf_guc_state(gt, vfid);
- mutex_unlock(pf_migration_mutex(gt));
+static ssize_t pf_migration_vram_size(struct xe_gt *gt, unsigned int vfid)
+{
+ if (!xe_gt_is_main_type(gt))
+ return 0;
- return err;
+ return xe_gt_sriov_pf_config_get_lmem(gt, vfid);
+}
+
+static struct dma_fence *__pf_save_restore_vram(struct xe_gt *gt, unsigned int vfid,
+ struct xe_bo *vram, u64 vram_offset,
+ struct xe_bo *sysmem, u64 sysmem_offset,
+ size_t size, bool save)
+{
+ struct dma_fence *ret = NULL;
+ struct drm_exec exec;
+ int err;
+
+ drm_exec_init(&exec, 0, 0);
+ drm_exec_until_all_locked(&exec) {
+ err = drm_exec_lock_obj(&exec, &vram->ttm.base);
+ drm_exec_retry_on_contention(&exec);
+ if (err) {
+ ret = ERR_PTR(err);
+ goto err;
+ }
+
+ err = drm_exec_lock_obj(&exec, &sysmem->ttm.base);
+ drm_exec_retry_on_contention(&exec);
+ if (err) {
+ ret = ERR_PTR(err);
+ goto err;
+ }
+ }
+
+ ret = xe_migrate_vram_copy_chunk(vram, vram_offset, sysmem, sysmem_offset, size,
+ save ? XE_MIGRATE_COPY_TO_SRAM : XE_MIGRATE_COPY_TO_VRAM);
+
+err:
+ drm_exec_fini(&exec);
+
+ return ret;
}
-static int pf_restore_vf_guc_state(struct xe_gt *gt, unsigned int vfid)
+#define PF_VRAM_SAVE_RESTORE_TIMEOUT (5 * HZ)
+static int pf_save_vram_chunk(struct xe_gt *gt, unsigned int vfid,
+ struct xe_bo *src_vram, u64 src_vram_offset,
+ size_t size)
{
- struct xe_gt_sriov_state_snapshot *snapshot = pf_pick_vf_snapshot(gt, vfid);
+ struct xe_sriov_packet *data;
+ struct dma_fence *fence;
int ret;
- if (!snapshot->guc.size)
- return -ENODATA;
+ data = xe_sriov_packet_alloc(gt_to_xe(gt));
+ if (!data)
+ return -ENOMEM;
- xe_gt_sriov_dbg_verbose(gt, "restoring %zu dwords of VF%u GuC state\n",
- snapshot->guc.size / sizeof(u32), vfid);
- ret = pf_send_guc_restore_vf_state(gt, vfid, snapshot->guc.buff, snapshot->guc.size);
- if (ret < 0)
+ ret = xe_sriov_packet_init(data, gt->tile->id, gt->info.id,
+ XE_SRIOV_PACKET_TYPE_VRAM, src_vram_offset,
+ size);
+ if (ret)
+ goto fail;
+
+ fence = __pf_save_restore_vram(gt, vfid,
+ src_vram, src_vram_offset,
+ data->bo, 0, size, true);
+ if (IS_ERR(fence)) {
+ ret = PTR_ERR(fence);
+ goto fail;
+ }
+
+ ret = dma_fence_wait_timeout(fence, false, PF_VRAM_SAVE_RESTORE_TIMEOUT);
+ dma_fence_put(fence);
+ if (!ret) {
+ ret = -ETIME;
+ goto fail;
+ }
+
+ pf_dump_mig_data(gt, vfid, data, "VRAM data save");
+
+ ret = xe_gt_sriov_pf_migration_save_produce(gt, vfid, data);
+ if (ret)
goto fail;
- xe_gt_sriov_dbg_verbose(gt, "restored %d dwords of VF%u GuC state\n", ret, vfid);
return 0;
fail:
- xe_gt_sriov_dbg(gt, "Failed to restore VF%u GuC state (%pe)\n", vfid, ERR_PTR(ret));
+ xe_sriov_packet_free(data);
+ return ret;
+}
+
+#define VF_VRAM_STATE_CHUNK_MAX_SIZE SZ_512M
+static int pf_save_vf_vram_mig_data(struct xe_gt *gt, unsigned int vfid)
+{
+ struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid);
+ loff_t *offset = &migration->save.vram_offset;
+ struct xe_bo *vram;
+ size_t vram_size, chunk_size;
+ int ret;
+
+ vram = xe_gt_sriov_pf_config_get_lmem_obj(gt, vfid);
+ if (!vram)
+ return -ENXIO;
+
+ vram_size = xe_bo_size(vram);
+
+ xe_gt_assert(gt, *offset < vram_size);
+
+ chunk_size = min(vram_size - *offset, VF_VRAM_STATE_CHUNK_MAX_SIZE);
+
+ ret = pf_save_vram_chunk(gt, vfid, vram, *offset, chunk_size);
+ if (ret)
+ goto fail;
+
+ *offset += chunk_size;
+
+ xe_bo_put(vram);
+
+ if (*offset < vram_size)
+ return -EAGAIN;
+
+ return 0;
+
+fail:
+ xe_bo_put(vram);
+ xe_gt_sriov_err(gt, "Failed to save VF%u VRAM data (%pe)\n", vfid, ERR_PTR(ret));
+ return ret;
+}
+
+static int pf_restore_vf_vram_mig_data(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ u64 end = data->hdr.offset + data->hdr.size;
+ struct dma_fence *fence;
+ struct xe_bo *vram;
+ size_t size;
+ int ret = 0;
+
+ vram = xe_gt_sriov_pf_config_get_lmem_obj(gt, vfid);
+ if (!vram)
+ return -ENXIO;
+
+ size = xe_bo_size(vram);
+
+ if (end > size || end < data->hdr.size) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pf_dump_mig_data(gt, vfid, data, "VRAM data restore");
+
+ fence = __pf_save_restore_vram(gt, vfid, vram, data->hdr.offset,
+ data->bo, 0, data->hdr.size, false);
+ if (IS_ERR(fence)) {
+ ret = PTR_ERR(fence);
+ goto err;
+ }
+
+ ret = dma_fence_wait_timeout(fence, false, PF_VRAM_SAVE_RESTORE_TIMEOUT);
+ dma_fence_put(fence);
+ if (!ret) {
+ ret = -ETIME;
+ goto err;
+ }
+
+ xe_bo_put(vram);
+
+ return 0;
+err:
+ xe_bo_put(vram);
+ xe_gt_sriov_err(gt, "Failed to restore VF%u VRAM data (%pe)\n", vfid, ERR_PTR(ret));
return ret;
}
/**
- * xe_gt_sriov_pf_migration_restore_guc_state() - Restore a GuC VF state.
+ * xe_gt_sriov_pf_migration_vram_save() - Save VF VRAM migration data.
* @gt: the &xe_gt
- * @vfid: the VF identifier
+ * @vfid: the VF identifier (can't be 0)
*
* This function is for PF only.
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_gt_sriov_pf_migration_restore_guc_state(struct xe_gt *gt, unsigned int vfid)
+int xe_gt_sriov_pf_migration_vram_save(struct xe_gt *gt, unsigned int vfid)
{
- int ret;
-
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
- if (!pf_migration_supported(gt))
- return -ENOPKG;
+ return pf_save_vf_vram_mig_data(gt, vfid);
+}
- mutex_lock(pf_migration_mutex(gt));
- ret = pf_restore_vf_guc_state(gt, vfid);
- mutex_unlock(pf_migration_mutex(gt));
+/**
+ * xe_gt_sriov_pf_migration_vram_restore() - Restore VF VRAM migration data.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ * @data: the &xe_sriov_packet containing migration data
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_vram_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
+ xe_gt_assert(gt, vfid != PFID);
+ xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
- return ret;
+ return pf_restore_vf_vram_mig_data(gt, vfid, data);
}
-#ifdef CONFIG_DEBUG_FS
/**
- * xe_gt_sriov_pf_migration_read_guc_state() - Read a GuC VF state.
+ * xe_gt_sriov_pf_migration_size() - Total size of migration data from all components within a GT.
* @gt: the &xe_gt
- * @vfid: the VF identifier
- * @buf: the user space buffer to read to
- * @count: the maximum number of bytes to read
- * @pos: the current position in the buffer
+ * @vfid: the VF identifier (can't be 0)
*
* This function is for PF only.
*
- * This function reads up to @count bytes from the saved VF GuC state buffer
- * at offset @pos into the user space address starting at @buf.
- *
- * Return: the number of bytes read or a negative error code on failure.
+ * Return: total migration data size in bytes or a negative error code on failure.
*/
-ssize_t xe_gt_sriov_pf_migration_read_guc_state(struct xe_gt *gt, unsigned int vfid,
- char __user *buf, size_t count, loff_t *pos)
+ssize_t xe_gt_sriov_pf_migration_size(struct xe_gt *gt, unsigned int vfid)
{
- struct xe_gt_sriov_state_snapshot *snapshot;
- ssize_t ret;
+ ssize_t total = 0;
+ ssize_t size;
xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
xe_gt_assert(gt, vfid != PFID);
xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
- if (!pf_migration_supported(gt))
- return -ENOPKG;
+ size = pf_migration_guc_size(gt, vfid);
+ if (size < 0)
+ return size;
+ if (size > 0)
+ size += sizeof(struct xe_sriov_packet_hdr);
+ total += size;
+
+ size = pf_migration_ggtt_size(gt, vfid);
+ if (size < 0)
+ return size;
+ if (size > 0)
+ size += sizeof(struct xe_sriov_packet_hdr);
+ total += size;
+
+ size = pf_migration_mmio_size(gt, vfid);
+ if (size < 0)
+ return size;
+ if (size > 0)
+ size += sizeof(struct xe_sriov_packet_hdr);
+ total += size;
+
+ size = pf_migration_vram_size(gt, vfid);
+ if (size < 0)
+ return size;
+ if (size > 0)
+ size += sizeof(struct xe_sriov_packet_hdr);
+ total += size;
+
+ return total;
+}
- mutex_lock(pf_migration_mutex(gt));
- snapshot = pf_pick_vf_snapshot(gt, vfid);
- if (snapshot->guc.size)
- ret = simple_read_from_buffer(buf, count, pos, snapshot->guc.buff,
- snapshot->guc.size);
- else
- ret = -ENODATA;
- mutex_unlock(pf_migration_mutex(gt));
+/**
+ * xe_gt_sriov_pf_migration_ring_empty() - Check if a migration ring is empty.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * Return: true if the ring is empty, otherwise false.
+ */
+bool xe_gt_sriov_pf_migration_ring_empty(struct xe_gt *gt, unsigned int vfid)
+{
+ return ptr_ring_empty(&pf_pick_gt_migration(gt, vfid)->ring);
+}
- return ret;
+/**
+ * xe_gt_sriov_pf_migration_ring_full() - Check if a migration ring is full.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * Return: true if the ring is full, otherwise false.
+ */
+bool xe_gt_sriov_pf_migration_ring_full(struct xe_gt *gt, unsigned int vfid)
+{
+ return ptr_ring_full(&pf_pick_gt_migration(gt, vfid)->ring);
+}
+
+/**
+ * xe_gt_sriov_pf_migration_ring_free() - Consume and free all data in migration ring
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ */
+void xe_gt_sriov_pf_migration_ring_free(struct xe_gt *gt, unsigned int vfid)
+{
+ struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid);
+ struct xe_sriov_packet *data;
+
+ if (ptr_ring_empty(&migration->ring))
+ return;
+
+ xe_gt_sriov_notice(gt, "VF%u unprocessed migration data left in the ring!\n", vfid);
+
+ while ((data = ptr_ring_consume(&migration->ring)))
+ xe_sriov_packet_free(data);
+}
+
+static void pf_migration_save_data_todo(struct xe_gt *gt, unsigned int vfid,
+ enum xe_sriov_packet_type type)
+{
+ set_bit(type, &pf_pick_gt_migration(gt, vfid)->save.data_remaining);
+}
+
+/**
+ * xe_gt_sriov_pf_migration_save_init() - Initialize per-GT migration related data.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ */
+void xe_gt_sriov_pf_migration_save_init(struct xe_gt *gt, unsigned int vfid)
+{
+ struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid);
+
+ migration->save.data_remaining = 0;
+ migration->save.vram_offset = 0;
+
+ xe_gt_assert(gt, pf_migration_guc_size(gt, vfid) > 0);
+ pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_GUC);
+
+ if (pf_migration_ggtt_size(gt, vfid) > 0)
+ pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_GGTT);
+
+ xe_gt_assert(gt, pf_migration_mmio_size(gt, vfid) > 0);
+ pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_MMIO);
+
+ if (pf_migration_vram_size(gt, vfid) > 0)
+ pf_migration_save_data_todo(gt, vfid, XE_SRIOV_PACKET_TYPE_VRAM);
}
/**
- * xe_gt_sriov_pf_migration_write_guc_state() - Write a GuC VF state.
+ * xe_gt_sriov_pf_migration_save_data_pending() - Check if migration data type needs to be saved.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ * @type: the &xe_sriov_packet_type of data to be checked
+ *
+ * Return: true if the data needs saving, otherwise false.
+ */
+bool xe_gt_sriov_pf_migration_save_data_pending(struct xe_gt *gt, unsigned int vfid,
+ enum xe_sriov_packet_type type)
+{
+ return test_bit(type, &pf_pick_gt_migration(gt, vfid)->save.data_remaining);
+}
+
+/**
+ * xe_gt_sriov_pf_migration_save_data_complete() - Complete migration data type save.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier (can't be 0)
+ * @type: the &xe_sriov_packet_type to be marked as completed.
+ */
+void xe_gt_sriov_pf_migration_save_data_complete(struct xe_gt *gt, unsigned int vfid,
+ enum xe_sriov_packet_type type)
+{
+ clear_bit(type, &pf_pick_gt_migration(gt, vfid)->save.data_remaining);
+}
+
+/**
+ * xe_gt_sriov_pf_migration_save_produce() - Add VF save data packet to migration ring.
* @gt: the &xe_gt
* @vfid: the VF identifier
- * @buf: the user space buffer with GuC VF state
- * @size: the size of GuC VF state (in bytes)
+ * @data: the &xe_sriov_packet
*
- * This function is for PF only.
+ * Called by the save migration data producer (PF SR-IOV Control worker) when
+ * processing migration data.
+ * Wakes up the save migration data consumer (userspace), that is potentially
+ * waiting for data when the ring was empty.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_save_produce(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ int ret;
+
+ ret = ptr_ring_produce(&pf_pick_gt_migration(gt, vfid)->ring, data);
+ if (ret)
+ return ret;
+
+ wake_up_all(xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid));
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_pf_migration_restore_consume() - Get VF restore data packet from migration ring.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
*
- * This function reads @size bytes of the VF GuC state stored at user space
- * address @buf and writes it into a internal VF state buffer.
+ * Called by the restore migration data consumer (PF SR-IOV Control worker) when
+ * processing migration data.
+ * Wakes up the restore migration data producer (userspace), that is
+ * potentially waiting to add more data when the ring is full.
*
- * Return: the number of bytes used or a negative error code on failure.
+ * Return: Pointer to &xe_sriov_packet on success,
+ * NULL if ring is empty.
*/
-ssize_t xe_gt_sriov_pf_migration_write_guc_state(struct xe_gt *gt, unsigned int vfid,
- const char __user *buf, size_t size)
+struct xe_sriov_packet *
+xe_gt_sriov_pf_migration_restore_consume(struct xe_gt *gt, unsigned int vfid)
{
- struct xe_gt_sriov_state_snapshot *snapshot;
- loff_t pos = 0;
- ssize_t ret;
+ struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid);
+ struct wait_queue_head *wq = xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid);
+ struct xe_sriov_packet *data;
- xe_gt_assert(gt, IS_SRIOV_PF(gt_to_xe(gt)));
- xe_gt_assert(gt, vfid != PFID);
- xe_gt_assert(gt, vfid <= xe_sriov_pf_get_totalvfs(gt_to_xe(gt)));
+ data = ptr_ring_consume(&migration->ring);
+ if (data)
+ wake_up_all(wq);
- if (!pf_migration_supported(gt))
- return -ENOPKG;
+ return data;
+}
- mutex_lock(pf_migration_mutex(gt));
- snapshot = pf_pick_vf_snapshot(gt, vfid);
- ret = pf_alloc_guc_state(gt, snapshot, size);
- if (!ret) {
- ret = simple_write_to_buffer(snapshot->guc.buff, size, &pos, buf, size);
- if (ret < 0)
- pf_free_guc_state(gt, snapshot);
- else
- pf_dump_guc_state(gt, snapshot);
+static bool pf_restore_data_ready(struct xe_gt *gt, unsigned int vfid)
+{
+ if (xe_gt_sriov_pf_control_check_restore_failed(gt, vfid) ||
+ !ptr_ring_full(&pf_pick_gt_migration(gt, vfid)->ring))
+ return true;
+
+ return false;
+}
+
+/**
+ * xe_gt_sriov_pf_migration_restore_produce() - Add VF restore data packet to migration ring.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ * @data: the &xe_sriov_packet
+ *
+ * Called by the restore migration data producer (userspace) when processing
+ * migration data.
+ * If the ring is full, waits until there is space.
+ * Queues the restore migration data consumer (PF SR-IOV Control worker), that
+ * is potentially waiting for data when the ring was empty.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_gt_sriov_pf_migration_restore_produce(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ int ret;
+
+ xe_gt_assert(gt, data->hdr.tile_id == gt->tile->id);
+ xe_gt_assert(gt, data->hdr.gt_id == gt->info.id);
+
+ for (;;) {
+ if (xe_gt_sriov_pf_control_check_restore_failed(gt, vfid))
+ return -EIO;
+
+ ret = ptr_ring_produce(&pf_pick_gt_migration(gt, vfid)->ring, data);
+ if (!ret)
+ break;
+
+ ret = wait_event_interruptible(*xe_sriov_pf_migration_waitqueue(gt_to_xe(gt), vfid),
+ pf_restore_data_ready(gt, vfid));
+ if (ret)
+ return ret;
}
- mutex_unlock(pf_migration_mutex(gt));
- return ret;
+ return xe_gt_sriov_pf_control_process_restore_data(gt, vfid);
}
-#endif /* CONFIG_DEBUG_FS */
-static bool pf_check_migration_support(struct xe_gt *gt)
+/**
+ * xe_gt_sriov_pf_migration_save_consume() - Get VF save data packet from migration ring.
+ * @gt: the &xe_gt
+ * @vfid: the VF identifier
+ *
+ * Called by the save migration data consumer (userspace) when
+ * processing migration data.
+ * Queues the save migration data producer (PF SR-IOV Control worker), that is
+ * potentially waiting to add more data when the ring is full.
+ *
+ * Return: Pointer to &xe_sriov_packet on success,
+ * NULL if ring is empty and there's no more data available,
+ * ERR_PTR(-EAGAIN) if the ring is empty, but data is still produced.
+ */
+struct xe_sriov_packet *
+xe_gt_sriov_pf_migration_save_consume(struct xe_gt *gt, unsigned int vfid)
{
- /* GuC 70.25 with save/restore v2 is required */
- xe_gt_assert(gt, GUC_FIRMWARE_VER(&gt->uc.guc) >= MAKE_GUC_VER(70, 25, 0));
+ struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, vfid);
+ struct xe_sriov_packet *data;
+ int ret;
+
+ data = ptr_ring_consume(&migration->ring);
+ if (data) {
+ ret = xe_gt_sriov_pf_control_process_save_data(gt, vfid);
+ if (ret) {
+ xe_sriov_packet_free(data);
+ return ERR_PTR(ret);
+ }
- /* XXX: for now this is for feature enabling only */
- return IS_ENABLED(CONFIG_DRM_XE_DEBUG);
+ return data;
+ }
+
+ if (xe_gt_sriov_pf_control_check_save_data_done(gt, vfid))
+ return NULL;
+
+ if (xe_gt_sriov_pf_control_check_save_failed(gt, vfid))
+ return ERR_PTR(-EIO);
+
+ return ERR_PTR(-EAGAIN);
+}
+
+static void destroy_pf_packet(void *ptr)
+{
+ struct xe_sriov_packet *data = ptr;
+
+ xe_sriov_packet_free(data);
+}
+
+static void action_ring_cleanup(void *arg)
+{
+ struct ptr_ring *r = arg;
+
+ ptr_ring_cleanup(r, destroy_pf_packet);
+}
+
+static void pf_gt_migration_check_support(struct xe_gt *gt)
+{
+ if (GUC_FIRMWARE_VER(&gt->uc.guc) < MAKE_GUC_VER(70, 54, 0))
+ xe_sriov_pf_migration_disable(gt_to_xe(gt), "requires GuC version >= 70.54.0");
}
/**
@@ -402,18 +1041,29 @@ static bool pf_check_migration_support(struct xe_gt *gt)
int xe_gt_sriov_pf_migration_init(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
+ unsigned int n, totalvfs;
int err;
xe_gt_assert(gt, IS_SRIOV_PF(xe));
- gt->sriov.pf.migration.supported = pf_check_migration_support(gt);
+ pf_gt_migration_check_support(gt);
if (!pf_migration_supported(gt))
return 0;
- err = drmm_mutex_init(&xe->drm, &gt->sriov.pf.migration.snapshot_lock);
- if (err)
- return err;
+ totalvfs = xe_sriov_pf_get_totalvfs(xe);
+ for (n = 1; n <= totalvfs; n++) {
+ struct xe_gt_sriov_migration_data *migration = pf_pick_gt_migration(gt, n);
+
+ err = ptr_ring_init(&migration->ring,
+ XE_GT_SRIOV_PF_MIGRATION_RING_SIZE, GFP_KERNEL);
+ if (err)
+ return err;
+
+ err = devm_add_action_or_reset(xe->drm.dev, action_ring_cleanup, &migration->ring);
+ if (err)
+ return err;
+ }
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.h
index 09faeae00ddb..181207a637b9 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration.h
@@ -9,16 +9,46 @@
#include <linux/types.h>
struct xe_gt;
+struct xe_sriov_packet;
+enum xe_sriov_packet_type;
+
+/* TODO: get this information by querying GuC in the future */
+#define XE_GT_SRIOV_PF_MIGRATION_GUC_DATA_MAX_SIZE SZ_8M
int xe_gt_sriov_pf_migration_init(struct xe_gt *gt);
-int xe_gt_sriov_pf_migration_save_guc_state(struct xe_gt *gt, unsigned int vfid);
-int xe_gt_sriov_pf_migration_restore_guc_state(struct xe_gt *gt, unsigned int vfid);
-
-#ifdef CONFIG_DEBUG_FS
-ssize_t xe_gt_sriov_pf_migration_read_guc_state(struct xe_gt *gt, unsigned int vfid,
- char __user *buf, size_t count, loff_t *pos);
-ssize_t xe_gt_sriov_pf_migration_write_guc_state(struct xe_gt *gt, unsigned int vfid,
- const char __user *buf, size_t count);
-#endif
+int xe_gt_sriov_pf_migration_guc_save(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_migration_guc_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data);
+int xe_gt_sriov_pf_migration_ggtt_save(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_migration_ggtt_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data);
+int xe_gt_sriov_pf_migration_mmio_save(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_migration_mmio_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data);
+int xe_gt_sriov_pf_migration_vram_save(struct xe_gt *gt, unsigned int vfid);
+int xe_gt_sriov_pf_migration_vram_restore(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data);
+
+ssize_t xe_gt_sriov_pf_migration_size(struct xe_gt *gt, unsigned int vfid);
+
+bool xe_gt_sriov_pf_migration_ring_empty(struct xe_gt *gt, unsigned int vfid);
+bool xe_gt_sriov_pf_migration_ring_full(struct xe_gt *gt, unsigned int vfid);
+void xe_gt_sriov_pf_migration_ring_free(struct xe_gt *gt, unsigned int vfid);
+
+void xe_gt_sriov_pf_migration_save_init(struct xe_gt *gt, unsigned int vfid);
+bool xe_gt_sriov_pf_migration_save_data_pending(struct xe_gt *gt, unsigned int vfid,
+ enum xe_sriov_packet_type type);
+void xe_gt_sriov_pf_migration_save_data_complete(struct xe_gt *gt, unsigned int vfid,
+ enum xe_sriov_packet_type type);
+
+int xe_gt_sriov_pf_migration_save_produce(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data);
+struct xe_sriov_packet *
+xe_gt_sriov_pf_migration_restore_consume(struct xe_gt *gt, unsigned int vfid);
+
+int xe_gt_sriov_pf_migration_restore_produce(struct xe_gt *gt, unsigned int vfid,
+ struct xe_sriov_packet *data);
+struct xe_sriov_packet *
+xe_gt_sriov_pf_migration_save_consume(struct xe_gt *gt, unsigned int vfid);
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration_types.h
index 1f3110b6d44f..f50c64241e9c 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_migration_types.h
@@ -6,35 +6,23 @@
#ifndef _XE_GT_SRIOV_PF_MIGRATION_TYPES_H_
#define _XE_GT_SRIOV_PF_MIGRATION_TYPES_H_
-#include <linux/mutex.h>
-#include <linux/types.h>
+#include <linux/ptr_ring.h>
/**
- * struct xe_gt_sriov_state_snapshot - GT-level per-VF state snapshot data.
+ * struct xe_gt_sriov_migration_data - GT-level per-VF migration data.
*
* Used by the PF driver to maintain per-VF migration data.
*/
-struct xe_gt_sriov_state_snapshot {
- /** @guc: GuC VF state snapshot */
+struct xe_gt_sriov_migration_data {
+ /** @ring: queue containing VF save / restore migration data */
+ struct ptr_ring ring;
+ /** @save: structure for currently processed save migration data */
struct {
- /** @guc.buff: buffer with the VF state */
- u32 *buff;
- /** @guc.size: size of the buffer (must be dwords aligned) */
- u32 size;
- } guc;
-};
-
-/**
- * struct xe_gt_sriov_pf_migration - GT-level data.
- *
- * Used by the PF driver to maintain non-VF specific per-GT data.
- */
-struct xe_gt_sriov_pf_migration {
- /** @supported: indicates whether the feature is supported */
- bool supported;
-
- /** @snapshot_lock: protects all VFs snapshots */
- struct mutex snapshot_lock;
+ /** @save.data_remaining: bitmap of migration types that need to be saved */
+ unsigned long data_remaining;
+ /** @save.vram_offset: last saved offset within VRAM, used for chunked VRAM save */
+ loff_t vram_offset;
+ } save;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
index 76dd9233ef9f..2eb21610e5a0 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_service.c
@@ -99,11 +99,30 @@ static const struct xe_reg ver_3000_runtime_regs[] = {
HUC_KERNEL_LOAD_INFO, /* _MMIO(0xc1dc) */
};
+static const struct xe_reg ver_35_runtime_regs[] = {
+ RPM_CONFIG0, /* _MMIO(0x0d00) */
+ XEHP_FUSE4, /* _MMIO(0x9114) */
+ MIRROR_FUSE3, /* _MMIO(0x9118) */
+ MIRROR_L3BANK_ENABLE, /* _MMIO(0x9130) */
+ XELP_EU_ENABLE, /* _MMIO(0x9134) */
+ XELP_GT_GEOMETRY_DSS_ENABLE, /* _MMIO(0x913c) */
+ GT_VEBOX_VDBOX_DISABLE, /* _MMIO(0x9140) */
+ XEHP_GT_COMPUTE_DSS_ENABLE, /* _MMIO(0x9144) */
+ XEHPC_GT_COMPUTE_DSS_ENABLE_EXT,/* _MMIO(0x9148) */
+ XE2_GT_COMPUTE_DSS_2, /* _MMIO(0x914c) */
+ XE2_GT_GEOMETRY_DSS_1, /* _MMIO(0x9150) */
+ XE2_GT_GEOMETRY_DSS_2, /* _MMIO(0x9154) */
+ SERVICE_COPY_ENABLE, /* _MMIO(0x9170) */
+};
+
static const struct xe_reg *pick_runtime_regs(struct xe_device *xe, unsigned int *count)
{
const struct xe_reg *regs;
- if (GRAPHICS_VERx100(xe) >= 3000) {
+ if (GRAPHICS_VER(xe) >= 35) {
+ *count = ARRAY_SIZE(ver_35_runtime_regs);
+ regs = ver_35_runtime_regs;
+ } else if (GRAPHICS_VERx100(xe) >= 3000) {
*count = ARRAY_SIZE(ver_3000_runtime_regs);
regs = ver_3000_runtime_regs;
} else if (GRAPHICS_VERx100(xe) >= 2000) {
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h
index a64a6835ad65..667b8310478d 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_pf_types.h
@@ -31,8 +31,8 @@ struct xe_gt_sriov_metadata {
/** @version: negotiated VF/PF ABI version */
struct xe_gt_sriov_pf_service_version version;
- /** @snapshot: snapshot of the VF state data */
- struct xe_gt_sriov_state_snapshot snapshot;
+ /** @migration: per-VF migration data. */
+ struct xe_gt_sriov_migration_data migration;
};
/**
@@ -58,7 +58,6 @@ struct xe_gt_sriov_pf {
struct xe_gt_sriov_pf_service service;
struct xe_gt_sriov_pf_control control;
struct xe_gt_sriov_pf_policy policy;
- struct xe_gt_sriov_pf_migration migration;
struct xe_gt_sriov_spare_config spare;
struct xe_gt_sriov_metadata *vfs;
};
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_printk.h b/drivers/gpu/drm/xe/xe_gt_sriov_printk.h
index 17624b16300a..d3457d608db8 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_printk.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_printk.h
@@ -7,10 +7,13 @@
#define _XE_GT_SRIOV_PRINTK_H_
#include "xe_gt_printk.h"
-#include "xe_sriov_printk.h"
+#include "xe_tile_sriov_printk.h"
+
+#define __XE_GT_SRIOV_PRINTK_FMT(_gt, _fmt, ...) \
+ __XE_TILE_SRIOV_PRINTK_FMT((_gt)->tile, __XE_GT_PRINTK_FMT((_gt), _fmt, ##__VA_ARGS__))
#define __xe_gt_sriov_printk(gt, _level, fmt, ...) \
- xe_gt_printk((gt), _level, "%s" fmt, xe_sriov_printk_prefix(gt_to_xe(gt)), ##__VA_ARGS__)
+ xe_sriov_##_level(gt_to_xe(gt), __XE_GT_SRIOV_PRINTK_FMT((gt), fmt, ##__VA_ARGS__))
#define xe_gt_sriov_err(_gt, _fmt, ...) \
__xe_gt_sriov_printk(_gt, err, _fmt, ##__VA_ARGS__)
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
index 0461d5513487..4c73a077d314 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
@@ -23,12 +23,19 @@
#include "xe_gt_sriov_vf.h"
#include "xe_gt_sriov_vf_types.h"
#include "xe_guc.h"
+#include "xe_guc_ct.h"
#include "xe_guc_hxg_helpers.h"
#include "xe_guc_relay.h"
+#include "xe_guc_submit.h"
+#include "xe_irq.h"
#include "xe_lrc.h"
+#include "xe_memirq.h"
#include "xe_mmio.h"
#include "xe_sriov.h"
#include "xe_sriov_vf.h"
+#include "xe_sriov_vf_ccs.h"
+#include "xe_tile_sriov_vf.h"
+#include "xe_tlb_inval.h"
#include "xe_uc_fw.h"
#include "xe_wopcm.h"
@@ -307,13 +314,13 @@ static int guc_action_vf_notify_resfix_done(struct xe_guc *guc)
}
/**
- * xe_gt_sriov_vf_notify_resfix_done - Notify GuC about resource fixups apply completed.
+ * vf_notify_resfix_done - Notify GuC about resource fixups apply completed.
* @gt: the &xe_gt struct instance linked to target GuC
*
* Returns: 0 if the operation completed successfully, or a negative error
* code otherwise.
*/
-int xe_gt_sriov_vf_notify_resfix_done(struct xe_gt *gt)
+static int vf_notify_resfix_done(struct xe_gt *gt)
{
struct xe_guc *guc = &gt->uc.guc;
int err;
@@ -433,13 +440,17 @@ u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt)
static int vf_get_ggtt_info(struct xe_gt *gt)
{
- struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_tile *tile = gt_to_tile(gt);
+ struct xe_ggtt *ggtt = tile->mem.ggtt;
struct xe_guc *guc = &gt->uc.guc;
- u64 start, size;
+ u64 start, size, ggtt_size;
+ s64 shift;
int err;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+ guard(mutex)(&ggtt->lock);
+
err = guc_action_query_single_klv64(guc, GUC_KLV_VF_CFG_GGTT_START_KEY, &start);
if (unlikely(err))
return err;
@@ -448,28 +459,44 @@ static int vf_get_ggtt_info(struct xe_gt *gt)
if (unlikely(err))
return err;
- if (config->ggtt_size && config->ggtt_size != size) {
+ if (!size)
+ return -ENODATA;
+
+ ggtt_size = xe_tile_sriov_vf_ggtt(tile);
+ if (ggtt_size && ggtt_size != size) {
xe_gt_sriov_err(gt, "Unexpected GGTT reassignment: %lluK != %lluK\n",
- size / SZ_1K, config->ggtt_size / SZ_1K);
+ size / SZ_1K, ggtt_size / SZ_1K);
return -EREMCHG;
}
xe_gt_sriov_dbg_verbose(gt, "GGTT %#llx-%#llx = %lluK\n",
start, start + size - 1, size / SZ_1K);
- config->ggtt_shift = start - (s64)config->ggtt_base;
- config->ggtt_base = start;
- config->ggtt_size = size;
+ shift = start - (s64)xe_tile_sriov_vf_ggtt_base(tile);
+ xe_tile_sriov_vf_ggtt_base_store(tile, start);
+ xe_tile_sriov_vf_ggtt_store(tile, size);
- return config->ggtt_size ? 0 : -ENODATA;
+ if (shift && shift != start) {
+ xe_gt_sriov_info(gt, "Shifting GGTT base by %lld to 0x%016llx\n",
+ shift, start);
+ xe_tile_sriov_vf_fixup_ggtt_nodes_locked(gt_to_tile(gt), shift);
+ }
+
+ if (xe_sriov_vf_migration_supported(gt_to_xe(gt))) {
+ WRITE_ONCE(gt->sriov.vf.migration.ggtt_need_fixes, false);
+ smp_wmb(); /* Ensure above write visible before wake */
+ wake_up_all(&gt->sriov.vf.migration.wq);
+ }
+
+ return 0;
}
static int vf_get_lmem_info(struct xe_gt *gt)
{
- struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
+ struct xe_tile *tile = gt_to_tile(gt);
struct xe_guc *guc = &gt->uc.guc;
char size_str[10];
- u64 size;
+ u64 size, lmem_size;
int err;
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
@@ -478,18 +505,19 @@ static int vf_get_lmem_info(struct xe_gt *gt)
if (unlikely(err))
return err;
- if (config->lmem_size && config->lmem_size != size) {
+ lmem_size = xe_tile_sriov_vf_lmem(tile);
+ if (lmem_size && lmem_size != size) {
xe_gt_sriov_err(gt, "Unexpected LMEM reassignment: %lluM != %lluM\n",
- size / SZ_1M, config->lmem_size / SZ_1M);
+ size / SZ_1M, lmem_size / SZ_1M);
return -EREMCHG;
}
string_get_size(size, 1, STRING_UNITS_2, size_str, sizeof(size_str));
xe_gt_sriov_dbg_verbose(gt, "LMEM %lluM %s\n", size / SZ_1M, size_str);
- config->lmem_size = size;
+ xe_tile_sriov_vf_lmem_store(tile, size);
- return config->lmem_size ? 0 : -ENODATA;
+ return size ? 0 : -ENODATA;
}
static int vf_get_submission_cfg(struct xe_gt *gt)
@@ -540,7 +568,9 @@ static void vf_cache_gmdid(struct xe_gt *gt)
* xe_gt_sriov_vf_query_config - Query SR-IOV config data over MMIO.
* @gt: the &xe_gt
*
- * This function is for VF use only.
+ * This function is for VF use only. This function may shift the GGTT and is
+ * performed under GGTT lock, making this step visible to all GTs that share a
+ * GGTT.
*
* Return: 0 on success or a negative error code on failure.
*/
@@ -586,75 +616,6 @@ u16 xe_gt_sriov_vf_guc_ids(struct xe_gt *gt)
return gt->sriov.vf.self_config.num_ctxs;
}
-/**
- * xe_gt_sriov_vf_lmem - VF LMEM configuration.
- * @gt: the &xe_gt
- *
- * This function is for VF use only.
- *
- * Return: size of the LMEM assigned to VF.
- */
-u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt)
-{
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
- xe_gt_assert(gt, gt->sriov.vf.self_config.lmem_size);
-
- return gt->sriov.vf.self_config.lmem_size;
-}
-
-/**
- * xe_gt_sriov_vf_ggtt - VF GGTT configuration.
- * @gt: the &xe_gt
- *
- * This function is for VF use only.
- *
- * Return: size of the GGTT assigned to VF.
- */
-u64 xe_gt_sriov_vf_ggtt(struct xe_gt *gt)
-{
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
- xe_gt_assert(gt, gt->sriov.vf.self_config.ggtt_size);
-
- return gt->sriov.vf.self_config.ggtt_size;
-}
-
-/**
- * xe_gt_sriov_vf_ggtt_base - VF GGTT base offset.
- * @gt: the &xe_gt
- *
- * This function is for VF use only.
- *
- * Return: base offset of the GGTT assigned to VF.
- */
-u64 xe_gt_sriov_vf_ggtt_base(struct xe_gt *gt)
-{
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, gt->sriov.vf.guc_version.major);
- xe_gt_assert(gt, gt->sriov.vf.self_config.ggtt_size);
-
- return gt->sriov.vf.self_config.ggtt_base;
-}
-
-/**
- * xe_gt_sriov_vf_ggtt_shift - Return shift in GGTT range due to VF migration
- * @gt: the &xe_gt struct instance
- *
- * This function is for VF use only.
- *
- * Return: The shift value; could be negative
- */
-s64 xe_gt_sriov_vf_ggtt_shift(struct xe_gt *gt)
-{
- struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
-
- xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- xe_gt_assert(gt, xe_gt_is_main_type(gt));
-
- return config->ggtt_shift;
-}
-
static int relay_action_handshake(struct xe_gt *gt, u32 *major, u32 *minor)
{
u32 request[VF2PF_HANDSHAKE_REQUEST_MSG_LEN] = {
@@ -755,7 +716,7 @@ failed:
* xe_gt_sriov_vf_default_lrcs_hwsp_rebase - Update GGTT references in HWSP of default LRCs.
* @gt: the &xe_gt struct instance
*/
-void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt)
+static void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt)
{
struct xe_hw_engine *hwe;
enum xe_hw_engine_id id;
@@ -764,6 +725,31 @@ void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt)
xe_default_lrc_update_memirq_regs_with_address(hwe);
}
+static void vf_start_migration_recovery(struct xe_gt *gt)
+{
+ bool started;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ spin_lock(&gt->sriov.vf.migration.lock);
+
+ if (!gt->sriov.vf.migration.recovery_queued ||
+ !gt->sriov.vf.migration.recovery_teardown) {
+ gt->sriov.vf.migration.recovery_queued = true;
+ WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, true);
+ WRITE_ONCE(gt->sriov.vf.migration.ggtt_need_fixes, true);
+ smp_wmb(); /* Ensure above writes visible before wake */
+
+ xe_guc_ct_wake_waiters(&gt->uc.guc.ct);
+
+ started = queue_work(gt->ordered_wq, &gt->sriov.vf.migration.worker);
+ xe_gt_sriov_info(gt, "VF migration recovery %s\n", started ?
+ "scheduled" : "already in progress");
+ }
+
+ spin_unlock(&gt->sriov.vf.migration.lock);
+}
+
/**
* xe_gt_sriov_vf_migrated_event_handler - Start a VF migration recovery,
* or just mark that a GuC is ready for it.
@@ -776,16 +762,15 @@ void xe_gt_sriov_vf_migrated_event_handler(struct xe_gt *gt)
struct xe_device *xe = gt_to_xe(gt);
xe_gt_assert(gt, IS_SRIOV_VF(xe));
+ xe_gt_assert(gt, xe_gt_sriov_vf_recovery_pending(gt));
- set_bit(gt->info.id, &xe->sriov.vf.migration.gt_flags);
- /*
- * We need to be certain that if all flags were set, at least one
- * thread will notice that and schedule the recovery.
- */
- smp_mb__after_atomic();
+ if (!xe_sriov_vf_migration_supported(xe)) {
+ xe_gt_sriov_err(gt, "migration not supported\n");
+ return;
+ }
xe_gt_sriov_info(gt, "ready for recovery after migration\n");
- xe_sriov_vf_start_migration_recovery(xe);
+ vf_start_migration_recovery(gt);
}
static bool vf_is_negotiated(struct xe_gt *gt, u16 major, u16 minor)
@@ -1040,22 +1025,25 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_gt_sriov_vf_selfconfig *config = &gt->sriov.vf.self_config;
struct xe_device *xe = gt_to_xe(gt);
+ u64 lmem_size;
char buf[10];
xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
- drm_printf(p, "GGTT range:\t%#llx-%#llx\n",
- config->ggtt_base,
- config->ggtt_base + config->ggtt_size - 1);
-
- string_get_size(config->ggtt_size, 1, STRING_UNITS_2, buf, sizeof(buf));
- drm_printf(p, "GGTT size:\t%llu (%s)\n", config->ggtt_size, buf);
+ if (xe_gt_is_main_type(gt)) {
+ u64 ggtt_size = xe_tile_sriov_vf_ggtt(gt_to_tile(gt));
+ u64 ggtt_base = xe_tile_sriov_vf_ggtt_base(gt_to_tile(gt));
- drm_printf(p, "GGTT shift on last restore:\t%lld\n", config->ggtt_shift);
+ drm_printf(p, "GGTT range:\t%#llx-%#llx\n",
+ ggtt_base, ggtt_base + ggtt_size - 1);
+ string_get_size(ggtt_size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ drm_printf(p, "GGTT size:\t%llu (%s)\n", ggtt_size, buf);
- if (IS_DGFX(xe) && xe_gt_is_main_type(gt)) {
- string_get_size(config->lmem_size, 1, STRING_UNITS_2, buf, sizeof(buf));
- drm_printf(p, "LMEM size:\t%llu (%s)\n", config->lmem_size, buf);
+ if (IS_DGFX(xe)) {
+ lmem_size = xe_tile_sriov_vf_lmem(gt_to_tile(gt));
+ string_get_size(lmem_size, 1, STRING_UNITS_2, buf, sizeof(buf));
+ drm_printf(p, "LMEM size:\t%llu (%s)\n", lmem_size, buf);
+ }
}
drm_printf(p, "GuC contexts:\t%u\n", config->num_ctxs);
@@ -1118,3 +1106,272 @@ void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p)
drm_printf(p, "\thandshake:\t%u.%u\n",
pf_version->major, pf_version->minor);
}
+
+static bool vf_post_migration_shutdown(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+
+ /*
+ * On platforms where CCS must be restored by the primary GT, the media
+ * GT's VF post-migration recovery must run afterward. Detect this case
+ * and re-queue the media GT's restore work item if necessary.
+ */
+ if (xe->info.needs_shared_vf_gt_wq && xe_gt_is_media_type(gt)) {
+ struct xe_gt *primary_gt = gt_to_tile(gt)->primary_gt;
+
+ if (xe_gt_sriov_vf_recovery_pending(primary_gt))
+ return true;
+ }
+
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ gt->sriov.vf.migration.recovery_queued = false;
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ xe_guc_ct_flush_and_stop(&gt->uc.guc.ct);
+ xe_guc_submit_pause(&gt->uc.guc);
+ xe_tlb_inval_reset(&gt->tlb_inval);
+
+ return false;
+}
+
+static size_t post_migration_scratch_size(struct xe_device *xe)
+{
+ return max(xe_lrc_reg_size(xe), LRC_WA_BB_SIZE);
+}
+
+static int vf_post_migration_fixups(struct xe_gt *gt)
+{
+ void *buf = gt->sriov.vf.migration.scratch;
+ int err;
+
+ /* xe_gt_sriov_vf_query_config will fixup the GGTT addresses */
+ err = xe_gt_sriov_vf_query_config(gt);
+ if (err)
+ return err;
+
+ if (xe_gt_is_main_type(gt))
+ xe_sriov_vf_ccs_rebase(gt_to_xe(gt));
+
+ xe_gt_sriov_vf_default_lrcs_hwsp_rebase(gt);
+ err = xe_guc_contexts_hwsp_rebase(&gt->uc.guc, buf);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static void vf_post_migration_rearm(struct xe_gt *gt)
+{
+ xe_guc_ct_restart(&gt->uc.guc.ct);
+ xe_guc_submit_unpause_prepare(&gt->uc.guc);
+}
+
+static void vf_post_migration_kickstart(struct xe_gt *gt)
+{
+ xe_guc_submit_unpause(&gt->uc.guc);
+}
+
+static void vf_post_migration_abort(struct xe_gt *gt)
+{
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, false);
+ WRITE_ONCE(gt->sriov.vf.migration.ggtt_need_fixes, false);
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ wake_up_all(&gt->sriov.vf.migration.wq);
+
+ xe_guc_submit_pause_abort(&gt->uc.guc);
+}
+
+static int vf_post_migration_notify_resfix_done(struct xe_gt *gt)
+{
+ bool skip_resfix = false;
+
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ if (gt->sriov.vf.migration.recovery_queued) {
+ skip_resfix = true;
+ xe_gt_sriov_dbg(gt, "another recovery imminent, resfix skipped\n");
+ } else {
+ WRITE_ONCE(gt->sriov.vf.migration.recovery_inprogress, false);
+ }
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ if (skip_resfix)
+ return -EAGAIN;
+
+ /*
+ * Make sure interrupts on the new HW are properly set. The GuC IRQ
+ * must be working at this point, since the recovery did started,
+ * but the rest was not enabled using the procedure from spec.
+ */
+ xe_irq_resume(gt_to_xe(gt));
+
+ return vf_notify_resfix_done(gt);
+}
+
+static void vf_post_migration_recovery(struct xe_gt *gt)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ int err;
+ bool retry;
+
+ xe_gt_sriov_dbg(gt, "migration recovery in progress\n");
+
+ retry = vf_post_migration_shutdown(gt);
+ if (retry)
+ goto queue;
+
+ if (!xe_sriov_vf_migration_supported(xe)) {
+ xe_gt_sriov_err(gt, "migration is not supported\n");
+ err = -ENOTRECOVERABLE;
+ goto fail;
+ }
+
+ err = vf_post_migration_fixups(gt);
+ if (err)
+ goto fail;
+
+ vf_post_migration_rearm(gt);
+
+ err = vf_post_migration_notify_resfix_done(gt);
+ if (err && err != -EAGAIN)
+ goto fail;
+
+ vf_post_migration_kickstart(gt);
+
+ xe_gt_sriov_notice(gt, "migration recovery ended\n");
+ return;
+fail:
+ vf_post_migration_abort(gt);
+ xe_gt_sriov_err(gt, "migration recovery failed (%pe)\n", ERR_PTR(err));
+ xe_device_declare_wedged(xe);
+ return;
+
+queue:
+ xe_gt_sriov_info(gt, "Re-queuing migration recovery\n");
+ queue_work(gt->ordered_wq, &gt->sriov.vf.migration.worker);
+}
+
+static void migration_worker_func(struct work_struct *w)
+{
+ struct xe_gt *gt = container_of(w, struct xe_gt,
+ sriov.vf.migration.worker);
+
+ vf_post_migration_recovery(gt);
+}
+
+static void vf_migration_fini(void *arg)
+{
+ struct xe_gt *gt = arg;
+
+ spin_lock_irq(&gt->sriov.vf.migration.lock);
+ gt->sriov.vf.migration.recovery_teardown = true;
+ spin_unlock_irq(&gt->sriov.vf.migration.lock);
+
+ cancel_work_sync(&gt->sriov.vf.migration.worker);
+}
+
+/**
+ * xe_gt_sriov_vf_init_early() - GT VF init early
+ * @gt: the &xe_gt
+ *
+ * Return 0 on success, errno on failure
+ */
+int xe_gt_sriov_vf_init_early(struct xe_gt *gt)
+{
+ void *buf;
+
+ if (!xe_sriov_vf_migration_supported(gt_to_xe(gt)))
+ return 0;
+
+ buf = drmm_kmalloc(&gt_to_xe(gt)->drm,
+ post_migration_scratch_size(gt_to_xe(gt)),
+ GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ gt->sriov.vf.migration.scratch = buf;
+ spin_lock_init(&gt->sriov.vf.migration.lock);
+ INIT_WORK(&gt->sriov.vf.migration.worker, migration_worker_func);
+ init_waitqueue_head(&gt->sriov.vf.migration.wq);
+
+ return 0;
+}
+
+/**
+ * xe_gt_sriov_vf_init() - GT VF init
+ * @gt: the &xe_gt
+ *
+ * Return 0 on success, errno on failure
+ */
+int xe_gt_sriov_vf_init(struct xe_gt *gt)
+{
+ if (!xe_sriov_vf_migration_supported(gt_to_xe(gt)))
+ return 0;
+
+ /*
+ * We want to tear down the VF post-migration early during driver
+ * unload; therefore, we add this finalization action later during
+ * driver load.
+ */
+ return devm_add_action_or_reset(gt_to_xe(gt)->drm.dev,
+ vf_migration_fini, gt);
+}
+
+/**
+ * xe_gt_sriov_vf_recovery_pending() - VF post migration recovery pending
+ * @gt: the &xe_gt
+ *
+ * The return value of this function must be immediately visible upon vCPU
+ * unhalt and must persist until RESFIX_DONE is issued. This guarantee is
+ * currently implemented only for platforms that support memirq. If non-memirq
+ * platforms begin to support VF migration, this function will need to be
+ * updated accordingly.
+ *
+ * Return: True if VF post migration recovery is pending, False otherwise
+ */
+bool xe_gt_sriov_vf_recovery_pending(struct xe_gt *gt)
+{
+ struct xe_memirq *memirq = &gt_to_tile(gt)->memirq;
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ /* early detection until recovery starts */
+ if (xe_device_uses_memirq(gt_to_xe(gt)) &&
+ xe_memirq_guc_sw_int_0_irq_pending(memirq, &gt->uc.guc))
+ return true;
+
+ return READ_ONCE(gt->sriov.vf.migration.recovery_inprogress);
+}
+
+static bool vf_valid_ggtt(struct xe_gt *gt)
+{
+ struct xe_memirq *memirq = &gt_to_tile(gt)->memirq;
+ bool irq_pending = xe_device_uses_memirq(gt_to_xe(gt)) &&
+ xe_memirq_guc_sw_int_0_irq_pending(memirq, &gt->uc.guc);
+
+ xe_gt_assert(gt, IS_SRIOV_VF(gt_to_xe(gt)));
+
+ if (irq_pending || READ_ONCE(gt->sriov.vf.migration.ggtt_need_fixes))
+ return false;
+
+ return true;
+}
+
+/**
+ * xe_gt_sriov_vf_wait_valid_ggtt() - VF wait for valid GGTT addresses
+ * @gt: the &xe_gt
+ */
+void xe_gt_sriov_vf_wait_valid_ggtt(struct xe_gt *gt)
+{
+ int ret;
+
+ if (!IS_SRIOV_VF(gt_to_xe(gt)) ||
+ !xe_sriov_vf_migration_supported(gt_to_xe(gt)))
+ return;
+
+ ret = wait_event_interruptible_timeout(gt->sriov.vf.migration.wq,
+ vf_valid_ggtt(gt),
+ HZ * 5);
+ xe_gt_WARN_ON(gt, !ret);
+}
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
index 0af1dc769fe0..af40276790fa 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.h
@@ -21,16 +21,15 @@ void xe_gt_sriov_vf_guc_versions(struct xe_gt *gt,
int xe_gt_sriov_vf_query_config(struct xe_gt *gt);
int xe_gt_sriov_vf_connect(struct xe_gt *gt);
int xe_gt_sriov_vf_query_runtime(struct xe_gt *gt);
-void xe_gt_sriov_vf_default_lrcs_hwsp_rebase(struct xe_gt *gt);
-int xe_gt_sriov_vf_notify_resfix_done(struct xe_gt *gt);
void xe_gt_sriov_vf_migrated_event_handler(struct xe_gt *gt);
+int xe_gt_sriov_vf_init_early(struct xe_gt *gt);
+int xe_gt_sriov_vf_init(struct xe_gt *gt);
+bool xe_gt_sriov_vf_recovery_pending(struct xe_gt *gt);
+
u32 xe_gt_sriov_vf_gmdid(struct xe_gt *gt);
u16 xe_gt_sriov_vf_guc_ids(struct xe_gt *gt);
u64 xe_gt_sriov_vf_lmem(struct xe_gt *gt);
-u64 xe_gt_sriov_vf_ggtt(struct xe_gt *gt);
-u64 xe_gt_sriov_vf_ggtt_base(struct xe_gt *gt);
-s64 xe_gt_sriov_vf_ggtt_shift(struct xe_gt *gt);
u32 xe_gt_sriov_vf_read32(struct xe_gt *gt, struct xe_reg reg);
void xe_gt_sriov_vf_write32(struct xe_gt *gt, struct xe_reg reg, u32 val);
@@ -39,4 +38,6 @@ void xe_gt_sriov_vf_print_config(struct xe_gt *gt, struct drm_printer *p);
void xe_gt_sriov_vf_print_runtime(struct xe_gt *gt, struct drm_printer *p);
void xe_gt_sriov_vf_print_version(struct xe_gt *gt, struct drm_printer *p);
+void xe_gt_sriov_vf_wait_valid_ggtt(struct xe_gt *gt);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
index 298dedf4b009..420b0e6089de 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf_types.h
@@ -7,20 +7,14 @@
#define _XE_GT_SRIOV_VF_TYPES_H_
#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
#include "xe_uc_fw_types.h"
/**
* struct xe_gt_sriov_vf_selfconfig - VF configuration data.
*/
struct xe_gt_sriov_vf_selfconfig {
- /** @ggtt_base: assigned base offset of the GGTT region. */
- u64 ggtt_base;
- /** @ggtt_size: assigned size of the GGTT region. */
- u64 ggtt_size;
- /** @ggtt_shift: difference in ggtt_base on last migration */
- s64 ggtt_shift;
- /** @lmem_size: assigned size of the LMEM. */
- u64 lmem_size;
/** @num_ctxs: assigned number of GuC submission context IDs. */
u16 num_ctxs;
/** @num_dbs: assigned number of GuC doorbells IDs. */
@@ -47,6 +41,28 @@ struct xe_gt_sriov_vf_runtime {
};
/**
+ * xe_gt_sriov_vf_migration - VF migration data.
+ */
+struct xe_gt_sriov_vf_migration {
+ /** @migration: VF migration recovery worker */
+ struct work_struct worker;
+ /** @lock: Protects recovery_queued, teardown */
+ spinlock_t lock;
+ /** @wq: wait queue for migration fixes */
+ wait_queue_head_t wq;
+ /** @scratch: Scratch memory for VF recovery */
+ void *scratch;
+ /** @recovery_teardown: VF post migration recovery is being torn down */
+ bool recovery_teardown;
+ /** @recovery_queued: VF post migration recovery in queued */
+ bool recovery_queued;
+ /** @recovery_inprogress: VF post migration recovery in progress */
+ bool recovery_inprogress;
+ /** @ggtt_need_fixes: VF GGTT needs fixes */
+ bool ggtt_need_fixes;
+};
+
+/**
* struct xe_gt_sriov_vf - GT level VF virtualization data.
*/
struct xe_gt_sriov_vf {
@@ -58,6 +74,8 @@ struct xe_gt_sriov_vf {
struct xe_gt_sriov_vf_selfconfig self_config;
/** @runtime: runtime data retrieved from the PF. */
struct xe_gt_sriov_vf_runtime runtime;
+ /** @migration: migration data for the VF. */
+ struct xe_gt_sriov_vf_migration migration;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_gt_throttle.c b/drivers/gpu/drm/xe/xe_gt_throttle.c
index aa962c783cdf..82c5fbcdfbe3 100644
--- a/drivers/gpu/drm/xe/xe_gt_throttle.c
+++ b/drivers/gpu/drm/xe/xe_gt_throttle.c
@@ -8,221 +8,222 @@
#include <regs/xe_gt_regs.h>
#include "xe_device.h"
#include "xe_gt.h"
-#include "xe_gt_printk.h"
#include "xe_gt_sysfs.h"
#include "xe_gt_throttle.h"
#include "xe_mmio.h"
+#include "xe_platform_types.h"
#include "xe_pm.h"
/**
* DOC: Xe GT Throttle
*
- * Provides sysfs entries and other helpers for frequency throttle reasons in GT
+ * The GT frequency may be throttled by hardware/firmware for various reasons
+ * that are provided through attributes under the ``freq0/throttle/`` directory.
+ * Their availability depend on the platform and some may not be visible if that
+ * reason is not available.
*
- * device/gt#/freq0/throttle/status - Overall status
- * device/gt#/freq0/throttle/reason_pl1 - Frequency throttle due to PL1
- * device/gt#/freq0/throttle/reason_pl2 - Frequency throttle due to PL2
- * device/gt#/freq0/throttle/reason_pl4 - Frequency throttle due to PL4, Iccmax etc.
- * device/gt#/freq0/throttle/reason_thermal - Frequency throttle due to thermal
- * device/gt#/freq0/throttle/reason_prochot - Frequency throttle due to prochot
- * device/gt#/freq0/throttle/reason_ratl - Frequency throttle due to RATL
- * device/gt#/freq0/throttle/reason_vr_thermalert - Frequency throttle due to VR THERMALERT
- * device/gt#/freq0/throttle/reason_vr_tdc - Frequency throttle due to VR TDC
+ * The ``reasons`` attribute can be used by sysadmin to monitor all possible
+ * reasons for throttling and report them. It's preferred over monitoring
+ * ``status`` and then reading the reason from individual attributes since that
+ * is racy. If there's no throttling happening, "none" is returned.
+ *
+ * The following attributes are available on Crescent Island platform:
+ *
+ * - ``status``: Overall throttle status (0: no throttling, 1: throttling)
+ * - ``reasons``: Array of reasons causing throttling separated by space
+ * - ``reason_pl1``: package PL1
+ * - ``reason_pl2``: package PL2
+ * - ``reason_pl4``: package PL4
+ * - ``reason_prochot``: prochot
+ * - ``reason_soc_thermal``: SoC thermal
+ * - ``reason_mem_thermal``: Memory thermal
+ * - ``reason_vr_thermal``: VR thermal
+ * - ``reason_iccmax``: ICCMAX
+ * - ``reason_ratl``: RATL thermal algorithm
+ * - ``reason_soc_avg_thermal``: SoC average temp
+ * - ``reason_fastvmode``: VR is hitting FastVMode
+ * - ``reason_psys_pl1``: PSYS PL1
+ * - ``reason_psys_pl2``: PSYS PL2
+ * - ``reason_p0_freq``: P0 frequency
+ * - ``reason_psys_crit``: PSYS critical
+ *
+ * Other platforms support the following reasons:
+ *
+ * - ``status``: Overall throttle status (0: no throttling, 1: throttling)
+ * - ``reasons``: Array of reasons causing throttling separated by space
+ * - ``reason_pl1``: package PL1
+ * - ``reason_pl2``: package PL2
+ * - ``reason_pl4``: package PL4, Iccmax etc.
+ * - ``reason_thermal``: thermal
+ * - ``reason_prochot``: prochot
+ * - ``reason_ratl``: RATL hermal algorithm
+ * - ``reason_vr_thermalert``: VR THERMALERT
+ * - ``reason_vr_tdc``: VR TDC
*/
-static struct xe_gt *
-dev_to_gt(struct device *dev)
-{
- return kobj_to_gt(dev->kobj.parent);
-}
-
-u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt)
-{
- u32 reg;
-
- xe_pm_runtime_get(gt_to_xe(gt));
- if (xe_gt_is_media_type(gt))
- reg = xe_mmio_read32(&gt->mmio, MTL_MEDIA_PERF_LIMIT_REASONS);
- else
- reg = xe_mmio_read32(&gt->mmio, GT0_PERF_LIMIT_REASONS);
- xe_pm_runtime_put(gt_to_xe(gt));
-
- return reg;
-}
-
-static u32 read_status(struct xe_gt *gt)
-{
- u32 status = xe_gt_throttle_get_limit_reasons(gt) & GT0_PERF_LIMIT_REASONS_MASK;
-
- xe_gt_dbg(gt, "throttle reasons: 0x%08x\n", status);
- return status;
-}
+struct throttle_attribute {
+ struct kobj_attribute attr;
+ u32 mask;
+};
-static u32 read_reason_pl1(struct xe_gt *gt)
+static struct xe_gt *dev_to_gt(struct device *dev)
{
- u32 pl1 = xe_gt_throttle_get_limit_reasons(gt) & POWER_LIMIT_1_MASK;
-
- return pl1;
+ return kobj_to_gt(dev->kobj.parent);
}
-static u32 read_reason_pl2(struct xe_gt *gt)
+static struct xe_gt *throttle_to_gt(struct kobject *kobj)
{
- u32 pl2 = xe_gt_throttle_get_limit_reasons(gt) & POWER_LIMIT_2_MASK;
-
- return pl2;
+ return dev_to_gt(kobj_to_dev(kobj));
}
-static u32 read_reason_pl4(struct xe_gt *gt)
+static struct throttle_attribute *kobj_attribute_to_throttle(struct kobj_attribute *attr)
{
- u32 pl4 = xe_gt_throttle_get_limit_reasons(gt) & POWER_LIMIT_4_MASK;
-
- return pl4;
+ return container_of(attr, struct throttle_attribute, attr);
}
-static u32 read_reason_thermal(struct xe_gt *gt)
-{
- u32 thermal = xe_gt_throttle_get_limit_reasons(gt) & THERMAL_LIMIT_MASK;
-
- return thermal;
-}
-
-static u32 read_reason_prochot(struct xe_gt *gt)
+u32 xe_gt_throttle_get_limit_reasons(struct xe_gt *gt)
{
- u32 prochot = xe_gt_throttle_get_limit_reasons(gt) & PROCHOT_MASK;
-
- return prochot;
-}
+ struct xe_device *xe = gt_to_xe(gt);
+ struct xe_reg reg;
+ u32 val, mask;
-static u32 read_reason_ratl(struct xe_gt *gt)
-{
- u32 ratl = xe_gt_throttle_get_limit_reasons(gt) & RATL_MASK;
+ if (xe_gt_is_media_type(gt))
+ reg = MTL_MEDIA_PERF_LIMIT_REASONS;
+ else
+ reg = GT0_PERF_LIMIT_REASONS;
- return ratl;
-}
+ if (xe->info.platform == XE_CRESCENTISLAND)
+ mask = CRI_PERF_LIMIT_REASONS_MASK;
+ else
+ mask = GT0_PERF_LIMIT_REASONS_MASK;
-static u32 read_reason_vr_thermalert(struct xe_gt *gt)
-{
- u32 thermalert = xe_gt_throttle_get_limit_reasons(gt) & VR_THERMALERT_MASK;
+ xe_pm_runtime_get(xe);
+ val = xe_mmio_read32(&gt->mmio, reg) & mask;
+ xe_pm_runtime_put(xe);
- return thermalert;
+ return val;
}
-static u32 read_reason_vr_tdc(struct xe_gt *gt)
+static bool is_throttled_by(struct xe_gt *gt, u32 mask)
{
- u32 tdc = xe_gt_throttle_get_limit_reasons(gt) & VR_TDC_MASK;
-
- return tdc;
+ return xe_gt_throttle_get_limit_reasons(gt) & mask;
}
-static ssize_t status_show(struct kobject *kobj,
+static ssize_t reason_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buff)
{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool status = !!read_status(gt);
+ struct throttle_attribute *ta = kobj_attribute_to_throttle(attr);
+ struct xe_gt *gt = throttle_to_gt(kobj);
- return sysfs_emit(buff, "%u\n", status);
+ return sysfs_emit(buff, "%u\n", is_throttled_by(gt, ta->mask));
}
-static struct kobj_attribute attr_status = __ATTR_RO(status);
-static ssize_t reason_pl1_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool pl1 = !!read_reason_pl1(gt);
+static const struct attribute_group *get_platform_throttle_group(struct xe_device *xe);
- return sysfs_emit(buff, "%u\n", pl1);
-}
-static struct kobj_attribute attr_reason_pl1 = __ATTR_RO(reason_pl1);
-
-static ssize_t reason_pl2_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
+static ssize_t reasons_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buff)
{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool pl2 = !!read_reason_pl2(gt);
+ struct xe_gt *gt = throttle_to_gt(kobj);
+ struct xe_device *xe = gt_to_xe(gt);
+ const struct attribute_group *group;
+ struct attribute **pother;
+ ssize_t ret = 0;
+ u32 reasons;
- return sysfs_emit(buff, "%u\n", pl2);
-}
-static struct kobj_attribute attr_reason_pl2 = __ATTR_RO(reason_pl2);
+ reasons = xe_gt_throttle_get_limit_reasons(gt);
+ if (!reasons)
+ goto ret_none;
-static ssize_t reason_pl4_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool pl4 = !!read_reason_pl4(gt);
+ group = get_platform_throttle_group(xe);
+ for (pother = group->attrs; *pother; pother++) {
+ struct kobj_attribute *kattr = container_of(*pother, struct kobj_attribute, attr);
+ struct throttle_attribute *other_ta = kobj_attribute_to_throttle(kattr);
- return sysfs_emit(buff, "%u\n", pl4);
-}
-static struct kobj_attribute attr_reason_pl4 = __ATTR_RO(reason_pl4);
+ if (other_ta->mask != U32_MAX && reasons & other_ta->mask)
+ ret += sysfs_emit_at(buff, ret, "%s ", (*pother)->name);
+ }
-static ssize_t reason_thermal_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool thermal = !!read_reason_thermal(gt);
+ if (drm_WARN_ONCE(&xe->drm, !ret, "Unknown reason: %#x\n", reasons))
+ goto ret_none;
- return sysfs_emit(buff, "%u\n", thermal);
-}
-static struct kobj_attribute attr_reason_thermal = __ATTR_RO(reason_thermal);
+ /* Drop extra space from last iteration above */
+ ret--;
+ ret += sysfs_emit_at(buff, ret, "\n");
-static ssize_t reason_prochot_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool prochot = !!read_reason_prochot(gt);
+ return ret;
- return sysfs_emit(buff, "%u\n", prochot);
+ret_none:
+ return sysfs_emit(buff, "none\n");
}
-static struct kobj_attribute attr_reason_prochot = __ATTR_RO(reason_prochot);
-static ssize_t reason_ratl_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool ratl = !!read_reason_ratl(gt);
-
- return sysfs_emit(buff, "%u\n", ratl);
-}
-static struct kobj_attribute attr_reason_ratl = __ATTR_RO(reason_ratl);
-
-static ssize_t reason_vr_thermalert_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool thermalert = !!read_reason_vr_thermalert(gt);
-
- return sysfs_emit(buff, "%u\n", thermalert);
-}
-static struct kobj_attribute attr_reason_vr_thermalert = __ATTR_RO(reason_vr_thermalert);
-
-static ssize_t reason_vr_tdc_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buff)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct xe_gt *gt = dev_to_gt(dev);
- bool tdc = !!read_reason_vr_tdc(gt);
-
- return sysfs_emit(buff, "%u\n", tdc);
-}
-static struct kobj_attribute attr_reason_vr_tdc = __ATTR_RO(reason_vr_tdc);
+#define THROTTLE_ATTR_RO(name, _mask) \
+ struct throttle_attribute attr_##name = { \
+ .attr = __ATTR(name, 0444, reason_show, NULL), \
+ .mask = _mask, \
+ }
+
+#define THROTTLE_ATTR_RO_FUNC(name, _mask, _show) \
+ struct throttle_attribute attr_##name = { \
+ .attr = __ATTR(name, 0444, _show, NULL), \
+ .mask = _mask, \
+ }
+
+static THROTTLE_ATTR_RO_FUNC(reasons, 0, reasons_show);
+static THROTTLE_ATTR_RO(status, U32_MAX);
+static THROTTLE_ATTR_RO(reason_pl1, POWER_LIMIT_1_MASK);
+static THROTTLE_ATTR_RO(reason_pl2, POWER_LIMIT_2_MASK);
+static THROTTLE_ATTR_RO(reason_pl4, POWER_LIMIT_4_MASK);
+static THROTTLE_ATTR_RO(reason_thermal, THERMAL_LIMIT_MASK);
+static THROTTLE_ATTR_RO(reason_prochot, PROCHOT_MASK);
+static THROTTLE_ATTR_RO(reason_ratl, RATL_MASK);
+static THROTTLE_ATTR_RO(reason_vr_thermalert, VR_THERMALERT_MASK);
+static THROTTLE_ATTR_RO(reason_vr_tdc, VR_TDC_MASK);
static struct attribute *throttle_attrs[] = {
- &attr_status.attr,
- &attr_reason_pl1.attr,
- &attr_reason_pl2.attr,
- &attr_reason_pl4.attr,
- &attr_reason_thermal.attr,
- &attr_reason_prochot.attr,
- &attr_reason_ratl.attr,
- &attr_reason_vr_thermalert.attr,
- &attr_reason_vr_tdc.attr,
+ &attr_reasons.attr.attr,
+ &attr_status.attr.attr,
+ &attr_reason_pl1.attr.attr,
+ &attr_reason_pl2.attr.attr,
+ &attr_reason_pl4.attr.attr,
+ &attr_reason_thermal.attr.attr,
+ &attr_reason_prochot.attr.attr,
+ &attr_reason_ratl.attr.attr,
+ &attr_reason_vr_thermalert.attr.attr,
+ &attr_reason_vr_tdc.attr.attr,
+ NULL
+};
+
+static THROTTLE_ATTR_RO(reason_vr_thermal, VR_THERMAL_MASK);
+static THROTTLE_ATTR_RO(reason_soc_thermal, SOC_THERMAL_LIMIT_MASK);
+static THROTTLE_ATTR_RO(reason_mem_thermal, MEM_THERMAL_MASK);
+static THROTTLE_ATTR_RO(reason_iccmax, ICCMAX_MASK);
+static THROTTLE_ATTR_RO(reason_soc_avg_thermal, SOC_AVG_THERMAL_MASK);
+static THROTTLE_ATTR_RO(reason_fastvmode, FASTVMODE_MASK);
+static THROTTLE_ATTR_RO(reason_psys_pl1, PSYS_PL1_MASK);
+static THROTTLE_ATTR_RO(reason_psys_pl2, PSYS_PL2_MASK);
+static THROTTLE_ATTR_RO(reason_p0_freq, P0_FREQ_MASK);
+static THROTTLE_ATTR_RO(reason_psys_crit, PSYS_CRIT_MASK);
+
+static struct attribute *cri_throttle_attrs[] = {
+ /* Common */
+ &attr_reasons.attr.attr,
+ &attr_status.attr.attr,
+ &attr_reason_pl1.attr.attr,
+ &attr_reason_pl2.attr.attr,
+ &attr_reason_pl4.attr.attr,
+ &attr_reason_prochot.attr.attr,
+ &attr_reason_ratl.attr.attr,
+ /* CRI */
+ &attr_reason_vr_thermal.attr.attr,
+ &attr_reason_soc_thermal.attr.attr,
+ &attr_reason_mem_thermal.attr.attr,
+ &attr_reason_iccmax.attr.attr,
+ &attr_reason_soc_avg_thermal.attr.attr,
+ &attr_reason_fastvmode.attr.attr,
+ &attr_reason_psys_pl1.attr.attr,
+ &attr_reason_psys_pl2.attr.attr,
+ &attr_reason_p0_freq.attr.attr,
+ &attr_reason_psys_crit.attr.attr,
NULL
};
@@ -231,19 +232,37 @@ static const struct attribute_group throttle_group_attrs = {
.attrs = throttle_attrs,
};
+static const struct attribute_group cri_throttle_group_attrs = {
+ .name = "throttle",
+ .attrs = cri_throttle_attrs,
+};
+
+static const struct attribute_group *get_platform_throttle_group(struct xe_device *xe)
+{
+ switch (xe->info.platform) {
+ case XE_CRESCENTISLAND:
+ return &cri_throttle_group_attrs;
+ default:
+ return &throttle_group_attrs;
+ }
+}
+
static void gt_throttle_sysfs_fini(void *arg)
{
struct xe_gt *gt = arg;
+ struct xe_device *xe = gt_to_xe(gt);
+ const struct attribute_group *group = get_platform_throttle_group(xe);
- sysfs_remove_group(gt->freq, &throttle_group_attrs);
+ sysfs_remove_group(gt->freq, group);
}
int xe_gt_throttle_init(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
+ const struct attribute_group *group = get_platform_throttle_group(xe);
int err;
- err = sysfs_create_group(gt->freq, &throttle_group_attrs);
+ err = sysfs_create_group(gt->freq, group);
if (err)
return err;
diff --git a/drivers/gpu/drm/xe/xe_gt_topology.c b/drivers/gpu/drm/xe/xe_gt_topology.c
index 4e61c5e39bcb..bd5260221d8d 100644
--- a/drivers/gpu/drm/xe/xe_gt_topology.c
+++ b/drivers/gpu/drm/xe/xe_gt_topology.c
@@ -148,7 +148,11 @@ load_l3_bank_mask(struct xe_gt *gt, xe_l3_bank_mask_t l3_bank_mask)
if (!xe_gt_topology_report_l3(gt))
return;
- if (GRAPHICS_VER(xe) >= 30) {
+ if (GRAPHICS_VER(xe) >= 35) {
+ u32 fuse_val = xe_mmio_read32(mmio, MIRROR_L3BANK_ENABLE);
+
+ bitmap_from_arr32(l3_bank_mask, &fuse_val, 32);
+ } else if (GRAPHICS_VER(xe) >= 30) {
xe_l3_bank_mask_t per_node = {};
u32 meml3_en = REG_FIELD_GET(XE2_NODE_ENABLE_MASK, fuse3);
u32 mirror_l3bank_enable = xe_mmio_read32(mmio, MIRROR_L3BANK_ENABLE);
@@ -269,8 +273,14 @@ static const char *eu_type_to_str(enum xe_gt_eu_type eu_type)
return NULL;
}
-void
-xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_gt_topology_dump() - Dump GT topology into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: always 0.
+ */
+int xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p)
{
drm_printf(p, "dss mask (geometry): %*pb\n", XE_MAX_DSS_FUSE_BITS,
gt->fuse_topo.g_dss_mask);
@@ -285,6 +295,7 @@ xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p)
if (xe_gt_topology_report_l3(gt))
drm_printf(p, "L3 bank mask: %*pb\n", XE_MAX_L3_BANK_MASK_BITS,
gt->fuse_topo.l3_bank_mask);
+ return 0;
}
/*
@@ -298,6 +309,13 @@ xe_dss_mask_group_ffs(const xe_dss_mask_t mask, int groupsize, int groupnum)
return find_next_bit(mask, XE_MAX_DSS_FUSE_BITS, groupnum * groupsize);
}
+/* Used to obtain the index of the first L3 bank. */
+unsigned int
+xe_l3_bank_mask_ffs(const xe_l3_bank_mask_t mask)
+{
+ return find_first_bit(mask, XE_MAX_L3_BANK_MASK_BITS);
+}
+
/**
* xe_gt_topology_has_dss_in_quadrant - check fusing of DSS in GT quadrant
* @gt: GT to check
diff --git a/drivers/gpu/drm/xe/xe_gt_topology.h b/drivers/gpu/drm/xe/xe_gt_topology.h
index 5e62f5949b7b..162d603c9b81 100644
--- a/drivers/gpu/drm/xe/xe_gt_topology.h
+++ b/drivers/gpu/drm/xe/xe_gt_topology.h
@@ -23,7 +23,7 @@ struct drm_printer;
void xe_gt_topology_init(struct xe_gt *gt);
-void xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_gt_topology_dump(struct xe_gt *gt, struct drm_printer *p);
/**
* xe_gt_topology_mask_last_dss() - Returns the index of the last DSS in a mask.
@@ -40,6 +40,8 @@ xe_gt_topology_mask_last_dss(const xe_dss_mask_t mask)
unsigned int
xe_dss_mask_group_ffs(const xe_dss_mask_t mask, int groupsize, int groupnum);
+unsigned int
+xe_l3_bank_mask_ffs(const xe_l3_bank_mask_t mask);
bool
xe_gt_topology_has_dss_in_quadrant(struct xe_gt *gt, int quad);
diff --git a/drivers/gpu/drm/xe/xe_gt_types.h b/drivers/gpu/drm/xe/xe_gt_types.h
index 66158105aca5..0a728180b6fe 100644
--- a/drivers/gpu/drm/xe/xe_gt_types.h
+++ b/drivers/gpu/drm/xe/xe_gt_types.h
@@ -66,6 +66,7 @@ struct xe_mmio_range {
*/
enum xe_steering_type {
L3BANK,
+ NODE,
MSLICE,
LNCF,
DSS,
@@ -73,6 +74,13 @@ enum xe_steering_type {
SQIDI_PSMI,
/*
+ * Although most GAM ranges must be steered to (0,0) and thus use the
+ * INSTANCE0 type farther down, some platforms have special rules
+ * for specific subtypes that require steering to (1,0) instead.
+ */
+ GAM1,
+
+ /*
* On some platforms there are multiple types of MCR registers that
* will always return a non-terminated value at instance (0, 0). We'll
* lump those all into a single category to keep things simple.
@@ -202,81 +210,16 @@ struct xe_gt {
/**
* @usm.bb_pool: Pool from which batchbuffers, for USM operations
* (e.g. migrations, fixing page tables), are allocated.
- * Dedicated pool needed so USM operations to not get blocked
+ * Dedicated pool needed so USM operations do not get blocked
* behind any user operations which may have resulted in a
* fault.
*/
struct xe_sa_manager *bb_pool;
/**
* @usm.reserved_bcs_instance: reserved BCS instance used for USM
- * operations (e.g. mmigrations, fixing page tables)
+ * operations (e.g. migrations, fixing page tables)
*/
u16 reserved_bcs_instance;
- /** @usm.pf_wq: page fault work queue, unbound, high priority */
- struct workqueue_struct *pf_wq;
- /** @usm.acc_wq: access counter work queue, unbound, high priority */
- struct workqueue_struct *acc_wq;
- /**
- * @usm.pf_queue: Page fault queue used to sync faults so faults can
- * be processed not under the GuC CT lock. The queue is sized so
- * it can sync all possible faults (1 per physical engine).
- * Multiple queues exists for page faults from different VMs are
- * be processed in parallel.
- */
- struct pf_queue {
- /** @usm.pf_queue.gt: back pointer to GT */
- struct xe_gt *gt;
- /** @usm.pf_queue.data: data in the page fault queue */
- u32 *data;
- /**
- * @usm.pf_queue.num_dw: number of DWORDS in the page
- * fault queue. Dynamically calculated based on the number
- * of compute resources available.
- */
- u32 num_dw;
- /**
- * @usm.pf_queue.tail: tail pointer in DWs for page fault queue,
- * moved by worker which processes faults (consumer).
- */
- u16 tail;
- /**
- * @usm.pf_queue.head: head pointer in DWs for page fault queue,
- * moved by G2H handler (producer).
- */
- u16 head;
- /** @usm.pf_queue.lock: protects page fault queue */
- spinlock_t lock;
- /** @usm.pf_queue.worker: to process page faults */
- struct work_struct worker;
-#define NUM_PF_QUEUE 4
- } pf_queue[NUM_PF_QUEUE];
- /**
- * @usm.acc_queue: Same as page fault queue, cannot process access
- * counters under CT lock.
- */
- struct acc_queue {
- /** @usm.acc_queue.gt: back pointer to GT */
- struct xe_gt *gt;
-#define ACC_QUEUE_NUM_DW 128
- /** @usm.acc_queue.data: data in the page fault queue */
- u32 data[ACC_QUEUE_NUM_DW];
- /**
- * @usm.acc_queue.tail: tail pointer in DWs for access counter queue,
- * moved by worker which processes counters
- * (consumer).
- */
- u16 tail;
- /**
- * @usm.acc_queue.head: head pointer in DWs for access counter queue,
- * moved by G2H handler (producer).
- */
- u16 head;
- /** @usm.acc_queue.lock: protects page fault queue */
- spinlock_t lock;
- /** @usm.acc_queue.worker: to process access counters */
- struct work_struct worker;
-#define NUM_ACC_QUEUE 4
- } acc_queue[NUM_ACC_QUEUE];
} usm;
/** @ordered_wq: used to serialize GT resets and TDRs */
@@ -387,7 +330,7 @@ struct xe_gt {
/**
* @wa_active.oob_initialized: mark oob as initialized to help
* detecting misuse of XE_GT_WA() - it can only be called on
- * initialization after OOB WAs have being processed
+ * initialization after OOB WAs have been processed
*/
bool oob_initialized;
} wa_active;
diff --git a/drivers/gpu/drm/xe/xe_guard.h b/drivers/gpu/drm/xe/xe_guard.h
new file mode 100644
index 000000000000..333f8e13b5a1
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guard.h
@@ -0,0 +1,119 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_GUARD_H_
+#define _XE_GUARD_H_
+
+#include <linux/spinlock.h>
+
+/**
+ * struct xe_guard - Simple logic to protect a feature.
+ *
+ * Implements simple semaphore-like logic that can be used to lockdown the
+ * feature unless it is already in use. Allows enabling of the otherwise
+ * incompatible features, where we can't follow the strict owner semantics
+ * required by the &rw_semaphore.
+ *
+ * NOTE! It shouldn't be used to protect a data, use &rw_semaphore instead.
+ */
+struct xe_guard {
+ /**
+ * @counter: implements simple exclusive/lockdown logic:
+ * if == 0 then guard/feature is idle/not in use,
+ * if < 0 then feature is active and can't be locked-down,
+ * if > 0 then feature is lockded-down and can't be activated.
+ */
+ int counter;
+
+ /** @name: the name of the guard (useful for debug) */
+ const char *name;
+
+ /** @owner: the info about the last owner of the guard (for debug) */
+ void *owner;
+
+ /** @lock: protects guard's data */
+ spinlock_t lock;
+};
+
+/**
+ * xe_guard_init() - Initialize the guard.
+ * @guard: the &xe_guard to init
+ * @name: name of the guard
+ */
+static inline void xe_guard_init(struct xe_guard *guard, const char *name)
+{
+ spin_lock_init(&guard->lock);
+ guard->counter = 0;
+ guard->name = name;
+}
+
+/**
+ * xe_guard_arm() - Arm the guard for the exclusive/lockdown mode.
+ * @guard: the &xe_guard to arm
+ * @lockdown: arm for lockdown(true) or exclusive(false) mode
+ * @who: optional owner info (for debug only)
+ *
+ * Multiple lockdown requests are allowed.
+ * Only single exclusive access can be granted.
+ * Will fail if the guard is already in exclusive mode.
+ * On success, must call the xe_guard_disarm() to release.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static inline int xe_guard_arm(struct xe_guard *guard, bool lockdown, void *who)
+{
+ guard(spinlock)(&guard->lock);
+
+ if (lockdown) {
+ if (guard->counter < 0)
+ return -EBUSY;
+ guard->counter++;
+ } else {
+ if (guard->counter > 0)
+ return -EPERM;
+ if (guard->counter < 0)
+ return -EUSERS;
+ guard->counter--;
+ }
+
+ guard->owner = who;
+ return 0;
+}
+
+/**
+ * xe_guard_disarm() - Disarm the guard from exclusive/lockdown mode.
+ * @guard: the &xe_guard to disarm
+ * @lockdown: disarm from lockdown(true) or exclusive(false) mode
+ *
+ * Return: true if successfully disarmed or false in case of mismatch.
+ */
+static inline bool xe_guard_disarm(struct xe_guard *guard, bool lockdown)
+{
+ guard(spinlock)(&guard->lock);
+
+ if (lockdown) {
+ if (guard->counter <= 0)
+ return false;
+ guard->counter--;
+ } else {
+ if (guard->counter != -1)
+ return false;
+ guard->counter++;
+ }
+ return true;
+}
+
+/**
+ * xe_guard_mode_str() - Convert guard mode into a string.
+ * @lockdown: flag used to select lockdown or exclusive mode
+ *
+ * Return: "lockdown" or "exclusive" string.
+ */
+static inline const char *xe_guard_mode_str(bool lockdown)
+{
+ return lockdown ? "lockdown" : "exclusive";
+}
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc.c b/drivers/gpu/drm/xe/xe_guc.c
index 00789844ea4d..a686b04879d6 100644
--- a/drivers/gpu/drm/xe/xe_guc.c
+++ b/drivers/gpu/drm/xe/xe_guc.c
@@ -5,6 +5,7 @@
#include "xe_guc.h"
+#include <linux/iopoll.h>
#include <drm/drm_managed.h>
#include <generated/xe_wa_oob.h>
@@ -23,6 +24,7 @@
#include "xe_gt_printk.h"
#include "xe_gt_sriov_vf.h"
#include "xe_gt_throttle.h"
+#include "xe_gt_sriov_pf_migration.h"
#include "xe_guc_ads.h"
#include "xe_guc_buf.h"
#include "xe_guc_capture.h"
@@ -39,6 +41,7 @@
#include "xe_mmio.h"
#include "xe_platform_types.h"
#include "xe_sriov.h"
+#include "xe_sriov_pf_migration.h"
#include "xe_uc.h"
#include "xe_uc_fw.h"
#include "xe_wa.h"
@@ -90,6 +93,9 @@ static u32 guc_ctl_feature_flags(struct xe_guc *guc)
if (xe_configfs_get_psmi_enabled(to_pci_dev(xe->drm.dev)))
flags |= GUC_CTL_ENABLE_PSMI_LOGGING;
+ if (xe_guc_using_main_gamctrl_queues(guc))
+ flags |= GUC_CTL_MAIN_GAMCTRL_QUEUES;
+
return flags;
}
@@ -817,6 +823,14 @@ static int vf_guc_init_post_hwconfig(struct xe_guc *guc)
return 0;
}
+static u32 guc_additional_cache_size(struct xe_device *xe)
+{
+ if (IS_SRIOV_PF(xe) && xe_sriov_pf_migration_supported(xe))
+ return XE_GT_SRIOV_PF_MIGRATION_GUC_DATA_MAX_SIZE;
+ else
+ return 0; /* Fallback to default size */
+}
+
/**
* xe_guc_init_post_hwconfig - initialize GuC post hwconfig load
* @guc: The GuC object
@@ -856,7 +870,8 @@ int xe_guc_init_post_hwconfig(struct xe_guc *guc)
if (ret)
return ret;
- ret = xe_guc_buf_cache_init(&guc->buf);
+ ret = xe_guc_buf_cache_init_with_size(&guc->buf,
+ guc_additional_cache_size(guc_to_xe(guc)));
if (ret)
return ret;
@@ -971,20 +986,93 @@ static int guc_xfer_rsa(struct xe_guc *guc)
}
/*
- * Check a previously read GuC status register (GUC_STATUS) looking for
- * known terminal states (either completion or failure) of either the
- * microkernel status field or the boot ROM status field. Returns +1 for
- * successful completion, -1 for failure and 0 for any intermediate state.
+ * Wait for the GuC to start up.
+ *
+ * Measurements indicate this should take no more than 20ms (assuming the GT
+ * clock is at maximum frequency). However, thermal throttling and other issues
+ * can prevent the clock hitting max and thus making the load take significantly
+ * longer. Allow up to 3s as a safety margin in normal builds. For
+ * CONFIG_DRM_XE_DEBUG allow up to 10s to account for slower execution, issues
+ * in PCODE, driver, fan, etc.
+ *
+ * Keep checking the GUC_STATUS every 10ms with a debug message every 100
+ * attempts as a "I'm slow, but alive" message. Regardless, if it takes more
+ * than 200ms, emit a warning.
+ */
+
+#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
+#define GUC_LOAD_TIMEOUT_SEC 20
+#else
+#define GUC_LOAD_TIMEOUT_SEC 3
+#endif
+#define GUC_LOAD_TIME_WARN_MSEC 200
+
+static void print_load_status_err(struct xe_gt *gt, u32 status)
+{
+ struct xe_mmio *mmio = &gt->mmio;
+ u32 ukernel = REG_FIELD_GET(GS_UKERNEL_MASK, status);
+ u32 bootrom = REG_FIELD_GET(GS_BOOTROM_MASK, status);
+
+ xe_gt_err(gt, "load failed: status: Reset = %d, BootROM = 0x%02X, UKernel = 0x%02X, MIA = 0x%02X, Auth = 0x%02X\n",
+ REG_FIELD_GET(GS_MIA_IN_RESET, status),
+ bootrom, ukernel,
+ REG_FIELD_GET(GS_MIA_MASK, status),
+ REG_FIELD_GET(GS_AUTH_STATUS_MASK, status));
+
+ switch (bootrom) {
+ case XE_BOOTROM_STATUS_NO_KEY_FOUND:
+ xe_gt_err(gt, "invalid key requested, header = 0x%08X\n",
+ xe_mmio_read32(mmio, GUC_HEADER_INFO));
+ break;
+ case XE_BOOTROM_STATUS_RSA_FAILED:
+ xe_gt_err(gt, "firmware signature verification failed\n");
+ break;
+ case XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE:
+ xe_gt_err(gt, "firmware production part check failure\n");
+ break;
+ }
+
+ switch (ukernel) {
+ case XE_GUC_LOAD_STATUS_HWCONFIG_START:
+ xe_gt_err(gt, "still extracting hwconfig table.\n");
+ break;
+ case XE_GUC_LOAD_STATUS_EXCEPTION:
+ xe_gt_err(gt, "firmware exception. EIP: %#x\n",
+ xe_mmio_read32(mmio, SOFT_SCRATCH(13)));
+ break;
+ case XE_GUC_LOAD_STATUS_INIT_DATA_INVALID:
+ xe_gt_err(gt, "illegal init/ADS data\n");
+ break;
+ case XE_GUC_LOAD_STATUS_INIT_MMIO_SAVE_RESTORE_INVALID:
+ xe_gt_err(gt, "illegal register in save/restore workaround list\n");
+ break;
+ case XE_GUC_LOAD_STATUS_KLV_WORKAROUND_INIT_ERROR:
+ xe_gt_err(gt, "illegal workaround KLV data\n");
+ break;
+ case XE_GUC_LOAD_STATUS_INVALID_FTR_FLAG:
+ xe_gt_err(gt, "illegal feature flag specified\n");
+ break;
+ }
+}
+
+/*
+ * Check GUC_STATUS looking for known terminal states (either completion or
+ * failure) of either the microkernel status field or the boot ROM status field.
+ *
+ * Returns 1 for successful completion, -1 for failure and 0 for any
+ * intermediate state.
*/
-static int guc_load_done(u32 status)
+static int guc_load_done(struct xe_gt *gt, u32 *status, u32 *tries)
{
- u32 uk_val = REG_FIELD_GET(GS_UKERNEL_MASK, status);
- u32 br_val = REG_FIELD_GET(GS_BOOTROM_MASK, status);
+ u32 ukernel, bootrom;
+
+ *status = xe_mmio_read32(&gt->mmio, GUC_STATUS);
+ ukernel = REG_FIELD_GET(GS_UKERNEL_MASK, *status);
+ bootrom = REG_FIELD_GET(GS_BOOTROM_MASK, *status);
- switch (uk_val) {
+ switch (ukernel) {
case XE_GUC_LOAD_STATUS_READY:
return 1;
-
case XE_GUC_LOAD_STATUS_ERROR_DEVID_BUILD_MISMATCH:
case XE_GUC_LOAD_STATUS_GUC_PREPROD_BUILD_MISMATCH:
case XE_GUC_LOAD_STATUS_ERROR_DEVID_INVALID_GUCTYPE:
@@ -1000,7 +1088,7 @@ static int guc_load_done(u32 status)
return -1;
}
- switch (br_val) {
+ switch (bootrom) {
case XE_BOOTROM_STATUS_NO_KEY_FOUND:
case XE_BOOTROM_STATUS_RSA_FAILED:
case XE_BOOTROM_STATUS_PAVPC_FAILED:
@@ -1014,165 +1102,58 @@ static int guc_load_done(u32 status)
return -1;
}
- return 0;
-}
+ if (++*tries >= 100) {
+ struct xe_guc_pc *guc_pc = &gt->uc.guc.pc;
-static s32 guc_pc_get_cur_freq(struct xe_guc_pc *guc_pc)
-{
- u32 freq;
- int ret = xe_guc_pc_get_cur_freq(guc_pc, &freq);
+ *tries = 0;
+ xe_gt_dbg(gt, "GuC load still in progress, freq = %dMHz (req %dMHz), status = 0x%08X [0x%02X/%02X]\n",
+ xe_guc_pc_get_act_freq(guc_pc),
+ xe_guc_pc_get_cur_freq_fw(guc_pc),
+ *status, ukernel, bootrom);
+ }
- return ret ? ret : freq;
+ return 0;
}
-/*
- * Wait for the GuC to start up.
- *
- * Measurements indicate this should take no more than 20ms (assuming the GT
- * clock is at maximum frequency). However, thermal throttling and other issues
- * can prevent the clock hitting max and thus making the load take significantly
- * longer. Allow up to 200ms as a safety margin for real world worst case situations.
- *
- * However, bugs anywhere from KMD to GuC to PCODE to fan failure in a CI farm can
- * lead to even longer times. E.g. if the GT is clamped to minimum frequency then
- * the load times can be in the seconds range. So the timeout is increased for debug
- * builds to ensure that problems can be correctly analysed. For release builds, the
- * timeout is kept short so that users don't wait forever to find out that there is a
- * problem. In either case, if the load took longer than is reasonable even with some
- * 'sensible' throttling, then flag a warning because something is not right.
- *
- * Note that there is a limit on how long an individual usleep_range() can wait for,
- * hence longer waits require wrapping a shorter wait in a loop.
- *
- * Note that the only reason an end user should hit the shorter timeout is in case of
- * extreme thermal throttling. And a system that is that hot during boot is probably
- * dead anyway!
- */
-#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
-#define GUC_LOAD_RETRY_LIMIT 20
-#else
-#define GUC_LOAD_RETRY_LIMIT 3
-#endif
-#define GUC_LOAD_TIME_WARN_MS 200
-
static int guc_wait_ucode(struct xe_guc *guc)
{
struct xe_gt *gt = guc_to_gt(guc);
- struct xe_mmio *mmio = &gt->mmio;
struct xe_guc_pc *guc_pc = &gt->uc.guc.pc;
- ktime_t before, after, delta;
- int load_done;
- u32 status = 0;
- int count = 0;
+ u32 before_freq, act_freq, cur_freq;
+ u32 status = 0, tries = 0;
+ ktime_t before;
u64 delta_ms;
- u32 before_freq;
+ int ret;
before_freq = xe_guc_pc_get_act_freq(guc_pc);
before = ktime_get();
- /*
- * Note, can't use any kind of timing information from the call to xe_mmio_wait.
- * It could return a thousand intermediate stages at random times. Instead, must
- * manually track the total time taken and locally implement the timeout.
- */
- do {
- u32 last_status = status & (GS_UKERNEL_MASK | GS_BOOTROM_MASK);
- int ret;
-
- /*
- * Wait for any change (intermediate or terminal) in the status register.
- * Note, the return value is a don't care. The only failure code is timeout
- * but the timeouts need to be accumulated over all the intermediate partial
- * timeouts rather than allowing a huge timeout each time. So basically, need
- * to treat a timeout no different to a value change.
- */
- ret = xe_mmio_wait32_not(mmio, GUC_STATUS, GS_UKERNEL_MASK | GS_BOOTROM_MASK,
- last_status, 1000 * 1000, &status, false);
- if (ret < 0)
- count++;
- after = ktime_get();
- delta = ktime_sub(after, before);
- delta_ms = ktime_to_ms(delta);
-
- load_done = guc_load_done(status);
- if (load_done != 0)
- break;
- if (delta_ms >= (GUC_LOAD_RETRY_LIMIT * 1000))
- break;
+ ret = poll_timeout_us(ret = guc_load_done(gt, &status, &tries), ret,
+ 10 * USEC_PER_MSEC,
+ GUC_LOAD_TIMEOUT_SEC * USEC_PER_SEC, false);
- xe_gt_dbg(gt, "load still in progress, timeouts = %d, freq = %dMHz (req %dMHz), status = 0x%08X [0x%02X/%02X]\n",
- count, xe_guc_pc_get_act_freq(guc_pc),
- guc_pc_get_cur_freq(guc_pc), status,
- REG_FIELD_GET(GS_BOOTROM_MASK, status),
- REG_FIELD_GET(GS_UKERNEL_MASK, status));
- } while (1);
+ delta_ms = ktime_to_ms(ktime_sub(ktime_get(), before));
+ act_freq = xe_guc_pc_get_act_freq(guc_pc);
+ cur_freq = xe_guc_pc_get_cur_freq_fw(guc_pc);
- if (load_done != 1) {
- u32 ukernel = REG_FIELD_GET(GS_UKERNEL_MASK, status);
- u32 bootrom = REG_FIELD_GET(GS_BOOTROM_MASK, status);
-
- xe_gt_err(gt, "load failed: status = 0x%08X, time = %lldms, freq = %dMHz (req %dMHz), done = %d\n",
+ if (ret) {
+ xe_gt_err(gt, "load failed: status = 0x%08X, time = %lldms, freq = %dMHz (req %dMHz)\n",
status, delta_ms, xe_guc_pc_get_act_freq(guc_pc),
- guc_pc_get_cur_freq(guc_pc), load_done);
- xe_gt_err(gt, "load failed: status: Reset = %d, BootROM = 0x%02X, UKernel = 0x%02X, MIA = 0x%02X, Auth = 0x%02X\n",
- REG_FIELD_GET(GS_MIA_IN_RESET, status),
- bootrom, ukernel,
- REG_FIELD_GET(GS_MIA_MASK, status),
- REG_FIELD_GET(GS_AUTH_STATUS_MASK, status));
-
- switch (bootrom) {
- case XE_BOOTROM_STATUS_NO_KEY_FOUND:
- xe_gt_err(gt, "invalid key requested, header = 0x%08X\n",
- xe_mmio_read32(mmio, GUC_HEADER_INFO));
- break;
-
- case XE_BOOTROM_STATUS_RSA_FAILED:
- xe_gt_err(gt, "firmware signature verification failed\n");
- break;
-
- case XE_BOOTROM_STATUS_PROD_KEY_CHECK_FAILURE:
- xe_gt_err(gt, "firmware production part check failure\n");
- break;
- }
-
- switch (ukernel) {
- case XE_GUC_LOAD_STATUS_HWCONFIG_START:
- xe_gt_err(gt, "still extracting hwconfig table.\n");
- break;
-
- case XE_GUC_LOAD_STATUS_EXCEPTION:
- xe_gt_err(gt, "firmware exception. EIP: %#x\n",
- xe_mmio_read32(mmio, SOFT_SCRATCH(13)));
- break;
-
- case XE_GUC_LOAD_STATUS_INIT_DATA_INVALID:
- xe_gt_err(gt, "illegal init/ADS data\n");
- break;
-
- case XE_GUC_LOAD_STATUS_INIT_MMIO_SAVE_RESTORE_INVALID:
- xe_gt_err(gt, "illegal register in save/restore workaround list\n");
- break;
-
- case XE_GUC_LOAD_STATUS_KLV_WORKAROUND_INIT_ERROR:
- xe_gt_err(gt, "illegal workaround KLV data\n");
- break;
-
- case XE_GUC_LOAD_STATUS_INVALID_FTR_FLAG:
- xe_gt_err(gt, "illegal feature flag specified\n");
- break;
- }
+ xe_guc_pc_get_cur_freq_fw(guc_pc));
+ print_load_status_err(gt, status);
return -EPROTO;
- } else if (delta_ms > GUC_LOAD_TIME_WARN_MS) {
- xe_gt_warn(gt, "excessive init time: %lldms! [status = 0x%08X, timeouts = %d]\n",
- delta_ms, status, count);
- xe_gt_warn(gt, "excessive init time: [freq = %dMHz (req = %dMHz), before = %dMHz, perf_limit_reasons = 0x%08X]\n",
- xe_guc_pc_get_act_freq(guc_pc), guc_pc_get_cur_freq(guc_pc),
- before_freq, xe_gt_throttle_get_limit_reasons(gt));
+ }
+
+ if (delta_ms > GUC_LOAD_TIME_WARN_MSEC) {
+ xe_gt_warn(gt, "GuC load: excessive init time: %lldms! [status = 0x%08X]\n",
+ delta_ms, status);
+ xe_gt_warn(gt, "GuC load: excessive init time: [freq = %dMHz (req = %dMHz), before = %dMHz, perf_limit_reasons = 0x%08X]\n",
+ act_freq, cur_freq, before_freq,
+ xe_gt_throttle_get_limit_reasons(gt));
} else {
- xe_gt_dbg(gt, "init took %lldms, freq = %dMHz (req = %dMHz), before = %dMHz, status = 0x%08X, timeouts = %d\n",
- delta_ms, xe_guc_pc_get_act_freq(guc_pc), guc_pc_get_cur_freq(guc_pc),
- before_freq, status, count);
+ xe_gt_dbg(gt, "GuC load: init took %lldms, freq = %dMHz (req = %dMHz), before = %dMHz, status = 0x%08X\n",
+ delta_ms, act_freq, cur_freq, before_freq, status);
}
return 0;
@@ -1288,8 +1269,13 @@ int xe_guc_min_load_for_hwconfig(struct xe_guc *guc)
int xe_guc_upload(struct xe_guc *guc)
{
+ struct xe_gt *gt = guc_to_gt(guc);
+
xe_guc_ads_populate(&guc->ads);
+ if (xe_guc_using_main_gamctrl_queues(guc))
+ xe_mmio_write32(&gt->mmio, MAIN_GAMCTRL_MODE, MAIN_GAMCTRL_QUEUE_SELECT);
+
return __xe_guc_upload(guc);
}
@@ -1472,7 +1458,7 @@ timeout:
BUILD_BUG_ON((GUC_HXG_TYPE_RESPONSE_SUCCESS ^ GUC_HXG_TYPE_RESPONSE_FAILURE) != 1);
ret = xe_mmio_wait32(mmio, reply_reg, resp_mask, resp_mask,
- 1000000, &header, false);
+ 2000000, &header, false);
if (unlikely(FIELD_GET(GUC_HXG_MSG_0_ORIGIN, header) !=
GUC_HXG_ORIGIN_GUC))
@@ -1690,6 +1676,44 @@ void xe_guc_declare_wedged(struct xe_guc *guc)
xe_guc_submit_wedge(guc);
}
+/**
+ * xe_guc_using_main_gamctrl_queues() - Detect which reporting queues to use.
+ * @guc: The GuC object
+ *
+ * For Xe3p and beyond, we want to program the hardware to use the
+ * "Main GAMCTRL queue" rather than the legacy queue before we upload
+ * the GuC firmware. This will allow the GuC to use a new set of
+ * registers for pagefault handling and avoid some unnecessary
+ * complications with MCR register range handling.
+ *
+ * Return: true if can use new main gamctrl queues.
+ */
+bool xe_guc_using_main_gamctrl_queues(struct xe_guc *guc)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+
+ /*
+ * For Xe3p media gt (35), the GuC and the CS subunits may be still Xe3
+ * that lacks the Main GAMCTRL support. Reserved bits from the GMD_ID
+ * inform the IP version of the subunits.
+ */
+ if (xe_gt_is_media_type(gt) && MEDIA_VER(gt_to_xe(gt)) == 35) {
+ u32 val = xe_mmio_read32(&gt->mmio, GMD_ID);
+ u32 subip = REG_FIELD_GET(GMD_ID_SUBIP_FLAG_MASK, val);
+
+ if (!subip)
+ return true;
+
+ xe_gt_WARN(gt, subip != 1,
+ "GMD_ID has unknown value in the SUBIP_FLAG field - 0x%x\n",
+ subip);
+
+ return false;
+ }
+
+ return GT_VER(gt) >= 35;
+}
+
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_guc_g2g_test.c"
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc.h b/drivers/gpu/drm/xe/xe_guc.h
index 1cca05967e62..e2d4c5f44ae3 100644
--- a/drivers/gpu/drm/xe/xe_guc.h
+++ b/drivers/gpu/drm/xe/xe_guc.h
@@ -52,6 +52,7 @@ void xe_guc_stop_prepare(struct xe_guc *guc);
void xe_guc_stop(struct xe_guc *guc);
int xe_guc_start(struct xe_guc *guc);
void xe_guc_declare_wedged(struct xe_guc *guc);
+bool xe_guc_using_main_gamctrl_queues(struct xe_guc *guc);
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
int xe_guc_g2g_test_notification(struct xe_guc *guc, u32 *payload, u32 len);
diff --git a/drivers/gpu/drm/xe/xe_guc_ads.c b/drivers/gpu/drm/xe/xe_guc_ads.c
index 58e0b0294a5b..bcb85a1bf26d 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads.c
+++ b/drivers/gpu/drm/xe/xe_guc_ads.c
@@ -18,6 +18,7 @@
#include "xe_bo.h"
#include "xe_gt.h"
#include "xe_gt_ccs_mode.h"
+#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_guc_buf.h"
@@ -30,7 +31,6 @@
#include "xe_platform_types.h"
#include "xe_uc_fw.h"
#include "xe_wa.h"
-#include "xe_gt_mcr.h"
/* Slack of a few additional entries per engine */
#define ADS_REGSET_EXTRA_MAX 8
@@ -820,16 +820,20 @@ static void guc_mmio_reg_state_init(struct xe_guc_ads *ads)
static void guc_um_init_params(struct xe_guc_ads *ads)
{
u32 um_queue_offset = guc_ads_um_queues_offset(ads);
+ struct xe_guc *guc = ads_to_guc(ads);
u64 base_dpa;
u32 base_ggtt;
+ bool with_dpa;
int i;
+ with_dpa = !xe_guc_using_main_gamctrl_queues(guc);
+
base_ggtt = xe_bo_ggtt_addr(ads->bo) + um_queue_offset;
base_dpa = xe_bo_main_addr(ads->bo, PAGE_SIZE) + um_queue_offset;
for (i = 0; i < GUC_UM_HW_QUEUE_MAX; ++i) {
ads_blob_write(ads, um_init_params.queue_params[i].base_dpa,
- base_dpa + (i * GUC_UM_QUEUE_SIZE));
+ with_dpa ? (base_dpa + (i * GUC_UM_QUEUE_SIZE)) : 0);
ads_blob_write(ads, um_init_params.queue_params[i].base_ggtt_address,
base_ggtt + (i * GUC_UM_QUEUE_SIZE));
ads_blob_write(ads, um_init_params.queue_params[i].size_in_bytes,
diff --git a/drivers/gpu/drm/xe/xe_guc_ads_types.h b/drivers/gpu/drm/xe/xe_guc_ads_types.h
index 70c132458ac3..48a8e092023f 100644
--- a/drivers/gpu/drm/xe/xe_guc_ads_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_ads_types.h
@@ -14,7 +14,7 @@ struct xe_bo;
* struct xe_guc_ads - GuC additional data structures (ADS)
*/
struct xe_guc_ads {
- /** @bo: XE BO for GuC ads blob */
+ /** @bo: Xe BO for GuC ads blob */
struct xe_bo *bo;
/** @golden_lrc_size: golden LRC size */
size_t golden_lrc_size;
diff --git a/drivers/gpu/drm/xe/xe_guc_buf.c b/drivers/gpu/drm/xe/xe_guc_buf.c
index 502ca3a4ee60..3ce442500130 100644
--- a/drivers/gpu/drm/xe/xe_guc_buf.c
+++ b/drivers/gpu/drm/xe/xe_guc_buf.c
@@ -13,6 +13,8 @@
#include "xe_guc_buf.h"
#include "xe_sa.h"
+#define XE_GUC_BUF_CACHE_DEFAULT_SIZE SZ_8K
+
static struct xe_guc *cache_to_guc(struct xe_guc_buf_cache *cache)
{
return container_of(cache, struct xe_guc, buf);
@@ -23,21 +25,12 @@ static struct xe_gt *cache_to_gt(struct xe_guc_buf_cache *cache)
return guc_to_gt(cache_to_guc(cache));
}
-/**
- * xe_guc_buf_cache_init() - Initialize the GuC Buffer Cache.
- * @cache: the &xe_guc_buf_cache to initialize
- *
- * The Buffer Cache allows to obtain a reusable buffer that can be used to pass
- * indirect H2G data to GuC without a need to create a ad-hoc allocation.
- *
- * Return: 0 on success or a negative error code on failure.
- */
-int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache)
+static int guc_buf_cache_init(struct xe_guc_buf_cache *cache, u32 size)
{
struct xe_gt *gt = cache_to_gt(cache);
struct xe_sa_manager *sam;
- sam = __xe_sa_bo_manager_init(gt_to_tile(gt), SZ_8K, 0, sizeof(u32));
+ sam = __xe_sa_bo_manager_init(gt_to_tile(gt), size, 0, sizeof(u32));
if (IS_ERR(sam))
return PTR_ERR(sam);
cache->sam = sam;
@@ -49,6 +42,35 @@ int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache)
}
/**
+ * xe_guc_buf_cache_init() - Initialize the GuC Buffer Cache.
+ * @cache: the &xe_guc_buf_cache to initialize
+ *
+ * The Buffer Cache allows to obtain a reusable buffer that can be used to pass
+ * data to GuC or read data from GuC without a need to create a ad-hoc allocation.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache)
+{
+ return guc_buf_cache_init(cache, XE_GUC_BUF_CACHE_DEFAULT_SIZE);
+}
+
+/**
+ * xe_guc_buf_cache_init_with_size() - Initialize the GuC Buffer Cache.
+ * @cache: the &xe_guc_buf_cache to initialize
+ * @size: size in bytes
+ *
+ * Like xe_guc_buf_cache_init(), except it allows the caller to make the cache
+ * buffer larger, allowing to accommodate larger objects.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_guc_buf_cache_init_with_size(struct xe_guc_buf_cache *cache, u32 size)
+{
+ return guc_buf_cache_init(cache, max(XE_GUC_BUF_CACHE_DEFAULT_SIZE, size));
+}
+
+/**
* xe_guc_buf_cache_dwords() - Number of dwords the GuC Buffer Cache supports.
* @cache: the &xe_guc_buf_cache to query
*
@@ -116,6 +138,19 @@ void xe_guc_buf_release(const struct xe_guc_buf buf)
}
/**
+ * xe_guc_buf_sync_read() - Copy the data from the GPU memory to the sub-allocation.
+ * @buf: the &xe_guc_buf to sync
+ *
+ * Return: a CPU pointer of the sub-allocation.
+ */
+void *xe_guc_buf_sync_read(const struct xe_guc_buf buf)
+{
+ xe_sa_bo_sync_read(buf.sa);
+
+ return xe_sa_bo_cpu_addr(buf.sa);
+}
+
+/**
* xe_guc_buf_flush() - Copy the data from the sub-allocation to the GPU memory.
* @buf: the &xe_guc_buf to flush
*
diff --git a/drivers/gpu/drm/xe/xe_guc_buf.h b/drivers/gpu/drm/xe/xe_guc_buf.h
index 0d67604d96bd..e3cca553fb00 100644
--- a/drivers/gpu/drm/xe/xe_guc_buf.h
+++ b/drivers/gpu/drm/xe/xe_guc_buf.h
@@ -12,6 +12,7 @@
#include "xe_guc_buf_types.h"
int xe_guc_buf_cache_init(struct xe_guc_buf_cache *cache);
+int xe_guc_buf_cache_init_with_size(struct xe_guc_buf_cache *cache, u32 size);
u32 xe_guc_buf_cache_dwords(struct xe_guc_buf_cache *cache);
struct xe_guc_buf xe_guc_buf_reserve(struct xe_guc_buf_cache *cache, u32 dwords);
struct xe_guc_buf xe_guc_buf_from_data(struct xe_guc_buf_cache *cache,
@@ -30,6 +31,7 @@ static inline bool xe_guc_buf_is_valid(const struct xe_guc_buf buf)
}
void *xe_guc_buf_cpu_ptr(const struct xe_guc_buf buf);
+void *xe_guc_buf_sync_read(const struct xe_guc_buf buf);
u64 xe_guc_buf_flush(const struct xe_guc_buf buf);
u64 xe_guc_buf_gpu_addr(const struct xe_guc_buf buf);
u64 xe_guc_cache_gpu_addr_from_ptr(struct xe_guc_buf_cache *cache, const void *ptr, u32 size);
diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c
index 243dad3e2418..0c1fbe97b8bf 100644
--- a/drivers/gpu/drm/xe/xe_guc_capture.c
+++ b/drivers/gpu/drm/xe/xe_guc_capture.c
@@ -122,6 +122,7 @@ struct __guc_capture_parsed_output {
{ RING_IPEHR(0), REG_32BIT, 0, 0, 0, "IPEHR"}, \
{ RING_INSTDONE(0), REG_32BIT, 0, 0, 0, "RING_INSTDONE"}, \
{ INDIRECT_RING_STATE(0), REG_32BIT, 0, 0, 0, "INDIRECT_RING_STATE"}, \
+ { RING_CURRENT_LRCA(0), REG_32BIT, 0, 0, 0, "CURRENT_LRCA"}, \
{ RING_ACTHD(0), REG_64BIT_LOW_DW, 0, 0, 0, NULL}, \
{ RING_ACTHD_UDW(0), REG_64BIT_HI_DW, 0, 0, 0, "ACTHD"}, \
{ RING_BBADDR(0), REG_64BIT_LOW_DW, 0, 0, 0, NULL}, \
@@ -149,6 +150,9 @@ struct __guc_capture_parsed_output {
{ SFC_DONE(2), 0, 0, 0, 0, "SFC_DONE[2]"}, \
{ SFC_DONE(3), 0, 0, 0, 0, "SFC_DONE[3]"}
+#define XE3P_BASE_ENGINE_INSTANCE \
+ { RING_CSMQDEBUG(0), REG_32BIT, 0, 0, 0, "CSMQDEBUG"}
+
/* XE_LP Global */
static const struct __guc_mmio_reg_descr xe_lp_global_regs[] = {
COMMON_XELP_BASE_GLOBAL,
@@ -195,6 +199,12 @@ static const struct __guc_mmio_reg_descr xe_lp_gsc_inst_regs[] = {
COMMON_BASE_ENGINE_INSTANCE,
};
+/* Render / Compute Per-Engine-Instance */
+static const struct __guc_mmio_reg_descr xe3p_rc_inst_regs[] = {
+ COMMON_BASE_ENGINE_INSTANCE,
+ XE3P_BASE_ENGINE_INSTANCE,
+};
+
/*
* Empty list to prevent warnings about unknown class/instance types
* as not all class/instance types have entries on all platforms.
@@ -245,6 +255,21 @@ static const struct __guc_mmio_reg_descr_group xe_hpg_lists[] = {
{}
};
+ /* List of lists for Xe3p and beyond */
+static const struct __guc_mmio_reg_descr_group xe3p_lists[] = {
+ MAKE_REGLIST(xe_lp_global_regs, PF, GLOBAL, 0),
+ MAKE_REGLIST(xe_hpg_rc_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE),
+ MAKE_REGLIST(xe3p_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE),
+ MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEO),
+ MAKE_REGLIST(xe_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEO),
+ MAKE_REGLIST(xe_vec_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE),
+ MAKE_REGLIST(xe_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE),
+ MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_BLITTER),
+ MAKE_REGLIST(xe_blt_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_BLITTER),
+ MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_GSC_OTHER),
+ MAKE_REGLIST(xe_lp_gsc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_GSC_OTHER),
+ {}
+};
static const char * const capture_list_type_names[] = {
"Global",
"Class",
@@ -292,7 +317,9 @@ guc_capture_remove_stale_matches_from_list(struct xe_guc_state_capture *gc,
static const struct __guc_mmio_reg_descr_group *
guc_capture_get_device_reglist(struct xe_device *xe)
{
- if (GRAPHICS_VERx100(xe) >= 1255)
+ if (GRAPHICS_VER(xe) >= 35)
+ return xe3p_lists;
+ else if (GRAPHICS_VERx100(xe) >= 1255)
return xe_hpg_lists;
else
return xe_lp_lists;
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.c b/drivers/gpu/drm/xe/xe_guc_ct.c
index 18f6327bf552..4ac434ad216f 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.c
+++ b/drivers/gpu/drm/xe/xe_guc_ct.c
@@ -21,18 +21,18 @@
#include "xe_devcoredump.h"
#include "xe_device.h"
#include "xe_gt.h"
-#include "xe_gt_pagefault.h"
#include "xe_gt_printk.h"
#include "xe_gt_sriov_pf_control.h"
#include "xe_gt_sriov_pf_monitor.h"
-#include "xe_gt_sriov_printk.h"
#include "xe_guc.h"
#include "xe_guc_log.h"
+#include "xe_guc_pagefault.h"
#include "xe_guc_relay.h"
#include "xe_guc_submit.h"
#include "xe_guc_tlb_inval.h"
#include "xe_map.h"
#include "xe_pm.h"
+#include "xe_sriov_vf.h"
#include "xe_trace_guc.h"
static void receive_g2h(struct xe_guc_ct *ct);
@@ -93,8 +93,6 @@ struct g2h_fence {
bool done;
};
-#define make_u64(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
-
static void g2h_fence_init(struct g2h_fence *g2h_fence, u32 *response_buffer)
{
memset(g2h_fence, 0, sizeof(*g2h_fence));
@@ -169,6 +167,7 @@ ct_to_xe(struct xe_guc_ct *ct)
*/
#define CTB_DESC_SIZE ALIGN(sizeof(struct guc_ct_buffer_desc), SZ_2K)
+#define CTB_H2G_BUFFER_OFFSET (CTB_DESC_SIZE * 2)
#define CTB_H2G_BUFFER_SIZE (SZ_4K)
#define CTB_G2H_BUFFER_SIZE (SZ_128K)
#define G2H_ROOM_BUFFER_SIZE (CTB_G2H_BUFFER_SIZE / 2)
@@ -192,7 +191,7 @@ long xe_guc_ct_queue_proc_time_jiffies(struct xe_guc_ct *ct)
static size_t guc_ct_size(void)
{
- return 2 * CTB_DESC_SIZE + CTB_H2G_BUFFER_SIZE +
+ return CTB_H2G_BUFFER_OFFSET + CTB_H2G_BUFFER_SIZE +
CTB_G2H_BUFFER_SIZE;
}
@@ -200,6 +199,9 @@ static void guc_ct_fini(struct drm_device *drm, void *arg)
{
struct xe_guc_ct *ct = arg;
+#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
+ cancel_work_sync(&ct->dead.worker);
+#endif
ct_exit_safe_mode(ct);
destroy_workqueue(ct->g2h_wq);
xa_destroy(&ct->fence_lookup);
@@ -223,6 +225,12 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct)
xe_gt_assert(gt, !(guc_ct_size() % PAGE_SIZE));
+ err = drmm_mutex_init(&xe->drm, &ct->lock);
+ if (err)
+ return err;
+
+ primelockdep(ct);
+
ct->g2h_wq = alloc_ordered_workqueue("xe-g2h-wq", WQ_MEM_RECLAIM);
if (!ct->g2h_wq)
return -ENOMEM;
@@ -234,16 +242,13 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct)
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
spin_lock_init(&ct->dead.lock);
INIT_WORK(&ct->dead.worker, ct_dead_worker_func);
+#if IS_ENABLED(CONFIG_DRM_XE_DEBUG_GUC)
+ stack_depot_init();
+#endif
#endif
init_waitqueue_head(&ct->wq);
init_waitqueue_head(&ct->g2h_fence_wq);
- err = drmm_mutex_init(&xe->drm, &ct->lock);
- if (err)
- return err;
-
- primelockdep(ct);
-
err = drmm_add_action_or_reset(&xe->drm, guc_ct_fini, ct);
if (err)
return err;
@@ -333,7 +338,7 @@ static void guc_ct_ctb_h2g_init(struct xe_device *xe, struct guc_ctb *h2g,
h2g->desc = *map;
xe_map_memset(xe, &h2g->desc, 0, 0, sizeof(struct guc_ct_buffer_desc));
- h2g->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_DESC_SIZE * 2);
+ h2g->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_H2G_BUFFER_OFFSET);
}
static void guc_ct_ctb_g2h_init(struct xe_device *xe, struct guc_ctb *g2h,
@@ -351,7 +356,7 @@ static void guc_ct_ctb_g2h_init(struct xe_device *xe, struct guc_ctb *g2h,
g2h->desc = IOSYS_MAP_INIT_OFFSET(map, CTB_DESC_SIZE);
xe_map_memset(xe, &g2h->desc, 0, 0, sizeof(struct guc_ct_buffer_desc));
- g2h->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_DESC_SIZE * 2 +
+ g2h->cmds = IOSYS_MAP_INIT_OFFSET(map, CTB_H2G_BUFFER_OFFSET +
CTB_H2G_BUFFER_SIZE);
}
@@ -362,7 +367,7 @@ static int guc_ct_ctb_h2g_register(struct xe_guc_ct *ct)
int err;
desc_addr = xe_bo_ggtt_addr(ct->bo);
- ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_DESC_SIZE * 2;
+ ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_H2G_BUFFER_OFFSET;
size = ct->ctbs.h2g.info.size * sizeof(u32);
err = xe_guc_self_cfg64(guc,
@@ -389,7 +394,7 @@ static int guc_ct_ctb_g2h_register(struct xe_guc_ct *ct)
int err;
desc_addr = xe_bo_ggtt_addr(ct->bo) + CTB_DESC_SIZE;
- ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_DESC_SIZE * 2 +
+ ctb_addr = xe_bo_ggtt_addr(ct->bo) + CTB_H2G_BUFFER_OFFSET +
CTB_H2G_BUFFER_SIZE;
size = ct->ctbs.g2h.info.size * sizeof(u32);
@@ -503,7 +508,7 @@ static void ct_exit_safe_mode(struct xe_guc_ct *ct)
xe_gt_dbg(ct_to_gt(ct), "GuC CT safe-mode disabled\n");
}
-int xe_guc_ct_enable(struct xe_guc_ct *ct)
+static int __xe_guc_ct_start(struct xe_guc_ct *ct, bool needs_register)
{
struct xe_device *xe = ct_to_xe(ct);
struct xe_gt *gt = ct_to_gt(ct);
@@ -511,21 +516,29 @@ int xe_guc_ct_enable(struct xe_guc_ct *ct)
xe_gt_assert(gt, !xe_guc_ct_enabled(ct));
- xe_map_memset(xe, &ct->bo->vmap, 0, 0, xe_bo_size(ct->bo));
- guc_ct_ctb_h2g_init(xe, &ct->ctbs.h2g, &ct->bo->vmap);
- guc_ct_ctb_g2h_init(xe, &ct->ctbs.g2h, &ct->bo->vmap);
+ if (needs_register) {
+ xe_map_memset(xe, &ct->bo->vmap, 0, 0, xe_bo_size(ct->bo));
+ guc_ct_ctb_h2g_init(xe, &ct->ctbs.h2g, &ct->bo->vmap);
+ guc_ct_ctb_g2h_init(xe, &ct->ctbs.g2h, &ct->bo->vmap);
- err = guc_ct_ctb_h2g_register(ct);
- if (err)
- goto err_out;
+ err = guc_ct_ctb_h2g_register(ct);
+ if (err)
+ goto err_out;
- err = guc_ct_ctb_g2h_register(ct);
- if (err)
- goto err_out;
+ err = guc_ct_ctb_g2h_register(ct);
+ if (err)
+ goto err_out;
- err = guc_ct_control_toggle(ct, true);
- if (err)
- goto err_out;
+ err = guc_ct_control_toggle(ct, true);
+ if (err)
+ goto err_out;
+ } else {
+ ct->ctbs.h2g.info.broken = false;
+ ct->ctbs.g2h.info.broken = false;
+ /* Skip everything in H2G buffer */
+ xe_map_memset(xe, &ct->bo->vmap, CTB_H2G_BUFFER_OFFSET, 0,
+ CTB_H2G_BUFFER_SIZE);
+ }
guc_ct_change_state(ct, XE_GUC_CT_STATE_ENABLED);
@@ -557,6 +570,32 @@ err_out:
return err;
}
+/**
+ * xe_guc_ct_restart() - Restart GuC CT
+ * @ct: the &xe_guc_ct
+ *
+ * Restart GuC CT to an empty state without issuing a CT register MMIO command.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int xe_guc_ct_restart(struct xe_guc_ct *ct)
+{
+ return __xe_guc_ct_start(ct, false);
+}
+
+/**
+ * xe_guc_ct_enable() - Enable GuC CT
+ * @ct: the &xe_guc_ct
+ *
+ * Enable GuC CT to an empty state and issue a CT register MMIO command.
+ *
+ * Return: 0 on success, or a negative errno on failure.
+ */
+int xe_guc_ct_enable(struct xe_guc_ct *ct)
+{
+ return __xe_guc_ct_start(ct, true);
+}
+
static void stop_g2h_handler(struct xe_guc_ct *ct)
{
cancel_work_sync(&ct->g2h_worker);
@@ -577,6 +616,16 @@ void xe_guc_ct_disable(struct xe_guc_ct *ct)
}
/**
+ * xe_guc_ct_flush_and_stop - Flush and stop all processing of G2H / H2G
+ * @ct: the &xe_guc_ct
+ */
+void xe_guc_ct_flush_and_stop(struct xe_guc_ct *ct)
+{
+ receive_g2h(ct);
+ xe_guc_ct_stop(ct);
+}
+
+/**
* xe_guc_ct_stop - Set GuC to stopped state
* @ct: the &xe_guc_ct
*
@@ -739,6 +788,28 @@ static u16 next_ct_seqno(struct xe_guc_ct *ct, bool is_g2h_fence)
return seqno;
}
+#define MAKE_ACTION(type, __action) \
+({ \
+ FIELD_PREP(GUC_HXG_MSG_0_TYPE, type) | \
+ FIELD_PREP(GUC_HXG_EVENT_MSG_0_ACTION | \
+ GUC_HXG_EVENT_MSG_0_DATA0, __action); \
+})
+
+static bool vf_action_can_safely_fail(struct xe_device *xe, u32 action)
+{
+ /*
+ * When resuming a VF, we can't reliably track whether context
+ * registration has completed in the GuC state machine. It is harmless
+ * to resend the request, as it will fail silently if GUC_HXG_TYPE_EVENT
+ * is used. Additionally, if there is an H2G protocol issue on a VF,
+ * subsequent H2G messages sent as GUC_HXG_TYPE_FAST_REQUEST will likely
+ * fail.
+ */
+ return IS_SRIOV_VF(xe) && xe_sriov_vf_migration_supported(xe) &&
+ (action == XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC ||
+ action == XE_GUC_ACTION_REGISTER_CONTEXT);
+}
+
#define H2G_CT_HEADERS (GUC_CTB_HDR_LEN + 1) /* one DW CTB header and one DW HxG header */
static int h2g_write(struct xe_guc_ct *ct, const u32 *action, u32 len,
@@ -810,18 +881,14 @@ static int h2g_write(struct xe_guc_ct *ct, const u32 *action, u32 len,
FIELD_PREP(GUC_CTB_MSG_0_NUM_DWORDS, len) |
FIELD_PREP(GUC_CTB_MSG_0_FENCE, ct_fence_value);
if (want_response) {
- cmd[1] =
- FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_REQUEST) |
- FIELD_PREP(GUC_HXG_EVENT_MSG_0_ACTION |
- GUC_HXG_EVENT_MSG_0_DATA0, action[0]);
+ cmd[1] = MAKE_ACTION(GUC_HXG_TYPE_REQUEST, action[0]);
+ } else if (vf_action_can_safely_fail(xe, action[0])) {
+ cmd[1] = MAKE_ACTION(GUC_HXG_TYPE_EVENT, action[0]);
} else {
fast_req_track(ct, ct_fence_value,
FIELD_GET(GUC_HXG_EVENT_MSG_0_ACTION, action[0]));
- cmd[1] =
- FIELD_PREP(GUC_HXG_MSG_0_TYPE, GUC_HXG_TYPE_FAST_REQUEST) |
- FIELD_PREP(GUC_HXG_EVENT_MSG_0_ACTION |
- GUC_HXG_EVENT_MSG_0_DATA0, action[0]);
+ cmd[1] = MAKE_ACTION(GUC_HXG_TYPE_FAST_REQUEST, action[0]);
}
/* H2G header in cmd[1] replaces action[0] so: */
@@ -854,7 +921,7 @@ static int __guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action,
u32 len, u32 g2h_len, u32 num_g2h,
struct g2h_fence *g2h_fence)
{
- struct xe_gt *gt __maybe_unused = ct_to_gt(ct);
+ struct xe_gt *gt = ct_to_gt(ct);
u16 seqno;
int ret;
@@ -875,7 +942,7 @@ static int __guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action,
goto out;
}
- if (ct->state == XE_GUC_CT_STATE_STOPPED) {
+ if (ct->state == XE_GUC_CT_STATE_STOPPED || xe_gt_recovery_pending(gt)) {
ret = -ECANCELED;
goto out;
}
@@ -930,22 +997,15 @@ static void kick_reset(struct xe_guc_ct *ct)
static int dequeue_one_g2h(struct xe_guc_ct *ct);
-static int guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, u32 len,
- u32 g2h_len, u32 num_g2h,
- struct g2h_fence *g2h_fence)
+/*
+ * wait before retry of sending h2g message
+ * Return: true if ready for retry, false if the wait timeouted
+ */
+static bool guc_ct_send_wait_for_retry(struct xe_guc_ct *ct, u32 len,
+ u32 g2h_len, struct g2h_fence *g2h_fence,
+ unsigned int *sleep_period_ms)
{
struct xe_device *xe = ct_to_xe(ct);
- struct xe_gt *gt = ct_to_gt(ct);
- unsigned int sleep_period_ms = 1;
- int ret;
-
- xe_gt_assert(gt, !g2h_len || !g2h_fence);
- lockdep_assert_held(&ct->lock);
- xe_device_assert_mem_access(ct_to_xe(ct));
-
-try_again:
- ret = __guc_ct_send_locked(ct, action, len, g2h_len, num_g2h,
- g2h_fence);
/*
* We wait to try to restore credits for about 1 second before bailing.
@@ -954,24 +1014,22 @@ try_again:
* the case of G2H we process any G2H in the channel, hopefully freeing
* credits as we consume the G2H messages.
*/
- if (unlikely(ret == -EBUSY &&
- !h2g_has_room(ct, len + GUC_CTB_HDR_LEN))) {
+ if (!h2g_has_room(ct, len + GUC_CTB_HDR_LEN)) {
struct guc_ctb *h2g = &ct->ctbs.h2g;
- if (sleep_period_ms == 1024)
- goto broken;
+ if (*sleep_period_ms == 1024)
+ return false;
trace_xe_guc_ct_h2g_flow_control(xe, h2g->info.head, h2g->info.tail,
h2g->info.size,
h2g->info.space,
len + GUC_CTB_HDR_LEN);
- msleep(sleep_period_ms);
- sleep_period_ms <<= 1;
-
- goto try_again;
- } else if (unlikely(ret == -EBUSY)) {
+ msleep(*sleep_period_ms);
+ *sleep_period_ms <<= 1;
+ } else {
struct xe_device *xe = ct_to_xe(ct);
struct guc_ctb *g2h = &ct->ctbs.g2h;
+ int ret;
trace_xe_guc_ct_g2h_flow_control(xe, g2h->info.head,
desc_read(xe, g2h, tail),
@@ -985,7 +1043,7 @@ try_again:
(desc_read(ct_to_xe(ct), (&ct->ctbs.g2h), tail) != ct->ctbs.g2h.info.head)
if (!wait_event_timeout(ct->wq, !ct->g2h_outstanding ||
g2h_avail(ct), HZ))
- goto broken;
+ return false;
#undef g2h_avail
ret = dequeue_one_g2h(ct);
@@ -993,9 +1051,32 @@ try_again:
if (ret != -ECANCELED)
xe_gt_err(ct_to_gt(ct), "CTB receive failed (%pe)",
ERR_PTR(ret));
- goto broken;
+ return false;
}
+ }
+ return true;
+}
+static int guc_ct_send_locked(struct xe_guc_ct *ct, const u32 *action, u32 len,
+ u32 g2h_len, u32 num_g2h,
+ struct g2h_fence *g2h_fence)
+{
+ struct xe_gt *gt = ct_to_gt(ct);
+ unsigned int sleep_period_ms = 1;
+ int ret;
+
+ xe_gt_assert(gt, !g2h_len || !g2h_fence);
+ lockdep_assert_held(&ct->lock);
+ xe_device_assert_mem_access(ct_to_xe(ct));
+
+try_again:
+ ret = __guc_ct_send_locked(ct, action, len, g2h_len, num_g2h,
+ g2h_fence);
+
+ if (unlikely(ret == -EBUSY)) {
+ if (!guc_ct_send_wait_for_retry(ct, len, g2h_len, g2h_fence,
+ &sleep_period_ms))
+ goto broken;
goto try_again;
}
@@ -1337,6 +1418,10 @@ static int parse_g2h_response(struct xe_guc_ct *ct, u32 *msg, u32 len)
fast_req_report(ct, fence);
+ /* FIXME: W/A race in the GuC, will get in firmware soon */
+ if (xe_gt_recovery_pending(gt))
+ return 0;
+
CT_DEAD(ct, NULL, PARSE_G2H_RESPONSE);
return -EPROTO;
@@ -1466,10 +1551,6 @@ static int process_g2h_msg(struct xe_guc_ct *ct, u32 *msg, u32 len)
case XE_GUC_ACTION_TLB_INVALIDATION_DONE:
ret = xe_guc_tlb_inval_done_handler(guc, payload, adj_len);
break;
- case XE_GUC_ACTION_ACCESS_COUNTER_NOTIFY:
- ret = xe_guc_access_counter_notify_handler(guc, payload,
- adj_len);
- break;
case XE_GUC_ACTION_GUC2PF_RELAY_FROM_VF:
ret = xe_guc_relay_process_guc2pf(&guc->relay, hxg, hxg_len);
break;
@@ -1793,186 +1874,6 @@ static void g2h_worker_func(struct work_struct *w)
receive_g2h(ct);
}
-static void xe_fixup_u64_in_cmds(struct xe_device *xe, struct iosys_map *cmds,
- u32 size, u32 idx, s64 shift)
-{
- u32 hi, lo;
- u64 offset;
-
- lo = xe_map_rd_ring_u32(xe, cmds, idx, size);
- hi = xe_map_rd_ring_u32(xe, cmds, idx + 1, size);
- offset = make_u64(hi, lo);
- offset += shift;
- lo = lower_32_bits(offset);
- hi = upper_32_bits(offset);
- xe_map_wr_ring_u32(xe, cmds, idx, size, lo);
- xe_map_wr_ring_u32(xe, cmds, idx + 1, size, hi);
-}
-
-/*
- * Shift any GGTT addresses within a single message left within CTB from
- * before post-migration recovery.
- * @ct: pointer to CT struct of the target GuC
- * @cmds: iomap buffer containing CT messages
- * @head: start of the target message within the buffer
- * @len: length of the target message
- * @size: size of the commands buffer
- * @shift: the address shift to be added to each GGTT reference
- * Return: true if the message was fixed or needed no fixups, false on failure
- */
-static bool ct_fixup_ggtt_in_message(struct xe_guc_ct *ct,
- struct iosys_map *cmds, u32 head,
- u32 len, u32 size, s64 shift)
-{
- struct xe_gt *gt = ct_to_gt(ct);
- struct xe_device *xe = ct_to_xe(ct);
- u32 msg[GUC_HXG_MSG_MIN_LEN];
- u32 action, i, n;
-
- xe_gt_assert(gt, len >= GUC_HXG_MSG_MIN_LEN);
-
- msg[0] = xe_map_rd_ring_u32(xe, cmds, head, size);
- action = FIELD_GET(GUC_HXG_REQUEST_MSG_0_ACTION, msg[0]);
-
- xe_gt_sriov_dbg_verbose(gt, "fixing H2G %#x\n", action);
-
- switch (action) {
- case XE_GUC_ACTION_REGISTER_CONTEXT:
- if (len != XE_GUC_REGISTER_CONTEXT_MSG_LEN)
- goto err_len;
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_DATA_5_WQ_DESC_ADDR_LOWER,
- shift);
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_DATA_7_WQ_BUF_BASE_LOWER,
- shift);
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_DATA_10_HW_LRC_ADDR, shift);
- break;
- case XE_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC:
- if (len < XE_GUC_REGISTER_CONTEXT_MULTI_LRC_MSG_MIN_LEN)
- goto err_len;
- n = xe_map_rd_ring_u32(xe, cmds, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_10_NUM_CTXS, size);
- if (len != XE_GUC_REGISTER_CONTEXT_MULTI_LRC_MSG_MIN_LEN + 2 * n)
- goto err_len;
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_5_WQ_DESC_ADDR_LOWER,
- shift);
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_7_WQ_BUF_BASE_LOWER,
- shift);
- for (i = 0; i < n; i++)
- xe_fixup_u64_in_cmds(xe, cmds, size, head +
- XE_GUC_REGISTER_CONTEXT_MULTI_LRC_DATA_11_HW_LRC_ADDR
- + 2 * i, shift);
- break;
- default:
- break;
- }
- return true;
-
-err_len:
- xe_gt_err(gt, "Skipped G2G %#x message fixups, unexpected length (%u)\n", action, len);
- return false;
-}
-
-/*
- * Apply fixups to the next outgoing CT message within given CTB
- * @ct: the &xe_guc_ct struct instance representing the target GuC
- * @h2g: the &guc_ctb struct instance of the target buffer
- * @shift: shift to be added to all GGTT addresses within the CTB
- * @mhead: pointer to an integer storing message start position; the
- * position is changed to next message before this function return
- * @avail: size of the area available for parsing, that is length
- * of all remaining messages stored within the CTB
- * Return: size of the area available for parsing after one message
- * has been parsed, that is length remaining from the updated mhead
- */
-static int ct_fixup_ggtt_in_buffer(struct xe_guc_ct *ct, struct guc_ctb *h2g,
- s64 shift, u32 *mhead, s32 avail)
-{
- struct xe_gt *gt = ct_to_gt(ct);
- struct xe_device *xe = ct_to_xe(ct);
- u32 msg[GUC_HXG_MSG_MIN_LEN];
- u32 size = h2g->info.size;
- u32 head = *mhead;
- u32 len;
-
- xe_gt_assert(gt, avail >= (s32)GUC_CTB_MSG_MIN_LEN);
-
- /* Read header */
- msg[0] = xe_map_rd_ring_u32(xe, &h2g->cmds, head, size);
- len = FIELD_GET(GUC_CTB_MSG_0_NUM_DWORDS, msg[0]) + GUC_CTB_MSG_MIN_LEN;
-
- if (unlikely(len > (u32)avail)) {
- xe_gt_err(gt, "H2G channel broken on read, avail=%d, len=%d, fixups skipped\n",
- avail, len);
- return 0;
- }
-
- head = (head + GUC_CTB_MSG_MIN_LEN) % size;
- if (!ct_fixup_ggtt_in_message(ct, &h2g->cmds, head, msg_len_to_hxg_len(len), size, shift))
- return 0;
- *mhead = (head + msg_len_to_hxg_len(len)) % size;
-
- return avail - len;
-}
-
-/**
- * xe_guc_ct_fixup_messages_with_ggtt - Fixup any pending H2G CTB messages
- * @ct: pointer to CT struct of the target GuC
- * @ggtt_shift: shift to be added to all GGTT addresses within the CTB
- *
- * Messages in GuC to Host CTB are owned by GuC and any fixups in them
- * are made by GuC. But content of the Host to GuC CTB is owned by the
- * KMD, so fixups to GGTT references in any pending messages need to be
- * applied here.
- * This function updates GGTT offsets in payloads of pending H2G CTB
- * messages (messages which were not consumed by GuC before the VF got
- * paused).
- */
-void xe_guc_ct_fixup_messages_with_ggtt(struct xe_guc_ct *ct, s64 ggtt_shift)
-{
- struct guc_ctb *h2g = &ct->ctbs.h2g;
- struct xe_guc *guc = ct_to_guc(ct);
- struct xe_gt *gt = guc_to_gt(guc);
- u32 head, tail, size;
- s32 avail;
-
- if (unlikely(h2g->info.broken))
- return;
-
- h2g->info.head = desc_read(ct_to_xe(ct), h2g, head);
- head = h2g->info.head;
- tail = READ_ONCE(h2g->info.tail);
- size = h2g->info.size;
-
- if (unlikely(head > size))
- goto corrupted;
-
- if (unlikely(tail >= size))
- goto corrupted;
-
- avail = tail - head;
-
- /* beware of buffer wrap case */
- if (unlikely(avail < 0))
- avail += size;
- xe_gt_dbg(gt, "available %d (%u:%u:%u)\n", avail, head, tail, size);
- xe_gt_assert(gt, avail >= 0);
-
- while (avail > 0)
- avail = ct_fixup_ggtt_in_buffer(ct, h2g, ggtt_shift, &head, avail);
-
- return;
-
-corrupted:
- xe_gt_err(gt, "Corrupted H2G descriptor head=%u tail=%u size=%u, fixups not applied\n",
- head, tail, size);
- h2g->info.broken = true;
-}
-
static struct xe_guc_ct_snapshot *guc_ct_snapshot_alloc(struct xe_guc_ct *ct, bool atomic,
bool want_ctb)
{
diff --git a/drivers/gpu/drm/xe/xe_guc_ct.h b/drivers/gpu/drm/xe/xe_guc_ct.h
index cf41210ab30a..ca1ce2b3c354 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct.h
@@ -15,8 +15,10 @@ int xe_guc_ct_init_noalloc(struct xe_guc_ct *ct);
int xe_guc_ct_init(struct xe_guc_ct *ct);
int xe_guc_ct_init_post_hwconfig(struct xe_guc_ct *ct);
int xe_guc_ct_enable(struct xe_guc_ct *ct);
+int xe_guc_ct_restart(struct xe_guc_ct *ct);
void xe_guc_ct_disable(struct xe_guc_ct *ct);
void xe_guc_ct_stop(struct xe_guc_ct *ct);
+void xe_guc_ct_flush_and_stop(struct xe_guc_ct *ct);
void xe_guc_ct_fast_path(struct xe_guc_ct *ct);
struct xe_guc_ct_snapshot *xe_guc_ct_snapshot_capture(struct xe_guc_ct *ct);
@@ -24,8 +26,6 @@ void xe_guc_ct_snapshot_print(struct xe_guc_ct_snapshot *snapshot, struct drm_pr
void xe_guc_ct_snapshot_free(struct xe_guc_ct_snapshot *snapshot);
void xe_guc_ct_print(struct xe_guc_ct *ct, struct drm_printer *p, bool want_ctb);
-void xe_guc_ct_fixup_messages_with_ggtt(struct xe_guc_ct *ct, s64 ggtt_shift);
-
static inline bool xe_guc_ct_initialized(struct xe_guc_ct *ct)
{
return ct->state != XE_GUC_CT_STATE_NOT_INITIALIZED;
@@ -74,4 +74,13 @@ xe_guc_ct_send_block_no_fail(struct xe_guc_ct *ct, const u32 *action, u32 len)
long xe_guc_ct_queue_proc_time_jiffies(struct xe_guc_ct *ct);
+/**
+ * xe_guc_ct_wake_waiters() - GuC CT wake up waiters
+ * @ct: GuC CT object
+ */
+static inline void xe_guc_ct_wake_waiters(struct xe_guc_ct *ct)
+{
+ wake_up_all(&ct->wq);
+}
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_ct_types.h b/drivers/gpu/drm/xe/xe_guc_ct_types.h
index 8b03b50313d9..09d7ff1ef42a 100644
--- a/drivers/gpu/drm/xe/xe_guc_ct_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_ct_types.h
@@ -126,7 +126,7 @@ struct xe_fast_req_fence {
* for the H2G and G2H requests sent and received through the buffers.
*/
struct xe_guc_ct {
- /** @bo: XE BO for CT */
+ /** @bo: Xe BO for CT */
struct xe_bo *bo;
/** @lock: protects everything in CT layer */
struct mutex lock;
diff --git a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
index c30c0e3ccbbb..a3b034e4b205 100644
--- a/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_exec_queue_types.h
@@ -51,6 +51,21 @@ struct xe_guc_exec_queue {
wait_queue_head_t suspend_wait;
/** @suspend_pending: a suspend of the exec_queue is pending */
bool suspend_pending;
+ /**
+ * @needs_cleanup: Needs a cleanup message during VF post migration
+ * recovery.
+ */
+ bool needs_cleanup;
+ /**
+ * @needs_suspend: Needs a suspend message during VF post migration
+ * recovery.
+ */
+ bool needs_suspend;
+ /**
+ * @needs_resume: Needs a resume message during VF post migration
+ * recovery.
+ */
+ bool needs_resume;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_fwif.h b/drivers/gpu/drm/xe/xe_guc_fwif.h
index 50c4c2406132..c90dd266e9cf 100644
--- a/drivers/gpu/drm/xe/xe_guc_fwif.h
+++ b/drivers/gpu/drm/xe/xe_guc_fwif.h
@@ -113,6 +113,7 @@ struct guc_update_exec_queue_policy {
#define GUC_CTL_ENABLE_SLPC BIT(2)
#define GUC_CTL_ENABLE_LITE_RESTORE BIT(4)
#define GUC_CTL_ENABLE_PSMI_LOGGING BIT(7)
+#define GUC_CTL_MAIN_GAMCTRL_QUEUES BIT(9)
#define GUC_CTL_DISABLE_SCHEDULER BIT(14)
#define GUC_CTL_DEBUG 3
diff --git a/drivers/gpu/drm/xe/xe_guc_log_types.h b/drivers/gpu/drm/xe/xe_guc_log_types.h
index b3d5c72ac752..02851b924aa4 100644
--- a/drivers/gpu/drm/xe/xe_guc_log_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_log_types.h
@@ -44,7 +44,7 @@ struct xe_guc_log_snapshot {
struct xe_guc_log {
/** @level: GuC log level */
u32 level;
- /** @bo: XE BO for GuC log */
+ /** @bo: Xe BO for GuC log */
struct xe_bo *bo;
/** @stats: logging related stats */
struct {
diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.c b/drivers/gpu/drm/xe/xe_guc_pagefault.c
new file mode 100644
index 000000000000..719a18187a31
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_pagefault.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "abi/guc_actions_abi.h"
+#include "xe_guc.h"
+#include "xe_guc_ct.h"
+#include "xe_guc_pagefault.h"
+#include "xe_pagefault.h"
+
+static void guc_ack_fault(struct xe_pagefault *pf, int err)
+{
+ u32 vfid = FIELD_GET(PFD_VFID, pf->producer.msg[2]);
+ u32 engine_instance = FIELD_GET(PFD_ENG_INSTANCE, pf->producer.msg[0]);
+ u32 engine_class = FIELD_GET(PFD_ENG_CLASS, pf->producer.msg[0]);
+ u32 pdata = FIELD_GET(PFD_PDATA_LO, pf->producer.msg[0]) |
+ (FIELD_GET(PFD_PDATA_HI, pf->producer.msg[1]) <<
+ PFD_PDATA_HI_SHIFT);
+ u32 action[] = {
+ XE_GUC_ACTION_PAGE_FAULT_RES_DESC,
+
+ FIELD_PREP(PFR_VALID, 1) |
+ FIELD_PREP(PFR_SUCCESS, !!err) |
+ FIELD_PREP(PFR_REPLY, PFR_ACCESS) |
+ FIELD_PREP(PFR_DESC_TYPE, FAULT_RESPONSE_DESC) |
+ FIELD_PREP(PFR_ASID, pf->consumer.asid),
+
+ FIELD_PREP(PFR_VFID, vfid) |
+ FIELD_PREP(PFR_ENG_INSTANCE, engine_instance) |
+ FIELD_PREP(PFR_ENG_CLASS, engine_class) |
+ FIELD_PREP(PFR_PDATA, pdata),
+ };
+ struct xe_guc *guc = pf->producer.private;
+
+ xe_guc_ct_send(&guc->ct, action, ARRAY_SIZE(action), 0, 0);
+}
+
+static const struct xe_pagefault_ops guc_pagefault_ops = {
+ .ack_fault = guc_ack_fault,
+};
+
+/**
+ * xe_guc_pagefault_handler() - G2H page fault handler
+ * @guc: GuC object
+ * @msg: G2H message
+ * @len: Length of G2H message
+ *
+ * Parse GuC to host (G2H) message into a struct xe_pagefault and forward onto
+ * the Xe page fault layer.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int xe_guc_pagefault_handler(struct xe_guc *guc, u32 *msg, u32 len)
+{
+ struct xe_pagefault pf;
+ int i;
+
+#define GUC_PF_MSG_LEN_DW \
+ (sizeof(struct xe_guc_pagefault_desc) / sizeof(u32))
+
+ BUILD_BUG_ON(GUC_PF_MSG_LEN_DW > XE_PAGEFAULT_PRODUCER_MSG_LEN_DW);
+
+ if (len != GUC_PF_MSG_LEN_DW)
+ return -EPROTO;
+
+ pf.gt = guc_to_gt(guc);
+
+ /*
+ * XXX: These values happen to match the enum in xe_pagefault_types.h.
+ * If that changes, we’ll need to remap them here.
+ */
+ pf.consumer.page_addr = ((u64)FIELD_GET(PFD_VIRTUAL_ADDR_HI, msg[3])
+ << PFD_VIRTUAL_ADDR_HI_SHIFT) |
+ (FIELD_GET(PFD_VIRTUAL_ADDR_LO, msg[2]) <<
+ PFD_VIRTUAL_ADDR_LO_SHIFT);
+ pf.consumer.asid = FIELD_GET(PFD_ASID, msg[1]);
+ pf.consumer.access_type = FIELD_GET(PFD_ACCESS_TYPE, msg[2]);
+ pf.consumer.fault_type = FIELD_GET(PFD_FAULT_TYPE, msg[2]);
+ if (FIELD_GET(XE2_PFD_TRVA_FAULT, msg[0]))
+ pf.consumer.fault_level = XE_PAGEFAULT_LEVEL_NACK;
+ else
+ pf.consumer.fault_level = FIELD_GET(PFD_FAULT_LEVEL, msg[0]);
+ pf.consumer.engine_class = FIELD_GET(PFD_ENG_CLASS, msg[0]);
+ pf.consumer.engine_instance = FIELD_GET(PFD_ENG_INSTANCE, msg[0]);
+
+ pf.producer.private = guc;
+ pf.producer.ops = &guc_pagefault_ops;
+ for (i = 0; i < GUC_PF_MSG_LEN_DW; ++i)
+ pf.producer.msg[i] = msg[i];
+
+#undef GUC_PF_MSG_LEN_DW
+
+ return xe_pagefault_handler(guc_to_xe(guc), &pf);
+}
diff --git a/drivers/gpu/drm/xe/xe_guc_pagefault.h b/drivers/gpu/drm/xe/xe_guc_pagefault.h
new file mode 100644
index 000000000000..3bd599e7207c
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_guc_pagefault.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_GUC_PAGEFAULT_H_
+#define _XE_GUC_PAGEFAULT_H_
+
+#include <linux/types.h>
+
+struct xe_guc;
+
+int xe_guc_pagefault_handler(struct xe_guc *guc, u32 *msg, u32 len);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_pc.c b/drivers/gpu/drm/xe/xe_guc_pc.c
index 53fdf59524c4..951a49fb1d3e 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc.c
+++ b/drivers/gpu/drm/xe/xe_guc_pc.c
@@ -7,12 +7,14 @@
#include <linux/cleanup.h>
#include <linux/delay.h>
+#include <linux/iopoll.h>
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/wait_bit.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
+#include <generated/xe_device_wa_oob.h>
#include <generated/xe_wa_oob.h>
#include "abi/guc_actions_slpc_abi.h"
@@ -130,26 +132,16 @@ static struct iosys_map *pc_to_maps(struct xe_guc_pc *pc)
FIELD_PREP(HOST2GUC_PC_SLPC_REQUEST_MSG_1_EVENT_ARGC, count))
static int wait_for_pc_state(struct xe_guc_pc *pc,
- enum slpc_global_state state,
+ enum slpc_global_state target_state,
int timeout_ms)
{
- int timeout_us = 1000 * timeout_ms;
- int slept, wait = 10;
+ enum slpc_global_state state;
xe_device_assert_mem_access(pc_to_xe(pc));
- for (slept = 0; slept < timeout_us;) {
- if (slpc_shared_data_read(pc, header.global_state) == state)
- return 0;
-
- usleep_range(wait, wait << 1);
- slept += wait;
- wait <<= 1;
- if (slept + wait > timeout_us)
- wait = timeout_us - slept;
- }
-
- return -ETIMEDOUT;
+ return poll_timeout_us(state = slpc_shared_data_read(pc, header.global_state),
+ state == target_state,
+ 20, timeout_ms * USEC_PER_MSEC, false);
}
static int wait_for_flush_complete(struct xe_guc_pc *pc)
@@ -164,24 +156,15 @@ static int wait_for_flush_complete(struct xe_guc_pc *pc)
return 0;
}
-static int wait_for_act_freq_limit(struct xe_guc_pc *pc, u32 freq)
+static int wait_for_act_freq_max_limit(struct xe_guc_pc *pc, u32 max_limit)
{
- int timeout_us = SLPC_ACT_FREQ_TIMEOUT_MS * USEC_PER_MSEC;
- int slept, wait = 10;
-
- for (slept = 0; slept < timeout_us;) {
- if (xe_guc_pc_get_act_freq(pc) <= freq)
- return 0;
-
- usleep_range(wait, wait << 1);
- slept += wait;
- wait <<= 1;
- if (slept + wait > timeout_us)
- wait = timeout_us - slept;
- }
+ u32 freq;
- return -ETIMEDOUT;
+ return poll_timeout_us(freq = xe_guc_pc_get_act_freq(pc),
+ freq <= max_limit,
+ 20, SLPC_ACT_FREQ_TIMEOUT_MS * USEC_PER_MSEC, false);
}
+
static int pc_action_reset(struct xe_guc_pc *pc)
{
struct xe_guc_ct *ct = pc_to_ct(pc);
@@ -348,7 +331,7 @@ static int pc_set_min_freq(struct xe_guc_pc *pc, u32 freq)
* Our goal is to have the admin choices respected.
*/
pc_action_set_param(pc, SLPC_PARAM_IGNORE_EFFICIENT_FREQUENCY,
- freq < pc->rpe_freq);
+ freq < xe_guc_pc_get_rpe_freq(pc));
return pc_action_set_param(pc,
SLPC_PARAM_GLOBAL_MIN_GT_UNSLICE_FREQ_MHZ,
@@ -380,7 +363,7 @@ static int pc_set_max_freq(struct xe_guc_pc *pc, u32 freq)
freq);
}
-static void mtl_update_rpa_value(struct xe_guc_pc *pc)
+static u32 mtl_get_rpa_freq(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
u32 reg;
@@ -390,10 +373,10 @@ static void mtl_update_rpa_value(struct xe_guc_pc *pc)
else
reg = xe_mmio_read32(&gt->mmio, MTL_GT_RPA_FREQUENCY);
- pc->rpa_freq = decode_freq(REG_FIELD_GET(MTL_RPA_MASK, reg));
+ return decode_freq(REG_FIELD_GET(MTL_RPA_MASK, reg));
}
-static void mtl_update_rpe_value(struct xe_guc_pc *pc)
+static u32 mtl_get_rpe_freq(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
u32 reg;
@@ -403,68 +386,56 @@ static void mtl_update_rpe_value(struct xe_guc_pc *pc)
else
reg = xe_mmio_read32(&gt->mmio, MTL_GT_RPE_FREQUENCY);
- pc->rpe_freq = decode_freq(REG_FIELD_GET(MTL_RPE_MASK, reg));
+ return decode_freq(REG_FIELD_GET(MTL_RPE_MASK, reg));
}
-static void tgl_update_rpa_value(struct xe_guc_pc *pc)
+static u32 pvc_get_rpa_freq(struct xe_guc_pc *pc)
{
- struct xe_gt *gt = pc_to_gt(pc);
- struct xe_device *xe = gt_to_xe(gt);
- u32 reg;
-
/*
* For PVC we still need to use fused RP0 as the approximation for RPa
* For other platforms than PVC we get the resolved RPa directly from
* PCODE at a different register
*/
- if (xe->info.platform == XE_PVC) {
- reg = xe_mmio_read32(&gt->mmio, PVC_RP_STATE_CAP);
- pc->rpa_freq = REG_FIELD_GET(RP0_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
- } else {
- reg = xe_mmio_read32(&gt->mmio, FREQ_INFO_REC);
- pc->rpa_freq = REG_FIELD_GET(RPA_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
- }
+
+ struct xe_gt *gt = pc_to_gt(pc);
+ u32 reg;
+
+ reg = xe_mmio_read32(&gt->mmio, PVC_RP_STATE_CAP);
+ return REG_FIELD_GET(RP0_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
}
-static void tgl_update_rpe_value(struct xe_guc_pc *pc)
+static u32 tgl_get_rpa_freq(struct xe_guc_pc *pc)
+{
+ struct xe_gt *gt = pc_to_gt(pc);
+ u32 reg;
+
+ reg = xe_mmio_read32(&gt->mmio, FREQ_INFO_REC);
+ return REG_FIELD_GET(RPA_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
+}
+
+static u32 pvc_get_rpe_freq(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
- struct xe_device *xe = gt_to_xe(gt);
u32 reg;
/*
* For PVC we still need to use fused RP1 as the approximation for RPe
- * For other platforms than PVC we get the resolved RPe directly from
- * PCODE at a different register
*/
- if (xe->info.platform == XE_PVC) {
- reg = xe_mmio_read32(&gt->mmio, PVC_RP_STATE_CAP);
- pc->rpe_freq = REG_FIELD_GET(RP1_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
- } else {
- reg = xe_mmio_read32(&gt->mmio, FREQ_INFO_REC);
- pc->rpe_freq = REG_FIELD_GET(RPE_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
- }
+ reg = xe_mmio_read32(&gt->mmio, PVC_RP_STATE_CAP);
+ return REG_FIELD_GET(RP1_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
}
-static void pc_update_rp_values(struct xe_guc_pc *pc)
+static u32 tgl_get_rpe_freq(struct xe_guc_pc *pc)
{
struct xe_gt *gt = pc_to_gt(pc);
- struct xe_device *xe = gt_to_xe(gt);
-
- if (GRAPHICS_VERx100(xe) >= 1270) {
- mtl_update_rpa_value(pc);
- mtl_update_rpe_value(pc);
- } else {
- tgl_update_rpa_value(pc);
- tgl_update_rpe_value(pc);
- }
+ u32 reg;
/*
- * RPe is decided at runtime by PCODE. In the rare case where that's
- * smaller than the fused min, we will trust the PCODE and use that
- * as our minimum one.
+ * For other platforms than PVC, we get the resolved RPe directly from
+ * PCODE at a different register
*/
- pc->rpn_freq = min(pc->rpn_freq, pc->rpe_freq);
+ reg = xe_mmio_read32(&gt->mmio, FREQ_INFO_REC);
+ return REG_FIELD_GET(RPE_MASK, reg) * GT_FREQUENCY_MULTIPLIER;
}
/**
@@ -565,9 +536,15 @@ u32 xe_guc_pc_get_rp0_freq(struct xe_guc_pc *pc)
*/
u32 xe_guc_pc_get_rpa_freq(struct xe_guc_pc *pc)
{
- pc_update_rp_values(pc);
+ struct xe_gt *gt = pc_to_gt(pc);
+ struct xe_device *xe = gt_to_xe(gt);
- return pc->rpa_freq;
+ if (GRAPHICS_VERx100(xe) == 1260)
+ return pvc_get_rpa_freq(pc);
+ else if (GRAPHICS_VERx100(xe) >= 1270)
+ return mtl_get_rpa_freq(pc);
+ else
+ return tgl_get_rpa_freq(pc);
}
/**
@@ -578,9 +555,17 @@ u32 xe_guc_pc_get_rpa_freq(struct xe_guc_pc *pc)
*/
u32 xe_guc_pc_get_rpe_freq(struct xe_guc_pc *pc)
{
- pc_update_rp_values(pc);
+ struct xe_device *xe = pc_to_xe(pc);
+ u32 freq;
+
+ if (GRAPHICS_VERx100(xe) == 1260)
+ freq = pvc_get_rpe_freq(pc);
+ else if (GRAPHICS_VERx100(xe) >= 1270)
+ freq = mtl_get_rpe_freq(pc);
+ else
+ freq = tgl_get_rpe_freq(pc);
- return pc->rpe_freq;
+ return freq;
}
/**
@@ -904,7 +889,7 @@ static int pc_adjust_freq_bounds(struct xe_guc_pc *pc)
if (pc_get_min_freq(pc) > pc->rp0_freq)
ret = pc_set_min_freq(pc, pc->rp0_freq);
- if (XE_GT_WA(tile->primary_gt, 14022085890))
+ if (XE_DEVICE_WA(tile_to_xe(tile), 14022085890))
ret = pc_set_min_freq(pc, max(BMG_MIN_FREQ, pc_get_min_freq(pc)));
out:
@@ -983,7 +968,7 @@ void xe_guc_pc_apply_flush_freq_limit(struct xe_guc_pc *pc)
* Wait for actual freq to go below the flush cap: even if the previous
* max was below cap, the current one might still be above it
*/
- ret = wait_for_act_freq_limit(pc, BMG_MERT_FLUSH_FREQ_CAP);
+ ret = wait_for_act_freq_max_limit(pc, BMG_MERT_FLUSH_FREQ_CAP);
if (ret)
xe_gt_err_once(gt, "Actual freq did not reduce to %u, %pe\n",
BMG_MERT_FLUSH_FREQ_CAP, ERR_PTR(ret));
@@ -1039,7 +1024,7 @@ static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
/*
* Ensure min and max are bound by MERT_FREQ_CAP until driver loads.
*/
- ret = pc_set_min_freq(pc, min(pc->rpe_freq, pc_max_freq_cap(pc)));
+ ret = pc_set_min_freq(pc, min(xe_guc_pc_get_rpe_freq(pc), pc_max_freq_cap(pc)));
if (!ret)
ret = pc_set_max_freq(pc, min(pc->rp0_freq, pc_max_freq_cap(pc)));
@@ -1150,8 +1135,6 @@ static int pc_init_freqs(struct xe_guc_pc *pc)
if (ret)
goto out;
- pc_update_rp_values(pc);
-
pc_init_pcode_freq(pc);
/*
@@ -1357,7 +1340,7 @@ static void xe_guc_pc_fini_hw(void *arg)
XE_WARN_ON(xe_guc_pc_stop(pc));
/* Bind requested freq to mert_freq_cap before unload */
- pc_set_cur_freq(pc, min(pc_max_freq_cap(pc), pc->rpe_freq));
+ pc_set_cur_freq(pc, min(pc_max_freq_cap(pc), xe_guc_pc_get_rpe_freq(pc)));
xe_force_wake_put(gt_to_fw(pc_to_gt(pc)), fw_ref);
}
diff --git a/drivers/gpu/drm/xe/xe_guc_pc_types.h b/drivers/gpu/drm/xe/xe_guc_pc_types.h
index 5e4ea53fbee6..711bbcdcb0d3 100644
--- a/drivers/gpu/drm/xe/xe_guc_pc_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_pc_types.h
@@ -19,10 +19,6 @@ struct xe_guc_pc {
atomic_t flush_freq_limit;
/** @rp0_freq: HW RP0 frequency - The Maximum one */
u32 rp0_freq;
- /** @rpa_freq: HW RPa frequency - The Achievable one */
- u32 rpa_freq;
- /** @rpe_freq: HW RPe frequency - The Efficient one */
- u32 rpe_freq;
/** @rpn_freq: HW RPN frequency - The Minimum one */
u32 rpn_freq;
/** @user_requested_min: Stash the minimum requested freq by user */
diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c
index e5dc94f3e618..0c0ff24ba62a 100644
--- a/drivers/gpu/drm/xe/xe_guc_relay.c
+++ b/drivers/gpu/drm/xe/xe_guc_relay.c
@@ -56,9 +56,19 @@ static struct xe_device *relay_to_xe(struct xe_guc_relay *relay)
return gt_to_xe(relay_to_gt(relay));
}
+#define XE_RELAY_DIAG_RATELIMIT_INTERVAL (10 * HZ)
+#define XE_RELAY_DIAG_RATELIMIT_BURST 10
+
+#define relay_ratelimit_printk(relay, _level, fmt...) ({ \
+ typeof(relay) _r = (relay); \
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG_SRIOV) || \
+ ___ratelimit(&_r->diag_ratelimit, "xe_guc_relay")) \
+ xe_gt_sriov_##_level(relay_to_gt(_r), "relay: " fmt); \
+})
+
#define relay_assert(relay, condition) xe_gt_assert(relay_to_gt(relay), condition)
-#define relay_notice(relay, msg...) xe_gt_sriov_notice(relay_to_gt(relay), "relay: " msg)
-#define relay_debug(relay, msg...) xe_gt_sriov_dbg_verbose(relay_to_gt(relay), "relay: " msg)
+#define relay_notice(relay, msg...) relay_ratelimit_printk((relay), notice, msg)
+#define relay_debug(relay, msg...) relay_ratelimit_printk((relay), dbg_verbose, msg)
static int relay_get_totalvfs(struct xe_guc_relay *relay)
{
@@ -345,6 +355,9 @@ int xe_guc_relay_init(struct xe_guc_relay *relay)
INIT_WORK(&relay->worker, relays_worker_fn);
INIT_LIST_HEAD(&relay->pending_relays);
INIT_LIST_HEAD(&relay->incoming_actions);
+ ratelimit_state_init(&relay->diag_ratelimit,
+ XE_RELAY_DIAG_RATELIMIT_INTERVAL,
+ XE_RELAY_DIAG_RATELIMIT_BURST);
err = mempool_init_kmalloc_pool(&relay->pool, XE_RELAY_MEMPOOL_MIN_NUM +
relay_get_totalvfs(relay),
diff --git a/drivers/gpu/drm/xe/xe_guc_relay_types.h b/drivers/gpu/drm/xe/xe_guc_relay_types.h
index 5999fcb77e96..20eee10856b2 100644
--- a/drivers/gpu/drm/xe/xe_guc_relay_types.h
+++ b/drivers/gpu/drm/xe/xe_guc_relay_types.h
@@ -7,6 +7,7 @@
#define _XE_GUC_RELAY_TYPES_H_
#include <linux/mempool.h>
+#include <linux/ratelimit_types.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@@ -31,6 +32,9 @@ struct xe_guc_relay {
/** @last_rid: last Relay-ID used while sending a message. */
u32 last_rid;
+
+ /** @diag_ratelimit: ratelimit state used to throttle diagnostics messages. */
+ struct ratelimit_state diag_ratelimit;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 94ed8159496f..ed7be50b2f72 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -70,6 +70,8 @@ exec_queue_to_guc(struct xe_exec_queue *q)
#define EXEC_QUEUE_STATE_BANNED (1 << 9)
#define EXEC_QUEUE_STATE_CHECK_TIMEOUT (1 << 10)
#define EXEC_QUEUE_STATE_EXTRA_REF (1 << 11)
+#define EXEC_QUEUE_STATE_PENDING_RESUME (1 << 12)
+#define EXEC_QUEUE_STATE_PENDING_TDR_EXIT (1 << 13)
static bool exec_queue_registered(struct xe_exec_queue *q)
{
@@ -141,6 +143,11 @@ static void set_exec_queue_destroyed(struct xe_exec_queue *q)
atomic_or(EXEC_QUEUE_STATE_DESTROYED, &q->guc->state);
}
+static void clear_exec_queue_destroyed(struct xe_exec_queue *q)
+{
+ atomic_and(~EXEC_QUEUE_STATE_DESTROYED, &q->guc->state);
+}
+
static bool exec_queue_banned(struct xe_exec_queue *q)
{
return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_BANNED;
@@ -221,6 +228,41 @@ static void set_exec_queue_extra_ref(struct xe_exec_queue *q)
atomic_or(EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state);
}
+static void clear_exec_queue_extra_ref(struct xe_exec_queue *q)
+{
+ atomic_and(~EXEC_QUEUE_STATE_EXTRA_REF, &q->guc->state);
+}
+
+static bool exec_queue_pending_resume(struct xe_exec_queue *q)
+{
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_RESUME;
+}
+
+static void set_exec_queue_pending_resume(struct xe_exec_queue *q)
+{
+ atomic_or(EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state);
+}
+
+static void clear_exec_queue_pending_resume(struct xe_exec_queue *q)
+{
+ atomic_and(~EXEC_QUEUE_STATE_PENDING_RESUME, &q->guc->state);
+}
+
+static bool exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+{
+ return atomic_read(&q->guc->state) & EXEC_QUEUE_STATE_PENDING_TDR_EXIT;
+}
+
+static void set_exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+{
+ atomic_or(EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state);
+}
+
+static void clear_exec_queue_pending_tdr_exit(struct xe_exec_queue *q)
+{
+ atomic_and(~EXEC_QUEUE_STATE_PENDING_TDR_EXIT, &q->guc->state);
+}
+
static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
{
return (atomic_read(&q->guc->state) &
@@ -670,6 +712,11 @@ static u32 wq_space_until_wrap(struct xe_exec_queue *q)
return (WQ_SIZE - q->guc->wqi_tail);
}
+static bool vf_recovery(struct xe_guc *guc)
+{
+ return xe_gt_recovery_pending(guc_to_gt(guc));
+}
+
static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
{
struct xe_guc *guc = exec_queue_to_guc(q);
@@ -679,7 +726,7 @@ static int wq_wait_for_space(struct xe_exec_queue *q, u32 wqi_size)
#define AVAILABLE_SPACE \
CIRC_SPACE(q->guc->wqi_tail, q->guc->wqi_head, WQ_SIZE)
- if (wqi_size > AVAILABLE_SPACE) {
+ if (wqi_size > AVAILABLE_SPACE && !vf_recovery(guc)) {
try_again:
q->guc->wqi_head = parallel_read(xe, map, wq_desc.head);
if (wqi_size > AVAILABLE_SPACE) {
@@ -736,18 +783,12 @@ static void wq_item_append(struct xe_exec_queue *q)
if (wq_wait_for_space(q, wqi_size))
return;
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_HEADER_DATA_0_TYPE_LEN);
wqi[i++] = FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_MULTI_LRC) |
FIELD_PREP(WQ_LEN_MASK, len_dw);
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_EL_INFO_DATA_1_CTX_DESC_LOW);
wqi[i++] = xe_lrc_descriptor(q->lrc[0]);
- xe_gt_assert(guc_to_gt(guc), i ==
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_2_GUCCTX_RINGTAIL_FREEZEPOCS);
wqi[i++] = FIELD_PREP(WQ_GUC_ID_MASK, q->guc->id) |
FIELD_PREP(WQ_RING_TAIL_MASK, q->lrc[0]->ring.tail / sizeof(u64));
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_EL_INFO_DATA_3_WI_FENCE_ID);
wqi[i++] = 0;
- xe_gt_assert(guc_to_gt(guc), i == XE_GUC_CONTEXT_WQ_EL_CHILD_LIST_DATA_4_RINGTAIL);
for (j = 1; j < q->width; ++j) {
struct xe_lrc *lrc = q->lrc[j];
@@ -768,52 +809,8 @@ static void wq_item_append(struct xe_exec_queue *q)
parallel_write(xe, map, wq_desc.tail, q->guc->wqi_tail);
}
-static int wq_items_rebase(struct xe_exec_queue *q)
-{
- struct xe_guc *guc = exec_queue_to_guc(q);
- struct xe_device *xe = guc_to_xe(guc);
- struct iosys_map map = xe_lrc_parallel_map(q->lrc[0]);
- int i = q->guc->wqi_head;
-
- /* the ring starts after a header struct */
- iosys_map_incr(&map, offsetof(struct guc_submit_parallel_scratch, wq[0]));
-
- while ((i % WQ_SIZE) != (q->guc->wqi_tail % WQ_SIZE)) {
- u32 len_dw, type, val;
-
- if (drm_WARN_ON_ONCE(&xe->drm, i < 0 || i > 2 * WQ_SIZE))
- break;
-
- val = xe_map_rd_ring_u32(xe, &map, i / sizeof(u32) +
- XE_GUC_CONTEXT_WQ_HEADER_DATA_0_TYPE_LEN,
- WQ_SIZE / sizeof(u32));
- len_dw = FIELD_GET(WQ_LEN_MASK, val);
- type = FIELD_GET(WQ_TYPE_MASK, val);
-
- if (drm_WARN_ON_ONCE(&xe->drm, len_dw >= WQ_SIZE / sizeof(u32)))
- break;
-
- if (type == WQ_TYPE_MULTI_LRC) {
- val = xe_lrc_descriptor(q->lrc[0]);
- xe_map_wr_ring_u32(xe, &map, i / sizeof(u32) +
- XE_GUC_CONTEXT_WQ_EL_INFO_DATA_1_CTX_DESC_LOW,
- WQ_SIZE / sizeof(u32), val);
- } else if (drm_WARN_ON_ONCE(&xe->drm, type != WQ_TYPE_NOOP)) {
- break;
- }
-
- i += (len_dw + 1) * sizeof(u32);
- }
-
- if ((i % WQ_SIZE) != (q->guc->wqi_tail % WQ_SIZE)) {
- xe_gt_err(q->gt, "Exec queue fixups incomplete - wqi parse failed\n");
- return -EBADMSG;
- }
- return 0;
-}
-
#define RESUME_PENDING ~0x0ull
-static void submit_exec_queue(struct xe_exec_queue *q)
+static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job)
{
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_lrc *lrc = q->lrc[0];
@@ -825,10 +822,13 @@ static void submit_exec_queue(struct xe_exec_queue *q)
xe_gt_assert(guc_to_gt(guc), exec_queue_registered(q));
- if (xe_exec_queue_is_parallel(q))
- wq_item_append(q);
- else
- xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
+ if (!job->restore_replay || job->last_replay) {
+ if (xe_exec_queue_is_parallel(q))
+ wq_item_append(q);
+ else
+ xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
+ job->last_replay = false;
+ }
if (exec_queue_suspended(q) && !xe_exec_queue_is_parallel(q))
return;
@@ -870,54 +870,33 @@ guc_exec_queue_run_job(struct drm_sched_job *drm_job)
struct xe_sched_job *job = to_xe_sched_job(drm_job);
struct xe_exec_queue *q = job->q;
struct xe_guc *guc = exec_queue_to_guc(q);
- struct dma_fence *fence = NULL;
- bool lr = xe_exec_queue_is_lr(q);
+ bool lr = xe_exec_queue_is_lr(q), killed_or_banned_or_wedged =
+ exec_queue_killed_or_banned_or_wedged(q);
xe_gt_assert(guc_to_gt(guc), !(exec_queue_destroyed(q) || exec_queue_pending_disable(q)) ||
exec_queue_banned(q) || exec_queue_suspended(q));
trace_xe_sched_job_run(job);
- if (!exec_queue_killed_or_banned_or_wedged(q) && !xe_sched_job_is_error(job)) {
+ if (!killed_or_banned_or_wedged && !xe_sched_job_is_error(job)) {
if (!exec_queue_registered(q))
register_exec_queue(q, GUC_CONTEXT_NORMAL);
- if (!lr) /* LR jobs are emitted in the exec IOCTL */
+ if (!job->restore_replay)
q->ring_ops->emit_job(job);
- submit_exec_queue(q);
- }
-
- if (lr) {
- xe_sched_job_set_error(job, -EOPNOTSUPP);
- dma_fence_put(job->fence); /* Drop ref from xe_sched_job_arm */
- } else {
- fence = job->fence;
+ submit_exec_queue(q, job);
+ job->restore_replay = false;
}
- return fence;
-}
-
-/**
- * xe_guc_jobs_ring_rebase - Re-emit ring commands of requests pending
- * on all queues under a guc.
- * @guc: the &xe_guc struct instance
- */
-void xe_guc_jobs_ring_rebase(struct xe_guc *guc)
-{
- struct xe_exec_queue *q;
- unsigned long index;
-
/*
- * This routine is used within VF migration recovery. This means
- * using the lock here introduces a restriction: we cannot wait
- * for any GFX HW response while the lock is taken.
+ * We don't care about job-fence ordering in LR VMs because these fences
+ * are never exported; they are used solely to keep jobs on the pending
+ * list. Once a queue enters an error state, there's no need to track
+ * them.
*/
- mutex_lock(&guc->submission_state.lock);
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
- if (exec_queue_killed_or_banned_or_wedged(q))
- continue;
- xe_exec_queue_jobs_ring_restore(q);
- }
- mutex_unlock(&guc->submission_state.lock);
+ if (killed_or_banned_or_wedged && lr)
+ xe_sched_job_set_error(job, -ECANCELED);
+
+ return job->fence;
}
static void guc_exec_queue_free_job(struct drm_sched_job *drm_job)
@@ -951,15 +930,17 @@ static void disable_scheduling_deregister(struct xe_guc *guc,
ret = wait_event_timeout(guc->ct.wq,
(!exec_queue_pending_enable(q) &&
!exec_queue_pending_disable(q)) ||
- xe_guc_read_stopped(guc),
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc),
HZ * 5);
- if (!ret) {
+ if (!ret && !vf_recovery(guc)) {
struct xe_gpu_scheduler *sched = &q->guc->sched;
xe_gt_warn(q->gt, "Pending enable/disable failed to respond\n");
xe_sched_submission_start(sched);
xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(sched);
+ if (!xe_exec_queue_is_lr(q))
+ xe_sched_tdr_queue_imm(sched);
return;
}
@@ -1051,9 +1032,14 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
struct xe_exec_queue *q = ge->q;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_gpu_scheduler *sched = &ge->sched;
+ struct xe_sched_job *job;
bool wedged = false;
xe_gt_assert(guc_to_gt(guc), xe_exec_queue_is_lr(q));
+
+ if (vf_recovery(guc))
+ return;
+
trace_xe_exec_queue_lr_cleanup(q);
if (!exec_queue_killed(q))
@@ -1086,7 +1072,11 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
*/
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_disable(q) ||
- xe_guc_read_stopped(guc), HZ * 5);
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc), HZ * 5);
+ if (vf_recovery(guc))
+ return;
+
if (!ret) {
xe_gt_warn(q->gt, "Schedule disable failed to respond, guc_id=%d\n",
q->guc->id);
@@ -1101,7 +1091,16 @@ static void xe_guc_exec_queue_lr_cleanup(struct work_struct *w)
if (!exec_queue_killed(q) && !xe_lrc_ring_is_idle(q->lrc[0]))
xe_devcoredump(q, NULL, "LR job cleanup, guc_id=%d", q->guc->id);
+ xe_hw_fence_irq_stop(q->fence_irq);
+
xe_sched_submission_start(sched);
+
+ spin_lock(&sched->base.job_list_lock);
+ list_for_each_entry(job, &sched->base.pending_list, drm.list)
+ xe_sched_job_set_error(job, -ECANCELED);
+ spin_unlock(&sched->base.job_list_lock);
+
+ xe_hw_fence_irq_start(q->fence_irq);
}
#define ADJUST_FIVE_PERCENT(__t) mul_u64_u32_div(__t, 105, 100)
@@ -1167,12 +1166,14 @@ static void enable_scheduling(struct xe_exec_queue *q)
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_enable(q) ||
- xe_guc_read_stopped(guc), HZ * 5);
- if (!ret || xe_guc_read_stopped(guc)) {
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc), HZ * 5);
+ if ((!ret && !vf_recovery(guc)) || xe_guc_read_stopped(guc)) {
xe_gt_warn(guc_to_gt(guc), "Schedule enable failed to respond");
set_exec_queue_banned(q);
xe_gt_reset_async(q->gt);
- xe_sched_tdr_queue_imm(&q->guc->sched);
+ if (!xe_exec_queue_is_lr(q))
+ xe_sched_tdr_queue_imm(&q->guc->sched);
}
}
@@ -1230,13 +1231,16 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
int i = 0;
bool wedged = false, skip_timeout_check;
+ xe_gt_assert(guc_to_gt(guc), !xe_exec_queue_is_lr(q));
+
/*
* TDR has fired before free job worker. Common if exec queue
* immediately closed after last fence signaled. Add back to pending
* list so job can be freed and kick scheduler ensuring free job is not
* lost.
*/
- if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &job->fence->flags))
+ if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &job->fence->flags) ||
+ vf_recovery(guc))
return DRM_GPU_SCHED_STAT_NO_HANG;
/* Kill the run_job entry point */
@@ -1288,7 +1292,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
ret = wait_event_timeout(guc->ct.wq,
(!exec_queue_pending_enable(q) &&
!exec_queue_pending_disable(q)) ||
- xe_guc_read_stopped(guc), HZ * 5);
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc), HZ * 5);
+ if (vf_recovery(guc))
+ goto handle_vf_resume;
if (!ret || xe_guc_read_stopped(guc))
goto trigger_reset;
@@ -1313,7 +1320,10 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
smp_rmb();
ret = wait_event_timeout(guc->ct.wq,
!exec_queue_pending_disable(q) ||
- xe_guc_read_stopped(guc), HZ * 5);
+ xe_guc_read_stopped(guc) ||
+ vf_recovery(guc), HZ * 5);
+ if (vf_recovery(guc))
+ goto handle_vf_resume;
if (!ret || xe_guc_read_stopped(guc)) {
trigger_reset:
if (!ret)
@@ -1409,6 +1419,7 @@ trigger_reset:
return DRM_GPU_SCHED_STAT_RESET;
sched_enable:
+ set_exec_queue_pending_tdr_exit(q);
enable_scheduling(q);
rearm:
/*
@@ -1417,6 +1428,7 @@ rearm:
* some thought, do this in a follow up.
*/
xe_sched_submission_start(sched);
+handle_vf_resume:
return DRM_GPU_SCHED_STAT_NO_HANG;
}
@@ -1523,11 +1535,24 @@ static void __guc_exec_queue_process_msg_set_sched_props(struct xe_sched_msg *ms
static void __suspend_fence_signal(struct xe_exec_queue *q)
{
+ struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
+
if (!q->guc->suspend_pending)
return;
WRITE_ONCE(q->guc->suspend_pending, false);
- wake_up(&q->guc->suspend_wait);
+
+ /*
+ * We use a GuC shared wait queue for VFs because the VF resfix start
+ * interrupt must be able to wake all instances of suspend_wait. This
+ * prevents the VF migration worker from being starved during
+ * scheduling.
+ */
+ if (IS_SRIOV_VF(xe))
+ wake_up_all(&guc->ct.wq);
+ else
+ wake_up(&q->guc->suspend_wait);
}
static void suspend_fence_signal(struct xe_exec_queue *q)
@@ -1548,8 +1573,9 @@ static void __guc_exec_queue_process_msg_suspend(struct xe_sched_msg *msg)
if (guc_exec_queue_allowed_to_change_state(q) && !exec_queue_suspended(q) &&
exec_queue_enabled(q)) {
- wait_event(guc->ct.wq, (q->guc->resume_time != RESUME_PENDING ||
- xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q));
+ wait_event(guc->ct.wq, vf_recovery(guc) ||
+ ((q->guc->resume_time != RESUME_PENDING ||
+ xe_guc_read_stopped(guc)) && !exec_queue_pending_disable(q)));
if (!xe_guc_read_stopped(guc)) {
s64 since_resume_ms =
@@ -1578,6 +1604,7 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
clear_exec_queue_suspended(q);
if (!exec_queue_enabled(q)) {
q->guc->resume_time = RESUME_PENDING;
+ set_exec_queue_pending_resume(q);
enable_scheduling(q);
}
} else {
@@ -1591,6 +1618,7 @@ static void __guc_exec_queue_process_msg_resume(struct xe_sched_msg *msg)
#define RESUME 4
#define OPCODE_MASK 0xf
#define MSG_LOCKED BIT(8)
+#define MSG_HEAD BIT(9)
static void guc_exec_queue_process_msg(struct xe_sched_msg *msg)
{
@@ -1653,7 +1681,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
timeout = (q->vm && xe_vm_in_lr_mode(q->vm)) ? MAX_SCHEDULE_TIMEOUT :
msecs_to_jiffies(q->sched_props.job_timeout_ms);
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
- NULL, q->lrc[0]->ring.size / MAX_JOB_SIZE_BYTES, 64,
+ NULL, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64,
timeout, guc_to_gt(guc)->ordered_wq, NULL,
q->name, gt_to_xe(q->gt)->drm.dev);
if (err)
@@ -1675,7 +1703,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
q->entity = &ge->entity;
- if (xe_guc_read_stopped(guc))
+ if (xe_guc_read_stopped(guc) || vf_recovery(guc))
xe_sched_stop(sched);
mutex_unlock(&guc->submission_state.lock);
@@ -1715,12 +1743,24 @@ static void guc_exec_queue_add_msg(struct xe_exec_queue *q, struct xe_sched_msg
msg->private_data = q;
trace_xe_sched_msg_add(msg);
- if (opcode & MSG_LOCKED)
+ if (opcode & MSG_HEAD)
+ xe_sched_add_msg_head(&q->guc->sched, msg);
+ else if (opcode & MSG_LOCKED)
xe_sched_add_msg_locked(&q->guc->sched, msg);
else
xe_sched_add_msg(&q->guc->sched, msg);
}
+static void guc_exec_queue_try_add_msg_head(struct xe_exec_queue *q,
+ struct xe_sched_msg *msg,
+ u32 opcode)
+{
+ if (!list_empty(&msg->link))
+ return;
+
+ guc_exec_queue_add_msg(q, msg, opcode | MSG_LOCKED | MSG_HEAD);
+}
+
static bool guc_exec_queue_try_add_msg(struct xe_exec_queue *q,
struct xe_sched_msg *msg,
u32 opcode)
@@ -1821,6 +1861,7 @@ static int guc_exec_queue_suspend(struct xe_exec_queue *q)
static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
+ struct xe_device *xe = guc_to_xe(guc);
int ret;
/*
@@ -1828,11 +1869,21 @@ static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
* suspend_pending upon kill but to be paranoid but races in which
* suspend_pending is set after kill also check kill here.
*/
- ret = wait_event_interruptible_timeout(q->guc->suspend_wait,
- !READ_ONCE(q->guc->suspend_pending) ||
- exec_queue_killed(q) ||
- xe_guc_read_stopped(guc),
- HZ * 5);
+#define WAIT_COND \
+ (!READ_ONCE(q->guc->suspend_pending) || exec_queue_killed(q) || \
+ xe_guc_read_stopped(guc))
+
+retry:
+ if (IS_SRIOV_VF(xe))
+ ret = wait_event_interruptible_timeout(guc->ct.wq, WAIT_COND ||
+ vf_recovery(guc),
+ HZ * 5);
+ else
+ ret = wait_event_interruptible_timeout(q->guc->suspend_wait,
+ WAIT_COND, HZ * 5);
+
+ if (vf_recovery(guc) && !xe_device_wedged((guc_to_xe(guc))))
+ return -EAGAIN;
if (!ret) {
xe_gt_warn(guc_to_gt(guc),
@@ -1840,8 +1891,13 @@ static int guc_exec_queue_suspend_wait(struct xe_exec_queue *q)
q->guc->id);
/* XXX: Trigger GT reset? */
return -ETIME;
+ } else if (IS_SRIOV_VF(xe) && !WAIT_COND) {
+ /* Corner case on RESFIX DONE where vf_recovery() changes */
+ goto retry;
}
+#undef WAIT_COND
+
return ret < 0 ? ret : 0;
}
@@ -1864,7 +1920,7 @@ static bool guc_exec_queue_reset_status(struct xe_exec_queue *q)
}
/*
- * All of these functions are an abstraction layer which other parts of XE can
+ * All of these functions are an abstraction layer which other parts of Xe can
* use to trap into the GuC backend. All of these functions, aside from init,
* really shouldn't do much other than trap into the DRM scheduler which
* synchronizes these operations.
@@ -1936,47 +1992,13 @@ static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q)
}
}
-/**
- * xe_guc_submit_reset_block - Disallow reset calls on given GuC.
- * @guc: the &xe_guc struct instance
- */
-int xe_guc_submit_reset_block(struct xe_guc *guc)
-{
- return atomic_fetch_or(1, &guc->submission_state.reset_blocked);
-}
-
-/**
- * xe_guc_submit_reset_unblock - Allow back reset calls on given GuC.
- * @guc: the &xe_guc struct instance
- */
-void xe_guc_submit_reset_unblock(struct xe_guc *guc)
-{
- atomic_set_release(&guc->submission_state.reset_blocked, 0);
- wake_up_all(&guc->ct.wq);
-}
-
-static int guc_submit_reset_is_blocked(struct xe_guc *guc)
-{
- return atomic_read_acquire(&guc->submission_state.reset_blocked);
-}
-
-/* Maximum time of blocking reset */
-#define RESET_BLOCK_PERIOD_MAX (HZ * 5)
-
-/**
- * xe_guc_wait_reset_unblock - Wait until reset blocking flag is lifted, or timeout.
- * @guc: the &xe_guc struct instance
- */
-int xe_guc_wait_reset_unblock(struct xe_guc *guc)
-{
- return wait_event_timeout(guc->ct.wq,
- !guc_submit_reset_is_blocked(guc), RESET_BLOCK_PERIOD_MAX);
-}
-
int xe_guc_submit_reset_prepare(struct xe_guc *guc)
{
int ret;
+ if (xe_gt_WARN_ON(guc_to_gt(guc), vf_recovery(guc)))
+ return 0;
+
if (!guc->submission_state.initialized)
return 0;
@@ -2026,6 +2048,131 @@ void xe_guc_submit_stop(struct xe_guc *guc)
}
+static void guc_exec_queue_revert_pending_state_change(struct xe_guc *guc,
+ struct xe_exec_queue *q)
+{
+ bool pending_enable, pending_disable, pending_resume;
+
+ pending_enable = exec_queue_pending_enable(q);
+ pending_resume = exec_queue_pending_resume(q);
+
+ if (pending_enable && pending_resume) {
+ q->guc->needs_resume = true;
+ xe_gt_dbg(guc_to_gt(guc), "Replay RESUME - guc_id=%d",
+ q->guc->id);
+ }
+
+ if (pending_enable && !pending_resume &&
+ !exec_queue_pending_tdr_exit(q)) {
+ clear_exec_queue_registered(q);
+ if (xe_exec_queue_is_lr(q))
+ xe_exec_queue_put(q);
+ xe_gt_dbg(guc_to_gt(guc), "Replay REGISTER - guc_id=%d",
+ q->guc->id);
+ }
+
+ if (pending_enable) {
+ clear_exec_queue_enabled(q);
+ clear_exec_queue_pending_resume(q);
+ clear_exec_queue_pending_tdr_exit(q);
+ clear_exec_queue_pending_enable(q);
+ xe_gt_dbg(guc_to_gt(guc), "Replay ENABLE - guc_id=%d",
+ q->guc->id);
+ }
+
+ if (exec_queue_destroyed(q) && exec_queue_registered(q)) {
+ clear_exec_queue_destroyed(q);
+ if (exec_queue_extra_ref(q))
+ xe_exec_queue_put(q);
+ else
+ q->guc->needs_cleanup = true;
+ clear_exec_queue_extra_ref(q);
+ xe_gt_dbg(guc_to_gt(guc), "Replay CLEANUP - guc_id=%d",
+ q->guc->id);
+ }
+
+ pending_disable = exec_queue_pending_disable(q);
+
+ if (pending_disable && exec_queue_suspended(q)) {
+ clear_exec_queue_suspended(q);
+ q->guc->needs_suspend = true;
+ xe_gt_dbg(guc_to_gt(guc), "Replay SUSPEND - guc_id=%d",
+ q->guc->id);
+ }
+
+ if (pending_disable) {
+ if (!pending_enable)
+ set_exec_queue_enabled(q);
+ clear_exec_queue_pending_disable(q);
+ clear_exec_queue_check_timeout(q);
+ xe_gt_dbg(guc_to_gt(guc), "Replay DISABLE - guc_id=%d",
+ q->guc->id);
+ }
+
+ q->guc->resume_time = 0;
+}
+
+static void lrc_parallel_clear(struct xe_lrc *lrc)
+{
+ struct xe_device *xe = gt_to_xe(lrc->gt);
+ struct iosys_map map = xe_lrc_parallel_map(lrc);
+ int i;
+
+ for (i = 0; i < WQ_SIZE / sizeof(u32); ++i)
+ parallel_write(xe, map, wq[i],
+ FIELD_PREP(WQ_TYPE_MASK, WQ_TYPE_NOOP) |
+ FIELD_PREP(WQ_LEN_MASK, 0));
+}
+
+/*
+ * This function is quite complex but only real way to ensure no state is lost
+ * during VF resume flows. The function scans the queue state, make adjustments
+ * as needed, and queues jobs / messages which replayed upon unpause.
+ */
+static void guc_exec_queue_pause(struct xe_guc *guc, struct xe_exec_queue *q)
+{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+ struct xe_sched_job *job;
+ int i;
+
+ lockdep_assert_held(&guc->submission_state.lock);
+
+ /* Stop scheduling + flush any DRM scheduler operations */
+ xe_sched_submission_stop(sched);
+ if (xe_exec_queue_is_lr(q))
+ cancel_work_sync(&q->guc->lr_tdr);
+ else
+ cancel_delayed_work_sync(&sched->base.work_tdr);
+
+ guc_exec_queue_revert_pending_state_change(guc, q);
+
+ if (xe_exec_queue_is_parallel(q)) {
+ /* Pairs with WRITE_ONCE in __xe_exec_queue_init */
+ struct xe_lrc *lrc = READ_ONCE(q->lrc[0]);
+
+ /*
+ * NOP existing WQ commands that may contain stale GGTT
+ * addresses. These will be replayed upon unpause. The hardware
+ * seems to get confused if the WQ head/tail pointers are
+ * adjusted.
+ */
+ if (lrc)
+ lrc_parallel_clear(lrc);
+ }
+
+ job = xe_sched_first_pending_job(sched);
+ if (job) {
+ job->restore_replay = true;
+
+ /*
+ * Adjust software tail so jobs submitted overwrite previous
+ * position in ring buffer with new GGTT addresses.
+ */
+ for (i = 0; i < q->width; ++i)
+ q->lrc[i]->ring.tail = job->ptrs[i].head;
+ }
+}
+
/**
* xe_guc_submit_pause - Stop further runs of submission tasks on given GuC.
* @guc: the &xe_guc struct instance whose scheduler is to be disabled
@@ -2035,8 +2182,17 @@ void xe_guc_submit_pause(struct xe_guc *guc)
struct xe_exec_queue *q;
unsigned long index;
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- xe_sched_submission_stop_async(&q->guc->sched);
+ xe_gt_assert(guc_to_gt(guc), vf_recovery(guc));
+
+ mutex_lock(&guc->submission_state.lock);
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
+ /* Prevent redundant attempts to stop parallel queues */
+ if (q->guc->id != index)
+ continue;
+
+ guc_exec_queue_pause(guc, q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
}
static void guc_exec_queue_start(struct xe_exec_queue *q)
@@ -2044,11 +2200,25 @@ static void guc_exec_queue_start(struct xe_exec_queue *q)
struct xe_gpu_scheduler *sched = &q->guc->sched;
if (!exec_queue_killed_or_banned_or_wedged(q)) {
+ struct xe_sched_job *job = xe_sched_first_pending_job(sched);
int i;
trace_xe_exec_queue_resubmit(q);
- for (i = 0; i < q->width; ++i)
- xe_lrc_set_ring_head(q->lrc[i], q->lrc[i]->ring.tail);
+ if (job) {
+ for (i = 0; i < q->width; ++i) {
+ /*
+ * The GuC context is unregistered at this point
+ * time, adjusting software ring tail ensures
+ * jobs are rewritten in original placement,
+ * adjusting LRC tail ensures the newly loaded
+ * GuC / contexts only view the LRC tail
+ * increasing as jobs are written out.
+ */
+ q->lrc[i]->ring.tail = job->ptrs[i].head;
+ xe_lrc_set_ring_tail(q->lrc[i],
+ xe_lrc_ring_head(q->lrc[i]));
+ }
+ }
xe_sched_resubmit_jobs(sched);
}
@@ -2079,11 +2249,101 @@ int xe_guc_submit_start(struct xe_guc *guc)
return 0;
}
-static void guc_exec_queue_unpause(struct xe_exec_queue *q)
+static void guc_exec_queue_unpause_prepare(struct xe_guc *guc,
+ struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched = &q->guc->sched;
+ struct xe_sched_job *job = NULL;
+ bool restore_replay = false;
+
+ list_for_each_entry(job, &sched->base.pending_list, drm.list) {
+ restore_replay |= job->restore_replay;
+ if (restore_replay) {
+ xe_gt_dbg(guc_to_gt(guc), "Replay JOB - guc_id=%d, seqno=%d",
+ q->guc->id, xe_sched_job_seqno(job));
+
+ q->ring_ops->emit_job(job);
+ job->restore_replay = true;
+ }
+ }
+
+ if (job)
+ job->last_replay = true;
+}
+/**
+ * xe_guc_submit_unpause_prepare - Prepare unpause submission tasks on given GuC.
+ * @guc: the &xe_guc struct instance whose scheduler is to be prepared for unpause
+ */
+void xe_guc_submit_unpause_prepare(struct xe_guc *guc)
+{
+ struct xe_exec_queue *q;
+ unsigned long index;
+
+ xe_gt_assert(guc_to_gt(guc), vf_recovery(guc));
+
+ mutex_lock(&guc->submission_state.lock);
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
+ /* Prevent redundant attempts to stop parallel queues */
+ if (q->guc->id != index)
+ continue;
+
+ guc_exec_queue_unpause_prepare(guc, q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
+}
+
+static void guc_exec_queue_replay_pending_state_change(struct xe_exec_queue *q)
+{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+ struct xe_sched_msg *msg;
+
+ if (q->guc->needs_cleanup) {
+ msg = q->guc->static_msgs + STATIC_MSG_CLEANUP;
+
+ guc_exec_queue_add_msg(q, msg, CLEANUP);
+ q->guc->needs_cleanup = false;
+ }
+
+ if (q->guc->needs_suspend) {
+ msg = q->guc->static_msgs + STATIC_MSG_SUSPEND;
+
+ xe_sched_msg_lock(sched);
+ guc_exec_queue_try_add_msg_head(q, msg, SUSPEND);
+ xe_sched_msg_unlock(sched);
+
+ q->guc->needs_suspend = false;
+ }
+
+ /*
+ * The resume must be in the message queue before the suspend as it is
+ * not possible for a resume to be issued if a suspend pending is, but
+ * the inverse is possible.
+ */
+ if (q->guc->needs_resume) {
+ msg = q->guc->static_msgs + STATIC_MSG_RESUME;
+
+ xe_sched_msg_lock(sched);
+ guc_exec_queue_try_add_msg_head(q, msg, RESUME);
+ xe_sched_msg_unlock(sched);
+
+ q->guc->needs_resume = false;
+ }
+}
+
+static void guc_exec_queue_unpause(struct xe_guc *guc, struct xe_exec_queue *q)
+{
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+ bool needs_tdr = exec_queue_killed_or_banned_or_wedged(q);
+
+ lockdep_assert_held(&guc->submission_state.lock);
+
+ xe_sched_resubmit_jobs(sched);
+ guc_exec_queue_replay_pending_state_change(q);
xe_sched_submission_start(sched);
+ if (needs_tdr)
+ xe_guc_exec_queue_trigger_cleanup(q);
+ xe_sched_submission_resume_tdr(sched);
}
/**
@@ -2095,10 +2355,43 @@ void xe_guc_submit_unpause(struct xe_guc *guc)
struct xe_exec_queue *q;
unsigned long index;
- xa_for_each(&guc->submission_state.exec_queue_lookup, index, q)
- guc_exec_queue_unpause(q);
+ mutex_lock(&guc->submission_state.lock);
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
+ /*
+ * Prevent redundant attempts to stop parallel queues, or queues
+ * created after resfix done.
+ */
+ if (q->guc->id != index ||
+ !READ_ONCE(q->guc->sched.base.pause_submit))
+ continue;
- wake_up_all(&guc->ct.wq);
+ guc_exec_queue_unpause(guc, q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
+}
+
+/**
+ * xe_guc_submit_pause_abort - Abort all paused submission task on given GuC.
+ * @guc: the &xe_guc struct instance whose scheduler is to be aborted
+ */
+void xe_guc_submit_pause_abort(struct xe_guc *guc)
+{
+ struct xe_exec_queue *q;
+ unsigned long index;
+
+ mutex_lock(&guc->submission_state.lock);
+ xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
+ struct xe_gpu_scheduler *sched = &q->guc->sched;
+
+ /* Prevent redundant attempts to stop parallel queues */
+ if (q->guc->id != index)
+ continue;
+
+ xe_sched_submission_start(sched);
+ if (exec_queue_killed_or_banned_or_wedged(q))
+ xe_guc_exec_queue_trigger_cleanup(q);
+ }
+ mutex_unlock(&guc->submission_state.lock);
}
static struct xe_exec_queue *
@@ -2150,6 +2443,8 @@ static void handle_sched_done(struct xe_guc *guc, struct xe_exec_queue *q,
xe_gt_assert(guc_to_gt(guc), exec_queue_pending_enable(q));
q->guc->resume_time = ktime_get();
+ clear_exec_queue_pending_resume(q);
+ clear_exec_queue_pending_tdr_exit(q);
clear_exec_queue_pending_enable(q);
smp_wmb();
wake_up_all(&guc->ct.wq);
@@ -2677,13 +2972,13 @@ int xe_guc_contexts_hwsp_rebase(struct xe_guc *guc, void *scratch)
mutex_lock(&guc->submission_state.lock);
xa_for_each(&guc->submission_state.exec_queue_lookup, index, q) {
+ /* Prevent redundant attempts to stop parallel queues */
+ if (q->guc->id != index)
+ continue;
+
err = xe_exec_queue_contexts_hwsp_rebase(q, scratch);
if (err)
break;
- if (xe_exec_queue_is_parallel(q))
- err = wq_items_rebase(q);
- if (err)
- break;
}
mutex_unlock(&guc->submission_state.lock);
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.h b/drivers/gpu/drm/xe/xe_guc_submit.h
index 78c3f07e31a0..b49a2748ec46 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.h
+++ b/drivers/gpu/drm/xe/xe_guc_submit.h
@@ -22,9 +22,8 @@ void xe_guc_submit_stop(struct xe_guc *guc);
int xe_guc_submit_start(struct xe_guc *guc);
void xe_guc_submit_pause(struct xe_guc *guc);
void xe_guc_submit_unpause(struct xe_guc *guc);
-int xe_guc_submit_reset_block(struct xe_guc *guc);
-void xe_guc_submit_reset_unblock(struct xe_guc *guc);
-int xe_guc_wait_reset_unblock(struct xe_guc *guc);
+void xe_guc_submit_unpause_prepare(struct xe_guc *guc);
+void xe_guc_submit_pause_abort(struct xe_guc *guc);
void xe_guc_submit_wedge(struct xe_guc *guc);
int xe_guc_read_stopped(struct xe_guc *guc);
@@ -36,8 +35,6 @@ int xe_guc_exec_queue_memory_cat_error_handler(struct xe_guc *guc, u32 *msg,
int xe_guc_exec_queue_reset_failure_handler(struct xe_guc *guc, u32 *msg, u32 len);
int xe_guc_error_capture_handler(struct xe_guc *guc, u32 *msg, u32 len);
-void xe_guc_jobs_ring_rebase(struct xe_guc *guc);
-
struct xe_guc_submit_exec_queue_snapshot *
xe_guc_exec_queue_snapshot_capture(struct xe_exec_queue *q);
void
diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c
index 6bf2103602f8..a80175c7c478 100644
--- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c
+++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c
@@ -207,7 +207,7 @@ static const struct xe_tlb_inval_ops guc_tlb_inval_ops = {
* @guc: GuC object
* @tlb_inval: TLB invalidation client
*
- * Inititialize GuC TLB invalidation by setting back pointer in TLB invalidation
+ * Initialize GuC TLB invalidation by setting back pointer in TLB invalidation
* client to the GuC and setting GuC backend ops.
*/
void xe_guc_tlb_inval_init_early(struct xe_guc *guc,
diff --git a/drivers/gpu/drm/xe/xe_heci_gsc.c b/drivers/gpu/drm/xe/xe_heci_gsc.c
index a415ca488791..2b3d49dd394c 100644
--- a/drivers/gpu/drm/xe/xe_heci_gsc.c
+++ b/drivers/gpu/drm/xe/xe_heci_gsc.c
@@ -8,6 +8,8 @@
#include <linux/pci.h>
#include <linux/sizes.h>
+#include <drm/drm_print.h>
+
#include "xe_device_types.h"
#include "xe_drv.h"
#include "xe_heci_gsc.h"
diff --git a/drivers/gpu/drm/xe/xe_huc.c b/drivers/gpu/drm/xe/xe_huc.c
index 7e43b2dd6a32..0a70c8924582 100644
--- a/drivers/gpu/drm/xe/xe_huc.c
+++ b/drivers/gpu/drm/xe/xe_huc.c
@@ -66,14 +66,18 @@ static int huc_alloc_gsc_pkt(struct xe_huc *huc)
int xe_huc_init(struct xe_huc *huc)
{
struct xe_gt *gt = huc_to_gt(huc);
- struct xe_tile *tile = gt_to_tile(gt);
struct xe_device *xe = gt_to_xe(gt);
int ret;
huc->fw.type = XE_UC_FW_TYPE_HUC;
- /* On platforms with a media GT the HuC is only available there */
- if (tile->media_gt && (gt != tile->media_gt)) {
+ /*
+ * The HuC is only available on the media GT on most platforms. The
+ * exception to that rule are the old Xe1 platforms where there was
+ * no separate GT for media IP, so the HuC was part of the primary
+ * GT. Such platforms have graphics versions 12.55 and earlier.
+ */
+ if (!xe_gt_is_media_type(gt) && GRAPHICS_VERx100(xe) > 1255) {
xe_uc_fw_change_status(&huc->fw, XE_UC_FIRMWARE_NOT_SUPPORTED);
return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c
index 1cf623b4a5bc..6a9e2a4272dd 100644
--- a/drivers/gpu/drm/xe/xe_hw_engine.c
+++ b/drivers/gpu/drm/xe/xe_hw_engine.c
@@ -346,17 +346,26 @@ void xe_hw_engine_enable_ring(struct xe_hw_engine *hwe)
xe_hw_engine_mmio_read32(hwe, RING_MI_MODE(0));
}
-static bool xe_hw_engine_match_fixed_cslice_mode(const struct xe_gt *gt,
+static bool xe_hw_engine_match_fixed_cslice_mode(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
+ /*
+ * Xe3p no longer supports load balance mode, so "fixed cslice" mode
+ * is automatic and no RCU_MODE programming is required.
+ */
+ if (GRAPHICS_VER(gt_to_xe(gt)) >= 35)
+ return false;
+
return xe_gt_ccs_mode_enabled(gt) &&
- xe_rtp_match_first_render_or_compute(gt, hwe);
+ xe_rtp_match_first_render_or_compute(xe, gt, hwe);
}
-static bool xe_rtp_cfeg_wmtp_disabled(const struct xe_gt *gt,
+static bool xe_rtp_cfeg_wmtp_disabled(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
- if (GRAPHICS_VER(gt_to_xe(gt)) < 20)
+ if (GRAPHICS_VER(xe) < 20)
return false;
if (hwe->class != XE_ENGINE_CLASS_COMPUTE &&
@@ -709,27 +718,52 @@ static void read_media_fuses(struct xe_gt *gt)
}
}
+static u32 infer_svccopy_from_meml3(struct xe_gt *gt)
+{
+ u32 meml3 = REG_FIELD_GET(MEML3_EN_MASK,
+ xe_mmio_read32(&gt->mmio, MIRROR_FUSE3));
+ u32 svccopy_mask = 0;
+
+ /*
+ * Each of the four meml3 bits determines the fusing of two service
+ * copy engines.
+ */
+ for (int i = 0; i < 4; i++)
+ svccopy_mask |= (meml3 & BIT(i)) ? 0b11 << 2 * i : 0;
+
+ return svccopy_mask;
+}
+
+static u32 read_svccopy_fuses(struct xe_gt *gt)
+{
+ return REG_FIELD_GET(FUSE_SERVICE_COPY_ENABLE_MASK,
+ xe_mmio_read32(&gt->mmio, SERVICE_COPY_ENABLE));
+}
+
static void read_copy_fuses(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
u32 bcs_mask;
- if (GRAPHICS_VERx100(xe) < 1260 || GRAPHICS_VERx100(xe) >= 1270)
- return;
-
xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
- bcs_mask = xe_mmio_read32(&gt->mmio, MIRROR_FUSE3);
- bcs_mask = REG_FIELD_GET(MEML3_EN_MASK, bcs_mask);
+ if (GRAPHICS_VER(xe) >= 35)
+ bcs_mask = read_svccopy_fuses(gt);
+ else if (GRAPHICS_VERx100(xe) == 1260)
+ bcs_mask = infer_svccopy_from_meml3(gt);
+ else
+ return;
- /* BCS0 is always present; only BCS1-BCS8 may be fused off */
- for (int i = XE_HW_ENGINE_BCS1, j = 0; i <= XE_HW_ENGINE_BCS8; ++i, ++j) {
+ /* Only BCS1-BCS8 may be fused off */
+ bcs_mask <<= XE_HW_ENGINE_BCS1;
+ for (int i = XE_HW_ENGINE_BCS1; i <= XE_HW_ENGINE_BCS8; ++i) {
if (!(gt->info.engine_mask & BIT(i)))
continue;
- if (!(BIT(j / 2) & bcs_mask)) {
+ if (!(bcs_mask & BIT(i))) {
gt->info.engine_mask &= ~BIT(i);
- xe_gt_info(gt, "bcs%u fused off\n", j);
+ xe_gt_info(gt, "bcs%u fused off\n",
+ i - XE_HW_ENGINE_BCS0);
}
}
}
@@ -870,7 +904,7 @@ void xe_hw_engine_handle_irq(struct xe_hw_engine *hwe, u16 intr_vec)
if (hwe->irq_handler)
hwe->irq_handler(hwe, intr_vec);
- if (intr_vec & GT_RENDER_USER_INTERRUPT)
+ if (intr_vec & GT_MI_USER_INTERRUPT)
xe_hw_fence_irq_run(hwe->fence_irq);
}
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index b6790589e623..97879daeefc1 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -658,8 +658,6 @@ static umode_t xe_hwmon_attributes_visible(struct kobject *kobj,
struct xe_reg rapl_limit;
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
- xe_pm_runtime_get(hwmon->xe);
-
if (hwmon->xe->info.has_mbx_power_limits) {
xe_hwmon_pcode_read_power_limit(hwmon, power_attr, channel, &uval);
} else if (power_attr != PL2_HWMON_ATTR) {
@@ -669,8 +667,6 @@ static umode_t xe_hwmon_attributes_visible(struct kobject *kobj,
}
ret = (uval & PWR_LIM_EN) ? attr->mode : 0;
- xe_pm_runtime_put(hwmon->xe);
-
return ret;
}
@@ -1096,8 +1092,6 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
struct xe_hwmon *hwmon = (struct xe_hwmon *)drvdata;
int ret;
- xe_pm_runtime_get(hwmon->xe);
-
switch (type) {
case hwmon_temp:
ret = xe_hwmon_temp_is_visible(hwmon, attr, channel);
@@ -1122,8 +1116,6 @@ xe_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
break;
}
- xe_pm_runtime_put(hwmon->xe);
-
return ret;
}
diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c
index 48dfcb41fa08..0b5452be0c87 100644
--- a/drivers/gpu/drm/xe/xe_i2c.c
+++ b/drivers/gpu/drm/xe/xe_i2c.c
@@ -160,6 +160,11 @@ bool xe_i2c_present(struct xe_device *xe)
return xe->i2c && xe->i2c->ep.cookie == XE_I2C_EP_COOKIE_DEVICE;
}
+static bool xe_i2c_irq_present(struct xe_device *xe)
+{
+ return xe->i2c && xe->i2c->adapter_irq;
+}
+
/**
* xe_i2c_irq_handler: Handler for I2C interrupts
* @xe: xe device instance
@@ -170,13 +175,33 @@ bool xe_i2c_present(struct xe_device *xe)
*/
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl)
{
- if (!xe->i2c || !xe->i2c->adapter_irq)
+ if (!xe_i2c_irq_present(xe))
return;
if (master_ctl & I2C_IRQ)
generic_handle_irq_safe(xe->i2c->adapter_irq);
}
+void xe_i2c_irq_reset(struct xe_device *xe)
+{
+ struct xe_mmio *mmio = xe_root_tile_mmio(xe);
+
+ if (!xe_i2c_irq_present(xe))
+ return;
+
+ xe_mmio_rmw32(mmio, I2C_BRIDGE_PCICFGCTL, ACPI_INTR_EN, 0);
+}
+
+void xe_i2c_irq_postinstall(struct xe_device *xe)
+{
+ struct xe_mmio *mmio = xe_root_tile_mmio(xe);
+
+ if (!xe_i2c_irq_present(xe))
+ return;
+
+ xe_mmio_rmw32(mmio, I2C_BRIDGE_PCICFGCTL, 0, ACPI_INTR_EN);
+}
+
static int xe_i2c_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw_irq_num)
{
@@ -334,6 +359,7 @@ int xe_i2c_probe(struct xe_device *xe)
if (ret)
goto err_remove_irq;
+ xe_i2c_irq_postinstall(xe);
return devm_add_action_or_reset(drm_dev, xe_i2c_remove, i2c);
err_remove_irq:
diff --git a/drivers/gpu/drm/xe/xe_i2c.h b/drivers/gpu/drm/xe/xe_i2c.h
index ecd5f10358e2..425d8160835f 100644
--- a/drivers/gpu/drm/xe/xe_i2c.h
+++ b/drivers/gpu/drm/xe/xe_i2c.h
@@ -51,12 +51,16 @@ struct xe_i2c {
int xe_i2c_probe(struct xe_device *xe);
bool xe_i2c_present(struct xe_device *xe);
void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl);
+void xe_i2c_irq_postinstall(struct xe_device *xe);
+void xe_i2c_irq_reset(struct xe_device *xe);
void xe_i2c_pm_suspend(struct xe_device *xe);
void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold);
#else
static inline int xe_i2c_probe(struct xe_device *xe) { return 0; }
static inline bool xe_i2c_present(struct xe_device *xe) { return false; }
static inline void xe_i2c_irq_handler(struct xe_device *xe, u32 master_ctl) { }
+static inline void xe_i2c_irq_postinstall(struct xe_device *xe) { }
+static inline void xe_i2c_irq_reset(struct xe_device *xe) { }
static inline void xe_i2c_pm_suspend(struct xe_device *xe) { }
static inline void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold) { }
#endif
diff --git a/drivers/gpu/drm/xe/xe_irq.c b/drivers/gpu/drm/xe/xe_irq.c
index 870edaf69388..024e13e606ec 100644
--- a/drivers/gpu/drm/xe/xe_irq.c
+++ b/drivers/gpu/drm/xe/xe_irq.c
@@ -139,68 +139,112 @@ void xe_irq_enable_hwe(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
struct xe_mmio *mmio = &gt->mmio;
- u32 ccs_mask, bcs_mask;
- u32 irqs, dmask, smask;
- u32 gsc_mask = 0;
- u32 heci_mask = 0;
+ u32 common_mask, val, gsc_mask = 0, heci_mask = 0,
+ rcs_mask = 0, bcs_mask = 0, vcs_mask = 0, vecs_mask = 0,
+ ccs_mask = 0;
if (xe_device_uses_memirq(xe))
return;
if (xe_device_uc_enabled(xe)) {
- irqs = GT_RENDER_USER_INTERRUPT |
- GT_RENDER_PIPECTL_NOTIFY_INTERRUPT;
+ common_mask = GT_MI_USER_INTERRUPT |
+ GT_FLUSH_COMPLETE_INTERRUPT;
+
+ /* Enable Compute Walker Interrupt for non-MSIX platforms */
+ if (GRAPHICS_VERx100(xe) >= 3511 && !xe_device_has_msix(xe)) {
+ rcs_mask |= GT_COMPUTE_WALKER_INTERRUPT;
+ ccs_mask |= GT_COMPUTE_WALKER_INTERRUPT;
+ }
} else {
- irqs = GT_RENDER_USER_INTERRUPT |
- GT_CS_MASTER_ERROR_INTERRUPT |
- GT_CONTEXT_SWITCH_INTERRUPT |
- GT_WAIT_SEMAPHORE_INTERRUPT;
+ common_mask = GT_MI_USER_INTERRUPT |
+ GT_CS_MASTER_ERROR_INTERRUPT |
+ GT_CONTEXT_SWITCH_INTERRUPT |
+ GT_WAIT_SEMAPHORE_INTERRUPT;
}
- ccs_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COMPUTE);
- bcs_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COPY);
-
- dmask = irqs << 16 | irqs;
- smask = irqs << 16;
+ rcs_mask |= common_mask;
+ bcs_mask |= common_mask;
+ vcs_mask |= common_mask;
+ vecs_mask |= common_mask;
+ ccs_mask |= common_mask;
if (xe_gt_is_main_type(gt)) {
+ /*
+ * For enabling the interrupts, the information about fused off
+ * engines doesn't matter much, but this also allows to check if
+ * the engine is available architecturally in the platform
+ */
+ u32 ccs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COMPUTE);
+ u32 bcs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_COPY);
+
/* Enable interrupts for each engine class */
- xe_mmio_write32(mmio, RENDER_COPY_INTR_ENABLE, dmask);
- if (ccs_mask)
- xe_mmio_write32(mmio, CCS_RSVD_INTR_ENABLE, smask);
+ xe_mmio_write32(mmio, RENDER_COPY_INTR_ENABLE,
+ REG_FIELD_PREP(ENGINE1_MASK, rcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, bcs_mask));
+ if (ccs_fuse_mask)
+ xe_mmio_write32(mmio, CCS_RSVD_INTR_ENABLE,
+ REG_FIELD_PREP(ENGINE1_MASK, ccs_mask));
/* Unmask interrupts for each engine instance */
- xe_mmio_write32(mmio, RCS0_RSVD_INTR_MASK, ~smask);
- xe_mmio_write32(mmio, BCS_RSVD_INTR_MASK, ~smask);
- if (bcs_mask & (BIT(1)|BIT(2)))
- xe_mmio_write32(mmio, XEHPC_BCS1_BCS2_INTR_MASK, ~dmask);
- if (bcs_mask & (BIT(3)|BIT(4)))
- xe_mmio_write32(mmio, XEHPC_BCS3_BCS4_INTR_MASK, ~dmask);
- if (bcs_mask & (BIT(5)|BIT(6)))
- xe_mmio_write32(mmio, XEHPC_BCS5_BCS6_INTR_MASK, ~dmask);
- if (bcs_mask & (BIT(7)|BIT(8)))
- xe_mmio_write32(mmio, XEHPC_BCS7_BCS8_INTR_MASK, ~dmask);
- if (ccs_mask & (BIT(0)|BIT(1)))
- xe_mmio_write32(mmio, CCS0_CCS1_INTR_MASK, ~dmask);
- if (ccs_mask & (BIT(2)|BIT(3)))
- xe_mmio_write32(mmio, CCS2_CCS3_INTR_MASK, ~dmask);
+ val = ~REG_FIELD_PREP(ENGINE1_MASK, rcs_mask);
+ xe_mmio_write32(mmio, RCS0_RSVD_INTR_MASK, val);
+ val = ~REG_FIELD_PREP(ENGINE1_MASK, bcs_mask);
+ xe_mmio_write32(mmio, BCS_RSVD_INTR_MASK, val);
+
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, bcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, bcs_mask));
+ if (bcs_fuse_mask & (BIT(1)|BIT(2)))
+ xe_mmio_write32(mmio, XEHPC_BCS1_BCS2_INTR_MASK, val);
+ if (bcs_fuse_mask & (BIT(3)|BIT(4)))
+ xe_mmio_write32(mmio, XEHPC_BCS3_BCS4_INTR_MASK, val);
+ if (bcs_fuse_mask & (BIT(5)|BIT(6)))
+ xe_mmio_write32(mmio, XEHPC_BCS5_BCS6_INTR_MASK, val);
+ if (bcs_fuse_mask & (BIT(7)|BIT(8)))
+ xe_mmio_write32(mmio, XEHPC_BCS7_BCS8_INTR_MASK, val);
+
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, ccs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, ccs_mask));
+ if (ccs_fuse_mask & (BIT(0)|BIT(1)))
+ xe_mmio_write32(mmio, CCS0_CCS1_INTR_MASK, val);
+ if (ccs_fuse_mask & (BIT(2)|BIT(3)))
+ xe_mmio_write32(mmio, CCS2_CCS3_INTR_MASK, val);
}
if (xe_gt_is_media_type(gt) || MEDIA_VER(xe) < 13) {
+ u32 vcs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_VIDEO_DECODE);
+ u32 vecs_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_VIDEO_ENHANCE);
+ u32 other_fuse_mask = xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_OTHER);
+
/* Enable interrupts for each engine class */
- xe_mmio_write32(mmio, VCS_VECS_INTR_ENABLE, dmask);
+ xe_mmio_write32(mmio, VCS_VECS_INTR_ENABLE,
+ REG_FIELD_PREP(ENGINE1_MASK, vcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, vecs_mask));
/* Unmask interrupts for each engine instance */
- xe_mmio_write32(mmio, VCS0_VCS1_INTR_MASK, ~dmask);
- xe_mmio_write32(mmio, VCS2_VCS3_INTR_MASK, ~dmask);
- xe_mmio_write32(mmio, VECS0_VECS1_INTR_MASK, ~dmask);
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, vcs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, vcs_mask));
+ if (vcs_fuse_mask & (BIT(0) | BIT(1)))
+ xe_mmio_write32(mmio, VCS0_VCS1_INTR_MASK, val);
+ if (vcs_fuse_mask & (BIT(2) | BIT(3)))
+ xe_mmio_write32(mmio, VCS2_VCS3_INTR_MASK, val);
+ if (vcs_fuse_mask & (BIT(4) | BIT(5)))
+ xe_mmio_write32(mmio, VCS4_VCS5_INTR_MASK, val);
+ if (vcs_fuse_mask & (BIT(6) | BIT(7)))
+ xe_mmio_write32(mmio, VCS6_VCS7_INTR_MASK, val);
+
+ val = ~(REG_FIELD_PREP(ENGINE1_MASK, vecs_mask) |
+ REG_FIELD_PREP(ENGINE0_MASK, vecs_mask));
+ if (vecs_fuse_mask & (BIT(0) | BIT(1)))
+ xe_mmio_write32(mmio, VECS0_VECS1_INTR_MASK, val);
+ if (vecs_fuse_mask & (BIT(2) | BIT(3)))
+ xe_mmio_write32(mmio, VECS2_VECS3_INTR_MASK, val);
/*
* the heci2 interrupt is enabled via the same register as the
* GSCCS interrupts, but it has its own mask register.
*/
- if (xe_hw_engine_mask_per_class(gt, XE_ENGINE_CLASS_OTHER)) {
- gsc_mask = irqs | GSC_ER_COMPLETE;
+ if (other_fuse_mask) {
+ gsc_mask = common_mask | GSC_ER_COMPLETE;
heci_mask = GSC_IRQ_INTF(1);
} else if (xe->info.has_heci_gscfi) {
gsc_mask = GSC_IRQ_INTF(1);
@@ -494,11 +538,15 @@ static irqreturn_t dg1_irq_handler(int irq, void *arg)
static void gt_irq_reset(struct xe_tile *tile)
{
struct xe_mmio *mmio = &tile->mmio;
-
- u32 ccs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
- XE_ENGINE_CLASS_COMPUTE);
- u32 bcs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
- XE_ENGINE_CLASS_COPY);
+ u32 ccs_mask = ~0;
+ u32 bcs_mask = ~0;
+
+ if (tile->primary_gt) {
+ ccs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
+ XE_ENGINE_CLASS_COMPUTE);
+ bcs_mask = xe_hw_engine_mask_per_class(tile->primary_gt,
+ XE_ENGINE_CLASS_COPY);
+ }
/* Disable RCS, BCS, VCS and VECS class engines. */
xe_mmio_write32(mmio, RENDER_COPY_INTR_ENABLE, 0);
@@ -616,6 +664,7 @@ static void xe_irq_reset(struct xe_device *xe)
tile = xe_device_get_root_tile(xe);
mask_and_disable(tile, GU_MISC_IRQ_OFFSET);
xe_display_irq_reset(xe);
+ xe_i2c_irq_reset(xe);
/*
* The tile's top-level status register should be the last one
@@ -656,7 +705,8 @@ static void xe_irq_postinstall(struct xe_device *xe)
xe_memirq_postinstall(&tile->memirq);
}
- xe_display_irq_postinstall(xe, xe_root_mmio_gt(xe));
+ xe_display_irq_postinstall(xe);
+ xe_i2c_irq_postinstall(xe);
/*
* ASLE backlight operations are reported via GUnit GSE interrupts
@@ -847,22 +897,6 @@ static int xe_irq_msix_init(struct xe_device *xe)
return 0;
}
-static irqreturn_t guc2host_irq_handler(int irq, void *arg)
-{
- struct xe_device *xe = arg;
- struct xe_tile *tile;
- u8 id;
-
- if (!atomic_read(&xe->irq.enabled))
- return IRQ_NONE;
-
- for_each_tile(tile, xe, id)
- xe_guc_irq_handler(&tile->primary_gt->uc.guc,
- GUC_INTR_GUC2HOST);
-
- return IRQ_HANDLED;
-}
-
static irqreturn_t xe_irq_msix_default_hwe_handler(int irq, void *arg)
{
unsigned int tile_id, gt_id;
@@ -979,7 +1013,7 @@ int xe_irq_msix_request_irqs(struct xe_device *xe)
u16 msix;
msix = GUC2HOST_MSIX;
- err = xe_irq_msix_request_irq(xe, guc2host_irq_handler, xe,
+ err = xe_irq_msix_request_irq(xe, xe_irq_handler(xe), xe,
DRIVER_NAME "-guc2host", false, &msix);
if (err)
return err;
diff --git a/drivers/gpu/drm/xe/xe_lmtt.c b/drivers/gpu/drm/xe/xe_lmtt.c
index 62fc5a1a332d..4dc1de482eee 100644
--- a/drivers/gpu/drm/xe/xe_lmtt.c
+++ b/drivers/gpu/drm/xe/xe_lmtt.c
@@ -17,7 +17,7 @@
#include "xe_mmio.h"
#include "xe_res_cursor.h"
#include "xe_sriov.h"
-#include "xe_sriov_printk.h"
+#include "xe_tile_sriov_printk.h"
/**
* DOC: Local Memory Translation Table
@@ -32,7 +32,7 @@
*/
#define lmtt_assert(lmtt, condition) xe_tile_assert(lmtt_to_tile(lmtt), condition)
-#define lmtt_debug(lmtt, msg...) xe_sriov_dbg_verbose(lmtt_to_xe(lmtt), "LMTT: " msg)
+#define lmtt_debug(lmtt, msg...) xe_tile_sriov_dbg_verbose(lmtt_to_tile(lmtt), "LMTT: " msg)
static bool xe_has_multi_level_lmtt(struct xe_device *xe)
{
@@ -267,15 +267,14 @@ static int lmtt_invalidate_hw(struct xe_lmtt *lmtt)
*/
void xe_lmtt_invalidate_hw(struct xe_lmtt *lmtt)
{
- struct xe_device *xe = lmtt_to_xe(lmtt);
int err;
- lmtt_assert(lmtt, IS_SRIOV_PF(xe));
+ lmtt_assert(lmtt, IS_SRIOV_PF(lmtt_to_xe(lmtt)));
err = lmtt_invalidate_hw(lmtt);
if (err)
- xe_sriov_warn(xe, "LMTT%u invalidation failed (%pe)",
- lmtt_to_tile(lmtt)->id, ERR_PTR(err));
+ xe_tile_sriov_err(lmtt_to_tile(lmtt), "LMTT invalidation failed (%pe)",
+ ERR_PTR(err));
}
static void lmtt_write_pte(struct xe_lmtt *lmtt, struct xe_lmtt_pt *pt,
diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c
index 47e9df775072..b5083c99dd50 100644
--- a/drivers/gpu/drm/xe/xe_lrc.c
+++ b/drivers/gpu/drm/xe/xe_lrc.c
@@ -1214,8 +1214,7 @@ static int setup_bo(struct bo_setup_state *state)
ssize_t remain;
if (state->lrc->bo->vmap.is_iomem) {
- if (!state->buffer)
- return -ENOMEM;
+ xe_gt_assert(state->hwe->gt, state->buffer);
state->ptr = state->buffer;
} else {
state->ptr = state->lrc->bo->vmap.vaddr + state->offset;
@@ -1248,7 +1247,7 @@ fail:
static void finish_bo(struct bo_setup_state *state)
{
- if (!state->buffer)
+ if (!state->lrc->bo->vmap.is_iomem)
return;
xe_map_memcpy_to(gt_to_xe(state->lrc->gt), &state->lrc->bo->vmap,
@@ -1303,8 +1302,11 @@ static int setup_wa_bb(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
u32 *buf = NULL;
int ret;
- if (lrc->bo->vmap.is_iomem)
+ if (lrc->bo->vmap.is_iomem) {
buf = kmalloc(LRC_WA_BB_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ }
ret = xe_lrc_setup_wa_bb_with_scratch(lrc, hwe, buf);
@@ -1347,8 +1349,11 @@ setup_indirect_ctx(struct xe_lrc *lrc, struct xe_hw_engine *hwe)
if (xe_gt_WARN_ON(lrc->gt, !state.funcs))
return 0;
- if (lrc->bo->vmap.is_iomem)
+ if (lrc->bo->vmap.is_iomem) {
state.buffer = kmalloc(state.max_size, GFP_KERNEL);
+ if (!state.buffer)
+ return -ENOMEM;
+ }
ret = setup_bo(&state);
if (ret) {
@@ -1412,8 +1417,9 @@ static int xe_lrc_init(struct xe_lrc *lrc, struct xe_hw_engine *hwe,
bo_flags = XE_BO_FLAG_VRAM_IF_DGFX(tile) | XE_BO_FLAG_GGTT |
XE_BO_FLAG_GGTT_INVALIDATE;
- if (vm && vm->xef) /* userspace */
- bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE;
+
+ if ((vm && vm->xef) || init_flags & XE_LRC_CREATE_USER_CTX) /* userspace */
+ bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE | XE_BO_FLAG_FORCE_USER_VRAM;
lrc->bo = xe_bo_create_pin_map_novm(xe, tile,
bo_size,
diff --git a/drivers/gpu/drm/xe/xe_lrc.h b/drivers/gpu/drm/xe/xe_lrc.h
index 188565465779..2fb628da5c43 100644
--- a/drivers/gpu/drm/xe/xe_lrc.h
+++ b/drivers/gpu/drm/xe/xe_lrc.h
@@ -44,8 +44,10 @@ struct xe_lrc_snapshot {
#define LRC_WA_BB_SIZE SZ_4K
-#define XE_LRC_CREATE_RUNALONE 0x1
-#define XE_LRC_CREATE_PXP 0x2
+#define XE_LRC_CREATE_RUNALONE BIT(0)
+#define XE_LRC_CREATE_PXP BIT(1)
+#define XE_LRC_CREATE_USER_CTX BIT(2)
+
struct xe_lrc *xe_lrc_create(struct xe_hw_engine *hwe, struct xe_vm *vm,
u32 ring_size, u16 msix_vec, u32 flags);
void xe_lrc_destroy(struct kref *ref);
@@ -74,6 +76,16 @@ static inline void xe_lrc_put(struct xe_lrc *lrc)
kref_put(&lrc->refcount, xe_lrc_destroy);
}
+/**
+ * xe_lrc_ring_size() - Xe LRC ring size
+ *
+ * Return: Size of LRC ring buffer
+ */
+static inline size_t xe_lrc_ring_size(void)
+{
+ return SZ_16K;
+}
+
size_t xe_gt_lrc_size(struct xe_gt *gt, enum xe_engine_class class);
u32 xe_lrc_pphwsp_offset(struct xe_lrc *lrc);
u32 xe_lrc_regs_offset(struct xe_lrc *lrc);
diff --git a/drivers/gpu/drm/xe/xe_map.h b/drivers/gpu/drm/xe/xe_map.h
index 8d67f6ba2d95..c44777125691 100644
--- a/drivers/gpu/drm/xe/xe_map.h
+++ b/drivers/gpu/drm/xe/xe_map.h
@@ -14,9 +14,9 @@
* DOC: Map layer
*
* All access to any memory shared with a device (both sysmem and vram) in the
- * XE driver should go through this layer (xe_map). This layer is built on top
+ * Xe driver should go through this layer (xe_map). This layer is built on top
* of :ref:`driver-api/device-io:Generalizing Access to System and I/O Memory`
- * and with extra hooks into the XE driver that allows adding asserts to memory
+ * and with extra hooks into the Xe driver that allows adding asserts to memory
* accesses (e.g. for blocking runtime_pm D3Cold on Discrete Graphics).
*/
@@ -78,24 +78,6 @@ static inline void xe_map_write32(struct xe_device *xe, struct iosys_map *map,
iosys_map_wr(map__, offset__, type__, val__); \
})
-#define xe_map_rd_array(xe__, map__, index__, type__) \
- xe_map_rd(xe__, map__, (index__) * sizeof(type__), type__)
-
-#define xe_map_wr_array(xe__, map__, index__, type__, val__) \
- xe_map_wr(xe__, map__, (index__) * sizeof(type__), type__, val__)
-
-#define xe_map_rd_array_u32(xe__, map__, index__) \
- xe_map_rd_array(xe__, map__, index__, u32)
-
-#define xe_map_wr_array_u32(xe__, map__, index__, val__) \
- xe_map_wr_array(xe__, map__, index__, u32, val__)
-
-#define xe_map_rd_ring_u32(xe__, map__, index__, size__) \
- xe_map_rd_array_u32(xe__, map__, (index__) % (size__))
-
-#define xe_map_wr_ring_u32(xe__, map__, index__, size__, val__) \
- xe_map_wr_array_u32(xe__, map__, (index__) % (size__), val__)
-
#define xe_map_rd_field(xe__, map__, struct_offset__, struct_type__, field__) ({ \
struct xe_device *__xe = xe__; \
xe_device_assert_mem_access(__xe); \
diff --git a/drivers/gpu/drm/xe/xe_memirq.c b/drivers/gpu/drm/xe/xe_memirq.c
index 49c45ec3e83c..b0c7ce0a5d1e 100644
--- a/drivers/gpu/drm/xe/xe_memirq.c
+++ b/drivers/gpu/drm/xe/xe_memirq.c
@@ -14,16 +14,15 @@
#include "xe_device.h"
#include "xe_device_types.h"
#include "xe_gt.h"
-#include "xe_gt_printk.h"
#include "xe_guc.h"
#include "xe_hw_engine.h"
#include "xe_map.h"
#include "xe_memirq.h"
+#include "xe_tile_printk.h"
#define memirq_assert(m, condition) xe_tile_assert(memirq_to_tile(m), condition)
#define memirq_printk(m, _level, _fmt, ...) \
- drm_##_level(&memirq_to_xe(m)->drm, "MEMIRQ%u: " _fmt, \
- memirq_to_tile(m)->id, ##__VA_ARGS__)
+ xe_tile_##_level(memirq_to_tile(m), "MEMIRQ: " _fmt, ##__VA_ARGS__)
#ifdef CONFIG_DRM_XE_DEBUG_MEMIRQ
#define memirq_debug(m, _fmt, ...) memirq_printk(m, dbg, _fmt, ##__VA_ARGS__)
@@ -398,8 +397,9 @@ void xe_memirq_postinstall(struct xe_memirq *memirq)
memirq_set_enable(memirq, true);
}
-static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
- u16 offset, const char *name)
+static bool __memirq_received(struct xe_memirq *memirq,
+ struct iosys_map *vector, u16 offset,
+ const char *name, bool clear)
{
u8 value;
@@ -409,19 +409,33 @@ static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
memirq_err_ratelimited(memirq,
"Unexpected memirq value %#x from %s at %u\n",
value, name, offset);
- iosys_map_wr(vector, offset, u8, 0x00);
+ if (clear)
+ iosys_map_wr(vector, offset, u8, 0x00);
}
return value;
}
+static bool memirq_received_noclear(struct xe_memirq *memirq,
+ struct iosys_map *vector,
+ u16 offset, const char *name)
+{
+ return __memirq_received(memirq, vector, offset, name, false);
+}
+
+static bool memirq_received(struct xe_memirq *memirq, struct iosys_map *vector,
+ u16 offset, const char *name)
+{
+ return __memirq_received(memirq, vector, offset, name, true);
+}
+
static void memirq_dispatch_engine(struct xe_memirq *memirq, struct iosys_map *status,
struct xe_hw_engine *hwe)
{
memirq_debug(memirq, "STATUS %s %*ph\n", hwe->name, 16, status->vaddr);
- if (memirq_received(memirq, status, ilog2(GT_RENDER_USER_INTERRUPT), hwe->name))
- xe_hw_engine_handle_irq(hwe, GT_RENDER_USER_INTERRUPT);
+ if (memirq_received(memirq, status, ilog2(GT_MI_USER_INTERRUPT), hwe->name))
+ xe_hw_engine_handle_irq(hwe, GT_MI_USER_INTERRUPT);
}
static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *status,
@@ -434,8 +448,16 @@ static void memirq_dispatch_guc(struct xe_memirq *memirq, struct iosys_map *stat
if (memirq_received(memirq, status, ilog2(GUC_INTR_GUC2HOST), name))
xe_guc_irq_handler(guc, GUC_INTR_GUC2HOST);
- if (memirq_received(memirq, status, ilog2(GUC_INTR_SW_INT_0), name))
+ /*
+ * This is a software interrupt that must be cleared after it's consumed
+ * to avoid race conditions where xe_gt_sriov_vf_recovery_pending()
+ * returns false.
+ */
+ if (memirq_received_noclear(memirq, status, ilog2(GUC_INTR_SW_INT_0),
+ name)) {
xe_guc_irq_handler(guc, GUC_INTR_SW_INT_0);
+ iosys_map_wr(status, ilog2(GUC_INTR_SW_INT_0), u8, 0x00);
+ }
}
/**
@@ -461,6 +483,23 @@ void xe_memirq_hwe_handler(struct xe_memirq *memirq, struct xe_hw_engine *hwe)
}
/**
+ * xe_memirq_guc_sw_int_0_irq_pending() - SW_INT_0 IRQ is pending
+ * @memirq: the &xe_memirq
+ * @guc: the &xe_guc to check for IRQ
+ *
+ * Return: True if SW_INT_0 IRQ is pending on @guc, False otherwise
+ */
+bool xe_memirq_guc_sw_int_0_irq_pending(struct xe_memirq *memirq, struct xe_guc *guc)
+{
+ struct xe_gt *gt = guc_to_gt(guc);
+ u32 offset = xe_gt_is_media_type(gt) ? ilog2(INTR_MGUC) : ilog2(INTR_GUC);
+ struct iosys_map map = IOSYS_MAP_INIT_OFFSET(&memirq->status, offset * SZ_16);
+
+ return memirq_received_noclear(memirq, &map, ilog2(GUC_INTR_SW_INT_0),
+ guc_name(guc));
+}
+
+/**
* xe_memirq_handler - The `Memory Based Interrupts`_ Handler.
* @memirq: the &xe_memirq
*
diff --git a/drivers/gpu/drm/xe/xe_memirq.h b/drivers/gpu/drm/xe/xe_memirq.h
index 06130650e9d6..e25d2234ab87 100644
--- a/drivers/gpu/drm/xe/xe_memirq.h
+++ b/drivers/gpu/drm/xe/xe_memirq.h
@@ -25,4 +25,6 @@ void xe_memirq_handler(struct xe_memirq *memirq);
int xe_memirq_init_guc(struct xe_memirq *memirq, struct xe_guc *guc);
+bool xe_memirq_guc_sw_int_0_irq_pending(struct xe_memirq *memirq, struct xe_guc *guc);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_migrate.c b/drivers/gpu/drm/xe/xe_migrate.c
index a36ce7dce8cc..2184af413b91 100644
--- a/drivers/gpu/drm/xe/xe_migrate.c
+++ b/drivers/gpu/drm/xe/xe_migrate.c
@@ -29,6 +29,7 @@
#include "xe_lrc.h"
#include "xe_map.h"
#include "xe_mocs.h"
+#include "xe_printk.h"
#include "xe_pt.h"
#include "xe_res_cursor.h"
#include "xe_sa.h"
@@ -57,6 +58,13 @@ struct xe_migrate {
u64 usm_batch_base_ofs;
/** @cleared_mem_ofs: VM offset of @cleared_bo. */
u64 cleared_mem_ofs;
+ /** @large_page_copy_ofs: VM offset of 2M pages used for large copies */
+ u64 large_page_copy_ofs;
+ /**
+ * @large_page_copy_pdes: BO offset to writeout 2M pages (PDEs) used for
+ * large copies
+ */
+ u64 large_page_copy_pdes;
/**
* @fence: dma-fence representing the last migration job batch.
* Protected by @job_mutex.
@@ -288,6 +296,12 @@ static int xe_migrate_prepare_vm(struct xe_tile *tile, struct xe_migrate *m,
(i + 1) * 8, u64, entry);
}
+ /* Reserve 2M PDEs */
+ level = 1;
+ m->large_page_copy_ofs = NUM_PT_SLOTS << xe_pt_shift(level);
+ m->large_page_copy_pdes = map_ofs + XE_PAGE_SIZE * level +
+ NUM_PT_SLOTS * 8;
+
/* Set up a 1GiB NULL mapping at 255GiB offset. */
level = 2;
xe_map_wr(xe, &bo->vmap, map_ofs + XE_PAGE_SIZE * level + 255 * 8, u64,
@@ -686,9 +700,9 @@ static void emit_copy_ccs(struct xe_gt *gt, struct xe_bb *bb,
}
#define EMIT_COPY_DW 10
-static void emit_copy(struct xe_gt *gt, struct xe_bb *bb,
- u64 src_ofs, u64 dst_ofs, unsigned int size,
- unsigned int pitch)
+static void emit_xy_fast_copy(struct xe_gt *gt, struct xe_bb *bb, u64 src_ofs,
+ u64 dst_ofs, unsigned int size,
+ unsigned int pitch)
{
struct xe_device *xe = gt_to_xe(gt);
u32 mocs = 0;
@@ -717,6 +731,61 @@ static void emit_copy(struct xe_gt *gt, struct xe_bb *bb,
bb->cs[bb->len++] = upper_32_bits(src_ofs);
}
+#define PAGE_COPY_MODE_PS SZ_256 /* hw uses 256 bytes as the page-size */
+static void emit_mem_copy(struct xe_gt *gt, struct xe_bb *bb, u64 src_ofs,
+ u64 dst_ofs, unsigned int size, unsigned int pitch)
+{
+ u32 mode, copy_type, width;
+
+ xe_gt_assert(gt, IS_ALIGNED(size, pitch));
+ xe_gt_assert(gt, pitch <= U16_MAX);
+ xe_gt_assert(gt, pitch);
+ xe_gt_assert(gt, size);
+
+ if (IS_ALIGNED(size, PAGE_COPY_MODE_PS) &&
+ IS_ALIGNED(lower_32_bits(src_ofs), PAGE_COPY_MODE_PS) &&
+ IS_ALIGNED(lower_32_bits(dst_ofs), PAGE_COPY_MODE_PS)) {
+ mode = MEM_COPY_PAGE_COPY_MODE;
+ copy_type = 0; /* linear copy */
+ width = size / PAGE_COPY_MODE_PS;
+ } else if (pitch > 1) {
+ xe_gt_assert(gt, size / pitch <= U16_MAX);
+ mode = 0; /* BYTE_COPY */
+ copy_type = MEM_COPY_MATRIX_COPY;
+ width = pitch;
+ } else {
+ mode = 0; /* BYTE_COPY */
+ copy_type = 0; /* linear copy */
+ width = size;
+ }
+
+ xe_gt_assert(gt, width <= U16_MAX);
+
+ bb->cs[bb->len++] = MEM_COPY_CMD | mode | copy_type;
+ bb->cs[bb->len++] = width - 1;
+ bb->cs[bb->len++] = size / pitch - 1; /* ignored by hw for page-copy/linear above */
+ bb->cs[bb->len++] = pitch - 1;
+ bb->cs[bb->len++] = pitch - 1;
+ bb->cs[bb->len++] = lower_32_bits(src_ofs);
+ bb->cs[bb->len++] = upper_32_bits(src_ofs);
+ bb->cs[bb->len++] = lower_32_bits(dst_ofs);
+ bb->cs[bb->len++] = upper_32_bits(dst_ofs);
+ bb->cs[bb->len++] = FIELD_PREP(MEM_COPY_SRC_MOCS_INDEX_MASK, gt->mocs.uc_index) |
+ FIELD_PREP(MEM_COPY_DST_MOCS_INDEX_MASK, gt->mocs.uc_index);
+}
+
+static void emit_copy(struct xe_gt *gt, struct xe_bb *bb,
+ u64 src_ofs, u64 dst_ofs, unsigned int size,
+ unsigned int pitch)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+
+ if (xe->info.has_mem_copy_instr)
+ emit_mem_copy(gt, bb, src_ofs, dst_ofs, size, pitch);
+ else
+ emit_xy_fast_copy(gt, bb, src_ofs, dst_ofs, size, pitch);
+}
+
static u64 xe_migrate_batch_base(struct xe_migrate *m, bool usm)
{
return usm ? m->usm_batch_base_ofs : m->batch_base_ofs;
@@ -834,7 +903,7 @@ struct dma_fence *xe_migrate_copy(struct xe_migrate *m,
&ccs_it);
while (size) {
- u32 batch_size = 2; /* arb_clear() + MI_BATCH_BUFFER_END */
+ u32 batch_size = 1; /* MI_BATCH_BUFFER_END */
struct xe_sched_job *job;
struct xe_bb *bb;
u32 flush_flags = 0;
@@ -980,15 +1049,27 @@ struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate)
return migrate->q->lrc[0];
}
-static int emit_flush_invalidate(struct xe_exec_queue *q, u32 *dw, int i,
- u32 flags)
+static u64 migrate_vm_ppgtt_addr_tlb_inval(void)
+{
+ /*
+ * The migrate VM is self-referential so it can modify its own PTEs (see
+ * pte_update_size() or emit_pte() functions). We reserve NUM_KERNEL_PDE
+ * entries for kernel operations (copies, clears, CCS migrate), and
+ * suballocate the rest to user operations (binds/unbinds). With
+ * NUM_KERNEL_PDE = 15, NUM_KERNEL_PDE - 1 is already used for PTE updates,
+ * so assign NUM_KERNEL_PDE - 2 for TLB invalidation.
+ */
+ return (NUM_KERNEL_PDE - 2) * XE_PAGE_SIZE;
+}
+
+static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
{
- struct xe_lrc *lrc = xe_exec_queue_lrc(q);
+ u64 addr = migrate_vm_ppgtt_addr_tlb_inval();
+
dw[i++] = MI_FLUSH_DW | MI_INVALIDATE_TLB | MI_FLUSH_DW_OP_STOREDW |
MI_FLUSH_IMM_DW | flags;
- dw[i++] = lower_32_bits(xe_lrc_start_seqno_ggtt_addr(lrc)) |
- MI_FLUSH_DW_USE_GTT;
- dw[i++] = upper_32_bits(xe_lrc_start_seqno_ggtt_addr(lrc));
+ dw[i++] = lower_32_bits(addr);
+ dw[i++] = upper_32_bits(addr);
dw[i++] = MI_NOOP;
dw[i++] = MI_NOOP;
@@ -1101,11 +1182,11 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
emit_pte(m, bb, ccs_pt, false, false, &ccs_it, ccs_size, src);
- bb->len = emit_flush_invalidate(q, bb->cs, bb->len, flush_flags);
+ bb->len = emit_flush_invalidate(bb->cs, bb->len, flush_flags);
flush_flags = xe_migrate_ccs_copy(m, bb, src_L0_ofs, src_is_pltt,
src_L0_ofs, dst_is_pltt,
src_L0, ccs_ofs, true);
- bb->len = emit_flush_invalidate(q, bb->cs, bb->len, flush_flags);
+ bb->len = emit_flush_invalidate(bb->cs, bb->len, flush_flags);
size -= src_L0;
}
@@ -1130,6 +1211,128 @@ struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate)
return migrate->q;
}
+/**
+ * xe_migrate_vram_copy_chunk() - Copy a chunk of a VRAM buffer object.
+ * @vram_bo: The VRAM buffer object.
+ * @vram_offset: The VRAM offset.
+ * @sysmem_bo: The sysmem buffer object.
+ * @sysmem_offset: The sysmem offset.
+ * @size: The size of VRAM chunk to copy.
+ * @dir: The direction of the copy operation.
+ *
+ * Copies a portion of a buffer object between VRAM and system memory.
+ * On Xe2 platforms that support flat CCS, VRAM data is decompressed when
+ * copying to system memory.
+ *
+ * Return: Pointer to a dma_fence representing the last copy batch, or
+ * an error pointer on failure. If there is a failure, any copy operation
+ * started by the function call has been synced.
+ */
+struct dma_fence *xe_migrate_vram_copy_chunk(struct xe_bo *vram_bo, u64 vram_offset,
+ struct xe_bo *sysmem_bo, u64 sysmem_offset,
+ u64 size, enum xe_migrate_copy_dir dir)
+{
+ struct xe_device *xe = xe_bo_device(vram_bo);
+ struct xe_tile *tile = vram_bo->tile;
+ struct xe_gt *gt = tile->primary_gt;
+ struct xe_migrate *m = tile->migrate;
+ struct dma_fence *fence = NULL;
+ struct ttm_resource *vram = vram_bo->ttm.resource;
+ struct ttm_resource *sysmem = sysmem_bo->ttm.resource;
+ struct xe_res_cursor vram_it, sysmem_it;
+ u64 vram_L0_ofs, sysmem_L0_ofs;
+ u32 vram_L0_pt, sysmem_L0_pt;
+ u64 vram_L0, sysmem_L0;
+ bool to_sysmem = (dir == XE_MIGRATE_COPY_TO_SRAM);
+ bool use_comp_pat = to_sysmem &&
+ GRAPHICS_VER(xe) >= 20 && xe_device_has_flat_ccs(xe);
+ int pass = 0;
+ int err;
+
+ xe_assert(xe, IS_ALIGNED(vram_offset | sysmem_offset | size, PAGE_SIZE));
+ xe_assert(xe, xe_bo_is_vram(vram_bo));
+ xe_assert(xe, !xe_bo_is_vram(sysmem_bo));
+ xe_assert(xe, !range_overflows(vram_offset, size, (u64)vram_bo->ttm.base.size));
+ xe_assert(xe, !range_overflows(sysmem_offset, size, (u64)sysmem_bo->ttm.base.size));
+
+ xe_res_first(vram, vram_offset, size, &vram_it);
+ xe_res_first_sg(xe_bo_sg(sysmem_bo), sysmem_offset, size, &sysmem_it);
+
+ while (size) {
+ u32 pte_flags = PTE_UPDATE_FLAG_IS_VRAM;
+ u32 batch_size = 2; /* arb_clear() + MI_BATCH_BUFFER_END */
+ struct xe_sched_job *job;
+ struct xe_bb *bb;
+ u32 update_idx;
+ bool usm = xe->info.has_usm;
+ u32 avail_pts = max_mem_transfer_per_pass(xe) / LEVEL0_PAGE_TABLE_ENCODE_SIZE;
+
+ sysmem_L0 = xe_migrate_res_sizes(m, &sysmem_it);
+ vram_L0 = min(xe_migrate_res_sizes(m, &vram_it), sysmem_L0);
+
+ xe_dbg(xe, "Pass %u, size: %llu\n", pass++, vram_L0);
+
+ pte_flags |= use_comp_pat ? PTE_UPDATE_FLAG_IS_COMP_PTE : 0;
+ batch_size += pte_update_size(m, pte_flags, vram, &vram_it, &vram_L0,
+ &vram_L0_ofs, &vram_L0_pt, 0, 0, avail_pts);
+
+ batch_size += pte_update_size(m, 0, sysmem, &sysmem_it, &vram_L0, &sysmem_L0_ofs,
+ &sysmem_L0_pt, 0, avail_pts, avail_pts);
+ batch_size += EMIT_COPY_DW;
+
+ bb = xe_bb_new(gt, batch_size, usm);
+ if (IS_ERR(bb)) {
+ err = PTR_ERR(bb);
+ return ERR_PTR(err);
+ }
+
+ if (xe_migrate_allow_identity(vram_L0, &vram_it))
+ xe_res_next(&vram_it, vram_L0);
+ else
+ emit_pte(m, bb, vram_L0_pt, true, use_comp_pat, &vram_it, vram_L0, vram);
+
+ emit_pte(m, bb, sysmem_L0_pt, false, false, &sysmem_it, vram_L0, sysmem);
+
+ bb->cs[bb->len++] = MI_BATCH_BUFFER_END;
+ update_idx = bb->len;
+
+ if (to_sysmem)
+ emit_copy(gt, bb, vram_L0_ofs, sysmem_L0_ofs, vram_L0, XE_PAGE_SIZE);
+ else
+ emit_copy(gt, bb, sysmem_L0_ofs, vram_L0_ofs, vram_L0, XE_PAGE_SIZE);
+
+ job = xe_bb_create_migration_job(m->q, bb, xe_migrate_batch_base(m, usm),
+ update_idx);
+ if (IS_ERR(job)) {
+ xe_bb_free(bb, NULL);
+ err = PTR_ERR(job);
+ return ERR_PTR(err);
+ }
+
+ xe_sched_job_add_migrate_flush(job, MI_INVALIDATE_TLB);
+
+ xe_assert(xe, dma_resv_test_signaled(vram_bo->ttm.base.resv,
+ DMA_RESV_USAGE_BOOKKEEP));
+ xe_assert(xe, dma_resv_test_signaled(sysmem_bo->ttm.base.resv,
+ DMA_RESV_USAGE_BOOKKEEP));
+
+ scoped_guard(mutex, &m->job_mutex) {
+ xe_sched_job_arm(job);
+ dma_fence_put(fence);
+ fence = dma_fence_get(&job->drm.s_fence->finished);
+ xe_sched_job_push(job);
+
+ dma_fence_put(m->fence);
+ m->fence = dma_fence_get(fence);
+ }
+
+ xe_bb_free(bb, fence);
+ size -= vram_L0;
+ }
+
+ return fence;
+}
+
static void emit_clear_link_copy(struct xe_gt *gt, struct xe_bb *bb, u64 src_ofs,
u32 size, u32 pitch)
{
@@ -1287,7 +1490,7 @@ struct dma_fence *xe_migrate_clear(struct xe_migrate *m,
/* Calculate final sizes and batch size.. */
pte_flags = clear_vram ? PTE_UPDATE_FLAG_IS_VRAM : 0;
- batch_size = 2 +
+ batch_size = 1 +
pte_update_size(m, pte_flags, src, &src_it,
&clear_L0, &clear_L0_ofs, &clear_L0_pt,
clear_bo_data ? emit_clear_cmd_len(gt) : 0, 0,
@@ -1766,16 +1969,22 @@ static u32 pte_update_cmd_size(u64 size)
static void build_pt_update_batch_sram(struct xe_migrate *m,
struct xe_bb *bb, u32 pt_offset,
struct drm_pagemap_addr *sram_addr,
- u32 size)
+ u32 size, int level)
{
u16 pat_index = tile_to_xe(m->tile)->pat.idx[XE_CACHE_WB];
+ u64 gpu_page_size = 0x1ull << xe_pt_shift(level);
u32 ptes;
int i = 0;
- ptes = DIV_ROUND_UP(size, XE_PAGE_SIZE);
+ xe_tile_assert(m->tile, PAGE_ALIGNED(size));
+
+ ptes = DIV_ROUND_UP(size, gpu_page_size);
while (ptes) {
u32 chunk = min(MAX_PTE_PER_SDI, ptes);
+ if (!level)
+ chunk = ALIGN_DOWN(chunk, PAGE_SIZE / XE_PAGE_SIZE);
+
bb->cs[bb->len++] = MI_STORE_DATA_IMM | MI_SDI_NUM_QW(chunk);
bb->cs[bb->len++] = pt_offset;
bb->cs[bb->len++] = 0;
@@ -1784,30 +1993,70 @@ static void build_pt_update_batch_sram(struct xe_migrate *m,
ptes -= chunk;
while (chunk--) {
- u64 addr = sram_addr[i].addr & PAGE_MASK;
+ u64 addr = sram_addr[i].addr;
+ u64 pte;
xe_tile_assert(m->tile, sram_addr[i].proto ==
DRM_INTERCONNECT_SYSTEM);
xe_tile_assert(m->tile, addr);
- addr = m->q->vm->pt_ops->pte_encode_addr(m->tile->xe,
- addr, pat_index,
- 0, false, 0);
- bb->cs[bb->len++] = lower_32_bits(addr);
- bb->cs[bb->len++] = upper_32_bits(addr);
-
- i++;
+ xe_tile_assert(m->tile, PAGE_ALIGNED(addr));
+
+again:
+ pte = m->q->vm->pt_ops->pte_encode_addr(m->tile->xe,
+ addr, pat_index,
+ level, false, 0);
+ bb->cs[bb->len++] = lower_32_bits(pte);
+ bb->cs[bb->len++] = upper_32_bits(pte);
+
+ if (gpu_page_size < PAGE_SIZE) {
+ addr += XE_PAGE_SIZE;
+ if (!PAGE_ALIGNED(addr)) {
+ chunk--;
+ goto again;
+ }
+ i++;
+ } else {
+ i += gpu_page_size / PAGE_SIZE;
+ }
}
}
}
-enum xe_migrate_copy_dir {
- XE_MIGRATE_COPY_TO_VRAM,
- XE_MIGRATE_COPY_TO_SRAM,
-};
+static bool xe_migrate_vram_use_pde(struct drm_pagemap_addr *sram_addr,
+ unsigned long size)
+{
+ u32 large_size = (0x1 << xe_pt_shift(1));
+ unsigned long i, incr = large_size / PAGE_SIZE;
+
+ for (i = 0; i < DIV_ROUND_UP(size, PAGE_SIZE); i += incr)
+ if (PAGE_SIZE << sram_addr[i].order != large_size)
+ return false;
+
+ return true;
+}
#define XE_CACHELINE_BYTES 64ull
#define XE_CACHELINE_MASK (XE_CACHELINE_BYTES - 1)
+static u32 xe_migrate_copy_pitch(struct xe_device *xe, u32 len)
+{
+ u32 pitch;
+
+ if (IS_ALIGNED(len, PAGE_SIZE))
+ pitch = PAGE_SIZE;
+ else if (IS_ALIGNED(len, SZ_4K))
+ pitch = SZ_4K;
+ else if (IS_ALIGNED(len, SZ_256))
+ pitch = SZ_256;
+ else if (IS_ALIGNED(len, 4))
+ pitch = 4;
+ else
+ pitch = 1;
+
+ xe_assert(xe, pitch > 1 || xe->info.has_mem_copy_instr);
+ return pitch;
+}
+
static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
unsigned long len,
unsigned long sram_offset,
@@ -1819,24 +2068,25 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
struct xe_device *xe = gt_to_xe(gt);
bool use_usm_batch = xe->info.has_usm;
struct dma_fence *fence = NULL;
- u32 batch_size = 2;
+ u32 batch_size = 1;
u64 src_L0_ofs, dst_L0_ofs;
struct xe_sched_job *job;
struct xe_bb *bb;
u32 update_idx, pt_slot = 0;
unsigned long npages = DIV_ROUND_UP(len + sram_offset, PAGE_SIZE);
- unsigned int pitch = len >= PAGE_SIZE && !(len & ~PAGE_MASK) ?
- PAGE_SIZE : 4;
+ unsigned int pitch = xe_migrate_copy_pitch(xe, len);
int err;
unsigned long i, j;
+ bool use_pde = xe_migrate_vram_use_pde(sram_addr, len + sram_offset);
- if (drm_WARN_ON(&xe->drm, (len & XE_CACHELINE_MASK) ||
- (sram_offset | vram_addr) & XE_CACHELINE_MASK))
+ if (!xe->info.has_mem_copy_instr &&
+ drm_WARN_ON(&xe->drm,
+ (!IS_ALIGNED(len, pitch)) || (sram_offset | vram_addr) & XE_CACHELINE_MASK))
return ERR_PTR(-EOPNOTSUPP);
xe_assert(xe, npages * PAGE_SIZE <= MAX_PREEMPTDISABLE_TRANSFER);
- batch_size += pte_update_cmd_size(len);
+ batch_size += pte_update_cmd_size(npages << PAGE_SHIFT);
batch_size += EMIT_COPY_DW;
bb = xe_bb_new(gt, batch_size, use_usm_batch);
@@ -1853,7 +2103,7 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
* struct drm_pagemap_addr. Ensure this is the case even with higher
* orders.
*/
- for (i = 0; i < npages;) {
+ for (i = 0; !use_pde && i < npages;) {
unsigned int order = sram_addr[i].order;
for (j = 1; j < NR_PAGES(order) && i + j < npages; j++)
@@ -1863,16 +2113,26 @@ static struct dma_fence *xe_migrate_vram(struct xe_migrate *m,
i += NR_PAGES(order);
}
- build_pt_update_batch_sram(m, bb, pt_slot * XE_PAGE_SIZE,
- sram_addr, len + sram_offset);
+ if (use_pde)
+ build_pt_update_batch_sram(m, bb, m->large_page_copy_pdes,
+ sram_addr, npages << PAGE_SHIFT, 1);
+ else
+ build_pt_update_batch_sram(m, bb, pt_slot * XE_PAGE_SIZE,
+ sram_addr, npages << PAGE_SHIFT, 0);
if (dir == XE_MIGRATE_COPY_TO_VRAM) {
- src_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
+ if (use_pde)
+ src_L0_ofs = m->large_page_copy_ofs + sram_offset;
+ else
+ src_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
dst_L0_ofs = xe_migrate_vram_ofs(xe, vram_addr, false);
} else {
src_L0_ofs = xe_migrate_vram_ofs(xe, vram_addr, false);
- dst_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
+ if (use_pde)
+ dst_L0_ofs = m->large_page_copy_ofs + sram_offset;
+ else
+ dst_L0_ofs = xe_migrate_vm_addr(pt_slot, 0) + sram_offset;
}
bb->cs[bb->len++] = MI_BATCH_BUFFER_END;
@@ -1918,7 +2178,7 @@ err:
*
* Copy from an array dma addresses to a VRAM device physical address
*
- * Return: dma fence for migrate to signal completion on succees, ERR_PTR on
+ * Return: dma fence for migrate to signal completion on success, ERR_PTR on
* failure
*/
struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m,
@@ -1939,7 +2199,7 @@ struct dma_fence *xe_migrate_to_vram(struct xe_migrate *m,
*
* Copy from a VRAM device physical address to an array dma addresses
*
- * Return: dma fence for migrate to signal completion on succees, ERR_PTR on
+ * Return: dma fence for migrate to signal completion on success, ERR_PTR on
* failure
*/
struct dma_fence *xe_migrate_from_vram(struct xe_migrate *m,
@@ -2040,8 +2300,10 @@ int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo,
xe_bo_assert_held(bo);
/* Use bounce buffer for small access and unaligned access */
- if (!IS_ALIGNED(len, XE_CACHELINE_BYTES) ||
- !IS_ALIGNED((unsigned long)buf + offset, XE_CACHELINE_BYTES)) {
+ if (!xe->info.has_mem_copy_instr &&
+ (!IS_ALIGNED(len, 4) ||
+ !IS_ALIGNED(page_offset, XE_CACHELINE_BYTES) ||
+ !IS_ALIGNED(offset, XE_CACHELINE_BYTES))) {
int buf_offset = 0;
void *bounce;
int err;
@@ -2103,6 +2365,7 @@ int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo,
u64 vram_addr = vram_region_gpu_offset(bo->ttm.resource) +
cursor.start;
int current_bytes;
+ u32 pitch;
if (cursor.size > MAX_PREEMPTDISABLE_TRANSFER)
current_bytes = min_t(int, bytes_left,
@@ -2110,13 +2373,13 @@ int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo,
else
current_bytes = min_t(int, bytes_left, cursor.size);
- if (current_bytes & ~PAGE_MASK) {
- int pitch = 4;
-
+ pitch = xe_migrate_copy_pitch(xe, current_bytes);
+ if (xe->info.has_mem_copy_instr)
+ current_bytes = min_t(int, current_bytes, U16_MAX * pitch);
+ else
current_bytes = min_t(int, current_bytes,
round_down(S16_MAX * pitch,
XE_CACHELINE_BYTES));
- }
__fence = xe_migrate_vram(m, current_bytes,
(unsigned long)buf & ~PAGE_MASK,
@@ -2188,6 +2451,20 @@ void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q)
xe_vm_assert_held(q->vm); /* User queues VM's should be locked */
}
+#if IS_ENABLED(CONFIG_PROVE_LOCKING)
+/**
+ * xe_migrate_job_lock_assert() - Assert migrate job lock held of queue
+ * @q: Migrate queue
+ */
+void xe_migrate_job_lock_assert(struct xe_exec_queue *q)
+{
+ struct xe_migrate *m = gt_to_tile(q->gt)->migrate;
+
+ xe_gt_assert(q->gt, q == m->q);
+ lockdep_assert_held(&m->job_mutex);
+}
+#endif
+
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_migrate.c"
#endif
diff --git a/drivers/gpu/drm/xe/xe_migrate.h b/drivers/gpu/drm/xe/xe_migrate.h
index 4fad324b6253..260e298e5dd7 100644
--- a/drivers/gpu/drm/xe/xe_migrate.h
+++ b/drivers/gpu/drm/xe/xe_migrate.h
@@ -28,6 +28,11 @@ struct xe_vma;
enum xe_sriov_vf_ccs_rw_ctxs;
+enum xe_migrate_copy_dir {
+ XE_MIGRATE_COPY_TO_VRAM,
+ XE_MIGRATE_COPY_TO_SRAM,
+};
+
/**
* struct xe_migrate_pt_update_ops - Callbacks for the
* xe_migrate_update_pgtables() function.
@@ -131,6 +136,9 @@ int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_lrc *xe_migrate_lrc(struct xe_migrate *migrate);
struct xe_exec_queue *xe_migrate_exec_queue(struct xe_migrate *migrate);
+struct dma_fence *xe_migrate_vram_copy_chunk(struct xe_bo *vram_bo, u64 vram_offset,
+ struct xe_bo *sysmem_bo, u64 sysmem_offset,
+ u64 size, enum xe_migrate_copy_dir dir);
int xe_migrate_access_memory(struct xe_migrate *m, struct xe_bo *bo,
unsigned long offset, void *buf, int len,
int write);
@@ -152,6 +160,14 @@ xe_migrate_update_pgtables(struct xe_migrate *m,
void xe_migrate_wait(struct xe_migrate *m);
+#if IS_ENABLED(CONFIG_PROVE_LOCKING)
+void xe_migrate_job_lock_assert(struct xe_exec_queue *q);
+#else
+static inline void xe_migrate_job_lock_assert(struct xe_exec_queue *q)
+{
+}
+#endif
+
void xe_migrate_job_lock(struct xe_migrate *m, struct xe_exec_queue *q);
void xe_migrate_job_unlock(struct xe_migrate *m, struct xe_exec_queue *q);
diff --git a/drivers/gpu/drm/xe/xe_migrate_doc.h b/drivers/gpu/drm/xe/xe_migrate_doc.h
index 63c7d67b5b62..c082bc0b7068 100644
--- a/drivers/gpu/drm/xe/xe_migrate_doc.h
+++ b/drivers/gpu/drm/xe/xe_migrate_doc.h
@@ -9,7 +9,7 @@
/**
* DOC: Migrate Layer
*
- * The XE migrate layer is used generate jobs which can copy memory (eviction),
+ * The Xe migrate layer is used generate jobs which can copy memory (eviction),
* clear memory, or program tables (binds). This layer exists in every GT, has
* a migrate engine, and uses a special VM for all generated jobs.
*
diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c
index ef6f3ea573a2..350dca1f0925 100644
--- a/drivers/gpu/drm/xe/xe_mmio.c
+++ b/drivers/gpu/drm/xe/xe_mmio.c
@@ -379,3 +379,32 @@ int xe_mmio_wait32_not(struct xe_mmio *mmio, struct xe_reg reg, u32 mask, u32 va
{
return __xe_mmio_wait32(mmio, reg, mask, val, timeout_us, out_val, atomic, false);
}
+
+#ifdef CONFIG_PCI_IOV
+static size_t vf_regs_stride(struct xe_device *xe)
+{
+ return GRAPHICS_VERx100(xe) > 1200 ? 0x400 : 0x1000;
+}
+
+/**
+ * xe_mmio_init_vf_view() - Initialize an MMIO instance for accesses like the VF
+ * @mmio: the target &xe_mmio to initialize as VF's view
+ * @base: the source &xe_mmio to initialize from
+ * @vfid: the VF identifier
+ */
+void xe_mmio_init_vf_view(struct xe_mmio *mmio, const struct xe_mmio *base, unsigned int vfid)
+{
+ struct xe_tile *tile = base->tile;
+ struct xe_device *xe = tile->xe;
+ size_t offset = vf_regs_stride(xe) * vfid;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, vfid);
+ xe_assert(xe, !base->sriov_vf_gt);
+ xe_assert(xe, base->regs_size > offset);
+
+ *mmio = *base;
+ mmio->regs += offset;
+ mmio->regs_size -= offset;
+}
+#endif
diff --git a/drivers/gpu/drm/xe/xe_mmio.h b/drivers/gpu/drm/xe/xe_mmio.h
index c151ba569003..15362789ab99 100644
--- a/drivers/gpu/drm/xe/xe_mmio.h
+++ b/drivers/gpu/drm/xe/xe_mmio.h
@@ -42,4 +42,8 @@ static inline struct xe_mmio *xe_root_tile_mmio(struct xe_device *xe)
return &xe->tiles[0].mmio;
}
+#ifdef CONFIG_PCI_IOV
+void xe_mmio_init_vf_view(struct xe_mmio *mmio, const struct xe_mmio *base, unsigned int vfid);
+#endif
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_mocs.c b/drivers/gpu/drm/xe/xe_mocs.c
index 0c737413fcb6..6613d3b48a84 100644
--- a/drivers/gpu/drm/xe/xe_mocs.c
+++ b/drivers/gpu/drm/xe/xe_mocs.c
@@ -568,6 +568,23 @@ static const struct xe_mocs_ops xe2_mocs_ops = {
.dump = xe2_mocs_dump,
};
+/*
+ * Note that the "L3" and "L4" register fields actually control the L2 and L3
+ * caches respectively on this platform.
+ */
+static const struct xe_mocs_entry xe3p_xpc_mocs_table[] = {
+ /* Defer to PAT */
+ MOCS_ENTRY(0, XE2_L3_0_WB | L4_3_UC, 0),
+ /* UC */
+ MOCS_ENTRY(1, IG_PAT | XE2_L3_3_UC | L4_3_UC, 0),
+ /* L2 */
+ MOCS_ENTRY(2, IG_PAT | XE2_L3_0_WB | L4_3_UC, 0),
+ /* L3 */
+ MOCS_ENTRY(3, IG_PAT | XE2_L3_3_UC | L4_0_WB, 0),
+ /* L2 + L3 */
+ MOCS_ENTRY(4, IG_PAT | XE2_L3_0_WB | L4_0_WB, 0),
+};
+
static unsigned int get_mocs_settings(struct xe_device *xe,
struct xe_mocs_info *info)
{
@@ -576,6 +593,16 @@ static unsigned int get_mocs_settings(struct xe_device *xe,
memset(info, 0, sizeof(struct xe_mocs_info));
switch (xe->info.platform) {
+ case XE_CRESCENTISLAND:
+ info->ops = &xe2_mocs_ops;
+ info->table_size = ARRAY_SIZE(xe3p_xpc_mocs_table);
+ info->table = xe3p_xpc_mocs_table;
+ info->num_mocs_regs = XE2_NUM_MOCS_ENTRIES;
+ info->uc_index = 1;
+ info->wb_index = 4;
+ info->unused_entries_index = 4;
+ break;
+ case XE_NOVALAKE_S:
case XE_PANTHERLAKE:
case XE_LUNARLAKE:
case XE_BATTLEMAGE:
@@ -772,12 +799,20 @@ void xe_mocs_init(struct xe_gt *gt)
init_l3cc_table(gt, &table);
}
-void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_mocs_dump() - Dump MOCS table.
+ * @gt: the &xe_gt with MOCS table
+ * @p: the &drm_printer to dump info to
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
enum xe_force_wake_domains domain;
struct xe_mocs_info table;
unsigned int fw_ref, flags;
+ int err = 0;
flags = get_mocs_settings(xe, &table);
@@ -785,14 +820,17 @@ void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
xe_pm_runtime_get_noresume(xe);
fw_ref = xe_force_wake_get(gt_to_fw(gt), domain);
- if (!xe_force_wake_ref_has_domain(fw_ref, domain))
+ if (!xe_force_wake_ref_has_domain(fw_ref, domain)) {
+ err = -ETIMEDOUT;
goto err_fw;
+ }
table.ops->dump(&table, flags, gt, p);
err_fw:
xe_force_wake_put(gt_to_fw(gt), fw_ref);
xe_pm_runtime_put(xe);
+ return err;
}
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
diff --git a/drivers/gpu/drm/xe/xe_mocs.h b/drivers/gpu/drm/xe/xe_mocs.h
index dc972ffd4d07..f00bbb269829 100644
--- a/drivers/gpu/drm/xe/xe_mocs.h
+++ b/drivers/gpu/drm/xe/xe_mocs.h
@@ -11,12 +11,6 @@ struct xe_gt;
void xe_mocs_init_early(struct xe_gt *gt);
void xe_mocs_init(struct xe_gt *gt);
-
-/**
- * xe_mocs_dump - Dump mocs table
- * @gt: GT structure
- * @p: Printer to dump info to
- */
-void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p);
#endif
diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c
index a4894eb0d7f3..890c363282ae 100644
--- a/drivers/gpu/drm/xe/xe_oa.c
+++ b/drivers/gpu/drm/xe/xe_oa.c
@@ -10,6 +10,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
+#include <drm/drm_syncobj.h>
#include <uapi/drm/xe_drm.h>
#include <generated/xe_wa_oob.h>
@@ -837,7 +838,8 @@ static void xe_oa_disable_metric_set(struct xe_oa_stream *stream)
xe_oa_configure_oa_context(stream, false);
/* Make sure we disable noa to save power. */
- xe_mmio_rmw32(mmio, RPM_CONFIG1, GT_NOA_ENABLE, 0);
+ if (GT_VER(stream->gt) < 35)
+ xe_mmio_rmw32(mmio, RPM_CONFIG1, GT_NOA_ENABLE, 0);
sqcnt1 = SQCNT1_PMON_ENABLE |
(HAS_OA_BPC_REPORTING(stream->oa->xe) ? SQCNT1_OABPC : 0);
@@ -868,7 +870,7 @@ static void xe_oa_stream_destroy(struct xe_oa_stream *stream)
xe_oa_free_oa_buffer(stream);
- xe_force_wake_put(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ xe_force_wake_put(gt_to_fw(gt), stream->fw_ref);
xe_pm_runtime_put(stream->oa->xe);
/* Wa_1509372804:pvc: Unset the override of GUCRC mode to enable rc6 */
@@ -1389,7 +1391,9 @@ static int xe_oa_user_extensions(struct xe_oa *oa, enum xe_oa_user_extn_from fro
return 0;
}
-static int xe_oa_parse_syncs(struct xe_oa *oa, struct xe_oa_open_param *param)
+static int xe_oa_parse_syncs(struct xe_oa *oa,
+ struct xe_oa_stream *stream,
+ struct xe_oa_open_param *param)
{
int ret, num_syncs, num_ufence = 0;
@@ -1409,7 +1413,9 @@ static int xe_oa_parse_syncs(struct xe_oa *oa, struct xe_oa_open_param *param)
for (num_syncs = 0; num_syncs < param->num_syncs; num_syncs++) {
ret = xe_sync_entry_parse(oa->xe, param->xef, &param->syncs[num_syncs],
- &param->syncs_user[num_syncs], 0);
+ &param->syncs_user[num_syncs],
+ stream->ufence_syncobj,
+ ++stream->ufence_timeline_value, 0);
if (ret)
goto err_syncs;
@@ -1539,7 +1545,7 @@ static long xe_oa_config_locked(struct xe_oa_stream *stream, u64 arg)
return -ENODEV;
param.xef = stream->xef;
- err = xe_oa_parse_syncs(stream->oa, &param);
+ err = xe_oa_parse_syncs(stream->oa, stream, &param);
if (err)
goto err_config_put;
@@ -1635,6 +1641,7 @@ static void xe_oa_destroy_locked(struct xe_oa_stream *stream)
if (stream->exec_q)
xe_exec_queue_put(stream->exec_q);
+ drm_syncobj_put(stream->ufence_syncobj);
kfree(stream);
}
@@ -1710,7 +1717,6 @@ static int xe_oa_stream_init(struct xe_oa_stream *stream,
struct xe_oa_open_param *param)
{
struct xe_gt *gt = param->hwe->gt;
- unsigned int fw_ref;
int ret;
stream->exec_q = param->exec_q;
@@ -1765,8 +1771,8 @@ static int xe_oa_stream_init(struct xe_oa_stream *stream,
/* Take runtime pm ref and forcewake to disable RC6 */
xe_pm_runtime_get(stream->oa->xe);
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
- if (!xe_force_wake_ref_has_domain(fw_ref, XE_FORCEWAKE_ALL)) {
+ stream->fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FORCEWAKE_ALL);
+ if (!xe_force_wake_ref_has_domain(stream->fw_ref, XE_FORCEWAKE_ALL)) {
ret = -ETIMEDOUT;
goto err_fw_put;
}
@@ -1811,7 +1817,7 @@ err_put_k_exec_q:
err_free_oa_buf:
xe_oa_free_oa_buffer(stream);
err_fw_put:
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ xe_force_wake_put(gt_to_fw(gt), stream->fw_ref);
xe_pm_runtime_put(stream->oa->xe);
if (stream->override_gucrc)
xe_gt_WARN_ON(gt, xe_guc_pc_unset_gucrc_mode(&gt->uc.guc.pc));
@@ -1826,6 +1832,7 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
struct xe_oa_open_param *param)
{
struct xe_oa_stream *stream;
+ struct drm_syncobj *ufence_syncobj;
int stream_fd;
int ret;
@@ -1836,17 +1843,31 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa,
goto exit;
}
+ ret = drm_syncobj_create(&ufence_syncobj, DRM_SYNCOBJ_CREATE_SIGNALED,
+ NULL);
+ if (ret)
+ goto exit;
+
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream) {
ret = -ENOMEM;
- goto exit;
+ goto err_syncobj;
}
-
+ stream->ufence_syncobj = ufence_syncobj;
stream->oa = oa;
- ret = xe_oa_stream_init(stream, param);
+
+ ret = xe_oa_parse_syncs(oa, stream, param);
if (ret)
goto err_free;
+ ret = xe_oa_stream_init(stream, param);
+ if (ret) {
+ while (param->num_syncs--)
+ xe_sync_entry_cleanup(&param->syncs[param->num_syncs]);
+ kfree(param->syncs);
+ goto err_free;
+ }
+
if (!param->disabled) {
ret = xe_oa_enable_locked(stream);
if (ret)
@@ -1870,6 +1891,8 @@ err_destroy:
xe_oa_stream_destroy(stream);
err_free:
kfree(stream);
+err_syncobj:
+ drm_syncobj_put(ufence_syncobj);
exit:
return ret;
}
@@ -2083,22 +2106,14 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f
goto err_exec_q;
}
- ret = xe_oa_parse_syncs(oa, &param);
- if (ret)
- goto err_exec_q;
-
mutex_lock(&param.hwe->gt->oa.gt_lock);
ret = xe_oa_stream_open_ioctl_locked(oa, &param);
mutex_unlock(&param.hwe->gt->oa.gt_lock);
if (ret < 0)
- goto err_sync_cleanup;
+ goto err_exec_q;
return ret;
-err_sync_cleanup:
- while (param.num_syncs--)
- xe_sync_entry_cleanup(&param.syncs[param.num_syncs]);
- kfree(param.syncs);
err_exec_q:
if (param.exec_q)
xe_exec_queue_put(param.exec_q);
@@ -2388,11 +2403,13 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi
goto sysfs_err;
}
- mutex_unlock(&oa->metrics_lock);
+ id = oa_config->id;
- drm_dbg(&oa->xe->drm, "Added config %s id=%i\n", oa_config->uuid, oa_config->id);
+ drm_dbg(&oa->xe->drm, "Added config %s id=%i\n", oa_config->uuid, id);
+
+ mutex_unlock(&oa->metrics_lock);
- return oa_config->id;
+ return id;
sysfs_err:
mutex_unlock(&oa->metrics_lock);
diff --git a/drivers/gpu/drm/xe/xe_oa_types.h b/drivers/gpu/drm/xe/xe_oa_types.h
index 2628f78c4e8d..cf080f412189 100644
--- a/drivers/gpu/drm/xe/xe_oa_types.h
+++ b/drivers/gpu/drm/xe/xe_oa_types.h
@@ -15,6 +15,8 @@
#include "regs/xe_reg_defs.h"
#include "xe_hw_engine_types.h"
+struct drm_syncobj;
+
#define DEFAULT_XE_OA_BUFFER_SIZE SZ_16M
enum xe_oa_report_header {
@@ -248,6 +250,12 @@ struct xe_oa_stream {
/** @xef: xe_file with which the stream was opened */
struct xe_file *xef;
+ /** @ufence_syncobj: User fence syncobj */
+ struct drm_syncobj *ufence_syncobj;
+
+ /** @ufence_timeline_value: User fence timeline value */
+ u64 ufence_timeline_value;
+
/** @last_fence: fence to use in stream destroy when needed */
struct dma_fence *last_fence;
@@ -256,5 +264,8 @@ struct xe_oa_stream {
/** @syncs: syncs to wait on and to signal */
struct xe_sync_entry *syncs;
+
+ /** @fw_ref: Forcewake reference */
+ unsigned int fw_ref;
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_pagefault.c b/drivers/gpu/drm/xe/xe_pagefault.c
new file mode 100644
index 000000000000..afb06598b6e1
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/circ_buf.h>
+
+#include <drm/drm_exec.h>
+#include <drm/drm_managed.h>
+
+#include "xe_bo.h"
+#include "xe_device.h"
+#include "xe_gt_printk.h"
+#include "xe_gt_types.h"
+#include "xe_gt_stats.h"
+#include "xe_hw_engine.h"
+#include "xe_pagefault.h"
+#include "xe_pagefault_types.h"
+#include "xe_svm.h"
+#include "xe_trace_bo.h"
+#include "xe_vm.h"
+
+/**
+ * DOC: Xe page faults
+ *
+ * Xe page faults are handled in two layers. The producer layer interacts with
+ * hardware or firmware to receive and parse faults into struct xe_pagefault,
+ * then forwards them to the consumer. The consumer layer services the faults
+ * (e.g., memory migration, page table updates) and acknowledges the result back
+ * to the producer, which then forwards the results to the hardware or firmware.
+ * The consumer uses a page fault queue sized to absorb all potential faults and
+ * a multi-threaded worker to process them. Multiple producers are supported,
+ * with a single shared consumer.
+ *
+ * xe_pagefault.c implements the consumer layer.
+ */
+
+static int xe_pagefault_entry_size(void)
+{
+ /*
+ * Power of two alignment is not a hardware requirement, rather a
+ * software restriction which makes the math for page fault queue
+ * management simplier.
+ */
+ return roundup_pow_of_two(sizeof(struct xe_pagefault));
+}
+
+static int xe_pagefault_begin(struct drm_exec *exec, struct xe_vma *vma,
+ struct xe_vram_region *vram, bool need_vram_move)
+{
+ struct xe_bo *bo = xe_vma_bo(vma);
+ struct xe_vm *vm = xe_vma_vm(vma);
+ int err;
+
+ err = xe_vm_lock_vma(exec, vma);
+ if (err)
+ return err;
+
+ if (!bo)
+ return 0;
+
+ return need_vram_move ? xe_bo_migrate(bo, vram->placement, NULL, exec) :
+ xe_bo_validate(bo, vm, true, exec);
+}
+
+static int xe_pagefault_handle_vma(struct xe_gt *gt, struct xe_vma *vma,
+ bool atomic)
+{
+ struct xe_vm *vm = xe_vma_vm(vma);
+ struct xe_tile *tile = gt_to_tile(gt);
+ struct xe_validation_ctx ctx;
+ struct drm_exec exec;
+ struct dma_fence *fence;
+ int err, needs_vram;
+
+ lockdep_assert_held_write(&vm->lock);
+
+ needs_vram = xe_vma_need_vram_for_atomic(vm->xe, vma, atomic);
+ if (needs_vram < 0 || (needs_vram && xe_vma_is_userptr(vma)))
+ return needs_vram < 0 ? needs_vram : -EACCES;
+
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_COUNT, 1);
+ xe_gt_stats_incr(gt, XE_GT_STATS_ID_VMA_PAGEFAULT_KB,
+ xe_vma_size(vma) / SZ_1K);
+
+ trace_xe_vma_pagefault(vma);
+
+ /* Check if VMA is valid, opportunistic check only */
+ if (xe_vm_has_valid_gpu_mapping(tile, vma->tile_present,
+ vma->tile_invalidated) && !atomic)
+ return 0;
+
+retry_userptr:
+ if (xe_vma_is_userptr(vma) &&
+ xe_vma_userptr_check_repin(to_userptr_vma(vma))) {
+ struct xe_userptr_vma *uvma = to_userptr_vma(vma);
+
+ err = xe_vma_userptr_pin_pages(uvma);
+ if (err)
+ return err;
+ }
+
+ /* Lock VM and BOs dma-resv */
+ xe_validation_ctx_init(&ctx, &vm->xe->val, &exec, (struct xe_val_flags) {});
+ drm_exec_until_all_locked(&exec) {
+ err = xe_pagefault_begin(&exec, vma, tile->mem.vram,
+ needs_vram == 1);
+ drm_exec_retry_on_contention(&exec);
+ xe_validation_retry_on_oom(&ctx, &err);
+ if (err)
+ goto unlock_dma_resv;
+
+ /* Bind VMA only to the GT that has faulted */
+ trace_xe_vma_pf_bind(vma);
+ xe_vm_set_validation_exec(vm, &exec);
+ fence = xe_vma_rebind(vm, vma, BIT(tile->id));
+ xe_vm_set_validation_exec(vm, NULL);
+ if (IS_ERR(fence)) {
+ err = PTR_ERR(fence);
+ xe_validation_retry_on_oom(&ctx, &err);
+ goto unlock_dma_resv;
+ }
+ }
+
+ dma_fence_wait(fence, false);
+ dma_fence_put(fence);
+
+unlock_dma_resv:
+ xe_validation_ctx_fini(&ctx);
+ if (err == -EAGAIN)
+ goto retry_userptr;
+
+ return err;
+}
+
+static bool
+xe_pagefault_access_is_atomic(enum xe_pagefault_access_type access_type)
+{
+ return access_type == XE_PAGEFAULT_ACCESS_TYPE_ATOMIC;
+}
+
+static struct xe_vm *xe_pagefault_asid_to_vm(struct xe_device *xe, u32 asid)
+{
+ struct xe_vm *vm;
+
+ down_read(&xe->usm.lock);
+ vm = xa_load(&xe->usm.asid_to_vm, asid);
+ if (vm && xe_vm_in_fault_mode(vm))
+ xe_vm_get(vm);
+ else
+ vm = ERR_PTR(-EINVAL);
+ up_read(&xe->usm.lock);
+
+ return vm;
+}
+
+static int xe_pagefault_service(struct xe_pagefault *pf)
+{
+ struct xe_gt *gt = pf->gt;
+ struct xe_device *xe = gt_to_xe(gt);
+ struct xe_vm *vm;
+ struct xe_vma *vma = NULL;
+ int err;
+ bool atomic;
+
+ /* Producer flagged this fault to be nacked */
+ if (pf->consumer.fault_level == XE_PAGEFAULT_LEVEL_NACK)
+ return -EFAULT;
+
+ vm = xe_pagefault_asid_to_vm(xe, pf->consumer.asid);
+ if (IS_ERR(vm))
+ return PTR_ERR(vm);
+
+ /*
+ * TODO: Change to read lock? Using write lock for simplicity.
+ */
+ down_write(&vm->lock);
+
+ if (xe_vm_is_closed(vm)) {
+ err = -ENOENT;
+ goto unlock_vm;
+ }
+
+ vma = xe_vm_find_vma_by_addr(vm, pf->consumer.page_addr);
+ if (!vma) {
+ err = -EINVAL;
+ goto unlock_vm;
+ }
+
+ atomic = xe_pagefault_access_is_atomic(pf->consumer.access_type);
+
+ if (xe_vma_is_cpu_addr_mirror(vma))
+ err = xe_svm_handle_pagefault(vm, vma, gt,
+ pf->consumer.page_addr, atomic);
+ else
+ err = xe_pagefault_handle_vma(gt, vma, atomic);
+
+unlock_vm:
+ if (!err)
+ vm->usm.last_fault_vma = vma;
+ up_write(&vm->lock);
+ xe_vm_put(vm);
+
+ return err;
+}
+
+static bool xe_pagefault_queue_pop(struct xe_pagefault_queue *pf_queue,
+ struct xe_pagefault *pf)
+{
+ bool found_fault = false;
+
+ spin_lock_irq(&pf_queue->lock);
+ if (pf_queue->tail != pf_queue->head) {
+ memcpy(pf, pf_queue->data + pf_queue->tail, sizeof(*pf));
+ pf_queue->tail = (pf_queue->tail + xe_pagefault_entry_size()) %
+ pf_queue->size;
+ found_fault = true;
+ }
+ spin_unlock_irq(&pf_queue->lock);
+
+ return found_fault;
+}
+
+static void xe_pagefault_print(struct xe_pagefault *pf)
+{
+ xe_gt_dbg(pf->gt, "\n\tASID: %d\n"
+ "\tFaulted Address: 0x%08x%08x\n"
+ "\tFaultType: %d\n"
+ "\tAccessType: %d\n"
+ "\tFaultLevel: %d\n"
+ "\tEngineClass: %d %s\n"
+ "\tEngineInstance: %d\n",
+ pf->consumer.asid,
+ upper_32_bits(pf->consumer.page_addr),
+ lower_32_bits(pf->consumer.page_addr),
+ pf->consumer.fault_type,
+ pf->consumer.access_type,
+ pf->consumer.fault_level,
+ pf->consumer.engine_class,
+ xe_hw_engine_class_to_str(pf->consumer.engine_class),
+ pf->consumer.engine_instance);
+}
+
+static void xe_pagefault_queue_work(struct work_struct *w)
+{
+ struct xe_pagefault_queue *pf_queue =
+ container_of(w, typeof(*pf_queue), worker);
+ struct xe_pagefault pf;
+ unsigned long threshold;
+
+#define USM_QUEUE_MAX_RUNTIME_MS 20
+ threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
+
+ while (xe_pagefault_queue_pop(pf_queue, &pf)) {
+ int err;
+
+ if (!pf.gt) /* Fault squashed during reset */
+ continue;
+
+ err = xe_pagefault_service(&pf);
+ if (err) {
+ xe_pagefault_print(&pf);
+ xe_gt_dbg(pf.gt, "Fault response: Unsuccessful %pe\n",
+ ERR_PTR(err));
+ }
+
+ pf.producer.ops->ack_fault(&pf, err);
+
+ if (time_after(jiffies, threshold)) {
+ queue_work(gt_to_xe(pf.gt)->usm.pf_wq, w);
+ break;
+ }
+ }
+#undef USM_QUEUE_MAX_RUNTIME_MS
+}
+
+static int xe_pagefault_queue_init(struct xe_device *xe,
+ struct xe_pagefault_queue *pf_queue)
+{
+ struct xe_gt *gt;
+ int total_num_eus = 0;
+ u8 id;
+
+ for_each_gt(gt, xe, id) {
+ xe_dss_mask_t all_dss;
+ int num_dss, num_eus;
+
+ bitmap_or(all_dss, gt->fuse_topo.g_dss_mask,
+ gt->fuse_topo.c_dss_mask, XE_MAX_DSS_FUSE_BITS);
+
+ num_dss = bitmap_weight(all_dss, XE_MAX_DSS_FUSE_BITS);
+ num_eus = bitmap_weight(gt->fuse_topo.eu_mask_per_dss,
+ XE_MAX_EU_FUSE_BITS) * num_dss;
+
+ total_num_eus += num_eus;
+ }
+
+ xe_assert(xe, total_num_eus);
+
+ /*
+ * user can issue separate page faults per EU and per CS
+ *
+ * XXX: Multiplier required as compute UMD are getting PF queue errors
+ * without it. Follow on why this multiplier is required.
+ */
+#define PF_MULTIPLIER 8
+ pf_queue->size = (total_num_eus + XE_NUM_HW_ENGINES) *
+ xe_pagefault_entry_size() * PF_MULTIPLIER;
+ pf_queue->size = roundup_pow_of_two(pf_queue->size);
+#undef PF_MULTIPLIER
+
+ drm_dbg(&xe->drm, "xe_pagefault_entry_size=%d, total_num_eus=%d, pf_queue->size=%u",
+ xe_pagefault_entry_size(), total_num_eus, pf_queue->size);
+
+ spin_lock_init(&pf_queue->lock);
+ INIT_WORK(&pf_queue->worker, xe_pagefault_queue_work);
+
+ pf_queue->data = drmm_kzalloc(&xe->drm, pf_queue->size, GFP_KERNEL);
+ if (!pf_queue->data)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void xe_pagefault_fini(void *arg)
+{
+ struct xe_device *xe = arg;
+
+ destroy_workqueue(xe->usm.pf_wq);
+}
+
+/**
+ * xe_pagefault_init() - Page fault init
+ * @xe: xe device instance
+ *
+ * Initialize Xe page fault state. Must be done after reading fuses.
+ *
+ * Return: 0 on Success, errno on failure
+ */
+int xe_pagefault_init(struct xe_device *xe)
+{
+ int err, i;
+
+ if (!xe->info.has_usm)
+ return 0;
+
+ xe->usm.pf_wq = alloc_workqueue("xe_page_fault_work_queue",
+ WQ_UNBOUND | WQ_HIGHPRI,
+ XE_PAGEFAULT_QUEUE_COUNT);
+ if (!xe->usm.pf_wq)
+ return -ENOMEM;
+
+ for (i = 0; i < XE_PAGEFAULT_QUEUE_COUNT; ++i) {
+ err = xe_pagefault_queue_init(xe, xe->usm.pf_queue + i);
+ if (err)
+ goto err_out;
+ }
+
+ return devm_add_action_or_reset(xe->drm.dev, xe_pagefault_fini, xe);
+
+err_out:
+ destroy_workqueue(xe->usm.pf_wq);
+ return err;
+}
+
+static void xe_pagefault_queue_reset(struct xe_device *xe, struct xe_gt *gt,
+ struct xe_pagefault_queue *pf_queue)
+{
+ u32 i;
+
+ /* Driver load failure guard / USM not enabled guard */
+ if (!pf_queue->data)
+ return;
+
+ /* Squash all pending faults on the GT */
+
+ spin_lock_irq(&pf_queue->lock);
+ for (i = pf_queue->tail; i != pf_queue->head;
+ i = (i + xe_pagefault_entry_size()) % pf_queue->size) {
+ struct xe_pagefault *pf = pf_queue->data + i;
+
+ if (pf->gt == gt)
+ pf->gt = NULL;
+ }
+ spin_unlock_irq(&pf_queue->lock);
+}
+
+/**
+ * xe_pagefault_reset() - Page fault reset for a GT
+ * @xe: xe device instance
+ * @gt: GT being reset
+ *
+ * Reset the Xe page fault state for a GT; that is, squash any pending faults on
+ * the GT.
+ */
+void xe_pagefault_reset(struct xe_device *xe, struct xe_gt *gt)
+{
+ int i;
+
+ for (i = 0; i < XE_PAGEFAULT_QUEUE_COUNT; ++i)
+ xe_pagefault_queue_reset(xe, gt, xe->usm.pf_queue + i);
+}
+
+static bool xe_pagefault_queue_full(struct xe_pagefault_queue *pf_queue)
+{
+ lockdep_assert_held(&pf_queue->lock);
+
+ return CIRC_SPACE(pf_queue->head, pf_queue->tail, pf_queue->size) <=
+ xe_pagefault_entry_size();
+}
+
+/**
+ * xe_pagefault_handler() - Page fault handler
+ * @xe: xe device instance
+ * @pf: Page fault
+ *
+ * Sink the page fault to a queue (i.e., a memory buffer) and queue a worker to
+ * service it. Safe to be called from IRQ or process context. Reclaim safe.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int xe_pagefault_handler(struct xe_device *xe, struct xe_pagefault *pf)
+{
+ struct xe_pagefault_queue *pf_queue = xe->usm.pf_queue +
+ (pf->consumer.asid % XE_PAGEFAULT_QUEUE_COUNT);
+ unsigned long flags;
+ bool full;
+
+ spin_lock_irqsave(&pf_queue->lock, flags);
+ full = xe_pagefault_queue_full(pf_queue);
+ if (!full) {
+ memcpy(pf_queue->data + pf_queue->head, pf, sizeof(*pf));
+ pf_queue->head = (pf_queue->head + xe_pagefault_entry_size()) %
+ pf_queue->size;
+ queue_work(xe->usm.pf_wq, &pf_queue->worker);
+ } else {
+ drm_warn(&xe->drm,
+ "PageFault Queue (%d) full, shouldn't be possible\n",
+ pf->consumer.asid % XE_PAGEFAULT_QUEUE_COUNT);
+ }
+ spin_unlock_irqrestore(&pf_queue->lock, flags);
+
+ return full ? -ENOSPC : 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_pagefault.h b/drivers/gpu/drm/xe/xe_pagefault.h
new file mode 100644
index 000000000000..bd0cdf9ed37f
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pagefault.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_PAGEFAULT_H_
+#define _XE_PAGEFAULT_H_
+
+struct xe_device;
+struct xe_gt;
+struct xe_pagefault;
+
+int xe_pagefault_init(struct xe_device *xe);
+
+void xe_pagefault_reset(struct xe_device *xe, struct xe_gt *gt);
+
+int xe_pagefault_handler(struct xe_device *xe, struct xe_pagefault *pf);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_pagefault_types.h b/drivers/gpu/drm/xe/xe_pagefault_types.h
new file mode 100644
index 000000000000..d3b516407d60
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pagefault_types.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_PAGEFAULT_TYPES_H_
+#define _XE_PAGEFAULT_TYPES_H_
+
+#include <linux/workqueue.h>
+
+struct xe_gt;
+struct xe_pagefault;
+
+/** enum xe_pagefault_access_type - Xe page fault access type */
+enum xe_pagefault_access_type {
+ /** @XE_PAGEFAULT_ACCESS_TYPE_READ: Read access type */
+ XE_PAGEFAULT_ACCESS_TYPE_READ = 0,
+ /** @XE_PAGEFAULT_ACCESS_TYPE_WRITE: Write access type */
+ XE_PAGEFAULT_ACCESS_TYPE_WRITE = 1,
+ /** @XE_PAGEFAULT_ACCESS_TYPE_ATOMIC: Atomic access type */
+ XE_PAGEFAULT_ACCESS_TYPE_ATOMIC = 2,
+};
+
+/** enum xe_pagefault_type - Xe page fault type */
+enum xe_pagefault_type {
+ /** @XE_PAGEFAULT_TYPE_NOT_PRESENT: Not present */
+ XE_PAGEFAULT_TYPE_NOT_PRESENT = 0,
+ /** @XE_PAGEFAULT_TYPE_WRITE_ACCESS_VIOLATION: Write access violation */
+ XE_PAGEFAULT_TYPE_WRITE_ACCESS_VIOLATION = 1,
+ /** @XE_PAGEFAULT_TYPE_ATOMIC_ACCESS_VIOLATION: Atomic access violation */
+ XE_PAGEFAULT_TYPE_ATOMIC_ACCESS_VIOLATION = 2,
+};
+
+/** struct xe_pagefault_ops - Xe pagefault ops (producer) */
+struct xe_pagefault_ops {
+ /**
+ * @ack_fault: Ack fault
+ * @pf: Page fault
+ * @err: Error state of fault
+ *
+ * Page fault producer receives acknowledgment from the consumer and
+ * sends the result to the HW/FW interface.
+ */
+ void (*ack_fault)(struct xe_pagefault *pf, int err);
+};
+
+/**
+ * struct xe_pagefault - Xe page fault
+ *
+ * Generic page fault structure for communication between producer and consumer.
+ * Carefully sized to be 64 bytes. Upon a device page fault, the producer
+ * populates this structure, and the consumer copies it into the page-fault
+ * queue for deferred handling.
+ */
+struct xe_pagefault {
+ /**
+ * @gt: GT of fault
+ */
+ struct xe_gt *gt;
+ /**
+ * @consumer: State for the software handling the fault. Populated by
+ * the producer and may be modified by the consumer to communicate
+ * information back to the producer upon fault acknowledgment.
+ */
+ struct {
+ /** @consumer.page_addr: address of page fault */
+ u64 page_addr;
+ /** @consumer.asid: address space ID */
+ u32 asid;
+ /**
+ * @consumer.access_type: access type, u8 rather than enum to
+ * keep size compact
+ */
+ u8 access_type;
+ /**
+ * @consumer.fault_type: fault type, u8 rather than enum to
+ * keep size compact
+ */
+ u8 fault_type;
+#define XE_PAGEFAULT_LEVEL_NACK 0xff /* Producer indicates nack fault */
+ /** @consumer.fault_level: fault level */
+ u8 fault_level;
+ /** @consumer.engine_class: engine class */
+ u8 engine_class;
+ /** @consumer.engine_instance: engine instance */
+ u8 engine_instance;
+ /** consumer.reserved: reserved bits for future expansion */
+ u8 reserved[7];
+ } consumer;
+ /**
+ * @producer: State for the producer (i.e., HW/FW interface). Populated
+ * by the producer and should not be modified—or even inspected—by the
+ * consumer, except for calling operations.
+ */
+ struct {
+ /** @producer.private: private pointer */
+ void *private;
+ /** @producer.ops: operations */
+ const struct xe_pagefault_ops *ops;
+#define XE_PAGEFAULT_PRODUCER_MSG_LEN_DW 4
+ /**
+ * @producer.msg: page fault message, used by producer in fault
+ * acknowledgment to formulate response to HW/FW interface.
+ * Included in the page-fault message because the producer
+ * typically receives the fault in a context where memory cannot
+ * be allocated (e.g., atomic context or the reclaim path).
+ */
+ u32 msg[XE_PAGEFAULT_PRODUCER_MSG_LEN_DW];
+ } producer;
+};
+
+/**
+ * struct xe_pagefault_queue: Xe pagefault queue (consumer)
+ *
+ * Used to capture all device page faults for deferred processing. Size this
+ * queue to absorb the device’s worst-case number of outstanding faults.
+ */
+struct xe_pagefault_queue {
+ /**
+ * @data: Data in queue containing struct xe_pagefault, protected by
+ * @lock
+ */
+ void *data;
+ /** @size: Size of queue in bytes */
+ u32 size;
+ /** @head: Head pointer in bytes, moved by producer, protected by @lock */
+ u32 head;
+ /** @tail: Tail pointer in bytes, moved by consumer, protected by @lock */
+ u32 tail;
+ /** @lock: protects page fault queue */
+ spinlock_t lock;
+ /** @worker: to process page faults */
+ struct work_struct worker;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_pat.c b/drivers/gpu/drm/xe/xe_pat.c
index 2e7cb99ae87a..68171cceea18 100644
--- a/drivers/gpu/drm/xe/xe_pat.c
+++ b/drivers/gpu/drm/xe/xe_pat.c
@@ -57,7 +57,7 @@ struct xe_pat_ops {
int n_entries);
void (*program_media)(struct xe_gt *gt, const struct xe_pat_table_entry table[],
int n_entries);
- void (*dump)(struct xe_gt *gt, struct drm_printer *p);
+ int (*dump)(struct xe_gt *gt, struct drm_printer *p);
};
static const struct xe_pat_table_entry xelp_pat_table[] = {
@@ -115,7 +115,8 @@ static const struct xe_pat_table_entry xelpg_pat_table[] = {
REG_FIELD_PREP(XE2_L4_POLICY, l4_policy) | \
REG_FIELD_PREP(XE2_COH_MODE, __coh_mode), \
.coh_mode = (BUILD_BUG_ON_ZERO(__coh_mode && comp_en) || __coh_mode) ? \
- XE_COH_AT_LEAST_1WAY : XE_COH_NONE \
+ XE_COH_AT_LEAST_1WAY : XE_COH_NONE, \
+ .valid = 1 \
}
static const struct xe_pat_table_entry xe2_pat_table[] = {
@@ -154,6 +155,41 @@ static const struct xe_pat_table_entry xe2_pat_table[] = {
static const struct xe_pat_table_entry xe2_pat_ats = XE2_PAT( 0, 0, 0, 0, 3, 3 );
static const struct xe_pat_table_entry xe2_pat_pta = XE2_PAT( 0, 0, 0, 0, 3, 0 );
+/*
+ * Xe3p_XPC PAT table uses the same layout as Xe2/Xe3, except that there's no
+ * option for compression. Also note that the "L3" and "L4" register fields
+ * actually control L2 and L3 cache respectively on this platform.
+ */
+#define XE3P_XPC_PAT(no_promote, l3clos, l3_policy, l4_policy, __coh_mode) \
+ XE2_PAT(no_promote, 0, l3clos, l3_policy, l4_policy, __coh_mode)
+
+static const struct xe_pat_table_entry xe3p_xpc_pat_ats = XE3P_XPC_PAT( 0, 0, 0, 0, 3 );
+static const struct xe_pat_table_entry xe3p_xpc_pat_pta = XE3P_XPC_PAT( 0, 0, 0, 0, 0 );
+
+static const struct xe_pat_table_entry xe3p_xpc_pat_table[] = {
+ [ 0] = XE3P_XPC_PAT( 0, 0, 0, 0, 0 ),
+ [ 1] = XE3P_XPC_PAT( 0, 0, 0, 0, 2 ),
+ [ 2] = XE3P_XPC_PAT( 0, 0, 0, 0, 3 ),
+ [ 3] = XE3P_XPC_PAT( 0, 0, 3, 3, 0 ),
+ [ 4] = XE3P_XPC_PAT( 0, 0, 3, 3, 2 ),
+ [ 5] = XE3P_XPC_PAT( 0, 0, 3, 0, 0 ),
+ [ 6] = XE3P_XPC_PAT( 0, 0, 3, 0, 2 ),
+ [ 7] = XE3P_XPC_PAT( 0, 0, 3, 0, 3 ),
+ [ 8] = XE3P_XPC_PAT( 0, 0, 0, 3, 0 ),
+ [ 9] = XE3P_XPC_PAT( 0, 0, 0, 3, 2 ),
+ [10] = XE3P_XPC_PAT( 0, 0, 0, 3, 3 ),
+ /* 11..22 are reserved; leave set to all 0's */
+ [23] = XE3P_XPC_PAT( 0, 1, 0, 0, 0 ),
+ [24] = XE3P_XPC_PAT( 0, 1, 0, 0, 2 ),
+ [25] = XE3P_XPC_PAT( 0, 1, 0, 0, 3 ),
+ [26] = XE3P_XPC_PAT( 0, 2, 0, 0, 0 ),
+ [27] = XE3P_XPC_PAT( 0, 2, 0, 0, 2 ),
+ [28] = XE3P_XPC_PAT( 0, 2, 0, 0, 3 ),
+ [29] = XE3P_XPC_PAT( 0, 3, 0, 0, 0 ),
+ [30] = XE3P_XPC_PAT( 0, 3, 0, 0, 2 ),
+ [31] = XE3P_XPC_PAT( 0, 3, 0, 0, 3 ),
+};
+
u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index)
{
WARN_ON(pat_index >= xe->pat.n_entries);
@@ -194,7 +230,7 @@ static void program_pat_mcr(struct xe_gt *gt, const struct xe_pat_table_entry ta
xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_PTA), xe->pat.pat_pta->value);
}
-static void xelp_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xelp_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -202,7 +238,7 @@ static void xelp_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -215,6 +251,7 @@ static void xelp_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xelp_pat_ops = {
@@ -222,7 +259,7 @@ static const struct xe_pat_ops xelp_pat_ops = {
.dump = xelp_dump,
};
-static void xehp_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xehp_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -230,7 +267,7 @@ static void xehp_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -245,6 +282,7 @@ static void xehp_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xehp_pat_ops = {
@@ -252,7 +290,7 @@ static const struct xe_pat_ops xehp_pat_ops = {
.dump = xehp_dump,
};
-static void xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -260,7 +298,7 @@ static void xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -273,6 +311,7 @@ static void xehpc_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xehpc_pat_ops = {
@@ -280,7 +319,7 @@ static const struct xe_pat_ops xehpc_pat_ops = {
.dump = xehpc_dump,
};
-static void xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -288,7 +327,7 @@ static void xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
drm_printf(p, "PAT table:\n");
@@ -306,6 +345,7 @@ static void xelpg_dump(struct xe_gt *gt, struct drm_printer *p)
}
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
/*
@@ -318,7 +358,7 @@ static const struct xe_pat_ops xelpg_pat_ops = {
.dump = xelpg_dump,
};
-static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
+static int xe2_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
unsigned int fw_ref;
@@ -327,9 +367,9 @@ static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
if (!fw_ref)
- return;
+ return -ETIMEDOUT;
- drm_printf(p, "PAT table:\n");
+ drm_printf(p, "PAT table: (* = reserved entry)\n");
for (i = 0; i < xe->pat.n_entries; i++) {
if (xe_gt_is_media_type(gt))
@@ -337,14 +377,14 @@ static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
else
pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
- drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u, %u ] (%#8x)\n", i,
+ drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u, %u ] (%#8x)%s\n", i,
!!(pat & XE2_NO_PROMOTE),
!!(pat & XE2_COMP_EN),
REG_FIELD_GET(XE2_L3_CLOS, pat),
REG_FIELD_GET(XE2_L3_POLICY, pat),
REG_FIELD_GET(XE2_L4_POLICY, pat),
REG_FIELD_GET(XE2_COH_MODE, pat),
- pat);
+ pat, xe->pat.table[i].valid ? "" : " *");
}
/*
@@ -367,6 +407,7 @@ static void xe2_dump(struct xe_gt *gt, struct drm_printer *p)
pat);
xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
}
static const struct xe_pat_ops xe2_pat_ops = {
@@ -375,9 +416,68 @@ static const struct xe_pat_ops xe2_pat_ops = {
.dump = xe2_dump,
};
+static int xe3p_xpc_dump(struct xe_gt *gt, struct drm_printer *p)
+{
+ struct xe_device *xe = gt_to_xe(gt);
+ unsigned int fw_ref;
+ u32 pat;
+ int i;
+
+ fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref)
+ return -ETIMEDOUT;
+
+ drm_printf(p, "PAT table: (* = reserved entry)\n");
+
+ for (i = 0; i < xe->pat.n_entries; i++) {
+ pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
+
+ drm_printf(p, "PAT[%2d] = [ %u, %u, %u, %u, %u ] (%#8x)%s\n", i,
+ !!(pat & XE2_NO_PROMOTE),
+ REG_FIELD_GET(XE2_L3_CLOS, pat),
+ REG_FIELD_GET(XE2_L3_POLICY, pat),
+ REG_FIELD_GET(XE2_L4_POLICY, pat),
+ REG_FIELD_GET(XE2_COH_MODE, pat),
+ pat, xe->pat.table[i].valid ? "" : " *");
+ }
+
+ /*
+ * Also print PTA_MODE, which describes how the hardware accesses
+ * PPGTT entries.
+ */
+ pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_PTA));
+
+ drm_printf(p, "Page Table Access:\n");
+ drm_printf(p, "PTA_MODE= [ %u, %u, %u, %u, %u ] (%#8x)\n",
+ !!(pat & XE2_NO_PROMOTE),
+ REG_FIELD_GET(XE2_L3_CLOS, pat),
+ REG_FIELD_GET(XE2_L3_POLICY, pat),
+ REG_FIELD_GET(XE2_L4_POLICY, pat),
+ REG_FIELD_GET(XE2_COH_MODE, pat),
+ pat);
+
+ xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ return 0;
+}
+
+static const struct xe_pat_ops xe3p_xpc_pat_ops = {
+ .program_graphics = program_pat_mcr,
+ .program_media = program_pat,
+ .dump = xe3p_xpc_dump,
+};
+
void xe_pat_init_early(struct xe_device *xe)
{
- if (GRAPHICS_VER(xe) == 30 || GRAPHICS_VER(xe) == 20) {
+ if (GRAPHICS_VERx100(xe) == 3511) {
+ xe->pat.ops = &xe3p_xpc_pat_ops;
+ xe->pat.table = xe3p_xpc_pat_table;
+ xe->pat.pat_ats = &xe3p_xpc_pat_ats;
+ xe->pat.pat_pta = &xe3p_xpc_pat_pta;
+ xe->pat.n_entries = ARRAY_SIZE(xe3p_xpc_pat_table);
+ xe->pat.idx[XE_CACHE_NONE] = 3;
+ xe->pat.idx[XE_CACHE_WT] = 3; /* N/A (no display); use UC */
+ xe->pat.idx[XE_CACHE_WB] = 2;
+ } else if (GRAPHICS_VER(xe) == 30 || GRAPHICS_VER(xe) == 20) {
xe->pat.ops = &xe2_pat_ops;
xe->pat.table = xe2_pat_table;
xe->pat.pat_ats = &xe2_pat_ats;
@@ -462,12 +562,19 @@ void xe_pat_init(struct xe_gt *gt)
xe->pat.ops->program_graphics(gt, xe->pat.table, xe->pat.n_entries);
}
-void xe_pat_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_pat_dump() - Dump GT PAT table into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
if (!xe->pat.ops)
- return;
+ return -EOPNOTSUPP;
- xe->pat.ops->dump(gt, p);
+ return xe->pat.ops->dump(gt, p);
}
diff --git a/drivers/gpu/drm/xe/xe_pat.h b/drivers/gpu/drm/xe/xe_pat.h
index fa0dfbe525cd..05dae03a5f54 100644
--- a/drivers/gpu/drm/xe/xe_pat.h
+++ b/drivers/gpu/drm/xe/xe_pat.h
@@ -29,6 +29,11 @@ struct xe_pat_table_entry {
#define XE_COH_NONE 1
#define XE_COH_AT_LEAST_1WAY 2
u16 coh_mode;
+
+ /**
+ * @valid: Set to 1 if the entry is valid, 0 if it's reserved.
+ */
+ u16 valid;
};
/**
@@ -43,12 +48,7 @@ void xe_pat_init_early(struct xe_device *xe);
*/
void xe_pat_init(struct xe_gt *gt);
-/**
- * xe_pat_dump - Dump PAT table
- * @gt: GT structure
- * @p: Printer to dump info to
- */
-void xe_pat_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_pat_dump(struct xe_gt *gt, struct drm_printer *p);
/**
* xe_pat_index_get_coh_mode - Extract the coherency mode for the given
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 9a6df79fc5b6..9c9ea10d994c 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -30,6 +30,7 @@
#include "xe_pci_sriov.h"
#include "xe_pci_types.h"
#include "xe_pm.h"
+#include "xe_printk.h"
#include "xe_sriov.h"
#include "xe_step.h"
#include "xe_survivability_mode.h"
@@ -51,15 +52,10 @@ __diag_ignore_all("-Woverride-init", "Allow field overrides in table");
static const struct xe_graphics_desc graphics_xelp = {
.hw_engine_mask = BIT(XE_HW_ENGINE_RCS0) | BIT(XE_HW_ENGINE_BCS0),
-
- .va_bits = 48,
- .vm_max_level = 3,
};
#define XE_HP_FEATURES \
- .has_range_tlb_inval = true, \
- .va_bits = 48, \
- .vm_max_level = 3
+ .has_range_tlb_inval = true
static const struct xe_graphics_desc graphics_xehpg = {
.hw_engine_mask =
@@ -68,9 +64,6 @@ static const struct xe_graphics_desc graphics_xehpg = {
BIT(XE_HW_ENGINE_CCS2) | BIT(XE_HW_ENGINE_CCS3),
XE_HP_FEATURES,
- .vram_flags = XE_VRAM_FLAGS_NEED64K,
-
- .has_flat_ccs = 1,
};
static const struct xe_graphics_desc graphics_xehpc = {
@@ -84,9 +77,6 @@ static const struct xe_graphics_desc graphics_xehpc = {
BIT(XE_HW_ENGINE_CCS2) | BIT(XE_HW_ENGINE_CCS3),
XE_HP_FEATURES,
- .va_bits = 57,
- .vm_max_level = 4,
- .vram_flags = XE_VRAM_FLAGS_NEED64K,
.has_asid = 1,
.has_atomic_enable_pte_bit = 1,
@@ -104,12 +94,9 @@ static const struct xe_graphics_desc graphics_xelpg = {
#define XE2_GFX_FEATURES \
.has_asid = 1, \
.has_atomic_enable_pte_bit = 1, \
- .has_flat_ccs = 1, \
.has_range_tlb_inval = 1, \
.has_usm = 1, \
.has_64bit_timestamp = 1, \
- .va_bits = 48, \
- .vm_max_level = 4, \
.hw_engine_mask = \
BIT(XE_HW_ENGINE_RCS0) | \
BIT(XE_HW_ENGINE_BCS8) | BIT(XE_HW_ENGINE_BCS0) | \
@@ -119,6 +106,13 @@ static const struct xe_graphics_desc graphics_xe2 = {
XE2_GFX_FEATURES,
};
+static const struct xe_graphics_desc graphics_xe3p_xpc = {
+ XE2_GFX_FEATURES,
+ .hw_engine_mask =
+ GENMASK(XE_HW_ENGINE_BCS8, XE_HW_ENGINE_BCS1) |
+ GENMASK(XE_HW_ENGINE_CCS3, XE_HW_ENGINE_CCS0),
+};
+
static const struct xe_media_desc media_xem = {
.hw_engine_mask =
GENMASK(XE_HW_ENGINE_VCS7, XE_HW_ENGINE_VCS0) |
@@ -149,6 +143,9 @@ static const struct xe_ip graphics_ips[] = {
{ 3000, "Xe3_LPG", &graphics_xe2 },
{ 3001, "Xe3_LPG", &graphics_xe2 },
{ 3003, "Xe3_LPG", &graphics_xe2 },
+ { 3004, "Xe3_LPG", &graphics_xe2 },
+ { 3005, "Xe3_LPG", &graphics_xe2 },
+ { 3511, "Xe3p_XPC", &graphics_xe3p_xpc },
};
/* Pre-GMDID Media IPs */
@@ -162,6 +159,8 @@ static const struct xe_ip media_ips[] = {
{ 2000, "Xe2_LPM", &media_xelpmp },
{ 3000, "Xe3_LPM", &media_xelpmp },
{ 3002, "Xe3_LPM", &media_xelpmp },
+ { 3500, "Xe3p_LPM", &media_xelpmp },
+ { 3503, "Xe3p_HPM", &media_xelpmp },
};
static const struct xe_device_desc tgl_desc = {
@@ -174,6 +173,8 @@ static const struct xe_device_desc tgl_desc = {
.has_sriov = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const struct xe_device_desc rkl_desc = {
@@ -185,6 +186,8 @@ static const struct xe_device_desc rkl_desc = {
.has_llc = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const u16 adls_rpls_ids[] = { INTEL_RPLS_IDS(NOP), 0 };
@@ -203,6 +206,8 @@ static const struct xe_device_desc adl_s_desc = {
{ XE_SUBPLATFORM_ALDERLAKE_S_RPLS, "RPLS", adls_rpls_ids },
{},
},
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const u16 adlp_rplu_ids[] = { INTEL_RPLU_IDS(NOP), 0 };
@@ -221,6 +226,8 @@ static const struct xe_device_desc adl_p_desc = {
{ XE_SUBPLATFORM_ALDERLAKE_P_RPLU, "RPLU", adlp_rplu_ids },
{},
},
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const struct xe_device_desc adl_n_desc = {
@@ -233,6 +240,8 @@ static const struct xe_device_desc adl_n_desc = {
.has_sriov = true,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
#define DGFX_FEATURES \
@@ -249,6 +258,8 @@ static const struct xe_device_desc dg1_desc = {
.has_heci_gscfi = 1,
.max_gt_per_tile = 1,
.require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const u16 dg2_g10_ids[] = { INTEL_DG2_G10_IDS(NOP), INTEL_ATS_M150_IDS(NOP), 0 };
@@ -258,6 +269,7 @@ static const u16 dg2_g12_ids[] = { INTEL_DG2_G12_IDS(NOP), 0 };
#define DG2_FEATURES \
DGFX_FEATURES, \
PLATFORM(DG2), \
+ .has_flat_ccs = 1, \
.has_gsc_nvm = 1, \
.has_heci_gscfi = 1, \
.subplatforms = (const struct xe_subplatform_desc[]) { \
@@ -265,7 +277,10 @@ static const u16 dg2_g12_ids[] = { INTEL_DG2_G12_IDS(NOP), 0 };
{ XE_SUBPLATFORM_DG2_G11, "G11", dg2_g11_ids }, \
{ XE_SUBPLATFORM_DG2_G12, "G12", dg2_g12_ids }, \
{ } \
- }
+ }, \
+ .va_bits = 48, \
+ .vm_max_level = 3, \
+ .vram_flags = XE_VRAM_FLAGS_NEED64K
static const struct xe_device_desc ats_m_desc = {
.pre_gmdid_graphics_ip = &graphics_ip_xehpg,
@@ -303,6 +318,9 @@ static const __maybe_unused struct xe_device_desc pvc_desc = {
.max_gt_per_tile = 1,
.max_remote_tiles = 1,
.require_force_probe = true,
+ .va_bits = 57,
+ .vm_max_level = 4,
+ .vram_flags = XE_VRAM_FLAGS_NEED64K,
.has_mbx_power_limits = false,
};
@@ -314,39 +332,86 @@ static const struct xe_device_desc mtl_desc = {
.has_display = true,
.has_pxp = true,
.max_gt_per_tile = 2,
+ .va_bits = 48,
+ .vm_max_level = 3,
};
static const struct xe_device_desc lnl_desc = {
PLATFORM(LUNARLAKE),
.dma_mask_size = 46,
.has_display = true,
+ .has_flat_ccs = 1,
.has_pxp = true,
+ .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
+ .va_bits = 48,
+ .vm_max_level = 4,
};
+static const u16 bmg_g21_ids[] = { INTEL_BMG_G21_IDS(NOP), 0 };
+
static const struct xe_device_desc bmg_desc = {
DGFX_FEATURES,
PLATFORM(BATTLEMAGE),
.dma_mask_size = 46,
.has_display = true,
.has_fan_control = true,
+ .has_flat_ccs = 1,
.has_mbx_power_limits = true,
.has_gsc_nvm = 1,
.has_heci_cscfi = 1,
.has_late_bind = true,
.has_sriov = true,
+ .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
+ .subplatforms = (const struct xe_subplatform_desc[]) {
+ { XE_SUBPLATFORM_BATTLEMAGE_G21, "G21", bmg_g21_ids },
+ { }
+ },
+ .va_bits = 48,
+ .vm_max_level = 4,
};
static const struct xe_device_desc ptl_desc = {
PLATFORM(PANTHERLAKE),
.dma_mask_size = 46,
.has_display = true,
+ .has_flat_ccs = 1,
.has_sriov = true,
+ .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
+ .needs_shared_vf_gt_wq = true,
+ .va_bits = 48,
+ .vm_max_level = 4,
+};
+
+static const struct xe_device_desc nvls_desc = {
+ PLATFORM(NOVALAKE_S),
+ .dma_mask_size = 46,
+ .has_display = true,
+ .has_flat_ccs = 1,
+ .has_mem_copy_instr = true,
+ .max_gt_per_tile = 2,
+ .require_force_probe = true,
+ .va_bits = 48,
+ .vm_max_level = 4,
+};
+
+static const struct xe_device_desc cri_desc = {
+ DGFX_FEATURES,
+ PLATFORM(CRESCENTISLAND),
+ .dma_mask_size = 52,
+ .has_display = false,
+ .has_flat_ccs = false,
+ .has_mbx_power_limits = true,
+ .has_sriov = true,
+ .max_gt_per_tile = 2,
+ .require_force_probe = true,
+ .va_bits = 57,
+ .vm_max_level = 4,
};
#undef PLATFORM
@@ -375,6 +440,9 @@ static const struct pci_device_id pciidlist[] = {
INTEL_LNL_IDS(INTEL_VGA_DEVICE, &lnl_desc),
INTEL_BMG_IDS(INTEL_VGA_DEVICE, &bmg_desc),
INTEL_PTL_IDS(INTEL_VGA_DEVICE, &ptl_desc),
+ INTEL_WCL_IDS(INTEL_VGA_DEVICE, &ptl_desc),
+ INTEL_NVLS_IDS(INTEL_VGA_DEVICE, &nvls_desc),
+ INTEL_CRI_IDS(INTEL_PCI_DEVICE, &cri_desc),
{ }
};
MODULE_DEVICE_TABLE(pci, pciidlist);
@@ -447,7 +515,7 @@ enum xe_gmdid_type {
GMDID_MEDIA
};
-static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver, u32 *revid)
+static int read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver, u32 *revid)
{
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
struct xe_reg gmdid_reg = GMD_ID;
@@ -456,22 +524,24 @@ static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver,
KUNIT_STATIC_STUB_REDIRECT(read_gmdid, xe, type, ver, revid);
if (IS_SRIOV_VF(xe)) {
- struct xe_gt *gt = xe_root_mmio_gt(xe);
-
/*
* To get the value of the GMDID register, VFs must obtain it
* from the GuC using MMIO communication.
*
- * Note that at this point the xe_gt is not fully uninitialized
- * and only basic access to MMIO registers is possible. To use
- * our existing GuC communication functions we must perform at
- * least basic xe_gt and xe_guc initialization.
- *
- * Since to obtain the value of GMDID_MEDIA we need to use the
- * media GuC, temporarily tweak the gt type.
+ * Note that at this point the GTs are not initialized and only
+ * tile-level access to MMIO registers is possible. To use our
+ * existing GuC communication functions we must create a dummy
+ * GT structure and perform at least basic xe_gt and xe_guc
+ * initialization.
*/
- xe_gt_assert(gt, gt->info.type == XE_GT_TYPE_UNINITIALIZED);
+ struct xe_gt *gt __free(kfree) = NULL;
+ int err;
+
+ gt = kzalloc(sizeof(*gt), GFP_KERNEL);
+ if (!gt)
+ return -ENOMEM;
+ gt->tile = &xe->tiles[0];
if (type == GMDID_MEDIA) {
gt->info.id = 1;
gt->info.type = XE_GT_TYPE_MEDIA;
@@ -483,15 +553,11 @@ static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver,
xe_gt_mmio_init(gt);
xe_guc_comm_init_early(&gt->uc.guc);
- /* Don't bother with GMDID if failed to negotiate the GuC ABI */
- val = xe_gt_sriov_vf_bootstrap(gt) ? 0 : xe_gt_sriov_vf_gmdid(gt);
+ err = xe_gt_sriov_vf_bootstrap(gt);
+ if (err)
+ return err;
- /*
- * Only undo xe_gt.info here, the remaining changes made above
- * will be overwritten as part of the regular initialization.
- */
- gt->info.id = 0;
- gt->info.type = XE_GT_TYPE_UNINITIALIZED;
+ val = xe_gt_sriov_vf_gmdid(gt);
} else {
/*
* GMD_ID is a GT register, but at this point in the driver
@@ -509,6 +575,8 @@ static void read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver,
*ver = REG_FIELD_GET(GMD_ID_ARCH_MASK, val) * 100 + REG_FIELD_GET(GMD_ID_RELEASE_MASK, val);
*revid = REG_FIELD_GET(GMD_ID_REVID, val);
+
+ return 0;
}
static const struct xe_ip *find_graphics_ip(unsigned int verx100)
@@ -535,18 +603,21 @@ static const struct xe_ip *find_media_ip(unsigned int verx100)
* Read IP version from hardware and select graphics/media IP descriptors
* based on the result.
*/
-static void handle_gmdid(struct xe_device *xe,
- const struct xe_ip **graphics_ip,
- const struct xe_ip **media_ip,
- u32 *graphics_revid,
- u32 *media_revid)
+static int handle_gmdid(struct xe_device *xe,
+ const struct xe_ip **graphics_ip,
+ const struct xe_ip **media_ip,
+ u32 *graphics_revid,
+ u32 *media_revid)
{
u32 ver;
+ int ret;
*graphics_ip = NULL;
*media_ip = NULL;
- read_gmdid(xe, GMDID_GRAPHICS, &ver, graphics_revid);
+ ret = read_gmdid(xe, GMDID_GRAPHICS, &ver, graphics_revid);
+ if (ret)
+ return ret;
*graphics_ip = find_graphics_ip(ver);
if (!*graphics_ip) {
@@ -554,16 +625,21 @@ static void handle_gmdid(struct xe_device *xe,
ver / 100, ver % 100);
}
- read_gmdid(xe, GMDID_MEDIA, &ver, media_revid);
+ ret = read_gmdid(xe, GMDID_MEDIA, &ver, media_revid);
+ if (ret)
+ return ret;
+
/* Media may legitimately be fused off / not present */
if (ver == 0)
- return;
+ return 0;
*media_ip = find_media_ip(ver);
if (!*media_ip) {
drm_err(&xe->drm, "Hardware reports unknown media version %u.%02u\n",
ver / 100, ver % 100);
}
+
+ return 0;
}
/*
@@ -582,8 +658,14 @@ static int xe_info_init_early(struct xe_device *xe,
subplatform_desc->subplatform : XE_SUBPLATFORM_NONE;
xe->info.dma_mask_size = desc->dma_mask_size;
+ xe->info.va_bits = desc->va_bits;
+ xe->info.vm_max_level = desc->vm_max_level;
+ xe->info.vram_flags = desc->vram_flags;
+
xe->info.is_dgfx = desc->is_dgfx;
xe->info.has_fan_control = desc->has_fan_control;
+ /* runtime fusing may force flat_ccs to disabled later */
+ xe->info.has_flat_ccs = desc->has_flat_ccs;
xe->info.has_mbx_power_limits = desc->has_mbx_power_limits;
xe->info.has_gsc_nvm = desc->has_gsc_nvm;
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
@@ -591,11 +673,14 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.has_late_bind = desc->has_late_bind;
xe->info.has_llc = desc->has_llc;
xe->info.has_pxp = desc->has_pxp;
- xe->info.has_sriov = desc->has_sriov;
+ xe->info.has_sriov = xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev)) &&
+ desc->has_sriov;
+ xe->info.has_mem_copy_instr = desc->has_mem_copy_instr;
xe->info.skip_guc_pc = desc->skip_guc_pc;
xe->info.skip_mtcfg = desc->skip_mtcfg;
xe->info.skip_pcode = desc->skip_pcode;
xe->info.needs_scratch = desc->needs_scratch;
+ xe->info.needs_shared_vf_gt_wq = desc->needs_shared_vf_gt_wq;
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
xe_modparam.probe_display &&
@@ -651,6 +736,63 @@ static void xe_info_probe_tile_count(struct xe_device *xe)
}
}
+static struct xe_gt *alloc_primary_gt(struct xe_tile *tile,
+ const struct xe_graphics_desc *graphics_desc,
+ const struct xe_media_desc *media_desc)
+{
+ struct xe_device *xe = tile_to_xe(tile);
+ struct xe_gt *gt;
+
+ if (!xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev))) {
+ xe_info(xe, "Primary GT disabled via configfs\n");
+ return NULL;
+ }
+
+ gt = xe_gt_alloc(tile);
+ if (IS_ERR(gt))
+ return gt;
+
+ gt->info.type = XE_GT_TYPE_MAIN;
+ gt->info.id = tile->id * xe->info.max_gt_per_tile;
+ gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state;
+ gt->info.engine_mask = graphics_desc->hw_engine_mask;
+
+ /*
+ * Before media version 13, the media IP was part of the primary GT
+ * so we need to add the media engines to the primary GT's engine list.
+ */
+ if (MEDIA_VER(xe) < 13 && media_desc)
+ gt->info.engine_mask |= media_desc->hw_engine_mask;
+
+ return gt;
+}
+
+static struct xe_gt *alloc_media_gt(struct xe_tile *tile,
+ const struct xe_media_desc *media_desc)
+{
+ struct xe_device *xe = tile_to_xe(tile);
+ struct xe_gt *gt;
+
+ if (!xe_configfs_media_gt_allowed(to_pci_dev(xe->drm.dev))) {
+ xe_info(xe, "Media GT disabled via configfs\n");
+ return NULL;
+ }
+
+ if (MEDIA_VER(xe) < 13 || !media_desc)
+ return NULL;
+
+ gt = xe_gt_alloc(tile);
+ if (IS_ERR(gt))
+ return gt;
+
+ gt->info.type = XE_GT_TYPE_MEDIA;
+ gt->info.id = tile->id * xe->info.max_gt_per_tile + 1;
+ gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state;
+ gt->info.engine_mask = media_desc->hw_engine_mask;
+
+ return gt;
+}
+
/*
* Initialize device info content that does require knowledge about
* graphics / media IP version.
@@ -667,6 +809,7 @@ static int xe_info_init(struct xe_device *xe,
const struct xe_media_desc *media_desc;
struct xe_tile *tile;
struct xe_gt *gt;
+ int ret;
u8 id;
/*
@@ -682,8 +825,11 @@ static int xe_info_init(struct xe_device *xe,
xe->info.step = xe_step_pre_gmdid_get(xe);
} else {
xe_assert(xe, !desc->pre_gmdid_media_ip);
- handle_gmdid(xe, &graphics_ip, &media_ip,
- &graphics_gmdid_revid, &media_gmdid_revid);
+ ret = handle_gmdid(xe, &graphics_ip, &media_ip,
+ &graphics_gmdid_revid, &media_gmdid_revid);
+ if (ret)
+ return ret;
+
xe->info.step = xe_step_gmdid_get(xe,
graphics_gmdid_revid,
media_gmdid_revid);
@@ -710,17 +856,11 @@ static int xe_info_init(struct xe_device *xe,
media_desc = NULL;
}
- xe->info.vram_flags = graphics_desc->vram_flags;
- xe->info.va_bits = graphics_desc->va_bits;
- xe->info.vm_max_level = graphics_desc->vm_max_level;
xe->info.has_asid = graphics_desc->has_asid;
xe->info.has_atomic_enable_pte_bit = graphics_desc->has_atomic_enable_pte_bit;
if (xe->info.platform != XE_PVC)
xe->info.has_device_atomics_on_smem = 1;
- /* Runtime detection may change this later */
- xe->info.has_flat_ccs = graphics_desc->has_flat_ccs;
-
xe->info.has_range_tlb_inval = graphics_desc->has_range_tlb_inval;
xe->info.has_usm = graphics_desc->has_usm;
xe->info.has_64bit_timestamp = graphics_desc->has_64bit_timestamp;
@@ -735,44 +875,33 @@ static int xe_info_init(struct xe_device *xe,
return err;
}
- /*
- * All platforms have at least one primary GT. Any platform with media
- * version 13 or higher has an additional dedicated media GT. And
- * depending on the graphics IP there may be additional "remote tiles."
- * All of these together determine the overall GT count.
- */
+ /* Allocate any GT and VRAM structures necessary for the platform. */
for_each_tile(tile, xe, id) {
int err;
- gt = tile->primary_gt;
- gt->info.type = XE_GT_TYPE_MAIN;
- gt->info.id = tile->id * xe->info.max_gt_per_tile;
- gt->info.has_indirect_ring_state = graphics_desc->has_indirect_ring_state;
- gt->info.engine_mask = graphics_desc->hw_engine_mask;
-
err = xe_tile_alloc_vram(tile);
if (err)
return err;
- if (MEDIA_VER(xe) < 13 && media_desc)
- gt->info.engine_mask |= media_desc->hw_engine_mask;
-
- if (MEDIA_VER(xe) < 13 || !media_desc)
- continue;
+ tile->primary_gt = alloc_primary_gt(tile, graphics_desc, media_desc);
+ if (IS_ERR(tile->primary_gt))
+ return PTR_ERR(tile->primary_gt);
/*
- * Allocate and setup media GT for platforms with standalone
- * media.
+ * It's not currently possible to probe a device with the
+ * primary GT disabled. With some work, this may be future in
+ * the possible for igpu platforms (although probably not for
+ * dgpu's since access to the primary GT's BCS engines is
+ * required for VRAM management).
*/
- tile->media_gt = xe_gt_alloc(tile);
+ if (!tile->primary_gt) {
+ drm_err(&xe->drm, "Cannot probe device with without a primary GT\n");
+ return -ENODEV;
+ }
+
+ tile->media_gt = alloc_media_gt(tile, media_desc);
if (IS_ERR(tile->media_gt))
return PTR_ERR(tile->media_gt);
-
- gt = tile->media_gt;
- gt->info.type = XE_GT_TYPE_MEDIA;
- gt->info.id = tile->id * xe->info.max_gt_per_tile + 1;
- gt->info.has_indirect_ring_state = media_desc->has_indirect_ring_state;
- gt->info.engine_mask = media_desc->hw_engine_mask;
}
/*
@@ -1094,6 +1223,23 @@ static struct pci_driver xe_pci_driver = {
#endif
};
+/**
+ * xe_pci_to_pf_device() - Get PF &xe_device.
+ * @pdev: the VF &pci_dev device
+ *
+ * Return: pointer to PF &xe_device, NULL otherwise.
+ */
+struct xe_device *xe_pci_to_pf_device(struct pci_dev *pdev)
+{
+ struct drm_device *drm;
+
+ drm = pci_iov_get_pf_drvdata(pdev, &xe_pci_driver);
+ if (IS_ERR(drm))
+ return NULL;
+
+ return to_xe_device(drm);
+}
+
int xe_register_pci_driver(void)
{
return pci_register_driver(&xe_pci_driver);
diff --git a/drivers/gpu/drm/xe/xe_pci.h b/drivers/gpu/drm/xe/xe_pci.h
index 611c1209b14c..11bcc5fe2c5b 100644
--- a/drivers/gpu/drm/xe/xe_pci.h
+++ b/drivers/gpu/drm/xe/xe_pci.h
@@ -6,7 +6,10 @@
#ifndef _XE_PCI_H_
#define _XE_PCI_H_
+struct pci_dev;
+
int xe_register_pci_driver(void);
void xe_unregister_pci_driver(void);
+struct xe_device *xe_pci_to_pf_device(struct pci_dev *pdev);
#endif
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.c b/drivers/gpu/drm/xe/xe_pci_sriov.c
index af05db07162e..9ff69c4843b0 100644
--- a/drivers/gpu/drm/xe/xe_pci_sriov.c
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.c
@@ -17,68 +17,18 @@
#include "xe_pm.h"
#include "xe_sriov.h"
#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_control.h"
#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_provision.h"
+#include "xe_sriov_pf_sysfs.h"
#include "xe_sriov_printk.h"
-static int pf_needs_provisioning(struct xe_gt *gt, unsigned int num_vfs)
-{
- unsigned int n;
-
- for (n = 1; n <= num_vfs; n++)
- if (!xe_gt_sriov_pf_config_is_empty(gt, n))
- return false;
-
- return true;
-}
-
-static int pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs)
-{
- struct xe_gt *gt;
- unsigned int id;
- int result = 0, err;
-
- for_each_gt(gt, xe, id) {
- if (!pf_needs_provisioning(gt, num_vfs))
- continue;
- err = xe_gt_sriov_pf_config_set_fair(gt, VFID(1), num_vfs);
- result = result ?: err;
- }
-
- return result;
-}
-
-static void pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs)
-{
- struct xe_gt *gt;
- unsigned int id;
- unsigned int n;
-
- for_each_gt(gt, xe, id)
- for (n = 1; n <= num_vfs; n++)
- xe_gt_sriov_pf_config_release(gt, n, true);
-}
-
static void pf_reset_vfs(struct xe_device *xe, unsigned int num_vfs)
{
- struct xe_gt *gt;
- unsigned int id;
unsigned int n;
- for_each_gt(gt, xe, id)
- for (n = 1; n <= num_vfs; n++)
- xe_gt_sriov_pf_control_trigger_flr(gt, n);
-}
-
-static struct pci_dev *xe_pci_pf_get_vf_dev(struct xe_device *xe, unsigned int vf_id)
-{
- struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
-
- xe_assert(xe, IS_SRIOV_PF(xe));
-
- /* caller must use pci_dev_put() */
- return pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
- pdev->bus->number,
- pci_iov_virtfn_devfn(pdev, vf_id));
+ for (n = 1; n <= num_vfs; n++)
+ xe_sriov_pf_control_reset_vf(xe, n);
}
static void pf_link_vfs(struct xe_device *xe, int num_vfs)
@@ -99,7 +49,7 @@ static void pf_link_vfs(struct xe_device *xe, int num_vfs)
* enforce correct resume order.
*/
for (n = 1; n <= num_vfs; n++) {
- pdev_vf = xe_pci_pf_get_vf_dev(xe, n - 1);
+ pdev_vf = xe_pci_sriov_get_vf_pdev(pdev_pf, n);
/* unlikely, something weird is happening, abort */
if (!pdev_vf) {
@@ -144,6 +94,20 @@ static int resize_vf_vram_bar(struct xe_device *xe, int num_vfs)
return pci_iov_vf_bar_set_size(pdev, VF_LMEM_BAR, __fls(sizes));
}
+static int pf_prepare_vfs_enabling(struct xe_device *xe)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ /* make sure we are not locked-down by other components */
+ return xe_sriov_pf_arm_guard(xe, &xe->sriov.pf.guard_vfs_enabling, false, NULL);
+}
+
+static void pf_finish_vfs_enabling(struct xe_device *xe)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ /* allow other components to lockdown VFs enabling */
+ xe_sriov_pf_disarm_guard(xe, &xe->sriov.pf.guard_vfs_enabling, false, NULL);
+}
+
static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
@@ -159,6 +123,10 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
if (err)
goto out;
+ err = pf_prepare_vfs_enabling(xe);
+ if (err)
+ goto out;
+
/*
* We must hold additional reference to the runtime PM to keep PF in D0
* during VFs lifetime, as our VFs do not implement the PM capability.
@@ -170,7 +138,7 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
*/
xe_pm_runtime_get_noresume(xe);
- err = pf_provision_vfs(xe, num_vfs);
+ err = xe_sriov_pf_provision_vfs(xe, num_vfs);
if (err < 0)
goto failed;
@@ -189,13 +157,16 @@ static int pf_enable_vfs(struct xe_device *xe, int num_vfs)
xe_sriov_info(xe, "Enabled %u of %u VF%s\n",
num_vfs, total_vfs, str_plural(total_vfs));
+ xe_sriov_pf_sysfs_link_vfs(xe, num_vfs);
+
pf_engine_activity_stats(xe, num_vfs, true);
return num_vfs;
failed:
- pf_unprovision_vfs(xe, num_vfs);
+ xe_sriov_pf_unprovision_vfs(xe, num_vfs);
xe_pm_runtime_put(xe);
+ pf_finish_vfs_enabling(xe);
out:
xe_sriov_notice(xe, "Failed to enable %u VF%s (%pe)\n",
num_vfs, str_plural(num_vfs), ERR_PTR(err));
@@ -216,15 +187,19 @@ static int pf_disable_vfs(struct xe_device *xe)
pf_engine_activity_stats(xe, num_vfs, false);
+ xe_sriov_pf_sysfs_unlink_vfs(xe, num_vfs);
+
pci_disable_sriov(pdev);
pf_reset_vfs(xe, num_vfs);
- pf_unprovision_vfs(xe, num_vfs);
+ xe_sriov_pf_unprovision_vfs(xe, num_vfs);
/* not needed anymore - see pf_enable_vfs() */
xe_pm_runtime_put(xe);
+ pf_finish_vfs_enabling(xe);
+
xe_sriov_info(xe, "Disabled %u VF%s\n", num_vfs, str_plural(num_vfs));
return 0;
}
@@ -267,3 +242,25 @@ int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
return ret;
}
+
+/**
+ * xe_pci_sriov_get_vf_pdev() - Lookup the VF's PCI device using the VF identifier.
+ * @pdev: the PF's &pci_dev
+ * @vfid: VF identifier (1-based)
+ *
+ * The caller must decrement the reference count by calling pci_dev_put().
+ *
+ * Return: the VF's &pci_dev or NULL if the VF device was not found.
+ */
+struct pci_dev *xe_pci_sriov_get_vf_pdev(struct pci_dev *pdev, unsigned int vfid)
+{
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+
+ xe_assert(xe, dev_is_pf(&pdev->dev));
+ xe_assert(xe, vfid);
+ xe_assert(xe, vfid <= pci_sriov_get_totalvfs(pdev));
+
+ return pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus),
+ pdev->bus->number,
+ pci_iov_virtfn_devfn(pdev, vfid - 1));
+}
diff --git a/drivers/gpu/drm/xe/xe_pci_sriov.h b/drivers/gpu/drm/xe/xe_pci_sriov.h
index c76dd0d90495..b9105d71dbb1 100644
--- a/drivers/gpu/drm/xe/xe_pci_sriov.h
+++ b/drivers/gpu/drm/xe/xe_pci_sriov.h
@@ -10,6 +10,7 @@ struct pci_dev;
#ifdef CONFIG_PCI_IOV
int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs);
+struct pci_dev *xe_pci_sriov_get_vf_pdev(struct pci_dev *pdev, unsigned int vfid);
#else
static inline int xe_pci_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h
index 9b9766a3baa3..9892c063a9c5 100644
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@ -30,36 +30,37 @@ struct xe_device_desc {
u8 dma_mask_size;
u8 max_remote_tiles:2;
u8 max_gt_per_tile:2;
+ u8 va_bits;
+ u8 vm_max_level;
+ u8 vram_flags;
u8 require_force_probe:1;
u8 is_dgfx:1;
u8 has_display:1;
u8 has_fan_control:1;
+ u8 has_flat_ccs:1;
u8 has_gsc_nvm:1;
u8 has_heci_gscfi:1;
u8 has_heci_cscfi:1;
u8 has_late_bind:1;
u8 has_llc:1;
u8 has_mbx_power_limits:1;
+ u8 has_mem_copy_instr:1;
u8 has_pxp:1;
u8 has_sriov:1;
u8 needs_scratch:1;
u8 skip_guc_pc:1;
u8 skip_mtcfg:1;
u8 skip_pcode:1;
+ u8 needs_shared_vf_gt_wq:1;
};
struct xe_graphics_desc {
- u8 va_bits;
- u8 vm_max_level;
- u8 vram_flags;
-
u64 hw_engine_mask; /* hardware engines provided by graphics IP */
u8 has_asid:1;
u8 has_atomic_enable_pte_bit:1;
- u8 has_flat_ccs:1;
u8 has_indirect_ring_state:1;
u8 has_range_tlb_inval:1;
u8 has_usm:1;
diff --git a/drivers/gpu/drm/xe/xe_pcode.c b/drivers/gpu/drm/xe/xe_pcode.c
index 6a7ddb9005f9..0d33c14ea0cf 100644
--- a/drivers/gpu/drm/xe/xe_pcode.c
+++ b/drivers/gpu/drm/xe/xe_pcode.c
@@ -32,27 +32,39 @@
static int pcode_mailbox_status(struct xe_tile *tile)
{
+ const char *err_str;
+ int err_decode;
u32 err;
- static const struct pcode_err_decode err_decode[] = {
- [PCODE_ILLEGAL_CMD] = {-ENXIO, "Illegal Command"},
- [PCODE_TIMEOUT] = {-ETIMEDOUT, "Timed out"},
- [PCODE_ILLEGAL_DATA] = {-EINVAL, "Illegal Data"},
- [PCODE_ILLEGAL_SUBCOMMAND] = {-ENXIO, "Illegal Subcommand"},
- [PCODE_LOCKED] = {-EBUSY, "PCODE Locked"},
- [PCODE_GT_RATIO_OUT_OF_RANGE] = {-EOVERFLOW,
- "GT ratio out of range"},
- [PCODE_REJECTED] = {-EACCES, "PCODE Rejected"},
- [PCODE_ERROR_MASK] = {-EPROTO, "Unknown"},
- };
+
+#define CASE_ERR(_err, _err_decode, _err_str) \
+ case _err: \
+ err_decode = _err_decode; \
+ err_str = _err_str; \
+ break
err = xe_mmio_read32(&tile->mmio, PCODE_MAILBOX) & PCODE_ERROR_MASK;
+ switch (err) {
+ CASE_ERR(PCODE_ILLEGAL_CMD, -ENXIO, "Illegal Command");
+ CASE_ERR(PCODE_TIMEOUT, -ETIMEDOUT, "Timed out");
+ CASE_ERR(PCODE_ILLEGAL_DATA, -EINVAL, "Illegal Data");
+ CASE_ERR(PCODE_ILLEGAL_SUBCOMMAND, -ENXIO, "Illegal Subcommand");
+ CASE_ERR(PCODE_LOCKED, -EBUSY, "PCODE Locked");
+ CASE_ERR(PCODE_GT_RATIO_OUT_OF_RANGE, -EOVERFLOW, "GT ratio out of range");
+ CASE_ERR(PCODE_REJECTED, -EACCES, "PCODE Rejected");
+ default:
+ err_decode = -EPROTO;
+ err_str = "Unknown";
+ }
+
if (err) {
- drm_err(&tile_to_xe(tile)->drm, "PCODE Mailbox failed: %d %s", err,
- err_decode[err].str ?: "Unknown");
- return err_decode[err].errno ?: -EPROTO;
+ drm_err(&tile_to_xe(tile)->drm, "PCODE Mailbox failed: %d %s",
+ err_decode, err_str);
+
+ return err_decode;
}
return 0;
+#undef CASE_ERR
}
static int __pcode_mailbox_rw(struct xe_tile *tile, u32 mbox, u32 *data0, u32 *data1,
diff --git a/drivers/gpu/drm/xe/xe_pcode_api.h b/drivers/gpu/drm/xe/xe_pcode_api.h
index 92bfcba51e19..70dcd6625680 100644
--- a/drivers/gpu/drm/xe/xe_pcode_api.h
+++ b/drivers/gpu/drm/xe/xe_pcode_api.h
@@ -92,9 +92,3 @@
#define BMG_PCIE_CAP XE_REG(0x138340)
#define LINK_DOWNGRADE REG_GENMASK(1, 0)
#define DOWNGRADE_CAPABLE 2
-
-struct pcode_err_decode {
- int errno;
- const char *str;
-};
-
diff --git a/drivers/gpu/drm/xe/xe_platform_types.h b/drivers/gpu/drm/xe/xe_platform_types.h
index d08574c4cdb8..f516dbddfd88 100644
--- a/drivers/gpu/drm/xe/xe_platform_types.h
+++ b/drivers/gpu/drm/xe/xe_platform_types.h
@@ -24,6 +24,8 @@ enum xe_platform {
XE_LUNARLAKE,
XE_BATTLEMAGE,
XE_PANTHERLAKE,
+ XE_NOVALAKE_S,
+ XE_CRESCENTISLAND,
};
enum xe_subplatform {
@@ -34,6 +36,7 @@ enum xe_subplatform {
XE_SUBPLATFORM_DG2_G10,
XE_SUBPLATFORM_DG2_G11,
XE_SUBPLATFORM_DG2_G12,
+ XE_SUBPLATFORM_BATTLEMAGE_G21,
};
#endif
diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
index 2c5a44377994..766922530265 100644
--- a/drivers/gpu/drm/xe/xe_pm.c
+++ b/drivers/gpu/drm/xe/xe_pm.c
@@ -83,8 +83,58 @@ static struct lockdep_map xe_pm_runtime_d3cold_map = {
static struct lockdep_map xe_pm_runtime_nod3cold_map = {
.name = "xe_rpm_nod3cold_map"
};
+
+static struct lockdep_map xe_pm_block_lockdep_map = {
+ .name = "xe_pm_block_map",
+};
#endif
+static void xe_pm_block_begin_signalling(void)
+{
+ lock_acquire_shared_recursive(&xe_pm_block_lockdep_map, 0, 1, NULL, _RET_IP_);
+}
+
+static void xe_pm_block_end_signalling(void)
+{
+ lock_release(&xe_pm_block_lockdep_map, _RET_IP_);
+}
+
+/**
+ * xe_pm_might_block_on_suspend() - Annotate that the code might block on suspend
+ *
+ * Annotation to use where the code might block or seize to make
+ * progress pending resume completion.
+ */
+void xe_pm_might_block_on_suspend(void)
+{
+ lock_map_acquire(&xe_pm_block_lockdep_map);
+ lock_map_release(&xe_pm_block_lockdep_map);
+}
+
+/**
+ * xe_pm_block_on_suspend() - Block pending suspend.
+ * @xe: The xe device about to be suspended.
+ *
+ * Block if the pm notifier has start evicting bos, to avoid
+ * racing and validating those bos back. The function is
+ * annotated to ensure no locks are held that are also grabbed
+ * in the pm notifier or the device suspend / resume.
+ * This is intended to be used by freezable tasks only.
+ * (Not freezable workqueues), with the intention that the function
+ * returns %-ERESTARTSYS when tasks are frozen during suspend,
+ * and allows the task to freeze. The caller must be able to
+ * handle the %-ERESTARTSYS.
+ *
+ * Return: %0 on success, %-ERESTARTSYS on signal pending or
+ * if freezing requested.
+ */
+int xe_pm_block_on_suspend(struct xe_device *xe)
+{
+ xe_pm_might_block_on_suspend();
+
+ return wait_for_completion_interruptible(&xe->pm_block);
+}
+
/**
* xe_rpm_reclaim_safe() - Whether runtime resume can be done from reclaim context
* @xe: The xe device.
@@ -124,6 +174,7 @@ int xe_pm_suspend(struct xe_device *xe)
int err;
drm_dbg(&xe->drm, "Suspending device\n");
+ xe_pm_block_begin_signalling();
trace_xe_pm_suspend(xe, __builtin_return_address(0));
err = xe_pxp_pm_suspend(xe->pxp);
@@ -155,6 +206,8 @@ int xe_pm_suspend(struct xe_device *xe)
xe_i2c_pm_suspend(xe);
drm_dbg(&xe->drm, "Device suspended\n");
+ xe_pm_block_end_signalling();
+
return 0;
err_display:
@@ -162,6 +215,7 @@ err_display:
xe_pxp_pm_resume(xe->pxp);
err:
drm_dbg(&xe->drm, "Device suspend failed %d\n", err);
+ xe_pm_block_end_signalling();
return err;
}
@@ -178,6 +232,7 @@ int xe_pm_resume(struct xe_device *xe)
u8 id;
int err;
+ xe_pm_block_begin_signalling();
drm_dbg(&xe->drm, "Resuming device\n");
trace_xe_pm_resume(xe, __builtin_return_address(0));
@@ -222,9 +277,11 @@ int xe_pm_resume(struct xe_device *xe)
xe_late_bind_fw_load(&xe->late_bind);
drm_dbg(&xe->drm, "Device resumed\n");
+ xe_pm_block_end_signalling();
return 0;
err:
drm_dbg(&xe->drm, "Device resume failed %d\n", err);
+ xe_pm_block_end_signalling();
return err;
}
@@ -329,9 +386,16 @@ static int xe_pm_notifier_callback(struct notifier_block *nb,
switch (action) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
+ {
+ struct xe_validation_ctx ctx;
+
reinit_completion(&xe->pm_block);
+ xe_pm_block_begin_signalling();
xe_pm_runtime_get(xe);
+ (void)xe_validation_ctx_init(&ctx, &xe->val, NULL,
+ (struct xe_val_flags) {.exclusive = true});
err = xe_bo_evict_all_user(xe);
+ xe_validation_ctx_fini(&ctx);
if (err)
drm_dbg(&xe->drm, "Notifier evict user failed (%d)\n", err);
@@ -343,7 +407,9 @@ static int xe_pm_notifier_callback(struct notifier_block *nb,
* avoid a runtime suspend interfering with evicted objects or backup
* allocations.
*/
+ xe_pm_block_end_signalling();
break;
+ }
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
complete_all(&xe->pm_block);
@@ -660,6 +726,13 @@ static void xe_pm_runtime_lockdep_prime(void)
/**
* xe_pm_runtime_get - Get a runtime_pm reference and resume synchronously
* @xe: xe device instance
+ *
+ * When possible, scope-based runtime PM (through guard(xe_pm_runtime)) is
+ * be preferred over direct usage of this function. Manual get/put handling
+ * should only be used when the function contains goto-based logic which
+ * can break scope-based handling, or when the lifetime of the runtime PM
+ * reference does not match a specific scope (e.g., runtime PM obtained in one
+ * function and released in a different one).
*/
void xe_pm_runtime_get(struct xe_device *xe)
{
@@ -692,6 +765,13 @@ void xe_pm_runtime_put(struct xe_device *xe)
* xe_pm_runtime_get_ioctl - Get a runtime_pm reference before ioctl
* @xe: xe device instance
*
+ * When possible, scope-based runtime PM (through
+ * ACQUIRE(xe_pm_runtime_ioctl, ...)) is be preferred over direct usage of this
+ * function. Manual get/put handling should only be used when the function
+ * contains goto-based logic which can break scope-based handling, or when the
+ * lifetime of the runtime PM reference does not match a specific scope (e.g.,
+ * runtime PM obtained in one function and released in a different one).
+ *
* Returns: Any number greater than or equal to 0 for success, negative error
* code otherwise.
*/
@@ -761,6 +841,13 @@ static bool xe_pm_suspending_or_resuming(struct xe_device *xe)
* It will warn if not protected.
* The reference should be put back after this function regardless, since it
* will always bump the usage counter, regardless.
+ *
+ * When possible, scope-based runtime PM (through guard(xe_pm_runtime_noresume))
+ * is be preferred over direct usage of this function. Manual get/put handling
+ * should only be used when the function contains goto-based logic which can
+ * break scope-based handling, or when the lifetime of the runtime PM reference
+ * does not match a specific scope (e.g., runtime PM obtained in one function
+ * and released in a different one).
*/
void xe_pm_runtime_get_noresume(struct xe_device *xe)
{
diff --git a/drivers/gpu/drm/xe/xe_pm.h b/drivers/gpu/drm/xe/xe_pm.h
index 59678b310e55..6b27039e7b2d 100644
--- a/drivers/gpu/drm/xe/xe_pm.h
+++ b/drivers/gpu/drm/xe/xe_pm.h
@@ -6,6 +6,7 @@
#ifndef _XE_PM_H_
#define _XE_PM_H_
+#include <linux/cleanup.h>
#include <linux/pm_runtime.h>
#define DEFAULT_VRAM_THRESHOLD 300 /* in MB */
@@ -33,6 +34,24 @@ int xe_pm_set_vram_threshold(struct xe_device *xe, u32 threshold);
void xe_pm_d3cold_allowed_toggle(struct xe_device *xe);
bool xe_rpm_reclaim_safe(const struct xe_device *xe);
struct task_struct *xe_pm_read_callback_task(struct xe_device *xe);
+int xe_pm_block_on_suspend(struct xe_device *xe);
+void xe_pm_might_block_on_suspend(void);
int xe_pm_module_init(void);
+static inline void __xe_pm_runtime_noop(struct xe_device *xe) {}
+
+DEFINE_GUARD(xe_pm_runtime, struct xe_device *,
+ xe_pm_runtime_get(_T), xe_pm_runtime_put(_T))
+DEFINE_GUARD(xe_pm_runtime_noresume, struct xe_device *,
+ xe_pm_runtime_get_noresume(_T), xe_pm_runtime_put(_T))
+DEFINE_GUARD_COND(xe_pm_runtime, _ioctl, xe_pm_runtime_get_ioctl(_T), _RET >= 0)
+
+/*
+ * Used when a function needs to release runtime PM in all possible cases
+ * and error paths, but the wakeref was already acquired by a different
+ * function (i.e., get() has already happened so only a put() is needed).
+ */
+DEFINE_GUARD(xe_pm_runtime_release_only, struct xe_device *,
+ __xe_pm_runtime_noop(_T), xe_pm_runtime_put(_T));
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_pmu.c b/drivers/gpu/drm/xe/xe_pmu.c
index cab51d826345..c63335eb69e5 100644
--- a/drivers/gpu/drm/xe/xe_pmu.c
+++ b/drivers/gpu/drm/xe/xe_pmu.c
@@ -497,7 +497,12 @@ static const struct attribute_group *pmu_events_attr_update[] = {
static void set_supported_events(struct xe_pmu *pmu)
{
struct xe_device *xe = container_of(pmu, typeof(*xe), pmu);
- struct xe_gt *gt = xe_device_get_gt(xe, 0);
+ struct xe_gt *gt;
+ int id;
+
+ /* If there are no GTs, don't support any GT-related events */
+ if (xe->info.gt_count == 0)
+ return;
if (!xe->info.skip_guc_pc) {
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_GT_C6_RESIDENCY);
@@ -505,6 +510,10 @@ static void set_supported_events(struct xe_pmu *pmu)
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_GT_REQUESTED_FREQUENCY);
}
+ /* Find the first available GT to query engine event capabilities */
+ for_each_gt(gt, xe, id)
+ break;
+
if (xe_guc_engine_activity_supported(&gt->uc.guc)) {
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_ENGINE_ACTIVE_TICKS);
pmu->supported_events |= BIT_ULL(XE_PMU_EVENT_ENGINE_TOTAL_TICKS);
diff --git a/drivers/gpu/drm/xe/xe_preempt_fence.c b/drivers/gpu/drm/xe/xe_preempt_fence.c
index 83fbeea5aa20..7f587ca3947d 100644
--- a/drivers/gpu/drm/xe/xe_preempt_fence.c
+++ b/drivers/gpu/drm/xe/xe_preempt_fence.c
@@ -8,6 +8,8 @@
#include <linux/slab.h>
#include "xe_exec_queue.h"
+#include "xe_gt_printk.h"
+#include "xe_guc_exec_queue_types.h"
#include "xe_vm.h"
static void preempt_fence_work_func(struct work_struct *w)
@@ -22,6 +24,15 @@ static void preempt_fence_work_func(struct work_struct *w)
} else if (!q->ops->reset_status(q)) {
int err = q->ops->suspend_wait(q);
+ if (err == -EAGAIN) {
+ xe_gt_dbg(q->gt, "PREEMPT FENCE RETRY guc_id=%d",
+ q->guc->id);
+ queue_work(q->vm->xe->preempt_fence_wq,
+ &pfence->preempt_work);
+ dma_fence_end_signalling(cookie);
+ return;
+ }
+
if (err)
dma_fence_set_error(&pfence->base, err);
} else {
diff --git a/drivers/gpu/drm/xe/xe_preempt_fence_types.h b/drivers/gpu/drm/xe/xe_preempt_fence_types.h
index 312c3372a49f..ac125c697a41 100644
--- a/drivers/gpu/drm/xe/xe_preempt_fence_types.h
+++ b/drivers/gpu/drm/xe/xe_preempt_fence_types.h
@@ -12,7 +12,7 @@
struct xe_exec_queue;
/**
- * struct xe_preempt_fence - XE preempt fence
+ * struct xe_preempt_fence - Xe preempt fence
*
* hardware and triggers a callback once the xe_engine is complete.
*/
diff --git a/drivers/gpu/drm/xe/xe_psmi.c b/drivers/gpu/drm/xe/xe_psmi.c
index 45d142191d60..6a54e38b81ba 100644
--- a/drivers/gpu/drm/xe/xe_psmi.c
+++ b/drivers/gpu/drm/xe/xe_psmi.c
@@ -70,8 +70,8 @@ static struct xe_bo *psmi_alloc_object(struct xe_device *xe,
{
struct xe_tile *tile;
- if (!id || !bo_size)
- return NULL;
+ xe_assert(xe, id);
+ xe_assert(xe, bo_size);
tile = &xe->tiles[id - 1];
diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c
index a1c88f9a6c76..884127b4d97d 100644
--- a/drivers/gpu/drm/xe/xe_pt.c
+++ b/drivers/gpu/drm/xe/xe_pt.c
@@ -3,8 +3,6 @@
* Copyright © 2022 Intel Corporation
*/
-#include <linux/dma-fence-array.h>
-
#include "xe_pt.h"
#include "regs/xe_gtt_defs.h"
@@ -122,7 +120,7 @@ struct xe_pt *xe_pt_create(struct xe_vm *vm, struct xe_tile *tile,
XE_BO_FLAG_IGNORE_MIN_PAGE_SIZE |
XE_BO_FLAG_NO_RESV_EVICT | XE_BO_FLAG_PAGETABLE;
if (vm->xef) /* userspace */
- bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE;
+ bo_flags |= XE_BO_FLAG_PINNED_LATE_RESTORE | XE_BO_FLAG_FORCE_USER_VRAM;
pt->level = level;
@@ -715,7 +713,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
.vm = vm,
.tile = tile,
.curs = &curs,
- .va_curs_start = range ? range->base.itree.start :
+ .va_curs_start = range ? xe_svm_range_start(range) :
xe_vma_start(vma),
.vma = vma,
.wupd.entries = entries,
@@ -734,7 +732,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
}
if (xe_svm_range_has_dma_mapping(range)) {
xe_res_first_dma(range->base.pages.dma_addr, 0,
- range->base.itree.last + 1 - range->base.itree.start,
+ xe_svm_range_size(range),
&curs);
xe_svm_range_debug(range, "BIND PREPARE - MIXED");
} else {
@@ -778,8 +776,8 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma,
walk_pt:
ret = xe_pt_walk_range(&pt->base, pt->level,
- range ? range->base.itree.start : xe_vma_start(vma),
- range ? range->base.itree.last + 1 : xe_vma_end(vma),
+ range ? xe_svm_range_start(range) : xe_vma_start(vma),
+ range ? xe_svm_range_end(range) : xe_vma_end(vma),
&xe_walk.base);
*num_entries = xe_walk.wupd.num_used_entries;
@@ -975,8 +973,8 @@ bool xe_pt_zap_ptes_range(struct xe_tile *tile, struct xe_vm *vm,
if (!(pt_mask & BIT(tile->id)))
return false;
- (void)xe_pt_walk_shared(&pt->base, pt->level, range->base.itree.start,
- range->base.itree.last + 1, &xe_walk.base);
+ (void)xe_pt_walk_shared(&pt->base, pt->level, xe_svm_range_start(range),
+ xe_svm_range_end(range), &xe_walk.base);
return xe_walk.needs_invalidate;
}
@@ -1340,13 +1338,6 @@ static int xe_pt_vm_dependencies(struct xe_sched_job *job,
return err;
}
- if (!(pt_update_ops->q->flags & EXEC_QUEUE_FLAG_KERNEL)) {
- if (job)
- err = xe_sched_job_last_fence_add_dep(job, vm);
- else
- err = xe_exec_queue_last_fence_test_dep(pt_update_ops->q, vm);
- }
-
for (i = 0; job && !err && i < vops->num_syncs; i++)
err = xe_sync_entry_add_deps(&vops->syncs[i], job);
@@ -1661,8 +1652,8 @@ static unsigned int xe_pt_stage_unbind(struct xe_tile *tile,
struct xe_svm_range *range,
struct xe_vm_pgtable_update *entries)
{
- u64 start = range ? range->base.itree.start : xe_vma_start(vma);
- u64 end = range ? range->base.itree.last + 1 : xe_vma_end(vma);
+ u64 start = range ? xe_svm_range_start(range) : xe_vma_start(vma);
+ u64 end = range ? xe_svm_range_end(range) : xe_vma_end(vma);
struct xe_pt_stage_unbind_walk xe_walk = {
.base = {
.ops = &xe_pt_stage_unbind_ops,
@@ -1872,7 +1863,7 @@ static int bind_range_prepare(struct xe_vm *vm, struct xe_tile *tile,
vm_dbg(&xe_vma_vm(vma)->xe->drm,
"Preparing bind, with range [%lx...%lx)\n",
- range->base.itree.start, range->base.itree.last);
+ xe_svm_range_start(range), xe_svm_range_end(range) - 1);
pt_op->vma = NULL;
pt_op->bind = true;
@@ -1887,8 +1878,8 @@ static int bind_range_prepare(struct xe_vm *vm, struct xe_tile *tile,
pt_op->num_entries, true);
xe_pt_update_ops_rfence_interval(pt_update_ops,
- range->base.itree.start,
- range->base.itree.last + 1);
+ xe_svm_range_start(range),
+ xe_svm_range_end(range));
++pt_update_ops->current_op;
pt_update_ops->needs_svm_lock = true;
@@ -1983,7 +1974,7 @@ static int unbind_range_prepare(struct xe_vm *vm,
vm_dbg(&vm->xe->drm,
"Preparing unbind, with range [%lx...%lx)\n",
- range->base.itree.start, range->base.itree.last);
+ xe_svm_range_start(range), xe_svm_range_end(range) - 1);
pt_op->vma = XE_INVALID_VMA;
pt_op->bind = false;
@@ -1994,8 +1985,8 @@ static int unbind_range_prepare(struct xe_vm *vm,
xe_vm_dbg_print_entries(tile_to_xe(tile), pt_op->entries,
pt_op->num_entries, false);
- xe_pt_update_ops_rfence_interval(pt_update_ops, range->base.itree.start,
- range->base.itree.last + 1);
+ xe_pt_update_ops_rfence_interval(pt_update_ops, xe_svm_range_start(range),
+ xe_svm_range_end(range));
++pt_update_ops->current_op;
pt_update_ops->needs_svm_lock = true;
pt_update_ops->needs_invalidation |= xe_vm_has_scratch(vm) ||
@@ -2022,7 +2013,7 @@ static int op_prepare(struct xe_vm *vm,
case DRM_GPUVA_OP_MAP:
if ((!op->map.immediate && xe_vm_in_fault_mode(vm) &&
!op->map.invalidate_on_bind) ||
- op->map.is_cpu_addr_mirror)
+ (op->map.vma_flags & XE_VMA_SYSTEM_ALLOCATOR))
break;
err = bind_op_prepare(vm, tile, pt_update_ops, op->map.vma,
@@ -2252,7 +2243,7 @@ static void op_commit(struct xe_vm *vm,
switch (op->base.op) {
case DRM_GPUVA_OP_MAP:
if ((!op->map.immediate && xe_vm_in_fault_mode(vm)) ||
- op->map.is_cpu_addr_mirror)
+ (op->map.vma_flags & XE_VMA_SYSTEM_ALLOCATOR))
break;
bind_op_commit(vm, tile, pt_update_ops, op->map.vma, fence,
@@ -2359,10 +2350,9 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
struct xe_vm *vm = vops->vm;
struct xe_vm_pgtable_update_ops *pt_update_ops =
&vops->pt_update_ops[tile->id];
- struct dma_fence *fence, *ifence, *mfence;
+ struct xe_exec_queue *q = pt_update_ops->q;
+ struct dma_fence *fence, *ifence = NULL, *mfence = NULL;
struct xe_tlb_inval_job *ijob = NULL, *mjob = NULL;
- struct dma_fence **fences = NULL;
- struct dma_fence_array *cf = NULL;
struct xe_range_fence *rfence;
struct xe_vma_op *op;
int err = 0, i;
@@ -2390,15 +2380,14 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
#endif
if (pt_update_ops->needs_invalidation) {
- struct xe_exec_queue *q = pt_update_ops->q;
struct xe_dep_scheduler *dep_scheduler =
to_dep_scheduler(q, tile->primary_gt);
ijob = xe_tlb_inval_job_create(q, &tile->primary_gt->tlb_inval,
- dep_scheduler,
+ dep_scheduler, vm,
pt_update_ops->start,
pt_update_ops->last,
- vm->usm.asid);
+ XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
if (IS_ERR(ijob)) {
err = PTR_ERR(ijob);
goto kill_vm_tile1;
@@ -2410,26 +2399,15 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
mjob = xe_tlb_inval_job_create(q,
&tile->media_gt->tlb_inval,
- dep_scheduler,
+ dep_scheduler, vm,
pt_update_ops->start,
pt_update_ops->last,
- vm->usm.asid);
+ XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT);
if (IS_ERR(mjob)) {
err = PTR_ERR(mjob);
goto free_ijob;
}
update.mjob = mjob;
-
- fences = kmalloc_array(2, sizeof(*fences), GFP_KERNEL);
- if (!fences) {
- err = -ENOMEM;
- goto free_ijob;
- }
- cf = dma_fence_array_alloc(2);
- if (!cf) {
- err = -ENOMEM;
- goto free_ijob;
- }
}
}
@@ -2460,31 +2438,12 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
pt_update_ops->last, fence))
dma_fence_wait(fence, false);
- /* tlb invalidation must be done before signaling unbind/rebind */
- if (ijob) {
- struct dma_fence *__fence;
-
+ if (ijob)
ifence = xe_tlb_inval_job_push(ijob, tile->migrate, fence);
- __fence = ifence;
-
- if (mjob) {
- fences[0] = ifence;
- mfence = xe_tlb_inval_job_push(mjob, tile->migrate,
- fence);
- fences[1] = mfence;
-
- dma_fence_array_init(cf, 2, fences,
- vm->composite_fence_ctx,
- vm->composite_fence_seqno++,
- false);
- __fence = &cf->base;
- }
-
- dma_fence_put(fence);
- fence = __fence;
- }
+ if (mjob)
+ mfence = xe_tlb_inval_job_push(mjob, tile->migrate, fence);
- if (!mjob) {
+ if (!mjob && !ijob) {
dma_resv_add_fence(xe_vm_resv(vm), fence,
pt_update_ops->wait_vm_bookkeep ?
DMA_RESV_USAGE_KERNEL :
@@ -2492,6 +2451,14 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
list_for_each_entry(op, &vops->list, link)
op_commit(vops->vm, tile, pt_update_ops, op, fence, NULL);
+ } else if (ijob && !mjob) {
+ dma_resv_add_fence(xe_vm_resv(vm), ifence,
+ pt_update_ops->wait_vm_bookkeep ?
+ DMA_RESV_USAGE_KERNEL :
+ DMA_RESV_USAGE_BOOKKEEP);
+
+ list_for_each_entry(op, &vops->list, link)
+ op_commit(vops->vm, tile, pt_update_ops, op, ifence, NULL);
} else {
dma_resv_add_fence(xe_vm_resv(vm), ifence,
pt_update_ops->wait_vm_bookkeep ?
@@ -2511,16 +2478,23 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops)
if (pt_update_ops->needs_svm_lock)
xe_svm_notifier_unlock(vm);
+ /*
+ * The last fence is only used for zero bind queue idling; migrate
+ * queues are not exposed to user space.
+ */
+ if (!(q->flags & EXEC_QUEUE_FLAG_MIGRATE))
+ xe_exec_queue_last_fence_set(q, vm, fence);
+
xe_tlb_inval_job_put(mjob);
xe_tlb_inval_job_put(ijob);
+ dma_fence_put(ifence);
+ dma_fence_put(mfence);
return fence;
free_rfence:
kfree(rfence);
free_ijob:
- kfree(cf);
- kfree(fences);
xe_tlb_inval_job_put(mjob);
xe_tlb_inval_job_put(ijob);
kill_vm_tile1:
diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c
index 2e9ff33ed2fe..1c0915e2cc16 100644
--- a/drivers/gpu/drm/xe/xe_query.c
+++ b/drivers/gpu/drm/xe/xe_query.c
@@ -436,7 +436,7 @@ static int query_hwconfig(struct xe_device *xe,
struct drm_xe_device_query *query)
{
struct xe_gt *gt = xe_root_mmio_gt(xe);
- size_t size = xe_guc_hwconfig_size(&gt->uc.guc);
+ size_t size = gt ? xe_guc_hwconfig_size(&gt->uc.guc) : 0;
void __user *query_ptr = u64_to_user_ptr(query->data);
void *hwconfig;
diff --git a/drivers/gpu/drm/xe/xe_range_fence.h b/drivers/gpu/drm/xe/xe_range_fence.h
index edd58b34f5c0..4934729dd904 100644
--- a/drivers/gpu/drm/xe/xe_range_fence.h
+++ b/drivers/gpu/drm/xe/xe_range_fence.h
@@ -13,13 +13,13 @@
struct xe_range_fence_tree;
struct xe_range_fence;
-/** struct xe_range_fence_ops - XE range fence ops */
+/** struct xe_range_fence_ops - Xe range fence ops */
struct xe_range_fence_ops {
/** @free: free range fence op */
void (*free)(struct xe_range_fence *rfence);
};
-/** struct xe_range_fence - XE range fence (address conflict tracking) */
+/** struct xe_range_fence - Xe range fence (address conflict tracking) */
struct xe_range_fence {
/** @rb: RB tree node inserted into interval tree */
struct rb_node rb;
diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c
index 23f6c81d9994..7ca360b2c20d 100644
--- a/drivers/gpu/drm/xe/xe_reg_whitelist.c
+++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c
@@ -19,7 +19,8 @@
#undef XE_REG_MCR
#define XE_REG_MCR(...) XE_REG(__VA_ARGS__, .mcr = 1)
-static bool match_not_render(const struct xe_gt *gt,
+static bool match_not_render(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
return hwe->class != XE_ENGINE_CLASS_RENDER;
@@ -88,6 +89,13 @@ static const struct xe_rtp_entry_sr register_whitelist[] = {
RING_FORCE_TO_NONPRIV_ACCESS_RD |
RING_FORCE_TO_NONPRIV_RANGE_4))
},
+ { XE_RTP_NAME("14024997852"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(WHITELIST(FF_MODE,
+ RING_FORCE_TO_NONPRIV_ACCESS_RW),
+ WHITELIST(VFLSKPD,
+ RING_FORCE_TO_NONPRIV_ACCESS_RW))
+ },
};
static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe)
diff --git a/drivers/gpu/drm/xe/xe_ring_ops.c b/drivers/gpu/drm/xe/xe_ring_ops.c
index d71837773d6c..ac0c6dcffe15 100644
--- a/drivers/gpu/drm/xe/xe_ring_ops.c
+++ b/drivers/gpu/drm/xe/xe_ring_ops.c
@@ -245,12 +245,14 @@ static int emit_copy_timestamp(struct xe_lrc *lrc, u32 *dw, int i)
/* for engines that don't require any special HW handling (no EUs, no aux inval, etc) */
static void __emit_job_gen12_simple(struct xe_sched_job *job, struct xe_lrc *lrc,
- u64 batch_addr, u32 seqno)
+ u64 batch_addr, u32 *head, u32 seqno)
{
u32 dw[MAX_JOB_SIZE_DW], i = 0;
u32 ppgtt_flag = get_ppgtt_flag(job);
struct xe_gt *gt = job->q->gt;
+ *head = lrc->ring.tail;
+
i = emit_copy_timestamp(lrc, dw, i);
if (job->ring_ops_flush_tlb) {
@@ -296,7 +298,7 @@ static bool has_aux_ccs(struct xe_device *xe)
}
static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
- u64 batch_addr, u32 seqno)
+ u64 batch_addr, u32 *head, u32 seqno)
{
u32 dw[MAX_JOB_SIZE_DW], i = 0;
u32 ppgtt_flag = get_ppgtt_flag(job);
@@ -304,6 +306,8 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
struct xe_device *xe = gt_to_xe(gt);
bool decode = job->q->class == XE_ENGINE_CLASS_VIDEO_DECODE;
+ *head = lrc->ring.tail;
+
i = emit_copy_timestamp(lrc, dw, i);
dw[i++] = preparser_disable(true);
@@ -346,7 +350,8 @@ static void __emit_job_gen12_video(struct xe_sched_job *job, struct xe_lrc *lrc,
static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
struct xe_lrc *lrc,
- u64 batch_addr, u32 seqno)
+ u64 batch_addr, u32 *head,
+ u32 seqno)
{
u32 dw[MAX_JOB_SIZE_DW], i = 0;
u32 ppgtt_flag = get_ppgtt_flag(job);
@@ -355,6 +360,8 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
bool lacks_render = !(gt->info.engine_mask & XE_HW_ENGINE_RCS_MASK);
u32 mask_flags = 0;
+ *head = lrc->ring.tail;
+
i = emit_copy_timestamp(lrc, dw, i);
dw[i++] = preparser_disable(true);
@@ -396,11 +403,14 @@ static void __emit_job_gen12_render_compute(struct xe_sched_job *job,
}
static void emit_migration_job_gen12(struct xe_sched_job *job,
- struct xe_lrc *lrc, u32 seqno)
+ struct xe_lrc *lrc, u32 *head,
+ u32 seqno)
{
u32 saddr = xe_lrc_start_seqno_ggtt_addr(lrc);
u32 dw[MAX_JOB_SIZE_DW], i = 0;
+ *head = lrc->ring.tail;
+
i = emit_copy_timestamp(lrc, dw, i);
i = emit_store_imm_ggtt(saddr, seqno, dw, i);
@@ -434,6 +444,7 @@ static void emit_job_gen12_gsc(struct xe_sched_job *job)
__emit_job_gen12_simple(job, job->q->lrc[0],
job->ptrs[0].batch_addr,
+ &job->ptrs[0].head,
xe_sched_job_lrc_seqno(job));
}
@@ -443,6 +454,7 @@ static void emit_job_gen12_copy(struct xe_sched_job *job)
if (xe_sched_job_is_migration(job->q)) {
emit_migration_job_gen12(job, job->q->lrc[0],
+ &job->ptrs[0].head,
xe_sched_job_lrc_seqno(job));
return;
}
@@ -450,6 +462,7 @@ static void emit_job_gen12_copy(struct xe_sched_job *job)
for (i = 0; i < job->q->width; ++i)
__emit_job_gen12_simple(job, job->q->lrc[i],
job->ptrs[i].batch_addr,
+ &job->ptrs[i].head,
xe_sched_job_lrc_seqno(job));
}
@@ -461,6 +474,7 @@ static void emit_job_gen12_video(struct xe_sched_job *job)
for (i = 0; i < job->q->width; ++i)
__emit_job_gen12_video(job, job->q->lrc[i],
job->ptrs[i].batch_addr,
+ &job->ptrs[i].head,
xe_sched_job_lrc_seqno(job));
}
@@ -471,6 +485,7 @@ static void emit_job_gen12_render_compute(struct xe_sched_job *job)
for (i = 0; i < job->q->width; ++i)
__emit_job_gen12_render_compute(job, job->q->lrc[i],
job->ptrs[i].batch_addr,
+ &job->ptrs[i].head,
xe_sched_job_lrc_seqno(job));
}
diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c
index b5f430d59f80..ed509b1c8cfc 100644
--- a/drivers/gpu/drm/xe/xe_rtp.c
+++ b/drivers/gpu/drm/xe/xe_rtp.c
@@ -133,10 +133,7 @@ static bool rule_matches(const struct xe_device *xe,
match = hwe->class != r->engine_class;
break;
case XE_RTP_MATCH_FUNC:
- if (drm_WARN_ON(&xe->drm, !gt))
- return false;
-
- match = r->match_func(gt, hwe);
+ match = r->match_func(xe, gt, hwe);
break;
default:
drm_warn(&xe->drm, "Invalid RTP match %u\n",
@@ -343,13 +340,15 @@ void xe_rtp_process(struct xe_rtp_process_ctx *ctx,
}
EXPORT_SYMBOL_IF_KUNIT(xe_rtp_process);
-bool xe_rtp_match_even_instance(const struct xe_gt *gt,
+bool xe_rtp_match_even_instance(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
return hwe->instance % 2 == 0;
}
-bool xe_rtp_match_first_render_or_compute(const struct xe_gt *gt,
+bool xe_rtp_match_first_render_or_compute(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
u64 render_compute_mask = gt->info.engine_mask &
@@ -359,20 +358,30 @@ bool xe_rtp_match_first_render_or_compute(const struct xe_gt *gt,
hwe->engine_id == __ffs(render_compute_mask);
}
-bool xe_rtp_match_not_sriov_vf(const struct xe_gt *gt,
+bool xe_rtp_match_not_sriov_vf(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
- return !IS_SRIOV_VF(gt_to_xe(gt));
+ return !IS_SRIOV_VF(xe);
}
-bool xe_rtp_match_psmi_enabled(const struct xe_gt *gt,
+bool xe_rtp_match_psmi_enabled(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
- return xe_configfs_get_psmi_enabled(to_pci_dev(gt_to_xe(gt)->drm.dev));
+ return xe_configfs_get_psmi_enabled(to_pci_dev(xe->drm.dev));
}
-bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_gt *gt,
+bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe)
{
return xe_gt_has_discontiguous_dss_groups(gt);
}
+
+bool xe_rtp_match_has_flat_ccs(const struct xe_device *xe,
+ const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe)
+{
+ return xe->info.has_flat_ccs;
+}
diff --git a/drivers/gpu/drm/xe/xe_rtp.h b/drivers/gpu/drm/xe/xe_rtp.h
index ac12ddf6cde6..ba5f940c0a96 100644
--- a/drivers/gpu/drm/xe/xe_rtp.h
+++ b/drivers/gpu/drm/xe/xe_rtp.h
@@ -440,18 +440,21 @@ void xe_rtp_process(struct xe_rtp_process_ctx *ctx,
/**
* xe_rtp_match_even_instance - Match if engine instance is even
+ * @xe: Device structure
* @gt: GT structure
* @hwe: Engine instance
*
* Returns: true if engine instance is even, false otherwise
*/
-bool xe_rtp_match_even_instance(const struct xe_gt *gt,
+bool xe_rtp_match_even_instance(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
/*
* xe_rtp_match_first_render_or_compute - Match if it's first render or compute
* engine in the GT
*
+ * @xe: Device structure
* @gt: GT structure
* @hwe: Engine instance
*
@@ -463,24 +466,41 @@ bool xe_rtp_match_even_instance(const struct xe_gt *gt,
* Returns: true if engine id is the first to match the render reset domain,
* false otherwise.
*/
-bool xe_rtp_match_first_render_or_compute(const struct xe_gt *gt,
+bool xe_rtp_match_first_render_or_compute(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
/*
* xe_rtp_match_not_sriov_vf - Match when not on SR-IOV VF device
*
+ * @xe: Device structure
* @gt: GT structure
* @hwe: Engine instance
*
* Returns: true if device is not VF, false otherwise.
*/
-bool xe_rtp_match_not_sriov_vf(const struct xe_gt *gt,
+bool xe_rtp_match_not_sriov_vf(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
-bool xe_rtp_match_psmi_enabled(const struct xe_gt *gt,
+bool xe_rtp_match_psmi_enabled(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
-bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_gt *gt,
+bool xe_rtp_match_gt_has_discontiguous_dss_groups(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
+/**
+ * xe_rtp_match_has_flat_ccs - Match when platform has FlatCCS compression
+ * @xe: Device structure
+ * @gt: GT structure
+ * @hwe: Engine instance
+ *
+ * Returns: true if platform has FlatCCS compression, false otherwise
+ */
+bool xe_rtp_match_has_flat_ccs(const struct xe_device *xe,
+ const struct xe_gt *gt,
+ const struct xe_hw_engine *hwe);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_rtp_types.h b/drivers/gpu/drm/xe/xe_rtp_types.h
index f4cf30e298cf..6ba7f226c227 100644
--- a/drivers/gpu/drm/xe/xe_rtp_types.h
+++ b/drivers/gpu/drm/xe/xe_rtp_types.h
@@ -10,6 +10,7 @@
#include "regs/xe_reg_defs.h"
+struct xe_device;
struct xe_hw_engine;
struct xe_gt;
@@ -86,7 +87,8 @@ struct xe_rtp_rule {
u8 engine_class;
};
/* MATCH_FUNC */
- bool (*match_func)(const struct xe_gt *gt,
+ bool (*match_func)(const struct xe_device *xe,
+ const struct xe_gt *gt,
const struct xe_hw_engine *hwe);
};
};
diff --git a/drivers/gpu/drm/xe/xe_sa.c b/drivers/gpu/drm/xe/xe_sa.c
index fedd017d6dd3..63a5263dcf1b 100644
--- a/drivers/gpu/drm/xe/xe_sa.c
+++ b/drivers/gpu/drm/xe/xe_sa.c
@@ -110,6 +110,10 @@ struct drm_suballoc *__xe_sa_bo_new(struct xe_sa_manager *sa_manager, u32 size,
return drm_suballoc_new(&sa_manager->base, size, gfp, true, 0);
}
+/**
+ * xe_sa_bo_flush_write() - Copy the data from the sub-allocation to the GPU memory.
+ * @sa_bo: the &drm_suballoc to flush
+ */
void xe_sa_bo_flush_write(struct drm_suballoc *sa_bo)
{
struct xe_sa_manager *sa_manager = to_xe_sa_manager(sa_bo->manager);
@@ -123,6 +127,23 @@ void xe_sa_bo_flush_write(struct drm_suballoc *sa_bo)
drm_suballoc_size(sa_bo));
}
+/**
+ * xe_sa_bo_sync_read() - Copy the data from GPU memory to the sub-allocation.
+ * @sa_bo: the &drm_suballoc to sync
+ */
+void xe_sa_bo_sync_read(struct drm_suballoc *sa_bo)
+{
+ struct xe_sa_manager *sa_manager = to_xe_sa_manager(sa_bo->manager);
+ struct xe_device *xe = tile_to_xe(sa_manager->bo->tile);
+
+ if (!sa_manager->bo->vmap.is_iomem)
+ return;
+
+ xe_map_memcpy_from(xe, xe_sa_bo_cpu_addr(sa_bo), &sa_manager->bo->vmap,
+ drm_suballoc_soffset(sa_bo),
+ drm_suballoc_size(sa_bo));
+}
+
void xe_sa_bo_free(struct drm_suballoc *sa_bo,
struct dma_fence *fence)
{
diff --git a/drivers/gpu/drm/xe/xe_sa.h b/drivers/gpu/drm/xe/xe_sa.h
index 99dbf0eea540..1be744350836 100644
--- a/drivers/gpu/drm/xe/xe_sa.h
+++ b/drivers/gpu/drm/xe/xe_sa.h
@@ -37,6 +37,7 @@ static inline struct drm_suballoc *xe_sa_bo_new(struct xe_sa_manager *sa_manager
}
void xe_sa_bo_flush_write(struct drm_suballoc *sa_bo);
+void xe_sa_bo_sync_read(struct drm_suballoc *sa_bo);
void xe_sa_bo_free(struct drm_suballoc *sa_bo, struct dma_fence *fence);
static inline struct xe_sa_manager *
diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c
index d21bf8f26964..cb674a322113 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.c
+++ b/drivers/gpu/drm/xe/xe_sched_job.c
@@ -146,6 +146,7 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
for (i = 0; i < width; ++i)
job->ptrs[i].batch_addr = batch_addr[i];
+ atomic_inc(&q->job_cnt);
xe_pm_runtime_get_noresume(job_to_xe(job));
trace_xe_sched_job_create(job);
return job;
@@ -160,11 +161,11 @@ err_free:
}
/**
- * xe_sched_job_destroy - Destroy XE schedule job
- * @ref: reference to XE schedule job
+ * xe_sched_job_destroy - Destroy Xe schedule job
+ * @ref: reference to Xe schedule job
*
* Called when ref == 0, drop a reference to job's xe_engine + fence, cleanup
- * base DRM schedule job, and free memory for XE schedule job.
+ * base DRM schedule job, and free memory for Xe schedule job.
*/
void xe_sched_job_destroy(struct kref *ref)
{
@@ -177,6 +178,7 @@ void xe_sched_job_destroy(struct kref *ref)
dma_fence_put(job->fence);
drm_sched_job_cleanup(&job->drm);
job_free(job);
+ atomic_dec(&q->job_cnt);
xe_exec_queue_put(q);
xe_pm_runtime_put(xe);
}
@@ -296,23 +298,6 @@ void xe_sched_job_push(struct xe_sched_job *job)
}
/**
- * xe_sched_job_last_fence_add_dep - Add last fence dependency to job
- * @job:job to add the last fence dependency to
- * @vm: virtual memory job belongs to
- *
- * Returns:
- * 0 on success, or an error on failing to expand the array.
- */
-int xe_sched_job_last_fence_add_dep(struct xe_sched_job *job, struct xe_vm *vm)
-{
- struct dma_fence *fence;
-
- fence = xe_exec_queue_last_fence_get(job->q, vm);
-
- return drm_sched_job_add_dependency(&job->drm, fence);
-}
-
-/**
* xe_sched_job_init_user_fence - Initialize user_fence for the job
* @job: job whose user_fence needs an init
* @sync: sync to be use to init user_fence
diff --git a/drivers/gpu/drm/xe/xe_sched_job.h b/drivers/gpu/drm/xe/xe_sched_job.h
index 3dc72c5c1f13..1c1cb44216c3 100644
--- a/drivers/gpu/drm/xe/xe_sched_job.h
+++ b/drivers/gpu/drm/xe/xe_sched_job.h
@@ -23,10 +23,10 @@ struct xe_sched_job *xe_sched_job_create(struct xe_exec_queue *q,
void xe_sched_job_destroy(struct kref *ref);
/**
- * xe_sched_job_get - get reference to XE schedule job
- * @job: XE schedule job object
+ * xe_sched_job_get - get reference to Xe schedule job
+ * @job: Xe schedule job object
*
- * Increment XE schedule job's reference count
+ * Increment Xe schedule job's reference count
*/
static inline struct xe_sched_job *xe_sched_job_get(struct xe_sched_job *job)
{
@@ -35,10 +35,10 @@ static inline struct xe_sched_job *xe_sched_job_get(struct xe_sched_job *job)
}
/**
- * xe_sched_job_put - put reference to XE schedule job
- * @job: XE schedule job object
+ * xe_sched_job_put - put reference to Xe schedule job
+ * @job: Xe schedule job object
*
- * Decrement XE schedule job's reference count, call xe_sched_job_destroy when
+ * Decrement Xe schedule job's reference count, call xe_sched_job_destroy when
* reference count == 0.
*/
static inline void xe_sched_job_put(struct xe_sched_job *job)
@@ -58,7 +58,6 @@ bool xe_sched_job_completed(struct xe_sched_job *job);
void xe_sched_job_arm(struct xe_sched_job *job);
void xe_sched_job_push(struct xe_sched_job *job);
-int xe_sched_job_last_fence_add_dep(struct xe_sched_job *job, struct xe_vm *vm);
void xe_sched_job_init_user_fence(struct xe_sched_job *job,
struct xe_sync_entry *sync);
diff --git a/drivers/gpu/drm/xe/xe_sched_job_types.h b/drivers/gpu/drm/xe/xe_sched_job_types.h
index dbf260dded8d..7c4c54fe920a 100644
--- a/drivers/gpu/drm/xe/xe_sched_job_types.h
+++ b/drivers/gpu/drm/xe/xe_sched_job_types.h
@@ -24,10 +24,15 @@ struct xe_job_ptrs {
struct dma_fence_chain *chain_fence;
/** @batch_addr: Batch buffer address. */
u64 batch_addr;
+ /**
+ * @head: The tail pointer of the LRC (so head pointer of job) when the
+ * job was submitted
+ */
+ u32 head;
};
/**
- * struct xe_sched_job - XE schedule job (batch buffer tracking)
+ * struct xe_sched_job - Xe schedule job (batch buffer tracking)
*/
struct xe_sched_job {
/** @drm: base DRM scheduler job */
@@ -58,6 +63,10 @@ struct xe_sched_job {
bool ring_ops_flush_tlb;
/** @ggtt: mapped in ggtt. */
bool ggtt;
+ /** @restore_replay: job being replayed for restore */
+ bool restore_replay;
+ /** @last_replay: last job being replayed */
+ bool last_replay;
/** @ptrs: per instance pointers. */
struct xe_job_ptrs ptrs[];
};
diff --git a/drivers/gpu/drm/xe/xe_sriov.c b/drivers/gpu/drm/xe/xe_sriov.c
index 7d2d6de2aabf..ea411944609b 100644
--- a/drivers/gpu/drm/xe/xe_sriov.c
+++ b/drivers/gpu/drm/xe/xe_sriov.c
@@ -167,6 +167,8 @@ const char *xe_sriov_function_name(unsigned int n, char *buf, size_t size)
*/
int xe_sriov_init_late(struct xe_device *xe)
{
+ if (IS_SRIOV_PF(xe))
+ return xe_sriov_pf_init_late(xe);
if (IS_SRIOV_VF(xe))
return xe_sriov_vf_init_late(xe);
diff --git a/drivers/gpu/drm/xe/xe_sriov_packet.c b/drivers/gpu/drm/xe/xe_sriov_packet.c
new file mode 100644
index 000000000000..bab994696896
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_packet.c
@@ -0,0 +1,520 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "xe_bo.h"
+#include "xe_device.h"
+#include "xe_guc_klv_helpers.h"
+#include "xe_printk.h"
+#include "xe_sriov_packet.h"
+#include "xe_sriov_packet_types.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_migration.h"
+#include "xe_sriov_printk.h"
+
+static struct mutex *pf_migration_mutex(struct xe_device *xe, unsigned int vfid)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, vfid <= xe_sriov_pf_get_totalvfs(xe));
+
+ return &xe->sriov.pf.vfs[vfid].migration.lock;
+}
+
+static struct xe_sriov_packet **pf_pick_pending(struct xe_device *xe, unsigned int vfid)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, vfid <= xe_sriov_pf_get_totalvfs(xe));
+ lockdep_assert_held(pf_migration_mutex(xe, vfid));
+
+ return &xe->sriov.pf.vfs[vfid].migration.pending;
+}
+
+static struct xe_sriov_packet **
+pf_pick_descriptor(struct xe_device *xe, unsigned int vfid)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, vfid <= xe_sriov_pf_get_totalvfs(xe));
+ lockdep_assert_held(pf_migration_mutex(xe, vfid));
+
+ return &xe->sriov.pf.vfs[vfid].migration.descriptor;
+}
+
+static struct xe_sriov_packet **pf_pick_trailer(struct xe_device *xe, unsigned int vfid)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, vfid <= xe_sriov_pf_get_totalvfs(xe));
+ lockdep_assert_held(pf_migration_mutex(xe, vfid));
+
+ return &xe->sriov.pf.vfs[vfid].migration.trailer;
+}
+
+static struct xe_sriov_packet **pf_pick_read_packet(struct xe_device *xe,
+ unsigned int vfid)
+{
+ struct xe_sriov_packet **data;
+
+ data = pf_pick_descriptor(xe, vfid);
+ if (*data)
+ return data;
+
+ data = pf_pick_pending(xe, vfid);
+ if (!*data)
+ *data = xe_sriov_pf_migration_save_consume(xe, vfid);
+ if (*data)
+ return data;
+
+ data = pf_pick_trailer(xe, vfid);
+ if (*data)
+ return data;
+
+ return NULL;
+}
+
+static bool pkt_needs_bo(struct xe_sriov_packet *data)
+{
+ return data->hdr.type == XE_SRIOV_PACKET_TYPE_VRAM;
+}
+
+/**
+ * xe_sriov_packet_alloc() - Allocate migration data packet
+ * @xe: the &xe_device
+ *
+ * Only allocates the "outer" structure, without initializing the migration
+ * data backing storage.
+ *
+ * Return: Pointer to &xe_sriov_packet on success,
+ * NULL in case of error.
+ */
+struct xe_sriov_packet *xe_sriov_packet_alloc(struct xe_device *xe)
+{
+ struct xe_sriov_packet *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ data->xe = xe;
+ data->hdr_remaining = sizeof(data->hdr);
+
+ return data;
+}
+
+/**
+ * xe_sriov_packet_free() - Free migration data packet.
+ * @data: the &xe_sriov_packet
+ */
+void xe_sriov_packet_free(struct xe_sriov_packet *data)
+{
+ if (IS_ERR_OR_NULL(data))
+ return;
+
+ if (pkt_needs_bo(data))
+ xe_bo_unpin_map_no_vm(data->bo);
+ else
+ kvfree(data->buff);
+
+ kfree(data);
+}
+
+static int pkt_init(struct xe_sriov_packet *data)
+{
+ struct xe_gt *gt = xe_device_get_gt(data->xe, data->hdr.gt_id);
+
+ if (!gt)
+ return -EINVAL;
+
+ if (data->hdr.size == 0)
+ return 0;
+
+ if (pkt_needs_bo(data)) {
+ struct xe_bo *bo;
+
+ bo = xe_bo_create_pin_map_novm(data->xe, gt->tile, PAGE_ALIGN(data->hdr.size),
+ ttm_bo_type_kernel,
+ XE_BO_FLAG_SYSTEM | XE_BO_FLAG_PINNED, false);
+ if (IS_ERR(bo))
+ return PTR_ERR(bo);
+
+ data->bo = bo;
+ data->vaddr = bo->vmap.vaddr;
+ } else {
+ void *buff = kvzalloc(data->hdr.size, GFP_KERNEL);
+
+ if (!buff)
+ return -ENOMEM;
+
+ data->buff = buff;
+ data->vaddr = buff;
+ }
+
+ return 0;
+}
+
+#define XE_SRIOV_PACKET_SUPPORTED_VERSION 1
+
+/**
+ * xe_sriov_packet_init() - Initialize migration packet header and backing storage.
+ * @data: the &xe_sriov_packet
+ * @tile_id: tile identifier
+ * @gt_id: GT identifier
+ * @type: &xe_sriov_packet_type
+ * @offset: offset of data packet payload (within wider resource)
+ * @size: size of data packet payload
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_packet_init(struct xe_sriov_packet *data, u8 tile_id, u8 gt_id,
+ enum xe_sriov_packet_type type, loff_t offset, size_t size)
+{
+ data->hdr.version = XE_SRIOV_PACKET_SUPPORTED_VERSION;
+ data->hdr.type = type;
+ data->hdr.tile_id = tile_id;
+ data->hdr.gt_id = gt_id;
+ data->hdr.offset = offset;
+ data->hdr.size = size;
+ data->remaining = size;
+
+ return pkt_init(data);
+}
+
+/**
+ * xe_sriov_packet_init_from_hdr() - Initialize migration packet backing storage based on header.
+ * @data: the &xe_sriov_packet
+ *
+ * Header data is expected to be filled prior to calling this function.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_packet_init_from_hdr(struct xe_sriov_packet *data)
+{
+ xe_assert(data->xe, !data->hdr_remaining);
+
+ if (data->hdr.version != XE_SRIOV_PACKET_SUPPORTED_VERSION)
+ return -EINVAL;
+
+ data->remaining = data->hdr.size;
+
+ return pkt_init(data);
+}
+
+static ssize_t pkt_hdr_read(struct xe_sriov_packet *data,
+ char __user *buf, size_t len)
+{
+ loff_t offset = sizeof(data->hdr) - data->hdr_remaining;
+
+ if (!data->hdr_remaining)
+ return -EINVAL;
+
+ if (len > data->hdr_remaining)
+ len = data->hdr_remaining;
+
+ if (copy_to_user(buf, (void *)&data->hdr + offset, len))
+ return -EFAULT;
+
+ data->hdr_remaining -= len;
+
+ return len;
+}
+
+static ssize_t pkt_data_read(struct xe_sriov_packet *data,
+ char __user *buf, size_t len)
+{
+ if (len > data->remaining)
+ len = data->remaining;
+
+ if (copy_to_user(buf, data->vaddr + (data->hdr.size - data->remaining), len))
+ return -EFAULT;
+
+ data->remaining -= len;
+
+ return len;
+}
+
+static ssize_t pkt_read_single(struct xe_sriov_packet **data,
+ unsigned int vfid, char __user *buf, size_t len)
+{
+ ssize_t copied = 0;
+
+ if ((*data)->hdr_remaining)
+ copied = pkt_hdr_read(*data, buf, len);
+ else
+ copied = pkt_data_read(*data, buf, len);
+
+ if ((*data)->remaining == 0 && (*data)->hdr_remaining == 0) {
+ xe_sriov_packet_free(*data);
+ *data = NULL;
+ }
+
+ return copied;
+}
+
+/**
+ * xe_sriov_packet_read_single() - Read migration data from a single packet.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ * @buf: start address of userspace buffer
+ * @len: requested read size from userspace
+ *
+ * Return: number of bytes that has been successfully read,
+ * 0 if no more migration data is available,
+ * -errno on failure.
+ */
+ssize_t xe_sriov_packet_read_single(struct xe_device *xe, unsigned int vfid,
+ char __user *buf, size_t len)
+{
+ struct xe_sriov_packet **data = pf_pick_read_packet(xe, vfid);
+
+ if (!data)
+ return -ENODATA;
+ if (IS_ERR(*data))
+ return PTR_ERR(*data);
+
+ return pkt_read_single(data, vfid, buf, len);
+}
+
+static ssize_t pkt_hdr_write(struct xe_sriov_packet *data,
+ const char __user *buf, size_t len)
+{
+ loff_t offset = sizeof(data->hdr) - data->hdr_remaining;
+ int ret;
+
+ if (len > data->hdr_remaining)
+ len = data->hdr_remaining;
+
+ if (copy_from_user((void *)&data->hdr + offset, buf, len))
+ return -EFAULT;
+
+ data->hdr_remaining -= len;
+
+ if (!data->hdr_remaining) {
+ ret = xe_sriov_packet_init_from_hdr(data);
+ if (ret)
+ return ret;
+ }
+
+ return len;
+}
+
+static ssize_t pkt_data_write(struct xe_sriov_packet *data,
+ const char __user *buf, size_t len)
+{
+ if (len > data->remaining)
+ len = data->remaining;
+
+ if (copy_from_user(data->vaddr + (data->hdr.size - data->remaining), buf, len))
+ return -EFAULT;
+
+ data->remaining -= len;
+
+ return len;
+}
+
+/**
+ * xe_sriov_packet_write_single() - Write migration data to a single packet.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ * @buf: start address of userspace buffer
+ * @len: requested write size from userspace
+ *
+ * Return: number of bytes that has been successfully written,
+ * -errno on failure.
+ */
+ssize_t xe_sriov_packet_write_single(struct xe_device *xe, unsigned int vfid,
+ const char __user *buf, size_t len)
+{
+ struct xe_sriov_packet **data = pf_pick_pending(xe, vfid);
+ int ret;
+ ssize_t copied;
+
+ if (IS_ERR_OR_NULL(*data)) {
+ *data = xe_sriov_packet_alloc(xe);
+ if (!*data)
+ return -ENOMEM;
+ }
+
+ if ((*data)->hdr_remaining)
+ copied = pkt_hdr_write(*data, buf, len);
+ else
+ copied = pkt_data_write(*data, buf, len);
+
+ if ((*data)->hdr_remaining == 0 && (*data)->remaining == 0) {
+ ret = xe_sriov_pf_migration_restore_produce(xe, vfid, *data);
+ if (ret) {
+ xe_sriov_packet_free(*data);
+ return ret;
+ }
+
+ *data = NULL;
+ }
+
+ return copied;
+}
+
+#define MIGRATION_KLV_DEVICE_DEVID_KEY 0xf001u
+#define MIGRATION_KLV_DEVICE_DEVID_LEN 1u
+#define MIGRATION_KLV_DEVICE_REVID_KEY 0xf002u
+#define MIGRATION_KLV_DEVICE_REVID_LEN 1u
+
+#define MIGRATION_DESCRIPTOR_DWORDS (GUC_KLV_LEN_MIN + MIGRATION_KLV_DEVICE_DEVID_LEN + \
+ GUC_KLV_LEN_MIN + MIGRATION_KLV_DEVICE_REVID_LEN)
+static size_t pf_descriptor_init(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_sriov_packet **desc = pf_pick_descriptor(xe, vfid);
+ struct xe_sriov_packet *data;
+ unsigned int len = 0;
+ u32 *klvs;
+ int ret;
+
+ data = xe_sriov_packet_alloc(xe);
+ if (!data)
+ return -ENOMEM;
+
+ ret = xe_sriov_packet_init(data, 0, 0, XE_SRIOV_PACKET_TYPE_DESCRIPTOR,
+ 0, MIGRATION_DESCRIPTOR_DWORDS * sizeof(u32));
+ if (ret) {
+ xe_sriov_packet_free(data);
+ return ret;
+ }
+
+ klvs = data->vaddr;
+ klvs[len++] = PREP_GUC_KLV_CONST(MIGRATION_KLV_DEVICE_DEVID_KEY,
+ MIGRATION_KLV_DEVICE_DEVID_LEN);
+ klvs[len++] = xe->info.devid;
+ klvs[len++] = PREP_GUC_KLV_CONST(MIGRATION_KLV_DEVICE_REVID_KEY,
+ MIGRATION_KLV_DEVICE_REVID_LEN);
+ klvs[len++] = xe->info.revid;
+
+ xe_assert(xe, len == MIGRATION_DESCRIPTOR_DWORDS);
+
+ *desc = data;
+
+ return 0;
+}
+
+/**
+ * xe_sriov_packet_process_descriptor() - Process migration data descriptor packet.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ * @data: the &xe_sriov_packet containing the descriptor
+ *
+ * The descriptor uses the same KLV format as GuC, and contains metadata used for
+ * checking migration data compatibility.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int xe_sriov_packet_process_descriptor(struct xe_device *xe, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ u32 num_dwords = data->hdr.size / sizeof(u32);
+ u32 *klvs = data->vaddr;
+
+ xe_assert(xe, data->hdr.type == XE_SRIOV_PACKET_TYPE_DESCRIPTOR);
+
+ if (data->hdr.size % sizeof(u32)) {
+ xe_sriov_warn(xe, "Aborting migration, descriptor not in KLV format (size=%llu)\n",
+ data->hdr.size);
+ return -EINVAL;
+ }
+
+ while (num_dwords >= GUC_KLV_LEN_MIN) {
+ u32 key = FIELD_GET(GUC_KLV_0_KEY, klvs[0]);
+ u32 len = FIELD_GET(GUC_KLV_0_LEN, klvs[0]);
+
+ klvs += GUC_KLV_LEN_MIN;
+ num_dwords -= GUC_KLV_LEN_MIN;
+
+ if (len > num_dwords) {
+ xe_sriov_warn(xe, "Aborting migration, truncated KLV %#x, len %u\n",
+ key, len);
+ return -EINVAL;
+ }
+
+ switch (key) {
+ case MIGRATION_KLV_DEVICE_DEVID_KEY:
+ if (*klvs != xe->info.devid) {
+ xe_sriov_warn(xe,
+ "Aborting migration, devid mismatch %#06x!=%#06x\n",
+ *klvs, xe->info.devid);
+ return -ENODEV;
+ }
+ break;
+ case MIGRATION_KLV_DEVICE_REVID_KEY:
+ if (*klvs != xe->info.revid) {
+ xe_sriov_warn(xe,
+ "Aborting migration, revid mismatch %#06x!=%#06x\n",
+ *klvs, xe->info.revid);
+ return -ENODEV;
+ }
+ break;
+ default:
+ xe_sriov_dbg(xe,
+ "Skipping unknown migration KLV %#x, len=%u\n",
+ key, len);
+ print_hex_dump_bytes("desc: ", DUMP_PREFIX_OFFSET, klvs,
+ min(SZ_64, len * sizeof(u32)));
+ break;
+ }
+
+ klvs += len;
+ num_dwords -= len;
+ }
+
+ return 0;
+}
+
+static void pf_pending_init(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_sriov_packet **data = pf_pick_pending(xe, vfid);
+
+ *data = NULL;
+}
+
+#define MIGRATION_TRAILER_SIZE 0
+static int pf_trailer_init(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_sriov_packet **trailer = pf_pick_trailer(xe, vfid);
+ struct xe_sriov_packet *data;
+ int ret;
+
+ data = xe_sriov_packet_alloc(xe);
+ if (!data)
+ return -ENOMEM;
+
+ ret = xe_sriov_packet_init(data, 0, 0, XE_SRIOV_PACKET_TYPE_TRAILER,
+ 0, MIGRATION_TRAILER_SIZE);
+ if (ret) {
+ xe_sriov_packet_free(data);
+ return ret;
+ }
+
+ *trailer = data;
+
+ return 0;
+}
+
+/**
+ * xe_sriov_packet_save_init() - Initialize the pending save migration packets.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int xe_sriov_packet_save_init(struct xe_device *xe, unsigned int vfid)
+{
+ int ret;
+
+ scoped_cond_guard(mutex_intr, return -EINTR, pf_migration_mutex(xe, vfid)) {
+ ret = pf_descriptor_init(xe, vfid);
+ if (ret)
+ return ret;
+
+ ret = pf_trailer_init(xe, vfid);
+ if (ret)
+ return ret;
+
+ pf_pending_init(xe, vfid);
+ }
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_packet.h b/drivers/gpu/drm/xe/xe_sriov_packet.h
new file mode 100644
index 000000000000..2731e52cf7ef
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_packet.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PACKET_H_
+#define _XE_SRIOV_PACKET_H_
+
+#include <linux/types.h>
+
+struct xe_device;
+struct xe_sriov_packet;
+enum xe_sriov_packet_type;
+
+struct xe_sriov_packet *xe_sriov_packet_alloc(struct xe_device *xe);
+void xe_sriov_packet_free(struct xe_sriov_packet *data);
+
+int xe_sriov_packet_init(struct xe_sriov_packet *data, u8 tile_id, u8 gt_id,
+ enum xe_sriov_packet_type, loff_t offset, size_t size);
+int xe_sriov_packet_init_from_hdr(struct xe_sriov_packet *data);
+
+ssize_t xe_sriov_packet_read_single(struct xe_device *xe, unsigned int vfid,
+ char __user *buf, size_t len);
+ssize_t xe_sriov_packet_write_single(struct xe_device *xe, unsigned int vfid,
+ const char __user *buf, size_t len);
+int xe_sriov_packet_save_init(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_packet_process_descriptor(struct xe_device *xe, unsigned int vfid,
+ struct xe_sriov_packet *data);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_packet_types.h b/drivers/gpu/drm/xe/xe_sriov_packet_types.h
new file mode 100644
index 000000000000..078a1c95e786
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_packet_types.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PACKET_TYPES_H_
+#define _XE_SRIOV_PACKET_TYPES_H_
+
+#include <linux/types.h>
+
+/**
+ * enum xe_sriov_packet_type - Xe SR-IOV VF migration data packet type
+ * @XE_SRIOV_PACKET_TYPE_DESCRIPTOR: Descriptor with VF device metadata
+ * @XE_SRIOV_PACKET_TYPE_TRAILER: Trailer indicating end-of-stream
+ * @XE_SRIOV_PACKET_TYPE_GGTT: Global GTT migration data
+ * @XE_SRIOV_PACKET_TYPE_MMIO: MMIO registers migration data
+ * @XE_SRIOV_PACKET_TYPE_GUC: GuC firmware migration data
+ * @XE_SRIOV_PACKET_TYPE_VRAM: VRAM migration data
+ */
+enum xe_sriov_packet_type {
+ /* Skipping 0 to catch uninitialized data */
+ XE_SRIOV_PACKET_TYPE_DESCRIPTOR = 1,
+ XE_SRIOV_PACKET_TYPE_TRAILER,
+ XE_SRIOV_PACKET_TYPE_GGTT,
+ XE_SRIOV_PACKET_TYPE_MMIO,
+ XE_SRIOV_PACKET_TYPE_GUC,
+ XE_SRIOV_PACKET_TYPE_VRAM,
+};
+
+/**
+ * struct xe_sriov_packet_hdr - Xe SR-IOV VF migration data packet header
+ */
+struct xe_sriov_packet_hdr {
+ /** @version: migration data protocol version */
+ u8 version;
+ /** @type: migration data type */
+ u8 type;
+ /** @tile_id: migration data tile id */
+ u8 tile_id;
+ /** @gt_id: migration data gt id */
+ u8 gt_id;
+ /** @flags: migration data flags */
+ u32 flags;
+ /**
+ * @offset: offset into the resource;
+ * used when multiple packets of given type are used for migration
+ */
+ u64 offset;
+ /** @size: migration data size */
+ u64 size;
+} __packed;
+
+/**
+ * struct xe_sriov_packet - Xe SR-IOV VF migration data packet
+ */
+struct xe_sriov_packet {
+ /** @xe: the PF &xe_device this data packet belongs to */
+ struct xe_device *xe;
+ /** @vaddr: CPU pointer to payload data */
+ void *vaddr;
+ /** @remaining: payload data remaining */
+ size_t remaining;
+ /** @hdr_remaining: header data remaining */
+ size_t hdr_remaining;
+ union {
+ /** @bo: Buffer object with migration data */
+ struct xe_bo *bo;
+ /** @buff: Buffer with migration data */
+ void *buff;
+ };
+ /** @hdr: data packet header */
+ struct xe_sriov_packet_hdr hdr;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.c b/drivers/gpu/drm/xe/xe_sriov_pf.c
index 27ddf3cc80e9..7c779d63179f 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_pf.c
@@ -8,17 +8,22 @@
#include <drm/drm_managed.h>
#include "xe_assert.h"
+#include "xe_configfs.h"
#include "xe_device.h"
#include "xe_gt_sriov_pf.h"
#include "xe_module.h"
#include "xe_sriov.h"
#include "xe_sriov_pf.h"
#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_migration.h"
#include "xe_sriov_pf_service.h"
+#include "xe_sriov_pf_sysfs.h"
#include "xe_sriov_printk.h"
static unsigned int wanted_max_vfs(struct xe_device *xe)
{
+ if (IS_ENABLED(CONFIG_CONFIGFS_FS))
+ return xe_configfs_get_max_vfs(to_pci_dev(xe->drm.dev));
return xe_modparam.max_vfs;
}
@@ -98,12 +103,47 @@ int xe_sriov_pf_init_early(struct xe_device *xe)
if (err)
return err;
+ err = xe_sriov_pf_migration_init(xe);
+ if (err)
+ return err;
+
+ xe_guard_init(&xe->sriov.pf.guard_vfs_enabling, "vfs_enabling");
+
xe_sriov_pf_service_init(xe);
return 0;
}
/**
+ * xe_sriov_pf_init_late() - Late initialization of the SR-IOV PF.
+ * @xe: the &xe_device to initialize
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_init_late(struct xe_device *xe)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int err;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_init(gt);
+ if (err)
+ return err;
+ }
+
+ err = xe_sriov_pf_sysfs_init(xe);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/**
* xe_sriov_pf_wait_ready() - Wait until PF is ready to operate.
* @xe: the &xe_device to test
*
@@ -130,61 +170,114 @@ int xe_sriov_pf_wait_ready(struct xe_device *xe)
}
/**
- * xe_sriov_pf_print_vfs_summary - Print SR-IOV PF information.
- * @xe: the &xe_device to print info from
- * @p: the &drm_printer
+ * xe_sriov_pf_arm_guard() - Arm the guard for exclusive/lockdown mode.
+ * @xe: the PF &xe_device
+ * @guard: the &xe_guard to arm
+ * @lockdown: arm for lockdown(true) or exclusive(false) mode
+ * @who: the address of the new owner, or NULL if it's a caller
*
- * Print SR-IOV PF related information into provided DRM printer.
+ * This function can only be called on PF.
+ *
+ * It is a simple wrapper for xe_guard_arm() with additional debug
+ * messages.
+ *
+ * Return: 0 on success or a negative error code on failure.
*/
-void xe_sriov_pf_print_vfs_summary(struct xe_device *xe, struct drm_printer *p)
+int xe_sriov_pf_arm_guard(struct xe_device *xe, struct xe_guard *guard,
+ bool lockdown, void *who)
{
- struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+ void *new_owner = who ?: __builtin_return_address(0);
+ int err;
- xe_assert(xe, IS_SRIOV_PF(xe));
+ err = xe_guard_arm(guard, lockdown, new_owner);
+ if (err) {
+ xe_sriov_dbg(xe, "%s/%s mode denied (%pe) last owner %ps\n",
+ guard->name, xe_guard_mode_str(lockdown),
+ ERR_PTR(err), guard->owner);
+ return err;
+ }
- drm_printf(p, "total: %u\n", xe->sriov.pf.device_total_vfs);
- drm_printf(p, "supported: %u\n", xe->sriov.pf.driver_max_vfs);
- drm_printf(p, "enabled: %u\n", pci_num_vf(pdev));
+ xe_sriov_dbg_verbose(xe, "%s/%s by %ps\n",
+ guard->name, xe_guard_mode_str(lockdown),
+ new_owner);
+ return 0;
}
-static int simple_show(struct seq_file *m, void *data)
+/**
+ * xe_sriov_pf_disarm_guard() - Disarm the guard.
+ * @xe: the PF &xe_device
+ * @guard: the &xe_guard to disarm
+ * @lockdown: disarm from lockdown(true) or exclusive(false) mode
+ * @who: the address of the indirect owner, or NULL if it's a caller
+ *
+ * This function can only be called on PF.
+ *
+ * It is a simple wrapper for xe_guard_disarm() with additional debug
+ * messages and xe_assert() to easily catch any illegal calls.
+ */
+void xe_sriov_pf_disarm_guard(struct xe_device *xe, struct xe_guard *guard,
+ bool lockdown, void *who)
{
- struct drm_printer p = drm_seq_file_printer(m);
- struct drm_info_node *node = m->private;
- struct dentry *parent = node->dent->d_parent;
- struct xe_device *xe = parent->d_inode->i_private;
- void (*print)(struct xe_device *, struct drm_printer *) = node->info_ent->data;
+ bool disarmed;
- print(xe, &p);
- return 0;
+ xe_sriov_dbg_verbose(xe, "%s/%s by %ps\n",
+ guard->name, xe_guard_mode_str(lockdown),
+ who ?: __builtin_return_address(0));
+
+ disarmed = xe_guard_disarm(guard, lockdown);
+ xe_assert_msg(xe, disarmed, "%s/%s not armed? last owner %ps",
+ guard->name, xe_guard_mode_str(lockdown), guard->owner);
}
-static const struct drm_info_list debugfs_list[] = {
- { .name = "vfs", .show = simple_show, .data = xe_sriov_pf_print_vfs_summary },
- { .name = "versions", .show = simple_show, .data = xe_sriov_pf_service_print_versions },
-};
+/**
+ * xe_sriov_pf_lockdown() - Lockdown the PF to prevent VFs enabling.
+ * @xe: the PF &xe_device
+ *
+ * This function can only be called on PF.
+ *
+ * Once the PF is locked down, it will not enable VFs.
+ * If VFs are already enabled, the -EBUSY will be returned.
+ * To allow the PF enable VFs again call xe_sriov_pf_end_lockdown().
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_lockdown(struct xe_device *xe)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ return xe_sriov_pf_arm_guard(xe, &xe->sriov.pf.guard_vfs_enabling, true,
+ __builtin_return_address(0));
+}
/**
- * xe_sriov_pf_debugfs_register - Register PF debugfs attributes.
- * @xe: the &xe_device
- * @root: the root &dentry
+ * xe_sriov_pf_end_lockdown() - Allow the PF to enable VFs again.
+ * @xe: the PF &xe_device
*
- * Prepare debugfs attributes exposed by the PF.
+ * This function can only be called on PF.
+ * See xe_sriov_pf_lockdown() for details.
*/
-void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
+void xe_sriov_pf_end_lockdown(struct xe_device *xe)
{
- struct drm_minor *minor = xe->drm.primary;
- struct dentry *parent;
-
- /*
- * /sys/kernel/debug/dri/0/
- * ├── pf
- * │   ├── ...
- */
- parent = debugfs_create_dir("pf", root);
- if (IS_ERR(parent))
- return;
- parent->d_inode->i_private = xe;
-
- drm_debugfs_create_files(debugfs_list, ARRAY_SIZE(debugfs_list), parent, minor);
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ xe_sriov_pf_disarm_guard(xe, &xe->sriov.pf.guard_vfs_enabling, true,
+ __builtin_return_address(0));
+}
+
+/**
+ * xe_sriov_pf_print_vfs_summary - Print SR-IOV PF information.
+ * @xe: the &xe_device to print info from
+ * @p: the &drm_printer
+ *
+ * Print SR-IOV PF related information into provided DRM printer.
+ */
+void xe_sriov_pf_print_vfs_summary(struct xe_device *xe, struct drm_printer *p)
+{
+ struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ drm_printf(p, "total: %u\n", xe->sriov.pf.device_total_vfs);
+ drm_printf(p, "supported: %u\n", xe->sriov.pf.driver_max_vfs);
+ drm_printf(p, "enabled: %u\n", pci_num_vf(pdev));
}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf.h b/drivers/gpu/drm/xe/xe_sriov_pf.h
index e3b34f8f5e04..b4d050ad5b7c 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf.h
+++ b/drivers/gpu/drm/xe/xe_sriov_pf.h
@@ -15,23 +15,17 @@ struct xe_device;
#ifdef CONFIG_PCI_IOV
bool xe_sriov_pf_readiness(struct xe_device *xe);
int xe_sriov_pf_init_early(struct xe_device *xe);
+int xe_sriov_pf_init_late(struct xe_device *xe);
int xe_sriov_pf_wait_ready(struct xe_device *xe);
-void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root);
+int xe_sriov_pf_lockdown(struct xe_device *xe);
+void xe_sriov_pf_end_lockdown(struct xe_device *xe);
void xe_sriov_pf_print_vfs_summary(struct xe_device *xe, struct drm_printer *p);
#else
-static inline bool xe_sriov_pf_readiness(struct xe_device *xe)
-{
- return false;
-}
-
-static inline int xe_sriov_pf_init_early(struct xe_device *xe)
-{
- return 0;
-}
-
-static inline void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
-{
-}
+static inline bool xe_sriov_pf_readiness(struct xe_device *xe) { return false; }
+static inline int xe_sriov_pf_init_early(struct xe_device *xe) { return 0; }
+static inline int xe_sriov_pf_init_late(struct xe_device *xe) { return 0; }
+static inline int xe_sriov_pf_lockdown(struct xe_device *xe) { return 0; }
+static inline void xe_sriov_pf_end_lockdown(struct xe_device *xe) { }
#endif
#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_control.c b/drivers/gpu/drm/xe/xe_sriov_pf_control.c
new file mode 100644
index 000000000000..ed4b9820b06e
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_control.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "xe_device.h"
+#include "xe_gt_sriov_pf_control.h"
+#include "xe_gt_sriov_pf_migration.h"
+#include "xe_sriov_packet.h"
+#include "xe_sriov_pf_control.h"
+#include "xe_sriov_printk.h"
+
+/**
+ * xe_sriov_pf_control_pause_vf() - Pause a VF on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier (can't be 0 == PFID)
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_pause_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_pause_vf(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ if (result)
+ return result;
+
+ xe_sriov_info(xe, "VF%u paused!\n", vfid);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_resume_vf() - Resume a VF on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_resume_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_resume_vf(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ if (result)
+ return result;
+
+ xe_sriov_info(xe, "VF%u resumed!\n", vfid);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_stop_vf - Stop a VF on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_stop_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_stop_vf(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ if (result)
+ return result;
+
+ xe_sriov_info(xe, "VF%u stopped!\n", vfid);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_reset_vf() - Perform a VF reset (FLR).
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_reset_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_trigger_flr(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_wait_flr(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_control_wait_flr() - Wait for a VF reset (FLR) to complete.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_wait_flr(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_control_wait_flr(gt, vfid);
+ result = result ? -EUCLEAN : err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_control_sync_flr() - Synchronize a VF FLR between all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_sync_flr(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int ret;
+
+ for_each_gt(gt, xe, id) {
+ ret = xe_gt_sriov_pf_control_sync_flr(gt, vfid, false);
+ if (ret < 0)
+ return ret;
+ }
+ for_each_gt(gt, xe, id) {
+ ret = xe_gt_sriov_pf_control_sync_flr(gt, vfid, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_trigger_save_vf() - Start VF migration data SAVE sequence on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_trigger_save_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int ret;
+
+ ret = xe_sriov_packet_save_init(xe, vfid);
+ if (ret)
+ return ret;
+
+ for_each_gt(gt, xe, id) {
+ xe_gt_sriov_pf_migration_save_init(gt, vfid);
+
+ ret = xe_gt_sriov_pf_control_trigger_save_vf(gt, vfid);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_control_finish_save_vf() - Complete VF migration data SAVE sequence on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_finish_save_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int ret;
+
+ for_each_gt(gt, xe, id) {
+ ret = xe_gt_sriov_pf_control_finish_save_vf(gt, vfid);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * xe_sriov_pf_control_trigger_restore_vf() - Start VF migration data RESTORE sequence on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_trigger_restore_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int ret;
+
+ for_each_gt(gt, xe, id) {
+ ret = xe_gt_sriov_pf_control_trigger_restore_vf(gt, vfid);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * xe_sriov_pf_control_finish_restore_vf() - Complete VF migration data RESTORE sequence on all GTs.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * This function is for PF only.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_control_finish_restore_vf(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int ret;
+
+ for_each_gt(gt, xe, id) {
+ ret = xe_gt_sriov_pf_control_finish_restore_vf(gt, vfid);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_control.h b/drivers/gpu/drm/xe/xe_sriov_pf_control.h
new file mode 100644
index 000000000000..ef9f219b2109
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_control.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_CONTROL_H_
+#define _XE_SRIOV_PF_CONTROL_H_
+
+struct xe_device;
+
+int xe_sriov_pf_control_pause_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_resume_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_stop_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_reset_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_wait_flr(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_sync_flr(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_trigger_save_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_finish_save_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_trigger_restore_vf(struct xe_device *xe, unsigned int vfid);
+int xe_sriov_pf_control_finish_restore_vf(struct xe_device *xe, unsigned int vfid);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c
new file mode 100644
index 000000000000..bad751217e1e
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+#include <drm/drm_debugfs.h>
+
+#include "xe_device.h"
+#include "xe_device_types.h"
+#include "xe_pm.h"
+#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_control.h"
+#include "xe_sriov_pf_debugfs.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_migration.h"
+#include "xe_sriov_pf_provision.h"
+#include "xe_sriov_pf_service.h"
+#include "xe_sriov_printk.h"
+#include "xe_tile_sriov_pf_debugfs.h"
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * : :
+ * │ ├── vfN # d_inode->i_private = VFID(N)
+ */
+
+static void *extract_priv(struct dentry *d)
+{
+ return d->d_inode->i_private;
+}
+
+static struct xe_device *extract_xe(struct dentry *d)
+{
+ return extract_priv(d->d_parent);
+}
+
+static unsigned int extract_vfid(struct dentry *d)
+{
+ void *p = extract_priv(d);
+
+ return p == extract_xe(d) ? PFID : (uintptr_t)p;
+}
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── restore_auto_provisioning
+ * │ :
+ * │ ├── pf/
+ * │ ├── vf1
+ * │ │ ├── ...
+ */
+
+static ssize_t from_file_write_to_xe_call(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos,
+ int (*call)(struct xe_device *))
+{
+ struct dentry *dent = file_dentry(file);
+ struct xe_device *xe = extract_xe(dent);
+ bool yes;
+ int ret;
+
+ if (*ppos)
+ return -EINVAL;
+ ret = kstrtobool_from_user(userbuf, count, &yes);
+ if (ret < 0)
+ return ret;
+ if (yes) {
+ xe_pm_runtime_get(xe);
+ ret = call(xe);
+ xe_pm_runtime_put(xe);
+ }
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+#define DEFINE_SRIOV_ATTRIBUTE(OP) \
+static int OP##_show(struct seq_file *s, void *unused) \
+{ \
+ return 0; \
+} \
+static ssize_t OP##_write(struct file *file, const char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ return from_file_write_to_xe_call(file, userbuf, count, ppos, \
+ xe_sriov_pf_##OP); \
+} \
+DEFINE_SHOW_STORE_ATTRIBUTE(OP)
+
+static inline int xe_sriov_pf_restore_auto_provisioning(struct xe_device *xe)
+{
+ return xe_sriov_pf_provision_set_mode(xe, XE_SRIOV_PROVISIONING_MODE_AUTO);
+}
+
+DEFINE_SRIOV_ATTRIBUTE(restore_auto_provisioning);
+
+static int lockdown_vfs_enabling_open(struct inode *inode, struct file *file)
+{
+ struct dentry *dent = file_dentry(file);
+ struct xe_device *xe = extract_xe(dent);
+ ssize_t ret;
+
+ ret = xe_sriov_pf_lockdown(xe);
+ if (ret < 0)
+ return ret;
+
+ file->private_data = xe;
+ return nonseekable_open(inode, file);
+}
+
+static int lockdown_vfs_enabling_release(struct inode *inode, struct file *file)
+{
+ struct xe_device *xe = file->private_data;
+
+ xe_sriov_pf_end_lockdown(xe);
+ return 0;
+}
+
+static const struct file_operations lockdown_vfs_enabling_fops = {
+ .owner = THIS_MODULE,
+ .open = lockdown_vfs_enabling_open,
+ .release = lockdown_vfs_enabling_release,
+};
+
+static void pf_populate_root(struct xe_device *xe, struct dentry *dent)
+{
+ debugfs_create_file("restore_auto_provisioning", 0200, dent, xe,
+ &restore_auto_provisioning_fops);
+ debugfs_create_file("lockdown_vfs_enabling", 0400, dent, xe,
+ &lockdown_vfs_enabling_fops);
+}
+
+static int simple_show(struct seq_file *m, void *data)
+{
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_info_node *node = m->private;
+ struct dentry *parent = node->dent->d_parent;
+ struct xe_device *xe = parent->d_inode->i_private;
+ void (*print)(struct xe_device *, struct drm_printer *) = node->info_ent->data;
+
+ print(xe, &p);
+ return 0;
+}
+
+static const struct drm_info_list debugfs_list[] = {
+ { .name = "vfs", .show = simple_show, .data = xe_sriov_pf_print_vfs_summary },
+ { .name = "versions", .show = simple_show, .data = xe_sriov_pf_service_print_versions },
+};
+
+static void pf_populate_pf(struct xe_device *xe, struct dentry *pfdent)
+{
+ struct drm_minor *minor = xe->drm.primary;
+
+ drm_debugfs_create_files(debugfs_list, ARRAY_SIZE(debugfs_list), pfdent, minor);
+}
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── vf1
+ * │ │ ├── migration_data
+ * │ │ ├── pause
+ * │ │ ├── reset
+ * │ │ ├── resume
+ * │ │ ├── stop
+ * │ │ ├── save
+ * │ │ ├── restore
+ * │ │ :
+ * │ ├── vf2
+ * │ │ ├── ...
+ */
+
+static int from_file_read_to_vf_call(struct seq_file *s,
+ int (*call)(struct xe_device *, unsigned int))
+{
+ struct dentry *dent = file_dentry(s->file)->d_parent;
+ struct xe_device *xe = extract_xe(dent);
+ unsigned int vfid = extract_vfid(dent);
+ int ret;
+
+ xe_pm_runtime_get(xe);
+ ret = call(xe, vfid);
+ xe_pm_runtime_put(xe);
+
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static ssize_t from_file_write_to_vf_call(struct file *file, const char __user *userbuf,
+ size_t count, loff_t *ppos,
+ int (*call)(struct xe_device *, unsigned int))
+{
+ struct dentry *dent = file_dentry(file)->d_parent;
+ struct xe_device *xe = extract_xe(dent);
+ unsigned int vfid = extract_vfid(dent);
+ bool yes;
+ int ret;
+
+ if (*ppos)
+ return -EINVAL;
+ ret = kstrtobool_from_user(userbuf, count, &yes);
+ if (ret < 0)
+ return ret;
+ if (yes) {
+ xe_pm_runtime_get(xe);
+ ret = call(xe, vfid);
+ xe_pm_runtime_put(xe);
+ }
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+#define DEFINE_VF_CONTROL_ATTRIBUTE(OP) \
+static int OP##_show(struct seq_file *s, void *unused) \
+{ \
+ return 0; \
+} \
+static ssize_t OP##_write(struct file *file, const char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ return from_file_write_to_vf_call(file, userbuf, count, ppos, \
+ xe_sriov_pf_control_##OP); \
+} \
+DEFINE_SHOW_STORE_ATTRIBUTE(OP)
+
+#define DEFINE_VF_CONTROL_ATTRIBUTE_RW(OP) \
+static int OP##_show(struct seq_file *s, void *unused) \
+{ \
+ return from_file_read_to_vf_call(s, \
+ xe_sriov_pf_control_finish_##OP); \
+} \
+static ssize_t OP##_write(struct file *file, const char __user *userbuf, \
+ size_t count, loff_t *ppos) \
+{ \
+ return from_file_write_to_vf_call(file, userbuf, count, ppos, \
+ xe_sriov_pf_control_trigger_##OP); \
+} \
+DEFINE_SHOW_STORE_ATTRIBUTE(OP)
+
+DEFINE_VF_CONTROL_ATTRIBUTE(pause_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE(resume_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE(stop_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE(reset_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE_RW(save_vf);
+DEFINE_VF_CONTROL_ATTRIBUTE_RW(restore_vf);
+
+static ssize_t data_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
+{
+ struct dentry *dent = file_dentry(file)->d_parent;
+ struct xe_device *xe = extract_xe(dent);
+ unsigned int vfid = extract_vfid(dent);
+
+ if (*pos)
+ return -ESPIPE;
+
+ return xe_sriov_pf_migration_write(xe, vfid, buf, count);
+}
+
+static ssize_t data_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct dentry *dent = file_dentry(file)->d_parent;
+ struct xe_device *xe = extract_xe(dent);
+ unsigned int vfid = extract_vfid(dent);
+
+ if (*ppos)
+ return -ESPIPE;
+
+ return xe_sriov_pf_migration_read(xe, vfid, buf, count);
+}
+
+static const struct file_operations data_vf_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = data_write,
+ .read = data_read,
+ .llseek = default_llseek,
+};
+
+static ssize_t size_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct dentry *dent = file_dentry(file)->d_parent;
+ struct xe_device *xe = extract_xe(dent);
+ unsigned int vfid = extract_vfid(dent);
+ char buf[21];
+ ssize_t ret;
+ int len;
+
+ xe_pm_runtime_get(xe);
+ ret = xe_sriov_pf_migration_size(xe, vfid);
+ xe_pm_runtime_put(xe);
+ if (ret < 0)
+ return ret;
+
+ len = scnprintf(buf, sizeof(buf), "%zd\n", ret);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations size_vf_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = size_read,
+ .llseek = default_llseek,
+};
+
+static void pf_populate_vf(struct xe_device *xe, struct dentry *vfdent)
+{
+ debugfs_create_file("pause", 0200, vfdent, xe, &pause_vf_fops);
+ debugfs_create_file("resume", 0200, vfdent, xe, &resume_vf_fops);
+ debugfs_create_file("stop", 0200, vfdent, xe, &stop_vf_fops);
+ debugfs_create_file("reset", 0200, vfdent, xe, &reset_vf_fops);
+ debugfs_create_file("save", 0600, vfdent, xe, &save_vf_fops);
+ debugfs_create_file("restore", 0600, vfdent, xe, &restore_vf_fops);
+ debugfs_create_file("migration_data", 0600, vfdent, xe, &data_vf_fops);
+ debugfs_create_file("migration_size", 0400, vfdent, xe, &size_vf_fops);
+}
+
+static void pf_populate_with_tiles(struct xe_device *xe, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_tile *tile;
+ unsigned int id;
+
+ for_each_tile(tile, xe, id)
+ xe_tile_sriov_pf_debugfs_populate(tile, dent, vfid);
+}
+
+/**
+ * xe_sriov_pf_debugfs_register - Register PF debugfs attributes.
+ * @xe: the &xe_device
+ * @root: the root &dentry
+ *
+ * Create separate directory that will contain all SR-IOV related files,
+ * organized per each SR-IOV function (PF, VF1, VF2, ..., VFn).
+ */
+void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root)
+{
+ int totalvfs = xe_sriov_pf_get_totalvfs(xe);
+ struct dentry *pfdent;
+ struct dentry *vfdent;
+ struct dentry *dent;
+ char vfname[16]; /* should be more than enough for "vf%u\0" and VFID(UINT_MAX) */
+ unsigned int n;
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── ...
+ */
+ dent = debugfs_create_dir("sriov", root);
+ if (IS_ERR(dent))
+ return;
+ dent->d_inode->i_private = xe;
+
+ pf_populate_root(xe, dent);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ │ ├── ...
+ */
+ pfdent = debugfs_create_dir("pf", dent);
+ if (IS_ERR(pfdent))
+ return;
+ pfdent->d_inode->i_private = xe;
+
+ pf_populate_pf(xe, pfdent);
+ pf_populate_with_tiles(xe, pfdent, PFID);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * │ ├── vf2 # d_inode->i_private = VFID(2)
+ * │ ├── ...
+ */
+ for (n = 1; n <= totalvfs; n++) {
+ snprintf(vfname, sizeof(vfname), "vf%u", VFID(n));
+ vfdent = debugfs_create_dir(vfname, dent);
+ if (IS_ERR(vfdent))
+ return;
+ vfdent->d_inode->i_private = (void *)(uintptr_t)VFID(n);
+
+ pf_populate_vf(xe, vfdent);
+ pf_populate_with_tiles(xe, vfdent, VFID(n));
+ }
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h
new file mode 100644
index 000000000000..93db13585b82
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_debugfs.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_DEBUGFS_H_
+#define _XE_SRIOV_PF_DEBUGFS_H_
+
+struct dentry;
+struct xe_device;
+
+#ifdef CONFIG_PCI_IOV
+void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root);
+#else
+static inline void xe_sriov_pf_debugfs_register(struct xe_device *xe, struct dentry *root) { }
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h b/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h
index dd1df950b021..9054fdc34597 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_helpers.h
@@ -37,10 +37,37 @@ static inline int xe_sriov_pf_get_totalvfs(struct xe_device *xe)
return xe->sriov.pf.driver_max_vfs;
}
+/**
+ * xe_sriov_pf_num_vfs() - Number of enabled VFs on the PF.
+ * @xe: the PF &xe_device
+ *
+ * Return: Number of enabled VFs on the PF.
+ */
+static inline unsigned int xe_sriov_pf_num_vfs(const struct xe_device *xe)
+{
+ return pci_num_vf(to_pci_dev(xe->drm.dev));
+}
+
+/**
+ * xe_sriov_pf_admin_only() - Check if PF is mainly used for VFs administration.
+ * @xe: the PF &xe_device
+ *
+ * Return: True if PF is mainly used for VFs administration.
+ */
+static inline bool xe_sriov_pf_admin_only(const struct xe_device *xe)
+{
+ return !xe->info.probe_display;
+}
+
static inline struct mutex *xe_sriov_pf_master_mutex(struct xe_device *xe)
{
xe_assert(xe, IS_SRIOV_PF(xe));
return &xe->sriov.pf.master_lock;
}
+int xe_sriov_pf_arm_guard(struct xe_device *xe, struct xe_guard *guard,
+ bool write, void *who);
+void xe_sriov_pf_disarm_guard(struct xe_device *xe, struct xe_guard *guard,
+ bool write, void *who);
+
#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_migration.c b/drivers/gpu/drm/xe/xe_sriov_pf_migration.c
new file mode 100644
index 000000000000..6c4b16409cc9
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_migration.c
@@ -0,0 +1,365 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <drm/drm_managed.h>
+
+#include "xe_device.h"
+#include "xe_gt_sriov_pf_control.h"
+#include "xe_gt_sriov_pf_migration.h"
+#include "xe_pm.h"
+#include "xe_sriov.h"
+#include "xe_sriov_packet.h"
+#include "xe_sriov_packet_types.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_migration.h"
+#include "xe_sriov_printk.h"
+
+static struct xe_sriov_migration_state *pf_pick_migration(struct xe_device *xe, unsigned int vfid)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, vfid <= xe_sriov_pf_get_totalvfs(xe));
+
+ return &xe->sriov.pf.vfs[vfid].migration;
+}
+
+/**
+ * xe_sriov_pf_migration_waitqueue() - Get waitqueue for migration.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * Return: pointer to the migration waitqueue.
+ */
+wait_queue_head_t *xe_sriov_pf_migration_waitqueue(struct xe_device *xe, unsigned int vfid)
+{
+ return &pf_pick_migration(xe, vfid)->wq;
+}
+
+/**
+ * xe_sriov_pf_migration_supported() - Check if SR-IOV VF migration is supported by the device
+ * @xe: the &xe_device
+ *
+ * Return: true if migration is supported, false otherwise
+ */
+bool xe_sriov_pf_migration_supported(struct xe_device *xe)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ return IS_ENABLED(CONFIG_DRM_XE_DEBUG) || !xe->sriov.pf.migration.disabled;
+}
+
+/**
+ * xe_sriov_pf_migration_disable() - Turn off SR-IOV VF migration support on PF.
+ * @xe: the &xe_device instance.
+ * @fmt: format string for the log message, to be combined with following VAs.
+ */
+void xe_sriov_pf_migration_disable(struct xe_device *xe, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list va_args;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ va_start(va_args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &va_args;
+ xe_sriov_notice(xe, "migration %s: %pV\n",
+ IS_ENABLED(CONFIG_DRM_XE_DEBUG) ?
+ "missing prerequisite" : "disabled",
+ &vaf);
+ va_end(va_args);
+
+ xe->sriov.pf.migration.disabled = true;
+}
+
+static void pf_migration_check_support(struct xe_device *xe)
+{
+ if (!xe_device_has_memirq(xe))
+ xe_sriov_pf_migration_disable(xe, "requires memory-based IRQ support");
+}
+
+static void pf_migration_cleanup(void *arg)
+{
+ struct xe_sriov_migration_state *migration = arg;
+
+ xe_sriov_packet_free(migration->pending);
+ xe_sriov_packet_free(migration->trailer);
+ xe_sriov_packet_free(migration->descriptor);
+}
+
+/**
+ * xe_sriov_pf_migration_init() - Initialize support for SR-IOV VF migration.
+ * @xe: the &xe_device
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_migration_init(struct xe_device *xe)
+{
+ unsigned int n, totalvfs;
+ int err;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ pf_migration_check_support(xe);
+
+ if (!xe_sriov_pf_migration_supported(xe))
+ return 0;
+
+ totalvfs = xe_sriov_pf_get_totalvfs(xe);
+ for (n = 1; n <= totalvfs; n++) {
+ struct xe_sriov_migration_state *migration = pf_pick_migration(xe, n);
+
+ err = drmm_mutex_init(&xe->drm, &migration->lock);
+ if (err)
+ return err;
+
+ init_waitqueue_head(&migration->wq);
+
+ err = devm_add_action_or_reset(xe->drm.dev, pf_migration_cleanup, migration);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static bool pf_migration_data_ready(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_gt *gt;
+ u8 gt_id;
+
+ for_each_gt(gt, xe, gt_id) {
+ if (xe_gt_sriov_pf_control_check_save_failed(gt, vfid) ||
+ xe_gt_sriov_pf_control_check_save_data_done(gt, vfid) ||
+ !xe_gt_sriov_pf_migration_ring_empty(gt, vfid))
+ return true;
+ }
+
+ return false;
+}
+
+static struct xe_sriov_packet *
+pf_migration_consume(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_sriov_packet *data;
+ bool more_data = false;
+ struct xe_gt *gt;
+ u8 gt_id;
+
+ for_each_gt(gt, xe, gt_id) {
+ data = xe_gt_sriov_pf_migration_save_consume(gt, vfid);
+ if (data && PTR_ERR(data) != EAGAIN)
+ return data;
+ if (PTR_ERR(data) == -EAGAIN)
+ more_data = true;
+ }
+
+ if (!more_data)
+ return NULL;
+
+ return ERR_PTR(-EAGAIN);
+}
+
+/**
+ * xe_sriov_pf_migration_save_consume() - Consume a VF migration data packet from the device.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ *
+ * Called by the save migration data consumer (userspace) when
+ * processing migration data.
+ * If there is no migration data to process, wait until more data is available.
+ *
+ * Return: Pointer to &xe_sriov_packet on success,
+ * NULL if ring is empty and no more migration data is expected,
+ * ERR_PTR value in case of error.
+ */
+struct xe_sriov_packet *
+xe_sriov_pf_migration_save_consume(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_sriov_migration_state *migration = pf_pick_migration(xe, vfid);
+ struct xe_sriov_packet *data;
+ int ret;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ for (;;) {
+ data = pf_migration_consume(xe, vfid);
+ if (PTR_ERR(data) != -EAGAIN)
+ break;
+
+ ret = wait_event_interruptible(migration->wq,
+ pf_migration_data_ready(xe, vfid));
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+ return data;
+}
+
+static int pf_handle_descriptor(struct xe_device *xe, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ int ret;
+
+ if (data->hdr.tile_id != 0 || data->hdr.gt_id != 0)
+ return -EINVAL;
+
+ ret = xe_sriov_packet_process_descriptor(xe, vfid, data);
+ if (ret)
+ return ret;
+
+ xe_sriov_packet_free(data);
+
+ return 0;
+}
+
+static int pf_handle_trailer(struct xe_device *xe, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ struct xe_gt *gt;
+ u8 gt_id;
+
+ if (data->hdr.tile_id != 0 || data->hdr.gt_id != 0)
+ return -EINVAL;
+ if (data->hdr.offset != 0 || data->hdr.size != 0 || data->buff || data->bo)
+ return -EINVAL;
+
+ xe_sriov_packet_free(data);
+
+ for_each_gt(gt, xe, gt_id)
+ xe_gt_sriov_pf_control_restore_data_done(gt, vfid);
+
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_migration_restore_produce() - Produce a VF migration data packet to the device.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ * @data: Pointer to &xe_sriov_packet
+ *
+ * Called by the restore migration data producer (userspace) when processing
+ * migration data.
+ * If the underlying data structure is full, wait until there is space.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_migration_restore_produce(struct xe_device *xe, unsigned int vfid,
+ struct xe_sriov_packet *data)
+{
+ struct xe_gt *gt;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ if (data->hdr.type == XE_SRIOV_PACKET_TYPE_DESCRIPTOR)
+ return pf_handle_descriptor(xe, vfid, data);
+ if (data->hdr.type == XE_SRIOV_PACKET_TYPE_TRAILER)
+ return pf_handle_trailer(xe, vfid, data);
+
+ gt = xe_device_get_gt(xe, data->hdr.gt_id);
+ if (!gt || data->hdr.tile_id != gt->tile->id || data->hdr.type == 0) {
+ xe_sriov_err_ratelimited(xe, "Received invalid restore packet for VF%u (type:%u, tile:%u, GT:%u)\n",
+ vfid, data->hdr.type, data->hdr.tile_id, data->hdr.gt_id);
+ return -EINVAL;
+ }
+
+ return xe_gt_sriov_pf_migration_restore_produce(gt, vfid, data);
+}
+
+/**
+ * xe_sriov_pf_migration_read() - Read migration data from the device.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ * @buf: start address of userspace buffer
+ * @len: requested read size from userspace
+ *
+ * Return: number of bytes that has been successfully read,
+ * 0 if no more migration data is available,
+ * -errno on failure.
+ */
+ssize_t xe_sriov_pf_migration_read(struct xe_device *xe, unsigned int vfid,
+ char __user *buf, size_t len)
+{
+ struct xe_sriov_migration_state *migration = pf_pick_migration(xe, vfid);
+ ssize_t ret, consumed = 0;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ scoped_cond_guard(mutex_intr, return -EINTR, &migration->lock) {
+ while (consumed < len) {
+ ret = xe_sriov_packet_read_single(xe, vfid, buf, len - consumed);
+ if (ret == -ENODATA)
+ break;
+ if (ret < 0)
+ return ret;
+
+ consumed += ret;
+ buf += ret;
+ }
+ }
+
+ return consumed;
+}
+
+/**
+ * xe_sriov_pf_migration_write() - Write migration data to the device.
+ * @xe: the &xe_device
+ * @vfid: the VF identifier
+ * @buf: start address of userspace buffer
+ * @len: requested write size from userspace
+ *
+ * Return: number of bytes that has been successfully written,
+ * -errno on failure.
+ */
+ssize_t xe_sriov_pf_migration_write(struct xe_device *xe, unsigned int vfid,
+ const char __user *buf, size_t len)
+{
+ struct xe_sriov_migration_state *migration = pf_pick_migration(xe, vfid);
+ ssize_t ret, produced = 0;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ scoped_cond_guard(mutex_intr, return -EINTR, &migration->lock) {
+ while (produced < len) {
+ ret = xe_sriov_packet_write_single(xe, vfid, buf, len - produced);
+ if (ret < 0)
+ return ret;
+
+ produced += ret;
+ buf += ret;
+ }
+ }
+
+ return produced;
+}
+
+/**
+ * xe_sriov_pf_migration_size() - Total size of migration data from all components within a device
+ * @xe: the &xe_device
+ * @vfid: the VF identifier (can't be 0)
+ *
+ * This function is for PF only.
+ *
+ * Return: total migration data size in bytes or a negative error code on failure.
+ */
+ssize_t xe_sriov_pf_migration_size(struct xe_device *xe, unsigned int vfid)
+{
+ size_t size = 0;
+ struct xe_gt *gt;
+ ssize_t ret;
+ u8 gt_id;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, vfid);
+
+ for_each_gt(gt, xe, gt_id) {
+ ret = xe_gt_sriov_pf_migration_size(gt, vfid);
+ if (ret < 0)
+ return ret;
+
+ size += ret;
+ }
+
+ return size;
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_migration.h b/drivers/gpu/drm/xe/xe_sriov_pf_migration.h
new file mode 100644
index 000000000000..f8f408df8481
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_migration.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_MIGRATION_H_
+#define _XE_SRIOV_PF_MIGRATION_H_
+
+#include <linux/types.h>
+#include <linux/wait.h>
+
+struct xe_device;
+struct xe_sriov_packet;
+
+int xe_sriov_pf_migration_init(struct xe_device *xe);
+bool xe_sriov_pf_migration_supported(struct xe_device *xe);
+void xe_sriov_pf_migration_disable(struct xe_device *xe, const char *fmt, ...);
+int xe_sriov_pf_migration_restore_produce(struct xe_device *xe, unsigned int vfid,
+ struct xe_sriov_packet *data);
+struct xe_sriov_packet *
+xe_sriov_pf_migration_save_consume(struct xe_device *xe, unsigned int vfid);
+ssize_t xe_sriov_pf_migration_size(struct xe_device *xe, unsigned int vfid);
+wait_queue_head_t *xe_sriov_pf_migration_waitqueue(struct xe_device *xe, unsigned int vfid);
+
+ssize_t xe_sriov_pf_migration_read(struct xe_device *xe, unsigned int vfid,
+ char __user *buf, size_t len);
+ssize_t xe_sriov_pf_migration_write(struct xe_device *xe, unsigned int vfid,
+ const char __user *buf, size_t len);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_migration_types.h b/drivers/gpu/drm/xe/xe_sriov_pf_migration_types.h
new file mode 100644
index 000000000000..7d9a8a278d91
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_migration_types.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_MIGRATION_TYPES_H_
+#define _XE_SRIOV_PF_MIGRATION_TYPES_H_
+
+#include <linux/types.h>
+#include <linux/mutex_types.h>
+#include <linux/wait.h>
+
+/**
+ * struct xe_sriov_pf_migration - Xe device level VF migration data
+ */
+struct xe_sriov_pf_migration {
+ /** @disabled: indicates whether VF migration feature is disabled */
+ bool disabled;
+};
+
+/**
+ * struct xe_sriov_migration_state - Per VF device-level migration related data
+ */
+struct xe_sriov_migration_state {
+ /** @wq: waitqueue used to avoid busy-waiting for snapshot production/consumption */
+ wait_queue_head_t wq;
+ /** @lock: Mutex protecting the migration data */
+ struct mutex lock;
+ /** @pending: currently processed data packet of VF resource */
+ struct xe_sriov_packet *pending;
+ /** @trailer: data packet used to indicate the end of stream */
+ struct xe_sriov_packet *trailer;
+ /** @descriptor: data packet containing the metadata describing the device */
+ struct xe_sriov_packet *descriptor;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_provision.c b/drivers/gpu/drm/xe/xe_sriov_pf_provision.c
new file mode 100644
index 000000000000..01470c42e8a7
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_provision.c
@@ -0,0 +1,438 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include "xe_assert.h"
+#include "xe_device.h"
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_gt_sriov_pf_policy.h"
+#include "xe_sriov.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_provision.h"
+#include "xe_sriov_pf_provision_types.h"
+#include "xe_sriov_printk.h"
+
+static const char *mode_to_string(enum xe_sriov_provisioning_mode mode)
+{
+ switch (mode) {
+ case XE_SRIOV_PROVISIONING_MODE_AUTO:
+ return "auto";
+ case XE_SRIOV_PROVISIONING_MODE_CUSTOM:
+ return "custom";
+ default:
+ return "<invalid>";
+ }
+}
+
+static bool pf_auto_provisioning_mode(struct xe_device *xe)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ return xe->sriov.pf.provision.mode == XE_SRIOV_PROVISIONING_MODE_AUTO;
+}
+
+static bool pf_needs_provisioning(struct xe_gt *gt, unsigned int num_vfs)
+{
+ unsigned int n;
+
+ for (n = 1; n <= num_vfs; n++)
+ if (!xe_gt_sriov_pf_config_is_empty(gt, n))
+ return false;
+
+ return true;
+}
+
+static int pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ if (!pf_needs_provisioning(gt, num_vfs))
+ return -EUCLEAN;
+ err = xe_gt_sriov_pf_config_set_fair(gt, VFID(1), num_vfs);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+static void pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ unsigned int n;
+
+ for_each_gt(gt, xe, id)
+ for (n = 1; n <= num_vfs; n++)
+ xe_gt_sriov_pf_config_release(gt, n, true);
+}
+
+static void pf_unprovision_all_vfs(struct xe_device *xe)
+{
+ pf_unprovision_vfs(xe, xe_sriov_pf_get_totalvfs(xe));
+}
+
+/**
+ * xe_sriov_pf_provision_vfs() - Provision VFs in auto-mode.
+ * @xe: the PF &xe_device
+ * @num_vfs: the number of VFs to auto-provision
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ if (!pf_auto_provisioning_mode(xe))
+ return 0;
+
+ return pf_provision_vfs(xe, num_vfs);
+}
+
+/**
+ * xe_sriov_pf_unprovision_vfs() - Unprovision VFs in auto-mode.
+ * @xe: the PF &xe_device
+ * @num_vfs: the number of VFs to unprovision
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ if (!pf_auto_provisioning_mode(xe))
+ return 0;
+
+ pf_unprovision_vfs(xe, num_vfs);
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_provision_set_mode() - Change VFs provision mode.
+ * @xe: the PF &xe_device
+ * @mode: the new VFs provisioning mode
+ *
+ * When changing from AUTO to CUSTOM mode, any already allocated VFs resources
+ * will remain allocated and will not be released upon VFs disabling.
+ *
+ * When changing back to AUTO mode, if VFs are not enabled, already allocated
+ * VFs resources will be immediately released. If VFs are still enabled, such
+ * mode change is rejected.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_set_mode(struct xe_device *xe, enum xe_sriov_provisioning_mode mode)
+{
+ xe_assert(xe, IS_SRIOV_PF(xe));
+
+ if (mode == xe->sriov.pf.provision.mode)
+ return 0;
+
+ if (mode == XE_SRIOV_PROVISIONING_MODE_AUTO) {
+ if (xe_sriov_pf_num_vfs(xe)) {
+ xe_sriov_dbg(xe, "can't restore %s: VFs must be disabled!\n",
+ mode_to_string(mode));
+ return -EBUSY;
+ }
+ pf_unprovision_all_vfs(xe);
+ }
+
+ xe_sriov_dbg(xe, "mode %s changed to %s by %ps\n",
+ mode_to_string(xe->sriov.pf.provision.mode),
+ mode_to_string(mode), __builtin_return_address(0));
+ xe->sriov.pf.provision.mode = mode;
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_provision_bulk_apply_eq() - Change execution quantum for all VFs and PF.
+ * @xe: the PF &xe_device
+ * @eq: execution quantum in [ms] to set
+ *
+ * Change execution quantum (EQ) provisioning on all tiles/GTs.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_bulk_apply_eq(struct xe_device *xe, u32 eq)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ guard(mutex)(xe_sriov_pf_master_mutex(xe));
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_config_bulk_set_exec_quantum_locked(gt, eq);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_provision_apply_vf_eq() - Change VF's execution quantum.
+ * @xe: the PF &xe_device
+ * @vfid: the VF identifier
+ * @eq: execution quantum in [ms] to set
+ *
+ * Change VF's execution quantum (EQ) provisioning on all tiles/GTs.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_apply_vf_eq(struct xe_device *xe, unsigned int vfid, u32 eq)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ guard(mutex)(xe_sriov_pf_master_mutex(xe));
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_config_set_exec_quantum_locked(gt, vfid, eq);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+static int pf_report_unclean(struct xe_gt *gt, unsigned int vfid,
+ const char *what, u32 found, u32 expected)
+{
+ char name[8];
+
+ xe_sriov_dbg(gt_to_xe(gt), "%s on GT%u has %s=%u (expected %u)\n",
+ xe_sriov_function_name(vfid, name, sizeof(name)),
+ gt->info.id, what, found, expected);
+ return -EUCLEAN;
+}
+
+/**
+ * xe_sriov_pf_provision_query_vf_eq() - Query VF's execution quantum.
+ * @xe: the PF &xe_device
+ * @vfid: the VF identifier
+ * @eq: placeholder for the returned execution quantum in [ms]
+ *
+ * Query VF's execution quantum (EQ) provisioning from all tiles/GTs.
+ * If values across tiles/GTs are inconsistent then -EUCLEAN error will be returned.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_query_vf_eq(struct xe_device *xe, unsigned int vfid, u32 *eq)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int count = 0;
+ u32 value;
+
+ guard(mutex)(xe_sriov_pf_master_mutex(xe));
+
+ for_each_gt(gt, xe, id) {
+ value = xe_gt_sriov_pf_config_get_exec_quantum_locked(gt, vfid);
+ if (!count++)
+ *eq = value;
+ else if (value != *eq)
+ return pf_report_unclean(gt, vfid, "EQ", value, *eq);
+ }
+
+ return !count ? -ENODATA : 0;
+}
+
+/**
+ * xe_sriov_pf_provision_bulk_apply_pt() - Change preemption timeout for all VFs and PF.
+ * @xe: the PF &xe_device
+ * @pt: preemption timeout in [us] to set
+ *
+ * Change preemption timeout (PT) provisioning on all tiles/GTs.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_bulk_apply_pt(struct xe_device *xe, u32 pt)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ guard(mutex)(xe_sriov_pf_master_mutex(xe));
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_config_bulk_set_preempt_timeout_locked(gt, pt);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_provision_apply_vf_pt() - Change VF's preemption timeout.
+ * @xe: the PF &xe_device
+ * @vfid: the VF identifier
+ * @pt: preemption timeout in [us] to set
+ *
+ * Change VF's preemption timeout (PT) provisioning on all tiles/GTs.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_apply_vf_pt(struct xe_device *xe, unsigned int vfid, u32 pt)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ guard(mutex)(xe_sriov_pf_master_mutex(xe));
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_config_set_preempt_timeout_locked(gt, vfid, pt);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_provision_query_vf_pt() - Query VF's preemption timeout.
+ * @xe: the PF &xe_device
+ * @vfid: the VF identifier
+ * @pt: placeholder for the returned preemption timeout in [us]
+ *
+ * Query VF's preemption timeout (PT) provisioning from all tiles/GTs.
+ * If values across tiles/GTs are inconsistent then -EUCLEAN error will be returned.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_query_vf_pt(struct xe_device *xe, unsigned int vfid, u32 *pt)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int count = 0;
+ u32 value;
+
+ guard(mutex)(xe_sriov_pf_master_mutex(xe));
+
+ for_each_gt(gt, xe, id) {
+ value = xe_gt_sriov_pf_config_get_preempt_timeout_locked(gt, vfid);
+ if (!count++)
+ *pt = value;
+ else if (value != *pt)
+ return pf_report_unclean(gt, vfid, "PT", value, *pt);
+ }
+
+ return !count ? -ENODATA : 0;
+}
+
+/**
+ * xe_sriov_pf_provision_bulk_apply_priority() - Change scheduling priority of all VFs and PF.
+ * @xe: the PF &xe_device
+ * @prio: scheduling priority to set
+ *
+ * Change the scheduling priority provisioning on all tiles/GTs.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_bulk_apply_priority(struct xe_device *xe, u32 prio)
+{
+ bool sched_if_idle;
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ /*
+ * Currently, priority changes that involves VFs are only allowed using
+ * the 'sched_if_idle' policy KLV, so only LOW and NORMAL are supported.
+ */
+ xe_assert(xe, prio < GUC_SCHED_PRIORITY_HIGH);
+ sched_if_idle = prio == GUC_SCHED_PRIORITY_NORMAL;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_policy_set_sched_if_idle(gt, sched_if_idle);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_provision_apply_vf_priority() - Change VF's scheduling priority.
+ * @xe: the PF &xe_device
+ * @vfid: the VF identifier
+ * @prio: scheduling priority to set
+ *
+ * Change VF's scheduling priority provisioning on all tiles/GTs.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_apply_vf_priority(struct xe_device *xe, unsigned int vfid, u32 prio)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int result = 0;
+ int err;
+
+ for_each_gt(gt, xe, id) {
+ err = xe_gt_sriov_pf_config_set_sched_priority(gt, vfid, prio);
+ result = result ?: err;
+ }
+
+ return result;
+}
+
+/**
+ * xe_sriov_pf_provision_query_vf_priority() - Query VF's scheduling priority.
+ * @xe: the PF &xe_device
+ * @vfid: the VF identifier
+ * @prio: placeholder for the returned scheduling priority
+ *
+ * Query VF's scheduling priority provisioning from all tiles/GTs.
+ * If values across tiles/GTs are inconsistent then -EUCLEAN error will be returned.
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_provision_query_vf_priority(struct xe_device *xe, unsigned int vfid, u32 *prio)
+{
+ struct xe_gt *gt;
+ unsigned int id;
+ int count = 0;
+ u32 value;
+
+ for_each_gt(gt, xe, id) {
+ value = xe_gt_sriov_pf_config_get_sched_priority(gt, vfid);
+ if (!count++)
+ *prio = value;
+ else if (value != *prio)
+ return pf_report_unclean(gt, vfid, "priority", value, *prio);
+ }
+
+ return !count ? -ENODATA : 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_provision.h b/drivers/gpu/drm/xe/xe_sriov_pf_provision.h
new file mode 100644
index 000000000000..bccf23d51396
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_provision.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_PROVISION_H_
+#define _XE_SRIOV_PF_PROVISION_H_
+
+#include <linux/types.h>
+
+#include "xe_sriov_pf_provision_types.h"
+
+struct xe_device;
+
+int xe_sriov_pf_provision_bulk_apply_eq(struct xe_device *xe, u32 eq);
+int xe_sriov_pf_provision_apply_vf_eq(struct xe_device *xe, unsigned int vfid, u32 eq);
+int xe_sriov_pf_provision_query_vf_eq(struct xe_device *xe, unsigned int vfid, u32 *eq);
+
+int xe_sriov_pf_provision_bulk_apply_pt(struct xe_device *xe, u32 pt);
+int xe_sriov_pf_provision_apply_vf_pt(struct xe_device *xe, unsigned int vfid, u32 pt);
+int xe_sriov_pf_provision_query_vf_pt(struct xe_device *xe, unsigned int vfid, u32 *pt);
+
+int xe_sriov_pf_provision_bulk_apply_priority(struct xe_device *xe, u32 prio);
+int xe_sriov_pf_provision_apply_vf_priority(struct xe_device *xe, unsigned int vfid, u32 prio);
+int xe_sriov_pf_provision_query_vf_priority(struct xe_device *xe, unsigned int vfid, u32 *prio);
+
+int xe_sriov_pf_provision_vfs(struct xe_device *xe, unsigned int num_vfs);
+int xe_sriov_pf_unprovision_vfs(struct xe_device *xe, unsigned int num_vfs);
+
+int xe_sriov_pf_provision_set_mode(struct xe_device *xe, enum xe_sriov_provisioning_mode mode);
+
+/**
+ * xe_sriov_pf_provision_set_custom_mode() - Change VFs provision mode to custom.
+ * @xe: the PF &xe_device
+ *
+ * This function can only be called on PF.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static inline int xe_sriov_pf_provision_set_custom_mode(struct xe_device *xe)
+{
+ return xe_sriov_pf_provision_set_mode(xe, XE_SRIOV_PROVISIONING_MODE_CUSTOM);
+}
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h b/drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h
new file mode 100644
index 000000000000..a847b8a4c4da
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_provision_types.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_PROVISION_TYPES_H_
+#define _XE_SRIOV_PF_PROVISION_TYPES_H_
+
+#include <linux/build_bug.h>
+
+/**
+ * enum xe_sriov_provisioning_mode - SR-IOV provisioning mode.
+ *
+ * @XE_SRIOV_PROVISIONING_MODE_AUTO: VFs are provisioned during VFs enabling.
+ * Any allocated resources to the VFs will be
+ * automatically released when disabling VFs.
+ * This is a default mode.
+ * @XE_SRIOV_PROVISIONING_MODE_CUSTOM: Explicit VFs provisioning using uABI interfaces.
+ * VFs resources remains allocated regardless if
+ * VFs are enabled or not.
+ */
+enum xe_sriov_provisioning_mode {
+ XE_SRIOV_PROVISIONING_MODE_AUTO,
+ XE_SRIOV_PROVISIONING_MODE_CUSTOM,
+};
+static_assert(XE_SRIOV_PROVISIONING_MODE_AUTO == 0);
+
+/**
+ * struct xe_sriov_pf_provision - Data used by the PF provisioning.
+ */
+struct xe_sriov_pf_provision {
+ /** @mode: selected provisioning mode. */
+ enum xe_sriov_provisioning_mode mode;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c
new file mode 100644
index 000000000000..c0b767ac735c
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.c
@@ -0,0 +1,647 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+#include <drm/drm_managed.h>
+
+#include "xe_assert.h"
+#include "xe_pci_sriov.h"
+#include "xe_pm.h"
+#include "xe_sriov.h"
+#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_control.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_provision.h"
+#include "xe_sriov_pf_sysfs.h"
+#include "xe_sriov_printk.h"
+
+static int emit_choice(char *buf, int choice, const char * const *array, size_t size)
+{
+ int pos = 0;
+ int n;
+
+ for (n = 0; n < size; n++) {
+ pos += sysfs_emit_at(buf, pos, "%s%s%s%s",
+ n ? " " : "",
+ n == choice ? "[" : "",
+ array[n],
+ n == choice ? "]" : "");
+ }
+ pos += sysfs_emit_at(buf, pos, "\n");
+
+ return pos;
+}
+
+/*
+ * /sys/bus/pci/drivers/xe/BDF/
+ * :
+ * ├── sriov_admin/
+ * ├── ...
+ * ├── .bulk_profile
+ * │ ├── exec_quantum_ms
+ * │ ├── preempt_timeout_us
+ * │ └── sched_priority
+ * ├── pf/
+ * │ ├── ...
+ * │ ├── device -> ../../../BDF
+ * │ └── profile
+ * │ ├── exec_quantum_ms
+ * │ ├── preempt_timeout_us
+ * │ └── sched_priority
+ * ├── vf1/
+ * │ ├── ...
+ * │ ├── device -> ../../../BDF.1
+ * │ ├── stop
+ * │ └── profile
+ * │ ├── exec_quantum_ms
+ * │ ├── preempt_timeout_us
+ * │ └── sched_priority
+ * ├── vf2/
+ * :
+ * └── vfN/
+ */
+
+struct xe_sriov_kobj {
+ struct kobject base;
+ struct xe_device *xe;
+ unsigned int vfid;
+};
+#define to_xe_sriov_kobj(p) container_of_const((p), struct xe_sriov_kobj, base)
+
+struct xe_sriov_dev_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct xe_device *xe, char *buf);
+ ssize_t (*store)(struct xe_device *xe, const char *buf, size_t count);
+};
+#define to_xe_sriov_dev_attr(p) container_of_const((p), struct xe_sriov_dev_attr, attr)
+
+#define XE_SRIOV_DEV_ATTR(NAME) \
+struct xe_sriov_dev_attr xe_sriov_dev_attr_##NAME = \
+ __ATTR(NAME, 0644, xe_sriov_dev_attr_##NAME##_show, xe_sriov_dev_attr_##NAME##_store)
+
+#define XE_SRIOV_DEV_ATTR_RO(NAME) \
+struct xe_sriov_dev_attr xe_sriov_dev_attr_##NAME = \
+ __ATTR(NAME, 0444, xe_sriov_dev_attr_##NAME##_show, NULL)
+
+#define XE_SRIOV_DEV_ATTR_WO(NAME) \
+struct xe_sriov_dev_attr xe_sriov_dev_attr_##NAME = \
+ __ATTR(NAME, 0200, NULL, xe_sriov_dev_attr_##NAME##_store)
+
+struct xe_sriov_vf_attr {
+ struct attribute attr;
+ ssize_t (*show)(struct xe_device *xe, unsigned int vfid, char *buf);
+ ssize_t (*store)(struct xe_device *xe, unsigned int vfid, const char *buf, size_t count);
+};
+#define to_xe_sriov_vf_attr(p) container_of_const((p), struct xe_sriov_vf_attr, attr)
+
+#define XE_SRIOV_VF_ATTR(NAME) \
+struct xe_sriov_vf_attr xe_sriov_vf_attr_##NAME = \
+ __ATTR(NAME, 0644, xe_sriov_vf_attr_##NAME##_show, xe_sriov_vf_attr_##NAME##_store)
+
+#define XE_SRIOV_VF_ATTR_RO(NAME) \
+struct xe_sriov_vf_attr xe_sriov_vf_attr_##NAME = \
+ __ATTR(NAME, 0444, xe_sriov_vf_attr_##NAME##_show, NULL)
+
+#define XE_SRIOV_VF_ATTR_WO(NAME) \
+struct xe_sriov_vf_attr xe_sriov_vf_attr_##NAME = \
+ __ATTR(NAME, 0200, NULL, xe_sriov_vf_attr_##NAME##_store)
+
+/* device level attributes go here */
+
+#define DEFINE_SIMPLE_BULK_PROVISIONING_SRIOV_DEV_ATTR_WO(NAME, ITEM, TYPE) \
+ \
+static ssize_t xe_sriov_dev_attr_##NAME##_store(struct xe_device *xe, \
+ const char *buf, size_t count) \
+{ \
+ TYPE value; \
+ int err; \
+ \
+ err = kstrto##TYPE(buf, 0, &value); \
+ if (err) \
+ return err; \
+ \
+ err = xe_sriov_pf_provision_bulk_apply_##ITEM(xe, value); \
+ return err ?: count; \
+} \
+ \
+static XE_SRIOV_DEV_ATTR_WO(NAME)
+
+DEFINE_SIMPLE_BULK_PROVISIONING_SRIOV_DEV_ATTR_WO(exec_quantum_ms, eq, u32);
+DEFINE_SIMPLE_BULK_PROVISIONING_SRIOV_DEV_ATTR_WO(preempt_timeout_us, pt, u32);
+
+static const char * const sched_priority_names[] = {
+ [GUC_SCHED_PRIORITY_LOW] = "low",
+ [GUC_SCHED_PRIORITY_NORMAL] = "normal",
+ [GUC_SCHED_PRIORITY_HIGH] = "high",
+};
+
+static bool sched_priority_change_allowed(unsigned int vfid)
+{
+ /* As of today GuC FW allows to selectively change only the PF priority. */
+ return vfid == PFID;
+}
+
+static bool sched_priority_high_allowed(unsigned int vfid)
+{
+ /* As of today GuC FW allows to select 'high' priority only for the PF. */
+ return vfid == PFID;
+}
+
+static bool sched_priority_bulk_high_allowed(struct xe_device *xe)
+{
+ /* all VFs are equal - it's sufficient to check VF1 only */
+ return sched_priority_high_allowed(VFID(1));
+}
+
+static ssize_t xe_sriov_dev_attr_sched_priority_store(struct xe_device *xe,
+ const char *buf, size_t count)
+{
+ size_t num_priorities = ARRAY_SIZE(sched_priority_names);
+ int match;
+ int err;
+
+ if (!sched_priority_bulk_high_allowed(xe))
+ num_priorities--;
+
+ match = __sysfs_match_string(sched_priority_names, num_priorities, buf);
+ if (match < 0)
+ return -EINVAL;
+
+ err = xe_sriov_pf_provision_bulk_apply_priority(xe, match);
+ return err ?: count;
+}
+
+static XE_SRIOV_DEV_ATTR_WO(sched_priority);
+
+static struct attribute *bulk_profile_dev_attrs[] = {
+ &xe_sriov_dev_attr_exec_quantum_ms.attr,
+ &xe_sriov_dev_attr_preempt_timeout_us.attr,
+ &xe_sriov_dev_attr_sched_priority.attr,
+ NULL
+};
+
+static const struct attribute_group bulk_profile_dev_attr_group = {
+ .name = ".bulk_profile",
+ .attrs = bulk_profile_dev_attrs,
+};
+
+static const struct attribute_group *xe_sriov_dev_attr_groups[] = {
+ &bulk_profile_dev_attr_group,
+ NULL
+};
+
+/* and VF-level attributes go here */
+
+#define DEFINE_SIMPLE_PROVISIONING_SRIOV_VF_ATTR(NAME, ITEM, TYPE, FORMAT) \
+static ssize_t xe_sriov_vf_attr_##NAME##_show(struct xe_device *xe, unsigned int vfid, \
+ char *buf) \
+{ \
+ TYPE value = 0; \
+ int err; \
+ \
+ err = xe_sriov_pf_provision_query_vf_##ITEM(xe, vfid, &value); \
+ if (err) \
+ return err; \
+ \
+ return sysfs_emit(buf, FORMAT, value); \
+} \
+ \
+static ssize_t xe_sriov_vf_attr_##NAME##_store(struct xe_device *xe, unsigned int vfid, \
+ const char *buf, size_t count) \
+{ \
+ TYPE value; \
+ int err; \
+ \
+ err = kstrto##TYPE(buf, 0, &value); \
+ if (err) \
+ return err; \
+ \
+ err = xe_sriov_pf_provision_apply_vf_##ITEM(xe, vfid, value); \
+ return err ?: count; \
+} \
+ \
+static XE_SRIOV_VF_ATTR(NAME)
+
+DEFINE_SIMPLE_PROVISIONING_SRIOV_VF_ATTR(exec_quantum_ms, eq, u32, "%u\n");
+DEFINE_SIMPLE_PROVISIONING_SRIOV_VF_ATTR(preempt_timeout_us, pt, u32, "%u\n");
+
+static ssize_t xe_sriov_vf_attr_sched_priority_show(struct xe_device *xe, unsigned int vfid,
+ char *buf)
+{
+ size_t num_priorities = ARRAY_SIZE(sched_priority_names);
+ u32 priority;
+ int err;
+
+ err = xe_sriov_pf_provision_query_vf_priority(xe, vfid, &priority);
+ if (err)
+ return err;
+
+ if (!sched_priority_high_allowed(vfid))
+ num_priorities--;
+
+ xe_assert(xe, priority < num_priorities);
+ return emit_choice(buf, priority, sched_priority_names, num_priorities);
+}
+
+static ssize_t xe_sriov_vf_attr_sched_priority_store(struct xe_device *xe, unsigned int vfid,
+ const char *buf, size_t count)
+{
+ size_t num_priorities = ARRAY_SIZE(sched_priority_names);
+ int match;
+ int err;
+
+ if (!sched_priority_change_allowed(vfid))
+ return -EOPNOTSUPP;
+
+ if (!sched_priority_high_allowed(vfid))
+ num_priorities--;
+
+ match = __sysfs_match_string(sched_priority_names, num_priorities, buf);
+ if (match < 0)
+ return -EINVAL;
+
+ err = xe_sriov_pf_provision_apply_vf_priority(xe, vfid, match);
+ return err ?: count;
+}
+
+static XE_SRIOV_VF_ATTR(sched_priority);
+
+static struct attribute *profile_vf_attrs[] = {
+ &xe_sriov_vf_attr_exec_quantum_ms.attr,
+ &xe_sriov_vf_attr_preempt_timeout_us.attr,
+ &xe_sriov_vf_attr_sched_priority.attr,
+ NULL
+};
+
+static umode_t profile_vf_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
+
+ if (attr == &xe_sriov_vf_attr_sched_priority.attr &&
+ !sched_priority_change_allowed(vkobj->vfid))
+ return attr->mode & 0444;
+
+ return attr->mode;
+}
+
+static const struct attribute_group profile_vf_attr_group = {
+ .name = "profile",
+ .attrs = profile_vf_attrs,
+ .is_visible = profile_vf_attr_is_visible,
+};
+
+#define DEFINE_SIMPLE_CONTROL_SRIOV_VF_ATTR(NAME) \
+ \
+static ssize_t xe_sriov_vf_attr_##NAME##_store(struct xe_device *xe, unsigned int vfid, \
+ const char *buf, size_t count) \
+{ \
+ bool yes; \
+ int err; \
+ \
+ if (!vfid) \
+ return -EPERM; \
+ \
+ err = kstrtobool(buf, &yes); \
+ if (err) \
+ return err; \
+ if (!yes) \
+ return count; \
+ \
+ err = xe_sriov_pf_control_##NAME##_vf(xe, vfid); \
+ return err ?: count; \
+} \
+ \
+static XE_SRIOV_VF_ATTR_WO(NAME)
+
+DEFINE_SIMPLE_CONTROL_SRIOV_VF_ATTR(stop);
+
+static struct attribute *control_vf_attrs[] = {
+ &xe_sriov_vf_attr_stop.attr,
+ NULL
+};
+
+static umode_t control_vf_attr_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
+
+ if (vkobj->vfid == PFID)
+ return 0;
+
+ return attr->mode;
+}
+
+static const struct attribute_group control_vf_attr_group = {
+ .attrs = control_vf_attrs,
+ .is_visible = control_vf_attr_is_visible,
+};
+
+static const struct attribute_group *xe_sriov_vf_attr_groups[] = {
+ &profile_vf_attr_group,
+ &control_vf_attr_group,
+ NULL
+};
+
+/* no user serviceable parts below */
+
+static struct kobject *create_xe_sriov_kobj(struct xe_device *xe, unsigned int vfid)
+{
+ struct xe_sriov_kobj *vkobj;
+
+ xe_sriov_pf_assert_vfid(xe, vfid);
+
+ vkobj = kzalloc(sizeof(*vkobj), GFP_KERNEL);
+ if (!vkobj)
+ return NULL;
+
+ vkobj->xe = xe;
+ vkobj->vfid = vfid;
+ return &vkobj->base;
+}
+
+static void release_xe_sriov_kobj(struct kobject *kobj)
+{
+ struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
+
+ kfree(vkobj);
+}
+
+static ssize_t xe_sriov_dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct xe_sriov_dev_attr *vattr = to_xe_sriov_dev_attr(attr);
+ struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
+ struct xe_device *xe = vkobj->xe;
+
+ if (!vattr->show)
+ return -EPERM;
+
+ return vattr->show(xe, buf);
+}
+
+static ssize_t xe_sriov_dev_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct xe_sriov_dev_attr *vattr = to_xe_sriov_dev_attr(attr);
+ struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
+ struct xe_device *xe = vkobj->xe;
+ ssize_t ret;
+
+ if (!vattr->store)
+ return -EPERM;
+
+ xe_pm_runtime_get(xe);
+ ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, buf, count);
+ xe_pm_runtime_put(xe);
+
+ return ret;
+}
+
+static ssize_t xe_sriov_vf_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ struct xe_sriov_vf_attr *vattr = to_xe_sriov_vf_attr(attr);
+ struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
+ struct xe_device *xe = vkobj->xe;
+ unsigned int vfid = vkobj->vfid;
+
+ xe_sriov_pf_assert_vfid(xe, vfid);
+
+ if (!vattr->show)
+ return -EPERM;
+
+ return vattr->show(xe, vfid, buf);
+}
+
+static ssize_t xe_sriov_vf_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct xe_sriov_vf_attr *vattr = to_xe_sriov_vf_attr(attr);
+ struct xe_sriov_kobj *vkobj = to_xe_sriov_kobj(kobj);
+ struct xe_device *xe = vkobj->xe;
+ unsigned int vfid = vkobj->vfid;
+ ssize_t ret;
+
+ xe_sriov_pf_assert_vfid(xe, vfid);
+
+ if (!vattr->store)
+ return -EPERM;
+
+ xe_pm_runtime_get(xe);
+ ret = xe_sriov_pf_wait_ready(xe) ?: vattr->store(xe, vfid, buf, count);
+ xe_pm_runtime_get(xe);
+
+ return ret;
+}
+
+static const struct sysfs_ops xe_sriov_dev_sysfs_ops = {
+ .show = xe_sriov_dev_attr_show,
+ .store = xe_sriov_dev_attr_store,
+};
+
+static const struct sysfs_ops xe_sriov_vf_sysfs_ops = {
+ .show = xe_sriov_vf_attr_show,
+ .store = xe_sriov_vf_attr_store,
+};
+
+static const struct kobj_type xe_sriov_dev_ktype = {
+ .release = release_xe_sriov_kobj,
+ .sysfs_ops = &xe_sriov_dev_sysfs_ops,
+ .default_groups = xe_sriov_dev_attr_groups,
+};
+
+static const struct kobj_type xe_sriov_vf_ktype = {
+ .release = release_xe_sriov_kobj,
+ .sysfs_ops = &xe_sriov_vf_sysfs_ops,
+ .default_groups = xe_sriov_vf_attr_groups,
+};
+
+static int pf_sysfs_error(struct xe_device *xe, int err, const char *what)
+{
+ if (IS_ENABLED(CONFIG_DRM_XE_DEBUG))
+ xe_sriov_dbg(xe, "Failed to setup sysfs %s (%pe)\n", what, ERR_PTR(err));
+ return err;
+}
+
+static void pf_sysfs_note(struct xe_device *xe, int err, const char *what)
+{
+ xe_sriov_dbg(xe, "Failed to setup sysfs %s (%pe)\n", what, ERR_PTR(err));
+}
+
+static void action_put_kobject(void *arg)
+{
+ struct kobject *kobj = arg;
+
+ kobject_put(kobj);
+}
+
+static int pf_setup_root(struct xe_device *xe)
+{
+ struct kobject *parent = &xe->drm.dev->kobj;
+ struct kobject *root;
+ int err;
+
+ root = create_xe_sriov_kobj(xe, PFID);
+ if (!root)
+ return pf_sysfs_error(xe, -ENOMEM, "root obj");
+
+ err = devm_add_action_or_reset(xe->drm.dev, action_put_kobject, root);
+ if (err)
+ return pf_sysfs_error(xe, err, "root action");
+
+ err = kobject_init_and_add(root, &xe_sriov_dev_ktype, parent, "sriov_admin");
+ if (err)
+ return pf_sysfs_error(xe, err, "root init");
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, !xe->sriov.pf.sysfs.root);
+ xe->sriov.pf.sysfs.root = root;
+ return 0;
+}
+
+static int pf_setup_tree(struct xe_device *xe)
+{
+ unsigned int totalvfs = xe_sriov_pf_get_totalvfs(xe);
+ struct kobject *root, *kobj;
+ unsigned int n;
+ int err;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ root = xe->sriov.pf.sysfs.root;
+
+ for (n = 0; n <= totalvfs; n++) {
+ kobj = create_xe_sriov_kobj(xe, VFID(n));
+ if (!kobj)
+ return pf_sysfs_error(xe, -ENOMEM, "tree obj");
+
+ err = devm_add_action_or_reset(xe->drm.dev, action_put_kobject, root);
+ if (err)
+ return pf_sysfs_error(xe, err, "tree action");
+
+ if (n)
+ err = kobject_init_and_add(kobj, &xe_sriov_vf_ktype,
+ root, "vf%u", n);
+ else
+ err = kobject_init_and_add(kobj, &xe_sriov_vf_ktype,
+ root, "pf");
+ if (err)
+ return pf_sysfs_error(xe, err, "tree init");
+
+ xe_assert(xe, !xe->sriov.pf.vfs[n].kobj);
+ xe->sriov.pf.vfs[n].kobj = kobj;
+ }
+
+ return 0;
+}
+
+static void action_rm_device_link(void *arg)
+{
+ struct kobject *kobj = arg;
+
+ sysfs_remove_link(kobj, "device");
+}
+
+static int pf_link_pf_device(struct xe_device *xe)
+{
+ struct kobject *kobj = xe->sriov.pf.vfs[PFID].kobj;
+ int err;
+
+ err = sysfs_create_link(kobj, &xe->drm.dev->kobj, "device");
+ if (err)
+ return pf_sysfs_error(xe, err, "PF device link");
+
+ err = devm_add_action_or_reset(xe->drm.dev, action_rm_device_link, kobj);
+ if (err)
+ return pf_sysfs_error(xe, err, "PF unlink action");
+
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_sysfs_init() - Setup PF's SR-IOV sysfs tree.
+ * @xe: the PF &xe_device to setup sysfs
+ *
+ * This function will create additional nodes that will represent PF and VFs
+ * devices, each populated with SR-IOV Xe specific attributes.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int xe_sriov_pf_sysfs_init(struct xe_device *xe)
+{
+ int err;
+
+ err = pf_setup_root(xe);
+ if (err)
+ return err;
+
+ err = pf_setup_tree(xe);
+ if (err)
+ return err;
+
+ err = pf_link_pf_device(xe);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+/**
+ * xe_sriov_pf_sysfs_link_vfs() - Add VF's links in SR-IOV sysfs tree.
+ * @xe: the &xe_device where to update sysfs
+ * @num_vfs: number of enabled VFs to link
+ *
+ * This function is specific for the PF driver.
+ *
+ * This function will add symbolic links between VFs represented in the SR-IOV
+ * sysfs tree maintained by the PF and enabled VF PCI devices.
+ *
+ * The @xe_sriov_pf_sysfs_unlink_vfs() shall be used to remove those links.
+ */
+void xe_sriov_pf_sysfs_link_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ unsigned int totalvfs = xe_sriov_pf_get_totalvfs(xe);
+ struct pci_dev *pf_pdev = to_pci_dev(xe->drm.dev);
+ struct pci_dev *vf_pdev = NULL;
+ unsigned int n;
+ int err;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, num_vfs <= totalvfs);
+
+ for (n = 1; n <= num_vfs; n++) {
+ vf_pdev = xe_pci_sriov_get_vf_pdev(pf_pdev, VFID(n));
+ if (!vf_pdev)
+ return pf_sysfs_note(xe, -ENOENT, "VF link");
+
+ err = sysfs_create_link(xe->sriov.pf.vfs[VFID(n)].kobj,
+ &vf_pdev->dev.kobj, "device");
+
+ /* must balance xe_pci_sriov_get_vf_pdev() */
+ pci_dev_put(vf_pdev);
+
+ if (err)
+ return pf_sysfs_note(xe, err, "VF link");
+ }
+}
+
+/**
+ * xe_sriov_pf_sysfs_unlink_vfs() - Remove VF's links from SR-IOV sysfs tree.
+ * @xe: the &xe_device where to update sysfs
+ * @num_vfs: number of VFs to unlink
+ *
+ * This function shall be called only on the PF.
+ * This function will remove "device" links added by @xe_sriov_sysfs_link_vfs().
+ */
+void xe_sriov_pf_sysfs_unlink_vfs(struct xe_device *xe, unsigned int num_vfs)
+{
+ unsigned int n;
+
+ xe_assert(xe, IS_SRIOV_PF(xe));
+ xe_assert(xe, num_vfs <= xe_sriov_pf_get_totalvfs(xe));
+
+ for (n = 1; n <= num_vfs; n++)
+ sysfs_remove_link(xe->sriov.pf.vfs[VFID(n)].kobj, "device");
+}
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.h b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.h
new file mode 100644
index 000000000000..ae92ed1766e7
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_sysfs.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_SRIOV_PF_SYSFS_H_
+#define _XE_SRIOV_PF_SYSFS_H_
+
+struct xe_device;
+
+int xe_sriov_pf_sysfs_init(struct xe_device *xe);
+
+void xe_sriov_pf_sysfs_link_vfs(struct xe_device *xe, unsigned int num_vfs);
+void xe_sriov_pf_sysfs_unlink_vfs(struct xe_device *xe, unsigned int num_vfs);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_pf_types.h b/drivers/gpu/drm/xe/xe_sriov_pf_types.h
index 956a88f9f213..b0253e1ae5da 100644
--- a/drivers/gpu/drm/xe/xe_sriov_pf_types.h
+++ b/drivers/gpu/drm/xe/xe_sriov_pf_types.h
@@ -9,14 +9,24 @@
#include <linux/mutex.h>
#include <linux/types.h>
+#include "xe_guard.h"
+#include "xe_sriov_pf_migration_types.h"
+#include "xe_sriov_pf_provision_types.h"
#include "xe_sriov_pf_service_types.h"
+struct kobject;
+
/**
* struct xe_sriov_metadata - per-VF device level metadata
*/
struct xe_sriov_metadata {
+ /** @kobj: kobject representing VF in PF's SR-IOV sysfs tree. */
+ struct kobject *kobj;
+
/** @version: negotiated VF/PF ABI version */
struct xe_sriov_pf_service_version version;
+ /** @migration: migration state */
+ struct xe_sriov_migration_state migration;
};
/**
@@ -32,12 +42,27 @@ struct xe_device_pf {
/** @driver_max_vfs: Maximum number of VFs supported by the driver. */
u16 driver_max_vfs;
+ /** @guard_vfs_enabling: guards VFs enabling */
+ struct xe_guard guard_vfs_enabling;
+
/** @master_lock: protects all VFs configurations across GTs */
struct mutex master_lock;
+ /** @provision: device level provisioning data. */
+ struct xe_sriov_pf_provision provision;
+
+ /** @migration: device level migration data. */
+ struct xe_sriov_pf_migration migration;
+
/** @service: device level service data. */
struct xe_sriov_pf_service service;
+ /** @sysfs: device level sysfs data. */
+ struct {
+ /** @sysfs.root: the root kobject for all SR-IOV entries in sysfs. */
+ struct kobject *root;
+ } sysfs;
+
/** @vfs: metadata for all VFs. */
struct xe_sriov_metadata *vfs;
};
diff --git a/drivers/gpu/drm/xe/xe_sriov_printk.h b/drivers/gpu/drm/xe/xe_sriov_printk.h
index 117e1d541692..4c6b5c3d2190 100644
--- a/drivers/gpu/drm/xe/xe_sriov_printk.h
+++ b/drivers/gpu/drm/xe/xe_sriov_printk.h
@@ -1,22 +1,22 @@
/* SPDX-License-Identifier: MIT */
/*
- * Copyright © 2023 Intel Corporation
+ * Copyright © 2023-2025 Intel Corporation
*/
#ifndef _XE_SRIOV_PRINTK_H_
#define _XE_SRIOV_PRINTK_H_
-#include <drm/drm_print.h>
-
-#include "xe_device_types.h"
-#include "xe_sriov_types.h"
+#include "xe_printk.h"
#define xe_sriov_printk_prefix(xe) \
((xe)->sriov.__mode == XE_SRIOV_MODE_PF ? "PF: " : \
(xe)->sriov.__mode == XE_SRIOV_MODE_VF ? "VF: " : "")
+#define __XE_SRIOV_PRINTK_FMT(_xe, _fmt, _args...) \
+ "%s" _fmt, xe_sriov_printk_prefix(_xe), ##_args
+
#define xe_sriov_printk(xe, _level, fmt, ...) \
- drm_##_level(&(xe)->drm, "%s" fmt, xe_sriov_printk_prefix(xe), ##__VA_ARGS__)
+ xe_##_level((xe), __XE_SRIOV_PRINTK_FMT((xe), fmt, ##__VA_ARGS__))
#define xe_sriov_err(xe, fmt, ...) \
xe_sriov_printk((xe), err, fmt, ##__VA_ARGS__)
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf.c b/drivers/gpu/drm/xe/xe_sriov_vf.c
index cdd9f8e78b2a..284ce37ca92d 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf.c
@@ -6,22 +6,12 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_managed.h>
-#include "xe_assert.h"
-#include "xe_device.h"
#include "xe_gt.h"
-#include "xe_gt_sriov_printk.h"
#include "xe_gt_sriov_vf.h"
#include "xe_guc.h"
-#include "xe_guc_ct.h"
-#include "xe_guc_submit.h"
-#include "xe_irq.h"
-#include "xe_lrc.h"
-#include "xe_pm.h"
-#include "xe_sriov.h"
#include "xe_sriov_printk.h"
#include "xe_sriov_vf.h"
#include "xe_sriov_vf_ccs.h"
-#include "xe_tile_sriov_vf.h"
/**
* DOC: VF restore procedure in PF KMD and VF KMD
@@ -140,10 +130,15 @@
bool xe_sriov_vf_migration_supported(struct xe_device *xe)
{
xe_assert(xe, IS_SRIOV_VF(xe));
- return xe->sriov.vf.migration.enabled;
+ return !xe->sriov.vf.migration.disabled;
}
-static void vf_disable_migration(struct xe_device *xe, const char *fmt, ...)
+/**
+ * xe_sriov_vf_migration_disable - Turn off VF migration with given log message.
+ * @xe: the &xe_device instance.
+ * @fmt: format string for the log message, to be combined with following VAs.
+ */
+void xe_sriov_vf_migration_disable(struct xe_device *xe, const char *fmt, ...)
{
struct va_format vaf;
va_list va_args;
@@ -156,39 +151,14 @@ static void vf_disable_migration(struct xe_device *xe, const char *fmt, ...)
xe_sriov_notice(xe, "migration disabled: %pV\n", &vaf);
va_end(va_args);
- xe->sriov.vf.migration.enabled = false;
+ xe->sriov.vf.migration.disabled = true;
}
-static void migration_worker_func(struct work_struct *w);
-
static void vf_migration_init_early(struct xe_device *xe)
{
- /*
- * TODO: Add conditions to allow specific platforms, when they're
- * supported at production quality.
- */
- if (!IS_ENABLED(CONFIG_DRM_XE_DEBUG))
- return vf_disable_migration(xe,
- "experimental feature not available on production builds");
-
- if (GRAPHICS_VER(xe) < 20)
- return vf_disable_migration(xe, "requires gfx version >= 20, but only %u found",
- GRAPHICS_VER(xe));
-
- if (!IS_DGFX(xe)) {
- struct xe_uc_fw_version guc_version;
-
- xe_gt_sriov_vf_guc_versions(xe_device_get_gt(xe, 0), NULL, &guc_version);
- if (MAKE_GUC_VER_STRUCT(guc_version) < MAKE_GUC_VER(1, 23, 0))
- return vf_disable_migration(xe,
- "CCS migration requires GuC ABI >= 1.23 but only %u.%u found",
- guc_version.major, guc_version.minor);
- }
+ if (!xe_device_has_memirq(xe))
+ return xe_sriov_vf_migration_disable(xe, "requires memory-based IRQ support");
- INIT_WORK(&xe->sriov.vf.migration.worker, migration_worker_func);
-
- xe->sriov.vf.migration.enabled = true;
- xe_sriov_dbg(xe, "migration support enabled\n");
}
/**
@@ -201,235 +171,6 @@ void xe_sriov_vf_init_early(struct xe_device *xe)
}
/**
- * vf_post_migration_shutdown - Stop the driver activities after VF migration.
- * @xe: the &xe_device struct instance
- *
- * After this VM is migrated and assigned to a new VF, it is running on a new
- * hardware, and therefore many hardware-dependent states and related structures
- * require fixups. Without fixups, the hardware cannot do any work, and therefore
- * all GPU pipelines are stalled.
- * Stop some of kernel activities to make the fixup process faster.
- */
-static void vf_post_migration_shutdown(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
- int ret = 0;
-
- for_each_gt(gt, xe, id) {
- xe_guc_submit_pause(&gt->uc.guc);
- ret |= xe_guc_submit_reset_block(&gt->uc.guc);
- }
-
- if (ret)
- drm_info(&xe->drm, "migration recovery encountered ongoing reset\n");
-}
-
-/**
- * vf_post_migration_kickstart - Re-start the driver activities under new hardware.
- * @xe: the &xe_device struct instance
- *
- * After we have finished with all post-migration fixups, restart the driver
- * activities to continue feeding the GPU with workloads.
- */
-static void vf_post_migration_kickstart(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
-
- /*
- * Make sure interrupts on the new HW are properly set. The GuC IRQ
- * must be working at this point, since the recovery did started,
- * but the rest was not enabled using the procedure from spec.
- */
- xe_irq_resume(xe);
-
- for_each_gt(gt, xe, id) {
- xe_guc_submit_reset_unblock(&gt->uc.guc);
- xe_guc_submit_unpause(&gt->uc.guc);
- }
-}
-
-static bool gt_vf_post_migration_needed(struct xe_gt *gt)
-{
- return test_bit(gt->info.id, &gt_to_xe(gt)->sriov.vf.migration.gt_flags);
-}
-
-/*
- * Notify GuCs marked in flags about resource fixups apply finished.
- * @xe: the &xe_device struct instance
- * @gt_flags: flags marking to which GTs the notification shall be sent
- */
-static int vf_post_migration_notify_resfix_done(struct xe_device *xe, unsigned long gt_flags)
-{
- struct xe_gt *gt;
- unsigned int id;
- int err = 0;
-
- for_each_gt(gt, xe, id) {
- if (!test_bit(id, &gt_flags))
- continue;
- /* skip asking GuC for RESFIX exit if new recovery request arrived */
- if (gt_vf_post_migration_needed(gt))
- continue;
- err = xe_gt_sriov_vf_notify_resfix_done(gt);
- if (err)
- break;
- clear_bit(id, &gt_flags);
- }
-
- if (gt_flags && !err)
- drm_dbg(&xe->drm, "another recovery imminent, skipped some notifications\n");
- return err;
-}
-
-static int vf_get_next_migrated_gt_id(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
-
- for_each_gt(gt, xe, id) {
- if (test_and_clear_bit(id, &xe->sriov.vf.migration.gt_flags))
- return id;
- }
- return -1;
-}
-
-static size_t post_migration_scratch_size(struct xe_device *xe)
-{
- return max(xe_lrc_reg_size(xe), LRC_WA_BB_SIZE);
-}
-
-/**
- * Perform post-migration fixups on a single GT.
- *
- * After migration, GuC needs to be re-queried for VF configuration to check
- * if it matches previous provisioning. Most of VF provisioning shall be the
- * same, except GGTT range, since GGTT is not virtualized per-VF. If GGTT
- * range has changed, we have to perform fixups - shift all GGTT references
- * used anywhere within the driver. After the fixups in this function succeed,
- * it is allowed to ask the GuC bound to this GT to continue normal operation.
- *
- * Returns: 0 if the operation completed successfully, or a negative error
- * code otherwise.
- */
-static int gt_vf_post_migration_fixups(struct xe_gt *gt)
-{
- s64 shift;
- void *buf;
- int err;
-
- buf = kmalloc(post_migration_scratch_size(gt_to_xe(gt)), GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- err = xe_gt_sriov_vf_query_config(gt);
- if (err)
- goto out;
-
- shift = xe_gt_sriov_vf_ggtt_shift(gt);
- if (shift) {
- xe_tile_sriov_vf_fixup_ggtt_nodes(gt_to_tile(gt), shift);
- xe_gt_sriov_vf_default_lrcs_hwsp_rebase(gt);
- err = xe_guc_contexts_hwsp_rebase(&gt->uc.guc, buf);
- if (err)
- goto out;
- xe_guc_jobs_ring_rebase(&gt->uc.guc);
- xe_guc_ct_fixup_messages_with_ggtt(&gt->uc.guc.ct, shift);
- }
-
-out:
- kfree(buf);
- return err;
-}
-
-static void vf_post_migration_recovery(struct xe_device *xe)
-{
- unsigned long fixed_gts = 0;
- int id, err;
-
- drm_dbg(&xe->drm, "migration recovery in progress\n");
- xe_pm_runtime_get(xe);
- vf_post_migration_shutdown(xe);
-
- if (!xe_sriov_vf_migration_supported(xe)) {
- xe_sriov_err(xe, "migration is not supported\n");
- err = -ENOTRECOVERABLE;
- goto fail;
- }
-
- while (id = vf_get_next_migrated_gt_id(xe), id >= 0) {
- struct xe_gt *gt = xe_device_get_gt(xe, id);
-
- err = gt_vf_post_migration_fixups(gt);
- if (err)
- goto fail;
-
- set_bit(id, &fixed_gts);
- }
-
- vf_post_migration_kickstart(xe);
- err = vf_post_migration_notify_resfix_done(xe, fixed_gts);
- if (err)
- goto fail;
-
- xe_pm_runtime_put(xe);
- drm_notice(&xe->drm, "migration recovery ended\n");
- return;
-fail:
- xe_pm_runtime_put(xe);
- drm_err(&xe->drm, "migration recovery failed (%pe)\n", ERR_PTR(err));
- xe_device_declare_wedged(xe);
-}
-
-static void migration_worker_func(struct work_struct *w)
-{
- struct xe_device *xe = container_of(w, struct xe_device,
- sriov.vf.migration.worker);
-
- vf_post_migration_recovery(xe);
-}
-
-/*
- * Check if post-restore recovery is coming on any of GTs.
- * @xe: the &xe_device struct instance
- *
- * Return: True if migration recovery worker will soon be running. Any worker currently
- * executing does not affect the result.
- */
-static bool vf_ready_to_recovery_on_any_gts(struct xe_device *xe)
-{
- struct xe_gt *gt;
- unsigned int id;
-
- for_each_gt(gt, xe, id) {
- if (test_bit(id, &xe->sriov.vf.migration.gt_flags))
- return true;
- }
- return false;
-}
-
-/**
- * xe_sriov_vf_start_migration_recovery - Start VF migration recovery.
- * @xe: the &xe_device to start recovery on
- *
- * This function shall be called only by VF.
- */
-void xe_sriov_vf_start_migration_recovery(struct xe_device *xe)
-{
- bool started;
-
- xe_assert(xe, IS_SRIOV_VF(xe));
-
- if (!vf_ready_to_recovery_on_any_gts(xe))
- return;
-
- started = queue_work(xe->sriov.wq, &xe->sriov.vf.migration.worker);
- drm_info(&xe->drm, "VF migration recovery %s\n", started ?
- "scheduled" : "already in progress");
-}
-
-/**
* xe_sriov_vf_init_late() - SR-IOV VF late initialization functions.
* @xe: the &xe_device to initialize
*
@@ -439,12 +180,7 @@ void xe_sriov_vf_start_migration_recovery(struct xe_device *xe)
*/
int xe_sriov_vf_init_late(struct xe_device *xe)
{
- int err = 0;
-
- if (xe_sriov_vf_migration_supported(xe))
- err = xe_sriov_vf_ccs_init(xe);
-
- return err;
+ return xe_sriov_vf_ccs_init(xe);
}
static int sa_info_vf_ccs(struct seq_file *m, void *data)
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf.h b/drivers/gpu/drm/xe/xe_sriov_vf.h
index 9e752105ec2a..e967d4166a43 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf.h
@@ -13,8 +13,8 @@ struct xe_device;
void xe_sriov_vf_init_early(struct xe_device *xe);
int xe_sriov_vf_init_late(struct xe_device *xe);
-void xe_sriov_vf_start_migration_recovery(struct xe_device *xe);
bool xe_sriov_vf_migration_supported(struct xe_device *xe);
+void xe_sriov_vf_migration_disable(struct xe_device *xe, const char *fmt, ...);
void xe_sriov_vf_debugfs_register(struct xe_device *xe, struct dentry *root);
#endif
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
index 8dec616c37c9..797a4b866226 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.c
@@ -10,6 +10,8 @@
#include "xe_device.h"
#include "xe_exec_queue.h"
#include "xe_exec_queue_types.h"
+#include "xe_gt_sriov_vf.h"
+#include "xe_guc.h"
#include "xe_guc_submit.h"
#include "xe_lrc.h"
#include "xe_migrate.h"
@@ -175,6 +177,15 @@ static void ccs_rw_update_ring(struct xe_sriov_vf_ccs_ctx *ctx)
struct xe_lrc *lrc = xe_exec_queue_lrc(ctx->mig_q);
u32 dw[10], i = 0;
+ /*
+ * XXX: Save/restore fixes — for some reason, the GuC only accepts the
+ * save/restore context if the LRC head pointer is zero. This is evident
+ * from repeated VF migrations failing when the LRC head pointer is
+ * non-zero.
+ */
+ lrc->ring.tail = 0;
+ xe_lrc_set_ring_head(lrc, 0);
+
dw[i++] = MI_ARB_ON_OFF | MI_ARB_ENABLE;
dw[i++] = MI_BATCH_BUFFER_START | XE_INSTR_NUM_DW(3);
dw[i++] = lower_32_bits(addr);
@@ -186,6 +197,25 @@ static void ccs_rw_update_ring(struct xe_sriov_vf_ccs_ctx *ctx)
xe_lrc_set_ring_tail(lrc, lrc->ring.tail);
}
+/**
+ * xe_sriov_vf_ccs_rebase - Rebase GGTT addresses for CCS save / restore
+ * @xe: the &xe_device.
+ */
+void xe_sriov_vf_ccs_rebase(struct xe_device *xe)
+{
+ enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
+
+ if (!IS_VF_CCS_READY(xe))
+ return;
+
+ for_each_ccs_rw_ctx(ctx_id) {
+ struct xe_sriov_vf_ccs_ctx *ctx =
+ &xe->sriov.vf.ccs.contexts[ctx_id];
+
+ ccs_rw_update_ring(ctx);
+ }
+}
+
static int register_save_restore_context(struct xe_sriov_vf_ccs_ctx *ctx)
{
int ctx_type;
@@ -232,6 +262,45 @@ int xe_sriov_vf_ccs_register_context(struct xe_device *xe)
return err;
}
+/*
+ * Whether GuC requires CCS copy BBs for VF migration.
+ * @xe: the &xe_device instance.
+ *
+ * Only selected platforms require VF KMD to maintain CCS copy BBs and linked LRCAs.
+ *
+ * Return: true if VF driver must participate in the CCS migration, false otherwise.
+ */
+static bool vf_migration_ccs_bb_needed(struct xe_device *xe)
+{
+ xe_assert(xe, IS_SRIOV_VF(xe));
+
+ return !IS_DGFX(xe) && xe_device_has_flat_ccs(xe);
+}
+
+/*
+ * Check for disable migration due to no CCS BBs support in GuC FW.
+ * @xe: the &xe_device instance.
+ *
+ * Performs late disable of VF migration feature in case GuC FW cannot support it.
+ *
+ * Returns: True if VF migration with CCS BBs is supported, false otherwise.
+ */
+static bool vf_migration_ccs_bb_support_check(struct xe_device *xe)
+{
+ struct xe_gt *gt = xe_root_mmio_gt(xe);
+ struct xe_uc_fw_version guc_version;
+
+ xe_gt_sriov_vf_guc_versions(gt, NULL, &guc_version);
+ if (MAKE_GUC_VER_STRUCT(guc_version) < MAKE_GUC_VER(1, 23, 0)) {
+ xe_sriov_vf_migration_disable(xe,
+ "CCS migration requires GuC ABI >= 1.23 but only %u.%u found",
+ guc_version.major, guc_version.minor);
+ return false;
+ }
+
+ return true;
+}
+
static void xe_sriov_vf_ccs_fini(void *arg)
{
struct xe_sriov_vf_ccs_ctx *ctx = arg;
@@ -264,9 +333,10 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe)
int err;
xe_assert(xe, IS_SRIOV_VF(xe));
- xe_assert(xe, xe_sriov_vf_migration_supported(xe));
- if (IS_DGFX(xe) || !xe_device_has_flat_ccs(xe))
+ if (!xe_sriov_vf_migration_supported(xe) ||
+ !vf_migration_ccs_bb_needed(xe) ||
+ !vf_migration_ccs_bb_support_check(xe))
return 0;
for_each_ccs_rw_ctx(ctx_id) {
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
index 0745c0ff0228..f8ca6efce9ee 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_ccs.h
@@ -18,6 +18,7 @@ int xe_sriov_vf_ccs_init(struct xe_device *xe);
int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo);
int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo);
int xe_sriov_vf_ccs_register_context(struct xe_device *xe);
+void xe_sriov_vf_ccs_rebase(struct xe_device *xe);
void xe_sriov_vf_ccs_print(struct xe_device *xe, struct drm_printer *p);
static inline bool xe_sriov_vf_ccs_ready(struct xe_device *xe)
diff --git a/drivers/gpu/drm/xe/xe_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_sriov_vf_types.h
index 426cc5841958..d5f72d667817 100644
--- a/drivers/gpu/drm/xe/xe_sriov_vf_types.h
+++ b/drivers/gpu/drm/xe/xe_sriov_vf_types.h
@@ -33,15 +33,11 @@ struct xe_device_vf {
/** @migration: VF Migration state data */
struct {
- /** @migration.worker: VF migration recovery worker */
- struct work_struct worker;
- /** @migration.gt_flags: Per-GT request flags for VF migration recovery */
- unsigned long gt_flags;
/**
- * @migration.enabled: flag indicating if migration support
- * was enabled or not due to missing prerequisites
+ * @migration.disabled: flag indicating if migration support
+ * was turned off due to missing prerequisites
*/
- bool enabled;
+ bool disabled;
} migration;
/** @ccs: VF CCS state data */
diff --git a/drivers/gpu/drm/xe/xe_sriov_vfio.c b/drivers/gpu/drm/xe/xe_sriov_vfio.c
new file mode 100644
index 000000000000..e9a7615bb5c5
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sriov_vfio.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <drm/intel/xe_sriov_vfio.h>
+#include <linux/cleanup.h>
+
+#include "xe_pci.h"
+#include "xe_pm.h"
+#include "xe_sriov_pf_control.h"
+#include "xe_sriov_pf_helpers.h"
+#include "xe_sriov_pf_migration.h"
+
+struct xe_device *xe_sriov_vfio_get_pf(struct pci_dev *pdev)
+{
+ return xe_pci_to_pf_device(pdev);
+}
+EXPORT_SYMBOL_FOR_MODULES(xe_sriov_vfio_get_pf, "xe-vfio-pci");
+
+bool xe_sriov_vfio_migration_supported(struct xe_device *xe)
+{
+ if (!IS_SRIOV_PF(xe))
+ return -EPERM;
+
+ return xe_sriov_pf_migration_supported(xe);
+}
+EXPORT_SYMBOL_FOR_MODULES(xe_sriov_vfio_migration_supported, "xe-vfio-pci");
+
+#define DEFINE_XE_SRIOV_VFIO_FUNCTION(_type, _func, _impl) \
+_type xe_sriov_vfio_##_func(struct xe_device *xe, unsigned int vfid) \
+{ \
+ if (!IS_SRIOV_PF(xe)) \
+ return -EPERM; \
+ if (vfid == PFID || vfid > xe_sriov_pf_num_vfs(xe)) \
+ return -EINVAL; \
+ \
+ guard(xe_pm_runtime_noresume)(xe); \
+ \
+ return xe_sriov_pf_##_impl(xe, vfid); \
+} \
+EXPORT_SYMBOL_FOR_MODULES(xe_sriov_vfio_##_func, "xe-vfio-pci")
+
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, wait_flr_done, control_wait_flr);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, suspend_device, control_pause_vf);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, resume_device, control_resume_vf);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, stop_copy_enter, control_trigger_save_vf);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, stop_copy_exit, control_finish_save_vf);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, resume_data_enter, control_trigger_restore_vf);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, resume_data_exit, control_finish_restore_vf);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(int, error, control_stop_vf);
+DEFINE_XE_SRIOV_VFIO_FUNCTION(ssize_t, stop_copy_size, migration_size);
+
+ssize_t xe_sriov_vfio_data_read(struct xe_device *xe, unsigned int vfid,
+ char __user *buf, size_t len)
+{
+ if (!IS_SRIOV_PF(xe))
+ return -EPERM;
+ if (vfid == PFID || vfid > xe_sriov_pf_num_vfs(xe))
+ return -EINVAL;
+
+ guard(xe_pm_runtime_noresume)(xe);
+
+ return xe_sriov_pf_migration_read(xe, vfid, buf, len);
+}
+EXPORT_SYMBOL_FOR_MODULES(xe_sriov_vfio_data_read, "xe-vfio-pci");
+
+ssize_t xe_sriov_vfio_data_write(struct xe_device *xe, unsigned int vfid,
+ const char __user *buf, size_t len)
+{
+ if (!IS_SRIOV_PF(xe))
+ return -EPERM;
+ if (vfid == PFID || vfid > xe_sriov_pf_num_vfs(xe))
+ return -EINVAL;
+
+ guard(xe_pm_runtime_noresume)(xe);
+
+ return xe_sriov_pf_migration_write(xe, vfid, buf, len);
+}
+EXPORT_SYMBOL_FOR_MODULES(xe_sriov_vfio_data_write, "xe-vfio-pci");
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index da2a412f80c0..55c5a0eb82e1 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -104,8 +104,7 @@ xe_svm_garbage_collector_add_range(struct xe_vm *vm, struct xe_svm_range *range,
&vm->svm.garbage_collector.range_list);
spin_unlock(&vm->svm.garbage_collector.lock);
- queue_work(xe_device_get_root_tile(xe)->primary_gt->usm.pf_wq,
- &vm->svm.garbage_collector.work);
+ queue_work(xe->usm.pf_wq, &vm->svm.garbage_collector.work);
}
static void xe_svm_tlb_inval_count_stats_incr(struct xe_gt *gt)
@@ -302,6 +301,11 @@ static int xe_svm_range_set_default_attr(struct xe_vm *vm, u64 range_start, u64
if (!vma)
return -EINVAL;
+ if (!(vma->gpuva.flags & XE_VMA_MADV_AUTORESET)) {
+ drm_dbg(&vm->xe->drm, "Skipping madvise reset for vma.\n");
+ return 0;
+ }
+
if (xe_vma_has_default_mem_attrs(vma))
return 0;
@@ -628,7 +632,7 @@ err_out:
/*
* XXX: We can't derive the GT here (or anywhere in this functions, but
- * compute always uses the primary GT so accumlate stats on the likely
+ * compute always uses the primary GT so accumulate stats on the likely
* GT of the fault.
*/
if (gt)
diff --git a/drivers/gpu/drm/xe/xe_sync.c b/drivers/gpu/drm/xe/xe_sync.c
index 82872a51f098..ff74528ca0c6 100644
--- a/drivers/gpu/drm/xe/xe_sync.c
+++ b/drivers/gpu/drm/xe/xe_sync.c
@@ -14,7 +14,7 @@
#include <drm/drm_syncobj.h>
#include <uapi/drm/xe_drm.h>
-#include "xe_device_types.h"
+#include "xe_device.h"
#include "xe_exec_queue.h"
#include "xe_macros.h"
#include "xe_sched_job_types.h"
@@ -113,6 +113,8 @@ static void user_fence_cb(struct dma_fence *fence, struct dma_fence_cb *cb)
int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
struct xe_sync_entry *sync,
struct drm_xe_sync __user *sync_user,
+ struct drm_syncobj *ufence_syncobj,
+ u64 ufence_timeline_value,
unsigned int flags)
{
struct drm_xe_sync sync_in;
@@ -192,10 +194,15 @@ int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
if (exec) {
sync->addr = sync_in.addr;
} else {
+ sync->ufence_timeline_value = ufence_timeline_value;
sync->ufence = user_fence_create(xe, sync_in.addr,
sync_in.timeline_value);
if (XE_IOCTL_DBG(xe, IS_ERR(sync->ufence)))
return PTR_ERR(sync->ufence);
+ sync->ufence_chain_fence = dma_fence_chain_alloc();
+ if (!sync->ufence_chain_fence)
+ return -ENOMEM;
+ sync->ufence_syncobj = ufence_syncobj;
}
break;
@@ -239,7 +246,12 @@ void xe_sync_entry_signal(struct xe_sync_entry *sync, struct dma_fence *fence)
} else if (sync->ufence) {
int err;
- dma_fence_get(fence);
+ drm_syncobj_add_point(sync->ufence_syncobj,
+ sync->ufence_chain_fence,
+ fence, sync->ufence_timeline_value);
+ sync->ufence_chain_fence = NULL;
+
+ fence = drm_syncobj_fence_get(sync->ufence_syncobj);
user_fence_get(sync->ufence);
err = dma_fence_add_callback(fence, &sync->ufence->cb,
user_fence_cb);
@@ -259,7 +271,8 @@ void xe_sync_entry_cleanup(struct xe_sync_entry *sync)
drm_syncobj_put(sync->syncobj);
dma_fence_put(sync->fence);
dma_fence_chain_free(sync->chain_fence);
- if (sync->ufence)
+ dma_fence_chain_free(sync->ufence_chain_fence);
+ if (!IS_ERR_OR_NULL(sync->ufence))
user_fence_put(sync->ufence);
}
@@ -284,51 +297,59 @@ xe_sync_in_fence_get(struct xe_sync_entry *sync, int num_sync,
struct dma_fence **fences = NULL;
struct dma_fence_array *cf = NULL;
struct dma_fence *fence;
- int i, num_in_fence = 0, current_fence = 0;
+ int i, num_fence = 0, current_fence = 0;
lockdep_assert_held(&vm->lock);
- /* Count in-fences */
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- ++num_in_fence;
- fence = sync[i].fence;
+ /* Reject in fences */
+ for (i = 0; i < num_sync; ++i)
+ if (sync[i].fence)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (q->flags & EXEC_QUEUE_FLAG_VM) {
+ struct xe_exec_queue *__q;
+ struct xe_tile *tile;
+ u8 id;
+
+ for_each_tile(tile, vm->xe, id)
+ num_fence += (1 + XE_MAX_GT_PER_TILE);
+
+ fences = kmalloc_array(num_fence, sizeof(*fences),
+ GFP_KERNEL);
+ if (!fences)
+ return ERR_PTR(-ENOMEM);
+
+ fences[current_fence++] =
+ xe_exec_queue_last_fence_get(q, vm);
+ for_each_tlb_inval(i)
+ fences[current_fence++] =
+ xe_exec_queue_tlb_inval_last_fence_get(q, vm, i);
+ list_for_each_entry(__q, &q->multi_gt_list,
+ multi_gt_link) {
+ fences[current_fence++] =
+ xe_exec_queue_last_fence_get(__q, vm);
+ for_each_tlb_inval(i)
+ fences[current_fence++] =
+ xe_exec_queue_tlb_inval_last_fence_get(__q, vm, i);
}
- }
- /* Easy case... */
- if (!num_in_fence) {
- fence = xe_exec_queue_last_fence_get(q, vm);
- return fence;
- }
+ xe_assert(vm->xe, current_fence == num_fence);
+ cf = dma_fence_array_create(num_fence, fences,
+ dma_fence_context_alloc(1),
+ 1, false);
+ if (!cf)
+ goto err_out;
- /* Create composite fence */
- fences = kmalloc_array(num_in_fence + 1, sizeof(*fences), GFP_KERNEL);
- if (!fences)
- return ERR_PTR(-ENOMEM);
- for (i = 0; i < num_sync; ++i) {
- if (sync[i].fence) {
- dma_fence_get(sync[i].fence);
- fences[current_fence++] = sync[i].fence;
- }
- }
- fences[current_fence++] = xe_exec_queue_last_fence_get(q, vm);
- cf = dma_fence_array_create(num_in_fence, fences,
- vm->composite_fence_ctx,
- vm->composite_fence_seqno++,
- false);
- if (!cf) {
- --vm->composite_fence_seqno;
- goto err_out;
+ return &cf->base;
}
- return &cf->base;
+ fence = xe_exec_queue_last_fence_get(q, vm);
+ return fence;
err_out:
while (current_fence)
dma_fence_put(fences[--current_fence]);
kfree(fences);
- kfree(cf);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/gpu/drm/xe/xe_sync.h b/drivers/gpu/drm/xe/xe_sync.h
index 256ffc1e54dc..51f2d803e977 100644
--- a/drivers/gpu/drm/xe/xe_sync.h
+++ b/drivers/gpu/drm/xe/xe_sync.h
@@ -8,6 +8,7 @@
#include "xe_sync_types.h"
+struct drm_syncobj;
struct xe_device;
struct xe_exec_queue;
struct xe_file;
@@ -21,6 +22,8 @@ struct xe_vm;
int xe_sync_entry_parse(struct xe_device *xe, struct xe_file *xef,
struct xe_sync_entry *sync,
struct drm_xe_sync __user *sync_user,
+ struct drm_syncobj *ufence_syncobj,
+ u64 ufence_timeline_value,
unsigned int flags);
int xe_sync_entry_add_deps(struct xe_sync_entry *sync,
struct xe_sched_job *job);
diff --git a/drivers/gpu/drm/xe/xe_sync_types.h b/drivers/gpu/drm/xe/xe_sync_types.h
index 30ac3f51993b..b88f1833e28c 100644
--- a/drivers/gpu/drm/xe/xe_sync_types.h
+++ b/drivers/gpu/drm/xe/xe_sync_types.h
@@ -18,9 +18,12 @@ struct xe_sync_entry {
struct drm_syncobj *syncobj;
struct dma_fence *fence;
struct dma_fence_chain *chain_fence;
+ struct dma_fence_chain *ufence_chain_fence;
+ struct drm_syncobj *ufence_syncobj;
struct xe_user_fence *ufence;
u64 addr;
u64 timeline_value;
+ u64 ufence_timeline_value;
u32 type;
u32 flags;
};
diff --git a/drivers/gpu/drm/xe/xe_tile.c b/drivers/gpu/drm/xe/xe_tile.c
index d49ba3401963..4f4f9a5c43af 100644
--- a/drivers/gpu/drm/xe/xe_tile.c
+++ b/drivers/gpu/drm/xe/xe_tile.c
@@ -19,9 +19,9 @@
#include "xe_tile.h"
#include "xe_tile_sysfs.h"
#include "xe_ttm_vram_mgr.h"
-#include "xe_wa.h"
#include "xe_vram.h"
#include "xe_vram_types.h"
+#include "xe_wa.h"
/**
* DOC: Multi-tile Design
@@ -124,6 +124,14 @@ int xe_tile_alloc_vram(struct xe_tile *tile)
return -ENOMEM;
tile->mem.vram = vram;
+ /*
+ * If the kernel_vram is not already allocated,
+ * it means that tile has common VRAM region for
+ * kernel and user space.
+ */
+ if (!tile->mem.kernel_vram)
+ tile->mem.kernel_vram = tile->mem.vram;
+
return 0;
}
@@ -149,10 +157,6 @@ int xe_tile_init_early(struct xe_tile *tile, struct xe_device *xe, u8 id)
if (err)
return err;
- tile->primary_gt = xe_gt_alloc(tile);
- if (IS_ERR(tile->primary_gt))
- return PTR_ERR(tile->primary_gt);
-
xe_pcode_init(tile);
return 0;
diff --git a/drivers/gpu/drm/xe/xe_tile_debugfs.c b/drivers/gpu/drm/xe/xe_tile_debugfs.c
index 5523874cba7b..fff242a5ae56 100644
--- a/drivers/gpu/drm/xe/xe_tile_debugfs.c
+++ b/drivers/gpu/drm/xe/xe_tile_debugfs.c
@@ -6,6 +6,7 @@
#include <linux/debugfs.h>
#include <drm/drm_debugfs.h>
+#include "xe_ggtt.h"
#include "xe_pm.h"
#include "xe_sa.h"
#include "xe_tile_debugfs.h"
@@ -16,7 +17,7 @@ static struct xe_tile *node_to_tile(struct drm_info_node *node)
}
/**
- * tile_debugfs_simple_show - A show callback for struct drm_info_list
+ * xe_tile_debugfs_simple_show() - A show callback for struct drm_info_list
* @m: the &seq_file
* @data: data used by the drm debugfs helpers
*
@@ -57,7 +58,7 @@ static struct xe_tile *node_to_tile(struct drm_info_node *node)
*
* Return: 0 on success or a negative error code on failure.
*/
-static int tile_debugfs_simple_show(struct seq_file *m, void *data)
+int xe_tile_debugfs_simple_show(struct seq_file *m, void *data)
{
struct drm_printer p = drm_seq_file_printer(m);
struct drm_info_node *node = m->private;
@@ -68,7 +69,7 @@ static int tile_debugfs_simple_show(struct seq_file *m, void *data)
}
/**
- * tile_debugfs_show_with_rpm - A show callback for struct drm_info_list
+ * xe_tile_debugfs_show_with_rpm() - A show callback for struct drm_info_list
* @m: the &seq_file
* @data: data used by the drm debugfs helpers
*
@@ -76,7 +77,7 @@ static int tile_debugfs_simple_show(struct seq_file *m, void *data)
*
* Return: 0 on success or a negative error code on failure.
*/
-static int tile_debugfs_show_with_rpm(struct seq_file *m, void *data)
+int xe_tile_debugfs_show_with_rpm(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct xe_tile *tile = node_to_tile(node);
@@ -84,12 +85,17 @@ static int tile_debugfs_show_with_rpm(struct seq_file *m, void *data)
int ret;
xe_pm_runtime_get(xe);
- ret = tile_debugfs_simple_show(m, data);
+ ret = xe_tile_debugfs_simple_show(m, data);
xe_pm_runtime_put(xe);
return ret;
}
+static int ggtt(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_ggtt_dump(tile->mem.ggtt, p);
+}
+
static int sa_info(struct xe_tile *tile, struct drm_printer *p)
{
drm_suballoc_dump_debug_info(&tile->mem.kernel_bb_pool->base, p,
@@ -100,7 +106,8 @@ static int sa_info(struct xe_tile *tile, struct drm_printer *p)
/* only for debugfs files which can be safely used on the VF */
static const struct drm_info_list vf_safe_debugfs_list[] = {
- { "sa_info", .show = tile_debugfs_show_with_rpm, .data = sa_info },
+ { "ggtt", .show = xe_tile_debugfs_show_with_rpm, .data = ggtt },
+ { "sa_info", .show = xe_tile_debugfs_show_with_rpm, .data = sa_info },
};
/**
diff --git a/drivers/gpu/drm/xe/xe_tile_debugfs.h b/drivers/gpu/drm/xe/xe_tile_debugfs.h
index 0e5f724de37f..4429c22542f4 100644
--- a/drivers/gpu/drm/xe/xe_tile_debugfs.h
+++ b/drivers/gpu/drm/xe/xe_tile_debugfs.h
@@ -6,8 +6,11 @@
#ifndef _XE_TILE_DEBUGFS_H_
#define _XE_TILE_DEBUGFS_H_
+struct seq_file;
struct xe_tile;
void xe_tile_debugfs_register(struct xe_tile *tile);
+int xe_tile_debugfs_simple_show(struct seq_file *m, void *data);
+int xe_tile_debugfs_show_with_rpm(struct seq_file *m, void *data);
#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c
new file mode 100644
index 000000000000..f3f478f14ff5
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#include <linux/debugfs.h>
+#include <drm/drm_debugfs.h>
+
+#include "xe_device.h"
+#include "xe_device_types.h"
+#include "xe_gt_sriov_pf_config.h"
+#include "xe_gt_sriov_pf_debugfs.h"
+#include "xe_pm.h"
+#include "xe_tile_debugfs.h"
+#include "xe_tile_sriov_pf_debugfs.h"
+#include "xe_sriov.h"
+#include "xe_sriov_pf.h"
+#include "xe_sriov_pf_provision.h"
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov # d_inode->i_private = (xe_device*)
+ * │ ├── pf # d_inode->i_private = (xe_device*)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vf1 # d_inode->i_private = VFID(1)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vfN # d_inode->i_private = VFID(N)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * : : : :
+ */
+
+static void *extract_priv(struct dentry *d)
+{
+ return d->d_inode->i_private;
+}
+
+__maybe_unused
+static struct xe_tile *extract_tile(struct dentry *d)
+{
+ return extract_priv(d);
+}
+
+static struct xe_device *extract_xe(struct dentry *d)
+{
+ return extract_priv(d->d_parent->d_parent);
+}
+
+__maybe_unused
+static unsigned int extract_vfid(struct dentry *d)
+{
+ void *pp = extract_priv(d->d_parent);
+
+ return pp == extract_xe(d) ? PFID : (uintptr_t)pp;
+}
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── ggtt_available
+ * ├── ggtt_provisioned
+ */
+
+static int pf_config_print_available_ggtt(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_gt_sriov_pf_config_print_available_ggtt(tile->primary_gt, p);
+}
+
+static int pf_config_print_ggtt(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_gt_sriov_pf_config_print_ggtt(tile->primary_gt, p);
+}
+
+static const struct drm_info_list pf_ggtt_info[] = {
+ {
+ "ggtt_available",
+ .show = xe_tile_debugfs_simple_show,
+ .data = pf_config_print_available_ggtt,
+ },
+ {
+ "ggtt_provisioned",
+ .show = xe_tile_debugfs_simple_show,
+ .data = pf_config_print_ggtt,
+ },
+};
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * : ├── pf
+ * : ├── tile0
+ * : ├── vram_provisioned
+ */
+
+static int pf_config_print_vram(struct xe_tile *tile, struct drm_printer *p)
+{
+ return xe_gt_sriov_pf_config_print_lmem(tile->primary_gt, p);
+}
+
+static const struct drm_info_list pf_vram_info[] = {
+ {
+ "vram_provisioned",
+ .show = xe_tile_debugfs_simple_show,
+ .data = pf_config_print_vram,
+ },
+};
+
+/*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── pf
+ * │ │ ├── tile0
+ * │ │ │ ├── ggtt_spare
+ * │ │ │ ├── vram_spare
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vf1
+ * │ : ├── tile0
+ * │ │ ├── ggtt_quota
+ * │ │ ├── vram_quota
+ * │ ├── tile1
+ * │ : :
+ */
+
+#define DEFINE_SRIOV_TILE_CONFIG_DEBUGFS_ATTRIBUTE(NAME, CONFIG, TYPE, FORMAT) \
+ \
+static int NAME##_set(void *data, u64 val) \
+{ \
+ struct xe_tile *tile = extract_tile(data); \
+ unsigned int vfid = extract_vfid(data); \
+ struct xe_gt *gt = tile->primary_gt; \
+ struct xe_device *xe = tile->xe; \
+ int err; \
+ \
+ if (val > (TYPE)~0ull) \
+ return -EOVERFLOW; \
+ \
+ xe_pm_runtime_get(xe); \
+ err = xe_sriov_pf_wait_ready(xe) ?: \
+ xe_gt_sriov_pf_config_set_##CONFIG(gt, vfid, val); \
+ if (!err) \
+ xe_sriov_pf_provision_set_custom_mode(xe); \
+ xe_pm_runtime_put(xe); \
+ \
+ return err; \
+} \
+ \
+static int NAME##_get(void *data, u64 *val) \
+{ \
+ struct xe_tile *tile = extract_tile(data); \
+ unsigned int vfid = extract_vfid(data); \
+ struct xe_gt *gt = tile->primary_gt; \
+ \
+ *val = xe_gt_sriov_pf_config_get_##CONFIG(gt, vfid); \
+ return 0; \
+} \
+ \
+DEFINE_DEBUGFS_ATTRIBUTE(NAME##_fops, NAME##_get, NAME##_set, FORMAT)
+
+DEFINE_SRIOV_TILE_CONFIG_DEBUGFS_ATTRIBUTE(ggtt, ggtt, u64, "%llu\n");
+DEFINE_SRIOV_TILE_CONFIG_DEBUGFS_ATTRIBUTE(vram, lmem, u64, "%llu\n");
+
+static void pf_add_config_attrs(struct xe_tile *tile, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_device *xe = tile->xe;
+
+ xe_tile_assert(tile, tile == extract_tile(dent));
+ xe_tile_assert(tile, vfid == extract_vfid(dent));
+
+ debugfs_create_file_unsafe(vfid ? "ggtt_quota" : "ggtt_spare",
+ 0644, dent, dent, &ggtt_fops);
+ if (IS_DGFX(xe))
+ debugfs_create_file_unsafe(vfid ? "vram_quota" : "vram_spare",
+ xe_device_has_lmtt(xe) ? 0644 : 0444,
+ dent, dent, &vram_fops);
+}
+
+static void pf_populate_tile(struct xe_tile *tile, struct dentry *dent, unsigned int vfid)
+{
+ struct xe_device *xe = tile->xe;
+ struct drm_minor *minor = xe->drm.primary;
+ struct xe_gt *gt;
+ unsigned int id;
+
+ pf_add_config_attrs(tile, dent, vfid);
+
+ if (!vfid) {
+ drm_debugfs_create_files(pf_ggtt_info,
+ ARRAY_SIZE(pf_ggtt_info),
+ dent, minor);
+ if (IS_DGFX(xe))
+ drm_debugfs_create_files(pf_vram_info,
+ ARRAY_SIZE(pf_vram_info),
+ dent, minor);
+ }
+
+ for_each_gt_on_tile(gt, tile, id)
+ xe_gt_sriov_pf_debugfs_populate(gt, dent, vfid);
+}
+
+/**
+ * xe_tile_sriov_pf_debugfs_populate() - Populate SR-IOV debugfs tree with tile files.
+ * @tile: the &xe_tile to register
+ * @parent: the parent &dentry that represents the SR-IOV @vfid function
+ * @vfid: the VF identifier
+ *
+ * Add to the @parent directory new debugfs directory that will represent a @tile and
+ * populate it with files that are related to the SR-IOV @vfid function.
+ *
+ * This function can only be called on PF.
+ */
+void xe_tile_sriov_pf_debugfs_populate(struct xe_tile *tile, struct dentry *parent,
+ unsigned int vfid)
+{
+ struct xe_device *xe = tile->xe;
+ struct dentry *dent;
+ char name[10]; /* should be enough up to "tile%u\0" for 2^16 - 1 */
+
+ xe_tile_assert(tile, IS_SRIOV_PF(xe));
+ xe_tile_assert(tile, extract_priv(parent->d_parent) == xe);
+ xe_tile_assert(tile, extract_priv(parent) == tile->xe ||
+ (uintptr_t)extract_priv(parent) == vfid);
+
+ /*
+ * /sys/kernel/debug/dri/BDF/
+ * ├── sriov
+ * │ ├── pf # parent, d_inode->i_private = (xe_device*)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * │ │ : :
+ * │ ├── vf1 # parent, d_inode->i_private = VFID(1)
+ * │ │ ├── tile0 # d_inode->i_private = (xe_tile*)
+ * │ │ ├── tile1
+ * : : : :
+ */
+ snprintf(name, sizeof(name), "tile%u", tile->id);
+ dent = debugfs_create_dir(name, parent);
+ if (IS_ERR(dent))
+ return;
+ dent->d_inode->i_private = tile;
+
+ xe_tile_assert(tile, extract_tile(dent) == tile);
+ xe_tile_assert(tile, extract_vfid(dent) == vfid);
+ xe_tile_assert(tile, extract_xe(dent) == xe);
+
+ pf_populate_tile(tile, dent, vfid);
+}
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h
new file mode 100644
index 000000000000..55d179c44634
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_pf_debugfs.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_TILE_SRIOV_PF_DEBUGFS_H_
+#define _XE_TILE_SRIOV_PF_DEBUGFS_H_
+
+struct dentry;
+struct xe_tile;
+
+void xe_tile_sriov_pf_debugfs_populate(struct xe_tile *tile, struct dentry *parent,
+ unsigned int vfid);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_printk.h b/drivers/gpu/drm/xe/xe_tile_sriov_printk.h
new file mode 100644
index 000000000000..68323512872c
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_printk.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_TILE_SRIOV_PRINTK_H_
+#define _XE_TILE_SRIOV_PRINTK_H_
+
+#include "xe_tile_printk.h"
+#include "xe_sriov_printk.h"
+
+#define __XE_TILE_SRIOV_PRINTK_FMT(_tile, _fmt, ...) \
+ __XE_TILE_PRINTK_FMT((_tile), _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_printk(_tile, _level, _fmt, ...) \
+ xe_sriov_##_level((_tile)->xe, __XE_TILE_SRIOV_PRINTK_FMT((_tile), _fmt, ##__VA_ARGS__))
+
+#define xe_tile_sriov_err(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, err, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_notice(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, notice, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_info(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, info, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_dbg(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, dbg, _fmt, ##__VA_ARGS__)
+
+#define xe_tile_sriov_dbg_verbose(_tile, _fmt, ...) \
+ xe_tile_sriov_printk(_tile, dbg_verbose, _fmt, ##__VA_ARGS__)
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_vf.c b/drivers/gpu/drm/xe/xe_tile_sriov_vf.c
index f221dbed16f0..c9bac2cfdd04 100644
--- a/drivers/gpu/drm/xe/xe_tile_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_vf.c
@@ -9,7 +9,6 @@
#include "xe_assert.h"
#include "xe_ggtt.h"
-#include "xe_gt_sriov_vf.h"
#include "xe_sriov.h"
#include "xe_sriov_printk.h"
#include "xe_tile_sriov_vf.h"
@@ -40,10 +39,10 @@ static int vf_init_ggtt_balloons(struct xe_tile *tile)
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_tile_sriov_vf_balloon_ggtt_locked(struct xe_tile *tile)
+static int xe_tile_sriov_vf_balloon_ggtt_locked(struct xe_tile *tile)
{
- u64 ggtt_base = xe_gt_sriov_vf_ggtt_base(tile->primary_gt);
- u64 ggtt_size = xe_gt_sriov_vf_ggtt(tile->primary_gt);
+ u64 ggtt_base = tile->sriov.vf.self_config.ggtt_base;
+ u64 ggtt_size = tile->sriov.vf.self_config.ggtt_size;
struct xe_device *xe = tile_to_xe(tile);
u64 wopcm = xe_wopcm_size(xe);
u64 start, end;
@@ -232,7 +231,7 @@ int xe_tile_sriov_vf_prepare_ggtt(struct xe_tile *tile)
*/
/**
- * xe_tile_sriov_vf_fixup_ggtt_nodes - Shift GGTT allocations to match assigned range.
+ * xe_tile_sriov_vf_fixup_ggtt_nodes_locked - Shift GGTT allocations to match assigned range.
* @tile: the &xe_tile struct instance
* @shift: the shift value
*
@@ -240,15 +239,112 @@ int xe_tile_sriov_vf_prepare_ggtt(struct xe_tile *tile)
* within the global space. This range might have changed during migration,
* which requires all memory addresses pointing to GGTT to be shifted.
*/
-void xe_tile_sriov_vf_fixup_ggtt_nodes(struct xe_tile *tile, s64 shift)
+void xe_tile_sriov_vf_fixup_ggtt_nodes_locked(struct xe_tile *tile, s64 shift)
{
struct xe_ggtt *ggtt = tile->mem.ggtt;
- mutex_lock(&ggtt->lock);
+ lockdep_assert_held(&ggtt->lock);
xe_tile_sriov_vf_deballoon_ggtt_locked(tile);
xe_ggtt_shift_nodes_locked(ggtt, shift);
xe_tile_sriov_vf_balloon_ggtt_locked(tile);
+}
- mutex_unlock(&ggtt->lock);
+/**
+ * xe_tile_sriov_vf_lmem - VF LMEM configuration.
+ * @tile: the &xe_tile
+ *
+ * This function is for VF use only.
+ *
+ * Return: size of the LMEM assigned to VF.
+ */
+u64 xe_tile_sriov_vf_lmem(struct xe_tile *tile)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ return config->lmem_size;
+}
+
+/**
+ * xe_tile_sriov_vf_lmem_store - Store VF LMEM configuration
+ * @tile: the &xe_tile
+ * @lmem_size: VF LMEM size to store
+ *
+ * This function is for VF use only.
+ */
+void xe_tile_sriov_vf_lmem_store(struct xe_tile *tile, u64 lmem_size)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ config->lmem_size = lmem_size;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt - VF GGTT configuration.
+ * @tile: the &xe_tile
+ *
+ * This function is for VF use only.
+ *
+ * Return: size of the GGTT assigned to VF.
+ */
+u64 xe_tile_sriov_vf_ggtt(struct xe_tile *tile)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ return config->ggtt_size;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt_store - Store VF GGTT configuration
+ * @tile: the &xe_tile
+ * @ggtt_size: VF GGTT size to store
+ *
+ * This function is for VF use only.
+ */
+void xe_tile_sriov_vf_ggtt_store(struct xe_tile *tile, u64 ggtt_size)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ config->ggtt_size = ggtt_size;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt_base - VF GGTT base configuration.
+ * @tile: the &xe_tile
+ *
+ * This function is for VF use only.
+ *
+ * Return: base of the GGTT assigned to VF.
+ */
+u64 xe_tile_sriov_vf_ggtt_base(struct xe_tile *tile)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ return config->ggtt_base;
+}
+
+/**
+ * xe_tile_sriov_vf_ggtt_base_store - Store VF GGTT base configuration
+ * @tile: the &xe_tile
+ * @ggtt_base: VF GGTT base to store
+ *
+ * This function is for VF use only.
+ */
+void xe_tile_sriov_vf_ggtt_base_store(struct xe_tile *tile, u64 ggtt_base)
+{
+ struct xe_tile_sriov_vf_selfconfig *config = &tile->sriov.vf.self_config;
+
+ xe_tile_assert(tile, IS_SRIOV_VF(tile_to_xe(tile)));
+
+ config->ggtt_base = ggtt_base;
}
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_vf.h b/drivers/gpu/drm/xe/xe_tile_sriov_vf.h
index 93eb043171e8..749f41504883 100644
--- a/drivers/gpu/drm/xe/xe_tile_sriov_vf.h
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_vf.h
@@ -11,8 +11,13 @@
struct xe_tile;
int xe_tile_sriov_vf_prepare_ggtt(struct xe_tile *tile);
-int xe_tile_sriov_vf_balloon_ggtt_locked(struct xe_tile *tile);
void xe_tile_sriov_vf_deballoon_ggtt_locked(struct xe_tile *tile);
-void xe_tile_sriov_vf_fixup_ggtt_nodes(struct xe_tile *tile, s64 shift);
+void xe_tile_sriov_vf_fixup_ggtt_nodes_locked(struct xe_tile *tile, s64 shift);
+u64 xe_tile_sriov_vf_ggtt(struct xe_tile *tile);
+void xe_tile_sriov_vf_ggtt_store(struct xe_tile *tile, u64 ggtt_size);
+u64 xe_tile_sriov_vf_ggtt_base(struct xe_tile *tile);
+void xe_tile_sriov_vf_ggtt_base_store(struct xe_tile *tile, u64 ggtt_size);
+u64 xe_tile_sriov_vf_lmem(struct xe_tile *tile);
+void xe_tile_sriov_vf_lmem_store(struct xe_tile *tile, u64 lmem_size);
#endif
diff --git a/drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h b/drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h
new file mode 100644
index 000000000000..4807ca51614c
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_tile_sriov_vf_types.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2025 Intel Corporation
+ */
+
+#ifndef _XE_TILE_SRIOV_VF_TYPES_H_
+#define _XE_TILE_SRIOV_VF_TYPES_H_
+
+#include <linux/types.h>
+
+/**
+ * struct xe_tile_sriov_vf_selfconfig - VF configuration data.
+ */
+struct xe_tile_sriov_vf_selfconfig {
+ /** @ggtt_base: assigned base offset of the GGTT region. */
+ u64 ggtt_base;
+ /** @ggtt_size: assigned size of the GGTT region. */
+ u64 ggtt_size;
+ /** @lmem_size: assigned size of the LMEM. */
+ u64 lmem_size;
+};
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval.h b/drivers/gpu/drm/xe/xe_tlb_inval.h
index 554634dfd4e2..05614915463a 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval.h
+++ b/drivers/gpu/drm/xe/xe_tlb_inval.h
@@ -33,7 +33,7 @@ void xe_tlb_inval_fence_init(struct xe_tlb_inval *tlb_inval,
* xe_tlb_inval_fence_wait() - TLB invalidiation fence wait
* @fence: TLB invalidation fence to wait on
*
- * Wait on a TLB invalidiation fence until it signals, non interruptable
+ * Wait on a TLB invalidiation fence until it signals, non interruptible
*/
static inline void
xe_tlb_inval_fence_wait(struct xe_tlb_inval_fence *fence)
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.c b/drivers/gpu/drm/xe/xe_tlb_inval_job.c
index 492def04a559..1ae0dec2cf31 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_job.c
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.c
@@ -12,6 +12,7 @@
#include "xe_tlb_inval_job.h"
#include "xe_migrate.h"
#include "xe_pm.h"
+#include "xe_vm.h"
/** struct xe_tlb_inval_job - TLB invalidation job */
struct xe_tlb_inval_job {
@@ -21,6 +22,8 @@ struct xe_tlb_inval_job {
struct xe_tlb_inval *tlb_inval;
/** @q: exec queue issuing the invalidate */
struct xe_exec_queue *q;
+ /** @vm: VM which TLB invalidation is being issued for */
+ struct xe_vm *vm;
/** @refcount: ref count of this job */
struct kref refcount;
/**
@@ -32,8 +35,8 @@ struct xe_tlb_inval_job {
u64 start;
/** @end: End address to invalidate */
u64 end;
- /** @asid: Address space ID to invalidate */
- u32 asid;
+ /** @type: GT type */
+ int type;
/** @fence_armed: Fence has been armed */
bool fence_armed;
};
@@ -46,7 +49,7 @@ static struct dma_fence *xe_tlb_inval_job_run(struct xe_dep_job *dep_job)
container_of(job->fence, typeof(*ifence), base);
xe_tlb_inval_range(job->tlb_inval, ifence, job->start,
- job->end, job->asid);
+ job->end, job->vm->usm.asid);
return job->fence;
}
@@ -70,9 +73,10 @@ static const struct xe_dep_job_ops dep_job_ops = {
* @q: exec queue issuing the invalidate
* @tlb_inval: TLB invalidation client
* @dep_scheduler: Dependency scheduler for job
+ * @vm: VM which TLB invalidation is being issued for
* @start: Start address to invalidate
* @end: End address to invalidate
- * @asid: Address space ID to invalidate
+ * @type: GT type
*
* Create a TLB invalidation job and initialize internal fields. The caller is
* responsible for releasing the creation reference.
@@ -81,8 +85,8 @@ static const struct xe_dep_job_ops dep_job_ops = {
*/
struct xe_tlb_inval_job *
xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
- struct xe_dep_scheduler *dep_scheduler, u64 start,
- u64 end, u32 asid)
+ struct xe_dep_scheduler *dep_scheduler,
+ struct xe_vm *vm, u64 start, u64 end, int type)
{
struct xe_tlb_inval_job *job;
struct drm_sched_entity *entity =
@@ -90,19 +94,24 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
struct xe_tlb_inval_fence *ifence;
int err;
+ xe_assert(vm->xe, type == XE_EXEC_QUEUE_TLB_INVAL_MEDIA_GT ||
+ type == XE_EXEC_QUEUE_TLB_INVAL_PRIMARY_GT);
+
job = kmalloc(sizeof(*job), GFP_KERNEL);
if (!job)
return ERR_PTR(-ENOMEM);
job->q = q;
+ job->vm = vm;
job->tlb_inval = tlb_inval;
job->start = start;
job->end = end;
- job->asid = asid;
job->fence_armed = false;
job->dep.ops = &dep_job_ops;
+ job->type = type;
kref_init(&job->refcount);
xe_exec_queue_get(q); /* Pairs with put in xe_tlb_inval_job_destroy */
+ xe_vm_get(vm); /* Pairs with put in xe_tlb_inval_job_destroy */
ifence = kmalloc(sizeof(*ifence), GFP_KERNEL);
if (!ifence) {
@@ -124,6 +133,7 @@ xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
err_fence:
kfree(ifence);
err_job:
+ xe_vm_put(vm);
xe_exec_queue_put(q);
kfree(job);
@@ -138,6 +148,7 @@ static void xe_tlb_inval_job_destroy(struct kref *ref)
container_of(job->fence, typeof(*ifence), base);
struct xe_exec_queue *q = job->q;
struct xe_device *xe = gt_to_xe(q->gt);
+ struct xe_vm *vm = job->vm;
if (!job->fence_armed)
kfree(ifence);
@@ -147,6 +158,7 @@ static void xe_tlb_inval_job_destroy(struct kref *ref)
drm_sched_job_cleanup(&job->dep.drm);
kfree(job);
+ xe_vm_put(vm); /* Pairs with get from xe_tlb_inval_job_create */
xe_exec_queue_put(q); /* Pairs with get from xe_tlb_inval_job_create */
xe_pm_runtime_put(xe); /* Pairs with get from xe_tlb_inval_job_create */
}
@@ -231,6 +243,11 @@ struct dma_fence *xe_tlb_inval_job_push(struct xe_tlb_inval_job *job,
dma_fence_get(&job->dep.drm.s_fence->finished);
drm_sched_entity_push_job(&job->dep.drm);
+ /* Let the upper layers fish this out */
+ xe_exec_queue_tlb_inval_last_fence_set(job->q, job->vm,
+ &job->dep.drm.s_fence->finished,
+ job->type);
+
xe_migrate_job_unlock(m, job->q);
/*
diff --git a/drivers/gpu/drm/xe/xe_tlb_inval_job.h b/drivers/gpu/drm/xe/xe_tlb_inval_job.h
index e63edcb26b50..4d6df1a6c6ca 100644
--- a/drivers/gpu/drm/xe/xe_tlb_inval_job.h
+++ b/drivers/gpu/drm/xe/xe_tlb_inval_job.h
@@ -11,14 +11,15 @@
struct dma_fence;
struct xe_dep_scheduler;
struct xe_exec_queue;
+struct xe_migrate;
struct xe_tlb_inval;
struct xe_tlb_inval_job;
-struct xe_migrate;
+struct xe_vm;
struct xe_tlb_inval_job *
xe_tlb_inval_job_create(struct xe_exec_queue *q, struct xe_tlb_inval *tlb_inval,
struct xe_dep_scheduler *dep_scheduler,
- u64 start, u64 end, u32 asid);
+ struct xe_vm *vm, u64 start, u64 end, int type);
int xe_tlb_inval_job_alloc_dep(struct xe_tlb_inval_job *job);
diff --git a/drivers/gpu/drm/xe/xe_trace.h b/drivers/gpu/drm/xe/xe_trace.h
index 314f42fcbcbd..79a97b086cb2 100644
--- a/drivers/gpu/drm/xe/xe_trace.h
+++ b/drivers/gpu/drm/xe/xe_trace.h
@@ -441,6 +441,29 @@ TRACE_EVENT(xe_eu_stall_data_read,
__entry->read_size, __entry->total_size)
);
+TRACE_EVENT(xe_exec_queue_reach_max_job_count,
+ TP_PROTO(struct xe_exec_queue *q, int max_cnt),
+ TP_ARGS(q, max_cnt),
+
+ TP_STRUCT__entry(__string(dev, __dev_name_eq(q))
+ __field(enum xe_engine_class, class)
+ __field(u32, logical_mask)
+ __field(u16, guc_id)
+ __field(int, max_cnt)
+ ),
+
+ TP_fast_assign(__assign_str(dev);
+ __entry->class = q->class;
+ __entry->logical_mask = q->logical_mask;
+ __entry->guc_id = q->guc->id;
+ __entry->max_cnt = max_cnt;
+ ),
+
+ TP_printk("dev=%s, job count exceeded the maximum limit (%d) per exec queue. engine_class=0x%x, logical_mask=0x%x, guc_id=%d",
+ __get_str(dev), __entry->max_cnt,
+ __entry->class, __entry->logical_mask, __entry->guc_id)
+);
+
#endif
/* This part must be outside protection */
diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
index dc588255674d..1bddecfb723a 100644
--- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021-2023 Intel Corporation
- * Copyright (C) 2021-2002 Red Hat
+ * Copyright (C) 2021-2022 Red Hat
*/
#include <drm/drm_managed.h>
@@ -24,8 +24,8 @@
#include "xe_sriov.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_ttm_vram_mgr.h"
-#include "xe_wa.h"
#include "xe_vram.h"
+#include "xe_wa.h"
struct xe_ttm_stolen_mgr {
struct xe_ttm_vram_mgr base;
@@ -81,7 +81,7 @@ static u32 get_wopcm_size(struct xe_device *xe)
return wopcm_size;
}
-static s64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
+static u64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
{
struct xe_vram_region *tile_vram = xe_device_get_root_tile(xe)->mem.vram;
resource_size_t tile_io_start = xe_vram_region_io_start(tile_vram);
@@ -105,6 +105,8 @@ static s64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
return 0;
stolen_size = tile_size - mgr->stolen_base;
+
+ xe_assert(xe, stolen_size >= wopcm_size);
stolen_size -= wopcm_size;
/* Verify usage fits in the actual resource available */
diff --git a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
index d38b91872da3..3e404eb8d098 100644
--- a/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_sys_mgr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021-2022 Intel Corporation
- * Copyright (C) 2021-2002 Red Hat
+ * Copyright (C) 2021-2022 Red Hat
*/
#include "xe_ttm_sys_mgr.h"
@@ -85,7 +85,7 @@ static const struct ttm_resource_manager_func xe_ttm_sys_mgr_func = {
.debug = xe_ttm_sys_mgr_debug
};
-static void ttm_sys_mgr_fini(struct drm_device *drm, void *arg)
+static void xe_ttm_sys_mgr_fini(struct drm_device *drm, void *arg)
{
struct xe_device *xe = (struct xe_device *)arg;
struct ttm_resource_manager *man = &xe->mem.sys_mgr;
@@ -116,5 +116,5 @@ int xe_ttm_sys_mgr_init(struct xe_device *xe)
ttm_resource_manager_init(man, &xe->ttm, gtt_size >> PAGE_SHIFT);
ttm_set_driver_manager(&xe->ttm, XE_PL_TT, man);
ttm_resource_manager_set_used(man, true);
- return drmm_add_action_or_reset(&xe->drm, ttm_sys_mgr_fini, xe);
+ return drmm_add_action_or_reset(&xe->drm, xe_ttm_sys_mgr_fini, xe);
}
diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
index 9175b4a2214b..9f70802fce92 100644
--- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
/*
* Copyright © 2021-2022 Intel Corporation
- * Copyright (C) 2021-2002 Red Hat
+ * Copyright (C) 2021-2022 Red Hat
*/
#include <drm/drm_managed.h>
@@ -284,7 +284,7 @@ static const struct ttm_resource_manager_func xe_ttm_vram_mgr_func = {
.debug = xe_ttm_vram_mgr_debug
};
-static void ttm_vram_mgr_fini(struct drm_device *dev, void *arg)
+static void xe_ttm_vram_mgr_fini(struct drm_device *dev, void *arg)
{
struct xe_device *xe = to_xe_device(dev);
struct xe_ttm_vram_mgr *mgr = arg;
@@ -335,7 +335,7 @@ int __xe_ttm_vram_mgr_init(struct xe_device *xe, struct xe_ttm_vram_mgr *mgr,
ttm_set_driver_manager(&xe->ttm, mem_type, &mgr->manager);
ttm_resource_manager_set_used(&mgr->manager, true);
- return drmm_add_action_or_reset(&xe->drm, ttm_vram_mgr_fini, mgr);
+ return drmm_add_action_or_reset(&xe->drm, xe_ttm_vram_mgr_fini, mgr);
}
/**
diff --git a/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h b/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h
index 1144f9232ebb..a71e14818ec2 100644
--- a/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h
+++ b/drivers/gpu/drm/xe/xe_ttm_vram_mgr_types.h
@@ -10,7 +10,7 @@
#include <drm/ttm/ttm_device.h>
/**
- * struct xe_ttm_vram_mgr - XE TTM VRAM manager
+ * struct xe_ttm_vram_mgr - Xe TTM VRAM manager
*
* Manages placement of TTM resource in VRAM.
*/
@@ -32,7 +32,7 @@ struct xe_ttm_vram_mgr {
};
/**
- * struct xe_ttm_vram_mgr_resource - XE TTM VRAM resource
+ * struct xe_ttm_vram_mgr_resource - Xe TTM VRAM resource
*/
struct xe_ttm_vram_mgr_resource {
/** @base: Base TTM resource */
diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c
index a524170a04d0..5766fa7742d3 100644
--- a/drivers/gpu/drm/xe/xe_tuning.c
+++ b/drivers/gpu/drm/xe/xe_tuning.c
@@ -8,6 +8,7 @@
#include <kunit/visibility.h>
#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
#include "regs/xe_gt_regs.h"
#include "xe_gt_types.h"
@@ -40,7 +41,8 @@ static const struct xe_rtp_entry_sr gt_tunings[] = {
REG_FIELD_PREP(L3_PWM_TIMER_INIT_VAL_MASK, 0x7f)))
},
{ XE_RTP_NAME("Tuning: Compression Overfetch"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED),
+ FUNC(xe_rtp_match_has_flat_ccs)),
XE_RTP_ACTIONS(CLR(CCCHKNREG1, ENCOMPPERFFIX),
SET(CCCHKNREG1, L3CMPCTRL))
},
@@ -58,12 +60,14 @@ static const struct xe_rtp_entry_sr gt_tunings[] = {
XE_RTP_ACTIONS(SET(XE2LPM_L3SQCREG3, COMPPWOVERFETCHEN))
},
{ XE_RTP_NAME("Tuning: L2 Overfetch Compressible Only"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED),
+ FUNC(xe_rtp_match_has_flat_ccs)),
XE_RTP_ACTIONS(SET(L3SQCREG2,
COMPMEMRD256BOVRFETCHEN))
},
{ XE_RTP_NAME("Tuning: L2 Overfetch Compressible Only - media"),
- XE_RTP_RULES(MEDIA_VERSION_RANGE(2000, XE_RTP_END_VERSION_UNDEFINED)),
+ XE_RTP_RULES(MEDIA_VERSION_RANGE(2000, XE_RTP_END_VERSION_UNDEFINED),
+ FUNC(xe_rtp_match_has_flat_ccs)),
XE_RTP_ACTIONS(SET(XE2LPM_L3SQCREG2,
COMPMEMRD256BOVRFETCHEN))
},
@@ -214,7 +218,14 @@ void xe_tuning_process_lrc(struct xe_hw_engine *hwe)
xe_rtp_process_to_sr(&ctx, lrc_tunings, ARRAY_SIZE(lrc_tunings), &hwe->reg_lrc);
}
-void xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_tuning_dump() - Dump GT tuning info into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: always 0.
+ */
+int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p)
{
size_t idx;
@@ -222,11 +233,15 @@ void xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p)
for_each_set_bit(idx, gt->tuning_active.gt, ARRAY_SIZE(gt_tunings))
drm_printf_indent(p, 1, "%s\n", gt_tunings[idx].name);
- drm_printf(p, "\nEngine Tunings\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "Engine Tunings\n");
for_each_set_bit(idx, gt->tuning_active.engine, ARRAY_SIZE(engine_tunings))
drm_printf_indent(p, 1, "%s\n", engine_tunings[idx].name);
- drm_printf(p, "\nLRC Tunings\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "LRC Tunings\n");
for_each_set_bit(idx, gt->tuning_active.lrc, ARRAY_SIZE(lrc_tunings))
drm_printf_indent(p, 1, "%s\n", lrc_tunings[idx].name);
+
+ return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_tuning.h b/drivers/gpu/drm/xe/xe_tuning.h
index dd0d3ccc9c65..c1cc5927fda7 100644
--- a/drivers/gpu/drm/xe/xe_tuning.h
+++ b/drivers/gpu/drm/xe/xe_tuning.h
@@ -14,6 +14,6 @@ int xe_tuning_init(struct xe_gt *gt);
void xe_tuning_process_gt(struct xe_gt *gt);
void xe_tuning_process_engine(struct xe_hw_engine *hwe);
void xe_tuning_process_lrc(struct xe_hw_engine *hwe);
-void xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p);
#endif
diff --git a/drivers/gpu/drm/xe/xe_uc_fw_types.h b/drivers/gpu/drm/xe/xe_uc_fw_types.h
index 77a1dcf8b4ed..2ebe8c9db6ce 100644
--- a/drivers/gpu/drm/xe/xe_uc_fw_types.h
+++ b/drivers/gpu/drm/xe/xe_uc_fw_types.h
@@ -62,7 +62,7 @@ enum xe_uc_fw_type {
};
/**
- * struct xe_uc_fw_version - Version for XE micro controller firmware
+ * struct xe_uc_fw_version - Version for Xe micro controller firmware
*/
struct xe_uc_fw_version {
/** @branch: branch version of the FW (not always available) */
@@ -84,7 +84,7 @@ enum xe_uc_fw_version_types {
};
/**
- * struct xe_uc_fw - XE micro controller firmware
+ * struct xe_uc_fw - Xe micro controller firmware
*/
struct xe_uc_fw {
/** @type: type uC firmware */
@@ -112,7 +112,7 @@ struct xe_uc_fw {
/** @size: size of uC firmware including css header */
size_t size;
- /** @bo: XE BO for uC firmware */
+ /** @bo: Xe BO for uC firmware */
struct xe_bo *bo;
/** @has_gsc_headers: whether the FW image starts with GSC headers */
diff --git a/drivers/gpu/drm/xe/xe_uc_types.h b/drivers/gpu/drm/xe/xe_uc_types.h
index 9924e4484866..1708379dc834 100644
--- a/drivers/gpu/drm/xe/xe_uc_types.h
+++ b/drivers/gpu/drm/xe/xe_uc_types.h
@@ -12,7 +12,7 @@
#include "xe_wopcm_types.h"
/**
- * struct xe_uc - XE micro controllers
+ * struct xe_uc - Xe micro controllers
*/
struct xe_uc {
/** @guc: Graphics micro controller */
diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c
index f16e92cd8090..0d9130b1958a 100644
--- a/drivers/gpu/drm/xe/xe_userptr.c
+++ b/drivers/gpu/drm/xe/xe_userptr.c
@@ -3,6 +3,7 @@
* Copyright © 2025 Intel Corporation
*/
+#include "xe_svm.h"
#include "xe_userptr.h"
#include <linux/mm.h>
@@ -54,7 +55,8 @@ int xe_vma_userptr_pin_pages(struct xe_userptr_vma *uvma)
struct xe_device *xe = vm->xe;
struct drm_gpusvm_ctx ctx = {
.read_only = xe_vma_read_only(vma),
- .device_private_page_owner = NULL,
+ .device_private_page_owner = xe_svm_devm_owner(xe),
+ .allow_mixed = true,
};
lockdep_assert_held(&vm->lock);
diff --git a/drivers/gpu/drm/xe/xe_validation.h b/drivers/gpu/drm/xe/xe_validation.h
index fec331d791e7..a30e732c4d51 100644
--- a/drivers/gpu/drm/xe/xe_validation.h
+++ b/drivers/gpu/drm/xe/xe_validation.h
@@ -108,7 +108,7 @@ struct xe_val_flags {
* @request_exclusive: Whether to lock exclusively (write mode) the next time
* the domain lock is locked.
* @exec_flags: The drm_exec flags used for drm_exec (re-)initialization.
- * @nr: The drm_exec nr parameter used for drm_exec (re-)initializaiton.
+ * @nr: The drm_exec nr parameter used for drm_exec (re-)initialization.
*/
struct xe_validation_ctx {
struct drm_exec *exec;
@@ -137,7 +137,7 @@ bool xe_validation_should_retry(struct xe_validation_ctx *ctx, int *ret);
* @_ret: The current error value possibly holding -ENOMEM
*
* Use this in way similar to drm_exec_retry_on_contention().
- * If @_ret contains -ENOMEM the tranaction is restarted once in a way that
+ * If @_ret contains -ENOMEM the transaction is restarted once in a way that
* blocks other transactions and allows exhastive eviction. If the transaction
* was already restarted once, Just return the -ENOMEM. May also set
* _ret to -EINTR if not retrying and waits are interruptible.
@@ -166,10 +166,10 @@ xe_validation_device_init(struct xe_validation_device *val)
*/
DEFINE_CLASS(xe_validation, struct xe_validation_ctx *,
if (_T) xe_validation_ctx_fini(_T);,
- ({_ret = xe_validation_ctx_init(_ctx, _val, _exec, _flags);
- _ret ? NULL : _ctx; }),
+ ({*_ret = xe_validation_ctx_init(_ctx, _val, _exec, _flags);
+ *_ret ? NULL : _ctx; }),
struct xe_validation_ctx *_ctx, struct xe_validation_device *_val,
- struct drm_exec *_exec, const struct xe_val_flags _flags, int _ret);
+ struct drm_exec *_exec, const struct xe_val_flags _flags, int *_ret);
static inline void *class_xe_validation_lock_ptr(class_xe_validation_t *_T)
{return *_T; }
#define class_xe_validation_is_conditional true
@@ -180,13 +180,13 @@ static inline void *class_xe_validation_lock_ptr(class_xe_validation_t *_T)
* @_val: The xe_validation_device.
* @_exec: The struct drm_exec object
* @_flags: Flags for the xe_validation_ctx initialization.
- * @_ret: Return in / out parameter. May be set by this macro. Typicall 0 when called.
+ * @_ret: Return in / out parameter. May be set by this macro. Typically 0 when called.
*
* This macro is will initiate a drm_exec transaction with additional support for
* exhaustive eviction.
*/
#define xe_validation_guard(_ctx, _val, _exec, _flags, _ret) \
- scoped_guard(xe_validation, _ctx, _val, _exec, _flags, _ret) \
+ scoped_guard(xe_validation, _ctx, _val, _exec, _flags, &_ret) \
drm_exec_until_all_locked(_exec)
#endif
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index f602b874e054..7cac646bdf1c 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -27,7 +27,6 @@
#include "xe_device.h"
#include "xe_drm_client.h"
#include "xe_exec_queue.h"
-#include "xe_gt_pagefault.h"
#include "xe_migrate.h"
#include "xe_pat.h"
#include "xe_pm.h"
@@ -35,6 +34,7 @@
#include "xe_pt.h"
#include "xe_pxp.h"
#include "xe_res_cursor.h"
+#include "xe_sriov_vf.h"
#include "xe_svm.h"
#include "xe_sync.h"
#include "xe_tile.h"
@@ -111,12 +111,22 @@ static int alloc_preempt_fences(struct xe_vm *vm, struct list_head *list,
static int wait_for_existing_preempt_fences(struct xe_vm *vm)
{
struct xe_exec_queue *q;
+ bool vf_migration = IS_SRIOV_VF(vm->xe) &&
+ xe_sriov_vf_migration_supported(vm->xe);
+ signed long wait_time = vf_migration ? HZ / 5 : MAX_SCHEDULE_TIMEOUT;
xe_vm_assert_held(vm);
list_for_each_entry(q, &vm->preempt.exec_queues, lr.link) {
if (q->lr.pfence) {
- long timeout = dma_fence_wait(q->lr.pfence, false);
+ long timeout;
+
+ timeout = dma_fence_wait_timeout(q->lr.pfence, false,
+ wait_time);
+ if (!timeout) {
+ xe_assert(vm->xe, vf_migration);
+ return -EAGAIN;
+ }
/* Only -ETIME on fence indicates VM needs to be killed */
if (timeout < 0 || q->lr.pfence->error == -ETIME)
@@ -466,6 +476,8 @@ static void preempt_rebind_work_func(struct work_struct *w)
retry:
if (!try_wait_for_completion(&vm->xe->pm_block) && vm_suspend_rebind_worker(vm)) {
up_write(&vm->lock);
+ /* We don't actually block but don't make progress. */
+ xe_pm_might_block_on_suspend();
return;
}
@@ -539,6 +551,19 @@ out_unlock:
out_unlock_outer:
if (err == -EAGAIN) {
trace_xe_vm_rebind_worker_retry(vm);
+
+ /*
+ * We can't block in workers on a VF which supports migration
+ * given this can block the VF post-migration workers from
+ * getting scheduled.
+ */
+ if (IS_SRIOV_VF(vm->xe) &&
+ xe_sriov_vf_migration_supported(vm->xe)) {
+ up_write(&vm->lock);
+ xe_vm_queue_rebind_worker(vm);
+ return;
+ }
+
goto retry;
}
@@ -616,6 +641,13 @@ static void xe_vma_ops_incr_pt_update_ops(struct xe_vma_ops *vops, u8 tile_mask,
vops->pt_update_ops[i].num_ops += inc_val;
}
+#define XE_VMA_CREATE_MASK ( \
+ XE_VMA_READ_ONLY | \
+ XE_VMA_DUMPABLE | \
+ XE_VMA_SYSTEM_ALLOCATOR | \
+ DRM_GPUVA_SPARSE | \
+ XE_VMA_MADV_AUTORESET)
+
static void xe_vm_populate_rebind(struct xe_vma_op *op, struct xe_vma *vma,
u8 tile_mask)
{
@@ -628,8 +660,7 @@ static void xe_vm_populate_rebind(struct xe_vma_op *op, struct xe_vma *vma,
op->base.map.gem.offset = vma->gpuva.gem.offset;
op->map.vma = vma;
op->map.immediate = true;
- op->map.dumpable = vma->gpuva.flags & XE_VMA_DUMPABLE;
- op->map.is_null = xe_vma_is_null(vma);
+ op->map.vma_flags = vma->gpuva.flags & XE_VMA_CREATE_MASK;
}
static int xe_vm_ops_add_rebind(struct xe_vma_ops *vops, struct xe_vma *vma,
@@ -723,6 +754,7 @@ struct dma_fence *xe_vma_rebind(struct xe_vm *vm, struct xe_vma *vma, u8 tile_ma
xe_assert(vm->xe, xe_vm_in_fault_mode(vm));
xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
+ vops.flags |= XE_VMA_OPS_FLAG_SKIP_TLB_WAIT;
for_each_tile(tile, vm->xe, id) {
vops.pt_update_ops[id].wait_vm_bookkeep = true;
vops.pt_update_ops[tile->id].q =
@@ -792,7 +824,7 @@ xe_vm_ops_add_range_rebind(struct xe_vma_ops *vops,
*
* (re)bind SVM range setting up GPU page tables for the range.
*
- * Return: dma fence for rebind to signal completion on succees, ERR_PTR on
+ * Return: dma fence for rebind to signal completion on success, ERR_PTR on
* failure
*/
struct dma_fence *xe_vm_range_rebind(struct xe_vm *vm,
@@ -813,6 +845,7 @@ struct dma_fence *xe_vm_range_rebind(struct xe_vm *vm,
xe_assert(vm->xe, xe_vma_is_cpu_addr_mirror(vma));
xe_vma_ops_init(&vops, vm, NULL, NULL, 0);
+ vops.flags |= XE_VMA_OPS_FLAG_SKIP_TLB_WAIT;
for_each_tile(tile, vm->xe, id) {
vops.pt_update_ops[id].wait_vm_bookkeep = true;
vops.pt_update_ops[tile->id].q =
@@ -875,7 +908,7 @@ xe_vm_ops_add_range_unbind(struct xe_vma_ops *vops,
*
* Unbind SVM range removing the GPU page tables for the range.
*
- * Return: dma fence for unbind to signal completion on succees, ERR_PTR on
+ * Return: dma fence for unbind to signal completion on success, ERR_PTR on
* failure
*/
struct dma_fence *xe_vm_range_unbind(struct xe_vm *vm,
@@ -932,11 +965,6 @@ static void xe_vma_free(struct xe_vma *vma)
kfree(vma);
}
-#define VMA_CREATE_FLAG_READ_ONLY BIT(0)
-#define VMA_CREATE_FLAG_IS_NULL BIT(1)
-#define VMA_CREATE_FLAG_DUMPABLE BIT(2)
-#define VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR BIT(3)
-
static struct xe_vma *xe_vma_create(struct xe_vm *vm,
struct xe_bo *bo,
u64 bo_offset_or_userptr,
@@ -947,11 +975,8 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
struct xe_vma *vma;
struct xe_tile *tile;
u8 id;
- bool read_only = (flags & VMA_CREATE_FLAG_READ_ONLY);
- bool is_null = (flags & VMA_CREATE_FLAG_IS_NULL);
- bool dumpable = (flags & VMA_CREATE_FLAG_DUMPABLE);
- bool is_cpu_addr_mirror =
- (flags & VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR);
+ bool is_null = (flags & DRM_GPUVA_SPARSE);
+ bool is_cpu_addr_mirror = (flags & XE_VMA_SYSTEM_ALLOCATOR);
xe_assert(vm->xe, start < end);
xe_assert(vm->xe, end < vm->size);
@@ -972,10 +997,6 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
if (!vma)
return ERR_PTR(-ENOMEM);
- if (is_cpu_addr_mirror)
- vma->gpuva.flags |= XE_VMA_SYSTEM_ALLOCATOR;
- if (is_null)
- vma->gpuva.flags |= DRM_GPUVA_SPARSE;
if (bo)
vma->gpuva.gem.obj = &bo->ttm.base;
}
@@ -986,10 +1007,7 @@ static struct xe_vma *xe_vma_create(struct xe_vm *vm,
vma->gpuva.vm = &vm->gpuvm;
vma->gpuva.va.addr = start;
vma->gpuva.va.range = end - start + 1;
- if (read_only)
- vma->gpuva.flags |= XE_VMA_READ_ONLY;
- if (dumpable)
- vma->gpuva.flags |= XE_VMA_DUMPABLE;
+ vma->gpuva.flags = flags;
for_each_tile(tile, vm->xe, id)
vma->tile_mask |= 0x1 << id;
@@ -1274,7 +1292,7 @@ static u16 pde_pat_index(struct xe_bo *bo)
* selection of options. The user PAT index is only for encoding leaf
* nodes, where we have use of more bits to do the encoding. The
* non-leaf nodes are instead under driver control so the chosen index
- * here should be distict from the user PAT index. Also the
+ * here should be distinct from the user PAT index. Also the
* corresponding coherency of the PAT index should be tied to the
* allocation type of the page table (or at least we should pick
* something which is always safe).
@@ -1441,7 +1459,7 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
struct xe_validation_ctx ctx;
struct drm_exec exec;
struct xe_vm *vm;
- int err, number_tiles = 0;
+ int err;
struct xe_tile *tile;
u8 id;
@@ -1602,13 +1620,9 @@ struct xe_vm *xe_vm_create(struct xe_device *xe, u32 flags, struct xe_file *xef)
goto err_close;
}
vm->q[id] = q;
- number_tiles++;
}
}
- if (number_tiles > 1)
- vm->composite_fence_ctx = dma_fence_context_alloc(1);
-
if (xef && xe->info.has_asid) {
u32 asid;
@@ -1714,8 +1728,13 @@ void xe_vm_close_and_put(struct xe_vm *vm)
down_write(&vm->lock);
for_each_tile(tile, xe, id) {
- if (vm->q[id])
+ if (vm->q[id]) {
+ int i;
+
xe_exec_queue_last_fence_put(vm->q[id], vm);
+ for_each_tlb_inval(i)
+ xe_exec_queue_tlb_inval_last_fence_put(vm->q[id], vm, i);
+ }
}
up_write(&vm->lock);
@@ -1884,6 +1903,7 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data,
struct xe_device *xe = to_xe_device(dev);
struct xe_file *xef = to_xe_file(file);
struct drm_xe_vm_create *args = data;
+ struct xe_gt *wa_gt = xe_root_mmio_gt(xe);
struct xe_vm *vm;
u32 id;
int err;
@@ -1892,7 +1912,7 @@ int xe_vm_create_ioctl(struct drm_device *dev, void *data,
if (XE_IOCTL_DBG(xe, args->extensions))
return -EINVAL;
- if (XE_GT_WA(xe_root_mmio_gt(xe), 14016763929))
+ if (wa_gt && XE_GT_WA(wa_gt, 22014953428))
args->flags |= DRM_XE_VM_CREATE_FLAG_SCRATCH_PAGE;
if (XE_IOCTL_DBG(xe, args->flags & DRM_XE_VM_CREATE_FLAG_FAULT_MODE &&
@@ -2272,12 +2292,16 @@ vm_bind_ioctl_ops_create(struct xe_vm *vm, struct xe_vma_ops *vops,
if (__op->op == DRM_GPUVA_OP_MAP) {
op->map.immediate =
flags & DRM_XE_VM_BIND_FLAG_IMMEDIATE;
- op->map.read_only =
- flags & DRM_XE_VM_BIND_FLAG_READONLY;
- op->map.is_null = flags & DRM_XE_VM_BIND_FLAG_NULL;
- op->map.is_cpu_addr_mirror = flags &
- DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR;
- op->map.dumpable = flags & DRM_XE_VM_BIND_FLAG_DUMPABLE;
+ if (flags & DRM_XE_VM_BIND_FLAG_READONLY)
+ op->map.vma_flags |= XE_VMA_READ_ONLY;
+ if (flags & DRM_XE_VM_BIND_FLAG_NULL)
+ op->map.vma_flags |= DRM_GPUVA_SPARSE;
+ if (flags & DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR)
+ op->map.vma_flags |= XE_VMA_SYSTEM_ALLOCATOR;
+ if (flags & DRM_XE_VM_BIND_FLAG_DUMPABLE)
+ op->map.vma_flags |= XE_VMA_DUMPABLE;
+ if (flags & DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET)
+ op->map.vma_flags |= XE_VMA_MADV_AUTORESET;
op->map.pat_index = pat_index;
op->map.invalidate_on_bind =
__xe_vm_needs_clear_scratch_pages(vm, flags);
@@ -2590,14 +2614,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
.pat_index = op->map.pat_index,
};
- flags |= op->map.read_only ?
- VMA_CREATE_FLAG_READ_ONLY : 0;
- flags |= op->map.is_null ?
- VMA_CREATE_FLAG_IS_NULL : 0;
- flags |= op->map.dumpable ?
- VMA_CREATE_FLAG_DUMPABLE : 0;
- flags |= op->map.is_cpu_addr_mirror ?
- VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR : 0;
+ flags |= op->map.vma_flags & XE_VMA_CREATE_MASK;
vma = new_vma(vm, &op->base.map, &default_attr,
flags);
@@ -2606,7 +2623,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
op->map.vma = vma;
if (((op->map.immediate || !xe_vm_in_fault_mode(vm)) &&
- !op->map.is_cpu_addr_mirror) ||
+ !(op->map.vma_flags & XE_VMA_SYSTEM_ALLOCATOR)) ||
op->map.invalidate_on_bind)
xe_vma_ops_incr_pt_update_ops(vops,
op->tile_mask, 1);
@@ -2637,18 +2654,7 @@ static int vm_bind_ioctl_ops_parse(struct xe_vm *vm, struct drm_gpuva_ops *ops,
op->remap.start = xe_vma_start(old);
op->remap.range = xe_vma_size(old);
- flags |= op->base.remap.unmap->va->flags &
- XE_VMA_READ_ONLY ?
- VMA_CREATE_FLAG_READ_ONLY : 0;
- flags |= op->base.remap.unmap->va->flags &
- DRM_GPUVA_SPARSE ?
- VMA_CREATE_FLAG_IS_NULL : 0;
- flags |= op->base.remap.unmap->va->flags &
- XE_VMA_DUMPABLE ?
- VMA_CREATE_FLAG_DUMPABLE : 0;
- flags |= xe_vma_is_cpu_addr_mirror(old) ?
- VMA_CREATE_FLAG_IS_SYSTEM_ALLOCATOR : 0;
-
+ flags |= op->base.remap.unmap->va->flags & XE_VMA_CREATE_MASK;
if (op->base.remap.prev) {
vma = new_vma(vm, op->base.remap.prev,
&old->attr, flags);
@@ -3098,20 +3104,31 @@ static struct dma_fence *ops_execute(struct xe_vm *vm,
struct dma_fence *fence = NULL;
struct dma_fence **fences = NULL;
struct dma_fence_array *cf = NULL;
- int number_tiles = 0, current_fence = 0, err;
+ int number_tiles = 0, current_fence = 0, n_fence = 0, err;
u8 id;
number_tiles = vm_ops_setup_tile_args(vm, vops);
if (number_tiles == 0)
return ERR_PTR(-ENODATA);
- if (number_tiles > 1) {
- fences = kmalloc_array(number_tiles, sizeof(*fences),
- GFP_KERNEL);
- if (!fences) {
- fence = ERR_PTR(-ENOMEM);
- goto err_trace;
- }
+ if (vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT) {
+ for_each_tile(tile, vm->xe, id)
+ ++n_fence;
+ } else {
+ for_each_tile(tile, vm->xe, id)
+ n_fence += (1 + XE_MAX_GT_PER_TILE);
+ }
+
+ fences = kmalloc_array(n_fence, sizeof(*fences), GFP_KERNEL);
+ if (!fences) {
+ fence = ERR_PTR(-ENOMEM);
+ goto err_trace;
+ }
+
+ cf = dma_fence_array_alloc(n_fence);
+ if (!cf) {
+ fence = ERR_PTR(-ENOMEM);
+ goto err_out;
}
for_each_tile(tile, vm->xe, id) {
@@ -3128,30 +3145,34 @@ static struct dma_fence *ops_execute(struct xe_vm *vm,
trace_xe_vm_ops_execute(vops);
for_each_tile(tile, vm->xe, id) {
+ struct xe_exec_queue *q = vops->pt_update_ops[tile->id].q;
+ int i;
+
+ fence = NULL;
if (!vops->pt_update_ops[id].num_ops)
- continue;
+ goto collect_fences;
fence = xe_pt_update_ops_run(tile, vops);
if (IS_ERR(fence))
goto err_out;
- if (fences)
- fences[current_fence++] = fence;
- }
+collect_fences:
+ fences[current_fence++] = fence ?: dma_fence_get_stub();
+ if (vops->flags & XE_VMA_OPS_FLAG_SKIP_TLB_WAIT)
+ continue;
- if (fences) {
- cf = dma_fence_array_create(number_tiles, fences,
- vm->composite_fence_ctx,
- vm->composite_fence_seqno++,
- false);
- if (!cf) {
- --vm->composite_fence_seqno;
- fence = ERR_PTR(-ENOMEM);
- goto err_out;
- }
- fence = &cf->base;
+ xe_migrate_job_lock(tile->migrate, q);
+ for_each_tlb_inval(i)
+ fences[current_fence++] =
+ xe_exec_queue_tlb_inval_last_fence_get(q, vm, i);
+ xe_migrate_job_unlock(tile->migrate, q);
}
+ xe_assert(vm->xe, current_fence == n_fence);
+ dma_fence_array_init(cf, n_fence, fences, dma_fence_context_alloc(1),
+ 1, false);
+ fence = &cf->base;
+
for_each_tile(tile, vm->xe, id) {
if (!vops->pt_update_ops[id].num_ops)
continue;
@@ -3211,7 +3232,6 @@ static void op_add_ufence(struct xe_vm *vm, struct xe_vma_op *op,
static void vm_bind_ioctl_ops_fini(struct xe_vm *vm, struct xe_vma_ops *vops,
struct dma_fence *fence)
{
- struct xe_exec_queue *wait_exec_queue = to_wait_exec_queue(vm, vops->q);
struct xe_user_fence *ufence;
struct xe_vma_op *op;
int i;
@@ -3232,7 +3252,6 @@ static void vm_bind_ioctl_ops_fini(struct xe_vm *vm, struct xe_vma_ops *vops,
if (fence) {
for (i = 0; i < vops->num_syncs; i++)
xe_sync_entry_signal(vops->syncs + i, fence);
- xe_exec_queue_last_fence_set(wait_exec_queue, vm, fence);
}
}
@@ -3279,7 +3298,8 @@ ALLOW_ERROR_INJECTION(vm_bind_ioctl_ops_execute, ERRNO);
DRM_XE_VM_BIND_FLAG_NULL | \
DRM_XE_VM_BIND_FLAG_DUMPABLE | \
DRM_XE_VM_BIND_FLAG_CHECK_PXP | \
- DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR)
+ DRM_XE_VM_BIND_FLAG_CPU_ADDR_MIRROR | \
+ DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET)
#ifdef TEST_VM_OPS_ERROR
#define SUPPORTED_FLAGS (SUPPORTED_FLAGS_STUB | FORCE_OP_ERROR)
@@ -3391,10 +3411,14 @@ static int vm_bind_ioctl_check_args(struct xe_device *xe, struct xe_vm *vm,
op == DRM_XE_VM_BIND_OP_PREFETCH) ||
XE_IOCTL_DBG(xe, prefetch_region &&
op != DRM_XE_VM_BIND_OP_PREFETCH) ||
- XE_IOCTL_DBG(xe, (prefetch_region != DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC &&
- !(BIT(prefetch_region) & xe->info.mem_region_mask))) ||
+ XE_IOCTL_DBG(xe, (prefetch_region != DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC &&
+ /* Guard against undefined shift in BIT(prefetch_region) */
+ (prefetch_region >= (sizeof(xe->info.mem_region_mask) * 8) ||
+ !(BIT(prefetch_region) & xe->info.mem_region_mask)))) ||
XE_IOCTL_DBG(xe, obj &&
- op == DRM_XE_VM_BIND_OP_UNMAP)) {
+ op == DRM_XE_VM_BIND_OP_UNMAP) ||
+ XE_IOCTL_DBG(xe, (flags & DRM_XE_VM_BIND_FLAG_MADVISE_AUTORESET) &&
+ (!is_cpu_addr_mirror || op != DRM_XE_VM_BIND_OP_MAP))) {
err = -EINVAL;
goto free_bind_ops;
}
@@ -3423,19 +3447,19 @@ static int vm_bind_ioctl_signal_fences(struct xe_vm *vm,
struct xe_sync_entry *syncs,
int num_syncs)
{
- struct dma_fence *fence;
+ struct dma_fence *fence = NULL;
int i, err = 0;
- fence = xe_sync_in_fence_get(syncs, num_syncs,
- to_wait_exec_queue(vm, q), vm);
- if (IS_ERR(fence))
- return PTR_ERR(fence);
+ if (num_syncs) {
+ fence = xe_sync_in_fence_get(syncs, num_syncs,
+ to_wait_exec_queue(vm, q), vm);
+ if (IS_ERR(fence))
+ return PTR_ERR(fence);
- for (i = 0; i < num_syncs; i++)
- xe_sync_entry_signal(&syncs[i], fence);
+ for (i = 0; i < num_syncs; i++)
+ xe_sync_entry_signal(&syncs[i], fence);
+ }
- xe_exec_queue_last_fence_set(to_wait_exec_queue(vm, q), vm,
- fence);
dma_fence_put(fence);
return err;
@@ -3626,8 +3650,12 @@ int xe_vm_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
syncs_user = u64_to_user_ptr(args->syncs);
for (num_syncs = 0; num_syncs < args->num_syncs; num_syncs++) {
+ struct xe_exec_queue *__q = q ?: vm->q[0];
+
err = xe_sync_entry_parse(xe, xef, &syncs[num_syncs],
&syncs_user[num_syncs],
+ __q->ufence_syncobj,
+ ++__q->ufence_timeline_value,
(xe_vm_in_lr_mode(vm) ?
SYNC_PARSE_FLAG_LR_MODE : 0) |
(!args->num_binds ?
@@ -4165,7 +4193,7 @@ void xe_vm_snapshot_free(struct xe_vm_snapshot *snap)
/**
* xe_vma_need_vram_for_atomic - Check if VMA needs VRAM migration for atomic operations
- * @xe: Pointer to the XE device structure
+ * @xe: Pointer to the Xe device structure
* @vma: Pointer to the virtual memory area (VMA) structure
* @is_atomic: In pagefault path and atomic operation
*
@@ -4212,7 +4240,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
struct xe_vma_ops vops;
struct drm_gpuva_ops *ops = NULL;
struct drm_gpuva_op *__op;
- bool is_cpu_addr_mirror = false;
+ unsigned int vma_flags = 0;
bool remap_op = false;
struct xe_vma_mem_attr tmp_attr;
u16 default_pat;
@@ -4242,15 +4270,17 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
vma = gpuva_to_vma(op->base.unmap.va);
XE_WARN_ON(!xe_vma_has_default_mem_attrs(vma));
default_pat = vma->attr.default_pat_index;
+ vma_flags = vma->gpuva.flags;
}
if (__op->op == DRM_GPUVA_OP_REMAP) {
vma = gpuva_to_vma(op->base.remap.unmap->va);
default_pat = vma->attr.default_pat_index;
+ vma_flags = vma->gpuva.flags;
}
if (__op->op == DRM_GPUVA_OP_MAP) {
- op->map.is_cpu_addr_mirror = true;
+ op->map.vma_flags |= vma_flags & XE_VMA_CREATE_MASK;
op->map.pat_index = default_pat;
}
} else {
@@ -4259,11 +4289,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
xe_assert(vm->xe, !remap_op);
xe_assert(vm->xe, xe_vma_has_no_bo(vma));
remap_op = true;
-
- if (xe_vma_is_cpu_addr_mirror(vma))
- is_cpu_addr_mirror = true;
- else
- is_cpu_addr_mirror = false;
+ vma_flags = vma->gpuva.flags;
}
if (__op->op == DRM_GPUVA_OP_MAP) {
@@ -4272,10 +4298,10 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
/*
* In case of madvise ops DRM_GPUVA_OP_MAP is
* always after DRM_GPUVA_OP_REMAP, so ensure
- * we assign op->map.is_cpu_addr_mirror true
- * if REMAP is for xe_vma_is_cpu_addr_mirror vma
+ * to propagate the flags from the vma we're
+ * unmapping.
*/
- op->map.is_cpu_addr_mirror = is_cpu_addr_mirror;
+ op->map.vma_flags |= vma_flags & XE_VMA_CREATE_MASK;
}
}
print_op(vm->xe, __op);
@@ -4314,7 +4340,7 @@ static int xe_vm_alloc_vma(struct xe_vm *vm,
xe_vma_destroy(gpuva_to_vma(op->base.remap.unmap->va), NULL);
} else if (__op->op == DRM_GPUVA_OP_MAP) {
vma = op->map.vma;
- /* In case of madvise call, MAP will always be follwed by REMAP.
+ /* In case of madvise call, MAP will always be followed by REMAP.
* Therefore temp_attr will always have sane values, making it safe to
* copy them to new vma.
*/
diff --git a/drivers/gpu/drm/xe/xe_vm_doc.h b/drivers/gpu/drm/xe/xe_vm_doc.h
index 1030ce214032..02e5288373c9 100644
--- a/drivers/gpu/drm/xe/xe_vm_doc.h
+++ b/drivers/gpu/drm/xe/xe_vm_doc.h
@@ -7,7 +7,7 @@
#define _XE_VM_DOC_H_
/**
- * DOC: XE VM (user address space)
+ * DOC: Xe VM (user address space)
*
* VM creation
* ===========
@@ -202,13 +202,13 @@
* User pointers are user allocated memory (malloc'd, mmap'd, etc..) for which the
* user wants to create a GPU mapping. Typically in other DRM drivers a dummy BO
* was created and then a binding was created. We bypass creating a dummy BO in
- * XE and simply create a binding directly from the userptr.
+ * Xe and simply create a binding directly from the userptr.
*
* Invalidation
* ------------
*
* Since this a core kernel managed memory the kernel can move this memory
- * whenever it wants. We register an invalidation MMU notifier to alert XE when
+ * whenever it wants. We register an invalidation MMU notifier to alert Xe when
* a user pointer is about to move. The invalidation notifier needs to block
* until all pending users (jobs or compute mode engines) of the userptr are
* idle to ensure no faults. This done by waiting on all of VM's dma-resv slots.
@@ -419,7 +419,7 @@
* =======
*
* VM locking protects all of the core data paths (bind operations, execs,
- * evictions, and compute mode rebind worker) in XE.
+ * evictions, and compute mode rebind worker) in Xe.
*
* Locks
* -----
diff --git a/drivers/gpu/drm/xe/xe_vm_types.h b/drivers/gpu/drm/xe/xe_vm_types.h
index 413353e1c225..ccd6cc090309 100644
--- a/drivers/gpu/drm/xe/xe_vm_types.h
+++ b/drivers/gpu/drm/xe/xe_vm_types.h
@@ -46,12 +46,13 @@ struct xe_vm_pgtable_update_op;
#define XE_VMA_PTE_COMPACT (DRM_GPUVA_USERBITS << 7)
#define XE_VMA_DUMPABLE (DRM_GPUVA_USERBITS << 8)
#define XE_VMA_SYSTEM_ALLOCATOR (DRM_GPUVA_USERBITS << 9)
+#define XE_VMA_MADV_AUTORESET (DRM_GPUVA_USERBITS << 10)
/**
* struct xe_vma_mem_attr - memory attributes associated with vma
*/
struct xe_vma_mem_attr {
- /** @preferred_loc: perferred memory_location */
+ /** @preferred_loc: preferred memory_location */
struct {
/** @preferred_loc.migration_policy: Pages migration policy */
u32 migration_policy;
@@ -220,11 +221,6 @@ struct xe_vm {
#define XE_VM_FLAG_GSC BIT(8)
unsigned long flags;
- /** @composite_fence_ctx: context composite fence */
- u64 composite_fence_ctx;
- /** @composite_fence_seqno: seqno for composite fence */
- u32 composite_fence_seqno;
-
/**
* @lock: outer most lock, protects objects of anything attached to this
* VM
@@ -337,7 +333,7 @@ struct xe_vm {
u64 tlb_flush_seqno;
/** @batch_invalidate_tlb: Always invalidate TLB before batch start */
bool batch_invalidate_tlb;
- /** @xef: XE file handle for tracking this VM's drm client */
+ /** @xef: Xe file handle for tracking this VM's drm client */
struct xe_file *xef;
};
@@ -345,17 +341,10 @@ struct xe_vm {
struct xe_vma_op_map {
/** @vma: VMA to map */
struct xe_vma *vma;
+ unsigned int vma_flags;
/** @immediate: Immediate bind */
bool immediate;
/** @read_only: Read only */
- bool read_only;
- /** @is_null: is NULL binding */
- bool is_null;
- /** @is_cpu_addr_mirror: is CPU address mirror binding */
- bool is_cpu_addr_mirror;
- /** @dumpable: whether BO is dumped on GPU hang */
- bool dumpable;
- /** @invalidate: invalidate the VMA before bind */
bool invalidate_on_bind;
/** @pat_index: The pat index to use for this operation. */
u16 pat_index;
@@ -477,6 +466,7 @@ struct xe_vma_ops {
#define XE_VMA_OPS_FLAG_HAS_SVM_PREFETCH BIT(0)
#define XE_VMA_OPS_FLAG_MADVISE BIT(1)
#define XE_VMA_OPS_ARRAY_OF_BINDS BIT(2)
+#define XE_VMA_OPS_FLAG_SKIP_TLB_WAIT BIT(3)
u32 flags;
#ifdef TEST_VM_OPS_ERROR
/** @inject_error: inject error to test error handling */
diff --git a/drivers/gpu/drm/xe/xe_vram.c b/drivers/gpu/drm/xe/xe_vram.c
index 652df7a5f4f6..d50baefcd124 100644
--- a/drivers/gpu/drm/xe/xe_vram.c
+++ b/drivers/gpu/drm/xe/xe_vram.c
@@ -13,50 +13,25 @@
#include "regs/xe_gt_regs.h"
#include "regs/xe_regs.h"
#include "xe_assert.h"
+#include "xe_bo.h"
#include "xe_device.h"
#include "xe_force_wake.h"
#include "xe_gt_mcr.h"
-#include "xe_gt_sriov_vf.h"
#include "xe_mmio.h"
#include "xe_module.h"
#include "xe_sriov.h"
+#include "xe_tile_sriov_vf.h"
#include "xe_ttm_vram_mgr.h"
#include "xe_vram.h"
#include "xe_vram_types.h"
-#define BAR_SIZE_SHIFT 20
-
-/*
- * Release all the BARs that could influence/block LMEMBAR resizing, i.e.
- * assigned IORESOURCE_MEM_64 BARs
- */
-static void release_bars(struct pci_dev *pdev)
-{
- struct resource *res;
- int i;
-
- pci_dev_for_each_resource(pdev, res, i) {
- /* Resource already un-assigned, do not reset it */
- if (!res->parent)
- continue;
-
- /* No need to release unrelated BARs */
- if (!(res->flags & IORESOURCE_MEM_64))
- continue;
-
- pci_release_resource(pdev, i);
- }
-}
-
static void resize_bar(struct xe_device *xe, int resno, resource_size_t size)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
int bar_size = pci_rebar_bytes_to_size(size);
int ret;
- release_bars(pdev);
-
- ret = pci_resize_resource(pdev, resno, bar_size);
+ ret = pci_resize_resource(pdev, resno, bar_size, 0);
if (ret) {
drm_info(&xe->drm, "Failed to resize BAR%d to %dM (%pe). Consider enabling 'Resizable BAR' support in your BIOS\n",
resno, 1 << bar_size, ERR_PTR(ret));
@@ -78,41 +53,37 @@ void xe_vram_resize_bar(struct xe_device *xe)
resource_size_t current_size;
resource_size_t rebar_size;
struct resource *root_res;
- u32 bar_size_mask;
+ int max_size, i;
u32 pci_cmd;
- int i;
/* gather some relevant info */
current_size = pci_resource_len(pdev, LMEM_BAR);
- bar_size_mask = pci_rebar_get_possible_sizes(pdev, LMEM_BAR);
-
- if (!bar_size_mask)
- return;
if (force_vram_bar_size < 0)
return;
/* set to a specific size? */
if (force_vram_bar_size) {
- u32 bar_size_bit;
-
- rebar_size = force_vram_bar_size * (resource_size_t)SZ_1M;
+ rebar_size = pci_rebar_bytes_to_size(force_vram_bar_size *
+ (resource_size_t)SZ_1M);
- bar_size_bit = bar_size_mask & BIT(pci_rebar_bytes_to_size(rebar_size));
-
- if (!bar_size_bit) {
+ if (!pci_rebar_size_supported(pdev, LMEM_BAR, rebar_size)) {
drm_info(&xe->drm,
- "Requested size: %lluMiB is not supported by rebar sizes: 0x%x. Leaving default: %lluMiB\n",
- (u64)rebar_size >> 20, bar_size_mask, (u64)current_size >> 20);
+ "Requested size: %lluMiB is not supported by rebar sizes: 0x%llx. Leaving default: %lluMiB\n",
+ (u64)pci_rebar_size_to_bytes(rebar_size) >> 20,
+ pci_rebar_get_possible_sizes(pdev, LMEM_BAR),
+ (u64)current_size >> 20);
return;
}
- rebar_size = 1ULL << (__fls(bar_size_bit) + BAR_SIZE_SHIFT);
-
+ rebar_size = pci_rebar_size_to_bytes(rebar_size);
if (rebar_size == current_size)
return;
} else {
- rebar_size = 1ULL << (__fls(bar_size_mask) + BAR_SIZE_SHIFT);
+ max_size = pci_rebar_get_max_size(pdev, LMEM_BAR);
+ if (max_size < 0)
+ return;
+ rebar_size = pci_rebar_size_to_bytes(max_size);
/* only resize if larger than current */
if (rebar_size <= current_size)
@@ -182,12 +153,17 @@ static int determine_lmem_bar_size(struct xe_device *xe, struct xe_vram_region *
return 0;
}
-static inline u64 get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size)
+static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset)
{
struct xe_device *xe = gt_to_xe(gt);
+ unsigned int fw_ref;
u64 offset;
u32 reg;
+ fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
+ if (!fw_ref)
+ return -ETIMEDOUT;
+
if (GRAPHICS_VER(xe) >= 20) {
u64 ccs_size = tile_size / 512;
u64 offset_hi, offset_lo;
@@ -217,7 +193,10 @@ static inline u64 get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size)
offset = (u64)REG_FIELD_GET(XEHP_FLAT_CCS_PTR, reg) * SZ_64K;
}
- return offset;
+ xe_force_wake_put(gt_to_fw(gt), fw_ref);
+ *poffset = offset;
+
+ return 0;
}
/*
@@ -244,7 +223,6 @@ static int tile_vram_size(struct xe_tile *tile, u64 *vram_size,
{
struct xe_device *xe = tile_to_xe(tile);
struct xe_gt *gt = tile->primary_gt;
- unsigned int fw_ref;
u64 offset;
u32 reg;
@@ -255,32 +233,31 @@ static int tile_vram_size(struct xe_tile *tile, u64 *vram_size,
offset = 0;
for_each_tile(t, xe, id)
for_each_if(t->id < tile->id)
- offset += xe_gt_sriov_vf_lmem(t->primary_gt);
+ offset += xe_tile_sriov_vf_lmem(t);
- *tile_size = xe_gt_sriov_vf_lmem(gt);
+ *tile_size = xe_tile_sriov_vf_lmem(tile);
*vram_size = *tile_size;
*tile_offset = offset;
return 0;
}
- fw_ref = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT);
- if (!fw_ref)
- return -ETIMEDOUT;
-
/* actual size */
if (unlikely(xe->info.platform == XE_DG1)) {
*tile_size = pci_resource_len(to_pci_dev(xe->drm.dev), LMEM_BAR);
*tile_offset = 0;
} else {
- reg = xe_gt_mcr_unicast_read_any(gt, XEHP_TILE_ADDR_RANGE(gt->info.id));
+ reg = xe_mmio_read32(&tile->mmio, SG_TILE_ADDR_RANGE(tile->id));
*tile_size = (u64)REG_FIELD_GET(GENMASK(14, 8), reg) * SZ_1G;
*tile_offset = (u64)REG_FIELD_GET(GENMASK(7, 1), reg) * SZ_1G;
}
/* minus device usage */
if (xe->info.has_flat_ccs) {
- offset = get_flat_ccs_offset(gt, *tile_size);
+ int ret = get_flat_ccs_offset(gt, *tile_size, &offset);
+
+ if (ret)
+ return ret;
} else {
offset = xe_mmio_read64_2x32(&tile->mmio, GSMBASE);
}
@@ -288,8 +265,6 @@ static int tile_vram_size(struct xe_tile *tile, u64 *vram_size,
/* remove the tile offset so we have just the available size */
*vram_size = offset - *tile_offset;
- xe_force_wake_put(gt_to_fw(gt), fw_ref);
-
return 0;
}
@@ -301,8 +276,11 @@ static void vram_fini(void *arg)
xe->mem.vram->mapping = NULL;
- for_each_tile(tile, xe, id)
+ for_each_tile(tile, xe, id) {
tile->mem.vram->mapping = NULL;
+ if (tile->mem.kernel_vram)
+ tile->mem.kernel_vram->mapping = NULL;
+ }
}
struct xe_vram_region *xe_vram_region_alloc(struct xe_device *xe, u8 id, u32 placement)
diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c
index cd03891654a1..3764abca3d4f 100644
--- a/drivers/gpu/drm/xe/xe_wa.c
+++ b/drivers/gpu/drm/xe/xe_wa.c
@@ -679,12 +679,14 @@ static const struct xe_rtp_entry_sr engine_was[] = {
},
{ XE_RTP_NAME("14023061436"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001),
+ FUNC(xe_rtp_match_first_render_or_compute), OR,
+ GRAPHICS_VERSION_RANGE(3003, 3005),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(TDL_CHICKEN, QID_WAIT_FOR_THREAD_NOT_RUN_DISABLE))
},
{ XE_RTP_NAME("13012615864"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001), OR,
- GRAPHICS_VERSION(3003),
+ GRAPHICS_VERSION_RANGE(3003, 3005),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, RES_CHK_SPR_DIS))
},
@@ -695,7 +697,7 @@ static const struct xe_rtp_entry_sr engine_was[] = {
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
},
{ XE_RTP_NAME("14021402888"),
- XE_RTP_RULES(GRAPHICS_VERSION(3003), FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3003, 3005), FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
},
{ XE_RTP_NAME("18041344222"),
@@ -913,9 +915,18 @@ static const struct xe_rtp_entry_sr lrc_was[] = {
DIS_AUTOSTRIP))
},
{ XE_RTP_NAME("22021007897"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3003), ENGINE_CLASS(RENDER)),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE))
},
+ { XE_RTP_NAME("14024681466"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(XEHP_SLICE_COMMON_ECO_CHICKEN1, FAST_CLEAR_VALIGN_FIX))
+ },
+ { XE_RTP_NAME("15016589081"),
+ XE_RTP_RULES(GRAPHICS_VERSION(3000), GRAPHICS_STEP(A0, B0),
+ ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(CHICKEN_RASTER_1, DIS_CLIP_NEGATIVE_BOUNDING_BOX))
+ },
};
static __maybe_unused const struct xe_rtp_entry oob_was[] = {
@@ -1086,7 +1097,14 @@ void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p)
drm_printf_indent(p, 1, "%s\n", device_oob_was[idx].name);
}
-void xe_wa_dump(struct xe_gt *gt, struct drm_printer *p)
+/**
+ * xe_wa_gt_dump() - Dump GT workarounds into a drm printer.
+ * @gt: the &xe_gt
+ * @p: the &drm_printer
+ *
+ * Return: always 0.
+ */
+int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p)
{
size_t idx;
@@ -1094,18 +1112,22 @@ void xe_wa_dump(struct xe_gt *gt, struct drm_printer *p)
for_each_set_bit(idx, gt->wa_active.gt, ARRAY_SIZE(gt_was))
drm_printf_indent(p, 1, "%s\n", gt_was[idx].name);
- drm_printf(p, "\nEngine Workarounds\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "Engine Workarounds\n");
for_each_set_bit(idx, gt->wa_active.engine, ARRAY_SIZE(engine_was))
drm_printf_indent(p, 1, "%s\n", engine_was[idx].name);
- drm_printf(p, "\nLRC Workarounds\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "LRC Workarounds\n");
for_each_set_bit(idx, gt->wa_active.lrc, ARRAY_SIZE(lrc_was))
drm_printf_indent(p, 1, "%s\n", lrc_was[idx].name);
- drm_printf(p, "\nOOB Workarounds\n");
+ drm_puts(p, "\n");
+ drm_printf(p, "OOB Workarounds\n");
for_each_set_bit(idx, gt->wa_active.oob, ARRAY_SIZE(oob_was))
if (oob_was[idx].name)
drm_printf_indent(p, 1, "%s\n", oob_was[idx].name);
+ return 0;
}
/*
@@ -1127,6 +1149,6 @@ void xe_wa_apply_tile_workarounds(struct xe_tile *tile)
if (IS_SRIOV_VF(tile->xe))
return;
- if (XE_GT_WA(tile->primary_gt, 22010954014))
+ if (XE_DEVICE_WA(tile->xe, 22010954014))
xe_mmio_rmw32(mmio, XEHP_CLOCK_GATE_DIS, 0, SGSI_SIDECLK_DIS);
}
diff --git a/drivers/gpu/drm/xe/xe_wa.h b/drivers/gpu/drm/xe/xe_wa.h
index 6a869b2de643..8fd6a5af0910 100644
--- a/drivers/gpu/drm/xe/xe_wa.h
+++ b/drivers/gpu/drm/xe/xe_wa.h
@@ -22,7 +22,7 @@ void xe_wa_process_engine(struct xe_hw_engine *hwe);
void xe_wa_process_lrc(struct xe_hw_engine *hwe);
void xe_wa_apply_tile_workarounds(struct xe_tile *tile);
void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p);
-void xe_wa_dump(struct xe_gt *gt, struct drm_printer *p);
+int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p);
/**
* XE_GT_WA - Out-of-band GT workarounds, to be queried and called as needed.
diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules
index f3a6d5d239ce..fb38eb3d6e9a 100644
--- a/drivers/gpu/drm/xe/xe_wa_oob.rules
+++ b/drivers/gpu/drm/xe/xe_wa_oob.rules
@@ -11,10 +11,9 @@
18020744125 PLATFORM(PVC)
1509372804 PLATFORM(PVC), GRAPHICS_STEP(A0, C0)
1409600907 GRAPHICS_VERSION_RANGE(1200, 1250)
-14016763929 SUBPLATFORM(DG2, G10)
+22014953428 SUBPLATFORM(DG2, G10)
SUBPLATFORM(DG2, G12)
16017236439 PLATFORM(PVC)
-22010954014 PLATFORM(DG2)
14019821291 MEDIA_VERSION_RANGE(1300, 2000)
14015076503 MEDIA_VERSION(1300)
16020292621 GRAPHICS_VERSION(2004), GRAPHICS_STEP(A0, B0)
@@ -34,18 +33,18 @@
13011645652 GRAPHICS_VERSION(2004)
GRAPHICS_VERSION_RANGE(3000, 3001)
GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3004, 3005)
14022293748 GRAPHICS_VERSION_RANGE(2001, 2002)
GRAPHICS_VERSION(2004)
- GRAPHICS_VERSION_RANGE(3000, 3001)
- GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3000, 3005)
22019794406 GRAPHICS_VERSION_RANGE(2001, 2002)
GRAPHICS_VERSION(2004)
GRAPHICS_VERSION_RANGE(3000, 3001)
GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3004, 3005)
22019338487 MEDIA_VERSION(2000)
GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
MEDIA_VERSION(3000), MEDIA_STEP(A0, B0), FUNC(xe_rtp_match_not_sriov_vf)
-22019338487_display PLATFORM(LUNARLAKE)
16023588340 GRAPHICS_VERSION(2001), FUNC(xe_rtp_match_not_sriov_vf)
14019789679 GRAPHICS_VERSION(1255)
GRAPHICS_VERSION_RANGE(1270, 2004)
@@ -63,11 +62,11 @@
16023105232 GRAPHICS_VERSION_RANGE(2001, 3001)
MEDIA_VERSION_RANGE(1301, 3000)
MEDIA_VERSION(3002)
- GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3003, 3005)
16026508708 GRAPHICS_VERSION_RANGE(1200, 3001)
MEDIA_VERSION_RANGE(1300, 3000)
MEDIA_VERSION(3002)
- GRAPHICS_VERSION(3003)
+ GRAPHICS_VERSION_RANGE(3003, 3005)
14020001231 GRAPHICS_VERSION_RANGE(2001,2004), FUNC(xe_rtp_match_psmi_enabled)
MEDIA_VERSION(2000), FUNC(xe_rtp_match_psmi_enabled)
MEDIA_VERSION(3000), FUNC(xe_rtp_match_psmi_enabled)
@@ -75,9 +74,5 @@
16023683509 MEDIA_VERSION(2000), FUNC(xe_rtp_match_psmi_enabled)
MEDIA_VERSION(3000), MEDIA_STEP(A0, B0), FUNC(xe_rtp_match_psmi_enabled)
-# SoC workaround - currently applies to all platforms with the following
-# primary GT GMDID
-14022085890 GRAPHICS_VERSION(2001)
-
15015404425_disable PLATFORM(PANTHERLAKE), MEDIA_STEP(B0, FOREVER)
16026007364 MEDIA_VERSION(3000)
diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c
index 1bda7ef606cc..4fa45dbe1dcb 100644
--- a/drivers/gpu/drm/xen/xen_drm_front.c
+++ b/drivers/gpu/drm/xen/xen_drm_front.c
@@ -18,6 +18,7 @@
#include <drm/drm_probe_helper.h>
#include <drm/drm_file.h>
#include <drm/drm_gem.h>
+#include <drm/drm_print.h>
#include <xen/platform_pci.h>
#include <xen/xen.h>
diff --git a/drivers/gpu/drm/xen/xen_drm_front_gem.c b/drivers/gpu/drm/xen/xen_drm_front_gem.c
index 63112ed975c4..386ae7441093 100644
--- a/drivers/gpu/drm/xen/xen_drm_front_gem.c
+++ b/drivers/gpu/drm/xen/xen_drm_front_gem.c
@@ -15,6 +15,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_prime.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <xen/balloon.h>
diff --git a/drivers/gpu/drm/xen/xen_drm_front_kms.c b/drivers/gpu/drm/xen/xen_drm_front_kms.c
index 806ec66ee7f7..48772b5fe71c 100644
--- a/drivers/gpu/drm/xen/xen_drm_front_kms.c
+++ b/drivers/gpu/drm/xen/xen_drm_front_kms.c
@@ -16,6 +16,7 @@
#include <drm/drm_gem.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 2bee0a2275ed..02f3a7d78cf8 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -19,6 +19,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
+#include <drm/drm_dumb_buffers.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fbdev_dma.h>
#include <drm/drm_fourcc.h>
@@ -363,10 +364,12 @@ static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv,
struct drm_mode_create_dumb *args)
{
struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
- unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+ int ret;
/* Enforce the alignment constraints of the DMA engine. */
- args->pitch = ALIGN(pitch, dpsub->dma_align);
+ ret = drm_mode_size_dumb(drm, args, dpsub->dma_align, 0);
+ if (ret)
+ return ret;
return drm_gem_dma_dumb_create_internal(file_priv, drm, args);
}
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
index 344cc9e741c1..723a80895cd4 100644
--- a/drivers/gpu/host1x/bus.c
+++ b/drivers/gpu/host1x/bus.c
@@ -471,6 +471,18 @@ static int host1x_device_add(struct host1x *host1x,
mutex_unlock(&clients_lock);
+ /*
+ * Add device even if there are no subdevs to ensure syncpoint functionality
+ * is available regardless of whether any engine subdevices are present
+ */
+ if (list_empty(&device->subdevs)) {
+ err = device_add(&device->dev);
+ if (err < 0)
+ dev_err(&device->dev, "failed to add device: %d\n", err);
+ else
+ device->registered = true;
+ }
+
return 0;
}
diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c
index 1f93e5e276c0..3f475f0e6545 100644
--- a/drivers/gpu/host1x/dev.c
+++ b/drivers/gpu/host1x/dev.c
@@ -71,6 +71,15 @@ u32 host1x_sync_readl(struct host1x *host1x, u32 r)
return readl(sync_regs + r);
}
+#ifdef CONFIG_64BIT
+u64 host1x_sync_readq(struct host1x *host1x, u32 r)
+{
+ void __iomem *sync_regs = host1x->regs + host1x->info->sync_offset;
+
+ return readq(sync_regs + r);
+}
+#endif
+
void host1x_ch_writel(struct host1x_channel *ch, u32 v, u32 r)
{
writel(v, ch->regs + r);
@@ -585,14 +594,8 @@ static int host1x_probe(struct platform_device *pdev)
}
host->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(host->clk)) {
- err = PTR_ERR(host->clk);
-
- if (err != -EPROBE_DEFER)
- dev_err(&pdev->dev, "failed to get clock: %d\n", err);
-
- return err;
- }
+ if (IS_ERR(host->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), "failed to get clock\n");
err = host1x_get_resets(host);
if (err)
@@ -821,6 +824,7 @@ u64 host1x_get_dma_mask(struct host1x *host1x)
}
EXPORT_SYMBOL(host1x_get_dma_mask);
+MODULE_SOFTDEP("post: tegra-drm");
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
MODULE_AUTHOR("Terje Bergstrom <tbergstrom@nvidia.com>");
MODULE_DESCRIPTION("Host1x driver for Tegra products");
diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h
index d3855a1c6b47..ef44618ed88a 100644
--- a/drivers/gpu/host1x/dev.h
+++ b/drivers/gpu/host1x/dev.h
@@ -179,6 +179,9 @@ void host1x_hypervisor_writel(struct host1x *host1x, u32 v, u32 r);
u32 host1x_hypervisor_readl(struct host1x *host1x, u32 r);
void host1x_sync_writel(struct host1x *host1x, u32 v, u32 r);
u32 host1x_sync_readl(struct host1x *host1x, u32 r);
+#ifdef CONFIG_64BIT
+u64 host1x_sync_readq(struct host1x *host1x, u32 r);
+#endif
void host1x_ch_writel(struct host1x_channel *ch, u32 v, u32 r);
u32 host1x_ch_readl(struct host1x_channel *ch, u32 r);
diff --git a/drivers/gpu/host1x/hw/channel_hw.c b/drivers/gpu/host1x/hw/channel_hw.c
index d44b8de890be..2df6a16d484e 100644
--- a/drivers/gpu/host1x/hw/channel_hw.c
+++ b/drivers/gpu/host1x/hw/channel_hw.c
@@ -47,24 +47,11 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
}
}
-static void submit_wait(struct host1x_job *job, u32 id, u32 threshold,
- u32 next_class)
+static void submit_wait(struct host1x_job *job, u32 id, u32 threshold)
{
struct host1x_cdma *cdma = &job->channel->cdma;
-#if HOST1X_HW >= 6
- u32 stream_id;
-
- /*
- * If a memory context has been set, use it. Otherwise
- * (if context isolation is disabled) use the engine's
- * firmware stream ID.
- */
- if (job->memory_context)
- stream_id = job->memory_context->stream_id;
- else
- stream_id = job->engine_fallback_streamid;
-
+#if HOST1X_HW >= 2
host1x_cdma_push_wide(cdma,
host1x_opcode_setclass(
HOST1X_CLASS_HOST1X,
@@ -76,23 +63,6 @@ static void submit_wait(struct host1x_job *job, u32 id, u32 threshold,
id,
HOST1X_OPCODE_NOP
);
- host1x_cdma_push_wide(&job->channel->cdma,
- host1x_opcode_setclass(job->class, 0, 0),
- host1x_opcode_setpayload(stream_id),
- host1x_opcode_setstreamid(job->engine_streamid_offset / 4),
- HOST1X_OPCODE_NOP);
-#elif HOST1X_HW >= 2
- host1x_cdma_push_wide(cdma,
- host1x_opcode_setclass(
- HOST1X_CLASS_HOST1X,
- HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32,
- /* WAIT_SYNCPT_32 is at SYNCPT_PAYLOAD_32+2 */
- BIT(0) | BIT(2)
- ),
- threshold,
- id,
- host1x_opcode_setclass(next_class, 0, 0)
- );
#else
/* TODO add waitchk or use waitbases or other mitigation */
host1x_cdma_push(cdma,
@@ -103,6 +73,32 @@ static void submit_wait(struct host1x_job *job, u32 id, u32 threshold,
),
host1x_class_host_wait_syncpt(id, threshold)
);
+#endif
+}
+
+static void submit_setclass(struct host1x_job *job, u32 next_class)
+{
+ struct host1x_cdma *cdma = &job->channel->cdma;
+
+#if HOST1X_HW >= 6
+ u32 stream_id;
+
+ /*
+ * If a memory context has been set, use it. Otherwise
+ * (if context isolation is disabled) use the engine's
+ * firmware stream ID.
+ */
+ if (job->memory_context)
+ stream_id = job->memory_context->stream_id;
+ else
+ stream_id = job->engine_fallback_streamid;
+
+ host1x_cdma_push_wide(cdma,
+ host1x_opcode_setclass(next_class, 0, 0),
+ host1x_opcode_setpayload(stream_id),
+ host1x_opcode_setstreamid(job->engine_streamid_offset / 4),
+ HOST1X_OPCODE_NOP);
+#else
host1x_cdma_push(cdma,
host1x_opcode_setclass(next_class, 0, 0),
HOST1X_OPCODE_NOP
@@ -110,7 +106,8 @@ static void submit_wait(struct host1x_job *job, u32 id, u32 threshold,
#endif
}
-static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base)
+static void submit_gathers(struct host1x_job *job, struct host1x_job_cmd *cmds, u32 num_cmds,
+ u32 job_syncpt_base)
{
struct host1x_cdma *cdma = &job->channel->cdma;
#if HOST1X_HW < 6
@@ -119,8 +116,8 @@ static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base)
unsigned int i;
u32 threshold;
- for (i = 0; i < job->num_cmds; i++) {
- struct host1x_job_cmd *cmd = &job->cmds[i];
+ for (i = 0; i < num_cmds; i++) {
+ struct host1x_job_cmd *cmd = &cmds[i];
if (cmd->is_wait) {
if (cmd->wait.relative)
@@ -128,7 +125,8 @@ static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base)
else
threshold = cmd->wait.threshold;
- submit_wait(job, cmd->wait.id, threshold, cmd->wait.next_class);
+ submit_wait(job, cmd->wait.id, threshold);
+ submit_setclass(job, cmd->wait.next_class);
} else {
struct host1x_job_gather *g = &cmd->gather;
@@ -216,7 +214,34 @@ static void channel_program_cdma(struct host1x_job *job)
#if HOST1X_HW >= 6
u32 fence;
+ int i = 0;
+
+ if (job->num_cmds == 0)
+ goto prefences_done;
+ if (!job->cmds[0].is_wait || job->cmds[0].wait.relative)
+ goto prefences_done;
+
+ /* Enter host1x class with invalid stream ID for prefence waits. */
+ host1x_cdma_push_wide(cdma,
+ host1x_opcode_acquire_mlock(1),
+ host1x_opcode_setclass(1, 0, 0),
+ host1x_opcode_setpayload(0),
+ host1x_opcode_setstreamid(0x1fffff));
+
+ for (i = 0; i < job->num_cmds; i++) {
+ struct host1x_job_cmd *cmd = &job->cmds[i];
+
+ if (!cmd->is_wait || cmd->wait.relative)
+ break;
+
+ submit_wait(job, cmd->wait.id, cmd->wait.threshold);
+ }
+
+ host1x_cdma_push(cdma,
+ HOST1X_OPCODE_NOP,
+ host1x_opcode_release_mlock(1));
+prefences_done:
/* Enter engine class with invalid stream ID. */
host1x_cdma_push_wide(cdma,
host1x_opcode_acquire_mlock(job->class),
@@ -230,11 +255,12 @@ static void channel_program_cdma(struct host1x_job *job)
host1x_opcode_nonincr(HOST1X_UCLASS_INCR_SYNCPT, 1),
HOST1X_UCLASS_INCR_SYNCPT_INDX_F(job->syncpt->id) |
HOST1X_UCLASS_INCR_SYNCPT_COND_F(4));
- submit_wait(job, job->syncpt->id, fence, job->class);
+ submit_wait(job, job->syncpt->id, fence);
+ submit_setclass(job, job->class);
/* Submit work. */
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
- submit_gathers(job, job->syncpt_end - job->syncpt_incrs);
+ submit_gathers(job, job->cmds + i, job->num_cmds - i, job->syncpt_end - job->syncpt_incrs);
/* Before releasing MLOCK, ensure engine is idle again. */
fence = host1x_syncpt_incr_max(sp, 1);
@@ -242,7 +268,7 @@ static void channel_program_cdma(struct host1x_job *job)
host1x_opcode_nonincr(HOST1X_UCLASS_INCR_SYNCPT, 1),
HOST1X_UCLASS_INCR_SYNCPT_INDX_F(job->syncpt->id) |
HOST1X_UCLASS_INCR_SYNCPT_COND_F(4));
- submit_wait(job, job->syncpt->id, fence, job->class);
+ submit_wait(job, job->syncpt->id, fence);
/* Release MLOCK. */
host1x_cdma_push(cdma,
@@ -272,7 +298,7 @@ static void channel_program_cdma(struct host1x_job *job)
job->syncpt_end = host1x_syncpt_incr_max(sp, job->syncpt_incrs);
- submit_gathers(job, job->syncpt_end - job->syncpt_incrs);
+ submit_gathers(job, job->cmds, job->num_cmds, job->syncpt_end - job->syncpt_incrs);
#endif
}
diff --git a/drivers/gpu/host1x/hw/intr_hw.c b/drivers/gpu/host1x/hw/intr_hw.c
index 415f8d7e4202..bd5b5ef62f35 100644
--- a/drivers/gpu/host1x/hw/intr_hw.c
+++ b/drivers/gpu/host1x/hw/intr_hw.c
@@ -11,26 +11,64 @@
#include "../intr.h"
#include "../dev.h"
+static void process_32_syncpts(struct host1x *host, unsigned long val, u32 reg_offset)
+{
+ unsigned int id;
+
+ if (!val)
+ return;
+
+ host1x_sync_writel(host, val, HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(reg_offset));
+ host1x_sync_writel(host, val, HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(reg_offset));
+
+ for_each_set_bit(id, &val, 32)
+ host1x_intr_handle_interrupt(host, reg_offset * 32 + id);
+}
+
static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
{
struct host1x_intr_irq_data *irq_data = dev_id;
struct host1x *host = irq_data->host;
unsigned long reg;
- unsigned int i, id;
+ unsigned int i;
+#if !defined(CONFIG_64BIT)
for (i = irq_data->offset; i < DIV_ROUND_UP(host->info->nb_pts, 32);
i += host->num_syncpt_irqs) {
reg = host1x_sync_readl(host,
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
- host1x_sync_writel(host, reg,
- HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
- host1x_sync_writel(host, reg,
+ process_32_syncpts(host, reg, i);
+ }
+#elif HOST1X_HW == 6 || HOST1X_HW == 7
+ /*
+ * Tegra186 and Tegra194 have the first INT_STATUS register not 64-bit aligned,
+ * and only have one interrupt line.
+ */
+ reg = host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(0));
+ process_32_syncpts(host, reg, 0);
+
+ for (i = 1; i < (host->info->nb_pts / 32) - 1; i += 2) {
+ reg = host1x_sync_readq(host,
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
- for_each_set_bit(id, &reg, 32)
- host1x_intr_handle_interrupt(host, i * 32 + id);
+ process_32_syncpts(host, lower_32_bits(reg), i);
+ process_32_syncpts(host, upper_32_bits(reg), i + 1);
+ }
+
+ reg = host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
+ process_32_syncpts(host, reg, i);
+#else
+ /* All 64-bit capable SoCs have number of syncpoints divisible by 64 */
+ for (i = irq_data->offset; i < DIV_ROUND_UP(host->info->nb_pts, 64);
+ i += host->num_syncpt_irqs) {
+ reg = host1x_sync_readq(host,
+ HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i * 2));
+
+ process_32_syncpts(host, lower_32_bits(reg), i * 2 + 0);
+ process_32_syncpts(host, upper_32_bits(reg), i * 2 + 1);
}
+#endif
return IRQ_HANDLED;
}
@@ -68,12 +106,12 @@ host1x_intr_init_host_sync(struct host1x *host, u32 cpm)
/*
* Program threshold interrupt destination among 8 lines per VM,
- * per syncpoint. For each group of 32 syncpoints (corresponding to one
- * interrupt status register), direct to one interrupt line, going
+ * per syncpoint. For each group of 64 syncpoints (corresponding to two
+ * interrupt status registers), direct to one interrupt line, going
* around in a round robin fashion.
*/
for (id = 0; id < host->info->nb_pts; id++) {
- u32 reg_offset = id / 32;
+ u32 reg_offset = id / 64;
u32 irq_index = reg_offset % host->num_syncpt_irqs;
host1x_sync_writel(host, irq_index, HOST1X_SYNC_SYNCPT_INTR_DEST(id));
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index f63d14a57a1d..acc7d82e0585 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -345,8 +345,6 @@ static void syncpt_release(struct kref *ref)
sp->locked = false;
- mutex_lock(&sp->host->syncpt_mutex);
-
host1x_syncpt_base_free(sp->base);
kfree(sp->name);
sp->base = NULL;
@@ -369,7 +367,7 @@ void host1x_syncpt_put(struct host1x_syncpt *sp)
if (!sp)
return;
- kref_put(&sp->ref, syncpt_release);
+ kref_put_mutex(&sp->ref, syncpt_release, &sp->host->syncpt_mutex);
}
EXPORT_SYMBOL(host1x_syncpt_put);
diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitfield.rs
new file mode 100644
index 000000000000..16e143658c51
--- /dev/null
+++ b/drivers/gpu/nova-core/bitfield.rs
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Bitfield library for Rust structures
+//!
+//! Support for defining bitfields in Rust structures. Also used by the [`register!`] macro.
+
+/// Defines a struct with accessors to access bits within an inner unsigned integer.
+///
+/// # Syntax
+///
+/// ```rust
+/// use nova_core::bitfield;
+///
+/// #[derive(Debug, Clone, Copy, Default)]
+/// enum Mode {
+/// #[default]
+/// Low = 0,
+/// High = 1,
+/// Auto = 2,
+/// }
+///
+/// impl TryFrom<u8> for Mode {
+/// type Error = u8;
+/// fn try_from(value: u8) -> Result<Self, Self::Error> {
+/// match value {
+/// 0 => Ok(Mode::Low),
+/// 1 => Ok(Mode::High),
+/// 2 => Ok(Mode::Auto),
+/// _ => Err(value),
+/// }
+/// }
+/// }
+///
+/// impl From<Mode> for u8 {
+/// fn from(mode: Mode) -> u8 {
+/// mode as u8
+/// }
+/// }
+///
+/// #[derive(Debug, Clone, Copy, Default)]
+/// enum State {
+/// #[default]
+/// Inactive = 0,
+/// Active = 1,
+/// }
+///
+/// impl From<bool> for State {
+/// fn from(value: bool) -> Self {
+/// if value { State::Active } else { State::Inactive }
+/// }
+/// }
+///
+/// impl From<State> for bool {
+/// fn from(state: State) -> bool {
+/// match state {
+/// State::Inactive => false,
+/// State::Active => true,
+/// }
+/// }
+/// }
+///
+/// bitfield! {
+/// pub struct ControlReg(u32) {
+/// 7:7 state as bool => State;
+/// 3:0 mode as u8 ?=> Mode;
+/// }
+/// }
+/// ```
+///
+/// This generates a struct with:
+/// - Field accessors: `mode()`, `state()`, etc.
+/// - Field setters: `set_mode()`, `set_state()`, etc. (supports chaining with builder pattern).
+/// Note that the compiler will error out if the size of the setter's arg exceeds the
+/// struct's storage size.
+/// - Debug and Default implementations.
+///
+/// Note: Field accessors and setters inherit the same visibility as the struct itself.
+/// In the example above, both `mode()` and `set_mode()` methods will be `pub`.
+///
+/// Fields are defined as follows:
+///
+/// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or
+/// `bool`. Note that `bool` fields must have a range of 1 bit.
+/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
+/// the result.
+/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
+/// and returns the result. This is useful with fields for which not all values are valid.
+macro_rules! bitfield {
+ // Main entry point - defines the bitfield struct with fields
+ ($vis:vis struct $name:ident($storage:ty) $(, $comment:literal)? { $($fields:tt)* }) => {
+ bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* });
+ };
+
+ // All rules below are helpers.
+
+ // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
+ // `Default`, and conversion to the value type) and field accessor methods.
+ (@core $vis:vis $name:ident $storage:ty $(, $comment:literal)? { $($fields:tt)* }) => {
+ $(
+ #[doc=$comment]
+ )?
+ #[repr(transparent)]
+ #[derive(Clone, Copy)]
+ $vis struct $name($storage);
+
+ impl ::core::convert::From<$name> for $storage {
+ fn from(val: $name) -> $storage {
+ val.0
+ }
+ }
+
+ bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* });
+ };
+
+ // Captures the fields and passes them to all the implementers that require field information.
+ //
+ // Used to simplify the matching rules for implementers, so they don't need to match the entire
+ // complex fields rule even though they only make use of part of it.
+ (@fields_dispatcher $vis:vis $name:ident $storage:ty {
+ $($hi:tt:$lo:tt $field:ident as $type:tt
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ $(, $comment:literal)?
+ ;
+ )*
+ }
+ ) => {
+ bitfield!(@field_accessors $vis $name $storage {
+ $(
+ $hi:$lo $field as $type
+ $(?=> $try_into_type)?
+ $(=> $into_type)?
+ $(, $comment)?
+ ;
+ )*
+ });
+ bitfield!(@debug $name { $($field;)* });
+ bitfield!(@default $name { $($field;)* });
+ };
+
+ // Defines all the field getter/setter methods for `$name`.
+ (
+ @field_accessors $vis:vis $name:ident $storage:ty {
+ $($hi:tt:$lo:tt $field:ident as $type:tt
+ $(?=> $try_into_type:ty)?
+ $(=> $into_type:ty)?
+ $(, $comment:literal)?
+ ;
+ )*
+ }
+ ) => {
+ $(
+ bitfield!(@check_field_bounds $hi:$lo $field as $type);
+ )*
+
+ #[allow(dead_code)]
+ impl $name {
+ $(
+ bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field as $type
+ $(?=> $try_into_type)?
+ $(=> $into_type)?
+ $(, $comment)?
+ ;
+ );
+ )*
+ }
+ };
+
+ // Boolean fields must have `$hi == $lo`.
+ (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
+ #[allow(clippy::eq_op)]
+ const _: () = {
+ ::kernel::build_assert!(
+ $hi == $lo,
+ concat!("boolean field `", stringify!($field), "` covers more than one bit")
+ );
+ };
+ };
+
+ // Non-boolean fields must have `$hi >= $lo`.
+ (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
+ #[allow(clippy::eq_op)]
+ const _: () = {
+ ::kernel::build_assert!(
+ $hi >= $lo,
+ concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
+ );
+ };
+ };
+
+ // Catches fields defined as `bool` and convert them into a boolean value.
+ (
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool
+ => $into_type:ty $(, $comment:literal)?;
+ ) => {
+ bitfield!(
+ @leaf_accessor $vis $name $storage, $hi:$lo $field
+ { |f| <$into_type>::from(f != 0) }
+ bool $into_type => $into_type $(, $comment)?;
+ );
+ };
+
+ // Shortcut for fields defined as `bool` without the `=>` syntax.
+ (
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as bool
+ $(, $comment:literal)?;
+ ) => {
+ bitfield!(
+ @field_accessor $vis $name $storage, $hi:$lo $field as bool => bool $(, $comment)?;
+ );
+ };
+
+ // Catches the `?=>` syntax for non-boolean fields.
+ (
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
+ ?=> $try_into_type:ty $(, $comment:literal)?;
+ ) => {
+ bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
+ { |f| <$try_into_type>::try_from(f as $type) } $type $try_into_type =>
+ ::core::result::Result<
+ $try_into_type,
+ <$try_into_type as ::core::convert::TryFrom<$type>>::Error
+ >
+ $(, $comment)?;);
+ };
+
+ // Catches the `=>` syntax for non-boolean fields.
+ (
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
+ => $into_type:ty $(, $comment:literal)?;
+ ) => {
+ bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field
+ { |f| <$into_type>::from(f as $type) } $type $into_type => $into_type $(, $comment)?;);
+ };
+
+ // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
+ (
+ @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident as $type:tt
+ $(, $comment:literal)?;
+ ) => {
+ bitfield!(
+ @field_accessor $vis $name $storage, $hi:$lo $field as $type => $type $(, $comment)?;
+ );
+ };
+
+ // Generates the accessor methods for a single field.
+ (
+ @leaf_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $field:ident
+ { $process:expr } $prim_type:tt $to_type:ty => $res_type:ty $(, $comment:literal)?;
+ ) => {
+ ::kernel::macros::paste!(
+ const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
+ const [<$field:upper _MASK>]: $storage = {
+ // Generate mask for shifting
+ match ::core::mem::size_of::<$storage>() {
+ 1 => ::kernel::bits::genmask_u8($lo..=$hi) as $storage,
+ 2 => ::kernel::bits::genmask_u16($lo..=$hi) as $storage,
+ 4 => ::kernel::bits::genmask_u32($lo..=$hi) as $storage,
+ 8 => ::kernel::bits::genmask_u64($lo..=$hi) as $storage,
+ _ => ::kernel::build_error!("Unsupported storage type size")
+ }
+ };
+ const [<$field:upper _SHIFT>]: u32 = $lo;
+ );
+
+ $(
+ #[doc="Returns the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn $field(self) -> $res_type {
+ ::kernel::macros::paste!(
+ const MASK: $storage = $name::[<$field:upper _MASK>];
+ const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
+ );
+ let field = ((self.0 & MASK) >> SHIFT);
+
+ $process(field)
+ }
+
+ ::kernel::macros::paste!(
+ $(
+ #[doc="Sets the value of this field:"]
+ #[doc=$comment]
+ )?
+ #[inline(always)]
+ $vis fn [<set_ $field>](mut self, value: $to_type) -> Self {
+ const MASK: $storage = $name::[<$field:upper _MASK>];
+ const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
+ let value = ($storage::from($prim_type::from(value)) << SHIFT) & MASK;
+ self.0 = (self.0 & !MASK) | value;
+
+ self
+ }
+ );
+ };
+
+ // Generates the `Debug` implementation for `$name`.
+ (@debug $name:ident { $($field:ident;)* }) => {
+ impl ::kernel::fmt::Debug for $name {
+ fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
+ f.debug_struct(stringify!($name))
+ .field("<raw>", &::kernel::prelude::fmt!("{:#x}", &self.0))
+ $(
+ .field(stringify!($field), &self.$field())
+ )*
+ .finish()
+ }
+ }
+ };
+
+ // Generates the `Default` implementation for `$name`.
+ (@default $name:ident { $($field:ident;)* }) => {
+ /// Returns a value for the bitfield where all fields are set to their default value.
+ impl ::core::default::Default for $name {
+ fn default() -> Self {
+ #[allow(unused_mut)]
+ let mut value = Self(Default::default());
+
+ ::kernel::macros::paste!(
+ $(
+ value.[<set_ $field>](Default::default());
+ )*
+ );
+
+ value
+ }
+ }
+ };
+}
diff --git a/drivers/gpu/nova-core/dma.rs b/drivers/gpu/nova-core/dma.rs
index 94f44bcfd748..7215398969da 100644
--- a/drivers/gpu/nova-core/dma.rs
+++ b/drivers/gpu/nova-core/dma.rs
@@ -2,12 +2,17 @@
//! Simple DMA object wrapper.
-use core::ops::{Deref, DerefMut};
-
-use kernel::device;
-use kernel::dma::CoherentAllocation;
-use kernel::page::PAGE_SIZE;
-use kernel::prelude::*;
+use core::ops::{
+ Deref,
+ DerefMut, //
+};
+
+use kernel::{
+ device,
+ dma::CoherentAllocation,
+ page::PAGE_SIZE,
+ prelude::*, //
+};
pub(crate) struct DmaObject {
dma: CoherentAllocation<u8>,
@@ -25,20 +30,11 @@ impl DmaObject {
}
pub(crate) fn from_data(dev: &device::Device<device::Bound>, data: &[u8]) -> Result<Self> {
- Self::new(dev, data.len()).map(|mut dma_obj| {
- // TODO[COHA]: replace with `CoherentAllocation::write()` once available.
- // SAFETY:
- // - `dma_obj`'s size is at least `data.len()`.
- // - We have just created this object and there is no other user at this stage.
- unsafe {
- core::ptr::copy_nonoverlapping(
- data.as_ptr(),
- dma_obj.dma.start_ptr_mut(),
- data.len(),
- );
- }
-
- dma_obj
+ Self::new(dev, data.len()).and_then(|mut dma_obj| {
+ // SAFETY: We have just allocated the DMA memory, we are the only users and
+ // we haven't made the device aware of the handle yet.
+ unsafe { dma_obj.write(data, 0)? }
+ Ok(dma_obj)
})
}
}
diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs
index edc72052e27a..d91bbc50cde7 100644
--- a/drivers/gpu/nova-core/driver.rs
+++ b/drivers/gpu/nova-core/driver.rs
@@ -1,13 +1,20 @@
// SPDX-License-Identifier: GPL-2.0
use kernel::{
- auxiliary, c_str,
+ auxiliary,
+ c_str,
device::Core,
+ dma::Device,
+ dma::DmaMask,
pci,
- pci::{Class, ClassMask, Vendor},
+ pci::{
+ Class,
+ ClassMask,
+ Vendor, //
+ },
prelude::*,
sizes::SZ_16M,
- sync::Arc,
+ sync::Arc, //
};
use crate::gpu::Gpu;
@@ -20,6 +27,15 @@ pub(crate) struct NovaCore {
}
const BAR0_SIZE: usize = SZ_16M;
+
+// For now we only support Ampere which can use up to 47-bit DMA addresses.
+//
+// TODO: Add an abstraction for this to support newer GPUs which may support
+// larger DMA addresses. Limiting these GPUs to smaller address widths won't
+// have any adverse affects, unless installed on systems which require larger
+// DMA addresses. These systems should be quite rare.
+const GPU_DMA_BITS: u32 = 47;
+
pub(crate) type Bar0 = pci::Bar<BAR0_SIZE>;
kernel::pci_device_table!(
@@ -57,6 +73,11 @@ impl pci::Driver for NovaCore {
pdev.enable_device_mem()?;
pdev.set_master();
+ // SAFETY: No concurrent DMA allocations or mappings can be made because
+ // the device is still being probed and therefore isn't being used by
+ // other threads of execution.
+ unsafe { pdev.dma_set_mask_and_coherent(DmaMask::new::<GPU_DMA_BITS>())? };
+
let devres_bar = Arc::pin_init(
pdev.iomap_region_sized::<BAR0_SIZE>(0, c_str!("nova-core/bar0")),
GFP_KERNEL,
diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon.rs
index 37e6298195e4..82c661aef594 100644
--- a/drivers/gpu/nova-core/falcon.rs
+++ b/drivers/gpu/nova-core/falcon.rs
@@ -3,30 +3,43 @@
//! Falcon microprocessor base support
use core::ops::Deref;
+
use hal::FalconHal;
-use kernel::device;
-use kernel::dma::DmaAddress;
-use kernel::prelude::*;
-use kernel::sync::aref::ARef;
-use kernel::time::Delta;
-
-use crate::dma::DmaObject;
-use crate::driver::Bar0;
-use crate::gpu::Chipset;
-use crate::regs;
-use crate::regs::macros::RegisterBase;
-use crate::util;
+
+use kernel::{
+ device,
+ dma::DmaAddress,
+ io::poll::read_poll_timeout,
+ prelude::*,
+ sync::aref::ARef,
+ time::{
+ delay::fsleep,
+ Delta, //
+ },
+};
+
+use crate::{
+ dma::DmaObject,
+ driver::Bar0,
+ gpu::Chipset,
+ num::{
+ FromSafeCast,
+ IntoSafeCast, //
+ },
+ regs,
+ regs::macros::RegisterBase, //
+};
pub(crate) mod gsp;
mod hal;
pub(crate) mod sec2;
// TODO[FPRI]: Replace with `ToPrimitive`.
-macro_rules! impl_from_enum_to_u32 {
+macro_rules! impl_from_enum_to_u8 {
($enum_type:ty) => {
- impl From<$enum_type> for u32 {
+ impl From<$enum_type> for u8 {
fn from(value: $enum_type) -> Self {
- value as u32
+ value as u8
}
}
};
@@ -46,7 +59,7 @@ pub(crate) enum FalconCoreRev {
Rev6 = 6,
Rev7 = 7,
}
-impl_from_enum_to_u32!(FalconCoreRev);
+impl_from_enum_to_u8!(FalconCoreRev);
// TODO[FPRI]: replace with `FromPrimitive`.
impl TryFrom<u8> for FalconCoreRev {
@@ -81,7 +94,7 @@ pub(crate) enum FalconCoreRevSubversion {
Subversion2 = 2,
Subversion3 = 3,
}
-impl_from_enum_to_u32!(FalconCoreRevSubversion);
+impl_from_enum_to_u8!(FalconCoreRevSubversion);
// TODO[FPRI]: replace with `FromPrimitive`.
impl TryFrom<u8> for FalconCoreRevSubversion {
@@ -125,7 +138,7 @@ pub(crate) enum FalconSecurityModel {
/// Also known as High-Secure, Privilege Level 3 or PL3.
Heavy = 3,
}
-impl_from_enum_to_u32!(FalconSecurityModel);
+impl_from_enum_to_u8!(FalconSecurityModel);
// TODO[FPRI]: replace with `FromPrimitive`.
impl TryFrom<u8> for FalconSecurityModel {
@@ -157,7 +170,7 @@ pub(crate) enum FalconModSelAlgo {
#[default]
Rsa3k = 1,
}
-impl_from_enum_to_u32!(FalconModSelAlgo);
+impl_from_enum_to_u8!(FalconModSelAlgo);
// TODO[FPRI]: replace with `FromPrimitive`.
impl TryFrom<u8> for FalconModSelAlgo {
@@ -179,7 +192,7 @@ pub(crate) enum DmaTrfCmdSize {
#[default]
Size256B = 0x6,
}
-impl_from_enum_to_u32!(DmaTrfCmdSize);
+impl_from_enum_to_u8!(DmaTrfCmdSize);
// TODO[FPRI]: replace with `FromPrimitive`.
impl TryFrom<u8> for DmaTrfCmdSize {
@@ -202,7 +215,6 @@ pub(crate) enum PeregrineCoreSelect {
/// RISC-V core is active.
Riscv = 1,
}
-impl_from_enum_to_u32!(PeregrineCoreSelect);
impl From<bool> for PeregrineCoreSelect {
fn from(value: bool) -> Self {
@@ -213,6 +225,15 @@ impl From<bool> for PeregrineCoreSelect {
}
}
+impl From<PeregrineCoreSelect> for bool {
+ fn from(value: PeregrineCoreSelect) -> Self {
+ match value {
+ PeregrineCoreSelect::Falcon => false,
+ PeregrineCoreSelect::Riscv => true,
+ }
+ }
+}
+
/// Different types of memory present in a falcon core.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum FalconMem {
@@ -236,7 +257,7 @@ pub(crate) enum FalconFbifTarget {
/// Non-coherent system memory (System DRAM).
NoncoherentSysmem = 2,
}
-impl_from_enum_to_u32!(FalconFbifTarget);
+impl_from_enum_to_u8!(FalconFbifTarget);
// TODO[FPRI]: replace with `FromPrimitive`.
impl TryFrom<u8> for FalconFbifTarget {
@@ -263,7 +284,6 @@ pub(crate) enum FalconFbifMemType {
/// Physical memory addresses.
Physical = 1,
}
-impl_from_enum_to_u32!(FalconFbifMemType);
/// Conversion from a single-bit register field.
impl From<bool> for FalconFbifMemType {
@@ -275,6 +295,15 @@ impl From<bool> for FalconFbifMemType {
}
}
+impl From<FalconFbifMemType> for bool {
+ fn from(value: FalconFbifMemType) -> Self {
+ match value {
+ FalconFbifMemType::Virtual => false,
+ FalconFbifMemType::Physical => true,
+ }
+ }
+}
+
/// Type used to represent the `PFALCON` registers address base for a given falcon engine.
pub(crate) struct PFalconBase(());
@@ -346,47 +375,29 @@ pub(crate) struct Falcon<E: FalconEngine> {
impl<E: FalconEngine + 'static> Falcon<E> {
/// Create a new falcon instance.
- ///
- /// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv
- /// controller.
- pub(crate) fn new(
- dev: &device::Device,
- chipset: Chipset,
- bar: &Bar0,
- need_riscv: bool,
- ) -> Result<Self> {
- let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, &E::ID);
- // Check that the revision and security model contain valid values.
- let _ = hwcfg1.core_rev()?;
- let _ = hwcfg1.security_model()?;
-
- if need_riscv {
- let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
- if !hwcfg2.riscv() {
- dev_err!(
- dev,
- "riscv support requested on a controller that does not support it\n"
- );
- return Err(EINVAL);
- }
- }
-
+ pub(crate) fn new(dev: &device::Device, chipset: Chipset) -> Result<Self> {
Ok(Self {
hal: hal::falcon_hal(chipset)?,
dev: dev.into(),
})
}
+ /// Resets DMA-related registers.
+ pub(crate) fn dma_reset(&self, bar: &Bar0) {
+ regs::NV_PFALCON_FBIF_CTL::update(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true));
+ regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
+ }
+
/// Wait for memory scrubbing to complete.
fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
// TIMEOUT: memory scrubbing should complete in less than 20ms.
- util::wait_on(Delta::from_millis(20), || {
- if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID).mem_scrubbing_done() {
- Some(())
- } else {
- None
- }
- })
+ read_poll_timeout(
+ || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)),
+ |r| r.mem_scrubbing_done(),
+ Delta::ZERO,
+ Delta::from_millis(20),
+ )
+ .map(|_| ())
}
/// Reset the falcon engine.
@@ -395,22 +406,19 @@ impl<E: FalconEngine + 'static> Falcon<E> {
// According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
// RESET_READY so a non-failing timeout is used.
- let _ = util::wait_on(Delta::from_micros(150), || {
- let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID);
- if r.reset_ready() {
- Some(())
- } else {
- None
- }
- });
+ let _ = read_poll_timeout(
+ || Ok(regs::NV_PFALCON_FALCON_HWCFG2::read(bar, &E::ID)),
+ |r| r.reset_ready(),
+ Delta::ZERO,
+ Delta::from_micros(150),
+ );
- regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(true));
+ regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(true));
- // TODO[DLAY]: replace with udelay() or equivalent once available.
// TIMEOUT: falcon engine should not take more than 10us to reset.
- let _: Result = util::wait_on(Delta::from_micros(10), || None);
+ fsleep(Delta::from_micros(10));
- regs::NV_PFALCON_FALCON_ENGINE::alter(bar, &E::ID, |v| v.set_reset(false));
+ regs::NV_PFALCON_FALCON_ENGINE::update(bar, &E::ID, |v| v.set_reset(false));
self.reset_wait_mem_scrubbing(bar)?;
@@ -452,7 +460,7 @@ impl<E: FalconEngine + 'static> Falcon<E> {
FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()),
FalconMem::Dmem => (
0,
- fw.dma_handle_with_offset(load_offsets.src_start as usize)?,
+ fw.dma_handle_with_offset(load_offsets.src_start.into_safe_cast())?,
),
};
if dma_start % DmaAddress::from(DMA_LEN) > 0 {
@@ -478,7 +486,7 @@ impl<E: FalconEngine + 'static> Falcon<E> {
dev_err!(self.dev, "DMA transfer length overflow");
return Err(EOVERFLOW);
}
- Some(upper_bound) if upper_bound as usize > fw.size() => {
+ Some(upper_bound) if usize::from_safe_cast(upper_bound) > fw.size() => {
dev_err!(self.dev, "DMA transfer goes beyond range of DMA object");
return Err(EINVAL);
}
@@ -488,9 +496,13 @@ impl<E: FalconEngine + 'static> Falcon<E> {
// Set up the base source DMA address.
regs::NV_PFALCON_FALCON_DMATRFBASE::default()
+ // CAST: `as u32` is used on purpose since we do want to strip the upper bits, which
+ // will be written to `NV_PFALCON_FALCON_DMATRFBASE1`.
.set_base((dma_start >> 8) as u32)
.write(bar, &E::ID);
regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
+ // CAST: `as u16` is used on purpose since the remaining bits are guaranteed to fit
+ // within a `u16`.
.set_base((dma_start >> 40) as u16)
.write(bar, &E::ID);
@@ -512,14 +524,12 @@ impl<E: FalconEngine + 'static> Falcon<E> {
// Wait for the transfer to complete.
// TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
// should ever take that long.
- util::wait_on(Delta::from_secs(2), || {
- let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID);
- if r.idle() {
- Some(())
- } else {
- None
- }
- })?;
+ read_poll_timeout(
+ || Ok(regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, &E::ID)),
+ |r| r.idle(),
+ Delta::ZERO,
+ Delta::from_secs(2),
+ )?;
}
Ok(())
@@ -527,9 +537,8 @@ impl<E: FalconEngine + 'static> Falcon<E> {
/// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
- regs::NV_PFALCON_FBIF_CTL::alter(bar, &E::ID, |v| v.set_allow_phys_no_ctx(true));
- regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, &E::ID);
- regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, &E::ID, 0, |v| {
+ self.dma_reset(bar);
+ regs::NV_PFALCON_FBIF_TRANSCFG::update(bar, &E::ID, 0, |v| {
v.set_target(FalconFbifTarget::CoherentSysmem)
.set_mem_type(FalconFbifMemType::Physical)
});
@@ -547,19 +556,35 @@ impl<E: FalconEngine + 'static> Falcon<E> {
Ok(())
}
- /// Runs the loaded firmware and waits for its completion.
- ///
- /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
- /// prior to running.
- ///
- /// Wait up to two seconds for the firmware to complete, and return its exit status read from
- /// the `MBOX0` and `MBOX1` registers.
- pub(crate) fn boot(
- &self,
- bar: &Bar0,
- mbox0: Option<u32>,
- mbox1: Option<u32>,
- ) -> Result<(u32, u32)> {
+ /// Wait until the falcon CPU is halted.
+ pub(crate) fn wait_till_halted(&self, bar: &Bar0) -> Result<()> {
+ // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
+ read_poll_timeout(
+ || Ok(regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID)),
+ |r| r.halted(),
+ Delta::ZERO,
+ Delta::from_secs(2),
+ )?;
+
+ Ok(())
+ }
+
+ /// Start the falcon CPU.
+ pub(crate) fn start(&self, bar: &Bar0) -> Result<()> {
+ match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() {
+ true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
+ .set_startcpu(true)
+ .write(bar, &E::ID),
+ false => regs::NV_PFALCON_FALCON_CPUCTL::default()
+ .set_startcpu(true)
+ .write(bar, &E::ID),
+ }
+
+ Ok(())
+ }
+
+ /// Writes values to the mailbox registers if provided.
+ pub(crate) fn write_mailboxes(&self, bar: &Bar0, mbox0: Option<u32>, mbox1: Option<u32>) {
if let Some(mbox0) = mbox0 {
regs::NV_PFALCON_FALCON_MAILBOX0::default()
.set_value(mbox0)
@@ -571,32 +596,43 @@ impl<E: FalconEngine + 'static> Falcon<E> {
.set_value(mbox1)
.write(bar, &E::ID);
}
+ }
- match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID).alias_en() {
- true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
- .set_startcpu(true)
- .write(bar, &E::ID),
- false => regs::NV_PFALCON_FALCON_CPUCTL::default()
- .set_startcpu(true)
- .write(bar, &E::ID),
- }
+ /// Reads the value from `mbox0` register.
+ pub(crate) fn read_mailbox0(&self, bar: &Bar0) -> u32 {
+ regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value()
+ }
- // TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
- util::wait_on(Delta::from_secs(2), || {
- let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, &E::ID);
- if r.halted() {
- Some(())
- } else {
- None
- }
- })?;
+ /// Reads the value from `mbox1` register.
+ pub(crate) fn read_mailbox1(&self, bar: &Bar0) -> u32 {
+ regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value()
+ }
- let (mbox0, mbox1) = (
- regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, &E::ID).value(),
- regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, &E::ID).value(),
- );
+ /// Reads values from both mailbox registers.
+ pub(crate) fn read_mailboxes(&self, bar: &Bar0) -> (u32, u32) {
+ let mbox0 = self.read_mailbox0(bar);
+ let mbox1 = self.read_mailbox1(bar);
+
+ (mbox0, mbox1)
+ }
- Ok((mbox0, mbox1))
+ /// Start running the loaded firmware.
+ ///
+ /// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
+ /// prior to running.
+ ///
+ /// Wait up to two seconds for the firmware to complete, and return its exit status read from
+ /// the `MBOX0` and `MBOX1` registers.
+ pub(crate) fn boot(
+ &self,
+ bar: &Bar0,
+ mbox0: Option<u32>,
+ mbox1: Option<u32>,
+ ) -> Result<(u32, u32)> {
+ self.write_mailboxes(bar, mbox0, mbox1);
+ self.start(bar)?;
+ self.wait_till_halted(bar)?;
+ Ok(self.read_mailboxes(bar))
}
/// Returns the fused version of the signature to use in order to run a HS firmware on this
@@ -610,4 +646,19 @@ impl<E: FalconEngine + 'static> Falcon<E> {
self.hal
.signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
}
+
+ /// Check if the RISC-V core is active.
+ ///
+ /// Returns `true` if the RISC-V core is active, `false` otherwise.
+ pub(crate) fn is_riscv_active(&self, bar: &Bar0) -> bool {
+ let cpuctl = regs::NV_PRISCV_RISCV_CPUCTL::read(bar, &E::ID);
+ cpuctl.active_stat()
+ }
+
+ /// Write the application version to the OS register.
+ pub(crate) fn write_os_version(&self, bar: &Bar0, app_version: u32) {
+ regs::NV_PFALCON_FALCON_OS::default()
+ .set_value(app_version)
+ .write(bar, &E::ID);
+ }
}
diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
index f17599cb49fa..67edef3636c1 100644
--- a/drivers/gpu/nova-core/falcon/gsp.rs
+++ b/drivers/gpu/nova-core/falcon/gsp.rs
@@ -1,9 +1,23 @@
// SPDX-License-Identifier: GPL-2.0
+use kernel::{
+ io::poll::read_poll_timeout,
+ prelude::*,
+ time::Delta, //
+};
+
use crate::{
driver::Bar0,
- falcon::{Falcon, FalconEngine, PFalcon2Base, PFalconBase},
- regs::{self, macros::RegisterBase},
+ falcon::{
+ Falcon,
+ FalconEngine,
+ PFalcon2Base,
+ PFalconBase, //
+ },
+ regs::{
+ self,
+ macros::RegisterBase, //
+ },
};
/// Type specifying the `Gsp` falcon engine. Cannot be instantiated.
@@ -29,4 +43,15 @@ impl Falcon<Gsp> {
.set_swgen0(true)
.write(bar, &Gsp::ID);
}
+
+ /// Checks if GSP reload/resume has completed during the boot process.
+ pub(crate) fn check_reload_completed(&self, bar: &Bar0, timeout: Delta) -> Result<bool> {
+ read_poll_timeout(
+ || Ok(regs::NV_PGC6_BSI_SECURE_SCRATCH_14::read(bar)),
+ |val| val.boot_stage_3_handoff(),
+ Delta::ZERO,
+ timeout,
+ )
+ .map(|_| true)
+ }
}
diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs
index bba288455617..8dc56a28ad65 100644
--- a/drivers/gpu/nova-core/falcon/hal.rs
+++ b/drivers/gpu/nova-core/falcon/hal.rs
@@ -2,9 +2,15 @@
use kernel::prelude::*;
-use crate::driver::Bar0;
-use crate::falcon::{Falcon, FalconBromParams, FalconEngine};
-use crate::gpu::Chipset;
+use crate::{
+ driver::Bar0,
+ falcon::{
+ Falcon,
+ FalconBromParams,
+ FalconEngine, //
+ },
+ gpu::Chipset,
+};
mod ga102;
@@ -44,7 +50,7 @@ pub(super) fn falcon_hal<E: FalconEngine + 'static>(
use Chipset::*;
let hal = match chipset {
- GA102 | GA103 | GA104 | GA106 | GA107 => {
+ GA102 | GA103 | GA104 | GA106 | GA107 | AD102 | AD103 | AD104 | AD106 | AD107 => {
KBox::new(ga102::Ga102::<E>::new(), GFP_KERNEL)? as KBox<dyn FalconHal<E>>
}
_ => return Err(ENOTSUPP),
diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-core/falcon/hal/ga102.rs
index 0b1cbe7853b3..69a7a95cac16 100644
--- a/drivers/gpu/nova-core/falcon/hal/ga102.rs
+++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs
@@ -2,16 +2,24 @@
use core::marker::PhantomData;
-use kernel::device;
-use kernel::prelude::*;
-use kernel::time::Delta;
+use kernel::{
+ device,
+ io::poll::read_poll_timeout,
+ prelude::*,
+ time::Delta, //
+};
-use crate::driver::Bar0;
-use crate::falcon::{
- Falcon, FalconBromParams, FalconEngine, FalconModSelAlgo, PeregrineCoreSelect,
+use crate::{
+ driver::Bar0,
+ falcon::{
+ Falcon,
+ FalconBromParams,
+ FalconEngine,
+ FalconModSelAlgo,
+ PeregrineCoreSelect, //
+ },
+ regs,
};
-use crate::regs;
-use crate::util;
use super::FalconHal;
@@ -23,14 +31,12 @@ fn select_core_ga102<E: FalconEngine>(bar: &Bar0) -> Result {
.write(bar, &E::ID);
// TIMEOUT: falcon core should take less than 10ms to report being enabled.
- util::wait_on(Delta::from_millis(10), || {
- let r = regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID);
- if r.valid() {
- Some(())
- } else {
- None
- }
- })?;
+ read_poll_timeout(
+ || Ok(regs::NV_PRISCV_RISCV_BCR_CTRL::read(bar, &E::ID)),
+ |r| r.valid(),
+ Delta::ZERO,
+ Delta::from_millis(10),
+ )?;
}
Ok(())
@@ -42,11 +48,9 @@ fn signature_reg_fuse_version_ga102(
engine_id_mask: u16,
ucode_id: u8,
) -> Result<u32> {
- const NV_FUSE_OPT_FPF_SIZE: u8 = regs::NV_FUSE_OPT_FPF_SIZE as u8;
-
// Each engine has 16 ucode version registers numbered from 1 to 16.
- let ucode_idx = match ucode_id {
- 1..=NV_FUSE_OPT_FPF_SIZE => (ucode_id - 1) as usize,
+ let ucode_idx = match usize::from(ucode_id) {
+ ucode_id @ 1..=regs::NV_FUSE_OPT_FPF_SIZE => ucode_id - 1,
_ => {
dev_err!(dev, "invalid ucode id {:#x}", ucode_id);
return Err(EINVAL);
diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/falcon/sec2.rs
index 815786c8480d..b57d362e576a 100644
--- a/drivers/gpu/nova-core/falcon/sec2.rs
+++ b/drivers/gpu/nova-core/falcon/sec2.rs
@@ -1,7 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
-use crate::falcon::{FalconEngine, PFalcon2Base, PFalconBase};
-use crate::regs::macros::RegisterBase;
+use crate::{
+ falcon::{
+ FalconEngine,
+ PFalcon2Base,
+ PFalconBase, //
+ },
+ regs::macros::RegisterBase,
+};
/// Type specifying the `Sec2` falcon engine. Cannot be instantiated.
pub(crate) struct Sec2(());
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index 27d9edab8347..3c9cf151786c 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -2,16 +2,29 @@
use core::ops::Range;
-use kernel::prelude::*;
-use kernel::ptr::{Alignable, Alignment};
-use kernel::sizes::*;
-use kernel::sync::aref::ARef;
-use kernel::{dev_warn, device};
-
-use crate::dma::DmaObject;
-use crate::driver::Bar0;
-use crate::gpu::Chipset;
-use crate::regs;
+use kernel::{
+ device,
+ prelude::*,
+ ptr::{
+ Alignable,
+ Alignment, //
+ },
+ sizes::*,
+ sync::aref::ARef, //
+};
+
+use crate::{
+ dma::DmaObject,
+ driver::Bar0,
+ firmware::gsp::GspFirmware,
+ gpu::Chipset,
+ gsp,
+ num::{
+ usize_as_u64,
+ FromSafeCast, //
+ },
+ regs,
+};
mod hal;
@@ -85,16 +98,28 @@ impl SysmemFlush {
///
/// Contains ranges of GPU memory reserved for a given purpose during the GSP boot process.
#[derive(Debug)]
-#[expect(dead_code)]
pub(crate) struct FbLayout {
+ /// Range of the framebuffer. Starts at `0`.
pub(crate) fb: Range<u64>,
+ /// VGA workspace, small area of reserved memory at the end of the framebuffer.
pub(crate) vga_workspace: Range<u64>,
+ /// FRTS range.
pub(crate) frts: Range<u64>,
+ /// Memory area containing the GSP bootloader image.
+ pub(crate) boot: Range<u64>,
+ /// Memory area containing the GSP firmware image.
+ pub(crate) elf: Range<u64>,
+ /// WPR2 heap.
+ pub(crate) wpr2_heap: Range<u64>,
+ /// WPR2 region range, starting with an instance of `GspFwWprMeta`.
+ pub(crate) wpr2: Range<u64>,
+ pub(crate) heap: Range<u64>,
+ pub(crate) vf_partition_count: u8,
}
impl FbLayout {
- /// Computes the FB layout.
- pub(crate) fn new(chipset: Chipset, bar: &Bar0) -> Result<Self> {
+ /// Computes the FB layout for `chipset` required to run the `gsp_fw` GSP firmware.
+ pub(crate) fn new(chipset: Chipset, bar: &Bar0, gsp_fw: &GspFirmware) -> Result<Self> {
let hal = hal::fb_hal(chipset);
let fb = {
@@ -105,14 +130,14 @@ impl FbLayout {
let vga_workspace = {
let vga_base = {
- const NV_PRAMIN_SIZE: u64 = SZ_1M as u64;
+ const NV_PRAMIN_SIZE: u64 = usize_as_u64(SZ_1M);
let base = fb.end - NV_PRAMIN_SIZE;
if hal.supports_display(bar) {
match regs::NV_PDISP_VGA_WORKSPACE_BASE::read(bar).vga_workspace_addr() {
Some(addr) => {
if addr < base {
- const VBIOS_WORKSPACE_SIZE: u64 = SZ_128K as u64;
+ const VBIOS_WORKSPACE_SIZE: u64 = usize_as_u64(SZ_128K);
// Point workspace address to end of framebuffer.
fb.end - VBIOS_WORKSPACE_SIZE
@@ -132,16 +157,61 @@ impl FbLayout {
let frts = {
const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
- const FRTS_SIZE: u64 = SZ_1M as u64;
+ const FRTS_SIZE: u64 = usize_as_u64(SZ_1M);
let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - FRTS_SIZE;
frts_base..frts_base + FRTS_SIZE
};
+ let boot = {
+ const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
+ let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
+ let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
+
+ bootloader_base..bootloader_base + bootloader_size
+ };
+
+ let elf = {
+ const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
+ let elf_size = u64::from_safe_cast(gsp_fw.size);
+ let elf_addr = (boot.start - elf_size).align_down(ELF_DOWN_ALIGN);
+
+ elf_addr..elf_addr + elf_size
+ };
+
+ let wpr2_heap = {
+ const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
+ let wpr2_heap_size =
+ gsp::LibosParams::from_chipset(chipset).wpr_heap_size(chipset, fb.end);
+ let wpr2_heap_addr = (elf.start - wpr2_heap_size).align_down(WPR2_HEAP_DOWN_ALIGN);
+
+ wpr2_heap_addr..(elf.start).align_down(WPR2_HEAP_DOWN_ALIGN)
+ };
+
+ let wpr2 = {
+ const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
+ let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
+ .align_down(WPR2_DOWN_ALIGN);
+
+ wpr2_addr..frts.end
+ };
+
+ let heap = {
+ const HEAP_SIZE: u64 = usize_as_u64(SZ_1M);
+
+ wpr2.start - HEAP_SIZE..wpr2.start
+ };
+
Ok(Self {
fb,
vga_workspace,
frts,
+ boot,
+ elf,
+ wpr2_heap,
+ wpr2,
+ heap,
+ vf_partition_count: 0,
})
}
}
diff --git a/drivers/gpu/nova-core/fb/hal.rs b/drivers/gpu/nova-core/fb/hal.rs
index 2f914948bb9a..aba0abd8ee00 100644
--- a/drivers/gpu/nova-core/fb/hal.rs
+++ b/drivers/gpu/nova-core/fb/hal.rs
@@ -2,8 +2,10 @@
use kernel::prelude::*;
-use crate::driver::Bar0;
-use crate::gpu::Chipset;
+use crate::{
+ driver::Bar0,
+ gpu::Chipset, //
+};
mod ga100;
mod ga102;
diff --git a/drivers/gpu/nova-core/fb/hal/ga100.rs b/drivers/gpu/nova-core/fb/hal/ga100.rs
index 871c42bf033a..e0acc41aa7cd 100644
--- a/drivers/gpu/nova-core/fb/hal/ga100.rs
+++ b/drivers/gpu/nova-core/fb/hal/ga100.rs
@@ -1,15 +1,17 @@
// SPDX-License-Identifier: GPL-2.0
-struct Ga100;
-
use kernel::prelude::*;
-use crate::driver::Bar0;
-use crate::fb::hal::FbHal;
-use crate::regs;
+use crate::{
+ driver::Bar0,
+ fb::hal::FbHal,
+ regs, //
+};
use super::tu102::FLUSH_SYSMEM_ADDR_SHIFT;
+struct Ga100;
+
pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) -> u64 {
u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::read(bar).adr_39_08()) << FLUSH_SYSMEM_ADDR_SHIFT
| u64::from(regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::read(bar).adr_63_40())
@@ -18,9 +20,13 @@ pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) -> u64 {
pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) {
regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::default()
+ // CAST: `as u32` is used on purpose since the remaining bits are guaranteed to fit within
+ // a `u32`.
.set_adr_63_40((addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI) as u32)
.write(bar);
regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
+ // CAST: `as u32` is used on purpose since we want to strip the upper bits that have been
+ // written to `NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI`.
.set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32)
.write(bar);
}
diff --git a/drivers/gpu/nova-core/fb/hal/ga102.rs b/drivers/gpu/nova-core/fb/hal/ga102.rs
index a73b77e39715..734605905031 100644
--- a/drivers/gpu/nova-core/fb/hal/ga102.rs
+++ b/drivers/gpu/nova-core/fb/hal/ga102.rs
@@ -2,9 +2,11 @@
use kernel::prelude::*;
-use crate::driver::Bar0;
-use crate::fb::hal::FbHal;
-use crate::regs;
+use crate::{
+ driver::Bar0,
+ fb::hal::FbHal,
+ regs, //
+};
fn vidmem_size_ga102(bar: &Bar0) -> u64 {
regs::NV_USABLE_FB_SIZE_IN_MB::read(bar).usable_fb_size()
diff --git a/drivers/gpu/nova-core/fb/hal/tu102.rs b/drivers/gpu/nova-core/fb/hal/tu102.rs
index b022c781caf4..eec984f4e816 100644
--- a/drivers/gpu/nova-core/fb/hal/tu102.rs
+++ b/drivers/gpu/nova-core/fb/hal/tu102.rs
@@ -1,10 +1,13 @@
// SPDX-License-Identifier: GPL-2.0
-use crate::driver::Bar0;
-use crate::fb::hal::FbHal;
-use crate::regs;
use kernel::prelude::*;
+use crate::{
+ driver::Bar0,
+ fb::hal::FbHal,
+ regs, //
+};
+
/// Shift applied to the sysmem address before it is written into `NV_PFB_NISO_FLUSH_SYSMEM_ADDR`,
/// to be used by HALs.
pub(super) const FLUSH_SYSMEM_ADDR_SHIFT: u32 = 8;
@@ -15,15 +18,13 @@ pub(super) fn read_sysmem_flush_page_gm107(bar: &Bar0) -> u64 {
pub(super) fn write_sysmem_flush_page_gm107(bar: &Bar0, addr: u64) -> Result {
// Check that the address doesn't overflow the receiving 32-bit register.
- if addr >> (u32::BITS + FLUSH_SYSMEM_ADDR_SHIFT) == 0 {
- regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
- .set_adr_39_08((addr >> FLUSH_SYSMEM_ADDR_SHIFT) as u32)
- .write(bar);
-
- Ok(())
- } else {
- Err(EINVAL)
- }
+ u32::try_from(addr >> FLUSH_SYSMEM_ADDR_SHIFT)
+ .map_err(|_| EINVAL)
+ .map(|addr| {
+ regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default()
+ .set_adr_39_08(addr)
+ .write(bar)
+ })
}
pub(super) fn display_enabled_gm107(bar: &Bar0) -> bool {
diff --git a/drivers/gpu/nova-core/firmware.rs b/drivers/gpu/nova-core/firmware.rs
index 4179a74a2342..2d2008b33fb4 100644
--- a/drivers/gpu/nova-core/firmware.rs
+++ b/drivers/gpu/nova-core/firmware.rs
@@ -4,17 +4,24 @@
//! to be loaded into a given execution unit.
use core::marker::PhantomData;
-use core::mem::size_of;
-use kernel::device;
-use kernel::firmware;
-use kernel::prelude::*;
-use kernel::str::CString;
-use kernel::transmute::FromBytes;
-
-use crate::dma::DmaObject;
-use crate::falcon::FalconFirmware;
-use crate::gpu;
+use kernel::{
+ device,
+ firmware,
+ prelude::*,
+ str::CString,
+ transmute::FromBytes, //
+};
+
+use crate::{
+ dma::DmaObject,
+ falcon::FalconFirmware,
+ gpu,
+ num::{
+ FromSafeCast,
+ IntoSafeCast, //
+ },
+};
pub(crate) mod booter;
pub(crate) mod fwsec;
@@ -75,7 +82,7 @@ impl FalconUCodeDescV3 {
const HDR_SIZE_SHIFT: u32 = 16;
const HDR_SIZE_MASK: u32 = 0xffff0000;
- ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT) as usize
+ ((self.hdr & HDR_SIZE_MASK) >> HDR_SIZE_SHIFT).into_safe_cast()
}
}
@@ -190,8 +197,8 @@ impl<'a> BinFirmware<'a> {
/// Returns the data payload of the firmware, or `None` if the data range is out of bounds of
/// the firmware image.
fn data(&self) -> Option<&[u8]> {
- let fw_start = self.hdr.data_offset as usize;
- let fw_size = self.hdr.data_size as usize;
+ let fw_start = usize::from_safe_cast(self.hdr.data_offset);
+ let fw_size = usize::from_safe_cast(self.hdr.data_size);
self.fw.get(fw_start..fw_start + fw_size)
}
diff --git a/drivers/gpu/nova-core/firmware/booter.rs b/drivers/gpu/nova-core/firmware/booter.rs
index b4ff1b17e4a0..f107f753214a 100644
--- a/drivers/gpu/nova-core/firmware/booter.rs
+++ b/drivers/gpu/nova-core/firmware/booter.rs
@@ -4,20 +4,41 @@
//! running on [`Sec2`], that is used on Turing/Ampere to load the GSP firmware into the GSP falcon
//! (and optionally unload it through a separate firmware image).
-use core::marker::PhantomData;
-use core::mem::size_of;
-use core::ops::Deref;
-
-use kernel::device;
-use kernel::prelude::*;
-use kernel::transmute::FromBytes;
-
-use crate::dma::DmaObject;
-use crate::driver::Bar0;
-use crate::falcon::sec2::Sec2;
-use crate::falcon::{Falcon, FalconBromParams, FalconFirmware, FalconLoadParams, FalconLoadTarget};
-use crate::firmware::{BinFirmware, FirmwareDmaObject, FirmwareSignature, Signed, Unsigned};
-use crate::gpu::Chipset;
+use core::{
+ marker::PhantomData,
+ ops::Deref, //
+};
+
+use kernel::{
+ device,
+ prelude::*,
+ transmute::FromBytes, //
+};
+
+use crate::{
+ dma::DmaObject,
+ driver::Bar0,
+ falcon::{
+ sec2::Sec2,
+ Falcon,
+ FalconBromParams,
+ FalconFirmware,
+ FalconLoadParams,
+ FalconLoadTarget, //
+ },
+ firmware::{
+ BinFirmware,
+ FirmwareDmaObject,
+ FirmwareSignature,
+ Signed,
+ Unsigned, //
+ },
+ gpu::Chipset,
+ num::{
+ FromSafeCast,
+ IntoSafeCast, //
+ },
+};
/// Local convenience function to return a copy of `S` by reinterpreting the bytes starting at
/// `offset` in `slice`.
@@ -74,7 +95,7 @@ impl<'a> HsFirmwareV2<'a> {
///
/// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.
fn new(bin_fw: &BinFirmware<'a>) -> Result<Self> {
- frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset as usize)
+ frombytes_at::<HsHeaderV2>(bin_fw.fw, bin_fw.hdr.header_offset.into_safe_cast())
.map(|hdr| Self { hdr, fw: bin_fw.fw })
}
@@ -83,7 +104,7 @@ impl<'a> HsFirmwareV2<'a> {
/// Fails if the offset of the patch location is outside the bounds of the firmware
/// image.
fn patch_location(&self) -> Result<u32> {
- frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset as usize)
+ frombytes_at::<u32>(self.fw, self.hdr.patch_loc_offset.into_safe_cast())
}
/// Returns an iterator to the signatures of the firmware. The iterator can be empty if the
@@ -91,19 +112,23 @@ impl<'a> HsFirmwareV2<'a> {
///
/// Fails if the pointed signatures are outside the bounds of the firmware image.
fn signatures_iter(&'a self) -> Result<impl Iterator<Item = BooterSignature<'a>>> {
- let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset as usize)?;
+ let num_sig = frombytes_at::<u32>(self.fw, self.hdr.num_sig_offset.into_safe_cast())?;
let iter = match self.hdr.sig_prod_size.checked_div(num_sig) {
// If there are no signatures, return an iterator that will yield zero elements.
None => (&[] as &[u8]).chunks_exact(1),
Some(sig_size) => {
- let patch_sig = frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset as usize)?;
- let signatures_start = (self.hdr.sig_prod_offset + patch_sig) as usize;
+ let patch_sig =
+ frombytes_at::<u32>(self.fw, self.hdr.patch_sig_offset.into_safe_cast())?;
+ let signatures_start = usize::from_safe_cast(self.hdr.sig_prod_offset + patch_sig);
self.fw
// Get signatures range.
- .get(signatures_start..signatures_start + self.hdr.sig_prod_size as usize)
+ .get(
+ signatures_start
+ ..signatures_start + usize::from_safe_cast(self.hdr.sig_prod_size),
+ )
.ok_or(EINVAL)?
- .chunks_exact(sig_size as usize)
+ .chunks_exact(sig_size.into_safe_cast())
}
};
@@ -132,9 +157,9 @@ impl HsSignatureParams {
/// Fails if the meta data parameter of `hs_fw` is outside the bounds of the firmware image, or
/// if its size doesn't match that of [`HsSignatureParams`].
fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
- let start = hs_fw.hdr.meta_data_offset as usize;
+ let start = usize::from_safe_cast(hs_fw.hdr.meta_data_offset);
let end = start
- .checked_add(hs_fw.hdr.meta_data_size as usize)
+ .checked_add(hs_fw.hdr.meta_data_size.into_safe_cast())
.ok_or(EINVAL)?;
hs_fw
@@ -169,7 +194,7 @@ impl HsLoadHeaderV2 {
///
/// Fails if the header pointed at by `hs_fw` is not within the bounds of the firmware image.
fn new(hs_fw: &HsFirmwareV2<'_>) -> Result<Self> {
- frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset as usize)
+ frombytes_at::<Self>(hs_fw.fw, hs_fw.hdr.header_offset.into_safe_cast())
}
}
@@ -198,12 +223,13 @@ impl HsLoadHeaderV2App {
} else {
frombytes_at::<Self>(
hs_fw.fw,
- (hs_fw.hdr.header_offset as usize)
+ usize::from_safe_cast(hs_fw.hdr.header_offset)
// Skip the load header...
.checked_add(size_of::<HsLoadHeaderV2>())
// ... and jump to app header `idx`.
.and_then(|offset| {
- offset.checked_add((idx as usize).checked_mul(size_of::<Self>())?)
+ offset
+ .checked_add(usize::from_safe_cast(idx).checked_mul(size_of::<Self>())?)
})
.ok_or(EINVAL)?,
)
@@ -318,12 +344,12 @@ impl BooterFirmware {
dev_err!(dev, "invalid fuse version for Booter firmware\n");
return Err(EINVAL);
};
- signatures.nth(idx as usize)
+ signatures.nth(idx.into_safe_cast())
}
}
.ok_or(EINVAL)?;
- ucode.patch_signature(&signature, patch_loc as usize)?
+ ucode.patch_signature(&signature, patch_loc.into_safe_cast())?
}
};
diff --git a/drivers/gpu/nova-core/firmware/fwsec.rs b/drivers/gpu/nova-core/firmware/fwsec.rs
index 8edbb5c0572c..b28e34d279f4 100644
--- a/drivers/gpu/nova-core/firmware/fwsec.rs
+++ b/drivers/gpu/nova-core/firmware/fwsec.rs
@@ -10,20 +10,48 @@
//! - The command to be run, as this firmware can perform several tasks ;
//! - The ucode signature, so the GSP falcon can run FWSEC in HS mode.
-use core::marker::PhantomData;
-use core::mem::{align_of, size_of};
-use core::ops::Deref;
-
-use kernel::device::{self, Device};
-use kernel::prelude::*;
-use kernel::transmute::FromBytes;
-
-use crate::dma::DmaObject;
-use crate::driver::Bar0;
-use crate::falcon::gsp::Gsp;
-use crate::falcon::{Falcon, FalconBromParams, FalconFirmware, FalconLoadParams, FalconLoadTarget};
-use crate::firmware::{FalconUCodeDescV3, FirmwareDmaObject, FirmwareSignature, Signed, Unsigned};
-use crate::vbios::Vbios;
+use core::{
+ marker::PhantomData,
+ mem::size_of,
+ ops::Deref, //
+};
+
+use kernel::{
+ device::{
+ self,
+ Device, //
+ },
+ prelude::*,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ dma::DmaObject,
+ driver::Bar0,
+ falcon::{
+ gsp::Gsp,
+ Falcon,
+ FalconBromParams,
+ FalconFirmware,
+ FalconLoadParams,
+ FalconLoadTarget, //
+ },
+ firmware::{
+ FalconUCodeDescV3,
+ FirmwareDmaObject,
+ FirmwareSignature,
+ Signed,
+ Unsigned, //
+ },
+ num::{
+ FromSafeCast,
+ IntoSafeCast, //
+ },
+ vbios::Vbios,
+};
const NVFW_FALCON_APPIF_ID_DMEMMAPPER: u32 = 0x4;
@@ -35,7 +63,7 @@ struct FalconAppifHdrV1 {
entry_size: u8,
entry_count: u8,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FalconAppifHdrV1 {}
#[repr(C, packed)]
@@ -44,7 +72,7 @@ struct FalconAppifV1 {
id: u32,
dmem_base: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FalconAppifV1 {}
#[derive(Debug)]
@@ -68,8 +96,10 @@ struct FalconAppifDmemmapperV3 {
ucode_cmd_mask1: u32,
multi_tgt_tbl: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FalconAppifDmemmapperV3 {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for FalconAppifDmemmapperV3 {}
#[derive(Debug)]
#[repr(C, packed)]
@@ -80,8 +110,10 @@ struct ReadVbios {
size: u32,
flags: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for ReadVbios {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for ReadVbios {}
#[derive(Debug)]
#[repr(C, packed)]
@@ -92,8 +124,10 @@ struct FrtsRegion {
size: u32,
ftype: u32,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FrtsRegion {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for FrtsRegion {}
const NVFW_FRTS_CMD_REGION_TYPE_FB: u32 = 2;
@@ -102,8 +136,10 @@ struct FrtsCmd {
read_vbios: ReadVbios,
frts_region: FrtsRegion,
}
-// SAFETY: any byte sequence is valid for this struct.
+// SAFETY: Any byte sequence is valid for this struct.
unsafe impl FromBytes for FrtsCmd {}
+// SAFETY: This struct doesn't contain uninitialized bytes and doesn't have interior mutability.
+unsafe impl AsBytes for FrtsCmd {}
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS: u32 = 0x15;
const NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB: u32 = 0x19;
@@ -147,26 +183,15 @@ impl FirmwareSignature<FwsecFirmware> for Bcrt30Rsa3kSignature {}
///
/// # Safety
///
-/// Callers must ensure that the region of memory returned is not written for as long as the
-/// returned reference is alive.
-///
-/// TODO[TRSM][COHA]: Remove this and `transmute_mut` once `CoherentAllocation::as_slice` is
-/// available and we have a way to transmute objects implementing FromBytes, e.g.:
-/// https://lore.kernel.org/lkml/20250330234039.29814-1-christiansantoslima21@gmail.com/
-unsafe fn transmute<'a, 'b, T: Sized + FromBytes>(
- fw: &'a DmaObject,
- offset: usize,
-) -> Result<&'b T> {
- if offset + size_of::<T>() > fw.size() {
- return Err(EINVAL);
- }
- if (fw.start_ptr() as usize + offset) % align_of::<T>() != 0 {
- return Err(EINVAL);
- }
-
- // SAFETY: we have checked that the pointer is properly aligned that its pointed memory is
- // large enough the contains an instance of `T`, which implements `FromBytes`.
- Ok(unsafe { &*(fw.start_ptr().add(offset).cast::<T>()) })
+/// * Callers must ensure that the device does not read/write to/from memory while the returned
+/// reference is live.
+/// * Callers must ensure that this call does not race with a write to the same region while
+/// the returned reference is live.
+unsafe fn transmute<T: Sized + FromBytes>(fw: &DmaObject, offset: usize) -> Result<&T> {
+ // SAFETY: The safety requirements of the function guarantee the device won't read
+ // or write to memory while the reference is alive and that this call won't race
+ // with writes to the same memory region.
+ T::from_bytes(unsafe { fw.as_slice(offset, size_of::<T>())? }).ok_or(EINVAL)
}
/// Reinterpret the area starting from `offset` in `fw` as a mutable instance of `T` (which must
@@ -174,22 +199,18 @@ unsafe fn transmute<'a, 'b, T: Sized + FromBytes>(
///
/// # Safety
///
-/// Callers must ensure that the region of memory returned is not read or written for as long as
-/// the returned reference is alive.
-unsafe fn transmute_mut<'a, 'b, T: Sized + FromBytes>(
- fw: &'a mut DmaObject,
+/// * Callers must ensure that the device does not read/write to/from memory while the returned
+/// slice is live.
+/// * Callers must ensure that this call does not race with a read or write to the same region
+/// while the returned slice is live.
+unsafe fn transmute_mut<T: Sized + FromBytes + AsBytes>(
+ fw: &mut DmaObject,
offset: usize,
-) -> Result<&'b mut T> {
- if offset + size_of::<T>() > fw.size() {
- return Err(EINVAL);
- }
- if (fw.start_ptr_mut() as usize + offset) % align_of::<T>() != 0 {
- return Err(EINVAL);
- }
-
- // SAFETY: we have checked that the pointer is properly aligned that its pointed memory is
- // large enough the contains an instance of `T`, which implements `FromBytes`.
- Ok(unsafe { &mut *(fw.start_ptr_mut().add(offset).cast::<T>()) })
+) -> Result<&mut T> {
+ // SAFETY: The safety requirements of the function guarantee the device won't read
+ // or write to memory while the reference is alive and that this call won't race
+ // with writes or reads to the same memory region.
+ T::from_bytes_mut(unsafe { fw.as_slice_mut(offset, size_of::<T>())? }).ok_or(EINVAL)
}
/// The FWSEC microcode, extracted from the BIOS and to be run on the GSP falcon.
@@ -250,7 +271,7 @@ impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
let ucode = bios.fwsec_image().ucode(desc)?;
let mut dma_object = DmaObject::from_data(dev, ucode)?;
- let hdr_offset = (desc.imem_load_size + desc.interface_offset) as usize;
+ let hdr_offset = usize::from_safe_cast(desc.imem_load_size + desc.interface_offset);
// SAFETY: we have exclusive access to `dma_object`.
let hdr: &FalconAppifHdrV1 = unsafe { transmute(&dma_object, hdr_offset) }?;
@@ -259,61 +280,62 @@ impl FirmwareDmaObject<FwsecFirmware, Unsigned> {
}
// Find the DMEM mapper section in the firmware.
- for i in 0..hdr.entry_count as usize {
- let app: &FalconAppifV1 =
+ for i in 0..usize::from(hdr.entry_count) {
// SAFETY: we have exclusive access to `dma_object`.
- unsafe {
+ let app: &FalconAppifV1 = unsafe {
transmute(
&dma_object,
- hdr_offset + hdr.header_size as usize + i * hdr.entry_size as usize
+ hdr_offset + usize::from(hdr.header_size) + i * usize::from(hdr.entry_size),
)
}?;
if app.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER {
continue;
}
+ let dmem_base = app.dmem_base;
// SAFETY: we have exclusive access to `dma_object`.
let dmem_mapper: &mut FalconAppifDmemmapperV3 = unsafe {
transmute_mut(
&mut dma_object,
- (desc.imem_load_size + app.dmem_base) as usize,
+ (desc.imem_load_size + dmem_base).into_safe_cast(),
)
}?;
+ dmem_mapper.init_cmd = match cmd {
+ FwsecCommand::Frts { .. } => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS,
+ FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB,
+ };
+ let cmd_in_buffer_offset = dmem_mapper.cmd_in_buffer_offset;
+
// SAFETY: we have exclusive access to `dma_object`.
let frts_cmd: &mut FrtsCmd = unsafe {
transmute_mut(
&mut dma_object,
- (desc.imem_load_size + dmem_mapper.cmd_in_buffer_offset) as usize,
+ (desc.imem_load_size + cmd_in_buffer_offset).into_safe_cast(),
)
}?;
frts_cmd.read_vbios = ReadVbios {
ver: 1,
- hdr: size_of::<ReadVbios>() as u32,
+ hdr: u32::try_from(size_of::<ReadVbios>())?,
addr: 0,
size: 0,
flags: 2,
};
-
- dmem_mapper.init_cmd = match cmd {
- FwsecCommand::Frts {
- frts_addr,
- frts_size,
- } => {
- frts_cmd.frts_region = FrtsRegion {
- ver: 1,
- hdr: size_of::<FrtsRegion>() as u32,
- addr: (frts_addr >> 12) as u32,
- size: (frts_size >> 12) as u32,
- ftype: NVFW_FRTS_CMD_REGION_TYPE_FB,
- };
-
- NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS
- }
- FwsecCommand::Sb => NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB,
- };
+ if let FwsecCommand::Frts {
+ frts_addr,
+ frts_size,
+ } = cmd
+ {
+ frts_cmd.frts_region = FrtsRegion {
+ ver: 1,
+ hdr: u32::try_from(size_of::<FrtsRegion>())?,
+ addr: u32::try_from(frts_addr >> 12)?,
+ size: u32::try_from(frts_size >> 12)?,
+ ftype: NVFW_FRTS_CMD_REGION_TYPE_FB,
+ };
+ }
// Return early as we found and patched the DMEMMAPPER region.
return Ok(Self(dma_object, PhantomData));
@@ -338,7 +360,7 @@ impl FwsecFirmware {
// Patch signature if needed.
let desc = bios.fwsec_image().header()?;
let ucode_signed = if desc.signature_count != 0 {
- let sig_base_img = (desc.imem_load_size + desc.pkc_data_offset) as usize;
+ let sig_base_img = usize::from_safe_cast(desc.imem_load_size + desc.pkc_data_offset);
let desc_sig_versions = u32::from(desc.signature_versions);
let reg_fuse_version =
falcon.signature_reg_fuse_version(bar, desc.engine_id_mask, desc.ucode_id)?;
@@ -369,7 +391,7 @@ impl FwsecFirmware {
// Mask of the bits of `desc_sig_versions` to preserve.
let reg_fuse_version_mask = reg_fuse_version_bit.wrapping_sub(1);
- (desc_sig_versions & reg_fuse_version_mask).count_ones() as usize
+ usize::from_safe_cast((desc_sig_versions & reg_fuse_version_mask).count_ones())
};
dev_dbg!(dev, "patching signature with index {}\n", signature_idx);
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 9b70095434c6..0549805282ab 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -2,16 +2,30 @@
use core::mem::size_of_val;
-use kernel::device;
-use kernel::dma::{DataDirection, DmaAddress};
-use kernel::kvec;
-use kernel::prelude::*;
-use kernel::scatterlist::{Owned, SGTable};
+use kernel::{
+ device,
+ dma::{
+ DataDirection,
+ DmaAddress, //
+ },
+ kvec,
+ prelude::*,
+ scatterlist::{
+ Owned,
+ SGTable, //
+ },
+};
-use crate::dma::DmaObject;
-use crate::firmware::riscv::RiscvFirmware;
-use crate::gpu::{Architecture, Chipset};
-use crate::gsp::GSP_PAGE_SIZE;
+use crate::{
+ dma::DmaObject,
+ firmware::riscv::RiscvFirmware,
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
+ gsp::GSP_PAGE_SIZE,
+ num::FromSafeCast,
+};
/// Ad-hoc and temporary module to extract sections from ELF images.
///
@@ -129,11 +143,11 @@ pub(crate) struct GspFirmware {
/// Level 0 page table (single 4KB page) with one entry: DMA address of first level 1 page.
level0: DmaObject,
/// Size in bytes of the firmware contained in [`Self::fw`].
- size: usize,
+ pub(crate) size: usize,
/// Device-mapped GSP signatures matching the GPU's [`Chipset`].
- signatures: DmaObject,
+ pub(crate) signatures: DmaObject,
/// GSP bootloader, verifies the GSP firmware before loading and running it.
- bootloader: RiscvFirmware,
+ pub(crate) bootloader: RiscvFirmware,
}
impl GspFirmware {
@@ -150,6 +164,7 @@ impl GspFirmware {
let sigs_section = match chipset.arch() {
Architecture::Ampere => ".fwsignature_ga10x",
+ Architecture::Ada => ".fwsignature_ad10x",
_ => return Err(ENOTSUPP),
};
let signatures = elf::elf64_section(fw.data(), sigs_section)
@@ -202,10 +217,10 @@ impl GspFirmware {
let mut level0_data = kvec![0u8; GSP_PAGE_SIZE]?;
// Fill level 1 page entry.
- #[allow(clippy::useless_conversion)]
- let level1_entry = u64::from(level1.iter().next().unwrap().dma_address());
- let dst = &mut level0_data[..size_of_val(&level1_entry)];
- dst.copy_from_slice(&level1_entry.to_le_bytes());
+ let level1_entry = level1.iter().next().ok_or(EINVAL)?;
+ let level1_entry_addr = level1_entry.dma_address();
+ let dst = &mut level0_data[..size_of_val(&level1_entry_addr)];
+ dst.copy_from_slice(&level1_entry_addr.to_le_bytes());
// Turn the level0 page table into a [`DmaObject`].
DmaObject::from_data(dev, &level0_data)?
@@ -216,7 +231,6 @@ impl GspFirmware {
}))
}
- #[expect(unused)]
/// Returns the DMA handle of the radix3 level 0 page table.
pub(crate) fn radix3_dma_handle(&self) -> DmaAddress {
self.level0.dma_handle()
@@ -231,10 +245,11 @@ impl GspFirmware {
fn map_into_lvl(sg_table: &SGTable<Owned<VVec<u8>>>, mut dst: VVec<u8>) -> Result<VVec<u8>> {
for sg_entry in sg_table.iter() {
// Number of pages we need to map.
- let num_pages = (sg_entry.dma_len() as usize).div_ceil(GSP_PAGE_SIZE);
+ let num_pages = usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE);
for i in 0..num_pages {
- let entry = sg_entry.dma_address() + (i as u64 * GSP_PAGE_SIZE as u64);
+ let entry = sg_entry.dma_address()
+ + (u64::from_safe_cast(i) * u64::from_safe_cast(GSP_PAGE_SIZE));
dst.extend_from_slice(&entry.to_le_bytes(), GFP_KERNEL)?;
}
}
diff --git a/drivers/gpu/nova-core/firmware/riscv.rs b/drivers/gpu/nova-core/firmware/riscv.rs
index afb08f5bc4ba..28dfef63657a 100644
--- a/drivers/gpu/nova-core/firmware/riscv.rs
+++ b/drivers/gpu/nova-core/firmware/riscv.rs
@@ -5,13 +5,18 @@
use core::mem::size_of;
-use kernel::device;
-use kernel::firmware::Firmware;
-use kernel::prelude::*;
-use kernel::transmute::FromBytes;
+use kernel::{
+ device,
+ firmware::Firmware,
+ prelude::*,
+ transmute::FromBytes, //
+};
-use crate::dma::DmaObject;
-use crate::firmware::BinFirmware;
+use crate::{
+ dma::DmaObject,
+ firmware::BinFirmware,
+ num::FromSafeCast, //
+};
/// Descriptor for microcode running on a RISC-V core.
#[repr(C)]
@@ -41,7 +46,7 @@ impl RmRiscvUCodeDesc {
///
/// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.
fn new(bin_fw: &BinFirmware<'_>) -> Result<Self> {
- let offset = bin_fw.hdr.header_offset as usize;
+ let offset = usize::from_safe_cast(bin_fw.hdr.header_offset);
bin_fw
.fw
@@ -52,18 +57,17 @@ impl RmRiscvUCodeDesc {
}
/// A parsed firmware for a RISC-V core, ready to be loaded and run.
-#[expect(unused)]
pub(crate) struct RiscvFirmware {
/// Offset at which the code starts in the firmware image.
- code_offset: u32,
+ pub(crate) code_offset: u32,
/// Offset at which the data starts in the firmware image.
- data_offset: u32,
+ pub(crate) data_offset: u32,
/// Offset at which the manifest starts in the firmware image.
- manifest_offset: u32,
+ pub(crate) manifest_offset: u32,
/// Application version.
- app_version: u32,
+ pub(crate) app_version: u32,
/// Device-mapped firmware image.
- ucode: DmaObject,
+ pub(crate) ucode: DmaObject,
}
impl RiscvFirmware {
@@ -74,8 +78,8 @@ impl RiscvFirmware {
let riscv_desc = RmRiscvUCodeDesc::new(&bin_fw)?;
let ucode = {
- let start = bin_fw.hdr.data_offset as usize;
- let len = bin_fw.hdr.data_size as usize;
+ let start = usize::from_safe_cast(bin_fw.hdr.data_offset);
+ let len = usize::from_safe_cast(bin_fw.hdr.data_size);
DmaObject::from_data(dev, fw.data().get(start..start + len).ok_or(EINVAL)?)?
};
diff --git a/drivers/gpu/nova-core/gfw.rs b/drivers/gpu/nova-core/gfw.rs
index 8ac1ed187199..9121f400046d 100644
--- a/drivers/gpu/nova-core/gfw.rs
+++ b/drivers/gpu/nova-core/gfw.rs
@@ -18,13 +18,16 @@
//!
//! Note that the devinit sequence also needs to run during suspend/resume.
-use kernel::bindings;
-use kernel::prelude::*;
-use kernel::time::Delta;
+use kernel::{
+ io::poll::read_poll_timeout,
+ prelude::*,
+ time::Delta, //
+};
-use crate::driver::Bar0;
-use crate::regs;
-use crate::util;
+use crate::{
+ driver::Bar0,
+ regs, //
+};
/// Wait for the `GFW` (GPU firmware) boot completion signal (`GFW_BOOT`), or a 4 seconds timeout.
///
@@ -50,22 +53,19 @@ pub(crate) fn wait_gfw_boot_completion(bar: &Bar0) -> Result {
//
// TIMEOUT: arbitrarily large value. GFW starts running immediately after the GPU is put out of
// reset, and should complete in less time than that.
- util::wait_on(Delta::from_secs(4), || {
- // Check that FWSEC has lowered its protection level before reading the GFW_BOOT status.
- let gfw_booted = regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK::read(bar)
- .read_protection_level0()
- && regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT::read(bar).completed();
-
- if gfw_booted {
- Some(())
- } else {
- // TODO[DLAY]: replace with [1] once it merges.
- // [1] https://lore.kernel.org/rust-for-linux/20250423192857.199712-6-fujita.tomonori@gmail.com/
- //
- // SAFETY: `msleep()` is safe to call with any parameter.
- unsafe { bindings::msleep(1) };
-
- None
- }
- })
+ read_poll_timeout(
+ || {
+ Ok(
+ // Check that FWSEC has lowered its protection level before reading the GFW_BOOT
+ // status.
+ regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK::read(bar)
+ .read_protection_level0()
+ && regs::NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT::read(bar).completed(),
+ )
+ },
+ |&gfw_booted| gfw_booted,
+ Delta::from_millis(1),
+ Delta::from_secs(4),
+ )
+ .map(|_| ())
}
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index af20e2daea24..629c9d2dc994 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -1,13 +1,26 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::{device, devres::Devres, error::code::*, fmt, pci, prelude::*, sync::Arc};
+use kernel::{
+ device,
+ devres::Devres,
+ fmt,
+ pci,
+ prelude::*,
+ sync::Arc, //
+};
-use crate::driver::Bar0;
-use crate::falcon::{gsp::Gsp as GspFalcon, sec2::Sec2 as Sec2Falcon, Falcon};
-use crate::fb::SysmemFlush;
-use crate::gfw;
-use crate::gsp::Gsp;
-use crate::regs;
+use crate::{
+ driver::Bar0,
+ falcon::{
+ gsp::Gsp as GspFalcon,
+ sec2::Sec2 as Sec2Falcon,
+ Falcon, //
+ },
+ fb::SysmemFlush,
+ gfw,
+ gsp::Gsp,
+ regs,
+};
macro_rules! define_chipset {
({ $($variant:ident = $value:expr),* $(,)* }) =>
@@ -109,8 +122,14 @@ impl fmt::Display for Chipset {
}
/// Enum representation of the GPU generation.
-#[derive(fmt::Debug)]
+///
+/// TODO: remove the `Default` trait implementation, and the `#[default]`
+/// attribute, once the register!() macro (which creates Architecture items) no
+/// longer requires it for read-only fields.
+#[derive(fmt::Debug, Default, Copy, Clone)]
+#[repr(u8)]
pub(crate) enum Architecture {
+ #[default]
Turing = 0x16,
Ampere = 0x17,
Ada = 0x19,
@@ -129,13 +148,20 @@ impl TryFrom<u8> for Architecture {
}
}
+impl From<Architecture> for u8 {
+ fn from(value: Architecture) -> Self {
+ // CAST: `Architecture` is `repr(u8)`, so this cast is always lossless.
+ value as u8
+ }
+}
+
pub(crate) struct Revision {
major: u8,
minor: u8,
}
-impl Revision {
- fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self {
+impl From<regs::NV_PMC_BOOT_42> for Revision {
+ fn from(boot0: regs::NV_PMC_BOOT_42) -> Self {
Self {
major: boot0.major_revision(),
minor: boot0.minor_revision(),
@@ -149,24 +175,67 @@ impl fmt::Display for Revision {
}
}
-/// Structure holding the metadata of the GPU.
+/// Structure holding a basic description of the GPU: `Chipset` and `Revision`.
pub(crate) struct Spec {
chipset: Chipset,
- /// The revision of the chipset.
revision: Revision,
}
impl Spec {
- fn new(bar: &Bar0) -> Result<Spec> {
+ fn new(dev: &device::Device, bar: &Bar0) -> Result<Spec> {
+ // Some brief notes about boot0 and boot42, in chronological order:
+ //
+ // NV04 through NV50:
+ //
+ // Not supported by Nova. boot0 is necessary and sufficient to identify these GPUs.
+ // boot42 may not even exist on some of these GPUs.
+ //
+ // Fermi through Volta:
+ //
+ // Not supported by Nova. boot0 is still sufficient to identify these GPUs, but boot42
+ // is also guaranteed to be both present and accurate.
+ //
+ // Turing and later:
+ //
+ // Supported by Nova. Identified by first checking boot0 to ensure that the GPU is not
+ // from an earlier (pre-Fermi) era, and then using boot42 to precisely identify the GPU.
+ // Somewhere in the Rubin timeframe, boot0 will no longer have space to add new GPU IDs.
+
let boot0 = regs::NV_PMC_BOOT_0::read(bar);
+ if boot0.is_older_than_fermi() {
+ return Err(ENODEV);
+ }
+
+ let boot42 = regs::NV_PMC_BOOT_42::read(bar);
+ Spec::try_from(boot42).inspect_err(|_| {
+ dev_err!(dev, "Unsupported chipset: {}\n", boot42);
+ })
+ }
+}
+
+impl TryFrom<regs::NV_PMC_BOOT_42> for Spec {
+ type Error = Error;
+
+ fn try_from(boot42: regs::NV_PMC_BOOT_42) -> Result<Self> {
Ok(Self {
- chipset: boot0.chipset()?,
- revision: Revision::from_boot0(boot0),
+ chipset: boot42.chipset()?,
+ revision: boot42.into(),
})
}
}
+impl fmt::Display for Spec {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_fmt(fmt!(
+ "Chipset: {}, Architecture: {:?}, Revision: {}",
+ self.chipset,
+ self.chipset.arch(),
+ self.revision
+ ))
+ }
+}
+
/// Structure holding the resources required to operate the GPU.
#[pin_data]
pub(crate) struct Gpu {
@@ -192,14 +261,8 @@ impl Gpu {
bar: &'a Bar0,
) -> impl PinInit<Self, Error> + 'a {
try_pin_init!(Self {
- spec: Spec::new(bar).inspect(|spec| {
- dev_info!(
- pdev.as_ref(),
- "NVIDIA (Chipset: {}, Architecture: {:?}, Revision: {})\n",
- spec.chipset,
- spec.chipset.arch(),
- spec.revision
- );
+ spec: Spec::new(pdev.as_ref(), bar).inspect(|spec| {
+ dev_info!(pdev.as_ref(),"NVIDIA ({})\n", spec);
})?,
// We must wait for GFW_BOOT completion before doing any significant setup on the GPU.
@@ -213,14 +276,12 @@ impl Gpu {
gsp_falcon: Falcon::new(
pdev.as_ref(),
spec.chipset,
- bar,
- spec.chipset > Chipset::GA100,
)
.inspect(|falcon| falcon.clear_swgen0_intr(bar))?,
- sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset, bar, true)?,
+ sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?,
- gsp <- Gsp::new(),
+ gsp <- Gsp::new(pdev)?,
_: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? },
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 64e472e7a9d3..fb6f74797178 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -2,21 +2,160 @@
mod boot;
-use kernel::prelude::*;
+use kernel::{
+ device,
+ dma::{
+ CoherentAllocation,
+ DmaAddress, //
+ },
+ dma_write,
+ pci,
+ prelude::*,
+ transmute::AsBytes, //
+};
+pub(crate) mod cmdq;
+pub(crate) mod commands;
mod fw;
+mod sequencer;
+
+pub(crate) use fw::{
+ GspFwWprMeta,
+ LibosParams, //
+};
+
+use crate::{
+ gsp::cmdq::Cmdq,
+ gsp::fw::{
+ GspArgumentsCached,
+ LibosMemoryRegionInitArgument, //
+ },
+ num,
+};
pub(crate) const GSP_PAGE_SHIFT: usize = 12;
pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT;
-/// GSP runtime data.
+/// Number of GSP pages to use in a RM log buffer.
+const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10;
+
+/// Array of page table entries, as understood by the GSP bootloader.
+#[repr(C)]
+struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
+
+/// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one.
+unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
+
+impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
+ /// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
+ fn new(start: DmaAddress) -> Result<Self> {
+ let mut ptes = [0u64; NUM_PAGES];
+ for (i, pte) in ptes.iter_mut().enumerate() {
+ *pte = start
+ .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
+ .ok_or(EOVERFLOW)?;
+ }
+
+ Ok(Self(ptes))
+ }
+}
+
+/// The logging buffers are byte queues that contain encoded printf-like
+/// messages from GSP-RM. They need to be decoded by a special application
+/// that can parse the buffers.
///
-/// This is an empty pinned placeholder for now.
+/// The 'loginit' buffer contains logs from early GSP-RM init and
+/// exception dumps. The 'logrm' buffer contains the subsequent logs. Both are
+/// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE.
+///
+/// The physical address map for the log buffer is stored in the buffer
+/// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp).
+/// Initially, pp is equal to 0. If the buffer has valid logging data in it,
+/// then pp points to index into the buffer where the next logging entry will
+/// be written. Therefore, the logging data is valid if:
+/// 1 <= pp < sizeof(buffer)/sizeof(u64)
+struct LogBuffer(CoherentAllocation<u8>);
+
+impl LogBuffer {
+ /// Creates a new `LogBuffer` mapped on `dev`.
+ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
+ const NUM_PAGES: usize = RM_LOG_BUFFER_NUM_PAGES;
+
+ let mut obj = Self(CoherentAllocation::<u8>::alloc_coherent(
+ dev,
+ NUM_PAGES * GSP_PAGE_SIZE,
+ GFP_KERNEL | __GFP_ZERO,
+ )?);
+ let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?;
+
+ // SAFETY: `obj` has just been created and we are its sole user.
+ unsafe {
+ // Copy the self-mapping PTE at the expected location.
+ obj.0
+ .as_slice_mut(size_of::<u64>(), size_of_val(&ptes))?
+ .copy_from_slice(ptes.as_bytes())
+ };
+
+ Ok(obj)
+ }
+}
+
+/// GSP runtime data.
#[pin_data]
-pub(crate) struct Gsp {}
+pub(crate) struct Gsp {
+ /// Libos arguments.
+ pub(crate) libos: CoherentAllocation<LibosMemoryRegionInitArgument>,
+ /// Init log buffer.
+ loginit: LogBuffer,
+ /// Interrupts log buffer.
+ logintr: LogBuffer,
+ /// RM log buffer.
+ logrm: LogBuffer,
+ /// Command queue.
+ pub(crate) cmdq: Cmdq,
+ /// RM arguments.
+ rmargs: CoherentAllocation<GspArgumentsCached>,
+}
impl Gsp {
- pub(crate) fn new() -> impl PinInit<Self> {
- pin_init!(Self {})
+ // Creates an in-place initializer for a `Gsp` manager for `pdev`.
+ pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self, Error>> {
+ let dev = pdev.as_ref();
+ let libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent(
+ dev,
+ GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(),
+ GFP_KERNEL | __GFP_ZERO,
+ )?;
+
+ // Initialise the logging structures. The OpenRM equivalents are in:
+ // _kgspInitLibosLoggingStructures (allocates memory for buffers)
+ // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
+ let loginit = LogBuffer::new(dev)?;
+ dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?;
+
+ let logintr = LogBuffer::new(dev)?;
+ dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?;
+
+ let logrm = LogBuffer::new(dev)?;
+ dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?;
+
+ let cmdq = Cmdq::new(dev)?;
+
+ let rmargs = CoherentAllocation::<GspArgumentsCached>::alloc_coherent(
+ dev,
+ 1,
+ GFP_KERNEL | __GFP_ZERO,
+ )?;
+ dma_write!(rmargs[0] = fw::GspArgumentsCached::new(&cmdq))?;
+ dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", &rmargs))?;
+
+ Ok(try_pin_init!(Self {
+ libos,
+ loginit,
+ logintr,
+ logrm,
+ rmargs,
+ cmdq,
+ }))
}
}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 2800f3aee37d..54937606b5b0 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -1,21 +1,47 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::device;
-use kernel::pci;
-use kernel::prelude::*;
-
-use crate::driver::Bar0;
-use crate::falcon::{gsp::Gsp, sec2::Sec2, Falcon};
-use crate::fb::FbLayout;
-use crate::firmware::{
- booter::{BooterFirmware, BooterKind},
- fwsec::{FwsecCommand, FwsecFirmware},
- gsp::GspFirmware,
- FIRMWARE_VERSION,
+use kernel::{
+ device,
+ dma::CoherentAllocation,
+ dma_write,
+ io::poll::read_poll_timeout,
+ pci,
+ prelude::*,
+ time::Delta, //
+};
+
+use crate::{
+ driver::Bar0,
+ falcon::{
+ gsp::Gsp,
+ sec2::Sec2,
+ Falcon, //
+ },
+ fb::FbLayout,
+ firmware::{
+ booter::{
+ BooterFirmware,
+ BooterKind, //
+ },
+ fwsec::{
+ FwsecCommand,
+ FwsecFirmware, //
+ },
+ gsp::GspFirmware,
+ FIRMWARE_VERSION, //
+ },
+ gpu::Chipset,
+ gsp::{
+ commands,
+ sequencer::{
+ GspSequencer,
+ GspSequencerParams, //
+ },
+ GspFwWprMeta, //
+ },
+ regs,
+ vbios::Vbios,
};
-use crate::gpu::Chipset;
-use crate::regs;
-use crate::vbios::Vbios;
impl super::Gsp {
/// Helper function to load and run the FWSEC-FRTS firmware and confirm that it has properly
@@ -102,7 +128,7 @@ impl super::Gsp {
///
/// Upon return, the GSP is up and running, and its runtime object given as return value.
pub(crate) fn boot(
- self: Pin<&mut Self>,
+ mut self: Pin<&mut Self>,
pdev: &pci::Device<device::Bound>,
bar: &Bar0,
chipset: Chipset,
@@ -113,17 +139,17 @@ impl super::Gsp {
let bios = Vbios::new(dev, bar)?;
- let _gsp_fw = KBox::pin_init(
+ let gsp_fw = KBox::pin_init(
GspFirmware::new(dev, chipset, FIRMWARE_VERSION)?,
GFP_KERNEL,
)?;
- let fb_layout = FbLayout::new(chipset, bar)?;
+ let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
dev_dbg!(dev, "{:#x?}\n", fb_layout);
Self::run_fwsec_frts(dev, gsp_falcon, bar, &bios, &fb_layout)?;
- let _booter_loader = BooterFirmware::new(
+ let booter_loader = BooterFirmware::new(
dev,
BooterKind::Loader,
chipset,
@@ -132,6 +158,95 @@ impl super::Gsp {
bar,
)?;
+ let wpr_meta =
+ CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
+ dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
+
+ self.cmdq
+ .send_command(bar, commands::SetSystemInfo::new(pdev))?;
+ self.cmdq.send_command(bar, commands::SetRegistry::new())?;
+
+ gsp_falcon.reset(bar)?;
+ let libos_handle = self.libos.dma_handle();
+ let (mbox0, mbox1) = gsp_falcon.boot(
+ bar,
+ Some(libos_handle as u32),
+ Some((libos_handle >> 32) as u32),
+ )?;
+ dev_dbg!(
+ pdev.as_ref(),
+ "GSP MBOX0: {:#x}, MBOX1: {:#x}\n",
+ mbox0,
+ mbox1
+ );
+
+ dev_dbg!(
+ pdev.as_ref(),
+ "Using SEC2 to load and run the booter_load firmware...\n"
+ );
+
+ sec2_falcon.reset(bar)?;
+ sec2_falcon.dma_load(bar, &booter_loader)?;
+ let wpr_handle = wpr_meta.dma_handle();
+ let (mbox0, mbox1) = sec2_falcon.boot(
+ bar,
+ Some(wpr_handle as u32),
+ Some((wpr_handle >> 32) as u32),
+ )?;
+ dev_dbg!(
+ pdev.as_ref(),
+ "SEC2 MBOX0: {:#x}, MBOX1{:#x}\n",
+ mbox0,
+ mbox1
+ );
+
+ if mbox0 != 0 {
+ dev_err!(
+ pdev.as_ref(),
+ "Booter-load failed with error {:#x}\n",
+ mbox0
+ );
+ return Err(ENODEV);
+ }
+
+ gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
+
+ // Poll for RISC-V to become active before running sequencer
+ read_poll_timeout(
+ || Ok(gsp_falcon.is_riscv_active(bar)),
+ |val: &bool| *val,
+ Delta::from_millis(10),
+ Delta::from_secs(5),
+ )?;
+
+ dev_dbg!(
+ pdev.as_ref(),
+ "RISC-V active? {}\n",
+ gsp_falcon.is_riscv_active(bar),
+ );
+
+ // Create and run the GSP sequencer.
+ let seq_params = GspSequencerParams {
+ bootloader_app_version: gsp_fw.bootloader.app_version,
+ libos_dma_handle: libos_handle,
+ gsp_falcon,
+ sec2_falcon,
+ dev: pdev.as_ref().into(),
+ bar,
+ };
+ GspSequencer::run(&mut self.cmdq, seq_params)?;
+
+ // Wait until GSP is fully initialized.
+ commands::wait_gsp_init_done(&mut self.cmdq)?;
+
+ // Obtain and display basic GPU information.
+ let info = commands::get_gsp_info(&mut self.cmdq, bar)?;
+ dev_info!(
+ pdev.as_ref(),
+ "GPU name: {}\n",
+ info.gpu_name().unwrap_or("invalid GPU name")
+ );
+
Ok(())
}
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
new file mode 100644
index 000000000000..6f946d14868a
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -0,0 +1,679 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use core::{
+ cmp,
+ mem,
+ sync::atomic::{
+ fence,
+ Ordering, //
+ }, //
+};
+
+use kernel::{
+ device,
+ dma::{
+ CoherentAllocation,
+ DmaAddress, //
+ },
+ dma_write,
+ io::poll::read_poll_timeout,
+ prelude::*,
+ sync::aref::ARef,
+ time::Delta,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ driver::Bar0,
+ gsp::{
+ fw::{
+ GspMsgElement,
+ MsgFunction,
+ MsgqRxHeader,
+ MsgqTxHeader, //
+ },
+ PteArray,
+ GSP_PAGE_SHIFT,
+ GSP_PAGE_SIZE, //
+ },
+ num,
+ regs,
+ sbuffer::SBufferIter, //
+};
+
+/// Trait implemented by types representing a command to send to the GSP.
+///
+/// The main purpose of this trait is to provide [`Cmdq::send_command`] with the information it
+/// needs to send a given command.
+///
+/// [`CommandToGsp::init`] in particular is responsible for initializing the command directly
+/// into the space reserved for it in the command queue buffer.
+///
+/// Some commands may be followed by a variable-length payload. For these, the
+/// [`CommandToGsp::variable_payload_len`] and [`CommandToGsp::init_variable_payload`] need to be
+/// defined as well.
+pub(crate) trait CommandToGsp {
+ /// Function identifying this command to the GSP.
+ const FUNCTION: MsgFunction;
+
+ /// Type generated by [`CommandToGsp::init`], to be written into the command queue buffer.
+ type Command: FromBytes + AsBytes;
+
+ /// Error type returned by [`CommandToGsp::init`].
+ type InitError;
+
+ /// In-place command initializer responsible for filling the command in the command queue
+ /// buffer.
+ fn init(&self) -> impl Init<Self::Command, Self::InitError>;
+
+ /// Size of the variable-length payload following the command structure generated by
+ /// [`CommandToGsp::init`].
+ ///
+ /// Most commands don't have a variable-length payload, so this is zero by default.
+ fn variable_payload_len(&self) -> usize {
+ 0
+ }
+
+ /// Method initializing the variable-length payload.
+ ///
+ /// The command buffer is circular, which means that we may need to jump back to its beginning
+ /// while in the middle of a command. For this reason, the variable-length payload is
+ /// initialized using a [`SBufferIter`].
+ ///
+ /// This method will receive a buffer of the length returned by
+ /// [`CommandToGsp::variable_payload_len`], and must write every single byte of it. Leaving
+ /// unwritten space will lead to an error.
+ ///
+ /// Most commands don't have a variable-length payload, so this does nothing by default.
+ fn init_variable_payload(
+ &self,
+ _dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
+ ) -> Result {
+ Ok(())
+ }
+}
+
+/// Trait representing messages received from the GSP.
+///
+/// This trait tells [`Cmdq::receive_msg`] how it can receive a given type of message.
+pub(crate) trait MessageFromGsp: Sized {
+ /// Function identifying this message from the GSP.
+ const FUNCTION: MsgFunction;
+
+ /// Error type returned by [`MessageFromGsp::read`].
+ type InitError;
+
+ /// Type containing the raw message to be read from the message queue.
+ type Message: FromBytes;
+
+ /// Method reading the message from the message queue and returning it.
+ ///
+ /// From a `Self::Message` and a [`SBufferIter`], constructs an instance of `Self` and returns
+ /// it.
+ fn read(
+ msg: &Self::Message,
+ sbuffer: &mut SBufferIter<core::array::IntoIter<&[u8], 2>>,
+ ) -> Result<Self, Self::InitError>;
+}
+
+/// Number of GSP pages making the [`Msgq`].
+pub(crate) const MSGQ_NUM_PAGES: u32 = 0x3f;
+
+/// Circular buffer of a [`Msgq`].
+///
+/// This area of memory is to be shared between the driver and the GSP to exchange commands or
+/// messages.
+#[repr(C, align(0x1000))]
+#[derive(Debug)]
+struct MsgqData {
+ data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)],
+}
+
+// Annoyingly we are forced to use a literal to specify the alignment of
+// `MsgqData`, so check that it corresponds to the actual GSP page size here.
+static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
+
+/// Unidirectional message queue.
+///
+/// Contains the data for a message queue, that either the driver or GSP writes to.
+///
+/// Note that while the write pointer of `tx` corresponds to the `msgq` of the same instance, the
+/// read pointer of `rx` actually refers to the `Msgq` owned by the other side.
+/// This design ensures that only the driver or GSP ever writes to a given instance of this struct.
+#[repr(C)]
+// There is no struct defined for this in the open-gpu-kernel-source headers.
+// Instead it is defined by code in `GspMsgQueuesInit()`.
+struct Msgq {
+ /// Header for sending messages, including the write pointer.
+ tx: MsgqTxHeader,
+ /// Header for receiving messages, including the read pointer.
+ rx: MsgqRxHeader,
+ /// The message queue proper.
+ msgq: MsgqData,
+}
+
+/// Structure shared between the driver and the GSP and containing the command and message queues.
+#[repr(C)]
+struct GspMem {
+ /// Self-mapping page table entries.
+ ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>,
+ /// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
+ /// write and read pointers that the CPU updates.
+ ///
+ /// This member is read-only for the GSP.
+ cpuq: Msgq,
+ /// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
+ /// write and read pointers that the GSP updates.
+ ///
+ /// This member is read-only for the driver.
+ gspq: Msgq,
+}
+
+// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
+// that is not a problem because they are not used outside the kernel.
+unsafe impl AsBytes for GspMem {}
+
+// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
+// that is not a problem because they are not used outside the kernel.
+unsafe impl FromBytes for GspMem {}
+
+/// Wrapper around [`GspMem`] to share it with the GPU using a [`CoherentAllocation`].
+///
+/// This provides the low-level functionality to communicate with the GSP, including allocation of
+/// queue space to write messages to and management of read/write pointers.
+///
+/// This is shared with the GSP, with clear ownership rules regarding the command queues:
+///
+/// * The driver owns (i.e. can write to) the part of the CPU message queue between the CPU write
+/// pointer and the GSP read pointer. This region is returned by [`Self::driver_write_area`].
+/// * The driver owns (i.e. can read from) the part of the GSP message queue between the CPU read
+/// pointer and the GSP write pointer. This region is returned by [`Self::driver_read_area`].
+struct DmaGspMem(CoherentAllocation<GspMem>);
+
+impl DmaGspMem {
+ /// Allocate a new instance and map it for `dev`.
+ fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
+ const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();
+ const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();
+
+ let gsp_mem =
+ CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
+ dma_write!(gsp_mem[0].ptes = PteArray::new(gsp_mem.dma_handle())?)?;
+ dma_write!(gsp_mem[0].cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES))?;
+ dma_write!(gsp_mem[0].cpuq.rx = MsgqRxHeader::new())?;
+
+ Ok(Self(gsp_mem))
+ }
+
+ /// Returns the region of the CPU message queue that the driver is currently allowed to write
+ /// to.
+ ///
+ /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
+ /// that case the second slice will have a non-zero length.
+ fn driver_write_area(&mut self) -> (&mut [[u8; GSP_PAGE_SIZE]], &mut [[u8; GSP_PAGE_SIZE]]) {
+ let tx = self.cpu_write_ptr() as usize;
+ let rx = self.gsp_read_ptr() as usize;
+
+ // SAFETY:
+ // - The `CoherentAllocation` contains exactly one object.
+ // - We will only access the driver-owned part of the shared memory.
+ // - Per the safety statement of the function, no concurrent access will be performed.
+ let gsp_mem = &mut unsafe { self.0.as_slice_mut(0, 1) }.unwrap()[0];
+ // PANIC: per the invariant of `cpu_write_ptr`, `tx` is `<= MSGQ_NUM_PAGES`.
+ let (before_tx, after_tx) = gsp_mem.cpuq.msgq.data.split_at_mut(tx);
+
+ if rx <= tx {
+ // The area from `tx` up to the end of the ring, and from the beginning of the ring up
+ // to `rx`, minus one unit, belongs to the driver.
+ if rx == 0 {
+ let last = after_tx.len() - 1;
+ (&mut after_tx[..last], &mut before_tx[0..0])
+ } else {
+ (after_tx, &mut before_tx[..rx])
+ }
+ } else {
+ // The area from `tx` to `rx`, minus one unit, belongs to the driver.
+ //
+ // PANIC: per the invariants of `cpu_write_ptr` and `gsp_read_ptr`, `rx` and `tx` are
+ // `<= MSGQ_NUM_PAGES`, and the test above ensured that `rx > tx`.
+ (after_tx.split_at_mut(rx - tx).0, &mut before_tx[0..0])
+ }
+ }
+
+ /// Returns the region of the GSP message queue that the driver is currently allowed to read
+ /// from.
+ ///
+ /// As the message queue is a circular buffer, the region may be discontiguous in memory. In
+ /// that case the second slice will have a non-zero length.
+ fn driver_read_area(&self) -> (&[[u8; GSP_PAGE_SIZE]], &[[u8; GSP_PAGE_SIZE]]) {
+ let tx = self.gsp_write_ptr() as usize;
+ let rx = self.cpu_read_ptr() as usize;
+
+ // SAFETY:
+ // - The `CoherentAllocation` contains exactly one object.
+ // - We will only access the driver-owned part of the shared memory.
+ // - Per the safety statement of the function, no concurrent access will be performed.
+ let gsp_mem = &unsafe { self.0.as_slice(0, 1) }.unwrap()[0];
+ // PANIC: per the invariant of `cpu_read_ptr`, `xx` is `<= MSGQ_NUM_PAGES`.
+ let (before_rx, after_rx) = gsp_mem.gspq.msgq.data.split_at(rx);
+
+ match tx.cmp(&rx) {
+ cmp::Ordering::Equal => (&after_rx[0..0], &after_rx[0..0]),
+ cmp::Ordering::Greater => (&after_rx[..tx], &before_rx[0..0]),
+ cmp::Ordering::Less => (after_rx, &before_rx[..tx]),
+ }
+ }
+
+ /// Allocates a region on the command queue that is large enough to send a command of `size`
+ /// bytes.
+ ///
+ /// This returns a [`GspCommand`] ready to be written to by the caller.
+ ///
+ /// # Errors
+ ///
+ /// - `EAGAIN` if the driver area is too small to hold the requested command.
+ /// - `EIO` if the command header is not properly aligned.
+ fn allocate_command(&mut self, size: usize) -> Result<GspCommand<'_>> {
+ // Get the current writable area as an array of bytes.
+ let (slice_1, slice_2) = {
+ let (slice_1, slice_2) = self.driver_write_area();
+
+ #[allow(clippy::incompatible_msrv)]
+ (slice_1.as_flattened_mut(), slice_2.as_flattened_mut())
+ };
+
+ // If the GSP is still processing previous messages the shared region
+ // may be full in which case we will have to retry once the GSP has
+ // processed the existing commands.
+ if size_of::<GspMsgElement>() + size > slice_1.len() + slice_2.len() {
+ return Err(EAGAIN);
+ }
+
+ // Extract area for the `GspMsgElement`.
+ let (header, slice_1) = GspMsgElement::from_bytes_mut_prefix(slice_1).ok_or(EIO)?;
+
+ // Create the contents area.
+ let (slice_1, slice_2) = if slice_1.len() > size {
+ // Contents fits entirely in `slice_1`.
+ (&mut slice_1[..size], &mut slice_2[0..0])
+ } else {
+ // Need all of `slice_1` and some of `slice_2`.
+ let slice_2_len = size - slice_1.len();
+ (slice_1, &mut slice_2[..slice_2_len])
+ };
+
+ Ok(GspCommand {
+ header,
+ contents: (slice_1, slice_2),
+ })
+ }
+
+ // Returns the index of the memory page the GSP will write the next message to.
+ //
+ // # Invariants
+ //
+ // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ fn gsp_write_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ (unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Returns the index of the memory page the GSP will read the next command from.
+ //
+ // # Invariants
+ //
+ // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ fn gsp_read_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ (unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Returns the index of the memory page the CPU can read the next message from.
+ //
+ // # Invariants
+ //
+ // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ fn cpu_read_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The ['CoherentAllocation'] contains at least one object.
+ // - By the invariants of CoherentAllocation the pointer is valid.
+ (unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Informs the GSP that it can send `elem_count` new pages into the message queue.
+ fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
+ let rptr = self.cpu_read_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES;
+
+ // Ensure read pointer is properly ordered.
+ fence(Ordering::SeqCst);
+
+ let gsp_mem = self.0.start_ptr_mut();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ unsafe { (*gsp_mem).cpuq.rx.set_read_ptr(rptr) };
+ }
+
+ // Returns the index of the memory page the CPU can write the next command to.
+ //
+ // # Invariants
+ //
+ // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
+ fn cpu_write_ptr(&self) -> u32 {
+ let gsp_mem = self.0.start_ptr();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ (unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES)
+ }
+
+ // Informs the GSP that it can process `elem_count` new pages from the command queue.
+ fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
+ let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES;
+ let gsp_mem = self.0.start_ptr_mut();
+
+ // SAFETY:
+ // - The 'CoherentAllocation' contains at least one object.
+ // - By the invariants of `CoherentAllocation` the pointer is valid.
+ unsafe { (*gsp_mem).cpuq.tx.set_write_ptr(wptr) };
+
+ // Ensure all command data is visible before triggering the GSP read.
+ fence(Ordering::SeqCst);
+ }
+}
+
+/// A command ready to be sent on the command queue.
+///
+/// This is the type returned by [`DmaGspMem::allocate_command`].
+struct GspCommand<'a> {
+ // Writable reference to the header of the command.
+ header: &'a mut GspMsgElement,
+ // Writable slices to the contents of the command. The second slice is zero unless the command
+ // loops over the command queue.
+ contents: (&'a mut [u8], &'a mut [u8]),
+}
+
+/// A message ready to be processed from the message queue.
+///
+/// This is the type returned by [`Cmdq::wait_for_msg`].
+struct GspMessage<'a> {
+ // Reference to the header of the message.
+ header: &'a GspMsgElement,
+ // Slices to the contents of the message. The second slice is zero unless the message loops
+ // over the message queue.
+ contents: (&'a [u8], &'a [u8]),
+}
+
+/// GSP command queue.
+///
+/// Provides the ability to send commands and receive messages from the GSP using a shared memory
+/// area.
+pub(crate) struct Cmdq {
+ /// Device this command queue belongs to.
+ dev: ARef<device::Device>,
+ /// Current command sequence number.
+ seq: u32,
+ /// Memory area shared with the GSP for communicating commands and messages.
+ gsp_mem: DmaGspMem,
+}
+
+impl Cmdq {
+ /// Offset of the data after the PTEs.
+ const POST_PTE_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq);
+
+ /// Offset of command queue ring buffer.
+ pub(crate) const CMDQ_OFFSET: usize = core::mem::offset_of!(GspMem, cpuq)
+ + core::mem::offset_of!(Msgq, msgq)
+ - Self::POST_PTE_OFFSET;
+
+ /// Offset of message queue ring buffer.
+ pub(crate) const STATQ_OFFSET: usize = core::mem::offset_of!(GspMem, gspq)
+ + core::mem::offset_of!(Msgq, msgq)
+ - Self::POST_PTE_OFFSET;
+
+ /// Number of page table entries for the GSP shared region.
+ pub(crate) const NUM_PTES: usize = size_of::<GspMem>() >> GSP_PAGE_SHIFT;
+
+ /// Creates a new command queue for `dev`.
+ pub(crate) fn new(dev: &device::Device<device::Bound>) -> Result<Cmdq> {
+ let gsp_mem = DmaGspMem::new(dev)?;
+
+ Ok(Cmdq {
+ dev: dev.into(),
+ seq: 0,
+ gsp_mem,
+ })
+ }
+
+ /// Computes the checksum for the message pointed to by `it`.
+ ///
+ /// A message is made of several parts, so `it` is an iterator over byte slices representing
+ /// these parts.
+ fn calculate_checksum<T: Iterator<Item = u8>>(it: T) -> u32 {
+ let sum64 = it
+ .enumerate()
+ .map(|(idx, byte)| (((idx % 8) * 8) as u32, byte))
+ .fold(0, |acc, (rol, byte)| acc ^ u64::from(byte).rotate_left(rol));
+
+ ((sum64 >> 32) as u32) ^ (sum64 as u32)
+ }
+
+ /// Notifies the GSP that we have updated the command queue pointers.
+ fn notify_gsp(bar: &Bar0) {
+ regs::NV_PGSP_QUEUE_HEAD::default()
+ .set_address(0)
+ .write(bar);
+ }
+
+ /// Sends `command` to the GSP.
+ ///
+ /// # Errors
+ ///
+ /// - `EAGAIN` if there was not enough space in the command queue to send the command.
+ /// - `EIO` if the variable payload requested by the command has not been entirely
+ /// written to by its [`CommandToGsp::init_variable_payload`] method.
+ ///
+ /// Error codes returned by the command initializers are propagated as-is.
+ pub(crate) fn send_command<M>(&mut self, bar: &Bar0, command: M) -> Result
+ where
+ M: CommandToGsp,
+ // This allows all error types, including `Infallible`, to be used for `M::InitError`.
+ Error: From<M::InitError>,
+ {
+ let command_size = size_of::<M::Command>() + command.variable_payload_len();
+ let dst = self.gsp_mem.allocate_command(command_size)?;
+
+ // Extract area for the command itself.
+ let (cmd, payload_1) = M::Command::from_bytes_mut_prefix(dst.contents.0).ok_or(EIO)?;
+
+ // Fill the header and command in-place.
+ let msg_element = GspMsgElement::init(self.seq, command_size, M::FUNCTION);
+ // SAFETY: `msg_header` and `cmd` are valid references, and not touched if the initializer
+ // fails.
+ unsafe {
+ msg_element.__init(core::ptr::from_mut(dst.header))?;
+ command.init().__init(core::ptr::from_mut(cmd))?;
+ }
+
+ // Fill the variable-length payload.
+ if command_size > size_of::<M::Command>() {
+ let mut sbuffer =
+ SBufferIter::new_writer([&mut payload_1[..], &mut dst.contents.1[..]]);
+ command.init_variable_payload(&mut sbuffer)?;
+
+ if !sbuffer.is_empty() {
+ return Err(EIO);
+ }
+ }
+
+ // Compute checksum now that the whole message is ready.
+ dst.header
+ .set_checksum(Cmdq::calculate_checksum(SBufferIter::new_reader([
+ dst.header.as_bytes(),
+ dst.contents.0,
+ dst.contents.1,
+ ])));
+
+ dev_dbg!(
+ &self.dev,
+ "GSP RPC: send: seq# {}, function={}, length=0x{:x}\n",
+ self.seq,
+ M::FUNCTION,
+ dst.header.length(),
+ );
+
+ // All set - update the write pointer and inform the GSP of the new command.
+ let elem_count = dst.header.element_count();
+ self.seq += 1;
+ self.gsp_mem.advance_cpu_write_ptr(elem_count);
+ Cmdq::notify_gsp(bar);
+
+ Ok(())
+ }
+
+ /// Wait for a message to become available on the message queue.
+ ///
+ /// This works purely at the transport layer and does not interpret or validate the message
+ /// beyond the advertised length in its [`GspMsgElement`].
+ ///
+ /// This method returns:
+ ///
+ /// - A reference to the [`GspMsgElement`] of the message,
+ /// - Two byte slices with the contents of the message. The second slice is empty unless the
+ /// message loops across the message queue.
+ ///
+ /// # Errors
+ ///
+ /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
+ /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the
+ /// message queue.
+ ///
+ /// Error codes returned by the message constructor are propagated as-is.
+ fn wait_for_msg(&self, timeout: Delta) -> Result<GspMessage<'_>> {
+ // Wait for a message to arrive from the GSP.
+ let (slice_1, slice_2) = read_poll_timeout(
+ || Ok(self.gsp_mem.driver_read_area()),
+ |driver_area| !driver_area.0.is_empty(),
+ Delta::from_millis(1),
+ timeout,
+ )
+ .map(|(slice_1, slice_2)| {
+ #[allow(clippy::incompatible_msrv)]
+ (slice_1.as_flattened(), slice_2.as_flattened())
+ })?;
+
+ // Extract the `GspMsgElement`.
+ let (header, slice_1) = GspMsgElement::from_bytes_prefix(slice_1).ok_or(EIO)?;
+
+ dev_dbg!(
+ self.dev,
+ "GSP RPC: receive: seq# {}, function={:?}, length=0x{:x}\n",
+ header.sequence(),
+ header.function(),
+ header.length(),
+ );
+
+ // Check that the driver read area is large enough for the message.
+ if slice_1.len() + slice_2.len() < header.length() {
+ return Err(EIO);
+ }
+
+ // Cut the message slices down to the actual length of the message.
+ let (slice_1, slice_2) = if slice_1.len() > header.length() {
+ // PANIC: we checked above that `slice_1` is at least as long as `msg_header.length()`.
+ (slice_1.split_at(header.length()).0, &slice_2[0..0])
+ } else {
+ (
+ slice_1,
+ // PANIC: we checked above that `slice_1.len() + slice_2.len()` is at least as
+ // large as `msg_header.length()`.
+ slice_2.split_at(header.length() - slice_1.len()).0,
+ )
+ };
+
+ // Validate checksum.
+ if Cmdq::calculate_checksum(SBufferIter::new_reader([
+ header.as_bytes(),
+ slice_1,
+ slice_2,
+ ])) != 0
+ {
+ dev_err!(
+ self.dev,
+ "GSP RPC: receive: Call {} - bad checksum",
+ header.sequence()
+ );
+ return Err(EIO);
+ }
+
+ Ok(GspMessage {
+ header,
+ contents: (slice_1, slice_2),
+ })
+ }
+
+ /// Receive a message from the GSP.
+ ///
+ /// `init` is a closure tasked with processing the message. It receives a reference to the
+ /// message in the message queue, and a [`SBufferIter`] pointing to its variable-length
+ /// payload, if any.
+ ///
+ /// The expected message is specified using the `M` generic parameter. If the pending message
+ /// is different, `EAGAIN` is returned and the unexpected message is dropped.
+ ///
+ /// This design is by no means final, but it is simple and will let us go through GSP
+ /// initialization.
+ ///
+ /// # Errors
+ ///
+ /// - `ETIMEDOUT` if `timeout` has elapsed before any message becomes available.
+ /// - `EIO` if there was some inconsistency (e.g. message shorter than advertised) on the
+ /// message queue.
+ /// - `EINVAL` if the function of the message was unrecognized.
+ pub(crate) fn receive_msg<M: MessageFromGsp>(&mut self, timeout: Delta) -> Result<M>
+ where
+ // This allows all error types, including `Infallible`, to be used for `M::InitError`.
+ Error: From<M::InitError>,
+ {
+ let message = self.wait_for_msg(timeout)?;
+ let function = message.header.function().map_err(|_| EINVAL)?;
+
+ // Extract the message. Store the result as we want to advance the read pointer even in
+ // case of failure.
+ let result = if function == M::FUNCTION {
+ let (cmd, contents_1) = M::Message::from_bytes_prefix(message.contents.0).ok_or(EIO)?;
+ let mut sbuffer = SBufferIter::new_reader([contents_1, message.contents.1]);
+
+ M::read(cmd, &mut sbuffer).map_err(|e| e.into())
+ } else {
+ Err(ERANGE)
+ };
+
+ // Advance the read pointer past this message.
+ self.gsp_mem.advance_cpu_read_ptr(u32::try_from(
+ message.header.length().div_ceil(GSP_PAGE_SIZE),
+ )?);
+
+ result
+ }
+
+ /// Returns the DMA handle of the command queue's shared memory region.
+ pub(crate) fn dma_handle(&self) -> DmaAddress {
+ self.gsp_mem.0.dma_handle()
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/commands.rs b/drivers/gpu/nova-core/gsp/commands.rs
new file mode 100644
index 000000000000..0425c65b5d6f
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/commands.rs
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use core::{
+ array,
+ convert::Infallible, //
+};
+
+use kernel::{
+ device,
+ pci,
+ prelude::*,
+ time::Delta,
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ }, //
+};
+
+use crate::{
+ driver::Bar0,
+ gsp::{
+ cmdq::{
+ Cmdq,
+ CommandToGsp,
+ MessageFromGsp, //
+ },
+ fw::{
+ commands::*,
+ MsgFunction, //
+ },
+ },
+ sbuffer::SBufferIter,
+ util,
+};
+
+/// The `GspSetSystemInfo` command.
+pub(crate) struct SetSystemInfo<'a> {
+ pdev: &'a pci::Device<device::Bound>,
+}
+
+impl<'a> SetSystemInfo<'a> {
+ /// Creates a new `GspSetSystemInfo` command using the parameters of `pdev`.
+ pub(crate) fn new(pdev: &'a pci::Device<device::Bound>) -> Self {
+ Self { pdev }
+ }
+}
+
+impl<'a> CommandToGsp for SetSystemInfo<'a> {
+ const FUNCTION: MsgFunction = MsgFunction::GspSetSystemInfo;
+ type Command = GspSetSystemInfo;
+ type InitError = Error;
+
+ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
+ GspSetSystemInfo::init(self.pdev)
+ }
+}
+
+struct RegistryEntry {
+ key: &'static str,
+ value: u32,
+}
+
+/// The `SetRegistry` command.
+pub(crate) struct SetRegistry {
+ entries: [RegistryEntry; Self::NUM_ENTRIES],
+}
+
+impl SetRegistry {
+ // For now we hard-code the registry entries. Future work will allow others to
+ // be added as module parameters.
+ const NUM_ENTRIES: usize = 3;
+
+ /// Creates a new `SetRegistry` command, using a set of hardcoded entries.
+ pub(crate) fn new() -> Self {
+ Self {
+ entries: [
+ // RMSecBusResetEnable - enables PCI secondary bus reset
+ RegistryEntry {
+ key: "RMSecBusResetEnable",
+ value: 1,
+ },
+ // RMForcePcieConfigSave - forces GSP-RM to preserve PCI configuration registers on
+ // any PCI reset.
+ RegistryEntry {
+ key: "RMForcePcieConfigSave",
+ value: 1,
+ },
+ // RMDevidCheckIgnore - allows GSP-RM to boot even if the PCI dev ID is not found
+ // in the internal product name database.
+ RegistryEntry {
+ key: "RMDevidCheckIgnore",
+ value: 1,
+ },
+ ],
+ }
+ }
+}
+
+impl CommandToGsp for SetRegistry {
+ const FUNCTION: MsgFunction = MsgFunction::SetRegistry;
+ type Command = PackedRegistryTable;
+ type InitError = Infallible;
+
+ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
+ PackedRegistryTable::init(Self::NUM_ENTRIES as u32, self.variable_payload_len() as u32)
+ }
+
+ fn variable_payload_len(&self) -> usize {
+ let mut key_size = 0;
+ for i in 0..Self::NUM_ENTRIES {
+ key_size += self.entries[i].key.len() + 1; // +1 for NULL terminator
+ }
+ Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>() + key_size
+ }
+
+ fn init_variable_payload(
+ &self,
+ dst: &mut SBufferIter<core::array::IntoIter<&mut [u8], 2>>,
+ ) -> Result {
+ let string_data_start_offset =
+ size_of::<PackedRegistryTable>() + Self::NUM_ENTRIES * size_of::<PackedRegistryEntry>();
+
+ // Array for string data.
+ let mut string_data = KVec::new();
+
+ for entry in self.entries.iter().take(Self::NUM_ENTRIES) {
+ dst.write_all(
+ PackedRegistryEntry::new(
+ (string_data_start_offset + string_data.len()) as u32,
+ entry.value,
+ )
+ .as_bytes(),
+ )?;
+
+ let key_bytes = entry.key.as_bytes();
+ string_data.extend_from_slice(key_bytes, GFP_KERNEL)?;
+ string_data.push(0, GFP_KERNEL)?;
+ }
+
+ dst.write_all(string_data.as_slice())
+ }
+}
+
+/// Message type for GSP initialization done notification.
+struct GspInitDone {}
+
+// SAFETY: `GspInitDone` is a zero-sized type with no bytes, therefore it
+// trivially has no uninitialized bytes.
+unsafe impl FromBytes for GspInitDone {}
+
+impl MessageFromGsp for GspInitDone {
+ const FUNCTION: MsgFunction = MsgFunction::GspInitDone;
+ type InitError = Infallible;
+ type Message = GspInitDone;
+
+ fn read(
+ _msg: &Self::Message,
+ _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
+ ) -> Result<Self, Self::InitError> {
+ Ok(GspInitDone {})
+ }
+}
+
+/// Waits for GSP initialization to complete.
+pub(crate) fn wait_gsp_init_done(cmdq: &mut Cmdq) -> Result {
+ loop {
+ match cmdq.receive_msg::<GspInitDone>(Delta::from_secs(10)) {
+ Ok(_) => break Ok(()),
+ Err(ERANGE) => continue,
+ Err(e) => break Err(e),
+ }
+ }
+}
+
+/// The `GetGspStaticInfo` command.
+struct GetGspStaticInfo;
+
+impl CommandToGsp for GetGspStaticInfo {
+ const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
+ type Command = GspStaticConfigInfo;
+ type InitError = Infallible;
+
+ fn init(&self) -> impl Init<Self::Command, Self::InitError> {
+ GspStaticConfigInfo::init_zeroed()
+ }
+}
+
+/// The reply from the GSP to the [`GetGspInfo`] command.
+pub(crate) struct GetGspStaticInfoReply {
+ gpu_name: [u8; 64],
+}
+
+impl MessageFromGsp for GetGspStaticInfoReply {
+ const FUNCTION: MsgFunction = MsgFunction::GetGspStaticInfo;
+ type Message = GspStaticConfigInfo;
+ type InitError = Infallible;
+
+ fn read(
+ msg: &Self::Message,
+ _sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
+ ) -> Result<Self, Self::InitError> {
+ Ok(GetGspStaticInfoReply {
+ gpu_name: msg.gpu_name_str(),
+ })
+ }
+}
+
+impl GetGspStaticInfoReply {
+ /// Returns the name of the GPU as a string, or `None` if the string given by the GSP was
+ /// invalid.
+ pub(crate) fn gpu_name(&self) -> Option<&str> {
+ util::str_from_null_terminated(&self.gpu_name)
+ }
+}
+
+/// Send the [`GetGspInfo`] command and awaits for its reply.
+pub(crate) fn get_gsp_info(cmdq: &mut Cmdq, bar: &Bar0) -> Result<GetGspStaticInfoReply> {
+ cmdq.send_command(bar, GetGspStaticInfo)?;
+
+ loop {
+ match cmdq.receive_msg::<GetGspStaticInfoReply>(Delta::from_secs(5)) {
+ Ok(info) => return Ok(info),
+ Err(ERANGE) => continue,
+ Err(e) => return Err(e),
+ }
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 34226dd00982..abffd6beec65 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -1,7 +1,928 @@
// SPDX-License-Identifier: GPL-2.0
+pub(crate) mod commands;
mod r570_144;
// Alias to avoid repeating the version number with every use.
-#[expect(unused)]
use r570_144 as bindings;
+
+use core::ops::Range;
+
+use kernel::{
+ dma::CoherentAllocation,
+ fmt,
+ prelude::*,
+ ptr::{
+ Alignable,
+ Alignment, //
+ },
+ sizes::{
+ SZ_128K,
+ SZ_1M, //
+ },
+ transmute::{
+ AsBytes,
+ FromBytes, //
+ },
+};
+
+use crate::{
+ fb::FbLayout,
+ firmware::gsp::GspFirmware,
+ gpu::Chipset,
+ gsp::{
+ cmdq::Cmdq, //
+ GSP_PAGE_SIZE,
+ },
+ num::{
+ self,
+ FromSafeCast, //
+ },
+};
+
+/// Empty type to group methods related to heap parameters for running the GSP firmware.
+enum GspFwHeapParams {}
+
+/// Minimum required alignment for the GSP heap.
+const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>();
+
+impl GspFwHeapParams {
+ /// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to
+ /// and including the first client subdevice allocation).
+ fn base_rm_size(_chipset: Chipset) -> u64 {
+ // TODO: this needs to be updated to return the correct value for Hopper+ once support for
+ // them is added:
+ // u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100)
+ u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X)
+ }
+
+ /// Returns the amount of heap memory required to support a single channel allocation.
+ fn client_alloc_size() -> u64 {
+ u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE)
+ .align_up(GSP_HEAP_ALIGNMENT)
+ .unwrap_or(u64::MAX)
+ }
+
+ /// Returns the amount of memory to reserve for management purposes for a framebuffer of size
+ /// `fb_size`.
+ fn management_overhead(fb_size: u64) -> u64 {
+ let fb_size_gb = fb_size.div_ceil(u64::from_safe_cast(kernel::sizes::SZ_1G));
+
+ u64::from(bindings::GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB)
+ .saturating_mul(fb_size_gb)
+ .align_up(GSP_HEAP_ALIGNMENT)
+ .unwrap_or(u64::MAX)
+ }
+}
+
+/// Heap memory requirements and constraints for a given version of the GSP LIBOS.
+pub(crate) struct LibosParams {
+ /// The base amount of heap required by the GSP operating system, in bytes.
+ carveout_size: u64,
+ /// The minimum and maximum sizes allowed for the GSP FW heap, in bytes.
+ allowed_heap_size: Range<u64>,
+}
+
+impl LibosParams {
+ /// Version 2 of the GSP LIBOS (Turing and GA100)
+ const LIBOS2: LibosParams = LibosParams {
+ carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2),
+ allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB)
+ * num::usize_as_u64(SZ_1M)
+ ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB)
+ * num::usize_as_u64(SZ_1M),
+ };
+
+ /// Version 3 of the GSP LIBOS (GA102+)
+ const LIBOS3: LibosParams = LibosParams {
+ carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
+ allowed_heap_size: num::u32_as_u64(
+ bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB,
+ ) * num::usize_as_u64(SZ_1M)
+ ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB)
+ * num::usize_as_u64(SZ_1M),
+ };
+
+ /// Returns the libos parameters corresponding to `chipset`.
+ pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams {
+ if chipset < Chipset::GA102 {
+ &Self::LIBOS2
+ } else {
+ &Self::LIBOS3
+ }
+ }
+
+ /// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size
+ /// of `fb_size` (in bytes) for `chipset`.
+ pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 {
+ // The WPR heap will contain the following:
+ // LIBOS carveout,
+ self.carveout_size
+ // RM boot working memory,
+ .saturating_add(GspFwHeapParams::base_rm_size(chipset))
+ // One RM client,
+ .saturating_add(GspFwHeapParams::client_alloc_size())
+ // Overhead for memory management.
+ .saturating_add(GspFwHeapParams::management_overhead(fb_size))
+ // Clamp to the supported heap sizes.
+ .clamp(self.allowed_heap_size.start, self.allowed_heap_size.end - 1)
+ }
+}
+
+/// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
+/// addresses of the GSP bootloader and firmware.
+#[repr(transparent)]
+pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta);
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for GspFwWprMeta {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspFwWprMeta {}
+
+type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
+type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1;
+
+impl GspFwWprMeta {
+ /// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the
+ /// `fb_layout` layout.
+ pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self {
+ Self(bindings::GspFwWprMeta {
+ // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified.
+ magic: r570_144::GSP_FW_WPR_META_MAGIC as u64,
+ revision: u64::from(r570_144::GSP_FW_WPR_META_REVISION),
+ sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(),
+ sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size),
+ sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(),
+ sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()),
+ bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset),
+ bootloaderDataOffset: u64::from(gsp_firmware.bootloader.data_offset),
+ bootloaderManifestOffset: u64::from(gsp_firmware.bootloader.manifest_offset),
+ __bindgen_anon_1: GspFwWprMetaBootResumeInfo {
+ __bindgen_anon_1: GspFwWprMetaBootInfo {
+ sysmemAddrOfSignature: gsp_firmware.signatures.dma_handle(),
+ sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()),
+ },
+ },
+ gspFwRsvdStart: fb_layout.heap.start,
+ nonWprHeapOffset: fb_layout.heap.start,
+ nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start,
+ gspFwWprStart: fb_layout.wpr2.start,
+ gspFwHeapOffset: fb_layout.wpr2_heap.start,
+ gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start,
+ gspFwOffset: fb_layout.elf.start,
+ bootBinOffset: fb_layout.boot.start,
+ frtsOffset: fb_layout.frts.start,
+ frtsSize: fb_layout.frts.end - fb_layout.frts.start,
+ gspFwWprEnd: fb_layout
+ .vga_workspace
+ .start
+ .align_down(Alignment::new::<SZ_128K>()),
+ gspFwHeapVfPartitionCount: fb_layout.vf_partition_count,
+ fbSize: fb_layout.fb.end - fb_layout.fb.start,
+ vgaWorkspaceOffset: fb_layout.vga_workspace.start,
+ vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start,
+ ..Default::default()
+ })
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(u32)]
+pub(crate) enum MsgFunction {
+ // Common function codes
+ Nop = bindings::NV_VGPU_MSG_FUNCTION_NOP,
+ SetGuestSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO,
+ AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT,
+ AllocDevice = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE,
+ AllocMemory = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY,
+ AllocCtxDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA,
+ AllocChannelDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA,
+ MapMemory = bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY,
+ BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA,
+ AllocObject = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT,
+ Free = bindings::NV_VGPU_MSG_FUNCTION_FREE,
+ Log = bindings::NV_VGPU_MSG_FUNCTION_LOG,
+ GetGspStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO,
+ SetRegistry = bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
+ GspSetSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
+ GspInitPostObjGpu = bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU,
+ GspRmControl = bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL,
+ GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO,
+
+ // Event codes
+ GspInitDone = bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE,
+ GspRunCpuSequencer = bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER,
+ PostEvent = bindings::NV_VGPU_MSG_EVENT_POST_EVENT,
+ RcTriggered = bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED,
+ MmuFaultQueued = bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED,
+ OsErrorLog = bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG,
+ GspPostNoCat = bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD,
+ GspLockdownNotice = bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE,
+ UcodeLibOsPrint = bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT,
+}
+
+impl fmt::Display for MsgFunction {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ // Common function codes
+ MsgFunction::Nop => write!(f, "NOP"),
+ MsgFunction::SetGuestSystemInfo => write!(f, "SET_GUEST_SYSTEM_INFO"),
+ MsgFunction::AllocRoot => write!(f, "ALLOC_ROOT"),
+ MsgFunction::AllocDevice => write!(f, "ALLOC_DEVICE"),
+ MsgFunction::AllocMemory => write!(f, "ALLOC_MEMORY"),
+ MsgFunction::AllocCtxDma => write!(f, "ALLOC_CTX_DMA"),
+ MsgFunction::AllocChannelDma => write!(f, "ALLOC_CHANNEL_DMA"),
+ MsgFunction::MapMemory => write!(f, "MAP_MEMORY"),
+ MsgFunction::BindCtxDma => write!(f, "BIND_CTX_DMA"),
+ MsgFunction::AllocObject => write!(f, "ALLOC_OBJECT"),
+ MsgFunction::Free => write!(f, "FREE"),
+ MsgFunction::Log => write!(f, "LOG"),
+ MsgFunction::GetGspStaticInfo => write!(f, "GET_GSP_STATIC_INFO"),
+ MsgFunction::SetRegistry => write!(f, "SET_REGISTRY"),
+ MsgFunction::GspSetSystemInfo => write!(f, "GSP_SET_SYSTEM_INFO"),
+ MsgFunction::GspInitPostObjGpu => write!(f, "GSP_INIT_POST_OBJGPU"),
+ MsgFunction::GspRmControl => write!(f, "GSP_RM_CONTROL"),
+ MsgFunction::GetStaticInfo => write!(f, "GET_STATIC_INFO"),
+
+ // Event codes
+ MsgFunction::GspInitDone => write!(f, "INIT_DONE"),
+ MsgFunction::GspRunCpuSequencer => write!(f, "RUN_CPU_SEQUENCER"),
+ MsgFunction::PostEvent => write!(f, "POST_EVENT"),
+ MsgFunction::RcTriggered => write!(f, "RC_TRIGGERED"),
+ MsgFunction::MmuFaultQueued => write!(f, "MMU_FAULT_QUEUED"),
+ MsgFunction::OsErrorLog => write!(f, "OS_ERROR_LOG"),
+ MsgFunction::GspPostNoCat => write!(f, "NOCAT"),
+ MsgFunction::GspLockdownNotice => write!(f, "LOCKDOWN_NOTICE"),
+ MsgFunction::UcodeLibOsPrint => write!(f, "LIBOS_PRINT"),
+ }
+ }
+}
+
+impl TryFrom<u32> for MsgFunction {
+ type Error = kernel::error::Error;
+
+ fn try_from(value: u32) -> Result<MsgFunction> {
+ match value {
+ bindings::NV_VGPU_MSG_FUNCTION_NOP => Ok(MsgFunction::Nop),
+ bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => {
+ Ok(MsgFunction::SetGuestSystemInfo)
+ }
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE => Ok(MsgFunction::AllocDevice),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY => Ok(MsgFunction::AllocMemory),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => Ok(MsgFunction::AllocCtxDma),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => Ok(MsgFunction::AllocChannelDma),
+ bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY => Ok(MsgFunction::MapMemory),
+ bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma),
+ bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => Ok(MsgFunction::AllocObject),
+ bindings::NV_VGPU_MSG_FUNCTION_FREE => Ok(MsgFunction::Free),
+ bindings::NV_VGPU_MSG_FUNCTION_LOG => Ok(MsgFunction::Log),
+ bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => Ok(MsgFunction::GetGspStaticInfo),
+ bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY => Ok(MsgFunction::SetRegistry),
+ bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => Ok(MsgFunction::GspSetSystemInfo),
+ bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU => {
+ Ok(MsgFunction::GspInitPostObjGpu)
+ }
+ bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL => Ok(MsgFunction::GspRmControl),
+ bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo),
+ bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE => Ok(MsgFunction::GspInitDone),
+ bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER => {
+ Ok(MsgFunction::GspRunCpuSequencer)
+ }
+ bindings::NV_VGPU_MSG_EVENT_POST_EVENT => Ok(MsgFunction::PostEvent),
+ bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED => Ok(MsgFunction::RcTriggered),
+ bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED => Ok(MsgFunction::MmuFaultQueued),
+ bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG => Ok(MsgFunction::OsErrorLog),
+ bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => Ok(MsgFunction::GspPostNoCat),
+ bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => Ok(MsgFunction::GspLockdownNotice),
+ bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT => Ok(MsgFunction::UcodeLibOsPrint),
+ _ => Err(EINVAL),
+ }
+ }
+}
+
+impl From<MsgFunction> for u32 {
+ fn from(value: MsgFunction) -> Self {
+ // CAST: `MsgFunction` is `repr(u32)` and can thus be cast losslessly.
+ value as u32
+ }
+}
+
+/// Sequencer buffer opcode for GSP sequencer commands.
+#[derive(Copy, Clone, Debug, PartialEq)]
+#[repr(u32)]
+pub(crate) enum SeqBufOpcode {
+ // Core operation opcodes
+ CoreReset = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET,
+ CoreResume = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME,
+ CoreStart = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START,
+ CoreWaitForHalt = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT,
+
+ // Delay opcode
+ DelayUs = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US,
+
+ // Register operation opcodes
+ RegModify = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY,
+ RegPoll = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL,
+ RegStore = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE,
+ RegWrite = r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE,
+}
+
+impl fmt::Display for SeqBufOpcode {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ SeqBufOpcode::CoreReset => write!(f, "CORE_RESET"),
+ SeqBufOpcode::CoreResume => write!(f, "CORE_RESUME"),
+ SeqBufOpcode::CoreStart => write!(f, "CORE_START"),
+ SeqBufOpcode::CoreWaitForHalt => write!(f, "CORE_WAIT_FOR_HALT"),
+ SeqBufOpcode::DelayUs => write!(f, "DELAY_US"),
+ SeqBufOpcode::RegModify => write!(f, "REG_MODIFY"),
+ SeqBufOpcode::RegPoll => write!(f, "REG_POLL"),
+ SeqBufOpcode::RegStore => write!(f, "REG_STORE"),
+ SeqBufOpcode::RegWrite => write!(f, "REG_WRITE"),
+ }
+ }
+}
+
+impl TryFrom<u32> for SeqBufOpcode {
+ type Error = kernel::error::Error;
+
+ fn try_from(value: u32) -> Result<SeqBufOpcode> {
+ match value {
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET => {
+ Ok(SeqBufOpcode::CoreReset)
+ }
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME => {
+ Ok(SeqBufOpcode::CoreResume)
+ }
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START => {
+ Ok(SeqBufOpcode::CoreStart)
+ }
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT => {
+ Ok(SeqBufOpcode::CoreWaitForHalt)
+ }
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US => Ok(SeqBufOpcode::DelayUs),
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY => {
+ Ok(SeqBufOpcode::RegModify)
+ }
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL => Ok(SeqBufOpcode::RegPoll),
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE => Ok(SeqBufOpcode::RegStore),
+ r570_144::GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE => Ok(SeqBufOpcode::RegWrite),
+ _ => Err(EINVAL),
+ }
+ }
+}
+
+impl From<SeqBufOpcode> for u32 {
+ fn from(value: SeqBufOpcode) -> Self {
+ // CAST: `SeqBufOpcode` is `repr(u32)` and can thus be cast losslessly.
+ value as u32
+ }
+}
+
+/// Wrapper for GSP sequencer register write payload.
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+pub(crate) struct RegWritePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_WRITE);
+
+impl RegWritePayload {
+ /// Returns the register address.
+ pub(crate) fn addr(&self) -> u32 {
+ self.0.addr
+ }
+
+ /// Returns the value to write.
+ pub(crate) fn val(&self) -> u32 {
+ self.0.val
+ }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for RegWritePayload {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for RegWritePayload {}
+
+/// Wrapper for GSP sequencer register modify payload.
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+pub(crate) struct RegModifyPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_MODIFY);
+
+impl RegModifyPayload {
+ /// Returns the register address.
+ pub(crate) fn addr(&self) -> u32 {
+ self.0.addr
+ }
+
+ /// Returns the mask to apply.
+ pub(crate) fn mask(&self) -> u32 {
+ self.0.mask
+ }
+
+ /// Returns the value to write.
+ pub(crate) fn val(&self) -> u32 {
+ self.0.val
+ }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for RegModifyPayload {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for RegModifyPayload {}
+
+/// Wrapper for GSP sequencer register poll payload.
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+pub(crate) struct RegPollPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_POLL);
+
+impl RegPollPayload {
+ /// Returns the register address.
+ pub(crate) fn addr(&self) -> u32 {
+ self.0.addr
+ }
+
+ /// Returns the mask to apply.
+ pub(crate) fn mask(&self) -> u32 {
+ self.0.mask
+ }
+
+ /// Returns the expected value.
+ pub(crate) fn val(&self) -> u32 {
+ self.0.val
+ }
+
+ /// Returns the timeout in microseconds.
+ pub(crate) fn timeout(&self) -> u32 {
+ self.0.timeout
+ }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for RegPollPayload {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for RegPollPayload {}
+
+/// Wrapper for GSP sequencer delay payload.
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+pub(crate) struct DelayUsPayload(r570_144::GSP_SEQ_BUF_PAYLOAD_DELAY_US);
+
+impl DelayUsPayload {
+ /// Returns the delay value in microseconds.
+ pub(crate) fn val(&self) -> u32 {
+ self.0.val
+ }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for DelayUsPayload {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for DelayUsPayload {}
+
+/// Wrapper for GSP sequencer register store payload.
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+pub(crate) struct RegStorePayload(r570_144::GSP_SEQ_BUF_PAYLOAD_REG_STORE);
+
+impl RegStorePayload {
+ /// Returns the register address.
+ pub(crate) fn addr(&self) -> u32 {
+ self.0.addr
+ }
+
+ /// Returns the storage index.
+ #[allow(unused)]
+ pub(crate) fn index(&self) -> u32 {
+ self.0.index
+ }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for RegStorePayload {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for RegStorePayload {}
+
+/// Wrapper for GSP sequencer buffer command.
+#[repr(transparent)]
+pub(crate) struct SequencerBufferCmd(r570_144::GSP_SEQUENCER_BUFFER_CMD);
+
+impl SequencerBufferCmd {
+ /// Returns the opcode as a `SeqBufOpcode` enum, or error if invalid.
+ pub(crate) fn opcode(&self) -> Result<SeqBufOpcode> {
+ self.0.opCode.try_into()
+ }
+
+ /// Returns the register write payload by value.
+ ///
+ /// Returns an error if the opcode is not `SeqBufOpcode::RegWrite`.
+ pub(crate) fn reg_write_payload(&self) -> Result<RegWritePayload> {
+ if self.opcode()? != SeqBufOpcode::RegWrite {
+ return Err(EINVAL);
+ }
+ // SAFETY: Opcode is verified to be `RegWrite`, so union contains valid `RegWritePayload`.
+ let payload_bytes = unsafe {
+ core::slice::from_raw_parts(
+ core::ptr::addr_of!(self.0.payload.regWrite).cast::<u8>(),
+ core::mem::size_of::<RegWritePayload>(),
+ )
+ };
+ Ok(*RegWritePayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
+ }
+
+ /// Returns the register modify payload by value.
+ ///
+ /// Returns an error if the opcode is not `SeqBufOpcode::RegModify`.
+ pub(crate) fn reg_modify_payload(&self) -> Result<RegModifyPayload> {
+ if self.opcode()? != SeqBufOpcode::RegModify {
+ return Err(EINVAL);
+ }
+ // SAFETY: Opcode is verified to be `RegModify`, so union contains valid `RegModifyPayload`.
+ let payload_bytes = unsafe {
+ core::slice::from_raw_parts(
+ core::ptr::addr_of!(self.0.payload.regModify).cast::<u8>(),
+ core::mem::size_of::<RegModifyPayload>(),
+ )
+ };
+ Ok(*RegModifyPayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
+ }
+
+ /// Returns the register poll payload by value.
+ ///
+ /// Returns an error if the opcode is not `SeqBufOpcode::RegPoll`.
+ pub(crate) fn reg_poll_payload(&self) -> Result<RegPollPayload> {
+ if self.opcode()? != SeqBufOpcode::RegPoll {
+ return Err(EINVAL);
+ }
+ // SAFETY: Opcode is verified to be `RegPoll`, so union contains valid `RegPollPayload`.
+ let payload_bytes = unsafe {
+ core::slice::from_raw_parts(
+ core::ptr::addr_of!(self.0.payload.regPoll).cast::<u8>(),
+ core::mem::size_of::<RegPollPayload>(),
+ )
+ };
+ Ok(*RegPollPayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
+ }
+
+ /// Returns the delay payload by value.
+ ///
+ /// Returns an error if the opcode is not `SeqBufOpcode::DelayUs`.
+ pub(crate) fn delay_us_payload(&self) -> Result<DelayUsPayload> {
+ if self.opcode()? != SeqBufOpcode::DelayUs {
+ return Err(EINVAL);
+ }
+ // SAFETY: Opcode is verified to be `DelayUs`, so union contains valid `DelayUsPayload`.
+ let payload_bytes = unsafe {
+ core::slice::from_raw_parts(
+ core::ptr::addr_of!(self.0.payload.delayUs).cast::<u8>(),
+ core::mem::size_of::<DelayUsPayload>(),
+ )
+ };
+ Ok(*DelayUsPayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
+ }
+
+ /// Returns the register store payload by value.
+ ///
+ /// Returns an error if the opcode is not `SeqBufOpcode::RegStore`.
+ pub(crate) fn reg_store_payload(&self) -> Result<RegStorePayload> {
+ if self.opcode()? != SeqBufOpcode::RegStore {
+ return Err(EINVAL);
+ }
+ // SAFETY: Opcode is verified to be `RegStore`, so union contains valid `RegStorePayload`.
+ let payload_bytes = unsafe {
+ core::slice::from_raw_parts(
+ core::ptr::addr_of!(self.0.payload.regStore).cast::<u8>(),
+ core::mem::size_of::<RegStorePayload>(),
+ )
+ };
+ Ok(*RegStorePayload::from_bytes(payload_bytes).ok_or(EINVAL)?)
+ }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for SequencerBufferCmd {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for SequencerBufferCmd {}
+
+/// Wrapper for GSP run CPU sequencer RPC.
+#[repr(transparent)]
+pub(crate) struct RunCpuSequencer(r570_144::rpc_run_cpu_sequencer_v17_00);
+
+impl RunCpuSequencer {
+ /// Returns the command index.
+ pub(crate) fn cmd_index(&self) -> u32 {
+ self.0.cmdIndex
+ }
+}
+
+// SAFETY: This struct only contains integer types for which all bit patterns are valid.
+unsafe impl FromBytes for RunCpuSequencer {}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for RunCpuSequencer {}
+
+/// Struct containing the arguments required to pass a memory buffer to the GSP
+/// for use during initialisation.
+///
+/// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is
+/// configured for a larger page size (e.g. 64K pages), we need to give
+/// the GSP an array of 4K pages. Since we only create physically contiguous
+/// buffers the math to calculate the addresses is simple.
+///
+/// The buffers must be a multiple of GSP_PAGE_SIZE. GSP-RM also currently
+/// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the
+/// buffers to be physically contiguous anyway.
+///
+/// The memory allocated for the arguments must remain until the GSP sends the
+/// init_done RPC.
+#[repr(transparent)]
+pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument);
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
+
+impl LibosMemoryRegionInitArgument {
+ pub(crate) fn new<A: AsBytes + FromBytes>(
+ name: &'static str,
+ obj: &CoherentAllocation<A>,
+ ) -> Self {
+ /// Generates the `ID8` identifier required for some GSP objects.
+ fn id8(name: &str) -> u64 {
+ let mut bytes = [0u8; core::mem::size_of::<u64>()];
+
+ for (c, b) in name.bytes().rev().zip(&mut bytes) {
+ *b = c;
+ }
+
+ u64::from_ne_bytes(bytes)
+ }
+
+ Self(bindings::LibosMemoryRegionInitArgument {
+ id8: id8(name),
+ pa: obj.dma_handle(),
+ size: num::usize_as_u64(obj.size()),
+ kind: num::u32_into_u8::<
+ { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS },
+ >(),
+ loc: num::u32_into_u8::<
+ { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM },
+ >(),
+ ..Default::default()
+ })
+ }
+}
+
+/// TX header for setting up a message queue with the GSP.
+#[repr(transparent)]
+pub(crate) struct MsgqTxHeader(bindings::msgqTxHeader);
+
+impl MsgqTxHeader {
+ /// Create a new TX queue header.
+ ///
+ /// # Arguments
+ ///
+ /// * `msgq_size` - Total size of the message queue structure, in bytes.
+ /// * `rx_hdr_offset` - Offset, in bytes, of the start of the RX header in the message queue
+ /// structure.
+ /// * `msg_count` - Number of messages that can be sent, i.e. the number of memory pages
+ /// allocated for the message queue in the message queue structure.
+ pub(crate) fn new(msgq_size: u32, rx_hdr_offset: u32, msg_count: u32) -> Self {
+ Self(bindings::msgqTxHeader {
+ version: 0,
+ size: msgq_size,
+ msgSize: num::usize_into_u32::<GSP_PAGE_SIZE>(),
+ msgCount: msg_count,
+ writePtr: 0,
+ flags: 1,
+ rxHdrOff: rx_hdr_offset,
+ entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
+ })
+ }
+
+ /// Returns the value of the write pointer for this queue.
+ pub(crate) fn write_ptr(&self) -> u32 {
+ let ptr = core::ptr::from_ref(&self.0.writePtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.read_volatile() }
+ }
+
+ /// Sets the value of the write pointer for this queue.
+ pub(crate) fn set_write_ptr(&mut self, val: u32) {
+ let ptr = core::ptr::from_mut(&mut self.0.writePtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.write_volatile(val) }
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for MsgqTxHeader {}
+
+/// RX header for setting up a message queue with the GSP.
+#[repr(transparent)]
+pub(crate) struct MsgqRxHeader(bindings::msgqRxHeader);
+
+/// Header for the message RX queue.
+impl MsgqRxHeader {
+ /// Creates a new RX queue header.
+ pub(crate) fn new() -> Self {
+ Self(Default::default())
+ }
+
+ /// Returns the value of the read pointer for this queue.
+ pub(crate) fn read_ptr(&self) -> u32 {
+ let ptr = core::ptr::from_ref(&self.0.readPtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.read_volatile() }
+ }
+
+ /// Sets the value of the read pointer for this queue.
+ pub(crate) fn set_read_ptr(&mut self, val: u32) {
+ let ptr = core::ptr::from_mut(&mut self.0.readPtr);
+
+ // SAFETY: `ptr` is a valid pointer to a `u32`.
+ unsafe { ptr.write_volatile(val) }
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for MsgqRxHeader {}
+
+bitfield! {
+ struct MsgHeaderVersion(u32) {
+ 31:24 major as u8;
+ 23:16 minor as u8;
+ }
+}
+
+impl MsgHeaderVersion {
+ const MAJOR_TOT: u8 = 3;
+ const MINOR_TOT: u8 = 0;
+
+ fn new() -> Self {
+ Self::default()
+ .set_major(Self::MAJOR_TOT)
+ .set_minor(Self::MINOR_TOT)
+ }
+}
+
+impl bindings::rpc_message_header_v {
+ fn init(cmd_size: usize, function: MsgFunction) -> impl Init<Self, Error> {
+ type RpcMessageHeader = bindings::rpc_message_header_v;
+
+ try_init!(RpcMessageHeader {
+ header_version: MsgHeaderVersion::new().into(),
+ signature: bindings::NV_VGPU_MSG_SIGNATURE_VALID,
+ function: function.into(),
+ length: size_of::<Self>()
+ .checked_add(cmd_size)
+ .ok_or(EOVERFLOW)
+ .and_then(|v| v.try_into().map_err(|_| EINVAL))?,
+ rpc_result: 0xffffffff,
+ rpc_result_private: 0xffffffff,
+ ..Zeroable::init_zeroed()
+ })
+ }
+}
+
+// SAFETY: We can't derive the Zeroable trait for this binding because the
+// procedural macro doesn't support the syntax used by bindgen to create the
+// __IncompleteArrayField types. So instead we implement it here, which is safe
+// because these are explicitly padded structures only containing types for
+// which any bit pattern, including all zeros, is valid.
+unsafe impl Zeroable for bindings::rpc_message_header_v {}
+
+/// GSP Message Element.
+///
+/// This is essentially a message header expected to be followed by the message data.
+#[repr(transparent)]
+pub(crate) struct GspMsgElement {
+ inner: bindings::GSP_MSG_QUEUE_ELEMENT,
+}
+
+impl GspMsgElement {
+ /// Creates a new message element.
+ ///
+ /// # Arguments
+ ///
+ /// * `sequence` - Sequence number of the message.
+ /// * `cmd_size` - Size of the command (not including the message element), in bytes.
+ /// * `function` - Function of the message.
+ #[allow(non_snake_case)]
+ pub(crate) fn init(
+ sequence: u32,
+ cmd_size: usize,
+ function: MsgFunction,
+ ) -> impl Init<Self, Error> {
+ type RpcMessageHeader = bindings::rpc_message_header_v;
+ type InnerGspMsgElement = bindings::GSP_MSG_QUEUE_ELEMENT;
+ let init_inner = try_init!(InnerGspMsgElement {
+ seqNum: sequence,
+ elemCount: size_of::<Self>()
+ .checked_add(cmd_size)
+ .ok_or(EOVERFLOW)?
+ .div_ceil(GSP_PAGE_SIZE)
+ .try_into()
+ .map_err(|_| EOVERFLOW)?,
+ rpc <- RpcMessageHeader::init(cmd_size, function),
+ ..Zeroable::init_zeroed()
+ });
+
+ try_init!(GspMsgElement {
+ inner <- init_inner,
+ })
+ }
+
+ /// Sets the checksum of this message.
+ ///
+ /// Since the header is also part of the checksum, this is usually called after the whole
+ /// message has been written to the shared memory area.
+ pub(crate) fn set_checksum(&mut self, checksum: u32) {
+ self.inner.checkSum = checksum;
+ }
+
+ /// Returns the total length of the message.
+ pub(crate) fn length(&self) -> usize {
+ // `rpc.length` includes the length of the GspRpcHeader but not the message header.
+ size_of::<Self>() - size_of::<bindings::rpc_message_header_v>()
+ + num::u32_as_usize(self.inner.rpc.length)
+ }
+
+ // Returns the sequence number of the message.
+ pub(crate) fn sequence(&self) -> u32 {
+ self.inner.rpc.sequence
+ }
+
+ // Returns the function of the message, if it is valid, or the invalid function number as an
+ // error.
+ pub(crate) fn function(&self) -> Result<MsgFunction, u32> {
+ self.inner
+ .rpc
+ .function
+ .try_into()
+ .map_err(|_| self.inner.rpc.function)
+ }
+
+ // Returns the number of elements (i.e. memory pages) used by this message.
+ pub(crate) fn element_count(&self) -> u32 {
+ self.inner.elemCount
+ }
+}
+
+// SAFETY: Padding is explicit and does not contain uninitialized data.
+unsafe impl AsBytes for GspMsgElement {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspMsgElement {}
+
+/// Arguments for GSP startup.
+#[repr(transparent)]
+pub(crate) struct GspArgumentsCached(bindings::GSP_ARGUMENTS_CACHED);
+
+impl GspArgumentsCached {
+ /// Creates the arguments for starting the GSP up using `cmdq` as its command queue.
+ pub(crate) fn new(cmdq: &Cmdq) -> Self {
+ Self(bindings::GSP_ARGUMENTS_CACHED {
+ messageQueueInitArguments: MessageQueueInitArguments::new(cmdq).0,
+ bDmemStack: 1,
+ ..Default::default()
+ })
+ }
+}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for GspArgumentsCached {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspArgumentsCached {}
+
+/// Init arguments for the message queue.
+#[repr(transparent)]
+struct MessageQueueInitArguments(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS);
+
+impl MessageQueueInitArguments {
+ /// Creates a new init arguments structure for `cmdq`.
+ fn new(cmdq: &Cmdq) -> Self {
+ Self(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS {
+ sharedMemPhysAddr: cmdq.dma_handle(),
+ pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(),
+ cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET),
+ statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET),
+ ..Default::default()
+ })
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-core/gsp/fw/commands.rs
new file mode 100644
index 000000000000..21be44199693
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/fw/commands.rs
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use kernel::prelude::*;
+use kernel::transmute::{AsBytes, FromBytes};
+use kernel::{device, pci};
+
+use crate::gsp::GSP_PAGE_SIZE;
+
+use super::bindings;
+
+/// Payload of the `GspSetSystemInfo` command.
+#[repr(transparent)]
+pub(crate) struct GspSetSystemInfo {
+ inner: bindings::GspSystemInfo,
+}
+static_assert!(size_of::<GspSetSystemInfo>() < GSP_PAGE_SIZE);
+
+impl GspSetSystemInfo {
+ /// Returns an in-place initializer for the `GspSetSystemInfo` command.
+ #[allow(non_snake_case)]
+ pub(crate) fn init<'a>(dev: &'a pci::Device<device::Bound>) -> impl Init<Self, Error> + 'a {
+ type InnerGspSystemInfo = bindings::GspSystemInfo;
+ let init_inner = try_init!(InnerGspSystemInfo {
+ gpuPhysAddr: dev.resource_start(0)?,
+ gpuPhysFbAddr: dev.resource_start(1)?,
+ gpuPhysInstAddr: dev.resource_start(3)?,
+ nvDomainBusDeviceFunc: u64::from(dev.dev_id()),
+
+ // Using TASK_SIZE in r535_gsp_rpc_set_system_info() seems wrong because
+ // TASK_SIZE is per-task. That's probably a design issue in GSP-RM though.
+ maxUserVa: (1 << 47) - 4096,
+ pciConfigMirrorBase: 0x088000,
+ pciConfigMirrorSize: 0x001000,
+
+ PCIDeviceID: (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id().as_raw()),
+ PCISubDeviceID: (u32::from(dev.subsystem_device_id()) << 16)
+ | u32::from(dev.subsystem_vendor_id()),
+ PCIRevisionID: u32::from(dev.revision_id()),
+ bIsPrimary: 0,
+ bPreserveVideoMemoryAllocations: 0,
+ ..Zeroable::init_zeroed()
+ });
+
+ try_init!(GspSetSystemInfo {
+ inner <- init_inner,
+ })
+ }
+}
+
+// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
+// that is not a problem because they are not used outside the kernel.
+unsafe impl AsBytes for GspSetSystemInfo {}
+
+// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
+// that is not a problem because they are not used outside the kernel.
+unsafe impl FromBytes for GspSetSystemInfo {}
+
+#[repr(transparent)]
+pub(crate) struct PackedRegistryEntry(bindings::PACKED_REGISTRY_ENTRY);
+
+impl PackedRegistryEntry {
+ pub(crate) fn new(offset: u32, value: u32) -> Self {
+ Self({
+ bindings::PACKED_REGISTRY_ENTRY {
+ nameOffset: offset,
+
+ // We only support DWORD types for now. Support for other types
+ // will come later if required.
+ type_: bindings::REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8,
+ __bindgen_padding_0: Default::default(),
+ data: value,
+ length: 0,
+ }
+ })
+ }
+}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for PackedRegistryEntry {}
+
+/// Payload of the `SetRegistry` command.
+#[repr(transparent)]
+pub(crate) struct PackedRegistryTable {
+ inner: bindings::PACKED_REGISTRY_TABLE,
+}
+
+impl PackedRegistryTable {
+ #[allow(non_snake_case)]
+ pub(crate) fn init(num_entries: u32, size: u32) -> impl Init<Self> {
+ type InnerPackedRegistryTable = bindings::PACKED_REGISTRY_TABLE;
+ let init_inner = init!(InnerPackedRegistryTable {
+ numEntries: num_entries,
+ size,
+ entries: Default::default()
+ });
+
+ init!(PackedRegistryTable { inner <- init_inner })
+ }
+}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for PackedRegistryTable {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for PackedRegistryTable {}
+
+/// Payload of the `GetGspStaticInfo` command and message.
+#[repr(transparent)]
+pub(crate) struct GspStaticConfigInfo(bindings::GspStaticConfigInfo_t);
+
+impl GspStaticConfigInfo {
+ /// Returns a bytes array containing the (hopefully) zero-terminated name of this GPU.
+ pub(crate) fn gpu_name_str(&self) -> [u8; 64] {
+ self.0.gpuNameString
+ }
+}
+
+// SAFETY: Padding is explicit and will not contain uninitialized data.
+unsafe impl AsBytes for GspStaticConfigInfo {}
+
+// SAFETY: This struct only contains integer types for which all bit patterns
+// are valid.
+unsafe impl FromBytes for GspStaticConfigInfo {}
+
+// SAFETY: This struct only contains integer types and fixed-size arrays for which
+// all bit patterns are valid.
+unsafe impl Zeroable for GspStaticConfigInfo {}
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144.rs b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
index 35cb0370a7c9..048234d1a9d1 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144.rs
@@ -12,7 +12,6 @@
#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]
#![allow(
dead_code,
- unused_imports,
clippy::all,
clippy::undocumented_unsafe_blocks,
clippy::ptr_as_ptr,
@@ -25,5 +24,8 @@
unreachable_pub,
unsafe_op_in_unsafe_fn
)]
-use kernel::ffi;
+use kernel::{
+ ffi,
+ prelude::Zeroable, //
+};
include!("r570_144/bindings.rs");
diff --git a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
index cec594032515..5bcfbcd1ad22 100644
--- a/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
+++ b/drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
@@ -1 +1,951 @@
// SPDX-License-Identifier: GPL-2.0
+
+#[repr(C)]
+#[derive(Default)]
+pub struct __IncompleteArrayField<T>(::core::marker::PhantomData<T>, [T; 0]);
+impl<T> __IncompleteArrayField<T> {
+ #[inline]
+ pub const fn new() -> Self {
+ __IncompleteArrayField(::core::marker::PhantomData, [])
+ }
+ #[inline]
+ pub fn as_ptr(&self) -> *const T {
+ self as *const _ as *const T
+ }
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut T {
+ self as *mut _ as *mut T
+ }
+ #[inline]
+ pub unsafe fn as_slice(&self, len: usize) -> &[T] {
+ ::core::slice::from_raw_parts(self.as_ptr(), len)
+ }
+ #[inline]
+ pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
+ ::core::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
+ }
+}
+impl<T> ::core::fmt::Debug for __IncompleteArrayField<T> {
+ fn fmt(&self, fmt: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
+ fmt.write_str("__IncompleteArrayField")
+ }
+}
+pub const NV_VGPU_MSG_SIGNATURE_VALID: u32 = 1129337430;
+pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2: u32 = 0;
+pub const GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL: u32 = 23068672;
+pub const GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X: u32 = 8388608;
+pub const GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB: u32 = 98304;
+pub const GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE: u32 = 100663296;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB: u32 = 64;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB: u32 = 256;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB: u32 = 88;
+pub const GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB: u32 = 280;
+pub const GSP_FW_WPR_META_REVISION: u32 = 1;
+pub const GSP_FW_WPR_META_MAGIC: i64 = -2577556379034558285;
+pub const REGISTRY_TABLE_ENTRY_TYPE_DWORD: u32 = 1;
+pub type __u8 = ffi::c_uchar;
+pub type __u16 = ffi::c_ushort;
+pub type __u32 = ffi::c_uint;
+pub type __u64 = ffi::c_ulonglong;
+pub type u8_ = __u8;
+pub type u16_ = __u16;
+pub type u32_ = __u32;
+pub type u64_ = __u64;
+pub const NV_VGPU_MSG_FUNCTION_NOP: _bindgen_ty_2 = 0;
+pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO: _bindgen_ty_2 = 1;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_ROOT: _bindgen_ty_2 = 2;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE: _bindgen_ty_2 = 3;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY: _bindgen_ty_2 = 4;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA: _bindgen_ty_2 = 5;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA: _bindgen_ty_2 = 6;
+pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY: _bindgen_ty_2 = 7;
+pub const NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA: _bindgen_ty_2 = 8;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT: _bindgen_ty_2 = 9;
+pub const NV_VGPU_MSG_FUNCTION_FREE: _bindgen_ty_2 = 10;
+pub const NV_VGPU_MSG_FUNCTION_LOG: _bindgen_ty_2 = 11;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIDMEM: _bindgen_ty_2 = 12;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY: _bindgen_ty_2 = 13;
+pub const NV_VGPU_MSG_FUNCTION_MAP_MEMORY_DMA: _bindgen_ty_2 = 14;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_MEMORY_DMA: _bindgen_ty_2 = 15;
+pub const NV_VGPU_MSG_FUNCTION_GET_EDID: _bindgen_ty_2 = 16;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_CHANNEL: _bindgen_ty_2 = 17;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DISP_OBJECT: _bindgen_ty_2 = 18;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_SUBDEVICE: _bindgen_ty_2 = 19;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_DYNAMIC_MEMORY: _bindgen_ty_2 = 20;
+pub const NV_VGPU_MSG_FUNCTION_DUP_OBJECT: _bindgen_ty_2 = 21;
+pub const NV_VGPU_MSG_FUNCTION_IDLE_CHANNELS: _bindgen_ty_2 = 22;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_EVENT: _bindgen_ty_2 = 23;
+pub const NV_VGPU_MSG_FUNCTION_SEND_EVENT: _bindgen_ty_2 = 24;
+pub const NV_VGPU_MSG_FUNCTION_REMAPPER_CONTROL: _bindgen_ty_2 = 25;
+pub const NV_VGPU_MSG_FUNCTION_DMA_CONTROL: _bindgen_ty_2 = 26;
+pub const NV_VGPU_MSG_FUNCTION_DMA_FILL_PTE_MEM: _bindgen_ty_2 = 27;
+pub const NV_VGPU_MSG_FUNCTION_MANAGE_HW_RESOURCE: _bindgen_ty_2 = 28;
+pub const NV_VGPU_MSG_FUNCTION_BIND_ARBITRARY_CTX_DMA: _bindgen_ty_2 = 29;
+pub const NV_VGPU_MSG_FUNCTION_CREATE_FB_SEGMENT: _bindgen_ty_2 = 30;
+pub const NV_VGPU_MSG_FUNCTION_DESTROY_FB_SEGMENT: _bindgen_ty_2 = 31;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_SHARE_DEVICE: _bindgen_ty_2 = 32;
+pub const NV_VGPU_MSG_FUNCTION_DEFERRED_API_CONTROL: _bindgen_ty_2 = 33;
+pub const NV_VGPU_MSG_FUNCTION_REMOVE_DEFERRED_API: _bindgen_ty_2 = 34;
+pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_READ: _bindgen_ty_2 = 35;
+pub const NV_VGPU_MSG_FUNCTION_SIM_ESCAPE_WRITE: _bindgen_ty_2 = 36;
+pub const NV_VGPU_MSG_FUNCTION_SIM_MANAGE_DISPLAY_CONTEXT_DMA: _bindgen_ty_2 = 37;
+pub const NV_VGPU_MSG_FUNCTION_FREE_VIDMEM_VIRT: _bindgen_ty_2 = 38;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PSTATE_INFO: _bindgen_ty_2 = 39;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_PERFMON_SAMPLE: _bindgen_ty_2 = 40;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_VIRTUAL_PSTATE_INFO: _bindgen_ty_2 = 41;
+pub const NV_VGPU_MSG_FUNCTION_PERF_GET_LEVEL_INFO: _bindgen_ty_2 = 42;
+pub const NV_VGPU_MSG_FUNCTION_MAP_SEMA_MEMORY: _bindgen_ty_2 = 43;
+pub const NV_VGPU_MSG_FUNCTION_UNMAP_SEMA_MEMORY: _bindgen_ty_2 = 44;
+pub const NV_VGPU_MSG_FUNCTION_SET_SURFACE_PROPERTIES: _bindgen_ty_2 = 45;
+pub const NV_VGPU_MSG_FUNCTION_CLEANUP_SURFACE: _bindgen_ty_2 = 46;
+pub const NV_VGPU_MSG_FUNCTION_UNLOADING_GUEST_DRIVER: _bindgen_ty_2 = 47;
+pub const NV_VGPU_MSG_FUNCTION_TDR_SET_TIMEOUT_STATE: _bindgen_ty_2 = 48;
+pub const NV_VGPU_MSG_FUNCTION_SWITCH_TO_VGA: _bindgen_ty_2 = 49;
+pub const NV_VGPU_MSG_FUNCTION_GPU_EXEC_REG_OPS: _bindgen_ty_2 = 50;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO: _bindgen_ty_2 = 51;
+pub const NV_VGPU_MSG_FUNCTION_ALLOC_VIRTMEM: _bindgen_ty_2 = 52;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_PDE_2: _bindgen_ty_2 = 53;
+pub const NV_VGPU_MSG_FUNCTION_SET_PAGE_DIRECTORY: _bindgen_ty_2 = 54;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_PSTATE_INFO: _bindgen_ty_2 = 55;
+pub const NV_VGPU_MSG_FUNCTION_TRANSLATE_GUEST_GPU_PTES: _bindgen_ty_2 = 56;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_57: _bindgen_ty_2 = 57;
+pub const NV_VGPU_MSG_FUNCTION_RESET_CURRENT_GR_CONTEXT: _bindgen_ty_2 = 58;
+pub const NV_VGPU_MSG_FUNCTION_SET_SEMA_MEM_VALIDATION_STATE: _bindgen_ty_2 = 59;
+pub const NV_VGPU_MSG_FUNCTION_GET_ENGINE_UTILIZATION: _bindgen_ty_2 = 60;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPU_PDES: _bindgen_ty_2 = 61;
+pub const NV_VGPU_MSG_FUNCTION_GET_ENCODER_CAPACITY: _bindgen_ty_2 = 62;
+pub const NV_VGPU_MSG_FUNCTION_VGPU_PF_REG_READ32: _bindgen_ty_2 = 63;
+pub const NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO_EXT: _bindgen_ty_2 = 64;
+pub const NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO: _bindgen_ty_2 = 65;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_INIT: _bindgen_ty_2 = 66;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_CLOSE_QUEUE: _bindgen_ty_2 = 67;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_CLEANUP: _bindgen_ty_2 = 68;
+pub const NV_VGPU_MSG_FUNCTION_RMFS_TEST: _bindgen_ty_2 = 69;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_BAR_PDE: _bindgen_ty_2 = 70;
+pub const NV_VGPU_MSG_FUNCTION_CONTINUATION_RECORD: _bindgen_ty_2 = 71;
+pub const NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO: _bindgen_ty_2 = 72;
+pub const NV_VGPU_MSG_FUNCTION_SET_REGISTRY: _bindgen_ty_2 = 73;
+pub const NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU: _bindgen_ty_2 = 74;
+pub const NV_VGPU_MSG_FUNCTION_SUBDEV_EVENT_SET_NOTIFICATION: _bindgen_ty_2 = 75;
+pub const NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL: _bindgen_ty_2 = 76;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO2: _bindgen_ty_2 = 77;
+pub const NV_VGPU_MSG_FUNCTION_DUMP_PROTOBUF_COMPONENT: _bindgen_ty_2 = 78;
+pub const NV_VGPU_MSG_FUNCTION_UNSET_PAGE_DIRECTORY: _bindgen_ty_2 = 79;
+pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_STATIC_INFO: _bindgen_ty_2 = 80;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_FAULT_BUFFER: _bindgen_ty_2 = 81;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_FAULT_BUFFER: _bindgen_ty_2 = 82;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_REGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 83;
+pub const NV_VGPU_MSG_FUNCTION_GMMU_UNREGISTER_CLIENT_SHADOW_FAULT_BUFFER: _bindgen_ty_2 = 84;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_VGPU_FB_USAGE: _bindgen_ty_2 = 85;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVFBC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 86;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVENC_SW_SESSION_UPDATE_INFO: _bindgen_ty_2 = 87;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_CHANNEL: _bindgen_ty_2 = 88;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESET_ISOLATED_CHANNEL: _bindgen_ty_2 = 89;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_HANDLE_VF_PRI_FAULT: _bindgen_ty_2 = 90;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CLK_GET_EXTENDED_INFO: _bindgen_ty_2 = 91;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_BOOST: _bindgen_ty_2 = 92;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_VPSTATES_GET_CONTROL: _bindgen_ty_2 = 93;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE: _bindgen_ty_2 = 94;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_COLOR_CLEAR: _bindgen_ty_2 = 95;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_DEPTH_CLEAR: _bindgen_ty_2 = 96;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SCHEDULE: _bindgen_ty_2 = 97;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TIMESLICE: _bindgen_ty_2 = 98;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PREEMPT: _bindgen_ty_2 = 99;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_DISABLE_CHANNELS: _bindgen_ty_2 = 100;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_TSG_INTERLEAVE_LEVEL: _bindgen_ty_2 = 101;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_CHANNEL_INTERLEAVE_LEVEL: _bindgen_ty_2 = 102;
+pub const NV_VGPU_MSG_FUNCTION_GSP_RM_ALLOC: _bindgen_ty_2 = 103;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_V2: _bindgen_ty_2 = 104;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_AES_ENCRYPT: _bindgen_ty_2 = 105;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY: _bindgen_ty_2 = 106;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CIPHER_SESSION_KEY_STATUS: _bindgen_ty_2 = 107;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 108;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_ALL_SM_ERROR_STATES: _bindgen_ty_2 = 109;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_EXCEPTION_MASK: _bindgen_ty_2 = 110;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_PROMOTE_CTX: _bindgen_ty_2 = 111;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_PREEMPTION_BIND: _bindgen_ty_2 = 112;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_CTXSW_PREEMPTION_MODE: _bindgen_ty_2 = 113;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_CTXSW_ZCULL_BIND: _bindgen_ty_2 = 114;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_INITIALIZE_CTX: _bindgen_ty_2 = 115;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_VASPACE_COPY_SERVER_RESERVED_PDES: _bindgen_ty_2 = 116;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_CLEAR_FAULTED_BIT: _bindgen_ty_2 = 117;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_LATEST_ECC_ADDRESSES: _bindgen_ty_2 = 118;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_MC_SERVICE_INTERRUPTS: _bindgen_ty_2 = 119;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DMA_SET_DEFAULT_VASPACE: _bindgen_ty_2 = 120;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_CE_PCE_MASK: _bindgen_ty_2 = 121;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_ZBC_CLEAR_TABLE_ENTRY: _bindgen_ty_2 = 122;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_PEER_ID_MASK: _bindgen_ty_2 = 123;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_NVLINK_STATUS: _bindgen_ty_2 = 124;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS: _bindgen_ty_2 = 125;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_P2P_CAPS_MATRIX: _bindgen_ty_2 = 126;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_0: _bindgen_ty_2 = 127;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_PM_AREA_SMPC: _bindgen_ty_2 = 128;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HWPM_LEGACY: _bindgen_ty_2 = 129;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_B0CC_EXEC_REG_OPS: _bindgen_ty_2 = 130;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BIND_PM_RESOURCES: _bindgen_ty_2 = 131;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SUSPEND_CONTEXT: _bindgen_ty_2 = 132;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_RESUME_CONTEXT: _bindgen_ty_2 = 133;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_EXEC_REG_OPS: _bindgen_ty_2 = 134;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_DEBUG: _bindgen_ty_2 = 135;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_READ_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 136;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_CLEAR_SINGLE_SM_ERROR_STATE: _bindgen_ty_2 = 137;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_ERRBAR_DEBUG: _bindgen_ty_2 = 138;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_NEXT_STOP_TRIGGER_TYPE: _bindgen_ty_2 = 139;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_ALLOC_PMA_STREAM: _bindgen_ty_2 = 140;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PMA_STREAM_UPDATE_GET_PUT: _bindgen_ty_2 = 141;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_INFO_V2: _bindgen_ty_2 = 142;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SET_CHANNEL_PROPERTIES: _bindgen_ty_2 = 143;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 144;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_KGR_GET_CTX_BUFFER_PTES: _bindgen_ty_2 = 145;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_EVICT_CTX: _bindgen_ty_2 = 146;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FB_GET_FS_INFO: _bindgen_ty_2 = 147;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GRMGR_GET_GR_FS_INFO: _bindgen_ty_2 = 148;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_STOP_CHANNEL: _bindgen_ty_2 = 149;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_PC_SAMPLING_MODE: _bindgen_ty_2 = 150;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_GET_STATUS: _bindgen_ty_2 = 151;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_RATED_TDP_SET_CONTROL: _bindgen_ty_2 = 152;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FREE_PMA_STREAM: _bindgen_ty_2 = 153;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_TIMER_SET_GR_TICK_FREQ: _bindgen_ty_2 = 154;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FIFO_SETUP_VF_ZOMBIE_SUBCTX_PDB: _bindgen_ty_2 = 155;
+pub const NV_VGPU_MSG_FUNCTION_GET_CONSOLIDATED_GR_STATIC_INFO: _bindgen_ty_2 = 156;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_SINGLE_SM_SINGLE_STEP: _bindgen_ty_2 = 157;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_GET_TPC_PARTITION_MODE: _bindgen_ty_2 = 158;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GR_SET_TPC_PARTITION_MODE: _bindgen_ty_2 = 159;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_ALLOCATE: _bindgen_ty_2 = 160;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_DESTROY: _bindgen_ty_2 = 161;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_MAP: _bindgen_ty_2 = 162;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_UNMAP: _bindgen_ty_2 = 163;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_PUSH_STREAM: _bindgen_ty_2 = 164;
+pub const NV_VGPU_MSG_FUNCTION_UVM_PAGING_CHANNEL_SET_HANDLES: _bindgen_ty_2 = 165;
+pub const NV_VGPU_MSG_FUNCTION_UVM_METHOD_STREAM_GUEST_PAGES_OPERATION: _bindgen_ty_2 = 166;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_QUIESCE_PMA_CHANNEL: _bindgen_ty_2 = 167;
+pub const NV_VGPU_MSG_FUNCTION_DCE_RM_INIT: _bindgen_ty_2 = 168;
+pub const NV_VGPU_MSG_FUNCTION_REGISTER_VIRTUAL_EVENT_BUFFER: _bindgen_ty_2 = 169;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EVENT_BUFFER_UPDATE_GET: _bindgen_ty_2 = 170;
+pub const NV_VGPU_MSG_FUNCTION_GET_PLCABLE_ADDRESS_KIND: _bindgen_ty_2 = 171;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PERF_LIMITS_SET_STATUS_V2: _bindgen_ty_2 = 172;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_SRIOV_PROMOTE_PMA_STREAM: _bindgen_ty_2 = 173;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_MMU_DEBUG_MODE: _bindgen_ty_2 = 174;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_PROMOTE_FAULT_METHOD_BUFFERS: _bindgen_ty_2 = 175;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_SIZE: _bindgen_ty_2 = 176;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLCN_GET_CTX_BUFFER_INFO: _bindgen_ty_2 = 177;
+pub const NV_VGPU_MSG_FUNCTION_DISABLE_CHANNELS: _bindgen_ty_2 = 178;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEMORY_DESCRIBE: _bindgen_ty_2 = 179;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FABRIC_MEM_STATS: _bindgen_ty_2 = 180;
+pub const NV_VGPU_MSG_FUNCTION_SAVE_HIBERNATION_DATA: _bindgen_ty_2 = 181;
+pub const NV_VGPU_MSG_FUNCTION_RESTORE_HIBERNATION_DATA: _bindgen_ty_2 = 182;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_INTERNAL_MEMSYS_SET_ZBC_REFERENCED: _bindgen_ty_2 = 183;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_CREATE: _bindgen_ty_2 = 184;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_DELETE: _bindgen_ty_2 = 185;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_GET_WORK_SUBMIT_TOKEN: _bindgen_ty_2 = 186;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPFIFO_SET_WORK_SUBMIT_TOKEN_NOTIF_INDEX: _bindgen_ty_2 = 187;
+pub const NV_VGPU_MSG_FUNCTION_PMA_SCRUBBER_SHARED_BUFFER_GUEST_PAGES_OPERATION: _bindgen_ty_2 =
+ 188;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_MASTER_GET_VIRTUAL_FUNCTION_ERROR_CONT_INTR_MASK:
+ _bindgen_ty_2 = 189;
+pub const NV_VGPU_MSG_FUNCTION_SET_SYSMEM_DIRTY_PAGE_TRACKING_BUFFER: _bindgen_ty_2 = 190;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_P2P_CAPS: _bindgen_ty_2 = 191;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_SET_P2P_MAPPING: _bindgen_ty_2 = 192;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_BUS_UNSET_P2P_MAPPING: _bindgen_ty_2 = 193;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_FLA_SETUP_INSTANCE_MEM_BLOCK: _bindgen_ty_2 = 194;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_MIGRATABLE_OPS: _bindgen_ty_2 = 195;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_TOTAL_HS_CREDITS: _bindgen_ty_2 = 196;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GET_HS_CREDITS: _bindgen_ty_2 = 197;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_HS_CREDITS: _bindgen_ty_2 = 198;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_PM_AREA_PC_SAMPLER: _bindgen_ty_2 = 199;
+pub const NV_VGPU_MSG_FUNCTION_INVALIDATE_TLB: _bindgen_ty_2 = 200;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_QUERY_ECC_STATUS: _bindgen_ty_2 = 201;
+pub const NV_VGPU_MSG_FUNCTION_ECC_NOTIFIER_WRITE_ACK: _bindgen_ty_2 = 202;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_DEBUG: _bindgen_ty_2 = 203;
+pub const NV_VGPU_MSG_FUNCTION_RM_API_CONTROL: _bindgen_ty_2 = 204;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_GPU_START_FABRIC_PROBE: _bindgen_ty_2 = 205;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_NVLINK_GET_INBAND_RECEIVED_DATA: _bindgen_ty_2 = 206;
+pub const NV_VGPU_MSG_FUNCTION_GET_STATIC_DATA: _bindgen_ty_2 = 207;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED_208: _bindgen_ty_2 = 208;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_GPU_GET_INFO_V2: _bindgen_ty_2 = 209;
+pub const NV_VGPU_MSG_FUNCTION_GET_BRAND_CAPS: _bindgen_ty_2 = 210;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_NVLINK_INBAND_SEND_DATA: _bindgen_ty_2 = 211;
+pub const NV_VGPU_MSG_FUNCTION_UPDATE_GPM_GUEST_BUFFER_INFO: _bindgen_ty_2 = 212;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_INTERNAL_CONTROL_GSP_TRACE: _bindgen_ty_2 = 213;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SET_ZBC_STENCIL_CLEAR: _bindgen_ty_2 = 214;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_VGPU_HEAP_STATS: _bindgen_ty_2 = 215;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_SUBDEVICE_GET_LIBOS_HEAP_STATS: _bindgen_ty_2 = 216;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_SET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 217;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_DBG_GET_MODE_MMU_GCC_DEBUG: _bindgen_ty_2 = 218;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_HES: _bindgen_ty_2 = 219;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_HES: _bindgen_ty_2 = 220;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RESERVE_CCU_PROF: _bindgen_ty_2 = 221;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_RELEASE_CCU_PROF: _bindgen_ty_2 = 222;
+pub const NV_VGPU_MSG_FUNCTION_RESERVED: _bindgen_ty_2 = 223;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_CHIPLET_HS_CREDIT_POOL: _bindgen_ty_2 = 224;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_CMD_GET_HS_CREDITS_MAPPING: _bindgen_ty_2 = 225;
+pub const NV_VGPU_MSG_FUNCTION_CTRL_EXEC_PARTITIONS_EXPORT: _bindgen_ty_2 = 226;
+pub const NV_VGPU_MSG_FUNCTION_NUM_FUNCTIONS: _bindgen_ty_2 = 227;
+pub type _bindgen_ty_2 = ffi::c_uint;
+pub const NV_VGPU_MSG_EVENT_FIRST_EVENT: _bindgen_ty_3 = 4096;
+pub const NV_VGPU_MSG_EVENT_GSP_INIT_DONE: _bindgen_ty_3 = 4097;
+pub const NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER: _bindgen_ty_3 = 4098;
+pub const NV_VGPU_MSG_EVENT_POST_EVENT: _bindgen_ty_3 = 4099;
+pub const NV_VGPU_MSG_EVENT_RC_TRIGGERED: _bindgen_ty_3 = 4100;
+pub const NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED: _bindgen_ty_3 = 4101;
+pub const NV_VGPU_MSG_EVENT_OS_ERROR_LOG: _bindgen_ty_3 = 4102;
+pub const NV_VGPU_MSG_EVENT_RG_LINE_INTR: _bindgen_ty_3 = 4103;
+pub const NV_VGPU_MSG_EVENT_GPUACCT_PERFMON_UTIL_SAMPLES: _bindgen_ty_3 = 4104;
+pub const NV_VGPU_MSG_EVENT_SIM_READ: _bindgen_ty_3 = 4105;
+pub const NV_VGPU_MSG_EVENT_SIM_WRITE: _bindgen_ty_3 = 4106;
+pub const NV_VGPU_MSG_EVENT_SEMAPHORE_SCHEDULE_CALLBACK: _bindgen_ty_3 = 4107;
+pub const NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT: _bindgen_ty_3 = 4108;
+pub const NV_VGPU_MSG_EVENT_VGPU_GSP_PLUGIN_TRIGGERED: _bindgen_ty_3 = 4109;
+pub const NV_VGPU_MSG_EVENT_PERF_GPU_BOOST_SYNC_LIMITS_CALLBACK: _bindgen_ty_3 = 4110;
+pub const NV_VGPU_MSG_EVENT_PERF_BRIDGELESS_INFO_UPDATE: _bindgen_ty_3 = 4111;
+pub const NV_VGPU_MSG_EVENT_VGPU_CONFIG: _bindgen_ty_3 = 4112;
+pub const NV_VGPU_MSG_EVENT_DISPLAY_MODESET: _bindgen_ty_3 = 4113;
+pub const NV_VGPU_MSG_EVENT_EXTDEV_INTR_SERVICE: _bindgen_ty_3 = 4114;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_256: _bindgen_ty_3 = 4115;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_512: _bindgen_ty_3 = 4116;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_1024: _bindgen_ty_3 = 4117;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_2048: _bindgen_ty_3 = 4118;
+pub const NV_VGPU_MSG_EVENT_NVLINK_INBAND_RECEIVED_DATA_4096: _bindgen_ty_3 = 4119;
+pub const NV_VGPU_MSG_EVENT_TIMED_SEMAPHORE_RELEASE: _bindgen_ty_3 = 4120;
+pub const NV_VGPU_MSG_EVENT_NVLINK_IS_GPU_DEGRADED: _bindgen_ty_3 = 4121;
+pub const NV_VGPU_MSG_EVENT_PFM_REQ_HNDLR_STATE_SYNC_CALLBACK: _bindgen_ty_3 = 4122;
+pub const NV_VGPU_MSG_EVENT_NVLINK_FAULT_UP: _bindgen_ty_3 = 4123;
+pub const NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE: _bindgen_ty_3 = 4124;
+pub const NV_VGPU_MSG_EVENT_MIG_CI_CONFIG_UPDATE: _bindgen_ty_3 = 4125;
+pub const NV_VGPU_MSG_EVENT_UPDATE_GSP_TRACE: _bindgen_ty_3 = 4126;
+pub const NV_VGPU_MSG_EVENT_NVLINK_FATAL_ERROR_RECOVERY: _bindgen_ty_3 = 4127;
+pub const NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD: _bindgen_ty_3 = 4128;
+pub const NV_VGPU_MSG_EVENT_FECS_ERROR: _bindgen_ty_3 = 4129;
+pub const NV_VGPU_MSG_EVENT_RECOVERY_ACTION: _bindgen_ty_3 = 4130;
+pub const NV_VGPU_MSG_EVENT_NUM_EVENTS: _bindgen_ty_3 = 4131;
+pub type _bindgen_ty_3 = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS {
+ pub totalVFs: u32_,
+ pub firstVfOffset: u32_,
+ pub vfFeatureMask: u32_,
+ pub FirstVFBar0Address: u64_,
+ pub FirstVFBar1Address: u64_,
+ pub FirstVFBar2Address: u64_,
+ pub bar0Size: u64_,
+ pub bar1Size: u64_,
+ pub bar2Size: u64_,
+ pub b64bitBar0: u8_,
+ pub b64bitBar1: u8_,
+ pub b64bitBar2: u8_,
+ pub bSriovEnabled: u8_,
+ pub bSriovHeavyEnabled: u8_,
+ pub bEmulateVFBar0TlbInvalidationRegister: u8_,
+ pub bClientRmAllocatedCtxBuffer: u8_,
+ pub bNonPowerOf2ChannelCountSupported: u8_,
+ pub bVfResizableBAR1Supported: u8_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS {
+ pub BoardID: u32_,
+ pub chipSKU: [ffi::c_char; 9usize],
+ pub chipSKUMod: [ffi::c_char; 5usize],
+ pub skuConfigVersion: u32_,
+ pub project: [ffi::c_char; 5usize],
+ pub projectSKU: [ffi::c_char; 5usize],
+ pub CDP: [ffi::c_char; 6usize],
+ pub projectSKUMod: [ffi::c_char; 2usize],
+ pub businessCycle: u32_,
+}
+pub type NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG = [u8_; 17usize];
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO {
+ pub base: u64_,
+ pub limit: u64_,
+ pub reserved: u64_,
+ pub performance: u32_,
+ pub supportCompressed: u8_,
+ pub supportISO: u8_,
+ pub bProtected: u8_,
+ pub blackList: NV2080_CTRL_CMD_FB_GET_FB_REGION_SURFACE_MEM_TYPE_FLAG,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS {
+ pub numFBRegions: u32_,
+ pub fbRegion: [NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+ pub index: u32_,
+ pub flags: u32_,
+ pub length: u32_,
+ pub data: [u8_; 256usize],
+}
+impl Default for NV2080_CTRL_GPU_GET_GID_INFO_PARAMS {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct DOD_METHOD_DATA {
+ pub status: u32_,
+ pub acpiIdListLen: u32_,
+ pub acpiIdList: [u32_; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct JT_METHOD_DATA {
+ pub status: u32_,
+ pub jtCaps: u32_,
+ pub jtRevId: u16_,
+ pub bSBIOSCaps: u8_,
+ pub __bindgen_padding_0: u8,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct MUX_METHOD_DATA_ELEMENT {
+ pub acpiId: u32_,
+ pub mode: u32_,
+ pub status: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct MUX_METHOD_DATA {
+ pub tableLen: u32_,
+ pub acpiIdMuxModeTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
+ pub acpiIdMuxPartTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
+ pub acpiIdMuxStateTable: [MUX_METHOD_DATA_ELEMENT; 16usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct CAPS_METHOD_DATA {
+ pub status: u32_,
+ pub optimusCaps: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct ACPI_METHOD_DATA {
+ pub bValid: u8_,
+ pub __bindgen_padding_0: [u8; 3usize],
+ pub dodMethodData: DOD_METHOD_DATA,
+ pub jtMethodData: JT_METHOD_DATA,
+ pub muxMethodData: MUX_METHOD_DATA,
+ pub capsMethodData: CAPS_METHOD_DATA,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS {
+ pub headIndex: u32_,
+ pub maxHResolution: u32_,
+ pub maxVResolution: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS {
+ pub numHeads: u32_,
+ pub maxNumHeads: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct BUSINFO {
+ pub deviceID: u16_,
+ pub vendorID: u16_,
+ pub subdeviceID: u16_,
+ pub subvendorID: u16_,
+ pub revisionID: u8_,
+ pub __bindgen_padding_0: u8,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GSP_VF_INFO {
+ pub totalVFs: u32_,
+ pub firstVFOffset: u32_,
+ pub FirstVFBar0Address: u64_,
+ pub FirstVFBar1Address: u64_,
+ pub FirstVFBar2Address: u64_,
+ pub b64bitBar0: u8_,
+ pub b64bitBar1: u8_,
+ pub b64bitBar2: u8_,
+ pub __bindgen_padding_0: [u8; 5usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GSP_PCIE_CONFIG_REG {
+ pub linkCap: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct EcidManufacturingInfo {
+ pub ecidLow: u32_,
+ pub ecidHigh: u32_,
+ pub ecidExtended: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct FW_WPR_LAYOUT_OFFSET {
+ pub nonWprHeapOffset: u64_,
+ pub frtsOffset: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct GspStaticConfigInfo_t {
+ pub grCapsBits: [u8_; 23usize],
+ pub gidInfo: NV2080_CTRL_GPU_GET_GID_INFO_PARAMS,
+ pub SKUInfo: NV2080_CTRL_BIOS_GET_SKU_INFO_PARAMS,
+ pub fbRegionInfoParams: NV2080_CTRL_CMD_FB_GET_FB_REGION_INFO_PARAMS,
+ pub sriovCaps: NV0080_CTRL_GPU_GET_SRIOV_CAPS_PARAMS,
+ pub sriovMaxGfid: u32_,
+ pub engineCaps: [u32_; 3usize],
+ pub poisonFuseEnabled: u8_,
+ pub fb_length: u64_,
+ pub fbio_mask: u64_,
+ pub fb_bus_width: u32_,
+ pub fb_ram_type: u32_,
+ pub fbp_mask: u64_,
+ pub l2_cache_size: u32_,
+ pub gpuNameString: [u8_; 64usize],
+ pub gpuShortNameString: [u8_; 64usize],
+ pub gpuNameString_Unicode: [u16_; 64usize],
+ pub bGpuInternalSku: u8_,
+ pub bIsQuadroGeneric: u8_,
+ pub bIsQuadroAd: u8_,
+ pub bIsNvidiaNvs: u8_,
+ pub bIsVgx: u8_,
+ pub bGeforceSmb: u8_,
+ pub bIsTitan: u8_,
+ pub bIsTesla: u8_,
+ pub bIsMobile: u8_,
+ pub bIsGc6Rtd3Allowed: u8_,
+ pub bIsGc8Rtd3Allowed: u8_,
+ pub bIsGcOffRtd3Allowed: u8_,
+ pub bIsGcoffLegacyAllowed: u8_,
+ pub bIsMigSupported: u8_,
+ pub RTD3GC6TotalBoardPower: u16_,
+ pub RTD3GC6PerstDelay: u16_,
+ pub bar1PdeBase: u64_,
+ pub bar2PdeBase: u64_,
+ pub bVbiosValid: u8_,
+ pub vbiosSubVendor: u32_,
+ pub vbiosSubDevice: u32_,
+ pub bPageRetirementSupported: u8_,
+ pub bSplitVasBetweenServerClientRm: u8_,
+ pub bClRootportNeedsNosnoopWAR: u8_,
+ pub displaylessMaxHeads: VIRTUAL_DISPLAY_GET_NUM_HEADS_PARAMS,
+ pub displaylessMaxResolution: VIRTUAL_DISPLAY_GET_MAX_RESOLUTION_PARAMS,
+ pub displaylessMaxPixels: u64_,
+ pub hInternalClient: u32_,
+ pub hInternalDevice: u32_,
+ pub hInternalSubdevice: u32_,
+ pub bSelfHostedMode: u8_,
+ pub bAtsSupported: u8_,
+ pub bIsGpuUefi: u8_,
+ pub bIsEfiInit: u8_,
+ pub ecidInfo: [EcidManufacturingInfo; 2usize],
+ pub fwWprLayoutOffset: FW_WPR_LAYOUT_OFFSET,
+}
+impl Default for GspStaticConfigInfo_t {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GspSystemInfo {
+ pub gpuPhysAddr: u64_,
+ pub gpuPhysFbAddr: u64_,
+ pub gpuPhysInstAddr: u64_,
+ pub gpuPhysIoAddr: u64_,
+ pub nvDomainBusDeviceFunc: u64_,
+ pub simAccessBufPhysAddr: u64_,
+ pub notifyOpSharedSurfacePhysAddr: u64_,
+ pub pcieAtomicsOpMask: u64_,
+ pub consoleMemSize: u64_,
+ pub maxUserVa: u64_,
+ pub pciConfigMirrorBase: u32_,
+ pub pciConfigMirrorSize: u32_,
+ pub PCIDeviceID: u32_,
+ pub PCISubDeviceID: u32_,
+ pub PCIRevisionID: u32_,
+ pub pcieAtomicsCplDeviceCapMask: u32_,
+ pub oorArch: u8_,
+ pub __bindgen_padding_0: [u8; 7usize],
+ pub clPdbProperties: u64_,
+ pub Chipset: u32_,
+ pub bGpuBehindBridge: u8_,
+ pub bFlrSupported: u8_,
+ pub b64bBar0Supported: u8_,
+ pub bMnocAvailable: u8_,
+ pub chipsetL1ssEnable: u32_,
+ pub bUpstreamL0sUnsupported: u8_,
+ pub bUpstreamL1Unsupported: u8_,
+ pub bUpstreamL1PorSupported: u8_,
+ pub bUpstreamL1PorMobileOnly: u8_,
+ pub bSystemHasMux: u8_,
+ pub upstreamAddressValid: u8_,
+ pub FHBBusInfo: BUSINFO,
+ pub chipsetIDInfo: BUSINFO,
+ pub __bindgen_padding_1: [u8; 2usize],
+ pub acpiMethodData: ACPI_METHOD_DATA,
+ pub hypervisorType: u32_,
+ pub bIsPassthru: u8_,
+ pub __bindgen_padding_2: [u8; 7usize],
+ pub sysTimerOffsetNs: u64_,
+ pub gspVFInfo: GSP_VF_INFO,
+ pub bIsPrimary: u8_,
+ pub isGridBuild: u8_,
+ pub __bindgen_padding_3: [u8; 2usize],
+ pub pcieConfigReg: GSP_PCIE_CONFIG_REG,
+ pub gridBuildCsp: u32_,
+ pub bPreserveVideoMemoryAllocations: u8_,
+ pub bTdrEventSupported: u8_,
+ pub bFeatureStretchVblankCapable: u8_,
+ pub bEnableDynamicGranularityPageArrays: u8_,
+ pub bClockBoostSupported: u8_,
+ pub bRouteDispIntrsToCPU: u8_,
+ pub __bindgen_padding_4: [u8; 6usize],
+ pub hostPageSize: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct MESSAGE_QUEUE_INIT_ARGUMENTS {
+ pub sharedMemPhysAddr: u64_,
+ pub pageTableEntryCount: u32_,
+ pub __bindgen_padding_0: [u8; 4usize],
+ pub cmdQueueOffset: u64_,
+ pub statQueueOffset: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GSP_SR_INIT_ARGUMENTS {
+ pub oldLevel: u32_,
+ pub flags: u32_,
+ pub bInPMTransition: u8_,
+ pub __bindgen_padding_0: [u8; 3usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GSP_ARGUMENTS_CACHED {
+ pub messageQueueInitArguments: MESSAGE_QUEUE_INIT_ARGUMENTS,
+ pub srInitArguments: GSP_SR_INIT_ARGUMENTS,
+ pub gpuInstance: u32_,
+ pub bDmemStack: u8_,
+ pub __bindgen_padding_0: [u8; 7usize],
+ pub profilerArgs: GSP_ARGUMENTS_CACHED__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GSP_ARGUMENTS_CACHED__bindgen_ty_1 {
+ pub pa: u64_,
+ pub size: u64_,
+}
+#[repr(C)]
+#[derive(Copy, Clone, Zeroable)]
+pub union rpc_message_rpc_union_field_v03_00 {
+ pub spare: u32_,
+ pub cpuRmGfid: u32_,
+}
+impl Default for rpc_message_rpc_union_field_v03_00 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type rpc_message_rpc_union_field_v = rpc_message_rpc_union_field_v03_00;
+#[repr(C)]
+pub struct rpc_message_header_v03_00 {
+ pub header_version: u32_,
+ pub signature: u32_,
+ pub length: u32_,
+ pub function: u32_,
+ pub rpc_result: u32_,
+ pub rpc_result_private: u32_,
+ pub sequence: u32_,
+ pub u: rpc_message_rpc_union_field_v,
+ pub rpc_message_data: __IncompleteArrayField<u8_>,
+}
+impl Default for rpc_message_header_v03_00 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type rpc_message_header_v = rpc_message_header_v03_00;
+#[repr(C)]
+#[derive(Copy, Clone, Zeroable)]
+pub struct GspFwWprMeta {
+ pub magic: u64_,
+ pub revision: u64_,
+ pub sysmemAddrOfRadix3Elf: u64_,
+ pub sizeOfRadix3Elf: u64_,
+ pub sysmemAddrOfBootloader: u64_,
+ pub sizeOfBootloader: u64_,
+ pub bootloaderCodeOffset: u64_,
+ pub bootloaderDataOffset: u64_,
+ pub bootloaderManifestOffset: u64_,
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1,
+ pub gspFwRsvdStart: u64_,
+ pub nonWprHeapOffset: u64_,
+ pub nonWprHeapSize: u64_,
+ pub gspFwWprStart: u64_,
+ pub gspFwHeapOffset: u64_,
+ pub gspFwHeapSize: u64_,
+ pub gspFwOffset: u64_,
+ pub bootBinOffset: u64_,
+ pub frtsOffset: u64_,
+ pub frtsSize: u64_,
+ pub gspFwWprEnd: u64_,
+ pub fbSize: u64_,
+ pub vgaWorkspaceOffset: u64_,
+ pub vgaWorkspaceSize: u64_,
+ pub bootCount: u64_,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2,
+ pub gspFwHeapVfPartitionCount: u8_,
+ pub flags: u8_,
+ pub padding: [u8_; 2usize],
+ pub pmuReservedSize: u32_,
+ pub verified: u64_,
+}
+#[repr(C)]
+#[derive(Copy, Clone, Zeroable)]
+pub union GspFwWprMeta__bindgen_ty_1 {
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_1__bindgen_ty_1,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_1__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_1 {
+ pub sysmemAddrOfSignature: u64_,
+ pub sizeOfSignature: u64_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GspFwWprMeta__bindgen_ty_1__bindgen_ty_2 {
+ pub gspFwHeapFreeListWprOffset: u32_,
+ pub unused0: u32_,
+ pub unused1: u64_,
+}
+impl Default for GspFwWprMeta__bindgen_ty_1 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone, Zeroable)]
+pub union GspFwWprMeta__bindgen_ty_2 {
+ pub __bindgen_anon_1: GspFwWprMeta__bindgen_ty_2__bindgen_ty_1,
+ pub __bindgen_anon_2: GspFwWprMeta__bindgen_ty_2__bindgen_ty_2,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_1 {
+ pub partitionRpcAddr: u64_,
+ pub partitionRpcRequestOffset: u16_,
+ pub partitionRpcReplyOffset: u16_,
+ pub elfCodeOffset: u32_,
+ pub elfDataOffset: u32_,
+ pub elfCodeSize: u32_,
+ pub elfDataSize: u32_,
+ pub lsUcodeVersion: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct GspFwWprMeta__bindgen_ty_2__bindgen_ty_2 {
+ pub partitionRpcPadding: [u32_; 4usize],
+ pub sysmemAddrOfCrashReportQueue: u64_,
+ pub sizeOfCrashReportQueue: u32_,
+ pub lsUcodeVersionPadding: [u32_; 1usize],
+}
+impl Default for GspFwWprMeta__bindgen_ty_2 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+impl Default for GspFwWprMeta {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+pub type LibosAddress = u64_;
+pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_NONE: LibosMemoryRegionKind = 0;
+pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS: LibosMemoryRegionKind = 1;
+pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_RADIX3: LibosMemoryRegionKind = 2;
+pub type LibosMemoryRegionKind = ffi::c_uint;
+pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_NONE: LibosMemoryRegionLoc = 0;
+pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM: LibosMemoryRegionLoc = 1;
+pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2;
+pub type LibosMemoryRegionLoc = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct LibosMemoryRegionInitArgument {
+ pub id8: LibosAddress,
+ pub pa: LibosAddress,
+ pub size: LibosAddress,
+ pub kind: u8_,
+ pub loc: u8_,
+ pub __bindgen_padding_0: [u8; 6usize],
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct PACKED_REGISTRY_ENTRY {
+ pub nameOffset: u32_,
+ pub type_: u8_,
+ pub __bindgen_padding_0: [u8; 3usize],
+ pub data: u32_,
+ pub length: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct PACKED_REGISTRY_TABLE {
+ pub size: u32_,
+ pub numEntries: u32_,
+ pub entries: __IncompleteArrayField<PACKED_REGISTRY_ENTRY>,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct msgqTxHeader {
+ pub version: u32_,
+ pub size: u32_,
+ pub msgSize: u32_,
+ pub msgCount: u32_,
+ pub writePtr: u32_,
+ pub flags: u32_,
+ pub rxHdrOff: u32_,
+ pub entryOff: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone, Zeroable)]
+pub struct msgqRxHeader {
+ pub readPtr: u32_,
+}
+#[repr(C)]
+#[repr(align(8))]
+#[derive(Zeroable)]
+pub struct GSP_MSG_QUEUE_ELEMENT {
+ pub authTagBuffer: [u8_; 16usize],
+ pub aadBuffer: [u8_; 16usize],
+ pub checkSum: u32_,
+ pub seqNum: u32_,
+ pub elemCount: u32_,
+ pub __bindgen_padding_0: [u8; 4usize],
+ pub rpc: rpc_message_header_v,
+}
+impl Default for GSP_MSG_QUEUE_ELEMENT {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+#[repr(C)]
+#[derive(Debug, Default)]
+pub struct rpc_run_cpu_sequencer_v17_00 {
+ pub bufferSizeDWord: u32_,
+ pub cmdIndex: u32_,
+ pub regSaveArea: [u32_; 8usize],
+ pub commandBuffer: __IncompleteArrayField<u32_>,
+}
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_WRITE: GSP_SEQ_BUF_OPCODE = 0;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_MODIFY: GSP_SEQ_BUF_OPCODE = 1;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_POLL: GSP_SEQ_BUF_OPCODE = 2;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_DELAY_US: GSP_SEQ_BUF_OPCODE = 3;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_REG_STORE: GSP_SEQ_BUF_OPCODE = 4;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESET: GSP_SEQ_BUF_OPCODE = 5;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_START: GSP_SEQ_BUF_OPCODE = 6;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_WAIT_FOR_HALT: GSP_SEQ_BUF_OPCODE = 7;
+pub const GSP_SEQ_BUF_OPCODE_GSP_SEQ_BUF_OPCODE_CORE_RESUME: GSP_SEQ_BUF_OPCODE = 8;
+pub type GSP_SEQ_BUF_OPCODE = ffi::c_uint;
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_WRITE {
+ pub addr: u32_,
+ pub val: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_MODIFY {
+ pub addr: u32_,
+ pub mask: u32_,
+ pub val: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_POLL {
+ pub addr: u32_,
+ pub mask: u32_,
+ pub val: u32_,
+ pub timeout: u32_,
+ pub error: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_DELAY_US {
+ pub val: u32_,
+}
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct GSP_SEQ_BUF_PAYLOAD_REG_STORE {
+ pub addr: u32_,
+ pub index: u32_,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct GSP_SEQUENCER_BUFFER_CMD {
+ pub opCode: GSP_SEQ_BUF_OPCODE,
+ pub payload: GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 {
+ pub regWrite: GSP_SEQ_BUF_PAYLOAD_REG_WRITE,
+ pub regModify: GSP_SEQ_BUF_PAYLOAD_REG_MODIFY,
+ pub regPoll: GSP_SEQ_BUF_PAYLOAD_REG_POLL,
+ pub delayUs: GSP_SEQ_BUF_PAYLOAD_DELAY_US,
+ pub regStore: GSP_SEQ_BUF_PAYLOAD_REG_STORE,
+}
+impl Default for GSP_SEQUENCER_BUFFER_CMD__bindgen_ty_1 {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
+impl Default for GSP_SEQUENCER_BUFFER_CMD {
+ fn default() -> Self {
+ let mut s = ::core::mem::MaybeUninit::<Self>::uninit();
+ unsafe {
+ ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
+ s.assume_init()
+ }
+ }
+}
diff --git a/drivers/gpu/nova-core/gsp/sequencer.rs b/drivers/gpu/nova-core/gsp/sequencer.rs
new file mode 100644
index 000000000000..2d0369c49092
--- /dev/null
+++ b/drivers/gpu/nova-core/gsp/sequencer.rs
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! GSP Sequencer implementation for Pre-hopper GSP boot sequence.
+
+use core::{
+ array,
+ mem::{
+ size_of,
+ size_of_val, //
+ },
+};
+
+use kernel::{
+ device,
+ io::poll::read_poll_timeout,
+ prelude::*,
+ time::{
+ delay::fsleep,
+ Delta, //
+ },
+ transmute::FromBytes,
+ types::ARef, //
+};
+
+use crate::{
+ driver::Bar0,
+ falcon::{
+ gsp::Gsp,
+ sec2::Sec2,
+ Falcon, //
+ },
+ gsp::{
+ cmdq::{
+ Cmdq,
+ MessageFromGsp, //
+ },
+ fw,
+ },
+ num::FromSafeCast,
+ sbuffer::SBufferIter,
+};
+
+/// GSP Sequencer information containing the command sequence and data.
+struct GspSequence {
+ /// Current command index for error reporting.
+ cmd_index: u32,
+ /// Command data buffer containing the sequence of commands.
+ cmd_data: KVec<u8>,
+}
+
+impl MessageFromGsp for GspSequence {
+ const FUNCTION: fw::MsgFunction = fw::MsgFunction::GspRunCpuSequencer;
+ type InitError = Error;
+ type Message = fw::RunCpuSequencer;
+
+ fn read(
+ msg: &Self::Message,
+ sbuffer: &mut SBufferIter<array::IntoIter<&[u8], 2>>,
+ ) -> Result<Self, Self::InitError> {
+ let cmd_data = sbuffer.flush_into_kvec(GFP_KERNEL)?;
+ Ok(GspSequence {
+ cmd_index: msg.cmd_index(),
+ cmd_data,
+ })
+ }
+}
+
+const CMD_SIZE: usize = size_of::<fw::SequencerBufferCmd>();
+
+/// GSP Sequencer Command types with payload data.
+/// Commands have an opcode and an opcode-dependent struct.
+#[allow(clippy::enum_variant_names)]
+pub(crate) enum GspSeqCmd {
+ RegWrite(fw::RegWritePayload),
+ RegModify(fw::RegModifyPayload),
+ RegPoll(fw::RegPollPayload),
+ DelayUs(fw::DelayUsPayload),
+ RegStore(fw::RegStorePayload),
+ CoreReset,
+ CoreStart,
+ CoreWaitForHalt,
+ CoreResume,
+}
+
+impl GspSeqCmd {
+ /// Creates a new `GspSeqCmd` from raw data returning the command and its size in bytes.
+ pub(crate) fn new(data: &[u8], dev: &device::Device) -> Result<(Self, usize)> {
+ let fw_cmd = fw::SequencerBufferCmd::from_bytes(data).ok_or(EINVAL)?;
+ let opcode_size = core::mem::size_of::<u32>();
+
+ let (cmd, size) = match fw_cmd.opcode()? {
+ fw::SeqBufOpcode::RegWrite => {
+ let payload = fw_cmd.reg_write_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegWrite(payload), size)
+ }
+ fw::SeqBufOpcode::RegModify => {
+ let payload = fw_cmd.reg_modify_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegModify(payload), size)
+ }
+ fw::SeqBufOpcode::RegPoll => {
+ let payload = fw_cmd.reg_poll_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegPoll(payload), size)
+ }
+ fw::SeqBufOpcode::DelayUs => {
+ let payload = fw_cmd.delay_us_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::DelayUs(payload), size)
+ }
+ fw::SeqBufOpcode::RegStore => {
+ let payload = fw_cmd.reg_store_payload()?;
+ let size = opcode_size + size_of_val(&payload);
+ (GspSeqCmd::RegStore(payload), size)
+ }
+ fw::SeqBufOpcode::CoreReset => (GspSeqCmd::CoreReset, opcode_size),
+ fw::SeqBufOpcode::CoreStart => (GspSeqCmd::CoreStart, opcode_size),
+ fw::SeqBufOpcode::CoreWaitForHalt => (GspSeqCmd::CoreWaitForHalt, opcode_size),
+ fw::SeqBufOpcode::CoreResume => (GspSeqCmd::CoreResume, opcode_size),
+ };
+
+ if data.len() < size {
+ dev_err!(dev, "Data is not enough for command");
+ return Err(EINVAL);
+ }
+
+ Ok((cmd, size))
+ }
+}
+
+/// GSP Sequencer for executing firmware commands during boot.
+pub(crate) struct GspSequencer<'a> {
+ /// Sequencer information with command data.
+ seq_info: GspSequence,
+ /// `Bar0` for register access.
+ bar: &'a Bar0,
+ /// SEC2 falcon for core operations.
+ sec2_falcon: &'a Falcon<Sec2>,
+ /// GSP falcon for core operations.
+ gsp_falcon: &'a Falcon<Gsp>,
+ /// LibOS DMA handle address.
+ libos_dma_handle: u64,
+ /// Bootloader application version.
+ bootloader_app_version: u32,
+ /// Device for logging.
+ dev: ARef<device::Device>,
+}
+
+/// Trait for running sequencer commands.
+pub(crate) trait GspSeqCmdRunner {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result;
+}
+
+impl GspSeqCmdRunner for fw::RegWritePayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ sequencer.bar.try_write32(self.val(), addr)
+ }
+}
+
+impl GspSeqCmdRunner for fw::RegModifyPayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ sequencer.bar.try_read32(addr).and_then(|val| {
+ sequencer
+ .bar
+ .try_write32((val & !self.mask()) | self.val(), addr)
+ })
+ }
+}
+
+impl GspSeqCmdRunner for fw::RegPollPayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ // Default timeout to 4 seconds.
+ let timeout_us = if self.timeout() == 0 {
+ 4_000_000
+ } else {
+ i64::from(self.timeout())
+ };
+
+ // First read.
+ sequencer.bar.try_read32(addr)?;
+
+ // Poll the requested register with requested timeout.
+ read_poll_timeout(
+ || sequencer.bar.try_read32(addr),
+ |current| (current & self.mask()) == self.val(),
+ Delta::ZERO,
+ Delta::from_micros(timeout_us),
+ )
+ .map(|_| ())
+ }
+}
+
+impl GspSeqCmdRunner for fw::DelayUsPayload {
+ fn run(&self, _sequencer: &GspSequencer<'_>) -> Result {
+ fsleep(Delta::from_micros(i64::from(self.val())));
+ Ok(())
+ }
+}
+
+impl GspSeqCmdRunner for fw::RegStorePayload {
+ fn run(&self, sequencer: &GspSequencer<'_>) -> Result {
+ let addr = usize::from_safe_cast(self.addr());
+
+ sequencer.bar.try_read32(addr).map(|_| ())
+ }
+}
+
+impl GspSeqCmdRunner for GspSeqCmd {
+ fn run(&self, seq: &GspSequencer<'_>) -> Result {
+ match self {
+ GspSeqCmd::RegWrite(cmd) => cmd.run(seq),
+ GspSeqCmd::RegModify(cmd) => cmd.run(seq),
+ GspSeqCmd::RegPoll(cmd) => cmd.run(seq),
+ GspSeqCmd::DelayUs(cmd) => cmd.run(seq),
+ GspSeqCmd::RegStore(cmd) => cmd.run(seq),
+ GspSeqCmd::CoreReset => {
+ seq.gsp_falcon.reset(seq.bar)?;
+ seq.gsp_falcon.dma_reset(seq.bar);
+ Ok(())
+ }
+ GspSeqCmd::CoreStart => {
+ seq.gsp_falcon.start(seq.bar)?;
+ Ok(())
+ }
+ GspSeqCmd::CoreWaitForHalt => {
+ seq.gsp_falcon.wait_till_halted(seq.bar)?;
+ Ok(())
+ }
+ GspSeqCmd::CoreResume => {
+ // At this point, 'SEC2-RTOS' has been loaded into SEC2 by the sequencer
+ // but neither SEC2-RTOS nor GSP-RM is running yet. This part of the
+ // sequencer will start both.
+
+ // Reset the GSP to prepare it for resuming.
+ seq.gsp_falcon.reset(seq.bar)?;
+
+ // Write the libOS DMA handle to GSP mailboxes.
+ seq.gsp_falcon.write_mailboxes(
+ seq.bar,
+ Some(seq.libos_dma_handle as u32),
+ Some((seq.libos_dma_handle >> 32) as u32),
+ );
+
+ // Start the SEC2 falcon which will trigger GSP-RM to resume on the GSP.
+ seq.sec2_falcon.start(seq.bar)?;
+
+ // Poll until GSP-RM reload/resume has completed (up to 2 seconds).
+ seq.gsp_falcon
+ .check_reload_completed(seq.bar, Delta::from_secs(2))?;
+
+ // Verify SEC2 completed successfully by checking its mailbox for errors.
+ let mbox0 = seq.sec2_falcon.read_mailbox0(seq.bar);
+ if mbox0 != 0 {
+ dev_err!(seq.dev, "Sequencer: sec2 errors: {:?}\n", mbox0);
+ return Err(EIO);
+ }
+
+ // Configure GSP with the bootloader version.
+ seq.gsp_falcon
+ .write_os_version(seq.bar, seq.bootloader_app_version);
+
+ // Verify the GSP's RISC-V core is active indicating successful GSP boot.
+ if !seq.gsp_falcon.is_riscv_active(seq.bar) {
+ dev_err!(seq.dev, "Sequencer: RISC-V core is not active\n");
+ return Err(EIO);
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+/// Iterator over GSP sequencer commands.
+pub(crate) struct GspSeqIter<'a> {
+ /// Command data buffer.
+ cmd_data: &'a [u8],
+ /// Current position in the buffer.
+ current_offset: usize,
+ /// Total number of commands to process.
+ total_cmds: u32,
+ /// Number of commands processed so far.
+ cmds_processed: u32,
+ /// Device for logging.
+ dev: ARef<device::Device>,
+}
+
+impl<'a> Iterator for GspSeqIter<'a> {
+ type Item = Result<GspSeqCmd>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // Stop if we've processed all commands or reached the end of data.
+ if self.cmds_processed >= self.total_cmds || self.current_offset >= self.cmd_data.len() {
+ return None;
+ }
+
+ // Check if we have enough data for opcode.
+ if self.current_offset + core::mem::size_of::<u32>() > self.cmd_data.len() {
+ return Some(Err(EIO));
+ }
+
+ let offset = self.current_offset;
+
+ // Handle command creation based on available data,
+ // zero-pad if necessary (since last command may not be full size).
+ let mut buffer = [0u8; CMD_SIZE];
+ let copy_len = if offset + CMD_SIZE <= self.cmd_data.len() {
+ CMD_SIZE
+ } else {
+ self.cmd_data.len() - offset
+ };
+ buffer[..copy_len].copy_from_slice(&self.cmd_data[offset..offset + copy_len]);
+ let cmd_result = GspSeqCmd::new(&buffer, &self.dev);
+
+ cmd_result.map_or_else(
+ |_err| {
+ dev_err!(self.dev, "Error parsing command at offset {}", offset);
+ None
+ },
+ |(cmd, size)| {
+ self.current_offset += size;
+ self.cmds_processed += 1;
+ Some(Ok(cmd))
+ },
+ )
+ }
+}
+
+impl<'a> GspSequencer<'a> {
+ fn iter(&self) -> GspSeqIter<'_> {
+ let cmd_data = &self.seq_info.cmd_data[..];
+
+ GspSeqIter {
+ cmd_data,
+ current_offset: 0,
+ total_cmds: self.seq_info.cmd_index,
+ cmds_processed: 0,
+ dev: self.dev.clone(),
+ }
+ }
+}
+
+/// Parameters for running the GSP sequencer.
+pub(crate) struct GspSequencerParams<'a> {
+ /// Bootloader application version.
+ pub(crate) bootloader_app_version: u32,
+ /// LibOS DMA handle address.
+ pub(crate) libos_dma_handle: u64,
+ /// GSP falcon for core operations.
+ pub(crate) gsp_falcon: &'a Falcon<Gsp>,
+ /// SEC2 falcon for core operations.
+ pub(crate) sec2_falcon: &'a Falcon<Sec2>,
+ /// Device for logging.
+ pub(crate) dev: ARef<device::Device>,
+ /// BAR0 for register access.
+ pub(crate) bar: &'a Bar0,
+}
+
+impl<'a> GspSequencer<'a> {
+ pub(crate) fn run(cmdq: &mut Cmdq, params: GspSequencerParams<'a>) -> Result {
+ let seq_info = loop {
+ match cmdq.receive_msg::<GspSequence>(Delta::from_secs(10)) {
+ Ok(seq_info) => break seq_info,
+ Err(ERANGE) => continue,
+ Err(e) => return Err(e),
+ }
+ };
+
+ let sequencer = GspSequencer {
+ seq_info,
+ bar: params.bar,
+ sec2_falcon: params.sec2_falcon,
+ gsp_falcon: params.gsp_falcon,
+ libos_dma_handle: params.libos_dma_handle,
+ bootloader_app_version: params.bootloader_app_version,
+ dev: params.dev,
+ };
+
+ dev_dbg!(sequencer.dev, "Running CPU Sequencer commands");
+
+ for cmd_result in sequencer.iter() {
+ match cmd_result {
+ Ok(cmd) => cmd.run(&sequencer)?,
+ Err(e) => {
+ dev_err!(
+ sequencer.dev,
+ "Error running command at index {}",
+ sequencer.seq_info.cmd_index
+ );
+ return Err(e);
+ }
+ }
+ }
+
+ dev_dbg!(
+ sequencer.dev,
+ "CPU Sequencer commands completed successfully"
+ );
+ Ok(())
+ }
+}
diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nova_core.rs
index fffcaee2249f..b98a1c03f13d 100644
--- a/drivers/gpu/nova-core/nova_core.rs
+++ b/drivers/gpu/nova-core/nova_core.rs
@@ -2,6 +2,9 @@
//! Nova Core GPU Driver
+#[macro_use]
+mod bitfield;
+
mod dma;
mod driver;
mod falcon;
@@ -10,7 +13,9 @@ mod firmware;
mod gfw;
mod gpu;
mod gsp;
+mod num;
mod regs;
+mod sbuffer;
mod util;
mod vbios;
diff --git a/drivers/gpu/nova-core/num.rs b/drivers/gpu/nova-core/num.rs
new file mode 100644
index 000000000000..c952a834e662
--- /dev/null
+++ b/drivers/gpu/nova-core/num.rs
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Numerical helpers functions and traits.
+//!
+//! This is essentially a staging module for code to mature until it can be moved to the `kernel`
+//! crate.
+
+use kernel::{
+ macros::paste,
+ prelude::*, //
+};
+
+/// Implements safe `as` conversion functions from a given type into a series of target types.
+///
+/// These functions can be used in place of `as`, with the guarantee that they will be lossless.
+macro_rules! impl_safe_as {
+ ($from:ty as { $($into:ty),* }) => {
+ $(
+ paste! {
+ #[doc = ::core::concat!(
+ "Losslessly converts a [`",
+ ::core::stringify!($from),
+ "`] into a [`",
+ ::core::stringify!($into),
+ "`].")]
+ ///
+ /// This conversion is allowed as it is always lossless. Prefer this over the `as`
+ /// keyword to ensure no lossy casts are performed.
+ ///
+ /// This is for use from a `const` context. For non `const` use, prefer the
+ /// [`FromSafeCast`] and [`IntoSafeCast`] traits.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use crate::num;
+ ///
+ #[doc = ::core::concat!(
+ "assert_eq!(num::",
+ ::core::stringify!($from),
+ "_as_",
+ ::core::stringify!($into),
+ "(1",
+ ::core::stringify!($from),
+ "), 1",
+ ::core::stringify!($into),
+ ");")]
+ /// ```
+ #[allow(unused)]
+ #[inline(always)]
+ pub(crate) const fn [<$from _as_ $into>](value: $from) -> $into {
+ kernel::static_assert!(size_of::<$into>() >= size_of::<$from>());
+
+ value as $into
+ }
+ }
+ )*
+ };
+}
+
+impl_safe_as!(u8 as { u16, u32, u64, usize });
+impl_safe_as!(u16 as { u32, u64, usize });
+impl_safe_as!(u32 as { u64, usize } );
+// `u64` and `usize` have the same size on 64-bit platforms.
+#[cfg(CONFIG_64BIT)]
+impl_safe_as!(u64 as { usize } );
+
+// A `usize` fits into a `u64` on 32 and 64-bit platforms.
+#[cfg(any(CONFIG_32BIT, CONFIG_64BIT))]
+impl_safe_as!(usize as { u64 });
+
+// A `usize` fits into a `u32` on 32-bit platforms.
+#[cfg(CONFIG_32BIT)]
+impl_safe_as!(usize as { u32 });
+
+/// Extension trait providing guaranteed lossless cast to `Self` from `T`.
+///
+/// The standard library's `From` implementations do not cover conversions that are not portable or
+/// future-proof. For instance, even though it is safe today, `From<usize>` is not implemented for
+/// [`u64`] because of the possibility to support larger-than-64bit architectures in the future.
+///
+/// The workaround is to either deal with the error handling of [`TryFrom`] for an operation that
+/// technically cannot fail, or to use the `as` keyword, which can silently strip data if the
+/// destination type is smaller than the source.
+///
+/// Both options are hardly acceptable for the kernel. It is also a much more architecture
+/// dependent environment, supporting only 32 and 64 bit architectures, with some modules
+/// explicitly depending on a specific bus width that could greatly benefit from infallible
+/// conversion operations.
+///
+/// Thus this extension trait that provides, for the architecture the kernel is built for, safe
+/// conversion between types for which such cast is lossless.
+///
+/// In other words, this trait is implemented if, for the current build target and with `t: T`, the
+/// `t as Self` operation is completely lossless.
+///
+/// Prefer this over the `as` keyword to ensure no lossy casts are performed.
+///
+/// If you need to perform a conversion in `const` context, use [`u64_as_usize`], [`u32_as_usize`],
+/// [`usize_as_u64`], etc.
+///
+/// # Examples
+///
+/// ```
+/// use crate::num::FromSafeCast;
+///
+/// assert_eq!(usize::from_safe_cast(0xf00u32), 0xf00u32 as usize);
+/// ```
+pub(crate) trait FromSafeCast<T> {
+ /// Create a `Self` from `value`. This operation is guaranteed to be lossless.
+ fn from_safe_cast(value: T) -> Self;
+}
+
+impl FromSafeCast<usize> for u64 {
+ fn from_safe_cast(value: usize) -> Self {
+ usize_as_u64(value)
+ }
+}
+
+#[cfg(CONFIG_32BIT)]
+impl FromSafeCast<usize> for u32 {
+ fn from_safe_cast(value: usize) -> Self {
+ usize_as_u32(value)
+ }
+}
+
+impl FromSafeCast<u32> for usize {
+ fn from_safe_cast(value: u32) -> Self {
+ u32_as_usize(value)
+ }
+}
+
+#[cfg(CONFIG_64BIT)]
+impl FromSafeCast<u64> for usize {
+ fn from_safe_cast(value: u64) -> Self {
+ u64_as_usize(value)
+ }
+}
+
+/// Counterpart to the [`FromSafeCast`] trait, i.e. this trait is to [`FromSafeCast`] what [`Into`]
+/// is to [`From`].
+///
+/// See the documentation of [`FromSafeCast`] for the motivation.
+///
+/// # Examples
+///
+/// ```
+/// use crate::num::IntoSafeCast;
+///
+/// assert_eq!(0xf00u32.into_safe_cast(), 0xf00u32 as usize);
+/// ```
+pub(crate) trait IntoSafeCast<T> {
+ /// Convert `self` into a `T`. This operation is guaranteed to be lossless.
+ fn into_safe_cast(self) -> T;
+}
+
+/// Reverse operation for types implementing [`FromSafeCast`].
+impl<S, T> IntoSafeCast<T> for S
+where
+ T: FromSafeCast<S>,
+{
+ fn into_safe_cast(self) -> T {
+ T::from_safe_cast(self)
+ }
+}
+
+/// Implements lossless conversion of a constant from a larger type into a smaller one.
+macro_rules! impl_const_into {
+ ($from:ty => { $($into:ty),* }) => {
+ $(
+ paste! {
+ #[doc = ::core::concat!(
+ "Performs a build-time safe conversion of a [`",
+ ::core::stringify!($from),
+ "`] constant value into a [`",
+ ::core::stringify!($into),
+ "`].")]
+ ///
+ /// This checks at compile-time that the conversion is lossless, and triggers a build
+ /// error if it isn't.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use crate::num;
+ ///
+ /// // Succeeds because the value of the source fits into the destination's type.
+ #[doc = ::core::concat!(
+ "assert_eq!(num::",
+ ::core::stringify!($from),
+ "_into_",
+ ::core::stringify!($into),
+ "::<1",
+ ::core::stringify!($from),
+ ">(), 1",
+ ::core::stringify!($into),
+ ");")]
+ /// ```
+ #[allow(unused)]
+ pub(crate) const fn [<$from _into_ $into>]<const N: $from>() -> $into {
+ // Make sure that the target type is smaller than the source one.
+ static_assert!($from::BITS >= $into::BITS);
+ // CAST: we statically enforced above that `$from` is larger than `$into`, so the
+ // `as` conversion will be lossless.
+ build_assert!(N >= $into::MIN as $from && N <= $into::MAX as $from);
+
+ N as $into
+ }
+ }
+ )*
+ };
+}
+
+impl_const_into!(usize => { u8, u16, u32 });
+impl_const_into!(u64 => { u8, u16, u32 });
+impl_const_into!(u32 => { u8, u16 });
+impl_const_into!(u16 => { u8 });
diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs
index 206dab2e1335..82cc6c0790e5 100644
--- a/drivers/gpu/nova-core/regs.rs
+++ b/drivers/gpu/nova-core/regs.rs
@@ -7,13 +7,28 @@
#[macro_use]
pub(crate) mod macros;
-use crate::falcon::{
- DmaTrfCmdSize, FalconCoreRev, FalconCoreRevSubversion, FalconFbifMemType, FalconFbifTarget,
- FalconModSelAlgo, FalconSecurityModel, PFalcon2Base, PFalconBase, PeregrineCoreSelect,
-};
-use crate::gpu::{Architecture, Chipset};
use kernel::prelude::*;
+use crate::{
+ falcon::{
+ DmaTrfCmdSize,
+ FalconCoreRev,
+ FalconCoreRevSubversion,
+ FalconFbifMemType,
+ FalconFbifTarget,
+ FalconModSelAlgo,
+ FalconSecurityModel,
+ PFalcon2Base,
+ PFalconBase,
+ PeregrineCoreSelect, //
+ },
+ gpu::{
+ Architecture,
+ Chipset, //
+ },
+ num::FromSafeCast,
+};
+
// PMC
register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU" {
@@ -25,13 +40,24 @@ register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU"
});
impl NV_PMC_BOOT_0 {
- /// Combines `architecture_0` and `architecture_1` to obtain the architecture of the chip.
- pub(crate) fn architecture(self) -> Result<Architecture> {
- Architecture::try_from(
- self.architecture_0() | (self.architecture_1() << Self::ARCHITECTURE_0_RANGE.len()),
- )
+ pub(crate) fn is_older_than_fermi(self) -> bool {
+ // From https://github.com/NVIDIA/open-gpu-doc/tree/master/manuals :
+ const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u8 = 0xc;
+
+ // Older chips left arch1 zeroed out. That, combined with an arch0 value that is less than
+ // GF100, means "older than Fermi".
+ self.architecture_1() == 0 && self.architecture_0() < NV_PMC_BOOT_0_ARCHITECTURE_GF100
}
+}
+register!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture information" {
+ 15:12 minor_revision as u8, "Minor revision of the chip";
+ 19:16 major_revision as u8, "Major revision of the chip";
+ 23:20 implementation as u8, "Implementation version of the architecture";
+ 29:24 architecture as u8 ?=> Architecture, "Architecture value";
+});
+
+impl NV_PMC_BOOT_42 {
/// Combines `architecture` and `implementation` to obtain a code unique to the chipset.
pub(crate) fn chipset(self) -> Result<Chipset> {
self.architecture()
@@ -41,6 +67,24 @@ impl NV_PMC_BOOT_0 {
})
.and_then(Chipset::try_from)
}
+
+ /// Returns the raw architecture value from the register.
+ fn architecture_raw(self) -> u8 {
+ ((self.0 >> Self::ARCHITECTURE_RANGE.start()) & ((1 << Self::ARCHITECTURE_RANGE.len()) - 1))
+ as u8
+ }
+}
+
+impl kernel::fmt::Display for NV_PMC_BOOT_42 {
+ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> kernel::fmt::Result {
+ write!(
+ f,
+ "boot42 = 0x{:08x} (architecture 0x{:x}, implementation 0x{:x})",
+ self.0,
+ self.architecture_raw(),
+ self.implementation()
+ )
+ }
}
// PBUS
@@ -71,11 +115,15 @@ register!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 {
30:30 ecc_mode_enabled as bool;
});
+register!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 {
+ 31:0 address as u32;
+});
+
impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE {
/// Returns the usable framebuffer size, in bytes.
pub(crate) fn usable_fb_size(self) -> u64 {
let size = (u64::from(self.lower_mag()) << u64::from(self.lower_scale()))
- * kernel::sizes::SZ_1M as u64;
+ * u64::from_safe_cast(kernel::sizes::SZ_1M);
if self.ecc_mode_enabled() {
// Remove the amount of memory reserved for ECC (one per 16 units).
@@ -119,6 +167,12 @@ impl NV_PFB_PRI_MMU_WPR2_ADDR_HI {
// These scratch registers remain powered on even in a low-power state and have a designated group
// number.
+// Boot Sequence Interface (BSI) register used to determine
+// if GSP reload/resume has completed during the boot process.
+register!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 {
+ 26:26 boot_stage_3_handoff as bool;
+});
+
// Privilege level mask register. It dictates whether the host CPU has privilege to access the
// `PGC6_AON_SECURE_SCRATCH_GROUP_05` register (which it needs to read GFW_BOOT).
register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128,
@@ -158,7 +212,7 @@ register!(
impl NV_USABLE_FB_SIZE_IN_MB {
/// Returns the usable framebuffer size, in bytes.
pub(crate) fn usable_fb_size(self) -> u64 {
- u64::from(self.value()) * kernel::sizes::SZ_1M as u64
+ u64::from(self.value()) * u64::from_safe_cast(kernel::sizes::SZ_1M)
}
}
@@ -211,6 +265,12 @@ register!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] {
31:0 value as u32;
});
+// Used to store version information about the firmware running
+// on the Falcon processor.
+register!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] {
+ 31:0 value as u32;
+});
+
register!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] {
31:0 value as u32;
});
@@ -320,7 +380,12 @@ register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] {
// PRISCV
-register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalconBase[0x00001668] {
+register!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] {
+ 0:0 halted as bool;
+ 7:7 active_stat as bool;
+});
+
+register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalcon2Base[0x00000668] {
0:0 valid as bool;
4:4 core_select as bool => PeregrineCoreSelect;
8:8 br_fetch as bool;
diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/regs/macros.rs
index 8058e1696df9..fd1a815fa57d 100644
--- a/drivers/gpu/nova-core/regs/macros.rs
+++ b/drivers/gpu/nova-core/regs/macros.rs
@@ -8,7 +8,8 @@
//!
//! The `register!` macro in this module provides an intuitive and readable syntax for defining a
//! dedicated type for each register. Each such type comes with its own field accessors that can
-//! return an error if a field's value is invalid.
+//! return an error if a field's value is invalid. Please look at the [`bitfield`] macro for the
+//! complete syntax of fields definitions.
/// Trait providing a base address to be added to the offset of a relative register to obtain
/// its actual offset.
@@ -51,18 +52,9 @@ pub(crate) trait RegisterBase<T> {
/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
///
/// // Or, just read and update the register in a single step:
-/// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
+/// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
/// ```
///
-/// Fields are defined as follows:
-///
-/// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or
-/// `bool`. Note that `bool` fields must have a range of 1 bit.
-/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
-/// the result.
-/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
-/// and returns the result. This is useful with fields for which not all values are valid.
-///
/// The documentation strings are optional. If present, they will be added to the type's
/// definition, or the field getter and setter methods they are attached to.
///
@@ -144,15 +136,15 @@ pub(crate) trait RegisterBase<T> {
/// 0:0 start as bool, "Start the CPU core";
/// });
///
-/// // The `read`, `write` and `alter` methods of relative registers take an extra `base` argument
+/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument
/// // that is used to resolve its final address by adding its `BASE` to the offset of the
/// // register.
///
/// // Start `CPU0`.
-/// CPU_CTL::alter(bar, &CPU0, |r| r.set_start(true));
+/// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true));
///
/// // Start `CPU1`.
-/// CPU_CTL::alter(bar, &CPU1, |r| r.set_start(true));
+/// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true));
///
/// // Aliases can also be defined for relative register.
/// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {
@@ -160,7 +152,7 @@ pub(crate) trait RegisterBase<T> {
/// });
///
/// // Start the aliased `CPU0`.
-/// CPU_CTL_ALIAS::alter(bar, &CPU0, |r| r.set_alias_start(true));
+/// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true));
/// ```
///
/// ## Arrays of registers
@@ -168,7 +160,7 @@ pub(crate) trait RegisterBase<T> {
/// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas
/// can be defined as an array of identical registers, allowing them to be accessed by index with
/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
-/// an `idx` parameter to their `read`, `write` and `alter` methods:
+/// an `idx` parameter to their `read`, `write` and `update` methods:
///
/// ```no_run
/// # fn no_run() -> Result<(), Error> {
@@ -284,25 +276,25 @@ pub(crate) trait RegisterBase<T> {
macro_rules! register {
// Creates a register at a fixed offset of the MMIO space.
($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_fixed $name @ $offset);
};
// Creates an alias register of fixed offset register `alias` with its own fields.
($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_fixed $name @ $alias::OFFSET);
};
// Creates a register at a relative offset from a base address provider.
($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $offset ]);
};
// Creates an alias register of relative offset register `alias` with its own fields.
($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => {
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET ]);
};
@@ -313,7 +305,7 @@ macro_rules! register {
}
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_array $name @ $offset [ $size ; $stride ]);
};
@@ -334,7 +326,7 @@ macro_rules! register {
$(, $comment:literal)? { $($fields:tt)* }
) => {
static_assert!(::core::mem::size_of::<u32>() <= $stride);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);
};
@@ -356,7 +348,7 @@ macro_rules! register {
}
) => {
static_assert!($idx < $alias::SIZE);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );
};
@@ -365,241 +357,10 @@ macro_rules! register {
// to avoid it being interpreted in place of the relative register array alias rule.
($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => {
static_assert!($idx < $alias::SIZE);
- register!(@core $name $(, $comment)? { $($fields)* } );
+ bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );
register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
};
- // All rules below are helpers.
-
- // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
- // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
- (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
- $(
- #[doc=$comment]
- )?
- #[repr(transparent)]
- #[derive(Clone, Copy)]
- pub(crate) struct $name(u32);
-
- impl ::core::ops::BitOr for $name {
- type Output = Self;
-
- fn bitor(self, rhs: Self) -> Self::Output {
- Self(self.0 | rhs.0)
- }
- }
-
- impl ::core::convert::From<$name> for u32 {
- fn from(reg: $name) -> u32 {
- reg.0
- }
- }
-
- register!(@fields_dispatcher $name { $($fields)* });
- };
-
- // Captures the fields and passes them to all the implementers that require field information.
- //
- // Used to simplify the matching rules for implementers, so they don't need to match the entire
- // complex fields rule even though they only make use of part of it.
- (@fields_dispatcher $name:ident {
- $($hi:tt:$lo:tt $field:ident as $type:tt
- $(?=> $try_into_type:ty)?
- $(=> $into_type:ty)?
- $(, $comment:literal)?
- ;
- )*
- }
- ) => {
- register!(@field_accessors $name {
- $(
- $hi:$lo $field as $type
- $(?=> $try_into_type)?
- $(=> $into_type)?
- $(, $comment)?
- ;
- )*
- });
- register!(@debug $name { $($field;)* });
- register!(@default $name { $($field;)* });
- };
-
- // Defines all the field getter/methods methods for `$name`.
- (
- @field_accessors $name:ident {
- $($hi:tt:$lo:tt $field:ident as $type:tt
- $(?=> $try_into_type:ty)?
- $(=> $into_type:ty)?
- $(, $comment:literal)?
- ;
- )*
- }
- ) => {
- $(
- register!(@check_field_bounds $hi:$lo $field as $type);
- )*
-
- #[allow(dead_code)]
- impl $name {
- $(
- register!(@field_accessor $name $hi:$lo $field as $type
- $(?=> $try_into_type)?
- $(=> $into_type)?
- $(, $comment)?
- ;
- );
- )*
- }
- };
-
- // Boolean fields must have `$hi == $lo`.
- (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
- #[allow(clippy::eq_op)]
- const _: () = {
- ::kernel::build_assert!(
- $hi == $lo,
- concat!("boolean field `", stringify!($field), "` covers more than one bit")
- );
- };
- };
-
- // Non-boolean fields must have `$hi >= $lo`.
- (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
- #[allow(clippy::eq_op)]
- const _: () = {
- ::kernel::build_assert!(
- $hi >= $lo,
- concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
- );
- };
- };
-
- // Catches fields defined as `bool` and convert them into a boolean value.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
- $(, $comment:literal)?;
- ) => {
- register!(
- @leaf_accessor $name $hi:$lo $field
- { |f| <$into_type>::from(if f != 0 { true } else { false }) }
- $into_type => $into_type $(, $comment)?;
- );
- };
-
- // Shortcut for fields defined as `bool` without the `=>` syntax.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
- ) => {
- register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
- };
-
- // Catches the `?=>` syntax for non-boolean fields.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
- $(, $comment:literal)?;
- ) => {
- register!(@leaf_accessor $name $hi:$lo $field
- { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
- ::core::result::Result<
- $try_into_type,
- <$try_into_type as ::core::convert::TryFrom<$type>>::Error
- >
- $(, $comment)?;);
- };
-
- // Catches the `=>` syntax for non-boolean fields.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
- $(, $comment:literal)?;
- ) => {
- register!(@leaf_accessor $name $hi:$lo $field
- { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
- };
-
- // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
- (
- @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
- $(, $comment:literal)?;
- ) => {
- register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
- };
-
- // Generates the accessor methods for a single field.
- (
- @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
- { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
- ) => {
- ::kernel::macros::paste!(
- const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
- const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
- const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
- );
-
- $(
- #[doc="Returns the value of this field:"]
- #[doc=$comment]
- )?
- #[inline(always)]
- pub(crate) fn $field(self) -> $res_type {
- ::kernel::macros::paste!(
- const MASK: u32 = $name::[<$field:upper _MASK>];
- const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
- );
- let field = ((self.0 & MASK) >> SHIFT);
-
- $process(field)
- }
-
- ::kernel::macros::paste!(
- $(
- #[doc="Sets the value of this field:"]
- #[doc=$comment]
- )?
- #[inline(always)]
- pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
- const MASK: u32 = $name::[<$field:upper _MASK>];
- const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
- let value = (u32::from(value) << SHIFT) & MASK;
- self.0 = (self.0 & !MASK) | value;
-
- self
- }
- );
- };
-
- // Generates the `Debug` implementation for `$name`.
- (@debug $name:ident { $($field:ident;)* }) => {
- impl ::kernel::fmt::Debug for $name {
- fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
- f.debug_struct(stringify!($name))
- .field("<raw>", &::kernel::prelude::fmt!("{:#x}", &self.0))
- $(
- .field(stringify!($field), &self.$field())
- )*
- .finish()
- }
- }
- };
-
- // Generates the `Default` implementation for `$name`.
- (@default $name:ident { $($field:ident;)* }) => {
- /// Returns a value for the register where all fields are set to their default value.
- impl ::core::default::Default for $name {
- fn default() -> Self {
- #[allow(unused_mut)]
- let mut value = Self(Default::default());
-
- ::kernel::macros::paste!(
- $(
- value.[<set_ $field>](Default::default());
- )*
- );
-
- value
- }
- }
- };
-
// Generates the IO accessors for a fixed offset register.
(@io_fixed $name:ident @ $offset:expr) => {
#[allow(dead_code)]
@@ -625,7 +386,7 @@ macro_rules! register {
/// Read the register from its address in `io` and run `f` on its value to obtain a new
/// value to write back.
#[inline(always)]
- pub(crate) fn alter<const SIZE: usize, T, F>(
+ pub(crate) fn update<const SIZE: usize, T, F>(
io: &T,
f: F,
) where
@@ -688,7 +449,7 @@ macro_rules! register {
/// the register's offset to it, then run `f` on its value to obtain a new value to
/// write back.
#[inline(always)]
- pub(crate) fn alter<const SIZE: usize, T, B, F>(
+ pub(crate) fn update<const SIZE: usize, T, B, F>(
io: &T,
base: &B,
f: F,
@@ -746,7 +507,7 @@ macro_rules! register {
/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
/// new value to write back.
#[inline(always)]
- pub(crate) fn alter<const SIZE: usize, T, F>(
+ pub(crate) fn update<const SIZE: usize, T, F>(
io: &T,
idx: usize,
f: F,
@@ -801,7 +562,7 @@ macro_rules! register {
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
/// access was out-of-bounds.
#[inline(always)]
- pub(crate) fn try_alter<const SIZE: usize, T, F>(
+ pub(crate) fn try_update<const SIZE: usize, T, F>(
io: &T,
idx: usize,
f: F,
@@ -810,7 +571,7 @@ macro_rules! register {
F: ::core::ops::FnOnce(Self) -> Self,
{
if idx < Self::SIZE {
- Ok(Self::alter(io, idx, f))
+ Ok(Self::update(io, idx, f))
} else {
Err(EINVAL)
}
@@ -875,7 +636,7 @@ macro_rules! register {
/// by `base` and adding the register's offset to it, then run `f` on its value to
/// obtain a new value to write back.
#[inline(always)]
- pub(crate) fn alter<const SIZE: usize, T, B, F>(
+ pub(crate) fn update<const SIZE: usize, T, B, F>(
io: &T,
base: &B,
idx: usize,
@@ -939,7 +700,7 @@ macro_rules! register {
/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
/// access was out-of-bounds.
#[inline(always)]
- pub(crate) fn try_alter<const SIZE: usize, T, B, F>(
+ pub(crate) fn try_update<const SIZE: usize, T, B, F>(
io: &T,
base: &B,
idx: usize,
@@ -950,7 +711,7 @@ macro_rules! register {
F: ::core::ops::FnOnce(Self) -> Self,
{
if idx < Self::SIZE {
- Ok(Self::alter(io, base, idx, f))
+ Ok(Self::update(io, base, idx, f))
} else {
Err(EINVAL)
}
diff --git a/drivers/gpu/nova-core/sbuffer.rs b/drivers/gpu/nova-core/sbuffer.rs
new file mode 100644
index 000000000000..64758b7fae56
--- /dev/null
+++ b/drivers/gpu/nova-core/sbuffer.rs
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+
+use core::ops::Deref;
+
+use kernel::{
+ alloc::KVec,
+ prelude::*, //
+};
+
+/// A buffer abstraction for discontiguous byte slices.
+///
+/// This allows you to treat multiple non-contiguous `&mut [u8]` slices
+/// of the same length as a single stream-like read/write buffer.
+///
+/// # Examples
+///
+/// ```
+// let mut buf1 = [0u8; 5];
+/// let mut buf2 = [0u8; 5];
+/// let mut sbuffer = SBufferIter::new_writer([&mut buf1[..], &mut buf2[..]]);
+///
+/// let data = b"hi world!";
+/// sbuffer.write_all(data)?;
+/// drop(sbuffer);
+///
+/// assert_eq!(buf1, *b"hi wo");
+/// assert_eq!(buf2, *b"rld!\0");
+///
+/// # Ok::<(), Error>(())
+/// ```
+pub(crate) struct SBufferIter<I: Iterator> {
+ // [`Some`] if we are not at the end of the data yet.
+ cur_slice: Option<I::Item>,
+ // All the slices remaining after `cur_slice`.
+ slices: I,
+}
+
+impl<'a, I> SBufferIter<I>
+where
+ I: Iterator,
+{
+ /// Creates a reader buffer for a discontiguous set of byte slices.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let buf1: [u8; 5] = [0, 1, 2, 3, 4];
+ /// let buf2: [u8; 5] = [5, 6, 7, 8, 9];
+ /// let sbuffer = SBufferIter::new_reader([&buf1[..], &buf2[..]]);
+ /// let sum: u8 = sbuffer.sum();
+ /// assert_eq!(sum, 45);
+ /// ```
+ pub(crate) fn new_reader(slices: impl IntoIterator<IntoIter = I>) -> Self
+ where
+ I: Iterator<Item = &'a [u8]>,
+ {
+ Self::new(slices)
+ }
+
+ /// Creates a writeable buffer for a discontiguous set of byte slices.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let mut buf1 = [0u8; 5];
+ /// let mut buf2 = [0u8; 5];
+ /// let mut sbuffer = SBufferIter::new_writer([&mut buf1[..], &mut buf2[..]]);
+ /// sbuffer.write_all(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9][..])?;
+ /// drop(sbuffer);
+ /// assert_eq!(buf1, [0, 1, 2, 3, 4]);
+ /// assert_eq!(buf2, [5, 6, 7, 8, 9]);
+ ///
+ /// ```
+ pub(crate) fn new_writer(slices: impl IntoIterator<IntoIter = I>) -> Self
+ where
+ I: Iterator<Item = &'a mut [u8]>,
+ {
+ Self::new(slices)
+ }
+
+ fn new(slices: impl IntoIterator<IntoIter = I>) -> Self
+ where
+ I::Item: Deref<Target = [u8]>,
+ {
+ let mut slices = slices.into_iter();
+
+ Self {
+ // Skip empty slices.
+ cur_slice: slices.find(|s| !s.deref().is_empty()),
+ slices,
+ }
+ }
+
+ /// Returns a slice of at most `len` bytes, or [`None`] if we are at the end of the data.
+ ///
+ /// If a slice shorter than `len` bytes has been returned, the caller can call this method
+ /// again until it returns [`None`] to try and obtain the remainder of the data.
+ ///
+ /// The closure `f` should split the slice received in it's first parameter
+ /// at the position given in the second parameter.
+ fn get_slice_internal(
+ &mut self,
+ len: usize,
+ mut f: impl FnMut(I::Item, usize) -> (I::Item, I::Item),
+ ) -> Option<I::Item>
+ where
+ I::Item: Deref<Target = [u8]>,
+ {
+ match self.cur_slice.take() {
+ None => None,
+ Some(cur_slice) => {
+ if len >= cur_slice.len() {
+ // Caller requested more data than is in the current slice, return it entirely
+ // and prepare the following slice for being used. Skip empty slices to avoid
+ // trouble.
+ self.cur_slice = self.slices.find(|s| !s.is_empty());
+
+ Some(cur_slice)
+ } else {
+ // The current slice can satisfy the request, split it and return a slice of
+ // the requested size.
+ let (ret, next) = f(cur_slice, len);
+ self.cur_slice = Some(next);
+
+ Some(ret)
+ }
+ }
+ }
+ }
+
+ /// Returns whether this buffer still has data available.
+ pub(crate) fn is_empty(&self) -> bool {
+ self.cur_slice.is_none()
+ }
+}
+
+/// Provides a way to get non-mutable slices of data to read from.
+impl<'a, I> SBufferIter<I>
+where
+ I: Iterator<Item = &'a [u8]>,
+{
+ /// Returns a slice of at most `len` bytes, or [`None`] if we are at the end of the data.
+ ///
+ /// If a slice shorter than `len` bytes has been returned, the caller can call this method
+ /// again until it returns [`None`] to try and obtain the remainder of the data.
+ fn get_slice(&mut self, len: usize) -> Option<&'a [u8]> {
+ self.get_slice_internal(len, |s, pos| s.split_at(pos))
+ }
+
+ /// Ideally we would implement `Read`, but it is not available in `core`.
+ /// So mimic `std::io::Read::read_exact`.
+ #[expect(unused)]
+ pub(crate) fn read_exact(&mut self, mut dst: &mut [u8]) -> Result {
+ while !dst.is_empty() {
+ match self.get_slice(dst.len()) {
+ None => return Err(EINVAL),
+ Some(src) => {
+ let dst_slice;
+ (dst_slice, dst) = dst.split_at_mut(src.len());
+ dst_slice.copy_from_slice(src);
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Read all the remaining data into a [`KVec`].
+ ///
+ /// `self` will be empty after this operation.
+ pub(crate) fn flush_into_kvec(&mut self, flags: kernel::alloc::Flags) -> Result<KVec<u8>> {
+ let mut buf = KVec::<u8>::new();
+
+ if let Some(slice) = core::mem::take(&mut self.cur_slice) {
+ buf.extend_from_slice(slice, flags)?;
+ }
+ for slice in &mut self.slices {
+ buf.extend_from_slice(slice, flags)?;
+ }
+
+ Ok(buf)
+ }
+}
+
+/// Provides a way to get mutable slices of data to write into.
+impl<'a, I> SBufferIter<I>
+where
+ I: Iterator<Item = &'a mut [u8]>,
+{
+ /// Returns a mutable slice of at most `len` bytes, or [`None`] if we are at the end of the
+ /// data.
+ ///
+ /// If a slice shorter than `len` bytes has been returned, the caller can call this method
+ /// again until it returns `None` to try and obtain the remainder of the data.
+ fn get_slice_mut(&mut self, len: usize) -> Option<&'a mut [u8]> {
+ self.get_slice_internal(len, |s, pos| s.split_at_mut(pos))
+ }
+
+ /// Ideally we would implement [`Write`], but it is not available in `core`.
+ /// So mimic `std::io::Write::write_all`.
+ pub(crate) fn write_all(&mut self, mut src: &[u8]) -> Result {
+ while !src.is_empty() {
+ match self.get_slice_mut(src.len()) {
+ None => return Err(ETOOSMALL),
+ Some(dst) => {
+ let src_slice;
+ (src_slice, src) = src.split_at(dst.len());
+ dst.copy_from_slice(src_slice);
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl<'a, I> Iterator for SBufferIter<I>
+where
+ I: Iterator<Item = &'a [u8]>,
+{
+ type Item = u8;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ // Returned slices are guaranteed to not be empty so we can safely index the first entry.
+ self.get_slice(1).map(|s| s[0])
+ }
+}
diff --git a/drivers/gpu/nova-core/util.rs b/drivers/gpu/nova-core/util.rs
index bf35f00cb732..4b503249a3ef 100644
--- a/drivers/gpu/nova-core/util.rs
+++ b/drivers/gpu/nova-core/util.rs
@@ -1,27 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
-use kernel::prelude::*;
-use kernel::time::{Delta, Instant, Monotonic};
-
-/// Wait until `cond` is true or `timeout` elapsed.
-///
-/// When `cond` evaluates to `Some`, its return value is returned.
+/// Converts a null-terminated byte slice to a string, or `None` if the array does not
+/// contains any null byte or contains invalid characters.
///
-/// `Err(ETIMEDOUT)` is returned if `timeout` has been reached without `cond` evaluating to
-/// `Some`.
-///
-/// TODO[DLAY]: replace with `read_poll_timeout` once it is available.
-/// (https://lore.kernel.org/lkml/20250220070611.214262-8-fujita.tomonori@gmail.com/)
-pub(crate) fn wait_on<R, F: Fn() -> Option<R>>(timeout: Delta, cond: F) -> Result<R> {
- let start_time = Instant::<Monotonic>::now();
-
- loop {
- if let Some(ret) = cond() {
- return Ok(ret);
- }
+/// Contrary to [`kernel::str::CStr::from_bytes_with_nul`], the null byte can be anywhere in the
+/// slice, and not only in the last position.
+pub(crate) fn str_from_null_terminated(bytes: &[u8]) -> Option<&str> {
+ use kernel::str::CStr;
- if start_time.elapsed().as_nanos() > timeout.as_nanos() {
- return Err(ETIMEDOUT);
- }
- }
+ bytes
+ .iter()
+ .position(|&b| b == 0)
+ .and_then(|null_pos| CStr::from_bytes_with_nul(&bytes[..=null_pos]).ok())
+ .and_then(|cstr| cstr.to_str().ok())
}
diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
index 71fbe71b84db..abf423560ff4 100644
--- a/drivers/gpu/nova-core/vbios.rs
+++ b/drivers/gpu/nova-core/vbios.rs
@@ -2,15 +2,27 @@
//! VBIOS extraction and parsing.
-use crate::driver::Bar0;
-use crate::firmware::fwsec::Bcrt30Rsa3kSignature;
-use crate::firmware::FalconUCodeDescV3;
use core::convert::TryFrom;
-use kernel::device;
-use kernel::error::Result;
-use kernel::prelude::*;
-use kernel::ptr::{Alignable, Alignment};
-use kernel::types::ARef;
+
+use kernel::{
+ device,
+ prelude::*,
+ ptr::{
+ Alignable,
+ Alignment, //
+ },
+ transmute::FromBytes,
+ types::ARef,
+};
+
+use crate::{
+ driver::Bar0,
+ firmware::{
+ fwsec::Bcrt30Rsa3kSignature,
+ FalconUCodeDescV3, //
+ },
+ num::FromSafeCast,
+};
/// The offset of the VBIOS ROM in the BAR0 space.
const ROM_OFFSET: usize = 0x300000;
@@ -22,6 +34,34 @@ const BIOS_READ_AHEAD_SIZE: usize = 1024;
/// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit.
const LAST_IMAGE_BIT_MASK: u8 = 0x80;
+/// BIOS Image Type from PCI Data Structure code_type field.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u8)]
+enum BiosImageType {
+ /// PC-AT compatible BIOS image (x86 legacy)
+ PciAt = 0x00,
+ /// EFI (Extensible Firmware Interface) BIOS image
+ Efi = 0x03,
+ /// NBSI (Notebook System Information) BIOS image
+ Nbsi = 0x70,
+ /// FwSec (Firmware Security) BIOS image
+ FwSec = 0xE0,
+}
+
+impl TryFrom<u8> for BiosImageType {
+ type Error = Error;
+
+ fn try_from(code: u8) -> Result<Self> {
+ match code {
+ 0x00 => Ok(Self::PciAt),
+ 0x03 => Ok(Self::Efi),
+ 0x70 => Ok(Self::Nbsi),
+ 0xE0 => Ok(Self::FwSec),
+ _ => Err(EINVAL),
+ }
+ }
+}
+
// PMU lookup table entry types. Used to locate PMU table entries
// in the Fwsec image, corresponding to falcon ucodes.
#[expect(dead_code)]
@@ -197,32 +237,37 @@ impl Vbios {
// Parse all VBIOS images in the ROM
for image_result in VbiosIterator::new(dev, bar0)? {
- let full_image = image_result?;
+ let image = image_result?;
dev_dbg!(
dev,
- "Found BIOS image: size: {:#x}, type: {}, last: {}\n",
- full_image.image_size_bytes(),
- full_image.image_type_str(),
- full_image.is_last()
+ "Found BIOS image: size: {:#x}, type: {:?}, last: {}\n",
+ image.image_size_bytes(),
+ image.image_type(),
+ image.is_last()
);
- // Get references to images we will need after the loop, in order to
- // setup the falcon data offset.
- match full_image {
- BiosImage::PciAt(image) => {
- pci_at_image = Some(image);
+ // Convert to a specific image type
+ match BiosImageType::try_from(image.pcir.code_type) {
+ Ok(BiosImageType::PciAt) => {
+ pci_at_image = Some(PciAtBiosImage::try_from(image)?);
}
- BiosImage::FwSec(image) => {
+ Ok(BiosImageType::FwSec) => {
+ let fwsec = FwSecBiosBuilder {
+ base: image,
+ falcon_data_offset: None,
+ pmu_lookup_table: None,
+ falcon_ucode_offset: None,
+ };
if first_fwsec_image.is_none() {
- first_fwsec_image = Some(image);
+ first_fwsec_image = Some(fwsec);
} else {
- second_fwsec_image = Some(image);
+ second_fwsec_image = Some(fwsec);
}
}
- // For now we don't need to handle these
- BiosImage::Efi(_image) => {}
- BiosImage::Nbsi(_image) => {}
+ _ => {
+ // Ignore other image types or unknown types
+ }
}
}
@@ -280,45 +325,29 @@ struct PcirStruct {
max_runtime_image_len: u16,
}
+// SAFETY: all bit patterns are valid for `PcirStruct`.
+unsafe impl FromBytes for PcirStruct {}
+
impl PcirStruct {
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
- if data.len() < core::mem::size_of::<PcirStruct>() {
- dev_err!(dev, "Not enough data for PcirStruct\n");
- return Err(EINVAL);
- }
-
- let mut signature = [0u8; 4];
- signature.copy_from_slice(&data[0..4]);
+ let (pcir, _) = PcirStruct::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
// Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e).
- if &signature != b"PCIR" && &signature != b"NPDS" {
- dev_err!(dev, "Invalid signature for PcirStruct: {:?}\n", signature);
+ if &pcir.signature != b"PCIR" && &pcir.signature != b"NPDS" {
+ dev_err!(
+ dev,
+ "Invalid signature for PcirStruct: {:?}\n",
+ pcir.signature
+ );
return Err(EINVAL);
}
- let mut class_code = [0u8; 3];
- class_code.copy_from_slice(&data[13..16]);
-
- let image_len = u16::from_le_bytes([data[16], data[17]]);
- if image_len == 0 {
+ if pcir.image_len == 0 {
dev_err!(dev, "Invalid image length: 0\n");
return Err(EINVAL);
}
- Ok(PcirStruct {
- signature,
- vendor_id: u16::from_le_bytes([data[4], data[5]]),
- device_id: u16::from_le_bytes([data[6], data[7]]),
- device_list_ptr: u16::from_le_bytes([data[8], data[9]]),
- pci_data_struct_len: u16::from_le_bytes([data[10], data[11]]),
- pci_data_struct_rev: data[12],
- class_code,
- image_len,
- vendor_rom_rev: u16::from_le_bytes([data[18], data[19]]),
- code_type: data[20],
- last_image: data[21],
- max_runtime_image_len: u16::from_le_bytes([data[22], data[23]]),
- })
+ Ok(pcir)
}
/// Check if this is the last image in the ROM.
@@ -328,7 +357,7 @@ impl PcirStruct {
/// Calculate image size in bytes from 512-byte blocks.
fn image_size_bytes(&self) -> usize {
- self.image_len as usize * 512
+ usize::from(self.image_len) * 512
}
}
@@ -356,30 +385,19 @@ struct BitHeader {
checksum: u8,
}
+// SAFETY: all bit patterns are valid for `BitHeader`.
+unsafe impl FromBytes for BitHeader {}
+
impl BitHeader {
fn new(data: &[u8]) -> Result<Self> {
- if data.len() < core::mem::size_of::<Self>() {
- return Err(EINVAL);
- }
-
- let mut signature = [0u8; 4];
- signature.copy_from_slice(&data[2..6]);
+ let (header, _) = BitHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
// Check header ID and signature
- let id = u16::from_le_bytes([data[0], data[1]]);
- if id != 0xB8FF || &signature != b"BIT\0" {
+ if header.id != 0xB8FF || &header.signature != b"BIT\0" {
return Err(EINVAL);
}
- Ok(BitHeader {
- id,
- signature,
- bcd_version: u16::from_le_bytes([data[6], data[7]]),
- header_size: data[8],
- token_size: data[9],
- token_entries: data[10],
- checksum: data[11],
- })
+ Ok(header)
}
}
@@ -406,13 +424,13 @@ impl BitToken {
let header = &image.bit_header;
// Offset to the first token entry
- let tokens_start = image.bit_offset + header.header_size as usize;
+ let tokens_start = image.bit_offset + usize::from(header.header_size);
- for i in 0..header.token_entries as usize {
- let entry_offset = tokens_start + (i * header.token_size as usize);
+ for i in 0..usize::from(header.token_entries) {
+ let entry_offset = tokens_start + (i * usize::from(header.token_size));
// Make sure we don't go out of bounds
- if entry_offset + header.token_size as usize > image.base.data.len() {
+ if entry_offset + usize::from(header.token_size) > image.base.data.len() {
return Err(EINVAL);
}
@@ -530,35 +548,29 @@ struct NpdeStruct {
last_image: u8,
}
+// SAFETY: all bit patterns are valid for `NpdeStruct`.
+unsafe impl FromBytes for NpdeStruct {}
+
impl NpdeStruct {
fn new(dev: &device::Device, data: &[u8]) -> Option<Self> {
- if data.len() < core::mem::size_of::<Self>() {
- dev_dbg!(dev, "Not enough data for NpdeStruct\n");
- return None;
- }
-
- let mut signature = [0u8; 4];
- signature.copy_from_slice(&data[0..4]);
+ let (npde, _) = NpdeStruct::from_bytes_copy_prefix(data)?;
// Signature should be "NPDE" (0x4544504E).
- if &signature != b"NPDE" {
- dev_dbg!(dev, "Invalid signature for NpdeStruct: {:?}\n", signature);
+ if &npde.signature != b"NPDE" {
+ dev_dbg!(
+ dev,
+ "Invalid signature for NpdeStruct: {:?}\n",
+ npde.signature
+ );
return None;
}
- let subimage_len = u16::from_le_bytes([data[8], data[9]]);
- if subimage_len == 0 {
+ if npde.subimage_len == 0 {
dev_dbg!(dev, "Invalid subimage length: 0\n");
return None;
}
- Some(NpdeStruct {
- signature,
- npci_data_ext_rev: u16::from_le_bytes([data[4], data[5]]),
- npci_data_ext_len: u16::from_le_bytes([data[6], data[7]]),
- subimage_len,
- last_image: data[10],
- })
+ Some(npde)
}
/// Check if this is the last image in the ROM.
@@ -568,7 +580,7 @@ impl NpdeStruct {
/// Calculate image size in bytes from 512-byte blocks.
fn image_size_bytes(&self) -> usize {
- self.subimage_len as usize * 512
+ usize::from(self.subimage_len) * 512
}
/// Try to find NPDE in the data, the NPDE is right after the PCIR.
@@ -580,8 +592,8 @@ impl NpdeStruct {
) -> Option<Self> {
// Calculate the offset where NPDE might be located
// NPDE should be right after the PCIR structure, aligned to 16 bytes
- let pcir_offset = rom_header.pci_data_struct_offset as usize;
- let npde_start = (pcir_offset + pcir.pci_data_struct_len as usize + 0x0F) & !0x0F;
+ let pcir_offset = usize::from(rom_header.pci_data_struct_offset);
+ let npde_start = (pcir_offset + usize::from(pcir.pci_data_struct_len) + 0x0F) & !0x0F;
// Check if we have enough data
if npde_start + core::mem::size_of::<Self>() > data.len() {
@@ -594,108 +606,29 @@ impl NpdeStruct {
}
}
-// Use a macro to implement BiosImage enum and methods. This avoids having to
-// repeat each enum type when implementing functions like base() in BiosImage.
-macro_rules! bios_image {
- (
- $($variant:ident: $class:ident),* $(,)?
- ) => {
- // BiosImage enum with variants for each image type
- enum BiosImage {
- $($variant($class)),*
- }
-
- impl BiosImage {
- /// Get a reference to the common BIOS image data regardless of type
- fn base(&self) -> &BiosImageBase {
- match self {
- $(Self::$variant(img) => &img.base),*
- }
- }
-
- /// Returns a string representing the type of BIOS image
- fn image_type_str(&self) -> &'static str {
- match self {
- $(Self::$variant(_) => stringify!($variant)),*
- }
- }
- }
- }
-}
-
-impl BiosImage {
- /// Check if this is the last image.
- fn is_last(&self) -> bool {
- let base = self.base();
-
- // For NBSI images (type == 0x70), return true as they're
- // considered the last image
- if matches!(self, Self::Nbsi(_)) {
- return true;
- }
-
- // For other image types, check the NPDE first if available
- if let Some(ref npde) = base.npde {
- return npde.is_last();
- }
-
- // Otherwise, fall back to checking the PCIR last_image flag
- base.pcir.is_last()
- }
-
- /// Get the image size in bytes.
- fn image_size_bytes(&self) -> usize {
- let base = self.base();
-
- // Prefer NPDE image size if available
- if let Some(ref npde) = base.npde {
- return npde.image_size_bytes();
- }
-
- // Otherwise, fall back to the PCIR image size
- base.pcir.image_size_bytes()
- }
-
- /// Create a [`BiosImageBase`] from a byte slice and convert it to a [`BiosImage`] which
- /// triggers the constructor of the specific BiosImage enum variant.
- fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
- let base = BiosImageBase::new(dev, data)?;
- let image = base.into_image().inspect_err(|e| {
- dev_err!(dev, "Failed to create BiosImage: {:?}\n", e);
- })?;
-
- Ok(image)
- }
-}
-
-bios_image! {
- PciAt: PciAtBiosImage, // PCI-AT compatible BIOS image
- Efi: EfiBiosImage, // EFI (Extensible Firmware Interface)
- Nbsi: NbsiBiosImage, // NBSI (Nvidia Bios System Interface)
- FwSec: FwSecBiosBuilder, // FWSEC (Firmware Security)
-}
-
/// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain.
///
/// It contains the BIT header and the BIT tokens.
struct PciAtBiosImage {
- base: BiosImageBase,
+ base: BiosImage,
bit_header: BitHeader,
bit_offset: usize,
}
+#[expect(dead_code)]
struct EfiBiosImage {
- base: BiosImageBase,
+ base: BiosImage,
// EFI-specific fields can be added here in the future.
}
+#[expect(dead_code)]
struct NbsiBiosImage {
- base: BiosImageBase,
+ base: BiosImage,
// NBSI-specific fields can be added here in the future.
}
struct FwSecBiosBuilder {
- base: BiosImageBase,
+ base: BiosImage,
/// These are temporary fields that are used during the construction of the
/// [`FwSecBiosBuilder`].
///
@@ -714,37 +647,16 @@ struct FwSecBiosBuilder {
///
/// The PMU table contains voltage/frequency tables as well as a pointer to the Falcon Ucode.
pub(crate) struct FwSecBiosImage {
- base: BiosImageBase,
+ base: BiosImage,
/// The offset of the Falcon ucode.
falcon_ucode_offset: usize,
}
-// Convert from BiosImageBase to BiosImage
-impl TryFrom<BiosImageBase> for BiosImage {
- type Error = Error;
-
- fn try_from(base: BiosImageBase) -> Result<Self> {
- match base.pcir.code_type {
- 0x00 => Ok(BiosImage::PciAt(base.try_into()?)),
- 0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })),
- 0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })),
- 0xE0 => Ok(BiosImage::FwSec(FwSecBiosBuilder {
- base,
- falcon_data_offset: None,
- pmu_lookup_table: None,
- falcon_ucode_offset: None,
- })),
- _ => Err(EINVAL),
- }
- }
-}
-
/// BIOS Image structure containing various headers and reference fields to all BIOS images.
///
-/// Each BiosImage type has a BiosImageBase type along with other image-specific fields. Note that
-/// Rust favors composition of types over inheritance.
+/// A BiosImage struct is embedded into all image types and implements common operations.
#[expect(dead_code)]
-struct BiosImageBase {
+struct BiosImage {
/// Used for logging.
dev: ARef<device::Device>,
/// PCI ROM Expansion Header
@@ -757,12 +669,40 @@ struct BiosImageBase {
data: KVec<u8>,
}
-impl BiosImageBase {
- fn into_image(self) -> Result<BiosImage> {
- BiosImage::try_from(self)
+impl BiosImage {
+ /// Get the image size in bytes.
+ fn image_size_bytes(&self) -> usize {
+ // Prefer NPDE image size if available
+ if let Some(ref npde) = self.npde {
+ npde.image_size_bytes()
+ } else {
+ // Otherwise, fall back to the PCIR image size
+ self.pcir.image_size_bytes()
+ }
+ }
+
+ /// Get the BIOS image type.
+ fn image_type(&self) -> Result<BiosImageType> {
+ BiosImageType::try_from(self.pcir.code_type)
+ }
+
+ /// Check if this is the last image.
+ fn is_last(&self) -> bool {
+ // For NBSI images, return true as they're considered the last image.
+ if self.image_type() == Ok(BiosImageType::Nbsi) {
+ return true;
+ }
+
+ // For other image types, check the NPDE first if available
+ if let Some(ref npde) = self.npde {
+ return npde.is_last();
+ }
+
+ // Otherwise, fall back to checking the PCIR last_image flag
+ self.pcir.is_last()
}
- /// Creates a new BiosImageBase from raw byte data.
+ /// Creates a new BiosImage from raw byte data.
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
// Ensure we have enough data for the ROM header.
if data.len() < 26 {
@@ -775,7 +715,7 @@ impl BiosImageBase {
.inspect_err(|e| dev_err!(dev, "Failed to create PciRomHeader: {:?}\n", e))?;
// Get the PCI Data Structure using the pointer from the ROM header.
- let pcir_offset = rom_header.pci_data_struct_offset as usize;
+ let pcir_offset = usize::from(rom_header.pci_data_struct_offset);
let pcir_data = data
.get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>())
.ok_or(EINVAL)
@@ -802,7 +742,7 @@ impl BiosImageBase {
let mut data_copy = KVec::new();
data_copy.extend_from_slice(data, GFP_KERNEL)?;
- Ok(BiosImageBase {
+ Ok(BiosImage {
dev: dev.into(),
rom_header,
pcir,
@@ -843,12 +783,12 @@ impl PciAtBiosImage {
let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?;
// Make sure we don't go out of bounds
- if token.data_offset as usize + 4 > self.base.data.len() {
+ if usize::from(token.data_offset) + 4 > self.base.data.len() {
return Err(EINVAL);
}
// read the 4 bytes at the offset specified in the token
- let offset = token.data_offset as usize;
+ let offset = usize::from(token.data_offset);
let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| {
dev_err!(self.base.dev, "Failed to convert data slice to array");
EINVAL
@@ -856,7 +796,7 @@ impl PciAtBiosImage {
let data_ptr = u32::from_le_bytes(bytes);
- if (data_ptr as usize) < self.base.data.len() {
+ if (usize::from_safe_cast(data_ptr)) < self.base.data.len() {
dev_err!(self.base.dev, "Falcon data pointer out of bounds\n");
return Err(EINVAL);
}
@@ -865,10 +805,10 @@ impl PciAtBiosImage {
}
}
-impl TryFrom<BiosImageBase> for PciAtBiosImage {
+impl TryFrom<BiosImage> for PciAtBiosImage {
type Error = Error;
- fn try_from(base: BiosImageBase) -> Result<Self> {
+ fn try_from(base: BiosImage) -> Result<Self> {
let data_slice = &base.data;
let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?;
@@ -904,29 +844,34 @@ impl PmuLookupTableEntry {
}
}
+#[repr(C)]
+struct PmuLookupTableHeader {
+ version: u8,
+ header_len: u8,
+ entry_len: u8,
+ entry_count: u8,
+}
+
+// SAFETY: all bit patterns are valid for `PmuLookupTableHeader`.
+unsafe impl FromBytes for PmuLookupTableHeader {}
+
/// The [`PmuLookupTableEntry`] structure is used to find the [`PmuLookupTableEntry`] for a given
/// application ID.
///
/// The table of entries is pointed to by the falcon data pointer in the BIT table, and is used to
/// locate the Falcon Ucode.
-#[expect(dead_code)]
struct PmuLookupTable {
- version: u8,
- header_len: u8,
- entry_len: u8,
- entry_count: u8,
+ header: PmuLookupTableHeader,
table_data: KVec<u8>,
}
impl PmuLookupTable {
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
- if data.len() < 4 {
- return Err(EINVAL);
- }
+ let (header, _) = PmuLookupTableHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
- let header_len = data[1] as usize;
- let entry_len = data[2] as usize;
- let entry_count = data[3] as usize;
+ let header_len = usize::from(header.header_len);
+ let entry_len = usize::from(header.entry_len);
+ let entry_count = usize::from(header.entry_count);
let required_bytes = header_len + (entry_count * entry_len);
@@ -947,27 +892,21 @@ impl PmuLookupTable {
dev_dbg!(dev, "PMU entry: {:02x?}\n", &data[i..][..entry_len]);
}
- Ok(PmuLookupTable {
- version: data[0],
- header_len: header_len as u8,
- entry_len: entry_len as u8,
- entry_count: entry_count as u8,
- table_data,
- })
+ Ok(PmuLookupTable { header, table_data })
}
fn lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry> {
- if idx >= self.entry_count {
+ if idx >= self.header.entry_count {
return Err(EINVAL);
}
- let index = (idx as usize) * self.entry_len as usize;
+ let index = (usize::from(idx)) * usize::from(self.header.entry_len);
PmuLookupTableEntry::new(&self.table_data[index..])
}
// find entry by type value
fn find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry> {
- for i in 0..self.entry_count {
+ for i in 0..self.header.entry_count {
let entry = self.lookup_index(i)?;
if entry.application_id == entry_type {
return Ok(entry);
@@ -984,7 +923,7 @@ impl FwSecBiosBuilder {
pci_at_image: &PciAtBiosImage,
first_fwsec: &FwSecBiosBuilder,
) -> Result {
- let mut offset = pci_at_image.falcon_data_ptr()? as usize;
+ let mut offset = usize::from_safe_cast(pci_at_image.falcon_data_ptr()?);
let mut pmu_in_first_fwsec = false;
// The falcon data pointer assumes that the PciAt and FWSEC images
@@ -1025,7 +964,7 @@ impl FwSecBiosBuilder {
.find_entry_by_type(FALCON_UCODE_ENTRY_APPID_FWSEC_PROD)
{
Ok(entry) => {
- let mut ucode_offset = entry.data as usize;
+ let mut ucode_offset = usize::from_safe_cast(entry.data);
ucode_offset -= pci_at_image.base.data.len();
if ucode_offset < first_fwsec.base.data.len() {
dev_err!(self.base.dev, "Falcon Ucode offset not in second Fwsec.\n");
@@ -1111,7 +1050,7 @@ impl FwSecBiosImage {
// The ucode data follows the descriptor.
let ucode_data_offset = falcon_ucode_offset + desc.size();
- let size = (desc.imem_load_size + desc.dmem_load_size) as usize;
+ let size = usize::from_safe_cast(desc.imem_load_size + desc.dmem_load_size);
// Get the data slice, checking bounds in a single operation.
self.base
@@ -1130,8 +1069,8 @@ impl FwSecBiosImage {
pub(crate) fn sigs(&self, desc: &FalconUCodeDescV3) -> Result<&[Bcrt30Rsa3kSignature]> {
// The signatures data follows the descriptor.
let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>();
- let sigs_size =
- desc.signature_count as usize * core::mem::size_of::<Bcrt30Rsa3kSignature>();
+ let sigs_count = usize::from(desc.signature_count);
+ let sigs_size = sigs_count * core::mem::size_of::<Bcrt30Rsa3kSignature>();
// Make sure the data is within bounds.
if sigs_data_offset + sigs_size > self.base.data.len() {
@@ -1151,7 +1090,7 @@ impl FwSecBiosImage {
.as_ptr()
.add(sigs_data_offset)
.cast::<Bcrt30Rsa3kSignature>(),
- desc.signature_count as usize,
+ sigs_count,
)
})
}
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 04420a713be0..920a64b66b25 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -383,6 +383,7 @@ config HID_EVISION
help
Support for some EVision keyboards. Note that this is needed only when
applying customization using userspace programs.
+ Support for some EVision devices requiring report descriptor fixups.
config HID_EZKEY
tristate "Ezkey BTC 8193 keyboard"
@@ -1318,6 +1319,8 @@ config HID_WINWING
help
Support for WinWing Orion2 throttle base with the following grips:
+ * TGRIP-15E
+ * TGRIP-15EX
* TGRIP-16EX
* TGRIP-18
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 0a9b44ce4904..b0bab2a1ddcc 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
@@ -194,6 +194,8 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
if (rc)
goto cleanup;
+ mp2_ops->stop(privdata, cl_data->sensor_idx[i]);
+ amd_sfh_wait_for_response(privdata, cl_data->sensor_idx[i], DISABLE_SENSOR);
writel(0, privdata->mmio + amd_get_p2c_val(privdata, 0));
mp2_ops->start(privdata, info);
status = amd_sfh_wait_for_response
diff --git a/drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c b/drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c
new file mode 100644
index 000000000000..183d408d893a
--- /dev/null
+++ b/drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c
@@ -0,0 +1,563 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2024 Red Hat, Inc
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include "hid_report_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_HUION 0x256C
+#define PID_INSPIROY_2_M 0x0067
+
+HID_BPF_CONFIG(
+ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_INSPIROY_2_M),
+);
+
+/* Filled in by udev-hid-bpf */
+char UDEV_PROP_HUION_FIRMWARE_ID[64];
+
+/* The prefix of the firmware ID we expect for this device. The full firmware
+ * string has a date suffix, e.g. HUION_T21j_221221
+ */
+char EXPECTED_FIRMWARE_ID[] = "HUION_T21k_";
+
+/* How this BPF program works: the tablet has two modes, firmware mode and
+ * tablet mode. In firmware mode (out of the box) the tablet sends button events
+ * and the dial as keyboard combinations. In tablet mode it uses a vendor specific
+ * hid report to report everything instead.
+ * Depending on the mode some hid reports are never sent and the corresponding
+ * devices are mute.
+ *
+ * To switch the tablet use e.g. https://github.com/whot/huion-switcher
+ * or one of the tools from the digimend project
+ *
+ * This BPF works for both modes. The huion-switcher tool sets the
+ * HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware
+ * pad and pen reports (by making them vendor collections that are ignored).
+ * If that property is not set we fix all hidraw nodes so the tablet works in
+ * either mode though the drawback is that the device will show up twice if
+ * you bind it to all event nodes
+ *
+ * Default report descriptor for the first exposed hidraw node:
+ *
+ * # HUION Huion Tablet_H641P
+ * # Report descriptor length: 18 bytes
+ * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 0xFF00) 0
+ * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 3
+ * # 0xa1, 0x01, // Collection (Application) 5
+ * # 0x85, 0x08, // Report ID (8) 7
+ * # 0x75, 0x58, // Report Size (88) 9
+ * # 0x95, 0x01, // Report Count (1) 11
+ * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 13
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 15
+ * # 0xc0, // End Collection 17
+ * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c0
+ *
+ * This rdesc does nothing until the tablet is switched to raw mode, see
+ * https://github.com/whot/huion-switcher
+ *
+ *
+ * Second hidraw node is the Pen. This one sends events until the tablet is
+ * switched to raw mode, then it's mute.
+ *
+ * # Report descriptor length: 93 bytes
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 0
+ * # 0x09, 0x02, // Usage (Pen) 2
+ * # 0xa1, 0x01, // Collection (Application) 4
+ * # 0x85, 0x0a, // Report ID (10) 6
+ * # 0x09, 0x20, // Usage (Stylus) 8
+ * # 0xa1, 0x01, // Collection (Application) 10
+ * # 0x09, 0x42, // Usage (Tip Switch) 12
+ * # 0x09, 0x44, // Usage (Barrel Switch) 14
+ * # 0x09, 0x45, // Usage (Eraser) 16
+ * # 0x09, 0x3c, // Usage (Invert) 18 <-- has no Invert eraser
+ * # 0x15, 0x00, // Logical Minimum (0) 20
+ * # 0x25, 0x01, // Logical Maximum (1) 22
+ * # 0x75, 0x01, // Report Size (1) 24
+ * # 0x95, 0x06, // Report Count (6) 26
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 28
+ * # 0x09, 0x32, // Usage (In Range) 30
+ * # 0x75, 0x01, // Report Size (1) 32
+ * # 0x95, 0x01, // Report Count (1) 34
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 36
+ * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 40
+ * # 0x09, 0x30, // Usage (X) 42
+ * # 0x09, 0x31, // Usage (Y) 44
+ * # 0x55, 0x0d, // Unit Exponent (-3) 46 <-- change to -2
+ * # 0x65, 0x33, // Unit (EnglishLinear: in³) 48 <-- change in³ to in
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 50
+ * # 0x35, 0x00, // Physical Minimum (0) 53
+ * # 0x46, 0x00, 0x08, // Physical Maximum (2048) 55 <-- invalid size
+ * # 0x75, 0x10, // Report Size (16) 58
+ * # 0x95, 0x02, // Report Count (2) 60
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 62
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 64
+ * # 0x09, 0x30, // Usage (Tip Pressure) 66
+ * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 68
+ * # 0x75, 0x10, // Report Size (16) 71
+ * # 0x95, 0x01, // Report Count (1) 73
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 75
+ * # 0x09, 0x3d, // Usage (X Tilt) 77 <-- No tilt reported
+ * # 0x09, 0x3e, // Usage (Y Tilt) 79
+ * # 0x15, 0x81, // Logical Minimum (-127) 81
+ * # 0x25, 0x7f, // Logical Maximum (127) 83
+ * # 0x75, 0x08, // Report Size (8) 85
+ * # 0x95, 0x02, // Report Count (2) 87
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 89
+ * # 0xc0, // End Collection 91
+ * # 0xc0, // End Collection 92
+ * R: 93 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 45 09 3c 15 00 25 01 7501 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 09 3d09 3e 15 81 25 7f 75 08 95 02 81 02 c0 c0
+ *
+ * Third hidraw node is the pad which sends a combination of keyboard shortcuts until
+ * the tablet is switched to raw mode, then it's mute:
+ *
+ * # Report descriptor length: 65 bytes
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 0
+ * # 0x09, 0x06, // Usage (Keyboard) 2
+ * # 0xa1, 0x01, // Collection (Application) 4
+ * # 0x85, 0x03, // Report ID (3) 6
+ * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 8
+ * # 0x19, 0xe0, // UsageMinimum (224) 10
+ * # 0x29, 0xe7, // UsageMaximum (231) 12
+ * # 0x15, 0x00, // Logical Minimum (0) 14
+ * # 0x25, 0x01, // Logical Maximum (1) 16
+ * # 0x75, 0x01, // Report Size (1) 18
+ * # 0x95, 0x08, // Report Count (8) 20
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 22
+ * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 24
+ * # 0x19, 0x00, // UsageMinimum (0) 26
+ * # 0x29, 0xff, // UsageMaximum (255) 28
+ * # 0x26, 0xff, 0x00, // Logical Maximum (255) 30
+ * # 0x75, 0x08, // Report Size (8) 33
+ * # 0x95, 0x06, // Report Count (6) 35
+ * # 0x81, 0x00, // Input (Data,Arr,Abs) 37
+ * # 0xc0, // End Collection 39
+ * # 0x05, 0x0c, // Usage Page (Consumer) 40
+ * # 0x09, 0x01, // Usage (Consumer Control) 42
+ * # 0xa1, 0x01, // Collection (Application) 44
+ * # 0x85, 0x04, // Report ID (4) 46
+ * # 0x19, 0x00, // UsageMinimum (0) 48
+ * # 0x2a, 0x3c, 0x02, // UsageMaximum (572) 50
+ * # 0x15, 0x00, // Logical Minimum (0) 53
+ * # 0x26, 0x3c, 0x02, // Logical Maximum (572) 55
+ * # 0x95, 0x01, // Report Count (1) 58
+ * # 0x75, 0x10, // Report Size (16) 60
+ * # 0x81, 0x00, // Input (Data,Arr,Abs) 62
+ * # 0xc0, // End Collection 64
+ * R: 65 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 0507 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0 05 0c 09 01 a1 01 85 04 19 00 2a 3c02 15 00 26 3c 02 95 01 75 10 81 00 c0
+ * N: HUION Huion Tablet_H641P
+ */
+
+#define PAD_REPORT_DESCRIPTOR_LENGTH 133
+#define PEN_REPORT_DESCRIPTOR_LENGTH 93
+#define VENDOR_REPORT_DESCRIPTOR_LENGTH 36
+#define PAD_REPORT_ID 3
+#define PEN_REPORT_ID 10
+#define VENDOR_REPORT_ID 8
+#define PAD_REPORT_LENGTH 8
+#define PEN_REPORT_LENGTH 10
+#define VENDOR_REPORT_LENGTH 12
+
+
+__u16 last_button_state;
+
+static const __u8 fixed_rdesc_pad[] = {
+ UsagePage_GenericDesktop
+ Usage_GD_Keypad
+ CollectionApplication(
+ // -- Byte 0 in report
+ ReportId(PAD_REPORT_ID)
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ UsagePage_Digitizers
+ Usage_Dig_TabletFunctionKeys
+ CollectionPhysical(
+ // Byte 1 in report - just exists so we get to be a tablet pad
+ Usage_Dig_BarrelSwitch // BTN_STYLUS
+ ReportCount(1)
+ ReportSize(1)
+ Input(Var|Abs)
+ ReportCount(7) // padding
+ Input(Const)
+ // Bytes 2/3 in report - just exists so we get to be a tablet pad
+ UsagePage_GenericDesktop
+ Usage_GD_X
+ Usage_GD_Y
+ ReportCount(2)
+ ReportSize(8)
+ Input(Var|Abs)
+ // Byte 4 in report is the wheel
+ Usage_GD_Wheel
+ LogicalMinimum_i8(-1)
+ LogicalMaximum_i8(1)
+ ReportCount(1)
+ ReportSize(8)
+ Input(Var|Rel)
+ // Byte 5 is the button state
+ UsagePage_Button
+ UsageMinimum_i8(0x1)
+ UsageMaximum_i8(0x8)
+ LogicalMinimum_i8(0x1)
+ LogicalMaximum_i8(0x8)
+ ReportCount(1)
+ ReportSize(8)
+ Input(Arr|Abs)
+ )
+ // Make sure we match our original report length
+ FixedSizeVendorReport(PAD_REPORT_LENGTH)
+ )
+};
+
+static const __u8 fixed_rdesc_pen[] = {
+ UsagePage_Digitizers
+ Usage_Dig_Pen
+ CollectionApplication(
+ // -- Byte 0 in report
+ ReportId(PEN_REPORT_ID)
+ Usage_Dig_Pen
+ CollectionPhysical(
+ // -- Byte 1 in report
+ Usage_Dig_TipSwitch
+ Usage_Dig_BarrelSwitch
+ Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ ReportSize(1)
+ ReportCount(3)
+ Input(Var|Abs)
+ ReportCount(4) // Padding
+ Input(Const)
+ Usage_Dig_InRange
+ ReportCount(1)
+ Input(Var|Abs)
+ ReportSize(16)
+ ReportCount(1)
+ PushPop(
+ UsagePage_GenericDesktop
+ Unit(cm)
+ UnitExponent(-1)
+ PhysicalMinimum_i16(0)
+ PhysicalMaximum_i16(160)
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(32767)
+ Usage_GD_X
+ Input(Var|Abs) // Bytes 2+3
+ PhysicalMinimum_i16(0)
+ PhysicalMaximum_i16(100)
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(32767)
+ Usage_GD_Y
+ Input(Var|Abs) // Bytes 4+5
+ )
+ UsagePage_Digitizers
+ Usage_Dig_TipPressure
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(8191)
+ Input(Var|Abs) // Byte 6+7
+ // Two bytes padding so we don't need to change the report at all
+ ReportSize(8)
+ ReportCount(2)
+ Input(Const) // Byte 6+7
+ )
+ )
+};
+
+static const __u8 fixed_rdesc_vendor[] = {
+ UsagePage_Digitizers
+ Usage_Dig_Pen
+ CollectionApplication(
+ // Byte 0
+ // We leave the pen on the vendor report ID
+ ReportId(VENDOR_REPORT_ID)
+ Usage_Dig_Pen
+ CollectionPhysical(
+ // Byte 1 are the buttons
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ ReportSize(1)
+ Usage_Dig_TipSwitch
+ Usage_Dig_BarrelSwitch
+ Usage_Dig_SecondaryBarrelSwitch
+ ReportCount(3)
+ Input(Var|Abs)
+ ReportCount(4) // Padding
+ Input(Const)
+ Usage_Dig_InRange
+ ReportCount(1)
+ Input(Var|Abs)
+ ReportSize(16)
+ ReportCount(1)
+ PushPop(
+ UsagePage_GenericDesktop
+ Unit(cm)
+ UnitExponent(-1)
+ // Note: reported logical range differs
+ // from the pen report ID for x and y
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(32000)
+ PhysicalMinimum_i16(0)
+ PhysicalMaximum_i16(160)
+ // Bytes 2/3 in report
+ Usage_GD_X
+ Input(Var|Abs)
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(20000)
+ PhysicalMinimum_i16(0)
+ PhysicalMaximum_i16(100)
+ // Bytes 4/5 in report
+ Usage_GD_Y
+ Input(Var|Abs)
+ )
+ // Bytes 6/7 in report
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(8192)
+ Usage_Dig_TipPressure
+ Input(Var|Abs)
+ )
+ )
+ UsagePage_GenericDesktop
+ Usage_GD_Keypad
+ CollectionApplication(
+ // Byte 0
+ ReportId(PAD_REPORT_ID)
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ UsagePage_Digitizers
+ Usage_Dig_TabletFunctionKeys
+ CollectionPhysical(
+ // Byte 1 are the buttons
+ Usage_Dig_BarrelSwitch // BTN_STYLUS, needed so we get to be a tablet pad
+ ReportCount(1)
+ ReportSize(1)
+ Input(Var|Abs)
+ ReportCount(7) // Padding
+ Input(Const)
+ // Bytes 2/3 - x/y just exist so we get to be a tablet pad
+ UsagePage_GenericDesktop
+ Usage_GD_X
+ Usage_GD_Y
+ ReportCount(2)
+ ReportSize(8)
+ Input(Var|Abs)
+ // Bytes 4 and 5 are the button state
+ UsagePage_Button
+ UsageMinimum_i8(0x1)
+ UsageMaximum_i8(0xa)
+ LogicalMinimum_i8(0x0)
+ LogicalMaximum_i8(0x1)
+ ReportCount(10)
+ ReportSize(1)
+ Input(Var|Abs)
+ Usage_i8(0x31) // maps to BTN_SOUTH
+ ReportCount(1)
+ Input(Var|Abs)
+ ReportCount(5)
+ Input(Const)
+ // Byte 6 is the wheel
+ UsagePage_GenericDesktop
+ Usage_GD_Wheel
+ LogicalMinimum_i8(-1)
+ LogicalMaximum_i8(1)
+ ReportCount(1)
+ ReportSize(8)
+ Input(Var|Rel)
+ )
+ // Make sure we match our original report length
+ FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
+ )
+};
+
+static const __u8 disabled_rdesc_pen[] = {
+ FixedSizeVendorReport(PEN_REPORT_LENGTH)
+};
+
+static const __u8 disabled_rdesc_pad[] = {
+ FixedSizeVendorReport(PAD_REPORT_LENGTH)
+};
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
+ __s32 rdesc_size = hctx->size;
+ __u8 have_fw_id;
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ /* If we have a firmware ID and it matches our expected prefix, we
+ * disable the default pad/pen nodes. They won't send events
+ * but cause duplicate devices.
+ */
+ have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
+ EXPECTED_FIRMWARE_ID,
+ sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
+ if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) {
+ if (have_fw_id) {
+ __builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad));
+ return sizeof(disabled_rdesc_pad);
+ }
+
+ __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
+ return sizeof(fixed_rdesc_pad);
+ }
+ if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) {
+ if (have_fw_id) {
+ __builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen));
+ return sizeof(disabled_rdesc_pen);
+ }
+
+ __builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen));
+ return sizeof(fixed_rdesc_pen);
+ }
+ /* Always fix the vendor mode so the tablet will work even if nothing sets
+ * the udev property (e.g. huion-switcher run manually)
+ */
+ if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) {
+ __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
+ return sizeof(fixed_rdesc_vendor);
+ }
+ return 0;
+}
+
+SEC(HID_BPF_DEVICE_EVENT)
+int BPF_PROG(inspiroy_2_fix_events, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ /* Only sent if tablet is in default mode */
+ if (data[0] == PAD_REPORT_ID) {
+ /* Nicely enough, this device only supports one button down at a time so
+ * the reports are easy to match. Buttons numbered from the top
+ * Button released: 03 00 00 00 00 00 00 00
+ * Button 1: 03 00 05 00 00 00 00 00 -> b
+ * Button 2: 03 07 11 00 00 00 00 00 -> Ctrl Shift N
+ * Button 3: 03 00 08 00 00 00 00 00 -> e
+ * Button 4: 03 00 0c 00 00 00 00 00 -> i
+ * Button 5: 03 00 2c 00 00 00 00 00 -> space
+ * Button 6: 03 01 08 00 00 00 00 00 -> Ctrl E
+ * Button 7: 03 01 16 00 00 00 00 00 -> Ctrl S
+ * Button 8: 03 05 1d 00 00 00 00 00 -> Ctrl Alt Z
+ *
+ * Wheel down: 03 01 2d 00 00 00 00 00 -> Ctrl -
+ * Wheel up: 03 01 2e 00 00 00 00 00 -> Ctrl =
+ */
+ __u8 button = 0;
+ __u8 wheel = 0;
+
+ switch (data[1] << 8 | data[2]) {
+ case 0x0000:
+ break;
+ case 0x0005:
+ button = 1;
+ break;
+ case 0x0711:
+ button = 2;
+ break;
+ case 0x0008:
+ button = 3;
+ break;
+ case 0x000c:
+ button = 4;
+ break;
+ case 0x002c:
+ button = 5;
+ break;
+ case 0x0108:
+ button = 6;
+ break;
+ case 0x0116:
+ button = 7;
+ break;
+ case 0x051d:
+ button = 8;
+ break;
+ case 0x012d:
+ wheel = -1;
+ break;
+ case 0x012e:
+ wheel = 1;
+ break;
+ }
+
+ __u8 report[6] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, wheel, button};
+
+ __builtin_memcpy(data, report, sizeof(report));
+ return sizeof(report);
+ }
+
+ /* Nothing to do for the PEN_REPORT_ID, it's already mapped */
+
+ /* Only sent if tablet is in raw mode */
+ if (data[0] == VENDOR_REPORT_ID) {
+ /* Pad reports */
+ if (data[1] & 0x20) {
+ /* See fixed_rdesc_pad */
+ struct pad_report {
+ __u8 report_id;
+ __u8 btn_stylus;
+ __u8 x;
+ __u8 y;
+ __u16 buttons;
+ __u8 wheel;
+ } __attribute__((packed)) *pad_report;
+ __u8 wheel = 0;
+
+ /* Wheel report */
+ if (data[1] == 0xf1) {
+ if (data[5] == 2)
+ wheel = 0xff;
+ else
+ wheel = data[5];
+ } else {
+ /* data[4] and data[5] are the buttons, mapped correctly */
+ last_button_state = data[4] | (data[5] << 8);
+ wheel = 0; // wheel
+ }
+
+ pad_report = (struct pad_report *)data;
+
+ pad_report->report_id = PAD_REPORT_ID;
+ pad_report->btn_stylus = 0;
+ pad_report->x = 0;
+ pad_report->y = 0;
+ pad_report->buttons = last_button_state;
+ pad_report->wheel = wheel;
+
+ return sizeof(struct pad_report);
+ }
+
+ /* Pen reports need nothing done */
+ }
+
+ return 0;
+}
+
+HID_BPF_OPS(inspiroy_2) = {
+ .hid_device_event = (void *)inspiroy_2_fix_events,
+ .hid_rdesc_fixup = (void *)hid_fix_rdesc,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+ switch (ctx->rdesc_size) {
+ case PAD_REPORT_DESCRIPTOR_LENGTH:
+ case PEN_REPORT_DESCRIPTOR_LENGTH:
+ case VENDOR_REPORT_DESCRIPTOR_LENGTH:
+ ctx->retval = 0;
+ break;
+ default:
+ ctx->retval = -EINVAL;
+ }
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c
index 13f64fb49800..79453362bf97 100644
--- a/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c
+++ b/drivers/hid/bpf/progs/Huion__Inspiroy-2-S.bpf.c
@@ -163,6 +163,9 @@ char EXPECTED_FIRMWARE_ID[] = "HUION_T21j_";
__u8 last_button_state;
+__u8 last_tip_state;
+__u8 last_sec_barrel_state;
+__u8 force_tip_down_count;
static const __u8 fixed_rdesc_pad[] = {
UsagePage_GenericDesktop
@@ -522,9 +525,31 @@ int BPF_PROG(inspiroy_2_fix_events, struct hid_bpf_ctx *hctx)
pad_report->wheel = wheel;
return sizeof(struct pad_report);
- }
+ } else if (data[1] & 0x80) { /* Pen reports with InRange 1 */
+ __u8 tip_state = data[1] & 0x1;
+ __u8 sec_barrel_state = data[1] & 0x4;
+
+ if (force_tip_down_count > 0) {
+ data[1] |= 0x1;
+ --force_tip_down_count;
+ if (tip_state)
+ force_tip_down_count = 0;
+ }
- /* Pen reports need nothing done */
+ /* Tip was down and we just pressed or released the
+ * secondary barrel switch (the physical eraser
+ * button). The device will send up to 4
+ * reports with Tip Switch 0 and sometimes
+ * this report has Tip Switch 0.
+ */
+ if (last_tip_state &&
+ last_sec_barrel_state != sec_barrel_state) {
+ force_tip_down_count = 4;
+ data[1] |= 0x1;
+ }
+ last_tip_state = tip_state;
+ last_sec_barrel_state = sec_barrel_state;
+ }
}
return 0;
diff --git a/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c b/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c
index 489cb4fcc2cd..5f43e4071848 100644
--- a/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c
+++ b/drivers/hid/bpf/progs/Huion__Kamvas-Pro-19.bpf.c
@@ -9,12 +9,15 @@
#define VID_HUION 0x256C
#define PID_KAMVAS_PRO_19 0x006B
+#define PID_KAMVAS_PRO_27 0x006c
#define NAME_KAMVAS_PRO_19 "HUION Huion Tablet_GT1902"
+#define NAME_KAMVAS_PRO_27 "HUION Huion Tablet_GT2701"
#define TEST_PREFIX "uhid test "
HID_BPF_CONFIG(
HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_19),
+ HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, VID_HUION, PID_KAMVAS_PRO_27),
);
bool prev_was_out_of_range;
@@ -351,7 +354,8 @@ int probe(struct hid_bpf_probe_args *ctx)
if (!__builtin_memcmp(name, TEST_PREFIX, sizeof(TEST_PREFIX) - 1))
name += sizeof(TEST_PREFIX) - 1;
- if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)))
+ if (__builtin_memcmp(name, NAME_KAMVAS_PRO_19, sizeof(NAME_KAMVAS_PRO_19)) &&
+ __builtin_memcmp(name, NAME_KAMVAS_PRO_27, sizeof(NAME_KAMVAS_PRO_27)))
ctx->retval = -EINVAL;
hid_bpf_release_context(hctx);
diff --git a/drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c b/drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c
new file mode 100644
index 000000000000..b63f9a48ea45
--- /dev/null
+++ b/drivers/hid/bpf/progs/Huion__Kamvas13Gen3.bpf.c
@@ -0,0 +1,1395 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Nicholas LaPointe
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include "hid_report_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_HUION 0x256c
+#define PID_KAMVAS13_GEN3 0x2008
+
+#define VENDOR_DESCRIPTOR_LENGTH 36
+#define TABLET_DESCRIPTOR_LENGTH 368
+#define WHEEL_DESCRIPTOR_LENGTH 108
+
+#define VENDOR_REPORT_ID 8
+#define VENDOR_REPORT_LENGTH 14
+
+#define VENDOR_REPORT_SUBTYPE_PEN 0x08
+#define VENDOR_REPORT_SUBTYPE_PEN_OUT 0x00
+#define VENDOR_REPORT_SUBTYPE_BUTTONS 0x0e
+#define VENDOR_REPORT_SUBTYPE_WHEELS 0x0f
+
+/* For the reports that we create ourselves */
+#define CUSTOM_PAD_REPORT_ID 9
+
+HID_BPF_CONFIG(
+ HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_HUION, PID_KAMVAS13_GEN3),
+);
+
+
+/*
+ * This tablet can send reports using one of two different data formats,
+ * depending on what "mode" the tablet is in.
+ *
+ * By default, the tablet will send reports that can be decoded using its
+ * included HID descriptors (descriptors 1 and 2, shown below).
+ * This mode will be called "firmware mode" throughout this file.
+ *
+ * The HID descriptor that describes pen events in firmware mode (descriptor 1)
+ * has multiple bugs:
+ * * "Secondary Tip Switch" instead of "Secondary Barrel Switch"
+ * * "Invert" instead of (or potentially shared with) third barrel button
+ * * Specified tablet area of 2048 in³ instead of 293.8 x 165.2mm
+ * * Specified tilt range of -90 to +90 instead of -60 to +60
+ *
+ * While these can be easily patched up by editing the descriptor, a larger
+ * problem with the firmware mode exists: it is impossible to tell which of the
+ * two wheels are being rotated (or having their central button pressed).
+ *
+ *
+ * By using a tool such as huion-switcher (https://github.com/whot/huion-switcher),
+ * the tablet can be made to send reports using a proprietary format that is not
+ * adequately described by its relevant descriptor (descriptor 0, shown below).
+ * This mode will be called "vendor mode" throughout this file.
+ *
+ * The reports sent while in vendor mode allow for proper decoding of the wheels.
+ *
+ * For simplicity and maximum functionality, this BPF focuses strictly on
+ * enabling one to make use of the vendor mode.
+ */
+
+/*
+ * DESCRIPTORS
+ * DESCRIPTOR 0
+ * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 0
+ * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 3
+ * # 0xa1, 0x01, // Collection (Application) 5
+ * # â”… 0x85, 0x08, // Report ID (8) 7
+ * # 0x75, 0x68, // Report Size (104) 9
+ * # 0x95, 0x01, // Report Count (1) 11
+ * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 13
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 15
+ * # 0xc0, // End Collection 17
+ * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 18
+ * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 21
+ * # 0xa1, 0x01, // Collection (Application) 23
+ * # â”… 0x85, 0x16, // Report ID (22) 25
+ * # 0x75, 0x08, // Report Size (8) 27
+ * # 0x95, 0x07, // Report Count (7) 29
+ * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 31
+ * # â•‘ 0xb1, 0x02, // Feature (Data,Var,Abs) 33
+ * # 0xc0, // End Collection 35
+ * R: 36 06 00 ff 09 01 a1 01 85 08 75 68 95 01 09 01 81 02 c0 06 00 ff 09 01 a1 01 85 16 75 08 95 07 09 01 b1 02 c0
+ * N: HUION Huion Tablet_GS1333
+ * I: 3 256c 2008
+ *
+ * DESCRIPTOR 1
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 0
+ * # 0x09, 0x02, // Usage (Pen) 2
+ * # 0xa1, 0x01, // Collection (Application) 4
+ * # â”… 0x85, 0x0a, // Report ID (10) 6
+ * # 0x09, 0x20, // Usage (Stylus) 8
+ * # 0xa1, 0x01, // Collection (Application) 10
+ * # 0x09, 0x42, // Usage (Tip Switch) 12
+ * # 0x09, 0x44, // Usage (Barrel Switch) 14
+ * # 0x09, 0x43, // Usage (Secondary Tip Switch) 16
+ * # 0x09, 0x3c, // Usage (Invert) 18
+ * # 0x09, 0x45, // Usage (Eraser) 20
+ * # 0x15, 0x00, // Logical Minimum (0) 22
+ * # 0x25, 0x01, // Logical Maximum (1) 24
+ * # 0x75, 0x01, // Report Size (1) 26
+ * # 0x95, 0x06, // Report Count (6) 28
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 30
+ * # 0x09, 0x32, // Usage (In Range) 32
+ * # 0x75, 0x01, // Report Size (1) 34
+ * # 0x95, 0x01, // Report Count (1) 36
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 38
+ * # ┇ 0x81, 0x03, // Input (Cnst,Var,Abs) 40
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 42
+ * # 0x09, 0x30, // Usage (X) 44
+ * # 0x09, 0x31, // Usage (Y) 46
+ * # 0x55, 0x0d, // Unit Exponent (-3) 48
+ * # 0x65, 0x33, // Unit (EnglishLinear: in³) 50
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 52
+ * # 0x35, 0x00, // Physical Minimum (0) 55
+ * # 0x46, 0x00, 0x08, // Physical Maximum (2048) 57
+ * # 0x75, 0x10, // Report Size (16) 60
+ * # 0x95, 0x02, // Report Count (2) 62
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 64
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 66
+ * # 0x09, 0x30, // Usage (Tip Pressure) 68
+ * # 0x26, 0xff, 0x3f, // Logical Maximum (16383) 70
+ * # 0x75, 0x10, // Report Size (16) 73
+ * # 0x95, 0x01, // Report Count (1) 75
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 77
+ * # 0x09, 0x3d, // Usage (X Tilt) 79
+ * # 0x09, 0x3e, // Usage (Y Tilt) 81
+ * # 0x15, 0xa6, // Logical Minimum (-90) 83
+ * # 0x25, 0x5a, // Logical Maximum (90) 85
+ * # 0x75, 0x08, // Report Size (8) 87
+ * # 0x95, 0x02, // Report Count (2) 89
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 91
+ * # 0xc0, // End Collection 93
+ * # 0xc0, // End Collection 94
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 95
+ * # 0x09, 0x04, // Usage (Touch Screen) 97
+ * # 0xa1, 0x01, // Collection (Application) 99
+ * # â”… 0x85, 0x04, // Report ID (4) 101
+ * # 0x09, 0x22, // Usage (Finger) 103
+ * # 0xa1, 0x02, // Collection (Logical) 105
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 107
+ * # 0x95, 0x01, // Report Count (1) 109
+ * # 0x75, 0x06, // Report Size (6) 111
+ * # 0x09, 0x51, // Usage (Contact Identifier) 113
+ * # 0x15, 0x00, // Logical Minimum (0) 115
+ * # 0x25, 0x3f, // Logical Maximum (63) 117
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 119
+ * # 0x09, 0x42, // Usage (Tip Switch) 121
+ * # 0x25, 0x01, // Logical Maximum (1) 123
+ * # 0x75, 0x01, // Report Size (1) 125
+ * # 0x95, 0x01, // Report Count (1) 127
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 129
+ * # 0x75, 0x01, // Report Size (1) 131
+ * # 0x95, 0x01, // Report Count (1) 133
+ * # ┇ 0x81, 0x03, // Input (Cnst,Var,Abs) 135
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 137
+ * # 0x75, 0x10, // Report Size (16) 139
+ * # 0x55, 0x0e, // Unit Exponent (-2) 141
+ * # 0x65, 0x11, // Unit (SILinear: cm) 143
+ * # 0x09, 0x30, // Usage (X) 145
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 147
+ * # 0x35, 0x00, // Physical Minimum (0) 150
+ * # 0x46, 0x15, 0x0c, // Physical Maximum (3093) 152
+ * # ┇ 0x81, 0x42, // Input (Data,Var,Abs,Null) 155
+ * # 0x09, 0x31, // Usage (Y) 157
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 159
+ * # 0x46, 0xcb, 0x06, // Physical Maximum (1739) 162
+ * # ┇ 0x81, 0x42, // Input (Data,Var,Abs,Null) 165
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 167
+ * # 0x09, 0x30, // Usage (Tip Pressure) 169
+ * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 171
+ * # 0x75, 0x10, // Report Size (16) 174
+ * # 0x95, 0x01, // Report Count (1) 176
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 178
+ * # 0xc0, // End Collection 180
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 181
+ * # 0x09, 0x22, // Usage (Finger) 183
+ * # 0xa1, 0x02, // Collection (Logical) 185
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 187
+ * # 0x95, 0x01, // Report Count (1) 189
+ * # 0x75, 0x06, // Report Size (6) 191
+ * # 0x09, 0x51, // Usage (Contact Identifier) 193
+ * # 0x15, 0x00, // Logical Minimum (0) 195
+ * # 0x25, 0x3f, // Logical Maximum (63) 197
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 199
+ * # 0x09, 0x42, // Usage (Tip Switch) 201
+ * # 0x25, 0x01, // Logical Maximum (1) 203
+ * # 0x75, 0x01, // Report Size (1) 205
+ * # 0x95, 0x01, // Report Count (1) 207
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 209
+ * # 0x75, 0x01, // Report Size (1) 211
+ * # 0x95, 0x01, // Report Count (1) 213
+ * # ┇ 0x81, 0x03, // Input (Cnst,Var,Abs) 215
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 217
+ * # 0x75, 0x10, // Report Size (16) 219
+ * # 0x55, 0x0e, // Unit Exponent (-2) 221
+ * # 0x65, 0x11, // Unit (SILinear: cm) 223
+ * # 0x09, 0x30, // Usage (X) 225
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 227
+ * # 0x35, 0x00, // Physical Minimum (0) 230
+ * # 0x46, 0x15, 0x0c, // Physical Maximum (3093) 232
+ * # ┇ 0x81, 0x42, // Input (Data,Var,Abs,Null) 235
+ * # 0x09, 0x31, // Usage (Y) 237
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 239
+ * # 0x46, 0xcb, 0x06, // Physical Maximum (1739) 242
+ * # ┇ 0x81, 0x42, // Input (Data,Var,Abs,Null) 245
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 247
+ * # 0x09, 0x30, // Usage (Tip Pressure) 249
+ * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 251
+ * # 0x75, 0x10, // Report Size (16) 254
+ * # 0x95, 0x01, // Report Count (1) 256
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 258
+ * # 0xc0, // End Collection 260
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 261
+ * # 0x09, 0x56, // Usage (Scan Time) 263
+ * # 0x55, 0x00, // Unit Exponent (0) 265
+ * # 0x65, 0x00, // Unit (None) 267
+ * # 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 269
+ * # 0x95, 0x01, // Report Count (1) 274
+ * # 0x75, 0x20, // Report Size (32) 276
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 278
+ * # 0x09, 0x54, // Usage (Contact Count) 280
+ * # 0x25, 0x7f, // Logical Maximum (127) 282
+ * # 0x95, 0x01, // Report Count (1) 284
+ * # 0x75, 0x08, // Report Size (8) 286
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 288
+ * # 0x75, 0x08, // Report Size (8) 290
+ * # 0x95, 0x08, // Report Count (8) 292
+ * # ┇ 0x81, 0x03, // Input (Cnst,Var,Abs) 294
+ * # â”… 0x85, 0x05, // Report ID (5) 296
+ * # 0x09, 0x55, // Usage (Contact Count Maximum) 298
+ * # 0x25, 0x0a, // Logical Maximum (10) 300
+ * # 0x75, 0x08, // Report Size (8) 302
+ * # 0x95, 0x01, // Report Count (1) 304
+ * # â•‘ 0xb1, 0x02, // Feature (Data,Var,Abs) 306
+ * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page FF00) 308
+ * # 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 311
+ * # â”… 0x85, 0x06, // Report ID (6) 313
+ * # 0x15, 0x00, // Logical Minimum (0) 315
+ * # 0x26, 0xff, 0x00, // Logical Maximum (255) 317
+ * # 0x75, 0x08, // Report Size (8) 320
+ * # 0x96, 0x00, 0x01, // Report Count (256) 322
+ * # â•‘ 0xb1, 0x02, // Feature (Data,Var,Abs) 325
+ * # 0xc0, // End Collection 327
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 328
+ * # 0x09, 0x06, // Usage (Keyboard) 330
+ * # 0xa1, 0x01, // Collection (Application) 332
+ * # â”… 0x85, 0x03, // Report ID (3) 334
+ * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 336
+ * # 0x19, 0xe0, // UsageMinimum (224) 338
+ * # 0x29, 0xe7, // UsageMaximum (231) 340
+ * # 0x15, 0x00, // Logical Minimum (0) 342
+ * # 0x25, 0x01, // Logical Maximum (1) 344
+ * # 0x75, 0x01, // Report Size (1) 346
+ * # 0x95, 0x08, // Report Count (8) 348
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 350
+ * # 0x05, 0x07, // Usage Page (Keyboard/Keypad) 352
+ * # 0x19, 0x00, // UsageMinimum (0) 354
+ * # 0x29, 0xff, // UsageMaximum (255) 356
+ * # 0x26, 0xff, 0x00, // Logical Maximum (255) 358
+ * # 0x75, 0x08, // Report Size (8) 361
+ * # 0x95, 0x06, // Report Count (6) 363
+ * # ┇ 0x81, 0x00, // Input (Data,Arr,Abs) 365
+ * # 0xc0, // End Collection 367
+ * R: 368 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 43 09 3c 09 45 15 00 25 01 75 01 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff 7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 3f 75 10 95 01 81 02 09 3d 09 3e 15 a6 25 5a 75 08 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 75 08 95 08 81 03 85 05 09 55 25 0a 75 08 95 01 b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 05 07 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0
+ * N: HUION Huion Tablet_GS1333
+ * I: 3 256c 2008
+ *
+ * DESCRIPTOR 2
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 0
+ * # 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2
+ * # 0xa1, 0x01, // Collection (Application) 4
+ * # â”… 0x85, 0x11, // Report ID (17) 6
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 8
+ * # 0x09, 0x21, // Usage (Puck) 10
+ * # 0xa1, 0x02, // Collection (Logical) 12
+ * # 0x15, 0x00, // Logical Minimum (0) 14
+ * # 0x25, 0x01, // Logical Maximum (1) 16
+ * # 0x75, 0x01, // Report Size (1) 18
+ * # 0x95, 0x01, // Report Count (1) 20
+ * # 0xa1, 0x00, // Collection (Physical) 22
+ * # 0x05, 0x09, // Usage Page (Button) 24
+ * # 0x09, 0x01, // Usage (Button 1) 26
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 28
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 30
+ * # 0x09, 0x33, // Usage (Touch) 32
+ * # ┇ 0x81, 0x02, // Input (Data,Var,Abs) 34
+ * # 0x95, 0x06, // Report Count (6) 36
+ * # ┇ 0x81, 0x03, // Input (Cnst,Var,Abs) 38
+ * # 0xa1, 0x02, // Collection (Logical) 40
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 42
+ * # 0x09, 0x37, // Usage (Dial) 44
+ * # 0x16, 0x00, 0x80, // Logical Minimum (-32768) 46
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 49
+ * # 0x75, 0x10, // Report Size (16) 52
+ * # 0x95, 0x01, // Report Count (1) 54
+ * # ┇ 0x81, 0x06, // Input (Data,Var,Rel) 56
+ * # 0x35, 0x00, // Physical Minimum (0) 58
+ * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 60
+ * # 0x15, 0x00, // Logical Minimum (0) 63
+ * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 65
+ * # 0x09, 0x48, // Usage (Resolution Multiplier) 68
+ * # â•‘ 0xb1, 0x02, // Feature (Data,Var,Abs) 70
+ * # 0x45, 0x00, // Physical Maximum (0) 72
+ * # 0xc0, // End Collection 74
+ * # 0x75, 0x08, // Report Size (8) 75
+ * # 0x95, 0x01, // Report Count (1) 77
+ * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 79
+ * # 0x75, 0x08, // Report Size (8) 81
+ * # 0x95, 0x01, // Report Count (1) 83
+ * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 85
+ * # 0x75, 0x08, // Report Size (8) 87
+ * # 0x95, 0x01, // Report Count (1) 89
+ * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 91
+ * # 0x75, 0x08, // Report Size (8) 93
+ * # 0x95, 0x01, // Report Count (1) 95
+ * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 97
+ * # 0x75, 0x08, // Report Size (8) 99
+ * # 0x95, 0x01, // Report Count (1) 101
+ * # ┇ 0x81, 0x01, // Input (Cnst,Arr,Abs) 103
+ * # 0xc0, // End Collection 105
+ * # 0xc0, // End Collection 106
+ * # 0xc0, // End Collection 107
+ * R: 108 05 01 09 0e a1 01 85 11 05 0d 09 21 a1 02 15 00 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0
+ * N: HUION Huion Tablet_GS1333
+ * I: 3 256c 2008
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * VENDOR MODE
+ * HUION_FIRMWARE_ID="HUION_M22c_240606"
+ * HUION_MAGIC_BYTES="140388e500108100ff3fd8130307008008004010"
+ *
+ * MAGIC BYTES
+ * [LogicalMaximum, X] [LogicalMaximum, Y] [LogicalMaximum, Pressure] [ LPI]
+ * 14 03 [ 88 e5] 00 [ 10 81] 00 [ ff 3f] [d8 13] 03 07 00 80 08 00 40 10
+ *
+ *
+ * HIDRAW 0
+ * DESCRIPTIONS
+ * report_subtype = (data[1] >> 4) & 0x0f
+ *
+ * REPORT SUBTYPES
+ * 0x0e Buttons
+ * (data[4] & 0x01) button 1
+ * (data[4] & 0x02) button 2
+ * (data[4] & 0x04) button 3
+ * (data[4] & 0x08) button 4
+ * (data[4] & 0x10) button 5
+ * (data[4] & 0x20) button 6 (top wheel button)
+ * (data[4] & 0x40) button 7 (bottom wheel button)
+ *
+ * All tablet buttons release with the same report:
+ * 08 e0 01 01 00 00 00 00 00 00 00 00 00 00
+ *
+ * Despite data[4] looking like a bit field, only one button
+ * can be unambiguously tracked at a time.
+ * (See NOTES ON SIMULTANEOUS BUTTON HOLDS at the end of this
+ * comment for examples of the confusion this can create.)
+ *
+ * All buttons, with the exceptions of 6 and 7, will repeatedly
+ * report a press event approximately every 225ms while held.
+ *
+ * 0x0f Wheels
+ * data[3] == 1: top wheel
+ * data[3] == 2: bottom wheel
+ * data[5] == 1: clockwise
+ * data[5] == 2: counter-clockwise
+ *
+ * 0x08/0x00 Pen
+ * report_subtype == 0x08: in-range
+ * report_subtype == 0x00: out-of-range
+ * For clarity, this is also equivalent to:
+ * (data[1] & 0x80) in-range
+ *
+ * Switches
+ * (data[1] & 0x01) tip switch
+ * (data[1] & 0x02) barrel switch
+ * (data[1] & 0x04) secondary barrel switch
+ * (data[1] & 0x08) third barrel switch
+ *
+ * Unfortunately, I don't have a pen with an eraser, so I can't
+ * confirm where the invert and eraser bits reside.
+ * If we guess using the definitions from HID descriptor 1,
+ * then they might be...
+ * (data[1] & 0x08) invert (conflicts with third barrel switch)
+ * (data[1] & 0x10) eraser
+ *
+ * data[2], data[3] X (little-endian, maximum 0xe588)
+ *
+ * data[4], data[5] Y (little-endian, maximum 0x8110)
+ *
+ * data[6], data[7] Pressure (little-endian, maximum 0x3fff)
+ *
+ * data[10] X tilt (signed, -60 to +60)
+ * data[11] Y tilt (signed, -60 to +60, inverted)
+ *
+ *
+ * EXAMPLE REPORTS
+ * Top wheel button, press, hold, then release
+ * E: 000000.000040 14 08 e0 01 01 20 00 00 00 00 00 00 00 00 00
+ * E: 000001.531559 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00
+ *
+ * Bottom wheel button, press, hold, then release
+ * E: 000002.787603 14 08 e0 01 01 40 00 00 00 00 00 00 00 00 00
+ * E: 000004.215609 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00
+ *
+ *
+ * Top wheel rotation, one detent CW
+ * E: 000194.003899 14 08 f1 01 01 00 01 00 00 00 00 00 00 00 00
+ *
+ * Top wheel rotation, one detent CCW
+ * E: 000194.997812 14 08 f1 01 01 00 02 00 00 00 00 00 00 00 00
+ *
+ * Bottom wheel rotation, one detent CW
+ * E: 000196.693840 14 08 f1 01 02 00 01 00 00 00 00 00 00 00 00
+ *
+ * Bottom wheel rotation, one detent CCW
+ * E: 000197.757895 14 08 f1 01 02 00 02 00 00 00 00 00 00 00 00
+ *
+ *
+ * Button 1, press, hold, then release
+ * E: 000000.000149 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 < press
+ * E: 000000.447598 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00 < starting to auto-repeat, every ~225ms
+ * E: 000000.673586 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00
+ * E: 000000.900582 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00
+ * E: 000001.126703 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00
+ * E: 000001.347706 14 08 e0 01 01 01 00 00 00 00 00 00 00 00 00
+ * E: 000001.533721 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < release
+ *
+ * Button 2, press, hold, then release
+ * E: 000003.304735 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 < press
+ * E: 000003.746743 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00 < starting to auto-repeat, every ~225ms
+ * E: 000003.973741 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00
+ * E: 000004.199832 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00
+ * E: 000004.426732 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00
+ * E: 000004.647738 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00
+ * E: 000004.874733 14 08 e0 01 01 02 00 00 00 00 00 00 00 00 00
+ * E: 000004.930713 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < release
+ *
+ * Button 3, press, hold, then release
+ * E: 000006.650346 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 < press
+ * E: 000007.051782 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00 < starting to auto-repeat, every ~225ms
+ * E: 000007.273738 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00
+ * E: 000007.499794 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00
+ * E: 000007.726725 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00
+ * E: 000007.947765 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00
+ * E: 000008.174755 14 08 e0 01 01 04 00 00 00 00 00 00 00 00 00
+ * E: 000008.328786 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < release
+ *
+ * Button 4, press, hold, then release
+ * E: 000009.893820 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 < press
+ * E: 000010.274781 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00 < starting to auto-repeat, every ~225ms
+ * E: 000010.500931 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00
+ * E: 000010.722777 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00
+ * E: 000010.948778 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00
+ * E: 000011.175799 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00
+ * E: 000011.401153 14 08 e0 01 01 08 00 00 00 00 00 00 00 00 00
+ * E: 000011.432114 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < release
+ *
+ * Button 5, press, hold, then release
+ * E: 000013.007778 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 < press
+ * E: 000013.424741 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00 < starting to auto-repeat, every ~225ms
+ * E: 000013.651715 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00
+ * E: 000013.872763 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00
+ * E: 000014.099789 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00
+ * E: 000014.325734 14 08 e0 01 01 10 00 00 00 00 00 00 00 00 00
+ * E: 000014.438080 14 08 e0 01 01 00 00 00 00 00 00 00 00 00 00 < release
+ *
+ *
+ * Pen: Top-left, then out of range
+ * E: 000368.572184 14 08 80 00 00 00 00 00 00 00 00 fb ed 03 00
+ * E: 000368.573030 14 08 00 00 00 00 00 00 00 00 00 fb ed 03 00
+ *
+ * Pen: Bottom-right, then out of range
+ * E: 000544.433185 14 08 80 88 e5 10 81 00 00 00 00 00 00 03 00
+ * E: 000544.434183 14 08 00 88 e5 10 81 00 00 00 00 00 00 03 00
+ *
+ * Pen: Max Y tilt (tip of pen points down)
+ * E: 000002.231927 14 08 80 f5 5d 6c 36 00 00 00 00 09 3c 03 00
+ *
+ * Pen: Min Y Tilt (tip of pen points up)
+ * E: 000657.593338 14 08 80 5f 69 fa 2c 00 00 00 00 fe c4 03 00
+ *
+ * Pen: Max X tilt (tip of pen points left)
+ * E: 000742.246503 14 08 80 2a 4f c4 38 00 00 00 00 3c ed 03 00
+ *
+ * Pen: Min X Tilt (tip of pen points right)
+ * E: 000776.404446 14 08 00 18 85 7c 3b 00 00 00 00 c4 ed 03 00
+ *
+ * Pen: Tip switch, max pressure, then low pressure
+ * E: 001138.935675 14 08 81 d2 66 04 40 ff 3f 00 00 00 08 03 00
+ *
+ * E: 001142.403715 14 08 81 9d 69 47 3e 82 04 00 00 00 07 03 00
+ *
+ * Pen: Barrel switch
+ * E: 001210.645652 14 08 82 0d 72 ea 2b 00 00 00 00 db c4 03 00
+ *
+ * Pen: Secondary barrel switch
+ * E: 001211.519729 14 08 84 2c 71 51 2b 00 00 00 00 da c4 03 00
+ *
+ * Pen: Third switch
+ * E: 001212.443722 14 08 88 1d 72 df 2b 00 00 00 00 dc c4 03 00
+ *
+ *
+ * HIDRAW 1
+ * No reports
+ *
+ *
+ * HIDRAW 2
+ * No reports
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * FIRMWARE MODE
+ * HIDRAW 0
+ * No reports
+ *
+ *
+ * HIDRAW 1
+ * EXAMPLE REPORTS
+ * Top wheel button, *release*
+ * E: 000067.043739 8 03 00 00 00 00 00 00 00
+ *
+ * Bottom wheel button, *release*
+ * E: 000068.219161 8 03 00 00 00 00 00 00 00
+ *
+ *
+ * Button 1, press, then release
+ * E: 000163.767870 8 03 00 05 00 00 00 00 00
+ * E: 000165.969193 8 03 00 00 00 00 00 00 00
+ *
+ * Button 2, press, then release
+ * E: 000261.728935 8 03 05 11 00 00 00 00 00
+ * E: 000262.956220 8 03 00 00 00 00 00 00 00
+ *
+ * Button 3, press, then release
+ * E: 000289.127881 8 03 01 16 00 00 00 00 00
+ * E: 000290.014594 8 03 00 00 00 00 00 00 00
+ *
+ * Button 4, press, then release
+ * E: 000303.025839 8 03 00 2c 00 00 00 00 00
+ * E: 000303.994479 8 03 00 00 00 00 00 00 00
+ *
+ * Button 5, press, then release
+ * E: 000315.500835 8 03 05 1d 00 00 00 00 00
+ * E: 000316.603274 8 03 00 00 00 00 00 00 00
+ *
+ * BUTTON SUMMARY
+ * 1 E: 000163.767870 8 03 00 05 00 00 00 00 00 Keyboard: B
+ * 2 E: 000261.728935 8 03 05 11 00 00 00 00 00 Keyboard: LCtrl+LAlt N
+ * 3 E: 000289.127881 8 03 01 16 00 00 00 00 00 Keyboard: LCtrl S
+ * 4 E: 000303.025839 8 03 00 2c 00 00 00 00 00 Keyboard: Space
+ * 5 E: 000315.500835 8 03 05 1d 00 00 00 00 00 Keyboard: LCtrl+LAlt
+ *
+ * All buttons (including the wheel buttons) release the same way:
+ * 03 00 00 00 00 00 00 00
+ *
+ *
+ * Pen: Top-left, then out of range
+ * E: 000063.196828 10 0a c0 00 00 00 00 00 00 00 02
+ * E: 000063.197762 10 0a 00 00 00 00 00 00 00 00 02
+ *
+ * Pen: Bottom-right, then out of range
+ * E: 000197.123138 10 0a c0 ff 7f ff 7f 00 00 00 00
+ * E: 000197.124915 10 0a 00 ff 7f ff 7f 00 00 00 00
+ *
+ * Pen: Max Y Tilt (tip of pen points up)
+ * E: 000291.399541 10 0a c0 19 32 0b 58 00 00 00 3c
+ *
+ * Pen: Min Y tilt (tip of pen points down)
+ * E: 000340.888288 10 0a c0 85 40 89 6e 00 00 17 c4
+ *
+ * Pen: Max X tilt (tip of pen points left)
+ * E: 000165.575115 10 0a c0 a7 34 99 42 00 00 3c f4
+ *
+ * Pen: Min X Tilt (tip of pen points right)
+ * E: 000129.507883 10 0a c0 ea 4b 08 40 00 00 c4 1a
+ *
+ * Pen: Tip switch, max pressure, then low pressure
+ * E: 000242.077160 10 0a c1 7e 3c 12 31 ff 3f 03 fd
+ *
+ * E: 000339.139188 10 0a c1 ee 3a 9e 32 b5 00 06 f6
+ *
+ * Pen: Barrel switch
+ * E: 000037.949777 10 0a c2 5c 28 47 2a 00 00 f6 3c
+ *
+ * Pen: Secondary barrel switch
+ * E: 000038.320840 10 0a c4 e4 27 fd 29 00 00 f3 38
+ *
+ * Pen: Third switch
+ * E: 000038.923822 10 0a c8 97 27 5f 29 00 00 f2 33
+ *
+ *
+ * HIDRAW 2
+ * EXAMPLE REPORTS
+ * Either wheel rotation, one detent CW
+ * E: 000097.276573 9 11 00 01 00 00 00 00 00 00
+ *
+ * Either wheel rotation, one detent CCW
+ * E: 000153.416538 9 11 00 ff ff 00 00 00 00 00
+ *
+ * Either wheel rotation, increasing rotation speed CW
+ * (Note that the wheels on my particular tablet may be
+ * damaged, so the false rotation direction changes
+ * that can be observed might not happen on other units.)
+ * E: 000210.514925 9 11 00 01 00 00 00 00 00 00
+ * E: 000210.725718 9 11 00 01 00 00 00 00 00 00
+ * E: 000210.924009 9 11 00 01 00 00 00 00 00 00
+ * E: 000211.205629 9 11 00 01 00 00 00 00 00 00
+ * E: 000211.280521 9 11 00 0b 00 00 00 00 00 00
+ * E: 000211.340121 9 11 00 0e 00 00 00 00 00 00
+ * E: 000211.404018 9 11 00 0d 00 00 00 00 00 00
+ * E: 000211.462060 9 11 00 0e 00 00 00 00 00 00
+ * E: 000211.544886 9 11 00 0a 00 00 00 00 00 00
+ * E: 000211.606130 9 11 00 0d 00 00 00 00 00 00
+ * E: 000211.674560 9 11 00 0c 00 00 00 00 00 00
+ * E: 000211.712039 9 11 00 16 00 00 00 00 00 00
+ * E: 000211.748076 9 11 00 17 00 00 00 00 00 00
+ * E: 000211.786016 9 11 00 17 00 00 00 00 00 00
+ * E: 000211.832960 9 11 00 11 00 00 00 00 00 00
+ * E: 000211.874081 9 11 00 14 00 00 00 00 00 00
+ * E: 000211.925094 9 11 00 10 00 00 00 00 00 00
+ * E: 000211.959048 9 11 00 18 00 00 00 00 00 00
+ * E: 000212.006937 9 11 00 11 00 00 00 00 00 00
+ * E: 000212.050055 9 11 00 13 00 00 00 00 00 00
+ * E: 000212.091947 9 11 00 14 00 00 00 00 00 00
+ * E: 000212.122989 9 11 00 1a 00 00 00 00 00 00
+ * E: 000212.160866 9 11 00 16 00 00 00 00 00 00
+ * E: 000212.194002 9 11 00 19 00 00 00 00 00 00
+ * E: 000212.242249 9 11 00 11 00 00 00 00 00 00
+ * E: 000212.278061 9 11 00 18 00 00 00 00 00 00
+ * E: 000212.328899 9 11 00 10 00 00 00 00 00 00
+ * E: 000212.354005 9 11 00 22 00 00 00 00 00 00
+ * E: 000212.398995 9 11 00 12 00 00 00 00 00 00
+ * E: 000212.432050 9 11 00 19 00 00 00 00 00 00
+ * E: 000212.471164 9 11 00 16 00 00 00 00 00 00
+ * E: 000212.507047 9 11 00 17 00 00 00 00 00 00
+ * E: 000212.540964 9 11 00 19 00 00 00 00 00 00
+ * E: 000212.567942 9 11 00 1f 00 00 00 00 00 00
+ * E: 000212.610007 9 11 00 14 00 00 00 00 00 00
+ * E: 000212.641101 9 11 00 1b 00 00 00 00 00 00
+ * E: 000212.674113 9 11 00 19 00 00 00 00 00 00
+ * E: 000212.674909 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.677062 9 11 00 00 02 00 00 00 00 00
+ * E: 000212.679048 9 11 00 55 01 00 00 00 00 00
+ * E: 000212.682166 9 11 00 55 01 00 00 00 00 00
+ * E: 000212.682788 9 11 00 ff ff 00 00 00 00 00
+ * E: 000212.683899 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.685827 9 11 00 67 fe 00 00 00 00 00
+ * E: 000212.686941 9 11 00 00 08 00 00 00 00 00
+ * E: 000212.727840 9 11 00 14 00 00 00 00 00 00
+ * E: 000212.772884 9 11 00 13 00 00 00 00 00 00
+ * E: 000212.810975 9 11 00 16 00 00 00 00 00 00
+ * E: 000212.811793 9 11 00 00 08 00 00 00 00 00
+ * E: 000212.812683 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.813905 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.814909 9 11 00 00 04 00 00 00 00 00
+ * E: 000212.816942 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.817851 9 11 00 ff ff 00 00 00 00 00
+ * E: 000212.818752 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.819910 9 11 00 56 fd 00 00 00 00 00
+ * E: 000212.820781 9 11 00 ff ff 00 00 00 00 00
+ * E: 000212.821811 9 11 00 00 04 00 00 00 00 00
+ * E: 000212.822920 9 11 00 00 08 00 00 00 00 00
+ * E: 000212.823861 9 11 00 00 02 00 00 00 00 00
+ * E: 000212.828781 9 11 00 ba 00 00 00 00 00 00
+ * E: 000212.874097 9 11 00 12 00 00 00 00 00 00
+ * E: 000212.874872 9 11 00 00 fc 00 00 00 00 00
+ * E: 000212.876136 9 11 00 00 fc 00 00 00 00 00
+ * E: 000212.877036 9 11 00 00 f8 00 00 00 00 00
+ * E: 000212.877993 9 11 00 00 f8 00 00 00 00 00
+ * E: 000212.879748 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.880728 9 11 00 01 00 00 00 00 00 00
+ * E: 000212.881956 9 11 00 00 04 00 00 00 00 00
+ * E: 000212.885065 9 11 00 ff ff 00 00 00 00 00
+ * E: 000212.917060 9 11 00 1a 00 00 00 00 00 00
+ * E: 000212.936458 9 11 00 2d 00 00 00 00 00 00
+ * E: 000212.957860 9 11 00 25 00 00 00 00 00 00
+ * E: 000212.984019 9 11 00 20 00 00 00 00 00 00
+ * E: 000213.017915 9 11 00 19 00 00 00 00 00 00
+ * E: 000213.039973 9 11 00 27 00 00 00 00 00 00
+ * E: 000213.065933 9 11 00 21 00 00 00 00 00 00
+ * E: 000213.085807 9 11 00 28 00 00 00 00 00 00
+ * E: 000213.108888 9 11 00 25 00 00 00 00 00 00
+ * E: 000213.129726 9 11 00 29 00 00 00 00 00 00
+ * E: 000213.172043 9 11 00 14 00 00 00 00 00 00
+ * E: 000213.195873 9 11 00 23 00 00 00 00 00 00
+ * E: 000213.222884 9 11 00 20 00 00 00 00 00 00
+ * E: 000213.243220 9 11 00 2a 00 00 00 00 00 00
+ * E: 000213.266778 9 11 00 24 00 00 00 00 00 00
+ * E: 000213.285951 9 11 00 2b 00 00 00 00 00 00
+ * E: 000213.306045 9 11 00 2a 00 00 00 00 00 00
+ * E: 000213.306796 9 11 00 ff ff 00 00 00 00 00
+ * E: 000213.307755 9 11 00 ff ff 00 00 00 00 00
+ * E: 000213.308820 9 11 00 ff ff 00 00 00 00 00
+ * E: 000213.309971 9 11 00 ff ff 00 00 00 00 00
+ * E: 000213.310980 9 11 00 01 00 00 00 00 00 00
+ * E: 000213.311853 9 11 00 01 00 00 00 00 00 00
+ * E: 000213.312861 9 11 00 aa 02 00 00 00 00 00
+ * E: 000213.313884 9 11 00 00 f8 00 00 00 00 00
+ * E: 000213.315111 9 11 00 ff ff 00 00 00 00 00
+ * E: 000213.315992 9 11 00 01 00 00 00 00 00 00
+ * E: 000213.316955 9 11 00 00 08 00 00 00 00 00
+ * E: 000213.346065 9 11 00 1d 00 00 00 00 00 00
+ * E: 000213.346963 9 11 00 ff ff 00 00 00 00 00
+ * E: 000213.347874 9 11 00 00 08 00 00 00 00 00
+ * E: 000213.348736 9 11 00 00 08 00 00 00 00 00
+ * E: 000213.349795 9 11 00 00 04 00 00 00 00 00
+ * E: 000213.350791 9 11 00 01 00 00 00 00 00 00
+ * E: 000213.351791 9 11 00 01 00 00 00 00 00 00
+ * E: 000213.352729 9 11 00 00 f8 00 00 00 00 00
+ * E: 000213.353811 9 11 00 01 00 00 00 00 00 00
+ * E: 000213.354755 9 11 00 00 f8 00 00 00 00 00
+ * E: 000213.355795 9 11 00 00 f8 00 00 00 00 00
+ * E: 000213.356813 9 11 00 01 00 00 00 00 00 00
+ * E: 000213.357817 9 11 00 00 04 00 00 00 00 00
+ * E: 000213.393838 9 11 00 17 00 00 00 00 00 00
+ * E: 000213.394719 9 11 00 00 04 00 00 00 00 00
+ * E: 000213.395682 9 11 00 00 08 00 00 00 00 00
+ * E: 000213.396679 9 11 00 00 04 00 00 00 00 00
+ * E: 000213.397651 9 11 00 00 fc 00 00 00 00 00
+ * E: 000213.398661 9 11 00 ff ff 00 00 00 00 00
+ * E: 000213.400308 9 11 00 56 fd 00 00 00 00 00
+ * E: 000213.400909 9 11 00 00 f8 00 00 00 00 00
+ * E: 000213.401837 9 11 00 01 00 00 00 00 00 00
+ *
+ * Either wheel rotation, increasing rotation speed CCW
+ * (Note that the wheels on my particular tablet may be
+ * damaged, so the false rotation direction changes
+ * that can be observed might not happen on other units.)
+ * E: 000040.527820 9 11 00 ff ff 00 00 00 00 00
+ * E: 000040.816644 9 11 00 ff ff 00 00 00 00 00
+ * E: 000040.880423 9 11 00 f3 ff 00 00 00 00 00
+ * E: 000040.882570 9 11 00 ff ff 00 00 00 00 00
+ * E: 000040.883381 9 11 00 ff ff 00 00 00 00 00
+ * E: 000040.885463 9 11 00 aa 02 00 00 00 00 00
+ * E: 000040.924106 9 11 00 ea ff 00 00 00 00 00
+ * E: 000041.006155 9 11 00 f6 ff 00 00 00 00 00
+ * E: 000041.085799 9 11 00 f6 ff 00 00 00 00 00
+ * E: 000041.168492 9 11 00 f6 ff 00 00 00 00 00
+ * E: 000041.233453 9 11 00 f3 ff 00 00 00 00 00
+ * E: 000041.296641 9 11 00 f3 ff 00 00 00 00 00
+ * E: 000041.370302 9 11 00 f5 ff 00 00 00 00 00
+ * E: 000041.437410 9 11 00 f4 ff 00 00 00 00 00
+ * E: 000041.474514 9 11 00 e9 ff 00 00 00 00 00
+ * E: 000041.522171 9 11 00 ef ff 00 00 00 00 00
+ * E: 000041.568160 9 11 00 ee ff 00 00 00 00 00
+ * E: 000041.608146 9 11 00 ec ff 00 00 00 00 00
+ * E: 000041.627132 9 11 00 d3 ff 00 00 00 00 00
+ * E: 000041.656151 9 11 00 e3 ff 00 00 00 00 00
+ * E: 000041.682264 9 11 00 e0 ff 00 00 00 00 00
+ * E: 000041.714186 9 11 00 e6 ff 00 00 00 00 00
+ * E: 000041.740339 9 11 00 e0 ff 00 00 00 00 00
+ * E: 000041.772087 9 11 00 e5 ff 00 00 00 00 00
+ * E: 000041.801093 9 11 00 e3 ff 00 00 00 00 00
+ * E: 000041.834051 9 11 00 e7 ff 00 00 00 00 00
+ * E: 000041.863094 9 11 00 e3 ff 00 00 00 00 00
+ * E: 000041.901016 9 11 00 ea ff 00 00 00 00 00
+ * E: 000041.901956 9 11 00 00 04 00 00 00 00 00
+ * E: 000041.902837 9 11 00 00 fe 00 00 00 00 00
+ * E: 000041.903927 9 11 00 01 00 00 00 00 00 00
+ * E: 000041.905066 9 11 00 01 00 00 00 00 00 00
+ * E: 000041.907214 9 11 00 00 fe 00 00 00 00 00
+ * E: 000041.909011 9 11 00 01 00 00 00 00 00 00
+ * E: 000041.909953 9 11 00 01 00 00 00 00 00 00
+ * E: 000041.910917 9 11 00 00 08 00 00 00 00 00
+ * E: 000041.913280 9 11 00 00 fe 00 00 00 00 00
+ * E: 000041.914121 9 11 00 56 fd 00 00 00 00 00
+ * E: 000041.915346 9 11 00 ff ff 00 00 00 00 00
+ * E: 000041.962101 9 11 00 ee ff 00 00 00 00 00
+ * E: 000041.964062 9 11 00 56 fd 00 00 00 00 00
+ * E: 000041.964978 9 11 00 00 fc 00 00 00 00 00
+ * E: 000041.968058 9 11 00 24 01 00 00 00 00 00
+ * E: 000041.968880 9 11 00 56 fd 00 00 00 00 00
+ * E: 000041.970977 9 11 00 aa 02 00 00 00 00 00
+ * E: 000041.971932 9 11 00 ff ff 00 00 00 00 00
+ * E: 000041.972943 9 11 00 01 00 00 00 00 00 00
+ * E: 000041.975291 9 11 00 ff ff 00 00 00 00 00
+ * E: 000041.978274 9 11 00 01 00 00 00 00 00 00
+ * E: 000042.035079 9 11 00 01 00 00 00 00 00 00
+ * E: 000042.041283 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.042057 9 11 00 00 04 00 00 00 00 00
+ * E: 000042.045169 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.051242 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.056099 9 11 00 63 ff 00 00 00 00 00
+ * E: 000042.106329 9 11 00 ef ff 00 00 00 00 00
+ * E: 000042.108601 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.116259 9 11 00 6b 00 00 00 00 00 00
+ * E: 000042.119140 9 11 00 55 01 00 00 00 00 00
+ * E: 000042.126101 9 11 00 88 ff 00 00 00 00 00
+ * E: 000042.158009 9 11 00 e6 ff 00 00 00 00 00
+ * E: 000042.172108 9 11 00 be ff 00 00 00 00 00
+ * E: 000042.207417 9 11 00 e8 ff 00 00 00 00 00
+ * E: 000042.223155 9 11 00 cc ff 00 00 00 00 00
+ * E: 000042.255185 9 11 00 e6 ff 00 00 00 00 00
+ * E: 000042.276280 9 11 00 d7 ff 00 00 00 00 00
+ * E: 000042.302128 9 11 00 e0 ff 00 00 00 00 00
+ * E: 000042.317423 9 11 00 c8 ff 00 00 00 00 00
+ * E: 000042.345226 9 11 00 e1 ff 00 00 00 00 00
+ * E: 000042.357243 9 11 00 bc ff 00 00 00 00 00
+ * E: 000042.381308 9 11 00 dc ff 00 00 00 00 00
+ * E: 000042.383180 9 11 00 dc fe 00 00 00 00 00
+ * E: 000042.412288 9 11 00 e3 ff 00 00 00 00 00
+ * E: 000042.451216 9 11 00 eb ff 00 00 00 00 00
+ * E: 000042.478372 9 11 00 e0 ff 00 00 00 00 00
+ * E: 000042.502116 9 11 00 dd ff 00 00 00 00 00
+ * E: 000042.520105 9 11 00 d3 ff 00 00 00 00 00
+ * E: 000042.540345 9 11 00 d6 ff 00 00 00 00 00
+ * E: 000042.541021 9 11 00 00 08 00 00 00 00 00
+ * E: 000042.542009 9 11 00 01 00 00 00 00 00 00
+ * E: 000042.543045 9 11 00 00 04 00 00 00 00 00
+ * E: 000042.544279 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.545097 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.546074 9 11 00 00 08 00 00 00 00 00
+ * E: 000042.547237 9 11 00 00 08 00 00 00 00 00
+ * E: 000042.548029 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.549304 9 11 00 00 f8 00 00 00 00 00
+ * E: 000042.553123 9 11 00 00 ff 00 00 00 00 00
+ * E: 000042.581186 9 11 00 e1 ff 00 00 00 00 00
+ * E: 000042.582238 9 11 00 00 f8 00 00 00 00 00
+ * E: 000042.583150 9 11 00 00 fc 00 00 00 00 00
+ * E: 000042.584273 9 11 00 00 f8 00 00 00 00 00
+ * E: 000042.585019 9 11 00 00 fc 00 00 00 00 00
+ * E: 000042.586059 9 11 00 01 00 00 00 00 00 00
+ * E: 000042.589012 9 11 00 67 fe 00 00 00 00 00
+ * E: 000042.590066 9 11 00 00 fc 00 00 00 00 00
+ * E: 000042.592916 9 11 00 dc fe 00 00 00 00 00
+ * E: 000042.621124 9 11 00 e1 ff 00 00 00 00 00
+ * E: 000042.622092 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.623069 9 11 00 01 00 00 00 00 00 00
+ * E: 000042.624030 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.625006 9 11 00 00 08 00 00 00 00 00
+ * E: 000042.626068 9 11 00 00 04 00 00 00 00 00
+ * E: 000042.626876 9 11 00 00 08 00 00 00 00 00
+ * E: 000042.628392 9 11 00 00 08 00 00 00 00 00
+ * E: 000042.628918 9 11 00 01 00 00 00 00 00 00
+ * E: 000042.630009 9 11 00 ff ff 00 00 00 00 00
+ * E: 000042.631934 9 11 00 00 fe 00 00 00 00 00
+ * E: 000042.656285 9 11 00 dd ff 00 00 00 00 00
+ * E: 000042.659870 9 11 00 cc 00 00 00 00 00 00
+ * E: 000042.666128 9 11 00 9d 00 00 00 00 00 00
+ * E: 000042.672458 9 11 00 80 ff 00 00 00 00 00
+ * E: 000042.696106 9 11 00 dc ff 00 00 00 00 00
+ * E: 000042.705129 9 11 00 61 00 00 00 00 00 00
+ * E: 000042.731303 9 11 00 e0 ff 00 00 00 00 00
+ * E: 000042.741278 9 11 00 ab ff 00 00 00 00 00
+ * E: 000042.788181 9 11 00 ee ff 00 00 00 00 00
+ * E: 000042.810441 9 11 00 db ff 00 00 00 00 00
+ * E: 000042.838073 9 11 00 e1 ff 00 00 00 00 00
+ * E: 000042.852235 9 11 00 c4 ff 00 00 00 00 00
+ * E: 000042.882290 9 11 00 e4 ff 00 00 00 00 00
+ *
+ * Either wheel button, press, hold, then release
+ * E: 000202.084982 9 11 02 00 00 00 00 00 00 00
+ * E: 000202.090172 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.094139 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.099172 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.105055 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.109132 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.114185 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.119212 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.124264 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.130147 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.135138 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.140072 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.145146 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.150157 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.155339 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.160064 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.165026 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.170037 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.175154 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.180044 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.186280 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.191281 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.196106 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.201083 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.206166 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.211084 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.216175 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.221036 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.226271 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.231150 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.235924 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.242046 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.247164 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.252359 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.257295 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.262167 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.267081 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.272175 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.277085 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.282596 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.287078 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.292191 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.298196 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.303004 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.308113 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.313079 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.318243 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.323309 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.328190 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.333050 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.338162 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.343022 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.348113 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.354133 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.359132 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.364053 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.369034 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.374144 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.379027 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.384238 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.389249 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.394049 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.398949 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.404203 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.410098 9 11 03 00 00 00 00 00 00 00
+ * E: 000202.415237 9 11 00 00 00 00 00 00 00 00
+ *
+ *
+ * Top wheel button press and release while holding bottom wheel button
+ * (The reverse action (a bottom wheel button press while holding the top wheel button) is invisible.)
+ * E: 000071.126966 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.133117 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.137481 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.142036 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.147027 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.151988 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.157945 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.163657 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.168240 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.173109 9 11 02 00 00 00 00 00 00 00 < top wheel button press?
+ * E: 000071.178119 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.183046 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.187983 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.192996 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.198341 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.203122 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.208998 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.214037 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.218945 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.223835 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.228987 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.234082 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.239028 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.244307 9 11 00 00 00 00 00 00 00 00 < top wheel button release?
+ * E: 000071.245867 9 11 03 00 00 00 00 00 00 00 < continued hold of bottom button
+ * E: 000071.249959 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.255032 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.259972 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.265409 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.270156 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.275530 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.279975 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.285046 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.290906 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.296146 9 11 03 00 00 00 00 00 00 00
+ * E: 000071.301288 9 11 03 00 00 00 00 00 00 00
+ *
+ * Top wheel button hold while top wheel rotate CCW
+ * (I did not test the other combinations of this)
+ * E: 000022.253144 9 11 03 00 00 00 00 00 00 00
+ * E: 000022.258157 9 11 03 00 00 00 00 00 00 00
+ * E: 000022.262011 9 11 00 ff ff 00 00 00 00 00
+ * E: 000022.264015 9 11 03 00 00 00 00 00 00 00
+ * E: 000022.268976 9 11 03 00 00 00 00 00 00 00
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * NOTES ON SIMULTANEOUS BUTTON HOLDS
+ * (applies to vendor mode only)
+ * Value replacements for ease of reading:
+ * .7 = 0x40 (button 7, a wheel button)
+ * .1 = 0x01 (button 1, a pad button)
+ * rr = 0x00 (no buttons pressed)
+ *
+ * Press 7
+ * Press 1
+ * Release 7
+ * Release 1
+ * B: 000000.000152 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00
+ * B: 000000.781784 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000000.869845 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000001.095688 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000001.322635 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000001.543643 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000001.770652 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000001.885659 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 release of 7
+ * B: 000001.993620 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000002.220671 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000002.446589 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000002.672559 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000002.765183 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 release of 1
+ *
+ * Press 7
+ * Press 1
+ * Release 1
+ * Release 7
+ * B: 000017.071517 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00
+ * B: 000018.270461 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000018.419486 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000018.646438 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000018.872493 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000019.094422 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000019.320488 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000020.360505 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 release of 1 is not reported until 7 is released, then both are rapidly reported
+ * B: 000020.361091 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00
+ *
+ * Press 1
+ * Press 7
+ * Release 7
+ * Release 1
+ * B: 000031.516315 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000031.922299 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000032.144165 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000032.370262 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000032.396242 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00
+ * B: 000032.597270 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000032.818187 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000033.045143 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000033.267535 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 release of 7
+ * B: 000033.272602 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000033.494246 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000033.721266 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000033.947237 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000034.169294 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000034.183585 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 release of 1
+ *
+ * Press 1
+ * Press 7
+ * Release 1
+ * Release 7
+ * B: 000056.628429 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000057.046348 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000057.272044 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000057.494434 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000057.601224 42 08 e0 01 01 .7 00 00 00 00 00 00 00 00 00
+ * B: 000057.719262 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000057.946941 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000058.172346 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000058.393994 42 08 e0 01 01 .1 00 00 00 00 00 00 00 00 00
+ * B: 000059.434576 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00 release of 1 is not reported until 7 is released, then both are rapidly reported
+ * B: 000059.435857 42 08 e0 01 01 rr 00 00 00 00 00 00 00 00 00
+ */
+
+
+/* Filled in by udev-hid-bpf */
+char UDEV_PROP_HUION_FIRMWARE_ID[64];
+
+char EXPECTED_FIRMWARE_ID[] = "HUION_M22c_";
+
+__u8 last_button_state;
+
+static const __u8 disabled_rdesc_tablet[] = {
+ FixedSizeVendorReport(28) /* Input report 4 */
+};
+
+static const __u8 disabled_rdesc_wheel[] = {
+ FixedSizeVendorReport(9) /* Input report 17 */
+};
+
+static const __u8 fixed_rdesc_vendor[] = {
+ UsagePage_Digitizers
+ Usage_Dig_Pen
+ CollectionApplication(
+ ReportId(VENDOR_REPORT_ID)
+ UsagePage_Digitizers
+ Usage_Dig_Pen
+ CollectionPhysical(
+ /*
+ * I have only examined the tablet's behavior while using
+ * the PW600L pen, which does not have an eraser.
+ * Because of this, I don't know where the Eraser and Invert
+ * bits will go, or if they work as one would expect.
+ *
+ * For the time being, there is no expectation that a pen
+ * with an eraser will work without modifications here.
+ */
+ ReportSize(1)
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ ReportCount(3)
+ Usage_Dig_TipSwitch
+ Usage_Dig_BarrelSwitch
+ Usage_Dig_SecondaryBarrelSwitch
+ Input(Var|Abs)
+ PushPop(
+ ReportCount(1)
+ UsagePage_Button
+ Usage_i8(0x4a) /* (BTN_STYLUS3 + 1) & 0xff */
+ Input(Var|Abs)
+ )
+ ReportCount(3)
+ Input(Const)
+ ReportCount(1)
+ Usage_Dig_InRange
+ Input(Var|Abs)
+ ReportSize(16)
+ ReportCount(1)
+ PushPop(
+ UsagePage_GenericDesktop
+ Unit(cm)
+ UnitExponent(-2)
+ LogicalMinimum_i16(0)
+ PhysicalMinimum_i16(0)
+ /*
+ * The tablet has a logical maximum of 58760 x 33040
+ * and a claimed resolution of 5080 LPI (200 L/mm)
+ * This works out to a physical maximum of
+ * 293.8 x 165.2mm, which matches Huion's advertised
+ * active area dimensions from
+ * https://www.huion.com/products/pen_display/Kamvas/kamvas-13-gen-3.html
+ */
+ LogicalMaximum_i16(58760)
+ PhysicalMaximum_i16(2938)
+ Usage_GD_X
+ Input(Var|Abs)
+ LogicalMaximum_i16(33040)
+ PhysicalMaximum_i16(1652)
+ Usage_GD_Y
+ Input(Var|Abs)
+ )
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(16383)
+ Usage_Dig_TipPressure
+ Input(Var|Abs)
+ ReportCount(1)
+ Input(Const)
+ ReportSize(8)
+ ReportCount(2)
+ PushPop(
+ Unit(deg)
+ UnitExponent(0)
+ LogicalMinimum_i8(-60)
+ PhysicalMinimum_i8(-60)
+ LogicalMaximum_i8(60)
+ PhysicalMaximum_i8(60)
+ Usage_Dig_XTilt
+ Usage_Dig_YTilt
+ Input(Var|Abs)
+ )
+ )
+ )
+ UsagePage_GenericDesktop
+ Usage_GD_Keypad
+ CollectionApplication(
+ ReportId(CUSTOM_PAD_REPORT_ID)
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ UsagePage_Digitizers
+ Usage_Dig_TabletFunctionKeys
+ CollectionPhysical(
+ /*
+ * The first 3 bytes are somewhat vestigial and will
+ * always be set to zero. Their presence here is needed
+ * to ensure that this device will be detected as a
+ * tablet pad by software that otherwise wouldn't know
+ * any better.
+ */
+ /* (data[1] & 0x01) barrel switch */
+ ReportSize(1)
+ ReportCount(1)
+ Usage_Dig_BarrelSwitch
+ Input(Var|Abs)
+ ReportCount(7)
+ Input(Const)
+ /* data[2] X */
+ /* data[3] Y */
+ ReportSize(8)
+ ReportCount(2)
+ UsagePage_GenericDesktop
+ Usage_GD_X
+ Usage_GD_Y
+ Input(Var|Abs)
+ /*
+ * (data[4] & 0x01) button 1
+ * (data[4] & 0x02) button 2
+ * (data[4] & 0x04) button 3
+ * (data[4] & 0x08) button 4
+ * (data[4] & 0x10) button 5
+ * (data[4] & 0x20) button 6 (top wheel button)
+ * (data[4] & 0x40) button 7 (bottom wheel button)
+ */
+ ReportSize(1)
+ ReportCount(7)
+ UsagePage_Button
+ UsageMinimum_i8(1)
+ UsageMaximum_i8(7)
+ Input(Var|Abs)
+ ReportCount(1)
+ Input(Const)
+ /* data[5] top wheel (signed, positive clockwise) */
+ ReportSize(8)
+ ReportCount(1)
+ UsagePage_GenericDesktop
+ Usage_GD_Wheel
+ LogicalMinimum_i8(-1)
+ LogicalMaximum_i8(1)
+ Input(Var|Rel)
+ /* data[6] bottom wheel (signed, positive clockwise) */
+ UsagePage_Consumer
+ Usage_Con_ACPan
+ Input(Var|Rel)
+ )
+ /*
+ * The kernel will drop reports that are bigger than the
+ * largest report specified in the HID descriptor.
+ * Therefore, our modified descriptor needs to have at least one
+ * HID report that is as long as, or longer than, the largest
+ * report in the original descriptor.
+ *
+ * This macro expands to a no-op report that is padded to the
+ * provided length.
+ */
+ FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
+ )
+};
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(hid_fix_rdesc_huion_kamvas13_gen3, struct hid_bpf_ctx *hid_ctx)
+{
+ __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
+ __s32 rdesc_size = hid_ctx->size;
+ __u8 have_fw_id;
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
+ EXPECTED_FIRMWARE_ID,
+ sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
+
+ if (have_fw_id) {
+ /*
+ * Tablet should be in vendor mode.
+ * Disable the unused devices
+ */
+ if (rdesc_size == TABLET_DESCRIPTOR_LENGTH) {
+ __builtin_memcpy(data, disabled_rdesc_tablet,
+ sizeof(disabled_rdesc_tablet));
+ return sizeof(disabled_rdesc_tablet);
+ }
+
+ if (rdesc_size == WHEEL_DESCRIPTOR_LENGTH) {
+ __builtin_memcpy(data, disabled_rdesc_wheel,
+ sizeof(disabled_rdesc_wheel));
+ return sizeof(disabled_rdesc_wheel);
+ }
+ }
+
+ /*
+ * Regardless of which mode the tablet is in, always fix the vendor
+ * descriptor in case the udev property just happened to not be set
+ */
+ if (rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {
+ __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
+ return sizeof(fixed_rdesc_vendor);
+ }
+
+ return 0;
+}
+
+SEC(HID_BPF_DEVICE_EVENT)
+int BPF_PROG(hid_fix_event_huion_kamvas13_gen3, struct hid_bpf_ctx *hid_ctx)
+{
+ __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, VENDOR_REPORT_LENGTH /* size */);
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ /* Handle vendor reports only */
+ if (hid_ctx->size != VENDOR_REPORT_LENGTH)
+ return 0;
+ if (data[0] != VENDOR_REPORT_ID)
+ return 0;
+
+ __u8 report_subtype = (data[1] >> 4) & 0x0f;
+
+ if (report_subtype == VENDOR_REPORT_SUBTYPE_PEN ||
+ report_subtype == VENDOR_REPORT_SUBTYPE_PEN_OUT) {
+ /* Invert Y tilt */
+ data[11] = -data[11];
+
+ } else if (report_subtype == VENDOR_REPORT_SUBTYPE_BUTTONS ||
+ report_subtype == VENDOR_REPORT_SUBTYPE_WHEELS) {
+ struct pad_report {
+ __u8 report_id;
+ __u8 btn_stylus:1;
+ __u8 padding:7;
+ __u8 x;
+ __u8 y;
+ __u8 buttons;
+ __s8 top_wheel;
+ __s8 bottom_wheel;
+ } __attribute__((packed)) *pad_report;
+
+ __s8 top_wheel = 0;
+ __s8 bottom_wheel = 0;
+
+ switch (report_subtype) {
+ case VENDOR_REPORT_SUBTYPE_WHEELS:
+ /*
+ * The wheel direction byte is 1 for clockwise rotation
+ * and 2 for counter-clockwise.
+ * Change it to 1 and -1, respectively.
+ */
+ switch (data[3]) {
+ case 1:
+ top_wheel = (data[5] == 1) ? 1 : -1;
+ break;
+ case 2:
+ bottom_wheel = (data[5] == 1) ? 1 : -1;
+ break;
+ }
+ break;
+
+ case VENDOR_REPORT_SUBTYPE_BUTTONS:
+ /*
+ * If a button is already being held, ignore any new
+ * button event unless it's a release.
+ *
+ * The tablet only cleanly handles one button being held
+ * at a time, and trying to hold multiple buttons
+ * (particularly wheel+pad buttons) can result in sequences
+ * of reports that look like imaginary presses and releases.
+ *
+ * This is an imperfect way to filter out some of these
+ * reports.
+ */
+ if (last_button_state != 0x00 && data[4] != 0x00)
+ break;
+
+ last_button_state = data[4];
+ break;
+ }
+
+
+ pad_report = (struct pad_report *)data;
+
+ pad_report->report_id = CUSTOM_PAD_REPORT_ID;
+ pad_report->btn_stylus = 0;
+ pad_report->x = 0;
+ pad_report->y = 0;
+ pad_report->buttons = last_button_state;
+ pad_report->top_wheel = top_wheel;
+ pad_report->bottom_wheel = bottom_wheel;
+
+ return sizeof(struct pad_report);
+ }
+
+ return 0;
+}
+
+HID_BPF_OPS(huion_kamvas13_gen3) = {
+ .hid_device_event = (void *)hid_fix_event_huion_kamvas13_gen3,
+ .hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas13_gen3,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+ switch (ctx->rdesc_size) {
+ case VENDOR_DESCRIPTOR_LENGTH:
+ case TABLET_DESCRIPTOR_LENGTH:
+ case WHEEL_DESCRIPTOR_LENGTH:
+ ctx->retval = 0;
+ break;
+ default:
+ ctx->retval = -EINVAL;
+ }
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c b/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c
new file mode 100644
index 000000000000..ac66c6e65eb4
--- /dev/null
+++ b/drivers/hid/bpf/progs/Huion__Kamvas16Gen3.bpf.c
@@ -0,0 +1,724 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Nicholas LaPointe
+ * Copyright (c) 2025 Higgins Dragon
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include "hid_report_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_HUION 0x256c
+#define PID_KAMVAS16_GEN3 0x2009
+
+#define VENDOR_DESCRIPTOR_LENGTH 36
+#define TABLET_DESCRIPTOR_LENGTH 328
+#define WHEEL_DESCRIPTOR_LENGTH 200
+
+#define VENDOR_REPORT_ID 8
+#define VENDOR_REPORT_LENGTH 14
+
+#define VENDOR_REPORT_SUBTYPE_PEN 0x08
+#define VENDOR_REPORT_SUBTYPE_PEN_OUT 0x00
+#define VENDOR_REPORT_SUBTYPE_BUTTONS 0x0e
+#define VENDOR_REPORT_SUBTYPE_WHEELS 0x0f
+
+/* For the reports that we create ourselves */
+#define CUSTOM_PAD_REPORT_ID 9
+
+HID_BPF_CONFIG(
+ HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_HUION, PID_KAMVAS16_GEN3),
+);
+
+/*
+ * This tablet can send reports using one of two different data formats,
+ * depending on what "mode" the tablet is in.
+ *
+ * By default, the tablet will send reports that can be decoded using its
+ * included HID descriptors (descriptors 1 and 2, shown below).
+ * This mode will be called "firmware mode" throughout this file.
+ *
+ * The HID descriptor that describes pen events in firmware mode (descriptor 1)
+ * has multiple bugs:
+ * * "Secondary Tip Switch" instead of "Secondary Barrel Switch"
+ * * "Invert" instead of (or potentially shared with) third barrel button
+ * * Specified tablet area of 2048 in³ instead of 293.8 x 165.2mm
+ * * Specified tilt range of -90 to +90 instead of -60 to +60
+ *
+ * While these can be easily patched up by editing the descriptor, a larger
+ * problem with the firmware mode exists: it is impossible to tell which of the
+ * two wheels are being rotated (or having their central button pressed).
+ *
+ *
+ * By using a tool such as huion-switcher (https://github.com/whot/huion-switcher),
+ * the tablet can be made to send reports using a proprietary format that is not
+ * adequately described by its relevant descriptor (descriptor 0, shown below).
+ * This mode will be called "vendor mode" throughout this file.
+ *
+ * The reports sent while in vendor mode allow for proper decoding of the wheels.
+ *
+ * For simplicity and maximum functionality, this BPF focuses strictly on
+ * enabling one to make use of the vendor mode.
+ */
+
+/*
+ * DESCRIPTORS
+ * DESCRIPTOR 0
+ * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
+ * # 0x09, 0x01, // Usage (Vendor Usage 1) 3
+ * # 0xa1, 0x01, // Collection (Application) 5
+ * # 0x85, 0x08, // Report ID (8) 7
+ * # 0x75, 0x68, // Report Size (104) 9
+ * # 0x95, 0x01, // Report Count (1) 11
+ * # 0x09, 0x01, // Usage (Vendor Usage 1) 13
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 15
+ * # 0xc0, // End Collection 17
+ * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 18
+ * # 0x09, 0x01, // Usage (Vendor Usage 1) 21
+ * # 0xa1, 0x01, // Collection (Application) 23
+ * # 0x85, 0x16, // Report ID (22) 25
+ * # 0x75, 0x08, // Report Size (8) 27
+ * # 0x95, 0x07, // Report Count (7) 29
+ * # 0x09, 0x01, // Usage (Vendor Usage 1) 31
+ * # 0xb1, 0x02, // Feature (Data,Var,Abs) 33
+ * # 0xc0, // End Collection 35
+ * #
+ * R: 36 06 00 ff 09 01 a1 01 85 08 75 68 95 01 09 01 81 02 c0 06 00 ff 09 01 a1 01 85 16 75 08 95 07 09 01 b1 02 c0
+ * N: HUION Huion Tablet_GS1563
+ * I: 3 256c 2009
+ *
+ *
+ * DESCRIPTOR 1
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 0
+ * # 0x09, 0x02, // Usage (Pen) 2
+ * # 0xa1, 0x01, // Collection (Application) 4
+ * # 0x85, 0x0a, // Report ID (10) 6
+ * # 0x09, 0x20, // Usage (Stylus) 8
+ * # 0xa1, 0x01, // Collection (Application) 10
+ * # 0x09, 0x42, // Usage (Tip Switch) 12
+ * # 0x09, 0x44, // Usage (Barrel Switch) 14
+ * # 0x09, 0x43, // Usage (Secondary Tip Switch) 16
+ * # 0x09, 0x3c, // Usage (Invert) 18
+ * # 0x09, 0x45, // Usage (Eraser) 20
+ * # 0x15, 0x00, // Logical Minimum (0) 22
+ * # 0x25, 0x01, // Logical Maximum (1) 24
+ * # 0x75, 0x01, // Report Size (1) 26
+ * # 0x95, 0x06, // Report Count (6) 28
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 30
+ * # 0x09, 0x32, // Usage (In Range) 32
+ * # 0x75, 0x01, // Report Size (1) 34
+ * # 0x95, 0x01, // Report Count (1) 36
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 38
+ * # 0x81, 0x03, // Input (Cnst,Var,Abs) 40
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 42
+ * # 0x09, 0x30, // Usage (X) 44
+ * # 0x09, 0x31, // Usage (Y) 46
+ * # 0x55, 0x0d, // Unit Exponent (-3) 48
+ * # 0x65, 0x33, // Unit (EnglishLinear: in³) 50
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 52
+ * # 0x35, 0x00, // Physical Minimum (0) 55
+ * # 0x46, 0x00, 0x08, // Physical Maximum (2048) 57
+ * # 0x75, 0x10, // Report Size (16) 60
+ * # 0x95, 0x02, // Report Count (2) 62
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 64
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 66
+ * # 0x09, 0x30, // Usage (Tip Pressure) 68
+ * # 0x26, 0xff, 0x3f, // Logical Maximum (16383) 70
+ * # 0x75, 0x10, // Report Size (16) 73
+ * # 0x95, 0x01, // Report Count (1) 75
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 77
+ * # 0x09, 0x3d, // Usage (X Tilt) 79
+ * # 0x09, 0x3e, // Usage (Y Tilt) 81
+ * # 0x15, 0xa6, // Logical Minimum (-90) 83
+ * # 0x25, 0x5a, // Logical Maximum (90) 85
+ * # 0x75, 0x08, // Report Size (8) 87
+ * # 0x95, 0x02, // Report Count (2) 89
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 91
+ * # 0xc0, // End Collection 93
+ * # 0xc0, // End Collection 94
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 95
+ * # 0x09, 0x04, // Usage (Touch Screen) 97
+ * # 0xa1, 0x01, // Collection (Application) 99
+ * # 0x85, 0x04, // Report ID (4) 101
+ * # 0x09, 0x22, // Usage (Finger) 103
+ * # 0xa1, 0x02, // Collection (Logical) 105
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 107
+ * # 0x95, 0x01, // Report Count (1) 109
+ * # 0x75, 0x06, // Report Size (6) 111
+ * # 0x09, 0x51, // Usage (Contact Id) 113
+ * # 0x15, 0x00, // Logical Minimum (0) 115
+ * # 0x25, 0x3f, // Logical Maximum (63) 117
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 119
+ * # 0x09, 0x42, // Usage (Tip Switch) 121
+ * # 0x25, 0x01, // Logical Maximum (1) 123
+ * # 0x75, 0x01, // Report Size (1) 125
+ * # 0x95, 0x01, // Report Count (1) 127
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 129
+ * # 0x75, 0x01, // Report Size (1) 131
+ * # 0x95, 0x01, // Report Count (1) 133
+ * # 0x81, 0x03, // Input (Cnst,Var,Abs) 135
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 137
+ * # 0x75, 0x10, // Report Size (16) 139
+ * # 0x55, 0x0e, // Unit Exponent (-2) 141
+ * # 0x65, 0x11, // Unit (SILinear: cm) 143
+ * # 0x09, 0x30, // Usage (X) 145
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 147
+ * # 0x35, 0x00, // Physical Minimum (0) 150
+ * # 0x46, 0x15, 0x0c, // Physical Maximum (3093) 152
+ * # 0x81, 0x42, // Input (Data,Var,Abs,Null) 155
+ * # 0x09, 0x31, // Usage (Y) 157
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 159
+ * # 0x46, 0xcb, 0x06, // Physical Maximum (1739) 162
+ * # 0x81, 0x42, // Input (Data,Var,Abs,Null) 165
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 167
+ * # 0x09, 0x30, // Usage (Tip Pressure) 169
+ * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 171
+ * # 0x75, 0x10, // Report Size (16) 174
+ * # 0x95, 0x01, // Report Count (1) 176
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 178
+ * # 0xc0, // End Collection 180
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 181
+ * # 0x09, 0x22, // Usage (Finger) 183
+ * # 0xa1, 0x02, // Collection (Logical) 185
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 187
+ * # 0x95, 0x01, // Report Count (1) 189
+ * # 0x75, 0x06, // Report Size (6) 191
+ * # 0x09, 0x51, // Usage (Contact Id) 193
+ * # 0x15, 0x00, // Logical Minimum (0) 195
+ * # 0x25, 0x3f, // Logical Maximum (63) 197
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 199
+ * # 0x09, 0x42, // Usage (Tip Switch) 201
+ * # 0x25, 0x01, // Logical Maximum (1) 203
+ * # 0x75, 0x01, // Report Size (1) 205
+ * # 0x95, 0x01, // Report Count (1) 207
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 209
+ * # 0x75, 0x01, // Report Size (1) 211
+ * # 0x95, 0x01, // Report Count (1) 213
+ * # 0x81, 0x03, // Input (Cnst,Var,Abs) 215
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 217
+ * # 0x75, 0x10, // Report Size (16) 219
+ * # 0x55, 0x0e, // Unit Exponent (-2) 221
+ * # 0x65, 0x11, // Unit (SILinear: cm) 223
+ * # 0x09, 0x30, // Usage (X) 225
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 227
+ * # 0x35, 0x00, // Physical Minimum (0) 230
+ * # 0x46, 0x15, 0x0c, // Physical Maximum (3093) 232
+ * # 0x81, 0x42, // Input (Data,Var,Abs,Null) 235
+ * # 0x09, 0x31, // Usage (Y) 237
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 239
+ * # 0x46, 0xcb, 0x06, // Physical Maximum (1739) 242
+ * # 0x81, 0x42, // Input (Data,Var,Abs,Null) 245
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 247
+ * # 0x09, 0x30, // Usage (Tip Pressure) 249
+ * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 251
+ * # 0x75, 0x10, // Report Size (16) 254
+ * # 0x95, 0x01, // Report Count (1) 256
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 258
+ * # 0xc0, // End Collection 260
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 261
+ * # 0x09, 0x56, // Usage (Scan Time) 263
+ * # 0x55, 0x00, // Unit Exponent (0) 265
+ * # 0x65, 0x00, // Unit (None) 267
+ * # 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 269
+ * # 0x95, 0x01, // Report Count (1) 274
+ * # 0x75, 0x20, // Report Size (32) 276
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 278
+ * # 0x09, 0x54, // Usage (Contact Count) 280
+ * # 0x25, 0x7f, // Logical Maximum (127) 282
+ * # 0x95, 0x01, // Report Count (1) 284
+ * # 0x75, 0x08, // Report Size (8) 286
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 288
+ * # 0x75, 0x08, // Report Size (8) 290
+ * # 0x95, 0x08, // Report Count (8) 292
+ * # 0x81, 0x03, // Input (Cnst,Var,Abs) 294
+ * # 0x85, 0x05, // Report ID (5) 296
+ * # 0x09, 0x55, // Usage (Contact Max) 298
+ * # 0x25, 0x0a, // Logical Maximum (10) 300
+ * # 0x75, 0x08, // Report Size (8) 302
+ * # 0x95, 0x01, // Report Count (1) 304
+ * # 0xb1, 0x02, // Feature (Data,Var,Abs) 306
+ * # 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 308
+ * # 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 311
+ * # 0x85, 0x06, // Report ID (6) 313
+ * # 0x15, 0x00, // Logical Minimum (0) 315
+ * # 0x26, 0xff, 0x00, // Logical Maximum (255) 317
+ * # 0x75, 0x08, // Report Size (8) 320
+ * # 0x96, 0x00, 0x01, // Report Count (256) 322
+ * # 0xb1, 0x02, // Feature (Data,Var,Abs) 325
+ * # 0xc0, // End Collection 327
+ * #
+ * R: 328 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 43 09 3c 09 45 15 00 25 01 75 01 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff 7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 3f 75 10 95 01 81 02 09 3d 09 3e 15 a6 25 5a 75 08 95 02 81 02 c0 c0 05 0d 09 04 a1 01 85 04 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 22 a1 02 05 0d 95 01 75 06 09 51 15 00 25 3f 81 02 09 42 25 01 75 01 95 01 81 02 75 01 95 01 81 03 05 01 75 10 55 0e 65 11 09 30 26 ff 7f 35 00 46 15 0c 81 42 09 31 26 ff 7f 46 cb 06 81 42 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 c0 05 0d 09 56 55 00 65 00 27 ff ff ff 7f 95 01 75 20 81 02 09 54 25 7f 95 01 75 08 81 02 75 08 95 08 81 03 85 05 09 55 25 0a 75 08 95 01 b1 02 06 00 ff 09 c5 85 06 15 00 26 ff 00 75 08 96 00 01 b1 02 c0
+ * N: HUION Huion Tablet_GS1563
+ * I: 3 256c 2009
+ *
+ * DESCRIPTOR 2
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 0
+ * # 0x09, 0x0e, // Usage (System Multi-Axis Controller) 2
+ * # 0xa1, 0x01, // Collection (Application) 4
+ * # 0x85, 0x11, // Report ID (17) 6
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 8
+ * # 0x09, 0x21, // Usage (Puck) 10
+ * # 0xa1, 0x02, // Collection (Logical) 12
+ * # 0x15, 0x00, // Logical Minimum (0) 14
+ * # 0x25, 0x01, // Logical Maximum (1) 16
+ * # 0x75, 0x01, // Report Size (1) 18
+ * # 0x95, 0x01, // Report Count (1) 20
+ * # 0xa1, 0x00, // Collection (Physical) 22
+ * # 0x05, 0x09, // Usage Page (Button) 24
+ * # 0x09, 0x01, // Usage (Vendor Usage 0x01) 26
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 28
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 30
+ * # 0x09, 0x33, // Usage (Touch) 32
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 34
+ * # 0x95, 0x06, // Report Count (6) 36
+ * # 0x81, 0x03, // Input (Cnst,Var,Abs) 38
+ * # 0xa1, 0x02, // Collection (Logical) 40
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 42
+ * # 0x09, 0x37, // Usage (Dial) 44
+ * # 0x16, 0x00, 0x80, // Logical Minimum (-32768) 46
+ * # 0x26, 0xff, 0x7f, // Logical Maximum (32767) 49
+ * # 0x75, 0x10, // Report Size (16) 52
+ * # 0x95, 0x01, // Report Count (1) 54
+ * # 0x81, 0x06, // Input (Data,Var,Rel) 56
+ * # 0x35, 0x00, // Physical Minimum (0) 58
+ * # 0x46, 0x10, 0x0e, // Physical Maximum (3600) 60
+ * # 0x15, 0x00, // Logical Minimum (0) 63
+ * # 0x26, 0x10, 0x0e, // Logical Maximum (3600) 65
+ * # 0x09, 0x48, // Usage (Resolution Multiplier) 68
+ * # 0xb1, 0x02, // Feature (Data,Var,Abs) 70
+ * # 0x45, 0x00, // Physical Maximum (0) 72
+ * # 0xc0, // End Collection 74
+ * # 0x75, 0x08, // Report Size (8) 75
+ * # 0x95, 0x01, // Report Count (1) 77
+ * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 79
+ * # 0x75, 0x08, // Report Size (8) 81
+ * # 0x95, 0x01, // Report Count (1) 83
+ * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 85
+ * # 0x75, 0x08, // Report Size (8) 87
+ * # 0x95, 0x01, // Report Count (1) 89
+ * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 91
+ * # 0x75, 0x08, // Report Size (8) 93
+ * # 0x95, 0x01, // Report Count (1) 95
+ * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 97
+ * # 0x75, 0x08, // Report Size (8) 99
+ * # 0x95, 0x01, // Report Count (1) 101
+ * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 103
+ * # 0xc0, // End Collection 105
+ * # 0xc0, // End Collection 106
+ * # 0xc0, // End Collection 107
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 108
+ * # 0x09, 0x06, // Usage (Keyboard) 110
+ * # 0xa1, 0x01, // Collection (Application) 112
+ * # 0x85, 0x03, // Report ID (3) 114
+ * # 0x05, 0x07, // Usage Page (Keyboard) 116
+ * # 0x19, 0xe0, // Usage Minimum (224) 118
+ * # 0x29, 0xe7, // Usage Maximum (231) 120
+ * # 0x15, 0x00, // Logical Minimum (0) 122
+ * # 0x25, 0x01, // Logical Maximum (1) 124
+ * # 0x75, 0x01, // Report Size (1) 126
+ * # 0x95, 0x08, // Report Count (8) 128
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 130
+ * # 0x05, 0x07, // Usage Page (Keyboard) 132
+ * # 0x19, 0x00, // Usage Minimum (0) 134
+ * # 0x29, 0xff, // Usage Maximum (255) 136
+ * # 0x26, 0xff, 0x00, // Logical Maximum (255) 138
+ * # 0x75, 0x08, // Report Size (8) 141
+ * # 0x95, 0x06, // Report Count (6) 143
+ * # 0x81, 0x00, // Input (Data,Arr,Abs) 145
+ * # 0xc0, // End Collection 147
+ * # 0x05, 0x0c, // Usage Page (Consumer Devices) 148
+ * # 0x09, 0x01, // Usage (Consumer Control) 150
+ * # 0xa1, 0x01, // Collection (Application) 152
+ * # 0x85, 0x04, // Report ID (4) 154
+ * # 0x19, 0x01, // Usage Minimum (1) 156
+ * # 0x2a, 0x9c, 0x02, // Usage Maximum (668) 158
+ * # 0x15, 0x01, // Logical Minimum (1) 161
+ * # 0x26, 0x9c, 0x02, // Logical Maximum (668) 163
+ * # 0x95, 0x01, // Report Count (1) 166
+ * # 0x75, 0x10, // Report Size (16) 168
+ * # 0x81, 0x00, // Input (Data,Arr,Abs) 170
+ * # 0xc0, // End Collection 172
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 173
+ * # 0x09, 0x80, // Usage (System Control) 175
+ * # 0xa1, 0x01, // Collection (Application) 177
+ * # 0x85, 0x05, // Report ID (5) 179
+ * # 0x19, 0x81, // Usage Minimum (129) 181
+ * # 0x29, 0x83, // Usage Maximum (131) 183
+ * # 0x15, 0x00, // Logical Minimum (0) 185
+ * # 0x25, 0x01, // Logical Maximum (1) 187
+ * # 0x75, 0x01, // Report Size (1) 189
+ * # 0x95, 0x03, // Report Count (3) 191
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 193
+ * # 0x95, 0x05, // Report Count (5) 195
+ * # 0x81, 0x01, // Input (Cnst,Arr,Abs) 197
+ * # 0xc0, // End Collection 199
+ * #
+ * R: 200 05 01 09 0e a1 01 85 11 05 0d 09 21 a1 02 15 00 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 05 07 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0 05 0c 09 01 a1 01 85 04 19 01 2a 9c 02 15 01 26 9c 02 95 01 75 10 81 00 c0 05 01 09 80 a1 01 85 05 19 81 29 83 15 00 25 01 75 01 95 03 81 02 95 05 81 01 c0
+ * N: HUION Huion Tablet_GS1563
+ * I: 3 256c 2009
+ *
+ *
+ *
+ * VENDOR MODE
+ * HUION_FIRMWARE_ID="HUION_M22d_241101"
+ * HUION_MAGIC_BYTES="1403201101ac9900ff3fd81305080080083c4010"
+ *
+ * MAGIC BYTES
+ * [LogicalMaximum, X ] [LogicalMaximum, Y ] [LogicalMaximum, Pressure] [ LPI]
+ * 14 03 [ 20 11 01] [ ac 99 00] [ ff 3f] [d8 13] 05 08 00 80 08 3c 40 10
+ *
+ * See Huion__Kamvas13Gen3.bpf.c for more details on detailed button/dial reports and caveats. It's very
+ * similar to the Kamvas 16 Gen 3.
+ */
+
+
+/* Filled in by udev-hid-bpf */
+char UDEV_PROP_HUION_FIRMWARE_ID[64];
+
+char EXPECTED_FIRMWARE_ID[] = "HUION_M22d_";
+
+__u8 last_button_state;
+
+static const __u8 disabled_rdesc_tablet[] = {
+ FixedSizeVendorReport(28) /* Input report 4 */
+};
+
+static const __u8 disabled_rdesc_wheel[] = {
+ FixedSizeVendorReport(9) /* Input report 17 */
+};
+
+static const __u8 fixed_rdesc_vendor[] = {
+ UsagePage_Digitizers
+ Usage_Dig_Pen
+ CollectionApplication(
+ ReportId(VENDOR_REPORT_ID)
+ UsagePage_Digitizers
+ Usage_Dig_Pen
+ CollectionPhysical(
+ /*
+ * I have only examined the tablet's behavior while using
+ * the PW600L pen, which does not have an eraser.
+ * Because of this, I don't know where the Eraser and Invert
+ * bits will go, or if they work as one would expect.
+ *
+ * For the time being, there is no expectation that a pen
+ * with an eraser will work without modifications here.
+ */
+ ReportSize(1)
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ ReportCount(3)
+ Usage_Dig_TipSwitch
+ Usage_Dig_BarrelSwitch
+ Usage_Dig_SecondaryBarrelSwitch
+ Input(Var|Abs)
+ PushPop(
+ ReportCount(1)
+ UsagePage_Button
+ Usage_i8(0x4a) /* (BTN_STYLUS3 + 1) & 0xff */
+ Input(Var|Abs)
+ )
+ ReportCount(3)
+ Input(Const)
+ ReportCount(1)
+ Usage_Dig_InRange
+ Input(Var|Abs)
+ ReportSize(16)
+ ReportCount(1)
+ PushPop(
+ UsagePage_GenericDesktop
+ Unit(cm)
+ UnitExponent(-2)
+ LogicalMinimum_i16(0)
+ PhysicalMinimum_i16(0)
+ /*
+ * The tablet has a logical maximum of 69920 x 39340
+ * and a claimed resolution of 5080 LPI (200 L/mm)
+ * This works out to a physical maximum of
+ * 349.6 x 196.7mm, which matches Huion's advertised
+ * (rounded) active area dimensions from
+ * https://www.huion.com/products/pen_display/Kamvas/kamvas-16-gen-3.html
+ *
+ * The Kamvas uses data[8] for the 3rd byte of the X-axis, and adding
+ * that after data[2] and data[3] makes a contiguous little-endian
+ * 24-bit value. (See BPF_PROG below)
+ */
+ ReportSize(24)
+ LogicalMaximum_i32(69920)
+ PhysicalMaximum_i16(3496)
+ Usage_GD_X
+ Input(Var|Abs)
+ ReportSize(16)
+ LogicalMaximum_i16(39340)
+ PhysicalMaximum_i16(1967)
+ Usage_GD_Y
+ Input(Var|Abs)
+ )
+ ReportSize(16)
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(16383)
+ Usage_Dig_TipPressure
+ Input(Var|Abs)
+ ReportSize(8)
+ ReportCount(1)
+ Input(Const)
+ ReportCount(2)
+ PushPop(
+ Unit(deg)
+ UnitExponent(0)
+ LogicalMinimum_i8(-60)
+ PhysicalMinimum_i8(-60)
+ LogicalMaximum_i8(60)
+ PhysicalMaximum_i8(60)
+ Usage_Dig_XTilt
+ Usage_Dig_YTilt
+ Input(Var|Abs)
+ )
+ )
+ )
+ UsagePage_GenericDesktop
+ Usage_GD_Keypad
+ CollectionApplication(
+ ReportId(CUSTOM_PAD_REPORT_ID)
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ UsagePage_Digitizers
+ Usage_Dig_TabletFunctionKeys
+ CollectionPhysical(
+ /*
+ * The first 3 bytes are somewhat vestigial and will
+ * always be set to zero. Their presence here is needed
+ * to ensure that this device will be detected as a
+ * tablet pad by software that otherwise wouldn't know
+ * any better.
+ */
+ /* (data[1] & 0x01) barrel switch */
+ ReportSize(1)
+ ReportCount(1)
+ Usage_Dig_BarrelSwitch
+ Input(Var|Abs)
+ ReportCount(7)
+ Input(Const)
+ /* data[2] X */
+ /* data[3] Y */
+ ReportSize(8)
+ ReportCount(2)
+ UsagePage_GenericDesktop
+ Usage_GD_X
+ Usage_GD_Y
+ Input(Var|Abs)
+ /*
+ * (data[4] & 0x01) button 1
+ * (data[4] & 0x02) button 2
+ * (data[4] & 0x04) button 3
+ * (data[4] & 0x08) button 4
+ * (data[4] & 0x10) button 5
+ * (data[4] & 0x20) button 6
+ * (data[4] & 0x40) button 7 (top wheel button)
+ * (data[4] & 0x80) button 8 (bottom wheel button)
+ */
+ ReportSize(1)
+ ReportCount(8)
+ UsagePage_Button
+ UsageMinimum_i8(1)
+ UsageMaximum_i8(8)
+ Input(Var|Abs)
+ /* data[5] top wheel (signed, positive clockwise) */
+ ReportSize(8)
+ ReportCount(1)
+ UsagePage_GenericDesktop
+ Usage_GD_Wheel
+ LogicalMinimum_i8(-1)
+ LogicalMaximum_i8(1)
+ Input(Var|Rel)
+ /* data[6] bottom wheel (signed, positive clockwise) */
+ UsagePage_Consumer
+ Usage_Con_ACPan
+ Input(Var|Rel)
+ )
+ /*
+ * The kernel will drop reports that are bigger than the
+ * largest report specified in the HID descriptor.
+ * Therefore, our modified descriptor needs to have at least one
+ * HID report that is as long as, or longer than, the largest
+ * report in the original descriptor.
+ *
+ * This macro expands to a no-op report that is padded to the
+ * provided length.
+ */
+ FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
+ )
+};
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(hid_fix_rdesc_huion_kamvas16_gen3, struct hid_bpf_ctx *hid_ctx)
+{
+ __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
+ __s32 rdesc_size = hid_ctx->size;
+ __u8 have_fw_id;
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
+ EXPECTED_FIRMWARE_ID,
+ sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
+
+ if (have_fw_id) {
+ /*
+ * Tablet should be in vendor mode.
+ * Disable the unused devices
+ */
+ if (rdesc_size == TABLET_DESCRIPTOR_LENGTH) {
+ __builtin_memcpy(data, disabled_rdesc_tablet,
+ sizeof(disabled_rdesc_tablet));
+ return sizeof(disabled_rdesc_tablet);
+ }
+
+ if (rdesc_size == WHEEL_DESCRIPTOR_LENGTH) {
+ __builtin_memcpy(data, disabled_rdesc_wheel,
+ sizeof(disabled_rdesc_wheel));
+ return sizeof(disabled_rdesc_wheel);
+ }
+ }
+
+ /*
+ * Regardless of which mode the tablet is in, always fix the vendor
+ * descriptor in case the udev property just happened to not be set
+ */
+ if (rdesc_size == VENDOR_DESCRIPTOR_LENGTH) {
+ __builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
+ return sizeof(fixed_rdesc_vendor);
+ }
+
+ return 0;
+}
+
+SEC(HID_BPF_DEVICE_EVENT)
+int BPF_PROG(hid_fix_event_huion_kamvas16_gen3, struct hid_bpf_ctx *hid_ctx)
+{
+ __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, VENDOR_REPORT_LENGTH /* size */);
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ /* Handle vendor reports only */
+ if (hid_ctx->size != VENDOR_REPORT_LENGTH)
+ return 0;
+ if (data[0] != VENDOR_REPORT_ID)
+ return 0;
+
+ __u8 report_subtype = (data[1] >> 4) & 0x0f;
+
+ if (report_subtype == VENDOR_REPORT_SUBTYPE_PEN ||
+ report_subtype == VENDOR_REPORT_SUBTYPE_PEN_OUT) {
+ /* Invert Y tilt */
+ data[11] = -data[11];
+
+ /*
+ * Rearrange the bytes of the report so that
+ * [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
+ * will be arranged as
+ * [0, 1, 2, 3, 8, 4, 5, 6, 7, 9, 10, 11, 12, 13]
+ */
+ __u8 x_24 = data[8];
+
+ data[8] = data[7];
+ data[7] = data[6];
+ data[6] = data[5];
+ data[5] = data[4];
+
+ data[4] = x_24;
+
+ } else if (report_subtype == VENDOR_REPORT_SUBTYPE_BUTTONS ||
+ report_subtype == VENDOR_REPORT_SUBTYPE_WHEELS) {
+ struct pad_report {
+ __u8 report_id;
+ __u8 btn_stylus:1;
+ __u8 padding:7;
+ __u8 x;
+ __u8 y;
+ __u8 buttons;
+ __s8 top_wheel;
+ __s8 bottom_wheel;
+ } __attribute__((packed)) *pad_report;
+
+ __s8 top_wheel = 0;
+ __s8 bottom_wheel = 0;
+
+ switch (report_subtype) {
+ case VENDOR_REPORT_SUBTYPE_WHEELS:
+ /*
+ * The wheel direction byte is 1 for clockwise rotation
+ * and 2 for counter-clockwise.
+ * Change it to 1 and -1, respectively.
+ */
+ switch (data[3]) {
+ case 1:
+ top_wheel = (data[5] == 1) ? 1 : -1;
+ break;
+ case 2:
+ bottom_wheel = (data[5] == 1) ? 1 : -1;
+ break;
+ }
+ break;
+
+ case VENDOR_REPORT_SUBTYPE_BUTTONS:
+ /*
+ * If a button is already being held, ignore any new
+ * button event unless it's a release.
+ *
+ * The tablet only cleanly handles one button being held
+ * at a time, and trying to hold multiple buttons
+ * (particularly wheel+pad buttons) can result in sequences
+ * of reports that look like imaginary presses and releases.
+ *
+ * This is an imperfect way to filter out some of these
+ * reports.
+ */
+ if (last_button_state != 0x00 && data[4] != 0x00)
+ break;
+
+ last_button_state = data[4];
+ break;
+ }
+
+ pad_report = (struct pad_report *)data;
+
+ pad_report->report_id = CUSTOM_PAD_REPORT_ID;
+ pad_report->btn_stylus = 0;
+ pad_report->x = 0;
+ pad_report->y = 0;
+ pad_report->buttons = last_button_state;
+ pad_report->top_wheel = top_wheel;
+ pad_report->bottom_wheel = bottom_wheel;
+
+ return sizeof(struct pad_report);
+ }
+
+ return 0;
+}
+
+HID_BPF_OPS(huion_kamvas16_gen3) = {
+ .hid_device_event = (void *)hid_fix_event_huion_kamvas16_gen3,
+ .hid_rdesc_fixup = (void *)hid_fix_rdesc_huion_kamvas16_gen3,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+ switch (ctx->rdesc_size) {
+ case VENDOR_DESCRIPTOR_LENGTH:
+ case TABLET_DESCRIPTOR_LENGTH:
+ case WHEEL_DESCRIPTOR_LENGTH:
+ ctx->retval = 0;
+ break;
+ default:
+ ctx->retval = -EINVAL;
+ }
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c b/drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c
new file mode 100644
index 000000000000..b17719d6d9c7
--- /dev/null
+++ b/drivers/hid/bpf/progs/Logitech__SpaceNavigator.bpf.c
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Curran Muhlberger
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_LOGITECH 0x046D
+#define PID_SPACENAVIGATOR 0xC626
+
+HID_BPF_CONFIG(
+ HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_LOGITECH, PID_SPACENAVIGATOR)
+);
+
+/*
+ * The 3Dconnexion SpaceNavigator 3D Mouse is a multi-axis controller with 6
+ * axes (grouped as X,Y,Z and Rx,Ry,Rz). Axis data is absolute, but the report
+ * descriptor erroneously declares it to be relative. We fix the report
+ * descriptor to mark both axis collections as absolute.
+ *
+ * The kernel attempted to fix this in commit 24985cf68612 (HID: support
+ * Logitech/3DConnexion SpaceTraveler and SpaceNavigator), but the descriptor
+ * data offsets are incorrect for at least some SpaceNavigator units.
+ */
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ /* Offset of Input item in X,Y,Z and Rx,Ry,Rz collections for all known
+ * firmware variants.
+ * - 2009 model: X,Y,Z @ 32-33, Rx,Ry,Rz @ 49-50 (fixup originally
+ * applied in kernel)
+ * - 2016 model (size==228): X,Y,Z @ 36-37, Rx,Ry,Rz @ 53-54
+ *
+ * The descriptor size of the 2009 model is not known, and there is evidence
+ * for at least two other variants (with sizes 202 & 217) besides the 2016
+ * model, so we try all known offsets regardless of descriptor size.
+ */
+ const u8 offsets[] = {32, 36, 49, 53};
+
+ for (size_t idx = 0; idx < ARRAY_SIZE(offsets); idx++) {
+ u8 offset = offsets[idx];
+
+ /* if Input (Data,Var,Rel) , make it Input (Data,Var,Abs) */
+ if (data[offset] == 0x81 && data[offset + 1] == 0x06)
+ data[offset + 1] = 0x02;
+ }
+
+ return 0;
+}
+
+HID_BPF_OPS(logitech_spacenavigator) = {
+ .hid_rdesc_fixup = (void *)hid_fix_rdesc,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+ /* Ensure report descriptor size matches one of the known variants. */
+ if (ctx->rdesc_size != 202 &&
+ ctx->rdesc_size != 217 &&
+ ctx->rdesc_size != 228) {
+ ctx->retval = -EINVAL;
+ return 0;
+ }
+
+ /* Check whether the kernel has already applied the fix. */
+ if ((ctx->rdesc[32] == 0x81 && ctx->rdesc[33] == 0x02 &&
+ ctx->rdesc[49] == 0x81 && ctx->rdesc[50] == 0x02) ||
+ (ctx->rdesc[36] == 0x81 && ctx->rdesc[37] == 0x02 &&
+ ctx->rdesc[53] == 0x81 && ctx->rdesc[54] == 0x02))
+ ctx->retval = -EINVAL;
+ else
+ ctx->retval = 0;
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c b/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c
new file mode 100644
index 000000000000..156d75af516d
--- /dev/null
+++ b/drivers/hid/bpf/progs/WALTOP__Batteryless-Tablet.bpf.c
@@ -0,0 +1,321 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Red Hat
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_WALTOP 0x172F
+#define PID_BATTERYLESS_TABLET 0x0505
+
+HID_BPF_CONFIG(
+ HID_DEVICE(BUS_USB, HID_GROUP_ANY, VID_WALTOP, PID_BATTERYLESS_TABLET)
+);
+
+#define EXPECTED_RDESC_SIZE 335
+#define PEN_REPORT_ID 16
+
+#define TIP_SWITCH BIT(0)
+#define BARREL_SWITCH BIT(1)
+#define SECONDARY_BARREL_SWITCH BIT(5)
+
+static __u8 last_button_state;
+
+static const __u8 fixed_rdesc[] = {
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x02, // Usage (Mouse)
+ 0xa1, 0x01, // Collection (Application)
+ 0x85, 0x01, // Report ID (1)
+ 0x09, 0x01, // Usage (Pointer)
+ 0xa1, 0x00, // Collection (Physical)
+ 0x05, 0x09, // Usage Page (Button)
+ 0x19, 0x01, // Usage Minimum (1)
+ 0x29, 0x05, // Usage Maximum (5)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x05, // Report Count (5)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0x75, 0x03, // Report Size (3)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x03, // Input (Cnst,Var,Abs)
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x30, // Usage (X)
+ 0x09, 0x31, // Usage (Y)
+ 0x09, 0x38, // Usage (Wheel)
+ 0x15, 0x81, // Logical Minimum (-127)
+ 0x25, 0x7f, // Logical Maximum (127)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x03, // Report Count (3)
+ 0x81, 0x06, // Input (Data,Var,Rel)
+ 0x05, 0x0c, // Usage Page (Consumer)
+ 0x15, 0x81, // Logical Minimum (-127)
+ 0x25, 0x7f, // Logical Maximum (127)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x01, // Report Count (1)
+ 0x0a, 0x38, 0x02, // Usage (AC Pan)
+ 0x81, 0x06, // Input (Data,Var,Rel)
+ 0xc0, // End Collection
+ 0xc0, // End Collection
+ 0x05, 0x0d, // Usage Page (Digitizers)
+ 0x09, 0x02, // Usage (Pen)
+ 0xa1, 0x01, // Collection (Application)
+ 0x85, 0x02, // Report ID (2)
+ 0x09, 0x20, // Usage (Stylus)
+ 0xa1, 0x00, // Collection (Physical)
+ 0x09, 0x00, // Usage (0x0000)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xff, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x09, // Report Count (9)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0x09, 0x3f, // Usage (Azimuth)
+ 0x09, 0x40, // Usage (Altitude)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xff, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x02, // Report Count (2)
+ 0xb1, 0x02, // Feature (Data,Var,Abs)
+ 0xc0, // End Collection
+ 0x85, 0x05, // Report ID (5)
+ 0x05, 0x0d, // Usage Page (Digitizers)
+ 0x09, 0x20, // Usage (Stylus)
+ 0xa1, 0x00, // Collection (Physical)
+ 0x09, 0x00, // Usage (0x0000)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xff, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x07, // Report Count (7)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0xc0, // End Collection
+ 0x85, 0x0a, // Report ID (10)
+ 0x05, 0x0d, // Usage Page (Digitizers)
+ 0x09, 0x20, // Usage (Stylus)
+ 0xa1, 0x00, // Collection (Physical)
+ 0x09, 0x00, // Usage (0x0000)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xff, 0x00, // Logical Maximum (255)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x07, // Report Count (7)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0xc0, // End Collection
+ 0x85, 0x10, // Report ID (16)
+ 0x09, 0x20, // Usage (Stylus)
+ 0xa1, 0x00, // Collection (Physical)
+ 0x09, 0x42, // Usage (Tip Switch)
+ 0x09, 0x44, // Usage (Barrel Switch)
+ 0x09, 0x3c, // Usage (Invert)
+ 0x09, 0x45, // Usage (Eraser)
+ 0x09, 0x32, // Usage (In Range)
+ 0x09, 0x5a, // Usage (Secondary Barrel Switch) <-- added
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x06, // Report Count (6) <--- changed from 5
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0x95, 0x02, // Report Count (2) <--- changed from 3
+ 0x81, 0x03, // Input (Cnst,Var,Abs)
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x30, // Usage (X)
+ 0x75, 0x10, // Report Size (16)
+ 0x95, 0x01, // Report Count (1)
+ 0xa4, // Push
+ 0x55, 0x0d, // Unit Exponent (-3)
+ 0x65, 0x33, // Unit (EnglishLinear: in³)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0x00, 0x7d, // Logical Maximum (32000)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x46, 0x00, 0x7d, // Physical Maximum (32000)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0x09, 0x31, // Usage (Y)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0x20, 0x4e, // Logical Maximum (20000)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x46, 0x20, 0x4e, // Physical Maximum (20000)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0x05, 0x0d, // Usage Page (Digitizers)
+ 0x09, 0x30, // Usage (Tip Pressure)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x26, 0xff, 0x07, // Logical Maximum (2047)
+ 0x35, 0x00, // Physical Minimum (0)
+ 0x46, 0xff, 0x07, // Physical Maximum (2047)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0x05, 0x0d, // Usage Page (Digitizers)
+ 0x09, 0x3d, // Usage (X Tilt)
+ 0x09, 0x3e, // Usage (Y Tilt)
+ 0x15, 0xc4, // Logical Minimum (-60) <- changed from -127
+ 0x25, 0x3c, // Logical Maximum (60) <- changed from 127
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x02, // Report Count (2)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0xc0, // End Collection
+ 0xc0, // End Collection
+ 0x05, 0x01, // Usage Page (Generic Desktop)
+ 0x09, 0x06, // Usage (Keyboard)
+ 0xa1, 0x01, // Collection (Application)
+ 0x85, 0x0d, // Report ID (13)
+ 0x05, 0x07, // Usage Page (Keyboard/Keypad)
+ 0x19, 0xe0, // Usage Minimum (224)
+ 0x29, 0xe7, // Usage Maximum (231)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x08, // Report Count (8)
+ 0x81, 0x02, // Input (Data,Var,Abs)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x01, // Input (Cnst,Arr,Abs)
+ 0x05, 0x07, // Usage Page (Keyboard/Keypad)
+ 0x19, 0x00, // Usage Minimum (0)
+ 0x29, 0x65, // Usage Maximum (101)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x65, // Logical Maximum (101)
+ 0x75, 0x08, // Report Size (8)
+ 0x95, 0x05, // Report Count (5)
+ 0x81, 0x00, // Input (Data,Arr,Abs)
+ 0xc0, // End Collection
+ 0x05, 0x0c, // Usage Page (Consumer)
+ 0x09, 0x01, // Usage (Consumer Control)
+ 0xa1, 0x01, // Collection (Application)
+ 0x85, 0x0c, // Report ID (12)
+ 0x09, 0xe9, // Usage (Volume Increment)
+ 0x09, 0xea, // Usage (Volume Decrement)
+ 0x09, 0xe2, // Usage (Mute)
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x01, // Logical Maximum (1)
+ 0x75, 0x01, // Report Size (1)
+ 0x95, 0x03, // Report Count (3)
+ 0x81, 0x06, // Input (Data,Var,Rel)
+ 0x75, 0x05, // Report Size (5)
+ 0x95, 0x01, // Report Count (1)
+ 0x81, 0x07, // Input (Cnst,Var,Rel)
+ 0xc0, // End Collection
+};
+
+static inline unsigned int bitwidth32(__u32 x)
+{
+ return 32 - __builtin_clzg(x, 32);
+}
+
+static inline unsigned int floor_log2_32(__u32 x)
+{
+ return bitwidth32(x) - 1;
+}
+
+/* Maps the interval [0, 2047] to itself using a scaled
+ * approximation of the function log2(x+1).
+ */
+static unsigned int scaled_log2(__u16 v)
+{
+ const unsigned int XMAX = 2047;
+ const unsigned int YMAX = 11; /* log2(2048) = 11 */
+
+ unsigned int x = v + 1;
+ unsigned int n = floor_log2_32(x);
+ unsigned int b = 1 << n;
+
+ /* Fixed-point fraction in [0, 1), linearly
+ * interpolated using delta-y = 1 and
+ * delta-x = (2b - b) = b.
+ */
+ unsigned int frac = (x - b) << YMAX;
+ unsigned int lerp = frac / b;
+ unsigned int log2 = (n << YMAX) + lerp;
+
+ return ((log2 * XMAX) / YMAX) >> YMAX;
+}
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4096 /* size */);
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ __builtin_memcpy(data, fixed_rdesc, sizeof(fixed_rdesc));
+
+ return sizeof(fixed_rdesc);
+}
+
+SEC(HID_BPF_DEVICE_EVENT)
+int BPF_PROG(waltop_fix_events, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ __u8 report_id = data[0];
+
+ if (report_id != PEN_REPORT_ID)
+ return 0;
+
+ /* On this tablet if the secondary barrel switch is pressed,
+ * the tablet sends tip down and barrel down. Change this to
+ * just secondary barrel down when there is no ambiguity.
+ *
+ * It's possible that there is a bug in the firmware and the
+ * device intends to set invert + eraser instead (i.e. the
+ * pysical button is an eraser button) but since
+ * the pressure is always zero, said eraser button
+ * would be useless anyway.
+ *
+ * So let's just change the button to secondary barrel down.
+ */
+
+ __u8 tip_switch = data[1] & TIP_SWITCH;
+ __u8 barrel_switch = data[1] & BARREL_SWITCH;
+
+ __u8 tip_held = last_button_state & TIP_SWITCH;
+ __u8 barrel_held = last_button_state & BARREL_SWITCH;
+
+ if (tip_switch && barrel_switch && !tip_held && !barrel_held) {
+ data[1] &= ~(TIP_SWITCH | BARREL_SWITCH); /* release tip and barrel */
+ data[1] |= SECONDARY_BARREL_SWITCH; /* set secondary barrel switch */
+ }
+
+ last_button_state = data[1];
+
+ /* The pressure sensor on this tablet maps around half of the
+ * logical pressure range into the interval [0-100]. Further
+ * pressure causes the sensor value to increase exponentially
+ * up to a maximum value of 2047.
+ *
+ * The values 12 and 102 were chosen to have an integer slope
+ * with smooth transition between the two curves around the
+ * value 100.
+ */
+
+ __u16 pressure = (((__u16)data[6]) << 0) | (((__u16)data[7]) << 8);
+
+ if (pressure <= 102)
+ pressure *= 12;
+ else
+ pressure = scaled_log2(pressure);
+
+ data[6] = pressure >> 0;
+ data[7] = pressure >> 8;
+
+ return 0;
+}
+
+HID_BPF_OPS(waltop_batteryless) = {
+ .hid_device_event = (void *)waltop_fix_events,
+ .hid_rdesc_fixup = (void *)hid_fix_rdesc,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+ if (ctx->rdesc_size == EXPECTED_RDESC_SIZE)
+ ctx->retval = 0;
+ else
+ ctx->retval = -EINVAL;
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c b/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c
new file mode 100644
index 000000000000..2502fcc9ede6
--- /dev/null
+++ b/drivers/hid/bpf/progs/XPPen__Deco01V3.bpf.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Red Hat
+ */
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include "hid_report_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_UGEE 0x28BD /* VID is shared with SinoWealth and Glorious and prob others */
+#define PID_DECO_01_V3 0x0947
+
+HID_BPF_CONFIG(
+ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_01_V3),
+);
+
+/*
+ * Default report descriptor reports:
+ * - a report descriptor for the pad buttons, reported as key sequences
+ * - a report descriptor for the pen
+ * - a vendor-specific report descriptor
+ *
+ * The Pad report descriptor, see
+ * https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/issues/54
+ *
+ * # Report descriptor length: 102 bytes
+ * 0x05, 0x01, // Usage Page (Generic Desktop) 0
+ * 0x09, 0x02, // Usage (Mouse) 2
+ * 0xa1, 0x01, // Collection (Application) 4
+ * 0x85, 0x09, // Report ID (9) 6
+ * 0x09, 0x01, // Usage (Pointer) 8
+ * 0xa1, 0x00, // Collection (Physical) 10
+ * 0x05, 0x09, // Usage Page (Button) 12
+ * 0x19, 0x01, // UsageMinimum (1) 14
+ * 0x29, 0x03, // UsageMaximum (3) 16
+ * 0x15, 0x00, // Logical Minimum (0) 18
+ * 0x25, 0x01, // Logical Maximum (1) 20
+ * 0x95, 0x03, // Report Count (3) 22
+ * 0x75, 0x01, // Report Size (1) 24
+ * 0x81, 0x02, // Input (Data,Var,Abs) 26
+ * 0x95, 0x05, // Report Count (5) 28
+ * 0x81, 0x01, // Input (Cnst,Arr,Abs) 30
+ * 0x05, 0x01, // Usage Page (Generic Desktop) 32
+ * 0x09, 0x30, // Usage (X) 34
+ * 0x09, 0x31, // Usage (Y) 36
+ * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 38
+ * 0x95, 0x02, // Report Count (2) 41
+ * 0x75, 0x10, // Report Size (16) 43
+ * 0x81, 0x02, // Input (Data,Var,Abs) 45
+ * 0x05, 0x0d, // Usage Page (Digitizers) 47
+ * 0x09, 0x30, // Usage (Tip Pressure) 49
+ * 0x26, 0xff, 0x07, // Logical Maximum (2047) 51
+ * 0x95, 0x01, // Report Count (1) 54
+ * 0x75, 0x10, // Report Size (16) 56
+ * 0x81, 0x02, // Input (Data,Var,Abs) 58
+ * 0xc0, // End Collection 60
+ * 0xc0, // End Collection 61
+ * 0x05, 0x01, // Usage Page (Generic Desktop) 62
+ * 0x09, 0x06, // Usage (Keyboard) 64
+ * 0xa1, 0x01, // Collection (Application) 66
+ * 0x85, 0x06, // Report ID (6) 68
+ * 0x05, 0x07, // Usage Page (Keyboard/Keypad) 70
+ * 0x19, 0xe0, // UsageMinimum (224) 72
+ * 0x29, 0xe7, // UsageMaximum (231) 74
+ * 0x15, 0x00, // Logical Minimum (0) 76
+ * 0x25, 0x01, // Logical Maximum (1) 78
+ * 0x75, 0x01, // Report Size (1) 80
+ * 0x95, 0x08, // Report Count (8) 82
+ * 0x81, 0x02, // Input (Data,Var,Abs) 84
+ * 0x05, 0x07, // Usage Page (Keyboard/Keypad) 86
+ * 0x19, 0x00, // UsageMinimum (0) 88
+ * 0x29, 0xff, // UsageMaximum (255) 90
+ * 0x26, 0xff, 0x00, // Logical Maximum (255) 92
+ * 0x75, 0x08, // Report Size (8) 95
+ * 0x95, 0x06, // Report Count (6) 97
+ * 0x81, 0x00, // Input (Data,Arr,Abs) 99
+ * 0xc0, // End Collection 101
+ *
+ * And key events for buttons top->bottom are:
+ * Buttons released: 06 00 00 00 00 00 00 00
+ * Button1: 06 00 05 00 00 00 00 00 -> b
+ * Button2: 06 00 08 00 00 00 00 00 -> e
+ * Button3: 06 04 00 00 00 00 00 00 -> LAlt
+ * Button4: 06 00 2c 00 00 00 00 00 -> Space
+ * Button5: 06 01 16 00 00 00 00 00 -> LControl + s
+ * Button6: 06 01 1d 00 00 00 00 00 -> LControl + z
+ * Button7: 06 01 57 00 00 00 00 00 -> LControl + Keypad Plus
+ * Button8: 06 01 56 00 00 00 00 00 -> LControl + Keypad Dash
+ *
+ * When multiple buttons are pressed at the same time, the values used to
+ * identify the buttons are identical, but they appear in different bytes of the
+ * record. For example, when button 2 (0x08) and button 1 (0x05) are pressed,
+ * this is the report:
+ *
+ * Buttons 2 and 1: 06 00 08 05 00 00 00 00 -> e + b
+ *
+ * Buttons 1, 2, 4, 5 and 6 can be matched by finding their values in the
+ * report.
+ *
+ * Button 3 is pressed when the 3rd bit is 1. For example, pressing buttons 3
+ * and 5 generates this report:
+ *
+ * Buttons 3 and 5: 06 05 16 00 00 00 00 00 -> LControl + LAlt + s
+ * -- --
+ * | |
+ * | `- Button 5 (0x16)
+ * `- 0x05 = 0101. Button 3 is pressed
+ * ^
+ *
+ * pad_buttons contains a list of buttons that can be matched in
+ * HID_BPF_DEVICE_EVENT. Button 3 as it has a dedicated bit.
+ *
+ *
+ * The Pen report descriptor announces a wrong tilt range:
+ *
+ * Report descriptor length: 109 bytes
+ * 0x05, 0x0d, // Usage Page (Digitizers) 0
+ * 0x09, 0x02, // Usage (Pen) 2
+ * 0xa1, 0x01, // Collection (Application) 4
+ * 0x85, 0x07, // Report ID (7) 6
+ * 0x09, 0x20, // Usage (Stylus) 8
+ * 0xa1, 0x01, // Collection (Application) 10
+ * 0x09, 0x42, // Usage (Tip Switch) 12
+ * 0x09, 0x44, // Usage (Barrel Switch) 14
+ * 0x09, 0x45, // Usage (Eraser) 16
+ * 0x09, 0x3c, // Usage (Invert) 18
+ * 0x15, 0x00, // Logical Minimum (0) 20
+ * 0x25, 0x01, // Logical Maximum (1) 22
+ * 0x75, 0x01, // Report Size (1) 24
+ * 0x95, 0x04, // Report Count (4) 26
+ * 0x81, 0x02, // Input (Data,Var,Abs) 28
+ * 0x95, 0x01, // Report Count (1) 30
+ * 0x81, 0x03, // Input (Cnst,Var,Abs) 32
+ * 0x09, 0x32, // Usage (In Range) 34
+ * 0x95, 0x01, // Report Count (1) 36
+ * 0x81, 0x02, // Input (Data,Var,Abs) 38
+ * 0x95, 0x02, // Report Count (2) 40
+ * 0x81, 0x03, // Input (Cnst,Var,Abs) 42
+ * 0x75, 0x10, // Report Size (16) 44
+ * 0x95, 0x01, // Report Count (1) 46
+ * 0x35, 0x00, // Physical Minimum (0) 48
+ * 0xa4, // Push 50
+ * 0x05, 0x01, // Usage Page (Generic Desktop) 51
+ * 0x09, 0x30, // Usage (X) 53
+ * 0x65, 0x13, // Unit (EnglishLinear: in) 55
+ * 0x55, 0x0d, // Unit Exponent (-3) 57
+ * 0x46, 0x10, 0x27, // Physical Maximum (10000) 59
+ * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 62
+ * 0x81, 0x02, // Input (Data,Var,Abs) 65
+ * 0x09, 0x31, // Usage (Y) 67
+ * 0x46, 0x6a, 0x18, // Physical Maximum (6250) 69
+ * 0x26, 0xff, 0x7f, // Logical Maximum (32767) 72
+ * 0x81, 0x02, // Input (Data,Var,Abs) 75
+ * 0xb4, // Pop 77
+ * 0x09, 0x30, // Usage (X) 78
+ * 0x45, 0x00, // Physical Maximum (0) 80
+ * 0x26, 0xff, 0x3f, // Logical Maximum (16383) 82
+ * 0x81, 0x42, // Input (Data,Var,Abs,Null) 85
+ * 0x09, 0x3d, // Usage (Start) 87
+ * 0x15, 0x81, // Logical Minimum (-127) 89 <- Change from -127 to -60
+ * 0x25, 0x7f, // Logical Maximum (127) 91 <- Change from 127 to 60
+ * 0x75, 0x08, // Report Size (8) 93
+ * 0x95, 0x01, // Report Count (1) 95
+ * 0x81, 0x02, // Input (Data,Var,Abs) 97
+ * 0x09, 0x3e, // Usage (Select) 99
+ * 0x15, 0x81, // Logical Minimum (-127) 101 <- Change from -127 to -60
+ * 0x25, 0x7f, // Logical Maximum (127) 103 <- Change from 127 to 60
+ * 0x81, 0x02, // Input (Data,Var,Abs) 105
+ * 0xc0, // End Collection 107
+ * 0xc0, // End Collection 108
+ */
+
+#define PEN_REPORT_DESCRIPTOR_LENGTH 109
+#define PAD_REPORT_DESCRIPTOR_LENGTH 102
+#define PAD_REPORT_LENGTH 8
+#define PAD_REPORT_ID 6
+#define PAD_NUM_BUTTONS 8
+
+static const __u8 fixed_rdesc_pad[] = {
+ UsagePage_GenericDesktop
+ Usage_GD_Keypad
+ CollectionApplication(
+ // Byte 0 in report is the report ID
+ ReportId(PAD_REPORT_ID)
+ ReportCount(1)
+ ReportSize(8)
+ UsagePage_Digitizers
+ Usage_Dig_TabletFunctionKeys
+ CollectionPhysical(
+ // Byte 1 is the button state
+ UsagePage_Button
+ UsageMinimum_i8(0x01)
+ UsageMaximum_i8(PAD_NUM_BUTTONS)
+ LogicalMinimum_i8(0x0)
+ LogicalMaximum_i8(0x1)
+ ReportCount(PAD_NUM_BUTTONS)
+ ReportSize(1)
+ Input(Var|Abs)
+ // Byte 2 in report - just exists so we get to be a tablet pad
+ UsagePage_Digitizers
+ Usage_Dig_BarrelSwitch // BTN_STYLUS
+ ReportCount(1)
+ ReportSize(1)
+ Input(Var|Abs)
+ ReportCount(7) // padding
+ Input(Const)
+ // Bytes 3/4 in report - just exists so we get to be a tablet pad
+ UsagePage_GenericDesktop
+ Usage_GD_X
+ Usage_GD_Y
+ ReportCount(2)
+ ReportSize(8)
+ Input(Var|Abs)
+ // Byte 5-7 are padding so we match the original report lengtth
+ ReportCount(3)
+ ReportSize(8)
+ Input(Const)
+ )
+ )
+};
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(xppen_deco01v3_rdesc_fixup, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
+
+ const __u8 wrong_logical_range[] = {0x15, 0x81, 0x25, 0x7f};
+ const __u8 correct_logical_range[] = {0x15, 0xc4, 0x25, 0x3c};
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ switch (hctx->size) {
+ case PAD_REPORT_DESCRIPTOR_LENGTH:
+ __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
+ return sizeof(fixed_rdesc_pad);
+ case PEN_REPORT_DESCRIPTOR_LENGTH:
+ if (__builtin_memcmp(&data[89], wrong_logical_range,
+ sizeof(wrong_logical_range)) == 0)
+ __builtin_memcpy(&data[89], correct_logical_range,
+ sizeof(correct_logical_range));
+ if (__builtin_memcmp(&data[101], wrong_logical_range,
+ sizeof(wrong_logical_range)) == 0)
+ __builtin_memcpy(&data[101], correct_logical_range,
+ sizeof(correct_logical_range));
+ break;
+ }
+
+ return 0;
+}
+
+SEC(HID_BPF_DEVICE_EVENT)
+int BPF_PROG(xppen_deco01v3_device_event, struct hid_bpf_ctx *hctx)
+{
+ static const __u8 pad_buttons[] = { 0x05, 0x08, 0x00, 0x2c, 0x16, 0x1d, 0x57, 0x56 };
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, PAD_REPORT_LENGTH /* size */);
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ if (data[0] == PAD_REPORT_ID) {
+ __u8 button_mask = 0;
+ size_t d, b;
+
+ /* data[1] stores the status of BTN_2 in the 3rd bit*/
+ if (data[1] & BIT(2))
+ button_mask |= BIT(2);
+
+ /* The rest of the descriptor stores the buttons as in pad_buttons */
+ for (d = 2; d < 8; d++) {
+ for (b = 0; b < sizeof(pad_buttons); b++) {
+ if (data[d] != 0 && data[d] == pad_buttons[b])
+ button_mask |= BIT(b);
+ }
+ }
+
+ __u8 report[8] = {PAD_REPORT_ID, button_mask, 0x00};
+
+ __builtin_memcpy(data, report, sizeof(report));
+ }
+ return 0;
+}
+
+HID_BPF_OPS(xppen_deco01v3) = {
+ .hid_rdesc_fixup = (void *)xppen_deco01v3_rdesc_fixup,
+ .hid_device_event = (void *)xppen_deco01v3_device_event,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+ switch (ctx->rdesc_size) {
+ case PAD_REPORT_DESCRIPTOR_LENGTH:
+ case PEN_REPORT_DESCRIPTOR_LENGTH:
+ ctx->retval = 0;
+ break;
+ default:
+ ctx->retval = -EINVAL;
+ }
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/XPPen__Deco02.bpf.c b/drivers/hid/bpf/progs/XPPen__Deco02.bpf.c
new file mode 100644
index 000000000000..4b2549031e56
--- /dev/null
+++ b/drivers/hid/bpf/progs/XPPen__Deco02.bpf.c
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "vmlinux.h"
+#include "hid_bpf.h"
+#include "hid_bpf_helpers.h"
+#include "hid_report_helpers.h"
+#include <bpf/bpf_tracing.h>
+
+#define VID_UGEE 0x28BD
+#define PID_DECO_02 0x0803
+
+HID_BPF_CONFIG(
+ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_UGEE, PID_DECO_02),
+);
+
+/*
+ * Devices are:
+ * - Pad input, including pen (This is the only one we are interested in)
+ * - Pen input as mouse
+ * - Vendor
+ *
+ * Descriptors on main device are:
+ * - 7: Pen
+ * - 6: Vendor settings? Unclear
+ * - 3: Keyboard (This is what we want to modify)
+ * - 5: Feature report
+ *
+ * This creates three event nodes:
+ * - XP-PEN DECO 02 Stylus
+ * - XP-PEN DECO 02
+ * - XP-PEN DECO 02 Keyboard (Again, what we want to modify)
+ *
+ * # Report descriptor length: 188 bytes
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 0
+ * # 0x09, 0x02, // Usage (Pen) 2
+ * # 0xa1, 0x01, // Collection (Application) 4
+ * # 0x85, 0x07, // Report ID (7) 6
+ * # 0x09, 0x20, // Usage (Stylus) 8
+ * # 0xa1, 0x00, // Collection (Physical) 10
+ * # 0x09, 0x42, // Usage (Tip Switch) 12
+ * # 0x09, 0x44, // Usage (Barrel Switch) 14
+ * # 0x09, 0x45, // Usage (Eraser) 16
+ * # 0x09, 0x3c, // Usage (Invert) 18
+ * # 0x09, 0x32, // Usage (In Range) 20
+ * # 0x15, 0x00, // Logical Minimum (0) 22
+ * # 0x25, 0x01, // Logical Maximum (1) 24
+ * # 0x75, 0x01, // Report Size (1) 26
+ * # 0x95, 0x05, // Report Count (5) 28
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 30
+ * # 0x95, 0x03, // Report Count (3) 32
+ * # 0x81, 0x03, // Input (Cnst,Var,Abs) 34
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 36
+ * # 0x09, 0x30, // Usage (X) 38
+ * # 0x15, 0x00, // Logical Minimum (0) 40
+ * # 0x26, 0x50, 0x57, // Logical Maximum (22352) 42
+ * # 0x55, 0x0d, // Unit Exponent (-3) 45
+ * # 0x65, 0x13, // Unit (EnglishLinear: in) 47
+ * # 0x35, 0x00, // Physical Minimum (0) 49
+ * # 0x46, 0x50, 0x57, // Physical Maximum (22352) 51
+ * # 0x75, 0x10, // Report Size (16) 54
+ * # 0x95, 0x01, // Report Count (1) 56
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 58
+ * # 0x09, 0x31, // Usage (Y) 60
+ * # 0x15, 0x00, // Logical Minimum (0) 62
+ * # 0x26, 0x92, 0x36, // Logical Maximum (13970) 64
+ * # 0x55, 0x0d, // Unit Exponent (-3) 67
+ * # 0x65, 0x13, // Unit (EnglishLinear: in) 69
+ * # 0x35, 0x00, // Physical Minimum (0) 71
+ * # 0x46, 0x92, 0x36, // Physical Maximum (13970) 73
+ * # 0x75, 0x10, // Report Size (16) 76
+ * # 0x95, 0x01, // Report Count (1) 78
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 80
+ * # 0x05, 0x0d, // Usage Page (Digitizers) 82
+ * # 0x09, 0x30, // Usage (Tip Pressure) 84
+ * # 0x15, 0x00, // Logical Minimum (0) 86
+ * # 0x26, 0xff, 0x1f, // Logical Maximum (8191) 88
+ * # 0x75, 0x10, // Report Size (16) 91
+ * # 0x95, 0x01, // Report Count (1) 93
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 95
+ * # 0xc0, // End Collection 97
+ * # 0xc0, // End Collection 98
+ * # 0x09, 0x0e, // Usage (Device Configuration) 99
+ * # 0xa1, 0x01, // Collection (Application) 101
+ * # 0x85, 0x05, // Report ID (5) 103
+ * # 0x09, 0x23, // Usage (Device Settings) 105
+ * # 0xa1, 0x02, // Collection (Logical) 107
+ * # 0x09, 0x52, // Usage (Inputmode) 109
+ * # 0x09, 0x53, // Usage (Device Index) 111
+ * # 0x25, 0x0a, // Logical Maximum (10) 113
+ * # 0x75, 0x08, // Report Size (8) 115
+ * # 0x95, 0x02, // Report Count (2) 117
+ * # 0xb1, 0x02, // Feature (Data,Var,Abs) 119
+ * # 0xc0, // End Collection 121
+ * # 0xc0, // End Collection 122
+ * # 0x05, 0x0c, // Usage Page (Consumer Devices) 123
+ * # 0x09, 0x36, // Usage (Function Buttons) 125
+ * # 0xa1, 0x00, // Collection (Physical) 127
+ * # 0x85, 0x06, // Report ID (6) 129
+ * # 0x05, 0x09, // Usage Page (Button) 131
+ * # 0x19, 0x01, // Usage Minimum (1) 133
+ * # 0x29, 0x20, // Usage Maximum (32) 135
+ * # 0x15, 0x00, // Logical Minimum (0) 137
+ * # 0x25, 0x01, // Logical Maximum (1) 139
+ * # 0x95, 0x20, // Report Count (32) 141
+ * # 0x75, 0x01, // Report Size (1) 143
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 145
+ * # 0xc0, // End Collection 147
+ * # 0x05, 0x01, // Usage Page (Generic Desktop) 148
+ * # 0x09, 0x06, // Usage (Keyboard) 150
+ * # 0xa1, 0x01, // Collection (Application) 152
+ * # 0x85, 0x03, // Report ID (3) 154
+ * # 0x05, 0x07, // Usage Page (Keyboard) 156
+ * # 0x19, 0xe0, // Usage Minimum (224) 158
+ * # 0x29, 0xe7, // Usage Maximum (231) 160
+ * # 0x15, 0x00, // Logical Minimum (0) 162
+ * # 0x25, 0x01, // Logical Maximum (1) 164
+ * # 0x75, 0x01, // Report Size (1) 166
+ * # 0x95, 0x08, // Report Count (8) 168
+ * # 0x81, 0x02, // Input (Data,Var,Abs) 170
+ * # 0x05, 0x07, // Usage Page (Keyboard) 172
+ * # 0x19, 0x00, // Usage Minimum (0) 174
+ * # 0x29, 0xff, // Usage Maximum (255) 176
+ * # 0x26, 0xff, 0x00, // Logical Maximum (255) 178
+ * # 0x75, 0x08, // Report Size (8) 181
+ * # 0x95, 0x06, // Report Count (6) 183
+ * # 0x81, 0x00, // Input (Data,Arr,Abs) 185
+ * # 0xc0, // End Collection 187
+ *
+ * Key events; top to bottom:
+ * Buttons released: 03 00 00 00 00 00 00 00
+ * Button1: 03 00 05 00 00 00 00 00 -> 'b and B'
+ * Button2: 03 00 2c 00 00 00 00 00 -> 'Spacebar'
+ * Button3: 03 00 08 00 00 00 00 00 -> 'e and E'
+ * Button4: 03 00 0c 00 00 00 00 00 -> 'i and I'
+ * Button5: 03 05 1d 00 00 00 00 00 -> LeftControl + LeftAlt + 'z and Z'
+ * Button6: 03 01 16 00 00 00 00 00 -> LeftControl + 's and S'
+ *
+ * Dial Events:
+ * Clockwise: 03 01 2e 00 00 00 00 00 -> LeftControl + '= and +'
+ * Anticlockwise: 03 01 2d 00 00 00 00 00 -> LeftControl + '- and (underscore)'
+ *
+ * NOTE: Input event descriptions begin at byte 2, and progressively build
+ * towards byte 7 as each new key is pressed maintaining the press order.
+ * For example:
+ * BTN1 followed by BTN2 is 03 00 05 2c 00 00 00 00
+ * BTN2 followed by BTN1 is 03 00 2c 05 00 00 00 00
+ *
+ * Releasing a button causes its byte to be freed, and the next item in the list
+ * is pushed forwards. Dial events are released immediately after an event is
+ * registered (i.e. after each "click"), so will continually appear pushed
+ * backwards in the report.
+ *
+ * When a button with a modifier key is pressed, the button identifier stacks in
+ * an abnormal way, where the highest modifier byte always supersedes others.
+ * In these cases, the button with the higher modifier is always last.
+ * For example:
+ * BTN6 followed by BTN5 is 03 05 1d 16 00 00 00 00
+ * BTN5 followed by BTN6 is 03 05 1d 16 00 00 00 00
+ * BTN5 followed by BTN1 is 03 05 05 1d 00 00 00 00
+ *
+ * For three button presses in order, demonstrating strictly above rules:
+ * BTN6, BTN1, BTN5 is 03 05 05 1d 16 00 00 00
+ * BTN5, BTN1, BTN6 is 03 05 05 1d 16 00 00 00
+ *
+ * In short, when BTN5/6 are pressed, the order of operations is lost, as they
+ * will always float to the end when pressed in combination with others.
+ *
+ * Fortunately, all states are recorded in the same way, with no overlaps.
+ * Byte 1 can be used as a spare for the wheel, since this is for mod keys.
+ */
+
+#define RDESC_SIZE_PAD 188
+#define REPORT_SIZE_PAD 8
+#define REPORT_ID_BUTTONS 3
+#define PAD_BUTTON_COUNT 6
+#define RDESC_KEYBOARD_OFFSET 148
+
+static const __u8 fixed_rdesc_pad[] = {
+ /* Copy of pen descriptor to avoid losing functionality */
+ UsagePage_Digitizers
+ Usage_Dig_Pen
+ CollectionApplication(
+ ReportId(7)
+ Usage_Dig_Stylus
+ CollectionPhysical(
+ Usage_Dig_TipSwitch
+ Usage_Dig_BarrelSwitch
+ Usage_Dig_Eraser
+ Usage_Dig_Invert
+ Usage_Dig_InRange
+ LogicalMinimum_i8(0)
+ LogicalMaximum_i8(1)
+ ReportSize(1)
+ ReportCount(5)
+ Input(Var|Abs)
+ ReportCount(3)
+ Input(Const) /* Input (Const, Var, Abs) */
+ UsagePage_GenericDesktop
+ Usage_GD_X
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(22352)
+ UnitExponent(-3)
+ Unit(in) /* (EnglishLinear: in) */
+ PhysicalMinimum_i16(0)
+ PhysicalMaximum_i16(22352)
+ ReportSize(16)
+ ReportCount(1)
+ Input(Var|Abs)
+ Usage_GD_Y
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(13970)
+ UnitExponent(-3)
+ Unit(in) /* (EnglishLinear: in) */
+ PhysicalMinimum_i16(0)
+ PhysicalMaximum_i16(13970)
+ ReportSize(16)
+ ReportCount(1)
+ Input(Var|Abs)
+ UsagePage_Digitizers
+ Usage_Dig_TipPressure
+ LogicalMinimum_i16(0)
+ LogicalMaximum_i16(8191)
+ ReportSize(16)
+ ReportCount(1)
+ Input(Var|Abs)
+ )
+ )
+
+ /* FIXES BEGIN */
+ UsagePage_GenericDesktop
+ Usage_GD_Keypad
+ CollectionApplication(
+ ReportId(REPORT_ID_BUTTONS) /* Retain original ID on byte 0 */
+ ReportCount(1)
+ ReportSize(REPORT_SIZE_PAD)
+ UsagePage_Digitizers
+ Usage_Dig_TabletFunctionKeys
+ CollectionPhysical(
+ /* Byte 1: Dial state */
+ UsagePage_GenericDesktop
+ Usage_GD_Dial
+ LogicalMinimum_i8(-1)
+ LogicalMaximum_i8(1)
+ ReportCount(1)
+ ReportSize(REPORT_SIZE_PAD)
+ Input(Var|Rel)
+ /* Byte 2: Button state */
+ UsagePage_Button
+ ReportSize(1)
+ ReportCount(PAD_BUTTON_COUNT)
+ UsageMinimum_i8(0x01)
+ UsageMaximum_i8(PAD_BUTTON_COUNT) /* Number of buttons */
+ LogicalMinimum_i8(0x0)
+ LogicalMaximum_i8(0x1)
+ Input(Var|Abs)
+ /* Byte 3: Exists to be tablet pad */
+ UsagePage_Digitizers
+ Usage_Dig_BarrelSwitch
+ ReportCount(1)
+ ReportSize(1)
+ Input(Var|Abs)
+ ReportCount(7) /* Padding, to fill full report space */
+ Input(Const)
+ /* Byte 4/5: Exists to be a tablet pad */
+ UsagePage_GenericDesktop
+ Usage_GD_X
+ Usage_GD_Y
+ ReportCount(2)
+ ReportSize(8)
+ Input(Var|Abs)
+ /* Bytes 6/7: Padding, to match original length */
+ ReportCount(2)
+ ReportSize(8)
+ Input(Const)
+ )
+ FixedSizeVendorReport(RDESC_SIZE_PAD)
+ )
+};
+
+SEC(HID_BPF_RDESC_FIXUP)
+int BPF_PROG(xppen_deco02_rdesc_fixup, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0, HID_MAX_DESCRIPTOR_SIZE);
+
+ if (!data)
+ return 0; /* EPERM Check */
+
+ if (hctx->size == RDESC_SIZE_PAD) {
+ __builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
+ return sizeof(fixed_rdesc_pad);
+ }
+
+ return 0;
+}
+
+SEC(HID_BPF_DEVICE_EVENT)
+int BPF_PROG(xppen_deco02_device_event, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0, REPORT_SIZE_PAD);
+
+ if (!data || data[0] != REPORT_ID_BUTTONS)
+ return 0; /* EPERM or wrong report */
+
+ __u8 dial_code = 0;
+ __u8 button_mask = 0;
+ size_t d;
+
+ /* Start from 2; 0 is report ID, 1 is modifier keys, replaced by dial */
+ for (d = 2; d < 8; d++) {
+ switch (data[d]) {
+ case 0x2e:
+ dial_code = 1;
+ break;
+ case 0x2d:
+ dial_code = -1;
+ break;
+ /* below are buttons, top to bottom */
+ case 0x05:
+ button_mask |= BIT(0);
+ break;
+ case 0x2c:
+ button_mask |= BIT(1);
+ break;
+ case 0x08:
+ button_mask |= BIT(2);
+ break;
+ case 0x0c:
+ button_mask |= BIT(3);
+ break;
+ case 0x1d:
+ button_mask |= BIT(4);
+ break;
+ case 0x16:
+ button_mask |= BIT(05);
+ break;
+ default:
+ break;
+ }
+ }
+
+ __u8 report[8] = { REPORT_ID_BUTTONS, dial_code, button_mask, 0x00 };
+
+ __builtin_memcpy(data, report, sizeof(report));
+ return 0;
+}
+
+HID_BPF_OPS(xppen_deco02) = {
+ .hid_rdesc_fixup = (void *)xppen_deco02_rdesc_fixup,
+ .hid_device_event = (void *)xppen_deco02_device_event,
+};
+
+SEC("syscall")
+int probe(struct hid_bpf_probe_args *ctx)
+{
+ ctx->retval = ctx->rdesc_size != RDESC_SIZE_PAD ? -EINVAL : 0;
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/drivers/hid/bpf/progs/hid_report_helpers.h b/drivers/hid/bpf/progs/hid_report_helpers.h
index 9b2a48e4a311..9944ff54d31d 100644
--- a/drivers/hid/bpf/progs/hid_report_helpers.h
+++ b/drivers/hid/bpf/progs/hid_report_helpers.h
@@ -143,8 +143,11 @@
* report with Report ID 0xac of the given size in bytes.
* The size is inclusive of the 1 byte Report ID prefix.
*
- * HID-BPF requires that at least one report has
- * the same size as the original report from the device.
+ * The kernel discards any HID reports that are larger
+ * than the largest report in a HID report descriptor.
+ * Thus at least one report must have (at least)
+ * the same size as the largest original report from
+ * the device.
* The easy way to ensure that is to add this
* macro as the last element of your CollectionApplication
* other reports can be of any size less than this.
@@ -295,6 +298,7 @@
#define Usage_GD_SystemSpeakerMute Usage_i8(0xa7)
#define Usage_GD_SystemHibernate Usage_i8(0xa8)
#define Usage_GD_SystemMicrophoneMute Usage_i8(0xa9)
+#define Usage_GD_SystemAccessibilityBinding Usage_i8(0xaa)
#define Usage_GD_SystemDisplayInvert Usage_i8(0xb0)
#define Usage_GD_SystemDisplayInternal Usage_i8(0xb1)
#define Usage_GD_SystemDisplayExternal Usage_i8(0xb2)
@@ -2669,7 +2673,7 @@
#define Usage_BS_iDeviceName Usage_i8(0x88)
#define Usage_BS_iDeviceChemistry Usage_i8(0x89)
#define Usage_BS_ManufacturerData Usage_i8(0x8a)
-#define Usage_BS_Rechargable Usage_i8(0x8b)
+#define Usage_BS_Rechargeable Usage_i8(0x8b)
#define Usage_BS_WarningCapacityLimit Usage_i8(0x8c)
#define Usage_BS_CapacityGranularity1 Usage_i8(0x8d)
#define Usage_BS_CapacityGranularity2 Usage_i8(0x8e)
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 61404d7a43ee..57da4f86a9fa 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -355,6 +355,7 @@ static const struct apple_key_translation swapped_fn_leftctrl_keys[] = {
static const struct apple_non_apple_keyboard non_apple_keyboards[] = {
{ "SONiX USB DEVICE" },
+ { "SONiX AK870 PRO" },
{ "Keychron" },
{ "AONE" },
{ "GANSS" },
diff --git a/drivers/hid/hid-corsair-void.c b/drivers/hid/hid-corsair-void.c
index fee134a7eba3..5e9a5b8f7f16 100644
--- a/drivers/hid/hid-corsair-void.c
+++ b/drivers/hid/hid-corsair-void.c
@@ -553,9 +553,8 @@ static void corsair_void_add_battery(struct corsair_void_drvdata *drvdata)
if (IS_ERR(new_supply)) {
hid_err(drvdata->hid_dev,
- "failed to register battery '%s' (reason: %ld)\n",
- drvdata->battery_desc.name,
- PTR_ERR(new_supply));
+ "failed to register battery '%s' (reason: %pe)\n",
+ drvdata->battery_desc.name, new_supply);
return;
}
diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c
index 69771fd35006..981d1b6e9658 100644
--- a/drivers/hid/hid-elecom.c
+++ b/drivers/hid/hid-elecom.c
@@ -75,7 +75,8 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*/
mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8);
break;
- case USB_DEVICE_ID_ELECOM_M_XT3URBK:
+ case USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB:
+ case USB_DEVICE_ID_ELECOM_M_XT3URBK_018F:
case USB_DEVICE_ID_ELECOM_M_XT3DRBK:
case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
/*
@@ -119,7 +120,8 @@ static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
static const struct hid_device_id elecom_devices[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
diff --git a/drivers/hid/hid-evision.c b/drivers/hid/hid-evision.c
index bb5997078491..3e7f43ab80bb 100644
--- a/drivers/hid/hid-evision.c
+++ b/drivers/hid/hid-evision.c
@@ -18,6 +18,10 @@ static int evision_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
+ /* mapping only applies to USB_DEVICE_ID_EVISION_ICL01 */
+ if (hdev->product != USB_DEVICE_ID_EVISION_ICL01)
+ return 0;
+
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
return 0;
@@ -37,8 +41,24 @@ static int evision_input_mapping(struct hid_device *hdev, struct hid_input *hi,
return 0;
}
+#define REP_DSC_SIZE 236
+#define USAGE_MAX_INDEX 59
+
+static const __u8 *evision_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ if (hdev->product == USB_DEVICE_ID_EV_TELINK_RECEIVER &&
+ *rsize == REP_DSC_SIZE && rdesc[USAGE_MAX_INDEX] == 0x29 &&
+ rdesc[USAGE_MAX_INDEX + 1] == 3) {
+ hid_info(hdev, "fixing EVision:TeLink Receiver report descriptor\n");
+ rdesc[USAGE_MAX_INDEX + 1] = 5; // change usage max from 3 to 5
+ }
+ return rdesc;
+}
+
static const struct hid_device_id evision_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_EVISION, USB_DEVICE_ID_EVISION_ICL01) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_EVISION, USB_DEVICE_ID_EV_TELINK_RECEIVER) },
{ }
};
MODULE_DEVICE_TABLE(hid, evision_devices);
@@ -47,6 +67,7 @@ static struct hid_driver evision_driver = {
.name = "evision",
.id_table = evision_devices,
.input_mapping = evision_input_mapping,
+ .report_fixup = evision_report_fixup,
};
module_hid_driver(evision_driver);
diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c
index 9e04c6d0fcc8..c2de916747de 100644
--- a/drivers/hid/hid-generic.c
+++ b/drivers/hid/hid-generic.c
@@ -70,6 +70,14 @@ static int hid_generic_probe(struct hid_device *hdev,
return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
}
+static int hid_generic_reset_resume(struct hid_device *hdev)
+{
+ if (hdev->claimed & HID_CLAIMED_INPUT)
+ hidinput_reset_resume(hdev);
+
+ return 0;
+}
+
static const struct hid_device_id hid_table[] = {
{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, HID_ANY_ID, HID_ANY_ID) },
{ }
@@ -81,6 +89,7 @@ static struct hid_driver hid_generic = {
.id_table = hid_table,
.match = hid_generic_match,
.probe = hid_generic_probe,
+ .reset_resume = hid_generic_reset_resume,
};
module_hid_driver(hid_generic);
diff --git a/drivers/hid/hid-haptic.c b/drivers/hid/hid-haptic.c
index aa090684c1f2..fc8a9997f815 100644
--- a/drivers/hid/hid-haptic.c
+++ b/drivers/hid/hid-haptic.c
@@ -86,7 +86,7 @@ int hid_haptic_input_configured(struct hid_device *hdev,
if (hi->application == HID_DG_TOUCHPAD) {
if (haptic->auto_trigger_report &&
haptic->manual_trigger_report) {
- __set_bit(INPUT_PROP_HAPTIC_TOUCHPAD, hi->input->propbit);
+ __set_bit(INPUT_PROP_PRESSUREPAD, hi->input->propbit);
return 1;
}
return 0;
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 0723b4b1c9ec..d31711f1aaec 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -449,7 +449,8 @@
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
#define USB_DEVICE_ID_ELECOM_M_XGL20DLBK 0x00e6
-#define USB_DEVICE_ID_ELECOM_M_XT3URBK 0x00fb
+#define USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB 0x00fb
+#define USB_DEVICE_ID_ELECOM_M_XT3URBK_018F 0x018f
#define USB_DEVICE_ID_ELECOM_M_XT3DRBK 0x00fc
#define USB_DEVICE_ID_ELECOM_M_XT4DRBK 0x00fd
#define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe
@@ -476,6 +477,7 @@
#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
#define USB_VENDOR_ID_EVISION 0x320f
+#define USB_DEVICE_ID_EV_TELINK_RECEIVER 0x226f
#define USB_DEVICE_ID_EVISION_ICL01 0x5041
#define USB_VENDOR_ID_FFBEAST 0x045b
@@ -718,6 +720,7 @@
#define USB_DEVICE_ID_ITE_LENOVO_YOGA2 0x8350
#define I2C_DEVICE_ID_ITE_LENOVO_LEGION_Y720 0x837a
#define USB_DEVICE_ID_ITE_LENOVO_YOGA900 0x8396
+#define I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD 0x8987
#define USB_DEVICE_ID_ITE8595 0x8595
#define USB_DEVICE_ID_ITE_MEDION_E1239T 0xce50
@@ -879,6 +882,7 @@
#define USB_DEVICE_ID_LOGITECH_DUAL_ACTION 0xc216
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2 0xc218
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2 0xc219
+#define USB_DEVICE_ID_LOGITECH_G13 0xc21c
#define USB_DEVICE_ID_LOGITECH_G15_LCD 0xc222
#define USB_DEVICE_ID_LOGITECH_G11 0xc225
#define USB_DEVICE_ID_LOGITECH_G15_V2_LCD 0xc227
@@ -914,6 +918,8 @@
#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1 0xc539
#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1 0xc53f
#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2 0xc543
+#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3 0xc547
+#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_4 0xc54d
#define USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY 0xc53a
#define USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER 0xc548
#define USB_DEVICE_ID_SPACETRAVELLER 0xc623
@@ -1419,6 +1425,7 @@
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW 0x0933
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO 0x091b
+#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_24_PRO 0x092d
#define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074
#define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071
#define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720 0x0055
@@ -1543,7 +1550,7 @@
#define USB_VENDOR_ID_SIGNOTEC 0x2133
#define USB_DEVICE_ID_SIGNOTEC_VIEWSONIC_PD1011 0x0018
-#define USB_VENDOR_ID_SMARTLINKTECHNOLOGY 0x4c4a
-#define USB_DEVICE_ID_SMARTLINKTECHNOLOGY_4155 0x4155
+#define USB_VENDOR_ID_JIELI_SDK_DEFAULT 0x4c4a
+#define USB_DEVICE_ID_JIELI_SDK_4155 0x4155
#endif
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index e56e7de53279..2633fcd8f910 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -399,10 +399,11 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_CHROMEBOOK_TROGDOR_POMPOM),
HID_BATTERY_QUIRK_AVOID_QUERY },
/*
- * Elan I2C-HID touchscreens seem to all report a non present battery,
- * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C-HID devices.
+ * Elan HID touchscreens seem to all report a non present battery,
+ * set HID_BATTERY_QUIRK_IGNORE for all Elan I2C and USB HID devices.
*/
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELAN, HID_ANY_ID), HID_BATTERY_QUIRK_IGNORE },
{}
};
@@ -877,7 +878,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
switch (usage->hid) {
/* These usage IDs map directly to the usage codes. */
- case HID_GD_X: case HID_GD_Y: case HID_GD_Z:
+ case HID_GD_X: case HID_GD_Y:
case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ:
if (field->flags & HID_MAIN_ITEM_RELATIVE)
map_rel(usage->hid & 0xf);
@@ -885,6 +886,22 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
map_abs_clear(usage->hid & 0xf);
break;
+ case HID_GD_Z:
+ /* HID_GD_Z is mapped to ABS_DISTANCE for stylus/pen */
+ if (field->flags & HID_MAIN_ITEM_RELATIVE) {
+ map_rel(usage->hid & 0xf);
+ } else {
+ if (field->application == HID_DG_PEN ||
+ field->physical == HID_DG_PEN ||
+ field->logical == HID_DG_STYLUS ||
+ field->physical == HID_DG_STYLUS ||
+ field->application == HID_DG_DIGITIZER)
+ map_abs_clear(ABS_DISTANCE);
+ else
+ map_abs_clear(usage->hid & 0xf);
+ }
+ break;
+
case HID_GD_WHEEL:
if (field->flags & HID_MAIN_ITEM_RELATIVE) {
set_bit(REL_WHEEL, input->relbit);
@@ -2399,6 +2416,13 @@ void hidinput_disconnect(struct hid_device *hid)
}
EXPORT_SYMBOL_GPL(hidinput_disconnect);
+void hidinput_reset_resume(struct hid_device *hid)
+{
+ /* renegotiate host-device shared state after reset */
+ hidinput_change_resolution_multipliers(hid);
+}
+EXPORT_SYMBOL_GPL(hidinput_reset_resume);
+
#ifdef CONFIG_HID_KUNIT_TEST
#include "hid-input-test.c"
#endif
diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c
index 654879814f97..9cc3e029e9f6 100644
--- a/drivers/hid/hid-lenovo.c
+++ b/drivers/hid/hid-lenovo.c
@@ -148,6 +148,14 @@ static const __u8 lenovo_tpIIbtkbd_need_fixup_collection[] = {
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
};
+static const __u8 lenovo_yoga7x_kbd_need_fixup_collection[] = {
+ 0x15, 0x00, // Logical Minimum (0)
+ 0x25, 0x65, // Logical Maximum (101)
+ 0x05, 0x07, // Usage Page (Keyboard)
+ 0x19, 0x00, // Usage Minimum (0)
+ 0x29, 0xDD, // Usage Maximum (221)
+};
+
static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -177,6 +185,13 @@ static const __u8 *lenovo_report_fixup(struct hid_device *hdev, __u8 *rdesc,
rdesc[260] = 0x01; /* report count (2) = 0x01 */
}
break;
+ case I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD:
+ if (*rsize == 176 &&
+ memcmp(&rdesc[52], lenovo_yoga7x_kbd_need_fixup_collection,
+ sizeof(lenovo_yoga7x_kbd_need_fixup_collection)) == 0) {
+ rdesc[55] = rdesc[61]; // logical maximum = usage maximum
+ }
+ break;
}
return rdesc;
}
@@ -1538,6 +1553,8 @@ static const struct hid_device_id lenovo_devices[] = {
USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) },
{ HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB2) },
+ { HID_DEVICE(BUS_I2C, HID_GROUP_GENERIC,
+ USB_VENDOR_ID_ITE, I2C_DEVICE_ID_ITE_LENOVO_YOGA_SLIM_7X_KEYBOARD) },
{ }
};
diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c
index f8605656257b..1a88bc44ada4 100644
--- a/drivers/hid/hid-lg-g15.c
+++ b/drivers/hid/hid-lg-g15.c
@@ -26,7 +26,24 @@
#define LG_G510_FEATURE_BACKLIGHT_RGB 0x05
#define LG_G510_FEATURE_POWER_ON_RGB 0x06
+#define LG_G510_INPUT_MACRO_KEYS 0x03
+#define LG_G510_INPUT_KBD_BACKLIGHT 0x04
+
+#define LG_G13_INPUT_REPORT 0x01
+#define LG_G13_FEATURE_M_KEYS_LEDS 0x05
+#define LG_G13_FEATURE_BACKLIGHT_RGB 0x07
+#define LG_G13_BACKLIGHT_HW_ON_BIT 23
+
+/**
+ * g13_input_report.keybits[] is not 32-bit aligned, so we can't use the bitops macros.
+ *
+ * @ary: Pointer to array of u8s
+ * @b: Bit index into ary, LSB first. Not range checked.
+ */
+#define TEST_BIT(ary, b) ((1 << ((b) & 7)) & (ary)[(b) >> 3])
+
enum lg_g15_model {
+ LG_G13,
LG_G15,
LG_G15_V2,
LG_G510,
@@ -45,6 +62,12 @@ enum lg_g15_led_type {
LG_G15_LED_MAX
};
+struct g13_input_report {
+ u8 report_id; /* Report ID is always set to 1. */
+ u8 joy_x, joy_y;
+ u8 keybits[5];
+};
+
struct lg_g15_led {
union {
struct led_classdev cdev;
@@ -63,12 +86,188 @@ struct lg_g15_data {
struct mutex mutex;
struct work_struct work;
struct input_dev *input;
+ struct input_dev *input_js; /* Separate joystick device for G13. */
struct hid_device *hdev;
enum lg_g15_model model;
struct lg_g15_led leds[LG_G15_LED_MAX];
bool game_mode_enabled;
+ bool backlight_disabled; /* true == HW backlight toggled *OFF* */
};
+/********* G13 LED functions ***********/
+/*
+ * G13 retains no state across power cycles, and always powers up with the backlight on,
+ * color #5AFF6E, all macro key LEDs off.
+ */
+static int lg_g13_get_leds_state(struct lg_g15_data *g15)
+{
+ u8 * const tbuf = g15->transfer_buf;
+ int ret, high;
+
+ /* RGB backlight. */
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_BACKLIGHT_RGB,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret != 5) {
+ hid_err(g15->hdev, "Error getting backlight brightness: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+
+ /* Normalize RGB intensities against the highest component. */
+ high = max3(tbuf[1], tbuf[2], tbuf[3]);
+ if (high) {
+ g15->leds[LG_G15_KBD_BRIGHTNESS].red =
+ DIV_ROUND_CLOSEST(tbuf[1] * 255, high);
+ g15->leds[LG_G15_KBD_BRIGHTNESS].green =
+ DIV_ROUND_CLOSEST(tbuf[2] * 255, high);
+ g15->leds[LG_G15_KBD_BRIGHTNESS].blue =
+ DIV_ROUND_CLOSEST(tbuf[3] * 255, high);
+ g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = high;
+ } else {
+ g15->leds[LG_G15_KBD_BRIGHTNESS].red = 255;
+ g15->leds[LG_G15_KBD_BRIGHTNESS].green = 255;
+ g15->leds[LG_G15_KBD_BRIGHTNESS].blue = 255;
+ g15->leds[LG_G15_KBD_BRIGHTNESS].brightness = 0;
+ }
+
+ /* Macro LEDs. */
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_M_KEYS_LEDS,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret != 5) {
+ hid_err(g15->hdev, "Error getting macro LED brightness: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+
+ for (int i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; ++i)
+ g15->leds[i].brightness = !!(tbuf[1] & (1 << (i - LG_G15_MACRO_PRESET1)));
+
+ /*
+ * Bit 23 of g13_input_report.keybits[] contains the backlight's
+ * current HW toggle state. Retrieve it from the device.
+ */
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_INPUT_REPORT,
+ tbuf, sizeof(struct g13_input_report),
+ HID_INPUT_REPORT, HID_REQ_GET_REPORT);
+ if (ret != sizeof(struct g13_input_report)) {
+ hid_err(g15->hdev, "Error getting backlight on/off state: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+ g15->backlight_disabled =
+ !TEST_BIT(((struct g13_input_report *) tbuf)->keybits,
+ LG_G13_BACKLIGHT_HW_ON_BIT);
+
+ return 0;
+}
+
+static int lg_g13_kbd_led_write(struct lg_g15_data *g15,
+ struct lg_g15_led *g15_led,
+ enum led_brightness brightness)
+{
+ struct mc_subled const * const subleds = g15_led->mcdev.subled_info;
+ u8 * const tbuf = g15->transfer_buf;
+ int ret;
+
+ guard(mutex)(&g15->mutex);
+
+ led_mc_calc_color_components(&g15_led->mcdev, brightness);
+
+ tbuf[0] = 5;
+ tbuf[1] = subleds[0].brightness;
+ tbuf[2] = subleds[1].brightness;
+ tbuf[3] = subleds[2].brightness;
+ tbuf[4] = 0;
+
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_BACKLIGHT_RGB,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != 5) {
+ hid_err(g15->hdev, "Error setting backlight brightness: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+
+ g15_led->brightness = brightness;
+ return 0;
+}
+
+static int lg_g13_kbd_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct led_classdev_mc *mc = lcdev_to_mccdev(led_cdev);
+ struct lg_g15_led *g15_led =
+ container_of(mc, struct lg_g15_led, mcdev);
+ struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
+
+ /* Ignore LED off on unregister / keyboard unplug */
+ if (led_cdev->flags & LED_UNREGISTERING)
+ return 0;
+
+ return lg_g13_kbd_led_write(g15, g15_led, brightness);
+}
+
+static enum led_brightness lg_g13_kbd_led_get(struct led_classdev *led_cdev)
+{
+ struct led_classdev_mc const * const mc = lcdev_to_mccdev(led_cdev);
+ struct lg_g15_led const *g15_led =
+ container_of(mc, struct lg_g15_led, mcdev);
+
+ return g15_led->brightness;
+}
+
+static int lg_g13_mkey_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
+{
+ struct lg_g15_led *g15_led =
+ container_of(led_cdev, struct lg_g15_led, cdev);
+ struct lg_g15_data *g15 = dev_get_drvdata(led_cdev->dev->parent);
+ int i, ret;
+ u8 * const tbuf = g15->transfer_buf;
+ u8 val, mask = 0;
+
+ /* Ignore LED off on unregister / keyboard unplug */
+ if (led_cdev->flags & LED_UNREGISTERING)
+ return 0;
+
+ guard(mutex)(&g15->mutex);
+
+ for (i = LG_G15_MACRO_PRESET1; i < LG_G15_LED_MAX; ++i) {
+ if (i == g15_led->led)
+ val = brightness;
+ else
+ val = g15->leds[i].brightness;
+
+ if (val)
+ mask |= 1 << (i - LG_G15_MACRO_PRESET1);
+ }
+
+ tbuf[0] = 5;
+ tbuf[1] = mask;
+ tbuf[2] = 0;
+ tbuf[3] = 0;
+ tbuf[4] = 0;
+
+ ret = hid_hw_raw_request(g15->hdev, LG_G13_FEATURE_M_KEYS_LEDS,
+ tbuf, 5,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret != 5) {
+ hid_err(g15->hdev, "Error setting LED brightness: %d\n", ret);
+ return (ret < 0) ? ret : -EIO;
+ }
+
+ g15_led->brightness = brightness;
+ return 0;
+}
+
+static enum led_brightness lg_g13_mkey_led_get(struct led_classdev *led_cdev)
+{
+ /*
+ * G13 doesn't change macro key LEDs behind our back, so they're
+ * whatever we last set them to.
+ */
+ struct lg_g15_led *g15_led =
+ container_of(led_cdev, struct lg_g15_led, cdev);
+
+ return g15_led->brightness;
+}
+
/******** G15 and G15 v2 LED functions ********/
static int lg_g15_update_led_brightness(struct lg_g15_data *g15)
@@ -227,6 +426,20 @@ static int lg_g510_get_initial_led_brightness(struct lg_g15_data *g15, int i)
g15->leds[i].brightness = 0;
}
+ if (i)
+ return 0;
+
+ ret = hid_hw_raw_request(g15->hdev, LG_G510_INPUT_KBD_BACKLIGHT,
+ g15->transfer_buf, 2,
+ HID_INPUT_REPORT, HID_REQ_GET_REPORT);
+ if (ret != 2) {
+ /* This can happen when a KVM switch is used, so only warn. */
+ hid_warn(g15->hdev, "Error getting backlight state: %d\n", ret);
+ return 0;
+ }
+
+ g15->backlight_disabled = g15->transfer_buf[1] & 0x04;
+
return 0;
}
@@ -390,6 +603,8 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)
int ret;
switch (g15->model) {
+ case LG_G13:
+ return lg_g13_get_leds_state(g15);
case LG_G15:
case LG_G15_V2:
return lg_g15_update_led_brightness(g15);
@@ -417,6 +632,108 @@ static int lg_g15_get_initial_led_brightness(struct lg_g15_data *g15)
/******** Input functions ********/
+/* Table mapping keybits[] bit positions to event codes. */
+/* Note: Indices are discontinuous to aid readability. */
+static const u16 g13_keys_for_bits[] = {
+ /* Main keypad - keys G1 - G22 */
+ [0] = KEY_MACRO1,
+ [1] = KEY_MACRO2,
+ [2] = KEY_MACRO3,
+ [3] = KEY_MACRO4,
+ [4] = KEY_MACRO5,
+ [5] = KEY_MACRO6,
+ [6] = KEY_MACRO7,
+ [7] = KEY_MACRO8,
+ [8] = KEY_MACRO9,
+ [9] = KEY_MACRO10,
+ [10] = KEY_MACRO11,
+ [11] = KEY_MACRO12,
+ [12] = KEY_MACRO13,
+ [13] = KEY_MACRO14,
+ [14] = KEY_MACRO15,
+ [15] = KEY_MACRO16,
+ [16] = KEY_MACRO17,
+ [17] = KEY_MACRO18,
+ [18] = KEY_MACRO19,
+ [19] = KEY_MACRO20,
+ [20] = KEY_MACRO21,
+ [21] = KEY_MACRO22,
+
+ /* LCD menu buttons. */
+ [24] = KEY_KBD_LCD_MENU5, /* "Next page" button */
+ [25] = KEY_KBD_LCD_MENU1, /* Left-most */
+ [26] = KEY_KBD_LCD_MENU2,
+ [27] = KEY_KBD_LCD_MENU3,
+ [28] = KEY_KBD_LCD_MENU4, /* Right-most */
+
+ /* Macro preset and record buttons; have red LEDs under them. */
+ [29] = KEY_MACRO_PRESET1,
+ [30] = KEY_MACRO_PRESET2,
+ [31] = KEY_MACRO_PRESET3,
+ [32] = KEY_MACRO_RECORD_START,
+
+ /* 33-35 handled by joystick device. */
+
+ /* Backlight toggle. */
+ [37] = KEY_LIGHTS_TOGGLE,
+};
+
+#define G13_JS_KEYBITS_OFFSET 33
+
+static const u16 g13_keys_for_bits_js[] = {
+ /* Joystick buttons */
+ /* These keybits are at bit indices 33, 34, and 35. */
+ BTN_BASE, /* Left side */
+ BTN_BASE2, /* Bottom side */
+ BTN_THUMB, /* Stick depress */
+};
+
+static int lg_g13_event(struct lg_g15_data *g15, u8 const *data)
+{
+ struct g13_input_report const * const rep = (struct g13_input_report *) data;
+ int i, val;
+ bool backlight_disabled;
+
+ /*
+ * Main macropad and menu keys.
+ * Emit key events defined for each bit position.
+ */
+ for (i = 0; i < ARRAY_SIZE(g13_keys_for_bits); ++i) {
+ if (g13_keys_for_bits[i]) {
+ val = TEST_BIT(rep->keybits, i);
+ input_report_key(g15->input, g13_keys_for_bits[i], val);
+ }
+ }
+ input_sync(g15->input);
+
+ /*
+ * Joystick.
+ * Emit button and deflection events.
+ */
+ for (i = 0; i < ARRAY_SIZE(g13_keys_for_bits_js); ++i) {
+ val = TEST_BIT(rep->keybits, i + G13_JS_KEYBITS_OFFSET);
+ input_report_key(g15->input_js, g13_keys_for_bits_js[i], val);
+ }
+ input_report_abs(g15->input_js, ABS_X, rep->joy_x);
+ input_report_abs(g15->input_js, ABS_Y, rep->joy_y);
+ input_sync(g15->input_js);
+
+ /*
+ * Bit 23 of keybits[] reports the current backlight on/off state. If
+ * it has changed from the last cached value, apply an update.
+ */
+ backlight_disabled = !TEST_BIT(rep->keybits, LG_G13_BACKLIGHT_HW_ON_BIT);
+ if (backlight_disabled ^ g15->backlight_disabled) {
+ led_classdev_notify_brightness_hw_changed(
+ &g15->leds[LG_G15_KBD_BRIGHTNESS].mcdev.led_cdev,
+ backlight_disabled
+ ? 0 : g15->leds[LG_G15_KBD_BRIGHTNESS].brightness);
+ g15->backlight_disabled = backlight_disabled;
+ }
+
+ return 0;
+}
+
/* On the G15 Mark I Logitech has been quite creative with which bit is what */
static void lg_g15_handle_lcd_menu_keys(struct lg_g15_data *g15, u8 *data)
{
@@ -549,14 +866,24 @@ static int lg_g510_event(struct lg_g15_data *g15, u8 *data)
static int lg_g510_leds_event(struct lg_g15_data *g15, u8 *data)
{
+ struct lg_g15_led *g15_led = &g15->leds[LG_G15_KBD_BRIGHTNESS];
bool backlight_disabled;
+ backlight_disabled = data[1] & 0x04;
+ if (backlight_disabled == g15->backlight_disabled)
+ return 0;
+
+ led_classdev_notify_brightness_hw_changed(
+ &g15_led->mcdev.led_cdev,
+ backlight_disabled ? 0 : g15_led->brightness);
+
+ g15->backlight_disabled = backlight_disabled;
+
/*
* The G510 ignores backlight updates when the backlight is turned off
* through the light toggle button on the keyboard, to work around this
* we queue a workitem to sync values when the backlight is turned on.
*/
- backlight_disabled = data[1] & 0x04;
if (!backlight_disabled)
schedule_work(&g15->work);
@@ -572,6 +899,10 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
switch (g15->model) {
+ case LG_G13:
+ if (data[0] == 0x01 && size == sizeof(struct g13_input_report))
+ return lg_g13_event(g15, data);
+ break;
case LG_G15:
if (data[0] == 0x02 && size == 9)
return lg_g15_event(g15, data);
@@ -588,9 +919,9 @@ static int lg_g15_raw_event(struct hid_device *hdev, struct hid_report *report,
break;
case LG_G510:
case LG_G510_USB_AUDIO:
- if (data[0] == 0x03 && size == 5)
+ if (data[0] == LG_G510_INPUT_MACRO_KEYS && size == 5)
return lg_g510_event(g15, data);
- if (data[0] == 0x04 && size == 2)
+ if (data[0] == LG_G510_INPUT_KBD_BACKLIGHT && size == 2)
return lg_g510_leds_event(g15, data);
break;
}
@@ -616,13 +947,24 @@ static void lg_g15_setup_led_rgb(struct lg_g15_data *g15, int index)
{
int i;
struct mc_subled *subled_info;
-
- g15->leds[index].mcdev.led_cdev.brightness_set_blocking =
- lg_g510_kbd_led_set;
- g15->leds[index].mcdev.led_cdev.brightness_get =
- lg_g510_kbd_led_get;
- g15->leds[index].mcdev.led_cdev.max_brightness = 255;
- g15->leds[index].mcdev.num_colors = 3;
+ struct lg_g15_led * const gled = &g15->leds[index];
+
+ if (g15->model == LG_G13) {
+ gled->mcdev.led_cdev.brightness_set_blocking =
+ lg_g13_kbd_led_set;
+ gled->mcdev.led_cdev.brightness_get =
+ lg_g13_kbd_led_get;
+ gled->mcdev.led_cdev.flags = LED_BRIGHT_HW_CHANGED;
+ } else {
+ gled->mcdev.led_cdev.brightness_set_blocking =
+ lg_g510_kbd_led_set;
+ gled->mcdev.led_cdev.brightness_get =
+ lg_g510_kbd_led_get;
+ if (index == LG_G15_KBD_BRIGHTNESS)
+ g15->leds[index].mcdev.led_cdev.flags = LED_BRIGHT_HW_CHANGED;
+ }
+ gled->mcdev.led_cdev.max_brightness = 255;
+ gled->mcdev.num_colors = 3;
subled_info = devm_kcalloc(&g15->hdev->dev, 3, sizeof(*subled_info), GFP_KERNEL);
if (!subled_info)
@@ -632,20 +974,20 @@ static void lg_g15_setup_led_rgb(struct lg_g15_data *g15, int index)
switch (i + 1) {
case LED_COLOR_ID_RED:
subled_info[i].color_index = LED_COLOR_ID_RED;
- subled_info[i].intensity = g15->leds[index].red;
+ subled_info[i].intensity = gled->red;
break;
case LED_COLOR_ID_GREEN:
subled_info[i].color_index = LED_COLOR_ID_GREEN;
- subled_info[i].intensity = g15->leds[index].green;
+ subled_info[i].intensity = gled->green;
break;
case LED_COLOR_ID_BLUE:
subled_info[i].color_index = LED_COLOR_ID_BLUE;
- subled_info[i].intensity = g15->leds[index].blue;
+ subled_info[i].intensity = gled->blue;
break;
}
subled_info[i].channel = i;
}
- g15->leds[index].mcdev.subled_info = subled_info;
+ gled->mcdev.subled_info = subled_info;
}
static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
@@ -656,6 +998,23 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
g15->leds[i].cdev.name = name;
switch (g15->model) {
+ case LG_G13:
+ if (i < LG_G15_BRIGHTNESS_MAX) {
+ /* RGB backlight. */
+ lg_g15_setup_led_rgb(g15, i);
+ ret = devm_led_classdev_multicolor_register_ext(&g15->hdev->dev,
+ &g15->leds[i].mcdev,
+ NULL);
+ } else {
+ /* Macro keys */
+ g15->leds[i].cdev.brightness_set_blocking = lg_g13_mkey_led_set;
+ g15->leds[i].cdev.brightness_get = lg_g13_mkey_led_get;
+ g15->leds[i].cdev.max_brightness = 1;
+
+ ret = devm_led_classdev_register(&g15->hdev->dev,
+ &g15->leds[i].cdev);
+ }
+ break;
case LG_G15:
case LG_G15_V2:
g15->leds[i].cdev.brightness_get = lg_g15_led_get;
@@ -702,11 +1061,9 @@ static int lg_g15_register_led(struct lg_g15_data *g15, int i, const char *name)
}
/* Common input device init code shared between keyboards and Z-10 speaker handling */
-static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input,
- const char *name)
+static void lg_g15_init_input_dev_core(struct hid_device *hdev, struct input_dev *input,
+ char const *name)
{
- int i;
-
input->name = name;
input->phys = hdev->phys;
input->uniq = hdev->uniq;
@@ -717,12 +1074,42 @@ static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *inp
input->dev.parent = &hdev->dev;
input->open = lg_g15_input_open;
input->close = lg_g15_input_close;
+}
+
+static void lg_g15_init_input_dev(struct hid_device *hdev, struct input_dev *input,
+ const char *name)
+{
+ int i;
+
+ lg_g15_init_input_dev_core(hdev, input, name);
/* Keys below the LCD, intended for controlling a menu on the LCD */
for (i = 0; i < 5; i++)
input_set_capability(input, EV_KEY, KEY_KBD_LCD_MENU1 + i);
}
+static void lg_g13_init_input_dev(struct hid_device *hdev,
+ struct input_dev *input, const char *name,
+ struct input_dev *input_js, const char *name_js)
+{
+ /* Macropad. */
+ lg_g15_init_input_dev_core(hdev, input, name);
+ for (int i = 0; i < ARRAY_SIZE(g13_keys_for_bits); ++i) {
+ if (g13_keys_for_bits[i])
+ input_set_capability(input, EV_KEY, g13_keys_for_bits[i]);
+ }
+
+ /* OBTW, we're a joystick, too... */
+ lg_g15_init_input_dev_core(hdev, input_js, name_js);
+ for (int i = 0; i < ARRAY_SIZE(g13_keys_for_bits_js); ++i)
+ input_set_capability(input_js, EV_KEY, g13_keys_for_bits_js[i]);
+
+ input_set_capability(input_js, EV_ABS, ABS_X);
+ input_set_abs_params(input_js, ABS_X, 0, 255, 0, 0);
+ input_set_capability(input_js, EV_ABS, ABS_Y);
+ input_set_abs_params(input_js, ABS_Y, 0, 255, 0, 0);
+}
+
static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
static const char * const led_names[] = {
@@ -739,7 +1126,7 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
unsigned int connect_mask = 0;
bool has_ff000000 = false;
struct lg_g15_data *g15;
- struct input_dev *input;
+ struct input_dev *input, *input_js;
struct hid_report *rep;
int ret, i, gkeys = 0;
@@ -778,6 +1165,25 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_set_drvdata(hdev, (void *)g15);
switch (g15->model) {
+ case LG_G13:
+ /*
+ * The G13 has an analog thumbstick with nearby buttons. Some
+ * libraries and applications are known to ignore devices that
+ * don't "look like" a joystick, and a device with two ABS axes
+ * and 25+ macro keys would confuse them.
+ *
+ * Create an additional input device dedicated to appear as a
+ * simplified joystick (two ABS axes, three BTN buttons).
+ */
+ input_js = devm_input_allocate_device(&hdev->dev);
+ if (!input_js)
+ return -ENOMEM;
+ g15->input_js = input_js;
+ input_set_drvdata(input_js, hdev);
+
+ connect_mask = HID_CONNECT_HIDRAW;
+ gkeys = 25;
+ break;
case LG_G15:
INIT_WORK(&g15->work, lg_g15_leds_changed_work);
/*
@@ -859,6 +1265,38 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto error_hw_stop;
return 0; /* All done */
+ } else if (g15->model == LG_G13) {
+ static char const * const g13_led_names[] = {
+ /* Backlight is shared between LCD and keys. */
+ "g13:rgb:kbd_backlight",
+ NULL, /* Keep in sync with led_type enum */
+ "g13:red:macro_preset_1",
+ "g13:red:macro_preset_2",
+ "g13:red:macro_preset_3",
+ "g13:red:macro_record",
+ };
+ lg_g13_init_input_dev(hdev,
+ input, "Logitech G13 Gaming Keypad",
+ input_js, "Logitech G13 Thumbstick");
+ ret = input_register_device(input);
+ if (ret)
+ goto error_hw_stop;
+ ret = input_register_device(input_js);
+ if (ret)
+ goto error_hw_stop;
+
+ for (i = 0; i < ARRAY_SIZE(g13_led_names); ++i) {
+ if (g13_led_names[i]) {
+ ret = lg_g15_register_led(g15, i, g13_led_names[i]);
+ if (ret)
+ goto error_hw_stop;
+ }
+ }
+ led_classdev_notify_brightness_hw_changed(
+ &g15->leds[LG_G15_KBD_BRIGHTNESS].mcdev.led_cdev,
+ g15->backlight_disabled
+ ? 0 : g15->leds[LG_G15_KBD_BRIGHTNESS].brightness);
+ return 0;
}
/* Setup and register input device */
@@ -903,6 +1341,13 @@ error_hw_stop:
}
static const struct hid_device_id lg_g15_devices[] = {
+ /*
+ * The G13 is a macropad-only device with an LCD, LED backlighing,
+ * and joystick.
+ */
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_G13),
+ .driver_data = LG_G13 },
/* The G11 is a G15 without the LCD, treat it as a G15 */
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_G11),
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index cce54dd9884a..44b716697510 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -116,6 +116,7 @@ enum recvr_type {
recvr_type_dj,
recvr_type_hidpp,
recvr_type_gaming_hidpp,
+ recvr_type_gaming_hidpp_ls_1_3,
recvr_type_mouse_only,
recvr_type_27mhz,
recvr_type_bluetooth,
@@ -148,6 +149,7 @@ struct dj_receiver_dev {
struct kfifo notif_fifo;
unsigned long last_query; /* in jiffies */
bool ready;
+ bool dj_mode;
enum recvr_type type;
unsigned int unnumbered_application;
spinlock_t lock;
@@ -211,6 +213,44 @@ static const char kbd_descriptor[] = {
0xC0
};
+/* Gaming Keyboard descriptor (1) */
+static const char kbd_lightspeed_1_3_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x06, /* Usage (Keyboard) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x85, 0x01, /* Report ID (1) */
+ 0x05, 0x07, /* Usage Page (Kbrd/Keypad) */
+ 0x19, 0xE0, /* Usage Minimum (0xE0) */
+ 0x29, 0xE7, /* Usage Maximum (0xE7) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x95, 0x08, /* Report Count (8) */
+ 0x81, 0x02, /* Input (Data,Var) */
+ 0x95, 0x70, /* Report Count (112) */
+ 0x19, 0x04, /* Usage Minimum (0x04) */
+ 0x29, 0x73, /* Usage Maximum (0x73) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x19, 0x87, /* Usage Minimum (0x87) */
+ 0x29, 0x8B, /* Usage Maximum (0x8B) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x19, 0x90, /* Usage Minimum (0x90) */
+ 0x29, 0x92, /* Usage Maximum (0x92) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x85, 0x0E, /* Report ID (14) */
+ 0x05, 0x08, /* Usage Page (LEDs) */
+ 0x19, 0x01, /* Usage Minimum (Num Lock) */
+ 0x29, 0x05, /* Usage Maximum (Kana) */
+ 0x91, 0x02, /* Output (Data,Var,Abs) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x03, /* Report Size (3) */
+ 0x91, 0x03, /* Output (Const,Var,Abs) */
+ 0xC0, /* End Collection */
+};
+
/* Mouse descriptor (2) */
static const char mse_descriptor[] = {
0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
@@ -415,6 +455,51 @@ static const char mse_high_res_descriptor[] = {
0xC0, /* END_COLLECTION */
};
+/* Gaming Mouse descriptor with vendor data (2) */
+static const char mse_high_res_ls_1_3_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x02, /* Usage (Mouse) */
+ 0xA1, 0x01, /* Collection (Application) */
+ 0x85, 0x02, /* Report ID (2) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xA1, 0x00, /* Collection (Physical) */
+ 0x95, 0x10, /* Report Count (16) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (0x01) */
+ 0x29, 0x10, /* Usage Maximum (0x10) */
+ 0x81, 0x02, /* Input (Data,Var,Abs) */
+ 0x95, 0x02, /* Report Count (2) */
+ 0x75, 0x10, /* Report Size (16) */
+ 0x16, 0x01, 0x80, /* Logical Minimum (-32767) */
+ 0x26, 0xFF, 0x7F, /* Logical Maximum (32767) */
+ 0x05, 0x01, /* Usage Page (Generic Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x81, 0x06, /* Input (Data,Var,Rel) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x15, 0x81, /* Logical Minimum (-127) */
+ 0x25, 0x7F, /* Logical Maximum (127) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x81, 0x06, /* Input (Data,Var,Rel) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x05, 0x0C, /* Usage Page (Consumer) */
+ 0x0A, 0x38, 0x02, /* Usage (AC Pan) */
+ 0x81, 0x06, /* Input (Data,Var,Rel) */
+ 0xC0, /* End Collection */
+ 0x06, 0x00, 0xFF, /* Usage Page (Vendor Defined 0xFF00) */
+ 0x09, 0xF1, /* Usage (0xF1) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x05, /* Report Count (5) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255) */
+ 0x81, 0x00, /* Input (Data,Array,Abs) */
+ 0xC0, /* End Collection */
+};
+
/* Consumer Control descriptor (3) */
static const char consumer_descriptor[] = {
0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
@@ -520,9 +605,9 @@ static const char hidpp_descriptor[] = {
/* Maximum size of all defined hid reports in bytes (including report id) */
#define MAX_REPORT_SIZE 8
-/* Make sure all descriptors are present here */
+/* Make sure the largest of each descriptor type is present here */
#define MAX_RDESC_SIZE \
- (sizeof(kbd_descriptor) + \
+ (sizeof(kbd_lightspeed_1_3_descriptor) +\
sizeof(mse_bluetooth_descriptor) + \
sizeof(mse5_bluetooth_descriptor) + \
sizeof(consumer_descriptor) + \
@@ -557,6 +642,8 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
static const struct hid_ll_driver logi_dj_ll_driver;
static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
+static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
+ unsigned int timeout);
static void delayedwork_callback(struct work_struct *work);
static LIST_HEAD(dj_hdev_list);
@@ -805,7 +892,6 @@ static void delayedwork_callback(struct work_struct *work)
struct dj_workitem workitem;
unsigned long flags;
int count;
- int retval;
dbg_hid("%s\n", __func__);
@@ -842,11 +928,10 @@ static void delayedwork_callback(struct work_struct *work)
logi_dj_recv_destroy_djhid_device(djrcv_dev, &workitem);
break;
case WORKITEM_TYPE_UNKNOWN:
- retval = logi_dj_recv_query_paired_devices(djrcv_dev);
- if (retval) {
- hid_err(djrcv_dev->hidpp, "%s: logi_dj_recv_query_paired_devices error: %d\n",
- __func__, retval);
- }
+ if (!djrcv_dev->dj_mode)
+ logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
+
+ logi_dj_recv_query_paired_devices(djrcv_dev);
break;
case WORKITEM_TYPE_EMPTY:
dbg_hid("%s: device list is empty\n", __func__);
@@ -1239,8 +1324,13 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
djrcv_dev->last_query = jiffies;
- if (djrcv_dev->type != recvr_type_dj)
- return logi_dj_recv_query_hidpp_devices(djrcv_dev);
+ if (!djrcv_dev->dj_mode)
+ return 0;
+
+ if (djrcv_dev->type != recvr_type_dj) {
+ retval = logi_dj_recv_query_hidpp_devices(djrcv_dev);
+ goto out;
+ }
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
@@ -1250,6 +1340,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
dj_report->report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES;
retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
kfree(dj_report);
+out:
+ if (retval < 0)
+ hid_err(djrcv_dev->hidpp, "%s error:%d\n", __func__, retval);
+
return retval;
}
@@ -1275,6 +1369,8 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
(u8)timeout;
retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
+ if (retval)
+ goto out;
/*
* Ugly sleep to work around a USB 3.0 bug when the receiver is
@@ -1283,11 +1379,6 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
* 50 msec should gives enough time to the receiver to be ready.
*/
msleep(50);
-
- if (retval) {
- kfree(dj_report);
- return retval;
- }
}
/*
@@ -1313,7 +1404,13 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT,
HID_REQ_SET_REPORT);
+out:
kfree(dj_report);
+
+ if (retval < 0)
+ hid_err(hdev, "%s error:%d\n", __func__, retval);
+
+ djrcv_dev->dj_mode = retval >= 0;
return retval;
}
@@ -1374,12 +1471,19 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
return -EINVAL;
if (djrcv_dev->type != recvr_type_dj && count >= 2) {
+ unsigned char led_report_id = 0;
+
if (!djrcv_dev->keyboard) {
hid_warn(hid, "Received REPORT_TYPE_LEDS request before the keyboard interface was enumerated\n");
return 0;
}
+
+ /* This Lightspeed receiver expects LED reports with report ID 1 */
+ if (djrcv_dev->type == recvr_type_gaming_hidpp_ls_1_3)
+ led_report_id = 1;
+
/* usbhid overrides the report ID and ignores the first byte */
- return hid_hw_raw_request(djrcv_dev->keyboard, 0, buf, count,
+ return hid_hw_raw_request(djrcv_dev->keyboard, led_report_id, buf, count,
report_type, reqtype);
}
@@ -1426,7 +1530,11 @@ static int logi_dj_ll_parse(struct hid_device *hid)
if (djdev->reports_supported & STD_KEYBOARD) {
dbg_hid("%s: sending a kbd descriptor, reports_supported: %llx\n",
__func__, djdev->reports_supported);
- rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
+ if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp_ls_1_3)
+ rdcat(rdesc, &rsize, kbd_lightspeed_1_3_descriptor,
+ sizeof(kbd_lightspeed_1_3_descriptor));
+ else
+ rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
}
if (djdev->reports_supported & STD_MOUSE) {
@@ -1436,6 +1544,9 @@ static int logi_dj_ll_parse(struct hid_device *hid)
djdev->dj_receiver_dev->type == recvr_type_mouse_only)
rdcat(rdesc, &rsize, mse_high_res_descriptor,
sizeof(mse_high_res_descriptor));
+ else if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp_ls_1_3)
+ rdcat(rdesc, &rsize, mse_high_res_ls_1_3_descriptor,
+ sizeof(mse_high_res_ls_1_3_descriptor));
else if (djdev->dj_receiver_dev->type == recvr_type_27mhz)
rdcat(rdesc, &rsize, mse_27mhz_descriptor,
sizeof(mse_27mhz_descriptor));
@@ -1695,11 +1806,12 @@ static int logi_dj_raw_event(struct hid_device *hdev,
}
/*
* Mouse-only receivers send unnumbered mouse data. The 27 MHz
- * receiver uses 6 byte packets, the nano receiver 8 bytes.
+ * receiver uses 6 byte packets, the nano receiver 8 bytes,
+ * the lightspeed receiver (Pro X Superlight) 13 bytes.
*/
if (djrcv_dev->unnumbered_application == HID_GD_MOUSE &&
- size <= 8) {
- u8 mouse_report[9];
+ size <= 13){
+ u8 mouse_report[14];
/* Prepend report id */
mouse_report[0] = REPORT_TYPE_MOUSE;
@@ -1776,6 +1888,7 @@ static int logi_dj_probe(struct hid_device *hdev,
case recvr_type_dj: no_dj_interfaces = 3; break;
case recvr_type_hidpp: no_dj_interfaces = 2; break;
case recvr_type_gaming_hidpp: no_dj_interfaces = 3; break;
+ case recvr_type_gaming_hidpp_ls_1_3: no_dj_interfaces = 3; break;
case recvr_type_mouse_only: no_dj_interfaces = 2; break;
case recvr_type_27mhz: no_dj_interfaces = 2; break;
case recvr_type_bluetooth: no_dj_interfaces = 2; break;
@@ -1834,12 +1947,11 @@ static int logi_dj_probe(struct hid_device *hdev,
}
if (has_hidpp) {
- retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
- if (retval < 0) {
- hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n",
- __func__, retval);
- goto switch_to_dj_mode_fail;
- }
+ /*
+ * This can fail with a KVM. Ignore errors to let the probe
+ * succeed, logi_dj_recv_queue_unknown_work will retry later.
+ */
+ logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
}
/* This is enabling the polling urb on the IN endpoint */
@@ -1857,21 +1969,13 @@ static int logi_dj_probe(struct hid_device *hdev,
spin_lock_irqsave(&djrcv_dev->lock, flags);
djrcv_dev->ready = true;
spin_unlock_irqrestore(&djrcv_dev->lock, flags);
- retval = logi_dj_recv_query_paired_devices(djrcv_dev);
- if (retval < 0) {
- hid_err(hdev, "%s: logi_dj_recv_query_paired_devices error:%d\n",
- __func__, retval);
- /*
- * This can happen with a KVM, let the probe succeed,
- * logi_dj_recv_queue_unknown_work will retry later.
- */
- }
+ /* This too can fail with a KVM, ignore errors. */
+ logi_dj_recv_query_paired_devices(djrcv_dev);
}
return 0;
llopen_failed:
-switch_to_dj_mode_fail:
hid_hw_stop(hdev);
hid_hw_start_fail:
@@ -1882,18 +1986,12 @@ hid_hw_start_fail:
#ifdef CONFIG_PM
static int logi_dj_reset_resume(struct hid_device *hdev)
{
- int retval;
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
if (!djrcv_dev || djrcv_dev->hidpp != hdev)
return 0;
- retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
- if (retval < 0) {
- hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n",
- __func__, retval);
- }
-
+ logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
return 0;
}
#endif
@@ -1987,6 +2085,14 @@ static const struct hid_device_id logi_dj_receivers[] = {
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_2),
.driver_data = recvr_type_gaming_hidpp},
+ { /* Logitech lightspeed receiver (0xc547) */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3),
+ .driver_data = recvr_type_gaming_hidpp_ls_1_3},
+ { /* Logitech lightspeed receiver (0xc54d) */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_4),
+ .driver_data = recvr_type_gaming_hidpp_ls_1_3},
{ /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 5e763de4b94f..d5011a5d0890 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -352,10 +352,15 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp,
do {
ret = __do_hidpp_send_message_sync(hidpp, message, response);
- if (ret != HIDPP20_ERROR_BUSY)
+ if (response->report_id == REPORT_ID_HIDPP_SHORT &&
+ ret != HIDPP_ERROR_BUSY)
+ break;
+ if ((response->report_id == REPORT_ID_HIDPP_LONG ||
+ response->report_id == REPORT_ID_HIDPP_VERY_LONG) &&
+ ret != HIDPP20_ERROR_BUSY)
break;
- dbg_hid("%s:got busy hidpp 2.0 error %02X, retrying\n", __func__, ret);
+ dbg_hid("%s:got busy hidpp error %02X, retrying\n", __func__, ret);
} while (--max_retries);
mutex_unlock(&hidpp->send_mutex);
@@ -971,7 +976,8 @@ static int hidpp_root_get_protocol_version(struct hidpp_device *hidpp)
}
/* the device might not be connected */
- if (ret == HIDPP_ERROR_RESOURCE_ERROR)
+ if (ret == HIDPP_ERROR_RESOURCE_ERROR ||
+ ret == HIDPP_ERROR_UNKNOWN_DEVICE)
return -EIO;
if (ret > 0) {
diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c
index c2849a541f65..7ac9217d9096 100644
--- a/drivers/hid/hid-nintendo.c
+++ b/drivers/hid/hid-nintendo.c
@@ -819,7 +819,7 @@ static void joycon_wait_for_input_report(struct joycon_ctlr *ctlr)
#define JC_INPUT_REPORT_MAX_DELTA 17
#define JC_SUBCMD_TX_OFFSET_MS 4
#define JC_SUBCMD_VALID_DELTA_REQ 3
-#define JC_SUBCMD_RATE_MAX_ATTEMPTS 500
+#define JC_SUBCMD_RATE_MAX_ATTEMPTS 25
#define JC_SUBCMD_RATE_LIMITER_USB_MS 20
#define JC_SUBCMD_RATE_LIMITER_BT_MS 60
#define JC_SUBCMD_RATE_LIMITER_MS(ctlr) ((ctlr)->hdev->bus == BUS_USB ? JC_SUBCMD_RATE_LIMITER_USB_MS : JC_SUBCMD_RATE_LIMITER_BT_MS)
@@ -2648,7 +2648,8 @@ static int nintendo_hid_probe(struct hid_device *hdev,
init_waitqueue_head(&ctlr->wait);
spin_lock_init(&ctlr->lock);
ctlr->rumble_queue = alloc_workqueue("hid-nintendo-rumble_wq",
- WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
+ WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
+ 0);
if (!ctlr->rumble_queue) {
ret = -ENOMEM;
goto err;
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 0f76e241e0af..a7f10c45f62b 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -142,13 +142,13 @@ static void ntrig_report_version(struct hid_device *hdev)
int ret;
char buf[20];
struct usb_device *usb_dev = hid_to_usb_dev(hdev);
- unsigned char *data = kmalloc(8, GFP_KERNEL);
+ unsigned char *data __free(kfree) = kmalloc(8, GFP_KERNEL);
if (!hid_is_usb(hdev))
return;
if (!data)
- goto err_free;
+ return;
ret = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
USB_REQ_CLEAR_FEATURE,
@@ -163,9 +163,6 @@ static void ntrig_report_version(struct hid_device *hdev)
hid_info(hdev, "Firmware version: %s (%02x%02x %02x%02x)\n",
buf, data[2], data[3], data[4], data[5]);
}
-
-err_free:
- kfree(data);
}
static ssize_t show_phys_width(struct device *dev,
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index 63f6eb9030d1..128aa6abd10b 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -1942,6 +1942,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
"Failed to retrieve DualShock4 calibration info: %d\n",
ret);
ret = -EILSEQ;
+ kfree(buf);
goto transfer_failed;
} else {
break;
@@ -1959,6 +1960,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
if (ret) {
hid_warn(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret);
+ kfree(buf);
goto transfer_failed;
}
}
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index bcd4bccf1a7c..c89a015686c0 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -410,7 +410,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
#if IS_ENABLED(CONFIG_HID_ELECOM)
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
@@ -915,7 +916,6 @@ static const struct hid_device_id hid_ignore_list[] = {
#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_HP_5MP_CAMERA_5473) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SMARTLINKTECHNOLOGY, USB_DEVICE_ID_SMARTLINKTECHNOLOGY_4155) },
{ }
};
@@ -1064,6 +1064,18 @@ bool hid_ignore(struct hid_device *hdev)
strlen(elan_acpi_id[i].id)))
return true;
break;
+ case USB_VENDOR_ID_JIELI_SDK_DEFAULT:
+ /*
+ * Multiple USB devices with identical IDs (mic & touchscreen).
+ * The touch screen requires hid core processing, but the
+ * microphone does not. They can be distinguished by manufacturer
+ * and serial number.
+ */
+ if (hdev->product == USB_DEVICE_ID_JIELI_SDK_4155 &&
+ strncmp(hdev->name, "SmartlinkTechnology", 19) == 0 &&
+ strncmp(hdev->uniq, "20201111000001", 14) == 0)
+ return true;
+ break;
}
if (hdev->type == HID_TYPE_USBMOUSE &&
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index 34fb03ae8ee2..90ebb81041ea 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -362,6 +362,23 @@ static int uclogic_raw_event_pen(struct uclogic_drvdata *drvdata,
data[8] = pressure_low_byte;
data[9] = pressure_high_byte;
}
+ if (size == 12 && pen->fragmented_hires2) {
+ // 00 00 when on the left side, 01 00 in the right
+ // we move these to the end of the x coord (u16) to create a correct x coord (u32)
+ u8 lsb_low_byte = data[10];
+ u8 lsb_high_byte = data[11];
+
+ // shift everything right by 2 bytes, to make space for the moved lsb
+ data[11] = data[9];
+ data[10] = data[8];
+ data[9] = data[7];
+ data[8] = data[6];
+ data[7] = data[5];
+ data[6] = data[4];
+
+ data[4] = lsb_low_byte;
+ data[5] = lsb_high_byte;
+ }
/* If we need to emulate in-range detection */
if (pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE) {
/* Set in-range bit */
@@ -604,6 +621,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
+ USB_DEVICE_ID_UGEE_XPPEN_TABLET_24_PRO) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index ffa14a4621ef..e28176d9d9c9 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -1123,6 +1123,9 @@ static int uclogic_params_parse_ugee_v2_desc(const __u8 *str_desc,
return -EINVAL;
pen_x_lm = get_unaligned_le16(str_desc + 2);
+ if (str_desc_size > 12)
+ pen_x_lm += (u8)str_desc[12] << 16;
+
pen_y_lm = get_unaligned_le16(str_desc + 4);
frame_num_buttons = str_desc[6];
*frame_type = str_desc[7];
@@ -1369,8 +1372,10 @@ static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev,
event_hook->hdev = hdev;
event_hook->size = ARRAY_SIZE(reconnect_event);
event_hook->event = kmemdup(reconnect_event, event_hook->size, GFP_KERNEL);
- if (!event_hook->event)
+ if (!event_hook->event) {
+ kfree(event_hook);
return -ENOMEM;
+ }
list_add_tail(&event_hook->list, &p->event_hooks->list);
@@ -1532,7 +1537,7 @@ cleanup:
}
/*
- * uclogic_params_init_ugee_xppen_pro_22r() - Initializes a UGEE XP-Pen Pro 22R tablet device.
+ * uclogic_params_init_ugee_xppen_pro() - Initializes a UGEE XP-Pen Pro tablet device.
*
* @hdev: The HID device of the tablet interface to initialize and get
* parameters from. Cannot be NULL.
@@ -1543,15 +1548,17 @@ cleanup:
* Returns:
* Zero, if successful. A negative errno code on error.
*/
-static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params,
- struct hid_device *hdev,
- const u8 rdesc_frame_arr[],
- const size_t rdesc_frame_size)
+static int uclogic_params_init_ugee_xppen_pro(struct uclogic_params *params,
+ struct hid_device *hdev,
+ const u8 rdesc_pen_arr[],
+ const size_t rdesc_pen_size,
+ const u8 rdesc_frame_arr[],
+ const size_t rdesc_frame_size,
+ size_t str_desc_len)
{
int rc = 0;
struct usb_interface *iface;
__u8 bInterfaceNumber;
- const int str_desc_len = 12;
u8 *str_desc = NULL;
__u8 *rdesc_pen = NULL;
s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
@@ -1614,8 +1621,8 @@ static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params,
/* Initialize the pen interface */
rdesc_pen = uclogic_rdesc_template_apply(
- uclogic_rdesc_ugee_v2_pen_template_arr,
- uclogic_rdesc_ugee_v2_pen_template_size,
+ rdesc_pen_arr,
+ rdesc_pen_size,
desc_params, ARRAY_SIZE(desc_params));
if (!rdesc_pen) {
rc = -ENOMEM;
@@ -1623,7 +1630,7 @@ static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params,
}
p.pen.desc_ptr = rdesc_pen;
- p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;
+ p.pen.desc_size = rdesc_pen_size;
p.pen.id = 0x02;
p.pen.subreport_list[0].value = 0xf0;
p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;
@@ -1970,10 +1977,30 @@ int uclogic_params_init(struct uclogic_params *params,
break;
case VID_PID(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO):
- rc = uclogic_params_init_ugee_xppen_pro_22r(&p,
+ rc = uclogic_params_init_ugee_xppen_pro(&p,
hdev,
+ uclogic_rdesc_ugee_v2_pen_template_arr,
+ uclogic_rdesc_ugee_v2_pen_template_size,
uclogic_rdesc_xppen_artist_22r_pro_frame_arr,
- uclogic_rdesc_xppen_artist_22r_pro_frame_size);
+ uclogic_rdesc_xppen_artist_22r_pro_frame_size,
+ 12);
+ if (rc != 0)
+ goto cleanup;
+
+ break;
+ case VID_PID(USB_VENDOR_ID_UGEE,
+ USB_DEVICE_ID_UGEE_XPPEN_TABLET_24_PRO):
+ rc = uclogic_params_init_ugee_xppen_pro(&p,
+ hdev,
+ uclogic_rdesc_xppen_artist_24_pro_pen_template_arr,
+ uclogic_rdesc_xppen_artist_24_pro_pen_template_size,
+ uclogic_rdesc_xppen_artist_24_pro_frame_arr,
+ uclogic_rdesc_xppen_artist_24_pro_frame_size,
+ 14);
+
+ // The 24 Pro has a fragmented X Coord.
+ p.pen.fragmented_hires2 = true;
+
if (rc != 0)
goto cleanup;
diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h
index 6ec8643d2ee5..c84ff17fb5d5 100644
--- a/drivers/hid/hid-uclogic-params.h
+++ b/drivers/hid/hid-uclogic-params.h
@@ -103,6 +103,11 @@ struct uclogic_params_pen {
* Only valid if "id" is not zero.
*/
bool tilt_y_flipped;
+ /*
+ * True, if reports include fragmented high resolution X coords.
+ * This moves bytes 10-11 to the LSB of the X coordinate.
+ */
+ bool fragmented_hires2;
};
/*
diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c
index 08a89c6aae3b..a1b31511b625 100644
--- a/drivers/hid/hid-uclogic-rdesc.c
+++ b/drivers/hid/hid-uclogic-rdesc.c
@@ -1237,6 +1237,131 @@ const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[] = {
const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size =
sizeof(uclogic_rdesc_xppen_artist_22r_pro_frame_arr);
+/* Fixed report descriptor template for XP-PEN 24 Pro reports
+ * Mostly identical to uclogic_rdesc_ugee_v2_pen_template_arr except that the X coordinate has to be
+ * 32-bits instead of 16-bits.
+ */
+const __u8 uclogic_rdesc_xppen_artist_24_pro_pen_template_arr[] = {
+ 0x05, 0x0d, /* Usage Page (Digitizers), */
+ 0x09, 0x01, /* Usage (Digitizer), */
+ 0xa1, 0x01, /* Collection (Application), */
+ 0x85, 0x02, /* Report ID (2), */
+ 0x09, 0x20, /* Usage (Stylus), */
+ 0xa1, 0x00, /* Collection (Physical), */
+ 0x09, 0x42, /* Usage (Tip Switch), */
+ 0x09, 0x44, /* Usage (Barrel Switch), */
+ 0x09, 0x46, /* Usage (Tablet Pick), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x09, 0x32, /* Usage (In Range), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x02, /* Report Count (2), */
+ 0x81, 0x03, /* Input (Constant, Variable), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x35, 0x00, /* Physical Minimum (0), */
+ 0xa4, /* Push, */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x65, 0x13, /* Unit (Inch), */
+ 0x55, 0x0d, /* Unit Exponent (-3), */
+ 0x27, UCLOGIC_RDESC_PEN_PH(X_LM),
+ /* Logical Maximum (PLACEHOLDER), */
+ 0x47, UCLOGIC_RDESC_PEN_PH(X_PM),
+ /* Physical Maximum (PLACEHOLDER), */
+ 0x75, 0x20, /* Report Size (32), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM),
+ /* Logical Maximum (PLACEHOLDER), */
+ 0x47, UCLOGIC_RDESC_PEN_PH(Y_PM),
+ /* Physical Maximum (PLACEHOLDER), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xb4, /* Pop, */
+ 0x09, 0x30, /* Usage (Tip Pressure), */
+ 0x45, 0x00, /* Physical Maximum (0), */
+ 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM),
+ /* Logical Maximum (PLACEHOLDER), */
+ 0x75, 0x0D, /* Report Size (13), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x09, 0x3d, /* Usage (X Tilt), */
+ 0x35, 0xC3, /* Physical Minimum (-61), */
+ 0x45, 0x3C, /* Physical Maximum (60), */
+ 0x15, 0xC3, /* Logical Minimum (-61), */
+ 0x25, 0x3C, /* Logical Maximum (60), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x3e, /* Usage (Y Tilt), */
+ 0x35, 0xC3, /* Physical Minimum (-61), */
+ 0x45, 0x3C, /* Physical Maximum (60), */
+ 0x15, 0xC3, /* Logical Minimum (-61), */
+ 0x25, 0x3C, /* Logical Maximum (60), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xc0, /* End Collection, */
+ 0xc0, /* End Collection */
+};
+const size_t uclogic_rdesc_xppen_artist_24_pro_pen_template_size =
+ sizeof(uclogic_rdesc_xppen_artist_24_pro_pen_template_arr);
+
+/* Fixed report descriptor for XP-Pen Arist 24 Pro frame */
+const __u8 uclogic_rdesc_xppen_artist_24_pro_frame_arr[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x07, /* Usage (Keypad), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, UCLOGIC_RDESC_V1_FRAME_ID,
+ /* Report ID (Virtual report), */
+ 0x05, 0x0D, /* Usage Page (Digitizer), */
+ 0x09, 0x39, /* Usage (Tablet Function Keys), */
+ 0xA0, /* Collection (Physical), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x08, /* Report Count (8), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x05, 0x09, /* Usage Page (Button), */
+ 0x19, 0x01, /* Usage Minimum (01h), */
+ 0x29, 0x14, /* Usage Maximum (14h), */
+ 0x95, 0x14, /* Report Count (20), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x14, /* Report Count (20), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x38, /* Usage (Wheel), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x15, 0xFF, /* Logical Minimum (-1), */
+ 0x25, 0x08, /* Logical Maximum (8), */
+ 0x81, 0x06, /* Input (Variable, Relative), */
+ 0x05, 0x0C, /* Usage Page (Consumer Devices), */
+ 0x0A, 0x38, 0x02, /* Usage (AC PAN), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x06, /* Input (Variable, Relative), */
+ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
+ 0x75, 0x08, /* Report Size (8), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 16, /* Report Count (16), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0xC0, /* End Collection */
+ 0xC0, /* End Collection */
+};
+
+const size_t uclogic_rdesc_xppen_artist_24_pro_frame_size =
+ sizeof(uclogic_rdesc_xppen_artist_24_pro_frame_arr);
+
/**
* uclogic_rdesc_template_apply() - apply report descriptor parameters to a
* report descriptor template, creating a report descriptor. Copies the
diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h
index 644a35ff12f2..0619daa6849d 100644
--- a/drivers/hid/hid-uclogic-rdesc.h
+++ b/drivers/hid/hid-uclogic-rdesc.h
@@ -214,4 +214,12 @@ extern const size_t uclogic_rdesc_ugee_g5_frame_size;
extern const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[];
extern const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size;
+/* Fixed report descriptor for XP-Pen Arist 24 Pro frame */
+extern const __u8 uclogic_rdesc_xppen_artist_24_pro_pen_template_arr[];
+extern const size_t uclogic_rdesc_xppen_artist_24_pro_pen_template_size;
+
+/* Fixed report descriptor for XP-Pen Arist 24 Pro frame */
+extern const __u8 uclogic_rdesc_xppen_artist_24_pro_frame_arr[];
+extern const size_t uclogic_rdesc_xppen_artist_24_pro_frame_size;
+
#endif /* _HID_UCLOGIC_RDESC_H */
diff --git a/drivers/hid/hid-winwing.c b/drivers/hid/hid-winwing.c
index d4afbbd27807..ab65dc12d1e0 100644
--- a/drivers/hid/hid-winwing.c
+++ b/drivers/hid/hid-winwing.c
@@ -37,6 +37,7 @@ struct winwing_drv_data {
struct hid_device *hdev;
__u8 *report_buf;
struct mutex lock;
+ int map_more_buttons;
unsigned int num_leds;
struct winwing_led leds[];
};
@@ -81,12 +82,10 @@ static int winwing_init_led(struct hid_device *hdev,
int ret;
int i;
- size_t data_size = struct_size(data, leds, 3);
-
- data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL);
+ data = hid_get_drvdata(hdev);
if (!data)
- return -ENOMEM;
+ return -EINVAL;
data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
@@ -106,6 +105,7 @@ static int winwing_init_led(struct hid_device *hdev,
"%s::%s",
dev_name(&input->dev),
info->led_name);
+
if (!led->cdev.name)
return -ENOMEM;
@@ -114,14 +114,98 @@ static int winwing_init_led(struct hid_device *hdev,
return ret;
}
- hid_set_drvdata(hdev, data);
-
return ret;
}
+static int winwing_map_button(int button, int map_more_buttons)
+{
+ if (button < 1)
+ return KEY_RESERVED;
+
+ if (button > 112)
+ return KEY_RESERVED;
+
+ if (button <= 16) {
+ /*
+ * Grip buttons [1 .. 16] are mapped to
+ * key codes BTN_TRIGGER .. BTN_DEAD
+ */
+ return (button - 1) + BTN_JOYSTICK;
+ }
+
+ if (button >= 65) {
+ /*
+ * Base buttons [65 .. 112] are mapped to
+ * key codes BTN_TRIGGER_HAPPY17 .. KEY_MAX
+ */
+ return (button - 65) + BTN_TRIGGER_HAPPY17;
+ }
+
+ if (!map_more_buttons) {
+ /*
+ * Not mapping numbers [33 .. 64] which
+ * are not assigned to any real buttons
+ */
+ if (button >= 33)
+ return KEY_RESERVED;
+ /*
+ * Grip buttons [17 .. 32] are mapped to
+ * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16
+ */
+ return (button - 17) + BTN_TRIGGER_HAPPY1;
+ }
+
+ if (button >= 49) {
+ /*
+ * Grip buttons [49 .. 64] are mapped to
+ * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16
+ */
+ return (button - 49) + BTN_TRIGGER_HAPPY1;
+ }
+
+ /*
+ * Grip buttons [17 .. 44] are mapped to
+ * key codes KEY_MACRO1 .. KEY_MACRO28;
+ * also mapping numbers [45 .. 48] which
+ * are not assigned to any real buttons.
+ */
+ return (button - 17) + KEY_MACRO1;
+}
+
+static int winwing_input_mapping(struct hid_device *hdev,
+ struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ struct winwing_drv_data *data;
+ int code = KEY_RESERVED;
+ int button = 0;
+
+ data = hid_get_drvdata(hdev);
+
+ if (!data)
+ return -EINVAL;
+
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+ return 0;
+
+ if (field->application != HID_GD_JOYSTICK)
+ return 0;
+
+ /* Button numbers start with 1 */
+ button = usage->hid & HID_USAGE;
+
+ code = winwing_map_button(button, data->map_more_buttons);
+
+ hid_map_usage(hi, usage, bit, max, EV_KEY, code);
+
+ return 1;
+}
+
static int winwing_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
+ struct winwing_drv_data *data;
+ size_t data_size = struct_size(data, leds, 3);
int ret;
ret = hid_parse(hdev);
@@ -130,6 +214,15 @@ static int winwing_probe(struct hid_device *hdev,
return ret;
}
+ data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL);
+
+ if (!data)
+ return -ENOMEM;
+
+ data->map_more_buttons = id->driver_data;
+
+ hid_set_drvdata(hdev, data);
+
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
if (ret) {
hid_err(hdev, "hw start failed\n");
@@ -152,64 +245,11 @@ static int winwing_input_configured(struct hid_device *hdev,
return ret;
}
-static const __u8 original_rdesc_buttons[] = {
- 0x05, 0x09, 0x19, 0x01, 0x29, 0x6F,
- 0x15, 0x00, 0x25, 0x01, 0x35, 0x00,
- 0x45, 0x01, 0x75, 0x01, 0x95, 0x6F,
- 0x81, 0x02, 0x75, 0x01, 0x95, 0x01,
- 0x81, 0x01
-};
-
-/*
- * HID report descriptor shows 111 buttons, which exceeds maximum
- * number of buttons (80) supported by Linux kernel HID subsystem.
- *
- * This module skips numbers 32-63, unused on some throttle grips.
- */
-
-static const __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc,
- unsigned int *rsize)
-{
- int sig_length = sizeof(original_rdesc_buttons);
- int unused_button_numbers = 32;
-
- if (*rsize < 34)
- return rdesc;
-
- if (memcmp(rdesc + 8, original_rdesc_buttons, sig_length) == 0) {
-
- /* Usage Maximum */
- rdesc[13] -= unused_button_numbers;
-
- /* Report Count for buttons */
- rdesc[25] -= unused_button_numbers;
-
- /* Report Count for padding [HID1_11, 6.2.2.9] */
- rdesc[31] += unused_button_numbers;
-
- hid_info(hdev, "winwing descriptor fixed\n");
- }
-
- return rdesc;
-}
-
-static int winwing_raw_event(struct hid_device *hdev,
- struct hid_report *report, u8 *raw_data, int size)
-{
- if (size >= 15) {
- /* Skip buttons 32 .. 63 */
- memmove(raw_data + 5, raw_data + 9, 6);
-
- /* Clear the padding */
- memset(raw_data + 11, 0, 4);
- }
-
- return 0;
-}
-
static const struct hid_device_id winwing_devices[] = {
- { HID_USB_DEVICE(0x4098, 0xbe62) }, /* TGRIP-18 */
- { HID_USB_DEVICE(0x4098, 0xbe68) }, /* TGRIP-16EX */
+ { HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 }, /* TGRIP-15E */
+ { HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 }, /* TGRIP-15EX */
+ { HID_USB_DEVICE(0x4098, 0xbe68), .driver_data = 0 }, /* TGRIP-16EX */
+ { HID_USB_DEVICE(0x4098, 0xbe62), .driver_data = 0 }, /* TGRIP-18 */
{}
};
@@ -218,10 +258,9 @@ MODULE_DEVICE_TABLE(hid, winwing_devices);
static struct hid_driver winwing_driver = {
.name = "winwing",
.id_table = winwing_devices,
- .probe = winwing_probe,
.input_configured = winwing_input_configured,
- .report_fixup = winwing_report_fixup,
- .raw_event = winwing_raw_event,
+ .input_mapping = winwing_input_mapping,
+ .probe = winwing_probe,
};
module_hid_driver(winwing_driver);
diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c
index 3ddaa2cd39d5..abf9c9a31c39 100644
--- a/drivers/hid/intel-ish-hid/ipc/ipc.c
+++ b/drivers/hid/intel-ish-hid/ipc/ipc.c
@@ -481,6 +481,20 @@ out:
return ret;
}
+static void ish_send_reset_notify_ack(struct ishtp_device *dev)
+{
+ /* Read reset ID */
+ u32 reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;
+
+ /*
+ * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending
+ * RESET_NOTIFY_ACK - FW will be checking for it
+ */
+ ish_set_host_rdy(dev);
+ /* Send RESET_NOTIFY_ACK (with reset_id) */
+ ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id, sizeof(u32));
+}
+
#define TIME_SLICE_FOR_FW_RDY_MS 100
#define TIME_SLICE_FOR_INPUT_RDY_MS 100
#define TIMEOUT_FOR_FW_RDY_MS 2000
@@ -496,13 +510,9 @@ out:
*/
static int ish_fw_reset_handler(struct ishtp_device *dev)
{
- uint32_t reset_id;
unsigned long flags;
int ret;
- /* Read reset ID */
- reset_id = ish_reg_read(dev, IPC_REG_ISH2HOST_MSG) & 0xFFFF;
-
/* Clear IPC output queue */
spin_lock_irqsave(&dev->wr_processing_spinlock, flags);
list_splice_init(&dev->wr_processing_list, &dev->wr_free_list);
@@ -521,15 +531,6 @@ static int ish_fw_reset_handler(struct ishtp_device *dev)
/* Send clock sync at once after reset */
ishtp_dev->prev_sync = 0;
- /*
- * Set HOST2ISH.ILUP. Apparently we need this BEFORE sending
- * RESET_NOTIFY_ACK - FW will be checking for it
- */
- ish_set_host_rdy(dev);
- /* Send RESET_NOTIFY_ACK (with reset_id) */
- ipc_send_mng_msg(dev, MNG_RESET_NOTIFY_ACK, &reset_id,
- sizeof(uint32_t));
-
/* Wait for ISH FW'es ILUP and ISHTP_READY */
ret = timed_wait_for_timeout(dev, WAIT_FOR_FW_RDY,
TIME_SLICE_FOR_FW_RDY_MS,
@@ -563,8 +564,6 @@ static void fw_reset_work_fn(struct work_struct *work)
if (!rv) {
/* ISH is ILUP & ISHTP-ready. Restart ISHTP */
msleep_interruptible(TIMEOUT_FOR_HW_RDY_MS);
- ishtp_dev->recvd_hw_ready = 1;
- wake_up_interruptible(&ishtp_dev->wait_hw_ready);
/* ISHTP notification in IPC_RESET sequence completion */
if (!work_pending(work))
@@ -625,15 +624,14 @@ static void recv_ipc(struct ishtp_device *dev, uint32_t doorbell_val)
break;
case MNG_RESET_NOTIFY:
- if (!ishtp_dev) {
- ishtp_dev = dev;
- }
- schedule_work(&fw_reset_work);
- break;
+ ish_send_reset_notify_ack(ishtp_dev);
+ fallthrough;
case MNG_RESET_NOTIFY_ACK:
dev->recvd_hw_ready = 1;
wake_up_interruptible(&dev->wait_hw_ready);
+ if (!work_pending(&fw_reset_work))
+ queue_work(dev->unbound_wq, &fw_reset_work);
break;
}
}
@@ -730,22 +728,28 @@ int ish_disable_dma(struct ishtp_device *dev)
* ish_wakeup() - wakeup ishfw from waiting-for-host state
* @dev: ishtp device pointer
*
- * Set the dma enable bit and send a void message to FW,
+ * Set the dma enable bit and send a IPC RESET message to FW,
* it wil wakeup FW from waiting-for-host state.
+ *
+ * Return: 0 for success else error code.
*/
-static void ish_wakeup(struct ishtp_device *dev)
+static int ish_wakeup(struct ishtp_device *dev)
{
+ int ret;
+
/* Set dma enable bit */
ish_reg_write(dev, IPC_REG_ISH_RMP2, IPC_RMP2_DMA_ENABLED);
/*
- * Send 0 IPC message so that ISH FW wakes up if it was already
+ * Send IPC RESET message so that ISH FW wakes up if it was already
* asleep.
*/
- ish_reg_write(dev, IPC_REG_HOST2ISH_DRBL, IPC_DRBL_BUSY_BIT);
+ ret = ish_ipc_reset(dev);
/* Flush writes to doorbell and REMAP2 */
ish_reg_read(dev, IPC_REG_ISH_HOST_FWSTS);
+
+ return ret;
}
/**
@@ -794,11 +798,11 @@ static int _ish_hw_reset(struct ishtp_device *dev)
pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, csr);
/* Now we can enable ISH DMA operation and wakeup ISHFW */
- ish_wakeup(dev);
-
- return 0;
+ return ish_wakeup(dev);
}
+#define RECVD_HW_READY_TIMEOUT (10 * HZ)
+
/**
* _ish_ipc_reset() - IPC reset
* @dev: ishtp device pointer
@@ -833,7 +837,8 @@ static int _ish_ipc_reset(struct ishtp_device *dev)
}
wait_event_interruptible_timeout(dev->wait_hw_ready,
- dev->recvd_hw_ready, 2 * HZ);
+ dev->recvd_hw_ready,
+ RECVD_HW_READY_TIMEOUT);
if (!dev->recvd_hw_ready) {
dev_err(dev->devc, "Timed out waiting for HW ready\n");
rv = -ENODEV;
@@ -857,21 +862,7 @@ int ish_hw_start(struct ishtp_device *dev)
set_host_ready(dev);
/* After that we can enable ISH DMA operation and wakeup ISHFW */
- ish_wakeup(dev);
-
- /* wait for FW-initiated reset flow */
- if (!dev->recvd_hw_ready)
- wait_event_interruptible_timeout(dev->wait_hw_ready,
- dev->recvd_hw_ready,
- 10 * HZ);
-
- if (!dev->recvd_hw_ready) {
- dev_err(dev->devc,
- "[ishtp-ish]: Timed out waiting for FW-initiated reset\n");
- return -ENODEV;
- }
-
- return 0;
+ return ish_wakeup(dev);
}
/**
@@ -933,6 +924,25 @@ static const struct ishtp_hw_ops ish_hw_ops = {
.dma_no_cache_snooping = _dma_no_cache_snooping
};
+static void ishtp_free_workqueue(void *wq)
+{
+ destroy_workqueue(wq);
+}
+
+static struct workqueue_struct *devm_ishtp_alloc_workqueue(struct device *dev)
+{
+ struct workqueue_struct *wq;
+
+ wq = alloc_workqueue("ishtp_unbound_%d", WQ_UNBOUND, 0, dev->id);
+ if (!wq)
+ return NULL;
+
+ if (devm_add_action_or_reset(dev, ishtp_free_workqueue, wq))
+ return NULL;
+
+ return wq;
+}
+
/**
* ish_dev_init() -Initialize ISH devoce
* @pdev: PCI device
@@ -953,6 +963,10 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
if (!dev)
return NULL;
+ dev->unbound_wq = devm_ishtp_alloc_workqueue(&pdev->dev);
+ if (!dev->unbound_wq)
+ return NULL;
+
dev->devc = &pdev->dev;
ishtp_device_init(dev);
@@ -982,6 +996,7 @@ struct ishtp_device *ish_dev_init(struct pci_dev *pdev)
list_add_tail(&tx_buf->link, &dev->wr_free_list);
}
+ ishtp_dev = dev;
ret = devm_work_autocancel(&pdev->dev, &fw_reset_work, fw_reset_work_fn);
if (ret) {
dev_err(dev->devc, "Failed to initialise FW reset work\n");
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
index 9d150ce234f2..1612e8cb23f0 100644
--- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c
+++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
@@ -147,6 +147,12 @@ static inline bool ish_should_enter_d0i3(struct pci_dev *pdev)
static inline bool ish_should_leave_d0i3(struct pci_dev *pdev)
{
+ struct ishtp_device *dev = pci_get_drvdata(pdev);
+ u32 fwsts = dev->ops->get_fw_status(dev);
+
+ if (dev->suspend_flag || !IPC_IS_ISH_ILUP(fwsts))
+ return false;
+
return !pm_resume_via_firmware() || pdev->device == PCI_DEVICE_ID_INTEL_ISH_CHV;
}
@@ -277,10 +283,8 @@ static void __maybe_unused ish_resume_handler(struct work_struct *work)
{
struct pci_dev *pdev = to_pci_dev(ish_resume_device);
struct ishtp_device *dev = pci_get_drvdata(pdev);
- uint32_t fwsts = dev->ops->get_fw_status(dev);
- if (ish_should_leave_d0i3(pdev) && !dev->suspend_flag
- && IPC_IS_ISH_ILUP(fwsts)) {
+ if (ish_should_leave_d0i3(pdev)) {
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(pdev->irq);
@@ -384,12 +388,29 @@ static int __maybe_unused ish_resume(struct device *device)
ish_resume_device = device;
dev->resume_flag = 1;
- schedule_work(&resume_work);
+ /* If ISH resume from D3, reset ishtp clients before return */
+ if (!ish_should_leave_d0i3(pdev))
+ ishtp_reset_handler(dev);
+
+ queue_work(dev->unbound_wq, &resume_work);
return 0;
}
-static SIMPLE_DEV_PM_OPS(ish_pm_ops, ish_suspend, ish_resume);
+static int __maybe_unused ish_freeze(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+
+ return pci_save_state(pdev);
+}
+
+static const struct dev_pm_ops __maybe_unused ish_pm_ops = {
+ .suspend = pm_sleep_ptr(ish_suspend),
+ .resume = pm_sleep_ptr(ish_resume),
+ .freeze = pm_sleep_ptr(ish_freeze),
+ .restore = pm_sleep_ptr(ish_resume),
+ .poweroff = pm_sleep_ptr(ish_suspend),
+};
static ssize_t base_version_show(struct device *cdev,
struct device_attribute *attr, char *buf)
diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c
index d8c3c54a8c0f..f37b3bc2bb7d 100644
--- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c
+++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c
@@ -757,8 +757,15 @@ static void hid_ishtp_cl_resume_handler(struct work_struct *work)
struct ishtp_cl *hid_ishtp_cl = client_data->hid_ishtp_cl;
if (ishtp_wait_resume(ishtp_get_ishtp_device(hid_ishtp_cl))) {
- client_data->suspended = false;
- wake_up_interruptible(&client_data->ishtp_resume_wait);
+ /*
+ * Clear the suspended flag only when the connection is established.
+ * If the connection is not established, the suspended flag will be cleared after
+ * the connection is made.
+ */
+ if (ishtp_get_connection_state(hid_ishtp_cl) == ISHTP_CL_CONNECTED) {
+ client_data->suspended = false;
+ wake_up_interruptible(&client_data->ishtp_resume_wait);
+ }
} else {
hid_ishtp_trace(client_data, "hid client: wait for resume timed out");
dev_err(cl_data_to_dev(client_data), "wait for resume timed out");
@@ -860,7 +867,7 @@ static int hid_ishtp_cl_reset(struct ishtp_cl_device *cl_device)
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl);
- schedule_work(&client_data->work);
+ queue_work(ishtp_get_workqueue(cl_device), &client_data->work);
return 0;
}
@@ -902,7 +909,7 @@ static int hid_ishtp_cl_resume(struct device *device)
hid_ishtp_trace(client_data, "%s hid_ishtp_cl %p\n", __func__,
hid_ishtp_cl);
- schedule_work(&client_data->resume_work);
+ queue_work(ishtp_get_workqueue(cl_device), &client_data->resume_work);
return 0;
}
diff --git a/drivers/hid/intel-ish-hid/ishtp/bus.c b/drivers/hid/intel-ish-hid/ishtp/bus.c
index 93a0432e7058..c6ce37244e49 100644
--- a/drivers/hid/intel-ish-hid/ishtp/bus.c
+++ b/drivers/hid/intel-ish-hid/ishtp/bus.c
@@ -541,7 +541,7 @@ void ishtp_cl_bus_rx_event(struct ishtp_cl_device *device)
return;
if (device->event_cb)
- schedule_work(&device->event_work);
+ queue_work(device->ishtp_dev->unbound_wq, &device->event_work);
}
/**
@@ -877,6 +877,22 @@ struct device *ishtp_get_pci_device(struct ishtp_cl_device *device)
EXPORT_SYMBOL(ishtp_get_pci_device);
/**
+ * ishtp_get_workqueue - Retrieve the workqueue associated with an ISHTP device
+ * @cl_device: Pointer to the ISHTP client device structure
+ *
+ * Returns the workqueue_struct pointer (unbound_wq) associated with the given
+ * ISHTP client device. This workqueue is typically used for scheduling work
+ * related to the device.
+ *
+ * Return: Pointer to struct workqueue_struct.
+ */
+struct workqueue_struct *ishtp_get_workqueue(struct ishtp_cl_device *cl_device)
+{
+ return cl_device->ishtp_dev->unbound_wq;
+}
+EXPORT_SYMBOL(ishtp_get_workqueue);
+
+/**
* ishtp_trace_callback() - Return trace callback
* @cl_device: ISH-TP client device instance
*
diff --git a/drivers/hid/intel-ish-hid/ishtp/client.c b/drivers/hid/intel-ish-hid/ishtp/client.c
index 21a2c0773cc2..40f510b1c072 100644
--- a/drivers/hid/intel-ish-hid/ishtp/client.c
+++ b/drivers/hid/intel-ish-hid/ishtp/client.c
@@ -1261,6 +1261,12 @@ void ishtp_set_connection_state(struct ishtp_cl *cl, int state)
}
EXPORT_SYMBOL(ishtp_set_connection_state);
+int ishtp_get_connection_state(struct ishtp_cl *cl)
+{
+ return cl->state;
+}
+EXPORT_SYMBOL(ishtp_get_connection_state);
+
void ishtp_cl_set_fw_client_id(struct ishtp_cl *cl, int fw_client_id)
{
cl->fw_client_id = fw_client_id;
diff --git a/drivers/hid/intel-ish-hid/ishtp/hbm.c b/drivers/hid/intel-ish-hid/ishtp/hbm.c
index 8ee5467127d8..97c4fcd9e3c6 100644
--- a/drivers/hid/intel-ish-hid/ishtp/hbm.c
+++ b/drivers/hid/intel-ish-hid/ishtp/hbm.c
@@ -573,7 +573,7 @@ void ishtp_hbm_dispatch(struct ishtp_device *dev,
/* Start firmware loading process if it has loader capability */
if (version_res->host_version_supported & ISHTP_SUPPORT_CAP_LOADER)
- schedule_work(&dev->work_fw_loader);
+ queue_work(dev->unbound_wq, &dev->work_fw_loader);
dev->version.major_version = HBM_MAJOR_VERSION;
dev->version.minor_version = HBM_MINOR_VERSION;
@@ -864,7 +864,7 @@ void recv_hbm(struct ishtp_device *dev, struct ishtp_msg_hdr *ishtp_hdr)
dev->rd_msg_fifo_tail = (dev->rd_msg_fifo_tail + IPC_PAYLOAD_SIZE) %
(RD_INT_FIFO_SIZE * IPC_PAYLOAD_SIZE);
spin_unlock_irqrestore(&dev->rd_msg_spinlock, flags);
- schedule_work(&dev->bh_hbm_work);
+ queue_work(dev->unbound_wq, &dev->bh_hbm_work);
eoi:
return;
}
diff --git a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
index 23db97ecf21c..4b0596eadf1c 100644
--- a/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
+++ b/drivers/hid/intel-ish-hid/ishtp/ishtp-dev.h
@@ -175,6 +175,9 @@ struct ishtp_device {
struct hbm_version version;
int transfer_path; /* Choice of transfer path: IPC or DMA */
+ /* Alloc a dedicated unbound workqueue for ishtp device */
+ struct workqueue_struct *unbound_wq;
+
/* work structure for scheduling firmware loading tasks */
struct work_struct work_fw_loader;
/* waitq for waiting for command response from the firmware loader */
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
index 0156ab391778..cfda66ee4895 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
@@ -344,7 +344,6 @@ exit:
if (try_recover(qcdev))
qcdev->state = QUICKI2C_DISABLED;
- pm_runtime_mark_last_busy(qcdev->dev);
pm_runtime_put_autosuspend(qcdev->dev);
return IRQ_HANDLED;
@@ -735,7 +734,6 @@ static int quicki2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* Enable runtime power management */
pm_runtime_use_autosuspend(qcdev->dev);
pm_runtime_set_autosuspend_delay(qcdev->dev, DEFAULT_AUTO_SUSPEND_DELAY_MS);
- pm_runtime_mark_last_busy(qcdev->dev);
pm_runtime_put_noidle(qcdev->dev);
pm_runtime_put_autosuspend(qcdev->dev);
diff --git a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
index 5c3ec95bb3fd..834a537b6780 100644
--- a/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
+++ b/drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-hid.c
@@ -72,7 +72,6 @@ static int quicki2c_hid_raw_request(struct hid_device *hid,
break;
}
- pm_runtime_mark_last_busy(qcdev->dev);
pm_runtime_put_autosuspend(qcdev->dev);
return ret;
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
index 14cabd5dc6dd..ad6bd59963b2 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/pci-quickspi.c
@@ -339,7 +339,6 @@ end:
if (try_recover(qsdev))
qsdev->state = QUICKSPI_DISABLED;
- pm_runtime_mark_last_busy(qsdev->dev);
pm_runtime_put_autosuspend(qsdev->dev);
return IRQ_HANDLED;
@@ -674,7 +673,6 @@ static int quickspi_probe(struct pci_dev *pdev,
/* Enable runtime power management */
pm_runtime_use_autosuspend(qsdev->dev);
pm_runtime_set_autosuspend_delay(qsdev->dev, DEFAULT_AUTO_SUSPEND_DELAY_MS);
- pm_runtime_mark_last_busy(qsdev->dev);
pm_runtime_put_noidle(qsdev->dev);
pm_runtime_put_autosuspend(qsdev->dev);
diff --git a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
index ad52e402c28a..82c72bfa2795 100644
--- a/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
+++ b/drivers/hid/intel-thc-hid/intel-quickspi/quickspi-hid.c
@@ -71,7 +71,6 @@ static int quickspi_hid_raw_request(struct hid_device *hid,
break;
}
- pm_runtime_mark_last_busy(qsdev->dev);
pm_runtime_put_autosuspend(qsdev->dev);
return ret;
diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c
index edd61ef50e16..95377c5f6335 100644
--- a/drivers/hid/usbhid/hid-pidff.c
+++ b/drivers/hid/usbhid/hid-pidff.c
@@ -806,8 +806,8 @@ static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
static int pidff_needs_playback(struct pidff_device *pidff, int effect_id, int n)
{
- return pidff->effect[effect_id].is_infinite ||
- pidff->effect[effect_id].loop_count != n;
+ return !pidff->effect[effect_id].is_infinite ||
+ pidff->effect[effect_id].loop_count != n;
}
/*
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index e3b2bd417c46..1d8d8d00e4e0 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -29,6 +29,7 @@
#include <linux/crash_dump.h>
#include <linux/panic_notifier.h>
#include <linux/vmalloc.h>
+#include <linux/rseq.h>
#include "mshv_eventfd.h"
#include "mshv.h"
@@ -560,6 +561,8 @@ static long mshv_run_vp_with_root_scheduler(struct mshv_vp *vp)
}
} while (!vp->run.flags.intercept_suspend);
+ rseq_virt_userspace_exit();
+
return ret;
}
@@ -1870,8 +1873,6 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
struct hv_partition_creation_properties creation_properties = {};
union hv_partition_isolation_properties isolation_properties = {};
struct mshv_partition *partition;
- struct file *file;
- int fd;
long ret;
if (copy_from_user(&args, user_arg, sizeof(args)))
@@ -1938,29 +1939,13 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
goto delete_partition;
ret = mshv_init_async_handler(partition);
- if (ret)
- goto remove_partition;
-
- fd = get_unused_fd_flags(O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto remove_partition;
- }
-
- file = anon_inode_getfile("mshv_partition", &mshv_partition_fops,
- partition, O_RDWR);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
- goto put_fd;
+ if (!ret) {
+ ret = FD_ADD(O_CLOEXEC, anon_inode_getfile("mshv_partition",
+ &mshv_partition_fops,
+ partition, O_RDWR));
+ if (ret >= 0)
+ return ret;
}
-
- fd_install(fd, file);
-
- return fd;
-
-put_fd:
- put_unused_fd(fd);
-remove_partition:
remove_partition(partition);
delete_partition:
hv_call_delete_partition(partition->pt_id);
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2760feb9f83b..157678b821fc 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -175,7 +175,7 @@ config SENSORS_ADT7X10
select REGMAP
help
This module contains common code shared by the ADT7310/ADT7320 and
- ADT7410/ADT7420 temperature monitoring chip drivers.
+ ADT7410/ADT7420/ADT7422 temperature monitoring chip drivers.
If built as a module, the module will be called adt7x10.
@@ -191,12 +191,12 @@ config SENSORS_ADT7310
will be called adt7310.
config SENSORS_ADT7410
- tristate "Analog Devices ADT7410/ADT7420"
+ tristate "Analog Devices ADT7410/ADT7420/ADT7422"
depends on I2C
select SENSORS_ADT7X10
help
If you say yes here you get support for the Analog Devices
- ADT7410 and ADT7420 temperature monitoring chips.
+ ADT7410, ADT7420 and ADT7422 temperature monitoring chips.
This driver can also be built as a module. If so, the module
will be called adt7410.
@@ -245,12 +245,12 @@ config SENSORS_ADT7475
will be called adt7475.
config SENSORS_AHT10
- tristate "Aosong AHT10, AHT20"
+ tristate "Aosong AHT10, AHT20, DHT20"
depends on I2C
select CRC8
help
- If you say yes here, you get support for the Aosong AHT10 and AHT20
- temperature and humidity sensors
+ If you say yes here, you get support for the Aosong AHT10, AHT20 and
+ DHT20 temperature and humidity sensors
This driver can also be built as a module. If so, the module
will be called aht10.
@@ -1174,6 +1174,18 @@ config SENSORS_LTQ_CPUTEMP
If you say yes here you get support for the temperature
sensor inside your CPU.
+config SENSORS_MACSMC_HWMON
+ tristate "Apple SMC (Apple Silicon)"
+ depends on MFD_MACSMC && OF
+ help
+ This driver enables hwmon support for current, power, temperature,
+ and voltage sensors, as well as fan speed reporting and control
+ on Apple Silicon devices. Say Y here if you have an Apple Silicon
+ device.
+
+ This driver can also be built as a module. If so, the module will
+ be called macsmc-hwmon.
+
config SENSORS_MAX1111
tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
depends on SPI_MASTER
@@ -2434,6 +2446,18 @@ config SENSORS_TMP513
This driver can also be built as a module. If so, the module
will be called tmp513.
+config SENSORS_TSC1641
+ tristate "ST Microelectronics TSC1641 Power Monitor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for TSC1641 power monitor chip.
+ The TSC1641 driver is configured for the default configuration of
+ the part except temperature is enabled by default.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsc1641.
+
config SENSORS_VEXPRESS
tristate "Versatile Express"
depends on VEXPRESS_CONFIG
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 73b2abdcc6dd..eade8e3b1bde 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
obj-$(CONFIG_SENSORS_LTC4282) += ltc4282.o
obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o
+obj-$(CONFIG_SENSORS_MACSMC_HWMON) += macsmc-hwmon.o
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
obj-$(CONFIG_SENSORS_MAX127) += max127.o
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
@@ -233,6 +234,7 @@ obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
obj-$(CONFIG_SENSORS_TMP464) += tmp464.o
obj-$(CONFIG_SENSORS_TMP513) += tmp513.o
+obj-$(CONFIG_SENSORS_TSC1641) += tsc1641.o
obj-$(CONFIG_SENSORS_VEXPRESS) += vexpress-hwmon.o
obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index 80d09b017d3b..c38c932e5d2a 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -197,8 +197,16 @@ static int adm1026_scaling[] = { /* .001 Volts */
#define FAN_TO_REG(val, div) ((val) <= 0 ? 0xff : \
clamp_val(1350000 / ((val) * (div)), \
1, 254))
-#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : (val) == 0xff ? 0 : \
- 1350000 / ((val) * (div)))
+
+static int fan_from_reg(int val, int div)
+{
+ if (val == 0)
+ return -1;
+ if (val == 0xff)
+ return 0;
+ return 1350000 / (val * div);
+}
+
#define DIV_FROM_REG(val) (1 << (val))
#define DIV_TO_REG(val) ((val) >= 8 ? 3 : (val) >= 4 ? 2 : (val) >= 2 ? 1 : 0)
@@ -656,7 +664,7 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
struct adm1026_data *data = adm1026_update_device(dev);
- return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr],
data->fan_div[nr]));
}
static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
@@ -665,7 +673,7 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
struct adm1026_data *data = adm1026_update_device(dev);
- return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan_min[nr],
data->fan_div[nr]));
}
static ssize_t fan_min_store(struct device *dev,
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
index 761c13092488..71eea8ae51b9 100644
--- a/drivers/hwmon/adm1029.c
+++ b/drivers/hwmon/adm1029.c
@@ -171,14 +171,17 @@ fan_show(struct device *dev, struct device_attribute *devattr, char *buf)
struct adm1029_data *data = adm1029_update_device(dev);
u16 val;
+ mutex_lock(&data->update_lock);
if (data->fan[attr->index] == 0 ||
(data->fan_div[attr->index] & 0xC0) == 0 ||
data->fan[attr->index] == 255) {
+ mutex_unlock(&data->update_lock);
return sprintf(buf, "0\n");
}
val = 1880 * 120 / DIV_FROM_REG(data->fan_div[attr->index])
/ data->fan[attr->index];
+ mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", val);
}
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 6dfbeb6acf00..86f6044b5bd0 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -37,7 +37,6 @@
#include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
/* Addresses to scan */
@@ -125,7 +124,6 @@ static inline unsigned int AOUT_FROM_REG(u8 reg)
struct adm9240_data {
struct device *dev;
struct regmap *regmap;
- struct mutex update_lock;
u8 fan_div[2]; /* rw fan1_div, read-only accessor */
u8 vrm; /* -- vrm set on startup, no accessor */
@@ -170,8 +168,6 @@ static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long va
u8 fan_min;
int err;
- mutex_lock(&data->update_lock);
-
if (!val) {
fan_min = 255;
new_div = data->fan_div[channel];
@@ -206,8 +202,6 @@ static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long va
}
err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min);
- mutex_unlock(&data->update_lock);
-
return err;
}
@@ -501,23 +495,17 @@ static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val
switch (attr) {
case hwmon_fan_input:
- mutex_lock(&data->update_lock);
err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), &regval);
- if (err < 0) {
- mutex_unlock(&data->update_lock);
+ if (err < 0)
return err;
- }
if (regval == 255 && data->fan_div[channel] < 3) {
/* adjust fan clock divider on overflow */
err = adm9240_write_fan_div(data, channel,
++data->fan_div[channel]);
- if (err) {
- mutex_unlock(&data->update_lock);
+ if (err)
return err;
- }
}
*val = FAN_FROM_REG(regval, BIT(data->fan_div[channel]));
- mutex_unlock(&data->update_lock);
break;
case hwmon_fan_div:
*val = BIT(data->fan_div[channel]);
@@ -791,7 +779,6 @@ static int adm9240_probe(struct i2c_client *client)
return -ENOMEM;
data->dev = dev;
- mutex_init(&data->update_lock);
data->regmap = devm_regmap_init_i2c(client, &adm9240_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
index 3bf0e0a0882c..73b196a78f3a 100644
--- a/drivers/hwmon/adt7410.c
+++ b/drivers/hwmon/adt7410.c
@@ -7,6 +7,7 @@
*/
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -90,14 +91,24 @@ static int adt7410_i2c_probe(struct i2c_client *client)
static const struct i2c_device_id adt7410_ids[] = {
{ "adt7410" },
{ "adt7420" },
+ { "adt7422" },
{}
};
MODULE_DEVICE_TABLE(i2c, adt7410_ids);
+static const struct of_device_id adt7410_of_match[] = {
+ { .compatible = "adi,adt7410" },
+ { .compatible = "adi,adt7420" },
+ { .compatible = "adi,adt7422" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adt7410_of_match);
+
static struct i2c_driver adt7410_driver = {
.driver = {
.name = "adt7410",
.pm = pm_sleep_ptr(&adt7x10_dev_pm_ops),
+ .of_match_table = adt7410_of_match,
},
.probe = adt7410_i2c_probe,
.id_table = adt7410_ids,
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index 08d0effd97f7..b9991a69e6c6 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -11,7 +11,6 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
@@ -99,8 +98,6 @@ static const u8 adt7411_in_alarm_bits[] = {
};
struct adt7411_data {
- struct mutex device_lock; /* for "atomic" device accesses */
- struct mutex update_lock;
unsigned long next_update;
long vref_cached;
struct i2c_client *client;
@@ -110,55 +107,41 @@ struct adt7411_data {
/*
* When reading a register containing (up to 4) lsb, all associated
* msb-registers get locked by the hardware. After _one_ of those msb is read,
- * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb
- * is protected here with a mutex, too.
+ * _all_ are unlocked.
*/
static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg,
- u8 msb_reg, u8 lsb_shift)
+ u8 msb_reg, u8 lsb_shift)
{
- struct adt7411_data *data = i2c_get_clientdata(client);
int val, tmp;
- mutex_lock(&data->device_lock);
-
val = i2c_smbus_read_byte_data(client, lsb_reg);
if (val < 0)
- goto exit_unlock;
+ return val;
tmp = (val >> lsb_shift) & 3;
val = i2c_smbus_read_byte_data(client, msb_reg);
+ if (val < 0)
+ return val;
- if (val >= 0)
- val = (val << 2) | tmp;
-
- exit_unlock:
- mutex_unlock(&data->device_lock);
-
+ val = (val << 2) | tmp;
return val;
}
static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit,
- bool flag)
+ bool flag)
{
- struct adt7411_data *data = i2c_get_clientdata(client);
int ret, val;
- mutex_lock(&data->device_lock);
-
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
- goto exit_unlock;
+ return ret;
if (flag)
val = ret | bit;
else
val = ret & ~bit;
- ret = i2c_smbus_write_byte_data(client, reg, val);
-
- exit_unlock:
- mutex_unlock(&data->device_lock);
- return ret;
+ return i2c_smbus_write_byte_data(client, reg, val);
}
static ssize_t adt7411_show_bit(struct device *dev,
@@ -186,12 +169,11 @@ static ssize_t adt7411_set_bit(struct device *dev,
if (ret || flag > 1)
return -EINVAL;
+ hwmon_lock(dev);
ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag);
-
/* force update */
- mutex_lock(&data->update_lock);
data->next_update = jiffies;
- mutex_unlock(&data->update_lock);
+ hwmon_unlock(dev);
return ret < 0 ? ret : count;
}
@@ -294,10 +276,9 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
int reg, lsb_reg, lsb_shift;
int nr = channel - 1;
- mutex_lock(&data->update_lock);
ret = adt7411_update_vref(dev);
if (ret < 0)
- goto exit_unlock;
+ return ret;
switch (attr) {
case hwmon_in_input:
@@ -307,7 +288,7 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
ADT7411_REG_EXT_TEMP_AIN1_MSB + nr,
lsb_shift);
if (ret < 0)
- goto exit_unlock;
+ return ret;
*val = ret * data->vref_cached / 1024;
ret = 0;
break;
@@ -318,7 +299,7 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
: ADT7411_REG_IN_HIGH(channel);
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0)
- goto exit_unlock;
+ return ret;
*val = ret * data->vref_cached / 256;
ret = 0;
break;
@@ -329,8 +310,6 @@ static int adt7411_read_in_chan(struct device *dev, u32 attr, int channel,
ret = -EOPNOTSUPP;
break;
}
- exit_unlock:
- mutex_unlock(&data->update_lock);
return ret;
}
@@ -457,10 +436,9 @@ static int adt7411_write_in_chan(struct device *dev, u32 attr, int channel,
struct i2c_client *client = data->client;
int ret, reg;
- mutex_lock(&data->update_lock);
ret = adt7411_update_vref(dev);
if (ret < 0)
- goto exit_unlock;
+ return ret;
val = clamp_val(val, 0, 255 * data->vref_cached / 256);
val = DIV_ROUND_CLOSEST(val * 256, data->vref_cached);
@@ -472,13 +450,10 @@ static int adt7411_write_in_chan(struct device *dev, u32 attr, int channel,
reg = ADT7411_REG_IN_HIGH(channel);
break;
default:
- ret = -EOPNOTSUPP;
- goto exit_unlock;
+ return -EOPNOTSUPP;
}
ret = i2c_smbus_write_byte_data(client, reg, val);
- exit_unlock:
- mutex_unlock(&data->update_lock);
return ret;
}
@@ -679,8 +654,6 @@ static int adt7411_probe(struct i2c_client *client)
i2c_set_clientdata(client, data);
data->client = client;
- mutex_init(&data->device_lock);
- mutex_init(&data->update_lock);
ret = adt7411_init_device(data);
if (ret < 0)
diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c
index 2d329391ed3f..d003ee3ebf06 100644
--- a/drivers/hwmon/adt7x10.c
+++ b/drivers/hwmon/adt7x10.c
@@ -15,7 +15,6 @@
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/regmap.h>
@@ -55,7 +54,6 @@
/* Each client has this additional data */
struct adt7x10_data {
struct regmap *regmap;
- struct mutex update_lock;
u8 config;
u8 oldconfig;
bool valid; /* true if temperature valid */
@@ -137,17 +135,13 @@ static int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val)
unsigned int regval;
int ret;
- mutex_lock(&data->update_lock);
if (index == adt7x10_temperature && !data->valid) {
/* wait for valid temperature */
ret = adt7x10_temp_ready(data->regmap);
- if (ret) {
- mutex_unlock(&data->update_lock);
+ if (ret)
return ret;
- }
data->valid = true;
}
- mutex_unlock(&data->update_lock);
ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &regval);
if (ret)
@@ -159,13 +153,8 @@ static int adt7x10_temp_read(struct adt7x10_data *data, int index, long *val)
static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp)
{
- int ret;
-
- mutex_lock(&data->update_lock);
- ret = regmap_write(data->regmap, ADT7X10_REG_TEMP[index],
- ADT7X10_TEMP_TO_REG(temp));
- mutex_unlock(&data->update_lock);
- return ret;
+ return regmap_write(data->regmap, ADT7X10_REG_TEMP[index],
+ ADT7X10_TEMP_TO_REG(temp));
}
static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val)
@@ -197,22 +186,17 @@ static int adt7x10_hyst_write(struct adt7x10_data *data, long hyst)
unsigned int regval;
int limit, ret;
- mutex_lock(&data->update_lock);
-
/* convert absolute hysteresis value to a 4 bit delta value */
ret = regmap_read(data->regmap, ADT7X10_T_ALARM_HIGH, &regval);
if (ret < 0)
- goto abort;
+ return ret;
limit = ADT7X10_REG_TO_TEMP(data, regval);
hyst = clamp_val(hyst, ADT7X10_TEMP_MIN, ADT7X10_TEMP_MAX);
regval = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0,
ADT7X10_T_HYST_MASK);
- ret = regmap_write(data->regmap, ADT7X10_T_HYST, regval);
-abort:
- mutex_unlock(&data->update_lock);
- return ret;
+ return regmap_write(data->regmap, ADT7X10_T_HYST, regval);
}
static int adt7x10_alarm_read(struct adt7x10_data *data, int index, long *val)
@@ -344,7 +328,6 @@ int adt7x10_probe(struct device *dev, const char *name, int irq,
data->regmap = regmap;
dev_set_drvdata(dev, data);
- mutex_init(&data->update_lock);
/* configure as specified */
ret = regmap_read(regmap, ADT7X10_CONFIG, &config);
diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c
index d1c55e2eb479..007befdba977 100644
--- a/drivers/hwmon/aht10.c
+++ b/drivers/hwmon/aht10.c
@@ -37,6 +37,8 @@
#define AHT10_CMD_MEAS 0b10101100
#define AHT10_CMD_RST 0b10111010
+#define DHT20_CMD_INIT 0x71
+
/*
* Flags in the answer byte/command
*/
@@ -48,11 +50,12 @@
#define AHT10_MAX_POLL_INTERVAL_LEN 30
-enum aht10_variant { aht10, aht20 };
+enum aht10_variant { aht10, aht20, dht20};
static const struct i2c_device_id aht10_id[] = {
{ "aht10", aht10 },
{ "aht20", aht20 },
+ { "dht20", dht20 },
{ },
};
MODULE_DEVICE_TABLE(i2c, aht10_id);
@@ -60,8 +63,6 @@ MODULE_DEVICE_TABLE(i2c, aht10_id);
/**
* struct aht10_data - All the data required to operate an AHT10/AHT20 chip
* @client: the i2c client associated with the AHT10/AHT20
- * @lock: a mutex that is used to prevent parallel access to the
- * i2c client
* @min_poll_interval: the minimum poll interval
* While the poll rate limit is not 100% necessary,
* the datasheet recommends that a measurement
@@ -77,21 +78,18 @@ MODULE_DEVICE_TABLE(i2c, aht10_id);
* AHT10/AHT20
* @crc8: crc8 support flag
* @meas_size: measurements data size
+ * @init_cmd: Initialization command
*/
struct aht10_data {
struct i2c_client *client;
- /*
- * Prevent simultaneous access to the i2c
- * client and previous_poll_time
- */
- struct mutex lock;
ktime_t min_poll_interval;
ktime_t previous_poll_time;
int temperature;
int humidity;
bool crc8;
unsigned int meas_size;
+ u8 init_cmd;
};
/*
@@ -101,13 +99,13 @@ struct aht10_data {
*/
static int aht10_init(struct aht10_data *data)
{
- const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC,
+ const u8 cmd_init[] = {data->init_cmd, AHT10_CAL_ENABLED | AHT10_MODE_CYC,
0x00};
int res;
u8 status;
struct i2c_client *client = data->client;
- res = i2c_master_send(client, cmd_init, 3);
+ res = i2c_master_send(client, cmd_init, sizeof(cmd_init));
if (res < 0)
return res;
@@ -168,32 +166,24 @@ static int aht10_read_values(struct aht10_data *data)
u8 raw_data[AHT20_MEAS_SIZE];
struct i2c_client *client = data->client;
- mutex_lock(&data->lock);
- if (!aht10_polltime_expired(data)) {
- mutex_unlock(&data->lock);
+ if (!aht10_polltime_expired(data))
return 0;
- }
res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas));
- if (res < 0) {
- mutex_unlock(&data->lock);
+ if (res < 0)
return res;
- }
usleep_range(AHT10_MEAS_DELAY, AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA);
res = i2c_master_recv(client, raw_data, data->meas_size);
if (res != data->meas_size) {
- mutex_unlock(&data->lock);
if (res >= 0)
return -ENODATA;
return res;
}
- if (data->crc8 && crc8_check(raw_data, data->meas_size)) {
- mutex_unlock(&data->lock);
+ if (data->crc8 && crc8_check(raw_data, data->meas_size))
return -EIO;
- }
hum = ((u32)raw_data[1] << 12u) |
((u32)raw_data[2] << 4u) |
@@ -210,7 +200,6 @@ static int aht10_read_values(struct aht10_data *data)
data->humidity = hum;
data->previous_poll_time = ktime_get_boottime();
- mutex_unlock(&data->lock);
return 0;
}
@@ -352,14 +341,20 @@ static int aht10_probe(struct i2c_client *client)
data->meas_size = AHT20_MEAS_SIZE;
data->crc8 = true;
crc8_populate_msb(crc8_table, AHT20_CRC8_POLY);
+ data->init_cmd = AHT10_CMD_INIT;
+ break;
+ case dht20:
+ data->meas_size = AHT20_MEAS_SIZE;
+ data->crc8 = true;
+ crc8_populate_msb(crc8_table, AHT20_CRC8_POLY);
+ data->init_cmd = DHT20_CMD_INIT;
break;
default:
data->meas_size = AHT10_MEAS_SIZE;
+ data->init_cmd = AHT10_CMD_INIT;
break;
}
- mutex_init(&data->lock);
-
res = aht10_init(data);
if (res < 0)
return res;
diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index 0dcb8a3a691d..1ca70e726298 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -20,7 +20,6 @@
#include <linux/jiffies.h>
#include <linux/ktime.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/unaligned.h>
@@ -551,7 +550,6 @@ struct aqc_data {
struct hid_device *hdev;
struct device *hwmon_dev;
struct dentry *debugfs;
- struct mutex mutex; /* Used for locking access when reading and writing PWM values */
enum kinds kind;
const char *name;
@@ -662,7 +660,6 @@ static void aqc_delay_ctrl_report(struct aqc_data *priv)
}
}
-/* Expects the mutex to be locked */
static int aqc_get_ctrl_data(struct aqc_data *priv)
{
int ret;
@@ -680,7 +677,6 @@ static int aqc_get_ctrl_data(struct aqc_data *priv)
return ret;
}
-/* Expects the mutex to be locked */
static int aqc_send_ctrl_data(struct aqc_data *priv)
{
int ret;
@@ -721,11 +717,9 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val, int ty
{
int ret;
- mutex_lock(&priv->mutex);
-
ret = aqc_get_ctrl_data(priv);
if (ret < 0)
- goto unlock_and_return;
+ return ret;
switch (type) {
case AQC_BE16:
@@ -737,9 +731,6 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val, int ty
default:
ret = -EINVAL;
}
-
-unlock_and_return:
- mutex_unlock(&priv->mutex);
return ret;
}
@@ -747,11 +738,9 @@ static int aqc_set_ctrl_vals(struct aqc_data *priv, int *offsets, long *vals, in
{
int ret, i;
- mutex_lock(&priv->mutex);
-
ret = aqc_get_ctrl_data(priv);
if (ret < 0)
- goto unlock_and_return;
+ return ret;
for (i = 0; i < len; i++) {
switch (types[i]) {
@@ -762,18 +751,11 @@ static int aqc_set_ctrl_vals(struct aqc_data *priv, int *offsets, long *vals, in
priv->buffer[offsets[i]] = (u8)vals[i];
break;
default:
- ret = -EINVAL;
+ return -EINVAL;
}
}
- if (ret < 0)
- goto unlock_and_return;
-
- ret = aqc_send_ctrl_data(priv);
-
-unlock_and_return:
- mutex_unlock(&priv->mutex);
- return ret;
+ return aqc_send_ctrl_data(priv);
}
static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val, int type)
@@ -953,13 +935,11 @@ static int aqc_legacy_read(struct aqc_data *priv)
{
int ret, i, sensor_value;
- mutex_lock(&priv->mutex);
-
memset(priv->buffer, 0x00, priv->buffer_size);
ret = hid_hw_raw_request(priv->hdev, priv->status_report_id, priv->buffer,
priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret < 0)
- goto unlock_and_return;
+ return ret;
/* Temperature sensor readings */
for (i = 0; i < priv->num_temp_sensors; i++) {
@@ -1020,10 +1000,7 @@ static int aqc_legacy_read(struct aqc_data *priv)
}
priv->updated = jiffies;
-
-unlock_and_return:
- mutex_unlock(&priv->mutex);
- return ret;
+ return 0;
}
static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
@@ -1870,8 +1847,6 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto fail_and_close;
}
- mutex_init(&priv->mutex);
-
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv,
&aqc_chip_info, NULL);
diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c
index 4174b129d1fc..44e1ecba205d 100644
--- a/drivers/hwmon/aspeed-g6-pwm-tach.c
+++ b/drivers/hwmon/aspeed-g6-pwm-tach.c
@@ -528,6 +528,9 @@ static const struct of_device_id aspeed_pwm_tach_match[] = {
{
.compatible = "aspeed,ast2600-pwm-tach",
},
+ {
+ .compatible = "aspeed,ast2700-pwm-tach",
+ },
{},
};
MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match);
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index 34a8f6b834c9..61b18b88ee8f 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -113,15 +113,19 @@ enum ec_sensors {
ec_sensor_temp_t_sensor,
/* VRM temperature [℃] */
ec_sensor_temp_vrm,
+ /* VRM east (right) temperature [℃] */
+ ec_sensor_temp_vrme,
+ /* VRM west (left) temperature [℃] */
+ ec_sensor_temp_vrmw,
/* CPU Core voltage [mV] */
ec_sensor_in_cpu_core,
/* CPU_Opt fan [RPM] */
ec_sensor_fan_cpu_opt,
/* VRM heat sink fan [RPM] */
ec_sensor_fan_vrm_hs,
- /* VRM east heat sink fan [RPM] */
+ /* VRM east (right) heat sink fan [RPM] */
ec_sensor_fan_vrme_hs,
- /* VRM west heat sink fan [RPM] */
+ /* VRM west (left) heat sink fan [RPM] */
ec_sensor_fan_vrmw_hs,
/* Chipset fan [RPM] */
ec_sensor_fan_chipset,
@@ -157,6 +161,8 @@ enum ec_sensors {
#define SENSOR_TEMP_MB BIT(ec_sensor_temp_mb)
#define SENSOR_TEMP_T_SENSOR BIT(ec_sensor_temp_t_sensor)
#define SENSOR_TEMP_VRM BIT(ec_sensor_temp_vrm)
+#define SENSOR_TEMP_VRME BIT(ec_sensor_temp_vrme)
+#define SENSOR_TEMP_VRMW BIT(ec_sensor_temp_vrmw)
#define SENSOR_IN_CPU_CORE BIT(ec_sensor_in_cpu_core)
#define SENSOR_FAN_CPU_OPT BIT(ec_sensor_fan_cpu_opt)
#define SENSOR_FAN_VRM_HS BIT(ec_sensor_fan_vrm_hs)
@@ -182,6 +188,7 @@ enum board_family {
family_amd_500_series,
family_amd_600_series,
family_amd_800_series,
+ family_amd_trx_50,
family_amd_wrx_90,
family_intel_200_series,
family_intel_300_series,
@@ -294,6 +301,19 @@ static const struct ec_sensor_info sensors_family_amd_800[] = {
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
};
+static const struct ec_sensor_info sensors_family_amd_trx_50[] = {
+ [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x30),
+ [ec_sensor_temp_cpu_package] =
+ EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
+ [ec_sensor_temp_vrme] = EC_SENSOR("VRM_E", hwmon_temp, 1, 0x00, 0x33),
+ [ec_sensor_temp_vrmw] = EC_SENSOR("VRM_W", hwmon_temp, 1, 0x00, 0x34),
+ [ec_sensor_fan_cpu_opt] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
+ [ec_sensor_fan_vrmw_hs] = EC_SENSOR("VRM_E HS", hwmon_fan, 2, 0x00, 0xb4),
+ [ec_sensor_fan_vrme_hs] = EC_SENSOR("VRM_W HS", hwmon_fan, 2, 0x00, 0xbc),
+ [ec_sensor_temp_t_sensor] =
+ EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x01, 0x04),
+};
+
static const struct ec_sensor_info sensors_family_amd_wrx_90[] = {
[ec_sensor_temp_cpu_package] =
EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
@@ -533,6 +553,15 @@ static const struct ec_board_info board_info_pro_art_x870E_creator_wifi = {
.family = family_amd_800_series,
};
+static const struct ec_board_info board_info_pro_ws_trx50_sage_wifi = {
+ /* Board also has a nct6798 */
+ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_VRME |
+ SENSOR_TEMP_VRMW | SENSOR_FAN_CPU_OPT | SENSOR_FAN_VRME_HS |
+ SENSOR_FAN_VRMW_HS | SENSOR_TEMP_T_SENSOR,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
+ .family = family_amd_trx_50,
+};
+
static const struct ec_board_info board_info_pro_ws_wrx90e_sage_se = {
/* Board also has a nct6798 with 7 more fans and temperatures */
.sensors = SENSOR_TEMP_CPU_PACKAGE | SENSOR_TEMP_T_SENSOR |
@@ -581,6 +610,14 @@ static const struct ec_board_info board_info_strix_b850_i_gaming_wifi = {
.family = family_amd_800_series,
};
+static const struct ec_board_info board_info_strix_x470_i_gaming = {
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_400_series,
+};
+
static const struct ec_board_info board_info_strix_x570_e_gaming = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR |
@@ -628,6 +665,13 @@ static const struct ec_board_info board_info_strix_x670e_i_gaming_wifi = {
.family = family_amd_600_series,
};
+static const struct ec_board_info board_info_strix_x870_f_gaming_wifi = {
+ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
+ SENSOR_TEMP_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
+ .family = family_amd_800_series,
+};
+
static const struct ec_board_info board_info_strix_x870_i_gaming_wifi = {
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
SENSOR_TEMP_MB | SENSOR_TEMP_VRM,
@@ -643,6 +687,14 @@ static const struct ec_board_info board_info_strix_x870e_e_gaming_wifi = {
.family = family_amd_800_series,
};
+static const struct ec_board_info board_info_strix_x870e_h_gaming_wifi7 = {
+ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
+ SENSOR_TEMP_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR |
+ SENSOR_FAN_CPU_OPT,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
+ .family = family_amd_800_series,
+};
+
static const struct ec_board_info board_info_strix_z390_f_gaming = {
.sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
SENSOR_TEMP_T_SENSOR |
@@ -739,6 +791,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_pro_art_x670E_creator_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X870E-CREATOR WIFI",
&board_info_pro_art_x870E_creator_wifi),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS TRX50-SAGE WIFI",
+ &board_info_pro_ws_trx50_sage_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS WRX90E-SAGE SE",
&board_info_pro_ws_wrx90e_sage_se),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE",
@@ -771,6 +825,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_strix_b650e_i_gaming),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-I GAMING WIFI",
&board_info_strix_b850_i_gaming_wifi),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-I GAMING",
+ &board_info_strix_x470_i_gaming),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING",
&board_info_strix_x570_e_gaming),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING WIFI II",
@@ -783,10 +839,14 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_strix_x670e_e_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X670E-I GAMING WIFI",
&board_info_strix_x670e_i_gaming_wifi),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870-F GAMING WIFI",
+ &board_info_strix_x870_f_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870-I GAMING WIFI",
&board_info_strix_x870_i_gaming_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-E GAMING WIFI",
&board_info_strix_x870e_e_gaming_wifi),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X870E-H GAMING WIFI7",
+ &board_info_strix_x870e_h_gaming_wifi7),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING",
&board_info_strix_z390_f_gaming),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z490-F GAMING",
@@ -1274,6 +1334,9 @@ static int asus_ec_probe(struct platform_device *pdev)
case family_amd_800_series:
ec_data->sensors_info = sensors_family_amd_800;
break;
+ case family_amd_trx_50:
+ ec_data->sensors_info = sensors_family_amd_trx_50;
+ break;
case family_amd_wrx_90:
ec_data->sensors_info = sensors_family_amd_wrx_90;
break;
diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c
index e5e93a20723c..10a1f5aca988 100644
--- a/drivers/hwmon/asus_rog_ryujin.c
+++ b/drivers/hwmon/asus_rog_ryujin.c
@@ -81,10 +81,6 @@ static const char *const rog_ryujin_speed_label[] = {
struct rog_ryujin_data {
struct hid_device *hdev;
struct device *hwmon_dev;
- /* For locking access to buffer */
- struct mutex buffer_lock;
- /* For queueing multiple readers */
- struct mutex status_report_request_mutex;
/* For reinitializing the completions below */
spinlock_t status_report_request_lock;
struct completion cooler_status_received;
@@ -153,18 +149,10 @@ static umode_t rog_ryujin_is_visible(const void *data,
/* Writes the command to the device with the rest of the report filled with zeroes */
static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length)
{
- int ret;
-
- mutex_lock(&priv->buffer_lock);
-
memcpy_and_pad(priv->buffer, MAX_REPORT_LENGTH, cmd, cmd_length, 0x00);
- ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);
-
- mutex_unlock(&priv->buffer_lock);
- return ret;
+ return hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);
}
-/* Assumes priv->status_report_request_mutex is locked */
static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length,
struct completion *status_completion)
{
@@ -196,14 +184,11 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i
static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
{
- int ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
-
- if (ret < 0)
- return ret;
+ int ret;
if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) {
/* Data is up to date */
- goto unlock_and_return;
+ return 0;
}
/* Retrieve cooler status */
@@ -211,36 +196,30 @@ static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
rog_ryujin_execute_cmd(priv, get_cooler_status_cmd, GET_CMD_LENGTH,
&priv->cooler_status_received);
if (ret < 0)
- goto unlock_and_return;
+ return ret;
/* Retrieve controller status (speeds) */
ret =
rog_ryujin_execute_cmd(priv, get_controller_speed_cmd, GET_CMD_LENGTH,
&priv->controller_status_received);
if (ret < 0)
- goto unlock_and_return;
+ return ret;
/* Retrieve cooler duty */
ret =
rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
&priv->cooler_duty_received);
if (ret < 0)
- goto unlock_and_return;
+ return ret;
/* Retrieve controller duty */
ret =
rog_ryujin_execute_cmd(priv, get_controller_duty_cmd, GET_CMD_LENGTH,
&priv->controller_duty_received);
if (ret < 0)
- goto unlock_and_return;
-
- priv->updated = jiffies;
-
-unlock_and_return:
- mutex_unlock(&priv->status_report_request_mutex);
- if (ret < 0)
return ret;
+ priv->updated = jiffies;
return 0;
}
@@ -303,14 +282,11 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
* Retrieve cooler duty since both pump and internal fan are set
* together, then write back with one of them modified.
*/
- ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
- if (ret < 0)
- return ret;
ret =
rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
&priv->cooler_duty_received);
if (ret < 0)
- goto unlock_and_return;
+ return ret;
memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
@@ -329,11 +305,7 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val;
}
- ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
-unlock_and_return:
- mutex_unlock(&priv->status_report_request_mutex);
- if (ret < 0)
- return ret;
+ return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
} else {
/*
* Controller fan duty (channel == 2). No need to retrieve current
@@ -538,8 +510,6 @@ static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id
goto fail_and_close;
}
- mutex_init(&priv->status_report_request_mutex);
- mutex_init(&priv->buffer_lock);
spin_lock_init(&priv->status_report_request_lock);
init_completion(&priv->cooler_status_received);
init_completion(&priv->controller_status_received);
diff --git a/drivers/hwmon/cgbc-hwmon.c b/drivers/hwmon/cgbc-hwmon.c
index 772f44d56ccf..3aff4e092132 100644
--- a/drivers/hwmon/cgbc-hwmon.c
+++ b/drivers/hwmon/cgbc-hwmon.c
@@ -107,6 +107,9 @@ static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *
nb_sensors = data[0];
hwmon->sensors = devm_kzalloc(dev, sizeof(*hwmon->sensors) * nb_sensors, GFP_KERNEL);
+ if (!hwmon->sensors)
+ return -ENOMEM;
+
sensor = hwmon->sensors;
for (i = 0; i < nb_sensors; i++) {
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 9d071f7ca9d2..645b8c2e704e 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -81,7 +81,6 @@ struct cc2_data {
struct completion complete;
struct device *hwmon;
struct i2c_client *client;
- struct mutex dev_access_lock; /* device access lock */
struct regulator *regulator;
const char *name;
int irq_ready;
@@ -558,8 +557,6 @@ static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
{
struct cc2_data *data = dev_get_drvdata(dev);
- guard(mutex)(&data->dev_access_lock);
-
switch (type) {
case hwmon_temp:
return cc2_measurement(data, type, val);
@@ -600,8 +597,6 @@ static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
if (val < 0 || val > CC2_RH_MAX)
return -EINVAL;
- guard(mutex)(&data->dev_access_lock);
-
switch (attr) {
case hwmon_humidity_min:
cmd = CC2_W_ALARM_L_ON;
@@ -708,8 +703,6 @@ static int cc2_probe(struct i2c_client *client)
i2c_set_clientdata(client, data);
- mutex_init(&data->dev_access_lock);
-
data->client = client;
data->regulator = devm_regulator_get_exclusive(dev, "vdd");
diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c
index b7b911f8359c..b6e508e43fa1 100644
--- a/drivers/hwmon/corsair-cpro.c
+++ b/drivers/hwmon/corsair-cpro.c
@@ -40,7 +40,7 @@
#define CTL_GET_TMP 0x11 /*
* send: byte 1 is channel, rest zero
* rcv: returns temp for channel in centi-degree celsius
- * in bytes 1 and 2
+ * in bytes 1 and 2 as a two's complement value
* returns 0x11 in byte 0 if no sensor is connected
*/
#define CTL_GET_VOLT 0x12 /*
@@ -90,10 +90,10 @@ struct ccp_device {
u8 *cmd_buffer;
u8 *buffer;
int buffer_recv_size; /* number of received bytes in buffer */
- int target[6];
+ int target[NUM_FANS];
DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
DECLARE_BITMAP(fan_cnct, NUM_FANS);
- char fan_label[6][LABEL_LENGTH];
+ char fan_label[NUM_FANS][LABEL_LENGTH];
u8 firmware_ver[3];
u8 bootloader_ver[2];
};
@@ -258,7 +258,7 @@ static int ccp_read(struct device *dev, enum hwmon_sensor_types type,
ret = get_data(ccp, CTL_GET_TMP, channel, true);
if (ret < 0)
return ret;
- *val = ret * 10;
+ *val = (s16)ret * 10;
return 0;
default:
break;
diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c
index 6b5c8f200780..dddbd2463f8d 100644
--- a/drivers/hwmon/corsair-psu.c
+++ b/drivers/hwmon/corsair-psu.c
@@ -9,11 +9,9 @@
#include <linux/errno.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -124,7 +122,6 @@ struct corsairpsu_data {
struct device *hwmon_dev;
struct dentry *debugfs;
struct completion wait_completion;
- struct mutex lock; /* for locking access to cmd_buffer */
u8 *cmd_buffer;
char vendor[REPLY_SIZE];
char product[REPLY_SIZE];
@@ -220,7 +217,6 @@ static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, voi
{
int ret;
- mutex_lock(&priv->lock);
switch (cmd) {
case PSU_CMD_RAIL_VOLTS_HCRIT:
case PSU_CMD_RAIL_VOLTS_LCRIT:
@@ -230,17 +226,13 @@ static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, voi
case PSU_CMD_RAIL_WATTS:
ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL);
if (ret < 0)
- goto cmd_fail;
+ return ret;
break;
default:
break;
}
- ret = corsairpsu_usb_cmd(priv, 3, cmd, 0, data);
-
-cmd_fail:
- mutex_unlock(&priv->lock);
- return ret;
+ return corsairpsu_usb_cmd(priv, 3, cmd, 0, data);
}
static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, long *val)
@@ -797,7 +789,6 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id
priv->hdev = hdev;
hid_set_drvdata(hdev, priv);
- mutex_init(&priv->lock);
init_completion(&priv->wait_completion);
hid_device_io_start(hdev);
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index cbe1a74a3dee..683baf361c4c 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -1534,6 +1534,15 @@ static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = {
static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
{
+ .ident = "Dell G5 5505",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5505"),
+
+ },
+ .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
+ },
+ {
.ident = "Dell Latitude 5480",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c
index 291d91f68646..9c5b021aab86 100644
--- a/drivers/hwmon/drivetemp.c
+++ b/drivers/hwmon/drivetemp.c
@@ -102,7 +102,6 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_driver.h>
@@ -110,7 +109,6 @@
struct drivetemp_data {
struct list_head list; /* list of instantiated devices */
- struct mutex lock; /* protect data buffer accesses */
struct scsi_device *sdev; /* SCSI device */
struct device *dev; /* instantiating device */
struct device *hwdev; /* hardware monitoring device */
@@ -462,9 +460,7 @@ static int drivetemp_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_temp_input:
case hwmon_temp_lowest:
case hwmon_temp_highest:
- mutex_lock(&st->lock);
err = st->get_temp(st, attr, val);
- mutex_unlock(&st->lock);
break;
case hwmon_temp_lcrit:
*val = st->temp_lcrit;
@@ -566,7 +562,6 @@ static int drivetemp_add(struct device *dev)
st->sdev = sdev;
st->dev = dev;
- mutex_init(&st->lock);
if (drivetemp_identify(st)) {
err = -ENODEV;
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index eca33220d34a..ccce948a4306 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -17,7 +17,6 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/sysfs.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
@@ -30,7 +29,6 @@ enum emc1403_chip { emc1402, emc1403, emc1404, emc1428 };
struct thermal_data {
enum emc1403_chip chip;
struct regmap *regmap;
- struct mutex mutex;
};
static ssize_t power_state_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -268,8 +266,8 @@ static s8 emc1403_temp_regs_low[][4] = {
},
};
-static int __emc1403_get_temp(struct thermal_data *data, int channel,
- enum emc1403_reg_map map, long *val)
+static int emc1403_get_temp(struct thermal_data *data, int channel,
+ enum emc1403_reg_map map, long *val)
{
unsigned int regvalh;
unsigned int regvall = 0;
@@ -295,38 +293,23 @@ static int __emc1403_get_temp(struct thermal_data *data, int channel,
return 0;
}
-static int emc1403_get_temp(struct thermal_data *data, int channel,
- enum emc1403_reg_map map, long *val)
-{
- int ret;
-
- mutex_lock(&data->mutex);
- ret = __emc1403_get_temp(data, channel, map, val);
- mutex_unlock(&data->mutex);
-
- return ret;
-}
-
static int emc1403_get_hyst(struct thermal_data *data, int channel,
enum emc1403_reg_map map, long *val)
{
int hyst, ret;
long limit;
- mutex_lock(&data->mutex);
- ret = __emc1403_get_temp(data, channel, map, &limit);
+ ret = emc1403_get_temp(data, channel, map, &limit);
if (ret < 0)
- goto unlock;
+ return ret;
ret = regmap_read(data->regmap, 0x21, &hyst);
if (ret < 0)
- goto unlock;
+ return ret;
if (map == temp_min)
*val = limit + hyst * 1000;
else
*val = limit - hyst * 1000;
-unlock:
- mutex_unlock(&data->mutex);
- return ret;
+ return 0;
}
static int emc1403_temp_read(struct thermal_data *data, u32 attr, int channel, long *val)
@@ -451,20 +434,16 @@ static int emc1403_set_hyst(struct thermal_data *data, long val)
else
val = clamp_val(val, 0, 255000);
- mutex_lock(&data->mutex);
- ret = __emc1403_get_temp(data, 0, temp_crit, &limit);
+ ret = emc1403_get_temp(data, 0, temp_crit, &limit);
if (ret < 0)
- goto unlock;
+ return ret;
hyst = limit - val;
if (data->chip == emc1428)
hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 127);
else
hyst = clamp_val(DIV_ROUND_CLOSEST(hyst, 1000), 0, 255);
- ret = regmap_write(data->regmap, 0x21, hyst);
-unlock:
- mutex_unlock(&data->mutex);
- return ret;
+ return regmap_write(data->regmap, 0x21, hyst);
}
static int emc1403_set_temp(struct thermal_data *data, int channel,
@@ -478,7 +457,6 @@ static int emc1403_set_temp(struct thermal_data *data, int channel,
regh = emc1403_temp_regs[channel][map];
regl = emc1403_temp_regs_low[channel][map];
- mutex_lock(&data->mutex);
if (regl >= 0) {
if (data->chip == emc1428)
val = clamp_val(val, -128000, 127875);
@@ -487,7 +465,7 @@ static int emc1403_set_temp(struct thermal_data *data, int channel,
regval = DIV_ROUND_CLOSEST(val, 125);
ret = regmap_write(data->regmap, regh, (regval >> 3) & 0xff);
if (ret < 0)
- goto unlock;
+ return ret;
ret = regmap_write(data->regmap, regl, (regval & 0x07) << 5);
} else {
if (data->chip == emc1428)
@@ -497,8 +475,6 @@ static int emc1403_set_temp(struct thermal_data *data, int channel,
regval = DIV_ROUND_CLOSEST(val, 1000);
ret = regmap_write(data->regmap, regh, regval);
}
-unlock:
- mutex_unlock(&data->mutex);
return ret;
}
@@ -695,8 +671,6 @@ static int emc1403_probe(struct i2c_client *client)
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
- mutex_init(&data->mutex);
-
hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
client->name, data,
&emc1403_chip_info,
diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c
index 60eddc7b0270..9b8e925af030 100644
--- a/drivers/hwmon/emc2103.c
+++ b/drivers/hwmon/emc2103.c
@@ -277,8 +277,10 @@ fan1_input_show(struct device *dev, struct device_attribute *da, char *buf)
{
struct emc2103_data *data = emc2103_update_device(dev);
int rpm = 0;
+ mutex_lock(&data->update_lock);
if (data->fan_tach != 0)
rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach;
+ mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", rpm);
}
@@ -363,10 +365,12 @@ fan1_target_show(struct device *dev, struct device_attribute *da, char *buf)
struct emc2103_data *data = emc2103_update_device(dev);
int rpm = 0;
+ mutex_lock(&data->update_lock);
/* high byte of 0xff indicates disabled so return 0 */
if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0))
rpm = (FAN_RPM_FACTOR * data->fan_multiplier)
/ data->fan_target;
+ mutex_unlock(&data->update_lock);
return sprintf(buf, "%d\n", rpm);
}
diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c
index 8aeec16a7a90..08dcc6a7fb62 100644
--- a/drivers/hwmon/ftsteutates.c
+++ b/drivers/hwmon/ftsteutates.c
@@ -12,7 +12,6 @@
#include <linux/jiffies.h>
#include <linux/math.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/watchdog.h>
@@ -62,10 +61,6 @@ enum WATCHDOG_RESOLUTION {
struct fts_data {
struct i2c_client *client;
- /* update sensor data lock */
- struct mutex update_lock;
- /* read/write register lock */
- struct mutex access_lock;
unsigned long last_updated; /* in jiffies */
struct watchdog_device wdd;
enum WATCHDOG_RESOLUTION resolution;
@@ -98,21 +93,15 @@ static int fts_read_byte(struct i2c_client *client, unsigned short reg)
{
int ret;
unsigned char page = reg >> 8;
- struct fts_data *data = dev_get_drvdata(&client->dev);
-
- mutex_lock(&data->access_lock);
dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
if (ret < 0)
- goto error;
+ return ret;
reg &= 0xFF;
ret = i2c_smbus_read_byte_data(client, reg);
dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret);
-
-error:
- mutex_unlock(&data->access_lock);
return ret;
}
@@ -121,22 +110,16 @@ static int fts_write_byte(struct i2c_client *client, unsigned short reg,
{
int ret;
unsigned char page = reg >> 8;
- struct fts_data *data = dev_get_drvdata(&client->dev);
-
- mutex_lock(&data->access_lock);
dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
if (ret < 0)
- goto error;
+ return ret;
reg &= 0xFF;
dev_dbg(&client->dev,
"write - reg: 0x%.02x: val: 0x%.02x\n", reg, value);
ret = i2c_smbus_write_byte_data(client, reg, value);
-
-error:
- mutex_unlock(&data->access_lock);
return ret;
}
@@ -145,44 +128,40 @@ error:
/*****************************************************************************/
static int fts_update_device(struct fts_data *data)
{
- int i;
- int err = 0;
+ int i, err;
- mutex_lock(&data->update_lock);
if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid)
- goto exit;
+ return 0;
err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG);
if (err < 0)
- goto exit;
+ return err;
data->valid = !!(err & 0x02); /* Data not ready yet */
- if (unlikely(!data->valid)) {
- err = -EAGAIN;
- goto exit;
- }
+ if (unlikely(!data->valid))
+ return -EAGAIN;
err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG);
if (err < 0)
- goto exit;
+ return err;
data->fan_present = err;
err = fts_read_byte(data->client, FTS_FAN_EVENT_REG);
if (err < 0)
- goto exit;
+ return err;
data->fan_alarm = err;
for (i = 0; i < FTS_NO_FAN_SENSORS; i++) {
if (data->fan_present & BIT(i)) {
err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i));
if (err < 0)
- goto exit;
+ return err;
data->fan_input[i] = err;
err = fts_read_byte(data->client,
FTS_REG_FAN_SOURCE(i));
if (err < 0)
- goto exit;
+ return err;
data->fan_source[i] = err;
} else {
data->fan_input[i] = 0;
@@ -192,27 +171,24 @@ static int fts_update_device(struct fts_data *data)
err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG);
if (err < 0)
- goto exit;
+ return err;
data->temp_alarm = err;
for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) {
err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i));
if (err < 0)
- goto exit;
+ return err;
data->temp_input[i] = err;
}
for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) {
err = fts_read_byte(data->client, FTS_REG_VOLT(i));
if (err < 0)
- goto exit;
+ return err;
data->volt[i] = err;
}
data->last_updated = jiffies;
- err = 0;
-exit:
- mutex_unlock(&data->update_lock);
- return err;
+ return 0;
}
/*****************************************************************************/
@@ -470,18 +446,14 @@ static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
if (val)
return -EINVAL;
- mutex_lock(&data->update_lock);
ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(channel));
- if (ret >= 0)
- ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel),
- ret | 0x1);
- if (ret >= 0)
- data->valid = false;
-
- mutex_unlock(&data->update_lock);
if (ret < 0)
return ret;
-
+ ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel),
+ ret | 0x1);
+ if (ret < 0)
+ return ret;
+ data->valid = false;
return 0;
default:
break;
@@ -493,18 +465,14 @@ static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
if (val)
return -EINVAL;
- mutex_lock(&data->update_lock);
ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(channel));
- if (ret >= 0)
- ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel),
- ret | 0x1);
- if (ret >= 0)
- data->valid = false;
-
- mutex_unlock(&data->update_lock);
if (ret < 0)
return ret;
-
+ ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel),
+ ret | 0x1);
+ if (ret < 0)
+ return ret;
+ data->valid = false;
return 0;
default:
break;
@@ -648,8 +616,6 @@ static int fts_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
- mutex_init(&data->update_lock);
- mutex_init(&data->access_lock);
data->client = client;
dev_set_drvdata(&client->dev, data);
diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c
index 644dc3ca9df7..237f496c4862 100644
--- a/drivers/hwmon/gpd-fan.c
+++ b/drivers/hwmon/gpd-fan.c
@@ -12,9 +12,9 @@
* Copyright (c) 2024 Cryolitia PukNgae
*/
-#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/hwmon.h>
+#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -26,9 +26,6 @@
static char *gpd_fan_board = "";
module_param(gpd_fan_board, charp, 0444);
-// EC read/write locker, protecting a sequence of EC operations
-static DEFINE_MUTEX(gpd_fan_sequence_lock);
-
enum gpd_board {
win_mini,
win4_6800u,
@@ -276,31 +273,6 @@ static int gpd_generic_read_rpm(void)
return (u16)high << 8 | low;
}
-static void gpd_win4_init_ec(void)
-{
- u8 chip_id, chip_ver;
-
- gpd_ecram_read(0x2000, &chip_id);
-
- if (chip_id == 0x55) {
- gpd_ecram_read(0x1060, &chip_ver);
- gpd_ecram_write(0x1060, chip_ver | 0x80);
- }
-}
-
-static int gpd_win4_read_rpm(void)
-{
- int ret;
-
- ret = gpd_generic_read_rpm();
-
- if (ret == 0)
- // Re-init EC when speed is 0
- gpd_win4_init_ec();
-
- return ret;
-}
-
static int gpd_wm2_read_rpm(void)
{
for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET;
@@ -320,11 +292,10 @@ static int gpd_wm2_read_rpm(void)
static int gpd_read_rpm(void)
{
switch (gpd_driver_priv.drvdata->board) {
+ case win4_6800u:
case win_mini:
case duo:
return gpd_generic_read_rpm();
- case win4_6800u:
- return gpd_win4_read_rpm();
case win_max_2:
return gpd_wm2_read_rpm();
}
@@ -507,87 +478,60 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev,
{
int ret;
- ret = mutex_lock_interruptible(&gpd_fan_sequence_lock);
- if (ret)
- return ret;
-
if (type == hwmon_fan) {
if (attr == hwmon_fan_input) {
ret = gpd_read_rpm();
if (ret < 0)
- goto OUT;
+ return ret;
*val = ret;
- ret = 0;
- goto OUT;
+ return 0;
}
} else if (type == hwmon_pwm) {
switch (attr) {
case hwmon_pwm_enable:
*val = gpd_driver_priv.pwm_enable;
- ret = 0;
- goto OUT;
+ return 0;
case hwmon_pwm_input:
ret = gpd_read_pwm();
if (ret < 0)
- goto OUT;
+ return ret;
*val = ret;
- ret = 0;
- goto OUT;
+ return 0;
}
}
- ret = -EOPNOTSUPP;
-
-OUT:
- mutex_unlock(&gpd_fan_sequence_lock);
- return ret;
+ return -EOPNOTSUPP;
}
static int gpd_fan_hwmon_write(__always_unused struct device *dev,
enum hwmon_sensor_types type, u32 attr,
__always_unused int channel, long val)
{
- int ret;
-
- ret = mutex_lock_interruptible(&gpd_fan_sequence_lock);
- if (ret)
- return ret;
-
if (type == hwmon_pwm) {
switch (attr) {
case hwmon_pwm_enable:
- if (!in_range(val, 0, 3)) {
- ret = -EINVAL;
- goto OUT;
- }
+ if (!in_range(val, 0, 3))
+ return -EINVAL;
gpd_driver_priv.pwm_enable = val;
gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
- ret = 0;
- goto OUT;
+ return 0;
case hwmon_pwm_input:
- if (!in_range(val, 0, 256)) {
- ret = -ERANGE;
- goto OUT;
- }
+ if (!in_range(val, 0, 256))
+ return -EINVAL;
gpd_driver_priv.pwm_value = val;
- ret = gpd_write_pwm(val);
- goto OUT;
+ return gpd_write_pwm(val);
}
}
- ret = -EOPNOTSUPP;
-
-OUT:
- mutex_unlock(&gpd_fan_sequence_lock);
- return ret;
+ return -EOPNOTSUPP;
}
static const struct hwmon_ops gpd_fan_ops = {
@@ -607,6 +551,28 @@ static struct hwmon_chip_info gpd_fan_chip_info = {
.info = gpd_fan_hwmon_channel_info
};
+static void gpd_win4_init_ec(void)
+{
+ u8 chip_id, chip_ver;
+
+ gpd_ecram_read(0x2000, &chip_id);
+
+ if (chip_id == 0x55) {
+ gpd_ecram_read(0x1060, &chip_ver);
+ gpd_ecram_write(0x1060, chip_ver | 0x80);
+ }
+}
+
+static void gpd_init_ec(void)
+{
+ // The buggy firmware won't initialize EC properly on boot.
+ // Before its initialization, reading RPM will always return 0,
+ // and writing PWM will have no effect.
+ // Initialize it manually on driver load.
+ if (gpd_driver_priv.drvdata->board == win4_6800u)
+ gpd_win4_init_ec();
+}
+
static int gpd_fan_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -615,14 +581,14 @@ static int gpd_fan_probe(struct platform_device *pdev)
const struct device *hwdev;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (IS_ERR(res))
- return dev_err_probe(dev, PTR_ERR(res),
+ if (!res)
+ return dev_err_probe(dev, -EINVAL,
"Failed to get platform resource\n");
region = devm_request_region(dev, res->start,
resource_size(res), DRIVER_NAME);
- if (IS_ERR(region))
- return dev_err_probe(dev, PTR_ERR(region),
+ if (!region)
+ return dev_err_probe(dev, -EBUSY,
"Failed to request region\n");
hwdev = devm_hwmon_device_register_with_info(dev,
@@ -631,9 +597,11 @@ static int gpd_fan_probe(struct platform_device *pdev)
&gpd_fan_chip_info,
NULL);
if (IS_ERR(hwdev))
- return dev_err_probe(dev, PTR_ERR(region),
+ return dev_err_probe(dev, PTR_ERR(hwdev),
"Failed to register hwmon device\n");
+ gpd_init_ec();
+
return 0;
}
diff --git a/drivers/hwmon/hs3001.c b/drivers/hwmon/hs3001.c
index 24ed3fb9a43a..50c6c15f8b18 100644
--- a/drivers/hwmon/hs3001.c
+++ b/drivers/hwmon/hs3001.c
@@ -42,7 +42,6 @@
struct hs3001_data {
struct i2c_client *client;
- struct mutex i2c_lock; /* lock for sending i2c commands */
u32 wait_time; /* in us */
int temperature; /* in milli degree */
u32 humidity; /* in milli % */
@@ -112,12 +111,9 @@ static int hs3001_read(struct device *dev, enum hwmon_sensor_types type,
struct i2c_client *client = data->client;
int ret;
- mutex_lock(&data->i2c_lock);
ret = i2c_master_send(client, NULL, 0);
- if (ret < 0) {
- mutex_unlock(&data->i2c_lock);
+ if (ret < 0)
return ret;
- }
/*
* Sensor needs some time to process measurement depending on
@@ -126,8 +122,6 @@ static int hs3001_read(struct device *dev, enum hwmon_sensor_types type,
fsleep(data->wait_time);
ret = hs3001_data_fetch_command(client, data);
- mutex_unlock(&data->i2c_lock);
-
if (ret < 0)
return ret;
@@ -211,8 +205,6 @@ static int hs3001_probe(struct i2c_client *client)
data->wait_time = (HS3001_WAKEUP_TIME + HS3001_14BIT_RESOLUTION +
HS3001_14BIT_RESOLUTION);
- mutex_init(&data->i2c_lock);
-
hwmon_dev = devm_hwmon_device_register_with_info(dev,
client->name,
data,
diff --git a/drivers/hwmon/i5500_temp.c b/drivers/hwmon/i5500_temp.c
index 2a530da21949..bf006cb272b1 100644
--- a/drivers/hwmon/i5500_temp.c
+++ b/drivers/hwmon/i5500_temp.c
@@ -8,13 +8,10 @@
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/hwmon.h>
#include <linux/err.h>
-#include <linux/mutex.h>
/* Register definitions from datasheet */
#define REG_TSTHRCATA 0xE2
diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c
index 356d19b7675c..ff67b03189f7 100644
--- a/drivers/hwmon/ina238.c
+++ b/drivers/hwmon/ina238.c
@@ -117,7 +117,6 @@ struct ina238_config {
struct ina238_data {
const struct ina238_config *config;
struct i2c_client *client;
- struct mutex config_lock;
struct regmap *regmap;
u32 rshunt;
int gain;
@@ -607,31 +606,18 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
static int ina238_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
- struct ina238_data *data = dev_get_drvdata(dev);
- int err;
-
- mutex_lock(&data->config_lock);
-
switch (type) {
case hwmon_in:
- err = ina238_write_in(dev, attr, channel, val);
- break;
+ return ina238_write_in(dev, attr, channel, val);
case hwmon_curr:
- err = ina238_write_curr(dev, attr, val);
- break;
+ return ina238_write_curr(dev, attr, val);
case hwmon_power:
- err = ina238_write_power_max(dev, val);
- break;
+ return ina238_write_power_max(dev, val);
case hwmon_temp:
- err = ina238_write_temp_max(dev, val);
- break;
+ return ina238_write_temp_max(dev, val);
default:
- err = -EOPNOTSUPP;
- break;
+ return -EOPNOTSUPP;
}
-
- mutex_unlock(&data->config_lock);
- return err;
}
static umode_t ina238_is_visible(const void *drvdata,
@@ -757,8 +743,6 @@ static int ina238_probe(struct i2c_client *client)
/* set the device type */
data->config = &ina238_config[chip];
- mutex_init(&data->config_lock);
-
data->regmap = devm_regmap_init_i2c(client, &ina238_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "failed to allocate register map\n");
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index bc3c1f7314b3..69ac0468dee4 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -156,7 +156,6 @@ struct ina2xx_data {
long rshunt;
long current_lsb_uA;
long power_lsb_uW;
- struct mutex config_lock;
struct regmap *regmap;
struct i2c_client *client;
};
@@ -390,22 +389,19 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg,
int regval;
int ret;
- mutex_lock(&data->config_lock);
ret = regmap_read(regmap, INA226_MASK_ENABLE, &regval);
if (ret)
- goto abort;
+ return ret;
if (regval & mask) {
ret = regmap_read(regmap, INA226_ALERT_LIMIT, &regval);
if (ret)
- goto abort;
+ return ret;
*val = ina2xx_get_value(data, reg, regval);
} else {
*val = 0;
}
-abort:
- mutex_unlock(&data->config_lock);
- return ret;
+ return 0;
}
static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg, long val)
@@ -421,23 +417,20 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg,
* due to register write sequence. Then, only enable the alert
* if the value is non-zero.
*/
- mutex_lock(&data->config_lock);
ret = regmap_update_bits(regmap, INA226_MASK_ENABLE,
INA226_ALERT_CONFIG_MASK, 0);
if (ret < 0)
- goto abort;
+ return ret;
ret = regmap_write(regmap, INA226_ALERT_LIMIT,
ina226_alert_to_reg(data, reg, val));
if (ret < 0)
- goto abort;
+ return ret;
if (val)
- ret = regmap_update_bits(regmap, INA226_MASK_ENABLE,
- INA226_ALERT_CONFIG_MASK, mask);
-abort:
- mutex_unlock(&data->config_lock);
- return ret;
+ return regmap_update_bits(regmap, INA226_MASK_ENABLE,
+ INA226_ALERT_CONFIG_MASK, mask);
+ return 0;
}
static int ina2xx_chip_read(struct device *dev, u32 attr, long *val)
@@ -859,9 +852,9 @@ static ssize_t shunt_resistor_store(struct device *dev,
if (status < 0)
return status;
- mutex_lock(&data->config_lock);
+ hwmon_lock(dev);
status = ina2xx_set_shunt(data, val);
- mutex_unlock(&data->config_lock);
+ hwmon_unlock(dev);
if (status < 0)
return status;
return count;
@@ -951,7 +944,6 @@ static int ina2xx_probe(struct i2c_client *client)
data->client = client;
data->config = &ina2xx_config[chip];
data->chip = chip;
- mutex_init(&data->config_lock);
data->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
if (IS_ERR(data->regmap)) {
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index ce0e3f214f5b..5ecc68dcf169 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -11,7 +11,6 @@
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -115,7 +114,6 @@ struct ina3221_input {
* @regmap: Register map of the device
* @fields: Register fields of the device
* @inputs: Array of channel input source specific structures
- * @lock: mutex lock to serialize sysfs attribute accesses
* @reg_config: Register value of INA3221_CONFIG
* @summation_shunt_resistor: equivalent shunt resistor value for summation
* @summation_channel_control: Value written to SCC field in INA3221_MASK_ENABLE
@@ -126,7 +124,6 @@ struct ina3221_data {
struct regmap *regmap;
struct regmap_field *fields[F_MAX_FIELDS];
struct ina3221_input inputs[INA3221_NUM_CHANNELS];
- struct mutex lock;
u32 reg_config;
int summation_shunt_resistor;
u32 summation_channel_control;
@@ -530,11 +527,8 @@ fail:
static int ina3221_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
- struct ina3221_data *ina = dev_get_drvdata(dev);
int ret;
- mutex_lock(&ina->lock);
-
switch (type) {
case hwmon_chip:
ret = ina3221_read_chip(dev, attr, val);
@@ -550,20 +544,14 @@ static int ina3221_read(struct device *dev, enum hwmon_sensor_types type,
ret = -EOPNOTSUPP;
break;
}
-
- mutex_unlock(&ina->lock);
-
return ret;
}
static int ina3221_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
- struct ina3221_data *ina = dev_get_drvdata(dev);
int ret;
- mutex_lock(&ina->lock);
-
switch (type) {
case hwmon_chip:
ret = ina3221_write_chip(dev, attr, val);
@@ -579,9 +567,6 @@ static int ina3221_write(struct device *dev, enum hwmon_sensor_types type,
ret = -EOPNOTSUPP;
break;
}
-
- mutex_unlock(&ina->lock);
-
return ret;
}
@@ -886,7 +871,6 @@ static int ina3221_probe(struct i2c_client *client)
}
ina->pm_dev = dev;
- mutex_init(&ina->lock);
dev_set_drvdata(dev, ina);
/* Enable PM runtime -- status is suspended by default */
@@ -925,7 +909,6 @@ fail:
/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
for (i = 0; i < INA3221_NUM_CHANNELS; i++)
pm_runtime_put_noidle(ina->pm_dev);
- mutex_destroy(&ina->lock);
return ret;
}
@@ -941,8 +924,6 @@ static void ina3221_remove(struct i2c_client *client)
/* pm_runtime_put_noidle() will decrease the PM refcount until 0 */
for (i = 0; i < INA3221_NUM_CHANNELS; i++)
pm_runtime_put_noidle(ina->pm_dev);
-
- mutex_destroy(&ina->lock);
}
static int ina3221_suspend(struct device *dev)
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index 06f0ab2f52fa..6549dc543781 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -19,7 +19,6 @@
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
/* Addresses to scan */
@@ -179,7 +178,6 @@ static struct jc42_chips jc42_chips[] = {
/* Each client has this additional data */
struct jc42_data {
- struct mutex update_lock; /* protect register access */
struct regmap *regmap;
bool extended; /* true if extended range supported */
bool valid;
@@ -216,8 +214,6 @@ static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
unsigned int regval;
int ret, temp, hyst;
- mutex_lock(&data->update_lock);
-
switch (attr) {
case hwmon_temp_input:
ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
@@ -295,8 +291,6 @@ static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
break;
}
- mutex_unlock(&data->update_lock);
-
return ret;
}
@@ -308,8 +302,6 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
int diff, hyst;
int ret;
- mutex_lock(&data->update_lock);
-
switch (attr) {
case hwmon_temp_min:
ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER,
@@ -356,8 +348,6 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
break;
}
- mutex_unlock(&data->update_lock);
-
return ret;
}
@@ -498,7 +488,6 @@ static int jc42_probe(struct i2c_client *client)
return PTR_ERR(data->regmap);
i2c_set_clientdata(client, data);
- mutex_init(&data->update_lock);
ret = regmap_read(data->regmap, JC42_REG_CAP, &cap);
if (ret)
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index b98d5ec72c4f..a5d8f45b7881 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -31,9 +31,6 @@ static bool force;
module_param(force, bool, 0444);
MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
-/* Provide lock for writing to NB_SMU_IND_ADDR */
-static DEFINE_MUTEX(nb_smu_ind_mutex);
-
#ifndef PCI_DEVICE_ID_AMD_15H_M70H_NB_F3
#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F3 0x15b3
#endif
@@ -85,6 +82,12 @@ static DEFINE_MUTEX(nb_smu_ind_mutex);
#define AMD_I3255_STR "3255"
/*
+ * PCI Device IDs for AMD's Family 17h-based SOCs.
+ * Defining locally as IDs are not shared.
+ */
+#define PCI_DEVICE_ID_AMD_17H_M90H_DF_F3 0x1663
+
+/*
* PCI Device IDs for AMD's Family 1Ah-based SOCs.
* Defining locally as IDs are not shared.
*/
@@ -137,12 +140,10 @@ static void read_tempreg_pci(struct pci_dev *pdev, u32 *regval)
static void amd_nb_index_read(struct pci_dev *pdev, unsigned int devfn,
unsigned int base, int offset, u32 *val)
{
- mutex_lock(&nb_smu_ind_mutex);
pci_bus_write_config_dword(pdev->bus, devfn,
base, offset);
pci_bus_read_config_dword(pdev->bus, devfn,
base + 4, val);
- mutex_unlock(&nb_smu_ind_mutex);
}
static void read_htcreg_nb_f15(struct pci_dev *pdev, u32 *regval)
@@ -553,6 +554,7 @@ static const struct pci_device_id k10temp_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M40H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M90H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F3) },
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 8b53bb312069..9378a47bf5af 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -843,17 +843,18 @@ static int __init lm78_isa_found(unsigned short address)
}
}
-#define REALLY_SLOW_IO
/*
* We need the timeouts for at least some LM78-like
* chips. But only if we read 'undefined' registers.
+ * There used to be a "#define REALLY_SLOW_IO" to enforce that, but
+ * this has been without any effect since more than a decade, so it
+ * has been dropped.
*/
val = inb_p(address + 1);
if (inb_p(address + 2) != val
|| inb_p(address + 3) != val
|| inb_p(address + 7) != val)
goto release;
-#undef REALLY_SLOW_IO
/*
* We should be able to change the 7 LSB of the address port. The
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index d2d970e73c61..37bf2d1d3d09 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -116,8 +116,14 @@ static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C };
(((val) < 0 ? (val) - 500 : \
(val) + 500) / 1000))
-#define FAN_FROM_REG(reg, div) ((reg) == 255 || (reg) == 0 ? 0 : \
- (1350000 + (reg)*(div) / 2) / ((reg) * (div)))
+static int fan_from_reg(int reg, int div)
+{
+ if (reg == 255 || reg == 0)
+ return 0;
+
+ return (1350000 + reg * div / 2) / (reg * div);
+}
+
#define FAN_TO_REG(val, div) ((val) * (div) * 255 <= 1350000 ? 255 : \
(1350000 + (val)*(div) / 2) / ((val) * (div)))
@@ -465,7 +471,7 @@ static ssize_t fan_input_show(struct device *dev,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
- return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])));
}
@@ -475,7 +481,7 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
struct lm87_data *data = lm87_update_device(dev);
int nr = to_sensor_dev_attr(attr)->index;
- return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr])));
}
@@ -534,7 +540,7 @@ static ssize_t fan_div_store(struct device *dev,
return err;
mutex_lock(&data->update_lock);
- min = FAN_FROM_REG(data->fan_min[nr],
+ min = fan_from_reg(data->fan_min[nr],
FAN_DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index c1f528e292f3..3c10a5066b53 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -108,7 +108,6 @@
#include <linux/hwmon.h>
#include <linux/kstrtox.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -735,7 +734,6 @@ struct lm90_data {
struct hwmon_channel_info temp_info;
const struct hwmon_channel_info *info[3];
struct hwmon_chip_info chip;
- struct mutex update_lock;
struct delayed_work alert_work;
struct work_struct report_work;
bool valid; /* true if register values are valid */
@@ -1226,9 +1224,9 @@ static int lm90_update_alarms(struct lm90_data *data, bool force)
{
int err;
- mutex_lock(&data->update_lock);
+ hwmon_lock(data->hwmon_dev);
err = lm90_update_alarms_locked(data, force);
- mutex_unlock(&data->update_lock);
+ hwmon_unlock(data->hwmon_dev);
return err;
}
@@ -1519,9 +1517,7 @@ static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
int err;
u16 bit;
- mutex_lock(&data->update_lock);
err = lm90_update_device(dev);
- mutex_unlock(&data->update_lock);
if (err)
return err;
@@ -1590,11 +1586,9 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
struct lm90_data *data = dev_get_drvdata(dev);
int err;
- mutex_lock(&data->update_lock);
-
err = lm90_update_device(dev);
if (err)
- goto error;
+ return err;
switch (attr) {
case hwmon_temp_min:
@@ -1624,9 +1618,6 @@ static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
err = -EOPNOTSUPP;
break;
}
-error:
- mutex_unlock(&data->update_lock);
-
return err;
}
@@ -1662,9 +1653,7 @@ static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val)
struct lm90_data *data = dev_get_drvdata(dev);
int err;
- mutex_lock(&data->update_lock);
err = lm90_update_device(dev);
- mutex_unlock(&data->update_lock);
if (err)
return err;
@@ -1710,11 +1699,9 @@ static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val)
struct i2c_client *client = data->client;
int err;
- mutex_lock(&data->update_lock);
-
err = lm90_update_device(dev);
if (err)
- goto error;
+ return err;
switch (attr) {
case hwmon_chip_update_interval:
@@ -1728,9 +1715,6 @@ static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val)
err = -EOPNOTSUPP;
break;
}
-error:
- mutex_unlock(&data->update_lock);
-
return err;
}
@@ -2793,7 +2777,6 @@ static int lm90_probe(struct i2c_client *client)
data->client = client;
i2c_set_clientdata(client, data);
- mutex_init(&data->update_lock);
INIT_DELAYED_WORK(&data->alert_work, lm90_alert_work);
INIT_WORK(&data->report_work, lm90_report_alarms);
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 0be439b38ee1..91a6b7525bb6 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -32,7 +32,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -78,7 +77,6 @@ static inline u8 ALARMS_FROM_REG(s16 reg)
/* Client data (each client gets its own) */
struct lm92_data {
struct regmap *regmap;
- struct mutex update_lock;
int resolution;
};
@@ -199,15 +197,11 @@ static int lm92_temp_write(struct lm92_data *data, u32 attr, long val)
break;
case hwmon_temp_crit_hyst:
val = clamp_val(val, -120000, 220000);
- mutex_lock(&data->update_lock);
err = regmap_read(regmap, LM92_REG_TEMP_CRIT, &temp);
if (err)
- goto unlock;
+ return err;
val = TEMP_TO_REG(TEMP_FROM_REG(temp) - val, data->resolution);
- err = regmap_write(regmap, LM92_REG_TEMP_HYST, val);
-unlock:
- mutex_unlock(&data->update_lock);
- return err;
+ return regmap_write(regmap, LM92_REG_TEMP_HYST, val);
default:
return -EOPNOTSUPP;
}
@@ -396,7 +390,6 @@ static int lm92_probe(struct i2c_client *client)
data->regmap = regmap;
data->resolution = (unsigned long)i2c_get_match_data(client);
- mutex_init(&data->update_lock);
/* Initialize the chipset */
err = lm92_init_client(regmap);
diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c
index 7da6c8f07332..387b3ba81dbf 100644
--- a/drivers/hwmon/lm95234.c
+++ b/drivers/hwmon/lm95234.c
@@ -14,7 +14,6 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
@@ -54,7 +53,6 @@ static const unsigned short normal_i2c[] = {
/* Client data (each client gets its own) */
struct lm95234_data {
struct regmap *regmap;
- struct mutex update_lock;
enum chips type;
};
@@ -107,19 +105,14 @@ static ssize_t lm95234_hyst_set(struct lm95234_data *data, long val)
u32 tcrit;
int ret;
- mutex_lock(&data->update_lock);
-
ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(0), &tcrit);
if (ret)
- goto unlock;
+ return ret;
val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000);
val = clamp_val((int)tcrit - val, 0, 31);
- ret = regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val);
-unlock:
- mutex_unlock(&data->update_lock);
- return ret;
+ return regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val);
}
static int lm95234_crit_reg(int channel)
@@ -526,7 +519,6 @@ static int lm95234_probe(struct i2c_client *client)
return PTR_ERR(regmap);
data->regmap = regmap;
- mutex_init(&data->update_lock);
/* Initialize the LM95234 chip */
err = lm95234_init_client(dev, regmap);
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index cad0a0ff8416..456381b0938e 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -15,7 +15,6 @@
#include <linux/jiffies.h>
#include <linux/hwmon.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/slab.h>
#define DEVNAME "lm95241"
@@ -75,7 +74,6 @@ static const u8 lm95241_reg_address[] = {
/* Client data (each client gets its own) */
struct lm95241_data {
struct i2c_client *client;
- struct mutex update_lock;
unsigned long last_updated; /* in jiffies */
unsigned long interval; /* in milli-seconds */
bool valid; /* false until following fields are valid */
@@ -102,8 +100,6 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
struct lm95241_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- mutex_lock(&data->update_lock);
-
if (time_after(jiffies, data->last_updated
+ msecs_to_jiffies(data->interval)) ||
!data->valid) {
@@ -120,9 +116,6 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
data->last_updated = jiffies;
data->valid = true;
}
-
- mutex_unlock(&data->update_lock);
-
return data;
}
@@ -204,8 +197,6 @@ static int lm95241_write_chip(struct device *dev, u32 attr, int channel,
u8 config;
int ret;
- mutex_lock(&data->update_lock);
-
switch (attr) {
case hwmon_chip_update_interval:
config = data->config & ~CFG_CRMASK;
@@ -231,7 +222,6 @@ static int lm95241_write_chip(struct device *dev, u32 attr, int channel,
ret = -EOPNOTSUPP;
break;
}
- mutex_unlock(&data->update_lock);
return ret;
}
@@ -242,8 +232,6 @@ static int lm95241_write_temp(struct device *dev, u32 attr, int channel,
struct i2c_client *client = data->client;
int ret;
- mutex_lock(&data->update_lock);
-
switch (attr) {
case hwmon_temp_min:
if (channel == 1) {
@@ -313,9 +301,6 @@ static int lm95241_write_temp(struct device *dev, u32 attr, int channel,
ret = -EOPNOTSUPP;
break;
}
-
- mutex_unlock(&data->update_lock);
-
return ret;
}
@@ -443,7 +428,6 @@ static int lm95241_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->update_lock);
/* Initialize the LM95241 chip */
lm95241_init_client(client, data);
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
index 3bdc30530847..9ed300c6b5f7 100644
--- a/drivers/hwmon/lm95245.c
+++ b/drivers/hwmon/lm95245.c
@@ -13,7 +13,6 @@
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -86,7 +85,6 @@ static const unsigned short normal_i2c[] = {
/* Client data (each client gets its own) */
struct lm95245_data {
struct regmap *regmap;
- struct mutex update_lock;
int interval; /* in msecs */
};
@@ -279,20 +277,16 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
ret = regmap_write(regmap, reg, val);
return ret;
case hwmon_temp_crit_hyst:
- mutex_lock(&data->update_lock);
ret = regmap_read(regmap, LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
&regval);
- if (ret < 0) {
- mutex_unlock(&data->update_lock);
+ if (ret < 0)
return ret;
- }
/* Clamp to reasonable range to prevent overflow */
val = clamp_val(val, -1000000, 1000000);
val = regval - val / 1000;
val = clamp_val(val, 0, 31);
ret = regmap_write(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
val);
- mutex_unlock(&data->update_lock);
return ret;
case hwmon_temp_offset:
val = clamp_val(val, -128000, 127875);
@@ -332,14 +326,10 @@ static int lm95245_write_chip(struct device *dev, u32 attr, int channel,
long val)
{
struct lm95245_data *data = dev_get_drvdata(dev);
- int ret;
switch (attr) {
case hwmon_chip_update_interval:
- mutex_lock(&data->update_lock);
- ret = lm95245_set_conversion_rate(data, val);
- mutex_unlock(&data->update_lock);
- return ret;
+ return lm95245_set_conversion_rate(data, val);
default:
return -EOPNOTSUPP;
}
@@ -542,8 +532,6 @@ static int lm95245_probe(struct i2c_client *client)
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
- mutex_init(&data->update_lock);
-
/* Initialize the LM95245 chip */
ret = lm95245_init_client(data);
if (ret < 0)
diff --git a/drivers/hwmon/lochnagar-hwmon.c b/drivers/hwmon/lochnagar-hwmon.c
index 5202dddfd61e..c1ba72f6132e 100644
--- a/drivers/hwmon/lochnagar-hwmon.c
+++ b/drivers/hwmon/lochnagar-hwmon.c
@@ -10,7 +10,6 @@
#include <linux/delay.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/math64.h>
#include <linux/mfd/lochnagar.h>
#include <linux/mfd/lochnagar2_regs.h>
@@ -42,9 +41,6 @@ struct lochnagar_hwmon {
struct regmap *regmap;
long power_nsamples[ARRAY_SIZE(lochnagar_chan_names)];
-
- /* Lock to ensure only a single sensor is read at a time */
- struct mutex sensor_lock;
};
enum lochnagar_measure_mode {
@@ -178,26 +174,20 @@ static int read_sensor(struct device *dev, int chan,
u32 data;
int ret;
- mutex_lock(&priv->sensor_lock);
-
ret = do_measurement(regmap, chan, mode, nsamples);
if (ret < 0) {
dev_err(dev, "Failed to perform measurement: %d\n", ret);
- goto error;
+ return ret;
}
ret = request_data(regmap, chan, &data);
if (ret < 0) {
dev_err(dev, "Failed to read measurement: %d\n", ret);
- goto error;
+ return ret;
}
*val = float_to_long(data, precision);
-
-error:
- mutex_unlock(&priv->sensor_lock);
-
- return ret;
+ return 0;
}
static int read_power(struct device *dev, int chan, long *val)
@@ -378,8 +368,6 @@ static int lochnagar_hwmon_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- mutex_init(&priv->sensor_lock);
-
priv->regmap = dev_get_regmap(dev->parent, NULL);
if (!priv->regmap) {
dev_err(dev, "No register map found\n");
diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c
index 244839167e51..ad7120d1e469 100644
--- a/drivers/hwmon/ltc2947-core.c
+++ b/drivers/hwmon/ltc2947-core.c
@@ -9,8 +9,8 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/module.h>
+#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regmap.h>
@@ -120,12 +120,6 @@
struct ltc2947_data {
struct regmap *map;
struct device *dev;
- /*
- * The mutex is needed because the device has 2 memory pages. When
- * reading/writing the correct page needs to be set so that, the
- * complete sequence select_page->read/write needs to be protected.
- */
- struct mutex lock;
u32 lsb_energy;
bool gpio_out;
};
@@ -181,13 +175,9 @@ static int ltc2947_val_read(struct ltc2947_data *st, const u8 reg,
int ret;
u64 __val = 0;
- mutex_lock(&st->lock);
-
ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
- if (ret) {
- mutex_unlock(&st->lock);
+ if (ret)
return ret;
- }
dev_dbg(st->dev, "Read val, reg:%02X, p:%d sz:%zu\n", reg, page,
size);
@@ -207,8 +197,6 @@ static int ltc2947_val_read(struct ltc2947_data *st, const u8 reg,
break;
}
- mutex_unlock(&st->lock);
-
if (ret)
return ret;
@@ -242,13 +230,10 @@ static int ltc2947_val_write(struct ltc2947_data *st, const u8 reg,
{
int ret;
- mutex_lock(&st->lock);
/* set device on correct page */
ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, page);
- if (ret) {
- mutex_unlock(&st->lock);
+ if (ret)
return ret;
- }
dev_dbg(st->dev, "Write val, r:%02X, p:%d, sz:%zu, val:%016llX\n",
reg, page, size, val);
@@ -265,8 +250,6 @@ static int ltc2947_val_write(struct ltc2947_data *st, const u8 reg,
break;
}
- mutex_unlock(&st->lock);
-
return ret;
}
@@ -295,11 +278,9 @@ static int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg,
memset(alarms, 0, sizeof(alarms));
- mutex_lock(&st->lock);
-
ret = regmap_write(st->map, LTC2947_REG_PAGE_CTRL, LTC2947_PAGE0);
if (ret)
- goto unlock;
+ return ret;
dev_dbg(st->dev, "Read alarm, reg:%02X, mask:%02X\n", reg, mask);
/*
@@ -310,31 +291,11 @@ static int ltc2947_alarm_read(struct ltc2947_data *st, const u8 reg,
ret = regmap_bulk_read(st->map, LTC2947_REG_STATUS, alarms,
sizeof(alarms));
if (ret)
- goto unlock;
+ return ret;
/* get the alarm */
*val = !!(alarms[offset] & mask);
-unlock:
- mutex_unlock(&st->lock);
- return ret;
-}
-
-static ssize_t ltc2947_show_value(struct device *dev,
- struct device_attribute *da, char *buf)
-{
- struct ltc2947_data *st = dev_get_drvdata(dev);
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- int ret;
- s64 val = 0;
-
- ret = ltc2947_val_read(st, attr->index, LTC2947_PAGE0, 6, &val);
- if (ret)
- return ret;
-
- /* value in microJoule. st->lsb_energy was multiplied by 10E9 */
- val = div_s64(val * st->lsb_energy, 1000);
-
- return sprintf(buf, "%lld\n", val);
+ return 0;
}
static int ltc2947_read_temp(struct device *dev, const u32 attr, long *val,
@@ -588,6 +549,23 @@ static int ltc2947_read_in(struct device *dev, const u32 attr, long *val,
return 0;
}
+static int ltc2947_read_energy(struct device *dev, s64 *val, const int channel)
+{
+ int reg = channel ? LTC2947_REG_ENERGY2 : LTC2947_REG_ENERGY1;
+ struct ltc2947_data *st = dev_get_drvdata(dev);
+ s64 __val = 0;
+ int ret;
+
+ ret = ltc2947_val_read(st, reg, LTC2947_PAGE0, 6, &__val);
+ if (ret)
+ return ret;
+
+ /* value in microJoule. st->lsb_energy was multiplied by 10E9 */
+ *val = DIV_S64_ROUND_CLOSEST(__val * st->lsb_energy, 1000);
+
+ return 0;
+}
+
static int ltc2947_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
@@ -600,6 +578,8 @@ static int ltc2947_read(struct device *dev, enum hwmon_sensor_types type,
return ltc2947_read_power(dev, attr, val);
case hwmon_temp:
return ltc2947_read_temp(dev, attr, val, channel);
+ case hwmon_energy64:
+ return ltc2947_read_energy(dev, (s64 *)val, channel);
default:
return -ENOTSUPP;
}
@@ -897,6 +877,8 @@ static umode_t ltc2947_is_visible(const void *data,
return ltc2947_power_is_visible(attr);
case hwmon_temp:
return ltc2947_temp_is_visible(attr);
+ case hwmon_energy64:
+ return 0444;
default:
return 0;
}
@@ -929,6 +911,9 @@ static const struct hwmon_channel_info * const ltc2947_info[] = {
HWMON_T_LABEL,
HWMON_T_MAX_ALARM | HWMON_T_MIN_ALARM | HWMON_T_MAX |
HWMON_T_MIN | HWMON_T_LABEL),
+ HWMON_CHANNEL_INFO(energy64,
+ HWMON_E_INPUT,
+ HWMON_E_INPUT),
NULL
};
@@ -944,19 +929,6 @@ static const struct hwmon_chip_info ltc2947_chip_info = {
.info = ltc2947_info,
};
-/* energy attributes are 6bytes wide so we need u64 */
-static SENSOR_DEVICE_ATTR(energy1_input, 0444, ltc2947_show_value, NULL,
- LTC2947_REG_ENERGY1);
-static SENSOR_DEVICE_ATTR(energy2_input, 0444, ltc2947_show_value, NULL,
- LTC2947_REG_ENERGY2);
-
-static struct attribute *ltc2947_attrs[] = {
- &sensor_dev_attr_energy1_input.dev_attr.attr,
- &sensor_dev_attr_energy2_input.dev_attr.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(ltc2947);
-
static int ltc2947_setup(struct ltc2947_data *st)
{
int ret;
@@ -1107,15 +1079,13 @@ int ltc2947_core_probe(struct regmap *map, const char *name)
st->map = map;
st->dev = dev;
dev_set_drvdata(dev, st);
- mutex_init(&st->lock);
ret = ltc2947_setup(st);
if (ret)
return ret;
hwmon = devm_hwmon_device_register_with_info(dev, name, st,
- &ltc2947_chip_info,
- ltc2947_groups);
+ &ltc2947_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon);
}
EXPORT_SYMBOL_GPL(ltc2947_core_probe);
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index 14593bc81e85..e8131a48bda7 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -18,7 +18,6 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/platform_data/ltc4245.h>
@@ -51,7 +50,6 @@ enum ltc4245_cmd {
struct ltc4245_data {
struct i2c_client *client;
- struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
@@ -132,10 +130,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
s32 val;
int i;
- mutex_lock(&data->update_lock);
-
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
-
/* Read control registers -- 0x00 to 0x07 */
for (i = 0; i < ARRAY_SIZE(data->cregs); i++) {
val = i2c_smbus_read_byte_data(client, i);
@@ -161,8 +156,6 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
data->valid = true;
}
- mutex_unlock(&data->update_lock);
-
return data;
}
@@ -454,7 +447,6 @@ static int ltc4245_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->update_lock);
data->use_extra_gpios = ltc4245_use_extra_gpios(client);
/* Initialize the LTC4245 chip */
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index 1d664a2d7b3c..b9cad89f2cd9 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -12,13 +12,11 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/property.h>
#include <linux/string.h>
@@ -132,8 +130,6 @@ struct ltc4282_cache {
struct ltc4282_state {
struct regmap *map;
- /* Protect against multiple accesses to the device registers */
- struct mutex lock;
struct clk_hw clk_hw;
/*
* Used to cache values for VDD/VSOURCE depending which will be used
@@ -282,14 +278,12 @@ static int __ltc4282_read_alarm(struct ltc4282_state *st, u32 reg, u32 mask,
static int ltc4282_read_alarm(struct ltc4282_state *st, u32 reg, u32 mask,
long *val)
{
- guard(mutex)(&st->lock);
return __ltc4282_read_alarm(st, reg, mask, val);
}
static int ltc4282_vdd_source_read_in(struct ltc4282_state *st, u32 channel,
long *val)
{
- guard(mutex)(&st->lock);
if (!st->in0_1_cache[channel].en)
return -ENODATA;
@@ -301,7 +295,6 @@ static int ltc4282_vdd_source_read_hist(struct ltc4282_state *st, u32 reg,
{
int ret;
- guard(mutex)(&st->lock);
if (!st->in0_1_cache[channel].en) {
*val = *cached;
return 0;
@@ -318,7 +311,6 @@ static int ltc4282_vdd_source_read_hist(struct ltc4282_state *st, u32 reg,
static int ltc4282_vdd_source_read_lim(struct ltc4282_state *st, u32 reg,
u32 channel, u32 *cached, long *val)
{
- guard(mutex)(&st->lock);
if (!st->in0_1_cache[channel].en)
return ltc4282_read_voltage_byte_cached(st, reg, st->vfs_out,
val, cached);
@@ -329,7 +321,6 @@ static int ltc4282_vdd_source_read_lim(struct ltc4282_state *st, u32 reg,
static int ltc4282_vdd_source_read_alm(struct ltc4282_state *st, u32 mask,
u32 channel, long *val)
{
- guard(mutex)(&st->lock);
if (!st->in0_1_cache[channel].en) {
/*
* Do this otherwise alarms can get confused because we clear
@@ -413,9 +404,7 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val,
channel,
&st->in0_1_cache[channel].in_min_raw, val);
case hwmon_in_enable:
- scoped_guard(mutex, &st->lock) {
- *val = st->in0_1_cache[channel].en;
- }
+ *val = st->in0_1_cache[channel].en;
return 0;
case hwmon_in_fault:
/*
@@ -541,7 +530,7 @@ static int ltc4282_read_power_byte(const struct ltc4282_state *st, u32 reg,
return 0;
}
-static int ltc4282_read_energy(const struct ltc4282_state *st, u64 *val)
+static int ltc4282_read_energy(const struct ltc4282_state *st, s64 *val)
{
u64 temp, energy;
__be64 raw;
@@ -613,10 +602,12 @@ static int ltc4282_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_power:
return ltc4282_read_power(st, attr, val);
case hwmon_energy:
- scoped_guard(mutex, &st->lock) {
- *val = st->energy_en;
- }
+ *val = st->energy_en;
return 0;
+ case hwmon_energy64:
+ if (st->energy_en)
+ return ltc4282_read_energy(st, (s64 *)val);
+ return -ENODATA;
default:
return -EOPNOTSUPP;
}
@@ -683,7 +674,6 @@ static int __ltc4282_in_write_history(const struct ltc4282_state *st, u32 reg,
static int ltc4282_in_write_history(struct ltc4282_state *st, u32 reg,
long lowest, long highest, u32 fs)
{
- guard(mutex)(&st->lock);
return __ltc4282_in_write_history(st, reg, lowest, highest, fs);
}
@@ -691,8 +681,6 @@ static int ltc4282_power_reset_hist(struct ltc4282_state *st)
{
int ret;
- guard(mutex)(&st->lock);
-
ret = ltc4282_write_power_word(st, LTC4282_POWER_LOWEST,
st->power_max);
if (ret)
@@ -798,7 +786,6 @@ static int ltc4282_vdd_source_write_lim(struct ltc4282_state *st, u32 reg,
{
int ret;
- guard(mutex)(&st->lock);
if (st->in0_1_cache[channel].en)
ret = ltc4282_write_voltage_byte(st, reg, st->vfs_out, val);
else
@@ -816,7 +803,6 @@ static int ltc4282_vdd_source_reset_hist(struct ltc4282_state *st, int channel)
if (channel == LTC4282_CHAN_VDD)
lowest = st->vdd;
- guard(mutex)(&st->lock);
if (st->in0_1_cache[channel].en) {
ret = __ltc4282_in_write_history(st, LTC4282_VSOURCE_LOWEST,
lowest, 0, st->vfs_out);
@@ -856,7 +842,6 @@ static int ltc4282_vdd_source_enable(struct ltc4282_state *st, int channel,
int ret, other_chan = ~channel & 0x1;
u8 __val = val;
- guard(mutex)(&st->lock);
if (st->in0_1_cache[channel].en == !!val)
return 0;
@@ -933,8 +918,6 @@ static int ltc4282_curr_reset_hist(struct ltc4282_state *st)
{
int ret;
- guard(mutex)(&st->lock);
-
ret = __ltc4282_in_write_history(st, LTC4282_VSENSE_LOWEST,
st->vsense_max, 0, 40 * MILLI);
if (ret)
@@ -969,7 +952,6 @@ static int ltc4282_energy_enable_set(struct ltc4282_state *st, long val)
{
int ret;
- guard(mutex)(&st->lock);
/* setting the bit halts the meter */
ret = regmap_update_bits(st->map, LTC4282_ADC_CTRL,
LTC4282_METER_HALT_MASK,
@@ -1078,6 +1060,9 @@ static umode_t ltc4282_is_visible(const void *data,
case hwmon_energy:
/* hwmon_energy_enable */
return 0644;
+ case hwmon_energy64:
+ /* hwmon_energy_input */
+ return 0444;
default:
return 0;
}
@@ -1106,24 +1091,6 @@ static int ltc4282_read_labels(struct device *dev,
}
}
-static ssize_t ltc4282_energy_show(struct device *dev,
- struct device_attribute *da, char *buf)
-{
- struct ltc4282_state *st = dev_get_drvdata(dev);
- u64 energy;
- int ret;
-
- guard(mutex)(&st->lock);
- if (!st->energy_en)
- return -ENODATA;
-
- ret = ltc4282_read_energy(st, &energy);
- if (ret < 0)
- return ret;
-
- return sysfs_emit(buf, "%llu\n", energy);
-}
-
static const struct clk_ops ltc4282_ops = {
.recalc_rate = ltc4282_recalc_rate,
.determine_rate = ltc4282_determine_rate,
@@ -1588,6 +1555,8 @@ static const struct hwmon_channel_info * const ltc4282_info[] = {
HWMON_P_RESET_HISTORY | HWMON_P_LABEL),
HWMON_CHANNEL_INFO(energy,
HWMON_E_ENABLE),
+ HWMON_CHANNEL_INFO(energy64,
+ HWMON_E_INPUT),
NULL
};
@@ -1603,15 +1572,6 @@ static const struct hwmon_chip_info ltc4282_chip_info = {
.info = ltc4282_info,
};
-/* energy attributes are 6bytes wide so we need u64 */
-static SENSOR_DEVICE_ATTR_RO(energy1_input, ltc4282_energy, 0);
-
-static struct attribute *ltc4282_attrs[] = {
- &sensor_dev_attr_energy1_input.dev_attr.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(ltc4282);
-
static int ltc4282_show_fault_log(void *arg, u64 *val, u32 mask)
{
struct ltc4282_state *st = arg;
@@ -1716,10 +1676,8 @@ static int ltc4282_probe(struct i2c_client *i2c)
if (ret)
return ret;
- mutex_init(&st->lock);
hwmon = devm_hwmon_device_register_with_info(dev, "ltc4282", st,
- &ltc4282_chip_info,
- ltc4282_groups);
+ &ltc4282_chip_info, NULL);
if (IS_ERR(hwmon))
return PTR_ERR(hwmon);
diff --git a/drivers/hwmon/macsmc-hwmon.c b/drivers/hwmon/macsmc-hwmon.c
new file mode 100644
index 000000000000..1c0bbec7e8eb
--- /dev/null
+++ b/drivers/hwmon/macsmc-hwmon.c
@@ -0,0 +1,851 @@
+// SPDX-License-Identifier: GPL-2.0-only OR MIT
+/*
+ * Apple SMC hwmon driver for Apple Silicon platforms
+ *
+ * The System Management Controller on Apple Silicon devices is responsible for
+ * measuring data from sensors across the SoC and machine. These include power,
+ * temperature, voltage and current sensors. Some "sensors" actually expose
+ * derived values. An example of this is the key PHPC, which is an estimate
+ * of the heat energy being dissipated by the SoC.
+ *
+ * While each SoC only has one SMC variant, each platform exposes a different
+ * set of sensors. For example, M1 MacBooks expose battery telemetry sensors
+ * which are not present on the M1 Mac mini. For this reason, the available
+ * sensors for a given platform are described in the device tree in a child
+ * node of the SMC device. We must walk this list of available sensors and
+ * populate the required hwmon data structures at runtime.
+ *
+ * Originally based on a concept by Jean-Francois Bortolotti <jeff@borto.fr>
+ *
+ * Copyright The Asahi Linux Contributors
+ */
+
+#include <linux/bitfield.h>
+#include <linux/hwmon.h>
+#include <linux/mfd/macsmc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#define MAX_LABEL_LENGTH 32
+
+/* Temperature, voltage, current, power, fan(s) */
+#define NUM_SENSOR_TYPES 5
+
+#define FLT_EXP_BIAS 127
+#define FLT_EXP_MASK GENMASK(30, 23)
+#define FLT_MANT_BIAS 23
+#define FLT_MANT_MASK GENMASK(22, 0)
+#define FLT_SIGN_MASK BIT(31)
+
+static bool fan_control;
+module_param_unsafe(fan_control, bool, 0644);
+MODULE_PARM_DESC(fan_control,
+ "Override the SMC to set your own fan speeds on supported machines");
+
+struct macsmc_hwmon_sensor {
+ struct apple_smc_key_info info;
+ smc_key macsmc_key;
+ char label[MAX_LABEL_LENGTH];
+ u32 attrs;
+};
+
+struct macsmc_hwmon_fan {
+ struct macsmc_hwmon_sensor now;
+ struct macsmc_hwmon_sensor min;
+ struct macsmc_hwmon_sensor max;
+ struct macsmc_hwmon_sensor set;
+ struct macsmc_hwmon_sensor mode;
+ char label[MAX_LABEL_LENGTH];
+ u32 attrs;
+ bool manual;
+};
+
+struct macsmc_hwmon_sensors {
+ struct hwmon_channel_info channel_info;
+ struct macsmc_hwmon_sensor *sensors;
+ u32 count;
+};
+
+struct macsmc_hwmon_fans {
+ struct hwmon_channel_info channel_info;
+ struct macsmc_hwmon_fan *fans;
+ u32 count;
+};
+
+struct macsmc_hwmon {
+ struct device *dev;
+ struct apple_smc *smc;
+ struct device *hwmon_dev;
+ struct hwmon_chip_info chip_info;
+ /* Chip + sensor types + NULL */
+ const struct hwmon_channel_info *channel_infos[1 + NUM_SENSOR_TYPES + 1];
+ struct macsmc_hwmon_sensors temp;
+ struct macsmc_hwmon_sensors volt;
+ struct macsmc_hwmon_sensors curr;
+ struct macsmc_hwmon_sensors power;
+ struct macsmc_hwmon_fans fan;
+};
+
+static int macsmc_hwmon_read_label(struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ struct macsmc_hwmon *hwmon = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ *str = hwmon->temp.sensors[channel].label;
+ break;
+ case hwmon_in:
+ *str = hwmon->volt.sensors[channel].label;
+ break;
+ case hwmon_curr:
+ *str = hwmon->curr.sensors[channel].label;
+ break;
+ case hwmon_power:
+ *str = hwmon->power.sensors[channel].label;
+ break;
+ case hwmon_fan:
+ *str = hwmon->fan.fans[channel].label;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*
+ * A number of sensors report data in a 48.16 fixed-point decimal format that is
+ * not used by any other function of the SMC.
+ */
+static int macsmc_hwmon_read_ioft_scaled(struct apple_smc *smc, smc_key key,
+ u64 *p, int scale)
+{
+ u64 val;
+ int ret;
+
+ ret = apple_smc_read_u64(smc, key, &val);
+ if (ret < 0)
+ return ret;
+
+ *p = mult_frac(val, scale, 65536);
+
+ return 0;
+}
+
+/*
+ * Many sensors report their data as IEEE-754 floats. No other SMC function uses
+ * them.
+ */
+static int macsmc_hwmon_read_f32_scaled(struct apple_smc *smc, smc_key key,
+ int *p, int scale)
+{
+ u32 fval;
+ u64 val;
+ int ret, exp;
+
+ ret = apple_smc_read_u32(smc, key, &fval);
+ if (ret < 0)
+ return ret;
+
+ val = ((u64)((fval & FLT_MANT_MASK) | BIT(23)));
+ exp = ((fval >> 23) & 0xff) - FLT_EXP_BIAS - FLT_MANT_BIAS;
+
+ /* We never have negatively scaled SMC floats */
+ val *= scale;
+
+ if (exp > 63)
+ val = U64_MAX;
+ else if (exp < -63)
+ val = 0;
+ else if (exp < 0)
+ val >>= -exp;
+ else if (exp != 0 && (val & ~((1UL << (64 - exp)) - 1))) /* overflow */
+ val = U64_MAX;
+ else
+ val <<= exp;
+
+ if (fval & FLT_SIGN_MASK) {
+ if (val > (-(s64)INT_MIN))
+ *p = INT_MIN;
+ else
+ *p = -val;
+ } else {
+ if (val > INT_MAX)
+ *p = INT_MAX;
+ else
+ *p = val;
+ }
+
+ return 0;
+}
+
+/*
+ * The SMC has keys of multiple types, denoted by a FourCC of the same format
+ * as the key ID. We don't know what data type a key encodes until we poke at it.
+ */
+static int macsmc_hwmon_read_key(struct apple_smc *smc,
+ struct macsmc_hwmon_sensor *sensor, int scale,
+ long *val)
+{
+ int ret;
+
+ switch (sensor->info.type_code) {
+ /* 32-bit IEEE 754 float */
+ case __SMC_KEY('f', 'l', 't', ' '): {
+ u32 flt_ = 0;
+
+ ret = macsmc_hwmon_read_f32_scaled(smc, sensor->macsmc_key,
+ &flt_, scale);
+ if (ret)
+ return ret;
+
+ *val = flt_;
+ break;
+ }
+ /* 48.16 fixed point decimal */
+ case __SMC_KEY('i', 'o', 'f', 't'): {
+ u64 ioft = 0;
+
+ ret = macsmc_hwmon_read_ioft_scaled(smc, sensor->macsmc_key,
+ &ioft, scale);
+ if (ret)
+ return ret;
+
+ *val = (long)ioft;
+ break;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int macsmc_hwmon_write_f32(struct apple_smc *smc, smc_key key, int value)
+{
+ u64 val;
+ u32 fval = 0;
+ int exp = 0, neg;
+
+ val = abs(value);
+ neg = val != value;
+
+ if (val) {
+ int msb = __fls(val) - exp;
+
+ if (msb > 23) {
+ val >>= msb - FLT_MANT_BIAS;
+ exp -= msb - FLT_MANT_BIAS;
+ } else if (msb < 23) {
+ val <<= FLT_MANT_BIAS - msb;
+ exp += msb;
+ }
+
+ fval = FIELD_PREP(FLT_SIGN_MASK, neg) |
+ FIELD_PREP(FLT_EXP_MASK, exp + FLT_EXP_BIAS) |
+ FIELD_PREP(FLT_MANT_MASK, val);
+ }
+
+ return apple_smc_write_u32(smc, key, fval);
+}
+
+static int macsmc_hwmon_write_key(struct apple_smc *smc,
+ struct macsmc_hwmon_sensor *sensor, long val)
+{
+ switch (sensor->info.type_code) {
+ /* 32-bit IEEE 754 float */
+ case __SMC_KEY('f', 'l', 't', ' '):
+ return macsmc_hwmon_write_f32(smc, sensor->macsmc_key, val);
+ /* unsigned 8-bit integer */
+ case __SMC_KEY('u', 'i', '8', ' '):
+ return apple_smc_write_u8(smc, sensor->macsmc_key, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int macsmc_hwmon_read_fan(struct macsmc_hwmon *hwmon, u32 attr, int chan,
+ long *val)
+{
+ switch (attr) {
+ case hwmon_fan_input:
+ return macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->fan.fans[chan].now, 1, val);
+ case hwmon_fan_min:
+ return macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->fan.fans[chan].min, 1, val);
+ case hwmon_fan_max:
+ return macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->fan.fans[chan].max, 1, val);
+ case hwmon_fan_target:
+ return macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->fan.fans[chan].set, 1, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel,
+ long val)
+{
+ struct macsmc_hwmon *hwmon = dev_get_drvdata(dev);
+ long min, max;
+ int ret;
+
+ if (!fan_control || hwmon->fan.fans[channel].mode.macsmc_key == 0)
+ return -EOPNOTSUPP;
+
+ /*
+ * The SMC does no sanity checks on requested fan speeds, so we need to.
+ */
+ ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].min,
+ 1, &min);
+ if (ret)
+ return ret;
+
+ ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].max,
+ 1, &max);
+ if (ret)
+ return ret;
+
+ if (val >= min && val <= max) {
+ if (!hwmon->fan.fans[channel].manual) {
+ /* Write 1 to mode key for manual control */
+ ret = macsmc_hwmon_write_key(hwmon->smc,
+ &hwmon->fan.fans[channel].mode, 1);
+ if (ret < 0)
+ return ret;
+
+ hwmon->fan.fans[channel].manual = true;
+ }
+ return macsmc_hwmon_write_key(hwmon->smc,
+ &hwmon->fan.fans[channel].set, val);
+ } else if (!val) {
+ if (hwmon->fan.fans[channel].manual) {
+ ret = macsmc_hwmon_write_key(hwmon->smc,
+ &hwmon->fan.fans[channel].mode, 0);
+ if (ret < 0)
+ return ret;
+
+ hwmon->fan.fans[channel].manual = false;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int macsmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct macsmc_hwmon *hwmon = dev_get_drvdata(dev);
+ int ret = 0;
+
+ switch (type) {
+ case hwmon_temp:
+ ret = macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->temp.sensors[channel], 1000, val);
+ break;
+ case hwmon_in:
+ ret = macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->volt.sensors[channel], 1000, val);
+ break;
+ case hwmon_curr:
+ ret = macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->curr.sensors[channel], 1000, val);
+ break;
+ case hwmon_power:
+ /* SMC returns power in Watts with acceptable precision to scale to uW */
+ ret = macsmc_hwmon_read_key(hwmon->smc,
+ &hwmon->power.sensors[channel],
+ 1000000, val);
+ break;
+ case hwmon_fan:
+ ret = macsmc_hwmon_read_fan(hwmon, attr, channel, val);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return ret;
+}
+
+static int macsmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_fan:
+ return macsmc_hwmon_write_fan(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t macsmc_hwmon_fan_is_visible(const struct macsmc_hwmon_fan *fan,
+ u32 attr)
+{
+ if (fan->attrs & BIT(attr)) {
+ if (attr == hwmon_fan_target && fan_control && fan->mode.macsmc_key)
+ return 0644;
+
+ return 0444;
+ }
+
+ return 0;
+}
+
+static umode_t macsmc_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ const struct macsmc_hwmon *hwmon = data;
+ struct macsmc_hwmon_sensor *sensor;
+
+ switch (type) {
+ case hwmon_in:
+ sensor = &hwmon->volt.sensors[channel];
+ break;
+ case hwmon_curr:
+ sensor = &hwmon->curr.sensors[channel];
+ break;
+ case hwmon_power:
+ sensor = &hwmon->power.sensors[channel];
+ break;
+ case hwmon_temp:
+ sensor = &hwmon->temp.sensors[channel];
+ break;
+ case hwmon_fan:
+ return macsmc_hwmon_fan_is_visible(&hwmon->fan.fans[channel], attr);
+ default:
+ return 0;
+ }
+
+ /* Sensors only register ro attributes */
+ if (sensor->attrs & BIT(attr))
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_ops macsmc_hwmon_ops = {
+ .is_visible = macsmc_hwmon_is_visible,
+ .read = macsmc_hwmon_read,
+ .read_string = macsmc_hwmon_read_label,
+ .write = macsmc_hwmon_write,
+};
+
+/*
+ * Get the key metadata, including key data type, from the SMC.
+ */
+static int macsmc_hwmon_parse_key(struct device *dev, struct apple_smc *smc,
+ struct macsmc_hwmon_sensor *sensor,
+ const char *key)
+{
+ int ret;
+
+ ret = apple_smc_get_key_info(smc, _SMC_KEY(key), &sensor->info);
+ if (ret) {
+ dev_dbg(dev, "Failed to retrieve key info for %s\n", key);
+ return ret;
+ }
+
+ sensor->macsmc_key = _SMC_KEY(key);
+
+ return 0;
+}
+
+/*
+ * A sensor is a single key-value pair as made available by the SMC.
+ * The devicetree gives us the SMC key ID and a friendly name where the
+ * purpose of the sensor is known.
+ */
+static int macsmc_hwmon_create_sensor(struct device *dev, struct apple_smc *smc,
+ struct device_node *sensor_node,
+ struct macsmc_hwmon_sensor *sensor)
+{
+ const char *key, *label;
+ int ret;
+
+ ret = of_property_read_string(sensor_node, "apple,key-id", &key);
+ if (ret) {
+ dev_dbg(dev, "Could not find apple,key-id in sensor node\n");
+ return ret;
+ }
+
+ ret = macsmc_hwmon_parse_key(dev, smc, sensor, key);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_string(sensor_node, "label", &label);
+ if (ret)
+ dev_dbg(dev, "No label found for sensor %s\n", key);
+ else
+ strscpy_pad(sensor->label, label, sizeof(sensor->label));
+
+ return 0;
+}
+
+/*
+ * Fan data is exposed by the SMC as multiple sensors.
+ *
+ * The devicetree schema reuses apple,key-id for the actual fan speed sensor.
+ * Min, max and target keys do not need labels, so we can reuse label
+ * for naming the entire fan.
+ */
+static int macsmc_hwmon_create_fan(struct device *dev, struct apple_smc *smc,
+ struct device_node *fan_node,
+ struct macsmc_hwmon_fan *fan)
+{
+ const char *label, *now, *min, *max, *set, *mode;
+ int ret;
+
+ ret = of_property_read_string(fan_node, "apple,key-id", &now);
+ if (ret) {
+ dev_err(dev, "apple,key-id not found in fan node!\n");
+ return ret;
+ }
+
+ ret = macsmc_hwmon_parse_key(dev, smc, &fan->now, now);
+ if (ret)
+ return ret;
+
+ fan->attrs = HWMON_F_INPUT;
+
+ ret = of_property_read_string(fan_node, "label", &label);
+ if (ret) {
+ dev_dbg(dev, "No label found for fan %s\n", now);
+ } else {
+ strscpy_pad(fan->label, label, sizeof(fan->label));
+ fan->attrs |= HWMON_F_LABEL;
+ }
+
+ /* The following keys are not required to simply monitor fan speed */
+ if (!of_property_read_string(fan_node, "apple,fan-minimum", &min)) {
+ ret = macsmc_hwmon_parse_key(dev, smc, &fan->min, min);
+ if (ret)
+ return ret;
+
+ fan->attrs |= HWMON_F_MIN;
+ }
+
+ if (!of_property_read_string(fan_node, "apple,fan-maximum", &max)) {
+ ret = macsmc_hwmon_parse_key(dev, smc, &fan->max, max);
+ if (ret)
+ return ret;
+
+ fan->attrs |= HWMON_F_MAX;
+ }
+
+ if (!of_property_read_string(fan_node, "apple,fan-target", &set)) {
+ ret = macsmc_hwmon_parse_key(dev, smc, &fan->set, set);
+ if (ret)
+ return ret;
+
+ fan->attrs |= HWMON_F_TARGET;
+ }
+
+ if (!of_property_read_string(fan_node, "apple,fan-mode", &mode)) {
+ ret = macsmc_hwmon_parse_key(dev, smc, &fan->mode, mode);
+ if (ret)
+ return ret;
+ }
+
+ /* Initialise fan control mode to automatic */
+ fan->manual = false;
+
+ return 0;
+}
+
+static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon,
+ struct device_node *hwmon_node)
+{
+ struct device_node *key_node __maybe_unused;
+ struct macsmc_hwmon_sensor *sensor;
+ u32 n_current = 0, n_fan = 0, n_power = 0, n_temperature = 0, n_voltage = 0;
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "current-") {
+ n_current++;
+ }
+
+ if (n_current) {
+ hwmon->curr.sensors = devm_kcalloc(hwmon->dev, n_current,
+ sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+ if (!hwmon->curr.sensors)
+ return -ENOMEM;
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "current-") {
+ sensor = &hwmon->curr.sensors[hwmon->curr.count];
+ if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+ sensor->attrs = HWMON_C_INPUT;
+
+ if (*sensor->label)
+ sensor->attrs |= HWMON_C_LABEL;
+
+ hwmon->curr.count++;
+ }
+ }
+ }
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "fan-") {
+ n_fan++;
+ }
+
+ if (n_fan) {
+ hwmon->fan.fans = devm_kcalloc(hwmon->dev, n_fan,
+ sizeof(struct macsmc_hwmon_fan), GFP_KERNEL);
+ if (!hwmon->fan.fans)
+ return -ENOMEM;
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "fan-") {
+ if (!macsmc_hwmon_create_fan(hwmon->dev, hwmon->smc, key_node,
+ &hwmon->fan.fans[hwmon->fan.count]))
+ hwmon->fan.count++;
+ }
+ }
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "power-") {
+ n_power++;
+ }
+
+ if (n_power) {
+ hwmon->power.sensors = devm_kcalloc(hwmon->dev, n_power,
+ sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+ if (!hwmon->power.sensors)
+ return -ENOMEM;
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "power-") {
+ sensor = &hwmon->power.sensors[hwmon->power.count];
+ if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+ sensor->attrs = HWMON_P_INPUT;
+
+ if (*sensor->label)
+ sensor->attrs |= HWMON_P_LABEL;
+
+ hwmon->power.count++;
+ }
+ }
+ }
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "temperature-") {
+ n_temperature++;
+ }
+
+ if (n_temperature) {
+ hwmon->temp.sensors = devm_kcalloc(hwmon->dev, n_temperature,
+ sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+ if (!hwmon->temp.sensors)
+ return -ENOMEM;
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "temperature-") {
+ sensor = &hwmon->temp.sensors[hwmon->temp.count];
+ if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+ sensor->attrs = HWMON_T_INPUT;
+
+ if (*sensor->label)
+ sensor->attrs |= HWMON_T_LABEL;
+
+ hwmon->temp.count++;
+ }
+ }
+ }
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "voltage-") {
+ n_voltage++;
+ }
+
+ if (n_voltage) {
+ hwmon->volt.sensors = devm_kcalloc(hwmon->dev, n_voltage,
+ sizeof(struct macsmc_hwmon_sensor), GFP_KERNEL);
+ if (!hwmon->volt.sensors)
+ return -ENOMEM;
+
+ for_each_child_of_node_with_prefix(hwmon_node, key_node, "volt-") {
+ sensor = &hwmon->temp.sensors[hwmon->temp.count];
+ if (!macsmc_hwmon_create_sensor(hwmon->dev, hwmon->smc, key_node, sensor)) {
+ sensor->attrs = HWMON_I_INPUT;
+
+ if (*sensor->label)
+ sensor->attrs |= HWMON_I_LABEL;
+
+ hwmon->volt.count++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Create NULL-terminated config arrays */
+static void macsmc_hwmon_populate_configs(u32 *configs, const struct macsmc_hwmon_sensors *sensors)
+{
+ int idx;
+
+ for (idx = 0; idx < sensors->count; idx++)
+ configs[idx] = sensors->sensors[idx].attrs;
+}
+
+static void macsmc_hwmon_populate_fan_configs(u32 *configs, const struct macsmc_hwmon_fans *fans)
+{
+ int idx;
+
+ for (idx = 0; idx < fans->count; idx++)
+ configs[idx] = fans->fans[idx].attrs;
+}
+
+static const struct hwmon_channel_info *const macsmc_chip_channel_info =
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ);
+
+static int macsmc_hwmon_create_infos(struct macsmc_hwmon *hwmon)
+{
+ struct hwmon_channel_info *channel_info;
+ int i = 0;
+
+ /* chip */
+ hwmon->channel_infos[i++] = macsmc_chip_channel_info;
+
+ if (hwmon->curr.count) {
+ channel_info = &hwmon->curr.channel_info;
+ channel_info->type = hwmon_curr;
+ channel_info->config = devm_kcalloc(hwmon->dev, hwmon->curr.count + 1,
+ sizeof(u32), GFP_KERNEL);
+ if (!channel_info->config)
+ return -ENOMEM;
+
+ macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->curr);
+ hwmon->channel_infos[i++] = channel_info;
+ }
+
+ if (hwmon->fan.count) {
+ channel_info = &hwmon->fan.channel_info;
+ channel_info->type = hwmon_fan;
+ channel_info->config = devm_kcalloc(hwmon->dev, hwmon->fan.count + 1,
+ sizeof(u32), GFP_KERNEL);
+ if (!channel_info->config)
+ return -ENOMEM;
+
+ macsmc_hwmon_populate_fan_configs((u32 *)channel_info->config, &hwmon->fan);
+ hwmon->channel_infos[i++] = channel_info;
+ }
+
+ if (hwmon->power.count) {
+ channel_info = &hwmon->power.channel_info;
+ channel_info->type = hwmon_power;
+ channel_info->config = devm_kcalloc(hwmon->dev, hwmon->power.count + 1,
+ sizeof(u32), GFP_KERNEL);
+ if (!channel_info->config)
+ return -ENOMEM;
+
+ macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->power);
+ hwmon->channel_infos[i++] = channel_info;
+ }
+
+ if (hwmon->temp.count) {
+ channel_info = &hwmon->temp.channel_info;
+ channel_info->type = hwmon_temp;
+ channel_info->config = devm_kcalloc(hwmon->dev, hwmon->temp.count + 1,
+ sizeof(u32), GFP_KERNEL);
+ if (!channel_info->config)
+ return -ENOMEM;
+
+ macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->temp);
+ hwmon->channel_infos[i++] = channel_info;
+ }
+
+ if (hwmon->volt.count) {
+ channel_info = &hwmon->volt.channel_info;
+ channel_info->type = hwmon_in;
+ channel_info->config = devm_kcalloc(hwmon->dev, hwmon->volt.count + 1,
+ sizeof(u32), GFP_KERNEL);
+ if (!channel_info->config)
+ return -ENOMEM;
+
+ macsmc_hwmon_populate_configs((u32 *)channel_info->config, &hwmon->volt);
+ hwmon->channel_infos[i++] = channel_info;
+ }
+
+ return 0;
+}
+
+static int macsmc_hwmon_probe(struct platform_device *pdev)
+{
+ struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
+ struct macsmc_hwmon *hwmon;
+ int ret;
+
+ /*
+ * The MFD driver will try to probe us unconditionally. Some devices
+ * with the SMC do not have hwmon capabilities. Only probe if we have
+ * a hwmon node.
+ */
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon),
+ GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ hwmon->dev = &pdev->dev;
+ hwmon->smc = smc;
+
+ ret = macsmc_hwmon_populate_sensors(hwmon, hwmon->dev->of_node);
+ if (ret) {
+ dev_err(hwmon->dev, "Could not parse sensors\n");
+ return ret;
+ }
+
+ if (!hwmon->curr.count && !hwmon->fan.count &&
+ !hwmon->power.count && !hwmon->temp.count &&
+ !hwmon->volt.count) {
+ dev_err(hwmon->dev,
+ "No valid sensors found of any supported type\n");
+ return -ENODEV;
+ }
+
+ ret = macsmc_hwmon_create_infos(hwmon);
+ if (ret)
+ return ret;
+
+ hwmon->chip_info.ops = &macsmc_hwmon_ops;
+ hwmon->chip_info.info =
+ (const struct hwmon_channel_info *const *)&hwmon->channel_infos;
+
+ hwmon->hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+ "macsmc_hwmon", hwmon,
+ &hwmon->chip_info, NULL);
+ if (IS_ERR(hwmon->hwmon_dev))
+ return dev_err_probe(hwmon->dev, PTR_ERR(hwmon->hwmon_dev),
+ "Probing SMC hwmon device failed\n");
+
+ dev_dbg(hwmon->dev, "Registered SMC hwmon device. Sensors:\n");
+ dev_dbg(hwmon->dev,
+ "Current: %d, Fans: %d, Power: %d, Temperature: %d, Voltage: %d",
+ hwmon->curr.count, hwmon->fan.count,
+ hwmon->power.count, hwmon->temp.count,
+ hwmon->volt.count);
+
+ return 0;
+}
+
+static const struct of_device_id macsmc_hwmon_of_table[] = {
+ { .compatible = "apple,smc-hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, macsmc_hwmon_of_table);
+
+static struct platform_driver macsmc_hwmon_driver = {
+ .probe = macsmc_hwmon_probe,
+ .driver = {
+ .name = "macsmc-hwmon",
+ .of_match_table = macsmc_hwmon_of_table,
+ },
+};
+module_platform_driver(macsmc_hwmon_driver);
+
+MODULE_DESCRIPTION("Apple Silicon SMC hwmon driver");
+MODULE_AUTHOR("James Calligeros <jcalligeros99@gmail.com>");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/hwmon/max127.c b/drivers/hwmon/max127.c
index a9aab8862f5e..5102d86d2619 100644
--- a/drivers/hwmon/max127.c
+++ b/drivers/hwmon/max127.c
@@ -45,7 +45,6 @@
#define MAX127_SIGN_BIT BIT(11)
struct max127_data {
- struct mutex lock;
struct i2c_client *client;
u8 ctrl_byte[MAX127_NUM_CHANNELS];
};
@@ -121,21 +120,16 @@ static int max127_read_input(struct max127_data *data, int channel, long *val)
struct i2c_client *client = data->client;
u8 ctrl_byte = data->ctrl_byte[channel];
- mutex_lock(&data->lock);
-
status = max127_select_channel(client, ctrl_byte);
if (status)
- goto exit;
+ return status;
status = max127_read_channel(client, &raw);
if (status)
- goto exit;
+ return status;
*val = max127_process_raw(ctrl_byte, raw);
-
-exit:
- mutex_unlock(&data->lock);
- return status;
+ return 0;
}
static int max127_read_min(struct max127_data *data, int channel, long *val)
@@ -170,8 +164,6 @@ static int max127_write_min(struct max127_data *data, int channel, long val)
{
u8 ctrl;
- mutex_lock(&data->lock);
-
ctrl = data->ctrl_byte[channel];
if (val <= -MAX127_FULL_RANGE) {
ctrl |= (MAX127_CTRL_RNG | MAX127_CTRL_BIP);
@@ -182,23 +174,15 @@ static int max127_write_min(struct max127_data *data, int channel, long val)
ctrl &= ~MAX127_CTRL_BIP;
}
data->ctrl_byte[channel] = ctrl;
-
- mutex_unlock(&data->lock);
-
return 0;
}
static int max127_write_max(struct max127_data *data, int channel, long val)
{
- mutex_lock(&data->lock);
-
if (val >= MAX127_FULL_RANGE)
data->ctrl_byte[channel] |= MAX127_CTRL_RNG;
else
data->ctrl_byte[channel] &= ~MAX127_CTRL_RNG;
-
- mutex_unlock(&data->lock);
-
return 0;
}
@@ -315,7 +299,6 @@ static int max127_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->lock);
for (i = 0; i < ARRAY_SIZE(data->ctrl_byte); i++)
data->ctrl_byte[i] = (MAX127_CTRL_START |
MAX127_SET_CHANNEL(i));
diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c
index 0ccb5eb596fc..4c9e7892a73c 100644
--- a/drivers/hwmon/max16065.c
+++ b/drivers/hwmon/max16065.c
@@ -216,12 +216,13 @@ static ssize_t max16065_current_show(struct device *dev,
struct device_attribute *da, char *buf)
{
struct max16065_data *data = max16065_update_device(dev);
+ int curr_sense = data->curr_sense;
- if (unlikely(data->curr_sense < 0))
- return data->curr_sense;
+ if (unlikely(curr_sense < 0))
+ return curr_sense;
return sysfs_emit(buf, "%d\n",
- ADC_TO_CURR(data->curr_sense, data->curr_gain));
+ ADC_TO_CURR(curr_sense, data->curr_gain));
}
static ssize_t max16065_limit_store(struct device *dev,
diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
index f56913327004..4f6171a17d9f 100644
--- a/drivers/hwmon/max31790.c
+++ b/drivers/hwmon/max31790.c
@@ -57,7 +57,6 @@
*/
struct max31790_data {
struct i2c_client *client;
- struct mutex update_lock;
bool valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
@@ -74,30 +73,27 @@ static struct max31790_data *max31790_update_device(struct device *dev)
{
struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- struct max31790_data *ret = data;
- int i;
- int rv;
-
- mutex_lock(&data->update_lock);
+ int i, rv;
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ data->valid = false;
rv = i2c_smbus_read_byte_data(client,
MAX31790_REG_FAN_FAULT_STATUS1);
if (rv < 0)
- goto abort;
+ return ERR_PTR(rv);
data->fault_status |= rv & 0x3F;
rv = i2c_smbus_read_byte_data(client,
MAX31790_REG_FAN_FAULT_STATUS2);
if (rv < 0)
- goto abort;
+ return ERR_PTR(rv);
data->fault_status |= (rv & 0x3F) << 6;
for (i = 0; i < NR_CHANNEL; i++) {
rv = i2c_smbus_read_word_swapped(client,
MAX31790_REG_TACH_COUNT(i));
if (rv < 0)
- goto abort;
+ return ERR_PTR(rv);
data->tach[i] = rv;
if (data->fan_config[i]
@@ -106,19 +102,19 @@ static struct max31790_data *max31790_update_device(struct device *dev)
MAX31790_REG_TACH_COUNT(NR_CHANNEL
+ i));
if (rv < 0)
- goto abort;
+ return ERR_PTR(rv);
data->tach[NR_CHANNEL + i] = rv;
} else {
rv = i2c_smbus_read_word_swapped(client,
MAX31790_REG_PWM_DUTY_CYCLE(i));
if (rv < 0)
- goto abort;
+ return ERR_PTR(rv);
data->pwm[i] = rv;
rv = i2c_smbus_read_word_swapped(client,
MAX31790_REG_TARGET_COUNT(i));
if (rv < 0)
- goto abort;
+ return ERR_PTR(rv);
data->target_count[i] = rv;
}
}
@@ -126,16 +122,7 @@ static struct max31790_data *max31790_update_device(struct device *dev)
data->last_updated = jiffies;
data->valid = true;
}
- goto done;
-
-abort:
- data->valid = false;
- ret = ERR_PTR(rv);
-
-done:
- mutex_unlock(&data->update_lock);
-
- return ret;
+ return data;
}
static const u8 tach_period[8] = { 1, 2, 4, 8, 16, 32, 32, 32 };
@@ -189,7 +176,6 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel,
*val = rpm;
return 0;
case hwmon_fan_fault:
- mutex_lock(&data->update_lock);
*val = !!(data->fault_status & (1 << channel));
data->fault_status &= ~(1 << channel);
/*
@@ -200,10 +186,9 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel,
if (*val) {
int reg = MAX31790_REG_TARGET_COUNT(channel % NR_CHANNEL);
- i2c_smbus_write_byte_data(data->client, reg,
- data->target_count[channel % NR_CHANNEL] >> 8);
+ return i2c_smbus_write_byte_data(data->client, reg,
+ data->target_count[channel % NR_CHANNEL] >> 8);
}
- mutex_unlock(&data->update_lock);
return 0;
case hwmon_fan_enable:
*val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN);
@@ -223,8 +208,6 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
u8 bits, fan_config;
int sr;
- mutex_lock(&data->update_lock);
-
switch (attr) {
case hwmon_fan_target:
val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX);
@@ -270,9 +253,6 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
err = -EOPNOTSUPP;
break;
}
-
- mutex_unlock(&data->update_lock);
-
return err;
}
@@ -338,8 +318,6 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
u8 fan_config;
int err = 0;
- mutex_lock(&data->update_lock);
-
switch (attr) {
case hwmon_pwm_input:
if (val < 0 || val > 255) {
@@ -389,9 +367,6 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
err = -EOPNOTSUPP;
break;
}
-
- mutex_unlock(&data->update_lock);
-
return err;
}
@@ -525,7 +500,6 @@ static int max31790_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->update_lock);
/*
* Initialize the max31790 chip
diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c
index a31c7b655da1..9b2e56c040df 100644
--- a/drivers/hwmon/max31827.c
+++ b/drivers/hwmon/max31827.c
@@ -10,7 +10,6 @@
#include <linux/delay.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
-#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -99,7 +98,6 @@ struct max31827_state {
/*
* Prevent simultaneous access to the i2c client.
*/
- struct mutex lock;
struct regmap *regmap;
bool enable;
unsigned int resolution;
@@ -123,30 +121,23 @@ static int shutdown_write(struct max31827_state *st, unsigned int reg,
* Before the Temperature Threshold Alarm, Alarm Hysteresis Threshold
* and Resolution bits from Configuration register are changed over I2C,
* the part must be in shutdown mode.
- *
- * Mutex is used to ensure, that some other process doesn't change the
- * configuration register.
*/
- mutex_lock(&st->lock);
-
if (!st->enable) {
if (!mask)
- ret = regmap_write(st->regmap, reg, val);
- else
- ret = regmap_update_bits(st->regmap, reg, mask, val);
- goto unlock;
+ return regmap_write(st->regmap, reg, val);
+ return regmap_update_bits(st->regmap, reg, mask, val);
}
ret = regmap_read(st->regmap, MAX31827_CONFIGURATION_REG, &cfg);
if (ret)
- goto unlock;
+ return ret;
cnv_rate = MAX31827_CONFIGURATION_CNV_RATE_MASK & cfg;
cfg = cfg & ~(MAX31827_CONFIGURATION_1SHOT_MASK |
MAX31827_CONFIGURATION_CNV_RATE_MASK);
ret = regmap_write(st->regmap, MAX31827_CONFIGURATION_REG, cfg);
if (ret)
- goto unlock;
+ return ret;
if (!mask)
ret = regmap_write(st->regmap, reg, val);
@@ -154,15 +145,11 @@ static int shutdown_write(struct max31827_state *st, unsigned int reg,
ret = regmap_update_bits(st->regmap, reg, mask, val);
if (ret)
- goto unlock;
-
- ret = regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG,
- MAX31827_CONFIGURATION_CNV_RATE_MASK,
- cnv_rate);
+ return ret;
-unlock:
- mutex_unlock(&st->lock);
- return ret;
+ return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG,
+ MAX31827_CONFIGURATION_CNV_RATE_MASK,
+ cnv_rate);
}
static int write_alarm_val(struct max31827_state *st, unsigned int reg,
@@ -223,23 +210,13 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type,
break;
case hwmon_temp_input:
- mutex_lock(&st->lock);
-
if (!st->enable) {
- /*
- * This operation requires mutex protection,
- * because the chip configuration should not
- * be changed during the conversion process.
- */
-
ret = regmap_update_bits(st->regmap,
MAX31827_CONFIGURATION_REG,
MAX31827_CONFIGURATION_1SHOT_MASK,
1);
- if (ret) {
- mutex_unlock(&st->lock);
+ if (ret)
return ret;
- }
msleep(max31827_conv_times[st->resolution]);
}
@@ -254,8 +231,6 @@ static int max31827_read(struct device *dev, enum hwmon_sensor_types type,
ret = regmap_read(st->regmap, MAX31827_T_REG, &uval);
- mutex_unlock(&st->lock);
-
if (ret)
break;
@@ -352,7 +327,6 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
if (val >> 1)
return -EINVAL;
- mutex_lock(&st->lock);
/**
* The chip should not be enabled while a conversion is
* performed. Neither should the chip be enabled when
@@ -361,15 +335,11 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
st->enable = val;
- ret = regmap_update_bits(st->regmap,
- MAX31827_CONFIGURATION_REG,
- MAX31827_CONFIGURATION_1SHOT_MASK |
- MAX31827_CONFIGURATION_CNV_RATE_MASK,
- MAX31827_DEVICE_ENABLE(val));
-
- mutex_unlock(&st->lock);
-
- return ret;
+ return regmap_update_bits(st->regmap,
+ MAX31827_CONFIGURATION_REG,
+ MAX31827_CONFIGURATION_1SHOT_MASK |
+ MAX31827_CONFIGURATION_CNV_RATE_MASK,
+ MAX31827_DEVICE_ENABLE(val));
case hwmon_temp_max:
return write_alarm_val(st, MAX31827_TH_REG, val);
@@ -623,8 +593,6 @@ static int max31827_probe(struct i2c_client *client)
if (!st)
return -ENOMEM;
- mutex_init(&st->lock);
-
st->regmap = devm_regmap_init_i2c(client, &max31827_regmap);
if (IS_ERR(st->regmap))
return dev_err_probe(dev, PTR_ERR(st->regmap),
diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c
index 13201fb755c9..4316dcdd03fc 100644
--- a/drivers/hwmon/max6620.c
+++ b/drivers/hwmon/max6620.c
@@ -130,7 +130,6 @@ static const u8 target_reg[] = {
struct max6620_data {
struct i2c_client *client;
- struct mutex update_lock;
bool valid; /* false until following fields are valid */
unsigned long last_updated; /* in jiffies */
@@ -161,39 +160,36 @@ static int max6620_update_device(struct device *dev)
{
struct max6620_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- int i;
- int ret = 0;
-
- mutex_lock(&data->update_lock);
+ int i, ret;
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
for (i = 0; i < 4; i++) {
ret = i2c_smbus_read_byte_data(client, config_reg[i]);
if (ret < 0)
- goto error;
+ return ret;
data->fancfg[i] = ret;
ret = i2c_smbus_read_byte_data(client, dyn_reg[i]);
if (ret < 0)
- goto error;
+ return ret;
data->fandyn[i] = ret;
ret = i2c_smbus_read_byte_data(client, tach_reg[i]);
if (ret < 0)
- goto error;
+ return ret;
data->tach[i] = (ret << 3) & 0x7f8;
ret = i2c_smbus_read_byte_data(client, tach_reg[i] + 1);
if (ret < 0)
- goto error;
+ return ret;
data->tach[i] |= (ret >> 5) & 0x7;
ret = i2c_smbus_read_byte_data(client, target_reg[i]);
if (ret < 0)
- goto error;
+ return ret;
data->target[i] = (ret << 3) & 0x7f8;
ret = i2c_smbus_read_byte_data(client, target_reg[i] + 1);
if (ret < 0)
- goto error;
+ return ret;
data->target[i] |= (ret >> 5) & 0x7;
}
@@ -204,16 +200,13 @@ static int max6620_update_device(struct device *dev)
*/
ret = i2c_smbus_read_byte_data(client, MAX6620_REG_FAULT);
if (ret < 0)
- goto error;
+ return ret;
data->fault |= (ret >> 4) & (ret & 0x0F);
data->last_updated = jiffies;
data->valid = true;
}
-
-error:
- mutex_unlock(&data->update_lock);
- return ret;
+ return 0;
}
static umode_t
@@ -261,7 +254,6 @@ max6620_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
case hwmon_fan:
switch (attr) {
case hwmon_fan_alarm:
- mutex_lock(&data->update_lock);
*val = !!(data->fault & BIT(channel));
/* Setting TACH count to re-enable fan fault detection */
@@ -270,21 +262,15 @@ max6620_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
val2 = (data->target[channel] << 5) & 0xe0;
ret = i2c_smbus_write_byte_data(client,
target_reg[channel], val1);
- if (ret < 0) {
- mutex_unlock(&data->update_lock);
+ if (ret < 0)
return ret;
- }
ret = i2c_smbus_write_byte_data(client,
target_reg[channel] + 1, val2);
- if (ret < 0) {
- mutex_unlock(&data->update_lock);
+ if (ret < 0)
return ret;
- }
data->fault &= ~BIT(channel);
}
- mutex_unlock(&data->update_lock);
-
break;
case hwmon_fan_div:
*val = max6620_fan_div_from_reg(data->fandyn[channel]);
@@ -334,7 +320,6 @@ max6620_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
return ret;
data = dev_get_drvdata(dev);
client = data->client;
- mutex_lock(&data->update_lock);
switch (type) {
case hwmon_fan:
@@ -360,8 +345,7 @@ max6620_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
div = 5;
break;
default:
- ret = -EINVAL;
- goto error;
+ return -EINVAL;
}
data->fandyn[channel] &= 0x1F;
data->fandyn[channel] |= div << 5;
@@ -396,8 +380,6 @@ max6620_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
break;
}
-error:
- mutex_unlock(&data->update_lock);
return ret;
}
@@ -478,7 +460,6 @@ static int max6620_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->update_lock);
err = max6620_init_client(data);
if (err)
diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c
index a06346496e1d..99140a2ca995 100644
--- a/drivers/hwmon/max6639.c
+++ b/drivers/hwmon/max6639.c
@@ -16,9 +16,7 @@
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
@@ -75,7 +73,6 @@ static const unsigned int freq_table[] = { 20, 33, 50, 100, 5000, 8333, 12500,
*/
struct max6639_data {
struct regmap *regmap;
- struct mutex update_lock;
/* Register values initialized only once */
u8 ppr[MAX6639_NUM_CHANNELS]; /* Pulses per rotation 0..3 for 1..4 ppr */
@@ -249,16 +246,11 @@ static int max6639_write_fan(struct device *dev, u32 attr, int channel,
if (val <= 0 || val > 4)
return -EINVAL;
- mutex_lock(&data->update_lock);
/* Set Fan pulse per revolution */
err = max6639_set_ppr(data, channel, val);
- if (err < 0) {
- mutex_unlock(&data->update_lock);
+ if (err < 0)
return err;
- }
data->ppr[channel] = val;
-
- mutex_unlock(&data->update_lock);
return 0;
default:
return -EOPNOTSUPP;
@@ -320,21 +312,17 @@ static int max6639_write_pwm(struct device *dev, u32 attr, int channel,
case hwmon_pwm_input:
if (val < 0 || val > 255)
return -EINVAL;
- err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(channel),
- val * 120 / 255);
- return err;
+ return regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(channel),
+ val * 120 / 255);
case hwmon_pwm_freq:
val = clamp_val(val, 0, 25000);
i = find_closest(val, freq_table, ARRAY_SIZE(freq_table));
- mutex_lock(&data->update_lock);
err = regmap_update_bits(data->regmap, MAX6639_REG_FAN_CONFIG3(channel),
MAX6639_FAN_CONFIG3_FREQ_MASK, i);
- if (err < 0) {
- mutex_unlock(&data->update_lock);
+ if (err < 0)
return err;
- }
if (i >> 2)
err = regmap_set_bits(data->regmap, MAX6639_REG_GCONFIG,
@@ -343,7 +331,6 @@ static int max6639_write_pwm(struct device *dev, u32 attr, int channel,
err = regmap_clear_bits(data->regmap, MAX6639_REG_GCONFIG,
MAX6639_GCONFIG_PWM_FREQ_HI);
- mutex_unlock(&data->update_lock);
return err;
default:
return -EOPNOTSUPP;
@@ -753,8 +740,6 @@ static int max6639_probe(struct i2c_client *client)
}
}
- mutex_init(&data->update_lock);
-
/* Initialize the max6639 chip */
err = max6639_init_client(client, data);
if (err < 0)
diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c
index 0735a1d2c20f..dd906cf491ca 100644
--- a/drivers/hwmon/max6697.c
+++ b/drivers/hwmon/max6697.c
@@ -13,7 +13,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -91,8 +90,6 @@ struct max6697_data {
int temp_offset; /* in degrees C */
- struct mutex update_lock;
-
#define MAX6697_TEMP_INPUT 0
#define MAX6697_TEMP_EXT 1
#define MAX6697_TEMP_MAX 2
@@ -302,7 +299,6 @@ static int max6697_write(struct device *dev, enum hwmon_sensor_types type,
val = clamp_val(val, 0, 255);
return regmap_write(regmap, MAX6697_REG_MIN, val);
case hwmon_temp_offset:
- mutex_lock(&data->update_lock);
val = clamp_val(val, MAX6581_OFFSET_MIN, MAX6581_OFFSET_MAX);
val = DIV_ROUND_CLOSEST(val, 250);
if (!val) { /* disable this (and only this) channel */
@@ -313,11 +309,9 @@ static int max6697_write(struct device *dev, enum hwmon_sensor_types type,
ret = regmap_set_bits(regmap, MAX6581_REG_OFFSET_SELECT,
BIT(channel - 1));
if (ret)
- goto unlock;
+ return ret;
ret = regmap_write(regmap, MAX6581_REG_OFFSET, val);
}
-unlock:
- mutex_unlock(&data->update_lock);
return ret;
default:
return -EOPNOTSUPP;
@@ -548,7 +542,7 @@ static int max6697_probe(struct i2c_client *client)
struct regmap *regmap;
int err;
- regmap = regmap_init_i2c(client, &max6697_regmap_config);
+ regmap = devm_regmap_init_i2c(client, &max6697_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -559,7 +553,6 @@ static int max6697_probe(struct i2c_client *client)
data->regmap = regmap;
data->type = (uintptr_t)i2c_get_match_data(client);
data->chip = &max6697_chip_data[data->type];
- mutex_init(&data->update_lock);
err = max6697_init_chip(client->dev.of_node, data);
if (err)
diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c
index 7848198f8996..32c1e42e1278 100644
--- a/drivers/hwmon/mr75203.c
+++ b/drivers/hwmon/mr75203.c
@@ -14,7 +14,6 @@
#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
-#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c
index 407945d2cd6a..c3a719aef1ac 100644
--- a/drivers/hwmon/nct6775-platform.c
+++ b/drivers/hwmon/nct6775-platform.c
@@ -1403,6 +1403,7 @@ static const char * const asus_msi_boards[] = {
"ROG STRIX X670E-E GAMING WIFI",
"ROG STRIX X670E-F GAMING WIFI",
"ROG STRIX X670E-I GAMING WIFI",
+ "ROG STRIX X870E-H GAMING WIFI7",
"ROG STRIX Z590-A GAMING WIFI",
"ROG STRIX Z590-A GAMING WIFI II",
"ROG STRIX Z590-E GAMING WIFI",
diff --git a/drivers/hwmon/nct7363.c b/drivers/hwmon/nct7363.c
index e13ab918b1ab..71cef794835d 100644
--- a/drivers/hwmon/nct7363.c
+++ b/drivers/hwmon/nct7363.c
@@ -7,10 +7,8 @@
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c
index f1e6eda949ba..2fa091720c79 100644
--- a/drivers/hwmon/nct7904.c
+++ b/drivers/hwmon/nct7904.c
@@ -21,7 +21,6 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/i2c.h>
-#include <linux/mutex.h>
#include <linux/hwmon.h>
#include <linux/watchdog.h>
@@ -128,7 +127,6 @@ static const unsigned short normal_i2c[] = {
struct nct7904_data {
struct i2c_client *client;
struct watchdog_device wdt;
- struct mutex bank_lock;
int bank_sel;
u32 fanin_mask;
u32 vsen_mask;
@@ -142,24 +140,19 @@ struct nct7904_data {
};
/* Access functions */
-static int nct7904_bank_lock(struct nct7904_data *data, unsigned int bank)
+static int nct7904_bank_select(struct nct7904_data *data, unsigned int bank)
{
int ret;
- mutex_lock(&data->bank_lock);
if (data->bank_sel == bank)
return 0;
ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank);
- if (ret == 0)
- data->bank_sel = bank;
- else
+ if (ret < 0) {
data->bank_sel = -1;
- return ret;
-}
-
-static inline void nct7904_bank_release(struct nct7904_data *data)
-{
- mutex_unlock(&data->bank_lock);
+ return ret;
+ }
+ data->bank_sel = bank;
+ return 0;
}
/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */
@@ -169,12 +162,10 @@ static int nct7904_read_reg(struct nct7904_data *data,
struct i2c_client *client = data->client;
int ret;
- ret = nct7904_bank_lock(data, bank);
- if (ret == 0)
- ret = i2c_smbus_read_byte_data(client, reg);
-
- nct7904_bank_release(data);
- return ret;
+ ret = nct7904_bank_select(data, bank);
+ if (ret < 0)
+ return ret;
+ return i2c_smbus_read_byte_data(client, reg);
}
/*
@@ -187,19 +178,16 @@ static int nct7904_read_reg16(struct nct7904_data *data,
struct i2c_client *client = data->client;
int ret, hi;
- ret = nct7904_bank_lock(data, bank);
- if (ret == 0) {
- ret = i2c_smbus_read_byte_data(client, reg);
- if (ret >= 0) {
- hi = ret;
- ret = i2c_smbus_read_byte_data(client, reg + 1);
- if (ret >= 0)
- ret |= hi << 8;
- }
- }
-
- nct7904_bank_release(data);
- return ret;
+ ret = nct7904_bank_select(data, bank);
+ if (ret < 0)
+ return ret;
+ hi = i2c_smbus_read_byte_data(client, reg);
+ if (hi < 0)
+ return hi;
+ ret = i2c_smbus_read_byte_data(client, reg + 1);
+ if (ret < 0)
+ return ret;
+ return ret | (hi << 8);
}
/* Write 1-byte register. Returns 0 or -ERRNO on error. */
@@ -209,12 +197,10 @@ static int nct7904_write_reg(struct nct7904_data *data,
struct i2c_client *client = data->client;
int ret;
- ret = nct7904_bank_lock(data, bank);
- if (ret == 0)
- ret = i2c_smbus_write_byte_data(client, reg, val);
-
- nct7904_bank_release(data);
- return ret;
+ ret = nct7904_bank_select(data, bank);
+ if (ret < 0)
+ return ret;
+ return i2c_smbus_write_byte_data(client, reg, val);
}
static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
@@ -1023,7 +1009,6 @@ static int nct7904_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->bank_lock);
data->bank_sel = -1;
/* Setup sensor groups. */
diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
index 802c73def428..c8f5e695fb6d 100644
--- a/drivers/hwmon/npcm750-pwm-fan.c
+++ b/drivers/hwmon/npcm750-pwm-fan.c
@@ -4,7 +4,6 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -198,7 +197,6 @@ struct npcm7xx_pwm_fan_data {
int pwm_modules;
struct clk *pwm_clk;
struct clk *fan_clk;
- struct mutex pwm_lock[NPCM7XX_PWM_MAX_MODULES];
spinlock_t fan_lock[NPCM7XX_FAN_MAX_MODULE];
int fan_irq[NPCM7XX_FAN_MAX_MODULE];
bool pwm_present[NPCM7XX_PWM_MAX_CHN_NUM];
@@ -221,7 +219,6 @@ static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
/*
* Config PWM Comparator register for setting duty cycle
*/
- mutex_lock(&data->pwm_lock[module]);
/* write new CMR value */
iowrite32(val, NPCM7XX_PWM_REG_CMRx(data->pwm_base, module, pwm_ch));
@@ -245,7 +242,6 @@ static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
env_bit = NPCM7XX_PWM_CTRL_CH3_INV_BIT;
break;
default:
- mutex_unlock(&data->pwm_lock[module]);
return -ENODEV;
}
@@ -260,8 +256,6 @@ static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
}
iowrite32(tmp_buf, NPCM7XX_PWM_REG_CR(data->pwm_base, module));
- mutex_unlock(&data->pwm_lock[module]);
-
return 0;
}
@@ -932,8 +926,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
struct resource *res;
struct device *hwmon;
char name[20];
- int ret, cnt;
u32 output_freq;
+ int ret;
u32 i;
np = dev->of_node;
@@ -985,9 +979,6 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
output_freq = npcm7xx_pwm_init(data);
npcm7xx_fan_init(data);
- for (cnt = 0; cnt < data->pwm_modules; cnt++)
- mutex_init(&data->pwm_lock[cnt]);
-
for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
spin_lock_init(&data->fan_lock[i]);
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index d21f7266c411..d6b48178343d 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -24,6 +24,7 @@ enum ntc_thermistor_type {
TYPE_NCPXXWF104,
TYPE_NCPXXWL333,
TYPE_NCPXXXH103,
+ TYPE_NCPXXWM474,
};
struct ntc_compensation {
@@ -46,6 +47,7 @@ enum {
NTC_NCP18WB473,
NTC_NCP21WB473,
NTC_SSG1404001221,
+ NTC_NCP18WM474,
NTC_LAST,
};
@@ -60,6 +62,7 @@ static const struct platform_device_id ntc_thermistor_id[] = {
[NTC_NCP18WB473] = { "ncp18wb473", TYPE_NCPXXWB473 },
[NTC_NCP21WB473] = { "ncp21wb473", TYPE_NCPXXWB473 },
[NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 },
+ [NTC_NCP18WM474] = { "ncp18wm474", TYPE_NCPXXWM474 },
[NTC_LAST] = { },
};
MODULE_DEVICE_TABLE(platform, ntc_thermistor_id);
@@ -217,6 +220,43 @@ static const struct ntc_compensation ncpXXxh103[] = {
{ .temp_c = 125, .ohm = 531 },
};
+static const struct ntc_compensation ncpXXwm474[] = {
+ { .temp_c = -40, .ohm = 10900000 },
+ { .temp_c = -35, .ohm = 9600000 },
+ { .temp_c = -30, .ohm = 8300000 },
+ { .temp_c = -25, .ohm = 7000000 },
+ { .temp_c = -20, .ohm = 5980000 },
+ { .temp_c = -15, .ohm = 4960000 },
+ { .temp_c = -10, .ohm = 3940000 },
+ { .temp_c = -5, .ohm = 2920000 },
+ { .temp_c = 0, .ohm = 1900000 },
+ { .temp_c = 5, .ohm = 1614000 },
+ { .temp_c = 10, .ohm = 1328000 },
+ { .temp_c = 15, .ohm = 1042000 },
+ { .temp_c = 20, .ohm = 756000 },
+ { .temp_c = 25, .ohm = 470000 },
+ { .temp_c = 30, .ohm = 404000 },
+ { .temp_c = 35, .ohm = 338000 },
+ { .temp_c = 40, .ohm = 272000 },
+ { .temp_c = 45, .ohm = 206000 },
+ { .temp_c = 50, .ohm = 140000 },
+ { .temp_c = 55, .ohm = 122000 },
+ { .temp_c = 60, .ohm = 104000 },
+ { .temp_c = 65, .ohm = 86000 },
+ { .temp_c = 70, .ohm = 68000 },
+ { .temp_c = 75, .ohm = 50000 },
+ { .temp_c = 80, .ohm = 44200 },
+ { .temp_c = 85, .ohm = 38400 },
+ { .temp_c = 90, .ohm = 32600 },
+ { .temp_c = 95, .ohm = 26800 },
+ { .temp_c = 100, .ohm = 21000 },
+ { .temp_c = 105, .ohm = 18600 },
+ { .temp_c = 110, .ohm = 16200 },
+ { .temp_c = 115, .ohm = 13800 },
+ { .temp_c = 120, .ohm = 11400 },
+ { .temp_c = 125, .ohm = 9000 },
+};
+
/*
* The following compensation tables are from the specifications in EPCOS NTC
* Thermistors Datasheets
@@ -319,6 +359,7 @@ static const struct ntc_type ntc_type[] = {
NTC_TYPE(TYPE_NCPXXWF104, ncpXXwf104),
NTC_TYPE(TYPE_NCPXXWL333, ncpXXwl333),
NTC_TYPE(TYPE_NCPXXXH103, ncpXXxh103),
+ NTC_TYPE(TYPE_NCPXXWM474, ncpXXwm474),
};
/*
@@ -675,6 +716,8 @@ static const struct of_device_id ntc_match[] = {
.data = &ntc_thermistor_id[NTC_NCP21WB473] },
{ .compatible = "samsung,1404-001221",
.data = &ntc_thermistor_id[NTC_SSG1404001221] },
+ { .compatible = "murata,ncp18wm474",
+ .data = &ntc_thermistor_id[NTC_NCP18WM474] },
/* Usage of vendor name "ntc" is deprecated */
{ .compatible = "ntc,ncp03wb473",
diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h
index 734506b0eca2..92a7ee1925bc 100644
--- a/drivers/hwmon/peci/common.h
+++ b/drivers/hwmon/peci/common.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2021 Intel Corporation */
-#include <linux/mutex.h>
#include <linux/types.h>
#ifndef __PECI_HWMON_COMMON_H
@@ -13,12 +12,10 @@
* struct peci_sensor_state - PECI state information
* @valid: flag to indicate the sensor value is valid
* @last_updated: time of the last update in jiffies
- * @lock: mutex to protect sensor access
*/
struct peci_sensor_state {
bool valid;
unsigned long last_updated;
- struct mutex lock; /* protect sensor access */
};
/**
diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c
index c7112dbf008b..b2fc936851e1 100644
--- a/drivers/hwmon/peci/cputemp.c
+++ b/drivers/hwmon/peci/cputemp.c
@@ -116,11 +116,9 @@ static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type
{
int ret;
- mutex_lock(&priv->temp.target.state.lock);
-
ret = update_temp_target(priv);
if (ret)
- goto unlock;
+ return ret;
switch (type) {
case tcontrol_type:
@@ -139,9 +137,6 @@ static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type
ret = -EOPNOTSUPP;
break;
}
-unlock:
- mutex_unlock(&priv->temp.target.state.lock);
-
return ret;
}
@@ -177,26 +172,23 @@ static s32 dts_eight_dot_eight_to_millidegree(u16 val)
static int get_die_temp(struct peci_cputemp *priv, long *val)
{
- int ret = 0;
long tjmax;
u16 temp;
+ int ret;
- mutex_lock(&priv->temp.die.state.lock);
if (!peci_sensor_need_update(&priv->temp.die.state))
goto skip_update;
ret = peci_temp_read(priv->peci_dev, &temp);
if (ret)
- goto err_unlock;
+ return ret;
- if (!dts_valid(temp)) {
- ret = -EIO;
- goto err_unlock;
- }
+ if (!dts_valid(temp))
+ return -EIO;
ret = get_temp_target(priv, tjmax_type, &tjmax);
if (ret)
- goto err_unlock;
+ return ret;
priv->temp.die.value = (s32)tjmax + dts_ten_dot_six_to_millidegree(temp);
@@ -204,35 +196,30 @@ static int get_die_temp(struct peci_cputemp *priv, long *val)
skip_update:
*val = priv->temp.die.value;
-err_unlock:
- mutex_unlock(&priv->temp.die.state.lock);
- return ret;
+ return 0;
}
static int get_dts(struct peci_cputemp *priv, long *val)
{
- int ret = 0;
u16 thermal_margin;
long tcontrol;
u32 pcs;
+ int ret;
- mutex_lock(&priv->temp.dts.state.lock);
if (!peci_sensor_need_update(&priv->temp.dts.state))
goto skip_update;
ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs);
if (ret)
- goto err_unlock;
+ return ret;
thermal_margin = FIELD_GET(DTS_MARGIN_MASK, pcs);
- if (!dts_valid(thermal_margin)) {
- ret = -EIO;
- goto err_unlock;
- }
+ if (!dts_valid(thermal_margin))
+ return -EIO;
ret = get_temp_target(priv, tcontrol_type, &tcontrol);
if (ret)
- goto err_unlock;
+ return ret;
/* Note that the tcontrol should be available before calling it */
priv->temp.dts.value =
@@ -242,35 +229,30 @@ static int get_dts(struct peci_cputemp *priv, long *val)
skip_update:
*val = priv->temp.dts.value;
-err_unlock:
- mutex_unlock(&priv->temp.dts.state.lock);
- return ret;
+ return 0;
}
static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val)
{
- int ret = 0;
u16 core_dts_margin;
long tjmax;
u32 pcs;
+ int ret;
- mutex_lock(&priv->temp.core[core_index].state.lock);
if (!peci_sensor_need_update(&priv->temp.core[core_index].state))
goto skip_update;
ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs);
if (ret)
- goto err_unlock;
+ return ret;
core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs);
- if (!dts_valid(core_dts_margin)) {
- ret = -EIO;
- goto err_unlock;
- }
+ if (!dts_valid(core_dts_margin))
+ return -EIO;
ret = get_temp_target(priv, tjmax_type, &tjmax);
if (ret)
- goto err_unlock;
+ return ret;
/* Note that the tjmax should be available before calling it */
priv->temp.core[core_index].value =
@@ -280,9 +262,7 @@ static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val)
skip_update:
*val = priv->temp.core[core_index].value;
-err_unlock:
- mutex_unlock(&priv->temp.core[core_index].state.lock);
- return ret;
+ return 0;
}
static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type,
@@ -364,6 +344,7 @@ static int init_core_mask(struct peci_cputemp *priv)
case INTEL_ICELAKE_X:
case INTEL_ICELAKE_D:
case INTEL_SAPPHIRERAPIDS_X:
+ case INTEL_EMERALDRAPIDS_X:
ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev,
reg->func, reg->offset + 4, &data);
if (ret)
@@ -430,18 +411,6 @@ static void check_resolved_cores(struct peci_cputemp *priv)
bitmap_zero(priv->core_mask, CORE_NUMS_MAX);
}
-static void sensor_init(struct peci_cputemp *priv)
-{
- int i;
-
- mutex_init(&priv->temp.target.state.lock);
- mutex_init(&priv->temp.die.state.lock);
- mutex_init(&priv->temp.dts.state.lock);
-
- for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX)
- mutex_init(&priv->temp.core[i].state.lock);
-}
-
static const struct hwmon_ops peci_cputemp_ops = {
.is_visible = cputemp_is_visible,
.read_string = cputemp_read_string,
@@ -506,8 +475,6 @@ static int peci_cputemp_probe(struct auxiliary_device *adev,
check_resolved_cores(priv);
- sensor_init(priv);
-
hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name,
priv, &peci_cputemp_chip_info, NULL);
@@ -539,6 +506,13 @@ static struct resolved_cores_reg resolved_cores_reg_spr = {
.offset = 0x80,
};
+static struct resolved_cores_reg resolved_cores_reg_emr = {
+ .bus = 31,
+ .dev = 30,
+ .func = 6,
+ .offset = 0x80,
+};
+
static const struct cpu_info cpu_hsx = {
.reg = &resolved_cores_reg_hsx,
.min_peci_revision = 0x33,
@@ -563,6 +537,12 @@ static const struct cpu_info cpu_spr = {
.thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree,
};
+static const struct cpu_info cpu_emr = {
+ .reg = &resolved_cores_reg_emr,
+ .min_peci_revision = 0x40,
+ .thermal_margin_to_millidegree = &dts_ten_dot_six_to_millidegree,
+};
+
static const struct auxiliary_device_id peci_cputemp_ids[] = {
{
.name = "peci_cpu.cputemp.hsx",
@@ -592,6 +572,10 @@ static const struct auxiliary_device_id peci_cputemp_ids[] = {
.name = "peci_cpu.cputemp.spr",
.driver_data = (kernel_ulong_t)&cpu_spr,
},
+ {
+ .name = "peci_cpu.cputemp.emr",
+ .driver_data = (kernel_ulong_t)&cpu_emr,
+ },
{ }
};
MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids);
diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c
index fbe82d9852e0..bd3e8715dfec 100644
--- a/drivers/hwmon/peci/dimmtemp.c
+++ b/drivers/hwmon/peci/dimmtemp.c
@@ -32,6 +32,8 @@
#define DIMM_IDX_MAX_ON_ICXD 2
#define CHAN_RANK_MAX_ON_SPR 8
#define DIMM_IDX_MAX_ON_SPR 2
+#define CHAN_RANK_MAX_ON_EMR 8
+#define DIMM_IDX_MAX_ON_EMR 2
#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX
#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX
@@ -94,16 +96,15 @@ static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val)
{
int dimm_order = dimm_no % priv->gen_info->dimm_idx_max;
int chan_rank = dimm_no / priv->gen_info->dimm_idx_max;
- int ret = 0;
u32 data;
+ int ret;
- mutex_lock(&priv->dimm[dimm_no].temp.state.lock);
if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state))
goto skip_update;
ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data);
if (ret)
- goto unlock;
+ return ret;
priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE;
@@ -111,9 +112,7 @@ static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val)
skip_update:
*val = priv->dimm[dimm_no].temp.value;
-unlock:
- mutex_unlock(&priv->dimm[dimm_no].temp.state.lock);
- return ret;
+ return 0;
}
static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no)
@@ -143,10 +142,9 @@ static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_thresh
{
int ret;
- mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock);
ret = update_thresholds(priv, dimm_no);
if (ret)
- goto unlock;
+ return ret;
switch (type) {
case temp_max_type:
@@ -159,9 +157,6 @@ static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_thresh
ret = -EOPNOTSUPP;
break;
}
-unlock:
- mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock);
-
return ret;
}
@@ -347,8 +342,6 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
ret = create_dimm_temp_label(priv, i);
if (ret)
return ret;
- mutex_init(&priv->dimm[i].thresholds.state.lock);
- mutex_init(&priv->dimm[i].temp.state.lock);
}
dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv,
@@ -571,6 +564,12 @@ read_thresholds_spr(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u
return 0;
}
+static int read_thresholds_emr(struct peci_dimmtemp *priv, int dimm_order,
+ int chan_rank, u32 *data)
+{
+ return read_thresholds_spr(priv, dimm_order, chan_rank, data);
+}
+
static const struct dimm_info dimm_hsx = {
.chan_rank_max = CHAN_RANK_MAX_ON_HSX,
.dimm_idx_max = DIMM_IDX_MAX_ON_HSX,
@@ -620,6 +619,13 @@ static const struct dimm_info dimm_spr = {
.read_thresholds = &read_thresholds_spr,
};
+static const struct dimm_info dimm_emr = {
+ .chan_rank_max = CHAN_RANK_MAX_ON_EMR,
+ .dimm_idx_max = DIMM_IDX_MAX_ON_EMR,
+ .min_peci_revision = 0x40,
+ .read_thresholds = &read_thresholds_emr,
+};
+
static const struct auxiliary_device_id peci_dimmtemp_ids[] = {
{
.name = "peci_cpu.dimmtemp.hsx",
@@ -649,6 +655,10 @@ static const struct auxiliary_device_id peci_dimmtemp_ids[] = {
.name = "peci_cpu.dimmtemp.spr",
.driver_data = (kernel_ulong_t)&dimm_spr,
},
+ {
+ .name = "peci_cpu.dimmtemp.emr",
+ .driver_data = (kernel_ulong_t)&dimm_emr,
+ },
{ }
};
MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids);
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index da04ff6df28b..f3fb94cebf1a 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -320,6 +320,15 @@ config SENSORS_MAX16601
This driver can also be built as a module. If so, the module will
be called max16601.
+config SENSORS_MAX17616
+ tristate "Analog Devices MAX17616/MAX17616A"
+ help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices MAX17616/MAX17616A.
+
+ This driver can also be built as a module. If so, the module will
+ be called max17616.
+
config SENSORS_MAX20730
tristate "Maxim MAX20710, MAX20730, MAX20734, MAX20743"
help
@@ -352,6 +361,7 @@ config SENSORS_MAX34440
help
If you say yes here you get hardware monitoring support for Maxim
MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461.
+ Other compatible are ADPM12160, and ADPM12200.
This driver can also be built as a module. If so, the module will
be called max34440.
@@ -401,6 +411,15 @@ config SENSORS_MP2891
This driver can also be built as a module. If so, the module will
be called mp2891.
+config SENSORS_MP2925
+ tristate "MPS MP2925"
+ help
+ If you say yes here you get hardware monitoring support for MPS
+ MP2925 Dual Loop Digital Multi-Phase Controller.
+
+ This driver can also be built as a module. If so, the module will
+ be called mp2925.
+
config SENSORS_MP29502
tristate "MPS MP29502"
help
@@ -471,6 +490,15 @@ config SENSORS_MP9941
This driver can also be built as a module. If so, the module will
be called mp9941.
+config SENSORS_MP9945
+ tristate "MPS MP9945"
+ help
+ If you say yes here you get hardware monitoring support for MPS
+ MP9945.
+
+ This driver can also be built as a module. If so, the module will
+ be called mp9945.
+
config SENSORS_MPQ7932_REGULATOR
bool "Regulator support for MPQ7932"
depends on SENSORS_MPQ7932 && REGULATOR
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index 4c5ff3f32c5e..349a89b6d92e 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_SENSORS_LTC4286) += ltc4286.o
obj-$(CONFIG_SENSORS_MAX15301) += max15301.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX16601) += max16601.o
+obj-$(CONFIG_SENSORS_MAX17616) += max17616.o
obj-$(CONFIG_SENSORS_MAX20730) += max20730.o
obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SENSORS_MP2856) += mp2856.o
obj-$(CONFIG_SENSORS_MP2869) += mp2869.o
obj-$(CONFIG_SENSORS_MP2888) += mp2888.o
obj-$(CONFIG_SENSORS_MP2891) += mp2891.o
+obj-$(CONFIG_SENSORS_MP2925) += mp2925.o
obj-$(CONFIG_SENSORS_MP29502) += mp29502.o
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
obj-$(CONFIG_SENSORS_MP2993) += mp2993.o
@@ -47,6 +49,7 @@ obj-$(CONFIG_SENSORS_MP5023) += mp5023.o
obj-$(CONFIG_SENSORS_MP5920) += mp5920.o
obj-$(CONFIG_SENSORS_MP5990) += mp5990.o
obj-$(CONFIG_SENSORS_MP9941) += mp9941.o
+obj-$(CONFIG_SENSORS_MP9945) += mp9945.o
obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o
obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o
obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o
diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c
index 52cf62e45a86..97b61836f53a 100644
--- a/drivers/hwmon/pmbus/isl68137.c
+++ b/drivers/hwmon/pmbus/isl68137.c
@@ -65,6 +65,7 @@ enum chips {
raa228246,
raa229001,
raa229004,
+ raa229141,
raa229621,
};
@@ -73,6 +74,7 @@ enum variants {
raa_dmpvr2_1rail,
raa_dmpvr2_2rail,
raa_dmpvr2_2rail_nontc,
+ raa_dmpvr2_2rail_pmbus,
raa_dmpvr2_3rail,
raa_dmpvr2_hv,
};
@@ -336,10 +338,9 @@ static int isl68137_probe_from_dt(struct device *dev,
struct isl68137_data *data)
{
const struct device_node *np = dev->of_node;
- struct device_node *child;
int err;
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
if (strcmp(child->name, "channel"))
continue;
@@ -400,6 +401,17 @@ static int isl68137_probe(struct i2c_client *client)
info->read_word_data = raa_dmpvr2_read_word_data;
info->write_word_data = raa_dmpvr2_write_word_data;
break;
+ case raa_dmpvr2_2rail_pmbus:
+ info->format[PSC_VOLTAGE_IN] = linear,
+ info->format[PSC_VOLTAGE_OUT] = linear,
+ info->format[PSC_CURRENT_IN] = linear;
+ info->format[PSC_CURRENT_OUT] = linear;
+ info->format[PSC_POWER] = linear;
+ info->format[PSC_TEMPERATURE] = linear;
+ info->pages = 2;
+ info->read_word_data = raa_dmpvr2_read_word_data;
+ info->write_word_data = raa_dmpvr2_write_word_data;
+ break;
case raa_dmpvr2_3rail:
info->read_word_data = raa_dmpvr2_read_word_data;
info->write_word_data = raa_dmpvr2_write_word_data;
@@ -470,6 +482,7 @@ static const struct i2c_device_id raa_dmpvr_id[] = {
{"raa228246", raa_dmpvr2_2rail_nontc},
{"raa229001", raa_dmpvr2_2rail},
{"raa229004", raa_dmpvr2_2rail},
+ {"raa229141", raa_dmpvr2_2rail_pmbus},
{"raa229621", raa_dmpvr2_2rail},
{}
};
diff --git a/drivers/hwmon/pmbus/max17616.c b/drivers/hwmon/pmbus/max17616.c
new file mode 100644
index 000000000000..1d4a0ddb95bb
--- /dev/null
+++ b/drivers/hwmon/pmbus/max17616.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hardware monitoring driver for Analog Devices MAX17616/MAX17616A
+ *
+ * Copyright (C) 2025 Analog Devices, Inc.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+#include "pmbus.h"
+
+static struct pmbus_driver_info max17616_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .m[PSC_VOLTAGE_IN] = 512,
+ .b[PSC_VOLTAGE_IN] = -18,
+ .R[PSC_VOLTAGE_IN] = -1,
+
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .m[PSC_VOLTAGE_OUT] = 512,
+ .b[PSC_VOLTAGE_OUT] = -18,
+ .R[PSC_VOLTAGE_OUT] = -1,
+
+ .format[PSC_CURRENT_OUT] = direct,
+ .m[PSC_CURRENT_OUT] = 5845,
+ .b[PSC_CURRENT_OUT] = 80,
+ .R[PSC_CURRENT_OUT] = -1,
+
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_TEMPERATURE] = 71,
+ .b[PSC_TEMPERATURE] = 19653,
+ .R[PSC_TEMPERATURE] = -1,
+
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT |
+ PMBUS_HAVE_STATUS_TEMP,
+};
+
+static int max17616_probe(struct i2c_client *client)
+{
+ return pmbus_do_probe(client, &max17616_info);
+}
+
+static const struct i2c_device_id max17616_id[] = {
+ { "max17616" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max17616_id);
+
+static const struct of_device_id max17616_of_match[] = {
+ { .compatible = "adi,max17616" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max17616_of_match);
+
+static struct i2c_driver max17616_driver = {
+ .driver = {
+ .name = "max17616",
+ .of_match_table = max17616_of_match,
+ },
+ .probe = max17616_probe,
+ .id_table = max17616_id,
+};
+module_i2c_driver(max17616_driver);
+
+MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices MAX17616/MAX17616A");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c
index 56834d26f8ef..8ea4e68d4e9d 100644
--- a/drivers/hwmon/pmbus/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -17,6 +17,7 @@
enum chips {
adpm12160,
+ adpm12200,
max34440,
max34441,
max34446,
@@ -98,7 +99,7 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
break;
case PMBUS_VIRT_READ_IOUT_AVG:
if (data->id != max34446 && data->id != max34451 &&
- data->id != adpm12160)
+ data->id != adpm12160 && data->id != adpm12200)
return -ENXIO;
ret = pmbus_read_word_data(client, page, phase,
MAX34446_MFR_IOUT_AVG);
@@ -183,7 +184,7 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
ret = pmbus_write_word_data(client, page,
MAX34440_MFR_IOUT_PEAK, 0);
if (!ret && (data->id == max34446 || data->id == max34451 ||
- data->id == adpm12160))
+ data->id == adpm12160 || data->id == adpm12200))
ret = pmbus_write_word_data(client, page,
MAX34446_MFR_IOUT_AVG, 0);
@@ -336,18 +337,18 @@ static struct pmbus_driver_info max34440_info[] = {
.format[PSC_CURRENT_IN] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
- .m[PSC_VOLTAGE_IN] = 1,
+ .m[PSC_VOLTAGE_IN] = 125,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 0,
- .m[PSC_VOLTAGE_OUT] = 1,
+ .m[PSC_VOLTAGE_OUT] = 125,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 0,
- .m[PSC_CURRENT_IN] = 1,
+ .m[PSC_CURRENT_IN] = 250,
.b[PSC_CURRENT_IN] = 0,
- .R[PSC_CURRENT_IN] = 2,
- .m[PSC_CURRENT_OUT] = 1,
+ .R[PSC_CURRENT_IN] = -1,
+ .m[PSC_CURRENT_OUT] = 250,
.b[PSC_CURRENT_OUT] = 0,
- .R[PSC_CURRENT_OUT] = 2,
+ .R[PSC_CURRENT_OUT] = -1,
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 0,
.R[PSC_TEMPERATURE] = 2,
@@ -364,6 +365,42 @@ static struct pmbus_driver_info max34440_info[] = {
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
},
+ [adpm12200] = {
+ .pages = 19,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_IN] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 125,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .m[PSC_VOLTAGE_OUT] = 125,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 0,
+ .m[PSC_CURRENT_IN] = 250,
+ .b[PSC_CURRENT_IN] = 0,
+ .R[PSC_CURRENT_IN] = -1,
+ .m[PSC_CURRENT_OUT] = 250,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = -1,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ /* absent func below [18] are not for monitoring */
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[4] = PMBUS_HAVE_STATUS_IOUT,
+ .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[7] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[8] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[14] = PMBUS_HAVE_IOUT,
+ .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_word_data = max34440_read_word_data,
+ .write_word_data = max34440_write_word_data,
+ },
[max34440] = {
.pages = 14,
.format[PSC_VOLTAGE_IN] = direct,
@@ -600,7 +637,7 @@ static int max34440_probe(struct i2c_client *client)
rv = max34451_set_supported_funcs(client, data);
if (rv)
return rv;
- } else if (data->id == adpm12160) {
+ } else if (data->id == adpm12160 || data->id == adpm12200) {
data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT;
}
@@ -610,6 +647,7 @@ static int max34440_probe(struct i2c_client *client)
static const struct i2c_device_id max34440_id[] = {
{"adpm12160", adpm12160},
+ {"adpm12200", adpm12200},
{"max34440", max34440},
{"max34441", max34441},
{"max34446", max34446},
diff --git a/drivers/hwmon/pmbus/mp2925.c b/drivers/hwmon/pmbus/mp2925.c
new file mode 100644
index 000000000000..6bebd6023021
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp2925.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2925)
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+/*
+ * Vender specific register MFR_VR_MULTI_CONFIG(0x08).
+ * This register is used to obtain vid scale.
+ */
+#define MFR_VR_MULTI_CONFIG 0x08
+
+#define MP2925_VOUT_DIV 512
+#define MP2925_VOUT_OVUV_UINT 195
+#define MP2925_VOUT_OVUV_DIV 100
+
+#define MP2925_PAGE_NUM 2
+
+#define MP2925_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_PIN | \
+ PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \
+ PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_TEMP | \
+ PMBUS_HAVE_STATUS_INPUT)
+
+#define MP2925_RAIL2_FUNC (PMBUS_HAVE_PIN | PMBUS_HAVE_VOUT | \
+ PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_IIN | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_TEMP | \
+ PMBUS_HAVE_STATUS_INPUT)
+
+struct mp2925_data {
+ struct pmbus_driver_info info;
+ int vout_scale[MP2925_PAGE_NUM];
+};
+
+#define to_mp2925_data(x) container_of(x, struct mp2925_data, info)
+
+static u16 mp2925_linear_exp_transfer(u16 word, u16 expect_exponent)
+{
+ s16 exponent, mantissa, target_exponent;
+
+ exponent = ((s16)word) >> 11;
+ mantissa = ((s16)((word & 0x7ff) << 5)) >> 5;
+ target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11;
+
+ if (exponent > target_exponent)
+ mantissa = mantissa << (exponent - target_exponent);
+ else
+ mantissa = mantissa >> (target_exponent - exponent);
+
+ return (mantissa & 0x7ff) | ((expect_exponent << 11) & 0xf800);
+}
+
+static int mp2925_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ /*
+ * The MP2925 does not follow standard PMBus protocol completely,
+ * and the calculation of vout in this driver is based on direct
+ * format. As a result, the format of vout is enforced to direct.
+ */
+ ret = PB_VOUT_MODE_DIRECT;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int mp2925_read_word_data(struct i2c_client *client, int page, int phase,
+ int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct mp2925_data *data = to_mp2925_data(info);
+ int ret;
+
+ switch (reg) {
+ case PMBUS_READ_VOUT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * data->vout_scale[page],
+ MP2925_VOUT_DIV);
+ break;
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ ret = pmbus_read_word_data(client, page, phase, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * MP2925_VOUT_OVUV_UINT,
+ MP2925_VOUT_OVUV_DIV);
+ break;
+ case PMBUS_STATUS_WORD:
+ case PMBUS_READ_VIN:
+ case PMBUS_READ_IOUT:
+ case PMBUS_READ_POUT:
+ case PMBUS_READ_PIN:
+ case PMBUS_READ_IIN:
+ case PMBUS_READ_TEMPERATURE_1:
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ ret = -ENODATA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int mp2925_write_word_data(struct i2c_client *client, int page, int reg,
+ u16 word)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIN_OV_FAULT_LIMIT:
+ case PMBUS_VIN_OV_WARN_LIMIT:
+ case PMBUS_VIN_UV_WARN_LIMIT:
+ case PMBUS_VIN_UV_FAULT_LIMIT:
+ /*
+ * The PMBUS_VIN_OV_FAULT_LIMIT, PMBUS_VIN_OV_WARN_LIMIT,
+ * PMBUS_VIN_UV_WARN_LIMIT and PMBUS_VIN_UV_FAULT_LIMIT
+ * of MP2925 is linear11 format, and the exponent is a
+ * constant value(5'b11100), so the exponent of word
+ * parameter should be converted to 5'b11100(0x1C).
+ */
+ ret = pmbus_write_word_data(client, page, reg,
+ mp2925_linear_exp_transfer(word, 0x1C));
+ break;
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ /*
+ * The bit0-bit11 is the limit value, and bit12-bit15
+ * should not be changed.
+ */
+ ret = pmbus_read_word_data(client, page, 0xff, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = pmbus_write_word_data(client, page, reg,
+ (ret & ~GENMASK(11, 0)) |
+ FIELD_PREP(GENMASK(11, 0),
+ DIV_ROUND_CLOSEST(word * MP2925_VOUT_OVUV_DIV,
+ MP2925_VOUT_OVUV_UINT)));
+ break;
+ case PMBUS_OT_FAULT_LIMIT:
+ case PMBUS_OT_WARN_LIMIT:
+ /*
+ * The PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT of
+ * MP2925 is linear11 format, and the exponent is a
+ * constant value(5'b00000), so the exponent of word
+ * parameter should be converted to 5'b00000.
+ */
+ ret = pmbus_write_word_data(client, page, reg,
+ mp2925_linear_exp_transfer(word, 0x00));
+ break;
+ case PMBUS_IOUT_OC_FAULT_LIMIT:
+ case PMBUS_IOUT_OC_WARN_LIMIT:
+ /*
+ * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT
+ * of MP2925 is linear11 format, and the exponent can not be
+ * changed.
+ */
+ ret = pmbus_read_word_data(client, page, 0xff, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = pmbus_write_word_data(client, page, reg,
+ mp2925_linear_exp_transfer(word,
+ FIELD_GET(GENMASK(15, 11),
+ ret)));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+mp2925_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info,
+ int page)
+{
+ struct mp2925_data *data = to_mp2925_data(info);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ if (FIELD_GET(GENMASK(5, 5), ret)) {
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE,
+ page == 0 ? 3 : 4);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG);
+ if (ret < 0)
+ return ret;
+
+ if (FIELD_GET(GENMASK(5, 5), ret))
+ data->vout_scale[page] = 2560;
+ else
+ data->vout_scale[page] = 5120;
+ } else if (FIELD_GET(GENMASK(4, 4), ret)) {
+ data->vout_scale[page] = 1;
+ } else {
+ data->vout_scale[page] = 512;
+ }
+
+ return 0;
+}
+
+static int mp2925_identify(struct i2c_client *client, struct pmbus_driver_info *info)
+{
+ int ret;
+
+ ret = mp2925_identify_vout_scale(client, info, 0);
+ if (ret < 0)
+ return ret;
+
+ return mp2925_identify_vout_scale(client, info, 1);
+}
+
+static const struct pmbus_driver_info mp2925_info = {
+ .pages = MP2925_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_VOLTAGE_OUT] = direct,
+
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .b[PSC_VOLTAGE_OUT] = 0,
+
+ .func[0] = MP2925_RAIL1_FUNC,
+ .func[1] = MP2925_RAIL2_FUNC,
+ .read_word_data = mp2925_read_word_data,
+ .read_byte_data = mp2925_read_byte_data,
+ .write_word_data = mp2925_write_word_data,
+ .identify = mp2925_identify,
+};
+
+static int mp2925_probe(struct i2c_client *client)
+{
+ struct mp2925_data *data;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct mp2925_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(&data->info, &mp2925_info, sizeof(mp2925_info));
+
+ return pmbus_do_probe(client, &data->info);
+}
+
+static const struct i2c_device_id mp2925_id[] = {
+ {"mp2925"},
+ {"mp2929"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mp2925_id);
+
+static const struct of_device_id __maybe_unused mp2925_of_match[] = {
+ {.compatible = "mps,mp2925"},
+ {.compatible = "mps,mp2929"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp2925_of_match);
+
+static struct i2c_driver mp2925_driver = {
+ .driver = {
+ .name = "mp2925",
+ .of_match_table = mp2925_of_match,
+ },
+ .probe = mp2925_probe,
+ .id_table = mp2925_id,
+};
+
+module_i2c_driver(mp2925_driver);
+
+MODULE_AUTHOR("Wensheng Wang <wenswang@yeah.net>");
+MODULE_DESCRIPTION("PMBus driver for MPS MP2925");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/mp9945.c b/drivers/hwmon/pmbus/mp9945.c
new file mode 100644
index 000000000000..34822e0de812
--- /dev/null
+++ b/drivers/hwmon/pmbus/mp9945.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for MPS Single-phase Digital VR Controllers(MP9945)
+ */
+
+#include <linux/bitfield.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include "pmbus.h"
+
+#define MFR_VR_MULTI_CONFIG_R1 0x08
+#define MFR_SVID_CFG_R1 0xBD
+
+/* VOUT_MODE register values */
+#define VOUT_MODE_LINEAR16 0x17
+#define VOUT_MODE_VID 0x21
+#define VOUT_MODE_DIRECT 0x40
+
+#define MP9945_PAGE_NUM 1
+
+#define MP9945_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
+ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | \
+ PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \
+ PMBUS_HAVE_TEMP | \
+ PMBUS_HAVE_STATUS_VOUT | \
+ PMBUS_HAVE_STATUS_IOUT | \
+ PMBUS_HAVE_STATUS_TEMP | \
+ PMBUS_HAVE_STATUS_INPUT)
+
+enum mp9945_vout_mode {
+ MP9945_VOUT_MODE_VID,
+ MP9945_VOUT_MODE_DIRECT,
+ MP9945_VOUT_MODE_LINEAR16,
+};
+
+struct mp9945_data {
+ struct pmbus_driver_info info;
+ enum mp9945_vout_mode vout_mode;
+ int vid_resolution;
+ int vid_offset;
+};
+
+#define to_mp9945_data(x) container_of(x, struct mp9945_data, info)
+
+static int mp9945_read_vout(struct i2c_client *client, struct mp9945_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_read_word_data(client, PMBUS_READ_VOUT);
+ if (ret < 0)
+ return ret;
+
+ ret &= GENMASK(11, 0);
+
+ switch (data->vout_mode) {
+ case MP9945_VOUT_MODE_VID:
+ if (ret > 0)
+ ret = (ret + data->vid_offset) * data->vid_resolution;
+ break;
+ case MP9945_VOUT_MODE_DIRECT:
+ break;
+ case MP9945_VOUT_MODE_LINEAR16:
+ /* LSB: 1000 * 2^-9 (mV) */
+ ret = DIV_ROUND_CLOSEST(ret * 125, 64);
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return ret;
+}
+
+static int mp9945_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return ret;
+
+ switch (reg) {
+ case PMBUS_VOUT_MODE:
+ /*
+ * Override VOUT_MODE to DIRECT as the driver handles custom
+ * VOUT format conversions internally.
+ */
+ return PB_VOUT_MODE_DIRECT;
+ default:
+ return -ENODATA;
+ }
+}
+
+static int mp9945_read_word_data(struct i2c_client *client, int page, int phase,
+ int reg)
+{
+ const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+ struct mp9945_data *data = to_mp9945_data(info);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return ret;
+
+ switch (reg) {
+ case PMBUS_READ_VOUT:
+ ret = mp9945_read_vout(client, data);
+ break;
+ case PMBUS_VOUT_OV_FAULT_LIMIT:
+ case PMBUS_VOUT_UV_FAULT_LIMIT:
+ ret = i2c_smbus_read_word_data(client, reg);
+ if (ret < 0)
+ return ret;
+
+ /* LSB: 1.95 (mV) */
+ ret = DIV_ROUND_CLOSEST((ret & GENMASK(11, 0)) * 39, 20);
+ break;
+ case PMBUS_VOUT_UV_WARN_LIMIT:
+ ret = i2c_smbus_read_word_data(client, reg);
+ if (ret < 0)
+ return ret;
+
+ ret &= GENMASK(9, 0);
+ if (ret > 0)
+ ret = (ret + data->vid_offset) * data->vid_resolution;
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+
+ return ret;
+}
+
+static int mp9945_identify(struct i2c_client *client,
+ struct pmbus_driver_info *info)
+{
+ struct mp9945_data *data = to_mp9945_data(info);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
+ if (ret < 0)
+ return ret;
+
+ switch (ret) {
+ case VOUT_MODE_LINEAR16:
+ data->vout_mode = MP9945_VOUT_MODE_LINEAR16;
+ break;
+ case VOUT_MODE_VID:
+ data->vout_mode = MP9945_VOUT_MODE_VID;
+ break;
+ case VOUT_MODE_DIRECT:
+ data->vout_mode = MP9945_VOUT_MODE_DIRECT;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 3);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG_R1);
+ if (ret < 0)
+ return ret;
+
+ data->vid_resolution = (FIELD_GET(BIT(2), ret)) ? 5 : 10;
+
+ ret = i2c_smbus_read_word_data(client, MFR_SVID_CFG_R1);
+ if (ret < 0)
+ return ret;
+
+ data->vid_offset = (FIELD_GET(BIT(15), ret)) ? 19 : 49;
+
+ return i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+}
+
+static struct pmbus_driver_info mp9945_info = {
+ .pages = MP9945_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .R[PSC_VOLTAGE_OUT] = 3,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .func[0] = MP9945_RAIL1_FUNC,
+ .read_word_data = mp9945_read_word_data,
+ .read_byte_data = mp9945_read_byte_data,
+ .identify = mp9945_identify,
+};
+
+static int mp9945_probe(struct i2c_client *client)
+{
+ struct mp9945_data *data;
+ int ret;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ memcpy(&data->info, &mp9945_info, sizeof(mp9945_info));
+
+ /*
+ * Set page 0 before probe. The core reads paged registers which are
+ * only on page 0 for this device.
+ */
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return ret;
+
+ return pmbus_do_probe(client, &data->info);
+}
+
+static const struct i2c_device_id mp9945_id[] = {
+ {"mp9945"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mp9945_id);
+
+static const struct of_device_id __maybe_unused mp9945_of_match[] = {
+ {.compatible = "mps,mp9945"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, mp9945_of_match);
+
+static struct i2c_driver mp9945_driver = {
+ .driver = {
+ .name = "mp9945",
+ .of_match_table = of_match_ptr(mp9945_of_match),
+ },
+ .probe = mp9945_probe,
+ .id_table = mp9945_id,
+};
+
+module_i2c_driver(mp9945_driver);
+
+MODULE_AUTHOR("Cosmo Chou <chou.cosmo@gmail.com>");
+MODULE_DESCRIPTION("PMBus driver for MPS MP9945");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c
index 5f9ca6543530..06a2c56016d1 100644
--- a/drivers/hwmon/powr1220.c
+++ b/drivers/hwmon/powr1220.c
@@ -16,7 +16,6 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/delay.h>
#define ADC_STEP_MV 2
@@ -75,7 +74,6 @@ enum powr1220_adc_values {
struct powr1220_data {
struct i2c_client *client;
- struct mutex update_lock;
u8 max_channels;
bool adc_valid[MAX_POWR1220_ADC_VALUES];
/* the next value is in jiffies */
@@ -111,8 +109,6 @@ static int powr1220_read_adc(struct device *dev, int ch_num)
int result;
int adc_range = 0;
- mutex_lock(&data->update_lock);
-
if (time_after(jiffies, data->adc_last_updated[ch_num] + HZ) ||
!data->adc_valid[ch_num]) {
/*
@@ -128,8 +124,8 @@ static int powr1220_read_adc(struct device *dev, int ch_num)
/* set the attenuator and mux */
result = i2c_smbus_write_byte_data(data->client, ADC_MUX,
adc_range | ch_num);
- if (result)
- goto exit;
+ if (result < 0)
+ return result;
/*
* wait at least Tconvert time (200 us) for the
@@ -140,14 +136,14 @@ static int powr1220_read_adc(struct device *dev, int ch_num)
/* get the ADC reading */
result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_LOW);
if (result < 0)
- goto exit;
+ return result;
reading = result >> 4;
/* get the upper half of the reading */
result = i2c_smbus_read_byte_data(data->client, ADC_VALUE_HIGH);
if (result < 0)
- goto exit;
+ return result;
reading |= result << 4;
@@ -163,10 +159,6 @@ static int powr1220_read_adc(struct device *dev, int ch_num)
} else {
result = data->adc_values[ch_num];
}
-
-exit:
- mutex_unlock(&data->update_lock);
-
return result;
}
@@ -302,7 +294,6 @@ static int powr1220_probe(struct i2c_client *client)
break;
}
- mutex_init(&data->update_lock);
data->client = client;
hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
diff --git a/drivers/hwmon/sbtsi_temp.c b/drivers/hwmon/sbtsi_temp.c
index a6c439e376ff..c5b2488c4c7f 100644
--- a/drivers/hwmon/sbtsi_temp.c
+++ b/drivers/hwmon/sbtsi_temp.c
@@ -12,7 +12,6 @@
#include <linux/init.h>
#include <linux/hwmon.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/bitfield.h>
@@ -52,7 +51,6 @@
/* Each client has this additional data */
struct sbtsi_data {
struct i2c_client *client;
- struct mutex lock;
bool ext_range_mode;
bool read_order;
};
@@ -94,7 +92,6 @@ static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_temp_input:
- mutex_lock(&data->lock);
if (data->read_order) {
temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
@@ -102,19 +99,14 @@ static int sbtsi_read(struct device *dev, enum hwmon_sensor_types type,
temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_INT);
temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_DEC);
}
- mutex_unlock(&data->lock);
break;
case hwmon_temp_max:
- mutex_lock(&data->lock);
temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_INT);
temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_HIGH_DEC);
- mutex_unlock(&data->lock);
break;
case hwmon_temp_min:
- mutex_lock(&data->lock);
temp_int = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_INT);
temp_dec = i2c_smbus_read_byte_data(data->client, SBTSI_REG_TEMP_LOW_DEC);
- mutex_unlock(&data->lock);
break;
default:
return -EINVAL;
@@ -158,15 +150,11 @@ static int sbtsi_write(struct device *dev, enum hwmon_sensor_types type,
val = clamp_val(val, SBTSI_TEMP_MIN, SBTSI_TEMP_MAX);
sbtsi_mc_to_reg(val, &temp_int, &temp_dec);
- mutex_lock(&data->lock);
err = i2c_smbus_write_byte_data(data->client, reg_int, temp_int);
if (err)
- goto exit;
+ return err;
- err = i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec);
-exit:
- mutex_unlock(&data->lock);
- return err;
+ return i2c_smbus_write_byte_data(data->client, reg_dec, temp_dec);
}
static umode_t sbtsi_is_visible(const void *data,
@@ -219,7 +207,6 @@ static int sbtsi_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->lock);
err = i2c_smbus_read_byte_data(data->client, SBTSI_REG_CONFIG);
if (err < 0)
diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c
index 364199b332c0..eec223d174c0 100644
--- a/drivers/hwmon/scmi-hwmon.c
+++ b/drivers/hwmon/scmi-hwmon.c
@@ -240,6 +240,8 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
const struct hwmon_channel_info **ptr_scmi_ci;
const struct scmi_handle *handle = sdev->handle;
struct scmi_protocol_handle *ph;
+ u32 sensor_config = FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
+ SCMI_SENS_CFG_SENSOR_ENABLE);
if (!handle)
return -ENODEV;
@@ -339,6 +341,13 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
if (!sensor)
continue;
+ ret = sensor_ops->config_set(ph, i, sensor_config);
+ if (ret) {
+ dev_err(dev, "Error enabling sensor %s. err=%d\n",
+ sensor->name, ret);
+ continue;
+ }
+
/*
* Warn on any misconfiguration related to thermal zones but
* bail out of probing only on memory errors.
diff --git a/drivers/hwmon/sfctemp.c b/drivers/hwmon/sfctemp.c
index fb1da93383d7..b78b2c099a12 100644
--- a/drivers/hwmon/sfctemp.c
+++ b/drivers/hwmon/sfctemp.c
@@ -10,7 +10,6 @@
#include <linux/hwmon.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
@@ -49,8 +48,6 @@
#define SFCTEMP_K1000 81100L
struct sfctemp {
- /* serialize access to hardware register and enabled below */
- struct mutex lock;
void __iomem *regs;
struct clk *clk_sense;
struct clk *clk_bus;
@@ -92,15 +89,14 @@ static void sfctemp_stop(struct sfctemp *sfctemp)
static int sfctemp_enable(struct sfctemp *sfctemp)
{
- int ret = 0;
+ int ret;
- mutex_lock(&sfctemp->lock);
if (sfctemp->enabled)
- goto done;
+ return 0;
ret = clk_prepare_enable(sfctemp->clk_bus);
if (ret)
- goto err;
+ return ret;
ret = reset_control_deassert(sfctemp->rst_bus);
if (ret)
goto err_disable_bus;
@@ -115,9 +111,7 @@ static int sfctemp_enable(struct sfctemp *sfctemp)
sfctemp_power_up(sfctemp);
sfctemp_run(sfctemp);
sfctemp->enabled = true;
-done:
- mutex_unlock(&sfctemp->lock);
- return ret;
+ return 0;
err_disable_sense:
clk_disable_unprepare(sfctemp->clk_sense);
@@ -125,16 +119,13 @@ err_assert_bus:
reset_control_assert(sfctemp->rst_bus);
err_disable_bus:
clk_disable_unprepare(sfctemp->clk_bus);
-err:
- mutex_unlock(&sfctemp->lock);
return ret;
}
static int sfctemp_disable(struct sfctemp *sfctemp)
{
- mutex_lock(&sfctemp->lock);
if (!sfctemp->enabled)
- goto done;
+ return 0;
sfctemp_stop(sfctemp);
sfctemp_power_down(sfctemp);
@@ -143,8 +134,6 @@ static int sfctemp_disable(struct sfctemp *sfctemp)
reset_control_assert(sfctemp->rst_bus);
clk_disable_unprepare(sfctemp->clk_bus);
sfctemp->enabled = false;
-done:
- mutex_unlock(&sfctemp->lock);
return 0;
}
@@ -155,22 +144,14 @@ static void sfctemp_disable_action(void *data)
static int sfctemp_convert(struct sfctemp *sfctemp, long *val)
{
- int ret;
-
- mutex_lock(&sfctemp->lock);
- if (!sfctemp->enabled) {
- ret = -ENODATA;
- goto out;
- }
+ if (!sfctemp->enabled)
+ return -ENODATA;
/* calculate temperature in milli Celcius */
*val = (long)((readl(sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS)
* SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000;
- ret = 0;
-out:
- mutex_unlock(&sfctemp->lock);
- return ret;
+ return 0;
}
static umode_t sfctemp_is_visible(const void *data, enum hwmon_sensor_types type,
@@ -263,7 +244,6 @@ static int sfctemp_probe(struct platform_device *pdev)
return -ENOMEM;
dev_set_drvdata(dev, sfctemp);
- mutex_init(&sfctemp->lock);
sfctemp->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(sfctemp->regs))
diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
index 557ad3e7752a..f36c0229328f 100644
--- a/drivers/hwmon/sht3x.c
+++ b/drivers/hwmon/sht3x.c
@@ -291,24 +291,26 @@ out:
return data;
}
-static int temp1_input_read(struct device *dev)
+static int temp1_input_read(struct device *dev, long *temp)
{
struct sht3x_data *data = sht3x_update_client(dev);
if (IS_ERR(data))
return PTR_ERR(data);
- return data->temperature;
+ *temp = data->temperature;
+ return 0;
}
-static int humidity1_input_read(struct device *dev)
+static int humidity1_input_read(struct device *dev, long *humidity)
{
struct sht3x_data *data = sht3x_update_client(dev);
if (IS_ERR(data))
return PTR_ERR(data);
- return data->humidity;
+ *humidity = data->humidity;
+ return 0;
}
/*
@@ -706,6 +708,7 @@ static int sht3x_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
enum sht3x_limits index;
+ int ret;
switch (type) {
case hwmon_chip:
@@ -720,10 +723,12 @@ static int sht3x_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
- *val = temp1_input_read(dev);
- break;
+ return temp1_input_read(dev, val);
case hwmon_temp_alarm:
- *val = temp1_alarm_read(dev);
+ ret = temp1_alarm_read(dev);
+ if (ret < 0)
+ return ret;
+ *val = ret;
break;
case hwmon_temp_max:
index = limit_max;
@@ -748,10 +753,12 @@ static int sht3x_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_humidity:
switch (attr) {
case hwmon_humidity_input:
- *val = humidity1_input_read(dev);
- break;
+ return humidity1_input_read(dev, val);
case hwmon_humidity_alarm:
- *val = humidity1_alarm_read(dev);
+ ret = humidity1_alarm_read(dev);
+ if (ret < 0)
+ return ret;
+ *val = ret;
break;
case hwmon_humidity_max:
index = limit_max;
diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c
index 6c9b776237c2..5abe1227e109 100644
--- a/drivers/hwmon/sht4x.c
+++ b/drivers/hwmon/sht4x.c
@@ -55,7 +55,6 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
/**
* struct sht4x_data - All the data required to operate an SHT4X chip
* @client: the i2c client associated with the SHT4X
- * @lock: a mutex that is used to prevent parallel access to the i2c client
* @heating_complete: the time that the last heating finished
* @data_pending: true if and only if there are measurements to retrieve after heating
* @heater_power: the power at which the heater will be started
@@ -68,7 +67,6 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
*/
struct sht4x_data {
struct i2c_client *client;
- struct mutex lock; /* atomic read data updates */
unsigned long heating_complete; /* in jiffies */
bool data_pending;
u32 heater_power; /* in milli-watts */
@@ -87,7 +85,7 @@ struct sht4x_data {
*/
static int sht4x_read_values(struct sht4x_data *data)
{
- int ret = 0;
+ int ret;
u16 t_ticks, rh_ticks;
unsigned long next_update;
struct i2c_client *client = data->client;
@@ -96,8 +94,6 @@ static int sht4x_read_values(struct sht4x_data *data)
u8 raw_data[SHT4X_RESPONSE_LENGTH];
unsigned long curr_jiffies;
- mutex_lock(&data->lock);
-
curr_jiffies = jiffies;
if (time_before(curr_jiffies, data->heating_complete))
msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
@@ -110,11 +106,11 @@ static int sht4x_read_values(struct sht4x_data *data)
msecs_to_jiffies(data->update_interval);
if (data->valid && time_before_eq(jiffies, next_update))
- goto unlock;
+ return 0;
ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
if (ret < 0)
- goto unlock;
+ return ret;
usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA);
}
@@ -123,7 +119,7 @@ static int sht4x_read_values(struct sht4x_data *data)
if (ret != SHT4X_RESPONSE_LENGTH) {
if (ret >= 0)
ret = -ENODATA;
- goto unlock;
+ return ret;
}
t_ticks = raw_data[0] << 8 | raw_data[1];
@@ -132,26 +128,20 @@ static int sht4x_read_values(struct sht4x_data *data)
crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
if (crc != raw_data[2]) {
dev_err(&client->dev, "data integrity check failed\n");
- ret = -EIO;
- goto unlock;
+ return -EIO;
}
crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
if (crc != raw_data[5]) {
dev_err(&client->dev, "data integrity check failed\n");
- ret = -EIO;
- goto unlock;
+ return -EIO;
}
data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
data->last_updated = jiffies;
data->valid = true;
- ret = 0;
-
-unlock:
- mutex_unlock(&data->lock);
- return ret;
+ return 0;
}
static ssize_t sht4x_interval_write(struct sht4x_data *data, long val)
@@ -287,22 +277,16 @@ static ssize_t heater_enable_store(struct device *dev,
heating_time_bound = 1100;
}
- mutex_lock(&data->lock);
-
- if (time_before(jiffies, data->heating_complete)) {
- ret = -EBUSY;
- goto unlock;
- }
+ if (time_before(jiffies, data->heating_complete))
+ return -EBUSY;
ret = i2c_master_send(data->client, &cmd, SHT4X_CMD_LEN);
if (ret < 0)
- goto unlock;
+ return ret;
data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
data->data_pending = true;
-unlock:
- mutex_unlock(&data->lock);
- return ret;
+ return 0;
}
static ssize_t heater_power_show(struct device *dev,
@@ -422,8 +406,6 @@ static int sht4x_probe(struct i2c_client *client)
data->heater_time = 1000;
data->heating_complete = jiffies;
- mutex_init(&data->lock);
-
crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
diff --git a/drivers/hwmon/sy7636a-hwmon.c b/drivers/hwmon/sy7636a-hwmon.c
index a12fc0ce70e7..d51daaf63d63 100644
--- a/drivers/hwmon/sy7636a-hwmon.c
+++ b/drivers/hwmon/sy7636a-hwmon.c
@@ -66,18 +66,13 @@ static const struct hwmon_chip_info sy7636a_chip_info = {
static int sy7636a_sensor_probe(struct platform_device *pdev)
{
struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL);
- struct regulator *regulator;
struct device *hwmon_dev;
int err;
if (!regmap)
return -EPROBE_DEFER;
- regulator = devm_regulator_get(&pdev->dev, "vcom");
- if (IS_ERR(regulator))
- return PTR_ERR(regulator);
-
- err = regulator_enable(regulator);
+ err = devm_regulator_get_enable(&pdev->dev, "vcom");
if (err)
return err;
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 376e0eac8cc1..5b10c395a84d 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -10,9 +10,7 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c
index f271a03e05ae..221bba8a215d 100644
--- a/drivers/hwmon/tmp103.c
+++ b/drivers/hwmon/tmp103.c
@@ -14,11 +14,8 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/device.h>
-#include <linux/jiffies.h>
#include <linux/regmap.h>
#define TMP103_TEMP_REG 0x00
diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c
index a971ff628435..60a237cbedbc 100644
--- a/drivers/hwmon/tmp108.c
+++ b/drivers/hwmon/tmp108.c
@@ -10,7 +10,6 @@
#include <linux/hwmon.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/i3c/device.h>
#include <linux/init.h>
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index 02c5a3bb1071..fbaa34973694 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -24,7 +24,6 @@
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -107,7 +106,6 @@ MODULE_DEVICE_TABLE(i2c, tmp401_id);
struct tmp401_data {
struct i2c_client *client;
struct regmap *regmap;
- struct mutex update_lock;
enum chips kind;
bool extended_range;
@@ -357,7 +355,6 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel,
unsigned int regval;
int reg, ret, temp;
- mutex_lock(&data->update_lock);
switch (attr) {
case hwmon_temp_min:
case hwmon_temp_max:
@@ -386,7 +383,6 @@ static int tmp401_temp_write(struct device *dev, u32 attr, int channel,
ret = -EOPNOTSUPP;
break;
}
- mutex_unlock(&data->update_lock);
return ret;
}
@@ -436,7 +432,6 @@ static int tmp401_chip_write(struct device *dev, u32 attr, int channel, long val
struct regmap *regmap = data->regmap;
int err;
- mutex_lock(&data->update_lock);
switch (attr) {
case hwmon_chip_update_interval:
err = tmp401_set_convrate(regmap, val);
@@ -456,8 +451,6 @@ static int tmp401_chip_write(struct device *dev, u32 attr, int channel, long val
err = -EOPNOTSUPP;
break;
}
- mutex_unlock(&data->update_lock);
-
return err;
}
@@ -685,7 +678,6 @@ static int tmp401_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
- mutex_init(&data->update_lock);
data->kind = (uintptr_t)i2c_get_match_data(client);
data->regmap = devm_regmap_init(dev, NULL, data, &tmp401_regmap_config);
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
index 9537727aad9a..2ea9d3e9553d 100644
--- a/drivers/hwmon/tmp421.c
+++ b/drivers/hwmon/tmp421.c
@@ -19,7 +19,6 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/sysfs.h>
@@ -99,7 +98,6 @@ struct tmp421_channel {
struct tmp421_data {
struct i2c_client *client;
- struct mutex update_lock;
u32 temp_config[MAX_CHANNELS + 1];
struct hwmon_channel_info temp_info;
const struct hwmon_channel_info *info[2];
@@ -130,38 +128,28 @@ static int tmp421_update_device(struct tmp421_data *data)
int ret = 0;
int i;
- mutex_lock(&data->update_lock);
-
if (time_after(jiffies, data->last_updated + (HZ / 2)) ||
!data->valid) {
+ data->valid = false;
ret = i2c_smbus_read_byte_data(client, TMP421_CONFIG_REG_1);
if (ret < 0)
- goto exit;
+ return ret;
data->config = ret;
for (i = 0; i < data->channels; i++) {
ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_MSB[i]);
if (ret < 0)
- goto exit;
+ return ret;
data->channel[i].temp = ret << 8;
ret = i2c_smbus_read_byte_data(client, TMP421_TEMP_LSB[i]);
if (ret < 0)
- goto exit;
+ return ret;
data->channel[i].temp |= ret;
}
data->last_updated = jiffies;
data->valid = true;
}
-
-exit:
- mutex_unlock(&data->update_lock);
-
- if (ret < 0) {
- data->valid = false;
- return ret;
- }
-
return 0;
}
@@ -262,7 +250,6 @@ static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type,
switch (attr) {
case hwmon_temp_fault:
case hwmon_temp_input:
- return 0444;
case hwmon_temp_label:
return 0444;
case hwmon_temp_enable:
@@ -381,7 +368,11 @@ static int tmp421_probe_child_from_dt(struct i2c_client *client,
return -EINVAL;
}
- of_property_read_string(child, "label", &data->channel[i].label);
+ err = of_property_read_string(child, "label", &data->channel[i].label);
+ if (err == -ENODATA || err == -EILSEQ) {
+ dev_err(dev, "invalid label property in %pOFn\n", child);
+ return err;
+ }
if (data->channel[i].label)
data->temp_config[i] |= HWMON_T_LABEL;
@@ -442,7 +433,6 @@ static int tmp421_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
- mutex_init(&data->update_lock);
data->channels = (unsigned long)i2c_get_match_data(client);
data->client = client;
diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c
index 0f629c6d7695..98f2576d94c6 100644
--- a/drivers/hwmon/tmp464.c
+++ b/drivers/hwmon/tmp464.c
@@ -13,7 +13,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -92,7 +91,6 @@ struct tmp464_channel {
struct tmp464_data {
struct regmap *regmap;
- struct mutex update_lock;
int channels;
s16 config_orig;
u16 open_reg;
@@ -172,19 +170,16 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val
* complete. That means we have to cache the value internally
* for one measurement cycle and report the cached value.
*/
- mutex_lock(&data->update_lock);
if (!data->valid || time_after(jiffies, data->last_updated +
msecs_to_jiffies(data->update_interval))) {
err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, &regval);
if (err < 0)
- goto unlock;
+ break;
data->open_reg = regval;
data->last_updated = jiffies;
data->valid = true;
}
*val = !!(data->open_reg & BIT(channel + 7));
-unlock:
- mutex_unlock(&data->update_lock);
break;
case hwmon_temp_max_hyst:
regs[0] = TMP464_THERM_LIMIT[channel];
@@ -345,8 +340,6 @@ static int tmp464_write(struct device *dev, enum hwmon_sensor_types type,
struct tmp464_data *data = dev_get_drvdata(dev);
int err;
- mutex_lock(&data->update_lock);
-
switch (type) {
case hwmon_chip:
err = tmp464_chip_write(data, attr, channel, val);
@@ -359,8 +352,6 @@ static int tmp464_write(struct device *dev, enum hwmon_sensor_types type,
break;
}
- mutex_unlock(&data->update_lock);
-
return err;
}
@@ -658,8 +649,6 @@ static int tmp464_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
- mutex_init(&data->update_lock);
-
data->channels = (int)(unsigned long)i2c_get_match_data(client);
data->regmap = devm_regmap_init_i2c(client, &tmp464_regmap_config);
diff --git a/drivers/hwmon/tsc1641.c b/drivers/hwmon/tsc1641.c
new file mode 100644
index 000000000000..2b5d34bab146
--- /dev/null
+++ b/drivers/hwmon/tsc1641.c
@@ -0,0 +1,748 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for ST Microelectronics TSC1641 I2C power monitor
+ *
+ * 60 V, 16-bit high-precision power monitor with I2C and MIPI I3C interface
+ * Datasheet: https://www.st.com/resource/en/datasheet/tsc1641.pdf
+ *
+ * Copyright (C) 2025 Igor Reznichenko <igor@reznichenko.net>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/util_macros.h>
+
+/* I2C registers */
+#define TSC1641_CONFIG 0x00
+#define TSC1641_SHUNT_VOLTAGE 0x01
+#define TSC1641_LOAD_VOLTAGE 0x02
+#define TSC1641_POWER 0x03
+#define TSC1641_CURRENT 0x04
+#define TSC1641_TEMP 0x05
+#define TSC1641_MASK 0x06
+#define TSC1641_FLAG 0x07
+#define TSC1641_RSHUNT 0x08 /* Shunt resistance */
+#define TSC1641_SOL 0x09
+#define TSC1641_SUL 0x0A
+#define TSC1641_LOL 0x0B
+#define TSC1641_LUL 0x0C
+#define TSC1641_POL 0x0D
+#define TSC1641_TOL 0x0E
+#define TSC1641_MANUF_ID 0xFE /* 0x0006 */
+#define TSC1641_DIE_ID 0xFF /* 0x1000 */
+#define TSC1641_MAX_REG 0xFF
+
+#define TSC1641_RSHUNT_DEFAULT 1000 /* 1mOhm */
+#define TSC1641_CONFIG_DEFAULT 0x003F /* Default mode and temperature sensor */
+#define TSC1641_MASK_DEFAULT 0xFC00 /* Unmask all alerts */
+
+/* Bit mask for conversion time in the configuration register */
+#define TSC1641_CONV_TIME_MASK GENMASK(7, 4)
+
+#define TSC1641_CONV_TIME_DEFAULT 1024
+#define TSC1641_MIN_UPDATE_INTERVAL 1024
+
+/* LSB value of different registers */
+#define TSC1641_VLOAD_LSB_MVOLT 2
+#define TSC1641_POWER_LSB_UWATT 25000
+#define TSC1641_VSHUNT_LSB_NVOLT 2500 /* Use nanovolts to make it integer */
+#define TSC1641_RSHUNT_LSB_UOHM 10
+#define TSC1641_TEMP_LSB_MDEGC 500
+
+/* Limits based on datasheet */
+#define TSC1641_RSHUNT_MIN_UOHM 100
+#define TSC1641_RSHUNT_MAX_UOHM 655350
+#define TSC1641_CURR_ABS_MAX_MAMP 819200 /* Max current at 100uOhm*/
+
+#define TSC1641_ALERT_POL_MASK BIT(1)
+#define TSC1641_ALERT_LATCH_EN_MASK BIT(0)
+
+/* Flags indicating alerts in TSC1641_FLAG register*/
+#define TSC1641_SAT_FLAG BIT(13)
+#define TSC1641_SHUNT_OV_FLAG BIT(6)
+#define TSC1641_SHUNT_UV_FLAG BIT(5)
+#define TSC1641_LOAD_OV_FLAG BIT(4)
+#define TSC1641_LOAD_UV_FLAG BIT(3)
+#define TSC1641_POWER_OVER_FLAG BIT(2)
+#define TSC1641_TEMP_OVER_FLAG BIT(1)
+
+static bool tsc1641_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TSC1641_CONFIG:
+ case TSC1641_MASK:
+ case TSC1641_RSHUNT:
+ case TSC1641_SOL:
+ case TSC1641_SUL:
+ case TSC1641_LOL:
+ case TSC1641_LUL:
+ case TSC1641_POL:
+ case TSC1641_TOL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tsc1641_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TSC1641_SHUNT_VOLTAGE:
+ case TSC1641_LOAD_VOLTAGE:
+ case TSC1641_POWER:
+ case TSC1641_CURRENT:
+ case TSC1641_TEMP:
+ case TSC1641_FLAG:
+ case TSC1641_MANUF_ID:
+ case TSC1641_DIE_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tsc1641_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_write = true,
+ .use_single_read = true,
+ .max_register = TSC1641_MAX_REG,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = tsc1641_volatile_reg,
+ .writeable_reg = tsc1641_writeable_reg,
+};
+
+struct tsc1641_data {
+ long rshunt_uohm;
+ long current_lsb_ua;
+ struct regmap *regmap;
+};
+
+/*
+ * Upper limit due to chip 16-bit shunt register, lower limit to
+ * prevent current and power registers overflow
+ */
+static inline int tsc1641_validate_shunt(u32 val)
+{
+ if (val < TSC1641_RSHUNT_MIN_UOHM || val > TSC1641_RSHUNT_MAX_UOHM)
+ return -EINVAL;
+ return 0;
+}
+
+static int tsc1641_set_shunt(struct tsc1641_data *data, u32 val)
+{
+ struct regmap *regmap = data->regmap;
+ long rshunt_reg;
+
+ /* RSHUNT register LSB is 10uOhm so need to divide further */
+ rshunt_reg = DIV_ROUND_CLOSEST(val, TSC1641_RSHUNT_LSB_UOHM);
+ /*
+ * Clamp value to the nearest multiple of TSC1641_RSHUNT_LSB_UOHM
+ * in case shunt value provided was not a multiple
+ */
+ data->rshunt_uohm = rshunt_reg * TSC1641_RSHUNT_LSB_UOHM;
+ data->current_lsb_ua = DIV_ROUND_CLOSEST(TSC1641_VSHUNT_LSB_NVOLT * 1000,
+ data->rshunt_uohm);
+
+ return regmap_write(regmap, TSC1641_RSHUNT, rshunt_reg);
+}
+
+/*
+ * Conversion times in uS, value in CONFIG[CT3:CT0] corresponds to index in this array
+ * See "Table 14. CT3 to CT0: conversion time" in:
+ * https://www.st.com/resource/en/datasheet/tsc1641.pdf
+ */
+static const int tsc1641_conv_times[] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 };
+
+static int tsc1641_reg_to_upd_interval(u16 config)
+{
+ int idx = FIELD_GET(TSC1641_CONV_TIME_MASK, config);
+
+ idx = clamp_val(idx, 0, ARRAY_SIZE(tsc1641_conv_times) - 1);
+ int conv_time = tsc1641_conv_times[idx];
+
+ /* Don't support sub-millisecond update interval as it's not supported in hwmon */
+ conv_time = max(conv_time, TSC1641_MIN_UPDATE_INTERVAL);
+ /* Return nearest value in milliseconds */
+ return DIV_ROUND_CLOSEST(conv_time, 1000);
+}
+
+static u16 tsc1641_upd_interval_to_reg(long interval)
+{
+ /* Supported interval is 1ms - 33ms */
+ interval = clamp_val(interval, 1, 33);
+
+ int conv = interval * 1000;
+ int conv_bits = find_closest(conv, tsc1641_conv_times,
+ ARRAY_SIZE(tsc1641_conv_times));
+
+ return FIELD_PREP(TSC1641_CONV_TIME_MASK, conv_bits);
+}
+
+static int tsc1641_chip_write(struct device *dev, u32 attr, long val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return regmap_update_bits(data->regmap, TSC1641_CONFIG,
+ TSC1641_CONV_TIME_MASK,
+ tsc1641_upd_interval_to_reg(val));
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int tsc1641_chip_read(struct device *dev, u32 attr, long *val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ u32 regval;
+ int ret;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ ret = regmap_read(data->regmap, TSC1641_CONFIG, &regval);
+ if (ret)
+ return ret;
+
+ *val = tsc1641_reg_to_upd_interval(regval);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int tsc1641_flag_read(struct regmap *regmap, u32 flag, long *val)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read_bypassed(regmap, TSC1641_FLAG, &regval);
+ if (ret)
+ return ret;
+
+ *val = !!(regval & flag);
+ return 0;
+}
+
+static int tsc1641_in_read(struct device *dev, u32 attr, long *val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int ret, reg;
+ long sat_flag;
+
+ switch (attr) {
+ case hwmon_in_input:
+ reg = TSC1641_LOAD_VOLTAGE;
+ break;
+ case hwmon_in_min:
+ reg = TSC1641_LUL;
+ break;
+ case hwmon_in_max:
+ reg = TSC1641_LOL;
+ break;
+ case hwmon_in_min_alarm:
+ return tsc1641_flag_read(regmap, TSC1641_LOAD_UV_FLAG, val);
+ case hwmon_in_max_alarm:
+ return tsc1641_flag_read(regmap, TSC1641_LOAD_OV_FLAG, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = regmap_read(regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ /* Check if load voltage is out of range */
+ if (reg == TSC1641_LOAD_VOLTAGE) {
+ /* Register is 15-bit max */
+ if (regval & 0x8000)
+ return -ENODATA;
+
+ ret = tsc1641_flag_read(regmap, TSC1641_SAT_FLAG, &sat_flag);
+ if (ret)
+ return ret;
+ /* Out of range conditions per datasheet */
+ if (sat_flag && (regval == 0x7FFF || !regval))
+ return -ENODATA;
+ }
+
+ *val = regval * TSC1641_VLOAD_LSB_MVOLT;
+ return 0;
+}
+
+/* Chip supports bidirectional (positive or negative) current */
+static int tsc1641_curr_read(struct device *dev, u32 attr, long *val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ int regval;
+ int ret, reg;
+ long sat_flag;
+
+ /* Current limits are the shunt under/over voltage limits */
+ switch (attr) {
+ case hwmon_curr_input:
+ reg = TSC1641_CURRENT;
+ break;
+ case hwmon_curr_min:
+ reg = TSC1641_SUL;
+ break;
+ case hwmon_curr_max:
+ reg = TSC1641_SOL;
+ break;
+ case hwmon_curr_min_alarm:
+ return tsc1641_flag_read(regmap, TSC1641_SHUNT_UV_FLAG, val);
+ case hwmon_curr_max_alarm:
+ return tsc1641_flag_read(regmap, TSC1641_SHUNT_OV_FLAG, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ /*
+ * Current uses shunt voltage, so check if it's out of range.
+ * We report current register in sysfs to stay consistent with internal
+ * power calculations which use current register values
+ */
+ if (reg == TSC1641_CURRENT) {
+ ret = regmap_read(regmap, TSC1641_SHUNT_VOLTAGE, &regval);
+ if (ret)
+ return ret;
+
+ ret = tsc1641_flag_read(regmap, TSC1641_SAT_FLAG, &sat_flag);
+ if (ret)
+ return ret;
+
+ if (sat_flag && (regval == 0x7FFF || regval == 0x8000))
+ return -ENODATA;
+ }
+
+ ret = regmap_read(regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ /* Current in milliamps, signed */
+ *val = DIV_ROUND_CLOSEST((s16)regval * data->current_lsb_ua, 1000);
+ return 0;
+}
+
+static int tsc1641_power_read(struct device *dev, u32 attr, long *val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int ret, reg;
+
+ switch (attr) {
+ case hwmon_power_input:
+ reg = TSC1641_POWER;
+ break;
+ case hwmon_power_max:
+ reg = TSC1641_POL;
+ break;
+ case hwmon_power_max_alarm:
+ return tsc1641_flag_read(regmap, TSC1641_POWER_OVER_FLAG, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = regmap_read(regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ *val = regval * TSC1641_POWER_LSB_UWATT;
+ return 0;
+}
+
+static int tsc1641_temp_read(struct device *dev, u32 attr, long *val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int ret, reg;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ reg = TSC1641_TEMP;
+ break;
+ case hwmon_temp_max:
+ reg = TSC1641_TOL;
+ break;
+ case hwmon_temp_max_alarm:
+ return tsc1641_flag_read(regmap, TSC1641_TEMP_OVER_FLAG, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = regmap_read(regmap, reg, &regval);
+ if (ret)
+ return ret;
+
+ /* 0x8000 means that TEMP measurement not enabled */
+ if (reg == TSC1641_TEMP && regval == 0x8000)
+ return -ENODATA;
+
+ /* Both temperature and limit registers are signed */
+ *val = (s16)regval * TSC1641_TEMP_LSB_MDEGC;
+ return 0;
+}
+
+static int tsc1641_in_write(struct device *dev, u32 attr, long val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int reg;
+
+ switch (attr) {
+ case hwmon_in_min:
+ reg = TSC1641_LUL;
+ break;
+ case hwmon_in_max:
+ reg = TSC1641_LOL;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ /* Clamp to full register range */
+ val = clamp_val(val, 0, TSC1641_VLOAD_LSB_MVOLT * USHRT_MAX);
+ regval = DIV_ROUND_CLOSEST(val, TSC1641_VLOAD_LSB_MVOLT);
+
+ return regmap_write(regmap, reg, regval);
+}
+
+static int tsc1641_curr_write(struct device *dev, u32 attr, long val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ int reg, regval;
+
+ switch (attr) {
+ case hwmon_curr_min:
+ reg = TSC1641_SUL;
+ break;
+ case hwmon_curr_max:
+ reg = TSC1641_SOL;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ /* Clamp to prevent over/underflow below */
+ val = clamp_val(val, -TSC1641_CURR_ABS_MAX_MAMP, TSC1641_CURR_ABS_MAX_MAMP);
+ /* Convert val in milliamps to register */
+ regval = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_ua);
+ /*
+ * Prevent signed 16-bit overflow.
+ * Integer arithmetic and shunt scaling can quantize values near 0x7FFF/0x8000,
+ * so reading and writing back may not preserve the exact original register value.
+ */
+ regval = clamp_val(regval, SHRT_MIN, SHRT_MAX);
+ /* SUL and SOL registers are signed */
+ return regmap_write(regmap, reg, regval & 0xFFFF);
+}
+
+static int tsc1641_power_write(struct device *dev, u32 attr, long val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+
+ switch (attr) {
+ case hwmon_power_max:
+ /* Clamp to full register range */
+ val = clamp_val(val, 0, TSC1641_POWER_LSB_UWATT * USHRT_MAX);
+ regval = DIV_ROUND_CLOSEST(val, TSC1641_POWER_LSB_UWATT);
+ return regmap_write(regmap, TSC1641_POL, regval);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int tsc1641_temp_write(struct device *dev, u32 attr, long val)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ int regval;
+
+ switch (attr) {
+ case hwmon_temp_max:
+ /* Clamp to full register range */
+ val = clamp_val(val, TSC1641_TEMP_LSB_MDEGC * SHRT_MIN,
+ TSC1641_TEMP_LSB_MDEGC * SHRT_MAX);
+ regval = DIV_ROUND_CLOSEST(val, TSC1641_TEMP_LSB_MDEGC);
+ /* TOL register is signed */
+ return regmap_write(regmap, TSC1641_TOL, regval & 0xFFFF);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t tsc1641_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ return 0444;
+ case hwmon_in_min:
+ case hwmon_in_max:
+ return 0644;
+ case hwmon_in_min_alarm:
+ case hwmon_in_max_alarm:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ return 0444;
+ case hwmon_curr_min:
+ case hwmon_curr_max:
+ return 0644;
+ case hwmon_curr_min_alarm:
+ case hwmon_curr_max_alarm:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ return 0444;
+ case hwmon_power_max:
+ return 0644;
+ case hwmon_power_max_alarm:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ case hwmon_temp_max:
+ return 0644;
+ case hwmon_temp_max_alarm:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int tsc1641_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return tsc1641_chip_read(dev, attr, val);
+ case hwmon_in:
+ return tsc1641_in_read(dev, attr, val);
+ case hwmon_curr:
+ return tsc1641_curr_read(dev, attr, val);
+ case hwmon_power:
+ return tsc1641_power_read(dev, attr, val);
+ case hwmon_temp:
+ return tsc1641_temp_read(dev, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int tsc1641_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return tsc1641_chip_write(dev, attr, val);
+ case hwmon_in:
+ return tsc1641_in_write(dev, attr, val);
+ case hwmon_curr:
+ return tsc1641_curr_write(dev, attr, val);
+ case hwmon_power:
+ return tsc1641_power_write(dev, attr, val);
+ case hwmon_temp:
+ return tsc1641_temp_write(dev, attr, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static const struct hwmon_channel_info * const tsc1641_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MAX_ALARM |
+ HWMON_I_MIN | HWMON_I_MIN_ALARM),
+ HWMON_CHANNEL_INFO(curr,
+ HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_MAX_ALARM |
+ HWMON_C_MIN | HWMON_C_MIN_ALARM),
+ HWMON_CHANNEL_INFO(power,
+ HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_MAX_ALARM),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_ALARM),
+ NULL
+};
+
+static ssize_t shunt_resistor_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%li\n", data->rshunt_uohm);
+}
+
+static ssize_t shunt_resistor_store(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct tsc1641_data *data = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ ret = kstrtouint(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = tsc1641_validate_shunt(val);
+ if (ret < 0)
+ return ret;
+
+ ret = tsc1641_set_shunt(data, val);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static const struct hwmon_ops tsc1641_hwmon_ops = {
+ .is_visible = tsc1641_is_visible,
+ .read = tsc1641_read,
+ .write = tsc1641_write,
+};
+
+static const struct hwmon_chip_info tsc1641_chip_info = {
+ .ops = &tsc1641_hwmon_ops,
+ .info = tsc1641_info,
+};
+
+static DEVICE_ATTR_RW(shunt_resistor);
+
+/* Shunt resistor value is exposed via sysfs attribute */
+static struct attribute *tsc1641_attrs[] = {
+ &dev_attr_shunt_resistor.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(tsc1641);
+
+static int tsc1641_init(struct device *dev, struct tsc1641_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ bool active_high;
+ u32 shunt;
+ int ret;
+
+ if (device_property_read_u32(dev, "shunt-resistor-micro-ohms", &shunt) < 0)
+ shunt = TSC1641_RSHUNT_DEFAULT;
+
+ if (tsc1641_validate_shunt(shunt) < 0) {
+ dev_err(dev, "invalid shunt resistor value %u\n", shunt);
+ return -EINVAL;
+ }
+
+ ret = tsc1641_set_shunt(data, shunt);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(regmap, TSC1641_CONFIG, TSC1641_CONFIG_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ active_high = device_property_read_bool(dev, "st,alert-polarity-active-high");
+
+ return regmap_write(regmap, TSC1641_MASK, TSC1641_MASK_DEFAULT |
+ FIELD_PREP(TSC1641_ALERT_POL_MASK, active_high));
+}
+
+static int tsc1641_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct tsc1641_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->regmap = devm_regmap_init_i2c(client, &tsc1641_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(dev, PTR_ERR(data->regmap),
+ "failed to allocate register map\n");
+
+ ret = tsc1641_init(dev, data);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to configure device\n");
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &tsc1641_chip_info, tsc1641_groups);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
+
+ dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n",
+ client->name, data->rshunt_uohm);
+
+ return 0;
+}
+
+static const struct i2c_device_id tsc1641_id[] = {
+ { "tsc1641", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tsc1641_id);
+
+static const struct of_device_id __maybe_unused tsc1641_of_match[] = {
+ { .compatible = "st,tsc1641" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tsc1641_of_match);
+
+static struct i2c_driver tsc1641_driver = {
+ .driver = {
+ .name = "tsc1641",
+ .of_match_table = of_match_ptr(tsc1641_of_match),
+ },
+ .probe = tsc1641_probe,
+ .id_table = tsc1641_id,
+};
+
+module_i2c_driver(tsc1641_driver);
+
+MODULE_AUTHOR("Igor Reznichenko <igor@reznichenko.net>");
+MODULE_DESCRIPTION("tsc1641 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c
index 386edea6b69e..1e52cabd6e24 100644
--- a/drivers/hwmon/vt1211.c
+++ b/drivers/hwmon/vt1211.c
@@ -142,9 +142,15 @@ struct vt1211_data {
* in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the
* driver according to the VT1211 BIOS porting guide
*/
-#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \
- (((reg) - 3) * 15882 + 479) / 958 : \
- (((reg) - 3) * 10000 + 479) / 958)
+static int in_from_reg(int ix, int reg)
+{
+ if (reg < 3)
+ return 0;
+ if (ix == 5)
+ return ((reg - 3) * 15882 + 479) / 958;
+ return ((reg - 3) * 10000 + 479) / 958;
+}
+
#define IN_TO_REG(ix, val) (clamp_val((ix) == 5 ? \
((val) * 958 + 7941) / 15882 + 3 : \
((val) * 958 + 5000) / 10000 + 3, 0, 255))
@@ -156,10 +162,15 @@ struct vt1211_data {
* temp3-7 are thermistor based so the driver returns the voltage measured at
* the pin (range 0V - 2.2V).
*/
-#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \
- (ix) == 1 ? (reg) < 51 ? 0 : \
- ((reg) - 51) * 1000 : \
- ((253 - (reg)) * 2200 + 105) / 210)
+static int temp_from_reg(int ix, int reg)
+{
+ if (ix == 0)
+ return reg * 1000;
+ if (ix == 1)
+ return reg < 51 ? 0 : (reg - 51) * 1000;
+ return ((253 - reg) * 2200 + 105) / 210;
+}
+
#define TEMP_TO_REG(ix, val) clamp_val( \
((ix) == 0 ? ((val) + 500) / 1000 : \
(ix) == 1 ? ((val) + 500) / 1000 + 51 : \
@@ -167,8 +178,14 @@ struct vt1211_data {
#define DIV_FROM_REG(reg) (1 << (reg))
-#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \
- 1310720 / (reg) / DIV_FROM_REG(div))
+static int rpm_from_reg(int reg, int div)
+{
+ if (reg == 0 || reg == 255)
+ return 0;
+
+ return 1310720 / reg / DIV_FROM_REG(div);
+}
+
#define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \
clamp_val((1310720 / (val) / \
DIV_FROM_REG(div)), 1, 254))
@@ -343,13 +360,13 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
switch (fn) {
case SHOW_IN_INPUT:
- res = IN_FROM_REG(ix, data->in[ix]);
+ res = in_from_reg(ix, data->in[ix]);
break;
case SHOW_SET_IN_MIN:
- res = IN_FROM_REG(ix, data->in_min[ix]);
+ res = in_from_reg(ix, data->in_min[ix]);
break;
case SHOW_SET_IN_MAX:
- res = IN_FROM_REG(ix, data->in_max[ix]);
+ res = in_from_reg(ix, data->in_max[ix]);
break;
case SHOW_IN_ALARM:
res = (data->alarms >> bitalarmin[ix]) & 1;
@@ -417,13 +434,13 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
switch (fn) {
case SHOW_TEMP_INPUT:
- res = TEMP_FROM_REG(ix, data->temp[ix]);
+ res = temp_from_reg(ix, data->temp[ix]);
break;
case SHOW_SET_TEMP_MAX:
- res = TEMP_FROM_REG(ix, data->temp_max[ix]);
+ res = temp_from_reg(ix, data->temp_max[ix]);
break;
case SHOW_SET_TEMP_MAX_HYST:
- res = TEMP_FROM_REG(ix, data->temp_hyst[ix]);
+ res = temp_from_reg(ix, data->temp_hyst[ix]);
break;
case SHOW_TEMP_ALARM:
res = (data->alarms >> bitalarmtemp[ix]) & 1;
@@ -493,10 +510,10 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
switch (fn) {
case SHOW_FAN_INPUT:
- res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]);
+ res = rpm_from_reg(data->fan[ix], data->fan_div[ix]);
break;
case SHOW_SET_FAN_MIN:
- res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]);
+ res = rpm_from_reg(data->fan_min[ix], data->fan_div[ix]);
break;
case SHOW_SET_FAN_DIV:
res = DIV_FROM_REG(data->fan_div[ix]);
@@ -751,7 +768,7 @@ static ssize_t show_pwm_auto_point_temp(struct device *dev,
int ix = sensor_attr_2->index;
int ap = sensor_attr_2->nr;
- return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7,
+ return sprintf(buf, "%d\n", temp_from_reg(data->pwm_ctl[ix] & 7,
data->pwm_auto_temp[ap]));
}
diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c
index 3bf27c21845b..5757a0979f3f 100644
--- a/drivers/hwmon/vt8231.c
+++ b/drivers/hwmon/vt8231.c
@@ -138,7 +138,12 @@ static inline u8 FAN_TO_REG(long rpm, int div)
return clamp_val(1310720 / (rpm * div), 1, 255);
}
-#define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : 1310720 / ((val) * (div)))
+static int fan_from_reg(int val, int div)
+{
+ if (val == 0)
+ return 0;
+ return 1310720 / (val * div);
+}
struct vt8231_data {
unsigned short addr;
@@ -561,7 +566,7 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
struct vt8231_data *data = vt8231_update_device(dev);
- return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr],
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr],
DIV_FROM_REG(data->fan_div[nr])));
}
@@ -571,7 +576,7 @@ static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
int nr = sensor_attr->index;
struct vt8231_data *data = vt8231_update_device(dev);
- return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr],
+ return sprintf(buf, "%d\n", fan_from_reg(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr])));
}
@@ -613,9 +618,8 @@ static ssize_t fan_div_store(struct device *dev,
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
unsigned long val;
int nr = sensor_attr->index;
- int old = vt8231_read_value(data, VT8231_REG_FANDIV);
- long min = FAN_FROM_REG(data->fan_min[nr],
- DIV_FROM_REG(data->fan_div[nr]));
+ int old;
+ long min;
int err;
err = kstrtoul(buf, 10, &val);
@@ -623,6 +627,8 @@ static ssize_t fan_div_store(struct device *dev,
return err;
mutex_lock(&data->update_lock);
+ old = vt8231_read_value(data, VT8231_REG_FANDIV);
+ min = fan_from_reg(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
switch (val) {
case 1:
data->fan_div[nr] = 0;
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index 076200ed2ec9..f664c2152a6d 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -1850,10 +1850,12 @@ w83781d_isa_found(unsigned short address)
}
}
-#define REALLY_SLOW_IO
/*
* We need the timeouts for at least some W83781D-like
* chips. But only if we read 'undefined' registers.
+ * There used to be a "#define REALLY_SLOW_IO" to enforce that, but
+ * this has been without any effect since more than a decade, so it
+ * has been dropped.
*/
val = inb_p(address + 1);
if (inb_p(address + 2) != val
@@ -1862,7 +1864,6 @@ w83781d_isa_found(unsigned short address)
pr_debug("Detection failed at step %d\n", 1);
goto release;
}
-#undef REALLY_SLOW_IO
/*
* We should be able to change the 7 LSB of the address port. The
diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c
index 9b81bd406e05..1d9109ca1585 100644
--- a/drivers/hwmon/w83l786ng.c
+++ b/drivers/hwmon/w83l786ng.c
@@ -76,15 +76,25 @@ FAN_TO_REG(long rpm, int div)
return clamp_val((1350000 + rpm * div / 2) / (rpm * div), 1, 254);
}
-#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 : \
- ((val) == 255 ? 0 : \
- 1350000 / ((val) * (div))))
+static int fan_from_reg(int val, int div)
+{
+ if (val == 0)
+ return -1;
+ if (val == 255)
+ return 0;
+ return 1350000 / (val * div);
+}
/* for temp */
#define TEMP_TO_REG(val) (clamp_val(((val) < 0 ? (val) + 0x100 * 1000 \
: (val)) / 1000, 0, 0xff))
-#define TEMP_FROM_REG(val) (((val) & 0x80 ? \
- (val) - 0x100 : (val)) * 1000)
+
+static int temp_from_reg(int val)
+{
+ if (val & 0x80)
+ return (val - 0x100) * 1000;
+ return val * 1000;
+}
/*
* The analog voltage inputs have 8mV LSB. Since the sysfs output is
@@ -280,7 +290,7 @@ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
int nr = to_sensor_dev_attr(attr)->index; \
struct w83l786ng_data *data = w83l786ng_update_device(dev); \
return sprintf(buf, "%d\n", \
- FAN_FROM_REG(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
+ fan_from_reg(data->reg[nr], DIV_FROM_REG(data->fan_div[nr]))); \
}
show_fan_reg(fan);
@@ -347,7 +357,7 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
/* Save fan_min */
mutex_lock(&data->update_lock);
- min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
+ min = fan_from_reg(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
data->fan_div[nr] = DIV_TO_REG(val);
@@ -409,7 +419,7 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sensor_attr->nr;
int index = sensor_attr->index;
struct w83l786ng_data *data = w83l786ng_update_device(dev);
- return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr][index]));
+ return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr][index]));
}
static ssize_t
diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c
index 43643d2c5bdd..9f64f463339d 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.c
+++ b/drivers/hwtracing/coresight/coresight-trbe.c
@@ -1474,9 +1474,10 @@ static void arm_trbe_remove_cpuhp(struct trbe_drvdata *drvdata)
static int arm_trbe_probe_irq(struct platform_device *pdev,
struct trbe_drvdata *drvdata)
{
+ const struct cpumask *affinity;
int ret;
- drvdata->irq = platform_get_irq(pdev, 0);
+ drvdata->irq = platform_get_irq_affinity(pdev, 0, &affinity);
if (drvdata->irq < 0) {
pr_err("IRQ not found for the platform device\n");
return drvdata->irq;
@@ -1487,14 +1488,14 @@ static int arm_trbe_probe_irq(struct platform_device *pdev,
return -EINVAL;
}
- if (irq_get_percpu_devid_partition(drvdata->irq, &drvdata->supported_cpus))
- return -EINVAL;
+ cpumask_copy(&drvdata->supported_cpus, affinity);
drvdata->handle = alloc_percpu(struct perf_output_handle *);
if (!drvdata->handle)
return -ENOMEM;
- ret = request_percpu_irq(drvdata->irq, arm_trbe_irq_handler, DRVNAME, drvdata->handle);
+ ret = request_percpu_irq_affinity(drvdata->irq, arm_trbe_irq_handler, DRVNAME,
+ affinity, drvdata->handle);
if (ret) {
free_percpu(drvdata->handle);
return ret;
diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 75c8d08fa24e..b9f370c9f018 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -118,6 +118,7 @@ struct pca954x {
raw_spinlock_t lock;
struct regulator *supply;
+ struct gpio_desc *reset_gpio;
struct reset_control *reset_cont;
};
@@ -315,25 +316,6 @@ static u8 pca954x_regval(struct pca954x *data, u8 chan)
return 1 << chan;
}
-static void pca954x_reset_assert(struct pca954x *data)
-{
- if (data->reset_cont)
- reset_control_assert(data->reset_cont);
-}
-
-static void pca954x_reset_deassert(struct pca954x *data)
-{
- if (data->reset_cont)
- reset_control_deassert(data->reset_cont);
-}
-
-static void pca954x_reset_mux(struct pca954x *data)
-{
- pca954x_reset_assert(data);
- udelay(1);
- pca954x_reset_deassert(data);
-}
-
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
{
struct pca954x *data = i2c_mux_priv(muxc);
@@ -347,8 +329,6 @@ static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
ret = pca954x_reg_write(muxc->parent, client, regval);
data->last_chan = ret < 0 ? 0 : regval;
}
- if (ret == -ETIMEDOUT && data->reset_cont)
- pca954x_reset_mux(data);
return ret;
}
@@ -358,7 +338,6 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
struct pca954x *data = i2c_mux_priv(muxc);
struct i2c_client *client = data->client;
s32 idle_state;
- int ret = 0;
idle_state = READ_ONCE(data->idle_state);
if (idle_state >= 0)
@@ -368,10 +347,8 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
if (idle_state == MUX_IDLE_DISCONNECT) {
/* Deselect active channel */
data->last_chan = 0;
- ret = pca954x_reg_write(muxc->parent, client,
- data->last_chan);
- if (ret == -ETIMEDOUT && data->reset_cont)
- pca954x_reset_mux(data);
+ return pca954x_reg_write(muxc->parent, client,
+ data->last_chan);
}
/* otherwise leave as-is */
@@ -550,10 +527,29 @@ static int pca954x_get_reset(struct device *dev, struct pca954x *data)
if (IS_ERR(data->reset_cont))
return dev_err_probe(dev, PTR_ERR(data->reset_cont),
"Failed to get reset\n");
+ else if (data->reset_cont)
+ return 0;
+
+ /*
+ * fallback to legacy reset-gpios
+ */
+ data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(data->reset_gpio)) {
+ return dev_err_probe(dev, PTR_ERR(data->reset_gpio),
+ "Failed to get reset gpio");
+ }
return 0;
}
+static void pca954x_reset_deassert(struct pca954x *data)
+{
+ if (data->reset_cont)
+ reset_control_deassert(data->reset_cont);
+ else
+ gpiod_set_value_cansleep(data->reset_gpio, 0);
+}
+
/*
* I2C init/probing/exit functions
*/
@@ -593,7 +589,7 @@ static int pca954x_probe(struct i2c_client *client)
if (ret)
goto fail_cleanup;
- if (data->reset_cont) {
+ if (data->reset_cont || data->reset_gpio) {
udelay(1);
pca954x_reset_deassert(data);
/* Give the chip some time to recover. */
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 2e00fd51b4d5..5fc7f814b907 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -56,6 +56,8 @@
#define ADXL355_POWER_CTL_DRDY_MSK BIT(2)
#define ADXL355_SELF_TEST_REG 0x2E
#define ADXL355_RESET_REG 0x2F
+#define ADXL355_BASE_ADDR_SHADOW_REG 0x50
+#define ADXL355_SHADOW_REG_COUNT 5
#define ADXL355_DEVID_AD_VAL 0xAD
#define ADXL355_DEVID_MST_VAL 0x1D
@@ -294,7 +296,12 @@ static void adxl355_fill_3db_frequency_table(struct adxl355_data *data)
static int adxl355_setup(struct adxl355_data *data)
{
unsigned int regval;
+ int retries = 5; /* the number is chosen based on empirical reasons */
int ret;
+ u8 *shadow_regs __free(kfree) = kzalloc(ADXL355_SHADOW_REG_COUNT, GFP_KERNEL);
+
+ if (!shadow_regs)
+ return -ENOMEM;
ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, &regval);
if (ret)
@@ -321,14 +328,41 @@ static int adxl355_setup(struct adxl355_data *data)
if (regval != ADXL355_PARTID_VAL)
dev_warn(data->dev, "Invalid DEV ID 0x%02x\n", regval);
- /*
- * Perform a software reset to make sure the device is in a consistent
- * state after start-up.
- */
- ret = regmap_write(data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE);
+ /* Read shadow registers to be compared after reset */
+ ret = regmap_bulk_read(data->regmap,
+ ADXL355_BASE_ADDR_SHADOW_REG,
+ shadow_regs, ADXL355_SHADOW_REG_COUNT);
if (ret)
return ret;
+ do {
+ if (--retries == 0) {
+ dev_err(data->dev, "Shadow registers mismatch\n");
+ return -EIO;
+ }
+
+ /*
+ * Perform a software reset to make sure the device is in a consistent
+ * state after start-up.
+ */
+ ret = regmap_write(data->regmap, ADXL355_RESET_REG,
+ ADXL355_RESET_CODE);
+ if (ret)
+ return ret;
+
+ /* Wait at least 5ms after software reset */
+ usleep_range(5000, 10000);
+
+ /* Read shadow registers for comparison */
+ ret = regmap_bulk_read(data->regmap,
+ ADXL355_BASE_ADDR_SHADOW_REG,
+ data->buffer.buf,
+ ADXL355_SHADOW_REG_COUNT);
+ if (ret)
+ return ret;
+ } while (memcmp(shadow_regs, data->buffer.buf,
+ ADXL355_SHADOW_REG_COUNT));
+
ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG,
ADXL355_POWER_CTL_DRDY_MSK,
FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, 1));
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 3c5d1560b163..42ccf0316ce5 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -523,6 +523,10 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
const struct bmc150_accel_interrupt_info *info = intr->info;
int ret;
+ /* We do not always have an IRQ */
+ if (data->irq <= 0)
+ return 0;
+
if (state) {
if (atomic_inc_return(&intr->users) > 1)
return 0;
@@ -1696,6 +1700,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
}
if (irq > 0) {
+ data->irq = irq;
ret = devm_request_threaded_irq(dev, irq,
bmc150_accel_irq_handler,
bmc150_accel_irq_thread_handler,
diff --git a/drivers/iio/accel/bmc150-accel.h b/drivers/iio/accel/bmc150-accel.h
index 7a7baf52e595..e8f26198359f 100644
--- a/drivers/iio/accel/bmc150-accel.h
+++ b/drivers/iio/accel/bmc150-accel.h
@@ -58,6 +58,7 @@ enum bmc150_accel_trigger_id {
struct bmc150_accel_data {
struct regmap *regmap;
+ int irq;
struct regulator_bulk_data regulators[2];
struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS];
struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS];
diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
index 1bc2f9a22470..d8bee6a4215a 100644
--- a/drivers/iio/adc/ad4030.c
+++ b/drivers/iio/adc/ad4030.c
@@ -385,7 +385,7 @@ static int ad4030_get_chan_scale(struct iio_dev *indio_dev,
struct ad4030_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
- scan_type = iio_get_current_scan_type(indio_dev, st->chip->channels);
+ scan_type = iio_get_current_scan_type(indio_dev, chan);
if (IS_ERR(scan_type))
return PTR_ERR(scan_type);
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index 910b40393f77..61623cc6cb25 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -1525,10 +1525,6 @@ static int __ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio
int ret, i;
for (i = 0; i < st->num_channels; i++) {
-
- if (indio_dev->channels[i].type != IIO_VOLTAGE)
- continue;
-
/*
* For calibration the OFFSET register should hold its reset default
* value. For the GAIN register there is no such requirement but
@@ -1539,6 +1535,14 @@ static int __ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio
st->channels[i].cfg.calibration_gain = st->gain_default;
/*
+ * Only the main voltage input channels are important enough
+ * to be automatically calibrated here. For everything else,
+ * just use the default values set above.
+ */
+ if (indio_dev->channels[i].type != IIO_VOLTAGE)
+ continue;
+
+ /*
* Full-scale calibration isn't supported at gain 1, so skip in
* that case. Note that untypically full-scale calibration has
* to happen before zero-scale calibration. This only applies to
diff --git a/drivers/iio/adc/ad7280a.c b/drivers/iio/adc/ad7280a.c
index dda2986ccda0..50a6ff7c8b1c 100644
--- a/drivers/iio/adc/ad7280a.c
+++ b/drivers/iio/adc/ad7280a.c
@@ -541,7 +541,7 @@ static ssize_t ad7280_store_balance_timer(struct iio_dev *indio_dev,
int val, val2;
int ret;
- ret = iio_str_to_fixpoint(buf, 1000, &val, &val2);
+ ret = iio_str_to_fixpoint(buf, 100, &val, &val2);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7380.c b/drivers/iio/adc/ad7380.c
index fa251dc1aae6..bfd908deefc0 100644
--- a/drivers/iio/adc/ad7380.c
+++ b/drivers/iio/adc/ad7380.c
@@ -1227,6 +1227,14 @@ static int ad7380_offload_buffer_postenable(struct iio_dev *indio_dev)
if (ret)
return ret;
+ /*
+ * When the sequencer is required to read all channels, we need to
+ * trigger twice per sample period in order to read one complete set
+ * of samples.
+ */
+ if (st->seq)
+ config.periodic.frequency_hz *= 2;
+
ret = spi_offload_trigger_enable(st->offload, st->offload_trigger, &config);
if (ret)
spi_unoptimize_message(&st->offload_msg);
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
index ad9738228b7f..2bf3a09ac6b0 100644
--- a/drivers/iio/adc/rtq6056.c
+++ b/drivers/iio/adc/rtq6056.c
@@ -300,7 +300,7 @@ static int rtq6056_adc_read_channel(struct rtq6056_priv *priv,
return IIO_VAL_INT;
case RTQ6056_REG_SHUNTVOLT:
case RTQ6056_REG_CURRENT:
- *val = sign_extend32(regval, 16);
+ *val = sign_extend32(regval, 15);
return IIO_VAL_INT;
default:
return -EINVAL;
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 74b1b4dc6e81..9664b9bd75d4 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -725,9 +725,8 @@ static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm,
}
df_ch->src = val;
- ret = fwnode_property_read_u32(node, "st,adc-alt-channel", &df_ch->alt_si);
- if (ret != -EINVAL)
- df_ch->alt_si = 0;
+ if (fwnode_property_present(node, "st,adc-alt-channel"))
+ df_ch->alt_si = 1;
if (adc->dev_data->type == DFSDM_IIO) {
backend = devm_iio_backend_fwnode_get(&indio_dev->dev, NULL, node);
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index ee294a775e8a..7a7a9d37339b 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -786,6 +786,12 @@ out_end_signalling:
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_enqueue_dmabuf, "IIO_DMA_BUFFER");
+struct device *iio_dma_buffer_get_dma_dev(struct iio_buffer *buffer)
+{
+ return iio_buffer_to_queue(buffer)->dev;
+}
+EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_get_dma_dev, "IIO_DMA_BUFFER");
+
void iio_dma_buffer_lock_queue(struct iio_buffer *buffer)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index e9d9a7d39fe1..27dd56334345 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -177,6 +177,8 @@ static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
.lock_queue = iio_dma_buffer_lock_queue,
.unlock_queue = iio_dma_buffer_unlock_queue,
+ .get_dma_dev = iio_dma_buffer_get_dma_dev,
+
.modes = INDIO_BUFFER_HARDWARE,
.flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
};
diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c
index 1e167dc673ca..da09c9f3ceb6 100644
--- a/drivers/iio/common/ssp_sensors/ssp_dev.c
+++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
@@ -503,7 +503,7 @@ static int ssp_probe(struct spi_device *spi)
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "Failed to setup spi\n");
- return ret;
+ goto err_setup_spi;
}
data->fw_dl_state = SSP_FW_DL_STATE_NONE;
@@ -568,6 +568,8 @@ err_read_reg:
err_setup_irq:
mutex_destroy(&data->pending_lock);
mutex_destroy(&data->comm_lock);
+err_setup_spi:
+ mfd_remove_devices(&spi->dev);
dev_err(&spi->dev, "Probe failed!\n");
diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c
index ffb25596d3a8..78b2c171c8da 100644
--- a/drivers/iio/humidity/hdc3020.c
+++ b/drivers/iio/humidity/hdc3020.c
@@ -72,6 +72,9 @@
#define HDC3020_MAX_TEMP_HYST_MICRO 164748607
#define HDC3020_MAX_HUM_MICRO 99220264
+/* Divide 65535 from the datasheet by 5 to avoid overflows */
+#define HDC3020_THRESH_FRACTION (65535 / 5)
+
struct hdc3020_data {
struct i2c_client *client;
struct gpio_desc *reset_gpio;
@@ -301,9 +304,9 @@ static int hdc3020_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
*val2 = 65536;
if (chan->type == IIO_TEMP)
- *val = 175;
+ *val = 175 * MILLI;
else
- *val = 100;
+ *val = 100 * MILLI;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_OFFSET:
@@ -376,15 +379,18 @@ static int hdc3020_thresh_get_temp(u16 thresh)
int temp;
/*
- * Get the temperature threshold from 9 LSBs, shift them to get
- * the truncated temperature threshold representation and
- * calculate the threshold according to the formula in the
- * datasheet. Result is degree celsius scaled by 65535.
+ * Get the temperature threshold from 9 LSBs, shift them to get the
+ * truncated temperature threshold representation and calculate the
+ * threshold according to the explicit formula in the datasheet:
+ * T(C) = -45 + (175 * temp) / 65535.
+ * Additionally scale by HDC3020_THRESH_FRACTION to avoid precision loss
+ * when calculating threshold and hysteresis values. Result is degree
+ * celsius scaled by HDC3020_THRESH_FRACTION.
*/
temp = FIELD_GET(HDC3020_THRESH_TEMP_MASK, thresh) <<
HDC3020_THRESH_TEMP_TRUNC_SHIFT;
- return -2949075 + (175 * temp);
+ return -2949075 / 5 + (175 / 5 * temp);
}
static int hdc3020_thresh_get_hum(u16 thresh)
@@ -394,13 +400,16 @@ static int hdc3020_thresh_get_hum(u16 thresh)
/*
* Get the humidity threshold from 7 MSBs, shift them to get the
* truncated humidity threshold representation and calculate the
- * threshold according to the formula in the datasheet. Result is
- * percent scaled by 65535.
+ * threshold according to the explicit formula in the datasheet:
+ * RH(%) = 100 * hum / 65535.
+ * Additionally scale by HDC3020_THRESH_FRACTION to avoid precision loss
+ * when calculating threshold and hysteresis values. Result is percent
+ * scaled by HDC3020_THRESH_FRACTION.
*/
hum = FIELD_GET(HDC3020_THRESH_HUM_MASK, thresh) <<
HDC3020_THRESH_HUM_TRUNC_SHIFT;
- return hum * 100;
+ return hum * 100 / 5;
}
static u16 hdc3020_thresh_set_temp(int s_temp, u16 curr_thresh)
@@ -455,8 +464,8 @@ int hdc3020_thresh_clr(s64 s_thresh, s64 s_hyst, enum iio_event_direction dir)
else
s_clr = s_thresh + s_hyst;
- /* Divide by 65535 to get units of micro */
- return div_s64(s_clr, 65535);
+ /* Divide by HDC3020_THRESH_FRACTION to get units of micro */
+ return div_s64(s_clr, HDC3020_THRESH_FRACTION);
}
static int _hdc3020_write_thresh(struct hdc3020_data *data, u16 reg, u16 val)
@@ -507,7 +516,7 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
clr = ret;
/* Scale value to include decimal part into calculations */
- s_val = (val < 0) ? (val * 1000000 - val2) : (val * 1000000 + val2);
+ s_val = (val < 0) ? (val * 1000 - val2) : (val * 1000 + val2);
switch (chan->type) {
case IIO_TEMP:
switch (info) {
@@ -523,7 +532,8 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
/* Calculate old hysteresis */
s_thresh = (s64)hdc3020_thresh_get_temp(thresh) * 1000000;
s_clr = (s64)hdc3020_thresh_get_temp(clr) * 1000000;
- s_hyst = div_s64(abs(s_thresh - s_clr), 65535);
+ s_hyst = div_s64(abs(s_thresh - s_clr),
+ HDC3020_THRESH_FRACTION);
/* Set new threshold */
thresh = reg_val;
/* Set old hysteresis */
@@ -532,16 +542,17 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
case IIO_EV_INFO_HYSTERESIS:
/*
* Function hdc3020_thresh_get_temp returns temperature
- * in degree celsius scaled by 65535. Scale by 1000000
- * to be able to subtract scaled hysteresis value.
+ * in degree celsius scaled by HDC3020_THRESH_FRACTION.
+ * Scale by 1000000 to be able to subtract scaled
+ * hysteresis value.
*/
s_thresh = (s64)hdc3020_thresh_get_temp(thresh) * 1000000;
/*
* Units of s_val are in micro degree celsius, scale by
- * 65535 to get same units as s_thresh.
+ * HDC3020_THRESH_FRACTION to get same units as s_thresh.
*/
s_val = min(abs(s_val), HDC3020_MAX_TEMP_HYST_MICRO);
- s_hyst = (s64)s_val * 65535;
+ s_hyst = (s64)s_val * HDC3020_THRESH_FRACTION;
s_clr = hdc3020_thresh_clr(s_thresh, s_hyst, dir);
s_clr = max(s_clr, HDC3020_MIN_TEMP_MICRO);
s_clr = min(s_clr, HDC3020_MAX_TEMP_MICRO);
@@ -565,7 +576,8 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
/* Calculate old hysteresis */
s_thresh = (s64)hdc3020_thresh_get_hum(thresh) * 1000000;
s_clr = (s64)hdc3020_thresh_get_hum(clr) * 1000000;
- s_hyst = div_s64(abs(s_thresh - s_clr), 65535);
+ s_hyst = div_s64(abs(s_thresh - s_clr),
+ HDC3020_THRESH_FRACTION);
/* Set new threshold */
thresh = reg_val;
/* Try to set old hysteresis */
@@ -574,15 +586,16 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
case IIO_EV_INFO_HYSTERESIS:
/*
* Function hdc3020_thresh_get_hum returns relative
- * humidity in percent scaled by 65535. Scale by 1000000
- * to be able to subtract scaled hysteresis value.
+ * humidity in percent scaled by HDC3020_THRESH_FRACTION.
+ * Scale by 1000000 to be able to subtract scaled
+ * hysteresis value.
*/
s_thresh = (s64)hdc3020_thresh_get_hum(thresh) * 1000000;
/*
- * Units of s_val are in micro percent, scale by 65535
- * to get same units as s_thresh.
+ * Units of s_val are in micro percent, scale by
+ * HDC3020_THRESH_FRACTION to get same units as s_thresh.
*/
- s_hyst = (s64)s_val * 65535;
+ s_hyst = (s64)s_val * HDC3020_THRESH_FRACTION;
s_clr = hdc3020_thresh_clr(s_thresh, s_hyst, dir);
s_clr = max(s_clr, 0);
s_clr = min(s_clr, HDC3020_MAX_HUM_MICRO);
@@ -630,7 +643,7 @@ static int hdc3020_read_thresh(struct iio_dev *indio_dev,
thresh = hdc3020_thresh_get_temp(ret);
switch (info) {
case IIO_EV_INFO_VALUE:
- *val = thresh;
+ *val = thresh * MILLI;
break;
case IIO_EV_INFO_HYSTERESIS:
ret = hdc3020_read_be16(data, reg_clr);
@@ -638,18 +651,18 @@ static int hdc3020_read_thresh(struct iio_dev *indio_dev,
return ret;
clr = hdc3020_thresh_get_temp(ret);
- *val = abs(thresh - clr);
+ *val = abs(thresh - clr) * MILLI;
break;
default:
return -EOPNOTSUPP;
}
- *val2 = 65535;
+ *val2 = HDC3020_THRESH_FRACTION;
return IIO_VAL_FRACTIONAL;
case IIO_HUMIDITYRELATIVE:
thresh = hdc3020_thresh_get_hum(ret);
switch (info) {
case IIO_EV_INFO_VALUE:
- *val = thresh;
+ *val = thresh * MILLI;
break;
case IIO_EV_INFO_HYSTERESIS:
ret = hdc3020_read_be16(data, reg_clr);
@@ -657,12 +670,12 @@ static int hdc3020_read_thresh(struct iio_dev *indio_dev,
return ret;
clr = hdc3020_thresh_get_hum(ret);
- *val = abs(thresh - clr);
+ *val = abs(thresh - clr) * MILLI;
break;
default:
return -EOPNOTSUPP;
}
- *val2 = 65535;
+ *val2 = HDC3020_THRESH_FRACTION;
return IIO_VAL_FRACTIONAL;
default:
return -EOPNOTSUPP;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index c225b246c8a5..381b016fa524 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -192,6 +192,22 @@ struct st_lsm6dsx_fifo_ops {
* @fifo_en: Hw timer FIFO enable register info (addr + mask).
* @decimator: Hw timer FIFO decimator register info (addr + mask).
* @freq_fine: Difference in % of ODR with respect to the typical.
+ * @ts_sensitivity: Nominal timestamp sensitivity.
+ * @ts_trim_coeff: Coefficient for calculating the calibrated timestamp gain.
+ * This coefficient comes into play when linearizing the formula
+ * used to calculate the calibrated timestamp (please see the
+ * relevant formula in the AN for the specific IMU).
+ * For example, in the case of LSM6DSO we have:
+ *
+ * 1 / (1 + x) ~= 1 - x (Taylor’s Series)
+ * ttrim[s] = 1 / (40000 * (1 + 0.0015 * val)) (from AN5192)
+ * ttrim[ns] ~= 25000 - 37.5 * val
+ * ttrim[ns] ~= 25000 - (37500 * val) / 1000
+ *
+ * so, replacing ts_sensitivity = 25000 and
+ * ts_trim_coeff = 37500
+ *
+ * ttrim[ns] ~= ts_sensitivity - (ts_trim_coeff * val) / 1000
*/
struct st_lsm6dsx_hw_ts_settings {
struct st_lsm6dsx_reg timer_en;
@@ -199,6 +215,8 @@ struct st_lsm6dsx_hw_ts_settings {
struct st_lsm6dsx_reg fifo_en;
struct st_lsm6dsx_reg decimator;
u8 freq_fine;
+ u16 ts_sensitivity;
+ u16 ts_trim_coeff;
};
/**
@@ -252,6 +270,15 @@ struct st_lsm6dsx_event_settings {
u8 wakeup_src_x_mask;
};
+enum st_lsm6dsx_sensor_id {
+ ST_LSM6DSX_ID_GYRO,
+ ST_LSM6DSX_ID_ACC,
+ ST_LSM6DSX_ID_EXT0,
+ ST_LSM6DSX_ID_EXT1,
+ ST_LSM6DSX_ID_EXT2,
+ ST_LSM6DSX_ID_MAX
+};
+
enum st_lsm6dsx_ext_sensor_id {
ST_LSM6DSX_ID_MAGN,
};
@@ -337,23 +364,14 @@ struct st_lsm6dsx_settings {
struct st_lsm6dsx_odr_table_entry odr_table[2];
struct st_lsm6dsx_samples_to_discard samples_to_discard[2];
struct st_lsm6dsx_fs_table_entry fs_table[2];
- struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
- struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
+ struct st_lsm6dsx_reg decimator[ST_LSM6DSX_ID_MAX];
+ struct st_lsm6dsx_reg batch[2];
struct st_lsm6dsx_fifo_ops fifo_ops;
struct st_lsm6dsx_hw_ts_settings ts_settings;
struct st_lsm6dsx_shub_settings shub_settings;
struct st_lsm6dsx_event_settings event_settings;
};
-enum st_lsm6dsx_sensor_id {
- ST_LSM6DSX_ID_GYRO,
- ST_LSM6DSX_ID_ACC,
- ST_LSM6DSX_ID_EXT0,
- ST_LSM6DSX_ID_EXT1,
- ST_LSM6DSX_ID_EXT2,
- ST_LSM6DSX_ID_MAX,
-};
-
enum st_lsm6dsx_fifo_mode {
ST_LSM6DSX_FIFO_BYPASS = 0x0,
ST_LSM6DSX_FIFO_CONT = 0x6,
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index d8cb4b0218d5..a2daf0c14d96 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -94,8 +94,6 @@
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
-#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */
-
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
@@ -983,6 +981,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.mask = GENMASK(7, 6),
},
.freq_fine = 0x63,
+ .ts_sensitivity = 25000,
+ .ts_trim_coeff = 37500,
},
.shub_settings = {
.page_mux = {
@@ -1196,6 +1196,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.mask = GENMASK(7, 6),
},
.freq_fine = 0x63,
+ .ts_sensitivity = 25000,
+ .ts_trim_coeff = 37500,
},
.event_settings = {
.enable_reg = {
@@ -1371,6 +1373,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.mask = GENMASK(7, 6),
},
.freq_fine = 0x4f,
+ .ts_sensitivity = 21701,
+ .ts_trim_coeff = 28212,
},
.shub_settings = {
.page_mux = {
@@ -2248,20 +2252,13 @@ static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
}
/* calibrate timestamp sensitivity */
- hw->ts_gain = ST_LSM6DSX_TS_SENSITIVITY;
+ hw->ts_gain = ts_settings->ts_sensitivity;
if (ts_settings->freq_fine) {
err = regmap_read(hw->regmap, ts_settings->freq_fine, &val);
if (err < 0)
return err;
- /*
- * linearize the AN5192 formula:
- * 1 / (1 + x) ~= 1 - x (Taylor’s Series)
- * ttrim[s] = 1 / (40000 * (1 + 0.0015 * val))
- * ttrim[ns] ~= 25000 - 37.5 * val
- * ttrim[ns] ~= 25000 - (37500 * val) / 1000
- */
- hw->ts_gain -= ((s8)val * 37500) / 1000;
+ hw->ts_gain -= ((s8)val * ts_settings->ts_trim_coeff) / 1000;
}
return 0;
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index a80f7cc25a27..96ea0f039dfb 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1623,19 +1623,28 @@ static int iio_dma_resv_lock(struct dma_buf *dmabuf, bool nonblock)
return 0;
}
+static struct device *iio_buffer_get_dma_dev(const struct iio_dev *indio_dev,
+ struct iio_buffer *buffer)
+{
+ if (buffer->access->get_dma_dev)
+ return buffer->access->get_dma_dev(buffer);
+
+ return indio_dev->dev.parent;
+}
+
static struct dma_buf_attachment *
iio_buffer_find_attachment(struct iio_dev_buffer_pair *ib,
struct dma_buf *dmabuf, bool nonblock)
{
- struct device *dev = ib->indio_dev->dev.parent;
struct iio_buffer *buffer = ib->buffer;
+ struct device *dma_dev = iio_buffer_get_dma_dev(ib->indio_dev, buffer);
struct dma_buf_attachment *attach = NULL;
struct iio_dmabuf_priv *priv;
guard(mutex)(&buffer->dmabufs_mutex);
list_for_each_entry(priv, &buffer->dmabufs, entry) {
- if (priv->attach->dev == dev
+ if (priv->attach->dev == dma_dev
&& priv->attach->dmabuf == dmabuf) {
attach = priv->attach;
break;
@@ -1653,6 +1662,7 @@ static int iio_buffer_attach_dmabuf(struct iio_dev_buffer_pair *ib,
{
struct iio_dev *indio_dev = ib->indio_dev;
struct iio_buffer *buffer = ib->buffer;
+ struct device *dma_dev = iio_buffer_get_dma_dev(indio_dev, buffer);
struct dma_buf_attachment *attach;
struct iio_dmabuf_priv *priv, *each;
struct dma_buf *dmabuf;
@@ -1679,7 +1689,7 @@ static int iio_buffer_attach_dmabuf(struct iio_dev_buffer_pair *ib,
goto err_free_priv;
}
- attach = dma_buf_attach(dmabuf, indio_dev->dev.parent);
+ attach = dma_buf_attach(dmabuf, dma_dev);
if (IS_ERR(attach)) {
err = PTR_ERR(attach);
goto err_dmabuf_put;
@@ -1719,7 +1729,7 @@ static int iio_buffer_attach_dmabuf(struct iio_dev_buffer_pair *ib,
* combo. If we do, refuse to attach.
*/
list_for_each_entry(each, &buffer->dmabufs, entry) {
- if (each->attach->dev == indio_dev->dev.parent
+ if (each->attach->dev == dma_dev
&& each->attach->dmabuf == dmabuf) {
/*
* We unlocked the reservation object, so going through
@@ -1758,6 +1768,7 @@ static int iio_buffer_detach_dmabuf(struct iio_dev_buffer_pair *ib,
{
struct iio_buffer *buffer = ib->buffer;
struct iio_dev *indio_dev = ib->indio_dev;
+ struct device *dma_dev = iio_buffer_get_dma_dev(indio_dev, buffer);
struct iio_dmabuf_priv *priv;
struct dma_buf *dmabuf;
int dmabuf_fd, ret = -EPERM;
@@ -1772,7 +1783,7 @@ static int iio_buffer_detach_dmabuf(struct iio_dev_buffer_pair *ib,
guard(mutex)(&buffer->dmabufs_mutex);
list_for_each_entry(priv, &buffer->dmabufs, entry) {
- if (priv->attach->dev == indio_dev->dev.parent
+ if (priv->attach->dev == dma_dev
&& priv->attach->dmabuf == dmabuf) {
list_del(&priv->entry);
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index c04e8bb4c993..d983ce9c0b99 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -1040,13 +1040,16 @@ static int bmp280_wait_conv(struct bmp280_data *data)
unsigned int reg, meas_time_us;
int ret;
- /* Check if we are using a BME280 device */
- if (data->oversampling_humid)
- meas_time_us = BMP280_PRESS_HUMID_MEAS_OFFSET +
- BIT(data->oversampling_humid) * BMP280_MEAS_DUR;
+ /* Constant part of the measurement time */
+ meas_time_us = BMP280_MEAS_OFFSET;
- else
- meas_time_us = 0;
+ /*
+ * Check if we are using a BME280 device,
+ * Humidity measurement time
+ */
+ if (data->chip_info->oversampling_humid_avail)
+ meas_time_us += BMP280_PRESS_HUMID_MEAS_OFFSET +
+ BIT(data->oversampling_humid) * BMP280_MEAS_DUR;
/* Pressure measurement time */
meas_time_us += BMP280_PRESS_HUMID_MEAS_OFFSET +
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index f0323f1d6f01..794b9778816b 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -80,6 +80,7 @@ config INFINIBAND_VIRT_DMA
if INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS
if !UML
source "drivers/infiniband/hw/bnxt_re/Kconfig"
+source "drivers/infiniband/hw/bng_re/Kconfig"
source "drivers/infiniband/hw/cxgb4/Kconfig"
source "drivers/infiniband/hw/efa/Kconfig"
source "drivers/infiniband/hw/erdma/Kconfig"
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index 01bede8ba105..024df6ee239d 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -34,7 +34,6 @@ MODULE_AUTHOR("Sean Hefty");
MODULE_DESCRIPTION("InfiniBand CM");
MODULE_LICENSE("Dual BSD/GPL");
-#define CM_DESTROY_ID_WAIT_TIMEOUT 10000 /* msecs */
#define CM_DIRECT_RETRY_CTX ((void *) 1UL)
#define CM_MRA_SETTING 24 /* 4.096us * 2^24 = ~68.7 seconds */
@@ -1057,6 +1056,7 @@ static void cm_destroy_id(struct ib_cm_id *cm_id, int err)
{
struct cm_id_private *cm_id_priv;
enum ib_cm_state old_state;
+ unsigned long timeout;
struct cm_work *work;
int ret;
@@ -1167,10 +1167,9 @@ retest:
xa_erase(&cm.local_id_table, cm_local_id(cm_id->local_id));
cm_deref_id(cm_id_priv);
+ timeout = msecs_to_jiffies((cm_id_priv->max_cm_retries * cm_id_priv->timeout_ms * 5) / 4);
do {
- ret = wait_for_completion_timeout(&cm_id_priv->comp,
- msecs_to_jiffies(
- CM_DESTROY_ID_WAIT_TIMEOUT));
+ ret = wait_for_completion_timeout(&cm_id_priv->comp, timeout);
if (!ret) /* timeout happened */
cm_destroy_id_wait_timeout(cm_id, old_state);
} while (!ret);
@@ -4518,7 +4517,7 @@ static int __init ib_cm_init(void)
get_random_bytes(&cm.random_id_operand, sizeof cm.random_id_operand);
INIT_LIST_HEAD(&cm.timewait_list);
- cm.wq = alloc_workqueue("ib_cm", 0, 1);
+ cm.wq = alloc_workqueue("ib_cm", WQ_PERCPU, 1);
if (!cm.wq) {
ret = -ENOMEM;
goto error2;
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 5b2d3ae3f9fc..95e89f5c147c 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -4475,6 +4475,8 @@ int rdma_connect_locked(struct rdma_cm_id *id,
container_of(id, struct rdma_id_private, id);
int ret;
+ lockdep_assert_held(&id_priv->handler_mutex);
+
if (!cma_comp_exch(id_priv, RDMA_CM_ROUTE_RESOLVED, RDMA_CM_CONNECT))
return -EINVAL;
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index b4f3c835844a..13e8a1714bbd 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -3021,7 +3021,7 @@ static int __init ib_core_init(void)
{
int ret = -ENOMEM;
- ib_wq = alloc_workqueue("infiniband", 0, 0);
+ ib_wq = alloc_workqueue("infiniband", WQ_PERCPU, 0);
if (!ib_wq)
return -ENOMEM;
@@ -3031,7 +3031,7 @@ static int __init ib_core_init(void)
goto err;
ib_comp_wq = alloc_workqueue("ib-comp-wq",
- WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
+ WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_SYSFS | WQ_PERCPU, 0);
if (!ib_comp_wq)
goto err_unbound;
diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c
index a7de6f403fca..b097cfcade1c 100644
--- a/drivers/infiniband/core/restrack.c
+++ b/drivers/infiniband/core/restrack.c
@@ -175,7 +175,7 @@ void rdma_restrack_new(struct rdma_restrack_entry *res,
EXPORT_SYMBOL(rdma_restrack_new);
/**
- * rdma_restrack_add() - add object to the reource tracking database
+ * rdma_restrack_add() - add object to the resource tracking database
* @res: resource entry
*/
void rdma_restrack_add(struct rdma_restrack_entry *res)
@@ -277,7 +277,7 @@ int rdma_restrack_put(struct rdma_restrack_entry *res)
EXPORT_SYMBOL(rdma_restrack_put);
/**
- * rdma_restrack_del() - delete object from the reource tracking database
+ * rdma_restrack_del() - delete object from the resource tracking database
* @res: resource entry
*/
void rdma_restrack_del(struct rdma_restrack_entry *res)
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index f86ece701db6..ec3be65a2b88 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -366,7 +366,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id,
if (event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) {
xa_lock(&ctx_table);
if (xa_load(&ctx_table, ctx->id) == ctx)
- queue_work(system_unbound_wq, &ctx->close_work);
+ queue_work(system_dfl_wq, &ctx->close_work);
xa_unlock(&ctx_table);
}
return 0;
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index c5b686394760..8137031c2a65 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -45,6 +45,8 @@
#include "uverbs.h"
+#define RESCHED_LOOP_CNT_THRESHOLD 0x1000
+
static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int dirty)
{
bool make_dirty = umem->writable && dirty;
@@ -55,10 +57,14 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d
ib_dma_unmap_sgtable_attrs(dev, &umem->sgt_append.sgt,
DMA_BIDIRECTIONAL, 0);
- for_each_sgtable_sg(&umem->sgt_append.sgt, sg, i)
+ for_each_sgtable_sg(&umem->sgt_append.sgt, sg, i) {
unpin_user_page_range_dirty_lock(sg_page(sg),
DIV_ROUND_UP(sg->length, PAGE_SIZE), make_dirty);
+ if (i && !(i % RESCHED_LOOP_CNT_THRESHOLD))
+ cond_resched();
+ }
+
sg_free_append_table(&umem->sgt_append);
}
diff --git a/drivers/infiniband/core/uverbs_std_types_cq.c b/drivers/infiniband/core/uverbs_std_types_cq.c
index 37cd37556510..fab5d914029d 100644
--- a/drivers/infiniband/core/uverbs_std_types_cq.c
+++ b/drivers/infiniband/core/uverbs_std_types_cq.c
@@ -206,6 +206,7 @@ static int UVERBS_HANDLER(UVERBS_METHOD_CQ_CREATE)(
return ret;
err_free:
+ ib_umem_release(umem);
rdma_restrack_put(&cq->res);
kfree(cq);
err_event_file:
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 3a5f81402d2f..11b1a194de44 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -148,6 +148,7 @@ __attribute_const__ int ib_rate_to_mult(enum ib_rate rate)
case IB_RATE_400_GBPS: return 160;
case IB_RATE_600_GBPS: return 240;
case IB_RATE_800_GBPS: return 320;
+ case IB_RATE_1600_GBPS: return 640;
default: return -1;
}
}
@@ -178,6 +179,7 @@ __attribute_const__ enum ib_rate mult_to_ib_rate(int mult)
case 160: return IB_RATE_400_GBPS;
case 240: return IB_RATE_600_GBPS;
case 320: return IB_RATE_800_GBPS;
+ case 640: return IB_RATE_1600_GBPS;
default: return IB_RATE_PORT_CURRENT;
}
}
@@ -208,6 +210,7 @@ __attribute_const__ int ib_rate_to_mbps(enum ib_rate rate)
case IB_RATE_400_GBPS: return 425000;
case IB_RATE_600_GBPS: return 637500;
case IB_RATE_800_GBPS: return 850000;
+ case IB_RATE_1600_GBPS: return 1700000;
default: return -1;
}
}
diff --git a/drivers/infiniband/hw/Makefile b/drivers/infiniband/hw/Makefile
index b706dc0d0263..c42b22ac3303 100644
--- a/drivers/infiniband/hw/Makefile
+++ b/drivers/infiniband/hw/Makefile
@@ -13,5 +13,6 @@ obj-$(CONFIG_INFINIBAND_HFI1) += hfi1/
obj-$(CONFIG_INFINIBAND_HNS_HIP08) += hns/
obj-$(CONFIG_INFINIBAND_QEDR) += qedr/
obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re/
+obj-$(CONFIG_INFINIBAND_BNG_RE) += bng_re/
obj-$(CONFIG_INFINIBAND_ERDMA) += erdma/
obj-$(CONFIG_INFINIBAND_IONIC) += ionic/
diff --git a/drivers/infiniband/hw/bng_re/Kconfig b/drivers/infiniband/hw/bng_re/Kconfig
new file mode 100644
index 000000000000..85845f72c64d
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config INFINIBAND_BNG_RE
+ tristate "Broadcom Next generation RoCE HCA support"
+ depends on 64BIT
+ depends on INET && DCB && BNGE
+ help
+ This driver supports Broadcom Next generation
+ 50/100/200/400/800 gigabit RoCE HCAs. The module
+ will be called bng_re. To compile this driver
+ as a module, choose M here.
diff --git a/drivers/infiniband/hw/bng_re/Makefile b/drivers/infiniband/hw/bng_re/Makefile
new file mode 100644
index 000000000000..c6aaaf853c77
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+ccflags-y := -I $(srctree)/drivers/net/ethernet/broadcom/bnge -I $(srctree)/drivers/infiniband/hw/bnxt_re
+
+obj-$(CONFIG_INFINIBAND_BNG_RE) += bng_re.o
+
+bng_re-y := bng_dev.o bng_fw.o \
+ bng_res.o bng_sp.o \
+ bng_debugfs.o
diff --git a/drivers/infiniband/hw/bng_re/bng_debugfs.c b/drivers/infiniband/hw/bng_re/bng_debugfs.c
new file mode 100644
index 000000000000..9ec5a8785250
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_debugfs.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+#include <linux/debugfs.h>
+#include <linux/pci.h>
+
+#include <rdma/ib_verbs.h>
+
+#include "bng_res.h"
+#include "bng_fw.h"
+#include "bnge.h"
+#include "bnge_auxr.h"
+#include "bng_re.h"
+#include "bng_debugfs.h"
+
+static struct dentry *bng_re_debugfs_root;
+
+void bng_re_debugfs_add_pdev(struct bng_re_dev *rdev)
+{
+ struct pci_dev *pdev = rdev->aux_dev->pdev;
+
+ rdev->dbg_root =
+ debugfs_create_dir(dev_name(&pdev->dev), bng_re_debugfs_root);
+}
+
+void bng_re_debugfs_rem_pdev(struct bng_re_dev *rdev)
+{
+ debugfs_remove_recursive(rdev->dbg_root);
+ rdev->dbg_root = NULL;
+}
+
+void bng_re_register_debugfs(void)
+{
+ bng_re_debugfs_root = debugfs_create_dir("bng_re", NULL);
+}
+
+void bng_re_unregister_debugfs(void)
+{
+ debugfs_remove(bng_re_debugfs_root);
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_debugfs.h b/drivers/infiniband/hw/bng_re/bng_debugfs.h
new file mode 100644
index 000000000000..baef71df4242
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_debugfs.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2025 Broadcom.
+
+#ifndef __BNG_RE_DEBUGFS__
+#define __BNG_RE_DEBUGFS__
+
+void bng_re_debugfs_add_pdev(struct bng_re_dev *rdev);
+void bng_re_debugfs_rem_pdev(struct bng_re_dev *rdev);
+
+void bng_re_register_debugfs(void);
+void bng_re_unregister_debugfs(void);
+#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_dev.c b/drivers/infiniband/hw/bng_re/bng_dev.c
new file mode 100644
index 000000000000..d8f8d7f7075f
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_dev.c
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/auxiliary_bus.h>
+
+#include <rdma/ib_verbs.h>
+
+#include "bng_res.h"
+#include "bng_sp.h"
+#include "bng_fw.h"
+#include "bnge.h"
+#include "bnge_auxr.h"
+#include "bng_re.h"
+#include "bnge_hwrm.h"
+#include "bng_debugfs.h"
+
+MODULE_AUTHOR("Siva Reddy Kallam <siva.kallam@broadcom.com>");
+MODULE_DESCRIPTION(BNG_RE_DESC);
+MODULE_LICENSE("Dual BSD/GPL");
+
+static struct bng_re_dev *bng_re_dev_add(struct auxiliary_device *adev,
+ struct bnge_auxr_dev *aux_dev)
+{
+ struct bng_re_dev *rdev;
+
+ /* Allocate bng_re_dev instance */
+ rdev = ib_alloc_device(bng_re_dev, ibdev);
+ if (!rdev) {
+ pr_err("%s: bng_re_dev allocation failure!", KBUILD_MODNAME);
+ return NULL;
+ }
+
+ /* Assign auxiliary device specific data */
+ rdev->netdev = aux_dev->net;
+ rdev->aux_dev = aux_dev;
+ rdev->adev = adev;
+ rdev->fn_id = rdev->aux_dev->pdev->devfn;
+
+ return rdev;
+}
+
+
+static int bng_re_register_netdev(struct bng_re_dev *rdev)
+{
+ struct bnge_auxr_dev *aux_dev;
+
+ aux_dev = rdev->aux_dev;
+ return bnge_register_dev(aux_dev, rdev->adev);
+}
+
+static void bng_re_destroy_chip_ctx(struct bng_re_dev *rdev)
+{
+ struct bng_re_chip_ctx *chip_ctx;
+
+ if (!rdev->chip_ctx)
+ return;
+
+ kfree(rdev->dev_attr);
+ rdev->dev_attr = NULL;
+
+ chip_ctx = rdev->chip_ctx;
+ rdev->chip_ctx = NULL;
+ rdev->rcfw.res = NULL;
+ rdev->bng_res.cctx = NULL;
+ rdev->bng_res.pdev = NULL;
+ kfree(chip_ctx);
+}
+
+static int bng_re_setup_chip_ctx(struct bng_re_dev *rdev)
+{
+ struct bng_re_chip_ctx *chip_ctx;
+ struct bnge_auxr_dev *aux_dev;
+ int rc = -ENOMEM;
+
+ aux_dev = rdev->aux_dev;
+ rdev->bng_res.pdev = aux_dev->pdev;
+ rdev->rcfw.res = &rdev->bng_res;
+ chip_ctx = kzalloc(sizeof(*chip_ctx), GFP_KERNEL);
+ if (!chip_ctx)
+ return -ENOMEM;
+ chip_ctx->chip_num = aux_dev->chip_num;
+ chip_ctx->hw_stats_size = aux_dev->hw_ring_stats_size;
+
+ rdev->chip_ctx = chip_ctx;
+ rdev->bng_res.cctx = rdev->chip_ctx;
+ rdev->dev_attr = kzalloc(sizeof(*rdev->dev_attr), GFP_KERNEL);
+ if (!rdev->dev_attr)
+ goto free_chip_ctx;
+ rdev->bng_res.dattr = rdev->dev_attr;
+
+ return 0;
+free_chip_ctx:
+ kfree(rdev->chip_ctx);
+ rdev->chip_ctx = NULL;
+ return rc;
+}
+
+static void bng_re_init_hwrm_hdr(struct input *hdr, u16 opcd)
+{
+ hdr->req_type = cpu_to_le16(opcd);
+ hdr->cmpl_ring = cpu_to_le16(-1);
+ hdr->target_id = cpu_to_le16(-1);
+}
+
+static void bng_re_fill_fw_msg(struct bnge_fw_msg *fw_msg, void *msg,
+ int msg_len, void *resp, int resp_max_len,
+ int timeout)
+{
+ fw_msg->msg = msg;
+ fw_msg->msg_len = msg_len;
+ fw_msg->resp = resp;
+ fw_msg->resp_max_len = resp_max_len;
+ fw_msg->timeout = timeout;
+}
+
+static int bng_re_net_ring_free(struct bng_re_dev *rdev,
+ u16 fw_ring_id, int type)
+{
+ struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
+ struct hwrm_ring_free_input req = {};
+ struct hwrm_ring_free_output resp;
+ struct bnge_fw_msg fw_msg = {};
+ int rc = -EINVAL;
+
+ if (!rdev)
+ return rc;
+
+ if (!aux_dev)
+ return rc;
+
+ bng_re_init_hwrm_hdr((void *)&req, HWRM_RING_FREE);
+ req.ring_type = type;
+ req.ring_id = cpu_to_le16(fw_ring_id);
+ bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
+ sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ rc = bnge_send_msg(aux_dev, &fw_msg);
+ if (rc)
+ ibdev_err(&rdev->ibdev, "Failed to free HW ring:%d :%#x",
+ req.ring_id, rc);
+ return rc;
+}
+
+static int bng_re_net_ring_alloc(struct bng_re_dev *rdev,
+ struct bng_re_ring_attr *ring_attr,
+ u16 *fw_ring_id)
+{
+ struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
+ struct hwrm_ring_alloc_input req = {};
+ struct hwrm_ring_alloc_output resp;
+ struct bnge_fw_msg fw_msg = {};
+ int rc = -EINVAL;
+
+ if (!aux_dev)
+ return rc;
+
+ bng_re_init_hwrm_hdr((void *)&req, HWRM_RING_ALLOC);
+ req.enables = 0;
+ req.page_tbl_addr = cpu_to_le64(ring_attr->dma_arr[0]);
+ if (ring_attr->pages > 1) {
+ /* Page size is in log2 units */
+ req.page_size = BNGE_PAGE_SHIFT;
+ req.page_tbl_depth = 1;
+ }
+ req.fbo = 0;
+ /* Association of ring index with doorbell index and MSIX number */
+ req.logical_id = cpu_to_le16(ring_attr->lrid);
+ req.length = cpu_to_le32(ring_attr->depth + 1);
+ req.ring_type = ring_attr->type;
+ req.int_mode = ring_attr->mode;
+ bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
+ sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ rc = bnge_send_msg(aux_dev, &fw_msg);
+ if (!rc)
+ *fw_ring_id = le16_to_cpu(resp.ring_id);
+
+ return rc;
+}
+
+static int bng_re_stats_ctx_free(struct bng_re_dev *rdev)
+{
+ struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
+ struct hwrm_stat_ctx_free_input req = {};
+ struct hwrm_stat_ctx_free_output resp = {};
+ struct bnge_fw_msg fw_msg = {};
+ int rc = -EINVAL;
+
+ if (!aux_dev)
+ return rc;
+
+ bng_re_init_hwrm_hdr((void *)&req, HWRM_STAT_CTX_FREE);
+ req.stat_ctx_id = cpu_to_le32(rdev->stats_ctx.fw_id);
+ bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
+ sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ rc = bnge_send_msg(aux_dev, &fw_msg);
+ if (rc)
+ ibdev_err(&rdev->ibdev, "Failed to free HW stats context %#x",
+ rc);
+
+ return rc;
+}
+
+static int bng_re_stats_ctx_alloc(struct bng_re_dev *rdev)
+{
+ struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
+ struct bng_re_stats *stats = &rdev->stats_ctx;
+ struct hwrm_stat_ctx_alloc_output resp = {};
+ struct hwrm_stat_ctx_alloc_input req = {};
+ struct bnge_fw_msg fw_msg = {};
+ int rc = -EINVAL;
+
+ stats->fw_id = BNGE_INVALID_STATS_CTX_ID;
+
+ if (!aux_dev)
+ return rc;
+
+ bng_re_init_hwrm_hdr((void *)&req, HWRM_STAT_CTX_ALLOC);
+ req.update_period_ms = cpu_to_le32(1000);
+ req.stats_dma_addr = cpu_to_le64(stats->dma_map);
+ req.stats_dma_length = cpu_to_le16(rdev->chip_ctx->hw_stats_size);
+ req.stat_ctx_flags = STAT_CTX_ALLOC_REQ_STAT_CTX_FLAGS_ROCE;
+ bng_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp,
+ sizeof(resp), BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ rc = bnge_send_msg(aux_dev, &fw_msg);
+ if (!rc)
+ stats->fw_id = le32_to_cpu(resp.stat_ctx_id);
+ return rc;
+}
+
+static void bng_re_query_hwrm_version(struct bng_re_dev *rdev)
+{
+ struct bnge_auxr_dev *aux_dev = rdev->aux_dev;
+ struct hwrm_ver_get_output ver_get_resp = {};
+ struct hwrm_ver_get_input ver_get_req = {};
+ struct bng_re_chip_ctx *cctx;
+ struct bnge_fw_msg fw_msg = {};
+ int rc;
+
+ bng_re_init_hwrm_hdr((void *)&ver_get_req, HWRM_VER_GET);
+ ver_get_req.hwrm_intf_maj = HWRM_VERSION_MAJOR;
+ ver_get_req.hwrm_intf_min = HWRM_VERSION_MINOR;
+ ver_get_req.hwrm_intf_upd = HWRM_VERSION_UPDATE;
+ bng_re_fill_fw_msg(&fw_msg, (void *)&ver_get_req, sizeof(ver_get_req),
+ (void *)&ver_get_resp, sizeof(ver_get_resp),
+ BNGE_DFLT_HWRM_CMD_TIMEOUT);
+ rc = bnge_send_msg(aux_dev, &fw_msg);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to query HW version, rc = 0x%x",
+ rc);
+ return;
+ }
+
+ cctx = rdev->chip_ctx;
+ cctx->hwrm_intf_ver =
+ (u64)le16_to_cpu(ver_get_resp.hwrm_intf_major) << 48 |
+ (u64)le16_to_cpu(ver_get_resp.hwrm_intf_minor) << 32 |
+ (u64)le16_to_cpu(ver_get_resp.hwrm_intf_build) << 16 |
+ le16_to_cpu(ver_get_resp.hwrm_intf_patch);
+
+ cctx->hwrm_cmd_max_timeout = le16_to_cpu(ver_get_resp.max_req_timeout);
+
+ if (!cctx->hwrm_cmd_max_timeout)
+ cctx->hwrm_cmd_max_timeout = BNG_ROCE_FW_MAX_TIMEOUT;
+}
+
+static void bng_re_dev_uninit(struct bng_re_dev *rdev)
+{
+ int rc;
+ bng_re_debugfs_rem_pdev(rdev);
+
+ if (test_and_clear_bit(BNG_RE_FLAG_RCFW_CHANNEL_EN, &rdev->flags)) {
+ rc = bng_re_deinit_rcfw(&rdev->rcfw);
+ if (rc)
+ ibdev_warn(&rdev->ibdev,
+ "Failed to deinitialize RCFW: %#x", rc);
+ bng_re_stats_ctx_free(rdev);
+ bng_re_free_stats_ctx_mem(rdev->bng_res.pdev, &rdev->stats_ctx);
+ bng_re_disable_rcfw_channel(&rdev->rcfw);
+ bng_re_net_ring_free(rdev, rdev->rcfw.creq.ring_id,
+ RING_ALLOC_REQ_RING_TYPE_NQ);
+ bng_re_free_rcfw_channel(&rdev->rcfw);
+ }
+
+ kfree(rdev->nqr);
+ rdev->nqr = NULL;
+ bng_re_destroy_chip_ctx(rdev);
+ if (test_and_clear_bit(BNG_RE_FLAG_NETDEV_REGISTERED, &rdev->flags))
+ bnge_unregister_dev(rdev->aux_dev);
+}
+
+static int bng_re_dev_init(struct bng_re_dev *rdev)
+{
+ struct bng_re_ring_attr rattr = {};
+ struct bng_re_creq_ctx *creq;
+ u32 db_offt;
+ int vid;
+ u8 type;
+ int rc;
+
+ /* Registered a new RoCE device instance to netdev */
+ rc = bng_re_register_netdev(rdev);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to register with netedev: %#x\n", rc);
+ return -EINVAL;
+ }
+
+ set_bit(BNG_RE_FLAG_NETDEV_REGISTERED, &rdev->flags);
+
+ if (rdev->aux_dev->auxr_info->msix_requested < BNG_RE_MIN_MSIX) {
+ ibdev_err(&rdev->ibdev,
+ "RoCE requires minimum 2 MSI-X vectors, but only %d reserved\n",
+ rdev->aux_dev->auxr_info->msix_requested);
+ bnge_unregister_dev(rdev->aux_dev);
+ clear_bit(BNG_RE_FLAG_NETDEV_REGISTERED, &rdev->flags);
+ return -EINVAL;
+ }
+ ibdev_dbg(&rdev->ibdev, "Got %d MSI-X vectors\n",
+ rdev->aux_dev->auxr_info->msix_requested);
+
+ rc = bng_re_setup_chip_ctx(rdev);
+ if (rc) {
+ bnge_unregister_dev(rdev->aux_dev);
+ clear_bit(BNG_RE_FLAG_NETDEV_REGISTERED, &rdev->flags);
+ ibdev_err(&rdev->ibdev, "Failed to get chip context\n");
+ return -EINVAL;
+ }
+
+ bng_re_query_hwrm_version(rdev);
+
+ rc = bng_re_alloc_fw_channel(&rdev->bng_res, &rdev->rcfw);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to allocate RCFW Channel: %#x\n", rc);
+ goto fail;
+ }
+
+ /* Allocate nq record memory */
+ rdev->nqr = kzalloc(sizeof(*rdev->nqr), GFP_KERNEL);
+ if (!rdev->nqr) {
+ bng_re_destroy_chip_ctx(rdev);
+ bnge_unregister_dev(rdev->aux_dev);
+ clear_bit(BNG_RE_FLAG_NETDEV_REGISTERED, &rdev->flags);
+ return -ENOMEM;
+ }
+
+ rdev->nqr->num_msix = rdev->aux_dev->auxr_info->msix_requested;
+ memcpy(rdev->nqr->msix_entries, rdev->aux_dev->msix_info,
+ sizeof(struct bnge_msix_info) * rdev->nqr->num_msix);
+
+ type = RING_ALLOC_REQ_RING_TYPE_NQ;
+ creq = &rdev->rcfw.creq;
+ rattr.dma_arr = creq->hwq.pbl[BNG_PBL_LVL_0].pg_map_arr;
+ rattr.pages = creq->hwq.pbl[creq->hwq.level].pg_count;
+ rattr.type = type;
+ rattr.mode = RING_ALLOC_REQ_INT_MODE_MSIX;
+ rattr.depth = BNG_FW_CREQE_MAX_CNT - 1;
+ rattr.lrid = rdev->nqr->msix_entries[BNG_RE_CREQ_NQ_IDX].ring_idx;
+ rc = bng_re_net_ring_alloc(rdev, &rattr, &creq->ring_id);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to allocate CREQ: %#x\n", rc);
+ goto free_rcfw;
+ }
+ db_offt = rdev->nqr->msix_entries[BNG_RE_CREQ_NQ_IDX].db_offset;
+ vid = rdev->nqr->msix_entries[BNG_RE_CREQ_NQ_IDX].vector;
+
+ rc = bng_re_enable_fw_channel(&rdev->rcfw,
+ vid, db_offt);
+ if (rc) {
+ ibdev_err(&rdev->ibdev, "Failed to enable RCFW channel: %#x\n",
+ rc);
+ goto free_ring;
+ }
+
+ rc = bng_re_get_dev_attr(&rdev->rcfw);
+ if (rc)
+ goto disable_rcfw;
+
+ bng_re_debugfs_add_pdev(rdev);
+ rc = bng_re_alloc_stats_ctx_mem(rdev->bng_res.pdev, rdev->chip_ctx,
+ &rdev->stats_ctx);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to allocate stats context: %#x\n", rc);
+ goto disable_rcfw;
+ }
+
+ rc = bng_re_stats_ctx_alloc(rdev);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to allocate QPLIB context: %#x\n", rc);
+ goto free_stats_ctx;
+ }
+
+ rc = bng_re_init_rcfw(&rdev->rcfw, &rdev->stats_ctx);
+ if (rc) {
+ ibdev_err(&rdev->ibdev,
+ "Failed to initialize RCFW: %#x\n", rc);
+ goto free_sctx;
+ }
+ set_bit(BNG_RE_FLAG_RCFW_CHANNEL_EN, &rdev->flags);
+
+ return 0;
+free_sctx:
+ bng_re_stats_ctx_free(rdev);
+free_stats_ctx:
+ bng_re_free_stats_ctx_mem(rdev->bng_res.pdev, &rdev->stats_ctx);
+disable_rcfw:
+ bng_re_disable_rcfw_channel(&rdev->rcfw);
+free_ring:
+ bng_re_net_ring_free(rdev, rdev->rcfw.creq.ring_id, type);
+free_rcfw:
+ bng_re_free_rcfw_channel(&rdev->rcfw);
+fail:
+ bng_re_dev_uninit(rdev);
+ return rc;
+}
+
+static int bng_re_add_device(struct auxiliary_device *adev)
+{
+ struct bnge_auxr_priv *auxr_priv =
+ container_of(adev, struct bnge_auxr_priv, aux_dev);
+ struct bng_re_en_dev_info *dev_info;
+ struct bng_re_dev *rdev;
+ int rc;
+
+ dev_info = auxiliary_get_drvdata(adev);
+
+ rdev = bng_re_dev_add(adev, auxr_priv->auxr_dev);
+ if (!rdev) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ dev_info->rdev = rdev;
+
+ rc = bng_re_dev_init(rdev);
+ if (rc)
+ goto re_dev_dealloc;
+
+ return 0;
+
+re_dev_dealloc:
+ ib_dealloc_device(&rdev->ibdev);
+exit:
+ return rc;
+}
+
+
+static void bng_re_remove_device(struct bng_re_dev *rdev,
+ struct auxiliary_device *aux_dev)
+{
+ bng_re_dev_uninit(rdev);
+ ib_dealloc_device(&rdev->ibdev);
+}
+
+
+static int bng_re_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct bnge_auxr_priv *aux_priv =
+ container_of(adev, struct bnge_auxr_priv, aux_dev);
+ struct bng_re_en_dev_info *en_info;
+ int rc;
+
+ en_info = kzalloc(sizeof(*en_info), GFP_KERNEL);
+ if (!en_info)
+ return -ENOMEM;
+
+ en_info->auxr_dev = aux_priv->auxr_dev;
+
+ auxiliary_set_drvdata(adev, en_info);
+
+ rc = bng_re_add_device(adev);
+ if (rc)
+ kfree(en_info);
+
+ return rc;
+}
+
+static void bng_re_remove(struct auxiliary_device *adev)
+{
+ struct bng_re_en_dev_info *dev_info = auxiliary_get_drvdata(adev);
+ struct bng_re_dev *rdev;
+
+ rdev = dev_info->rdev;
+
+ if (rdev)
+ bng_re_remove_device(rdev, adev);
+ kfree(dev_info);
+}
+
+static const struct auxiliary_device_id bng_re_id_table[] = {
+ { .name = BNG_RE_ADEV_NAME ".rdma", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(auxiliary, bng_re_id_table);
+
+static struct auxiliary_driver bng_re_driver = {
+ .name = "rdma",
+ .probe = bng_re_probe,
+ .remove = bng_re_remove,
+ .id_table = bng_re_id_table,
+};
+
+static int __init bng_re_mod_init(void)
+{
+ int rc;
+
+
+ bng_re_register_debugfs();
+
+ rc = auxiliary_driver_register(&bng_re_driver);
+ if (rc) {
+ pr_err("%s: Failed to register auxiliary driver\n",
+ KBUILD_MODNAME);
+ goto unreg_debugfs;
+ }
+ return 0;
+unreg_debugfs:
+ bng_re_unregister_debugfs();
+ return rc;
+}
+
+static void __exit bng_re_mod_exit(void)
+{
+ auxiliary_driver_unregister(&bng_re_driver);
+ bng_re_unregister_debugfs();
+}
+
+module_init(bng_re_mod_init);
+module_exit(bng_re_mod_exit);
diff --git a/drivers/infiniband/hw/bng_re/bng_fw.c b/drivers/infiniband/hw/bng_re/bng_fw.c
new file mode 100644
index 000000000000..7d9539113cf5
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_fw.c
@@ -0,0 +1,767 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+#include <linux/pci.h>
+
+#include "roce_hsi.h"
+#include "bng_res.h"
+#include "bng_fw.h"
+#include "bng_sp.h"
+
+/**
+ * bng_re_map_rc - map return type based on opcode
+ * @opcode: roce slow path opcode
+ *
+ * case #1
+ * Firmware initiated error recovery is a safe state machine and
+ * driver can consider all the underlying rdma resources are free.
+ * In this state, it is safe to return success for opcodes related to
+ * destroying rdma resources (like destroy qp, destroy cq etc.).
+ *
+ * case #2
+ * If driver detect potential firmware stall, it is not safe state machine
+ * and the driver can not consider all the underlying rdma resources are
+ * freed.
+ * In this state, it is not safe to return success for opcodes related to
+ * destroying rdma resources (like destroy qp, destroy cq etc.).
+ *
+ * Scope of this helper function is only for case #1.
+ *
+ * Returns:
+ * 0 to communicate success to caller.
+ * Non zero error code to communicate failure to caller.
+ */
+static int bng_re_map_rc(u8 opcode)
+{
+ switch (opcode) {
+ case CMDQ_BASE_OPCODE_DESTROY_QP:
+ case CMDQ_BASE_OPCODE_DESTROY_SRQ:
+ case CMDQ_BASE_OPCODE_DESTROY_CQ:
+ case CMDQ_BASE_OPCODE_DEALLOCATE_KEY:
+ case CMDQ_BASE_OPCODE_DEREGISTER_MR:
+ case CMDQ_BASE_OPCODE_DELETE_GID:
+ case CMDQ_BASE_OPCODE_DESTROY_QP1:
+ case CMDQ_BASE_OPCODE_DESTROY_AH:
+ case CMDQ_BASE_OPCODE_DEINITIALIZE_FW:
+ case CMDQ_BASE_OPCODE_MODIFY_ROCE_CC:
+ case CMDQ_BASE_OPCODE_SET_LINK_AGGR_MODE:
+ return 0;
+ default:
+ return -ETIMEDOUT;
+ }
+}
+
+void bng_re_free_rcfw_channel(struct bng_re_rcfw *rcfw)
+{
+ kfree(rcfw->crsqe_tbl);
+ bng_re_free_hwq(rcfw->res, &rcfw->cmdq.hwq);
+ bng_re_free_hwq(rcfw->res, &rcfw->creq.hwq);
+ rcfw->pdev = NULL;
+}
+
+int bng_re_alloc_fw_channel(struct bng_re_res *res,
+ struct bng_re_rcfw *rcfw)
+{
+ struct bng_re_hwq_attr hwq_attr = {};
+ struct bng_re_sg_info sginfo = {};
+ struct bng_re_cmdq_ctx *cmdq;
+ struct bng_re_creq_ctx *creq;
+
+ rcfw->pdev = res->pdev;
+ cmdq = &rcfw->cmdq;
+ creq = &rcfw->creq;
+ rcfw->res = res;
+
+ sginfo.pgsize = PAGE_SIZE;
+ sginfo.pgshft = PAGE_SHIFT;
+
+ hwq_attr.sginfo = &sginfo;
+ hwq_attr.res = rcfw->res;
+ hwq_attr.depth = BNG_FW_CREQE_MAX_CNT;
+ hwq_attr.stride = BNG_FW_CREQE_UNITS;
+ hwq_attr.type = BNG_HWQ_TYPE_QUEUE;
+
+ if (bng_re_alloc_init_hwq(&creq->hwq, &hwq_attr)) {
+ dev_err(&rcfw->pdev->dev,
+ "HW channel CREQ allocation failed\n");
+ goto fail;
+ }
+
+ rcfw->cmdq_depth = BNG_FW_CMDQE_MAX_CNT;
+
+ sginfo.pgsize = bng_fw_cmdqe_page_size(rcfw->cmdq_depth);
+ hwq_attr.depth = rcfw->cmdq_depth & 0x7FFFFFFF;
+ hwq_attr.stride = BNG_FW_CMDQE_UNITS;
+ hwq_attr.type = BNG_HWQ_TYPE_CTX;
+ if (bng_re_alloc_init_hwq(&cmdq->hwq, &hwq_attr)) {
+ dev_err(&rcfw->pdev->dev,
+ "HW channel CMDQ allocation failed\n");
+ goto fail;
+ }
+
+ rcfw->crsqe_tbl = kcalloc(cmdq->hwq.max_elements,
+ sizeof(*rcfw->crsqe_tbl), GFP_KERNEL);
+ if (!rcfw->crsqe_tbl)
+ goto fail;
+
+ spin_lock_init(&rcfw->tbl_lock);
+
+ rcfw->max_timeout = res->cctx->hwrm_cmd_max_timeout;
+ return 0;
+
+fail:
+ bng_re_free_rcfw_channel(rcfw);
+ return -ENOMEM;
+}
+
+static int bng_re_process_qp_event(struct bng_re_rcfw *rcfw,
+ struct creq_qp_event *qp_event,
+ u32 *num_wait)
+{
+ struct bng_re_hwq *hwq = &rcfw->cmdq.hwq;
+ struct bng_re_crsqe *crsqe;
+ u32 req_size;
+ u16 cookie;
+ bool is_waiter_alive;
+ struct pci_dev *pdev;
+ u32 wait_cmds = 0;
+ int rc = 0;
+
+ pdev = rcfw->pdev;
+ switch (qp_event->event) {
+ case CREQ_QP_EVENT_EVENT_QP_ERROR_NOTIFICATION:
+ dev_err(&pdev->dev, "Received QP error notification\n");
+ break;
+ default:
+ /*
+ * Command Response
+ * cmdq->lock needs to be acquired to synchronie
+ * the command send and completion reaping. This function
+ * is always called with creq->lock held. Using
+ * the nested variant of spin_lock.
+ *
+ */
+
+ spin_lock_nested(&hwq->lock, SINGLE_DEPTH_NESTING);
+ cookie = le16_to_cpu(qp_event->cookie);
+ cookie &= BNG_FW_MAX_COOKIE_VALUE;
+ crsqe = &rcfw->crsqe_tbl[cookie];
+
+ if (WARN_ONCE(test_bit(FIRMWARE_STALL_DETECTED,
+ &rcfw->cmdq.flags),
+ "Unreponsive rcfw channel detected.!!")) {
+ dev_info(&pdev->dev,
+ "rcfw timedout: cookie = %#x, free_slots = %d",
+ cookie, crsqe->free_slots);
+ spin_unlock(&hwq->lock);
+ return rc;
+ }
+
+ if (crsqe->is_waiter_alive) {
+ if (crsqe->resp) {
+ memcpy(crsqe->resp, qp_event, sizeof(*qp_event));
+ /* Insert write memory barrier to ensure that
+ * response data is copied before clearing the
+ * flags
+ */
+ smp_wmb();
+ }
+ }
+
+ wait_cmds++;
+
+ req_size = crsqe->req_size;
+ is_waiter_alive = crsqe->is_waiter_alive;
+
+ crsqe->req_size = 0;
+ if (!is_waiter_alive)
+ crsqe->resp = NULL;
+
+ crsqe->is_in_used = false;
+
+ hwq->cons += req_size;
+
+ spin_unlock(&hwq->lock);
+ }
+ *num_wait += wait_cmds;
+ return rc;
+}
+
+/* function events */
+static int bng_re_process_func_event(struct bng_re_rcfw *rcfw,
+ struct creq_func_event *func_event)
+{
+ switch (func_event->event) {
+ case CREQ_FUNC_EVENT_EVENT_TX_WQE_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_TX_DATA_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_RX_WQE_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_RX_DATA_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_CQ_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_TQM_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_CFCQ_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_CFCS_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_CFCC_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_CFCM_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_TIM_ERROR:
+ case CREQ_FUNC_EVENT_EVENT_VF_COMM_REQUEST:
+ case CREQ_FUNC_EVENT_EVENT_RESOURCE_EXHAUSTED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* CREQ Completion handlers */
+static void bng_re_service_creq(struct tasklet_struct *t)
+{
+ struct bng_re_rcfw *rcfw = from_tasklet(rcfw, t, creq.creq_tasklet);
+ struct bng_re_creq_ctx *creq = &rcfw->creq;
+ u32 type, budget = BNG_FW_CREQ_ENTRY_POLL_BUDGET;
+ struct bng_re_hwq *hwq = &creq->hwq;
+ struct creq_base *creqe;
+ u32 num_wakeup = 0;
+ u32 hw_polled = 0;
+
+ /* Service the CREQ until budget is over */
+ spin_lock_bh(&hwq->lock);
+ while (budget > 0) {
+ creqe = bng_re_get_qe(hwq, hwq->cons, NULL);
+ if (!BNG_FW_CREQ_CMP_VALID(creqe, creq->creq_db.dbinfo.flags))
+ break;
+ /* The valid test of the entry must be done first before
+ * reading any further.
+ */
+ dma_rmb();
+
+ type = creqe->type & CREQ_BASE_TYPE_MASK;
+ switch (type) {
+ case CREQ_BASE_TYPE_QP_EVENT:
+ bng_re_process_qp_event
+ (rcfw, (struct creq_qp_event *)creqe,
+ &num_wakeup);
+ creq->stats.creq_qp_event_processed++;
+ break;
+ case CREQ_BASE_TYPE_FUNC_EVENT:
+ if (!bng_re_process_func_event
+ (rcfw, (struct creq_func_event *)creqe))
+ creq->stats.creq_func_event_processed++;
+ else
+ dev_warn(&rcfw->pdev->dev,
+ "aeqe:%#x Not handled\n", type);
+ break;
+ default:
+ if (type != ASYNC_EVENT_CMPL_TYPE_HWRM_ASYNC_EVENT)
+ dev_warn(&rcfw->pdev->dev,
+ "creqe with event 0x%x not handled\n",
+ type);
+ break;
+ }
+ budget--;
+ hw_polled++;
+ bng_re_hwq_incr_cons(hwq->max_elements, &hwq->cons,
+ 1, &creq->creq_db.dbinfo.flags);
+ }
+
+ if (hw_polled)
+ bng_re_ring_nq_db(&creq->creq_db.dbinfo,
+ rcfw->res->cctx, true);
+ spin_unlock_bh(&hwq->lock);
+ if (num_wakeup)
+ wake_up_nr(&rcfw->cmdq.waitq, num_wakeup);
+}
+
+static int __send_message_basic_sanity(struct bng_re_rcfw *rcfw,
+ struct bng_re_cmdqmsg *msg,
+ u8 opcode)
+{
+ struct bng_re_cmdq_ctx *cmdq;
+
+ cmdq = &rcfw->cmdq;
+
+ if (test_bit(FIRMWARE_STALL_DETECTED, &cmdq->flags))
+ return -ETIMEDOUT;
+
+ if (test_bit(FIRMWARE_INITIALIZED_FLAG, &cmdq->flags) &&
+ opcode == CMDQ_BASE_OPCODE_INITIALIZE_FW) {
+ dev_err(&rcfw->pdev->dev, "RCFW already initialized!");
+ return -EINVAL;
+ }
+
+ if (!test_bit(FIRMWARE_INITIALIZED_FLAG, &cmdq->flags) &&
+ (opcode != CMDQ_BASE_OPCODE_QUERY_FUNC &&
+ opcode != CMDQ_BASE_OPCODE_INITIALIZE_FW &&
+ opcode != CMDQ_BASE_OPCODE_QUERY_VERSION)) {
+ dev_err(&rcfw->pdev->dev,
+ "RCFW not initialized, reject opcode 0x%x",
+ opcode);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int __send_message(struct bng_re_rcfw *rcfw,
+ struct bng_re_cmdqmsg *msg, u8 opcode)
+{
+ u32 bsize, free_slots, required_slots;
+ struct bng_re_cmdq_ctx *cmdq;
+ struct bng_re_crsqe *crsqe;
+ struct bng_fw_cmdqe *cmdqe;
+ struct bng_re_hwq *hwq;
+ u32 sw_prod, cmdq_prod;
+ struct pci_dev *pdev;
+ u16 cookie;
+ u8 *preq;
+
+ cmdq = &rcfw->cmdq;
+ hwq = &cmdq->hwq;
+ pdev = rcfw->pdev;
+
+ /* Cmdq are in 16-byte units, each request can consume 1 or more
+ * cmdqe
+ */
+ spin_lock_bh(&hwq->lock);
+ required_slots = bng_re_get_cmd_slots(msg->req);
+ free_slots = HWQ_FREE_SLOTS(hwq);
+ cookie = cmdq->seq_num & BNG_FW_MAX_COOKIE_VALUE;
+ crsqe = &rcfw->crsqe_tbl[cookie];
+
+ if (required_slots >= free_slots) {
+ dev_info_ratelimited(&pdev->dev,
+ "CMDQ is full req/free %d/%d!",
+ required_slots, free_slots);
+ spin_unlock_bh(&hwq->lock);
+ return -EAGAIN;
+ }
+ __set_cmdq_base_cookie(msg->req, msg->req_sz, cpu_to_le16(cookie));
+
+ bsize = bng_re_set_cmd_slots(msg->req);
+ crsqe->free_slots = free_slots;
+ crsqe->resp = (struct creq_qp_event *)msg->resp;
+ crsqe->is_waiter_alive = true;
+ crsqe->is_in_used = true;
+ crsqe->opcode = opcode;
+
+ crsqe->req_size = __get_cmdq_base_cmd_size(msg->req, msg->req_sz);
+ if (__get_cmdq_base_resp_size(msg->req, msg->req_sz) && msg->sb) {
+ struct bng_re_rcfw_sbuf *sbuf = msg->sb;
+
+ __set_cmdq_base_resp_addr(msg->req, msg->req_sz,
+ cpu_to_le64(sbuf->dma_addr));
+ __set_cmdq_base_resp_size(msg->req, msg->req_sz,
+ ALIGN(sbuf->size,
+ BNG_FW_CMDQE_UNITS) /
+ BNG_FW_CMDQE_UNITS);
+ }
+
+ preq = (u8 *)msg->req;
+ do {
+ /* Locate the next cmdq slot */
+ sw_prod = HWQ_CMP(hwq->prod, hwq);
+ cmdqe = bng_re_get_qe(hwq, sw_prod, NULL);
+ /* Copy a segment of the req cmd to the cmdq */
+ memset(cmdqe, 0, sizeof(*cmdqe));
+ memcpy(cmdqe, preq, min_t(u32, bsize, sizeof(*cmdqe)));
+ preq += min_t(u32, bsize, sizeof(*cmdqe));
+ bsize -= min_t(u32, bsize, sizeof(*cmdqe));
+ hwq->prod++;
+ } while (bsize > 0);
+ cmdq->seq_num++;
+
+ cmdq_prod = hwq->prod & 0xFFFF;
+ if (test_bit(FIRMWARE_FIRST_FLAG, &cmdq->flags)) {
+ /* The very first doorbell write
+ * is required to set this flag
+ * which prompts the FW to reset
+ * its internal pointers
+ */
+ cmdq_prod |= BIT(FIRMWARE_FIRST_FLAG);
+ clear_bit(FIRMWARE_FIRST_FLAG, &cmdq->flags);
+ }
+ /* ring CMDQ DB */
+ wmb();
+ writel(cmdq_prod, cmdq->cmdq_mbox.prod);
+ writel(BNG_FW_CMDQ_TRIG_VAL, cmdq->cmdq_mbox.db);
+ spin_unlock_bh(&hwq->lock);
+ /* Return the CREQ response pointer */
+ return 0;
+}
+
+/**
+ * __wait_for_resp - Don't hold the cpu context and wait for response
+ * @rcfw: rcfw channel instance of rdev
+ * @cookie: cookie to track the command
+ *
+ * Wait for command completion in sleepable context.
+ *
+ * Returns:
+ * 0 if command is completed by firmware.
+ * Non zero error code for rest of the case.
+ */
+static int __wait_for_resp(struct bng_re_rcfw *rcfw, u16 cookie)
+{
+ struct bng_re_cmdq_ctx *cmdq;
+ struct bng_re_crsqe *crsqe;
+
+ cmdq = &rcfw->cmdq;
+ crsqe = &rcfw->crsqe_tbl[cookie];
+
+ do {
+ wait_event_timeout(cmdq->waitq,
+ !crsqe->is_in_used,
+ secs_to_jiffies(rcfw->max_timeout));
+
+ if (!crsqe->is_in_used)
+ return 0;
+
+ bng_re_service_creq(&rcfw->creq.creq_tasklet);
+
+ if (!crsqe->is_in_used)
+ return 0;
+ } while (true);
+};
+
+/**
+ * bng_re_rcfw_send_message - interface to send
+ * and complete rcfw command.
+ * @rcfw: rcfw channel instance of rdev
+ * @msg: message to send
+ *
+ * This function does not account shadow queue depth. It will send
+ * all the command unconditionally as long as send queue is not full.
+ *
+ * Returns:
+ * 0 if command completed by firmware.
+ * Non zero if the command is not completed by firmware.
+ */
+int bng_re_rcfw_send_message(struct bng_re_rcfw *rcfw,
+ struct bng_re_cmdqmsg *msg)
+{
+ struct creq_qp_event *evnt = (struct creq_qp_event *)msg->resp;
+ struct bng_re_crsqe *crsqe;
+ u16 cookie;
+ int rc;
+ u8 opcode;
+
+ opcode = __get_cmdq_base_opcode(msg->req, msg->req_sz);
+
+ rc = __send_message_basic_sanity(rcfw, msg, opcode);
+ if (rc)
+ return rc == -ENXIO ? bng_re_map_rc(opcode) : rc;
+
+ rc = __send_message(rcfw, msg, opcode);
+ if (rc)
+ return rc;
+
+ cookie = le16_to_cpu(__get_cmdq_base_cookie(msg->req, msg->req_sz))
+ & BNG_FW_MAX_COOKIE_VALUE;
+
+ rc = __wait_for_resp(rcfw, cookie);
+
+ if (rc) {
+ spin_lock_bh(&rcfw->cmdq.hwq.lock);
+ crsqe = &rcfw->crsqe_tbl[cookie];
+ crsqe->is_waiter_alive = false;
+ if (rc == -ENODEV)
+ set_bit(FIRMWARE_STALL_DETECTED, &rcfw->cmdq.flags);
+ spin_unlock_bh(&rcfw->cmdq.hwq.lock);
+ return -ETIMEDOUT;
+ }
+
+ if (evnt->status) {
+ /* failed with status */
+ dev_err(&rcfw->pdev->dev, "cmdq[%#x]=%#x status %#x\n",
+ cookie, opcode, evnt->status);
+ rc = -EIO;
+ }
+
+ return rc;
+}
+
+static int bng_re_map_cmdq_mbox(struct bng_re_rcfw *rcfw)
+{
+ struct bng_re_cmdq_mbox *mbox;
+ resource_size_t bar_reg;
+ struct pci_dev *pdev;
+
+ pdev = rcfw->pdev;
+ mbox = &rcfw->cmdq.cmdq_mbox;
+
+ mbox->reg.bar_id = BNG_FW_COMM_PCI_BAR_REGION;
+ mbox->reg.len = BNG_FW_COMM_SIZE;
+ mbox->reg.bar_base = pci_resource_start(pdev, mbox->reg.bar_id);
+ if (!mbox->reg.bar_base) {
+ dev_err(&pdev->dev,
+ "CMDQ BAR region %d resc start is 0!\n",
+ mbox->reg.bar_id);
+ return -ENOMEM;
+ }
+
+ bar_reg = mbox->reg.bar_base + BNG_FW_COMM_BASE_OFFSET;
+ mbox->reg.len = BNG_FW_COMM_SIZE;
+ mbox->reg.bar_reg = ioremap(bar_reg, mbox->reg.len);
+ if (!mbox->reg.bar_reg) {
+ dev_err(&pdev->dev,
+ "CMDQ BAR region %d mapping failed\n",
+ mbox->reg.bar_id);
+ return -ENOMEM;
+ }
+
+ mbox->prod = (void __iomem *)(mbox->reg.bar_reg +
+ BNG_FW_PF_VF_COMM_PROD_OFFSET);
+ mbox->db = (void __iomem *)(mbox->reg.bar_reg + BNG_FW_COMM_TRIG_OFFSET);
+ return 0;
+}
+
+static irqreturn_t bng_re_creq_irq(int irq, void *dev_instance)
+{
+ struct bng_re_rcfw *rcfw = dev_instance;
+ struct bng_re_creq_ctx *creq;
+ struct bng_re_hwq *hwq;
+ u32 sw_cons;
+
+ creq = &rcfw->creq;
+ hwq = &creq->hwq;
+ /* Prefetch the CREQ element */
+ sw_cons = HWQ_CMP(hwq->cons, hwq);
+ bng_re_get_qe(hwq, sw_cons, NULL);
+
+ tasklet_schedule(&creq->creq_tasklet);
+ return IRQ_HANDLED;
+}
+
+int bng_re_rcfw_start_irq(struct bng_re_rcfw *rcfw, int msix_vector,
+ bool need_init)
+{
+ struct bng_re_creq_ctx *creq;
+ struct bng_re_res *res;
+ int rc;
+
+ creq = &rcfw->creq;
+ res = rcfw->res;
+
+ if (creq->irq_handler_avail)
+ return -EFAULT;
+
+ creq->msix_vec = msix_vector;
+ if (need_init)
+ tasklet_setup(&creq->creq_tasklet, bng_re_service_creq);
+ else
+ tasklet_enable(&creq->creq_tasklet);
+
+ creq->irq_name = kasprintf(GFP_KERNEL, "bng_re-creq@pci:%s",
+ pci_name(res->pdev));
+ if (!creq->irq_name)
+ return -ENOMEM;
+ rc = request_irq(creq->msix_vec, bng_re_creq_irq, 0,
+ creq->irq_name, rcfw);
+ if (rc) {
+ kfree(creq->irq_name);
+ creq->irq_name = NULL;
+ tasklet_disable(&creq->creq_tasklet);
+ return rc;
+ }
+ creq->irq_handler_avail = true;
+
+ bng_re_ring_nq_db(&creq->creq_db.dbinfo, res->cctx, true);
+ atomic_inc(&rcfw->rcfw_intr_enabled);
+
+ return 0;
+}
+
+static int bng_re_map_creq_db(struct bng_re_rcfw *rcfw, u32 reg_offt)
+{
+ struct bng_re_creq_db *creq_db;
+ resource_size_t bar_reg;
+ struct pci_dev *pdev;
+
+ pdev = rcfw->pdev;
+ creq_db = &rcfw->creq.creq_db;
+
+ creq_db->dbinfo.flags = 0;
+ creq_db->reg.bar_id = BNG_FW_COMM_CONS_PCI_BAR_REGION;
+ creq_db->reg.bar_base = pci_resource_start(pdev, creq_db->reg.bar_id);
+ if (!creq_db->reg.bar_id)
+ dev_err(&pdev->dev,
+ "CREQ BAR region %d resc start is 0!",
+ creq_db->reg.bar_id);
+
+ bar_reg = creq_db->reg.bar_base + reg_offt;
+
+ creq_db->reg.len = BNG_FW_CREQ_DB_LEN;
+ creq_db->reg.bar_reg = ioremap(bar_reg, creq_db->reg.len);
+ if (!creq_db->reg.bar_reg) {
+ dev_err(&pdev->dev,
+ "CREQ BAR region %d mapping failed",
+ creq_db->reg.bar_id);
+ return -ENOMEM;
+ }
+ creq_db->dbinfo.db = creq_db->reg.bar_reg;
+ creq_db->dbinfo.hwq = &rcfw->creq.hwq;
+ creq_db->dbinfo.xid = rcfw->creq.ring_id;
+ return 0;
+}
+
+void bng_re_rcfw_stop_irq(struct bng_re_rcfw *rcfw, bool kill)
+{
+ struct bng_re_creq_ctx *creq;
+
+ creq = &rcfw->creq;
+
+ if (!creq->irq_handler_avail)
+ return;
+
+ creq->irq_handler_avail = false;
+ /* Mask h/w interrupts */
+ bng_re_ring_nq_db(&creq->creq_db.dbinfo, rcfw->res->cctx, false);
+ /* Sync with last running IRQ-handler */
+ synchronize_irq(creq->msix_vec);
+ free_irq(creq->msix_vec, rcfw);
+ kfree(creq->irq_name);
+ creq->irq_name = NULL;
+ atomic_set(&rcfw->rcfw_intr_enabled, 0);
+ if (kill)
+ tasklet_kill(&creq->creq_tasklet);
+ tasklet_disable(&creq->creq_tasklet);
+}
+
+void bng_re_disable_rcfw_channel(struct bng_re_rcfw *rcfw)
+{
+ struct bng_re_creq_ctx *creq;
+ struct bng_re_cmdq_ctx *cmdq;
+
+ creq = &rcfw->creq;
+ cmdq = &rcfw->cmdq;
+ /* Make sure the HW channel is stopped! */
+ bng_re_rcfw_stop_irq(rcfw, true);
+
+ iounmap(cmdq->cmdq_mbox.reg.bar_reg);
+ iounmap(creq->creq_db.reg.bar_reg);
+
+ cmdq->cmdq_mbox.reg.bar_reg = NULL;
+ creq->creq_db.reg.bar_reg = NULL;
+ creq->msix_vec = 0;
+}
+
+static void bng_re_start_rcfw(struct bng_re_rcfw *rcfw)
+{
+ struct bng_re_cmdq_ctx *cmdq;
+ struct bng_re_creq_ctx *creq;
+ struct bng_re_cmdq_mbox *mbox;
+ struct cmdq_init init = {0};
+
+ cmdq = &rcfw->cmdq;
+ creq = &rcfw->creq;
+ mbox = &cmdq->cmdq_mbox;
+
+ init.cmdq_pbl = cpu_to_le64(cmdq->hwq.pbl[BNG_PBL_LVL_0].pg_map_arr[0]);
+ init.cmdq_size_cmdq_lvl =
+ cpu_to_le16(((rcfw->cmdq_depth <<
+ CMDQ_INIT_CMDQ_SIZE_SFT) &
+ CMDQ_INIT_CMDQ_SIZE_MASK) |
+ ((cmdq->hwq.level <<
+ CMDQ_INIT_CMDQ_LVL_SFT) &
+ CMDQ_INIT_CMDQ_LVL_MASK));
+ init.creq_ring_id = cpu_to_le16(creq->ring_id);
+ /* Write to the mailbox register */
+ __iowrite32_copy(mbox->reg.bar_reg, &init, sizeof(init) / 4);
+}
+
+int bng_re_enable_fw_channel(struct bng_re_rcfw *rcfw,
+ int msix_vector,
+ int cp_bar_reg_off)
+{
+ struct bng_re_cmdq_ctx *cmdq;
+ int rc;
+
+ cmdq = &rcfw->cmdq;
+
+ /* Assign defaults */
+ cmdq->seq_num = 0;
+ set_bit(FIRMWARE_FIRST_FLAG, &cmdq->flags);
+ init_waitqueue_head(&cmdq->waitq);
+
+ rc = bng_re_map_cmdq_mbox(rcfw);
+ if (rc)
+ return rc;
+
+ rc = bng_re_map_creq_db(rcfw, cp_bar_reg_off);
+ if (rc)
+ return rc;
+
+ rc = bng_re_rcfw_start_irq(rcfw, msix_vector, true);
+ if (rc) {
+ dev_err(&rcfw->pdev->dev,
+ "Failed to request IRQ for CREQ rc = 0x%x\n", rc);
+ bng_re_disable_rcfw_channel(rcfw);
+ return rc;
+ }
+
+ bng_re_start_rcfw(rcfw);
+ return 0;
+}
+
+int bng_re_deinit_rcfw(struct bng_re_rcfw *rcfw)
+{
+ struct creq_deinitialize_fw_resp resp = {};
+ struct cmdq_deinitialize_fw req = {};
+ struct bng_re_cmdqmsg msg = {};
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_DEINITIALIZE_FW,
+ sizeof(req));
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL,
+ sizeof(req), sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ return rc;
+
+ clear_bit(FIRMWARE_INITIALIZED_FLAG, &rcfw->cmdq.flags);
+ return 0;
+}
+static inline bool _is_hw_retx_supported(u16 dev_cap_flags)
+{
+ return dev_cap_flags &
+ (CREQ_QUERY_FUNC_RESP_SB_HW_REQUESTER_RETX_ENABLED |
+ CREQ_QUERY_FUNC_RESP_SB_HW_RESPONDER_RETX_ENABLED);
+}
+
+#define BNG_RE_HW_RETX(a) _is_hw_retx_supported((a))
+static inline bool _is_optimize_modify_qp_supported(u16 dev_cap_ext_flags2)
+{
+ return dev_cap_ext_flags2 &
+ CREQ_QUERY_FUNC_RESP_SB_OPTIMIZE_MODIFY_QP_SUPPORTED;
+}
+
+int bng_re_init_rcfw(struct bng_re_rcfw *rcfw,
+ struct bng_re_stats *stats_ctx)
+{
+ struct creq_initialize_fw_resp resp = {};
+ struct cmdq_initialize_fw req = {};
+ struct bng_re_cmdqmsg msg = {};
+ int rc;
+ u16 flags = 0;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_INITIALIZE_FW,
+ sizeof(req));
+ /* Supply (log-base-2-of-host-page-size - base-page-shift)
+ * to bono to adjust the doorbell page sizes.
+ */
+ req.log2_dbr_pg_size = cpu_to_le16(PAGE_SHIFT -
+ BNG_FW_DBR_BASE_PAGE_SHIFT);
+ if (BNG_RE_HW_RETX(rcfw->res->dattr->dev_cap_flags))
+ flags |= CMDQ_INITIALIZE_FW_FLAGS_HW_REQUESTER_RETX_SUPPORTED;
+ if (_is_optimize_modify_qp_supported(rcfw->res->dattr->dev_cap_flags2))
+ flags |= CMDQ_INITIALIZE_FW_FLAGS_OPTIMIZE_MODIFY_QP_SUPPORTED;
+ req.flags |= cpu_to_le16(flags);
+ req.stat_ctx_id = cpu_to_le32(stats_ctx->fw_id);
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req), sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ return rc;
+ set_bit(FIRMWARE_INITIALIZED_FLAG, &rcfw->cmdq.flags);
+ return 0;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_fw.h b/drivers/infiniband/hw/bng_re/bng_fw.h
new file mode 100644
index 000000000000..c89c926ec2fc
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_fw.h
@@ -0,0 +1,211 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2025 Broadcom.
+
+#ifndef __BNG_FW_H__
+#define __BNG_FW_H__
+
+#include "bng_tlv.h"
+
+/* FW DB related */
+#define BNG_FW_CMDQ_TRIG_VAL 1
+#define BNG_FW_COMM_PCI_BAR_REGION 0
+#define BNG_FW_COMM_CONS_PCI_BAR_REGION 2
+#define BNG_FW_DBR_BASE_PAGE_SHIFT 12
+#define BNG_FW_COMM_SIZE 0x104
+#define BNG_FW_COMM_BASE_OFFSET 0x600
+#define BNG_FW_COMM_TRIG_OFFSET 0x100
+#define BNG_FW_PF_VF_COMM_PROD_OFFSET 0xc
+#define BNG_FW_CREQ_DB_LEN 8
+
+/* CREQ */
+#define BNG_FW_CREQE_MAX_CNT (64 * 1024)
+#define BNG_FW_CREQE_UNITS 16
+#define BNG_FW_CREQ_ENTRY_POLL_BUDGET 0x100
+#define BNG_FW_CREQ_CMP_VALID(hdr, pass) \
+ (!!((hdr)->v & CREQ_BASE_V) == \
+ !((pass) & BNG_RE_FLAG_EPOCH_CONS_MASK))
+#define BNG_FW_CREQ_ENTRY_POLL_BUDGET 0x100
+
+/* CMDQ */
+struct bng_fw_cmdqe {
+ u8 data[16];
+};
+
+#define BNG_FW_CMDQE_MAX_CNT 8192
+#define BNG_FW_CMDQE_UNITS sizeof(struct bng_fw_cmdqe)
+#define BNG_FW_CMDQE_BYTES(depth) ((depth) * BNG_FW_CMDQE_UNITS)
+
+#define BNG_FW_MAX_COOKIE_VALUE (BNG_FW_CMDQE_MAX_CNT - 1)
+#define BNG_FW_CMD_IS_BLOCKING 0x8000
+
+/* Crsq buf is 1024-Byte */
+struct bng_re_crsbe {
+ u8 data[1024];
+};
+
+
+static inline u32 bng_fw_cmdqe_npages(u32 depth)
+{
+ u32 npages;
+
+ npages = BNG_FW_CMDQE_BYTES(depth) / PAGE_SIZE;
+ if (BNG_FW_CMDQE_BYTES(depth) % PAGE_SIZE)
+ npages++;
+ return npages;
+}
+
+static inline u32 bng_fw_cmdqe_page_size(u32 depth)
+{
+ return (bng_fw_cmdqe_npages(depth) * PAGE_SIZE);
+}
+struct bng_re_cmdq_mbox {
+ struct bng_re_reg_desc reg;
+ void __iomem *prod;
+ void __iomem *db;
+};
+
+/* HWQ */
+struct bng_re_cmdq_ctx {
+ struct bng_re_hwq hwq;
+ struct bng_re_cmdq_mbox cmdq_mbox;
+ unsigned long flags;
+#define FIRMWARE_INITIALIZED_FLAG (0)
+#define FIRMWARE_STALL_DETECTED (3)
+#define FIRMWARE_FIRST_FLAG (31)
+ wait_queue_head_t waitq;
+ u32 seq_num;
+};
+
+struct bng_re_creq_db {
+ struct bng_re_reg_desc reg;
+ struct bng_re_db_info dbinfo;
+};
+
+struct bng_re_creq_stat {
+ u64 creq_qp_event_processed;
+ u64 creq_func_event_processed;
+};
+
+struct bng_re_creq_ctx {
+ struct bng_re_hwq hwq;
+ struct bng_re_creq_db creq_db;
+ struct bng_re_creq_stat stats;
+ struct tasklet_struct creq_tasklet;
+ u16 ring_id;
+ int msix_vec;
+ bool irq_handler_avail;
+ char *irq_name;
+};
+
+struct bng_re_crsqe {
+ struct creq_qp_event *resp;
+ u32 req_size;
+ /* Free slots at the time of submission */
+ u32 free_slots;
+ u8 opcode;
+ bool is_waiter_alive;
+ bool is_in_used;
+};
+
+struct bng_re_rcfw_sbuf {
+ void *sb;
+ dma_addr_t dma_addr;
+ u32 size;
+};
+
+/* RoCE FW Communication Channels */
+struct bng_re_rcfw {
+ struct pci_dev *pdev;
+ struct bng_re_res *res;
+ struct bng_re_cmdq_ctx cmdq;
+ struct bng_re_creq_ctx creq;
+ struct bng_re_crsqe *crsqe_tbl;
+ /* To synchronize the qp-handle hash table */
+ spinlock_t tbl_lock;
+ u32 cmdq_depth;
+ /* cached from chip cctx for quick reference in slow path */
+ u16 max_timeout;
+ atomic_t rcfw_intr_enabled;
+};
+
+struct bng_re_cmdqmsg {
+ struct cmdq_base *req;
+ struct creq_base *resp;
+ void *sb;
+ u32 req_sz;
+ u32 res_sz;
+ u8 block;
+};
+
+static inline void bng_re_rcfw_cmd_prep(struct cmdq_base *req,
+ u8 opcode, u8 cmd_size)
+{
+ req->opcode = opcode;
+ req->cmd_size = cmd_size;
+}
+
+static inline void bng_re_fill_cmdqmsg(struct bng_re_cmdqmsg *msg,
+ void *req, void *resp, void *sb,
+ u32 req_sz, u32 res_sz, u8 block)
+{
+ msg->req = req;
+ msg->resp = resp;
+ msg->sb = sb;
+ msg->req_sz = req_sz;
+ msg->res_sz = res_sz;
+ msg->block = block;
+}
+
+/* Get the number of command units required for the req. The
+ * function returns correct value only if called before
+ * setting using bng_re_set_cmd_slots
+ */
+static inline u32 bng_re_get_cmd_slots(struct cmdq_base *req)
+{
+ u32 cmd_units = 0;
+
+ if (HAS_TLV_HEADER(req)) {
+ struct roce_tlv *tlv_req = (struct roce_tlv *)req;
+
+ cmd_units = tlv_req->total_size;
+ } else {
+ cmd_units = (req->cmd_size + BNG_FW_CMDQE_UNITS - 1) /
+ BNG_FW_CMDQE_UNITS;
+ }
+
+ return cmd_units;
+}
+
+static inline u32 bng_re_set_cmd_slots(struct cmdq_base *req)
+{
+ u32 cmd_byte = 0;
+
+ if (HAS_TLV_HEADER(req)) {
+ struct roce_tlv *tlv_req = (struct roce_tlv *)req;
+
+ cmd_byte = tlv_req->total_size * BNG_FW_CMDQE_UNITS;
+ } else {
+ cmd_byte = req->cmd_size;
+ req->cmd_size = (req->cmd_size + BNG_FW_CMDQE_UNITS - 1) /
+ BNG_FW_CMDQE_UNITS;
+ }
+
+ return cmd_byte;
+}
+
+void bng_re_free_rcfw_channel(struct bng_re_rcfw *rcfw);
+int bng_re_alloc_fw_channel(struct bng_re_res *res,
+ struct bng_re_rcfw *rcfw);
+int bng_re_enable_fw_channel(struct bng_re_rcfw *rcfw,
+ int msix_vector,
+ int cp_bar_reg_off);
+void bng_re_disable_rcfw_channel(struct bng_re_rcfw *rcfw);
+int bng_re_rcfw_start_irq(struct bng_re_rcfw *rcfw, int msix_vector,
+ bool need_init);
+void bng_re_rcfw_stop_irq(struct bng_re_rcfw *rcfw, bool kill);
+int bng_re_rcfw_send_message(struct bng_re_rcfw *rcfw,
+ struct bng_re_cmdqmsg *msg);
+int bng_re_init_rcfw(struct bng_re_rcfw *rcfw,
+ struct bng_re_stats *stats_ctx);
+int bng_re_deinit_rcfw(struct bng_re_rcfw *rcfw);
+#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_re.h b/drivers/infiniband/hw/bng_re/bng_re.h
new file mode 100644
index 000000000000..dae4862621a7
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_re.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2025 Broadcom.
+
+#ifndef __BNG_RE_H__
+#define __BNG_RE_H__
+
+#include "bng_res.h"
+
+#define BNG_RE_ADEV_NAME "bng_en"
+
+#define BNG_RE_DESC "Broadcom 800G RoCE Driver"
+
+#define rdev_to_dev(rdev) ((rdev) ? (&(rdev)->ibdev.dev) : NULL)
+
+#define BNG_RE_MIN_MSIX 2
+#define BNG_RE_MAX_MSIX BNGE_MAX_ROCE_MSIX
+
+#define BNG_RE_CREQ_NQ_IDX 0
+
+#define BNGE_INVALID_STATS_CTX_ID -1
+/* NQ specific structures */
+struct bng_re_nq_db {
+ struct bng_re_reg_desc reg;
+ struct bng_re_db_info dbinfo;
+};
+
+struct bng_re_nq {
+ struct pci_dev *pdev;
+ struct bng_re_res *res;
+ char *name;
+ struct bng_re_hwq hwq;
+ struct bng_re_nq_db nq_db;
+ u16 ring_id;
+ int msix_vec;
+ cpumask_t mask;
+ struct tasklet_struct nq_tasklet;
+ bool requested;
+ int budget;
+ u32 load;
+
+ struct workqueue_struct *cqn_wq;
+};
+
+struct bng_re_nq_record {
+ struct bnge_msix_info msix_entries[BNG_RE_MAX_MSIX];
+ struct bng_re_nq nq[BNG_RE_MAX_MSIX];
+ int num_msix;
+ /* serialize NQ access */
+ struct mutex load_lock;
+};
+
+struct bng_re_en_dev_info {
+ struct bng_re_dev *rdev;
+ struct bnge_auxr_dev *auxr_dev;
+};
+
+struct bng_re_ring_attr {
+ dma_addr_t *dma_arr;
+ int pages;
+ int type;
+ u32 depth;
+ u32 lrid; /* Logical ring id */
+ u8 mode;
+};
+
+struct bng_re_dev {
+ struct ib_device ibdev;
+ unsigned long flags;
+#define BNG_RE_FLAG_NETDEV_REGISTERED 0
+#define BNG_RE_FLAG_RCFW_CHANNEL_EN 1
+ struct net_device *netdev;
+ struct auxiliary_device *adev;
+ struct bnge_auxr_dev *aux_dev;
+ struct bng_re_chip_ctx *chip_ctx;
+ int fn_id;
+ struct bng_re_res bng_res;
+ struct bng_re_rcfw rcfw;
+ struct bng_re_nq_record *nqr;
+ /* Device Resources */
+ struct bng_re_dev_attr *dev_attr;
+ struct dentry *dbg_root;
+ struct bng_re_stats stats_ctx;
+};
+
+#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_res.c b/drivers/infiniband/hw/bng_re/bng_res.c
new file mode 100644
index 000000000000..c50823758b53
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_res.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include <linux/pci.h>
+#include <linux/vmalloc.h>
+#include <rdma/ib_umem.h>
+
+#include <linux/bnxt/hsi.h>
+#include "bng_res.h"
+#include "roce_hsi.h"
+
+/* Stats */
+void bng_re_free_stats_ctx_mem(struct pci_dev *pdev,
+ struct bng_re_stats *stats)
+{
+ if (stats->dma) {
+ dma_free_coherent(&pdev->dev, stats->size,
+ stats->dma, stats->dma_map);
+ }
+ memset(stats, 0, sizeof(*stats));
+ stats->fw_id = -1;
+}
+
+int bng_re_alloc_stats_ctx_mem(struct pci_dev *pdev,
+ struct bng_re_chip_ctx *cctx,
+ struct bng_re_stats *stats)
+{
+ memset(stats, 0, sizeof(*stats));
+ stats->fw_id = -1;
+ stats->size = cctx->hw_stats_size;
+ stats->dma = dma_alloc_coherent(&pdev->dev, stats->size,
+ &stats->dma_map, GFP_KERNEL);
+ if (!stats->dma)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void bng_free_pbl(struct bng_re_res *res, struct bng_re_pbl *pbl)
+{
+ struct pci_dev *pdev = res->pdev;
+ int i;
+
+ for (i = 0; i < pbl->pg_count; i++) {
+ if (pbl->pg_arr[i])
+ dma_free_coherent(&pdev->dev, pbl->pg_size,
+ (void *)((unsigned long)
+ pbl->pg_arr[i] &
+ PAGE_MASK),
+ pbl->pg_map_arr[i]);
+ else
+ dev_warn(&pdev->dev,
+ "PBL free pg_arr[%d] empty?!\n", i);
+ pbl->pg_arr[i] = NULL;
+ }
+
+ vfree(pbl->pg_arr);
+ pbl->pg_arr = NULL;
+ vfree(pbl->pg_map_arr);
+ pbl->pg_map_arr = NULL;
+ pbl->pg_count = 0;
+ pbl->pg_size = 0;
+}
+
+static int bng_alloc_pbl(struct bng_re_res *res,
+ struct bng_re_pbl *pbl,
+ struct bng_re_sg_info *sginfo)
+{
+ struct pci_dev *pdev = res->pdev;
+ u32 pages;
+ int i;
+
+ if (sginfo->nopte)
+ return 0;
+ pages = sginfo->npages;
+
+ /* page ptr arrays */
+ pbl->pg_arr = vmalloc_array(pages, sizeof(void *));
+ if (!pbl->pg_arr)
+ return -ENOMEM;
+
+ pbl->pg_map_arr = vmalloc_array(pages, sizeof(dma_addr_t));
+ if (!pbl->pg_map_arr) {
+ vfree(pbl->pg_arr);
+ pbl->pg_arr = NULL;
+ return -ENOMEM;
+ }
+ pbl->pg_count = 0;
+ pbl->pg_size = sginfo->pgsize;
+
+ for (i = 0; i < pages; i++) {
+ pbl->pg_arr[i] = dma_alloc_coherent(&pdev->dev,
+ pbl->pg_size,
+ &pbl->pg_map_arr[i],
+ GFP_KERNEL);
+ if (!pbl->pg_arr[i])
+ goto fail;
+ pbl->pg_count++;
+ }
+
+ return 0;
+fail:
+ bng_free_pbl(res, pbl);
+ return -ENOMEM;
+}
+
+void bng_re_free_hwq(struct bng_re_res *res,
+ struct bng_re_hwq *hwq)
+{
+ int i;
+
+ if (!hwq->max_elements)
+ return;
+ if (hwq->level >= BNG_PBL_LVL_MAX)
+ return;
+
+ for (i = 0; i < hwq->level + 1; i++)
+ bng_free_pbl(res, &hwq->pbl[i]);
+
+ hwq->level = BNG_PBL_LVL_MAX;
+ hwq->max_elements = 0;
+ hwq->element_size = 0;
+ hwq->prod = 0;
+ hwq->cons = 0;
+}
+
+/* All HWQs are power of 2 in size */
+int bng_re_alloc_init_hwq(struct bng_re_hwq *hwq,
+ struct bng_re_hwq_attr *hwq_attr)
+{
+ u32 npages, pg_size;
+ struct bng_re_sg_info sginfo = {};
+ u32 depth, stride, npbl, npde;
+ dma_addr_t *src_phys_ptr, **dst_virt_ptr;
+ struct bng_re_res *res;
+ struct pci_dev *pdev;
+ int i, rc, lvl;
+
+ res = hwq_attr->res;
+ pdev = res->pdev;
+ pg_size = hwq_attr->sginfo->pgsize;
+ hwq->level = BNG_PBL_LVL_MAX;
+
+ depth = roundup_pow_of_two(hwq_attr->depth);
+ stride = roundup_pow_of_two(hwq_attr->stride);
+
+ npages = (depth * stride) / pg_size;
+ if ((depth * stride) % pg_size)
+ npages++;
+ if (!npages)
+ return -EINVAL;
+ hwq_attr->sginfo->npages = npages;
+
+ if (npages == MAX_PBL_LVL_0_PGS && !hwq_attr->sginfo->nopte) {
+ /* This request is Level 0, map PTE */
+ rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_0], hwq_attr->sginfo);
+ if (rc)
+ goto fail;
+ hwq->level = BNG_PBL_LVL_0;
+ goto done;
+ }
+
+ if (npages >= MAX_PBL_LVL_0_PGS) {
+ if (npages > MAX_PBL_LVL_1_PGS) {
+ u32 flag = PTU_PTE_VALID;
+ /* 2 levels of indirection */
+ npbl = npages >> MAX_PBL_LVL_1_PGS_SHIFT;
+ if (npages % BIT(MAX_PBL_LVL_1_PGS_SHIFT))
+ npbl++;
+ npde = npbl >> MAX_PDL_LVL_SHIFT;
+ if (npbl % BIT(MAX_PDL_LVL_SHIFT))
+ npde++;
+ /* Alloc PDE pages */
+ sginfo.pgsize = npde * pg_size;
+ sginfo.npages = 1;
+ rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_0], &sginfo);
+ if (rc)
+ goto fail;
+
+ /* Alloc PBL pages */
+ sginfo.npages = npbl;
+ sginfo.pgsize = PAGE_SIZE;
+ rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_1], &sginfo);
+ if (rc)
+ goto fail;
+ /* Fill PDL with PBL page pointers */
+ dst_virt_ptr =
+ (dma_addr_t **)hwq->pbl[BNG_PBL_LVL_0].pg_arr;
+ src_phys_ptr = hwq->pbl[BNG_PBL_LVL_1].pg_map_arr;
+ for (i = 0; i < hwq->pbl[BNG_PBL_LVL_1].pg_count; i++)
+ dst_virt_ptr[0][i] = src_phys_ptr[i] | flag;
+
+ /* Alloc or init PTEs */
+ rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_2],
+ hwq_attr->sginfo);
+ if (rc)
+ goto fail;
+ hwq->level = BNG_PBL_LVL_2;
+ if (hwq_attr->sginfo->nopte)
+ goto done;
+ /* Fill PBLs with PTE pointers */
+ dst_virt_ptr =
+ (dma_addr_t **)hwq->pbl[BNG_PBL_LVL_1].pg_arr;
+ src_phys_ptr = hwq->pbl[BNG_PBL_LVL_2].pg_map_arr;
+ for (i = 0; i < hwq->pbl[BNG_PBL_LVL_2].pg_count; i++) {
+ dst_virt_ptr[PTR_PG(i)][PTR_IDX(i)] =
+ src_phys_ptr[i] | PTU_PTE_VALID;
+ }
+ if (hwq_attr->type == BNG_HWQ_TYPE_QUEUE) {
+ /* Find the last pg of the size */
+ i = hwq->pbl[BNG_PBL_LVL_2].pg_count;
+ dst_virt_ptr[PTR_PG(i - 1)][PTR_IDX(i - 1)] |=
+ PTU_PTE_LAST;
+ if (i > 1)
+ dst_virt_ptr[PTR_PG(i - 2)]
+ [PTR_IDX(i - 2)] |=
+ PTU_PTE_NEXT_TO_LAST;
+ }
+ } else { /* pages < 512 npbl = 1, npde = 0 */
+ u32 flag = PTU_PTE_VALID;
+
+ /* 1 level of indirection */
+ npbl = npages >> MAX_PBL_LVL_1_PGS_SHIFT;
+ if (npages % BIT(MAX_PBL_LVL_1_PGS_SHIFT))
+ npbl++;
+ sginfo.npages = npbl;
+ sginfo.pgsize = PAGE_SIZE;
+ /* Alloc PBL page */
+ rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_0], &sginfo);
+ if (rc)
+ goto fail;
+ /* Alloc or init PTEs */
+ rc = bng_alloc_pbl(res, &hwq->pbl[BNG_PBL_LVL_1],
+ hwq_attr->sginfo);
+ if (rc)
+ goto fail;
+ hwq->level = BNG_PBL_LVL_1;
+ if (hwq_attr->sginfo->nopte)
+ goto done;
+ /* Fill PBL with PTE pointers */
+ dst_virt_ptr =
+ (dma_addr_t **)hwq->pbl[BNG_PBL_LVL_0].pg_arr;
+ src_phys_ptr = hwq->pbl[BNG_PBL_LVL_1].pg_map_arr;
+ for (i = 0; i < hwq->pbl[BNG_PBL_LVL_1].pg_count; i++)
+ dst_virt_ptr[PTR_PG(i)][PTR_IDX(i)] =
+ src_phys_ptr[i] | flag;
+ if (hwq_attr->type == BNG_HWQ_TYPE_QUEUE) {
+ /* Find the last pg of the size */
+ i = hwq->pbl[BNG_PBL_LVL_1].pg_count;
+ dst_virt_ptr[PTR_PG(i - 1)][PTR_IDX(i - 1)] |=
+ PTU_PTE_LAST;
+ if (i > 1)
+ dst_virt_ptr[PTR_PG(i - 2)]
+ [PTR_IDX(i - 2)] |=
+ PTU_PTE_NEXT_TO_LAST;
+ }
+ }
+ }
+done:
+ hwq->prod = 0;
+ hwq->cons = 0;
+ hwq->pdev = pdev;
+ hwq->depth = hwq_attr->depth;
+ hwq->max_elements = hwq->depth;
+ hwq->element_size = stride;
+ hwq->qe_ppg = pg_size / stride;
+ /* For direct access to the elements */
+ lvl = hwq->level;
+ if (hwq_attr->sginfo->nopte && hwq->level)
+ lvl = hwq->level - 1;
+ hwq->pbl_ptr = hwq->pbl[lvl].pg_arr;
+ hwq->pbl_dma_ptr = hwq->pbl[lvl].pg_map_arr;
+ spin_lock_init(&hwq->lock);
+
+ return 0;
+fail:
+ bng_re_free_hwq(res, hwq);
+ return -ENOMEM;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_res.h b/drivers/infiniband/hw/bng_re/bng_res.h
new file mode 100644
index 000000000000..9997f86d6a0e
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_res.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2025 Broadcom.
+
+#ifndef __BNG_RES_H__
+#define __BNG_RES_H__
+
+#include "roce_hsi.h"
+
+#define BNG_ROCE_FW_MAX_TIMEOUT 60
+
+#define PTR_CNT_PER_PG (PAGE_SIZE / sizeof(void *))
+#define PTR_MAX_IDX_PER_PG (PTR_CNT_PER_PG - 1)
+#define PTR_PG(x) (((x) & ~PTR_MAX_IDX_PER_PG) / PTR_CNT_PER_PG)
+#define PTR_IDX(x) ((x) & PTR_MAX_IDX_PER_PG)
+
+#define HWQ_CMP(idx, hwq) ((idx) & ((hwq)->max_elements - 1))
+#define HWQ_FREE_SLOTS(hwq) (hwq->max_elements - \
+ ((HWQ_CMP(hwq->prod, hwq)\
+ - HWQ_CMP(hwq->cons, hwq))\
+ & (hwq->max_elements - 1)))
+
+#define MAX_PBL_LVL_0_PGS 1
+#define MAX_PBL_LVL_1_PGS 512
+#define MAX_PBL_LVL_1_PGS_SHIFT 9
+#define MAX_PBL_LVL_1_PGS_FOR_LVL_2 256
+#define MAX_PBL_LVL_2_PGS (256 * 512)
+#define MAX_PDL_LVL_SHIFT 9
+
+#define BNG_RE_DBR_VALID (0x1UL << 26)
+#define BNG_RE_DBR_EPOCH_SHIFT 24
+#define BNG_RE_DBR_TOGGLE_SHIFT 25
+
+#define BNG_MAX_TQM_ALLOC_REQ 48
+
+struct bng_re_reg_desc {
+ u8 bar_id;
+ resource_size_t bar_base;
+ unsigned long offset;
+ void __iomem *bar_reg;
+ size_t len;
+};
+
+struct bng_re_db_info {
+ void __iomem *db;
+ void __iomem *priv_db;
+ struct bng_re_hwq *hwq;
+ u32 xid;
+ u32 max_slot;
+ u32 flags;
+ u8 toggle;
+};
+
+enum bng_re_db_info_flags_mask {
+ BNG_RE_FLAG_EPOCH_CONS_SHIFT = 0x0UL,
+ BNG_RE_FLAG_EPOCH_PROD_SHIFT = 0x1UL,
+ BNG_RE_FLAG_EPOCH_CONS_MASK = 0x1UL,
+ BNG_RE_FLAG_EPOCH_PROD_MASK = 0x2UL,
+};
+
+enum bng_re_db_epoch_flag_shift {
+ BNG_RE_DB_EPOCH_CONS_SHIFT = BNG_RE_DBR_EPOCH_SHIFT,
+ BNG_RE_DB_EPOCH_PROD_SHIFT = (BNG_RE_DBR_EPOCH_SHIFT - 1),
+};
+
+struct bng_re_chip_ctx {
+ u16 chip_num;
+ u16 hw_stats_size;
+ u64 hwrm_intf_ver;
+ u16 hwrm_cmd_max_timeout;
+};
+
+struct bng_re_pbl {
+ u32 pg_count;
+ u32 pg_size;
+ void **pg_arr;
+ dma_addr_t *pg_map_arr;
+};
+
+enum bng_re_pbl_lvl {
+ BNG_PBL_LVL_0,
+ BNG_PBL_LVL_1,
+ BNG_PBL_LVL_2,
+ BNG_PBL_LVL_MAX
+};
+
+enum bng_re_hwq_type {
+ BNG_HWQ_TYPE_CTX,
+ BNG_HWQ_TYPE_QUEUE
+};
+
+struct bng_re_sg_info {
+ u32 npages;
+ u32 pgshft;
+ u32 pgsize;
+ bool nopte;
+};
+
+struct bng_re_hwq_attr {
+ struct bng_re_res *res;
+ struct bng_re_sg_info *sginfo;
+ enum bng_re_hwq_type type;
+ u32 depth;
+ u32 stride;
+ u32 aux_stride;
+ u32 aux_depth;
+};
+
+struct bng_re_hwq {
+ struct pci_dev *pdev;
+ /* lock to protect hwq */
+ spinlock_t lock;
+ struct bng_re_pbl pbl[BNG_PBL_LVL_MAX + 1];
+ /* Valid values: 0, 1, 2 */
+ enum bng_re_pbl_lvl level;
+ /* PBL entries */
+ void **pbl_ptr;
+ /* PBL dma_addr */
+ dma_addr_t *pbl_dma_ptr;
+ u32 max_elements;
+ u32 depth;
+ u16 element_size;
+ u32 prod;
+ u32 cons;
+ /* queue entry per page */
+ u16 qe_ppg;
+};
+
+struct bng_re_stats {
+ dma_addr_t dma_map;
+ void *dma;
+ u32 size;
+ u32 fw_id;
+};
+
+struct bng_re_res {
+ struct pci_dev *pdev;
+ struct bng_re_chip_ctx *cctx;
+ struct bng_re_dev_attr *dattr;
+};
+
+static inline void *bng_re_get_qe(struct bng_re_hwq *hwq,
+ u32 indx, u64 *pg)
+{
+ u32 pg_num, pg_idx;
+
+ pg_num = (indx / hwq->qe_ppg);
+ pg_idx = (indx % hwq->qe_ppg);
+ if (pg)
+ *pg = (u64)&hwq->pbl_ptr[pg_num];
+ return (void *)(hwq->pbl_ptr[pg_num] + hwq->element_size * pg_idx);
+}
+
+#define BNG_RE_INIT_DBHDR(xid, type, indx, toggle) \
+ (((u64)(((xid) & DBC_DBC_XID_MASK) | DBC_DBC_PATH_ROCE | \
+ (type) | BNG_RE_DBR_VALID) << 32) | (indx) | \
+ (((u32)(toggle)) << (BNG_RE_DBR_TOGGLE_SHIFT)))
+
+static inline void bng_re_ring_db(struct bng_re_db_info *info,
+ u32 type)
+{
+ u64 key = 0;
+ u32 indx;
+ u8 toggle = 0;
+
+ if (type == DBC_DBC_TYPE_CQ_ARMALL ||
+ type == DBC_DBC_TYPE_CQ_ARMSE)
+ toggle = info->toggle;
+
+ indx = (info->hwq->cons & DBC_DBC_INDEX_MASK) |
+ ((info->flags & BNG_RE_FLAG_EPOCH_CONS_MASK) <<
+ BNG_RE_DB_EPOCH_CONS_SHIFT);
+
+ key = BNG_RE_INIT_DBHDR(info->xid, type, indx, toggle);
+ writeq(key, info->db);
+}
+
+static inline void bng_re_ring_nq_db(struct bng_re_db_info *info,
+ struct bng_re_chip_ctx *cctx,
+ bool arm)
+{
+ u32 type;
+
+ type = arm ? DBC_DBC_TYPE_NQ_ARM : DBC_DBC_TYPE_NQ;
+ bng_re_ring_db(info, type);
+}
+
+static inline void bng_re_hwq_incr_cons(u32 max_elements, u32 *cons, u32 cnt,
+ u32 *dbinfo_flags)
+{
+ /* move cons and update toggle/epoch if wrap around */
+ *cons += cnt;
+ if (*cons >= max_elements) {
+ *cons %= max_elements;
+ *dbinfo_flags ^= 1UL << BNG_RE_FLAG_EPOCH_CONS_SHIFT;
+ }
+}
+
+static inline bool _is_max_srq_ext_supported(u16 dev_cap_ext_flags_2)
+{
+ return !!(dev_cap_ext_flags_2 & CREQ_QUERY_FUNC_RESP_SB_MAX_SRQ_EXTENDED);
+}
+
+void bng_re_free_hwq(struct bng_re_res *res,
+ struct bng_re_hwq *hwq);
+
+int bng_re_alloc_init_hwq(struct bng_re_hwq *hwq,
+ struct bng_re_hwq_attr *hwq_attr);
+
+void bng_re_free_stats_ctx_mem(struct pci_dev *pdev,
+ struct bng_re_stats *stats);
+
+int bng_re_alloc_stats_ctx_mem(struct pci_dev *pdev,
+ struct bng_re_chip_ctx *cctx,
+ struct bng_re_stats *stats);
+#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.c b/drivers/infiniband/hw/bng_re/bng_sp.c
new file mode 100644
index 000000000000..83099e05328d
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_sp.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+#include "bng_res.h"
+#include "bng_fw.h"
+#include "bng_sp.h"
+#include "bng_tlv.h"
+
+static bool bng_re_is_atomic_cap(struct bng_re_rcfw *rcfw)
+{
+ u16 pcie_ctl2 = 0;
+
+ pcie_capability_read_word(rcfw->pdev, PCI_EXP_DEVCTL2, &pcie_ctl2);
+ return (pcie_ctl2 & PCI_EXP_DEVCTL2_ATOMIC_REQ);
+}
+
+static void bng_re_query_version(struct bng_re_rcfw *rcfw,
+ char *fw_ver)
+{
+ struct creq_query_version_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct cmdq_query_version req = {};
+ int rc;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_QUERY_VERSION,
+ sizeof(req));
+
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, NULL, sizeof(req), sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ return;
+ fw_ver[0] = resp.fw_maj;
+ fw_ver[1] = resp.fw_minor;
+ fw_ver[2] = resp.fw_bld;
+ fw_ver[3] = resp.fw_rsvd;
+}
+
+int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw)
+{
+ struct bng_re_dev_attr *attr = rcfw->res->dattr;
+ struct creq_query_func_resp resp = {};
+ struct bng_re_cmdqmsg msg = {};
+ struct creq_query_func_resp_sb *sb;
+ struct bng_re_rcfw_sbuf sbuf;
+ struct cmdq_query_func req = {};
+ u8 *tqm_alloc;
+ int i, rc;
+ u32 temp;
+
+ bng_re_rcfw_cmd_prep((struct cmdq_base *)&req,
+ CMDQ_BASE_OPCODE_QUERY_FUNC,
+ sizeof(req));
+
+ sbuf.size = ALIGN(sizeof(*sb), BNG_FW_CMDQE_UNITS);
+ sbuf.sb = dma_alloc_coherent(&rcfw->pdev->dev, sbuf.size,
+ &sbuf.dma_addr, GFP_KERNEL);
+ if (!sbuf.sb)
+ return -ENOMEM;
+ sb = sbuf.sb;
+ req.resp_size = sbuf.size / BNG_FW_CMDQE_UNITS;
+ bng_re_fill_cmdqmsg(&msg, &req, &resp, &sbuf, sizeof(req),
+ sizeof(resp), 0);
+ rc = bng_re_rcfw_send_message(rcfw, &msg);
+ if (rc)
+ goto bail;
+ /* Extract the context from the side buffer */
+ attr->max_qp = le32_to_cpu(sb->max_qp);
+ /* max_qp value reported by FW doesn't include the QP1 */
+ attr->max_qp += 1;
+ attr->max_qp_rd_atom =
+ sb->max_qp_rd_atom > BNG_RE_MAX_OUT_RD_ATOM ?
+ BNG_RE_MAX_OUT_RD_ATOM : sb->max_qp_rd_atom;
+ attr->max_qp_init_rd_atom =
+ sb->max_qp_init_rd_atom > BNG_RE_MAX_OUT_RD_ATOM ?
+ BNG_RE_MAX_OUT_RD_ATOM : sb->max_qp_init_rd_atom;
+ attr->max_qp_wqes = le16_to_cpu(sb->max_qp_wr) - 1;
+
+ /* Adjust for max_qp_wqes for variable wqe */
+ attr->max_qp_wqes = min_t(u32, attr->max_qp_wqes, BNG_VAR_MAX_WQE - 1);
+
+ attr->max_qp_sges = min_t(u32, sb->max_sge_var_wqe, BNG_VAR_MAX_SGE);
+ attr->max_cq = le32_to_cpu(sb->max_cq);
+ attr->max_cq_wqes = le32_to_cpu(sb->max_cqe);
+ attr->max_cq_sges = attr->max_qp_sges;
+ attr->max_mr = le32_to_cpu(sb->max_mr);
+ attr->max_mw = le32_to_cpu(sb->max_mw);
+
+ attr->max_mr_size = le64_to_cpu(sb->max_mr_size);
+ attr->max_pd = 64 * 1024;
+ attr->max_raw_ethy_qp = le32_to_cpu(sb->max_raw_eth_qp);
+ attr->max_ah = le32_to_cpu(sb->max_ah);
+
+ attr->max_srq = le16_to_cpu(sb->max_srq);
+ attr->max_srq_wqes = le32_to_cpu(sb->max_srq_wr) - 1;
+ attr->max_srq_sges = sb->max_srq_sge;
+ attr->max_pkey = 1;
+ attr->max_inline_data = le32_to_cpu(sb->max_inline_data);
+ /*
+ * Read the max gid supported by HW.
+ * For each entry in HW GID in HW table, we consume 2
+ * GID entries in the kernel GID table. So max_gid reported
+ * to stack can be up to twice the value reported by the HW, up to 256 gids.
+ */
+ attr->max_sgid = le32_to_cpu(sb->max_gid);
+ attr->max_sgid = min_t(u32, BNG_RE_NUM_GIDS_SUPPORTED, 2 * attr->max_sgid);
+ attr->dev_cap_flags = le16_to_cpu(sb->dev_cap_flags);
+ attr->dev_cap_flags2 = le16_to_cpu(sb->dev_cap_ext_flags_2);
+
+ if (_is_max_srq_ext_supported(attr->dev_cap_flags2))
+ attr->max_srq += le16_to_cpu(sb->max_srq_ext);
+
+ bng_re_query_version(rcfw, attr->fw_ver);
+ for (i = 0; i < BNG_MAX_TQM_ALLOC_REQ / 4; i++) {
+ temp = le32_to_cpu(sb->tqm_alloc_reqs[i]);
+ tqm_alloc = (u8 *)&temp;
+ attr->tqm_alloc_reqs[i * 4] = *tqm_alloc;
+ attr->tqm_alloc_reqs[i * 4 + 1] = *(++tqm_alloc);
+ attr->tqm_alloc_reqs[i * 4 + 2] = *(++tqm_alloc);
+ attr->tqm_alloc_reqs[i * 4 + 3] = *(++tqm_alloc);
+ }
+
+ attr->max_dpi = le32_to_cpu(sb->max_dpi);
+ attr->is_atomic = bng_re_is_atomic_cap(rcfw);
+bail:
+ dma_free_coherent(&rcfw->pdev->dev, sbuf.size,
+ sbuf.sb, sbuf.dma_addr);
+ return rc;
+}
diff --git a/drivers/infiniband/hw/bng_re/bng_sp.h b/drivers/infiniband/hw/bng_re/bng_sp.h
new file mode 100644
index 000000000000..e15190515ed1
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_sp.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2025 Broadcom.
+
+#ifndef __BNG_SP_H__
+#define __BNG_SP_H__
+
+#include "bng_fw.h"
+
+#define BNG_VAR_MAX_WQE 4352
+#define BNG_VAR_MAX_SGE 13
+
+struct bng_re_dev_attr {
+#define FW_VER_ARR_LEN 4
+ u8 fw_ver[FW_VER_ARR_LEN];
+#define BNG_RE_NUM_GIDS_SUPPORTED 256
+ u16 max_sgid;
+ u16 max_mrw;
+ u32 max_qp;
+#define BNG_RE_MAX_OUT_RD_ATOM 126
+ u32 max_qp_rd_atom;
+ u32 max_qp_init_rd_atom;
+ u32 max_qp_wqes;
+ u32 max_qp_sges;
+ u32 max_cq;
+ u32 max_cq_wqes;
+ u32 max_cq_sges;
+ u32 max_mr;
+ u64 max_mr_size;
+ u32 max_pd;
+ u32 max_mw;
+ u32 max_raw_ethy_qp;
+ u32 max_ah;
+ u32 max_srq;
+ u32 max_srq_wqes;
+ u32 max_srq_sges;
+ u32 max_pkey;
+ u32 max_inline_data;
+ u32 l2_db_size;
+ u8 tqm_alloc_reqs[BNG_MAX_TQM_ALLOC_REQ];
+ bool is_atomic;
+ u16 dev_cap_flags;
+ u16 dev_cap_flags2;
+ u32 max_dpi;
+};
+
+int bng_re_get_dev_attr(struct bng_re_rcfw *rcfw);
+#endif
diff --git a/drivers/infiniband/hw/bng_re/bng_tlv.h b/drivers/infiniband/hw/bng_re/bng_tlv.h
new file mode 100644
index 000000000000..278f4922962d
--- /dev/null
+++ b/drivers/infiniband/hw/bng_re/bng_tlv.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+
+#ifndef __BNG_TLV_H__
+#define __BNG_TLV_H__
+
+#include "roce_hsi.h"
+
+struct roce_tlv {
+ struct tlv tlv;
+ u8 total_size; // in units of 16 byte chunks
+ u8 unused[7]; // for 16 byte alignment
+};
+
+/*
+ * TLV size in units of 16 byte chunks
+ */
+#define TLV_SIZE ((sizeof(struct roce_tlv) + 15) / 16)
+/*
+ * TLV length in bytes
+ */
+#define TLV_BYTES (TLV_SIZE * 16)
+
+#define HAS_TLV_HEADER(msg) (le16_to_cpu(((struct tlv *)(msg))->cmd_discr) == CMD_DISCR_TLV_ENCAP)
+#define GET_TLV_DATA(tlv) ((void *)&((uint8_t *)(tlv))[TLV_BYTES])
+
+static inline u8 __get_cmdq_base_opcode(struct cmdq_base *req, u32 size)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ return ((struct cmdq_base *)GET_TLV_DATA(req))->opcode;
+ else
+ return req->opcode;
+}
+
+static inline void __set_cmdq_base_opcode(struct cmdq_base *req,
+ u32 size, u8 val)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ ((struct cmdq_base *)GET_TLV_DATA(req))->opcode = val;
+ else
+ req->opcode = val;
+}
+
+static inline __le16 __get_cmdq_base_cookie(struct cmdq_base *req, u32 size)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ return ((struct cmdq_base *)GET_TLV_DATA(req))->cookie;
+ else
+ return req->cookie;
+}
+
+static inline void __set_cmdq_base_cookie(struct cmdq_base *req,
+ u32 size, __le16 val)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ ((struct cmdq_base *)GET_TLV_DATA(req))->cookie = val;
+ else
+ req->cookie = val;
+}
+
+static inline __le64 __get_cmdq_base_resp_addr(struct cmdq_base *req, u32 size)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ return ((struct cmdq_base *)GET_TLV_DATA(req))->resp_addr;
+ else
+ return req->resp_addr;
+}
+
+static inline void __set_cmdq_base_resp_addr(struct cmdq_base *req,
+ u32 size, __le64 val)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ ((struct cmdq_base *)GET_TLV_DATA(req))->resp_addr = val;
+ else
+ req->resp_addr = val;
+}
+
+static inline u8 __get_cmdq_base_resp_size(struct cmdq_base *req, u32 size)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ return ((struct cmdq_base *)GET_TLV_DATA(req))->resp_size;
+ else
+ return req->resp_size;
+}
+
+static inline void __set_cmdq_base_resp_size(struct cmdq_base *req,
+ u32 size, u8 val)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ ((struct cmdq_base *)GET_TLV_DATA(req))->resp_size = val;
+ else
+ req->resp_size = val;
+}
+
+static inline u8 __get_cmdq_base_cmd_size(struct cmdq_base *req, u32 size)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ return ((struct roce_tlv *)(req))->total_size;
+ else
+ return req->cmd_size;
+}
+
+static inline void __set_cmdq_base_cmd_size(struct cmdq_base *req,
+ u32 size, u8 val)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ ((struct cmdq_base *)GET_TLV_DATA(req))->cmd_size = val;
+ else
+ req->cmd_size = val;
+}
+
+static inline __le16 __get_cmdq_base_flags(struct cmdq_base *req, u32 size)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ return ((struct cmdq_base *)GET_TLV_DATA(req))->flags;
+ else
+ return req->flags;
+}
+
+static inline void __set_cmdq_base_flags(struct cmdq_base *req,
+ u32 size, __le16 val)
+{
+ if (HAS_TLV_HEADER(req) && size > TLV_BYTES)
+ ((struct cmdq_base *)GET_TLV_DATA(req))->flags = val;
+ else
+ req->flags = val;
+}
+
+#endif /* __BNG_TLV_H__ */
diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
index 3485e495ac6a..3a7ce4729fcf 100644
--- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h
+++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h
@@ -224,6 +224,8 @@ struct bnxt_re_dev {
struct workqueue_struct *dcb_wq;
struct dentry *cc_config;
struct bnxt_re_dbg_cc_config_params *cc_config_params;
+ struct dentry *cq_coal_cfg;
+ struct bnxt_re_dbg_cq_coal_params *cq_coal_cfg_params;
#define BNXT_VPD_FLD_LEN 32
char board_partno[BNXT_VPD_FLD_LEN];
/* RoCE mirror */
diff --git a/drivers/infiniband/hw/bnxt_re/debugfs.c b/drivers/infiniband/hw/bnxt_re/debugfs.c
index be5e9b5ca2f0..88817c86ae24 100644
--- a/drivers/infiniband/hw/bnxt_re/debugfs.c
+++ b/drivers/infiniband/hw/bnxt_re/debugfs.c
@@ -23,6 +23,14 @@
static struct dentry *bnxt_re_debugfs_root;
+static const char * const bnxt_re_cq_coal_str[] = {
+ "buf_maxtime",
+ "normal_maxbuf",
+ "during_maxbuf",
+ "en_ring_idle_mode",
+ "enable",
+};
+
static const char * const bnxt_re_cc_gen0_name[] = {
"enable_cc",
"run_avg_weight_g",
@@ -349,6 +357,123 @@ static void bnxt_re_debugfs_add_info(struct bnxt_re_dev *rdev)
debugfs_create_file("info", 0400, rdev->dbg_root, rdev, &info_fops);
}
+static ssize_t cq_coal_cfg_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct seq_file *s = file->private_data;
+ struct bnxt_re_cq_coal_param *param = s->private;
+ struct bnxt_re_dev *rdev = param->rdev;
+ int offset = param->offset;
+ char lbuf[16] = { };
+ u32 val;
+
+ if (count > sizeof(lbuf))
+ return -EINVAL;
+
+ if (copy_from_user(lbuf, buf, count))
+ return -EFAULT;
+
+ lbuf[sizeof(lbuf) - 1] = '\0';
+
+ if (kstrtou32(lbuf, 0, &val))
+ return -EINVAL;
+
+ switch (offset) {
+ case BNXT_RE_COAL_CQ_BUF_MAXTIME:
+ if (val < 1 || val > BNXT_QPLIB_CQ_COAL_MAX_BUF_MAXTIME)
+ return -EINVAL;
+ rdev->cq_coalescing.buf_maxtime = val;
+ break;
+ case BNXT_RE_COAL_CQ_NORMAL_MAXBUF:
+ if (val < 1 || val > BNXT_QPLIB_CQ_COAL_MAX_NORMAL_MAXBUF)
+ return -EINVAL;
+ rdev->cq_coalescing.normal_maxbuf = val;
+ break;
+ case BNXT_RE_COAL_CQ_DURING_MAXBUF:
+ if (val < 1 || val > BNXT_QPLIB_CQ_COAL_MAX_DURING_MAXBUF)
+ return -EINVAL;
+ rdev->cq_coalescing.during_maxbuf = val;
+ break;
+ case BNXT_RE_COAL_CQ_EN_RING_IDLE_MODE:
+ if (val > BNXT_QPLIB_CQ_COAL_MAX_EN_RING_IDLE_MODE)
+ return -EINVAL;
+ rdev->cq_coalescing.en_ring_idle_mode = val;
+ break;
+ case BNXT_RE_COAL_CQ_ENABLE:
+ if (val > 1)
+ return -EINVAL;
+ rdev->cq_coalescing.enable = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return count;
+}
+
+static int cq_coal_cfg_show(struct seq_file *s, void *unused)
+{
+ struct bnxt_re_cq_coal_param *param = s->private;
+ struct bnxt_re_dev *rdev = param->rdev;
+ int offset = param->offset;
+ u32 val = 0;
+
+ switch (offset) {
+ case BNXT_RE_COAL_CQ_BUF_MAXTIME:
+ val = rdev->cq_coalescing.buf_maxtime;
+ break;
+ case BNXT_RE_COAL_CQ_NORMAL_MAXBUF:
+ val = rdev->cq_coalescing.normal_maxbuf;
+ break;
+ case BNXT_RE_COAL_CQ_DURING_MAXBUF:
+ val = rdev->cq_coalescing.during_maxbuf;
+ break;
+ case BNXT_RE_COAL_CQ_EN_RING_IDLE_MODE:
+ val = rdev->cq_coalescing.en_ring_idle_mode;
+ break;
+ case BNXT_RE_COAL_CQ_ENABLE:
+ val = rdev->cq_coalescing.enable;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ seq_printf(s, "%u\n", val);
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(cq_coal_cfg);
+
+static void bnxt_re_cleanup_cq_coal_debugfs(struct bnxt_re_dev *rdev)
+{
+ debugfs_remove_recursive(rdev->cq_coal_cfg);
+ kfree(rdev->cq_coal_cfg_params);
+}
+
+static void bnxt_re_init_cq_coal_debugfs(struct bnxt_re_dev *rdev)
+{
+ struct bnxt_re_dbg_cq_coal_params *dbg_cq_coal_params;
+ int i;
+
+ if (!_is_cq_coalescing_supported(rdev->dev_attr->dev_cap_flags2))
+ return;
+
+ dbg_cq_coal_params = kzalloc(sizeof(*dbg_cq_coal_params), GFP_KERNEL);
+ if (!dbg_cq_coal_params)
+ return;
+
+ rdev->cq_coal_cfg = debugfs_create_dir("cq_coal_cfg", rdev->dbg_root);
+ rdev->cq_coal_cfg_params = dbg_cq_coal_params;
+
+ for (i = 0; i < BNXT_RE_COAL_CQ_MAX; i++) {
+ dbg_cq_coal_params->params[i].offset = i;
+ dbg_cq_coal_params->params[i].rdev = rdev;
+ debugfs_create_file(bnxt_re_cq_coal_str[i],
+ 0600, rdev->cq_coal_cfg,
+ &dbg_cq_coal_params->params[i],
+ &cq_coal_cfg_fops);
+ }
+}
+
void bnxt_re_debugfs_add_pdev(struct bnxt_re_dev *rdev)
{
struct pci_dev *pdev = rdev->en_dev->pdev;
@@ -374,10 +499,13 @@ void bnxt_re_debugfs_add_pdev(struct bnxt_re_dev *rdev)
rdev->cc_config, tmp_params,
&bnxt_re_cc_config_ops);
}
+
+ bnxt_re_init_cq_coal_debugfs(rdev);
}
void bnxt_re_debugfs_rem_pdev(struct bnxt_re_dev *rdev)
{
+ bnxt_re_cleanup_cq_coal_debugfs(rdev);
debugfs_remove_recursive(rdev->qp_debugfs);
debugfs_remove_recursive(rdev->cc_config);
kfree(rdev->cc_config_params);
diff --git a/drivers/infiniband/hw/bnxt_re/debugfs.h b/drivers/infiniband/hw/bnxt_re/debugfs.h
index 8f101df4e838..98f4620ef245 100644
--- a/drivers/infiniband/hw/bnxt_re/debugfs.h
+++ b/drivers/infiniband/hw/bnxt_re/debugfs.h
@@ -33,4 +33,23 @@ struct bnxt_re_cc_param {
struct bnxt_re_dbg_cc_config_params {
struct bnxt_re_cc_param gen0_parms[BNXT_RE_CC_PARAM_GEN0];
};
+
+struct bnxt_re_cq_coal_param {
+ struct bnxt_re_dev *rdev;
+ u32 offset;
+};
+
+enum bnxt_re_cq_coal_types {
+ BNXT_RE_COAL_CQ_BUF_MAXTIME,
+ BNXT_RE_COAL_CQ_NORMAL_MAXBUF,
+ BNXT_RE_COAL_CQ_DURING_MAXBUF,
+ BNXT_RE_COAL_CQ_EN_RING_IDLE_MODE,
+ BNXT_RE_COAL_CQ_ENABLE,
+ BNXT_RE_COAL_CQ_MAX
+
+};
+
+struct bnxt_re_dbg_cq_coal_params {
+ struct bnxt_re_cq_coal_param params[BNXT_RE_COAL_CQ_MAX];
+};
#endif
diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
index 4dab5ca7362b..f19b55c13d58 100644
--- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c
+++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c
@@ -601,7 +601,8 @@ static int bnxt_re_create_fence_mr(struct bnxt_re_pd *pd)
mr->qplib_mr.va = (u64)(unsigned long)fence->va;
mr->qplib_mr.total_size = BNXT_RE_FENCE_BYTES;
rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &mr->qplib_mr, NULL,
- BNXT_RE_FENCE_PBL_SIZE, PAGE_SIZE);
+ BNXT_RE_FENCE_PBL_SIZE, PAGE_SIZE,
+ _is_alloc_mr_unified(rdev->dev_attr->dev_cap_flags));
if (rc) {
ibdev_err(&rdev->ibdev, "Failed to register fence-MR\n");
goto fail;
@@ -913,7 +914,7 @@ void bnxt_re_unlock_cqs(struct bnxt_re_qp *qp,
spin_unlock_irqrestore(&qp->scq->cq_lock, flags);
}
-static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp)
+static void bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp)
{
struct bnxt_re_qp *gsi_sqp;
struct bnxt_re_ah *gsi_sah;
@@ -933,10 +934,9 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp)
ibdev_dbg(&rdev->ibdev, "Destroy the shadow QP\n");
rc = bnxt_qplib_destroy_qp(&rdev->qplib_res, &gsi_sqp->qplib_qp);
- if (rc) {
+ if (rc)
ibdev_err(&rdev->ibdev, "Destroy Shadow QP failed");
- goto fail;
- }
+
bnxt_qplib_free_qp_res(&rdev->qplib_res, &gsi_sqp->qplib_qp);
/* remove from active qp list */
@@ -951,10 +951,6 @@ static int bnxt_re_destroy_gsi_sqp(struct bnxt_re_qp *qp)
rdev->gsi_ctx.gsi_sqp = NULL;
rdev->gsi_ctx.gsi_sah = NULL;
rdev->gsi_ctx.sqp_tbl = NULL;
-
- return 0;
-fail:
- return rc;
}
static void bnxt_re_del_unique_gid(struct bnxt_re_dev *rdev)
@@ -4032,7 +4028,7 @@ struct ib_mr *bnxt_re_get_dma_mr(struct ib_pd *ib_pd, int mr_access_flags)
mr->qplib_mr.hwq.level = PBL_LVL_MAX;
mr->qplib_mr.total_size = -1; /* Infinte length */
rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &mr->qplib_mr, NULL, 0,
- PAGE_SIZE);
+ PAGE_SIZE, false);
if (rc)
goto fail_mr;
@@ -4262,7 +4258,8 @@ static struct ib_mr *__bnxt_re_user_reg_mr(struct ib_pd *ib_pd, u64 length, u64
umem_pgs = ib_umem_num_dma_blocks(umem, page_size);
rc = bnxt_qplib_reg_mr(&rdev->qplib_res, &mr->qplib_mr, umem,
- umem_pgs, page_size);
+ umem_pgs, page_size,
+ _is_alloc_mr_unified(rdev->dev_attr->dev_cap_flags));
if (rc) {
ibdev_err(&rdev->ibdev, "Failed to register user MR - rc = %d\n", rc);
rc = -EIO;
diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c
index b13810572c2e..73003ad25ee8 100644
--- a/drivers/infiniband/hw/bnxt_re/main.c
+++ b/drivers/infiniband/hw/bnxt_re/main.c
@@ -1453,6 +1453,7 @@ static struct bnxt_re_dev *bnxt_re_dev_add(struct auxiliary_device *adev,
atomic_set(&rdev->stats.res.pd_count, 0);
rdev->cosq[0] = 0xFFFF;
rdev->cosq[1] = 0xFFFF;
+ rdev->cq_coalescing.enable = 1;
rdev->cq_coalescing.buf_maxtime = BNXT_QPLIB_CQ_COAL_DEF_BUF_MAXTIME;
if (bnxt_re_chip_gen_p7(en_dev->chip_num)) {
rdev->cq_coalescing.normal_maxbuf = BNXT_QPLIB_CQ_COAL_DEF_NORMAL_MAXBUF_P7;
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
index ce90d3d834d4..c88f049136fc 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c
@@ -2226,7 +2226,8 @@ int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq)
req.cq_handle = cpu_to_le64(cq->cq_handle);
req.cq_size = cpu_to_le32(cq->max_wqe);
- if (_is_cq_coalescing_supported(res->dattr->dev_cap_flags2)) {
+ if (_is_cq_coalescing_supported(res->dattr->dev_cap_flags2) &&
+ cq->coalescing->enable) {
req.flags |= cpu_to_le16(CMDQ_CREATE_CQ_FLAGS_COALESCING_VALID);
coalescing |= ((cq->coalescing->buf_maxtime <<
CMDQ_CREATE_CQ_BUF_MAXTIME_SFT) &
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
index b990d0c0ce1a..1b414a73b46d 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h
+++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h
@@ -395,6 +395,7 @@ struct bnxt_qplib_cq_coal_param {
u8 normal_maxbuf;
u8 during_maxbuf;
u8 en_ring_idle_mode;
+ u8 enable;
};
#define BNXT_QPLIB_CQ_COAL_DEF_BUF_MAXTIME 0x1
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
index 9ef581ed785c..408a34df2667 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c
+++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c
@@ -162,7 +162,7 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw)
attr->max_srq_wqes = le32_to_cpu(sb->max_srq_wr) - 1;
attr->max_srq_sges = sb->max_srq_sge;
attr->max_pkey = 1;
- attr->max_inline_data = le32_to_cpu(sb->max_inline_data);
+ attr->max_inline_data = attr->max_qp_sges * sizeof(struct sq_sge);
if (!bnxt_qplib_is_chip_gen_p7(rcfw->res->cctx))
attr->l2_db_size = (sb->l2_db_space_size + 1) *
(0x01 << RCFW_DBR_BASE_PAGE_SHIFT);
@@ -578,7 +578,7 @@ int bnxt_qplib_dereg_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw,
}
int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr,
- struct ib_umem *umem, int num_pbls, u32 buf_pg_size)
+ struct ib_umem *umem, int num_pbls, u32 buf_pg_size, bool unified_mr)
{
struct bnxt_qplib_rcfw *rcfw = res->rcfw;
struct bnxt_qplib_hwq_attr hwq_attr = {};
@@ -640,7 +640,7 @@ int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr,
req.access = (mr->access_flags & BNXT_QPLIB_MR_ACCESS_MASK);
req.va = cpu_to_le64(mr->va);
req.key = cpu_to_le32(mr->lkey);
- if (_is_alloc_mr_unified(res->dattr->dev_cap_flags))
+ if (unified_mr)
req.key = cpu_to_le32(mr->pd->id);
req.flags = cpu_to_le16(mr->flags);
req.mr_size = cpu_to_le64(mr->total_size);
@@ -651,7 +651,7 @@ int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr,
if (rc)
goto fail;
- if (_is_alloc_mr_unified(res->dattr->dev_cap_flags)) {
+ if (unified_mr) {
mr->lkey = le32_to_cpu(resp.xid);
mr->rkey = mr->lkey;
}
diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.h b/drivers/infiniband/hw/bnxt_re/qplib_sp.h
index 147b5d9c0313..5a45c55c6464 100644
--- a/drivers/infiniband/hw/bnxt_re/qplib_sp.h
+++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.h
@@ -341,7 +341,7 @@ int bnxt_qplib_alloc_mrw(struct bnxt_qplib_res *res,
int bnxt_qplib_dereg_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw,
bool block);
int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr,
- struct ib_umem *umem, int num_pbls, u32 buf_pg_size);
+ struct ib_umem *umem, int num_pbls, u32 buf_pg_size, bool unified_mr);
int bnxt_qplib_free_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr);
int bnxt_qplib_alloc_fast_reg_mr(struct bnxt_qplib_res *res,
struct bnxt_qplib_mrw *mr, int max);
diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c
index dcdfe250bdbe..adeed7447e7b 100644
--- a/drivers/infiniband/hw/cxgb4/mem.c
+++ b/drivers/infiniband/hw/cxgb4/mem.c
@@ -348,7 +348,7 @@ static int write_pbl(struct c4iw_rdev *rdev, __be64 *pbl,
{
int err;
- pr_debug("*pdb_addr 0x%x, pbl_base 0x%x, pbl_size %d\n",
+ pr_debug("*pbl_addr 0x%x, pbl_base 0x%x, pbl_size %d\n",
pbl_addr, rdev->lldi.vr->pbl.start,
pbl_size);
diff --git a/drivers/infiniband/hw/efa/efa_verbs.c b/drivers/infiniband/hw/efa/efa_verbs.c
index d9a12681f843..22d3e25c3b9d 100644
--- a/drivers/infiniband/hw/efa/efa_verbs.c
+++ b/drivers/infiniband/hw/efa/efa_verbs.c
@@ -1216,13 +1216,13 @@ int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
if (umem->length < cq->size) {
ibdev_dbg(&dev->ibdev, "External memory too small\n");
err = -EINVAL;
- goto err_free_mem;
+ goto err_out;
}
if (!ib_umem_is_contiguous(umem)) {
ibdev_dbg(&dev->ibdev, "Non contiguous CQ unsupported\n");
err = -EINVAL;
- goto err_free_mem;
+ goto err_out;
}
cq->cpu_addr = NULL;
@@ -1251,7 +1251,7 @@ int efa_create_cq_umem(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
err = efa_com_create_cq(&dev->edev, &params, &result);
if (err)
- goto err_free_mem;
+ goto err_free_mapped;
resp.db_off = result.db_off;
resp.cq_idx = result.cq_idx;
@@ -1299,12 +1299,10 @@ err_remove_mmap:
efa_cq_user_mmap_entries_remove(cq);
err_destroy_cq:
efa_destroy_cq_idx(dev, cq->cq_idx);
-err_free_mem:
- if (umem)
- ib_umem_release(umem);
- else
- efa_free_mapped(dev, cq->cpu_addr, cq->dma_addr, cq->size, DMA_FROM_DEVICE);
-
+err_free_mapped:
+ if (!umem)
+ efa_free_mapped(dev, cq->cpu_addr, cq->dma_addr, cq->size,
+ DMA_FROM_DEVICE);
err_out:
atomic64_inc(&dev->stats.create_cq_err);
return err;
diff --git a/drivers/infiniband/hw/erdma/erdma_cm.c b/drivers/infiniband/hw/erdma/erdma_cm.c
index e0acc185e719..ed21ba0037a4 100644
--- a/drivers/infiniband/hw/erdma/erdma_cm.c
+++ b/drivers/infiniband/hw/erdma/erdma_cm.c
@@ -993,10 +993,10 @@ static int kernel_bindconnect(struct socket *s, struct sockaddr *laddr,
int ret;
sock_set_reuseaddr(s->sk);
- ret = s->ops->bind(s, laddr, laddrlen);
+ ret = s->ops->bind(s, (struct sockaddr_unsized *)laddr, laddrlen);
if (ret)
return ret;
- ret = s->ops->connect(s, raddr, raddrlen, flags);
+ ret = s->ops->connect(s, (struct sockaddr_unsized *)raddr, raddrlen, flags);
return ret < 0 ? ret : 0;
}
@@ -1315,7 +1315,7 @@ int erdma_create_listen(struct iw_cm_id *id, int backlog)
if (ipv4_is_zeronet(laddr->sin_addr.s_addr))
s->sk->sk_bound_dev_if = dev->netdev->ifindex;
- ret = s->ops->bind(s, (struct sockaddr *)laddr,
+ ret = s->ops->bind(s, (struct sockaddr_unsized *)laddr,
sizeof(struct sockaddr_in));
if (ret)
goto error;
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index b35f92e7d865..e4aef102dac0 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -745,8 +745,8 @@ static int create_workqueues(struct hfi1_devdata *dd)
ppd->hfi1_wq =
alloc_workqueue(
"hfi%d_%d",
- WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE |
- WQ_MEM_RECLAIM,
+ WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM |
+ WQ_PERCPU,
HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES,
dd->unit, pidx);
if (!ppd->hfi1_wq)
diff --git a/drivers/infiniband/hw/hfi1/opfn.c b/drivers/infiniband/hw/hfi1/opfn.c
index 370a5a8eaa71..6e0e3458d202 100644
--- a/drivers/infiniband/hw/hfi1/opfn.c
+++ b/drivers/infiniband/hw/hfi1/opfn.c
@@ -305,8 +305,8 @@ void opfn_trigger_conn_request(struct rvt_qp *qp, u32 bth1)
int opfn_init(void)
{
opfn_wq = alloc_workqueue("hfi_opfn",
- WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE |
- WQ_MEM_RECLAIM,
+ WQ_SYSFS | WQ_HIGHPRI | WQ_CPU_INTENSIVE | WQ_MEM_RECLAIM |
+ WQ_PERCPU,
HFI1_MAX_ACTIVE_WORKQUEUE_ENTRIES);
if (!opfn_wq)
return -ENOMEM;
diff --git a/drivers/infiniband/hw/hns/Makefile b/drivers/infiniband/hw/hns/Makefile
index baf592e6f21b..d07ef02c5231 100644
--- a/drivers/infiniband/hw/hns/Makefile
+++ b/drivers/infiniband/hw/hns/Makefile
@@ -4,11 +4,13 @@
#
ccflags-y := -I $(srctree)/drivers/net/ethernet/hisilicon/hns3
+ccflags-y += -I $(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3pf
+ccflags-y += -I $(srctree)/drivers/net/ethernet/hisilicon/hns3/hns3_common
ccflags-y += -I $(src)
hns-roce-hw-v2-objs := hns_roce_main.o hns_roce_cmd.o hns_roce_pd.o \
hns_roce_ah.o hns_roce_hem.o hns_roce_mr.o hns_roce_qp.o \
hns_roce_cq.o hns_roce_alloc.o hns_roce_db.o hns_roce_srq.o hns_roce_restrack.o \
- hns_roce_debugfs.o hns_roce_hw_v2.o
+ hns_roce_debugfs.o hns_roce_hw_v2.o hns_roce_bond.o
obj-$(CONFIG_INFINIBAND_HNS_HIP08) += hns-roce-hw-v2.o
diff --git a/drivers/infiniband/hw/hns/hns_roce_ah.c b/drivers/infiniband/hw/hns/hns_roce_ah.c
index 307c35888b30..0c1c32d23c88 100644
--- a/drivers/infiniband/hw/hns/hns_roce_ah.c
+++ b/drivers/infiniband/hw/hns/hns_roce_ah.c
@@ -30,7 +30,6 @@
* SOFTWARE.
*/
-#include <linux/pci.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_cache.h>
#include "hns_roce_device.h"
diff --git a/drivers/infiniband/hw/hns/hns_roce_bond.c b/drivers/infiniband/hw/hns/hns_roce_bond.c
new file mode 100644
index 000000000000..cc85f3ce1f3e
--- /dev/null
+++ b/drivers/infiniband/hw/hns/hns_roce_bond.c
@@ -0,0 +1,1012 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025 Hisilicon Limited.
+ */
+
+#include <net/lag.h>
+#include <net/bonding.h>
+#include "hns_roce_device.h"
+#include "hns_roce_hw_v2.h"
+#include "hns_roce_bond.h"
+
+static DEFINE_XARRAY(roce_bond_xa);
+
+static struct hns_roce_dev *hns_roce_get_hrdev_by_netdev(struct net_device *net_dev)
+{
+ struct ib_device *ibdev =
+ ib_device_get_by_netdev(net_dev, RDMA_DRIVER_HNS);
+
+ if (!ibdev)
+ return NULL;
+
+ return container_of(ibdev, struct hns_roce_dev, ib_dev);
+}
+
+static struct net_device *get_upper_dev_from_ndev(struct net_device *net_dev)
+{
+ struct net_device *upper_dev;
+
+ rcu_read_lock();
+ upper_dev = netdev_master_upper_dev_get_rcu(net_dev);
+ dev_hold(upper_dev);
+ rcu_read_unlock();
+
+ return upper_dev;
+}
+
+static int get_netdev_bond_slave_id(struct net_device *net_dev,
+ struct hns_roce_bond_group *bond_grp)
+{
+ int i;
+
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++)
+ if (net_dev == bond_grp->bond_func_info[i].net_dev)
+ return i;
+
+ return -ENOENT;
+}
+
+struct hns_roce_bond_group *hns_roce_get_bond_grp(struct net_device *net_dev,
+ u8 bus_num)
+{
+ struct hns_roce_die_info *die_info = xa_load(&roce_bond_xa, bus_num);
+ struct hns_roce_bond_group *bond_grp;
+ struct net_device *upper_dev = NULL;
+ int i;
+
+ if (!die_info)
+ return NULL;
+
+ for (i = 0; i < ROCE_BOND_NUM_MAX; i++) {
+ bond_grp = die_info->bgrps[i];
+ if (!bond_grp)
+ continue;
+ if (get_netdev_bond_slave_id(net_dev, bond_grp) >= 0)
+ return bond_grp;
+ if (bond_grp->upper_dev) {
+ upper_dev = get_upper_dev_from_ndev(net_dev);
+ if (bond_grp->upper_dev == upper_dev) {
+ dev_put(upper_dev);
+ return bond_grp;
+ }
+ dev_put(upper_dev);
+ }
+ }
+
+ return NULL;
+}
+
+static int hns_roce_set_bond_netdev(struct hns_roce_bond_group *bond_grp,
+ struct hns_roce_dev *hr_dev)
+{
+ struct net_device *active_dev;
+ struct net_device *old_dev;
+ int i, ret = 0;
+
+ if (bond_grp->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) {
+ rcu_read_lock();
+ active_dev =
+ bond_option_active_slave_get_rcu(netdev_priv(bond_grp->upper_dev));
+ rcu_read_unlock();
+ } else {
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++) {
+ active_dev = bond_grp->bond_func_info[i].net_dev;
+ if (active_dev &&
+ ib_get_curr_port_state(active_dev) == IB_PORT_ACTIVE)
+ break;
+ }
+ }
+
+ if (!active_dev || i == ROCE_BOND_FUNC_MAX)
+ active_dev = get_hr_netdev(hr_dev, 0);
+
+ old_dev = ib_device_get_netdev(&hr_dev->ib_dev, 1);
+ if (old_dev == active_dev)
+ goto out;
+
+ ret = ib_device_set_netdev(&hr_dev->ib_dev, active_dev, 1);
+ if (ret) {
+ dev_err(hr_dev->dev, "failed to set netdev for bond.\n");
+ goto out;
+ }
+
+ if (bond_grp->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) {
+ if (old_dev)
+ roce_del_all_netdev_gids(&hr_dev->ib_dev, 1, old_dev);
+ rdma_roce_rescan_port(&hr_dev->ib_dev, 1);
+ }
+out:
+ dev_put(old_dev);
+ return ret;
+}
+
+bool hns_roce_bond_is_active(struct hns_roce_dev *hr_dev)
+{
+ struct net_device *net_dev = get_hr_netdev(hr_dev, 0);
+ struct hns_roce_bond_group *bond_grp;
+ u8 bus_num = get_hr_bus_num(hr_dev);
+
+ bond_grp = hns_roce_get_bond_grp(net_dev, bus_num);
+ if (bond_grp && bond_grp->bond_state != HNS_ROCE_BOND_NOT_BONDED &&
+ bond_grp->bond_state != HNS_ROCE_BOND_NOT_ATTACHED)
+ return true;
+
+ return false;
+}
+
+static void hns_roce_bond_get_active_slave(struct hns_roce_bond_group *bond_grp)
+{
+ struct net_device *net_dev;
+ u32 active_slave_map = 0;
+ u8 active_slave_num = 0;
+ bool active;
+ u8 i;
+
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++) {
+ net_dev = bond_grp->bond_func_info[i].net_dev;
+ if (!net_dev || !(bond_grp->slave_map & (1U << i)))
+ continue;
+
+ active = (bond_grp->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) ?
+ net_lag_port_dev_txable(net_dev) :
+ (ib_get_curr_port_state(net_dev) == IB_PORT_ACTIVE);
+ if (active) {
+ active_slave_num++;
+ active_slave_map |= (1U << i);
+ }
+ }
+
+ bond_grp->active_slave_num = active_slave_num;
+ bond_grp->active_slave_map = active_slave_map;
+}
+
+static int hns_roce_recover_bond(struct hns_roce_bond_group *bond_grp,
+ struct hns_roce_dev *hr_dev)
+{
+ bond_grp->main_hr_dev = hr_dev;
+ hns_roce_bond_get_active_slave(bond_grp);
+
+ return hns_roce_cmd_bond(bond_grp, HNS_ROCE_SET_BOND);
+}
+
+static void hns_roce_slave_uninit(struct hns_roce_bond_group *bond_grp,
+ u8 func_idx)
+{
+ struct hnae3_handle *handle;
+
+ handle = bond_grp->bond_func_info[func_idx].handle;
+ if (handle->priv)
+ hns_roce_bond_uninit_client(bond_grp, func_idx);
+}
+
+static struct hns_roce_dev
+ *hns_roce_slave_init(struct hns_roce_bond_group *bond_grp,
+ u8 func_idx, bool need_switch);
+
+static int switch_main_dev(struct hns_roce_bond_group *bond_grp,
+ u8 main_func_idx)
+{
+ struct hns_roce_dev *hr_dev;
+ struct net_device *net_dev;
+ u8 i;
+
+ bond_grp->main_hr_dev = NULL;
+ hns_roce_bond_uninit_client(bond_grp, main_func_idx);
+
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++) {
+ net_dev = bond_grp->bond_func_info[i].net_dev;
+ if ((bond_grp->slave_map & (1U << i)) && net_dev) {
+ /* In case this slave is still being registered as
+ * a non-bonded PF, uninit it first and then re-init
+ * it as the main device.
+ */
+ hns_roce_slave_uninit(bond_grp, i);
+ hr_dev = hns_roce_slave_init(bond_grp, i, false);
+ if (hr_dev) {
+ bond_grp->main_hr_dev = hr_dev;
+ break;
+ }
+ }
+ }
+
+ if (!bond_grp->main_hr_dev)
+ return -ENODEV;
+
+ return 0;
+}
+
+static struct hns_roce_dev
+ *hns_roce_slave_init(struct hns_roce_bond_group *bond_grp,
+ u8 func_idx, bool need_switch)
+{
+ struct hns_roce_dev *hr_dev = NULL;
+ struct hnae3_handle *handle;
+ u8 main_func_idx;
+ int ret;
+
+ if (need_switch) {
+ main_func_idx = PCI_FUNC(bond_grp->main_hr_dev->pci_dev->devfn);
+ if (func_idx == main_func_idx) {
+ ret = switch_main_dev(bond_grp, main_func_idx);
+ if (ret == -ENODEV)
+ return NULL;
+ }
+ }
+
+ handle = bond_grp->bond_func_info[func_idx].handle;
+ if (handle) {
+ if (handle->priv)
+ return handle->priv;
+ /* Prevent this device from being initialized as a bond device */
+ if (need_switch)
+ bond_grp->bond_func_info[func_idx].net_dev = NULL;
+ hr_dev = hns_roce_bond_init_client(bond_grp, func_idx);
+ if (!hr_dev)
+ BOND_ERR_LOG("failed to init slave %u.\n", func_idx);
+ }
+
+ return hr_dev;
+}
+
+static struct hns_roce_die_info *alloc_die_info(int bus_num)
+{
+ struct hns_roce_die_info *die_info;
+ int ret;
+
+ die_info = kzalloc(sizeof(*die_info), GFP_KERNEL);
+ if (!die_info)
+ return NULL;
+
+ ret = xa_err(xa_store(&roce_bond_xa, bus_num, die_info, GFP_KERNEL));
+ if (ret) {
+ kfree(die_info);
+ return NULL;
+ }
+
+ mutex_init(&die_info->die_mutex);
+
+ return die_info;
+}
+
+static void dealloc_die_info(struct hns_roce_die_info *die_info, u8 bus_num)
+{
+ mutex_destroy(&die_info->die_mutex);
+ xa_erase(&roce_bond_xa, bus_num);
+ kfree(die_info);
+}
+
+static int alloc_bond_id(struct hns_roce_bond_group *bond_grp)
+{
+ u8 bus_num = bond_grp->bus_num;
+ struct hns_roce_die_info *die_info = xa_load(&roce_bond_xa, bus_num);
+ int i;
+
+ if (!die_info) {
+ die_info = alloc_die_info(bus_num);
+ if (!die_info)
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < ROCE_BOND_NUM_MAX; i++) {
+ if (die_info->bond_id_mask & BOND_ID(i))
+ continue;
+
+ die_info->bond_id_mask |= BOND_ID(i);
+ die_info->bgrps[i] = bond_grp;
+ bond_grp->bond_id = i;
+
+ return 0;
+ }
+
+ return -ENOSPC;
+}
+
+static int remove_bond_id(int bus_num, u8 bond_id)
+{
+ struct hns_roce_die_info *die_info = xa_load(&roce_bond_xa, bus_num);
+
+ if (bond_id >= ROCE_BOND_NUM_MAX)
+ return -EINVAL;
+
+ if (!die_info)
+ return -ENODEV;
+
+ die_info->bond_id_mask &= ~BOND_ID(bond_id);
+ die_info->bgrps[bond_id] = NULL;
+ if (!die_info->bond_id_mask)
+ dealloc_die_info(die_info, bus_num);
+
+ return 0;
+}
+
+static void hns_roce_set_bond(struct hns_roce_bond_group *bond_grp)
+{
+ struct hns_roce_dev *hr_dev;
+ int ret;
+ int i;
+
+ for (i = ROCE_BOND_FUNC_MAX - 1; i >= 0; i--) {
+ if (bond_grp->slave_map & (1 << i))
+ hns_roce_slave_uninit(bond_grp, i);
+ }
+
+ mutex_lock(&bond_grp->bond_mutex);
+ bond_grp->bond_state = HNS_ROCE_BOND_IS_BONDED;
+ mutex_unlock(&bond_grp->bond_mutex);
+ bond_grp->main_hr_dev = NULL;
+
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++) {
+ if (bond_grp->slave_map & (1 << i)) {
+ hr_dev = hns_roce_slave_init(bond_grp, i, false);
+ if (hr_dev) {
+ bond_grp->main_hr_dev = hr_dev;
+ break;
+ }
+ }
+ }
+
+ if (!bond_grp->main_hr_dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ hns_roce_bond_get_active_slave(bond_grp);
+
+ ret = hns_roce_cmd_bond(bond_grp, HNS_ROCE_SET_BOND);
+
+out:
+ if (ret) {
+ BOND_ERR_LOG("failed to set RoCE bond, ret = %d.\n", ret);
+ hns_roce_cleanup_bond(bond_grp);
+ } else {
+ ibdev_info(&bond_grp->main_hr_dev->ib_dev,
+ "RoCE set bond finished!\n");
+ }
+}
+
+static void hns_roce_clear_bond(struct hns_roce_bond_group *bond_grp)
+{
+ u8 main_func_idx = PCI_FUNC(bond_grp->main_hr_dev->pci_dev->devfn);
+ struct hns_roce_dev *hr_dev;
+ u8 i;
+
+ if (bond_grp->bond_state == HNS_ROCE_BOND_NOT_BONDED)
+ goto out;
+
+ bond_grp->bond_state = HNS_ROCE_BOND_NOT_BONDED;
+ bond_grp->main_hr_dev = NULL;
+
+ hns_roce_slave_uninit(bond_grp, main_func_idx);
+
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++) {
+ hr_dev = hns_roce_slave_init(bond_grp, i, false);
+ if (hr_dev)
+ bond_grp->main_hr_dev = hr_dev;
+ }
+
+out:
+ hns_roce_cleanup_bond(bond_grp);
+}
+
+static void hns_roce_slave_changestate(struct hns_roce_bond_group *bond_grp)
+{
+ int ret;
+
+ hns_roce_bond_get_active_slave(bond_grp);
+
+ ret = hns_roce_cmd_bond(bond_grp, HNS_ROCE_CHANGE_BOND);
+
+ mutex_lock(&bond_grp->bond_mutex);
+ if (bond_grp->bond_state == HNS_ROCE_BOND_SLAVE_CHANGESTATE)
+ bond_grp->bond_state = HNS_ROCE_BOND_IS_BONDED;
+ mutex_unlock(&bond_grp->bond_mutex);
+
+ if (ret)
+ ibdev_err(&bond_grp->main_hr_dev->ib_dev,
+ "failed to change RoCE bond slave state, ret = %d.\n",
+ ret);
+ else
+ ibdev_info(&bond_grp->main_hr_dev->ib_dev,
+ "RoCE slave changestate finished!\n");
+}
+
+static void hns_roce_slave_change_num(struct hns_roce_bond_group *bond_grp)
+{
+ int ret;
+ u8 i;
+
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++) {
+ if (bond_grp->slave_map & (1U << i)) {
+ if (i == PCI_FUNC(bond_grp->main_hr_dev->pci_dev->devfn))
+ continue;
+ hns_roce_slave_uninit(bond_grp, i);
+ } else {
+ hns_roce_slave_init(bond_grp, i, true);
+ if (!bond_grp->main_hr_dev) {
+ ret = -ENODEV;
+ goto out;
+ }
+ bond_grp->bond_func_info[i].net_dev = NULL;
+ bond_grp->bond_func_info[i].handle = NULL;
+ }
+ }
+
+ hns_roce_bond_get_active_slave(bond_grp);
+
+ ret = hns_roce_cmd_bond(bond_grp, HNS_ROCE_CHANGE_BOND);
+
+out:
+ if (ret) {
+ BOND_ERR_LOG("failed to change RoCE bond slave num, ret = %d.\n", ret);
+ hns_roce_cleanup_bond(bond_grp);
+ } else {
+ mutex_lock(&bond_grp->bond_mutex);
+ if (bond_grp->bond_state == HNS_ROCE_BOND_SLAVE_CHANGE_NUM)
+ bond_grp->bond_state = HNS_ROCE_BOND_IS_BONDED;
+ mutex_unlock(&bond_grp->bond_mutex);
+ ibdev_info(&bond_grp->main_hr_dev->ib_dev,
+ "RoCE slave change num finished!\n");
+ }
+}
+
+static void hns_roce_bond_info_update_nolock(struct hns_roce_bond_group *bond_grp,
+ struct net_device *upper_dev)
+{
+ struct hns_roce_v2_priv *priv;
+ struct hns_roce_dev *hr_dev;
+ struct net_device *net_dev;
+ int func_idx;
+
+ bond_grp->slave_map = 0;
+ rcu_read_lock();
+ for_each_netdev_in_bond_rcu(upper_dev, net_dev) {
+ func_idx = get_netdev_bond_slave_id(net_dev, bond_grp);
+ if (func_idx < 0) {
+ hr_dev = hns_roce_get_hrdev_by_netdev(net_dev);
+ if (!hr_dev)
+ continue;
+ func_idx = PCI_FUNC(hr_dev->pci_dev->devfn);
+ if (!bond_grp->bond_func_info[func_idx].net_dev) {
+ priv = hr_dev->priv;
+ bond_grp->bond_func_info[func_idx].net_dev =
+ net_dev;
+ bond_grp->bond_func_info[func_idx].handle =
+ priv->handle;
+ }
+ ib_device_put(&hr_dev->ib_dev);
+ }
+
+ bond_grp->slave_map |= (1 << func_idx);
+ }
+ rcu_read_unlock();
+}
+
+static bool is_dev_bond_supported(struct hns_roce_bond_group *bond_grp,
+ struct net_device *net_dev)
+{
+ struct hns_roce_dev *hr_dev = hns_roce_get_hrdev_by_netdev(net_dev);
+ bool ret = true;
+
+ if (!hr_dev) {
+ if (bond_grp &&
+ get_netdev_bond_slave_id(net_dev, bond_grp) >= 0)
+ return true;
+ else
+ return false;
+ }
+
+ if (!(hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_BOND)) {
+ ret = false;
+ goto out;
+ }
+
+ if (hr_dev->is_vf || pci_num_vf(hr_dev->pci_dev) > 0) {
+ ret = false;
+ goto out;
+ }
+
+ if (bond_grp->bus_num != get_hr_bus_num(hr_dev))
+ ret = false;
+
+out:
+ ib_device_put(&hr_dev->ib_dev);
+ return ret;
+}
+
+static bool check_slave_support(struct hns_roce_bond_group *bond_grp,
+ struct net_device *upper_dev)
+{
+ struct net_device *net_dev;
+ u8 slave_num = 0;
+
+ rcu_read_lock();
+ for_each_netdev_in_bond_rcu(upper_dev, net_dev) {
+ if (is_dev_bond_supported(bond_grp, net_dev)) {
+ slave_num++;
+ continue;
+ }
+ rcu_read_unlock();
+ return false;
+ }
+ rcu_read_unlock();
+
+ return (slave_num > 1 && slave_num <= ROCE_BOND_FUNC_MAX);
+}
+
+static void hns_roce_bond_work(struct work_struct *work)
+{
+ struct delayed_work *delayed_work = to_delayed_work(work);
+ struct hns_roce_bond_group *bond_grp =
+ container_of(delayed_work, struct hns_roce_bond_group,
+ bond_work);
+ enum hns_roce_bond_state bond_state;
+ bool bond_ready;
+
+ mutex_lock(&bond_grp->bond_mutex);
+ bond_ready = check_slave_support(bond_grp, bond_grp->upper_dev);
+ hns_roce_bond_info_update_nolock(bond_grp, bond_grp->upper_dev);
+ bond_state = bond_grp->bond_state;
+ bond_grp->bond_ready = bond_ready;
+ mutex_unlock(&bond_grp->bond_mutex);
+
+ ibdev_info(&bond_grp->main_hr_dev->ib_dev,
+ "bond work: bond_ready - %d, bond_state - %d.\n",
+ bond_ready, bond_state);
+
+ if (!bond_ready) {
+ hns_roce_clear_bond(bond_grp);
+ return;
+ }
+
+ switch (bond_state) {
+ case HNS_ROCE_BOND_NOT_BONDED:
+ hns_roce_set_bond(bond_grp);
+ /* In set_bond flow, we don't need to set bond netdev here as
+ * it has been done when bond_grp->main_hr_dev is registered.
+ */
+ return;
+ case HNS_ROCE_BOND_SLAVE_CHANGESTATE:
+ hns_roce_slave_changestate(bond_grp);
+ break;
+ case HNS_ROCE_BOND_SLAVE_CHANGE_NUM:
+ hns_roce_slave_change_num(bond_grp);
+ break;
+ default:
+ return;
+ }
+ hns_roce_set_bond_netdev(bond_grp, bond_grp->main_hr_dev);
+}
+
+static void hns_roce_attach_bond_grp(struct hns_roce_bond_group *bond_grp,
+ struct hns_roce_dev *hr_dev,
+ struct net_device *upper_dev)
+{
+ bond_grp->upper_dev = upper_dev;
+ bond_grp->main_hr_dev = hr_dev;
+ bond_grp->bond_state = HNS_ROCE_BOND_NOT_BONDED;
+ bond_grp->bond_ready = false;
+}
+
+static void hns_roce_detach_bond_grp(struct hns_roce_bond_group *bond_grp)
+{
+ mutex_lock(&bond_grp->bond_mutex);
+
+ cancel_delayed_work(&bond_grp->bond_work);
+ bond_grp->upper_dev = NULL;
+ bond_grp->main_hr_dev = NULL;
+ bond_grp->bond_ready = false;
+ bond_grp->bond_state = HNS_ROCE_BOND_NOT_ATTACHED;
+ bond_grp->slave_map = 0;
+ memset(bond_grp->bond_func_info, 0, sizeof(bond_grp->bond_func_info));
+
+ mutex_unlock(&bond_grp->bond_mutex);
+}
+
+void hns_roce_cleanup_bond(struct hns_roce_bond_group *bond_grp)
+{
+ int ret;
+
+ ret = bond_grp->main_hr_dev ?
+ hns_roce_cmd_bond(bond_grp, HNS_ROCE_CLEAR_BOND) : -EIO;
+ if (ret)
+ BOND_ERR_LOG("failed to clear RoCE bond, ret = %d.\n", ret);
+ else
+ ibdev_info(&bond_grp->main_hr_dev->ib_dev,
+ "RoCE clear bond finished!\n");
+
+ hns_roce_detach_bond_grp(bond_grp);
+}
+
+static bool lowerstate_event_filter(struct hns_roce_bond_group *bond_grp,
+ struct net_device *net_dev)
+{
+ struct hns_roce_bond_group *bond_grp_tmp;
+
+ bond_grp_tmp = hns_roce_get_bond_grp(net_dev, bond_grp->bus_num);
+ return bond_grp_tmp == bond_grp;
+}
+
+static void lowerstate_event_setting(struct hns_roce_bond_group *bond_grp,
+ struct netdev_notifier_changelowerstate_info *info)
+{
+ mutex_lock(&bond_grp->bond_mutex);
+
+ if (bond_grp->bond_ready &&
+ bond_grp->bond_state == HNS_ROCE_BOND_IS_BONDED)
+ bond_grp->bond_state = HNS_ROCE_BOND_SLAVE_CHANGESTATE;
+
+ mutex_unlock(&bond_grp->bond_mutex);
+}
+
+static bool hns_roce_bond_lowerstate_event(struct hns_roce_bond_group *bond_grp,
+ struct netdev_notifier_changelowerstate_info *info)
+{
+ struct net_device *net_dev =
+ netdev_notifier_info_to_dev((struct netdev_notifier_info *)info);
+
+ if (!netif_is_lag_port(net_dev))
+ return false;
+
+ if (!lowerstate_event_filter(bond_grp, net_dev))
+ return false;
+
+ lowerstate_event_setting(bond_grp, info);
+
+ return true;
+}
+
+static bool is_bond_setting_supported(struct netdev_lag_upper_info *bond_info)
+{
+ if (!bond_info)
+ return false;
+
+ if (bond_info->tx_type != NETDEV_LAG_TX_TYPE_ACTIVEBACKUP &&
+ bond_info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
+ return false;
+
+ if (bond_info->tx_type == NETDEV_LAG_TX_TYPE_HASH &&
+ bond_info->hash_type > NETDEV_LAG_HASH_L23)
+ return false;
+
+ return true;
+}
+
+static void upper_event_setting(struct hns_roce_bond_group *bond_grp,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct netdev_lag_upper_info *bond_upper_info = NULL;
+ bool slave_inc = info->linking;
+
+ if (slave_inc)
+ bond_upper_info = info->upper_info;
+
+ if (bond_upper_info) {
+ bond_grp->tx_type = bond_upper_info->tx_type;
+ bond_grp->hash_type = bond_upper_info->hash_type;
+ }
+}
+
+static bool check_unlinking_bond_support(struct hns_roce_bond_group *bond_grp)
+{
+ struct net_device *net_dev;
+ u8 slave_num = 0;
+
+ rcu_read_lock();
+ for_each_netdev_in_bond_rcu(bond_grp->upper_dev, net_dev) {
+ if (get_netdev_bond_slave_id(net_dev, bond_grp) >= 0)
+ slave_num++;
+ }
+ rcu_read_unlock();
+
+ return (slave_num > 1);
+}
+
+static bool check_linking_bond_support(struct netdev_lag_upper_info *bond_info,
+ struct hns_roce_bond_group *bond_grp,
+ struct net_device *upper_dev)
+{
+ if (!is_bond_setting_supported(bond_info))
+ return false;
+
+ return check_slave_support(bond_grp, upper_dev);
+}
+
+static enum bond_support_type
+ check_bond_support(struct hns_roce_bond_group *bond_grp,
+ struct net_device *upper_dev,
+ struct netdev_notifier_changeupper_info *info)
+{
+ bool bond_grp_exist = false;
+ bool support;
+
+ if (upper_dev == bond_grp->upper_dev)
+ bond_grp_exist = true;
+
+ if (!info->linking && !bond_grp_exist)
+ return BOND_NOT_SUPPORT;
+
+ if (info->linking)
+ support = check_linking_bond_support(info->upper_info, bond_grp,
+ upper_dev);
+ else
+ support = check_unlinking_bond_support(bond_grp);
+
+ if (support)
+ return BOND_SUPPORT;
+
+ return bond_grp_exist ? BOND_EXISTING_NOT_SUPPORT : BOND_NOT_SUPPORT;
+}
+
+static bool upper_event_filter(struct netdev_notifier_changeupper_info *info,
+ struct hns_roce_bond_group *bond_grp,
+ struct net_device *net_dev)
+{
+ struct net_device *upper_dev = info->upper_dev;
+ struct hns_roce_bond_group *bond_grp_tmp;
+ struct hns_roce_dev *hr_dev;
+ bool ret = true;
+ u8 bus_num;
+
+ if (!info->linking ||
+ bond_grp->bond_state != HNS_ROCE_BOND_NOT_ATTACHED)
+ return bond_grp->upper_dev == upper_dev;
+
+ hr_dev = hns_roce_get_hrdev_by_netdev(net_dev);
+ if (!hr_dev)
+ return false;
+
+ bus_num = get_hr_bus_num(hr_dev);
+ if (bond_grp->bus_num != bus_num) {
+ ret = false;
+ goto out;
+ }
+
+ bond_grp_tmp = hns_roce_get_bond_grp(net_dev, bus_num);
+ if (bond_grp_tmp && bond_grp_tmp != bond_grp)
+ ret = false;
+out:
+ ib_device_put(&hr_dev->ib_dev);
+ return ret;
+}
+
+static bool hns_roce_bond_upper_event(struct hns_roce_bond_group *bond_grp,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct net_device *net_dev =
+ netdev_notifier_info_to_dev((struct netdev_notifier_info *)info);
+ struct net_device *upper_dev = info->upper_dev;
+ enum bond_support_type support = BOND_SUPPORT;
+ struct hns_roce_dev *hr_dev;
+ int slave_id;
+
+ if (!upper_dev || !netif_is_lag_master(upper_dev))
+ return false;
+
+ if (!upper_event_filter(info, bond_grp, net_dev))
+ return false;
+
+ mutex_lock(&bond_grp->bond_mutex);
+ support = check_bond_support(bond_grp, upper_dev, info);
+ if (support == BOND_NOT_SUPPORT) {
+ mutex_unlock(&bond_grp->bond_mutex);
+ return false;
+ }
+
+ if (bond_grp->bond_state == HNS_ROCE_BOND_NOT_ATTACHED) {
+ hr_dev = hns_roce_get_hrdev_by_netdev(net_dev);
+ if (!hr_dev) {
+ mutex_unlock(&bond_grp->bond_mutex);
+ return false;
+ }
+ hns_roce_attach_bond_grp(bond_grp, hr_dev, upper_dev);
+ ib_device_put(&hr_dev->ib_dev);
+ }
+
+ /* In the case of netdev being unregistered, the roce
+ * instance shouldn't be inited.
+ */
+ if (net_dev->reg_state >= NETREG_UNREGISTERING) {
+ slave_id = get_netdev_bond_slave_id(net_dev, bond_grp);
+ if (slave_id >= 0) {
+ bond_grp->bond_func_info[slave_id].net_dev = NULL;
+ bond_grp->bond_func_info[slave_id].handle = NULL;
+ }
+ }
+
+ if (support == BOND_SUPPORT) {
+ bond_grp->bond_ready = true;
+ if (bond_grp->bond_state != HNS_ROCE_BOND_NOT_BONDED)
+ bond_grp->bond_state = HNS_ROCE_BOND_SLAVE_CHANGE_NUM;
+ }
+ mutex_unlock(&bond_grp->bond_mutex);
+ if (support == BOND_SUPPORT)
+ upper_event_setting(bond_grp, info);
+
+ return true;
+}
+
+static int hns_roce_bond_event(struct notifier_block *self,
+ unsigned long event, void *ptr)
+{
+ struct hns_roce_bond_group *bond_grp =
+ container_of(self, struct hns_roce_bond_group, bond_nb);
+ bool changed = false;
+
+ if (event == NETDEV_CHANGEUPPER)
+ changed = hns_roce_bond_upper_event(bond_grp, ptr);
+ if (event == NETDEV_CHANGELOWERSTATE)
+ changed = hns_roce_bond_lowerstate_event(bond_grp, ptr);
+
+ if (changed)
+ schedule_delayed_work(&bond_grp->bond_work, HZ);
+
+ return NOTIFY_DONE;
+}
+
+int hns_roce_alloc_bond_grp(struct hns_roce_dev *hr_dev)
+{
+ struct hns_roce_bond_group *bgrps[ROCE_BOND_NUM_MAX];
+ struct hns_roce_bond_group *bond_grp;
+ u8 bus_num = get_hr_bus_num(hr_dev);
+ int ret;
+ int i;
+
+ if (xa_load(&roce_bond_xa, bus_num))
+ return 0;
+
+ for (i = 0; i < ROCE_BOND_NUM_MAX; i++) {
+ bond_grp = kvzalloc(sizeof(*bond_grp), GFP_KERNEL);
+ if (!bond_grp) {
+ ret = -ENOMEM;
+ goto mem_err;
+ }
+
+ mutex_init(&bond_grp->bond_mutex);
+ INIT_DELAYED_WORK(&bond_grp->bond_work, hns_roce_bond_work);
+
+ bond_grp->bond_ready = false;
+ bond_grp->bond_state = HNS_ROCE_BOND_NOT_ATTACHED;
+ bond_grp->bus_num = bus_num;
+
+ ret = alloc_bond_id(bond_grp);
+ if (ret) {
+ dev_err(hr_dev->dev,
+ "failed to alloc bond ID, ret = %d.\n", ret);
+ goto alloc_id_err;
+ }
+
+ bond_grp->bond_nb.notifier_call = hns_roce_bond_event;
+ ret = register_netdevice_notifier(&bond_grp->bond_nb);
+ if (ret) {
+ ibdev_err(&hr_dev->ib_dev,
+ "failed to register bond nb, ret = %d.\n", ret);
+ goto register_nb_err;
+ }
+ bgrps[i] = bond_grp;
+ }
+
+ return 0;
+
+register_nb_err:
+ remove_bond_id(bond_grp->bus_num, bond_grp->bond_id);
+alloc_id_err:
+ mutex_destroy(&bond_grp->bond_mutex);
+ kvfree(bond_grp);
+mem_err:
+ for (i--; i >= 0; i--) {
+ unregister_netdevice_notifier(&bgrps[i]->bond_nb);
+ cancel_delayed_work_sync(&bgrps[i]->bond_work);
+ remove_bond_id(bgrps[i]->bus_num, bgrps[i]->bond_id);
+ mutex_destroy(&bgrps[i]->bond_mutex);
+ kvfree(bgrps[i]);
+ }
+ return ret;
+}
+
+void hns_roce_dealloc_bond_grp(void)
+{
+ struct hns_roce_bond_group *bond_grp;
+ struct hns_roce_die_info *die_info;
+ unsigned long id;
+ int i;
+
+ xa_for_each(&roce_bond_xa, id, die_info) {
+ for (i = 0; i < ROCE_BOND_NUM_MAX; i++) {
+ bond_grp = die_info->bgrps[i];
+ if (!bond_grp)
+ continue;
+ unregister_netdevice_notifier(&bond_grp->bond_nb);
+ cancel_delayed_work_sync(&bond_grp->bond_work);
+ remove_bond_id(bond_grp->bus_num, bond_grp->bond_id);
+ mutex_destroy(&bond_grp->bond_mutex);
+ kvfree(bond_grp);
+ }
+ }
+}
+
+int hns_roce_bond_init(struct hns_roce_dev *hr_dev)
+{
+ struct net_device *net_dev = get_hr_netdev(hr_dev, 0);
+ struct hns_roce_v2_priv *priv = hr_dev->priv;
+ struct hns_roce_bond_group *bond_grp;
+ u8 bus_num = get_hr_bus_num(hr_dev);
+ int ret;
+
+ bond_grp = hns_roce_get_bond_grp(net_dev, bus_num);
+
+ if (priv->handle->rinfo.reset_state == HNS_ROCE_STATE_RST_INIT) {
+ ret = hns_roce_recover_bond(bond_grp, hr_dev);
+ if (ret) {
+ dev_err(hr_dev->dev,
+ "failed to recover RoCE bond, ret = %d.\n", ret);
+ return ret;
+ }
+ }
+
+ return hns_roce_set_bond_netdev(bond_grp, hr_dev);
+}
+
+void hns_roce_bond_suspend(struct hnae3_handle *handle)
+{
+ u8 bus_num = handle->pdev->bus->number;
+ struct hns_roce_bond_group *bond_grp;
+ struct hns_roce_die_info *die_info;
+ int i;
+
+ die_info = xa_load(&roce_bond_xa, bus_num);
+ if (!die_info)
+ return;
+
+ mutex_lock(&die_info->die_mutex);
+
+ /*
+ * Avoid duplicated processing when calling this function
+ * multiple times.
+ */
+ if (die_info->suspend_cnt)
+ goto out;
+
+ for (i = 0; i < ROCE_BOND_NUM_MAX; i++) {
+ bond_grp = die_info->bgrps[i];
+ if (!bond_grp)
+ continue;
+ unregister_netdevice_notifier(&bond_grp->bond_nb);
+ cancel_delayed_work_sync(&bond_grp->bond_work);
+ }
+
+out:
+ die_info->suspend_cnt++;
+ mutex_unlock(&die_info->die_mutex);
+}
+
+void hns_roce_bond_resume(struct hnae3_handle *handle)
+{
+ u8 bus_num = handle->pdev->bus->number;
+ struct hns_roce_bond_group *bond_grp;
+ struct hns_roce_die_info *die_info;
+ int i, ret;
+
+ die_info = xa_load(&roce_bond_xa, bus_num);
+ if (!die_info)
+ return;
+
+ mutex_lock(&die_info->die_mutex);
+
+ die_info->suspend_cnt--;
+ if (die_info->suspend_cnt)
+ goto out;
+
+ for (i = 0; i < ROCE_BOND_NUM_MAX; i++) {
+ bond_grp = die_info->bgrps[i];
+ if (!bond_grp)
+ continue;
+ ret = register_netdevice_notifier(&bond_grp->bond_nb);
+ if (ret)
+ dev_err(&handle->pdev->dev,
+ "failed to resume bond notifier(bus_num = %u, id = %u), ret = %d.\n",
+ bus_num, bond_grp->bond_id, ret);
+ }
+
+out:
+ mutex_unlock(&die_info->die_mutex);
+}
diff --git a/drivers/infiniband/hw/hns/hns_roce_bond.h b/drivers/infiniband/hw/hns/hns_roce_bond.h
new file mode 100644
index 000000000000..98c295d78ca1
--- /dev/null
+++ b/drivers/infiniband/hw/hns/hns_roce_bond.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2025 Hisilicon Limited.
+ */
+
+#ifndef _HNS_ROCE_BOND_H
+#define _HNS_ROCE_BOND_H
+
+#include <linux/netdevice.h>
+#include <net/bonding.h>
+
+#define ROCE_BOND_FUNC_MAX 4
+#define ROCE_BOND_NUM_MAX 2
+
+#define BOND_ID(id) BIT(id)
+
+#define BOND_ERR_LOG(fmt, ...) \
+ pr_err("HNS RoCE Bonding: " fmt, ##__VA_ARGS__)
+
+enum {
+ BOND_MODE_1,
+ BOND_MODE_2_4,
+};
+
+enum hns_roce_bond_hashtype {
+ BOND_HASH_L2,
+ BOND_HASH_L34,
+ BOND_HASH_L23,
+};
+
+enum bond_support_type {
+ BOND_NOT_SUPPORT,
+ /*
+ * bond_grp already exists, but in the current
+ * conditions it's no longer supported
+ */
+ BOND_EXISTING_NOT_SUPPORT,
+ BOND_SUPPORT,
+};
+
+enum hns_roce_bond_state {
+ HNS_ROCE_BOND_NOT_ATTACHED,
+ HNS_ROCE_BOND_NOT_BONDED,
+ HNS_ROCE_BOND_IS_BONDED,
+ HNS_ROCE_BOND_SLAVE_CHANGE_NUM,
+ HNS_ROCE_BOND_SLAVE_CHANGESTATE,
+};
+
+enum hns_roce_bond_cmd_type {
+ HNS_ROCE_SET_BOND,
+ HNS_ROCE_CHANGE_BOND,
+ HNS_ROCE_CLEAR_BOND,
+};
+
+struct hns_roce_func_info {
+ struct net_device *net_dev;
+ struct hnae3_handle *handle;
+};
+
+struct hns_roce_bond_group {
+ struct net_device *upper_dev;
+ struct hns_roce_dev *main_hr_dev;
+ u8 active_slave_num;
+ u32 slave_map;
+ u32 active_slave_map;
+ u8 bond_id;
+ u8 bus_num;
+ struct hns_roce_func_info bond_func_info[ROCE_BOND_FUNC_MAX];
+ bool bond_ready;
+ enum hns_roce_bond_state bond_state;
+ enum netdev_lag_tx_type tx_type;
+ enum netdev_lag_hash hash_type;
+ struct mutex bond_mutex;
+ struct notifier_block bond_nb;
+ struct delayed_work bond_work;
+};
+
+struct hns_roce_die_info {
+ u8 bond_id_mask;
+ struct hns_roce_bond_group *bgrps[ROCE_BOND_NUM_MAX];
+ struct mutex die_mutex;
+ u8 suspend_cnt;
+};
+
+struct hns_roce_bond_group *hns_roce_get_bond_grp(struct net_device *net_dev,
+ u8 bus_num);
+int hns_roce_alloc_bond_grp(struct hns_roce_dev *hr_dev);
+void hns_roce_dealloc_bond_grp(void);
+void hns_roce_cleanup_bond(struct hns_roce_bond_group *bond_grp);
+bool hns_roce_bond_is_active(struct hns_roce_dev *hr_dev);
+int hns_roce_bond_init(struct hns_roce_dev *hr_dev);
+void hns_roce_bond_suspend(struct hnae3_handle *handle);
+void hns_roce_bond_resume(struct hnae3_handle *handle);
+
+#endif
diff --git a/drivers/infiniband/hw/hns/hns_roce_cq.c b/drivers/infiniband/hw/hns/hns_roce_cq.c
index 3a5c93c9fb3e..6aa82fe9dd3d 100644
--- a/drivers/infiniband/hw/hns/hns_roce_cq.c
+++ b/drivers/infiniband/hw/hns/hns_roce_cq.c
@@ -30,6 +30,7 @@
* SOFTWARE.
*/
+#include <linux/pci.h>
#include <rdma/ib_umem.h>
#include <rdma/uverbs_ioctl.h>
#include "hns_roce_device.h"
@@ -37,6 +38,43 @@
#include "hns_roce_hem.h"
#include "hns_roce_common.h"
+void hns_roce_put_cq_bankid_for_uctx(struct hns_roce_ucontext *uctx)
+{
+ struct hns_roce_dev *hr_dev = to_hr_dev(uctx->ibucontext.device);
+ struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
+
+ if (hr_dev->pci_dev->revision < PCI_REVISION_ID_HIP09)
+ return;
+
+ mutex_lock(&cq_table->bank_mutex);
+ cq_table->ctx_num[uctx->cq_bank_id]--;
+ mutex_unlock(&cq_table->bank_mutex);
+}
+
+void hns_roce_get_cq_bankid_for_uctx(struct hns_roce_ucontext *uctx)
+{
+ struct hns_roce_dev *hr_dev = to_hr_dev(uctx->ibucontext.device);
+ struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
+ u32 least_load = cq_table->ctx_num[0];
+ u8 bankid = 0;
+ u8 i;
+
+ if (hr_dev->pci_dev->revision < PCI_REVISION_ID_HIP09)
+ return;
+
+ mutex_lock(&cq_table->bank_mutex);
+ for (i = 1; i < HNS_ROCE_CQ_BANK_NUM; i++) {
+ if (cq_table->ctx_num[i] < least_load) {
+ least_load = cq_table->ctx_num[i];
+ bankid = i;
+ }
+ }
+ cq_table->ctx_num[bankid]++;
+ mutex_unlock(&cq_table->bank_mutex);
+
+ uctx->cq_bank_id = bankid;
+}
+
static u8 get_least_load_bankid_for_cq(struct hns_roce_bank *bank)
{
u32 least_load = bank[0].inuse;
@@ -55,7 +93,21 @@ static u8 get_least_load_bankid_for_cq(struct hns_roce_bank *bank)
return bankid;
}
-static int alloc_cqn(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq)
+static u8 select_cq_bankid(struct hns_roce_dev *hr_dev,
+ struct hns_roce_bank *bank, struct ib_udata *udata)
+{
+ struct hns_roce_ucontext *uctx = udata ?
+ rdma_udata_to_drv_context(udata, struct hns_roce_ucontext,
+ ibucontext) : NULL;
+
+ if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09)
+ return uctx ? uctx->cq_bank_id : 0;
+
+ return get_least_load_bankid_for_cq(bank);
+}
+
+static int alloc_cqn(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq,
+ struct ib_udata *udata)
{
struct hns_roce_cq_table *cq_table = &hr_dev->cq_table;
struct hns_roce_bank *bank;
@@ -63,7 +115,7 @@ static int alloc_cqn(struct hns_roce_dev *hr_dev, struct hns_roce_cq *hr_cq)
int id;
mutex_lock(&cq_table->bank_mutex);
- bankid = get_least_load_bankid_for_cq(cq_table->bank);
+ bankid = select_cq_bankid(hr_dev, cq_table->bank, udata);
bank = &cq_table->bank[bankid];
id = ida_alloc_range(&bank->ida, bank->min, bank->max, GFP_KERNEL);
@@ -396,7 +448,7 @@ int hns_roce_create_cq(struct ib_cq *ib_cq, const struct ib_cq_init_attr *attr,
goto err_cq_buf;
}
- ret = alloc_cqn(hr_dev, hr_cq);
+ ret = alloc_cqn(hr_dev, hr_cq, udata);
if (ret) {
ibdev_err(ibdev, "failed to alloc CQN, ret = %d.\n", ret);
goto err_cq_db;
diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h
index 78ee04a48a74..318f18cf37aa 100644
--- a/drivers/infiniband/hw/hns/hns_roce_device.h
+++ b/drivers/infiniband/hw/hns/hns_roce_device.h
@@ -33,6 +33,7 @@
#ifndef _HNS_ROCE_DEVICE_H
#define _HNS_ROCE_DEVICE_H
+#include <linux/pci.h>
#include <rdma/ib_verbs.h>
#include <rdma/hns-abi.h>
#include "hns_roce_debugfs.h"
@@ -153,6 +154,7 @@ enum {
HNS_ROCE_CAP_FLAG_SDI_MODE = BIT(14),
HNS_ROCE_CAP_FLAG_STASH = BIT(17),
HNS_ROCE_CAP_FLAG_CQE_INLINE = BIT(19),
+ HNS_ROCE_CAP_FLAG_BOND = BIT(21),
HNS_ROCE_CAP_FLAG_SRQ_RECORD_DB = BIT(22),
};
@@ -177,6 +179,7 @@ enum hns_roce_instance_state {
HNS_ROCE_STATE_INIT,
HNS_ROCE_STATE_INITED,
HNS_ROCE_STATE_UNINIT,
+ HNS_ROCE_STATE_BOND_UNINIT,
};
enum {
@@ -217,6 +220,7 @@ struct hns_roce_ucontext {
struct mutex page_mutex;
struct hns_user_mmap_entry *db_mmap_entry;
u32 config;
+ u8 cq_bank_id;
};
struct hns_roce_pd {
@@ -495,6 +499,7 @@ struct hns_roce_cq_table {
struct hns_roce_hem_table table;
struct hns_roce_bank bank[HNS_ROCE_CQ_BANK_NUM];
struct mutex bank_mutex;
+ u32 ctx_num[HNS_ROCE_CQ_BANK_NUM];
};
struct hns_roce_srq_table {
@@ -1165,6 +1170,17 @@ static inline u8 get_tclass(const struct ib_global_route *grh)
grh->traffic_class >> DSCP_SHIFT : grh->traffic_class;
}
+static inline struct net_device *get_hr_netdev(struct hns_roce_dev *hr_dev,
+ u8 port)
+{
+ return hr_dev->iboe.netdevs[port];
+}
+
+static inline u8 get_hr_bus_num(struct hns_roce_dev *hr_dev)
+{
+ return hr_dev->pci_dev->bus->number;
+}
+
void hns_roce_init_uar_table(struct hns_roce_dev *dev);
int hns_roce_uar_alloc(struct hns_roce_dev *dev, struct hns_roce_uar *uar);
@@ -1291,7 +1307,7 @@ void hns_roce_flush_cqe(struct hns_roce_dev *hr_dev, u32 qpn);
void hns_roce_srq_event(struct hns_roce_dev *hr_dev, u32 srqn, int event_type);
void hns_roce_handle_device_err(struct hns_roce_dev *hr_dev);
int hns_roce_init(struct hns_roce_dev *hr_dev);
-void hns_roce_exit(struct hns_roce_dev *hr_dev);
+void hns_roce_exit(struct hns_roce_dev *hr_dev, bool bond_cleanup);
int hns_roce_fill_res_cq_entry(struct sk_buff *msg, struct ib_cq *ib_cq);
int hns_roce_fill_res_cq_entry_raw(struct sk_buff *msg, struct ib_cq *ib_cq);
int hns_roce_fill_res_qp_entry(struct sk_buff *msg, struct ib_qp *ib_qp);
@@ -1305,5 +1321,7 @@ hns_roce_user_mmap_entry_insert(struct ib_ucontext *ucontext, u64 address,
size_t length,
enum hns_roce_mmap_type mmap_type);
bool check_sl_valid(struct hns_roce_dev *hr_dev, u8 sl);
+void hns_roce_put_cq_bankid_for_uctx(struct hns_roce_ucontext *uctx);
+void hns_roce_get_cq_bankid_for_uctx(struct hns_roce_ucontext *uctx);
#endif /* _HNS_ROCE_DEVICE_H */
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
index f82bdd46a917..2d6ae89e525b 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c
@@ -43,11 +43,13 @@
#include <rdma/ib_umem.h>
#include <rdma/uverbs_ioctl.h>
+#include "hclge_main.h"
#include "hns_roce_common.h"
#include "hns_roce_device.h"
#include "hns_roce_cmd.h"
#include "hns_roce_hem.h"
#include "hns_roce_hw_v2.h"
+#include "hns_roce_bond.h"
#define CREATE_TRACE_POINTS
#include "hns_roce_trace.h"
@@ -165,6 +167,8 @@ static void set_frmr_seg(struct hns_roce_v2_rc_send_wqe *rc_sq_wqe,
hr_reg_write(fseg, FRMR_PBL_BUF_PG_SZ,
to_hr_hw_page_shift(mr->pbl_mtr.hem_cfg.buf_pg_shift));
hr_reg_clear(fseg, FRMR_BLK_MODE);
+ hr_reg_clear(fseg, FRMR_BLOCK_SIZE);
+ hr_reg_clear(fseg, FRMR_ZBVA);
}
static void set_atomic_seg(const struct ib_send_wr *wr,
@@ -339,9 +343,6 @@ static int set_rwqe_data_seg(struct ib_qp *ibqp, const struct ib_send_wr *wr,
int j = 0;
int i;
- hr_reg_write(rc_sq_wqe, RC_SEND_WQE_MSG_START_SGE_IDX,
- (*sge_ind) & (qp->sge.sge_cnt - 1));
-
hr_reg_write(rc_sq_wqe, RC_SEND_WQE_INLINE,
!!(wr->send_flags & IB_SEND_INLINE));
if (wr->send_flags & IB_SEND_INLINE)
@@ -586,6 +587,9 @@ static inline int set_rc_wqe(struct hns_roce_qp *qp,
hr_reg_write(rc_sq_wqe, RC_SEND_WQE_CQE,
(wr->send_flags & IB_SEND_SIGNALED) ? 1 : 0);
+ hr_reg_write(rc_sq_wqe, RC_SEND_WQE_MSG_START_SGE_IDX,
+ curr_idx & (qp->sge.sge_cnt - 1));
+
if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
wr->opcode == IB_WR_ATOMIC_FETCH_AND_ADD) {
if (msg_len != ATOMIC_WR_LEN)
@@ -734,6 +738,9 @@ static int hns_roce_v2_post_send(struct ib_qp *ibqp,
owner_bit =
~(((qp->sq.head + nreq) >> ilog2(qp->sq.wqe_cnt)) & 0x1);
+ /* RC and UD share the same DirectWQE field layout */
+ ((struct hns_roce_v2_rc_send_wqe *)wqe)->byte_4 = 0;
+
/* Corresponding to the QP type, wqe process separately */
if (ibqp->qp_type == IB_QPT_RC)
ret = set_rc_wqe(qp, wr, wqe, &sge_idx, owner_bit);
@@ -1429,6 +1436,79 @@ static int hns_roce_cmq_send(struct hns_roce_dev *hr_dev,
return ret;
}
+static enum hns_roce_opcode_type
+ get_bond_opcode(enum hns_roce_bond_cmd_type bond_type)
+{
+ switch (bond_type) {
+ case HNS_ROCE_SET_BOND:
+ return HNS_ROCE_OPC_SET_BOND_INFO;
+ case HNS_ROCE_CHANGE_BOND:
+ return HNS_ROCE_OPC_CHANGE_ACTIVE_PORT;
+ case HNS_ROCE_CLEAR_BOND:
+ return HNS_ROCE_OPC_CLEAR_BOND_INFO;
+ default:
+ WARN(true, "Invalid bond type %d!\n", bond_type);
+ return HNS_ROCE_OPC_SET_BOND_INFO;
+ }
+}
+
+static enum hns_roce_bond_hashtype
+ get_bond_hashtype(enum netdev_lag_hash netdev_hashtype)
+{
+ switch (netdev_hashtype) {
+ case NETDEV_LAG_HASH_L2:
+ return BOND_HASH_L2;
+ case NETDEV_LAG_HASH_L34:
+ return BOND_HASH_L34;
+ case NETDEV_LAG_HASH_L23:
+ return BOND_HASH_L23;
+ default:
+ WARN(true, "Invalid hash type %d!\n", netdev_hashtype);
+ return BOND_HASH_L2;
+ }
+}
+
+int hns_roce_cmd_bond(struct hns_roce_bond_group *bond_grp,
+ enum hns_roce_bond_cmd_type bond_type)
+{
+ enum hns_roce_opcode_type opcode = get_bond_opcode(bond_type);
+ struct hns_roce_bond_info *slave_info;
+ struct hns_roce_cmq_desc desc = {};
+ int ret;
+
+ slave_info = (struct hns_roce_bond_info *)desc.data;
+ hns_roce_cmq_setup_basic_desc(&desc, opcode, false);
+
+ slave_info->bond_id = cpu_to_le32(bond_grp->bond_id);
+ if (bond_type == HNS_ROCE_CLEAR_BOND)
+ goto out;
+
+ if (bond_grp->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) {
+ slave_info->bond_mode = cpu_to_le32(BOND_MODE_1);
+ if (bond_grp->active_slave_num != 1)
+ ibdev_warn(&bond_grp->main_hr_dev->ib_dev,
+ "active slave cnt(%u) in Mode 1 is invalid.\n",
+ bond_grp->active_slave_num);
+ } else {
+ slave_info->bond_mode = cpu_to_le32(BOND_MODE_2_4);
+ slave_info->hash_policy =
+ cpu_to_le32(get_bond_hashtype(bond_grp->hash_type));
+ }
+
+ slave_info->active_slave_cnt = cpu_to_le32(bond_grp->active_slave_num);
+ slave_info->active_slave_mask = cpu_to_le32(bond_grp->active_slave_map);
+ slave_info->slave_mask = cpu_to_le32(bond_grp->slave_map);
+
+out:
+ ret = hns_roce_cmq_send(bond_grp->main_hr_dev, &desc, 1);
+ if (ret)
+ ibdev_err(&bond_grp->main_hr_dev->ib_dev,
+ "cmq bond type(%d) failed, ret = %d.\n",
+ bond_type, ret);
+
+ return ret;
+}
+
static int config_hem_ba_to_hw(struct hns_roce_dev *hr_dev,
dma_addr_t base_addr, u8 cmd, unsigned long tag)
{
@@ -2270,6 +2350,9 @@ static int hns_roce_query_caps(struct hns_roce_dev *hr_dev)
caps->flags |= le16_to_cpu(resp_d->cap_flags_ex) <<
HNS_ROCE_CAP_FLAGS_EX_SHIFT;
+ if (hr_dev->is_vf)
+ caps->flags &= ~HNS_ROCE_CAP_FLAG_BOND;
+
caps->num_cqs = 1 << hr_reg_read(resp_c, PF_CAPS_C_NUM_CQS);
caps->gid_table_len[0] = hr_reg_read(resp_c, PF_CAPS_C_MAX_GID);
caps->max_cqes = 1 << hr_reg_read(resp_c, PF_CAPS_C_CQ_DEPTH);
@@ -7048,7 +7131,6 @@ static int __hns_roce_hw_v2_init_instance(struct hnae3_handle *handle)
goto error_failed_roce_init;
}
-
handle->priv = hr_dev;
return 0;
@@ -7063,7 +7145,7 @@ error_failed_kzalloc:
}
static void __hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle,
- bool reset)
+ bool reset, bool bond_cleanup)
{
struct hns_roce_dev *hr_dev = handle->priv;
@@ -7075,7 +7157,7 @@ static void __hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle,
hr_dev->state = HNS_ROCE_DEVICE_STATE_UNINIT;
hns_roce_handle_device_err(hr_dev);
- hns_roce_exit(hr_dev);
+ hns_roce_exit(hr_dev, bond_cleanup);
kfree(hr_dev->priv);
ib_dealloc_device(&hr_dev->ib_dev);
}
@@ -7126,12 +7208,51 @@ reset_chk_err:
static void hns_roce_hw_v2_uninit_instance(struct hnae3_handle *handle,
bool reset)
{
+ /* Suspend bond to avoid concurrency */
+ hns_roce_bond_suspend(handle);
+
if (handle->rinfo.instance_state != HNS_ROCE_STATE_INITED)
- return;
+ goto out;
handle->rinfo.instance_state = HNS_ROCE_STATE_UNINIT;
- __hns_roce_hw_v2_uninit_instance(handle, reset);
+ __hns_roce_hw_v2_uninit_instance(handle, reset, true);
+
+ handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT;
+
+out:
+ hns_roce_bond_resume(handle);
+}
+
+struct hns_roce_dev
+ *hns_roce_bond_init_client(struct hns_roce_bond_group *bond_grp,
+ int func_idx)
+{
+ struct hnae3_handle *handle;
+ int ret;
+
+ handle = bond_grp->bond_func_info[func_idx].handle;
+ if (!handle || !handle->client)
+ return NULL;
+
+ ret = hns_roce_hw_v2_init_instance(handle);
+ if (ret)
+ return NULL;
+
+ return handle->priv;
+}
+
+void hns_roce_bond_uninit_client(struct hns_roce_bond_group *bond_grp,
+ int func_idx)
+{
+ struct hnae3_handle *handle = bond_grp->bond_func_info[func_idx].handle;
+
+ if (handle->rinfo.instance_state != HNS_ROCE_STATE_INITED)
+ return;
+
+ handle->rinfo.instance_state = HNS_ROCE_STATE_BOND_UNINIT;
+
+ __hns_roce_hw_v2_uninit_instance(handle, false, false);
handle->rinfo.instance_state = HNS_ROCE_STATE_NON_INIT;
}
@@ -7140,6 +7261,9 @@ static int hns_roce_hw_v2_reset_notify_down(struct hnae3_handle *handle)
{
struct hns_roce_dev *hr_dev;
+ /* Suspend bond to avoid concurrency */
+ hns_roce_bond_suspend(handle);
+
if (handle->rinfo.instance_state != HNS_ROCE_STATE_INITED) {
set_bit(HNS_ROCE_RST_DIRECT_RETURN, &handle->rinfo.state);
return 0;
@@ -7170,6 +7294,7 @@ static int hns_roce_hw_v2_reset_notify_init(struct hnae3_handle *handle)
if (test_and_clear_bit(HNS_ROCE_RST_DIRECT_RETURN,
&handle->rinfo.state)) {
handle->rinfo.reset_state = HNS_ROCE_STATE_RST_INITED;
+ hns_roce_bond_resume(handle);
return 0;
}
@@ -7189,6 +7314,7 @@ static int hns_roce_hw_v2_reset_notify_init(struct hnae3_handle *handle)
dev_info(dev, "reset done, RoCE client reinit finished.\n");
}
+ hns_roce_bond_resume(handle);
return ret;
}
@@ -7200,7 +7326,7 @@ static int hns_roce_hw_v2_reset_notify_uninit(struct hnae3_handle *handle)
handle->rinfo.reset_state = HNS_ROCE_STATE_RST_UNINIT;
dev_info(&handle->pdev->dev, "In reset process RoCE client uninit.\n");
msleep(HNS_ROCE_V2_HW_RST_UNINT_DELAY);
- __hns_roce_hw_v2_uninit_instance(handle, false);
+ __hns_roce_hw_v2_uninit_instance(handle, false, false);
return 0;
}
@@ -7236,6 +7362,14 @@ static void hns_roce_hw_v2_link_status_change(struct hnae3_handle *handle,
if (linkup || !hr_dev)
return;
+ /* For bond device, the link status depends on the upper netdev,
+ * and the upper device's link status depends on all the slaves'
+ * netdev but not only one. So bond device cannot get a correct
+ * link status from this path.
+ */
+ if (hns_roce_get_bond_grp(netdev, get_hr_bus_num(hr_dev)))
+ return;
+
ib_dispatch_port_state_event(&hr_dev->ib_dev, netdev);
}
@@ -7260,6 +7394,7 @@ static int __init hns_roce_hw_v2_init(void)
static void __exit hns_roce_hw_v2_exit(void)
{
+ hns_roce_dealloc_bond_grp();
hnae3_unregister_client(&hns_roce_hw_v2_client);
hns_roce_cleanup_debugfs();
}
diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
index e64a04d6f85b..285fe0875fac 100644
--- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
+++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h
@@ -35,6 +35,7 @@
#include <linux/bitops.h>
#include "hnae3.h"
+#include "hns_roce_bond.h"
#define HNS_ROCE_V2_MAX_RC_INL_INN_SZ 32
#define HNS_ROCE_V2_MTT_ENTRY_SZ 64
@@ -228,6 +229,9 @@ enum hns_roce_opcode_type {
HNS_ROCE_OPC_CFG_GMV_BT = 0x8510,
HNS_ROCE_QUERY_RAM_ECC = 0x8513,
HNS_SWITCH_PARAMETER_CFG = 0x1033,
+ HNS_ROCE_OPC_SET_BOND_INFO = 0x8601,
+ HNS_ROCE_OPC_CLEAR_BOND_INFO = 0x8602,
+ HNS_ROCE_OPC_CHANGE_ACTIVE_PORT = 0x8603,
};
#define HNS_ROCE_OPC_POST_MB_TIMEOUT 35000
@@ -1465,7 +1469,23 @@ struct hns_roce_sccc_clr_done {
__le32 rsv[5];
};
+struct hns_roce_bond_info {
+ __le32 bond_id;
+ __le32 bond_mode;
+ __le32 active_slave_cnt;
+ __le32 active_slave_mask;
+ __le32 slave_mask;
+ __le32 hash_policy;
+};
+
+struct hns_roce_dev
+ *hns_roce_bond_init_client(struct hns_roce_bond_group *bond_grp,
+ int func_idx);
+void hns_roce_bond_uninit_client(struct hns_roce_bond_group *bond_grp,
+ int func_idx);
int hns_roce_v2_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata);
+int hns_roce_cmd_bond(struct hns_roce_bond_group *bond_grp,
+ enum hns_roce_bond_cmd_type bond_type);
static inline void hns_roce_write64(struct hns_roce_dev *hr_dev, __le32 val[2],
void __iomem *dest)
diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c
index d50f36f8a110..2f4864ab7d4e 100644
--- a/drivers/infiniband/hw/hns/hns_roce_main.c
+++ b/drivers/infiniband/hw/hns/hns_roce_main.c
@@ -32,7 +32,6 @@
*/
#include <linux/acpi.h>
#include <linux/module.h>
-#include <linux/pci.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_smi.h>
#include <rdma/ib_user_verbs.h>
@@ -41,6 +40,7 @@
#include "hns_roce_device.h"
#include "hns_roce_hem.h"
#include "hns_roce_hw_v2.h"
+#include "hns_roce_bond.h"
static int hns_roce_set_mac(struct hns_roce_dev *hr_dev, u32 port,
const u8 *addr)
@@ -89,30 +89,75 @@ static int hns_roce_del_gid(const struct ib_gid_attr *attr, void **context)
return ret;
}
-static int handle_en_event(struct hns_roce_dev *hr_dev, u32 port,
- unsigned long event)
+static int hns_roce_get_port_state(struct hns_roce_dev *hr_dev, u32 port_num,
+ enum ib_port_state *state)
{
+ struct hns_roce_bond_group *bond_grp;
+ u8 bus_num = get_hr_bus_num(hr_dev);
+ struct net_device *net_dev;
+
+ net_dev = ib_device_get_netdev(&hr_dev->ib_dev, port_num);
+ if (!net_dev)
+ return -ENODEV;
+
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_BOND) {
+ bond_grp = hns_roce_get_bond_grp(net_dev, bus_num);
+ if (bond_grp) {
+ *state = ib_get_curr_port_state(bond_grp->upper_dev);
+ goto out;
+ }
+ }
+
+ *state = ib_get_curr_port_state(net_dev);
+out:
+ dev_put(net_dev);
+ return 0;
+}
+
+static int handle_en_event(struct net_device *netdev,
+ struct hns_roce_dev *hr_dev,
+ u32 port, unsigned long event)
+{
+ struct ib_device *ibdev = &hr_dev->ib_dev;
struct device *dev = hr_dev->dev;
- struct net_device *netdev;
+ enum ib_port_state curr_state;
+ struct ib_event ibevent;
int ret = 0;
- netdev = hr_dev->iboe.netdevs[port];
if (!netdev) {
dev_err(dev, "can't find netdev on port(%u)!\n", port);
return -ENODEV;
}
switch (event) {
- case NETDEV_UP:
- case NETDEV_CHANGE:
case NETDEV_REGISTER:
case NETDEV_CHANGEADDR:
ret = hns_roce_set_mac(hr_dev, port, netdev->dev_addr);
break;
+ case NETDEV_UP:
+ case NETDEV_CHANGE:
+ ret = hns_roce_set_mac(hr_dev, port, netdev->dev_addr);
+ if (ret)
+ return ret;
+ fallthrough;
case NETDEV_DOWN:
- /*
- * In v1 engine, only support all ports closed together.
- */
+ if (!netif_is_lag_master(netdev))
+ break;
+ curr_state = ib_get_curr_port_state(netdev);
+
+ write_lock_irq(&ibdev->cache_lock);
+ if (ibdev->port_data[port].cache.last_port_state == curr_state) {
+ write_unlock_irq(&ibdev->cache_lock);
+ return 0;
+ }
+ ibdev->port_data[port].cache.last_port_state = curr_state;
+ write_unlock_irq(&ibdev->cache_lock);
+
+ ibevent.event = (curr_state == IB_PORT_DOWN) ?
+ IB_EVENT_PORT_ERR : IB_EVENT_PORT_ACTIVE;
+ ibevent.device = ibdev;
+ ibevent.element.port_num = port + 1;
+ ib_dispatch_event(&ibevent);
break;
default:
dev_dbg(dev, "NETDEV event = 0x%x!\n", (u32)(event));
@@ -126,17 +171,25 @@ static int hns_roce_netdev_event(struct notifier_block *self,
unsigned long event, void *ptr)
{
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct hns_roce_bond_group *bond_grp;
struct hns_roce_ib_iboe *iboe = NULL;
struct hns_roce_dev *hr_dev = NULL;
+ struct net_device *upper = NULL;
int ret;
u32 port;
hr_dev = container_of(self, struct hns_roce_dev, iboe.nb);
iboe = &hr_dev->iboe;
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_BOND) {
+ bond_grp = hns_roce_get_bond_grp(get_hr_netdev(hr_dev, 0),
+ get_hr_bus_num(hr_dev));
+ upper = bond_grp ? bond_grp->upper_dev : NULL;
+ }
for (port = 0; port < hr_dev->caps.num_ports; port++) {
- if (dev == iboe->netdevs[port]) {
- ret = handle_en_event(hr_dev, port, event);
+ if ((!upper && dev == iboe->netdevs[port]) ||
+ (upper && dev == upper)) {
+ ret = handle_en_event(dev, hr_dev, port, event);
if (ret)
return NOTIFY_DONE;
break;
@@ -148,12 +201,13 @@ static int hns_roce_netdev_event(struct notifier_block *self,
static int hns_roce_setup_mtu_mac(struct hns_roce_dev *hr_dev)
{
+ struct net_device *net_dev;
int ret;
u8 i;
for (i = 0; i < hr_dev->caps.num_ports; i++) {
- ret = hns_roce_set_mac(hr_dev, i,
- hr_dev->iboe.netdevs[i]->dev_addr);
+ net_dev = get_hr_netdev(hr_dev, i);
+ ret = hns_roce_set_mac(hr_dev, i, net_dev->dev_addr);
if (ret)
return ret;
}
@@ -221,9 +275,7 @@ static int hns_roce_query_port(struct ib_device *ib_dev, u32 port_num,
struct ib_port_attr *props)
{
struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
- struct device *dev = hr_dev->dev;
struct net_device *net_dev;
- unsigned long flags;
enum ib_mtu mtu;
u32 port;
int ret;
@@ -244,26 +296,26 @@ static int hns_roce_query_port(struct ib_device *ib_dev, u32 port_num,
if (ret)
ibdev_warn(ib_dev, "failed to get speed, ret = %d.\n", ret);
- spin_lock_irqsave(&hr_dev->iboe.lock, flags);
-
- net_dev = hr_dev->iboe.netdevs[port];
+ net_dev = ib_device_get_netdev(ib_dev, port_num);
if (!net_dev) {
- spin_unlock_irqrestore(&hr_dev->iboe.lock, flags);
- dev_err(dev, "find netdev %u failed!\n", port);
+ ibdev_err(ib_dev, "find netdev %u failed!\n", port);
return -EINVAL;
}
mtu = iboe_get_mtu(net_dev->mtu);
props->active_mtu = mtu ? min(props->max_mtu, mtu) : IB_MTU_256;
- props->state = netif_running(net_dev) && netif_carrier_ok(net_dev) ?
- IB_PORT_ACTIVE :
- IB_PORT_DOWN;
+
+ dev_put(net_dev);
+
+ ret = hns_roce_get_port_state(hr_dev, port_num, &props->state);
+ if (ret) {
+ ibdev_err(ib_dev, "failed to get port state.\n");
+ return ret;
+ }
+
props->phys_state = props->state == IB_PORT_ACTIVE ?
IB_PORT_PHYS_STATE_LINK_UP :
IB_PORT_PHYS_STATE_DISABLED;
-
- spin_unlock_irqrestore(&hr_dev->iboe.lock, flags);
-
return 0;
}
@@ -425,6 +477,8 @@ static int hns_roce_alloc_ucontext(struct ib_ucontext *uctx,
if (ret)
goto error_fail_copy_to_udata;
+ hns_roce_get_cq_bankid_for_uctx(context);
+
return 0;
error_fail_copy_to_udata:
@@ -447,6 +501,8 @@ static void hns_roce_dealloc_ucontext(struct ib_ucontext *ibcontext)
struct hns_roce_ucontext *context = to_hr_ucontext(ibcontext);
struct hns_roce_dev *hr_dev = to_hr_dev(ibcontext->device);
+ hns_roce_put_cq_bankid_for_uctx(context);
+
if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_CQ_RECORD_DB ||
hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_RECORD_DB)
mutex_destroy(&context->page_mutex);
@@ -613,9 +669,40 @@ static int hns_roce_get_hw_stats(struct ib_device *device,
return num_counters;
}
-static void hns_roce_unregister_device(struct hns_roce_dev *hr_dev)
+static void
+ hns_roce_unregister_bond_cleanup(struct hns_roce_dev *hr_dev,
+ struct hns_roce_bond_group *bond_grp)
+{
+ struct net_device *net_dev;
+ int i;
+
+ /* To avoid the loss of other slave devices when main_hr_dev
+ * is unregistered, re-initialize the remaining slaves before
+ * the bond resources cleanup.
+ */
+ bond_grp->bond_state = HNS_ROCE_BOND_NOT_BONDED;
+ for (i = 0; i < ROCE_BOND_FUNC_MAX; i++) {
+ net_dev = bond_grp->bond_func_info[i].net_dev;
+ if (net_dev && net_dev != get_hr_netdev(hr_dev, 0))
+ hns_roce_bond_init_client(bond_grp, i);
+ }
+
+ hns_roce_cleanup_bond(bond_grp);
+}
+
+static void hns_roce_unregister_device(struct hns_roce_dev *hr_dev,
+ bool bond_cleanup)
{
+ struct net_device *net_dev = get_hr_netdev(hr_dev, 0);
struct hns_roce_ib_iboe *iboe = &hr_dev->iboe;
+ struct hns_roce_bond_group *bond_grp;
+ u8 bus_num = get_hr_bus_num(hr_dev);
+
+ if (bond_cleanup && hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_BOND) {
+ bond_grp = hns_roce_get_bond_grp(net_dev, bus_num);
+ if (bond_grp)
+ hns_roce_unregister_bond_cleanup(hr_dev, bond_grp);
+ }
hr_dev->active = false;
unregister_netdevice_notifier(&iboe->nb);
@@ -704,11 +791,12 @@ static const struct ib_device_ops hns_roce_dev_restrack_ops = {
static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
{
- int ret;
struct hns_roce_ib_iboe *iboe = NULL;
- struct ib_device *ib_dev = NULL;
struct device *dev = hr_dev->dev;
+ struct ib_device *ib_dev = NULL;
+ struct net_device *net_dev;
unsigned int i;
+ int ret;
iboe = &hr_dev->iboe;
spin_lock_init(&iboe->lock);
@@ -743,17 +831,38 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
ib_set_device_ops(ib_dev, hr_dev->hw->hns_roce_dev_ops);
ib_set_device_ops(ib_dev, &hns_roce_dev_ops);
ib_set_device_ops(ib_dev, &hns_roce_dev_restrack_ops);
- for (i = 0; i < hr_dev->caps.num_ports; i++) {
- if (!hr_dev->iboe.netdevs[i])
- continue;
- ret = ib_device_set_netdev(ib_dev, hr_dev->iboe.netdevs[i],
- i + 1);
- if (ret)
+ dma_set_max_seg_size(dev, SZ_2G);
+
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_BOND) {
+ ret = hns_roce_alloc_bond_grp(hr_dev);
+ if (ret) {
+ dev_err(dev, "failed to alloc bond_grp for bus %u, ret = %d\n",
+ get_hr_bus_num(hr_dev), ret);
+ return ret;
+ }
+ }
+
+ if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_BOND &&
+ hns_roce_bond_is_active(hr_dev)) {
+ ret = hns_roce_bond_init(hr_dev);
+ if (ret) {
+ dev_err(dev, "failed to init bond!\n");
return ret;
+ }
+ ret = ib_register_device(ib_dev, "hns_bond_%d", dev);
+ } else {
+ for (i = 0; i < hr_dev->caps.num_ports; i++) {
+ net_dev = get_hr_netdev(hr_dev, i);
+ if (!net_dev)
+ continue;
+
+ ret = ib_device_set_netdev(ib_dev, net_dev, i + 1);
+ if (ret)
+ return ret;
+ }
+ ret = ib_register_device(ib_dev, "hns_%d", dev);
}
- dma_set_max_seg_size(dev, SZ_2G);
- ret = ib_register_device(ib_dev, "hns_%d", dev);
if (ret) {
dev_err(dev, "ib_register_device failed!\n");
return ret;
@@ -1153,10 +1262,10 @@ error_failed_alloc_dfx_cnt:
return ret;
}
-void hns_roce_exit(struct hns_roce_dev *hr_dev)
+void hns_roce_exit(struct hns_roce_dev *hr_dev, bool bond_cleanup)
{
hns_roce_unregister_debugfs(hr_dev);
- hns_roce_unregister_device(hr_dev);
+ hns_roce_unregister_device(hr_dev, bond_cleanup);
if (hr_dev->hw->hw_exit)
hr_dev->hw->hw_exit(hr_dev);
diff --git a/drivers/infiniband/hw/hns/hns_roce_pd.c b/drivers/infiniband/hw/hns/hns_roce_pd.c
index d35cf59d0f43..225c3e328e0e 100644
--- a/drivers/infiniband/hw/hns/hns_roce_pd.c
+++ b/drivers/infiniband/hw/hns/hns_roce_pd.c
@@ -30,7 +30,6 @@
* SOFTWARE.
*/
-#include <linux/pci.h>
#include "hns_roce_device.h"
void hns_roce_init_pd_table(struct hns_roce_dev *hr_dev)
diff --git a/drivers/infiniband/hw/hns/hns_roce_qp.c b/drivers/infiniband/hw/hns/hns_roce_qp.c
index 6ff1b8ce580c..d1640c5fbaab 100644
--- a/drivers/infiniband/hw/hns/hns_roce_qp.c
+++ b/drivers/infiniband/hw/hns/hns_roce_qp.c
@@ -31,7 +31,6 @@
* SOFTWARE.
*/
-#include <linux/pci.h>
#include <rdma/ib_addr.h>
#include <rdma/ib_umem.h>
#include <rdma/uverbs_ioctl.h>
@@ -662,7 +661,6 @@ static int set_user_sq_size(struct hns_roce_dev *hr_dev,
hr_qp->sq.wqe_shift = ucmd->log_sq_stride;
hr_qp->sq.wqe_cnt = cnt;
- cap->max_send_sge = hr_qp->sq.max_gs;
return 0;
}
@@ -744,7 +742,6 @@ static int set_kernel_sq_size(struct hns_roce_dev *hr_dev,
/* sync the parameters of kernel QP to user's configuration */
cap->max_send_wr = cnt;
- cap->max_send_sge = hr_qp->sq.max_gs;
return 0;
}
@@ -1350,11 +1347,13 @@ static int check_mtu_validate(struct hns_roce_dev *hr_dev,
struct hns_roce_qp *hr_qp,
struct ib_qp_attr *attr, int attr_mask)
{
+ struct net_device *net_dev;
enum ib_mtu active_mtu;
int p;
p = attr_mask & IB_QP_PORT ? (attr->port_num - 1) : hr_qp->port;
- active_mtu = iboe_get_mtu(hr_dev->iboe.netdevs[p]->mtu);
+ net_dev = get_hr_netdev(hr_dev, p);
+ active_mtu = iboe_get_mtu(net_dev->mtu);
if ((hr_dev->caps.max_mtu >= IB_MTU_2048 &&
attr->path_mtu > hr_dev->caps.max_mtu) ||
diff --git a/drivers/infiniband/hw/hns/hns_roce_srq.c b/drivers/infiniband/hw/hns/hns_roce_srq.c
index 1090051f493b..8a6efb6b9c9e 100644
--- a/drivers/infiniband/hw/hns/hns_roce_srq.c
+++ b/drivers/infiniband/hw/hns/hns_roce_srq.c
@@ -3,7 +3,6 @@
* Copyright (c) 2018 Hisilicon Limited.
*/
-#include <linux/pci.h>
#include <rdma/ib_umem.h>
#include <rdma/uverbs_ioctl.h>
#include "hns_roce_device.h"
diff --git a/drivers/infiniband/hw/irdma/cm.c b/drivers/infiniband/hw/irdma/cm.c
index c6a0a661d6e7..f4f4f92ba63a 100644
--- a/drivers/infiniband/hw/irdma/cm.c
+++ b/drivers/infiniband/hw/irdma/cm.c
@@ -3710,7 +3710,7 @@ int irdma_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
iwpd = iwqp->iwpd;
tagged_offset = (uintptr_t)iwqp->ietf_mem.va;
ibmr = irdma_reg_phys_mr(&iwpd->ibpd, iwqp->ietf_mem.pa, buf_len,
- IB_ACCESS_LOCAL_WRITE, &tagged_offset);
+ IB_ACCESS_LOCAL_WRITE, &tagged_offset, false);
if (IS_ERR(ibmr)) {
ret = -ENOMEM;
goto error;
diff --git a/drivers/infiniband/hw/irdma/ctrl.c b/drivers/infiniband/hw/irdma/ctrl.c
index 4ef1c29032f7..ce5cf89c463c 100644
--- a/drivers/infiniband/hw/irdma/ctrl.c
+++ b/drivers/infiniband/hw/irdma/ctrl.c
@@ -2943,8 +2943,6 @@ static int irdma_sc_cq_create(struct irdma_sc_cq *cq, u64 scratch,
__le64 *wqe;
struct irdma_sc_cqp *cqp;
u64 hdr;
- struct irdma_sc_ceq *ceq;
- int ret_code = 0;
cqp = cq->dev->cqp;
if (cq->cq_uk.cq_id >= cqp->dev->hmc_info->hmc_obj[IRDMA_HMC_IW_CQ].max_cnt)
@@ -2953,19 +2951,9 @@ static int irdma_sc_cq_create(struct irdma_sc_cq *cq, u64 scratch,
if (cq->ceq_id >= cq->dev->hmc_fpm_misc.max_ceqs)
return -EINVAL;
- ceq = cq->dev->ceq[cq->ceq_id];
- if (ceq && ceq->reg_cq)
- ret_code = irdma_sc_add_cq_ctx(ceq, cq);
-
- if (ret_code)
- return ret_code;
-
wqe = irdma_sc_cqp_get_next_send_wqe(cqp, scratch);
- if (!wqe) {
- if (ceq && ceq->reg_cq)
- irdma_sc_remove_cq_ctx(ceq, cq);
+ if (!wqe)
return -ENOMEM;
- }
set_64bit_val(wqe, 0, cq->cq_uk.cq_size);
set_64bit_val(wqe, 8, (uintptr_t)cq >> 1);
@@ -3018,17 +3006,12 @@ int irdma_sc_cq_destroy(struct irdma_sc_cq *cq, u64 scratch, bool post_sq)
struct irdma_sc_cqp *cqp;
__le64 *wqe;
u64 hdr;
- struct irdma_sc_ceq *ceq;
cqp = cq->dev->cqp;
wqe = irdma_sc_cqp_get_next_send_wqe(cqp, scratch);
if (!wqe)
return -ENOMEM;
- ceq = cq->dev->ceq[cq->ceq_id];
- if (ceq && ceq->reg_cq)
- irdma_sc_remove_cq_ctx(ceq, cq);
-
set_64bit_val(wqe, 0, cq->cq_uk.cq_size);
set_64bit_val(wqe, 8, (uintptr_t)cq >> 1);
set_64bit_val(wqe, 40, cq->shadow_area_pa);
@@ -3602,71 +3585,6 @@ static int irdma_sc_parse_fpm_query_buf(struct irdma_sc_dev *dev, __le64 *buf,
}
/**
- * irdma_sc_find_reg_cq - find cq ctx index
- * @ceq: ceq sc structure
- * @cq: cq sc structure
- */
-static u32 irdma_sc_find_reg_cq(struct irdma_sc_ceq *ceq,
- struct irdma_sc_cq *cq)
-{
- u32 i;
-
- for (i = 0; i < ceq->reg_cq_size; i++) {
- if (cq == ceq->reg_cq[i])
- return i;
- }
-
- return IRDMA_INVALID_CQ_IDX;
-}
-
-/**
- * irdma_sc_add_cq_ctx - add cq ctx tracking for ceq
- * @ceq: ceq sc structure
- * @cq: cq sc structure
- */
-int irdma_sc_add_cq_ctx(struct irdma_sc_ceq *ceq, struct irdma_sc_cq *cq)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ceq->req_cq_lock, flags);
-
- if (ceq->reg_cq_size == ceq->elem_cnt) {
- spin_unlock_irqrestore(&ceq->req_cq_lock, flags);
- return -ENOMEM;
- }
-
- ceq->reg_cq[ceq->reg_cq_size++] = cq;
-
- spin_unlock_irqrestore(&ceq->req_cq_lock, flags);
-
- return 0;
-}
-
-/**
- * irdma_sc_remove_cq_ctx - remove cq ctx tracking for ceq
- * @ceq: ceq sc structure
- * @cq: cq sc structure
- */
-void irdma_sc_remove_cq_ctx(struct irdma_sc_ceq *ceq, struct irdma_sc_cq *cq)
-{
- unsigned long flags;
- u32 cq_ctx_idx;
-
- spin_lock_irqsave(&ceq->req_cq_lock, flags);
- cq_ctx_idx = irdma_sc_find_reg_cq(ceq, cq);
- if (cq_ctx_idx == IRDMA_INVALID_CQ_IDX)
- goto exit;
-
- ceq->reg_cq_size--;
- if (cq_ctx_idx != ceq->reg_cq_size)
- ceq->reg_cq[cq_ctx_idx] = ceq->reg_cq[ceq->reg_cq_size];
- ceq->reg_cq[ceq->reg_cq_size] = NULL;
-
-exit:
- spin_unlock_irqrestore(&ceq->req_cq_lock, flags);
-}
-
-/**
* irdma_sc_cqp_init - Initialize buffers for a control Queue Pair
* @cqp: IWARP control queue pair pointer
* @info: IWARP control queue pair init info pointer
@@ -3950,11 +3868,13 @@ int irdma_sc_cqp_destroy(struct irdma_sc_cqp *cqp)
*/
void irdma_sc_ccq_arm(struct irdma_sc_cq *ccq)
{
+ unsigned long flags;
u64 temp_val;
u16 sw_cq_sel;
u8 arm_next_se;
u8 arm_seq_num;
+ spin_lock_irqsave(&ccq->dev->cqp_lock, flags);
get_64bit_val(ccq->cq_uk.shadow_area, 32, &temp_val);
sw_cq_sel = (u16)FIELD_GET(IRDMA_CQ_DBSA_SW_CQ_SELECT, temp_val);
arm_next_se = (u8)FIELD_GET(IRDMA_CQ_DBSA_ARM_NEXT_SE, temp_val);
@@ -3965,6 +3885,7 @@ void irdma_sc_ccq_arm(struct irdma_sc_cq *ccq)
FIELD_PREP(IRDMA_CQ_DBSA_ARM_NEXT_SE, arm_next_se) |
FIELD_PREP(IRDMA_CQ_DBSA_ARM_NEXT, 1);
set_64bit_val(ccq->cq_uk.shadow_area, 32, temp_val);
+ spin_unlock_irqrestore(&ccq->dev->cqp_lock, flags);
dma_wmb(); /* make sure shadow area is updated before arming */
@@ -4387,9 +4308,6 @@ int irdma_sc_ceq_init(struct irdma_sc_ceq *ceq,
ceq->ceq_elem_pa = info->ceqe_pa;
ceq->virtual_map = info->virtual_map;
ceq->itr_no_expire = info->itr_no_expire;
- ceq->reg_cq = info->reg_cq;
- ceq->reg_cq_size = 0;
- spin_lock_init(&ceq->req_cq_lock);
ceq->pbl_chunk_size = (ceq->virtual_map ? info->pbl_chunk_size : 0);
ceq->first_pm_pbl_idx = (ceq->virtual_map ? info->first_pm_pbl_idx : 0);
ceq->pbl_list = (ceq->virtual_map ? info->pbl_list : NULL);
@@ -4472,9 +4390,6 @@ int irdma_sc_cceq_destroy_done(struct irdma_sc_ceq *ceq)
{
struct irdma_sc_cqp *cqp;
- if (ceq->reg_cq)
- irdma_sc_remove_cq_ctx(ceq, ceq->dev->ccq);
-
cqp = ceq->dev->cqp;
cqp->process_cqp_sds = irdma_update_sds_noccq;
@@ -4493,11 +4408,6 @@ int irdma_sc_cceq_create(struct irdma_sc_ceq *ceq, u64 scratch)
struct irdma_sc_dev *dev = ceq->dev;
dev->ccq->vsi_idx = ceq->vsi_idx;
- if (ceq->reg_cq) {
- ret_code = irdma_sc_add_cq_ctx(ceq, ceq->dev->ccq);
- if (ret_code)
- return ret_code;
- }
ret_code = irdma_sc_ceq_create(ceq, scratch, true);
if (!ret_code)
@@ -4562,7 +4472,6 @@ void *irdma_sc_process_ceq(struct irdma_sc_dev *dev, struct irdma_sc_ceq *ceq)
struct irdma_sc_cq *temp_cq;
u8 polarity;
u32 cq_idx;
- unsigned long flags;
do {
cq_idx = 0;
@@ -4583,11 +4492,6 @@ void *irdma_sc_process_ceq(struct irdma_sc_dev *dev, struct irdma_sc_ceq *ceq)
}
cq = temp_cq;
- if (ceq->reg_cq) {
- spin_lock_irqsave(&ceq->req_cq_lock, flags);
- cq_idx = irdma_sc_find_reg_cq(ceq, cq);
- spin_unlock_irqrestore(&ceq->req_cq_lock, flags);
- }
IRDMA_RING_MOVE_TAIL(ceq->ceq_ring);
if (!IRDMA_RING_CURRENT_TAIL(ceq->ceq_ring))
@@ -4731,7 +4635,8 @@ static int irdma_sc_aeq_destroy(struct irdma_sc_aeq *aeq, u64 scratch,
u64 hdr;
dev = aeq->dev;
- if (dev->privileged)
+
+ if (dev->hw_attrs.uk_attrs.hw_rev <= IRDMA_GEN_2)
writel(0, dev->hw_regs[IRDMA_PFINT_AEQCTL]);
cqp = dev->cqp;
diff --git a/drivers/infiniband/hw/irdma/hw.c b/drivers/infiniband/hw/irdma/hw.c
index 7bad0e38786a..d1fc5726b979 100644
--- a/drivers/infiniband/hw/irdma/hw.c
+++ b/drivers/infiniband/hw/irdma/hw.c
@@ -2365,7 +2365,6 @@ static int irdma_cqp_manage_apbvt_cmd(struct irdma_device *iwdev,
cqp_info = &cqp_request->info;
info = &cqp_info->in.u.manage_apbvt_entry.info;
- memset(info, 0, sizeof(*info));
info->add = add_port;
info->port = accel_local_port;
cqp_info->cqp_cmd = IRDMA_OP_MANAGE_APBVT_ENTRY;
@@ -2474,7 +2473,6 @@ void irdma_manage_arp_cache(struct irdma_pci_f *rf,
if (action == IRDMA_ARP_ADD) {
cqp_info->cqp_cmd = IRDMA_OP_ADD_ARP_CACHE_ENTRY;
info = &cqp_info->in.u.add_arp_cache_entry.info;
- memset(info, 0, sizeof(*info));
info->arp_index = (u16)arp_index;
info->permanent = true;
ether_addr_copy(info->mac_addr, mac_addr);
@@ -2533,7 +2531,6 @@ int irdma_manage_qhash(struct irdma_device *iwdev, struct irdma_cm_info *cminfo,
cqp_info = &cqp_request->info;
info = &cqp_info->in.u.manage_qhash_table_entry.info;
- memset(info, 0, sizeof(*info));
info->vsi = &iwdev->vsi;
info->manage = mtype;
info->entry_type = etype;
diff --git a/drivers/infiniband/hw/irdma/icrdma_if.c b/drivers/infiniband/hw/irdma/icrdma_if.c
index 27b191f61caf..b49fd9cf2476 100644
--- a/drivers/infiniband/hw/irdma/icrdma_if.c
+++ b/drivers/infiniband/hw/irdma/icrdma_if.c
@@ -302,7 +302,8 @@ err_rt_init:
err_ctrl_init:
icrdma_deinit_interrupts(rf, cdev_info);
err_init_interrupts:
- kfree(iwdev->rf);
+ mutex_destroy(&rf->ah_tbl_lock);
+ kfree(rf);
ib_dealloc_device(&iwdev->ibdev);
return err;
@@ -319,6 +320,9 @@ static void icrdma_remove(struct auxiliary_device *aux_dev)
ice_rdma_update_vsi_filter(cdev_info, iwdev->vsi_num, false);
irdma_ib_unregister_device(iwdev);
icrdma_deinit_interrupts(iwdev->rf, cdev_info);
+ mutex_destroy(&iwdev->rf->ah_tbl_lock);
+
+ kfree(iwdev->rf);
pr_debug("INIT: Gen[%d] func[%d] device remove success\n",
rdma_ver, PCI_FUNC(cdev_info->pdev->devfn));
diff --git a/drivers/infiniband/hw/irdma/ig3rdma_if.c b/drivers/infiniband/hw/irdma/ig3rdma_if.c
index 1bb42eb298ba..e1d6670d9396 100644
--- a/drivers/infiniband/hw/irdma/ig3rdma_if.c
+++ b/drivers/infiniband/hw/irdma/ig3rdma_if.c
@@ -55,6 +55,7 @@ static int ig3rdma_vchnl_init(struct irdma_pci_f *rf,
ret = irdma_sc_vchnl_init(&rf->sc_dev, &virt_info);
if (ret) {
destroy_workqueue(rf->vchnl_wq);
+ mutex_destroy(&rf->sc_dev.vchnl_mutex);
return ret;
}
@@ -124,7 +125,9 @@ static void ig3rdma_decfg_rf(struct irdma_pci_f *rf)
{
struct irdma_hw *hw = &rf->hw;
+ mutex_destroy(&rf->ah_tbl_lock);
destroy_workqueue(rf->vchnl_wq);
+ mutex_destroy(&rf->sc_dev.vchnl_mutex);
kfree(hw->io_regs);
iounmap(hw->rdma_reg.addr);
}
@@ -149,6 +152,7 @@ static int ig3rdma_cfg_rf(struct irdma_pci_f *rf,
err = ig3rdma_cfg_regions(&rf->hw, cdev_info);
if (err) {
destroy_workqueue(rf->vchnl_wq);
+ mutex_destroy(&rf->sc_dev.vchnl_mutex);
return err;
}
diff --git a/drivers/infiniband/hw/irdma/main.h b/drivers/infiniband/hw/irdma/main.h
index 886b30da188a..baab61e424a2 100644
--- a/drivers/infiniband/hw/irdma/main.h
+++ b/drivers/infiniband/hw/irdma/main.h
@@ -556,7 +556,7 @@ void irdma_copy_ip_htonl(__be32 *dst, u32 *src);
u16 irdma_get_vlan_ipv4(u32 *addr);
void irdma_get_vlan_mac_ipv6(u32 *addr, u16 *vlan_id, u8 *mac);
struct ib_mr *irdma_reg_phys_mr(struct ib_pd *ib_pd, u64 addr, u64 size,
- int acc, u64 *iova_start);
+ int acc, u64 *iova_start, bool dma_mr);
int irdma_upload_qp_context(struct irdma_qp *iwqp, bool freeze, bool raw);
void irdma_cqp_ce_handler(struct irdma_pci_f *rf, struct irdma_sc_cq *cq);
int irdma_ah_cqp_op(struct irdma_pci_f *rf, struct irdma_sc_ah *sc_ah, u8 cmd,
@@ -564,7 +564,6 @@ int irdma_ah_cqp_op(struct irdma_pci_f *rf, struct irdma_sc_ah *sc_ah, u8 cmd,
void (*callback_fcn)(struct irdma_cqp_request *cqp_request),
void *cb_param);
void irdma_gsi_ud_qp_ah_cb(struct irdma_cqp_request *cqp_request);
-bool irdma_cq_empty(struct irdma_cq *iwcq);
int irdma_inetaddr_event(struct notifier_block *notifier, unsigned long event,
void *ptr);
int irdma_inet6addr_event(struct notifier_block *notifier, unsigned long event,
diff --git a/drivers/infiniband/hw/irdma/pble.c b/drivers/infiniband/hw/irdma/pble.c
index 3091f9345f12..28dfad7f940c 100644
--- a/drivers/infiniband/hw/irdma/pble.c
+++ b/drivers/infiniband/hw/irdma/pble.c
@@ -71,7 +71,7 @@ int irdma_hmc_init_pble(struct irdma_sc_dev *dev,
static void get_sd_pd_idx(struct irdma_hmc_pble_rsrc *pble_rsrc,
struct sd_pd_idx *idx)
{
- idx->sd_idx = (u32)pble_rsrc->next_fpm_addr / IRDMA_HMC_DIRECT_BP_SIZE;
+ idx->sd_idx = pble_rsrc->next_fpm_addr / IRDMA_HMC_DIRECT_BP_SIZE;
idx->pd_idx = (u32)(pble_rsrc->next_fpm_addr / IRDMA_HMC_PAGED_BP_SIZE);
idx->rel_pd_idx = (idx->pd_idx % IRDMA_HMC_PD_CNT_IN_SD);
}
@@ -506,12 +506,14 @@ exit:
void irdma_free_pble(struct irdma_hmc_pble_rsrc *pble_rsrc,
struct irdma_pble_alloc *palloc)
{
- pble_rsrc->freedpbles += palloc->total_cnt;
-
if (palloc->level == PBLE_LEVEL_2)
free_lvl2(pble_rsrc, palloc);
else
irdma_prm_return_pbles(&pble_rsrc->pinfo,
&palloc->level1.chunkinfo);
+
+ mutex_lock(&pble_rsrc->pble_mutex_lock);
+ pble_rsrc->freedpbles += palloc->total_cnt;
pble_rsrc->stats_alloc_freed++;
+ mutex_unlock(&pble_rsrc->pble_mutex_lock);
}
diff --git a/drivers/infiniband/hw/irdma/puda.c b/drivers/infiniband/hw/irdma/puda.c
index 694e5a9ed15d..cee47ddbd1b5 100644
--- a/drivers/infiniband/hw/irdma/puda.c
+++ b/drivers/infiniband/hw/irdma/puda.c
@@ -685,7 +685,6 @@ static int irdma_puda_qp_create(struct irdma_puda_rsrc *rsrc)
ukqp->rq_size = rsrc->rq_size;
IRDMA_RING_INIT(ukqp->sq_ring, ukqp->sq_size);
- IRDMA_RING_INIT(ukqp->initial_ring, ukqp->sq_size);
IRDMA_RING_INIT(ukqp->rq_ring, ukqp->rq_size);
ukqp->wqe_alloc_db = qp->pd->dev->wqe_alloc_db;
@@ -726,7 +725,6 @@ static int irdma_puda_cq_wqe(struct irdma_sc_dev *dev, struct irdma_sc_cq *cq)
struct irdma_sc_cqp *cqp;
u64 hdr;
struct irdma_ccq_cqe_info compl_info;
- int status = 0;
cqp = dev->cqp;
wqe = irdma_sc_cqp_get_next_send_wqe(cqp, 0);
@@ -756,16 +754,8 @@ static int irdma_puda_cq_wqe(struct irdma_sc_dev *dev, struct irdma_sc_cq *cq)
print_hex_dump_debug("PUDA: PUDA CREATE CQ", DUMP_PREFIX_OFFSET, 16,
8, wqe, IRDMA_CQP_WQE_SIZE * 8, false);
irdma_sc_cqp_post_sq(dev->cqp);
- status = irdma_sc_poll_for_cqp_op_done(dev->cqp, IRDMA_CQP_OP_CREATE_CQ,
- &compl_info);
- if (!status) {
- struct irdma_sc_ceq *ceq = dev->ceq[0];
-
- if (ceq && ceq->reg_cq)
- status = irdma_sc_add_cq_ctx(ceq, cq);
- }
-
- return status;
+ return irdma_sc_poll_for_cqp_op_done(dev->cqp, IRDMA_CQP_OP_CREATE_CQ,
+ &compl_info);
}
/**
@@ -897,23 +887,17 @@ void irdma_puda_dele_rsrc(struct irdma_sc_vsi *vsi, enum puda_rsrc_type type,
struct irdma_puda_buf *buf = NULL;
struct irdma_puda_buf *nextbuf = NULL;
struct irdma_virt_mem *vmem;
- struct irdma_sc_ceq *ceq;
- ceq = vsi->dev->ceq[0];
switch (type) {
case IRDMA_PUDA_RSRC_TYPE_ILQ:
rsrc = vsi->ilq;
vmem = &vsi->ilq_mem;
vsi->ilq = NULL;
- if (ceq && ceq->reg_cq)
- irdma_sc_remove_cq_ctx(ceq, &rsrc->cq);
break;
case IRDMA_PUDA_RSRC_TYPE_IEQ:
rsrc = vsi->ieq;
vmem = &vsi->ieq_mem;
vsi->ieq = NULL;
- if (ceq && ceq->reg_cq)
- irdma_sc_remove_cq_ctx(ceq, &rsrc->cq);
break;
default:
ibdev_dbg(to_ibdev(dev), "PUDA: error resource type = 0x%x\n",
diff --git a/drivers/infiniband/hw/irdma/type.h b/drivers/infiniband/hw/irdma/type.h
index 4ae77cdde9dc..cab4896640a1 100644
--- a/drivers/infiniband/hw/irdma/type.h
+++ b/drivers/infiniband/hw/irdma/type.h
@@ -492,9 +492,6 @@ struct irdma_sc_ceq {
u32 first_pm_pbl_idx;
u8 polarity;
u16 vsi_idx;
- struct irdma_sc_cq **reg_cq;
- u32 reg_cq_size;
- spinlock_t req_cq_lock; /* protect access to reg_cq array */
bool virtual_map:1;
bool tph_en:1;
bool itr_no_expire:1;
@@ -706,7 +703,7 @@ struct irdma_sc_dev {
u32 vchnl_ver;
u16 num_vfs;
u16 hmc_fn_id;
- u8 vf_id;
+ u16 vf_id;
bool privileged:1;
bool vchnl_up:1;
bool ceq_valid:1;
@@ -894,8 +891,6 @@ struct irdma_ceq_init_info {
u8 tph_val;
u16 vsi_idx;
u32 first_pm_pbl_idx;
- struct irdma_sc_cq **reg_cq;
- u32 reg_cq_idx;
};
struct irdma_aeq_init_info {
diff --git a/drivers/infiniband/hw/irdma/uk.c b/drivers/infiniband/hw/irdma/uk.c
index ce1ae10c30fc..f0846b800913 100644
--- a/drivers/infiniband/hw/irdma/uk.c
+++ b/drivers/infiniband/hw/irdma/uk.c
@@ -114,33 +114,8 @@ void irdma_clr_wqes(struct irdma_qp_uk *qp, u32 qp_wqe_idx)
*/
void irdma_uk_qp_post_wr(struct irdma_qp_uk *qp)
{
- u64 temp;
- u32 hw_sq_tail;
- u32 sw_sq_head;
-
- /* valid bit is written and loads completed before reading shadow */
- mb();
-
- /* read the doorbell shadow area */
- get_64bit_val(qp->shadow_area, 0, &temp);
-
- hw_sq_tail = (u32)FIELD_GET(IRDMA_QP_DBSA_HW_SQ_TAIL, temp);
- sw_sq_head = IRDMA_RING_CURRENT_HEAD(qp->sq_ring);
- if (sw_sq_head != qp->initial_ring.head) {
- if (sw_sq_head != hw_sq_tail) {
- if (sw_sq_head > qp->initial_ring.head) {
- if (hw_sq_tail >= qp->initial_ring.head &&
- hw_sq_tail < sw_sq_head)
- writel(qp->qp_id, qp->wqe_alloc_db);
- } else {
- if (hw_sq_tail >= qp->initial_ring.head ||
- hw_sq_tail < sw_sq_head)
- writel(qp->qp_id, qp->wqe_alloc_db);
- }
- }
- }
-
- qp->initial_ring.head = qp->sq_ring.head;
+ dma_wmb();
+ writel(qp->qp_id, qp->wqe_alloc_db);
}
/**
@@ -194,6 +169,7 @@ __le64 *irdma_qp_get_next_send_wqe(struct irdma_qp_uk *qp, u32 *wqe_idx,
qp->sq_wrtrk_array[*wqe_idx].wrid = info->wr_id;
qp->sq_wrtrk_array[*wqe_idx].wr_len = total_size;
qp->sq_wrtrk_array[*wqe_idx].quanta = quanta;
+ qp->sq_wrtrk_array[*wqe_idx].signaled = info->signaled;
return wqe;
}
@@ -1137,6 +1113,27 @@ void irdma_uk_cq_request_notification(struct irdma_cq_uk *cq,
}
/**
+ * irdma_uk_cq_empty - Check if CQ is empty
+ * @cq: hw cq
+ */
+bool irdma_uk_cq_empty(struct irdma_cq_uk *cq)
+{
+ __le64 *cqe;
+ u8 polarity;
+ u64 qword3;
+
+ if (cq->avoid_mem_cflct)
+ cqe = IRDMA_GET_CURRENT_EXTENDED_CQ_ELEM(cq);
+ else
+ cqe = IRDMA_GET_CURRENT_CQ_ELEM(cq);
+
+ get_64bit_val(cqe, 24, &qword3);
+ polarity = (u8)FIELD_GET(IRDMA_CQ_VALID, qword3);
+
+ return polarity != cq->polarity;
+}
+
+/**
* irdma_uk_cq_poll_cmpl - get cq completion info
* @cq: hw cq
* @info: cq poll information returned
@@ -1287,6 +1284,8 @@ int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq,
info->op_type = (u8)FIELD_GET(IRDMACQ_OP, qword3);
if (info->q_type == IRDMA_CQE_QTYPE_RQ && is_srq) {
+ unsigned long flags;
+
srq = qp->srq_uk;
get_64bit_val(cqe, 8, &info->wr_id);
@@ -1299,8 +1298,11 @@ int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq,
} else {
info->stag_invalid_set = false;
}
+ spin_lock_irqsave(srq->lock, flags);
IRDMA_RING_MOVE_TAIL(srq->srq_ring);
+ spin_unlock_irqrestore(srq->lock, flags);
pring = &srq->srq_ring;
+
} else if (info->q_type == IRDMA_CQE_QTYPE_RQ && !is_srq) {
u32 array_idx;
@@ -1355,6 +1357,10 @@ int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq,
info->wr_id = qp->sq_wrtrk_array[wqe_idx].wrid;
if (!info->comp_status)
info->bytes_xfered = qp->sq_wrtrk_array[wqe_idx].wr_len;
+ if (!qp->sq_wrtrk_array[wqe_idx].signaled) {
+ ret_code = -EFAULT;
+ goto exit;
+ }
info->op_type = (u8)FIELD_GET(IRDMACQ_OP, qword3);
IRDMA_RING_SET_TAIL(qp->sq_ring,
wqe_idx + qp->sq_wrtrk_array[wqe_idx].quanta);
@@ -1420,8 +1426,9 @@ exit:
IRDMA_RING_MOVE_TAIL(cq->cq_ring);
if (!cq->avoid_mem_cflct && ext_valid)
IRDMA_RING_MOVE_TAIL(cq->cq_ring);
- set_64bit_val(cq->shadow_area, 0,
- IRDMA_RING_CURRENT_HEAD(cq->cq_ring));
+ if (IRDMA_RING_CURRENT_HEAD(cq->cq_ring) & 0x3F || irdma_uk_cq_empty(cq))
+ set_64bit_val(cq->shadow_area, 0,
+ IRDMA_RING_CURRENT_HEAD(cq->cq_ring));
} else {
qword3 &= ~IRDMA_CQ_WQEIDX;
qword3 |= FIELD_PREP(IRDMA_CQ_WQEIDX, pring->tail);
@@ -1574,7 +1581,6 @@ static void irdma_setup_connection_wqes(struct irdma_qp_uk *qp,
qp->conn_wqes = move_cnt;
IRDMA_RING_MOVE_HEAD_BY_COUNT_NOCHECK(qp->sq_ring, move_cnt);
IRDMA_RING_MOVE_TAIL_BY_COUNT(qp->sq_ring, move_cnt);
- IRDMA_RING_MOVE_HEAD_BY_COUNT_NOCHECK(qp->initial_ring, move_cnt);
}
/**
@@ -1719,7 +1725,6 @@ int irdma_uk_qp_init(struct irdma_qp_uk *qp, struct irdma_qp_uk_init_info *info)
qp->max_sq_frag_cnt = info->max_sq_frag_cnt;
sq_ring_size = qp->sq_size << info->sq_shift;
IRDMA_RING_INIT(qp->sq_ring, sq_ring_size);
- IRDMA_RING_INIT(qp->initial_ring, sq_ring_size);
if (info->first_sq_wq) {
irdma_setup_connection_wqes(qp, info);
qp->swqe_polarity = 1;
diff --git a/drivers/infiniband/hw/irdma/user.h b/drivers/infiniband/hw/irdma/user.h
index ab57f689827a..9eb7fd0b1cbf 100644
--- a/drivers/infiniband/hw/irdma/user.h
+++ b/drivers/infiniband/hw/irdma/user.h
@@ -429,6 +429,7 @@ struct irdma_wqe_uk_ops {
struct irdma_bind_window *op_info);
};
+bool irdma_uk_cq_empty(struct irdma_cq_uk *cq);
int irdma_uk_cq_poll_cmpl(struct irdma_cq_uk *cq,
struct irdma_cq_poll_info *info);
void irdma_uk_cq_request_notification(struct irdma_cq_uk *cq,
@@ -456,7 +457,6 @@ struct irdma_srq_uk {
struct irdma_uk_attrs *uk_attrs;
__le64 *shadow_area;
struct irdma_ring srq_ring;
- struct irdma_ring initial_ring;
u32 srq_id;
u32 srq_size;
u32 max_srq_frag_cnt;
@@ -465,6 +465,7 @@ struct irdma_srq_uk {
u8 wqe_size;
u8 wqe_size_multiplier;
u8 deferred_flag;
+ spinlock_t *lock;
};
struct irdma_srq_uk_init_info {
@@ -482,7 +483,8 @@ struct irdma_sq_uk_wr_trk_info {
u64 wrid;
u32 wr_len;
u16 quanta;
- u8 reserved[2];
+ u8 signaled;
+ u8 reserved[1];
};
struct irdma_qp_quanta {
diff --git a/drivers/infiniband/hw/irdma/utils.c b/drivers/infiniband/hw/irdma/utils.c
index 8b94d87b0192..cc2a12f735d3 100644
--- a/drivers/infiniband/hw/irdma/utils.c
+++ b/drivers/infiniband/hw/irdma/utils.c
@@ -452,6 +452,7 @@ struct irdma_cqp_request *irdma_alloc_and_get_cqp_request(struct irdma_cqp *cqp,
cqp_request->waiting = wait;
refcount_set(&cqp_request->refcnt, 1);
memset(&cqp_request->compl_info, 0, sizeof(cqp_request->compl_info));
+ memset(&cqp_request->info, 0, sizeof(cqp_request->info));
return cqp_request;
}
@@ -1068,7 +1069,6 @@ int irdma_cqp_qp_create_cmd(struct irdma_sc_dev *dev, struct irdma_sc_qp *qp)
cqp_info = &cqp_request->info;
qp_info = &cqp_request->info.in.u.qp_create.info;
- memset(qp_info, 0, sizeof(*qp_info));
qp_info->cq_num_valid = true;
qp_info->next_iwarp_state = IRDMA_QP_STATE_RTS;
cqp_info->cqp_cmd = IRDMA_OP_QP_CREATE;
@@ -1343,7 +1343,6 @@ int irdma_cqp_qp_destroy_cmd(struct irdma_sc_dev *dev, struct irdma_sc_qp *qp)
return -ENOMEM;
cqp_info = &cqp_request->info;
- memset(cqp_info, 0, sizeof(*cqp_info));
cqp_info->cqp_cmd = IRDMA_OP_QP_DESTROY;
cqp_info->post_sq = 1;
cqp_info->in.u.qp_destroy.qp = qp;
@@ -1749,7 +1748,6 @@ int irdma_cqp_gather_stats_cmd(struct irdma_sc_dev *dev,
return -ENOMEM;
cqp_info = &cqp_request->info;
- memset(cqp_info, 0, sizeof(*cqp_info));
cqp_info->cqp_cmd = IRDMA_OP_STATS_GATHER;
cqp_info->post_sq = 1;
cqp_info->in.u.stats_gather.info = pestat->gather_info;
@@ -1789,7 +1787,6 @@ int irdma_cqp_stats_inst_cmd(struct irdma_sc_vsi *vsi, u8 cmd,
return -ENOMEM;
cqp_info = &cqp_request->info;
- memset(cqp_info, 0, sizeof(*cqp_info));
cqp_info->cqp_cmd = cmd;
cqp_info->post_sq = 1;
cqp_info->in.u.stats_manage.info = *stats_info;
@@ -1890,7 +1887,6 @@ int irdma_cqp_ws_node_cmd(struct irdma_sc_dev *dev, u8 cmd,
return -ENOMEM;
cqp_info = &cqp_request->info;
- memset(cqp_info, 0, sizeof(*cqp_info));
cqp_info->cqp_cmd = cmd;
cqp_info->post_sq = 1;
cqp_info->in.u.ws_node.info = *node_info;
@@ -2357,24 +2353,6 @@ void irdma_ib_qp_event(struct irdma_qp *iwqp, enum irdma_qp_event_type event)
iwqp->ibqp.event_handler(&ibevent, iwqp->ibqp.qp_context);
}
-bool irdma_cq_empty(struct irdma_cq *iwcq)
-{
- struct irdma_cq_uk *ukcq;
- u64 qword3;
- __le64 *cqe;
- u8 polarity;
-
- ukcq = &iwcq->sc_cq.cq_uk;
- if (ukcq->avoid_mem_cflct)
- cqe = IRDMA_GET_CURRENT_EXTENDED_CQ_ELEM(ukcq);
- else
- cqe = IRDMA_GET_CURRENT_CQ_ELEM(ukcq);
- get_64bit_val(cqe, 24, &qword3);
- polarity = (u8)FIELD_GET(IRDMA_CQ_VALID, qword3);
-
- return polarity != ukcq->polarity;
-}
-
void irdma_remove_cmpls_list(struct irdma_cq *iwcq)
{
struct irdma_cmpl_gen *cmpl_node;
@@ -2436,6 +2414,8 @@ void irdma_generate_flush_completions(struct irdma_qp *iwqp)
struct irdma_qp_uk *qp = &iwqp->sc_qp.qp_uk;
struct irdma_ring *sq_ring = &qp->sq_ring;
struct irdma_ring *rq_ring = &qp->rq_ring;
+ struct irdma_cq *iwscq = iwqp->iwscq;
+ struct irdma_cq *iwrcq = iwqp->iwrcq;
struct irdma_cmpl_gen *cmpl;
__le64 *sw_wqe;
u64 wqe_qword;
@@ -2443,8 +2423,8 @@ void irdma_generate_flush_completions(struct irdma_qp *iwqp)
bool compl_generated = false;
unsigned long flags1;
- spin_lock_irqsave(&iwqp->iwscq->lock, flags1);
- if (irdma_cq_empty(iwqp->iwscq)) {
+ spin_lock_irqsave(&iwscq->lock, flags1);
+ if (irdma_uk_cq_empty(&iwscq->sc_cq.cq_uk)) {
unsigned long flags2;
spin_lock_irqsave(&iwqp->lock, flags2);
@@ -2452,7 +2432,7 @@ void irdma_generate_flush_completions(struct irdma_qp *iwqp)
cmpl = kzalloc(sizeof(*cmpl), GFP_ATOMIC);
if (!cmpl) {
spin_unlock_irqrestore(&iwqp->lock, flags2);
- spin_unlock_irqrestore(&iwqp->iwscq->lock, flags1);
+ spin_unlock_irqrestore(&iwscq->lock, flags1);
return;
}
@@ -2471,24 +2451,24 @@ void irdma_generate_flush_completions(struct irdma_qp *iwqp)
kfree(cmpl);
continue;
}
- ibdev_dbg(iwqp->iwscq->ibcq.device,
+ ibdev_dbg(iwscq->ibcq.device,
"DEV: %s: adding wr_id = 0x%llx SQ Completion to list qp_id=%d\n",
__func__, cmpl->cpi.wr_id, qp->qp_id);
- list_add_tail(&cmpl->list, &iwqp->iwscq->cmpl_generated);
+ list_add_tail(&cmpl->list, &iwscq->cmpl_generated);
compl_generated = true;
}
spin_unlock_irqrestore(&iwqp->lock, flags2);
- spin_unlock_irqrestore(&iwqp->iwscq->lock, flags1);
+ spin_unlock_irqrestore(&iwscq->lock, flags1);
if (compl_generated)
- irdma_comp_handler(iwqp->iwscq);
+ irdma_comp_handler(iwscq);
} else {
- spin_unlock_irqrestore(&iwqp->iwscq->lock, flags1);
+ spin_unlock_irqrestore(&iwscq->lock, flags1);
mod_delayed_work(iwqp->iwdev->cleanup_wq, &iwqp->dwork_flush,
msecs_to_jiffies(IRDMA_FLUSH_DELAY_MS));
}
- spin_lock_irqsave(&iwqp->iwrcq->lock, flags1);
- if (irdma_cq_empty(iwqp->iwrcq)) {
+ spin_lock_irqsave(&iwrcq->lock, flags1);
+ if (irdma_uk_cq_empty(&iwrcq->sc_cq.cq_uk)) {
unsigned long flags2;
spin_lock_irqsave(&iwqp->lock, flags2);
@@ -2496,7 +2476,7 @@ void irdma_generate_flush_completions(struct irdma_qp *iwqp)
cmpl = kzalloc(sizeof(*cmpl), GFP_ATOMIC);
if (!cmpl) {
spin_unlock_irqrestore(&iwqp->lock, flags2);
- spin_unlock_irqrestore(&iwqp->iwrcq->lock, flags1);
+ spin_unlock_irqrestore(&iwrcq->lock, flags1);
return;
}
@@ -2508,20 +2488,20 @@ void irdma_generate_flush_completions(struct irdma_qp *iwqp)
cmpl->cpi.q_type = IRDMA_CQE_QTYPE_RQ;
/* remove the RQ WR by moving RQ tail */
IRDMA_RING_SET_TAIL(*rq_ring, rq_ring->tail + 1);
- ibdev_dbg(iwqp->iwrcq->ibcq.device,
+ ibdev_dbg(iwrcq->ibcq.device,
"DEV: %s: adding wr_id = 0x%llx RQ Completion to list qp_id=%d, wqe_idx=%d\n",
__func__, cmpl->cpi.wr_id, qp->qp_id,
wqe_idx);
- list_add_tail(&cmpl->list, &iwqp->iwrcq->cmpl_generated);
+ list_add_tail(&cmpl->list, &iwrcq->cmpl_generated);
compl_generated = true;
}
spin_unlock_irqrestore(&iwqp->lock, flags2);
- spin_unlock_irqrestore(&iwqp->iwrcq->lock, flags1);
+ spin_unlock_irqrestore(&iwrcq->lock, flags1);
if (compl_generated)
- irdma_comp_handler(iwqp->iwrcq);
+ irdma_comp_handler(iwrcq);
} else {
- spin_unlock_irqrestore(&iwqp->iwrcq->lock, flags1);
+ spin_unlock_irqrestore(&iwrcq->lock, flags1);
mod_delayed_work(iwqp->iwdev->cleanup_wq, &iwqp->dwork_flush,
msecs_to_jiffies(IRDMA_FLUSH_DELAY_MS));
}
diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c
index 76ce6137f2ba..6d9af41a2884 100644
--- a/drivers/infiniband/hw/irdma/verbs.c
+++ b/drivers/infiniband/hw/irdma/verbs.c
@@ -27,7 +27,8 @@ static int irdma_query_device(struct ib_device *ibdev,
irdma_fw_minor_ver(&rf->sc_dev);
props->device_cap_flags = IB_DEVICE_MEM_WINDOW |
IB_DEVICE_MEM_MGT_EXTENSIONS;
- props->kernel_cap_flags = IBK_LOCAL_DMA_LKEY;
+ if (hw_attrs->uk_attrs.hw_rev < IRDMA_GEN_3)
+ props->kernel_cap_flags = IBK_LOCAL_DMA_LKEY;
props->vendor_id = pcidev->vendor;
props->vendor_part_id = pcidev->device;
@@ -771,7 +772,6 @@ static int irdma_cqp_create_qp_cmd(struct irdma_qp *iwqp)
cqp_info = &cqp_request->info;
qp_info = &cqp_request->info.in.u.qp_create.info;
- memset(qp_info, 0, sizeof(*qp_info));
qp_info->mac_valid = true;
qp_info->cq_num_valid = true;
qp_info->next_iwarp_state = IRDMA_QP_STATE_IDLE;
@@ -2029,6 +2029,7 @@ static int irdma_resize_cq(struct ib_cq *ibcq, int entries,
struct irdma_pci_f *rf;
struct irdma_cq_buf *cq_buf = NULL;
unsigned long flags;
+ u8 cqe_size;
int ret;
iwdev = to_iwdev(ibcq->device);
@@ -2045,7 +2046,7 @@ static int irdma_resize_cq(struct ib_cq *ibcq, int entries,
return -EINVAL;
if (!iwcq->user_mode) {
- entries++;
+ entries += 2;
if (!iwcq->sc_cq.cq_uk.avoid_mem_cflct &&
dev->hw_attrs.uk_attrs.hw_rev >= IRDMA_GEN_2)
@@ -2053,6 +2054,10 @@ static int irdma_resize_cq(struct ib_cq *ibcq, int entries,
if (entries & 1)
entries += 1; /* cq size must be an even number */
+
+ cqe_size = iwcq->sc_cq.cq_uk.avoid_mem_cflct ? 64 : 32;
+ if (entries * cqe_size == IRDMA_HW_PAGE_SIZE)
+ entries += 2;
}
info.cq_size = max(entries, 4);
@@ -2306,8 +2311,8 @@ static int irdma_setup_kmode_srq(struct irdma_device *iwdev,
ukinfo->srq_size = depth >> shift;
ukinfo->shadow_area = mem->va + ring_size;
- info->shadow_area_pa = info->srq_pa + ring_size;
info->srq_pa = mem->pa;
+ info->shadow_area_pa = info->srq_pa + ring_size;
return 0;
}
@@ -2384,6 +2389,7 @@ static int irdma_create_srq(struct ib_srq *ibsrq,
info.vsi = &iwdev->vsi;
info.pd = &iwpd->sc_pd;
+ iwsrq->sc_srq.srq_uk.lock = &iwsrq->lock;
err_code = irdma_sc_srq_init(&iwsrq->sc_srq, &info);
if (err_code)
goto free_dmem;
@@ -2483,6 +2489,7 @@ static int irdma_create_cq(struct ib_cq *ibcq,
int err_code;
int entries = attr->cqe;
bool cqe_64byte_ena;
+ u8 cqe_size;
err_code = cq_validate_flags(attr->flags, dev->hw_attrs.uk_attrs.hw_rev);
if (err_code)
@@ -2503,11 +2510,13 @@ static int irdma_create_cq(struct ib_cq *ibcq,
spin_lock_init(&iwcq->lock);
INIT_LIST_HEAD(&iwcq->resize_list);
INIT_LIST_HEAD(&iwcq->cmpl_generated);
+ iwcq->cq_num = cq_num;
info.dev = dev;
ukinfo->cq_size = max(entries, 4);
ukinfo->cq_id = cq_num;
cqe_64byte_ena = dev->hw_attrs.uk_attrs.feature_flags & IRDMA_FEATURE_64_BYTE_CQE ?
true : false;
+ cqe_size = cqe_64byte_ena ? 64 : 32;
ukinfo->avoid_mem_cflct = cqe_64byte_ena;
iwcq->ibcq.cqe = info.cq_uk_init_info.cq_size;
if (attr->comp_vector < rf->ceqs_count)
@@ -2580,13 +2589,16 @@ static int irdma_create_cq(struct ib_cq *ibcq,
goto cq_free_rsrc;
}
- entries++;
+ entries += 2;
if (!cqe_64byte_ena && dev->hw_attrs.uk_attrs.hw_rev >= IRDMA_GEN_2)
entries *= 2;
if (entries & 1)
entries += 1; /* cq size must be an even number */
+ if (entries * cqe_size == IRDMA_HW_PAGE_SIZE)
+ entries += 2;
+
ukinfo->cq_size = entries;
if (cqe_64byte_ena)
@@ -3102,12 +3114,10 @@ static int irdma_hw_alloc_stag(struct irdma_device *iwdev,
cqp_info = &cqp_request->info;
info = &cqp_info->in.u.alloc_stag.info;
- memset(info, 0, sizeof(*info));
info->page_size = PAGE_SIZE;
info->stag_idx = iwmr->stag >> IRDMA_CQPSQ_STAG_IDX_S;
info->pd_id = iwpd->sc_pd.pd_id;
info->total_len = iwmr->len;
- info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY;
info->remote_access = true;
cqp_info->cqp_cmd = IRDMA_OP_ALLOC_STAG;
cqp_info->post_sq = 1;
@@ -3118,7 +3128,7 @@ static int irdma_hw_alloc_stag(struct irdma_device *iwdev,
if (status)
return status;
- iwmr->is_hwreg = 1;
+ iwmr->is_hwreg = true;
return 0;
}
@@ -3252,7 +3262,6 @@ static int irdma_hwreg_mr(struct irdma_device *iwdev, struct irdma_mr *iwmr,
cqp_info = &cqp_request->info;
stag_info = &cqp_info->in.u.mr_reg_non_shared.info;
- memset(stag_info, 0, sizeof(*stag_info));
stag_info->va = iwpbl->user_base;
stag_info->stag_idx = iwmr->stag >> IRDMA_CQPSQ_STAG_IDX_S;
stag_info->stag_key = (u8)iwmr->stag;
@@ -3262,7 +3271,7 @@ static int irdma_hwreg_mr(struct irdma_device *iwdev, struct irdma_mr *iwmr,
if (iwdev->rf->sc_dev.hw_attrs.uk_attrs.feature_flags & IRDMA_FEATURE_ATOMIC_OPS)
stag_info->remote_atomics_en = (access & IB_ACCESS_REMOTE_ATOMIC) ? 1 : 0;
stag_info->pd_id = iwpd->sc_pd.pd_id;
- stag_info->all_memory = pd->flags & IB_PD_UNSAFE_GLOBAL_RKEY;
+ stag_info->all_memory = iwmr->dma_mr;
if (stag_info->access_rights & IRDMA_ACCESS_FLAGS_ZERO_BASED)
stag_info->addr_type = IRDMA_ADDR_TYPE_ZERO_BASED;
else
@@ -3289,7 +3298,7 @@ static int irdma_hwreg_mr(struct irdma_device *iwdev, struct irdma_mr *iwmr,
irdma_put_cqp_request(&iwdev->rf->cqp, cqp_request);
if (!ret)
- iwmr->is_hwreg = 1;
+ iwmr->is_hwreg = true;
return ret;
}
@@ -3646,7 +3655,6 @@ static int irdma_hwdereg_mr(struct ib_mr *ib_mr)
cqp_info = &cqp_request->info;
info = &cqp_info->in.u.dealloc_stag.info;
- memset(info, 0, sizeof(*info));
info->pd_id = iwpd->sc_pd.pd_id;
info->stag_idx = ib_mr->rkey >> IRDMA_CQPSQ_STAG_IDX_S;
info->mr = true;
@@ -3662,7 +3670,7 @@ static int irdma_hwdereg_mr(struct ib_mr *ib_mr)
if (status)
return status;
- iwmr->is_hwreg = 0;
+ iwmr->is_hwreg = false;
return 0;
}
@@ -3785,9 +3793,10 @@ static struct ib_mr *irdma_rereg_user_mr(struct ib_mr *ib_mr, int flags,
* @size: size of memory to register
* @access: Access rights
* @iova_start: start of virtual address for physical buffers
+ * @dma_mr: Flag indicating whether this region is a PD DMA MR
*/
struct ib_mr *irdma_reg_phys_mr(struct ib_pd *pd, u64 addr, u64 size, int access,
- u64 *iova_start)
+ u64 *iova_start, bool dma_mr)
{
struct irdma_device *iwdev = to_iwdev(pd->device);
struct irdma_pbl *iwpbl;
@@ -3804,6 +3813,7 @@ struct ib_mr *irdma_reg_phys_mr(struct ib_pd *pd, u64 addr, u64 size, int access
iwpbl = &iwmr->iwpbl;
iwpbl->iwmr = iwmr;
iwmr->type = IRDMA_MEMREG_TYPE_MEM;
+ iwmr->dma_mr = dma_mr;
iwpbl->user_base = *iova_start;
stag = irdma_create_stag(iwdev);
if (!stag) {
@@ -3842,7 +3852,7 @@ static struct ib_mr *irdma_get_dma_mr(struct ib_pd *pd, int acc)
{
u64 kva = 0;
- return irdma_reg_phys_mr(pd, 0, 0, acc, &kva);
+ return irdma_reg_phys_mr(pd, 0, 0, acc, &kva, true);
}
/**
@@ -4077,7 +4087,7 @@ static int irdma_post_send(struct ib_qp *ibqp,
break;
case IB_WR_LOCAL_INV:
info.op_type = IRDMA_OP_TYPE_INV_STAG;
- info.local_fence = info.read_fence;
+ info.local_fence = true;
info.op.inv_local_stag.target_stag = ib_wr->ex.invalidate_rkey;
err = irdma_uk_stag_local_invalidate(ukqp, &info, true);
break;
@@ -4504,7 +4514,7 @@ static int irdma_req_notify_cq(struct ib_cq *ibcq,
}
if ((notify_flags & IB_CQ_REPORT_MISSED_EVENTS) &&
- (!irdma_cq_empty(iwcq) || !list_empty(&iwcq->cmpl_generated)))
+ (!irdma_uk_cq_empty(ukcq) || !list_empty(&iwcq->cmpl_generated)))
ret = 1;
spin_unlock_irqrestore(&iwcq->lock, flags);
@@ -5203,7 +5213,7 @@ static int irdma_create_user_ah(struct ib_ah *ibah,
struct irdma_ah *parent_ah;
int err;
- if (udata && udata->outlen < IRDMA_CREATE_AH_MIN_RESP_LEN)
+ if (udata->outlen < IRDMA_CREATE_AH_MIN_RESP_LEN)
return -EINVAL;
err = irdma_setup_ah(ibah, attr);
@@ -5499,7 +5509,9 @@ void irdma_ib_dealloc_device(struct ib_device *ibdev)
irdma_rt_deinit_hw(iwdev);
if (!iwdev->is_vport) {
irdma_ctrl_deinit_hw(iwdev->rf);
- if (iwdev->rf->vchnl_wq)
+ if (iwdev->rf->vchnl_wq) {
destroy_workqueue(iwdev->rf->vchnl_wq);
+ mutex_destroy(&iwdev->rf->sc_dev.vchnl_mutex);
+ }
}
}
diff --git a/drivers/infiniband/hw/irdma/verbs.h b/drivers/infiniband/hw/irdma/verbs.h
index ed21c1b56e8e..aabbb3442098 100644
--- a/drivers/infiniband/hw/irdma/verbs.h
+++ b/drivers/infiniband/hw/irdma/verbs.h
@@ -111,7 +111,8 @@ struct irdma_mr {
};
struct ib_umem *region;
int access;
- u8 is_hwreg;
+ bool is_hwreg:1;
+ bool dma_mr:1;
u16 type;
u32 page_cnt;
u64 page_size;
@@ -140,7 +141,7 @@ struct irdma_srq {
struct irdma_cq {
struct ib_cq ibcq;
struct irdma_sc_cq sc_cq;
- u16 cq_num;
+ u32 cq_num;
bool user_mode;
atomic_t armed;
enum irdma_cmpl_notify last_notify;
diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c
index 12b481d138cf..03aacd526860 100644
--- a/drivers/infiniband/hw/mlx4/cm.c
+++ b/drivers/infiniband/hw/mlx4/cm.c
@@ -591,7 +591,7 @@ void mlx4_ib_cm_paravirt_clean(struct mlx4_ib_dev *dev, int slave)
int mlx4_ib_cm_init(void)
{
- cm_wq = alloc_workqueue("mlx4_ib_cm", 0, 0);
+ cm_wq = alloc_workqueue("mlx4_ib_cm", WQ_PERCPU, 0);
if (!cm_wq)
return -ENOMEM;
diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c
index a23b364e24ff..651d76bca114 100644
--- a/drivers/infiniband/hw/mlx5/cq.c
+++ b/drivers/infiniband/hw/mlx5/cq.c
@@ -1020,15 +1020,18 @@ int mlx5_ib_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr,
if (cq->create_flags & IB_UVERBS_CQ_FLAGS_IGNORE_OVERRUN)
MLX5_SET(cqc, cqc, oi, 1);
+ if (udata) {
+ cq->mcq.comp = mlx5_add_cq_to_tasklet;
+ cq->mcq.tasklet_ctx.comp = mlx5_ib_cq_comp;
+ } else {
+ cq->mcq.comp = mlx5_ib_cq_comp;
+ }
+
err = mlx5_core_create_cq(dev->mdev, &cq->mcq, cqb, inlen, out, sizeof(out));
if (err)
goto err_cqb;
mlx5_ib_dbg(dev, "cqn 0x%x\n", cq->mcq.cqn);
- if (udata)
- cq->mcq.tasklet_ctx.comp = mlx5_ib_cq_comp;
- else
- cq->mcq.comp = mlx5_ib_cq_comp;
cq->mcq.event = mlx5_ib_cq_event;
INIT_LIST_HEAD(&cq->wc_list);
diff --git a/drivers/infiniband/hw/mlx5/devx.c b/drivers/infiniband/hw/mlx5/devx.c
index 8b506417ad2f..d31d7f3005c6 100644
--- a/drivers/infiniband/hw/mlx5/devx.c
+++ b/drivers/infiniband/hw/mlx5/devx.c
@@ -1225,6 +1225,11 @@ static void devx_obj_build_destroy_cmd(void *in, void *out, void *din,
MLX5_GET(create_flow_table_in, in, other_vport));
MLX5_SET(destroy_flow_table_in, din, vport_number,
MLX5_GET(create_flow_table_in, in, vport_number));
+ MLX5_SET(destroy_flow_table_in, din, other_eswitch,
+ MLX5_GET(create_flow_table_in, in, other_eswitch));
+ MLX5_SET(destroy_flow_table_in, din, eswitch_owner_vhca_id,
+ MLX5_GET(create_flow_table_in, in,
+ eswitch_owner_vhca_id));
MLX5_SET(destroy_flow_table_in, din, table_type,
MLX5_GET(create_flow_table_in, in, table_type));
MLX5_SET(destroy_flow_table_in, din, table_id, *obj_id);
@@ -1237,6 +1242,11 @@ static void devx_obj_build_destroy_cmd(void *in, void *out, void *din,
MLX5_GET(create_flow_group_in, in, other_vport));
MLX5_SET(destroy_flow_group_in, din, vport_number,
MLX5_GET(create_flow_group_in, in, vport_number));
+ MLX5_SET(destroy_flow_group_in, din, other_eswitch,
+ MLX5_GET(create_flow_group_in, in, other_eswitch));
+ MLX5_SET(destroy_flow_group_in, din, eswitch_owner_vhca_id,
+ MLX5_GET(create_flow_group_in, in,
+ eswitch_owner_vhca_id));
MLX5_SET(destroy_flow_group_in, din, table_type,
MLX5_GET(create_flow_group_in, in, table_type));
MLX5_SET(destroy_flow_group_in, din, table_id,
@@ -1251,6 +1261,10 @@ static void devx_obj_build_destroy_cmd(void *in, void *out, void *din,
MLX5_GET(set_fte_in, in, other_vport));
MLX5_SET(delete_fte_in, din, vport_number,
MLX5_GET(set_fte_in, in, vport_number));
+ MLX5_SET(delete_fte_in, din, other_eswitch,
+ MLX5_GET(set_fte_in, in, other_eswitch));
+ MLX5_SET(delete_fte_in, din, eswitch_owner_vhca_id,
+ MLX5_GET(set_fte_in, in, eswitch_owner_vhca_id));
MLX5_SET(delete_fte_in, din, table_type,
MLX5_GET(set_fte_in, in, table_type));
MLX5_SET(delete_fte_in, din, table_id,
diff --git a/drivers/infiniband/hw/mlx5/fs.c b/drivers/infiniband/hw/mlx5/fs.c
index b0f7663c24c1..d17823ce7f38 100644
--- a/drivers/infiniband/hw/mlx5/fs.c
+++ b/drivers/infiniband/hw/mlx5/fs.c
@@ -691,22 +691,13 @@ static bool __maybe_unused mlx5_ib_shared_ft_allowed(struct ib_device *device)
return MLX5_CAP_GEN(dev->mdev, shared_object_to_user_object_allowed);
}
-static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_ib_dev *dev,
- struct mlx5_flow_namespace *ns,
+static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_flow_namespace *ns,
struct mlx5_ib_flow_prio *prio,
- int priority,
- int num_entries, int num_groups,
- u32 flags, u16 vport)
+ struct mlx5_flow_table_attr *ft_attr)
{
- struct mlx5_flow_table_attr ft_attr = {};
struct mlx5_flow_table *ft;
- ft_attr.prio = priority;
- ft_attr.max_fte = num_entries;
- ft_attr.flags = flags;
- ft_attr.vport = vport;
- ft_attr.autogroup.max_num_groups = num_groups;
- ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr);
+ ft = mlx5_create_auto_grouped_flow_table(ns, ft_attr);
if (IS_ERR(ft))
return ERR_CAST(ft);
@@ -720,6 +711,7 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
enum flow_table_type ft_type)
{
bool dont_trap = flow_attr->flags & IB_FLOW_ATTR_FLAGS_DONT_TRAP;
+ struct mlx5_flow_table_attr ft_attr = {};
struct mlx5_flow_namespace *ns = NULL;
enum mlx5_flow_namespace_type fn_type;
struct mlx5_ib_flow_prio *prio;
@@ -797,11 +789,14 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev,
max_table_size = min_t(int, num_entries, max_table_size);
ft = prio->flow_table;
- if (!ft)
- return _get_prio(dev, ns, prio, priority, max_table_size,
- num_groups, flags, 0);
+ if (ft)
+ return prio;
- return prio;
+ ft_attr.prio = priority;
+ ft_attr.max_fte = max_table_size;
+ ft_attr.flags = flags;
+ ft_attr.autogroup.max_num_groups = num_groups;
+ return _get_prio(ns, prio, &ft_attr);
}
enum {
@@ -950,6 +945,7 @@ static int get_per_qp_prio(struct mlx5_ib_dev *dev,
enum mlx5_ib_optional_counter_type type)
{
enum mlx5_ib_optional_counter_type per_qp_type;
+ struct mlx5_flow_table_attr ft_attr = {};
enum mlx5_flow_namespace_type fn_type;
struct mlx5_flow_namespace *ns;
struct mlx5_ib_flow_prio *prio;
@@ -1003,7 +999,10 @@ static int get_per_qp_prio(struct mlx5_ib_dev *dev,
if (prio->flow_table)
return 0;
- prio = _get_prio(dev, ns, prio, priority, MLX5_FS_MAX_POOL_SIZE, 1, 0, 0);
+ ft_attr.prio = priority;
+ ft_attr.max_fte = MLX5_FS_MAX_POOL_SIZE;
+ ft_attr.autogroup.max_num_groups = 1;
+ prio = _get_prio(ns, prio, &ft_attr);
if (IS_ERR(prio))
return PTR_ERR(prio);
@@ -1223,6 +1222,7 @@ int mlx5_ib_fs_add_op_fc(struct mlx5_ib_dev *dev, u32 port_num,
struct mlx5_ib_op_fc *opfc,
enum mlx5_ib_optional_counter_type type)
{
+ struct mlx5_flow_table_attr ft_attr = {};
enum mlx5_flow_namespace_type fn_type;
int priority, i, err, spec_num;
struct mlx5_flow_act flow_act = {};
@@ -1304,8 +1304,10 @@ int mlx5_ib_fs_add_op_fc(struct mlx5_ib_dev *dev, u32 port_num,
if (err)
goto free;
- prio = _get_prio(dev, ns, prio, priority,
- dev->num_ports * MAX_OPFC_RULES, 1, 0, 0);
+ ft_attr.prio = priority;
+ ft_attr.max_fte = dev->num_ports * MAX_OPFC_RULES;
+ ft_attr.autogroup.max_num_groups = 1;
+ prio = _get_prio(ns, prio, &ft_attr);
if (IS_ERR(prio)) {
err = PTR_ERR(prio);
goto put_prio;
@@ -1872,7 +1874,7 @@ static int mlx5_ib_fill_transport_ns_info(struct mlx5_ib_dev *dev,
u32 *flags, u16 *vport_idx,
u16 *vport,
struct mlx5_core_dev **ft_mdev,
- u32 ib_port)
+ u32 ib_port, u16 *esw_owner_vhca_id)
{
struct mlx5_core_dev *esw_mdev;
@@ -1886,8 +1888,13 @@ static int mlx5_ib_fill_transport_ns_info(struct mlx5_ib_dev *dev,
return -EINVAL;
esw_mdev = mlx5_eswitch_get_core_dev(dev->port[ib_port - 1].rep->esw);
- if (esw_mdev != dev->mdev)
- return -EOPNOTSUPP;
+ if (esw_mdev != dev->mdev) {
+ if (!MLX5_CAP_ADV_RDMA(dev->mdev,
+ rdma_transport_manager_other_eswitch))
+ return -EOPNOTSUPP;
+ *flags |= MLX5_FLOW_TABLE_OTHER_ESWITCH;
+ *esw_owner_vhca_id = MLX5_CAP_GEN(esw_mdev, vhca_id);
+ }
*flags |= MLX5_FLOW_TABLE_OTHER_VPORT;
*ft_mdev = esw_mdev;
@@ -1903,8 +1910,10 @@ _get_flow_table(struct mlx5_ib_dev *dev, u16 user_priority,
bool mcast, u32 ib_port)
{
struct mlx5_core_dev *ft_mdev = dev->mdev;
+ struct mlx5_flow_table_attr ft_attr = {};
struct mlx5_flow_namespace *ns = NULL;
struct mlx5_ib_flow_prio *prio = NULL;
+ u16 esw_owner_vhca_id = 0;
int max_table_size = 0;
u16 vport_idx = 0;
bool esw_encap;
@@ -1966,7 +1975,8 @@ _get_flow_table(struct mlx5_ib_dev *dev, u16 user_priority,
return ERR_PTR(-EINVAL);
ret = mlx5_ib_fill_transport_ns_info(dev, ns_type, &flags,
&vport_idx, &vport,
- &ft_mdev, ib_port);
+ &ft_mdev, ib_port,
+ &esw_owner_vhca_id);
if (ret)
return ERR_PTR(ret);
@@ -2026,8 +2036,13 @@ _get_flow_table(struct mlx5_ib_dev *dev, u16 user_priority,
if (prio->flow_table)
return prio;
- return _get_prio(dev, ns, prio, priority, max_table_size,
- MLX5_FS_MAX_TYPES, flags, vport);
+ ft_attr.prio = priority;
+ ft_attr.max_fte = max_table_size;
+ ft_attr.flags = flags;
+ ft_attr.vport = vport;
+ ft_attr.esw_owner_vhca_id = esw_owner_vhca_id;
+ ft_attr.autogroup.max_num_groups = MLX5_FS_MAX_TYPES;
+ return _get_prio(ns, prio, &ft_attr);
}
static struct mlx5_ib_flow_handler *
diff --git a/drivers/infiniband/hw/mlx5/ib_rep.c b/drivers/infiniband/hw/mlx5/ib_rep.c
index cc8859d3c2f5..bbecca405171 100644
--- a/drivers/infiniband/hw/mlx5/ib_rep.c
+++ b/drivers/infiniband/hw/mlx5/ib_rep.c
@@ -44,6 +44,63 @@ static void mlx5_ib_num_ports_update(struct mlx5_core_dev *dev, u32 *num_ports)
}
}
+static int mlx5_ib_set_owner_transport(struct mlx5_core_dev *cur_owner,
+ struct mlx5_core_dev *new_owner)
+{
+ int ret;
+
+ if (!MLX5_CAP_FLOWTABLE_RDMA_TRANSPORT_TX(cur_owner, ft_support) ||
+ !MLX5_CAP_FLOWTABLE_RDMA_TRANSPORT_RX(cur_owner, ft_support))
+ return 0;
+
+ if (!MLX5_CAP_ADV_RDMA(new_owner, rdma_transport_manager) ||
+ !MLX5_CAP_ADV_RDMA(new_owner, rdma_transport_manager_other_eswitch))
+ return 0;
+
+ ret = mlx5_fs_set_root_dev(cur_owner, new_owner,
+ FS_FT_RDMA_TRANSPORT_TX);
+ if (ret)
+ return ret;
+
+ ret = mlx5_fs_set_root_dev(cur_owner, new_owner,
+ FS_FT_RDMA_TRANSPORT_RX);
+ if (ret) {
+ mlx5_fs_set_root_dev(cur_owner, cur_owner,
+ FS_FT_RDMA_TRANSPORT_TX);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void mlx5_ib_release_transport(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_dev *peer_dev;
+ int i, ret;
+
+ mlx5_lag_for_each_peer_mdev(dev, peer_dev, i) {
+ ret = mlx5_ib_set_owner_transport(peer_dev, peer_dev);
+ WARN_ON_ONCE(ret);
+ }
+}
+
+static int mlx5_ib_take_transport(struct mlx5_core_dev *dev)
+{
+ struct mlx5_core_dev *peer_dev;
+ int ret;
+ int i;
+
+ mlx5_lag_for_each_peer_mdev(dev, peer_dev, i) {
+ ret = mlx5_ib_set_owner_transport(peer_dev, dev);
+ if (ret) {
+ mlx5_ib_release_transport(dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int
mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
{
@@ -88,10 +145,18 @@ mlx5_ib_vport_rep_load(struct mlx5_core_dev *dev, struct mlx5_eswitch_rep *rep)
else
return mlx5_ib_set_vport_rep(lag_master, rep, vport_index);
+ if (mlx5_lag_is_shared_fdb(dev)) {
+ ret = mlx5_ib_take_transport(lag_master);
+ if (ret)
+ return ret;
+ }
+
ibdev = ib_alloc_device_with_net(mlx5_ib_dev, ib_dev,
mlx5_core_net(lag_master));
- if (!ibdev)
- return -ENOMEM;
+ if (!ibdev) {
+ ret = -ENOMEM;
+ goto release_transport;
+ }
ibdev->port = kcalloc(num_ports, sizeof(*ibdev->port),
GFP_KERNEL);
@@ -127,6 +192,10 @@ fail_add:
kfree(ibdev->port);
fail_port:
ib_dealloc_device(&ibdev->ib_dev);
+release_transport:
+ if (mlx5_lag_is_shared_fdb(lag_master))
+ mlx5_ib_release_transport(lag_master);
+
return ret;
}
@@ -182,6 +251,7 @@ mlx5_ib_vport_rep_unload(struct mlx5_eswitch_rep *rep)
esw = peer_mdev->priv.eswitch;
mlx5_eswitch_unregister_vport_reps(esw, REP_IB);
}
+ mlx5_ib_release_transport(mdev);
}
__mlx5_ib_remove(dev, dev->profile, MLX5_IB_STAGE_MAX);
}
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index fc1e86f6c409..40284bbb45d6 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -511,6 +511,10 @@ static int translate_eth_ext_proto_oper(u32 eth_proto_oper, u16 *active_speed,
*active_width = IB_WIDTH_4X;
*active_speed = IB_SPEED_XDR;
break;
+ case MLX5E_PROT_MASK(MLX5E_1600TAUI_8_1600TBASE_CR8_KR8):
+ *active_width = IB_WIDTH_8X;
+ *active_speed = IB_SPEED_XDR;
+ break;
default:
return -EINVAL;
}
@@ -842,7 +846,7 @@ static int mlx5_query_node_guid(struct mlx5_ib_dev *dev,
break;
case MLX5_VPORT_ACCESS_METHOD_NIC:
- err = mlx5_query_nic_vport_node_guid(dev->mdev, &tmp);
+ err = mlx5_query_nic_vport_node_guid(dev->mdev, 0, false, &tmp);
break;
default:
diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c
index 0e8ae85af5a6..e71ee3d52eb0 100644
--- a/drivers/infiniband/hw/mlx5/odp.c
+++ b/drivers/infiniband/hw/mlx5/odp.c
@@ -97,33 +97,28 @@ struct mlx5_pagefault {
* a pagefault. */
#define MMU_NOTIFIER_TIMEOUT 1000
-#define MLX5_IMR_MTT_BITS (30 - PAGE_SHIFT)
-#define MLX5_IMR_MTT_SHIFT (MLX5_IMR_MTT_BITS + PAGE_SHIFT)
-#define MLX5_IMR_MTT_ENTRIES BIT_ULL(MLX5_IMR_MTT_BITS)
-#define MLX5_IMR_MTT_SIZE BIT_ULL(MLX5_IMR_MTT_SHIFT)
-#define MLX5_IMR_MTT_MASK (~(MLX5_IMR_MTT_SIZE - 1))
-
-#define MLX5_KSM_PAGE_SHIFT MLX5_IMR_MTT_SHIFT
-
static u64 mlx5_imr_ksm_entries;
+static u64 mlx5_imr_mtt_entries;
+static u64 mlx5_imr_mtt_size;
+static u8 mlx5_imr_mtt_shift;
+static u8 mlx5_imr_ksm_page_shift;
-static void populate_klm(struct mlx5_klm *pklm, size_t idx, size_t nentries,
+static void populate_ksm(struct mlx5_ksm *pksm, size_t idx, size_t nentries,
struct mlx5_ib_mr *imr, int flags)
{
struct mlx5_core_dev *dev = mr_to_mdev(imr)->mdev;
- struct mlx5_klm *end = pklm + nentries;
- int step = MLX5_CAP_ODP(dev, mem_page_fault) ? MLX5_IMR_MTT_SIZE : 0;
+ struct mlx5_ksm *end = pksm + nentries;
+ u64 step = MLX5_CAP_ODP(dev, mem_page_fault) ? mlx5_imr_mtt_size : 0;
__be32 key = MLX5_CAP_ODP(dev, mem_page_fault) ?
cpu_to_be32(imr->null_mmkey.key) :
mr_to_mdev(imr)->mkeys.null_mkey;
u64 va =
- MLX5_CAP_ODP(dev, mem_page_fault) ? idx * MLX5_IMR_MTT_SIZE : 0;
+ MLX5_CAP_ODP(dev, mem_page_fault) ? idx * mlx5_imr_mtt_size : 0;
if (flags & MLX5_IB_UPD_XLT_ZAP) {
- for (; pklm != end; pklm++, idx++, va += step) {
- pklm->bcount = cpu_to_be32(MLX5_IMR_MTT_SIZE);
- pklm->key = key;
- pklm->va = cpu_to_be64(va);
+ for (; pksm != end; pksm++, idx++, va += step) {
+ pksm->key = key;
+ pksm->va = cpu_to_be64(va);
}
return;
}
@@ -147,16 +142,15 @@ static void populate_klm(struct mlx5_klm *pklm, size_t idx, size_t nentries,
*/
lockdep_assert_held(&to_ib_umem_odp(imr->umem)->umem_mutex);
- for (; pklm != end; pklm++, idx++, va += step) {
+ for (; pksm != end; pksm++, idx++, va += step) {
struct mlx5_ib_mr *mtt = xa_load(&imr->implicit_children, idx);
- pklm->bcount = cpu_to_be32(MLX5_IMR_MTT_SIZE);
if (mtt) {
- pklm->key = cpu_to_be32(mtt->ibmr.lkey);
- pklm->va = cpu_to_be64(idx * MLX5_IMR_MTT_SIZE);
+ pksm->key = cpu_to_be32(mtt->ibmr.lkey);
+ pksm->va = cpu_to_be64(idx * mlx5_imr_mtt_size);
} else {
- pklm->key = key;
- pklm->va = cpu_to_be64(va);
+ pksm->key = key;
+ pksm->va = cpu_to_be64(va);
}
}
}
@@ -201,7 +195,7 @@ int mlx5_odp_populate_xlt(void *xlt, size_t idx, size_t nentries,
struct mlx5_ib_mr *mr, int flags)
{
if (flags & MLX5_IB_UPD_XLT_INDIRECT) {
- populate_klm(xlt, idx, nentries, mr, flags);
+ populate_ksm(xlt, idx, nentries, mr, flags);
return 0;
} else {
return populate_mtt(xlt, idx, nentries, mr, flags);
@@ -226,7 +220,7 @@ static void free_implicit_child_mr_work(struct work_struct *work)
mutex_lock(&odp_imr->umem_mutex);
mlx5r_umr_update_xlt(mr->parent,
- ib_umem_start(odp) >> MLX5_IMR_MTT_SHIFT, 1, 0,
+ ib_umem_start(odp) >> mlx5_imr_mtt_shift, 1, 0,
MLX5_IB_UPD_XLT_INDIRECT | MLX5_IB_UPD_XLT_ATOMIC);
mutex_unlock(&odp_imr->umem_mutex);
mlx5_ib_dereg_mr(&mr->ibmr, NULL);
@@ -237,7 +231,7 @@ static void free_implicit_child_mr_work(struct work_struct *work)
static void destroy_unused_implicit_child_mr(struct mlx5_ib_mr *mr)
{
struct ib_umem_odp *odp = to_ib_umem_odp(mr->umem);
- unsigned long idx = ib_umem_start(odp) >> MLX5_IMR_MTT_SHIFT;
+ unsigned long idx = ib_umem_start(odp) >> mlx5_imr_mtt_shift;
struct mlx5_ib_mr *imr = mr->parent;
/*
@@ -265,7 +259,7 @@ static void destroy_unused_implicit_child_mr(struct mlx5_ib_mr *mr)
/* Freeing a MR is a sleeping operation, so bounce to a work queue */
INIT_WORK(&mr->odp_destroy.work, free_implicit_child_mr_work);
- queue_work(system_unbound_wq, &mr->odp_destroy.work);
+ queue_work(system_dfl_wq, &mr->odp_destroy.work);
}
static bool mlx5_ib_invalidate_range(struct mmu_interval_notifier *mni,
@@ -425,7 +419,10 @@ static void internal_fill_odp_caps(struct mlx5_ib_dev *dev)
if (MLX5_CAP_GEN(dev->mdev, fixed_buffer_size) &&
MLX5_CAP_GEN(dev->mdev, null_mkey) &&
MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset) &&
- !MLX5_CAP_GEN(dev->mdev, umr_indirect_mkey_disabled))
+ !MLX5_CAP_GEN(dev->mdev, umr_indirect_mkey_disabled) &&
+ mlx5_imr_ksm_entries != 0 &&
+ !(mlx5_imr_ksm_page_shift >
+ get_max_log_entity_size_cap(dev, MLX5_MKC_ACCESS_MODE_KSM)))
caps->general_caps |= IB_ODP_SUPPORT_IMPLICIT;
}
@@ -476,14 +473,14 @@ static struct mlx5_ib_mr *implicit_get_child_mr(struct mlx5_ib_mr *imr,
int err;
odp = ib_umem_odp_alloc_child(to_ib_umem_odp(imr->umem),
- idx * MLX5_IMR_MTT_SIZE,
- MLX5_IMR_MTT_SIZE, &mlx5_mn_ops);
+ idx * mlx5_imr_mtt_size,
+ mlx5_imr_mtt_size, &mlx5_mn_ops);
if (IS_ERR(odp))
return ERR_CAST(odp);
mr = mlx5_mr_cache_alloc(dev, imr->access_flags,
MLX5_MKC_ACCESS_MODE_MTT,
- MLX5_IMR_MTT_ENTRIES);
+ mlx5_imr_mtt_entries);
if (IS_ERR(mr)) {
ib_umem_odp_release(odp);
return mr;
@@ -495,7 +492,7 @@ static struct mlx5_ib_mr *implicit_get_child_mr(struct mlx5_ib_mr *imr,
mr->umem = &odp->umem;
mr->ibmr.lkey = mr->mmkey.key;
mr->ibmr.rkey = mr->mmkey.key;
- mr->ibmr.iova = idx * MLX5_IMR_MTT_SIZE;
+ mr->ibmr.iova = idx * mlx5_imr_mtt_size;
mr->parent = imr;
odp->private = mr;
@@ -506,7 +503,7 @@ static struct mlx5_ib_mr *implicit_get_child_mr(struct mlx5_ib_mr *imr,
refcount_set(&mr->mmkey.usecount, 2);
err = mlx5r_umr_update_xlt(mr, 0,
- MLX5_IMR_MTT_ENTRIES,
+ mlx5_imr_mtt_entries,
PAGE_SHIFT,
MLX5_IB_UPD_XLT_ZAP |
MLX5_IB_UPD_XLT_ENABLE);
@@ -611,7 +608,7 @@ struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd,
struct mlx5_ib_mr *imr;
int err;
- if (!mlx5r_umr_can_load_pas(dev, MLX5_IMR_MTT_ENTRIES * PAGE_SIZE))
+ if (!mlx5r_umr_can_load_pas(dev, mlx5_imr_mtt_entries * PAGE_SIZE))
return ERR_PTR(-EOPNOTSUPP);
umem_odp = ib_umem_odp_alloc_implicit(&dev->ib_dev, access_flags);
@@ -647,7 +644,7 @@ struct mlx5_ib_mr *mlx5_ib_alloc_implicit_mr(struct mlx5_ib_pd *pd,
err = mlx5r_umr_update_xlt(imr, 0,
mlx5_imr_ksm_entries,
- MLX5_KSM_PAGE_SHIFT,
+ mlx5_imr_ksm_page_shift,
MLX5_IB_UPD_XLT_INDIRECT |
MLX5_IB_UPD_XLT_ZAP |
MLX5_IB_UPD_XLT_ENABLE);
@@ -750,20 +747,20 @@ static int pagefault_implicit_mr(struct mlx5_ib_mr *imr,
struct ib_umem_odp *odp_imr, u64 user_va,
size_t bcnt, u32 *bytes_mapped, u32 flags)
{
- unsigned long end_idx = (user_va + bcnt - 1) >> MLX5_IMR_MTT_SHIFT;
+ unsigned long end_idx = (user_va + bcnt - 1) >> mlx5_imr_mtt_shift;
unsigned long upd_start_idx = end_idx + 1;
unsigned long upd_len = 0;
unsigned long npages = 0;
int err;
int ret;
- if (unlikely(user_va >= mlx5_imr_ksm_entries * MLX5_IMR_MTT_SIZE ||
- mlx5_imr_ksm_entries * MLX5_IMR_MTT_SIZE - user_va < bcnt))
+ if (unlikely(user_va >= mlx5_imr_ksm_entries * mlx5_imr_mtt_size ||
+ mlx5_imr_ksm_entries * mlx5_imr_mtt_size - user_va < bcnt))
return -EFAULT;
/* Fault each child mr that intersects with our interval. */
while (bcnt) {
- unsigned long idx = user_va >> MLX5_IMR_MTT_SHIFT;
+ unsigned long idx = user_va >> mlx5_imr_mtt_shift;
struct ib_umem_odp *umem_odp;
struct mlx5_ib_mr *mtt;
u64 len;
@@ -1924,9 +1921,25 @@ void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *dev)
int mlx5_ib_odp_init(void)
{
+ u32 log_va_pages = ilog2(TASK_SIZE) - PAGE_SHIFT;
+ u8 mlx5_imr_mtt_bits;
+
+ /* 48 is default ARM64 VA space and covers X86 4-level paging which is 47 */
+ if (log_va_pages <= 48 - PAGE_SHIFT)
+ mlx5_imr_mtt_shift = 30;
+ /* 56 is x86-64, 5-level paging */
+ else if (log_va_pages <= 56 - PAGE_SHIFT)
+ mlx5_imr_mtt_shift = 34;
+ else
+ return 0;
+
+ mlx5_imr_mtt_size = BIT_ULL(mlx5_imr_mtt_shift);
+ mlx5_imr_mtt_bits = mlx5_imr_mtt_shift - PAGE_SHIFT;
+ mlx5_imr_mtt_entries = BIT_ULL(mlx5_imr_mtt_bits);
mlx5_imr_ksm_entries = BIT_ULL(get_order(TASK_SIZE) -
- MLX5_IMR_MTT_BITS);
+ mlx5_imr_mtt_bits);
+ mlx5_imr_ksm_page_shift = mlx5_imr_mtt_shift;
return 0;
}
@@ -2093,6 +2106,6 @@ int mlx5_ib_advise_mr_prefetch(struct ib_pd *pd,
destroy_prefetch_work(work);
return rc;
}
- queue_work(system_unbound_wq, &work->work);
+ queue_work(system_dfl_wq, &work->work);
return 0;
}
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 88724d15705d..69af20790481 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -3451,10 +3451,11 @@ int mlx5r_ib_rate(struct mlx5_ib_dev *dev, u8 rate)
{
u32 stat_rate_support;
- if (rate == IB_RATE_PORT_CURRENT || rate == IB_RATE_800_GBPS)
+ if (rate == IB_RATE_PORT_CURRENT || rate == IB_RATE_800_GBPS ||
+ rate == IB_RATE_1600_GBPS)
return 0;
- if (rate < IB_RATE_2_5_GBPS || rate > IB_RATE_800_GBPS)
+ if (rate < IB_RATE_2_5_GBPS || rate > IB_RATE_1600_GBPS)
return -EINVAL;
stat_rate_support = MLX5_CAP_GEN(dev->mdev, stat_rate_support);
diff --git a/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h b/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h
index 1d7fc3226bca..cfb42a8f5768 100644
--- a/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h
+++ b/drivers/infiniband/hw/usnic/usnic_uiom_interval_tree.h
@@ -53,6 +53,10 @@ extern void
usnic_uiom_interval_tree_remove(struct usnic_uiom_interval_node *node,
struct rb_root_cached *root);
extern struct usnic_uiom_interval_node *
+usnic_uiom_interval_tree_subtree_search(struct usnic_uiom_interval_node *node,
+ unsigned long start,
+ unsigned long last);
+extern struct usnic_uiom_interval_node *
usnic_uiom_interval_tree_iter_first(struct rb_root_cached *root,
unsigned long start,
unsigned long last);
diff --git a/drivers/infiniband/sw/rdmavt/cq.c b/drivers/infiniband/sw/rdmavt/cq.c
index 0ca2743f1075..e7835ca70e2b 100644
--- a/drivers/infiniband/sw/rdmavt/cq.c
+++ b/drivers/infiniband/sw/rdmavt/cq.c
@@ -518,7 +518,8 @@ int rvt_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
*/
int rvt_driver_cq_init(void)
{
- comp_vector_wq = alloc_workqueue("%s", WQ_HIGHPRI | WQ_CPU_INTENSIVE,
+ comp_vector_wq = alloc_workqueue("%s",
+ WQ_HIGHPRI | WQ_CPU_INTENSIVE | WQ_PERCPU,
0, "rdmavt_cq");
if (!comp_vector_wq)
return -ENOMEM;
diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c
index bcb97b3ea58a..b1df05238848 100644
--- a/drivers/infiniband/sw/rxe/rxe_mr.c
+++ b/drivers/infiniband/sw/rxe/rxe_mr.c
@@ -452,7 +452,6 @@ static int rxe_mr_flush_pmem_iova(struct rxe_mr *mr, u64 iova, unsigned int leng
length -= bytes;
iova += bytes;
- page_offset = 0;
}
return 0;
diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c
index ac0183a2ff7a..0195d361e5e3 100644
--- a/drivers/infiniband/sw/rxe/rxe_net.c
+++ b/drivers/infiniband/sw/rxe/rxe_net.c
@@ -20,6 +20,54 @@
static struct rxe_recv_sockets recv_sockets;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+/*
+ * lockdep can detect false positive circular dependencies
+ * when there are user-space socket API users or in kernel
+ * users switching between a tcp and rdma transport.
+ * Maybe also switching between siw and rxe may cause
+ * problems as per default sockets are only classified
+ * by family and not by ip protocol. And there might
+ * be different locks used between the application
+ * and the low level sockets.
+ *
+ * Problems were seen with ksmbd.ko and cifs.ko,
+ * switching transports, use git blame to find
+ * more details.
+ */
+static struct lock_class_key rxe_recv_sk_key[2];
+static struct lock_class_key rxe_recv_slock_key[2];
+#endif /* CONFIG_DEBUG_LOCK_ALLOC */
+
+static inline void rxe_reclassify_recv_socket(struct socket *sock)
+{
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct sock *sk = sock->sk;
+
+ if (WARN_ON_ONCE(!sock_allow_reclassification(sk)))
+ return;
+
+ switch (sk->sk_family) {
+ case AF_INET:
+ sock_lock_init_class_and_name(sk,
+ "slock-AF_INET-RDMA-RXE-RECV",
+ &rxe_recv_slock_key[0],
+ "sk_lock-AF_INET-RDMA-RXE-RECV",
+ &rxe_recv_sk_key[0]);
+ break;
+ case AF_INET6:
+ sock_lock_init_class_and_name(sk,
+ "slock-AF_INET6-RDMA-RXE-RECV",
+ &rxe_recv_slock_key[1],
+ "sk_lock-AF_INET6-RDMA-RXE-RECV",
+ &rxe_recv_sk_key[1]);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+#endif /* CONFIG_DEBUG_LOCK_ALLOC */
+}
+
static struct dst_entry *rxe_find_route4(struct rxe_qp *qp,
struct net_device *ndev,
struct in_addr *saddr,
@@ -192,6 +240,7 @@ static struct socket *rxe_setup_udp_tunnel(struct net *net, __be16 port,
err = udp_sock_create(net, &udp_cfg, &sock);
if (err < 0)
return ERR_PTR(err);
+ rxe_reclassify_recv_socket(sock);
tnl_cfg.encap_type = 1;
tnl_cfg.encap_rcv = rxe_udp_encap_recv;
diff --git a/drivers/infiniband/sw/rxe/rxe_odp.c b/drivers/infiniband/sw/rxe/rxe_odp.c
index f58e3ec6252f..ae71812bea82 100644
--- a/drivers/infiniband/sw/rxe/rxe_odp.c
+++ b/drivers/infiniband/sw/rxe/rxe_odp.c
@@ -358,7 +358,6 @@ int rxe_odp_flush_pmem_iova(struct rxe_mr *mr, u64 iova,
length -= bytes;
iova += bytes;
- page_offset = 0;
}
mutex_unlock(&umem_odp->umem_mutex);
diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c
index 95f1c1c2949d..845bdd03ca28 100644
--- a/drivers/infiniband/sw/rxe/rxe_qp.c
+++ b/drivers/infiniband/sw/rxe/rxe_qp.c
@@ -15,6 +15,54 @@
#include "rxe_queue.h"
#include "rxe_task.h"
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+/*
+ * lockdep can detect false positive circular dependencies
+ * when there are user-space socket API users or in kernel
+ * users switching between a tcp and rdma transport.
+ * Maybe also switching between siw and rxe may cause
+ * problems as per default sockets are only classified
+ * by family and not by ip protocol. And there might
+ * be different locks used between the application
+ * and the low level sockets.
+ *
+ * Problems were seen with ksmbd.ko and cifs.ko,
+ * switching transports, use git blame to find
+ * more details.
+ */
+static struct lock_class_key rxe_send_sk_key[2];
+static struct lock_class_key rxe_send_slock_key[2];
+#endif /* CONFIG_DEBUG_LOCK_ALLOC */
+
+static inline void rxe_reclassify_send_socket(struct socket *sock)
+{
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct sock *sk = sock->sk;
+
+ if (WARN_ON_ONCE(!sock_allow_reclassification(sk)))
+ return;
+
+ switch (sk->sk_family) {
+ case AF_INET:
+ sock_lock_init_class_and_name(sk,
+ "slock-AF_INET-RDMA-RXE-SEND",
+ &rxe_send_slock_key[0],
+ "sk_lock-AF_INET-RDMA-RXE-SEND",
+ &rxe_send_sk_key[0]);
+ break;
+ case AF_INET6:
+ sock_lock_init_class_and_name(sk,
+ "slock-AF_INET6-RDMA-RXE-SEND",
+ &rxe_send_slock_key[1],
+ "sk_lock-AF_INET6-RDMA-RXE-SEND",
+ &rxe_send_sk_key[1]);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+#endif /* CONFIG_DEBUG_LOCK_ALLOC */
+}
+
static int rxe_qp_chk_cap(struct rxe_dev *rxe, struct ib_qp_cap *cap,
int has_srq)
{
@@ -244,6 +292,7 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp,
err = sock_create_kern(&init_net, AF_INET, SOCK_DGRAM, 0, &qp->sk);
if (err < 0)
return err;
+ rxe_reclassify_send_socket(qp->sk);
qp->sk->sk->sk_user_data = qp;
/* pick a source UDP port number for this QP based on
diff --git a/drivers/infiniband/sw/rxe/rxe_srq.c b/drivers/infiniband/sw/rxe/rxe_srq.c
index 3661cb627d28..2a234f26ac10 100644
--- a/drivers/infiniband/sw/rxe/rxe_srq.c
+++ b/drivers/infiniband/sw/rxe/rxe_srq.c
@@ -171,7 +171,7 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
udata, mi, &srq->rq.producer_lock,
&srq->rq.consumer_lock);
if (err)
- goto err_free;
+ return err;
srq->rq.max_wr = attr->max_wr;
}
@@ -180,11 +180,6 @@ int rxe_srq_from_attr(struct rxe_dev *rxe, struct rxe_srq *srq,
srq->limit = attr->srq_limit;
return 0;
-
-err_free:
- rxe_queue_cleanup(q);
- srq->rq.queue = NULL;
- return err;
}
void rxe_srq_cleanup(struct rxe_pool_elem *elem)
diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c
index 708b13993fdf..1d3de8209bfa 100644
--- a/drivers/infiniband/sw/siw/siw_cm.c
+++ b/drivers/infiniband/sw/siw/siw_cm.c
@@ -39,6 +39,55 @@ static void siw_cm_llp_error_report(struct sock *s);
static int siw_cm_upcall(struct siw_cep *cep, enum iw_cm_event_type reason,
int status);
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+/*
+ * lockdep can detect false positive circular dependencies
+ * when there are user-space socket API users or in kernel
+ * users switching between a tcp and rdma transport.
+ * Maybe also switching between siw and rxe may cause
+ * problems as per default sockets are only classified
+ * by family and not by ip protocol. And there might
+ * be different locks used between the application
+ * and the low level sockets.
+ *
+ * Problems were seen with ksmbd.ko and cifs.ko,
+ * switching transports, use git blame to find
+ * more details.
+ */
+static struct lock_class_key siw_sk_key[2];
+static struct lock_class_key siw_slock_key[2];
+#endif /* CONFIG_DEBUG_LOCK_ALLOC */
+
+static inline void siw_reclassify_socket(struct socket *sock)
+{
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ struct sock *sk = sock->sk;
+
+ if (WARN_ON_ONCE(!sock_allow_reclassification(sk)))
+ return;
+
+ switch (sk->sk_family) {
+ case AF_INET:
+ sock_lock_init_class_and_name(sk,
+ "slock-AF_INET-RDMA-SIW",
+ &siw_slock_key[0],
+ "sk_lock-AF_INET-RDMA-SIW",
+ &siw_sk_key[0]);
+ break;
+ case AF_INET6:
+ sock_lock_init_class_and_name(sk,
+ "slock-AF_INET6-RDMA-SIW",
+ &siw_slock_key[1],
+ "sk_lock-AF_INET6-RDMA-SIW",
+ &siw_sk_key[1]);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+#endif /* CONFIG_DEBUG_LOCK_ALLOC */
+}
+
static void siw_sk_assign_cm_upcalls(struct sock *sk)
{
struct siw_cep *cep = sk_to_cep(sk);
@@ -1340,11 +1389,11 @@ static int kernel_bindconnect(struct socket *s, struct sockaddr *laddr,
return rv;
}
- rv = s->ops->bind(s, laddr, size);
+ rv = s->ops->bind(s, (struct sockaddr_unsized *)laddr, size);
if (rv < 0)
return rv;
- rv = s->ops->connect(s, raddr, size, flags);
+ rv = s->ops->connect(s, (struct sockaddr_unsized *)raddr, size, flags);
return rv < 0 ? rv : 0;
}
@@ -1394,6 +1443,7 @@ int siw_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params)
rv = sock_create(v4 ? AF_INET : AF_INET6, SOCK_STREAM, IPPROTO_TCP, &s);
if (rv < 0)
goto error;
+ siw_reclassify_socket(s);
/*
* NOTE: For simplification, connect() is called in blocking
@@ -1770,6 +1820,7 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
rv = sock_create(addr_family, SOCK_STREAM, IPPROTO_TCP, &s);
if (rv < 0)
return rv;
+ siw_reclassify_socket(s);
/*
* Allow binding local port when still in TIME_WAIT from last close.
@@ -1789,7 +1840,7 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
goto error;
}
}
- rv = s->ops->bind(s, (struct sockaddr *)laddr,
+ rv = s->ops->bind(s, (struct sockaddr_unsized *)laddr,
sizeof(struct sockaddr_in));
} else {
struct sockaddr_in6 *laddr = &to_sockaddr_in6(id->local_addr);
@@ -1813,7 +1864,7 @@ int siw_create_listen(struct iw_cm_id *id, int backlog)
goto error;
}
}
- rv = s->ops->bind(s, (struct sockaddr *)laddr,
+ rv = s->ops->bind(s, (struct sockaddr_unsized *)laddr,
sizeof(struct sockaddr_in6));
}
if (rv) {
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 5b4d76e97437..300afc27c561 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1825,6 +1825,31 @@ static int ipoib_ioctl(struct net_device *dev, struct ifreq *ifr,
return priv->rn_ops->ndo_eth_ioctl(dev, ifr, cmd);
}
+static int ipoib_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct ipoib_dev_priv *priv = ipoib_priv(dev);
+
+ if (!priv->rn_ops->ndo_hwtstamp_get)
+ /* legacy */
+ return dev_eth_ioctl(dev, config->ifr, SIOCGHWTSTAMP);
+
+ return priv->rn_ops->ndo_hwtstamp_get(dev, config);
+}
+
+static int ipoib_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct ipoib_dev_priv *priv = ipoib_priv(dev);
+
+ if (!priv->rn_ops->ndo_hwtstamp_set)
+ /* legacy */
+ return dev_eth_ioctl(dev, config->ifr, SIOCSHWTSTAMP);
+
+ return priv->rn_ops->ndo_hwtstamp_set(dev, config, extack);
+}
+
static int ipoib_dev_init(struct net_device *dev)
{
struct ipoib_dev_priv *priv = ipoib_priv(dev);
@@ -2149,6 +2174,8 @@ static const struct net_device_ops ipoib_netdev_ops_pf = {
.ndo_set_mac_address = ipoib_set_mac,
.ndo_get_stats64 = ipoib_get_stats,
.ndo_eth_ioctl = ipoib_ioctl,
+ .ndo_hwtstamp_get = ipoib_hwtstamp_get,
+ .ndo_hwtstamp_set = ipoib_hwtstamp_set,
};
static const struct net_device_ops ipoib_netdev_ops_vf = {
@@ -2164,6 +2191,8 @@ static const struct net_device_ops ipoib_netdev_ops_vf = {
.ndo_get_iflink = ipoib_get_iflink,
.ndo_get_stats64 = ipoib_get_stats,
.ndo_eth_ioctl = ipoib_ioctl,
+ .ndo_hwtstamp_get = ipoib_hwtstamp_get,
+ .ndo_hwtstamp_set = ipoib_hwtstamp_set,
};
static const struct net_device_ops ipoib_netdev_default_pf = {
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 2e3c0516ce8f..dc531fad73de 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -1029,7 +1029,7 @@ static int __init iser_init(void)
mutex_init(&ig.connlist_mutex);
INIT_LIST_HEAD(&ig.connlist);
- release_wq = alloc_workqueue("release workqueue", 0, 0);
+ release_wq = alloc_workqueue("release workqueue", WQ_PERCPU, 0);
if (!release_wq) {
iser_err("failed to allocate release workqueue\n");
err = -ENOMEM;
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 42977a5326ee..af811d060cc8 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -2613,7 +2613,7 @@ static struct iscsit_transport iser_target_transport = {
static int __init isert_init(void)
{
- isert_login_wq = alloc_workqueue("isert_login_wq", 0, 0);
+ isert_login_wq = alloc_workqueue("isert_login_wq", WQ_PERCPU, 0);
if (!isert_login_wq) {
isert_err("Unable to allocate isert_login_wq\n");
return -ENOMEM;
diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c
index ef4abdea3c2d..9ecc6343455d 100644
--- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c
+++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c
@@ -1450,7 +1450,7 @@ err_free_chunks:
kfree(srv->chunks);
err_free_srv:
- kfree(srv);
+ put_device(&srv->dev);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index f7209c8ebbcc..1c6b0461dc35 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -261,6 +261,12 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
case EC_MKBP_EVENT_KEY_MATRIX:
pm_wakeup_event(ckdev->dev, 0);
+ if (!ckdev->idev) {
+ dev_warn_once(ckdev->dev,
+ "Unexpected key matrix event\n");
+ return NOTIFY_OK;
+ }
+
if (ckdev->ec->event_size != ckdev->cols) {
dev_err(ckdev->dev,
"Discarded incomplete key matrix event.\n");
diff --git a/drivers/input/keyboard/imx_sc_key.c b/drivers/input/keyboard/imx_sc_key.c
index d18839f1f4f6..b620cd310cdb 100644
--- a/drivers/input/keyboard/imx_sc_key.c
+++ b/drivers/input/keyboard/imx_sc_key.c
@@ -158,7 +158,7 @@ static int imx_sc_key_probe(struct platform_device *pdev)
return error;
}
- error = devm_add_action_or_reset(&pdev->dev, imx_sc_key_action, &priv);
+ error = devm_add_action_or_reset(&pdev->dev, imx_sc_key_action, priv);
if (error)
return error;
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index cc2558630797..94a753fcb64f 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -190,6 +190,17 @@ config INPUT_PCSPKR
To compile this driver as a module, choose M here: the
module will be called pcspkr.
+config INPUT_PF1550_ONKEY
+ tristate "NXP PF1550 Onkey support"
+ depends on MFD_PF1550
+ help
+ Say Y here if you want support for PF1550 PMIC. Onkey can trigger
+ release and 1s(push hold), 2s, 3s, 4s, 8s interrupt for long press
+ detect.
+
+ To compile this driver as a module, choose M here. The module will be
+ called pf1550-onkey.
+
config INPUT_PM8941_PWRKEY
tristate "Qualcomm PM8941 power key support"
depends on MFD_SPMI_PMIC
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index f5ebfa9d9983..415fc4e2918b 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
+obj-$(CONFIG_INPUT_PF1550_ONKEY) += pf1550-onkey.o
obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o
obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
index 5fa1c9438a85..bb1544d63c51 100644
--- a/drivers/input/misc/arizona-haptics.c
+++ b/drivers/input/misc/arizona-haptics.c
@@ -34,8 +34,6 @@ static void arizona_haptics_work(struct work_struct *work)
struct arizona_haptics,
work);
struct arizona *arizona = haptics->arizona;
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(arizona->dapm);
int ret;
if (!haptics->arizona->dapm) {
@@ -65,7 +63,7 @@ static void arizona_haptics_work(struct work_struct *work)
return;
}
- ret = snd_soc_component_enable_pin(component, "HAPTICS");
+ ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
if (ret != 0) {
dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
ret);
@@ -80,7 +78,7 @@ static void arizona_haptics_work(struct work_struct *work)
}
} else {
/* This disable sequence will be a noop if already enabled */
- ret = snd_soc_component_disable_pin(component, "HAPTICS");
+ ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
if (ret != 0) {
dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
ret);
@@ -139,14 +137,12 @@ static int arizona_haptics_play(struct input_dev *input, void *data,
static void arizona_haptics_close(struct input_dev *input)
{
struct arizona_haptics *haptics = input_get_drvdata(input);
- struct snd_soc_component *component;
+ struct snd_soc_dapm_context *dapm = haptics->arizona->dapm;
cancel_work_sync(&haptics->work);
- if (haptics->arizona->dapm) {
- component = snd_soc_dapm_to_component(haptics->arizona->dapm);
- snd_soc_component_disable_pin(component, "HAPTICS");
- }
+ if (dapm)
+ snd_soc_dapm_disable_pin(dapm, "HAPTICS");
}
static int arizona_haptics_probe(struct platform_device *pdev)
diff --git a/drivers/input/misc/pf1550-onkey.c b/drivers/input/misc/pf1550-onkey.c
new file mode 100644
index 000000000000..9be6377151cb
--- /dev/null
+++ b/drivers/input/misc/pf1550-onkey.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for the PF1550 ONKEY
+ * Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
+ * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
+ */
+
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/pf1550.h>
+#include <linux/platform_device.h>
+
+#define PF1550_ONKEY_IRQ_NR 6
+
+struct onkey_drv_data {
+ struct device *dev;
+ const struct pf1550_ddata *pf1550;
+ bool wakeup;
+ struct input_dev *input;
+};
+
+static irqreturn_t pf1550_onkey_irq_handler(int irq, void *data)
+{
+ struct onkey_drv_data *onkey = data;
+ struct platform_device *pdev = to_platform_device(onkey->dev);
+ int i, state, irq_type = -1;
+
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++)
+ if (irq == platform_get_irq(pdev, i))
+ irq_type = i;
+
+ switch (irq_type) {
+ case PF1550_ONKEY_IRQ_PUSHI:
+ state = 0;
+ break;
+ case PF1550_ONKEY_IRQ_1SI:
+ case PF1550_ONKEY_IRQ_2SI:
+ case PF1550_ONKEY_IRQ_3SI:
+ case PF1550_ONKEY_IRQ_4SI:
+ case PF1550_ONKEY_IRQ_8SI:
+ state = 1;
+ break;
+ default:
+ dev_err(onkey->dev, "onkey interrupt: irq %d occurred\n",
+ irq_type);
+ return IRQ_HANDLED;
+ }
+
+ input_event(onkey->input, EV_KEY, KEY_POWER, state);
+ input_sync(onkey->input);
+
+ return IRQ_HANDLED;
+}
+
+static int pf1550_onkey_probe(struct platform_device *pdev)
+{
+ struct onkey_drv_data *onkey;
+ struct input_dev *input;
+ bool key_power = false;
+ int i, irq, error;
+
+ onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey)
+ return -ENOMEM;
+
+ onkey->dev = &pdev->dev;
+
+ onkey->pf1550 = dev_get_drvdata(pdev->dev.parent);
+ if (!onkey->pf1550->regmap)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "failed to get regmap\n");
+
+ onkey->wakeup = device_property_read_bool(pdev->dev.parent,
+ "wakeup-source");
+
+ if (device_property_read_bool(pdev->dev.parent,
+ "nxp,disable-key-power")) {
+ error = regmap_clear_bits(onkey->pf1550->regmap,
+ PF1550_PMIC_REG_PWRCTRL1,
+ PF1550_ONKEY_RST_EN);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "failed: disable turn system off");
+ } else {
+ key_power = true;
+ }
+
+ input = devm_input_allocate_device(&pdev->dev);
+ if (!input)
+ return dev_err_probe(&pdev->dev, -ENOMEM,
+ "failed to allocate the input device\n");
+
+ input->name = pdev->name;
+ input->phys = "pf1550-onkey/input0";
+ input->id.bustype = BUS_HOST;
+
+ if (key_power)
+ input_set_capability(input, EV_KEY, KEY_POWER);
+
+ onkey->input = input;
+ platform_set_drvdata(pdev, onkey);
+
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ error = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ pf1550_onkey_irq_handler,
+ IRQF_NO_SUSPEND,
+ "pf1550-onkey", onkey);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "failed: irq request (IRQ: %d)\n",
+ i);
+ }
+
+ error = input_register_device(input);
+ if (error)
+ return dev_err_probe(&pdev->dev, error,
+ "failed to register input device\n");
+
+ device_init_wakeup(&pdev->dev, onkey->wakeup);
+
+ return 0;
+}
+
+static int pf1550_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
+ int i, irq;
+
+ if (!device_may_wakeup(&pdev->dev))
+ regmap_write(onkey->pf1550->regmap,
+ PF1550_PMIC_REG_ONKEY_INT_MASK0,
+ ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI | ONKEY_IRQ_2SI |
+ ONKEY_IRQ_3SI | ONKEY_IRQ_4SI | ONKEY_IRQ_8SI);
+ else
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ enable_irq_wake(irq);
+ }
+
+ return 0;
+}
+
+static int pf1550_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct onkey_drv_data *onkey = platform_get_drvdata(pdev);
+ int i, irq;
+
+ if (!device_may_wakeup(&pdev->dev))
+ regmap_write(onkey->pf1550->regmap,
+ PF1550_PMIC_REG_ONKEY_INT_MASK0,
+ ~((u8)(ONKEY_IRQ_PUSHI | ONKEY_IRQ_1SI |
+ ONKEY_IRQ_2SI | ONKEY_IRQ_3SI | ONKEY_IRQ_4SI |
+ ONKEY_IRQ_8SI)));
+ else
+ for (i = 0; i < PF1550_ONKEY_IRQ_NR; i++) {
+ irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ disable_irq_wake(irq);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pf1550_onkey_pm_ops, pf1550_onkey_suspend,
+ pf1550_onkey_resume);
+
+static const struct platform_device_id pf1550_onkey_id[] = {
+ { "pf1550-onkey", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, pf1550_onkey_id);
+
+static struct platform_driver pf1550_onkey_driver = {
+ .driver = {
+ .name = "pf1550-onkey",
+ .pm = pm_sleep_ptr(&pf1550_onkey_pm_ops),
+ },
+ .probe = pf1550_onkey_probe,
+ .id_table = pf1550_onkey_id,
+};
+module_platform_driver(pf1550_onkey_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("PF1550 onkey Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/tablet/pegasus_notetaker.c b/drivers/input/tablet/pegasus_notetaker.c
index 8d6b71d59793..eabb4a0b8a0d 100644
--- a/drivers/input/tablet/pegasus_notetaker.c
+++ b/drivers/input/tablet/pegasus_notetaker.c
@@ -63,6 +63,9 @@
#define BUTTON_PRESSED 0xb5
#define COMMAND_VERSION 0xa9
+/* 1 Status + 1 Color + 2 X + 2 Y = 6 bytes */
+#define NOTETAKER_PACKET_SIZE 6
+
/* in xy data packet */
#define BATTERY_NO_REPORT 0x40
#define BATTERY_LOW 0x41
@@ -311,6 +314,12 @@ static int pegasus_probe(struct usb_interface *intf,
}
pegasus->data_len = usb_maxpacket(dev, pipe);
+ if (pegasus->data_len < NOTETAKER_PACKET_SIZE) {
+ dev_err(&intf->dev, "packet size is too small (%d)\n",
+ pegasus->data_len);
+ error = -EINVAL;
+ goto err_free_mem;
+ }
pegasus->data = usb_alloc_coherent(dev, pegasus->data_len, GFP_KERNEL,
&pegasus->data_dma);
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 252dcae039f8..f8798d11ec03 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -796,17 +796,6 @@ int goodix_reset_no_int_sync(struct goodix_ts_data *ts)
usleep_range(6000, 10000); /* T4: > 5ms */
- /*
- * Put the reset pin back in to input / high-impedance mode to save
- * power. Only do this in the non ACPI case since some ACPI boards
- * don't have a pull-up, so there the reset pin must stay active-high.
- */
- if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_GPIO) {
- error = gpiod_direction_input(ts->gpiod_rst);
- if (error)
- goto error;
- }
-
return 0;
error:
@@ -957,14 +946,6 @@ static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)
return -EINVAL;
}
- /*
- * Normally we put the reset pin in input / high-impedance mode to save
- * power. But some x86/ACPI boards don't have a pull-up, so for the ACPI
- * case, leave the pin as is. This results in the pin not being touched
- * at all on x86/ACPI boards, except when needed for error-recover.
- */
- ts->gpiod_rst_flags = GPIOD_ASIS;
-
return devm_acpi_dev_add_driver_gpios(dev, gpio_mapping);
}
#else
@@ -989,12 +970,6 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
return -EINVAL;
dev = &ts->client->dev;
- /*
- * By default we request the reset pin as input, leaving it in
- * high-impedance when not resetting the controller to save power.
- */
- ts->gpiod_rst_flags = GPIOD_IN;
-
ts->avdd28 = devm_regulator_get(dev, "AVDD28");
if (IS_ERR(ts->avdd28))
return dev_err_probe(dev, PTR_ERR(ts->avdd28), "Failed to get AVDD28 regulator\n");
@@ -1019,7 +994,7 @@ retry_get_irq_gpio:
ts->gpiod_int = gpiod;
/* Get the reset line GPIO pin number */
- gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, ts->gpiod_rst_flags);
+ gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_ASIS);
if (IS_ERR(gpiod))
return dev_err_probe(dev, PTR_ERR(gpiod), "Failed to get %s GPIO\n",
GOODIX_GPIO_RST_NAME);
@@ -1557,6 +1532,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
static const struct acpi_device_id goodix_acpi_match[] = {
{ "GDIX1001", 0 },
{ "GDIX1002", 0 },
+ { "GDIX1003", 0 },
{ "GDX9110", 0 },
{ }
};
diff --git a/drivers/input/touchscreen/goodix.h b/drivers/input/touchscreen/goodix.h
index 87797cc88b32..0d1e8a8d2cba 100644
--- a/drivers/input/touchscreen/goodix.h
+++ b/drivers/input/touchscreen/goodix.h
@@ -88,7 +88,6 @@ struct goodix_ts_data {
struct gpio_desc *gpiod_rst;
int gpio_count;
int gpio_int_idx;
- enum gpiod_flags gpiod_rst_flags;
char id[GOODIX_ID_MAX_LEN + 1];
char cfg_name[64];
u16 version;
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 70d29b14d851..99095645134f 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -40,12 +40,13 @@ config IOMMU_IO_PGTABLE_LPAE
sizes at both stage-1 and stage-2, as well as address spaces
up to 48-bits in size.
-config IOMMU_IO_PGTABLE_LPAE_SELFTEST
- bool "LPAE selftests"
- depends on IOMMU_IO_PGTABLE_LPAE
+config IOMMU_IO_PGTABLE_LPAE_KUNIT_TEST
+ tristate "KUnit tests for LPAE"
+ depends on IOMMU_IO_PGTABLE_LPAE && KUNIT
+ default KUNIT_ALL_TESTS
help
- Enable self-tests for LPAE page table allocator. This performs
- a series of page-table consistency checks during boot.
+ Enable kunit tests for LPAE page table allocator. This performs
+ a series of page-table consistency checks.
If unsure, say N here.
@@ -247,7 +248,7 @@ config SUN50I_IOMMU
config TEGRA_IOMMU_SMMU
bool "NVIDIA Tegra SMMU Support"
- depends on ARCH_TEGRA
+ depends on ARCH_TEGRA || COMPILE_TEST
depends on TEGRA_AHB
depends on TEGRA_MC
select IOMMU_API
@@ -384,3 +385,5 @@ config SPRD_IOMMU
Say Y here if you want to use the multimedia devices listed above.
endif # IOMMU_SUPPORT
+
+source "drivers/iommu/generic_pt/Kconfig"
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 355294fa9033..8e8843316c4b 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,6 +3,7 @@ obj-y += arm/ iommufd/
obj-$(CONFIG_AMD_IOMMU) += amd/
obj-$(CONFIG_INTEL_IOMMU) += intel/
obj-$(CONFIG_RISCV_IOMMU) += riscv/
+obj-$(CONFIG_GENERIC_PT) += generic_pt/fmt/
obj-$(CONFIG_IOMMU_API) += iommu.o
obj-$(CONFIG_IOMMU_SUPPORT) += iommu-pages.o
obj-$(CONFIG_IOMMU_API) += iommu-traces.o
@@ -12,6 +13,7 @@ obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o
obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
+obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE_KUNIT_TEST) += io-pgtable-arm-selftests.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_DART) += io-pgtable-dart.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
diff --git a/drivers/iommu/amd/Kconfig b/drivers/iommu/amd/Kconfig
index ecef69c11144..f2acf471cb5d 100644
--- a/drivers/iommu/amd/Kconfig
+++ b/drivers/iommu/amd/Kconfig
@@ -11,10 +11,13 @@ config AMD_IOMMU
select MMU_NOTIFIER
select IOMMU_API
select IOMMU_IOVA
- select IOMMU_IO_PGTABLE
select IOMMU_SVA
select IOMMU_IOPF
select IOMMUFD_DRIVER if IOMMUFD
+ select GENERIC_PT
+ select IOMMU_PT
+ select IOMMU_PT_AMDV1
+ select IOMMU_PT_X86_64
depends on X86_64 && PCI && ACPI && HAVE_CMPXCHG_DOUBLE
help
With this option you can enable support for AMD IOMMU hardware in
diff --git a/drivers/iommu/amd/Makefile b/drivers/iommu/amd/Makefile
index 59c04a67f398..5412a563c697 100644
--- a/drivers/iommu/amd/Makefile
+++ b/drivers/iommu/amd/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += iommu.o init.o quirks.o io_pgtable.o io_pgtable_v2.o ppr.o pasid.o
+obj-y += iommu.o init.o quirks.o ppr.o pasid.o
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += debugfs.o
diff --git a/drivers/iommu/amd/amd_iommu.h b/drivers/iommu/amd/amd_iommu.h
index 9b4b589a54b5..25044d28f28a 100644
--- a/drivers/iommu/amd/amd_iommu.h
+++ b/drivers/iommu/amd/amd_iommu.h
@@ -88,7 +88,6 @@ int amd_iommu_complete_ppr(struct device *dev, u32 pasid, int status, int tag);
* the IOMMU used by this driver.
*/
void amd_iommu_flush_all_caches(struct amd_iommu *iommu);
-void amd_iommu_update_and_flush_device_table(struct protection_domain *domain);
void amd_iommu_domain_flush_pages(struct protection_domain *domain,
u64 address, size_t size);
void amd_iommu_dev_flush_pasid_pages(struct iommu_dev_data *dev_data,
diff --git a/drivers/iommu/amd/amd_iommu_types.h b/drivers/iommu/amd/amd_iommu_types.h
index a698a2e7ce2a..78b1c44bd6b5 100644
--- a/drivers/iommu/amd/amd_iommu_types.h
+++ b/drivers/iommu/amd/amd_iommu_types.h
@@ -18,7 +18,7 @@
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <linux/irqreturn.h>
-#include <linux/io-pgtable.h>
+#include <linux/generic_pt/iommu.h>
/*
* Maximum number of IOMMUs supported
@@ -247,6 +247,10 @@
#define CMD_BUFFER_ENTRIES 512
#define MMIO_CMD_SIZE_SHIFT 56
#define MMIO_CMD_SIZE_512 (0x9ULL << MMIO_CMD_SIZE_SHIFT)
+#define MMIO_CMD_HEAD_MASK GENMASK_ULL(18, 4) /* Command buffer head ptr field [18:4] */
+#define MMIO_CMD_BUFFER_HEAD(x) FIELD_GET(MMIO_CMD_HEAD_MASK, (x))
+#define MMIO_CMD_TAIL_MASK GENMASK_ULL(18, 4) /* Command buffer tail ptr field [18:4] */
+#define MMIO_CMD_BUFFER_TAIL(x) FIELD_GET(MMIO_CMD_TAIL_MASK, (x))
/* constants for event buffer handling */
#define EVT_BUFFER_SIZE 8192 /* 512 entries */
@@ -337,76 +341,7 @@
#define GUEST_PGTABLE_4_LEVEL 0x00
#define GUEST_PGTABLE_5_LEVEL 0x01
-#define PM_LEVEL_SHIFT(x) (12 + ((x) * 9))
-#define PM_LEVEL_SIZE(x) (((x) < 6) ? \
- ((1ULL << PM_LEVEL_SHIFT((x))) - 1): \
- (0xffffffffffffffffULL))
-#define PM_LEVEL_INDEX(x, a) (((a) >> PM_LEVEL_SHIFT((x))) & 0x1ffULL)
-#define PM_LEVEL_ENC(x) (((x) << 9) & 0xe00ULL)
-#define PM_LEVEL_PDE(x, a) ((a) | PM_LEVEL_ENC((x)) | \
- IOMMU_PTE_PR | IOMMU_PTE_IR | IOMMU_PTE_IW)
-#define PM_PTE_LEVEL(pte) (((pte) >> 9) & 0x7ULL)
-
-#define PM_MAP_4k 0
#define PM_ADDR_MASK 0x000ffffffffff000ULL
-#define PM_MAP_MASK(lvl) (PM_ADDR_MASK & \
- (~((1ULL << (12 + ((lvl) * 9))) - 1)))
-#define PM_ALIGNED(lvl, addr) ((PM_MAP_MASK(lvl) & (addr)) == (addr))
-
-/*
- * Returns the page table level to use for a given page size
- * Pagesize is expected to be a power-of-two
- */
-#define PAGE_SIZE_LEVEL(pagesize) \
- ((__ffs(pagesize) - 12) / 9)
-/*
- * Returns the number of ptes to use for a given page size
- * Pagesize is expected to be a power-of-two
- */
-#define PAGE_SIZE_PTE_COUNT(pagesize) \
- (1ULL << ((__ffs(pagesize) - 12) % 9))
-
-/*
- * Aligns a given io-virtual address to a given page size
- * Pagesize is expected to be a power-of-two
- */
-#define PAGE_SIZE_ALIGN(address, pagesize) \
- ((address) & ~((pagesize) - 1))
-/*
- * Creates an IOMMU PTE for an address and a given pagesize
- * The PTE has no permission bits set
- * Pagesize is expected to be a power-of-two larger than 4096
- */
-#define PAGE_SIZE_PTE(address, pagesize) \
- (((address) | ((pagesize) - 1)) & \
- (~(pagesize >> 1)) & PM_ADDR_MASK)
-
-/*
- * Takes a PTE value with mode=0x07 and returns the page size it maps
- */
-#define PTE_PAGE_SIZE(pte) \
- (1ULL << (1 + ffz(((pte) | 0xfffULL))))
-
-/*
- * Takes a page-table level and returns the default page-size for this level
- */
-#define PTE_LEVEL_PAGE_SIZE(level) \
- (1ULL << (12 + (9 * (level))))
-
-/*
- * The IOPTE dirty bit
- */
-#define IOMMU_PTE_HD_BIT (6)
-
-/*
- * Bit value definition for I/O PTE fields
- */
-#define IOMMU_PTE_PR BIT_ULL(0)
-#define IOMMU_PTE_HD BIT_ULL(IOMMU_PTE_HD_BIT)
-#define IOMMU_PTE_U BIT_ULL(59)
-#define IOMMU_PTE_FC BIT_ULL(60)
-#define IOMMU_PTE_IR BIT_ULL(61)
-#define IOMMU_PTE_IW BIT_ULL(62)
/*
* Bit value definition for DTE fields
@@ -436,12 +371,6 @@
/* DTE[128:179] | DTE[184:191] */
#define DTE_DATA2_INTR_MASK ~GENMASK_ULL(55, 52)
-#define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL)
-#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_PR)
-#define IOMMU_PTE_DIRTY(pte) ((pte) & IOMMU_PTE_HD)
-#define IOMMU_PTE_PAGE(pte) (iommu_phys_to_virt((pte) & IOMMU_PAGE_MASK))
-#define IOMMU_PTE_MODE(pte) (((pte) >> 9) & 0x07)
-
#define IOMMU_PROT_MASK 0x03
#define IOMMU_PROT_IR 0x01
#define IOMMU_PROT_IW 0x02
@@ -534,19 +463,6 @@ struct amd_irte_ops;
#define AMD_IOMMU_FLAG_TRANS_PRE_ENABLED (1 << 0)
-#define io_pgtable_to_data(x) \
- container_of((x), struct amd_io_pgtable, pgtbl)
-
-#define io_pgtable_ops_to_data(x) \
- io_pgtable_to_data(io_pgtable_ops_to_pgtable(x))
-
-#define io_pgtable_ops_to_domain(x) \
- container_of(io_pgtable_ops_to_data(x), \
- struct protection_domain, iop)
-
-#define io_pgtable_cfg_to_data(x) \
- container_of((x), struct amd_io_pgtable, pgtbl.cfg)
-
struct gcr3_tbl_info {
u64 *gcr3_tbl; /* Guest CR3 table */
int glx; /* Number of levels for GCR3 table */
@@ -554,14 +470,6 @@ struct gcr3_tbl_info {
u16 domid; /* Per device domain ID */
};
-struct amd_io_pgtable {
- seqcount_t seqcount; /* Protects root/mode update */
- struct io_pgtable pgtbl;
- int mode;
- u64 *root;
- u64 *pgd; /* v2 pgtable pgd pointer */
-};
-
enum protection_domain_mode {
PD_MODE_NONE,
PD_MODE_V1,
@@ -589,10 +497,13 @@ struct pdom_iommu_info {
* independent of their use.
*/
struct protection_domain {
+ union {
+ struct iommu_domain domain;
+ struct pt_iommu iommu;
+ struct pt_iommu_amdv1 amdv1;
+ struct pt_iommu_x86_64 amdv2;
+ };
struct list_head dev_list; /* List of all devices in this domain */
- struct iommu_domain domain; /* generic domain handle used by
- iommu core code */
- struct amd_io_pgtable iop;
spinlock_t lock; /* mostly used to lock the page table*/
u16 id; /* the domain id written to the device table */
enum protection_domain_mode pd_mode; /* Track page table type */
@@ -602,6 +513,9 @@ struct protection_domain {
struct mmu_notifier mn; /* mmu notifier for the SVA domain */
struct list_head dev_data_list; /* List of pdom_dev_data */
};
+PT_IOMMU_CHECK_DOMAIN(struct protection_domain, iommu, domain);
+PT_IOMMU_CHECK_DOMAIN(struct protection_domain, amdv1.iommu, domain);
+PT_IOMMU_CHECK_DOMAIN(struct protection_domain, amdv2.iommu, domain);
/*
* This structure contains information about one PCI segment in the system.
diff --git a/drivers/iommu/amd/debugfs.c b/drivers/iommu/amd/debugfs.c
index 10fa217a7119..20b04996441d 100644
--- a/drivers/iommu/amd/debugfs.c
+++ b/drivers/iommu/amd/debugfs.c
@@ -37,7 +37,7 @@ static ssize_t iommu_mmio_write(struct file *filp, const char __user *ubuf,
if (ret)
return ret;
- if (iommu->dbg_mmio_offset > iommu->mmio_phys_end - 4) {
+ if (iommu->dbg_mmio_offset > iommu->mmio_phys_end - sizeof(u64)) {
iommu->dbg_mmio_offset = -1;
return -EINVAL;
}
diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index f2991c11867c..4f4d4955269e 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -1710,13 +1710,22 @@ static struct amd_iommu_pci_seg *__init alloc_pci_segment(u16 id,
list_add_tail(&pci_seg->list, &amd_iommu_pci_seg_list);
if (alloc_dev_table(pci_seg))
- return NULL;
+ goto err_free_pci_seg;
if (alloc_alias_table(pci_seg))
- return NULL;
+ goto err_free_dev_table;
if (alloc_rlookup_table(pci_seg))
- return NULL;
+ goto err_free_alias_table;
return pci_seg;
+
+err_free_alias_table:
+ free_alias_table(pci_seg);
+err_free_dev_table:
+ free_dev_table(pci_seg);
+err_free_pci_seg:
+ list_del(&pci_seg->list);
+ kfree(pci_seg);
+ return NULL;
}
static struct amd_iommu_pci_seg *__init get_pci_segment(u16 id,
diff --git a/drivers/iommu/amd/io_pgtable.c b/drivers/iommu/amd/io_pgtable.c
deleted file mode 100644
index 70c2f5b1631b..000000000000
--- a/drivers/iommu/amd/io_pgtable.c
+++ /dev/null
@@ -1,577 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * CPU-agnostic AMD IO page table allocator.
- *
- * Copyright (C) 2020 Advanced Micro Devices, Inc.
- * Author: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
- */
-
-#define pr_fmt(fmt) "AMD-Vi: " fmt
-#define dev_fmt(fmt) pr_fmt(fmt)
-
-#include <linux/atomic.h>
-#include <linux/bitops.h>
-#include <linux/io-pgtable.h>
-#include <linux/kernel.h>
-#include <linux/sizes.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/dma-mapping.h>
-#include <linux/seqlock.h>
-
-#include <asm/barrier.h>
-
-#include "amd_iommu_types.h"
-#include "amd_iommu.h"
-#include "../iommu-pages.h"
-
-/*
- * Helper function to get the first pte of a large mapping
- */
-static u64 *first_pte_l7(u64 *pte, unsigned long *page_size,
- unsigned long *count)
-{
- unsigned long pte_mask, pg_size, cnt;
- u64 *fpte;
-
- pg_size = PTE_PAGE_SIZE(*pte);
- cnt = PAGE_SIZE_PTE_COUNT(pg_size);
- pte_mask = ~((cnt << 3) - 1);
- fpte = (u64 *)(((unsigned long)pte) & pte_mask);
-
- if (page_size)
- *page_size = pg_size;
-
- if (count)
- *count = cnt;
-
- return fpte;
-}
-
-static void free_pt_lvl(u64 *pt, struct iommu_pages_list *freelist, int lvl)
-{
- u64 *p;
- int i;
-
- for (i = 0; i < 512; ++i) {
- /* PTE present? */
- if (!IOMMU_PTE_PRESENT(pt[i]))
- continue;
-
- /* Large PTE? */
- if (PM_PTE_LEVEL(pt[i]) == 0 ||
- PM_PTE_LEVEL(pt[i]) == 7)
- continue;
-
- /*
- * Free the next level. No need to look at l1 tables here since
- * they can only contain leaf PTEs; just free them directly.
- */
- p = IOMMU_PTE_PAGE(pt[i]);
- if (lvl > 2)
- free_pt_lvl(p, freelist, lvl - 1);
- else
- iommu_pages_list_add(freelist, p);
- }
-
- iommu_pages_list_add(freelist, pt);
-}
-
-static void free_sub_pt(u64 *root, int mode, struct iommu_pages_list *freelist)
-{
- switch (mode) {
- case PAGE_MODE_NONE:
- case PAGE_MODE_7_LEVEL:
- break;
- case PAGE_MODE_1_LEVEL:
- iommu_pages_list_add(freelist, root);
- break;
- case PAGE_MODE_2_LEVEL:
- case PAGE_MODE_3_LEVEL:
- case PAGE_MODE_4_LEVEL:
- case PAGE_MODE_5_LEVEL:
- case PAGE_MODE_6_LEVEL:
- free_pt_lvl(root, freelist, mode);
- break;
- default:
- BUG();
- }
-}
-
-/*
- * This function is used to add another level to an IO page table. Adding
- * another level increases the size of the address space by 9 bits to a size up
- * to 64 bits.
- */
-static bool increase_address_space(struct amd_io_pgtable *pgtable,
- unsigned long address,
- unsigned int page_size_level,
- gfp_t gfp)
-{
- struct io_pgtable_cfg *cfg = &pgtable->pgtbl.cfg;
- struct protection_domain *domain =
- container_of(pgtable, struct protection_domain, iop);
- unsigned long flags;
- bool ret = true;
- u64 *pte;
-
- pte = iommu_alloc_pages_node_sz(cfg->amd.nid, gfp, SZ_4K);
- if (!pte)
- return false;
-
- spin_lock_irqsave(&domain->lock, flags);
-
- if (address <= PM_LEVEL_SIZE(pgtable->mode) &&
- pgtable->mode - 1 >= page_size_level)
- goto out;
-
- ret = false;
- if (WARN_ON_ONCE(pgtable->mode == amd_iommu_hpt_level))
- goto out;
-
- *pte = PM_LEVEL_PDE(pgtable->mode, iommu_virt_to_phys(pgtable->root));
-
- write_seqcount_begin(&pgtable->seqcount);
- pgtable->root = pte;
- pgtable->mode += 1;
- write_seqcount_end(&pgtable->seqcount);
-
- amd_iommu_update_and_flush_device_table(domain);
-
- pte = NULL;
- ret = true;
-
-out:
- spin_unlock_irqrestore(&domain->lock, flags);
- iommu_free_pages(pte);
-
- return ret;
-}
-
-static u64 *alloc_pte(struct amd_io_pgtable *pgtable,
- unsigned long address,
- unsigned long page_size,
- u64 **pte_page,
- gfp_t gfp,
- bool *updated)
-{
- unsigned long last_addr = address + (page_size - 1);
- struct io_pgtable_cfg *cfg = &pgtable->pgtbl.cfg;
- unsigned int seqcount;
- int level, end_lvl;
- u64 *pte, *page;
-
- BUG_ON(!is_power_of_2(page_size));
-
- while (last_addr > PM_LEVEL_SIZE(pgtable->mode) ||
- pgtable->mode - 1 < PAGE_SIZE_LEVEL(page_size)) {
- /*
- * Return an error if there is no memory to update the
- * page-table.
- */
- if (!increase_address_space(pgtable, last_addr,
- PAGE_SIZE_LEVEL(page_size), gfp))
- return NULL;
- }
-
-
- do {
- seqcount = read_seqcount_begin(&pgtable->seqcount);
-
- level = pgtable->mode - 1;
- pte = &pgtable->root[PM_LEVEL_INDEX(level, address)];
- } while (read_seqcount_retry(&pgtable->seqcount, seqcount));
-
-
- address = PAGE_SIZE_ALIGN(address, page_size);
- end_lvl = PAGE_SIZE_LEVEL(page_size);
-
- while (level > end_lvl) {
- u64 __pte, __npte;
- int pte_level;
-
- __pte = *pte;
- pte_level = PM_PTE_LEVEL(__pte);
-
- /*
- * If we replace a series of large PTEs, we need
- * to tear down all of them.
- */
- if (IOMMU_PTE_PRESENT(__pte) &&
- pte_level == PAGE_MODE_7_LEVEL) {
- unsigned long count, i;
- u64 *lpte;
-
- lpte = first_pte_l7(pte, NULL, &count);
-
- /*
- * Unmap the replicated PTEs that still match the
- * original large mapping
- */
- for (i = 0; i < count; ++i)
- cmpxchg64(&lpte[i], __pte, 0ULL);
-
- *updated = true;
- continue;
- }
-
- if (!IOMMU_PTE_PRESENT(__pte) ||
- pte_level == PAGE_MODE_NONE) {
- page = iommu_alloc_pages_node_sz(cfg->amd.nid, gfp,
- SZ_4K);
-
- if (!page)
- return NULL;
-
- __npte = PM_LEVEL_PDE(level, iommu_virt_to_phys(page));
-
- /* pte could have been changed somewhere. */
- if (!try_cmpxchg64(pte, &__pte, __npte))
- iommu_free_pages(page);
- else if (IOMMU_PTE_PRESENT(__pte))
- *updated = true;
-
- continue;
- }
-
- /* No level skipping support yet */
- if (pte_level != level)
- return NULL;
-
- level -= 1;
-
- pte = IOMMU_PTE_PAGE(__pte);
-
- if (pte_page && level == end_lvl)
- *pte_page = pte;
-
- pte = &pte[PM_LEVEL_INDEX(level, address)];
- }
-
- return pte;
-}
-
-/*
- * This function checks if there is a PTE for a given dma address. If
- * there is one, it returns the pointer to it.
- */
-static u64 *fetch_pte(struct amd_io_pgtable *pgtable,
- unsigned long address,
- unsigned long *page_size)
-{
- int level;
- unsigned int seqcount;
- u64 *pte;
-
- *page_size = 0;
-
- if (address > PM_LEVEL_SIZE(pgtable->mode))
- return NULL;
-
- do {
- seqcount = read_seqcount_begin(&pgtable->seqcount);
- level = pgtable->mode - 1;
- pte = &pgtable->root[PM_LEVEL_INDEX(level, address)];
- } while (read_seqcount_retry(&pgtable->seqcount, seqcount));
-
- *page_size = PTE_LEVEL_PAGE_SIZE(level);
-
- while (level > 0) {
-
- /* Not Present */
- if (!IOMMU_PTE_PRESENT(*pte))
- return NULL;
-
- /* Large PTE */
- if (PM_PTE_LEVEL(*pte) == PAGE_MODE_7_LEVEL ||
- PM_PTE_LEVEL(*pte) == PAGE_MODE_NONE)
- break;
-
- /* No level skipping support yet */
- if (PM_PTE_LEVEL(*pte) != level)
- return NULL;
-
- level -= 1;
-
- /* Walk to the next level */
- pte = IOMMU_PTE_PAGE(*pte);
- pte = &pte[PM_LEVEL_INDEX(level, address)];
- *page_size = PTE_LEVEL_PAGE_SIZE(level);
- }
-
- /*
- * If we have a series of large PTEs, make
- * sure to return a pointer to the first one.
- */
- if (PM_PTE_LEVEL(*pte) == PAGE_MODE_7_LEVEL)
- pte = first_pte_l7(pte, page_size, NULL);
-
- return pte;
-}
-
-static void free_clear_pte(u64 *pte, u64 pteval,
- struct iommu_pages_list *freelist)
-{
- u64 *pt;
- int mode;
-
- while (!try_cmpxchg64(pte, &pteval, 0))
- pr_warn("AMD-Vi: IOMMU pte changed since we read it\n");
-
- if (!IOMMU_PTE_PRESENT(pteval))
- return;
-
- pt = IOMMU_PTE_PAGE(pteval);
- mode = IOMMU_PTE_MODE(pteval);
-
- free_sub_pt(pt, mode, freelist);
-}
-
-/*
- * Generic mapping functions. It maps a physical address into a DMA
- * address space. It allocates the page table pages if necessary.
- * In the future it can be extended to a generic mapping function
- * supporting all features of AMD IOMMU page tables like level skipping
- * and full 64 bit address spaces.
- */
-static int iommu_v1_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
- phys_addr_t paddr, size_t pgsize, size_t pgcount,
- int prot, gfp_t gfp, size_t *mapped)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_ops_to_data(ops);
- struct iommu_pages_list freelist = IOMMU_PAGES_LIST_INIT(freelist);
- bool updated = false;
- u64 __pte, *pte;
- int ret, i, count;
- size_t size = pgcount << __ffs(pgsize);
- unsigned long o_iova = iova;
-
- BUG_ON(!IS_ALIGNED(iova, pgsize));
- BUG_ON(!IS_ALIGNED(paddr, pgsize));
-
- ret = -EINVAL;
- if (!(prot & IOMMU_PROT_MASK))
- goto out;
-
- while (pgcount > 0) {
- count = PAGE_SIZE_PTE_COUNT(pgsize);
- pte = alloc_pte(pgtable, iova, pgsize, NULL, gfp, &updated);
-
- ret = -ENOMEM;
- if (!pte)
- goto out;
-
- for (i = 0; i < count; ++i)
- free_clear_pte(&pte[i], pte[i], &freelist);
-
- if (!iommu_pages_list_empty(&freelist))
- updated = true;
-
- if (count > 1) {
- __pte = PAGE_SIZE_PTE(__sme_set(paddr), pgsize);
- __pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_PR | IOMMU_PTE_FC;
- } else
- __pte = __sme_set(paddr) | IOMMU_PTE_PR | IOMMU_PTE_FC;
-
- if (prot & IOMMU_PROT_IR)
- __pte |= IOMMU_PTE_IR;
- if (prot & IOMMU_PROT_IW)
- __pte |= IOMMU_PTE_IW;
-
- for (i = 0; i < count; ++i)
- pte[i] = __pte;
-
- iova += pgsize;
- paddr += pgsize;
- pgcount--;
- if (mapped)
- *mapped += pgsize;
- }
-
- ret = 0;
-
-out:
- if (updated) {
- struct protection_domain *dom = io_pgtable_ops_to_domain(ops);
- unsigned long flags;
-
- spin_lock_irqsave(&dom->lock, flags);
- /*
- * Flush domain TLB(s) and wait for completion. Any Device-Table
- * Updates and flushing already happened in
- * increase_address_space().
- */
- amd_iommu_domain_flush_pages(dom, o_iova, size);
- spin_unlock_irqrestore(&dom->lock, flags);
- }
-
- /* Everything flushed out, free pages now */
- iommu_put_pages_list(&freelist);
-
- return ret;
-}
-
-static unsigned long iommu_v1_unmap_pages(struct io_pgtable_ops *ops,
- unsigned long iova,
- size_t pgsize, size_t pgcount,
- struct iommu_iotlb_gather *gather)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_ops_to_data(ops);
- unsigned long long unmapped;
- unsigned long unmap_size;
- u64 *pte;
- size_t size = pgcount << __ffs(pgsize);
-
- BUG_ON(!is_power_of_2(pgsize));
-
- unmapped = 0;
-
- while (unmapped < size) {
- pte = fetch_pte(pgtable, iova, &unmap_size);
- if (pte) {
- int i, count;
-
- count = PAGE_SIZE_PTE_COUNT(unmap_size);
- for (i = 0; i < count; i++)
- pte[i] = 0ULL;
- } else {
- return unmapped;
- }
-
- iova = (iova & ~(unmap_size - 1)) + unmap_size;
- unmapped += unmap_size;
- }
-
- return unmapped;
-}
-
-static phys_addr_t iommu_v1_iova_to_phys(struct io_pgtable_ops *ops, unsigned long iova)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_ops_to_data(ops);
- unsigned long offset_mask, pte_pgsize;
- u64 *pte, __pte;
-
- pte = fetch_pte(pgtable, iova, &pte_pgsize);
-
- if (!pte || !IOMMU_PTE_PRESENT(*pte))
- return 0;
-
- offset_mask = pte_pgsize - 1;
- __pte = __sme_clr(*pte & PM_ADDR_MASK);
-
- return (__pte & ~offset_mask) | (iova & offset_mask);
-}
-
-static bool pte_test_and_clear_dirty(u64 *ptep, unsigned long size,
- unsigned long flags)
-{
- bool test_only = flags & IOMMU_DIRTY_NO_CLEAR;
- bool dirty = false;
- int i, count;
-
- /*
- * 2.2.3.2 Host Dirty Support
- * When a non-default page size is used , software must OR the
- * Dirty bits in all of the replicated host PTEs used to map
- * the page. The IOMMU does not guarantee the Dirty bits are
- * set in all of the replicated PTEs. Any portion of the page
- * may have been written even if the Dirty bit is set in only
- * one of the replicated PTEs.
- */
- count = PAGE_SIZE_PTE_COUNT(size);
- for (i = 0; i < count && test_only; i++) {
- if (test_bit(IOMMU_PTE_HD_BIT, (unsigned long *)&ptep[i])) {
- dirty = true;
- break;
- }
- }
-
- for (i = 0; i < count && !test_only; i++) {
- if (test_and_clear_bit(IOMMU_PTE_HD_BIT,
- (unsigned long *)&ptep[i])) {
- dirty = true;
- }
- }
-
- return dirty;
-}
-
-static int iommu_v1_read_and_clear_dirty(struct io_pgtable_ops *ops,
- unsigned long iova, size_t size,
- unsigned long flags,
- struct iommu_dirty_bitmap *dirty)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_ops_to_data(ops);
- unsigned long end = iova + size - 1;
-
- do {
- unsigned long pgsize = 0;
- u64 *ptep, pte;
-
- ptep = fetch_pte(pgtable, iova, &pgsize);
- if (ptep)
- pte = READ_ONCE(*ptep);
- if (!ptep || !IOMMU_PTE_PRESENT(pte)) {
- pgsize = pgsize ?: PTE_LEVEL_PAGE_SIZE(0);
- iova += pgsize;
- continue;
- }
-
- /*
- * Mark the whole IOVA range as dirty even if only one of
- * the replicated PTEs were marked dirty.
- */
- if (pte_test_and_clear_dirty(ptep, pgsize, flags))
- iommu_dirty_bitmap_record(dirty, iova, pgsize);
- iova += pgsize;
- } while (iova < end);
-
- return 0;
-}
-
-/*
- * ----------------------------------------------------
- */
-static void v1_free_pgtable(struct io_pgtable *iop)
-{
- struct amd_io_pgtable *pgtable = container_of(iop, struct amd_io_pgtable, pgtbl);
- struct iommu_pages_list freelist = IOMMU_PAGES_LIST_INIT(freelist);
-
- if (pgtable->mode == PAGE_MODE_NONE)
- return;
-
- /* Page-table is not visible to IOMMU anymore, so free it */
- BUG_ON(pgtable->mode < PAGE_MODE_NONE ||
- pgtable->mode > amd_iommu_hpt_level);
-
- free_sub_pt(pgtable->root, pgtable->mode, &freelist);
- iommu_put_pages_list(&freelist);
-}
-
-static struct io_pgtable *v1_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_cfg_to_data(cfg);
-
- pgtable->root =
- iommu_alloc_pages_node_sz(cfg->amd.nid, GFP_KERNEL, SZ_4K);
- if (!pgtable->root)
- return NULL;
- pgtable->mode = PAGE_MODE_3_LEVEL;
- seqcount_init(&pgtable->seqcount);
-
- cfg->pgsize_bitmap = amd_iommu_pgsize_bitmap;
- cfg->ias = IOMMU_IN_ADDR_BIT_SIZE;
- cfg->oas = IOMMU_OUT_ADDR_BIT_SIZE;
-
- pgtable->pgtbl.ops.map_pages = iommu_v1_map_pages;
- pgtable->pgtbl.ops.unmap_pages = iommu_v1_unmap_pages;
- pgtable->pgtbl.ops.iova_to_phys = iommu_v1_iova_to_phys;
- pgtable->pgtbl.ops.read_and_clear_dirty = iommu_v1_read_and_clear_dirty;
-
- return &pgtable->pgtbl;
-}
-
-struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns = {
- .alloc = v1_alloc_pgtable,
- .free = v1_free_pgtable,
-};
diff --git a/drivers/iommu/amd/io_pgtable_v2.c b/drivers/iommu/amd/io_pgtable_v2.c
deleted file mode 100644
index b47941353ccb..000000000000
--- a/drivers/iommu/amd/io_pgtable_v2.c
+++ /dev/null
@@ -1,370 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * CPU-agnostic AMD IO page table v2 allocator.
- *
- * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc.
- * Author: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
- * Author: Vasant Hegde <vasant.hegde@amd.com>
- */
-
-#define pr_fmt(fmt) "AMD-Vi: " fmt
-#define dev_fmt(fmt) pr_fmt(fmt)
-
-#include <linux/bitops.h>
-#include <linux/io-pgtable.h>
-#include <linux/kernel.h>
-
-#include <asm/barrier.h>
-
-#include "amd_iommu_types.h"
-#include "amd_iommu.h"
-#include "../iommu-pages.h"
-
-#define IOMMU_PAGE_PRESENT BIT_ULL(0) /* Is present */
-#define IOMMU_PAGE_RW BIT_ULL(1) /* Writeable */
-#define IOMMU_PAGE_USER BIT_ULL(2) /* Userspace addressable */
-#define IOMMU_PAGE_PWT BIT_ULL(3) /* Page write through */
-#define IOMMU_PAGE_PCD BIT_ULL(4) /* Page cache disabled */
-#define IOMMU_PAGE_ACCESS BIT_ULL(5) /* Was accessed (updated by IOMMU) */
-#define IOMMU_PAGE_DIRTY BIT_ULL(6) /* Was written to (updated by IOMMU) */
-#define IOMMU_PAGE_PSE BIT_ULL(7) /* Page Size Extensions */
-#define IOMMU_PAGE_NX BIT_ULL(63) /* No execute */
-
-#define MAX_PTRS_PER_PAGE 512
-
-#define IOMMU_PAGE_SIZE_2M BIT_ULL(21)
-#define IOMMU_PAGE_SIZE_1G BIT_ULL(30)
-
-
-static inline int get_pgtable_level(void)
-{
- return amd_iommu_gpt_level;
-}
-
-static inline bool is_large_pte(u64 pte)
-{
- return (pte & IOMMU_PAGE_PSE);
-}
-
-static inline u64 set_pgtable_attr(u64 *page)
-{
- u64 prot;
-
- prot = IOMMU_PAGE_PRESENT | IOMMU_PAGE_RW | IOMMU_PAGE_USER;
- prot |= IOMMU_PAGE_ACCESS;
-
- return (iommu_virt_to_phys(page) | prot);
-}
-
-static inline void *get_pgtable_pte(u64 pte)
-{
- return iommu_phys_to_virt(pte & PM_ADDR_MASK);
-}
-
-static u64 set_pte_attr(u64 paddr, u64 pg_size, int prot)
-{
- u64 pte;
-
- pte = __sme_set(paddr & PM_ADDR_MASK);
- pte |= IOMMU_PAGE_PRESENT | IOMMU_PAGE_USER;
- pte |= IOMMU_PAGE_ACCESS | IOMMU_PAGE_DIRTY;
-
- if (prot & IOMMU_PROT_IW)
- pte |= IOMMU_PAGE_RW;
-
- /* Large page */
- if (pg_size == IOMMU_PAGE_SIZE_1G || pg_size == IOMMU_PAGE_SIZE_2M)
- pte |= IOMMU_PAGE_PSE;
-
- return pte;
-}
-
-static inline u64 get_alloc_page_size(u64 size)
-{
- if (size >= IOMMU_PAGE_SIZE_1G)
- return IOMMU_PAGE_SIZE_1G;
-
- if (size >= IOMMU_PAGE_SIZE_2M)
- return IOMMU_PAGE_SIZE_2M;
-
- return PAGE_SIZE;
-}
-
-static inline int page_size_to_level(u64 pg_size)
-{
- if (pg_size == IOMMU_PAGE_SIZE_1G)
- return PAGE_MODE_3_LEVEL;
- if (pg_size == IOMMU_PAGE_SIZE_2M)
- return PAGE_MODE_2_LEVEL;
-
- return PAGE_MODE_1_LEVEL;
-}
-
-static void free_pgtable(u64 *pt, int level)
-{
- u64 *p;
- int i;
-
- for (i = 0; i < MAX_PTRS_PER_PAGE; i++) {
- /* PTE present? */
- if (!IOMMU_PTE_PRESENT(pt[i]))
- continue;
-
- if (is_large_pte(pt[i]))
- continue;
-
- /*
- * Free the next level. No need to look at l1 tables here since
- * they can only contain leaf PTEs; just free them directly.
- */
- p = get_pgtable_pte(pt[i]);
- if (level > 2)
- free_pgtable(p, level - 1);
- else
- iommu_free_pages(p);
- }
-
- iommu_free_pages(pt);
-}
-
-/* Allocate page table */
-static u64 *v2_alloc_pte(int nid, u64 *pgd, unsigned long iova,
- unsigned long pg_size, gfp_t gfp, bool *updated)
-{
- u64 *pte, *page;
- int level, end_level;
-
- level = get_pgtable_level() - 1;
- end_level = page_size_to_level(pg_size);
- pte = &pgd[PM_LEVEL_INDEX(level, iova)];
- iova = PAGE_SIZE_ALIGN(iova, PAGE_SIZE);
-
- while (level >= end_level) {
- u64 __pte, __npte;
-
- __pte = *pte;
-
- if (IOMMU_PTE_PRESENT(__pte) && is_large_pte(__pte)) {
- /* Unmap large pte */
- cmpxchg64(pte, *pte, 0ULL);
- *updated = true;
- continue;
- }
-
- if (!IOMMU_PTE_PRESENT(__pte)) {
- page = iommu_alloc_pages_node_sz(nid, gfp, SZ_4K);
- if (!page)
- return NULL;
-
- __npte = set_pgtable_attr(page);
- /* pte could have been changed somewhere. */
- if (!try_cmpxchg64(pte, &__pte, __npte))
- iommu_free_pages(page);
- else if (IOMMU_PTE_PRESENT(__pte))
- *updated = true;
-
- continue;
- }
-
- level -= 1;
- pte = get_pgtable_pte(__pte);
- pte = &pte[PM_LEVEL_INDEX(level, iova)];
- }
-
- /* Tear down existing pte entries */
- if (IOMMU_PTE_PRESENT(*pte)) {
- u64 *__pte;
-
- *updated = true;
- __pte = get_pgtable_pte(*pte);
- cmpxchg64(pte, *pte, 0ULL);
- if (pg_size == IOMMU_PAGE_SIZE_1G)
- free_pgtable(__pte, end_level - 1);
- else if (pg_size == IOMMU_PAGE_SIZE_2M)
- iommu_free_pages(__pte);
- }
-
- return pte;
-}
-
-/*
- * This function checks if there is a PTE for a given dma address.
- * If there is one, it returns the pointer to it.
- */
-static u64 *fetch_pte(struct amd_io_pgtable *pgtable,
- unsigned long iova, unsigned long *page_size)
-{
- u64 *pte;
- int level;
-
- level = get_pgtable_level() - 1;
- pte = &pgtable->pgd[PM_LEVEL_INDEX(level, iova)];
- /* Default page size is 4K */
- *page_size = PAGE_SIZE;
-
- while (level) {
- /* Not present */
- if (!IOMMU_PTE_PRESENT(*pte))
- return NULL;
-
- /* Walk to the next level */
- pte = get_pgtable_pte(*pte);
- pte = &pte[PM_LEVEL_INDEX(level - 1, iova)];
-
- /* Large page */
- if (is_large_pte(*pte)) {
- if (level == PAGE_MODE_3_LEVEL)
- *page_size = IOMMU_PAGE_SIZE_1G;
- else if (level == PAGE_MODE_2_LEVEL)
- *page_size = IOMMU_PAGE_SIZE_2M;
- else
- return NULL; /* Wrongly set PSE bit in PTE */
-
- break;
- }
-
- level -= 1;
- }
-
- return pte;
-}
-
-static int iommu_v2_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
- phys_addr_t paddr, size_t pgsize, size_t pgcount,
- int prot, gfp_t gfp, size_t *mapped)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_ops_to_data(ops);
- struct io_pgtable_cfg *cfg = &pgtable->pgtbl.cfg;
- u64 *pte;
- unsigned long map_size;
- unsigned long mapped_size = 0;
- unsigned long o_iova = iova;
- size_t size = pgcount << __ffs(pgsize);
- int ret = 0;
- bool updated = false;
-
- if (WARN_ON(!pgsize || (pgsize & cfg->pgsize_bitmap) != pgsize) || !pgcount)
- return -EINVAL;
-
- if (!(prot & IOMMU_PROT_MASK))
- return -EINVAL;
-
- while (mapped_size < size) {
- map_size = get_alloc_page_size(pgsize);
- pte = v2_alloc_pte(cfg->amd.nid, pgtable->pgd,
- iova, map_size, gfp, &updated);
- if (!pte) {
- ret = -ENOMEM;
- goto out;
- }
-
- *pte = set_pte_attr(paddr, map_size, prot);
-
- iova += map_size;
- paddr += map_size;
- mapped_size += map_size;
- }
-
-out:
- if (updated) {
- struct protection_domain *pdom = io_pgtable_ops_to_domain(ops);
- unsigned long flags;
-
- spin_lock_irqsave(&pdom->lock, flags);
- amd_iommu_domain_flush_pages(pdom, o_iova, size);
- spin_unlock_irqrestore(&pdom->lock, flags);
- }
-
- if (mapped)
- *mapped += mapped_size;
-
- return ret;
-}
-
-static unsigned long iommu_v2_unmap_pages(struct io_pgtable_ops *ops,
- unsigned long iova,
- size_t pgsize, size_t pgcount,
- struct iommu_iotlb_gather *gather)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_ops_to_data(ops);
- struct io_pgtable_cfg *cfg = &pgtable->pgtbl.cfg;
- unsigned long unmap_size;
- unsigned long unmapped = 0;
- size_t size = pgcount << __ffs(pgsize);
- u64 *pte;
-
- if (WARN_ON(!pgsize || (pgsize & cfg->pgsize_bitmap) != pgsize || !pgcount))
- return 0;
-
- while (unmapped < size) {
- pte = fetch_pte(pgtable, iova, &unmap_size);
- if (!pte)
- return unmapped;
-
- *pte = 0ULL;
-
- iova = (iova & ~(unmap_size - 1)) + unmap_size;
- unmapped += unmap_size;
- }
-
- return unmapped;
-}
-
-static phys_addr_t iommu_v2_iova_to_phys(struct io_pgtable_ops *ops, unsigned long iova)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_ops_to_data(ops);
- unsigned long offset_mask, pte_pgsize;
- u64 *pte, __pte;
-
- pte = fetch_pte(pgtable, iova, &pte_pgsize);
- if (!pte || !IOMMU_PTE_PRESENT(*pte))
- return 0;
-
- offset_mask = pte_pgsize - 1;
- __pte = __sme_clr(*pte & PM_ADDR_MASK);
-
- return (__pte & ~offset_mask) | (iova & offset_mask);
-}
-
-/*
- * ----------------------------------------------------
- */
-static void v2_free_pgtable(struct io_pgtable *iop)
-{
- struct amd_io_pgtable *pgtable = container_of(iop, struct amd_io_pgtable, pgtbl);
-
- if (!pgtable || !pgtable->pgd)
- return;
-
- /* Free page table */
- free_pgtable(pgtable->pgd, get_pgtable_level());
- pgtable->pgd = NULL;
-}
-
-static struct io_pgtable *v2_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
-{
- struct amd_io_pgtable *pgtable = io_pgtable_cfg_to_data(cfg);
- int ias = IOMMU_IN_ADDR_BIT_SIZE;
-
- pgtable->pgd = iommu_alloc_pages_node_sz(cfg->amd.nid, GFP_KERNEL, SZ_4K);
- if (!pgtable->pgd)
- return NULL;
-
- if (get_pgtable_level() == PAGE_MODE_5_LEVEL)
- ias = 57;
-
- pgtable->pgtbl.ops.map_pages = iommu_v2_map_pages;
- pgtable->pgtbl.ops.unmap_pages = iommu_v2_unmap_pages;
- pgtable->pgtbl.ops.iova_to_phys = iommu_v2_iova_to_phys;
-
- cfg->pgsize_bitmap = AMD_IOMMU_PGSIZES_V2;
- cfg->ias = ias;
- cfg->oas = IOMMU_OUT_ADDR_BIT_SIZE;
-
- return &pgtable->pgtbl;
-}
-
-struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns = {
- .alloc = v2_alloc_pgtable,
- .free = v2_free_pgtable,
-};
diff --git a/drivers/iommu/amd/iommu.c b/drivers/iommu/amd/iommu.c
index 2e1865daa1ce..9f1d56a5e145 100644
--- a/drivers/iommu/amd/iommu.c
+++ b/drivers/iommu/amd/iommu.c
@@ -30,7 +30,6 @@
#include <linux/msi.h>
#include <linux/irqdomain.h>
#include <linux/percpu.h>
-#include <linux/io-pgtable.h>
#include <linux/cc_platform.h>
#include <asm/irq_remapping.h>
#include <asm/io_apic.h>
@@ -41,9 +40,9 @@
#include <asm/gart.h>
#include <asm/dma.h>
#include <uapi/linux/iommufd.h>
+#include <linux/generic_pt/iommu.h>
#include "amd_iommu.h"
-#include "../dma-iommu.h"
#include "../irq_remapping.h"
#include "../iommu-pages.h"
@@ -60,7 +59,6 @@ LIST_HEAD(hpet_map);
LIST_HEAD(acpihid_map);
const struct iommu_ops amd_iommu_ops;
-static const struct iommu_dirty_ops amd_dirty_ops;
int amd_iommu_max_glx_val = -1;
@@ -70,15 +68,22 @@ int amd_iommu_max_glx_val = -1;
*/
DEFINE_IDA(pdom_ids);
-static int amd_iommu_attach_device(struct iommu_domain *dom,
- struct device *dev);
+static int amd_iommu_attach_device(struct iommu_domain *dom, struct device *dev,
+ struct iommu_domain *old);
static void set_dte_entry(struct amd_iommu *iommu,
- struct iommu_dev_data *dev_data);
+ struct iommu_dev_data *dev_data,
+ phys_addr_t top_paddr, unsigned int top_level);
+
+static void amd_iommu_change_top(struct pt_iommu *iommu_table,
+ phys_addr_t top_paddr, unsigned int top_level);
static void iommu_flush_dte_sync(struct amd_iommu *iommu, u16 devid);
static struct iommu_dev_data *find_dev_data(struct amd_iommu *iommu, u16 devid);
+static bool amd_iommu_enforce_cache_coherency(struct iommu_domain *domain);
+static int amd_iommu_set_dirty_tracking(struct iommu_domain *domain,
+ bool enable);
/****************************************************************************
*
@@ -1157,6 +1162,25 @@ irqreturn_t amd_iommu_int_handler(int irq, void *data)
*
****************************************************************************/
+static void dump_command_buffer(struct amd_iommu *iommu)
+{
+ struct iommu_cmd *cmd;
+ u32 head, tail;
+ int i;
+
+ head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET);
+ tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET);
+
+ pr_err("CMD Buffer head=%llu tail=%llu\n", MMIO_CMD_BUFFER_HEAD(head),
+ MMIO_CMD_BUFFER_TAIL(tail));
+
+ for (i = 0; i < CMD_BUFFER_ENTRIES; i++) {
+ cmd = (struct iommu_cmd *)(iommu->cmd_buf + i * sizeof(*cmd));
+ pr_err("%3d: %08x %08x %08x %08x\n", i, cmd->data[0], cmd->data[1], cmd->data[2],
+ cmd->data[3]);
+ }
+}
+
static int wait_on_sem(struct amd_iommu *iommu, u64 data)
{
int i = 0;
@@ -1167,7 +1191,14 @@ static int wait_on_sem(struct amd_iommu *iommu, u64 data)
}
if (i == LOOP_TIMEOUT) {
- pr_alert("Completion-Wait loop timed out\n");
+
+ pr_alert("IOMMU %04x:%02x:%02x.%01x: Completion-Wait loop timed out\n",
+ iommu->pci_seg->id, PCI_BUS_NUM(iommu->devid),
+ PCI_SLOT(iommu->devid), PCI_FUNC(iommu->devid));
+
+ if (amd_iommu_dump)
+ DO_ONCE_LITE(dump_command_buffer, iommu);
+
return -EIO;
}
@@ -1756,42 +1787,6 @@ static void dev_flush_pasid_all(struct iommu_dev_data *dev_data,
CMD_INV_IOMMU_ALL_PAGES_ADDRESS);
}
-/* Flush the not present cache if it exists */
-static void domain_flush_np_cache(struct protection_domain *domain,
- dma_addr_t iova, size_t size)
-{
- if (unlikely(amd_iommu_np_cache)) {
- unsigned long flags;
-
- spin_lock_irqsave(&domain->lock, flags);
- amd_iommu_domain_flush_pages(domain, iova, size);
- spin_unlock_irqrestore(&domain->lock, flags);
- }
-}
-
-
-/*
- * This function flushes the DTEs for all devices in domain
- */
-void amd_iommu_update_and_flush_device_table(struct protection_domain *domain)
-{
- struct iommu_dev_data *dev_data;
-
- lockdep_assert_held(&domain->lock);
-
- list_for_each_entry(dev_data, &domain->dev_list, list) {
- struct amd_iommu *iommu = rlookup_amd_iommu(dev_data->dev);
-
- set_dte_entry(iommu, dev_data);
- clone_aliases(iommu, dev_data->dev);
- }
-
- list_for_each_entry(dev_data, &domain->dev_list, list)
- device_flush_dte(dev_data);
-
- domain_flush_complete(domain);
-}
-
int amd_iommu_complete_ppr(struct device *dev, u32 pasid, int status, int tag)
{
struct iommu_dev_data *dev_data;
@@ -2051,7 +2046,8 @@ static void set_dte_gcr3_table(struct amd_iommu *iommu,
}
static void set_dte_entry(struct amd_iommu *iommu,
- struct iommu_dev_data *dev_data)
+ struct iommu_dev_data *dev_data,
+ phys_addr_t top_paddr, unsigned int top_level)
{
u16 domid;
u32 old_domid;
@@ -2060,19 +2056,36 @@ static void set_dte_entry(struct amd_iommu *iommu,
struct protection_domain *domain = dev_data->domain;
struct gcr3_tbl_info *gcr3_info = &dev_data->gcr3_info;
struct dev_table_entry *dte = &get_dev_table(iommu)[dev_data->devid];
+ struct pt_iommu_amdv1_hw_info pt_info;
+
+ make_clear_dte(dev_data, dte, &new);
if (gcr3_info && gcr3_info->gcr3_tbl)
domid = dev_data->gcr3_info.domid;
- else
+ else {
domid = domain->id;
- make_clear_dte(dev_data, dte, &new);
-
- if (domain->iop.mode != PAGE_MODE_NONE)
- new.data[0] |= iommu_virt_to_phys(domain->iop.root);
+ if (domain->domain.type & __IOMMU_DOMAIN_PAGING) {
+ /*
+ * When updating the IO pagetable, the new top and level
+ * are provided as parameters. For other operations i.e.
+ * device attach, retrieve the current pagetable info
+ * via the IOMMU PT API.
+ */
+ if (top_paddr) {
+ pt_info.host_pt_root = top_paddr;
+ pt_info.mode = top_level + 1;
+ } else {
+ WARN_ON(top_paddr || top_level);
+ pt_iommu_amdv1_hw_info(&domain->amdv1,
+ &pt_info);
+ }
- new.data[0] |= (domain->iop.mode & DEV_ENTRY_MODE_MASK)
- << DEV_ENTRY_MODE_SHIFT;
+ new.data[0] |= __sme_set(pt_info.host_pt_root) |
+ (pt_info.mode & DEV_ENTRY_MODE_MASK)
+ << DEV_ENTRY_MODE_SHIFT;
+ }
+ }
new.data[0] |= DTE_FLAG_IR | DTE_FLAG_IW;
@@ -2138,7 +2151,7 @@ static void dev_update_dte(struct iommu_dev_data *dev_data, bool set)
struct amd_iommu *iommu = get_amd_iommu_from_dev(dev_data->dev);
if (set)
- set_dte_entry(iommu, dev_data);
+ set_dte_entry(iommu, dev_data, 0, 0);
else
clear_dte_entry(iommu, dev_data);
@@ -2156,6 +2169,7 @@ static int init_gcr3_table(struct iommu_dev_data *dev_data,
{
struct amd_iommu *iommu = get_amd_iommu_from_dev_data(dev_data);
int max_pasids = dev_data->max_pasids;
+ struct pt_iommu_x86_64_hw_info pt_info;
int ret = 0;
/*
@@ -2178,7 +2192,8 @@ static int init_gcr3_table(struct iommu_dev_data *dev_data,
if (!pdom_is_v2_pgtbl_mode(pdom))
return ret;
- ret = update_gcr3(dev_data, 0, iommu_virt_to_phys(pdom->iop.pgd), true);
+ pt_iommu_x86_64_hw_info(&pdom->amdv2, &pt_info);
+ ret = update_gcr3(dev_data, 0, __sme_set(pt_info.gcr3_pt), true);
if (ret)
free_gcr3_table(&dev_data->gcr3_info);
@@ -2500,94 +2515,240 @@ struct protection_domain *protection_domain_alloc(void)
return domain;
}
-static int pdom_setup_pgtable(struct protection_domain *domain,
- struct device *dev)
+static bool amd_iommu_hd_support(struct amd_iommu *iommu)
+{
+ if (amd_iommu_hatdis)
+ return false;
+
+ return iommu && (iommu->features & FEATURE_HDSUP);
+}
+
+static spinlock_t *amd_iommu_get_top_lock(struct pt_iommu *iommupt)
{
- struct io_pgtable_ops *pgtbl_ops;
- enum io_pgtable_fmt fmt;
+ struct protection_domain *pdom =
+ container_of(iommupt, struct protection_domain, iommu);
- switch (domain->pd_mode) {
- case PD_MODE_V1:
- fmt = AMD_IOMMU_V1;
- break;
- case PD_MODE_V2:
- fmt = AMD_IOMMU_V2;
- break;
- case PD_MODE_NONE:
- WARN_ON_ONCE(1);
- return -EPERM;
+ return &pdom->lock;
+}
+
+/*
+ * Update all HW references to the domain with a new pgtable configuration.
+ */
+static void amd_iommu_change_top(struct pt_iommu *iommu_table,
+ phys_addr_t top_paddr, unsigned int top_level)
+{
+ struct protection_domain *pdom =
+ container_of(iommu_table, struct protection_domain, iommu);
+ struct iommu_dev_data *dev_data;
+
+ lockdep_assert_held(&pdom->lock);
+
+ /* Update the DTE for all devices attached to this domain */
+ list_for_each_entry(dev_data, &pdom->dev_list, list) {
+ struct amd_iommu *iommu = rlookup_amd_iommu(dev_data->dev);
+
+ /* Update the HW references with the new level and top ptr */
+ set_dte_entry(iommu, dev_data, top_paddr, top_level);
+ clone_aliases(iommu, dev_data->dev);
}
- domain->iop.pgtbl.cfg.amd.nid = dev_to_node(dev);
- pgtbl_ops = alloc_io_pgtable_ops(fmt, &domain->iop.pgtbl.cfg, domain);
- if (!pgtbl_ops)
- return -ENOMEM;
+ list_for_each_entry(dev_data, &pdom->dev_list, list)
+ device_flush_dte(dev_data);
+
+ domain_flush_complete(pdom);
+}
+
+/*
+ * amd_iommu_iotlb_sync_map() is used to generate flushes for non-present to
+ * present (ie mapping) operations. It is a NOP if the IOMMU doesn't have non
+ * present caching (like hypervisor shadowing).
+ */
+static int amd_iommu_iotlb_sync_map(struct iommu_domain *dom,
+ unsigned long iova, size_t size)
+{
+ struct protection_domain *domain = to_pdomain(dom);
+ unsigned long flags;
+ if (likely(!amd_iommu_np_cache))
+ return 0;
+
+ spin_lock_irqsave(&domain->lock, flags);
+ amd_iommu_domain_flush_pages(domain, iova, size);
+ spin_unlock_irqrestore(&domain->lock, flags);
return 0;
}
-static inline u64 dma_max_address(enum protection_domain_mode pgtable)
+static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain)
{
- if (pgtable == PD_MODE_V1)
- return PM_LEVEL_SIZE(amd_iommu_hpt_level);
+ struct protection_domain *dom = to_pdomain(domain);
+ unsigned long flags;
- /*
- * V2 with 4/5 level page table. Note that "2.2.6.5 AMD64 4-Kbyte Page
- * Translation" shows that the V2 table sign extends the top of the
- * address space creating a reserved region in the middle of the
- * translation, just like the CPU does. Further Vasant says the docs are
- * incomplete and this only applies to non-zero PASIDs. If the AMDv2
- * page table is assigned to the 0 PASID then there is no sign extension
- * check.
- *
- * Since the IOMMU must have a fixed geometry, and the core code does
- * not understand sign extended addressing, we have to chop off the high
- * bit to get consistent behavior with attachments of the domain to any
- * PASID.
- */
- return ((1ULL << (PM_LEVEL_SHIFT(amd_iommu_gpt_level) - 1)) - 1);
+ spin_lock_irqsave(&dom->lock, flags);
+ amd_iommu_domain_flush_all(dom);
+ spin_unlock_irqrestore(&dom->lock, flags);
}
-static bool amd_iommu_hd_support(struct amd_iommu *iommu)
+static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
+ struct iommu_iotlb_gather *gather)
{
- if (amd_iommu_hatdis)
- return false;
+ struct protection_domain *dom = to_pdomain(domain);
+ unsigned long flags;
- return iommu && (iommu->features & FEATURE_HDSUP);
+ spin_lock_irqsave(&dom->lock, flags);
+ amd_iommu_domain_flush_pages(dom, gather->start,
+ gather->end - gather->start + 1);
+ spin_unlock_irqrestore(&dom->lock, flags);
+ iommu_put_pages_list(&gather->freelist);
}
-static struct iommu_domain *
-do_iommu_domain_alloc(struct device *dev, u32 flags,
- enum protection_domain_mode pgtable)
+static const struct pt_iommu_driver_ops amd_hw_driver_ops_v1 = {
+ .get_top_lock = amd_iommu_get_top_lock,
+ .change_top = amd_iommu_change_top,
+};
+
+static const struct iommu_domain_ops amdv1_ops = {
+ IOMMU_PT_DOMAIN_OPS(amdv1),
+ .iotlb_sync_map = amd_iommu_iotlb_sync_map,
+ .flush_iotlb_all = amd_iommu_flush_iotlb_all,
+ .iotlb_sync = amd_iommu_iotlb_sync,
+ .attach_dev = amd_iommu_attach_device,
+ .free = amd_iommu_domain_free,
+ .enforce_cache_coherency = amd_iommu_enforce_cache_coherency,
+};
+
+static const struct iommu_dirty_ops amdv1_dirty_ops = {
+ IOMMU_PT_DIRTY_OPS(amdv1),
+ .set_dirty_tracking = amd_iommu_set_dirty_tracking,
+};
+
+static struct iommu_domain *amd_iommu_domain_alloc_paging_v1(struct device *dev,
+ u32 flags)
{
- bool dirty_tracking = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
- struct amd_iommu *iommu = get_amd_iommu_from_dev(dev);
+ struct pt_iommu_amdv1_cfg cfg = {};
struct protection_domain *domain;
int ret;
+ if (amd_iommu_hatdis)
+ return ERR_PTR(-EOPNOTSUPP);
+
domain = protection_domain_alloc();
if (!domain)
return ERR_PTR(-ENOMEM);
- domain->pd_mode = pgtable;
- ret = pdom_setup_pgtable(domain, dev);
+ domain->pd_mode = PD_MODE_V1;
+ domain->iommu.driver_ops = &amd_hw_driver_ops_v1;
+ domain->iommu.nid = dev_to_node(dev);
+ if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING)
+ domain->domain.dirty_ops = &amdv1_dirty_ops;
+
+ /*
+ * Someday FORCE_COHERENCE should be set by
+ * amd_iommu_enforce_cache_coherency() like VT-d does.
+ */
+ cfg.common.features = BIT(PT_FEAT_DYNAMIC_TOP) |
+ BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) |
+ BIT(PT_FEAT_AMDV1_FORCE_COHERENCE);
+
+ /*
+ * AMD's IOMMU can flush as many pages as necessary in a single flush.
+ * Unless we run in a virtual machine, which can be inferred according
+ * to whether "non-present cache" is on, it is probably best to prefer
+ * (potentially) too extensive TLB flushing (i.e., more misses) over
+ * multiple TLB flushes (i.e., more flushes). For virtual machines the
+ * hypervisor needs to synchronize the host IOMMU PTEs with those of
+ * the guest, and the trade-off is different: unnecessary TLB flushes
+ * should be avoided.
+ */
+ if (amd_iommu_np_cache)
+ cfg.common.features |= BIT(PT_FEAT_FLUSH_RANGE_NO_GAPS);
+ else
+ cfg.common.features |= BIT(PT_FEAT_FLUSH_RANGE);
+
+ cfg.common.hw_max_vasz_lg2 =
+ min(64, (amd_iommu_hpt_level - 1) * 9 + 21);
+ cfg.common.hw_max_oasz_lg2 = 52;
+ cfg.starting_level = 2;
+ domain->domain.ops = &amdv1_ops;
+
+ ret = pt_iommu_amdv1_init(&domain->amdv1, &cfg, GFP_KERNEL);
if (ret) {
- pdom_id_free(domain->id);
- kfree(domain);
+ amd_iommu_domain_free(&domain->domain);
return ERR_PTR(ret);
}
- domain->domain.geometry.aperture_start = 0;
- domain->domain.geometry.aperture_end = dma_max_address(pgtable);
- domain->domain.geometry.force_aperture = true;
- domain->domain.pgsize_bitmap = domain->iop.pgtbl.cfg.pgsize_bitmap;
+ /*
+ * Narrow the supported page sizes to those selected by the kernel
+ * command line.
+ */
+ domain->domain.pgsize_bitmap &= amd_iommu_pgsize_bitmap;
+ return &domain->domain;
+}
- domain->domain.type = IOMMU_DOMAIN_UNMANAGED;
- domain->domain.ops = iommu->iommu.ops->default_domain_ops;
+static const struct iommu_domain_ops amdv2_ops = {
+ IOMMU_PT_DOMAIN_OPS(x86_64),
+ .iotlb_sync_map = amd_iommu_iotlb_sync_map,
+ .flush_iotlb_all = amd_iommu_flush_iotlb_all,
+ .iotlb_sync = amd_iommu_iotlb_sync,
+ .attach_dev = amd_iommu_attach_device,
+ .free = amd_iommu_domain_free,
+ /*
+ * Note the AMDv2 page table format does not support a Force Coherency
+ * bit, so enforce_cache_coherency should not be set. However VFIO is
+ * not prepared to handle a case where some domains will support
+ * enforcement and others do not. VFIO and iommufd will have to be fixed
+ * before it can fully use the V2 page table. See the comment in
+ * iommufd_hwpt_paging_alloc(). For now leave things as they have
+ * historically been and lie about enforce_cache_coherencey.
+ */
+ .enforce_cache_coherency = amd_iommu_enforce_cache_coherency,
+};
- if (dirty_tracking)
- domain->domain.dirty_ops = &amd_dirty_ops;
+static struct iommu_domain *amd_iommu_domain_alloc_paging_v2(struct device *dev,
+ u32 flags)
+{
+ struct pt_iommu_x86_64_cfg cfg = {};
+ struct protection_domain *domain;
+ int ret;
+ if (!amd_iommu_v2_pgtbl_supported())
+ return ERR_PTR(-EOPNOTSUPP);
+
+ domain = protection_domain_alloc();
+ if (!domain)
+ return ERR_PTR(-ENOMEM);
+
+ domain->pd_mode = PD_MODE_V2;
+ domain->iommu.nid = dev_to_node(dev);
+
+ cfg.common.features = BIT(PT_FEAT_X86_64_AMD_ENCRYPT_TABLES);
+ if (amd_iommu_np_cache)
+ cfg.common.features |= BIT(PT_FEAT_FLUSH_RANGE_NO_GAPS);
+ else
+ cfg.common.features |= BIT(PT_FEAT_FLUSH_RANGE);
+
+ /*
+ * The v2 table behaves differently if it is attached to PASID 0 vs a
+ * non-zero PASID. On PASID 0 it has no sign extension and the full
+ * 57/48 bits decode the lower addresses. Otherwise it behaves like a
+ * normal sign extended x86 page table. Since we want the domain to work
+ * in both modes the top bit is removed and PT_FEAT_SIGN_EXTEND is not
+ * set which creates a table that is compatible in both modes.
+ */
+ if (amd_iommu_gpt_level == PAGE_MODE_5_LEVEL) {
+ cfg.common.hw_max_vasz_lg2 = 56;
+ cfg.top_level = 4;
+ } else {
+ cfg.common.hw_max_vasz_lg2 = 47;
+ cfg.top_level = 3;
+ }
+ cfg.common.hw_max_oasz_lg2 = 52;
+ domain->domain.ops = &amdv2_ops;
+
+ ret = pt_iommu_x86_64_init(&domain->amdv2, &cfg, GFP_KERNEL);
+ if (ret) {
+ amd_iommu_domain_free(&domain->domain);
+ return ERR_PTR(ret);
+ }
return &domain->domain;
}
@@ -2608,15 +2769,27 @@ amd_iommu_domain_alloc_paging_flags(struct device *dev, u32 flags,
/* Allocate domain with v1 page table for dirty tracking */
if (!amd_iommu_hd_support(iommu))
break;
- return do_iommu_domain_alloc(dev, flags, PD_MODE_V1);
+ return amd_iommu_domain_alloc_paging_v1(dev, flags);
case IOMMU_HWPT_ALLOC_PASID:
/* Allocate domain with v2 page table if IOMMU supports PASID. */
if (!amd_iommu_pasid_supported())
break;
- return do_iommu_domain_alloc(dev, flags, PD_MODE_V2);
- case 0:
+ return amd_iommu_domain_alloc_paging_v2(dev, flags);
+ case 0: {
+ struct iommu_domain *ret;
+
/* If nothing specific is required use the kernel commandline default */
- return do_iommu_domain_alloc(dev, 0, amd_iommu_pgtable);
+ if (amd_iommu_pgtable == PD_MODE_V1) {
+ ret = amd_iommu_domain_alloc_paging_v1(dev, flags);
+ if (ret != ERR_PTR(-EOPNOTSUPP))
+ return ret;
+ return amd_iommu_domain_alloc_paging_v2(dev, flags);
+ }
+ ret = amd_iommu_domain_alloc_paging_v2(dev, flags);
+ if (ret != ERR_PTR(-EOPNOTSUPP))
+ return ret;
+ return amd_iommu_domain_alloc_paging_v1(dev, flags);
+ }
default:
break;
}
@@ -2628,14 +2801,14 @@ void amd_iommu_domain_free(struct iommu_domain *dom)
struct protection_domain *domain = to_pdomain(dom);
WARN_ON(!list_empty(&domain->dev_list));
- if (domain->domain.type & __IOMMU_DOMAIN_PAGING)
- free_io_pgtable_ops(&domain->iop.pgtbl.ops);
+ pt_iommu_deinit(&domain->iommu);
pdom_id_free(domain->id);
kfree(domain);
}
static int blocked_domain_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
@@ -2685,16 +2858,8 @@ void amd_iommu_init_identity_domain(void)
protection_domain_init(&identity_domain);
}
-/* Same as blocked domain except it supports only ops->attach_dev() */
-static struct iommu_domain release_domain = {
- .type = IOMMU_DOMAIN_BLOCKED,
- .ops = &(const struct iommu_domain_ops) {
- .attach_dev = blocked_domain_attach_device,
- }
-};
-
-static int amd_iommu_attach_device(struct iommu_domain *dom,
- struct device *dev)
+static int amd_iommu_attach_device(struct iommu_domain *dom, struct device *dev,
+ struct iommu_domain *old)
{
struct iommu_dev_data *dev_data = dev_iommu_priv_get(dev);
struct protection_domain *domain = to_pdomain(dom);
@@ -2734,93 +2899,6 @@ static int amd_iommu_attach_device(struct iommu_domain *dom,
return ret;
}
-static int amd_iommu_iotlb_sync_map(struct iommu_domain *dom,
- unsigned long iova, size_t size)
-{
- struct protection_domain *domain = to_pdomain(dom);
- struct io_pgtable_ops *ops = &domain->iop.pgtbl.ops;
-
- if (ops->map_pages)
- domain_flush_np_cache(domain, iova, size);
- return 0;
-}
-
-static int amd_iommu_map_pages(struct iommu_domain *dom, unsigned long iova,
- phys_addr_t paddr, size_t pgsize, size_t pgcount,
- int iommu_prot, gfp_t gfp, size_t *mapped)
-{
- struct protection_domain *domain = to_pdomain(dom);
- struct io_pgtable_ops *ops = &domain->iop.pgtbl.ops;
- int prot = 0;
- int ret = -EINVAL;
-
- if ((domain->pd_mode == PD_MODE_V1) &&
- (domain->iop.mode == PAGE_MODE_NONE))
- return -EINVAL;
-
- if (iommu_prot & IOMMU_READ)
- prot |= IOMMU_PROT_IR;
- if (iommu_prot & IOMMU_WRITE)
- prot |= IOMMU_PROT_IW;
-
- if (ops->map_pages) {
- ret = ops->map_pages(ops, iova, paddr, pgsize,
- pgcount, prot, gfp, mapped);
- }
-
- return ret;
-}
-
-static void amd_iommu_iotlb_gather_add_page(struct iommu_domain *domain,
- struct iommu_iotlb_gather *gather,
- unsigned long iova, size_t size)
-{
- /*
- * AMD's IOMMU can flush as many pages as necessary in a single flush.
- * Unless we run in a virtual machine, which can be inferred according
- * to whether "non-present cache" is on, it is probably best to prefer
- * (potentially) too extensive TLB flushing (i.e., more misses) over
- * mutliple TLB flushes (i.e., more flushes). For virtual machines the
- * hypervisor needs to synchronize the host IOMMU PTEs with those of
- * the guest, and the trade-off is different: unnecessary TLB flushes
- * should be avoided.
- */
- if (amd_iommu_np_cache &&
- iommu_iotlb_gather_is_disjoint(gather, iova, size))
- iommu_iotlb_sync(domain, gather);
-
- iommu_iotlb_gather_add_range(gather, iova, size);
-}
-
-static size_t amd_iommu_unmap_pages(struct iommu_domain *dom, unsigned long iova,
- size_t pgsize, size_t pgcount,
- struct iommu_iotlb_gather *gather)
-{
- struct protection_domain *domain = to_pdomain(dom);
- struct io_pgtable_ops *ops = &domain->iop.pgtbl.ops;
- size_t r;
-
- if ((domain->pd_mode == PD_MODE_V1) &&
- (domain->iop.mode == PAGE_MODE_NONE))
- return 0;
-
- r = (ops->unmap_pages) ? ops->unmap_pages(ops, iova, pgsize, pgcount, NULL) : 0;
-
- if (r)
- amd_iommu_iotlb_gather_add_page(dom, gather, iova, r);
-
- return r;
-}
-
-static phys_addr_t amd_iommu_iova_to_phys(struct iommu_domain *dom,
- dma_addr_t iova)
-{
- struct protection_domain *domain = to_pdomain(dom);
- struct io_pgtable_ops *ops = &domain->iop.pgtbl.ops;
-
- return ops->iova_to_phys(ops, iova);
-}
-
static bool amd_iommu_capable(struct device *dev, enum iommu_cap cap)
{
switch (cap) {
@@ -2887,28 +2965,6 @@ static int amd_iommu_set_dirty_tracking(struct iommu_domain *domain,
return 0;
}
-static int amd_iommu_read_and_clear_dirty(struct iommu_domain *domain,
- unsigned long iova, size_t size,
- unsigned long flags,
- struct iommu_dirty_bitmap *dirty)
-{
- struct protection_domain *pdomain = to_pdomain(domain);
- struct io_pgtable_ops *ops = &pdomain->iop.pgtbl.ops;
- unsigned long lflags;
-
- if (!ops || !ops->read_and_clear_dirty)
- return -EOPNOTSUPP;
-
- spin_lock_irqsave(&pdomain->lock, lflags);
- if (!pdomain->dirty_tracking && dirty->bitmap) {
- spin_unlock_irqrestore(&pdomain->lock, lflags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&pdomain->lock, lflags);
-
- return ops->read_and_clear_dirty(ops, iova, size, flags, dirty);
-}
-
static void amd_iommu_get_resv_regions(struct device *dev,
struct list_head *head)
{
@@ -2978,28 +3034,6 @@ static bool amd_iommu_is_attach_deferred(struct device *dev)
return dev_data->defer_attach;
}
-static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain)
-{
- struct protection_domain *dom = to_pdomain(domain);
- unsigned long flags;
-
- spin_lock_irqsave(&dom->lock, flags);
- amd_iommu_domain_flush_all(dom);
- spin_unlock_irqrestore(&dom->lock, flags);
-}
-
-static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
- struct iommu_iotlb_gather *gather)
-{
- struct protection_domain *dom = to_pdomain(domain);
- unsigned long flags;
-
- spin_lock_irqsave(&dom->lock, flags);
- amd_iommu_domain_flush_pages(dom, gather->start,
- gather->end - gather->start + 1);
- spin_unlock_irqrestore(&dom->lock, flags);
-}
-
static int amd_iommu_def_domain_type(struct device *dev)
{
struct iommu_dev_data *dev_data;
@@ -3034,15 +3068,10 @@ static bool amd_iommu_enforce_cache_coherency(struct iommu_domain *domain)
return true;
}
-static const struct iommu_dirty_ops amd_dirty_ops = {
- .set_dirty_tracking = amd_iommu_set_dirty_tracking,
- .read_and_clear_dirty = amd_iommu_read_and_clear_dirty,
-};
-
const struct iommu_ops amd_iommu_ops = {
.capable = amd_iommu_capable,
.blocked_domain = &blocked_domain,
- .release_domain = &release_domain,
+ .release_domain = &blocked_domain,
.identity_domain = &identity_domain.domain,
.domain_alloc_paging_flags = amd_iommu_domain_alloc_paging_flags,
.domain_alloc_sva = amd_iommu_domain_alloc_sva,
@@ -3053,17 +3082,6 @@ const struct iommu_ops amd_iommu_ops = {
.is_attach_deferred = amd_iommu_is_attach_deferred,
.def_domain_type = amd_iommu_def_domain_type,
.page_response = amd_iommu_page_response,
- .default_domain_ops = &(const struct iommu_domain_ops) {
- .attach_dev = amd_iommu_attach_device,
- .map_pages = amd_iommu_map_pages,
- .unmap_pages = amd_iommu_unmap_pages,
- .iotlb_sync_map = amd_iommu_iotlb_sync_map,
- .iova_to_phys = amd_iommu_iova_to_phys,
- .flush_iotlb_all = amd_iommu_flush_iotlb_all,
- .iotlb_sync = amd_iommu_iotlb_sync,
- .free = amd_iommu_domain_free,
- .enforce_cache_coherency = amd_iommu_enforce_cache_coherency,
- }
};
#ifdef CONFIG_IRQ_REMAP
@@ -3354,7 +3372,7 @@ static int __modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index,
static int modify_irte_ga(struct amd_iommu *iommu, u16 devid, int index,
struct irte_ga *irte)
{
- bool ret;
+ int ret;
ret = __modify_irte_ga(iommu, devid, index, irte);
if (ret)
@@ -4072,3 +4090,5 @@ int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
return 0;
}
#endif
+
+MODULE_IMPORT_NS("GENERIC_PT_IOMMU");
diff --git a/drivers/iommu/apple-dart.c b/drivers/iommu/apple-dart.c
index 95a4e62b8f63..83a5aabcd15d 100644
--- a/drivers/iommu/apple-dart.c
+++ b/drivers/iommu/apple-dart.c
@@ -672,7 +672,8 @@ static int apple_dart_domain_add_streams(struct apple_dart_domain *domain,
}
static int apple_dart_attach_dev_paging(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
int ret, i;
struct apple_dart_stream_map *stream_map;
@@ -693,7 +694,8 @@ static int apple_dart_attach_dev_paging(struct iommu_domain *domain,
}
static int apple_dart_attach_dev_identity(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
struct apple_dart_stream_map *stream_map;
@@ -717,7 +719,8 @@ static struct iommu_domain apple_dart_identity_domain = {
};
static int apple_dart_attach_dev_blocked(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
struct apple_dart_stream_map *stream_map;
@@ -802,6 +805,8 @@ static int apple_dart_of_xlate(struct device *dev,
struct apple_dart *cfg_dart;
int i, sid;
+ put_device(&iommu_pdev->dev);
+
if (args->args_count != 1)
return -EINVAL;
sid = args->args[0];
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
index 8cd8929bbfdf..93fdadd07431 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c
@@ -99,6 +99,8 @@ static void arm_smmu_make_nested_domain_ste(
int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
struct arm_smmu_nested_domain *nested_domain)
{
+ unsigned int cfg =
+ FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(nested_domain->ste[0]));
struct arm_smmu_vmaster *vmaster;
unsigned long vsid;
int ret;
@@ -107,8 +109,17 @@ int arm_smmu_attach_prepare_vmaster(struct arm_smmu_attach_state *state,
ret = iommufd_viommu_get_vdev_id(&nested_domain->vsmmu->core,
state->master->dev, &vsid);
- if (ret)
+ /*
+ * Attaching to a translate nested domain must allocate a vDEVICE prior,
+ * as CD/ATS invalidations and vevents require a vSID to work properly.
+ * A abort/bypass domain is allowed to attach w/o vmaster for GBPA case.
+ */
+ if (ret) {
+ if (cfg == STRTAB_STE_0_CFG_ABORT ||
+ cfg == STRTAB_STE_0_CFG_BYPASS)
+ return 0;
return ret;
+ }
vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL);
if (!vmaster)
@@ -138,14 +149,15 @@ void arm_smmu_master_clear_vmaster(struct arm_smmu_master *master)
}
static int arm_smmu_attach_dev_nested(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old_domain)
{
struct arm_smmu_nested_domain *nested_domain =
to_smmu_nested_domain(domain);
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
struct arm_smmu_attach_state state = {
.master = master,
- .old_domain = iommu_get_domain_for_dev(dev),
+ .old_domain = old_domain,
.ssid = IOMMU_NO_PASID,
};
struct arm_smmu_ste ste;
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 2a8b46b948f0..d16d35c78c06 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1464,7 +1464,7 @@ static int arm_smmu_alloc_cd_tables(struct arm_smmu_master *master)
cd_table->l2.l1tab = dma_alloc_coherent(smmu->dev, l1size,
&cd_table->cdtab_dma,
GFP_KERNEL);
- if (!cd_table->l2.l2ptrs) {
+ if (!cd_table->l2.l1tab) {
ret = -ENOMEM;
goto err_free_l2ptrs;
}
@@ -3002,7 +3002,8 @@ void arm_smmu_attach_commit(struct arm_smmu_attach_state *state)
master->ats_enabled = state->ats_enabled;
}
-static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
+static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev,
+ struct iommu_domain *old_domain)
{
int ret = 0;
struct arm_smmu_ste target;
@@ -3010,7 +3011,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
struct arm_smmu_device *smmu;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_attach_state state = {
- .old_domain = iommu_get_domain_for_dev(dev),
+ .old_domain = old_domain,
.ssid = IOMMU_NO_PASID,
};
struct arm_smmu_master *master;
@@ -3186,7 +3187,7 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
/*
* When the last user of the CD table goes away downgrade the STE back
- * to a non-cd_table one.
+ * to a non-cd_table one, by re-attaching its sid_domain.
*/
if (!arm_smmu_ssids_in_use(&master->cd_table)) {
struct iommu_domain *sid_domain =
@@ -3194,12 +3195,14 @@ static int arm_smmu_blocking_set_dev_pasid(struct iommu_domain *new_domain,
if (sid_domain->type == IOMMU_DOMAIN_IDENTITY ||
sid_domain->type == IOMMU_DOMAIN_BLOCKED)
- sid_domain->ops->attach_dev(sid_domain, dev);
+ sid_domain->ops->attach_dev(sid_domain, dev,
+ sid_domain);
}
return 0;
}
static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
+ struct iommu_domain *old_domain,
struct device *dev,
struct arm_smmu_ste *ste,
unsigned int s1dss)
@@ -3207,7 +3210,7 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
struct arm_smmu_attach_state state = {
.master = master,
- .old_domain = iommu_get_domain_for_dev(dev),
+ .old_domain = old_domain,
.ssid = IOMMU_NO_PASID,
};
@@ -3248,14 +3251,16 @@ static void arm_smmu_attach_dev_ste(struct iommu_domain *domain,
}
static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old_domain)
{
struct arm_smmu_ste ste;
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
arm_smmu_master_clear_vmaster(master);
arm_smmu_make_bypass_ste(master->smmu, &ste);
- arm_smmu_attach_dev_ste(domain, dev, &ste, STRTAB_STE_1_S1DSS_BYPASS);
+ arm_smmu_attach_dev_ste(domain, old_domain, dev, &ste,
+ STRTAB_STE_1_S1DSS_BYPASS);
return 0;
}
@@ -3269,14 +3274,15 @@ static struct iommu_domain arm_smmu_identity_domain = {
};
static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old_domain)
{
struct arm_smmu_ste ste;
struct arm_smmu_master *master = dev_iommu_priv_get(dev);
arm_smmu_master_clear_vmaster(master);
arm_smmu_make_abort_ste(&ste);
- arm_smmu_attach_dev_ste(domain, dev, &ste,
+ arm_smmu_attach_dev_ste(domain, old_domain, dev, &ste,
STRTAB_STE_1_S1DSS_TERMINATE);
return 0;
}
@@ -3582,12 +3588,6 @@ static void arm_smmu_release_device(struct device *dev)
WARN_ON(master->iopf_refcount);
- /* Put the STE back to what arm_smmu_init_strtab() sets */
- if (dev->iommu->require_direct)
- arm_smmu_attach_dev_identity(&arm_smmu_identity_domain, dev);
- else
- arm_smmu_attach_dev_blocked(&arm_smmu_blocked_domain, dev);
-
arm_smmu_disable_pasid(master);
arm_smmu_remove_master(master);
if (arm_smmu_cdtab_allocated(&master->cd_table))
@@ -3678,6 +3678,7 @@ static int arm_smmu_def_domain_type(struct device *dev)
static const struct iommu_ops arm_smmu_ops = {
.identity_domain = &arm_smmu_identity_domain,
.blocked_domain = &arm_smmu_blocked_domain,
+ .release_domain = &arm_smmu_blocked_domain,
.capable = arm_smmu_capable,
.hw_info = arm_smmu_hw_info,
.domain_alloc_sva = arm_smmu_sva_domain_alloc,
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
index 57c097e87613..573085349df3 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
@@ -367,6 +367,7 @@ static int qcom_adreno_smmu_init_context(struct arm_smmu_domain *smmu_domain,
static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
{ .compatible = "qcom,adreno" },
{ .compatible = "qcom,adreno-gmu" },
+ { .compatible = "qcom,glymur-mdss" },
{ .compatible = "qcom,mdp4" },
{ .compatible = "qcom,mdss" },
{ .compatible = "qcom,qcm2290-mdss" },
@@ -431,17 +432,19 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
/*
* Some platforms support more than the Arm SMMU architected maximum of
- * 128 stream matching groups. For unknown reasons, the additional
- * groups don't exhibit the same behavior as the architected registers,
- * so limit the groups to 128 until the behavior is fixed for the other
- * groups.
+ * 128 stream matching groups. The additional registers appear to have
+ * the same behavior as the architected registers in the hardware.
+ * However, on some firmware versions, the hypervisor does not
+ * correctly trap and emulate accesses to the additional registers,
+ * resulting in unexpected behavior.
+ *
+ * If there are more than 128 groups, use the last reliable group to
+ * detect if we need to apply the bypass quirk.
*/
- if (smmu->num_mapping_groups > 128) {
- dev_notice(smmu->dev, "\tLimiting the stream matching groups to 128\n");
- smmu->num_mapping_groups = 128;
- }
-
- last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
+ if (smmu->num_mapping_groups > 128)
+ last_s2cr = ARM_SMMU_GR0_S2CR(127);
+ else
+ last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
/*
* With some firmware versions writes to S2CR of type FAULT are
@@ -464,6 +467,11 @@ static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS);
arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg);
+
+ if (smmu->num_mapping_groups > 128) {
+ dev_notice(smmu->dev, "\tLimiting the stream matching groups to 128\n");
+ smmu->num_mapping_groups = 128;
+ }
}
for (i = 0; i < smmu->num_mapping_groups; i++) {
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 4ced4b5bee4d..5e690cf85ec9 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -1165,7 +1165,8 @@ static void arm_smmu_master_install_s2crs(struct arm_smmu_master_cfg *cfg,
}
}
-static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
+static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev,
+ struct iommu_domain *old)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
@@ -1234,7 +1235,8 @@ static int arm_smmu_attach_dev_type(struct device *dev,
}
static int arm_smmu_attach_dev_identity(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
return arm_smmu_attach_dev_type(dev, S2CR_TYPE_BYPASS);
}
@@ -1249,7 +1251,8 @@ static struct iommu_domain arm_smmu_identity_domain = {
};
static int arm_smmu_attach_dev_blocked(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
return arm_smmu_attach_dev_type(dev, S2CR_TYPE_FAULT);
}
diff --git a/drivers/iommu/arm/arm-smmu/qcom_iommu.c b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
index c5be95e56031..f69d9276dc55 100644
--- a/drivers/iommu/arm/arm-smmu/qcom_iommu.c
+++ b/drivers/iommu/arm/arm-smmu/qcom_iommu.c
@@ -359,7 +359,8 @@ static void qcom_iommu_domain_free(struct iommu_domain *domain)
kfree(qcom_domain);
}
-static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+static int qcom_iommu_attach_dev(struct iommu_domain *domain,
+ struct device *dev, struct iommu_domain *old)
{
struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev);
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
@@ -388,18 +389,18 @@ static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev
}
static int qcom_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
- struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct qcom_iommu_domain *qcom_domain;
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct qcom_iommu_dev *qcom_iommu = dev_iommu_priv_get(dev);
unsigned int i;
- if (domain == identity_domain || !domain)
+ if (old == identity_domain || !old)
return 0;
- qcom_domain = to_qcom_iommu_domain(domain);
+ qcom_domain = to_qcom_iommu_domain(old);
if (WARN_ON(!qcom_domain->iommu))
return -EINVAL;
@@ -565,14 +566,14 @@ static int qcom_iommu_of_xlate(struct device *dev,
qcom_iommu = platform_get_drvdata(iommu_pdev);
+ put_device(&iommu_pdev->dev);
+
/* make sure the asid specified in dt is valid, so we don't have
* to sanity check this elsewhere:
*/
if (WARN_ON(asid > qcom_iommu->max_asid) ||
- WARN_ON(qcom_iommu->ctxs[asid] == NULL)) {
- put_device(&iommu_pdev->dev);
+ WARN_ON(qcom_iommu->ctxs[asid] == NULL))
return -EINVAL;
- }
if (!dev_iommu_priv_get(dev)) {
dev_iommu_priv_set(dev, qcom_iommu);
@@ -581,10 +582,8 @@ static int qcom_iommu_of_xlate(struct device *dev,
* multiple different iommu devices. Multiple context
* banks are ok, but multiple devices are not:
*/
- if (WARN_ON(qcom_iommu != dev_iommu_priv_get(dev))) {
- put_device(&iommu_pdev->dev);
+ if (WARN_ON(qcom_iommu != dev_iommu_priv_get(dev)))
return -EINVAL;
- }
}
return iommu_fwspec_add_ids(dev, &asid, 1);
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 7944a3af4545..c92088855450 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -1439,8 +1439,8 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
* as a bus address, __finalise_sg() will copy the dma
* address into the output segment.
*/
- s->dma_address = pci_p2pdma_bus_addr_map(&p2pdma_state,
- sg_phys(s));
+ s->dma_address = pci_p2pdma_bus_addr_map(
+ p2pdma_state.mem, sg_phys(s));
sg_dma_len(s) = sg->length;
sg_dma_mark_bus_address(s);
continue;
@@ -2008,7 +2008,7 @@ static void iommu_dma_iova_unlink_range_slow(struct device *dev,
end - addr, iovad->granule - iova_start_pad);
if (!dev_is_dma_coherent(dev) &&
- !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+ !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO)))
arch_sync_dma_for_cpu(phys, len, dir);
swiotlb_tbl_unmap_single(dev, phys, len, dir, attrs);
@@ -2032,7 +2032,8 @@ static void __iommu_dma_iova_unlink(struct device *dev,
size_t unmapped;
if ((state->__size & DMA_IOVA_USE_SWIOTLB) ||
- (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC)))
+ (!dev_is_dma_coherent(dev) &&
+ !(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))))
iommu_dma_iova_unlink_range_slow(dev, addr, size, dir, attrs);
iommu_iotlb_gather_init(&iotlb_gather);
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index b6edd178fe25..b512c6b939ac 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -984,7 +984,8 @@ static void exynos_iommu_domain_free(struct iommu_domain *iommu_domain)
}
static int exynos_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
struct exynos_iommu_domain *domain;
@@ -1035,7 +1036,8 @@ static struct iommu_domain exynos_identity_domain = {
};
static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
@@ -1044,7 +1046,7 @@ static int exynos_iommu_attach_device(struct iommu_domain *iommu_domain,
unsigned long flags;
int err;
- err = exynos_iommu_identity_attach(&exynos_identity_domain, dev);
+ err = exynos_iommu_identity_attach(&exynos_identity_domain, dev, old);
if (err)
return err;
@@ -1429,8 +1431,6 @@ static void exynos_iommu_release_device(struct device *dev)
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
struct sysmmu_drvdata *data;
- WARN_ON(exynos_iommu_identity_attach(&exynos_identity_domain, dev));
-
list_for_each_entry(data, &owner->controllers, owner_node)
device_link_del(data->link);
}
@@ -1446,17 +1446,14 @@ static int exynos_iommu_of_xlate(struct device *dev,
return -ENODEV;
data = platform_get_drvdata(sysmmu);
- if (!data) {
- put_device(&sysmmu->dev);
+ put_device(&sysmmu->dev);
+ if (!data)
return -ENODEV;
- }
if (!owner) {
owner = kzalloc(sizeof(*owner), GFP_KERNEL);
- if (!owner) {
- put_device(&sysmmu->dev);
+ if (!owner)
return -ENOMEM;
- }
INIT_LIST_HEAD(&owner->controllers);
mutex_init(&owner->rpm_lock);
@@ -1476,6 +1473,7 @@ static int exynos_iommu_of_xlate(struct device *dev,
static const struct iommu_ops exynos_iommu_ops = {
.identity_domain = &exynos_identity_domain,
+ .release_domain = &exynos_identity_domain,
.domain_alloc_paging = exynos_iommu_domain_alloc_paging,
.device_group = generic_device_group,
.probe_device = exynos_iommu_probe_device,
diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c
index 5f08523f97cb..9664ef9840d2 100644
--- a/drivers/iommu/fsl_pamu_domain.c
+++ b/drivers/iommu/fsl_pamu_domain.c
@@ -238,7 +238,7 @@ static int update_domain_stash(struct fsl_dma_domain *dma_domain, u32 val)
}
static int fsl_pamu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct fsl_dma_domain *dma_domain = to_fsl_dma_domain(domain);
unsigned long flags;
@@ -298,9 +298,9 @@ static int fsl_pamu_attach_device(struct iommu_domain *domain,
* switches to what looks like BLOCKING.
*/
static int fsl_pamu_platform_attach(struct iommu_domain *platform_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
- struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct fsl_dma_domain *dma_domain;
const u32 *prop;
int len;
@@ -311,11 +311,11 @@ static int fsl_pamu_platform_attach(struct iommu_domain *platform_domain,
* Hack to keep things working as they always have, only leaving an
* UNMANAGED domain makes it BLOCKING.
*/
- if (domain == platform_domain || !domain ||
- domain->type != IOMMU_DOMAIN_UNMANAGED)
+ if (old == platform_domain || !old ||
+ old->type != IOMMU_DOMAIN_UNMANAGED)
return 0;
- dma_domain = to_fsl_dma_domain(domain);
+ dma_domain = to_fsl_dma_domain(old);
/*
* Use LIODN of the PCI controller while detaching a
diff --git a/drivers/iommu/generic_pt/.kunitconfig b/drivers/iommu/generic_pt/.kunitconfig
new file mode 100644
index 000000000000..52ac9e661ffd
--- /dev/null
+++ b/drivers/iommu/generic_pt/.kunitconfig
@@ -0,0 +1,14 @@
+CONFIG_KUNIT=y
+CONFIG_GENERIC_PT=y
+CONFIG_DEBUG_GENERIC_PT=y
+CONFIG_IOMMU_PT=y
+CONFIG_IOMMU_PT_AMDV1=y
+CONFIG_IOMMU_PT_VTDSS=y
+CONFIG_IOMMU_PT_X86_64=y
+CONFIG_IOMMU_PT_KUNIT_TEST=y
+
+CONFIG_IOMMUFD=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_RUNTIME_TESTING_MENU=y
+CONFIG_IOMMUFD_TEST=y
diff --git a/drivers/iommu/generic_pt/Kconfig b/drivers/iommu/generic_pt/Kconfig
new file mode 100644
index 000000000000..ce4fb4786914
--- /dev/null
+++ b/drivers/iommu/generic_pt/Kconfig
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menuconfig GENERIC_PT
+ bool "Generic Radix Page Table" if COMPILE_TEST
+ help
+ Generic library for building radix tree page tables.
+
+ Generic PT provides a set of HW page table formats and a common
+ set of APIs to work with them.
+
+if GENERIC_PT
+config DEBUG_GENERIC_PT
+ bool "Extra debugging checks for GENERIC_PT"
+ help
+ Enable extra run time debugging checks for GENERIC_PT code. This
+ incurs a runtime cost and should not be enabled for production
+ kernels.
+
+ The kunit tests require this to be enabled to get full coverage.
+
+config IOMMU_PT
+ tristate "IOMMU Page Tables"
+ select IOMMU_API
+ depends on IOMMU_SUPPORT
+ depends on GENERIC_PT
+ help
+ Generic library for building IOMMU page tables
+
+ IOMMU_PT provides an implementation of the page table operations
+ related to struct iommu_domain using GENERIC_PT. It provides a single
+ implementation of the page table operations that can be shared by
+ multiple drivers.
+
+if IOMMU_PT
+config IOMMU_PT_AMDV1
+ tristate "IOMMU page table for 64-bit AMD IOMMU v1"
+ depends on !GENERIC_ATOMIC64 # for cmpxchg64
+ help
+ iommu_domain implementation for the AMD v1 page table. AMDv1 is the
+ "host" page table. It supports granular page sizes of almost every
+ power of 2 and decodes the full 64-bit IOVA space.
+
+ Selected automatically by an IOMMU driver that uses this format.
+
+config IOMMU_PT_VTDSS
+ tristate "IOMMU page table for Intel VT-d Second Stage"
+ depends on !GENERIC_ATOMIC64 # for cmpxchg64
+ help
+ iommu_domain implementation for the Intel VT-d's 64 bit 3/4/5
+ level Second Stage page table. It is similar to the X86_64 format with
+ 4K/2M/1G page sizes.
+
+ Selected automatically by an IOMMU driver that uses this format.
+
+config IOMMU_PT_X86_64
+ tristate "IOMMU page table for x86 64-bit, 4/5 levels"
+ depends on !GENERIC_ATOMIC64 # for cmpxchg64
+ help
+ iommu_domain implementation for the x86 64-bit 4/5 level page table.
+ It supports 4K/2M/1G page sizes and can decode a sign-extended
+ portion of the 64-bit IOVA space.
+
+ Selected automatically by an IOMMU driver that uses this format.
+
+config IOMMU_PT_KUNIT_TEST
+ tristate "IOMMU Page Table KUnit Test" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ depends on IOMMU_PT_AMDV1 || !IOMMU_PT_AMDV1
+ depends on IOMMU_PT_X86_64 || !IOMMU_PT_X86_64
+ depends on IOMMU_PT_VTDSS || !IOMMU_PT_VTDSS
+ default KUNIT_ALL_TESTS
+ help
+ Enable kunit tests for GENERIC_PT and IOMMU_PT that covers all the
+ enabled page table formats. The test covers most of the GENERIC_PT
+ functions provided by the page table format, as well as covering the
+ iommu_domain related functions.
+
+endif
+endif
diff --git a/drivers/iommu/generic_pt/fmt/Makefile b/drivers/iommu/generic_pt/fmt/Makefile
new file mode 100644
index 000000000000..976b49ec97dc
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+
+iommu_pt_fmt-$(CONFIG_IOMMU_PT_AMDV1) += amdv1
+iommu_pt_fmt-$(CONFIG_IOMMUFD_TEST) += mock
+
+iommu_pt_fmt-$(CONFIG_IOMMU_PT_VTDSS) += vtdss
+
+iommu_pt_fmt-$(CONFIG_IOMMU_PT_X86_64) += x86_64
+
+IOMMU_PT_KUNIT_TEST :=
+define create_format
+obj-$(2) += iommu_$(1).o
+iommu_pt_kunit_test-y += kunit_iommu_$(1).o
+CFLAGS_kunit_iommu_$(1).o += -DGENERIC_PT_KUNIT=1
+IOMMU_PT_KUNIT_TEST := iommu_pt_kunit_test.o
+
+endef
+
+$(eval $(foreach fmt,$(iommu_pt_fmt-y),$(call create_format,$(fmt),y)))
+$(eval $(foreach fmt,$(iommu_pt_fmt-m),$(call create_format,$(fmt),m)))
+
+# The kunit objects are constructed by compiling the main source
+# with -DGENERIC_PT_KUNIT
+$(obj)/kunit_iommu_%.o: $(src)/iommu_%.c FORCE
+ $(call rule_mkdir)
+ $(call if_changed_dep,cc_o_c)
+
+obj-$(CONFIG_IOMMU_PT_KUNIT_TEST) += $(IOMMU_PT_KUNIT_TEST)
diff --git a/drivers/iommu/generic_pt/fmt/amdv1.h b/drivers/iommu/generic_pt/fmt/amdv1.h
new file mode 100644
index 000000000000..aa8e1a8ec95f
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/amdv1.h
@@ -0,0 +1,411 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * AMD IOMMU v1 page table
+ *
+ * This is described in Section "2.2.3 I/O Page Tables for Host Translations"
+ * of the "AMD I/O Virtualization Technology (IOMMU) Specification"
+ *
+ * Note the level numbering here matches the core code, so level 0 is the same
+ * as mode 1.
+ *
+ */
+#ifndef __GENERIC_PT_FMT_AMDV1_H
+#define __GENERIC_PT_FMT_AMDV1_H
+
+#include "defs_amdv1.h"
+#include "../pt_defs.h"
+
+#include <asm/page.h>
+#include <linux/bitfield.h>
+#include <linux/container_of.h>
+#include <linux/mem_encrypt.h>
+#include <linux/minmax.h>
+#include <linux/sizes.h>
+#include <linux/string.h>
+
+enum {
+ PT_ITEM_WORD_SIZE = sizeof(u64),
+ /*
+ * The IOMMUFD selftest uses the AMDv1 format with some alterations It
+ * uses a 2k page size to test cases where the CPU page size is not the
+ * same.
+ */
+#ifdef AMDV1_IOMMUFD_SELFTEST
+ PT_MAX_VA_ADDRESS_LG2 = 56,
+ PT_MAX_OUTPUT_ADDRESS_LG2 = 51,
+ PT_MAX_TOP_LEVEL = 4,
+ PT_GRANULE_LG2SZ = 11,
+#else
+ PT_MAX_VA_ADDRESS_LG2 = 64,
+ PT_MAX_OUTPUT_ADDRESS_LG2 = 52,
+ PT_MAX_TOP_LEVEL = 5,
+ PT_GRANULE_LG2SZ = 12,
+#endif
+ PT_TABLEMEM_LG2SZ = 12,
+
+ /* The DTE only has these bits for the top phyiscal address */
+ PT_TOP_PHYS_MASK = GENMASK_ULL(51, 12),
+};
+
+/* PTE bits */
+enum {
+ AMDV1PT_FMT_PR = BIT(0),
+ AMDV1PT_FMT_D = BIT(6),
+ AMDV1PT_FMT_NEXT_LEVEL = GENMASK_ULL(11, 9),
+ AMDV1PT_FMT_OA = GENMASK_ULL(51, 12),
+ AMDV1PT_FMT_FC = BIT_ULL(60),
+ AMDV1PT_FMT_IR = BIT_ULL(61),
+ AMDV1PT_FMT_IW = BIT_ULL(62),
+};
+
+/*
+ * gcc 13 has a bug where it thinks the output of FIELD_GET() is an enum, make
+ * these defines to avoid it.
+ */
+#define AMDV1PT_FMT_NL_DEFAULT 0
+#define AMDV1PT_FMT_NL_SIZE 7
+
+static inline pt_oaddr_t amdv1pt_table_pa(const struct pt_state *pts)
+{
+ u64 entry = pts->entry;
+
+ if (pts_feature(pts, PT_FEAT_AMDV1_ENCRYPT_TABLES))
+ entry = __sme_clr(entry);
+ return oalog2_mul(FIELD_GET(AMDV1PT_FMT_OA, entry), PT_GRANULE_LG2SZ);
+}
+#define pt_table_pa amdv1pt_table_pa
+
+/* Returns the oa for the start of the contiguous entry */
+static inline pt_oaddr_t amdv1pt_entry_oa(const struct pt_state *pts)
+{
+ u64 entry = pts->entry;
+ pt_oaddr_t oa;
+
+ if (pts_feature(pts, PT_FEAT_AMDV1_ENCRYPT_TABLES))
+ entry = __sme_clr(entry);
+ oa = FIELD_GET(AMDV1PT_FMT_OA, entry);
+
+ if (FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, entry) == AMDV1PT_FMT_NL_SIZE) {
+ unsigned int sz_bits = oaffz(oa);
+
+ oa = oalog2_set_mod(oa, 0, sz_bits);
+ } else if (PT_WARN_ON(FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, entry) !=
+ AMDV1PT_FMT_NL_DEFAULT))
+ return 0;
+ return oalog2_mul(oa, PT_GRANULE_LG2SZ);
+}
+#define pt_entry_oa amdv1pt_entry_oa
+
+static inline bool amdv1pt_can_have_leaf(const struct pt_state *pts)
+{
+ /*
+ * Table 15: Page Table Level Parameters
+ * The top most level cannot have translation entries
+ */
+ return pts->level < PT_MAX_TOP_LEVEL;
+}
+#define pt_can_have_leaf amdv1pt_can_have_leaf
+
+/* Body in pt_fmt_defaults.h */
+static inline unsigned int pt_table_item_lg2sz(const struct pt_state *pts);
+
+static inline unsigned int
+amdv1pt_entry_num_contig_lg2(const struct pt_state *pts)
+{
+ u32 code;
+
+ if (FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, pts->entry) ==
+ AMDV1PT_FMT_NL_DEFAULT)
+ return ilog2(1);
+
+ PT_WARN_ON(FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, pts->entry) !=
+ AMDV1PT_FMT_NL_SIZE);
+
+ /*
+ * The contiguous size is encoded in the length of a string of 1's in
+ * the low bits of the OA. Reverse the equation:
+ * code = log2_to_int(num_contig_lg2 + item_lg2sz -
+ * PT_GRANULE_LG2SZ - 1) - 1
+ * Which can be expressed as:
+ * num_contig_lg2 = oalog2_ffz(code) + 1 -
+ * item_lg2sz - PT_GRANULE_LG2SZ
+ *
+ * Assume the bit layout is correct and remove the masking. Reorganize
+ * the equation to move all the arithmetic before the ffz.
+ */
+ code = pts->entry >> (__bf_shf(AMDV1PT_FMT_OA) - 1 +
+ pt_table_item_lg2sz(pts) - PT_GRANULE_LG2SZ);
+ return ffz_t(u32, code);
+}
+#define pt_entry_num_contig_lg2 amdv1pt_entry_num_contig_lg2
+
+static inline unsigned int amdv1pt_num_items_lg2(const struct pt_state *pts)
+{
+ /*
+ * Top entry covers bits [63:57] only, this is handled through
+ * max_vasz_lg2.
+ */
+ if (PT_WARN_ON(pts->level == 5))
+ return 7;
+ return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64));
+}
+#define pt_num_items_lg2 amdv1pt_num_items_lg2
+
+static inline pt_vaddr_t amdv1pt_possible_sizes(const struct pt_state *pts)
+{
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+
+ if (!amdv1pt_can_have_leaf(pts))
+ return 0;
+
+ /*
+ * Table 14: Example Page Size Encodings
+ * Address bits 51:32 can be used to encode page sizes greater than 4
+ * Gbytes. Address bits 63:52 are zero-extended.
+ *
+ * 512GB Pages are not supported due to a hardware bug.
+ * Otherwise every power of two size is supported.
+ */
+ return GENMASK_ULL(min(51, isz_lg2 + amdv1pt_num_items_lg2(pts) - 1),
+ isz_lg2) & ~SZ_512G;
+}
+#define pt_possible_sizes amdv1pt_possible_sizes
+
+static inline enum pt_entry_type amdv1pt_load_entry_raw(struct pt_state *pts)
+{
+ const u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+ unsigned int next_level;
+ u64 entry;
+
+ pts->entry = entry = READ_ONCE(*tablep);
+ if (!(entry & AMDV1PT_FMT_PR))
+ return PT_ENTRY_EMPTY;
+
+ next_level = FIELD_GET(AMDV1PT_FMT_NEXT_LEVEL, pts->entry);
+ if (pts->level == 0 || next_level == AMDV1PT_FMT_NL_DEFAULT ||
+ next_level == AMDV1PT_FMT_NL_SIZE)
+ return PT_ENTRY_OA;
+ return PT_ENTRY_TABLE;
+}
+#define pt_load_entry_raw amdv1pt_load_entry_raw
+
+static inline void
+amdv1pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa,
+ unsigned int oasz_lg2,
+ const struct pt_write_attrs *attrs)
+{
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+ u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+ u64 entry;
+
+ if (!pt_check_install_leaf_args(pts, oa, oasz_lg2))
+ return;
+
+ entry = AMDV1PT_FMT_PR |
+ FIELD_PREP(AMDV1PT_FMT_OA, log2_div(oa, PT_GRANULE_LG2SZ)) |
+ attrs->descriptor_bits;
+
+ if (oasz_lg2 == isz_lg2) {
+ entry |= FIELD_PREP(AMDV1PT_FMT_NEXT_LEVEL,
+ AMDV1PT_FMT_NL_DEFAULT);
+ WRITE_ONCE(*tablep, entry);
+ } else {
+ unsigned int num_contig_lg2 = oasz_lg2 - isz_lg2;
+ u64 *end = tablep + log2_to_int(num_contig_lg2);
+
+ entry |= FIELD_PREP(AMDV1PT_FMT_NEXT_LEVEL,
+ AMDV1PT_FMT_NL_SIZE) |
+ FIELD_PREP(AMDV1PT_FMT_OA,
+ oalog2_to_int(oasz_lg2 - PT_GRANULE_LG2SZ -
+ 1) -
+ 1);
+
+ /* See amdv1pt_clear_entries() */
+ if (num_contig_lg2 <= ilog2(32)) {
+ for (; tablep != end; tablep++)
+ WRITE_ONCE(*tablep, entry);
+ } else {
+ memset64(tablep, entry, log2_to_int(num_contig_lg2));
+ }
+ }
+ pts->entry = entry;
+}
+#define pt_install_leaf_entry amdv1pt_install_leaf_entry
+
+static inline bool amdv1pt_install_table(struct pt_state *pts,
+ pt_oaddr_t table_pa,
+ const struct pt_write_attrs *attrs)
+{
+ u64 entry;
+
+ /*
+ * IR and IW are ANDed from the table levels along with the PTE. We
+ * always control permissions from the PTE, so always set IR and IW for
+ * tables.
+ */
+ entry = AMDV1PT_FMT_PR |
+ FIELD_PREP(AMDV1PT_FMT_NEXT_LEVEL, pts->level) |
+ FIELD_PREP(AMDV1PT_FMT_OA,
+ log2_div(table_pa, PT_GRANULE_LG2SZ)) |
+ AMDV1PT_FMT_IR | AMDV1PT_FMT_IW;
+ if (pts_feature(pts, PT_FEAT_AMDV1_ENCRYPT_TABLES))
+ entry = __sme_set(entry);
+ return pt_table_install64(pts, entry);
+}
+#define pt_install_table amdv1pt_install_table
+
+static inline void amdv1pt_attr_from_entry(const struct pt_state *pts,
+ struct pt_write_attrs *attrs)
+{
+ attrs->descriptor_bits =
+ pts->entry & (AMDV1PT_FMT_FC | AMDV1PT_FMT_IR | AMDV1PT_FMT_IW);
+}
+#define pt_attr_from_entry amdv1pt_attr_from_entry
+
+static inline void amdv1pt_clear_entries(struct pt_state *pts,
+ unsigned int num_contig_lg2)
+{
+ u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+ u64 *end = tablep + log2_to_int(num_contig_lg2);
+
+ /*
+ * gcc generates rep stos for the io-pgtable code, and this difference
+ * can show in microbenchmarks with larger contiguous page sizes.
+ * rep is slower for small cases.
+ */
+ if (num_contig_lg2 <= ilog2(32)) {
+ for (; tablep != end; tablep++)
+ WRITE_ONCE(*tablep, 0);
+ } else {
+ memset64(tablep, 0, log2_to_int(num_contig_lg2));
+ }
+}
+#define pt_clear_entries amdv1pt_clear_entries
+
+static inline bool amdv1pt_entry_is_write_dirty(const struct pt_state *pts)
+{
+ unsigned int num_contig_lg2 = amdv1pt_entry_num_contig_lg2(pts);
+ u64 *tablep = pt_cur_table(pts, u64) +
+ log2_set_mod(pts->index, 0, num_contig_lg2);
+ u64 *end = tablep + log2_to_int(num_contig_lg2);
+
+ for (; tablep != end; tablep++)
+ if (READ_ONCE(*tablep) & AMDV1PT_FMT_D)
+ return true;
+ return false;
+}
+#define pt_entry_is_write_dirty amdv1pt_entry_is_write_dirty
+
+static inline void amdv1pt_entry_make_write_clean(struct pt_state *pts)
+{
+ unsigned int num_contig_lg2 = amdv1pt_entry_num_contig_lg2(pts);
+ u64 *tablep = pt_cur_table(pts, u64) +
+ log2_set_mod(pts->index, 0, num_contig_lg2);
+ u64 *end = tablep + log2_to_int(num_contig_lg2);
+
+ for (; tablep != end; tablep++)
+ WRITE_ONCE(*tablep, READ_ONCE(*tablep) & ~(u64)AMDV1PT_FMT_D);
+}
+#define pt_entry_make_write_clean amdv1pt_entry_make_write_clean
+
+static inline bool amdv1pt_entry_make_write_dirty(struct pt_state *pts)
+{
+ u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+ u64 new = pts->entry | AMDV1PT_FMT_D;
+
+ return try_cmpxchg64(tablep, &pts->entry, new);
+}
+#define pt_entry_make_write_dirty amdv1pt_entry_make_write_dirty
+
+/* --- iommu */
+#include <linux/generic_pt/iommu.h>
+#include <linux/iommu.h>
+
+#define pt_iommu_table pt_iommu_amdv1
+
+/* The common struct is in the per-format common struct */
+static inline struct pt_common *common_from_iommu(struct pt_iommu *iommu_table)
+{
+ return &container_of(iommu_table, struct pt_iommu_amdv1, iommu)
+ ->amdpt.common;
+}
+
+static inline struct pt_iommu *iommu_from_common(struct pt_common *common)
+{
+ return &container_of(common, struct pt_iommu_amdv1, amdpt.common)->iommu;
+}
+
+static inline int amdv1pt_iommu_set_prot(struct pt_common *common,
+ struct pt_write_attrs *attrs,
+ unsigned int iommu_prot)
+{
+ u64 pte = 0;
+
+ if (pt_feature(common, PT_FEAT_AMDV1_FORCE_COHERENCE))
+ pte |= AMDV1PT_FMT_FC;
+ if (iommu_prot & IOMMU_READ)
+ pte |= AMDV1PT_FMT_IR;
+ if (iommu_prot & IOMMU_WRITE)
+ pte |= AMDV1PT_FMT_IW;
+
+ /*
+ * Ideally we'd have an IOMMU_ENCRYPTED flag set by higher levels to
+ * control this. For now if the tables use sme_set then so do the ptes.
+ */
+ if (pt_feature(common, PT_FEAT_AMDV1_ENCRYPT_TABLES))
+ pte = __sme_set(pte);
+
+ attrs->descriptor_bits = pte;
+ return 0;
+}
+#define pt_iommu_set_prot amdv1pt_iommu_set_prot
+
+static inline int amdv1pt_iommu_fmt_init(struct pt_iommu_amdv1 *iommu_table,
+ const struct pt_iommu_amdv1_cfg *cfg)
+{
+ struct pt_amdv1 *table = &iommu_table->amdpt;
+ unsigned int max_vasz_lg2 = PT_MAX_VA_ADDRESS_LG2;
+
+ if (cfg->starting_level == 0 || cfg->starting_level > PT_MAX_TOP_LEVEL)
+ return -EINVAL;
+
+ if (!pt_feature(&table->common, PT_FEAT_DYNAMIC_TOP) &&
+ cfg->starting_level != PT_MAX_TOP_LEVEL)
+ max_vasz_lg2 = PT_GRANULE_LG2SZ +
+ (PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64))) *
+ (cfg->starting_level + 1);
+
+ table->common.max_vasz_lg2 =
+ min(max_vasz_lg2, cfg->common.hw_max_vasz_lg2);
+ table->common.max_oasz_lg2 =
+ min(PT_MAX_OUTPUT_ADDRESS_LG2, cfg->common.hw_max_oasz_lg2);
+ pt_top_set_level(&table->common, cfg->starting_level);
+ return 0;
+}
+#define pt_iommu_fmt_init amdv1pt_iommu_fmt_init
+
+#ifndef PT_FMT_VARIANT
+static inline void
+amdv1pt_iommu_fmt_hw_info(struct pt_iommu_amdv1 *table,
+ const struct pt_range *top_range,
+ struct pt_iommu_amdv1_hw_info *info)
+{
+ info->host_pt_root = virt_to_phys(top_range->top_table);
+ PT_WARN_ON(info->host_pt_root & ~PT_TOP_PHYS_MASK);
+ info->mode = top_range->top_level + 1;
+}
+#define pt_iommu_fmt_hw_info amdv1pt_iommu_fmt_hw_info
+#endif
+
+#if defined(GENERIC_PT_KUNIT)
+static const struct pt_iommu_amdv1_cfg amdv1_kunit_fmt_cfgs[] = {
+ /* Matches what io_pgtable does */
+ [0] = { .starting_level = 2 },
+};
+#define kunit_fmt_cfgs amdv1_kunit_fmt_cfgs
+enum { KUNIT_FMT_FEATURES = 0 };
+#endif
+
+#endif
diff --git a/drivers/iommu/generic_pt/fmt/defs_amdv1.h b/drivers/iommu/generic_pt/fmt/defs_amdv1.h
new file mode 100644
index 000000000000..0b9614ca6d10
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/defs_amdv1.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ */
+#ifndef __GENERIC_PT_FMT_DEFS_AMDV1_H
+#define __GENERIC_PT_FMT_DEFS_AMDV1_H
+
+#include <linux/generic_pt/common.h>
+#include <linux/types.h>
+
+typedef u64 pt_vaddr_t;
+typedef u64 pt_oaddr_t;
+
+struct amdv1pt_write_attrs {
+ u64 descriptor_bits;
+ gfp_t gfp;
+};
+#define pt_write_attrs amdv1pt_write_attrs
+
+#endif
diff --git a/drivers/iommu/generic_pt/fmt/defs_vtdss.h b/drivers/iommu/generic_pt/fmt/defs_vtdss.h
new file mode 100644
index 000000000000..4a239bcaae2a
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/defs_vtdss.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES
+ *
+ */
+#ifndef __GENERIC_PT_FMT_DEFS_VTDSS_H
+#define __GENERIC_PT_FMT_DEFS_VTDSS_H
+
+#include <linux/generic_pt/common.h>
+#include <linux/types.h>
+
+typedef u64 pt_vaddr_t;
+typedef u64 pt_oaddr_t;
+
+struct vtdss_pt_write_attrs {
+ u64 descriptor_bits;
+ gfp_t gfp;
+};
+#define pt_write_attrs vtdss_pt_write_attrs
+
+#endif
diff --git a/drivers/iommu/generic_pt/fmt/defs_x86_64.h b/drivers/iommu/generic_pt/fmt/defs_x86_64.h
new file mode 100644
index 000000000000..6f589e1f55d3
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/defs_x86_64.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ */
+#ifndef __GENERIC_PT_FMT_DEFS_X86_64_H
+#define __GENERIC_PT_FMT_DEFS_X86_64_H
+
+#include <linux/generic_pt/common.h>
+#include <linux/types.h>
+
+typedef u64 pt_vaddr_t;
+typedef u64 pt_oaddr_t;
+
+struct x86_64_pt_write_attrs {
+ u64 descriptor_bits;
+ gfp_t gfp;
+};
+#define pt_write_attrs x86_64_pt_write_attrs
+
+#endif
diff --git a/drivers/iommu/generic_pt/fmt/iommu_amdv1.c b/drivers/iommu/generic_pt/fmt/iommu_amdv1.c
new file mode 100644
index 000000000000..72a2337d0c55
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/iommu_amdv1.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ */
+#define PT_FMT amdv1
+#define PT_SUPPORTED_FEATURES \
+ (BIT(PT_FEAT_FULL_VA) | BIT(PT_FEAT_DYNAMIC_TOP) | \
+ BIT(PT_FEAT_FLUSH_RANGE) | BIT(PT_FEAT_FLUSH_RANGE_NO_GAPS) | \
+ BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) | \
+ BIT(PT_FEAT_AMDV1_FORCE_COHERENCE))
+#define PT_FORCE_ENABLED_FEATURES \
+ (BIT(PT_FEAT_DYNAMIC_TOP) | BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) | \
+ BIT(PT_FEAT_AMDV1_FORCE_COHERENCE))
+
+#include "iommu_template.h"
diff --git a/drivers/iommu/generic_pt/fmt/iommu_mock.c b/drivers/iommu/generic_pt/fmt/iommu_mock.c
new file mode 100644
index 000000000000..74e597cba9d9
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/iommu_mock.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ */
+#define AMDV1_IOMMUFD_SELFTEST 1
+#define PT_FMT amdv1
+#define PT_FMT_VARIANT mock
+#define PT_SUPPORTED_FEATURES 0
+
+#include "iommu_template.h"
diff --git a/drivers/iommu/generic_pt/fmt/iommu_template.h b/drivers/iommu/generic_pt/fmt/iommu_template.h
new file mode 100644
index 000000000000..d28e86abdf2e
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/iommu_template.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Template to build the iommu module and kunit from the format and
+ * implementation headers.
+ *
+ * The format should have:
+ * #define PT_FMT <name>
+ * #define PT_SUPPORTED_FEATURES (BIT(PT_FEAT_xx) | BIT(PT_FEAT_yy))
+ * And optionally:
+ * #define PT_FORCE_ENABLED_FEATURES ..
+ * #define PT_FMT_VARIANT <suffix>
+ */
+#include <linux/args.h>
+#include <linux/stringify.h>
+
+#ifdef PT_FMT_VARIANT
+#define PTPFX_RAW \
+ CONCATENATE(CONCATENATE(PT_FMT, _), PT_FMT_VARIANT)
+#else
+#define PTPFX_RAW PT_FMT
+#endif
+
+#define PTPFX CONCATENATE(PTPFX_RAW, _)
+
+#define _PT_FMT_H PT_FMT.h
+#define PT_FMT_H __stringify(_PT_FMT_H)
+
+#define _PT_DEFS_H CONCATENATE(defs_, _PT_FMT_H)
+#define PT_DEFS_H __stringify(_PT_DEFS_H)
+
+#include <linux/generic_pt/common.h>
+#include PT_DEFS_H
+#include "../pt_defs.h"
+#include PT_FMT_H
+#include "../pt_common.h"
+
+#ifndef GENERIC_PT_KUNIT
+#include "../iommu_pt.h"
+#else
+/*
+ * The makefile will compile the .c file twice, once with GENERIC_PT_KUNIT set
+ * which means we are building the kunit modle.
+ */
+#include "../kunit_generic_pt.h"
+#include "../kunit_iommu_pt.h"
+#endif
diff --git a/drivers/iommu/generic_pt/fmt/iommu_vtdss.c b/drivers/iommu/generic_pt/fmt/iommu_vtdss.c
new file mode 100644
index 000000000000..f551711e2a33
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/iommu_vtdss.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES
+ */
+#define PT_FMT vtdss
+#define PT_SUPPORTED_FEATURES \
+ (BIT(PT_FEAT_FLUSH_RANGE) | BIT(PT_FEAT_VTDSS_FORCE_COHERENCE) | \
+ BIT(PT_FEAT_VTDSS_FORCE_WRITEABLE) | BIT(PT_FEAT_DMA_INCOHERENT))
+
+#include "iommu_template.h"
diff --git a/drivers/iommu/generic_pt/fmt/iommu_x86_64.c b/drivers/iommu/generic_pt/fmt/iommu_x86_64.c
new file mode 100644
index 000000000000..5472660c2d71
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/iommu_x86_64.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ */
+#define PT_FMT x86_64
+#define PT_SUPPORTED_FEATURES \
+ (BIT(PT_FEAT_SIGN_EXTEND) | BIT(PT_FEAT_FLUSH_RANGE) | \
+ BIT(PT_FEAT_FLUSH_RANGE_NO_GAPS) | \
+ BIT(PT_FEAT_X86_64_AMD_ENCRYPT_TABLES) | BIT(PT_FEAT_DMA_INCOHERENT))
+
+#include "iommu_template.h"
diff --git a/drivers/iommu/generic_pt/fmt/vtdss.h b/drivers/iommu/generic_pt/fmt/vtdss.h
new file mode 100644
index 000000000000..f5f8981edde7
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/vtdss.h
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Intel VT-d Second Stange 5/4 level page table
+ *
+ * This is described in
+ * Section "3.7 Second-Stage Translation"
+ * Section "9.8 Second-Stage Paging Entries"
+ *
+ * Of the "Intel Virtualization Technology for Directed I/O Architecture
+ * Specification".
+ *
+ * The named levels in the spec map to the pts->level as:
+ * Table/SS-PTE - 0
+ * Directory/SS-PDE - 1
+ * Directory Ptr/SS-PDPTE - 2
+ * PML4/SS-PML4E - 3
+ * PML5/SS-PML5E - 4
+ */
+#ifndef __GENERIC_PT_FMT_VTDSS_H
+#define __GENERIC_PT_FMT_VTDSS_H
+
+#include "defs_vtdss.h"
+#include "../pt_defs.h"
+
+#include <linux/bitfield.h>
+#include <linux/container_of.h>
+#include <linux/log2.h>
+
+enum {
+ PT_MAX_OUTPUT_ADDRESS_LG2 = 52,
+ PT_MAX_VA_ADDRESS_LG2 = 57,
+ PT_ITEM_WORD_SIZE = sizeof(u64),
+ PT_MAX_TOP_LEVEL = 4,
+ PT_GRANULE_LG2SZ = 12,
+ PT_TABLEMEM_LG2SZ = 12,
+
+ /* SSPTPTR is 4k aligned and limited by HAW */
+ PT_TOP_PHYS_MASK = GENMASK_ULL(63, 12),
+};
+
+/* Shared descriptor bits */
+enum {
+ VTDSS_FMT_R = BIT(0),
+ VTDSS_FMT_W = BIT(1),
+ VTDSS_FMT_A = BIT(8),
+ VTDSS_FMT_D = BIT(9),
+ VTDSS_FMT_SNP = BIT(11),
+ VTDSS_FMT_OA = GENMASK_ULL(51, 12),
+};
+
+/* PDPTE/PDE */
+enum {
+ VTDSS_FMT_PS = BIT(7),
+};
+
+#define common_to_vtdss_pt(common_ptr) \
+ container_of_const(common_ptr, struct pt_vtdss, common)
+#define to_vtdss_pt(pts) common_to_vtdss_pt((pts)->range->common)
+
+static inline pt_oaddr_t vtdss_pt_table_pa(const struct pt_state *pts)
+{
+ return oalog2_mul(FIELD_GET(VTDSS_FMT_OA, pts->entry),
+ PT_TABLEMEM_LG2SZ);
+}
+#define pt_table_pa vtdss_pt_table_pa
+
+static inline pt_oaddr_t vtdss_pt_entry_oa(const struct pt_state *pts)
+{
+ return oalog2_mul(FIELD_GET(VTDSS_FMT_OA, pts->entry),
+ PT_GRANULE_LG2SZ);
+}
+#define pt_entry_oa vtdss_pt_entry_oa
+
+static inline bool vtdss_pt_can_have_leaf(const struct pt_state *pts)
+{
+ return pts->level <= 2;
+}
+#define pt_can_have_leaf vtdss_pt_can_have_leaf
+
+static inline unsigned int vtdss_pt_num_items_lg2(const struct pt_state *pts)
+{
+ return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64));
+}
+#define pt_num_items_lg2 vtdss_pt_num_items_lg2
+
+static inline enum pt_entry_type vtdss_pt_load_entry_raw(struct pt_state *pts)
+{
+ const u64 *tablep = pt_cur_table(pts, u64);
+ u64 entry;
+
+ pts->entry = entry = READ_ONCE(tablep[pts->index]);
+ if (!entry)
+ return PT_ENTRY_EMPTY;
+ if (pts->level == 0 ||
+ (vtdss_pt_can_have_leaf(pts) && (pts->entry & VTDSS_FMT_PS)))
+ return PT_ENTRY_OA;
+ return PT_ENTRY_TABLE;
+}
+#define pt_load_entry_raw vtdss_pt_load_entry_raw
+
+static inline void
+vtdss_pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa,
+ unsigned int oasz_lg2,
+ const struct pt_write_attrs *attrs)
+{
+ u64 *tablep = pt_cur_table(pts, u64);
+ u64 entry;
+
+ if (!pt_check_install_leaf_args(pts, oa, oasz_lg2))
+ return;
+
+ entry = FIELD_PREP(VTDSS_FMT_OA, log2_div(oa, PT_GRANULE_LG2SZ)) |
+ attrs->descriptor_bits;
+ if (pts->level != 0)
+ entry |= VTDSS_FMT_PS;
+
+ WRITE_ONCE(tablep[pts->index], entry);
+ pts->entry = entry;
+}
+#define pt_install_leaf_entry vtdss_pt_install_leaf_entry
+
+static inline bool vtdss_pt_install_table(struct pt_state *pts,
+ pt_oaddr_t table_pa,
+ const struct pt_write_attrs *attrs)
+{
+ u64 entry;
+
+ entry = VTDSS_FMT_R | VTDSS_FMT_W |
+ FIELD_PREP(VTDSS_FMT_OA, log2_div(table_pa, PT_GRANULE_LG2SZ));
+ return pt_table_install64(pts, entry);
+}
+#define pt_install_table vtdss_pt_install_table
+
+static inline void vtdss_pt_attr_from_entry(const struct pt_state *pts,
+ struct pt_write_attrs *attrs)
+{
+ attrs->descriptor_bits = pts->entry &
+ (VTDSS_FMT_R | VTDSS_FMT_W | VTDSS_FMT_SNP);
+}
+#define pt_attr_from_entry vtdss_pt_attr_from_entry
+
+static inline bool vtdss_pt_entry_is_write_dirty(const struct pt_state *pts)
+{
+ u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+
+ return READ_ONCE(*tablep) & VTDSS_FMT_D;
+}
+#define pt_entry_is_write_dirty vtdss_pt_entry_is_write_dirty
+
+static inline void vtdss_pt_entry_make_write_clean(struct pt_state *pts)
+{
+ u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+
+ WRITE_ONCE(*tablep, READ_ONCE(*tablep) & ~(u64)VTDSS_FMT_D);
+}
+#define pt_entry_make_write_clean vtdss_pt_entry_make_write_clean
+
+static inline bool vtdss_pt_entry_make_write_dirty(struct pt_state *pts)
+{
+ u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+ u64 new = pts->entry | VTDSS_FMT_D;
+
+ return try_cmpxchg64(tablep, &pts->entry, new);
+}
+#define pt_entry_make_write_dirty vtdss_pt_entry_make_write_dirty
+
+static inline unsigned int vtdss_pt_max_sw_bit(struct pt_common *common)
+{
+ return 10;
+}
+#define pt_max_sw_bit vtdss_pt_max_sw_bit
+
+static inline u64 vtdss_pt_sw_bit(unsigned int bitnr)
+{
+ if (__builtin_constant_p(bitnr) && bitnr > 10)
+ BUILD_BUG();
+
+ /* Bits marked Ignored in the specification */
+ switch (bitnr) {
+ case 0:
+ return BIT(10);
+ case 1 ... 9:
+ return BIT_ULL((bitnr - 1) + 52);
+ case 10:
+ return BIT_ULL(63);
+ /* Some bits in 9-3 are available in some entries */
+ default:
+ PT_WARN_ON(true);
+ return 0;
+ }
+}
+#define pt_sw_bit vtdss_pt_sw_bit
+
+/* --- iommu */
+#include <linux/generic_pt/iommu.h>
+#include <linux/iommu.h>
+
+#define pt_iommu_table pt_iommu_vtdss
+
+/* The common struct is in the per-format common struct */
+static inline struct pt_common *common_from_iommu(struct pt_iommu *iommu_table)
+{
+ return &container_of(iommu_table, struct pt_iommu_table, iommu)
+ ->vtdss_pt.common;
+}
+
+static inline struct pt_iommu *iommu_from_common(struct pt_common *common)
+{
+ return &container_of(common, struct pt_iommu_table, vtdss_pt.common)
+ ->iommu;
+}
+
+static inline int vtdss_pt_iommu_set_prot(struct pt_common *common,
+ struct pt_write_attrs *attrs,
+ unsigned int iommu_prot)
+{
+ u64 pte = 0;
+
+ /*
+ * VTDSS does not have a present bit, so we tell if any entry is present
+ * by checking for R or W.
+ */
+ if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))
+ return -EINVAL;
+
+ if (iommu_prot & IOMMU_READ)
+ pte |= VTDSS_FMT_R;
+ if (iommu_prot & IOMMU_WRITE)
+ pte |= VTDSS_FMT_W;
+ if (pt_feature(common, PT_FEAT_VTDSS_FORCE_COHERENCE))
+ pte |= VTDSS_FMT_SNP;
+
+ if (pt_feature(common, PT_FEAT_VTDSS_FORCE_WRITEABLE) &&
+ !(iommu_prot & IOMMU_WRITE)) {
+ pr_err_ratelimited(
+ "Read-only mapping is disallowed on the domain which serves as the parent in a nested configuration, due to HW errata (ERRATA_772415_SPR17)\n");
+ return -EINVAL;
+ }
+
+ attrs->descriptor_bits = pte;
+ return 0;
+}
+#define pt_iommu_set_prot vtdss_pt_iommu_set_prot
+
+static inline int vtdss_pt_iommu_fmt_init(struct pt_iommu_vtdss *iommu_table,
+ const struct pt_iommu_vtdss_cfg *cfg)
+{
+ struct pt_vtdss *table = &iommu_table->vtdss_pt;
+
+ if (cfg->top_level > 4 || cfg->top_level < 2)
+ return -EOPNOTSUPP;
+
+ pt_top_set_level(&table->common, cfg->top_level);
+ return 0;
+}
+#define pt_iommu_fmt_init vtdss_pt_iommu_fmt_init
+
+static inline void
+vtdss_pt_iommu_fmt_hw_info(struct pt_iommu_vtdss *table,
+ const struct pt_range *top_range,
+ struct pt_iommu_vtdss_hw_info *info)
+{
+ info->ssptptr = virt_to_phys(top_range->top_table);
+ PT_WARN_ON(info->ssptptr & ~PT_TOP_PHYS_MASK);
+ /*
+ * top_level = 2 = 3 level table aw=1
+ * top_level = 3 = 4 level table aw=2
+ * top_level = 4 = 5 level table aw=3
+ */
+ info->aw = top_range->top_level - 1;
+}
+#define pt_iommu_fmt_hw_info vtdss_pt_iommu_fmt_hw_info
+
+#if defined(GENERIC_PT_KUNIT)
+static const struct pt_iommu_vtdss_cfg vtdss_kunit_fmt_cfgs[] = {
+ [0] = { .common.hw_max_vasz_lg2 = 39, .top_level = 2},
+ [1] = { .common.hw_max_vasz_lg2 = 48, .top_level = 3},
+ [2] = { .common.hw_max_vasz_lg2 = 57, .top_level = 4},
+};
+#define kunit_fmt_cfgs vtdss_kunit_fmt_cfgs
+enum { KUNIT_FMT_FEATURES = BIT(PT_FEAT_VTDSS_FORCE_WRITEABLE) };
+#endif
+#endif
diff --git a/drivers/iommu/generic_pt/fmt/x86_64.h b/drivers/iommu/generic_pt/fmt/x86_64.h
new file mode 100644
index 000000000000..210748d9d6e8
--- /dev/null
+++ b/drivers/iommu/generic_pt/fmt/x86_64.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * x86 page table. Supports the 4 and 5 level variations.
+ *
+ * The 4 and 5 level version is described in:
+ * Section "4.4 4-Level Paging and 5-Level Paging" of the Intel Software
+ * Developer's Manual Volume 3
+ *
+ * Section "9.7 First-Stage Paging Entries" of the "Intel Virtualization
+ * Technology for Directed I/O Architecture Specification"
+ *
+ * Section "2.2.6 I/O Page Tables for Guest Translations" of the "AMD I/O
+ * Virtualization Technology (IOMMU) Specification"
+ *
+ * It is used by x86 CPUs, AMD and VT-d IOMMU HW.
+ *
+ * Note the 3 level format is very similar and almost implemented here. The
+ * reserved/ignored layout is different and there are functional bit
+ * differences.
+ *
+ * This format uses PT_FEAT_SIGN_EXTEND to have a upper/non-canonical/lower
+ * split. PT_FEAT_SIGN_EXTEND is optional as AMD IOMMU sometimes uses non-sign
+ * extended addressing with this page table format.
+ *
+ * The named levels in the spec map to the pts->level as:
+ * Table/PTE - 0
+ * Directory/PDE - 1
+ * Directory Ptr/PDPTE - 2
+ * PML4/PML4E - 3
+ * PML5/PML5E - 4
+ */
+#ifndef __GENERIC_PT_FMT_X86_64_H
+#define __GENERIC_PT_FMT_X86_64_H
+
+#include "defs_x86_64.h"
+#include "../pt_defs.h"
+
+#include <linux/bitfield.h>
+#include <linux/container_of.h>
+#include <linux/log2.h>
+#include <linux/mem_encrypt.h>
+
+enum {
+ PT_MAX_OUTPUT_ADDRESS_LG2 = 52,
+ PT_MAX_VA_ADDRESS_LG2 = 57,
+ PT_ITEM_WORD_SIZE = sizeof(u64),
+ PT_MAX_TOP_LEVEL = 4,
+ PT_GRANULE_LG2SZ = 12,
+ PT_TABLEMEM_LG2SZ = 12,
+
+ /*
+ * For AMD the GCR3 Base only has these bits. For VT-d FSPTPTR is 4k
+ * aligned and is limited by the architected HAW
+ */
+ PT_TOP_PHYS_MASK = GENMASK_ULL(51, 12),
+};
+
+/* Shared descriptor bits */
+enum {
+ X86_64_FMT_P = BIT(0),
+ X86_64_FMT_RW = BIT(1),
+ X86_64_FMT_U = BIT(2),
+ X86_64_FMT_A = BIT(5),
+ X86_64_FMT_D = BIT(6),
+ X86_64_FMT_OA = GENMASK_ULL(51, 12),
+ X86_64_FMT_XD = BIT_ULL(63),
+};
+
+/* PDPTE/PDE */
+enum {
+ X86_64_FMT_PS = BIT(7),
+};
+
+static inline pt_oaddr_t x86_64_pt_table_pa(const struct pt_state *pts)
+{
+ u64 entry = pts->entry;
+
+ if (pts_feature(pts, PT_FEAT_X86_64_AMD_ENCRYPT_TABLES))
+ entry = __sme_clr(entry);
+ return oalog2_mul(FIELD_GET(X86_64_FMT_OA, entry),
+ PT_TABLEMEM_LG2SZ);
+}
+#define pt_table_pa x86_64_pt_table_pa
+
+static inline pt_oaddr_t x86_64_pt_entry_oa(const struct pt_state *pts)
+{
+ u64 entry = pts->entry;
+
+ if (pts_feature(pts, PT_FEAT_X86_64_AMD_ENCRYPT_TABLES))
+ entry = __sme_clr(entry);
+ return oalog2_mul(FIELD_GET(X86_64_FMT_OA, entry),
+ PT_GRANULE_LG2SZ);
+}
+#define pt_entry_oa x86_64_pt_entry_oa
+
+static inline bool x86_64_pt_can_have_leaf(const struct pt_state *pts)
+{
+ return pts->level <= 2;
+}
+#define pt_can_have_leaf x86_64_pt_can_have_leaf
+
+static inline unsigned int x86_64_pt_num_items_lg2(const struct pt_state *pts)
+{
+ return PT_TABLEMEM_LG2SZ - ilog2(sizeof(u64));
+}
+#define pt_num_items_lg2 x86_64_pt_num_items_lg2
+
+static inline enum pt_entry_type x86_64_pt_load_entry_raw(struct pt_state *pts)
+{
+ const u64 *tablep = pt_cur_table(pts, u64);
+ u64 entry;
+
+ pts->entry = entry = READ_ONCE(tablep[pts->index]);
+ if (!(entry & X86_64_FMT_P))
+ return PT_ENTRY_EMPTY;
+ if (pts->level == 0 ||
+ (x86_64_pt_can_have_leaf(pts) && (entry & X86_64_FMT_PS)))
+ return PT_ENTRY_OA;
+ return PT_ENTRY_TABLE;
+}
+#define pt_load_entry_raw x86_64_pt_load_entry_raw
+
+static inline void
+x86_64_pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa,
+ unsigned int oasz_lg2,
+ const struct pt_write_attrs *attrs)
+{
+ u64 *tablep = pt_cur_table(pts, u64);
+ u64 entry;
+
+ if (!pt_check_install_leaf_args(pts, oa, oasz_lg2))
+ return;
+
+ entry = X86_64_FMT_P |
+ FIELD_PREP(X86_64_FMT_OA, log2_div(oa, PT_GRANULE_LG2SZ)) |
+ attrs->descriptor_bits;
+ if (pts->level != 0)
+ entry |= X86_64_FMT_PS;
+
+ WRITE_ONCE(tablep[pts->index], entry);
+ pts->entry = entry;
+}
+#define pt_install_leaf_entry x86_64_pt_install_leaf_entry
+
+static inline bool x86_64_pt_install_table(struct pt_state *pts,
+ pt_oaddr_t table_pa,
+ const struct pt_write_attrs *attrs)
+{
+ u64 entry;
+
+ entry = X86_64_FMT_P | X86_64_FMT_RW | X86_64_FMT_U | X86_64_FMT_A |
+ FIELD_PREP(X86_64_FMT_OA, log2_div(table_pa, PT_GRANULE_LG2SZ));
+ if (pts_feature(pts, PT_FEAT_X86_64_AMD_ENCRYPT_TABLES))
+ entry = __sme_set(entry);
+ return pt_table_install64(pts, entry);
+}
+#define pt_install_table x86_64_pt_install_table
+
+static inline void x86_64_pt_attr_from_entry(const struct pt_state *pts,
+ struct pt_write_attrs *attrs)
+{
+ attrs->descriptor_bits = pts->entry &
+ (X86_64_FMT_RW | X86_64_FMT_U | X86_64_FMT_A |
+ X86_64_FMT_D | X86_64_FMT_XD);
+}
+#define pt_attr_from_entry x86_64_pt_attr_from_entry
+
+static inline unsigned int x86_64_pt_max_sw_bit(struct pt_common *common)
+{
+ return 12;
+}
+#define pt_max_sw_bit x86_64_pt_max_sw_bit
+
+static inline u64 x86_64_pt_sw_bit(unsigned int bitnr)
+{
+ if (__builtin_constant_p(bitnr) && bitnr > 12)
+ BUILD_BUG();
+
+ /* Bits marked Ignored/AVL in the specification */
+ switch (bitnr) {
+ case 0:
+ return BIT(9);
+ case 1:
+ return BIT(11);
+ case 2 ... 12:
+ return BIT_ULL((bitnr - 2) + 52);
+ /* Some bits in 8,6,4,3 are available in some entries */
+ default:
+ PT_WARN_ON(true);
+ return 0;
+ }
+}
+#define pt_sw_bit x86_64_pt_sw_bit
+
+/* --- iommu */
+#include <linux/generic_pt/iommu.h>
+#include <linux/iommu.h>
+
+#define pt_iommu_table pt_iommu_x86_64
+
+/* The common struct is in the per-format common struct */
+static inline struct pt_common *common_from_iommu(struct pt_iommu *iommu_table)
+{
+ return &container_of(iommu_table, struct pt_iommu_table, iommu)
+ ->x86_64_pt.common;
+}
+
+static inline struct pt_iommu *iommu_from_common(struct pt_common *common)
+{
+ return &container_of(common, struct pt_iommu_table, x86_64_pt.common)
+ ->iommu;
+}
+
+static inline int x86_64_pt_iommu_set_prot(struct pt_common *common,
+ struct pt_write_attrs *attrs,
+ unsigned int iommu_prot)
+{
+ u64 pte;
+
+ pte = X86_64_FMT_U | X86_64_FMT_A;
+ if (iommu_prot & IOMMU_WRITE)
+ pte |= X86_64_FMT_RW | X86_64_FMT_D;
+
+ /*
+ * Ideally we'd have an IOMMU_ENCRYPTED flag set by higher levels to
+ * control this. For now if the tables use sme_set then so do the ptes.
+ */
+ if (pt_feature(common, PT_FEAT_X86_64_AMD_ENCRYPT_TABLES))
+ pte = __sme_set(pte);
+
+ attrs->descriptor_bits = pte;
+ return 0;
+}
+#define pt_iommu_set_prot x86_64_pt_iommu_set_prot
+
+static inline int
+x86_64_pt_iommu_fmt_init(struct pt_iommu_x86_64 *iommu_table,
+ const struct pt_iommu_x86_64_cfg *cfg)
+{
+ struct pt_x86_64 *table = &iommu_table->x86_64_pt;
+
+ if (cfg->top_level < 3 || cfg->top_level > 4)
+ return -EOPNOTSUPP;
+
+ pt_top_set_level(&table->common, cfg->top_level);
+
+ table->common.max_oasz_lg2 =
+ min(PT_MAX_OUTPUT_ADDRESS_LG2, cfg->common.hw_max_oasz_lg2);
+ return 0;
+}
+#define pt_iommu_fmt_init x86_64_pt_iommu_fmt_init
+
+static inline void
+x86_64_pt_iommu_fmt_hw_info(struct pt_iommu_x86_64 *table,
+ const struct pt_range *top_range,
+ struct pt_iommu_x86_64_hw_info *info)
+{
+ info->gcr3_pt = virt_to_phys(top_range->top_table);
+ PT_WARN_ON(info->gcr3_pt & ~PT_TOP_PHYS_MASK);
+ info->levels = top_range->top_level + 1;
+}
+#define pt_iommu_fmt_hw_info x86_64_pt_iommu_fmt_hw_info
+
+#if defined(GENERIC_PT_KUNIT)
+static const struct pt_iommu_x86_64_cfg x86_64_kunit_fmt_cfgs[] = {
+ [0] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND),
+ .common.hw_max_vasz_lg2 = 48, .top_level = 3 },
+ [1] = { .common.features = BIT(PT_FEAT_SIGN_EXTEND),
+ .common.hw_max_vasz_lg2 = 57, .top_level = 4 },
+ /* AMD IOMMU PASID 0 formats with no SIGN_EXTEND */
+ [2] = { .common.hw_max_vasz_lg2 = 47, .top_level = 3 },
+ [3] = { .common.hw_max_vasz_lg2 = 56, .top_level = 4},
+};
+#define kunit_fmt_cfgs x86_64_kunit_fmt_cfgs
+enum { KUNIT_FMT_FEATURES = BIT(PT_FEAT_SIGN_EXTEND)};
+#endif
+#endif
diff --git a/drivers/iommu/generic_pt/iommu_pt.h b/drivers/iommu/generic_pt/iommu_pt.h
new file mode 100644
index 000000000000..97aeda1ad01c
--- /dev/null
+++ b/drivers/iommu/generic_pt/iommu_pt.h
@@ -0,0 +1,1289 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * "Templated C code" for implementing the iommu operations for page tables.
+ * This is compiled multiple times, over all the page table formats to pick up
+ * the per-format definitions.
+ */
+#ifndef __GENERIC_PT_IOMMU_PT_H
+#define __GENERIC_PT_IOMMU_PT_H
+
+#include "pt_iter.h"
+
+#include <linux/export.h>
+#include <linux/iommu.h>
+#include "../iommu-pages.h"
+#include <linux/cleanup.h>
+#include <linux/dma-mapping.h>
+
+enum {
+ SW_BIT_CACHE_FLUSH_DONE = 0,
+};
+
+static void flush_writes_range(const struct pt_state *pts,
+ unsigned int start_index, unsigned int end_index)
+{
+ if (pts_feature(pts, PT_FEAT_DMA_INCOHERENT))
+ iommu_pages_flush_incoherent(
+ iommu_from_common(pts->range->common)->iommu_device,
+ pts->table, start_index * PT_ITEM_WORD_SIZE,
+ (end_index - start_index) * PT_ITEM_WORD_SIZE);
+}
+
+static void flush_writes_item(const struct pt_state *pts)
+{
+ if (pts_feature(pts, PT_FEAT_DMA_INCOHERENT))
+ iommu_pages_flush_incoherent(
+ iommu_from_common(pts->range->common)->iommu_device,
+ pts->table, pts->index * PT_ITEM_WORD_SIZE,
+ PT_ITEM_WORD_SIZE);
+}
+
+static void gather_range_pages(struct iommu_iotlb_gather *iotlb_gather,
+ struct pt_iommu *iommu_table, pt_vaddr_t iova,
+ pt_vaddr_t len,
+ struct iommu_pages_list *free_list)
+{
+ struct pt_common *common = common_from_iommu(iommu_table);
+
+ if (pt_feature(common, PT_FEAT_DMA_INCOHERENT))
+ iommu_pages_stop_incoherent_list(free_list,
+ iommu_table->iommu_device);
+
+ if (pt_feature(common, PT_FEAT_FLUSH_RANGE_NO_GAPS) &&
+ iommu_iotlb_gather_is_disjoint(iotlb_gather, iova, len)) {
+ iommu_iotlb_sync(&iommu_table->domain, iotlb_gather);
+ /*
+ * Note that the sync frees the gather's free list, so we must
+ * not have any pages on that list that are covered by iova/len
+ */
+ } else if (pt_feature(common, PT_FEAT_FLUSH_RANGE)) {
+ iommu_iotlb_gather_add_range(iotlb_gather, iova, len);
+ }
+
+ iommu_pages_list_splice(free_list, &iotlb_gather->freelist);
+}
+
+#define DOMAIN_NS(op) CONCATENATE(CONCATENATE(pt_iommu_, PTPFX), op)
+
+static int make_range_ul(struct pt_common *common, struct pt_range *range,
+ unsigned long iova, unsigned long len)
+{
+ unsigned long last;
+
+ if (unlikely(len == 0))
+ return -EINVAL;
+
+ if (check_add_overflow(iova, len - 1, &last))
+ return -EOVERFLOW;
+
+ *range = pt_make_range(common, iova, last);
+ if (sizeof(iova) > sizeof(range->va)) {
+ if (unlikely(range->va != iova || range->last_va != last))
+ return -EOVERFLOW;
+ }
+ return 0;
+}
+
+static __maybe_unused int make_range_u64(struct pt_common *common,
+ struct pt_range *range, u64 iova,
+ u64 len)
+{
+ if (unlikely(iova > ULONG_MAX || len > ULONG_MAX))
+ return -EOVERFLOW;
+ return make_range_ul(common, range, iova, len);
+}
+
+/*
+ * Some APIs use unsigned long, while othersuse dma_addr_t as the type. Dispatch
+ * to the correct validation based on the type.
+ */
+#define make_range_no_check(common, range, iova, len) \
+ ({ \
+ int ret; \
+ if (sizeof(iova) > sizeof(unsigned long) || \
+ sizeof(len) > sizeof(unsigned long)) \
+ ret = make_range_u64(common, range, iova, len); \
+ else \
+ ret = make_range_ul(common, range, iova, len); \
+ ret; \
+ })
+
+#define make_range(common, range, iova, len) \
+ ({ \
+ int ret = make_range_no_check(common, range, iova, len); \
+ if (!ret) \
+ ret = pt_check_range(range); \
+ ret; \
+ })
+
+static inline unsigned int compute_best_pgsize(struct pt_state *pts,
+ pt_oaddr_t oa)
+{
+ struct pt_iommu *iommu_table = iommu_from_common(pts->range->common);
+
+ if (!pt_can_have_leaf(pts))
+ return 0;
+
+ /*
+ * The page size is limited by the domain's bitmap. This allows the core
+ * code to reduce the supported page sizes by changing the bitmap.
+ */
+ return pt_compute_best_pgsize(pt_possible_sizes(pts) &
+ iommu_table->domain.pgsize_bitmap,
+ pts->range->va, pts->range->last_va, oa);
+}
+
+static __always_inline int __do_iova_to_phys(struct pt_range *range, void *arg,
+ unsigned int level,
+ struct pt_table_p *table,
+ pt_level_fn_t descend_fn)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ pt_oaddr_t *res = arg;
+
+ switch (pt_load_single_entry(&pts)) {
+ case PT_ENTRY_EMPTY:
+ return -ENOENT;
+ case PT_ENTRY_TABLE:
+ return pt_descend(&pts, arg, descend_fn);
+ case PT_ENTRY_OA:
+ *res = pt_entry_oa_exact(&pts);
+ return 0;
+ }
+ return -ENOENT;
+}
+PT_MAKE_LEVELS(__iova_to_phys, __do_iova_to_phys);
+
+/**
+ * iova_to_phys() - Return the output address for the given IOVA
+ * @domain: Table to query
+ * @iova: IO virtual address to query
+ *
+ * Determine the output address from the given IOVA. @iova may have any
+ * alignment, the returned physical will be adjusted with any sub page offset.
+ *
+ * Context: The caller must hold a read range lock that includes @iova.
+ *
+ * Return: 0 if there is no translation for the given iova.
+ */
+phys_addr_t DOMAIN_NS(iova_to_phys)(struct iommu_domain *domain,
+ dma_addr_t iova)
+{
+ struct pt_iommu *iommu_table =
+ container_of(domain, struct pt_iommu, domain);
+ struct pt_range range;
+ pt_oaddr_t res;
+ int ret;
+
+ ret = make_range(common_from_iommu(iommu_table), &range, iova, 1);
+ if (ret)
+ return ret;
+
+ ret = pt_walk_range(&range, __iova_to_phys, &res);
+ /* PHYS_ADDR_MAX would be a better error code */
+ if (ret)
+ return 0;
+ return res;
+}
+EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(iova_to_phys), "GENERIC_PT_IOMMU");
+
+struct pt_iommu_dirty_args {
+ struct iommu_dirty_bitmap *dirty;
+ unsigned int flags;
+};
+
+static void record_dirty(struct pt_state *pts,
+ struct pt_iommu_dirty_args *dirty,
+ unsigned int num_contig_lg2)
+{
+ pt_vaddr_t dirty_len;
+
+ if (num_contig_lg2 != ilog2(1)) {
+ unsigned int index = pts->index;
+ unsigned int end_index = log2_set_mod_max_t(
+ unsigned int, pts->index, num_contig_lg2);
+
+ /* Adjust for being contained inside a contiguous page */
+ end_index = min(end_index, pts->end_index);
+ dirty_len = (end_index - index) *
+ log2_to_int(pt_table_item_lg2sz(pts));
+ } else {
+ dirty_len = log2_to_int(pt_table_item_lg2sz(pts));
+ }
+
+ if (dirty->dirty->bitmap)
+ iova_bitmap_set(dirty->dirty->bitmap, pts->range->va,
+ dirty_len);
+
+ if (!(dirty->flags & IOMMU_DIRTY_NO_CLEAR)) {
+ /*
+ * No write log required because DMA incoherence and atomic
+ * dirty tracking bits can't work together
+ */
+ pt_entry_make_write_clean(pts);
+ iommu_iotlb_gather_add_range(dirty->dirty->gather,
+ pts->range->va, dirty_len);
+ }
+}
+
+static inline int __read_and_clear_dirty(struct pt_range *range, void *arg,
+ unsigned int level,
+ struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct pt_iommu_dirty_args *dirty = arg;
+ int ret;
+
+ for_each_pt_level_entry(&pts) {
+ if (pts.type == PT_ENTRY_TABLE) {
+ ret = pt_descend(&pts, arg, __read_and_clear_dirty);
+ if (ret)
+ return ret;
+ continue;
+ }
+ if (pts.type == PT_ENTRY_OA && pt_entry_is_write_dirty(&pts))
+ record_dirty(&pts, dirty,
+ pt_entry_num_contig_lg2(&pts));
+ }
+ return 0;
+}
+
+/**
+ * read_and_clear_dirty() - Manipulate the HW set write dirty state
+ * @domain: Domain to manipulate
+ * @iova: IO virtual address to start
+ * @size: Length of the IOVA
+ * @flags: A bitmap of IOMMU_DIRTY_NO_CLEAR
+ * @dirty: Place to store the dirty bits
+ *
+ * Iterate over all the entries in the mapped range and record their write dirty
+ * status in iommu_dirty_bitmap. If IOMMU_DIRTY_NO_CLEAR is not specified then
+ * the entries will be left dirty, otherwise they are returned to being not
+ * write dirty.
+ *
+ * Context: The caller must hold a read range lock that includes @iova.
+ *
+ * Returns: -ERRNO on failure, 0 on success.
+ */
+int DOMAIN_NS(read_and_clear_dirty)(struct iommu_domain *domain,
+ unsigned long iova, size_t size,
+ unsigned long flags,
+ struct iommu_dirty_bitmap *dirty)
+{
+ struct pt_iommu *iommu_table =
+ container_of(domain, struct pt_iommu, domain);
+ struct pt_iommu_dirty_args dirty_args = {
+ .dirty = dirty,
+ .flags = flags,
+ };
+ struct pt_range range;
+ int ret;
+
+#if !IS_ENABLED(CONFIG_IOMMUFD_DRIVER) || !defined(pt_entry_is_write_dirty)
+ return -EOPNOTSUPP;
+#endif
+
+ ret = make_range(common_from_iommu(iommu_table), &range, iova, size);
+ if (ret)
+ return ret;
+
+ ret = pt_walk_range(&range, __read_and_clear_dirty, &dirty_args);
+ PT_WARN_ON(ret);
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(read_and_clear_dirty), "GENERIC_PT_IOMMU");
+
+static inline int __set_dirty(struct pt_range *range, void *arg,
+ unsigned int level, struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+
+ switch (pt_load_single_entry(&pts)) {
+ case PT_ENTRY_EMPTY:
+ return -ENOENT;
+ case PT_ENTRY_TABLE:
+ return pt_descend(&pts, arg, __set_dirty);
+ case PT_ENTRY_OA:
+ if (!pt_entry_make_write_dirty(&pts))
+ return -EAGAIN;
+ return 0;
+ }
+ return -ENOENT;
+}
+
+static int __maybe_unused NS(set_dirty)(struct pt_iommu *iommu_table,
+ dma_addr_t iova)
+{
+ struct pt_range range;
+ int ret;
+
+ ret = make_range(common_from_iommu(iommu_table), &range, iova, 1);
+ if (ret)
+ return ret;
+
+ /*
+ * Note: There is no locking here yet, if the test suite races this it
+ * can crash. It should use RCU locking eventually.
+ */
+ return pt_walk_range(&range, __set_dirty, NULL);
+}
+
+struct pt_iommu_collect_args {
+ struct iommu_pages_list free_list;
+ /* Fail if any OAs are within the range */
+ u8 check_mapped : 1;
+};
+
+static int __collect_tables(struct pt_range *range, void *arg,
+ unsigned int level, struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct pt_iommu_collect_args *collect = arg;
+ int ret;
+
+ if (!collect->check_mapped && !pt_can_have_table(&pts))
+ return 0;
+
+ for_each_pt_level_entry(&pts) {
+ if (pts.type == PT_ENTRY_TABLE) {
+ iommu_pages_list_add(&collect->free_list, pts.table_lower);
+ ret = pt_descend(&pts, arg, __collect_tables);
+ if (ret)
+ return ret;
+ continue;
+ }
+ if (pts.type == PT_ENTRY_OA && collect->check_mapped)
+ return -EADDRINUSE;
+ }
+ return 0;
+}
+
+enum alloc_mode {ALLOC_NORMAL, ALLOC_DEFER_COHERENT_FLUSH};
+
+/* Allocate a table, the empty table will be ready to be installed. */
+static inline struct pt_table_p *_table_alloc(struct pt_common *common,
+ size_t lg2sz, gfp_t gfp,
+ enum alloc_mode mode)
+{
+ struct pt_iommu *iommu_table = iommu_from_common(common);
+ struct pt_table_p *table_mem;
+
+ table_mem = iommu_alloc_pages_node_sz(iommu_table->nid, gfp,
+ log2_to_int(lg2sz));
+ if (pt_feature(common, PT_FEAT_DMA_INCOHERENT) &&
+ mode == ALLOC_NORMAL) {
+ int ret = iommu_pages_start_incoherent(
+ table_mem, iommu_table->iommu_device);
+ if (ret) {
+ iommu_free_pages(table_mem);
+ return ERR_PTR(ret);
+ }
+ }
+ return table_mem;
+}
+
+static inline struct pt_table_p *table_alloc_top(struct pt_common *common,
+ uintptr_t top_of_table,
+ gfp_t gfp,
+ enum alloc_mode mode)
+{
+ /*
+ * Top doesn't need the free list or otherwise, so it technically
+ * doesn't need to use iommu pages. Use the API anyhow as the top is
+ * usually not smaller than PAGE_SIZE to keep things simple.
+ */
+ return _table_alloc(common, pt_top_memsize_lg2(common, top_of_table),
+ gfp, mode);
+}
+
+/* Allocate an interior table */
+static inline struct pt_table_p *table_alloc(const struct pt_state *parent_pts,
+ gfp_t gfp, enum alloc_mode mode)
+{
+ struct pt_state child_pts =
+ pt_init(parent_pts->range, parent_pts->level - 1, NULL);
+
+ return _table_alloc(parent_pts->range->common,
+ pt_num_items_lg2(&child_pts) +
+ ilog2(PT_ITEM_WORD_SIZE),
+ gfp, mode);
+}
+
+static inline int pt_iommu_new_table(struct pt_state *pts,
+ struct pt_write_attrs *attrs)
+{
+ struct pt_table_p *table_mem;
+ phys_addr_t phys;
+
+ /* Given PA/VA/length can't be represented */
+ if (PT_WARN_ON(!pt_can_have_table(pts)))
+ return -ENXIO;
+
+ table_mem = table_alloc(pts, attrs->gfp, ALLOC_NORMAL);
+ if (IS_ERR(table_mem))
+ return PTR_ERR(table_mem);
+
+ phys = virt_to_phys(table_mem);
+ if (!pt_install_table(pts, phys, attrs)) {
+ iommu_pages_free_incoherent(
+ table_mem,
+ iommu_from_common(pts->range->common)->iommu_device);
+ return -EAGAIN;
+ }
+
+ if (pts_feature(pts, PT_FEAT_DMA_INCOHERENT)) {
+ flush_writes_item(pts);
+ pt_set_sw_bit_release(pts, SW_BIT_CACHE_FLUSH_DONE);
+ }
+
+ if (IS_ENABLED(CONFIG_DEBUG_GENERIC_PT)) {
+ /*
+ * The underlying table can't store the physical table address.
+ * This happens when kunit testing tables outside their normal
+ * environment where a CPU might be limited.
+ */
+ pt_load_single_entry(pts);
+ if (PT_WARN_ON(pt_table_pa(pts) != phys)) {
+ pt_clear_entries(pts, ilog2(1));
+ iommu_pages_free_incoherent(
+ table_mem, iommu_from_common(pts->range->common)
+ ->iommu_device);
+ return -EINVAL;
+ }
+ }
+
+ pts->table_lower = table_mem;
+ return 0;
+}
+
+struct pt_iommu_map_args {
+ struct iommu_iotlb_gather *iotlb_gather;
+ struct pt_write_attrs attrs;
+ pt_oaddr_t oa;
+ unsigned int leaf_pgsize_lg2;
+ unsigned int leaf_level;
+};
+
+/*
+ * This will recursively check any tables in the block to validate they are
+ * empty and then free them through the gather.
+ */
+static int clear_contig(const struct pt_state *start_pts,
+ struct iommu_iotlb_gather *iotlb_gather,
+ unsigned int step, unsigned int pgsize_lg2)
+{
+ struct pt_iommu *iommu_table =
+ iommu_from_common(start_pts->range->common);
+ struct pt_range range = *start_pts->range;
+ struct pt_state pts =
+ pt_init(&range, start_pts->level, start_pts->table);
+ struct pt_iommu_collect_args collect = { .check_mapped = true };
+ int ret;
+
+ pts.index = start_pts->index;
+ pts.end_index = start_pts->index + step;
+ for (; _pt_iter_load(&pts); pt_next_entry(&pts)) {
+ if (pts.type == PT_ENTRY_TABLE) {
+ collect.free_list =
+ IOMMU_PAGES_LIST_INIT(collect.free_list);
+ ret = pt_walk_descend_all(&pts, __collect_tables,
+ &collect);
+ if (ret)
+ return ret;
+
+ /*
+ * The table item must be cleared before we can update
+ * the gather
+ */
+ pt_clear_entries(&pts, ilog2(1));
+ flush_writes_item(&pts);
+
+ iommu_pages_list_add(&collect.free_list,
+ pt_table_ptr(&pts));
+ gather_range_pages(
+ iotlb_gather, iommu_table, range.va,
+ log2_to_int(pt_table_item_lg2sz(&pts)),
+ &collect.free_list);
+ } else if (pts.type != PT_ENTRY_EMPTY) {
+ return -EADDRINUSE;
+ }
+ }
+ return 0;
+}
+
+static int __map_range_leaf(struct pt_range *range, void *arg,
+ unsigned int level, struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct pt_iommu_map_args *map = arg;
+ unsigned int leaf_pgsize_lg2 = map->leaf_pgsize_lg2;
+ unsigned int start_index;
+ pt_oaddr_t oa = map->oa;
+ unsigned int step;
+ bool need_contig;
+ int ret = 0;
+
+ PT_WARN_ON(map->leaf_level != level);
+ PT_WARN_ON(!pt_can_have_leaf(&pts));
+
+ step = log2_to_int_t(unsigned int,
+ leaf_pgsize_lg2 - pt_table_item_lg2sz(&pts));
+ need_contig = leaf_pgsize_lg2 != pt_table_item_lg2sz(&pts);
+
+ _pt_iter_first(&pts);
+ start_index = pts.index;
+ do {
+ pts.type = pt_load_entry_raw(&pts);
+ if (pts.type != PT_ENTRY_EMPTY || need_contig) {
+ if (pts.index != start_index)
+ pt_index_to_va(&pts);
+ ret = clear_contig(&pts, map->iotlb_gather, step,
+ leaf_pgsize_lg2);
+ if (ret)
+ break;
+ }
+
+ if (IS_ENABLED(CONFIG_DEBUG_GENERIC_PT)) {
+ pt_index_to_va(&pts);
+ PT_WARN_ON(compute_best_pgsize(&pts, oa) !=
+ leaf_pgsize_lg2);
+ }
+ pt_install_leaf_entry(&pts, oa, leaf_pgsize_lg2, &map->attrs);
+
+ oa += log2_to_int(leaf_pgsize_lg2);
+ pts.index += step;
+ } while (pts.index < pts.end_index);
+
+ flush_writes_range(&pts, start_index, pts.index);
+
+ map->oa = oa;
+ return ret;
+}
+
+static int __map_range(struct pt_range *range, void *arg, unsigned int level,
+ struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct pt_iommu_map_args *map = arg;
+ int ret;
+
+ PT_WARN_ON(map->leaf_level == level);
+ PT_WARN_ON(!pt_can_have_table(&pts));
+
+ _pt_iter_first(&pts);
+
+ /* Descend to a child table */
+ do {
+ pts.type = pt_load_entry_raw(&pts);
+
+ if (pts.type != PT_ENTRY_TABLE) {
+ if (pts.type != PT_ENTRY_EMPTY)
+ return -EADDRINUSE;
+ ret = pt_iommu_new_table(&pts, &map->attrs);
+ if (ret) {
+ /*
+ * Racing with another thread installing a table
+ */
+ if (ret == -EAGAIN)
+ continue;
+ return ret;
+ }
+ } else {
+ pts.table_lower = pt_table_ptr(&pts);
+ /*
+ * Racing with a shared pt_iommu_new_table()? The other
+ * thread is still flushing the cache, so we have to
+ * also flush it to ensure that when our thread's map
+ * completes all the table items leading to our mapping
+ * are visible.
+ *
+ * This requires the pt_set_bit_release() to be a
+ * release of the cache flush so that this can acquire
+ * visibility at the iommu.
+ */
+ if (pts_feature(&pts, PT_FEAT_DMA_INCOHERENT) &&
+ !pt_test_sw_bit_acquire(&pts,
+ SW_BIT_CACHE_FLUSH_DONE))
+ flush_writes_item(&pts);
+ }
+
+ /*
+ * The already present table can possibly be shared with another
+ * concurrent map.
+ */
+ if (map->leaf_level == level - 1)
+ ret = pt_descend(&pts, arg, __map_range_leaf);
+ else
+ ret = pt_descend(&pts, arg, __map_range);
+ if (ret)
+ return ret;
+
+ pts.index++;
+ pt_index_to_va(&pts);
+ if (pts.index >= pts.end_index)
+ break;
+ } while (true);
+ return 0;
+}
+
+/*
+ * Fast path for the easy case of mapping a 4k page to an already allocated
+ * table. This is a common workload. If it returns EAGAIN run the full algorithm
+ * instead.
+ */
+static __always_inline int __do_map_single_page(struct pt_range *range,
+ void *arg, unsigned int level,
+ struct pt_table_p *table,
+ pt_level_fn_t descend_fn)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct pt_iommu_map_args *map = arg;
+
+ pts.type = pt_load_single_entry(&pts);
+ if (level == 0) {
+ if (pts.type != PT_ENTRY_EMPTY)
+ return -EADDRINUSE;
+ pt_install_leaf_entry(&pts, map->oa, PAGE_SHIFT,
+ &map->attrs);
+ /* No flush, not used when incoherent */
+ map->oa += PAGE_SIZE;
+ return 0;
+ }
+ if (pts.type == PT_ENTRY_TABLE)
+ return pt_descend(&pts, arg, descend_fn);
+ /* Something else, use the slow path */
+ return -EAGAIN;
+}
+PT_MAKE_LEVELS(__map_single_page, __do_map_single_page);
+
+/*
+ * Add a table to the top, increasing the top level as much as necessary to
+ * encompass range.
+ */
+static int increase_top(struct pt_iommu *iommu_table, struct pt_range *range,
+ struct pt_iommu_map_args *map)
+{
+ struct iommu_pages_list free_list = IOMMU_PAGES_LIST_INIT(free_list);
+ struct pt_common *common = common_from_iommu(iommu_table);
+ uintptr_t top_of_table = READ_ONCE(common->top_of_table);
+ uintptr_t new_top_of_table = top_of_table;
+ struct pt_table_p *table_mem;
+ unsigned int new_level;
+ spinlock_t *domain_lock;
+ unsigned long flags;
+ int ret;
+
+ while (true) {
+ struct pt_range top_range =
+ _pt_top_range(common, new_top_of_table);
+ struct pt_state pts = pt_init_top(&top_range);
+
+ top_range.va = range->va;
+ top_range.last_va = range->last_va;
+
+ if (!pt_check_range(&top_range) &&
+ map->leaf_level <= pts.level) {
+ new_level = pts.level;
+ break;
+ }
+
+ pts.level++;
+ if (pts.level > PT_MAX_TOP_LEVEL ||
+ pt_table_item_lg2sz(&pts) >= common->max_vasz_lg2) {
+ ret = -ERANGE;
+ goto err_free;
+ }
+
+ table_mem =
+ table_alloc_top(common, _pt_top_set(NULL, pts.level),
+ map->attrs.gfp, ALLOC_DEFER_COHERENT_FLUSH);
+ if (IS_ERR(table_mem)) {
+ ret = PTR_ERR(table_mem);
+ goto err_free;
+ }
+ iommu_pages_list_add(&free_list, table_mem);
+
+ /* The new table links to the lower table always at index 0 */
+ top_range.va = 0;
+ top_range.top_level = pts.level;
+ pts.table_lower = pts.table;
+ pts.table = table_mem;
+ pt_load_single_entry(&pts);
+ PT_WARN_ON(pts.index != 0);
+ pt_install_table(&pts, virt_to_phys(pts.table_lower),
+ &map->attrs);
+ new_top_of_table = _pt_top_set(pts.table, pts.level);
+ }
+
+ /*
+ * Avoid double flushing, flush it once after all pt_install_table()
+ */
+ if (pt_feature(common, PT_FEAT_DMA_INCOHERENT)) {
+ ret = iommu_pages_start_incoherent_list(
+ &free_list, iommu_table->iommu_device);
+ if (ret)
+ goto err_free;
+ }
+
+ /*
+ * top_of_table is write locked by the spinlock, but readers can use
+ * READ_ONCE() to get the value. Since we encode both the level and the
+ * pointer in one quanta the lockless reader will always see something
+ * valid. The HW must be updated to the new level under the spinlock
+ * before top_of_table is updated so that concurrent readers don't map
+ * into the new level until it is fully functional. If another thread
+ * already updated it while we were working then throw everything away
+ * and try again.
+ */
+ domain_lock = iommu_table->driver_ops->get_top_lock(iommu_table);
+ spin_lock_irqsave(domain_lock, flags);
+ if (common->top_of_table != top_of_table ||
+ top_of_table == new_top_of_table) {
+ spin_unlock_irqrestore(domain_lock, flags);
+ ret = -EAGAIN;
+ goto err_free;
+ }
+
+ /*
+ * We do not issue any flushes for change_top on the expectation that
+ * any walk cache will not become a problem by adding another layer to
+ * the tree. Misses will rewalk from the updated top pointer, hits
+ * continue to be correct. Negative caching is fine too since all the
+ * new IOVA added by the new top is non-present.
+ */
+ iommu_table->driver_ops->change_top(
+ iommu_table, virt_to_phys(table_mem), new_level);
+ WRITE_ONCE(common->top_of_table, new_top_of_table);
+ spin_unlock_irqrestore(domain_lock, flags);
+ return 0;
+
+err_free:
+ if (pt_feature(common, PT_FEAT_DMA_INCOHERENT))
+ iommu_pages_stop_incoherent_list(&free_list,
+ iommu_table->iommu_device);
+ iommu_put_pages_list(&free_list);
+ return ret;
+}
+
+static int check_map_range(struct pt_iommu *iommu_table, struct pt_range *range,
+ struct pt_iommu_map_args *map)
+{
+ struct pt_common *common = common_from_iommu(iommu_table);
+ int ret;
+
+ do {
+ ret = pt_check_range(range);
+ if (!pt_feature(common, PT_FEAT_DYNAMIC_TOP))
+ return ret;
+
+ if (!ret && map->leaf_level <= range->top_level)
+ break;
+
+ ret = increase_top(iommu_table, range, map);
+ if (ret && ret != -EAGAIN)
+ return ret;
+
+ /* Reload the new top */
+ *range = pt_make_range(common, range->va, range->last_va);
+ } while (ret);
+ PT_WARN_ON(pt_check_range(range));
+ return 0;
+}
+
+static int do_map(struct pt_range *range, struct pt_common *common,
+ bool single_page, struct pt_iommu_map_args *map)
+{
+ /*
+ * The __map_single_page() fast path does not support DMA_INCOHERENT
+ * flushing to keep its .text small.
+ */
+ if (single_page && !pt_feature(common, PT_FEAT_DMA_INCOHERENT)) {
+ int ret;
+
+ ret = pt_walk_range(range, __map_single_page, map);
+ if (ret != -EAGAIN)
+ return ret;
+ /* EAGAIN falls through to the full path */
+ }
+
+ if (map->leaf_level == range->top_level)
+ return pt_walk_range(range, __map_range_leaf, map);
+ return pt_walk_range(range, __map_range, map);
+}
+
+/**
+ * map_pages() - Install translation for an IOVA range
+ * @domain: Domain to manipulate
+ * @iova: IO virtual address to start
+ * @paddr: Physical/Output address to start
+ * @pgsize: Length of each page
+ * @pgcount: Length of the range in pgsize units starting from @iova
+ * @prot: A bitmap of IOMMU_READ/WRITE/CACHE/NOEXEC/MMIO
+ * @gfp: GFP flags for any memory allocations
+ * @mapped: Total bytes successfully mapped
+ *
+ * The range starting at IOVA will have paddr installed into it. The caller
+ * must specify a valid pgsize and pgcount to segment the range into compatible
+ * blocks.
+ *
+ * On error the caller will probably want to invoke unmap on the range from iova
+ * up to the amount indicated by @mapped to return the table back to an
+ * unchanged state.
+ *
+ * Context: The caller must hold a write range lock that includes the whole
+ * range.
+ *
+ * Returns: -ERRNO on failure, 0 on success. The number of bytes of VA that were
+ * mapped are added to @mapped, @mapped is not zerod first.
+ */
+int DOMAIN_NS(map_pages)(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t pgsize, size_t pgcount,
+ int prot, gfp_t gfp, size_t *mapped)
+{
+ struct pt_iommu *iommu_table =
+ container_of(domain, struct pt_iommu, domain);
+ pt_vaddr_t pgsize_bitmap = iommu_table->domain.pgsize_bitmap;
+ struct pt_common *common = common_from_iommu(iommu_table);
+ struct iommu_iotlb_gather iotlb_gather;
+ pt_vaddr_t len = pgsize * pgcount;
+ struct pt_iommu_map_args map = {
+ .iotlb_gather = &iotlb_gather,
+ .oa = paddr,
+ .leaf_pgsize_lg2 = vaffs(pgsize),
+ };
+ bool single_page = false;
+ struct pt_range range;
+ int ret;
+
+ iommu_iotlb_gather_init(&iotlb_gather);
+
+ if (WARN_ON(!(prot & (IOMMU_READ | IOMMU_WRITE))))
+ return -EINVAL;
+
+ /* Check the paddr doesn't exceed what the table can store */
+ if ((sizeof(pt_oaddr_t) < sizeof(paddr) &&
+ (pt_vaddr_t)paddr > PT_VADDR_MAX) ||
+ (common->max_oasz_lg2 != PT_VADDR_MAX_LG2 &&
+ oalog2_div(paddr, common->max_oasz_lg2)))
+ return -ERANGE;
+
+ ret = pt_iommu_set_prot(common, &map.attrs, prot);
+ if (ret)
+ return ret;
+ map.attrs.gfp = gfp;
+
+ ret = make_range_no_check(common, &range, iova, len);
+ if (ret)
+ return ret;
+
+ /* Calculate target page size and level for the leaves */
+ if (pt_has_system_page_size(common) && pgsize == PAGE_SIZE &&
+ pgcount == 1) {
+ PT_WARN_ON(!(pgsize_bitmap & PAGE_SIZE));
+ if (log2_mod(iova | paddr, PAGE_SHIFT))
+ return -ENXIO;
+ map.leaf_pgsize_lg2 = PAGE_SHIFT;
+ map.leaf_level = 0;
+ single_page = true;
+ } else {
+ map.leaf_pgsize_lg2 = pt_compute_best_pgsize(
+ pgsize_bitmap, range.va, range.last_va, paddr);
+ if (!map.leaf_pgsize_lg2)
+ return -ENXIO;
+ map.leaf_level =
+ pt_pgsz_lg2_to_level(common, map.leaf_pgsize_lg2);
+ }
+
+ ret = check_map_range(iommu_table, &range, &map);
+ if (ret)
+ return ret;
+
+ PT_WARN_ON(map.leaf_level > range.top_level);
+
+ ret = do_map(&range, common, single_page, &map);
+
+ /*
+ * Table levels were freed and replaced with large items, flush any walk
+ * cache that may refer to the freed levels.
+ */
+ if (!iommu_pages_list_empty(&iotlb_gather.freelist))
+ iommu_iotlb_sync(&iommu_table->domain, &iotlb_gather);
+
+ /* Bytes successfully mapped */
+ PT_WARN_ON(!ret && map.oa - paddr != len);
+ *mapped += map.oa - paddr;
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(map_pages), "GENERIC_PT_IOMMU");
+
+struct pt_unmap_args {
+ struct iommu_pages_list free_list;
+ pt_vaddr_t unmapped;
+};
+
+static __maybe_unused int __unmap_range(struct pt_range *range, void *arg,
+ unsigned int level,
+ struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct pt_unmap_args *unmap = arg;
+ unsigned int num_oas = 0;
+ unsigned int start_index;
+ int ret = 0;
+
+ _pt_iter_first(&pts);
+ start_index = pts.index;
+ pts.type = pt_load_entry_raw(&pts);
+ /*
+ * A starting index is in the middle of a contiguous entry
+ *
+ * The IOMMU API does not require drivers to support unmapping parts of
+ * large pages. Long ago VFIO would try to split maps but the current
+ * version never does.
+ *
+ * Instead when unmap reaches a partial unmap of the start of a large
+ * IOPTE it should remove the entire IOPTE and return that size to the
+ * caller.
+ */
+ if (pts.type == PT_ENTRY_OA) {
+ if (log2_mod(range->va, pt_entry_oa_lg2sz(&pts)))
+ return -EINVAL;
+ /* Micro optimization */
+ goto start_oa;
+ }
+
+ do {
+ if (pts.type != PT_ENTRY_OA) {
+ bool fully_covered;
+
+ if (pts.type != PT_ENTRY_TABLE) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (pts.index != start_index)
+ pt_index_to_va(&pts);
+ pts.table_lower = pt_table_ptr(&pts);
+
+ fully_covered = pt_entry_fully_covered(
+ &pts, pt_table_item_lg2sz(&pts));
+
+ ret = pt_descend(&pts, arg, __unmap_range);
+ if (ret)
+ break;
+
+ /*
+ * If the unmapping range fully covers the table then we
+ * can free it as well. The clear is delayed until we
+ * succeed in clearing the lower table levels.
+ */
+ if (fully_covered) {
+ iommu_pages_list_add(&unmap->free_list,
+ pts.table_lower);
+ pt_clear_entries(&pts, ilog2(1));
+ }
+ pts.index++;
+ } else {
+ unsigned int num_contig_lg2;
+start_oa:
+ /*
+ * If the caller requested an last that falls within a
+ * single entry then the entire entry is unmapped and
+ * the length returned will be larger than requested.
+ */
+ num_contig_lg2 = pt_entry_num_contig_lg2(&pts);
+ pt_clear_entries(&pts, num_contig_lg2);
+ num_oas += log2_to_int(num_contig_lg2);
+ pts.index += log2_to_int(num_contig_lg2);
+ }
+ if (pts.index >= pts.end_index)
+ break;
+ pts.type = pt_load_entry_raw(&pts);
+ } while (true);
+
+ unmap->unmapped += log2_mul(num_oas, pt_table_item_lg2sz(&pts));
+ flush_writes_range(&pts, start_index, pts.index);
+
+ return ret;
+}
+
+/**
+ * unmap_pages() - Make a range of IOVA empty/not present
+ * @domain: Domain to manipulate
+ * @iova: IO virtual address to start
+ * @pgsize: Length of each page
+ * @pgcount: Length of the range in pgsize units starting from @iova
+ * @iotlb_gather: Gather struct that must be flushed on return
+ *
+ * unmap_pages() will remove a translation created by map_pages(). It cannot
+ * subdivide a mapping created by map_pages(), so it should be called with IOVA
+ * ranges that match those passed to map_pages(). The IOVA range can aggregate
+ * contiguous map_pages() calls so long as no individual range is split.
+ *
+ * Context: The caller must hold a write range lock that includes
+ * the whole range.
+ *
+ * Returns: Number of bytes of VA unmapped. iova + res will be the point
+ * unmapping stopped.
+ */
+size_t DOMAIN_NS(unmap_pages)(struct iommu_domain *domain, unsigned long iova,
+ size_t pgsize, size_t pgcount,
+ struct iommu_iotlb_gather *iotlb_gather)
+{
+ struct pt_iommu *iommu_table =
+ container_of(domain, struct pt_iommu, domain);
+ struct pt_unmap_args unmap = { .free_list = IOMMU_PAGES_LIST_INIT(
+ unmap.free_list) };
+ pt_vaddr_t len = pgsize * pgcount;
+ struct pt_range range;
+ int ret;
+
+ ret = make_range(common_from_iommu(iommu_table), &range, iova, len);
+ if (ret)
+ return 0;
+
+ pt_walk_range(&range, __unmap_range, &unmap);
+
+ gather_range_pages(iotlb_gather, iommu_table, iova, len,
+ &unmap.free_list);
+
+ return unmap.unmapped;
+}
+EXPORT_SYMBOL_NS_GPL(DOMAIN_NS(unmap_pages), "GENERIC_PT_IOMMU");
+
+static void NS(get_info)(struct pt_iommu *iommu_table,
+ struct pt_iommu_info *info)
+{
+ struct pt_common *common = common_from_iommu(iommu_table);
+ struct pt_range range = pt_top_range(common);
+ struct pt_state pts = pt_init_top(&range);
+ pt_vaddr_t pgsize_bitmap = 0;
+
+ if (pt_feature(common, PT_FEAT_DYNAMIC_TOP)) {
+ for (pts.level = 0; pts.level <= PT_MAX_TOP_LEVEL;
+ pts.level++) {
+ if (pt_table_item_lg2sz(&pts) >= common->max_vasz_lg2)
+ break;
+ pgsize_bitmap |= pt_possible_sizes(&pts);
+ }
+ } else {
+ for (pts.level = 0; pts.level <= range.top_level; pts.level++)
+ pgsize_bitmap |= pt_possible_sizes(&pts);
+ }
+
+ /* Hide page sizes larger than the maximum OA */
+ info->pgsize_bitmap = oalog2_mod(pgsize_bitmap, common->max_oasz_lg2);
+}
+
+static void NS(deinit)(struct pt_iommu *iommu_table)
+{
+ struct pt_common *common = common_from_iommu(iommu_table);
+ struct pt_range range = pt_all_range(common);
+ struct pt_iommu_collect_args collect = {
+ .free_list = IOMMU_PAGES_LIST_INIT(collect.free_list),
+ };
+
+ iommu_pages_list_add(&collect.free_list, range.top_table);
+ pt_walk_range(&range, __collect_tables, &collect);
+
+ /*
+ * The driver has to already have fenced the HW access to the page table
+ * and invalidated any caching referring to this memory.
+ */
+ if (pt_feature(common, PT_FEAT_DMA_INCOHERENT))
+ iommu_pages_stop_incoherent_list(&collect.free_list,
+ iommu_table->iommu_device);
+ iommu_put_pages_list(&collect.free_list);
+}
+
+static const struct pt_iommu_ops NS(ops) = {
+#if IS_ENABLED(CONFIG_IOMMUFD_DRIVER) && defined(pt_entry_is_write_dirty) && \
+ IS_ENABLED(CONFIG_IOMMUFD_TEST) && defined(pt_entry_make_write_dirty)
+ .set_dirty = NS(set_dirty),
+#endif
+ .get_info = NS(get_info),
+ .deinit = NS(deinit),
+};
+
+static int pt_init_common(struct pt_common *common)
+{
+ struct pt_range top_range = pt_top_range(common);
+
+ if (PT_WARN_ON(top_range.top_level > PT_MAX_TOP_LEVEL))
+ return -EINVAL;
+
+ if (top_range.top_level == PT_MAX_TOP_LEVEL ||
+ common->max_vasz_lg2 == top_range.max_vasz_lg2)
+ common->features &= ~BIT(PT_FEAT_DYNAMIC_TOP);
+
+ if (top_range.max_vasz_lg2 == PT_VADDR_MAX_LG2)
+ common->features |= BIT(PT_FEAT_FULL_VA);
+
+ /* Requested features must match features compiled into this format */
+ if ((common->features & ~(unsigned int)PT_SUPPORTED_FEATURES) ||
+ (!IS_ENABLED(CONFIG_DEBUG_GENERIC_PT) &&
+ (common->features & PT_FORCE_ENABLED_FEATURES) !=
+ PT_FORCE_ENABLED_FEATURES))
+ return -EOPNOTSUPP;
+
+ /*
+ * Check if the top level of the page table is too small to hold the
+ * specified maxvasz.
+ */
+ if (!pt_feature(common, PT_FEAT_DYNAMIC_TOP) &&
+ top_range.top_level != PT_MAX_TOP_LEVEL) {
+ struct pt_state pts = { .range = &top_range,
+ .level = top_range.top_level };
+
+ if (common->max_vasz_lg2 >
+ pt_num_items_lg2(&pts) + pt_table_item_lg2sz(&pts))
+ return -EOPNOTSUPP;
+ }
+
+ if (common->max_oasz_lg2 == 0)
+ common->max_oasz_lg2 = pt_max_oa_lg2(common);
+ else
+ common->max_oasz_lg2 = min(common->max_oasz_lg2,
+ pt_max_oa_lg2(common));
+ return 0;
+}
+
+static int pt_iommu_init_domain(struct pt_iommu *iommu_table,
+ struct iommu_domain *domain)
+{
+ struct pt_common *common = common_from_iommu(iommu_table);
+ struct pt_iommu_info info;
+ struct pt_range range;
+
+ NS(get_info)(iommu_table, &info);
+
+ domain->type = __IOMMU_DOMAIN_PAGING;
+ domain->pgsize_bitmap = info.pgsize_bitmap;
+
+ if (pt_feature(common, PT_FEAT_DYNAMIC_TOP))
+ range = _pt_top_range(common,
+ _pt_top_set(NULL, PT_MAX_TOP_LEVEL));
+ else
+ range = pt_top_range(common);
+
+ /* A 64-bit high address space table on a 32-bit system cannot work. */
+ domain->geometry.aperture_start = (unsigned long)range.va;
+ if ((pt_vaddr_t)domain->geometry.aperture_start != range.va)
+ return -EOVERFLOW;
+
+ /*
+ * The aperture is limited to what the API can do after considering all
+ * the different types dma_addr_t/unsigned long/pt_vaddr_t that are used
+ * to store a VA. Set the aperture to something that is valid for all
+ * cases. Saturate instead of truncate the end if the types are smaller
+ * than the top range. aperture_end should be called aperture_last.
+ */
+ domain->geometry.aperture_end = (unsigned long)range.last_va;
+ if ((pt_vaddr_t)domain->geometry.aperture_end != range.last_va) {
+ domain->geometry.aperture_end = ULONG_MAX;
+ domain->pgsize_bitmap &= ULONG_MAX;
+ }
+ domain->geometry.force_aperture = true;
+
+ return 0;
+}
+
+static void pt_iommu_zero(struct pt_iommu_table *fmt_table)
+{
+ struct pt_iommu *iommu_table = &fmt_table->iommu;
+ struct pt_iommu cfg = *iommu_table;
+
+ static_assert(offsetof(struct pt_iommu_table, iommu.domain) == 0);
+ memset_after(fmt_table, 0, iommu.domain);
+
+ /* The caller can initialize some of these values */
+ iommu_table->iommu_device = cfg.iommu_device;
+ iommu_table->driver_ops = cfg.driver_ops;
+ iommu_table->nid = cfg.nid;
+}
+
+#define pt_iommu_table_cfg CONCATENATE(pt_iommu_table, _cfg)
+#define pt_iommu_init CONCATENATE(CONCATENATE(pt_iommu_, PTPFX), init)
+
+int pt_iommu_init(struct pt_iommu_table *fmt_table,
+ const struct pt_iommu_table_cfg *cfg, gfp_t gfp)
+{
+ struct pt_iommu *iommu_table = &fmt_table->iommu;
+ struct pt_common *common = common_from_iommu(iommu_table);
+ struct pt_table_p *table_mem;
+ int ret;
+
+ if (cfg->common.hw_max_vasz_lg2 > PT_MAX_VA_ADDRESS_LG2 ||
+ !cfg->common.hw_max_vasz_lg2 || !cfg->common.hw_max_oasz_lg2)
+ return -EINVAL;
+
+ pt_iommu_zero(fmt_table);
+ common->features = cfg->common.features;
+ common->max_vasz_lg2 = cfg->common.hw_max_vasz_lg2;
+ common->max_oasz_lg2 = cfg->common.hw_max_oasz_lg2;
+ ret = pt_iommu_fmt_init(fmt_table, cfg);
+ if (ret)
+ return ret;
+
+ if (cfg->common.hw_max_oasz_lg2 > pt_max_oa_lg2(common))
+ return -EINVAL;
+
+ ret = pt_init_common(common);
+ if (ret)
+ return ret;
+
+ if (pt_feature(common, PT_FEAT_DYNAMIC_TOP) &&
+ WARN_ON(!iommu_table->driver_ops ||
+ !iommu_table->driver_ops->change_top ||
+ !iommu_table->driver_ops->get_top_lock))
+ return -EINVAL;
+
+ if (pt_feature(common, PT_FEAT_SIGN_EXTEND) &&
+ (pt_feature(common, PT_FEAT_FULL_VA) ||
+ pt_feature(common, PT_FEAT_DYNAMIC_TOP)))
+ return -EINVAL;
+
+ if (pt_feature(common, PT_FEAT_DMA_INCOHERENT) &&
+ WARN_ON(!iommu_table->iommu_device))
+ return -EINVAL;
+
+ ret = pt_iommu_init_domain(iommu_table, &iommu_table->domain);
+ if (ret)
+ return ret;
+
+ table_mem = table_alloc_top(common, common->top_of_table, gfp,
+ ALLOC_NORMAL);
+ if (IS_ERR(table_mem))
+ return PTR_ERR(table_mem);
+ pt_top_set(common, table_mem, pt_top_get_level(common));
+
+ /* Must be last, see pt_iommu_deinit() */
+ iommu_table->ops = &NS(ops);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(pt_iommu_init, "GENERIC_PT_IOMMU");
+
+#ifdef pt_iommu_fmt_hw_info
+#define pt_iommu_table_hw_info CONCATENATE(pt_iommu_table, _hw_info)
+#define pt_iommu_hw_info CONCATENATE(CONCATENATE(pt_iommu_, PTPFX), hw_info)
+void pt_iommu_hw_info(struct pt_iommu_table *fmt_table,
+ struct pt_iommu_table_hw_info *info)
+{
+ struct pt_iommu *iommu_table = &fmt_table->iommu;
+ struct pt_common *common = common_from_iommu(iommu_table);
+ struct pt_range top_range = pt_top_range(common);
+
+ pt_iommu_fmt_hw_info(fmt_table, &top_range, info);
+}
+EXPORT_SYMBOL_NS_GPL(pt_iommu_hw_info, "GENERIC_PT_IOMMU");
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("IOMMU Page table implementation for " __stringify(PTPFX_RAW));
+MODULE_IMPORT_NS("GENERIC_PT");
+/* For iommu_dirty_bitmap_record() */
+MODULE_IMPORT_NS("IOMMUFD");
+
+#endif /* __GENERIC_PT_IOMMU_PT_H */
diff --git a/drivers/iommu/generic_pt/kunit_generic_pt.h b/drivers/iommu/generic_pt/kunit_generic_pt.h
new file mode 100644
index 000000000000..68278bf15cfe
--- /dev/null
+++ b/drivers/iommu/generic_pt/kunit_generic_pt.h
@@ -0,0 +1,823 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Test the format API directly.
+ *
+ */
+#include "kunit_iommu.h"
+#include "pt_iter.h"
+
+static void do_map(struct kunit *test, pt_vaddr_t va, pt_oaddr_t pa,
+ pt_vaddr_t len)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ int ret;
+
+ KUNIT_ASSERT_EQ(test, len, (size_t)len);
+
+ ret = iommu_map(&priv->domain, va, pa, len, IOMMU_READ | IOMMU_WRITE,
+ GFP_KERNEL);
+ KUNIT_ASSERT_NO_ERRNO_FN(test, "map_pages", ret);
+}
+
+#define KUNIT_ASSERT_PT_LOAD(test, pts, entry) \
+ ({ \
+ pt_load_entry(pts); \
+ KUNIT_ASSERT_EQ(test, (pts)->type, entry); \
+ })
+
+struct check_levels_arg {
+ struct kunit *test;
+ void *fn_arg;
+ void (*fn)(struct kunit *test, struct pt_state *pts, void *arg);
+};
+
+static int __check_all_levels(struct pt_range *range, void *arg,
+ unsigned int level, struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct check_levels_arg *chk = arg;
+ struct kunit *test = chk->test;
+ int ret;
+
+ _pt_iter_first(&pts);
+
+
+ /*
+ * If we were able to use the full VA space this should always be the
+ * last index in each table.
+ */
+ if (!(IS_32BIT && range->max_vasz_lg2 > 32)) {
+ if (pt_feature(range->common, PT_FEAT_SIGN_EXTEND) &&
+ pts.level == pts.range->top_level)
+ KUNIT_ASSERT_EQ(test, pts.index,
+ log2_to_int(range->max_vasz_lg2 - 1 -
+ pt_table_item_lg2sz(&pts)) -
+ 1);
+ else
+ KUNIT_ASSERT_EQ(test, pts.index,
+ log2_to_int(pt_table_oa_lg2sz(&pts) -
+ pt_table_item_lg2sz(&pts)) -
+ 1);
+ }
+
+ if (pt_can_have_table(&pts)) {
+ pt_load_single_entry(&pts);
+ KUNIT_ASSERT_EQ(test, pts.type, PT_ENTRY_TABLE);
+ ret = pt_descend(&pts, arg, __check_all_levels);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ /* Index 0 is used by the test */
+ if (IS_32BIT && !pts.index)
+ return 0;
+ KUNIT_ASSERT_NE(chk->test, pts.index, 0);
+ }
+
+ /*
+ * A format should not create a table with only one entry, at least this
+ * test approach won't work.
+ */
+ KUNIT_ASSERT_GT(chk->test, pts.end_index, 1);
+
+ /*
+ * For increase top we end up using index 0 for the original top's tree,
+ * so use index 1 for testing instead.
+ */
+ pts.index = 0;
+ pt_index_to_va(&pts);
+ pt_load_single_entry(&pts);
+ if (pts.type == PT_ENTRY_TABLE && pts.end_index > 2) {
+ pts.index = 1;
+ pt_index_to_va(&pts);
+ }
+ (*chk->fn)(chk->test, &pts, chk->fn_arg);
+ return 0;
+}
+
+/*
+ * Call fn for each level in the table with a pts setup to index 0 in a table
+ * for that level. This allows writing tests that run on every level.
+ * The test can use every index in the table except the last one.
+ */
+static void check_all_levels(struct kunit *test,
+ void (*fn)(struct kunit *test,
+ struct pt_state *pts, void *arg),
+ void *fn_arg)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range range = pt_top_range(priv->common);
+ struct check_levels_arg chk = {
+ .test = test,
+ .fn = fn,
+ .fn_arg = fn_arg,
+ };
+ int ret;
+
+ if (pt_feature(priv->common, PT_FEAT_DYNAMIC_TOP) &&
+ priv->common->max_vasz_lg2 > range.max_vasz_lg2)
+ range.last_va = fvalog2_set_mod_max(range.va,
+ priv->common->max_vasz_lg2);
+
+ /*
+ * Map a page at the highest VA, this will populate all the levels so we
+ * can then iterate over them. Index 0 will be used for testing.
+ */
+ if (IS_32BIT && range.max_vasz_lg2 > 32)
+ range.last_va = (u32)range.last_va;
+ range.va = range.last_va - (priv->smallest_pgsz - 1);
+ do_map(test, range.va, 0, priv->smallest_pgsz);
+
+ range = pt_make_range(priv->common, range.va, range.last_va);
+ ret = pt_walk_range(&range, __check_all_levels, &chk);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+}
+
+static void test_init(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+
+ /* Fixture does the setup */
+ KUNIT_ASSERT_NE(test, priv->info.pgsize_bitmap, 0);
+}
+
+/*
+ * Basic check that the log2_* functions are working, especially at the integer
+ * limits.
+ */
+static void test_bitops(struct kunit *test)
+{
+ int i;
+
+ KUNIT_ASSERT_EQ(test, fls_t(u32, 0), 0);
+ KUNIT_ASSERT_EQ(test, fls_t(u32, 1), 1);
+ KUNIT_ASSERT_EQ(test, fls_t(u32, BIT(2)), 3);
+ KUNIT_ASSERT_EQ(test, fls_t(u32, U32_MAX), 32);
+
+ KUNIT_ASSERT_EQ(test, fls_t(u64, 0), 0);
+ KUNIT_ASSERT_EQ(test, fls_t(u64, 1), 1);
+ KUNIT_ASSERT_EQ(test, fls_t(u64, BIT(2)), 3);
+ KUNIT_ASSERT_EQ(test, fls_t(u64, U64_MAX), 64);
+
+ KUNIT_ASSERT_EQ(test, ffs_t(u32, 1), 0);
+ KUNIT_ASSERT_EQ(test, ffs_t(u32, BIT(2)), 2);
+ KUNIT_ASSERT_EQ(test, ffs_t(u32, BIT(31)), 31);
+
+ KUNIT_ASSERT_EQ(test, ffs_t(u64, 1), 0);
+ KUNIT_ASSERT_EQ(test, ffs_t(u64, BIT(2)), 2);
+ KUNIT_ASSERT_EQ(test, ffs_t(u64, BIT_ULL(63)), 63);
+
+ for (i = 0; i != 31; i++)
+ KUNIT_ASSERT_EQ(test, ffz_t(u64, BIT_ULL(i) - 1), i);
+
+ for (i = 0; i != 63; i++)
+ KUNIT_ASSERT_EQ(test, ffz_t(u64, BIT_ULL(i) - 1), i);
+
+ for (i = 0; i != 32; i++) {
+ u64 val = get_random_u64();
+
+ KUNIT_ASSERT_EQ(test, log2_mod_t(u32, val, ffs_t(u32, val)), 0);
+ KUNIT_ASSERT_EQ(test, log2_mod_t(u64, val, ffs_t(u64, val)), 0);
+
+ KUNIT_ASSERT_EQ(test, log2_mod_t(u32, val, ffz_t(u32, val)),
+ log2_to_max_int_t(u32, ffz_t(u32, val)));
+ KUNIT_ASSERT_EQ(test, log2_mod_t(u64, val, ffz_t(u64, val)),
+ log2_to_max_int_t(u64, ffz_t(u64, val)));
+ }
+}
+
+static unsigned int ref_best_pgsize(pt_vaddr_t pgsz_bitmap, pt_vaddr_t va,
+ pt_vaddr_t last_va, pt_oaddr_t oa)
+{
+ pt_vaddr_t pgsz_lg2;
+
+ /* Brute force the constraints described in pt_compute_best_pgsize() */
+ for (pgsz_lg2 = PT_VADDR_MAX_LG2 - 1; pgsz_lg2 != 0; pgsz_lg2--) {
+ if ((pgsz_bitmap & log2_to_int(pgsz_lg2)) &&
+ log2_mod(va, pgsz_lg2) == 0 &&
+ oalog2_mod(oa, pgsz_lg2) == 0 &&
+ va + log2_to_int(pgsz_lg2) - 1 <= last_va &&
+ log2_div_eq(va, va + log2_to_int(pgsz_lg2) - 1, pgsz_lg2) &&
+ oalog2_div_eq(oa, oa + log2_to_int(pgsz_lg2) - 1, pgsz_lg2))
+ return pgsz_lg2;
+ }
+ return 0;
+}
+
+/* Check that the bit logic in pt_compute_best_pgsize() works. */
+static void test_best_pgsize(struct kunit *test)
+{
+ unsigned int a_lg2;
+ unsigned int b_lg2;
+ unsigned int c_lg2;
+
+ /* Try random prefixes with every suffix combination */
+ for (a_lg2 = 1; a_lg2 != 10; a_lg2++) {
+ for (b_lg2 = 1; b_lg2 != 10; b_lg2++) {
+ for (c_lg2 = 1; c_lg2 != 10; c_lg2++) {
+ pt_vaddr_t pgsz_bitmap = get_random_u64();
+ pt_vaddr_t va = get_random_u64() << a_lg2;
+ pt_oaddr_t oa = get_random_u64() << b_lg2;
+ pt_vaddr_t last_va = log2_set_mod_max(
+ get_random_u64(), c_lg2);
+
+ if (va > last_va)
+ swap(va, last_va);
+ KUNIT_ASSERT_EQ(
+ test,
+ pt_compute_best_pgsize(pgsz_bitmap, va,
+ last_va, oa),
+ ref_best_pgsize(pgsz_bitmap, va,
+ last_va, oa));
+ }
+ }
+ }
+
+ /* 0 prefix, every suffix */
+ for (c_lg2 = 1; c_lg2 != PT_VADDR_MAX_LG2 - 1; c_lg2++) {
+ pt_vaddr_t pgsz_bitmap = get_random_u64();
+ pt_vaddr_t va = 0;
+ pt_oaddr_t oa = 0;
+ pt_vaddr_t last_va = log2_set_mod_max(0, c_lg2);
+
+ KUNIT_ASSERT_EQ(test,
+ pt_compute_best_pgsize(pgsz_bitmap, va, last_va,
+ oa),
+ ref_best_pgsize(pgsz_bitmap, va, last_va, oa));
+ }
+
+ /* 1's prefix, every suffix */
+ for (a_lg2 = 1; a_lg2 != 10; a_lg2++) {
+ for (b_lg2 = 1; b_lg2 != 10; b_lg2++) {
+ for (c_lg2 = 1; c_lg2 != 10; c_lg2++) {
+ pt_vaddr_t pgsz_bitmap = get_random_u64();
+ pt_vaddr_t va = PT_VADDR_MAX << a_lg2;
+ pt_oaddr_t oa = PT_VADDR_MAX << b_lg2;
+ pt_vaddr_t last_va = PT_VADDR_MAX;
+
+ KUNIT_ASSERT_EQ(
+ test,
+ pt_compute_best_pgsize(pgsz_bitmap, va,
+ last_va, oa),
+ ref_best_pgsize(pgsz_bitmap, va,
+ last_va, oa));
+ }
+ }
+ }
+
+ /* pgsize_bitmap is always 0 */
+ for (a_lg2 = 1; a_lg2 != 10; a_lg2++) {
+ for (b_lg2 = 1; b_lg2 != 10; b_lg2++) {
+ for (c_lg2 = 1; c_lg2 != 10; c_lg2++) {
+ pt_vaddr_t pgsz_bitmap = 0;
+ pt_vaddr_t va = get_random_u64() << a_lg2;
+ pt_oaddr_t oa = get_random_u64() << b_lg2;
+ pt_vaddr_t last_va = log2_set_mod_max(
+ get_random_u64(), c_lg2);
+
+ if (va > last_va)
+ swap(va, last_va);
+ KUNIT_ASSERT_EQ(
+ test,
+ pt_compute_best_pgsize(pgsz_bitmap, va,
+ last_va, oa),
+ 0);
+ }
+ }
+ }
+
+ if (sizeof(pt_vaddr_t) <= 4)
+ return;
+
+ /* over 32 bit page sizes */
+ for (a_lg2 = 32; a_lg2 != 42; a_lg2++) {
+ for (b_lg2 = 32; b_lg2 != 42; b_lg2++) {
+ for (c_lg2 = 32; c_lg2 != 42; c_lg2++) {
+ pt_vaddr_t pgsz_bitmap = get_random_u64();
+ pt_vaddr_t va = get_random_u64() << a_lg2;
+ pt_oaddr_t oa = get_random_u64() << b_lg2;
+ pt_vaddr_t last_va = log2_set_mod_max(
+ get_random_u64(), c_lg2);
+
+ if (va > last_va)
+ swap(va, last_va);
+ KUNIT_ASSERT_EQ(
+ test,
+ pt_compute_best_pgsize(pgsz_bitmap, va,
+ last_va, oa),
+ ref_best_pgsize(pgsz_bitmap, va,
+ last_va, oa));
+ }
+ }
+ }
+}
+
+/*
+ * Check that pt_install_table() and pt_table_pa() match
+ */
+static void test_lvl_table_ptr(struct kunit *test, struct pt_state *pts,
+ void *arg)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ pt_oaddr_t paddr =
+ log2_set_mod(priv->test_oa, 0, priv->smallest_pgsz_lg2);
+ struct pt_write_attrs attrs = {};
+
+ if (!pt_can_have_table(pts))
+ return;
+
+ KUNIT_ASSERT_NO_ERRNO_FN(test, "pt_iommu_set_prot",
+ pt_iommu_set_prot(pts->range->common, &attrs,
+ IOMMU_READ));
+
+ pt_load_single_entry(pts);
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_EMPTY);
+
+ KUNIT_ASSERT_TRUE(test, pt_install_table(pts, paddr, &attrs));
+
+ /* A second install should pass because install updates pts->entry. */
+ KUNIT_ASSERT_EQ(test, pt_install_table(pts, paddr, &attrs), true);
+
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_TABLE);
+ KUNIT_ASSERT_EQ(test, pt_table_pa(pts), paddr);
+
+ pt_clear_entries(pts, ilog2(1));
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_EMPTY);
+}
+
+static void test_table_ptr(struct kunit *test)
+{
+ check_all_levels(test, test_lvl_table_ptr, NULL);
+}
+
+struct lvl_radix_arg {
+ pt_vaddr_t vbits;
+};
+
+/*
+ * Check pt_table_oa_lg2sz() and pt_table_item_lg2sz() they need to decode a
+ * continuous list of VA across all the levels that covers the entire advertised
+ * VA space.
+ */
+static void test_lvl_radix(struct kunit *test, struct pt_state *pts, void *arg)
+{
+ unsigned int table_lg2sz = pt_table_oa_lg2sz(pts);
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+ struct lvl_radix_arg *radix = arg;
+
+ /* Every bit below us is decoded */
+ KUNIT_ASSERT_EQ(test, log2_set_mod_max(0, isz_lg2), radix->vbits);
+
+ /* We are not decoding bits someone else is */
+ KUNIT_ASSERT_EQ(test, log2_div(radix->vbits, isz_lg2), 0);
+
+ /* Can't decode past the pt_vaddr_t size */
+ KUNIT_ASSERT_LE(test, table_lg2sz, PT_VADDR_MAX_LG2);
+ KUNIT_ASSERT_EQ(test, fvalog2_div(table_lg2sz, PT_MAX_VA_ADDRESS_LG2),
+ 0);
+
+ radix->vbits = fvalog2_set_mod_max(0, table_lg2sz);
+}
+
+static void test_max_va(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range range = pt_top_range(priv->common);
+
+ KUNIT_ASSERT_GE(test, priv->common->max_vasz_lg2, range.max_vasz_lg2);
+}
+
+static void test_table_radix(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct lvl_radix_arg radix = { .vbits = priv->smallest_pgsz - 1 };
+ struct pt_range range;
+
+ check_all_levels(test, test_lvl_radix, &radix);
+
+ range = pt_top_range(priv->common);
+ if (range.max_vasz_lg2 == PT_VADDR_MAX_LG2) {
+ KUNIT_ASSERT_EQ(test, radix.vbits, PT_VADDR_MAX);
+ } else {
+ if (!IS_32BIT)
+ KUNIT_ASSERT_EQ(test,
+ log2_set_mod_max(0, range.max_vasz_lg2),
+ radix.vbits);
+ KUNIT_ASSERT_EQ(test, log2_div(radix.vbits, range.max_vasz_lg2),
+ 0);
+ }
+}
+
+static unsigned int safe_pt_num_items_lg2(const struct pt_state *pts)
+{
+ struct pt_range top_range = pt_top_range(pts->range->common);
+ struct pt_state top_pts = pt_init_top(&top_range);
+
+ /*
+ * Avoid calling pt_num_items_lg2() on the top, instead we can derive
+ * the size of the top table from the top range.
+ */
+ if (pts->level == top_range.top_level)
+ return ilog2(pt_range_to_end_index(&top_pts));
+ return pt_num_items_lg2(pts);
+}
+
+static void test_lvl_possible_sizes(struct kunit *test, struct pt_state *pts,
+ void *arg)
+{
+ unsigned int num_items_lg2 = safe_pt_num_items_lg2(pts);
+ pt_vaddr_t pgsize_bitmap = pt_possible_sizes(pts);
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+
+ if (!pt_can_have_leaf(pts)) {
+ KUNIT_ASSERT_EQ(test, pgsize_bitmap, 0);
+ return;
+ }
+
+ /* No bits for sizes that would be outside this table */
+ KUNIT_ASSERT_EQ(test, log2_mod(pgsize_bitmap, isz_lg2), 0);
+ KUNIT_ASSERT_EQ(
+ test, fvalog2_div(pgsize_bitmap, num_items_lg2 + isz_lg2), 0);
+
+ /*
+ * Non contiguous must be supported. AMDv1 has a HW bug where it does
+ * not support it on one of the levels.
+ */
+ if ((u64)pgsize_bitmap != 0xff0000000000ULL ||
+ strcmp(__stringify(PTPFX_RAW), "amdv1") != 0)
+ KUNIT_ASSERT_TRUE(test, pgsize_bitmap & log2_to_int(isz_lg2));
+ else
+ KUNIT_ASSERT_NE(test, pgsize_bitmap, 0);
+
+ /* A contiguous entry should not span the whole table */
+ if (num_items_lg2 + isz_lg2 != PT_VADDR_MAX_LG2)
+ KUNIT_ASSERT_FALSE(
+ test,
+ pgsize_bitmap & log2_to_int(num_items_lg2 + isz_lg2));
+}
+
+static void test_entry_possible_sizes(struct kunit *test)
+{
+ check_all_levels(test, test_lvl_possible_sizes, NULL);
+}
+
+static void sweep_all_pgsizes(struct kunit *test, struct pt_state *pts,
+ struct pt_write_attrs *attrs,
+ pt_oaddr_t test_oaddr)
+{
+ pt_vaddr_t pgsize_bitmap = pt_possible_sizes(pts);
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+ unsigned int len_lg2;
+
+ if (pts->index != 0)
+ return;
+
+ for (len_lg2 = 0; len_lg2 < PT_VADDR_MAX_LG2 - 1; len_lg2++) {
+ struct pt_state sub_pts = *pts;
+ pt_oaddr_t oaddr;
+
+ if (!(pgsize_bitmap & log2_to_int(len_lg2)))
+ continue;
+
+ oaddr = log2_set_mod(test_oaddr, 0, len_lg2);
+ pt_install_leaf_entry(pts, oaddr, len_lg2, attrs);
+ /* Verify that every contiguous item translates correctly */
+ for (sub_pts.index = 0;
+ sub_pts.index != log2_to_int(len_lg2 - isz_lg2);
+ sub_pts.index++) {
+ KUNIT_ASSERT_PT_LOAD(test, &sub_pts, PT_ENTRY_OA);
+ KUNIT_ASSERT_EQ(test, pt_item_oa(&sub_pts),
+ oaddr + sub_pts.index *
+ oalog2_mul(1, isz_lg2));
+ KUNIT_ASSERT_EQ(test, pt_entry_oa(&sub_pts), oaddr);
+ KUNIT_ASSERT_EQ(test, pt_entry_num_contig_lg2(&sub_pts),
+ len_lg2 - isz_lg2);
+ }
+
+ pt_clear_entries(pts, len_lg2 - isz_lg2);
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_EMPTY);
+ }
+}
+
+/*
+ * Check that pt_install_leaf_entry() and pt_entry_oa() match.
+ * Check that pt_clear_entries() works.
+ */
+static void test_lvl_entry_oa(struct kunit *test, struct pt_state *pts,
+ void *arg)
+{
+ unsigned int max_oa_lg2 = pts->range->common->max_oasz_lg2;
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_write_attrs attrs = {};
+
+ if (!pt_can_have_leaf(pts))
+ return;
+
+ KUNIT_ASSERT_NO_ERRNO_FN(test, "pt_iommu_set_prot",
+ pt_iommu_set_prot(pts->range->common, &attrs,
+ IOMMU_READ));
+
+ sweep_all_pgsizes(test, pts, &attrs, priv->test_oa);
+
+ /* Check that the table can store the boundary OAs */
+ sweep_all_pgsizes(test, pts, &attrs, 0);
+ if (max_oa_lg2 == PT_OADDR_MAX_LG2)
+ sweep_all_pgsizes(test, pts, &attrs, PT_OADDR_MAX);
+ else
+ sweep_all_pgsizes(test, pts, &attrs,
+ oalog2_to_max_int(max_oa_lg2));
+}
+
+static void test_entry_oa(struct kunit *test)
+{
+ check_all_levels(test, test_lvl_entry_oa, NULL);
+}
+
+/* Test pt_attr_from_entry() */
+static void test_lvl_attr_from_entry(struct kunit *test, struct pt_state *pts,
+ void *arg)
+{
+ pt_vaddr_t pgsize_bitmap = pt_possible_sizes(pts);
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+ struct kunit_iommu_priv *priv = test->priv;
+ unsigned int len_lg2;
+ unsigned int prot;
+
+ if (!pt_can_have_leaf(pts))
+ return;
+
+ for (len_lg2 = 0; len_lg2 < PT_VADDR_MAX_LG2; len_lg2++) {
+ if (!(pgsize_bitmap & log2_to_int(len_lg2)))
+ continue;
+ for (prot = 0; prot <= (IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE |
+ IOMMU_NOEXEC | IOMMU_MMIO);
+ prot++) {
+ pt_oaddr_t oaddr;
+ struct pt_write_attrs attrs = {};
+ u64 good_entry;
+
+ /*
+ * If the format doesn't support this combination of
+ * prot bits skip it
+ */
+ if (pt_iommu_set_prot(pts->range->common, &attrs,
+ prot)) {
+ /* But RW has to be supported */
+ KUNIT_ASSERT_NE(test, prot,
+ IOMMU_READ | IOMMU_WRITE);
+ continue;
+ }
+
+ oaddr = log2_set_mod(priv->test_oa, 0, len_lg2);
+ pt_install_leaf_entry(pts, oaddr, len_lg2, &attrs);
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_OA);
+
+ good_entry = pts->entry;
+
+ memset(&attrs, 0, sizeof(attrs));
+ pt_attr_from_entry(pts, &attrs);
+
+ pt_clear_entries(pts, len_lg2 - isz_lg2);
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_EMPTY);
+
+ pt_install_leaf_entry(pts, oaddr, len_lg2, &attrs);
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_OA);
+
+ /*
+ * The descriptor produced by pt_attr_from_entry()
+ * produce an identical entry value when re-written
+ */
+ KUNIT_ASSERT_EQ(test, good_entry, pts->entry);
+
+ pt_clear_entries(pts, len_lg2 - isz_lg2);
+ }
+ }
+}
+
+static void test_attr_from_entry(struct kunit *test)
+{
+ check_all_levels(test, test_lvl_attr_from_entry, NULL);
+}
+
+static void test_lvl_dirty(struct kunit *test, struct pt_state *pts, void *arg)
+{
+ pt_vaddr_t pgsize_bitmap = pt_possible_sizes(pts);
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+ struct kunit_iommu_priv *priv = test->priv;
+ unsigned int start_idx = pts->index;
+ struct pt_write_attrs attrs = {};
+ unsigned int len_lg2;
+
+ if (!pt_can_have_leaf(pts))
+ return;
+
+ KUNIT_ASSERT_NO_ERRNO_FN(test, "pt_iommu_set_prot",
+ pt_iommu_set_prot(pts->range->common, &attrs,
+ IOMMU_READ | IOMMU_WRITE));
+
+ for (len_lg2 = 0; len_lg2 < PT_VADDR_MAX_LG2; len_lg2++) {
+ pt_oaddr_t oaddr;
+ unsigned int i;
+
+ if (!(pgsize_bitmap & log2_to_int(len_lg2)))
+ continue;
+
+ oaddr = log2_set_mod(priv->test_oa, 0, len_lg2);
+ pt_install_leaf_entry(pts, oaddr, len_lg2, &attrs);
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_OA);
+
+ pt_load_entry(pts);
+ pt_entry_make_write_clean(pts);
+ pt_load_entry(pts);
+ KUNIT_ASSERT_FALSE(test, pt_entry_is_write_dirty(pts));
+
+ for (i = 0; i != log2_to_int(len_lg2 - isz_lg2); i++) {
+ /* dirty every contiguous entry */
+ pts->index = start_idx + i;
+ pt_load_entry(pts);
+ KUNIT_ASSERT_TRUE(test, pt_entry_make_write_dirty(pts));
+ pts->index = start_idx;
+ pt_load_entry(pts);
+ KUNIT_ASSERT_TRUE(test, pt_entry_is_write_dirty(pts));
+
+ pt_entry_make_write_clean(pts);
+ pt_load_entry(pts);
+ KUNIT_ASSERT_FALSE(test, pt_entry_is_write_dirty(pts));
+ }
+
+ pt_clear_entries(pts, len_lg2 - isz_lg2);
+ }
+}
+
+static __maybe_unused void test_dirty(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+
+ if (!pt_dirty_supported(priv->common))
+ kunit_skip(test,
+ "Page table features do not support dirty tracking");
+
+ check_all_levels(test, test_lvl_dirty, NULL);
+}
+
+static void test_lvl_sw_bit_leaf(struct kunit *test, struct pt_state *pts,
+ void *arg)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ pt_vaddr_t pgsize_bitmap = pt_possible_sizes(pts);
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+ struct pt_write_attrs attrs = {};
+ unsigned int len_lg2;
+
+ if (!pt_can_have_leaf(pts))
+ return;
+ if (pts->index != 0)
+ return;
+
+ KUNIT_ASSERT_NO_ERRNO_FN(test, "pt_iommu_set_prot",
+ pt_iommu_set_prot(pts->range->common, &attrs,
+ IOMMU_READ));
+
+ for (len_lg2 = 0; len_lg2 < PT_VADDR_MAX_LG2 - 1; len_lg2++) {
+ pt_oaddr_t paddr = log2_set_mod(priv->test_oa, 0, len_lg2);
+ struct pt_write_attrs new_attrs = {};
+ unsigned int bitnr;
+
+ if (!(pgsize_bitmap & log2_to_int(len_lg2)))
+ continue;
+
+ pt_install_leaf_entry(pts, paddr, len_lg2, &attrs);
+
+ for (bitnr = 0; bitnr <= pt_max_sw_bit(pts->range->common);
+ bitnr++)
+ KUNIT_ASSERT_FALSE(test,
+ pt_test_sw_bit_acquire(pts, bitnr));
+
+ for (bitnr = 0; bitnr <= pt_max_sw_bit(pts->range->common);
+ bitnr++) {
+ KUNIT_ASSERT_FALSE(test,
+ pt_test_sw_bit_acquire(pts, bitnr));
+ pt_set_sw_bit_release(pts, bitnr);
+ KUNIT_ASSERT_TRUE(test,
+ pt_test_sw_bit_acquire(pts, bitnr));
+ }
+
+ for (bitnr = 0; bitnr <= pt_max_sw_bit(pts->range->common);
+ bitnr++)
+ KUNIT_ASSERT_TRUE(test,
+ pt_test_sw_bit_acquire(pts, bitnr));
+
+ KUNIT_ASSERT_EQ(test, pt_item_oa(pts), paddr);
+
+ /* SW bits didn't leak into the attrs */
+ pt_attr_from_entry(pts, &new_attrs);
+ KUNIT_ASSERT_MEMEQ(test, &new_attrs, &attrs, sizeof(attrs));
+
+ pt_clear_entries(pts, len_lg2 - isz_lg2);
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_EMPTY);
+ }
+}
+
+static __maybe_unused void test_sw_bit_leaf(struct kunit *test)
+{
+ check_all_levels(test, test_lvl_sw_bit_leaf, NULL);
+}
+
+static void test_lvl_sw_bit_table(struct kunit *test, struct pt_state *pts,
+ void *arg)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_write_attrs attrs = {};
+ pt_oaddr_t paddr =
+ log2_set_mod(priv->test_oa, 0, priv->smallest_pgsz_lg2);
+ unsigned int bitnr;
+
+ if (!pt_can_have_leaf(pts))
+ return;
+ if (pts->index != 0)
+ return;
+
+ KUNIT_ASSERT_NO_ERRNO_FN(test, "pt_iommu_set_prot",
+ pt_iommu_set_prot(pts->range->common, &attrs,
+ IOMMU_READ));
+
+ KUNIT_ASSERT_TRUE(test, pt_install_table(pts, paddr, &attrs));
+
+ for (bitnr = 0; bitnr <= pt_max_sw_bit(pts->range->common); bitnr++)
+ KUNIT_ASSERT_FALSE(test, pt_test_sw_bit_acquire(pts, bitnr));
+
+ for (bitnr = 0; bitnr <= pt_max_sw_bit(pts->range->common); bitnr++) {
+ KUNIT_ASSERT_FALSE(test, pt_test_sw_bit_acquire(pts, bitnr));
+ pt_set_sw_bit_release(pts, bitnr);
+ KUNIT_ASSERT_TRUE(test, pt_test_sw_bit_acquire(pts, bitnr));
+ }
+
+ for (bitnr = 0; bitnr <= pt_max_sw_bit(pts->range->common); bitnr++)
+ KUNIT_ASSERT_TRUE(test, pt_test_sw_bit_acquire(pts, bitnr));
+
+ KUNIT_ASSERT_EQ(test, pt_table_pa(pts), paddr);
+
+ pt_clear_entries(pts, ilog2(1));
+ KUNIT_ASSERT_PT_LOAD(test, pts, PT_ENTRY_EMPTY);
+}
+
+static __maybe_unused void test_sw_bit_table(struct kunit *test)
+{
+ check_all_levels(test, test_lvl_sw_bit_table, NULL);
+}
+
+static struct kunit_case generic_pt_test_cases[] = {
+ KUNIT_CASE_FMT(test_init),
+ KUNIT_CASE_FMT(test_bitops),
+ KUNIT_CASE_FMT(test_best_pgsize),
+ KUNIT_CASE_FMT(test_table_ptr),
+ KUNIT_CASE_FMT(test_max_va),
+ KUNIT_CASE_FMT(test_table_radix),
+ KUNIT_CASE_FMT(test_entry_possible_sizes),
+ KUNIT_CASE_FMT(test_entry_oa),
+ KUNIT_CASE_FMT(test_attr_from_entry),
+#ifdef pt_entry_is_write_dirty
+ KUNIT_CASE_FMT(test_dirty),
+#endif
+#ifdef pt_sw_bit
+ KUNIT_CASE_FMT(test_sw_bit_leaf),
+ KUNIT_CASE_FMT(test_sw_bit_table),
+#endif
+ {},
+};
+
+static int pt_kunit_generic_pt_init(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv;
+ int ret;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ ret = pt_kunit_priv_init(test, priv);
+ if (ret) {
+ kunit_kfree(test, priv);
+ return ret;
+ }
+ test->priv = priv;
+ return 0;
+}
+
+static void pt_kunit_generic_pt_exit(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+
+ if (!test->priv)
+ return;
+
+ pt_iommu_deinit(priv->iommu);
+ kunit_kfree(test, test->priv);
+}
+
+static struct kunit_suite NS(generic_pt_suite) = {
+ .name = __stringify(NS(fmt_test)),
+ .init = pt_kunit_generic_pt_init,
+ .exit = pt_kunit_generic_pt_exit,
+ .test_cases = generic_pt_test_cases,
+};
+kunit_test_suites(&NS(generic_pt_suite));
diff --git a/drivers/iommu/generic_pt/kunit_iommu.h b/drivers/iommu/generic_pt/kunit_iommu.h
new file mode 100644
index 000000000000..22c9e4c4dd97
--- /dev/null
+++ b/drivers/iommu/generic_pt/kunit_iommu.h
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ */
+#ifndef __GENERIC_PT_KUNIT_IOMMU_H
+#define __GENERIC_PT_KUNIT_IOMMU_H
+
+#define GENERIC_PT_KUNIT 1
+#include <kunit/device.h>
+#include <kunit/test.h>
+#include "../iommu-pages.h"
+#include "pt_iter.h"
+
+#define pt_iommu_table_cfg CONCATENATE(pt_iommu_table, _cfg)
+#define pt_iommu_init CONCATENATE(CONCATENATE(pt_iommu_, PTPFX), init)
+int pt_iommu_init(struct pt_iommu_table *fmt_table,
+ const struct pt_iommu_table_cfg *cfg, gfp_t gfp);
+
+/* The format can provide a list of configurations it would like to test */
+#ifdef kunit_fmt_cfgs
+static const void *kunit_pt_gen_params_cfg(struct kunit *test, const void *prev,
+ char *desc)
+{
+ uintptr_t cfg_id = (uintptr_t)prev;
+
+ cfg_id++;
+ if (cfg_id >= ARRAY_SIZE(kunit_fmt_cfgs) + 1)
+ return NULL;
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s_cfg_%u",
+ __stringify(PTPFX_RAW), (unsigned int)(cfg_id - 1));
+ return (void *)cfg_id;
+}
+#define KUNIT_CASE_FMT(test_name) \
+ KUNIT_CASE_PARAM(test_name, kunit_pt_gen_params_cfg)
+#else
+#define KUNIT_CASE_FMT(test_name) KUNIT_CASE(test_name)
+#endif
+
+#define KUNIT_ASSERT_NO_ERRNO(test, ret) \
+ KUNIT_ASSERT_EQ_MSG(test, ret, 0, KUNIT_SUBSUBTEST_INDENT "errno %pe", \
+ ERR_PTR(ret))
+
+#define KUNIT_ASSERT_NO_ERRNO_FN(test, fn, ret) \
+ KUNIT_ASSERT_EQ_MSG(test, ret, 0, \
+ KUNIT_SUBSUBTEST_INDENT "errno %pe from %s", \
+ ERR_PTR(ret), fn)
+
+/*
+ * When the test is run on a 32 bit system unsigned long can be 32 bits. This
+ * cause the iommu op signatures to be restricted to 32 bits. Meaning the test
+ * has to be mindful not to create any VA's over the 32 bit limit. Reduce the
+ * scope of the testing as the main purpose of checking on full 32 bit is to
+ * look for 32bitism in the core code. Run the test on i386 with X86_PAE=y to
+ * get the full coverage when dma_addr_t & phys_addr_t are 8 bytes
+ */
+#define IS_32BIT (sizeof(unsigned long) == 4)
+
+struct kunit_iommu_priv {
+ union {
+ struct iommu_domain domain;
+ struct pt_iommu_table fmt_table;
+ };
+ spinlock_t top_lock;
+ struct device *dummy_dev;
+ struct pt_iommu *iommu;
+ struct pt_common *common;
+ struct pt_iommu_table_cfg cfg;
+ struct pt_iommu_info info;
+ unsigned int smallest_pgsz_lg2;
+ pt_vaddr_t smallest_pgsz;
+ unsigned int largest_pgsz_lg2;
+ pt_oaddr_t test_oa;
+ pt_vaddr_t safe_pgsize_bitmap;
+ unsigned long orig_nr_secondary_pagetable;
+
+};
+PT_IOMMU_CHECK_DOMAIN(struct kunit_iommu_priv, fmt_table.iommu, domain);
+
+static void pt_kunit_iotlb_sync(struct iommu_domain *domain,
+ struct iommu_iotlb_gather *gather)
+{
+ iommu_put_pages_list(&gather->freelist);
+}
+
+#define IOMMU_PT_DOMAIN_OPS1(x) IOMMU_PT_DOMAIN_OPS(x)
+static const struct iommu_domain_ops kunit_pt_ops = {
+ IOMMU_PT_DOMAIN_OPS1(PTPFX_RAW),
+ .iotlb_sync = &pt_kunit_iotlb_sync,
+};
+
+static void pt_kunit_change_top(struct pt_iommu *iommu_table,
+ phys_addr_t top_paddr, unsigned int top_level)
+{
+}
+
+static spinlock_t *pt_kunit_get_top_lock(struct pt_iommu *iommu_table)
+{
+ struct kunit_iommu_priv *priv = container_of(
+ iommu_table, struct kunit_iommu_priv, fmt_table.iommu);
+
+ return &priv->top_lock;
+}
+
+static const struct pt_iommu_driver_ops pt_kunit_driver_ops = {
+ .change_top = &pt_kunit_change_top,
+ .get_top_lock = &pt_kunit_get_top_lock,
+};
+
+static int pt_kunit_priv_init(struct kunit *test, struct kunit_iommu_priv *priv)
+{
+ unsigned int va_lg2sz;
+ int ret;
+
+ /* Enough so the memory allocator works */
+ priv->dummy_dev = kunit_device_register(test, "pt_kunit_dev");
+ if (IS_ERR(priv->dummy_dev))
+ return PTR_ERR(priv->dummy_dev);
+ set_dev_node(priv->dummy_dev, NUMA_NO_NODE);
+
+ spin_lock_init(&priv->top_lock);
+
+#ifdef kunit_fmt_cfgs
+ priv->cfg = kunit_fmt_cfgs[((uintptr_t)test->param_value) - 1];
+ /*
+ * The format can set a list of features that the kunit_fmt_cfgs
+ * controls, other features are default to on.
+ */
+ priv->cfg.common.features |= PT_SUPPORTED_FEATURES &
+ (~KUNIT_FMT_FEATURES);
+#else
+ priv->cfg.common.features = PT_SUPPORTED_FEATURES;
+#endif
+
+ /* Defaults, for the kunit */
+ if (!priv->cfg.common.hw_max_vasz_lg2)
+ priv->cfg.common.hw_max_vasz_lg2 = PT_MAX_VA_ADDRESS_LG2;
+ if (!priv->cfg.common.hw_max_oasz_lg2)
+ priv->cfg.common.hw_max_oasz_lg2 = pt_max_oa_lg2(NULL);
+
+ priv->fmt_table.iommu.nid = NUMA_NO_NODE;
+ priv->fmt_table.iommu.driver_ops = &pt_kunit_driver_ops;
+ priv->fmt_table.iommu.iommu_device = priv->dummy_dev;
+ priv->domain.ops = &kunit_pt_ops;
+ ret = pt_iommu_init(&priv->fmt_table, &priv->cfg, GFP_KERNEL);
+ if (ret) {
+ if (ret == -EOVERFLOW)
+ kunit_skip(
+ test,
+ "This configuration cannot be tested on 32 bit");
+ return ret;
+ }
+
+ priv->iommu = &priv->fmt_table.iommu;
+ priv->common = common_from_iommu(&priv->fmt_table.iommu);
+ priv->iommu->ops->get_info(priv->iommu, &priv->info);
+
+ /*
+ * size_t is used to pass the mapping length, it can be 32 bit, truncate
+ * the pagesizes so we don't use large sizes.
+ */
+ priv->info.pgsize_bitmap = (size_t)priv->info.pgsize_bitmap;
+
+ priv->smallest_pgsz_lg2 = vaffs(priv->info.pgsize_bitmap);
+ priv->smallest_pgsz = log2_to_int(priv->smallest_pgsz_lg2);
+ priv->largest_pgsz_lg2 =
+ vafls((dma_addr_t)priv->info.pgsize_bitmap) - 1;
+
+ priv->test_oa =
+ oalog2_mod(0x74a71445deadbeef, priv->common->max_oasz_lg2);
+
+ /*
+ * We run out of VA space if the mappings get too big, make something
+ * smaller that can safely pass through dma_addr_t API.
+ */
+ va_lg2sz = priv->common->max_vasz_lg2;
+ if (IS_32BIT && va_lg2sz > 32)
+ va_lg2sz = 32;
+ priv->safe_pgsize_bitmap =
+ log2_mod(priv->info.pgsize_bitmap, va_lg2sz - 1);
+
+ return 0;
+}
+
+#endif
diff --git a/drivers/iommu/generic_pt/kunit_iommu_pt.h b/drivers/iommu/generic_pt/kunit_iommu_pt.h
new file mode 100644
index 000000000000..e8a63c8ea850
--- /dev/null
+++ b/drivers/iommu/generic_pt/kunit_iommu_pt.h
@@ -0,0 +1,487 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES
+ */
+#include "kunit_iommu.h"
+#include "pt_iter.h"
+#include <linux/generic_pt/iommu.h>
+#include <linux/iommu.h>
+
+static void do_map(struct kunit *test, pt_vaddr_t va, pt_oaddr_t pa,
+ pt_vaddr_t len);
+
+struct count_valids {
+ u64 per_size[PT_VADDR_MAX_LG2];
+};
+
+static int __count_valids(struct pt_range *range, void *arg, unsigned int level,
+ struct pt_table_p *table)
+{
+ struct pt_state pts = pt_init(range, level, table);
+ struct count_valids *valids = arg;
+
+ for_each_pt_level_entry(&pts) {
+ if (pts.type == PT_ENTRY_TABLE) {
+ pt_descend(&pts, arg, __count_valids);
+ continue;
+ }
+ if (pts.type == PT_ENTRY_OA) {
+ valids->per_size[pt_entry_oa_lg2sz(&pts)]++;
+ continue;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Number of valid table entries. This counts contiguous entries as a single
+ * valid.
+ */
+static unsigned int count_valids(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range range = pt_top_range(priv->common);
+ struct count_valids valids = {};
+ u64 total = 0;
+ unsigned int i;
+
+ KUNIT_ASSERT_NO_ERRNO(test,
+ pt_walk_range(&range, __count_valids, &valids));
+
+ for (i = 0; i != ARRAY_SIZE(valids.per_size); i++)
+ total += valids.per_size[i];
+ return total;
+}
+
+/* Only a single page size is present, count the number of valid entries */
+static unsigned int count_valids_single(struct kunit *test, pt_vaddr_t pgsz)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range range = pt_top_range(priv->common);
+ struct count_valids valids = {};
+ u64 total = 0;
+ unsigned int i;
+
+ KUNIT_ASSERT_NO_ERRNO(test,
+ pt_walk_range(&range, __count_valids, &valids));
+
+ for (i = 0; i != ARRAY_SIZE(valids.per_size); i++) {
+ if ((1ULL << i) == pgsz)
+ total = valids.per_size[i];
+ else
+ KUNIT_ASSERT_EQ(test, valids.per_size[i], 0);
+ }
+ return total;
+}
+
+static void do_unmap(struct kunit *test, pt_vaddr_t va, pt_vaddr_t len)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ size_t ret;
+
+ ret = iommu_unmap(&priv->domain, va, len);
+ KUNIT_ASSERT_EQ(test, ret, len);
+}
+
+static void check_iova(struct kunit *test, pt_vaddr_t va, pt_oaddr_t pa,
+ pt_vaddr_t len)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ pt_vaddr_t pfn = log2_div(va, priv->smallest_pgsz_lg2);
+ pt_vaddr_t end_pfn = pfn + log2_div(len, priv->smallest_pgsz_lg2);
+
+ for (; pfn != end_pfn; pfn++) {
+ phys_addr_t res = iommu_iova_to_phys(&priv->domain,
+ pfn * priv->smallest_pgsz);
+
+ KUNIT_ASSERT_EQ(test, res, (phys_addr_t)pa);
+ if (res != pa)
+ break;
+ pa += priv->smallest_pgsz;
+ }
+}
+
+static void test_increase_level(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_common *common = priv->common;
+
+ if (!pt_feature(common, PT_FEAT_DYNAMIC_TOP))
+ kunit_skip(test, "PT_FEAT_DYNAMIC_TOP not set for this format");
+
+ if (IS_32BIT)
+ kunit_skip(test, "Unable to test on 32bit");
+
+ KUNIT_ASSERT_GT(test, common->max_vasz_lg2,
+ pt_top_range(common).max_vasz_lg2);
+
+ /* Add every possible level to the max */
+ while (common->max_vasz_lg2 != pt_top_range(common).max_vasz_lg2) {
+ struct pt_range top_range = pt_top_range(common);
+
+ if (top_range.va == 0)
+ do_map(test, top_range.last_va + 1, 0,
+ priv->smallest_pgsz);
+ else
+ do_map(test, top_range.va - priv->smallest_pgsz, 0,
+ priv->smallest_pgsz);
+
+ KUNIT_ASSERT_EQ(test, pt_top_range(common).top_level,
+ top_range.top_level + 1);
+ KUNIT_ASSERT_GE(test, common->max_vasz_lg2,
+ pt_top_range(common).max_vasz_lg2);
+ }
+}
+
+static void test_map_simple(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range range = pt_top_range(priv->common);
+ struct count_valids valids = {};
+ pt_vaddr_t pgsize_bitmap = priv->safe_pgsize_bitmap;
+ unsigned int pgsz_lg2;
+ pt_vaddr_t cur_va;
+
+ /* Map every reported page size */
+ cur_va = range.va + priv->smallest_pgsz * 256;
+ for (pgsz_lg2 = 0; pgsz_lg2 != PT_VADDR_MAX_LG2; pgsz_lg2++) {
+ pt_oaddr_t paddr = log2_set_mod(priv->test_oa, 0, pgsz_lg2);
+ u64 len = log2_to_int(pgsz_lg2);
+
+ if (!(pgsize_bitmap & len))
+ continue;
+
+ cur_va = ALIGN(cur_va, len);
+ do_map(test, cur_va, paddr, len);
+ if (len <= SZ_2G)
+ check_iova(test, cur_va, paddr, len);
+ cur_va += len;
+ }
+
+ /* The read interface reports that every page size was created */
+ range = pt_top_range(priv->common);
+ KUNIT_ASSERT_NO_ERRNO(test,
+ pt_walk_range(&range, __count_valids, &valids));
+ for (pgsz_lg2 = 0; pgsz_lg2 != PT_VADDR_MAX_LG2; pgsz_lg2++) {
+ if (pgsize_bitmap & (1ULL << pgsz_lg2))
+ KUNIT_ASSERT_EQ(test, valids.per_size[pgsz_lg2], 1);
+ else
+ KUNIT_ASSERT_EQ(test, valids.per_size[pgsz_lg2], 0);
+ }
+
+ /* Unmap works */
+ range = pt_top_range(priv->common);
+ cur_va = range.va + priv->smallest_pgsz * 256;
+ for (pgsz_lg2 = 0; pgsz_lg2 != PT_VADDR_MAX_LG2; pgsz_lg2++) {
+ u64 len = log2_to_int(pgsz_lg2);
+
+ if (!(pgsize_bitmap & len))
+ continue;
+ cur_va = ALIGN(cur_va, len);
+ do_unmap(test, cur_va, len);
+ cur_va += len;
+ }
+ KUNIT_ASSERT_EQ(test, count_valids(test), 0);
+}
+
+/*
+ * Test to convert a table pointer into an OA by mapping something small,
+ * unmapping it so as to leave behind a table pointer, then mapping something
+ * larger that will convert the table into an OA.
+ */
+static void test_map_table_to_oa(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ pt_vaddr_t limited_pgbitmap =
+ priv->info.pgsize_bitmap % (IS_32BIT ? SZ_2G : SZ_16G);
+ struct pt_range range = pt_top_range(priv->common);
+ unsigned int pgsz_lg2;
+ pt_vaddr_t max_pgsize;
+ pt_vaddr_t cur_va;
+
+ max_pgsize = 1ULL << (vafls(limited_pgbitmap) - 1);
+ KUNIT_ASSERT_TRUE(test, priv->info.pgsize_bitmap & max_pgsize);
+
+ for (pgsz_lg2 = 0; pgsz_lg2 != PT_VADDR_MAX_LG2; pgsz_lg2++) {
+ pt_oaddr_t paddr = log2_set_mod(priv->test_oa, 0, pgsz_lg2);
+ u64 len = log2_to_int(pgsz_lg2);
+ pt_vaddr_t offset;
+
+ if (!(priv->info.pgsize_bitmap & len))
+ continue;
+ if (len > max_pgsize)
+ break;
+
+ cur_va = ALIGN(range.va + priv->smallest_pgsz * 256,
+ max_pgsize);
+ for (offset = 0; offset != max_pgsize; offset += len)
+ do_map(test, cur_va + offset, paddr + offset, len);
+ check_iova(test, cur_va, paddr, max_pgsize);
+ KUNIT_ASSERT_EQ(test, count_valids_single(test, len),
+ log2_div(max_pgsize, pgsz_lg2));
+
+ if (len == max_pgsize) {
+ do_unmap(test, cur_va, max_pgsize);
+ } else {
+ do_unmap(test, cur_va, max_pgsize / 2);
+ for (offset = max_pgsize / 2; offset != max_pgsize;
+ offset += len)
+ do_unmap(test, cur_va + offset, len);
+ }
+
+ KUNIT_ASSERT_EQ(test, count_valids(test), 0);
+ }
+}
+
+/*
+ * Test unmapping a small page at the start of a large page. This always unmaps
+ * the large page.
+ */
+static void test_unmap_split(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range top_range = pt_top_range(priv->common);
+ pt_vaddr_t pgsize_bitmap = priv->safe_pgsize_bitmap;
+ unsigned int pgsz_lg2;
+ unsigned int count = 0;
+
+ for (pgsz_lg2 = 0; pgsz_lg2 != PT_VADDR_MAX_LG2; pgsz_lg2++) {
+ pt_vaddr_t base_len = log2_to_int(pgsz_lg2);
+ unsigned int next_pgsz_lg2;
+
+ if (!(pgsize_bitmap & base_len))
+ continue;
+
+ for (next_pgsz_lg2 = pgsz_lg2 + 1;
+ next_pgsz_lg2 != PT_VADDR_MAX_LG2; next_pgsz_lg2++) {
+ pt_vaddr_t next_len = log2_to_int(next_pgsz_lg2);
+ pt_vaddr_t vaddr = top_range.va;
+ pt_oaddr_t paddr = 0;
+ size_t gnmapped;
+
+ if (!(pgsize_bitmap & next_len))
+ continue;
+
+ do_map(test, vaddr, paddr, next_len);
+ gnmapped = iommu_unmap(&priv->domain, vaddr, base_len);
+ KUNIT_ASSERT_EQ(test, gnmapped, next_len);
+
+ /* Make sure unmap doesn't keep going */
+ do_map(test, vaddr, paddr, next_len);
+ do_map(test, vaddr + next_len, paddr, next_len);
+ gnmapped = iommu_unmap(&priv->domain, vaddr, base_len);
+ KUNIT_ASSERT_EQ(test, gnmapped, next_len);
+ gnmapped = iommu_unmap(&priv->domain, vaddr + next_len,
+ next_len);
+ KUNIT_ASSERT_EQ(test, gnmapped, next_len);
+
+ count++;
+ }
+ }
+
+ if (count == 0)
+ kunit_skip(test, "Test needs two page sizes");
+}
+
+static void unmap_collisions(struct kunit *test, struct maple_tree *mt,
+ pt_vaddr_t start, pt_vaddr_t last)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ MA_STATE(mas, mt, start, last);
+ void *entry;
+
+ mtree_lock(mt);
+ mas_for_each(&mas, entry, last) {
+ pt_vaddr_t mas_start = mas.index;
+ pt_vaddr_t len = (mas.last - mas_start) + 1;
+ pt_oaddr_t paddr;
+
+ mas_erase(&mas);
+ mas_pause(&mas);
+ mtree_unlock(mt);
+
+ paddr = oalog2_mod(mas_start, priv->common->max_oasz_lg2);
+ check_iova(test, mas_start, paddr, len);
+ do_unmap(test, mas_start, len);
+ mtree_lock(mt);
+ }
+ mtree_unlock(mt);
+}
+
+static void clamp_range(struct kunit *test, struct pt_range *range)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+
+ if (range->last_va - range->va > SZ_1G)
+ range->last_va = range->va + SZ_1G;
+ KUNIT_ASSERT_NE(test, range->last_va, PT_VADDR_MAX);
+ if (range->va <= MAPLE_RESERVED_RANGE)
+ range->va =
+ ALIGN(MAPLE_RESERVED_RANGE, priv->smallest_pgsz);
+}
+
+/*
+ * Randomly map and unmap ranges that can large physical pages. If a random
+ * range overlaps with existing ranges then unmap them. This hits all the
+ * special cases.
+ */
+static void test_random_map(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range upper_range = pt_upper_range(priv->common);
+ struct pt_range top_range = pt_top_range(priv->common);
+ struct maple_tree mt;
+ unsigned int iter;
+
+ mt_init(&mt);
+
+ /*
+ * Shrink the range so randomization is more likely to have
+ * intersections
+ */
+ clamp_range(test, &top_range);
+ clamp_range(test, &upper_range);
+
+ for (iter = 0; iter != 1000; iter++) {
+ struct pt_range *range = &top_range;
+ pt_oaddr_t paddr;
+ pt_vaddr_t start;
+ pt_vaddr_t end;
+ int ret;
+
+ if (pt_feature(priv->common, PT_FEAT_SIGN_EXTEND) &&
+ ULONG_MAX >= PT_VADDR_MAX && get_random_u32_inclusive(0, 1))
+ range = &upper_range;
+
+ start = get_random_u32_below(
+ min(U32_MAX, range->last_va - range->va));
+ end = get_random_u32_below(
+ min(U32_MAX, range->last_va - start));
+
+ start = ALIGN_DOWN(start, priv->smallest_pgsz);
+ end = ALIGN(end, priv->smallest_pgsz);
+ start += range->va;
+ end += start;
+ if (start < range->va || end > range->last_va + 1 ||
+ start >= end)
+ continue;
+
+ /* Try overmapping to test the failure handling */
+ paddr = oalog2_mod(start, priv->common->max_oasz_lg2);
+ ret = iommu_map(&priv->domain, start, paddr, end - start,
+ IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
+ if (ret) {
+ KUNIT_ASSERT_EQ(test, ret, -EADDRINUSE);
+ unmap_collisions(test, &mt, start, end - 1);
+ do_map(test, start, paddr, end - start);
+ }
+
+ KUNIT_ASSERT_NO_ERRNO_FN(test, "mtree_insert_range",
+ mtree_insert_range(&mt, start, end - 1,
+ XA_ZERO_ENTRY,
+ GFP_KERNEL));
+
+ check_iova(test, start, paddr, end - start);
+ if (iter % 100)
+ cond_resched();
+ }
+
+ unmap_collisions(test, &mt, 0, PT_VADDR_MAX);
+ KUNIT_ASSERT_EQ(test, count_valids(test), 0);
+
+ mtree_destroy(&mt);
+}
+
+/* See https://lore.kernel.org/r/b9b18a03-63a2-4065-a27e-d92dd5c860bc@amd.com */
+static void test_pgsize_boundary(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range top_range = pt_top_range(priv->common);
+
+ if (top_range.va != 0 || top_range.last_va < 0xfef9ffff ||
+ priv->smallest_pgsz != SZ_4K)
+ kunit_skip(test, "Format does not have the required range");
+
+ do_map(test, 0xfef80000, 0x208b95d000, 0xfef9ffff - 0xfef80000 + 1);
+}
+
+/* See https://lore.kernel.org/r/20250826143816.38686-1-eugkoira@amazon.com */
+static void test_mixed(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+ struct pt_range top_range = pt_top_range(priv->common);
+ u64 start = 0x3fe400ULL << 12;
+ u64 end = 0x4c0600ULL << 12;
+ pt_vaddr_t len = end - start;
+ pt_oaddr_t oa = start;
+
+ if (top_range.last_va <= start || sizeof(unsigned long) == 4)
+ kunit_skip(test, "range is too small");
+ if ((priv->safe_pgsize_bitmap & GENMASK(30, 21)) != (BIT(30) | BIT(21)))
+ kunit_skip(test, "incompatible psize");
+
+ do_map(test, start, oa, len);
+ /* 14 2M, 3 1G, 3 2M */
+ KUNIT_ASSERT_EQ(test, count_valids(test), 20);
+ check_iova(test, start, oa, len);
+}
+
+static struct kunit_case iommu_test_cases[] = {
+ KUNIT_CASE_FMT(test_increase_level),
+ KUNIT_CASE_FMT(test_map_simple),
+ KUNIT_CASE_FMT(test_map_table_to_oa),
+ KUNIT_CASE_FMT(test_unmap_split),
+ KUNIT_CASE_FMT(test_random_map),
+ KUNIT_CASE_FMT(test_pgsize_boundary),
+ KUNIT_CASE_FMT(test_mixed),
+ {},
+};
+
+static int pt_kunit_iommu_init(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv;
+ int ret;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->orig_nr_secondary_pagetable =
+ global_node_page_state(NR_SECONDARY_PAGETABLE);
+ ret = pt_kunit_priv_init(test, priv);
+ if (ret) {
+ kunit_kfree(test, priv);
+ return ret;
+ }
+ test->priv = priv;
+ return 0;
+}
+
+static void pt_kunit_iommu_exit(struct kunit *test)
+{
+ struct kunit_iommu_priv *priv = test->priv;
+
+ if (!test->priv)
+ return;
+
+ pt_iommu_deinit(priv->iommu);
+ /*
+ * Look for memory leaks, assumes kunit is running isolated and nothing
+ * else is using secondary page tables.
+ */
+ KUNIT_ASSERT_EQ(test, priv->orig_nr_secondary_pagetable,
+ global_node_page_state(NR_SECONDARY_PAGETABLE));
+ kunit_kfree(test, test->priv);
+}
+
+static struct kunit_suite NS(iommu_suite) = {
+ .name = __stringify(NS(iommu_test)),
+ .init = pt_kunit_iommu_init,
+ .exit = pt_kunit_iommu_exit,
+ .test_cases = iommu_test_cases,
+};
+kunit_test_suites(&NS(iommu_suite));
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Kunit for generic page table");
+MODULE_IMPORT_NS("GENERIC_PT_IOMMU");
diff --git a/drivers/iommu/generic_pt/pt_common.h b/drivers/iommu/generic_pt/pt_common.h
new file mode 100644
index 000000000000..e1123d35c907
--- /dev/null
+++ b/drivers/iommu/generic_pt/pt_common.h
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * This header is included after the format. It contains definitions
+ * that build on the format definitions to create the basic format API.
+ *
+ * The format API is listed here, with kdocs. The functions without bodies are
+ * implemented in the format using the pattern:
+ * static inline FMTpt_XXX(..) {..}
+ * #define pt_XXX FMTpt_XXX
+ *
+ * If the format doesn't implement a function then pt_fmt_defaults.h can provide
+ * a generic version.
+ *
+ * The routines marked "@pts: Entry to query" operate on the entire contiguous
+ * entry and can be called with a pts->index pointing to any sub item that makes
+ * up that entry.
+ *
+ * The header order is:
+ * pt_defs.h
+ * FMT.h
+ * pt_common.h
+ */
+#ifndef __GENERIC_PT_PT_COMMON_H
+#define __GENERIC_PT_PT_COMMON_H
+
+#include "pt_defs.h"
+#include "pt_fmt_defaults.h"
+
+/**
+ * pt_attr_from_entry() - Convert the permission bits back to attrs
+ * @pts: Entry to convert from
+ * @attrs: Resulting attrs
+ *
+ * Fill in the attrs with the permission bits encoded in the current leaf entry.
+ * The attrs should be usable with pt_install_leaf_entry() to reconstruct the
+ * same entry.
+ */
+static inline void pt_attr_from_entry(const struct pt_state *pts,
+ struct pt_write_attrs *attrs);
+
+/**
+ * pt_can_have_leaf() - True if the current level can have an OA entry
+ * @pts: The current level
+ *
+ * True if the current level can support pt_install_leaf_entry(). A leaf
+ * entry produce an OA.
+ */
+static inline bool pt_can_have_leaf(const struct pt_state *pts);
+
+/**
+ * pt_can_have_table() - True if the current level can have a lower table
+ * @pts: The current level
+ *
+ * Every level except 0 is allowed to have a lower table.
+ */
+static inline bool pt_can_have_table(const struct pt_state *pts)
+{
+ /* No further tables at level 0 */
+ return pts->level > 0;
+}
+
+/**
+ * pt_clear_entries() - Make entries empty (non-present)
+ * @pts: Starting table index
+ * @num_contig_lg2: Number of contiguous items to clear
+ *
+ * Clear a run of entries. A cleared entry will load back as PT_ENTRY_EMPTY
+ * and does not have any effect on table walking. The starting index must be
+ * aligned to num_contig_lg2.
+ */
+static inline void pt_clear_entries(struct pt_state *pts,
+ unsigned int num_contig_lg2);
+
+/**
+ * pt_entry_make_write_dirty() - Make an entry dirty
+ * @pts: Table entry to change
+ *
+ * Make pt_entry_is_write_dirty() return true for this entry. This can be called
+ * asynchronously with any other table manipulation under a RCU lock and must
+ * not corrupt the table.
+ */
+static inline bool pt_entry_make_write_dirty(struct pt_state *pts);
+
+/**
+ * pt_entry_make_write_clean() - Make the entry write clean
+ * @pts: Table entry to change
+ *
+ * Modify the entry so that pt_entry_is_write_dirty() == false. The HW will
+ * eventually be notified of this change via a TLB flush, which is the point
+ * that the HW must become synchronized. Any "write dirty" prior to the TLB
+ * flush can be lost, but once the TLB flush completes all writes must make
+ * their entries write dirty.
+ *
+ * The format should alter the entry in a way that is compatible with any
+ * concurrent update from HW. The entire contiguous entry is changed.
+ */
+static inline void pt_entry_make_write_clean(struct pt_state *pts);
+
+/**
+ * pt_entry_is_write_dirty() - True if the entry has been written to
+ * @pts: Entry to query
+ *
+ * "write dirty" means that the HW has written to the OA translated
+ * by this entry. If the entry is contiguous then the consolidated
+ * "write dirty" for all the items must be returned.
+ */
+static inline bool pt_entry_is_write_dirty(const struct pt_state *pts);
+
+/**
+ * pt_dirty_supported() - True if the page table supports dirty tracking
+ * @common: Page table to query
+ */
+static inline bool pt_dirty_supported(struct pt_common *common);
+
+/**
+ * pt_entry_num_contig_lg2() - Number of contiguous items for this leaf entry
+ * @pts: Entry to query
+ *
+ * Return the number of contiguous items this leaf entry spans. If the entry
+ * is single item it returns ilog2(1).
+ */
+static inline unsigned int pt_entry_num_contig_lg2(const struct pt_state *pts);
+
+/**
+ * pt_entry_oa() - Output Address for this leaf entry
+ * @pts: Entry to query
+ *
+ * Return the output address for the start of the entry. If the entry
+ * is contiguous this returns the same value for each sub-item. I.e.::
+ *
+ * log2_mod(pt_entry_oa(), pt_entry_oa_lg2sz()) == 0
+ *
+ * See pt_item_oa(). The format should implement one of these two functions
+ * depending on how it stores the OAs in the table.
+ */
+static inline pt_oaddr_t pt_entry_oa(const struct pt_state *pts);
+
+/**
+ * pt_entry_oa_lg2sz() - Return the size of an OA entry
+ * @pts: Entry to query
+ *
+ * If the entry is not contiguous this returns pt_table_item_lg2sz(), otherwise
+ * it returns the total VA/OA size of the entire contiguous entry.
+ */
+static inline unsigned int pt_entry_oa_lg2sz(const struct pt_state *pts)
+{
+ return pt_entry_num_contig_lg2(pts) + pt_table_item_lg2sz(pts);
+}
+
+/**
+ * pt_entry_oa_exact() - Return the complete OA for an entry
+ * @pts: Entry to query
+ *
+ * During iteration the first entry could have a VA with an offset from the
+ * natural start of the entry. Return the exact OA including the pts's VA
+ * offset.
+ */
+static inline pt_oaddr_t pt_entry_oa_exact(const struct pt_state *pts)
+{
+ return _pt_entry_oa_fast(pts) |
+ log2_mod(pts->range->va, pt_entry_oa_lg2sz(pts));
+}
+
+/**
+ * pt_full_va_prefix() - The top bits of the VA
+ * @common: Page table to query
+ *
+ * This is usually 0, but some formats have their VA space going downward from
+ * PT_VADDR_MAX, and will return that instead. This value must always be
+ * adjusted by struct pt_common max_vasz_lg2.
+ */
+static inline pt_vaddr_t pt_full_va_prefix(const struct pt_common *common);
+
+/**
+ * pt_has_system_page_size() - True if level 0 can install a PAGE_SHIFT entry
+ * @common: Page table to query
+ *
+ * If true the caller can use, at level 0, pt_install_leaf_entry(PAGE_SHIFT).
+ * This is useful to create optimized paths for common cases of PAGE_SIZE
+ * mappings.
+ */
+static inline bool pt_has_system_page_size(const struct pt_common *common);
+
+/**
+ * pt_install_leaf_entry() - Write a leaf entry to the table
+ * @pts: Table index to change
+ * @oa: Output Address for this leaf
+ * @oasz_lg2: Size in VA/OA for this leaf
+ * @attrs: Attributes to modify the entry
+ *
+ * A leaf OA entry will return PT_ENTRY_OA from pt_load_entry(). It translates
+ * the VA indicated by pts to the given OA.
+ *
+ * For a single item non-contiguous entry oasz_lg2 is pt_table_item_lg2sz().
+ * For contiguous it is pt_table_item_lg2sz() + num_contig_lg2.
+ *
+ * This must not be called if pt_can_have_leaf() == false. Contiguous sizes
+ * not indicated by pt_possible_sizes() must not be specified.
+ */
+static inline void pt_install_leaf_entry(struct pt_state *pts, pt_oaddr_t oa,
+ unsigned int oasz_lg2,
+ const struct pt_write_attrs *attrs);
+
+/**
+ * pt_install_table() - Write a table entry to the table
+ * @pts: Table index to change
+ * @table_pa: CPU physical address of the lower table's memory
+ * @attrs: Attributes to modify the table index
+ *
+ * A table entry will return PT_ENTRY_TABLE from pt_load_entry(). The table_pa
+ * is the table at pts->level - 1. This is done by cmpxchg so pts must have the
+ * current entry loaded. The pts is updated with the installed entry.
+ *
+ * This must not be called if pt_can_have_table() == false.
+ *
+ * Returns: true if the table was installed successfully.
+ */
+static inline bool pt_install_table(struct pt_state *pts, pt_oaddr_t table_pa,
+ const struct pt_write_attrs *attrs);
+
+/**
+ * pt_item_oa() - Output Address for this leaf item
+ * @pts: Item to query
+ *
+ * Return the output address for this item. If the item is part of a contiguous
+ * entry it returns the value of the OA for this individual sub item.
+ *
+ * See pt_entry_oa(). The format should implement one of these two functions
+ * depending on how it stores the OA's in the table.
+ */
+static inline pt_oaddr_t pt_item_oa(const struct pt_state *pts);
+
+/**
+ * pt_load_entry_raw() - Read from the location pts points at into the pts
+ * @pts: Table index to load
+ *
+ * Return the type of entry that was loaded. pts->entry will be filled in with
+ * the entry's content. See pt_load_entry()
+ */
+static inline enum pt_entry_type pt_load_entry_raw(struct pt_state *pts);
+
+/**
+ * pt_max_oa_lg2() - Return the maximum OA the table format can hold
+ * @common: Page table to query
+ *
+ * The value oalog2_to_max_int(pt_max_oa_lg2()) is the MAX for the
+ * OA. This is the absolute maximum address the table can hold. struct pt_common
+ * max_oasz_lg2 sets a lower dynamic maximum based on HW capability.
+ */
+static inline unsigned int
+pt_max_oa_lg2(const struct pt_common *common);
+
+/**
+ * pt_num_items_lg2() - Return the number of items in this table level
+ * @pts: The current level
+ *
+ * The number of items in a table level defines the number of bits this level
+ * decodes from the VA. This function is not called for the top level,
+ * so it does not need to compute a special value for the top case. The
+ * result for the top is based on pt_common max_vasz_lg2.
+ *
+ * The value is used as part of determining the table indexes via the
+ * equation::
+ *
+ * log2_mod(log2_div(VA, pt_table_item_lg2sz()), pt_num_items_lg2())
+ */
+static inline unsigned int pt_num_items_lg2(const struct pt_state *pts);
+
+/**
+ * pt_pgsz_lg2_to_level - Return the level that maps the page size
+ * @common: Page table to query
+ * @pgsize_lg2: Log2 page size
+ *
+ * Returns the table level that will map the given page size. The page
+ * size must be part of the pt_possible_sizes() for some level.
+ */
+static inline unsigned int pt_pgsz_lg2_to_level(struct pt_common *common,
+ unsigned int pgsize_lg2);
+
+/**
+ * pt_possible_sizes() - Return a bitmap of possible output sizes at this level
+ * @pts: The current level
+ *
+ * Each level has a list of possible output sizes that can be installed as
+ * leaf entries. If pt_can_have_leaf() is false returns zero.
+ *
+ * Otherwise the bit in position pt_table_item_lg2sz() should be set indicating
+ * that a non-contiguous single item leaf entry is supported. The following
+ * pt_num_items_lg2() number of bits can be set indicating contiguous entries
+ * are supported. Bit pt_table_item_lg2sz() + pt_num_items_lg2() must not be
+ * set, contiguous entries cannot span the entire table.
+ *
+ * The OR of pt_possible_sizes() of all levels is the typical bitmask of all
+ * supported sizes in the entire table.
+ */
+static inline pt_vaddr_t pt_possible_sizes(const struct pt_state *pts);
+
+/**
+ * pt_table_item_lg2sz() - Size of a single item entry in this table level
+ * @pts: The current level
+ *
+ * The size of the item specifies how much VA and OA a single item occupies.
+ *
+ * See pt_entry_oa_lg2sz() for the same value including the effect of contiguous
+ * entries.
+ */
+static inline unsigned int pt_table_item_lg2sz(const struct pt_state *pts);
+
+/**
+ * pt_table_oa_lg2sz() - Return the VA/OA size of the entire table
+ * @pts: The current level
+ *
+ * Return the size of VA decoded by the entire table level.
+ */
+static inline unsigned int pt_table_oa_lg2sz(const struct pt_state *pts)
+{
+ if (pts->range->top_level == pts->level)
+ return pts->range->max_vasz_lg2;
+ return min_t(unsigned int, pts->range->common->max_vasz_lg2,
+ pt_num_items_lg2(pts) + pt_table_item_lg2sz(pts));
+}
+
+/**
+ * pt_table_pa() - Return the CPU physical address of the table entry
+ * @pts: Entry to query
+ *
+ * This is only ever called on PT_ENTRY_TABLE entries. Must return the same
+ * value passed to pt_install_table().
+ */
+static inline pt_oaddr_t pt_table_pa(const struct pt_state *pts);
+
+/**
+ * pt_table_ptr() - Return a CPU pointer for a table item
+ * @pts: Entry to query
+ *
+ * Same as pt_table_pa() but returns a CPU pointer.
+ */
+static inline struct pt_table_p *pt_table_ptr(const struct pt_state *pts)
+{
+ return __va(pt_table_pa(pts));
+}
+
+/**
+ * pt_max_sw_bit() - Return the maximum software bit usable for any level and
+ * entry
+ * @common: Page table
+ *
+ * The swbit can be passed as bitnr to the other sw_bit functions.
+ */
+static inline unsigned int pt_max_sw_bit(struct pt_common *common);
+
+/**
+ * pt_test_sw_bit_acquire() - Read a software bit in an item
+ * @pts: Entry to read
+ * @bitnr: Bit to read
+ *
+ * Software bits are ignored by HW and can be used for any purpose by the
+ * software. This does a test bit and acquire operation.
+ */
+static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
+ unsigned int bitnr);
+
+/**
+ * pt_set_sw_bit_release() - Set a software bit in an item
+ * @pts: Entry to set
+ * @bitnr: Bit to set
+ *
+ * Software bits are ignored by HW and can be used for any purpose by the
+ * software. This does a set bit and release operation.
+ */
+static inline void pt_set_sw_bit_release(struct pt_state *pts,
+ unsigned int bitnr);
+
+/**
+ * pt_load_entry() - Read from the location pts points at into the pts
+ * @pts: Table index to load
+ *
+ * Set the type of entry that was loaded. pts->entry and pts->table_lower
+ * will be filled in with the entry's content.
+ */
+static inline void pt_load_entry(struct pt_state *pts)
+{
+ pts->type = pt_load_entry_raw(pts);
+ if (pts->type == PT_ENTRY_TABLE)
+ pts->table_lower = pt_table_ptr(pts);
+}
+#endif
diff --git a/drivers/iommu/generic_pt/pt_defs.h b/drivers/iommu/generic_pt/pt_defs.h
new file mode 100644
index 000000000000..c25544d72f97
--- /dev/null
+++ b/drivers/iommu/generic_pt/pt_defs.h
@@ -0,0 +1,332 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * This header is included before the format. It contains definitions
+ * that are required to compile the format. The header order is:
+ * pt_defs.h
+ * fmt_XX.h
+ * pt_common.h
+ */
+#ifndef __GENERIC_PT_DEFS_H
+#define __GENERIC_PT_DEFS_H
+
+#include <linux/generic_pt/common.h>
+
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/bits.h>
+#include <linux/limits.h>
+#include <linux/bug.h>
+#include <linux/kconfig.h>
+#include "pt_log2.h"
+
+/* Header self-compile default defines */
+#ifndef pt_write_attrs
+typedef u64 pt_vaddr_t;
+typedef u64 pt_oaddr_t;
+#endif
+
+struct pt_table_p;
+
+enum {
+ PT_VADDR_MAX = sizeof(pt_vaddr_t) == 8 ? U64_MAX : U32_MAX,
+ PT_VADDR_MAX_LG2 = sizeof(pt_vaddr_t) == 8 ? 64 : 32,
+ PT_OADDR_MAX = sizeof(pt_oaddr_t) == 8 ? U64_MAX : U32_MAX,
+ PT_OADDR_MAX_LG2 = sizeof(pt_oaddr_t) == 8 ? 64 : 32,
+};
+
+/*
+ * The format instantiation can have features wired off or on to optimize the
+ * code gen. Supported features are just a reflection of what the current set of
+ * kernel users want to use.
+ */
+#ifndef PT_SUPPORTED_FEATURES
+#define PT_SUPPORTED_FEATURES 0
+#endif
+
+/*
+ * When in debug mode we compile all formats with all features. This allows the
+ * kunit to test the full matrix. SIGN_EXTEND can't co-exist with DYNAMIC_TOP or
+ * FULL_VA. DMA_INCOHERENT requires a SW bit that not all formats have
+ */
+#if IS_ENABLED(CONFIG_DEBUG_GENERIC_PT)
+enum {
+ PT_ORIG_SUPPORTED_FEATURES = PT_SUPPORTED_FEATURES,
+ PT_DEBUG_SUPPORTED_FEATURES =
+ UINT_MAX &
+ ~((PT_ORIG_SUPPORTED_FEATURES & BIT(PT_FEAT_DMA_INCOHERENT) ?
+ 0 :
+ BIT(PT_FEAT_DMA_INCOHERENT))) &
+ ~((PT_ORIG_SUPPORTED_FEATURES & BIT(PT_FEAT_SIGN_EXTEND)) ?
+ BIT(PT_FEAT_DYNAMIC_TOP) | BIT(PT_FEAT_FULL_VA) :
+ BIT(PT_FEAT_SIGN_EXTEND)),
+};
+#undef PT_SUPPORTED_FEATURES
+#define PT_SUPPORTED_FEATURES PT_DEBUG_SUPPORTED_FEATURES
+#endif
+
+#ifndef PT_FORCE_ENABLED_FEATURES
+#define PT_FORCE_ENABLED_FEATURES 0
+#endif
+
+/**
+ * DOC: Generic Page Table Language
+ *
+ * Language used in Generic Page Table
+ * VA
+ * The input address to the page table, often the virtual address.
+ * OA
+ * The output address from the page table, often the physical address.
+ * leaf
+ * An entry that results in an output address.
+ * start/end
+ * An half-open range, e.g. [0,0) refers to no VA.
+ * start/last
+ * An inclusive closed range, e.g. [0,0] refers to the VA 0
+ * common
+ * The generic page table container struct pt_common
+ * level
+ * Level 0 is always a table of only leaves with no futher table pointers.
+ * Increasing levels increase the size of the table items. The least
+ * significant VA bits used to index page tables are used to index the Level
+ * 0 table. The various labels for table levels used by HW descriptions are
+ * not used.
+ * top_level
+ * The inclusive highest level of the table. A two-level table
+ * has a top level of 1.
+ * table
+ * A linear array of translation items for that level.
+ * index
+ * The position in a table of an element: item = table[index]
+ * item
+ * A single index in a table
+ * entry
+ * A single logical element in a table. If contiguous pages are not
+ * supported then item and entry are the same thing, otherwise entry refers
+ * to all the items that comprise a single contiguous translation.
+ * item/entry_size
+ * The number of bytes of VA the table index translates for.
+ * If the item is a table entry then the next table covers
+ * this size. If the entry translates to an output address then the
+ * full OA is: OA | (VA % entry_size)
+ * contig_count
+ * The number of consecutive items fused into a single entry.
+ * item_size * contig_count is the size of that entry's translation.
+ * lg2
+ * Indicates the value is encoded as log2, i.e. 1<<x is the actual value.
+ * Normally the compiler is fine to optimize divide and mod with log2 values
+ * automatically when inlining, however if the values are not constant
+ * expressions it can't. So we do it by hand; we want to avoid 64-bit
+ * divmod.
+ */
+
+/* Returned by pt_load_entry() and for_each_pt_level_entry() */
+enum pt_entry_type {
+ PT_ENTRY_EMPTY,
+ /* Entry is valid and points to a lower table level */
+ PT_ENTRY_TABLE,
+ /* Entry is valid and returns an output address */
+ PT_ENTRY_OA,
+};
+
+struct pt_range {
+ struct pt_common *common;
+ struct pt_table_p *top_table;
+ pt_vaddr_t va;
+ pt_vaddr_t last_va;
+ u8 top_level;
+ u8 max_vasz_lg2;
+};
+
+/*
+ * Similar to xa_state, this records information about an in-progress parse at a
+ * single level.
+ */
+struct pt_state {
+ struct pt_range *range;
+ struct pt_table_p *table;
+ struct pt_table_p *table_lower;
+ u64 entry;
+ enum pt_entry_type type;
+ unsigned short index;
+ unsigned short end_index;
+ u8 level;
+};
+
+#define pt_cur_table(pts, type) ((type *)((pts)->table))
+
+/*
+ * Try to install a new table pointer. The locking methodology requires this to
+ * be atomic (multiple threads can race to install a pointer). The losing
+ * threads will fail the atomic and return false. They should free any memory
+ * and reparse the table level again.
+ */
+#if !IS_ENABLED(CONFIG_GENERIC_ATOMIC64)
+static inline bool pt_table_install64(struct pt_state *pts, u64 table_entry)
+{
+ u64 *entryp = pt_cur_table(pts, u64) + pts->index;
+ u64 old_entry = pts->entry;
+ bool ret;
+
+ /*
+ * Ensure the zero'd table content itself is visible before its PTE can
+ * be. release is a NOP on !SMP, but the HW is still doing an acquire.
+ */
+ if (!IS_ENABLED(CONFIG_SMP))
+ dma_wmb();
+ ret = try_cmpxchg64_release(entryp, &old_entry, table_entry);
+ if (ret)
+ pts->entry = table_entry;
+ return ret;
+}
+#endif
+
+static inline bool pt_table_install32(struct pt_state *pts, u32 table_entry)
+{
+ u32 *entryp = pt_cur_table(pts, u32) + pts->index;
+ u32 old_entry = pts->entry;
+ bool ret;
+
+ /*
+ * Ensure the zero'd table content itself is visible before its PTE can
+ * be. release is a NOP on !SMP, but the HW is still doing an acquire.
+ */
+ if (!IS_ENABLED(CONFIG_SMP))
+ dma_wmb();
+ ret = try_cmpxchg_release(entryp, &old_entry, table_entry);
+ if (ret)
+ pts->entry = table_entry;
+ return ret;
+}
+
+#define PT_SUPPORTED_FEATURE(feature_nr) (PT_SUPPORTED_FEATURES & BIT(feature_nr))
+
+static inline bool pt_feature(const struct pt_common *common,
+ unsigned int feature_nr)
+{
+ if (PT_FORCE_ENABLED_FEATURES & BIT(feature_nr))
+ return true;
+ if (!PT_SUPPORTED_FEATURE(feature_nr))
+ return false;
+ return common->features & BIT(feature_nr);
+}
+
+static inline bool pts_feature(const struct pt_state *pts,
+ unsigned int feature_nr)
+{
+ return pt_feature(pts->range->common, feature_nr);
+}
+
+/*
+ * PT_WARN_ON is used for invariants that the kunit should be checking can't
+ * happen.
+ */
+#if IS_ENABLED(CONFIG_DEBUG_GENERIC_PT)
+#define PT_WARN_ON WARN_ON
+#else
+static inline bool PT_WARN_ON(bool condition)
+{
+ return false;
+}
+#endif
+
+/* These all work on the VA type */
+#define log2_to_int(a_lg2) log2_to_int_t(pt_vaddr_t, a_lg2)
+#define log2_to_max_int(a_lg2) log2_to_max_int_t(pt_vaddr_t, a_lg2)
+#define log2_div(a, b_lg2) log2_div_t(pt_vaddr_t, a, b_lg2)
+#define log2_div_eq(a, b, c_lg2) log2_div_eq_t(pt_vaddr_t, a, b, c_lg2)
+#define log2_mod(a, b_lg2) log2_mod_t(pt_vaddr_t, a, b_lg2)
+#define log2_mod_eq_max(a, b_lg2) log2_mod_eq_max_t(pt_vaddr_t, a, b_lg2)
+#define log2_set_mod(a, val, b_lg2) log2_set_mod_t(pt_vaddr_t, a, val, b_lg2)
+#define log2_set_mod_max(a, b_lg2) log2_set_mod_max_t(pt_vaddr_t, a, b_lg2)
+#define log2_mul(a, b_lg2) log2_mul_t(pt_vaddr_t, a, b_lg2)
+#define vaffs(a) ffs_t(pt_vaddr_t, a)
+#define vafls(a) fls_t(pt_vaddr_t, a)
+#define vaffz(a) ffz_t(pt_vaddr_t, a)
+
+/*
+ * The full VA (fva) versions permit the lg2 value to be == PT_VADDR_MAX_LG2 and
+ * generate a useful defined result. The non-fva versions will malfunction at
+ * this extreme.
+ */
+static inline pt_vaddr_t fvalog2_div(pt_vaddr_t a, unsigned int b_lg2)
+{
+ if (PT_SUPPORTED_FEATURE(PT_FEAT_FULL_VA) && b_lg2 == PT_VADDR_MAX_LG2)
+ return 0;
+ return log2_div_t(pt_vaddr_t, a, b_lg2);
+}
+
+static inline pt_vaddr_t fvalog2_mod(pt_vaddr_t a, unsigned int b_lg2)
+{
+ if (PT_SUPPORTED_FEATURE(PT_FEAT_FULL_VA) && b_lg2 == PT_VADDR_MAX_LG2)
+ return a;
+ return log2_mod_t(pt_vaddr_t, a, b_lg2);
+}
+
+static inline bool fvalog2_div_eq(pt_vaddr_t a, pt_vaddr_t b,
+ unsigned int c_lg2)
+{
+ if (PT_SUPPORTED_FEATURE(PT_FEAT_FULL_VA) && c_lg2 == PT_VADDR_MAX_LG2)
+ return true;
+ return log2_div_eq_t(pt_vaddr_t, a, b, c_lg2);
+}
+
+static inline pt_vaddr_t fvalog2_set_mod(pt_vaddr_t a, pt_vaddr_t val,
+ unsigned int b_lg2)
+{
+ if (PT_SUPPORTED_FEATURE(PT_FEAT_FULL_VA) && b_lg2 == PT_VADDR_MAX_LG2)
+ return val;
+ return log2_set_mod_t(pt_vaddr_t, a, val, b_lg2);
+}
+
+static inline pt_vaddr_t fvalog2_set_mod_max(pt_vaddr_t a, unsigned int b_lg2)
+{
+ if (PT_SUPPORTED_FEATURE(PT_FEAT_FULL_VA) && b_lg2 == PT_VADDR_MAX_LG2)
+ return PT_VADDR_MAX;
+ return log2_set_mod_max_t(pt_vaddr_t, a, b_lg2);
+}
+
+/* These all work on the OA type */
+#define oalog2_to_int(a_lg2) log2_to_int_t(pt_oaddr_t, a_lg2)
+#define oalog2_to_max_int(a_lg2) log2_to_max_int_t(pt_oaddr_t, a_lg2)
+#define oalog2_div(a, b_lg2) log2_div_t(pt_oaddr_t, a, b_lg2)
+#define oalog2_div_eq(a, b, c_lg2) log2_div_eq_t(pt_oaddr_t, a, b, c_lg2)
+#define oalog2_mod(a, b_lg2) log2_mod_t(pt_oaddr_t, a, b_lg2)
+#define oalog2_mod_eq_max(a, b_lg2) log2_mod_eq_max_t(pt_oaddr_t, a, b_lg2)
+#define oalog2_set_mod(a, val, b_lg2) log2_set_mod_t(pt_oaddr_t, a, val, b_lg2)
+#define oalog2_set_mod_max(a, b_lg2) log2_set_mod_max_t(pt_oaddr_t, a, b_lg2)
+#define oalog2_mul(a, b_lg2) log2_mul_t(pt_oaddr_t, a, b_lg2)
+#define oaffs(a) ffs_t(pt_oaddr_t, a)
+#define oafls(a) fls_t(pt_oaddr_t, a)
+#define oaffz(a) ffz_t(pt_oaddr_t, a)
+
+static inline uintptr_t _pt_top_set(struct pt_table_p *table_mem,
+ unsigned int top_level)
+{
+ return top_level | (uintptr_t)table_mem;
+}
+
+static inline void pt_top_set(struct pt_common *common,
+ struct pt_table_p *table_mem,
+ unsigned int top_level)
+{
+ WRITE_ONCE(common->top_of_table, _pt_top_set(table_mem, top_level));
+}
+
+static inline void pt_top_set_level(struct pt_common *common,
+ unsigned int top_level)
+{
+ pt_top_set(common, NULL, top_level);
+}
+
+static inline unsigned int pt_top_get_level(const struct pt_common *common)
+{
+ return READ_ONCE(common->top_of_table) % (1 << PT_TOP_LEVEL_BITS);
+}
+
+static inline bool pt_check_install_leaf_args(struct pt_state *pts,
+ pt_oaddr_t oa,
+ unsigned int oasz_lg2);
+
+#endif
diff --git a/drivers/iommu/generic_pt/pt_fmt_defaults.h b/drivers/iommu/generic_pt/pt_fmt_defaults.h
new file mode 100644
index 000000000000..69fb7c2314ca
--- /dev/null
+++ b/drivers/iommu/generic_pt/pt_fmt_defaults.h
@@ -0,0 +1,295 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Default definitions for formats that don't define these functions.
+ */
+#ifndef __GENERIC_PT_PT_FMT_DEFAULTS_H
+#define __GENERIC_PT_PT_FMT_DEFAULTS_H
+
+#include "pt_defs.h"
+#include <linux/log2.h>
+
+/* Header self-compile default defines */
+#ifndef pt_load_entry_raw
+#include "fmt/amdv1.h"
+#endif
+
+/*
+ * The format must provide PT_GRANULE_LG2SZ, PT_TABLEMEM_LG2SZ, and
+ * PT_ITEM_WORD_SIZE. They must be the same at every level excluding the top.
+ */
+#ifndef pt_table_item_lg2sz
+static inline unsigned int pt_table_item_lg2sz(const struct pt_state *pts)
+{
+ return PT_GRANULE_LG2SZ +
+ (PT_TABLEMEM_LG2SZ - ilog2(PT_ITEM_WORD_SIZE)) * pts->level;
+}
+#endif
+
+#ifndef pt_pgsz_lg2_to_level
+static inline unsigned int pt_pgsz_lg2_to_level(struct pt_common *common,
+ unsigned int pgsize_lg2)
+{
+ return ((unsigned int)(pgsize_lg2 - PT_GRANULE_LG2SZ)) /
+ (PT_TABLEMEM_LG2SZ - ilog2(PT_ITEM_WORD_SIZE));
+}
+#endif
+
+/*
+ * If not supplied by the format then contiguous pages are not supported.
+ *
+ * If contiguous pages are supported then the format must also provide
+ * pt_contig_count_lg2() if it supports a single contiguous size per level,
+ * or pt_possible_sizes() if it supports multiple sizes per level.
+ */
+#ifndef pt_entry_num_contig_lg2
+static inline unsigned int pt_entry_num_contig_lg2(const struct pt_state *pts)
+{
+ return ilog2(1);
+}
+
+/*
+ * Return the number of contiguous OA items forming an entry at this table level
+ */
+static inline unsigned short pt_contig_count_lg2(const struct pt_state *pts)
+{
+ return ilog2(1);
+}
+#endif
+
+/* If not supplied by the format then dirty tracking is not supported */
+#ifndef pt_entry_is_write_dirty
+static inline bool pt_entry_is_write_dirty(const struct pt_state *pts)
+{
+ return false;
+}
+
+static inline void pt_entry_make_write_clean(struct pt_state *pts)
+{
+}
+
+static inline bool pt_dirty_supported(struct pt_common *common)
+{
+ return false;
+}
+#else
+/* If not supplied then dirty tracking is always enabled */
+#ifndef pt_dirty_supported
+static inline bool pt_dirty_supported(struct pt_common *common)
+{
+ return true;
+}
+#endif
+#endif
+
+#ifndef pt_entry_make_write_dirty
+static inline bool pt_entry_make_write_dirty(struct pt_state *pts)
+{
+ return false;
+}
+#endif
+
+/*
+ * Format supplies either:
+ * pt_entry_oa - OA is at the start of a contiguous entry
+ * or
+ * pt_item_oa - OA is adjusted for every item in a contiguous entry
+ *
+ * Build the missing one
+ *
+ * The internal helper _pt_entry_oa_fast() allows generating
+ * an efficient pt_entry_oa_exact(), it doesn't care which
+ * option is selected.
+ */
+#ifdef pt_entry_oa
+static inline pt_oaddr_t pt_item_oa(const struct pt_state *pts)
+{
+ return pt_entry_oa(pts) |
+ log2_mul(pts->index, pt_table_item_lg2sz(pts));
+}
+#define _pt_entry_oa_fast pt_entry_oa
+#endif
+
+#ifdef pt_item_oa
+static inline pt_oaddr_t pt_entry_oa(const struct pt_state *pts)
+{
+ return log2_set_mod(pt_item_oa(pts), 0,
+ pt_entry_num_contig_lg2(pts) +
+ pt_table_item_lg2sz(pts));
+}
+#define _pt_entry_oa_fast pt_item_oa
+#endif
+
+/*
+ * If not supplied by the format then use the constant
+ * PT_MAX_OUTPUT_ADDRESS_LG2.
+ */
+#ifndef pt_max_oa_lg2
+static inline unsigned int
+pt_max_oa_lg2(const struct pt_common *common)
+{
+ return PT_MAX_OUTPUT_ADDRESS_LG2;
+}
+#endif
+
+#ifndef pt_has_system_page_size
+static inline bool pt_has_system_page_size(const struct pt_common *common)
+{
+ return PT_GRANULE_LG2SZ == PAGE_SHIFT;
+}
+#endif
+
+/*
+ * If not supplied by the format then assume only one contiguous size determined
+ * by pt_contig_count_lg2()
+ */
+#ifndef pt_possible_sizes
+static inline unsigned short pt_contig_count_lg2(const struct pt_state *pts);
+
+/* Return a bitmap of possible leaf page sizes at this level */
+static inline pt_vaddr_t pt_possible_sizes(const struct pt_state *pts)
+{
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+
+ if (!pt_can_have_leaf(pts))
+ return 0;
+ return log2_to_int(isz_lg2) |
+ log2_to_int(pt_contig_count_lg2(pts) + isz_lg2);
+}
+#endif
+
+/* If not supplied by the format then use 0. */
+#ifndef pt_full_va_prefix
+static inline pt_vaddr_t pt_full_va_prefix(const struct pt_common *common)
+{
+ return 0;
+}
+#endif
+
+/* If not supplied by the format then zero fill using PT_ITEM_WORD_SIZE */
+#ifndef pt_clear_entries
+static inline void pt_clear_entries64(struct pt_state *pts,
+ unsigned int num_contig_lg2)
+{
+ u64 *tablep = pt_cur_table(pts, u64) + pts->index;
+ u64 *end = tablep + log2_to_int(num_contig_lg2);
+
+ PT_WARN_ON(log2_mod(pts->index, num_contig_lg2));
+ for (; tablep != end; tablep++)
+ WRITE_ONCE(*tablep, 0);
+}
+
+static inline void pt_clear_entries32(struct pt_state *pts,
+ unsigned int num_contig_lg2)
+{
+ u32 *tablep = pt_cur_table(pts, u32) + pts->index;
+ u32 *end = tablep + log2_to_int(num_contig_lg2);
+
+ PT_WARN_ON(log2_mod(pts->index, num_contig_lg2));
+ for (; tablep != end; tablep++)
+ WRITE_ONCE(*tablep, 0);
+}
+
+static inline void pt_clear_entries(struct pt_state *pts,
+ unsigned int num_contig_lg2)
+{
+ if (PT_ITEM_WORD_SIZE == sizeof(u32))
+ pt_clear_entries32(pts, num_contig_lg2);
+ else
+ pt_clear_entries64(pts, num_contig_lg2);
+}
+#define pt_clear_entries pt_clear_entries
+#endif
+
+/* If not supplied then SW bits are not supported */
+#ifdef pt_sw_bit
+static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
+ unsigned int bitnr)
+{
+ /* Acquire, pairs with pt_set_sw_bit_release() */
+ smp_mb();
+ /* For a contiguous entry the sw bit is only stored in the first item. */
+ return pts->entry & pt_sw_bit(bitnr);
+}
+#define pt_test_sw_bit_acquire pt_test_sw_bit_acquire
+
+static inline void pt_set_sw_bit_release(struct pt_state *pts,
+ unsigned int bitnr)
+{
+#if !IS_ENABLED(CONFIG_GENERIC_ATOMIC64)
+ if (PT_ITEM_WORD_SIZE == sizeof(u64)) {
+ u64 *entryp = pt_cur_table(pts, u64) + pts->index;
+ u64 old_entry = pts->entry;
+ u64 new_entry;
+
+ do {
+ new_entry = old_entry | pt_sw_bit(bitnr);
+ } while (!try_cmpxchg64_release(entryp, &old_entry, new_entry));
+ pts->entry = new_entry;
+ return;
+ }
+#endif
+ if (PT_ITEM_WORD_SIZE == sizeof(u32)) {
+ u32 *entryp = pt_cur_table(pts, u32) + pts->index;
+ u32 old_entry = pts->entry;
+ u32 new_entry;
+
+ do {
+ new_entry = old_entry | pt_sw_bit(bitnr);
+ } while (!try_cmpxchg_release(entryp, &old_entry, new_entry));
+ pts->entry = new_entry;
+ } else
+ BUILD_BUG();
+}
+#define pt_set_sw_bit_release pt_set_sw_bit_release
+#else
+static inline unsigned int pt_max_sw_bit(struct pt_common *common)
+{
+ return 0;
+}
+
+extern void __pt_no_sw_bit(void);
+static inline bool pt_test_sw_bit_acquire(struct pt_state *pts,
+ unsigned int bitnr)
+{
+ __pt_no_sw_bit();
+ return false;
+}
+
+static inline void pt_set_sw_bit_release(struct pt_state *pts,
+ unsigned int bitnr)
+{
+ __pt_no_sw_bit();
+}
+#endif
+
+/*
+ * Format can call in the pt_install_leaf_entry() to check the arguments are all
+ * aligned correctly.
+ */
+static inline bool pt_check_install_leaf_args(struct pt_state *pts,
+ pt_oaddr_t oa,
+ unsigned int oasz_lg2)
+{
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+
+ if (PT_WARN_ON(oalog2_mod(oa, oasz_lg2)))
+ return false;
+
+#ifdef pt_possible_sizes
+ if (PT_WARN_ON(isz_lg2 > oasz_lg2 ||
+ oasz_lg2 > isz_lg2 + pt_num_items_lg2(pts)))
+ return false;
+#else
+ if (PT_WARN_ON(oasz_lg2 != isz_lg2 &&
+ oasz_lg2 != isz_lg2 + pt_contig_count_lg2(pts)))
+ return false;
+#endif
+
+ if (PT_WARN_ON(oalog2_mod(pts->index, oasz_lg2 - isz_lg2)))
+ return false;
+ return true;
+}
+
+#endif
diff --git a/drivers/iommu/generic_pt/pt_iter.h b/drivers/iommu/generic_pt/pt_iter.h
new file mode 100644
index 000000000000..c0d8617cce29
--- /dev/null
+++ b/drivers/iommu/generic_pt/pt_iter.h
@@ -0,0 +1,636 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Iterators for Generic Page Table
+ */
+#ifndef __GENERIC_PT_PT_ITER_H
+#define __GENERIC_PT_PT_ITER_H
+
+#include "pt_common.h"
+
+#include <linux/errno.h>
+
+/*
+ * Use to mangle symbols so that backtraces and the symbol table are
+ * understandable. Any non-inlined function should get mangled like this.
+ */
+#define NS(fn) CONCATENATE(PTPFX, fn)
+
+/**
+ * pt_check_range() - Validate the range can be iterated
+ * @range: Range to validate
+ *
+ * Check that VA and last_va fall within the permitted range of VAs. If the
+ * format is using PT_FEAT_SIGN_EXTEND then this also checks the sign extension
+ * is correct.
+ */
+static inline int pt_check_range(struct pt_range *range)
+{
+ pt_vaddr_t prefix;
+
+ PT_WARN_ON(!range->max_vasz_lg2);
+
+ if (pt_feature(range->common, PT_FEAT_SIGN_EXTEND)) {
+ PT_WARN_ON(range->common->max_vasz_lg2 != range->max_vasz_lg2);
+ prefix = fvalog2_div(range->va, range->max_vasz_lg2 - 1) ?
+ PT_VADDR_MAX :
+ 0;
+ } else {
+ prefix = pt_full_va_prefix(range->common);
+ }
+
+ if (!fvalog2_div_eq(range->va, prefix, range->max_vasz_lg2) ||
+ !fvalog2_div_eq(range->last_va, prefix, range->max_vasz_lg2))
+ return -ERANGE;
+ return 0;
+}
+
+/**
+ * pt_index_to_va() - Update range->va to the current pts->index
+ * @pts: Iteration State
+ *
+ * Adjust range->va to match the current index. This is done in a lazy manner
+ * since computing the VA takes several instructions and is rarely required.
+ */
+static inline void pt_index_to_va(struct pt_state *pts)
+{
+ pt_vaddr_t lower_va;
+
+ lower_va = log2_mul(pts->index, pt_table_item_lg2sz(pts));
+ pts->range->va = fvalog2_set_mod(pts->range->va, lower_va,
+ pt_table_oa_lg2sz(pts));
+}
+
+/*
+ * Add index_count_lg2 number of entries to pts's VA and index. The VA will be
+ * adjusted to the end of the contiguous block if it is currently in the middle.
+ */
+static inline void _pt_advance(struct pt_state *pts,
+ unsigned int index_count_lg2)
+{
+ pts->index = log2_set_mod(pts->index + log2_to_int(index_count_lg2), 0,
+ index_count_lg2);
+}
+
+/**
+ * pt_entry_fully_covered() - Check if the item or entry is entirely contained
+ * within pts->range
+ * @pts: Iteration State
+ * @oasz_lg2: The size of the item to check, pt_table_item_lg2sz() or
+ * pt_entry_oa_lg2sz()
+ *
+ * Returns: true if the item is fully enclosed by the pts->range.
+ */
+static inline bool pt_entry_fully_covered(const struct pt_state *pts,
+ unsigned int oasz_lg2)
+{
+ struct pt_range *range = pts->range;
+
+ /* Range begins at the start of the entry */
+ if (log2_mod(pts->range->va, oasz_lg2))
+ return false;
+
+ /* Range ends past the end of the entry */
+ if (!log2_div_eq(range->va, range->last_va, oasz_lg2))
+ return true;
+
+ /* Range ends at the end of the entry */
+ return log2_mod_eq_max(range->last_va, oasz_lg2);
+}
+
+/**
+ * pt_range_to_index() - Starting index for an iteration
+ * @pts: Iteration State
+ *
+ * Return: the starting index for the iteration in pts.
+ */
+static inline unsigned int pt_range_to_index(const struct pt_state *pts)
+{
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+
+ PT_WARN_ON(pts->level > pts->range->top_level);
+ if (pts->range->top_level == pts->level)
+ return log2_div(fvalog2_mod(pts->range->va,
+ pts->range->max_vasz_lg2),
+ isz_lg2);
+ return log2_mod(log2_div(pts->range->va, isz_lg2),
+ pt_num_items_lg2(pts));
+}
+
+/**
+ * pt_range_to_end_index() - Ending index iteration
+ * @pts: Iteration State
+ *
+ * Return: the last index for the iteration in pts.
+ */
+static inline unsigned int pt_range_to_end_index(const struct pt_state *pts)
+{
+ unsigned int isz_lg2 = pt_table_item_lg2sz(pts);
+ struct pt_range *range = pts->range;
+ unsigned int num_entries_lg2;
+
+ if (range->va == range->last_va)
+ return pts->index + 1;
+
+ if (pts->range->top_level == pts->level)
+ return log2_div(fvalog2_mod(pts->range->last_va,
+ pts->range->max_vasz_lg2),
+ isz_lg2) +
+ 1;
+
+ num_entries_lg2 = pt_num_items_lg2(pts);
+
+ /* last_va falls within this table */
+ if (log2_div_eq(range->va, range->last_va, num_entries_lg2 + isz_lg2))
+ return log2_mod(log2_div(pts->range->last_va, isz_lg2),
+ num_entries_lg2) +
+ 1;
+
+ return log2_to_int(num_entries_lg2);
+}
+
+static inline void _pt_iter_first(struct pt_state *pts)
+{
+ pts->index = pt_range_to_index(pts);
+ pts->end_index = pt_range_to_end_index(pts);
+ PT_WARN_ON(pts->index > pts->end_index);
+}
+
+static inline bool _pt_iter_load(struct pt_state *pts)
+{
+ if (pts->index >= pts->end_index)
+ return false;
+ pt_load_entry(pts);
+ return true;
+}
+
+/**
+ * pt_next_entry() - Advance pts to the next entry
+ * @pts: Iteration State
+ *
+ * Update pts to go to the next index at this level. If pts is pointing at a
+ * contiguous entry then the index may advance my more than one.
+ */
+static inline void pt_next_entry(struct pt_state *pts)
+{
+ if (pts->type == PT_ENTRY_OA &&
+ !__builtin_constant_p(pt_entry_num_contig_lg2(pts) == 0))
+ _pt_advance(pts, pt_entry_num_contig_lg2(pts));
+ else
+ pts->index++;
+ pt_index_to_va(pts);
+}
+
+/**
+ * for_each_pt_level_entry() - For loop wrapper over entries in the range
+ * @pts: Iteration State
+ *
+ * This is the basic iteration primitive. It iterates over all the entries in
+ * pts->range that fall within the pts's current table level. Each step does
+ * pt_load_entry(pts).
+ */
+#define for_each_pt_level_entry(pts) \
+ for (_pt_iter_first(pts); _pt_iter_load(pts); pt_next_entry(pts))
+
+/**
+ * pt_load_single_entry() - Version of pt_load_entry() usable within a walker
+ * @pts: Iteration State
+ *
+ * Alternative to for_each_pt_level_entry() if the walker function uses only a
+ * single entry.
+ */
+static inline enum pt_entry_type pt_load_single_entry(struct pt_state *pts)
+{
+ pts->index = pt_range_to_index(pts);
+ pt_load_entry(pts);
+ return pts->type;
+}
+
+static __always_inline struct pt_range _pt_top_range(struct pt_common *common,
+ uintptr_t top_of_table)
+{
+ struct pt_range range = {
+ .common = common,
+ .top_table =
+ (struct pt_table_p *)(top_of_table &
+ ~(uintptr_t)PT_TOP_LEVEL_MASK),
+ .top_level = top_of_table % (1 << PT_TOP_LEVEL_BITS),
+ };
+ struct pt_state pts = { .range = &range, .level = range.top_level };
+ unsigned int max_vasz_lg2;
+
+ max_vasz_lg2 = common->max_vasz_lg2;
+ if (pt_feature(common, PT_FEAT_DYNAMIC_TOP) &&
+ pts.level != PT_MAX_TOP_LEVEL)
+ max_vasz_lg2 = min_t(unsigned int, common->max_vasz_lg2,
+ pt_num_items_lg2(&pts) +
+ pt_table_item_lg2sz(&pts));
+
+ /*
+ * The top range will default to the lower region only with sign extend.
+ */
+ range.max_vasz_lg2 = max_vasz_lg2;
+ if (pt_feature(common, PT_FEAT_SIGN_EXTEND))
+ max_vasz_lg2--;
+
+ range.va = fvalog2_set_mod(pt_full_va_prefix(common), 0, max_vasz_lg2);
+ range.last_va =
+ fvalog2_set_mod_max(pt_full_va_prefix(common), max_vasz_lg2);
+ return range;
+}
+
+/**
+ * pt_top_range() - Return a range that spans part of the top level
+ * @common: Table
+ *
+ * For PT_FEAT_SIGN_EXTEND this will return the lower range, and cover half the
+ * total page table. Otherwise it returns the entire page table.
+ */
+static __always_inline struct pt_range pt_top_range(struct pt_common *common)
+{
+ /*
+ * The top pointer can change without locking. We capture the value and
+ * it's level here and are safe to walk it so long as both values are
+ * captured without tearing.
+ */
+ return _pt_top_range(common, READ_ONCE(common->top_of_table));
+}
+
+/**
+ * pt_all_range() - Return a range that spans the entire page table
+ * @common: Table
+ *
+ * The returned range spans the whole page table. Due to how PT_FEAT_SIGN_EXTEND
+ * is supported range->va and range->last_va will be incorrect during the
+ * iteration and must not be accessed.
+ */
+static inline struct pt_range pt_all_range(struct pt_common *common)
+{
+ struct pt_range range = pt_top_range(common);
+
+ if (!pt_feature(common, PT_FEAT_SIGN_EXTEND))
+ return range;
+
+ /*
+ * Pretend the table is linear from 0 without a sign extension. This
+ * generates the correct indexes for iteration.
+ */
+ range.last_va = fvalog2_set_mod_max(0, range.max_vasz_lg2);
+ return range;
+}
+
+/**
+ * pt_upper_range() - Return a range that spans part of the top level
+ * @common: Table
+ *
+ * For PT_FEAT_SIGN_EXTEND this will return the upper range, and cover half the
+ * total page table. Otherwise it returns the entire page table.
+ */
+static inline struct pt_range pt_upper_range(struct pt_common *common)
+{
+ struct pt_range range = pt_top_range(common);
+
+ if (!pt_feature(common, PT_FEAT_SIGN_EXTEND))
+ return range;
+
+ range.va = fvalog2_set_mod(PT_VADDR_MAX, 0, range.max_vasz_lg2 - 1);
+ range.last_va = PT_VADDR_MAX;
+ return range;
+}
+
+/**
+ * pt_make_range() - Return a range that spans part of the table
+ * @common: Table
+ * @va: Start address
+ * @last_va: Last address
+ *
+ * The caller must validate the range with pt_check_range() before using it.
+ */
+static __always_inline struct pt_range
+pt_make_range(struct pt_common *common, pt_vaddr_t va, pt_vaddr_t last_va)
+{
+ struct pt_range range =
+ _pt_top_range(common, READ_ONCE(common->top_of_table));
+
+ range.va = va;
+ range.last_va = last_va;
+
+ return range;
+}
+
+/*
+ * Span a slice of the table starting at a lower table level from an active
+ * walk.
+ */
+static __always_inline struct pt_range
+pt_make_child_range(const struct pt_range *parent, pt_vaddr_t va,
+ pt_vaddr_t last_va)
+{
+ struct pt_range range = *parent;
+
+ range.va = va;
+ range.last_va = last_va;
+
+ PT_WARN_ON(last_va < va);
+ PT_WARN_ON(pt_check_range(&range));
+
+ return range;
+}
+
+/**
+ * pt_init() - Initialize a pt_state on the stack
+ * @range: Range pointer to embed in the state
+ * @level: Table level for the state
+ * @table: Pointer to the table memory at level
+ *
+ * Helper to initialize the on-stack pt_state from walker arguments.
+ */
+static __always_inline struct pt_state
+pt_init(struct pt_range *range, unsigned int level, struct pt_table_p *table)
+{
+ struct pt_state pts = {
+ .range = range,
+ .table = table,
+ .level = level,
+ };
+ return pts;
+}
+
+/**
+ * pt_init_top() - Initialize a pt_state on the stack
+ * @range: Range pointer to embed in the state
+ *
+ * The pt_state points to the top most level.
+ */
+static __always_inline struct pt_state pt_init_top(struct pt_range *range)
+{
+ return pt_init(range, range->top_level, range->top_table);
+}
+
+typedef int (*pt_level_fn_t)(struct pt_range *range, void *arg,
+ unsigned int level, struct pt_table_p *table);
+
+/**
+ * pt_descend() - Recursively invoke the walker for the lower level
+ * @pts: Iteration State
+ * @arg: Value to pass to the function
+ * @fn: Walker function to call
+ *
+ * pts must point to a table item. Invoke fn as a walker on the table
+ * pts points to.
+ */
+static __always_inline int pt_descend(struct pt_state *pts, void *arg,
+ pt_level_fn_t fn)
+{
+ int ret;
+
+ if (PT_WARN_ON(!pts->table_lower))
+ return -EINVAL;
+
+ ret = (*fn)(pts->range, arg, pts->level - 1, pts->table_lower);
+ return ret;
+}
+
+/**
+ * pt_walk_range() - Walk over a VA range
+ * @range: Range pointer
+ * @fn: Walker function to call
+ * @arg: Value to pass to the function
+ *
+ * Walk over a VA range. The caller should have done a validity check, at
+ * least calling pt_check_range(), when building range. The walk will
+ * start at the top most table.
+ */
+static __always_inline int pt_walk_range(struct pt_range *range,
+ pt_level_fn_t fn, void *arg)
+{
+ return fn(range, arg, range->top_level, range->top_table);
+}
+
+/*
+ * pt_walk_descend() - Recursively invoke the walker for a slice of a lower
+ * level
+ * @pts: Iteration State
+ * @va: Start address
+ * @last_va: Last address
+ * @fn: Walker function to call
+ * @arg: Value to pass to the function
+ *
+ * With pts pointing at a table item this will descend and over a slice of the
+ * lower table. The caller must ensure that va/last_va are within the table
+ * item. This creates a new walk and does not alter pts or pts->range.
+ */
+static __always_inline int pt_walk_descend(const struct pt_state *pts,
+ pt_vaddr_t va, pt_vaddr_t last_va,
+ pt_level_fn_t fn, void *arg)
+{
+ struct pt_range range = pt_make_child_range(pts->range, va, last_va);
+
+ if (PT_WARN_ON(!pt_can_have_table(pts)) ||
+ PT_WARN_ON(!pts->table_lower))
+ return -EINVAL;
+
+ return fn(&range, arg, pts->level - 1, pts->table_lower);
+}
+
+/*
+ * pt_walk_descend_all() - Recursively invoke the walker for a table item
+ * @parent_pts: Iteration State
+ * @fn: Walker function to call
+ * @arg: Value to pass to the function
+ *
+ * With pts pointing at a table item this will descend and over the entire lower
+ * table. This creates a new walk and does not alter pts or pts->range.
+ */
+static __always_inline int
+pt_walk_descend_all(const struct pt_state *parent_pts, pt_level_fn_t fn,
+ void *arg)
+{
+ unsigned int isz_lg2 = pt_table_item_lg2sz(parent_pts);
+
+ return pt_walk_descend(parent_pts,
+ log2_set_mod(parent_pts->range->va, 0, isz_lg2),
+ log2_set_mod_max(parent_pts->range->va, isz_lg2),
+ fn, arg);
+}
+
+/**
+ * pt_range_slice() - Return a range that spans indexes
+ * @pts: Iteration State
+ * @start_index: Starting index within pts
+ * @end_index: Ending index within pts
+ *
+ * Create a range than spans an index range of the current table level
+ * pt_state points at.
+ */
+static inline struct pt_range pt_range_slice(const struct pt_state *pts,
+ unsigned int start_index,
+ unsigned int end_index)
+{
+ unsigned int table_lg2sz = pt_table_oa_lg2sz(pts);
+ pt_vaddr_t last_va;
+ pt_vaddr_t va;
+
+ va = fvalog2_set_mod(pts->range->va,
+ log2_mul(start_index, pt_table_item_lg2sz(pts)),
+ table_lg2sz);
+ last_va = fvalog2_set_mod(
+ pts->range->va,
+ log2_mul(end_index, pt_table_item_lg2sz(pts)) - 1, table_lg2sz);
+ return pt_make_child_range(pts->range, va, last_va);
+}
+
+/**
+ * pt_top_memsize_lg2()
+ * @common: Table
+ * @top_of_table: Top of table value from _pt_top_set()
+ *
+ * Compute the allocation size of the top table. For PT_FEAT_DYNAMIC_TOP this
+ * will compute the top size assuming the table will grow.
+ */
+static inline unsigned int pt_top_memsize_lg2(struct pt_common *common,
+ uintptr_t top_of_table)
+{
+ struct pt_range range = _pt_top_range(common, top_of_table);
+ struct pt_state pts = pt_init_top(&range);
+ unsigned int num_items_lg2;
+
+ num_items_lg2 = common->max_vasz_lg2 - pt_table_item_lg2sz(&pts);
+ if (range.top_level != PT_MAX_TOP_LEVEL &&
+ pt_feature(common, PT_FEAT_DYNAMIC_TOP))
+ num_items_lg2 = min(num_items_lg2, pt_num_items_lg2(&pts));
+
+ /* Round up the allocation size to the minimum alignment */
+ return max(ffs_t(u64, PT_TOP_PHYS_MASK),
+ num_items_lg2 + ilog2(PT_ITEM_WORD_SIZE));
+}
+
+/**
+ * pt_compute_best_pgsize() - Determine the best page size for leaf entries
+ * @pgsz_bitmap: Permitted page sizes
+ * @va: Starting virtual address for the leaf entry
+ * @last_va: Last virtual address for the leaf entry, sets the max page size
+ * @oa: Starting output address for the leaf entry
+ *
+ * Compute the largest page size for va, last_va, and oa together and return it
+ * in lg2. The largest page size depends on the format's supported page sizes at
+ * this level, and the relative alignment of the VA and OA addresses. 0 means
+ * the OA cannot be stored with the provided pgsz_bitmap.
+ */
+static inline unsigned int pt_compute_best_pgsize(pt_vaddr_t pgsz_bitmap,
+ pt_vaddr_t va,
+ pt_vaddr_t last_va,
+ pt_oaddr_t oa)
+{
+ unsigned int best_pgsz_lg2;
+ unsigned int pgsz_lg2;
+ pt_vaddr_t len = last_va - va + 1;
+ pt_vaddr_t mask;
+
+ if (PT_WARN_ON(va >= last_va))
+ return 0;
+
+ /*
+ * Given a VA/OA pair the best page size is the largest page size
+ * where:
+ *
+ * 1) VA and OA start at the page. Bitwise this is the count of least
+ * significant 0 bits.
+ * This also implies that last_va/oa has the same prefix as va/oa.
+ */
+ mask = va | oa;
+
+ /*
+ * 2) The page size is not larger than the last_va (length). Since page
+ * sizes are always power of two this can't be larger than the
+ * largest power of two factor of the length.
+ */
+ mask |= log2_to_int(vafls(len) - 1);
+
+ best_pgsz_lg2 = vaffs(mask);
+
+ /* Choose the highest bit <= best_pgsz_lg2 */
+ if (best_pgsz_lg2 < PT_VADDR_MAX_LG2 - 1)
+ pgsz_bitmap = log2_mod(pgsz_bitmap, best_pgsz_lg2 + 1);
+
+ pgsz_lg2 = vafls(pgsz_bitmap);
+ if (!pgsz_lg2)
+ return 0;
+
+ pgsz_lg2--;
+
+ PT_WARN_ON(log2_mod(va, pgsz_lg2) != 0);
+ PT_WARN_ON(oalog2_mod(oa, pgsz_lg2) != 0);
+ PT_WARN_ON(va + log2_to_int(pgsz_lg2) - 1 > last_va);
+ PT_WARN_ON(!log2_div_eq(va, va + log2_to_int(pgsz_lg2) - 1, pgsz_lg2));
+ PT_WARN_ON(
+ !oalog2_div_eq(oa, oa + log2_to_int(pgsz_lg2) - 1, pgsz_lg2));
+ return pgsz_lg2;
+}
+
+#define _PT_MAKE_CALL_LEVEL(fn) \
+ static __always_inline int fn(struct pt_range *range, void *arg, \
+ unsigned int level, \
+ struct pt_table_p *table) \
+ { \
+ static_assert(PT_MAX_TOP_LEVEL <= 5); \
+ if (level == 0) \
+ return CONCATENATE(fn, 0)(range, arg, 0, table); \
+ if (level == 1 || PT_MAX_TOP_LEVEL == 1) \
+ return CONCATENATE(fn, 1)(range, arg, 1, table); \
+ if (level == 2 || PT_MAX_TOP_LEVEL == 2) \
+ return CONCATENATE(fn, 2)(range, arg, 2, table); \
+ if (level == 3 || PT_MAX_TOP_LEVEL == 3) \
+ return CONCATENATE(fn, 3)(range, arg, 3, table); \
+ if (level == 4 || PT_MAX_TOP_LEVEL == 4) \
+ return CONCATENATE(fn, 4)(range, arg, 4, table); \
+ return CONCATENATE(fn, 5)(range, arg, 5, table); \
+ }
+
+static inline int __pt_make_level_fn_err(struct pt_range *range, void *arg,
+ unsigned int unused_level,
+ struct pt_table_p *table)
+{
+ static_assert(PT_MAX_TOP_LEVEL <= 5);
+ return -EPROTOTYPE;
+}
+
+#define __PT_MAKE_LEVEL_FN(fn, level, descend_fn, do_fn) \
+ static inline int fn(struct pt_range *range, void *arg, \
+ unsigned int unused_level, \
+ struct pt_table_p *table) \
+ { \
+ return do_fn(range, arg, level, table, descend_fn); \
+ }
+
+/**
+ * PT_MAKE_LEVELS() - Build an unwound walker
+ * @fn: Name of the walker function
+ * @do_fn: Function to call at each level
+ *
+ * This builds a function call tree that can be fully inlined.
+ * The caller must provide a function body in an __always_inline function::
+ *
+ * static __always_inline int do_fn(struct pt_range *range, void *arg,
+ * unsigned int level, struct pt_table_p *table,
+ * pt_level_fn_t descend_fn)
+ *
+ * An inline function will be created for each table level that calls do_fn with
+ * a compile time constant for level and a pointer to the next lower function.
+ * This generates an optimally inlined walk where each of the functions sees a
+ * constant level and can codegen the exact constants/etc for that level.
+ *
+ * Note this can produce a lot of code!
+ */
+#define PT_MAKE_LEVELS(fn, do_fn) \
+ __PT_MAKE_LEVEL_FN(CONCATENATE(fn, 0), 0, __pt_make_level_fn_err, \
+ do_fn); \
+ __PT_MAKE_LEVEL_FN(CONCATENATE(fn, 1), 1, CONCATENATE(fn, 0), do_fn); \
+ __PT_MAKE_LEVEL_FN(CONCATENATE(fn, 2), 2, CONCATENATE(fn, 1), do_fn); \
+ __PT_MAKE_LEVEL_FN(CONCATENATE(fn, 3), 3, CONCATENATE(fn, 2), do_fn); \
+ __PT_MAKE_LEVEL_FN(CONCATENATE(fn, 4), 4, CONCATENATE(fn, 3), do_fn); \
+ __PT_MAKE_LEVEL_FN(CONCATENATE(fn, 5), 5, CONCATENATE(fn, 4), do_fn); \
+ _PT_MAKE_CALL_LEVEL(fn)
+
+#endif
diff --git a/drivers/iommu/generic_pt/pt_log2.h b/drivers/iommu/generic_pt/pt_log2.h
new file mode 100644
index 000000000000..6dbbed119238
--- /dev/null
+++ b/drivers/iommu/generic_pt/pt_log2.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
+ *
+ * Helper macros for working with log2 values
+ *
+ */
+#ifndef __GENERIC_PT_LOG2_H
+#define __GENERIC_PT_LOG2_H
+#include <linux/bitops.h>
+#include <linux/limits.h>
+
+/* Compute a */
+#define log2_to_int_t(type, a_lg2) ((type)(((type)1) << (a_lg2)))
+static_assert(log2_to_int_t(unsigned int, 0) == 1);
+
+/* Compute a - 1 (aka all low bits set) */
+#define log2_to_max_int_t(type, a_lg2) ((type)(log2_to_int_t(type, a_lg2) - 1))
+
+/* Compute a / b */
+#define log2_div_t(type, a, b_lg2) ((type)(((type)a) >> (b_lg2)))
+static_assert(log2_div_t(unsigned int, 4, 2) == 1);
+
+/*
+ * Compute:
+ * a / c == b / c
+ * aka the high bits are equal
+ */
+#define log2_div_eq_t(type, a, b, c_lg2) \
+ (log2_div_t(type, (a) ^ (b), c_lg2) == 0)
+static_assert(log2_div_eq_t(unsigned int, 1, 1, 2));
+
+/* Compute a % b */
+#define log2_mod_t(type, a, b_lg2) \
+ ((type)(((type)a) & log2_to_max_int_t(type, b_lg2)))
+static_assert(log2_mod_t(unsigned int, 1, 2) == 1);
+
+/*
+ * Compute:
+ * a % b == b - 1
+ * aka the low bits are all 1s
+ */
+#define log2_mod_eq_max_t(type, a, b_lg2) \
+ (log2_mod_t(type, a, b_lg2) == log2_to_max_int_t(type, b_lg2))
+static_assert(log2_mod_eq_max_t(unsigned int, 3, 2));
+
+/*
+ * Return a value such that:
+ * a / b == ret / b
+ * ret % b == val
+ * aka set the low bits to val. val must be < b
+ */
+#define log2_set_mod_t(type, a, val, b_lg2) \
+ ((((type)(a)) & (~log2_to_max_int_t(type, b_lg2))) | ((type)(val)))
+static_assert(log2_set_mod_t(unsigned int, 3, 1, 2) == 1);
+
+/* Return a value such that:
+ * a / b == ret / b
+ * ret % b == b - 1
+ * aka set the low bits to all 1s
+ */
+#define log2_set_mod_max_t(type, a, b_lg2) \
+ (((type)(a)) | log2_to_max_int_t(type, b_lg2))
+static_assert(log2_set_mod_max_t(unsigned int, 2, 2) == 3);
+
+/* Compute a * b */
+#define log2_mul_t(type, a, b_lg2) ((type)(((type)a) << (b_lg2)))
+static_assert(log2_mul_t(unsigned int, 2, 2) == 8);
+
+#define _dispatch_sz(type, fn, a) \
+ (sizeof(type) == 4 ? fn##32((u32)a) : fn##64(a))
+
+/*
+ * Return the highest value such that:
+ * fls_t(u32, 0) == 0
+ * fls_t(u3, 1) == 1
+ * a >= log2_to_int(ret - 1)
+ * aka find last set bit
+ */
+static inline unsigned int fls32(u32 a)
+{
+ return fls(a);
+}
+#define fls_t(type, a) _dispatch_sz(type, fls, a)
+
+/*
+ * Return the highest value such that:
+ * ffs_t(u32, 0) == UNDEFINED
+ * ffs_t(u32, 1) == 0
+ * log_mod(a, ret) == 0
+ * aka find first set bit
+ */
+static inline unsigned int __ffs32(u32 a)
+{
+ return __ffs(a);
+}
+#define ffs_t(type, a) _dispatch_sz(type, __ffs, a)
+
+/*
+ * Return the highest value such that:
+ * ffz_t(u32, U32_MAX) == UNDEFINED
+ * ffz_t(u32, 0) == 0
+ * ffz_t(u32, 1) == 1
+ * log_mod(a, ret) == log_to_max_int(ret)
+ * aka find first zero bit
+ */
+static inline unsigned int ffz32(u32 a)
+{
+ return ffz(a);
+}
+static inline unsigned int ffz64(u64 a)
+{
+ if (sizeof(u64) == sizeof(unsigned long))
+ return ffz(a);
+
+ if ((u32)a == U32_MAX)
+ return ffz32(a >> 32) + 32;
+ return ffz32(a);
+}
+#define ffz_t(type, a) _dispatch_sz(type, ffz, a)
+
+#endif
diff --git a/drivers/iommu/intel/Kconfig b/drivers/iommu/intel/Kconfig
index f2f538c70650..5471f814e073 100644
--- a/drivers/iommu/intel/Kconfig
+++ b/drivers/iommu/intel/Kconfig
@@ -13,6 +13,10 @@ config INTEL_IOMMU
bool "Support for Intel IOMMU using DMA Remapping Devices"
depends on PCI_MSI && ACPI && X86
select IOMMU_API
+ select GENERIC_PT
+ select IOMMU_PT
+ select IOMMU_PT_X86_64
+ select IOMMU_PT_VTDSS
select IOMMU_IOVA
select IOMMU_IOPF
select IOMMUFD_DRIVER if IOMMUFD
@@ -66,7 +70,7 @@ config INTEL_IOMMU_DEFAULT_ON
config INTEL_IOMMU_FLOPPY_WA
def_bool y
- depends on X86
+ depends on X86 && BLK_DEV_FD
help
Floppy disk drivers are known to bypass DMA API calls
thereby failing to work when IOMMU is enabled. This
diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c
index e236c7ec221f..4e888867e85c 100644
--- a/drivers/iommu/intel/iommu.c
+++ b/drivers/iommu/intel/iommu.c
@@ -45,16 +45,9 @@
#define DEFAULT_DOMAIN_ADDRESS_WIDTH 57
-#define __DOMAIN_MAX_PFN(gaw) ((((uint64_t)1) << ((gaw) - VTD_PAGE_SHIFT)) - 1)
-#define __DOMAIN_MAX_ADDR(gaw) ((((uint64_t)1) << (gaw)) - 1)
-
-/* We limit DOMAIN_MAX_PFN to fit in an unsigned long, and DOMAIN_MAX_ADDR
- to match. That way, we can use 'unsigned long' for PFNs with impunity. */
-#define DOMAIN_MAX_PFN(gaw) ((unsigned long) min_t(uint64_t, \
- __DOMAIN_MAX_PFN(gaw), (unsigned long)-1))
-#define DOMAIN_MAX_ADDR(gaw) (((uint64_t)__DOMAIN_MAX_PFN(gaw)) << VTD_PAGE_SHIFT)
-
static void __init check_tylersburg_isoch(void);
+static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
+ bool enable);
static int rwbf_quirk;
#define rwbf_required(iommu) (rwbf_quirk || cap_rwbf((iommu)->cap))
@@ -217,7 +210,6 @@ static int disable_igfx_iommu;
#define IDENTMAP_AZALIA 4
const struct iommu_ops intel_iommu_ops;
-static const struct iommu_dirty_ops intel_dirty_ops;
static bool translation_pre_enabled(struct intel_iommu *iommu)
{
@@ -285,13 +277,6 @@ static int __init intel_iommu_setup(char *str)
}
__setup("intel_iommu=", intel_iommu_setup);
-static int domain_pfn_supported(struct dmar_domain *domain, unsigned long pfn)
-{
- int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
-
- return !(addr_width < BITS_PER_LONG && pfn >> addr_width);
-}
-
/*
* Calculate the Supported Adjusted Guest Address Widths of an IOMMU.
* Refer to 11.4.2 of the VT-d spec for the encoding of each bit of
@@ -353,23 +338,6 @@ static bool iommu_paging_structure_coherency(struct intel_iommu *iommu)
ecap_smpwc(iommu->ecap) : ecap_coherent(iommu->ecap);
}
-/* Return the super pagesize bitmap if supported. */
-static unsigned long domain_super_pgsize_bitmap(struct dmar_domain *domain)
-{
- unsigned long bitmap = 0;
-
- /*
- * 1-level super page supports page size of 2MiB, 2-level super page
- * supports page size of both 2MiB and 1GiB.
- */
- if (domain->iommu_superpage == 1)
- bitmap |= SZ_2M;
- else if (domain->iommu_superpage == 2)
- bitmap |= SZ_2M | SZ_1G;
-
- return bitmap;
-}
-
struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
u8 devfn, int alloc)
{
@@ -556,13 +524,6 @@ out:
return iommu;
}
-static void domain_flush_cache(struct dmar_domain *domain,
- void *addr, int size)
-{
- if (!domain->iommu_coherency)
- clflush_cache_range(addr, size);
-}
-
static void free_context_table(struct intel_iommu *iommu)
{
struct context_entry *context;
@@ -707,280 +668,6 @@ pgtable_walk:
}
#endif
-static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
- unsigned long pfn, int *target_level,
- gfp_t gfp)
-{
- struct dma_pte *parent, *pte;
- int level = agaw_to_level(domain->agaw);
- int offset;
-
- if (!domain_pfn_supported(domain, pfn))
- /* Address beyond IOMMU's addressing capabilities. */
- return NULL;
-
- parent = domain->pgd;
-
- while (1) {
- void *tmp_page;
-
- offset = pfn_level_offset(pfn, level);
- pte = &parent[offset];
- if (!*target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
- break;
- if (level == *target_level)
- break;
-
- if (!dma_pte_present(pte)) {
- uint64_t pteval, tmp;
-
- tmp_page = iommu_alloc_pages_node_sz(domain->nid, gfp,
- SZ_4K);
-
- if (!tmp_page)
- return NULL;
-
- domain_flush_cache(domain, tmp_page, VTD_PAGE_SIZE);
- pteval = virt_to_phys(tmp_page) | DMA_PTE_READ |
- DMA_PTE_WRITE;
- if (domain->use_first_level)
- pteval |= DMA_FL_PTE_US | DMA_FL_PTE_ACCESS;
-
- tmp = 0ULL;
- if (!try_cmpxchg64(&pte->val, &tmp, pteval))
- /* Someone else set it while we were thinking; use theirs. */
- iommu_free_pages(tmp_page);
- else
- domain_flush_cache(domain, pte, sizeof(*pte));
- }
- if (level == 1)
- break;
-
- parent = phys_to_virt(dma_pte_addr(pte));
- level--;
- }
-
- if (!*target_level)
- *target_level = level;
-
- return pte;
-}
-
-/* return address's pte at specific level */
-static struct dma_pte *dma_pfn_level_pte(struct dmar_domain *domain,
- unsigned long pfn,
- int level, int *large_page)
-{
- struct dma_pte *parent, *pte;
- int total = agaw_to_level(domain->agaw);
- int offset;
-
- parent = domain->pgd;
- while (level <= total) {
- offset = pfn_level_offset(pfn, total);
- pte = &parent[offset];
- if (level == total)
- return pte;
-
- if (!dma_pte_present(pte)) {
- *large_page = total;
- break;
- }
-
- if (dma_pte_superpage(pte)) {
- *large_page = total;
- return pte;
- }
-
- parent = phys_to_virt(dma_pte_addr(pte));
- total--;
- }
- return NULL;
-}
-
-/* clear last level pte, a tlb flush should be followed */
-static void dma_pte_clear_range(struct dmar_domain *domain,
- unsigned long start_pfn,
- unsigned long last_pfn)
-{
- unsigned int large_page;
- struct dma_pte *first_pte, *pte;
-
- if (WARN_ON(!domain_pfn_supported(domain, last_pfn)) ||
- WARN_ON(start_pfn > last_pfn))
- return;
-
- /* we don't need lock here; nobody else touches the iova range */
- do {
- large_page = 1;
- first_pte = pte = dma_pfn_level_pte(domain, start_pfn, 1, &large_page);
- if (!pte) {
- start_pfn = align_to_level(start_pfn + 1, large_page + 1);
- continue;
- }
- do {
- dma_clear_pte(pte);
- start_pfn += lvl_to_nr_pages(large_page);
- pte++;
- } while (start_pfn <= last_pfn && !first_pte_in_page(pte));
-
- domain_flush_cache(domain, first_pte,
- (void *)pte - (void *)first_pte);
-
- } while (start_pfn && start_pfn <= last_pfn);
-}
-
-static void dma_pte_free_level(struct dmar_domain *domain, int level,
- int retain_level, struct dma_pte *pte,
- unsigned long pfn, unsigned long start_pfn,
- unsigned long last_pfn)
-{
- pfn = max(start_pfn, pfn);
- pte = &pte[pfn_level_offset(pfn, level)];
-
- do {
- unsigned long level_pfn;
- struct dma_pte *level_pte;
-
- if (!dma_pte_present(pte) || dma_pte_superpage(pte))
- goto next;
-
- level_pfn = pfn & level_mask(level);
- level_pte = phys_to_virt(dma_pte_addr(pte));
-
- if (level > 2) {
- dma_pte_free_level(domain, level - 1, retain_level,
- level_pte, level_pfn, start_pfn,
- last_pfn);
- }
-
- /*
- * Free the page table if we're below the level we want to
- * retain and the range covers the entire table.
- */
- if (level < retain_level && !(start_pfn > level_pfn ||
- last_pfn < level_pfn + level_size(level) - 1)) {
- dma_clear_pte(pte);
- domain_flush_cache(domain, pte, sizeof(*pte));
- iommu_free_pages(level_pte);
- }
-next:
- pfn += level_size(level);
- } while (!first_pte_in_page(++pte) && pfn <= last_pfn);
-}
-
-/*
- * clear last level (leaf) ptes and free page table pages below the
- * level we wish to keep intact.
- */
-static void dma_pte_free_pagetable(struct dmar_domain *domain,
- unsigned long start_pfn,
- unsigned long last_pfn,
- int retain_level)
-{
- dma_pte_clear_range(domain, start_pfn, last_pfn);
-
- /* We don't need lock here; nobody else touches the iova range */
- dma_pte_free_level(domain, agaw_to_level(domain->agaw), retain_level,
- domain->pgd, 0, start_pfn, last_pfn);
-
- /* free pgd */
- if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
- iommu_free_pages(domain->pgd);
- domain->pgd = NULL;
- }
-}
-
-/* When a page at a given level is being unlinked from its parent, we don't
- need to *modify* it at all. All we need to do is make a list of all the
- pages which can be freed just as soon as we've flushed the IOTLB and we
- know the hardware page-walk will no longer touch them.
- The 'pte' argument is the *parent* PTE, pointing to the page that is to
- be freed. */
-static void dma_pte_list_pagetables(struct dmar_domain *domain,
- int level, struct dma_pte *parent_pte,
- struct iommu_pages_list *freelist)
-{
- struct dma_pte *pte = phys_to_virt(dma_pte_addr(parent_pte));
-
- iommu_pages_list_add(freelist, pte);
-
- if (level == 1)
- return;
-
- do {
- if (dma_pte_present(pte) && !dma_pte_superpage(pte))
- dma_pte_list_pagetables(domain, level - 1, pte, freelist);
- pte++;
- } while (!first_pte_in_page(pte));
-}
-
-static void dma_pte_clear_level(struct dmar_domain *domain, int level,
- struct dma_pte *pte, unsigned long pfn,
- unsigned long start_pfn, unsigned long last_pfn,
- struct iommu_pages_list *freelist)
-{
- struct dma_pte *first_pte = NULL, *last_pte = NULL;
-
- pfn = max(start_pfn, pfn);
- pte = &pte[pfn_level_offset(pfn, level)];
-
- do {
- unsigned long level_pfn = pfn & level_mask(level);
-
- if (!dma_pte_present(pte))
- goto next;
-
- /* If range covers entire pagetable, free it */
- if (start_pfn <= level_pfn &&
- last_pfn >= level_pfn + level_size(level) - 1) {
- /* These suborbinate page tables are going away entirely. Don't
- bother to clear them; we're just going to *free* them. */
- if (level > 1 && !dma_pte_superpage(pte))
- dma_pte_list_pagetables(domain, level - 1, pte, freelist);
-
- dma_clear_pte(pte);
- if (!first_pte)
- first_pte = pte;
- last_pte = pte;
- } else if (level > 1) {
- /* Recurse down into a level that isn't *entirely* obsolete */
- dma_pte_clear_level(domain, level - 1,
- phys_to_virt(dma_pte_addr(pte)),
- level_pfn, start_pfn, last_pfn,
- freelist);
- }
-next:
- pfn = level_pfn + level_size(level);
- } while (!first_pte_in_page(++pte) && pfn <= last_pfn);
-
- if (first_pte)
- domain_flush_cache(domain, first_pte,
- (void *)++last_pte - (void *)first_pte);
-}
-
-/* We can't just free the pages because the IOMMU may still be walking
- the page tables, and may have cached the intermediate levels. The
- pages can only be freed after the IOTLB flush has been done. */
-static void domain_unmap(struct dmar_domain *domain, unsigned long start_pfn,
- unsigned long last_pfn,
- struct iommu_pages_list *freelist)
-{
- if (WARN_ON(!domain_pfn_supported(domain, last_pfn)) ||
- WARN_ON(start_pfn > last_pfn))
- return;
-
- /* we don't need lock here; nobody else touches the iova range */
- dma_pte_clear_level(domain, agaw_to_level(domain->agaw),
- domain->pgd, 0, start_pfn, last_pfn, freelist);
-
- /* free pgd */
- if (start_pfn == 0 && last_pfn == DOMAIN_MAX_PFN(domain->gaw)) {
- iommu_pages_list_add(freelist, domain->pgd);
- domain->pgd = NULL;
- }
-}
-
/* iommu handling */
static int iommu_alloc_root_entry(struct intel_iommu *iommu)
{
@@ -1460,13 +1147,15 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
domain_lookup_dev_info(domain, iommu, bus, devfn);
u16 did = domain_id_iommu(domain, iommu);
int translation = CONTEXT_TT_MULTI_LEVEL;
- struct dma_pte *pgd = domain->pgd;
+ struct pt_iommu_vtdss_hw_info pt_info;
struct context_entry *context;
int ret;
if (WARN_ON(!intel_domain_is_ss_paging(domain)))
return -EINVAL;
+ pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info);
+
pr_debug("Set context mapping for %02x:%02x.%d\n",
bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
@@ -1489,8 +1178,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain,
else
translation = CONTEXT_TT_MULTI_LEVEL;
- context_set_address_root(context, virt_to_phys(pgd));
- context_set_address_width(context, domain->agaw);
+ context_set_address_root(context, pt_info.ssptptr);
+ context_set_address_width(context, pt_info.aw);
context_set_translation_type(context, translation);
context_set_fault_enable(context);
context_set_present(context);
@@ -1537,177 +1226,6 @@ domain_context_mapping(struct dmar_domain *domain, struct device *dev)
return 0;
}
-/* Return largest possible superpage level for a given mapping */
-static int hardware_largepage_caps(struct dmar_domain *domain, unsigned long iov_pfn,
- unsigned long phy_pfn, unsigned long pages)
-{
- int support, level = 1;
- unsigned long pfnmerge;
-
- support = domain->iommu_superpage;
-
- /* To use a large page, the virtual *and* physical addresses
- must be aligned to 2MiB/1GiB/etc. Lower bits set in either
- of them will mean we have to use smaller pages. So just
- merge them and check both at once. */
- pfnmerge = iov_pfn | phy_pfn;
-
- while (support && !(pfnmerge & ~VTD_STRIDE_MASK)) {
- pages >>= VTD_STRIDE_SHIFT;
- if (!pages)
- break;
- pfnmerge >>= VTD_STRIDE_SHIFT;
- level++;
- support--;
- }
- return level;
-}
-
-/*
- * Ensure that old small page tables are removed to make room for superpage(s).
- * We're going to add new large pages, so make sure we don't remove their parent
- * tables. The IOTLB/devTLBs should be flushed if any PDE/PTEs are cleared.
- */
-static void switch_to_super_page(struct dmar_domain *domain,
- unsigned long start_pfn,
- unsigned long end_pfn, int level)
-{
- unsigned long lvl_pages = lvl_to_nr_pages(level);
- struct dma_pte *pte = NULL;
-
- if (WARN_ON(!IS_ALIGNED(start_pfn, lvl_pages) ||
- !IS_ALIGNED(end_pfn + 1, lvl_pages)))
- return;
-
- while (start_pfn <= end_pfn) {
- if (!pte)
- pte = pfn_to_dma_pte(domain, start_pfn, &level,
- GFP_ATOMIC);
-
- if (dma_pte_present(pte)) {
- dma_pte_free_pagetable(domain, start_pfn,
- start_pfn + lvl_pages - 1,
- level + 1);
-
- cache_tag_flush_range(domain, start_pfn << VTD_PAGE_SHIFT,
- end_pfn << VTD_PAGE_SHIFT, 0);
- }
-
- pte++;
- start_pfn += lvl_pages;
- if (first_pte_in_page(pte))
- pte = NULL;
- }
-}
-
-static int
-__domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
- unsigned long phys_pfn, unsigned long nr_pages, int prot,
- gfp_t gfp)
-{
- struct dma_pte *first_pte = NULL, *pte = NULL;
- unsigned int largepage_lvl = 0;
- unsigned long lvl_pages = 0;
- phys_addr_t pteval;
- u64 attr;
-
- if (unlikely(!domain_pfn_supported(domain, iov_pfn + nr_pages - 1)))
- return -EINVAL;
-
- if ((prot & (DMA_PTE_READ|DMA_PTE_WRITE)) == 0)
- return -EINVAL;
-
- if (!(prot & DMA_PTE_WRITE) && domain->nested_parent) {
- pr_err_ratelimited("Read-only mapping is disallowed on the domain which serves as the parent in a nested configuration, due to HW errata (ERRATA_772415_SPR17)\n");
- return -EINVAL;
- }
-
- attr = prot & (DMA_PTE_READ | DMA_PTE_WRITE | DMA_PTE_SNP);
- if (domain->use_first_level) {
- attr |= DMA_FL_PTE_PRESENT | DMA_FL_PTE_US | DMA_FL_PTE_ACCESS;
- if (prot & DMA_PTE_WRITE)
- attr |= DMA_FL_PTE_DIRTY;
- }
-
- domain->has_mappings = true;
-
- pteval = ((phys_addr_t)phys_pfn << VTD_PAGE_SHIFT) | attr;
-
- while (nr_pages > 0) {
- uint64_t tmp;
-
- if (!pte) {
- largepage_lvl = hardware_largepage_caps(domain, iov_pfn,
- phys_pfn, nr_pages);
-
- pte = pfn_to_dma_pte(domain, iov_pfn, &largepage_lvl,
- gfp);
- if (!pte)
- return -ENOMEM;
- first_pte = pte;
-
- lvl_pages = lvl_to_nr_pages(largepage_lvl);
-
- /* It is large page*/
- if (largepage_lvl > 1) {
- unsigned long end_pfn;
- unsigned long pages_to_remove;
-
- pteval |= DMA_PTE_LARGE_PAGE;
- pages_to_remove = min_t(unsigned long,
- round_down(nr_pages, lvl_pages),
- nr_pte_to_next_page(pte) * lvl_pages);
- end_pfn = iov_pfn + pages_to_remove - 1;
- switch_to_super_page(domain, iov_pfn, end_pfn, largepage_lvl);
- } else {
- pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE;
- }
-
- }
- /* We don't need lock here, nobody else
- * touches the iova range
- */
- tmp = 0ULL;
- if (!try_cmpxchg64_local(&pte->val, &tmp, pteval)) {
- static int dumps = 5;
- pr_crit("ERROR: DMA PTE for vPFN 0x%lx already set (to %llx not %llx)\n",
- iov_pfn, tmp, (unsigned long long)pteval);
- if (dumps) {
- dumps--;
- debug_dma_dump_mappings(NULL);
- }
- WARN_ON(1);
- }
-
- nr_pages -= lvl_pages;
- iov_pfn += lvl_pages;
- phys_pfn += lvl_pages;
- pteval += lvl_pages * VTD_PAGE_SIZE;
-
- /* If the next PTE would be the first in a new page, then we
- * need to flush the cache on the entries we've just written.
- * And then we'll need to recalculate 'pte', so clear it and
- * let it get set again in the if (!pte) block above.
- *
- * If we're done (!nr_pages) we need to flush the cache too.
- *
- * Also if we've been setting superpages, we may need to
- * recalculate 'pte' and switch back to smaller pages for the
- * end of the mapping, if the trailing size is not enough to
- * use another superpage (i.e. nr_pages < lvl_pages).
- */
- pte++;
- if (!nr_pages || first_pte_in_page(pte) ||
- (largepage_lvl > 1 && nr_pages < lvl_pages)) {
- domain_flush_cache(domain, first_pte,
- (void *)pte - (void *)first_pte);
- pte = NULL;
- }
- }
-
- return 0;
-}
-
static void domain_context_clear_one(struct device_domain_info *info, u8 bus, u8 devfn)
{
struct intel_iommu *iommu = info->iommu;
@@ -1769,22 +1287,26 @@ static int domain_setup_first_level(struct intel_iommu *iommu,
struct device *dev,
u32 pasid, struct iommu_domain *old)
{
- struct dma_pte *pgd = domain->pgd;
- int level, flags = 0;
+ struct pt_iommu_x86_64_hw_info pt_info;
+ unsigned int flags = 0;
- level = agaw_to_level(domain->agaw);
- if (level != 4 && level != 5)
+ pt_iommu_x86_64_hw_info(&domain->fspt, &pt_info);
+ if (WARN_ON(pt_info.levels != 4 && pt_info.levels != 5))
return -EINVAL;
- if (level == 5)
+ if (pt_info.levels == 5)
flags |= PASID_FLAG_FL5LP;
if (domain->force_snooping)
flags |= PASID_FLAG_PAGE_SNOOP;
+ if (!(domain->fspt.x86_64_pt.common.features &
+ BIT(PT_FEAT_DMA_INCOHERENT)))
+ flags |= PASID_FLAG_PWSNP;
+
return __domain_setup_first_level(iommu, dev, pasid,
domain_id_iommu(domain, iommu),
- __pa(pgd), flags, old);
+ pt_info.gcr3_pt, flags, old);
}
static int dmar_domain_attach_device(struct dmar_domain *domain,
@@ -3230,7 +2752,8 @@ void device_block_translation(struct device *dev)
}
static int blocking_domain_attach_dev(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
@@ -3251,23 +2774,9 @@ static struct iommu_domain blocking_domain = {
}
};
-static int iommu_superpage_capability(struct intel_iommu *iommu, bool first_stage)
-{
- if (!intel_iommu_superpage)
- return 0;
-
- if (first_stage)
- return cap_fl1gp_support(iommu->cap) ? 2 : 1;
-
- return fls(cap_super_page_val(iommu->cap));
-}
-
-static struct dmar_domain *paging_domain_alloc(struct device *dev, bool first_stage)
+static struct dmar_domain *paging_domain_alloc(void)
{
- struct device_domain_info *info = dev_iommu_priv_get(dev);
- struct intel_iommu *iommu = info->iommu;
struct dmar_domain *domain;
- int addr_width;
domain = kzalloc(sizeof(*domain), GFP_KERNEL);
if (!domain)
@@ -3282,56 +2791,38 @@ static struct dmar_domain *paging_domain_alloc(struct device *dev, bool first_st
INIT_LIST_HEAD(&domain->s1_domains);
spin_lock_init(&domain->s1_lock);
- domain->nid = dev_to_node(dev);
- domain->use_first_level = first_stage;
-
- domain->domain.type = IOMMU_DOMAIN_UNMANAGED;
-
- /* calculate the address width */
- addr_width = agaw_to_width(iommu->agaw);
- if (addr_width > cap_mgaw(iommu->cap))
- addr_width = cap_mgaw(iommu->cap);
- domain->gaw = addr_width;
- domain->agaw = iommu->agaw;
- domain->max_addr = __DOMAIN_MAX_ADDR(addr_width);
-
- /* iommu memory access coherency */
- domain->iommu_coherency = iommu_paging_structure_coherency(iommu);
+ return domain;
+}
- /* pagesize bitmap */
- domain->domain.pgsize_bitmap = SZ_4K;
- domain->iommu_superpage = iommu_superpage_capability(iommu, first_stage);
- domain->domain.pgsize_bitmap |= domain_super_pgsize_bitmap(domain);
+static unsigned int compute_vasz_lg2_fs(struct intel_iommu *iommu,
+ unsigned int *top_level)
+{
+ unsigned int mgaw = cap_mgaw(iommu->cap);
/*
- * IOVA aperture: First-level translation restricts the input-address
- * to a canonical address (i.e., address bits 63:N have the same value
- * as address bit [N-1], where N is 48-bits with 4-level paging and
- * 57-bits with 5-level paging). Hence, skip bit [N-1].
+ * Spec 3.6 First-Stage Translation:
+ *
+ * Software must limit addresses to less than the minimum of MGAW
+ * and the lower canonical address width implied by FSPM (i.e.,
+ * 47-bit when FSPM is 4-level and 56-bit when FSPM is 5-level).
*/
- domain->domain.geometry.force_aperture = true;
- domain->domain.geometry.aperture_start = 0;
- if (first_stage)
- domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw - 1);
- else
- domain->domain.geometry.aperture_end = __DOMAIN_MAX_ADDR(domain->gaw);
-
- /* always allocate the top pgd */
- domain->pgd = iommu_alloc_pages_node_sz(domain->nid, GFP_KERNEL, SZ_4K);
- if (!domain->pgd) {
- kfree(domain);
- return ERR_PTR(-ENOMEM);
+ if (mgaw > 48 && cap_fl5lp_support(iommu->cap)) {
+ *top_level = 4;
+ return min(57, mgaw);
}
- domain_flush_cache(domain, domain->pgd, PAGE_SIZE);
- return domain;
+ /* Four level is always supported */
+ *top_level = 3;
+ return min(48, mgaw);
}
static struct iommu_domain *
intel_iommu_domain_alloc_first_stage(struct device *dev,
struct intel_iommu *iommu, u32 flags)
{
+ struct pt_iommu_x86_64_cfg cfg = {};
struct dmar_domain *dmar_domain;
+ int ret;
if (flags & ~IOMMU_HWPT_ALLOC_PASID)
return ERR_PTR(-EOPNOTSUPP);
@@ -3340,10 +2831,20 @@ intel_iommu_domain_alloc_first_stage(struct device *dev,
if (!sm_supported(iommu) || !ecap_flts(iommu->ecap))
return ERR_PTR(-EOPNOTSUPP);
- dmar_domain = paging_domain_alloc(dev, true);
+ dmar_domain = paging_domain_alloc();
if (IS_ERR(dmar_domain))
return ERR_CAST(dmar_domain);
+ cfg.common.hw_max_vasz_lg2 =
+ compute_vasz_lg2_fs(iommu, &cfg.top_level);
+ cfg.common.hw_max_oasz_lg2 = 52;
+ cfg.common.features = BIT(PT_FEAT_SIGN_EXTEND) |
+ BIT(PT_FEAT_FLUSH_RANGE);
+ /* First stage always uses scalable mode */
+ if (!ecap_smpwc(iommu->ecap))
+ cfg.common.features |= BIT(PT_FEAT_DMA_INCOHERENT);
+ dmar_domain->iommu.iommu_device = dev;
+ dmar_domain->iommu.nid = dev_to_node(dev);
dmar_domain->domain.ops = &intel_fs_paging_domain_ops;
/*
* iotlb sync for map is only needed for legacy implementations that
@@ -3353,14 +2854,58 @@ intel_iommu_domain_alloc_first_stage(struct device *dev,
if (rwbf_required(iommu))
dmar_domain->iotlb_sync_map = true;
+ ret = pt_iommu_x86_64_init(&dmar_domain->fspt, &cfg, GFP_KERNEL);
+ if (ret) {
+ kfree(dmar_domain);
+ return ERR_PTR(ret);
+ }
+
+ if (!cap_fl1gp_support(iommu->cap))
+ dmar_domain->domain.pgsize_bitmap &= ~(u64)SZ_1G;
+ if (!intel_iommu_superpage)
+ dmar_domain->domain.pgsize_bitmap = SZ_4K;
+
return &dmar_domain->domain;
}
+static unsigned int compute_vasz_lg2_ss(struct intel_iommu *iommu,
+ unsigned int *top_level)
+{
+ unsigned int sagaw = cap_sagaw(iommu->cap);
+ unsigned int mgaw = cap_mgaw(iommu->cap);
+
+ /*
+ * Find the largest table size that both the mgaw and sagaw support.
+ * This sets the valid range of IOVA and the top starting level.
+ * Some HW may only support a 4 or 5 level walk but must limit IOVA to
+ * 3 levels.
+ */
+ if (mgaw > 48 && sagaw >= BIT(3)) {
+ *top_level = 4;
+ return min(57, mgaw);
+ } else if (mgaw > 39 && sagaw >= BIT(2)) {
+ *top_level = 3 + ffs(sagaw >> 3);
+ return min(48, mgaw);
+ } else if (mgaw > 30 && sagaw >= BIT(1)) {
+ *top_level = 2 + ffs(sagaw >> 2);
+ return min(39, mgaw);
+ }
+ return 0;
+}
+
+static const struct iommu_dirty_ops intel_second_stage_dirty_ops = {
+ IOMMU_PT_DIRTY_OPS(vtdss),
+ .set_dirty_tracking = intel_iommu_set_dirty_tracking,
+};
+
static struct iommu_domain *
intel_iommu_domain_alloc_second_stage(struct device *dev,
struct intel_iommu *iommu, u32 flags)
{
+ struct pt_iommu_vtdss_cfg cfg = {};
struct dmar_domain *dmar_domain;
+ unsigned int sslps;
+ int ret;
if (flags &
(~(IOMMU_HWPT_ALLOC_NEST_PARENT | IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
@@ -3377,15 +2922,46 @@ intel_iommu_domain_alloc_second_stage(struct device *dev,
if (sm_supported(iommu) && !ecap_slts(iommu->ecap))
return ERR_PTR(-EOPNOTSUPP);
- dmar_domain = paging_domain_alloc(dev, false);
+ dmar_domain = paging_domain_alloc();
if (IS_ERR(dmar_domain))
return ERR_CAST(dmar_domain);
+ cfg.common.hw_max_vasz_lg2 = compute_vasz_lg2_ss(iommu, &cfg.top_level);
+ cfg.common.hw_max_oasz_lg2 = 52;
+ cfg.common.features = BIT(PT_FEAT_FLUSH_RANGE);
+
+ /*
+ * Read-only mapping is disallowed on the domain which serves as the
+ * parent in a nested configuration, due to HW errata
+ * (ERRATA_772415_SPR17)
+ */
+ if (flags & IOMMU_HWPT_ALLOC_NEST_PARENT)
+ cfg.common.features |= BIT(PT_FEAT_VTDSS_FORCE_WRITEABLE);
+
+ if (!iommu_paging_structure_coherency(iommu))
+ cfg.common.features |= BIT(PT_FEAT_DMA_INCOHERENT);
+ dmar_domain->iommu.iommu_device = dev;
+ dmar_domain->iommu.nid = dev_to_node(dev);
dmar_domain->domain.ops = &intel_ss_paging_domain_ops;
dmar_domain->nested_parent = flags & IOMMU_HWPT_ALLOC_NEST_PARENT;
if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING)
- dmar_domain->domain.dirty_ops = &intel_dirty_ops;
+ dmar_domain->domain.dirty_ops = &intel_second_stage_dirty_ops;
+
+ ret = pt_iommu_vtdss_init(&dmar_domain->sspt, &cfg, GFP_KERNEL);
+ if (ret) {
+ kfree(dmar_domain);
+ return ERR_PTR(ret);
+ }
+
+ /* Adjust the supported page sizes to HW capability */
+ sslps = cap_super_page_val(iommu->cap);
+ if (!(sslps & BIT(0)))
+ dmar_domain->domain.pgsize_bitmap &= ~(u64)SZ_2M;
+ if (!(sslps & BIT(1)))
+ dmar_domain->domain.pgsize_bitmap &= ~(u64)SZ_1G;
+ if (!intel_iommu_superpage)
+ dmar_domain->domain.pgsize_bitmap = SZ_4K;
/*
* Besides the internal write buffer flush, the caching mode used for
@@ -3427,14 +3003,7 @@ static void intel_iommu_domain_free(struct iommu_domain *domain)
if (WARN_ON(!list_empty(&dmar_domain->devices)))
return;
- if (dmar_domain->pgd) {
- struct iommu_pages_list freelist =
- IOMMU_PAGES_LIST_INIT(freelist);
-
- domain_unmap(dmar_domain, 0, DOMAIN_MAX_PFN(dmar_domain->gaw),
- &freelist);
- iommu_put_pages_list(&freelist);
- }
+ pt_iommu_deinit(&dmar_domain->iommu);
kfree(dmar_domain->qi_batch);
kfree(dmar_domain);
@@ -3451,6 +3020,16 @@ static int paging_domain_compatible_first_stage(struct dmar_domain *dmar_domain,
if (!sm_supported(iommu) || !ecap_flts(iommu->ecap))
return -EINVAL;
+ if (!ecap_smpwc(iommu->ecap) &&
+ !(dmar_domain->fspt.x86_64_pt.common.features &
+ BIT(PT_FEAT_DMA_INCOHERENT)))
+ return -EINVAL;
+
+ /* Supports the number of table levels */
+ if (!cap_fl5lp_support(iommu->cap) &&
+ dmar_domain->fspt.x86_64_pt.common.max_vasz_lg2 > 48)
+ return -EINVAL;
+
/* Same page size support */
if (!cap_fl1gp_support(iommu->cap) &&
(dmar_domain->domain.pgsize_bitmap & SZ_1G))
@@ -3467,7 +3046,11 @@ static int
paging_domain_compatible_second_stage(struct dmar_domain *dmar_domain,
struct intel_iommu *iommu)
{
+ unsigned int vasz_lg2 = dmar_domain->sspt.vtdss_pt.common.max_vasz_lg2;
unsigned int sslps = cap_super_page_val(iommu->cap);
+ struct pt_iommu_vtdss_hw_info pt_info;
+
+ pt_iommu_vtdss_hw_info(&dmar_domain->sspt, &pt_info);
if (dmar_domain->domain.dirty_ops && !ssads_supported(iommu))
return -EINVAL;
@@ -3478,6 +3061,19 @@ paging_domain_compatible_second_stage(struct dmar_domain *dmar_domain,
if (sm_supported(iommu) && !ecap_slts(iommu->ecap))
return -EINVAL;
+ if (!iommu_paging_structure_coherency(iommu) &&
+ !(dmar_domain->sspt.vtdss_pt.common.features &
+ BIT(PT_FEAT_DMA_INCOHERENT)))
+ return -EINVAL;
+
+ /* Address width falls within the capability */
+ if (cap_mgaw(iommu->cap) < vasz_lg2)
+ return -EINVAL;
+
+ /* Page table level is supported. */
+ if (!(cap_sagaw(iommu->cap) & BIT(pt_info.aw)))
+ return -EINVAL;
+
/* Same page size support */
if (!(sslps & BIT(0)) && (dmar_domain->domain.pgsize_bitmap & SZ_2M))
return -EINVAL;
@@ -3489,6 +3085,14 @@ paging_domain_compatible_second_stage(struct dmar_domain *dmar_domain,
!dmar_domain->iotlb_sync_map)
return -EINVAL;
+ /*
+ * FIXME this is locked wrong, it needs to be under the
+ * dmar_domain->lock
+ */
+ if ((dmar_domain->sspt.vtdss_pt.common.features &
+ BIT(PT_FEAT_VTDSS_FORCE_COHERENCE)) &&
+ !ecap_sc_support(iommu->ecap))
+ return -EINVAL;
return 0;
}
@@ -3498,7 +3102,6 @@ int paging_domain_compatible(struct iommu_domain *domain, struct device *dev)
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
struct intel_iommu *iommu = info->iommu;
int ret = -EINVAL;
- int addr_width;
if (intel_domain_is_fs_paging(dmar_domain))
ret = paging_domain_compatible_first_stage(dmar_domain, iommu);
@@ -3509,26 +3112,6 @@ int paging_domain_compatible(struct iommu_domain *domain, struct device *dev)
if (ret)
return ret;
- /*
- * FIXME this is locked wrong, it needs to be under the
- * dmar_domain->lock
- */
- if (dmar_domain->force_snooping && !ecap_sc_support(iommu->ecap))
- return -EINVAL;
-
- if (dmar_domain->iommu_coherency !=
- iommu_paging_structure_coherency(iommu))
- return -EINVAL;
-
-
- /* check if this iommu agaw is sufficient for max mapped address */
- addr_width = agaw_to_width(iommu->agaw);
- if (addr_width > cap_mgaw(iommu->cap))
- addr_width = cap_mgaw(iommu->cap);
-
- if (dmar_domain->gaw > addr_width || dmar_domain->agaw > iommu->agaw)
- return -EINVAL;
-
if (sm_supported(iommu) && !dev_is_real_dma_subdevice(dev) &&
context_copied(iommu, info->bus, info->devfn))
return intel_pasid_setup_sm_context(dev);
@@ -3537,7 +3120,8 @@ int paging_domain_compatible(struct iommu_domain *domain, struct device *dev)
}
static int intel_iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
int ret;
@@ -3558,110 +3142,6 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
return ret;
}
-static int intel_iommu_map(struct iommu_domain *domain,
- unsigned long iova, phys_addr_t hpa,
- size_t size, int iommu_prot, gfp_t gfp)
-{
- struct dmar_domain *dmar_domain = to_dmar_domain(domain);
- u64 max_addr;
- int prot = 0;
-
- if (iommu_prot & IOMMU_READ)
- prot |= DMA_PTE_READ;
- if (iommu_prot & IOMMU_WRITE)
- prot |= DMA_PTE_WRITE;
- if (dmar_domain->set_pte_snp)
- prot |= DMA_PTE_SNP;
-
- max_addr = iova + size;
- if (dmar_domain->max_addr < max_addr) {
- u64 end;
-
- /* check if minimum agaw is sufficient for mapped address */
- end = __DOMAIN_MAX_ADDR(dmar_domain->gaw) + 1;
- if (end < max_addr) {
- pr_err("%s: iommu width (%d) is not "
- "sufficient for the mapped address (%llx)\n",
- __func__, dmar_domain->gaw, max_addr);
- return -EFAULT;
- }
- dmar_domain->max_addr = max_addr;
- }
- /* Round up size to next multiple of PAGE_SIZE, if it and
- the low bits of hpa would take us onto the next page */
- size = aligned_nrpages(hpa, size);
- return __domain_mapping(dmar_domain, iova >> VTD_PAGE_SHIFT,
- hpa >> VTD_PAGE_SHIFT, size, prot, gfp);
-}
-
-static int intel_iommu_map_pages(struct iommu_domain *domain,
- unsigned long iova, phys_addr_t paddr,
- size_t pgsize, size_t pgcount,
- int prot, gfp_t gfp, size_t *mapped)
-{
- unsigned long pgshift = __ffs(pgsize);
- size_t size = pgcount << pgshift;
- int ret;
-
- if (pgsize != SZ_4K && pgsize != SZ_2M && pgsize != SZ_1G)
- return -EINVAL;
-
- if (!IS_ALIGNED(iova | paddr, pgsize))
- return -EINVAL;
-
- ret = intel_iommu_map(domain, iova, paddr, size, prot, gfp);
- if (!ret && mapped)
- *mapped = size;
-
- return ret;
-}
-
-static size_t intel_iommu_unmap(struct iommu_domain *domain,
- unsigned long iova, size_t size,
- struct iommu_iotlb_gather *gather)
-{
- struct dmar_domain *dmar_domain = to_dmar_domain(domain);
- unsigned long start_pfn, last_pfn;
- int level = 0;
-
- /* Cope with horrid API which requires us to unmap more than the
- size argument if it happens to be a large-page mapping. */
- if (unlikely(!pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT,
- &level, GFP_ATOMIC)))
- return 0;
-
- if (size < VTD_PAGE_SIZE << level_to_offset_bits(level))
- size = VTD_PAGE_SIZE << level_to_offset_bits(level);
-
- start_pfn = iova >> VTD_PAGE_SHIFT;
- last_pfn = (iova + size - 1) >> VTD_PAGE_SHIFT;
-
- domain_unmap(dmar_domain, start_pfn, last_pfn, &gather->freelist);
-
- if (dmar_domain->max_addr == iova + size)
- dmar_domain->max_addr = iova;
-
- /*
- * We do not use page-selective IOTLB invalidation in flush queue,
- * so there is no need to track page and sync iotlb.
- */
- if (!iommu_iotlb_gather_queued(gather))
- iommu_iotlb_gather_add_page(domain, gather, iova, size);
-
- return size;
-}
-
-static size_t intel_iommu_unmap_pages(struct iommu_domain *domain,
- unsigned long iova,
- size_t pgsize, size_t pgcount,
- struct iommu_iotlb_gather *gather)
-{
- unsigned long pgshift = __ffs(pgsize);
- size_t size = pgcount << pgshift;
-
- return intel_iommu_unmap(domain, iova, size, gather);
-}
-
static void intel_iommu_tlb_sync(struct iommu_domain *domain,
struct iommu_iotlb_gather *gather)
{
@@ -3671,24 +3151,6 @@ static void intel_iommu_tlb_sync(struct iommu_domain *domain,
iommu_put_pages_list(&gather->freelist);
}
-static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
- dma_addr_t iova)
-{
- struct dmar_domain *dmar_domain = to_dmar_domain(domain);
- struct dma_pte *pte;
- int level = 0;
- u64 phys = 0;
-
- pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level,
- GFP_ATOMIC);
- if (pte && dma_pte_present(pte))
- phys = dma_pte_addr(pte) +
- (iova & (BIT_MASK(level_to_offset_bits(level) +
- VTD_PAGE_SHIFT) - 1));
-
- return phys;
-}
-
static bool domain_support_force_snooping(struct dmar_domain *domain)
{
struct device_domain_info *info;
@@ -3730,15 +3192,15 @@ static bool intel_iommu_enforce_cache_coherency_ss(struct iommu_domain *domain)
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
guard(spinlock_irqsave)(&dmar_domain->lock);
- if (!domain_support_force_snooping(dmar_domain) ||
- dmar_domain->has_mappings)
+ if (!domain_support_force_snooping(dmar_domain))
return false;
/*
* Second level page table supports per-PTE snoop control. The
* iommu_map() interface will handle this by setting SNP bit.
*/
- dmar_domain->set_pte_snp = true;
+ dmar_domain->sspt.vtdss_pt.common.features |=
+ BIT(PT_FEAT_VTDSS_FORCE_COHERENCE);
dmar_domain->force_snooping = true;
return true;
}
@@ -4302,49 +3764,6 @@ err_unwind:
return ret;
}
-static int intel_iommu_read_and_clear_dirty(struct iommu_domain *domain,
- unsigned long iova, size_t size,
- unsigned long flags,
- struct iommu_dirty_bitmap *dirty)
-{
- struct dmar_domain *dmar_domain = to_dmar_domain(domain);
- unsigned long end = iova + size - 1;
- unsigned long pgsize;
-
- /*
- * IOMMUFD core calls into a dirty tracking disabled domain without an
- * IOVA bitmap set in order to clean dirty bits in all PTEs that might
- * have occurred when we stopped dirty tracking. This ensures that we
- * never inherit dirtied bits from a previous cycle.
- */
- if (!dmar_domain->dirty_tracking && dirty->bitmap)
- return -EINVAL;
-
- do {
- struct dma_pte *pte;
- int lvl = 0;
-
- pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &lvl,
- GFP_ATOMIC);
- pgsize = level_size(lvl) << VTD_PAGE_SHIFT;
- if (!pte || !dma_pte_present(pte)) {
- iova += pgsize;
- continue;
- }
-
- if (dma_sl_pte_test_and_clear_dirty(pte, flags))
- iommu_dirty_bitmap_record(dirty, iova, pgsize);
- iova += pgsize;
- } while (iova < end);
-
- return 0;
-}
-
-static const struct iommu_dirty_ops intel_dirty_ops = {
- .set_dirty_tracking = intel_iommu_set_dirty_tracking,
- .read_and_clear_dirty = intel_iommu_read_and_clear_dirty,
-};
-
static int context_setup_pass_through(struct device *dev, u8 bus, u8 devfn)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
@@ -4401,7 +3820,9 @@ static int device_setup_pass_through(struct device *dev)
context_setup_pass_through_cb, dev);
}
-static int identity_domain_attach_dev(struct iommu_domain *domain, struct device *dev)
+static int identity_domain_attach_dev(struct iommu_domain *domain,
+ struct device *dev,
+ struct iommu_domain *old)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
@@ -4462,27 +3883,23 @@ static struct iommu_domain identity_domain = {
};
const struct iommu_domain_ops intel_fs_paging_domain_ops = {
+ IOMMU_PT_DOMAIN_OPS(x86_64),
.attach_dev = intel_iommu_attach_device,
.set_dev_pasid = intel_iommu_set_dev_pasid,
- .map_pages = intel_iommu_map_pages,
- .unmap_pages = intel_iommu_unmap_pages,
.iotlb_sync_map = intel_iommu_iotlb_sync_map,
.flush_iotlb_all = intel_flush_iotlb_all,
.iotlb_sync = intel_iommu_tlb_sync,
- .iova_to_phys = intel_iommu_iova_to_phys,
.free = intel_iommu_domain_free,
.enforce_cache_coherency = intel_iommu_enforce_cache_coherency_fs,
};
const struct iommu_domain_ops intel_ss_paging_domain_ops = {
+ IOMMU_PT_DOMAIN_OPS(vtdss),
.attach_dev = intel_iommu_attach_device,
.set_dev_pasid = intel_iommu_set_dev_pasid,
- .map_pages = intel_iommu_map_pages,
- .unmap_pages = intel_iommu_unmap_pages,
.iotlb_sync_map = intel_iommu_iotlb_sync_map,
.flush_iotlb_all = intel_flush_iotlb_all,
.iotlb_sync = intel_iommu_tlb_sync,
- .iova_to_phys = intel_iommu_iova_to_phys,
.free = intel_iommu_domain_free,
.enforce_cache_coherency = intel_iommu_enforce_cache_coherency_ss,
};
@@ -4797,3 +4214,5 @@ err:
return ret;
}
+
+MODULE_IMPORT_NS("GENERIC_PT_IOMMU");
diff --git a/drivers/iommu/intel/iommu.h b/drivers/iommu/intel/iommu.h
index 3056583d7f56..25c5e22096d4 100644
--- a/drivers/iommu/intel/iommu.h
+++ b/drivers/iommu/intel/iommu.h
@@ -23,8 +23,8 @@
#include <linux/xarray.h>
#include <linux/perf_event.h>
#include <linux/pci.h>
+#include <linux/generic_pt/iommu.h>
-#include <asm/cacheflush.h>
#include <asm/iommu.h>
#include <uapi/linux/iommufd.h>
@@ -595,22 +595,20 @@ struct qi_batch {
};
struct dmar_domain {
- int nid; /* node id */
+ union {
+ struct iommu_domain domain;
+ struct pt_iommu iommu;
+ /* First stage page table */
+ struct pt_iommu_x86_64 fspt;
+ /* Second stage page table */
+ struct pt_iommu_vtdss sspt;
+ };
+
struct xarray iommu_array; /* Attached IOMMU array */
- u8 iommu_coherency: 1; /* indicate coherency of iommu access */
- u8 force_snooping : 1; /* Create IOPTEs with snoop control */
- u8 set_pte_snp:1;
- u8 use_first_level:1; /* DMA translation for the domain goes
- * through the first level page table,
- * otherwise, goes through the second
- * level.
- */
+ u8 force_snooping:1; /* Create PASID entry with snoop control */
u8 dirty_tracking:1; /* Dirty tracking is enabled */
u8 nested_parent:1; /* Has other domains nested on it */
- u8 has_mappings:1; /* Has mappings configured through
- * iommu_map() interface.
- */
u8 iotlb_sync_map:1; /* Need to flush IOTLB cache or write
* buffer when creating mappings.
*/
@@ -623,26 +621,9 @@ struct dmar_domain {
struct list_head cache_tags; /* Cache tag list */
struct qi_batch *qi_batch; /* Batched QI descriptors */
- int iommu_superpage;/* Level of superpages supported:
- 0 == 4KiB (no superpages), 1 == 2MiB,
- 2 == 1GiB, 3 == 512GiB, 4 == 1TiB */
union {
/* DMA remapping domain */
struct {
- /* virtual address */
- struct dma_pte *pgd;
- /* max guest address width */
- int gaw;
- /*
- * adjusted guest address width:
- * 0: level 2 30-bit
- * 1: level 3 39-bit
- * 2: level 4 48-bit
- * 3: level 5 57-bit
- */
- int agaw;
- /* maximum mapped address */
- u64 max_addr;
/* Protect the s1_domains list */
spinlock_t s1_lock;
/* Track s1_domains nested on this domain */
@@ -664,10 +645,10 @@ struct dmar_domain {
struct mmu_notifier notifier;
};
};
-
- struct iommu_domain domain; /* generic domain data structure for
- iommu core */
};
+PT_IOMMU_CHECK_DOMAIN(struct dmar_domain, iommu, domain);
+PT_IOMMU_CHECK_DOMAIN(struct dmar_domain, sspt.iommu, domain);
+PT_IOMMU_CHECK_DOMAIN(struct dmar_domain, fspt.iommu, domain);
/*
* In theory, the VT-d 4.0 spec can support up to 2 ^ 16 counters.
@@ -866,11 +847,6 @@ struct dma_pte {
u64 val;
};
-static inline void dma_clear_pte(struct dma_pte *pte)
-{
- pte->val = 0;
-}
-
static inline u64 dma_pte_addr(struct dma_pte *pte)
{
#ifdef CONFIG_64BIT
@@ -886,32 +862,11 @@ static inline bool dma_pte_present(struct dma_pte *pte)
return (pte->val & 3) != 0;
}
-static inline bool dma_sl_pte_test_and_clear_dirty(struct dma_pte *pte,
- unsigned long flags)
-{
- if (flags & IOMMU_DIRTY_NO_CLEAR)
- return (pte->val & DMA_SL_PTE_DIRTY) != 0;
-
- return test_and_clear_bit(DMA_SL_PTE_DIRTY_BIT,
- (unsigned long *)&pte->val);
-}
-
static inline bool dma_pte_superpage(struct dma_pte *pte)
{
return (pte->val & DMA_PTE_LARGE_PAGE);
}
-static inline bool first_pte_in_page(struct dma_pte *pte)
-{
- return IS_ALIGNED((unsigned long)pte, VTD_PAGE_SIZE);
-}
-
-static inline int nr_pte_to_next_page(struct dma_pte *pte)
-{
- return first_pte_in_page(pte) ? BIT_ULL(VTD_STRIDE_SHIFT) :
- (struct dma_pte *)ALIGN((unsigned long)pte, VTD_PAGE_SIZE) - pte;
-}
-
static inline bool context_present(struct context_entry *context)
{
return (context->lo & 1);
@@ -927,11 +882,6 @@ static inline int agaw_to_level(int agaw)
return agaw + 2;
}
-static inline int agaw_to_width(int agaw)
-{
- return min_t(int, 30 + agaw * LEVEL_STRIDE, MAX_AGAW_WIDTH);
-}
-
static inline int width_to_agaw(int width)
{
return DIV_ROUND_UP(width - 30, LEVEL_STRIDE);
@@ -947,25 +897,6 @@ static inline int pfn_level_offset(u64 pfn, int level)
return (pfn >> level_to_offset_bits(level)) & LEVEL_MASK;
}
-static inline u64 level_mask(int level)
-{
- return -1ULL << level_to_offset_bits(level);
-}
-
-static inline u64 level_size(int level)
-{
- return 1ULL << level_to_offset_bits(level);
-}
-
-static inline u64 align_to_level(u64 pfn, int level)
-{
- return (pfn + level_size(level) - 1) & level_mask(level);
-}
-
-static inline unsigned long lvl_to_nr_pages(unsigned int lvl)
-{
- return 1UL << min_t(int, (lvl - 1) * LEVEL_STRIDE, MAX_AGAW_PFN_WIDTH);
-}
static inline void context_set_present(struct context_entry *context)
{
@@ -1097,7 +1028,7 @@ static inline void qi_desc_iotlb(struct intel_iommu *iommu, u16 did, u64 addr,
struct qi_desc *desc)
{
u8 dw = 0, dr = 0;
- int ih = 0;
+ int ih = addr & 1;
if (cap_write_drain(iommu->cap))
dw = 1;
diff --git a/drivers/iommu/intel/nested.c b/drivers/iommu/intel/nested.c
index 1b6ad9c900a5..a3fb8c193ca6 100644
--- a/drivers/iommu/intel/nested.c
+++ b/drivers/iommu/intel/nested.c
@@ -19,7 +19,7 @@
#include "pasid.h"
static int intel_nested_attach_dev(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
@@ -29,11 +29,6 @@ static int intel_nested_attach_dev(struct iommu_domain *domain,
device_block_translation(dev);
- if (iommu->agaw < dmar_domain->s2_domain->agaw) {
- dev_err_ratelimited(dev, "Adjusted guest address width not compatible\n");
- return -ENODEV;
- }
-
/*
* Stage-1 domain cannot work alone, it is nested on a s2_domain.
* The s2_domain will be used in nested translation, hence needs
diff --git a/drivers/iommu/intel/pasid.c b/drivers/iommu/intel/pasid.c
index 52f678975da7..3e2255057079 100644
--- a/drivers/iommu/intel/pasid.c
+++ b/drivers/iommu/intel/pasid.c
@@ -366,7 +366,7 @@ static void pasid_pte_config_first_level(struct intel_iommu *iommu,
pasid_set_domain_id(pte, did);
pasid_set_address_width(pte, iommu->agaw);
- pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap));
+ pasid_set_page_snoop(pte, flags & PASID_FLAG_PWSNP);
/* Setup Present and PASID Granular Transfer Type: */
pasid_set_translation_type(pte, PASID_ENTRY_PGTT_FL_ONLY);
@@ -461,19 +461,22 @@ int intel_pasid_replace_first_level(struct intel_iommu *iommu,
*/
static void pasid_pte_config_second_level(struct intel_iommu *iommu,
struct pasid_entry *pte,
- u64 pgd_val, int agaw, u16 did,
- bool dirty_tracking)
+ struct dmar_domain *domain, u16 did)
{
+ struct pt_iommu_vtdss_hw_info pt_info;
+
lockdep_assert_held(&iommu->lock);
+ pt_iommu_vtdss_hw_info(&domain->sspt, &pt_info);
pasid_clear_entry(pte);
pasid_set_domain_id(pte, did);
- pasid_set_slptr(pte, pgd_val);
- pasid_set_address_width(pte, agaw);
+ pasid_set_slptr(pte, pt_info.ssptptr);
+ pasid_set_address_width(pte, pt_info.aw);
pasid_set_translation_type(pte, PASID_ENTRY_PGTT_SL_ONLY);
pasid_set_fault_enable(pte);
- pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap));
- if (dirty_tracking)
+ pasid_set_page_snoop(pte, !(domain->sspt.vtdss_pt.common.features &
+ BIT(PT_FEAT_DMA_INCOHERENT)));
+ if (domain->dirty_tracking)
pasid_set_ssade(pte);
pasid_set_present(pte);
@@ -484,10 +487,9 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
struct device *dev, u32 pasid)
{
struct pasid_entry *pte;
- struct dma_pte *pgd;
- u64 pgd_val;
u16 did;
+
/*
* If hardware advertises no support for second level
* translation, return directly.
@@ -498,8 +500,6 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
return -EINVAL;
}
- pgd = domain->pgd;
- pgd_val = virt_to_phys(pgd);
did = domain_id_iommu(domain, iommu);
spin_lock(&iommu->lock);
@@ -514,8 +514,7 @@ int intel_pasid_setup_second_level(struct intel_iommu *iommu,
return -EBUSY;
}
- pasid_pte_config_second_level(iommu, pte, pgd_val, domain->agaw,
- did, domain->dirty_tracking);
+ pasid_pte_config_second_level(iommu, pte, domain, did);
spin_unlock(&iommu->lock);
pasid_flush_caches(iommu, pte, pasid, did);
@@ -529,8 +528,6 @@ int intel_pasid_replace_second_level(struct intel_iommu *iommu,
u32 pasid)
{
struct pasid_entry *pte, new_pte;
- struct dma_pte *pgd;
- u64 pgd_val;
u16 did;
/*
@@ -543,13 +540,9 @@ int intel_pasid_replace_second_level(struct intel_iommu *iommu,
return -EINVAL;
}
- pgd = domain->pgd;
- pgd_val = virt_to_phys(pgd);
did = domain_id_iommu(domain, iommu);
- pasid_pte_config_second_level(iommu, &new_pte, pgd_val,
- domain->agaw, did,
- domain->dirty_tracking);
+ pasid_pte_config_second_level(iommu, &new_pte, domain, did);
spin_lock(&iommu->lock);
pte = intel_pasid_get_entry(dev, pasid);
@@ -747,10 +740,12 @@ static void pasid_pte_config_nestd(struct intel_iommu *iommu,
struct dmar_domain *s2_domain,
u16 did)
{
- struct dma_pte *pgd = s2_domain->pgd;
+ struct pt_iommu_vtdss_hw_info pt_info;
lockdep_assert_held(&iommu->lock);
+ pt_iommu_vtdss_hw_info(&s2_domain->sspt, &pt_info);
+
pasid_clear_entry(pte);
if (s1_cfg->addr_width == ADDR_WIDTH_5LEVEL)
@@ -770,11 +765,12 @@ static void pasid_pte_config_nestd(struct intel_iommu *iommu,
if (s2_domain->force_snooping)
pasid_set_pgsnp(pte);
- pasid_set_slptr(pte, virt_to_phys(pgd));
+ pasid_set_slptr(pte, pt_info.ssptptr);
pasid_set_fault_enable(pte);
pasid_set_domain_id(pte, did);
- pasid_set_address_width(pte, s2_domain->agaw);
- pasid_set_page_snoop(pte, !!ecap_smpwc(iommu->ecap));
+ pasid_set_address_width(pte, pt_info.aw);
+ pasid_set_page_snoop(pte, !(s2_domain->sspt.vtdss_pt.common.features &
+ BIT(PT_FEAT_DMA_INCOHERENT)));
if (s2_domain->dirty_tracking)
pasid_set_ssade(pte);
pasid_set_translation_type(pte, PASID_ENTRY_PGTT_NESTED);
diff --git a/drivers/iommu/intel/pasid.h b/drivers/iommu/intel/pasid.h
index a771a77d4239..b4c85242dc79 100644
--- a/drivers/iommu/intel/pasid.h
+++ b/drivers/iommu/intel/pasid.h
@@ -24,6 +24,7 @@
#define PASID_FLAG_NESTED BIT(1)
#define PASID_FLAG_PAGE_SNOOP BIT(2)
+#define PASID_FLAG_PWSNP BIT(2)
/*
* The PASID_FLAG_FL5LP flag Indicates using 5-level paging for first-
diff --git a/drivers/iommu/intel/svm.c b/drivers/iommu/intel/svm.c
index e147f71f91b7..71de7947971f 100644
--- a/drivers/iommu/intel/svm.c
+++ b/drivers/iommu/intel/svm.c
@@ -170,6 +170,7 @@ static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
/* Setup the pasid table: */
sflags = cpu_feature_enabled(X86_FEATURE_LA57) ? PASID_FLAG_FL5LP : 0;
+ sflags |= PASID_FLAG_PWSNP;
ret = __domain_setup_first_level(iommu, dev, pasid,
FLPT_DEFAULT_DID, __pa(mm->pgd),
sflags, old);
diff --git a/drivers/iommu/io-pgtable-arm-selftests.c b/drivers/iommu/io-pgtable-arm-selftests.c
new file mode 100644
index 000000000000..334e70350924
--- /dev/null
+++ b/drivers/iommu/io-pgtable-arm-selftests.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * CPU-agnostic ARM page table allocator.
+ *
+ * Copyright (C) 2014 ARM Limited
+ *
+ * Author: Will Deacon <will.deacon@arm.com>
+ */
+
+#define pr_fmt(fmt) "arm-lpae io-pgtable: " fmt
+
+#include <kunit/device.h>
+#include <kunit/test.h>
+#include <linux/io-pgtable.h>
+#include <linux/kernel.h>
+
+#include "io-pgtable-arm.h"
+
+static struct io_pgtable_cfg *cfg_cookie;
+
+static void dummy_tlb_flush_all(void *cookie)
+{
+ WARN_ON(cookie != cfg_cookie);
+}
+
+static void dummy_tlb_flush(unsigned long iova, size_t size,
+ size_t granule, void *cookie)
+{
+ WARN_ON(cookie != cfg_cookie);
+ WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
+}
+
+static void dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
+ unsigned long iova, size_t granule,
+ void *cookie)
+{
+ dummy_tlb_flush(iova, granule, granule, cookie);
+}
+
+static const struct iommu_flush_ops dummy_tlb_ops = {
+ .tlb_flush_all = dummy_tlb_flush_all,
+ .tlb_flush_walk = dummy_tlb_flush,
+ .tlb_add_page = dummy_tlb_add_page,
+};
+
+#define __FAIL(test, i) ({ \
+ KUNIT_FAIL(test, "test failed for fmt idx %d\n", (i)); \
+ -EFAULT; \
+})
+
+static int arm_lpae_run_tests(struct kunit *test, struct io_pgtable_cfg *cfg)
+{
+ static const enum io_pgtable_fmt fmts[] = {
+ ARM_64_LPAE_S1,
+ ARM_64_LPAE_S2,
+ };
+
+ int i, j;
+ unsigned long iova;
+ size_t size, mapped;
+ struct io_pgtable_ops *ops;
+
+ for (i = 0; i < ARRAY_SIZE(fmts); ++i) {
+ cfg_cookie = cfg;
+ ops = alloc_io_pgtable_ops(fmts[i], cfg, cfg);
+ if (!ops) {
+ kunit_err(test, "failed to allocate io pgtable ops\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Initial sanity checks.
+ * Empty page tables shouldn't provide any translations.
+ */
+ if (ops->iova_to_phys(ops, 42))
+ return __FAIL(test, i);
+
+ if (ops->iova_to_phys(ops, SZ_1G + 42))
+ return __FAIL(test, i);
+
+ if (ops->iova_to_phys(ops, SZ_2G + 42))
+ return __FAIL(test, i);
+
+ /*
+ * Distinct mappings of different granule sizes.
+ */
+ iova = 0;
+ for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
+ size = 1UL << j;
+
+ if (ops->map_pages(ops, iova, iova, size, 1,
+ IOMMU_READ | IOMMU_WRITE |
+ IOMMU_NOEXEC | IOMMU_CACHE,
+ GFP_KERNEL, &mapped))
+ return __FAIL(test, i);
+
+ /* Overlapping mappings */
+ if (!ops->map_pages(ops, iova, iova + size, size, 1,
+ IOMMU_READ | IOMMU_NOEXEC,
+ GFP_KERNEL, &mapped))
+ return __FAIL(test, i);
+
+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
+ return __FAIL(test, i);
+
+ iova += SZ_1G;
+ }
+
+ /* Full unmap */
+ iova = 0;
+ for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
+ size = 1UL << j;
+
+ if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
+ return __FAIL(test, i);
+
+ if (ops->iova_to_phys(ops, iova + 42))
+ return __FAIL(test, i);
+
+ /* Remap full block */
+ if (ops->map_pages(ops, iova, iova, size, 1,
+ IOMMU_WRITE, GFP_KERNEL, &mapped))
+ return __FAIL(test, i);
+
+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
+ return __FAIL(test, i);
+
+ iova += SZ_1G;
+ }
+
+ /*
+ * Map/unmap the last largest supported page of the IAS, this can
+ * trigger corner cases in the concatednated page tables.
+ */
+ mapped = 0;
+ size = 1UL << __fls(cfg->pgsize_bitmap);
+ iova = (1UL << cfg->ias) - size;
+ if (ops->map_pages(ops, iova, iova, size, 1,
+ IOMMU_READ | IOMMU_WRITE |
+ IOMMU_NOEXEC | IOMMU_CACHE,
+ GFP_KERNEL, &mapped))
+ return __FAIL(test, i);
+ if (mapped != size)
+ return __FAIL(test, i);
+ if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
+ return __FAIL(test, i);
+
+ free_io_pgtable_ops(ops);
+ }
+
+ return 0;
+}
+
+static void arm_lpae_do_selftests(struct kunit *test)
+{
+ static const unsigned long pgsize[] = {
+ SZ_4K | SZ_2M | SZ_1G,
+ SZ_16K | SZ_32M,
+ SZ_64K | SZ_512M,
+ };
+
+ static const unsigned int address_size[] = {
+ 32, 36, 40, 42, 44, 48,
+ };
+
+ int i, j, k, pass = 0, fail = 0;
+ struct device *dev;
+ struct io_pgtable_cfg cfg = {
+ .tlb = &dummy_tlb_ops,
+ .coherent_walk = true,
+ .quirks = IO_PGTABLE_QUIRK_NO_WARN,
+ };
+
+ dev = kunit_device_register(test, "io-pgtable-test");
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, dev);
+ if (IS_ERR_OR_NULL(dev))
+ return;
+
+ cfg.iommu_dev = dev;
+
+ for (i = 0; i < ARRAY_SIZE(pgsize); ++i) {
+ for (j = 0; j < ARRAY_SIZE(address_size); ++j) {
+ /* Don't use ias > oas as it is not valid for stage-2. */
+ for (k = 0; k <= j; ++k) {
+ cfg.pgsize_bitmap = pgsize[i];
+ cfg.ias = address_size[k];
+ cfg.oas = address_size[j];
+ kunit_info(test, "pgsize_bitmap 0x%08lx, IAS %u OAS %u\n",
+ pgsize[i], cfg.ias, cfg.oas);
+ if (arm_lpae_run_tests(test, &cfg))
+ fail++;
+ else
+ pass++;
+ }
+ }
+ }
+
+ kunit_info(test, "completed with %d PASS %d FAIL\n", pass, fail);
+}
+
+static struct kunit_case io_pgtable_arm_test_cases[] = {
+ KUNIT_CASE(arm_lpae_do_selftests),
+ {},
+};
+
+static struct kunit_suite io_pgtable_arm_test = {
+ .name = "io-pgtable-arm-test",
+ .test_cases = io_pgtable_arm_test_cases,
+};
+
+kunit_test_suite(io_pgtable_arm_test);
+
+MODULE_DESCRIPTION("io-pgtable-arm library kunit tests");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 7e8e2216c294..e6626004b323 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -12,8 +12,6 @@
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/io-pgtable.h>
-#include <linux/kernel.h>
-#include <linux/device/faux.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -1267,204 +1265,3 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
.alloc = arm_mali_lpae_alloc_pgtable,
.free = arm_lpae_free_pgtable,
};
-
-#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE_SELFTEST
-
-static struct io_pgtable_cfg *cfg_cookie __initdata;
-
-static void __init dummy_tlb_flush_all(void *cookie)
-{
- WARN_ON(cookie != cfg_cookie);
-}
-
-static void __init dummy_tlb_flush(unsigned long iova, size_t size,
- size_t granule, void *cookie)
-{
- WARN_ON(cookie != cfg_cookie);
- WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
-}
-
-static void __init dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
- unsigned long iova, size_t granule,
- void *cookie)
-{
- dummy_tlb_flush(iova, granule, granule, cookie);
-}
-
-static const struct iommu_flush_ops dummy_tlb_ops __initconst = {
- .tlb_flush_all = dummy_tlb_flush_all,
- .tlb_flush_walk = dummy_tlb_flush,
- .tlb_add_page = dummy_tlb_add_page,
-};
-
-static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops)
-{
- struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
- struct io_pgtable_cfg *cfg = &data->iop.cfg;
-
- pr_err("cfg: pgsize_bitmap 0x%lx, ias %u-bit\n",
- cfg->pgsize_bitmap, cfg->ias);
- pr_err("data: %d levels, 0x%zx pgd_size, %u pg_shift, %u bits_per_level, pgd @ %p\n",
- ARM_LPAE_MAX_LEVELS - data->start_level, ARM_LPAE_PGD_SIZE(data),
- ilog2(ARM_LPAE_GRANULE(data)), data->bits_per_level, data->pgd);
-}
-
-#define __FAIL(ops, i) ({ \
- WARN(1, "selftest: test failed for fmt idx %d\n", (i)); \
- arm_lpae_dump_ops(ops); \
- -EFAULT; \
-})
-
-static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg)
-{
- static const enum io_pgtable_fmt fmts[] __initconst = {
- ARM_64_LPAE_S1,
- ARM_64_LPAE_S2,
- };
-
- int i, j;
- unsigned long iova;
- size_t size, mapped;
- struct io_pgtable_ops *ops;
-
- for (i = 0; i < ARRAY_SIZE(fmts); ++i) {
- cfg_cookie = cfg;
- ops = alloc_io_pgtable_ops(fmts[i], cfg, cfg);
- if (!ops) {
- pr_err("selftest: failed to allocate io pgtable ops\n");
- return -ENOMEM;
- }
-
- /*
- * Initial sanity checks.
- * Empty page tables shouldn't provide any translations.
- */
- if (ops->iova_to_phys(ops, 42))
- return __FAIL(ops, i);
-
- if (ops->iova_to_phys(ops, SZ_1G + 42))
- return __FAIL(ops, i);
-
- if (ops->iova_to_phys(ops, SZ_2G + 42))
- return __FAIL(ops, i);
-
- /*
- * Distinct mappings of different granule sizes.
- */
- iova = 0;
- for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
- size = 1UL << j;
-
- if (ops->map_pages(ops, iova, iova, size, 1,
- IOMMU_READ | IOMMU_WRITE |
- IOMMU_NOEXEC | IOMMU_CACHE,
- GFP_KERNEL, &mapped))
- return __FAIL(ops, i);
-
- /* Overlapping mappings */
- if (!ops->map_pages(ops, iova, iova + size, size, 1,
- IOMMU_READ | IOMMU_NOEXEC,
- GFP_KERNEL, &mapped))
- return __FAIL(ops, i);
-
- if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
- return __FAIL(ops, i);
-
- iova += SZ_1G;
- }
-
- /* Full unmap */
- iova = 0;
- for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
- size = 1UL << j;
-
- if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
- return __FAIL(ops, i);
-
- if (ops->iova_to_phys(ops, iova + 42))
- return __FAIL(ops, i);
-
- /* Remap full block */
- if (ops->map_pages(ops, iova, iova, size, 1,
- IOMMU_WRITE, GFP_KERNEL, &mapped))
- return __FAIL(ops, i);
-
- if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
- return __FAIL(ops, i);
-
- iova += SZ_1G;
- }
-
- /*
- * Map/unmap the last largest supported page of the IAS, this can
- * trigger corner cases in the concatednated page tables.
- */
- mapped = 0;
- size = 1UL << __fls(cfg->pgsize_bitmap);
- iova = (1UL << cfg->ias) - size;
- if (ops->map_pages(ops, iova, iova, size, 1,
- IOMMU_READ | IOMMU_WRITE |
- IOMMU_NOEXEC | IOMMU_CACHE,
- GFP_KERNEL, &mapped))
- return __FAIL(ops, i);
- if (mapped != size)
- return __FAIL(ops, i);
- if (ops->unmap_pages(ops, iova, size, 1, NULL) != size)
- return __FAIL(ops, i);
-
- free_io_pgtable_ops(ops);
- }
-
- return 0;
-}
-
-static int __init arm_lpae_do_selftests(void)
-{
- static const unsigned long pgsize[] __initconst = {
- SZ_4K | SZ_2M | SZ_1G,
- SZ_16K | SZ_32M,
- SZ_64K | SZ_512M,
- };
-
- static const unsigned int address_size[] __initconst = {
- 32, 36, 40, 42, 44, 48,
- };
-
- int i, j, k, pass = 0, fail = 0;
- struct faux_device *dev;
- struct io_pgtable_cfg cfg = {
- .tlb = &dummy_tlb_ops,
- .coherent_walk = true,
- .quirks = IO_PGTABLE_QUIRK_NO_WARN,
- };
-
- dev = faux_device_create("io-pgtable-test", NULL, 0);
- if (!dev)
- return -ENOMEM;
-
- cfg.iommu_dev = &dev->dev;
-
- for (i = 0; i < ARRAY_SIZE(pgsize); ++i) {
- for (j = 0; j < ARRAY_SIZE(address_size); ++j) {
- /* Don't use ias > oas as it is not valid for stage-2. */
- for (k = 0; k <= j; ++k) {
- cfg.pgsize_bitmap = pgsize[i];
- cfg.ias = address_size[k];
- cfg.oas = address_size[j];
- pr_info("selftest: pgsize_bitmap 0x%08lx, IAS %u OAS %u\n",
- pgsize[i], cfg.ias, cfg.oas);
- if (arm_lpae_run_tests(&cfg))
- fail++;
- else
- pass++;
- }
- }
- }
-
- pr_info("selftest: completed with %d PASS %d FAIL\n", pass, fail);
- faux_device_destroy(dev);
-
- return fail ? -EFAULT : 0;
-}
-subsys_initcall(arm_lpae_do_selftests);
-#endif
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 8841c1487f00..843fec8e8a51 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -28,10 +28,6 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
#ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S
[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
#endif
-#ifdef CONFIG_AMD_IOMMU
- [AMD_IOMMU_V1] = &io_pgtable_amd_iommu_v1_init_fns,
- [AMD_IOMMU_V2] = &io_pgtable_amd_iommu_v2_init_fns,
-#endif
};
static int check_custom_allocator(enum io_pgtable_fmt fmt,
diff --git a/drivers/iommu/iommu-pages.c b/drivers/iommu/iommu-pages.c
index 238c09e5166b..3bab175d8557 100644
--- a/drivers/iommu/iommu-pages.c
+++ b/drivers/iommu/iommu-pages.c
@@ -4,6 +4,7 @@
* Pasha Tatashin <pasha.tatashin@soleen.com>
*/
#include "iommu-pages.h"
+#include <linux/dma-mapping.h>
#include <linux/gfp.h>
#include <linux/mm.h>
@@ -22,6 +23,11 @@ IOPTDESC_MATCH(memcg_data, memcg_data);
#undef IOPTDESC_MATCH
static_assert(sizeof(struct ioptdesc) <= sizeof(struct page));
+static inline size_t ioptdesc_mem_size(struct ioptdesc *desc)
+{
+ return 1UL << (folio_order(ioptdesc_folio(desc)) + PAGE_SHIFT);
+}
+
/**
* iommu_alloc_pages_node_sz - Allocate a zeroed page of a given size from
* specific NUMA node
@@ -36,6 +42,7 @@ static_assert(sizeof(struct ioptdesc) <= sizeof(struct page));
*/
void *iommu_alloc_pages_node_sz(int nid, gfp_t gfp, size_t size)
{
+ struct ioptdesc *iopt;
unsigned long pgcnt;
struct folio *folio;
unsigned int order;
@@ -60,6 +67,9 @@ void *iommu_alloc_pages_node_sz(int nid, gfp_t gfp, size_t size)
if (unlikely(!folio))
return NULL;
+ iopt = folio_ioptdesc(folio);
+ iopt->incoherent = false;
+
/*
* All page allocations that should be reported to as "iommu-pagetables"
* to userspace must use one of the functions below. This includes
@@ -80,7 +90,10 @@ EXPORT_SYMBOL_GPL(iommu_alloc_pages_node_sz);
static void __iommu_free_desc(struct ioptdesc *iopt)
{
struct folio *folio = ioptdesc_folio(iopt);
- const unsigned long pgcnt = 1UL << folio_order(folio);
+ const unsigned long pgcnt = folio_nr_pages(folio);
+
+ if (IOMMU_PAGES_USE_DMA_API)
+ WARN_ON_ONCE(iopt->incoherent);
mod_node_page_state(folio_pgdat(folio), NR_IOMMU_PAGES, -pgcnt);
lruvec_stat_mod_folio(folio, NR_SECONDARY_PAGETABLE, -pgcnt);
@@ -117,3 +130,124 @@ void iommu_put_pages_list(struct iommu_pages_list *list)
__iommu_free_desc(iopt);
}
EXPORT_SYMBOL_GPL(iommu_put_pages_list);
+
+/**
+ * iommu_pages_start_incoherent - Setup the page for cache incoherent operation
+ * @virt: The page to setup
+ * @dma_dev: The iommu device
+ *
+ * For incoherent memory this will use the DMA API to manage the cache flushing
+ * on some arches. This is a lot of complexity compared to just calling
+ * arch_sync_dma_for_device(), but it is what the existing ARM iommu drivers
+ * have been doing. The DMA API requires keeping track of the DMA map and
+ * freeing it when required. This keeps track of the dma map inside the ioptdesc
+ * so that error paths are simple for the caller.
+ */
+int iommu_pages_start_incoherent(void *virt, struct device *dma_dev)
+{
+ struct ioptdesc *iopt = virt_to_ioptdesc(virt);
+ dma_addr_t dma;
+
+ if (WARN_ON(iopt->incoherent))
+ return -EINVAL;
+
+ if (!IOMMU_PAGES_USE_DMA_API) {
+ iommu_pages_flush_incoherent(dma_dev, virt, 0,
+ ioptdesc_mem_size(iopt));
+ } else {
+ dma = dma_map_single(dma_dev, virt, ioptdesc_mem_size(iopt),
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dma_dev, dma))
+ return -EINVAL;
+
+ /*
+ * The DMA API is not allowed to do anything other than DMA
+ * direct. It would be nice to also check
+ * dev_is_dma_coherent(dma_dev));
+ */
+ if (WARN_ON(dma != virt_to_phys(virt))) {
+ dma_unmap_single(dma_dev, dma, ioptdesc_mem_size(iopt),
+ DMA_TO_DEVICE);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ iopt->incoherent = 1;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_pages_start_incoherent);
+
+/**
+ * iommu_pages_start_incoherent_list - Make a list of pages incoherent
+ * @list: The list of pages to setup
+ * @dma_dev: The iommu device
+ *
+ * Perform iommu_pages_start_incoherent() across all of list.
+ *
+ * If this fails the caller must call iommu_pages_stop_incoherent_list().
+ */
+int iommu_pages_start_incoherent_list(struct iommu_pages_list *list,
+ struct device *dma_dev)
+{
+ struct ioptdesc *cur;
+ int ret;
+
+ list_for_each_entry(cur, &list->pages, iopt_freelist_elm) {
+ if (WARN_ON(cur->incoherent))
+ continue;
+
+ ret = iommu_pages_start_incoherent(
+ folio_address(ioptdesc_folio(cur)), dma_dev);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_pages_start_incoherent_list);
+
+/**
+ * iommu_pages_stop_incoherent_list - Undo incoherence across a list
+ * @list: The list of pages to release
+ * @dma_dev: The iommu device
+ *
+ * Revert iommu_pages_start_incoherent() across all of the list. Pages that did
+ * not call or succeed iommu_pages_start_incoherent() will be ignored.
+ */
+#if IOMMU_PAGES_USE_DMA_API
+void iommu_pages_stop_incoherent_list(struct iommu_pages_list *list,
+ struct device *dma_dev)
+{
+ struct ioptdesc *cur;
+
+ list_for_each_entry(cur, &list->pages, iopt_freelist_elm) {
+ struct folio *folio = ioptdesc_folio(cur);
+
+ if (!cur->incoherent)
+ continue;
+ dma_unmap_single(dma_dev, virt_to_phys(folio_address(folio)),
+ ioptdesc_mem_size(cur), DMA_TO_DEVICE);
+ cur->incoherent = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(iommu_pages_stop_incoherent_list);
+
+/**
+ * iommu_pages_free_incoherent - Free an incoherent page
+ * @virt: virtual address of the page to be freed.
+ * @dma_dev: The iommu device
+ *
+ * If the page is incoherent it made coherent again then freed.
+ */
+void iommu_pages_free_incoherent(void *virt, struct device *dma_dev)
+{
+ struct ioptdesc *iopt = virt_to_ioptdesc(virt);
+
+ if (iopt->incoherent) {
+ dma_unmap_single(dma_dev, virt_to_phys(virt),
+ ioptdesc_mem_size(iopt), DMA_TO_DEVICE);
+ iopt->incoherent = 0;
+ }
+ __iommu_free_desc(iopt);
+}
+EXPORT_SYMBOL_GPL(iommu_pages_free_incoherent);
+#endif
diff --git a/drivers/iommu/iommu-pages.h b/drivers/iommu/iommu-pages.h
index b3af2813ed0c..ae9da4f571f6 100644
--- a/drivers/iommu/iommu-pages.h
+++ b/drivers/iommu/iommu-pages.h
@@ -21,7 +21,10 @@ struct ioptdesc {
struct list_head iopt_freelist_elm;
unsigned long __page_mapping;
- pgoff_t __index;
+ union {
+ u8 incoherent;
+ pgoff_t __index;
+ };
void *_private;
unsigned int __page_type;
@@ -98,4 +101,48 @@ static inline void *iommu_alloc_pages_sz(gfp_t gfp, size_t size)
return iommu_alloc_pages_node_sz(NUMA_NO_NODE, gfp, size);
}
-#endif /* __IOMMU_PAGES_H */
+int iommu_pages_start_incoherent(void *virt, struct device *dma_dev);
+int iommu_pages_start_incoherent_list(struct iommu_pages_list *list,
+ struct device *dma_dev);
+
+#ifdef CONFIG_X86
+#define IOMMU_PAGES_USE_DMA_API 0
+#include <linux/cacheflush.h>
+
+static inline void iommu_pages_flush_incoherent(struct device *dma_dev,
+ void *virt, size_t offset,
+ size_t len)
+{
+ clflush_cache_range(virt + offset, len);
+}
+static inline void
+iommu_pages_stop_incoherent_list(struct iommu_pages_list *list,
+ struct device *dma_dev)
+{
+ /*
+ * For performance leave the incoherent flag alone which turns this into
+ * a NOP. For X86 the rest of the stop/free flow ignores the flag.
+ */
+}
+static inline void iommu_pages_free_incoherent(void *virt,
+ struct device *dma_dev)
+{
+ iommu_free_pages(virt);
+}
+#else
+#define IOMMU_PAGES_USE_DMA_API 1
+#include <linux/dma-mapping.h>
+
+static inline void iommu_pages_flush_incoherent(struct device *dma_dev,
+ void *virt, size_t offset,
+ size_t len)
+{
+ dma_sync_single_for_device(dma_dev, (uintptr_t)virt + offset, len,
+ DMA_TO_DEVICE);
+}
+void iommu_pages_stop_incoherent_list(struct iommu_pages_list *list,
+ struct device *dma_dev);
+void iommu_pages_free_incoherent(void *virt, struct device *dma_dev);
+#endif
+
+#endif /* __IOMMU_PAGES_H */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 59244c744eab..2ca990dfbb88 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -100,7 +100,7 @@ static int iommu_bus_notifier(struct notifier_block *nb,
unsigned long action, void *data);
static void iommu_release_device(struct device *dev);
static int __iommu_attach_device(struct iommu_domain *domain,
- struct device *dev);
+ struct device *dev, struct iommu_domain *old);
static int __iommu_attach_group(struct iommu_domain *domain,
struct iommu_group *group);
static struct iommu_domain *__iommu_paging_domain_alloc_flags(struct device *dev,
@@ -114,6 +114,7 @@ enum {
static int __iommu_device_set_domain(struct iommu_group *group,
struct device *dev,
struct iommu_domain *new_domain,
+ struct iommu_domain *old_domain,
unsigned int flags);
static int __iommu_group_set_domain_internal(struct iommu_group *group,
struct iommu_domain *new_domain,
@@ -542,8 +543,21 @@ static void iommu_deinit_device(struct device *dev)
* Regardless, if a delayed attach never occurred, then the release
* should still avoid touching any hardware configuration either.
*/
- if (!dev->iommu->attach_deferred && ops->release_domain)
- ops->release_domain->ops->attach_dev(ops->release_domain, dev);
+ if (!dev->iommu->attach_deferred && ops->release_domain) {
+ struct iommu_domain *release_domain = ops->release_domain;
+
+ /*
+ * If the device requires direct mappings then it should not
+ * be parked on a BLOCKED domain during release as that would
+ * break the direct mappings.
+ */
+ if (dev->iommu->require_direct && ops->identity_domain &&
+ release_domain == ops->blocked_domain)
+ release_domain = ops->identity_domain;
+
+ release_domain->ops->attach_dev(release_domain, dev,
+ group->domain);
+ }
if (ops->release_device)
ops->release_device(dev);
@@ -628,7 +642,8 @@ static int __iommu_probe_device(struct device *dev, struct list_head *group_list
if (group->default_domain)
iommu_create_device_direct_mappings(group->default_domain, dev);
if (group->domain) {
- ret = __iommu_device_set_domain(group, dev, group->domain, 0);
+ ret = __iommu_device_set_domain(group, dev, group->domain, NULL,
+ 0);
if (ret)
goto err_remove_gdev;
} else if (!group->default_domain && !group_list) {
@@ -2115,14 +2130,14 @@ static void __iommu_group_set_core_domain(struct iommu_group *group)
}
static int __iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
int ret;
if (unlikely(domain->ops->attach_dev == NULL))
return -ENODEV;
- ret = domain->ops->attach_dev(domain, dev);
+ ret = domain->ops->attach_dev(domain, dev, old);
if (ret)
return ret;
dev->iommu->attach_deferred = 0;
@@ -2171,7 +2186,7 @@ EXPORT_SYMBOL_GPL(iommu_attach_device);
int iommu_deferred_attach(struct device *dev, struct iommu_domain *domain)
{
if (dev->iommu && dev->iommu->attach_deferred)
- return __iommu_attach_device(domain, dev);
+ return __iommu_attach_device(domain, dev, NULL);
return 0;
}
@@ -2284,6 +2299,7 @@ EXPORT_SYMBOL_GPL(iommu_attach_group);
static int __iommu_device_set_domain(struct iommu_group *group,
struct device *dev,
struct iommu_domain *new_domain,
+ struct iommu_domain *old_domain,
unsigned int flags)
{
int ret;
@@ -2309,7 +2325,7 @@ static int __iommu_device_set_domain(struct iommu_group *group,
dev->iommu->attach_deferred = 0;
}
- ret = __iommu_attach_device(new_domain, dev);
+ ret = __iommu_attach_device(new_domain, dev, old_domain);
if (ret) {
/*
* If we have a blocking domain then try to attach that in hopes
@@ -2319,7 +2335,8 @@ static int __iommu_device_set_domain(struct iommu_group *group,
if ((flags & IOMMU_SET_DOMAIN_MUST_SUCCEED) &&
group->blocking_domain &&
group->blocking_domain != new_domain)
- __iommu_attach_device(group->blocking_domain, dev);
+ __iommu_attach_device(group->blocking_domain, dev,
+ old_domain);
return ret;
}
return 0;
@@ -2366,7 +2383,7 @@ static int __iommu_group_set_domain_internal(struct iommu_group *group,
result = 0;
for_each_group_device(group, gdev) {
ret = __iommu_device_set_domain(group, gdev->dev, new_domain,
- flags);
+ group->domain, flags);
if (ret) {
result = ret;
/*
@@ -2391,6 +2408,9 @@ err_revert:
*/
last_gdev = gdev;
for_each_group_device(group, gdev) {
+ /* No need to revert the last gdev that failed to set domain */
+ if (gdev == last_gdev)
+ break;
/*
* A NULL domain can happen only for first probe, in which case
* we leave group->domain as NULL and let release clean
@@ -2398,10 +2418,8 @@ err_revert:
*/
if (group->domain)
WARN_ON(__iommu_device_set_domain(
- group, gdev->dev, group->domain,
+ group, gdev->dev, group->domain, new_domain,
IOMMU_SET_DOMAIN_MUST_SUCCEED));
- if (gdev == last_gdev)
- break;
}
return ret;
}
diff --git a/drivers/iommu/iommufd/Kconfig b/drivers/iommu/iommufd/Kconfig
index 2beeb4f60ee5..eae3f03629b0 100644
--- a/drivers/iommu/iommufd/Kconfig
+++ b/drivers/iommu/iommufd/Kconfig
@@ -41,6 +41,7 @@ config IOMMUFD_TEST
depends on DEBUG_KERNEL
depends on FAULT_INJECTION
depends on RUNTIME_TESTING_MENU
+ depends on IOMMU_PT_AMDV1
select IOMMUFD_DRIVER
default n
help
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 6f1010da221c..21d4a35538f6 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -161,8 +161,8 @@ int iommufd_viommu_report_event(struct iommufd_viommu *viommu,
vevent = &veventq->lost_events_header;
goto out_set_header;
}
- memcpy(vevent->event_data, event_data, data_len);
vevent->data_len = data_len;
+ memcpy(vevent->event_data, event_data, data_len);
veventq->num_events++;
out_set_header:
diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c
index c0360c450880..54cf4d856179 100644
--- a/drivers/iommu/iommufd/io_pagetable.c
+++ b/drivers/iommu/iommufd/io_pagetable.c
@@ -8,8 +8,10 @@
* The datastructure uses the iopt_pages to optimize the storage of the PFNs
* between the domains and xarray.
*/
+#include <linux/dma-buf.h>
#include <linux/err.h>
#include <linux/errno.h>
+#include <linux/file.h>
#include <linux/iommu.h>
#include <linux/iommufd.h>
#include <linux/lockdep.h>
@@ -284,6 +286,9 @@ static int iopt_alloc_area_pages(struct io_pagetable *iopt,
case IOPT_ADDRESS_FILE:
start = elm->start_byte + elm->pages->start;
break;
+ case IOPT_ADDRESS_DMABUF:
+ start = elm->start_byte + elm->pages->dmabuf.start;
+ break;
}
rc = iopt_alloc_iova(iopt, dst_iova, start, length);
if (rc)
@@ -468,25 +473,53 @@ int iopt_map_user_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
* @iopt: io_pagetable to act on
* @iova: If IOPT_ALLOC_IOVA is set this is unused on input and contains
* the chosen iova on output. Otherwise is the iova to map to on input
- * @file: file to map
+ * @fd: fdno of a file to map
* @start: map file starting at this byte offset
* @length: Number of bytes to map
* @iommu_prot: Combination of IOMMU_READ/WRITE/etc bits for the mapping
* @flags: IOPT_ALLOC_IOVA or zero
*/
int iopt_map_file_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
- unsigned long *iova, struct file *file,
- unsigned long start, unsigned long length,
- int iommu_prot, unsigned int flags)
+ unsigned long *iova, int fd, unsigned long start,
+ unsigned long length, int iommu_prot,
+ unsigned int flags)
{
struct iopt_pages *pages;
+ struct dma_buf *dmabuf;
+ unsigned long start_byte;
+ unsigned long last;
+
+ if (!length)
+ return -EINVAL;
+ if (check_add_overflow(start, length - 1, &last))
+ return -EOVERFLOW;
+
+ start_byte = start - ALIGN_DOWN(start, PAGE_SIZE);
+ dmabuf = dma_buf_get(fd);
+ if (!IS_ERR(dmabuf)) {
+ pages = iopt_alloc_dmabuf_pages(ictx, dmabuf, start_byte, start,
+ length,
+ iommu_prot & IOMMU_WRITE);
+ if (IS_ERR(pages)) {
+ dma_buf_put(dmabuf);
+ return PTR_ERR(pages);
+ }
+ } else {
+ struct file *file;
+
+ file = fget(fd);
+ if (!file)
+ return -EBADF;
+
+ pages = iopt_alloc_file_pages(file, start_byte, start, length,
+ iommu_prot & IOMMU_WRITE);
+ fput(file);
+ if (IS_ERR(pages))
+ return PTR_ERR(pages);
+ }
- pages = iopt_alloc_file_pages(file, start, length,
- iommu_prot & IOMMU_WRITE);
- if (IS_ERR(pages))
- return PTR_ERR(pages);
return iopt_map_common(ictx, iopt, pages, iova, length,
- start - pages->start, iommu_prot, flags);
+ start_byte, iommu_prot, flags);
}
struct iova_bitmap_fn_arg {
@@ -707,7 +740,8 @@ static int iopt_unmap_iova_range(struct io_pagetable *iopt, unsigned long start,
struct iopt_area *area;
unsigned long unmapped_bytes = 0;
unsigned int tries = 0;
- int rc = -ENOENT;
+ /* If there are no mapped entries then success */
+ int rc = 0;
/*
* The domains_rwsem must be held in read mode any time any area->pages
@@ -777,8 +811,6 @@ again:
down_write(&iopt->iova_rwsem);
}
- if (unmapped_bytes)
- rc = 0;
out_unlock_iova:
up_write(&iopt->iova_rwsem);
@@ -815,13 +847,8 @@ int iopt_unmap_iova(struct io_pagetable *iopt, unsigned long iova,
int iopt_unmap_all(struct io_pagetable *iopt, unsigned long *unmapped)
{
- int rc;
-
- rc = iopt_unmap_iova_range(iopt, 0, ULONG_MAX, unmapped);
/* If the IOVAs are empty then unmap all succeeds */
- if (rc == -ENOENT)
- return 0;
- return rc;
+ return iopt_unmap_iova_range(iopt, 0, ULONG_MAX, unmapped);
}
/* The caller must always free all the nodes in the allowed_iova rb_root. */
@@ -967,9 +994,15 @@ static void iopt_unfill_domain(struct io_pagetable *iopt,
WARN_ON(!area->storage_domain);
if (area->storage_domain == domain)
area->storage_domain = storage_domain;
+ if (iopt_is_dmabuf(pages)) {
+ if (!iopt_dmabuf_revoked(pages))
+ iopt_area_unmap_domain(area, domain);
+ iopt_dmabuf_untrack_domain(pages, area, domain);
+ }
mutex_unlock(&pages->mutex);
- iopt_area_unmap_domain(area, domain);
+ if (!iopt_is_dmabuf(pages))
+ iopt_area_unmap_domain(area, domain);
}
return;
}
@@ -986,6 +1019,8 @@ static void iopt_unfill_domain(struct io_pagetable *iopt,
WARN_ON(area->storage_domain != domain);
area->storage_domain = NULL;
iopt_area_unfill_domain(area, pages, domain);
+ if (iopt_is_dmabuf(pages))
+ iopt_dmabuf_untrack_domain(pages, area, domain);
mutex_unlock(&pages->mutex);
}
}
@@ -1015,10 +1050,16 @@ static int iopt_fill_domain(struct io_pagetable *iopt,
if (!pages)
continue;
- mutex_lock(&pages->mutex);
+ guard(mutex)(&pages->mutex);
+ if (iopt_is_dmabuf(pages)) {
+ rc = iopt_dmabuf_track_domain(pages, area, domain);
+ if (rc)
+ goto out_unfill;
+ }
rc = iopt_area_fill_domain(area, domain);
if (rc) {
- mutex_unlock(&pages->mutex);
+ if (iopt_is_dmabuf(pages))
+ iopt_dmabuf_untrack_domain(pages, area, domain);
goto out_unfill;
}
if (!area->storage_domain) {
@@ -1027,7 +1068,6 @@ static int iopt_fill_domain(struct io_pagetable *iopt,
interval_tree_insert(&area->pages_node,
&pages->domains_itree);
}
- mutex_unlock(&pages->mutex);
}
return 0;
@@ -1048,6 +1088,8 @@ out_unfill:
area->storage_domain = NULL;
}
iopt_area_unfill_domain(area, pages, domain);
+ if (iopt_is_dmabuf(pages))
+ iopt_dmabuf_untrack_domain(pages, area, domain);
mutex_unlock(&pages->mutex);
}
return rc;
@@ -1258,6 +1300,10 @@ static int iopt_area_split(struct iopt_area *area, unsigned long iova)
if (!pages || area->prevent_access)
return -EBUSY;
+ /* Maintaining the domains_itree below is a bit complicated */
+ if (iopt_is_dmabuf(pages))
+ return -EOPNOTSUPP;
+
if (new_start & (alignment - 1) ||
iopt_area_start_byte(area, new_start) & (alignment - 1))
return -EINVAL;
diff --git a/drivers/iommu/iommufd/io_pagetable.h b/drivers/iommu/iommufd/io_pagetable.h
index b6064f4ce4af..14cd052fd320 100644
--- a/drivers/iommu/iommufd/io_pagetable.h
+++ b/drivers/iommu/iommufd/io_pagetable.h
@@ -5,6 +5,7 @@
#ifndef __IO_PAGETABLE_H
#define __IO_PAGETABLE_H
+#include <linux/dma-buf.h>
#include <linux/interval_tree.h>
#include <linux/kref.h>
#include <linux/mutex.h>
@@ -69,6 +70,16 @@ void iopt_area_unfill_domain(struct iopt_area *area, struct iopt_pages *pages,
void iopt_area_unmap_domain(struct iopt_area *area,
struct iommu_domain *domain);
+int iopt_dmabuf_track_domain(struct iopt_pages *pages, struct iopt_area *area,
+ struct iommu_domain *domain);
+void iopt_dmabuf_untrack_domain(struct iopt_pages *pages,
+ struct iopt_area *area,
+ struct iommu_domain *domain);
+int iopt_dmabuf_track_all_domains(struct iopt_area *area,
+ struct iopt_pages *pages);
+void iopt_dmabuf_untrack_all_domains(struct iopt_area *area,
+ struct iopt_pages *pages);
+
static inline unsigned long iopt_area_index(struct iopt_area *area)
{
return area->pages_node.start;
@@ -179,7 +190,22 @@ enum {
enum iopt_address_type {
IOPT_ADDRESS_USER = 0,
- IOPT_ADDRESS_FILE = 1,
+ IOPT_ADDRESS_FILE,
+ IOPT_ADDRESS_DMABUF,
+};
+
+struct iopt_pages_dmabuf_track {
+ struct iommu_domain *domain;
+ struct iopt_area *area;
+ struct list_head elm;
+};
+
+struct iopt_pages_dmabuf {
+ struct dma_buf_attachment *attach;
+ struct dma_buf_phys_vec phys;
+ /* Always PAGE_SIZE aligned */
+ unsigned long start;
+ struct list_head tracker;
};
/*
@@ -209,6 +235,8 @@ struct iopt_pages {
struct file *file;
unsigned long start;
};
+ /* IOPT_ADDRESS_DMABUF */
+ struct iopt_pages_dmabuf dmabuf;
};
bool writable:1;
u8 account_mode;
@@ -220,10 +248,32 @@ struct iopt_pages {
struct rb_root_cached domains_itree;
};
+static inline bool iopt_is_dmabuf(struct iopt_pages *pages)
+{
+ if (!IS_ENABLED(CONFIG_DMA_SHARED_BUFFER))
+ return false;
+ return pages->type == IOPT_ADDRESS_DMABUF;
+}
+
+static inline bool iopt_dmabuf_revoked(struct iopt_pages *pages)
+{
+ lockdep_assert_held(&pages->mutex);
+ if (iopt_is_dmabuf(pages))
+ return pages->dmabuf.phys.len == 0;
+ return false;
+}
+
struct iopt_pages *iopt_alloc_user_pages(void __user *uptr,
unsigned long length, bool writable);
-struct iopt_pages *iopt_alloc_file_pages(struct file *file, unsigned long start,
+struct iopt_pages *iopt_alloc_file_pages(struct file *file,
+ unsigned long start_byte,
+ unsigned long start,
unsigned long length, bool writable);
+struct iopt_pages *iopt_alloc_dmabuf_pages(struct iommufd_ctx *ictx,
+ struct dma_buf *dmabuf,
+ unsigned long start_byte,
+ unsigned long start,
+ unsigned long length, bool writable);
void iopt_release_pages(struct kref *kref);
static inline void iopt_put_pages(struct iopt_pages *pages)
{
diff --git a/drivers/iommu/iommufd/ioas.c b/drivers/iommu/iommufd/ioas.c
index 1542c5fd10a8..f4721afedadc 100644
--- a/drivers/iommu/iommufd/ioas.c
+++ b/drivers/iommu/iommufd/ioas.c
@@ -207,7 +207,6 @@ int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd)
unsigned long iova = cmd->iova;
struct iommufd_ioas *ioas;
unsigned int flags = 0;
- struct file *file;
int rc;
if (cmd->flags &
@@ -229,11 +228,7 @@ int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd)
if (!(cmd->flags & IOMMU_IOAS_MAP_FIXED_IOVA))
flags = IOPT_ALLOC_IOVA;
- file = fget(cmd->fd);
- if (!file)
- return -EBADF;
-
- rc = iopt_map_file_pages(ucmd->ictx, &ioas->iopt, &iova, file,
+ rc = iopt_map_file_pages(ucmd->ictx, &ioas->iopt, &iova, cmd->fd,
cmd->start, cmd->length,
conv_iommu_prot(cmd->flags), flags);
if (rc)
@@ -243,7 +238,6 @@ int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd)
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
out_put:
iommufd_put_object(ucmd->ictx, &ioas->obj);
- fput(file);
return rc;
}
@@ -367,6 +361,10 @@ int iommufd_ioas_unmap(struct iommufd_ucmd *ucmd)
&unmapped);
if (rc)
goto out_put;
+ if (!unmapped) {
+ rc = -ENOENT;
+ goto out_put;
+ }
}
cmd->length = unmapped;
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 627f9b78483a..eb6d1a70f673 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -19,6 +19,8 @@ struct iommu_domain;
struct iommu_group;
struct iommu_option;
struct iommufd_device;
+struct dma_buf_attachment;
+struct dma_buf_phys_vec;
struct iommufd_sw_msi_map {
struct list_head sw_msi_item;
@@ -108,7 +110,7 @@ int iopt_map_user_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
unsigned long length, int iommu_prot,
unsigned int flags);
int iopt_map_file_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt,
- unsigned long *iova, struct file *file,
+ unsigned long *iova, int fd,
unsigned long start, unsigned long length,
int iommu_prot, unsigned int flags);
int iopt_map_pages(struct io_pagetable *iopt, struct list_head *pages_list,
@@ -504,6 +506,8 @@ void iommufd_device_pre_destroy(struct iommufd_object *obj);
void iommufd_device_destroy(struct iommufd_object *obj);
int iommufd_get_hw_info(struct iommufd_ucmd *ucmd);
+struct device *iommufd_global_device(void);
+
struct iommufd_access {
struct iommufd_object obj;
struct iommufd_ctx *ictx;
@@ -614,7 +618,6 @@ struct iommufd_veventq {
struct iommufd_eventq common;
struct iommufd_viommu *viommu;
struct list_head node; /* for iommufd_viommu::veventqs */
- struct iommufd_vevent lost_events_header;
enum iommu_veventq_type type;
unsigned int depth;
@@ -622,6 +625,9 @@ struct iommufd_veventq {
/* Use common.lock for protection */
u32 num_events;
u32 sequence;
+
+ /* Must be last as it ends in a flexible-array member. */
+ struct iommufd_vevent lost_events_header;
};
static inline struct iommufd_veventq *
@@ -711,6 +717,8 @@ bool iommufd_should_fail(void);
int __init iommufd_test_init(void);
void iommufd_test_exit(void);
bool iommufd_selftest_is_mock_dev(struct device *dev);
+int iommufd_test_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
+ struct dma_buf_phys_vec *phys);
#else
static inline void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
unsigned int ioas_id,
@@ -732,5 +740,11 @@ static inline bool iommufd_selftest_is_mock_dev(struct device *dev)
{
return false;
}
+static inline int
+iommufd_test_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
+ struct dma_buf_phys_vec *phys)
+{
+ return -EOPNOTSUPP;
+}
#endif
#endif
diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h
index 8fc618b2bcf9..73e73e1ec158 100644
--- a/drivers/iommu/iommufd/iommufd_test.h
+++ b/drivers/iommu/iommufd/iommufd_test.h
@@ -29,11 +29,22 @@ enum {
IOMMU_TEST_OP_PASID_REPLACE,
IOMMU_TEST_OP_PASID_DETACH,
IOMMU_TEST_OP_PASID_CHECK_HWPT,
+ IOMMU_TEST_OP_DMABUF_GET,
+ IOMMU_TEST_OP_DMABUF_REVOKE,
};
enum {
+ MOCK_IOMMUPT_DEFAULT = 0,
+ MOCK_IOMMUPT_HUGE,
+ MOCK_IOMMUPT_AMDV1,
+};
+
+/* These values are true for MOCK_IOMMUPT_DEFAULT */
+enum {
MOCK_APERTURE_START = 1UL << 24,
MOCK_APERTURE_LAST = (1UL << 31) - 1,
+ MOCK_PAGE_SIZE = 2048,
+ MOCK_HUGE_PAGE_SIZE = 512 * MOCK_PAGE_SIZE,
};
enum {
@@ -52,7 +63,6 @@ enum {
enum {
MOCK_FLAGS_DEVICE_NO_DIRTY = 1 << 0,
- MOCK_FLAGS_DEVICE_HUGE_IOVA = 1 << 1,
MOCK_FLAGS_DEVICE_PASID = 1 << 2,
};
@@ -176,6 +186,14 @@ struct iommu_test_cmd {
__u32 hwpt_id;
/* @id is stdev_id */
} pasid_check;
+ struct {
+ __u32 length;
+ __u32 open_flags;
+ } dmabuf_get;
+ struct {
+ __s32 dmabuf_fd;
+ __u32 revoked;
+ } dmabuf_revoke;
};
__u32 last;
};
@@ -205,6 +223,7 @@ struct iommu_test_hw_info {
*/
struct iommu_hwpt_selftest {
__u32 iotlb;
+ __u32 pagetable_type;
};
/* Should not be equal to any defined value in enum iommu_hwpt_invalidate_data_type */
diff --git a/drivers/iommu/iommufd/iova_bitmap.c b/drivers/iommu/iommufd/iova_bitmap.c
index 4514575818fc..b5b67a9d3fb3 100644
--- a/drivers/iommu/iommufd/iova_bitmap.c
+++ b/drivers/iommu/iommufd/iova_bitmap.c
@@ -130,9 +130,8 @@ struct iova_bitmap {
static unsigned long iova_bitmap_offset_to_index(struct iova_bitmap *bitmap,
unsigned long iova)
{
- unsigned long pgsize = 1UL << bitmap->mapped.pgshift;
-
- return iova / (BITS_PER_TYPE(*bitmap->bitmap) * pgsize);
+ return (iova >> bitmap->mapped.pgshift) /
+ BITS_PER_TYPE(*bitmap->bitmap);
}
/*
diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
index ce775fbbae94..5cc4b08c25f5 100644
--- a/drivers/iommu/iommufd/main.c
+++ b/drivers/iommu/iommufd/main.c
@@ -751,6 +751,15 @@ static struct miscdevice vfio_misc_dev = {
.mode = 0666,
};
+/*
+ * Used only by DMABUF, returns a valid struct device to use as a dummy struct
+ * device for attachment.
+ */
+struct device *iommufd_global_device(void)
+{
+ return iommu_misc_dev.this_device;
+}
+
static int __init iommufd_init(void)
{
int ret;
@@ -794,5 +803,6 @@ MODULE_ALIAS("devname:vfio/vfio");
#endif
MODULE_IMPORT_NS("IOMMUFD_INTERNAL");
MODULE_IMPORT_NS("IOMMUFD");
+MODULE_IMPORT_NS("DMA_BUF");
MODULE_DESCRIPTION("I/O Address Space Management for passthrough devices");
MODULE_LICENSE("GPL");
diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c
index c3433b845561..dbe51ecb9a20 100644
--- a/drivers/iommu/iommufd/pages.c
+++ b/drivers/iommu/iommufd/pages.c
@@ -45,6 +45,8 @@
* last_iova + 1 can overflow. An iopt_pages index will always be much less than
* ULONG_MAX so last_index + 1 cannot overflow.
*/
+#include <linux/dma-buf.h>
+#include <linux/dma-resv.h>
#include <linux/file.h>
#include <linux/highmem.h>
#include <linux/iommu.h>
@@ -53,6 +55,7 @@
#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/sched/mm.h>
+#include <linux/vfio_pci_core.h>
#include "double_span.h"
#include "io_pagetable.h"
@@ -258,6 +261,11 @@ static struct iopt_area *iopt_pages_find_domain_area(struct iopt_pages *pages,
return container_of(node, struct iopt_area, pages_node);
}
+enum batch_kind {
+ BATCH_CPU_MEMORY = 0,
+ BATCH_MMIO,
+};
+
/*
* A simple datastructure to hold a vector of PFNs, optimized for contiguous
* PFNs. This is used as a temporary holding memory for shuttling pfns from one
@@ -271,7 +279,9 @@ struct pfn_batch {
unsigned int array_size;
unsigned int end;
unsigned int total_pfns;
+ enum batch_kind kind;
};
+enum { MAX_NPFNS = type_max(typeof(((struct pfn_batch *)0)->npfns[0])) };
static void batch_clear(struct pfn_batch *batch)
{
@@ -348,11 +358,17 @@ static void batch_destroy(struct pfn_batch *batch, void *backup)
}
static bool batch_add_pfn_num(struct pfn_batch *batch, unsigned long pfn,
- u32 nr)
+ u32 nr, enum batch_kind kind)
{
- const unsigned int MAX_NPFNS = type_max(typeof(*batch->npfns));
unsigned int end = batch->end;
+ if (batch->kind != kind) {
+ /* One kind per batch */
+ if (batch->end != 0)
+ return false;
+ batch->kind = kind;
+ }
+
if (end && pfn == batch->pfns[end - 1] + batch->npfns[end - 1] &&
nr <= MAX_NPFNS - batch->npfns[end - 1]) {
batch->npfns[end - 1] += nr;
@@ -379,7 +395,7 @@ static void batch_remove_pfn_num(struct pfn_batch *batch, unsigned long nr)
/* true if the pfn was added, false otherwise */
static bool batch_add_pfn(struct pfn_batch *batch, unsigned long pfn)
{
- return batch_add_pfn_num(batch, pfn, 1);
+ return batch_add_pfn_num(batch, pfn, 1, BATCH_CPU_MEMORY);
}
/*
@@ -492,6 +508,7 @@ static int batch_to_domain(struct pfn_batch *batch, struct iommu_domain *domain,
{
bool disable_large_pages = area->iopt->disable_large_pages;
unsigned long last_iova = iopt_area_last_iova(area);
+ int iommu_prot = area->iommu_prot;
unsigned int page_offset = 0;
unsigned long start_iova;
unsigned long next_iova;
@@ -499,6 +516,11 @@ static int batch_to_domain(struct pfn_batch *batch, struct iommu_domain *domain,
unsigned long iova;
int rc;
+ if (batch->kind == BATCH_MMIO) {
+ iommu_prot &= ~IOMMU_CACHE;
+ iommu_prot |= IOMMU_MMIO;
+ }
+
/* The first index might be a partial page */
if (start_index == iopt_area_index(area))
page_offset = area->page_offset;
@@ -512,11 +534,11 @@ static int batch_to_domain(struct pfn_batch *batch, struct iommu_domain *domain,
rc = batch_iommu_map_small(
domain, iova,
PFN_PHYS(batch->pfns[cur]) + page_offset,
- next_iova - iova, area->iommu_prot);
+ next_iova - iova, iommu_prot);
else
rc = iommu_map(domain, iova,
PFN_PHYS(batch->pfns[cur]) + page_offset,
- next_iova - iova, area->iommu_prot,
+ next_iova - iova, iommu_prot,
GFP_KERNEL_ACCOUNT);
if (rc)
goto err_unmap;
@@ -652,7 +674,7 @@ static int batch_from_folios(struct pfn_batch *batch, struct folio ***folios_p,
nr = min(nr, npages);
npages -= nr;
- if (!batch_add_pfn_num(batch, pfn, nr))
+ if (!batch_add_pfn_num(batch, pfn, nr, BATCH_CPU_MEMORY))
break;
if (nr > 1) {
rc = folio_add_pins(folio, nr - 1);
@@ -1054,6 +1076,41 @@ static int pfn_reader_user_update_pinned(struct pfn_reader_user *user,
return iopt_pages_update_pinned(pages, npages, inc, user);
}
+struct pfn_reader_dmabuf {
+ struct dma_buf_phys_vec phys;
+ unsigned long start_offset;
+};
+
+static int pfn_reader_dmabuf_init(struct pfn_reader_dmabuf *dmabuf,
+ struct iopt_pages *pages)
+{
+ /* Callers must not get here if the dmabuf was already revoked */
+ if (WARN_ON(iopt_dmabuf_revoked(pages)))
+ return -EINVAL;
+
+ dmabuf->phys = pages->dmabuf.phys;
+ dmabuf->start_offset = pages->dmabuf.start;
+ return 0;
+}
+
+static int pfn_reader_fill_dmabuf(struct pfn_reader_dmabuf *dmabuf,
+ struct pfn_batch *batch,
+ unsigned long start_index,
+ unsigned long last_index)
+{
+ unsigned long start = dmabuf->start_offset + start_index * PAGE_SIZE;
+
+ /*
+ * start/last_index and start are all PAGE_SIZE aligned, the batch is
+ * always filled using page size aligned PFNs just like the other types.
+ * If the dmabuf has been sliced on a sub page offset then the common
+ * batch to domain code will adjust it before mapping to the domain.
+ */
+ batch_add_pfn_num(batch, PHYS_PFN(dmabuf->phys.paddr + start),
+ last_index - start_index + 1, BATCH_MMIO);
+ return 0;
+}
+
/*
* PFNs are stored in three places, in order of preference:
* - The iopt_pages xarray. This is only populated if there is a
@@ -1072,7 +1129,10 @@ struct pfn_reader {
unsigned long batch_end_index;
unsigned long last_index;
- struct pfn_reader_user user;
+ union {
+ struct pfn_reader_user user;
+ struct pfn_reader_dmabuf dmabuf;
+ };
};
static int pfn_reader_update_pinned(struct pfn_reader *pfns)
@@ -1108,7 +1168,7 @@ static int pfn_reader_fill_span(struct pfn_reader *pfns)
{
struct interval_tree_double_span_iter *span = &pfns->span;
unsigned long start_index = pfns->batch_end_index;
- struct pfn_reader_user *user = &pfns->user;
+ struct pfn_reader_user *user;
unsigned long npages;
struct iopt_area *area;
int rc;
@@ -1140,8 +1200,13 @@ static int pfn_reader_fill_span(struct pfn_reader *pfns)
return 0;
}
- if (start_index >= pfns->user.upages_end) {
- rc = pfn_reader_user_pin(&pfns->user, pfns->pages, start_index,
+ if (iopt_is_dmabuf(pfns->pages))
+ return pfn_reader_fill_dmabuf(&pfns->dmabuf, &pfns->batch,
+ start_index, span->last_hole);
+
+ user = &pfns->user;
+ if (start_index >= user->upages_end) {
+ rc = pfn_reader_user_pin(user, pfns->pages, start_index,
span->last_hole);
if (rc)
return rc;
@@ -1209,7 +1274,10 @@ static int pfn_reader_init(struct pfn_reader *pfns, struct iopt_pages *pages,
pfns->batch_start_index = start_index;
pfns->batch_end_index = start_index;
pfns->last_index = last_index;
- pfn_reader_user_init(&pfns->user, pages);
+ if (iopt_is_dmabuf(pages))
+ pfn_reader_dmabuf_init(&pfns->dmabuf, pages);
+ else
+ pfn_reader_user_init(&pfns->user, pages);
rc = batch_init(&pfns->batch, last_index - start_index + 1);
if (rc)
return rc;
@@ -1230,8 +1298,12 @@ static int pfn_reader_init(struct pfn_reader *pfns, struct iopt_pages *pages,
static void pfn_reader_release_pins(struct pfn_reader *pfns)
{
struct iopt_pages *pages = pfns->pages;
- struct pfn_reader_user *user = &pfns->user;
+ struct pfn_reader_user *user;
+
+ if (iopt_is_dmabuf(pages))
+ return;
+ user = &pfns->user;
if (user->upages_end > pfns->batch_end_index) {
/* Any pages not transferred to the batch are just unpinned */
@@ -1261,7 +1333,8 @@ static void pfn_reader_destroy(struct pfn_reader *pfns)
struct iopt_pages *pages = pfns->pages;
pfn_reader_release_pins(pfns);
- pfn_reader_user_destroy(&pfns->user, pfns->pages);
+ if (!iopt_is_dmabuf(pfns->pages))
+ pfn_reader_user_destroy(&pfns->user, pfns->pages);
batch_destroy(&pfns->batch, NULL);
WARN_ON(pages->last_npinned != pages->npinned);
}
@@ -1340,26 +1413,234 @@ struct iopt_pages *iopt_alloc_user_pages(void __user *uptr,
return pages;
}
-struct iopt_pages *iopt_alloc_file_pages(struct file *file, unsigned long start,
+struct iopt_pages *iopt_alloc_file_pages(struct file *file,
+ unsigned long start_byte,
+ unsigned long start,
unsigned long length, bool writable)
{
struct iopt_pages *pages;
- unsigned long start_down = ALIGN_DOWN(start, PAGE_SIZE);
- unsigned long end;
- if (length && check_add_overflow(start, length - 1, &end))
- return ERR_PTR(-EOVERFLOW);
-
- pages = iopt_alloc_pages(start - start_down, length, writable);
+ pages = iopt_alloc_pages(start_byte, length, writable);
if (IS_ERR(pages))
return pages;
pages->file = get_file(file);
- pages->start = start_down;
+ pages->start = start - start_byte;
pages->type = IOPT_ADDRESS_FILE;
return pages;
}
+static void iopt_revoke_notify(struct dma_buf_attachment *attach)
+{
+ struct iopt_pages *pages = attach->importer_priv;
+ struct iopt_pages_dmabuf_track *track;
+
+ guard(mutex)(&pages->mutex);
+ if (iopt_dmabuf_revoked(pages))
+ return;
+
+ list_for_each_entry(track, &pages->dmabuf.tracker, elm) {
+ struct iopt_area *area = track->area;
+
+ iopt_area_unmap_domain_range(area, track->domain,
+ iopt_area_index(area),
+ iopt_area_last_index(area));
+ }
+ pages->dmabuf.phys.len = 0;
+}
+
+static struct dma_buf_attach_ops iopt_dmabuf_attach_revoke_ops = {
+ .allow_peer2peer = true,
+ .move_notify = iopt_revoke_notify,
+};
+
+/*
+ * iommufd and vfio have a circular dependency. Future work for a phys
+ * based private interconnect will remove this.
+ */
+static int
+sym_vfio_pci_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
+ struct dma_buf_phys_vec *phys)
+{
+ typeof(&vfio_pci_dma_buf_iommufd_map) fn;
+ int rc;
+
+ rc = iommufd_test_dma_buf_iommufd_map(attachment, phys);
+ if (rc != -EOPNOTSUPP)
+ return rc;
+
+ if (!IS_ENABLED(CONFIG_VFIO_PCI_DMABUF))
+ return -EOPNOTSUPP;
+
+ fn = symbol_get(vfio_pci_dma_buf_iommufd_map);
+ if (!fn)
+ return -EOPNOTSUPP;
+ rc = fn(attachment, phys);
+ symbol_put(vfio_pci_dma_buf_iommufd_map);
+ return rc;
+}
+
+static int iopt_map_dmabuf(struct iommufd_ctx *ictx, struct iopt_pages *pages,
+ struct dma_buf *dmabuf)
+{
+ struct dma_buf_attachment *attach;
+ int rc;
+
+ attach = dma_buf_dynamic_attach(dmabuf, iommufd_global_device(),
+ &iopt_dmabuf_attach_revoke_ops, pages);
+ if (IS_ERR(attach))
+ return PTR_ERR(attach);
+
+ dma_resv_lock(dmabuf->resv, NULL);
+ /*
+ * Lock ordering requires the mutex to be taken inside the reservation,
+ * make sure lockdep sees this.
+ */
+ if (IS_ENABLED(CONFIG_LOCKDEP)) {
+ mutex_lock(&pages->mutex);
+ mutex_unlock(&pages->mutex);
+ }
+
+ rc = sym_vfio_pci_dma_buf_iommufd_map(attach, &pages->dmabuf.phys);
+ if (rc)
+ goto err_detach;
+
+ dma_resv_unlock(dmabuf->resv);
+
+ /* On success iopt_release_pages() will detach and put the dmabuf. */
+ pages->dmabuf.attach = attach;
+ return 0;
+
+err_detach:
+ dma_resv_unlock(dmabuf->resv);
+ dma_buf_detach(dmabuf, attach);
+ return rc;
+}
+
+struct iopt_pages *iopt_alloc_dmabuf_pages(struct iommufd_ctx *ictx,
+ struct dma_buf *dmabuf,
+ unsigned long start_byte,
+ unsigned long start,
+ unsigned long length, bool writable)
+{
+ static struct lock_class_key pages_dmabuf_mutex_key;
+ struct iopt_pages *pages;
+ int rc;
+
+ if (!IS_ENABLED(CONFIG_DMA_SHARED_BUFFER))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (dmabuf->size <= (start + length - 1) ||
+ length / PAGE_SIZE >= MAX_NPFNS)
+ return ERR_PTR(-EINVAL);
+
+ pages = iopt_alloc_pages(start_byte, length, writable);
+ if (IS_ERR(pages))
+ return pages;
+
+ /*
+ * The mmap_lock can be held when obtaining the dmabuf reservation lock
+ * which creates a locking cycle with the pages mutex which is held
+ * while obtaining the mmap_lock. This locking path is not present for
+ * IOPT_ADDRESS_DMABUF so split the lock class.
+ */
+ lockdep_set_class(&pages->mutex, &pages_dmabuf_mutex_key);
+
+ /* dmabuf does not use pinned page accounting. */
+ pages->account_mode = IOPT_PAGES_ACCOUNT_NONE;
+ pages->type = IOPT_ADDRESS_DMABUF;
+ pages->dmabuf.start = start - start_byte;
+ INIT_LIST_HEAD(&pages->dmabuf.tracker);
+
+ rc = iopt_map_dmabuf(ictx, pages, dmabuf);
+ if (rc) {
+ iopt_put_pages(pages);
+ return ERR_PTR(rc);
+ }
+
+ return pages;
+}
+
+int iopt_dmabuf_track_domain(struct iopt_pages *pages, struct iopt_area *area,
+ struct iommu_domain *domain)
+{
+ struct iopt_pages_dmabuf_track *track;
+
+ lockdep_assert_held(&pages->mutex);
+ if (WARN_ON(!iopt_is_dmabuf(pages)))
+ return -EINVAL;
+
+ list_for_each_entry(track, &pages->dmabuf.tracker, elm)
+ if (WARN_ON(track->domain == domain && track->area == area))
+ return -EINVAL;
+
+ track = kzalloc(sizeof(*track), GFP_KERNEL);
+ if (!track)
+ return -ENOMEM;
+ track->domain = domain;
+ track->area = area;
+ list_add_tail(&track->elm, &pages->dmabuf.tracker);
+
+ return 0;
+}
+
+void iopt_dmabuf_untrack_domain(struct iopt_pages *pages,
+ struct iopt_area *area,
+ struct iommu_domain *domain)
+{
+ struct iopt_pages_dmabuf_track *track;
+
+ lockdep_assert_held(&pages->mutex);
+ WARN_ON(!iopt_is_dmabuf(pages));
+
+ list_for_each_entry(track, &pages->dmabuf.tracker, elm) {
+ if (track->domain == domain && track->area == area) {
+ list_del(&track->elm);
+ kfree(track);
+ return;
+ }
+ }
+ WARN_ON(true);
+}
+
+int iopt_dmabuf_track_all_domains(struct iopt_area *area,
+ struct iopt_pages *pages)
+{
+ struct iopt_pages_dmabuf_track *track;
+ struct iommu_domain *domain;
+ unsigned long index;
+ int rc;
+
+ list_for_each_entry(track, &pages->dmabuf.tracker, elm)
+ if (WARN_ON(track->area == area))
+ return -EINVAL;
+
+ xa_for_each(&area->iopt->domains, index, domain) {
+ rc = iopt_dmabuf_track_domain(pages, area, domain);
+ if (rc)
+ goto err_untrack;
+ }
+ return 0;
+err_untrack:
+ iopt_dmabuf_untrack_all_domains(area, pages);
+ return rc;
+}
+
+void iopt_dmabuf_untrack_all_domains(struct iopt_area *area,
+ struct iopt_pages *pages)
+{
+ struct iopt_pages_dmabuf_track *track;
+ struct iopt_pages_dmabuf_track *tmp;
+
+ list_for_each_entry_safe(track, tmp, &pages->dmabuf.tracker,
+ elm) {
+ if (track->area == area) {
+ list_del(&track->elm);
+ kfree(track);
+ }
+ }
+}
+
void iopt_release_pages(struct kref *kref)
{
struct iopt_pages *pages = container_of(kref, struct iopt_pages, kref);
@@ -1372,8 +1653,15 @@ void iopt_release_pages(struct kref *kref)
mutex_destroy(&pages->mutex);
put_task_struct(pages->source_task);
free_uid(pages->source_user);
- if (pages->type == IOPT_ADDRESS_FILE)
+ if (iopt_is_dmabuf(pages) && pages->dmabuf.attach) {
+ struct dma_buf *dmabuf = pages->dmabuf.attach->dmabuf;
+
+ dma_buf_detach(dmabuf, pages->dmabuf.attach);
+ dma_buf_put(dmabuf);
+ WARN_ON(!list_empty(&pages->dmabuf.tracker));
+ } else if (pages->type == IOPT_ADDRESS_FILE) {
fput(pages->file);
+ }
kfree(pages);
}
@@ -1451,6 +1739,14 @@ static void __iopt_area_unfill_domain(struct iopt_area *area,
lockdep_assert_held(&pages->mutex);
+ if (iopt_is_dmabuf(pages)) {
+ if (WARN_ON(iopt_dmabuf_revoked(pages)))
+ return;
+ iopt_area_unmap_domain_range(area, domain, start_index,
+ last_index);
+ return;
+ }
+
/*
* For security we must not unpin something that is still DMA mapped,
* so this must unmap any IOVA before we go ahead and unpin the pages.
@@ -1526,6 +1822,9 @@ void iopt_area_unmap_domain(struct iopt_area *area, struct iommu_domain *domain)
void iopt_area_unfill_domain(struct iopt_area *area, struct iopt_pages *pages,
struct iommu_domain *domain)
{
+ if (iopt_dmabuf_revoked(pages))
+ return;
+
__iopt_area_unfill_domain(area, pages, domain,
iopt_area_last_index(area));
}
@@ -1546,6 +1845,9 @@ int iopt_area_fill_domain(struct iopt_area *area, struct iommu_domain *domain)
lockdep_assert_held(&area->pages->mutex);
+ if (iopt_dmabuf_revoked(area->pages))
+ return 0;
+
rc = pfn_reader_first(&pfns, area->pages, iopt_area_index(area),
iopt_area_last_index(area));
if (rc)
@@ -1605,33 +1907,44 @@ int iopt_area_fill_domains(struct iopt_area *area, struct iopt_pages *pages)
return 0;
mutex_lock(&pages->mutex);
- rc = pfn_reader_first(&pfns, pages, iopt_area_index(area),
- iopt_area_last_index(area));
- if (rc)
- goto out_unlock;
+ if (iopt_is_dmabuf(pages)) {
+ rc = iopt_dmabuf_track_all_domains(area, pages);
+ if (rc)
+ goto out_unlock;
+ }
- while (!pfn_reader_done(&pfns)) {
- done_first_end_index = pfns.batch_end_index;
- done_all_end_index = pfns.batch_start_index;
- xa_for_each(&area->iopt->domains, index, domain) {
- rc = batch_to_domain(&pfns.batch, domain, area,
- pfns.batch_start_index);
+ if (!iopt_dmabuf_revoked(pages)) {
+ rc = pfn_reader_first(&pfns, pages, iopt_area_index(area),
+ iopt_area_last_index(area));
+ if (rc)
+ goto out_untrack;
+
+ while (!pfn_reader_done(&pfns)) {
+ done_first_end_index = pfns.batch_end_index;
+ done_all_end_index = pfns.batch_start_index;
+ xa_for_each(&area->iopt->domains, index, domain) {
+ rc = batch_to_domain(&pfns.batch, domain, area,
+ pfns.batch_start_index);
+ if (rc)
+ goto out_unmap;
+ }
+ done_all_end_index = done_first_end_index;
+
+ rc = pfn_reader_next(&pfns);
if (rc)
goto out_unmap;
}
- done_all_end_index = done_first_end_index;
-
- rc = pfn_reader_next(&pfns);
+ rc = pfn_reader_update_pinned(&pfns);
if (rc)
goto out_unmap;
+
+ pfn_reader_destroy(&pfns);
}
- rc = pfn_reader_update_pinned(&pfns);
- if (rc)
- goto out_unmap;
area->storage_domain = xa_load(&area->iopt->domains, 0);
interval_tree_insert(&area->pages_node, &pages->domains_itree);
- goto out_destroy;
+ mutex_unlock(&pages->mutex);
+ return 0;
out_unmap:
pfn_reader_release_pins(&pfns);
@@ -1658,8 +1971,10 @@ out_unmap:
end_index);
}
}
-out_destroy:
pfn_reader_destroy(&pfns);
+out_untrack:
+ if (iopt_is_dmabuf(pages))
+ iopt_dmabuf_untrack_all_domains(area, pages);
out_unlock:
mutex_unlock(&pages->mutex);
return rc;
@@ -1685,16 +2000,22 @@ void iopt_area_unfill_domains(struct iopt_area *area, struct iopt_pages *pages)
if (!area->storage_domain)
goto out_unlock;
- xa_for_each(&iopt->domains, index, domain)
- if (domain != area->storage_domain)
+ xa_for_each(&iopt->domains, index, domain) {
+ if (domain == area->storage_domain)
+ continue;
+
+ if (!iopt_dmabuf_revoked(pages))
iopt_area_unmap_domain_range(
area, domain, iopt_area_index(area),
iopt_area_last_index(area));
+ }
if (IS_ENABLED(CONFIG_IOMMUFD_TEST))
WARN_ON(RB_EMPTY_NODE(&area->pages_node.rb));
interval_tree_remove(&area->pages_node, &pages->domains_itree);
iopt_area_unfill_domain(area, pages, area->storage_domain);
+ if (iopt_is_dmabuf(pages))
+ iopt_dmabuf_untrack_all_domains(area, pages);
area->storage_domain = NULL;
out_unlock:
mutex_unlock(&pages->mutex);
@@ -2031,15 +2352,14 @@ int iopt_pages_rw_access(struct iopt_pages *pages, unsigned long start_byte,
if ((flags & IOMMUFD_ACCESS_RW_WRITE) && !pages->writable)
return -EPERM;
- if (pages->type == IOPT_ADDRESS_FILE)
+ if (iopt_is_dmabuf(pages))
+ return -EINVAL;
+
+ if (pages->type != IOPT_ADDRESS_USER)
return iopt_pages_rw_slow(pages, start_index, last_index,
start_byte % PAGE_SIZE, data, length,
flags);
- if (IS_ENABLED(CONFIG_IOMMUFD_TEST) &&
- WARN_ON(pages->type != IOPT_ADDRESS_USER))
- return -EINVAL;
-
if (!(flags & IOMMUFD_ACCESS_RW_KTHREAD) && change_mm) {
if (start_index == last_index)
return iopt_pages_rw_page(pages, start_index,
diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index de178827a078..c4322fd26f93 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -5,6 +5,8 @@
*/
#include <linux/anon_inodes.h>
#include <linux/debugfs.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-resv.h>
#include <linux/fault-inject.h>
#include <linux/file.h>
#include <linux/iommu.h>
@@ -12,6 +14,8 @@
#include <linux/slab.h>
#include <linux/xarray.h>
#include <uapi/linux/iommufd.h>
+#include <linux/generic_pt/iommu.h>
+#include "../iommu-pages.h"
#include "../iommu-priv.h"
#include "io_pagetable.h"
@@ -41,21 +45,6 @@ static DEFINE_IDA(mock_dev_ida);
enum {
MOCK_DIRTY_TRACK = 1,
- MOCK_IO_PAGE_SIZE = PAGE_SIZE / 2,
- MOCK_HUGE_PAGE_SIZE = 512 * MOCK_IO_PAGE_SIZE,
-
- /*
- * Like a real page table alignment requires the low bits of the address
- * to be zero. xarray also requires the high bit to be zero, so we store
- * the pfns shifted. The upper bits are used for metadata.
- */
- MOCK_PFN_MASK = ULONG_MAX / MOCK_IO_PAGE_SIZE,
-
- _MOCK_PFN_START = MOCK_PFN_MASK + 1,
- MOCK_PFN_START_IOVA = _MOCK_PFN_START,
- MOCK_PFN_LAST_IOVA = _MOCK_PFN_START,
- MOCK_PFN_DIRTY_IOVA = _MOCK_PFN_START << 1,
- MOCK_PFN_HUGE_IOVA = _MOCK_PFN_START << 2,
};
static int mock_dev_enable_iopf(struct device *dev, struct iommu_domain *domain);
@@ -124,10 +113,15 @@ void iommufd_test_syz_conv_iova_id(struct iommufd_ucmd *ucmd,
}
struct mock_iommu_domain {
+ union {
+ struct iommu_domain domain;
+ struct pt_iommu iommu;
+ struct pt_iommu_amdv1 amdv1;
+ };
unsigned long flags;
- struct iommu_domain domain;
- struct xarray pfns;
};
+PT_IOMMU_CHECK_DOMAIN(struct mock_iommu_domain, iommu, domain);
+PT_IOMMU_CHECK_DOMAIN(struct mock_iommu_domain, amdv1.iommu, domain);
static inline struct mock_iommu_domain *
to_mock_domain(struct iommu_domain *domain)
@@ -216,7 +210,7 @@ static inline struct selftest_obj *to_selftest_obj(struct iommufd_object *obj)
}
static int mock_domain_nop_attach(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct mock_dev *mdev = to_mock_dev(dev);
struct mock_viommu *new_viommu = NULL;
@@ -344,74 +338,6 @@ static int mock_domain_set_dirty_tracking(struct iommu_domain *domain,
return 0;
}
-static bool mock_test_and_clear_dirty(struct mock_iommu_domain *mock,
- unsigned long iova, size_t page_size,
- unsigned long flags)
-{
- unsigned long cur, end = iova + page_size - 1;
- bool dirty = false;
- void *ent, *old;
-
- for (cur = iova; cur < end; cur += MOCK_IO_PAGE_SIZE) {
- ent = xa_load(&mock->pfns, cur / MOCK_IO_PAGE_SIZE);
- if (!ent || !(xa_to_value(ent) & MOCK_PFN_DIRTY_IOVA))
- continue;
-
- dirty = true;
- /* Clear dirty */
- if (!(flags & IOMMU_DIRTY_NO_CLEAR)) {
- unsigned long val;
-
- val = xa_to_value(ent) & ~MOCK_PFN_DIRTY_IOVA;
- old = xa_store(&mock->pfns, cur / MOCK_IO_PAGE_SIZE,
- xa_mk_value(val), GFP_KERNEL);
- WARN_ON_ONCE(ent != old);
- }
- }
-
- return dirty;
-}
-
-static int mock_domain_read_and_clear_dirty(struct iommu_domain *domain,
- unsigned long iova, size_t size,
- unsigned long flags,
- struct iommu_dirty_bitmap *dirty)
-{
- struct mock_iommu_domain *mock = to_mock_domain(domain);
- unsigned long end = iova + size;
- void *ent;
-
- if (!(mock->flags & MOCK_DIRTY_TRACK) && dirty->bitmap)
- return -EINVAL;
-
- do {
- unsigned long pgsize = MOCK_IO_PAGE_SIZE;
- unsigned long head;
-
- ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
- if (!ent) {
- iova += pgsize;
- continue;
- }
-
- if (xa_to_value(ent) & MOCK_PFN_HUGE_IOVA)
- pgsize = MOCK_HUGE_PAGE_SIZE;
- head = iova & ~(pgsize - 1);
-
- /* Clear dirty */
- if (mock_test_and_clear_dirty(mock, head, pgsize, flags))
- iommu_dirty_bitmap_record(dirty, iova, pgsize);
- iova += pgsize;
- } while (iova < end);
-
- return 0;
-}
-
-static const struct iommu_dirty_ops dirty_ops = {
- .set_dirty_tracking = mock_domain_set_dirty_tracking,
- .read_and_clear_dirty = mock_domain_read_and_clear_dirty,
-};
-
static struct mock_iommu_domain_nested *
__mock_domain_alloc_nested(const struct iommu_user_data *user_data)
{
@@ -446,7 +372,7 @@ mock_domain_alloc_nested(struct device *dev, struct iommu_domain *parent,
if (flags & ~IOMMU_HWPT_ALLOC_PASID)
return ERR_PTR(-EOPNOTSUPP);
- if (!parent || parent->ops != mock_ops.default_domain_ops)
+ if (!parent || !(parent->type & __IOMMU_DOMAIN_PAGING))
return ERR_PTR(-EINVAL);
mock_parent = to_mock_domain(parent);
@@ -459,159 +385,170 @@ mock_domain_alloc_nested(struct device *dev, struct iommu_domain *parent,
return &mock_nested->domain;
}
-static struct iommu_domain *
-mock_domain_alloc_paging_flags(struct device *dev, u32 flags,
- const struct iommu_user_data *user_data)
-{
- bool has_dirty_flag = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
- const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
- IOMMU_HWPT_ALLOC_NEST_PARENT |
- IOMMU_HWPT_ALLOC_PASID;
- struct mock_dev *mdev = to_mock_dev(dev);
- bool no_dirty_ops = mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY;
- struct mock_iommu_domain *mock;
-
- if (user_data)
- return ERR_PTR(-EOPNOTSUPP);
- if ((flags & ~PAGING_FLAGS) || (has_dirty_flag && no_dirty_ops))
- return ERR_PTR(-EOPNOTSUPP);
-
- mock = kzalloc(sizeof(*mock), GFP_KERNEL);
- if (!mock)
- return ERR_PTR(-ENOMEM);
- mock->domain.geometry.aperture_start = MOCK_APERTURE_START;
- mock->domain.geometry.aperture_end = MOCK_APERTURE_LAST;
- mock->domain.pgsize_bitmap = MOCK_IO_PAGE_SIZE;
- if (dev && mdev->flags & MOCK_FLAGS_DEVICE_HUGE_IOVA)
- mock->domain.pgsize_bitmap |= MOCK_HUGE_PAGE_SIZE;
- mock->domain.ops = mock_ops.default_domain_ops;
- mock->domain.type = IOMMU_DOMAIN_UNMANAGED;
- xa_init(&mock->pfns);
-
- if (has_dirty_flag)
- mock->domain.dirty_ops = &dirty_ops;
- return &mock->domain;
-}
-
static void mock_domain_free(struct iommu_domain *domain)
{
struct mock_iommu_domain *mock = to_mock_domain(domain);
- WARN_ON(!xa_empty(&mock->pfns));
+ pt_iommu_deinit(&mock->iommu);
kfree(mock);
}
-static int mock_domain_map_pages(struct iommu_domain *domain,
- unsigned long iova, phys_addr_t paddr,
- size_t pgsize, size_t pgcount, int prot,
- gfp_t gfp, size_t *mapped)
+static void mock_iotlb_sync(struct iommu_domain *domain,
+ struct iommu_iotlb_gather *gather)
{
- struct mock_iommu_domain *mock = to_mock_domain(domain);
- unsigned long flags = MOCK_PFN_START_IOVA;
- unsigned long start_iova = iova;
+ iommu_put_pages_list(&gather->freelist);
+}
- /*
- * xarray does not reliably work with fault injection because it does a
- * retry allocation, so put our own failure point.
- */
- if (iommufd_should_fail())
- return -ENOENT;
+static const struct iommu_domain_ops amdv1_mock_ops = {
+ IOMMU_PT_DOMAIN_OPS(amdv1_mock),
+ .free = mock_domain_free,
+ .attach_dev = mock_domain_nop_attach,
+ .set_dev_pasid = mock_domain_set_dev_pasid_nop,
+ .iotlb_sync = &mock_iotlb_sync,
+};
- WARN_ON(iova % MOCK_IO_PAGE_SIZE);
- WARN_ON(pgsize % MOCK_IO_PAGE_SIZE);
- for (; pgcount; pgcount--) {
- size_t cur;
+static const struct iommu_domain_ops amdv1_mock_huge_ops = {
+ IOMMU_PT_DOMAIN_OPS(amdv1_mock),
+ .free = mock_domain_free,
+ .attach_dev = mock_domain_nop_attach,
+ .set_dev_pasid = mock_domain_set_dev_pasid_nop,
+ .iotlb_sync = &mock_iotlb_sync,
+};
+#undef pt_iommu_amdv1_mock_map_pages
- for (cur = 0; cur != pgsize; cur += MOCK_IO_PAGE_SIZE) {
- void *old;
+static const struct iommu_dirty_ops amdv1_mock_dirty_ops = {
+ IOMMU_PT_DIRTY_OPS(amdv1_mock),
+ .set_dirty_tracking = mock_domain_set_dirty_tracking,
+};
- if (pgcount == 1 && cur + MOCK_IO_PAGE_SIZE == pgsize)
- flags = MOCK_PFN_LAST_IOVA;
- if (pgsize != MOCK_IO_PAGE_SIZE) {
- flags |= MOCK_PFN_HUGE_IOVA;
- }
- old = xa_store(&mock->pfns, iova / MOCK_IO_PAGE_SIZE,
- xa_mk_value((paddr / MOCK_IO_PAGE_SIZE) |
- flags),
- gfp);
- if (xa_is_err(old)) {
- for (; start_iova != iova;
- start_iova += MOCK_IO_PAGE_SIZE)
- xa_erase(&mock->pfns,
- start_iova /
- MOCK_IO_PAGE_SIZE);
- return xa_err(old);
- }
- WARN_ON(old);
- iova += MOCK_IO_PAGE_SIZE;
- paddr += MOCK_IO_PAGE_SIZE;
- *mapped += MOCK_IO_PAGE_SIZE;
- flags = 0;
- }
- }
- return 0;
-}
+static const struct iommu_domain_ops amdv1_ops = {
+ IOMMU_PT_DOMAIN_OPS(amdv1),
+ .free = mock_domain_free,
+ .attach_dev = mock_domain_nop_attach,
+ .set_dev_pasid = mock_domain_set_dev_pasid_nop,
+ .iotlb_sync = &mock_iotlb_sync,
+};
-static size_t mock_domain_unmap_pages(struct iommu_domain *domain,
- unsigned long iova, size_t pgsize,
- size_t pgcount,
- struct iommu_iotlb_gather *iotlb_gather)
+static const struct iommu_dirty_ops amdv1_dirty_ops = {
+ IOMMU_PT_DIRTY_OPS(amdv1),
+ .set_dirty_tracking = mock_domain_set_dirty_tracking,
+};
+
+static struct mock_iommu_domain *
+mock_domain_alloc_pgtable(struct device *dev,
+ const struct iommu_hwpt_selftest *user_cfg, u32 flags)
{
- struct mock_iommu_domain *mock = to_mock_domain(domain);
- bool first = true;
- size_t ret = 0;
- void *ent;
+ struct mock_iommu_domain *mock;
+ int rc;
- WARN_ON(iova % MOCK_IO_PAGE_SIZE);
- WARN_ON(pgsize % MOCK_IO_PAGE_SIZE);
+ mock = kzalloc(sizeof(*mock), GFP_KERNEL);
+ if (!mock)
+ return ERR_PTR(-ENOMEM);
+ mock->domain.type = IOMMU_DOMAIN_UNMANAGED;
- for (; pgcount; pgcount--) {
- size_t cur;
+ mock->amdv1.iommu.nid = NUMA_NO_NODE;
+
+ switch (user_cfg->pagetable_type) {
+ case MOCK_IOMMUPT_DEFAULT:
+ case MOCK_IOMMUPT_HUGE: {
+ struct pt_iommu_amdv1_cfg cfg = {};
+
+ /* The mock version has a 2k page size */
+ cfg.common.hw_max_vasz_lg2 = 56;
+ cfg.common.hw_max_oasz_lg2 = 51;
+ cfg.starting_level = 2;
+ if (user_cfg->pagetable_type == MOCK_IOMMUPT_HUGE)
+ mock->domain.ops = &amdv1_mock_huge_ops;
+ else
+ mock->domain.ops = &amdv1_mock_ops;
+ rc = pt_iommu_amdv1_mock_init(&mock->amdv1, &cfg, GFP_KERNEL);
+ if (rc)
+ goto err_free;
+
+ /*
+ * In huge mode userspace should only provide huge pages, we
+ * have to include PAGE_SIZE for the domain to be accepted by
+ * iommufd.
+ */
+ if (user_cfg->pagetable_type == MOCK_IOMMUPT_HUGE)
+ mock->domain.pgsize_bitmap = MOCK_HUGE_PAGE_SIZE |
+ PAGE_SIZE;
+ if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING)
+ mock->domain.dirty_ops = &amdv1_mock_dirty_ops;
+ break;
+ }
- for (cur = 0; cur != pgsize; cur += MOCK_IO_PAGE_SIZE) {
- ent = xa_erase(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
+ case MOCK_IOMMUPT_AMDV1: {
+ struct pt_iommu_amdv1_cfg cfg = {};
+
+ cfg.common.hw_max_vasz_lg2 = 64;
+ cfg.common.hw_max_oasz_lg2 = 52;
+ cfg.common.features = BIT(PT_FEAT_DYNAMIC_TOP) |
+ BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) |
+ BIT(PT_FEAT_AMDV1_FORCE_COHERENCE);
+ cfg.starting_level = 2;
+ mock->domain.ops = &amdv1_ops;
+ rc = pt_iommu_amdv1_init(&mock->amdv1, &cfg, GFP_KERNEL);
+ if (rc)
+ goto err_free;
+ if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING)
+ mock->domain.dirty_ops = &amdv1_dirty_ops;
+ break;
+ }
+ default:
+ rc = -EOPNOTSUPP;
+ goto err_free;
+ }
- /*
- * iommufd generates unmaps that must be a strict
- * superset of the map's performend So every
- * starting/ending IOVA should have been an iova passed
- * to map.
- *
- * This simple logic doesn't work when the HUGE_PAGE is
- * turned on since the core code will automatically
- * switch between the two page sizes creating a break in
- * the unmap calls. The break can land in the middle of
- * contiguous IOVA.
- */
- if (!(domain->pgsize_bitmap & MOCK_HUGE_PAGE_SIZE)) {
- if (first) {
- WARN_ON(ent && !(xa_to_value(ent) &
- MOCK_PFN_START_IOVA));
- first = false;
- }
- if (pgcount == 1 &&
- cur + MOCK_IO_PAGE_SIZE == pgsize)
- WARN_ON(ent && !(xa_to_value(ent) &
- MOCK_PFN_LAST_IOVA));
- }
+ /*
+ * Override the real aperture to the MOCK aperture for test purposes.
+ */
+ if (user_cfg->pagetable_type == MOCK_IOMMUPT_DEFAULT) {
+ WARN_ON(mock->domain.geometry.aperture_start != 0);
+ WARN_ON(mock->domain.geometry.aperture_end < MOCK_APERTURE_LAST);
- iova += MOCK_IO_PAGE_SIZE;
- ret += MOCK_IO_PAGE_SIZE;
- }
+ mock->domain.geometry.aperture_start = MOCK_APERTURE_START;
+ mock->domain.geometry.aperture_end = MOCK_APERTURE_LAST;
}
- return ret;
+
+ return mock;
+err_free:
+ kfree(mock);
+ return ERR_PTR(rc);
}
-static phys_addr_t mock_domain_iova_to_phys(struct iommu_domain *domain,
- dma_addr_t iova)
+static struct iommu_domain *
+mock_domain_alloc_paging_flags(struct device *dev, u32 flags,
+ const struct iommu_user_data *user_data)
{
- struct mock_iommu_domain *mock = to_mock_domain(domain);
- void *ent;
+ bool has_dirty_flag = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
+ const u32 PAGING_FLAGS = IOMMU_HWPT_ALLOC_DIRTY_TRACKING |
+ IOMMU_HWPT_ALLOC_NEST_PARENT |
+ IOMMU_HWPT_ALLOC_PASID;
+ struct mock_dev *mdev = to_mock_dev(dev);
+ bool no_dirty_ops = mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY;
+ struct iommu_hwpt_selftest user_cfg = {};
+ struct mock_iommu_domain *mock;
+ int rc;
- WARN_ON(iova % MOCK_IO_PAGE_SIZE);
- ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
- WARN_ON(!ent);
- return (xa_to_value(ent) & MOCK_PFN_MASK) * MOCK_IO_PAGE_SIZE;
+ if ((flags & ~PAGING_FLAGS) || (has_dirty_flag && no_dirty_ops))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (user_data && (user_data->type != IOMMU_HWPT_DATA_SELFTEST &&
+ user_data->type != IOMMU_HWPT_DATA_NONE))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ if (user_data) {
+ rc = iommu_copy_struct_from_user(
+ &user_cfg, user_data, IOMMU_HWPT_DATA_SELFTEST, iotlb);
+ if (rc)
+ return ERR_PTR(rc);
+ }
+
+ mock = mock_domain_alloc_pgtable(dev, &user_cfg, flags);
+ if (IS_ERR(mock))
+ return ERR_CAST(mock);
+ return &mock->domain;
}
static bool mock_domain_capable(struct device *dev, enum iommu_cap cap)
@@ -955,15 +892,6 @@ static const struct iommu_ops mock_ops = {
.user_pasid_table = true,
.get_viommu_size = mock_get_viommu_size,
.viommu_init = mock_viommu_init,
- .default_domain_ops =
- &(struct iommu_domain_ops){
- .free = mock_domain_free,
- .attach_dev = mock_domain_nop_attach,
- .map_pages = mock_domain_map_pages,
- .unmap_pages = mock_domain_unmap_pages,
- .iova_to_phys = mock_domain_iova_to_phys,
- .set_dev_pasid = mock_domain_set_dev_pasid_nop,
- },
};
static void mock_domain_free_nested(struct iommu_domain *domain)
@@ -1047,7 +975,7 @@ get_md_pagetable(struct iommufd_ucmd *ucmd, u32 mockpt_id,
if (IS_ERR(hwpt))
return hwpt;
if (hwpt->domain->type != IOMMU_DOMAIN_UNMANAGED ||
- hwpt->domain->ops != mock_ops.default_domain_ops) {
+ hwpt->domain->owner != &mock_ops) {
iommufd_put_object(ucmd->ictx, &hwpt->obj);
return ERR_PTR(-EINVAL);
}
@@ -1088,7 +1016,6 @@ static struct mock_dev *mock_dev_create(unsigned long dev_flags)
{},
};
const u32 valid_flags = MOCK_FLAGS_DEVICE_NO_DIRTY |
- MOCK_FLAGS_DEVICE_HUGE_IOVA |
MOCK_FLAGS_DEVICE_PASID;
struct mock_dev *mdev;
int rc, i;
@@ -1277,23 +1204,25 @@ static int iommufd_test_md_check_pa(struct iommufd_ucmd *ucmd,
{
struct iommufd_hw_pagetable *hwpt;
struct mock_iommu_domain *mock;
+ unsigned int page_size;
uintptr_t end;
int rc;
- if (iova % MOCK_IO_PAGE_SIZE || length % MOCK_IO_PAGE_SIZE ||
- (uintptr_t)uptr % MOCK_IO_PAGE_SIZE ||
- check_add_overflow((uintptr_t)uptr, (uintptr_t)length, &end))
- return -EINVAL;
-
hwpt = get_md_pagetable(ucmd, mockpt_id, &mock);
if (IS_ERR(hwpt))
return PTR_ERR(hwpt);
- for (; length; length -= MOCK_IO_PAGE_SIZE) {
+ page_size = 1 << __ffs(mock->domain.pgsize_bitmap);
+ if (iova % page_size || length % page_size ||
+ (uintptr_t)uptr % page_size ||
+ check_add_overflow((uintptr_t)uptr, (uintptr_t)length, &end))
+ return -EINVAL;
+
+ for (; length; length -= page_size) {
struct page *pages[1];
+ phys_addr_t io_phys;
unsigned long pfn;
long npages;
- void *ent;
npages = get_user_pages_fast((uintptr_t)uptr & PAGE_MASK, 1, 0,
pages);
@@ -1308,15 +1237,14 @@ static int iommufd_test_md_check_pa(struct iommufd_ucmd *ucmd,
pfn = page_to_pfn(pages[0]);
put_page(pages[0]);
- ent = xa_load(&mock->pfns, iova / MOCK_IO_PAGE_SIZE);
- if (!ent ||
- (xa_to_value(ent) & MOCK_PFN_MASK) * MOCK_IO_PAGE_SIZE !=
- pfn * PAGE_SIZE + ((uintptr_t)uptr % PAGE_SIZE)) {
+ io_phys = mock->domain.ops->iova_to_phys(&mock->domain, iova);
+ if (io_phys !=
+ pfn * PAGE_SIZE + ((uintptr_t)uptr % PAGE_SIZE)) {
rc = -EINVAL;
goto out_put;
}
- iova += MOCK_IO_PAGE_SIZE;
- uptr += MOCK_IO_PAGE_SIZE;
+ iova += page_size;
+ uptr += page_size;
}
rc = 0;
@@ -1795,7 +1723,7 @@ static int iommufd_test_dirty(struct iommufd_ucmd *ucmd, unsigned int mockpt_id,
if (IS_ERR(hwpt))
return PTR_ERR(hwpt);
- if (!(mock->flags & MOCK_DIRTY_TRACK)) {
+ if (!(mock->flags & MOCK_DIRTY_TRACK) || !mock->iommu.ops->set_dirty) {
rc = -EINVAL;
goto out_put;
}
@@ -1814,22 +1742,10 @@ static int iommufd_test_dirty(struct iommufd_ucmd *ucmd, unsigned int mockpt_id,
}
for (i = 0; i < max; i++) {
- unsigned long cur = iova + i * page_size;
- void *ent, *old;
-
if (!test_bit(i, (unsigned long *)tmp))
continue;
-
- ent = xa_load(&mock->pfns, cur / page_size);
- if (ent) {
- unsigned long val;
-
- val = xa_to_value(ent) | MOCK_PFN_DIRTY_IOVA;
- old = xa_store(&mock->pfns, cur / page_size,
- xa_mk_value(val), GFP_KERNEL);
- WARN_ON_ONCE(ent != old);
- count++;
- }
+ mock->iommu.ops->set_dirty(&mock->iommu, iova + i * page_size);
+ count++;
}
cmd->dirty.out_nr_dirty = count;
@@ -2031,6 +1947,140 @@ void iommufd_selftest_destroy(struct iommufd_object *obj)
}
}
+struct iommufd_test_dma_buf {
+ void *memory;
+ size_t length;
+ bool revoked;
+};
+
+static int iommufd_test_dma_buf_attach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+ return 0;
+}
+
+static void iommufd_test_dma_buf_detach(struct dma_buf *dmabuf,
+ struct dma_buf_attachment *attachment)
+{
+}
+
+static struct sg_table *
+iommufd_test_dma_buf_map(struct dma_buf_attachment *attachment,
+ enum dma_data_direction dir)
+{
+ return ERR_PTR(-EOPNOTSUPP);
+}
+
+static void iommufd_test_dma_buf_unmap(struct dma_buf_attachment *attachment,
+ struct sg_table *sgt,
+ enum dma_data_direction dir)
+{
+}
+
+static void iommufd_test_dma_buf_release(struct dma_buf *dmabuf)
+{
+ struct iommufd_test_dma_buf *priv = dmabuf->priv;
+
+ kfree(priv->memory);
+ kfree(priv);
+}
+
+static const struct dma_buf_ops iommufd_test_dmabuf_ops = {
+ .attach = iommufd_test_dma_buf_attach,
+ .detach = iommufd_test_dma_buf_detach,
+ .map_dma_buf = iommufd_test_dma_buf_map,
+ .release = iommufd_test_dma_buf_release,
+ .unmap_dma_buf = iommufd_test_dma_buf_unmap,
+};
+
+int iommufd_test_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
+ struct dma_buf_phys_vec *phys)
+{
+ struct iommufd_test_dma_buf *priv = attachment->dmabuf->priv;
+
+ dma_resv_assert_held(attachment->dmabuf->resv);
+
+ if (attachment->dmabuf->ops != &iommufd_test_dmabuf_ops)
+ return -EOPNOTSUPP;
+
+ if (priv->revoked)
+ return -ENODEV;
+
+ phys->paddr = virt_to_phys(priv->memory);
+ phys->len = priv->length;
+ return 0;
+}
+
+static int iommufd_test_dmabuf_get(struct iommufd_ucmd *ucmd,
+ unsigned int open_flags,
+ size_t len)
+{
+ DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+ struct iommufd_test_dma_buf *priv;
+ struct dma_buf *dmabuf;
+ int rc;
+
+ len = ALIGN(len, PAGE_SIZE);
+ if (len == 0 || len > PAGE_SIZE * 512)
+ return -EINVAL;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->length = len;
+ priv->memory = kzalloc(len, GFP_KERNEL);
+ if (!priv->memory) {
+ rc = -ENOMEM;
+ goto err_free;
+ }
+
+ exp_info.ops = &iommufd_test_dmabuf_ops;
+ exp_info.size = len;
+ exp_info.flags = open_flags;
+ exp_info.priv = priv;
+
+ dmabuf = dma_buf_export(&exp_info);
+ if (IS_ERR(dmabuf)) {
+ rc = PTR_ERR(dmabuf);
+ goto err_free;
+ }
+
+ return dma_buf_fd(dmabuf, open_flags);
+
+err_free:
+ kfree(priv->memory);
+ kfree(priv);
+ return rc;
+}
+
+static int iommufd_test_dmabuf_revoke(struct iommufd_ucmd *ucmd, int fd,
+ bool revoked)
+{
+ struct iommufd_test_dma_buf *priv;
+ struct dma_buf *dmabuf;
+ int rc = 0;
+
+ dmabuf = dma_buf_get(fd);
+ if (IS_ERR(dmabuf))
+ return PTR_ERR(dmabuf);
+
+ if (dmabuf->ops != &iommufd_test_dmabuf_ops) {
+ rc = -EOPNOTSUPP;
+ goto err_put;
+ }
+
+ priv = dmabuf->priv;
+ dma_resv_lock(dmabuf->resv, NULL);
+ priv->revoked = revoked;
+ dma_buf_move_notify(dmabuf);
+ dma_resv_unlock(dmabuf->resv);
+
+err_put:
+ dma_buf_put(dmabuf);
+ return rc;
+}
+
int iommufd_test(struct iommufd_ucmd *ucmd)
{
struct iommu_test_cmd *cmd = ucmd->cmd;
@@ -2109,6 +2159,13 @@ int iommufd_test(struct iommufd_ucmd *ucmd)
return iommufd_test_pasid_detach(ucmd, cmd);
case IOMMU_TEST_OP_PASID_CHECK_HWPT:
return iommufd_test_pasid_check_hwpt(ucmd, cmd);
+ case IOMMU_TEST_OP_DMABUF_GET:
+ return iommufd_test_dmabuf_get(ucmd, cmd->dmabuf_get.open_flags,
+ cmd->dmabuf_get.length);
+ case IOMMU_TEST_OP_DMABUF_REVOKE:
+ return iommufd_test_dmabuf_revoke(ucmd,
+ cmd->dmabuf_revoke.dmabuf_fd,
+ cmd->dmabuf_revoke.revoked);
default:
return -EOPNOTSUPP;
}
@@ -2202,3 +2259,5 @@ void iommufd_test_exit(void)
platform_device_unregister(selftest_iommu_dev);
debugfs_remove_recursive(dbgfs_root);
}
+
+MODULE_IMPORT_NS("GENERIC_PT_IOMMU");
diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
index ffa892f65714..ca848288dbf2 100644
--- a/drivers/iommu/ipmmu-vmsa.c
+++ b/drivers/iommu/ipmmu-vmsa.c
@@ -590,7 +590,7 @@ static void ipmmu_domain_free(struct iommu_domain *io_domain)
}
static int ipmmu_attach_device(struct iommu_domain *io_domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct ipmmu_vmsa_device *mmu = to_ipmmu(dev);
@@ -637,17 +637,17 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
}
static int ipmmu_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
- struct iommu_domain *io_domain = iommu_get_domain_for_dev(dev);
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct ipmmu_vmsa_domain *domain;
unsigned int i;
- if (io_domain == identity_domain || !io_domain)
+ if (old == identity_domain || !old)
return 0;
- domain = to_vmsa_domain(io_domain);
+ domain = to_vmsa_domain(old);
for (i = 0; i < fwspec->num_ids; ++i)
ipmmu_utlb_disable(domain, fwspec->ids[i]);
@@ -720,6 +720,8 @@ static int ipmmu_init_platform_device(struct device *dev,
dev_iommu_priv_set(dev, platform_get_drvdata(ipmmu_pdev));
+ put_device(&ipmmu_pdev->dev);
+
return 0;
}
diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
index 43a61ba021a5..819add75a665 100644
--- a/drivers/iommu/msm_iommu.c
+++ b/drivers/iommu/msm_iommu.c
@@ -391,7 +391,8 @@ static struct iommu_device *msm_iommu_probe_device(struct device *dev)
return &iommu->iommu;
}
-static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev,
+ struct iommu_domain *old)
{
int ret = 0;
unsigned long flags;
@@ -441,19 +442,19 @@ fail:
}
static int msm_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
- struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct msm_priv *priv;
unsigned long flags;
struct msm_iommu_dev *iommu;
struct msm_iommu_ctx_dev *master;
int ret = 0;
- if (domain == identity_domain || !domain)
+ if (old == identity_domain || !old)
return 0;
- priv = to_msm_priv(domain);
+ priv = to_msm_priv(old);
free_io_pgtable_ops(priv->iop);
spin_lock_irqsave(&msm_iommu_lock, flags);
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index 0e0285348d2b..60fcd3d3b5eb 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -139,6 +139,7 @@
/* 2 bits: iommu type */
#define MTK_IOMMU_TYPE_MM (0x0 << 13)
#define MTK_IOMMU_TYPE_INFRA (0x1 << 13)
+#define MTK_IOMMU_TYPE_APU (0x2 << 13)
#define MTK_IOMMU_TYPE_MASK (0x3 << 13)
/* PM and clock always on. e.g. infra iommu */
#define PM_CLK_AO BIT(15)
@@ -147,6 +148,7 @@
#define TF_PORT_TO_ADDR_MT8173 BIT(18)
#define INT_ID_PORT_WIDTH_6 BIT(19)
#define CFG_IFA_MASTER_IN_ATF BIT(20)
+#define DL_WITH_MULTI_LARB BIT(21)
#define MTK_IOMMU_HAS_FLAG_MASK(pdata, _x, mask) \
((((pdata)->flags) & (mask)) == (_x))
@@ -172,6 +174,7 @@ enum mtk_iommu_plat {
M4U_MT8183,
M4U_MT8186,
M4U_MT8188,
+ M4U_MT8189,
M4U_MT8192,
M4U_MT8195,
M4U_MT8365,
@@ -335,6 +338,8 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data, unsigned int ban
*/
#define MTK_IOMMU_4GB_MODE_REMAP_BASE 0x140000000UL
+static LIST_HEAD(apulist); /* List the apu iommu HWs */
+static LIST_HEAD(infralist); /* List the iommu_infra HW */
static LIST_HEAD(m4ulist); /* List all the M4U HWs */
#define for_each_m4u(data, head) list_for_each_entry(data, head, list)
@@ -350,6 +355,15 @@ static const struct mtk_iommu_iova_region single_domain[] = {
#define MT8192_MULTI_REGION_NR (IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) ? \
MT8192_MULTI_REGION_NR_MAX : 1)
+static const struct mtk_iommu_iova_region mt8189_multi_dom_apu[] = {
+ { .iova_base = 0x200000ULL, .size = SZ_512M}, /* APU SECURE */
+#if IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT)
+ { .iova_base = SZ_1G, .size = 0xc0000000}, /* APU CODE */
+ { .iova_base = 0x70000000ULL, .size = 0x12600000}, /* APU VLM */
+ { .iova_base = SZ_4G, .size = SZ_4G * 3}, /* APU VPU */
+#endif
+};
+
static const struct mtk_iommu_iova_region mt8192_multi_dom[MT8192_MULTI_REGION_NR] = {
{ .iova_base = 0x0, .size = MTK_IOMMU_IOVA_SZ_4G}, /* 0 ~ 4G, */
#if IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT)
@@ -705,7 +719,7 @@ static void mtk_iommu_domain_free(struct iommu_domain *domain)
}
static int mtk_iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct mtk_iommu_data *data = dev_iommu_priv_get(dev), *frstdata;
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
@@ -773,12 +787,12 @@ err_unlock:
}
static int mtk_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
- struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct mtk_iommu_data *data = dev_iommu_priv_get(dev);
- if (domain == identity_domain || !domain)
+ if (old == identity_domain || !old)
return 0;
mtk_iommu_config(data, dev, false, 0);
@@ -865,6 +879,7 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev)
struct mtk_iommu_data *data = dev_iommu_priv_get(dev);
struct device_link *link;
struct device *larbdev;
+ unsigned long larbid_msk = 0;
unsigned int larbid, larbidx, i;
if (!MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM))
@@ -872,30 +887,50 @@ static struct iommu_device *mtk_iommu_probe_device(struct device *dev)
/*
* Link the consumer device with the smi-larb device(supplier).
- * The device that connects with each a larb is a independent HW.
- * All the ports in each a device should be in the same larbs.
+ * w/DL_WITH_MULTI_LARB: the master may connect with multi larbs,
+ * we should create device link with each larb.
+ * w/o DL_WITH_MULTI_LARB: the master must connect with one larb,
+ * otherwise fail.
*/
larbid = MTK_M4U_TO_LARB(fwspec->ids[0]);
if (larbid >= MTK_LARB_NR_MAX)
return ERR_PTR(-EINVAL);
+ larbid_msk |= BIT(larbid);
+
for (i = 1; i < fwspec->num_ids; i++) {
larbidx = MTK_M4U_TO_LARB(fwspec->ids[i]);
- if (larbid != larbidx) {
+ if (MTK_IOMMU_HAS_FLAG(data->plat_data, DL_WITH_MULTI_LARB)) {
+ larbid_msk |= BIT(larbidx);
+ } else if (larbid != larbidx) {
dev_err(dev, "Can only use one larb. Fail@larb%d-%d.\n",
larbid, larbidx);
return ERR_PTR(-EINVAL);
}
}
- larbdev = data->larb_imu[larbid].dev;
- if (!larbdev)
- return ERR_PTR(-EINVAL);
- link = device_link_add(dev, larbdev,
- DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
- if (!link)
- dev_err(dev, "Unable to link %s\n", dev_name(larbdev));
+ for_each_set_bit(larbid, &larbid_msk, 32) {
+ larbdev = data->larb_imu[larbid].dev;
+ if (!larbdev)
+ return ERR_PTR(-EINVAL);
+
+ link = device_link_add(dev, larbdev,
+ DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
+ if (!link) {
+ dev_err(dev, "Unable to link %s\n", dev_name(larbdev));
+ goto link_remove;
+ }
+ }
+
return &data->iommu;
+
+link_remove:
+ for_each_set_bit(i, &larbid_msk, larbid) {
+ larbdev = data->larb_imu[i].dev;
+ device_link_remove(dev, larbdev);
+ }
+
+ return ERR_PTR(-ENODEV);
}
static void mtk_iommu_release_device(struct device *dev)
@@ -903,11 +938,19 @@ static void mtk_iommu_release_device(struct device *dev)
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct mtk_iommu_data *data;
struct device *larbdev;
- unsigned int larbid;
+ unsigned int larbid, i;
+ unsigned long larbid_msk = 0;
data = dev_iommu_priv_get(dev);
- if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) {
- larbid = MTK_M4U_TO_LARB(fwspec->ids[0]);
+ if (!MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM))
+ return;
+
+ for (i = 0; i < fwspec->num_ids; i++) {
+ larbid = MTK_M4U_TO_LARB(fwspec->ids[i]);
+ larbid_msk |= BIT(larbid);
+ }
+
+ for_each_set_bit(larbid, &larbid_msk, 32) {
larbdev = data->larb_imu[larbid].dev;
device_link_remove(dev, larbdev);
}
@@ -974,6 +1017,8 @@ static int mtk_iommu_of_xlate(struct device *dev,
return -EINVAL;
dev_iommu_priv_set(dev, platform_get_drvdata(m4updev));
+
+ put_device(&m4updev->dev);
}
return iommu_fwspec_add_ids(dev, args->args, 1);
@@ -1211,16 +1256,19 @@ static int mtk_iommu_mm_dts_parse(struct device *dev, struct component_match **m
}
component_match_add(dev, match, component_compare_dev, &plarbdev->dev);
- platform_device_put(plarbdev);
}
- if (!frst_avail_smicomm_node)
- return -EINVAL;
+ if (!frst_avail_smicomm_node) {
+ ret = -EINVAL;
+ goto err_larbdev_put;
+ }
pcommdev = of_find_device_by_node(frst_avail_smicomm_node);
of_node_put(frst_avail_smicomm_node);
- if (!pcommdev)
- return -ENODEV;
+ if (!pcommdev) {
+ ret = -ENODEV;
+ goto err_larbdev_put;
+ }
data->smicomm_dev = &pcommdev->dev;
link = device_link_add(data->smicomm_dev, dev,
@@ -1228,16 +1276,16 @@ static int mtk_iommu_mm_dts_parse(struct device *dev, struct component_match **m
platform_device_put(pcommdev);
if (!link) {
dev_err(dev, "Unable to link %s.\n", dev_name(data->smicomm_dev));
- return -EINVAL;
+ ret = -EINVAL;
+ goto err_larbdev_put;
}
return 0;
err_larbdev_put:
- for (i = MTK_LARB_NR_MAX - 1; i >= 0; i--) {
- if (!data->larb_imu[i].dev)
- continue;
+ /* id mapping may not be linear, loop the whole array */
+ for (i = 0; i < MTK_LARB_NR_MAX; i++)
put_device(data->larb_imu[i].dev);
- }
+
return ret;
}
@@ -1400,8 +1448,12 @@ out_sysfs_remove:
iommu_device_sysfs_remove(&data->iommu);
out_list_del:
list_del(&data->list);
- if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM))
+ if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) {
device_link_remove(data->smicomm_dev, dev);
+
+ for (i = 0; i < MTK_LARB_NR_MAX; i++)
+ put_device(data->larb_imu[i].dev);
+ }
out_runtime_disable:
pm_runtime_disable(dev);
return ret;
@@ -1421,6 +1473,9 @@ static void mtk_iommu_remove(struct platform_device *pdev)
if (MTK_IOMMU_IS_TYPE(data->plat_data, MTK_IOMMU_TYPE_MM)) {
device_link_remove(data->smicomm_dev, &pdev->dev);
component_master_del(&pdev->dev, &mtk_iommu_com_ops);
+
+ for (i = 0; i < MTK_LARB_NR_MAX; i++)
+ put_device(data->larb_imu[i].dev);
}
pm_runtime_disable(&pdev->dev);
for (i = 0; i < data->plat_data->banks_num; i++) {
@@ -1695,6 +1750,66 @@ static const struct mtk_iommu_plat_data mt8188_data_vpp = {
27, 28 /* ccu0 */, MTK_INVALID_LARBID}, {4, 6}},
};
+static const unsigned int mt8189_apu_region_msk[][MTK_LARB_NR_MAX] = {
+ [0] = {[0] = BIT(2)}, /* Region0: fake larb 0 APU_SECURE */
+ [1] = {[0] = BIT(1)}, /* Region1: fake larb 0 APU_CODE */
+ [2] = {[0] = BIT(3)}, /* Region2: fake larb 0 APU_VLM */
+ [3] = {[0] = BIT(0)}, /* Region3: fake larb 0 APU_DATA */
+};
+
+static const struct mtk_iommu_plat_data mt8189_data_apu = {
+ .m4u_plat = M4U_MT8189,
+ .flags = IOVA_34_EN | DCM_DISABLE |
+ MTK_IOMMU_TYPE_APU | PGTABLE_PA_35_EN,
+ .hw_list = &apulist,
+ .inv_sel_reg = REG_MMU_INV_SEL_GEN2,
+ .banks_num = 1,
+ .banks_enable = {true},
+ .iova_region = mt8189_multi_dom_apu,
+ .iova_region_nr = ARRAY_SIZE(mt8189_multi_dom_apu),
+ .larbid_remap = {{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}},
+ .iova_region_larb_msk = mt8189_apu_region_msk,
+};
+
+static const struct mtk_iommu_plat_data mt8189_data_infra = {
+ .m4u_plat = M4U_MT8189,
+ .flags = WR_THROT_EN | DCM_DISABLE | MTK_IOMMU_TYPE_INFRA |
+ CFG_IFA_MASTER_IN_ATF | SHARE_PGTABLE | PGTABLE_PA_35_EN,
+ .hw_list = &infralist,
+ .banks_num = 1,
+ .banks_enable = {true},
+ .inv_sel_reg = REG_MMU_INV_SEL_GEN2,
+ .iova_region = single_domain,
+ .iova_region_nr = ARRAY_SIZE(single_domain),
+};
+
+static const u32 mt8189_larb_region_msk[MT8192_MULTI_REGION_NR_MAX][MTK_LARB_NR_MAX] = {
+ [0] = {~0, ~0, ~0, [22] = BIT(0)}, /* Region0: all ports for larb0/1/2 */
+ [1] = {[3] = ~0, [4] = ~0}, /* Region1: all ports for larb4(3)/7(4) */
+ [2] = {[5] = ~0, [6] = ~0, /* Region2: all ports for larb9(5)/11(6) */
+ [7] = ~0, [8] = ~0, /* Region2: all ports for larb13(7)/14(8) */
+ [9] = ~0, [10] = ~0, /* Region2: all ports for larb16(9)/17(10) */
+ [11] = ~0, [12] = ~0, /* Region2: all ports for larb19(11)/20(12) */
+ [21] = ~0}, /* Region2: larb21 fake GCE larb */
+};
+
+static const struct mtk_iommu_plat_data mt8189_data_mm = {
+ .m4u_plat = M4U_MT8189,
+ .flags = HAS_BCLK | HAS_SUB_COMM_3BITS | OUT_ORDER_WR_EN |
+ WR_THROT_EN | IOVA_34_EN | MTK_IOMMU_TYPE_MM |
+ PGTABLE_PA_35_EN | DL_WITH_MULTI_LARB,
+ .hw_list = &m4ulist,
+ .inv_sel_reg = REG_MMU_INV_SEL_GEN2,
+ .banks_num = 5,
+ .banks_enable = {true, false, false, false, false},
+ .iova_region = mt8192_multi_dom,
+ .iova_region_nr = ARRAY_SIZE(mt8192_multi_dom),
+ .iova_region_larb_msk = mt8189_larb_region_msk,
+ .larbid_remap = {{0}, {1}, {21/* GCE_D */, 21/* GCE_M */, 2},
+ {19, 20, 9, 11}, {7}, {4},
+ {13, 17}, {14, 16}},
+};
+
static const struct mtk_iommu_plat_data mt8192_data = {
.m4u_plat = M4U_MT8192,
.flags = HAS_BCLK | HAS_SUB_COMM_2BITS | OUT_ORDER_WR_EN |
@@ -1796,6 +1911,9 @@ static const struct of_device_id mtk_iommu_of_ids[] = {
{ .compatible = "mediatek,mt8188-iommu-infra", .data = &mt8188_data_infra},
{ .compatible = "mediatek,mt8188-iommu-vdo", .data = &mt8188_data_vdo},
{ .compatible = "mediatek,mt8188-iommu-vpp", .data = &mt8188_data_vpp},
+ { .compatible = "mediatek,mt8189-iommu-apu", .data = &mt8189_data_apu},
+ { .compatible = "mediatek,mt8189-iommu-infra", .data = &mt8189_data_infra},
+ { .compatible = "mediatek,mt8189-iommu-mm", .data = &mt8189_data_mm},
{ .compatible = "mediatek,mt8192-m4u", .data = &mt8192_data},
{ .compatible = "mediatek,mt8195-iommu-infra", .data = &mt8195_data_infra},
{ .compatible = "mediatek,mt8195-iommu-vdo", .data = &mt8195_data_vdo},
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 10cc0b1197e8..c8d8eff5373d 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -303,7 +303,9 @@ static void mtk_iommu_v1_domain_free(struct iommu_domain *domain)
kfree(to_mtk_domain(domain));
}
-static int mtk_iommu_v1_attach_device(struct iommu_domain *domain, struct device *dev)
+static int mtk_iommu_v1_attach_device(struct iommu_domain *domain,
+ struct device *dev,
+ struct iommu_domain *old)
{
struct mtk_iommu_v1_data *data = dev_iommu_priv_get(dev);
struct mtk_iommu_v1_domain *dom = to_mtk_domain(domain);
@@ -329,7 +331,8 @@ static int mtk_iommu_v1_attach_device(struct iommu_domain *domain, struct device
}
static int mtk_iommu_v1_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct mtk_iommu_v1_data *data = dev_iommu_priv_get(dev);
@@ -435,6 +438,8 @@ static int mtk_iommu_v1_create_mapping(struct device *dev,
return -EINVAL;
dev_iommu_priv_set(dev, platform_get_drvdata(m4updev));
+
+ put_device(&m4updev->dev);
}
ret = iommu_fwspec_add_ids(dev, args->args, 1);
@@ -641,13 +646,18 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
if (larb_nr < 0)
return larb_nr;
+ if (larb_nr > MTK_LARB_NR_MAX)
+ return -EINVAL;
+
for (i = 0; i < larb_nr; i++) {
struct device_node *larbnode;
struct platform_device *plarbdev;
larbnode = of_parse_phandle(dev->of_node, "mediatek,larbs", i);
- if (!larbnode)
- return -EINVAL;
+ if (!larbnode) {
+ ret = -EINVAL;
+ goto out_put_larbs;
+ }
if (!of_device_is_available(larbnode)) {
of_node_put(larbnode);
@@ -657,11 +667,14 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
plarbdev = of_find_device_by_node(larbnode);
if (!plarbdev) {
of_node_put(larbnode);
- return -ENODEV;
+ ret = -ENODEV;
+ goto out_put_larbs;
}
if (!plarbdev->dev.driver) {
of_node_put(larbnode);
- return -EPROBE_DEFER;
+ put_device(&plarbdev->dev);
+ ret = -EPROBE_DEFER;
+ goto out_put_larbs;
}
data->larb_imu[i].dev = &plarbdev->dev;
@@ -673,7 +686,7 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
ret = mtk_iommu_v1_hw_init(data);
if (ret)
- return ret;
+ goto out_put_larbs;
ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL,
dev_name(&pdev->dev));
@@ -695,12 +708,17 @@ out_sysfs_remove:
iommu_device_sysfs_remove(&data->iommu);
out_clk_unprepare:
clk_disable_unprepare(data->bclk);
+out_put_larbs:
+ for (i = 0; i < MTK_LARB_NR_MAX; i++)
+ put_device(data->larb_imu[i].dev);
+
return ret;
}
static void mtk_iommu_v1_remove(struct platform_device *pdev)
{
struct mtk_iommu_v1_data *data = platform_get_drvdata(pdev);
+ int i;
iommu_device_sysfs_remove(&data->iommu);
iommu_device_unregister(&data->iommu);
@@ -708,6 +726,9 @@ static void mtk_iommu_v1_remove(struct platform_device *pdev)
clk_disable_unprepare(data->bclk);
devm_free_irq(&pdev->dev, data->irq, data);
component_master_del(&pdev->dev, &mtk_iommu_v1_com_ops);
+
+ for (i = 0; i < MTK_LARB_NR_MAX; i++)
+ put_device(data->larb_imu[i].dev);
}
static int __maybe_unused mtk_iommu_v1_suspend(struct device *dev)
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index 5c6f5943f44b..768973b7e511 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -1431,8 +1431,8 @@ static void omap_iommu_detach_fini(struct omap_iommu_domain *odomain)
odomain->iommus = NULL;
}
-static int
-omap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+static int omap_iommu_attach_dev(struct iommu_domain *domain,
+ struct device *dev, struct iommu_domain *old)
{
struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
@@ -1536,15 +1536,15 @@ static void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
}
static int omap_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
- struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct omap_iommu_domain *omap_domain;
- if (domain == identity_domain || !domain)
+ if (old == identity_domain || !old)
return 0;
- omap_domain = to_omap_domain(domain);
+ omap_domain = to_omap_domain(old);
spin_lock(&omap_domain->lock);
_omap_iommu_detach_dev(omap_domain, dev);
spin_unlock(&omap_domain->lock);
@@ -1668,23 +1668,20 @@ static struct iommu_device *omap_iommu_probe_device(struct device *dev)
}
pdev = of_find_device_by_node(np);
+ of_node_put(np);
if (!pdev) {
- of_node_put(np);
kfree(arch_data);
return ERR_PTR(-ENODEV);
}
oiommu = platform_get_drvdata(pdev);
+ put_device(&pdev->dev);
if (!oiommu) {
- of_node_put(np);
kfree(arch_data);
return ERR_PTR(-EINVAL);
}
tmp->iommu_dev = oiommu;
- tmp->dev = &pdev->dev;
-
- of_node_put(np);
}
dev_iommu_priv_set(dev, arch_data);
diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h
index 27697109ec79..50b39be61abc 100644
--- a/drivers/iommu/omap-iommu.h
+++ b/drivers/iommu/omap-iommu.h
@@ -88,7 +88,6 @@ struct omap_iommu {
/**
* struct omap_iommu_arch_data - omap iommu private data
* @iommu_dev: handle of the OMAP iommu device
- * @dev: handle of the iommu device
*
* This is an omap iommu private data object, which binds an iommu user
* to its iommu device. This object should be placed at the iommu user's
@@ -97,7 +96,6 @@ struct omap_iommu {
*/
struct omap_iommu_arch_data {
struct omap_iommu *iommu_dev;
- struct device *dev;
};
struct cr_regs {
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index ebb22979075d..d9429097a2b5 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1321,7 +1321,8 @@ static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_m
}
static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
@@ -1426,7 +1427,8 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
}
static int riscv_iommu_attach_blocking_domain(struct iommu_domain *iommu_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
@@ -1447,7 +1449,8 @@ static struct iommu_domain riscv_iommu_blocking_domain = {
};
static int riscv_iommu_attach_identity_domain(struct iommu_domain *iommu_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index 0861dd469bd8..85f3667e797c 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -960,7 +960,8 @@ out_disable_clocks:
}
static int rk_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct rk_iommu *iommu;
struct rk_iommu_domain *rk_domain;
@@ -1005,7 +1006,7 @@ static struct iommu_domain rk_identity_domain = {
};
static int rk_iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct rk_iommu *iommu;
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
@@ -1026,7 +1027,7 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
if (iommu->domain == domain)
return 0;
- ret = rk_iommu_identity_attach(&rk_identity_domain, dev);
+ ret = rk_iommu_identity_attach(&rk_identity_domain, dev, old);
if (ret)
return ret;
@@ -1041,8 +1042,17 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
return 0;
ret = rk_iommu_enable(iommu);
- if (ret)
- WARN_ON(rk_iommu_identity_attach(&rk_identity_domain, dev));
+ if (ret) {
+ /*
+ * Note rk_iommu_identity_attach() might fail before physically
+ * attaching the dev to iommu->domain, in which case the actual
+ * old domain for this revert should be rk_identity_domain v.s.
+ * iommu->domain. Since rk_iommu_identity_attach() does not care
+ * about the old domain argument for now, this is not a problem.
+ */
+ WARN_ON(rk_iommu_identity_attach(&rk_identity_domain, dev,
+ iommu->domain));
+ }
pm_runtime_put(iommu->dev);
diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c
index aa576736d60b..fe679850af28 100644
--- a/drivers/iommu/s390-iommu.c
+++ b/drivers/iommu/s390-iommu.c
@@ -670,7 +670,8 @@ int zpci_iommu_register_ioat(struct zpci_dev *zdev, u8 *status)
}
static int blocking_domain_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct zpci_dev *zdev = to_zpci_dev(dev);
struct s390_domain *s390_domain;
@@ -694,7 +695,8 @@ static int blocking_domain_attach_device(struct iommu_domain *domain,
}
static int s390_iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct s390_domain *s390_domain = to_s390_domain(domain);
struct zpci_dev *zdev = to_zpci_dev(dev);
@@ -709,7 +711,7 @@ static int s390_iommu_attach_device(struct iommu_domain *domain,
domain->geometry.aperture_end < zdev->start_dma))
return -EINVAL;
- blocking_domain_attach_device(&blocking_domain, dev);
+ blocking_domain_attach_device(&blocking_domain, dev, old);
/* If we fail now DMA remains blocked via blocking domain */
cc = s390_iommu_domain_reg_ioat(zdev, domain, &status);
@@ -1131,13 +1133,14 @@ static int __init s390_iommu_init(void)
subsys_initcall(s390_iommu_init);
static int s390_attach_dev_identity(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct zpci_dev *zdev = to_zpci_dev(dev);
u8 status;
int cc;
- blocking_domain_attach_device(&blocking_domain, dev);
+ blocking_domain_attach_device(&blocking_domain, dev, old);
/* If we fail now DMA remains blocked via blocking domain */
cc = s390_iommu_domain_reg_ioat(zdev, domain, &status);
diff --git a/drivers/iommu/sprd-iommu.c b/drivers/iommu/sprd-iommu.c
index c7ca1d8a0b15..555d4505c747 100644
--- a/drivers/iommu/sprd-iommu.c
+++ b/drivers/iommu/sprd-iommu.c
@@ -247,7 +247,8 @@ static void sprd_iommu_domain_free(struct iommu_domain *domain)
}
static int sprd_iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct sprd_iommu_device *sdev = dev_iommu_priv_get(dev);
struct sprd_iommu_domain *dom = to_sprd_domain(domain);
diff --git a/drivers/iommu/sun50i-iommu.c b/drivers/iommu/sun50i-iommu.c
index de10b569d9a9..90b26fe21817 100644
--- a/drivers/iommu/sun50i-iommu.c
+++ b/drivers/iommu/sun50i-iommu.c
@@ -771,7 +771,8 @@ static void sun50i_iommu_detach_domain(struct sun50i_iommu *iommu,
}
static int sun50i_iommu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct sun50i_iommu *iommu = dev_iommu_priv_get(dev);
struct sun50i_iommu_domain *sun50i_domain;
@@ -797,7 +798,8 @@ static struct iommu_domain sun50i_iommu_identity_domain = {
};
static int sun50i_iommu_attach_device(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
struct sun50i_iommu_domain *sun50i_domain = to_sun50i_domain(domain);
struct sun50i_iommu *iommu;
@@ -813,7 +815,7 @@ static int sun50i_iommu_attach_device(struct iommu_domain *domain,
if (iommu->domain == domain)
return 0;
- sun50i_iommu_identity_attach(&sun50i_iommu_identity_domain, dev);
+ sun50i_iommu_identity_attach(&sun50i_iommu_identity_domain, dev, old);
sun50i_iommu_attach_domain(iommu, sun50i_domain);
@@ -839,6 +841,8 @@ static int sun50i_iommu_of_xlate(struct device *dev,
dev_iommu_priv_set(dev, platform_get_drvdata(iommu_pdev));
+ put_device(&iommu_pdev->dev);
+
return iommu_fwspec_add_ids(dev, &id, 1);
}
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 36cdd5fbab07..c391e7f2cde6 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -490,7 +490,7 @@ static void tegra_smmu_as_unprepare(struct tegra_smmu *smmu,
}
static int tegra_smmu_attach_dev(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev, struct iommu_domain *old)
{
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct tegra_smmu *smmu = dev_iommu_priv_get(dev);
@@ -524,9 +524,9 @@ disable:
}
static int tegra_smmu_identity_attach(struct iommu_domain *identity_domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
- struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
struct tegra_smmu_as *as;
struct tegra_smmu *smmu;
@@ -535,10 +535,10 @@ static int tegra_smmu_identity_attach(struct iommu_domain *identity_domain,
if (!fwspec)
return -ENODEV;
- if (domain == identity_domain || !domain)
+ if (old == identity_domain || !old)
return 0;
- as = to_smmu_as(domain);
+ as = to_smmu_as(old);
smmu = as->smmu;
for (index = 0; index < fwspec->num_ids; index++) {
tegra_smmu_disable(smmu, fwspec->ids[index], as->id);
@@ -830,10 +830,9 @@ static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
return NULL;
mc = platform_get_drvdata(pdev);
- if (!mc) {
- put_device(&pdev->dev);
+ put_device(&pdev->dev);
+ if (!mc)
return NULL;
- }
return mc->smmu;
}
diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
index b39d6f134ab2..d314fa5cd847 100644
--- a/drivers/iommu/virtio-iommu.c
+++ b/drivers/iommu/virtio-iommu.c
@@ -730,7 +730,8 @@ static struct iommu_domain *viommu_domain_alloc_identity(struct device *dev)
return domain;
}
-static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev,
+ struct iommu_domain *old)
{
int ret = 0;
struct virtio_iommu_req_attach req;
@@ -781,7 +782,8 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
}
static int viommu_attach_identity_domain(struct iommu_domain *domain,
- struct device *dev)
+ struct device *dev,
+ struct iommu_domain *old)
{
int ret = 0;
struct virtio_iommu_req_attach req;
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index a61c6dc63c29..f334f49c9791 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -36,7 +36,6 @@ config GIC_NON_BANKED
config ARM_GIC_V3
bool
select IRQ_DOMAIN_HIERARCHY
- select PARTITION_PERCPU
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP
select HAVE_ARM_SMCCC_DISCOVERY
select IRQ_MSI_IOMMU
@@ -151,7 +150,7 @@ config BCM6345_L1_IRQ
config BCM7038_L1_IRQ
tristate "Broadcom STB 7038-style L1/L2 interrupt controller driver"
- depends on ARCH_BRCMSTB || BMIPS_GENERIC
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
default ARCH_BRCMSTB || BMIPS_GENERIC
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
@@ -159,14 +158,14 @@ config BCM7038_L1_IRQ
config BCM7120_L2_IRQ
tristate "Broadcom STB 7120-style L2 interrupt controller driver"
- depends on ARCH_BRCMSTB || BMIPS_GENERIC
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
default ARCH_BRCMSTB || BMIPS_GENERIC
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
config BRCMSTB_L2_IRQ
tristate "Broadcom STB generic L2 interrupt controller driver"
- depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC
+ depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
default ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN
@@ -451,9 +450,6 @@ config LS_SCFG_MSI
depends on PCI_MSI
select IRQ_MSI_LIB
-config PARTITION_PERCPU
- bool
-
config STM32MP_EXTI
tristate "STM32MP extended interrupts and event controller"
depends on (ARCH_STM32 && !ARM_SINGLE_ARMV7M) || COMPILE_TEST
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 3de083f5484c..6a229443efe0 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -36,7 +36,6 @@ obj-$(CONFIG_ARM_GIC_V3) += irq-gic-v3.o irq-gic-v3-mbi.o irq-gic-common.o
obj-$(CONFIG_ARM_GIC_ITS_PARENT) += irq-gic-its-msi-parent.o
obj-$(CONFIG_ARM_GIC_V3_ITS) += irq-gic-v3-its.o irq-gic-v4.o
obj-$(CONFIG_ARM_GIC_V3_ITS_FSL_MC) += irq-gic-v3-its-fsl-mc-msi.o
-obj-$(CONFIG_PARTITION_PERCPU) += irq-partition-percpu.o
obj-$(CONFIG_ARM_GIC_V5) += irq-gic-v5.o irq-gic-v5-irs.o irq-gic-v5-its.o \
irq-gic-v5-iwb.o
obj-$(CONFIG_HISILICON_IRQ_MBIGEN) += irq-mbigen.o
diff --git a/drivers/irqchip/irq-aclint-sswi.c b/drivers/irqchip/irq-aclint-sswi.c
index 93e28e9f281f..fee30f3bc5ac 100644
--- a/drivers/irqchip/irq-aclint-sswi.c
+++ b/drivers/irqchip/irq-aclint-sswi.c
@@ -175,7 +175,8 @@ static int __init generic_aclint_sswi_early_probe(struct device_node *node,
{
return generic_aclint_sswi_probe(&node->fwnode);
}
-IRQCHIP_DECLARE(generic_aclint_sswi, "mips,p8700-aclint-sswi", generic_aclint_sswi_early_probe);
+IRQCHIP_DECLARE(mips_p8700_sswi, "mips,p8700-aclint-sswi", generic_aclint_sswi_early_probe);
+IRQCHIP_DECLARE(nuclei_ux900_sswi, "nuclei,ux900-aclint-sswi", generic_aclint_sswi_early_probe);
/* THEAD variant */
#define THEAD_C9XX_CSR_SXSTATUS 0x5c0
diff --git a/drivers/irqchip/irq-apple-aic.c b/drivers/irqchip/irq-apple-aic.c
index 032d66dceb8e..795b3db4554a 100644
--- a/drivers/irqchip/irq-apple-aic.c
+++ b/drivers/irqchip/irq-apple-aic.c
@@ -578,16 +578,9 @@ static void __exception_irq_entry aic_handle_fiq(struct pt_regs *regs)
}
if ((read_sysreg_s(SYS_IMP_APL_PMCR0_EL1) & (PMCR0_IMODE | PMCR0_IACT)) ==
- (FIELD_PREP(PMCR0_IMODE, PMCR0_IMODE_FIQ) | PMCR0_IACT)) {
- int irq;
- if (cpumask_test_cpu(smp_processor_id(),
- &aic_irqc->fiq_aff[AIC_CPU_PMU_P]->aff))
- irq = AIC_CPU_PMU_P;
- else
- irq = AIC_CPU_PMU_E;
+ (FIELD_PREP(PMCR0_IMODE, PMCR0_IMODE_FIQ) | PMCR0_IACT))
generic_handle_domain_irq(aic_irqc->hw_domain,
- AIC_FIQ_HWIRQ(irq));
- }
+ AIC_FIQ_HWIRQ(AIC_CPU_PMU_P));
if (static_branch_likely(&use_fast_ipi) &&
(FIELD_GET(UPMCR0_IMODE, read_sysreg_s(SYS_IMP_APL_UPMCR0_EL1)) == UPMCR0_IMODE_FIQ) &&
@@ -632,18 +625,7 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
handle_fasteoi_irq, NULL, NULL);
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
} else {
- int fiq = FIELD_GET(AIC_EVENT_NUM, hw);
-
- switch (fiq) {
- case AIC_CPU_PMU_P:
- case AIC_CPU_PMU_E:
- irq_set_percpu_devid_partition(irq, &ic->fiq_aff[fiq]->aff);
- break;
- default:
- irq_set_percpu_devid(irq);
- break;
- }
-
+ irq_set_percpu_devid(irq);
irq_domain_set_info(id, irq, hw, &fiq_chip, id->host_data,
handle_percpu_devid_irq, NULL, NULL);
}
@@ -651,6 +633,33 @@ static int aic_irq_domain_map(struct irq_domain *id, unsigned int irq,
return 0;
}
+static int aic_irq_get_fwspec_info(struct irq_fwspec *fwspec, struct irq_fwspec_info *info)
+{
+ const struct cpumask *mask;
+ u32 intid;
+
+ info->flags = 0;
+ info->affinity = NULL;
+
+ if (fwspec->param[0] != AIC_FIQ)
+ return 0;
+
+ if (fwspec->param_count == 3)
+ intid = fwspec->param[1];
+ else
+ intid = fwspec->param[2];
+
+ if (aic_irqc->fiq_aff[intid])
+ mask = &aic_irqc->fiq_aff[intid]->aff;
+ else
+ mask = cpu_possible_mask;
+
+ info->affinity = mask;
+ info->flags = IRQ_FWSPEC_INFO_AFFINITY_VALID;
+
+ return 0;
+}
+
static int aic_irq_domain_translate(struct irq_domain *id,
struct irq_fwspec *fwspec,
unsigned long *hwirq,
@@ -705,6 +714,10 @@ static int aic_irq_domain_translate(struct irq_domain *id,
break;
}
}
+
+ /* Merge the two PMUs on a single interrupt */
+ if (*hwirq == AIC_CPU_PMU_E)
+ *hwirq = AIC_CPU_PMU_P;
break;
default:
return -EINVAL;
@@ -750,9 +763,10 @@ static void aic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
}
static const struct irq_domain_ops aic_irq_domain_ops = {
- .translate = aic_irq_domain_translate,
- .alloc = aic_irq_domain_alloc,
- .free = aic_irq_domain_free,
+ .translate = aic_irq_domain_translate,
+ .alloc = aic_irq_domain_alloc,
+ .free = aic_irq_domain_free,
+ .get_fwspec_info = aic_irq_get_fwspec_info,
};
/*
diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c
index 3cad30a40c19..e68853815c7a 100644
--- a/drivers/irqchip/irq-atmel-aic-common.c
+++ b/drivers/irqchip/irq-atmel-aic-common.c
@@ -187,20 +187,11 @@ void __init aic_common_rtt_irq_fixup(void)
static void __init aic_common_irq_fixup(const struct of_device_id *matches)
{
- struct device_node *root = of_find_node_by_path("/");
- const struct of_device_id *match;
+ void (*fixup)(void);
- if (!root)
- return;
-
- match = of_match_node(matches, root);
-
- if (match) {
- void (*fixup)(void) = match->data;
+ fixup = of_machine_get_match_data(matches);
+ if (fixup)
fixup();
- }
-
- of_node_put(root);
}
struct irq_domain *__init aic_common_of_init(struct device_node *node,
diff --git a/drivers/irqchip/irq-bcm2712-mip.c b/drivers/irqchip/irq-bcm2712-mip.c
index 9bd7bc0bf6d5..4761974ad650 100644
--- a/drivers/irqchip/irq-bcm2712-mip.c
+++ b/drivers/irqchip/irq-bcm2712-mip.c
@@ -232,17 +232,12 @@ err_put:
return ret;
}
-static int __init mip_of_msi_init(struct device_node *node, struct device_node *parent)
+static int mip_msi_probe(struct platform_device *pdev, struct device_node *parent)
{
- struct platform_device *pdev;
+ struct device_node *node = pdev->dev.of_node;
struct mip_priv *mip;
int ret;
- pdev = of_find_device_by_node(node);
- of_node_put(node);
- if (!pdev)
- return -EPROBE_DEFER;
-
mip = kzalloc(sizeof(*mip), GFP_KERNEL);
if (!mip)
return -ENOMEM;
@@ -285,7 +280,7 @@ err_priv:
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(mip_msi)
-IRQCHIP_MATCH("brcm,bcm2712-mip", mip_of_msi_init)
+IRQCHIP_MATCH("brcm,bcm2712-mip", mip_msi_probe)
IRQCHIP_PLATFORM_DRIVER_END(mip_msi)
MODULE_DESCRIPTION("Broadcom BCM2712 MSI-X interrupt controller");
MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
diff --git a/drivers/irqchip/irq-bcm7038-l1.c b/drivers/irqchip/irq-bcm7038-l1.c
index 04fac0cc857f..ea1446c0a09c 100644
--- a/drivers/irqchip/irq-bcm7038-l1.c
+++ b/drivers/irqchip/irq-bcm7038-l1.c
@@ -82,12 +82,6 @@ static inline unsigned int reg_status(struct bcm7038_l1_chip *intc,
return (0 * intc->n_words + word) * sizeof(u32);
}
-static inline unsigned int reg_mask_status(struct bcm7038_l1_chip *intc,
- unsigned int word)
-{
- return (1 * intc->n_words + word) * sizeof(u32);
-}
-
static inline unsigned int reg_mask_set(struct bcm7038_l1_chip *intc,
unsigned int word)
{
@@ -219,9 +213,8 @@ static int bcm7038_l1_set_affinity(struct irq_data *d,
}
#endif
-static int __init bcm7038_l1_init_one(struct device_node *dn,
- unsigned int idx,
- struct bcm7038_l1_chip *intc)
+static int bcm7038_l1_init_one(struct device_node *dn, unsigned int idx,
+ struct bcm7038_l1_chip *intc)
{
struct resource res;
resource_size_t sz;
@@ -395,9 +388,9 @@ static const struct irq_domain_ops bcm7038_l1_domain_ops = {
.map = bcm7038_l1_map,
};
-static int __init bcm7038_l1_of_init(struct device_node *dn,
- struct device_node *parent)
+static int bcm7038_l1_probe(struct platform_device *pdev, struct device_node *parent)
{
+ struct device_node *dn = pdev->dev.of_node;
struct bcm7038_l1_chip *intc;
int idx, ret;
@@ -455,7 +448,7 @@ out_free:
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(bcm7038_l1)
-IRQCHIP_MATCH("brcm,bcm7038-l1-intc", bcm7038_l1_of_init)
+IRQCHIP_MATCH("brcm,bcm7038-l1-intc", bcm7038_l1_probe)
IRQCHIP_PLATFORM_DRIVER_END(bcm7038_l1)
MODULE_DESCRIPTION("Broadcom STB 7038-style L1/L2 interrupt controller");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c
index ff22c3104401..518c9d4366a5 100644
--- a/drivers/irqchip/irq-bcm7120-l2.c
+++ b/drivers/irqchip/irq-bcm7120-l2.c
@@ -143,8 +143,7 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn,
return 0;
}
-static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn,
- struct bcm7120_l2_intc_data *data)
+static int bcm7120_l2_intc_iomap_7120(struct device_node *dn, struct bcm7120_l2_intc_data *data)
{
int ret;
@@ -177,8 +176,7 @@ static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn,
return 0;
}
-static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn,
- struct bcm7120_l2_intc_data *data)
+static int bcm7120_l2_intc_iomap_3380(struct device_node *dn, struct bcm7120_l2_intc_data *data)
{
unsigned int gc_idx;
@@ -208,15 +206,14 @@ static int __init bcm7120_l2_intc_iomap_3380(struct device_node *dn,
return 0;
}
-static int __init bcm7120_l2_intc_probe(struct device_node *dn,
- struct device_node *parent,
+static int bcm7120_l2_intc_probe(struct platform_device *pdev, struct device_node *parent,
int (*iomap_regs_fn)(struct device_node *,
- struct bcm7120_l2_intc_data *),
+ struct bcm7120_l2_intc_data *),
const char *intc_name)
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
+ struct device_node *dn = pdev->dev.of_node;
struct bcm7120_l2_intc_data *data;
- struct platform_device *pdev;
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
int ret = 0;
@@ -227,14 +224,7 @@ static int __init bcm7120_l2_intc_probe(struct device_node *dn,
if (!data)
return -ENOMEM;
- pdev = of_find_device_by_node(dn);
- if (!pdev) {
- ret = -ENODEV;
- goto out_free_data;
- }
-
data->num_parent_irqs = platform_irq_count(pdev);
- put_device(&pdev->dev);
if (data->num_parent_irqs <= 0) {
pr_err("invalid number of parent interrupts\n");
ret = -ENOMEM;
@@ -334,22 +324,19 @@ out_unmap:
if (data->map_base[idx])
iounmap(data->map_base[idx]);
}
-out_free_data:
kfree(data);
return ret;
}
-static int __init bcm7120_l2_intc_probe_7120(struct device_node *dn,
- struct device_node *parent)
+static int bcm7120_l2_intc_probe_7120(struct platform_device *pdev, struct device_node *parent)
{
- return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120,
+ return bcm7120_l2_intc_probe(pdev, parent, bcm7120_l2_intc_iomap_7120,
"BCM7120 L2");
}
-static int __init bcm7120_l2_intc_probe_3380(struct device_node *dn,
- struct device_node *parent)
+static int bcm7120_l2_intc_probe_3380(struct platform_device *pdev, struct device_node *parent)
{
- return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_3380,
+ return bcm7120_l2_intc_probe(pdev, parent, bcm7120_l2_intc_iomap_3380,
"BCM3380 L2");
}
diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c
index 1bec5b2cd3f0..bb7078d6524f 100644
--- a/drivers/irqchip/irq-brcmstb-l2.c
+++ b/drivers/irqchip/irq-brcmstb-l2.c
@@ -138,13 +138,12 @@ static void brcmstb_l2_intc_resume(struct irq_data *d)
irq_reg_writel(gc, ~b->saved_mask, ct->regs.enable);
}
-static int __init brcmstb_l2_intc_of_init(struct device_node *np,
- struct device_node *parent,
- const struct brcmstb_intc_init_params
- *init_params)
+static int brcmstb_l2_intc_probe(struct platform_device *pdev, struct device_node *parent,
+ const struct brcmstb_intc_init_params *init_params)
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
unsigned int set = 0;
+ struct device_node *np = pdev->dev.of_node;
struct brcmstb_l2_intc_data *data;
struct irq_chip_type *ct;
int ret;
@@ -257,23 +256,21 @@ out_free:
return ret;
}
-static int __init brcmstb_l2_edge_intc_of_init(struct device_node *np,
- struct device_node *parent)
+static int brcmstb_l2_edge_intc_probe(struct platform_device *pdev, struct device_node *parent)
{
- return brcmstb_l2_intc_of_init(np, parent, &l2_edge_intc_init);
+ return brcmstb_l2_intc_probe(pdev, parent, &l2_edge_intc_init);
}
-static int __init brcmstb_l2_lvl_intc_of_init(struct device_node *np,
- struct device_node *parent)
+static int brcmstb_l2_lvl_intc_probe(struct platform_device *pdev, struct device_node *parent)
{
- return brcmstb_l2_intc_of_init(np, parent, &l2_lvl_intc_init);
+ return brcmstb_l2_intc_probe(pdev, parent, &l2_lvl_intc_init);
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(brcmstb_l2)
-IRQCHIP_MATCH("brcm,l2-intc", brcmstb_l2_edge_intc_of_init)
-IRQCHIP_MATCH("brcm,hif-spi-l2-intc", brcmstb_l2_edge_intc_of_init)
-IRQCHIP_MATCH("brcm,upg-aux-aon-l2-intc", brcmstb_l2_edge_intc_of_init)
-IRQCHIP_MATCH("brcm,bcm7271-l2-intc", brcmstb_l2_lvl_intc_of_init)
+IRQCHIP_MATCH("brcm,l2-intc", brcmstb_l2_edge_intc_probe)
+IRQCHIP_MATCH("brcm,hif-spi-l2-intc", brcmstb_l2_edge_intc_probe)
+IRQCHIP_MATCH("brcm,upg-aux-aon-l2-intc", brcmstb_l2_edge_intc_probe)
+IRQCHIP_MATCH("brcm,bcm7271-l2-intc", brcmstb_l2_lvl_intc_probe)
IRQCHIP_PLATFORM_DRIVER_END(brcmstb_l2)
MODULE_DESCRIPTION("Broadcom STB generic L2 interrupt controller");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c
index eb1473f1448a..12f45228c867 100644
--- a/drivers/irqchip/irq-gic-its-msi-parent.c
+++ b/drivers/irqchip/irq-gic-its-msi-parent.c
@@ -142,83 +142,38 @@ static int its_v5_pci_msi_prepare(struct irq_domain *domain, struct device *dev,
#define its_v5_pci_msi_prepare NULL
#endif /* !CONFIG_PCI_MSI */
-static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev,
- u32 *dev_id)
+static int of_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev, u32 *dev_id,
+ phys_addr_t *pa)
{
- int ret, index = 0;
+ struct of_phandle_iterator it;
+ int ret;
/* Suck the DeviceID out of the msi-parent property */
- do {
- struct of_phandle_args args;
-
- ret = of_parse_phandle_with_args(dev->of_node,
- "msi-parent", "#msi-cells",
- index, &args);
- if (args.np == irq_domain_get_of_node(domain)) {
- if (WARN_ON(args.args_count != 1))
- return -EINVAL;
- *dev_id = args.args[0];
- break;
- }
- index++;
- } while (!ret);
-
- if (ret) {
- struct device_node *np = NULL;
+ of_for_each_phandle(&it, ret, dev->of_node, "msi-parent", "#msi-cells", -1) {
+ /* GICv5 ITS domain matches the MSI controller node parent */
+ struct device_node *np __free(device_node) = pa ? of_get_parent(it.node)
+ : of_node_get(it.node);
- ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
- if (np)
- of_node_put(np);
- }
+ if (np == irq_domain_get_of_node(domain)) {
+ u32 args;
- return ret;
-}
+ if (WARN_ON(of_phandle_iterator_args(&it, &args, 1) != 1))
+ ret = -EINVAL;
-static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev,
- u32 *dev_id, phys_addr_t *pa)
-{
- int ret, index = 0;
- /*
- * Retrieve the DeviceID and the ITS translate frame node pointer
- * out of the msi-parent property.
- */
- do {
- struct of_phandle_args args;
-
- ret = of_parse_phandle_with_args(dev->of_node,
- "msi-parent", "#msi-cells",
- index, &args);
- if (ret)
- break;
- /*
- * The IRQ domain fwnode is the msi controller parent
- * in GICv5 (where the msi controller nodes are the
- * ITS translate frames).
- */
- if (args.np->parent == irq_domain_get_of_node(domain)) {
- if (WARN_ON(args.args_count != 1))
- return -EINVAL;
- *dev_id = args.args[0];
-
- ret = its_translate_frame_address(args.np, pa);
- if (ret)
- return -ENODEV;
- break;
- }
- index++;
- } while (!ret);
+ if (!ret && pa)
+ ret = its_translate_frame_address(it.node, pa);
- if (ret) {
- struct device_node *np = NULL;
+ if (!ret)
+ *dev_id = args;
- ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id);
- if (np) {
- ret = its_translate_frame_address(np, pa);
- of_node_put(np);
+ of_node_put(it.node);
+ return ret;
}
}
- return ret;
+ struct device_node *msi_ctrl __free(device_node) = NULL;
+
+ return of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &msi_ctrl, dev_id);
}
int __weak iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id)
@@ -234,7 +189,7 @@ static int its_pmsi_prepare(struct irq_domain *domain, struct device *dev,
int ret;
if (dev->of_node)
- ret = of_pmsi_get_dev_id(domain->parent, dev, &dev_id);
+ ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, NULL);
else
ret = iort_pmsi_get_dev_id(dev, &dev_id);
if (ret)
@@ -262,7 +217,7 @@ static int its_v5_pmsi_prepare(struct irq_domain *domain, struct device *dev,
if (!dev->of_node)
return -ENODEV;
- ret = of_v5_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
+ ret = of_pmsi_get_msi_info(domain->parent, dev, &dev_id, &pa);
if (ret)
return ret;
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 3de351e66ee8..6607ab58f72e 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -26,7 +26,6 @@
#include <linux/irqchip/arm-gic-common.h>
#include <linux/irqchip/arm-gic-v3.h>
#include <linux/irqchip/arm-gic-v3-prio.h>
-#include <linux/irqchip/irq-partition-percpu.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/arm-smccc.h>
@@ -46,8 +45,6 @@ static u8 dist_prio_nmi __ro_after_init = GICV3_PRIO_NMI;
#define FLAGS_WORKAROUND_ASR_ERRATUM_8601001 (1ULL << 2)
#define FLAGS_WORKAROUND_INSECURE (1ULL << 3)
-#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
-
static struct cpumask broken_rdists __read_mostly __maybe_unused;
struct redist_region {
@@ -68,7 +65,13 @@ struct gic_chip_data {
u64 flags;
bool has_rss;
unsigned int ppi_nr;
- struct partition_desc **ppi_descs;
+ struct partition_affinity *parts;
+ unsigned int nr_parts;
+};
+
+struct partition_affinity {
+ cpumask_t mask;
+ struct fwnode_handle *partition_id;
};
#define T241_CHIPS_MAX 4
@@ -228,9 +231,6 @@ static void __init gic_prio_init(void)
!cpus_have_group0);
}
-/* rdist_nmi_refs[n] == number of cpus having the rdist interrupt n set as NMI */
-static refcount_t *rdist_nmi_refs;
-
static struct gic_kvm_info gic_v3_kvm_info __initdata;
static DEFINE_PER_CPU(bool, has_rss);
@@ -594,36 +594,6 @@ static void gic_irq_set_prio(struct irq_data *d, u8 prio)
writeb_relaxed(prio, base + offset + index);
}
-static u32 __gic_get_ppi_index(irq_hw_number_t hwirq)
-{
- switch (__get_intid_range(hwirq)) {
- case PPI_RANGE:
- return hwirq - 16;
- case EPPI_RANGE:
- return hwirq - EPPI_BASE_INTID + 16;
- default:
- unreachable();
- }
-}
-
-static u32 __gic_get_rdist_index(irq_hw_number_t hwirq)
-{
- switch (__get_intid_range(hwirq)) {
- case SGI_RANGE:
- case PPI_RANGE:
- return hwirq;
- case EPPI_RANGE:
- return hwirq - EPPI_BASE_INTID + 32;
- default:
- unreachable();
- }
-}
-
-static u32 gic_get_rdist_index(struct irq_data *d)
-{
- return __gic_get_rdist_index(d->hwirq);
-}
-
static int gic_irq_nmi_setup(struct irq_data *d)
{
struct irq_desc *desc = irq_to_desc(d->irq);
@@ -644,20 +614,8 @@ static int gic_irq_nmi_setup(struct irq_data *d)
return -EINVAL;
/* desc lock should already be held */
- if (gic_irq_in_rdist(d)) {
- u32 idx = gic_get_rdist_index(d);
-
- /*
- * Setting up a percpu interrupt as NMI, only switch handler
- * for first NMI
- */
- if (!refcount_inc_not_zero(&rdist_nmi_refs[idx])) {
- refcount_set(&rdist_nmi_refs[idx], 1);
- desc->handle_irq = handle_percpu_devid_fasteoi_nmi;
- }
- } else {
+ if (!gic_irq_in_rdist(d))
desc->handle_irq = handle_fasteoi_nmi;
- }
gic_irq_set_prio(d, dist_prio_nmi);
@@ -684,15 +642,8 @@ static void gic_irq_nmi_teardown(struct irq_data *d)
return;
/* desc lock should already be held */
- if (gic_irq_in_rdist(d)) {
- u32 idx = gic_get_rdist_index(d);
-
- /* Tearing down NMI, only switch handler for last NMI */
- if (refcount_dec_and_test(&rdist_nmi_refs[idx]))
- desc->handle_irq = handle_percpu_devid_irq;
- } else {
+ if (!gic_irq_in_rdist(d))
desc->handle_irq = handle_fasteoi_irq;
- }
gic_irq_set_prio(d, dist_prio_irq);
}
@@ -1666,13 +1617,6 @@ static int gic_irq_domain_translate(struct irq_domain *d,
case GIC_IRQ_TYPE_LPI: /* LPI */
*hwirq = fwspec->param[1];
break;
- case GIC_IRQ_TYPE_PARTITION:
- *hwirq = fwspec->param[1];
- if (fwspec->param[1] >= 16)
- *hwirq += EPPI_BASE_INTID - 16;
- else
- *hwirq += 16;
- break;
default:
return -EINVAL;
}
@@ -1681,10 +1625,8 @@ static int gic_irq_domain_translate(struct irq_domain *d,
/*
* Make it clear that broken DTs are... broken.
- * Partitioned PPIs are an unfortunate exception.
*/
- WARN_ON(*type == IRQ_TYPE_NONE &&
- fwspec->param[0] != GIC_IRQ_TYPE_PARTITION);
+ WARN_ON(*type == IRQ_TYPE_NONE);
return 0;
}
@@ -1741,33 +1683,12 @@ static void gic_irq_domain_free(struct irq_domain *domain, unsigned int virq,
}
}
-static bool fwspec_is_partitioned_ppi(struct irq_fwspec *fwspec,
- irq_hw_number_t hwirq)
-{
- enum gic_intid_range range;
-
- if (!gic_data.ppi_descs)
- return false;
-
- if (!is_of_node(fwspec->fwnode))
- return false;
-
- if (fwspec->param_count < 4 || !fwspec->param[3])
- return false;
-
- range = __get_intid_range(hwirq);
- if (range != PPI_RANGE && range != EPPI_RANGE)
- return false;
-
- return true;
-}
-
static int gic_irq_domain_select(struct irq_domain *d,
struct irq_fwspec *fwspec,
enum irq_domain_bus_token bus_token)
{
- unsigned int type, ppi_idx;
irq_hw_number_t hwirq;
+ unsigned int type;
int ret;
/* Not for us */
@@ -1786,60 +1707,61 @@ static int gic_irq_domain_select(struct irq_domain *d,
if (WARN_ON_ONCE(ret))
return 0;
- if (!fwspec_is_partitioned_ppi(fwspec, hwirq))
- return d == gic_data.domain;
-
- /*
- * If this is a PPI and we have a 4th (non-null) parameter,
- * then we need to match the partition domain.
- */
- ppi_idx = __gic_get_ppi_index(hwirq);
- return d == partition_get_domain(gic_data.ppi_descs[ppi_idx]);
+ return d == gic_data.domain;
}
-static const struct irq_domain_ops gic_irq_domain_ops = {
- .translate = gic_irq_domain_translate,
- .alloc = gic_irq_domain_alloc,
- .free = gic_irq_domain_free,
- .select = gic_irq_domain_select,
-};
-
-static int partition_domain_translate(struct irq_domain *d,
- struct irq_fwspec *fwspec,
- unsigned long *hwirq,
- unsigned int *type)
+static int gic_irq_get_fwspec_info(struct irq_fwspec *fwspec, struct irq_fwspec_info *info)
{
- unsigned long ppi_intid;
- struct device_node *np;
- unsigned int ppi_idx;
- int ret;
-
- if (!gic_data.ppi_descs)
- return -ENOMEM;
+ const struct cpumask *mask = NULL;
- np = of_find_node_by_phandle(fwspec->param[3]);
- if (WARN_ON(!np))
- return -EINVAL;
+ info->flags = 0;
+ info->affinity = NULL;
- ret = gic_irq_domain_translate(d, fwspec, &ppi_intid, type);
- if (WARN_ON_ONCE(ret))
+ /* ACPI is not capable of describing PPI affinity -- yet */
+ if (!is_of_node(fwspec->fwnode))
return 0;
- ppi_idx = __gic_get_ppi_index(ppi_intid);
- ret = partition_translate_id(gic_data.ppi_descs[ppi_idx],
- of_fwnode_handle(np));
- if (ret < 0)
- return ret;
+ /* If the specifier provides an affinity, use it */
+ if (fwspec->param_count == 4 && fwspec->param[3]) {
+ struct fwnode_handle *fw;
+
+ switch (fwspec->param[0]) {
+ case 1: /* PPI */
+ case 3: /* EPPI */
+ break;
+ default:
+ return 0;
+ }
+
+ fw = of_fwnode_handle(of_find_node_by_phandle(fwspec->param[3]));
+ if (!fw)
+ return -ENOENT;
+
+ for (int i = 0; i < gic_data.nr_parts; i++) {
+ if (gic_data.parts[i].partition_id == fw) {
+ mask = &gic_data.parts[i].mask;
+ break;
+ }
+ }
+
+ if (!mask)
+ return -ENOENT;
+ } else {
+ mask = cpu_possible_mask;
+ }
- *hwirq = ret;
- *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
+ info->affinity = mask;
+ info->flags = IRQ_FWSPEC_INFO_AFFINITY_VALID;
return 0;
}
-static const struct irq_domain_ops partition_domain_ops = {
- .translate = partition_domain_translate,
+static const struct irq_domain_ops gic_irq_domain_ops = {
+ .translate = gic_irq_domain_translate,
+ .alloc = gic_irq_domain_alloc,
+ .free = gic_irq_domain_free,
.select = gic_irq_domain_select,
+ .get_fwspec_info = gic_irq_get_fwspec_info,
};
static bool gic_enable_quirk_msm8996(void *data)
@@ -2030,19 +1952,9 @@ static const struct gic_quirk gic_quirks[] = {
static void gic_enable_nmi_support(void)
{
- int i;
-
if (!gic_prio_masking_enabled() || nmi_support_forbidden)
return;
- rdist_nmi_refs = kcalloc(gic_data.ppi_nr + SGI_NR,
- sizeof(*rdist_nmi_refs), GFP_KERNEL);
- if (!rdist_nmi_refs)
- return;
-
- for (i = 0; i < gic_data.ppi_nr + SGI_NR; i++)
- refcount_set(&rdist_nmi_refs[i], 0);
-
pr_info("Pseudo-NMIs enabled using %s ICC_PMR_EL1 synchronisation\n",
gic_has_relaxed_pmr_sync() ? "relaxed" : "forced");
@@ -2174,12 +2086,7 @@ static void __init gic_populate_ppi_partitions(struct device_node *gic_node)
if (!parts_node)
return;
- gic_data.ppi_descs = kcalloc(gic_data.ppi_nr, sizeof(*gic_data.ppi_descs), GFP_KERNEL);
- if (!gic_data.ppi_descs)
- goto out_put_node;
-
nr_parts = of_get_child_count(parts_node);
-
if (!nr_parts)
goto out_put_node;
@@ -2232,29 +2139,8 @@ static void __init gic_populate_ppi_partitions(struct device_node *gic_node)
part_idx++;
}
- for (i = 0; i < gic_data.ppi_nr; i++) {
- unsigned int irq;
- struct partition_desc *desc;
- struct irq_fwspec ppi_fwspec = {
- .fwnode = gic_data.fwnode,
- .param_count = 3,
- .param = {
- [0] = GIC_IRQ_TYPE_PARTITION,
- [1] = i,
- [2] = IRQ_TYPE_NONE,
- },
- };
-
- irq = irq_create_fwspec_mapping(&ppi_fwspec);
- if (WARN_ON(!irq))
- continue;
- desc = partition_create_desc(gic_data.fwnode, parts, nr_parts,
- irq, &partition_domain_ops);
- if (WARN_ON(!desc))
- continue;
-
- gic_data.ppi_descs[i] = desc;
- }
+ gic_data.parts = parts;
+ gic_data.nr_parts = nr_parts;
out_put_node:
of_node_put(parts_node);
diff --git a/drivers/irqchip/irq-imx-mu-msi.c b/drivers/irqchip/irq-imx-mu-msi.c
index d2a4e8a61a42..c598f2f52fc6 100644
--- a/drivers/irqchip/irq-imx-mu-msi.c
+++ b/drivers/irqchip/irq-imx-mu-msi.c
@@ -296,11 +296,9 @@ static const struct imx_mu_dcfg imx_mu_cfg_imx8ulp = {
},
};
-static int __init imx_mu_of_init(struct device_node *dn,
- struct device_node *parent,
- const struct imx_mu_dcfg *cfg)
+static int imx_mu_probe(struct platform_device *pdev, struct device_node *parent,
+ const struct imx_mu_dcfg *cfg)
{
- struct platform_device *pdev = of_find_device_by_node(dn);
struct device_link *pd_link_a;
struct device_link *pd_link_b;
struct imx_mu_msi *msi_data;
@@ -416,31 +414,27 @@ static const struct dev_pm_ops imx_mu_pm_ops = {
imx_mu_runtime_resume, NULL)
};
-static int __init imx_mu_imx7ulp_of_init(struct device_node *dn,
- struct device_node *parent)
+static int imx_mu_imx7ulp_probe(struct platform_device *pdev, struct device_node *parent)
{
- return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx7ulp);
+ return imx_mu_probe(pdev, parent, &imx_mu_cfg_imx7ulp);
}
-static int __init imx_mu_imx6sx_of_init(struct device_node *dn,
- struct device_node *parent)
+static int imx_mu_imx6sx_probe(struct platform_device *pdev, struct device_node *parent)
{
- return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx6sx);
+ return imx_mu_probe(pdev, parent, &imx_mu_cfg_imx6sx);
}
-static int __init imx_mu_imx8ulp_of_init(struct device_node *dn,
- struct device_node *parent)
+static int imx_mu_imx8ulp_probe(struct platform_device *pdev, struct device_node *parent)
{
- return imx_mu_of_init(dn, parent, &imx_mu_cfg_imx8ulp);
+ return imx_mu_probe(pdev, parent, &imx_mu_cfg_imx8ulp);
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(imx_mu_msi)
-IRQCHIP_MATCH("fsl,imx7ulp-mu-msi", imx_mu_imx7ulp_of_init)
-IRQCHIP_MATCH("fsl,imx6sx-mu-msi", imx_mu_imx6sx_of_init)
-IRQCHIP_MATCH("fsl,imx8ulp-mu-msi", imx_mu_imx8ulp_of_init)
+IRQCHIP_MATCH("fsl,imx7ulp-mu-msi", imx_mu_imx7ulp_probe)
+IRQCHIP_MATCH("fsl,imx6sx-mu-msi", imx_mu_imx6sx_probe)
+IRQCHIP_MATCH("fsl,imx8ulp-mu-msi", imx_mu_imx8ulp_probe)
IRQCHIP_PLATFORM_DRIVER_END(imx_mu_msi, .pm = &imx_mu_pm_ops)
-
MODULE_AUTHOR("Frank Li <Frank.Li@nxp.com>");
MODULE_DESCRIPTION("Freescale MU MSI controller driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/irqchip/irq-mchp-eic.c b/drivers/irqchip/irq-mchp-eic.c
index 516a3a0e359c..b513a899c085 100644
--- a/drivers/irqchip/irq-mchp-eic.c
+++ b/drivers/irqchip/irq-mchp-eic.c
@@ -199,8 +199,9 @@ static const struct irq_domain_ops mchp_eic_domain_ops = {
.free = irq_domain_free_irqs_common,
};
-static int mchp_eic_init(struct device_node *node, struct device_node *parent)
+static int mchp_eic_probe(struct platform_device *pdev, struct device_node *parent)
{
+ struct device_node *node = pdev->dev.of_node;
struct irq_domain *parent_domain = NULL;
int ret, i;
@@ -273,7 +274,7 @@ free:
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(mchp_eic)
-IRQCHIP_MATCH("microchip,sama7g5-eic", mchp_eic_init)
+IRQCHIP_MATCH("microchip,sama7g5-eic", mchp_eic_probe)
IRQCHIP_PLATFORM_DRIVER_END(mchp_eic)
MODULE_DESCRIPTION("Microchip External Interrupt Controller");
diff --git a/drivers/irqchip/irq-meson-gpio.c b/drivers/irqchip/irq-meson-gpio.c
index 7d177626d64b..3fcbb044ae60 100644
--- a/drivers/irqchip/irq-meson-gpio.c
+++ b/drivers/irqchip/irq-meson-gpio.c
@@ -174,6 +174,14 @@ static const struct meson_gpio_irq_params s4_params = {
INIT_MESON_S4_COMMON_DATA(82)
};
+static const struct meson_gpio_irq_params s6_params = {
+ INIT_MESON_S4_COMMON_DATA(100)
+};
+
+static const struct meson_gpio_irq_params s7_params = {
+ INIT_MESON_S4_COMMON_DATA(84)
+};
+
static const struct meson_gpio_irq_params c3_params = {
INIT_MESON_S4_COMMON_DATA(55)
};
@@ -195,6 +203,9 @@ static const struct of_device_id meson_irq_gpio_matches[] __maybe_unused = {
{ .compatible = "amlogic,a4-gpio-ao-intc", .data = &a4_ao_params },
{ .compatible = "amlogic,a4-gpio-intc", .data = &a4_params },
{ .compatible = "amlogic,a5-gpio-intc", .data = &a5_params },
+ { .compatible = "amlogic,s6-gpio-intc", .data = &s6_params },
+ { .compatible = "amlogic,s7-gpio-intc", .data = &s7_params },
+ { .compatible = "amlogic,s7d-gpio-intc", .data = &s7_params },
{ .compatible = "amlogic,c3-gpio-intc", .data = &c3_params },
{ .compatible = "amlogic,t7-gpio-intc", .data = &t7_params },
{ }
@@ -572,8 +583,9 @@ static int meson_gpio_irq_parse_dt(struct device_node *node, struct meson_gpio_i
return 0;
}
-static int meson_gpio_irq_of_init(struct device_node *node, struct device_node *parent)
+static int meson_gpio_irq_probe(struct platform_device *pdev, struct device_node *parent)
{
+ struct device_node *node = pdev->dev.of_node;
struct irq_domain *domain, *parent_domain;
struct meson_gpio_irq_controller *ctl;
int ret;
@@ -630,10 +642,9 @@ free_ctl:
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(meson_gpio_intc)
-IRQCHIP_MATCH("amlogic,meson-gpio-intc", meson_gpio_irq_of_init)
+IRQCHIP_MATCH("amlogic,meson-gpio-intc", meson_gpio_irq_probe)
IRQCHIP_PLATFORM_DRIVER_END(meson_gpio_intc)
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
MODULE_DESCRIPTION("Meson GPIO Interrupt Multiplexer driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:meson-gpio-intc");
diff --git a/drivers/irqchip/irq-mvebu-pic.c b/drivers/irqchip/irq-mvebu-pic.c
index cd8b73482b9f..10b85128183a 100644
--- a/drivers/irqchip/irq-mvebu-pic.c
+++ b/drivers/irqchip/irq-mvebu-pic.c
@@ -195,5 +195,3 @@ MODULE_AUTHOR("Yehuda Yitschak <yehuday@marvell.com>");
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
MODULE_DESCRIPTION("Marvell Armada 7K/8K PIC driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:mvebu_pic");
-
diff --git a/drivers/irqchip/irq-partition-percpu.c b/drivers/irqchip/irq-partition-percpu.c
deleted file mode 100644
index 4441ffe149ea..000000000000
--- a/drivers/irqchip/irq-partition-percpu.c
+++ /dev/null
@@ -1,241 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) 2016 ARM Limited, All Rights Reserved.
- * Author: Marc Zyngier <marc.zyngier@arm.com>
- */
-
-#include <linux/bitops.h>
-#include <linux/interrupt.h>
-#include <linux/irqchip.h>
-#include <linux/irqchip/chained_irq.h>
-#include <linux/irqchip/irq-partition-percpu.h>
-#include <linux/irqdomain.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-
-struct partition_desc {
- int nr_parts;
- struct partition_affinity *parts;
- struct irq_domain *domain;
- struct irq_desc *chained_desc;
- unsigned long *bitmap;
- struct irq_domain_ops ops;
-};
-
-static bool partition_check_cpu(struct partition_desc *part,
- unsigned int cpu, unsigned int hwirq)
-{
- return cpumask_test_cpu(cpu, &part->parts[hwirq].mask);
-}
-
-static void partition_irq_mask(struct irq_data *d)
-{
- struct partition_desc *part = irq_data_get_irq_chip_data(d);
- struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
- struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
-
- if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
- chip->irq_mask)
- chip->irq_mask(data);
-}
-
-static void partition_irq_unmask(struct irq_data *d)
-{
- struct partition_desc *part = irq_data_get_irq_chip_data(d);
- struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
- struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
-
- if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
- chip->irq_unmask)
- chip->irq_unmask(data);
-}
-
-static int partition_irq_set_irqchip_state(struct irq_data *d,
- enum irqchip_irq_state which,
- bool val)
-{
- struct partition_desc *part = irq_data_get_irq_chip_data(d);
- struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
- struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
-
- if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
- chip->irq_set_irqchip_state)
- return chip->irq_set_irqchip_state(data, which, val);
-
- return -EINVAL;
-}
-
-static int partition_irq_get_irqchip_state(struct irq_data *d,
- enum irqchip_irq_state which,
- bool *val)
-{
- struct partition_desc *part = irq_data_get_irq_chip_data(d);
- struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
- struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
-
- if (partition_check_cpu(part, smp_processor_id(), d->hwirq) &&
- chip->irq_get_irqchip_state)
- return chip->irq_get_irqchip_state(data, which, val);
-
- return -EINVAL;
-}
-
-static int partition_irq_set_type(struct irq_data *d, unsigned int type)
-{
- struct partition_desc *part = irq_data_get_irq_chip_data(d);
- struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
- struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
-
- if (chip->irq_set_type)
- return chip->irq_set_type(data, type);
-
- return -EINVAL;
-}
-
-static void partition_irq_print_chip(struct irq_data *d, struct seq_file *p)
-{
- struct partition_desc *part = irq_data_get_irq_chip_data(d);
- struct irq_chip *chip = irq_desc_get_chip(part->chained_desc);
- struct irq_data *data = irq_desc_get_irq_data(part->chained_desc);
-
- seq_printf(p, "%5s-%lu", chip->name, data->hwirq);
-}
-
-static struct irq_chip partition_irq_chip = {
- .irq_mask = partition_irq_mask,
- .irq_unmask = partition_irq_unmask,
- .irq_set_type = partition_irq_set_type,
- .irq_get_irqchip_state = partition_irq_get_irqchip_state,
- .irq_set_irqchip_state = partition_irq_set_irqchip_state,
- .irq_print_chip = partition_irq_print_chip,
-};
-
-static void partition_handle_irq(struct irq_desc *desc)
-{
- struct partition_desc *part = irq_desc_get_handler_data(desc);
- struct irq_chip *chip = irq_desc_get_chip(desc);
- int cpu = smp_processor_id();
- int hwirq;
-
- chained_irq_enter(chip, desc);
-
- for_each_set_bit(hwirq, part->bitmap, part->nr_parts) {
- if (partition_check_cpu(part, cpu, hwirq))
- break;
- }
-
- if (unlikely(hwirq == part->nr_parts))
- handle_bad_irq(desc);
- else
- generic_handle_domain_irq(part->domain, hwirq);
-
- chained_irq_exit(chip, desc);
-}
-
-static int partition_domain_alloc(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs, void *arg)
-{
- int ret;
- irq_hw_number_t hwirq;
- unsigned int type;
- struct irq_fwspec *fwspec = arg;
- struct partition_desc *part;
-
- BUG_ON(nr_irqs != 1);
- ret = domain->ops->translate(domain, fwspec, &hwirq, &type);
- if (ret)
- return ret;
-
- part = domain->host_data;
-
- set_bit(hwirq, part->bitmap);
- irq_set_chained_handler_and_data(irq_desc_get_irq(part->chained_desc),
- partition_handle_irq, part);
- irq_set_percpu_devid_partition(virq, &part->parts[hwirq].mask);
- irq_domain_set_info(domain, virq, hwirq, &partition_irq_chip, part,
- handle_percpu_devid_irq, NULL, NULL);
- irq_set_status_flags(virq, IRQ_NOAUTOEN);
-
- return 0;
-}
-
-static void partition_domain_free(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs)
-{
- struct irq_data *d;
-
- BUG_ON(nr_irqs != 1);
-
- d = irq_domain_get_irq_data(domain, virq);
- irq_set_handler(virq, NULL);
- irq_domain_reset_irq_data(d);
-}
-
-int partition_translate_id(struct partition_desc *desc, void *partition_id)
-{
- struct partition_affinity *part = NULL;
- int i;
-
- for (i = 0; i < desc->nr_parts; i++) {
- if (desc->parts[i].partition_id == partition_id) {
- part = &desc->parts[i];
- break;
- }
- }
-
- if (WARN_ON(!part)) {
- pr_err("Failed to find partition\n");
- return -EINVAL;
- }
-
- return i;
-}
-
-struct partition_desc *partition_create_desc(struct fwnode_handle *fwnode,
- struct partition_affinity *parts,
- int nr_parts,
- int chained_irq,
- const struct irq_domain_ops *ops)
-{
- struct partition_desc *desc;
- struct irq_domain *d;
-
- BUG_ON(!ops->select || !ops->translate);
-
- desc = kzalloc(sizeof(*desc), GFP_KERNEL);
- if (!desc)
- return NULL;
-
- desc->ops = *ops;
- desc->ops.free = partition_domain_free;
- desc->ops.alloc = partition_domain_alloc;
-
- d = irq_domain_create_linear(fwnode, nr_parts, &desc->ops, desc);
- if (!d)
- goto out;
- desc->domain = d;
-
- desc->bitmap = bitmap_zalloc(nr_parts, GFP_KERNEL);
- if (WARN_ON(!desc->bitmap))
- goto out;
-
- desc->chained_desc = irq_to_desc(chained_irq);
- desc->nr_parts = nr_parts;
- desc->parts = parts;
-
- return desc;
-out:
- if (d)
- irq_domain_remove(d);
- kfree(desc);
-
- return NULL;
-}
-
-struct irq_domain *partition_get_domain(struct partition_desc *dsc)
-{
- if (dsc)
- return dsc->domain;
-
- return NULL;
-}
diff --git a/drivers/irqchip/irq-qcom-mpm.c b/drivers/irqchip/irq-qcom-mpm.c
index 8d569f7c5a7a..83f31ea657b7 100644
--- a/drivers/irqchip/irq-qcom-mpm.c
+++ b/drivers/irqchip/irq-qcom-mpm.c
@@ -320,9 +320,9 @@ static bool gic_hwirq_is_mapped(struct mpm_gic_map *maps, int cnt, u32 hwirq)
return false;
}
-static int qcom_mpm_init(struct device_node *np, struct device_node *parent)
+static int qcom_mpm_probe(struct platform_device *pdev, struct device_node *parent)
{
- struct platform_device *pdev = of_find_device_by_node(np);
+ struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct irq_domain *parent_domain;
struct generic_pm_domain *genpd;
@@ -478,7 +478,7 @@ remove_genpd:
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(qcom_mpm)
-IRQCHIP_MATCH("qcom,mpm", qcom_mpm_init)
+IRQCHIP_MATCH("qcom,mpm", qcom_mpm_probe)
IRQCHIP_PLATFORM_DRIVER_END(qcom_mpm)
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. MSM Power Manager");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/irqchip/irq-renesas-rzg2l.c b/drivers/irqchip/irq-renesas-rzg2l.c
index 2a54adeb4cc7..1bf19deb02c4 100644
--- a/drivers/irqchip/irq-renesas-rzg2l.c
+++ b/drivers/irqchip/irq-renesas-rzg2l.c
@@ -8,7 +8,6 @@
*/
#include <linux/bitfield.h>
-#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
@@ -528,18 +527,15 @@ static int rzg2l_irqc_parse_interrupts(struct rzg2l_irqc_priv *priv,
return 0;
}
-static int rzg2l_irqc_common_init(struct device_node *node, struct device_node *parent,
- const struct irq_chip *irq_chip)
+static int rzg2l_irqc_common_probe(struct platform_device *pdev, struct device_node *parent,
+ const struct irq_chip *irq_chip)
{
- struct platform_device *pdev = of_find_device_by_node(node);
- struct device *dev __free(put_device) = pdev ? &pdev->dev : NULL;
struct irq_domain *irq_domain, *parent_domain;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
struct reset_control *resetn;
int ret;
- if (!pdev)
- return -ENODEV;
-
parent_domain = irq_find_host(parent);
if (!parent_domain)
return dev_err_probe(dev, -ENODEV, "cannot find parent domain\n");
@@ -583,35 +579,22 @@ static int rzg2l_irqc_common_init(struct device_node *node, struct device_node *
register_syscore_ops(&rzg2l_irqc_syscore_ops);
- /*
- * Prevent the cleanup function from invoking put_device by assigning
- * NULL to dev.
- *
- * make coccicheck will complain about missing put_device calls, but
- * those are false positives, as dev will be automatically "put" via
- * __free_put_device on the failing path.
- * On the successful path we don't actually want to "put" dev.
- */
- dev = NULL;
-
return 0;
}
-static int __init rzg2l_irqc_init(struct device_node *node,
- struct device_node *parent)
+static int rzg2l_irqc_probe(struct platform_device *pdev, struct device_node *parent)
{
- return rzg2l_irqc_common_init(node, parent, &rzg2l_irqc_chip);
+ return rzg2l_irqc_common_probe(pdev, parent, &rzg2l_irqc_chip);
}
-static int __init rzfive_irqc_init(struct device_node *node,
- struct device_node *parent)
+static int rzfive_irqc_probe(struct platform_device *pdev, struct device_node *parent)
{
- return rzg2l_irqc_common_init(node, parent, &rzfive_irqc_chip);
+ return rzg2l_irqc_common_probe(pdev, parent, &rzfive_irqc_chip);
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(rzg2l_irqc)
-IRQCHIP_MATCH("renesas,rzg2l-irqc", rzg2l_irqc_init)
-IRQCHIP_MATCH("renesas,r9a07g043f-irqc", rzfive_irqc_init)
+IRQCHIP_MATCH("renesas,rzg2l-irqc", rzg2l_irqc_probe)
+IRQCHIP_MATCH("renesas,r9a07g043f-irqc", rzfive_irqc_probe)
IRQCHIP_PLATFORM_DRIVER_END(rzg2l_irqc)
MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
MODULE_DESCRIPTION("Renesas RZ/G2L IRQC Driver");
diff --git a/drivers/irqchip/irq-renesas-rzv2h.c b/drivers/irqchip/irq-renesas-rzv2h.c
index 9018d9c3911e..899a423b5da8 100644
--- a/drivers/irqchip/irq-renesas-rzv2h.c
+++ b/drivers/irqchip/irq-renesas-rzv2h.c
@@ -490,29 +490,15 @@ static int rzv2h_icu_parse_interrupts(struct rzv2h_icu_priv *priv, struct device
return 0;
}
-static void rzv2h_icu_put_device(void *data)
-{
- put_device(data);
-}
-
-static int rzv2h_icu_init_common(struct device_node *node, struct device_node *parent,
- const struct rzv2h_hw_info *hw_info)
+static int rzv2h_icu_probe_common(struct platform_device *pdev, struct device_node *parent,
+ const struct rzv2h_hw_info *hw_info)
{
struct irq_domain *irq_domain, *parent_domain;
+ struct device_node *node = pdev->dev.of_node;
struct rzv2h_icu_priv *rzv2h_icu_data;
- struct platform_device *pdev;
struct reset_control *resetn;
int ret;
- pdev = of_find_device_by_node(node);
- if (!pdev)
- return -ENODEV;
-
- ret = devm_add_action_or_reset(&pdev->dev, rzv2h_icu_put_device,
- &pdev->dev);
- if (ret < 0)
- return ret;
-
parent_domain = irq_find_host(parent);
if (!parent_domain) {
dev_err(&pdev->dev, "cannot find parent domain\n");
@@ -618,19 +604,19 @@ static const struct rzv2h_hw_info rzv2h_hw_params = {
.field_width = 8,
};
-static int rzg3e_icu_init(struct device_node *node, struct device_node *parent)
+static int rzg3e_icu_probe(struct platform_device *pdev, struct device_node *parent)
{
- return rzv2h_icu_init_common(node, parent, &rzg3e_hw_params);
+ return rzv2h_icu_probe_common(pdev, parent, &rzg3e_hw_params);
}
-static int rzv2h_icu_init(struct device_node *node, struct device_node *parent)
+static int rzv2h_icu_probe(struct platform_device *pdev, struct device_node *parent)
{
- return rzv2h_icu_init_common(node, parent, &rzv2h_hw_params);
+ return rzv2h_icu_probe_common(pdev, parent, &rzv2h_hw_params);
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(rzv2h_icu)
-IRQCHIP_MATCH("renesas,r9a09g047-icu", rzg3e_icu_init)
-IRQCHIP_MATCH("renesas,r9a09g057-icu", rzv2h_icu_init)
+IRQCHIP_MATCH("renesas,r9a09g047-icu", rzg3e_icu_probe)
+IRQCHIP_MATCH("renesas,r9a09g057-icu", rzv2h_icu_probe)
IRQCHIP_PLATFORM_DRIVER_END(rzv2h_icu)
MODULE_AUTHOR("Fabrizio Castro <fabrizio.castro.jz@renesas.com>");
MODULE_DESCRIPTION("Renesas RZ/V2H(P) ICU Driver");
diff --git a/drivers/irqchip/irq-riscv-imsic-early.c b/drivers/irqchip/irq-riscv-imsic-early.c
index 2c4c682627b8..6bac67cc0b6d 100644
--- a/drivers/irqchip/irq-riscv-imsic-early.c
+++ b/drivers/irqchip/irq-riscv-imsic-early.c
@@ -91,9 +91,8 @@ static int __init imsic_ipi_domain_init(void) { return 0; }
*/
static void imsic_handle_irq(struct irq_desc *desc)
{
+ struct imsic_local_priv *lpriv = this_cpu_ptr(imsic->lpriv);
struct irq_chip *chip = irq_desc_get_chip(desc);
- int cpu = smp_processor_id();
- struct imsic_vector *vec;
unsigned long local_id;
/*
@@ -113,16 +112,12 @@ static void imsic_handle_irq(struct irq_desc *desc)
continue;
}
- if (unlikely(!imsic->base_domain))
- continue;
-
- vec = imsic_vector_from_local_id(cpu, local_id);
- if (!vec) {
+ if (unlikely(local_id > imsic->global.nr_ids)) {
pr_warn_ratelimited("vector not found for local ID 0x%lx\n", local_id);
continue;
}
- generic_handle_irq(vec->irq);
+ generic_handle_irq(lpriv->vectors[local_id].irq);
}
chained_irq_exit(chip, desc);
diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c
index 643c8e459611..7228a33f6c37 100644
--- a/drivers/irqchip/irq-riscv-imsic-platform.c
+++ b/drivers/irqchip/irq-riscv-imsic-platform.c
@@ -158,11 +158,11 @@ static int imsic_irq_set_affinity(struct irq_data *d, const struct cpumask *mask
tmp_vec.local_id = new_vec->local_id;
/* Point device to the temporary vector */
- imsic_msi_update_msg(irq_get_irq_data(d->irq), &tmp_vec);
+ imsic_msi_update_msg(d, &tmp_vec);
}
/* Point device to the new vector */
- imsic_msi_update_msg(irq_get_irq_data(d->irq), new_vec);
+ imsic_msi_update_msg(d, new_vec);
/* Update irq descriptors with the new vector */
d->chip_data = new_vec;
diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c
index dc95ad856d80..385368052d5c 100644
--- a/drivers/irqchip/irq-riscv-imsic-state.c
+++ b/drivers/irqchip/irq-riscv-imsic-state.c
@@ -434,16 +434,6 @@ void imsic_vector_debug_show_summary(struct seq_file *m, int ind)
}
#endif
-struct imsic_vector *imsic_vector_from_local_id(unsigned int cpu, unsigned int local_id)
-{
- struct imsic_local_priv *lpriv = per_cpu_ptr(imsic->lpriv, cpu);
-
- if (!lpriv || imsic->global.nr_ids < local_id)
- return NULL;
-
- return &lpriv->vectors[local_id];
-}
-
struct imsic_vector *imsic_vector_alloc(unsigned int irq, const struct cpumask *mask)
{
struct imsic_vector *vec = NULL;
@@ -487,7 +477,6 @@ static void __init imsic_local_cleanup(void)
lpriv = per_cpu_ptr(imsic->lpriv, cpu);
bitmap_free(lpriv->dirty_bitmap);
- kfree(lpriv->vectors);
}
free_percpu(imsic->lpriv);
@@ -501,7 +490,8 @@ static int __init imsic_local_init(void)
int cpu, i;
/* Allocate per-CPU private state */
- imsic->lpriv = alloc_percpu(typeof(*imsic->lpriv));
+ imsic->lpriv = __alloc_percpu(struct_size(imsic->lpriv, vectors, global->nr_ids + 1),
+ __alignof__(*imsic->lpriv));
if (!imsic->lpriv)
return -ENOMEM;
@@ -521,12 +511,6 @@ static int __init imsic_local_init(void)
timer_setup(&lpriv->timer, imsic_local_timer_callback, TIMER_PINNED);
#endif
- /* Allocate vector array */
- lpriv->vectors = kcalloc(global->nr_ids + 1, sizeof(*lpriv->vectors),
- GFP_KERNEL);
- if (!lpriv->vectors)
- goto fail_local_cleanup;
-
/* Setup vector array */
for (i = 0; i <= global->nr_ids; i++) {
vec = &lpriv->vectors[i];
diff --git a/drivers/irqchip/irq-riscv-imsic-state.h b/drivers/irqchip/irq-riscv-imsic-state.h
index 57f951952b0c..6332501dcbd8 100644
--- a/drivers/irqchip/irq-riscv-imsic-state.h
+++ b/drivers/irqchip/irq-riscv-imsic-state.h
@@ -40,7 +40,7 @@ struct imsic_local_priv {
#endif
/* Local vector table */
- struct imsic_vector *vectors;
+ struct imsic_vector vectors[];
};
struct imsic_priv {
@@ -95,8 +95,6 @@ static inline struct imsic_vector *imsic_vector_get_move(struct imsic_vector *ve
void imsic_vector_force_move_cleanup(struct imsic_vector *vec);
void imsic_vector_move(struct imsic_vector *old_vec, struct imsic_vector *new_vec);
-struct imsic_vector *imsic_vector_from_local_id(unsigned int cpu, unsigned int local_id);
-
struct imsic_vector *imsic_vector_alloc(unsigned int irq, const struct cpumask *mask);
void imsic_vector_free(struct imsic_vector *vector);
diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c
index e5805885394e..70290b35b317 100644
--- a/drivers/irqchip/irq-riscv-intc.c
+++ b/drivers/irqchip/irq-riscv-intc.c
@@ -166,7 +166,8 @@ static int riscv_intc_domain_alloc(struct irq_domain *domain,
static const struct irq_domain_ops riscv_intc_domain_ops = {
.map = riscv_intc_domain_map,
.xlate = irq_domain_xlate_onecell,
- .alloc = riscv_intc_domain_alloc
+ .alloc = riscv_intc_domain_alloc,
+ .free = irq_domain_free_irqs_top,
};
static struct fwnode_handle *riscv_intc_hwnode(void)
diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
index cbd7697bc148..c5db7d6e3f7c 100644
--- a/drivers/irqchip/irq-sifive-plic.c
+++ b/drivers/irqchip/irq-sifive-plic.c
@@ -49,6 +49,8 @@
#define CONTEXT_ENABLE_BASE 0x2000
#define CONTEXT_ENABLE_SIZE 0x80
+#define PENDING_BASE 0x1000
+
/*
* Each hart context has a set of control registers associated with it. Right
* now there's only two: a source priority threshold over which the hart will
@@ -63,6 +65,7 @@
#define PLIC_ENABLE_THRESHOLD 0
#define PLIC_QUIRK_EDGE_INTERRUPT 0
+#define PLIC_QUIRK_CP100_CLAIM_REGISTER_ERRATUM 1
struct plic_priv {
struct fwnode_handle *fwnode;
@@ -94,15 +97,22 @@ static DEFINE_PER_CPU(struct plic_handler, plic_handlers);
static int plic_irq_set_type(struct irq_data *d, unsigned int type);
-static void __plic_toggle(void __iomem *enable_base, int hwirq, int enable)
+static void __plic_toggle(struct plic_handler *handler, int hwirq, int enable)
{
- u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32);
+ u32 __iomem *base = handler->enable_base;
u32 hwirq_mask = 1 << (hwirq % 32);
+ int group = hwirq / 32;
+ u32 value;
+
+ value = readl(base + group);
if (enable)
- writel(readl(reg) | hwirq_mask, reg);
+ value |= hwirq_mask;
else
- writel(readl(reg) & ~hwirq_mask, reg);
+ value &= ~hwirq_mask;
+
+ handler->enable_save[group] = value;
+ writel(value, base + group);
}
static void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
@@ -110,7 +120,7 @@ static void plic_toggle(struct plic_handler *handler, int hwirq, int enable)
unsigned long flags;
raw_spin_lock_irqsave(&handler->enable_lock, flags);
- __plic_toggle(handler->enable_base, hwirq, enable);
+ __plic_toggle(handler, hwirq, enable);
raw_spin_unlock_irqrestore(&handler->enable_lock, flags);
}
@@ -247,33 +257,16 @@ static int plic_irq_set_type(struct irq_data *d, unsigned int type)
static int plic_irq_suspend(void)
{
- unsigned int i, cpu;
- unsigned long flags;
- u32 __iomem *reg;
struct plic_priv *priv;
priv = per_cpu_ptr(&plic_handlers, smp_processor_id())->priv;
/* irq ID 0 is reserved */
- for (i = 1; i < priv->nr_irqs; i++) {
+ for (unsigned int i = 1; i < priv->nr_irqs; i++) {
__assign_bit(i, priv->prio_save,
readl(priv->regs + PRIORITY_BASE + i * PRIORITY_PER_ID));
}
- for_each_present_cpu(cpu) {
- struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
-
- if (!handler->present)
- continue;
-
- raw_spin_lock_irqsave(&handler->enable_lock, flags);
- for (i = 0; i < DIV_ROUND_UP(priv->nr_irqs, 32); i++) {
- reg = handler->enable_base + i * sizeof(u32);
- handler->enable_save[i] = readl(reg);
- }
- raw_spin_unlock_irqrestore(&handler->enable_lock, flags);
- }
-
return 0;
}
@@ -398,6 +391,98 @@ static void plic_handle_irq(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
+static u32 cp100_isolate_pending_irq(int nr_irq_groups, struct plic_handler *handler)
+{
+ u32 __iomem *pending = handler->priv->regs + PENDING_BASE;
+ u32 __iomem *enable = handler->enable_base;
+ u32 pending_irqs = 0;
+ int i, j;
+
+ /* Look for first pending interrupt */
+ for (i = 0; i < nr_irq_groups; i++) {
+ /* Any pending interrupts would be annihilated, so skip checking them */
+ if (!handler->enable_save[i])
+ continue;
+
+ pending_irqs = handler->enable_save[i] & readl_relaxed(pending + i);
+ if (pending_irqs)
+ break;
+ }
+
+ if (!pending_irqs)
+ return 0;
+
+ /* Isolate lowest set bit */
+ pending_irqs &= -pending_irqs;
+
+ /* Disable all interrupts but the first pending one */
+ for (j = 0; j < nr_irq_groups; j++) {
+ u32 new_mask = j == i ? pending_irqs : 0;
+
+ if (new_mask != handler->enable_save[j])
+ writel_relaxed(new_mask, enable + j);
+ }
+ return pending_irqs;
+}
+
+static irq_hw_number_t cp100_get_hwirq(struct plic_handler *handler, void __iomem *claim)
+{
+ int nr_irq_groups = DIV_ROUND_UP(handler->priv->nr_irqs, 32);
+ u32 __iomem *enable = handler->enable_base;
+ irq_hw_number_t hwirq = 0;
+ u32 iso_mask;
+ int i;
+
+ guard(raw_spinlock)(&handler->enable_lock);
+
+ /* Existing enable state is already cached in enable_save */
+ iso_mask = cp100_isolate_pending_irq(nr_irq_groups, handler);
+ if (!iso_mask)
+ return 0;
+
+ /*
+ * Interrupts delievered to hardware still become pending, but only
+ * interrupts that are both pending and enabled can be claimed.
+ * Clearing the enable bit for all interrupts but the first pending
+ * one avoids a hardware bug that occurs during read from the claim
+ * register with more than one eligible interrupt.
+ */
+ hwirq = readl(claim);
+
+ /* Restore previous state */
+ for (i = 0; i < nr_irq_groups; i++) {
+ u32 written = i == hwirq / 32 ? iso_mask : 0;
+ u32 stored = handler->enable_save[i];
+
+ if (stored != written)
+ writel_relaxed(stored, enable + i);
+ }
+ return hwirq;
+}
+
+static void plic_handle_irq_cp100(struct irq_desc *desc)
+{
+ struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ void __iomem *claim = handler->hart_base + CONTEXT_CLAIM;
+ irq_hw_number_t hwirq;
+
+ WARN_ON_ONCE(!handler->present);
+
+ chained_irq_enter(chip, desc);
+
+ while ((hwirq = cp100_get_hwirq(handler, claim))) {
+ int err = generic_handle_domain_irq(handler->priv->irqdomain, hwirq);
+
+ if (unlikely(err)) {
+ pr_warn_ratelimited("%pfwP: can't find mapping for hwirq %lu\n",
+ handler->priv->fwnode, hwirq);
+ }
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
static void plic_set_threshold(struct plic_handler *handler, u32 threshold)
{
/* priority must be > threshold to trigger an interrupt */
@@ -434,6 +519,8 @@ static const struct of_device_id plic_match[] = {
.data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
{ .compatible = "thead,c900-plic",
.data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
+ { .compatible = "ultrarisc,cp100-plic",
+ .data = (const void *)BIT(PLIC_QUIRK_CP100_CLAIM_REGISTER_ERRATUM) },
{}
};
@@ -592,12 +679,11 @@ static int plic_probe(struct fwnode_handle *fwnode)
if (parent_hwirq != RV_IRQ_EXT) {
/* Disable S-mode enable bits if running in M-mode. */
if (IS_ENABLED(CONFIG_RISCV_M_MODE)) {
- void __iomem *enable_base = priv->regs +
- CONTEXT_ENABLE_BASE +
- i * CONTEXT_ENABLE_SIZE;
+ u32 __iomem *enable_base = priv->regs + CONTEXT_ENABLE_BASE +
+ i * CONTEXT_ENABLE_SIZE;
- for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
- __plic_toggle(enable_base, hwirq, 0);
+ for (int j = 0; j <= nr_irqs / 32; j++)
+ writel(0, enable_base + j);
}
continue;
}
@@ -668,12 +754,17 @@ done:
}
if (global_setup) {
+ void (*handler_fn)(struct irq_desc *) = plic_handle_irq;
+
+ if (test_bit(PLIC_QUIRK_CP100_CLAIM_REGISTER_ERRATUM, &handler->priv->plic_quirks))
+ handler_fn = plic_handle_irq_cp100;
+
/* Find parent domain and register chained handler */
domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(), DOMAIN_BUS_ANY);
if (domain)
plic_parent_irq = irq_create_mapping(domain, RV_IRQ_EXT);
if (plic_parent_irq)
- irq_set_chained_handler(plic_parent_irq, plic_handle_irq);
+ irq_set_chained_handler(plic_parent_irq, handler_fn);
cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
"irqchip/sifive/plic:starting",
diff --git a/drivers/irqchip/irq-starfive-jh8100-intc.c b/drivers/irqchip/irq-starfive-jh8100-intc.c
index 2460798ec158..705361b4ebe0 100644
--- a/drivers/irqchip/irq-starfive-jh8100-intc.c
+++ b/drivers/irqchip/irq-starfive-jh8100-intc.c
@@ -114,9 +114,9 @@ static void starfive_intc_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
-static int __init starfive_intc_init(struct device_node *intc,
- struct device_node *parent)
+static int starfive_intc_probe(struct platform_device *pdev, struct device_node *parent)
{
+ struct device_node *intc = pdev->dev.of_node;
struct starfive_irq_chip *irqc;
struct reset_control *rst;
struct clk *clk;
@@ -199,7 +199,7 @@ err_free:
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(starfive_intc)
-IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_init)
+IRQCHIP_MATCH("starfive,jh8100-intc", starfive_intc_probe)
IRQCHIP_PLATFORM_DRIVER_END(starfive_intc)
MODULE_DESCRIPTION("StarFive JH8100 External Interrupt Controller");
diff --git a/drivers/irqchip/irq-ts4800.c b/drivers/irqchip/irq-ts4800.c
index 1e236d5b7516..2e4013c6834d 100644
--- a/drivers/irqchip/irq-ts4800.c
+++ b/drivers/irqchip/irq-ts4800.c
@@ -165,4 +165,3 @@ module_platform_driver(ts4800_ic_driver);
MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
MODULE_DESCRIPTION("Multiplexed-IRQs driver for TS-4800's FPGA");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:ts4800_irqc");
diff --git a/drivers/irqchip/irqchip.c b/drivers/irqchip/irqchip.c
index 0ee7b6b71f5f..689c8e448901 100644
--- a/drivers/irqchip/irqchip.c
+++ b/drivers/irqchip/irqchip.c
@@ -36,11 +36,10 @@ int platform_irqchip_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *par_np __free(device_node) = of_irq_find_parent(np);
- of_irq_init_cb_t irq_init_cb = of_device_get_match_data(&pdev->dev);
+ platform_irq_probe_t irq_probe = of_device_get_match_data(&pdev->dev);
- if (!irq_init_cb) {
+ if (!irq_probe)
return -EINVAL;
- }
if (par_np == np)
par_np = NULL;
@@ -53,10 +52,9 @@ int platform_irqchip_probe(struct platform_device *pdev)
* interrupt controller. The actual initialization callback of this
* interrupt controller can check for specific domains as necessary.
*/
- if (par_np && !irq_find_matching_host(par_np, DOMAIN_BUS_ANY)) {
+ if (par_np && !irq_find_matching_host(par_np, DOMAIN_BUS_ANY))
return -EPROBE_DEFER;
- }
- return irq_init_cb(np, par_np);
+ return irq_probe(pdev, par_np);
}
EXPORT_SYMBOL_GPL(platform_irqchip_probe);
diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c
index 18e696dc7f4d..09819007d08e 100644
--- a/drivers/irqchip/qcom-irq-combiner.c
+++ b/drivers/irqchip/qcom-irq-combiner.c
@@ -222,7 +222,7 @@ static int get_registers(struct platform_device *pdev, struct combiner *comb)
return 0;
}
-static int __init combiner_probe(struct platform_device *pdev)
+static int combiner_probe(struct platform_device *pdev)
{
struct combiner *combiner;
int nregs;
@@ -266,11 +266,11 @@ static const struct acpi_device_id qcom_irq_combiner_ids[] = {
{ }
};
-static struct platform_driver qcom_irq_combiner_probe = {
+static struct platform_driver qcom_irq_combiner_driver = {
.driver = {
.name = "qcom-irq-combiner",
.acpi_match_table = ACPI_PTR(qcom_irq_combiner_ids),
},
.probe = combiner_probe,
};
-builtin_platform_driver(qcom_irq_combiner_probe);
+builtin_platform_driver(qcom_irq_combiner_driver);
diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c
index 52d77546aacb..518f7f0f3dab 100644
--- a/drivers/irqchip/qcom-pdc.c
+++ b/drivers/irqchip/qcom-pdc.c
@@ -350,9 +350,10 @@ static int pdc_setup_pin_mapping(struct device_node *np)
#define QCOM_PDC_SIZE 0x30000
-static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
+static int qcom_pdc_probe(struct platform_device *pdev, struct device_node *parent)
{
struct irq_domain *parent_domain, *pdc_domain;
+ struct device_node *node = pdev->dev.of_node;
resource_size_t res_size;
struct resource res;
int ret;
@@ -428,7 +429,7 @@ fail:
}
IRQCHIP_PLATFORM_DRIVER_BEGIN(qcom_pdc)
-IRQCHIP_MATCH("qcom,pdc", qcom_pdc_init)
+IRQCHIP_MATCH("qcom,pdc", qcom_pdc_probe)
IRQCHIP_PLATFORM_DRIVER_END(qcom_pdc)
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Power Domain Controller");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
index c5d13bdc239b..e8f7e52354bc 100644
--- a/drivers/isdn/capi/kcapi.c
+++ b/drivers/isdn/capi/kcapi.c
@@ -907,7 +907,7 @@ int __init kcapi_init(void)
{
int err;
- kcapi_wq = alloc_workqueue("kcapi", 0, 0);
+ kcapi_wq = alloc_workqueue("kcapi", WQ_PERCPU, 0);
if (!kcapi_wq)
return -ENOMEM;
diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c
index e54419a4e731..541a20cb58f1 100644
--- a/drivers/isdn/hardware/mISDN/hfcsusb.c
+++ b/drivers/isdn/hardware/mISDN/hfcsusb.c
@@ -1904,13 +1904,13 @@ out:
mISDN_freebchannel(&hw->bch[1]);
mISDN_freebchannel(&hw->bch[0]);
mISDN_freedchannel(&hw->dch);
- kfree(hw);
return err;
}
static int
hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
+ int err;
struct hfcsusb *hw;
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *iface = intf->cur_altsetting;
@@ -2101,20 +2101,28 @@ hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
if (!hw->ctrl_urb) {
pr_warn("%s: No memory for control urb\n",
driver_info->vend_name);
- kfree(hw);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto err_free_hw;
}
pr_info("%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n",
hw->name, __func__, driver_info->vend_name,
conf_str[small_match], ifnum, alt_used);
- if (setup_instance(hw, dev->dev.parent))
- return -EIO;
+ if (setup_instance(hw, dev->dev.parent)) {
+ err = -EIO;
+ goto err_free_urb;
+ }
hw->intf = intf;
usb_set_intfdata(hw->intf, hw);
return 0;
+
+err_free_urb:
+ usb_free_urb(hw->ctrl_urb);
+err_free_hw:
+ kfree(hw);
+ return err;
}
/* function called when an active device is removed */
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
index f732f6614d37..6ab036e4a35f 100644
--- a/drivers/isdn/mISDN/l1oip_core.c
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -676,7 +676,7 @@ l1oip_socket_thread(void *data)
hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport);
/* bind to incoming port */
- if (socket->ops->bind(socket, (struct sockaddr *)&hc->sin_local,
+ if (socket->ops->bind(socket, (struct sockaddr_unsized *)&hc->sin_local,
sizeof(hc->sin_local))) {
printk(KERN_ERR "%s: Failed to bind socket to port %d.\n",
__func__, hc->localport);
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
index b215b28cad7b..77b900db1cac 100644
--- a/drivers/isdn/mISDN/socket.c
+++ b/drivers/isdn/mISDN/socket.c
@@ -462,7 +462,7 @@ static int data_sock_getsockopt(struct socket *sock, int level, int optname,
}
static int
-data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+data_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct sock *sk = sock->sk;
@@ -696,7 +696,7 @@ base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
static int
-base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+base_sock_bind(struct socket *sock, struct sockaddr_unsized *addr, int addr_len)
{
struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
struct sock *sk = sock->sk;
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 06e6291be11b..11e7282dc297 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -214,10 +214,6 @@ config LEDS_EL15203000
To compile this driver as a module, choose M here: the module
will be called leds-el15203000.
-config LEDS_EXPRESSWIRE
- bool
- depends on GPIOLIB
-
config LEDS_TURRIS_OMNIA
tristate "LED support for CZ.NIC's Turris Omnia"
depends on LEDS_CLASS_MULTICOLOR
@@ -443,8 +439,8 @@ config LEDS_LP55XX_COMMON
depends on LEDS_CLASS_MULTICOLOR
depends on OF
depends on I2C
- select FW_LOADER
- select FW_LOADER_USER_HELPER
+ imply FW_LOADER
+ imply FW_LOADER_USER_HELPER
help
This option supports common operations for LP5521/5523/55231/5562/5569/
8501 devices.
diff --git a/drivers/leds/flash/leds-rt4505.c b/drivers/leds/flash/leds-rt4505.c
index f16358b8dfc1..18fd5b7e528f 100644
--- a/drivers/leds/flash/leds-rt4505.c
+++ b/drivers/leds/flash/leds-rt4505.c
@@ -365,7 +365,7 @@ static int rt4505_probe(struct i2c_client *client)
return ret;
}
- child = fwnode_get_next_available_child_node(client->dev.fwnode, NULL);
+ child = device_get_next_child_node(&client->dev, NULL);
if (!child) {
dev_err(priv->dev, "Failed to get child node\n");
return -EINVAL;
diff --git a/drivers/leds/flash/leds-rt8515.c b/drivers/leds/flash/leds-rt8515.c
index 6af0d2c7fc56..f6b439674c03 100644
--- a/drivers/leds/flash/leds-rt8515.c
+++ b/drivers/leds/flash/leds-rt8515.c
@@ -304,7 +304,7 @@ static int rt8515_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(rt->enable_torch),
"cannot get ENT (enable torch) GPIO\n");
- child = fwnode_get_next_available_child_node(dev->fwnode, NULL);
+ child = device_get_next_child_node(dev, NULL);
if (!child) {
dev_err(dev,
"No fwnode child node found for connected LED.\n");
diff --git a/drivers/leds/flash/leds-sgm3140.c b/drivers/leds/flash/leds-sgm3140.c
index 3e83200675f2..dc6840357370 100644
--- a/drivers/leds/flash/leds-sgm3140.c
+++ b/drivers/leds/flash/leds-sgm3140.c
@@ -214,8 +214,7 @@ static int sgm3140_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"Failed to request regulator\n");
- child_node = fwnode_get_next_available_child_node(pdev->dev.fwnode,
- NULL);
+ child_node = device_get_next_child_node(&pdev->dev, NULL);
if (!child_node) {
dev_err(&pdev->dev,
"No fwnode child node found for connected LED.\n");
diff --git a/drivers/leds/flash/leds-tps6131x.c b/drivers/leds/flash/leds-tps6131x.c
index 6f4d4fd55361..f0f1f2b77d5a 100644
--- a/drivers/leds/flash/leds-tps6131x.c
+++ b/drivers/leds/flash/leds-tps6131x.c
@@ -544,7 +544,7 @@ static int tps6131x_parse_node(struct tps6131x *tps6131x)
tps6131x->valley_current_limit = device_property_read_bool(dev, "ti,valley-current-limit");
- tps6131x->led_node = fwnode_get_next_available_child_node(dev->fwnode, NULL);
+ tps6131x->led_node = device_get_next_child_node(dev, NULL);
if (!tps6131x->led_node) {
dev_err(dev, "Missing LED node\n");
return -EINVAL;
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index f3faf37f9a08..885399ed0776 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -38,7 +38,7 @@ static ssize_t brightness_show(struct device *dev,
brightness = led_cdev->brightness;
mutex_unlock(&led_cdev->led_access);
- return sprintf(buf, "%u\n", brightness);
+ return sysfs_emit(buf, "%u\n", brightness);
}
static ssize_t brightness_store(struct device *dev,
@@ -80,7 +80,7 @@ static ssize_t max_brightness_show(struct device *dev,
max_brightness = led_cdev->max_brightness;
mutex_unlock(&led_cdev->led_access);
- return sprintf(buf, "%u\n", max_brightness);
+ return sysfs_emit(buf, "%u\n", max_brightness);
}
static DEVICE_ATTR_RO(max_brightness);
@@ -122,7 +122,7 @@ static ssize_t brightness_hw_changed_show(struct device *dev,
if (led_cdev->brightness_hw_changed == -1)
return -ENODATA;
- return sprintf(buf, "%u\n", led_cdev->brightness_hw_changed);
+ return sysfs_emit(buf, "%u\n", led_cdev->brightness_hw_changed);
}
static DEVICE_ATTR_RO(brightness_hw_changed);
diff --git a/drivers/leds/leds-cros_ec.c b/drivers/leds/leds-cros_ec.c
index 377cf04e202a..bea3cc3fbfd2 100644
--- a/drivers/leds/leds-cros_ec.c
+++ b/drivers/leds/leds-cros_ec.c
@@ -142,9 +142,6 @@ static int cros_ec_led_count_subleds(struct device *dev,
}
}
- if (!num_subleds)
- return -EINVAL;
-
*max_brightness = common_range;
return num_subleds;
}
@@ -189,6 +186,8 @@ static int cros_ec_led_probe_one(struct device *dev, struct cros_ec_device *cros
&priv->led_mc_cdev.led_cdev.max_brightness);
if (num_subleds < 0)
return num_subleds;
+ if (num_subleds == 0)
+ return 0; /* LED without any colors, skip */
priv->cros_ec = cros_ec;
priv->led_id = id;
diff --git a/drivers/leds/leds-lp50xx.c b/drivers/leds/leds-lp50xx.c
index 94f8ef6b482c..e2a9c8592953 100644
--- a/drivers/leds/leds-lp50xx.c
+++ b/drivers/leds/leds-lp50xx.c
@@ -50,11 +50,17 @@
#define LP50XX_SW_RESET 0xff
#define LP50XX_CHIP_EN BIT(6)
+#define LP50XX_CHIP_DISABLE 0x00
+#define LP50XX_START_TIME_US 500
+#define LP50XX_RESET_TIME_US 3
+
+#define LP50XX_EN_GPIO_LOW 0
+#define LP50XX_EN_GPIO_HIGH 1
/* There are 3 LED outputs per bank */
#define LP50XX_LEDS_PER_MODULE 3
-#define LP5009_MAX_LED_MODULES 2
+#define LP5009_MAX_LED_MODULES 3
#define LP5012_MAX_LED_MODULES 4
#define LP5018_MAX_LED_MODULES 6
#define LP5024_MAX_LED_MODULES 8
@@ -341,17 +347,15 @@ out:
return ret;
}
-static int lp50xx_set_banks(struct lp50xx *priv, u32 led_banks[])
+static int lp50xx_set_banks(struct lp50xx *priv, u32 led_banks[], int num_leds)
{
u8 led_config_lo, led_config_hi;
u32 bank_enable_mask = 0;
int ret;
int i;
- for (i = 0; i < priv->chip_info->max_modules; i++) {
- if (led_banks[i])
- bank_enable_mask |= (1 << led_banks[i]);
- }
+ for (i = 0; i < num_leds; i++)
+ bank_enable_mask |= (1 << led_banks[i]);
led_config_lo = bank_enable_mask;
led_config_hi = bank_enable_mask >> 8;
@@ -371,19 +375,42 @@ static int lp50xx_reset(struct lp50xx *priv)
return regmap_write(priv->regmap, priv->chip_info->reset_reg, LP50XX_SW_RESET);
}
-static int lp50xx_enable_disable(struct lp50xx *priv, int enable_disable)
+static int lp50xx_enable(struct lp50xx *priv)
{
int ret;
- ret = gpiod_direction_output(priv->enable_gpio, enable_disable);
+ if (priv->enable_gpio) {
+ ret = gpiod_direction_output(priv->enable_gpio, LP50XX_EN_GPIO_HIGH);
+ if (ret)
+ return ret;
+
+ udelay(LP50XX_START_TIME_US);
+ }
+
+ ret = lp50xx_reset(priv);
if (ret)
return ret;
- if (enable_disable)
- return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN);
- else
- return regmap_write(priv->regmap, LP50XX_DEV_CFG0, 0);
+ return regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_EN);
+}
+
+static int lp50xx_disable(struct lp50xx *priv)
+{
+ int ret;
+
+ ret = regmap_write(priv->regmap, LP50XX_DEV_CFG0, LP50XX_CHIP_DISABLE);
+ if (ret)
+ return ret;
+ if (priv->enable_gpio) {
+ ret = gpiod_direction_output(priv->enable_gpio, LP50XX_EN_GPIO_LOW);
+ if (ret)
+ return ret;
+
+ udelay(LP50XX_RESET_TIME_US);
+ }
+
+ return 0;
}
static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv,
@@ -405,7 +432,7 @@ static int lp50xx_probe_leds(struct fwnode_handle *child, struct lp50xx *priv,
return ret;
}
- ret = lp50xx_set_banks(priv, led_banks);
+ ret = lp50xx_set_banks(priv, led_banks, num_leds);
if (ret) {
dev_err(priv->dev, "Cannot setup banked LEDs\n");
return ret;
@@ -447,6 +474,10 @@ static int lp50xx_probe_dt(struct lp50xx *priv)
return dev_err_probe(priv->dev, PTR_ERR(priv->enable_gpio),
"Failed to get enable GPIO\n");
+ ret = lp50xx_enable(priv);
+ if (ret)
+ return ret;
+
priv->regulator = devm_regulator_get(priv->dev, "vled");
if (IS_ERR(priv->regulator))
priv->regulator = NULL;
@@ -547,14 +578,6 @@ static int lp50xx_probe(struct i2c_client *client)
return ret;
}
- ret = lp50xx_reset(led);
- if (ret)
- return ret;
-
- ret = lp50xx_enable_disable(led, 1);
- if (ret)
- return ret;
-
return lp50xx_probe_dt(led);
}
@@ -563,7 +586,7 @@ static void lp50xx_remove(struct i2c_client *client)
struct lp50xx *led = i2c_get_clientdata(client);
int ret;
- ret = lp50xx_enable_disable(led, 0);
+ ret = lp50xx_disable(led);
if (ret)
dev_err(led->dev, "Failed to disable chip\n");
diff --git a/drivers/leds/leds-max5970.c b/drivers/leds/leds-max5970.c
index 285074c53b23..a1e91a06249c 100644
--- a/drivers/leds/leds-max5970.c
+++ b/drivers/leds/leds-max5970.c
@@ -60,7 +60,7 @@ static int max5970_led_probe(struct platform_device *pdev)
if (!led_node)
return -ENODEV;
- fwnode_for_each_available_child_node(led_node, child) {
+ fwnode_for_each_child_node(led_node, child) {
u32 reg;
if (fwnode_property_read_u32(child, "reg", &reg))
diff --git a/drivers/leds/leds-max77705.c b/drivers/leds/leds-max77705.c
index b7403b3fcf5e..1e2054c1bf80 100644
--- a/drivers/leds/leds-max77705.c
+++ b/drivers/leds/leds-max77705.c
@@ -191,7 +191,7 @@ static int max77705_add_led(struct device *dev, struct regmap *regmap, struct fw
cdev->brightness_set_blocking = max77705_led_brightness_set_multi;
cdev->blink_set = max77705_rgb_blink;
- fwnode_for_each_available_child_node(np, child) {
+ fwnode_for_each_child_node(np, child) {
ret = max77705_parse_subled(dev, child, &info[i]);
if (ret < 0)
return ret;
diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c
index e95287416ef8..99df46f2d9f5 100644
--- a/drivers/leds/leds-netxbig.c
+++ b/drivers/leds/leds-netxbig.c
@@ -364,6 +364,9 @@ static int netxbig_gpio_ext_get(struct device *dev,
if (!addr)
return -ENOMEM;
+ gpio_ext->addr = addr;
+ gpio_ext->num_addr = 0;
+
/*
* We cannot use devm_ managed resources with these GPIO descriptors
* since they are associated with the "GPIO extension device" which
@@ -375,45 +378,58 @@ static int netxbig_gpio_ext_get(struct device *dev,
gpiod = gpiod_get_index(gpio_ext_dev, "addr", i,
GPIOD_OUT_LOW);
if (IS_ERR(gpiod))
- return PTR_ERR(gpiod);
+ goto err_set_code;
gpiod_set_consumer_name(gpiod, "GPIO extension addr");
addr[i] = gpiod;
+ gpio_ext->num_addr++;
}
- gpio_ext->addr = addr;
- gpio_ext->num_addr = num_addr;
ret = gpiod_count(gpio_ext_dev, "data");
if (ret < 0) {
dev_err(dev,
"Failed to count GPIOs in DT property data-gpios\n");
- return ret;
+ goto err_free_addr;
}
num_data = ret;
data = devm_kcalloc(dev, num_data, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
+ if (!data) {
+ ret = -ENOMEM;
+ goto err_free_addr;
+ }
+
+ gpio_ext->data = data;
+ gpio_ext->num_data = 0;
for (i = 0; i < num_data; i++) {
gpiod = gpiod_get_index(gpio_ext_dev, "data", i,
GPIOD_OUT_LOW);
if (IS_ERR(gpiod))
- return PTR_ERR(gpiod);
+ goto err_free_data;
gpiod_set_consumer_name(gpiod, "GPIO extension data");
data[i] = gpiod;
+ gpio_ext->num_data++;
}
- gpio_ext->data = data;
- gpio_ext->num_data = num_data;
gpiod = gpiod_get(gpio_ext_dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(gpiod)) {
dev_err(dev,
"Failed to get GPIO from DT property enable-gpio\n");
- return PTR_ERR(gpiod);
+ goto err_free_data;
}
gpiod_set_consumer_name(gpiod, "GPIO extension enable");
gpio_ext->enable = gpiod;
return devm_add_action_or_reset(dev, netxbig_gpio_ext_remove, gpio_ext);
+
+err_free_data:
+ for (i = 0; i < gpio_ext->num_data; i++)
+ gpiod_put(gpio_ext->data[i]);
+err_set_code:
+ ret = PTR_ERR(gpiod);
+err_free_addr:
+ for (i = 0; i < gpio_ext->num_addr; i++)
+ gpiod_put(gpio_ext->addr[i]);
+ return ret;
}
static int netxbig_leds_get_of_pdata(struct device *dev,
diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c
index c73134e7b951..6c1f2f50ff85 100644
--- a/drivers/leds/leds-pwm.c
+++ b/drivers/leds/leds-pwm.c
@@ -9,12 +9,13 @@
* based on leds-gpio.c by Raphael Assenat <raph@8d.com>
*/
-#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
#include <linux/leds.h>
-#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/slab.h>
@@ -26,6 +27,7 @@ struct led_pwm {
};
struct led_pwm_data {
+ struct gpio_desc *enable_gpio;
struct led_classdev cdev;
struct pwm_device *pwm;
struct pwm_state pwmstate;
@@ -51,6 +53,8 @@ static int led_pwm_set(struct led_classdev *led_cdev,
if (led_dat->active_low)
duty = led_dat->pwmstate.period - duty;
+ gpiod_set_value_cansleep(led_dat->enable_gpio, !!brightness);
+
led_dat->pwmstate.duty_cycle = duty;
/*
* Disabling a PWM doesn't guarantee that it emits the inactive level.
@@ -132,6 +136,21 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv,
break;
}
+ /*
+ * Claim the GPIO as GPIOD_ASIS and set the value
+ * later on to honor the different default states
+ */
+ led_data->enable_gpio = devm_fwnode_gpiod_get(dev, fwnode, "enable", GPIOD_ASIS, NULL);
+ if (IS_ERR(led_data->enable_gpio)) {
+ if (PTR_ERR(led_data->enable_gpio) == -ENOENT)
+ /* Enable GPIO is optional */
+ led_data->enable_gpio = NULL;
+ else
+ return PTR_ERR(led_data->enable_gpio);
+ }
+
+ gpiod_direction_output(led_data->enable_gpio, !!led_data->cdev.brightness);
+
ret = devm_led_classdev_register_ext(dev, &led_data->cdev, &init_data);
if (ret) {
dev_err(dev, "failed to register PWM led for %s: %d\n",
diff --git a/drivers/leds/leds-upboard.c b/drivers/leds/leds-upboard.c
index b350eb294280..12989b2f1953 100644
--- a/drivers/leds/leds-upboard.c
+++ b/drivers/leds/leds-upboard.c
@@ -123,4 +123,4 @@ MODULE_AUTHOR("Gary Wang <garywang@aaeon.com.tw>");
MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
MODULE_DESCRIPTION("UP Board LED driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:upboard-led");
+MODULE_ALIAS("platform:upboard-leds");
diff --git a/drivers/leds/rgb/leds-ktd202x.c b/drivers/leds/rgb/leds-ktd202x.c
index 04e62faa3a00..e4f0f25a5e45 100644
--- a/drivers/leds/rgb/leds-ktd202x.c
+++ b/drivers/leds/rgb/leds-ktd202x.c
@@ -391,7 +391,7 @@ static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct fwnode_handle *fwn
int i = 0;
num_channels = 0;
- fwnode_for_each_available_child_node(fwnode, child)
+ fwnode_for_each_child_node(fwnode, child)
num_channels++;
if (!num_channels || num_channels > chip->num_leds)
@@ -401,7 +401,7 @@ static int ktd202x_setup_led_rgb(struct ktd202x *chip, struct fwnode_handle *fwn
if (!info)
return -ENOMEM;
- fwnode_for_each_available_child_node(fwnode, child) {
+ fwnode_for_each_child_node(fwnode, child) {
u32 mono_color;
u32 reg;
int ret;
diff --git a/drivers/leds/rgb/leds-ncp5623.c b/drivers/leds/rgb/leds-ncp5623.c
index 7c7d44623a9e..85d6be6fff2b 100644
--- a/drivers/leds/rgb/leds-ncp5623.c
+++ b/drivers/leds/rgb/leds-ncp5623.c
@@ -180,7 +180,7 @@ static int ncp5623_probe(struct i2c_client *client)
goto release_mc_node;
}
- fwnode_for_each_available_child_node(mc_node, led_node) {
+ fwnode_for_each_child_node(mc_node, led_node) {
ret = fwnode_property_read_u32(led_node, "color", &color_index);
if (ret)
goto release_led_node;
diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c
index 4f2a178e3d26..72da0bf469ad 100644
--- a/drivers/leds/rgb/leds-qcom-lpg.c
+++ b/drivers/leds/rgb/leds-qcom-lpg.c
@@ -2,7 +2,7 @@
/*
* Copyright (c) 2017-2022 Linaro Ltd
* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
- * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
@@ -1247,8 +1247,6 @@ static int lpg_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
lpg_apply(chan);
- triled_set(lpg, chan->triled_mask, chan->enabled ? chan->triled_mask : 0);
-
out_unlock:
mutex_unlock(&lpg->lock);
@@ -1382,7 +1380,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
return dev_err_probe(lpg->dev, ret,
"failed to parse \"color\" of %pOF\n", np);
- if (color == LED_COLOR_ID_RGB)
+ if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI)
num_channels = of_get_available_child_count(np);
else
num_channels = 1;
@@ -1394,7 +1392,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
led->lpg = lpg;
led->num_channels = num_channels;
- if (color == LED_COLOR_ID_RGB) {
+ if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI) {
info = devm_kcalloc(lpg->dev, num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
@@ -1454,7 +1452,7 @@ static int lpg_add_led(struct lpg *lpg, struct device_node *np)
init_data.fwnode = of_fwnode_handle(np);
- if (color == LED_COLOR_ID_RGB)
+ if (color == LED_COLOR_ID_RGB || color == LED_COLOR_ID_MULTI)
ret = devm_led_classdev_multicolor_register_ext(lpg->dev, &led->mcdev, &init_data);
else
ret = devm_led_classdev_register_ext(lpg->dev, &led->cdev, &init_data);
diff --git a/drivers/leds/trigger/ledtrig-input-events.c b/drivers/leds/trigger/ledtrig-input-events.c
index 1c79731562c2..3c6414259c27 100644
--- a/drivers/leds/trigger/ledtrig-input-events.c
+++ b/drivers/leds/trigger/ledtrig-input-events.c
@@ -66,7 +66,7 @@ static void input_events_event(struct input_handle *handle, unsigned int type,
spin_unlock_irqrestore(&data->lock, flags);
- mod_delayed_work(system_wq, &data->work, led_off_delay);
+ mod_delayed_work(system_percpu_wq, &data->work, led_off_delay);
}
static int input_events_connect(struct input_handler *handler, struct input_dev *dev,
diff --git a/drivers/mailbox/mailbox-test.c b/drivers/mailbox/mailbox-test.c
index c9dd8c42c0cd..3a28ab5c42e5 100644
--- a/drivers/mailbox/mailbox-test.c
+++ b/drivers/mailbox/mailbox-test.c
@@ -268,7 +268,7 @@ static int mbox_test_add_debugfs(struct platform_device *pdev,
return 0;
tdev->root_debugfs_dir = debugfs_create_dir(dev_name(&pdev->dev), NULL);
- if (!tdev->root_debugfs_dir) {
+ if (IS_ERR(tdev->root_debugfs_dir)) {
dev_err(&pdev->dev, "Failed to create Mailbox debugfs\n");
return -EINVAL;
}
diff --git a/drivers/mailbox/mailbox-th1520.c b/drivers/mailbox/mailbox-th1520.c
index a6b2aa9ae952..626957c2e435 100644
--- a/drivers/mailbox/mailbox-th1520.c
+++ b/drivers/mailbox/mailbox-th1520.c
@@ -435,10 +435,8 @@ static int th1520_mbox_probe(struct platform_device *pdev)
}
ret = devm_add_action_or_reset(dev, th1520_disable_clk, priv);
- if (ret) {
- clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
+ if (ret)
return ret;
- }
/*
* The address mappings in the device tree align precisely with those
diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c
index 654a60f63756..5791f80f995a 100644
--- a/drivers/mailbox/mtk-cmdq-mailbox.c
+++ b/drivers/mailbox/mtk-cmdq-mailbox.c
@@ -92,6 +92,18 @@ struct gce_plat {
u32 gce_num;
};
+static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata)
+{
+ /* Convert DMA addr (PA or IOVA) to GCE readable addr */
+ return addr >> pdata->shift;
+}
+
+static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata)
+{
+ /* Revert GCE readable addr to DMA addr (PA or IOVA) */
+ return (dma_addr_t)addr << pdata->shift;
+}
+
u8 cmdq_get_shift_pa(struct mbox_chan *chan)
{
struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
@@ -188,13 +200,12 @@ static void cmdq_task_insert_into_thread(struct cmdq_task *task)
struct cmdq_task *prev_task = list_last_entry(
&thread->task_busy_list, typeof(*task), list_entry);
u64 *prev_task_base = prev_task->pkt->va_base;
+ u32 gce_addr = cmdq_convert_gce_addr(task->pa_base, task->cmdq->pdata);
/* let previous task jump to this task */
dma_sync_single_for_cpu(dev, prev_task->pa_base,
prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
- prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] =
- (u64)CMDQ_JUMP_BY_PA << 32 |
- (task->pa_base >> task->cmdq->pdata->shift);
+ prev_task_base[CMDQ_NUM_CMD(prev_task->pkt) - 1] = (u64)CMDQ_JUMP_BY_PA << 32 | gce_addr;
dma_sync_single_for_device(dev, prev_task->pa_base,
prev_task->pkt->cmd_buf_size, DMA_TO_DEVICE);
@@ -237,7 +248,8 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq,
struct cmdq_thread *thread)
{
struct cmdq_task *task, *tmp, *curr_task = NULL;
- u32 curr_pa, irq_flag, task_end_pa;
+ u32 irq_flag, gce_addr;
+ dma_addr_t curr_pa, task_end_pa;
bool err;
irq_flag = readl(thread->base + CMDQ_THR_IRQ_STATUS);
@@ -259,7 +271,8 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq,
else
return;
- curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) << cmdq->pdata->shift;
+ gce_addr = readl(thread->base + CMDQ_THR_CURR_ADDR);
+ curr_pa = cmdq_revert_gce_addr(gce_addr, cmdq->pdata);
list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
list_entry) {
@@ -378,7 +391,8 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
struct cmdq_thread *thread = (struct cmdq_thread *)chan->con_priv;
struct cmdq *cmdq = dev_get_drvdata(chan->mbox->dev);
struct cmdq_task *task;
- unsigned long curr_pa, end_pa;
+ u32 gce_addr;
+ dma_addr_t curr_pa, end_pa;
/* Client should not flush new tasks if suspended. */
WARN_ON(cmdq->suspended);
@@ -402,20 +416,20 @@ static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
*/
WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
- writel(task->pa_base >> cmdq->pdata->shift,
- thread->base + CMDQ_THR_CURR_ADDR);
- writel((task->pa_base + pkt->cmd_buf_size) >> cmdq->pdata->shift,
- thread->base + CMDQ_THR_END_ADDR);
+ gce_addr = cmdq_convert_gce_addr(task->pa_base, cmdq->pdata);
+ writel(gce_addr, thread->base + CMDQ_THR_CURR_ADDR);
+ gce_addr = cmdq_convert_gce_addr(task->pa_base + pkt->cmd_buf_size, cmdq->pdata);
+ writel(gce_addr, thread->base + CMDQ_THR_END_ADDR);
writel(thread->priority, thread->base + CMDQ_THR_PRIORITY);
writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE);
writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK);
} else {
WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
- curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR) <<
- cmdq->pdata->shift;
- end_pa = readl(thread->base + CMDQ_THR_END_ADDR) <<
- cmdq->pdata->shift;
+ gce_addr = readl(thread->base + CMDQ_THR_CURR_ADDR);
+ curr_pa = cmdq_revert_gce_addr(gce_addr, cmdq->pdata);
+ gce_addr = readl(thread->base + CMDQ_THR_END_ADDR);
+ end_pa = cmdq_revert_gce_addr(gce_addr, cmdq->pdata);
/* check boundary */
if (curr_pa == end_pa - CMDQ_INST_SIZE ||
curr_pa == end_pa) {
@@ -646,6 +660,9 @@ static int cmdq_probe(struct platform_device *pdev)
if (err)
return err;
+ dma_set_coherent_mask(dev,
+ DMA_BIT_MASK(sizeof(u32) * BITS_PER_BYTE + cmdq->pdata->shift));
+
cmdq->mbox.dev = dev;
cmdq->mbox.chans = devm_kcalloc(dev, cmdq->pdata->thread_nr,
sizeof(*cmdq->mbox.chans), GFP_KERNEL);
diff --git a/drivers/mailbox/mtk-gpueb-mailbox.c b/drivers/mailbox/mtk-gpueb-mailbox.c
index 925bcf21f650..f6d2beccd91b 100644
--- a/drivers/mailbox/mtk-gpueb-mailbox.c
+++ b/drivers/mailbox/mtk-gpueb-mailbox.c
@@ -200,7 +200,7 @@ static bool mtk_gpueb_mbox_last_tx_done(struct mbox_chan *chan)
return !(readl(ch->ebm->mbox_ctl + GPUEB_MBOX_CTL_TX_STS) & BIT(ch->num));
}
-const struct mbox_chan_ops mtk_gpueb_mbox_ops = {
+static const struct mbox_chan_ops mtk_gpueb_mbox_ops = {
.send_data = mtk_gpueb_mbox_send_data,
.startup = mtk_gpueb_mbox_startup,
.shutdown = mtk_gpueb_mbox_shutdown,
diff --git a/drivers/mailbox/omap-mailbox.c b/drivers/mailbox/omap-mailbox.c
index 680243751d62..17fe6545875d 100644
--- a/drivers/mailbox/omap-mailbox.c
+++ b/drivers/mailbox/omap-mailbox.c
@@ -68,6 +68,7 @@ struct omap_mbox_fifo {
struct omap_mbox_match_data {
u32 intr_type;
+ bool is_exclusive;
};
struct omap_mbox_device {
@@ -78,6 +79,7 @@ struct omap_mbox_device {
u32 num_users;
u32 num_fifos;
u32 intr_type;
+ const struct omap_mbox_match_data *mbox_data;
};
struct omap_mbox {
@@ -341,11 +343,13 @@ static int omap_mbox_suspend(struct device *dev)
if (pm_runtime_status_suspended(dev))
return 0;
- for (fifo = 0; fifo < mdev->num_fifos; fifo++) {
- if (mbox_read_reg(mdev, MAILBOX_MSGSTATUS(fifo))) {
- dev_err(mdev->dev, "fifo %d has unexpected unread messages\n",
- fifo);
- return -EBUSY;
+ if (mdev->mbox_data->is_exclusive) {
+ for (fifo = 0; fifo < mdev->num_fifos; fifo++) {
+ if (mbox_read_reg(mdev, MAILBOX_MSGSTATUS(fifo))) {
+ dev_err(mdev->dev, "fifo %d has unexpected unread messages\n",
+ fifo);
+ return -EBUSY;
+ }
}
}
@@ -378,8 +382,9 @@ static const struct dev_pm_ops omap_mbox_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(omap_mbox_suspend, omap_mbox_resume)
};
-static const struct omap_mbox_match_data omap2_data = { MBOX_INTR_CFG_TYPE1 };
-static const struct omap_mbox_match_data omap4_data = { MBOX_INTR_CFG_TYPE2 };
+static const struct omap_mbox_match_data omap2_data = { MBOX_INTR_CFG_TYPE1, true };
+static const struct omap_mbox_match_data omap4_data = { MBOX_INTR_CFG_TYPE2, true };
+static const struct omap_mbox_match_data am654_data = { MBOX_INTR_CFG_TYPE2, false };
static const struct of_device_id omap_mailbox_of_match[] = {
{
@@ -396,11 +401,11 @@ static const struct of_device_id omap_mailbox_of_match[] = {
},
{
.compatible = "ti,am654-mailbox",
- .data = &omap4_data,
+ .data = &am654_data,
},
{
.compatible = "ti,am64-mailbox",
- .data = &omap4_data,
+ .data = &am654_data,
},
{
/* end */
@@ -449,7 +454,6 @@ static int omap_mbox_probe(struct platform_device *pdev)
struct omap_mbox_fifo *fifo;
struct device_node *node = pdev->dev.of_node;
struct device_node *child;
- const struct omap_mbox_match_data *match_data;
struct mbox_controller *controller;
u32 intr_type, info_count;
u32 num_users, num_fifos;
@@ -462,11 +466,6 @@ static int omap_mbox_probe(struct platform_device *pdev)
return -ENODEV;
}
- match_data = of_device_get_match_data(&pdev->dev);
- if (!match_data)
- return -ENODEV;
- intr_type = match_data->intr_type;
-
if (of_property_read_u32(node, "ti,mbox-num-users", &num_users))
return -ENODEV;
@@ -483,6 +482,12 @@ static int omap_mbox_probe(struct platform_device *pdev)
if (!mdev)
return -ENOMEM;
+ mdev->mbox_data = device_get_match_data(&pdev->dev);
+ if (!mdev->mbox_data)
+ return -ENODEV;
+
+ intr_type = mdev->mbox_data->intr_type;
+
mdev->mbox_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(mdev->mbox_base))
return PTR_ERR(mdev->mbox_base);
diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c
index 0a00719b2482..ff292b9e0be9 100644
--- a/drivers/mailbox/pcc.c
+++ b/drivers/mailbox/pcc.c
@@ -276,9 +276,8 @@ static int pcc_mbox_error_check_and_clear(struct pcc_chan_info *pchan)
if (ret)
return ret;
- val &= pchan->error.status_mask;
- if (val) {
- val &= ~pchan->error.status_mask;
+ if (val & pchan->error.status_mask) {
+ val &= pchan->error.preserve_mask;
pcc_chan_reg_write(&pchan->error, val);
return -EIO;
}
@@ -745,7 +744,8 @@ static int pcc_parse_subspace_db_reg(struct pcc_chan_info *pchan,
ret = pcc_chan_reg_init(&pchan->error,
&pcct_ext->error_status_register,
- 0, 0, pcct_ext->error_status_mask,
+ ~pcct_ext->error_status_mask, 0,
+ pcct_ext->error_status_mask,
"Error Status");
}
return ret;
diff --git a/drivers/md/bcache/alloc.c b/drivers/md/bcache/alloc.c
index 48ce750bf70a..7708d92df23e 100644
--- a/drivers/md/bcache/alloc.c
+++ b/drivers/md/bcache/alloc.c
@@ -24,21 +24,18 @@
* Since the gens and priorities are all stored contiguously on disk, we can
* batch this up: We fill up the free_inc list with freshly invalidated buckets,
* call prio_write(), and when prio_write() finishes we pull buckets off the
- * free_inc list and optionally discard them.
+ * free_inc list.
*
* free_inc isn't the only freelist - if it was, we'd often to sleep while
* priorities and gens were being written before we could allocate. c->free is a
* smaller freelist, and buckets on that list are always ready to be used.
*
- * If we've got discards enabled, that happens when a bucket moves from the
- * free_inc list to the free list.
- *
* There is another freelist, because sometimes we have buckets that we know
* have nothing pointing into them - these we can reuse without waiting for
* priorities to be rewritten. These come from freed btree nodes and buckets
* that garbage collection discovered no longer had valid keys pointing into
* them (because they were overwritten). That's the unused list - buckets on the
- * unused list move to the free list, optionally being discarded in the process.
+ * unused list move to the free list.
*
* It's also important to ensure that gens don't wrap around - with respect to
* either the oldest gen in the btree or the gen on disk. This is quite
@@ -118,8 +115,7 @@ void bch_rescale_priorities(struct cache_set *c, int sectors)
/*
* Background allocation thread: scans for buckets to be invalidated,
* invalidates them, rewrites prios/gens (marking them as invalidated on disk),
- * then optionally issues discard commands to the newly free buckets, then puts
- * them on the various freelists.
+ * then puts them on the various freelists.
*/
static inline bool can_inc_bucket_gen(struct bucket *b)
@@ -321,8 +317,7 @@ static int bch_allocator_thread(void *arg)
while (1) {
/*
* First, we pull buckets off of the unused and free_inc lists,
- * possibly issue discards to them, then we add the bucket to
- * the free list:
+ * then we add the bucket to the free list:
*/
while (1) {
long bucket;
@@ -330,14 +325,6 @@ static int bch_allocator_thread(void *arg)
if (!fifo_pop(&ca->free_inc, bucket))
break;
- if (ca->discard) {
- mutex_unlock(&ca->set->bucket_lock);
- blkdev_issue_discard(ca->bdev,
- bucket_to_sector(ca->set, bucket),
- ca->sb.bucket_size, GFP_KERNEL);
- mutex_lock(&ca->set->bucket_lock);
- }
-
allocator_wait(ca, bch_allocator_push(ca, bucket));
wake_up(&ca->set->btree_cache_wait);
wake_up(&ca->set->bucket_wait);
@@ -412,7 +399,11 @@ long bch_bucket_alloc(struct cache *ca, unsigned int reserve, bool wait)
TASK_UNINTERRUPTIBLE);
mutex_unlock(&ca->set->bucket_lock);
+
+ atomic_inc(&ca->set->bucket_wait_cnt);
schedule();
+ atomic_dec(&ca->set->bucket_wait_cnt);
+
mutex_lock(&ca->set->bucket_lock);
} while (!fifo_pop(&ca->free[RESERVE_NONE], r) &&
!fifo_pop(&ca->free[reserve], r));
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 1d33e40d26ea..8ccacba85547 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -447,8 +447,7 @@ struct cache {
* free_inc: Incoming buckets - these are buckets that currently have
* cached data in them, and we can't reuse them until after we write
* their new gen to disk. After prio_write() finishes writing the new
- * gens/prios, they'll be moved to the free list (and possibly discarded
- * in the process)
+ * gens/prios, they'll be moved to the free list.
*/
DECLARE_FIFO(long, free)[RESERVE_NR];
DECLARE_FIFO(long, free_inc);
@@ -467,8 +466,6 @@ struct cache {
*/
unsigned int invalidate_needs_gc;
- bool discard; /* Get rid of? */
-
struct journal_device journal;
/* The rest of this all shows up in sysfs */
@@ -607,6 +604,7 @@ struct cache_set {
*/
atomic_t prio_blocked;
wait_queue_head_t bucket_wait;
+ atomic_t bucket_wait_cnt;
/*
* For any bio we don't skip we subtract the number of sectors from
diff --git a/drivers/md/bcache/bset.h b/drivers/md/bcache/bset.h
index 011f6062c4c0..6ee2c6a506a2 100644
--- a/drivers/md/bcache/bset.h
+++ b/drivers/md/bcache/bset.h
@@ -327,9 +327,13 @@ struct btree_iter {
/* Fixed-size btree_iter that can be allocated on the stack */
struct btree_iter_stack {
- struct btree_iter iter;
- struct btree_iter_set stack_data[MAX_BSETS];
+ /* Must be last as it ends in a flexible-array member. */
+ TRAILING_OVERLAP(struct btree_iter, iter, data,
+ struct btree_iter_set stack_data[MAX_BSETS];
+ );
};
+static_assert(offsetof(struct btree_iter_stack, iter.data) ==
+ offsetof(struct btree_iter_stack, stack_data));
typedef bool (*ptr_filter_fn)(struct btree_keys *b, const struct bkey *k);
diff --git a/drivers/md/bcache/btree.c b/drivers/md/bcache/btree.c
index 210b59007d98..3ed39c823826 100644
--- a/drivers/md/bcache/btree.c
+++ b/drivers/md/bcache/btree.c
@@ -89,8 +89,9 @@
* Test module load/unload
*/
-#define MAX_GC_TIMES 100
-#define MIN_GC_NODES 100
+#define MAX_GC_TIMES_SHIFT 7 /* 128 loops */
+#define GC_NODES_MIN 10
+#define GC_SLEEP_MS_MIN 10
#define GC_SLEEP_MS 100
#define PTR_DIRTY_BIT (((uint64_t) 1 << 36))
@@ -371,7 +372,7 @@ static void do_btree_node_write(struct btree *b)
SET_PTR_OFFSET(&k.key, 0, PTR_OFFSET(&k.key, 0) +
bset_sector_offset(&b->keys, i));
- if (!bch_bio_alloc_pages(b->bio, __GFP_NOWARN|GFP_NOWAIT)) {
+ if (!bch_bio_alloc_pages(b->bio, GFP_NOWAIT)) {
struct bio_vec *bv;
void *addr = (void *) ((unsigned long) i & ~(PAGE_SIZE - 1));
struct bvec_iter_all iter_all;
@@ -1578,29 +1579,29 @@ static unsigned int btree_gc_count_keys(struct btree *b)
static size_t btree_gc_min_nodes(struct cache_set *c)
{
- size_t min_nodes;
+ size_t min_nodes = GC_NODES_MIN;
- /*
- * Since incremental GC would stop 100ms when front
- * side I/O comes, so when there are many btree nodes,
- * if GC only processes constant (100) nodes each time,
- * GC would last a long time, and the front side I/Os
- * would run out of the buckets (since no new bucket
- * can be allocated during GC), and be blocked again.
- * So GC should not process constant nodes, but varied
- * nodes according to the number of btree nodes, which
- * realized by dividing GC into constant(100) times,
- * so when there are many btree nodes, GC can process
- * more nodes each time, otherwise, GC will process less
- * nodes each time (but no less than MIN_GC_NODES)
- */
- min_nodes = c->gc_stats.nodes / MAX_GC_TIMES;
- if (min_nodes < MIN_GC_NODES)
- min_nodes = MIN_GC_NODES;
+ if (atomic_read(&c->search_inflight) == 0) {
+ size_t n = c->gc_stats.nodes >> MAX_GC_TIMES_SHIFT;
+
+ if (min_nodes < n)
+ min_nodes = n;
+ }
return min_nodes;
}
+static uint64_t btree_gc_sleep_ms(struct cache_set *c)
+{
+ uint64_t sleep_ms;
+
+ if (atomic_read(&c->bucket_wait_cnt) > 0)
+ sleep_ms = GC_SLEEP_MS_MIN;
+ else
+ sleep_ms = GC_SLEEP_MS;
+
+ return sleep_ms;
+}
static int btree_gc_recurse(struct btree *b, struct btree_op *op,
struct closure *writes, struct gc_stat *gc)
@@ -1668,8 +1669,7 @@ static int btree_gc_recurse(struct btree *b, struct btree_op *op,
memmove(r + 1, r, sizeof(r[0]) * (GC_MERGE_NODES - 1));
r->b = NULL;
- if (atomic_read(&b->c->search_inflight) &&
- gc->nodes >= gc->nodes_pre + btree_gc_min_nodes(b->c)) {
+ if (gc->nodes >= (gc->nodes_pre + btree_gc_min_nodes(b->c))) {
gc->nodes_pre = gc->nodes;
ret = -EAGAIN;
break;
@@ -1846,8 +1846,8 @@ static void bch_btree_gc(struct cache_set *c)
cond_resched();
if (ret == -EAGAIN)
- schedule_timeout_interruptible(msecs_to_jiffies
- (GC_SLEEP_MS));
+ schedule_timeout_interruptible(
+ msecs_to_jiffies(btree_gc_sleep_ms(c)));
else if (ret)
pr_warn("gc failed!\n");
} while (ret && !test_bit(CACHE_SET_IO_DISABLE, &c->flags));
@@ -2822,7 +2822,8 @@ void bch_btree_exit(void)
int __init bch_btree_init(void)
{
- btree_io_wq = alloc_workqueue("bch_btree_io", WQ_MEM_RECLAIM, 0);
+ btree_io_wq = alloc_workqueue("bch_btree_io",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!btree_io_wq)
return -ENOMEM;
diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
index d50eb82ccb4f..144693b7c46a 100644
--- a/drivers/md/bcache/journal.c
+++ b/drivers/md/bcache/journal.c
@@ -275,8 +275,7 @@ bsearch:
* ja->cur_idx
*/
ja->cur_idx = i;
- ja->last_idx = ja->discard_idx = (i + 1) %
- ca->sb.njournal_buckets;
+ ja->last_idx = (i + 1) % ca->sb.njournal_buckets;
}
@@ -336,16 +335,6 @@ void bch_journal_mark(struct cache_set *c, struct list_head *list)
}
}
-static bool is_discard_enabled(struct cache_set *s)
-{
- struct cache *ca = s->cache;
-
- if (ca->discard)
- return true;
-
- return false;
-}
-
int bch_journal_replay(struct cache_set *s, struct list_head *list)
{
int ret = 0, keys = 0, entries = 0;
@@ -360,15 +349,10 @@ int bch_journal_replay(struct cache_set *s, struct list_head *list)
BUG_ON(i->pin && atomic_read(i->pin) != 1);
if (n != i->j.seq) {
- if (n == start && is_discard_enabled(s))
- pr_info("journal entries %llu-%llu may be discarded! (replaying %llu-%llu)\n",
- n, i->j.seq - 1, start, end);
- else {
- pr_err("journal entries %llu-%llu missing! (replaying %llu-%llu)\n",
- n, i->j.seq - 1, start, end);
- ret = -EIO;
- goto err;
- }
+ pr_err("journal entries %llu-%llu missing! (replaying %llu-%llu)\n",
+ n, i->j.seq - 1, start, end);
+ ret = -EIO;
+ goto err;
}
for (k = i->j.start;
@@ -568,65 +552,6 @@ out:
#define last_seq(j) ((j)->seq - fifo_used(&(j)->pin) + 1)
-static void journal_discard_endio(struct bio *bio)
-{
- struct journal_device *ja =
- container_of(bio, struct journal_device, discard_bio);
- struct cache *ca = container_of(ja, struct cache, journal);
-
- atomic_set(&ja->discard_in_flight, DISCARD_DONE);
-
- closure_wake_up(&ca->set->journal.wait);
- closure_put(&ca->set->cl);
-}
-
-static void journal_discard_work(struct work_struct *work)
-{
- struct journal_device *ja =
- container_of(work, struct journal_device, discard_work);
-
- submit_bio(&ja->discard_bio);
-}
-
-static void do_journal_discard(struct cache *ca)
-{
- struct journal_device *ja = &ca->journal;
- struct bio *bio = &ja->discard_bio;
-
- if (!ca->discard) {
- ja->discard_idx = ja->last_idx;
- return;
- }
-
- switch (atomic_read(&ja->discard_in_flight)) {
- case DISCARD_IN_FLIGHT:
- return;
-
- case DISCARD_DONE:
- ja->discard_idx = (ja->discard_idx + 1) %
- ca->sb.njournal_buckets;
-
- atomic_set(&ja->discard_in_flight, DISCARD_READY);
- fallthrough;
-
- case DISCARD_READY:
- if (ja->discard_idx == ja->last_idx)
- return;
-
- atomic_set(&ja->discard_in_flight, DISCARD_IN_FLIGHT);
-
- bio_init_inline(bio, ca->bdev, 1, REQ_OP_DISCARD);
- bio->bi_iter.bi_sector = bucket_to_sector(ca->set,
- ca->sb.d[ja->discard_idx]);
- bio->bi_iter.bi_size = bucket_bytes(ca);
- bio->bi_end_io = journal_discard_endio;
-
- closure_get(&ca->set->cl);
- INIT_WORK(&ja->discard_work, journal_discard_work);
- queue_work(bch_journal_wq, &ja->discard_work);
- }
-}
-
static unsigned int free_journal_buckets(struct cache_set *c)
{
struct journal *j = &c->journal;
@@ -635,10 +560,10 @@ static unsigned int free_journal_buckets(struct cache_set *c)
unsigned int n;
/* In case njournal_buckets is not power of 2 */
- if (ja->cur_idx >= ja->discard_idx)
- n = ca->sb.njournal_buckets + ja->discard_idx - ja->cur_idx;
+ if (ja->cur_idx >= ja->last_idx)
+ n = ca->sb.njournal_buckets + ja->last_idx - ja->cur_idx;
else
- n = ja->discard_idx - ja->cur_idx;
+ n = ja->last_idx - ja->cur_idx;
if (n > (1 + j->do_reserve))
return n - (1 + j->do_reserve);
@@ -668,8 +593,6 @@ static void journal_reclaim(struct cache_set *c)
ja->last_idx = (ja->last_idx + 1) %
ca->sb.njournal_buckets;
- do_journal_discard(ca);
-
if (c->journal.blocks_free)
goto out;
diff --git a/drivers/md/bcache/journal.h b/drivers/md/bcache/journal.h
index cd316b4a1e95..9e9d1b3016a5 100644
--- a/drivers/md/bcache/journal.h
+++ b/drivers/md/bcache/journal.h
@@ -139,19 +139,6 @@ struct journal_device {
/* Last journal bucket that still contains an open journal entry */
unsigned int last_idx;
- /* Next journal bucket to be discarded */
- unsigned int discard_idx;
-
-#define DISCARD_READY 0
-#define DISCARD_IN_FLIGHT 1
-#define DISCARD_DONE 2
- /* 1 - discard in flight, -1 - discard completed */
- atomic_t discard_in_flight;
-
- struct work_struct discard_work;
- struct bio discard_bio;
- struct bio_vec discard_bv;
-
/* Bio for journal reads/writes to this device */
struct bio bio;
struct bio_vec bv[8];
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 6d250e366412..c17d4517af22 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1388,7 +1388,7 @@ static CLOSURE_CALLBACK(cached_dev_flush)
bch_cache_accounting_destroy(&dc->accounting);
kobject_del(&d->kobj);
- continue_at(cl, cached_dev_free, system_wq);
+ continue_at(cl, cached_dev_free, system_percpu_wq);
}
static int cached_dev_init(struct cached_dev *dc, unsigned int block_size)
@@ -1400,7 +1400,7 @@ static int cached_dev_init(struct cached_dev *dc, unsigned int block_size)
__module_get(THIS_MODULE);
INIT_LIST_HEAD(&dc->list);
closure_init(&dc->disk.cl, NULL);
- set_closure_fn(&dc->disk.cl, cached_dev_flush, system_wq);
+ set_closure_fn(&dc->disk.cl, cached_dev_flush, system_percpu_wq);
kobject_init(&dc->disk.kobj, &bch_cached_dev_ktype);
INIT_WORK(&dc->detach, cached_dev_detach_finish);
sema_init(&dc->sb_write_mutex, 1);
@@ -1513,7 +1513,7 @@ static CLOSURE_CALLBACK(flash_dev_flush)
bcache_device_unlink(d);
mutex_unlock(&bch_register_lock);
kobject_del(&d->kobj);
- continue_at(cl, flash_dev_free, system_wq);
+ continue_at(cl, flash_dev_free, system_percpu_wq);
}
static int flash_dev_run(struct cache_set *c, struct uuid_entry *u)
@@ -1525,7 +1525,7 @@ static int flash_dev_run(struct cache_set *c, struct uuid_entry *u)
goto err_ret;
closure_init(&d->cl, NULL);
- set_closure_fn(&d->cl, flash_dev_flush, system_wq);
+ set_closure_fn(&d->cl, flash_dev_flush, system_percpu_wq);
kobject_init(&d->kobj, &bch_flash_dev_ktype);
@@ -1833,7 +1833,7 @@ static CLOSURE_CALLBACK(__cache_set_unregister)
mutex_unlock(&bch_register_lock);
- continue_at(cl, cache_set_flush, system_wq);
+ continue_at(cl, cache_set_flush, system_percpu_wq);
}
void bch_cache_set_stop(struct cache_set *c)
@@ -1863,10 +1863,10 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
__module_get(THIS_MODULE);
closure_init(&c->cl, NULL);
- set_closure_fn(&c->cl, cache_set_free, system_wq);
+ set_closure_fn(&c->cl, cache_set_free, system_percpu_wq);
closure_init(&c->caching, &c->cl);
- set_closure_fn(&c->caching, __cache_set_unregister, system_wq);
+ set_closure_fn(&c->caching, __cache_set_unregister, system_percpu_wq);
/* Maybe create continue_at_noreturn() and use it here? */
closure_set_stopped(&c->cl);
@@ -1939,7 +1939,8 @@ struct cache_set *bch_cache_set_alloc(struct cache_sb *sb)
if (!c->uuids)
goto err;
- c->moving_gc_wq = alloc_workqueue("bcache_gc", WQ_MEM_RECLAIM, 0);
+ c->moving_gc_wq = alloc_workqueue("bcache_gc",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!c->moving_gc_wq)
goto err;
@@ -2382,9 +2383,6 @@ static int register_cache(struct cache_sb *sb, struct cache_sb_disk *sb_disk,
ca->bdev = file_bdev(bdev_file);
ca->sb_disk = sb_disk;
- if (bdev_max_discard_sectors(file_bdev(bdev_file)))
- ca->discard = CACHE_DISCARD(&ca->sb);
-
ret = cache_alloc(ca);
if (ret != 0) {
if (ret == -ENOMEM)
@@ -2531,7 +2529,7 @@ static void register_device_async(struct async_reg_args *args)
INIT_DELAYED_WORK(&args->reg_work, register_cache_worker);
/* 10 jiffies is enough for a delay */
- queue_delayed_work(system_wq, &args->reg_work, 10);
+ queue_delayed_work(system_percpu_wq, &args->reg_work, 10);
}
static void *alloc_holder_object(struct cache_sb *sb)
@@ -2905,24 +2903,25 @@ static int __init bcache_init(void)
if (bch_btree_init())
goto err;
- bcache_wq = alloc_workqueue("bcache", WQ_MEM_RECLAIM, 0);
+ bcache_wq = alloc_workqueue("bcache", WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!bcache_wq)
goto err;
/*
* Let's not make this `WQ_MEM_RECLAIM` for the following reasons:
*
- * 1. It used `system_wq` before which also does no memory reclaim.
+ * 1. It used `system_percpu_wq` before which also does no memory reclaim.
* 2. With `WQ_MEM_RECLAIM` desktop stalls, increased boot times, and
* reduced throughput can be observed.
*
- * We still want to user our own queue to not congest the `system_wq`.
+ * We still want to user our own queue to not congest the `system_percpu_wq`.
*/
- bch_flush_wq = alloc_workqueue("bch_flush", 0, 0);
+ bch_flush_wq = alloc_workqueue("bch_flush", WQ_PERCPU, 0);
if (!bch_flush_wq)
goto err;
- bch_journal_wq = alloc_workqueue("bch_journal", WQ_MEM_RECLAIM, 0);
+ bch_journal_wq = alloc_workqueue("bch_journal",
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!bch_journal_wq)
goto err;
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index 826b14cae4e5..72f38e5b6f5c 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -134,7 +134,6 @@ read_attribute(partial_stripes_expensive);
rw_attribute(synchronous);
rw_attribute(journal_delay_ms);
rw_attribute(io_disable);
-rw_attribute(discard);
rw_attribute(running);
rw_attribute(label);
rw_attribute(errors);
@@ -1036,7 +1035,6 @@ SHOW(__bch_cache)
sysfs_hprint(bucket_size, bucket_bytes(ca));
sysfs_hprint(block_size, block_bytes(ca));
sysfs_print(nbuckets, ca->sb.nbuckets);
- sysfs_print(discard, ca->discard);
sysfs_hprint(written, atomic_long_read(&ca->sectors_written) << 9);
sysfs_hprint(btree_written,
atomic_long_read(&ca->btree_sectors_written) << 9);
@@ -1142,18 +1140,6 @@ STORE(__bch_cache)
if (bcache_is_reboot)
return -EBUSY;
- if (attr == &sysfs_discard) {
- bool v = strtoul_or_return(buf);
-
- if (bdev_max_discard_sectors(ca->bdev))
- ca->discard = v;
-
- if (v != CACHE_DISCARD(&ca->sb)) {
- SET_CACHE_DISCARD(&ca->sb, v);
- bcache_write_super(ca->set);
- }
- }
-
if (attr == &sysfs_cache_replacement_policy) {
v = __sysfs_match_string(cache_replacement_policies, -1, buf);
if (v < 0)
@@ -1185,7 +1171,6 @@ static struct attribute *bch_cache_attrs[] = {
&sysfs_block_size,
&sysfs_nbuckets,
&sysfs_priority_stats,
- &sysfs_discard,
&sysfs_written,
&sysfs_btree_written,
&sysfs_metadata_written,
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index 6ba73dc1a3df..4b237074f453 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -805,8 +805,7 @@ static int bch_writeback_thread(void *arg)
* may set BCH_ENABLE_AUTO_GC via sysfs, then when
* BCH_DO_AUTO_GC is set, garbage collection thread
* will be wake up here. After moving gc, the shrunk
- * btree and discarded free buckets SSD space may be
- * helpful for following write requests.
+ * btree may be helpful for following write requests.
*/
if (c->gc_after_writeback ==
(BCH_ENABLE_AUTO_GC|BCH_DO_AUTO_GC)) {
@@ -1076,7 +1075,7 @@ void bch_cached_dev_writeback_init(struct cached_dev *dc)
int bch_cached_dev_writeback_start(struct cached_dev *dc)
{
dc->writeback_write_wq = alloc_workqueue("bcache_writeback_wq",
- WQ_MEM_RECLAIM, 0);
+ WQ_MEM_RECLAIM | WQ_PERCPU, 0);
if (!dc->writeback_write_wq)
return -ENOMEM;
diff --git a/drivers/md/dm-pcache/Makefile b/drivers/md/dm-pcache/Makefile
index 86776e4acad2..cedfd38854f6 100644
--- a/drivers/md/dm-pcache/Makefile
+++ b/drivers/md/dm-pcache/Makefile
@@ -1,3 +1,3 @@
dm-pcache-y := dm_pcache.o cache_dev.o segment.o backing_dev.o cache.o cache_gc.o cache_writeback.o cache_segment.o cache_key.o cache_req.o
-obj-m += dm-pcache.o
+obj-$(CONFIG_DM_PCACHE) += dm-pcache.o
diff --git a/drivers/md/dm-pcache/cache.c b/drivers/md/dm-pcache/cache.c
index d8e92367d947..698697a7a73c 100644
--- a/drivers/md/dm-pcache/cache.c
+++ b/drivers/md/dm-pcache/cache.c
@@ -181,7 +181,7 @@ static void cache_info_init_default(struct pcache_cache *cache)
{
struct pcache_cache_info *cache_info = &cache->cache_info;
- cache_info->header.seq = 0;
+ memset(cache_info, 0, sizeof(*cache_info));
cache_info->n_segs = cache->cache_dev->seg_num;
cache_info_set_gc_percent(cache_info, PCACHE_CACHE_GC_PERCENT_DEFAULT);
}
@@ -411,7 +411,7 @@ void pcache_cache_stop(struct dm_pcache *pcache)
{
struct pcache_cache *cache = &pcache->cache;
- cache_flush(cache);
+ pcache_cache_flush(cache);
cancel_delayed_work_sync(&cache->gc_work);
flush_work(&cache->clean_work);
diff --git a/drivers/md/dm-pcache/cache.h b/drivers/md/dm-pcache/cache.h
index 1136d86958c8..27613b56be54 100644
--- a/drivers/md/dm-pcache/cache.h
+++ b/drivers/md/dm-pcache/cache.h
@@ -339,7 +339,7 @@ void cache_seg_put(struct pcache_cache_segment *cache_seg);
void cache_seg_set_next_seg(struct pcache_cache_segment *cache_seg, u32 seg_id);
/* cache request*/
-int cache_flush(struct pcache_cache *cache);
+int pcache_cache_flush(struct pcache_cache *cache);
void miss_read_end_work_fn(struct work_struct *work);
int pcache_cache_handle_req(struct pcache_cache *cache, struct pcache_request *pcache_req);
diff --git a/drivers/md/dm-pcache/cache_req.c b/drivers/md/dm-pcache/cache_req.c
index 27f94c1fa968..7854a30e07b7 100644
--- a/drivers/md/dm-pcache/cache_req.c
+++ b/drivers/md/dm-pcache/cache_req.c
@@ -790,7 +790,7 @@ err:
}
/**
- * cache_flush - Flush all ksets to persist any pending cache data
+ * pcache_cache_flush - Flush all ksets to persist any pending cache data
* @cache: Pointer to the cache structure
*
* This function iterates through all ksets associated with the provided `cache`
@@ -802,7 +802,7 @@ err:
* the respective error code, preventing the flush operation from proceeding to
* subsequent ksets.
*/
-int cache_flush(struct pcache_cache *cache)
+int pcache_cache_flush(struct pcache_cache *cache)
{
struct pcache_cache_kset *kset;
int ret;
@@ -827,7 +827,7 @@ int pcache_cache_handle_req(struct pcache_cache *cache, struct pcache_request *p
struct bio *bio = pcache_req->bio;
if (unlikely(bio->bi_opf & REQ_PREFLUSH))
- return cache_flush(cache);
+ return pcache_cache_flush(cache);
if (bio_data_dir(bio) == READ)
return cache_read(cache, pcache_req);
diff --git a/drivers/md/dm-pcache/pcache_internal.h b/drivers/md/dm-pcache/pcache_internal.h
index d427e534727c..b7a3319d2bd3 100644
--- a/drivers/md/dm-pcache/pcache_internal.h
+++ b/drivers/md/dm-pcache/pcache_internal.h
@@ -99,7 +99,7 @@ static inline void __must_check *pcache_meta_find_latest(struct pcache_meta_head
/* Update latest if a more recent sequence is found */
if (!latest || pcache_meta_seq_after(meta->seq, seq_latest)) {
seq_latest = meta->seq;
- latest = (void *)header + (i * meta_max_size);
+ latest = meta_addr;
}
}
diff --git a/drivers/md/dm-vdo/logger.c b/drivers/md/dm-vdo/logger.c
index 3f7dc2cb6b98..76a987ccf926 100644
--- a/drivers/md/dm-vdo/logger.c
+++ b/drivers/md/dm-vdo/logger.c
@@ -34,7 +34,7 @@ static const char *get_current_interrupt_type(void)
if (in_nmi())
return "NMI";
- if (in_irq())
+ if (in_hardirq())
return "HI";
if (in_softirq())
diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c
index d382a390d39a..72047b47a7a0 100644
--- a/drivers/md/dm-verity-fec.c
+++ b/drivers/md/dm-verity-fec.c
@@ -320,11 +320,7 @@ static int fec_alloc_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio)
if (fio->bufs[n])
continue;
- fio->bufs[n] = mempool_alloc(&v->fec->prealloc_pool, GFP_NOWAIT);
- if (unlikely(!fio->bufs[n])) {
- DMERR("failed to allocate FEC buffer");
- return -ENOMEM;
- }
+ fio->bufs[n] = mempool_alloc(&v->fec->prealloc_pool, GFP_NOIO);
}
/* try to allocate the maximum number of buffers */
diff --git a/drivers/md/dm-zone.c b/drivers/md/dm-zone.c
index 78e17dd4d01b..5a840c4ae316 100644
--- a/drivers/md/dm-zone.c
+++ b/drivers/md/dm-zone.c
@@ -17,33 +17,26 @@
* For internal zone reports bypassing the top BIO submission path.
*/
static int dm_blk_do_report_zones(struct mapped_device *md, struct dm_table *t,
- sector_t sector, unsigned int nr_zones,
- report_zones_cb cb, void *data)
+ unsigned int nr_zones,
+ struct dm_report_zones_args *args)
{
- struct gendisk *disk = md->disk;
- int ret;
- struct dm_report_zones_args args = {
- .next_sector = sector,
- .orig_data = data,
- .orig_cb = cb,
- };
-
do {
struct dm_target *tgt;
+ int ret;
- tgt = dm_table_find_target(t, args.next_sector);
+ tgt = dm_table_find_target(t, args->next_sector);
if (WARN_ON_ONCE(!tgt->type->report_zones))
return -EIO;
- args.tgt = tgt;
- ret = tgt->type->report_zones(tgt, &args,
- nr_zones - args.zone_idx);
+ args->tgt = tgt;
+ ret = tgt->type->report_zones(tgt, args,
+ nr_zones - args->zone_idx);
if (ret < 0)
return ret;
- } while (args.zone_idx < nr_zones &&
- args.next_sector < get_capacity(disk));
+ } while (args->zone_idx < nr_zones &&
+ args->next_sector < get_capacity(md->disk));
- return args.zone_idx;
+ return args->zone_idx;
}
/*
@@ -52,7 +45,8 @@ static int dm_blk_do_report_zones(struct mapped_device *md, struct dm_table *t,
* generally implemented by targets using dm_report_zones().
*/
int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones,
+ struct blk_report_zones_args *args)
{
struct mapped_device *md = disk->private_data;
struct dm_table *map;
@@ -76,9 +70,14 @@ int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
map = zone_revalidate_map;
}
- if (map)
- ret = dm_blk_do_report_zones(md, map, sector, nr_zones, cb,
- data);
+ if (map) {
+ struct dm_report_zones_args dm_args = {
+ .disk = md->disk,
+ .next_sector = sector,
+ .rep_args = args,
+ };
+ ret = dm_blk_do_report_zones(md, map, nr_zones, &dm_args);
+ }
if (put_table)
dm_put_live_table(md, srcu_idx);
@@ -113,7 +112,18 @@ static int dm_report_zones_cb(struct blk_zone *zone, unsigned int idx,
}
args->next_sector = zone->start + zone->len;
- return args->orig_cb(zone, args->zone_idx++, args->orig_data);
+
+ /* If we have an internal callback, call it first. */
+ if (args->cb) {
+ int ret;
+
+ ret = args->cb(zone, args->zone_idx, args->data);
+ if (ret)
+ return ret;
+ }
+
+ return disk_report_zone(args->disk, zone, args->zone_idx++,
+ args->rep_args);
}
/*
@@ -492,10 +502,15 @@ int dm_zone_get_reset_bitmap(struct mapped_device *md, struct dm_table *t,
sector_t sector, unsigned int nr_zones,
unsigned long *need_reset)
{
+ struct dm_report_zones_args args = {
+ .disk = md->disk,
+ .next_sector = sector,
+ .cb = dm_zone_need_reset_cb,
+ .data = need_reset,
+ };
int ret;
- ret = dm_blk_do_report_zones(md, t, sector, nr_zones,
- dm_zone_need_reset_cb, need_reset);
+ ret = dm_blk_do_report_zones(md, t, nr_zones, &args);
if (ret != nr_zones) {
DMERR("Get %s zone reset bitmap failed\n",
md->disk->disk_name);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index f5e5e59b232b..6c83ab940af7 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2005,7 +2005,7 @@ static void dm_split_and_process_bio(struct mapped_device *md,
* linear target or multiple linear targets pointing to the same
* device), we can send the flush with data directly to it.
*/
- if (map->flush_bypasses_map) {
+ if (bio->bi_iter.bi_size && map->flush_bypasses_map) {
struct list_head *devices = dm_table_get_devices(map);
if (devices->next == devices->prev)
goto send_preflush_with_data;
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 245f52b59215..7a795979ec72 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -109,7 +109,8 @@ void dm_finalize_zone_settings(struct dm_table *t, struct queue_limits *lim);
void dm_zone_endio(struct dm_io *io, struct bio *clone);
#ifdef CONFIG_BLK_DEV_ZONED
int dm_blk_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data);
+ unsigned int nr_zones,
+ struct blk_report_zones_args *args);
bool dm_is_zone_write(struct mapped_device *md, struct bio *bio);
int dm_zone_get_reset_bitmap(struct mapped_device *md, struct dm_table *t,
sector_t sector, unsigned int nr_zones,
diff --git a/drivers/md/md-linear.c b/drivers/md/md-linear.c
index 7033d982d377..8d7b82c4a723 100644
--- a/drivers/md/md-linear.c
+++ b/drivers/md/md-linear.c
@@ -72,9 +72,11 @@ static int linear_set_limits(struct mddev *mddev)
md_init_stacking_limits(&lim);
lim.max_hw_sectors = mddev->chunk_sectors;
+ lim.logical_block_size = mddev->logical_block_size;
lim.max_write_zeroes_sectors = mddev->chunk_sectors;
lim.max_hw_wzeroes_unmap_sectors = mddev->chunk_sectors;
lim.io_min = mddev->chunk_sectors << 9;
+ lim.features |= BLK_FEAT_ATOMIC_WRITES;
err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY);
if (err)
return err;
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 1eb434306162..9c1ade19b774 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -378,7 +378,7 @@ static void llbitmap_infect_dirty_bits(struct llbitmap *llbitmap,
case BitClean:
pctl->state[pos] = BitDirty;
break;
- };
+ }
}
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 41c476b40c7a..e5922a682953 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -99,7 +99,7 @@ static int remove_and_add_spares(struct mddev *mddev,
struct md_rdev *this);
static void mddev_detach(struct mddev *mddev);
static void export_rdev(struct md_rdev *rdev, struct mddev *mddev);
-static void md_wakeup_thread_directly(struct md_thread __rcu *thread);
+static void md_wakeup_thread_directly(struct md_thread __rcu **thread);
/*
* Default number of read corrections we'll attempt on an rdev
@@ -339,6 +339,7 @@ static int start_readonly;
*/
static bool create_on_open = true;
static bool legacy_async_del_gendisk = true;
+static bool check_new_feature = true;
/*
* We have a system wide 'event count' that is incremented
@@ -730,6 +731,8 @@ static void mddev_clear_bitmap_ops(struct mddev *mddev)
int mddev_init(struct mddev *mddev)
{
+ int err = 0;
+
if (!IS_ENABLED(CONFIG_MD_BITMAP))
mddev->bitmap_id = ID_BITMAP_NONE;
else
@@ -741,10 +744,23 @@ int mddev_init(struct mddev *mddev)
if (percpu_ref_init(&mddev->writes_pending, no_op,
PERCPU_REF_ALLOW_REINIT, GFP_KERNEL)) {
- percpu_ref_exit(&mddev->active_io);
- return -ENOMEM;
+ err = -ENOMEM;
+ goto exit_acitve_io;
}
+ err = bioset_init(&mddev->bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS);
+ if (err)
+ goto exit_writes_pending;
+
+ err = bioset_init(&mddev->sync_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS);
+ if (err)
+ goto exit_bio_set;
+
+ err = bioset_init(&mddev->io_clone_set, BIO_POOL_SIZE,
+ offsetof(struct md_io_clone, bio_clone), 0);
+ if (err)
+ goto exit_sync_set;
+
/* We want to start with the refcount at zero */
percpu_ref_put(&mddev->writes_pending);
@@ -773,11 +789,24 @@ int mddev_init(struct mddev *mddev)
INIT_WORK(&mddev->del_work, mddev_delayed_delete);
return 0;
+
+exit_sync_set:
+ bioset_exit(&mddev->sync_set);
+exit_bio_set:
+ bioset_exit(&mddev->bio_set);
+exit_writes_pending:
+ percpu_ref_exit(&mddev->writes_pending);
+exit_acitve_io:
+ percpu_ref_exit(&mddev->active_io);
+ return err;
}
EXPORT_SYMBOL_GPL(mddev_init);
void mddev_destroy(struct mddev *mddev)
{
+ bioset_exit(&mddev->bio_set);
+ bioset_exit(&mddev->sync_set);
+ bioset_exit(&mddev->io_clone_set);
percpu_ref_exit(&mddev->active_io);
percpu_ref_exit(&mddev->writes_pending);
}
@@ -941,8 +970,11 @@ void mddev_unlock(struct mddev *mddev)
* do_md_stop. dm raid only uses md_stop to stop. So dm raid
* doesn't need to check MD_DELETED when getting reconfig lock
*/
- if (test_bit(MD_DELETED, &mddev->flags))
+ if (test_bit(MD_DELETED, &mddev->flags) &&
+ !test_and_set_bit(MD_DO_DELETE, &mddev->flags)) {
+ kobject_del(&mddev->kobj);
del_gendisk(mddev->gendisk);
+ }
}
}
EXPORT_SYMBOL_GPL(mddev_unlock);
@@ -1820,9 +1852,13 @@ static int super_1_load(struct md_rdev *rdev, struct md_rdev *refdev, int minor_
}
if (sb->pad0 ||
sb->pad3[0] ||
- memcmp(sb->pad3, sb->pad3+1, sizeof(sb->pad3) - sizeof(sb->pad3[1])))
- /* Some padding is non-zero, might be a new feature */
- return -EINVAL;
+ memcmp(sb->pad3, sb->pad3+1, sizeof(sb->pad3) - sizeof(sb->pad3[1]))) {
+ pr_warn("Some padding is non-zero on %pg, might be a new feature\n",
+ rdev->bdev);
+ if (check_new_feature)
+ return -EINVAL;
+ pr_warn("check_new_feature is disabled, data corruption possible\n");
+ }
rdev->preferred_minor = 0xffff;
rdev->data_offset = le64_to_cpu(sb->data_offset);
@@ -1963,6 +1999,7 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *freshest, struc
mddev->layout = le32_to_cpu(sb->layout);
mddev->raid_disks = le32_to_cpu(sb->raid_disks);
mddev->dev_sectors = le64_to_cpu(sb->size);
+ mddev->logical_block_size = le32_to_cpu(sb->logical_block_size);
mddev->events = ev1;
mddev->bitmap_info.offset = 0;
mddev->bitmap_info.space = 0;
@@ -2172,6 +2209,7 @@ static void super_1_sync(struct mddev *mddev, struct md_rdev *rdev)
sb->chunksize = cpu_to_le32(mddev->chunk_sectors);
sb->level = cpu_to_le32(mddev->level);
sb->layout = cpu_to_le32(mddev->layout);
+ sb->logical_block_size = cpu_to_le32(mddev->logical_block_size);
if (test_bit(FailFast, &rdev->flags))
sb->devflags |= FailFast1;
else
@@ -2750,6 +2788,7 @@ void md_update_sb(struct mddev *mddev, int force_change)
if (!md_is_rdwr(mddev)) {
if (force_change)
set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
+ pr_err("%s: can't update sb for read-only array %s\n", __func__, mdname(mddev));
return;
}
@@ -5134,7 +5173,7 @@ static void stop_sync_thread(struct mddev *mddev, bool locked)
* Thread might be blocked waiting for metadata update which will now
* never happen
*/
- md_wakeup_thread_directly(mddev->sync_thread);
+ md_wakeup_thread_directly(&mddev->sync_thread);
if (work_pending(&mddev->sync_work))
flush_work(&mddev->sync_work);
@@ -5900,6 +5939,68 @@ static struct md_sysfs_entry md_serialize_policy =
__ATTR(serialize_policy, S_IRUGO | S_IWUSR, serialize_policy_show,
serialize_policy_store);
+static int mddev_set_logical_block_size(struct mddev *mddev,
+ unsigned int lbs)
+{
+ int err = 0;
+ struct queue_limits lim;
+
+ if (queue_logical_block_size(mddev->gendisk->queue) >= lbs) {
+ pr_err("%s: Cannot set LBS smaller than mddev LBS %u\n",
+ mdname(mddev), lbs);
+ return -EINVAL;
+ }
+
+ lim = queue_limits_start_update(mddev->gendisk->queue);
+ lim.logical_block_size = lbs;
+ pr_info("%s: logical_block_size is changed, data may be lost\n",
+ mdname(mddev));
+ err = queue_limits_commit_update(mddev->gendisk->queue, &lim);
+ if (err)
+ return err;
+
+ mddev->logical_block_size = lbs;
+ /* New lbs will be written to superblock after array is running */
+ set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
+ return 0;
+}
+
+static ssize_t
+lbs_show(struct mddev *mddev, char *page)
+{
+ return sprintf(page, "%u\n", mddev->logical_block_size);
+}
+
+static ssize_t
+lbs_store(struct mddev *mddev, const char *buf, size_t len)
+{
+ unsigned int lbs;
+ int err = -EBUSY;
+
+ /* Only 1.x meta supports configurable LBS */
+ if (mddev->major_version == 0)
+ return -EINVAL;
+
+ if (mddev->pers)
+ return -EBUSY;
+
+ err = kstrtouint(buf, 10, &lbs);
+ if (err < 0)
+ return -EINVAL;
+
+ err = mddev_lock(mddev);
+ if (err)
+ goto unlock;
+
+ err = mddev_set_logical_block_size(mddev, lbs);
+
+unlock:
+ mddev_unlock(mddev);
+ return err ?: len;
+}
+
+static struct md_sysfs_entry md_logical_block_size =
+__ATTR(logical_block_size, 0644, lbs_show, lbs_store);
static struct attribute *md_default_attrs[] = {
&md_level.attr,
@@ -5922,6 +6023,7 @@ static struct attribute *md_default_attrs[] = {
&md_consistency_policy.attr,
&md_fail_last_dev.attr,
&md_serialize_policy.attr,
+ &md_logical_block_size.attr,
NULL,
};
@@ -6052,6 +6154,17 @@ int mddev_stack_rdev_limits(struct mddev *mddev, struct queue_limits *lim,
return -EINVAL;
}
+ /*
+ * Before RAID adding folio support, the logical_block_size
+ * should be smaller than the page size.
+ */
+ if (lim->logical_block_size > PAGE_SIZE) {
+ pr_err("%s: logical_block_size must not larger than PAGE_SIZE\n",
+ mdname(mddev));
+ return -EINVAL;
+ }
+ mddev->logical_block_size = lim->logical_block_size;
+
return 0;
}
EXPORT_SYMBOL_GPL(mddev_stack_rdev_limits);
@@ -6064,6 +6177,13 @@ int mddev_stack_new_rdev(struct mddev *mddev, struct md_rdev *rdev)
if (mddev_is_dm(mddev))
return 0;
+ if (queue_logical_block_size(rdev->bdev->bd_disk->queue) >
+ queue_logical_block_size(mddev->gendisk->queue)) {
+ pr_err("%s: incompatible logical_block_size, can not add\n",
+ mdname(mddev));
+ return -EINVAL;
+ }
+
lim = queue_limits_start_update(mddev->gendisk->queue);
queue_limits_stack_bdev(&lim, rdev->bdev, rdev->data_offset,
mddev->gendisk->disk_name);
@@ -6384,29 +6504,9 @@ int md_run(struct mddev *mddev)
nowait = nowait && bdev_nowait(rdev->bdev);
}
- if (!bioset_initialized(&mddev->bio_set)) {
- err = bioset_init(&mddev->bio_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS);
- if (err)
- return err;
- }
- if (!bioset_initialized(&mddev->sync_set)) {
- err = bioset_init(&mddev->sync_set, BIO_POOL_SIZE, 0, BIOSET_NEED_BVECS);
- if (err)
- goto exit_bio_set;
- }
-
- if (!bioset_initialized(&mddev->io_clone_set)) {
- err = bioset_init(&mddev->io_clone_set, BIO_POOL_SIZE,
- offsetof(struct md_io_clone, bio_clone), 0);
- if (err)
- goto exit_sync_set;
- }
-
pers = get_pers(mddev->level, mddev->clevel);
- if (!pers) {
- err = -EINVAL;
- goto abort;
- }
+ if (!pers)
+ return -EINVAL;
if (mddev->level != pers->head.id) {
mddev->level = pers->head.id;
mddev->new_level = pers->head.id;
@@ -6417,8 +6517,7 @@ int md_run(struct mddev *mddev)
pers->start_reshape == NULL) {
/* This personality cannot handle reshaping... */
put_pers(pers);
- err = -EINVAL;
- goto abort;
+ return -EINVAL;
}
if (pers->sync_request) {
@@ -6545,12 +6644,6 @@ bitmap_abort:
mddev->private = NULL;
put_pers(pers);
md_bitmap_destroy(mddev);
-abort:
- bioset_exit(&mddev->io_clone_set);
-exit_sync_set:
- bioset_exit(&mddev->sync_set);
-exit_bio_set:
- bioset_exit(&mddev->bio_set);
return err;
}
EXPORT_SYMBOL_GPL(md_run);
@@ -6683,6 +6776,7 @@ static void md_clean(struct mddev *mddev)
mddev->chunk_sectors = 0;
mddev->ctime = mddev->utime = 0;
mddev->layout = 0;
+ mddev->logical_block_size = 0;
mddev->max_disks = 0;
mddev->events = 0;
mddev->can_decrease_events = 0;
@@ -6775,10 +6869,6 @@ static void __md_stop(struct mddev *mddev)
mddev->private = NULL;
put_pers(pers);
clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
-
- bioset_exit(&mddev->bio_set);
- bioset_exit(&mddev->sync_set);
- bioset_exit(&mddev->io_clone_set);
}
void md_stop(struct mddev *mddev)
@@ -6869,6 +6959,10 @@ static int do_md_stop(struct mddev *mddev, int mode)
if (!md_is_rdwr(mddev))
set_disk_ro(disk, 0);
+ if (mode == 2 && mddev->pers->sync_request &&
+ mddev->to_remove == NULL)
+ mddev->to_remove = &md_redundancy_group;
+
__md_stop_writes(mddev);
__md_stop(mddev);
@@ -8373,22 +8467,21 @@ static int md_thread(void *arg)
return 0;
}
-static void md_wakeup_thread_directly(struct md_thread __rcu *thread)
+static void md_wakeup_thread_directly(struct md_thread __rcu **thread)
{
struct md_thread *t;
rcu_read_lock();
- t = rcu_dereference(thread);
+ t = rcu_dereference(*thread);
if (t)
wake_up_process(t->tsk);
rcu_read_unlock();
}
-void md_wakeup_thread(struct md_thread __rcu *thread)
+void __md_wakeup_thread(struct md_thread __rcu *thread)
{
struct md_thread *t;
- rcu_read_lock();
t = rcu_dereference(thread);
if (t) {
pr_debug("md: waking up MD thread %s.\n", t->tsk->comm);
@@ -8396,9 +8489,8 @@ void md_wakeup_thread(struct md_thread __rcu *thread)
if (wq_has_sleeper(&t->wqueue))
wake_up(&t->wqueue);
}
- rcu_read_unlock();
}
-EXPORT_SYMBOL(md_wakeup_thread);
+EXPORT_SYMBOL(__md_wakeup_thread);
struct md_thread *md_register_thread(void (*run) (struct md_thread *),
struct mddev *mddev, const char *name)
@@ -9978,6 +10070,52 @@ static void unregister_sync_thread(struct mddev *mddev)
md_reap_sync_thread(mddev);
}
+static bool md_should_do_recovery(struct mddev *mddev)
+{
+ /*
+ * As long as one of the following flags is set,
+ * recovery needs to do or cleanup.
+ */
+ if (test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
+ test_bit(MD_RECOVERY_DONE, &mddev->recovery))
+ return true;
+
+ /*
+ * If no flags are set and it is in read-only status,
+ * there is nothing to do.
+ */
+ if (!md_is_rdwr(mddev))
+ return false;
+
+ /*
+ * MD_SB_CHANGE_PENDING indicates that the array is switching from clean to
+ * active, and no action is needed for now.
+ * All other MD_SB_* flags require to update the superblock.
+ */
+ if (mddev->sb_flags & ~ (1<<MD_SB_CHANGE_PENDING))
+ return true;
+
+ /*
+ * If the array is not using external metadata and there has been no data
+ * written for some time, then the array's status needs to be set to
+ * in_sync.
+ */
+ if (mddev->external == 0 && mddev->safemode == 1)
+ return true;
+
+ /*
+ * When the system is about to restart or the process receives an signal,
+ * the array needs to be synchronized as soon as possible.
+ * Once the data synchronization is completed, need to change the array
+ * status to in_sync.
+ */
+ if (mddev->safemode == 2 && !mddev->in_sync &&
+ mddev->resync_offset == MaxSector)
+ return true;
+
+ return false;
+}
+
/*
* This routine is regularly called by all per-raid-array threads to
* deal with generic issues like resync and super-block update.
@@ -10014,18 +10152,7 @@ void md_check_recovery(struct mddev *mddev)
flush_signals(current);
}
- if (!md_is_rdwr(mddev) &&
- !test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) &&
- !test_bit(MD_RECOVERY_DONE, &mddev->recovery))
- return;
- if ( ! (
- (mddev->sb_flags & ~ (1<<MD_SB_CHANGE_PENDING)) ||
- test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
- test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
- (mddev->external == 0 && mddev->safemode == 1) ||
- (mddev->safemode == 2
- && !mddev->in_sync && mddev->resync_offset == MaxSector)
- ))
+ if (!md_should_do_recovery(mddev))
return;
if (mddev_trylock(mddev)) {
@@ -10281,7 +10408,6 @@ static int md_notify_reboot(struct notifier_block *this,
unsigned long code, void *x)
{
struct mddev *mddev;
- int need_delay = 0;
spin_lock(&all_mddevs_lock);
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
@@ -10295,21 +10421,11 @@ static int md_notify_reboot(struct notifier_block *this,
mddev->safemode = 2;
mddev_unlock(mddev);
}
- need_delay = 1;
spin_lock(&all_mddevs_lock);
mddev_put_locked(mddev);
}
spin_unlock(&all_mddevs_lock);
- /*
- * certain more exotic SCSI devices are known to be
- * volatile wrt too early system reboots. While the
- * right place to handle this issue is the given
- * driver, we do want to have a safe RAID driver ...
- */
- if (need_delay)
- msleep(1000);
-
return NOTIFY_DONE;
}
@@ -10697,6 +10813,7 @@ module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR);
module_param_call(new_array, add_named_array, NULL, NULL, S_IWUSR);
module_param(create_on_open, bool, S_IRUSR|S_IWUSR);
module_param(legacy_async_del_gendisk, bool, 0600);
+module_param(check_new_feature, bool, 0600);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MD RAID framework");
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 1979c2d4fe89..6985f2829bbd 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -354,6 +354,7 @@ enum mddev_flags {
MD_HAS_MULTIPLE_PPLS,
MD_NOT_READY,
MD_BROKEN,
+ MD_DO_DELETE,
MD_DELETED,
};
@@ -432,6 +433,7 @@ struct mddev {
sector_t array_sectors; /* exported array size */
int external_size; /* size managed
* externally */
+ unsigned int logical_block_size;
__u64 events;
/* If the last 'event' was simply a clean->dirty transition, and
* we didn't write it to the spares, then it is safe and simple
@@ -882,6 +884,12 @@ struct md_io_clone {
#define THREAD_WAKEUP 0
+#define md_wakeup_thread(thread) do { \
+ rcu_read_lock(); \
+ __md_wakeup_thread(thread); \
+ rcu_read_unlock(); \
+} while (0)
+
static inline void safe_put_page(struct page *p)
{
if (p) put_page(p);
@@ -895,7 +903,7 @@ extern struct md_thread *md_register_thread(
struct mddev *mddev,
const char *name);
extern void md_unregister_thread(struct mddev *mddev, struct md_thread __rcu **threadp);
-extern void md_wakeup_thread(struct md_thread __rcu *thread);
+extern void __md_wakeup_thread(struct md_thread __rcu *thread);
extern void md_check_recovery(struct mddev *mddev);
extern void md_reap_sync_thread(struct mddev *mddev);
extern enum sync_action md_sync_action(struct mddev *mddev);
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index e443e478645a..985c377356eb 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -68,7 +68,10 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
struct strip_zone *zone;
int cnt;
struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL);
- unsigned blksize = 512;
+ unsigned int blksize = 512;
+
+ if (!mddev_is_dm(mddev))
+ blksize = queue_logical_block_size(mddev->gendisk->queue);
*private_conf = ERR_PTR(-ENOMEM);
if (!conf)
@@ -84,7 +87,8 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
sector_div(sectors, mddev->chunk_sectors);
rdev1->sectors = sectors * mddev->chunk_sectors;
- blksize = max(blksize, queue_logical_block_size(
+ if (mddev_is_dm(mddev))
+ blksize = max(blksize, queue_logical_block_size(
rdev1->bdev->bd_disk->queue));
rdev_for_each(rdev2, mddev) {
@@ -383,6 +387,7 @@ static int raid0_set_limits(struct mddev *mddev)
lim.max_hw_sectors = mddev->chunk_sectors;
lim.max_write_zeroes_sectors = mddev->chunk_sectors;
lim.max_hw_wzeroes_unmap_sectors = mddev->chunk_sectors;
+ lim.logical_block_size = mddev->logical_block_size;
lim.io_min = mddev->chunk_sectors << 9;
lim.io_opt = lim.io_min * mddev->raid_disks;
lim.chunk_sectors = mddev->chunk_sectors;
@@ -405,6 +410,12 @@ static int raid0_run(struct mddev *mddev)
if (md_check_no_bitmap(mddev))
return -EINVAL;
+ if (!mddev_is_dm(mddev)) {
+ ret = raid0_set_limits(mddev);
+ if (ret)
+ return ret;
+ }
+
/* if private is not null, we are here after takeover */
if (mddev->private == NULL) {
ret = create_strip_zones(mddev, &conf);
@@ -413,11 +424,6 @@ static int raid0_run(struct mddev *mddev)
mddev->private = conf;
}
conf = mddev->private;
- if (!mddev_is_dm(mddev)) {
- ret = raid0_set_limits(mddev);
- if (ret)
- return ret;
- }
/* calculate array device size */
md_set_array_sectors(mddev, raid0_size(mddev, 0, 0));
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 592a40233004..57d50465eed1 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -3213,6 +3213,7 @@ static int raid1_set_limits(struct mddev *mddev)
md_init_stacking_limits(&lim);
lim.max_write_zeroes_sectors = 0;
lim.max_hw_wzeroes_unmap_sectors = 0;
+ lim.logical_block_size = mddev->logical_block_size;
lim.features |= BLK_FEAT_ATOMIC_WRITES;
err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY);
if (err)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 14dcd5142eb4..84be4cc7e873 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -4000,6 +4000,7 @@ static int raid10_set_queue_limits(struct mddev *mddev)
md_init_stacking_limits(&lim);
lim.max_write_zeroes_sectors = 0;
lim.max_hw_wzeroes_unmap_sectors = 0;
+ lim.logical_block_size = mddev->logical_block_size;
lim.io_min = mddev->chunk_sectors << 9;
lim.chunk_sectors = mddev->chunk_sectors;
lim.io_opt = lim.io_min * raid10_nr_stripes(conf);
diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c
index ba768ca7f422..e29e69335c69 100644
--- a/drivers/md/raid5-cache.c
+++ b/drivers/md/raid5-cache.c
@@ -3104,7 +3104,7 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev)
goto out_mempool;
spin_lock_init(&log->tree_lock);
- INIT_RADIX_TREE(&log->big_stripe_tree, GFP_NOWAIT | __GFP_NOWARN);
+ INIT_RADIX_TREE(&log->big_stripe_tree, GFP_NOWAIT);
thread = md_register_thread(r5l_reclaim_thread, log->rdev->mddev,
"reclaim");
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 24b32a0c95b4..e57ce3295292 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -4956,7 +4956,8 @@ static void handle_stripe(struct stripe_head *sh)
goto finish;
if (s.handle_bad_blocks ||
- test_bit(MD_SB_CHANGE_PENDING, &conf->mddev->sb_flags)) {
+ (md_is_rdwr(conf->mddev) &&
+ test_bit(MD_SB_CHANGE_PENDING, &conf->mddev->sb_flags))) {
set_bit(STRIPE_HANDLE, &sh->state);
goto finish;
}
@@ -6768,7 +6769,8 @@ static void raid5d(struct md_thread *thread)
int batch_size, released;
unsigned int offset;
- if (test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags))
+ if (md_is_rdwr(mddev) &&
+ test_bit(MD_SB_CHANGE_PENDING, &mddev->sb_flags))
break;
released = release_stripe_list(conf, conf->temp_inactive_list);
@@ -7745,6 +7747,7 @@ static int raid5_set_limits(struct mddev *mddev)
stripe = roundup_pow_of_two(data_disks * (mddev->chunk_sectors << 9));
md_init_stacking_limits(&lim);
+ lim.logical_block_size = mddev->logical_block_size;
lim.io_min = mddev->chunk_sectors << 9;
lim.io_opt = lim.io_min * (conf->raid_disks - conf->max_degraded);
lim.features |= BLK_FEAT_RAID_PARTIAL_STRIPES_EXPENSIVE;
diff --git a/drivers/media/cec/core/cec-core.c b/drivers/media/cec/core/cec-core.c
index d7259599029f..dd6e24a0899b 100644
--- a/drivers/media/cec/core/cec-core.c
+++ b/drivers/media/cec/core/cec-core.c
@@ -421,6 +421,7 @@ static int __init cec_devnode_init(void)
ret = bus_register(&cec_bus_type);
if (ret < 0) {
+ debugfs_remove_recursive(top_cec_dir);
unregister_chrdev_region(cec_dev_t, CEC_NUM_DEVICES);
pr_warn("cec: bus_register failed\n");
return -EIO;
diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c
index 9d0362a75ecd..a9e3bad76d54 100644
--- a/drivers/media/common/saa7146/saa7146_fops.c
+++ b/drivers/media/common/saa7146/saa7146_fops.c
@@ -186,11 +186,11 @@ static ssize_t fops_write(struct file *file, const char __user *data, size_t cou
struct saa7146_dev *dev = video_drvdata(file);
int ret;
- if (vdev->vfl_type != VFL_TYPE_VBI || !dev->ext_vv_data->vbi_fops.write)
+ if (vdev->vfl_type != VFL_TYPE_VBI || !dev->ext_vv_data->vbi_write)
return -EINVAL;
if (mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
- ret = dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
+ ret = dev->ext_vv_data->vbi_write(file, data, count, ppos);
mutex_unlock(vdev->lock);
return ret;
}
diff --git a/drivers/media/common/siano/smsir.c b/drivers/media/common/siano/smsir.c
index d85c78c104b9..af07fed21ae1 100644
--- a/drivers/media/common/siano/smsir.c
+++ b/drivers/media/common/siano/smsir.c
@@ -28,7 +28,7 @@ void sms_ir_event(struct smscore_device_t *coredev, const char *buf, int len)
for (i = 0; i < len >> 2; i++) {
struct ir_raw_event ev = {
.duration = abs(samples[i]),
- .pulse = (samples[i] > 0) ? false : true
+ .pulse = samples[i] <= 0
};
ir_raw_event_store(coredev->ir.dev, &ev);
diff --git a/drivers/media/common/videobuf2/videobuf2-dma-contig.c b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
index a13ec569c82f..7123c5fae92c 100644
--- a/drivers/media/common/videobuf2/videobuf2-dma-contig.c
+++ b/drivers/media/common/videobuf2/videobuf2-dma-contig.c
@@ -258,6 +258,7 @@ static void *vb2_dc_alloc(struct vb2_buffer *vb,
if (ret) {
dev_err(dev, "dma alloc of size %lu failed\n", size);
+ put_device(buf->dev);
kfree(buf);
return ERR_PTR(-ENOMEM);
}
diff --git a/drivers/media/common/videobuf2/videobuf2-v4l2.c b/drivers/media/common/videobuf2/videobuf2-v4l2.c
index d911021c1bb0..83862d57b126 100644
--- a/drivers/media/common/videobuf2/videobuf2-v4l2.c
+++ b/drivers/media/common/videobuf2/videobuf2-v4l2.c
@@ -1010,6 +1010,11 @@ int vb2_ioctl_remove_bufs(struct file *file, void *priv,
if (vb2_queue_is_busy(vdev->queue, file))
return -EBUSY;
+ if (vb2_fileio_is_active(vdev->queue)) {
+ dprintk(vdev->queue, 1, "file io in progress\n");
+ return -EBUSY;
+ }
+
return vb2_core_remove_bufs(vdev->queue, d->index, d->count);
}
EXPORT_SYMBOL_GPL(vb2_ioctl_remove_bufs);
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 151177e5a06d..8c6f5aafda1d 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -1414,8 +1414,8 @@ int dvb_dmxdev_init(struct dmxdev *dmxdev, struct dvb_adapter *dvb_adapter)
if (dmxdev->demux->open(dmxdev->demux) < 0)
return -EUSERS;
- dmxdev->filter = vmalloc(array_size(sizeof(struct dmxdev_filter),
- dmxdev->filternum));
+ dmxdev->filter = vmalloc_array(dmxdev->filternum,
+ sizeof(struct dmxdev_filter));
if (!dmxdev->filter)
return -ENOMEM;
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index baf64540dc00..7b591aa1179f 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -785,7 +785,7 @@ exit:
* be written.
* @bytes_write: Size of ebuf.
* @size_write_flag: A flag on Command Register which says whether the link size
- * information will be writen or not.
+ * information will be written or not.
*
* return: Number of bytes written, or < 0 on error.
*/
diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
index 7c4d86bfdd6c..290fc7961647 100644
--- a/drivers/media/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -744,7 +744,8 @@ static int dmx_ts_feed_start_filtering(struct dmx_ts_feed *ts_feed)
return -ENODEV;
}
- if ((ret = demux->start_feed(feed)) < 0) {
+ ret = demux->start_feed(feed);
+ if (ret < 0) {
mutex_unlock(&demux->mutex);
return ret;
}
@@ -797,7 +798,8 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
if (mutex_lock_interruptible(&demux->mutex))
return -ERESTARTSYS;
- if (!(feed = dvb_dmx_feed_alloc(demux))) {
+ feed = dvb_dmx_feed_alloc(demux);
+ if (!feed) {
mutex_unlock(&demux->mutex);
return -EBUSY;
}
@@ -817,7 +819,8 @@ static int dvbdmx_allocate_ts_feed(struct dmx_demux *dmx,
(*ts_feed)->stop_filtering = dmx_ts_feed_stop_filtering;
(*ts_feed)->set = dmx_ts_feed_set;
- if (!(feed->filter = dvb_dmx_filter_alloc(demux))) {
+ feed->filter = dvb_dmx_filter_alloc(demux);
+ if (!feed->filter) {
feed->state = DMX_STATE_FREE;
mutex_unlock(&demux->mutex);
return -EBUSY;
@@ -923,7 +926,8 @@ static void prepare_secfilters(struct dvb_demux_feed *dvbdmxfeed)
struct dmx_section_filter *sf;
u8 mask, mode, doneq;
- if (!(f = dvbdmxfeed->filter))
+ f = dvbdmxfeed->filter;
+ if (!f)
return;
do {
sf = &f->filter;
@@ -970,7 +974,8 @@ static int dmx_section_feed_start_filtering(struct dmx_section_feed *feed)
prepare_secfilters(dvbdmxfeed);
- if ((ret = dvbdmx->start_feed(dvbdmxfeed)) < 0) {
+ ret = dvbdmx->start_feed(dvbdmxfeed);
+ if (ret < 0) {
mutex_unlock(&dvbdmx->mutex);
return ret;
}
@@ -1057,7 +1062,8 @@ static int dvbdmx_allocate_section_feed(struct dmx_demux *demux,
if (mutex_lock_interruptible(&dvbdmx->mutex))
return -ERESTARTSYS;
- if (!(dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx))) {
+ dvbdmxfeed = dvb_dmx_feed_alloc(dvbdmx);
+ if (!dvbdmxfeed) {
mutex_unlock(&dvbdmx->mutex);
return -EBUSY;
}
@@ -1223,7 +1229,7 @@ static int dvbdmx_disconnect_frontend(struct dmx_demux *demux)
return 0;
}
-static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 * pids)
+static int dvbdmx_get_pes_pids(struct dmx_demux *demux, u16 *pids)
{
struct dvb_demux *dvbdemux = (struct dvb_demux *)demux;
@@ -1238,14 +1244,14 @@ int dvb_dmx_init(struct dvb_demux *dvbdemux)
dvbdemux->cnt_storage = NULL;
dvbdemux->users = 0;
- dvbdemux->filter = vmalloc(array_size(sizeof(struct dvb_demux_filter),
- dvbdemux->filternum));
+ dvbdemux->filter = vmalloc_array(dvbdemux->filternum,
+ sizeof(struct dvb_demux_filter));
if (!dvbdemux->filter)
return -ENOMEM;
- dvbdemux->feed = vmalloc(array_size(sizeof(struct dvb_demux_feed),
- dvbdemux->feednum));
+ dvbdemux->feed = vmalloc_array(dvbdemux->feednum,
+ sizeof(struct dvb_demux_feed));
if (!dvbdemux->feed) {
vfree(dvbdemux->filter);
dvbdemux->filter = NULL;
diff --git a/drivers/media/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb-core/dvb_ringbuffer.c
index 7d4558de8e83..de6226556826 100644
--- a/drivers/media/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb-core/dvb_ringbuffer.c
@@ -37,10 +37,11 @@
void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
{
- rbuf->pread=rbuf->pwrite=0;
- rbuf->data=data;
- rbuf->size=len;
- rbuf->error=0;
+ rbuf->pread = 0;
+ rbuf->pwrite = 0;
+ rbuf->data = data;
+ rbuf->size = len;
+ rbuf->error = 0;
init_waitqueue_head(&rbuf->queue);
@@ -235,7 +236,7 @@ ssize_t dvb_ringbuffer_write_user(struct dvb_ringbuffer *rbuf,
return len;
}
-ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len)
+ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8 *buf, size_t len)
{
int status;
ssize_t oldpwrite = rbuf->pwrite;
@@ -245,7 +246,8 @@ ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t le
DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
status = dvb_ringbuffer_write(rbuf, buf, len);
- if (status < 0) rbuf->pwrite = oldpwrite;
+ if (status < 0)
+ rbuf->pwrite = oldpwrite;
return status;
}
@@ -258,8 +260,10 @@ ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
pktlen = rbuf->data[idx] << 8;
pktlen |= rbuf->data[(idx + 1) % rbuf->size];
- if (offset > pktlen) return -EINVAL;
- if ((offset + len) > pktlen) len = pktlen - offset;
+ if (offset > pktlen)
+ return -EINVAL;
+ if ((offset + len) > pktlen)
+ len = pktlen - offset;
idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
todo = len;
@@ -278,7 +282,7 @@ ssize_t dvb_ringbuffer_pkt_read_user(struct dvb_ringbuffer *rbuf, size_t idx,
}
ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
- int offset, u8* buf, size_t len)
+ int offset, u8 *buf, size_t len)
{
size_t todo;
size_t split;
@@ -286,8 +290,10 @@ ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
pktlen = rbuf->data[idx] << 8;
pktlen |= rbuf->data[(idx + 1) % rbuf->size];
- if (offset > pktlen) return -EINVAL;
- if ((offset + len) > pktlen) len = pktlen - offset;
+ if (offset > pktlen)
+ return -EINVAL;
+ if ((offset + len) > pktlen)
+ len = pktlen - offset;
idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
todo = len;
@@ -309,7 +315,7 @@ void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
// clean up disposed packets
- while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
+ while (dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
@@ -321,14 +327,14 @@ void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
}
}
-ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
+ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t *pktlen)
{
int consumed;
int curpktlen;
int curpktstatus;
if (idx == -1) {
- idx = rbuf->pread;
+ idx = rbuf->pread;
} else {
curpktlen = rbuf->data[idx] << 8;
curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
@@ -339,7 +345,7 @@ ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t*
if (consumed < 0)
consumed += rbuf->size;
- while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
+ while ((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
curpktlen = rbuf->data[idx] << 8;
curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index 9df7c213716a..8b980d371a45 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -571,8 +571,8 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
MKDEV(DVB_MAJOR, minor),
dvbdev, "dvb%d.%s%d", adap->num, dnames[type], id);
if (IS_ERR(clsdev)) {
- pr_err("%s: failed to create device dvb%d.%s%d (%ld)\n",
- __func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
+ pr_err("%s: failed to create device dvb%d.%s%d (%pe)\n",
+ __func__, adap->num, dnames[type], id, clsdev);
if (new_node) {
list_del(&new_node->list_head);
kfree(dvbdevfops);
diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c
index 415f1f91cc30..8fcb4417ba22 100644
--- a/drivers/media/dvb-frontends/cxd2841er.c
+++ b/drivers/media/dvb-frontends/cxd2841er.c
@@ -1936,7 +1936,8 @@ static void cxd2841er_read_ber(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct cxd2841er_priv *priv = fe->demodulator_priv;
- u32 ret, bit_error = 0, bit_count = 0;
+ u32 bit_error = 0, bit_count = 0;
+ int ret;
dev_dbg(&priv->i2c->dev, "%s()\n", __func__);
switch (p->delivery_system) {
diff --git a/drivers/media/dvb-frontends/drx39xyj/drxj.c b/drivers/media/dvb-frontends/drx39xyj/drxj.c
index 779cce93e94a..428b31e60874 100644
--- a/drivers/media/dvb-frontends/drx39xyj/drxj.c
+++ b/drivers/media/dvb-frontends/drx39xyj/drxj.c
@@ -1270,7 +1270,7 @@ static const u16 nicam_presc_table_val[43] = {
TODO: check ignoring single/multimaster is ok for AUD access ?
*/
-#define DRXJ_ISAUDWRITE(addr) (((((addr)>>16)&1) == 1) ? true : false)
+#define DRXJ_ISAUDWRITE(addr) ((((addr) >> 16) & 1) == 1)
#define DRXJ_DAP_AUDTRIF_TIMEOUT 80 /* millisec */
/*============================================================================*/
diff --git a/drivers/media/dvb-frontends/drxk_hard.c b/drivers/media/dvb-frontends/drxk_hard.c
index 87f3d4f0eb8c..9ef367918824 100644
--- a/drivers/media/dvb-frontends/drxk_hard.c
+++ b/drivers/media/dvb-frontends/drxk_hard.c
@@ -6324,8 +6324,7 @@ static int drxk_set_parameters(struct dvb_frontend *fe)
case SYS_DVBC_ANNEX_C:
if (!state->m_has_dvbc)
return -EINVAL;
- state->m_itut_annex_c = (delsys == SYS_DVBC_ANNEX_C) ?
- true : false;
+ state->m_itut_annex_c = delsys == SYS_DVBC_ANNEX_C;
if (state->m_itut_annex_c)
setoperation_mode(state, OM_QAM_ITU_C);
else
diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
index cab442a350a5..8c34a5b850bc 100644
--- a/drivers/media/dvb-frontends/lgdt330x.c
+++ b/drivers/media/dvb-frontends/lgdt330x.c
@@ -124,7 +124,6 @@ static int i2c_read_demod_bytes(struct lgdt330x_state *state,
/* Software reset */
static int lgdt3302_sw_reset(struct lgdt330x_state *state)
{
- u8 ret;
u8 reset[] = {
IRQ_MASK,
/*
@@ -133,6 +132,7 @@ static int lgdt3302_sw_reset(struct lgdt330x_state *state)
*/
0x00
};
+ int ret;
ret = i2c_write_demod_bytes(state,
reset, sizeof(reset));
@@ -147,11 +147,11 @@ static int lgdt3302_sw_reset(struct lgdt330x_state *state)
static int lgdt3303_sw_reset(struct lgdt330x_state *state)
{
- u8 ret;
u8 reset[] = {
0x02,
0x00 /* bit 0 is active low software reset */
};
+ int ret;
ret = i2c_write_demod_bytes(state,
reset, sizeof(reset));
diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c
index 7a58f53ab999..818c4e67364c 100644
--- a/drivers/media/dvb-frontends/mn88443x.c
+++ b/drivers/media/dvb-frontends/mn88443x.c
@@ -694,8 +694,7 @@ static int mn88443x_probe(struct i2c_client *client)
chip->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(chip->mclk) && !conf) {
- dev_err(dev, "Failed to request mclk: %ld\n",
- PTR_ERR(chip->mclk));
+ dev_err(dev, "Failed to request mclk: %pe\n", chip->mclk);
return PTR_ERR(chip->mclk);
}
@@ -709,8 +708,8 @@ static int mn88443x_probe(struct i2c_client *client)
chip->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(chip->reset_gpio)) {
- dev_err(dev, "Failed to request reset_gpio: %ld\n",
- PTR_ERR(chip->reset_gpio));
+ dev_err(dev, "Failed to request reset_gpio: %pe\n",
+ chip->reset_gpio);
return PTR_ERR(chip->reset_gpio);
}
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index cdd7ba5da0d5..4b4db8c4f496 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -137,6 +137,16 @@ config VIDEO_HI847
To compile this driver as a module, choose M here: the
module will be called hi847.
+config VIDEO_IMX111
+ tristate "Sony IMX111 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a V4L2 sensor driver for the Sony IMX111 camera
+ sensors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx111.
+
config VIDEO_IMX208
tristate "Sony IMX208 sensor support"
help
@@ -471,7 +481,7 @@ config VIDEO_OV2735
tristate "OmniVision OV2735 sensor support"
select V4L2_CCI_I2C
help
- This is a Video4Linux2 sensor driver for the Sony
+ This is a Video4Linux2 sensor driver for the OmniVision
OV2735 camera.
To compile this driver as a module, choose M here: the
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 57cdd8dc96f6..c5f17602454f 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_VIDEO_HI556) += hi556.o
obj-$(CONFIG_VIDEO_HI846) += hi846.o
obj-$(CONFIG_VIDEO_HI847) += hi847.o
obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
+obj-$(CONFIG_VIDEO_IMX111) += imx111.o
obj-$(CONFIG_VIDEO_IMX208) += imx208.o
obj-$(CONFIG_VIDEO_IMX214) += imx214.o
obj-$(CONFIG_VIDEO_IMX219) += imx219.o
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 8fe7c2f72883..516553fb17e9 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -3670,7 +3670,7 @@ static int adv76xx_probe(struct i2c_client *client)
err = media_entity_pads_init(&sd->entity, state->source_pad + 1,
state->pads);
if (err)
- goto err_work_queues;
+ goto err_i2c;
/* Configure regmaps */
err = configure_regmaps(state);
@@ -3711,8 +3711,6 @@ static int adv76xx_probe(struct i2c_client *client)
err_entity:
media_entity_cleanup(&sd->entity);
-err_work_queues:
- cancel_delayed_work(&state->delayed_work_enable_hotplug);
err_i2c:
adv76xx_unregister_clients(state);
err_hdl:
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 9780082db841..ea6966c0605e 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -2699,6 +2699,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
/* CP block */
struct adv7842_state *state = to_state(sd);
struct v4l2_dv_timings timings;
+ int temp;
u8 reg_io_0x02 = io_read(sd, 0x02);
u8 reg_io_0x21 = io_read(sd, 0x21);
u8 reg_rep_0x77 = rep_read(sd, 0x77);
@@ -2821,8 +2822,9 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
(((reg_io_0x02 >> 2) & 0x01) ^ (reg_io_0x02 & 0x01)) ?
"(16-235)" : "(0-255)",
(reg_io_0x02 & 0x08) ? "enabled" : "disabled");
+ temp = cp_read(sd, 0xf4) >> 4;
v4l2_info(sd, "Color space conversion: %s\n",
- csc_coeff_sel_rb[cp_read(sd, 0xf4) >> 4]);
+ temp < 0 ? "" : csc_coeff_sel_rb[temp]);
if (!is_digital_input(sd))
return 0;
@@ -2852,8 +2854,9 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd)
hdmi_read(sd, 0x5f));
v4l2_info(sd, "AV Mute: %s\n",
(hdmi_read(sd, 0x04) & 0x40) ? "on" : "off");
+ temp = hdmi_read(sd, 0x0b) >> 6;
v4l2_info(sd, "Deep color mode: %s\n",
- deep_color_mode_txt[hdmi_read(sd, 0x0b) >> 6]);
+ temp < 0 ? "" : deep_color_mode_txt[temp]);
adv7842_log_infoframes(sd);
@@ -3466,8 +3469,8 @@ static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, const cha
cp = i2c_new_dummy_device(client->adapter, io_read(sd, io_reg) >> 1);
if (IS_ERR(cp)) {
- v4l2_err(sd, "register %s on i2c addr 0x%x failed with %ld\n",
- desc, addr, PTR_ERR(cp));
+ v4l2_err(sd, "register %s on i2c addr 0x%x failed with %pe\n",
+ desc, addr, cp);
cp = NULL;
}
@@ -3626,7 +3629,7 @@ static int adv7842_probe(struct i2c_client *client)
err = media_entity_pads_init(&sd->entity, ADV7842_PAD_SOURCE + 1,
state->pads);
if (err)
- goto err_work_queues;
+ goto err_i2c;
err = adv7842_core_init(sd);
if (err)
@@ -3647,8 +3650,6 @@ static int adv7842_probe(struct i2c_client *client)
err_entity:
media_entity_cleanup(&sd->entity);
-err_work_queues:
- cancel_delayed_work(&state->delayed_work_enable_hotplug);
err_i2c:
adv7842_unregister_clients(sd);
err_hdl:
diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c
index 939bf590d4b2..f156058500e3 100644
--- a/drivers/media/i2c/ar0521.c
+++ b/drivers/media/i2c/ar0521.c
@@ -1109,8 +1109,8 @@ static int ar0521_probe(struct i2c_client *client)
ar0521_supply_names[cnt]);
if (IS_ERR(supply)) {
- dev_info(dev, "no %s regulator found: %li\n",
- ar0521_supply_names[cnt], PTR_ERR(supply));
+ dev_info(dev, "no %s regulator found: %pe\n",
+ ar0521_supply_names[cnt], supply);
return PTR_ERR(supply);
}
sensor->supplies[cnt] = supply;
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 1c889c878abd..f8523140784c 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -3237,8 +3237,8 @@ static int ccs_probe(struct i2c_client *client)
dev_info(&client->dev, "no clock defined, continuing...\n");
sensor->ext_clk = NULL;
} else if (IS_ERR(sensor->ext_clk)) {
- dev_err(&client->dev, "could not get clock (%ld)\n",
- PTR_ERR(sensor->ext_clk));
+ dev_err(&client->dev, "could not get clock (%pe)\n",
+ sensor->ext_clk);
return -EPROBE_DEFER;
}
@@ -3294,8 +3294,8 @@ static int ccs_probe(struct i2c_client *client)
sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
if (IS_ERR(sensor->regmap)) {
- dev_err(&client->dev, "can't initialise CCI (%ld)\n",
- PTR_ERR(sensor->regmap));
+ dev_err(&client->dev, "can't initialise CCI (%pe)\n",
+ sensor->regmap);
return PTR_ERR(sensor->regmap);
}
diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c
index 73150061ea45..e97e499b04e6 100644
--- a/drivers/media/i2c/ds90ub913.c
+++ b/drivers/media/i2c/ds90ub913.c
@@ -622,7 +622,7 @@ static int ub913_v4l2_notifier_register(struct ub913_data *priv)
fwnode_handle_put(ep_fwnode);
if (IS_ERR(asd)) {
- dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd));
+ dev_err(dev, "Failed to add subdev: %pe", asd);
v4l2_async_nf_cleanup(&priv->notifier);
return PTR_ERR(asd);
}
diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index e3fc9d66970a..daefdb108fbf 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -776,7 +776,7 @@ static int ub953_v4l2_notifier_register(struct ub953_data *priv)
fwnode_handle_put(ep_fwnode);
if (IS_ERR(asd)) {
- dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd));
+ dev_err(dev, "Failed to add subdev: %pe", asd);
v4l2_async_nf_cleanup(&priv->notifier);
return PTR_ERR(asd);
}
@@ -1023,15 +1023,17 @@ static unsigned long ub953_clkout_recalc_rate(struct clk_hw *hw,
return rate;
}
-static long ub953_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int ub953_clkout_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct ub953_data *priv = container_of(hw, struct ub953_data, clkout_clk_hw);
struct ub953_clkout_data clkout_data;
- ub953_calc_clkout_params(priv, rate, &clkout_data);
+ ub953_calc_clkout_params(priv, req->rate, &clkout_data);
+
+ req->rate = clkout_data.rate;
- return clkout_data.rate;
+ return 0;
}
static int ub953_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -1050,7 +1052,7 @@ static int ub953_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
static const struct clk_ops ub953_clkout_ops = {
.recalc_rate = ub953_clkout_recalc_rate,
- .round_rate = ub953_clkout_round_rate,
+ .determine_rate = ub953_clkout_determine_rate,
.set_rate = ub953_clkout_set_rate,
};
diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
index 032fbcb981f2..59558335989e 100644
--- a/drivers/media/i2c/dw9719.c
+++ b/drivers/media/i2c/dw9719.c
@@ -23,6 +23,25 @@
#define DW9719_CTRL_STEPS 16
#define DW9719_CTRL_DELAY_US 1000
+#define DW9718S_PD CCI_REG8(0)
+
+#define DW9718S_CONTROL CCI_REG8(1)
+#define DW9718S_CONTROL_SW_LINEAR BIT(0)
+#define DW9718S_CONTROL_SAC_SHIFT 1
+#define DW9718S_CONTROL_SAC_MASK 0x7
+#define DW9718S_CONTROL_OCP_DISABLE BIT(4)
+#define DW9718S_CONTROL_UVLO_DISABLE BIT(5)
+#define DW9718S_DEFAULT_SAC 4
+
+#define DW9718S_VCM_CURRENT CCI_REG16(2)
+
+#define DW9718S_SW CCI_REG8(4)
+#define DW9718S_SW_VCM_FREQ_MASK 0xF
+#define DW9718S_DEFAULT_VCM_FREQ 0
+
+#define DW9718S_SACT CCI_REG8(5)
+#define DW9718S_SACT_PERIOD_8_8MS 0x19
+
#define DW9719_INFO CCI_REG8(0)
#define DW9719_ID 0xF1
#define DW9761_ID 0xF4
@@ -49,12 +68,17 @@
#define DW9761_VCM_PRELOAD CCI_REG8(8)
#define DW9761_DEFAULT_VCM_PRELOAD 0x73
+#define DW9800K_DEFAULT_SAC 1
+#define DW9800K_MODE_SAC_SHIFT 6
+#define DW9800K_DEFAULT_VCM_FREQ 0x10
#define to_dw9719_device(x) container_of(x, struct dw9719_device, sd)
enum dw9719_model {
+ DW9718S,
DW9719,
DW9761,
+ DW9800K,
};
struct dw9719_device {
@@ -75,26 +99,55 @@ struct dw9719_device {
static int dw9719_power_down(struct dw9719_device *dw9719)
{
+ u32 reg_pwr = dw9719->model == DW9718S ? DW9718S_PD : DW9719_CONTROL;
+
+ /*
+ * Worth engaging the internal SHUTDOWN mode especially due to the
+ * regulator being potentially shared with other devices.
+ */
+ if (cci_write(dw9719->regmap, reg_pwr, DW9719_SHUTDOWN, NULL))
+ dev_err(dw9719->dev, "Error writing to power register\n");
return regulator_disable(dw9719->regulator);
}
static int dw9719_power_up(struct dw9719_device *dw9719, bool detect)
{
+ u32 reg_pwr = dw9719->model == DW9718S ? DW9718S_PD : DW9719_CONTROL;
u64 val;
int ret;
+ int err;
ret = regulator_enable(dw9719->regulator);
if (ret)
return ret;
- /* Jiggle SCL pin to wake up device */
- cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_SHUTDOWN, &ret);
- fsleep(100);
- cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_STANDBY, &ret);
- /* Need 100us to transit from SHUTDOWN to STANDBY */
- fsleep(100);
+ /*
+ * Need 100us to transition from SHUTDOWN to STANDBY.
+ * Jiggle the SCL pin to wake up the device (even when the regulator is
+ * shared) and wait double the time to be sure, as 100us is not enough
+ * at least on the DW9718S as found on the motorola-nora smartphone,
+ * then retry the write.
+ */
+ cci_write(dw9719->regmap, reg_pwr, DW9719_STANDBY, NULL);
+ /* the jiggle is expected to fail, don't even log that as error */
+ fsleep(200);
+ cci_write(dw9719->regmap, reg_pwr, DW9719_STANDBY, &ret);
if (detect) {
+ /* These models do not have an INFO register */
+ switch (dw9719->model) {
+ case DW9718S:
+ dw9719->sac_mode = DW9718S_DEFAULT_SAC;
+ dw9719->vcm_freq = DW9718S_DEFAULT_VCM_FREQ;
+ goto props;
+ case DW9800K:
+ dw9719->sac_mode = DW9800K_DEFAULT_SAC;
+ dw9719->vcm_freq = DW9800K_DEFAULT_VCM_FREQ;
+ goto props;
+ default:
+ break;
+ }
+
ret = cci_read(dw9719->regmap, DW9719_INFO, &val, NULL);
if (ret < 0)
return ret;
@@ -118,23 +171,52 @@ static int dw9719_power_up(struct dw9719_device *dw9719, bool detect)
return -ENXIO;
}
+props:
/* Optional indication of SAC mode select */
device_property_read_u32(dw9719->dev, "dongwoon,sac-mode",
&dw9719->sac_mode);
/* Optional indication of VCM frequency */
- device_property_read_u32(dw9719->dev, "dongwoon,vcm-freq",
+ err = device_property_read_u32(dw9719->dev, "dongwoon,vcm-freq",
+ &dw9719->vcm_freq);
+ if (err == 0)
+ dev_warn(dw9719->dev, "dongwoon,vcm-freq property is deprecated, please use dongwoon,vcm-prescale\n");
+
+ /* Optional indication of VCM prescale */
+ device_property_read_u32(dw9719->dev, "dongwoon,vcm-prescale",
&dw9719->vcm_freq);
}
- cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
- cci_write(dw9719->regmap, DW9719_MODE, dw9719->mode_low_bits |
- (dw9719->sac_mode << DW9719_MODE_SAC_SHIFT), &ret);
- cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
-
- if (dw9719->model == DW9761)
+ switch (dw9719->model) {
+ case DW9800K:
+ cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
+ cci_write(dw9719->regmap, DW9719_MODE,
+ dw9719->sac_mode << DW9800K_MODE_SAC_SHIFT, &ret);
+ cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
+ break;
+ case DW9718S:
+ /* Datasheet says [OCP/UVLO] should be disabled below 2.5V */
+ dw9719->sac_mode &= DW9718S_CONTROL_SAC_MASK;
+ cci_write(dw9719->regmap, DW9718S_CONTROL,
+ DW9718S_CONTROL_SW_LINEAR |
+ (dw9719->sac_mode << DW9718S_CONTROL_SAC_SHIFT) |
+ DW9718S_CONTROL_OCP_DISABLE |
+ DW9718S_CONTROL_UVLO_DISABLE, &ret);
+ cci_write(dw9719->regmap, DW9718S_SACT,
+ DW9718S_SACT_PERIOD_8_8MS, &ret);
+ cci_write(dw9719->regmap, DW9718S_SW,
+ dw9719->vcm_freq & DW9718S_SW_VCM_FREQ_MASK, &ret);
+ break;
+ case DW9761:
cci_write(dw9719->regmap, DW9761_VCM_PRELOAD,
DW9761_DEFAULT_VCM_PRELOAD, &ret);
+ fallthrough;
+ case DW9719:
+ cci_write(dw9719->regmap, DW9719_CONTROL, DW9719_ENABLE_RINGING, &ret);
+ cci_write(dw9719->regmap, DW9719_MODE, dw9719->mode_low_bits |
+ (dw9719->sac_mode << DW9719_MODE_SAC_SHIFT), &ret);
+ cci_write(dw9719->regmap, DW9719_VCM_FREQ, dw9719->vcm_freq, &ret);
+ }
if (ret)
dw9719_power_down(dw9719);
@@ -144,7 +226,9 @@ static int dw9719_power_up(struct dw9719_device *dw9719, bool detect)
static int dw9719_t_focus_abs(struct dw9719_device *dw9719, s32 value)
{
- return cci_write(dw9719->regmap, DW9719_VCM_CURRENT, value, NULL);
+ u32 reg = dw9719->model == DW9718S ? DW9718S_VCM_CURRENT
+ : DW9719_VCM_CURRENT;
+ return cci_write(dw9719->regmap, reg, value, NULL);
}
static int dw9719_set_ctrl(struct v4l2_ctrl *ctrl)
@@ -229,7 +313,7 @@ static int dw9719_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
static int dw9719_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
- pm_runtime_put(sd->dev);
+ pm_runtime_put_autosuspend(sd->dev);
return 0;
}
@@ -275,6 +359,8 @@ static int dw9719_probe(struct i2c_client *client)
if (!dw9719)
return -ENOMEM;
+ dw9719->model = (enum dw9719_model)(uintptr_t)i2c_get_match_data(client);
+
dw9719->regmap = devm_cci_regmap_init_i2c(client, 8);
if (IS_ERR(dw9719->regmap))
return PTR_ERR(dw9719->regmap);
@@ -353,12 +439,14 @@ static void dw9719_remove(struct i2c_client *client)
pm_runtime_set_suspended(&client->dev);
}
-static const struct i2c_device_id dw9719_id_table[] = {
- { "dw9719" },
- { "dw9761" },
+static const struct of_device_id dw9719_of_table[] = {
+ { .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S },
+ { .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 },
+ { .compatible = "dongwoon,dw9761", .data = (const void *)DW9761 },
+ { .compatible = "dongwoon,dw9800k", .data = (const void *)DW9800K },
{ }
};
-MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
+MODULE_DEVICE_TABLE(of, dw9719_of_table);
static DEFINE_RUNTIME_DEV_PM_OPS(dw9719_pm_ops, dw9719_suspend, dw9719_resume,
NULL);
@@ -367,10 +455,10 @@ static struct i2c_driver dw9719_i2c_driver = {
.driver = {
.name = "dw9719",
.pm = pm_sleep_ptr(&dw9719_pm_ops),
+ .of_match_table = dw9719_of_table,
},
.probe = dw9719_probe,
.remove = dw9719_remove,
- .id_table = dw9719_id_table,
};
module_i2c_driver(dw9719_i2c_driver);
diff --git a/drivers/media/i2c/imx111.c b/drivers/media/i2c/imx111.c
new file mode 100644
index 000000000000..8eb919788ef7
--- /dev/null
+++ b/drivers/media/i2c/imx111.c
@@ -0,0 +1,1610 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/media.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/units.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-mediabus.h>
+
+/* product information registers */
+#define IMX111_PRODUCT_ID CCI_REG16(0x0000)
+#define IMX111_CHIP_ID 0x111
+#define IMX111_REVISION CCI_REG8(0x0002)
+#define IMX111_MANUFACTURER_ID CCI_REG8(0x0003)
+#define IMX111_FRAME_COUNTER CCI_REG8(0x0005)
+#define IMX111_PIXEL_ORDER CCI_REG8(0x0006)
+
+/* general configuration registers */
+#define IMX111_STREAMING_MODE CCI_REG8(0x0100)
+#define IMX111_MODE_STANDBY 0
+#define IMX111_MODE_STREAMING 1
+#define IMX111_IMAGE_ORIENTATION CCI_REG8(0x0101)
+#define IMX111_IMAGE_HFLIP BIT(0)
+#define IMX111_IMAGE_VFLIP BIT(1)
+#define IMX111_SOFTWARE_RESET CCI_REG8(0x0103)
+#define IMX111_RESET_ON 1
+#define IMX111_GROUP_WRITE CCI_REG8(0x0104)
+#define IMX111_GROUP_WRITE_ON 1
+#define IMX111_FRAME_DROP CCI_REG8(0x0105)
+#define IMX111_FRAME_DROP_ON 1
+#define IMX111_CHANNEL_ID CCI_REG8(0x0110)
+#define IMX111_SIGNALLING_MODE CCI_REG8(0x0111)
+#define IMX111_DATA_DEPTH CCI_REG16(0x0112)
+#define IMX111_DATA_DEPTH_RAW8 0x08
+#define IMX111_DATA_DEPTH_RAW10 0x0a
+
+/* integration time registers */
+#define IMX111_INTEGRATION_TIME CCI_REG16(0x0202)
+#define IMX111_INTEGRATION_TIME_MIN 0x1
+#define IMX111_INTEGRATION_TIME_MAX 0xffff
+#define IMX111_INTEGRATION_TIME_STEP 1
+#define IMX111_INTEGRATION_TIME_OFFSET 5
+
+/* analog gain control */
+#define IMX111_REG_ANALOG_GAIN CCI_REG8(0x0205)
+#define IMX111_ANA_GAIN_MIN 0
+#define IMX111_ANA_GAIN_MAX 240
+#define IMX111_ANA_GAIN_STEP 1
+#define IMX111_ANA_GAIN_DEFAULT 0
+
+/* digital gain control */
+#define IMX111_REG_DIG_GAIN_GREENR CCI_REG16(0x020e)
+#define IMX111_REG_DIG_GAIN_RED CCI_REG16(0x0210)
+#define IMX111_REG_DIG_GAIN_BLUE CCI_REG16(0x0212)
+#define IMX111_REG_DIG_GAIN_GREENB CCI_REG16(0x0214)
+#define IMX111_DGTL_GAIN_MIN 0x0100
+#define IMX111_DGTL_GAIN_MAX 0x0fff
+#define IMX111_DGTL_GAIN_DEFAULT 0x0100
+#define IMX111_DGTL_GAIN_STEP 1
+
+/* clock configuration registers */
+#define IMX111_PIXEL_CLK_DIVIDER_PLL1 CCI_REG8(0x0301)
+#define IMX111_SYSTEM_CLK_DIVIDER_PLL1 CCI_REG8(0x0303)
+#define IMX111_PRE_PLL_CLK_DIVIDER_PLL1 CCI_REG8(0x0305)
+#define IMX111_PLL_MULTIPLIER_PLL1 CCI_REG8(0x0307)
+#define IMX111_PLL_SETTLING_TIME CCI_REG8(0x303c)
+#define IMX111_PLL_SETTLING_TIME_DEFAULT 200
+#define IMX111_POST_DIVIDER CCI_REG8(0x30a4)
+#define IMX111_POST_DIVIDER_DIV1 2
+#define IMX111_POST_DIVIDER_DIV2 0
+#define IMX111_POST_DIVIDER_DIV4 1
+
+/* frame timing registers */
+#define IMX111_VERTICAL_TOTAL_LENGTH CCI_REG16(0x0340)
+#define IMX111_VTL_MAX 0x09d8
+#define IMX111_VBLANK_MIN 16
+#define IMX111_HORIZONTAL_TOTAL_LENGTH CCI_REG16(0x0342)
+#define IMX111_HTL_MAX 0x0dd0
+#define IMX111_HBLANK_MIN 16
+
+/* image size registers */
+#define IMX111_HORIZONTAL_START CCI_REG16(0x0344)
+#define IMX111_VERTICAL_START CCI_REG16(0x0346)
+#define IMX111_HORIZONTAL_END CCI_REG16(0x0348)
+#define IMX111_VERTICAL_END CCI_REG16(0x034a)
+#define IMX111_IMAGE_WIDTH CCI_REG16(0x034c)
+#define IMX111_IMAGE_HEIGHT CCI_REG16(0x034e)
+#define IMX111_H_EVEN_INC CCI_REG8(0x0381)
+#define IMX111_H_ODD_INC CCI_REG8(0x0383)
+#define IMX111_W_EVEN_INC CCI_REG8(0x0385)
+#define IMX111_W_ODD_INC CCI_REG8(0x0387)
+
+/* test pattern registers */
+#define IMX111_TEST_PATTERN CCI_REG8(0x0601)
+#define IMX111_TEST_PATTERN_NONE 0
+#define IMX111_TEST_PATTERN_SOLID 1
+#define IMX111_TEST_PATTERN_BARS 2
+#define IMX111_TEST_PATTERN_FADE 3
+#define IMX111_TEST_PATTERN_PN9 4
+#define IMX111_SOLID_COLOR_RED CCI_REG16(0x0602)
+#define IMX111_SOLID_COLOR_GR CCI_REG16(0x0604)
+#define IMX111_SOLID_COLOR_BLUE CCI_REG16(0x0606)
+#define IMX111_SOLID_COLOR_GB CCI_REG16(0x0608)
+#define IMX111_TESTP_COLOUR_MIN 0
+#define IMX111_TESTP_COLOUR_MAX 0x03ff
+#define IMX111_TESTP_COLOUR_STEP 1
+
+#define IMX111_FRAME_RATE_STEP 5
+
+#define IMX111_PIXEL_ARRAY_WIDTH 3280U
+#define IMX111_PIXEL_ARRAY_HEIGHT 2464U
+
+enum {
+ IMX111_MODE_3280x2464,
+ IMX111_MODE_3280x1848,
+ IMX111_MODE_3280x1098,
+ IMX111_MODE_2100x1200,
+ IMX111_MODE_1952x1098,
+ IMX111_MODE_1920x1080,
+ IMX111_MODE_1640x1232,
+ IMX111_MODE_1440x1080,
+ IMX111_MODE_1640x924,
+ IMX111_MODE_1308x736,
+ IMX111_MODE_1280x720,
+ IMX111_MODE_820x614,
+ IMX111_MODE_640x480,
+};
+
+static const struct regulator_bulk_data imx111_supplies[] = {
+ { .supply = "iovdd" },
+ { .supply = "dvdd" },
+ { .supply = "avdd" },
+};
+
+struct imx111_mode {
+ u32 width;
+ u32 height;
+
+ /* Default vertical and horizontal total length */
+ u32 vtl_def;
+ u32 htl_def;
+
+ struct {
+ const struct cci_reg_sequence *regs;
+ u32 num_of_regs;
+ } reg_list;
+};
+
+struct imx111_pll {
+ u64 extclk_rate;
+ u8 pre_div;
+ u8 mult;
+};
+
+struct imx111 {
+ struct regmap *regmap;
+
+ struct clk *extclk;
+ struct gpio_desc *reset;
+ struct regulator_bulk_data *supplies;
+
+ struct v4l2_fwnode_endpoint bus_cfg;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+
+ /* V4L2 Controls */
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+
+ /* Current mode */
+ const struct imx111_mode *cur_mode;
+ const struct imx111_pll *pll;
+ u32 data_depth;
+
+ u64 pixel_clk_raw;
+ s64 default_link_freq;
+};
+
+static const struct imx111_pll imx111_pll[] = {
+ { .extclk_rate = 6000000, .pre_div = 1, .mult = 113, },
+ { .extclk_rate = 12000000, .pre_div = 2, .mult = 113, },
+ { .extclk_rate = 13500000, .pre_div = 1, .mult = 50, },
+ { .extclk_rate = 18000000, .pre_div = 2, .mult = 75, },
+ { .extclk_rate = 24000000, .pre_div = 4, .mult = 113, },
+ { .extclk_rate = 27000000, .pre_div = 2, .mult = 50, },
+ { .extclk_rate = 36000000, .pre_div = 4, .mult = 75, },
+ { .extclk_rate = 54000000, .pre_div = 4, .mult = 50, },
+};
+
+/*
+ * This table MUST contain 4 entries per format, to cover the various flip
+ * combinations in the order
+ * - no flip
+ * - h flip
+ * - v flip
+ * - h&v flips
+ */
+static const u32 imx111_mbus_formats[] = {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+};
+
+static const struct cci_reg_sequence imx111_global_init[] = {
+ { CCI_REG8(0x3080), 0x50 },
+ { CCI_REG8(0x3087), 0x53 },
+ { CCI_REG8(0x309d), 0x94 },
+ { CCI_REG8(0x30b1), 0x03 },
+ { CCI_REG8(0x30c6), 0x00 },
+ { CCI_REG8(0x30c7), 0x00 },
+ { CCI_REG8(0x3115), 0x0b },
+ { CCI_REG8(0x3118), 0x30 },
+ { CCI_REG8(0x311d), 0x25 },
+ { CCI_REG8(0x3121), 0x0a },
+ { CCI_REG8(0x3212), 0xf2 },
+ { CCI_REG8(0x3213), 0x0f },
+ { CCI_REG8(0x3215), 0x0f },
+ { CCI_REG8(0x3217), 0x0b },
+ { CCI_REG8(0x3219), 0x0b },
+ { CCI_REG8(0x321b), 0x0d },
+ { CCI_REG8(0x321d), 0x0d },
+ { CCI_REG8(0x32aa), 0x11 },
+ { CCI_REG8(0x3032), 0x40 },
+};
+
+static const struct cci_reg_sequence mode_820x614[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0034 },
+ { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cb },
+ { IMX111_IMAGE_WIDTH, 0x0334 }, { IMX111_IMAGE_HEIGHT, 0x0266 },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x05 }, { IMX111_H_ODD_INC, 0x03 },
+ { IMX111_W_EVEN_INC, 0x05 }, { IMX111_W_ODD_INC, 0x03 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 },
+ { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 },
+ { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 },
+ { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 },
+ { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 },
+ { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 },
+ { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 },
+ { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 },
+ { CCI_REG8(0x30b2), 0x03 }, { CCI_REG8(0x30d5), 0x09 },
+ { CCI_REG8(0x30d6), 0x00 }, { CCI_REG8(0x30d7), 0x00 },
+ { CCI_REG8(0x30d8), 0x00 }, { CCI_REG8(0x30d9), 0x00 },
+ { CCI_REG8(0x30de), 0x04 }, { CCI_REG8(0x30df), 0x20 },
+ { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 },
+ { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 },
+ { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 },
+ { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 },
+ { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c },
+ { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d },
+ { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x7a },
+ { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1308x736[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0154 }, { IMX111_VERTICAL_START, 0x0220 },
+ { IMX111_HORIZONTAL_END, 0x0b8b }, { IMX111_VERTICAL_END, 0x07df },
+ { IMX111_IMAGE_WIDTH, 0x051c }, { IMX111_IMAGE_HEIGHT, 0x02e0 },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 },
+ { CCI_REG8(0x3033), 0x84 }, { CCI_REG8(0x303d), 0x10 },
+ { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 },
+ { CCI_REG8(0x304c), 0xd7 }, { CCI_REG8(0x304d), 0x01 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 },
+ { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 },
+ { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 },
+ { CCI_REG8(0x309b), 0x48 }, { CCI_REG8(0x309c), 0x12 },
+ { CCI_REG8(0x309e), 0x04 }, { CCI_REG8(0x30a0), 0x14 },
+ { CCI_REG8(0x30a1), 0x0a }, { CCI_REG8(0x30aa), 0x01 },
+ { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x04 },
+ { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a },
+ { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 },
+ { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 },
+ { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 },
+ { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 },
+ { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 },
+ { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 },
+ { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x42 },
+ { CCI_REG8(0x315d), 0x41 }, { CCI_REG8(0x316e), 0x43 },
+ { CCI_REG8(0x316f), 0x42 }, { CCI_REG8(0x3318), 0x62 },
+ { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1640x924[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0164 },
+ { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x089b },
+ { IMX111_IMAGE_WIDTH, 0x0668 }, { IMX111_IMAGE_HEIGHT, 0x039c },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x03 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 },
+ { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 },
+ { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 },
+ { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 },
+ { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 },
+ { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 },
+ { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 },
+ { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 },
+ { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x09 },
+ { CCI_REG8(0x30d6), 0x01 }, { CCI_REG8(0x30d7), 0x01 },
+ { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 },
+ { CCI_REG8(0x30de), 0x02 }, { CCI_REG8(0x30df), 0x20 },
+ { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 },
+ { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 },
+ { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 },
+ { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 },
+ { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c },
+ { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d },
+ { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x72 },
+ { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1640x1232[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0030 },
+ { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cf },
+ { IMX111_IMAGE_WIDTH, 0x0668 }, { IMX111_IMAGE_HEIGHT, 0x04d0 },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x03 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x03 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 },
+ { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x01 },
+ { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 },
+ { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 },
+ { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 },
+ { CCI_REG8(0x309b), 0x28 }, { CCI_REG8(0x309c), 0x13 },
+ { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 },
+ { CCI_REG8(0x30a1), 0x09 }, { CCI_REG8(0x30aa), 0x03 },
+ { CCI_REG8(0x30b2), 0x05 }, { CCI_REG8(0x30d5), 0x09 },
+ { CCI_REG8(0x30d6), 0x01 }, { CCI_REG8(0x30d7), 0x01 },
+ { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 },
+ { CCI_REG8(0x30de), 0x02 }, { CCI_REG8(0x30df), 0x20 },
+ { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 },
+ { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 },
+ { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 },
+ { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 },
+ { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c },
+ { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d },
+ { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x72 },
+ { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_1952x1098[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0016 }, { IMX111_VERTICAL_START, 0x016e },
+ { IMX111_HORIZONTAL_END, 0x0ccb }, { IMX111_VERTICAL_END, 0x0893 },
+ { IMX111_IMAGE_WIDTH, 0x07a0 }, { IMX111_IMAGE_HEIGHT, 0x044a },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 },
+ { CCI_REG8(0x303e), 0x00 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x91 }, { CCI_REG8(0x3048), 0x00 },
+ { CCI_REG8(0x304c), 0x67 }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x10 }, { CCI_REG8(0x3073), 0xa0 },
+ { CCI_REG8(0x3074), 0x12 }, { CCI_REG8(0x3075), 0x12 },
+ { CCI_REG8(0x3076), 0x12 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x0a }, { CCI_REG8(0x307a), 0x0a },
+ { CCI_REG8(0x309b), 0x60 }, { CCI_REG8(0x309e), 0x04 },
+ { CCI_REG8(0x30a0), 0x15 }, { CCI_REG8(0x30a1), 0x08 },
+ { CCI_REG8(0x30aa), 0x03 }, { CCI_REG8(0x30b2), 0x05 },
+ { CCI_REG8(0x30d5), 0x20 }, { CCI_REG8(0x30d6), 0x85 },
+ { CCI_REG8(0x30d7), 0x2a }, { CCI_REG8(0x30d8), 0x64 },
+ { CCI_REG8(0x30d9), 0x89 }, { CCI_REG8(0x30de), 0x00 },
+ { CCI_REG8(0x30df), 0x21 }, { CCI_REG8(0x3102), 0x08 },
+ { CCI_REG8(0x3103), 0x1d }, { CCI_REG8(0x3104), 0x1e },
+ { CCI_REG8(0x3105), 0x00 }, { CCI_REG8(0x3106), 0x74 },
+ { CCI_REG8(0x3107), 0x00 }, { CCI_REG8(0x3108), 0x03 },
+ { CCI_REG8(0x3109), 0x02 }, { CCI_REG8(0x310a), 0x03 },
+ { CCI_REG8(0x315c), 0x37 }, { CCI_REG8(0x315d), 0x36 },
+ { CCI_REG8(0x316e), 0x38 }, { CCI_REG8(0x316f), 0x37 },
+ { CCI_REG8(0x3318), 0x63 }, { CCI_REG8(0x3348), 0xA0 },
+};
+
+static const struct cci_reg_sequence mode_2100x1200[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0256 }, { IMX111_VERTICAL_START, 0x02a8 },
+ { IMX111_HORIZONTAL_END, 0x0a89 }, { IMX111_VERTICAL_END, 0x0757 },
+ { IMX111_IMAGE_WIDTH, 0x0834 }, { IMX111_IMAGE_HEIGHT, 0x04b0 },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 },
+ { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 },
+ { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 },
+ { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 },
+ { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 },
+ { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 },
+ { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 },
+ { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 },
+ { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 },
+ { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a },
+ { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 },
+ { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 },
+ { CCI_REG8(0x3102), 0x08 }, { CCI_REG8(0x3103), 0x22 },
+ { CCI_REG8(0x3104), 0x20 }, { CCI_REG8(0x3105), 0x00 },
+ { CCI_REG8(0x3106), 0x87 }, { CCI_REG8(0x3107), 0x00 },
+ { CCI_REG8(0x3108), 0x03 }, { CCI_REG8(0x3109), 0x02 },
+ { CCI_REG8(0x310a), 0x03 }, { CCI_REG8(0x315c), 0x9c },
+ { CCI_REG8(0x315d), 0x9b }, { CCI_REG8(0x316e), 0x9d },
+ { CCI_REG8(0x316f), 0x9c }, { CCI_REG8(0x3318), 0x62 },
+ { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_3280x1098[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x01f6 },
+ { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x080b },
+ { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x044a },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x10 },
+ { CCI_REG8(0x303e), 0x40 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x93 }, { CCI_REG8(0x3048), 0x00 },
+ { CCI_REG8(0x304c), 0x67 }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0xe0 },
+ { CCI_REG8(0x3074), 0x12 }, { CCI_REG8(0x3075), 0x12 },
+ { CCI_REG8(0x3076), 0x12 }, { CCI_REG8(0x3077), 0x12 },
+ { CCI_REG8(0x3079), 0x2a }, { CCI_REG8(0x307a), 0x0a },
+ { CCI_REG8(0x309b), 0x60 }, { CCI_REG8(0x309e), 0x04 },
+ { CCI_REG8(0x30a0), 0x15 }, { CCI_REG8(0x30a1), 0x08 },
+ { CCI_REG8(0x30aa), 0x03 }, { CCI_REG8(0x30b2), 0x05 },
+ { CCI_REG8(0x30d5), 0x00 }, { CCI_REG8(0x30d6), 0x85 },
+ { CCI_REG8(0x30d7), 0x2a }, { CCI_REG8(0x30d8), 0x64 },
+ { CCI_REG8(0x30d9), 0x89 }, { CCI_REG8(0x30de), 0x00 },
+ { CCI_REG8(0x30df), 0x20 }, { CCI_REG8(0x3102), 0x08 },
+ { CCI_REG8(0x3103), 0x1d }, { CCI_REG8(0x3104), 0x1e },
+ { CCI_REG8(0x3105), 0x00 }, { CCI_REG8(0x3106), 0x74 },
+ { CCI_REG8(0x3107), 0x00 }, { CCI_REG8(0x3108), 0x03 },
+ { CCI_REG8(0x3109), 0x02 }, { CCI_REG8(0x310a), 0x03 },
+ { CCI_REG8(0x315c), 0x37 }, { CCI_REG8(0x315d), 0x36 },
+ { CCI_REG8(0x316e), 0x38 }, { CCI_REG8(0x316f), 0x37 },
+ { CCI_REG8(0x3318), 0x63 }, { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_3280x1848[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0164 },
+ { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x089b },
+ { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x0738 },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x00 },
+ { CCI_REG8(0x303e), 0x41 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 },
+ { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 },
+ { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 },
+ { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 },
+ { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 },
+ { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 },
+ { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 },
+ { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 },
+ { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a },
+ { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 },
+ { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 },
+ { CCI_REG8(0x3102), 0x10 }, { CCI_REG8(0x3103), 0x44 },
+ { CCI_REG8(0x3104), 0x40 }, { CCI_REG8(0x3105), 0x00 },
+ { CCI_REG8(0x3106), 0x0d }, { CCI_REG8(0x3107), 0x01 },
+ { CCI_REG8(0x3108), 0x09 }, { CCI_REG8(0x3109), 0x08 },
+ { CCI_REG8(0x310a), 0x0f }, { CCI_REG8(0x315c), 0x5d },
+ { CCI_REG8(0x315d), 0x5c }, { CCI_REG8(0x316e), 0x5e },
+ { CCI_REG8(0x316f), 0x5d }, { CCI_REG8(0x3318), 0x60 },
+ { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct cci_reg_sequence mode_3280x2464[] = {
+ { IMX111_GROUP_WRITE, 1 },
+ { IMX111_HORIZONTAL_START, 0x0008 }, { IMX111_VERTICAL_START, 0x0030 },
+ { IMX111_HORIZONTAL_END, 0x0cd7 }, { IMX111_VERTICAL_END, 0x09cf },
+ { IMX111_IMAGE_WIDTH, 0x0cd0 }, { IMX111_IMAGE_HEIGHT, 0x09a0 },
+ { IMX111_GROUP_WRITE, 0 },
+ { IMX111_H_EVEN_INC, 0x01 }, { IMX111_H_ODD_INC, 0x01 },
+ { IMX111_W_EVEN_INC, 0x01 }, { IMX111_W_ODD_INC, 0x01 },
+ { CCI_REG8(0x3033), 0x00 }, { CCI_REG8(0x303d), 0x00 },
+ { CCI_REG8(0x303e), 0x41 }, { CCI_REG8(0x3040), 0x08 },
+ { CCI_REG8(0x3041), 0x97 }, { CCI_REG8(0x3048), 0x00 },
+ { CCI_REG8(0x304c), 0x6f }, { CCI_REG8(0x304d), 0x03 },
+ { CCI_REG8(0x3064), 0x12 }, { CCI_REG8(0x3073), 0x00 },
+ { CCI_REG8(0x3074), 0x11 }, { CCI_REG8(0x3075), 0x11 },
+ { CCI_REG8(0x3076), 0x11 }, { CCI_REG8(0x3077), 0x11 },
+ { CCI_REG8(0x3079), 0x00 }, { CCI_REG8(0x307a), 0x00 },
+ { CCI_REG8(0x309b), 0x20 }, { CCI_REG8(0x309c), 0x13 },
+ { CCI_REG8(0x309e), 0x00 }, { CCI_REG8(0x30a0), 0x14 },
+ { CCI_REG8(0x30a1), 0x08 }, { CCI_REG8(0x30aa), 0x03 },
+ { CCI_REG8(0x30b2), 0x07 }, { CCI_REG8(0x30d5), 0x00 },
+ { CCI_REG8(0x30d6), 0x85 }, { CCI_REG8(0x30d7), 0x2a },
+ { CCI_REG8(0x30d8), 0x64 }, { CCI_REG8(0x30d9), 0x89 },
+ { CCI_REG8(0x30de), 0x00 }, { CCI_REG8(0x30df), 0x20 },
+ { CCI_REG8(0x3102), 0x10 }, { CCI_REG8(0x3103), 0x44 },
+ { CCI_REG8(0x3104), 0x40 }, { CCI_REG8(0x3105), 0x00 },
+ { CCI_REG8(0x3106), 0x0d }, { CCI_REG8(0x3107), 0x01 },
+ { CCI_REG8(0x3108), 0x09 }, { CCI_REG8(0x3109), 0x08 },
+ { CCI_REG8(0x310a), 0x0f }, { CCI_REG8(0x315c), 0x5d },
+ { CCI_REG8(0x315d), 0x5c }, { CCI_REG8(0x316e), 0x5e },
+ { CCI_REG8(0x316f), 0x5d }, { CCI_REG8(0x3318), 0x60 },
+ { CCI_REG8(0x3348), 0xe0 },
+};
+
+static const struct imx111_mode imx111_modes[] = {
+ [IMX111_MODE_3280x2464] = {
+ .width = 3280,
+ .height = 2464,
+ .vtl_def = 2490,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_3280x2464,
+ .num_of_regs = ARRAY_SIZE(mode_3280x2464),
+ },
+ },
+ [IMX111_MODE_3280x1848] = {
+ .width = 3280,
+ .height = 1848,
+ .vtl_def = 1874,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_3280x1848,
+ .num_of_regs = ARRAY_SIZE(mode_3280x1848),
+ },
+ },
+ [IMX111_MODE_3280x1098] = {
+ .width = 3280,
+ .height = 1098,
+ .vtl_def = 1130,
+ .htl_def = 3500,
+ .reg_list = {
+ .regs = mode_3280x1098,
+ .num_of_regs = ARRAY_SIZE(mode_3280x1098),
+ },
+ },
+ [IMX111_MODE_2100x1200] = {
+ .width = 2100,
+ .height = 1200,
+ .vtl_def = 1260,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_2100x1200,
+ .num_of_regs = ARRAY_SIZE(mode_2100x1200),
+ },
+ },
+ [IMX111_MODE_1952x1098] = {
+ .width = 1952,
+ .height = 1098,
+ .vtl_def = 1884,
+ .htl_def = 3500,
+ .reg_list = {
+ .regs = mode_1952x1098,
+ .num_of_regs = ARRAY_SIZE(mode_1952x1098),
+ },
+ },
+ [IMX111_MODE_1920x1080] = {
+ .width = 1920,
+ .height = 1080,
+ .vtl_def = 1884,
+ .htl_def = 3500,
+ .reg_list = {
+ .regs = mode_1952x1098,
+ .num_of_regs = ARRAY_SIZE(mode_1952x1098),
+ },
+ },
+ [IMX111_MODE_1640x1232] = {
+ .width = 1640,
+ .height = 1232,
+ .vtl_def = 1254,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_1640x1232,
+ .num_of_regs = ARRAY_SIZE(mode_1640x1232),
+ },
+ },
+ [IMX111_MODE_1440x1080] = {
+ .width = 1440,
+ .height = 1080,
+ .vtl_def = 1254,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_1640x1232,
+ .num_of_regs = ARRAY_SIZE(mode_1640x1232),
+ },
+ },
+ [IMX111_MODE_1640x924] = {
+ .width = 1640,
+ .height = 924,
+ .vtl_def = 946,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_1640x924,
+ .num_of_regs = ARRAY_SIZE(mode_1640x924),
+ },
+ },
+ [IMX111_MODE_1308x736] = {
+ .width = 1308,
+ .height = 736,
+ .vtl_def = 2369,
+ .htl_def = 1896,
+ .reg_list = {
+ .regs = mode_1308x736,
+ .num_of_regs = ARRAY_SIZE(mode_1308x736),
+ },
+ },
+ [IMX111_MODE_1280x720] = {
+ .width = 1280,
+ .height = 720,
+ .vtl_def = 2369,
+ .htl_def = 1896,
+ .reg_list = {
+ .regs = mode_1308x736,
+ .num_of_regs = ARRAY_SIZE(mode_1308x736),
+ },
+ },
+ [IMX111_MODE_820x614] = {
+ .width = 820,
+ .height = 614,
+ .vtl_def = 1260,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_820x614,
+ .num_of_regs = ARRAY_SIZE(mode_820x614),
+ },
+ },
+ [IMX111_MODE_640x480] = {
+ .width = 640,
+ .height = 480,
+ .vtl_def = 1260,
+ .htl_def = 3536,
+ .reg_list = {
+ .regs = mode_820x614,
+ .num_of_regs = ARRAY_SIZE(mode_820x614),
+ },
+ },
+};
+
+static inline struct imx111 *sd_to_imx111(struct v4l2_subdev *sd)
+{
+ return container_of_const(sd, struct imx111, sd);
+}
+
+static inline struct imx111 *ctrl_to_imx111(struct v4l2_ctrl *ctrl)
+{
+ return container_of_const(ctrl->handler, struct imx111, hdl);
+}
+
+static u8 to_settle_delay(u64 extclk_rate)
+{
+ u64 extclk_mhz = div_u64(extclk_rate, MEGA);
+
+ return DIV_ROUND_UP(IMX111_PLL_SETTLING_TIME_DEFAULT * extclk_mhz - 63,
+ 64);
+}
+
+static u32 imx111_get_format_code(struct imx111 *sensor, u32 code, bool test)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(imx111_mbus_formats); i++)
+ if (imx111_mbus_formats[i] == code)
+ break;
+
+ if (i >= ARRAY_SIZE(imx111_mbus_formats))
+ i = 0;
+
+ if (test)
+ return imx111_mbus_formats[i];
+
+ i = (i & ~3) | (sensor->vflip->val ? 2 : 0) |
+ (sensor->hflip->val ? 1 : 0);
+
+ return imx111_mbus_formats[i];
+}
+
+static u32 imx111_get_format_bpp(const struct v4l2_mbus_framefmt *format)
+{
+ switch (format->code) {
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ return 8;
+
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ default:
+ return 10;
+ }
+}
+
+static int imx111_update_digital_gain(struct imx111 *sensor, u32 val)
+{
+ int ret = 0;
+
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+
+ cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENR, val, &ret);
+ cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_RED, val, &ret);
+ cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_BLUE, val, &ret);
+ cci_write(sensor->regmap, IMX111_REG_DIG_GAIN_GREENB, val, &ret);
+
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+
+ return ret;
+}
+
+static int imx111_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct imx111 *sensor = ctrl_to_imx111(ctrl);
+ struct device *dev = regmap_get_device(sensor->regmap);
+ int ret = 0;
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ s64 max = sensor->cur_mode->height + ctrl->val -
+ IMX111_INTEGRATION_TIME_OFFSET;
+
+ ret = __v4l2_ctrl_modify_range(sensor->exposure,
+ sensor->exposure->minimum,
+ max, sensor->exposure->step,
+ max);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Applying V4L2 control value only happens
+ * when power is up for streaming
+ */
+ if (!pm_runtime_get_if_in_use(dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ cci_write(sensor->regmap, IMX111_REG_ANALOG_GAIN, ctrl->val,
+ &ret);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = imx111_update_digital_gain(sensor, ctrl->val);
+ break;
+ case V4L2_CID_EXPOSURE:
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_INTEGRATION_TIME, ctrl->val,
+ &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+ break;
+ case V4L2_CID_HBLANK:
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_HORIZONTAL_TOTAL_LENGTH,
+ sensor->cur_mode->width + ctrl->val, &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+ break;
+ case V4L2_CID_VBLANK:
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_VERTICAL_TOTAL_LENGTH,
+ sensor->cur_mode->height + ctrl->val, &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+ break;
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ cci_write(sensor->regmap, IMX111_IMAGE_ORIENTATION,
+ sensor->hflip->val | sensor->vflip->val << 1, &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ cci_write(sensor->regmap, IMX111_TEST_PATTERN, ctrl->val,
+ &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN_RED:
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_SOLID_COLOR_RED, ctrl->val,
+ &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN_GREENR:
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_SOLID_COLOR_GR, ctrl->val,
+ &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN_BLUE:
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_SOLID_COLOR_BLUE, ctrl->val,
+ &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN_GREENB:
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_SOLID_COLOR_GB, ctrl->val,
+ &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops imx111_ctrl_ops = {
+ .s_ctrl = imx111_set_ctrl,
+};
+
+static const char * const test_pattern_menu[] = {
+ "Disabled",
+ "Solid Color Fill",
+ "Standard Color Bars",
+ "Fade To Grey Color Bars",
+ "Pseudorandom data",
+};
+
+static int imx111_init_controls(struct imx111 *sensor)
+{
+ const struct v4l2_ctrl_ops *ops = &imx111_ctrl_ops;
+ struct device *dev = regmap_get_device(sensor->regmap);
+ const struct imx111_mode *mode = sensor->cur_mode;
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *hdl = &sensor->hdl;
+ s64 pixel_rate_min, pixel_rate_max;
+ int i, ret;
+
+ ret = v4l2_fwnode_device_parse(dev, &props);
+ if (ret < 0)
+ return ret;
+
+ v4l2_ctrl_handler_init(hdl, 15);
+
+ pixel_rate_min = div_u64(sensor->pixel_clk_raw,
+ 2 * IMX111_DATA_DEPTH_RAW10);
+ pixel_rate_max = div_u64(sensor->pixel_clk_raw,
+ 2 * IMX111_DATA_DEPTH_RAW8);
+ sensor->pixel_rate = v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE,
+ pixel_rate_min, pixel_rate_max,
+ 1,
+ div_u64(sensor->pixel_clk_raw,
+ 2 *
+ sensor->data_depth));
+
+ sensor->link_freq = v4l2_ctrl_new_int_menu(hdl, NULL,
+ V4L2_CID_LINK_FREQ, 0, 0,
+ &sensor->default_link_freq);
+ if (sensor->link_freq)
+ sensor->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_ANALOGUE_GAIN,
+ IMX111_ANA_GAIN_MIN, IMX111_ANA_GAIN_MAX,
+ IMX111_ANA_GAIN_STEP, IMX111_ANA_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DIGITAL_GAIN,
+ IMX111_DGTL_GAIN_MIN, IMX111_DGTL_GAIN_MAX,
+ IMX111_DGTL_GAIN_STEP, IMX111_DGTL_GAIN_DEFAULT);
+
+ sensor->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1,
+ 0);
+ if (sensor->hflip)
+ sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ sensor->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1,
+ 0);
+ if (sensor->vflip)
+ sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ sensor->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK,
+ IMX111_VBLANK_MIN,
+ IMX111_VTL_MAX - mode->height, 1,
+ mode->vtl_def - mode->height);
+ sensor->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK,
+ IMX111_HBLANK_MIN,
+ IMX111_HTL_MAX - mode->width, 1,
+ mode->htl_def - mode->width);
+
+ /*
+ * The maximum coarse integration time is the frame length in lines
+ * minus five.
+ */
+ sensor->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE,
+ IMX111_INTEGRATION_TIME_MIN,
+ IMX111_PIXEL_ARRAY_HEIGHT -
+ IMX111_INTEGRATION_TIME_OFFSET,
+ IMX111_INTEGRATION_TIME_STEP,
+ IMX111_PIXEL_ARRAY_HEIGHT -
+ IMX111_INTEGRATION_TIME_OFFSET);
+
+ v4l2_ctrl_new_fwnode_properties(hdl, ops, &props);
+
+ v4l2_ctrl_new_std_menu_items(hdl, ops, V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(test_pattern_menu) - 1, 0, 0,
+ test_pattern_menu);
+ for (i = 0; i < 4; i++) {
+ /*
+ * The assumption is that
+ * TEST_PATTERN_GREENR == TEST_PATTERN_RED + 1
+ * TEST_PATTERN_BLUE == TEST_PATTERN_RED + 2
+ * TEST_PATTERN_GREENB == TEST_PATTERN_RED + 3
+ */
+ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_TEST_PATTERN_RED + i,
+ IMX111_TESTP_COLOUR_MIN,
+ IMX111_TESTP_COLOUR_MAX,
+ IMX111_TESTP_COLOUR_STEP,
+ IMX111_TESTP_COLOUR_MAX);
+ /* The "Solid color" pattern is white by default */
+ }
+
+ if (hdl->error)
+ return hdl->error;
+
+ sensor->sd.ctrl_handler = hdl;
+
+ return 0;
+};
+
+static int imx111_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct imx111 *sensor = sd_to_imx111(sd);
+ struct device *dev = regmap_get_device(sensor->regmap);
+ const struct imx111_mode *mode = sensor->cur_mode;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ /* Apply default values of current mode */
+ ret = cci_multi_reg_write(sensor->regmap, mode->reg_list.regs,
+ mode->reg_list.num_of_regs, NULL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize the sensor\n");
+ goto err_rpm_put;
+ }
+
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON,
+ IMX111_GROUP_WRITE_ON, &ret);
+ cci_write(sensor->regmap, IMX111_DATA_DEPTH,
+ sensor->data_depth | sensor->data_depth << 8, &ret);
+ cci_update_bits(sensor->regmap, IMX111_GROUP_WRITE,
+ IMX111_GROUP_WRITE_ON, 0, &ret);
+
+ if (ret)
+ goto err_rpm_put;
+
+ ret = __v4l2_ctrl_handler_setup(&sensor->hdl);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = cci_write(sensor->regmap, IMX111_STREAMING_MODE,
+ IMX111_MODE_STREAMING, NULL);
+ if (ret)
+ dev_err(dev, "failed to start stream");
+
+ /* vflip and hflip cannot change during streaming */
+ __v4l2_ctrl_grab(sensor->vflip, true);
+ __v4l2_ctrl_grab(sensor->hflip, true);
+
+ msleep(30);
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int imx111_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct imx111 *sensor = sd_to_imx111(sd);
+ struct device *dev = regmap_get_device(sensor->regmap);
+ int ret;
+
+ ret = cci_write(sensor->regmap, IMX111_STREAMING_MODE,
+ IMX111_MODE_STANDBY, NULL);
+ if (ret)
+ dev_err(dev, "failed to stop stream\n");
+
+ __v4l2_ctrl_grab(sensor->vflip, false);
+ __v4l2_ctrl_grab(sensor->hflip, false);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+static int imx111_initialize(struct imx111 *sensor)
+{
+ struct device *dev = regmap_get_device(sensor->regmap);
+ int ret = 0;
+
+ /* Configure the PLL. */
+ cci_write(sensor->regmap, IMX111_PRE_PLL_CLK_DIVIDER_PLL1,
+ sensor->pll->pre_div, &ret);
+ cci_write(sensor->regmap, IMX111_PLL_MULTIPLIER_PLL1,
+ sensor->pll->mult, &ret);
+ cci_write(sensor->regmap, IMX111_POST_DIVIDER,
+ IMX111_POST_DIVIDER_DIV1, &ret);
+ cci_write(sensor->regmap, IMX111_PLL_SETTLING_TIME,
+ to_settle_delay(sensor->pll->extclk_rate), &ret);
+
+ cci_multi_reg_write(sensor->regmap, imx111_global_init,
+ ARRAY_SIZE(imx111_global_init), &ret);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize the sensor\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------
+ * IMX111 Pad Subdev Init and Operations
+ */
+static int imx111_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct imx111 *sensor = sd_to_imx111(sd);
+
+ if (code->index >= ARRAY_SIZE(imx111_mbus_formats) / 4)
+ return -EINVAL;
+
+ code->code = imx111_get_format_code(sensor,
+ imx111_mbus_formats[code->index *
+ 4], false);
+
+ return 0;
+}
+
+static int imx111_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct imx111 *sensor = sd_to_imx111(sd);
+ u32 code;
+
+ if (fse->index >= ARRAY_SIZE(imx111_modes))
+ return -EINVAL;
+
+ code = imx111_get_format_code(sensor, fse->code, true);
+ if (fse->code != code)
+ return -EINVAL;
+
+ fse->min_width = imx111_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = imx111_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int imx111_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct imx111 *sensor = sd_to_imx111(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt = &format->format;
+ struct v4l2_mbus_framefmt *fmt;
+ const struct imx111_mode *mode;
+
+ mode = v4l2_find_nearest_size(imx111_modes, ARRAY_SIZE(imx111_modes),
+ width, height,
+ mbus_fmt->width, mbus_fmt->height);
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ int ret;
+
+ sensor->cur_mode = mode;
+ sensor->data_depth = imx111_get_format_bpp(fmt);
+
+ ret = __v4l2_ctrl_s_ctrl_int64(sensor->pixel_rate,
+ div_u64(sensor->pixel_clk_raw,
+ 2 *
+ sensor->data_depth));
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_modify_range(sensor->vblank,
+ IMX111_VBLANK_MIN,
+ IMX111_VTL_MAX - mode->height,
+ 1,
+ mode->vtl_def - mode->height);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_s_ctrl(sensor->vblank, mode->vtl_def -
+ mode->height);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_modify_range(sensor->hblank,
+ IMX111_HBLANK_MIN,
+ IMX111_HTL_MAX - mode->width,
+ 1,
+ mode->htl_def - mode->width);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_s_ctrl(sensor->hblank, mode->htl_def -
+ mode->width);
+ if (ret)
+ return ret;
+ }
+
+ fmt->code = imx111_get_format_code(sensor, mbus_fmt->code, false);
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+
+ *mbus_fmt = *fmt;
+
+ return 0;
+}
+
+static int imx111_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct imx111 *sensor = sd_to_imx111(sd);
+ const struct imx111_mode *mode = sensor->cur_mode;
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, 0);
+
+ fmt->code = MEDIA_BUS_FMT_SGBRG10_1X10;
+ fmt->width = mode->width;
+ fmt->height = mode->height;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_video_ops imx111_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops imx111_pad_ops = {
+ .enum_mbus_code = imx111_enum_mbus_code,
+ .enum_frame_size = imx111_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = imx111_set_format,
+ .enable_streams = imx111_enable_streams,
+ .disable_streams = imx111_disable_streams,
+};
+
+static const struct v4l2_subdev_ops imx111_subdev_ops = {
+ .video = &imx111_video_ops,
+ .pad = &imx111_pad_ops,
+};
+
+static const struct media_entity_operations imx111_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_internal_ops imx111_internal_ops = {
+ .init_state = imx111_init_state,
+};
+
+static int imx111_init_subdev(struct imx111 *sensor, struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd = &sensor->sd;
+ struct media_pad *pad = &sensor->pad;
+ struct v4l2_ctrl_handler *hdl = &sensor->hdl;
+ int ret;
+
+ /* Initialize the subdev. */
+ v4l2_i2c_subdev_init(sd, client, &imx111_subdev_ops);
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &imx111_internal_ops;
+
+ /* Initialize the media entity. */
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ sd->entity.ops = &imx111_subdev_entity_ops;
+ pad->flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, 1, pad);
+ if (ret < 0) {
+ dev_err(dev, "failed to init entity pads: %d", ret);
+ return ret;
+ }
+
+ /* Initialize the control handler. */
+ ret = imx111_init_controls(sensor);
+ if (ret)
+ goto error;
+
+ return 0;
+error:
+ v4l2_ctrl_handler_free(hdl);
+ media_entity_cleanup(&sd->entity);
+ return ret;
+};
+
+/* ----------------------------------------------------------------------------
+ * Power Management
+ */
+
+static int imx111_power_on(struct imx111 *sensor)
+{
+ int ret;
+
+ if (sensor->reset)
+ gpiod_set_value(sensor->reset, 1);
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(imx111_supplies),
+ sensor->supplies);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(500, 600);
+
+ if (sensor->reset)
+ gpiod_set_value(sensor->reset, 0);
+
+ usleep_range(200, 250);
+
+ ret = clk_prepare_enable(sensor->extclk);
+ if (ret < 0)
+ goto error_regulator;
+
+ usleep_range(200, 250);
+
+ return 0;
+
+error_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(imx111_supplies), sensor->supplies);
+ return ret;
+}
+
+static void imx111_power_off(struct imx111 *sensor)
+{
+ if (sensor->reset)
+ gpiod_set_value(sensor->reset, 1);
+ usleep_range(1000, 2000);
+
+ clk_disable_unprepare(sensor->extclk);
+ regulator_bulk_disable(ARRAY_SIZE(imx111_supplies), sensor->supplies);
+}
+
+static int __maybe_unused imx111_pm_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx111 *sensor = sd_to_imx111(sd);
+ int ret;
+
+ ret = imx111_power_on(sensor);
+ if (ret)
+ return ret;
+
+ ret = imx111_initialize(sensor);
+ if (ret) {
+ imx111_power_off(sensor);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused imx111_pm_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct imx111 *sensor = sd_to_imx111(sd);
+
+ imx111_power_off(sensor);
+
+ return 0;
+}
+
+static const struct dev_pm_ops imx111_pm_ops = {
+ SET_RUNTIME_PM_OPS(imx111_pm_runtime_suspend,
+ imx111_pm_runtime_resume, NULL)
+};
+
+/* ----------------------------------------------------------------------------
+ * Probe & Remove
+ */
+
+static int imx111_identify_module(struct imx111 *sensor)
+{
+ struct device *dev = regmap_get_device(sensor->regmap);
+ u64 value, revision, manufacturer;
+ int ret = 0;
+
+ ret = cci_read(sensor->regmap, IMX111_PRODUCT_ID, &value, NULL);
+ if (ret)
+ return ret;
+
+ if (value != IMX111_CHIP_ID) {
+ dev_err(dev, "chip id mismatch: %x!=%04llx", IMX111_CHIP_ID,
+ value);
+ return -ENXIO;
+ }
+
+ cci_read(sensor->regmap, IMX111_REVISION, &revision, &ret);
+ cci_read(sensor->regmap, IMX111_MANUFACTURER_ID, &manufacturer, &ret);
+
+ dev_dbg(dev, "module IMX%03llx rev. %llu manufacturer %llu\n",
+ value, revision, manufacturer);
+
+ return ret;
+}
+
+static int imx111_clk_init(struct imx111 *sensor)
+{
+ struct device *dev = regmap_get_device(sensor->regmap);
+ u32 ndata_lanes = sensor->bus_cfg.bus.mipi_csi2.num_data_lanes;
+ u64 extclk_rate, system_clk;
+ unsigned int i;
+
+ extclk_rate = clk_get_rate(sensor->extclk);
+ if (!extclk_rate)
+ return dev_err_probe(dev, -EINVAL, "EXTCLK rate unknown\n");
+
+ for (i = 0; i < ARRAY_SIZE(imx111_pll); i++) {
+ if (clk_get_rate(sensor->extclk) ==
+ imx111_pll[i].extclk_rate) {
+ sensor->pll = &imx111_pll[i];
+ break;
+ }
+ }
+ if (!sensor->pll)
+ return dev_err_probe(dev, -EINVAL,
+ "Unsupported EXTCLK rate %llu\n",
+ extclk_rate);
+
+ system_clk = div_u64(extclk_rate, sensor->pll->pre_div) *
+ sensor->pll->mult;
+
+ /*
+ * Pixel clock or Logic clock is used for internal image processing is
+ * generated by dividing into 1/10 or 1/8 frequency according to the
+ * word length of the CSI2 interface. This clock is designating the
+ * pixel rate and used as the base of integration time, frame rate etc.
+ */
+ sensor->pixel_clk_raw = system_clk * ndata_lanes;
+
+ /*
+ * The CSI-2 bus is clocked for 16-bit per pixel, transmitted in DDR
+ * over n lanes for RAW10 default format.
+ */
+ sensor->default_link_freq = div_u64(sensor->pixel_clk_raw * 8,
+ 2 * IMX111_DATA_DEPTH_RAW10);
+
+ if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
+ sensor->bus_cfg.link_frequencies[0] != sensor->default_link_freq)
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid link-frequency, expected %llu\n",
+ sensor->default_link_freq);
+
+ return 0;
+}
+
+static int imx111_parse_dt(struct imx111 *sensor)
+{
+ struct device *dev = regmap_get_device(sensor->regmap);
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct fwnode_handle *ep;
+ int ret;
+
+ ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (!ep) {
+ dev_err(dev, "No endpoint found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &sensor->bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret < 0) {
+ dev_err(dev, "Failed to parse endpoint\n");
+ goto error;
+ }
+
+ sensor->bus_cfg.bus_type = V4L2_MBUS_CSI2_DPHY;
+
+ /* Check the number of MIPI CSI2 data lanes */
+ if (sensor->bus_cfg.bus.mipi_csi2.num_data_lanes > 2) {
+ dev_err(dev, "number of lanes is more than 2\n");
+ ret = -EINVAL;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
+ return ret;
+}
+
+static int imx111_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct imx111 *sensor;
+ int ret;
+
+ sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
+ return -ENOMEM;
+
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(sensor->regmap))
+ return dev_err_probe(dev, PTR_ERR(sensor->regmap),
+ "Failed to allocate register map\n");
+
+ sensor->extclk = devm_v4l2_sensor_clk_get(dev, NULL);
+ if (IS_ERR(sensor->extclk))
+ return dev_err_probe(dev, PTR_ERR(sensor->extclk),
+ "Failed to get clock\n");
+
+ sensor->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(sensor->reset))
+ return dev_err_probe(dev, PTR_ERR(sensor->reset),
+ "Failed to get reset GPIO\n");
+
+ ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(imx111_supplies),
+ imx111_supplies,
+ &sensor->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ret = imx111_parse_dt(sensor);
+ if (ret < 0)
+ return ret;
+
+ ret = imx111_clk_init(sensor);
+ if (ret < 0)
+ goto error_ep_free;
+
+ ret = imx111_power_on(sensor);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "Could not power on the device\n");
+ goto error_ep_free;
+ }
+
+ ret = imx111_identify_module(sensor);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "Could not identify module\n");
+ goto error_power_off;
+ }
+
+ sensor->cur_mode = &imx111_modes[IMX111_MODE_3280x2464];
+ sensor->data_depth = IMX111_DATA_DEPTH_RAW10;
+
+ ret = imx111_initialize(sensor);
+ if (ret < 0)
+ goto error_power_off;
+
+ ret = imx111_init_subdev(sensor, client);
+ if (ret < 0) {
+ dev_err(dev, "failed to init controls: %d", ret);
+ goto error_v4l2_ctrl_handler_free;
+ }
+
+ ret = v4l2_subdev_init_finalize(&sensor->sd);
+ if (ret)
+ goto error_v4l2_ctrl_handler_free;
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
+ if (ret < 0) {
+ dev_err(dev, "failed to register V4L2 subdev: %d", ret);
+ goto error_pm;
+ }
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+
+error_pm:
+ v4l2_subdev_cleanup(&sensor->sd);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+
+error_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&sensor->hdl);
+ media_entity_cleanup(&sensor->sd.entity);
+
+error_power_off:
+ imx111_power_off(sensor);
+
+error_ep_free:
+ v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
+
+ return ret;
+}
+
+static void imx111_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct imx111 *sensor = sd_to_imx111(sd);
+
+ v4l2_async_unregister_subdev(&sensor->sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sensor->sd.entity);
+ v4l2_ctrl_handler_free(&sensor->hdl);
+ v4l2_fwnode_endpoint_free(&sensor->bus_cfg);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ imx111_power_off(sensor);
+ pm_runtime_set_suspended(&client->dev);
+ }
+}
+
+static const struct of_device_id imx111_of_match[] = {
+ { .compatible = "sony,imx111" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx111_of_match);
+
+static struct i2c_driver imx111_i2c_driver = {
+ .driver = {
+ .name = "imx111",
+ .of_match_table = imx111_of_match,
+ .pm = &imx111_pm_ops,
+ },
+ .probe = imx111_probe,
+ .remove = imx111_remove,
+};
+module_i2c_driver(imx111_i2c_driver);
+
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("Sony IMX111 CMOS Image Sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index 94ebe625c9e6..d4945b192776 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -1014,8 +1014,6 @@ static int imx214_ctrls_init(struct imx214 *imx214)
V4L2_CID_LINK_FREQ,
imx214->bus_cfg.nr_of_link_frequencies - 1,
0, imx214->bus_cfg.link_frequencies);
- if (imx214->link_freq)
- imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
/*
* WARNING!
@@ -1038,9 +1036,6 @@ static int imx214_ctrls_init(struct imx214 *imx214)
imx214->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops,
V4L2_CID_HBLANK, hblank, hblank,
1, hblank);
- if (imx214->hblank)
- imx214->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-
exposure_max = mode->vts_def - IMX214_EXPOSURE_OFFSET;
exposure_def = min(exposure_max, IMX214_EXPOSURE_DEFAULT);
imx214->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops,
@@ -1060,13 +1055,9 @@ static int imx214_ctrls_init(struct imx214 *imx214)
imx214->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
- if (imx214->hflip)
- imx214->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
imx214->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx214_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
- if (imx214->vflip)
- imx214->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
v4l2_ctrl_cluster(2, &imx214->hflip);
@@ -1106,6 +1097,12 @@ static int imx214_ctrls_init(struct imx214 *imx214)
return ret;
}
+ /* Now that the controls have been properly created, set their flags. */
+ imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ imx214->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ imx214->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+ imx214->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
ret = imx214_pll_update(imx214);
if (ret < 0) {
v4l2_ctrl_handler_free(ctrl_hdlr);
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index c680aa6c3a55..bc55fe2a93b4 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -68,6 +68,7 @@
#define IMX219_EXPOSURE_STEP 1
#define IMX219_EXPOSURE_DEFAULT 0x640
#define IMX219_EXPOSURE_MAX 65535
+#define IMX219_EXPOSURE_OFFSET 4
/* V_TIMING internal */
#define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160)
@@ -409,24 +410,14 @@ static void imx219_get_binning(struct v4l2_subdev_state *state, u8 *bin_h,
u32 hbin = crop->width / format->width;
u32 vbin = crop->height / format->height;
- *bin_h = IMX219_BINNING_NONE;
- *bin_v = IMX219_BINNING_NONE;
-
- /*
- * Use analog binning only if both dimensions are binned, as it crops
- * the other dimension.
- */
if (hbin == 2 && vbin == 2) {
*bin_h = IMX219_BINNING_X2_ANALOG;
*bin_v = IMX219_BINNING_X2_ANALOG;
-
- return;
+ } else {
+ *bin_h = IMX219_BINNING_NONE;
+ *bin_v = IMX219_BINNING_NONE;
}
- if (hbin == 2)
- *bin_h = IMX219_BINNING_X2;
- if (vbin == 2)
- *bin_v = IMX219_BINNING_X2;
}
static inline u32 imx219_get_rate_factor(struct v4l2_subdev_state *state)
@@ -460,13 +451,17 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
int exposure_max, exposure_def;
/* Update max exposure while meeting expected vblanking */
- exposure_max = format->height + ctrl->val - 4;
+ exposure_max = format->height + ctrl->val - IMX219_EXPOSURE_OFFSET;
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
- exposure_max : IMX219_EXPOSURE_DEFAULT;
- __v4l2_ctrl_modify_range(imx219->exposure,
- imx219->exposure->minimum,
- exposure_max, imx219->exposure->step,
- exposure_def);
+ exposure_max : IMX219_EXPOSURE_DEFAULT;
+ ret = __v4l2_ctrl_modify_range(imx219->exposure,
+ imx219->exposure->minimum,
+ exposure_max,
+ imx219->exposure->step,
+ exposure_def);
+ if (ret)
+ return ret;
+
}
/*
@@ -585,9 +580,9 @@ static int imx219_init_controls(struct imx219 *imx219)
IMX219_LLP_MIN - mode->width,
IMX219_LLP_MAX - mode->width, 1,
IMX219_LLP_MIN - mode->width);
- exposure_max = mode->fll_def - 4;
+ exposure_max = mode->fll_def - IMX219_EXPOSURE_OFFSET;
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
- exposure_max : IMX219_EXPOSURE_DEFAULT;
+ exposure_max : IMX219_EXPOSURE_DEFAULT;
imx219->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
V4L2_CID_EXPOSURE,
IMX219_EXPOSURE_MIN, exposure_max,
@@ -856,8 +851,9 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
const struct imx219_mode *mode;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
- u8 bin_h, bin_v;
+ u8 bin_h, bin_v, binning;
u32 prev_line_len;
+ int ret;
format = v4l2_subdev_state_get_format(state, 0);
prev_line_len = format->width + imx219->hblank->val;
@@ -877,9 +873,12 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U);
bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U);
+ /* Ensure bin_h and bin_v are same to avoid 1:2 or 2:1 stretching */
+ binning = min(bin_h, bin_v);
+
crop = v4l2_subdev_state_get_crop(state, 0);
- crop->width = format->width * bin_h;
- crop->height = format->height * bin_v;
+ crop->width = format->width * binning;
+ crop->height = format->height * binning;
crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2;
crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2;
@@ -890,19 +889,28 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
int pixel_rate;
/* Update limits and set FPS to default */
- __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
- IMX219_FLL_MAX - mode->height, 1,
+ ret = __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
+ IMX219_FLL_MAX - mode->height, 1,
+ mode->fll_def - mode->height);
+ if (ret)
+ return ret;
+
+ ret = __v4l2_ctrl_s_ctrl(imx219->vblank,
mode->fll_def - mode->height);
- __v4l2_ctrl_s_ctrl(imx219->vblank,
- mode->fll_def - mode->height);
+ if (ret)
+ return ret;
+
/* Update max exposure while meeting expected vblanking */
- exposure_max = mode->fll_def - 4;
+ exposure_max = mode->fll_def - IMX219_EXPOSURE_OFFSET;
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
- exposure_max : IMX219_EXPOSURE_DEFAULT;
- __v4l2_ctrl_modify_range(imx219->exposure,
- imx219->exposure->minimum,
- exposure_max, imx219->exposure->step,
- exposure_def);
+ exposure_max : IMX219_EXPOSURE_DEFAULT;
+ ret = __v4l2_ctrl_modify_range(imx219->exposure,
+ imx219->exposure->minimum,
+ exposure_max,
+ imx219->exposure->step,
+ exposure_def);
+ if (ret)
+ return ret;
/*
* With analog binning the default minimum line length of 3448
@@ -913,9 +921,12 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
imx219_get_binning(state, &bin_h, &bin_v);
llp_min = (bin_h & bin_v) == IMX219_BINNING_X2_ANALOG ?
IMX219_BINNED_LLP_MIN : IMX219_LLP_MIN;
- __v4l2_ctrl_modify_range(imx219->hblank, llp_min - mode->width,
- IMX219_LLP_MAX - mode->width, 1,
- llp_min - mode->width);
+ ret = __v4l2_ctrl_modify_range(imx219->hblank,
+ llp_min - mode->width,
+ IMX219_LLP_MAX - mode->width, 1,
+ llp_min - mode->width);
+ if (ret)
+ return ret;
/*
* Retain PPL setting from previous mode so that the
* line time does not change on a mode change.
@@ -924,13 +935,17 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
* mode width subtracted.
*/
hblank = prev_line_len - mode->width;
- __v4l2_ctrl_s_ctrl(imx219->hblank, hblank);
+ ret = __v4l2_ctrl_s_ctrl(imx219->hblank, hblank);
+ if (ret)
+ return ret;
/* Scale the pixel rate based on the mode specific factor */
pixel_rate = imx219_get_pixel_rate(imx219) *
imx219_get_rate_factor(state);
- __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate,
- pixel_rate, 1, pixel_rate);
+ ret = __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate,
+ pixel_rate, 1, pixel_rate);
+ if (ret)
+ return ret;
}
return 0;
@@ -979,9 +994,7 @@ static int imx219_init_state(struct v4l2_subdev *sd,
},
};
- imx219_set_pad_format(sd, state, &fmt);
-
- return 0;
+ return imx219_set_pad_format(sd, state, &fmt);
}
static const struct v4l2_subdev_video_ops imx219_video_ops = {
diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c
index d86d08c29174..8ec78b60bea6 100644
--- a/drivers/media/i2c/imx274.c
+++ b/drivers/media/i2c/imx274.c
@@ -2034,8 +2034,7 @@ static int imx274_probe(struct i2c_client *client)
/* initialize regmap */
imx274->regmap = devm_regmap_init_i2c(client, &imx274_regmap_config);
if (IS_ERR(imx274->regmap)) {
- dev_err(dev,
- "regmap init failed: %ld\n", PTR_ERR(imx274->regmap));
+ dev_err(dev, "regmap init failed: %pe\n", imx274->regmap);
ret = -ENODEV;
goto err_regmap;
}
diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c
index c043df2f15fb..5790aa4fabeb 100644
--- a/drivers/media/i2c/imx335.c
+++ b/drivers/media/i2c/imx335.c
@@ -35,6 +35,7 @@
/* Lines per frame */
#define IMX335_REG_VMAX CCI_REG24_LE(0x3030)
+#define IMX335_REG_HMAX CCI_REG16_LE(0x3034)
#define IMX335_REG_OPB_SIZE_V CCI_REG8(0x304c)
#define IMX335_REG_ADBIT CCI_REG8(0x3050)
@@ -42,10 +43,13 @@
#define IMX335_REG_SHUTTER CCI_REG24_LE(0x3058)
#define IMX335_EXPOSURE_MIN 1
-#define IMX335_EXPOSURE_OFFSET 9
+#define IMX335_SHUTTER_MIN 9
+#define IMX335_SHUTTER_MIN_BINNED 17
#define IMX335_EXPOSURE_STEP 1
#define IMX335_EXPOSURE_DEFAULT 0x0648
+#define IMX335_REG_AREA2_WIDTH_1 CCI_REG16_LE(0x3072)
+
#define IMX335_REG_AREA3_ST_ADR_1 CCI_REG16_LE(0x3074)
#define IMX335_REG_AREA3_WIDTH_1 CCI_REG16_LE(0x3076)
@@ -56,6 +60,9 @@
#define IMX335_AGAIN_STEP 1
#define IMX335_AGAIN_DEFAULT 0
+/* Vertical flip */
+#define IMX335_REG_VREVERSE CCI_REG8(0x304f)
+
#define IMX335_REG_TPG_TESTCLKEN CCI_REG8(0x3148)
#define IMX335_REG_INCLKSEL1 CCI_REG16_LE(0x314c)
@@ -121,12 +128,19 @@
#define IMX335_NUM_DATA_LANES 4
/* IMX335 native and active pixel array size. */
-#define IMX335_NATIVE_WIDTH 2616U
-#define IMX335_NATIVE_HEIGHT 1964U
-#define IMX335_PIXEL_ARRAY_LEFT 12U
-#define IMX335_PIXEL_ARRAY_TOP 12U
-#define IMX335_PIXEL_ARRAY_WIDTH 2592U
-#define IMX335_PIXEL_ARRAY_HEIGHT 1944U
+static const struct v4l2_rect imx335_native_area = {
+ .top = 0,
+ .left = 0,
+ .width = 2696,
+ .height = 2044,
+};
+
+static const struct v4l2_rect imx335_active_area = {
+ .top = 50,
+ .left = 36,
+ .width = 2624,
+ .height = 1944,
+};
/**
* struct imx335_reg_list - imx335 sensor register list
@@ -144,8 +158,14 @@ static const char * const imx335_supply_name[] = {
"dvdd", /* Digital Core (1.2V) supply */
};
+enum imx335_scan_mode {
+ IMX335_ALL_PIXEL,
+ IMX335_2_2_BINNING,
+};
+
/**
* struct imx335_mode - imx335 sensor mode structure
+ * @scan_mode: Configuration scan mode (All pixel / 2x2Binning)
* @width: Frame width
* @height: Frame height
* @code: Format code
@@ -155,8 +175,11 @@ static const char * const imx335_supply_name[] = {
* @vblank_max: Maximum vertical blanking in lines
* @pclk: Sensor pixel clock
* @reg_list: Register list for sensor mode
+ * @vflip_normal: Register list vflip (normal readout)
+ * @vflip_inverted: Register list vflip (inverted readout)
*/
struct imx335_mode {
+ enum imx335_scan_mode scan_mode;
u32 width;
u32 height;
u32 code;
@@ -166,6 +189,8 @@ struct imx335_mode {
u32 vblank_max;
u64 pclk;
struct imx335_reg_list reg_list;
+ struct imx335_reg_list vflip_normal;
+ struct imx335_reg_list vflip_inverted;
};
/**
@@ -183,12 +208,12 @@ struct imx335_mode {
* @pclk_ctrl: Pointer to pixel clock control
* @hblank_ctrl: Pointer to horizontal blanking control
* @vblank_ctrl: Pointer to vertical blanking control
+ * @vflip: Pointer to vertical flip control
* @exp_ctrl: Pointer to exposure control
* @again_ctrl: Pointer to analog gain control
* @vblank: Vertical blanking in lines
* @lane_mode: Mode for number of connected data lanes
* @cur_mode: Pointer to current selected sensor mode
- * @mutex: Mutex for serializing sensor controls
* @link_freq_bitmap: Menu bitmap for link_freq_ctrl
* @cur_mbus_code: Currently selected media bus format code
*/
@@ -207,6 +232,7 @@ struct imx335 {
struct v4l2_ctrl *pclk_ctrl;
struct v4l2_ctrl *hblank_ctrl;
struct v4l2_ctrl *vblank_ctrl;
+ struct v4l2_ctrl *vflip;
struct {
struct v4l2_ctrl *exp_ctrl;
struct v4l2_ctrl *again_ctrl;
@@ -214,7 +240,6 @@ struct imx335 {
u32 vblank;
u32 lane_mode;
const struct imx335_mode *cur_mode;
- struct mutex mutex;
unsigned long link_freq_bitmap;
u32 cur_mbus_code;
};
@@ -252,17 +277,37 @@ static const int imx335_tpg_val[] = {
};
/* Sensor mode registers */
-static const struct cci_reg_sequence mode_2592x1940_regs[] = {
+static const struct cci_reg_sequence mode_2592x1944_regs[] = {
{ IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY },
{ IMX335_REG_MASTER_MODE, 0x00 },
{ IMX335_REG_WINMODE, 0x04 },
+ { IMX335_REG_HMAX, 550 },
{ IMX335_REG_HTRIMMING_START, 48 },
{ IMX335_REG_HNUM, 2592 },
{ IMX335_REG_Y_OUT_SIZE, 1944 },
- { IMX335_REG_AREA3_ST_ADR_1, 176 },
+ { IMX335_REG_AREA2_WIDTH_1, 40 },
{ IMX335_REG_AREA3_WIDTH_1, 3928 },
{ IMX335_REG_OPB_SIZE_V, 0 },
{ IMX335_REG_XVS_XHS_DRV, 0x00 },
+};
+
+static const struct cci_reg_sequence mode_1312x972_regs[] = {
+ { IMX335_REG_MODE_SELECT, IMX335_MODE_STANDBY },
+ { IMX335_REG_MASTER_MODE, 0x00 },
+ { IMX335_REG_WINMODE, 0x01 },
+ { IMX335_REG_HMAX, 275 },
+ { IMX335_REG_HTRIMMING_START, 48 },
+ { IMX335_REG_HNUM, 2600 },
+ { IMX335_REG_Y_OUT_SIZE, 972 },
+ { IMX335_REG_AREA2_WIDTH_1, 48 },
+ { IMX335_REG_AREA3_WIDTH_1, 3936 },
+ { IMX335_REG_OPB_SIZE_V, 0 },
+ { IMX335_REG_XVS_XHS_DRV, 0x00 },
+ { CCI_REG8(0x3300), 1 }, /* TCYCLE */
+ { CCI_REG8(0x3199), 0x30 }, /* HADD/VADD */
+};
+
+static const struct cci_reg_sequence imx335_common_regs[] = {
{ CCI_REG8(0x3288), 0x21 },
{ CCI_REG8(0x328a), 0x02 },
{ CCI_REG8(0x3414), 0x05 },
@@ -333,16 +378,92 @@ static const struct cci_reg_sequence mode_2592x1940_regs[] = {
{ CCI_REG8(0x3a00), 0x00 },
};
-static const struct cci_reg_sequence raw10_framefmt_regs[] = {
- { IMX335_REG_ADBIT, 0x00 },
- { IMX335_REG_MDBIT, 0x00 },
- { IMX335_REG_ADBIT1, 0x1ff },
+static const struct cci_reg_sequence mode_2592x1944_vflip_normal[] = {
+ { IMX335_REG_AREA3_ST_ADR_1, 176 },
+
+ /* Undocumented V-Flip related registers on Page 55 of datasheet. */
+ { CCI_REG8(0x3081), 0x02, },
+ { CCI_REG8(0x3083), 0x02, },
+ { CCI_REG16_LE(0x30b6), 0x00 },
+ { CCI_REG16_LE(0x3116), 0x08 },
};
-static const struct cci_reg_sequence raw12_framefmt_regs[] = {
- { IMX335_REG_ADBIT, 0x01 },
- { IMX335_REG_MDBIT, 0x01 },
- { IMX335_REG_ADBIT1, 0x47 },
+static const struct cci_reg_sequence mode_2592x1944_vflip_inverted[] = {
+ { IMX335_REG_AREA3_ST_ADR_1, 4112 },
+
+ /* Undocumented V-Flip related registers on Page 55 of datasheet. */
+ { CCI_REG8(0x3081), 0xfe, },
+ { CCI_REG8(0x3083), 0xfe, },
+ { CCI_REG16_LE(0x30b6), 0x1fa },
+ { CCI_REG16_LE(0x3116), 0x002 },
+};
+
+static const struct cci_reg_sequence mode_1312x972_vflip_normal[] = {
+ { IMX335_REG_AREA3_ST_ADR_1, 176 },
+
+ /* Undocumented */
+ { CCI_REG8(0x3078), 0x04 },
+ { CCI_REG8(0x3079), 0xfd },
+ { CCI_REG8(0x307a), 0x04 },
+ { CCI_REG8(0x307b), 0xfe },
+ { CCI_REG8(0x307c), 0x04 },
+ { CCI_REG8(0x307d), 0xfb },
+ { CCI_REG8(0x307e), 0x04 },
+ { CCI_REG8(0x307f), 0x02 },
+ { CCI_REG8(0x3080), 0x04 },
+ { CCI_REG8(0x3081), 0xfd },
+ { CCI_REG8(0x3082), 0x04 },
+ { CCI_REG8(0x3083), 0xfe },
+ { CCI_REG8(0x3084), 0x04 },
+ { CCI_REG8(0x3085), 0xfb },
+ { CCI_REG8(0x3086), 0x04 },
+ { CCI_REG8(0x3087), 0x02 },
+ { CCI_REG8(0x30a4), 0x77 },
+ { CCI_REG8(0x30a8), 0x20 },
+ { CCI_REG8(0x30a9), 0x00 },
+ { CCI_REG8(0x30ac), 0x08 },
+ { CCI_REG8(0x30ad), 0x08 },
+ { CCI_REG8(0x30b0), 0x20 },
+ { CCI_REG8(0x30b1), 0x00 },
+ { CCI_REG8(0x30b4), 0x10 },
+ { CCI_REG8(0x30b5), 0x10 },
+ { CCI_REG16_LE(0x30b6), 0x00 },
+ { CCI_REG16_LE(0x3112), 0x10 },
+ { CCI_REG16_LE(0x3116), 0x10 },
+};
+
+static const struct cci_reg_sequence mode_1312x972_vflip_inverted[] = {
+ { IMX335_REG_AREA3_ST_ADR_1, 4112 },
+
+ /* Undocumented */
+ { CCI_REG8(0x3078), 0x04 },
+ { CCI_REG8(0x3079), 0xfd },
+ { CCI_REG8(0x307a), 0x04 },
+ { CCI_REG8(0x307b), 0xfe },
+ { CCI_REG8(0x307c), 0x04 },
+ { CCI_REG8(0x307d), 0xfb },
+ { CCI_REG8(0x307e), 0x04 },
+ { CCI_REG8(0x307f), 0x02 },
+ { CCI_REG8(0x3080), 0xfc },
+ { CCI_REG8(0x3081), 0x05 },
+ { CCI_REG8(0x3082), 0xfc },
+ { CCI_REG8(0x3083), 0x02 },
+ { CCI_REG8(0x3084), 0xfc },
+ { CCI_REG8(0x3085), 0x03 },
+ { CCI_REG8(0x3086), 0xfc },
+ { CCI_REG8(0x3087), 0xfe },
+ { CCI_REG8(0x30a4), 0x77 },
+ { CCI_REG8(0x30a8), 0x20 },
+ { CCI_REG8(0x30a9), 0x00 },
+ { CCI_REG8(0x30ac), 0x08 },
+ { CCI_REG8(0x30ad), 0x78 },
+ { CCI_REG8(0x30b0), 0x20 },
+ { CCI_REG8(0x30b1), 0x00 },
+ { CCI_REG8(0x30b4), 0x10 },
+ { CCI_REG8(0x30b5), 0x70 },
+ { CCI_REG16_LE(0x30b6), 0x01f2 },
+ { CCI_REG16_LE(0x3112), 0x10 },
+ { CCI_REG16_LE(0x3116), 0x02 },
};
static const struct cci_reg_sequence mipi_data_rate_1188Mbps[] = {
@@ -407,17 +528,49 @@ static const u32 imx335_mbus_codes[] = {
};
/* Supported sensor mode configurations */
-static const struct imx335_mode supported_mode = {
- .width = 2592,
- .height = 1944,
- .hblank = 342,
- .vblank = 2556,
- .vblank_min = 2556,
- .vblank_max = 133060,
- .pclk = 396000000,
- .reg_list = {
- .num_of_regs = ARRAY_SIZE(mode_2592x1940_regs),
- .regs = mode_2592x1940_regs,
+static const struct imx335_mode supported_modes[] = {
+ {
+ .scan_mode = IMX335_ALL_PIXEL,
+ .width = 2592,
+ .height = 1944,
+ .hblank = 342,
+ .vblank = 2556,
+ .vblank_min = 2556,
+ .vblank_max = 133060,
+ .pclk = 396000000,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs),
+ .regs = mode_2592x1944_regs,
+ },
+ .vflip_normal = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_normal),
+ .regs = mode_2592x1944_vflip_normal,
+ },
+ .vflip_inverted = {
+ .num_of_regs = ARRAY_SIZE(mode_2592x1944_vflip_inverted),
+ .regs = mode_2592x1944_vflip_inverted,
+ }
+ }, {
+ .scan_mode = IMX335_2_2_BINNING,
+ .width = 1312,
+ .height = 972,
+ .hblank = 155,
+ .vblank = 3528,
+ .vblank_min = 3528,
+ .vblank_max = 133060,
+ .pclk = 396000000,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1312x972_regs),
+ .regs = mode_1312x972_regs,
+ },
+ .vflip_normal = {
+ .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_normal),
+ .regs = mode_1312x972_vflip_normal,
+ },
+ .vflip_inverted = {
+ .num_of_regs = ARRAY_SIZE(mode_1312x972_vflip_inverted),
+ .regs = mode_1312x972_vflip_inverted,
+ },
},
};
@@ -449,7 +602,8 @@ static int imx335_update_controls(struct imx335 *imx335,
if (ret)
return ret;
- ret = __v4l2_ctrl_s_ctrl(imx335->hblank_ctrl, mode->hblank);
+ ret = __v4l2_ctrl_modify_range(imx335->hblank_ctrl, mode->hblank,
+ mode->hblank, 1, mode->hblank);
if (ret)
return ret;
@@ -492,6 +646,19 @@ static int imx335_update_exp_gain(struct imx335 *imx335, u32 exposure, u32 gain)
return ret;
}
+static int imx335_update_vertical_flip(struct imx335 *imx335, u32 vflip)
+{
+ const struct imx335_reg_list * const vflip_regs =
+ vflip ? &imx335->cur_mode->vflip_inverted :
+ &imx335->cur_mode->vflip_normal;
+ int ret = 0;
+
+ cci_multi_reg_write(imx335->cci, vflip_regs->regs,
+ vflip_regs->num_of_regs, &ret);
+
+ return cci_write(imx335->cci, IMX335_REG_VREVERSE, vflip, &ret);
+}
+
static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index)
{
int ret = 0;
@@ -553,18 +720,22 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl)
/* Propagate change of current control to all related controls */
if (ctrl->id == V4L2_CID_VBLANK) {
+ u32 shutter_min = IMX335_SHUTTER_MIN;
+ u32 lpfr;
+
imx335->vblank = imx335->vblank_ctrl->val;
+ lpfr = imx335->vblank + imx335->cur_mode->height;
dev_dbg(imx335->dev, "Received vblank %u, new lpfr %u\n",
- imx335->vblank,
- imx335->vblank + imx335->cur_mode->height);
+ imx335->vblank, lpfr);
+
+ if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING)
+ shutter_min = IMX335_SHUTTER_MIN_BINNED;
ret = __v4l2_ctrl_modify_range(imx335->exp_ctrl,
IMX335_EXPOSURE_MIN,
- imx335->vblank +
- imx335->cur_mode->height -
- IMX335_EXPOSURE_OFFSET,
- 1, IMX335_EXPOSURE_DEFAULT);
+ lpfr - shutter_min, 1,
+ IMX335_EXPOSURE_DEFAULT);
if (ret)
return ret;
}
@@ -594,6 +765,10 @@ static int imx335_set_ctrl(struct v4l2_ctrl *ctrl)
ret = imx335_update_exp_gain(imx335, exposure, analog_gain);
break;
+ case V4L2_CID_VFLIP:
+ ret = imx335_update_vertical_flip(imx335, ctrl->val);
+
+ break;
case V4L2_CID_TEST_PATTERN:
ret = imx335_update_test_pattern(imx335, ctrl->val);
@@ -660,17 +835,16 @@ static int imx335_enum_frame_size(struct v4l2_subdev *sd,
struct imx335 *imx335 = to_imx335(sd);
u32 code;
- /* Only a single supported_mode available. */
- if (fsize->index > 0)
+ if (fsize->index >= ARRAY_SIZE(supported_modes))
return -EINVAL;
code = imx335_get_format_code(imx335, fsize->code);
if (fsize->code != code)
return -EINVAL;
- fsize->min_width = supported_mode.width;
+ fsize->min_width = supported_modes[fsize->index].width;
fsize->max_width = fsize->min_width;
- fsize->min_height = supported_mode.height;
+ fsize->min_height = supported_modes[fsize->index].height;
fsize->max_height = fsize->min_height;
return 0;
@@ -698,36 +872,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335,
}
/**
- * imx335_get_pad_format() - Get subdevice pad format
- * @sd: pointer to imx335 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int imx335_get_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct imx335 *imx335 = to_imx335(sd);
-
- mutex_lock(&imx335->mutex);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- struct v4l2_mbus_framefmt *framefmt;
-
- framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
- fmt->format = *framefmt;
- } else {
- imx335_fill_pad_format(imx335, imx335->cur_mode, fmt);
- }
-
- mutex_unlock(&imx335->mutex);
-
- return 0;
-}
-
-/**
* imx335_set_pad_format() - Set subdevice pad format
* @sd: pointer to imx335 V4L2 sub-device structure
* @sd_state: V4L2 sub-device configuration
@@ -740,12 +884,16 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_format *fmt)
{
struct imx335 *imx335 = to_imx335(sd);
+ struct v4l2_mbus_framefmt *format;
const struct imx335_mode *mode;
+ struct v4l2_rect *crop;
int i, ret = 0;
- mutex_lock(&imx335->mutex);
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
- mode = &supported_mode;
for (i = 0; i < ARRAY_SIZE(imx335_mbus_codes); i++) {
if (imx335_mbus_codes[i] == fmt->format.code)
imx335->cur_mbus_code = imx335_mbus_codes[i];
@@ -753,19 +901,25 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd,
imx335_fill_pad_format(imx335, mode, fmt);
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
- struct v4l2_mbus_framefmt *framefmt;
+ format = v4l2_subdev_state_get_format(sd_state, fmt->pad);
+ *format = fmt->format;
- framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
- *framefmt = fmt->format;
- } else {
+ crop = v4l2_subdev_state_get_crop(sd_state, fmt->pad);
+ crop->width = fmt->format.width;
+ crop->height = fmt->format.height;
+ if (mode->scan_mode == IMX335_2_2_BINNING) {
+ crop->width *= 2;
+ crop->height *= 2;
+ }
+ crop->left = (imx335_native_area.width - crop->width) / 2;
+ crop->top = (imx335_native_area.height - crop->height) / 2;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
ret = imx335_update_controls(imx335, mode);
if (!ret)
imx335->cur_mode = mode;
}
- mutex_unlock(&imx335->mutex);
-
return ret;
}
@@ -783,14 +937,12 @@ static int imx335_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_format fmt = { 0 };
fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
- imx335_fill_pad_format(imx335, &supported_mode, &fmt);
+ imx335_fill_pad_format(imx335, &supported_modes[0], &fmt);
- mutex_lock(&imx335->mutex);
__v4l2_ctrl_modify_range(imx335->link_freq_ctrl, 0,
__fls(imx335->link_freq_bitmap),
~(imx335->link_freq_bitmap),
__ffs(imx335->link_freq_bitmap));
- mutex_unlock(&imx335->mutex);
return imx335_set_pad_format(sd, sd_state, &fmt);
}
@@ -808,22 +960,18 @@ static int imx335_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_selection *sel)
{
switch (sel->target) {
- case V4L2_SEL_TGT_NATIVE_SIZE:
- sel->r.top = 0;
- sel->r.left = 0;
- sel->r.width = IMX335_NATIVE_WIDTH;
- sel->r.height = IMX335_NATIVE_HEIGHT;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(sd_state, 0);
return 0;
- case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r = imx335_native_area;
+ return 0;
+
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.top = IMX335_PIXEL_ARRAY_TOP;
- sel->r.left = IMX335_PIXEL_ARRAY_LEFT;
- sel->r.width = IMX335_PIXEL_ARRAY_WIDTH;
- sel->r.height = IMX335_PIXEL_ARRAY_HEIGHT;
-
+ sel->r = imx335_active_area;
return 0;
}
@@ -832,39 +980,65 @@ static int imx335_get_selection(struct v4l2_subdev *sd,
static int imx335_set_framefmt(struct imx335 *imx335)
{
- switch (imx335->cur_mbus_code) {
- case MEDIA_BUS_FMT_SRGGB10_1X10:
- return cci_multi_reg_write(imx335->cci, raw10_framefmt_regs,
- ARRAY_SIZE(raw10_framefmt_regs),
- NULL);
-
- case MEDIA_BUS_FMT_SRGGB12_1X12:
- return cci_multi_reg_write(imx335->cci, raw12_framefmt_regs,
- ARRAY_SIZE(raw12_framefmt_regs),
- NULL);
+ /*
+ * In the all-pixel scan mode the AD conversion shall match the output
+ * bit width requested.
+ *
+ * However, when 2/2 binning is enabled, the AD conversion is always
+ * 10-bit, so we ensure ADBIT is clear and ADBIT1 is assigned 0x1ff.
+ * That's as much as the documentation gives us...
+ */
+ int ret = 0;
+ u8 bpp = imx335->cur_mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10 ? 10 : 12;
+ u8 ad_conv = bpp;
+
+ /* Start with the output mode */
+ cci_write(imx335->cci, IMX335_REG_MDBIT, bpp == 12, &ret);
+
+ /* Enforce 10 bit AD on binning modes */
+ if (imx335->cur_mode->scan_mode == IMX335_2_2_BINNING)
+ ad_conv = 10;
+
+ /* AD Conversion configuration */
+ if (ad_conv == 10) {
+ cci_write(imx335->cci, IMX335_REG_ADBIT, 0x00, &ret);
+ cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x1ff, &ret);
+ } else { /* 12 bit AD Conversion */
+ cci_write(imx335->cci, IMX335_REG_ADBIT, 0x01, &ret);
+ cci_write(imx335->cci, IMX335_REG_ADBIT1, 0x47, &ret);
}
- return -EINVAL;
+ return ret;
}
/**
- * imx335_start_streaming() - Start sensor stream
- * @imx335: pointer to imx335 device
+ * imx335_enable_streams() - Enable sensor streams
+ * @sd: V4L2 subdevice
+ * @state: V4L2 subdevice state
+ * @pad: The pad to enable
+ * @streams_mask: Bitmask of streams to enable
*
* Return: 0 if successful, error code otherwise.
*/
-static int imx335_start_streaming(struct imx335 *imx335)
+static int imx335_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
+ struct imx335 *imx335 = to_imx335(sd);
const struct imx335_reg_list *reg_list;
int ret;
+ ret = pm_runtime_resume_and_get(imx335->dev);
+ if (ret < 0)
+ return ret;
+
/* Setup PLL */
reg_list = &link_freq_reglist[__ffs(imx335->link_freq_bitmap)];
ret = cci_multi_reg_write(imx335->cci, reg_list->regs,
reg_list->num_of_regs, NULL);
if (ret) {
dev_err(imx335->dev, "%s failed to set plls\n", __func__);
- return ret;
+ goto err_rpm_put;
}
/* Write sensor mode registers */
@@ -873,27 +1047,35 @@ static int imx335_start_streaming(struct imx335 *imx335)
reg_list->num_of_regs, NULL);
if (ret) {
dev_err(imx335->dev, "fail to write initial registers\n");
- return ret;
+ goto err_rpm_put;
+ }
+
+ /* Write sensor common registers */
+ ret = cci_multi_reg_write(imx335->cci, imx335_common_regs,
+ ARRAY_SIZE(imx335_common_regs), NULL);
+ if (ret) {
+ dev_err(imx335->dev, "fail to write initial registers\n");
+ goto err_rpm_put;
}
ret = imx335_set_framefmt(imx335);
if (ret) {
dev_err(imx335->dev, "%s failed to set frame format: %d\n",
__func__, ret);
- return ret;
+ goto err_rpm_put;
}
/* Configure lanes */
ret = cci_write(imx335->cci, IMX335_REG_LANEMODE,
imx335->lane_mode, NULL);
if (ret)
- return ret;
+ goto err_rpm_put;
/* Setup handler will write actual exposure and gain */
ret = __v4l2_ctrl_handler_setup(imx335->sd.ctrl_handler);
if (ret) {
dev_err(imx335->dev, "fail to setup handler\n");
- return ret;
+ goto err_rpm_put;
}
/* Start streaming */
@@ -901,62 +1083,39 @@ static int imx335_start_streaming(struct imx335 *imx335)
IMX335_MODE_STREAMING, NULL);
if (ret) {
dev_err(imx335->dev, "fail to start streaming\n");
- return ret;
+ goto err_rpm_put;
}
/* Initial regulator stabilization period */
usleep_range(18000, 20000);
return 0;
-}
-/**
- * imx335_stop_streaming() - Stop sensor stream
- * @imx335: pointer to imx335 device
- *
- * Return: 0 if successful, error code otherwise.
- */
-static int imx335_stop_streaming(struct imx335 *imx335)
-{
- return cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
- IMX335_MODE_STANDBY, NULL);
+err_rpm_put:
+ pm_runtime_put(imx335->dev);
+
+ return ret;
}
/**
- * imx335_set_stream() - Enable sensor streaming
- * @sd: pointer to imx335 subdevice
- * @enable: set to enable sensor streaming
+ * imx335_disable_streams() - Disable sensor streams
+ * @sd: V4L2 subdevice
+ * @state: V4L2 subdevice state
+ * @pad: The pad to disable
+ * @streams_mask: Bitmask of streams to disable
*
* Return: 0 if successful, error code otherwise.
*/
-static int imx335_set_stream(struct v4l2_subdev *sd, int enable)
+static int imx335_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
struct imx335 *imx335 = to_imx335(sd);
int ret;
- mutex_lock(&imx335->mutex);
-
- if (enable) {
- ret = pm_runtime_resume_and_get(imx335->dev);
- if (ret)
- goto error_unlock;
-
- ret = imx335_start_streaming(imx335);
- if (ret)
- goto error_power_off;
- } else {
- imx335_stop_streaming(imx335);
- pm_runtime_put(imx335->dev);
- }
-
- mutex_unlock(&imx335->mutex);
-
- return 0;
-
-error_power_off:
+ ret = cci_write(imx335->cci, IMX335_REG_MODE_SELECT,
+ IMX335_MODE_STANDBY, NULL);
pm_runtime_put(imx335->dev);
-error_unlock:
- mutex_unlock(&imx335->mutex);
return ret;
}
@@ -1009,8 +1168,8 @@ static int imx335_parse_hw_config(struct imx335 *imx335)
imx335->reset_gpio = devm_gpiod_get_optional(imx335->dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(imx335->reset_gpio)) {
- dev_err(imx335->dev, "failed to get reset gpio %ld\n",
- PTR_ERR(imx335->reset_gpio));
+ dev_err(imx335->dev, "failed to get reset gpio %pe\n",
+ imx335->reset_gpio);
return PTR_ERR(imx335->reset_gpio);
}
@@ -1076,7 +1235,7 @@ done_endpoint_free:
/* V4l2 subdevice ops */
static const struct v4l2_subdev_video_ops imx335_video_ops = {
- .s_stream = imx335_set_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops imx335_pad_ops = {
@@ -1084,8 +1243,10 @@ static const struct v4l2_subdev_pad_ops imx335_pad_ops = {
.enum_frame_size = imx335_enum_frame_size,
.get_selection = imx335_get_selection,
.set_selection = imx335_get_selection,
- .get_fmt = imx335_get_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = imx335_set_pad_format,
+ .enable_streams = imx335_enable_streams,
+ .disable_streams = imx335_disable_streams,
};
static const struct v4l2_subdev_ops imx335_subdev_ops = {
@@ -1167,7 +1328,7 @@ static int imx335_init_controls(struct imx335 *imx335)
struct v4l2_ctrl_handler *ctrl_hdlr = &imx335->ctrl_handler;
const struct imx335_mode *mode = imx335->cur_mode;
struct v4l2_fwnode_device_properties props;
- u32 lpfr;
+ u32 lpfr, shutter_min;
int ret;
ret = v4l2_fwnode_device_parse(imx335->dev, &props);
@@ -1175,20 +1336,20 @@ static int imx335_init_controls(struct imx335 *imx335)
return ret;
/* v4l2_fwnode_device_properties can add two more controls */
- ret = v4l2_ctrl_handler_init(ctrl_hdlr, 9);
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10);
if (ret)
return ret;
- /* Serialize controls with sensor device */
- ctrl_hdlr->lock = &imx335->mutex;
-
/* Initialize exposure and gain */
lpfr = mode->vblank + mode->height;
+ shutter_min = IMX335_SHUTTER_MIN;
+ if (mode->scan_mode == IMX335_2_2_BINNING)
+ shutter_min = IMX335_SHUTTER_MIN_BINNED;
imx335->exp_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
&imx335_ctrl_ops,
V4L2_CID_EXPOSURE,
IMX335_EXPOSURE_MIN,
- lpfr - IMX335_EXPOSURE_OFFSET,
+ lpfr - shutter_min,
IMX335_EXPOSURE_STEP,
IMX335_EXPOSURE_DEFAULT);
@@ -1210,6 +1371,13 @@ static int imx335_init_controls(struct imx335 *imx335)
v4l2_ctrl_cluster(2, &imx335->exp_ctrl);
+ imx335->vflip = v4l2_ctrl_new_std(ctrl_hdlr,
+ &imx335_ctrl_ops,
+ V4L2_CID_VFLIP,
+ 0, 1, 1, 0);
+ if (imx335->vflip)
+ imx335->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
imx335->vblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
&imx335_ctrl_ops,
V4L2_CID_VBLANK,
@@ -1294,12 +1462,10 @@ static int imx335_probe(struct i2c_client *client)
return ret;
}
- mutex_init(&imx335->mutex);
-
ret = imx335_power_on(imx335->dev);
if (ret) {
dev_err(imx335->dev, "failed to power-on the sensor\n");
- goto error_mutex_destroy;
+ return ret;
}
/* Check module identity */
@@ -1310,7 +1476,7 @@ static int imx335_probe(struct i2c_client *client)
}
/* Set default mode to max resolution */
- imx335->cur_mode = &supported_mode;
+ imx335->cur_mode = &supported_modes[0];
imx335->cur_mbus_code = imx335_mbus_codes[0];
imx335->vblank = imx335->cur_mode->vblank;
@@ -1332,11 +1498,18 @@ static int imx335_probe(struct i2c_client *client)
goto error_handler_free;
}
+ imx335->sd.state_lock = imx335->ctrl_handler.lock;
+ ret = v4l2_subdev_init_finalize(&imx335->sd);
+ if (ret < 0) {
+ dev_err(imx335->dev, "subdev init error\n");
+ goto error_media_entity;
+ }
+
ret = v4l2_async_register_subdev_sensor(&imx335->sd);
if (ret < 0) {
dev_err(imx335->dev,
"failed to register async subdev: %d\n", ret);
- goto error_media_entity;
+ goto error_subdev_cleanup;
}
pm_runtime_set_active(imx335->dev);
@@ -1345,14 +1518,14 @@ static int imx335_probe(struct i2c_client *client)
return 0;
+error_subdev_cleanup:
+ v4l2_subdev_cleanup(&imx335->sd);
error_media_entity:
media_entity_cleanup(&imx335->sd.entity);
error_handler_free:
v4l2_ctrl_handler_free(imx335->sd.ctrl_handler);
error_power_off:
imx335_power_off(imx335->dev);
-error_mutex_destroy:
- mutex_destroy(&imx335->mutex);
return ret;
}
@@ -1366,9 +1539,9 @@ error_mutex_destroy:
static void imx335_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct imx335 *imx335 = to_imx335(sd);
v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
@@ -1376,8 +1549,6 @@ static void imx335_remove(struct i2c_client *client)
if (!pm_runtime_status_suspended(&client->dev))
imx335_power_off(&client->dev);
pm_runtime_set_suspended(&client->dev);
-
- mutex_destroy(&imx335->mutex);
}
static const struct dev_pm_ops imx335_pm_ops = {
diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c
index 7bbd639a9ddf..b3826f803547 100644
--- a/drivers/media/i2c/imx412.c
+++ b/drivers/media/i2c/imx412.c
@@ -927,8 +927,8 @@ static int imx412_parse_hw_config(struct imx412 *imx412)
imx412->reset_gpio = devm_gpiod_get_optional(imx412->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(imx412->reset_gpio)) {
- dev_err(imx412->dev, "failed to get reset gpio %ld\n",
- PTR_ERR(imx412->reset_gpio));
+ dev_err(imx412->dev, "failed to get reset gpio %pe\n",
+ imx412->reset_gpio);
return PTR_ERR(imx412->reset_gpio);
}
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 7c0961688d61..e6e214f8294b 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -751,8 +751,8 @@ static int max9286_v4l2_notifier_register(struct max9286_priv *priv)
mas = v4l2_async_nf_add_fwnode(&priv->notifier, source->fwnode,
struct max9286_asd);
if (IS_ERR(mas)) {
- dev_err(dev, "Failed to add subdev for source %u: %ld",
- i, PTR_ERR(mas));
+ dev_err(dev, "Failed to add subdev for source %u: %pe",
+ i, mas);
v4l2_async_nf_cleanup(&priv->notifier);
return PTR_ERR(mas);
}
diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
index c8ae7890d9fa..72f021b1a7b9 100644
--- a/drivers/media/i2c/max96717.c
+++ b/drivers/media/i2c/max96717.c
@@ -650,7 +650,7 @@ static int max96717_v4l2_notifier_register(struct max96717_priv *priv)
fwnode_handle_put(ep_fwnode);
if (IS_ERR(asd)) {
- dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd));
+ dev_err(dev, "Failed to add subdev: %pe", asd);
v4l2_async_nf_cleanup(&priv->notifier);
return PTR_ERR(asd);
}
@@ -782,21 +782,23 @@ static unsigned int max96717_clk_find_best_index(struct max96717_priv *priv,
return idx;
}
-static long max96717_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int max96717_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct max96717_priv *priv = clk_hw_to_max96717(hw);
struct device *dev = &priv->client->dev;
unsigned int idx;
- idx = max96717_clk_find_best_index(priv, rate);
+ idx = max96717_clk_find_best_index(priv, req->rate);
- if (rate != max96717_predef_freqs[idx].freq) {
+ if (req->rate != max96717_predef_freqs[idx].freq) {
dev_warn(dev, "Request CLK freq:%lu, found CLK freq:%lu\n",
- rate, max96717_predef_freqs[idx].freq);
+ req->rate, max96717_predef_freqs[idx].freq);
}
- return max96717_predef_freqs[idx].freq;
+ req->rate = max96717_predef_freqs[idx].freq;
+
+ return 0;
}
static int max96717_clk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -847,7 +849,7 @@ static const struct clk_ops max96717_clk_ops = {
.unprepare = max96717_clk_unprepare,
.set_rate = max96717_clk_set_rate,
.recalc_rate = max96717_clk_recalc_rate,
- .round_rate = max96717_clk_round_rate,
+ .determine_rate = max96717_clk_determine_rate,
};
static int max96717_register_clkout(struct max96717_priv *priv)
diff --git a/drivers/media/i2c/msp3400-kthreads.c b/drivers/media/i2c/msp3400-kthreads.c
index ecabc0e1d32e..1d9f41dd7c21 100644
--- a/drivers/media/i2c/msp3400-kthreads.c
+++ b/drivers/media/i2c/msp3400-kthreads.c
@@ -596,6 +596,8 @@ restart:
"carrier2 val: %5d / %s\n", val, cd[i].name);
}
+ if (max1 < 0 || max1 > 3)
+ goto restart;
/* program the msp3400 according to the results */
state->main = msp3400c_carrier_detect_main[max1].cdo;
switch (max1) {
diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c
index 05dcf37c6f01..3532c7c38bec 100644
--- a/drivers/media/i2c/mt9m111.c
+++ b/drivers/media/i2c/mt9m111.c
@@ -1286,8 +1286,8 @@ static int mt9m111_probe(struct i2c_client *client)
mt9m111->regulator = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(mt9m111->regulator)) {
- dev_err(&client->dev, "regulator not found: %ld\n",
- PTR_ERR(mt9m111->regulator));
+ dev_err(&client->dev, "regulator not found: %pe\n",
+ mt9m111->regulator);
return PTR_ERR(mt9m111->regulator);
}
diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c
index b4f2703faa18..64a758c95ab7 100644
--- a/drivers/media/i2c/mt9v111.c
+++ b/drivers/media/i2c/mt9v111.c
@@ -1139,24 +1139,24 @@ static int mt9v111_probe(struct i2c_client *client)
mt9v111->oe = devm_gpiod_get_optional(&client->dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(mt9v111->oe)) {
- dev_err(&client->dev, "Unable to get GPIO \"enable\": %ld\n",
- PTR_ERR(mt9v111->oe));
+ dev_err(&client->dev, "Unable to get GPIO \"enable\": %pe\n",
+ mt9v111->oe);
return PTR_ERR(mt9v111->oe);
}
mt9v111->standby = devm_gpiod_get_optional(&client->dev, "standby",
GPIOD_OUT_HIGH);
if (IS_ERR(mt9v111->standby)) {
- dev_err(&client->dev, "Unable to get GPIO \"standby\": %ld\n",
- PTR_ERR(mt9v111->standby));
+ dev_err(&client->dev, "Unable to get GPIO \"standby\": %pe\n",
+ mt9v111->standby);
return PTR_ERR(mt9v111->standby);
}
mt9v111->reset = devm_gpiod_get_optional(&client->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(mt9v111->reset)) {
- dev_err(&client->dev, "Unable to get GPIO \"reset\": %ld\n",
- PTR_ERR(mt9v111->reset));
+ dev_err(&client->dev, "Unable to get GPIO \"reset\": %pe\n",
+ mt9v111->reset);
return PTR_ERR(mt9v111->reset);
}
diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c
index 8c4d85dc7922..b1e540eb8326 100644
--- a/drivers/media/i2c/ov02c10.c
+++ b/drivers/media/i2c/ov02c10.c
@@ -174,7 +174,7 @@ static const struct reg_sequence sensor_1928x1092_30fps_setting[] = {
{0x3816, 0x01},
{0x3817, 0x01},
- {0x3820, 0xb0},
+ {0x3820, 0xa0},
{0x3821, 0x00},
{0x3822, 0x80},
{0x3823, 0x08},
@@ -385,6 +385,8 @@ struct ov02c10 {
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
struct clk *img_clk;
struct gpio_desc *reset;
@@ -462,6 +464,16 @@ static int ov02c10_set_ctrl(struct v4l2_ctrl *ctrl)
ret = ov02c10_test_pattern(ov02c10, ctrl->val);
break;
+ case V4L2_CID_HFLIP:
+ cci_update_bits(ov02c10->regmap, OV02C10_ROTATE_CONTROL,
+ BIT(3), ov02c10->hflip->val << 3, &ret);
+ break;
+
+ case V4L2_CID_VFLIP:
+ cci_update_bits(ov02c10->regmap, OV02C10_ROTATE_CONTROL,
+ BIT(4), ov02c10->vflip->val << 4, &ret);
+ break;
+
default:
ret = -EINVAL;
break;
@@ -485,7 +497,7 @@ static int ov02c10_init_controls(struct ov02c10 *ov02c10)
s64 exposure_max, h_blank, pixel_rate;
int ret;
- v4l2_ctrl_handler_init(ctrl_hdlr, 10);
+ v4l2_ctrl_handler_init(ctrl_hdlr, 12);
ov02c10->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr,
&ov02c10_ctrl_ops,
@@ -536,6 +548,17 @@ static int ov02c10_init_controls(struct ov02c10 *ov02c10)
exposure_max,
OV02C10_EXPOSURE_STEP,
exposure_max);
+
+ ov02c10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (ov02c10->hflip)
+ ov02c10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ ov02c10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &ov02c10_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (ov02c10->vflip)
+ ov02c10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &ov02c10_ctrl_ops,
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov02c10_test_pattern_menu) - 1,
diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c
index 869bc78ed792..5421874732bc 100644
--- a/drivers/media/i2c/ov13b10.c
+++ b/drivers/media/i2c/ov13b10.c
@@ -1693,6 +1693,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(ov13b10_pm_ops, ov13b10_suspend,
static const struct acpi_device_id ov13b10_acpi_ids[] = {
{"OVTIDB10"},
{"OVTI13B1"},
+ {"OMNI13B1"}, /* ASUS ROG Flow Z13 (GZ302) uses this ACPI ID */
{ /* sentinel */ }
};
diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c
index 30e27d39ee44..ea26df328189 100644
--- a/drivers/media/i2c/ov5675.c
+++ b/drivers/media/i2c/ov5675.c
@@ -1184,8 +1184,8 @@ static int ov5675_get_hwcfg(struct ov5675 *ov5675)
ov5675->xvclk = devm_v4l2_sensor_clk_get(dev, NULL);
if (IS_ERR(ov5675->xvclk))
return dev_err_probe(dev, PTR_ERR(ov5675->xvclk),
- "failed to get xvclk: %ld\n",
- PTR_ERR(ov5675->xvclk));
+ "failed to get xvclk: %pe\n",
+ ov5675->xvclk);
xvclk_rate = clk_get_rate(ov5675->xvclk);
if (xvclk_rate != OV5675_XVCLK_19_2) {
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index d294477f9dd3..4cc796bbee92 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1292,8 +1292,8 @@ static int ov5693_probe(struct i2c_client *client)
ov5693->xvclk = devm_v4l2_sensor_clk_get(&client->dev, "xvclk");
if (IS_ERR(ov5693->xvclk))
return dev_err_probe(&client->dev, PTR_ERR(ov5693->xvclk),
- "failed to get xvclk: %ld\n",
- PTR_ERR(ov5693->xvclk));
+ "failed to get xvclk: %pe\n",
+ ov5693->xvclk);
xvclk_rate = clk_get_rate(ov5693->xvclk);
if (xvclk_rate != OV5693_XVCLK_FREQ)
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index a9f6176e9729..3e24d88f603c 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -1129,8 +1129,8 @@ static int ov9282_parse_hw_config(struct ov9282 *ov9282)
ov9282->reset_gpio = devm_gpiod_get_optional(ov9282->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(ov9282->reset_gpio)) {
- dev_err(ov9282->dev, "failed to get reset gpio %ld",
- PTR_ERR(ov9282->reset_gpio));
+ dev_err(ov9282->dev, "failed to get reset gpio %pe",
+ ov9282->reset_gpio);
return PTR_ERR(ov9282->reset_gpio);
}
diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c
index 6dfc91216851..e95342d706c3 100644
--- a/drivers/media/i2c/rj54n1cb0c.c
+++ b/drivers/media/i2c/rj54n1cb0c.c
@@ -1357,8 +1357,8 @@ static int rj54n1_probe(struct i2c_client *client)
rj54n1->pwup_gpio = gpiod_get_optional(&client->dev, "powerup",
GPIOD_OUT_LOW);
if (IS_ERR(rj54n1->pwup_gpio)) {
- dev_info(&client->dev, "Unable to get GPIO \"powerup\": %ld\n",
- PTR_ERR(rj54n1->pwup_gpio));
+ dev_info(&client->dev, "Unable to get GPIO \"powerup\": %pe\n",
+ rj54n1->pwup_gpio);
ret = PTR_ERR(rj54n1->pwup_gpio);
goto err_clk_put;
}
@@ -1366,8 +1366,8 @@ static int rj54n1_probe(struct i2c_client *client)
rj54n1->enable_gpio = gpiod_get_optional(&client->dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(rj54n1->enable_gpio)) {
- dev_info(&client->dev, "Unable to get GPIO \"enable\": %ld\n",
- PTR_ERR(rj54n1->enable_gpio));
+ dev_info(&client->dev, "Unable to get GPIO \"enable\": %pe\n",
+ rj54n1->enable_gpio);
ret = PTR_ERR(rj54n1->enable_gpio);
goto err_gpio_put;
}
diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c
index 41ae25b0911f..4675181af5fb 100644
--- a/drivers/media/i2c/st-mipid02.c
+++ b/drivers/media/i2c/st-mipid02.c
@@ -747,8 +747,8 @@ static int mipid02_parse_rx_ep(struct mipid02_dev *bridge)
of_node_put(ep_node);
if (IS_ERR(asd)) {
- dev_err(&client->dev, "fail to register asd to notifier %ld",
- PTR_ERR(asd));
+ dev_err(&client->dev, "fail to register asd to notifier %pe",
+ asd);
return PTR_ERR(asd);
}
bridge->notifier.ops = &mipid02_notifier_ops;
diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c
index bcfc274cf891..86d9ba3ea4e5 100644
--- a/drivers/media/i2c/tc358746.c
+++ b/drivers/media/i2c/tc358746.c
@@ -1222,14 +1222,16 @@ tc358746_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
return tc358746->pll_rate / (prediv * postdiv);
}
-static long tc358746_mclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int tc358746_mclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct tc358746 *tc358746 = clk_hw_to_tc358746(hw);
- *parent_rate = tc358746->pll_rate;
+ req->best_parent_rate = tc358746->pll_rate;
- return tc358746_find_mclk_settings(tc358746, rate);
+ req->rate = tc358746_find_mclk_settings(tc358746, req->rate);
+
+ return 0;
}
static int tc358746_mclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -1246,7 +1248,7 @@ static const struct clk_ops tc358746_mclk_ops = {
.enable = tc358746_mclk_enable,
.disable = tc358746_mclk_disable,
.recalc_rate = tc358746_recalc_rate,
- .round_rate = tc358746_mclk_round_rate,
+ .determine_rate = tc358746_mclk_determine_rate,
.set_rate = tc358746_mclk_set_rate,
};
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 1087d2bddaf2..3532766cd795 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -2797,7 +2797,6 @@ err_free_media:
err_free_handler:
v4l2_ctrl_handler_free(&state->hdl);
err_free_mutex:
- cancel_delayed_work(&state->delayed_work_enable_hpd);
mutex_destroy(&state->page_lock);
mutex_destroy(&state->lock);
tda1997x_set_power(state, 0);
diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c
index f09d6bf32641..78d18c028154 100644
--- a/drivers/media/i2c/vd55g1.c
+++ b/drivers/media/i2c/vd55g1.c
@@ -29,9 +29,11 @@
/* Register Map */
#define VD55G1_REG_MODEL_ID CCI_REG32_LE(0x0000)
-#define VD55G1_MODEL_ID 0x53354731
+#define VD55G1_MODEL_ID_VD55G1 0x53354731 /* Mono */
+#define VD55G1_MODEL_ID_VD65G4 0x53354733 /* RGB */
#define VD55G1_REG_REVISION CCI_REG16_LE(0x0004)
#define VD55G1_REVISION_CCB 0x2020
+#define VD55G1_REVISION_BAYER 0x3030
#define VD55G1_REG_FWPATCH_REVISION CCI_REG16_LE(0x0012)
#define VD55G1_REG_FWPATCH_START_ADDR CCI_REG8(0x2000)
#define VD55G1_REG_SYSTEM_FSM CCI_REG8(0x001c)
@@ -39,7 +41,8 @@
#define VD55G1_SYSTEM_FSM_SW_STBY 0x02
#define VD55G1_SYSTEM_FSM_STREAMING 0x03
#define VD55G1_REG_BOOT CCI_REG8(0x0200)
-#define VD55G1_BOOT_PATCH_SETUP 2
+#define VD55G1_BOOT_BOOT 1
+#define VD55G1_BOOT_PATCH_AND_BOOT 2
#define VD55G1_REG_STBY CCI_REG8(0x0201)
#define VD55G1_STBY_START_STREAM 1
#define VD55G1_REG_STREAMING CCI_REG8(0x0202)
@@ -132,7 +135,10 @@
#define VD55G1_MIPI_RATE_MIN (250 * MEGA)
#define VD55G1_MIPI_RATE_MAX (1200 * MEGA)
-static const u8 patch_array[] = {
+#define VD55G1_MODEL_ID_NAME(id) \
+ ((id) == VD55G1_MODEL_ID_VD55G1 ? "vd55g1" : "vd65g4")
+
+static const u8 vd55g1_patch_array[] = {
0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00,
0xf0, 0x01, 0x42, 0x00, 0xe6, 0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -466,22 +472,24 @@ struct vd55g1_mode {
u32 height;
};
-struct vd55g1_fmt_desc {
- u32 code;
- u8 bpp;
- u8 data_type;
+static const u32 vd55g1_mbus_formats_mono[] = {
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
};
-static const struct vd55g1_fmt_desc vd55g1_mbus_codes[] = {
+/* Format order is : no flip, hflip, vflip, both */
+static const u32 vd55g1_mbus_formats_bayer[][4] = {
{
- .code = MEDIA_BUS_FMT_Y8_1X8,
- .bpp = 8,
- .data_type = MIPI_CSI2_DT_RAW8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
},
{
- .code = MEDIA_BUS_FMT_Y10_1X10,
- .bpp = 10,
- .data_type = MIPI_CSI2_DT_RAW10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
},
};
@@ -524,6 +532,7 @@ struct vd55g1_vblank_limits {
struct vd55g1 {
struct device *dev;
+ unsigned int id;
struct v4l2_subdev sd;
struct media_pad pad;
struct regulator_bulk_data supplies[ARRAY_SIZE(vd55g1_supply_name)];
@@ -572,27 +581,78 @@ static inline struct vd55g1 *ctrl_to_vd55g1(struct v4l2_ctrl *ctrl)
return to_vd55g1(sd);
}
-static const struct vd55g1_fmt_desc *vd55g1_get_fmt_desc(struct vd55g1 *sensor,
- u32 code)
+static unsigned int vd55g1_get_fmt_bpp(u32 code)
{
- unsigned int i;
+ switch (code) {
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ default:
+ return 8;
+
+ case MEDIA_BUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ return 10;
+ }
+}
+
+static unsigned int vd55g1_get_fmt_data_type(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ default:
+ return MIPI_CSI2_DT_RAW8;
+
+ case MEDIA_BUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ return MIPI_CSI2_DT_RAW10;
+ }
+}
+
+static u32 vd55g1_get_fmt_code(struct vd55g1 *sensor, u32 code)
+{
+ unsigned int i, j;
- for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_codes); i++) {
- if (vd55g1_mbus_codes[i].code == code)
- return &vd55g1_mbus_codes[i];
+ if (sensor->id == VD55G1_MODEL_ID_VD55G1)
+ return code;
+
+ for (i = 0; i < ARRAY_SIZE(vd55g1_mbus_formats_bayer); i++) {
+ for (j = 0; j < ARRAY_SIZE(vd55g1_mbus_formats_bayer[i]); j++) {
+ if (vd55g1_mbus_formats_bayer[i][j] == code)
+ goto adapt_bayer_pattern;
+ }
}
+ dev_warn(sensor->dev, "Unsupported mbus format\n");
- /* Should never happen */
- dev_warn(sensor->dev, "Unsupported code %d. default to 8 bpp\n", code);
+ return code;
+
+adapt_bayer_pattern:
+ j = 0;
+ /* In first init_state() call, controls might not be initialized yet */
+ if (sensor->hflip_ctrl && sensor->vflip_ctrl) {
+ j = (sensor->hflip_ctrl->val ? 1 : 0) +
+ (sensor->vflip_ctrl->val ? 2 : 0);
+ }
- return &vd55g1_mbus_codes[0];
+ return vd55g1_mbus_formats_bayer[i][j];
}
static s32 vd55g1_get_pixel_rate(struct vd55g1 *sensor,
struct v4l2_mbus_framefmt *format)
{
- return sensor->mipi_rate /
- vd55g1_get_fmt_desc(sensor, format->code)->bpp;
+ return sensor->mipi_rate / vd55g1_get_fmt_bpp(format->code);
}
static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor,
@@ -605,7 +665,7 @@ static unsigned int vd55g1_get_hblank_min(struct vd55g1 *sensor,
/* MIPI required time */
mipi_req_line_time = (crop->width *
- vd55g1_get_fmt_desc(sensor, format->code)->bpp +
+ vd55g1_get_fmt_bpp(format->code) +
VD55G1_MIPI_MARGIN) /
(sensor->mipi_rate / MEGA);
mipi_req_line_length = mipi_req_line_time * sensor->pixel_clock /
@@ -887,7 +947,7 @@ static void vd55g1_update_pad_fmt(struct vd55g1 *sensor,
const struct vd55g1_mode *mode, u32 code,
struct v4l2_mbus_framefmt *fmt)
{
- fmt->code = code;
+ fmt->code = vd55g1_get_fmt_code(sensor, code);
fmt->width = mode->width;
fmt->height = mode->height;
fmt->colorspace = V4L2_COLORSPACE_RAW;
@@ -951,10 +1011,9 @@ static int vd55g1_set_framefmt(struct vd55g1 *sensor,
int ret = 0;
vd55g1_write(sensor, VD55G1_REG_FORMAT_CTRL,
- vd55g1_get_fmt_desc(sensor, format->code)->bpp, &ret);
+ vd55g1_get_fmt_bpp(format->code), &ret);
vd55g1_write(sensor, VD55G1_REG_OIF_IMG_CTRL,
- vd55g1_get_fmt_desc(sensor, format->code)->data_type,
- &ret);
+ vd55g1_get_fmt_data_type(format->code), &ret);
switch (crop->width / format->width) {
case 1:
@@ -1114,26 +1173,45 @@ static int vd55g1_patch(struct vd55g1 *sensor)
u64 patch;
int ret = 0;
- vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR,
- sizeof(patch_array), patch_array, &ret);
- vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_PATCH_SETUP, &ret);
- vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
- if (ret) {
- dev_err(sensor->dev, "Failed to apply patch\n");
- return ret;
- }
+ /* vd55g1 needs a patch while vd65g4 does not */
+ if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
+ vd55g1_write_array(sensor, VD55G1_REG_FWPATCH_START_ADDR,
+ sizeof(vd55g1_patch_array),
+ vd55g1_patch_array, &ret);
+ vd55g1_write(sensor, VD55G1_REG_BOOT,
+ VD55G1_BOOT_PATCH_AND_BOOT, &ret);
+ vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
+ if (ret) {
+ dev_err(sensor->dev, "Failed to apply patch\n");
+ return ret;
+ }
- vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret);
- if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) +
- VD55G1_FWPATCH_REVISION_MINOR) {
- dev_err(sensor->dev, "Bad patch version expected %d.%d got %d.%d\n",
- VD55G1_FWPATCH_REVISION_MAJOR,
- VD55G1_FWPATCH_REVISION_MINOR,
+ vd55g1_read(sensor, VD55G1_REG_FWPATCH_REVISION, &patch, &ret);
+ if (patch != (VD55G1_FWPATCH_REVISION_MAJOR << 8) +
+ VD55G1_FWPATCH_REVISION_MINOR) {
+ dev_err(sensor->dev, "Bad patch version expected %d.%d got %d.%d\n",
+ VD55G1_FWPATCH_REVISION_MAJOR,
+ VD55G1_FWPATCH_REVISION_MINOR,
+ (u8)(patch >> 8), (u8)(patch & 0xff));
+ return -ENODEV;
+ }
+ dev_dbg(sensor->dev, "patch %d.%d applied\n",
(u8)(patch >> 8), (u8)(patch & 0xff));
- return -ENODEV;
+
+ } else {
+ vd55g1_write(sensor, VD55G1_REG_BOOT, VD55G1_BOOT_BOOT, &ret);
+ vd55g1_poll_reg(sensor, VD55G1_REG_BOOT, 0, &ret);
+ if (ret) {
+ dev_err(sensor->dev, "Failed to boot\n");
+ return ret;
+ }
+ }
+
+ ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL);
+ if (ret) {
+ dev_err(sensor->dev, "Sensor waiting after boot failed\n");
+ return ret;
}
- dev_dbg(sensor->dev, "patch %d.%d applied\n",
- (u8)(patch >> 8), (u8)(patch & 0xff));
return 0;
}
@@ -1165,10 +1243,19 @@ static int vd55g1_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- if (code->index >= ARRAY_SIZE(vd55g1_mbus_codes))
- return -EINVAL;
+ struct vd55g1 *sensor = to_vd55g1(sd);
+ u32 base_code;
- code->code = vd55g1_mbus_codes[code->index].code;
+ if (sensor->id == VD55G1_MODEL_ID_VD55G1) {
+ if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_mono))
+ return -EINVAL;
+ base_code = vd55g1_mbus_formats_mono[code->index];
+ } else {
+ if (code->index >= ARRAY_SIZE(vd55g1_mbus_formats_bayer))
+ return -EINVAL;
+ base_code = vd55g1_mbus_formats_bayer[code->index][0];
+ }
+ code->code = vd55g1_get_fmt_code(sensor, base_code);
return 0;
}
@@ -1275,7 +1362,7 @@ static int vd55g1_init_state(struct v4l2_subdev *sd,
return ret;
vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF],
- vd55g1_mbus_codes[VD55G1_MBUS_CODE_DEF].code,
+ vd55g1_get_fmt_code(sensor, VD55G1_MBUS_CODE_DEF),
&fmt.format);
return vd55g1_set_pad_fmt(sd, sd_state, &fmt);
@@ -1285,9 +1372,16 @@ static int vd55g1_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
+ struct vd55g1 *sensor = to_vd55g1(sd);
+ u32 code;
+
if (fse->index >= ARRAY_SIZE(vd55g1_supported_modes))
return -EINVAL;
+ code = vd55g1_get_fmt_code(sensor, fse->code);
+ if (fse->code != code)
+ return -EINVAL;
+
fse->min_width = vd55g1_supported_modes[fse->index].width;
fse->max_width = fse->min_width;
fse->min_height = vd55g1_supported_modes[fse->index].height;
@@ -1463,8 +1557,12 @@ static int vd55g1_init_ctrls(struct vd55g1 *sensor)
/* Flip cluster */
sensor->hflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP,
0, 1, 1, 0);
+ if (sensor->hflip_ctrl)
+ sensor->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
sensor->vflip_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP,
0, 1, 1, 0);
+ if (sensor->vflip_ctrl)
+ sensor->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
v4l2_ctrl_cluster(2, &sensor->hflip_ctrl);
/* Exposition cluster */
@@ -1548,26 +1646,34 @@ unlock_state:
static int vd55g1_detect(struct vd55g1 *sensor)
{
- u64 device_rev;
- u64 id;
+ unsigned int dt_id = (uintptr_t)device_get_match_data(sensor->dev);
+ u64 rev, id;
int ret;
ret = vd55g1_read(sensor, VD55G1_REG_MODEL_ID, &id, NULL);
if (ret)
return ret;
- if (id != VD55G1_MODEL_ID) {
- dev_warn(sensor->dev, "Unsupported sensor id %x\n", (u32)id);
+ if (id != VD55G1_MODEL_ID_VD55G1 && id != VD55G1_MODEL_ID_VD65G4) {
+ dev_warn(sensor->dev, "Unsupported sensor id 0x%x\n",
+ (u32)id);
+ return -ENODEV;
+ }
+ if (id != dt_id) {
+ dev_err(sensor->dev, "Probed sensor %s and device tree definition (%s) mismatch",
+ VD55G1_MODEL_ID_NAME(id), VD55G1_MODEL_ID_NAME(dt_id));
return -ENODEV;
}
+ sensor->id = id;
- ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &device_rev, NULL);
+ ret = vd55g1_read(sensor, VD55G1_REG_REVISION, &rev, NULL);
if (ret)
return ret;
- if (device_rev != VD55G1_REVISION_CCB) {
- dev_err(sensor->dev, "Unsupported sensor revision (0x%x)\n",
- (u16)device_rev);
+ if ((id == VD55G1_MODEL_ID_VD55G1 && rev != VD55G1_REVISION_CCB) &&
+ (id == VD55G1_MODEL_ID_VD65G4 && rev != VD55G1_REVISION_BAYER)) {
+ dev_err(sensor->dev, "Unsupported sensor revision 0x%x for sensor %s\n",
+ (u16)rev, VD55G1_MODEL_ID_NAME(id));
return -ENODEV;
}
@@ -1616,13 +1722,6 @@ static int vd55g1_power_on(struct device *dev)
goto disable_clock;
}
- ret = vd55g1_wait_state(sensor, VD55G1_SYSTEM_FSM_SW_STBY, NULL);
- if (ret) {
- dev_err(dev, "Sensor waiting after patch failed %d\n",
- ret);
- goto disable_clock;
- }
-
return 0;
disable_clock:
@@ -1934,7 +2033,8 @@ static void vd55g1_remove(struct i2c_client *client)
}
static const struct of_device_id vd55g1_dt_ids[] = {
- { .compatible = "st,vd55g1" },
+ { .compatible = "st,vd55g1", .data = (void *)VD55G1_MODEL_ID_VD55G1 },
+ { .compatible = "st,vd65g4", .data = (void *)VD55G1_MODEL_ID_VD65G4 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vd55g1_dt_ids);
diff --git a/drivers/media/mc/mc-request.c b/drivers/media/mc/mc-request.c
index f66f728b1b43..2ac9ac0a740b 100644
--- a/drivers/media/mc/mc-request.c
+++ b/drivers/media/mc/mc-request.c
@@ -282,8 +282,6 @@ EXPORT_SYMBOL_GPL(media_request_get_by_fd);
int media_request_alloc(struct media_device *mdev, int *alloc_fd)
{
struct media_request *req;
- struct file *filp;
- int fd;
int ret;
/* Either both are NULL or both are non-NULL */
@@ -297,19 +295,6 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd)
if (!req)
return -ENOMEM;
- fd = get_unused_fd_flags(O_CLOEXEC);
- if (fd < 0) {
- ret = fd;
- goto err_free_req;
- }
-
- filp = anon_inode_getfile("request", &request_fops, NULL, O_CLOEXEC);
- if (IS_ERR(filp)) {
- ret = PTR_ERR(filp);
- goto err_put_fd;
- }
-
- filp->private_data = req;
req->mdev = mdev;
req->state = MEDIA_REQUEST_STATE_IDLE;
req->num_incomplete_objects = 0;
@@ -320,19 +305,24 @@ int media_request_alloc(struct media_device *mdev, int *alloc_fd)
req->updating_count = 0;
req->access_count = 0;
- *alloc_fd = fd;
+ FD_PREPARE(fdf, O_CLOEXEC,
+ anon_inode_getfile("request", &request_fops, NULL,
+ O_CLOEXEC));
+ if (fdf.err) {
+ ret = fdf.err;
+ goto err_free_req;
+ }
+
+ fd_prepare_file(fdf)->private_data = req;
+
+ *alloc_fd = fd_publish(fdf);
snprintf(req->debug_str, sizeof(req->debug_str), "%u:%d",
- atomic_inc_return(&mdev->request_id), fd);
+ atomic_inc_return(&mdev->request_id), *alloc_fd);
dev_dbg(mdev->dev, "request: allocated %s\n", req->debug_str);
- fd_install(fd, filp);
-
return 0;
-err_put_fd:
- put_unused_fd(fd);
-
err_free_req:
if (mdev->ops->req_free)
mdev->ops->req_free(req);
diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
index b62fd12c93c1..74c59a94b2b0 100644
--- a/drivers/media/pci/cx18/cx18-driver.c
+++ b/drivers/media/pci/cx18/cx18-driver.c
@@ -1136,11 +1136,8 @@ int cx18_init_on_first_open(struct cx18 *cx)
int video_input;
int fw_retry_count = 3;
struct v4l2_frequency vf;
- struct cx18_open_id fh;
v4l2_std_id std;
- fh.cx = cx;
-
if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
return -ENXIO;
@@ -1220,14 +1217,14 @@ int cx18_init_on_first_open(struct cx18 *cx)
video_input = cx->active_input;
cx->active_input++; /* Force update of input */
- cx18_s_input(NULL, &fh, video_input);
+ cx18_do_s_input(cx, video_input);
/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
in one place. */
cx->std++; /* Force full standard initialization */
std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
- cx18_s_std(NULL, &fh, std);
- cx18_s_frequency(NULL, &fh, &vf);
+ cx18_do_s_std(cx, std);
+ cx18_do_s_frequency(cx, &vf);
return 0;
}
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 0f3019739d03..0d676a57e24e 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -521,10 +521,8 @@ static int cx18_g_input(struct file *file, void *fh, unsigned int *i)
return 0;
}
-int cx18_s_input(struct file *file, void *fh, unsigned int inp)
+int cx18_do_s_input(struct cx18 *cx, unsigned int inp)
{
- struct cx18_open_id *id = file2id(file);
- struct cx18 *cx = id->cx;
v4l2_std_id std = V4L2_STD_ALL;
const struct cx18_card_video_input *card_input =
cx->card->video_inputs + inp;
@@ -558,6 +556,11 @@ int cx18_s_input(struct file *file, void *fh, unsigned int inp)
return 0;
}
+static int cx18_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ return cx18_do_s_input(file2id(file)->cx, inp);
+}
+
static int cx18_g_frequency(struct file *file, void *fh,
struct v4l2_frequency *vf)
{
@@ -570,11 +573,8 @@ static int cx18_g_frequency(struct file *file, void *fh,
return 0;
}
-int cx18_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
+int cx18_do_s_frequency(struct cx18 *cx, const struct v4l2_frequency *vf)
{
- struct cx18_open_id *id = file2id(file);
- struct cx18 *cx = id->cx;
-
if (vf->tuner != 0)
return -EINVAL;
@@ -585,6 +585,12 @@ int cx18_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *v
return 0;
}
+static int cx18_s_frequency(struct file *file, void *fh,
+ const struct v4l2_frequency *vf)
+{
+ return cx18_do_s_frequency(file2id(file)->cx, vf);
+}
+
static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
{
struct cx18 *cx = file2id(file)->cx;
@@ -593,11 +599,8 @@ static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std)
return 0;
}
-int cx18_s_std(struct file *file, void *fh, v4l2_std_id std)
+int cx18_do_s_std(struct cx18 *cx, v4l2_std_id std)
{
- struct cx18_open_id *id = file2id(file);
- struct cx18 *cx = id->cx;
-
if ((std & V4L2_STD_ALL) == 0)
return -EINVAL;
@@ -642,6 +645,11 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id std)
return 0;
}
+static int cx18_s_std(struct file *file, void *fh, v4l2_std_id std)
+{
+ return cx18_do_s_std(file2id(file)->cx, std);
+}
+
static int cx18_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *vt)
{
struct cx18_open_id *id = file2id(file);
diff --git a/drivers/media/pci/cx18/cx18-ioctl.h b/drivers/media/pci/cx18/cx18-ioctl.h
index 97cd9f99e22d..42a8acd69735 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.h
+++ b/drivers/media/pci/cx18/cx18-ioctl.h
@@ -12,6 +12,8 @@ u16 cx18_service2vbi(int type);
void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt);
void cx18_set_funcs(struct video_device *vdev);
-int cx18_s_std(struct file *file, void *fh, v4l2_std_id std);
-int cx18_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf);
-int cx18_s_input(struct file *file, void *fh, unsigned int inp);
+
+struct cx18;
+int cx18_do_s_std(struct cx18 *cx, v4l2_std_id std);
+int cx18_do_s_frequency(struct cx18 *cx, const struct v4l2_frequency *vf);
+int cx18_do_s_input(struct cx18 *cx, unsigned int inp);
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 4e579352ab2c..c9827e9fe6ff 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -79,6 +79,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI02C1", 1, 400000000),
/* Omnivision OV02E10 */
IPU_SENSOR_CONFIG("OVTI02E1", 1, 360000000),
+ /* Omnivision ov05c10 */
+ IPU_SENSOR_CONFIG("OVTI05C1", 1, 480000000),
/* Omnivision OV08A10 */
IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000),
/* Omnivision OV08x40 */
@@ -90,6 +92,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
+ /* Sony IMX471 */
+ IPU_SENSOR_CONFIG("SONY471A", 1, 200000000),
/* Toshiba T4KA3 */
IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000),
};
@@ -563,8 +567,8 @@ static void ipu_bridge_instantiate_vcm_work(struct work_struct *work)
vcm_client = i2c_acpi_new_device_by_fwnode(acpi_fwnode_handle(adev),
1, &data->board_info);
if (IS_ERR(vcm_client)) {
- dev_err(data->sensor, "Error instantiating VCM client: %ld\n",
- PTR_ERR(vcm_client));
+ dev_err(data->sensor, "Error instantiating VCM client: %pe\n",
+ vcm_client);
goto out_pm_put;
}
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index a87f105beb5e..986b9afd7cb5 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -314,8 +314,8 @@ static int cio2_csi2_calc_timing(struct cio2_device *cio2, struct cio2_queue *q,
src_pad = media_entity_remote_source_pad_unique(&q->subdev.entity);
if (IS_ERR(src_pad)) {
- dev_err(dev, "can't get source pad of %s (%ld)\n",
- q->subdev.name, PTR_ERR(src_pad));
+ dev_err(dev, "can't get source pad of %s (%pe)\n",
+ q->subdev.name, src_pad);
return PTR_ERR(src_pad);
}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
index d1fece6210ab..43a2a16a3c2a 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
@@ -42,6 +42,10 @@ static const u32 csi2_supported_codes[] = {
MEDIA_BUS_FMT_SGBRG8_1X8,
MEDIA_BUS_FMT_SGRBG8_1X8,
MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y12_1X12,
+ MEDIA_BUS_FMT_Y16_1X16,
MEDIA_BUS_FMT_META_8,
MEDIA_BUS_FMT_META_10,
MEDIA_BUS_FMT_META_12,
@@ -87,8 +91,8 @@ s64 ipu6_isys_csi2_get_link_freq(struct ipu6_isys_csi2 *csi2)
src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
if (IS_ERR(src_pad)) {
dev_err(&csi2->isys->adev->auxdev.dev,
- "can't get source pad of %s (%ld)\n",
- csi2->asd.sd.name, PTR_ERR(src_pad));
+ "can't get source pad of %s (%pe)\n",
+ csi2->asd.sd.name, src_pad);
return PTR_ERR(src_pad);
}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
index 463a0adf9e13..869e7d4ba572 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
@@ -25,24 +25,28 @@ unsigned int ipu6_isys_mbus_code_to_bpp(u32 code)
case MEDIA_BUS_FMT_RGB565_1X16:
case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_Y16_1X16:
case MEDIA_BUS_FMT_META_16:
return 16;
case MEDIA_BUS_FMT_SBGGR12_1X12:
case MEDIA_BUS_FMT_SGBRG12_1X12:
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
+ case MEDIA_BUS_FMT_Y12_1X12:
case MEDIA_BUS_FMT_META_12:
return 12;
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_Y10_1X10:
case MEDIA_BUS_FMT_META_10:
return 10;
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SGBRG8_1X8:
case MEDIA_BUS_FMT_SGRBG8_1X8:
case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
case MEDIA_BUS_FMT_META_8:
return 8;
default:
@@ -65,21 +69,25 @@ unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)
case MEDIA_BUS_FMT_SGBRG16_1X16:
case MEDIA_BUS_FMT_SGRBG16_1X16:
case MEDIA_BUS_FMT_SRGGB16_1X16:
+ case MEDIA_BUS_FMT_Y16_1X16:
return MIPI_CSI2_DT_RAW16;
case MEDIA_BUS_FMT_SBGGR12_1X12:
case MEDIA_BUS_FMT_SGBRG12_1X12:
case MEDIA_BUS_FMT_SGRBG12_1X12:
case MEDIA_BUS_FMT_SRGGB12_1X12:
+ case MEDIA_BUS_FMT_Y12_1X12:
return MIPI_CSI2_DT_RAW12;
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_Y10_1X10:
return MIPI_CSI2_DT_RAW10;
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SGBRG8_1X8:
case MEDIA_BUS_FMT_SGRBG8_1X8:
case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_Y8_1X8:
return MIPI_CSI2_DT_RAW8;
case MEDIA_BUS_FMT_META_8:
case MEDIA_BUS_FMT_META_10:
@@ -96,15 +104,23 @@ unsigned int ipu6_isys_mbus_code_to_mipi(u32 code)
bool ipu6_isys_is_bayer_format(u32 code)
{
- switch (ipu6_isys_mbus_code_to_mipi(code)) {
- case MIPI_CSI2_DT_RAW8:
- case MIPI_CSI2_DT_RAW10:
- case MIPI_CSI2_DT_RAW12:
- case MIPI_CSI2_DT_RAW14:
- case MIPI_CSI2_DT_RAW16:
- case MIPI_CSI2_DT_RAW20:
- case MIPI_CSI2_DT_RAW24:
- case MIPI_CSI2_DT_RAW28:
+ switch (code) {
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ case MEDIA_BUS_FMT_SRGGB16_1X16:
+ case MEDIA_BUS_FMT_SGRBG16_1X16:
+ case MEDIA_BUS_FMT_SGBRG16_1X16:
+ case MEDIA_BUS_FMT_SBGGR16_1X16:
return true;
default:
return false;
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
index f3f3bc0615e5..dec8f5ffcfa5 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
@@ -77,6 +77,20 @@ const struct ipu6_isys_pixelformat ipu6_isys_pfmts[] = {
IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
{ V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
+
+ { V4L2_PIX_FMT_GREY, 8, 8, MEDIA_BUS_FMT_Y8_1X8,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW8 },
+ { V4L2_PIX_FMT_Y10, 16, 10, MEDIA_BUS_FMT_Y10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_Y12, 16, 12, MEDIA_BUS_FMT_Y12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_Y16, 16, 16, MEDIA_BUS_FMT_Y16_1X16,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW16 },
+ { V4L2_PIX_FMT_Y10P, 10, 10, MEDIA_BUS_FMT_Y10_1X10,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW10 },
+ { V4L2_PIX_FMT_Y12P, 12, 12, MEDIA_BUS_FMT_Y12_1X12,
+ IPU6_FW_ISYS_FRAME_FORMAT_RAW12 },
+
{ V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
IPU6_FW_ISYS_FRAME_FORMAT_UYVY},
{ V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c
index 98310b8511b1..b306a320b70f 100644
--- a/drivers/media/pci/intel/ivsc/mei_ace.c
+++ b/drivers/media/pci/intel/ivsc/mei_ace.c
@@ -414,10 +414,11 @@ static int mei_ace_setup_dev_link(struct mei_ace *ace)
/* setup link between mei_ace and mei_csi */
ace->csi_link = device_link_add(csi_dev, dev, DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE | DL_FLAG_STATELESS);
+ put_device(csi_dev);
if (!ace->csi_link) {
ret = -EINVAL;
dev_err(dev, "failed to link to %s\n", dev_name(csi_dev));
- goto err_put;
+ goto err;
}
ace->csi_dev = csi_dev;
@@ -522,7 +523,6 @@ static void mei_ace_remove(struct mei_cl_device *cldev)
cancel_work_sync(&ace->work);
device_link_del(ace->csi_link);
- put_device(ace->csi_dev);
pm_runtime_disable(&cldev->dev);
pm_runtime_set_suspended(&cldev->dev);
diff --git a/drivers/media/pci/ivtv/ivtv-driver.c b/drivers/media/pci/ivtv/ivtv-driver.c
index 72a8f76a41f4..459eb6cc370c 100644
--- a/drivers/media/pci/ivtv/ivtv-driver.c
+++ b/drivers/media/pci/ivtv/ivtv-driver.c
@@ -1247,15 +1247,12 @@ err:
int ivtv_init_on_first_open(struct ivtv *itv)
{
- struct v4l2_frequency vf;
/* Needed to call ioctls later */
- struct ivtv_open_id fh;
+ struct ivtv_stream *s = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+ struct v4l2_frequency vf;
int fw_retry_count = 3;
int video_input;
- fh.itv = itv;
- fh.type = IVTV_ENC_STREAM_TYPE_MPG;
-
if (test_bit(IVTV_F_I_FAILED, &itv->i_flags))
return -ENXIO;
@@ -1297,13 +1294,13 @@ int ivtv_init_on_first_open(struct ivtv *itv)
video_input = itv->active_input;
itv->active_input++; /* Force update of input */
- ivtv_s_input(NULL, &fh, video_input);
+ ivtv_do_s_input(itv, video_input);
/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
in one place. */
itv->std++; /* Force full standard initialization */
itv->std_out = itv->std;
- ivtv_s_frequency(NULL, &fh, &vf);
+ ivtv_do_s_frequency(s, &vf);
if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) {
/* Turn on the TV-out: ivtv_init_mpeg_decoder() initializes
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index 84c73bd22f2d..8d5ea3aec06f 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -974,9 +974,8 @@ static int ivtv_g_input(struct file *file, void *fh, unsigned int *i)
return 0;
}
-int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
+int ivtv_do_s_input(struct ivtv *itv, unsigned int inp)
{
- struct ivtv *itv = file2id(file)->itv;
v4l2_std_id std;
int i;
@@ -1017,6 +1016,11 @@ int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
return 0;
}
+static int ivtv_s_input(struct file *file, void *fh, unsigned int inp)
+{
+ return ivtv_do_s_input(file2id(file)->itv, inp);
+}
+
static int ivtv_g_output(struct file *file, void *fh, unsigned int *i)
{
struct ivtv *itv = file2id(file)->itv;
@@ -1065,10 +1069,9 @@ static int ivtv_g_frequency(struct file *file, void *fh, struct v4l2_frequency *
return 0;
}
-int ivtv_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf)
+int ivtv_do_s_frequency(struct ivtv_stream *s, const struct v4l2_frequency *vf)
{
- struct ivtv *itv = file2id(file)->itv;
- struct ivtv_stream *s = &itv->streams[file2id(file)->type];
+ struct ivtv *itv = s->itv;
if (s->vdev.vfl_dir)
return -ENOTTY;
@@ -1082,6 +1085,15 @@ int ivtv_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *v
return 0;
}
+static int ivtv_s_frequency(struct file *file, void *fh,
+ const struct v4l2_frequency *vf)
+{
+ struct ivtv_open_id *id = file2id(file);
+ struct ivtv *itv = id->itv;
+
+ return ivtv_do_s_frequency(&itv->streams[id->type], vf);
+}
+
static int ivtv_g_std(struct file *file, void *fh, v4l2_std_id *std)
{
struct ivtv *itv = file2id(file)->itv;
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.h b/drivers/media/pci/ivtv/ivtv-ioctl.h
index 7f8c6f43d397..96ca7e2ef973 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.h
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.h
@@ -9,6 +9,8 @@
#ifndef IVTV_IOCTL_H
#define IVTV_IOCTL_H
+struct ivtv;
+
u16 ivtv_service2vbi(int type);
void ivtv_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
u16 ivtv_get_service_set(struct v4l2_sliced_vbi_format *fmt);
@@ -17,7 +19,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed);
void ivtv_set_funcs(struct video_device *vdev);
void ivtv_s_std_enc(struct ivtv *itv, v4l2_std_id std);
void ivtv_s_std_dec(struct ivtv *itv, v4l2_std_id std);
-int ivtv_s_frequency(struct file *file, void *fh, const struct v4l2_frequency *vf);
-int ivtv_s_input(struct file *file, void *fh, unsigned int inp);
+int ivtv_do_s_frequency(struct ivtv_stream *s, const struct v4l2_frequency *vf);
+int ivtv_do_s_input(struct ivtv *itv, unsigned int inp);
#endif
diff --git a/drivers/media/pci/mgb4/mgb4_trigger.c b/drivers/media/pci/mgb4/mgb4_trigger.c
index d7dddc5c8728..4f9a35904b41 100644
--- a/drivers/media/pci/mgb4/mgb4_trigger.c
+++ b/drivers/media/pci/mgb4/mgb4_trigger.c
@@ -17,6 +17,7 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/pci.h>
#include <linux/dma/amd_xdma.h>
+#include <linux/types.h>
#include "mgb4_core.h"
#include "mgb4_trigger.h"
@@ -90,13 +91,13 @@ static irqreturn_t trigger_handler(int irq, void *p)
struct trigger_data *st = iio_priv(indio_dev);
struct {
u32 data;
- s64 ts __aligned(8);
+ aligned_s64 ts;
} scan = { };
scan.data = mgb4_read_reg(&st->mgbdev->video, 0xA0);
mgb4_write_reg(&st->mgbdev->video, 0xA0, scan.data);
- iio_push_to_buffers_with_timestamp(indio_dev, &scan, pf->timestamp);
+ iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), pf->timestamp);
iio_trigger_notify_done(indio_dev->trig);
mgb4_write_reg(&st->mgbdev->video, 0xB4, 1U << 11);
diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c
index 42c327bc50e1..4b38076486ff 100644
--- a/drivers/media/pci/mgb4/mgb4_vin.c
+++ b/drivers/media/pci/mgb4/mgb4_vin.c
@@ -64,10 +64,10 @@ static const struct mgb4_i2c_kv gmsl_i2c[] = {
static const struct v4l2_dv_timings_cap video_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
- .min_width = 320,
+ .min_width = 240,
.max_width = 4096,
.min_height = 240,
- .max_height = 2160,
+ .max_height = 4096,
.min_pixelclock = 1843200, /* 320 x 240 x 24Hz */
.max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */
.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c
index c179c425e167..fd93fbbaf755 100644
--- a/drivers/media/pci/mgb4/mgb4_vout.c
+++ b/drivers/media/pci/mgb4/mgb4_vout.c
@@ -44,10 +44,10 @@ static const struct mgb4_i2c_kv fpdl3_i2c[] = {
static const struct v4l2_dv_timings_cap video_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
- .min_width = 320,
+ .min_width = 240,
.max_width = 4096,
.min_height = 240,
- .max_height = 2160,
+ .max_height = 4096,
.min_pixelclock = 1843200, /* 320 x 240 x 24Hz */
.max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */
.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
diff --git a/drivers/media/pci/pt1/pt1.c b/drivers/media/pci/pt1/pt1.c
index 121a4a92ea10..1ced093583ac 100644
--- a/drivers/media/pci/pt1/pt1.c
+++ b/drivers/media/pci/pt1/pt1.c
@@ -639,7 +639,7 @@ static int pt1_init_tables(struct pt1 *pt1)
if (!pt1_nr_tables)
return 0;
- tables = vmalloc(array_size(pt1_nr_tables, sizeof(struct pt1_table)));
+ tables = vmalloc_array(pt1_nr_tables, sizeof(struct pt1_table));
if (tables == NULL)
return -ENOMEM;
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 9287faafdce5..3f0b7bb68cc9 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -65,6 +65,7 @@ config VIDEO_MUX
source "drivers/media/platform/allegro-dvt/Kconfig"
source "drivers/media/platform/amlogic/Kconfig"
source "drivers/media/platform/amphion/Kconfig"
+source "drivers/media/platform/arm/Kconfig"
source "drivers/media/platform/aspeed/Kconfig"
source "drivers/media/platform/atmel/Kconfig"
source "drivers/media/platform/broadcom/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 6fd7db0541c7..6d5f79ddfcc3 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -8,6 +8,7 @@
obj-y += allegro-dvt/
obj-y += amlogic/
obj-y += amphion/
+obj-y += arm/
obj-y += aspeed/
obj-y += atmel/
obj-y += broadcom/
diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c
index 510c3c9661d9..eec0b8b30b7f 100644
--- a/drivers/media/platform/allegro-dvt/allegro-core.c
+++ b/drivers/media/platform/allegro-dvt/allegro-core.c
@@ -177,6 +177,7 @@ struct allegro_dev {
*/
unsigned long channel_user_ids;
struct list_head channels;
+ struct mutex channels_lock;
};
static const struct regmap_config allegro_regmap_config = {
@@ -198,6 +199,7 @@ static const struct regmap_config allegro_sram_config = {
};
struct allegro_channel {
+ struct kref ref;
struct allegro_dev *dev;
struct v4l2_fh fh;
struct v4l2_ctrl_handler ctrl_handler;
@@ -430,33 +432,55 @@ static unsigned long allegro_next_user_id(struct allegro_dev *dev)
}
static struct allegro_channel *
-allegro_find_channel_by_user_id(struct allegro_dev *dev,
- unsigned int user_id)
+allegro_ref_get_channel_by_user_id(struct allegro_dev *dev,
+ unsigned int user_id)
{
struct allegro_channel *channel;
+ guard(mutex)(&dev->channels_lock);
+
list_for_each_entry(channel, &dev->channels, list) {
- if (channel->user_id == user_id)
- return channel;
+ if (channel->user_id == user_id) {
+ if (kref_get_unless_zero(&channel->ref))
+ return channel;
+ break;
+ }
}
return ERR_PTR(-EINVAL);
}
static struct allegro_channel *
-allegro_find_channel_by_channel_id(struct allegro_dev *dev,
- unsigned int channel_id)
+allegro_ref_get_channel_by_channel_id(struct allegro_dev *dev,
+ unsigned int channel_id)
{
struct allegro_channel *channel;
+ guard(mutex)(&dev->channels_lock);
+
list_for_each_entry(channel, &dev->channels, list) {
- if (channel->mcu_channel_id == channel_id)
- return channel;
+ if (channel->mcu_channel_id == channel_id) {
+ if (kref_get_unless_zero(&channel->ref))
+ return channel;
+ break;
+ }
}
return ERR_PTR(-EINVAL);
}
+static void allegro_free_channel(struct kref *ref)
+{
+ struct allegro_channel *channel = container_of(ref, struct allegro_channel, ref);
+
+ kfree(channel);
+}
+
+static int allegro_ref_put_channel(struct allegro_channel *channel)
+{
+ return kref_put(&channel->ref, allegro_free_channel);
+}
+
static inline bool channel_exists(struct allegro_channel *channel)
{
return channel->mcu_channel_id != -1;
@@ -831,6 +855,20 @@ out:
return err;
}
+static unsigned int allegro_mbox_get_available(struct allegro_mbox *mbox)
+{
+ struct regmap *sram = mbox->dev->sram;
+ unsigned int head, tail;
+
+ regmap_read(sram, mbox->head, &head);
+ regmap_read(sram, mbox->tail, &tail);
+
+ if (tail >= head)
+ return tail - head;
+ else
+ return mbox->size - (head - tail);
+}
+
static ssize_t allegro_mbox_read(struct allegro_mbox *mbox,
u32 *dst, size_t nbyte)
{
@@ -839,11 +877,15 @@ static ssize_t allegro_mbox_read(struct allegro_mbox *mbox,
u16 type;
} __attribute__ ((__packed__)) *header;
struct regmap *sram = mbox->dev->sram;
- unsigned int head;
+ unsigned int available, head;
ssize_t size;
size_t body_no_wrap;
int stride = regmap_get_reg_stride(sram);
+ available = allegro_mbox_get_available(mbox);
+ if (available < sizeof(*header))
+ return -EAGAIN;
+
regmap_read(sram, mbox->head, &head);
if (head > mbox->size)
return -EIO;
@@ -857,6 +899,8 @@ static ssize_t allegro_mbox_read(struct allegro_mbox *mbox,
return -EIO;
if (size > nbyte)
return -EINVAL;
+ if (size > available)
+ return -EAGAIN;
/*
* The message might wrap within the mailbox. If the message does not
@@ -916,26 +960,27 @@ out:
* allegro_mbox_notify() - Notify the mailbox about a new message
* @mbox: The allegro_mbox to notify
*/
-static void allegro_mbox_notify(struct allegro_mbox *mbox)
+static int allegro_mbox_notify(struct allegro_mbox *mbox)
{
struct allegro_dev *dev = mbox->dev;
union mcu_msg_response *msg;
- ssize_t size;
u32 *tmp;
int err;
msg = kmalloc(sizeof(*msg), GFP_KERNEL);
if (!msg)
- return;
+ return -ENOMEM;
msg->header.version = dev->fw_info->mailbox_version;
tmp = kmalloc(mbox->size, GFP_KERNEL);
- if (!tmp)
+ if (!tmp) {
+ err = -ENOMEM;
goto out;
+ }
- size = allegro_mbox_read(mbox, tmp, mbox->size);
- if (size < 0)
+ err = allegro_mbox_read(mbox, tmp, mbox->size);
+ if (err < 0)
goto out;
err = allegro_decode_mail(msg, tmp);
@@ -947,6 +992,8 @@ static void allegro_mbox_notify(struct allegro_mbox *mbox)
out:
kfree(tmp);
kfree(msg);
+
+ return err;
}
static int allegro_encoder_buffer_init(struct allegro_dev *dev,
@@ -2124,7 +2171,7 @@ static void allegro_channel_finish_frame(struct allegro_channel *channel,
state = VB2_BUF_STATE_DONE;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
if (msg->is_idr)
dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
else
@@ -2163,7 +2210,7 @@ allegro_handle_create_channel(struct allegro_dev *dev,
int err = 0;
struct create_channel_param param;
- channel = allegro_find_channel_by_user_id(dev, msg->user_id);
+ channel = allegro_ref_get_channel_by_user_id(dev, msg->user_id);
if (IS_ERR(channel)) {
v4l2_warn(&dev->v4l2_dev,
"received %s for unknown user %d\n",
@@ -2230,6 +2277,7 @@ allegro_handle_create_channel(struct allegro_dev *dev,
out:
channel->error = err;
complete(&channel->completion);
+ allegro_ref_put_channel(channel);
/* Handled successfully, error is passed via channel->error */
return 0;
@@ -2241,7 +2289,7 @@ allegro_handle_destroy_channel(struct allegro_dev *dev,
{
struct allegro_channel *channel;
- channel = allegro_find_channel_by_channel_id(dev, msg->channel_id);
+ channel = allegro_ref_get_channel_by_channel_id(dev, msg->channel_id);
if (IS_ERR(channel)) {
v4l2_err(&dev->v4l2_dev,
"received %s for unknown channel %d\n",
@@ -2254,6 +2302,7 @@ allegro_handle_destroy_channel(struct allegro_dev *dev,
"user %d: vcu destroyed channel %d\n",
channel->user_id, channel->mcu_channel_id);
complete(&channel->completion);
+ allegro_ref_put_channel(channel);
return 0;
}
@@ -2264,7 +2313,7 @@ allegro_handle_encode_frame(struct allegro_dev *dev,
{
struct allegro_channel *channel;
- channel = allegro_find_channel_by_channel_id(dev, msg->channel_id);
+ channel = allegro_ref_get_channel_by_channel_id(dev, msg->channel_id);
if (IS_ERR(channel)) {
v4l2_err(&dev->v4l2_dev,
"received %s for unknown channel %d\n",
@@ -2274,6 +2323,7 @@ allegro_handle_encode_frame(struct allegro_dev *dev,
}
allegro_channel_finish_frame(channel, msg);
+ allegro_ref_put_channel(channel);
return 0;
}
@@ -2329,7 +2379,10 @@ static irqreturn_t allegro_irq_thread(int irq, void *data)
if (!dev->mbox_status)
return IRQ_NONE;
- allegro_mbox_notify(dev->mbox_status);
+ while (allegro_mbox_get_available(dev->mbox_status) > 0) {
+ if (allegro_mbox_notify(dev->mbox_status))
+ break;
+ }
return IRQ_HANDLED;
}
@@ -2602,8 +2655,14 @@ static int allegro_create_channel(struct allegro_channel *channel)
allegro_mcu_send_create_channel(dev, channel);
time_left = wait_for_completion_timeout(&channel->completion,
msecs_to_jiffies(5000));
- if (time_left == 0)
+ if (time_left == 0) {
+ v4l2_warn(&dev->v4l2_dev,
+ "user %d: timeout while creating channel\n",
+ channel->user_id);
+
channel->error = -ETIMEDOUT;
+ }
+
if (channel->error)
goto err;
@@ -3050,6 +3109,8 @@ static int allegro_open(struct file *file)
if (!channel)
return -ENOMEM;
+ kref_init(&channel->ref);
+
v4l2_fh_init(&channel->fh, vdev);
init_completion(&channel->completion);
@@ -3216,7 +3277,10 @@ static int allegro_open(struct file *file)
goto error;
}
- list_add(&channel->list, &dev->channels);
+ scoped_guard(mutex, &dev->channels_lock) {
+ list_add(&channel->list, &dev->channels);
+ }
+
v4l2_fh_add(&channel->fh, file);
allegro_channel_adjust(channel);
@@ -3232,17 +3296,20 @@ error:
static int allegro_release(struct file *file)
{
struct allegro_channel *channel = file_to_channel(file);
+ struct allegro_dev *dev = channel->dev;
v4l2_m2m_ctx_release(channel->fh.m2m_ctx);
- list_del(&channel->list);
+ scoped_guard(mutex, &dev->channels_lock) {
+ list_del(&channel->list);
+ }
v4l2_ctrl_handler_free(&channel->ctrl_handler);
v4l2_fh_del(&channel->fh, file);
v4l2_fh_exit(&channel->fh);
- kfree(channel);
+ allegro_ref_put_channel(channel);
return 0;
}
@@ -3333,8 +3400,6 @@ static int allegro_s_fmt_vid_cap(struct file *file, void *fh,
return err;
vq = v4l2_m2m_get_vq(channel->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
if (vb2_is_busy(vq))
return -EBUSY;
@@ -3836,6 +3901,7 @@ static int allegro_probe(struct platform_device *pdev)
dev->plat_dev = pdev;
init_completion(&dev->init_complete);
INIT_LIST_HEAD(&dev->channels);
+ mutex_init(&dev->channels_lock);
mutex_init(&dev->lock);
diff --git a/drivers/media/platform/amlogic/c3/isp/Kconfig b/drivers/media/platform/amlogic/c3/isp/Kconfig
index 02c62a50a5e8..809208cd7e3a 100644
--- a/drivers/media/platform/amlogic/c3/isp/Kconfig
+++ b/drivers/media/platform/amlogic/c3/isp/Kconfig
@@ -10,6 +10,7 @@ config VIDEO_C3_ISP
select VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_VMALLOC
+ select V4L2_ISP
help
Video4Linux2 driver for Amlogic C3 ISP pipeline.
The C3 ISP is used for processing raw images and
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
index c80667dd7662..6f9ca7a7dd88 100644
--- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
@@ -3,11 +3,13 @@
* Copyright (C) 2024 Amlogic, Inc. All rights reserved
*/
+#include <linux/build_bug.h>
#include <linux/cleanup.h>
#include <linux/media/amlogic/c3-isp-config.h>
#include <linux/pm_runtime.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
#include <media/v4l2-mc.h>
#include <media/videobuf2-vmalloc.h>
@@ -51,11 +53,6 @@ union c3_isp_params_block {
typedef void (*c3_isp_block_handler)(struct c3_isp_device *isp,
const union c3_isp_params_block *block);
-struct c3_isp_params_handler {
- size_t size;
- c3_isp_block_handler handler;
-};
-
#define to_c3_isp_params_buffer(vbuf) \
container_of(vbuf, struct c3_isp_params_buffer, vb)
@@ -523,41 +520,37 @@ static void c3_isp_params_cfg_blc(struct c3_isp_device *isp,
ISP_TOP_BEO_CTRL_BLC_EN);
}
-static const struct c3_isp_params_handler c3_isp_params_handlers[] = {
- [C3_ISP_PARAMS_BLOCK_AWB_GAINS] = {
- .size = sizeof(struct c3_isp_params_awb_gains),
- .handler = c3_isp_params_cfg_awb_gains,
- },
- [C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = {
- .size = sizeof(struct c3_isp_params_awb_config),
- .handler = c3_isp_params_cfg_awb_config,
- },
- [C3_ISP_PARAMS_BLOCK_AE_CONFIG] = {
- .size = sizeof(struct c3_isp_params_ae_config),
- .handler = c3_isp_params_cfg_ae_config,
- },
- [C3_ISP_PARAMS_BLOCK_AF_CONFIG] = {
- .size = sizeof(struct c3_isp_params_af_config),
- .handler = c3_isp_params_cfg_af_config,
- },
- [C3_ISP_PARAMS_BLOCK_PST_GAMMA] = {
- .size = sizeof(struct c3_isp_params_pst_gamma),
- .handler = c3_isp_params_cfg_pst_gamma,
- },
- [C3_ISP_PARAMS_BLOCK_CCM] = {
- .size = sizeof(struct c3_isp_params_ccm),
- .handler = c3_isp_params_cfg_ccm,
- },
- [C3_ISP_PARAMS_BLOCK_CSC] = {
- .size = sizeof(struct c3_isp_params_csc),
- .handler = c3_isp_params_cfg_csc,
- },
- [C3_ISP_PARAMS_BLOCK_BLC] = {
- .size = sizeof(struct c3_isp_params_blc),
- .handler = c3_isp_params_cfg_blc,
- },
+static const c3_isp_block_handler c3_isp_params_handlers[] = {
+ [C3_ISP_PARAMS_BLOCK_AWB_GAINS] = c3_isp_params_cfg_awb_gains,
+ [C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = c3_isp_params_cfg_awb_config,
+ [C3_ISP_PARAMS_BLOCK_AE_CONFIG] = c3_isp_params_cfg_ae_config,
+ [C3_ISP_PARAMS_BLOCK_AF_CONFIG] = c3_isp_params_cfg_af_config,
+ [C3_ISP_PARAMS_BLOCK_PST_GAMMA] = c3_isp_params_cfg_pst_gamma,
+ [C3_ISP_PARAMS_BLOCK_CCM] = c3_isp_params_cfg_ccm,
+ [C3_ISP_PARAMS_BLOCK_CSC] = c3_isp_params_cfg_csc,
+ [C3_ISP_PARAMS_BLOCK_BLC] = c3_isp_params_cfg_blc,
+};
+
+#define C3_ISP_PARAMS_BLOCK_INFO(block, data) \
+ [C3_ISP_PARAMS_BLOCK_ ## block] = { \
+ .size = sizeof(struct c3_isp_params_ ## data), \
+ }
+
+static const struct v4l2_isp_params_block_type_info
+c3_isp_params_block_types_info[] = {
+ C3_ISP_PARAMS_BLOCK_INFO(AWB_GAINS, awb_gains),
+ C3_ISP_PARAMS_BLOCK_INFO(AWB_CONFIG, awb_config),
+ C3_ISP_PARAMS_BLOCK_INFO(AE_CONFIG, ae_config),
+ C3_ISP_PARAMS_BLOCK_INFO(AF_CONFIG, af_config),
+ C3_ISP_PARAMS_BLOCK_INFO(PST_GAMMA, pst_gamma),
+ C3_ISP_PARAMS_BLOCK_INFO(CCM, ccm),
+ C3_ISP_PARAMS_BLOCK_INFO(CSC, csc),
+ C3_ISP_PARAMS_BLOCK_INFO(BLC, blc),
};
+static_assert(ARRAY_SIZE(c3_isp_params_handlers) ==
+ ARRAY_SIZE(c3_isp_params_block_types_info));
+
static void c3_isp_params_cfg_blocks(struct c3_isp_params *params)
{
struct c3_isp_params_cfg *config = params->buff->cfg;
@@ -568,14 +561,14 @@ static void c3_isp_params_cfg_blocks(struct c3_isp_params *params)
/* Walk the list of parameter blocks and process them */
while (block_offset < config->data_size) {
- const struct c3_isp_params_handler *block_handler;
const union c3_isp_params_block *block;
+ c3_isp_block_handler block_handler;
block = (const union c3_isp_params_block *)
&config->data[block_offset];
- block_handler = &c3_isp_params_handlers[block->header.type];
- block_handler->handler(params->isp, block);
+ block_handler = c3_isp_params_handlers[block->header.type];
+ block_handler(params->isp, block);
block_offset += block->header.size;
}
@@ -771,26 +764,15 @@ static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(vbuf);
struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
- struct c3_isp_params_cfg *cfg = buf->cfg;
struct c3_isp_params_cfg *usr_cfg = vb2_plane_vaddr(vb, 0);
size_t payload_size = vb2_get_plane_payload(vb, 0);
- size_t header_size = offsetof(struct c3_isp_params_cfg, data);
- size_t block_offset = 0;
- size_t cfg_size;
-
- /* Payload size can't be greater than the destination buffer size */
- if (payload_size > params->vfmt.fmt.meta.buffersize) {
- dev_dbg(params->isp->dev,
- "Payload size is too large: %zu\n", payload_size);
- return -EINVAL;
- }
+ struct c3_isp_params_cfg *cfg = buf->cfg;
+ int ret;
- /* Payload size can't be smaller than the header size */
- if (payload_size < header_size) {
- dev_dbg(params->isp->dev,
- "Payload size is too small: %zu\n", payload_size);
- return -EINVAL;
- }
+ ret = v4l2_isp_params_validate_buffer_size(params->isp->dev, vb,
+ params->vfmt.fmt.meta.buffersize);
+ if (ret)
+ return ret;
/*
* Use the internal scratch buffer to avoid userspace modifying
@@ -798,70 +780,10 @@ static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb)
*/
memcpy(cfg, usr_cfg, payload_size);
- /* Only v0 is supported at the moment */
- if (cfg->version != C3_ISP_PARAMS_BUFFER_V0) {
- dev_dbg(params->isp->dev,
- "Invalid params buffer version: %u\n", cfg->version);
- return -EINVAL;
- }
-
- /* Validate the size reported in the parameter buffer header */
- cfg_size = header_size + cfg->data_size;
- if (cfg_size != payload_size) {
- dev_dbg(params->isp->dev,
- "Data size %zu and payload size %zu are different\n",
- cfg_size, payload_size);
- return -EINVAL;
- }
-
- /* Walk the list of parameter blocks and validate them */
- cfg_size = cfg->data_size;
- while (cfg_size >= sizeof(struct c3_isp_params_block_header)) {
- const struct c3_isp_params_block_header *block;
- const struct c3_isp_params_handler *handler;
-
- block = (struct c3_isp_params_block_header *)
- &cfg->data[block_offset];
-
- if (block->type >= ARRAY_SIZE(c3_isp_params_handlers)) {
- dev_dbg(params->isp->dev,
- "Invalid params block type\n");
- return -EINVAL;
- }
-
- if (block->size > cfg_size) {
- dev_dbg(params->isp->dev,
- "Block size is greater than cfg size\n");
- return -EINVAL;
- }
-
- if ((block->flags & (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
- C3_ISP_PARAMS_BLOCK_FL_DISABLE)) ==
- (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
- C3_ISP_PARAMS_BLOCK_FL_DISABLE)) {
- dev_dbg(params->isp->dev,
- "Invalid parameters block flags\n");
- return -EINVAL;
- }
-
- handler = &c3_isp_params_handlers[block->type];
- if (block->size != handler->size) {
- dev_dbg(params->isp->dev,
- "Invalid params block size\n");
- return -EINVAL;
- }
-
- block_offset += block->size;
- cfg_size -= block->size;
- }
-
- if (cfg_size) {
- dev_dbg(params->isp->dev,
- "Unexpected data after the params buffer end\n");
- return -EINVAL;
- }
-
- return 0;
+ return v4l2_isp_params_validate_buffer(params->isp->dev, vb,
+ (struct v4l2_isp_params_buffer *)cfg,
+ c3_isp_params_block_types_info,
+ ARRAY_SIZE(c3_isp_params_block_types_info));
}
static int c3_isp_params_vb2_buf_init(struct vb2_buffer *vb)
diff --git a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
index 5744853a4003..c51c6f4e41dc 100644
--- a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
+++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
@@ -632,13 +632,8 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct ge2d_ctx *ctx = file_to_ge2d_ctx(file);
- struct vb2_queue *vq;
struct ge2d_frame *frm;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
frm = get_frame(ctx, f->type);
f->fmt.pix = frm->pix_fmt;
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
index 32eef2fd1f2a..c0d2aabb9e0e 100644
--- a/drivers/media/platform/amphion/vdec.c
+++ b/drivers/media/platform/amphion/vdec.c
@@ -532,8 +532,6 @@ static int vdec_s_fmt_common(struct vpu_inst *inst, struct v4l2_format *f)
return -EINVAL;
q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
- if (!q)
- return -EINVAL;
if (vb2_is_busy(q))
return -EBUSY;
@@ -823,7 +821,7 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg)
vbuf = &vpu_buf->m2m_buf.vb;
src_buf = vdec_get_src_buffer(inst, info->consumed_count);
if (src_buf) {
- v4l2_m2m_buf_copy_metadata(src_buf, vbuf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, vbuf);
if (info->consumed_count) {
v4l2_m2m_src_buf_remove(inst->fh.m2m_ctx);
vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE);
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
index c5c1f1fbaa80..aced76401b69 100644
--- a/drivers/media/platform/amphion/venc.c
+++ b/drivers/media/platform/amphion/venc.c
@@ -223,8 +223,6 @@ static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
- if (!q)
- return -EINVAL;
if (vb2_is_busy(q))
return -EBUSY;
@@ -790,7 +788,7 @@ static int venc_get_one_encoded_frame(struct vpu_inst *inst,
src_buf = vpu_find_buf_by_sequence(inst, inst->out_format.type, frame->info.frame_id);
if (src_buf) {
- v4l2_m2m_buf_copy_metadata(src_buf, vbuf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, vbuf);
vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE);
v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, src_buf);
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c
index da00f5fc0e5d..168f0514851e 100644
--- a/drivers/media/platform/amphion/vpu_core.c
+++ b/drivers/media/platform/amphion/vpu_core.c
@@ -10,7 +10,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -542,47 +542,30 @@ const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst)
static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
{
- struct device_node *node;
struct resource res;
int ret;
- if (of_count_phandle_with_args(np, "memory-region", NULL) < 2) {
- dev_err(core->dev, "need 2 memory-region for boot and rpc\n");
- return -ENODEV;
+ ret = of_reserved_mem_region_to_resource(np, 0, &res);
+ if (ret) {
+ dev_err(core->dev, "Cannot get boot-region\n");
+ return ret;
}
- node = of_parse_phandle(np, "memory-region", 0);
- if (!node) {
- dev_err(core->dev, "boot-region of_parse_phandle error\n");
- return -ENODEV;
- }
- if (of_address_to_resource(node, 0, &res)) {
- dev_err(core->dev, "boot-region of_address_to_resource error\n");
- of_node_put(node);
- return -EINVAL;
- }
core->fw.phys = res.start;
core->fw.length = resource_size(&res);
- of_node_put(node);
-
- node = of_parse_phandle(np, "memory-region", 1);
- if (!node) {
- dev_err(core->dev, "rpc-region of_parse_phandle error\n");
- return -ENODEV;
- }
- if (of_address_to_resource(node, 0, &res)) {
- dev_err(core->dev, "rpc-region of_address_to_resource error\n");
- of_node_put(node);
- return -EINVAL;
+ ret = of_reserved_mem_region_to_resource(np, 1, &res);
+ if (ret) {
+ dev_err(core->dev, "Cannot get rpc-region\n");
+ return ret;
}
+
core->rpc.phys = res.start;
core->rpc.length = resource_size(&res);
if (core->rpc.length < core->res->rpc_size + core->res->fwlog_size) {
dev_err(core->dev, "the rpc-region <%pad, 0x%x> is not enough\n",
&core->rpc.phys, core->rpc.length);
- of_node_put(node);
return -EINVAL;
}
@@ -594,7 +577,6 @@ static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
if (ret != VPU_CORE_MEMORY_UNCACHED) {
dev_err(core->dev, "rpc region<%pad, 0x%x> isn't uncached\n",
&core->rpc.phys, core->rpc.length);
- of_node_put(node);
return -EINVAL;
}
@@ -606,8 +588,6 @@ static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
core->act.length = core->rpc.length - core->res->rpc_size - core->log.length;
core->rpc.length = core->res->rpc_size;
- of_node_put(node);
-
return 0;
}
diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c
index efbfd2652721..2cca61f41bea 100644
--- a/drivers/media/platform/amphion/vpu_drv.c
+++ b/drivers/media/platform/amphion/vpu_drv.c
@@ -175,31 +175,6 @@ static void vpu_remove(struct platform_device *pdev)
mutex_destroy(&vpu->lock);
}
-static int __maybe_unused vpu_runtime_resume(struct device *dev)
-{
- return 0;
-}
-
-static int __maybe_unused vpu_runtime_suspend(struct device *dev)
-{
- return 0;
-}
-
-static int __maybe_unused vpu_resume(struct device *dev)
-{
- return 0;
-}
-
-static int __maybe_unused vpu_suspend(struct device *dev)
-{
- return 0;
-}
-
-static const struct dev_pm_ops vpu_pm_ops = {
- SET_RUNTIME_PM_OPS(vpu_runtime_suspend, vpu_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(vpu_suspend, vpu_resume)
-};
-
static struct vpu_resources imx8qxp_res = {
.plat_type = IMX8QXP,
.mreg_base = 0x40000000,
@@ -231,7 +206,6 @@ static struct platform_driver amphion_vpu_driver = {
.driver = {
.name = "amphion-vpu",
.of_match_table = vpu_dt_match,
- .pm = &vpu_pm_ops,
},
};
diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c
index ba688566dffd..80802975c4f1 100644
--- a/drivers/media/platform/amphion/vpu_malone.c
+++ b/drivers/media/platform/amphion/vpu_malone.c
@@ -1337,22 +1337,18 @@ static int vpu_malone_insert_scode_vc1_g_seq(struct malone_scode_t *scode)
{
if (!scode->inst->total_input_count)
return 0;
- if (vpu_vb_is_codecconfig(to_vb2_v4l2_buffer(scode->vb)))
- scode->need_data = 0;
return 0;
}
static int vpu_malone_insert_scode_vc1_g_pic(struct malone_scode_t *scode)
{
- struct vb2_v4l2_buffer *vbuf;
u8 nal_hdr[MALONE_VC1_NAL_HEADER_LEN];
u32 *data = NULL;
int ret;
- vbuf = to_vb2_v4l2_buffer(scode->vb);
data = vb2_plane_vaddr(scode->vb, 0);
- if (scode->inst->total_input_count == 0 || vpu_vb_is_codecconfig(vbuf))
+ if (scode->inst->total_input_count == 0)
return 0;
if (MALONE_VC1_CONTAIN_NAL(*data))
return 0;
@@ -1373,8 +1369,6 @@ static int vpu_malone_insert_scode_vc1_l_seq(struct malone_scode_t *scode)
int size = 0;
u8 rcv_seqhdr[MALONE_VC1_RCV_SEQ_HEADER_LEN];
- if (vpu_vb_is_codecconfig(to_vb2_v4l2_buffer(scode->vb)))
- scode->need_data = 0;
if (scode->inst->total_input_count)
return 0;
scode->need_data = 0;
@@ -1560,7 +1554,7 @@ static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str
scode.vb = vb;
scode.wptr = wptr;
scode.need_data = 1;
- if (vbuf->sequence == 0 || vpu_vb_is_codecconfig(vbuf))
+ if (vbuf->sequence == 0)
ret = vpu_malone_insert_scode(&scode, SCODE_SEQUENCE);
if (ret < 0)
@@ -1596,7 +1590,7 @@ static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str
* This module is currently only supported for the H264 and HEVC formats,
* for other formats, vpu_malone_add_scode() will return 0.
*/
- if ((disp_imm || low_latency) && !vpu_vb_is_codecconfig(vbuf)) {
+ if (disp_imm || low_latency) {
ret = vpu_malone_add_scode(inst->core->iface,
inst->id,
&inst->stream_buffer,
@@ -1643,7 +1637,6 @@ int vpu_malone_input_frame(struct vpu_shared_addr *shared,
struct vpu_inst *inst, struct vb2_buffer *vb)
{
struct vpu_dec_ctrl *hc = shared->priv;
- struct vb2_v4l2_buffer *vbuf;
struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[inst->id];
u32 disp_imm = hc->codec_param[inst->id].disp_imm;
u32 size;
@@ -1657,16 +1650,6 @@ int vpu_malone_input_frame(struct vpu_shared_addr *shared,
return ret;
size = ret;
- /*
- * if buffer only contain codec data, and the timestamp is invalid,
- * don't put the invalid timestamp to resync
- * merge the data to next frame
- */
- vbuf = to_vb2_v4l2_buffer(vb);
- if (vpu_vb_is_codecconfig(vbuf)) {
- inst->extra_size += size;
- return 0;
- }
if (inst->extra_size) {
size += inst->extra_size;
inst->extra_size = 0;
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
index fcb2eff813ac..47dff9a35bb4 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.c
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -349,16 +349,6 @@ struct vb2_v4l2_buffer *vpu_next_src_buf(struct vpu_inst *inst)
if (!src_buf || vpu_get_buffer_state(src_buf) == VPU_BUF_STATE_IDLE)
return NULL;
- while (vpu_vb_is_codecconfig(src_buf)) {
- v4l2_m2m_src_buf_remove(inst->fh.m2m_ctx);
- vpu_set_buffer_state(src_buf, VPU_BUF_STATE_IDLE);
- v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
-
- src_buf = v4l2_m2m_next_src_buf(inst->fh.m2m_ctx);
- if (!src_buf || vpu_get_buffer_state(src_buf) == VPU_BUF_STATE_IDLE)
- return NULL;
- }
-
return src_buf;
}
@@ -713,15 +703,15 @@ static int vpu_v4l2_release(struct vpu_inst *inst)
{
vpu_trace(inst->vpu->dev, "%p\n", inst);
- vpu_release_core(inst->core);
- put_device(inst->dev);
-
if (inst->workqueue) {
cancel_work_sync(&inst->msg_work);
destroy_workqueue(inst->workqueue);
inst->workqueue = NULL;
}
+ vpu_release_core(inst->core);
+ put_device(inst->dev);
+
v4l2_ctrl_handler_free(&inst->ctrl_handler);
mutex_destroy(&inst->lock);
diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h
index 4a87b06ae520..da9945f25e32 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.h
+++ b/drivers/media/platform/amphion/vpu_v4l2.h
@@ -39,14 +39,4 @@ static inline struct vpu_format *vpu_get_format(struct vpu_inst *inst, u32 type)
else
return &inst->cap_format;
}
-
-static inline int vpu_vb_is_codecconfig(struct vb2_v4l2_buffer *vbuf)
-{
-#ifdef V4L2_BUF_FLAG_CODECCONFIG
- return (vbuf->flags & V4L2_BUF_FLAG_CODECCONFIG) ? 1 : 0;
-#else
- return 0;
-#endif
-}
-
#endif
diff --git a/drivers/media/platform/arm/Kconfig b/drivers/media/platform/arm/Kconfig
new file mode 100644
index 000000000000..4f0764c329c7
--- /dev/null
+++ b/drivers/media/platform/arm/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "ARM media platform drivers"
+
+source "drivers/media/platform/arm/mali-c55/Kconfig"
diff --git a/arch/s390/kernel/vdso32/.gitignore b/drivers/media/platform/arm/Makefile
index 5167384843b9..8cc4918725ef 100644
--- a/arch/s390/kernel/vdso32/.gitignore
+++ b/drivers/media/platform/arm/Makefile
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
-vdso32.lds
+obj-y += mali-c55/
diff --git a/drivers/media/platform/arm/mali-c55/Kconfig b/drivers/media/platform/arm/mali-c55/Kconfig
new file mode 100644
index 000000000000..5b084b3c3340
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_MALI_C55
+ tristate "ARM Mali-C55 Image Signal Processor driver"
+ depends on ARCH_VEXPRESS || ARCH_RENESAS || COMPILE_TEST
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ select GENERIC_PHY_MIPI_DPHY
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select V4L2_ISP
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ help
+ Enable this to support Arm's Mali-C55 Image Signal Processor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mali-c55.
diff --git a/drivers/media/platform/arm/mali-c55/Makefile b/drivers/media/platform/arm/mali-c55/Makefile
new file mode 100644
index 000000000000..d5718b0b23e0
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+
+mali-c55-y := mali-c55-capture.o \
+ mali-c55-core.o \
+ mali-c55-isp.o \
+ mali-c55-params.o \
+ mali-c55-resizer.o \
+ mali-c55-stats.o \
+ mali-c55-tpg.o
+
+obj-$(CONFIG_VIDEO_MALI_C55) += mali-c55.o
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-capture.c b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c
new file mode 100644
index 000000000000..7aaa5c3f7354
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-capture.c
@@ -0,0 +1,959 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Mali-C55 ISP Driver - Video capture devices
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include <linux/cleanup.h>
+#include <linux/minmax.h>
+#include <linux/lockdep.h>
+#include <linux/pm_runtime.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mali-c55-common.h"
+#include "mali-c55-registers.h"
+
+static const struct mali_c55_format_info mali_c55_fmts[] = {
+ /*
+ * This table is missing some entries which need further work or
+ * investigation:
+ *
+ * Base mode 5 is "Generic Data"
+ * Base mode 8 is a backwards V4L2_PIX_FMT_XYUV32 - no V4L2 equivalent
+ * Base mode 9 seems to have no V4L2 equivalent
+ * Base mode 17, 19 and 20 describe formats which seem to have no V4L2
+ * equivalent
+ */
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_RGB121212_1X36,
+ MEDIA_BUS_FMT_RGB202020_1X60,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_RGB32,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB2101010,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_RGB121212_1X36,
+ MEDIA_BUS_FMT_RGB202020_1X60,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_A2R10G10B10,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_RGB121212_1X36,
+ MEDIA_BUS_FMT_RGB202020_1X60,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_RGB565,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_RGB121212_1X36,
+ MEDIA_BUS_FMT_RGB202020_1X60,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_RGB24,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_YUV10_1X30,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_YUY2,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_YUV10_1X30,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_UYVY,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y210,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_YUV10_1X30,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_Y210,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ /*
+ * This is something of a hack, the ISP thinks it's running NV12M but
+ * by setting uv_plane = 0 we simply discard that planes and only output
+ * the Y-plane.
+ */
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_YUV10_1X30,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_NV12_21,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_YUV10_1X30,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_NV12_21,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT1
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_YUV10_1X30,
+ },
+ .is_raw = false,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_NV12_21,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT2
+ }
+ },
+ /*
+ * RAW uncompressed formats are all packed in 16 bpp.
+ * TODO: Expand this list to encompass all possible RAW formats.
+ */
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SRGGB16_1X16,
+ },
+ .is_raw = true,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_RAW16,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SBGGR16_1X16,
+ },
+ .is_raw = true,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_RAW16,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SGBRG16_1X16,
+ },
+ .is_raw = true,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_RAW16,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SGRBG16_1X16,
+ },
+ .is_raw = true,
+ .registers = {
+ .base_mode = MALI_C55_OUTPUT_RAW16,
+ .uv_plane = MALI_C55_OUTPUT_PLANE_ALT0
+ }
+ },
+};
+
+void mali_c55_cap_dev_write(struct mali_c55_cap_dev *cap_dev, unsigned int addr,
+ u32 val)
+{
+ mali_c55_ctx_write(cap_dev->mali_c55, addr + cap_dev->reg_offset, val);
+}
+
+static u32 mali_c55_cap_dev_read(struct mali_c55_cap_dev *cap_dev, unsigned int addr)
+{
+ return mali_c55_ctx_read(cap_dev->mali_c55, addr + cap_dev->reg_offset);
+}
+
+static void mali_c55_cap_dev_update_bits(struct mali_c55_cap_dev *cap_dev,
+ unsigned int addr, u32 mask, u32 val)
+{
+ u32 orig, tmp;
+
+ orig = mali_c55_cap_dev_read(cap_dev, addr);
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ if (tmp != orig)
+ mali_c55_cap_dev_write(cap_dev, addr, tmp);
+}
+
+static bool
+mali_c55_mbus_code_can_produce_fmt(const struct mali_c55_format_info *fmt,
+ u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fmt->mbus_codes); i++) {
+ if (fmt->mbus_codes[i] == code)
+ return true;
+ }
+
+ return false;
+}
+
+bool mali_c55_format_is_raw(unsigned int mbus_code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_fmts); i++) {
+ if (mali_c55_fmts[i].mbus_codes[0] == mbus_code)
+ return mali_c55_fmts[i].is_raw;
+ }
+
+ return false;
+}
+
+static const struct mali_c55_format_info *
+mali_c55_format_from_pix(const u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_fmts); i++) {
+ if (mali_c55_fmts[i].fourcc == pixelformat)
+ return &mali_c55_fmts[i];
+ }
+
+ /*
+ * If we find no matching pixelformat, we'll just default to the first
+ * one for now.
+ */
+
+ return &mali_c55_fmts[0];
+}
+
+static const char * const capture_device_names[] = {
+ "mali-c55 fr",
+ "mali-c55 ds",
+};
+
+static int mali_c55_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct mali_c55_cap_dev *cap_dev = video_get_drvdata(vdev);
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(link->source->entity);
+ const struct v4l2_pix_format_mplane *pix_mp;
+ const struct mali_c55_format_info *cap_fmt;
+ struct v4l2_subdev_format sd_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = link->source->index,
+ };
+ int ret;
+
+ ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt);
+ if (ret)
+ return ret;
+
+ pix_mp = &cap_dev->format.format;
+ cap_fmt = cap_dev->format.info;
+
+ if (sd_fmt.format.width != pix_mp->width ||
+ sd_fmt.format.height != pix_mp->height) {
+ dev_dbg(cap_dev->mali_c55->dev,
+ "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ sd_fmt.format.width, sd_fmt.format.height,
+ pix_mp->width, pix_mp->height);
+ return -EPIPE;
+ }
+
+ if (!mali_c55_mbus_code_can_produce_fmt(cap_fmt, sd_fmt.format.code)) {
+ dev_dbg(cap_dev->mali_c55->dev,
+ "link '%s':%u -> '%s':%u not valid: mbus_code 0x%04x cannot produce pixel format %p4cc\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ sd_fmt.format.code, &pix_mp->pixelformat);
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations mali_c55_media_ops = {
+ .link_validate = mali_c55_link_validate,
+};
+
+static int mali_c55_vb2_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct mali_c55_cap_dev *cap_dev = q->drv_priv;
+ unsigned int i;
+
+ if (*num_planes) {
+ if (*num_planes != cap_dev->format.format.num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < cap_dev->format.format.num_planes; i++)
+ if (sizes[i] < cap_dev->format.format.plane_fmt[i].sizeimage)
+ return -EINVAL;
+ } else {
+ *num_planes = cap_dev->format.format.num_planes;
+ for (i = 0; i < cap_dev->format.format.num_planes; i++)
+ sizes[i] = cap_dev->format.format.plane_fmt[i].sizeimage;
+ }
+
+ return 0;
+}
+
+static void mali_c55_buf_queue(struct vb2_buffer *vb)
+{
+ struct mali_c55_cap_dev *cap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mali_c55_buffer *buf = container_of(vbuf,
+ struct mali_c55_buffer, vb);
+ unsigned int i;
+
+ buf->planes_pending = cap_dev->format.format.num_planes;
+
+ for (i = 0; i < cap_dev->format.format.num_planes; i++) {
+ unsigned long size = cap_dev->format.format.plane_fmt[i].sizeimage;
+
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ buf->vb.field = V4L2_FIELD_NONE;
+
+ guard(spinlock)(&cap_dev->buffers.lock);
+ list_add_tail(&buf->queue, &cap_dev->buffers.input);
+}
+
+static int mali_c55_buf_init(struct vb2_buffer *vb)
+{
+ struct mali_c55_cap_dev *cap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mali_c55_buffer *buf = container_of(vbuf,
+ struct mali_c55_buffer, vb);
+ unsigned int i;
+
+ for (i = 0; i < cap_dev->format.format.num_planes; i++)
+ buf->addrs[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ return 0;
+}
+
+void mali_c55_set_next_buffer(struct mali_c55_cap_dev *cap_dev)
+{
+ struct v4l2_pix_format_mplane *pix_mp;
+ struct mali_c55_buffer *buf;
+ dma_addr_t *addrs;
+
+ scoped_guard(spinlock, &cap_dev->buffers.lock) {
+ buf = list_first_entry_or_null(&cap_dev->buffers.input,
+ struct mali_c55_buffer, queue);
+ if (buf)
+ list_del(&buf->queue);
+ }
+
+ if (!buf) {
+ /*
+ * If we underflow then we can tell the ISP that we don't want
+ * to write out the next frame.
+ */
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_Y_WRITER_MODE,
+ MALI_C55_WRITER_FRAME_WRITE_MASK,
+ 0x00);
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_UV_WRITER_MODE,
+ MALI_C55_WRITER_FRAME_WRITE_MASK,
+ 0x00);
+ return;
+ }
+
+ pix_mp = &cap_dev->format.format;
+
+ mali_c55_cap_dev_update_bits(cap_dev, MALI_C55_REG_Y_WRITER_MODE,
+ MALI_C55_WRITER_FRAME_WRITE_MASK,
+ MALI_C55_WRITER_FRAME_WRITE_ENABLE);
+ if (cap_dev->format.info->registers.uv_plane)
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_UV_WRITER_MODE,
+ MALI_C55_WRITER_FRAME_WRITE_MASK,
+ MALI_C55_WRITER_FRAME_WRITE_ENABLE);
+
+ addrs = buf->addrs;
+ mali_c55_cap_dev_write(cap_dev,
+ MALI_C55_REG_Y_WRITER_BANKS_BASE,
+ addrs[MALI_C55_PLANE_Y]);
+ mali_c55_cap_dev_write(cap_dev,
+ MALI_C55_REG_UV_WRITER_BANKS_BASE,
+ addrs[MALI_C55_PLANE_UV]);
+
+ mali_c55_cap_dev_write(cap_dev,
+ MALI_C55_REG_Y_WRITER_OFFSET,
+ pix_mp->plane_fmt[MALI_C55_PLANE_Y].bytesperline);
+ mali_c55_cap_dev_write(cap_dev,
+ MALI_C55_REG_UV_WRITER_OFFSET,
+ pix_mp->plane_fmt[MALI_C55_PLANE_UV].bytesperline);
+
+ guard(spinlock)(&cap_dev->buffers.processing_lock);
+ list_add_tail(&buf->queue, &cap_dev->buffers.processing);
+}
+
+/**
+ * mali_c55_set_plane_done - mark the plane as written and process the buffer if
+ * both planes are finished.
+ * @cap_dev: pointer to the fr or ds pipe output
+ * @plane: the plane to mark as completed
+ *
+ * The Mali C55 ISP has muliplanar outputs for some formats that come with two
+ * separate "buffer write completed" interrupts - we need to flag each plane's
+ * completion and check whether both planes are done - if so, complete the buf
+ * in vb2.
+ */
+void mali_c55_set_plane_done(struct mali_c55_cap_dev *cap_dev,
+ enum mali_c55_planes plane)
+{
+ struct mali_c55_isp *isp = &cap_dev->mali_c55->isp;
+ struct mali_c55_buffer *buf;
+
+ scoped_guard(spinlock, &cap_dev->buffers.processing_lock) {
+ buf = list_first_entry_or_null(&cap_dev->buffers.processing,
+ struct mali_c55_buffer, queue);
+
+ /*
+ * If the stream was stopped, the buffer might have been sent
+ * back to userspace already.
+ */
+ if (!buf || --buf->planes_pending)
+ return;
+
+ list_del(&buf->queue);
+ }
+
+ /* If the other plane is also done... */
+ buf->vb.vb2_buf.timestamp = ktime_get_boottime_ns();
+ buf->vb.sequence = isp->frame_sequence++;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void mali_c55_cap_dev_stream_disable(struct mali_c55_cap_dev *cap_dev)
+{
+ mali_c55_cap_dev_update_bits(cap_dev, MALI_C55_REG_Y_WRITER_MODE,
+ MALI_C55_WRITER_FRAME_WRITE_MASK, 0x00);
+ mali_c55_cap_dev_update_bits(cap_dev, MALI_C55_REG_UV_WRITER_MODE,
+ MALI_C55_WRITER_FRAME_WRITE_MASK, 0x00);
+}
+
+static void mali_c55_cap_dev_stream_enable(struct mali_c55_cap_dev *cap_dev)
+{
+ /*
+ * The Mali ISP can hold up to 5 buffer addresses and simply cycle
+ * through them, but it's not clear to me that the vb2 queue _guarantees_
+ * it will queue buffers to the driver in a fixed order, and ensuring
+ * we call vb2_buffer_done() for the right buffer seems to me to add
+ * pointless complexity given in multi-context mode we'd need to
+ * re-write those registers every frame anyway...so we tell the ISP to
+ * use a single register and update it for each frame.
+ */
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_Y_WRITER_BANKS_CONFIG,
+ MALI_C55_REG_Y_WRITER_MAX_BANKS_MASK, 0);
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_UV_WRITER_BANKS_CONFIG,
+ MALI_C55_REG_UV_WRITER_MAX_BANKS_MASK, 0);
+
+ mali_c55_set_next_buffer(cap_dev);
+}
+
+static void mali_c55_cap_dev_return_buffers(struct mali_c55_cap_dev *cap_dev,
+ enum vb2_buffer_state state)
+{
+ struct mali_c55_buffer *buf, *tmp;
+
+ scoped_guard(spinlock, &cap_dev->buffers.lock) {
+ list_for_each_entry_safe(buf, tmp, &cap_dev->buffers.input,
+ queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ }
+
+ guard(spinlock)(&cap_dev->buffers.processing_lock);
+ list_for_each_entry_safe(buf, tmp, &cap_dev->buffers.processing, queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+}
+
+static void mali_c55_cap_dev_format_configure(struct mali_c55_cap_dev *cap_dev)
+{
+ const struct mali_c55_format_info *capture_format = cap_dev->format.info;
+ const struct v4l2_pix_format_mplane *pix_mp = &cap_dev->format.format;
+ const struct v4l2_format_info *info;
+
+ info = v4l2_format_info(pix_mp->pixelformat);
+ if (WARN_ON(!info))
+ return;
+
+ mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_Y_WRITER_MODE,
+ capture_format->registers.base_mode);
+ mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_ACTIVE_OUT_Y_SIZE,
+ MALI_C55_REG_ACTIVE_OUT_SIZE_W(pix_mp->width) |
+ MALI_C55_REG_ACTIVE_OUT_SIZE_H(pix_mp->height));
+
+ if (info->mem_planes > 1) {
+ mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_UV_WRITER_MODE,
+ capture_format->registers.base_mode);
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_UV_WRITER_MODE,
+ MALI_C55_WRITER_SUBMODE_MASK,
+ MALI_C55_WRITER_SUBMODE(capture_format->registers.uv_plane));
+
+ mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_ACTIVE_OUT_UV_SIZE,
+ MALI_C55_REG_ACTIVE_OUT_SIZE_W(pix_mp->width) |
+ MALI_C55_REG_ACTIVE_OUT_SIZE_H(pix_mp->height));
+ }
+
+ if (info->pixel_enc == V4L2_PIXEL_ENC_YUV) {
+ mali_c55_cap_dev_write(cap_dev, MALI_C55_REG_CS_CONV_CONFIG,
+ MALI_C55_CS_CONV_MATRIX_MASK);
+
+ if (info->hdiv > 1)
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_CS_CONV_CONFIG,
+ MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_MASK,
+ MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_ENABLE);
+ if (info->vdiv > 1)
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_CS_CONV_CONFIG,
+ MALI_C55_CS_CONV_VERT_DOWNSAMPLE_MASK,
+ MALI_C55_CS_CONV_VERT_DOWNSAMPLE_ENABLE);
+ if (info->hdiv > 1 || info->vdiv > 1)
+ mali_c55_cap_dev_update_bits(cap_dev,
+ MALI_C55_REG_CS_CONV_CONFIG,
+ MALI_C55_CS_CONV_FILTER_MASK,
+ MALI_C55_CS_CONV_FILTER_ENABLE);
+ }
+}
+
+static int mali_c55_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct mali_c55_cap_dev *cap_dev = q->drv_priv;
+ struct mali_c55 *mali_c55 = cap_dev->mali_c55;
+ struct mali_c55_resizer *rsz = cap_dev->rsz;
+ struct mali_c55_isp *isp = &mali_c55->isp;
+ int ret;
+
+ guard(mutex)(&isp->capture_lock);
+
+ ret = pm_runtime_resume_and_get(mali_c55->dev);
+ if (ret)
+ goto err_return_buffers;
+
+ ret = video_device_pipeline_alloc_start(&cap_dev->vdev);
+ if (ret) {
+ dev_dbg(mali_c55->dev, "%s failed to start media pipeline\n",
+ cap_dev->vdev.name);
+ goto err_pm_put;
+ }
+
+ mali_c55_cap_dev_format_configure(cap_dev);
+ mali_c55_cap_dev_stream_enable(cap_dev);
+
+ ret = v4l2_subdev_enable_streams(&rsz->sd, MALI_C55_RSZ_SOURCE_PAD,
+ BIT(0));
+ if (ret)
+ goto err_disable_cap_dev;
+
+ if (mali_c55_pipeline_ready(mali_c55)) {
+ ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ BIT(0));
+ if (ret < 0)
+ goto err_disable_rsz;
+ }
+
+ return 0;
+
+err_disable_rsz:
+ v4l2_subdev_disable_streams(&rsz->sd, MALI_C55_RSZ_SOURCE_PAD, BIT(0));
+err_disable_cap_dev:
+ mali_c55_cap_dev_stream_disable(cap_dev);
+ video_device_pipeline_stop(&cap_dev->vdev);
+err_pm_put:
+ pm_runtime_put_autosuspend(mali_c55->dev);
+err_return_buffers:
+ mali_c55_cap_dev_return_buffers(cap_dev, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void mali_c55_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct mali_c55_cap_dev *cap_dev = q->drv_priv;
+ struct mali_c55 *mali_c55 = cap_dev->mali_c55;
+ struct mali_c55_resizer *rsz = cap_dev->rsz;
+ struct mali_c55_isp *isp = &mali_c55->isp;
+
+ guard(mutex)(&isp->capture_lock);
+
+ if (mali_c55_pipeline_ready(mali_c55)) {
+ if (v4l2_subdev_is_streaming(&isp->sd))
+ v4l2_subdev_disable_streams(&isp->sd,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ BIT(0));
+ }
+
+ v4l2_subdev_disable_streams(&rsz->sd, MALI_C55_RSZ_SOURCE_PAD, BIT(0));
+ mali_c55_cap_dev_stream_disable(cap_dev);
+ mali_c55_cap_dev_return_buffers(cap_dev, VB2_BUF_STATE_ERROR);
+ video_device_pipeline_stop(&cap_dev->vdev);
+ pm_runtime_put_autosuspend(mali_c55->dev);
+}
+
+static const struct vb2_ops mali_c55_vb2_ops = {
+ .queue_setup = &mali_c55_vb2_queue_setup,
+ .buf_queue = &mali_c55_buf_queue,
+ .buf_init = &mali_c55_buf_init,
+ .start_streaming = &mali_c55_vb2_start_streaming,
+ .stop_streaming = &mali_c55_vb2_stop_streaming,
+};
+
+static const struct v4l2_file_operations mali_c55_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static void mali_c55_try_fmt(struct v4l2_pix_format_mplane *pix_mp)
+{
+ const struct mali_c55_format_info *capture_format;
+ const struct v4l2_format_info *info;
+ struct v4l2_plane_pix_format *plane, *y_plane;
+ unsigned int padding;
+ unsigned int i;
+
+ capture_format = mali_c55_format_from_pix(pix_mp->pixelformat);
+ pix_mp->pixelformat = capture_format->fourcc;
+
+ pix_mp->width = clamp(pix_mp->width, MALI_C55_MIN_WIDTH,
+ MALI_C55_MAX_WIDTH);
+ pix_mp->height = clamp(pix_mp->height, MALI_C55_MIN_HEIGHT,
+ MALI_C55_MAX_HEIGHT);
+
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ info = v4l2_format_info(pix_mp->pixelformat);
+ pix_mp->num_planes = info->mem_planes;
+ memset(pix_mp->plane_fmt, 0, sizeof(pix_mp->plane_fmt));
+
+ y_plane = &pix_mp->plane_fmt[0];
+ y_plane->bytesperline = clamp(y_plane->bytesperline,
+ info->bpp[0] * pix_mp->width, 65535U);
+
+ /*
+ * The ISP requires that the stride be aligned to 16-bytes. This is not
+ * detailed in the documentation but has been verified experimentally.
+ */
+ y_plane->bytesperline = ALIGN(y_plane->bytesperline, 16);
+ y_plane->sizeimage = y_plane->bytesperline * pix_mp->height;
+
+ padding = y_plane->bytesperline - (pix_mp->width * info->bpp[0]);
+
+ for (i = 1; i < info->comp_planes; i++) {
+ plane = &pix_mp->plane_fmt[i];
+
+ plane->bytesperline = DIV_ROUND_UP(info->bpp[i] * pix_mp->width,
+ info->hdiv) + padding;
+ plane->sizeimage = DIV_ROUND_UP(plane->bytesperline *
+ pix_mp->height, info->vdiv);
+ }
+
+ if (info->mem_planes == 1) {
+ for (i = 1; i < info->comp_planes; i++) {
+ plane = &pix_mp->plane_fmt[i];
+ y_plane->sizeimage += plane->sizeimage;
+ }
+ }
+}
+
+static int mali_c55_try_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ mali_c55_try_fmt(&f->fmt.pix_mp);
+
+ return 0;
+}
+
+static void mali_c55_set_format(struct mali_c55_cap_dev *cap_dev,
+ struct v4l2_pix_format_mplane *pix_mp)
+{
+ mali_c55_try_fmt(pix_mp);
+
+ cap_dev->format.format = *pix_mp;
+ cap_dev->format.info = mali_c55_format_from_pix(pix_mp->pixelformat);
+}
+
+static int mali_c55_s_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct mali_c55_cap_dev *cap_dev = video_drvdata(file);
+
+ if (vb2_is_busy(&cap_dev->queue))
+ return -EBUSY;
+
+ mali_c55_set_format(cap_dev, &f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int mali_c55_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct mali_c55_cap_dev *cap_dev = video_drvdata(file);
+
+ f->fmt.pix_mp = cap_dev->format.format;
+
+ return 0;
+}
+
+static int mali_c55_enum_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct mali_c55_cap_dev *cap_dev = video_drvdata(file);
+ unsigned int j = 0;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_fmts); i++) {
+ if (f->mbus_code &&
+ !mali_c55_mbus_code_can_produce_fmt(&mali_c55_fmts[i],
+ f->mbus_code))
+ continue;
+
+ /* Downscale pipe can't output RAW formats */
+ if (mali_c55_fmts[i].is_raw &&
+ cap_dev->reg_offset == MALI_C55_CAP_DEV_DS_REG_OFFSET)
+ continue;
+
+ if (j++ == f->index) {
+ f->pixelformat = mali_c55_fmts[i].fourcc;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mali_c55_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops mali_c55_v4l2_ioctl_ops = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_try_fmt_vid_cap_mplane = mali_c55_try_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = mali_c55_s_fmt_vid_cap_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = mali_c55_g_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_cap = mali_c55_enum_fmt_vid_cap_mplane,
+ .vidioc_querycap = mali_c55_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int mali_c55_register_cap_dev(struct mali_c55 *mali_c55,
+ enum mali_c55_cap_devs cap_dev_id)
+{
+ struct mali_c55_cap_dev *cap_dev = &mali_c55->cap_devs[cap_dev_id];
+ struct v4l2_pix_format_mplane pix_mp;
+ struct video_device *vdev;
+ struct vb2_queue *vb2q;
+ int ret;
+
+ vdev = &cap_dev->vdev;
+ vb2q = &cap_dev->queue;
+
+ cap_dev->mali_c55 = mali_c55;
+ mutex_init(&cap_dev->lock);
+ INIT_LIST_HEAD(&cap_dev->buffers.input);
+ INIT_LIST_HEAD(&cap_dev->buffers.processing);
+ spin_lock_init(&cap_dev->buffers.lock);
+ spin_lock_init(&cap_dev->buffers.processing_lock);
+
+ switch (cap_dev_id) {
+ case MALI_C55_CAP_DEV_FR:
+ cap_dev->rsz = &mali_c55->resizers[MALI_C55_RSZ_FR];
+ cap_dev->reg_offset = MALI_C55_CAP_DEV_FR_REG_OFFSET;
+ break;
+ case MALI_C55_CAP_DEV_DS:
+ cap_dev->rsz = &mali_c55->resizers[MALI_C55_RSZ_DS];
+ cap_dev->reg_offset = MALI_C55_CAP_DEV_DS_REG_OFFSET;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_destroy_mutex;
+ }
+
+ cap_dev->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&cap_dev->vdev.entity, 1, &cap_dev->pad);
+ if (ret) {
+ mutex_destroy(&cap_dev->lock);
+ goto err_destroy_mutex;
+ }
+
+ vb2q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ vb2q->io_modes = VB2_MMAP | VB2_DMABUF;
+ vb2q->drv_priv = cap_dev;
+ vb2q->mem_ops = &vb2_dma_contig_memops;
+ vb2q->ops = &mali_c55_vb2_ops;
+ vb2q->buf_struct_size = sizeof(struct mali_c55_buffer);
+ vb2q->min_queued_buffers = 1;
+ vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2q->lock = &cap_dev->lock;
+ vb2q->dev = mali_c55->dev;
+
+ ret = vb2_queue_init(vb2q);
+ if (ret) {
+ dev_err(mali_c55->dev, "%s vb2 queue init failed\n",
+ cap_dev->vdev.name);
+ goto err_cleanup_media_entity;
+ }
+
+ strscpy(cap_dev->vdev.name, capture_device_names[cap_dev_id],
+ sizeof(cap_dev->vdev.name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &mali_c55_v4l2_fops;
+ vdev->ioctl_ops = &mali_c55_v4l2_ioctl_ops;
+ vdev->lock = &cap_dev->lock;
+ vdev->v4l2_dev = &mali_c55->v4l2_dev;
+ vdev->queue = &cap_dev->queue;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+ vdev->entity.ops = &mali_c55_media_ops;
+ video_set_drvdata(vdev, cap_dev);
+
+ memset(&pix_mp, 0, sizeof(pix_mp));
+ pix_mp.pixelformat = V4L2_PIX_FMT_RGB565;
+ pix_mp.width = MALI_C55_DEFAULT_WIDTH;
+ pix_mp.height = MALI_C55_DEFAULT_HEIGHT;
+ mali_c55_set_format(cap_dev, &pix_mp);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "%s failed to register video device\n",
+ cap_dev->vdev.name);
+ goto err_release_vb2q;
+ }
+
+ return 0;
+
+err_release_vb2q:
+ vb2_queue_release(vb2q);
+err_cleanup_media_entity:
+ media_entity_cleanup(&cap_dev->vdev.entity);
+err_destroy_mutex:
+ mutex_destroy(&cap_dev->lock);
+
+ return ret;
+}
+
+int mali_c55_register_capture_devs(struct mali_c55 *mali_c55)
+{
+ int ret;
+
+ ret = mali_c55_register_cap_dev(mali_c55, MALI_C55_CAP_DEV_FR);
+ if (ret)
+ return ret;
+
+ if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) {
+ ret = mali_c55_register_cap_dev(mali_c55, MALI_C55_CAP_DEV_DS);
+ if (ret) {
+ mali_c55_unregister_capture_devs(mali_c55);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void mali_c55_unregister_cap_dev(struct mali_c55 *mali_c55,
+ enum mali_c55_cap_devs cap_dev_id)
+{
+ struct mali_c55_cap_dev *cap_dev = &mali_c55->cap_devs[cap_dev_id];
+
+ if (!video_is_registered(&cap_dev->vdev))
+ return;
+
+ vb2_video_unregister_device(&cap_dev->vdev);
+ media_entity_cleanup(&cap_dev->vdev.entity);
+ mutex_destroy(&cap_dev->lock);
+}
+
+void mali_c55_unregister_capture_devs(struct mali_c55 *mali_c55)
+{
+ mali_c55_unregister_cap_dev(mali_c55, MALI_C55_CAP_DEV_FR);
+ if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED)
+ mali_c55_unregister_cap_dev(mali_c55, MALI_C55_CAP_DEV_DS);
+}
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-common.h b/drivers/media/platform/arm/mali-c55/mali-c55-common.h
new file mode 100644
index 000000000000..31c1deaca146
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-common.h
@@ -0,0 +1,310 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ARM Mali-C55 ISP Driver - Common definitions
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#ifndef _MALI_C55_COMMON_H
+#define _MALI_C55_COMMON_H
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/videodev2.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-isp.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#define MALI_C55_DRIVER_NAME "mali-c55"
+
+/* min and max values for the image sizes */
+#define MALI_C55_MIN_WIDTH 640U
+#define MALI_C55_MIN_HEIGHT 480U
+#define MALI_C55_MAX_WIDTH 8192U
+#define MALI_C55_MAX_HEIGHT 8192U
+#define MALI_C55_DEFAULT_WIDTH 1920U
+#define MALI_C55_DEFAULT_HEIGHT 1080U
+
+#define MALI_C55_DEFAULT_MEDIA_BUS_FMT MEDIA_BUS_FMT_RGB121212_1X36
+
+#define MALI_C55_NUM_CLKS 3
+#define MALI_C55_NUM_RESETS 3
+
+struct device;
+struct mali_c55;
+struct mali_c55_cap_dev;
+struct media_pipeline;
+struct mali_c55_params_buffer;
+struct platform_device;
+struct resource;
+
+enum mali_c55_isp_pads {
+ MALI_C55_ISP_PAD_SINK_VIDEO,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ MALI_C55_ISP_PAD_SOURCE_BYPASS,
+ MALI_C55_ISP_PAD_SOURCE_STATS,
+ MALI_C55_ISP_PAD_SINK_PARAMS,
+ MALI_C55_ISP_NUM_PADS,
+};
+
+struct mali_c55_tpg {
+ struct mali_c55 *mali_c55;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct mali_c55_tpg_ctrls {
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *vblank;
+ } ctrls;
+};
+
+struct mali_c55_isp {
+ struct mali_c55 *mali_c55;
+ struct v4l2_subdev sd;
+ struct media_pad pads[MALI_C55_ISP_NUM_PADS];
+ struct v4l2_ctrl_handler handler;
+ struct media_pad *remote_src;
+ /* Mutex to guard vb2 start/stop streaming */
+ struct mutex capture_lock;
+ unsigned int frame_sequence;
+};
+
+enum mali_c55_resizer_ids {
+ MALI_C55_RSZ_FR,
+ MALI_C55_RSZ_DS,
+ MALI_C55_NUM_RSZS,
+};
+
+enum mali_c55_rsz_pads {
+ MALI_C55_RSZ_SINK_PAD,
+ MALI_C55_RSZ_SOURCE_PAD,
+ MALI_C55_RSZ_SINK_BYPASS_PAD,
+ MALI_C55_RSZ_NUM_PADS
+};
+
+struct mali_c55_resizer {
+ struct mali_c55 *mali_c55;
+ struct mali_c55_cap_dev *cap_dev;
+ enum mali_c55_resizer_ids id;
+ struct v4l2_subdev sd;
+ struct media_pad pads[MALI_C55_RSZ_NUM_PADS];
+ unsigned int num_routes;
+};
+
+enum mali_c55_cap_devs {
+ MALI_C55_CAP_DEV_FR,
+ MALI_C55_CAP_DEV_DS,
+ MALI_C55_NUM_CAP_DEVS
+};
+
+struct mali_c55_format_info {
+ u32 fourcc;
+ /*
+ * The output formats can be produced by a couple of different media bus
+ * formats, depending on how the ISP is configured.
+ */
+ unsigned int mbus_codes[2];
+ bool is_raw;
+ struct {
+ u32 base_mode;
+ u32 uv_plane;
+ } registers;
+};
+
+struct mali_c55_isp_format_info {
+ u32 code;
+ u32 shifted_code;
+ bool bypass;
+ u32 order;
+};
+
+enum mali_c55_planes {
+ MALI_C55_PLANE_Y,
+ MALI_C55_PLANE_UV,
+ MALI_C55_NUM_PLANES
+};
+
+struct mali_c55_buffer {
+ struct vb2_v4l2_buffer vb;
+ unsigned int planes_pending;
+ struct list_head queue;
+ dma_addr_t addrs[MALI_C55_NUM_PLANES];
+};
+
+struct mali_c55_cap_dev {
+ struct mali_c55 *mali_c55;
+ struct mali_c55_resizer *rsz;
+ struct video_device vdev;
+ struct media_pad pad;
+ struct vb2_queue queue;
+ /* Mutex to provide to vb2 */
+ struct mutex lock;
+ unsigned int reg_offset;
+
+ struct {
+ const struct mali_c55_format_info *info;
+ struct v4l2_pix_format_mplane format;
+ } format;
+
+ struct {
+ /* Spinlock to guard buffer queue */
+ spinlock_t lock;
+ /* Spinlock to guard the queue of buffers being processed */
+ spinlock_t processing_lock;
+ struct list_head input;
+ struct list_head processing;
+ } buffers;
+};
+
+struct mali_c55_stats_buf {
+ struct vb2_v4l2_buffer vb;
+ unsigned int segments_remaining;
+ struct list_head queue;
+ bool failed;
+};
+
+struct mali_c55_params_buf {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ struct v4l2_isp_params_buffer *config;
+};
+
+struct mali_c55_stats {
+ struct mali_c55 *mali_c55;
+ struct video_device vdev;
+ struct vb2_queue queue;
+ struct media_pad pad;
+ /* Mutex to provide to vb2 */
+ struct mutex lock;
+
+ struct {
+ /* Spinlock to guard buffer queue */
+ spinlock_t lock;
+ struct list_head queue;
+ } buffers;
+};
+
+struct mali_c55_params {
+ struct mali_c55 *mali_c55;
+ struct video_device vdev;
+ struct vb2_queue queue;
+ struct media_pad pad;
+ /* Mutex to provide to vb2 */
+ struct mutex lock;
+
+ struct {
+ /* Spinlock to guard buffer queue */
+ spinlock_t lock;
+ struct list_head queue;
+ } buffers;
+};
+
+enum mali_c55_config_spaces {
+ MALI_C55_CONFIG_PONG,
+ MALI_C55_CONFIG_PING,
+};
+
+/**
+ * struct mali_c55_context - Fields relating to a single camera context
+ *
+ * @mali_c55: Pointer to the main struct mali_c55
+ * @registers: A pointer to some allocated memory holding register
+ * values to be written to the hardware at frame interrupt
+ * @base: Base address of the config space in the hardware
+ * @lock: A spinlock to protect against writes to @registers whilst that
+ * space is being copied to the hardware
+ * @list: A list head to facilitate a context queue
+ */
+struct mali_c55_context {
+ struct mali_c55 *mali_c55;
+ u32 *registers;
+ phys_addr_t base;
+ /* Spinlock to prevent simultaneous access of register space */
+ spinlock_t lock;
+ struct list_head list;
+};
+
+struct mali_c55 {
+ struct device *dev;
+ void __iomem *base;
+ struct clk_bulk_data clks[MALI_C55_NUM_CLKS];
+ struct reset_control_bulk_data resets[MALI_C55_NUM_RESETS];
+ int irqnum;
+
+ u16 capabilities;
+ bool inline_mode;
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_async_notifier notifier;
+ struct media_pipeline pipe;
+
+ struct mali_c55_tpg tpg;
+ struct mali_c55_isp isp;
+ struct mali_c55_resizer resizers[MALI_C55_NUM_RSZS];
+ struct mali_c55_cap_dev cap_devs[MALI_C55_NUM_CAP_DEVS];
+ struct mali_c55_params params;
+ struct mali_c55_stats stats;
+
+ struct mali_c55_context context;
+ u32 next_config;
+};
+
+void mali_c55_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val);
+void mali_c55_cap_dev_write(struct mali_c55_cap_dev *cap_dev, unsigned int addr,
+ u32 val);
+void mali_c55_update_bits(struct mali_c55 *mali_c55, unsigned int addr,
+ u32 mask, u32 val);
+u32 mali_c55_read(struct mali_c55 *mali_c55, unsigned int addr);
+void mali_c55_ctx_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val);
+u32 mali_c55_ctx_read(struct mali_c55 *mali_c55, unsigned int addr);
+void mali_c55_ctx_update_bits(struct mali_c55 *mali_c55, unsigned int addr,
+ u32 mask, u32 val);
+
+int mali_c55_config_write(struct mali_c55_context *ctx,
+ enum mali_c55_config_spaces cfg_space,
+ bool force_synchronous);
+
+int mali_c55_register_isp(struct mali_c55 *mali_c55);
+int mali_c55_register_tpg(struct mali_c55 *mali_c55);
+void mali_c55_unregister_tpg(struct mali_c55 *mali_c55);
+void mali_c55_unregister_isp(struct mali_c55 *mali_c55);
+int mali_c55_register_resizers(struct mali_c55 *mali_c55);
+void mali_c55_unregister_resizers(struct mali_c55 *mali_c55);
+int mali_c55_register_capture_devs(struct mali_c55 *mali_c55);
+void mali_c55_unregister_capture_devs(struct mali_c55 *mali_c55);
+int mali_c55_register_stats(struct mali_c55 *mali_c55);
+void mali_c55_unregister_stats(struct mali_c55 *mali_c55);
+int mali_c55_register_params(struct mali_c55 *mali_c55);
+void mali_c55_unregister_params(struct mali_c55 *mali_c55);
+struct mali_c55_context *mali_c55_get_active_context(struct mali_c55 *mali_c55);
+void mali_c55_set_plane_done(struct mali_c55_cap_dev *cap_dev,
+ enum mali_c55_planes plane);
+void mali_c55_set_next_buffer(struct mali_c55_cap_dev *cap_dev);
+void mali_c55_isp_queue_event_sof(struct mali_c55 *mali_c55);
+
+bool mali_c55_format_is_raw(unsigned int mbus_code);
+
+const struct mali_c55_isp_format_info *
+mali_c55_isp_fmt_next(const struct mali_c55_isp_format_info *fmt);
+const struct mali_c55_isp_format_info *
+mali_c55_isp_get_mbus_config_by_code(u32 code);
+const struct mali_c55_isp_format_info *
+mali_c55_isp_get_mbus_config_by_shifted_code(u32 code);
+const struct mali_c55_isp_format_info *
+mali_c55_isp_get_mbus_config_by_index(u32 index);
+bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55);
+void mali_c55_stats_fill_buffer(struct mali_c55 *mali_c55,
+ enum mali_c55_config_spaces cfg_space);
+void mali_c55_params_write_config(struct mali_c55 *mali_c55);
+
+#endif /* _MALI_C55_COMMON_H */
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
new file mode 100644
index 000000000000..43b834459ccf
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
@@ -0,0 +1,917 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Mali-C55 ISP Driver - Core driver code
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/ioport.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mali-c55-common.h"
+#include "mali-c55-registers.h"
+
+static const char * const mali_c55_interrupt_names[] = {
+ [MALI_C55_IRQ_ISP_START] = "ISP start",
+ [MALI_C55_IRQ_ISP_DONE] = "ISP done",
+ [MALI_C55_IRQ_MCM_ERROR] = "Multi-context management error",
+ [MALI_C55_IRQ_BROKEN_FRAME_ERROR] = "Broken frame error",
+ [MALI_C55_IRQ_MET_AF_DONE] = "AF metering done",
+ [MALI_C55_IRQ_MET_AEXP_DONE] = "AEXP metering done",
+ [MALI_C55_IRQ_MET_AWB_DONE] = "AWB metering done",
+ [MALI_C55_IRQ_AEXP_1024_DONE] = "AEXP 1024-bit histogram done",
+ [MALI_C55_IRQ_IRIDIX_MET_DONE] = "Iridix metering done",
+ [MALI_C55_IRQ_LUT_INIT_DONE] = "LUT memory init done",
+ [MALI_C55_IRQ_FR_Y_DONE] = "Full resolution Y plane DMA done",
+ [MALI_C55_IRQ_FR_UV_DONE] = "Full resolution U/V plane DMA done",
+ [MALI_C55_IRQ_DS_Y_DONE] = "Downscale Y plane DMA done",
+ [MALI_C55_IRQ_DS_UV_DONE] = "Downscale U/V plane DMA done",
+ [MALI_C55_IRQ_LINEARIZATION_DONE] = "Linearisation done",
+ [MALI_C55_IRQ_RAW_FRONTEND_DONE] = "Raw frontend processing done",
+ [MALI_C55_IRQ_NOISE_REDUCTION_DONE] = "Noise reduction done",
+ [MALI_C55_IRQ_IRIDIX_DONE] = "Iridix done",
+ [MALI_C55_IRQ_BAYER2RGB_DONE] = "Bayer to RGB conversion done",
+ [MALI_C55_IRQ_WATCHDOG_TIMER] = "Watchdog timer timed out",
+ [MALI_C55_IRQ_FRAME_COLLISION] = "Frame collision error",
+ [MALI_C55_IRQ_UNUSED] = "IRQ bit unused",
+ [MALI_C55_IRQ_DMA_ERROR] = "DMA error",
+ [MALI_C55_IRQ_INPUT_STOPPED] = "Input port safely stopped",
+ [MALI_C55_IRQ_MET_AWB_TARGET1_HIT] = "AWB metering target 1 address hit",
+ [MALI_C55_IRQ_MET_AWB_TARGET2_HIT] = "AWB metering target 2 address hit"
+};
+
+static const unsigned int config_space_addrs[] = {
+ [MALI_C55_CONFIG_PING] = 0x0ab6c,
+ [MALI_C55_CONFIG_PONG] = 0x22b2c,
+};
+
+static const char * const mali_c55_clk_names[MALI_C55_NUM_CLKS] = {
+ "vclk",
+ "aclk",
+ "hclk",
+};
+
+static const char * const mali_c55_reset_names[MALI_C55_NUM_RESETS] = {
+ "vresetn",
+ "aresetn",
+ "hresetn",
+};
+
+/*
+ * System IO
+ *
+ * The Mali-C55 ISP has up to two configuration register spaces (called 'ping'
+ * and 'pong'), with the expectation that the 'active' space will be left
+ * untouched whilst a frame is being processed and the 'inactive' space
+ * configured ready to be switched to during the blanking period before the next
+ * frame processing starts. These spaces should ideally be set via DMA transfer
+ * from a buffer rather than through individual register set operations. There
+ * is also a shared global register space which should be set normally. For now
+ * though we will simply use a CPU write and target DMA transfers of the config
+ * space in the future.
+ *
+ * As groundwork for that path any read/write call that is made to an address
+ * within those config spaces should infact be directed to a buffer that was
+ * allocated to hold them rather than the IO memory itself. The actual copy of
+ * that buffer to IO mem will happen on interrupt.
+ */
+
+void mali_c55_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val)
+{
+ WARN_ON(addr >= MALI_C55_REG_CONFIG_SPACES_OFFSET);
+
+ writel(val, mali_c55->base + addr);
+}
+
+u32 mali_c55_read(struct mali_c55 *mali_c55, unsigned int addr)
+{
+ WARN_ON(addr >= MALI_C55_REG_CONFIG_SPACES_OFFSET);
+
+ return readl(mali_c55->base + addr);
+}
+
+void mali_c55_update_bits(struct mali_c55 *mali_c55, unsigned int addr,
+ u32 mask, u32 val)
+{
+ u32 orig, new;
+
+ orig = mali_c55_read(mali_c55, addr);
+
+ new = orig & ~mask;
+ new |= val & mask;
+
+ if (new != orig)
+ mali_c55_write(mali_c55, addr, new);
+}
+
+static void __mali_c55_ctx_write(struct mali_c55_context *ctx,
+ unsigned int addr, u32 val)
+{
+ addr = (addr - MALI_C55_REG_CONFIG_SPACES_OFFSET) / 4;
+ ctx->registers[addr] = val;
+}
+
+void mali_c55_ctx_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val)
+{
+ struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55);
+
+ WARN_ON(addr < MALI_C55_REG_CONFIG_SPACES_OFFSET);
+
+ spin_lock(&ctx->lock);
+ __mali_c55_ctx_write(ctx, addr, val);
+ spin_unlock(&ctx->lock);
+}
+
+static u32 __mali_c55_ctx_read(struct mali_c55_context *ctx, unsigned int addr)
+{
+ addr = (addr - MALI_C55_REG_CONFIG_SPACES_OFFSET) / 4;
+ return ctx->registers[addr];
+}
+
+u32 mali_c55_ctx_read(struct mali_c55 *mali_c55, unsigned int addr)
+{
+ struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55);
+ u32 val;
+
+ WARN_ON(addr < MALI_C55_REG_CONFIG_SPACES_OFFSET);
+
+ spin_lock(&ctx->lock);
+ val = __mali_c55_ctx_read(ctx, addr);
+ spin_unlock(&ctx->lock);
+
+ return val;
+}
+
+void mali_c55_ctx_update_bits(struct mali_c55 *mali_c55, unsigned int addr,
+ u32 mask, u32 val)
+{
+ struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55);
+ u32 orig, tmp;
+
+ WARN_ON(addr < MALI_C55_REG_CONFIG_SPACES_OFFSET);
+
+ spin_lock(&ctx->lock);
+
+ orig = __mali_c55_ctx_read(ctx, addr);
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ if (tmp != orig)
+ __mali_c55_ctx_write(ctx, addr, tmp);
+
+ spin_unlock(&ctx->lock);
+}
+
+int mali_c55_config_write(struct mali_c55_context *ctx,
+ enum mali_c55_config_spaces cfg_space,
+ bool force_synchronous)
+{
+ struct mali_c55 *mali_c55 = ctx->mali_c55;
+
+ memcpy_toio(mali_c55->base + config_space_addrs[cfg_space],
+ ctx->registers, MALI_C55_CONFIG_SPACE_SIZE);
+
+ return 0;
+}
+
+struct mali_c55_context *mali_c55_get_active_context(struct mali_c55 *mali_c55)
+{
+ return &mali_c55->context;
+}
+
+static void mali_c55_remove_links(struct mali_c55 *mali_c55)
+{
+ unsigned int i;
+
+ media_entity_remove_links(&mali_c55->tpg.sd.entity);
+ media_entity_remove_links(&mali_c55->isp.sd.entity);
+
+ for (i = 0; i < MALI_C55_NUM_RSZS; i++)
+ media_entity_remove_links(&mali_c55->resizers[i].sd.entity);
+
+ for (i = 0; i < MALI_C55_NUM_CAP_DEVS; i++)
+ media_entity_remove_links(&mali_c55->cap_devs[i].vdev.entity);
+}
+
+static int mali_c55_create_links(struct mali_c55 *mali_c55)
+{
+ int ret;
+
+ /* Test pattern generator to ISP */
+ ret = media_create_pad_link(&mali_c55->tpg.sd.entity, 0,
+ &mali_c55->isp.sd.entity,
+ MALI_C55_ISP_PAD_SINK_VIDEO, 0);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to link TPG and ISP\n");
+ goto err_remove_links;
+ }
+
+ /* Full resolution resizer pipe. */
+ ret = media_create_pad_link(&mali_c55->isp.sd.entity,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ &mali_c55->resizers[MALI_C55_RSZ_FR].sd.entity,
+ MALI_C55_RSZ_SINK_PAD,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to link ISP and FR resizer\n");
+ goto err_remove_links;
+ }
+
+ /* Full resolution bypass. */
+ ret = media_create_pad_link(&mali_c55->isp.sd.entity,
+ MALI_C55_ISP_PAD_SOURCE_BYPASS,
+ &mali_c55->resizers[MALI_C55_RSZ_FR].sd.entity,
+ MALI_C55_RSZ_SINK_BYPASS_PAD,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to link ISP and FR resizer\n");
+ goto err_remove_links;
+ }
+
+ /* Resizer pipe to video capture nodes. */
+ ret = media_create_pad_link(&mali_c55->resizers[0].sd.entity,
+ MALI_C55_RSZ_SOURCE_PAD,
+ &mali_c55->cap_devs[MALI_C55_CAP_DEV_FR].vdev.entity,
+ 0, MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "failed to link FR resizer and video device\n");
+ goto err_remove_links;
+ }
+
+ /* The downscale pipe is an optional hardware block */
+ if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) {
+ ret = media_create_pad_link(&mali_c55->isp.sd.entity,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ &mali_c55->resizers[MALI_C55_RSZ_DS].sd.entity,
+ MALI_C55_RSZ_SINK_PAD,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "failed to link ISP and DS resizer\n");
+ goto err_remove_links;
+ }
+
+ ret = media_create_pad_link(&mali_c55->resizers[1].sd.entity,
+ MALI_C55_RSZ_SOURCE_PAD,
+ &mali_c55->cap_devs[MALI_C55_CAP_DEV_DS].vdev.entity,
+ 0, MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "failed to link DS resizer and video device\n");
+ goto err_remove_links;
+ }
+ }
+
+ ret = media_create_pad_link(&mali_c55->isp.sd.entity,
+ MALI_C55_ISP_PAD_SOURCE_STATS,
+ &mali_c55->stats.vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "failed to link ISP and 3a stats node\n");
+ goto err_remove_links;
+ }
+
+ ret = media_create_pad_link(&mali_c55->params.vdev.entity, 0,
+ &mali_c55->isp.sd.entity,
+ MALI_C55_ISP_PAD_SINK_PARAMS,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "failed to link ISP and parameters video node\n");
+ goto err_remove_links;
+ }
+
+ return 0;
+
+err_remove_links:
+ mali_c55_remove_links(mali_c55);
+ return ret;
+}
+
+static void mali_c55_unregister_entities(struct mali_c55 *mali_c55)
+{
+ mali_c55_remove_links(mali_c55);
+ mali_c55_unregister_tpg(mali_c55);
+ mali_c55_unregister_isp(mali_c55);
+ mali_c55_unregister_resizers(mali_c55);
+ mali_c55_unregister_capture_devs(mali_c55);
+ mali_c55_unregister_params(mali_c55);
+ mali_c55_unregister_stats(mali_c55);
+}
+
+static void mali_c55_swap_next_config(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55);
+
+ mali_c55_config_write(ctx, mali_c55->next_config ?
+ MALI_C55_CONFIG_PING : MALI_C55_CONFIG_PONG,
+ false);
+
+ mali_c55_update_bits(mali_c55, MALI_C55_REG_MCU_CONFIG,
+ MALI_C55_REG_MCU_CONFIG_WRITE_MASK,
+ MALI_C55_MCU_CONFIG_WRITE(mali_c55->next_config));
+}
+
+static int mali_c55_register_entities(struct mali_c55 *mali_c55)
+{
+ int ret;
+
+ ret = mali_c55_register_tpg(mali_c55);
+ if (ret)
+ return ret;
+
+ ret = mali_c55_register_isp(mali_c55);
+ if (ret)
+ goto err_unregister_entities;
+
+ ret = mali_c55_register_resizers(mali_c55);
+ if (ret)
+ goto err_unregister_entities;
+
+ ret = mali_c55_register_capture_devs(mali_c55);
+ if (ret)
+ goto err_unregister_entities;
+
+ ret = mali_c55_register_params(mali_c55);
+ if (ret)
+ goto err_unregister_entities;
+
+ ret = mali_c55_register_stats(mali_c55);
+ if (ret)
+ goto err_unregister_entities;
+
+ ret = mali_c55_create_links(mali_c55);
+ if (ret)
+ goto err_unregister_entities;
+
+ return 0;
+
+err_unregister_entities:
+ mali_c55_unregister_entities(mali_c55);
+
+ return ret;
+}
+
+static int mali_c55_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asc)
+{
+ struct mali_c55 *mali_c55 = container_of(notifier,
+ struct mali_c55, notifier);
+ struct media_pad *pad = &mali_c55->isp.pads[MALI_C55_ISP_PAD_SINK_VIDEO];
+ int ret;
+
+ /*
+ * By default we'll flag this link enabled and the TPG disabled, but
+ * no immutable flag because we need to be able to switch between the
+ * two.
+ */
+ ret = v4l2_create_fwnode_links_to_pad(subdev, pad,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ dev_err(mali_c55->dev, "failed to create link for %s\n",
+ subdev->name);
+
+ return ret;
+}
+
+static int mali_c55_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct mali_c55 *mali_c55 = container_of(notifier,
+ struct mali_c55, notifier);
+
+ return v4l2_device_register_subdev_nodes(&mali_c55->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations mali_c55_notifier_ops = {
+ .bound = mali_c55_notifier_bound,
+ .complete = mali_c55_notifier_complete,
+};
+
+static int mali_c55_parse_endpoint(struct mali_c55 *mali_c55)
+{
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *ep;
+
+ /*
+ * The ISP should have a single endpoint pointing to some flavour of
+ * CSI-2 receiver...but for now at least we do want everything to work
+ * normally even with no sensors connected, as we have the TPG. If we
+ * don't find a sensor just warn and return success.
+ */
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(mali_c55->dev),
+ 0, 0, 0);
+ if (!ep) {
+ dev_warn(mali_c55->dev, "no local endpoint found\n");
+ return 0;
+ }
+
+ asc = v4l2_async_nf_add_fwnode_remote(&mali_c55->notifier, ep,
+ struct v4l2_async_connection);
+ fwnode_handle_put(ep);
+ if (IS_ERR(asc)) {
+ dev_err(mali_c55->dev, "failed to add remote fwnode\n");
+ return PTR_ERR(asc);
+ }
+
+ return 0;
+}
+
+static int mali_c55_media_frameworks_init(struct mali_c55 *mali_c55)
+{
+ int ret;
+
+ strscpy(mali_c55->media_dev.model, "ARM Mali-C55 ISP",
+ sizeof(mali_c55->media_dev.model));
+
+ media_device_init(&mali_c55->media_dev);
+
+ ret = media_device_register(&mali_c55->media_dev);
+ if (ret)
+ goto err_cleanup_media_device;
+
+ mali_c55->v4l2_dev.mdev = &mali_c55->media_dev;
+ ret = v4l2_device_register(mali_c55->dev, &mali_c55->v4l2_dev);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to register V4L2 device\n");
+ goto err_unregister_media_device;
+ };
+
+ mali_c55->notifier.ops = &mali_c55_notifier_ops;
+ v4l2_async_nf_init(&mali_c55->notifier, &mali_c55->v4l2_dev);
+
+ ret = mali_c55_register_entities(mali_c55);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to register entities\n");
+ goto err_cleanup_nf;
+ }
+
+ ret = mali_c55_parse_endpoint(mali_c55);
+ if (ret)
+ goto err_cleanup_nf;
+
+ ret = v4l2_async_nf_register(&mali_c55->notifier);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to register notifier\n");
+ goto err_unregister_entities;
+ }
+
+ return 0;
+
+err_unregister_entities:
+ mali_c55_unregister_entities(mali_c55);
+err_cleanup_nf:
+ v4l2_async_nf_cleanup(&mali_c55->notifier);
+ v4l2_device_unregister(&mali_c55->v4l2_dev);
+err_unregister_media_device:
+ media_device_unregister(&mali_c55->media_dev);
+err_cleanup_media_device:
+ media_device_cleanup(&mali_c55->media_dev);
+
+ return ret;
+}
+
+static void mali_c55_media_frameworks_deinit(struct mali_c55 *mali_c55)
+{
+ v4l2_async_nf_unregister(&mali_c55->notifier);
+ mali_c55_unregister_entities(mali_c55);
+ v4l2_async_nf_cleanup(&mali_c55->notifier);
+ v4l2_device_unregister(&mali_c55->v4l2_dev);
+ media_device_unregister(&mali_c55->media_dev);
+ media_device_cleanup(&mali_c55->media_dev);
+}
+
+bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_cap_dev *fr = &mali_c55->cap_devs[MALI_C55_CAP_DEV_FR];
+ struct mali_c55_cap_dev *ds = &mali_c55->cap_devs[MALI_C55_CAP_DEV_DS];
+ struct mali_c55_params *params = &mali_c55->params;
+ struct mali_c55_stats *stats = &mali_c55->stats;
+
+ return vb2_start_streaming_called(&fr->queue) &&
+ (!(mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) ||
+ vb2_start_streaming_called(&ds->queue)) &&
+ vb2_start_streaming_called(&params->queue) &&
+ vb2_start_streaming_called(&stats->queue);
+}
+
+static int mali_c55_check_hwcfg(struct mali_c55 *mali_c55)
+{
+ u32 product, version, revision, capabilities;
+
+ product = mali_c55_read(mali_c55, MALI_C55_REG_PRODUCT);
+ version = mali_c55_read(mali_c55, MALI_C55_REG_VERSION);
+ revision = mali_c55_read(mali_c55, MALI_C55_REG_REVISION);
+
+ mali_c55->media_dev.hw_revision = version;
+
+ dev_info(mali_c55->dev, "Detected Mali-C55 ISP %u.%u.%u\n",
+ product, version, revision);
+
+ capabilities = mali_c55_read(mali_c55,
+ MALI_C55_REG_GLOBAL_PARAMETER_STATUS);
+
+ /*
+ * In its current iteration, the driver only supports inline mode. Given
+ * we cannot control input data timing in this mode, we cannot guarantee
+ * that the vertical blanking periods between frames will be long enough
+ * for us to write configuration data to the ISP during them. For that
+ * reason we can't really support single config space configuration
+ * until memory input mode is implemented.
+ */
+ if (!(capabilities & MALI_C55_GPS_PONG_FITTED)) {
+ dev_err(mali_c55->dev, "Pong config space not fitted.\n");
+ return -EINVAL;
+ }
+
+ mali_c55->capabilities = capabilities & 0xffff;
+
+ return 0;
+}
+
+static irqreturn_t mali_c55_isr(int irq, void *context)
+{
+ struct device *dev = context;
+ struct mali_c55 *mali_c55 = dev_get_drvdata(dev);
+ unsigned long interrupt_status;
+ u32 curr_config;
+ unsigned int i;
+
+ interrupt_status = mali_c55_read(mali_c55,
+ MALI_C55_REG_INTERRUPT_STATUS_VECTOR);
+ if (!interrupt_status)
+ return IRQ_NONE;
+
+ mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR_VECTOR,
+ interrupt_status);
+ mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 1);
+ mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 0);
+
+ for_each_set_bit(i, &interrupt_status, MALI_C55_NUM_IRQ_BITS) {
+ switch (i) {
+ case MALI_C55_IRQ_ISP_START:
+ mali_c55_isp_queue_event_sof(mali_c55);
+
+ mali_c55_set_next_buffer(&mali_c55->cap_devs[MALI_C55_CAP_DEV_FR]);
+ if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED)
+ mali_c55_set_next_buffer(&mali_c55->cap_devs[MALI_C55_CAP_DEV_DS]);
+
+ /*
+ * When the ISP starts a frame we have some work to do:
+ *
+ * 1. Copy over the config for the **next** frame
+ * 2. Read out the metering stats for the **last** frame
+ */
+
+ curr_config = mali_c55_read(mali_c55,
+ MALI_C55_REG_PING_PONG_READ);
+ curr_config &= MALI_C55_REG_PING_PONG_READ_MASK;
+ curr_config >>= ffs(MALI_C55_REG_PING_PONG_READ_MASK) - 1;
+ mali_c55->next_config = curr_config ^ 1;
+
+ /*
+ * Write the configuration parameters received from
+ * userspace into the configuration buffer, which will
+ * be transferred to the 'next' active config space at
+ * by mali_c55_swap_next_config().
+ */
+ mali_c55_params_write_config(mali_c55);
+
+ mali_c55_stats_fill_buffer(mali_c55,
+ mali_c55->next_config ^ 1);
+
+ mali_c55_swap_next_config(mali_c55);
+
+ break;
+ case MALI_C55_IRQ_ISP_DONE:
+ /*
+ * TODO: Where the ISP has no Pong config fitted, we'd
+ * have to do the mali_c55_swap_next_config() call here.
+ */
+ break;
+ case MALI_C55_IRQ_FR_Y_DONE:
+ mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_FR],
+ MALI_C55_PLANE_Y);
+ break;
+ case MALI_C55_IRQ_FR_UV_DONE:
+ mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_FR],
+ MALI_C55_PLANE_UV);
+ break;
+ case MALI_C55_IRQ_DS_Y_DONE:
+ mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_DS],
+ MALI_C55_PLANE_Y);
+ break;
+ case MALI_C55_IRQ_DS_UV_DONE:
+ mali_c55_set_plane_done(&mali_c55->cap_devs[MALI_C55_CAP_DEV_DS],
+ MALI_C55_PLANE_UV);
+ break;
+ default:
+ /*
+ * Only the above interrupts are currently unmasked. If
+ * we receive anything else here then something weird
+ * has gone on.
+ */
+ dev_err(dev, "masked interrupt %s triggered\n",
+ mali_c55_interrupt_names[i]);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mali_c55_init_context(struct mali_c55 *mali_c55,
+ struct resource *res)
+{
+ struct mali_c55_context *ctx = &mali_c55->context;
+
+ ctx->base = res->start;
+ ctx->mali_c55 = mali_c55;
+ spin_lock_init(&ctx->lock);
+
+ ctx->registers = kzalloc(MALI_C55_CONFIG_SPACE_SIZE, GFP_KERNEL);
+ if (!ctx->registers)
+ return -ENOMEM;
+
+ /*
+ * The allocated memory is empty, we need to load the default
+ * register settings. We just read Ping; it's identical to Pong.
+ */
+ memcpy_fromio(ctx->registers,
+ mali_c55->base + config_space_addrs[MALI_C55_CONFIG_PING],
+ MALI_C55_CONFIG_SPACE_SIZE);
+
+ /*
+ * Some features of the ISP need to be disabled by default and only
+ * enabled at the same time as they're configured by a parameters buffer
+ */
+
+ /* Bypass the sqrt and square compression and expansion modules */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_1,
+ MALI_C55_REG_BYPASS_1_FE_SQRT,
+ MALI_C55_REG_BYPASS_1_FE_SQRT);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
+ MALI_C55_REG_BYPASS_3_SQUARE_BE,
+ MALI_C55_REG_BYPASS_3_SQUARE_BE);
+
+ /* Bypass the temper module */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_2,
+ MALI_C55_REG_BYPASS_2_TEMPER);
+
+ /* Disable the temper module's DMA read/write */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_TEMPER_DMA_IO, 0x0);
+
+ /* Bypass the colour noise reduction */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_BYPASS_4,
+ MALI_C55_REG_BYPASS_4_CNR);
+
+ /* Disable the sinter module */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_SINTER_CONFIG,
+ MALI_C55_SINTER_ENABLE_MASK, 0);
+
+ /* Disable the RGB Gamma module for each output */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_FR_GAMMA_RGB_ENABLE, 0);
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_DS_GAMMA_RGB_ENABLE, 0);
+
+ /* Disable the colour correction matrix */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_CCM_ENABLE, 0);
+
+ return 0;
+}
+
+static void __mali_c55_power_off(struct mali_c55 *mali_c55)
+{
+ reset_control_bulk_assert(ARRAY_SIZE(mali_c55->resets), mali_c55->resets);
+ clk_bulk_disable_unprepare(ARRAY_SIZE(mali_c55->clks), mali_c55->clks);
+}
+
+static int __maybe_unused mali_c55_runtime_suspend(struct device *dev)
+{
+ struct mali_c55 *mali_c55 = dev_get_drvdata(dev);
+
+ if (irq_has_action(mali_c55->irqnum))
+ free_irq(mali_c55->irqnum, dev);
+ __mali_c55_power_off(mali_c55);
+
+ return 0;
+}
+
+static int __mali_c55_power_on(struct mali_c55 *mali_c55)
+{
+ int ret;
+ u32 val;
+
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(mali_c55->clks),
+ mali_c55->clks);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ ret = reset_control_bulk_deassert(ARRAY_SIZE(mali_c55->resets),
+ mali_c55->resets);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to deassert resets\n");
+ return ret;
+ }
+
+ /* Use "software only" context management. */
+ mali_c55_update_bits(mali_c55, MALI_C55_REG_MCU_CONFIG,
+ MALI_C55_REG_MCU_CONFIG_OVERRIDE_MASK, 0x01);
+
+ /*
+ * Mask the interrupts and clear any that were set, then unmask the ones
+ * that we actually want to handle.
+ */
+ mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_MASK_VECTOR,
+ MALI_C55_INTERRUPT_MASK_ALL);
+ mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR_VECTOR,
+ MALI_C55_INTERRUPT_MASK_ALL);
+ mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 0x01);
+ mali_c55_write(mali_c55, MALI_C55_REG_INTERRUPT_CLEAR, 0x00);
+
+ mali_c55_update_bits(mali_c55, MALI_C55_REG_INTERRUPT_MASK_VECTOR,
+ MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_ISP_START) |
+ MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_ISP_DONE) |
+ MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_FR_Y_DONE) |
+ MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_FR_UV_DONE) |
+ MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_DS_Y_DONE) |
+ MALI_C55_INTERRUPT_BIT(MALI_C55_IRQ_DS_UV_DONE),
+ 0x00);
+
+ /* Set safe stop to ensure we're in a non-streaming state */
+ mali_c55_write(mali_c55, MALI_C55_REG_INPUT_MODE_REQUEST,
+ MALI_C55_INPUT_SAFE_STOP);
+ readl_poll_timeout(mali_c55->base + MALI_C55_REG_MODE_STATUS,
+ val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
+
+ return 0;
+}
+
+static int __maybe_unused mali_c55_runtime_resume(struct device *dev)
+{
+ struct mali_c55 *mali_c55 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = __mali_c55_power_on(mali_c55);
+ if (ret)
+ return ret;
+
+ /*
+ * The driver needs to transfer large amounts of register settings to
+ * the ISP each frame, using either a DMA transfer or memcpy. We use a
+ * threaded IRQ to avoid disabling interrupts the entire time that's
+ * happening.
+ */
+ ret = request_threaded_irq(mali_c55->irqnum, NULL, mali_c55_isr,
+ IRQF_ONESHOT, dev_driver_string(dev), dev);
+ if (ret) {
+ __mali_c55_power_off(mali_c55);
+ dev_err(dev, "failed to request irq\n");
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops mali_c55_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(mali_c55_runtime_suspend, mali_c55_runtime_resume,
+ NULL)
+};
+
+static int mali_c55_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mali_c55 *mali_c55;
+ struct resource *res;
+ int ret;
+
+ mali_c55 = devm_kzalloc(dev, sizeof(*mali_c55), GFP_KERNEL);
+ if (!mali_c55)
+ return -ENOMEM;
+
+ mali_c55->dev = dev;
+ platform_set_drvdata(pdev, mali_c55);
+
+ mali_c55->base = devm_platform_get_and_ioremap_resource(pdev, 0,
+ &res);
+ if (IS_ERR(mali_c55->base))
+ return dev_err_probe(dev, PTR_ERR(mali_c55->base),
+ "failed to map IO memory\n");
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(mali_c55_clk_names); i++)
+ mali_c55->clks[i].id = mali_c55_clk_names[i];
+
+ ret = devm_clk_bulk_get(dev, ARRAY_SIZE(mali_c55->clks), mali_c55->clks);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to acquire clocks\n");
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(mali_c55_reset_names); i++)
+ mali_c55->resets[i].id = mali_c55_reset_names[i];
+
+ ret = devm_reset_control_bulk_get_optional_shared(dev,
+ ARRAY_SIZE(mali_c55_reset_names), mali_c55->resets);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to acquire resets\n");
+
+ of_reserved_mem_device_init(dev);
+ vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
+
+ ret = __mali_c55_power_on(mali_c55);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to power on\n");
+
+ ret = mali_c55_check_hwcfg(mali_c55);
+ if (ret)
+ goto err_power_off;
+
+ ret = mali_c55_init_context(mali_c55, res);
+ if (ret)
+ goto err_power_off;
+
+ mali_c55->media_dev.dev = dev;
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = mali_c55_media_frameworks_init(mali_c55);
+ if (ret)
+ goto err_free_context_registers;
+
+ pm_runtime_idle(&pdev->dev);
+
+ mali_c55->irqnum = platform_get_irq(pdev, 0);
+ if (mali_c55->irqnum < 0) {
+ ret = mali_c55->irqnum;
+ dev_err(dev, "failed to get interrupt\n");
+ goto err_deinit_media_frameworks;
+ }
+
+ return 0;
+
+err_deinit_media_frameworks:
+ mali_c55_media_frameworks_deinit(mali_c55);
+ pm_runtime_disable(&pdev->dev);
+err_free_context_registers:
+ kfree(mali_c55->context.registers);
+err_power_off:
+ __mali_c55_power_off(mali_c55);
+
+ return ret;
+}
+
+static void mali_c55_remove(struct platform_device *pdev)
+{
+ struct mali_c55 *mali_c55 = platform_get_drvdata(pdev);
+
+ kfree(mali_c55->context.registers);
+ mali_c55_media_frameworks_deinit(mali_c55);
+}
+
+static const struct of_device_id mali_c55_of_match[] = {
+ { .compatible = "arm,mali-c55", },
+ { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mali_c55_of_match);
+
+static struct platform_driver mali_c55_driver = {
+ .driver = {
+ .name = "mali-c55",
+ .of_match_table = mali_c55_of_match,
+ .pm = &mali_c55_pm_ops,
+ },
+ .probe = mali_c55_probe,
+ .remove = mali_c55_remove,
+};
+
+module_platform_driver(mali_c55_driver);
+
+MODULE_AUTHOR("Daniel Scally <dan.scally@ideasonboard.com>");
+MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com>");
+MODULE_DESCRIPTION("ARM Mali-C55 ISP platform driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
new file mode 100644
index 000000000000..497f25fbdd13
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
@@ -0,0 +1,665 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Mali-C55 ISP Driver - Image signal processor
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include <linux/media/arm/mali-c55-config.h>
+
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/property.h>
+#include <linux/string.h>
+
+#include <uapi/linux/media/arm/mali-c55-config.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#include "mali-c55-common.h"
+#include "mali-c55-registers.h"
+
+static const struct mali_c55_isp_format_info mali_c55_isp_fmts[] = {
+ {
+ .code = MEDIA_BUS_FMT_SRGGB20_1X20,
+ .shifted_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .order = MALI_C55_BAYER_ORDER_RGGB,
+ .bypass = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG20_1X20,
+ .shifted_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .order = MALI_C55_BAYER_ORDER_GRBG,
+ .bypass = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG20_1X20,
+ .shifted_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .order = MALI_C55_BAYER_ORDER_GBRG,
+ .bypass = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR20_1X20,
+ .shifted_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .order = MALI_C55_BAYER_ORDER_BGGR,
+ .bypass = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_RGB202020_1X60,
+ .shifted_code = 0, /* Not relevant for this format */
+ .order = 0, /* Not relevant for this format */
+ .bypass = true,
+ }
+ /*
+ * TODO: Support MEDIA_BUS_FMT_YUV20_1X60 here. This is so that we can
+ * also support YUV input from a sensor passed-through to the output. At
+ * present we have no mechanism to test that though so it may have to
+ * wait a while...
+ */
+};
+
+const struct mali_c55_isp_format_info *
+mali_c55_isp_get_mbus_config_by_index(u32 index)
+{
+ if (index < ARRAY_SIZE(mali_c55_isp_fmts))
+ return &mali_c55_isp_fmts[index];
+
+ return NULL;
+}
+
+const struct mali_c55_isp_format_info *
+mali_c55_isp_get_mbus_config_by_code(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_isp_fmts); i++) {
+ if (mali_c55_isp_fmts[i].code == code)
+ return &mali_c55_isp_fmts[i];
+ }
+
+ return NULL;
+}
+
+const struct mali_c55_isp_format_info *
+mali_c55_isp_get_mbus_config_by_shifted_code(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_isp_fmts); i++) {
+ if (mali_c55_isp_fmts[i].shifted_code == code)
+ return &mali_c55_isp_fmts[i];
+ }
+
+ return NULL;
+}
+
+static void mali_c55_isp_stop(struct mali_c55 *mali_c55)
+{
+ u32 val;
+
+ mali_c55_write(mali_c55, MALI_C55_REG_INPUT_MODE_REQUEST,
+ MALI_C55_INPUT_SAFE_STOP);
+ readl_poll_timeout(mali_c55->base + MALI_C55_REG_MODE_STATUS,
+ val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
+}
+
+static int mali_c55_isp_start(struct mali_c55 *mali_c55,
+ const struct v4l2_subdev_state *state)
+{
+ struct mali_c55_context *ctx = mali_c55_get_active_context(mali_c55);
+ const struct mali_c55_isp_format_info *cfg;
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
+ u32 val;
+ int ret;
+
+ mali_c55_update_bits(mali_c55, MALI_C55_REG_MCU_CONFIG,
+ MALI_C55_REG_MCU_CONFIG_WRITE_MASK,
+ MALI_C55_REG_MCU_CONFIG_WRITE_PING);
+
+ /* Apply input windowing */
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO);
+ format = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SINK_VIDEO);
+ cfg = mali_c55_isp_get_mbus_config_by_code(format->code);
+
+ mali_c55_write(mali_c55, MALI_C55_REG_HC_START,
+ MALI_C55_HC_START(crop->left));
+ mali_c55_write(mali_c55, MALI_C55_REG_HC_SIZE,
+ MALI_C55_HC_SIZE(crop->width));
+ mali_c55_write(mali_c55, MALI_C55_REG_VC_START_SIZE,
+ MALI_C55_VC_START(crop->top) |
+ MALI_C55_VC_SIZE(crop->height));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
+ MALI_C55_REG_ACTIVE_WIDTH_MASK, format->width);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BASE_ADDR,
+ MALI_C55_REG_ACTIVE_HEIGHT_MASK,
+ format->height << 16);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BAYER_ORDER,
+ MALI_C55_BAYER_ORDER_MASK, cfg->order);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_INPUT_WIDTH,
+ MALI_C55_INPUT_WIDTH_MASK,
+ MALI_C55_INPUT_WIDTH_20BIT);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
+ MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK,
+ cfg->bypass ? MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK :
+ 0x00);
+
+ mali_c55_params_write_config(mali_c55);
+ ret = mali_c55_config_write(ctx, MALI_C55_CONFIG_PING, true);
+ if (ret) {
+ dev_err(mali_c55->dev, "failed to write ISP config\n");
+ return ret;
+ }
+
+ mali_c55_write(mali_c55, MALI_C55_REG_INPUT_MODE_REQUEST,
+ MALI_C55_INPUT_SAFE_START);
+
+ ret = readl_poll_timeout(mali_c55->base + MALI_C55_REG_MODE_STATUS, val,
+ val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
+ if (ret) {
+ mali_c55_isp_stop(mali_c55);
+ dev_err(mali_c55->dev, "timeout starting ISP\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mali_c55_isp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ /*
+ * Only the internal RGB processed format is allowed on the regular
+ * processing source pad.
+ */
+ if (code->pad == MALI_C55_ISP_PAD_SOURCE_VIDEO) {
+ if (code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_RGB121212_1X36;
+ return 0;
+ }
+
+ /* On the sink and bypass pads all the supported formats are allowed. */
+ if (code->index >= ARRAY_SIZE(mali_c55_isp_fmts))
+ return -EINVAL;
+
+ code->code = mali_c55_isp_fmts[code->index].code;
+
+ return 0;
+}
+
+static int mali_c55_isp_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct mali_c55_isp_format_info *cfg;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ /*
+ * Only the internal RGB processed format is allowed on the regular
+ * processing source pad.
+ *
+ * On the sink and bypass pads all the supported formats are allowed.
+ */
+ if (fse->pad == MALI_C55_ISP_PAD_SOURCE_VIDEO) {
+ if (fse->code != MEDIA_BUS_FMT_RGB121212_1X36)
+ return -EINVAL;
+ } else {
+ cfg = mali_c55_isp_get_mbus_config_by_code(fse->code);
+ if (!cfg)
+ return -EINVAL;
+ }
+
+ fse->min_width = MALI_C55_MIN_WIDTH;
+ fse->min_height = MALI_C55_MIN_HEIGHT;
+ fse->max_width = MALI_C55_MAX_WIDTH;
+ fse->max_height = MALI_C55_MAX_HEIGHT;
+
+ return 0;
+}
+
+static int mali_c55_isp_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct v4l2_mbus_framefmt *src_fmt, *sink_fmt;
+ const struct mali_c55_isp_format_info *cfg;
+ struct v4l2_rect *crop;
+
+ /*
+ * Disallow set_fmt on the source pads; format is fixed and the sizes
+ * are the result of applying the sink crop rectangle to the sink
+ * format.
+ */
+ if (format->pad != MALI_C55_ISP_PAD_SINK_VIDEO)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SINK_VIDEO);
+
+ cfg = mali_c55_isp_get_mbus_config_by_code(fmt->code);
+ sink_fmt->code = cfg ? fmt->code : MEDIA_BUS_FMT_SRGGB20_1X20;
+
+ /*
+ * Clamp sizes in the accepted limits and clamp the crop rectangle in
+ * the new sizes.
+ */
+ sink_fmt->width = clamp(fmt->width, MALI_C55_MIN_WIDTH,
+ MALI_C55_MAX_WIDTH);
+ sink_fmt->height = clamp(fmt->height, MALI_C55_MIN_HEIGHT,
+ MALI_C55_MAX_HEIGHT);
+
+ *fmt = *sink_fmt;
+
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO);
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = sink_fmt->width;
+ crop->height = sink_fmt->height;
+
+ /*
+ * Propagate format to source pads. On the 'regular' output pad use
+ * the internal RGB processed format, while on the bypass pad simply
+ * replicate the ISP sink format. The sizes on both pads are the same as
+ * the ISP sink crop rectangle. The "field" and "colorspace" fields are
+ * set in .init_state() and fixed for both source pads, as is the "code"
+ * field for the processed data source pad.
+ */
+ src_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO);
+ src_fmt->width = crop->width;
+ src_fmt->height = crop->height;
+
+ src_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SOURCE_BYPASS);
+ src_fmt->code = sink_fmt->code;
+ src_fmt->width = crop->width;
+ src_fmt->height = crop->height;
+
+ return 0;
+}
+
+static int mali_c55_isp_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ if (sel->pad != MALI_C55_ISP_PAD_SINK_VIDEO ||
+ sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ sel->r = *v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO);
+
+ return 0;
+}
+
+static int mali_c55_isp_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *src_fmt;
+ const struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+
+ if (sel->pad != MALI_C55_ISP_PAD_SINK_VIDEO ||
+ sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(state, MALI_C55_ISP_PAD_SINK_VIDEO);
+
+ sel->r.left = clamp_t(unsigned int, sel->r.left, 0, fmt->width);
+ sel->r.top = clamp_t(unsigned int, sel->r.top, 0, fmt->height);
+ sel->r.width = clamp_t(unsigned int, sel->r.width, MALI_C55_MIN_WIDTH,
+ fmt->width - sel->r.left);
+ sel->r.height = clamp_t(unsigned int, sel->r.height,
+ MALI_C55_MIN_HEIGHT,
+ fmt->height - sel->r.top);
+
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_ISP_PAD_SINK_VIDEO);
+ *crop = sel->r;
+
+ /*
+ * Propagate the crop rectangle sizes to the source pad format. The crop
+ * isn't propagated to the bypass source pad, because the bypassed data
+ * cannot be cropped.
+ */
+ src_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO);
+ src_fmt->width = crop->width;
+ src_fmt->height = crop->height;
+
+ return 0;
+}
+
+static int mali_c55_isp_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct mali_c55_isp *isp = container_of(sd, struct mali_c55_isp, sd);
+ struct mali_c55 *mali_c55 = isp->mali_c55;
+ struct v4l2_subdev *src_sd;
+ struct media_pad *sink_pad;
+ int ret;
+
+ /*
+ * We have two source pads, both of which have only a single stream. The
+ * core v4l2 code already validated those parameters so we can just get
+ * on with starting the ISP.
+ */
+
+ sink_pad = &isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO];
+ isp->remote_src = media_pad_remote_pad_unique(sink_pad);
+ src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity);
+
+ isp->frame_sequence = 0;
+ ret = mali_c55_isp_start(mali_c55, state);
+ if (ret) {
+ dev_err(mali_c55->dev, "Failed to start ISP\n");
+ isp->remote_src = NULL;
+ return ret;
+ }
+
+ /*
+ * We only support a single input stream, so we can just enable the 1st
+ * entry in the streams mask.
+ */
+ ret = v4l2_subdev_enable_streams(src_sd, isp->remote_src->index, BIT(0));
+ if (ret) {
+ dev_err(mali_c55->dev, "Failed to start ISP source\n");
+ mali_c55_isp_stop(mali_c55);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mali_c55_isp_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct mali_c55_isp *isp = container_of(sd, struct mali_c55_isp, sd);
+ struct mali_c55 *mali_c55 = isp->mali_c55;
+ struct v4l2_subdev *src_sd;
+
+ if (isp->remote_src) {
+ src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity);
+ v4l2_subdev_disable_streams(src_sd, isp->remote_src->index,
+ BIT(0));
+ }
+ isp->remote_src = NULL;
+
+ mali_c55_isp_stop(mali_c55);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops mali_c55_isp_pad_ops = {
+ .enum_mbus_code = mali_c55_isp_enum_mbus_code,
+ .enum_frame_size = mali_c55_isp_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = mali_c55_isp_set_fmt,
+ .get_selection = mali_c55_isp_get_selection,
+ .set_selection = mali_c55_isp_set_selection,
+ .link_validate = v4l2_subdev_link_validate_default,
+ .enable_streams = mali_c55_isp_enable_streams,
+ .disable_streams = mali_c55_isp_disable_streams,
+};
+
+void mali_c55_isp_queue_event_sof(struct mali_c55 *mali_c55)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+
+ event.u.frame_sync.frame_sequence = mali_c55->isp.frame_sequence;
+ v4l2_event_queue(mali_c55->isp.sd.devnode, &event);
+}
+
+static int
+mali_c55_isp_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_subdev_core_ops mali_c55_isp_core_ops = {
+ .subscribe_event = mali_c55_isp_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops mali_c55_isp_ops = {
+ .pad = &mali_c55_isp_pad_ops,
+ .core = &mali_c55_isp_core_ops,
+};
+
+static int mali_c55_isp_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *in_crop;
+
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SINK_VIDEO);
+ src_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO);
+ in_crop = v4l2_subdev_state_get_crop(state,
+ MALI_C55_ISP_PAD_SINK_VIDEO);
+
+ sink_fmt->width = MALI_C55_DEFAULT_WIDTH;
+ sink_fmt->height = MALI_C55_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
+ sink_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
+ sink_fmt->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
+ sink_fmt->ycbcr_enc);
+
+ *v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SOURCE_BYPASS) = *sink_fmt;
+
+ src_fmt->width = MALI_C55_DEFAULT_WIDTH;
+ src_fmt->height = MALI_C55_DEFAULT_HEIGHT;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->code = MEDIA_BUS_FMT_RGB121212_1X36;
+ src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ src_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
+ src_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
+ src_fmt->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, sink_fmt->colorspace,
+ sink_fmt->ycbcr_enc);
+
+ in_crop->top = 0;
+ in_crop->left = 0;
+ in_crop->width = MALI_C55_DEFAULT_WIDTH;
+ in_crop->height = MALI_C55_DEFAULT_HEIGHT;
+
+ src_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SOURCE_STATS);
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_ISP_PAD_SINK_PARAMS);
+
+ src_fmt->width = 0;
+ src_fmt->height = 0;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ sink_fmt->width = 0;
+ sink_fmt->height = 0;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops mali_c55_isp_internal_ops = {
+ .init_state = mali_c55_isp_init_state,
+};
+
+static int mali_c55_subdev_link_validate(struct media_link *link)
+{
+ /*
+ * Skip validation for the parameters sink pad, as the source is not
+ * a subdevice.
+ */
+ if (link->sink->index == MALI_C55_ISP_PAD_SINK_PARAMS)
+ return 0;
+
+ return v4l2_subdev_link_validate(link);
+}
+
+static const struct media_entity_operations mali_c55_isp_media_ops = {
+ .link_validate = mali_c55_subdev_link_validate,
+};
+
+static int mali_c55_isp_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ /*
+ * .s_ctrl() is a mandatory operation, but the driver has only a single
+ * read only control. If we got here, something went badly wrong.
+ */
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops mali_c55_isp_ctrl_ops = {
+ .s_ctrl = mali_c55_isp_s_ctrl,
+};
+
+/* NOT const because the default needs to be filled in at runtime */
+static struct v4l2_ctrl_config mali_c55_isp_v4l2_custom_ctrls[] = {
+ {
+ .ops = &mali_c55_isp_ctrl_ops,
+ .id = V4L2_CID_MALI_C55_CAPABILITIES,
+ .name = "Mali-C55 ISP Capabilities",
+ .type = V4L2_CTRL_TYPE_BITMASK,
+ .min = 0,
+ .max = MALI_C55_GPS_PONG_FITTED |
+ MALI_C55_GPS_WDR_FITTED |
+ MALI_C55_GPS_COMPRESSION_FITTED |
+ MALI_C55_GPS_TEMPER_FITTED |
+ MALI_C55_GPS_SINTER_LITE_FITTED |
+ MALI_C55_GPS_SINTER_FITTED |
+ MALI_C55_GPS_IRIDIX_LTM_FITTED |
+ MALI_C55_GPS_IRIDIX_GTM_FITTED |
+ MALI_C55_GPS_CNR_FITTED |
+ MALI_C55_GPS_FRSCALER_FITTED |
+ MALI_C55_GPS_DS_PIPE_FITTED,
+ .def = 0,
+ },
+};
+
+static int mali_c55_isp_init_controls(struct mali_c55 *mali_c55)
+{
+ struct v4l2_ctrl_handler *handler = &mali_c55->isp.handler;
+ struct v4l2_ctrl *capabilities;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(handler, 1);
+ if (ret)
+ return ret;
+
+ mali_c55_isp_v4l2_custom_ctrls[0].def = mali_c55->capabilities;
+
+ capabilities = v4l2_ctrl_new_custom(handler,
+ &mali_c55_isp_v4l2_custom_ctrls[0],
+ NULL);
+ if (capabilities)
+ capabilities->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (handler->error) {
+ dev_err(mali_c55->dev, "failed to register capabilities control\n");
+ ret = handler->error;
+ v4l2_ctrl_handler_free(handler);
+ return ret;
+ }
+
+ mali_c55->isp.sd.ctrl_handler = handler;
+
+ return 0;
+}
+
+int mali_c55_register_isp(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_isp *isp = &mali_c55->isp;
+ struct v4l2_subdev *sd = &isp->sd;
+ int ret;
+
+ isp->mali_c55 = mali_c55;
+
+ v4l2_subdev_init(sd, &mali_c55_isp_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->entity.ops = &mali_c55_isp_media_ops;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
+ sd->internal_ops = &mali_c55_isp_internal_ops;
+ strscpy(sd->name, MALI_C55_DRIVER_NAME " isp", sizeof(sd->name));
+
+ isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ isp->pads[MALI_C55_ISP_PAD_SOURCE_VIDEO].flags = MEDIA_PAD_FL_SOURCE;
+ isp->pads[MALI_C55_ISP_PAD_SOURCE_BYPASS].flags = MEDIA_PAD_FL_SOURCE;
+ isp->pads[MALI_C55_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+ isp->pads[MALI_C55_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
+
+ ret = media_entity_pads_init(&sd->entity, MALI_C55_ISP_NUM_PADS,
+ isp->pads);
+ if (ret)
+ return ret;
+
+ ret = mali_c55_isp_init_controls(mali_c55);
+ if (ret)
+ goto err_cleanup_media_entity;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_free_ctrl_handler;
+
+ ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd);
+ if (ret)
+ goto err_cleanup_subdev;
+
+ mutex_init(&isp->capture_lock);
+
+ return 0;
+
+err_cleanup_subdev:
+ v4l2_subdev_cleanup(sd);
+err_free_ctrl_handler:
+ v4l2_ctrl_handler_free(&isp->handler);
+err_cleanup_media_entity:
+ media_entity_cleanup(&sd->entity);
+ isp->mali_c55 = NULL;
+
+ return ret;
+}
+
+void mali_c55_unregister_isp(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_isp *isp = &mali_c55->isp;
+
+ if (!isp->mali_c55)
+ return;
+
+ mutex_destroy(&isp->capture_lock);
+ v4l2_device_unregister_subdev(&isp->sd);
+ v4l2_subdev_cleanup(&isp->sd);
+ media_entity_cleanup(&isp->sd.entity);
+}
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-params.c b/drivers/media/platform/arm/mali-c55/mali-c55-params.c
new file mode 100644
index 000000000000..082cda4f4f63
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-params.c
@@ -0,0 +1,819 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Mali-C55 ISP Driver - Configuration parameters output device
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+#include <linux/media/arm/mali-c55-config.h>
+#include <linux/pm_runtime.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mali-c55-common.h"
+#include "mali-c55-registers.h"
+
+/**
+ * union mali_c55_params_block - Generalisation of a parameter block
+ *
+ * This union allows the driver to treat a block as a generic pointer to this
+ * union and safely access the header and block-specific struct without having
+ * to resort to casting. The header member is accessed first, and the type field
+ * checked which allows the driver to determine which of the other members
+ * should be used. The data member at the end allows a pointer to an address
+ * within the data member of :c:type:`mali_c55_params_buffer` to initialise a
+ * union variable.
+ *
+ * @header: Pointer to the shared header struct embedded as the
+ * first member of all the possible other members (except
+ * @data). This member would be accessed first and the type
+ * field checked to determine which of the other members
+ * should be accessed.
+ * @sensor_offs: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS
+ * @aexp_hist: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST and
+ * header->type == MALI_C55_PARAM_BLOCK_AEXP_IHIST
+ * @aexp_weights: For header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS
+ * and header->type = MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS
+ * @digital_gain: For header->type == MALI_C55_PARAM_BLOCK_DIGITAL_GAIN
+ * @awb_gains: For header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS and
+ * header->type = MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP
+ * @awb_config: For header->type == MALI_C55_PARAM_MESH_SHADING_CONFIG
+ * @shading_config: For header->type == MALI_C55_PARAM_MESH_SHADING_SELECTION
+ * @shading_selection: For header->type == MALI_C55_PARAM_BLOCK_SENSOR_OFFS
+ * @data: Allows easy initialisation of a union variable with a
+ * pointer into a __u8 array.
+ */
+union mali_c55_params_block {
+ const struct v4l2_isp_params_block_header *header;
+ const struct mali_c55_params_sensor_off_preshading *sensor_offs;
+ const struct mali_c55_params_aexp_hist *aexp_hist;
+ const struct mali_c55_params_aexp_weights *aexp_weights;
+ const struct mali_c55_params_digital_gain *digital_gain;
+ const struct mali_c55_params_awb_gains *awb_gains;
+ const struct mali_c55_params_awb_config *awb_config;
+ const struct mali_c55_params_mesh_shading_config *shading_config;
+ const struct mali_c55_params_mesh_shading_selection *shading_selection;
+ const __u8 *data;
+};
+
+typedef void (*mali_c55_params_handler)(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block);
+
+#define to_mali_c55_params_buf(vbuf) \
+ container_of(vbuf, struct mali_c55_params_buf, vb)
+
+static void mali_c55_params_sensor_offs(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_sensor_off_preshading *p;
+ __u32 global_offset;
+
+ p = block.sensor_offs;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
+ MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH,
+ MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH);
+ return;
+ }
+
+ if (!(p->chan00 || p->chan01 || p->chan10 || p->chan11))
+ return;
+
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_00,
+ p->chan00 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK);
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_01,
+ p->chan01 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK);
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_10,
+ p->chan10 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK);
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_SENSOR_OFF_PRE_SHA_11,
+ p->chan11 & MALI_C55_SENSOR_OFF_PRE_SHA_MASK);
+
+ /*
+ * The average offset is applied as a global offset for the digital
+ * gain block
+ */
+ global_offset = (p->chan00 + p->chan01 + p->chan10 + p->chan11) >> 2;
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN_OFFSET,
+ MALI_C55_DIGITAL_GAIN_OFFSET_MASK,
+ global_offset);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_BYPASS_3,
+ MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH,
+ 0x00);
+}
+
+static void mali_c55_params_aexp_hist(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_aexp_hist *params;
+ u32 disable_mask;
+ u32 disable_val;
+ u32 base;
+
+ if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST) {
+ disable_mask = MALI_C55_AEXP_HIST_DISABLE_MASK;
+ disable_val = MALI_C55_AEXP_HIST_DISABLE;
+ base = MALI_C55_REG_AEXP_HIST_BASE;
+ } else {
+ disable_mask = MALI_C55_AEXP_IHIST_DISABLE_MASK;
+ disable_val = MALI_C55_AEXP_IHIST_DISABLE;
+ base = MALI_C55_REG_AEXP_IHIST_BASE;
+ }
+
+ params = block.aexp_hist;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ disable_mask, disable_val);
+ return;
+ }
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ disable_mask, false);
+
+ mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET,
+ MALI_C55_AEXP_HIST_SKIP_X_MASK, params->skip_x);
+ mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET,
+ MALI_C55_AEXP_HIST_OFFSET_X_MASK,
+ MALI_C55_AEXP_HIST_OFFSET_X(params->offset_x));
+ mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET,
+ MALI_C55_AEXP_HIST_SKIP_Y_MASK,
+ MALI_C55_AEXP_HIST_SKIP_Y(params->skip_y));
+ mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SKIP_OFFSET,
+ MALI_C55_AEXP_HIST_OFFSET_Y_MASK,
+ MALI_C55_AEXP_HIST_OFFSET_Y(params->offset_y));
+
+ mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET,
+ MALI_C55_AEXP_HIST_SCALE_BOTTOM_MASK,
+ params->scale_bottom);
+ mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_SCALE_OFFSET,
+ MALI_C55_AEXP_HIST_SCALE_TOP_MASK,
+ MALI_C55_AEXP_HIST_SCALE_TOP(params->scale_top));
+
+ mali_c55_ctx_update_bits(mali_c55, base + MALI_C55_AEXP_HIST_PLANE_MODE_OFFSET,
+ MALI_C55_AEXP_HIST_PLANE_MODE_MASK,
+ params->plane_mode);
+
+ if (block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST)
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ MALI_C55_AEXP_HIST_SWITCH_MASK,
+ MALI_C55_AEXP_HIST_SWITCH(params->tap_point));
+}
+
+static void
+mali_c55_params_aexp_hist_weights(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_aexp_weights *params;
+ u32 base, val, addr;
+
+ params = block.aexp_weights;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)
+ return;
+
+ base = block.header->type == MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS ?
+ MALI_C55_REG_AEXP_HIST_BASE :
+ MALI_C55_REG_AEXP_IHIST_BASE;
+
+ mali_c55_ctx_update_bits(mali_c55,
+ base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET,
+ MALI_C55_AEXP_HIST_NODES_USED_HORIZ_MASK,
+ params->nodes_used_horiz);
+ mali_c55_ctx_update_bits(mali_c55,
+ base + MALI_C55_AEXP_HIST_NODES_USED_OFFSET,
+ MALI_C55_AEXP_HIST_NODES_USED_VERT_MASK,
+ MALI_C55_AEXP_HIST_NODES_USED_VERT(params->nodes_used_vert));
+
+ /*
+ * The zone weights array is a 225-element array of u8 values, but that
+ * is a bit annoying to handle given the ISP expects 32-bit writes. We
+ * just reinterpret it as 56-element array of 32-bit values for the
+ * purposes of this transaction. The last register is handled separately
+ * to stop static analysers worrying about buffer overflow. The 3 bytes
+ * of additional space at the end of the write is just padding for the
+ * array of weights in the ISP memory space anyway, so there's no risk
+ * of overwriting other registers.
+ */
+ for (unsigned int i = 0; i < 56; i++) {
+ val = ((u32 *)params->zone_weights)[i]
+ & MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK;
+ addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * i);
+
+ mali_c55_ctx_write(mali_c55, addr, val);
+ }
+
+ val = params->zone_weights[MALI_C55_MAX_ZONES - 1];
+ addr = base + MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET + (4 * 56);
+}
+
+static void mali_c55_params_digital_gain(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_digital_gain *dgain;
+ u32 gain;
+
+ dgain = block.digital_gain;
+
+ /*
+ * If the block is flagged as disabled we write a gain of 1.0, which in
+ * Q5.8 format is 256.
+ */
+ gain = block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE ?
+ 256 : dgain->gain;
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_DIGITAL_GAIN,
+ MALI_C55_DIGITAL_GAIN_MASK,
+ gain);
+}
+
+static void mali_c55_params_awb_gains(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_awb_gains *gains;
+ u32 gain00, gain01, gain10, gain11;
+
+ gains = block.awb_gains;
+
+ /*
+ * There are two places AWB gains can be set in the ISP; one affects the
+ * image output data and the other affects the statistics for the
+ * AEXP-0 tap point.
+ */
+ u32 addr1 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ?
+ MALI_C55_REG_AWB_GAINS1 :
+ MALI_C55_REG_AWB_GAINS1_AEXP;
+ u32 addr2 = block.header->type == MALI_C55_PARAM_BLOCK_AWB_GAINS ?
+ MALI_C55_REG_AWB_GAINS2 :
+ MALI_C55_REG_AWB_GAINS2_AEXP;
+
+ /* If the block is flagged disabled, set all of the gains to 1.0 */
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ gain00 = 256;
+ gain01 = 256;
+ gain10 = 256;
+ gain11 = 256;
+ } else {
+ gain00 = gains->gain00;
+ gain01 = gains->gain01;
+ gain10 = gains->gain10;
+ gain11 = gains->gain11;
+ }
+
+ mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN00_MASK,
+ gain00);
+ mali_c55_ctx_update_bits(mali_c55, addr1, MALI_C55_AWB_GAIN01_MASK,
+ MALI_C55_AWB_GAIN01(gain01));
+ mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN10_MASK,
+ gain10);
+ mali_c55_ctx_update_bits(mali_c55, addr2, MALI_C55_AWB_GAIN11_MASK,
+ MALI_C55_AWB_GAIN11(gain11));
+}
+
+static void mali_c55_params_awb_config(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_awb_config *params;
+
+ params = block.awb_config;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ MALI_C55_AWB_DISABLE_MASK,
+ MALI_C55_AWB_DISABLE_MASK);
+ return;
+ }
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ MALI_C55_AWB_DISABLE_MASK, false);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_STATS_MODE,
+ MALI_C55_AWB_STATS_MODE_MASK, params->stats_mode);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_WHITE_LEVEL,
+ MALI_C55_AWB_WHITE_LEVEL_MASK, params->white_level);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_BLACK_LEVEL,
+ MALI_C55_AWB_BLACK_LEVEL_MASK, params->black_level);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MAX,
+ MALI_C55_AWB_CR_MAX_MASK, params->cr_max);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_MIN,
+ MALI_C55_AWB_CR_MIN_MASK, params->cr_min);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MAX,
+ MALI_C55_AWB_CB_MAX_MASK, params->cb_max);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_MIN,
+ MALI_C55_AWB_CB_MIN_MASK, params->cb_min);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED,
+ MALI_C55_AWB_NODES_USED_HORIZ_MASK,
+ params->nodes_used_horiz);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_NODES_USED,
+ MALI_C55_AWB_NODES_USED_VERT_MASK,
+ MALI_C55_AWB_NODES_USED_VERT(params->nodes_used_vert));
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_HIGH,
+ MALI_C55_AWB_CR_HIGH_MASK, params->cr_high);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CR_LOW,
+ MALI_C55_AWB_CR_LOW_MASK, params->cr_low);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_HIGH,
+ MALI_C55_AWB_CB_HIGH_MASK, params->cb_high);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_AWB_CB_LOW,
+ MALI_C55_AWB_CB_LOW_MASK, params->cb_low);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_METERING_CONFIG,
+ MALI_C55_AWB_SWITCH_MASK,
+ MALI_C55_AWB_SWITCH(params->tap_point));
+}
+
+static void mali_c55_params_lsc_config(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_mesh_shading_config *params;
+ unsigned int i;
+ u32 addr;
+
+ params = block.shading_config;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) {
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_ENABLE_MASK,
+ false);
+ return;
+ }
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_ENABLE_MASK, true);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_MESH_SHOW_MASK,
+ MALI_C55_MESH_SHADING_MESH_SHOW(params->mesh_show));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_SCALE_MASK,
+ MALI_C55_MESH_SHADING_SCALE(params->mesh_scale));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_PAGE_R_MASK,
+ MALI_C55_MESH_SHADING_PAGE_R(params->mesh_page_r));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_PAGE_G_MASK,
+ MALI_C55_MESH_SHADING_PAGE_G(params->mesh_page_g));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_PAGE_B_MASK,
+ MALI_C55_MESH_SHADING_PAGE_B(params->mesh_page_b));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_MESH_WIDTH_MASK,
+ MALI_C55_MESH_SHADING_MESH_WIDTH(params->mesh_width));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_CONFIG,
+ MALI_C55_MESH_SHADING_MESH_HEIGHT_MASK,
+ MALI_C55_MESH_SHADING_MESH_HEIGHT(params->mesh_height));
+
+ for (i = 0; i < MALI_C55_NUM_MESH_SHADING_ELEMENTS; i++) {
+ addr = MALI_C55_REG_MESH_SHADING_TABLES + (i * 4);
+ mali_c55_ctx_write(mali_c55, addr, params->mesh[i]);
+ }
+}
+
+static void mali_c55_params_lsc_selection(struct mali_c55 *mali_c55,
+ union mali_c55_params_block block)
+{
+ const struct mali_c55_params_mesh_shading_selection *params;
+
+ params = block.shading_selection;
+
+ if (block.header->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)
+ return;
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK,
+ MALI_C55_MESH_SHADING_ALPHA_BANK_R_MASK,
+ params->mesh_alpha_bank_r);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK,
+ MALI_C55_MESH_SHADING_ALPHA_BANK_G_MASK,
+ MALI_C55_MESH_SHADING_ALPHA_BANK_G(params->mesh_alpha_bank_g));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA_BANK,
+ MALI_C55_MESH_SHADING_ALPHA_BANK_B_MASK,
+ MALI_C55_MESH_SHADING_ALPHA_BANK_B(params->mesh_alpha_bank_b));
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA,
+ MALI_C55_MESH_SHADING_ALPHA_R_MASK,
+ params->mesh_alpha_r);
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA,
+ MALI_C55_MESH_SHADING_ALPHA_G_MASK,
+ MALI_C55_MESH_SHADING_ALPHA_G(params->mesh_alpha_g));
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_MESH_SHADING_ALPHA,
+ MALI_C55_MESH_SHADING_ALPHA_B_MASK,
+ MALI_C55_MESH_SHADING_ALPHA_B(params->mesh_alpha_b));
+
+ mali_c55_ctx_update_bits(mali_c55,
+ MALI_C55_REG_MESH_SHADING_MESH_STRENGTH,
+ MALI_c55_MESH_STRENGTH_MASK,
+ params->mesh_strength);
+}
+
+static const mali_c55_params_handler mali_c55_params_handlers[] = {
+ [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = &mali_c55_params_sensor_offs,
+ [MALI_C55_PARAM_BLOCK_AEXP_HIST] = &mali_c55_params_aexp_hist,
+ [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = &mali_c55_params_aexp_hist,
+ [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights,
+ [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = &mali_c55_params_aexp_hist_weights,
+ [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = &mali_c55_params_digital_gain,
+ [MALI_C55_PARAM_BLOCK_AWB_GAINS] = &mali_c55_params_awb_gains,
+ [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = &mali_c55_params_awb_config,
+ [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = &mali_c55_params_awb_gains,
+ [MALI_C55_PARAM_MESH_SHADING_CONFIG] = &mali_c55_params_lsc_config,
+ [MALI_C55_PARAM_MESH_SHADING_SELECTION] = &mali_c55_params_lsc_selection,
+};
+
+static const struct v4l2_isp_params_block_type_info
+mali_c55_params_block_types_info[] = {
+ [MALI_C55_PARAM_BLOCK_SENSOR_OFFS] = {
+ .size = sizeof(struct mali_c55_params_sensor_off_preshading),
+ },
+ [MALI_C55_PARAM_BLOCK_AEXP_HIST] = {
+ .size = sizeof(struct mali_c55_params_aexp_hist),
+ },
+ [MALI_C55_PARAM_BLOCK_AEXP_IHIST] = {
+ .size = sizeof(struct mali_c55_params_aexp_hist),
+ },
+ [MALI_C55_PARAM_BLOCK_AEXP_HIST_WEIGHTS] = {
+ .size = sizeof(struct mali_c55_params_aexp_weights),
+ },
+ [MALI_C55_PARAM_BLOCK_AEXP_IHIST_WEIGHTS] = {
+ .size = sizeof(struct mali_c55_params_aexp_weights),
+ },
+ [MALI_C55_PARAM_BLOCK_DIGITAL_GAIN] = {
+ .size = sizeof(struct mali_c55_params_digital_gain),
+ },
+ [MALI_C55_PARAM_BLOCK_AWB_GAINS] = {
+ .size = sizeof(struct mali_c55_params_awb_gains),
+ },
+ [MALI_C55_PARAM_BLOCK_AWB_CONFIG] = {
+ .size = sizeof(struct mali_c55_params_awb_config),
+ },
+ [MALI_C55_PARAM_BLOCK_AWB_GAINS_AEXP] = {
+ .size = sizeof(struct mali_c55_params_awb_gains),
+ },
+ [MALI_C55_PARAM_MESH_SHADING_CONFIG] = {
+ .size = sizeof(struct mali_c55_params_mesh_shading_config),
+ },
+ [MALI_C55_PARAM_MESH_SHADING_SELECTION] = {
+ .size = sizeof(struct mali_c55_params_mesh_shading_selection),
+ },
+};
+
+static_assert(ARRAY_SIZE(mali_c55_params_handlers) ==
+ ARRAY_SIZE(mali_c55_params_block_types_info));
+
+static int mali_c55_params_enum_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ if (f->mbus_code && f->mbus_code != MEDIA_BUS_FMT_METADATA_FIXED)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_MALI_C55_PARAMS;
+
+ return 0;
+}
+
+static int mali_c55_params_g_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ static const struct v4l2_meta_format mfmt = {
+ .dataformat = V4L2_META_FMT_MALI_C55_PARAMS,
+ .buffersize = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE),
+ };
+
+ f->fmt.meta = mfmt;
+
+ return 0;
+}
+
+static int mali_c55_params_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops mali_c55_params_v4l2_ioctl_ops = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_out = mali_c55_params_enum_fmt_meta_out,
+ .vidioc_g_fmt_meta_out = mali_c55_params_g_fmt_meta_out,
+ .vidioc_s_fmt_meta_out = mali_c55_params_g_fmt_meta_out,
+ .vidioc_try_fmt_meta_out = mali_c55_params_g_fmt_meta_out,
+ .vidioc_querycap = mali_c55_params_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations mali_c55_params_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static int
+mali_c55_params_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes && *num_planes > 1)
+ return -EINVAL;
+
+ if (sizes[0] && sizes[0] < v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE))
+ return -EINVAL;
+
+ *num_planes = 1;
+
+ if (!sizes[0])
+ sizes[0] = v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE);
+
+ return 0;
+}
+
+static int mali_c55_params_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf);
+
+ buf->config = kvmalloc(v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE),
+ GFP_KERNEL);
+ if (!buf->config)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void mali_c55_params_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf);
+
+ kvfree(buf->config);
+ buf->config = NULL;
+}
+
+static int mali_c55_params_buf_prepare(struct vb2_buffer *vb)
+{
+ struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf);
+ struct v4l2_isp_params_buffer *config = vb2_plane_vaddr(vb, 0);
+ struct mali_c55 *mali_c55 = params->mali_c55;
+ int ret;
+
+ if (config->version != MALI_C55_PARAM_BUFFER_V1) {
+ dev_dbg(mali_c55->dev,
+ "Unsupported extensible format version: %u\n",
+ config->version);
+ return -EINVAL;
+ }
+
+ ret = v4l2_isp_params_validate_buffer_size(mali_c55->dev, vb,
+ v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE));
+ if (ret)
+ return ret;
+
+ /*
+ * Copy the parameters buffer provided by userspace to the internal
+ * scratch buffer. This protects against the chance of userspace making
+ * changed to the buffer content whilst the driver processes it.
+ */
+
+ memcpy(buf->config, config, v4l2_isp_params_buffer_size(MALI_C55_PARAMS_MAX_SIZE));
+
+ return v4l2_isp_params_validate_buffer(mali_c55->dev, vb, buf->config,
+ mali_c55_params_block_types_info,
+ ARRAY_SIZE(mali_c55_params_block_types_info));
+}
+
+static void mali_c55_params_buf_queue(struct vb2_buffer *vb)
+{
+ struct mali_c55_params *params = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mali_c55_params_buf *buf = to_mali_c55_params_buf(vbuf);
+
+ spin_lock(&params->buffers.lock);
+ list_add_tail(&buf->queue, &params->buffers.queue);
+ spin_unlock(&params->buffers.lock);
+}
+
+static void mali_c55_params_return_buffers(struct mali_c55_params *params,
+ enum vb2_buffer_state state)
+{
+ struct mali_c55_params_buf *buf, *tmp;
+
+ guard(spinlock)(&params->buffers.lock);
+
+ list_for_each_entry_safe(buf, tmp, &params->buffers.queue, queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+}
+
+static int mali_c55_params_start_streaming(struct vb2_queue *q,
+ unsigned int count)
+{
+ struct mali_c55_params *params = vb2_get_drv_priv(q);
+ struct mali_c55 *mali_c55 = params->mali_c55;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(mali_c55->dev);
+ if (ret)
+ goto err_return_buffers;
+
+ ret = video_device_pipeline_alloc_start(&params->vdev);
+ if (ret)
+ goto err_pm_put;
+
+ if (mali_c55_pipeline_ready(mali_c55)) {
+ ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ BIT(0));
+ if (ret < 0)
+ goto err_stop_pipeline;
+ }
+
+ return 0;
+
+err_stop_pipeline:
+ video_device_pipeline_stop(&params->vdev);
+err_pm_put:
+ pm_runtime_put_autosuspend(mali_c55->dev);
+err_return_buffers:
+ mali_c55_params_return_buffers(params, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void mali_c55_params_stop_streaming(struct vb2_queue *q)
+{
+ struct mali_c55_params *params = vb2_get_drv_priv(q);
+ struct mali_c55 *mali_c55 = params->mali_c55;
+ struct mali_c55_isp *isp = &mali_c55->isp;
+
+ if (mali_c55_pipeline_ready(mali_c55)) {
+ if (v4l2_subdev_is_streaming(&isp->sd))
+ v4l2_subdev_disable_streams(&isp->sd,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ BIT(0));
+ }
+
+ video_device_pipeline_stop(&params->vdev);
+ mali_c55_params_return_buffers(params, VB2_BUF_STATE_ERROR);
+ pm_runtime_put_autosuspend(params->mali_c55->dev);
+}
+
+static const struct vb2_ops mali_c55_params_vb2_ops = {
+ .queue_setup = mali_c55_params_queue_setup,
+ .buf_init = mali_c55_params_buf_init,
+ .buf_cleanup = mali_c55_params_buf_cleanup,
+ .buf_queue = mali_c55_params_buf_queue,
+ .buf_prepare = mali_c55_params_buf_prepare,
+ .start_streaming = mali_c55_params_start_streaming,
+ .stop_streaming = mali_c55_params_stop_streaming,
+};
+
+void mali_c55_params_write_config(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_params *params = &mali_c55->params;
+ struct v4l2_isp_params_buffer *config;
+ struct mali_c55_params_buf *buf;
+ size_t block_offset = 0;
+ size_t max_offset;
+
+ spin_lock(&params->buffers.lock);
+
+ buf = list_first_entry_or_null(&params->buffers.queue,
+ struct mali_c55_params_buf, queue);
+ if (buf)
+ list_del(&buf->queue);
+ spin_unlock(&params->buffers.lock);
+
+ if (!buf)
+ return;
+
+ buf->vb.sequence = mali_c55->isp.frame_sequence;
+ config = buf->config;
+
+ max_offset = config->data_size;
+
+ /*
+ * Walk the list of parameter blocks and process them. No validation is
+ * done here, as the contents of the config buffer are already checked
+ * when the buffer is queued.
+ */
+ while (max_offset && block_offset < max_offset) {
+ union mali_c55_params_block block;
+ mali_c55_params_handler handler;
+
+ block.data = &config->data[block_offset];
+
+ /* We checked the array index already in .buf_queue() */
+ handler = mali_c55_params_handlers[block.header->type];
+ handler(mali_c55, block);
+
+ block_offset += block.header->size;
+ }
+
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+void mali_c55_unregister_params(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_params *params = &mali_c55->params;
+
+ if (!video_is_registered(&params->vdev))
+ return;
+
+ vb2_video_unregister_device(&params->vdev);
+ media_entity_cleanup(&params->vdev.entity);
+ mutex_destroy(&params->lock);
+}
+
+int mali_c55_register_params(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_params *params = &mali_c55->params;
+ struct video_device *vdev = &params->vdev;
+ struct vb2_queue *vb2q = &params->queue;
+ int ret;
+
+ mutex_init(&params->lock);
+ INIT_LIST_HEAD(&params->buffers.queue);
+ spin_lock_init(&params->buffers.lock);
+
+ params->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&params->vdev.entity, 1, &params->pad);
+ if (ret)
+ goto err_destroy_mutex;
+
+ vb2q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ vb2q->io_modes = VB2_MMAP | VB2_DMABUF;
+ vb2q->drv_priv = params;
+ vb2q->mem_ops = &vb2_dma_contig_memops;
+ vb2q->ops = &mali_c55_params_vb2_ops;
+ vb2q->buf_struct_size = sizeof(struct mali_c55_params_buf);
+ vb2q->min_queued_buffers = 1;
+ vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2q->lock = &params->lock;
+ vb2q->dev = mali_c55->dev;
+
+ ret = vb2_queue_init(vb2q);
+ if (ret) {
+ dev_err(mali_c55->dev, "params vb2 queue init failed\n");
+ goto err_cleanup_entity;
+ }
+
+ strscpy(params->vdev.name, "mali-c55 3a params",
+ sizeof(params->vdev.name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &mali_c55_params_v4l2_fops;
+ vdev->ioctl_ops = &mali_c55_params_v4l2_ioctl_ops;
+ vdev->lock = &params->lock;
+ vdev->v4l2_dev = &mali_c55->v4l2_dev;
+ vdev->queue = &params->queue;
+ vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING |
+ V4L2_CAP_IO_MC;
+ vdev->vfl_dir = VFL_DIR_TX;
+ video_set_drvdata(vdev, params);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "failed to register params video device\n");
+ goto err_release_vb2q;
+ }
+
+ params->mali_c55 = mali_c55;
+
+ return 0;
+
+err_release_vb2q:
+ vb2_queue_release(vb2q);
+err_cleanup_entity:
+ media_entity_cleanup(&params->vdev.entity);
+err_destroy_mutex:
+ mutex_destroy(&params->lock);
+
+ return ret;
+}
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-registers.h b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
new file mode 100644
index 000000000000..f5a148add1c8
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-registers.h
@@ -0,0 +1,449 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ARM Mali-C55 ISP Driver - Register definitions
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#ifndef _MALI_C55_REGISTERS_H
+#define _MALI_C55_REGISTERS_H
+
+#include <linux/bits.h>
+
+/* ISP Common 0x00000 - 0x000ff */
+
+#define MALI_C55_REG_API 0x00000
+#define MALI_C55_REG_PRODUCT 0x00004
+#define MALI_C55_REG_VERSION 0x00008
+#define MALI_C55_REG_REVISION 0x0000c
+#define MALI_C55_REG_PULSE_MODE 0x0003c
+#define MALI_C55_REG_INPUT_MODE_REQUEST 0x0009c
+#define MALI_C55_INPUT_SAFE_STOP 0x00
+#define MALI_C55_INPUT_SAFE_START 0x01
+#define MALI_C55_REG_MODE_STATUS 0x000a0
+#define MALI_C55_REG_INTERRUPT_MASK_VECTOR 0x00030
+#define MALI_C55_INTERRUPT_MASK_ALL GENMASK(31, 0)
+
+#define MALI_C55_REG_GLOBAL_MONITOR 0x00050
+
+#define MALI_C55_REG_GEN_VIDEO 0x00080
+#define MALI_C55_REG_GEN_VIDEO_ON_MASK BIT(0)
+#define MALI_C55_REG_GEN_VIDEO_MULTI_MASK BIT(1)
+#define MALI_C55_REG_GEN_PREFETCH_MASK GENMASK(31, 16)
+
+#define MALI_C55_REG_MCU_CONFIG 0x00020
+#define MALI_C55_REG_MCU_CONFIG_OVERRIDE_MASK BIT(0)
+#define MALI_C55_REG_MCU_CONFIG_WRITE_MASK BIT(1)
+#define MALI_C55_MCU_CONFIG_WRITE(x) ((x) << 1)
+#define MALI_C55_REG_MCU_CONFIG_WRITE_PING BIT(1)
+#define MALI_C55_REG_MCU_CONFIG_WRITE_PONG 0x00
+#define MALI_C55_REG_MULTI_CONTEXT_MODE_MASK BIT(8)
+#define MALI_C55_REG_PING_PONG_READ 0x00024
+#define MALI_C55_REG_PING_PONG_READ_MASK BIT(2)
+
+#define MALI_C55_REG_INTERRUPT_CLEAR_VECTOR 0x00034
+#define MALI_C55_REG_INTERRUPT_CLEAR 0x00040
+#define MALI_C55_REG_INTERRUPT_STATUS_VECTOR 0x00044
+
+enum mali_c55_interrupts {
+ MALI_C55_IRQ_ISP_START,
+ MALI_C55_IRQ_ISP_DONE,
+ MALI_C55_IRQ_MCM_ERROR,
+ MALI_C55_IRQ_BROKEN_FRAME_ERROR,
+ MALI_C55_IRQ_MET_AF_DONE,
+ MALI_C55_IRQ_MET_AEXP_DONE,
+ MALI_C55_IRQ_MET_AWB_DONE,
+ MALI_C55_IRQ_AEXP_1024_DONE,
+ MALI_C55_IRQ_IRIDIX_MET_DONE,
+ MALI_C55_IRQ_LUT_INIT_DONE,
+ MALI_C55_IRQ_FR_Y_DONE,
+ MALI_C55_IRQ_FR_UV_DONE,
+ MALI_C55_IRQ_DS_Y_DONE,
+ MALI_C55_IRQ_DS_UV_DONE,
+ MALI_C55_IRQ_LINEARIZATION_DONE,
+ MALI_C55_IRQ_RAW_FRONTEND_DONE,
+ MALI_C55_IRQ_NOISE_REDUCTION_DONE,
+ MALI_C55_IRQ_IRIDIX_DONE,
+ MALI_C55_IRQ_BAYER2RGB_DONE,
+ MALI_C55_IRQ_WATCHDOG_TIMER,
+ MALI_C55_IRQ_FRAME_COLLISION,
+ MALI_C55_IRQ_UNUSED,
+ MALI_C55_IRQ_DMA_ERROR,
+ MALI_C55_IRQ_INPUT_STOPPED,
+ MALI_C55_IRQ_MET_AWB_TARGET1_HIT,
+ MALI_C55_IRQ_MET_AWB_TARGET2_HIT,
+ MALI_C55_NUM_IRQ_BITS
+};
+
+#define MALI_C55_INTERRUPT_BIT(x) BIT(x)
+
+#define MALI_C55_REG_GLOBAL_PARAMETER_STATUS 0x00068
+#define MALI_C55_GPS_PONG_FITTED BIT(0)
+#define MALI_C55_GPS_WDR_FITTED BIT(1)
+#define MALI_C55_GPS_COMPRESSION_FITTED BIT(2)
+#define MALI_C55_GPS_TEMPER_FITTED BIT(3)
+#define MALI_C55_GPS_SINTER_LITE_FITTED BIT(4)
+#define MALI_C55_GPS_SINTER_FITTED BIT(5)
+#define MALI_C55_GPS_IRIDIX_LTM_FITTED BIT(6)
+#define MALI_C55_GPS_IRIDIX_GTM_FITTED BIT(7)
+#define MALI_C55_GPS_CNR_FITTED BIT(8)
+#define MALI_C55_GPS_FRSCALER_FITTED BIT(9)
+#define MALI_C55_GPS_DS_PIPE_FITTED BIT(10)
+
+#define MALI_C55_REG_BLANKING 0x00084
+#define MALI_C55_REG_HBLANK_MASK GENMASK(15, 0)
+#define MALI_C55_REG_VBLANK_MASK GENMASK(31, 16)
+#define MALI_C55_VBLANK(x) ((x) << 16)
+
+#define MALI_C55_REG_HC_START 0x00088
+#define MALI_C55_HC_START(h) (((h) & 0xffff) << 16)
+#define MALI_C55_REG_HC_SIZE 0x0008c
+#define MALI_C55_HC_SIZE(h) ((h) & 0xffff)
+#define MALI_C55_REG_VC_START_SIZE 0x00094
+#define MALI_C55_VC_START(v) ((v) & 0xffff)
+#define MALI_C55_VC_SIZE(v) (((v) & 0xffff) << 16)
+
+#define MALI_C55_REG_1024BIN_HIST 0x054a8
+#define MALI_C55_1024BIN_HIST_SIZE 4096
+
+/* Ping/Pong Configuration Space */
+#define MALI_C55_REG_BASE_ADDR 0x18e88
+#define MALI_C55_REG_BYPASS_0 0x18eac
+#define MALI_C55_REG_BYPASS_0_VIDEO_TEST BIT(0)
+#define MALI_C55_REG_BYPASS_0_INPUT_FMT BIT(1)
+#define MALI_C55_REG_BYPASS_0_DECOMPANDER BIT(2)
+#define MALI_C55_REG_BYPASS_0_SENSOR_OFFSET_WDR BIT(3)
+#define MALI_C55_REG_BYPASS_0_GAIN_WDR BIT(4)
+#define MALI_C55_REG_BYPASS_0_FRAME_STITCH BIT(5)
+#define MALI_C55_REG_BYPASS_1 0x18eb0
+#define MALI_C55_REG_BYPASS_1_DIGI_GAIN BIT(0)
+#define MALI_C55_REG_BYPASS_1_FE_SENSOR_OFFS BIT(1)
+#define MALI_C55_REG_BYPASS_1_FE_SQRT BIT(2)
+#define MALI_C55_REG_BYPASS_1_RAW_FE BIT(3)
+#define MALI_C55_REG_BYPASS_2 0x18eb8
+#define MALI_C55_REG_BYPASS_2_SINTER BIT(0)
+#define MALI_C55_REG_BYPASS_2_TEMPER BIT(1)
+#define MALI_C55_REG_BYPASS_3 0x18ebc
+#define MALI_C55_REG_BYPASS_3_SQUARE_BE BIT(0)
+#define MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH BIT(1)
+#define MALI_C55_REG_BYPASS_3_MESH_SHADING BIT(3)
+#define MALI_C55_REG_BYPASS_3_WHITE_BALANCE BIT(4)
+#define MALI_C55_REG_BYPASS_3_IRIDIX BIT(5)
+#define MALI_C55_REG_BYPASS_3_IRIDIX_GAIN BIT(6)
+#define MALI_C55_REG_BYPASS_4 0x18ec0
+#define MALI_C55_REG_BYPASS_4_DEMOSAIC_RGB BIT(1)
+#define MALI_C55_REG_BYPASS_4_PF_CORRECTION BIT(3)
+#define MALI_C55_REG_BYPASS_4_CCM BIT(4)
+#define MALI_C55_REG_BYPASS_4_CNR BIT(5)
+#define MALI_C55_REG_FR_BYPASS 0x18ec4
+#define MALI_C55_REG_DS_BYPASS 0x18ec8
+#define MALI_C55_BYPASS_CROP BIT(0)
+#define MALI_C55_BYPASS_SCALER BIT(1)
+#define MALI_C55_BYPASS_GAMMA_RGB BIT(2)
+#define MALI_C55_BYPASS_SHARPEN BIT(3)
+#define MALI_C55_BYPASS_CS_CONV BIT(4)
+#define MALI_C55_REG_ISP_RAW_BYPASS 0x18ecc
+#define MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK BIT(0)
+#define MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK GENMASK(9, 8)
+#define MALI_C55_ISP_RAW_BYPASS_RAW_FR_BYPASS (2 << 8)
+#define MALI_C55_ISP_RAW_BYPASS_RGB_FR_BYPASS (1 << 8)
+#define MALI_C55_ISP_RAW_BYPASS_DS_PIPE_DISABLE BIT(1)
+#define MALI_C55_ISP_RAW_BYPASS_RAW_BYPASS BIT(0)
+
+#define MALI_C55_REG_ACTIVE_WIDTH_MASK 0xffff
+#define MALI_C55_REG_ACTIVE_HEIGHT_MASK 0xffff0000
+#define MALI_C55_REG_BAYER_ORDER 0x18e8c
+#define MALI_C55_BAYER_ORDER_MASK GENMASK(1, 0)
+#define MALI_C55_BAYER_ORDER_RGGB 0
+#define MALI_C55_BAYER_ORDER_GRBG 1
+#define MALI_C55_BAYER_ORDER_GBRG 2
+#define MALI_C55_BAYER_ORDER_BGGR 3
+
+#define MALI_C55_REG_METERING_CONFIG 0x18ed0
+#define MALI_C55_5BIN_HIST_DISABLE_MASK BIT(0)
+#define MALI_C55_5BIN_HIST_SWITCH_MASK GENMASK(2, 1)
+#define MALI_C55_5BIN_HIST_SWITCH(x) ((x) << 1)
+#define MALI_C55_AF_DISABLE_MASK BIT(4)
+#define MALI_C55_AF_SWITCH_MASK BIT(5)
+#define MALI_C55_AWB_DISABLE_MASK BIT(8)
+#define MALI_C55_AWB_SWITCH_MASK BIT(9)
+#define MALI_C55_AWB_SWITCH(x) ((x) << 9)
+#define MALI_C55_AEXP_HIST_DISABLE_MASK BIT(12)
+#define MALI_C55_AEXP_HIST_DISABLE (0x01 << 12)
+#define MALI_C55_AEXP_HIST_SWITCH_MASK GENMASK(14, 13)
+#define MALI_C55_AEXP_HIST_SWITCH(x) ((x) << 13)
+#define MALI_C55_AEXP_IHIST_DISABLE_MASK BIT(16)
+#define MALI_C55_AEXP_IHIST_DISABLE (0x01 << 12)
+#define MALI_C55_AEXP_SRC_MASK BIT(24)
+
+#define MALI_C55_REG_TPG_CH0 0x18ed8
+#define MALI_C55_TEST_PATTERN_ON_OFF BIT(0)
+#define MALI_C55_TEST_PATTERN_RGB_MASK BIT(1)
+#define MALI_C55_TEST_PATTERN_RGB(x) ((x) << 1)
+#define MALI_C55_REG_TPG_R_BACKGROUND 0x18ee0
+#define MALI_C55_REG_TPG_G_BACKGROUND 0x18ee4
+#define MALI_C55_REG_TPG_B_BACKGROUND 0x18ee8
+#define MALI_C55_TPG_BACKGROUND_MAX 0xfffff
+#define MALI_C55_REG_INPUT_WIDTH 0x18f98
+#define MALI_C55_INPUT_WIDTH_MASK GENMASK(18, 16)
+#define MALI_C55_INPUT_WIDTH_8BIT (0 << 16)
+#define MALI_C55_INPUT_WIDTH_10BIT (1 << 16)
+#define MALI_C55_INPUT_WIDTH_12BIT (2 << 16)
+#define MALI_C55_INPUT_WIDTH_14BIT (3 << 16)
+#define MALI_C55_INPUT_WIDTH_16BIT (4 << 16)
+#define MALI_C55_INPUT_WIDTH_20BIT (5 << 16)
+#define MALI_C55_REG_SPACE_SIZE 0x4000
+#define MALI_C55_REG_CONFIG_SPACES_OFFSET 0x0ab6c
+#define MALI_C55_CONFIG_SPACE_SIZE 0x1231c
+
+#define MALI_C55_REG_DIGITAL_GAIN 0x1926c
+#define MALI_C55_DIGITAL_GAIN_MASK GENMASK(12, 0)
+#define MALI_C55_REG_DIGITAL_GAIN_OFFSET 0x19270
+#define MALI_C55_DIGITAL_GAIN_OFFSET_MASK GENMASK(19, 0)
+
+#define MALI_C55_REG_SINTER_CONFIG 0x19348
+#define MALI_C55_SINTER_VIEW_FILTER_MASK GENMASK(1, 0)
+#define MALI_C55_SINTER_SCALE_MODE_MASK GENMASK(3, 2)
+#define MALI_C55_SINTER_ENABLE_MASK BIT(4)
+#define MALI_C55_SINTER_FILTER_SELECT_MASK BIT(5)
+#define MALI_C55_SINTER_INT_SELECT_MASK BIT(6)
+#define MALI_C55_SINTER_RM_ENABLE_MASK BIT(7)
+
+/* Temper DMA */
+#define MALI_C55_REG_TEMPER_DMA_IO 0x1ab78
+#define MALI_C55_TEMPER_DMA_WRITE_ON BIT(0)
+#define MALI_C55_TEMPER_DMA_READ_ON BIT(1)
+
+/* Black Level Correction Configuration */
+#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_00 0x1abcc
+#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_01 0x1abd0
+#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_10 0x1abd4
+#define MALI_C55_REG_SENSOR_OFF_PRE_SHA_11 0x1abd8
+#define MALI_C55_SENSOR_OFF_PRE_SHA_MASK 0xfffff
+
+/* Lens Mesh Shading Configuration */
+#define MALI_C55_REG_MESH_SHADING_TABLES 0x13074
+#define MALI_C55_REG_MESH_SHADING_CONFIG 0x1abfc
+#define MALI_C55_MESH_SHADING_ENABLE_MASK BIT(0)
+#define MALI_C55_MESH_SHADING_MESH_SHOW_MASK BIT(1)
+#define MALI_C55_MESH_SHADING_MESH_SHOW(x) ((x) << 1)
+#define MALI_C55_MESH_SHADING_SCALE_MASK GENMASK(4, 2)
+#define MALI_C55_MESH_SHADING_SCALE(x) ((x) << 2)
+#define MALI_C55_MESH_SHADING_PAGE_R_MASK GENMASK(9, 8)
+#define MALI_C55_MESH_SHADING_PAGE_R(x) ((x) << 8)
+#define MALI_C55_MESH_SHADING_PAGE_G_MASK GENMASK(11, 10)
+#define MALI_C55_MESH_SHADING_PAGE_G(x) ((x) << 10)
+#define MALI_C55_MESH_SHADING_PAGE_B_MASK GENMASK(13, 12)
+#define MALI_C55_MESH_SHADING_PAGE_B(x) ((x) << 12)
+#define MALI_C55_MESH_SHADING_MESH_WIDTH_MASK GENMASK(21, 16)
+#define MALI_C55_MESH_SHADING_MESH_WIDTH(x) ((x) << 16)
+#define MALI_C55_MESH_SHADING_MESH_HEIGHT_MASK GENMASK(29, 24)
+#define MALI_C55_MESH_SHADING_MESH_HEIGHT(x) ((x) << 24)
+
+#define MALI_C55_REG_MESH_SHADING_ALPHA_BANK 0x1ac04
+#define MALI_C55_MESH_SHADING_ALPHA_BANK_R_MASK GENMASK(2, 0)
+#define MALI_C55_MESH_SHADING_ALPHA_BANK_G_MASK GENMASK(5, 3)
+#define MALI_C55_MESH_SHADING_ALPHA_BANK_G(x) ((x) << 3)
+#define MALI_C55_MESH_SHADING_ALPHA_BANK_B_MASK GENMASK(8, 6)
+#define MALI_C55_MESH_SHADING_ALPHA_BANK_B(x) ((x) << 6)
+#define MALI_C55_REG_MESH_SHADING_ALPHA 0x1ac08
+#define MALI_C55_MESH_SHADING_ALPHA_R_MASK GENMASK(7, 0)
+#define MALI_C55_MESH_SHADING_ALPHA_G_MASK GENMASK(15, 8)
+#define MALI_C55_MESH_SHADING_ALPHA_G(x) ((x) << 8)
+#define MALI_C55_MESH_SHADING_ALPHA_B_MASK GENMASK(23, 16)
+#define MALI_C55_MESH_SHADING_ALPHA_B(x) ((x) << 16)
+#define MALI_C55_REG_MESH_SHADING_MESH_STRENGTH 0x1ac0c
+#define MALI_c55_MESH_STRENGTH_MASK GENMASK(15, 0)
+
+/* AWB Gains Configuration */
+#define MALI_C55_REG_AWB_GAINS1 0x1ac10
+#define MALI_C55_AWB_GAIN00_MASK GENMASK(11, 0)
+#define MALI_C55_AWB_GAIN01_MASK GENMASK(27, 16)
+#define MALI_C55_AWB_GAIN01(x) ((x) << 16)
+#define MALI_C55_REG_AWB_GAINS2 0x1ac14
+#define MALI_C55_AWB_GAIN10_MASK GENMASK(11, 0)
+#define MALI_C55_AWB_GAIN11_MASK GENMASK(27, 16)
+#define MALI_C55_AWB_GAIN11(x) ((x) << 16)
+#define MALI_C55_REG_AWB_GAINS1_AEXP 0x1ac18
+#define MALI_C55_REG_AWB_GAINS2_AEXP 0x1ac1c
+
+/* Colour Correction Matrix Configuration */
+#define MALI_C55_REG_CCM_ENABLE 0x1b07c
+#define MALI_C55_CCM_ENABLE_MASK BIT(0)
+#define MALI_C55_REG_CCM_COEF_R_R 0x1b080
+#define MALI_C55_REG_CCM_COEF_R_G 0x1b084
+#define MALI_C55_REG_CCM_COEF_R_B 0x1b088
+#define MALI_C55_REG_CCM_COEF_G_R 0x1b090
+#define MALI_C55_REG_CCM_COEF_G_G 0x1b094
+#define MALI_C55_REG_CCM_COEF_G_B 0x1b098
+#define MALI_C55_REG_CCM_COEF_B_R 0x1b0a0
+#define MALI_C55_REG_CCM_COEF_B_G 0x1b0a4
+#define MALI_C55_REG_CCM_COEF_B_B 0x1b0a8
+#define MALI_C55_CCM_COEF_MASK GENMASK(12, 0)
+#define MALI_C55_REG_CCM_ANTIFOG_GAIN_R 0x1b0b0
+#define MALI_C55_REG_CCM_ANTIFOG_GAIN_G 0x1b0b4
+#define MALI_C55_REG_CCM_ANTIFOG_GAIN_B 0x1b0b8
+#define MALI_C55_CCM_ANTIFOG_GAIN_MASK GENMASK(11, 0)
+#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_R 0x1b0c0
+#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_G 0x1b0c4
+#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_B 0x1b0c8
+#define MALI_C55_CCM_ANTIFOG_OFFSET_MASK GENMASK(11, 0)
+
+/* AWB Statistics Configuration */
+#define MALI_C55_REG_AWB_STATS_MODE 0x1b29c
+#define MALI_C55_AWB_STATS_MODE_MASK BIT(0)
+#define MALI_C55_REG_AWB_WHITE_LEVEL 0x1b2a0
+#define MALI_C55_AWB_WHITE_LEVEL_MASK GENMASK(9, 0)
+#define MALI_C55_REG_AWB_BLACK_LEVEL 0x1b2a4
+#define MALI_C55_AWB_BLACK_LEVEL_MASK GENMASK(9, 0)
+#define MALI_C55_REG_AWB_CR_MAX 0x1b2a8
+#define MALI_C55_AWB_CR_MAX_MASK GENMASK(11, 0)
+#define MALI_C55_REG_AWB_CR_MIN 0x1b2ac
+#define MALI_C55_AWB_CR_MIN_MASK GENMASK(11, 0)
+#define MALI_C55_REG_AWB_CB_MAX 0x1b2b0
+#define MALI_C55_AWB_CB_MAX_MASK GENMASK(11, 0)
+#define MALI_C55_REG_AWB_CB_MIN 0x1b2b4
+#define MALI_C55_AWB_CB_MIN_MASK GENMASK(11, 0)
+#define MALI_C55_REG_AWB_NODES_USED 0x1b2c4
+#define MALI_C55_AWB_NODES_USED_HORIZ_MASK GENMASK(7, 0)
+#define MALI_C55_AWB_NODES_USED_VERT_MASK GENMASK(15, 8)
+#define MALI_C55_AWB_NODES_USED_VERT(x) ((x) << 8)
+#define MALI_C55_REG_AWB_CR_HIGH 0x1b2c8
+#define MALI_C55_AWB_CR_HIGH_MASK GENMASK(11, 0)
+#define MALI_C55_REG_AWB_CR_LOW 0x1b2cc
+#define MALI_C55_AWB_CR_LOW_MASK GENMASK(11, 0)
+#define MALI_C55_REG_AWB_CB_HIGH 0x1b2d0
+#define MALI_C55_AWB_CB_HIGH_MASK GENMASK(11, 0)
+#define MALI_C55_REG_AWB_CB_LOW 0x1b2d4
+#define MALI_C55_AWB_CB_LOW_MASK GENMASK(11, 0)
+
+/* AEXP Metering Histogram Configuration */
+#define MALI_C55_REG_AEXP_HIST_BASE 0x1b730
+#define MALI_C55_REG_AEXP_IHIST_BASE 0x1bbac
+#define MALI_C55_AEXP_HIST_SKIP_OFFSET 0
+#define MALI_C55_AEXP_HIST_SKIP_X_MASK GENMASK(2, 0)
+#define MALI_C55_AEXP_HIST_SKIP_X(x) ((x) << 0)
+#define MALI_C55_AEXP_HIST_OFFSET_X_MASK BIT(3)
+#define MALI_C55_AEXP_HIST_OFFSET_X(x) ((x) << 3)
+#define MALI_C55_AEXP_HIST_SKIP_Y_MASK GENMASK(6, 4)
+#define MALI_C55_AEXP_HIST_SKIP_Y(x) ((x) << 4)
+#define MALI_C55_AEXP_HIST_OFFSET_Y_MASK BIT(7)
+#define MALI_C55_AEXP_HIST_OFFSET_Y(x) ((x) << 7)
+#define MALI_C55_AEXP_HIST_SCALE_OFFSET 4
+#define MALI_C55_AEXP_HIST_SCALE_BOTTOM_MASK GENMASK(3, 0)
+#define MALI_C55_AEXP_HIST_SCALE_TOP_MASK GENMASK(7, 4)
+#define MALI_C55_AEXP_HIST_SCALE_TOP(x) ((x) << 4)
+#define MALI_C55_AEXP_HIST_PLANE_MODE_OFFSET 16
+#define MALI_C55_AEXP_HIST_PLANE_MODE_MASK GENMASK(2, 0)
+#define MALI_C55_AEXP_HIST_NODES_USED_OFFSET 52
+#define MALI_C55_AEXP_HIST_NODES_USED_HORIZ_MASK GENMASK(7, 0)
+#define MALI_C55_AEXP_HIST_NODES_USED_VERT_MASK GENMASK(15, 8)
+#define MALI_C55_AEXP_HIST_NODES_USED_VERT(x) ((x) << 8)
+#define MALI_C55_AEXP_HIST_ZONE_WEIGHTS_OFFSET 56
+#define MALI_C55_AEXP_HIST_ZONE_WEIGHT_MASK 0x0f0f0f0f
+
+/*
+ * The Mali-C55 ISP has up to two output pipes; known as full resolution and
+ * down scaled. The register space for these is laid out identically, but offset
+ * by 372 bytes.
+ */
+#define MALI_C55_CAP_DEV_FR_REG_OFFSET 0x0
+#define MALI_C55_CAP_DEV_DS_REG_OFFSET 0x174
+
+#define MALI_C55_REG_CS_CONV_CONFIG 0x1c098
+#define MALI_C55_CS_CONV_MATRIX_MASK BIT(0)
+#define MALI_C55_CS_CONV_FILTER_MASK BIT(1)
+#define MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_MASK BIT(2)
+#define MALI_C55_CS_CONV_VERT_DOWNSAMPLE_MASK BIT(3)
+#define MALI_C55_CS_CONV_FILTER_ENABLE (0x01 << 1)
+#define MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_ENABLE (0x01 << 2)
+#define MALI_C55_CS_CONV_VERT_DOWNSAMPLE_ENABLE (0x01 << 3)
+#define MALI_C55_REG_Y_WRITER_MODE 0x1c0ec
+#define MALI_C55_REG_UV_WRITER_MODE 0x1c144
+#define MALI_C55_WRITER_MODE_MASK GENMASK(4, 0)
+#define MALI_C55_OUTPUT_DISABLED 0
+#define MALI_C55_OUTPUT_RGB32 1
+#define MALI_C55_OUTPUT_A2R10G10B10 2
+#define MALI_C55_OUTPUT_RGB565 3
+#define MALI_C55_OUTPUT_RGB24 4
+#define MALI_C55_OUTPUT_GEN32 5
+#define MALI_C55_OUTPUT_RAW16 6
+#define MALI_C55_OUTPUT_AYUV 8
+#define MALI_C55_OUTPUT_Y410 9
+#define MALI_C55_OUTPUT_YUY2 10
+#define MALI_C55_OUTPUT_UYVY 11
+#define MALI_C55_OUTPUT_Y210 12
+#define MALI_C55_OUTPUT_NV12_21 13
+#define MALI_C55_OUTPUT_YUV_420_422 17
+#define MALI_C55_OUTPUT_P210_P010 19
+#define MALI_C55_OUTPUT_YUV422 20
+#define MALI_C55_WRITER_SUBMODE_MASK GENMASK(7, 6)
+#define MALI_C55_WRITER_SUBMODE(x) ((x) << 6)
+#define MALI_C55_OUTPUT_PLANE_ALT0 0
+#define MALI_C55_OUTPUT_PLANE_ALT1 1
+#define MALI_C55_OUTPUT_PLANE_ALT2 2
+#define MALI_C55_WRITER_FRAME_WRITE_MASK BIT(9)
+#define MALI_C55_WRITER_FRAME_WRITE_ENABLE (0x01 << 9)
+#define MALI_C55_REG_ACTIVE_OUT_Y_SIZE 0x1c0f0
+#define MALI_C55_REG_ACTIVE_OUT_UV_SIZE 0x1c148
+#define MALI_C55_REG_ACTIVE_OUT_SIZE_W(w) ((w) << 0)
+#define MALI_C55_REG_ACTIVE_OUT_SIZE_H(h) ((h) << 16)
+#define MALI_C55_REG_Y_WRITER_BANKS_BASE 0x1c0f4
+#define MALI_C55_REG_Y_WRITER_BANKS_CONFIG 0x1c108
+#define MALI_C55_REG_Y_WRITER_MAX_BANKS_MASK GENMASK(2, 0)
+#define MALI_C55_REG_Y_WRITER_BANKS_RESTART BIT(3)
+#define MALI_C55_REG_Y_WRITER_OFFSET 0x1c10c
+#define MALI_C55_REG_UV_WRITER_BANKS_BASE 0x1c14c
+#define MALI_C55_REG_UV_WRITER_BANKS_CONFIG 0x1c160
+#define MALI_C55_REG_UV_WRITER_MAX_BANKS_MASK GENMASK(2, 0)
+#define MALI_C55_REG_UV_WRITER_BANKS_RESTART BIT(3)
+#define MALI_C55_REG_UV_WRITER_OFFSET 0x1c164
+
+#define MALI_C55_REG_TEST_GEN_CH0_OFF_ON
+#define MALI_C55_REG_TEST_GEN_CH0_PATTERN_TYPE 0x18edc
+
+#define MALI_C55_REG_CROP_EN 0x1c028
+#define MALI_C55_CROP_ENABLE BIT(0)
+#define MALI_C55_REG_CROP_X_START 0x1c02c
+#define MALI_C55_REG_CROP_Y_START 0x1c030
+#define MALI_C55_REG_CROP_X_SIZE 0x1c034
+#define MALI_C55_REG_CROP_Y_SIZE 0x1c038
+#define MALI_C55_REG_SCALER_TIMEOUT_EN 0x1c040
+#define MALI_C55_SCALER_TIMEOUT_EN BIT(4)
+#define MALI_C55_SCALER_TIMEOUT(t) ((t) << 16)
+#define MALI_C55_REG_SCALER_IN_WIDTH 0x1c044
+#define MALI_C55_REG_SCALER_IN_HEIGHT 0x1c048
+#define MALI_C55_REG_SCALER_OUT_WIDTH 0x1c04c
+#define MALI_C55_REG_SCALER_OUT_HEIGHT 0x1c050
+#define MALI_C55_REG_SCALER_HFILT_TINC 0x1c054
+#define MALI_C55_REG_SCALER_HFILT_COEF 0x1c058
+#define MALI_C55_REG_SCALER_VFILT_TINC 0x1c05c
+#define MALI_C55_REG_SCALER_VFILT_COEF 0x1c060
+
+#define MALI_C55_REG_GAMMA_RGB_ENABLE 0x1c064
+#define MALI_C55_GAMMA_ENABLE_MASK BIT(0)
+#define MALI_C55_REG_GAMMA_GAINS_1 0x1c068
+#define MALI_C55_GAMMA_GAIN_R_MASK GENMASK(11, 0)
+#define MALI_C55_GAMMA_GAIN_G_MASK GENMASK(27, 16)
+#define MALI_C55_REG_GAMMA_GAINS_2 0x1c06c
+#define MALI_C55_GAMMA_GAIN_B_MASK GENMASK(11, 0)
+#define MALI_C55_REG_GAMMA_OFFSETS_1 0x1c070
+#define MALI_C55_GAMMA_OFFSET_R_MASK GENMASK(11, 0)
+#define MALI_C55_GAMMA_OFFSET_G_MASK GENMASK(27, 16)
+#define MALI_C55_REG_GAMMA_OFFSETS_2 0x1c074
+#define MALI_C55_GAMMA_OFFSET_B_MASK GENMASK(11, 0)
+
+/*
+ * A re-definition of an above register. These will usually be written on a per
+ * capture device basis and handled with mali_c55_cap_dev_write(), but on
+ * startup is written by core.c
+ */
+#define MALI_C55_REG_FR_GAMMA_RGB_ENABLE 0x1c064
+#define MALI_C55_REG_DS_GAMMA_RGB_ENABLE 0x1c1d8
+
+#define MALI_C55_REG_FR_SCALER_HFILT 0x34a8
+#define MALI_C55_REG_FR_SCALER_VFILT 0x44a8
+#define MALI_C55_REG_DS_SCALER_HFILT 0x14a8
+#define MALI_C55_REG_DS_SCALER_VFILT 0x24a8
+
+#endif /* _MALI_C55_REGISTERS_H */
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
new file mode 100644
index 000000000000..a8d739af74b6
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
@@ -0,0 +1,1156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Mali-C55 ISP Driver - Image signal processor
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include <linux/math.h>
+#include <linux/minmax.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "mali-c55-common.h"
+#include "mali-c55-registers.h"
+
+/* Scaling factor in Q4.20 format. */
+#define MALI_C55_RSZ_SCALER_FACTOR (1U << 20)
+
+#define MALI_C55_RSZ_COEFS_BANKS 8
+#define MALI_C55_RSZ_COEFS_ENTRIES 64
+
+static inline struct mali_c55_resizer *
+sd_to_mali_c55_rsz(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct mali_c55_resizer, sd);
+}
+
+static const unsigned int
+mali_c55_rsz_filter_coeffs_h[MALI_C55_RSZ_COEFS_BANKS]
+ [MALI_C55_RSZ_COEFS_ENTRIES] = {
+ { /* Bank 0 */
+ 0x24fc0000, 0x0000fc24, 0x27fc0000, 0x0000fc21,
+ 0x28fc0000, 0x0000fd1f, 0x2cfb0000, 0x0000fd1c,
+ 0x2efb0000, 0x0000fd1a, 0x30fb0000, 0x0000fe17,
+ 0x32fb0000, 0x0000fe15, 0x35fb0000, 0x0000fe12,
+ 0x35fc0000, 0x0000ff10, 0x37fc0000, 0x0000ff0e,
+ 0x39fc0000, 0x0000ff0c, 0x3afd0000, 0x0000ff0a,
+ 0x3afe0000, 0x00000008, 0x3cfe0000, 0x00000006,
+ 0x3dff0000, 0x00000004, 0x3d000000, 0x00000003,
+ 0x3c020000, 0x00000002, 0x3d030000, 0x00000000,
+ 0x3d040000, 0x000000ff, 0x3c060000, 0x000000fe,
+ 0x3a080000, 0x000000fe, 0x3a0aff00, 0x000000fd,
+ 0x390cff00, 0x000000fc, 0x370eff00, 0x000000fc,
+ 0x3510ff00, 0x000000fc, 0x3512fe00, 0x000000fb,
+ 0x3215fe00, 0x000000fb, 0x3017fe00, 0x000000fb,
+ 0x2e1afd00, 0x000000fb, 0x2c1cfd00, 0x000000fb,
+ 0x281ffd00, 0x000000fc, 0x2721fc00, 0x000000fc,
+ },
+ { /* Bank 1 */
+ 0x25fb0000, 0x0000fb25, 0x27fb0000, 0x0000fb23,
+ 0x29fb0000, 0x0000fb21, 0x2afc0000, 0x0000fb1f,
+ 0x2cfc0000, 0x0000fb1d, 0x2efc0000, 0x0000fb1b,
+ 0x2ffd0000, 0x0000fb19, 0x2ffe0000, 0x0000fc17,
+ 0x31fe0000, 0x0000fc15, 0x32ff0000, 0x0000fc13,
+ 0x3400ff00, 0x0000fc11, 0x3301ff00, 0x0000fd10,
+ 0x3402ff00, 0x0000fd0e, 0x3503ff00, 0x0000fd0c,
+ 0x3505ff00, 0x0000fd0a, 0x3506fe00, 0x0000fe09,
+ 0x3607fe00, 0x0000fe07, 0x3509fe00, 0x0000fe06,
+ 0x350afd00, 0x0000ff05, 0x350cfd00, 0x0000ff03,
+ 0x340efd00, 0x0000ff02, 0x3310fd00, 0x0000ff01,
+ 0x3411fc00, 0x0000ff00, 0x3213fc00, 0x000000ff,
+ 0x3115fc00, 0x000000fe, 0x2f17fc00, 0x000000fe,
+ 0x2f19fb00, 0x000000fd, 0x2e1bfb00, 0x000000fc,
+ 0x2c1dfb00, 0x000000fc, 0x2a1ffb00, 0x000000fc,
+ 0x2921fb00, 0x000000fb, 0x2723fb00, 0x000000fb,
+ },
+ { /* Bank 2 */
+ 0x1f010000, 0x0000011f, 0x21010000, 0x0000001e,
+ 0x21020000, 0x0000001d, 0x22020000, 0x0000001c,
+ 0x23030000, 0x0000ff1b, 0x2404ff00, 0x0000ff1a,
+ 0x2504ff00, 0x0000ff19, 0x2505ff00, 0x0000ff18,
+ 0x2606ff00, 0x0000fe17, 0x2607ff00, 0x0000fe16,
+ 0x2708ff00, 0x0000fe14, 0x2709ff00, 0x0000fe13,
+ 0x270aff00, 0x0000fe12, 0x280bfe00, 0x0000fe11,
+ 0x280cfe00, 0x0000fe10, 0x280dfe00, 0x0000fe0f,
+ 0x280efe00, 0x0000fe0e, 0x280ffe00, 0x0000fe0d,
+ 0x2810fe00, 0x0000fe0c, 0x2811fe00, 0x0000fe0b,
+ 0x2712fe00, 0x0000ff0a, 0x2713fe00, 0x0000ff09,
+ 0x2714fe00, 0x0000ff08, 0x2616fe00, 0x0000ff07,
+ 0x2617fe00, 0x0000ff06, 0x2518ff00, 0x0000ff05,
+ 0x2519ff00, 0x0000ff04, 0x241aff00, 0x0000ff04,
+ 0x231bff00, 0x00000003, 0x221c0000, 0x00000002,
+ 0x211d0000, 0x00000002, 0x211e0000, 0x00000001,
+ },
+ { /* Bank 3 */
+ 0x1b06ff00, 0x00ff061b, 0x1b07ff00, 0x00ff061a,
+ 0x1c07ff00, 0x00ff051a, 0x1c08ff00, 0x00ff0519,
+ 0x1c09ff00, 0x00ff0419, 0x1d09ff00, 0x00ff0418,
+ 0x1e0aff00, 0x00ff0317, 0x1e0aff00, 0x00ff0317,
+ 0x1e0bff00, 0x00ff0316, 0x1f0cff00, 0x00ff0215,
+ 0x1e0cff00, 0x00000215, 0x1e0dff00, 0x00000214,
+ 0x1e0e0000, 0x00000113, 0x1e0e0000, 0x00000113,
+ 0x1e0f0000, 0x00000112, 0x1f100000, 0x00000011,
+ 0x20100000, 0x00000010, 0x1f110000, 0x00000010,
+ 0x1e120100, 0x0000000f, 0x1e130100, 0x0000000e,
+ 0x1e130100, 0x0000000e, 0x1e140200, 0x0000ff0d,
+ 0x1e150200, 0x0000ff0c, 0x1f1502ff, 0x0000ff0c,
+ 0x1e1603ff, 0x0000ff0b, 0x1e1703ff, 0x0000ff0a,
+ 0x1e1703ff, 0x0000ff0a, 0x1d1804ff, 0x0000ff09,
+ 0x1c1904ff, 0x0000ff09, 0x1c1905ff, 0x0000ff08,
+ 0x1c1a05ff, 0x0000ff07, 0x1b1a06ff, 0x0000ff07,
+ },
+ { /* Bank 4 */
+ 0x17090000, 0x00000917, 0x18090000, 0x00000916,
+ 0x170a0100, 0x00000816, 0x170a0100, 0x00000816,
+ 0x180b0100, 0x00000715, 0x180b0100, 0x00000715,
+ 0x170c0100, 0x00000715, 0x190c0100, 0x00000614,
+ 0x180d0100, 0x00000614, 0x190d0200, 0x00000513,
+ 0x180e0200, 0x00000513, 0x180e0200, 0x00000513,
+ 0x1a0e0200, 0x00000412, 0x190f0200, 0x00000412,
+ 0x190f0300, 0x00000411, 0x18100300, 0x00000411,
+ 0x1a100300, 0x00000310, 0x18110400, 0x00000310,
+ 0x19110400, 0x0000030f, 0x19120400, 0x0000020f,
+ 0x1a120400, 0x0000020e, 0x18130500, 0x0000020e,
+ 0x18130500, 0x0000020e, 0x19130500, 0x0000020d,
+ 0x18140600, 0x0000010d, 0x19140600, 0x0000010c,
+ 0x17150700, 0x0000010c, 0x18150700, 0x0000010b,
+ 0x18150700, 0x0000010b, 0x17160800, 0x0000010a,
+ 0x17160800, 0x0000010a, 0x18160900, 0x00000009,
+ },
+ { /* Bank 5 */
+ 0x120b0300, 0x00030b12, 0x120c0300, 0x00030b11,
+ 0x110c0400, 0x00030b11, 0x110c0400, 0x00030b11,
+ 0x130c0400, 0x00020a11, 0x120d0400, 0x00020a11,
+ 0x110d0500, 0x00020a11, 0x110d0500, 0x00020a11,
+ 0x130d0500, 0x00010911, 0x130e0500, 0x00010910,
+ 0x120e0600, 0x00010910, 0x120e0600, 0x00010910,
+ 0x130e0600, 0x00010810, 0x120f0600, 0x00010810,
+ 0x120f0700, 0x00000810, 0x130f0700, 0x0000080f,
+ 0x140f0700, 0x0000070f, 0x130f0800, 0x0000070f,
+ 0x12100800, 0x0000070f, 0x12100801, 0x0000060f,
+ 0x13100801, 0x0000060e, 0x12100901, 0x0000060e,
+ 0x12100901, 0x0000060e, 0x13100901, 0x0000050e,
+ 0x13110901, 0x0000050d, 0x11110a02, 0x0000050d,
+ 0x11110a02, 0x0000050d, 0x12110a02, 0x0000040d,
+ 0x13110a02, 0x0000040c, 0x11110b03, 0x0000040c,
+ 0x11110b03, 0x0000040c, 0x12110b03, 0x0000030c,
+ },
+ { /* Bank 6 */
+ 0x0b0a0805, 0x00080a0c, 0x0b0a0805, 0x00080a0c,
+ 0x0c0a0805, 0x00080a0b, 0x0c0a0805, 0x00080a0b,
+ 0x0d0a0805, 0x00070a0b, 0x0d0a0805, 0x00070a0b,
+ 0x0d0a0805, 0x00070a0b, 0x0c0a0806, 0x00070a0b,
+ 0x0b0b0806, 0x00070a0b, 0x0c0b0806, 0x0007090b,
+ 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
+ 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
+ 0x0b0b0906, 0x0007090b, 0x0c0b0906, 0x0006090b,
+ 0x0c0b0906, 0x0006090b, 0x0c0b0906, 0x0006090b,
+ 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
+ 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
+ 0x0b0b0907, 0x0006090b, 0x0c0b0907, 0x0006080b,
+ 0x0b0b0a07, 0x0006080b, 0x0c0b0a07, 0x0006080a,
+ 0x0d0b0a07, 0x0005080a, 0x0d0b0a07, 0x0005080a,
+ 0x0d0b0a07, 0x0005080a, 0x0c0b0a08, 0x0005080a,
+ 0x0c0b0a08, 0x0005080a, 0x0c0b0a08, 0x0005080a,
+ },
+ { /* Bank 7 */
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ 0x0909090a, 0x00090909, 0x0909090a, 0x00090909,
+ }
+};
+
+static const unsigned int
+mali_c55_rsz_filter_coeffs_v[MALI_C55_RSZ_COEFS_BANKS]
+ [MALI_C55_RSZ_COEFS_ENTRIES] = {
+ { /* Bank 0 */
+ 0x2424fc00, 0x000000fc, 0x2721fc00, 0x000000fc,
+ 0x281ffd00, 0x000000fc, 0x2c1cfd00, 0x000000fb,
+ 0x2e1afd00, 0x000000fb, 0x3017fe00, 0x000000fb,
+ 0x3215fe00, 0x000000fb, 0x3512fe00, 0x000000fb,
+ 0x3510ff00, 0x000000fc, 0x370eff00, 0x000000fc,
+ 0x390cff00, 0x000000fc, 0x3a0aff00, 0x000000fd,
+ 0x3a080000, 0x000000fe, 0x3c060000, 0x000000fe,
+ 0x3d040000, 0x000000ff, 0x3d030000, 0x00000000,
+ 0x3c020000, 0x00000002, 0x3d000000, 0x00000003,
+ 0x3dff0000, 0x00000004, 0x3cfe0000, 0x00000006,
+ 0x3afe0000, 0x00000008, 0x3afd0000, 0x0000ff0a,
+ 0x39fc0000, 0x0000ff0c, 0x37fc0000, 0x0000ff0e,
+ 0x35fc0000, 0x0000ff10, 0x35fb0000, 0x0000fe12,
+ 0x32fb0000, 0x0000fe15, 0x30fb0000, 0x0000fe17,
+ 0x2efb0000, 0x0000fd1a, 0x2cfb0000, 0x0000fd1c,
+ 0x28fc0000, 0x0000fd1f, 0x27fc0000, 0x0000fc21,
+ },
+ { /* Bank 1 */
+ 0x2525fb00, 0x000000fb, 0x2723fb00, 0x000000fb,
+ 0x2921fb00, 0x000000fb, 0x2a1ffb00, 0x000000fc,
+ 0x2c1dfb00, 0x000000fc, 0x2e1bfb00, 0x000000fc,
+ 0x2f19fb00, 0x000000fd, 0x2f17fc00, 0x000000fe,
+ 0x3115fc00, 0x000000fe, 0x3213fc00, 0x000000ff,
+ 0x3411fc00, 0x0000ff00, 0x3310fd00, 0x0000ff01,
+ 0x340efd00, 0x0000ff02, 0x350cfd00, 0x0000ff03,
+ 0x350afd00, 0x0000ff05, 0x3509fe00, 0x0000fe06,
+ 0x3607fe00, 0x0000fe07, 0x3506fe00, 0x0000fe09,
+ 0x3505ff00, 0x0000fd0a, 0x3503ff00, 0x0000fd0c,
+ 0x3402ff00, 0x0000fd0e, 0x3301ff00, 0x0000fd10,
+ 0x3400ff00, 0x0000fc11, 0x32ff0000, 0x0000fc13,
+ 0x31fe0000, 0x0000fc15, 0x2ffe0000, 0x0000fc17,
+ 0x2ffd0000, 0x0000fb19, 0x2efc0000, 0x0000fb1b,
+ 0x2cfc0000, 0x0000fb1d, 0x2afc0000, 0x0000fb1f,
+ 0x29fb0000, 0x0000fb21, 0x27fb0000, 0x0000fb23,
+ },
+ { /* Bank 2 */
+ 0x1f1f0100, 0x00000001, 0x211e0000, 0x00000001,
+ 0x211d0000, 0x00000002, 0x221c0000, 0x00000002,
+ 0x231bff00, 0x00000003, 0x241aff00, 0x0000ff04,
+ 0x2519ff00, 0x0000ff04, 0x2518ff00, 0x0000ff05,
+ 0x2617fe00, 0x0000ff06, 0x2616fe00, 0x0000ff07,
+ 0x2714fe00, 0x0000ff08, 0x2713fe00, 0x0000ff09,
+ 0x2712fe00, 0x0000ff0a, 0x2811fe00, 0x0000fe0b,
+ 0x2810fe00, 0x0000fe0c, 0x280ffe00, 0x0000fe0d,
+ 0x280efe00, 0x0000fe0e, 0x280dfe00, 0x0000fe0f,
+ 0x280cfe00, 0x0000fe10, 0x280bfe00, 0x0000fe11,
+ 0x270aff00, 0x0000fe12, 0x2709ff00, 0x0000fe13,
+ 0x2708ff00, 0x0000fe14, 0x2607ff00, 0x0000fe16,
+ 0x2606ff00, 0x0000fe17, 0x2505ff00, 0x0000ff18,
+ 0x2504ff00, 0x0000ff19, 0x2404ff00, 0x0000ff1a,
+ 0x23030000, 0x0000ff1b, 0x22020000, 0x0000001c,
+ 0x21020000, 0x0000001d, 0x21010000, 0x0000001e,
+ },
+ { /* Bank 3 */
+ 0x1b1b06ff, 0x0000ff06, 0x1b1a06ff, 0x0000ff07,
+ 0x1c1a05ff, 0x0000ff07, 0x1c1905ff, 0x0000ff08,
+ 0x1c1904ff, 0x0000ff09, 0x1d1804ff, 0x0000ff09,
+ 0x1e1703ff, 0x0000ff0a, 0x1e1703ff, 0x0000ff0a,
+ 0x1e1603ff, 0x0000ff0b, 0x1f1502ff, 0x0000ff0c,
+ 0x1e150200, 0x0000ff0c, 0x1e140200, 0x0000ff0d,
+ 0x1e130100, 0x0000000e, 0x1e130100, 0x0000000e,
+ 0x1e120100, 0x0000000f, 0x1f110000, 0x00000010,
+ 0x20100000, 0x00000010, 0x1f100000, 0x00000011,
+ 0x1e0f0000, 0x00000112, 0x1e0e0000, 0x00000113,
+ 0x1e0e0000, 0x00000113, 0x1e0dff00, 0x00000214,
+ 0x1e0cff00, 0x00000215, 0x1f0cff00, 0x00ff0215,
+ 0x1e0bff00, 0x00ff0316, 0x1e0aff00, 0x00ff0317,
+ 0x1e0aff00, 0x00ff0317, 0x1d09ff00, 0x00ff0418,
+ 0x1c09ff00, 0x00ff0419, 0x1c08ff00, 0x00ff0519,
+ 0x1c07ff00, 0x00ff051a, 0x1b07ff00, 0x00ff061a,
+ },
+ { /* Bank 4 */
+ 0x17170900, 0x00000009, 0x18160900, 0x00000009,
+ 0x17160800, 0x0000010a, 0x17160800, 0x0000010a,
+ 0x18150700, 0x0000010b, 0x18150700, 0x0000010b,
+ 0x17150700, 0x0000010c, 0x19140600, 0x0000010c,
+ 0x18140600, 0x0000010d, 0x19130500, 0x0000020d,
+ 0x18130500, 0x0000020e, 0x18130500, 0x0000020e,
+ 0x1a120400, 0x0000020e, 0x19120400, 0x0000020f,
+ 0x19110400, 0x0000030f, 0x18110400, 0x00000310,
+ 0x1a100300, 0x00000310, 0x18100300, 0x00000411,
+ 0x190f0300, 0x00000411, 0x190f0200, 0x00000412,
+ 0x1a0e0200, 0x00000412, 0x180e0200, 0x00000513,
+ 0x180e0200, 0x00000513, 0x190d0200, 0x00000513,
+ 0x180d0100, 0x00000614, 0x190c0100, 0x00000614,
+ 0x170c0100, 0x00000715, 0x180b0100, 0x00000715,
+ 0x180b0100, 0x00000715, 0x170a0100, 0x00000816,
+ 0x170a0100, 0x00000816, 0x18090000, 0x00000916,
+ },
+ { /* Bank 5 */
+ 0x12120b03, 0x0000030b, 0x12110b03, 0x0000030c,
+ 0x11110b03, 0x0000040c, 0x11110b03, 0x0000040c,
+ 0x13110a02, 0x0000040c, 0x12110a02, 0x0000040d,
+ 0x11110a02, 0x0000050d, 0x11110a02, 0x0000050d,
+ 0x13110901, 0x0000050d, 0x13100901, 0x0000050e,
+ 0x12100901, 0x0000060e, 0x12100901, 0x0000060e,
+ 0x13100801, 0x0000060e, 0x12100801, 0x0000060f,
+ 0x12100800, 0x0000070f, 0x130f0800, 0x0000070f,
+ 0x140f0700, 0x0000070f, 0x130f0700, 0x0000080f,
+ 0x120f0700, 0x00000810, 0x120f0600, 0x00010810,
+ 0x130e0600, 0x00010810, 0x120e0600, 0x00010910,
+ 0x120e0600, 0x00010910, 0x130e0500, 0x00010910,
+ 0x130d0500, 0x00010911, 0x110d0500, 0x00020a11,
+ 0x110d0500, 0x00020a11, 0x120d0400, 0x00020a11,
+ 0x130c0400, 0x00020a11, 0x110c0400, 0x00030b11,
+ 0x110c0400, 0x00030b11, 0x120c0300, 0x00030b11,
+ },
+ { /* Bank 6 */
+ 0x0b0c0a08, 0x0005080a, 0x0b0c0a08, 0x0005080a,
+ 0x0c0b0a08, 0x0005080a, 0x0c0b0a08, 0x0005080a,
+ 0x0d0b0a07, 0x0005080a, 0x0d0b0a07, 0x0005080a,
+ 0x0d0b0a07, 0x0005080a, 0x0c0b0a07, 0x0006080a,
+ 0x0b0b0a07, 0x0006080b, 0x0c0b0907, 0x0006080b,
+ 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
+ 0x0b0b0907, 0x0006090b, 0x0b0b0907, 0x0006090b,
+ 0x0b0b0907, 0x0006090b, 0x0c0b0906, 0x0006090b,
+ 0x0c0b0906, 0x0006090b, 0x0c0b0906, 0x0006090b,
+ 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
+ 0x0b0b0906, 0x0007090b, 0x0b0b0906, 0x0007090b,
+ 0x0b0b0906, 0x0007090b, 0x0c0b0806, 0x0007090b,
+ 0x0b0b0806, 0x00070a0b, 0x0c0a0806, 0x00070a0b,
+ 0x0d0a0805, 0x00070a0b, 0x0d0a0805, 0x00070a0b,
+ 0x0d0a0805, 0x00070a0b, 0x0c0a0805, 0x00080a0b,
+ 0x0c0a0805, 0x00080a0b, 0x0c0a0805, 0x00080a0b,
+ },
+ { /* Bank 7 */
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ 0x09090909, 0x000a0909, 0x09090909, 0x000a0909,
+ }
+};
+
+static const unsigned int mali_c55_rsz_coef_banks_range_start[] = {
+ 770, 600, 460, 354, 273, 210, 162, 125
+};
+
+/*
+ * Select the right filter coefficients bank based on the scaler input and the
+ * scaler output sizes ratio, set by the v4l2 crop and scale selection
+ * rectangles respectively.
+ */
+static unsigned int mali_c55_rsz_calculate_bank(struct mali_c55 *mali_c55,
+ unsigned int rsz_in,
+ unsigned int rsz_out)
+{
+ unsigned int rsz_ratio = (rsz_out * 1000U) / rsz_in;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_rsz_coef_banks_range_start); i++)
+ if (rsz_ratio >= mali_c55_rsz_coef_banks_range_start[i])
+ break;
+
+ return i;
+}
+
+static const u32 rsz_non_bypass_src_fmts[] = {
+ MEDIA_BUS_FMT_RGB121212_1X36,
+ MEDIA_BUS_FMT_YUV10_1X30
+};
+
+static void mali_c55_resizer_program_coefficients(struct mali_c55_resizer *rsz)
+{
+ struct mali_c55 *mali_c55 = rsz->mali_c55;
+ unsigned int haddr = rsz->id == MALI_C55_RSZ_FR ?
+ MALI_C55_REG_FR_SCALER_HFILT :
+ MALI_C55_REG_DS_SCALER_HFILT;
+ unsigned int vaddr = rsz->id == MALI_C55_RSZ_FR ?
+ MALI_C55_REG_FR_SCALER_VFILT :
+ MALI_C55_REG_DS_SCALER_VFILT;
+
+ for (unsigned int i = 0; i < MALI_C55_RSZ_COEFS_BANKS; i++) {
+ for (unsigned int j = 0; j < MALI_C55_RSZ_COEFS_ENTRIES; j++) {
+ mali_c55_write(mali_c55, haddr,
+ mali_c55_rsz_filter_coeffs_h[i][j]);
+ mali_c55_write(mali_c55, vaddr,
+ mali_c55_rsz_filter_coeffs_v[i][j]);
+
+ haddr += sizeof(u32);
+ vaddr += sizeof(u32);
+ }
+ }
+}
+
+static int mali_c55_rsz_program_crop(struct mali_c55_resizer *rsz,
+ const struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *fmt;
+ const struct v4l2_rect *crop;
+
+ /* Verify if crop should be enabled. */
+ fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SINK_PAD, 0);
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD, 0);
+
+ if (fmt->width == crop->width && fmt->height == crop->height)
+ return MALI_C55_BYPASS_CROP;
+
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_X_START,
+ crop->left);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_Y_START,
+ crop->top);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_X_SIZE,
+ crop->width);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_Y_SIZE,
+ crop->height);
+
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_CROP_EN,
+ MALI_C55_CROP_ENABLE);
+
+ return 0;
+}
+
+static int mali_c55_rsz_program_resizer(struct mali_c55_resizer *rsz,
+ struct v4l2_subdev_state *state)
+{
+ struct mali_c55 *mali_c55 = rsz->mali_c55;
+ const struct v4l2_rect *crop, *scale;
+ unsigned int h_bank, v_bank;
+ u64 h_scale, v_scale;
+
+ /* Verify if scaling should be enabled. */
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD, 0);
+ scale = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD, 0);
+
+ if (crop->width == scale->width && crop->height == scale->height)
+ return MALI_C55_BYPASS_SCALER;
+
+ /* Program the scaler coefficients if the scaler is in use. */
+ mali_c55_resizer_program_coefficients(rsz);
+
+ /* Program the V/H scaling factor in Q4.20 format. */
+ h_scale = crop->width * MALI_C55_RSZ_SCALER_FACTOR;
+ v_scale = crop->height * MALI_C55_RSZ_SCALER_FACTOR;
+
+ do_div(h_scale, scale->width);
+ do_div(v_scale, scale->height);
+
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_IN_WIDTH,
+ crop->width);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_IN_HEIGHT,
+ crop->height);
+
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_OUT_WIDTH,
+ scale->width);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_OUT_HEIGHT,
+ scale->height);
+
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_HFILT_TINC,
+ h_scale);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_VFILT_TINC,
+ v_scale);
+
+ /* Select the scaler coefficients bank to use. */
+ h_bank = mali_c55_rsz_calculate_bank(mali_c55, crop->width,
+ scale->width);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_HFILT_COEF,
+ h_bank);
+
+ v_bank = mali_c55_rsz_calculate_bank(mali_c55, crop->height,
+ scale->height);
+ mali_c55_cap_dev_write(rsz->cap_dev, MALI_C55_REG_SCALER_VFILT_COEF,
+ v_bank);
+
+ return 0;
+}
+
+static void mali_c55_rsz_program(struct mali_c55_resizer *rsz,
+ struct v4l2_subdev_state *state)
+{
+ struct mali_c55 *mali_c55 = rsz->mali_c55;
+ u32 bypass = 0;
+
+ /* Verify if cropping and scaling should be enabled. */
+ bypass |= mali_c55_rsz_program_crop(rsz, state);
+ bypass |= mali_c55_rsz_program_resizer(rsz, state);
+
+ mali_c55_ctx_update_bits(mali_c55, rsz->id == MALI_C55_RSZ_FR ?
+ MALI_C55_REG_FR_BYPASS : MALI_C55_REG_DS_BYPASS,
+ MALI_C55_BYPASS_CROP | MALI_C55_BYPASS_SCALER,
+ bypass);
+}
+
+/*
+ * Inspect the routing table to know which of the two (mutually exclusive)
+ * routes is enabled and return the sink pad id of the active route.
+ */
+static unsigned int mali_c55_rsz_get_active_sink(struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_krouting *routing = &state->routing;
+ struct v4l2_subdev_route *route;
+
+ /* A single route is enabled at a time. */
+ for_each_active_route(routing, route)
+ return route->sink_pad;
+
+ return MALI_C55_RSZ_SINK_PAD;
+}
+
+/*
+ * When operating in bypass mode, the ISP takes input in a 20-bit format, but
+ * can only output 16-bit RAW bayer data (with the 4 least significant bits from
+ * the input being lost). Return the 16-bit version of the 20-bit input formats.
+ */
+static u32 mali_c55_rsz_shift_mbus_code(u32 mbus_code)
+{
+ const struct mali_c55_isp_format_info *fmt =
+ mali_c55_isp_get_mbus_config_by_code(mbus_code);
+
+ if (!fmt)
+ return -EINVAL;
+
+ return fmt->shifted_code;
+}
+
+static int __mali_c55_rsz_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ const struct v4l2_subdev_krouting *routing)
+{
+ struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
+ unsigned int active_sink = UINT_MAX;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_subdev_route *route;
+ unsigned int active_routes = 0;
+ struct v4l2_mbus_framefmt *fmt;
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing, 0);
+ if (ret)
+ return ret;
+
+ /* Only a single route can be enabled at a time. */
+ for_each_active_route(routing, route) {
+ if (++active_routes > 1) {
+ dev_dbg(rsz->mali_c55->dev,
+ "Only one route can be active");
+ return -EINVAL;
+ }
+
+ active_sink = route->sink_pad;
+ }
+ if (active_sink == UINT_MAX) {
+ dev_dbg(rsz->mali_c55->dev, "One route has to be active");
+ return -EINVAL;
+ }
+
+ ret = v4l2_subdev_set_routing(sd, state, routing);
+ if (ret) {
+ dev_dbg(rsz->mali_c55->dev, "Failed to set routing\n");
+ return ret;
+ }
+
+ fmt = v4l2_subdev_state_get_format(state, active_sink, 0);
+ fmt->width = MALI_C55_DEFAULT_WIDTH;
+ fmt->height = MALI_C55_DEFAULT_HEIGHT;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false,
+ fmt->colorspace,
+ fmt->ycbcr_enc);
+ fmt->field = V4L2_FIELD_NONE;
+
+ if (active_sink == MALI_C55_RSZ_SINK_PAD) {
+ struct v4l2_rect *crop, *compose;
+
+ fmt->code = MEDIA_BUS_FMT_RGB121212_1X36;
+
+ crop = v4l2_subdev_state_get_crop(state, active_sink, 0);
+ compose = v4l2_subdev_state_get_compose(state, active_sink, 0);
+
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = MALI_C55_DEFAULT_WIDTH;
+ crop->height = MALI_C55_DEFAULT_HEIGHT;
+
+ *compose = *crop;
+ } else {
+ fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
+ }
+
+ /* Propagate the format to the source pad */
+ src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD,
+ 0);
+ *src_fmt = *fmt;
+
+ /* In the event this is the bypass pad the mbus code needs correcting */
+ if (active_sink == MALI_C55_RSZ_SINK_BYPASS_PAD)
+ src_fmt->code = mali_c55_rsz_shift_mbus_code(src_fmt->code);
+
+ return 0;
+}
+
+static int mali_c55_rsz_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct mali_c55_isp_format_info *fmt;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ u32 sink_pad;
+
+ switch (code->pad) {
+ case MALI_C55_RSZ_SINK_PAD:
+ if (code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_RGB121212_1X36;
+
+ return 0;
+ case MALI_C55_RSZ_SOURCE_PAD:
+ sink_pad = mali_c55_rsz_get_active_sink(state);
+ sink_fmt = v4l2_subdev_state_get_format(state, sink_pad, 0);
+
+ /*
+ * If the active route is from the Bypass sink pad, then the
+ * source pad is a simple passthrough of the sink format,
+ * downshifted to 16-bits.
+ */
+
+ if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) {
+ if (code->index)
+ return -EINVAL;
+
+ code->code = mali_c55_rsz_shift_mbus_code(sink_fmt->code);
+ if (!code->code)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /*
+ * If the active route is from the non-bypass sink then we can
+ * select either RGB or conversion to YUV.
+ */
+
+ if (code->index >= ARRAY_SIZE(rsz_non_bypass_src_fmts))
+ return -EINVAL;
+
+ code->code = rsz_non_bypass_src_fmts[code->index];
+
+ return 0;
+ case MALI_C55_RSZ_SINK_BYPASS_PAD:
+ fmt = mali_c55_isp_get_mbus_config_by_index(code->index);
+ if (fmt) {
+ code->code = fmt->code;
+ return 0;
+ }
+
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int mali_c55_rsz_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct mali_c55_isp_format_info *fmt;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *compose;
+ u32 sink_pad;
+
+ switch (fse->pad) {
+ case MALI_C55_RSZ_SINK_PAD:
+ if (fse->index || fse->code != MEDIA_BUS_FMT_RGB121212_1X36)
+ return -EINVAL;
+
+ fse->max_width = MALI_C55_MAX_WIDTH;
+ fse->max_height = MALI_C55_MAX_HEIGHT;
+ fse->min_width = MALI_C55_MIN_WIDTH;
+ fse->min_height = MALI_C55_MIN_HEIGHT;
+
+ return 0;
+ case MALI_C55_RSZ_SOURCE_PAD:
+ sink_pad = mali_c55_rsz_get_active_sink(state);
+ sink_fmt = v4l2_subdev_state_get_format(state, sink_pad, 0);
+
+ if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) {
+ if (fse->index)
+ return -EINVAL;
+
+ fmt = mali_c55_isp_get_mbus_config_by_shifted_code(fse->code);
+ if (!fmt)
+ return -EINVAL;
+
+ fse->min_width = sink_fmt->width;
+ fse->max_width = sink_fmt->width;
+ fse->min_height = sink_fmt->height;
+ fse->max_height = sink_fmt->height;
+
+ return 0;
+ }
+
+ if ((fse->code != MEDIA_BUS_FMT_RGB121212_1X36 &&
+ fse->code != MEDIA_BUS_FMT_YUV10_1X30) || fse->index > 1)
+ return -EINVAL;
+
+ compose = v4l2_subdev_state_get_compose(state,
+ MALI_C55_RSZ_SINK_PAD,
+ 0);
+
+ fse->min_width = compose->width;
+ fse->max_width = compose->width;
+ fse->min_height = compose->height;
+ fse->max_height = compose->height;
+
+ return 0;
+ case MALI_C55_RSZ_SINK_BYPASS_PAD:
+ fmt = mali_c55_isp_get_mbus_config_by_code(fse->code);
+ if (fse->index || !fmt)
+ return -EINVAL;
+
+ fse->max_width = MALI_C55_MAX_WIDTH;
+ fse->max_height = MALI_C55_MAX_HEIGHT;
+ fse->min_width = MALI_C55_MIN_WIDTH;
+ fse->min_height = MALI_C55_MIN_HEIGHT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int mali_c55_rsz_set_sink_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ unsigned int active_sink;
+ struct v4l2_rect *rect;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, format->pad, 0);
+
+ /*
+ * Clamp to min/max and then reset crop and compose rectangles to the
+ * newly applied size.
+ */
+ sink_fmt->width = clamp_t(unsigned int, fmt->width,
+ MALI_C55_MIN_WIDTH, MALI_C55_MAX_WIDTH);
+ sink_fmt->height = clamp_t(unsigned int, fmt->height,
+ MALI_C55_MIN_HEIGHT, MALI_C55_MAX_HEIGHT);
+
+ /*
+ * Make sure the media bus code for the bypass pad is one of the
+ * supported ISP input media bus codes. Default it to SRGGB otherwise.
+ */
+ if (format->pad == MALI_C55_RSZ_SINK_BYPASS_PAD)
+ sink_fmt->code = mali_c55_isp_get_mbus_config_by_code(fmt->code) ?
+ fmt->code : MEDIA_BUS_FMT_SRGGB20_1X20;
+
+ *fmt = *sink_fmt;
+
+ if (format->pad == MALI_C55_RSZ_SINK_PAD) {
+ rect = v4l2_subdev_state_get_crop(state, format->pad);
+ rect->left = 0;
+ rect->top = 0;
+ rect->width = fmt->width;
+ rect->height = fmt->height;
+
+ rect = v4l2_subdev_state_get_compose(state, format->pad);
+ rect->left = 0;
+ rect->top = 0;
+ rect->width = fmt->width;
+ rect->height = fmt->height;
+ }
+
+ /* If format->pad is routed to the source pad, propagate the format. */
+ active_sink = mali_c55_rsz_get_active_sink(state);
+ if (active_sink == format->pad) {
+ /* If the bypass route is used, downshift the code to 16bpp. */
+ if (active_sink == MALI_C55_RSZ_SINK_BYPASS_PAD)
+ fmt->code = mali_c55_rsz_shift_mbus_code(fmt->code);
+
+ *v4l2_subdev_state_get_format(state,
+ MALI_C55_RSZ_SOURCE_PAD, 0) = *fmt;
+ }
+
+ return 0;
+}
+
+static int mali_c55_rsz_set_source_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ unsigned int active_sink;
+
+ active_sink = mali_c55_rsz_get_active_sink(state);
+ sink_fmt = v4l2_subdev_state_get_format(state, active_sink, 0);
+ src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD);
+
+ if (active_sink == MALI_C55_RSZ_SINK_PAD) {
+ /*
+ * Regular processing pipe: RGB121212 can be color-space
+ * converted to YUV101010.
+ */
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rsz_non_bypass_src_fmts); i++) {
+ if (fmt->code == rsz_non_bypass_src_fmts[i])
+ break;
+ }
+
+ src_fmt->code = i == ARRAY_SIZE(rsz_non_bypass_src_fmts) ?
+ MEDIA_BUS_FMT_RGB121212_1X36 : fmt->code;
+ } else {
+ /*
+ * Bypass pipe: the source format is the same as the bypass
+ * sink pad downshifted to 16bpp.
+ */
+ fmt->code = mali_c55_rsz_shift_mbus_code(sink_fmt->code);
+ }
+
+ *fmt = *src_fmt;
+
+ return 0;
+}
+
+static int mali_c55_rsz_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ /*
+ * On sink pads fmt is either fixed for the 'regular' processing
+ * pad or a RAW format or 20-bit wide RGB/YUV format for the FR bypass
+ * pad.
+ *
+ * On source pad sizes are the result of crop+compose on the sink
+ * pad sizes, while the format depends on the active route.
+ */
+
+ if (format->pad == MALI_C55_RSZ_SINK_PAD ||
+ format->pad == MALI_C55_RSZ_SINK_BYPASS_PAD)
+ return mali_c55_rsz_set_sink_fmt(sd, state, format);
+
+ return mali_c55_rsz_set_source_fmt(sd, state, format);
+}
+
+static int mali_c55_rsz_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ if (sel->pad != MALI_C55_RSZ_SINK_PAD)
+ return -EINVAL;
+
+ if (sel->target != V4L2_SEL_TGT_CROP &&
+ sel->target != V4L2_SEL_TGT_COMPOSE)
+ return -EINVAL;
+
+ sel->r = sel->target == V4L2_SEL_TGT_CROP
+ ? *v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD)
+ : *v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD);
+
+ return 0;
+}
+
+static int mali_c55_rsz_set_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *crop, *compose;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SINK_PAD);
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD);
+ compose = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ v4l2_subdev_is_streaming(sd)) {
+ /*
+ * At runtime the compose rectangle and output size cannot be
+ * changed so we need to clamp the crop rectangle such that the
+ * compose rectangle can fit within it.
+ */
+ crop->left = clamp_t(unsigned int, sel->r.left, 0,
+ sink_fmt->width - compose->width);
+ crop->top = clamp_t(unsigned int, sel->r.top, 0,
+ sink_fmt->height - compose->height);
+ crop->width = clamp_t(unsigned int, sel->r.width, compose->width,
+ sink_fmt->width - crop->left);
+ crop->height = clamp_t(unsigned int, sel->r.height, compose->height,
+ sink_fmt->height - crop->top);
+
+ mali_c55_rsz_program(rsz, state);
+ } else {
+ /*
+ * If we're not streaming we can utilise the ISP's full range
+ * and simply need to propagate the selected rectangle to the
+ * compose target and source pad format.
+ */
+ crop->left = clamp_t(unsigned int, sel->r.left, 0,
+ sink_fmt->width);
+ crop->top = clamp_t(unsigned int, sel->r.top, 0,
+ sink_fmt->height);
+ crop->width = clamp_t(unsigned int, sel->r.width,
+ MALI_C55_MIN_WIDTH,
+ sink_fmt->width - crop->left);
+ crop->height = clamp_t(unsigned int, sel->r.height,
+ MALI_C55_MIN_HEIGHT,
+ sink_fmt->height - crop->top);
+
+ *compose = *crop;
+
+ src_fmt = v4l2_subdev_state_get_format(state,
+ MALI_C55_RSZ_SOURCE_PAD);
+ src_fmt->width = compose->width;
+ src_fmt->height = compose->height;
+ }
+
+ sel->r = *crop;
+ return 0;
+}
+
+static int mali_c55_rsz_set_compose(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
+ struct mali_c55 *mali_c55 = rsz->mali_c55;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_rect *compose, *crop;
+
+ /*
+ * We cannot change the compose rectangle during streaming, as that
+ * would require a change in the output buffer size.
+ */
+ if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ /*
+ * In the FR pipe, the scaler is an optional component that may not be
+ * fitted.
+ */
+ if (rsz->id == MALI_C55_RSZ_FR &&
+ !(mali_c55->capabilities & MALI_C55_GPS_FRSCALER_FITTED))
+ return -EINVAL;
+
+ compose = v4l2_subdev_state_get_compose(state, MALI_C55_RSZ_SINK_PAD);
+ crop = v4l2_subdev_state_get_crop(state, MALI_C55_RSZ_SINK_PAD);
+
+ compose->left = 0;
+ compose->top = 0;
+ compose->width = clamp_t(unsigned int, sel->r.width, crop->width / 8,
+ crop->width);
+ compose->height = clamp_t(unsigned int, sel->r.height, crop->height / 8,
+ crop->height);
+
+ sel->r = *compose;
+
+ /*
+ * We need to be sure to propagate the compose rectangle size to the
+ * source pad format.
+ */
+ src_fmt = v4l2_subdev_state_get_format(state, MALI_C55_RSZ_SOURCE_PAD);
+ src_fmt->width = compose->width;
+ src_fmt->height = compose->height;
+
+ return 0;
+}
+
+static int mali_c55_rsz_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ if (sel->pad != MALI_C55_RSZ_SINK_PAD)
+ return -EINVAL;
+
+ if (sel->target == V4L2_SEL_TGT_CROP)
+ return mali_c55_rsz_set_crop(sd, state, sel);
+
+ if (sel->target == V4L2_SEL_TGT_COMPOSE)
+ return mali_c55_rsz_set_compose(sd, state, sel);
+
+ return -EINVAL;
+}
+
+static int mali_c55_rsz_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ media_entity_is_streaming(&sd->entity))
+ return -EBUSY;
+
+ return __mali_c55_rsz_set_routing(sd, state, routing);
+}
+
+static int mali_c55_rsz_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
+ struct mali_c55 *mali_c55 = rsz->mali_c55;
+ unsigned int sink_pad;
+
+ sink_pad = mali_c55_rsz_get_active_sink(state);
+ if (sink_pad == MALI_C55_RSZ_SINK_BYPASS_PAD) {
+ /* Bypass FR pipe processing if the bypass route is active. */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
+ MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK,
+ MALI_C55_ISP_RAW_BYPASS_RAW_FR_BYPASS);
+ return 0;
+ }
+
+ /* Disable bypass and use regular processing. */
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_ISP_RAW_BYPASS,
+ MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK, 0);
+ mali_c55_rsz_program(rsz, state);
+
+ return 0;
+}
+
+static int mali_c55_rsz_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops mali_c55_resizer_pad_ops = {
+ .enum_mbus_code = mali_c55_rsz_enum_mbus_code,
+ .enum_frame_size = mali_c55_rsz_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = mali_c55_rsz_set_fmt,
+ .get_selection = mali_c55_rsz_get_selection,
+ .set_selection = mali_c55_rsz_set_selection,
+ .set_routing = mali_c55_rsz_set_routing,
+ .enable_streams = mali_c55_rsz_enable_streams,
+ .disable_streams = mali_c55_rsz_disable_streams,
+};
+
+static const struct v4l2_subdev_ops mali_c55_resizer_ops = {
+ .pad = &mali_c55_resizer_pad_ops,
+};
+
+static int mali_c55_rsz_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct mali_c55_resizer *rsz = sd_to_mali_c55_rsz(sd);
+ struct v4l2_subdev_route routes[2] = {
+ {
+ .sink_pad = MALI_C55_RSZ_SINK_PAD,
+ .source_pad = MALI_C55_RSZ_SOURCE_PAD,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ }, {
+ .sink_pad = MALI_C55_RSZ_SINK_BYPASS_PAD,
+ .source_pad = MALI_C55_RSZ_SOURCE_PAD,
+ },
+ };
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = rsz->num_routes,
+ .routes = routes,
+ };
+
+ return __mali_c55_rsz_set_routing(sd, state, &routing);
+}
+
+static const struct v4l2_subdev_internal_ops mali_c55_resizer_internal_ops = {
+ .init_state = mali_c55_rsz_init_state,
+};
+
+static int mali_c55_register_resizer(struct mali_c55 *mali_c55,
+ unsigned int index)
+{
+ struct mali_c55_resizer *rsz = &mali_c55->resizers[index];
+ struct v4l2_subdev *sd = &rsz->sd;
+ unsigned int num_pads;
+ int ret;
+
+ rsz->id = index;
+ v4l2_subdev_init(sd, &mali_c55_resizer_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+ sd->internal_ops = &mali_c55_resizer_internal_ops;
+
+ rsz->pads[MALI_C55_RSZ_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
+ rsz->pads[MALI_C55_RSZ_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
+
+ if (rsz->id == MALI_C55_RSZ_FR) {
+ num_pads = MALI_C55_RSZ_NUM_PADS;
+ rsz->num_routes = 2;
+
+ rsz->pads[MALI_C55_RSZ_SINK_BYPASS_PAD].flags =
+ MEDIA_PAD_FL_SINK;
+
+ snprintf(sd->name, sizeof(sd->name), "%s resizer fr",
+ MALI_C55_DRIVER_NAME);
+
+ } else {
+ num_pads = MALI_C55_RSZ_NUM_PADS - 1;
+ rsz->num_routes = 1;
+
+ snprintf(sd->name, sizeof(sd->name), "%s resizer ds",
+ MALI_C55_DRIVER_NAME);
+ }
+
+ ret = media_entity_pads_init(&sd->entity, num_pads, rsz->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_media_cleanup;
+
+ ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd);
+ if (ret)
+ goto err_subdev_cleanup;
+
+ rsz->cap_dev = &mali_c55->cap_devs[index];
+ rsz->mali_c55 = mali_c55;
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_media_cleanup:
+ media_entity_cleanup(&sd->entity);
+
+ return ret;
+}
+
+static void mali_c55_unregister_resizer(struct mali_c55_resizer *rsz)
+{
+ if (!rsz->mali_c55)
+ return;
+
+ v4l2_device_unregister_subdev(&rsz->sd);
+ v4l2_subdev_cleanup(&rsz->sd);
+ media_entity_cleanup(&rsz->sd.entity);
+}
+
+int mali_c55_register_resizers(struct mali_c55 *mali_c55)
+{
+ int ret;
+
+ ret = mali_c55_register_resizer(mali_c55, MALI_C55_RSZ_FR);
+ if (ret)
+ return ret;
+
+ if (mali_c55->capabilities & MALI_C55_GPS_DS_PIPE_FITTED) {
+ ret = mali_c55_register_resizer(mali_c55, MALI_C55_RSZ_DS);
+ if (ret)
+ goto err_unregister_fr;
+ }
+
+ return 0;
+
+err_unregister_fr:
+ mali_c55_unregister_resizer(&mali_c55->resizers[MALI_C55_RSZ_FR]);
+
+ return ret;
+}
+
+void mali_c55_unregister_resizers(struct mali_c55 *mali_c55)
+{
+ for (unsigned int i = 0; i < MALI_C55_NUM_RSZS; i++)
+ mali_c55_unregister_resizer(&mali_c55->resizers[i]);
+}
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-stats.c b/drivers/media/platform/arm/mali-c55/mali-c55-stats.c
new file mode 100644
index 000000000000..655e52a5f288
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-stats.c
@@ -0,0 +1,323 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Mali-C55 ISP Driver - 3A Statistics capture device
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include <linux/container_of.h>
+#include <linux/dev_printk.h>
+#include <linux/list.h>
+#include <linux/media/arm/mali-c55-config.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "mali-c55-common.h"
+#include "mali-c55-registers.h"
+
+static const unsigned int metering_space_addrs[] = {
+ [MALI_C55_CONFIG_PING] = 0x095ac,
+ [MALI_C55_CONFIG_PONG] = 0x2156c,
+};
+
+static int mali_c55_stats_enum_fmt_meta_cap(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_MALI_C55_STATS;
+
+ return 0;
+}
+
+static int mali_c55_stats_g_fmt_meta_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ static const struct v4l2_meta_format mfmt = {
+ .dataformat = V4L2_META_FMT_MALI_C55_STATS,
+ .buffersize = sizeof(struct mali_c55_stats_buffer)
+ };
+
+ f->fmt.meta = mfmt;
+
+ return 0;
+}
+
+static int mali_c55_stats_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, MALI_C55_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "ARM Mali-C55 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops mali_c55_stats_v4l2_ioctl_ops = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_cap = mali_c55_stats_enum_fmt_meta_cap,
+ .vidioc_g_fmt_meta_cap = mali_c55_stats_g_fmt_meta_cap,
+ .vidioc_s_fmt_meta_cap = mali_c55_stats_g_fmt_meta_cap,
+ .vidioc_try_fmt_meta_cap = mali_c55_stats_g_fmt_meta_cap,
+ .vidioc_querycap = mali_c55_stats_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations mali_c55_stats_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static int
+mali_c55_stats_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes && *num_planes > 1)
+ return -EINVAL;
+
+ if (sizes[0] && sizes[0] < sizeof(struct mali_c55_stats_buffer))
+ return -EINVAL;
+
+ *num_planes = 1;
+
+ if (!sizes[0])
+ sizes[0] = sizeof(struct mali_c55_stats_buffer);
+
+ return 0;
+}
+
+static void mali_c55_stats_buf_queue(struct vb2_buffer *vb)
+{
+ struct mali_c55_stats *stats = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mali_c55_stats_buf *buf = container_of(vbuf,
+ struct mali_c55_stats_buf, vb);
+
+ vb2_set_plane_payload(vb, 0, sizeof(struct mali_c55_stats_buffer));
+ buf->segments_remaining = 2;
+ buf->failed = false;
+
+ spin_lock(&stats->buffers.lock);
+ list_add_tail(&buf->queue, &stats->buffers.queue);
+ spin_unlock(&stats->buffers.lock);
+}
+
+static void mali_c55_stats_return_buffers(struct mali_c55_stats *stats,
+ enum vb2_buffer_state state)
+{
+ struct mali_c55_stats_buf *buf, *tmp;
+
+ guard(spinlock)(&stats->buffers.lock);
+
+ list_for_each_entry_safe(buf, tmp, &stats->buffers.queue, queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+}
+
+static int mali_c55_stats_start_streaming(struct vb2_queue *q,
+ unsigned int count)
+{
+ struct mali_c55_stats *stats = vb2_get_drv_priv(q);
+ struct mali_c55 *mali_c55 = stats->mali_c55;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(mali_c55->dev);
+ if (ret)
+ goto err_return_buffers;
+
+ ret = video_device_pipeline_alloc_start(&stats->vdev);
+ if (ret)
+ goto err_pm_put;
+
+ if (mali_c55_pipeline_ready(mali_c55)) {
+ ret = v4l2_subdev_enable_streams(&mali_c55->isp.sd,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ BIT(0));
+ if (ret < 0)
+ goto err_stop_pipeline;
+ }
+
+ return 0;
+
+err_stop_pipeline:
+ video_device_pipeline_stop(&stats->vdev);
+err_pm_put:
+ pm_runtime_put_autosuspend(mali_c55->dev);
+err_return_buffers:
+ mali_c55_stats_return_buffers(stats, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void mali_c55_stats_stop_streaming(struct vb2_queue *q)
+{
+ struct mali_c55_stats *stats = vb2_get_drv_priv(q);
+ struct mali_c55 *mali_c55 = stats->mali_c55;
+ struct mali_c55_isp *isp = &mali_c55->isp;
+
+ if (mali_c55_pipeline_ready(mali_c55)) {
+ if (v4l2_subdev_is_streaming(&isp->sd))
+ v4l2_subdev_disable_streams(&isp->sd,
+ MALI_C55_ISP_PAD_SOURCE_VIDEO,
+ BIT(0));
+ }
+
+ video_device_pipeline_stop(&stats->vdev);
+ mali_c55_stats_return_buffers(stats, VB2_BUF_STATE_ERROR);
+
+ pm_runtime_put_autosuspend(stats->mali_c55->dev);
+}
+
+static const struct vb2_ops mali_c55_stats_vb2_ops = {
+ .queue_setup = mali_c55_stats_queue_setup,
+ .buf_queue = mali_c55_stats_buf_queue,
+ .start_streaming = mali_c55_stats_start_streaming,
+ .stop_streaming = mali_c55_stats_stop_streaming,
+};
+
+static void mali_c55_stats_cpu_read(struct mali_c55_stats *stats,
+ struct mali_c55_stats_buf *buf,
+ enum mali_c55_config_spaces cfg_space)
+{
+ struct mali_c55 *mali_c55 = stats->mali_c55;
+ const void __iomem *src;
+ size_t length;
+ void *dst;
+
+ src = mali_c55->base + MALI_C55_REG_1024BIN_HIST;
+ dst = vb2_plane_vaddr(&buf->vb.vb2_buf, 0);
+ memcpy_fromio(dst, src, MALI_C55_1024BIN_HIST_SIZE);
+
+ src = mali_c55->base + metering_space_addrs[cfg_space];
+ dst += MALI_C55_1024BIN_HIST_SIZE;
+ length = sizeof(struct mali_c55_stats_buffer) - MALI_C55_1024BIN_HIST_SIZE;
+ memcpy_fromio(dst, src, length);
+}
+
+void mali_c55_stats_fill_buffer(struct mali_c55 *mali_c55,
+ enum mali_c55_config_spaces cfg_space)
+{
+ struct mali_c55_stats *stats = &mali_c55->stats;
+ struct mali_c55_stats_buf *buf = NULL;
+
+ spin_lock(&stats->buffers.lock);
+ if (!list_empty(&stats->buffers.queue)) {
+ buf = list_first_entry(&stats->buffers.queue,
+ struct mali_c55_stats_buf, queue);
+ list_del(&buf->queue);
+ }
+ spin_unlock(&stats->buffers.lock);
+
+ if (!buf)
+ return;
+
+ buf->vb.sequence = mali_c55->isp.frame_sequence;
+ buf->vb.vb2_buf.timestamp = ktime_get_boottime_ns();
+
+ mali_c55_stats_cpu_read(stats, buf, cfg_space);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+void mali_c55_unregister_stats(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_stats *stats = &mali_c55->stats;
+
+ if (!video_is_registered(&stats->vdev))
+ return;
+
+ vb2_video_unregister_device(&stats->vdev);
+ media_entity_cleanup(&stats->vdev.entity);
+
+ mutex_destroy(&stats->lock);
+}
+
+int mali_c55_register_stats(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_stats *stats = &mali_c55->stats;
+ struct video_device *vdev = &stats->vdev;
+ struct vb2_queue *vb2q = &stats->queue;
+ int ret;
+
+ mutex_init(&stats->lock);
+ INIT_LIST_HEAD(&stats->buffers.queue);
+ spin_lock_init(&stats->buffers.lock);
+
+ stats->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&stats->vdev.entity, 1, &stats->pad);
+ if (ret)
+ goto err_destroy_mutex;
+
+ vb2q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ vb2q->io_modes = VB2_MMAP | VB2_DMABUF;
+ vb2q->drv_priv = stats;
+ vb2q->mem_ops = &vb2_dma_contig_memops;
+ vb2q->ops = &mali_c55_stats_vb2_ops;
+ vb2q->buf_struct_size = sizeof(struct mali_c55_stats_buf);
+ vb2q->min_queued_buffers = 1;
+ vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2q->lock = &stats->lock;
+ vb2q->dev = mali_c55->dev;
+
+ ret = vb2_queue_init(vb2q);
+ if (ret) {
+ dev_err(mali_c55->dev, "stats vb2 queue init failed\n");
+ goto err_cleanup_entity;
+ }
+
+ strscpy(stats->vdev.name, "mali-c55 3a stats", sizeof(stats->vdev.name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &mali_c55_stats_v4l2_fops;
+ vdev->ioctl_ops = &mali_c55_stats_v4l2_ioctl_ops;
+ vdev->lock = &stats->lock;
+ vdev->v4l2_dev = &mali_c55->v4l2_dev;
+ vdev->queue = &stats->queue;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ video_set_drvdata(vdev, stats);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "failed to register stats video device\n");
+ goto err_release_vb2q;
+ }
+
+ stats->mali_c55 = mali_c55;
+
+ return 0;
+
+err_release_vb2q:
+ vb2_queue_release(vb2q);
+err_cleanup_entity:
+ media_entity_cleanup(&stats->vdev.entity);
+err_destroy_mutex:
+
+ mutex_destroy(&stats->lock);
+
+ return ret;
+}
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c
new file mode 100644
index 000000000000..1af5d2759a83
--- /dev/null
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ARM Mali-C55 ISP Driver - Test pattern generator
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include <linux/minmax.h>
+#include <linux/pm_runtime.h>
+#include <linux/string.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
+
+#include "mali-c55-common.h"
+#include "mali-c55-registers.h"
+
+#define MALI_C55_TPG_SRC_PAD 0
+#define MALI_C55_TPG_FIXED_HBLANK 0x20
+#define MALI_C55_TPG_DEFAULT_MIN_VBLANK 66
+#define MALI_C55_TPG_DEFAULT_DEF_VBLANK 626
+#define MALI_C55_TPG_MAX_VBLANK 0xffff
+#define MALI_C55_TPG_PIXEL_RATE 100000000
+
+static const char * const mali_c55_tpg_test_pattern_menu[] = {
+ "Flat field",
+ "Horizontal gradient",
+ "Vertical gradient",
+ "Vertical bars",
+ "Arbitrary rectangle",
+ "White frame on black field"
+};
+
+static const u32 mali_c55_tpg_mbus_codes[] = {
+ MEDIA_BUS_FMT_SRGGB20_1X20,
+ MEDIA_BUS_FMT_RGB202020_1X60,
+};
+
+static void mali_c55_tpg_update_vblank(struct mali_c55_tpg *tpg,
+ struct v4l2_mbus_framefmt *format)
+{
+ unsigned int def_vblank;
+ unsigned int min_vblank;
+ unsigned int hts;
+ int tgt_fps;
+
+ hts = format->width + MALI_C55_TPG_FIXED_HBLANK;
+
+ /*
+ * The ISP has minimum vertical blanking requirements that must be
+ * adhered to by the TPG. The minimum is a function of the Iridix blocks
+ * clocking requirements and the width of the image and horizontal
+ * blanking, but if we assume the worst case iVariance and sVariance
+ * values then it boils down to the below (plus one to the numerator to
+ * ensure the answer is rounded up).
+ */
+ min_vblank = 15 + (120501 / hts);
+
+ /*
+ * We need to set a sensible default vblank for whatever format height
+ * we happen to be given from set_fmt(). This function just targets
+ * an even multiple of 15fps. If we can't get 15fps, let's target 5fps.
+ * If we can't get 5fps we'll take whatever the minimum vblank gives us.
+ */
+ tgt_fps = MALI_C55_TPG_PIXEL_RATE / hts / (format->height + min_vblank);
+
+ if (tgt_fps < 5)
+ def_vblank = min_vblank;
+ else
+ def_vblank = (MALI_C55_TPG_PIXEL_RATE / hts
+ / max(rounddown(tgt_fps, 15), 5)) - format->height;
+
+ def_vblank = ALIGN_DOWN(def_vblank, 2);
+
+ __v4l2_ctrl_modify_range(tpg->ctrls.vblank, min_vblank,
+ MALI_C55_TPG_MAX_VBLANK, 1, def_vblank);
+ __v4l2_ctrl_s_ctrl(tpg->ctrls.vblank, def_vblank);
+}
+
+static int mali_c55_tpg_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mali_c55_tpg *tpg = container_of(ctrl->handler,
+ struct mali_c55_tpg,
+ ctrls.handler);
+ struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg);
+ int ret = 0;
+
+ if (!pm_runtime_get_if_in_use(mali_c55->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_TEST_PATTERN:
+ mali_c55_ctx_write(mali_c55,
+ MALI_C55_REG_TEST_GEN_CH0_PATTERN_TYPE,
+ ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ mali_c55_update_bits(mali_c55, MALI_C55_REG_BLANKING,
+ MALI_C55_REG_VBLANK_MASK,
+ MALI_C55_VBLANK(ctrl->val));
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ pm_runtime_put_autosuspend(mali_c55->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops mali_c55_tpg_ctrl_ops = {
+ .s_ctrl = &mali_c55_tpg_s_ctrl,
+};
+
+static void mali_c55_tpg_configure(struct mali_c55_tpg *tpg,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ u32 test_pattern_format;
+
+ /*
+ * hblank needs setting, but is a read-only control and thus won't be
+ * called during __v4l2_ctrl_handler_setup(). Do it here instead.
+ */
+ mali_c55_update_bits(tpg->mali_c55, MALI_C55_REG_BLANKING,
+ MALI_C55_REG_HBLANK_MASK,
+ MALI_C55_TPG_FIXED_HBLANK);
+ mali_c55_update_bits(tpg->mali_c55, MALI_C55_REG_GEN_VIDEO,
+ MALI_C55_REG_GEN_VIDEO_MULTI_MASK,
+ MALI_C55_REG_GEN_VIDEO_MULTI_MASK);
+
+ fmt = v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD);
+
+ test_pattern_format = fmt->code == MEDIA_BUS_FMT_RGB202020_1X60 ?
+ 0x01 : 0x0;
+
+ mali_c55_ctx_update_bits(tpg->mali_c55, MALI_C55_REG_TPG_CH0,
+ MALI_C55_TEST_PATTERN_RGB_MASK,
+ MALI_C55_TEST_PATTERN_RGB(test_pattern_format));
+}
+
+static int mali_c55_tpg_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(mali_c55_tpg_mbus_codes))
+ return -EINVAL;
+
+ code->code = mali_c55_tpg_mbus_codes[code->index];
+
+ return 0;
+}
+
+static int mali_c55_tpg_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ unsigned int i;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_tpg_mbus_codes); i++) {
+ if (fse->code == mali_c55_tpg_mbus_codes[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mali_c55_tpg_mbus_codes))
+ return -EINVAL;
+
+ fse->min_width = MALI_C55_MIN_WIDTH;
+ fse->max_width = MALI_C55_MAX_WIDTH;
+ fse->min_height = MALI_C55_MIN_HEIGHT;
+ fse->max_height = MALI_C55_MAX_HEIGHT;
+
+ return 0;
+}
+
+static int mali_c55_tpg_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd);
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int i;
+
+ fmt = v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD);
+ fmt->code = format->format.code;
+
+ for (i = 0; i < ARRAY_SIZE(mali_c55_tpg_mbus_codes); i++) {
+ if (fmt->code == mali_c55_tpg_mbus_codes[i])
+ break;
+ }
+
+ if (i == ARRAY_SIZE(mali_c55_tpg_mbus_codes))
+ fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
+
+ /*
+ * The TPG says that the test frame timing generation logic expects a
+ * minimum framesize of 4x4 pixels, but given the rest of the ISP can't
+ * handle anything smaller than 128x128 it seems pointless to allow a
+ * smaller frame.
+ */
+ fmt->width = clamp(format->format.width, MALI_C55_MIN_WIDTH,
+ MALI_C55_MAX_WIDTH);
+ fmt->height = clamp(format->format.height, MALI_C55_MIN_HEIGHT,
+ MALI_C55_MAX_HEIGHT);
+
+ format->format = *fmt;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY)
+ return 0;
+
+ mali_c55_tpg_update_vblank(tpg, fmt);
+
+ return 0;
+}
+
+static int mali_c55_tpg_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd);
+ struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg);
+
+ /*
+ * We only have a source pad and a single stream, and v4l2-core already
+ * validated both so we don't need to do that. One might reasonably
+ * expect the framesize to be set here given it's configurable in
+ * .set_fmt(), but it's done in the ISP subdevice's .enable_streams()
+ * instead, as the same register is also used to indicate the size of
+ * the data coming from the sensor.
+ */
+ mali_c55_tpg_configure(tpg, state);
+ __v4l2_ctrl_handler_setup(sd->ctrl_handler);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_TPG_CH0,
+ MALI_C55_TEST_PATTERN_ON_OFF,
+ MALI_C55_TEST_PATTERN_ON_OFF);
+ mali_c55_update_bits(mali_c55, MALI_C55_REG_GEN_VIDEO,
+ MALI_C55_REG_GEN_VIDEO_ON_MASK,
+ MALI_C55_REG_GEN_VIDEO_ON_MASK);
+
+ return 0;
+}
+
+static int mali_c55_tpg_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd);
+ struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg);
+
+ mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_TPG_CH0,
+ MALI_C55_TEST_PATTERN_ON_OFF, 0x00);
+ mali_c55_update_bits(mali_c55, MALI_C55_REG_GEN_VIDEO,
+ MALI_C55_REG_GEN_VIDEO_ON_MASK, 0x00);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops mali_c55_tpg_pad_ops = {
+ .enum_mbus_code = mali_c55_tpg_enum_mbus_code,
+ .enum_frame_size = mali_c55_tpg_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = mali_c55_tpg_set_fmt,
+ .enable_streams = mali_c55_tpg_enable_streams,
+ .disable_streams = mali_c55_tpg_disable_streams,
+};
+
+static const struct v4l2_subdev_core_ops mali_c55_isp_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops mali_c55_tpg_ops = {
+ .core = &mali_c55_isp_core_ops,
+ .pad = &mali_c55_tpg_pad_ops,
+};
+
+static int mali_c55_tpg_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt =
+ v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD);
+
+ fmt->width = MALI_C55_DEFAULT_WIDTH;
+ fmt->height = MALI_C55_DEFAULT_HEIGHT;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false,
+ fmt->colorspace,
+ fmt->ycbcr_enc);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops mali_c55_tpg_internal_ops = {
+ .init_state = mali_c55_tpg_init_state,
+};
+
+static int mali_c55_tpg_init_controls(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_tpg_ctrls *ctrls = &mali_c55->tpg.ctrls;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *hblank;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&ctrls->handler, 4);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_new_std_menu_items(&ctrls->handler, &mali_c55_tpg_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(mali_c55_tpg_test_pattern_menu) - 1,
+ 0, 3, mali_c55_tpg_test_pattern_menu);
+
+ /*
+ * We fix hblank at the minimum allowed value and control framerate
+ * solely through the vblank control.
+ */
+ hblank = v4l2_ctrl_new_std(&ctrls->handler, &mali_c55_tpg_ctrl_ops,
+ V4L2_CID_HBLANK, MALI_C55_TPG_FIXED_HBLANK,
+ MALI_C55_TPG_FIXED_HBLANK, 1,
+ MALI_C55_TPG_FIXED_HBLANK);
+ if (hblank)
+ hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ ctrls->vblank = v4l2_ctrl_new_std(&ctrls->handler,
+ &mali_c55_tpg_ctrl_ops,
+ V4L2_CID_VBLANK,
+ MALI_C55_TPG_DEFAULT_MIN_VBLANK,
+ MALI_C55_TPG_MAX_VBLANK, 1,
+ MALI_C55_TPG_DEFAULT_DEF_VBLANK);
+
+ pixel_rate = v4l2_ctrl_new_std(&ctrls->handler, &mali_c55_tpg_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ MALI_C55_TPG_PIXEL_RATE,
+ MALI_C55_TPG_PIXEL_RATE, 1,
+ MALI_C55_TPG_PIXEL_RATE);
+ if (pixel_rate)
+ pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ if (ctrls->handler.error) {
+ dev_err(mali_c55->dev, "Error during v4l2 controls init\n");
+ v4l2_ctrl_handler_free(&ctrls->handler);
+ return ctrls->handler.error;
+ }
+
+ mali_c55->tpg.sd.ctrl_handler = &ctrls->handler;
+ mali_c55->tpg.sd.state_lock = ctrls->handler.lock;
+
+ return 0;
+}
+
+int mali_c55_register_tpg(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_tpg *tpg = &mali_c55->tpg;
+ struct v4l2_subdev *sd = &tpg->sd;
+ struct media_pad *pad = &tpg->pad;
+ int ret;
+
+ v4l2_subdev_init(sd, &mali_c55_tpg_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ sd->internal_ops = &mali_c55_tpg_internal_ops;
+ strscpy(sd->name, MALI_C55_DRIVER_NAME " tpg", sizeof(sd->name));
+
+ pad->flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, 1, pad);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "Failed to initialize media entity pads\n");
+ return ret;
+ }
+
+ ret = mali_c55_tpg_init_controls(mali_c55);
+ if (ret) {
+ dev_err(mali_c55->dev,
+ "Error initialising controls\n");
+ goto err_cleanup_media_entity;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_free_ctrl_handler;
+
+ ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd);
+ if (ret) {
+ dev_err(mali_c55->dev, "Failed to register tpg subdev\n");
+ goto err_cleanup_subdev;
+ }
+
+ /*
+ * By default the colour settings lead to a very dim image that is
+ * nearly indistinguishable from black on some monitor settings. Ramp
+ * them up a bit so the image is brighter.
+ */
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_R_BACKGROUND,
+ MALI_C55_TPG_BACKGROUND_MAX);
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_G_BACKGROUND,
+ MALI_C55_TPG_BACKGROUND_MAX);
+ mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_B_BACKGROUND,
+ MALI_C55_TPG_BACKGROUND_MAX);
+
+ tpg->mali_c55 = mali_c55;
+
+ return 0;
+
+err_cleanup_subdev:
+ v4l2_subdev_cleanup(sd);
+err_free_ctrl_handler:
+ v4l2_ctrl_handler_free(&tpg->ctrls.handler);
+err_cleanup_media_entity:
+ media_entity_cleanup(&sd->entity);
+
+ return ret;
+}
+
+void mali_c55_unregister_tpg(struct mali_c55 *mali_c55)
+{
+ struct mali_c55_tpg *tpg = &mali_c55->tpg;
+
+ if (!tpg->mali_c55)
+ return;
+
+ v4l2_device_unregister_subdev(&tpg->sd);
+ v4l2_ctrl_handler_free(&tpg->ctrls.handler);
+ v4l2_subdev_cleanup(&tpg->sd);
+ media_entity_cleanup(&tpg->sd.entity);
+}
diff --git a/drivers/media/platform/chips-media/coda/coda-bit.c b/drivers/media/platform/chips-media/coda/coda-bit.c
index 84ded154adfe..fa6b72c3bd93 100644
--- a/drivers/media/platform/chips-media/coda/coda-bit.c
+++ b/drivers/media/platform/chips-media/coda/coda-bit.c
@@ -1685,7 +1685,7 @@ static void coda_finish_encode(struct coda_ctx *ctx)
dst_buf->flags |= V4L2_BUF_FLAG_PFRAME;
dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/chips-media/coda/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c
index 9a57b042d9fd..33f712ff8556 100644
--- a/drivers/media/platform/chips-media/coda/coda-common.c
+++ b/drivers/media/platform/chips-media/coda/coda-common.c
@@ -790,8 +790,6 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f,
struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = get_q_data(ctx, f->type);
if (!q_data)
@@ -942,8 +940,6 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv,
ctx->codec = codec;
dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- if (!dst_vq)
- return -EINVAL;
/*
* Setting the capture queue format is not possible while the capture
diff --git a/drivers/media/platform/chips-media/coda/coda-jpeg.c b/drivers/media/platform/chips-media/coda/coda-jpeg.c
index 5746892658b1..fb150b87c773 100644
--- a/drivers/media/platform/chips-media/coda/coda-jpeg.c
+++ b/drivers/media/platform/chips-media/coda/coda-jpeg.c
@@ -1245,7 +1245,7 @@ static void coda9_jpeg_finish_encode(struct coda_ctx *ctx)
dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
coda_m2m_buf_done(ctx, dst_buf, err_mb ? VB2_BUF_STATE_ERROR :
@@ -1472,7 +1472,7 @@ static void coda9_jpeg_finish_decode(struct coda_ctx *ctx)
dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME;
dst_buf->flags |= src_buf->flags & V4L2_BUF_FLAG_LAST;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, q_data_dst->sizeimage);
diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c
index c4e0097cb8b7..1c6e076033ec 100644
--- a/drivers/media/platform/imagination/e5010-jpeg-enc.c
+++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c
@@ -396,8 +396,6 @@ static int e5010_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct e5010_fmt *fmt;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
if (vb2_is_busy(vq)) {
v4l2_err(&ctx->e5010->v4l2_dev, "queue busy\n");
@@ -496,8 +494,6 @@ static int e5010_s_selection(struct file *file, void *fh, struct v4l2_selection
struct v4l2_rect base_rect;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, s->type);
- if (!vq)
- return -EINVAL;
if (vb2_is_streaming(vq))
return -EBUSY;
@@ -1358,7 +1354,7 @@ static void e5010_device_run(void *priv)
s_vb->sequence = ctx->out_queue.sequence++;
d_vb->sequence = ctx->cap_queue.sequence++;
- v4l2_m2m_buf_copy_metadata(s_vb, d_vb, false);
+ v4l2_m2m_buf_copy_metadata(s_vb, d_vb);
if (ctx != e5010->last_context_run || ctx->update_qp) {
dprintk(e5010, 1, "ctx updated: 0x%p -> 0x%p, updating qp tables\n",
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index e07e57d4206b..af098874ead5 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -485,13 +485,8 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
static int vidioc_g_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct deinterlace_q_data *q_data;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = get_q_data(f->type);
f->fmt.pix.width = q_data->width;
@@ -586,8 +581,6 @@ static int vidioc_s_fmt(struct deinterlace_ctx *ctx, struct v4l2_format *f)
struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = get_q_data(f->type);
if (!q_data)
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index 6268d651bdcf..d08fe365cbb2 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -302,17 +302,12 @@ static int mtk_jpeg_try_fmt_mplane(struct v4l2_pix_format_mplane *pix_mp,
static int mtk_jpeg_g_fmt_vid_mplane(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct mtk_jpeg_q_data *q_data = NULL;
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file);
struct mtk_jpeg_dev *jpeg = ctx->jpeg;
int i;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = mtk_jpeg_get_q_data(ctx, f->type);
pix_mp->width = q_data->pix_mp.width;
@@ -416,8 +411,6 @@ static int mtk_jpeg_s_fmt_mplane(struct mtk_jpeg_ctx *ctx,
int i;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = mtk_jpeg_get_q_data(ctx, f->type);
@@ -1625,7 +1618,7 @@ retry_select:
if (!dst_buf)
goto getbuf_fail;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
mtk_jpegenc_set_hw_param(ctx, hw_id, src_buf, dst_buf);
ret = pm_runtime_get_sync(comp_jpeg[hw_id]->dev);
@@ -1721,7 +1714,7 @@ retry_select:
if (!dst_buf)
goto getbuf_fail;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
jpeg_src_buf = mtk_jpeg_vb2_to_srcbuf(&src_buf->vb2_buf);
jpeg_dst_buf = mtk_jpeg_vb2_to_srcbuf(&dst_buf->vb2_buf);
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
index e78e1d11093c..32372781daf5 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
@@ -530,7 +530,7 @@ static void mtk_jpegdec_timeout_work(struct work_struct *work)
src_buf = cjpeg->hw_param.src_buffer;
dst_buf = cjpeg->hw_param.dst_buffer;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
mtk_jpeg_dec_reset(cjpeg->reg_base);
clk_disable_unprepare(cjpeg->jdec_clk.clks->clk);
@@ -560,7 +560,7 @@ static irqreturn_t mtk_jpegdec_hw_irq_handler(int irq, void *priv)
ctx = jpeg->hw_param.curr_ctx;
src_buf = jpeg->hw_param.src_buffer;
dst_buf = jpeg->hw_param.dst_buffer;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
irq_status = mtk_jpeg_dec_get_int_status(jpeg->reg_base);
dec_irq_ret = mtk_jpeg_dec_enum_result(irq_status);
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
index 9ab27aee302a..b6f5b2249f1f 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
@@ -261,7 +261,7 @@ static void mtk_jpegenc_timeout_work(struct work_struct *work)
src_buf = cjpeg->hw_param.src_buffer;
dst_buf = cjpeg->hw_param.dst_buffer;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
mtk_jpeg_enc_reset(cjpeg->reg_base);
clk_disable_unprepare(cjpeg->venc_clk.clks->clk);
@@ -289,7 +289,7 @@ static irqreturn_t mtk_jpegenc_hw_irq_handler(int irq, void *priv)
ctx = jpeg->hw_param.curr_ctx;
src_buf = jpeg->hw_param.src_buffer;
dst_buf = jpeg->hw_param.dst_buffer;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
irq_status = readl(jpeg->reg_base + JPEG_ENC_INT_STS) &
JPEG_ENC_INT_STATUS_MASK_ALLIRQ;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
index 6559d72d5d42..6d26d4aa1eef 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
@@ -157,10 +157,18 @@ void mdp_video_device_release(struct video_device *vdev)
kfree(mdp);
}
+static void mdp_put_device(void *_dev)
+{
+ struct device *dev = _dev;
+
+ put_device(dev);
+}
+
static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id)
{
struct platform_device *mm_pdev = NULL;
struct device **dev;
+ int ret;
int i;
if (!mdp)
@@ -194,6 +202,11 @@ static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id)
if (WARN_ON(!mm_pdev))
return -ENODEV;
+ ret = devm_add_action_or_reset(&mdp->pdev->dev, mdp_put_device,
+ &mm_pdev->dev);
+ if (ret)
+ return ret;
+
*dev = &mm_pdev->dev;
}
@@ -279,6 +292,7 @@ static int mdp_probe(struct platform_device *pdev)
goto err_destroy_clock_wq;
}
mdp->scp = platform_get_drvdata(mm_pdev);
+ put_device(&mm_pdev->dev);
}
mdp->rproc_handle = scp_get_rproc(mdp->scp);
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
index 9ef956b565a7..44140987cf5f 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
@@ -51,7 +51,7 @@ static void mdp_m2m_process_done(void *priv, int vb_state)
ctx->curr_param.frame_no = ctx->frame_count[MDP_M2M_SRC];
src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++;
dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++;
- v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, true);
+ v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf);
v4l2_m2m_buf_done(src_vbuf, vb_state);
v4l2_m2m_buf_done(dst_vbuf, vb_state);
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
index 5ad3797836db..643d6fff088b 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_dbgfs.c
@@ -183,8 +183,8 @@ static void mtk_vcodec_dbgfs_vdec_init(struct mtk_vcodec_dec_dev *vcodec_dev)
vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-dec", NULL);
if (IS_ERR(vcodec_dev->dbgfs.vcodec_root))
- dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%ld\n",
- PTR_ERR(vcodec_dev->dbgfs.vcodec_root));
+ dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%pe\n",
+ vcodec_dev->dbgfs.vcodec_root);
vcodec_root = vcodec_dev->dbgfs.vcodec_root;
debugfs_create_x32("mtk_v4l2_dbg_level", 0644, vcodec_root, &mtk_v4l2_dbg_level);
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c
index d7027d600208..3632037f78f5 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_vpu.c
@@ -47,30 +47,32 @@ static void mtk_vcodec_vpu_reset_dec_handler(void *priv)
{
struct mtk_vcodec_dec_dev *dev = priv;
struct mtk_vcodec_dec_ctx *ctx;
+ unsigned long flags;
dev_err(&dev->plat_dev->dev, "Watchdog timeout!!");
- mutex_lock(&dev->dev_ctx_lock);
+ spin_lock_irqsave(&dev->dev_ctx_lock, flags);
list_for_each_entry(ctx, &dev->ctx_list, list) {
ctx->state = MTK_STATE_ABORT;
mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id);
}
- mutex_unlock(&dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&dev->dev_ctx_lock, flags);
}
static void mtk_vcodec_vpu_reset_enc_handler(void *priv)
{
struct mtk_vcodec_enc_dev *dev = priv;
struct mtk_vcodec_enc_ctx *ctx;
+ unsigned long flags;
dev_err(&dev->plat_dev->dev, "Watchdog timeout!!");
- mutex_lock(&dev->dev_ctx_lock);
+ spin_lock_irqsave(&dev->dev_ctx_lock, flags);
list_for_each_entry(ctx, &dev->ctx_list, list) {
ctx->state = MTK_STATE_ABORT;
mtk_v4l2_vdec_dbg(0, ctx, "[%d] Change to state MTK_STATE_ABORT", ctx->id);
}
- mutex_unlock(&dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&dev->dev_ctx_lock, flags);
}
static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = {
@@ -117,8 +119,10 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(void *priv, enum mtk_vcodec_fw_use
vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_enc_handler, priv, rst_id);
fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL);
- if (!fw)
+ if (!fw) {
+ put_device(&fw_pdev->dev);
return ERR_PTR(-ENOMEM);
+ }
fw->type = VPU;
fw->ops = &mtk_vcodec_vpu_msg;
fw->pdev = fw_pdev;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
index d691bd533103..1f32ba11a18c 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
@@ -674,15 +674,8 @@ static int vidioc_vdec_g_fmt(struct file *file, void *priv,
{
struct mtk_vcodec_dec_ctx *ctx = file_to_dec_ctx(file);
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
- struct vb2_queue *vq;
struct mtk_q_data *q_data;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq) {
- mtk_v4l2_vdec_err(ctx, "no vb2 queue for type=%d", f->type);
- return -EINVAL;
- }
-
q_data = mtk_vdec_get_q_data(ctx, f->type);
pix_mp->field = V4L2_FIELD_NONE;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
index 46d176e6de63..3b81fae9f913 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
@@ -198,6 +198,7 @@ static int fops_vcodec_open(struct file *file)
struct mtk_vcodec_dec_ctx *ctx = NULL;
int ret = 0, i, hw_count;
struct vb2_queue *src_vq;
+ unsigned long flags;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -267,9 +268,9 @@ static int fops_vcodec_open(struct file *file)
ctx->dev->vdec_pdata->init_vdec_params(ctx);
- mutex_lock(&dev->dev_ctx_lock);
+ spin_lock_irqsave(&dev->dev_ctx_lock, flags);
list_add(&ctx->list, &dev->ctx_list);
- mutex_unlock(&dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&dev->dev_ctx_lock, flags);
mtk_vcodec_dbgfs_create(ctx);
mutex_unlock(&dev->dev_mutex);
@@ -294,6 +295,7 @@ static int fops_vcodec_release(struct file *file)
{
struct mtk_vcodec_dec_dev *dev = video_drvdata(file);
struct mtk_vcodec_dec_ctx *ctx = file_to_dec_ctx(file);
+ unsigned long flags;
mtk_v4l2_vdec_dbg(0, ctx, "[%d] decoder", ctx->id);
mutex_lock(&dev->dev_mutex);
@@ -312,9 +314,9 @@ static int fops_vcodec_release(struct file *file)
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
mtk_vcodec_dbgfs_remove(dev, ctx->id);
- mutex_lock(&dev->dev_ctx_lock);
+ spin_lock_irqsave(&dev->dev_ctx_lock, flags);
list_del_init(&ctx->list);
- mutex_unlock(&dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&dev->dev_ctx_lock, flags);
kfree(ctx);
mutex_unlock(&dev->dev_mutex);
return 0;
@@ -407,7 +409,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
for (i = 0; i < MTK_VDEC_HW_MAX; i++)
mutex_init(&dev->dec_mutex[i]);
mutex_init(&dev->dev_mutex);
- mutex_init(&dev->dev_ctx_lock);
+ spin_lock_init(&dev->dev_ctx_lock);
spin_lock_init(&dev->irqlock);
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s",
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
index d047d7c580fb..9d68808e8f9c 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
@@ -285,7 +285,7 @@ struct mtk_vcodec_dec_dev {
/* decoder hardware mutex lock */
struct mutex dec_mutex[MTK_VDEC_HW_MAX];
struct mutex dev_mutex;
- struct mutex dev_ctx_lock;
+ spinlock_t dev_ctx_lock;
struct workqueue_struct *decode_workqueue;
spinlock_t irqlock;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index bf21f2467a0f..7be4b6086920 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -1073,7 +1073,7 @@ static int vdec_av1_slice_setup_lat_from_src_buf(struct vdec_av1_slice_instance
lat_buf->src_buf_req = src->vb2_buf.req_obj.req;
dst = &lat_buf->ts_info;
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
vsi->frame.cur_ts = dst->vb2_buf.timestamp;
return 0;
@@ -1780,7 +1780,7 @@ static int vdec_av1_slice_setup_core_to_dst_buf(struct vdec_av1_slice_instance *
if (!dst)
return -EINVAL;
- v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst, true);
+ v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst);
return 0;
}
@@ -1810,8 +1810,6 @@ static int vdec_av1_slice_setup_core_buffer(struct vdec_av1_slice_instance *inst
/* reference buffers */
vq = v4l2_m2m_get_vq(instance->ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
- if (!vq)
- return -EINVAL;
/* get current output buffer */
vb = &v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx)->vb2_buf;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
index 1e1b32faac77..b9a5ea7887d3 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
@@ -367,7 +367,7 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
inst->vsi_ctx.dec.vdec_fb_va = (u64)(uintptr_t)fb;
v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
- &dst_buf_info->m2m_buf.vb, true);
+ &dst_buf_info->m2m_buf.vb);
err = get_vdec_decode_parameters(inst);
if (err)
goto err_free_fb_out;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
index 5b25e1679b51..9a9dc2f88d6e 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
@@ -570,7 +570,7 @@ static int vdec_h264_slice_setup_core_buffer_ext(struct vdec_h264_slice_inst *in
}
vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true);
+ v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2);
return 0;
}
@@ -674,7 +674,7 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
}
vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true);
+ v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2);
vdec_h264_slice_fill_decode_reflist(inst, &inst->vsi_core->h264_slice_params,
share_info);
@@ -768,7 +768,8 @@ static int vdec_h264_slice_lat_decode_ext(void *h_vdec, struct mtk_vcodec_mem *b
src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req;
- v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true);
+ v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
+ &lat_buf->ts_info);
err = vdec_h264_slice_fill_decode_parameters(inst, share_info,
&inst->vsi_ext->h264_slice_params);
@@ -900,7 +901,8 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
inst->vsi->dec.nal_info = buf[nal_start_idx];
lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req;
- v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true);
+ v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
+ &lat_buf->ts_info);
err = vdec_h264_slice_fill_decode_parameters(inst, share_info,
&inst->vsi->h264_slice_params);
@@ -1039,7 +1041,7 @@ static int vdec_h264_slice_single_decode_ext(void *h_vdec, struct mtk_vcodec_mem
inst->vsi_ctx_ext.dec.vdec_fb_va = (u64)(uintptr_t)fb;
v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
- &dst_buf_info->m2m_buf.vb, true);
+ &dst_buf_info->m2m_buf.vb);
err = get_vdec_sig_decode_parameters(inst);
if (err)
goto err_free_fb_out;
@@ -1135,7 +1137,7 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs
inst->vsi_ctx.dec.vdec_fb_va = (u64)(uintptr_t)fb;
v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
- &dst_buf_info->m2m_buf.vb, true);
+ &dst_buf_info->m2m_buf.vb);
err = get_vdec_sig_decode_parameters(inst);
if (err)
goto err_free_fb_out;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
index 2725db882e5b..88eca50c2017 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
@@ -742,7 +742,8 @@ static int vdec_hevc_slice_setup_lat_buffer(struct vdec_hevc_slice_inst *inst,
src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req;
- v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true);
+ v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
+ &lat_buf->ts_info);
*res_chg = inst->resolution_changed;
if (inst->resolution_changed) {
@@ -847,7 +848,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst,
}
vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true);
+ v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2);
return 0;
}
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
index 232ef3bd246a..e1d4960553f2 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
@@ -358,7 +358,7 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
y_fb_dma, c_fb_dma);
v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
- &dst_buf_info->m2m_buf.vb, true);
+ &dst_buf_info->m2m_buf.vb);
err = vdec_vp8_slice_get_decode_parameters(inst);
if (err)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
index 47c302745c1d..cd1935014d76 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
@@ -706,7 +706,7 @@ int vdec_vp9_slice_setup_single_from_src_to_dst(struct vdec_vp9_slice_instance *
if (!dst)
return -EINVAL;
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
return 0;
}
@@ -724,7 +724,7 @@ static int vdec_vp9_slice_setup_lat_from_src_buf(struct vdec_vp9_slice_instance
lat_buf->src_buf_req = src->vb2_buf.req_obj.req;
dst = &lat_buf->ts_info;
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
return 0;
}
@@ -1652,7 +1652,7 @@ static int vdec_vp9_slice_setup_core_to_dst_buf(struct vdec_vp9_slice_instance *
if (!dst)
return -EINVAL;
- v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst, true);
+ v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst);
return 0;
}
@@ -1686,8 +1686,6 @@ static int vdec_vp9_slice_setup_core_buffer(struct vdec_vp9_slice_instance *inst
/* reference buffers */
vq = v4l2_m2m_get_vq(instance->ctx->m2m_ctx,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
- if (!vq)
- return -EINVAL;
/* get current output buffer */
vb = &v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx)->vb2_buf;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
index 145958206e38..40b97f114cf6 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
@@ -75,16 +75,17 @@ static void handle_get_param_msg_ack(const struct vdec_vpu_ipi_get_param_ack *ms
static bool vpu_dec_check_ap_inst(struct mtk_vcodec_dec_dev *dec_dev, struct vdec_vpu_inst *vpu)
{
struct mtk_vcodec_dec_ctx *ctx;
+ unsigned long flags;
int ret = false;
- mutex_lock(&dec_dev->dev_ctx_lock);
+ spin_lock_irqsave(&dec_dev->dev_ctx_lock, flags);
list_for_each_entry(ctx, &dec_dev->ctx_list, list) {
if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) {
ret = true;
break;
}
}
- mutex_unlock(&dec_dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&dec_dev->dev_ctx_lock, flags);
return ret;
}
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
index d815e962ab89..6faf3f659e75 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
@@ -421,10 +421,6 @@ static int vidioc_venc_s_fmt_cap(struct file *file, void *priv,
const struct mtk_video_fmt *fmt;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq) {
- mtk_v4l2_venc_err(ctx, "fail to get vq");
- return -EINVAL;
- }
if (vb2_is_busy(vq)) {
mtk_v4l2_venc_err(ctx, "queue busy");
@@ -476,10 +472,6 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv,
const struct mtk_video_fmt *fmt;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq) {
- mtk_v4l2_venc_err(ctx, "fail to get vq");
- return -EINVAL;
- }
if (vb2_is_busy(vq)) {
mtk_v4l2_venc_err(ctx, "queue busy");
@@ -524,15 +516,9 @@ static int vidioc_venc_g_fmt(struct file *file, void *priv,
{
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
struct mtk_vcodec_enc_ctx *ctx = file_to_enc_ctx(file);
- struct vb2_queue *vq;
struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type);
int i;
- vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
-
pix->width = q_data->coded_width;
pix->height = q_data->coded_height;
pix->pixelformat = q_data->fmt->fourcc;
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
index fb1c3bdc2dae..82b8ff38e8f1 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
@@ -117,6 +117,7 @@ static int fops_vcodec_open(struct file *file)
struct mtk_vcodec_enc_ctx *ctx = NULL;
int ret = 0;
struct vb2_queue *src_vq;
+ unsigned long flags;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -176,9 +177,9 @@ static int fops_vcodec_open(struct file *file)
mtk_v4l2_venc_dbg(2, ctx, "Create instance [%d]@%p m2m_ctx=%p ",
ctx->id, ctx, ctx->m2m_ctx);
- mutex_lock(&dev->dev_ctx_lock);
+ spin_lock_irqsave(&dev->dev_ctx_lock, flags);
list_add(&ctx->list, &dev->ctx_list);
- mutex_unlock(&dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&dev->dev_ctx_lock, flags);
mutex_unlock(&dev->dev_mutex);
mtk_v4l2_venc_dbg(0, ctx, "%s encoder [%d]", dev_name(&dev->plat_dev->dev),
@@ -203,6 +204,7 @@ static int fops_vcodec_release(struct file *file)
{
struct mtk_vcodec_enc_dev *dev = video_drvdata(file);
struct mtk_vcodec_enc_ctx *ctx = file_to_enc_ctx(file);
+ unsigned long flags;
mtk_v4l2_venc_dbg(1, ctx, "[%d] encoder", ctx->id);
mutex_lock(&dev->dev_mutex);
@@ -213,9 +215,9 @@ static int fops_vcodec_release(struct file *file)
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
- mutex_lock(&dev->dev_ctx_lock);
+ spin_lock_irqsave(&dev->dev_ctx_lock, flags);
list_del_init(&ctx->list);
- mutex_unlock(&dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&dev->dev_ctx_lock, flags);
kfree(ctx);
mutex_unlock(&dev->dev_mutex);
return 0;
@@ -297,7 +299,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
mutex_init(&dev->enc_mutex);
mutex_init(&dev->dev_mutex);
- mutex_init(&dev->dev_ctx_lock);
+ spin_lock_init(&dev->dev_ctx_lock);
spin_lock_init(&dev->irqlock);
snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s",
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
index 5b304a551236..0cddfa13594f 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.h
@@ -206,7 +206,7 @@ struct mtk_vcodec_enc_dev {
/* encoder hardware mutex lock */
struct mutex enc_mutex;
struct mutex dev_mutex;
- struct mutex dev_ctx_lock;
+ spinlock_t dev_ctx_lock;
struct workqueue_struct *encode_workqueue;
int enc_irq;
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
index 51bb7ee141b9..3c229b1f6b21 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_vpu_if.c
@@ -45,16 +45,17 @@ static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data)
static bool vpu_enc_check_ap_inst(struct mtk_vcodec_enc_dev *enc_dev, struct venc_vpu_inst *vpu)
{
struct mtk_vcodec_enc_ctx *ctx;
+ unsigned long flags;
int ret = false;
- mutex_lock(&enc_dev->dev_ctx_lock);
+ spin_lock_irqsave(&enc_dev->dev_ctx_lock, flags);
list_for_each_entry(ctx, &enc_dev->ctx_list, list) {
if (!IS_ERR_OR_NULL(ctx) && ctx->vpu_inst == vpu) {
ret = true;
break;
}
}
- mutex_unlock(&enc_dev->dev_ctx_lock);
+ spin_unlock_irqrestore(&enc_dev->dev_ctx_lock, flags);
return ret;
}
diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c
index 45f8f6904867..2a2211671fd9 100644
--- a/drivers/media/platform/nvidia/tegra-vde/h264.c
+++ b/drivers/media/platform/nvidia/tegra-vde/h264.c
@@ -776,7 +776,7 @@ static int tegra_vde_h264_setup_frames(struct tegra_ctx *ctx,
* If userspace doesn't tell us frame's type, then we will try decode
* as-is.
*/
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
if (h->decode_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME)
tb->b_frame = true;
diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c
index 97744c7b7c03..4aaf9c3fff53 100644
--- a/drivers/media/platform/nxp/dw100/dw100.c
+++ b/drivers/media/platform/nxp/dw100/dw100.c
@@ -735,13 +735,8 @@ static int dw100_enum_framesizes(struct file *file, void *priv,
static int dw100_g_fmt_vid(struct file *file, void *priv, struct v4l2_format *f)
{
struct dw100_ctx *ctx = dw100_file2ctx(file);
- struct vb2_queue *vq;
struct dw100_q_data *q_data;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = dw100_get_q_data(ctx, f->type);
f->fmt.pix_mp = q_data->pix_fmt;
@@ -803,8 +798,6 @@ static int dw100_s_fmt(struct dw100_ctx *ctx, struct v4l2_format *f)
struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = dw100_get_q_data(ctx, f->type);
if (!q_data)
@@ -1437,7 +1430,7 @@ static void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb,
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE),
in_vb->sequence, out_vb->sequence);
- v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true);
+ v4l2_m2m_buf_copy_metadata(in_vb, out_vb);
/* Now, let's deal with hardware ... */
dw100_hw_master_bus_disable(dw_dev);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index df3ccdf767ba..9e4a813489c0 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -1537,7 +1537,7 @@ static void mxc_jpeg_device_run(void *priv)
src_buf->sequence = q_data_out->sequence++;
dst_buf->sequence = q_data_cap->sequence++;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
jpeg_src_buf = vb2_to_mxc_buf(&src_buf->vb2_buf);
if (q_data_cap->fmt->mem_planes != dst_buf->vb2_buf.num_planes) {
@@ -2491,8 +2491,6 @@ static int mxc_jpeg_s_fmt(struct mxc_jpeg_ctx *ctx,
struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
if (vb2_is_busy(vq)) {
v4l2_err(&jpeg->v4l2_dev, "queue busy\n");
@@ -2528,8 +2526,6 @@ static int mxc_jpeg_s_fmt_vid_out(struct file *file, void *priv,
return 0;
dst_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, cap_type);
- if (!dst_vq)
- return -EINVAL;
if (vb2_is_busy(dst_vq))
return 0;
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index d5de7854f579..088b2945aee3 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -351,6 +351,8 @@ struct mipi_csis_device {
u32 hs_settle;
u32 clk_settle;
+ unsigned int num_data_lanes;
+
spinlock_t slock; /* Protect events */
struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS];
struct dentry *debugfs_root;
@@ -573,7 +575,7 @@ static void mipi_csis_system_enable(struct mipi_csis_device *csis, int on)
val = mipi_csis_read(csis, MIPI_CSIS_DPHY_CMN_CTRL);
val &= ~MIPI_CSIS_DPHY_CMN_CTRL_ENABLE;
if (on) {
- mask = (1 << (csis->bus.num_data_lanes + 1)) - 1;
+ mask = (1 << (csis->num_data_lanes + 1)) - 1;
val |= (mask & MIPI_CSIS_DPHY_CMN_CTRL_ENABLE);
}
mipi_csis_write(csis, MIPI_CSIS_DPHY_CMN_CTRL, val);
@@ -623,7 +625,7 @@ static int mipi_csis_calculate_params(struct mipi_csis_device *csis,
/* Calculate the line rate from the pixel rate. */
link_freq = v4l2_get_link_freq(csis->source.pad, csis_fmt->width,
- csis->bus.num_data_lanes * 2);
+ csis->num_data_lanes * 2);
if (link_freq < 0) {
dev_err(csis->dev, "Unable to obtain link frequency: %d\n",
(int)link_freq);
@@ -668,7 +670,7 @@ static void mipi_csis_set_params(struct mipi_csis_device *csis,
const struct v4l2_mbus_framefmt *format,
const struct csis_pix_format *csis_fmt)
{
- int lanes = csis->bus.num_data_lanes;
+ int lanes = csis->num_data_lanes;
u32 val;
val = mipi_csis_read(csis, MIPI_CSIS_CMN_CTRL);
@@ -1032,6 +1034,12 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable)
format = v4l2_subdev_state_get_format(state, CSIS_PAD_SINK);
csis_fmt = find_csis_format(format->code);
+ ret = v4l2_get_active_data_lanes(csis->source.pad, csis->bus.num_data_lanes);
+ if (ret < 0)
+ goto err_unlock;
+
+ csis->num_data_lanes = ret;
+
ret = mipi_csis_calculate_params(csis, csis_fmt);
if (ret < 0)
goto err_unlock;
@@ -1366,8 +1374,9 @@ static int mipi_csis_async_register(struct mipi_csis_device *csis)
}
csis->bus = vep.bus.mipi_csi2;
+ csis->num_data_lanes = csis->bus.num_data_lanes;
- dev_dbg(csis->dev, "data lanes: %d\n", csis->bus.num_data_lanes);
+ dev_dbg(csis->dev, "max data lanes: %d\n", csis->bus.num_data_lanes);
dev_dbg(csis->dev, "flags: 0x%08x\n", csis->bus.flags);
asd = v4l2_async_nf_add_fwnode_remote(&csis->notifier, ep,
@@ -1481,6 +1490,7 @@ static int mipi_csis_parse_dt(struct mipi_csis_device *csis)
struct device_node *node = csis->dev->of_node;
of_property_read_u32(node, "clock-frequency", &csis->clk_frequency);
+ dev_dbg(csis->dev, "clock frequency: %u\n", csis->clk_frequency);
csis->num_channels = 1;
of_property_read_u32(node, "fsl,num-channels", &csis->num_channels);
@@ -1566,9 +1576,6 @@ static int mipi_csis_probe(struct platform_device *pdev)
goto err_unregister_all;
}
- dev_info(dev, "lanes: %d, freq: %u\n",
- csis->bus.num_data_lanes, csis->clk_frequency);
-
return 0;
err_unregister_all:
@@ -1634,4 +1641,3 @@ module_platform_driver(mipi_csis_driver);
MODULE_DESCRIPTION("i.MX7 & i.MX8 MIPI CSI-2 receiver driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:imx-mipi-csi2");
diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c
index 6cc9b07ea53a..3f9a67a6bd4d 100644
--- a/drivers/media/platform/nxp/imx-pxp.c
+++ b/drivers/media/platform/nxp/imx-pxp.c
@@ -1180,13 +1180,8 @@ static int pxp_enum_fmt_vid_out(struct file *file, void *priv,
static int pxp_g_fmt(struct pxp_ctx *ctx, struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct pxp_q_data *q_data;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = get_q_data(ctx, f->type);
f->fmt.pix.width = q_data->width;
@@ -1329,8 +1324,6 @@ static int pxp_s_fmt(struct pxp_ctx *ctx, struct v4l2_format *f)
struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = get_q_data(ctx, f->type);
if (!q_data)
diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c
index 34a92642bbfe..933a5f39f9f4 100644
--- a/drivers/media/platform/nxp/imx7-media-csi.c
+++ b/drivers/media/platform/nxp/imx7-media-csi.c
@@ -2290,4 +2290,3 @@ module_platform_driver(imx7_csi_driver);
MODULE_DESCRIPTION("i.MX7 CSI subdev driver");
MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:imx7-csi");
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
index adc8d9960bf0..c3d411ddf492 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -314,6 +314,28 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = {
.has_36bit_dma = true,
};
+static const struct mxc_isi_plat_data mxc_imx8qm_data = {
+ .model = MXC_ISI_IMX8QM,
+ .num_ports = 5,
+ .num_channels = 8,
+ .reg_offset = 0x10000,
+ .ier_reg = &mxc_imx8_isi_ier_qm,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .buf_active_reverse = true,
+ .has_36bit_dma = false,
+};
+
+static const struct mxc_isi_plat_data mxc_imx8qxp_data = {
+ .model = MXC_ISI_IMX8QXP,
+ .num_ports = 5,
+ .num_channels = 6,
+ .reg_offset = 0x10000,
+ .ier_reg = &mxc_imx8_isi_ier_v2,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .buf_active_reverse = true,
+ .has_36bit_dma = false,
+};
+
static const struct mxc_isi_plat_data mxc_imx8ulp_data = {
.model = MXC_ISI_IMX8ULP,
.num_ports = 1,
@@ -325,37 +347,26 @@ static const struct mxc_isi_plat_data mxc_imx8ulp_data = {
.has_36bit_dma = false,
};
-static const struct mxc_isi_plat_data mxc_imx93_data = {
- .model = MXC_ISI_IMX93,
+static const struct mxc_isi_plat_data mxc_imx91_data = {
+ .model = MXC_ISI_IMX91,
.num_ports = 1,
.num_channels = 1,
.reg_offset = 0,
.ier_reg = &mxc_imx8_isi_ier_v2,
.set_thd = &mxc_imx8_isi_thd_v1,
.buf_active_reverse = true,
- .gasket_ops = &mxc_imx93_gasket_ops,
- .has_36bit_dma = false,
-};
-
-static const struct mxc_isi_plat_data mxc_imx8qm_data = {
- .model = MXC_ISI_IMX8QM,
- .num_ports = 5,
- .num_channels = 8,
- .reg_offset = 0x10000,
- .ier_reg = &mxc_imx8_isi_ier_qm,
- .set_thd = &mxc_imx8_isi_thd_v1,
- .buf_active_reverse = true,
.has_36bit_dma = false,
};
-static const struct mxc_isi_plat_data mxc_imx8qxp_data = {
- .model = MXC_ISI_IMX8QXP,
- .num_ports = 5,
- .num_channels = 6,
- .reg_offset = 0x10000,
+static const struct mxc_isi_plat_data mxc_imx93_data = {
+ .model = MXC_ISI_IMX93,
+ .num_ports = 1,
+ .num_channels = 1,
+ .reg_offset = 0,
.ier_reg = &mxc_imx8_isi_ier_v2,
.set_thd = &mxc_imx8_isi_thd_v1,
.buf_active_reverse = true,
+ .gasket_ops = &mxc_imx93_gasket_ops,
.has_36bit_dma = false,
};
@@ -547,6 +558,7 @@ static const struct of_device_id mxc_isi_of_match[] = {
{ .compatible = "fsl,imx8qm-isi", .data = &mxc_imx8qm_data },
{ .compatible = "fsl,imx8qxp-isi", .data = &mxc_imx8qxp_data },
{ .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data },
+ { .compatible = "fsl,imx91-isi", .data = &mxc_imx91_data },
{ .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data },
{ /* sentinel */ },
};
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
index e84af5127e4e..3cbd35305af0 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -160,6 +160,7 @@ enum model {
MXC_ISI_IMX8QM,
MXC_ISI_IMX8QXP,
MXC_ISI_IMX8ULP,
+ MXC_ISI_IMX91,
MXC_ISI_IMX93,
};
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c
index f69c3b5d4782..58ec7eddcd3d 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-gasket.c
@@ -3,6 +3,8 @@
* Copyright 2019-2023 NXP
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/regmap.h>
#include <media/mipi-csi2.h>
@@ -16,8 +18,7 @@
#define GASKET_BASE(n) (0x0060 + (n) * 0x30)
#define GASKET_CTRL 0x0000
-#define GASKET_CTRL_DATA_TYPE(dt) ((dt) << 8)
-#define GASKET_CTRL_DATA_TYPE_MASK (0x3f << 8)
+#define GASKET_CTRL_DATA_TYPE(dt) FIELD_PREP(GENMASK(13, 8), dt)
#define GASKET_CTRL_DUAL_COMP_ENABLE BIT(1)
#define GASKET_CTRL_ENABLE BIT(0)
@@ -57,9 +58,10 @@ const struct mxc_gasket_ops mxc_imx8_gasket_ops = {
* i.MX93 gasket
*/
-#define DISP_MIX_CAMERA_MUX 0x30
-#define DISP_MIX_CAMERA_MUX_DATA_TYPE(x) (((x) & 0x3f) << 3)
-#define DISP_MIX_CAMERA_MUX_GASKET_ENABLE BIT(16)
+#define DISP_MIX_CAMERA_MUX 0x30
+#define DISP_MIX_CAMERA_MUX_DATA_TYPE(x) FIELD_PREP(GENMASK(8, 3), x)
+#define DISP_MIX_CAMERA_MUX_GASKET_ENABLE BIT(16)
+#define DISP_MIX_CAMERA_MUX_GASKET_SOURCE_TYPE BIT(17)
static void mxc_imx93_gasket_enable(struct mxc_isi_dev *isi,
const struct v4l2_mbus_frame_desc *fd,
@@ -70,6 +72,16 @@ static void mxc_imx93_gasket_enable(struct mxc_isi_dev *isi,
val = DISP_MIX_CAMERA_MUX_DATA_TYPE(fd->entry[0].bus.csi2.dt);
val |= DISP_MIX_CAMERA_MUX_GASKET_ENABLE;
+
+ /*
+ * CAMERA MUX
+ * - [17]: Selects source input to gasket
+ * 0: Data from MIPI CSI
+ * 1: Data from parallel camera
+ */
+ if (fd->type == V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL)
+ val |= DISP_MIX_CAMERA_MUX_GASKET_SOURCE_TYPE;
+
regmap_write(isi->gasket, DISP_MIX_CAMERA_MUX, val);
}
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
index 00afcbfbdde4..f425ac786854 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
@@ -107,7 +107,7 @@ static void mxc_isi_m2m_frame_write_done(struct mxc_isi_pipe *pipe, u32 status)
src_vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
dst_vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf, false);
+ v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf);
src_vbuf->sequence = ctx->queues.out.sequence++;
dst_vbuf->sequence = ctx->queues.cap.sequence++;
@@ -554,8 +554,6 @@ static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh,
struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
if (vb2_is_busy(vq))
return -EBUSY;
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index 3a4645f59a44..371b4e81328c 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -418,8 +418,8 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
src_pad = media_entity_remote_source_pad_unique(&sd_state->sd->entity);
if (IS_ERR(src_pad)) {
- dev_err(state->dev, "can't get source pad of %s (%ld)\n",
- sd_state->sd->name, PTR_ERR(src_pad));
+ dev_err(state->dev, "can't get source pad of %s (%pe)\n",
+ sd_state->sd->name, src_pad);
return PTR_ERR(src_pad);
}
@@ -1114,4 +1114,3 @@ module_platform_driver(imx8mq_mipi_csi_driver);
MODULE_DESCRIPTION("i.MX8MQ MIPI CSI-2 receiver driver");
MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@puri.sm>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:imx8mq-mipi-csi2");
diff --git a/drivers/media/platform/nxp/mx2_emmaprp.c b/drivers/media/platform/nxp/mx2_emmaprp.c
index 3aae8c0b690c..02d57229b9b3 100644
--- a/drivers/media/platform/nxp/mx2_emmaprp.c
+++ b/drivers/media/platform/nxp/mx2_emmaprp.c
@@ -431,13 +431,8 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct emmaprp_q_data *q_data;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = get_q_data(ctx, f->type);
f->fmt.pix.width = q_data->width;
@@ -540,8 +535,6 @@ static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
int ret;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = get_q_data(ctx, f->type);
if (!q_data)
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 23960d02877d..5e349b491513 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -23,6 +23,7 @@ qcom-camss-objs += \
camss-vfe-680.o \
camss-vfe-gen3.o \
camss-vfe-gen1.o \
+ camss-vfe-vbif.o \
camss-vfe.o \
camss-video.o \
camss-format.o \
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index a229ba04b158..619abbf60781 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -587,6 +587,102 @@ csiphy_lane_regs lane_regs_sm8550[] = {
{0x0C64, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
};
+/* GEN2 2.2.0 2PH 4 lane DPHY mode */
+static const struct
+csiphy_lane_regs lane_regs_sm8650[] = {
+ {0x0e94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0ea0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e94, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS},
+ {0x0e30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e0c, 0xff, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e38, 0x1f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e2c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e34, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e1c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e3c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e04, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0e08, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0e10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0094, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x00a0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0090, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS},
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x04a0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0490, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x08a0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0890, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS},
+ {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0800, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0838, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x082c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0834, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x081c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x083c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0804, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0808, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0c94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0ca0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c94, 0x07, 0xd1, CSIPHY_DEFAULT_PARAMS},
+ {0x0c30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c00, 0x8e, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c38, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c2c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c34, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c1c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c3c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c04, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c08, 0x19, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0c10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */
static const struct
csiphy_lane_regs lane_regs_x1e80100[] = {
@@ -914,6 +1010,7 @@ static bool csiphy_is_gen2(u32 version)
case CAMSS_8300:
case CAMSS_845:
case CAMSS_8550:
+ case CAMSS_8650:
case CAMSS_8775P:
case CAMSS_X1E80100:
ret = true;
@@ -1018,6 +1115,11 @@ static int csiphy_init(struct csiphy_device *csiphy)
regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8550);
regs->offset = 0x1000;
break;
+ case CAMSS_8650:
+ regs->lane_regs = &lane_regs_sm8650[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8650);
+ regs->offset = 0x1000;
+ break;
case CAMSS_8300:
case CAMSS_8775P:
regs->lane_regs = &lane_regs_sa8775p[0];
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 2de97f58f9ae..a734fb7dde0a 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -600,6 +600,7 @@ int msm_csiphy_subdev_init(struct camss *camss,
return PTR_ERR(csiphy->base);
if (camss->res->version == CAMSS_8x16 ||
+ camss->res->version == CAMSS_8x39 ||
camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_8x96) {
csiphy->base_clk_mux =
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index 2dc585c6123d..aaf3caa42d33 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -1112,6 +1112,8 @@ int msm_ispif_subdev_init(struct camss *camss,
/* Number of ISPIF lines - same as number of CSID hardware modules */
if (camss->res->version == CAMSS_8x16)
ispif->line_num = 2;
+ else if (camss->res->version == CAMSS_8x39)
+ ispif->line_num = 3;
else if (camss->res->version == CAMSS_8x96 ||
camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_660)
@@ -1128,7 +1130,8 @@ int msm_ispif_subdev_init(struct camss *camss,
ispif->line[i].ispif = ispif;
ispif->line[i].id = i;
- if (camss->res->version == CAMSS_8x16) {
+ if (camss->res->version == CAMSS_8x16 ||
+ camss->res->version == CAMSS_8x39) {
ispif->line[i].formats = ispif_formats_8x16;
ispif->line[i].nformats =
ARRAY_SIZE(ispif_formats_8x16);
@@ -1162,7 +1165,8 @@ int msm_ispif_subdev_init(struct camss *camss,
ispif->irq = ret;
snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s",
dev_name(dev), MSM_ISPIF_NAME);
- if (camss->res->version == CAMSS_8x16)
+ if (camss->res->version == CAMSS_8x16 ||
+ camss->res->version == CAMSS_8x39)
ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16,
IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
else if (camss->res->version == CAMSS_8x96 ||
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
index 901677293d97..9cf1ccdb2fe7 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
@@ -15,6 +15,7 @@
#include "camss.h"
#include "camss-vfe.h"
#include "camss-vfe-gen1.h"
+#include "camss-vfe-vbif.h"
#define VFE_0_HW_VERSION 0x000
@@ -733,6 +734,7 @@ static void vfe_set_qos(struct vfe_device *vfe)
{
u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
+ int ret;
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
@@ -742,6 +744,16 @@ static void vfe_set_qos(struct vfe_device *vfe)
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
+
+ /* SoC-specific VBIF settings */
+ if (vfe->res->has_vbif) {
+ ret = vfe_vbif_apply_settings(vfe);
+ if (ret < 0) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "VFE: VBIF error %d\n",
+ ret);
+ }
+ }
}
static void vfe_set_ds(struct vfe_device *vfe)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-vbif.c b/drivers/media/platform/qcom/camss/camss-vfe-vbif.c
new file mode 100644
index 000000000000..911f8da02f1f
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-vbif.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * camss-vfe-vbif.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE VBIF Module
+ *
+ * Copyright (c) 2025, The Linux Foundation. All rights reserved.
+ *
+ */
+
+#include <linux/io.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+#include "camss-vfe-vbif.h"
+
+#define VBIF_FIXED_SORT_EN 0x30
+#define VBIF_FIXED_SORT_SEL0 0x34
+
+void vfe_vbif_write_reg(struct vfe_device *vfe, u32 reg, u32 val)
+{
+ writel_relaxed(val, vfe->vbif_base + reg);
+}
+
+int vfe_vbif_apply_settings(struct vfe_device *vfe)
+{
+ vfe_vbif_write_reg(vfe, VBIF_FIXED_SORT_EN, 0xfff);
+ vfe_vbif_write_reg(vfe, VBIF_FIXED_SORT_SEL0, 0x555000);
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-vbif.h b/drivers/media/platform/qcom/camss/camss-vfe-vbif.h
new file mode 100644
index 000000000000..502db629e961
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-vbif.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * camss-vfe-vbif.h
+ *
+ * Qualcomm MSM Camera Subsystem - VFE VBIF Module
+ *
+ * Copyright (c) 2025, The Linux Foundation. All rights reserved.
+ *
+ */
+#ifndef QC_MSM_CAMSS_VFE_VBIF_H
+#define QC_MSM_CAMSS_VFE_VBIF_H
+
+#include "camss-vfe.h"
+
+void vfe_vbif_write_reg(struct vfe_device *vfe, u32 reg, u32 val);
+
+int vfe_vbif_apply_settings(struct vfe_device *vfe);
+
+#endif /* QC_MSM_CAMSS_VFE_VBIF_H */
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index dff8d0a1e8c2..9c7ad8aa4058 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -290,6 +290,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
switch (vfe->camss->res->version) {
case CAMSS_8x16:
+ case CAMSS_8x39:
case CAMSS_8x53:
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
@@ -348,6 +349,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
case CAMSS_8300:
case CAMSS_845:
case CAMSS_8550:
+ case CAMSS_8650:
case CAMSS_8775P:
case CAMSS_X1E80100:
switch (sink_code) {
@@ -541,7 +543,7 @@ int vfe_enable_output_v2(struct vfe_line *line)
ops->vfe_wm_start(vfe, output->wm_idx[0], line);
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < CAMSS_INIT_BUF_COUNT; i++) {
output->buf[i] = vfe_buf_get_pending(output);
if (!output->buf[i])
break;
@@ -914,7 +916,8 @@ static int vfe_match_clock_names(struct vfe_device *vfe,
return (!strcmp(clock->name, vfe_name) ||
!strcmp(clock->name, vfe_lite_name) ||
!strcmp(clock->name, "vfe_lite") ||
- !strcmp(clock->name, "camnoc_axi"));
+ !strcmp(clock->name, "camnoc_axi") ||
+ !strcmp(clock->name, "camnoc_rt_axi"));
}
/*
@@ -1827,6 +1830,15 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
return PTR_ERR(vfe->base);
}
+ if (vfe->res->has_vbif) {
+ vfe->vbif_base = devm_platform_ioremap_resource_byname(pdev,
+ vfe->res->vbif_name);
+ if (IS_ERR(vfe->vbif_base)) {
+ dev_err(dev, "could not map vbif memory\n");
+ return PTR_ERR(vfe->vbif_base);
+ }
+ }
+
/* Interrupt */
ret = platform_get_irq_byname(pdev, res->interrupt[0]);
@@ -1995,6 +2007,7 @@ static int vfe_bpl_align(struct vfe_device *vfe)
case CAMSS_8300:
case CAMSS_845:
case CAMSS_8550:
+ case CAMSS_8650:
case CAMSS_8775P:
case CAMSS_X1E80100:
ret = 16;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 0300efdb1c46..ae9dad353a37 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -136,6 +136,8 @@ struct vfe_subdev_resources {
u8 line_num;
bool has_pd;
char *pd_name;
+ bool has_vbif;
+ char *vbif_name;
const struct vfe_hw_ops *hw_ops;
const struct camss_formats *formats_rdi;
const struct camss_formats *formats_pix;
@@ -145,6 +147,7 @@ struct vfe_device {
struct camss *camss;
u8 id;
void __iomem *base;
+ void __iomem *vbif_base;
u32 irq;
char irq_name[30];
struct camss_clock *clock;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 2fbcd0e343aa..fcc2b2c3cba0 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -154,6 +154,149 @@ static const struct camss_subdev_resources vfe_res_8x16[] = {
}
};
+static const struct camss_subdev_resources csiphy_res_8x39[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" },
+ .clock_rate = { { 0 },
+ { 40000000, 80000000 },
+ { 0 },
+ { 100000000, 200000000 } },
+ .reg = { "csiphy0", "csiphy0_clk_mux" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_2ph_1_0,
+ .formats = &csiphy_formats_8x16
+ }
+ },
+
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" },
+ .clock_rate = { { 0 },
+ { 40000000, 80000000 },
+ { 0 },
+ { 100000000, 200000000 } },
+ .reg = { "csiphy1", "csiphy1_clk_mux" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_2ph_1_0,
+ .formats = &csiphy_formats_8x16
+ }
+ }
+};
+
+static const struct camss_subdev_resources csid_res_8x39[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
+ "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
+ .clock_rate = { { 0 },
+ { 40000000, 80000000 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_4_1,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_1
+ }
+ },
+
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
+ "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
+ .clock_rate = { { 0 },
+ { 40000000, 80000000 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_4_1,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_1
+ }
+ },
+
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
+ "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" },
+ .clock_rate = { { 0 },
+ { 40000000, 80000000 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .hw_ops = &csid_ops_4_1,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_1
+ }
+ },
+};
+
+static const struct camss_subdev_resources ispif_res_8x39 = {
+ /* ISPIF */
+ .clock = { "top_ahb", "ispif_ahb", "ahb",
+ "csi0", "csi0_pix", "csi0_rdi",
+ "csi1", "csi1_pix", "csi1_rdi",
+ "csi2", "csi2_pix", "csi2_rdi" },
+ .clock_for_reset = { "vfe0", "csi_vfe0" },
+ .reg = { "ispif", "csi_clk_mux" },
+ .interrupt = { "ispif" },
+};
+
+static const struct camss_subdev_resources vfe_res_8x39[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "top_ahb", "ispif_ahb", "vfe0", "csi_vfe0",
+ "vfe_ahb", "vfe_axi", "ahb" },
+ .clock_rate = { { 0 },
+ { 40000000, 80000000 },
+ { 50000000, 80000000, 100000000, 160000000,
+ 177780000, 200000000, 266670000, 320000000,
+ 400000000, 465000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .has_vbif = true,
+ .vbif_name = "vfe0_vbif",
+ .hw_ops = &vfe_ops_4_1,
+ .formats_rdi = &vfe_formats_rdi_8x16,
+ .formats_pix = &vfe_formats_pix_8x16
+ }
+ }
+};
+
static const struct camss_subdev_resources csid_res_8x53[] = {
/* CSID0 */
{
@@ -2617,6 +2760,317 @@ static const struct resources_icc icc_res_sm8550[] = {
},
};
+static const struct camss_subdev_resources csiphy_res_sm8650[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", },
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 400000000 },
+ { 400000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845,
+ },
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdd-csiphy01-0p9", "vdd-csiphy01-1p2", },
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 400000000 },
+ { 400000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845,
+ },
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", },
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 400000000 },
+ { 400000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845,
+ },
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", },
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 400000000 },
+ { 400000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" },
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845,
+ },
+ },
+ /* CSIPHY4 */
+ {
+ .regulators = { "vdd-csiphy24-0p9", "vdd-csiphy24-1p2", },
+ .clock = { "csiphy4", "csiphy4_timer" },
+ .clock_rate = { { 400000000 },
+ { 400000000 } },
+ .reg = { "csiphy4" },
+ .interrupt = { "csiphy4" },
+ .csiphy = {
+ .id = 4,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845,
+ },
+ },
+ /* CSIPHY5 */
+ {
+ .regulators = { "vdd-csiphy35-0p9", "vdd-csiphy35-1p2", },
+ .clock = { "csiphy5", "csiphy5_timer" },
+ .clock_rate = { { 400000000 },
+ { 400000000 } },
+ .reg = { "csiphy5" },
+ .interrupt = { "csiphy5" },
+ .csiphy = {
+ .id = 5,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845,
+ },
+ },
+};
+
+static const struct camss_subdev_resources csid_res_sm8650[] = {
+ /* CSID0 */
+ {
+ .regulators = { },
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_gen3,
+ .formats = &csid_formats_gen2,
+ },
+ },
+ /* CSID1 */
+ {
+ .regulators = { },
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_gen3,
+ .formats = &csid_formats_gen2,
+ },
+ },
+ /* CSID2 */
+ {
+ .regulators = { },
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_gen3,
+ .formats = &csid_formats_gen2,
+ },
+ },
+ /* CSID3 lite */
+ {
+ .regulators = { },
+ .clock = { "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx" },
+ .clock_rate = { { 0 },
+ { 400000000, 480000000 },
+ { 0 } },
+ .reg = { "csid_lite0" },
+ .interrupt = { "csid_lite0" },
+ .csid = {
+ .is_lite = true,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_gen3,
+ .formats = &csid_formats_gen2,
+ },
+ },
+ /* CSID4 lite */
+ {
+ .regulators = { },
+ .clock = { "vfe_lite_ahb", "vfe_lite_csid", "vfe_lite_cphy_rx" },
+ .clock_rate = { { 0 },
+ { 400000000, 480000000 },
+ { 0 } },
+ .reg = { "csid_lite1" },
+ .interrupt = { "csid_lite1" },
+ .csid = {
+ .is_lite = true,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_gen3,
+ .formats = &csid_formats_gen2,
+ },
+ },
+};
+
+static const struct camss_subdev_resources vfe_res_sm8650[] = {
+ /* VFE0 */
+ {
+ .regulators = { },
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb",
+ "camnoc_axi", "vfe0_fast_ahb", "vfe0", "cpas_vfe0",
+ "qdss_debug_xo",
+ },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 0 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 0 },
+ { 0 },
+ },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_gen3,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* VFE1 */
+ {
+ .regulators = { },
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb",
+ "camnoc_axi", "vfe1_fast_ahb", "vfe1", "cpas_vfe1",
+ "qdss_debug_xo",
+ },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 0 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 0 },
+ { 0 },
+ },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_gen3,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* VFE2 */
+ {
+ .regulators = { },
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb",
+ "camnoc_axi", "vfe2_fast_ahb", "vfe2", "cpas_vfe2",
+ "qdss_debug_xo",
+ },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 0 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 0 },
+ { 0 },
+ },
+ .reg = { "vfe2" },
+ .interrupt = { "vfe2" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife2",
+ .hw_ops = &vfe_ops_gen3,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* VFE3 lite */
+ {
+ .regulators = { },
+ .clock = { "gcc_axi_hf", "cpas_ahb", "camnoc_axi",
+ "vfe_lite_ahb", "vfe_lite", "cpas_vfe_lite",
+ "qdss_debug_xo",
+ },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 0 },
+ { 400000000, 480000000 },
+ { 0 },
+ { 0 },
+ },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_gen3,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* VFE4 lite */
+ {
+ .regulators = { },
+ .clock = { "gcc_axi_hf", "cpas_ahb", "camnoc_axi",
+ "vfe_lite_ahb", "vfe_lite", "cpas_vfe_lite",
+ "qdss_debug_xo",
+ },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 0 },
+ { 400000000, 480000000 },
+ { 0 },
+ { 0 },
+ },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_gen3,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+};
+
+static const struct resources_icc icc_res_sm8650[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 38400,
+ .icc_bw_tbl.peak = 76800,
+ },
+ {
+ .name = "hf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
static const struct camss_subdev_resources csiphy_res_8300[] = {
/* CSIPHY0 */
{
@@ -4171,6 +4625,7 @@ static int camss_probe(struct platform_device *pdev)
return -ENOMEM;
if (camss->res->version == CAMSS_8x16 ||
+ camss->res->version == CAMSS_8x39 ||
camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_8x96) {
camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL);
@@ -4302,6 +4757,17 @@ static const struct camss_resources msm8916_resources = {
.vfe_num = ARRAY_SIZE(vfe_res_8x16),
};
+static const struct camss_resources msm8939_resources = {
+ .version = CAMSS_8x39,
+ .csiphy_res = csiphy_res_8x39,
+ .csid_res = csid_res_8x39,
+ .ispif_res = &ispif_res_8x39,
+ .vfe_res = vfe_res_8x39,
+ .csiphy_num = ARRAY_SIZE(csiphy_res_8x39),
+ .csid_num = ARRAY_SIZE(csid_res_8x39),
+ .vfe_num = ARRAY_SIZE(vfe_res_8x39),
+};
+
static const struct camss_resources msm8953_resources = {
.version = CAMSS_8x53,
.icc_res = icc_res_8x53,
@@ -4452,6 +4918,20 @@ static const struct camss_resources sm8550_resources = {
.vfe_num = ARRAY_SIZE(vfe_res_8550),
};
+static const struct camss_resources sm8650_resources = {
+ .version = CAMSS_8650,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_sm8650,
+ .csid_res = csid_res_sm8650,
+ .csid_wrapper_res = &csid_wrapper_res_sm8550,
+ .vfe_res = vfe_res_sm8650,
+ .icc_res = icc_res_sm8650,
+ .icc_path_num = ARRAY_SIZE(icc_res_sm8650),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_sm8650),
+ .csid_num = ARRAY_SIZE(csid_res_sm8650),
+ .vfe_num = ARRAY_SIZE(vfe_res_sm8650),
+};
+
static const struct camss_resources x1e80100_resources = {
.version = CAMSS_X1E80100,
.pd_name = "top",
@@ -4468,6 +4948,7 @@ static const struct camss_resources x1e80100_resources = {
static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,msm8916-camss", .data = &msm8916_resources },
+ { .compatible = "qcom,msm8939-camss", .data = &msm8939_resources },
{ .compatible = "qcom,msm8953-camss", .data = &msm8953_resources },
{ .compatible = "qcom,msm8996-camss", .data = &msm8996_resources },
{ .compatible = "qcom,qcm2290-camss", .data = &qcm2290_resources },
@@ -4480,6 +4961,7 @@ static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,sdm845-camss", .data = &sdm845_resources },
{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
{ .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
+ { .compatible = "qcom,sm8650-camss", .data = &sm8650_resources },
{ .compatible = "qcom,x1e80100-camss", .data = &x1e80100_resources },
{ }
};
@@ -4537,7 +5019,6 @@ static struct platform_driver qcom_camss_driver = {
module_platform_driver(qcom_camss_driver);
-MODULE_ALIAS("platform:qcom-camss");
MODULE_DESCRIPTION("Qualcomm Camera Subsystem driver");
MODULE_AUTHOR("Todor Tomov <todor.tomov@linaro.org>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index a70fbc78ccc3..9d9a62640e25 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -41,6 +41,7 @@
(to_camss_index(ptr_module, index)->dev)
#define CAMSS_RES_MAX 17
+#define CAMSS_INIT_BUF_COUNT 2
struct camss_subdev_resources {
char *regulators[CAMSS_RES_MAX];
@@ -81,6 +82,7 @@ enum camss_version {
CAMSS_2290,
CAMSS_7280,
CAMSS_8x16,
+ CAMSS_8x39,
CAMSS_8x53,
CAMSS_8x96,
CAMSS_8250,
@@ -88,6 +90,7 @@ enum camss_version {
CAMSS_8300,
CAMSS_845,
CAMSS_8550,
+ CAMSS_8650,
CAMSS_8775P,
CAMSS_X1E80100,
};
diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile
index 13270cd6d899..fad3be044e5f 100644
--- a/drivers/media/platform/qcom/iris/Makefile
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -26,7 +26,7 @@ qcom-iris-objs += iris_buffer.o \
iris_vpu_common.o \
ifeq ($(CONFIG_VIDEO_QCOM_VENUS),)
-qcom-iris-objs += iris_platform_sm8250.o
+qcom-iris-objs += iris_platform_gen1.o
endif
obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
index c0900038e7de..b89b1ee06cce 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -171,9 +171,14 @@ static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst)
static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst)
{
u32 y_plane, uv_plane, y_stride, uv_stride;
- struct v4l2_format *f = inst->fmt_dst;
u32 uv_meta_stride, uv_meta_plane;
u32 y_meta_stride, y_meta_plane;
+ struct v4l2_format *f = NULL;
+
+ if (inst->domain == DECODER)
+ f = inst->fmt_dst;
+ else
+ f = inst->fmt_src;
y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, META_STRIDE_ALIGNED >> 1),
META_STRIDE_ALIGNED);
@@ -261,7 +266,10 @@ int iris_get_buffer_size(struct iris_inst *inst,
case BUF_INPUT:
return iris_dec_bitstream_buffer_size(inst);
case BUF_OUTPUT:
- return iris_yuv_buffer_size_nv12(inst);
+ if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
+ return iris_yuv_buffer_size_qc08c(inst);
+ else
+ return iris_yuv_buffer_size_nv12(inst);
case BUF_DPB:
return iris_yuv_buffer_size_qc08c(inst);
default:
@@ -270,7 +278,10 @@ int iris_get_buffer_size(struct iris_inst *inst,
} else {
switch (buffer_type) {
case BUF_INPUT:
- return iris_yuv_buffer_size_nv12(inst);
+ if (inst->fmt_src->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
+ return iris_yuv_buffer_size_qc08c(inst);
+ else
+ return iris_yuv_buffer_size_nv12(inst);
case BUF_OUTPUT:
return iris_enc_bitstream_buffer_size(inst);
default:
diff --git a/drivers/media/platform/qcom/iris/iris_common.c b/drivers/media/platform/qcom/iris/iris_common.c
index 9fc663bdaf3f..7f1c7fe144f7 100644
--- a/drivers/media/platform/qcom/iris/iris_common.c
+++ b/drivers/media/platform/qcom/iris/iris_common.c
@@ -91,12 +91,14 @@ int iris_process_streamon_input(struct iris_inst *inst)
int iris_process_streamon_output(struct iris_inst *inst)
{
const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
- bool drain_active = false, drc_active = false;
enum iris_inst_sub_state clear_sub_state = 0;
+ bool drain_active, drc_active, first_ipsc;
int ret = 0;
iris_scale_power(inst);
+ first_ipsc = inst->sub_state & IRIS_INST_SUB_FIRST_IPSC;
+
drain_active = inst->sub_state & IRIS_INST_SUB_DRAIN &&
inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
@@ -108,7 +110,8 @@ int iris_process_streamon_output(struct iris_inst *inst)
else if (drain_active)
clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
- if (inst->domain == DECODER && inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ /* Input internal buffer reconfiguration required in case of resolution change */
+ if (first_ipsc || drc_active) {
ret = iris_alloc_and_queue_input_int_bufs(inst);
if (ret)
return ret;
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index 754a5ad718bc..c0b3a09ad3e3 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -301,7 +301,7 @@ error:
void iris_session_init_caps(struct iris_core *core)
{
- struct platform_inst_fw_cap *caps;
+ const struct platform_inst_fw_cap *caps;
u32 i, num_cap, cap_id;
caps = core->iris_platform_data->inst_fw_caps_dec;
@@ -313,13 +313,23 @@ void iris_session_init_caps(struct iris_core *core)
continue;
core->inst_fw_caps_dec[cap_id].cap_id = caps[i].cap_id;
- core->inst_fw_caps_dec[cap_id].min = caps[i].min;
- core->inst_fw_caps_dec[cap_id].max = caps[i].max;
core->inst_fw_caps_dec[cap_id].step_or_mask = caps[i].step_or_mask;
- core->inst_fw_caps_dec[cap_id].value = caps[i].value;
core->inst_fw_caps_dec[cap_id].flags = caps[i].flags;
core->inst_fw_caps_dec[cap_id].hfi_id = caps[i].hfi_id;
core->inst_fw_caps_dec[cap_id].set = caps[i].set;
+
+ if (cap_id == PIPE) {
+ core->inst_fw_caps_dec[cap_id].value =
+ core->iris_platform_data->num_vpp_pipe;
+ core->inst_fw_caps_dec[cap_id].min =
+ core->iris_platform_data->num_vpp_pipe;
+ core->inst_fw_caps_dec[cap_id].max =
+ core->iris_platform_data->num_vpp_pipe;
+ } else {
+ core->inst_fw_caps_dec[cap_id].min = caps[i].min;
+ core->inst_fw_caps_dec[cap_id].max = caps[i].max;
+ core->inst_fw_caps_dec[cap_id].value = caps[i].value;
+ }
}
caps = core->iris_platform_data->inst_fw_caps_enc;
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
index 9ab499fad946..679444327ed7 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -19,8 +19,7 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
u32 pas_id = core->iris_platform_data->pas_id;
const struct firmware *firmware = NULL;
struct device *dev = core->dev;
- struct reserved_mem *rmem;
- struct device_node *node;
+ struct resource res;
phys_addr_t mem_phys;
size_t res_size;
ssize_t fw_size;
@@ -30,17 +29,12 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4)
return -EINVAL;
- node = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (!node)
- return -EINVAL;
-
- rmem = of_reserved_mem_lookup(node);
- of_node_put(node);
- if (!rmem)
- return -EINVAL;
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ if (ret)
+ return ret;
- mem_phys = rmem->base;
- res_size = rmem->size;
+ mem_phys = res.start;
+ res_size = resource_size(&res);
ret = request_firmware(&firmware, fw_name, dev);
if (ret)
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
index e1788c266bb1..52da7ef7bab0 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -774,27 +774,29 @@ static int iris_hfi_gen1_set_raw_format(struct iris_inst *inst, u32 plane)
pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
if (iris_split_mode_enabled(inst)) {
fmt.buffer_type = HFI_BUFFER_OUTPUT;
- fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ?
- HFI_COLOR_FORMAT_NV12_UBWC : 0;
+ fmt.format = HFI_COLOR_FORMAT_NV12_UBWC;
ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
if (ret)
return ret;
fmt.buffer_type = HFI_BUFFER_OUTPUT2;
- fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ?
+ HFI_COLOR_FORMAT_NV12 : HFI_COLOR_FORMAT_NV12_UBWC;
ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
} else {
fmt.buffer_type = HFI_BUFFER_OUTPUT;
- fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ?
+ HFI_COLOR_FORMAT_NV12 : HFI_COLOR_FORMAT_NV12_UBWC;
ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
}
} else {
pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat;
fmt.buffer_type = HFI_BUFFER_INPUT;
- fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ?
+ HFI_COLOR_FORMAT_NV12 : HFI_COLOR_FORMAT_NV12_UBWC;
ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
}
@@ -806,6 +808,9 @@ static int iris_hfi_gen1_set_format_constraints(struct iris_inst *inst, u32 plan
const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO;
struct hfi_uncompressed_plane_actual_constraints_info pconstraint;
+ if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
+ return 0;
+
pconstraint.buffer_type = HFI_BUFFER_OUTPUT2;
pconstraint.num_planes = 2;
pconstraint.plane_format[0].stride_multiples = 128;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
index 4ce71a142508..6a772db2ec33 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -422,6 +422,20 @@ static int iris_hfi_gen2_set_level(struct iris_inst *inst, u32 plane)
sizeof(u32));
}
+static int iris_hfi_gen2_set_opb_enable(struct iris_inst *inst, u32 plane)
+{
+ u32 port = iris_hfi_gen2_get_port(inst, plane);
+ u32 opb_enable = iris_split_mode_enabled(inst);
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_OPB_ENABLE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &opb_enable,
+ sizeof(u32));
+}
+
static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane)
{
u32 port = iris_hfi_gen2_get_port(inst, plane);
@@ -429,10 +443,12 @@ static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane)
if (inst->domain == DECODER) {
pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
- hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FMT_NV12 : 0;
+ hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ?
+ HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC;
} else {
pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat;
- hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FMT_NV12 : 0;
+ hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ?
+ HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC;
}
return iris_hfi_gen2_session_set_property(inst,
@@ -527,6 +543,7 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
{HFI_PROP_SIGNAL_COLOR_INFO, iris_hfi_gen2_set_colorspace },
{HFI_PROP_PROFILE, iris_hfi_gen2_set_profile },
{HFI_PROP_LEVEL, iris_hfi_gen2_set_level },
+ {HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable },
{HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat },
{HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline },
{HFI_PROP_TIER, iris_hfi_gen2_set_tier },
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
index aa1f795f5626..1b6a4dbac828 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -91,6 +91,7 @@ enum hfi_seq_header_mode {
#define HFI_PROP_BUFFER_MARK 0x0300016c
#define HFI_PROP_RAW_RESOLUTION 0x03000178
#define HFI_PROP_TOTAL_PEAK_BITRATE 0x0300017C
+#define HFI_PROP_OPB_ENABLE 0x03000184
#define HFI_PROP_COMV_BUFFER_COUNT 0x03000193
#define HFI_PROP_END 0x03FFFFFF
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
index 5982d7adefea..62fbb30691ff 100644
--- a/drivers/media/platform/qcom/iris/iris_instance.h
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -15,12 +15,17 @@
#define DEFAULT_WIDTH 320
#define DEFAULT_HEIGHT 240
-enum iris_fmt_type {
+enum iris_fmt_type_out {
IRIS_FMT_H264,
IRIS_FMT_HEVC,
IRIS_FMT_VP9,
};
+enum iris_fmt_type_cap {
+ IRIS_FMT_NV12,
+ IRIS_FMT_QC08C,
+};
+
struct iris_fmt {
u32 pixfmt;
u32 type;
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index 58d05e0a112e..8d8cdb56a3c7 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -41,16 +41,19 @@ enum pipe_type {
PIPE_4 = 4,
};
-extern struct iris_platform_data qcs8300_data;
-extern struct iris_platform_data sm8250_data;
-extern struct iris_platform_data sm8550_data;
-extern struct iris_platform_data sm8650_data;
-extern struct iris_platform_data sm8750_data;
+extern const struct iris_platform_data qcs8300_data;
+extern const struct iris_platform_data sc7280_data;
+extern const struct iris_platform_data sm8250_data;
+extern const struct iris_platform_data sm8550_data;
+extern const struct iris_platform_data sm8650_data;
+extern const struct iris_platform_data sm8750_data;
enum platform_clk_type {
IRIS_AXI_CLK, /* AXI0 in case of platforms with multiple AXI clocks */
IRIS_CTRL_CLK,
+ IRIS_AHB_CLK,
IRIS_HW_CLK,
+ IRIS_HW_AHB_CLK,
IRIS_AXI1_CLK,
IRIS_CTRL_FREERUN_CLK,
IRIS_HW_FREERUN_CLK,
@@ -215,15 +218,16 @@ struct iris_platform_data {
const char *fwname;
u32 pas_id;
struct platform_inst_caps *inst_caps;
- struct platform_inst_fw_cap *inst_fw_caps_dec;
+ const struct platform_inst_fw_cap *inst_fw_caps_dec;
u32 inst_fw_caps_dec_size;
- struct platform_inst_fw_cap *inst_fw_caps_enc;
+ const struct platform_inst_fw_cap *inst_fw_caps_enc;
u32 inst_fw_caps_enc_size;
struct tz_cp_config *tz_cp_config_data;
u32 core_arch;
u32 hw_response_timeout;
struct ubwc_config_data *ubwc_config;
u32 num_vpp_pipe;
+ bool no_aon;
u32 max_session_count;
/* max number of macroblocks per frame supported */
u32 max_core_mbpf;
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_gen1.c
index 16486284f8ac..34cbeb8f52e2 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen1.c
@@ -12,18 +12,18 @@
#include "iris_vpu_buffer.h"
#include "iris_vpu_common.h"
+#include "iris_platform_sc7280.h"
+
#define BITRATE_MIN 32000
#define BITRATE_MAX 160000000
#define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2)
#define BITRATE_STEP 100
-static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = {
+static const struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = {
{
.cap_id = PIPE,
- .min = PIPE_1,
- .max = PIPE_4,
+ /* .max, .min and .value are set via platform data */
.step_or_mask = 1,
- .value = PIPE_4,
.hfi_id = HFI_PROPERTY_PARAM_WORK_ROUTE,
.set = iris_set_pipe,
},
@@ -38,7 +38,7 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = {
},
};
-static struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
+static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
{
.cap_id = STAGE,
.min = STAGE_1,
@@ -314,7 +314,7 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = {
BUF_SCRATCH_2,
};
-struct iris_platform_data sm8250_data = {
+const struct iris_platform_data sm8250_data = {
.get_instance = iris_hfi_gen1_get_instance,
.init_hfi_command_ops = &iris_hfi_gen1_command_ops_init,
.init_hfi_response_ops = iris_hfi_gen1_response_ops_init,
@@ -364,3 +364,54 @@ struct iris_platform_data sm8250_data = {
.enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl,
.enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl),
};
+
+const struct iris_platform_data sc7280_data = {
+ .get_instance = iris_hfi_gen1_get_instance,
+ .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen1_response_ops_init,
+ .get_vpu_buffer_size = iris_vpu_buf_size,
+ .vpu_ops = &iris_vpu2_ops,
+ .set_preset_registers = iris_set_sm8250_preset_registers,
+ .icc_tbl = sm8250_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table),
+ .bw_tbl_dec = sc7280_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec),
+ .pmdomain_tbl = sm8250_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table),
+ .opp_pd_tbl = sc7280_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table),
+ .clk_tbl = sc7280_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu/vpu20_p1.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_sm8250,
+ .inst_fw_caps_dec = inst_fw_cap_sm8250_dec,
+ .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec),
+ .inst_fw_caps_enc = inst_fw_cap_sm8250_enc,
+ .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc),
+ .tz_cp_config_data = &tz_cp_config_sm8250,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .num_vpp_pipe = 1,
+ .no_aon = true,
+ .max_session_count = 16,
+ .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256,
+ /* max spec for SC7280 is 4096x2176@60fps */
+ .max_core_mbps = 4096 * 2176 / 256 * 60,
+ .dec_input_config_params_default =
+ sm8250_vdec_input_config_param_default,
+ .dec_input_config_params_default_size =
+ ARRAY_SIZE(sm8250_vdec_input_config_param_default),
+ .enc_input_config_params = sm8250_venc_input_config_param,
+ .enc_input_config_params_size =
+ ARRAY_SIZE(sm8250_venc_input_config_param),
+
+ .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl),
+
+ .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl,
+ .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl),
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
index 36d69cc73986..c1989240c248 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
@@ -19,7 +19,7 @@
#define VIDEO_ARCH_LX 1
#define BITRATE_MAX 245000000
-static struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
+static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
{
.cap_id = PROFILE_H264,
.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
@@ -160,10 +160,8 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
},
{
.cap_id = PIPE,
- .min = PIPE_1,
- .max = PIPE_4,
+ /* .max, .min and .value are set via platform data */
.step_or_mask = 1,
- .value = PIPE_4,
.hfi_id = HFI_PROP_PIPE,
.set = iris_set_pipe,
},
@@ -203,7 +201,7 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
},
};
-static struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
+static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
{
.cap_id = PROFILE_H264,
.min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
@@ -691,6 +689,7 @@ static const u32 sm8550_venc_input_config_params[] = {
};
static const u32 sm8550_vdec_output_config_params[] = {
+ HFI_PROP_OPB_ENABLE,
HFI_PROP_COLOR_FORMAT,
HFI_PROP_LINEAR_STRIDE_SCANLINE,
};
@@ -737,7 +736,7 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = {
BUF_SCRATCH_2,
};
-struct iris_platform_data sm8550_data = {
+const struct iris_platform_data sm8550_data = {
.get_instance = iris_hfi_gen2_get_instance,
.init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
.init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
@@ -827,7 +826,7 @@ struct iris_platform_data sm8550_data = {
* - controller_rst_tbl to sm8650_controller_reset_table
* - fwname to "qcom/vpu/vpu33_p4.mbn"
*/
-struct iris_platform_data sm8650_data = {
+const struct iris_platform_data sm8650_data = {
.get_instance = iris_hfi_gen2_get_instance,
.init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
.init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
@@ -912,7 +911,7 @@ struct iris_platform_data sm8650_data = {
.enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
};
-struct iris_platform_data sm8750_data = {
+const struct iris_platform_data sm8750_data = {
.get_instance = iris_hfi_gen2_get_instance,
.init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
.init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
@@ -996,9 +995,8 @@ struct iris_platform_data sm8750_data = {
/*
* Shares most of SM8550 data except:
* - inst_caps to platform_inst_cap_qcs8300
- * - inst_fw_caps to inst_fw_cap_qcs8300
*/
-struct iris_platform_data qcs8300_data = {
+const struct iris_platform_data qcs8300_data = {
.get_instance = iris_hfi_gen2_get_instance,
.init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
.init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
@@ -1022,10 +1020,10 @@ struct iris_platform_data qcs8300_data = {
.fwname = "qcom/vpu/vpu30_p4_s6.mbn",
.pas_id = IRIS_PAS_ID,
.inst_caps = &platform_inst_cap_qcs8300,
- .inst_fw_caps_dec = inst_fw_cap_qcs8300_dec,
- .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_qcs8300_dec),
- .inst_fw_caps_enc = inst_fw_cap_qcs8300_enc,
- .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_qcs8300_enc),
+ .inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
+ .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
+ .inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
+ .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
.tz_cp_config_data = &tz_cp_config_sm8550,
.core_arch = VIDEO_ARCH_LX,
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
index 35ea0efade73..61025f1e965b 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
@@ -3,537 +3,8 @@
* Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
*/
-#define BITRATE_MAX 245000000
-
-static struct platform_inst_fw_cap inst_fw_cap_qcs8300_dec[] = {
- {
- .cap_id = PROFILE_H264,
- .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
- .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH),
- .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
- .hfi_id = HFI_PROP_PROFILE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- .set = iris_set_u32_enum,
- },
- {
- .cap_id = PROFILE_HEVC,
- .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
- .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
- BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
- .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
- .hfi_id = HFI_PROP_PROFILE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- .set = iris_set_u32_enum,
- },
- {
- .cap_id = PROFILE_VP9,
- .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
- .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) |
- BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2),
- .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
- .hfi_id = HFI_PROP_PROFILE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- .set = iris_set_u32_enum,
- },
- {
- .cap_id = LEVEL_H264,
- .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
- .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2),
- .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1,
- .hfi_id = HFI_PROP_LEVEL,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- .set = iris_set_u32_enum,
- },
- {
- .cap_id = LEVEL_HEVC,
- .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
- .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2),
- .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
- .hfi_id = HFI_PROP_LEVEL,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- .set = iris_set_u32_enum,
- },
- {
- .cap_id = LEVEL_VP9,
- .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0,
- .max = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) |
- BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0),
- .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0,
- .hfi_id = HFI_PROP_LEVEL,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- .set = iris_set_u32_enum,
- },
- {
- .cap_id = TIER,
- .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
- .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) |
- BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH),
- .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
- .hfi_id = HFI_PROP_TIER,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- .set = iris_set_u32_enum,
- },
- {
- .cap_id = INPUT_BUF_HOST_MAX_COUNT,
- .min = DEFAULT_MAX_HOST_BUF_COUNT,
- .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
- .step_or_mask = 1,
- .value = DEFAULT_MAX_HOST_BUF_COUNT,
- .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
- .flags = CAP_FLAG_INPUT_PORT,
- .set = iris_set_u32,
- },
- {
- .cap_id = STAGE,
- .min = STAGE_1,
- .max = STAGE_2,
- .step_or_mask = 1,
- .value = STAGE_2,
- .hfi_id = HFI_PROP_STAGE,
- .set = iris_set_stage,
- },
- {
- .cap_id = PIPE,
- .min = PIPE_1,
- .max = PIPE_2,
- .step_or_mask = 1,
- .value = PIPE_2,
- .hfi_id = HFI_PROP_PIPE,
- .set = iris_set_pipe,
- },
- {
- .cap_id = POC,
- .min = 0,
- .max = 2,
- .step_or_mask = 1,
- .value = 1,
- .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE,
- },
- {
- .cap_id = CODED_FRAMES,
- .min = CODED_FRAMES_PROGRESSIVE,
- .max = CODED_FRAMES_PROGRESSIVE,
- .step_or_mask = 0,
- .value = CODED_FRAMES_PROGRESSIVE,
- .hfi_id = HFI_PROP_CODED_FRAMES,
- },
- {
- .cap_id = BIT_DEPTH,
- .min = BIT_DEPTH_8,
- .max = BIT_DEPTH_8,
- .step_or_mask = 1,
- .value = BIT_DEPTH_8,
- .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
- },
- {
- .cap_id = RAP_FRAME,
- .min = 0,
- .max = 1,
- .step_or_mask = 1,
- .value = 1,
- .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME,
- .flags = CAP_FLAG_INPUT_PORT,
- .set = iris_set_u32,
- },
-};
-
-static struct platform_inst_fw_cap inst_fw_cap_qcs8300_enc[] = {
- {
- .cap_id = PROFILE_H264,
- .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
- .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
- BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH),
- .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
- .hfi_id = HFI_PROP_PROFILE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = PROFILE_HEVC,
- .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
- .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
- BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) |
- BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10),
- .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
- .hfi_id = HFI_PROP_PROFILE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = LEVEL_H264,
- .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
- .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_0,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
- BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0),
- .value = V4L2_MPEG_VIDEO_H264_LEVEL_5_0,
- .hfi_id = HFI_PROP_LEVEL,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = LEVEL_HEVC,
- .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
- .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
- BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2),
- .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_5,
- .hfi_id = HFI_PROP_LEVEL,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = STAGE,
- .min = STAGE_1,
- .max = STAGE_2,
- .step_or_mask = 1,
- .value = STAGE_2,
- .hfi_id = HFI_PROP_STAGE,
- },
- {
- .cap_id = HEADER_MODE,
- .min = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
- .max = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) |
- BIT(V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME),
- .value = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
- .hfi_id = HFI_PROP_SEQ_HEADER_MODE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = PREPEND_SPSPPS_TO_IDR,
- .min = 0,
- .max = 1,
- .step_or_mask = 1,
- .value = 0,
- },
- {
- .cap_id = BITRATE,
- .min = 1,
- .max = BITRATE_MAX,
- .step_or_mask = 1,
- .value = BITRATE_DEFAULT,
- .hfi_id = HFI_PROP_TOTAL_BITRATE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = BITRATE_PEAK,
- .min = 1,
- .max = BITRATE_MAX,
- .step_or_mask = 1,
- .value = BITRATE_DEFAULT,
- .hfi_id = HFI_PROP_TOTAL_PEAK_BITRATE,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = BITRATE_MODE,
- .min = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
- .max = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
- BIT(V4L2_MPEG_VIDEO_BITRATE_MODE_CBR),
- .value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
- .hfi_id = HFI_PROP_RATE_CONTROL,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = FRAME_SKIP_MODE,
- .min = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
- .max = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) |
- BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT) |
- BIT(V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT),
- .value = V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = FRAME_RC_ENABLE,
- .min = 0,
- .max = 1,
- .step_or_mask = 1,
- .value = 1,
- },
- {
- .cap_id = GOP_SIZE,
- .min = 0,
- .max = INT_MAX,
- .step_or_mask = 1,
- .value = 2 * DEFAULT_FPS - 1,
- .hfi_id = HFI_PROP_MAX_GOP_FRAMES,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = ENTROPY_MODE,
- .min = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
- .max = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
- .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) |
- BIT(V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC),
- .value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
- .hfi_id = HFI_PROP_CABAC_SESSION,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
- },
- {
- .cap_id = MIN_FRAME_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- .hfi_id = HFI_PROP_MIN_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT,
- },
- {
- .cap_id = MIN_FRAME_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- .hfi_id = HFI_PROP_MIN_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT,
- },
- {
- .cap_id = MAX_FRAME_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- .hfi_id = HFI_PROP_MAX_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT,
- },
- {
- .cap_id = MAX_FRAME_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- .hfi_id = HFI_PROP_MAX_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT,
- },
- {
- .cap_id = I_FRAME_MIN_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- },
- {
- .cap_id = I_FRAME_MIN_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- },
- {
- .cap_id = P_FRAME_MIN_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- },
- {
- .cap_id = P_FRAME_MIN_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- },
- {
- .cap_id = B_FRAME_MIN_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- },
- {
- .cap_id = B_FRAME_MIN_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MIN_QP_8BIT,
- },
- {
- .cap_id = I_FRAME_MAX_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- },
- {
- .cap_id = I_FRAME_MAX_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- },
- {
- .cap_id = P_FRAME_MAX_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- },
- {
- .cap_id = P_FRAME_MAX_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- },
- {
- .cap_id = B_FRAME_MAX_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- },
- {
- .cap_id = B_FRAME_MAX_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = MAX_QP,
- },
- {
- .cap_id = I_FRAME_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = DEFAULT_QP,
- .hfi_id = HFI_PROP_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = I_FRAME_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = DEFAULT_QP,
- .hfi_id = HFI_PROP_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = P_FRAME_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = DEFAULT_QP,
- .hfi_id = HFI_PROP_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = P_FRAME_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = DEFAULT_QP,
- .hfi_id = HFI_PROP_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = B_FRAME_QP_H264,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = DEFAULT_QP,
- .hfi_id = HFI_PROP_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
- {
- .cap_id = B_FRAME_QP_HEVC,
- .min = MIN_QP_8BIT,
- .max = MAX_QP,
- .step_or_mask = 1,
- .value = DEFAULT_QP,
- .hfi_id = HFI_PROP_QP_PACKED,
- .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
- CAP_FLAG_DYNAMIC_ALLOWED,
- },
-};
+#ifndef __IRIS_PLATFORM_QCS8300_H__
+#define __IRIS_PLATFORM_QCS8300_H__
static struct platform_inst_caps platform_inst_cap_qcs8300 = {
.min_frame_width = 96,
@@ -548,3 +19,5 @@ static struct platform_inst_caps platform_inst_cap_qcs8300 = {
.max_frame_rate = MAXIMUM_FPS,
.max_operating_rate = MAXIMUM_FPS,
};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sc7280.h b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h
new file mode 100644
index 000000000000..f1bef4d4bcfe
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_sc7280.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __IRIS_PLATFORM_SC7280_H__
+#define __IRIS_PLATFORM_SC7280_H__
+
+static const struct bw_info sc7280_bw_table_dec[] = {
+ { ((3840 * 2160) / 256) * 60, 1896000, },
+ { ((3840 * 2160) / 256) * 30, 968000, },
+ { ((1920 * 1080) / 256) * 60, 618000, },
+ { ((1920 * 1080) / 256) * 30, 318000, },
+};
+
+static const char * const sc7280_opp_pd_table[] = { "cx" };
+
+static const struct platform_clk_data sc7280_clk_table[] = {
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_AHB_CLK, "bus" },
+ {IRIS_HW_CLK, "vcodec_core" },
+ {IRIS_HW_AHB_CLK, "vcodec_bus" },
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index 00e99be16e08..9bc9b34c2576 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -358,6 +358,10 @@ static const struct of_device_id iris_dt_match[] = {
},
#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS))
{
+ .compatible = "qcom,sc7280-venus",
+ .data = &sc7280_data,
+ },
+ {
.compatible = "qcom,sm8250-venus",
.data = &sm8250_data,
},
diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c
index cf32f268b703..164490c49c95 100644
--- a/drivers/media/platform/qcom/iris/iris_resources.c
+++ b/drivers/media/platform/qcom/iris/iris_resources.c
@@ -112,7 +112,7 @@ int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk
clock = iris_get_clk_by_type(core, clk_type);
if (!clock)
- return -EINVAL;
+ return -ENOENT;
return clk_prepare_enable(clock);
}
diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c
index 85c70a62b1fd..e2f1131de431 100644
--- a/drivers/media/platform/qcom/iris/iris_utils.c
+++ b/drivers/media/platform/qcom/iris/iris_utils.c
@@ -34,7 +34,8 @@ int iris_get_mbpf(struct iris_inst *inst)
bool iris_split_mode_enabled(struct iris_inst *inst)
{
- return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12;
+ return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12 ||
+ inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C;
}
void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
index 139b821f7952..db8768d8a8f6 100644
--- a/drivers/media/platform/qcom/iris/iris_vb2.c
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -231,6 +231,8 @@ void iris_vb2_stop_streaming(struct vb2_queue *q)
return;
mutex_lock(&inst->lock);
+ if (inst->state == IRIS_INST_ERROR)
+ goto exit;
if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
!V4L2_TYPE_IS_CAPTURE(q->type))
@@ -241,10 +243,10 @@ void iris_vb2_stop_streaming(struct vb2_queue *q)
goto exit;
exit:
- iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
- if (ret)
+ if (ret) {
+ iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
iris_inst_change_state(inst, IRIS_INST_ERROR);
-
+ }
mutex_unlock(&inst->lock);
}
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
index ae13c3e1b426..69ffe52590d3 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.c
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -67,7 +67,7 @@ void iris_vdec_inst_deinit(struct iris_inst *inst)
kfree(inst->fmt_src);
}
-static const struct iris_fmt iris_vdec_formats[] = {
+static const struct iris_fmt iris_vdec_formats_out[] = {
[IRIS_FMT_H264] = {
.pixfmt = V4L2_PIX_FMT_H264,
.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
@@ -82,12 +82,35 @@ static const struct iris_fmt iris_vdec_formats[] = {
},
};
+static const struct iris_fmt iris_vdec_formats_cap[] = {
+ [IRIS_FMT_NV12] = {
+ .pixfmt = V4L2_PIX_FMT_NV12,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ },
+ [IRIS_FMT_QC08C] = {
+ .pixfmt = V4L2_PIX_FMT_QC08C,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ },
+};
+
static const struct iris_fmt *
find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
{
- unsigned int size = ARRAY_SIZE(iris_vdec_formats);
- const struct iris_fmt *fmt = iris_vdec_formats;
+ const struct iris_fmt *fmt = NULL;
+ unsigned int size = 0;
unsigned int i;
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ fmt = iris_vdec_formats_out;
+ size = ARRAY_SIZE(iris_vdec_formats_out);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ fmt = iris_vdec_formats_cap;
+ size = ARRAY_SIZE(iris_vdec_formats_cap);
+ break;
+ default:
+ return NULL;
+ }
for (i = 0; i < size; i++) {
if (fmt[i].pixfmt == pixfmt)
@@ -103,8 +126,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
static const struct iris_fmt *
find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
{
- const struct iris_fmt *fmt = iris_vdec_formats;
- unsigned int size = ARRAY_SIZE(iris_vdec_formats);
+ const struct iris_fmt *fmt = NULL;
+ unsigned int size = 0;
+
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ fmt = iris_vdec_formats_out;
+ size = ARRAY_SIZE(iris_vdec_formats_out);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ fmt = iris_vdec_formats_cap;
+ size = ARRAY_SIZE(iris_vdec_formats_cap);
+ break;
+ default:
+ return NULL;
+ }
if (index >= size || fmt[index].type != type)
return NULL;
@@ -126,9 +162,10 @@ int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (f->index)
+ fmt = find_format_by_index(inst, f->index, f->type);
+ if (!fmt)
return -EINVAL;
- f->pixelformat = V4L2_PIX_FMT_NV12;
+ f->pixelformat = fmt->pixfmt;
break;
default:
return -EINVAL;
@@ -157,7 +194,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) {
+ if (!fmt) {
f_inst = inst->fmt_dst;
f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
@@ -190,8 +227,6 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
u32 codec_align;
q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
- if (!q)
- return -EINVAL;
if (vb2_is_busy(q))
return -EBUSY;
@@ -238,10 +273,11 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
inst->crop.height = f->fmt.pix_mp.height;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
+ return -EINVAL;
+
fmt = inst->fmt_dst;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12)
- return -EINVAL;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
@@ -268,7 +304,8 @@ int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat)
{
const struct iris_fmt *fmt = NULL;
- if (pixelformat != V4L2_PIX_FMT_NV12) {
+ fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (!fmt) {
fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (!fmt)
return -EINVAL;
diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c
index 099bd5ed4ae0..5830eba93c68 100644
--- a/drivers/media/platform/qcom/iris/iris_venc.c
+++ b/drivers/media/platform/qcom/iris/iris_venc.c
@@ -80,7 +80,7 @@ void iris_venc_inst_deinit(struct iris_inst *inst)
kfree(inst->fmt_src);
}
-static const struct iris_fmt iris_venc_formats[] = {
+static const struct iris_fmt iris_venc_formats_cap[] = {
[IRIS_FMT_H264] = {
.pixfmt = V4L2_PIX_FMT_H264,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
@@ -91,12 +91,35 @@ static const struct iris_fmt iris_venc_formats[] = {
},
};
+static const struct iris_fmt iris_venc_formats_out[] = {
+ [IRIS_FMT_NV12] = {
+ .pixfmt = V4L2_PIX_FMT_NV12,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_QC08C] = {
+ .pixfmt = V4L2_PIX_FMT_QC08C,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+};
+
static const struct iris_fmt *
find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
{
- const struct iris_fmt *fmt = iris_venc_formats;
- unsigned int size = ARRAY_SIZE(iris_venc_formats);
+ const struct iris_fmt *fmt = NULL;
+ unsigned int size = 0;
unsigned int i;
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ fmt = iris_venc_formats_out;
+ size = ARRAY_SIZE(iris_venc_formats_out);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ fmt = iris_venc_formats_cap;
+ size = ARRAY_SIZE(iris_venc_formats_cap);
+ break;
+ default:
+ return NULL;
+ }
for (i = 0; i < size; i++) {
if (fmt[i].pixfmt == pixfmt)
@@ -112,8 +135,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
static const struct iris_fmt *
find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
{
- const struct iris_fmt *fmt = iris_venc_formats;
- unsigned int size = ARRAY_SIZE(iris_venc_formats);
+ const struct iris_fmt *fmt = NULL;
+ unsigned int size = 0;
+
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ fmt = iris_venc_formats_out;
+ size = ARRAY_SIZE(iris_venc_formats_out);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ fmt = iris_venc_formats_cap;
+ size = ARRAY_SIZE(iris_venc_formats_cap);
+ break;
+ default:
+ return NULL;
+ }
if (index >= size || fmt[index].type != type)
return NULL;
@@ -127,9 +163,11 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (f->index)
+ fmt = find_format_by_index(inst, f->index, f->type);
+ if (!fmt)
return -EINVAL;
- f->pixelformat = V4L2_PIX_FMT_NV12;
+
+ f->pixelformat = fmt->pixfmt;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = find_format_by_index(inst, f->index, f->type);
@@ -156,7 +194,7 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
fmt = find_format(inst, pixmp->pixelformat, f->type);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) {
+ if (!fmt) {
f_inst = inst->fmt_src;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
@@ -221,7 +259,7 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f)
iris_venc_try_fmt(inst, f);
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12)
+ if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
return -EINVAL;
fmt = inst->fmt_src;
@@ -269,8 +307,6 @@ int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
struct vb2_queue *q;
q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
- if (!q)
- return -EINVAL;
if (vb2_is_busy(q))
return -EBUSY;
@@ -289,7 +325,8 @@ int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat)
{
const struct iris_fmt *fmt = NULL;
- if (pixelformat != V4L2_PIX_FMT_NV12) {
+ fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (!fmt) {
fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (!fmt)
return -EINVAL;
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index d38d0f6961cd..c9b881923ef1 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -630,7 +630,7 @@ unlock:
return ret;
}
-static struct v4l2_file_operations iris_v4l2_file_ops = {
+static const struct v4l2_file_operations iris_v4l2_file_ops = {
.owner = THIS_MODULE,
.open = iris_open,
.release = iris_close,
diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c
index de7d142316d2..9c103a2e4e4e 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu2.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu2.c
@@ -3,9 +3,15 @@
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
+#include <linux/bits.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
#include "iris_instance.h"
#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size)
{
struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c
index bb98950e018f..515dd55a3377 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -222,12 +222,14 @@ int iris_vpu_power_off_controller(struct iris_core *core)
writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
- writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+ if (!core->iris_platform_data->no_aon) {
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
- ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
- val, val & BIT(0), 200, 2000);
- if (ret)
- goto disable_power;
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+ }
writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
@@ -250,6 +252,7 @@ int iris_vpu_power_off_controller(struct iris_core *core)
writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
disable_power:
+ iris_disable_unprepare_clock(core, IRIS_AHB_CLK);
iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
@@ -261,6 +264,7 @@ void iris_vpu_power_off_hw(struct iris_core *core)
{
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK);
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
}
@@ -294,11 +298,17 @@ int iris_vpu_power_on_controller(struct iris_core *core)
ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
if (ret)
- goto err_disable_clock;
+ goto err_disable_axi_clock;
+
+ ret = iris_prepare_enable_clock(core, IRIS_AHB_CLK);
+ if (ret && ret != -ENOENT)
+ goto err_disable_ctrl_clock;
return 0;
-err_disable_clock:
+err_disable_ctrl_clock:
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+err_disable_axi_clock:
iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
err_disable_power:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
@@ -318,13 +328,19 @@ int iris_vpu_power_on_hw(struct iris_core *core)
if (ret)
goto err_disable_power;
+ ret = iris_prepare_enable_clock(core, IRIS_HW_AHB_CLK);
+ if (ret && ret != -ENOENT)
+ goto err_disable_hw_clock;
+
ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true);
if (ret)
- goto err_disable_clock;
+ goto err_disable_hw_ahb_clock;
return 0;
-err_disable_clock:
+err_disable_hw_ahb_clock:
+ iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK);
+err_disable_hw_clock:
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
err_disable_power:
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index abf959b8f3a6..24d2b2fd0340 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -1146,6 +1146,5 @@ static struct platform_driver qcom_venus_driver = {
};
module_platform_driver(qcom_venus_driver);
-MODULE_ALIAS("platform:qcom-venus");
MODULE_DESCRIPTION("Qualcomm Venus video encoder and decoder driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index af0ac40bec9b..1de7436713ed 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -9,7 +9,6 @@
#include <linux/iommu.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
@@ -83,8 +82,7 @@ static int venus_load_fw(struct venus_core *core, const char *fwname,
phys_addr_t *mem_phys, size_t *mem_size)
{
const struct firmware *mdt;
- struct reserved_mem *rmem;
- struct device_node *node;
+ struct resource res;
struct device *dev;
ssize_t fw_size;
void *mem_va;
@@ -94,15 +92,8 @@ static int venus_load_fw(struct venus_core *core, const char *fwname,
*mem_size = 0;
dev = core->dev;
- node = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (!node) {
- dev_err(dev, "no memory-region specified\n");
- return -EINVAL;
- }
-
- rmem = of_reserved_mem_lookup(node);
- of_node_put(node);
- if (!rmem) {
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ if (ret) {
dev_err(dev, "failed to lookup reserved memory-region\n");
return -EINVAL;
}
@@ -117,8 +108,8 @@ static int venus_load_fw(struct venus_core *core, const char *fwname,
goto err_release_fw;
}
- *mem_phys = rmem->base;
- *mem_size = rmem->size;
+ *mem_phys = res.start;
+ *mem_size = resource_size(&res);
if (*mem_size < fw_size || fw_size > VENUS_FW_MEM_SIZE) {
ret = -EINVAL;
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 55c27345b7d8..4a6641fdffcf 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -329,8 +329,6 @@ static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
struct vb2_queue *q;
q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
- if (!q)
- return -EINVAL;
if (vb2_is_busy(q))
return -EBUSY;
@@ -1778,12 +1776,9 @@ static int vdec_probe(struct platform_device *pdev)
struct venus_core *core;
int ret;
- if (!dev->parent)
- return -EPROBE_DEFER;
-
core = dev_get_drvdata(dev->parent);
if (!core)
- return -EPROBE_DEFER;
+ return -EINVAL;
platform_set_drvdata(pdev, core);
@@ -1882,6 +1877,5 @@ static struct platform_driver qcom_venus_dec_driver = {
};
module_platform_driver(qcom_venus_dec_driver);
-MODULE_ALIAS("platform:qcom-venus-decoder");
MODULE_DESCRIPTION("Qualcomm Venus video decoder driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index fba07557a399..b478b982a80d 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -241,8 +241,6 @@ static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
struct vb2_queue *q;
q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
- if (!q)
- return -EINVAL;
if (vb2_is_busy(q))
return -EBUSY;
@@ -1560,12 +1558,9 @@ static int venc_probe(struct platform_device *pdev)
struct venus_core *core;
int ret;
- if (!dev->parent)
- return -EPROBE_DEFER;
-
core = dev_get_drvdata(dev->parent);
if (!core)
- return -EPROBE_DEFER;
+ return -EINVAL;
platform_set_drvdata(pdev, core);
@@ -1664,6 +1659,5 @@ static struct platform_driver qcom_venus_enc_driver = {
};
module_platform_driver(qcom_venus_enc_driver);
-MODULE_ALIAS("platform:qcom-venus-encoder");
MODULE_DESCRIPTION("Qualcomm Venus video encoder driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig
index 27a54fa79083..bd8247c0b8aa 100644
--- a/drivers/media/platform/renesas/Kconfig
+++ b/drivers/media/platform/renesas/Kconfig
@@ -42,6 +42,7 @@ config VIDEO_SH_VOU
source "drivers/media/platform/renesas/rcar-isp/Kconfig"
source "drivers/media/platform/renesas/rcar-vin/Kconfig"
source "drivers/media/platform/renesas/rzg2l-cru/Kconfig"
+source "drivers/media/platform/renesas/rzv2h-ivc/Kconfig"
# Mem2mem drivers
diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile
index 1127259c09d6..b6b4abf01db2 100644
--- a/drivers/media/platform/renesas/Makefile
+++ b/drivers/media/platform/renesas/Makefile
@@ -6,6 +6,7 @@
obj-y += rcar-isp/
obj-y += rcar-vin/
obj-y += rzg2l-cru/
+obj-y += rzv2h-ivc/
obj-y += vsp1/
obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o
diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c
index 11bf47fb8266..0844934f7aa6 100644
--- a/drivers/media/platform/renesas/rcar_drif.c
+++ b/drivers/media/platform/renesas/rcar_drif.c
@@ -1246,6 +1246,7 @@ static struct device_node *rcar_drif_bond_enabled(struct platform_device *p)
if (np && of_device_is_available(np))
return np;
+ of_node_put(np);
return NULL;
}
diff --git a/drivers/media/platform/renesas/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c
index e615c56083f1..672869815f63 100644
--- a/drivers/media/platform/renesas/rcar_fdp1.c
+++ b/drivers/media/platform/renesas/rcar_fdp1.c
@@ -1409,9 +1409,6 @@ static int fdp1_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct fdp1_ctx *ctx = file_to_ctx(file);
struct fdp1_q_data *q_data;
- if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type))
- return -EINVAL;
-
q_data = get_q_data(ctx, f->type);
f->fmt.pix_mp = q_data->format;
@@ -2302,8 +2299,7 @@ static int fdp1_probe(struct platform_device *pdev)
fdp1->fcp = rcar_fcp_get(fcp_node);
of_node_put(fcp_node);
if (IS_ERR(fdp1->fcp)) {
- dev_dbg(&pdev->dev, "FCP not found (%ld)\n",
- PTR_ERR(fdp1->fcp));
+ dev_dbg(&pdev->dev, "FCP not found (%pe)\n", fdp1->fcp);
return PTR_ERR(fdp1->fcp);
}
}
diff --git a/drivers/media/platform/renesas/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c
index 46ea259a2bb9..a6d26b446494 100644
--- a/drivers/media/platform/renesas/rcar_jpu.c
+++ b/drivers/media/platform/renesas/rcar_jpu.c
@@ -825,9 +825,6 @@ static int jpu_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct jpu_ctx *ctx = file_to_ctx(file);
- if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type))
- return -EINVAL;
-
return __jpu_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type);
}
@@ -841,8 +838,6 @@ static int jpu_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
int ret;
vq = v4l2_m2m_get_vq(m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
if (vb2_is_busy(vq)) {
v4l2_err(&ctx->jpu->v4l2_dev, "%s queue busy\n", __func__);
@@ -866,9 +861,6 @@ static int jpu_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct jpu_ctx *ctx = file_to_ctx(file);
struct jpu_q_data *q_data;
- if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type))
- return -EINVAL;
-
q_data = jpu_get_q_data(ctx, f->type);
f->fmt.pix_mp = q_data->format;
@@ -1701,7 +1693,6 @@ static void jpu_remove(struct platform_device *pdev)
v4l2_device_unregister(&jpu->v4l2_dev);
}
-#ifdef CONFIG_PM_SLEEP
static int jpu_suspend(struct device *dev)
{
struct jpu *jpu = dev_get_drvdata(dev);
@@ -1725,11 +1716,8 @@ static int jpu_resume(struct device *dev)
return 0;
}
-#endif
-static const struct dev_pm_ops jpu_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(jpu_suspend, jpu_resume)
-};
+static DEFINE_SIMPLE_DEV_PM_OPS(jpu_pm_ops, jpu_suspend, jpu_resume);
static struct platform_driver jpu_driver = {
.probe = jpu_probe,
@@ -1737,7 +1725,7 @@ static struct platform_driver jpu_driver = {
.driver = {
.of_match_table = jpu_dt_ids,
.name = DRV_NAME,
- .pm = &jpu_pm_ops,
+ .pm = pm_sleep_ptr(&jpu_pm_ops),
},
};
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
index 1520211e7418..0fbdae280fdc 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
@@ -303,8 +303,8 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2)
remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]);
if (IS_ERR(remote_pad)) {
- dev_err(csi2->dev, "can't get source pad of %s (%ld)\n",
- csi2->remote_source->name, PTR_ERR(remote_pad));
+ dev_err(csi2->dev, "can't get source pad of %s (%pe)\n",
+ csi2->remote_source->name, remote_pad);
return PTR_ERR(remote_pad);
}
@@ -722,8 +722,8 @@ static int rzg2l_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]);
if (IS_ERR(remote_pad)) {
- dev_err(csi2->dev, "can't get source pad of %s (%ld)\n",
- csi2->remote_source->name, PTR_ERR(remote_pad));
+ dev_err(csi2->dev, "can't get source pad of %s (%pe)\n",
+ csi2->remote_source->name, remote_pad);
return PTR_ERR(remote_pad);
}
return v4l2_subdev_call(csi2->remote_source, pad, get_frame_desc,
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/Kconfig b/drivers/media/platform/renesas/rzv2h-ivc/Kconfig
new file mode 100644
index 000000000000..eb6c6ce1caba
--- /dev/null
+++ b/drivers/media/platform/renesas/rzv2h-ivc/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_RZV2H_IVC
+ tristate "Renesas RZ/V2H(P) Input Video Control block driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on OF
+ depends on PM
+ select VIDEOBUF2_DMA_CONTIG
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ Support for the Renesas RZ/V2H(P) Input Video Control Block
+ (IVC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called rzv2h-ivc.
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/Makefile b/drivers/media/platform/renesas/rzv2h-ivc/Makefile
new file mode 100644
index 000000000000..080ee3570f09
--- /dev/null
+++ b/drivers/media/platform/renesas/rzv2h-ivc/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+rzv2h-ivc-y := rzv2h-ivc-dev.o rzv2h-ivc-subdev.o rzv2h-ivc-video.o
+
+obj-$(CONFIG_VIDEO_RZV2H_IVC) += rzv2h-ivc.o
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c
new file mode 100644
index 000000000000..e9857eb5b51a
--- /dev/null
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-dev.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/V2H(P) Input Video Control Block driver
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include "rzv2h-ivc.h"
+
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+void rzv2h_ivc_write(struct rzv2h_ivc *ivc, u32 addr, u32 val)
+{
+ writel(val, ivc->base + addr);
+}
+
+void rzv2h_ivc_update_bits(struct rzv2h_ivc *ivc, unsigned int addr,
+ u32 mask, u32 val)
+{
+ u32 orig, new;
+
+ orig = readl(ivc->base + addr);
+
+ new = orig & ~mask;
+ new |= val & mask;
+
+ if (new != orig)
+ writel(new, ivc->base + addr);
+}
+
+static int rzv2h_ivc_get_hardware_resources(struct rzv2h_ivc *ivc,
+ struct platform_device *pdev)
+{
+ static const char * const resource_names[RZV2H_IVC_NUM_HW_RESOURCES] = {
+ "reg",
+ "axi",
+ "isp",
+ };
+ struct resource *res;
+ int ret;
+
+ ivc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(ivc->base))
+ return dev_err_probe(ivc->dev, PTR_ERR(ivc->base),
+ "failed to map IO memory\n");
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(resource_names); i++)
+ ivc->clks[i].id = resource_names[i];
+
+ ret = devm_clk_bulk_get(ivc->dev, ARRAY_SIZE(resource_names), ivc->clks);
+ if (ret)
+ return dev_err_probe(ivc->dev, ret, "failed to acquire clks\n");
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(resource_names); i++)
+ ivc->resets[i].id = resource_names[i];
+
+ ret = devm_reset_control_bulk_get_optional_shared(ivc->dev,
+ ARRAY_SIZE(resource_names),
+ ivc->resets);
+ if (ret)
+ return dev_err_probe(ivc->dev, ret, "failed to acquire resets\n");
+
+ return 0;
+}
+
+static void rzv2h_ivc_global_config(struct rzv2h_ivc *ivc)
+{
+ /* Currently we only support single-exposure input */
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_PLNUM, RZV2H_IVC_ONE_EXPOSURE);
+
+ /*
+ * Datasheet says we should disable the interrupts before changing mode
+ * to avoid spurious IFP interrupt.
+ */
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_INT_EN, 0x0);
+
+ /*
+ * RZ/V2H(P) documentation says software controlled single context mode
+ * is not supported, and currently the driver does not support the
+ * multi-context mode. That being so we just set single context sw-hw
+ * mode.
+ */
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_CONTEXT,
+ RZV2H_IVC_SINGLE_CONTEXT_SW_HW_CFG);
+
+ /*
+ * We enable the frame end interrupt so that we know when we should send
+ * follow-up frames.
+ */
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_INT_EN, RZV2H_IVC_VVAL_IFPE);
+}
+
+static irqreturn_t rzv2h_ivc_isr(int irq, void *context)
+{
+ struct device *dev = context;
+ struct rzv2h_ivc *ivc = dev_get_drvdata(dev);
+
+ guard(spinlock)(&ivc->spinlock);
+
+ /* IRQ should never be triggered before vvalid_ifp has been reset to 2 */
+ if (WARN_ON(!ivc->vvalid_ifp))
+ return IRQ_HANDLED;
+
+ /*
+ * The first interrupt indicates that the buffer transfer has been
+ * completed.
+ */
+ if (--ivc->vvalid_ifp) {
+ rzv2h_ivc_buffer_done(ivc);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * The second interrupt indicates that the post-frame transfer VBLANK
+ * has completed, we can now schedule a new frame transfer, if any.
+ */
+ queue_work(ivc->buffers.async_wq, &ivc->buffers.work);
+
+ return IRQ_HANDLED;
+}
+
+static int rzv2h_ivc_runtime_resume(struct device *dev)
+{
+ struct rzv2h_ivc *ivc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(ivc->clks), ivc->clks);
+ if (ret) {
+ dev_err(ivc->dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ ret = reset_control_bulk_deassert(ARRAY_SIZE(ivc->resets), ivc->resets);
+ if (ret) {
+ dev_err(ivc->dev, "failed to deassert resets\n");
+ goto err_disable_clks;
+ }
+
+ rzv2h_ivc_global_config(ivc);
+
+ ret = request_irq(ivc->irqnum, rzv2h_ivc_isr, 0, dev_driver_string(dev),
+ dev);
+ if (ret) {
+ dev_err(dev, "failed to request irq\n");
+ goto err_assert_resets;
+ }
+
+ return 0;
+
+err_assert_resets:
+ reset_control_bulk_assert(ARRAY_SIZE(ivc->resets), ivc->resets);
+err_disable_clks:
+ clk_bulk_disable_unprepare(ARRAY_SIZE(ivc->clks), ivc->clks);
+
+ return ret;
+}
+
+static int rzv2h_ivc_runtime_suspend(struct device *dev)
+{
+ struct rzv2h_ivc *ivc = dev_get_drvdata(dev);
+
+ reset_control_bulk_assert(ARRAY_SIZE(ivc->resets), ivc->resets);
+ clk_bulk_disable_unprepare(ARRAY_SIZE(ivc->clks), ivc->clks);
+ free_irq(ivc->irqnum, dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rzv2h_ivc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(rzv2h_ivc_runtime_suspend, rzv2h_ivc_runtime_resume,
+ NULL)
+};
+
+static int rzv2h_ivc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rzv2h_ivc *ivc;
+ int ret;
+
+ ivc = devm_kzalloc(dev, sizeof(*ivc), GFP_KERNEL);
+ if (!ivc)
+ return -ENOMEM;
+
+ ivc->dev = dev;
+ platform_set_drvdata(pdev, ivc);
+
+ ret = devm_mutex_init(dev, &ivc->lock);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&ivc->spinlock);
+
+ ret = rzv2h_ivc_get_hardware_resources(ivc, pdev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 2000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ivc->irqnum = platform_get_irq(pdev, 0);
+ if (ivc->irqnum < 0)
+ return ivc->irqnum;
+
+ ret = rzv2h_ivc_initialise_subdevice(ivc);
+ if (ret)
+ goto err_disable_pm_runtime;
+
+ return 0;
+
+err_disable_pm_runtime:
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static void rzv2h_ivc_remove(struct platform_device *pdev)
+{
+ struct rzv2h_ivc *ivc = platform_get_drvdata(pdev);
+
+ rzv2h_deinit_video_dev_and_queue(ivc);
+ rzv2h_ivc_deinit_subdevice(ivc);
+}
+
+static const struct of_device_id rzv2h_ivc_of_match[] = {
+ { .compatible = "renesas,r9a09g057-ivc", },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rzv2h_ivc_of_match);
+
+static struct platform_driver rzv2h_ivc_driver = {
+ .driver = {
+ .name = "rzv2h-ivc",
+ .of_match_table = rzv2h_ivc_of_match,
+ .pm = &rzv2h_ivc_pm_ops,
+ },
+ .probe = rzv2h_ivc_probe,
+ .remove = rzv2h_ivc_remove,
+};
+
+module_platform_driver(rzv2h_ivc_driver);
+
+MODULE_AUTHOR("Daniel Scally <dan.scally@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas RZ/V2H(P) Input Video Control Block driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c
new file mode 100644
index 000000000000..b1659544eaa0
--- /dev/null
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/V2H(P) Input Video Control Block driver
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include "rzv2h-ivc.h"
+
+#include <linux/media.h>
+#include <linux/media-bus-format.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+
+#define RZV2H_IVC_N_INPUTS_PER_OUTPUT 6
+
+/*
+ * We support 8/10/12/14/16/20 bit input in any bayer order, but the output
+ * format is fixed at 20-bits with the same order as the input.
+ */
+static const struct {
+ u32 inputs[RZV2H_IVC_N_INPUTS_PER_OUTPUT];
+ u32 output;
+} rzv2h_ivc_formats[] = {
+ {
+ .inputs = {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MEDIA_BUS_FMT_SBGGR16_1X16,
+ MEDIA_BUS_FMT_SBGGR20_1X20,
+ },
+ .output = MEDIA_BUS_FMT_SBGGR20_1X20
+ },
+ {
+ .inputs = {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MEDIA_BUS_FMT_SGBRG16_1X16,
+ MEDIA_BUS_FMT_SGBRG20_1X20,
+ },
+ .output = MEDIA_BUS_FMT_SGBRG20_1X20
+ },
+ {
+ .inputs = {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MEDIA_BUS_FMT_SGRBG16_1X16,
+ MEDIA_BUS_FMT_SGRBG20_1X20,
+ },
+ .output = MEDIA_BUS_FMT_SGRBG20_1X20
+ },
+ {
+ .inputs = {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ MEDIA_BUS_FMT_SRGGB16_1X16,
+ MEDIA_BUS_FMT_SRGGB20_1X20,
+ },
+ .output = MEDIA_BUS_FMT_SRGGB20_1X20
+ },
+};
+
+static u32 rzv2h_ivc_get_mbus_output_from_input(u32 mbus_code)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(rzv2h_ivc_formats); i++) {
+ for (j = 0; j < RZV2H_IVC_N_INPUTS_PER_OUTPUT; j++) {
+ if (rzv2h_ivc_formats[i].inputs[j] == mbus_code)
+ return rzv2h_ivc_formats[i].output;
+ }
+ }
+
+ return 0;
+}
+
+static int rzv2h_ivc_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct v4l2_mbus_framefmt *fmt;
+ unsigned int order_index;
+ unsigned int index;
+
+ /*
+ * On the source pad, only the 20-bit format corresponding to the sink
+ * pad format's bayer order is supported.
+ */
+ if (code->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) {
+ if (code->index)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(state,
+ RZV2H_IVC_SUBDEV_SINK_PAD);
+ code->code = rzv2h_ivc_get_mbus_output_from_input(fmt->code);
+
+ return 0;
+ }
+
+ if (code->index >= ARRAY_SIZE(rzv2h_ivc_formats) *
+ RZV2H_IVC_N_INPUTS_PER_OUTPUT)
+ return -EINVAL;
+
+ order_index = code->index / RZV2H_IVC_N_INPUTS_PER_OUTPUT;
+ index = code->index % RZV2H_IVC_N_INPUTS_PER_OUTPUT;
+
+ code->code = rzv2h_ivc_formats[order_index].inputs[index];
+
+ return 0;
+}
+
+static int rzv2h_ivc_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct v4l2_mbus_framefmt *fmt;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ if (fse->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD) {
+ fmt = v4l2_subdev_state_get_format(state,
+ RZV2H_IVC_SUBDEV_SINK_PAD);
+
+ if (fse->code != rzv2h_ivc_get_mbus_output_from_input(fmt->code))
+ return -EINVAL;
+
+ fse->min_width = fmt->width;
+ fse->max_width = fmt->width;
+ fse->min_height = fmt->height;
+ fse->max_height = fmt->height;
+
+ return 0;
+ }
+
+ if (!rzv2h_ivc_get_mbus_output_from_input(fse->code))
+ return -EINVAL;
+
+ fse->min_width = RZV2H_IVC_MIN_WIDTH;
+ fse->max_width = RZV2H_IVC_MAX_WIDTH;
+ fse->min_height = RZV2H_IVC_MIN_HEIGHT;
+ fse->max_height = RZV2H_IVC_MAX_HEIGHT;
+
+ return 0;
+}
+
+static int rzv2h_ivc_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt = &format->format;
+ struct v4l2_mbus_framefmt *src_fmt, *sink_fmt;
+
+ if (format->pad == RZV2H_IVC_SUBDEV_SOURCE_PAD)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ RZV2H_IVC_SUBDEV_SINK_PAD);
+
+ sink_fmt->code = rzv2h_ivc_get_mbus_output_from_input(fmt->code) ?
+ fmt->code : rzv2h_ivc_formats[0].inputs[0];
+
+ sink_fmt->width = clamp(fmt->width, RZV2H_IVC_MIN_WIDTH,
+ RZV2H_IVC_MAX_WIDTH);
+ sink_fmt->height = clamp(fmt->height, RZV2H_IVC_MIN_HEIGHT,
+ RZV2H_IVC_MAX_HEIGHT);
+
+ *fmt = *sink_fmt;
+
+ src_fmt = v4l2_subdev_state_get_format(state,
+ RZV2H_IVC_SUBDEV_SOURCE_PAD);
+ *src_fmt = *sink_fmt;
+ src_fmt->code = rzv2h_ivc_get_mbus_output_from_input(sink_fmt->code);
+
+ return 0;
+}
+
+static int rzv2h_ivc_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ /*
+ * We have a single source pad, which has a single stream. V4L2 core has
+ * already validated those things. The actual power-on and programming
+ * of registers will be done through the video device's .vidioc_streamon
+ * so there's nothing to actually do here...
+ */
+
+ return 0;
+}
+
+static int rzv2h_ivc_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops rzv2h_ivc_pad_ops = {
+ .enum_mbus_code = rzv2h_ivc_enum_mbus_code,
+ .enum_frame_size = rzv2h_ivc_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = rzv2h_ivc_set_fmt,
+ .enable_streams = rzv2h_ivc_enable_streams,
+ .disable_streams = rzv2h_ivc_disable_streams,
+};
+
+static const struct v4l2_subdev_core_ops rzv2h_ivc_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops rzv2h_ivc_subdev_ops = {
+ .core = &rzv2h_ivc_core_ops,
+ .pad = &rzv2h_ivc_pad_ops,
+};
+
+static int rzv2h_ivc_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ RZV2H_IVC_SUBDEV_SINK_PAD);
+ sink_fmt->width = RZV2H_IVC_DEFAULT_WIDTH;
+ sink_fmt->height = RZV2H_IVC_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(sink_fmt->colorspace);
+ sink_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(sink_fmt->colorspace);
+ sink_fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
+ sink_fmt->colorspace,
+ sink_fmt->ycbcr_enc);
+
+ src_fmt = v4l2_subdev_state_get_format(state,
+ RZV2H_IVC_SUBDEV_SOURCE_PAD);
+
+ *src_fmt = *sink_fmt;
+ src_fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
+
+ return 0;
+}
+
+static int rzv2h_ivc_registered(struct v4l2_subdev *sd)
+{
+ struct rzv2h_ivc *ivc = container_of(sd, struct rzv2h_ivc, subdev.sd);
+
+ return rzv2h_ivc_init_vdev(ivc, sd->v4l2_dev);
+}
+
+static const struct v4l2_subdev_internal_ops rzv2h_ivc_subdev_internal_ops = {
+ .init_state = rzv2h_ivc_init_state,
+ .registered = rzv2h_ivc_registered,
+};
+
+static int rzv2h_ivc_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->source->entity);
+ struct rzv2h_ivc *ivc = video_get_drvdata(vdev);
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(link->sink->entity);
+ const struct rzv2h_ivc_format *fmt;
+ const struct v4l2_pix_format_mplane *pix;
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *mf;
+ unsigned int i;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ mf = v4l2_subdev_state_get_format(state, link->sink->index);
+
+ pix = &ivc->format.pix;
+ fmt = ivc->format.fmt;
+
+ if (mf->width != pix->width || mf->height != pix->height) {
+ dev_dbg(ivc->dev,
+ "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ mf->width, mf->height, pix->width, pix->height);
+ ret = -EPIPE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(fmt->mbus_codes); i++)
+ if (mf->code == fmt->mbus_codes[i])
+ break;
+
+ if (i == ARRAY_SIZE(fmt->mbus_codes)) {
+ dev_dbg(ivc->dev,
+ "link '%s':%u -> '%s':%u not valid: pixel format %p4cc cannot produce mbus_code 0x%04x\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ &pix->pixelformat, mf->code);
+ ret = -EPIPE;
+ }
+
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static const struct media_entity_operations rzv2h_ivc_media_ops = {
+ .link_validate = rzv2h_ivc_link_validate,
+};
+
+int rzv2h_ivc_initialise_subdevice(struct rzv2h_ivc *ivc)
+{
+ struct v4l2_subdev *sd;
+ int ret;
+
+ /* Initialise subdevice */
+ sd = &ivc->subdev.sd;
+ sd->dev = ivc->dev;
+ v4l2_subdev_init(sd, &rzv2h_ivc_subdev_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->internal_ops = &rzv2h_ivc_subdev_internal_ops;
+ sd->entity.ops = &rzv2h_ivc_media_ops;
+
+ ivc->subdev.pads[RZV2H_IVC_SUBDEV_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
+ ivc->subdev.pads[RZV2H_IVC_SUBDEV_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
+
+ snprintf(sd->name, sizeof(sd->name), "rzv2h ivc block");
+
+ ret = media_entity_pads_init(&sd->entity, RZV2H_IVC_NUM_SUBDEV_PADS,
+ ivc->subdev.pads);
+ if (ret) {
+ dev_err(ivc->dev, "failed to initialise media entity\n");
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ dev_err(ivc->dev, "failed to finalize subdev init\n");
+ goto err_cleanup_subdev_entity;
+ }
+
+ ret = v4l2_async_register_subdev(sd);
+ if (ret) {
+ dev_err(ivc->dev, "failed to register subdevice\n");
+ goto err_cleanup_subdev;
+ }
+
+ return 0;
+
+err_cleanup_subdev:
+ v4l2_subdev_cleanup(sd);
+err_cleanup_subdev_entity:
+ media_entity_cleanup(&sd->entity);
+
+ return ret;
+}
+
+void rzv2h_ivc_deinit_subdevice(struct rzv2h_ivc *ivc)
+{
+ struct v4l2_subdev *sd = &ivc->subdev.sd;
+
+ v4l2_subdev_cleanup(sd);
+ media_entity_remove_links(&sd->entity);
+ v4l2_async_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+}
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
new file mode 100644
index 000000000000..799453250b85
--- /dev/null
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
@@ -0,0 +1,531 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas RZ/V2H(P) Input Video Control Block driver
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include "rzv2h-ivc.h"
+
+#include <linux/cleanup.h>
+#include <linux/iopoll.h>
+#include <linux/lockdep.h>
+#include <linux/media-bus-format.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#define RZV2H_IVC_FIXED_HBLANK 0x20
+#define RZV2H_IVC_MIN_VBLANK(hts) max(0x1b, 15 + (120501 / (hts)))
+
+struct rzv2h_ivc_buf {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ dma_addr_t addr;
+};
+
+#define to_rzv2h_ivc_buf(vbuf) \
+ container_of(vbuf, struct rzv2h_ivc_buf, vb)
+
+static const struct rzv2h_ivc_format rzv2h_ivc_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ },
+ .dtype = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ },
+ .dtype = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ },
+ .dtype = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ },
+ .dtype = MIPI_CSI2_DT_RAW8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RAW_CRU10,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10
+ },
+ .dtype = MIPI_CSI2_DT_RAW10,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RAW_CRU12,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12
+ },
+ .dtype = MIPI_CSI2_DT_RAW12,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RAW_CRU14,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MEDIA_BUS_FMT_SRGGB14_1X14
+ },
+ .dtype = MIPI_CSI2_DT_RAW14,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SBGGR16_1X16,
+ },
+ .dtype = MIPI_CSI2_DT_RAW16,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SGBRG16_1X16,
+ },
+ .dtype = MIPI_CSI2_DT_RAW16,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SGRBG16_1X16,
+ },
+ .dtype = MIPI_CSI2_DT_RAW16,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+ .mbus_codes = {
+ MEDIA_BUS_FMT_SRGGB16_1X16,
+ },
+ .dtype = MIPI_CSI2_DT_RAW16,
+ },
+};
+
+void rzv2h_ivc_buffer_done(struct rzv2h_ivc *ivc)
+{
+ struct rzv2h_ivc_buf *buf;
+
+ lockdep_assert_in_irq();
+
+ scoped_guard(spinlock, &ivc->buffers.lock) {
+ if (!ivc->buffers.curr)
+ return;
+
+ buf = ivc->buffers.curr;
+ ivc->buffers.curr = NULL;
+ }
+
+ buf->vb.sequence = ivc->buffers.sequence++;
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void rzv2h_ivc_transfer_buffer(struct work_struct *work)
+{
+ struct rzv2h_ivc *ivc = container_of(work, struct rzv2h_ivc,
+ buffers.work);
+ struct rzv2h_ivc_buf *buf;
+
+ /* Setup buffers */
+ scoped_guard(spinlock_irqsave, &ivc->buffers.lock) {
+ buf = list_first_entry_or_null(&ivc->buffers.queue,
+ struct rzv2h_ivc_buf, queue);
+ }
+
+ if (!buf)
+ return;
+
+ list_del(&buf->queue);
+
+ ivc->buffers.curr = buf;
+ buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_SADDL_P0, buf->addr);
+
+ scoped_guard(spinlock_irqsave, &ivc->spinlock) {
+ ivc->vvalid_ifp = 2;
+ }
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_FRCON, 0x1);
+}
+
+static int rzv2h_ivc_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rzv2h_ivc *ivc = vb2_get_drv_priv(q);
+
+ if (*num_planes && *num_planes > 1)
+ return -EINVAL;
+
+ if (sizes[0] && sizes[0] < ivc->format.pix.plane_fmt[0].sizeimage)
+ return -EINVAL;
+
+ *num_planes = 1;
+
+ if (!sizes[0])
+ sizes[0] = ivc->format.pix.plane_fmt[0].sizeimage;
+
+ return 0;
+}
+
+static void rzv2h_ivc_buf_queue(struct vb2_buffer *vb)
+{
+ struct rzv2h_ivc *ivc = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rzv2h_ivc_buf *buf = to_rzv2h_ivc_buf(vbuf);
+
+ scoped_guard(spinlock_irq, &ivc->buffers.lock) {
+ list_add_tail(&buf->queue, &ivc->buffers.queue);
+ }
+
+ scoped_guard(spinlock_irq, &ivc->spinlock) {
+ if (vb2_is_streaming(vb->vb2_queue) && !ivc->vvalid_ifp)
+ queue_work(ivc->buffers.async_wq, &ivc->buffers.work);
+ }
+}
+
+static void rzv2h_ivc_format_configure(struct rzv2h_ivc *ivc)
+{
+ const struct rzv2h_ivc_format *fmt = ivc->format.fmt;
+ struct v4l2_pix_format_mplane *pix = &ivc->format.pix;
+ unsigned int vblank;
+ unsigned int hts;
+
+ /* Currently only CRU packed pixel formats are supported */
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_PXFMT,
+ RZV2H_IVC_INPUT_FMT_CRU_PACKED);
+
+ rzv2h_ivc_update_bits(ivc, RZV2H_IVC_REG_AXIRX_PXFMT,
+ RZV2H_IVC_PXFMT_DTYPE, fmt->dtype);
+
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_HSIZE, pix->width);
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_VSIZE, pix->height);
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_STRD,
+ pix->plane_fmt[0].bytesperline);
+
+ /*
+ * The ISP has minimum vertical blanking requirements that must be
+ * adhered to by the IVC. The minimum is a function of the Iridix blocks
+ * clocking requirements and the width of the image and horizontal
+ * blanking, but if we assume the worst case then it boils down to the
+ * below (plus one to the numerator to ensure the answer is rounded up)
+ */
+
+ hts = pix->width + RZV2H_IVC_FIXED_HBLANK;
+ vblank = RZV2H_IVC_MIN_VBLANK(hts);
+
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_AXIRX_BLANK,
+ RZV2H_IVC_VBLANK(vblank));
+}
+
+static void rzv2h_ivc_return_buffers(struct rzv2h_ivc *ivc,
+ enum vb2_buffer_state state)
+{
+ struct rzv2h_ivc_buf *buf, *tmp;
+
+ guard(spinlock_irqsave)(&ivc->buffers.lock);
+
+ if (ivc->buffers.curr) {
+ vb2_buffer_done(&ivc->buffers.curr->vb.vb2_buf, state);
+ ivc->buffers.curr = NULL;
+ }
+
+ list_for_each_entry_safe(buf, tmp, &ivc->buffers.queue, queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+}
+
+static int rzv2h_ivc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct rzv2h_ivc *ivc = vb2_get_drv_priv(q);
+ int ret;
+
+ ivc->buffers.sequence = 0;
+ ivc->vvalid_ifp = 0;
+
+ ret = pm_runtime_resume_and_get(ivc->dev);
+ if (ret)
+ goto err_return_buffers;
+
+ ret = video_device_pipeline_alloc_start(&ivc->vdev.dev);
+ if (ret) {
+ dev_err(ivc->dev, "failed to start media pipeline\n");
+ goto err_pm_runtime_put;
+ }
+
+ rzv2h_ivc_format_configure(ivc);
+
+ queue_work(ivc->buffers.async_wq, &ivc->buffers.work);
+
+ return 0;
+
+err_pm_runtime_put:
+ pm_runtime_put(ivc->dev);
+err_return_buffers:
+ rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void rzv2h_ivc_stop_streaming(struct vb2_queue *q)
+{
+ struct rzv2h_ivc *ivc = vb2_get_drv_priv(q);
+ u32 val = 0;
+
+ rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, 0x1);
+ readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP,
+ val, !val, 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
+
+ rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR);
+ video_device_pipeline_stop(&ivc->vdev.dev);
+ pm_runtime_put_autosuspend(ivc->dev);
+}
+
+static const struct vb2_ops rzv2h_ivc_vb2_ops = {
+ .queue_setup = &rzv2h_ivc_queue_setup,
+ .buf_queue = &rzv2h_ivc_buf_queue,
+ .start_streaming = &rzv2h_ivc_start_streaming,
+ .stop_streaming = &rzv2h_ivc_stop_streaming,
+};
+
+static const struct rzv2h_ivc_format *
+rzv2h_ivc_format_from_pixelformat(u32 fourcc)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(rzv2h_ivc_formats); i++)
+ if (fourcc == rzv2h_ivc_formats[i].fourcc)
+ return &rzv2h_ivc_formats[i];
+
+ return &rzv2h_ivc_formats[0];
+}
+
+static int rzv2h_ivc_enum_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(rzv2h_ivc_formats))
+ return -EINVAL;
+
+ f->pixelformat = rzv2h_ivc_formats[f->index].fourcc;
+ return 0;
+}
+
+static int rzv2h_ivc_g_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rzv2h_ivc *ivc = video_drvdata(file);
+
+ f->fmt.pix_mp = ivc->format.pix;
+
+ return 0;
+}
+
+static void rzv2h_ivc_try_fmt(struct v4l2_pix_format_mplane *pix,
+ const struct rzv2h_ivc_format *fmt)
+{
+ pix->pixelformat = fmt->fourcc;
+
+ pix->width = clamp(pix->width, RZV2H_IVC_MIN_WIDTH,
+ RZV2H_IVC_MAX_WIDTH);
+ pix->height = clamp(pix->height, RZV2H_IVC_MIN_HEIGHT,
+ RZV2H_IVC_MAX_HEIGHT);
+
+ pix->field = V4L2_FIELD_NONE;
+ pix->colorspace = V4L2_COLORSPACE_RAW;
+ pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace);
+ pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true,
+ pix->colorspace,
+ pix->ycbcr_enc);
+
+ v4l2_fill_pixfmt_mp(pix, pix->pixelformat, pix->width, pix->height);
+}
+
+static void rzv2h_ivc_set_format(struct rzv2h_ivc *ivc,
+ struct v4l2_pix_format_mplane *pix)
+{
+ const struct rzv2h_ivc_format *fmt;
+
+ fmt = rzv2h_ivc_format_from_pixelformat(pix->pixelformat);
+
+ rzv2h_ivc_try_fmt(pix, fmt);
+ ivc->format.pix = *pix;
+ ivc->format.fmt = fmt;
+}
+
+static int rzv2h_ivc_s_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rzv2h_ivc *ivc = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+
+ if (vb2_is_busy(&ivc->vdev.vb2q))
+ return -EBUSY;
+
+ rzv2h_ivc_set_format(ivc, pix);
+
+ return 0;
+}
+
+static int rzv2h_ivc_try_fmt_vid_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ const struct rzv2h_ivc_format *fmt;
+
+ fmt = rzv2h_ivc_format_from_pixelformat(f->fmt.pix.pixelformat);
+ rzv2h_ivc_try_fmt(&f->fmt.pix_mp, fmt);
+
+ return 0;
+}
+
+static int rzv2h_ivc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, "rzv2h-ivc", sizeof(cap->driver));
+ strscpy(cap->card, "Renesas Input Video Control", sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rzv2h_ivc_v4l2_ioctl_ops = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_vid_out = rzv2h_ivc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out_mplane = rzv2h_ivc_g_fmt_vid_out,
+ .vidioc_s_fmt_vid_out_mplane = rzv2h_ivc_s_fmt_vid_out,
+ .vidioc_try_fmt_vid_out_mplane = rzv2h_ivc_try_fmt_vid_out,
+ .vidioc_querycap = rzv2h_ivc_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations rzv2h_ivc_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_pix_format_mplane pix = { };
+ struct video_device *vdev;
+ struct vb2_queue *vb2q;
+ int ret;
+
+ spin_lock_init(&ivc->buffers.lock);
+ INIT_LIST_HEAD(&ivc->buffers.queue);
+ INIT_WORK(&ivc->buffers.work, rzv2h_ivc_transfer_buffer);
+
+ ivc->buffers.async_wq = alloc_workqueue("rzv2h-ivc", 0, 0);
+ if (!ivc->buffers.async_wq)
+ return -EINVAL;
+
+ /* Initialise vb2 queue */
+ vb2q = &ivc->vdev.vb2q;
+ vb2q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ vb2q->io_modes = VB2_MMAP | VB2_DMABUF;
+ vb2q->drv_priv = ivc;
+ vb2q->mem_ops = &vb2_dma_contig_memops;
+ vb2q->ops = &rzv2h_ivc_vb2_ops;
+ vb2q->buf_struct_size = sizeof(struct rzv2h_ivc_buf);
+ vb2q->min_queued_buffers = 0;
+ vb2q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2q->lock = &ivc->lock;
+ vb2q->dev = ivc->dev;
+
+ ret = vb2_queue_init(vb2q);
+ if (ret) {
+ dev_err(ivc->dev, "vb2 queue init failed\n");
+ goto err_destroy_workqueue;
+ }
+
+ /* Initialise Video Device */
+ vdev = &ivc->vdev.dev;
+ strscpy(vdev->name, "rzv2h-ivc", sizeof(vdev->name));
+ vdev->release = video_device_release_empty;
+ vdev->fops = &rzv2h_ivc_v4l2_fops;
+ vdev->ioctl_ops = &rzv2h_ivc_v4l2_ioctl_ops;
+ vdev->lock = &ivc->lock;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->queue = vb2q;
+ vdev->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_TX;
+ video_set_drvdata(vdev, ivc);
+
+ pix.pixelformat = V4L2_PIX_FMT_SRGGB16;
+ pix.width = RZV2H_IVC_DEFAULT_WIDTH;
+ pix.height = RZV2H_IVC_DEFAULT_HEIGHT;
+ rzv2h_ivc_set_format(ivc, &pix);
+
+ ivc->vdev.pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&ivc->vdev.dev.entity, 1, &ivc->vdev.pad);
+ if (ret)
+ goto err_release_vb2q;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(ivc->dev, "failed to register IVC video device\n");
+ goto err_cleanup_vdev_entity;
+ }
+
+ ret = media_create_pad_link(&vdev->entity, 0, &ivc->subdev.sd.entity,
+ RZV2H_IVC_SUBDEV_SINK_PAD,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err(ivc->dev, "failed to create media link\n");
+ goto err_unregister_vdev;
+ }
+
+ return 0;
+
+err_unregister_vdev:
+ video_unregister_device(vdev);
+err_cleanup_vdev_entity:
+ media_entity_cleanup(&vdev->entity);
+err_release_vb2q:
+ vb2_queue_release(vb2q);
+err_destroy_workqueue:
+ destroy_workqueue(ivc->buffers.async_wq);
+
+ return ret;
+}
+
+void rzv2h_deinit_video_dev_and_queue(struct rzv2h_ivc *ivc)
+{
+ struct video_device *vdev = &ivc->vdev.dev;
+ struct vb2_queue *vb2q = &ivc->vdev.vb2q;
+
+ vb2_video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ vb2_queue_release(vb2q);
+}
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h
new file mode 100644
index 000000000000..3bcaab990b0f
--- /dev/null
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Renesas RZ/V2H(P) Input Video Control Block driver
+ *
+ * Copyright (C) 2025 Ideas on Board Oy
+ */
+
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#define RZV2H_IVC_REG_AXIRX_PLNUM 0x0000
+#define RZV2H_IVC_ONE_EXPOSURE 0x00
+#define RZV2H_IVC_TWO_EXPOSURE 0x01
+#define RZV2H_IVC_REG_AXIRX_PXFMT 0x0004
+#define RZV2H_IVC_INPUT_FMT_MIPI (0 << 16)
+#define RZV2H_IVC_INPUT_FMT_CRU_PACKED BIT(16)
+#define RZV2H_IVC_PXFMT_DTYPE GENMASK(7, 0)
+#define RZV2H_IVC_REG_AXIRX_SADDL_P0 0x0010
+#define RZV2H_IVC_REG_AXIRX_SADDH_P0 0x0014
+#define RZV2H_IVC_REG_AXIRX_SADDL_P1 0x0018
+#define RZV2H_IVC_REG_AXIRX_SADDH_P1 0x001c
+#define RZV2H_IVC_REG_AXIRX_HSIZE 0x0020
+#define RZV2H_IVC_REG_AXIRX_VSIZE 0x0024
+#define RZV2H_IVC_REG_AXIRX_BLANK 0x0028
+#define RZV2H_IVC_VBLANK(x) ((x) << 16)
+#define RZV2H_IVC_REG_AXIRX_STRD 0x0030
+#define RZV2H_IVC_REG_AXIRX_ISSU 0x0040
+#define RZV2H_IVC_REG_AXIRX_ERACT 0x0048
+#define RZV2H_IVC_REG_FM_CONTEXT 0x0100
+#define RZV2H_IVC_SOFTWARE_CFG 0x00
+#define RZV2H_IVC_SINGLE_CONTEXT_SW_HW_CFG BIT(0)
+#define RZV2H_IVC_MULTI_CONTEXT_SW_HW_CFG BIT(1)
+#define RZV2H_IVC_REG_FM_MCON 0x0104
+#define RZV2H_IVC_REG_FM_FRCON 0x0108
+#define RZV2H_IVC_REG_FM_STOP 0x010c
+#define RZV2H_IVC_REG_FM_INT_EN 0x0120
+#define RZV2H_IVC_VVAL_IFPE BIT(0)
+#define RZV2H_IVC_REG_FM_INT_STA 0x0124
+#define RZV2H_IVC_REG_AXIRX_FIFOCAP0 0x0208
+#define RZV2H_IVC_REG_CORE_CAPCON 0x020c
+#define RZV2H_IVC_REG_CORE_FIFOCAP0 0x0228
+#define RZV2H_IVC_REG_CORE_FIFOCAP1 0x022c
+
+#define RZV2H_IVC_MIN_WIDTH 640
+#define RZV2H_IVC_MAX_WIDTH 4096
+#define RZV2H_IVC_MIN_HEIGHT 480
+#define RZV2H_IVC_MAX_HEIGHT 4096
+#define RZV2H_IVC_DEFAULT_WIDTH 1920
+#define RZV2H_IVC_DEFAULT_HEIGHT 1080
+
+#define RZV2H_IVC_NUM_HW_RESOURCES 3
+
+struct device;
+
+enum rzv2h_ivc_subdev_pads {
+ RZV2H_IVC_SUBDEV_SINK_PAD,
+ RZV2H_IVC_SUBDEV_SOURCE_PAD,
+ RZV2H_IVC_NUM_SUBDEV_PADS
+};
+
+struct rzv2h_ivc_format {
+ u32 fourcc;
+ /*
+ * The CRU packed pixel formats are bayer-order agnostic, so each could
+ * support any one of the 4 possible media bus formats.
+ */
+ u32 mbus_codes[4];
+ u8 dtype;
+};
+
+struct rzv2h_ivc {
+ struct device *dev;
+ void __iomem *base;
+ struct clk_bulk_data clks[RZV2H_IVC_NUM_HW_RESOURCES];
+ struct reset_control_bulk_data resets[RZV2H_IVC_NUM_HW_RESOURCES];
+ int irqnum;
+ u8 vvalid_ifp;
+
+ struct {
+ struct video_device dev;
+ struct vb2_queue vb2q;
+ struct media_pad pad;
+ } vdev;
+
+ struct {
+ struct v4l2_subdev sd;
+ struct media_pad pads[RZV2H_IVC_NUM_SUBDEV_PADS];
+ } subdev;
+
+ struct {
+ /* Spinlock to guard buffer queue */
+ spinlock_t lock;
+ struct workqueue_struct *async_wq;
+ struct work_struct work;
+ struct list_head queue;
+ struct rzv2h_ivc_buf *curr;
+ unsigned int sequence;
+ } buffers;
+
+ struct {
+ struct v4l2_pix_format_mplane pix;
+ const struct rzv2h_ivc_format *fmt;
+ } format;
+
+ /* Mutex to provide to vb2 */
+ struct mutex lock;
+ /* Lock to protect the interrupt counter */
+ spinlock_t spinlock;
+};
+
+int rzv2h_ivc_init_vdev(struct rzv2h_ivc *ivc, struct v4l2_device *v4l2_dev);
+void rzv2h_deinit_video_dev_and_queue(struct rzv2h_ivc *ivc);
+void rzv2h_ivc_buffer_done(struct rzv2h_ivc *ivc);
+int rzv2h_ivc_initialise_subdevice(struct rzv2h_ivc *ivc);
+void rzv2h_ivc_deinit_subdevice(struct rzv2h_ivc *ivc);
+void rzv2h_ivc_write(struct rzv2h_ivc *ivc, u32 addr, u32 val);
+void rzv2h_ivc_update_bits(struct rzv2h_ivc *ivc, unsigned int addr,
+ u32 mask, u32 val);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index 6c64657fc4f3..2de515c497eb 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -954,8 +954,7 @@ static int vsp1_probe(struct platform_device *pdev)
vsp1->fcp = rcar_fcp_get(fcp_node);
of_node_put(fcp_node);
if (IS_ERR(vsp1->fcp)) {
- dev_dbg(&pdev->dev, "FCP not found (%ld)\n",
- PTR_ERR(vsp1->fcp));
+ dev_dbg(&pdev->dev, "FCP not found (%pe)\n", vsp1->fcp);
return PTR_ERR(vsp1->fcp);
}
diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig
index 9bbeec4996aa..ba401d32f01b 100644
--- a/drivers/media/platform/rockchip/Kconfig
+++ b/drivers/media/platform/rockchip/Kconfig
@@ -3,5 +3,6 @@
comment "Rockchip media platform drivers"
source "drivers/media/platform/rockchip/rga/Kconfig"
+source "drivers/media/platform/rockchip/rkcif/Kconfig"
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
source "drivers/media/platform/rockchip/rkvdec/Kconfig"
diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile
index 286dc5c53f7e..0e0b2cbbd4bd 100644
--- a/drivers/media/platform/rockchip/Makefile
+++ b/drivers/media/platform/rockchip/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += rga/
+obj-y += rkcif/
obj-y += rkisp1/
obj-y += rkvdec/
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 776046de979a..43f6a8d99381 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -75,7 +75,7 @@ static irqreturn_t rga_isr(int irq, void *prv)
WARN_ON(!src);
WARN_ON(!dst);
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
dst->sequence = ctx->csequence++;
@@ -463,12 +463,8 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp;
struct rga_ctx *ctx = file_to_rga_ctx(file);
- struct vb2_queue *vq;
struct rga_frame *frm;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
frm = rga_get_frame(ctx, f->type);
if (IS_ERR(frm))
return PTR_ERR(frm);
diff --git a/drivers/media/platform/rockchip/rkcif/Kconfig b/drivers/media/platform/rockchip/rkcif/Kconfig
new file mode 100644
index 000000000000..efd82ac35bd8
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/Kconfig
@@ -0,0 +1,18 @@
+config VIDEO_ROCKCHIP_CIF
+ tristate "Rockchip Camera Interface (CIF)"
+ depends on VIDEO_DEV
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on V4L_PLATFORM_DRIVERS
+ depends on PM && COMMON_CLK
+ select MEDIA_CONTROLLER
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This is a driver for Rockchip Camera Interface (CIF). It is featured
+ in many Rockchips SoCs in different variations, such as the PX30
+ Video Input Processor (VIP, one Digital Video Port (DVP)) or the
+ RK3568 Video Capture (VICAP, one DVP, one MIPI CSI-2 receiver) unit.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rockchip-cif.
diff --git a/drivers/media/platform/rockchip/rkcif/Makefile b/drivers/media/platform/rockchip/rkcif/Makefile
new file mode 100644
index 000000000000..dca2bf45159f
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip-cif.o
+
+rockchip-cif-objs += rkcif-capture-dvp.o
+rockchip-cif-objs += rkcif-capture-mipi.o
+rockchip-cif-objs += rkcif-dev.o
+rockchip-cif-objs += rkcif-interface.o
+rockchip-cif-objs += rkcif-stream.o
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c
new file mode 100644
index 000000000000..dbaf7636aeeb
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.c
@@ -0,0 +1,865 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com>
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#include "rkcif-capture-dvp.h"
+#include "rkcif-common.h"
+#include "rkcif-interface.h"
+#include "rkcif-regs.h"
+#include "rkcif-stream.h"
+
+static const struct rkcif_output_fmt dvp_out_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_422 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_OUTPUT_420 |
+ RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU,
+ .cplanes = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR666,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .cplanes = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y16,
+ .cplanes = 1,
+ },
+};
+
+static const struct rkcif_input_fmt px30_dvp_in_fmts[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_Y12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ }
+};
+
+const struct rkcif_dvp_match_data rkcif_px30_vip_dvp_match_data = {
+ .in_fmts = px30_dvp_in_fmts,
+ .in_fmts_num = ARRAY_SIZE(px30_dvp_in_fmts),
+ .out_fmts = dvp_out_fmts,
+ .out_fmts_num = ARRAY_SIZE(dvp_out_fmts),
+ .has_scaler = true,
+ .regs = {
+ [RKCIF_DVP_CTRL] = 0x00,
+ [RKCIF_DVP_INTEN] = 0x04,
+ [RKCIF_DVP_INTSTAT] = 0x08,
+ [RKCIF_DVP_FOR] = 0x0c,
+ [RKCIF_DVP_LINE_NUM_ADDR] = 0x10,
+ [RKCIF_DVP_FRM0_ADDR_Y] = 0x14,
+ [RKCIF_DVP_FRM0_ADDR_UV] = 0x18,
+ [RKCIF_DVP_FRM1_ADDR_Y] = 0x1c,
+ [RKCIF_DVP_FRM1_ADDR_UV] = 0x20,
+ [RKCIF_DVP_VIR_LINE_WIDTH] = 0x24,
+ [RKCIF_DVP_SET_SIZE] = 0x28,
+ [RKCIF_DVP_SCL_CTRL] = 0x48,
+ [RKCIF_DVP_FRAME_STATUS] = 0x60,
+ [RKCIF_DVP_LAST_LINE] = 0x68,
+ [RKCIF_DVP_LAST_PIX] = 0x6c,
+ },
+};
+
+static const struct rkcif_input_fmt rk3568_dvp_in_fmts[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY,
+ .fmt_type = RKCIF_FMT_TYPE_YUV,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
+ RKCIF_FORMAT_BT1120_YC_SWAP |
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV |
+ RKCIF_FORMAT_BT1120_YC_SWAP |
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
+ RKCIF_FORMAT_INPUT_MODE_BT1120 |
+ RKCIF_FORMAT_BT1120_YC_SWAP |
+ RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .dvp_fmt_val = RKCIF_FORMAT_YUV_INPUT_422 |
+ RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU |
+ RKCIF_FORMAT_BT1120_YC_SWAP |
+ RKCIF_FORMAT_INPUT_MODE_BT1120,
+ .field = V4L2_FIELD_INTERLACED,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_8,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_10,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_Y12_1X12,
+ .dvp_fmt_val = RKCIF_FORMAT_INPUT_MODE_RAW |
+ RKCIF_FORMAT_RAW_DATA_WIDTH_12,
+ .fmt_type = RKCIF_FMT_TYPE_RAW,
+ .field = V4L2_FIELD_NONE,
+ },
+};
+
+static void rk3568_dvp_grf_setup(struct rkcif_device *rkcif)
+{
+ u32 con1 = RK3568_GRF_WRITE_ENABLE(RK3568_GRF_VI_CON1_CIF_DATAPATH |
+ RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM);
+
+ if (!rkcif->grf)
+ return;
+
+ con1 |= rkcif->interfaces[RKCIF_DVP].dvp.dvp_clk_delay &
+ RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM;
+
+ if (rkcif->interfaces[RKCIF_DVP].vep.bus.parallel.flags &
+ V4L2_MBUS_PCLK_SAMPLE_DUALEDGE)
+ con1 |= RK3568_GRF_VI_CON1_CIF_DATAPATH;
+
+ regmap_write(rkcif->grf, RK3568_GRF_VI_CON1, con1);
+}
+
+const struct rkcif_dvp_match_data rkcif_rk3568_vicap_dvp_match_data = {
+ .in_fmts = rk3568_dvp_in_fmts,
+ .in_fmts_num = ARRAY_SIZE(rk3568_dvp_in_fmts),
+ .out_fmts = dvp_out_fmts,
+ .out_fmts_num = ARRAY_SIZE(dvp_out_fmts),
+ .setup = rk3568_dvp_grf_setup,
+ .has_scaler = false,
+ .regs = {
+ [RKCIF_DVP_CTRL] = 0x00,
+ [RKCIF_DVP_INTEN] = 0x04,
+ [RKCIF_DVP_INTSTAT] = 0x08,
+ [RKCIF_DVP_FOR] = 0x0c,
+ [RKCIF_DVP_LINE_NUM_ADDR] = 0x2c,
+ [RKCIF_DVP_FRM0_ADDR_Y] = 0x14,
+ [RKCIF_DVP_FRM0_ADDR_UV] = 0x18,
+ [RKCIF_DVP_FRM1_ADDR_Y] = 0x1c,
+ [RKCIF_DVP_FRM1_ADDR_UV] = 0x20,
+ [RKCIF_DVP_VIR_LINE_WIDTH] = 0x24,
+ [RKCIF_DVP_SET_SIZE] = 0x28,
+ [RKCIF_DVP_CROP] = 0x34,
+ [RKCIF_DVP_FRAME_STATUS] = 0x3c,
+ [RKCIF_DVP_LAST_LINE] = 0x44,
+ [RKCIF_DVP_LAST_PIX] = 0x48,
+ },
+};
+
+static inline unsigned int rkcif_dvp_get_addr(struct rkcif_device *rkcif,
+ unsigned int index)
+{
+ if (WARN_ON_ONCE(index >= RKCIF_DVP_REGISTER_MAX))
+ return RKCIF_REGISTER_NOTSUPPORTED;
+
+ return rkcif->match_data->dvp->regs[index];
+}
+
+static inline __maybe_unused void rkcif_dvp_write(struct rkcif_device *rkcif,
+ unsigned int index, u32 val)
+{
+ unsigned int addr = rkcif_dvp_get_addr(rkcif, index);
+
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
+ return;
+
+ writel(val, rkcif->base_addr + addr);
+}
+
+static inline __maybe_unused u32 rkcif_dvp_read(struct rkcif_device *rkcif,
+ unsigned int index)
+{
+ unsigned int addr = rkcif_dvp_get_addr(rkcif, index);
+
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
+ return 0;
+
+ return readl(rkcif->base_addr + addr);
+}
+
+static void rkcif_dvp_queue_buffer(struct rkcif_stream *stream,
+ unsigned int index)
+{
+ struct rkcif_device *rkcif = stream->rkcif;
+ struct rkcif_buffer *buffer = stream->buffers[index];
+ u32 frm_addr_y, frm_addr_uv;
+
+ frm_addr_y = index ? RKCIF_DVP_FRM1_ADDR_Y : RKCIF_DVP_FRM0_ADDR_Y;
+ frm_addr_uv = index ? RKCIF_DVP_FRM1_ADDR_UV : RKCIF_DVP_FRM0_ADDR_UV;
+
+ rkcif_dvp_write(rkcif, frm_addr_y, buffer->buff_addr[RKCIF_PLANE_Y]);
+ rkcif_dvp_write(rkcif, frm_addr_uv, buffer->buff_addr[RKCIF_PLANE_UV]);
+}
+
+static int rkcif_dvp_start_streaming(struct rkcif_stream *stream)
+{
+ struct rkcif_device *rkcif = stream->rkcif;
+ struct rkcif_interface *interface = stream->interface;
+ struct v4l2_mbus_config_parallel *parallel;
+ struct v4l2_mbus_framefmt *source_fmt;
+ struct v4l2_subdev_state *state;
+ const struct rkcif_input_fmt *active_in_fmt;
+ const struct rkcif_output_fmt *active_out_fmt;
+ u32 val = 0;
+ int ret = -EINVAL;
+
+ state = v4l2_subdev_lock_and_get_active_state(&interface->sd);
+ source_fmt = v4l2_subdev_state_get_format(state, RKCIF_IF_PAD_SRC,
+ stream->id);
+ if (!source_fmt)
+ goto out;
+
+ active_in_fmt = rkcif_interface_find_input_fmt(interface, false,
+ source_fmt->code);
+ active_out_fmt = rkcif_stream_find_output_fmt(stream, false,
+ stream->pix.pixelformat);
+ if (!active_in_fmt || !active_out_fmt)
+ goto out;
+
+ parallel = &interface->vep.bus.parallel;
+ if (parallel->bus_width == 16 &&
+ (parallel->flags & V4L2_MBUS_PCLK_SAMPLE_DUALEDGE))
+ val |= RKCIF_FORMAT_BT1120_CLOCK_DOUBLE_EDGES;
+ val |= active_in_fmt->dvp_fmt_val;
+ val |= active_out_fmt->dvp_fmt_val;
+ rkcif_dvp_write(rkcif, RKCIF_DVP_FOR, val);
+
+ val = stream->pix.width;
+ if (active_in_fmt->fmt_type == RKCIF_FMT_TYPE_RAW)
+ val = stream->pix.width * 2;
+ rkcif_dvp_write(rkcif, RKCIF_DVP_VIR_LINE_WIDTH, val);
+
+ val = RKCIF_XY_COORD(stream->pix.width, stream->pix.height);
+ rkcif_dvp_write(rkcif, RKCIF_DVP_SET_SIZE, val);
+
+ rkcif_dvp_write(rkcif, RKCIF_DVP_FRAME_STATUS, RKCIF_FRAME_STAT_CLS);
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT, RKCIF_INTSTAT_CLS);
+ if (rkcif->match_data->dvp->has_scaler) {
+ val = active_in_fmt->fmt_type == RKCIF_FMT_TYPE_YUV ?
+ RKCIF_SCL_CTRL_ENABLE_YUV_16BIT_BYPASS :
+ RKCIF_SCL_CTRL_ENABLE_RAW_16BIT_BYPASS;
+ rkcif_dvp_write(rkcif, RKCIF_DVP_SCL_CTRL, val);
+ }
+
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTEN,
+ RKCIF_INTEN_FRAME_END_EN |
+ RKCIF_INTEN_PST_INF_FRAME_END_EN);
+
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL,
+ RKCIF_CTRL_AXI_BURST_16 | RKCIF_CTRL_MODE_PINGPONG |
+ RKCIF_CTRL_ENABLE_CAPTURE);
+
+ ret = 0;
+
+out:
+ v4l2_subdev_unlock_state(state);
+ return ret;
+}
+
+static void rkcif_dvp_stop_streaming(struct rkcif_stream *stream)
+{
+ struct rkcif_device *rkcif = stream->rkcif;
+ u32 val;
+
+ val = rkcif_dvp_read(rkcif, RKCIF_DVP_CTRL);
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL,
+ val & (~RKCIF_CTRL_ENABLE_CAPTURE));
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTEN, 0x0);
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT, 0x3ff);
+ rkcif_dvp_write(rkcif, RKCIF_DVP_FRAME_STATUS, 0x0);
+
+ stream->stopping = false;
+}
+
+static void rkcif_dvp_reset_stream(struct rkcif_device *rkcif)
+{
+ u32 ctl = rkcif_dvp_read(rkcif, RKCIF_DVP_CTRL);
+
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL,
+ ctl & (~RKCIF_CTRL_ENABLE_CAPTURE));
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CTRL, ctl | RKCIF_CTRL_ENABLE_CAPTURE);
+}
+
+static void rkcif_dvp_set_crop(struct rkcif_stream *stream, u16 left, u16 top)
+{
+ struct rkcif_device *rkcif = stream->rkcif;
+ u32 val;
+
+ val = RKCIF_XY_COORD(left, top);
+ rkcif_dvp_write(rkcif, RKCIF_DVP_CROP, val);
+}
+
+irqreturn_t rkcif_dvp_isr(int irq, void *ctx)
+{
+ struct device *dev = ctx;
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
+ struct rkcif_stream *stream;
+ u32 intstat, lastline, lastpix, cif_frmst;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (!rkcif->match_data->dvp)
+ return ret;
+
+ intstat = rkcif_dvp_read(rkcif, RKCIF_DVP_INTSTAT);
+ cif_frmst = rkcif_dvp_read(rkcif, RKCIF_DVP_FRAME_STATUS);
+ lastline = RKCIF_FETCH_Y(rkcif_dvp_read(rkcif, RKCIF_DVP_LAST_LINE));
+ lastpix = RKCIF_FETCH_Y(rkcif_dvp_read(rkcif, RKCIF_DVP_LAST_PIX));
+
+ if (intstat & RKCIF_INTSTAT_FRAME_END) {
+ rkcif_dvp_write(rkcif, RKCIF_DVP_INTSTAT,
+ RKCIF_INTSTAT_FRAME_END_CLR |
+ RKCIF_INTSTAT_LINE_END_CLR);
+
+ stream = &rkcif->interfaces[RKCIF_DVP].streams[RKCIF_ID0];
+
+ if (stream->stopping) {
+ rkcif_dvp_stop_streaming(stream);
+ wake_up(&stream->wq_stopped);
+ ret = IRQ_HANDLED;
+ goto out;
+ }
+
+ if (lastline != stream->pix.height) {
+ v4l2_err(&rkcif->v4l2_dev,
+ "bad frame, irq:%#x frmst:%#x size:%dx%d\n",
+ intstat, cif_frmst, lastpix, lastline);
+
+ rkcif_dvp_reset_stream(rkcif);
+ }
+
+ rkcif_stream_pingpong(stream);
+
+ ret = IRQ_HANDLED;
+ }
+out:
+ return ret;
+}
+
+int rkcif_dvp_register(struct rkcif_device *rkcif)
+{
+ struct rkcif_interface *interface;
+ unsigned int streams_num;
+ int ret;
+
+ if (!rkcif->match_data->dvp)
+ return 0;
+
+ interface = &rkcif->interfaces[RKCIF_DVP];
+ interface->index = RKCIF_DVP;
+ interface->type = RKCIF_IF_DVP;
+ interface->in_fmts = rkcif->match_data->dvp->in_fmts;
+ interface->in_fmts_num = rkcif->match_data->dvp->in_fmts_num;
+ interface->set_crop = rkcif_dvp_set_crop;
+ ret = rkcif_interface_register(rkcif, interface);
+ if (ret)
+ return ret;
+
+ if (rkcif->match_data->dvp->setup)
+ rkcif->match_data->dvp->setup(rkcif);
+
+ streams_num = rkcif->match_data->dvp->has_ids ? 4 : 1;
+ for (unsigned int i = 0; i < streams_num; i++) {
+ struct rkcif_stream *stream = &interface->streams[i];
+
+ stream->id = i;
+ stream->interface = interface;
+ stream->out_fmts = rkcif->match_data->dvp->out_fmts;
+ stream->out_fmts_num = rkcif->match_data->dvp->out_fmts_num;
+ stream->queue_buffer = rkcif_dvp_queue_buffer;
+ stream->start_streaming = rkcif_dvp_start_streaming;
+ stream->stop_streaming = rkcif_dvp_stop_streaming;
+
+ ret = rkcif_stream_register(rkcif, stream);
+ if (ret)
+ goto err_streams_unregister;
+
+ interface->streams_num++;
+ }
+
+ return 0;
+
+err_streams_unregister:
+ for (unsigned int i = 0; i < interface->streams_num; i++)
+ rkcif_stream_unregister(&interface->streams[i]);
+
+ rkcif_interface_unregister(interface);
+
+ return ret;
+}
+
+void rkcif_dvp_unregister(struct rkcif_device *rkcif)
+{
+ struct rkcif_interface *interface;
+
+ if (!rkcif->match_data->dvp)
+ return;
+
+ interface = &rkcif->interfaces[RKCIF_DVP];
+
+ for (unsigned int i = 0; i < interface->streams_num; i++)
+ rkcif_stream_unregister(&interface->streams[i]);
+
+ rkcif_interface_unregister(interface);
+}
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h
new file mode 100644
index 000000000000..a4ed37833bd6
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-dvp.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#ifndef _RKCIF_CAPTURE_DVP_H
+#define _RKCIF_CAPTURE_DVP_H
+
+#include "rkcif-common.h"
+
+extern const struct rkcif_dvp_match_data rkcif_px30_vip_dvp_match_data;
+extern const struct rkcif_dvp_match_data rkcif_rk3568_vicap_dvp_match_data;
+
+int rkcif_dvp_register(struct rkcif_device *rkcif);
+
+void rkcif_dvp_unregister(struct rkcif_device *rkcif);
+
+irqreturn_t rkcif_dvp_isr(int irq, void *ctx);
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
new file mode 100644
index 000000000000..1b81bcc067ef
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#include <linux/interrupt.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#include "rkcif-capture-mipi.h"
+#include "rkcif-common.h"
+#include "rkcif-interface.h"
+#include "rkcif-regs.h"
+#include "rkcif-stream.h"
+
+#define RK3568_MIPI_CTRL0_HIGH_ALIGN BIT(31)
+#define RK3568_MIPI_CTRL0_UV_SWAP_EN BIT(7)
+#define RK3568_MIPI_CTRL0_COMPACT_EN BIT(6)
+#define RK3568_MIPI_CTRL0_CROP_EN BIT(5)
+#define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1)
+
+#define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10)
+#define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8)
+#define RKCIF_MIPI_CTRL0_CAP_EN BIT(0)
+
+#define RKCIF_MIPI_INT_FRAME0_END(id) BIT(8 + (id) * 2 + 0)
+#define RKCIF_MIPI_INT_FRAME1_END(id) BIT(8 + (id) * 2 + 1)
+
+static const struct rkcif_output_fmt mipi_out_fmts[] = {
+ /* YUV formats */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .depth = 16,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_YUV422_8B,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .depth = 16,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_YUV422_8B,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .depth = 16,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_YUV422_8B,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .depth = 16,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_YUV422_8B,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ /* RGB formats */
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ .depth = 24,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RGB888,
+ .type = RKCIF_MIPI_TYPE_RGB888,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .mbus_code = MEDIA_BUS_FMT_BGR888_1X24,
+ .depth = 24,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RGB888,
+ .type = RKCIF_MIPI_TYPE_RGB888,
+ },
+ },
+ /* Bayer formats */
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW8,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW8,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW8,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW8,
+ .type = RKCIF_MIPI_TYPE_RAW8,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10P,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10P,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10P,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10P,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 10,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW10,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW10,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR12P,
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG12P,
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG12P,
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB12P,
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 12,
+ .cplanes = 1,
+ .mipi = {
+ .dt = MIPI_CSI2_DT_RAW12,
+ .compact = true,
+ .type = RKCIF_MIPI_TYPE_RAW12,
+ },
+ },
+};
+
+static const struct rkcif_input_fmt mipi_in_fmts[] = {
+ /* YUV formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ },
+ /* RGB formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_BGR888_1X24,
+ },
+ /* Bayer formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ },
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ },
+};
+
+static u32
+rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream,
+ const struct rkcif_output_fmt *active_out_fmt)
+{
+ u32 ctrl0 = 0;
+
+ ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt);
+ ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN;
+ ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN;
+
+ if (active_out_fmt->mipi.compact)
+ ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN;
+
+ switch (active_out_fmt->mipi.type) {
+ case RKCIF_MIPI_TYPE_RAW8:
+ break;
+ case RKCIF_MIPI_TYPE_RAW10:
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x1);
+ break;
+ case RKCIF_MIPI_TYPE_RAW12:
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x2);
+ break;
+ case RKCIF_MIPI_TYPE_RGB888:
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x3);
+ break;
+ case RKCIF_MIPI_TYPE_YUV422SP:
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x4);
+ break;
+ case RKCIF_MIPI_TYPE_YUV420SP:
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x5);
+ break;
+ case RKCIF_MIPI_TYPE_YUV400:
+ ctrl0 |= RK3568_MIPI_CTRL0_WRDDR(0x6);
+ break;
+ default:
+ break;
+ }
+
+ return ctrl0;
+}
+
+const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data = {
+ .mipi_num = 1,
+ .mipi_ctrl0 = rkcif_rk3568_mipi_ctrl0,
+ .regs = {
+ [RKCIF_MIPI_CTRL] = 0x20,
+ [RKCIF_MIPI_INTEN] = 0xa4,
+ [RKCIF_MIPI_INTSTAT] = 0xa8,
+ },
+ .regs_id = {
+ [RKCIF_ID0] = {
+ [RKCIF_MIPI_CTRL0] = 0x00,
+ [RKCIF_MIPI_CTRL1] = 0x04,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x3c,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x38,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0x40,
+ [RKCIF_MIPI_CROP_START] = 0xbc,
+ },
+ [RKCIF_ID1] = {
+ [RKCIF_MIPI_CTRL0] = 0x08,
+ [RKCIF_MIPI_CTRL1] = 0x0c,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x44,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x4c,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x54,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x5c,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x48,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x50,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x58,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0x60,
+ [RKCIF_MIPI_CROP_START] = 0xc0,
+ },
+ [RKCIF_ID2] = {
+ [RKCIF_MIPI_CTRL0] = 0x10,
+ [RKCIF_MIPI_CTRL1] = 0x14,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x64,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x6c,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x74,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x7c,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x68,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x70,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x78,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0x80,
+ [RKCIF_MIPI_CROP_START] = 0xc4,
+ },
+ [RKCIF_ID3] = {
+ [RKCIF_MIPI_CTRL0] = 0x18,
+ [RKCIF_MIPI_CTRL1] = 0x1c,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x84,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x8c,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x94,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = 0x9c,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x88,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x90,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = 0x98,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = 0xa0,
+ [RKCIF_MIPI_CROP_START] = 0xc8,
+ },
+ },
+ .blocks = {
+ {
+ .offset = 0x80,
+ },
+ },
+};
+
+static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface,
+ unsigned int index)
+{
+ struct rkcif_device *rkcif = interface->rkcif;
+ unsigned int block, offset, reg;
+
+ block = interface->index - RKCIF_MIPI_BASE;
+
+ if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
+ WARN_ON_ONCE(index > RKCIF_MIPI_REGISTER_MAX))
+ return RKCIF_REGISTER_NOTSUPPORTED;
+
+ offset = rkcif->match_data->mipi->blocks[block].offset;
+ reg = rkcif->match_data->mipi->regs[index];
+ if (reg == RKCIF_REGISTER_NOTSUPPORTED)
+ return reg;
+
+ return offset + reg;
+}
+
+static inline unsigned int rkcif_mipi_id_get_reg(struct rkcif_stream *stream,
+ unsigned int index)
+{
+ struct rkcif_device *rkcif = stream->rkcif;
+ unsigned int block, id, offset, reg;
+
+ block = stream->interface->index - RKCIF_MIPI_BASE;
+ id = stream->id;
+
+ if (WARN_ON_ONCE(block > RKCIF_MIPI_MAX - RKCIF_MIPI_BASE) ||
+ WARN_ON_ONCE(id > RKCIF_ID_MAX) ||
+ WARN_ON_ONCE(index > RKCIF_MIPI_ID_REGISTER_MAX))
+ return RKCIF_REGISTER_NOTSUPPORTED;
+
+ offset = rkcif->match_data->mipi->blocks[block].offset;
+ reg = rkcif->match_data->mipi->regs_id[id][index];
+ if (reg == RKCIF_REGISTER_NOTSUPPORTED)
+ return reg;
+
+ return offset + reg;
+}
+
+static inline __maybe_unused void
+rkcif_mipi_write(struct rkcif_interface *interface, unsigned int index, u32 val)
+{
+ unsigned int addr = rkcif_mipi_get_reg(interface, index);
+
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
+ return;
+
+ writel(val, interface->rkcif->base_addr + addr);
+}
+
+static inline __maybe_unused void
+rkcif_mipi_stream_write(struct rkcif_stream *stream, unsigned int index,
+ u32 val)
+{
+ unsigned int addr = rkcif_mipi_id_get_reg(stream, index);
+
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
+ return;
+
+ writel(val, stream->rkcif->base_addr + addr);
+}
+
+static inline __maybe_unused u32
+rkcif_mipi_read(struct rkcif_interface *interface, unsigned int index)
+{
+ unsigned int addr = rkcif_mipi_get_reg(interface, index);
+
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
+ return 0;
+
+ return readl(interface->rkcif->base_addr + addr);
+}
+
+static inline __maybe_unused u32
+rkcif_mipi_stream_read(struct rkcif_stream *stream, unsigned int index)
+{
+ unsigned int addr = rkcif_mipi_id_get_reg(stream, index);
+
+ if (addr == RKCIF_REGISTER_NOTSUPPORTED)
+ return 0;
+
+ return readl(stream->rkcif->base_addr + addr);
+}
+
+static void rkcif_mipi_queue_buffer(struct rkcif_stream *stream,
+ unsigned int index)
+{
+ struct rkcif_buffer *buffer = stream->buffers[index];
+ u32 frm_addr_y, frm_addr_uv;
+
+ frm_addr_y = index ? RKCIF_MIPI_FRAME1_ADDR_Y :
+ RKCIF_MIPI_FRAME0_ADDR_Y;
+ frm_addr_uv = index ? RKCIF_MIPI_FRAME1_ADDR_UV :
+ RKCIF_MIPI_FRAME0_ADDR_UV;
+
+ rkcif_mipi_stream_write(stream, frm_addr_y,
+ buffer->buff_addr[RKCIF_PLANE_Y]);
+ rkcif_mipi_stream_write(stream, frm_addr_uv,
+ buffer->buff_addr[RKCIF_PLANE_UV]);
+}
+
+static int rkcif_mipi_start_streaming(struct rkcif_stream *stream)
+{
+ struct rkcif_interface *interface = stream->interface;
+ const struct rkcif_output_fmt *active_out_fmt;
+ const struct rkcif_mipi_match_data *match_data;
+ struct v4l2_subdev_state *state;
+ u32 ctrl0 = 0, ctrl1 = 0, int_temp = 0, int_mask = 0, vlw = 0;
+ u16 height, width;
+ int ret = -EINVAL;
+
+ state = v4l2_subdev_lock_and_get_active_state(&interface->sd);
+
+ active_out_fmt = rkcif_stream_find_output_fmt(stream, false,
+ stream->pix.pixelformat);
+ if (!active_out_fmt)
+ goto out;
+
+ height = stream->pix.height;
+ width = stream->pix.width;
+ vlw = stream->pix.plane_fmt[0].bytesperline;
+
+ match_data = stream->rkcif->match_data->mipi;
+ if (match_data->mipi_ctrl0)
+ ctrl0 = match_data->mipi_ctrl0(stream, active_out_fmt);
+
+ ctrl1 = RKCIF_XY_COORD(width, height);
+
+ int_mask |= RKCIF_MIPI_INT_FRAME0_END(stream->id);
+ int_mask |= RKCIF_MIPI_INT_FRAME1_END(stream->id);
+
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTEN);
+ int_temp |= int_mask;
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTEN, int_temp);
+
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT);
+ int_temp &= ~int_mask;
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, int_temp);
+
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME0_VLW_Y, vlw);
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME1_VLW_Y, vlw);
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME0_VLW_UV, vlw);
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_FRAME1_VLW_UV, vlw);
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CROP_START, 0x0);
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1);
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0);
+
+ ret = 0;
+
+out:
+ v4l2_subdev_unlock_state(state);
+ return ret;
+}
+
+static void rkcif_mipi_stop_streaming(struct rkcif_stream *stream)
+{
+ struct rkcif_interface *interface = stream->interface;
+ struct v4l2_subdev_state *state;
+ u32 int_temp = 0, int_mask = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(&interface->sd);
+
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, 0);
+
+ int_mask |= RKCIF_MIPI_INT_FRAME0_END(stream->id);
+ int_mask |= RKCIF_MIPI_INT_FRAME1_END(stream->id);
+
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTEN);
+ int_temp &= ~int_mask;
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTEN, int_temp);
+
+ int_temp = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT);
+ int_temp &= ~int_mask;
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, int_temp);
+
+ stream->stopping = false;
+
+ v4l2_subdev_unlock_state(state);
+}
+
+static void rkcif_mipi_set_crop(struct rkcif_stream *stream, u16 left, u16 top)
+{
+ u32 val;
+
+ val = RKCIF_XY_COORD(left, top);
+ rkcif_mipi_stream_write(stream, RKCIF_MIPI_CROP_START, val);
+}
+
+irqreturn_t rkcif_mipi_isr(int irq, void *ctx)
+{
+ struct device *dev = ctx;
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
+ irqreturn_t ret = IRQ_NONE;
+ u32 intstat;
+
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
+
+ intstat = rkcif_mipi_read(interface, RKCIF_MIPI_INTSTAT);
+ rkcif_mipi_write(interface, RKCIF_MIPI_INTSTAT, intstat);
+
+ for (unsigned int j = 0; j < interface->streams_num; j++) {
+ struct rkcif_stream *stream = &interface->streams[j];
+
+ if (intstat & RKCIF_MIPI_INT_FRAME0_END(stream->id) ||
+ intstat & RKCIF_MIPI_INT_FRAME1_END(stream->id)) {
+ ret = IRQ_HANDLED;
+
+ if (stream->stopping) {
+ rkcif_mipi_stop_streaming(stream);
+ wake_up(&stream->wq_stopped);
+ continue;
+ }
+
+ rkcif_stream_pingpong(stream);
+ }
+ }
+ }
+
+ return ret;
+}
+
+int rkcif_mipi_register(struct rkcif_device *rkcif)
+{
+ int ret;
+
+ if (!rkcif->match_data->mipi)
+ return 0;
+
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
+
+ interface->index = index;
+ interface->type = RKCIF_IF_MIPI;
+ interface->in_fmts = mipi_in_fmts;
+ interface->in_fmts_num = ARRAY_SIZE(mipi_in_fmts);
+ interface->set_crop = rkcif_mipi_set_crop;
+ interface->streams_num = 0;
+ ret = rkcif_interface_register(rkcif, interface);
+ if (ret)
+ continue;
+
+ for (unsigned int j = 0; j < RKCIF_ID_MAX; j++) {
+ struct rkcif_stream *stream = &interface->streams[j];
+
+ stream->id = j;
+ stream->interface = interface;
+ stream->out_fmts = mipi_out_fmts;
+ stream->out_fmts_num = ARRAY_SIZE(mipi_out_fmts);
+ stream->queue_buffer = rkcif_mipi_queue_buffer;
+ stream->start_streaming = rkcif_mipi_start_streaming;
+ stream->stop_streaming = rkcif_mipi_stop_streaming;
+ ret = rkcif_stream_register(rkcif, stream);
+ if (ret)
+ goto err;
+ interface->streams_num++;
+ }
+ }
+
+ return 0;
+
+err:
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
+
+ for (unsigned int j = 0; j < interface->streams_num; j++)
+ rkcif_stream_unregister(&interface->streams[j]);
+
+ rkcif_interface_unregister(interface);
+ }
+ return ret;
+}
+
+void rkcif_mipi_unregister(struct rkcif_device *rkcif)
+{
+ if (!rkcif->match_data->mipi)
+ return;
+
+ for (unsigned int i = 0; i < rkcif->match_data->mipi->mipi_num; i++) {
+ enum rkcif_interface_index index = RKCIF_MIPI_BASE + i;
+ struct rkcif_interface *interface = &rkcif->interfaces[index];
+
+ for (unsigned int j = 0; j < interface->streams_num; j++)
+ rkcif_stream_unregister(&interface->streams[j]);
+
+ rkcif_interface_unregister(interface);
+ }
+}
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
new file mode 100644
index 000000000000..7f16eadc474c
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#ifndef _RKCIF_CAPTURE_MIPI_H
+#define _RKCIF_CAPTURE_MIPI_H
+
+#include "rkcif-common.h"
+
+extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data;
+
+int rkcif_mipi_register(struct rkcif_device *rkcif);
+
+void rkcif_mipi_unregister(struct rkcif_device *rkcif);
+
+irqreturn_t rkcif_mipi_isr(int irq, void *ctx);
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
new file mode 100644
index 000000000000..dd92cfbc879f
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#ifndef _RKCIF_COMMON_H
+#define _RKCIF_COMMON_H
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "rkcif-regs.h"
+
+#define RKCIF_DRIVER_NAME "rockchip-cif"
+#define RKCIF_CLK_MAX 4
+
+enum rkcif_format_type {
+ RKCIF_FMT_TYPE_INVALID,
+ RKCIF_FMT_TYPE_YUV,
+ RKCIF_FMT_TYPE_RAW,
+};
+
+enum rkcif_id_index {
+ RKCIF_ID0,
+ RKCIF_ID1,
+ RKCIF_ID2,
+ RKCIF_ID3,
+ RKCIF_ID_MAX
+};
+
+enum rkcif_interface_index {
+ RKCIF_DVP,
+ RKCIF_MIPI_BASE,
+ RKCIF_MIPI1 = RKCIF_MIPI_BASE,
+ RKCIF_MIPI2,
+ RKCIF_MIPI3,
+ RKCIF_MIPI4,
+ RKCIF_MIPI5,
+ RKCIF_MIPI6,
+ RKCIF_MIPI_MAX,
+ RKCIF_IF_MAX = RKCIF_MIPI_MAX
+};
+
+enum rkcif_interface_pad_index {
+ RKCIF_IF_PAD_SINK,
+ RKCIF_IF_PAD_SRC,
+ RKCIF_IF_PAD_MAX
+};
+
+enum rkcif_interface_status {
+ RKCIF_IF_INACTIVE,
+ RKCIF_IF_ACTIVE,
+};
+
+enum rkcif_interface_type {
+ RKCIF_IF_INVALID,
+ RKCIF_IF_DVP,
+ RKCIF_IF_MIPI,
+};
+
+enum rkcif_mipi_format_type {
+ RKCIF_MIPI_TYPE_INVALID,
+ RKCIF_MIPI_TYPE_RAW8,
+ RKCIF_MIPI_TYPE_RAW10,
+ RKCIF_MIPI_TYPE_RAW12,
+ RKCIF_MIPI_TYPE_RGB888,
+ RKCIF_MIPI_TYPE_YUV422SP,
+ RKCIF_MIPI_TYPE_YUV420SP,
+ RKCIF_MIPI_TYPE_YUV400,
+};
+
+struct rkcif_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ dma_addr_t buff_addr[VIDEO_MAX_PLANES];
+ bool is_dummy;
+};
+
+struct rkcif_dummy_buffer {
+ struct rkcif_buffer buffer;
+ void *vaddr;
+ u32 size;
+};
+
+enum rkcif_plane_index {
+ RKCIF_PLANE_Y,
+ RKCIF_PLANE_UV,
+ RKCIF_PLANE_MAX
+};
+
+struct rkcif_input_fmt {
+ u32 mbus_code;
+
+ enum rkcif_format_type fmt_type;
+ enum v4l2_field field;
+
+ union {
+ u32 dvp_fmt_val;
+ };
+};
+
+struct rkcif_output_fmt {
+ u32 fourcc;
+ u32 mbus_code;
+ u8 cplanes;
+ u8 depth;
+
+ union {
+ u32 dvp_fmt_val;
+ struct {
+ u8 dt;
+ bool compact;
+ enum rkcif_mipi_format_type type;
+ } mipi;
+ };
+};
+
+struct rkcif_interface;
+
+struct rkcif_remote {
+ struct v4l2_async_connection async_conn;
+ struct v4l2_subdev *sd;
+
+ struct rkcif_interface *interface;
+};
+
+struct rkcif_stream {
+ enum rkcif_id_index id;
+ struct rkcif_device *rkcif;
+ struct rkcif_interface *interface;
+ const struct rkcif_output_fmt *out_fmts;
+ unsigned int out_fmts_num;
+
+ /* in ping-pong mode, two buffers can be provided to the HW */
+ struct rkcif_buffer *buffers[2];
+ int frame_idx;
+ int frame_phase;
+
+ /* in case of no available buffer, HW can write to the dummy buffer */
+ struct rkcif_dummy_buffer dummy;
+
+ bool stopping;
+ wait_queue_head_t wq_stopped;
+
+ /* queue of available buffers plus spinlock that protects it */
+ spinlock_t driver_queue_lock;
+ struct list_head driver_queue;
+
+ /* lock used by the V4L2 core */
+ struct mutex vlock;
+
+ struct media_pad pad;
+ struct media_pipeline pipeline;
+ struct v4l2_pix_format_mplane pix;
+ struct vb2_queue buf_queue;
+ struct video_device vdev;
+
+ void (*queue_buffer)(struct rkcif_stream *stream, unsigned int index);
+ int (*start_streaming)(struct rkcif_stream *stream);
+ void (*stop_streaming)(struct rkcif_stream *stream);
+};
+
+struct rkcif_dvp {
+ u32 dvp_clk_delay;
+};
+
+struct rkcif_interface {
+ enum rkcif_interface_type type;
+ enum rkcif_interface_status status;
+ enum rkcif_interface_index index;
+ struct rkcif_device *rkcif;
+ struct rkcif_remote *remote;
+ struct rkcif_stream streams[RKCIF_ID_MAX];
+ unsigned int streams_num;
+ const struct rkcif_input_fmt *in_fmts;
+ unsigned int in_fmts_num;
+
+ struct media_pad pads[RKCIF_IF_PAD_MAX];
+ struct v4l2_fwnode_endpoint vep;
+ struct v4l2_subdev sd;
+
+ union {
+ struct rkcif_dvp dvp;
+ };
+
+ void (*set_crop)(struct rkcif_stream *stream, u16 left, u16 top);
+};
+
+struct rkcif_mipi_match_data {
+ unsigned int mipi_num;
+ unsigned int regs[RKCIF_MIPI_REGISTER_MAX];
+ unsigned int regs_id[RKCIF_ID_MAX][RKCIF_MIPI_ID_REGISTER_MAX];
+ u32 (*mipi_ctrl0)(struct rkcif_stream *stream,
+ const struct rkcif_output_fmt *active_out_fmt);
+ struct {
+ unsigned int offset;
+ } blocks[RKCIF_MIPI_MAX - RKCIF_MIPI_BASE];
+};
+
+struct rkcif_dvp_match_data {
+ const struct rkcif_input_fmt *in_fmts;
+ unsigned int in_fmts_num;
+ const struct rkcif_output_fmt *out_fmts;
+ unsigned int out_fmts_num;
+ void (*setup)(struct rkcif_device *rkcif);
+ bool has_scaler;
+ bool has_ids;
+ unsigned int regs[RKCIF_DVP_REGISTER_MAX];
+};
+
+struct rkcif_match_data {
+ const char *const *clks;
+ unsigned int clks_num;
+ const struct rkcif_dvp_match_data *dvp;
+ const struct rkcif_mipi_match_data *mipi;
+};
+
+struct rkcif_device {
+ struct device *dev;
+
+ const struct rkcif_match_data *match_data;
+ struct clk_bulk_data clks[RKCIF_CLK_MAX];
+ unsigned int clks_num;
+ struct regmap *grf;
+ struct reset_control *reset;
+ void __iomem *base_addr;
+
+ struct rkcif_interface interfaces[RKCIF_IF_MAX];
+
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_async_notifier notifier;
+};
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
new file mode 100644
index 000000000000..b4cf1146f131
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com>
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+
+#include "rkcif-capture-dvp.h"
+#include "rkcif-capture-mipi.h"
+#include "rkcif-common.h"
+
+static const char *const px30_vip_clks[] = {
+ "aclk",
+ "hclk",
+ "pclk",
+};
+
+static const struct rkcif_match_data px30_vip_match_data = {
+ .clks = px30_vip_clks,
+ .clks_num = ARRAY_SIZE(px30_vip_clks),
+ .dvp = &rkcif_px30_vip_dvp_match_data,
+};
+
+static const char *const rk3568_vicap_clks[] = {
+ "aclk",
+ "hclk",
+ "dclk",
+ "iclk",
+};
+
+static const struct rkcif_match_data rk3568_vicap_match_data = {
+ .clks = rk3568_vicap_clks,
+ .clks_num = ARRAY_SIZE(rk3568_vicap_clks),
+ .dvp = &rkcif_rk3568_vicap_dvp_match_data,
+ .mipi = &rkcif_rk3568_vicap_mipi_match_data,
+};
+
+static const struct of_device_id rkcif_plat_of_match[] = {
+ {
+ .compatible = "rockchip,px30-vip",
+ .data = &px30_vip_match_data,
+ },
+ {
+ .compatible = "rockchip,rk3568-vicap",
+ .data = &rk3568_vicap_match_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rkcif_plat_of_match);
+
+static int rkcif_register(struct rkcif_device *rkcif)
+{
+ int ret;
+
+ ret = rkcif_dvp_register(rkcif);
+ if (ret && ret != -ENODEV)
+ goto err;
+
+ ret = rkcif_mipi_register(rkcif);
+ if (ret && ret != -ENODEV)
+ goto err_dvp_unregister;
+
+ return 0;
+
+err_dvp_unregister:
+ rkcif_dvp_unregister(rkcif);
+err:
+ return ret;
+}
+
+static void rkcif_unregister(struct rkcif_device *rkcif)
+{
+ rkcif_mipi_unregister(rkcif);
+ rkcif_dvp_unregister(rkcif);
+}
+
+static int rkcif_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asd)
+{
+ struct rkcif_device *rkcif =
+ container_of(notifier, struct rkcif_device, notifier);
+ struct rkcif_remote *remote =
+ container_of(asd, struct rkcif_remote, async_conn);
+ struct media_pad *sink_pad =
+ &remote->interface->pads[RKCIF_IF_PAD_SINK];
+ int ret;
+
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink_pad,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(rkcif->dev, "failed to link source pad of %s\n",
+ sd->name);
+ return ret;
+ }
+
+ remote->sd = sd;
+
+ return 0;
+}
+
+static int rkcif_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rkcif_device *rkcif =
+ container_of(notifier, struct rkcif_device, notifier);
+
+ return v4l2_device_register_subdev_nodes(&rkcif->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations rkcif_notifier_ops = {
+ .bound = rkcif_notifier_bound,
+ .complete = rkcif_notifier_complete,
+};
+
+static irqreturn_t rkcif_isr(int irq, void *ctx)
+{
+ irqreturn_t ret = IRQ_NONE;
+
+ if (rkcif_dvp_isr(irq, ctx) == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+
+ if (rkcif_mipi_isr(irq, ctx) == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+
+ return ret;
+}
+
+static int rkcif_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rkcif_device *rkcif;
+ int ret, irq;
+
+ rkcif = devm_kzalloc(dev, sizeof(*rkcif), GFP_KERNEL);
+ if (!rkcif)
+ return -ENOMEM;
+
+ rkcif->match_data = of_device_get_match_data(dev);
+ if (!rkcif->match_data)
+ return -ENODEV;
+
+ dev_set_drvdata(dev, rkcif);
+ rkcif->dev = dev;
+
+ rkcif->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(rkcif->base_addr))
+ return PTR_ERR(rkcif->base_addr);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, rkcif_isr, IRQF_SHARED,
+ dev_driver_string(dev), dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request irq\n");
+
+ if (rkcif->match_data->clks_num > RKCIF_CLK_MAX)
+ return dev_err_probe(dev, -EINVAL, "invalid number of clocks\n");
+
+ rkcif->clks_num = rkcif->match_data->clks_num;
+ for (unsigned int i = 0; i < rkcif->clks_num; i++)
+ rkcif->clks[i].id = rkcif->match_data->clks[i];
+ ret = devm_clk_bulk_get(dev, rkcif->clks_num, rkcif->clks);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get clocks\n");
+
+ rkcif->reset = devm_reset_control_array_get_exclusive(dev);
+ if (IS_ERR(rkcif->reset))
+ return PTR_ERR(rkcif->reset);
+
+ rkcif->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(rkcif->grf))
+ rkcif->grf = NULL;
+
+ pm_runtime_enable(&pdev->dev);
+
+ rkcif->media_dev.dev = dev;
+ strscpy(rkcif->media_dev.model, RKCIF_DRIVER_NAME,
+ sizeof(rkcif->media_dev.model));
+ media_device_init(&rkcif->media_dev);
+
+ rkcif->v4l2_dev.mdev = &rkcif->media_dev;
+ ret = v4l2_device_register(dev, &rkcif->v4l2_dev);
+ if (ret)
+ goto err_media_dev_cleanup;
+
+ ret = media_device_register(&rkcif->media_dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to register media device: %d\n", ret);
+ goto err_v4l2_dev_unregister;
+ }
+
+ v4l2_async_nf_init(&rkcif->notifier, &rkcif->v4l2_dev);
+ rkcif->notifier.ops = &rkcif_notifier_ops;
+
+ ret = rkcif_register(rkcif);
+ if (ret) {
+ dev_err(dev, "failed to register media entities: %d\n", ret);
+ goto err_notifier_cleanup;
+ }
+
+ ret = v4l2_async_nf_register(&rkcif->notifier);
+ if (ret)
+ goto err_rkcif_unregister;
+
+ return 0;
+
+err_rkcif_unregister:
+ rkcif_unregister(rkcif);
+err_notifier_cleanup:
+ v4l2_async_nf_cleanup(&rkcif->notifier);
+ media_device_unregister(&rkcif->media_dev);
+err_v4l2_dev_unregister:
+ v4l2_device_unregister(&rkcif->v4l2_dev);
+err_media_dev_cleanup:
+ media_device_cleanup(&rkcif->media_dev);
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+}
+
+static void rkcif_remove(struct platform_device *pdev)
+{
+ struct rkcif_device *rkcif = platform_get_drvdata(pdev);
+
+ v4l2_async_nf_unregister(&rkcif->notifier);
+ rkcif_unregister(rkcif);
+ v4l2_async_nf_cleanup(&rkcif->notifier);
+ media_device_unregister(&rkcif->media_dev);
+ v4l2_device_unregister(&rkcif->v4l2_dev);
+ media_device_cleanup(&rkcif->media_dev);
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int rkcif_runtime_suspend(struct device *dev)
+{
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
+
+ /*
+ * Reset CIF (CRU, DMA, FIFOs) to allow a clean resume.
+ * Since this resets the IOMMU too, we cannot issue this reset when
+ * resuming.
+ */
+ reset_control_assert(rkcif->reset);
+ udelay(5);
+ reset_control_deassert(rkcif->reset);
+
+ clk_bulk_disable_unprepare(rkcif->clks_num, rkcif->clks);
+
+ return 0;
+}
+
+static int rkcif_runtime_resume(struct device *dev)
+{
+ struct rkcif_device *rkcif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_bulk_prepare_enable(rkcif->clks_num, rkcif->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable clocks\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops rkcif_plat_pm_ops = {
+ .runtime_suspend = rkcif_runtime_suspend,
+ .runtime_resume = rkcif_runtime_resume,
+};
+
+static struct platform_driver rkcif_plat_drv = {
+ .driver = {
+ .name = RKCIF_DRIVER_NAME,
+ .of_match_table = rkcif_plat_of_match,
+ .pm = &rkcif_plat_pm_ops,
+ },
+ .probe = rkcif_probe,
+ .remove = rkcif_remove,
+};
+module_platform_driver(rkcif_plat_drv);
+
+MODULE_DESCRIPTION("Rockchip Camera Interface (CIF) platform driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
new file mode 100644
index 000000000000..523103872b7a
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#include "rkcif-common.h"
+#include "rkcif-interface.h"
+
+static inline struct rkcif_interface *to_rkcif_interface(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct rkcif_interface, sd);
+}
+
+static const struct media_entity_operations rkcif_interface_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+static int rkcif_interface_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct rkcif_interface *interface = to_rkcif_interface(sd);
+ const struct rkcif_input_fmt *input;
+ struct v4l2_mbus_framefmt *sink, *src;
+ struct v4l2_rect *crop;
+ u32 other_pad, other_stream;
+ int ret;
+
+ /* the format on the source pad always matches the sink pad */
+ if (format->pad == RKCIF_IF_PAD_SRC)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ input = rkcif_interface_find_input_fmt(interface, true,
+ format->format.code);
+ format->format.code = input->mbus_code;
+
+ sink = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!sink)
+ return -EINVAL;
+
+ *sink = format->format;
+
+ /* propagate the format to the source pad */
+ src = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!src)
+ return -EINVAL;
+
+ *src = *sink;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ format->pad, format->stream,
+ &other_pad, &other_stream);
+ if (ret)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
+ if (!crop)
+ return -EINVAL;
+
+ /* reset crop */
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = sink->width;
+ crop->height = sink->height;
+
+ return 0;
+}
+
+static int rkcif_interface_get_sel(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *sink;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ if (sel->pad != RKCIF_IF_PAD_SRC)
+ return -EINVAL;
+
+ sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad,
+ sel->stream);
+ if (!sink)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ if (!crop)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = sink->width;
+ sel->r.height = sink->height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *crop;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int rkcif_interface_set_sel(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *sink, *src;
+ struct v4l2_rect *crop;
+
+ if (sel->pad != RKCIF_IF_PAD_SRC || sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ sink = v4l2_subdev_state_get_opposite_stream_format(state, sel->pad,
+ sel->stream);
+ if (!sink)
+ return -EINVAL;
+
+ src = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
+ if (!src)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ if (!crop)
+ return -EINVAL;
+
+ *crop = sel->r;
+
+ src->height = sel->r.height;
+ src->width = sel->r.width;
+
+ return 0;
+}
+
+static int rkcif_interface_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < routing->num_routes; i++) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (route->source_stream >= RKCIF_ID_MAX)
+ return -EINVAL;
+ }
+
+ ret = v4l2_subdev_set_routing(sd, state, routing);
+
+ return ret;
+}
+
+static int rkcif_interface_apply_crop(struct rkcif_stream *stream,
+ struct v4l2_subdev_state *state)
+{
+ struct rkcif_interface *interface = stream->interface;
+ struct v4l2_rect *crop;
+
+ crop = v4l2_subdev_state_get_crop(state, RKCIF_IF_PAD_SRC, stream->id);
+ if (!crop)
+ return -EINVAL;
+
+ if (interface->set_crop)
+ interface->set_crop(stream, crop->left, crop->top);
+
+ return 0;
+}
+
+static int rkcif_interface_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct rkcif_interface *interface = to_rkcif_interface(sd);
+ struct rkcif_stream *stream;
+ struct v4l2_subdev_route *route;
+ struct v4l2_subdev *remote_sd;
+ struct media_pad *remote_pad;
+ u64 mask;
+
+ remote_pad =
+ media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]);
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ /* DVP has one crop setting for all IDs */
+ if (interface->type == RKCIF_IF_DVP) {
+ stream = &interface->streams[RKCIF_ID0];
+ rkcif_interface_apply_crop(stream, state);
+ } else {
+ for_each_active_route(&state->routing, route) {
+ stream = &interface->streams[route->sink_stream];
+ rkcif_interface_apply_crop(stream, state);
+ }
+ }
+
+ mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK,
+ RKCIF_IF_PAD_SRC, &streams_mask);
+
+ return v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
+}
+
+static int rkcif_interface_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct v4l2_subdev *remote_sd;
+ struct media_pad *remote_pad;
+ u64 mask;
+
+ remote_pad =
+ media_pad_remote_pad_first(&sd->entity.pads[RKCIF_IF_PAD_SINK]);
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ mask = v4l2_subdev_state_xlate_streams(state, RKCIF_IF_PAD_SINK,
+ RKCIF_IF_PAD_SRC, &streams_mask);
+
+ return v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
+}
+
+static const struct v4l2_subdev_pad_ops rkcif_interface_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = rkcif_interface_set_fmt,
+ .get_selection = rkcif_interface_get_sel,
+ .set_selection = rkcif_interface_set_sel,
+ .set_routing = rkcif_interface_set_routing,
+ .enable_streams = rkcif_interface_enable_streams,
+ .disable_streams = rkcif_interface_disable_streams,
+};
+
+static const struct v4l2_subdev_ops rkcif_interface_ops = {
+ .pad = &rkcif_interface_pad_ops,
+};
+
+static int rkcif_interface_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct rkcif_interface *interface = to_rkcif_interface(sd);
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = RKCIF_IF_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = RKCIF_IF_PAD_SRC,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+ struct v4l2_subdev_krouting routing = {
+ .len_routes = ARRAY_SIZE(routes),
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+ const struct v4l2_mbus_framefmt dvp_default_format = {
+ .width = 3840,
+ .height = 2160,
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_REC709,
+ .ycbcr_enc = V4L2_YCBCR_ENC_709,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_NONE,
+ };
+ const struct v4l2_mbus_framefmt mipi_default_format = {
+ .width = 3840,
+ .height = 2160,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_NONE,
+ };
+ const struct v4l2_mbus_framefmt *default_format;
+ int ret;
+
+ default_format = (interface->type == RKCIF_IF_DVP) ?
+ &dvp_default_format :
+ &mipi_default_format;
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
+ default_format);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_internal_ops rkcif_interface_internal_ops = {
+ .init_state = rkcif_interface_init_state,
+};
+
+static int rkcif_interface_add(struct rkcif_interface *interface)
+{
+ struct rkcif_device *rkcif = interface->rkcif;
+ struct rkcif_remote *remote;
+ struct v4l2_async_notifier *ntf = &rkcif->notifier;
+ struct v4l2_fwnode_endpoint *vep = &interface->vep;
+ struct device *dev = rkcif->dev;
+ struct fwnode_handle *ep;
+ u32 dvp_clk_delay = 0;
+ int ret;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), interface->index,
+ 0, 0);
+ if (!ep)
+ return -ENODEV;
+
+ vep->bus_type = V4L2_MBUS_UNKNOWN;
+ ret = v4l2_fwnode_endpoint_parse(ep, vep);
+ if (ret)
+ goto complete;
+
+ if (interface->type == RKCIF_IF_DVP) {
+ if (vep->bus_type != V4L2_MBUS_BT656 &&
+ vep->bus_type != V4L2_MBUS_PARALLEL) {
+ ret = dev_err_probe(dev, -EINVAL,
+ "unsupported bus type\n");
+ goto complete;
+ }
+
+ fwnode_property_read_u32(ep, "rockchip,dvp-clk-delay",
+ &dvp_clk_delay);
+ interface->dvp.dvp_clk_delay = dvp_clk_delay;
+ }
+
+ remote = v4l2_async_nf_add_fwnode_remote(ntf, ep, struct rkcif_remote);
+ if (IS_ERR(remote)) {
+ ret = PTR_ERR(remote);
+ goto complete;
+ }
+
+ remote->interface = interface;
+ interface->remote = remote;
+ interface->status = RKCIF_IF_ACTIVE;
+ ret = 0;
+
+complete:
+ fwnode_handle_put(ep);
+
+ return ret;
+}
+
+int rkcif_interface_register(struct rkcif_device *rkcif,
+ struct rkcif_interface *interface)
+{
+ struct media_pad *pads = interface->pads;
+ struct v4l2_subdev *sd = &interface->sd;
+ int ret;
+
+ interface->rkcif = rkcif;
+
+ v4l2_subdev_init(sd, &rkcif_interface_ops);
+ sd->dev = rkcif->dev;
+ sd->entity.ops = &rkcif_interface_media_ops;
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
+ sd->internal_ops = &rkcif_interface_internal_ops;
+ sd->owner = THIS_MODULE;
+
+ if (interface->type == RKCIF_IF_DVP)
+ snprintf(sd->name, sizeof(sd->name), "rkcif-dvp0");
+ else if (interface->type == RKCIF_IF_MIPI)
+ snprintf(sd->name, sizeof(sd->name), "rkcif-mipi%d",
+ interface->index - RKCIF_MIPI_BASE);
+
+ pads[RKCIF_IF_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ pads[RKCIF_IF_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, RKCIF_IF_PAD_MAX, pads);
+ if (ret)
+ goto err;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(&rkcif->v4l2_dev, sd);
+ if (ret) {
+ dev_err(sd->dev, "failed to register subdev\n");
+ goto err_subdev_cleanup;
+ }
+
+ ret = rkcif_interface_add(interface);
+ if (ret)
+ goto err_subdev_unregister;
+
+ return 0;
+
+err_subdev_unregister:
+ v4l2_device_unregister_subdev(sd);
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
+err:
+ return ret;
+}
+
+void rkcif_interface_unregister(struct rkcif_interface *interface)
+{
+ struct v4l2_subdev *sd = &interface->sd;
+
+ if (interface->status != RKCIF_IF_ACTIVE)
+ return;
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+}
+
+const struct rkcif_input_fmt *
+rkcif_interface_find_input_fmt(struct rkcif_interface *interface, bool ret_def,
+ u32 mbus_code)
+{
+ const struct rkcif_input_fmt *fmt;
+
+ WARN_ON(interface->in_fmts_num == 0);
+
+ for (unsigned int i = 0; i < interface->in_fmts_num; i++) {
+ fmt = &interface->in_fmts[i];
+ if (fmt->mbus_code == mbus_code)
+ return fmt;
+ }
+ if (ret_def)
+ return &interface->in_fmts[0];
+ else
+ return NULL;
+}
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.h b/drivers/media/platform/rockchip/rkcif/rkcif-interface.h
new file mode 100644
index 000000000000..f13aa28b6fa7
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Abstraction for the INTERFACE and CROP parts of the different CIF variants.
+ * They shall be represented as V4L2 subdevice with one sink pad and one
+ * source pad. The sink pad is connected to a subdevice: either the subdevice
+ * provided by the driver of the companion chip connected to the DVP, or the
+ * subdevice provided by the MIPI CSI-2 receiver driver. The source pad is
+ * to V4l2 device(s) provided by one or many instance(s) of the DMA
+ * abstraction.
+ *
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#ifndef _RKCIF_INTERFACE_H
+#define _RKCIF_INTERFACE_H
+
+#include "rkcif-common.h"
+
+int rkcif_interface_register(struct rkcif_device *rkcif,
+ struct rkcif_interface *interface);
+
+void rkcif_interface_unregister(struct rkcif_interface *interface);
+
+const struct rkcif_input_fmt *
+rkcif_interface_find_input_fmt(struct rkcif_interface *interface, bool ret_def,
+ u32 mbus_code);
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-regs.h b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
new file mode 100644
index 000000000000..3cf7ee19de30
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-regs.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2023 Mehdi Djait <mehdi.djait@bootlin.com>
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ */
+
+#ifndef _RKCIF_REGS_H
+#define _RKCIF_REGS_H
+
+#define RKCIF_REGISTER_NOTSUPPORTED 0x420000
+#define RKCIF_FETCH_Y(VAL) ((VAL) & 0x1fff)
+#define RKCIF_XY_COORD(x, y) (((y) << 16) | (x))
+
+/* DVP register contents */
+#define RKCIF_CTRL_ENABLE_CAPTURE BIT(0)
+#define RKCIF_CTRL_MODE_PINGPONG BIT(1)
+#define RKCIF_CTRL_MODE_LINELOOP BIT(2)
+#define RKCIF_CTRL_AXI_BURST_16 (0xf << 12)
+
+#define RKCIF_INTEN_FRAME_END_EN BIT(0)
+#define RKCIF_INTEN_LINE_ERR_EN BIT(2)
+#define RKCIF_INTEN_BUS_ERR_EN BIT(6)
+#define RKCIF_INTEN_SCL_ERR_EN BIT(7)
+#define RKCIF_INTEN_PST_INF_FRAME_END_EN BIT(9)
+
+#define RKCIF_INTSTAT_CLS 0x3ff
+#define RKCIF_INTSTAT_FRAME_END BIT(0)
+#define RKCIF_INTSTAT_LINE_END BIT(1)
+#define RKCIF_INTSTAT_LINE_ERR BIT(2)
+#define RKCIF_INTSTAT_PIX_ERR BIT(3)
+#define RKCIF_INTSTAT_DFIFO_OF BIT(5)
+#define RKCIF_INTSTAT_BUS_ERR BIT(6)
+#define RKCIF_INTSTAT_PRE_INF_FRAME_END BIT(8)
+#define RKCIF_INTSTAT_PST_INF_FRAME_END BIT(9)
+#define RKCIF_INTSTAT_FRAME_END_CLR BIT(0)
+#define RKCIF_INTSTAT_LINE_END_CLR BIT(1)
+#define RKCIF_INTSTAT_LINE_ERR_CLR BIT(2)
+#define RKCIF_INTSTAT_PST_INF_FRAME_END_CLR BIT(9)
+#define RKCIF_INTSTAT_ERR 0xfc
+
+#define RKCIF_FRAME_STAT_CLS 0x00
+#define RKCIF_FRAME_FRM0_STAT_CLS 0x20
+
+#define RKCIF_FORMAT_VSY_HIGH_ACTIVE BIT(0)
+#define RKCIF_FORMAT_HSY_LOW_ACTIVE BIT(1)
+
+#define RKCIF_FORMAT_INPUT_MODE_YUV (0x00 << 2)
+#define RKCIF_FORMAT_INPUT_MODE_PAL (0x02 << 2)
+#define RKCIF_FORMAT_INPUT_MODE_NTSC (0x03 << 2)
+#define RKCIF_FORMAT_INPUT_MODE_BT1120 (0x07 << 2)
+#define RKCIF_FORMAT_INPUT_MODE_RAW (0x04 << 2)
+#define RKCIF_FORMAT_INPUT_MODE_JPEG (0x05 << 2)
+#define RKCIF_FORMAT_INPUT_MODE_MIPI (0x06 << 2)
+
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_UYVY (0x00 << 5)
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_YVYU (0x01 << 5)
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_VYUY (0x02 << 5)
+#define RKCIF_FORMAT_YUV_INPUT_ORDER_YUYV (0x03 << 5)
+#define RKCIF_FORMAT_YUV_INPUT_422 (0x00 << 7)
+#define RKCIF_FORMAT_YUV_INPUT_420 BIT(7)
+
+#define RKCIF_FORMAT_INPUT_420_ORDER_ODD BIT(8)
+
+#define RKCIF_FORMAT_CCIR_INPUT_ORDER_EVEN BIT(9)
+
+#define RKCIF_FORMAT_RAW_DATA_WIDTH_8 (0x00 << 11)
+#define RKCIF_FORMAT_RAW_DATA_WIDTH_10 (0x01 << 11)
+#define RKCIF_FORMAT_RAW_DATA_WIDTH_12 (0x02 << 11)
+
+#define RKCIF_FORMAT_YUV_OUTPUT_422 (0x00 << 16)
+#define RKCIF_FORMAT_YUV_OUTPUT_420 BIT(16)
+
+#define RKCIF_FORMAT_OUTPUT_420_ORDER_EVEN (0x00 << 17)
+#define RKCIF_FORMAT_OUTPUT_420_ORDER_ODD BIT(17)
+
+#define RKCIF_FORMAT_RAWD_DATA_LITTLE_ENDIAN (0x00 << 18)
+#define RKCIF_FORMAT_RAWD_DATA_BIG_ENDIAN BIT(18)
+
+#define RKCIF_FORMAT_UV_STORAGE_ORDER_UVUV (0x00 << 19)
+#define RKCIF_FORMAT_UV_STORAGE_ORDER_VUVU BIT(19)
+
+#define RKCIF_FORMAT_BT1120_CLOCK_SINGLE_EDGES (0x00 << 24)
+#define RKCIF_FORMAT_BT1120_CLOCK_DOUBLE_EDGES BIT(24)
+#define RKCIF_FORMAT_BT1120_TRANSMIT_INTERFACE (0x00 << 25)
+#define RKCIF_FORMAT_BT1120_TRANSMIT_PROGRESS BIT(25)
+#define RKCIF_FORMAT_BT1120_YC_SWAP BIT(26)
+
+#define RKCIF_SCL_CTRL_ENABLE_SCL_DOWN BIT(0)
+#define RKCIF_SCL_CTRL_ENABLE_SCL_UP BIT(1)
+#define RKCIF_SCL_CTRL_ENABLE_YUV_16BIT_BYPASS BIT(4)
+#define RKCIF_SCL_CTRL_ENABLE_RAW_16BIT_BYPASS BIT(5)
+#define RKCIF_SCL_CTRL_ENABLE_32BIT_BYPASS BIT(6)
+#define RKCIF_SCL_CTRL_DISABLE_32BIT_BYPASS (0x00 << 6)
+
+#define RKCIF_INTSTAT_F0_READY BIT(0)
+#define RKCIF_INTSTAT_F1_READY BIT(1)
+
+/* GRF register offsets and contents */
+#define RK3568_GRF_VI_CON0 0x340
+#define RK3568_GRF_VI_CON1 0x344
+#define RK3568_GRF_VI_STATUS0 0x348
+
+#define RK3568_GRF_VI_CON1_CIF_DATAPATH BIT(9)
+#define RK3568_GRF_VI_CON1_CIF_CLK_DELAYNUM GENMASK(6, 0)
+
+#define RK3568_GRF_WRITE_ENABLE(x) ((x) << 16)
+
+enum rkcif_dvp_register_index {
+ RKCIF_DVP_CTRL,
+ RKCIF_DVP_INTEN,
+ RKCIF_DVP_INTSTAT,
+ RKCIF_DVP_FOR,
+ RKCIF_DVP_LINE_NUM_ADDR,
+ RKCIF_DVP_FRM0_ADDR_Y,
+ RKCIF_DVP_FRM0_ADDR_UV,
+ RKCIF_DVP_FRM1_ADDR_Y,
+ RKCIF_DVP_FRM1_ADDR_UV,
+ RKCIF_DVP_VIR_LINE_WIDTH,
+ RKCIF_DVP_SET_SIZE,
+ RKCIF_DVP_SCL_CTRL,
+ RKCIF_DVP_CROP,
+ RKCIF_DVP_FRAME_STATUS,
+ RKCIF_DVP_LAST_LINE,
+ RKCIF_DVP_LAST_PIX,
+ RKCIF_DVP_REGISTER_MAX
+};
+
+enum rkcif_mipi_register_index {
+ RKCIF_MIPI_CTRL,
+ RKCIF_MIPI_INTEN,
+ RKCIF_MIPI_INTSTAT,
+ RKCIF_MIPI_REGISTER_MAX
+};
+
+enum rkcif_mipi_id_register_index {
+ RKCIF_MIPI_CTRL0,
+ RKCIF_MIPI_CTRL1,
+ RKCIF_MIPI_FRAME0_ADDR_Y,
+ RKCIF_MIPI_FRAME0_ADDR_UV,
+ RKCIF_MIPI_FRAME0_VLW_Y,
+ RKCIF_MIPI_FRAME0_VLW_UV,
+ RKCIF_MIPI_FRAME1_ADDR_Y,
+ RKCIF_MIPI_FRAME1_ADDR_UV,
+ RKCIF_MIPI_FRAME1_VLW_Y,
+ RKCIF_MIPI_FRAME1_VLW_UV,
+ RKCIF_MIPI_CROP_START,
+ RKCIF_MIPI_ID_REGISTER_MAX
+};
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-stream.c b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
new file mode 100644
index 000000000000..e00010a91e8b
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.c
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "rkcif-common.h"
+#include "rkcif-stream.h"
+
+#define CIF_REQ_BUFS_MIN 1
+#define CIF_MIN_WIDTH 64
+#define CIF_MIN_HEIGHT 64
+#define CIF_MAX_WIDTH 8192
+#define CIF_MAX_HEIGHT 8192
+
+static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb)
+{
+ return container_of(vb, struct rkcif_buffer, vb);
+}
+
+static inline struct rkcif_stream *to_rkcif_stream(struct video_device *vdev)
+{
+ return container_of(vdev, struct rkcif_stream, vdev);
+}
+
+static struct rkcif_buffer *rkcif_stream_pop_buffer(struct rkcif_stream *stream)
+{
+ struct rkcif_buffer *buffer;
+
+ guard(spinlock_irqsave)(&stream->driver_queue_lock);
+
+ if (list_empty(&stream->driver_queue))
+ return NULL;
+
+ buffer = list_first_entry(&stream->driver_queue, struct rkcif_buffer,
+ queue);
+ list_del(&buffer->queue);
+
+ return buffer;
+}
+
+static void rkcif_stream_push_buffer(struct rkcif_stream *stream,
+ struct rkcif_buffer *buffer)
+{
+ guard(spinlock_irqsave)(&stream->driver_queue_lock);
+
+ list_add_tail(&buffer->queue, &stream->driver_queue);
+}
+
+static inline void rkcif_stream_return_buffer(struct rkcif_buffer *buffer,
+ enum vb2_buffer_state state)
+{
+ struct vb2_v4l2_buffer *vb = &buffer->vb;
+
+ vb2_buffer_done(&vb->vb2_buf, state);
+}
+
+static void rkcif_stream_complete_buffer(struct rkcif_stream *stream,
+ struct rkcif_buffer *buffer)
+{
+ struct vb2_v4l2_buffer *vb = &buffer->vb;
+
+ vb->vb2_buf.timestamp = ktime_get_ns();
+ vb->sequence = stream->frame_idx;
+ vb2_buffer_done(&vb->vb2_buf, VB2_BUF_STATE_DONE);
+ stream->frame_idx++;
+}
+
+void rkcif_stream_pingpong(struct rkcif_stream *stream)
+{
+ struct rkcif_buffer *buffer;
+
+ buffer = stream->buffers[stream->frame_phase];
+ if (!buffer->is_dummy)
+ rkcif_stream_complete_buffer(stream, buffer);
+
+ buffer = rkcif_stream_pop_buffer(stream);
+ if (buffer) {
+ stream->buffers[stream->frame_phase] = buffer;
+ stream->buffers[stream->frame_phase]->is_dummy = false;
+ } else {
+ stream->buffers[stream->frame_phase] = &stream->dummy.buffer;
+ stream->buffers[stream->frame_phase]->is_dummy = true;
+ dev_dbg(stream->rkcif->dev,
+ "no buffer available, frame will be dropped\n");
+ }
+
+ if (stream->queue_buffer)
+ stream->queue_buffer(stream, stream->frame_phase);
+
+ stream->frame_phase = 1 - stream->frame_phase;
+}
+
+static int rkcif_stream_init_buffers(struct rkcif_stream *stream)
+{
+ struct v4l2_pix_format_mplane *pix = &stream->pix;
+
+ stream->buffers[0] = rkcif_stream_pop_buffer(stream);
+ if (!stream->buffers[0])
+ goto err_buff_0;
+
+ stream->buffers[1] = rkcif_stream_pop_buffer(stream);
+ if (!stream->buffers[1])
+ goto err_buff_1;
+
+ if (stream->queue_buffer) {
+ stream->queue_buffer(stream, 0);
+ stream->queue_buffer(stream, 1);
+ }
+
+ stream->dummy.size = pix->num_planes * pix->plane_fmt[0].sizeimage;
+ stream->dummy.vaddr =
+ dma_alloc_attrs(stream->rkcif->dev, stream->dummy.size,
+ &stream->dummy.buffer.buff_addr[0], GFP_KERNEL,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+ if (!stream->dummy.vaddr)
+ goto err_dummy;
+
+ for (unsigned int i = 1; i < pix->num_planes; i++)
+ stream->dummy.buffer.buff_addr[i] =
+ stream->dummy.buffer.buff_addr[i - 1] +
+ pix->plane_fmt[i - 1].bytesperline * pix->height;
+
+ return 0;
+
+err_dummy:
+ rkcif_stream_return_buffer(stream->buffers[1], VB2_BUF_STATE_QUEUED);
+ stream->buffers[1] = NULL;
+
+err_buff_1:
+ rkcif_stream_return_buffer(stream->buffers[0], VB2_BUF_STATE_QUEUED);
+ stream->buffers[0] = NULL;
+err_buff_0:
+ return -EINVAL;
+}
+
+static void rkcif_stream_return_all_buffers(struct rkcif_stream *stream,
+ enum vb2_buffer_state state)
+{
+ struct rkcif_buffer *buffer;
+
+ if (stream->buffers[0] && !stream->buffers[0]->is_dummy) {
+ rkcif_stream_return_buffer(stream->buffers[0], state);
+ stream->buffers[0] = NULL;
+ }
+
+ if (stream->buffers[1] && !stream->buffers[1]->is_dummy) {
+ rkcif_stream_return_buffer(stream->buffers[1], state);
+ stream->buffers[1] = NULL;
+ }
+
+ while ((buffer = rkcif_stream_pop_buffer(stream)))
+ rkcif_stream_return_buffer(buffer, state);
+
+ if (stream->dummy.vaddr) {
+ dma_free_attrs(stream->rkcif->dev, stream->dummy.size,
+ stream->dummy.vaddr,
+ stream->dummy.buffer.buff_addr[0],
+ DMA_ATTR_NO_KERNEL_MAPPING);
+ stream->dummy.vaddr = NULL;
+ }
+}
+
+static int rkcif_stream_setup_queue(struct vb2_queue *queue,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkcif_stream *stream = queue->drv_priv;
+ struct v4l2_pix_format_mplane *pix = &stream->pix;
+
+ if (*num_planes) {
+ if (*num_planes != pix->num_planes)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < pix->num_planes; i++)
+ if (sizes[i] < pix->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ } else {
+ *num_planes = pix->num_planes;
+ for (unsigned int i = 0; i < pix->num_planes; i++)
+ sizes[i] = pix->plane_fmt[i].sizeimage;
+ }
+
+ return 0;
+}
+
+static int rkcif_stream_prepare_buffer(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkcif_buffer *buffer = to_rkcif_buffer(vbuf);
+ struct rkcif_stream *stream = vb->vb2_queue->drv_priv;
+ const struct rkcif_output_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix = &stream->pix;
+ unsigned int i;
+
+ memset(buffer->buff_addr, 0, sizeof(buffer->buff_addr));
+ for (i = 0; i < pix->num_planes; i++)
+ buffer->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ /* apply fallback for non-mplane formats, if required */
+ if (pix->num_planes == 1) {
+ fmt = rkcif_stream_find_output_fmt(stream, true,
+ pix->pixelformat);
+ for (i = 1; i < fmt->cplanes; i++)
+ buffer->buff_addr[i] =
+ buffer->buff_addr[i - 1] +
+ pix->plane_fmt[i - 1].bytesperline *
+ pix->height;
+ }
+
+ for (i = 0; i < pix->num_planes; i++) {
+ unsigned long size = pix->plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < size) {
+ dev_err(stream->rkcif->dev,
+ "user buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static void rkcif_stream_queue_buffer(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkcif_buffer *buffer = to_rkcif_buffer(vbuf);
+ struct rkcif_stream *stream = vb->vb2_queue->drv_priv;
+
+ rkcif_stream_push_buffer(stream, buffer);
+}
+
+static int rkcif_stream_start_streaming(struct vb2_queue *queue,
+ unsigned int count)
+{
+ struct rkcif_stream *stream = queue->drv_priv;
+ struct rkcif_device *rkcif = stream->rkcif;
+ u64 mask;
+ int ret;
+
+ stream->frame_idx = 0;
+ stream->frame_phase = 0;
+
+ ret = video_device_pipeline_start(&stream->vdev, &stream->pipeline);
+ if (ret) {
+ dev_err(rkcif->dev, "failed to start pipeline %d\n", ret);
+ goto err_out;
+ }
+
+ ret = pm_runtime_resume_and_get(rkcif->dev);
+ if (ret < 0) {
+ dev_err(rkcif->dev, "failed to get runtime pm, %d\n", ret);
+ goto err_pipeline_stop;
+ }
+
+ ret = rkcif_stream_init_buffers(stream);
+ if (ret)
+ goto err_runtime_put;
+
+ if (stream->start_streaming) {
+ ret = stream->start_streaming(stream);
+ if (ret < 0)
+ goto err_runtime_put;
+ }
+
+ mask = BIT_ULL(stream->id);
+ ret = v4l2_subdev_enable_streams(&stream->interface->sd,
+ RKCIF_IF_PAD_SRC, mask);
+ if (ret < 0)
+ goto err_stop_stream;
+
+ return 0;
+
+err_stop_stream:
+ if (stream->stop_streaming)
+ stream->stop_streaming(stream);
+err_runtime_put:
+ pm_runtime_put(rkcif->dev);
+err_pipeline_stop:
+ video_device_pipeline_stop(&stream->vdev);
+err_out:
+ rkcif_stream_return_all_buffers(stream, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void rkcif_stream_stop_streaming(struct vb2_queue *queue)
+{
+ struct rkcif_stream *stream = queue->drv_priv;
+ struct rkcif_device *rkcif = stream->rkcif;
+ u64 mask;
+ int ret;
+
+ mask = BIT_ULL(stream->id);
+ v4l2_subdev_disable_streams(&stream->interface->sd, RKCIF_IF_PAD_SRC,
+ mask);
+
+ stream->stopping = true;
+ ret = wait_event_timeout(stream->wq_stopped, !stream->stopping,
+ msecs_to_jiffies(1000));
+
+ if (!ret && stream->stop_streaming)
+ stream->stop_streaming(stream);
+
+ pm_runtime_put(rkcif->dev);
+
+ rkcif_stream_return_all_buffers(stream, VB2_BUF_STATE_ERROR);
+
+ video_device_pipeline_stop(&stream->vdev);
+}
+
+static const struct vb2_ops rkcif_stream_vb2_ops = {
+ .queue_setup = rkcif_stream_setup_queue,
+ .buf_prepare = rkcif_stream_prepare_buffer,
+ .buf_queue = rkcif_stream_queue_buffer,
+ .start_streaming = rkcif_stream_start_streaming,
+ .stop_streaming = rkcif_stream_stop_streaming,
+};
+
+static int rkcif_stream_fill_format(struct rkcif_stream *stream,
+ struct v4l2_pix_format_mplane *pix)
+{
+ const struct rkcif_output_fmt *fmt;
+ u32 height, width;
+ int ret;
+
+ fmt = rkcif_stream_find_output_fmt(stream, true, pix->pixelformat);
+ height = clamp_t(u32, pix->height, CIF_MIN_HEIGHT, CIF_MAX_HEIGHT);
+ width = clamp_t(u32, pix->width, CIF_MIN_WIDTH, CIF_MAX_WIDTH);
+ ret = v4l2_fill_pixfmt_mp(pix, fmt->fourcc, width, height);
+ if (ret)
+ return ret;
+
+ pix->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int rkcif_stream_try_format(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rkcif_stream *stream = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+
+ return rkcif_stream_fill_format(stream, pix);
+}
+
+static int rkcif_stream_set_format(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct rkcif_stream *stream = video_drvdata(file);
+ struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
+ int ret;
+
+ if (vb2_is_busy(&stream->buf_queue))
+ return -EBUSY;
+
+ ret = rkcif_stream_try_format(file, priv, f);
+ if (ret)
+ return ret;
+
+ stream->pix = *pix;
+
+ return 0;
+}
+
+static int rkcif_stream_get_format(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rkcif_stream *stream = video_drvdata(file);
+
+ f->fmt.pix_mp = stream->pix;
+
+ return 0;
+}
+
+static int rkcif_stream_enum_formats(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rkcif_stream *stream = video_drvdata(file);
+
+ if (f->index >= stream->out_fmts_num)
+ return -EINVAL;
+
+ f->pixelformat = stream->out_fmts[f->index].fourcc;
+
+ return 0;
+}
+
+static int rkcif_stream_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct rkcif_stream *stream = video_drvdata(file);
+
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ if (!rkcif_stream_find_output_fmt(stream, false, fsize->pixel_format))
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = CIF_MIN_WIDTH;
+ fsize->stepwise.max_width = CIF_MAX_WIDTH;
+ fsize->stepwise.step_width = 8;
+ fsize->stepwise.min_height = CIF_MIN_HEIGHT;
+ fsize->stepwise.max_height = CIF_MAX_HEIGHT;
+ fsize->stepwise.step_height = 8;
+
+ return 0;
+}
+
+static int rkcif_stream_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct rkcif_stream *stream = video_drvdata(file);
+ struct device *dev = stream->rkcif->dev;
+
+ strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
+ strscpy(cap->card, dev->driver->name, sizeof(cap->card));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rkcif_stream_ioctl_ops = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_try_fmt_vid_cap_mplane = rkcif_stream_try_format,
+ .vidioc_s_fmt_vid_cap_mplane = rkcif_stream_set_format,
+ .vidioc_g_fmt_vid_cap_mplane = rkcif_stream_get_format,
+ .vidioc_enum_fmt_vid_cap = rkcif_stream_enum_formats,
+ .vidioc_enum_framesizes = rkcif_stream_enum_framesizes,
+ .vidioc_querycap = rkcif_stream_querycap,
+};
+
+static int rkcif_stream_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct v4l2_mbus_framefmt *source_fmt;
+ struct v4l2_subdev *sd;
+ struct v4l2_subdev_state *state;
+ struct rkcif_stream *stream = to_rkcif_stream(vdev);
+ int ret = -EINVAL;
+
+ if (!media_entity_remote_source_pad_unique(link->sink->entity))
+ return -ENOTCONN;
+
+ sd = media_entity_to_v4l2_subdev(link->source->entity);
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ source_fmt = v4l2_subdev_state_get_format(state, link->source->index,
+ stream->id);
+ if (!source_fmt)
+ goto out;
+
+ if (source_fmt->height != stream->pix.height ||
+ source_fmt->width != stream->pix.width) {
+ dev_dbg(stream->rkcif->dev,
+ "link '%s':%u -> '%s':%u not valid: %ux%u != %ux%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ source_fmt->width, source_fmt->height,
+ stream->pix.width, stream->pix.height);
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ v4l2_subdev_unlock_state(state);
+ return ret;
+}
+
+static const struct media_entity_operations rkcif_stream_media_ops = {
+ .link_validate = rkcif_stream_link_validate,
+};
+
+static const struct v4l2_file_operations rkcif_stream_file_ops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static int rkcif_stream_init_vb2_queue(struct vb2_queue *q,
+ struct rkcif_stream *stream)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stream;
+ q->ops = &rkcif_stream_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct rkcif_buffer);
+ q->min_queued_buffers = CIF_REQ_BUFS_MIN;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &stream->vlock;
+ q->dev = stream->rkcif->dev;
+
+ return vb2_queue_init(q);
+}
+
+int rkcif_stream_register(struct rkcif_device *rkcif,
+ struct rkcif_stream *stream)
+{
+ struct rkcif_interface *interface = stream->interface;
+ struct v4l2_device *v4l2_dev = &rkcif->v4l2_dev;
+ struct video_device *vdev = &stream->vdev;
+ u32 link_flags = 0;
+ int ret;
+
+ stream->rkcif = rkcif;
+
+ INIT_LIST_HEAD(&stream->driver_queue);
+ spin_lock_init(&stream->driver_queue_lock);
+
+ init_waitqueue_head(&stream->wq_stopped);
+
+ mutex_init(&stream->vlock);
+
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING |
+ V4L2_CAP_IO_MC;
+ vdev->entity.ops = &rkcif_stream_media_ops;
+ vdev->fops = &rkcif_stream_file_ops;
+ vdev->ioctl_ops = &rkcif_stream_ioctl_ops;
+ vdev->lock = &stream->vlock;
+ vdev->minor = -1;
+ vdev->release = video_device_release_empty;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ video_set_drvdata(vdev, stream);
+
+ stream->pad.flags = MEDIA_PAD_FL_SINK;
+
+ stream->pix.height = CIF_MIN_HEIGHT;
+ stream->pix.width = CIF_MIN_WIDTH;
+ rkcif_stream_fill_format(stream, &stream->pix);
+
+ rkcif_stream_init_vb2_queue(&stream->buf_queue, stream);
+
+ vdev->queue = &stream->buf_queue;
+ if (interface->type == RKCIF_IF_DVP)
+ snprintf(vdev->name, sizeof(vdev->name), "rkcif-dvp0-id%d",
+ stream->id);
+ else if (interface->type == RKCIF_IF_MIPI)
+ snprintf(vdev->name, sizeof(vdev->name), "rkcif-mipi%d-id%d",
+ interface->index - RKCIF_MIPI_BASE, stream->id);
+
+ ret = media_entity_pads_init(&vdev->entity, 1, &stream->pad);
+ if (ret < 0) {
+ dev_err(rkcif->dev,
+ "failed to initialize stream media pad: %d\n", ret);
+ return ret;
+ }
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ dev_err(rkcif->dev, "failed to register video device: %d\n",
+ ret);
+ goto err_media_entity_cleanup;
+ }
+
+ /* enable only stream ID0 by default */
+ if (stream->id == RKCIF_ID0)
+ link_flags |= MEDIA_LNK_FL_ENABLED;
+
+ ret = media_create_pad_link(&interface->sd.entity, RKCIF_IF_PAD_SRC,
+ &stream->vdev.entity, 0, link_flags);
+ if (ret) {
+ dev_err(rkcif->dev, "failed to link stream media pad: %d\n",
+ ret);
+ goto err_video_unregister;
+ }
+
+ v4l2_info(v4l2_dev, "registered %s as /dev/video%d\n", vdev->name,
+ vdev->num);
+
+ return 0;
+
+err_video_unregister:
+ video_unregister_device(&stream->vdev);
+err_media_entity_cleanup:
+ media_entity_cleanup(&stream->vdev.entity);
+ return ret;
+}
+
+void rkcif_stream_unregister(struct rkcif_stream *stream)
+{
+ video_unregister_device(&stream->vdev);
+ media_entity_cleanup(&stream->vdev.entity);
+}
+
+const struct rkcif_output_fmt *
+rkcif_stream_find_output_fmt(struct rkcif_stream *stream, bool ret_def,
+ u32 pixelfmt)
+{
+ const struct rkcif_output_fmt *fmt;
+
+ WARN_ON(stream->out_fmts_num == 0);
+
+ for (unsigned int i = 0; i < stream->out_fmts_num; i++) {
+ fmt = &stream->out_fmts[i];
+ if (fmt->fourcc == pixelfmt)
+ return fmt;
+ }
+
+ if (ret_def)
+ return &stream->out_fmts[0];
+ else
+ return NULL;
+}
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-stream.h b/drivers/media/platform/rockchip/rkcif/rkcif-stream.h
new file mode 100644
index 000000000000..590faf5d1a87
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-stream.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Camera Interface (CIF) Driver
+ *
+ * Abstraction for the DMA part and the ping-pong scheme (a double-buffering
+ * mechanism) of the different CIF variants.
+ * Each stream is represented as V4L2 device whose corresponding media entity
+ * has one sink pad.
+ * The sink pad is connected to an instance of the INTERFACE/CROP abstraction
+ * in rkcif-interface.c.
+ *
+ * Copyright (C) 2025 Michael Riesch <michael.riesch@wolfvision.net>
+ * Copyright (C) 2025 Collabora, Ltd.
+ */
+
+#ifndef _RKCIF_STREAM_H
+#define _RKCIF_STREAM_H
+
+#include "rkcif-common.h"
+
+void rkcif_stream_pingpong(struct rkcif_stream *stream);
+
+int rkcif_stream_register(struct rkcif_device *rkcif,
+ struct rkcif_stream *stream);
+
+void rkcif_stream_unregister(struct rkcif_stream *stream);
+
+const struct rkcif_output_fmt *
+rkcif_stream_find_output_fmt(struct rkcif_stream *stream, bool ret_def,
+ u32 pixelfmt);
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkisp1/Kconfig b/drivers/media/platform/rockchip/rkisp1/Kconfig
index 731c9acbf6ef..f53eb1f3f3e7 100644
--- a/drivers/media/platform/rockchip/rkisp1/Kconfig
+++ b/drivers/media/platform/rockchip/rkisp1/Kconfig
@@ -10,6 +10,7 @@ config VIDEO_ROCKCHIP_ISP1
select VIDEOBUF2_VMALLOC
select V4L2_FWNODE
select GENERIC_PHY_MIPI_DPHY
+ select V4L2_ISP
default n
help
Enable this to support the Image Signal Processing (ISP) module
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
index 6028ecdd23de..5e6a4d5f6fd1 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
@@ -227,6 +227,7 @@ struct rkisp1_isp {
struct media_pad pads[RKISP1_ISP_PAD_MAX];
const struct rkisp1_mbus_info *sink_fmt;
__u32 frame_sequence;
+ bool frame_active;
};
/*
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
index 841e58c20f7f..ddc6182f3e4b 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
@@ -368,8 +368,8 @@ static int rkisp1_csi_s_stream(struct v4l2_subdev *sd, int enable)
source_pad = media_entity_remote_source_pad_unique(&sd->entity);
if (IS_ERR(source_pad)) {
- dev_dbg(rkisp1->dev, "Failed to get source for CSI: %ld\n",
- PTR_ERR(source_pad));
+ dev_dbg(rkisp1->dev, "Failed to get source for CSI: %pe\n",
+ source_pad);
return -EPIPE;
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
index 8c29a1c9309a..2311672cedb1 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
@@ -936,8 +936,8 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable)
sink_pad = &isp->pads[RKISP1_ISP_PAD_SINK_VIDEO];
source_pad = media_pad_remote_pad_unique(sink_pad);
if (IS_ERR(source_pad)) {
- dev_dbg(rkisp1->dev, "Failed to get source for ISP: %ld\n",
- PTR_ERR(source_pad));
+ dev_dbg(rkisp1->dev, "Failed to get source for ISP: %pe\n",
+ source_pad);
return -EPIPE;
}
@@ -965,6 +965,7 @@ static int rkisp1_isp_s_stream(struct v4l2_subdev *sd, int enable)
}
isp->frame_sequence = -1;
+ isp->frame_active = false;
sd_state = v4l2_subdev_lock_and_get_active_state(sd);
@@ -1086,12 +1087,15 @@ void rkisp1_isp_unregister(struct rkisp1_device *rkisp1)
* Interrupt handlers
*/
-static void rkisp1_isp_queue_event_sof(struct rkisp1_isp *isp)
+static void rkisp1_isp_sof(struct rkisp1_isp *isp)
{
struct v4l2_event event = {
.type = V4L2_EVENT_FRAME_SYNC,
};
+ isp->frame_sequence++;
+ isp->frame_active = true;
+
event.u.frame_sync.frame_sequence = isp->frame_sequence;
v4l2_event_queue(isp->sd.devnode, &event);
}
@@ -1111,15 +1115,20 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
rkisp1_write(rkisp1, RKISP1_CIF_ISP_ICR, status);
- /* Vertical sync signal, starting generating new frame */
- if (status & RKISP1_CIF_ISP_V_START) {
- rkisp1->isp.frame_sequence++;
- rkisp1_isp_queue_event_sof(&rkisp1->isp);
+ /*
+ * Vertical sync signal, starting new frame. Defer handling of vsync
+ * after RKISP1_CIF_ISP_FRAME if the previous frame was not completed
+ * yet.
+ */
+ if (status & RKISP1_CIF_ISP_V_START && !rkisp1->isp.frame_active) {
+ status &= ~RKISP1_CIF_ISP_V_START;
+ rkisp1_isp_sof(&rkisp1->isp);
if (status & RKISP1_CIF_ISP_FRAME) {
WARN_ONCE(1, "irq delay is too long, buffers might not be in sync\n");
rkisp1->debug.irq_delay++;
}
}
+
if (status & RKISP1_CIF_ISP_PIC_SIZE_ERROR) {
/* Clear pic_size_error */
isp_err = rkisp1_read(rkisp1, RKISP1_CIF_ISP_ERR);
@@ -1138,6 +1147,7 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
if (status & RKISP1_CIF_ISP_FRAME) {
u32 isp_ris;
+ rkisp1->isp.frame_active = false;
rkisp1->debug.complete_frames++;
/* New frame from the sensor received */
@@ -1152,5 +1162,12 @@ irqreturn_t rkisp1_isp_isr(int irq, void *ctx)
rkisp1_params_isr(rkisp1);
}
+ /*
+ * Deferred handling of vsync if RKISP1_CIF_ISP_V_START and
+ * RKISP1_CIF_ISP_FRAME occurred in the same irq.
+ */
+ if (status & RKISP1_CIF_ISP_V_START)
+ rkisp1_isp_sof(&rkisp1->isp);
+
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index f1585f8fa0f4..c9f88635224c 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -6,12 +6,14 @@
*/
#include <linux/bitfield.h>
+#include <linux/build_bug.h>
#include <linux/math.h>
#include <linux/string.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-isp.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-vmalloc.h> /* for ISP params */
@@ -2097,122 +2099,133 @@ typedef void (*rkisp1_block_handler)(struct rkisp1_params *params,
const union rkisp1_ext_params_config *config);
static const struct rkisp1_ext_params_handler {
- size_t size;
rkisp1_block_handler handler;
unsigned int group;
unsigned int features;
} rkisp1_ext_params_handlers[] = {
[RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS] = {
- .size = sizeof(struct rkisp1_ext_params_bls_config),
.handler = rkisp1_ext_params_bls,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
.features = RKISP1_FEATURE_BLS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_DPCC] = {
- .size = sizeof(struct rkisp1_ext_params_dpcc_config),
.handler = rkisp1_ext_params_dpcc,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_SDG] = {
- .size = sizeof(struct rkisp1_ext_params_sdg_config),
.handler = rkisp1_ext_params_sdg,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_GAIN] = {
- .size = sizeof(struct rkisp1_ext_params_awb_gain_config),
.handler = rkisp1_ext_params_awbg,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_FLT] = {
- .size = sizeof(struct rkisp1_ext_params_flt_config),
.handler = rkisp1_ext_params_flt,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_BDM] = {
- .size = sizeof(struct rkisp1_ext_params_bdm_config),
.handler = rkisp1_ext_params_bdm,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_CTK] = {
- .size = sizeof(struct rkisp1_ext_params_ctk_config),
.handler = rkisp1_ext_params_ctk,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_GOC] = {
- .size = sizeof(struct rkisp1_ext_params_goc_config),
.handler = rkisp1_ext_params_goc,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF] = {
- .size = sizeof(struct rkisp1_ext_params_dpf_config),
.handler = rkisp1_ext_params_dpf,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF_STRENGTH] = {
- .size = sizeof(struct rkisp1_ext_params_dpf_strength_config),
.handler = rkisp1_ext_params_dpfs,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_CPROC] = {
- .size = sizeof(struct rkisp1_ext_params_cproc_config),
.handler = rkisp1_ext_params_cproc,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_IE] = {
- .size = sizeof(struct rkisp1_ext_params_ie_config),
.handler = rkisp1_ext_params_ie,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_LSC] = {
- .size = sizeof(struct rkisp1_ext_params_lsc_config),
.handler = rkisp1_ext_params_lsc,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_MEAS] = {
- .size = sizeof(struct rkisp1_ext_params_awb_meas_config),
.handler = rkisp1_ext_params_awbm,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_HST_MEAS] = {
- .size = sizeof(struct rkisp1_ext_params_hst_config),
.handler = rkisp1_ext_params_hstm,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_AEC_MEAS] = {
- .size = sizeof(struct rkisp1_ext_params_aec_config),
.handler = rkisp1_ext_params_aecm,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_AFC_MEAS] = {
- .size = sizeof(struct rkisp1_ext_params_afc_config),
.handler = rkisp1_ext_params_afcm,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS] = {
- .size = sizeof(struct rkisp1_ext_params_compand_bls_config),
.handler = rkisp1_ext_params_compand_bls,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
.features = RKISP1_FEATURE_COMPAND,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND] = {
- .size = sizeof(struct rkisp1_ext_params_compand_curve_config),
.handler = rkisp1_ext_params_compand_expand,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
.features = RKISP1_FEATURE_COMPAND,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS] = {
- .size = sizeof(struct rkisp1_ext_params_compand_curve_config),
.handler = rkisp1_ext_params_compand_compress,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
.features = RKISP1_FEATURE_COMPAND,
},
[RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR] = {
- .size = sizeof(struct rkisp1_ext_params_wdr_config),
.handler = rkisp1_ext_params_wdr,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
};
+#define RKISP1_PARAMS_BLOCK_INFO(block, data) \
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_ ## block] = { \
+ .size = sizeof(struct rkisp1_ext_params_ ## data ## _config), \
+ }
+
+static const struct v4l2_isp_params_block_type_info
+rkisp1_ext_params_block_types_info[] = {
+ RKISP1_PARAMS_BLOCK_INFO(BLS, bls),
+ RKISP1_PARAMS_BLOCK_INFO(DPCC, dpcc),
+ RKISP1_PARAMS_BLOCK_INFO(SDG, sdg),
+ RKISP1_PARAMS_BLOCK_INFO(AWB_GAIN, awb_gain),
+ RKISP1_PARAMS_BLOCK_INFO(FLT, flt),
+ RKISP1_PARAMS_BLOCK_INFO(BDM, bdm),
+ RKISP1_PARAMS_BLOCK_INFO(CTK, ctk),
+ RKISP1_PARAMS_BLOCK_INFO(GOC, goc),
+ RKISP1_PARAMS_BLOCK_INFO(DPF, dpf),
+ RKISP1_PARAMS_BLOCK_INFO(DPF_STRENGTH, dpf_strength),
+ RKISP1_PARAMS_BLOCK_INFO(CPROC, cproc),
+ RKISP1_PARAMS_BLOCK_INFO(IE, ie),
+ RKISP1_PARAMS_BLOCK_INFO(LSC, lsc),
+ RKISP1_PARAMS_BLOCK_INFO(AWB_MEAS, awb_meas),
+ RKISP1_PARAMS_BLOCK_INFO(HST_MEAS, hst),
+ RKISP1_PARAMS_BLOCK_INFO(AEC_MEAS, aec),
+ RKISP1_PARAMS_BLOCK_INFO(AFC_MEAS, afc),
+ RKISP1_PARAMS_BLOCK_INFO(COMPAND_BLS, compand_bls),
+ RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve),
+ RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve),
+ RKISP1_PARAMS_BLOCK_INFO(WDR, wdr),
+};
+
+static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) ==
+ ARRAY_SIZE(rkisp1_ext_params_block_types_info));
+
static void rkisp1_ext_params_config(struct rkisp1_params *params,
struct rkisp1_ext_params_cfg *cfg,
u32 block_group_mask)
@@ -2646,31 +2659,16 @@ static int rkisp1_params_prepare_ext_params(struct rkisp1_params *params,
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf);
- size_t header_size = offsetof(struct rkisp1_ext_params_cfg, data);
struct rkisp1_ext_params_cfg *cfg = params_buf->cfg;
size_t payload_size = vb2_get_plane_payload(vb, 0);
struct rkisp1_ext_params_cfg *usr_cfg =
vb2_plane_vaddr(&vbuf->vb2_buf, 0);
- size_t block_offset = 0;
- size_t cfg_size;
-
- /*
- * Validate the buffer payload size before copying the parameters. The
- * payload has to be smaller than the destination buffer size and larger
- * than the header size.
- */
- if (payload_size > params->metafmt->buffersize) {
- dev_dbg(params->rkisp1->dev,
- "Too large buffer payload size %zu\n", payload_size);
- return -EINVAL;
- }
+ int ret;
- if (payload_size < header_size) {
- dev_dbg(params->rkisp1->dev,
- "Buffer payload %zu smaller than header size %zu\n",
- payload_size, header_size);
- return -EINVAL;
- }
+ ret = v4l2_isp_params_validate_buffer_size(params->rkisp1->dev, vb,
+ params->metafmt->buffersize);
+ if (ret)
+ return ret;
/*
* Copy the parameters buffer to the internal scratch buffer to avoid
@@ -2678,71 +2676,10 @@ static int rkisp1_params_prepare_ext_params(struct rkisp1_params *params,
*/
memcpy(cfg, usr_cfg, payload_size);
- /* Only v1 is supported at the moment. */
- if (cfg->version != RKISP1_EXT_PARAM_BUFFER_V1) {
- dev_dbg(params->rkisp1->dev,
- "Unsupported extensible format version: %u\n",
- cfg->version);
- return -EINVAL;
- }
-
- /* Validate the size reported in the parameters buffer header. */
- cfg_size = header_size + cfg->data_size;
- if (cfg_size != payload_size) {
- dev_dbg(params->rkisp1->dev,
- "Data size %zu different than buffer payload size %zu\n",
- cfg_size, payload_size);
- return -EINVAL;
- }
-
- /* Walk the list of parameter blocks and validate them. */
- cfg_size = cfg->data_size;
- while (cfg_size >= sizeof(struct rkisp1_ext_params_block_header)) {
- const struct rkisp1_ext_params_block_header *block;
- const struct rkisp1_ext_params_handler *handler;
-
- block = (const struct rkisp1_ext_params_block_header *)
- &cfg->data[block_offset];
-
- if (block->type >= ARRAY_SIZE(rkisp1_ext_params_handlers)) {
- dev_dbg(params->rkisp1->dev,
- "Invalid parameters block type\n");
- return -EINVAL;
- }
-
- if (block->size > cfg_size) {
- dev_dbg(params->rkisp1->dev,
- "Premature end of parameters data\n");
- return -EINVAL;
- }
-
- if ((block->flags & (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE |
- RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) ==
- (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE |
- RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) {
- dev_dbg(params->rkisp1->dev,
- "Invalid parameters block flags\n");
- return -EINVAL;
- }
-
- handler = &rkisp1_ext_params_handlers[block->type];
- if (block->size != handler->size) {
- dev_dbg(params->rkisp1->dev,
- "Invalid parameters block size\n");
- return -EINVAL;
- }
-
- block_offset += block->size;
- cfg_size -= block->size;
- }
-
- if (cfg_size) {
- dev_dbg(params->rkisp1->dev,
- "Unexpected data after the parameters buffer end\n");
- return -EINVAL;
- }
-
- return 0;
+ return v4l2_isp_params_validate_buffer(params->rkisp1->dev, vb,
+ (struct v4l2_isp_params_buffer *)cfg,
+ rkisp1_ext_params_block_types_info,
+ ARRAY_SIZE(rkisp1_ext_params_block_types_info));
}
static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb)
diff --git a/drivers/media/platform/rockchip/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile
index cb86b429cfaa..a77122641d14 100644
--- a/drivers/media/platform/rockchip/rkvdec/Makefile
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rockchip-vdec.o
-rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-vp9.o
+rockchip-vdec-y += rkvdec.o rkvdec-h264.o rkvdec-hevc.o rkvdec-vp9.o
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c
new file mode 100644
index 000000000000..eac4ea604949
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-data.c
@@ -0,0 +1,1848 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Video Decoder driver
+ *
+ * Copyright (C) 2023 Collabora, Ltd.
+ * Sebastian Fricke <sebastian.fricke@collabora.com>
+ */
+
+#include <linux/types.h>
+
+#define RKV_CABAC_TABLE_SIZE 27456
+
+/*
+ * This file is #include from rkvdec-hevc.c and not compiled.
+ */
+static const u8 rkvdec_hevc_cabac_table[RKV_CABAC_TABLE_SIZE] = {
+ 0x07, 0x0f, 0x48, 0x58, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x40, 0x0f,
+ 0x68, 0x48, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x40,
+ 0x40, 0x68, 0x58, 0x60, 0x40, 0x1f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x60,
+ 0x60, 0x50, 0x58, 0x50, 0x07, 0x58, 0x68, 0x50, 0x58, 0x68, 0x68, 0x68, 0x68, 0x68, 0x50,
+ 0x48, 0x68, 0x60, 0x60, 0x50, 0x58, 0x50, 0x07, 0x58, 0x68, 0x50, 0x58, 0x68, 0x68, 0x68,
+ 0x68, 0x68, 0x50, 0x48, 0x68, 0x48, 0x48, 0x1f, 0x58, 0x68, 0x68, 0x58, 0x60, 0x60, 0x60,
+ 0x50, 0x50, 0x50, 0x48, 0x58, 0x58, 0x37, 0x07, 0x58, 0x48, 0x58, 0x58, 0x37, 0x07, 0x58,
+ 0x48, 0x58, 0x58, 0x37, 0x07, 0x58, 0x50, 0x48, 0x1f, 0x1f, 0x0f, 0x0f, 0x0f, 0x0f, 0x07,
+ 0x0f, 0x48, 0x68, 0x0f, 0x48, 0x68, 0x40, 0x40, 0x50, 0x50, 0x07, 0x40, 0x50, 0x0f, 0x40,
+ 0x48, 0x07, 0x40, 0x27, 0x50, 0x48, 0x48, 0x40, 0x0f, 0x50, 0x37, 0x1f, 0x1f, 0x50, 0x37,
+ 0x40, 0x27, 0x40, 0x07, 0x0f, 0x17, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0f, 0x47, 0x57,
+ 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x40, 0x0f, 0x66, 0x47, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x00, 0x00, 0x67, 0x57, 0x5e,
+ 0x00, 0x1f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x47, 0x47, 0x5f, 0x5f, 0x4f, 0x57, 0x4f,
+ 0x07, 0x57, 0x67, 0x4f, 0x57, 0x67, 0x67, 0x67, 0x67, 0x66, 0x4f, 0x47, 0x66, 0x5f, 0x5f,
+ 0x4f, 0x57, 0x4f, 0x07, 0x57, 0x67, 0x4f, 0x57, 0x67, 0x67, 0x67, 0x67, 0x66, 0x4f, 0x47,
+ 0x66, 0x46, 0x48, 0x20, 0x57, 0x67, 0x67, 0x57, 0x5f, 0x5f, 0x5e, 0x4f, 0x4f, 0x4f, 0x47,
+ 0x57, 0x57, 0x37, 0x07, 0x57, 0x47, 0x57, 0x57, 0x37, 0x07, 0x57, 0x47, 0x57, 0x57, 0x37,
+ 0x07, 0x57, 0x4f, 0x47, 0x1f, 0x1f, 0x0f, 0x10, 0x0f, 0x10, 0x07, 0x10, 0x47, 0x67, 0x10,
+ 0x47, 0x67, 0x40, 0x40, 0x4f, 0x4e, 0x08, 0x00, 0x4f, 0x0f, 0x00, 0x47, 0x07, 0x01, 0x27,
+ 0x4e, 0x47, 0x47, 0x00, 0x0f, 0x4f, 0x37, 0x1f, 0x1f, 0x4f, 0x36, 0x00, 0x27, 0x00, 0x07,
+ 0x10, 0x17, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0e, 0x47, 0x57, 0x58, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x0e, 0x40, 0x40, 0x40, 0x0e, 0x64, 0x47, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x00, 0x00, 0x66, 0x57, 0x5d, 0x00, 0x1e, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x47, 0x47, 0x5e, 0x5e, 0x4e, 0x56, 0x4f, 0x07, 0x56, 0x66, 0x4f,
+ 0x56, 0x66, 0x67, 0x66, 0x66, 0x64, 0x4e, 0x46, 0x64, 0x5e, 0x5e, 0x4e, 0x56, 0x4f, 0x07,
+ 0x56, 0x66, 0x4f, 0x56, 0x66, 0x67, 0x66, 0x66, 0x64, 0x4e, 0x46, 0x64, 0x45, 0x48, 0x20,
+ 0x57, 0x66, 0x66, 0x56, 0x5e, 0x5e, 0x5d, 0x4e, 0x4e, 0x4e, 0x46, 0x56, 0x57, 0x36, 0x07,
+ 0x56, 0x46, 0x56, 0x57, 0x36, 0x07, 0x56, 0x46, 0x56, 0x57, 0x36, 0x07, 0x56, 0x4f, 0x47,
+ 0x1e, 0x1e, 0x0f, 0x10, 0x0f, 0x10, 0x07, 0x10, 0x47, 0x66, 0x10, 0x47, 0x66, 0x40, 0x40,
+ 0x4f, 0x4d, 0x08, 0x00, 0x4f, 0x0f, 0x00, 0x47, 0x07, 0x03, 0x27, 0x4d, 0x47, 0x46, 0x01,
+ 0x0f, 0x4f, 0x36, 0x1f, 0x1e, 0x4f, 0x34, 0x01, 0x26, 0x00, 0x07, 0x10, 0x17, 0x0f, 0x0f,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x0d, 0x47, 0x57, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0e, 0x40,
+ 0x40, 0x40, 0x0e, 0x62, 0x47, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x07, 0x00, 0x00, 0x65, 0x57, 0x5c, 0x00, 0x1e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x47, 0x47, 0x5d, 0x5d, 0x4e, 0x56, 0x4f, 0x07, 0x56, 0x66, 0x4f, 0x55, 0x65, 0x67, 0x66,
+ 0x65, 0x63, 0x4d, 0x46, 0x62, 0x5d, 0x5d, 0x4e, 0x56, 0x4f, 0x07, 0x56, 0x66, 0x4f, 0x55,
+ 0x65, 0x67, 0x66, 0x65, 0x63, 0x4d, 0x46, 0x62, 0x44, 0x48, 0x20, 0x57, 0x65, 0x65, 0x56,
+ 0x5d, 0x5d, 0x5c, 0x4e, 0x4d, 0x4e, 0x45, 0x56, 0x57, 0x36, 0x07, 0x56, 0x45, 0x56, 0x57,
+ 0x36, 0x07, 0x56, 0x45, 0x56, 0x57, 0x36, 0x07, 0x56, 0x4f, 0x47, 0x1e, 0x1e, 0x0f, 0x10,
+ 0x0f, 0x10, 0x07, 0x10, 0x47, 0x65, 0x10, 0x47, 0x65, 0x40, 0x40, 0x4f, 0x4c, 0x08, 0x00,
+ 0x4f, 0x0f, 0x00, 0x47, 0x07, 0x04, 0x27, 0x4c, 0x47, 0x45, 0x01, 0x0f, 0x4f, 0x36, 0x1f,
+ 0x1e, 0x4f, 0x33, 0x01, 0x25, 0x00, 0x07, 0x10, 0x17, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x0c, 0x46, 0x56, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0d, 0x40, 0x40, 0x40, 0x0d, 0x60,
+ 0x46, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x01, 0x01,
+ 0x64, 0x56, 0x5b, 0x01, 0x1d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x46, 0x46, 0x5c, 0x5c,
+ 0x4d, 0x55, 0x4e, 0x07, 0x55, 0x65, 0x4e, 0x54, 0x64, 0x66, 0x65, 0x64, 0x61, 0x4c, 0x45,
+ 0x60, 0x5c, 0x5c, 0x4d, 0x55, 0x4e, 0x07, 0x55, 0x65, 0x4e, 0x54, 0x64, 0x66, 0x65, 0x64,
+ 0x61, 0x4c, 0x45, 0x60, 0x43, 0x49, 0x21, 0x56, 0x64, 0x64, 0x55, 0x5c, 0x5c, 0x5b, 0x4d,
+ 0x4c, 0x4d, 0x44, 0x55, 0x56, 0x35, 0x07, 0x55, 0x44, 0x55, 0x56, 0x35, 0x07, 0x55, 0x44,
+ 0x55, 0x56, 0x35, 0x07, 0x55, 0x4e, 0x46, 0x1d, 0x1d, 0x0f, 0x11, 0x0f, 0x11, 0x07, 0x11,
+ 0x46, 0x64, 0x11, 0x46, 0x64, 0x40, 0x40, 0x4e, 0x4b, 0x09, 0x01, 0x4e, 0x0f, 0x01, 0x46,
+ 0x07, 0x06, 0x27, 0x4b, 0x46, 0x44, 0x02, 0x0f, 0x4e, 0x35, 0x1e, 0x1d, 0x4e, 0x31, 0x02,
+ 0x24, 0x01, 0x07, 0x11, 0x16, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0b, 0x46, 0x56, 0x58,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x0c, 0x40, 0x40, 0x40, 0x0c, 0x5e, 0x46, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x01, 0x01, 0x63, 0x56, 0x59, 0x01,
+ 0x1c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x46, 0x46, 0x5b, 0x5b, 0x4c, 0x54, 0x4e, 0x07,
+ 0x54, 0x64, 0x4e, 0x53, 0x63, 0x66, 0x64, 0x63, 0x60, 0x4b, 0x44, 0x5e, 0x5b, 0x5b, 0x4c,
+ 0x54, 0x4e, 0x07, 0x54, 0x64, 0x4e, 0x53, 0x63, 0x66, 0x64, 0x63, 0x60, 0x4b, 0x44, 0x5e,
+ 0x41, 0x49, 0x21, 0x56, 0x63, 0x63, 0x54, 0x5b, 0x5b, 0x59, 0x4c, 0x4b, 0x4c, 0x43, 0x54,
+ 0x56, 0x34, 0x07, 0x54, 0x43, 0x54, 0x56, 0x34, 0x07, 0x54, 0x43, 0x54, 0x56, 0x34, 0x07,
+ 0x54, 0x4e, 0x46, 0x1c, 0x1c, 0x0f, 0x11, 0x0f, 0x11, 0x07, 0x11, 0x46, 0x63, 0x11, 0x46,
+ 0x63, 0x40, 0x40, 0x4e, 0x49, 0x09, 0x01, 0x4e, 0x0f, 0x01, 0x46, 0x07, 0x07, 0x27, 0x49,
+ 0x46, 0x43, 0x03, 0x0f, 0x4e, 0x34, 0x1e, 0x1c, 0x4e, 0x30, 0x03, 0x23, 0x01, 0x07, 0x11,
+ 0x16, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0a, 0x46, 0x56, 0x58, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x0c, 0x40, 0x40, 0x40, 0x0c, 0x5c, 0x46, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x07, 0x01, 0x01, 0x62, 0x56, 0x58, 0x01, 0x1c, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x46, 0x46, 0x5a, 0x5a, 0x4c, 0x54, 0x4e, 0x07, 0x54, 0x64, 0x4e, 0x52,
+ 0x62, 0x66, 0x64, 0x62, 0x5e, 0x4a, 0x44, 0x5c, 0x5a, 0x5a, 0x4c, 0x54, 0x4e, 0x07, 0x54,
+ 0x64, 0x4e, 0x52, 0x62, 0x66, 0x64, 0x62, 0x5e, 0x4a, 0x44, 0x5c, 0x40, 0x49, 0x21, 0x56,
+ 0x62, 0x62, 0x54, 0x5a, 0x5a, 0x58, 0x4c, 0x4a, 0x4c, 0x42, 0x54, 0x56, 0x34, 0x07, 0x54,
+ 0x42, 0x54, 0x56, 0x34, 0x07, 0x54, 0x42, 0x54, 0x56, 0x34, 0x07, 0x54, 0x4e, 0x46, 0x1c,
+ 0x1c, 0x0f, 0x11, 0x0f, 0x11, 0x07, 0x11, 0x46, 0x62, 0x11, 0x46, 0x62, 0x40, 0x40, 0x4e,
+ 0x48, 0x09, 0x01, 0x4e, 0x0f, 0x01, 0x46, 0x07, 0x09, 0x27, 0x48, 0x46, 0x42, 0x03, 0x0f,
+ 0x4e, 0x34, 0x1e, 0x1c, 0x4e, 0x2e, 0x03, 0x22, 0x01, 0x07, 0x11, 0x16, 0x0f, 0x0f, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x09, 0x45, 0x55, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0b, 0x40, 0x40,
+ 0x40, 0x0b, 0x5a, 0x45, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x07, 0x02, 0x02, 0x61, 0x55, 0x57, 0x02, 0x1b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45,
+ 0x45, 0x59, 0x59, 0x4b, 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x51, 0x61, 0x65, 0x63, 0x61,
+ 0x5d, 0x49, 0x43, 0x5a, 0x59, 0x59, 0x4b, 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x51, 0x61,
+ 0x65, 0x63, 0x61, 0x5d, 0x49, 0x43, 0x5a, 0x00, 0x4a, 0x22, 0x55, 0x61, 0x61, 0x53, 0x59,
+ 0x59, 0x57, 0x4b, 0x49, 0x4b, 0x41, 0x53, 0x55, 0x33, 0x07, 0x53, 0x41, 0x53, 0x55, 0x33,
+ 0x07, 0x53, 0x41, 0x53, 0x55, 0x33, 0x07, 0x53, 0x4d, 0x45, 0x1b, 0x1b, 0x0f, 0x12, 0x0f,
+ 0x12, 0x07, 0x12, 0x45, 0x61, 0x12, 0x45, 0x61, 0x40, 0x40, 0x4d, 0x47, 0x0a, 0x02, 0x4d,
+ 0x0f, 0x02, 0x45, 0x07, 0x0a, 0x27, 0x47, 0x45, 0x41, 0x04, 0x0f, 0x4d, 0x33, 0x1d, 0x1b,
+ 0x4d, 0x2d, 0x04, 0x21, 0x02, 0x07, 0x12, 0x15, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08,
+ 0x45, 0x55, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0a, 0x40, 0x40, 0x40, 0x0a, 0x59, 0x45,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x02, 0x02, 0x60,
+ 0x55, 0x56, 0x02, 0x1a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x45, 0x58, 0x58, 0x4b,
+ 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x50, 0x60, 0x65, 0x63, 0x60, 0x5b, 0x48, 0x43, 0x59,
+ 0x58, 0x58, 0x4b, 0x53, 0x4d, 0x07, 0x53, 0x63, 0x4d, 0x50, 0x60, 0x65, 0x63, 0x60, 0x5b,
+ 0x48, 0x43, 0x59, 0x01, 0x4a, 0x22, 0x55, 0x60, 0x60, 0x53, 0x58, 0x58, 0x56, 0x4b, 0x48,
+ 0x4b, 0x40, 0x53, 0x55, 0x32, 0x07, 0x53, 0x40, 0x53, 0x55, 0x32, 0x07, 0x53, 0x40, 0x53,
+ 0x55, 0x32, 0x07, 0x53, 0x4d, 0x45, 0x1a, 0x1a, 0x0f, 0x12, 0x0f, 0x12, 0x07, 0x12, 0x45,
+ 0x60, 0x12, 0x45, 0x60, 0x40, 0x40, 0x4d, 0x46, 0x0a, 0x02, 0x4d, 0x0f, 0x02, 0x45, 0x07,
+ 0x0c, 0x27, 0x46, 0x45, 0x40, 0x04, 0x0f, 0x4d, 0x32, 0x1d, 0x1a, 0x4d, 0x2b, 0x04, 0x20,
+ 0x02, 0x07, 0x12, 0x15, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x45, 0x55, 0x58, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x0a, 0x40, 0x40, 0x40, 0x0a, 0x57, 0x45, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x02, 0x02, 0x5f, 0x55, 0x54, 0x02, 0x1a,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x45, 0x57, 0x57, 0x4a, 0x52, 0x4d, 0x07, 0x52,
+ 0x62, 0x4d, 0x4f, 0x5f, 0x65, 0x62, 0x5f, 0x59, 0x47, 0x42, 0x57, 0x57, 0x57, 0x4a, 0x52,
+ 0x4d, 0x07, 0x52, 0x62, 0x4d, 0x4f, 0x5f, 0x65, 0x62, 0x5f, 0x59, 0x47, 0x42, 0x57, 0x03,
+ 0x4a, 0x22, 0x55, 0x5f, 0x5f, 0x52, 0x57, 0x57, 0x54, 0x4a, 0x47, 0x4a, 0x00, 0x52, 0x55,
+ 0x32, 0x07, 0x52, 0x00, 0x52, 0x55, 0x32, 0x07, 0x52, 0x00, 0x52, 0x55, 0x32, 0x07, 0x52,
+ 0x4d, 0x45, 0x1a, 0x1a, 0x0f, 0x12, 0x0f, 0x12, 0x07, 0x12, 0x45, 0x5f, 0x12, 0x45, 0x5f,
+ 0x40, 0x40, 0x4d, 0x44, 0x0a, 0x02, 0x4d, 0x0f, 0x02, 0x45, 0x07, 0x0e, 0x27, 0x44, 0x45,
+ 0x00, 0x05, 0x0f, 0x4d, 0x32, 0x1d, 0x1a, 0x4d, 0x29, 0x05, 0x1f, 0x02, 0x07, 0x12, 0x15,
+ 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x44, 0x54, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x09, 0x40, 0x40, 0x40, 0x09, 0x55, 0x44, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x07, 0x03, 0x03, 0x5e, 0x54, 0x53, 0x03, 0x19, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x44, 0x44, 0x56, 0x56, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61, 0x4c, 0x4e, 0x5e,
+ 0x64, 0x61, 0x5e, 0x58, 0x46, 0x41, 0x55, 0x56, 0x56, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61,
+ 0x4c, 0x4e, 0x5e, 0x64, 0x61, 0x5e, 0x58, 0x46, 0x41, 0x55, 0x04, 0x4b, 0x23, 0x54, 0x5e,
+ 0x5e, 0x51, 0x56, 0x56, 0x53, 0x49, 0x46, 0x49, 0x01, 0x51, 0x54, 0x31, 0x07, 0x51, 0x01,
+ 0x51, 0x54, 0x31, 0x07, 0x51, 0x01, 0x51, 0x54, 0x31, 0x07, 0x51, 0x4c, 0x44, 0x19, 0x19,
+ 0x0f, 0x13, 0x0f, 0x13, 0x07, 0x13, 0x44, 0x5e, 0x13, 0x44, 0x5e, 0x40, 0x40, 0x4c, 0x43,
+ 0x0b, 0x03, 0x4c, 0x0f, 0x03, 0x44, 0x07, 0x0f, 0x27, 0x43, 0x44, 0x01, 0x06, 0x0f, 0x4c,
+ 0x31, 0x1c, 0x19, 0x4c, 0x28, 0x06, 0x1e, 0x03, 0x07, 0x13, 0x14, 0x0f, 0x0f, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x05, 0x44, 0x54, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x09, 0x40, 0x40, 0x40,
+ 0x09, 0x53, 0x44, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07,
+ 0x03, 0x03, 0x5d, 0x54, 0x52, 0x03, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44,
+ 0x55, 0x55, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61, 0x4c, 0x4d, 0x5d, 0x64, 0x61, 0x5d, 0x56,
+ 0x45, 0x41, 0x53, 0x55, 0x55, 0x49, 0x51, 0x4c, 0x07, 0x51, 0x61, 0x4c, 0x4d, 0x5d, 0x64,
+ 0x61, 0x5d, 0x56, 0x45, 0x41, 0x53, 0x05, 0x4b, 0x23, 0x54, 0x5d, 0x5d, 0x51, 0x55, 0x55,
+ 0x52, 0x49, 0x45, 0x49, 0x02, 0x51, 0x54, 0x31, 0x07, 0x51, 0x02, 0x51, 0x54, 0x31, 0x07,
+ 0x51, 0x02, 0x51, 0x54, 0x31, 0x07, 0x51, 0x4c, 0x44, 0x19, 0x19, 0x0f, 0x13, 0x0f, 0x13,
+ 0x07, 0x13, 0x44, 0x5d, 0x13, 0x44, 0x5d, 0x40, 0x40, 0x4c, 0x42, 0x0b, 0x03, 0x4c, 0x0f,
+ 0x03, 0x44, 0x07, 0x11, 0x27, 0x42, 0x44, 0x02, 0x06, 0x0f, 0x4c, 0x31, 0x1c, 0x19, 0x4c,
+ 0x26, 0x06, 0x1d, 0x03, 0x07, 0x13, 0x14, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x44,
+ 0x54, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x08, 0x40, 0x40, 0x40, 0x08, 0x51, 0x44, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x03, 0x03, 0x5c, 0x54,
+ 0x51, 0x03, 0x18, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x44, 0x54, 0x54, 0x48, 0x50,
+ 0x4c, 0x07, 0x50, 0x60, 0x4c, 0x4c, 0x5c, 0x64, 0x60, 0x5c, 0x55, 0x44, 0x40, 0x51, 0x54,
+ 0x54, 0x48, 0x50, 0x4c, 0x07, 0x50, 0x60, 0x4c, 0x4c, 0x5c, 0x64, 0x60, 0x5c, 0x55, 0x44,
+ 0x40, 0x51, 0x06, 0x4b, 0x23, 0x54, 0x5c, 0x5c, 0x50, 0x54, 0x54, 0x51, 0x48, 0x44, 0x48,
+ 0x03, 0x50, 0x54, 0x30, 0x07, 0x50, 0x03, 0x50, 0x54, 0x30, 0x07, 0x50, 0x03, 0x50, 0x54,
+ 0x30, 0x07, 0x50, 0x4c, 0x44, 0x18, 0x18, 0x0f, 0x13, 0x0f, 0x13, 0x07, 0x13, 0x44, 0x5c,
+ 0x13, 0x44, 0x5c, 0x40, 0x40, 0x4c, 0x41, 0x0b, 0x03, 0x4c, 0x0f, 0x03, 0x44, 0x07, 0x12,
+ 0x27, 0x41, 0x44, 0x03, 0x07, 0x0f, 0x4c, 0x30, 0x1c, 0x18, 0x4c, 0x25, 0x07, 0x1c, 0x03,
+ 0x07, 0x13, 0x14, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x43, 0x53, 0x58, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x07, 0x40, 0x40, 0x40, 0x07, 0x4f, 0x43, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x04, 0x04, 0x5b, 0x53, 0x4f, 0x04, 0x17, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x43, 0x53, 0x53, 0x47, 0x4f, 0x4b, 0x07, 0x4f, 0x5f,
+ 0x4b, 0x4b, 0x5b, 0x63, 0x5f, 0x5b, 0x53, 0x43, 0x00, 0x4f, 0x53, 0x53, 0x47, 0x4f, 0x4b,
+ 0x07, 0x4f, 0x5f, 0x4b, 0x4b, 0x5b, 0x63, 0x5f, 0x5b, 0x53, 0x43, 0x00, 0x4f, 0x08, 0x4c,
+ 0x24, 0x53, 0x5b, 0x5b, 0x4f, 0x53, 0x53, 0x4f, 0x47, 0x43, 0x47, 0x04, 0x4f, 0x53, 0x2f,
+ 0x07, 0x4f, 0x04, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x04, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x4b,
+ 0x43, 0x17, 0x17, 0x0f, 0x14, 0x0f, 0x14, 0x07, 0x14, 0x43, 0x5b, 0x14, 0x43, 0x5b, 0x40,
+ 0x40, 0x4b, 0x00, 0x0c, 0x04, 0x4b, 0x0f, 0x04, 0x43, 0x07, 0x14, 0x27, 0x00, 0x43, 0x04,
+ 0x08, 0x0f, 0x4b, 0x2f, 0x1b, 0x17, 0x4b, 0x23, 0x08, 0x1b, 0x04, 0x07, 0x14, 0x13, 0x0f,
+ 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x43, 0x53, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07,
+ 0x40, 0x40, 0x40, 0x07, 0x4d, 0x43, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x07, 0x04, 0x04, 0x5a, 0x53, 0x4e, 0x04, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x43, 0x43, 0x52, 0x52, 0x47, 0x4f, 0x4b, 0x07, 0x4f, 0x5f, 0x4b, 0x4a, 0x5a, 0x63,
+ 0x5f, 0x5a, 0x52, 0x42, 0x00, 0x4d, 0x52, 0x52, 0x47, 0x4f, 0x4b, 0x07, 0x4f, 0x5f, 0x4b,
+ 0x4a, 0x5a, 0x63, 0x5f, 0x5a, 0x52, 0x42, 0x00, 0x4d, 0x09, 0x4c, 0x24, 0x53, 0x5a, 0x5a,
+ 0x4f, 0x52, 0x52, 0x4e, 0x47, 0x42, 0x47, 0x05, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x05, 0x4f,
+ 0x53, 0x2f, 0x07, 0x4f, 0x05, 0x4f, 0x53, 0x2f, 0x07, 0x4f, 0x4b, 0x43, 0x17, 0x17, 0x0f,
+ 0x14, 0x0f, 0x14, 0x07, 0x14, 0x43, 0x5a, 0x14, 0x43, 0x5a, 0x40, 0x40, 0x4b, 0x01, 0x0c,
+ 0x04, 0x4b, 0x0f, 0x04, 0x43, 0x07, 0x15, 0x27, 0x01, 0x43, 0x05, 0x08, 0x0f, 0x4b, 0x2f,
+ 0x1b, 0x17, 0x4b, 0x22, 0x08, 0x1a, 0x04, 0x07, 0x14, 0x13, 0x0f, 0x0f, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x01, 0x43, 0x53, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x06, 0x40, 0x40, 0x40, 0x06,
+ 0x4b, 0x43, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x04,
+ 0x04, 0x59, 0x53, 0x4d, 0x04, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x43, 0x51,
+ 0x51, 0x46, 0x4e, 0x4b, 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e, 0x59, 0x50, 0x41,
+ 0x01, 0x4b, 0x51, 0x51, 0x46, 0x4e, 0x4b, 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e,
+ 0x59, 0x50, 0x41, 0x01, 0x4b, 0x0a, 0x4c, 0x24, 0x53, 0x59, 0x59, 0x4e, 0x51, 0x51, 0x4d,
+ 0x46, 0x41, 0x46, 0x06, 0x4e, 0x53, 0x2e, 0x07, 0x4e, 0x06, 0x4e, 0x53, 0x2e, 0x07, 0x4e,
+ 0x06, 0x4e, 0x53, 0x2e, 0x07, 0x4e, 0x4b, 0x43, 0x16, 0x16, 0x0f, 0x14, 0x0f, 0x14, 0x07,
+ 0x14, 0x43, 0x59, 0x14, 0x43, 0x59, 0x40, 0x40, 0x4b, 0x02, 0x0c, 0x04, 0x4b, 0x0f, 0x04,
+ 0x43, 0x07, 0x17, 0x27, 0x02, 0x43, 0x06, 0x09, 0x0f, 0x4b, 0x2e, 0x1b, 0x16, 0x4b, 0x20,
+ 0x09, 0x19, 0x04, 0x07, 0x14, 0x13, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x43, 0x53,
+ 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x05, 0x40, 0x40, 0x40, 0x05, 0x4a, 0x43, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x04, 0x04, 0x59, 0x53, 0x4c,
+ 0x04, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x43, 0x51, 0x51, 0x46, 0x4e, 0x4b,
+ 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e, 0x59, 0x4f, 0x41, 0x01, 0x4a, 0x51, 0x51,
+ 0x46, 0x4e, 0x4b, 0x07, 0x4e, 0x5e, 0x4b, 0x49, 0x59, 0x63, 0x5e, 0x59, 0x4f, 0x41, 0x01,
+ 0x4a, 0x0b, 0x4d, 0x24, 0x53, 0x59, 0x59, 0x4e, 0x51, 0x51, 0x4c, 0x46, 0x41, 0x46, 0x06,
+ 0x4e, 0x53, 0x2d, 0x07, 0x4e, 0x06, 0x4e, 0x53, 0x2d, 0x07, 0x4e, 0x06, 0x4e, 0x53, 0x2d,
+ 0x07, 0x4e, 0x4b, 0x43, 0x15, 0x15, 0x0f, 0x14, 0x0f, 0x14, 0x07, 0x14, 0x43, 0x59, 0x14,
+ 0x43, 0x59, 0x40, 0x40, 0x4b, 0x03, 0x0c, 0x04, 0x4b, 0x0f, 0x04, 0x43, 0x07, 0x18, 0x27,
+ 0x03, 0x43, 0x06, 0x09, 0x0f, 0x4b, 0x2d, 0x1a, 0x15, 0x4b, 0x1e, 0x09, 0x18, 0x04, 0x07,
+ 0x14, 0x12, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x42, 0x52, 0x58, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x05, 0x40, 0x40, 0x40, 0x05, 0x48, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x05, 0x05, 0x58, 0x52, 0x4a, 0x05, 0x15, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x42, 0x42, 0x50, 0x50, 0x45, 0x4d, 0x4a, 0x07, 0x4d, 0x5d, 0x4a,
+ 0x48, 0x58, 0x62, 0x5d, 0x58, 0x4d, 0x40, 0x02, 0x48, 0x50, 0x50, 0x45, 0x4d, 0x4a, 0x07,
+ 0x4d, 0x5d, 0x4a, 0x48, 0x58, 0x62, 0x5d, 0x58, 0x4d, 0x40, 0x02, 0x48, 0x0d, 0x4d, 0x25,
+ 0x52, 0x58, 0x58, 0x4d, 0x50, 0x50, 0x4a, 0x45, 0x40, 0x45, 0x07, 0x4d, 0x52, 0x2d, 0x07,
+ 0x4d, 0x07, 0x4d, 0x52, 0x2d, 0x07, 0x4d, 0x07, 0x4d, 0x52, 0x2d, 0x07, 0x4d, 0x4a, 0x42,
+ 0x15, 0x15, 0x0f, 0x15, 0x0f, 0x15, 0x07, 0x15, 0x42, 0x58, 0x15, 0x42, 0x58, 0x40, 0x40,
+ 0x4a, 0x05, 0x0d, 0x05, 0x4a, 0x0f, 0x05, 0x42, 0x07, 0x1a, 0x27, 0x05, 0x42, 0x07, 0x0a,
+ 0x0f, 0x4a, 0x2d, 0x1a, 0x15, 0x4a, 0x1d, 0x0a, 0x18, 0x05, 0x07, 0x15, 0x12, 0x0f, 0x0f,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x40, 0x42, 0x52, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x40,
+ 0x40, 0x40, 0x04, 0x46, 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x07, 0x05, 0x05, 0x57, 0x52, 0x49, 0x05, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x4f, 0x4f, 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x47, 0x57, 0x62, 0x5c,
+ 0x57, 0x4b, 0x00, 0x03, 0x46, 0x4f, 0x4f, 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x47,
+ 0x57, 0x62, 0x5c, 0x57, 0x4b, 0x00, 0x03, 0x46, 0x0e, 0x4d, 0x25, 0x52, 0x57, 0x57, 0x4c,
+ 0x4f, 0x4f, 0x49, 0x44, 0x00, 0x44, 0x08, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x08, 0x4c, 0x52,
+ 0x2c, 0x07, 0x4c, 0x08, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x4a, 0x42, 0x14, 0x14, 0x0f, 0x15,
+ 0x0f, 0x15, 0x07, 0x15, 0x42, 0x57, 0x15, 0x42, 0x57, 0x40, 0x40, 0x4a, 0x06, 0x0d, 0x05,
+ 0x4a, 0x0f, 0x05, 0x42, 0x07, 0x1c, 0x27, 0x06, 0x42, 0x08, 0x0b, 0x0f, 0x4a, 0x2c, 0x1a,
+ 0x14, 0x4a, 0x1b, 0x0b, 0x17, 0x05, 0x07, 0x15, 0x12, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x41, 0x42, 0x52, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x40, 0x40, 0x40, 0x04, 0x44,
+ 0x42, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x05, 0x05,
+ 0x56, 0x52, 0x48, 0x05, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4e, 0x4e,
+ 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x46, 0x56, 0x62, 0x5c, 0x56, 0x4a, 0x01, 0x03,
+ 0x44, 0x4e, 0x4e, 0x44, 0x4c, 0x4a, 0x07, 0x4c, 0x5c, 0x4a, 0x46, 0x56, 0x62, 0x5c, 0x56,
+ 0x4a, 0x01, 0x03, 0x44, 0x0f, 0x4d, 0x25, 0x52, 0x56, 0x56, 0x4c, 0x4e, 0x4e, 0x48, 0x44,
+ 0x01, 0x44, 0x09, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x09, 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x09,
+ 0x4c, 0x52, 0x2c, 0x07, 0x4c, 0x4a, 0x42, 0x14, 0x14, 0x0f, 0x15, 0x0f, 0x15, 0x07, 0x15,
+ 0x42, 0x56, 0x15, 0x42, 0x56, 0x40, 0x40, 0x4a, 0x07, 0x0d, 0x05, 0x4a, 0x0f, 0x05, 0x42,
+ 0x07, 0x1d, 0x27, 0x07, 0x42, 0x09, 0x0b, 0x0f, 0x4a, 0x2c, 0x1a, 0x14, 0x4a, 0x1a, 0x0b,
+ 0x16, 0x05, 0x07, 0x15, 0x12, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x41, 0x51, 0x58,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x03, 0x40, 0x40, 0x40, 0x03, 0x42, 0x41, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x06, 0x06, 0x55, 0x51, 0x47, 0x06,
+ 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4d, 0x4d, 0x43, 0x4b, 0x49, 0x07,
+ 0x4b, 0x5b, 0x49, 0x45, 0x55, 0x61, 0x5b, 0x55, 0x48, 0x02, 0x04, 0x42, 0x4d, 0x4d, 0x43,
+ 0x4b, 0x49, 0x07, 0x4b, 0x5b, 0x49, 0x45, 0x55, 0x61, 0x5b, 0x55, 0x48, 0x02, 0x04, 0x42,
+ 0x10, 0x4e, 0x26, 0x51, 0x55, 0x55, 0x4b, 0x4d, 0x4d, 0x47, 0x43, 0x02, 0x43, 0x0a, 0x4b,
+ 0x51, 0x2b, 0x07, 0x4b, 0x0a, 0x4b, 0x51, 0x2b, 0x07, 0x4b, 0x0a, 0x4b, 0x51, 0x2b, 0x07,
+ 0x4b, 0x49, 0x41, 0x13, 0x13, 0x0f, 0x16, 0x0f, 0x16, 0x07, 0x16, 0x41, 0x55, 0x16, 0x41,
+ 0x55, 0x40, 0x40, 0x49, 0x08, 0x0e, 0x06, 0x49, 0x0f, 0x06, 0x41, 0x07, 0x1f, 0x27, 0x08,
+ 0x41, 0x0a, 0x0c, 0x0f, 0x49, 0x2b, 0x19, 0x13, 0x49, 0x18, 0x0c, 0x15, 0x06, 0x07, 0x16,
+ 0x11, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x43, 0x41, 0x51, 0x58, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x02, 0x40, 0x40, 0x40, 0x02, 0x40, 0x41, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x07, 0x06, 0x06, 0x54, 0x51, 0x45, 0x06, 0x12, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x41, 0x41, 0x4c, 0x4c, 0x42, 0x4a, 0x49, 0x07, 0x4a, 0x5a, 0x49, 0x44,
+ 0x54, 0x61, 0x5a, 0x54, 0x47, 0x03, 0x05, 0x40, 0x4c, 0x4c, 0x42, 0x4a, 0x49, 0x07, 0x4a,
+ 0x5a, 0x49, 0x44, 0x54, 0x61, 0x5a, 0x54, 0x47, 0x03, 0x05, 0x40, 0x12, 0x4e, 0x26, 0x51,
+ 0x54, 0x54, 0x4a, 0x4c, 0x4c, 0x45, 0x42, 0x03, 0x42, 0x0b, 0x4a, 0x51, 0x2a, 0x07, 0x4a,
+ 0x0b, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x0b, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x49, 0x41, 0x12,
+ 0x12, 0x0f, 0x16, 0x0f, 0x16, 0x07, 0x16, 0x41, 0x54, 0x16, 0x41, 0x54, 0x40, 0x40, 0x49,
+ 0x0a, 0x0e, 0x06, 0x49, 0x0f, 0x06, 0x41, 0x07, 0x20, 0x27, 0x0a, 0x41, 0x0b, 0x0d, 0x0f,
+ 0x49, 0x2a, 0x19, 0x12, 0x49, 0x17, 0x0d, 0x14, 0x06, 0x07, 0x16, 0x11, 0x0f, 0x0f, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x44, 0x41, 0x51, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x40, 0x40,
+ 0x40, 0x02, 0x01, 0x41, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x07, 0x06, 0x06, 0x53, 0x51, 0x44, 0x06, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41,
+ 0x41, 0x4b, 0x4b, 0x42, 0x4a, 0x49, 0x07, 0x4a, 0x5a, 0x49, 0x43, 0x53, 0x61, 0x5a, 0x53,
+ 0x45, 0x04, 0x05, 0x01, 0x4b, 0x4b, 0x42, 0x4a, 0x49, 0x07, 0x4a, 0x5a, 0x49, 0x43, 0x53,
+ 0x61, 0x5a, 0x53, 0x45, 0x04, 0x05, 0x01, 0x13, 0x4e, 0x26, 0x51, 0x53, 0x53, 0x4a, 0x4b,
+ 0x4b, 0x44, 0x42, 0x04, 0x42, 0x0c, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x0c, 0x4a, 0x51, 0x2a,
+ 0x07, 0x4a, 0x0c, 0x4a, 0x51, 0x2a, 0x07, 0x4a, 0x49, 0x41, 0x12, 0x12, 0x0f, 0x16, 0x0f,
+ 0x16, 0x07, 0x16, 0x41, 0x53, 0x16, 0x41, 0x53, 0x40, 0x40, 0x49, 0x0b, 0x0e, 0x06, 0x49,
+ 0x0f, 0x06, 0x41, 0x07, 0x22, 0x27, 0x0b, 0x41, 0x0c, 0x0d, 0x0f, 0x49, 0x2a, 0x19, 0x12,
+ 0x49, 0x15, 0x0d, 0x13, 0x06, 0x07, 0x16, 0x11, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x45,
+ 0x40, 0x50, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x01, 0x40, 0x40, 0x40, 0x01, 0x03, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x52,
+ 0x50, 0x43, 0x07, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x4a, 0x41,
+ 0x49, 0x48, 0x07, 0x49, 0x59, 0x48, 0x42, 0x52, 0x60, 0x59, 0x52, 0x44, 0x05, 0x06, 0x03,
+ 0x4a, 0x4a, 0x41, 0x49, 0x48, 0x07, 0x49, 0x59, 0x48, 0x42, 0x52, 0x60, 0x59, 0x52, 0x44,
+ 0x05, 0x06, 0x03, 0x14, 0x4f, 0x27, 0x50, 0x52, 0x52, 0x49, 0x4a, 0x4a, 0x43, 0x41, 0x05,
+ 0x41, 0x0d, 0x49, 0x50, 0x29, 0x07, 0x49, 0x0d, 0x49, 0x50, 0x29, 0x07, 0x49, 0x0d, 0x49,
+ 0x50, 0x29, 0x07, 0x49, 0x48, 0x40, 0x11, 0x11, 0x0f, 0x17, 0x0f, 0x17, 0x07, 0x17, 0x40,
+ 0x52, 0x17, 0x40, 0x52, 0x40, 0x40, 0x48, 0x0c, 0x0f, 0x07, 0x48, 0x0f, 0x07, 0x40, 0x07,
+ 0x23, 0x27, 0x0c, 0x40, 0x0d, 0x0e, 0x0f, 0x48, 0x29, 0x18, 0x11, 0x48, 0x14, 0x0e, 0x12,
+ 0x07, 0x07, 0x17, 0x10, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x46, 0x40, 0x50, 0x58, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x40, 0x40, 0x40, 0x00, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x51, 0x50, 0x42, 0x07, 0x10,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x41, 0x49, 0x48, 0x07, 0x49,
+ 0x59, 0x48, 0x41, 0x51, 0x60, 0x59, 0x51, 0x42, 0x06, 0x06, 0x04, 0x49, 0x49, 0x41, 0x49,
+ 0x48, 0x07, 0x49, 0x59, 0x48, 0x41, 0x51, 0x60, 0x59, 0x51, 0x42, 0x06, 0x06, 0x04, 0x15,
+ 0x4f, 0x27, 0x50, 0x51, 0x51, 0x49, 0x49, 0x49, 0x42, 0x41, 0x06, 0x41, 0x0e, 0x49, 0x50,
+ 0x28, 0x07, 0x49, 0x0e, 0x49, 0x50, 0x28, 0x07, 0x49, 0x0e, 0x49, 0x50, 0x28, 0x07, 0x49,
+ 0x48, 0x40, 0x10, 0x10, 0x0f, 0x17, 0x0f, 0x17, 0x07, 0x17, 0x40, 0x51, 0x17, 0x40, 0x51,
+ 0x40, 0x40, 0x48, 0x0d, 0x0f, 0x07, 0x48, 0x0f, 0x07, 0x40, 0x07, 0x25, 0x27, 0x0d, 0x40,
+ 0x0e, 0x0e, 0x0f, 0x48, 0x28, 0x18, 0x10, 0x48, 0x12, 0x0e, 0x11, 0x07, 0x07, 0x17, 0x10,
+ 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x40, 0x50, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x40, 0x40, 0x40, 0x00, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x50, 0x50, 0x40, 0x07, 0x10, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x40, 0x48, 0x48, 0x07, 0x48, 0x58, 0x48, 0x40, 0x50,
+ 0x60, 0x58, 0x50, 0x40, 0x07, 0x07, 0x06, 0x48, 0x48, 0x40, 0x48, 0x48, 0x07, 0x48, 0x58,
+ 0x48, 0x40, 0x50, 0x60, 0x58, 0x50, 0x40, 0x07, 0x07, 0x06, 0x17, 0x4f, 0x27, 0x50, 0x50,
+ 0x50, 0x48, 0x48, 0x48, 0x40, 0x40, 0x07, 0x40, 0x0f, 0x48, 0x50, 0x28, 0x07, 0x48, 0x0f,
+ 0x48, 0x50, 0x28, 0x07, 0x48, 0x0f, 0x48, 0x50, 0x28, 0x07, 0x48, 0x48, 0x40, 0x10, 0x10,
+ 0x0f, 0x17, 0x0f, 0x17, 0x07, 0x17, 0x40, 0x50, 0x17, 0x40, 0x50, 0x40, 0x40, 0x48, 0x0f,
+ 0x0f, 0x07, 0x48, 0x0f, 0x07, 0x40, 0x07, 0x27, 0x27, 0x0f, 0x40, 0x0f, 0x0f, 0x0f, 0x48,
+ 0x28, 0x18, 0x10, 0x48, 0x10, 0x0f, 0x10, 0x07, 0x07, 0x17, 0x10, 0x0f, 0x0f, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x48, 0x00, 0x4f, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x08, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07,
+ 0x08, 0x08, 0x4f, 0x4f, 0x00, 0x08, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x47, 0x47, 0x00, 0x47, 0x47, 0x07, 0x47, 0x57, 0x47, 0x00, 0x4f, 0x5f, 0x57, 0x4f, 0x00,
+ 0x08, 0x08, 0x08, 0x47, 0x47, 0x00, 0x47, 0x47, 0x07, 0x47, 0x57, 0x47, 0x00, 0x4f, 0x5f,
+ 0x57, 0x4f, 0x00, 0x08, 0x08, 0x08, 0x18, 0x50, 0x28, 0x4f, 0x4f, 0x4f, 0x47, 0x47, 0x47,
+ 0x00, 0x00, 0x08, 0x00, 0x10, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x10, 0x47, 0x4f, 0x27, 0x07,
+ 0x47, 0x10, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x47, 0x00, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18,
+ 0x07, 0x18, 0x00, 0x4f, 0x18, 0x00, 0x4f, 0x40, 0x40, 0x47, 0x10, 0x10, 0x08, 0x47, 0x0f,
+ 0x08, 0x00, 0x07, 0x28, 0x27, 0x10, 0x00, 0x10, 0x10, 0x0f, 0x47, 0x27, 0x17, 0x0f, 0x47,
+ 0x0f, 0x10, 0x0f, 0x08, 0x07, 0x18, 0x0f, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x49, 0x00,
+ 0x4f, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0a, 0x00, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x08, 0x08, 0x4e, 0x4f,
+ 0x01, 0x08, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x46, 0x46, 0x00, 0x47,
+ 0x47, 0x07, 0x47, 0x57, 0x47, 0x01, 0x4e, 0x5f, 0x57, 0x4e, 0x02, 0x09, 0x08, 0x0a, 0x46,
+ 0x46, 0x00, 0x47, 0x47, 0x07, 0x47, 0x57, 0x47, 0x01, 0x4e, 0x5f, 0x57, 0x4e, 0x02, 0x09,
+ 0x08, 0x0a, 0x19, 0x50, 0x28, 0x4f, 0x4e, 0x4e, 0x47, 0x46, 0x46, 0x01, 0x00, 0x09, 0x00,
+ 0x11, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x11, 0x47, 0x4f, 0x27, 0x07, 0x47, 0x11, 0x47, 0x4f,
+ 0x27, 0x07, 0x47, 0x47, 0x00, 0x0f, 0x0f, 0x0f, 0x18, 0x0f, 0x18, 0x07, 0x18, 0x00, 0x4e,
+ 0x18, 0x00, 0x4e, 0x40, 0x40, 0x47, 0x11, 0x10, 0x08, 0x47, 0x0f, 0x08, 0x00, 0x07, 0x2a,
+ 0x27, 0x11, 0x00, 0x11, 0x10, 0x0f, 0x47, 0x27, 0x17, 0x0f, 0x47, 0x0d, 0x10, 0x0e, 0x08,
+ 0x07, 0x18, 0x0f, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4a, 0x00, 0x4f, 0x58, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x41, 0x40, 0x40, 0x40, 0x41, 0x0c, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x08, 0x08, 0x4d, 0x4f, 0x02, 0x08, 0x0e, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x45, 0x45, 0x01, 0x46, 0x47, 0x07, 0x46, 0x56,
+ 0x47, 0x02, 0x4d, 0x5f, 0x56, 0x4d, 0x03, 0x0a, 0x09, 0x0c, 0x45, 0x45, 0x01, 0x46, 0x47,
+ 0x07, 0x46, 0x56, 0x47, 0x02, 0x4d, 0x5f, 0x56, 0x4d, 0x03, 0x0a, 0x09, 0x0c, 0x1a, 0x50,
+ 0x28, 0x4f, 0x4d, 0x4d, 0x46, 0x45, 0x45, 0x02, 0x01, 0x0a, 0x01, 0x12, 0x46, 0x4f, 0x26,
+ 0x07, 0x46, 0x12, 0x46, 0x4f, 0x26, 0x07, 0x46, 0x12, 0x46, 0x4f, 0x26, 0x07, 0x46, 0x47,
+ 0x00, 0x0e, 0x0e, 0x0f, 0x18, 0x0f, 0x18, 0x07, 0x18, 0x00, 0x4d, 0x18, 0x00, 0x4d, 0x40,
+ 0x40, 0x47, 0x12, 0x10, 0x08, 0x47, 0x0f, 0x08, 0x00, 0x07, 0x2b, 0x27, 0x12, 0x00, 0x12,
+ 0x11, 0x0f, 0x47, 0x26, 0x17, 0x0e, 0x47, 0x0c, 0x11, 0x0d, 0x08, 0x07, 0x18, 0x0f, 0x0f,
+ 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x4b, 0x01, 0x4e, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42,
+ 0x40, 0x40, 0x40, 0x42, 0x0e, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x07, 0x09, 0x09, 0x4c, 0x4e, 0x04, 0x09, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x01, 0x01, 0x44, 0x44, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46, 0x03, 0x4c, 0x5e,
+ 0x55, 0x4c, 0x05, 0x0b, 0x0a, 0x0e, 0x44, 0x44, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46,
+ 0x03, 0x4c, 0x5e, 0x55, 0x4c, 0x05, 0x0b, 0x0a, 0x0e, 0x1c, 0x51, 0x29, 0x4e, 0x4c, 0x4c,
+ 0x45, 0x44, 0x44, 0x04, 0x02, 0x0b, 0x02, 0x13, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x13, 0x45,
+ 0x4e, 0x25, 0x07, 0x45, 0x13, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x46, 0x01, 0x0d, 0x0d, 0x0f,
+ 0x19, 0x0f, 0x19, 0x07, 0x19, 0x01, 0x4c, 0x19, 0x01, 0x4c, 0x40, 0x40, 0x46, 0x14, 0x11,
+ 0x09, 0x46, 0x0f, 0x09, 0x01, 0x07, 0x2d, 0x27, 0x14, 0x01, 0x13, 0x12, 0x0f, 0x46, 0x25,
+ 0x16, 0x0d, 0x46, 0x0a, 0x12, 0x0c, 0x09, 0x07, 0x19, 0x0e, 0x0f, 0x0f, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x4c, 0x01, 0x4e, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x42, 0x40, 0x40, 0x40, 0x42,
+ 0x10, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x09,
+ 0x09, 0x4b, 0x4e, 0x05, 0x09, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x43,
+ 0x43, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46, 0x04, 0x4b, 0x5e, 0x55, 0x4b, 0x06, 0x0c,
+ 0x0a, 0x10, 0x43, 0x43, 0x02, 0x45, 0x46, 0x07, 0x45, 0x55, 0x46, 0x04, 0x4b, 0x5e, 0x55,
+ 0x4b, 0x06, 0x0c, 0x0a, 0x10, 0x1d, 0x51, 0x29, 0x4e, 0x4b, 0x4b, 0x45, 0x43, 0x43, 0x05,
+ 0x02, 0x0c, 0x02, 0x14, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x14, 0x45, 0x4e, 0x25, 0x07, 0x45,
+ 0x14, 0x45, 0x4e, 0x25, 0x07, 0x45, 0x46, 0x01, 0x0d, 0x0d, 0x0f, 0x19, 0x0f, 0x19, 0x07,
+ 0x19, 0x01, 0x4b, 0x19, 0x01, 0x4b, 0x40, 0x40, 0x46, 0x15, 0x11, 0x09, 0x46, 0x0f, 0x09,
+ 0x01, 0x07, 0x2e, 0x27, 0x15, 0x01, 0x14, 0x12, 0x0f, 0x46, 0x25, 0x16, 0x0d, 0x46, 0x09,
+ 0x12, 0x0b, 0x09, 0x07, 0x19, 0x0e, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4d, 0x01, 0x4e,
+ 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x43, 0x40, 0x40, 0x40, 0x43, 0x12, 0x01, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x09, 0x09, 0x4a, 0x4e, 0x06,
+ 0x09, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x42, 0x42, 0x03, 0x44, 0x46,
+ 0x07, 0x44, 0x54, 0x46, 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x08, 0x0d, 0x0b, 0x12, 0x42, 0x42,
+ 0x03, 0x44, 0x46, 0x07, 0x44, 0x54, 0x46, 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x08, 0x0d, 0x0b,
+ 0x12, 0x1e, 0x51, 0x29, 0x4e, 0x4a, 0x4a, 0x44, 0x42, 0x42, 0x06, 0x03, 0x0d, 0x03, 0x15,
+ 0x44, 0x4e, 0x24, 0x07, 0x44, 0x15, 0x44, 0x4e, 0x24, 0x07, 0x44, 0x15, 0x44, 0x4e, 0x24,
+ 0x07, 0x44, 0x46, 0x01, 0x0c, 0x0c, 0x0f, 0x19, 0x0f, 0x19, 0x07, 0x19, 0x01, 0x4a, 0x19,
+ 0x01, 0x4a, 0x40, 0x40, 0x46, 0x16, 0x11, 0x09, 0x46, 0x0f, 0x09, 0x01, 0x07, 0x30, 0x27,
+ 0x16, 0x01, 0x15, 0x13, 0x0f, 0x46, 0x24, 0x16, 0x0c, 0x46, 0x07, 0x13, 0x0a, 0x09, 0x07,
+ 0x19, 0x0e, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4e, 0x01, 0x4e, 0x58, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x44, 0x40, 0x40, 0x40, 0x44, 0x13, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x09, 0x09, 0x4a, 0x4e, 0x07, 0x09, 0x0b, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x42, 0x42, 0x03, 0x44, 0x46, 0x07, 0x44, 0x54, 0x46,
+ 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x09, 0x0d, 0x0b, 0x13, 0x42, 0x42, 0x03, 0x44, 0x46, 0x07,
+ 0x44, 0x54, 0x46, 0x05, 0x4a, 0x5e, 0x54, 0x4a, 0x09, 0x0d, 0x0b, 0x13, 0x1f, 0x52, 0x29,
+ 0x4e, 0x4a, 0x4a, 0x44, 0x42, 0x42, 0x07, 0x03, 0x0d, 0x03, 0x15, 0x44, 0x4e, 0x23, 0x07,
+ 0x44, 0x15, 0x44, 0x4e, 0x23, 0x07, 0x44, 0x15, 0x44, 0x4e, 0x23, 0x07, 0x44, 0x46, 0x01,
+ 0x0b, 0x0b, 0x0f, 0x19, 0x0f, 0x19, 0x07, 0x19, 0x01, 0x4a, 0x19, 0x01, 0x4a, 0x40, 0x40,
+ 0x46, 0x17, 0x11, 0x09, 0x46, 0x0f, 0x09, 0x01, 0x07, 0x31, 0x27, 0x17, 0x01, 0x15, 0x13,
+ 0x0f, 0x46, 0x23, 0x15, 0x0b, 0x46, 0x05, 0x13, 0x09, 0x09, 0x07, 0x19, 0x0d, 0x0f, 0x0f,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x4e, 0x02, 0x4d, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x44, 0x40,
+ 0x40, 0x40, 0x44, 0x15, 0x02, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x07, 0x0a, 0x0a, 0x49, 0x4d, 0x09, 0x0a, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x02, 0x02, 0x41, 0x41, 0x04, 0x43, 0x45, 0x07, 0x43, 0x53, 0x45, 0x06, 0x49, 0x5d, 0x53,
+ 0x49, 0x0b, 0x0e, 0x0c, 0x15, 0x41, 0x41, 0x04, 0x43, 0x45, 0x07, 0x43, 0x53, 0x45, 0x06,
+ 0x49, 0x5d, 0x53, 0x49, 0x0b, 0x0e, 0x0c, 0x15, 0x21, 0x52, 0x2a, 0x4d, 0x49, 0x49, 0x43,
+ 0x41, 0x41, 0x09, 0x04, 0x0e, 0x04, 0x16, 0x43, 0x4d, 0x23, 0x07, 0x43, 0x16, 0x43, 0x4d,
+ 0x23, 0x07, 0x43, 0x16, 0x43, 0x4d, 0x23, 0x07, 0x43, 0x45, 0x02, 0x0b, 0x0b, 0x0f, 0x1a,
+ 0x0f, 0x1a, 0x07, 0x1a, 0x02, 0x49, 0x1a, 0x02, 0x49, 0x40, 0x40, 0x45, 0x19, 0x12, 0x0a,
+ 0x45, 0x0f, 0x0a, 0x02, 0x07, 0x33, 0x27, 0x19, 0x02, 0x16, 0x14, 0x0f, 0x45, 0x23, 0x15,
+ 0x0b, 0x45, 0x04, 0x14, 0x09, 0x0a, 0x07, 0x1a, 0x0d, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x4f, 0x02, 0x4d, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x40, 0x40, 0x40, 0x45, 0x17,
+ 0x02, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0a, 0x0a,
+ 0x48, 0x4d, 0x0a, 0x0a, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x40, 0x40,
+ 0x05, 0x42, 0x45, 0x07, 0x42, 0x52, 0x45, 0x07, 0x48, 0x5d, 0x52, 0x48, 0x0d, 0x0f, 0x0d,
+ 0x17, 0x40, 0x40, 0x05, 0x42, 0x45, 0x07, 0x42, 0x52, 0x45, 0x07, 0x48, 0x5d, 0x52, 0x48,
+ 0x0d, 0x0f, 0x0d, 0x17, 0x22, 0x52, 0x2a, 0x4d, 0x48, 0x48, 0x42, 0x40, 0x40, 0x0a, 0x05,
+ 0x0f, 0x05, 0x17, 0x42, 0x4d, 0x22, 0x07, 0x42, 0x17, 0x42, 0x4d, 0x22, 0x07, 0x42, 0x17,
+ 0x42, 0x4d, 0x22, 0x07, 0x42, 0x45, 0x02, 0x0a, 0x0a, 0x0f, 0x1a, 0x0f, 0x1a, 0x07, 0x1a,
+ 0x02, 0x48, 0x1a, 0x02, 0x48, 0x40, 0x40, 0x45, 0x1a, 0x12, 0x0a, 0x45, 0x0f, 0x0a, 0x02,
+ 0x07, 0x35, 0x27, 0x1a, 0x02, 0x17, 0x15, 0x0f, 0x45, 0x22, 0x15, 0x0a, 0x45, 0x02, 0x15,
+ 0x08, 0x0a, 0x07, 0x1a, 0x0d, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x50, 0x02, 0x4d, 0x58,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x45, 0x40, 0x40, 0x40, 0x45, 0x19, 0x02, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0a, 0x0a, 0x47, 0x4d, 0x0b, 0x0a,
+ 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x00, 0x00, 0x05, 0x42, 0x45, 0x07,
+ 0x42, 0x52, 0x45, 0x08, 0x47, 0x5d, 0x52, 0x47, 0x0e, 0x10, 0x0d, 0x19, 0x00, 0x00, 0x05,
+ 0x42, 0x45, 0x07, 0x42, 0x52, 0x45, 0x08, 0x47, 0x5d, 0x52, 0x47, 0x0e, 0x10, 0x0d, 0x19,
+ 0x23, 0x52, 0x2a, 0x4d, 0x47, 0x47, 0x42, 0x00, 0x00, 0x0b, 0x05, 0x10, 0x05, 0x18, 0x42,
+ 0x4d, 0x22, 0x07, 0x42, 0x18, 0x42, 0x4d, 0x22, 0x07, 0x42, 0x18, 0x42, 0x4d, 0x22, 0x07,
+ 0x42, 0x45, 0x02, 0x0a, 0x0a, 0x0f, 0x1a, 0x0f, 0x1a, 0x07, 0x1a, 0x02, 0x47, 0x1a, 0x02,
+ 0x47, 0x40, 0x40, 0x45, 0x1b, 0x12, 0x0a, 0x45, 0x0f, 0x0a, 0x02, 0x07, 0x36, 0x27, 0x1b,
+ 0x02, 0x18, 0x15, 0x0f, 0x45, 0x22, 0x15, 0x0a, 0x45, 0x01, 0x15, 0x07, 0x0a, 0x07, 0x1a,
+ 0x0d, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x51, 0x03, 0x4c, 0x58, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x46, 0x40, 0x40, 0x40, 0x46, 0x1b, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x07, 0x0b, 0x0b, 0x46, 0x4c, 0x0c, 0x0b, 0x09, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x03, 0x03, 0x01, 0x01, 0x06, 0x41, 0x44, 0x07, 0x41, 0x51, 0x44, 0x09,
+ 0x46, 0x5c, 0x51, 0x46, 0x10, 0x11, 0x0e, 0x1b, 0x01, 0x01, 0x06, 0x41, 0x44, 0x07, 0x41,
+ 0x51, 0x44, 0x09, 0x46, 0x5c, 0x51, 0x46, 0x10, 0x11, 0x0e, 0x1b, 0x24, 0x53, 0x2b, 0x4c,
+ 0x46, 0x46, 0x41, 0x01, 0x01, 0x0c, 0x06, 0x11, 0x06, 0x19, 0x41, 0x4c, 0x21, 0x07, 0x41,
+ 0x19, 0x41, 0x4c, 0x21, 0x07, 0x41, 0x19, 0x41, 0x4c, 0x21, 0x07, 0x41, 0x44, 0x03, 0x09,
+ 0x09, 0x0f, 0x1b, 0x0f, 0x1b, 0x07, 0x1b, 0x03, 0x46, 0x1b, 0x03, 0x46, 0x40, 0x40, 0x44,
+ 0x1c, 0x13, 0x0b, 0x44, 0x0f, 0x0b, 0x03, 0x07, 0x38, 0x27, 0x1c, 0x03, 0x19, 0x16, 0x0f,
+ 0x44, 0x21, 0x14, 0x09, 0x44, 0x40, 0x16, 0x06, 0x0b, 0x07, 0x1b, 0x0c, 0x0f, 0x0f, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x52, 0x03, 0x4c, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x47, 0x40, 0x40,
+ 0x40, 0x47, 0x1d, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x07, 0x0b, 0x0b, 0x45, 0x4c, 0x0e, 0x0b, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x03,
+ 0x03, 0x02, 0x02, 0x07, 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0a, 0x45, 0x5c, 0x50, 0x45,
+ 0x11, 0x12, 0x0f, 0x1d, 0x02, 0x02, 0x07, 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0a, 0x45,
+ 0x5c, 0x50, 0x45, 0x11, 0x12, 0x0f, 0x1d, 0x26, 0x53, 0x2b, 0x4c, 0x45, 0x45, 0x40, 0x02,
+ 0x02, 0x0e, 0x07, 0x12, 0x07, 0x1a, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x1a, 0x40, 0x4c, 0x20,
+ 0x07, 0x40, 0x1a, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x44, 0x03, 0x08, 0x08, 0x0f, 0x1b, 0x0f,
+ 0x1b, 0x07, 0x1b, 0x03, 0x45, 0x1b, 0x03, 0x45, 0x40, 0x40, 0x44, 0x1e, 0x13, 0x0b, 0x44,
+ 0x0f, 0x0b, 0x03, 0x07, 0x39, 0x27, 0x1e, 0x03, 0x1a, 0x17, 0x0f, 0x44, 0x20, 0x14, 0x08,
+ 0x44, 0x41, 0x17, 0x05, 0x0b, 0x07, 0x1b, 0x0c, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x53,
+ 0x03, 0x4c, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x47, 0x40, 0x40, 0x40, 0x47, 0x1f, 0x03,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0b, 0x0b, 0x44,
+ 0x4c, 0x0f, 0x0b, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x03, 0x03, 0x03, 0x03, 0x07,
+ 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0b, 0x44, 0x5c, 0x50, 0x44, 0x13, 0x13, 0x0f, 0x1f,
+ 0x03, 0x03, 0x07, 0x40, 0x44, 0x07, 0x40, 0x50, 0x44, 0x0b, 0x44, 0x5c, 0x50, 0x44, 0x13,
+ 0x13, 0x0f, 0x1f, 0x27, 0x53, 0x2b, 0x4c, 0x44, 0x44, 0x40, 0x03, 0x03, 0x0f, 0x07, 0x13,
+ 0x07, 0x1b, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x1b, 0x40, 0x4c, 0x20, 0x07, 0x40, 0x1b, 0x40,
+ 0x4c, 0x20, 0x07, 0x40, 0x44, 0x03, 0x08, 0x08, 0x0f, 0x1b, 0x0f, 0x1b, 0x07, 0x1b, 0x03,
+ 0x44, 0x1b, 0x03, 0x44, 0x40, 0x40, 0x44, 0x1f, 0x13, 0x0b, 0x44, 0x0f, 0x0b, 0x03, 0x07,
+ 0x3b, 0x27, 0x1f, 0x03, 0x1b, 0x17, 0x0f, 0x44, 0x20, 0x14, 0x08, 0x44, 0x43, 0x17, 0x04,
+ 0x0b, 0x07, 0x1b, 0x0c, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x54, 0x04, 0x4b, 0x58, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x48, 0x40, 0x40, 0x40, 0x48, 0x21, 0x04, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0c, 0x0c, 0x43, 0x4b, 0x10, 0x0c, 0x07,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x04, 0x04, 0x04, 0x08, 0x00, 0x43, 0x07, 0x00,
+ 0x4f, 0x43, 0x0c, 0x43, 0x5b, 0x4f, 0x43, 0x14, 0x14, 0x10, 0x21, 0x04, 0x04, 0x08, 0x00,
+ 0x43, 0x07, 0x00, 0x4f, 0x43, 0x0c, 0x43, 0x5b, 0x4f, 0x43, 0x14, 0x14, 0x10, 0x21, 0x28,
+ 0x54, 0x2c, 0x4b, 0x43, 0x43, 0x00, 0x04, 0x04, 0x10, 0x08, 0x14, 0x08, 0x1c, 0x00, 0x4b,
+ 0x1f, 0x07, 0x00, 0x1c, 0x00, 0x4b, 0x1f, 0x07, 0x00, 0x1c, 0x00, 0x4b, 0x1f, 0x07, 0x00,
+ 0x43, 0x04, 0x07, 0x07, 0x0f, 0x1c, 0x0f, 0x1c, 0x07, 0x1c, 0x04, 0x43, 0x1c, 0x04, 0x43,
+ 0x40, 0x40, 0x43, 0x20, 0x14, 0x0c, 0x43, 0x0f, 0x0c, 0x04, 0x07, 0x3c, 0x27, 0x20, 0x04,
+ 0x1c, 0x18, 0x0f, 0x43, 0x1f, 0x13, 0x07, 0x43, 0x44, 0x18, 0x03, 0x0c, 0x07, 0x1c, 0x0b,
+ 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x55, 0x04, 0x4b, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x49, 0x40, 0x40, 0x40, 0x49, 0x22, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x07, 0x0c, 0x0c, 0x42, 0x4b, 0x11, 0x0c, 0x06, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x04, 0x04, 0x05, 0x05, 0x08, 0x00, 0x43, 0x07, 0x00, 0x4f, 0x43, 0x0d, 0x42,
+ 0x5b, 0x4f, 0x42, 0x16, 0x15, 0x10, 0x22, 0x05, 0x05, 0x08, 0x00, 0x43, 0x07, 0x00, 0x4f,
+ 0x43, 0x0d, 0x42, 0x5b, 0x4f, 0x42, 0x16, 0x15, 0x10, 0x22, 0x29, 0x54, 0x2c, 0x4b, 0x42,
+ 0x42, 0x00, 0x05, 0x05, 0x11, 0x08, 0x15, 0x08, 0x1d, 0x00, 0x4b, 0x1e, 0x07, 0x00, 0x1d,
+ 0x00, 0x4b, 0x1e, 0x07, 0x00, 0x1d, 0x00, 0x4b, 0x1e, 0x07, 0x00, 0x43, 0x04, 0x06, 0x06,
+ 0x0f, 0x1c, 0x0f, 0x1c, 0x07, 0x1c, 0x04, 0x42, 0x1c, 0x04, 0x42, 0x40, 0x40, 0x43, 0x21,
+ 0x14, 0x0c, 0x43, 0x0f, 0x0c, 0x04, 0x07, 0x3e, 0x27, 0x21, 0x04, 0x1d, 0x18, 0x0f, 0x43,
+ 0x1e, 0x13, 0x06, 0x43, 0x46, 0x18, 0x02, 0x0c, 0x07, 0x1c, 0x0b, 0x0f, 0x0f, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x56, 0x04, 0x4b, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x40, 0x40, 0x40,
+ 0x49, 0x24, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07,
+ 0x0c, 0x0c, 0x41, 0x4b, 0x13, 0x0c, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x04, 0x04,
+ 0x06, 0x06, 0x09, 0x01, 0x43, 0x07, 0x01, 0x4e, 0x43, 0x0e, 0x41, 0x5b, 0x4e, 0x41, 0x18,
+ 0x16, 0x11, 0x24, 0x06, 0x06, 0x09, 0x01, 0x43, 0x07, 0x01, 0x4e, 0x43, 0x0e, 0x41, 0x5b,
+ 0x4e, 0x41, 0x18, 0x16, 0x11, 0x24, 0x2b, 0x54, 0x2c, 0x4b, 0x41, 0x41, 0x01, 0x06, 0x06,
+ 0x13, 0x09, 0x16, 0x09, 0x1e, 0x01, 0x4b, 0x1e, 0x07, 0x01, 0x1e, 0x01, 0x4b, 0x1e, 0x07,
+ 0x01, 0x1e, 0x01, 0x4b, 0x1e, 0x07, 0x01, 0x43, 0x04, 0x06, 0x06, 0x0f, 0x1c, 0x0f, 0x1c,
+ 0x07, 0x1c, 0x04, 0x41, 0x1c, 0x04, 0x41, 0x40, 0x40, 0x43, 0x23, 0x14, 0x0c, 0x43, 0x0f,
+ 0x0c, 0x04, 0x07, 0x3e, 0x27, 0x23, 0x04, 0x1e, 0x19, 0x0f, 0x43, 0x1e, 0x13, 0x06, 0x43,
+ 0x48, 0x19, 0x01, 0x0c, 0x07, 0x1c, 0x0b, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x57, 0x05,
+ 0x4a, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4a, 0x40, 0x40, 0x40, 0x4a, 0x26, 0x05, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0d, 0x0d, 0x40, 0x4a,
+ 0x14, 0x0d, 0x05, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x05, 0x05, 0x07, 0x07, 0x0a, 0x02,
+ 0x42, 0x07, 0x02, 0x4d, 0x42, 0x0f, 0x40, 0x5a, 0x4d, 0x40, 0x19, 0x17, 0x12, 0x26, 0x07,
+ 0x07, 0x0a, 0x02, 0x42, 0x07, 0x02, 0x4d, 0x42, 0x0f, 0x40, 0x5a, 0x4d, 0x40, 0x19, 0x17,
+ 0x12, 0x26, 0x2c, 0x55, 0x2d, 0x4a, 0x40, 0x40, 0x02, 0x07, 0x07, 0x14, 0x0a, 0x17, 0x0a,
+ 0x1f, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x1f, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x1f, 0x02, 0x4a,
+ 0x1d, 0x07, 0x02, 0x42, 0x05, 0x05, 0x05, 0x0f, 0x1d, 0x0f, 0x1d, 0x07, 0x1d, 0x05, 0x40,
+ 0x1d, 0x05, 0x40, 0x40, 0x40, 0x42, 0x24, 0x15, 0x0d, 0x42, 0x0f, 0x0d, 0x05, 0x07, 0x3e,
+ 0x27, 0x24, 0x05, 0x1f, 0x1a, 0x0f, 0x42, 0x1d, 0x12, 0x05, 0x42, 0x49, 0x1a, 0x00, 0x0d,
+ 0x07, 0x1d, 0x0a, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x58, 0x05, 0x4a, 0x58, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x4a, 0x40, 0x40, 0x40, 0x4a, 0x28, 0x05, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0d, 0x0d, 0x00, 0x4a, 0x15, 0x0d, 0x05, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x05, 0x05, 0x08, 0x08, 0x0a, 0x02, 0x42, 0x07, 0x02, 0x4d,
+ 0x42, 0x10, 0x00, 0x5a, 0x4d, 0x00, 0x1b, 0x18, 0x12, 0x28, 0x08, 0x08, 0x0a, 0x02, 0x42,
+ 0x07, 0x02, 0x4d, 0x42, 0x10, 0x00, 0x5a, 0x4d, 0x00, 0x1b, 0x18, 0x12, 0x28, 0x2d, 0x55,
+ 0x2d, 0x4a, 0x00, 0x00, 0x02, 0x08, 0x08, 0x15, 0x0a, 0x18, 0x0a, 0x20, 0x02, 0x4a, 0x1d,
+ 0x07, 0x02, 0x20, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x20, 0x02, 0x4a, 0x1d, 0x07, 0x02, 0x42,
+ 0x05, 0x05, 0x05, 0x0f, 0x1d, 0x0f, 0x1d, 0x07, 0x1d, 0x05, 0x00, 0x1d, 0x05, 0x00, 0x40,
+ 0x40, 0x42, 0x25, 0x15, 0x0d, 0x42, 0x0f, 0x0d, 0x05, 0x07, 0x3e, 0x27, 0x25, 0x05, 0x20,
+ 0x1a, 0x0f, 0x42, 0x1d, 0x12, 0x05, 0x42, 0x4b, 0x1a, 0x40, 0x0d, 0x07, 0x1d, 0x0a, 0x0f,
+ 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x59, 0x05, 0x4a, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4b,
+ 0x40, 0x40, 0x40, 0x4b, 0x2a, 0x05, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x07, 0x0d, 0x0d, 0x01, 0x4a, 0x16, 0x0d, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x05, 0x05, 0x09, 0x09, 0x0b, 0x03, 0x42, 0x07, 0x03, 0x4c, 0x42, 0x11, 0x01, 0x5a,
+ 0x4c, 0x01, 0x1c, 0x19, 0x13, 0x2a, 0x09, 0x09, 0x0b, 0x03, 0x42, 0x07, 0x03, 0x4c, 0x42,
+ 0x11, 0x01, 0x5a, 0x4c, 0x01, 0x1c, 0x19, 0x13, 0x2a, 0x2e, 0x55, 0x2d, 0x4a, 0x01, 0x01,
+ 0x03, 0x09, 0x09, 0x16, 0x0b, 0x19, 0x0b, 0x21, 0x03, 0x4a, 0x1c, 0x07, 0x03, 0x21, 0x03,
+ 0x4a, 0x1c, 0x07, 0x03, 0x21, 0x03, 0x4a, 0x1c, 0x07, 0x03, 0x42, 0x05, 0x04, 0x04, 0x0f,
+ 0x1d, 0x0f, 0x1d, 0x07, 0x1d, 0x05, 0x01, 0x1d, 0x05, 0x01, 0x40, 0x40, 0x42, 0x26, 0x15,
+ 0x0d, 0x42, 0x0f, 0x0d, 0x05, 0x07, 0x3e, 0x27, 0x26, 0x05, 0x21, 0x1b, 0x0f, 0x42, 0x1c,
+ 0x12, 0x04, 0x42, 0x4c, 0x1b, 0x41, 0x0d, 0x07, 0x1d, 0x0a, 0x0f, 0x0f, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x5a, 0x06, 0x49, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4c, 0x40, 0x40, 0x40, 0x4c,
+ 0x2c, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0e,
+ 0x0e, 0x02, 0x49, 0x18, 0x0e, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x06, 0x06, 0x0a,
+ 0x0a, 0x0c, 0x04, 0x41, 0x07, 0x04, 0x4b, 0x41, 0x12, 0x02, 0x59, 0x4b, 0x02, 0x1e, 0x1a,
+ 0x14, 0x2c, 0x0a, 0x0a, 0x0c, 0x04, 0x41, 0x07, 0x04, 0x4b, 0x41, 0x12, 0x02, 0x59, 0x4b,
+ 0x02, 0x1e, 0x1a, 0x14, 0x2c, 0x30, 0x56, 0x2e, 0x49, 0x02, 0x02, 0x04, 0x0a, 0x0a, 0x18,
+ 0x0c, 0x1a, 0x0c, 0x22, 0x04, 0x49, 0x1b, 0x07, 0x04, 0x22, 0x04, 0x49, 0x1b, 0x07, 0x04,
+ 0x22, 0x04, 0x49, 0x1b, 0x07, 0x04, 0x41, 0x06, 0x03, 0x03, 0x0f, 0x1e, 0x0f, 0x1e, 0x07,
+ 0x1e, 0x06, 0x02, 0x1e, 0x06, 0x02, 0x40, 0x40, 0x41, 0x28, 0x16, 0x0e, 0x41, 0x0f, 0x0e,
+ 0x06, 0x07, 0x3e, 0x27, 0x28, 0x06, 0x22, 0x1c, 0x0f, 0x41, 0x1b, 0x11, 0x03, 0x41, 0x4e,
+ 0x1c, 0x42, 0x0e, 0x07, 0x1e, 0x09, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5b, 0x06, 0x49,
+ 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4c, 0x40, 0x40, 0x40, 0x4c, 0x2e, 0x06, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0e, 0x0e, 0x03, 0x49, 0x19,
+ 0x0e, 0x03, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x06, 0x06, 0x0b, 0x0b, 0x0c, 0x04, 0x41,
+ 0x07, 0x04, 0x4b, 0x41, 0x13, 0x03, 0x59, 0x4b, 0x03, 0x1f, 0x1b, 0x14, 0x2e, 0x0b, 0x0b,
+ 0x0c, 0x04, 0x41, 0x07, 0x04, 0x4b, 0x41, 0x13, 0x03, 0x59, 0x4b, 0x03, 0x1f, 0x1b, 0x14,
+ 0x2e, 0x31, 0x56, 0x2e, 0x49, 0x03, 0x03, 0x04, 0x0b, 0x0b, 0x19, 0x0c, 0x1b, 0x0c, 0x23,
+ 0x04, 0x49, 0x1b, 0x07, 0x04, 0x23, 0x04, 0x49, 0x1b, 0x07, 0x04, 0x23, 0x04, 0x49, 0x1b,
+ 0x07, 0x04, 0x41, 0x06, 0x03, 0x03, 0x0f, 0x1e, 0x0f, 0x1e, 0x07, 0x1e, 0x06, 0x03, 0x1e,
+ 0x06, 0x03, 0x40, 0x40, 0x41, 0x29, 0x16, 0x0e, 0x41, 0x0f, 0x0e, 0x06, 0x07, 0x3e, 0x27,
+ 0x29, 0x06, 0x23, 0x1c, 0x0f, 0x41, 0x1b, 0x11, 0x03, 0x41, 0x4f, 0x1c, 0x43, 0x0e, 0x07,
+ 0x1e, 0x09, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5c, 0x06, 0x49, 0x58, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x4d, 0x40, 0x40, 0x40, 0x4d, 0x30, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0e, 0x0e, 0x04, 0x49, 0x1a, 0x0e, 0x02, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x06, 0x06, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07, 0x05, 0x4a, 0x41,
+ 0x14, 0x04, 0x59, 0x4a, 0x04, 0x21, 0x1c, 0x15, 0x30, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07,
+ 0x05, 0x4a, 0x41, 0x14, 0x04, 0x59, 0x4a, 0x04, 0x21, 0x1c, 0x15, 0x30, 0x32, 0x56, 0x2e,
+ 0x49, 0x04, 0x04, 0x05, 0x0c, 0x0c, 0x1a, 0x0d, 0x1c, 0x0d, 0x24, 0x05, 0x49, 0x1a, 0x07,
+ 0x05, 0x24, 0x05, 0x49, 0x1a, 0x07, 0x05, 0x24, 0x05, 0x49, 0x1a, 0x07, 0x05, 0x41, 0x06,
+ 0x02, 0x02, 0x0f, 0x1e, 0x0f, 0x1e, 0x07, 0x1e, 0x06, 0x04, 0x1e, 0x06, 0x04, 0x40, 0x40,
+ 0x41, 0x2a, 0x16, 0x0e, 0x41, 0x0f, 0x0e, 0x06, 0x07, 0x3e, 0x27, 0x2a, 0x06, 0x24, 0x1d,
+ 0x0f, 0x41, 0x1a, 0x11, 0x02, 0x41, 0x51, 0x1d, 0x44, 0x0e, 0x07, 0x1e, 0x09, 0x0f, 0x0f,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x5d, 0x06, 0x49, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4e, 0x40,
+ 0x40, 0x40, 0x4e, 0x31, 0x06, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x07, 0x0e, 0x0e, 0x04, 0x49, 0x1b, 0x0e, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x06, 0x06, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07, 0x05, 0x4a, 0x41, 0x14, 0x04, 0x59, 0x4a,
+ 0x04, 0x22, 0x1c, 0x15, 0x31, 0x0c, 0x0c, 0x0d, 0x05, 0x41, 0x07, 0x05, 0x4a, 0x41, 0x14,
+ 0x04, 0x59, 0x4a, 0x04, 0x22, 0x1c, 0x15, 0x31, 0x33, 0x57, 0x2e, 0x49, 0x04, 0x04, 0x05,
+ 0x0c, 0x0c, 0x1b, 0x0d, 0x1c, 0x0d, 0x24, 0x05, 0x49, 0x19, 0x07, 0x05, 0x24, 0x05, 0x49,
+ 0x19, 0x07, 0x05, 0x24, 0x05, 0x49, 0x19, 0x07, 0x05, 0x41, 0x06, 0x01, 0x01, 0x0f, 0x1e,
+ 0x0f, 0x1e, 0x07, 0x1e, 0x06, 0x04, 0x1e, 0x06, 0x04, 0x40, 0x40, 0x41, 0x2b, 0x16, 0x0e,
+ 0x41, 0x0f, 0x0e, 0x06, 0x07, 0x3e, 0x27, 0x2b, 0x06, 0x24, 0x1d, 0x0f, 0x41, 0x19, 0x10,
+ 0x01, 0x41, 0x53, 0x1d, 0x45, 0x0e, 0x07, 0x1e, 0x08, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x5d, 0x07, 0x48, 0x58, 0x40, 0x40, 0x40, 0x40, 0x40, 0x4e, 0x40, 0x40, 0x40, 0x4e, 0x33,
+ 0x07, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0f, 0x0f,
+ 0x05, 0x48, 0x1d, 0x0f, 0x01, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x0d, 0x0d,
+ 0x0e, 0x06, 0x40, 0x07, 0x06, 0x49, 0x40, 0x15, 0x05, 0x58, 0x49, 0x05, 0x24, 0x1d, 0x16,
+ 0x33, 0x0d, 0x0d, 0x0e, 0x06, 0x40, 0x07, 0x06, 0x49, 0x40, 0x15, 0x05, 0x58, 0x49, 0x05,
+ 0x24, 0x1d, 0x16, 0x33, 0x35, 0x57, 0x2f, 0x48, 0x05, 0x05, 0x06, 0x0d, 0x0d, 0x1d, 0x0e,
+ 0x1d, 0x0e, 0x25, 0x06, 0x48, 0x19, 0x07, 0x06, 0x25, 0x06, 0x48, 0x19, 0x07, 0x06, 0x25,
+ 0x06, 0x48, 0x19, 0x07, 0x06, 0x40, 0x07, 0x01, 0x01, 0x0f, 0x1f, 0x0f, 0x1f, 0x07, 0x1f,
+ 0x07, 0x05, 0x1f, 0x07, 0x05, 0x40, 0x40, 0x40, 0x2d, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x07,
+ 0x07, 0x3e, 0x27, 0x2d, 0x07, 0x25, 0x1e, 0x0f, 0x40, 0x19, 0x10, 0x01, 0x40, 0x54, 0x1e,
+ 0x45, 0x0f, 0x07, 0x1f, 0x08, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5e, 0x07, 0x48, 0x58,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x4f, 0x40, 0x40, 0x40, 0x4f, 0x35, 0x07, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x0f, 0x0f, 0x06, 0x48, 0x1e, 0x0f,
+ 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x07, 0x07, 0x0e, 0x0e, 0x0f, 0x07, 0x40, 0x07,
+ 0x07, 0x48, 0x40, 0x16, 0x06, 0x58, 0x48, 0x06, 0x26, 0x1e, 0x17, 0x35, 0x0e, 0x0e, 0x0f,
+ 0x07, 0x40, 0x07, 0x07, 0x48, 0x40, 0x16, 0x06, 0x58, 0x48, 0x06, 0x26, 0x1e, 0x17, 0x35,
+ 0x36, 0x57, 0x2f, 0x48, 0x06, 0x06, 0x07, 0x0e, 0x0e, 0x1e, 0x0f, 0x1e, 0x0f, 0x26, 0x07,
+ 0x48, 0x18, 0x07, 0x07, 0x26, 0x07, 0x48, 0x18, 0x07, 0x07, 0x26, 0x07, 0x48, 0x18, 0x07,
+ 0x07, 0x40, 0x07, 0x00, 0x00, 0x0f, 0x1f, 0x0f, 0x1f, 0x07, 0x1f, 0x07, 0x06, 0x1f, 0x07,
+ 0x06, 0x40, 0x40, 0x40, 0x2e, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x07, 0x07, 0x3e, 0x27, 0x2e,
+ 0x07, 0x26, 0x1f, 0x0f, 0x40, 0x18, 0x10, 0x00, 0x40, 0x56, 0x1f, 0x46, 0x0f, 0x07, 0x1f,
+ 0x08, 0x0f, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x5f, 0x07, 0x48, 0x58, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x4f, 0x40, 0x40, 0x40, 0x4f, 0x37, 0x07, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x07, 0x0f, 0x0f, 0x07, 0x48, 0x1f, 0x0f, 0x00, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x07, 0x40, 0x07, 0x07, 0x48, 0x40, 0x17,
+ 0x07, 0x58, 0x48, 0x07, 0x27, 0x1f, 0x17, 0x37, 0x0f, 0x0f, 0x0f, 0x07, 0x40, 0x07, 0x07,
+ 0x48, 0x40, 0x17, 0x07, 0x58, 0x48, 0x07, 0x27, 0x1f, 0x17, 0x37, 0x37, 0x57, 0x2f, 0x48,
+ 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x1f, 0x0f, 0x1f, 0x0f, 0x27, 0x07, 0x48, 0x18, 0x07, 0x07,
+ 0x27, 0x07, 0x48, 0x18, 0x07, 0x07, 0x27, 0x07, 0x48, 0x18, 0x07, 0x07, 0x40, 0x07, 0x00,
+ 0x00, 0x0f, 0x1f, 0x0f, 0x1f, 0x07, 0x1f, 0x07, 0x07, 0x1f, 0x07, 0x07, 0x40, 0x40, 0x40,
+ 0x2f, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x07, 0x07, 0x3e, 0x27, 0x2f, 0x07, 0x27, 0x1f, 0x0f,
+ 0x40, 0x18, 0x10, 0x00, 0x40, 0x57, 0x1f, 0x47, 0x0f, 0x07, 0x1f, 0x08, 0x0f, 0x0f, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x07, 0x48, 0x48, 0x60, 0x40, 0x27, 0x07, 0x07, 0x27, 0x40, 0x48, 0x40,
+ 0x40, 0x40, 0x0f, 0x48, 0x68, 0x60, 0x40, 0x68, 0x68, 0x68, 0x68, 0x68, 0x07, 0x07, 0x0f,
+ 0x50, 0x40, 0x60, 0x07, 0x68, 0x27, 0x48, 0x17, 0x40, 0x50, 0x1f, 0x40, 0x40, 0x40, 0x48,
+ 0x48, 0x58, 0x60, 0x60, 0x60, 0x68, 0x68, 0x58, 0x68, 0x60, 0x60, 0x60, 0x68, 0x68, 0x68,
+ 0x60, 0x50, 0x48, 0x50, 0x58, 0x60, 0x60, 0x60, 0x68, 0x68, 0x58, 0x68, 0x60, 0x60, 0x60,
+ 0x68, 0x68, 0x68, 0x60, 0x50, 0x48, 0x50, 0x07, 0x50, 0x58, 0x40, 0x48, 0x40, 0x48, 0x07,
+ 0x48, 0x48, 0x48, 0x68, 0x07, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x1f, 0x17, 0x50, 0x0f,
+ 0x07, 0x40, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x40, 0x07, 0x48, 0x48, 0x48, 0x07, 0x48,
+ 0x07, 0x17, 0x17, 0x17, 0x50, 0x17, 0x17, 0x50, 0x40, 0x40, 0x40, 0x2f, 0x2f, 0x17, 0x40,
+ 0x0f, 0x17, 0x1f, 0x1f, 0x1f, 0x27, 0x0f, 0x07, 0x07, 0x0f, 0x07, 0x07, 0x3e, 0x1f, 0x17,
+ 0x40, 0x17, 0x07, 0x1f, 0x48, 0x17, 0x48, 0x40, 0x48, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07,
+ 0x47, 0x47, 0x5f, 0x40, 0x27, 0x07, 0x07, 0x27, 0x40, 0x47, 0x40, 0x40, 0x40, 0x0f, 0x47,
+ 0x66, 0x5f, 0x00, 0x66, 0x66, 0x66, 0x65, 0x65, 0x07, 0x07, 0x0f, 0x4f, 0x00, 0x5e, 0x07,
+ 0x67, 0x27, 0x47, 0x17, 0x40, 0x4f, 0x1f, 0x40, 0x40, 0x40, 0x47, 0x47, 0x57, 0x5f, 0x5e,
+ 0x5f, 0x66, 0x66, 0x57, 0x67, 0x5f, 0x5e, 0x5f, 0x67, 0x67, 0x66, 0x5e, 0x4f, 0x47, 0x4f,
+ 0x57, 0x5f, 0x5e, 0x5f, 0x66, 0x66, 0x57, 0x67, 0x5f, 0x5e, 0x5f, 0x67, 0x67, 0x66, 0x5e,
+ 0x4f, 0x47, 0x4f, 0x08, 0x4f, 0x56, 0x40, 0x48, 0x40, 0x47, 0x07, 0x47, 0x47, 0x47, 0x66,
+ 0x07, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x17,
+ 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07, 0x47, 0x47, 0x47, 0x08, 0x47, 0x08, 0x17, 0x17, 0x17,
+ 0x4f, 0x17, 0x17, 0x4f, 0x40, 0x40, 0x40, 0x2f, 0x2f, 0x17, 0x40, 0x0f, 0x17, 0x1f, 0x1f,
+ 0x20, 0x27, 0x10, 0x07, 0x08, 0x10, 0x08, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x17, 0x08, 0x1f,
+ 0x47, 0x17, 0x46, 0x00, 0x47, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x46, 0x47, 0x5e, 0x40,
+ 0x26, 0x06, 0x06, 0x27, 0x40, 0x47, 0x40, 0x40, 0x40, 0x0f, 0x47, 0x64, 0x5e, 0x01, 0x65,
+ 0x64, 0x64, 0x63, 0x63, 0x07, 0x07, 0x0f, 0x4e, 0x00, 0x5d, 0x07, 0x66, 0x27, 0x46, 0x17,
+ 0x40, 0x4f, 0x1e, 0x40, 0x40, 0x40, 0x47, 0x47, 0x56, 0x5e, 0x5d, 0x5e, 0x65, 0x64, 0x56,
+ 0x66, 0x5e, 0x5c, 0x5e, 0x66, 0x66, 0x65, 0x5d, 0x4e, 0x46, 0x4e, 0x56, 0x5e, 0x5d, 0x5e,
+ 0x65, 0x64, 0x56, 0x66, 0x5e, 0x5c, 0x5e, 0x66, 0x66, 0x65, 0x5d, 0x4e, 0x46, 0x4e, 0x09,
+ 0x4f, 0x54, 0x40, 0x48, 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x64, 0x07, 0x1f, 0x16, 0x4f,
+ 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40,
+ 0x40, 0x07, 0x46, 0x46, 0x46, 0x09, 0x46, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17, 0x16, 0x4f,
+ 0x40, 0x40, 0x40, 0x2e, 0x2e, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27, 0x10, 0x07,
+ 0x09, 0x10, 0x08, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x17, 0x08, 0x1e, 0x46, 0x17, 0x45, 0x01,
+ 0x46, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x45, 0x47, 0x5e, 0x40, 0x25, 0x06, 0x05, 0x27,
+ 0x40, 0x47, 0x40, 0x40, 0x40, 0x0f, 0x47, 0x63, 0x5d, 0x01, 0x64, 0x63, 0x62, 0x60, 0x60,
+ 0x07, 0x07, 0x0f, 0x4e, 0x00, 0x5c, 0x07, 0x65, 0x27, 0x45, 0x17, 0x40, 0x4f, 0x1d, 0x40,
+ 0x40, 0x40, 0x47, 0x47, 0x56, 0x5d, 0x5c, 0x5d, 0x64, 0x63, 0x56, 0x65, 0x5d, 0x5b, 0x5d,
+ 0x65, 0x65, 0x64, 0x5c, 0x4d, 0x46, 0x4d, 0x56, 0x5d, 0x5c, 0x5d, 0x64, 0x63, 0x56, 0x65,
+ 0x5d, 0x5b, 0x5d, 0x65, 0x65, 0x64, 0x5c, 0x4d, 0x46, 0x4d, 0x09, 0x4f, 0x52, 0x40, 0x48,
+ 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x62, 0x07, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f,
+ 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07, 0x46, 0x46,
+ 0x45, 0x09, 0x45, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17, 0x16, 0x4f, 0x40, 0x40, 0x40, 0x2d,
+ 0x2d, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27, 0x10, 0x07, 0x09, 0x10, 0x08, 0x07,
+ 0x3d, 0x1f, 0x17, 0x40, 0x17, 0x08, 0x1e, 0x45, 0x17, 0x44, 0x01, 0x45, 0x17, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x05, 0x44, 0x46, 0x5d, 0x40, 0x24, 0x05, 0x04, 0x27, 0x40, 0x46, 0x40, 0x40,
+ 0x40, 0x0f, 0x46, 0x61, 0x5c, 0x02, 0x63, 0x61, 0x60, 0x5e, 0x5e, 0x07, 0x07, 0x0e, 0x4d,
+ 0x01, 0x5b, 0x07, 0x64, 0x27, 0x44, 0x16, 0x40, 0x4e, 0x1c, 0x40, 0x40, 0x40, 0x46, 0x46,
+ 0x55, 0x5c, 0x5b, 0x5c, 0x63, 0x61, 0x55, 0x64, 0x5c, 0x59, 0x5c, 0x64, 0x64, 0x63, 0x5b,
+ 0x4c, 0x45, 0x4c, 0x55, 0x5c, 0x5b, 0x5c, 0x63, 0x61, 0x55, 0x64, 0x5c, 0x59, 0x5c, 0x64,
+ 0x64, 0x63, 0x5b, 0x4c, 0x45, 0x4c, 0x0a, 0x4e, 0x50, 0x40, 0x48, 0x40, 0x46, 0x07, 0x46,
+ 0x45, 0x45, 0x60, 0x07, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x15, 0x4e, 0x11, 0x07,
+ 0x40, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x41, 0x07, 0x45, 0x45, 0x44, 0x0a, 0x44, 0x0a,
+ 0x16, 0x17, 0x15, 0x4e, 0x17, 0x15, 0x4e, 0x40, 0x40, 0x40, 0x2c, 0x2c, 0x16, 0x40, 0x0f,
+ 0x16, 0x1d, 0x1d, 0x21, 0x27, 0x11, 0x07, 0x0a, 0x11, 0x09, 0x06, 0x3c, 0x1e, 0x16, 0x40,
+ 0x16, 0x09, 0x1d, 0x44, 0x16, 0x43, 0x02, 0x44, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x43,
+ 0x46, 0x5c, 0x40, 0x23, 0x04, 0x03, 0x27, 0x40, 0x46, 0x40, 0x40, 0x40, 0x0f, 0x46, 0x60,
+ 0x5b, 0x03, 0x61, 0x60, 0x5e, 0x5b, 0x5b, 0x07, 0x07, 0x0e, 0x4c, 0x01, 0x59, 0x07, 0x63,
+ 0x27, 0x43, 0x16, 0x40, 0x4e, 0x1b, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5b, 0x59, 0x5b,
+ 0x61, 0x60, 0x54, 0x63, 0x5b, 0x58, 0x5b, 0x63, 0x63, 0x61, 0x59, 0x4b, 0x44, 0x4b, 0x54,
+ 0x5b, 0x59, 0x5b, 0x61, 0x60, 0x54, 0x63, 0x5b, 0x58, 0x5b, 0x63, 0x63, 0x61, 0x59, 0x4b,
+ 0x44, 0x4b, 0x0b, 0x4e, 0x4e, 0x40, 0x48, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44, 0x5e, 0x07,
+ 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e,
+ 0x11, 0x07, 0x40, 0x41, 0x07, 0x44, 0x44, 0x43, 0x0b, 0x43, 0x0b, 0x16, 0x17, 0x14, 0x4e,
+ 0x17, 0x14, 0x4e, 0x40, 0x40, 0x40, 0x2b, 0x2b, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x1c, 0x21,
+ 0x27, 0x11, 0x07, 0x0b, 0x11, 0x09, 0x06, 0x3b, 0x1e, 0x16, 0x40, 0x16, 0x09, 0x1c, 0x43,
+ 0x16, 0x41, 0x03, 0x43, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x04, 0x42, 0x46, 0x5c, 0x40, 0x22,
+ 0x04, 0x02, 0x27, 0x40, 0x46, 0x40, 0x40, 0x40, 0x0f, 0x46, 0x5e, 0x5a, 0x03, 0x60, 0x5e,
+ 0x5c, 0x59, 0x59, 0x07, 0x07, 0x0e, 0x4c, 0x01, 0x58, 0x07, 0x62, 0x27, 0x42, 0x16, 0x40,
+ 0x4e, 0x1a, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5a, 0x58, 0x5a, 0x60, 0x5e, 0x54, 0x62,
+ 0x5a, 0x56, 0x5a, 0x62, 0x62, 0x60, 0x58, 0x4a, 0x44, 0x4a, 0x54, 0x5a, 0x58, 0x5a, 0x60,
+ 0x5e, 0x54, 0x62, 0x5a, 0x56, 0x5a, 0x62, 0x62, 0x60, 0x58, 0x4a, 0x44, 0x4a, 0x0b, 0x4e,
+ 0x4c, 0x40, 0x48, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44, 0x5c, 0x07, 0x1e, 0x14, 0x4e, 0x11,
+ 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x41,
+ 0x07, 0x44, 0x44, 0x42, 0x0b, 0x42, 0x0b, 0x16, 0x17, 0x14, 0x4e, 0x17, 0x14, 0x4e, 0x40,
+ 0x40, 0x40, 0x2a, 0x2a, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x1c, 0x21, 0x27, 0x11, 0x07, 0x0b,
+ 0x11, 0x09, 0x06, 0x3a, 0x1e, 0x16, 0x40, 0x16, 0x09, 0x1c, 0x42, 0x16, 0x40, 0x03, 0x42,
+ 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x41, 0x45, 0x5b, 0x40, 0x21, 0x03, 0x01, 0x27, 0x40,
+ 0x45, 0x40, 0x40, 0x40, 0x0f, 0x45, 0x5d, 0x59, 0x04, 0x5f, 0x5d, 0x5a, 0x56, 0x56, 0x07,
+ 0x07, 0x0d, 0x4b, 0x02, 0x57, 0x07, 0x61, 0x27, 0x41, 0x15, 0x40, 0x4d, 0x19, 0x40, 0x40,
+ 0x40, 0x45, 0x45, 0x53, 0x59, 0x57, 0x59, 0x5f, 0x5d, 0x53, 0x61, 0x59, 0x55, 0x59, 0x61,
+ 0x61, 0x5f, 0x57, 0x49, 0x43, 0x49, 0x53, 0x59, 0x57, 0x59, 0x5f, 0x5d, 0x53, 0x61, 0x59,
+ 0x55, 0x59, 0x61, 0x61, 0x5f, 0x57, 0x49, 0x43, 0x49, 0x0c, 0x4d, 0x4a, 0x40, 0x48, 0x40,
+ 0x45, 0x07, 0x45, 0x43, 0x43, 0x5a, 0x07, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x13,
+ 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x43, 0x43, 0x41,
+ 0x0c, 0x41, 0x0c, 0x15, 0x17, 0x13, 0x4d, 0x17, 0x13, 0x4d, 0x40, 0x40, 0x40, 0x29, 0x29,
+ 0x15, 0x40, 0x0f, 0x15, 0x1b, 0x1b, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x0a, 0x05, 0x39,
+ 0x1d, 0x15, 0x40, 0x15, 0x0a, 0x1b, 0x41, 0x15, 0x00, 0x04, 0x41, 0x15, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x02, 0x40, 0x45, 0x5b, 0x40, 0x20, 0x02, 0x00, 0x27, 0x40, 0x45, 0x40, 0x40, 0x40,
+ 0x0f, 0x45, 0x5b, 0x58, 0x04, 0x5e, 0x5b, 0x59, 0x54, 0x54, 0x07, 0x07, 0x0d, 0x4b, 0x02,
+ 0x56, 0x07, 0x60, 0x27, 0x40, 0x15, 0x40, 0x4d, 0x18, 0x40, 0x40, 0x40, 0x45, 0x45, 0x53,
+ 0x58, 0x56, 0x58, 0x5e, 0x5b, 0x53, 0x60, 0x58, 0x53, 0x58, 0x60, 0x60, 0x5e, 0x56, 0x48,
+ 0x43, 0x48, 0x53, 0x58, 0x56, 0x58, 0x5e, 0x5b, 0x53, 0x60, 0x58, 0x53, 0x58, 0x60, 0x60,
+ 0x5e, 0x56, 0x48, 0x43, 0x48, 0x0c, 0x4d, 0x49, 0x40, 0x48, 0x40, 0x45, 0x07, 0x45, 0x43,
+ 0x43, 0x59, 0x07, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40,
+ 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x43, 0x43, 0x40, 0x0c, 0x40, 0x0c, 0x15,
+ 0x17, 0x12, 0x4d, 0x17, 0x12, 0x4d, 0x40, 0x40, 0x40, 0x28, 0x28, 0x15, 0x40, 0x0f, 0x15,
+ 0x1a, 0x1a, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x0a, 0x05, 0x38, 0x1d, 0x15, 0x40, 0x15,
+ 0x0a, 0x1a, 0x40, 0x15, 0x01, 0x04, 0x40, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x45,
+ 0x5a, 0x40, 0x1f, 0x02, 0x40, 0x27, 0x40, 0x45, 0x40, 0x40, 0x40, 0x0f, 0x45, 0x59, 0x57,
+ 0x05, 0x5c, 0x59, 0x57, 0x51, 0x51, 0x07, 0x07, 0x0d, 0x4a, 0x02, 0x54, 0x07, 0x5f, 0x27,
+ 0x00, 0x15, 0x40, 0x4d, 0x17, 0x40, 0x40, 0x40, 0x45, 0x45, 0x52, 0x57, 0x54, 0x57, 0x5c,
+ 0x59, 0x52, 0x5f, 0x57, 0x51, 0x57, 0x5f, 0x5f, 0x5c, 0x54, 0x47, 0x42, 0x47, 0x52, 0x57,
+ 0x54, 0x57, 0x5c, 0x59, 0x52, 0x5f, 0x57, 0x51, 0x57, 0x5f, 0x5f, 0x5c, 0x54, 0x47, 0x42,
+ 0x47, 0x0d, 0x4d, 0x47, 0x40, 0x48, 0x40, 0x45, 0x07, 0x45, 0x42, 0x42, 0x57, 0x07, 0x1d,
+ 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12,
+ 0x07, 0x40, 0x42, 0x07, 0x42, 0x42, 0x00, 0x0d, 0x00, 0x0d, 0x15, 0x17, 0x12, 0x4d, 0x17,
+ 0x12, 0x4d, 0x40, 0x40, 0x40, 0x27, 0x27, 0x15, 0x40, 0x0f, 0x15, 0x1a, 0x1a, 0x22, 0x27,
+ 0x12, 0x07, 0x0d, 0x12, 0x0a, 0x05, 0x37, 0x1d, 0x15, 0x40, 0x15, 0x0a, 0x1a, 0x00, 0x15,
+ 0x03, 0x05, 0x00, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x01, 0x44, 0x59, 0x40, 0x1e, 0x01,
+ 0x41, 0x27, 0x40, 0x44, 0x40, 0x40, 0x40, 0x0f, 0x44, 0x58, 0x56, 0x06, 0x5b, 0x58, 0x55,
+ 0x4f, 0x4f, 0x07, 0x07, 0x0c, 0x49, 0x03, 0x53, 0x07, 0x5e, 0x27, 0x01, 0x14, 0x40, 0x4c,
+ 0x16, 0x40, 0x40, 0x40, 0x44, 0x44, 0x51, 0x56, 0x53, 0x56, 0x5b, 0x58, 0x51, 0x5e, 0x56,
+ 0x50, 0x56, 0x5e, 0x5e, 0x5b, 0x53, 0x46, 0x41, 0x46, 0x51, 0x56, 0x53, 0x56, 0x5b, 0x58,
+ 0x51, 0x5e, 0x56, 0x50, 0x56, 0x5e, 0x5e, 0x5b, 0x53, 0x46, 0x41, 0x46, 0x0e, 0x4c, 0x45,
+ 0x40, 0x48, 0x40, 0x44, 0x07, 0x44, 0x41, 0x41, 0x55, 0x07, 0x1c, 0x11, 0x4c, 0x13, 0x07,
+ 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07,
+ 0x41, 0x41, 0x01, 0x0e, 0x01, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c, 0x40, 0x40,
+ 0x40, 0x26, 0x26, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07, 0x0e, 0x13,
+ 0x0b, 0x04, 0x36, 0x1c, 0x14, 0x40, 0x14, 0x0b, 0x19, 0x01, 0x14, 0x04, 0x06, 0x01, 0x14,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x01, 0x02, 0x44, 0x59, 0x40, 0x1d, 0x01, 0x42, 0x27, 0x40, 0x44,
+ 0x40, 0x40, 0x40, 0x0f, 0x44, 0x56, 0x55, 0x06, 0x5a, 0x56, 0x53, 0x4c, 0x4c, 0x07, 0x07,
+ 0x0c, 0x49, 0x03, 0x52, 0x07, 0x5d, 0x27, 0x02, 0x14, 0x40, 0x4c, 0x15, 0x40, 0x40, 0x40,
+ 0x44, 0x44, 0x51, 0x55, 0x52, 0x55, 0x5a, 0x56, 0x51, 0x5d, 0x55, 0x4e, 0x55, 0x5d, 0x5d,
+ 0x5a, 0x52, 0x45, 0x41, 0x45, 0x51, 0x55, 0x52, 0x55, 0x5a, 0x56, 0x51, 0x5d, 0x55, 0x4e,
+ 0x55, 0x5d, 0x5d, 0x5a, 0x52, 0x45, 0x41, 0x45, 0x0e, 0x4c, 0x43, 0x40, 0x48, 0x40, 0x44,
+ 0x07, 0x44, 0x41, 0x41, 0x53, 0x07, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c,
+ 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x41, 0x41, 0x02, 0x0e,
+ 0x02, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c, 0x40, 0x40, 0x40, 0x25, 0x25, 0x14,
+ 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07, 0x0e, 0x13, 0x0b, 0x04, 0x35, 0x1c,
+ 0x14, 0x40, 0x14, 0x0b, 0x19, 0x02, 0x14, 0x05, 0x06, 0x02, 0x14, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x00, 0x03, 0x44, 0x58, 0x40, 0x1c, 0x00, 0x43, 0x27, 0x40, 0x44, 0x40, 0x40, 0x40, 0x0f,
+ 0x44, 0x55, 0x54, 0x07, 0x59, 0x55, 0x51, 0x4a, 0x4a, 0x07, 0x07, 0x0c, 0x48, 0x03, 0x51,
+ 0x07, 0x5c, 0x27, 0x03, 0x14, 0x40, 0x4c, 0x14, 0x40, 0x40, 0x40, 0x44, 0x44, 0x50, 0x54,
+ 0x51, 0x54, 0x59, 0x55, 0x50, 0x5c, 0x54, 0x4d, 0x54, 0x5c, 0x5c, 0x59, 0x51, 0x44, 0x40,
+ 0x44, 0x50, 0x54, 0x51, 0x54, 0x59, 0x55, 0x50, 0x5c, 0x54, 0x4d, 0x54, 0x5c, 0x5c, 0x59,
+ 0x51, 0x44, 0x40, 0x44, 0x0f, 0x4c, 0x41, 0x40, 0x48, 0x40, 0x44, 0x07, 0x44, 0x40, 0x40,
+ 0x51, 0x07, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x1c,
+ 0x10, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x40, 0x40, 0x03, 0x0f, 0x03, 0x0f, 0x14, 0x17,
+ 0x10, 0x4c, 0x17, 0x10, 0x4c, 0x40, 0x40, 0x40, 0x24, 0x24, 0x14, 0x40, 0x0f, 0x14, 0x18,
+ 0x18, 0x23, 0x27, 0x13, 0x07, 0x0f, 0x13, 0x0b, 0x04, 0x34, 0x1c, 0x14, 0x40, 0x14, 0x0b,
+ 0x18, 0x03, 0x14, 0x06, 0x07, 0x03, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x40, 0x04, 0x43, 0x57,
+ 0x40, 0x1b, 0x40, 0x44, 0x27, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0f, 0x43, 0x53, 0x53, 0x08,
+ 0x57, 0x53, 0x4f, 0x47, 0x47, 0x07, 0x07, 0x0b, 0x47, 0x04, 0x4f, 0x07, 0x5b, 0x27, 0x04,
+ 0x13, 0x40, 0x4b, 0x13, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x53, 0x4f, 0x53, 0x57, 0x53,
+ 0x4f, 0x5b, 0x53, 0x4b, 0x53, 0x5b, 0x5b, 0x57, 0x4f, 0x43, 0x00, 0x43, 0x4f, 0x53, 0x4f,
+ 0x53, 0x57, 0x53, 0x4f, 0x5b, 0x53, 0x4b, 0x53, 0x5b, 0x5b, 0x57, 0x4f, 0x43, 0x00, 0x43,
+ 0x10, 0x4b, 0x00, 0x40, 0x48, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4f, 0x07, 0x1b, 0x0f,
+ 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07,
+ 0x40, 0x44, 0x07, 0x00, 0x00, 0x04, 0x10, 0x04, 0x10, 0x13, 0x17, 0x0f, 0x4b, 0x17, 0x0f,
+ 0x4b, 0x40, 0x40, 0x40, 0x23, 0x23, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24, 0x27, 0x14,
+ 0x07, 0x10, 0x14, 0x0c, 0x03, 0x33, 0x1b, 0x13, 0x40, 0x13, 0x0c, 0x17, 0x04, 0x13, 0x08,
+ 0x08, 0x04, 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x40, 0x05, 0x43, 0x57, 0x40, 0x1a, 0x40, 0x45,
+ 0x27, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0f, 0x43, 0x52, 0x52, 0x08, 0x56, 0x52, 0x4d, 0x45,
+ 0x45, 0x07, 0x07, 0x0b, 0x47, 0x04, 0x4e, 0x07, 0x5a, 0x27, 0x05, 0x13, 0x40, 0x4b, 0x12,
+ 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x52, 0x4e, 0x52, 0x56, 0x52, 0x4f, 0x5a, 0x52, 0x4a,
+ 0x52, 0x5a, 0x5a, 0x56, 0x4e, 0x42, 0x00, 0x42, 0x4f, 0x52, 0x4e, 0x52, 0x56, 0x52, 0x4f,
+ 0x5a, 0x52, 0x4a, 0x52, 0x5a, 0x5a, 0x56, 0x4e, 0x42, 0x00, 0x42, 0x10, 0x4b, 0x02, 0x40,
+ 0x48, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4d, 0x07, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40,
+ 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x44, 0x07, 0x00,
+ 0x00, 0x05, 0x10, 0x05, 0x10, 0x13, 0x17, 0x0f, 0x4b, 0x17, 0x0f, 0x4b, 0x40, 0x40, 0x40,
+ 0x22, 0x22, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24, 0x27, 0x14, 0x07, 0x10, 0x14, 0x0c,
+ 0x03, 0x32, 0x1b, 0x13, 0x40, 0x13, 0x0c, 0x17, 0x05, 0x13, 0x09, 0x08, 0x05, 0x13, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x41, 0x06, 0x43, 0x56, 0x40, 0x19, 0x41, 0x46, 0x27, 0x40, 0x43, 0x40,
+ 0x40, 0x40, 0x0f, 0x43, 0x50, 0x51, 0x09, 0x55, 0x50, 0x4b, 0x42, 0x42, 0x07, 0x07, 0x0b,
+ 0x46, 0x04, 0x4d, 0x07, 0x59, 0x27, 0x06, 0x13, 0x40, 0x4b, 0x11, 0x40, 0x40, 0x40, 0x43,
+ 0x43, 0x4e, 0x51, 0x4d, 0x51, 0x55, 0x50, 0x4e, 0x59, 0x51, 0x48, 0x51, 0x59, 0x59, 0x55,
+ 0x4d, 0x41, 0x01, 0x41, 0x4e, 0x51, 0x4d, 0x51, 0x55, 0x50, 0x4e, 0x59, 0x51, 0x48, 0x51,
+ 0x59, 0x59, 0x55, 0x4d, 0x41, 0x01, 0x41, 0x11, 0x4b, 0x04, 0x40, 0x48, 0x40, 0x43, 0x07,
+ 0x43, 0x01, 0x01, 0x4b, 0x07, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0e, 0x4b, 0x14,
+ 0x07, 0x40, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x44, 0x07, 0x01, 0x01, 0x06, 0x11, 0x06,
+ 0x11, 0x13, 0x17, 0x0e, 0x4b, 0x17, 0x0e, 0x4b, 0x40, 0x40, 0x40, 0x21, 0x21, 0x13, 0x40,
+ 0x0f, 0x13, 0x16, 0x16, 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x0c, 0x03, 0x31, 0x1b, 0x13,
+ 0x40, 0x13, 0x0c, 0x16, 0x06, 0x13, 0x0a, 0x09, 0x06, 0x13, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42,
+ 0x06, 0x43, 0x56, 0x40, 0x18, 0x42, 0x47, 0x27, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0f, 0x43,
+ 0x4f, 0x51, 0x09, 0x54, 0x4f, 0x4a, 0x40, 0x40, 0x07, 0x07, 0x0a, 0x46, 0x04, 0x4c, 0x07,
+ 0x59, 0x27, 0x06, 0x12, 0x40, 0x4b, 0x10, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4e, 0x51, 0x4c,
+ 0x51, 0x54, 0x4f, 0x4e, 0x59, 0x51, 0x47, 0x51, 0x59, 0x59, 0x54, 0x4c, 0x41, 0x01, 0x41,
+ 0x4e, 0x51, 0x4c, 0x51, 0x54, 0x4f, 0x4e, 0x59, 0x51, 0x47, 0x51, 0x59, 0x59, 0x54, 0x4c,
+ 0x41, 0x01, 0x41, 0x11, 0x4b, 0x05, 0x40, 0x48, 0x40, 0x43, 0x07, 0x43, 0x01, 0x01, 0x4a,
+ 0x07, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x1a, 0x0d,
+ 0x4b, 0x14, 0x07, 0x40, 0x45, 0x07, 0x01, 0x01, 0x06, 0x11, 0x06, 0x11, 0x12, 0x17, 0x0d,
+ 0x4b, 0x17, 0x0d, 0x4b, 0x40, 0x40, 0x40, 0x20, 0x20, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x15,
+ 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x0c, 0x02, 0x30, 0x1a, 0x12, 0x40, 0x12, 0x0c, 0x15,
+ 0x06, 0x12, 0x0b, 0x09, 0x06, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x07, 0x42, 0x55, 0x40,
+ 0x18, 0x42, 0x47, 0x27, 0x40, 0x42, 0x40, 0x40, 0x40, 0x0f, 0x42, 0x4d, 0x50, 0x0a, 0x52,
+ 0x4d, 0x48, 0x02, 0x02, 0x07, 0x07, 0x0a, 0x45, 0x05, 0x4a, 0x07, 0x58, 0x27, 0x07, 0x12,
+ 0x40, 0x4a, 0x10, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4d, 0x50, 0x4a, 0x50, 0x52, 0x4d, 0x4d,
+ 0x58, 0x50, 0x45, 0x50, 0x58, 0x58, 0x52, 0x4a, 0x40, 0x02, 0x40, 0x4d, 0x50, 0x4a, 0x50,
+ 0x52, 0x4d, 0x4d, 0x58, 0x50, 0x45, 0x50, 0x58, 0x58, 0x52, 0x4a, 0x40, 0x02, 0x40, 0x12,
+ 0x4a, 0x07, 0x40, 0x48, 0x40, 0x42, 0x07, 0x42, 0x02, 0x02, 0x48, 0x07, 0x1a, 0x0d, 0x4a,
+ 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15, 0x07, 0x40,
+ 0x45, 0x07, 0x02, 0x02, 0x07, 0x12, 0x07, 0x12, 0x12, 0x17, 0x0d, 0x4a, 0x17, 0x0d, 0x4a,
+ 0x40, 0x40, 0x40, 0x20, 0x20, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x15, 0x25, 0x27, 0x15, 0x07,
+ 0x12, 0x15, 0x0d, 0x02, 0x30, 0x1a, 0x12, 0x40, 0x12, 0x0d, 0x15, 0x07, 0x12, 0x0d, 0x0a,
+ 0x07, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x43, 0x08, 0x42, 0x54, 0x40, 0x17, 0x43, 0x48, 0x27,
+ 0x40, 0x42, 0x40, 0x40, 0x40, 0x0f, 0x42, 0x4b, 0x4f, 0x0b, 0x51, 0x4b, 0x46, 0x04, 0x04,
+ 0x07, 0x07, 0x0a, 0x44, 0x05, 0x49, 0x07, 0x57, 0x27, 0x08, 0x12, 0x40, 0x4a, 0x0f, 0x40,
+ 0x40, 0x40, 0x42, 0x42, 0x4c, 0x4f, 0x49, 0x4f, 0x51, 0x4b, 0x4c, 0x57, 0x4f, 0x43, 0x4f,
+ 0x57, 0x57, 0x51, 0x49, 0x00, 0x03, 0x00, 0x4c, 0x4f, 0x49, 0x4f, 0x51, 0x4b, 0x4c, 0x57,
+ 0x4f, 0x43, 0x4f, 0x57, 0x57, 0x51, 0x49, 0x00, 0x03, 0x00, 0x13, 0x4a, 0x09, 0x40, 0x48,
+ 0x40, 0x42, 0x07, 0x42, 0x03, 0x03, 0x46, 0x07, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a,
+ 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07, 0x03, 0x03,
+ 0x08, 0x13, 0x08, 0x13, 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40, 0x40, 0x1f,
+ 0x1f, 0x12, 0x40, 0x0f, 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15, 0x0d, 0x02,
+ 0x2f, 0x1a, 0x12, 0x40, 0x12, 0x0d, 0x14, 0x08, 0x12, 0x0e, 0x0b, 0x08, 0x12, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x43, 0x09, 0x42, 0x54, 0x40, 0x16, 0x43, 0x49, 0x27, 0x40, 0x42, 0x40, 0x40,
+ 0x40, 0x0f, 0x42, 0x4a, 0x4e, 0x0b, 0x50, 0x4a, 0x44, 0x07, 0x07, 0x07, 0x07, 0x0a, 0x44,
+ 0x05, 0x48, 0x07, 0x56, 0x27, 0x09, 0x12, 0x40, 0x4a, 0x0e, 0x40, 0x40, 0x40, 0x42, 0x42,
+ 0x4c, 0x4e, 0x48, 0x4e, 0x50, 0x4a, 0x4c, 0x56, 0x4e, 0x42, 0x4e, 0x56, 0x56, 0x50, 0x48,
+ 0x01, 0x03, 0x01, 0x4c, 0x4e, 0x48, 0x4e, 0x50, 0x4a, 0x4c, 0x56, 0x4e, 0x42, 0x4e, 0x56,
+ 0x56, 0x50, 0x48, 0x01, 0x03, 0x01, 0x13, 0x4a, 0x0b, 0x40, 0x48, 0x40, 0x42, 0x07, 0x42,
+ 0x03, 0x03, 0x44, 0x07, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07,
+ 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07, 0x03, 0x03, 0x09, 0x13, 0x09, 0x13,
+ 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40, 0x40, 0x1e, 0x1e, 0x12, 0x40, 0x0f,
+ 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15, 0x0d, 0x02, 0x2e, 0x1a, 0x12, 0x40,
+ 0x12, 0x0d, 0x14, 0x09, 0x12, 0x0f, 0x0b, 0x09, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x44, 0x0a,
+ 0x41, 0x53, 0x40, 0x15, 0x44, 0x4a, 0x27, 0x40, 0x41, 0x40, 0x40, 0x40, 0x0f, 0x41, 0x48,
+ 0x4d, 0x0c, 0x4f, 0x48, 0x42, 0x09, 0x09, 0x07, 0x07, 0x09, 0x43, 0x06, 0x47, 0x07, 0x55,
+ 0x27, 0x0a, 0x11, 0x40, 0x49, 0x0d, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4b, 0x4d, 0x47, 0x4d,
+ 0x4f, 0x48, 0x4b, 0x55, 0x4d, 0x40, 0x4d, 0x55, 0x55, 0x4f, 0x47, 0x02, 0x04, 0x02, 0x4b,
+ 0x4d, 0x47, 0x4d, 0x4f, 0x48, 0x4b, 0x55, 0x4d, 0x40, 0x4d, 0x55, 0x55, 0x4f, 0x47, 0x02,
+ 0x04, 0x02, 0x14, 0x49, 0x0d, 0x40, 0x48, 0x40, 0x41, 0x07, 0x41, 0x04, 0x04, 0x42, 0x07,
+ 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0b, 0x49,
+ 0x16, 0x07, 0x40, 0x46, 0x07, 0x04, 0x04, 0x0a, 0x14, 0x0a, 0x14, 0x11, 0x17, 0x0b, 0x49,
+ 0x17, 0x0b, 0x49, 0x40, 0x40, 0x40, 0x1d, 0x1d, 0x11, 0x40, 0x0f, 0x11, 0x13, 0x13, 0x26,
+ 0x27, 0x16, 0x07, 0x14, 0x16, 0x0e, 0x01, 0x2d, 0x19, 0x11, 0x40, 0x11, 0x0e, 0x13, 0x0a,
+ 0x11, 0x10, 0x0c, 0x0a, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x45, 0x0b, 0x41, 0x52, 0x40, 0x14,
+ 0x45, 0x4b, 0x27, 0x40, 0x41, 0x40, 0x40, 0x40, 0x0f, 0x41, 0x47, 0x4c, 0x0d, 0x4d, 0x47,
+ 0x40, 0x0c, 0x0c, 0x07, 0x07, 0x09, 0x42, 0x06, 0x45, 0x07, 0x54, 0x27, 0x0b, 0x11, 0x40,
+ 0x49, 0x0c, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4a, 0x4c, 0x45, 0x4c, 0x4d, 0x47, 0x4a, 0x54,
+ 0x4c, 0x00, 0x4c, 0x54, 0x54, 0x4d, 0x45, 0x03, 0x05, 0x03, 0x4a, 0x4c, 0x45, 0x4c, 0x4d,
+ 0x47, 0x4a, 0x54, 0x4c, 0x00, 0x4c, 0x54, 0x54, 0x4d, 0x45, 0x03, 0x05, 0x03, 0x15, 0x49,
+ 0x0f, 0x40, 0x48, 0x40, 0x41, 0x07, 0x41, 0x05, 0x05, 0x40, 0x07, 0x19, 0x0a, 0x49, 0x16,
+ 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x46,
+ 0x07, 0x05, 0x05, 0x0b, 0x15, 0x0b, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a, 0x49, 0x40,
+ 0x40, 0x40, 0x1c, 0x1c, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16, 0x07, 0x15,
+ 0x16, 0x0e, 0x01, 0x2c, 0x19, 0x11, 0x40, 0x11, 0x0e, 0x12, 0x0b, 0x11, 0x12, 0x0d, 0x0b,
+ 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x45, 0x0c, 0x41, 0x52, 0x40, 0x13, 0x45, 0x4c, 0x27, 0x40,
+ 0x41, 0x40, 0x40, 0x40, 0x0f, 0x41, 0x45, 0x4b, 0x0d, 0x4c, 0x45, 0x01, 0x0e, 0x0e, 0x07,
+ 0x07, 0x09, 0x42, 0x06, 0x44, 0x07, 0x53, 0x27, 0x0c, 0x11, 0x40, 0x49, 0x0b, 0x40, 0x40,
+ 0x40, 0x41, 0x41, 0x4a, 0x4b, 0x44, 0x4b, 0x4c, 0x45, 0x4a, 0x53, 0x4b, 0x02, 0x4b, 0x53,
+ 0x53, 0x4c, 0x44, 0x04, 0x05, 0x04, 0x4a, 0x4b, 0x44, 0x4b, 0x4c, 0x45, 0x4a, 0x53, 0x4b,
+ 0x02, 0x4b, 0x53, 0x53, 0x4c, 0x44, 0x04, 0x05, 0x04, 0x15, 0x49, 0x11, 0x40, 0x48, 0x40,
+ 0x41, 0x07, 0x41, 0x05, 0x05, 0x01, 0x07, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a,
+ 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x46, 0x07, 0x05, 0x05, 0x0c,
+ 0x15, 0x0c, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a, 0x49, 0x40, 0x40, 0x40, 0x1b, 0x1b,
+ 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16, 0x07, 0x15, 0x16, 0x0e, 0x01, 0x2b,
+ 0x19, 0x11, 0x40, 0x11, 0x0e, 0x12, 0x0c, 0x11, 0x13, 0x0d, 0x0c, 0x11, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x46, 0x0d, 0x40, 0x51, 0x40, 0x12, 0x46, 0x4d, 0x27, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x0f, 0x40, 0x44, 0x4a, 0x0e, 0x4b, 0x44, 0x03, 0x11, 0x11, 0x07, 0x07, 0x08, 0x41, 0x07,
+ 0x43, 0x07, 0x52, 0x27, 0x0d, 0x10, 0x40, 0x48, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49,
+ 0x4a, 0x43, 0x4a, 0x4b, 0x44, 0x49, 0x52, 0x4a, 0x03, 0x4a, 0x52, 0x52, 0x4b, 0x43, 0x05,
+ 0x06, 0x05, 0x49, 0x4a, 0x43, 0x4a, 0x4b, 0x44, 0x49, 0x52, 0x4a, 0x03, 0x4a, 0x52, 0x52,
+ 0x4b, 0x43, 0x05, 0x06, 0x05, 0x16, 0x48, 0x13, 0x40, 0x48, 0x40, 0x40, 0x07, 0x40, 0x06,
+ 0x06, 0x03, 0x07, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40,
+ 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x47, 0x07, 0x06, 0x06, 0x0d, 0x16, 0x0d, 0x16, 0x10,
+ 0x17, 0x09, 0x48, 0x17, 0x09, 0x48, 0x40, 0x40, 0x40, 0x1a, 0x1a, 0x10, 0x40, 0x0f, 0x10,
+ 0x11, 0x11, 0x27, 0x27, 0x17, 0x07, 0x16, 0x17, 0x0f, 0x00, 0x2a, 0x18, 0x10, 0x40, 0x10,
+ 0x0f, 0x11, 0x0d, 0x10, 0x14, 0x0e, 0x0d, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x0e, 0x40,
+ 0x51, 0x40, 0x11, 0x47, 0x4e, 0x27, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x42, 0x49,
+ 0x0e, 0x4a, 0x42, 0x04, 0x13, 0x13, 0x07, 0x07, 0x08, 0x41, 0x07, 0x42, 0x07, 0x51, 0x27,
+ 0x0e, 0x10, 0x40, 0x48, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x42, 0x49, 0x4a,
+ 0x42, 0x49, 0x51, 0x49, 0x05, 0x49, 0x51, 0x51, 0x4a, 0x42, 0x06, 0x06, 0x06, 0x49, 0x49,
+ 0x42, 0x49, 0x4a, 0x42, 0x49, 0x51, 0x49, 0x05, 0x49, 0x51, 0x51, 0x4a, 0x42, 0x06, 0x06,
+ 0x06, 0x16, 0x48, 0x14, 0x40, 0x48, 0x40, 0x40, 0x07, 0x40, 0x06, 0x06, 0x04, 0x07, 0x18,
+ 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17,
+ 0x07, 0x40, 0x47, 0x07, 0x06, 0x06, 0x0e, 0x16, 0x0e, 0x16, 0x10, 0x17, 0x08, 0x48, 0x17,
+ 0x08, 0x48, 0x40, 0x40, 0x40, 0x19, 0x19, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10, 0x27, 0x27,
+ 0x17, 0x07, 0x16, 0x17, 0x0f, 0x00, 0x29, 0x18, 0x10, 0x40, 0x10, 0x0f, 0x10, 0x0e, 0x10,
+ 0x15, 0x0e, 0x0e, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x0f, 0x40, 0x50, 0x40, 0x10, 0x47,
+ 0x4f, 0x27, 0x40, 0x40, 0x40, 0x40, 0x40, 0x0f, 0x40, 0x40, 0x48, 0x0f, 0x48, 0x40, 0x06,
+ 0x16, 0x16, 0x07, 0x07, 0x08, 0x40, 0x07, 0x40, 0x07, 0x50, 0x27, 0x0f, 0x10, 0x40, 0x48,
+ 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x40, 0x48, 0x48, 0x40, 0x48, 0x50, 0x48,
+ 0x07, 0x48, 0x50, 0x50, 0x48, 0x40, 0x07, 0x07, 0x07, 0x48, 0x48, 0x40, 0x48, 0x48, 0x40,
+ 0x48, 0x50, 0x48, 0x07, 0x48, 0x50, 0x50, 0x48, 0x40, 0x07, 0x07, 0x07, 0x17, 0x48, 0x16,
+ 0x40, 0x48, 0x40, 0x40, 0x07, 0x40, 0x07, 0x07, 0x06, 0x07, 0x18, 0x08, 0x48, 0x17, 0x07,
+ 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x47, 0x07,
+ 0x07, 0x07, 0x0f, 0x17, 0x0f, 0x17, 0x10, 0x17, 0x08, 0x48, 0x17, 0x08, 0x48, 0x40, 0x40,
+ 0x40, 0x18, 0x18, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10, 0x27, 0x27, 0x17, 0x07, 0x17, 0x17,
+ 0x0f, 0x00, 0x28, 0x18, 0x10, 0x40, 0x10, 0x0f, 0x10, 0x0f, 0x10, 0x17, 0x0f, 0x0f, 0x10,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x48, 0x10, 0x00, 0x4f, 0x40, 0x0f, 0x48, 0x50, 0x27, 0x40, 0x00,
+ 0x40, 0x40, 0x40, 0x0f, 0x00, 0x00, 0x47, 0x10, 0x47, 0x00, 0x08, 0x18, 0x18, 0x07, 0x07,
+ 0x07, 0x00, 0x08, 0x00, 0x07, 0x4f, 0x27, 0x10, 0x0f, 0x40, 0x47, 0x07, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f, 0x47, 0x08, 0x47, 0x4f, 0x4f,
+ 0x47, 0x00, 0x08, 0x08, 0x08, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f, 0x47, 0x08,
+ 0x47, 0x4f, 0x4f, 0x47, 0x00, 0x08, 0x08, 0x08, 0x18, 0x47, 0x18, 0x40, 0x48, 0x40, 0x00,
+ 0x07, 0x00, 0x08, 0x08, 0x08, 0x07, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47,
+ 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x10, 0x18,
+ 0x10, 0x18, 0x0f, 0x17, 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x17, 0x17, 0x0f,
+ 0x40, 0x0f, 0x0f, 0x0f, 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40, 0x27, 0x17,
+ 0x0f, 0x40, 0x0f, 0x10, 0x0f, 0x10, 0x0f, 0x18, 0x10, 0x10, 0x0f, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x48, 0x11, 0x00, 0x4f, 0x40, 0x0e, 0x48, 0x51, 0x27, 0x40, 0x00, 0x40, 0x40, 0x40, 0x0f,
+ 0x00, 0x02, 0x46, 0x10, 0x46, 0x02, 0x0a, 0x1b, 0x1b, 0x07, 0x07, 0x07, 0x00, 0x08, 0x01,
+ 0x07, 0x4e, 0x27, 0x11, 0x0f, 0x40, 0x47, 0x06, 0x40, 0x40, 0x40, 0x00, 0x00, 0x47, 0x46,
+ 0x01, 0x46, 0x46, 0x02, 0x47, 0x4e, 0x46, 0x0a, 0x46, 0x4e, 0x4e, 0x46, 0x01, 0x09, 0x08,
+ 0x09, 0x47, 0x46, 0x01, 0x46, 0x46, 0x02, 0x47, 0x4e, 0x46, 0x0a, 0x46, 0x4e, 0x4e, 0x46,
+ 0x01, 0x09, 0x08, 0x09, 0x18, 0x47, 0x1a, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00, 0x08, 0x08,
+ 0x0a, 0x07, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17,
+ 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x11, 0x18, 0x11, 0x18, 0x0f, 0x17,
+ 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x16, 0x16, 0x0f, 0x40, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40, 0x26, 0x17, 0x0f, 0x40, 0x0f, 0x10,
+ 0x0f, 0x11, 0x0f, 0x19, 0x10, 0x11, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x49, 0x12, 0x00, 0x4e,
+ 0x40, 0x0d, 0x49, 0x52, 0x27, 0x40, 0x00, 0x40, 0x40, 0x40, 0x0f, 0x00, 0x03, 0x45, 0x11,
+ 0x45, 0x03, 0x0c, 0x1d, 0x1d, 0x07, 0x07, 0x07, 0x01, 0x08, 0x02, 0x07, 0x4d, 0x27, 0x12,
+ 0x0f, 0x40, 0x47, 0x05, 0x40, 0x40, 0x40, 0x00, 0x00, 0x46, 0x45, 0x02, 0x45, 0x45, 0x03,
+ 0x46, 0x4d, 0x45, 0x0b, 0x45, 0x4d, 0x4d, 0x45, 0x02, 0x0a, 0x09, 0x0a, 0x46, 0x45, 0x02,
+ 0x45, 0x45, 0x03, 0x46, 0x4d, 0x45, 0x0b, 0x45, 0x4d, 0x4d, 0x45, 0x02, 0x0a, 0x09, 0x0a,
+ 0x19, 0x47, 0x1c, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00, 0x09, 0x09, 0x0c, 0x07, 0x17, 0x06,
+ 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47, 0x18, 0x07,
+ 0x40, 0x48, 0x07, 0x09, 0x09, 0x12, 0x19, 0x12, 0x19, 0x0f, 0x17, 0x06, 0x47, 0x17, 0x06,
+ 0x47, 0x40, 0x40, 0x40, 0x15, 0x15, 0x0f, 0x40, 0x0f, 0x0f, 0x0e, 0x0e, 0x28, 0x27, 0x18,
+ 0x07, 0x19, 0x18, 0x10, 0x40, 0x25, 0x17, 0x0f, 0x40, 0x0f, 0x10, 0x0e, 0x12, 0x0f, 0x1a,
+ 0x11, 0x12, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4a, 0x13, 0x01, 0x4d, 0x40, 0x0c, 0x4a, 0x53,
+ 0x27, 0x40, 0x01, 0x40, 0x40, 0x40, 0x0f, 0x01, 0x05, 0x44, 0x12, 0x43, 0x05, 0x0e, 0x20,
+ 0x20, 0x07, 0x07, 0x06, 0x02, 0x09, 0x04, 0x07, 0x4c, 0x27, 0x13, 0x0e, 0x40, 0x46, 0x04,
+ 0x40, 0x40, 0x40, 0x01, 0x01, 0x45, 0x44, 0x04, 0x44, 0x43, 0x05, 0x45, 0x4c, 0x44, 0x0d,
+ 0x44, 0x4c, 0x4c, 0x43, 0x04, 0x0b, 0x0a, 0x0b, 0x45, 0x44, 0x04, 0x44, 0x43, 0x05, 0x45,
+ 0x4c, 0x44, 0x0d, 0x44, 0x4c, 0x4c, 0x43, 0x04, 0x0b, 0x0a, 0x0b, 0x1a, 0x46, 0x1e, 0x40,
+ 0x48, 0x40, 0x01, 0x07, 0x01, 0x0a, 0x0a, 0x0e, 0x07, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40,
+ 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x0a,
+ 0x0a, 0x13, 0x1a, 0x13, 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40, 0x40, 0x40,
+ 0x14, 0x14, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a, 0x19, 0x11,
+ 0x41, 0x24, 0x16, 0x0e, 0x40, 0x0e, 0x11, 0x0d, 0x13, 0x0e, 0x1c, 0x12, 0x13, 0x0e, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x4a, 0x14, 0x01, 0x4d, 0x40, 0x0b, 0x4a, 0x54, 0x27, 0x40, 0x01, 0x40,
+ 0x40, 0x40, 0x0f, 0x01, 0x06, 0x43, 0x12, 0x42, 0x06, 0x10, 0x22, 0x22, 0x07, 0x07, 0x06,
+ 0x02, 0x09, 0x05, 0x07, 0x4b, 0x27, 0x14, 0x0e, 0x40, 0x46, 0x03, 0x40, 0x40, 0x40, 0x01,
+ 0x01, 0x45, 0x43, 0x05, 0x43, 0x42, 0x06, 0x45, 0x4b, 0x43, 0x0e, 0x43, 0x4b, 0x4b, 0x42,
+ 0x05, 0x0c, 0x0a, 0x0c, 0x45, 0x43, 0x05, 0x43, 0x42, 0x06, 0x45, 0x4b, 0x43, 0x0e, 0x43,
+ 0x4b, 0x4b, 0x42, 0x05, 0x0c, 0x0a, 0x0c, 0x1a, 0x46, 0x20, 0x40, 0x48, 0x40, 0x01, 0x07,
+ 0x01, 0x0a, 0x0a, 0x10, 0x07, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19,
+ 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x0a, 0x0a, 0x14, 0x1a, 0x14,
+ 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40, 0x40, 0x40, 0x13, 0x13, 0x0e, 0x40,
+ 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a, 0x19, 0x11, 0x41, 0x23, 0x16, 0x0e,
+ 0x40, 0x0e, 0x11, 0x0d, 0x14, 0x0e, 0x1d, 0x12, 0x14, 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4b,
+ 0x15, 0x01, 0x4c, 0x40, 0x0a, 0x4b, 0x55, 0x27, 0x40, 0x01, 0x40, 0x40, 0x40, 0x0f, 0x01,
+ 0x08, 0x42, 0x13, 0x41, 0x08, 0x12, 0x25, 0x25, 0x07, 0x07, 0x06, 0x03, 0x09, 0x06, 0x07,
+ 0x4a, 0x27, 0x15, 0x0e, 0x40, 0x46, 0x02, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44, 0x42, 0x06,
+ 0x42, 0x41, 0x08, 0x44, 0x4a, 0x42, 0x10, 0x42, 0x4a, 0x4a, 0x41, 0x06, 0x0d, 0x0b, 0x0d,
+ 0x44, 0x42, 0x06, 0x42, 0x41, 0x08, 0x44, 0x4a, 0x42, 0x10, 0x42, 0x4a, 0x4a, 0x41, 0x06,
+ 0x0d, 0x0b, 0x0d, 0x1b, 0x46, 0x22, 0x40, 0x48, 0x40, 0x01, 0x07, 0x01, 0x0b, 0x0b, 0x12,
+ 0x07, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x16, 0x04,
+ 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x0b, 0x0b, 0x15, 0x1b, 0x15, 0x1b, 0x0e, 0x17, 0x04,
+ 0x46, 0x17, 0x04, 0x46, 0x40, 0x40, 0x40, 0x12, 0x12, 0x0e, 0x40, 0x0f, 0x0e, 0x0c, 0x0c,
+ 0x29, 0x27, 0x19, 0x07, 0x1b, 0x19, 0x11, 0x41, 0x22, 0x16, 0x0e, 0x40, 0x0e, 0x11, 0x0c,
+ 0x15, 0x0e, 0x1e, 0x13, 0x15, 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4c, 0x15, 0x01, 0x4c, 0x40,
+ 0x09, 0x4c, 0x56, 0x27, 0x40, 0x01, 0x40, 0x40, 0x40, 0x0f, 0x01, 0x09, 0x42, 0x13, 0x40,
+ 0x09, 0x13, 0x27, 0x27, 0x07, 0x07, 0x05, 0x03, 0x09, 0x07, 0x07, 0x4a, 0x27, 0x15, 0x0d,
+ 0x40, 0x46, 0x01, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44, 0x42, 0x07, 0x42, 0x40, 0x09, 0x44,
+ 0x4a, 0x42, 0x11, 0x42, 0x4a, 0x4a, 0x40, 0x07, 0x0d, 0x0b, 0x0d, 0x44, 0x42, 0x07, 0x42,
+ 0x40, 0x09, 0x44, 0x4a, 0x42, 0x11, 0x42, 0x4a, 0x4a, 0x40, 0x07, 0x0d, 0x0b, 0x0d, 0x1b,
+ 0x46, 0x23, 0x40, 0x48, 0x40, 0x01, 0x07, 0x01, 0x0b, 0x0b, 0x13, 0x07, 0x15, 0x03, 0x46,
+ 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19, 0x07, 0x40,
+ 0x4a, 0x07, 0x0b, 0x0b, 0x15, 0x1b, 0x15, 0x1b, 0x0d, 0x17, 0x03, 0x46, 0x17, 0x03, 0x46,
+ 0x40, 0x40, 0x40, 0x11, 0x11, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x29, 0x27, 0x19, 0x07,
+ 0x1b, 0x19, 0x11, 0x42, 0x21, 0x15, 0x0d, 0x40, 0x0d, 0x11, 0x0b, 0x15, 0x0d, 0x1f, 0x13,
+ 0x15, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4c, 0x16, 0x02, 0x4b, 0x40, 0x09, 0x4c, 0x56, 0x27,
+ 0x40, 0x02, 0x40, 0x40, 0x40, 0x0f, 0x02, 0x0b, 0x41, 0x14, 0x01, 0x0b, 0x15, 0x2a, 0x2a,
+ 0x07, 0x07, 0x05, 0x04, 0x0a, 0x09, 0x07, 0x49, 0x27, 0x16, 0x0d, 0x40, 0x45, 0x01, 0x40,
+ 0x40, 0x40, 0x02, 0x02, 0x43, 0x41, 0x09, 0x41, 0x01, 0x0b, 0x43, 0x49, 0x41, 0x13, 0x41,
+ 0x49, 0x49, 0x01, 0x09, 0x0e, 0x0c, 0x0e, 0x43, 0x41, 0x09, 0x41, 0x01, 0x0b, 0x43, 0x49,
+ 0x41, 0x13, 0x41, 0x49, 0x49, 0x01, 0x09, 0x0e, 0x0c, 0x0e, 0x1c, 0x45, 0x25, 0x40, 0x48,
+ 0x40, 0x02, 0x07, 0x02, 0x0c, 0x0c, 0x15, 0x07, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x15,
+ 0x03, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0c, 0x0c,
+ 0x16, 0x1c, 0x16, 0x1c, 0x0d, 0x17, 0x03, 0x45, 0x17, 0x03, 0x45, 0x40, 0x40, 0x40, 0x11,
+ 0x11, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x2a, 0x27, 0x1a, 0x07, 0x1c, 0x1a, 0x12, 0x42,
+ 0x21, 0x15, 0x0d, 0x40, 0x0d, 0x12, 0x0b, 0x16, 0x0d, 0x21, 0x14, 0x16, 0x0d, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x4d, 0x17, 0x02, 0x4a, 0x40, 0x08, 0x4d, 0x57, 0x27, 0x40, 0x02, 0x40, 0x40,
+ 0x40, 0x0f, 0x02, 0x0d, 0x40, 0x15, 0x02, 0x0d, 0x17, 0x2c, 0x2c, 0x07, 0x07, 0x05, 0x05,
+ 0x0a, 0x0a, 0x07, 0x48, 0x27, 0x17, 0x0d, 0x40, 0x45, 0x00, 0x40, 0x40, 0x40, 0x02, 0x02,
+ 0x42, 0x40, 0x0a, 0x40, 0x02, 0x0d, 0x42, 0x48, 0x40, 0x15, 0x40, 0x48, 0x48, 0x02, 0x0a,
+ 0x0f, 0x0d, 0x0f, 0x42, 0x40, 0x0a, 0x40, 0x02, 0x0d, 0x42, 0x48, 0x40, 0x15, 0x40, 0x48,
+ 0x48, 0x02, 0x0a, 0x0f, 0x0d, 0x0f, 0x1d, 0x45, 0x27, 0x40, 0x48, 0x40, 0x02, 0x07, 0x02,
+ 0x0d, 0x0d, 0x17, 0x07, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07,
+ 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0d, 0x0d, 0x17, 0x1d, 0x17, 0x1d,
+ 0x0d, 0x17, 0x02, 0x45, 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x10, 0x10, 0x0d, 0x40, 0x0f,
+ 0x0d, 0x0a, 0x0a, 0x2a, 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x12, 0x42, 0x20, 0x15, 0x0d, 0x40,
+ 0x0d, 0x12, 0x0a, 0x17, 0x0d, 0x22, 0x15, 0x17, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4d, 0x18,
+ 0x02, 0x4a, 0x40, 0x07, 0x4d, 0x58, 0x27, 0x40, 0x02, 0x40, 0x40, 0x40, 0x0f, 0x02, 0x0e,
+ 0x00, 0x15, 0x03, 0x0e, 0x19, 0x2f, 0x2f, 0x07, 0x07, 0x05, 0x05, 0x0a, 0x0b, 0x07, 0x47,
+ 0x27, 0x18, 0x0d, 0x40, 0x45, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x42, 0x00, 0x0b, 0x00,
+ 0x03, 0x0e, 0x42, 0x47, 0x00, 0x16, 0x00, 0x47, 0x47, 0x03, 0x0b, 0x10, 0x0d, 0x10, 0x42,
+ 0x00, 0x0b, 0x00, 0x03, 0x0e, 0x42, 0x47, 0x00, 0x16, 0x00, 0x47, 0x47, 0x03, 0x0b, 0x10,
+ 0x0d, 0x10, 0x1d, 0x45, 0x29, 0x40, 0x48, 0x40, 0x02, 0x07, 0x02, 0x0d, 0x0d, 0x19, 0x07,
+ 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45,
+ 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0d, 0x0d, 0x18, 0x1d, 0x18, 0x1d, 0x0d, 0x17, 0x02, 0x45,
+ 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x0f, 0x0f, 0x0d, 0x40, 0x0f, 0x0d, 0x0a, 0x0a, 0x2a,
+ 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x12, 0x42, 0x1f, 0x15, 0x0d, 0x40, 0x0d, 0x12, 0x0a, 0x18,
+ 0x0d, 0x23, 0x15, 0x18, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x4e, 0x19, 0x03, 0x49, 0x40, 0x06,
+ 0x4e, 0x59, 0x27, 0x40, 0x03, 0x40, 0x40, 0x40, 0x0f, 0x03, 0x10, 0x01, 0x16, 0x04, 0x10,
+ 0x1b, 0x31, 0x31, 0x07, 0x07, 0x04, 0x06, 0x0b, 0x0c, 0x07, 0x46, 0x27, 0x19, 0x0c, 0x40,
+ 0x44, 0x41, 0x40, 0x40, 0x40, 0x03, 0x03, 0x41, 0x01, 0x0c, 0x01, 0x04, 0x10, 0x41, 0x46,
+ 0x01, 0x18, 0x01, 0x46, 0x46, 0x04, 0x0c, 0x11, 0x0e, 0x11, 0x41, 0x01, 0x0c, 0x01, 0x04,
+ 0x10, 0x41, 0x46, 0x01, 0x18, 0x01, 0x46, 0x46, 0x04, 0x0c, 0x11, 0x0e, 0x11, 0x1e, 0x44,
+ 0x2b, 0x40, 0x48, 0x40, 0x03, 0x07, 0x03, 0x0e, 0x0e, 0x1b, 0x07, 0x14, 0x01, 0x44, 0x1b,
+ 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07, 0x40, 0x4b,
+ 0x07, 0x0e, 0x0e, 0x19, 0x1e, 0x19, 0x1e, 0x0c, 0x17, 0x01, 0x44, 0x17, 0x01, 0x44, 0x40,
+ 0x40, 0x40, 0x0e, 0x0e, 0x0c, 0x40, 0x0f, 0x0c, 0x09, 0x09, 0x2b, 0x27, 0x1b, 0x07, 0x1e,
+ 0x1b, 0x13, 0x43, 0x1e, 0x14, 0x0c, 0x40, 0x0c, 0x13, 0x09, 0x19, 0x0c, 0x24, 0x16, 0x19,
+ 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x4f, 0x1a, 0x03, 0x48, 0x40, 0x05, 0x4f, 0x5a, 0x27, 0x40,
+ 0x03, 0x40, 0x40, 0x40, 0x0f, 0x03, 0x11, 0x02, 0x17, 0x06, 0x11, 0x1d, 0x34, 0x34, 0x07,
+ 0x07, 0x04, 0x07, 0x0b, 0x0e, 0x07, 0x45, 0x27, 0x1a, 0x0c, 0x40, 0x44, 0x42, 0x40, 0x40,
+ 0x40, 0x03, 0x03, 0x40, 0x02, 0x0e, 0x02, 0x06, 0x11, 0x40, 0x45, 0x02, 0x19, 0x02, 0x45,
+ 0x45, 0x06, 0x0e, 0x12, 0x0f, 0x12, 0x40, 0x02, 0x0e, 0x02, 0x06, 0x11, 0x40, 0x45, 0x02,
+ 0x19, 0x02, 0x45, 0x45, 0x06, 0x0e, 0x12, 0x0f, 0x12, 0x1f, 0x44, 0x2d, 0x40, 0x48, 0x40,
+ 0x03, 0x07, 0x03, 0x0f, 0x0f, 0x1d, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00,
+ 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0f, 0x0f, 0x1a,
+ 0x1f, 0x1a, 0x1f, 0x0c, 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40, 0x0d, 0x0d,
+ 0x0c, 0x40, 0x0f, 0x0c, 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x13, 0x43, 0x1d,
+ 0x14, 0x0c, 0x40, 0x0c, 0x13, 0x08, 0x1a, 0x0c, 0x26, 0x17, 0x1a, 0x0c, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x4f, 0x1b, 0x03, 0x48, 0x40, 0x04, 0x4f, 0x5b, 0x27, 0x40, 0x03, 0x40, 0x40, 0x40,
+ 0x0f, 0x03, 0x13, 0x03, 0x17, 0x07, 0x13, 0x1f, 0x36, 0x36, 0x07, 0x07, 0x04, 0x07, 0x0b,
+ 0x0f, 0x07, 0x44, 0x27, 0x1b, 0x0c, 0x40, 0x44, 0x43, 0x40, 0x40, 0x40, 0x03, 0x03, 0x40,
+ 0x03, 0x0f, 0x03, 0x07, 0x13, 0x40, 0x44, 0x03, 0x1b, 0x03, 0x44, 0x44, 0x07, 0x0f, 0x13,
+ 0x0f, 0x13, 0x40, 0x03, 0x0f, 0x03, 0x07, 0x13, 0x40, 0x44, 0x03, 0x1b, 0x03, 0x44, 0x44,
+ 0x07, 0x0f, 0x13, 0x0f, 0x13, 0x1f, 0x44, 0x2f, 0x40, 0x48, 0x40, 0x03, 0x07, 0x03, 0x0f,
+ 0x0f, 0x1f, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40,
+ 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0f, 0x0f, 0x1b, 0x1f, 0x1b, 0x1f, 0x0c,
+ 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40, 0x0c, 0x0c, 0x0c, 0x40, 0x0f, 0x0c,
+ 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x13, 0x43, 0x1c, 0x14, 0x0c, 0x40, 0x0c,
+ 0x13, 0x08, 0x1b, 0x0c, 0x27, 0x17, 0x1b, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x50, 0x1c, 0x04,
+ 0x47, 0x40, 0x03, 0x50, 0x5c, 0x27, 0x40, 0x04, 0x40, 0x40, 0x40, 0x0f, 0x04, 0x14, 0x04,
+ 0x18, 0x08, 0x14, 0x21, 0x39, 0x39, 0x07, 0x07, 0x03, 0x08, 0x0c, 0x10, 0x07, 0x43, 0x27,
+ 0x1c, 0x0b, 0x40, 0x43, 0x44, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x04, 0x10, 0x04, 0x08,
+ 0x14, 0x00, 0x43, 0x04, 0x1c, 0x04, 0x43, 0x43, 0x08, 0x10, 0x14, 0x10, 0x14, 0x00, 0x04,
+ 0x10, 0x04, 0x08, 0x14, 0x00, 0x43, 0x04, 0x1c, 0x04, 0x43, 0x43, 0x08, 0x10, 0x14, 0x10,
+ 0x14, 0x20, 0x43, 0x31, 0x40, 0x48, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x21, 0x07, 0x13,
+ 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40, 0x43, 0x1c,
+ 0x07, 0x40, 0x4c, 0x07, 0x10, 0x10, 0x1c, 0x20, 0x1c, 0x20, 0x0b, 0x17, 0x40, 0x43, 0x17,
+ 0x40, 0x43, 0x40, 0x40, 0x40, 0x0b, 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x07, 0x07, 0x2c, 0x27,
+ 0x1c, 0x07, 0x20, 0x1c, 0x14, 0x44, 0x1b, 0x13, 0x0b, 0x40, 0x0b, 0x14, 0x07, 0x1c, 0x0b,
+ 0x28, 0x18, 0x1c, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x51, 0x1d, 0x04, 0x47, 0x40, 0x02, 0x51,
+ 0x5d, 0x27, 0x40, 0x04, 0x40, 0x40, 0x40, 0x0f, 0x04, 0x16, 0x05, 0x18, 0x09, 0x16, 0x22,
+ 0x3b, 0x3b, 0x07, 0x07, 0x03, 0x08, 0x0c, 0x11, 0x07, 0x42, 0x27, 0x1d, 0x0b, 0x40, 0x43,
+ 0x45, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x05, 0x11, 0x05, 0x09, 0x16, 0x00, 0x42, 0x05,
+ 0x1e, 0x05, 0x42, 0x42, 0x09, 0x11, 0x15, 0x10, 0x15, 0x00, 0x05, 0x11, 0x05, 0x09, 0x16,
+ 0x00, 0x42, 0x05, 0x1e, 0x05, 0x42, 0x42, 0x09, 0x11, 0x15, 0x10, 0x15, 0x20, 0x43, 0x32,
+ 0x40, 0x48, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x22, 0x07, 0x13, 0x41, 0x43, 0x1c, 0x07,
+ 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07,
+ 0x10, 0x10, 0x1d, 0x20, 0x1d, 0x20, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43, 0x40, 0x40,
+ 0x40, 0x0a, 0x0a, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07, 0x20, 0x1c,
+ 0x14, 0x44, 0x1a, 0x13, 0x0b, 0x40, 0x0b, 0x14, 0x06, 0x1d, 0x0b, 0x29, 0x18, 0x1d, 0x0b,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x51, 0x1e, 0x04, 0x46, 0x40, 0x01, 0x51, 0x5e, 0x27, 0x40, 0x04,
+ 0x40, 0x40, 0x40, 0x0f, 0x04, 0x18, 0x06, 0x19, 0x0b, 0x18, 0x24, 0x3e, 0x3e, 0x07, 0x07,
+ 0x03, 0x09, 0x0c, 0x13, 0x07, 0x41, 0x27, 0x1e, 0x0b, 0x40, 0x43, 0x46, 0x40, 0x40, 0x40,
+ 0x04, 0x04, 0x01, 0x06, 0x13, 0x06, 0x0b, 0x18, 0x01, 0x41, 0x06, 0x20, 0x06, 0x41, 0x41,
+ 0x0b, 0x13, 0x16, 0x11, 0x16, 0x01, 0x06, 0x13, 0x06, 0x0b, 0x18, 0x01, 0x41, 0x06, 0x20,
+ 0x06, 0x41, 0x41, 0x0b, 0x13, 0x16, 0x11, 0x16, 0x21, 0x43, 0x34, 0x40, 0x48, 0x40, 0x04,
+ 0x07, 0x04, 0x11, 0x11, 0x24, 0x07, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43,
+ 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07, 0x11, 0x11, 0x1e, 0x21,
+ 0x1e, 0x21, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43, 0x40, 0x40, 0x40, 0x09, 0x09, 0x0b,
+ 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07, 0x21, 0x1c, 0x14, 0x44, 0x19, 0x13,
+ 0x0b, 0x40, 0x0b, 0x14, 0x06, 0x1e, 0x0b, 0x2b, 0x19, 0x1e, 0x0b, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x52, 0x1f, 0x05, 0x45, 0x40, 0x00, 0x52, 0x5f, 0x27, 0x40, 0x05, 0x40, 0x40, 0x40, 0x0f,
+ 0x05, 0x19, 0x07, 0x1a, 0x0c, 0x19, 0x26, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0a, 0x0d, 0x14,
+ 0x07, 0x40, 0x27, 0x1f, 0x0a, 0x40, 0x42, 0x47, 0x40, 0x40, 0x40, 0x05, 0x05, 0x02, 0x07,
+ 0x14, 0x07, 0x0c, 0x19, 0x02, 0x40, 0x07, 0x21, 0x07, 0x40, 0x40, 0x0c, 0x14, 0x17, 0x12,
+ 0x17, 0x02, 0x07, 0x14, 0x07, 0x0c, 0x19, 0x02, 0x40, 0x07, 0x21, 0x07, 0x40, 0x40, 0x0c,
+ 0x14, 0x17, 0x12, 0x17, 0x22, 0x42, 0x36, 0x40, 0x48, 0x40, 0x05, 0x07, 0x05, 0x12, 0x12,
+ 0x26, 0x07, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12,
+ 0x42, 0x42, 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x12, 0x12, 0x1f, 0x22, 0x1f, 0x22, 0x0a, 0x17,
+ 0x42, 0x42, 0x17, 0x42, 0x42, 0x40, 0x40, 0x40, 0x08, 0x08, 0x0a, 0x40, 0x0f, 0x0a, 0x05,
+ 0x05, 0x2d, 0x27, 0x1d, 0x07, 0x22, 0x1d, 0x15, 0x45, 0x18, 0x12, 0x0a, 0x40, 0x0a, 0x15,
+ 0x05, 0x1f, 0x0a, 0x2c, 0x1a, 0x1f, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x52, 0x20, 0x05, 0x45,
+ 0x40, 0x40, 0x52, 0x60, 0x27, 0x40, 0x05, 0x40, 0x40, 0x40, 0x0f, 0x05, 0x1b, 0x08, 0x1a,
+ 0x0d, 0x1b, 0x28, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0a, 0x0d, 0x15, 0x07, 0x00, 0x27, 0x20,
+ 0x0a, 0x40, 0x42, 0x48, 0x40, 0x40, 0x40, 0x05, 0x05, 0x02, 0x08, 0x15, 0x08, 0x0d, 0x1b,
+ 0x02, 0x00, 0x08, 0x23, 0x08, 0x00, 0x00, 0x0d, 0x15, 0x18, 0x12, 0x18, 0x02, 0x08, 0x15,
+ 0x08, 0x0d, 0x1b, 0x02, 0x00, 0x08, 0x23, 0x08, 0x00, 0x00, 0x0d, 0x15, 0x18, 0x12, 0x18,
+ 0x22, 0x42, 0x38, 0x40, 0x48, 0x40, 0x05, 0x07, 0x05, 0x12, 0x12, 0x28, 0x07, 0x12, 0x42,
+ 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07,
+ 0x40, 0x4d, 0x07, 0x12, 0x12, 0x20, 0x22, 0x20, 0x22, 0x0a, 0x17, 0x42, 0x42, 0x17, 0x42,
+ 0x42, 0x40, 0x40, 0x40, 0x07, 0x07, 0x0a, 0x40, 0x0f, 0x0a, 0x05, 0x05, 0x2d, 0x27, 0x1d,
+ 0x07, 0x22, 0x1d, 0x15, 0x45, 0x17, 0x12, 0x0a, 0x40, 0x0a, 0x15, 0x05, 0x20, 0x0a, 0x2d,
+ 0x1a, 0x20, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x53, 0x21, 0x05, 0x44, 0x40, 0x41, 0x53, 0x61,
+ 0x27, 0x40, 0x05, 0x40, 0x40, 0x40, 0x0f, 0x05, 0x1c, 0x09, 0x1b, 0x0e, 0x1c, 0x2a, 0x3e,
+ 0x3e, 0x07, 0x07, 0x02, 0x0b, 0x0d, 0x16, 0x07, 0x01, 0x27, 0x21, 0x0a, 0x40, 0x42, 0x49,
+ 0x40, 0x40, 0x40, 0x05, 0x05, 0x03, 0x09, 0x16, 0x09, 0x0e, 0x1c, 0x03, 0x01, 0x09, 0x24,
+ 0x09, 0x01, 0x01, 0x0e, 0x16, 0x19, 0x13, 0x19, 0x03, 0x09, 0x16, 0x09, 0x0e, 0x1c, 0x03,
+ 0x01, 0x09, 0x24, 0x09, 0x01, 0x01, 0x0e, 0x16, 0x19, 0x13, 0x19, 0x23, 0x42, 0x3a, 0x40,
+ 0x48, 0x40, 0x05, 0x07, 0x05, 0x13, 0x13, 0x2a, 0x07, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40,
+ 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x13,
+ 0x13, 0x21, 0x23, 0x21, 0x23, 0x0a, 0x17, 0x43, 0x42, 0x17, 0x43, 0x42, 0x40, 0x40, 0x40,
+ 0x06, 0x06, 0x0a, 0x40, 0x0f, 0x0a, 0x04, 0x04, 0x2d, 0x27, 0x1d, 0x07, 0x23, 0x1d, 0x15,
+ 0x45, 0x16, 0x12, 0x0a, 0x40, 0x0a, 0x15, 0x04, 0x21, 0x0a, 0x2e, 0x1b, 0x21, 0x0a, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x54, 0x22, 0x06, 0x43, 0x40, 0x42, 0x54, 0x62, 0x27, 0x40, 0x06, 0x40,
+ 0x40, 0x40, 0x0f, 0x06, 0x1e, 0x0a, 0x1c, 0x10, 0x1e, 0x2c, 0x3e, 0x3e, 0x07, 0x07, 0x01,
+ 0x0c, 0x0e, 0x18, 0x07, 0x02, 0x27, 0x22, 0x09, 0x40, 0x41, 0x4a, 0x40, 0x40, 0x40, 0x06,
+ 0x06, 0x04, 0x0a, 0x18, 0x0a, 0x10, 0x1e, 0x04, 0x02, 0x0a, 0x26, 0x0a, 0x02, 0x02, 0x10,
+ 0x18, 0x1a, 0x14, 0x1a, 0x04, 0x0a, 0x18, 0x0a, 0x10, 0x1e, 0x04, 0x02, 0x0a, 0x26, 0x0a,
+ 0x02, 0x02, 0x10, 0x18, 0x1a, 0x14, 0x1a, 0x24, 0x41, 0x3c, 0x40, 0x48, 0x40, 0x06, 0x07,
+ 0x06, 0x14, 0x14, 0x2c, 0x07, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e,
+ 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x14, 0x14, 0x22, 0x24, 0x22,
+ 0x24, 0x09, 0x17, 0x44, 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x05, 0x05, 0x09, 0x40,
+ 0x0f, 0x09, 0x03, 0x03, 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x16, 0x46, 0x15, 0x11, 0x09,
+ 0x40, 0x09, 0x16, 0x03, 0x22, 0x09, 0x30, 0x1c, 0x22, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x54,
+ 0x23, 0x06, 0x43, 0x40, 0x43, 0x54, 0x63, 0x27, 0x40, 0x06, 0x40, 0x40, 0x40, 0x0f, 0x06,
+ 0x1f, 0x0b, 0x1c, 0x11, 0x1f, 0x2e, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x0c, 0x0e, 0x19, 0x07,
+ 0x03, 0x27, 0x23, 0x09, 0x40, 0x41, 0x4b, 0x40, 0x40, 0x40, 0x06, 0x06, 0x04, 0x0b, 0x19,
+ 0x0b, 0x11, 0x1f, 0x04, 0x03, 0x0b, 0x27, 0x0b, 0x03, 0x03, 0x11, 0x19, 0x1b, 0x14, 0x1b,
+ 0x04, 0x0b, 0x19, 0x0b, 0x11, 0x1f, 0x04, 0x03, 0x0b, 0x27, 0x0b, 0x03, 0x03, 0x11, 0x19,
+ 0x1b, 0x14, 0x1b, 0x24, 0x41, 0x3e, 0x40, 0x48, 0x40, 0x06, 0x07, 0x06, 0x14, 0x14, 0x2e,
+ 0x07, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44,
+ 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x14, 0x14, 0x23, 0x24, 0x23, 0x24, 0x09, 0x17, 0x44,
+ 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x04, 0x04, 0x09, 0x40, 0x0f, 0x09, 0x03, 0x03,
+ 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x16, 0x46, 0x14, 0x11, 0x09, 0x40, 0x09, 0x16, 0x03,
+ 0x23, 0x09, 0x31, 0x1c, 0x23, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x55, 0x24, 0x06, 0x42, 0x40,
+ 0x44, 0x55, 0x64, 0x27, 0x40, 0x06, 0x40, 0x40, 0x40, 0x0f, 0x06, 0x21, 0x0c, 0x1d, 0x12,
+ 0x21, 0x30, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x0d, 0x0e, 0x1a, 0x07, 0x04, 0x27, 0x24, 0x09,
+ 0x40, 0x41, 0x4c, 0x40, 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x1a, 0x0c, 0x12, 0x21, 0x05,
+ 0x04, 0x0c, 0x29, 0x0c, 0x04, 0x04, 0x12, 0x1a, 0x1c, 0x15, 0x1c, 0x05, 0x0c, 0x1a, 0x0c,
+ 0x12, 0x21, 0x05, 0x04, 0x0c, 0x29, 0x0c, 0x04, 0x04, 0x12, 0x1a, 0x1c, 0x15, 0x1c, 0x25,
+ 0x41, 0x3e, 0x40, 0x48, 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x30, 0x07, 0x11, 0x45, 0x41,
+ 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e, 0x07, 0x40,
+ 0x4e, 0x07, 0x15, 0x15, 0x24, 0x25, 0x24, 0x25, 0x09, 0x17, 0x45, 0x41, 0x17, 0x45, 0x41,
+ 0x40, 0x40, 0x40, 0x03, 0x03, 0x09, 0x40, 0x0f, 0x09, 0x02, 0x02, 0x2e, 0x27, 0x1e, 0x07,
+ 0x25, 0x1e, 0x16, 0x46, 0x13, 0x11, 0x09, 0x40, 0x09, 0x16, 0x02, 0x24, 0x09, 0x32, 0x1d,
+ 0x24, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x56, 0x24, 0x06, 0x42, 0x40, 0x45, 0x56, 0x65, 0x27,
+ 0x40, 0x06, 0x40, 0x40, 0x40, 0x0f, 0x06, 0x22, 0x0c, 0x1d, 0x13, 0x22, 0x31, 0x3e, 0x3e,
+ 0x07, 0x07, 0x00, 0x0d, 0x0e, 0x1b, 0x07, 0x04, 0x27, 0x24, 0x08, 0x40, 0x41, 0x4d, 0x40,
+ 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x1b, 0x0c, 0x13, 0x22, 0x05, 0x04, 0x0c, 0x2a, 0x0c,
+ 0x04, 0x04, 0x13, 0x1b, 0x1c, 0x15, 0x1c, 0x05, 0x0c, 0x1b, 0x0c, 0x13, 0x22, 0x05, 0x04,
+ 0x0c, 0x2a, 0x0c, 0x04, 0x04, 0x13, 0x1b, 0x1c, 0x15, 0x1c, 0x25, 0x41, 0x3e, 0x40, 0x48,
+ 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x31, 0x07, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x10,
+ 0x46, 0x41, 0x1e, 0x07, 0x40, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x4f, 0x07, 0x15, 0x15,
+ 0x24, 0x25, 0x24, 0x25, 0x08, 0x17, 0x46, 0x41, 0x17, 0x46, 0x41, 0x40, 0x40, 0x40, 0x02,
+ 0x02, 0x08, 0x40, 0x0f, 0x08, 0x01, 0x01, 0x2e, 0x27, 0x1e, 0x07, 0x25, 0x1e, 0x16, 0x47,
+ 0x12, 0x10, 0x08, 0x40, 0x08, 0x16, 0x01, 0x24, 0x08, 0x33, 0x1d, 0x24, 0x08, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x56, 0x25, 0x07, 0x41, 0x40, 0x45, 0x56, 0x65, 0x27, 0x40, 0x07, 0x40, 0x40,
+ 0x40, 0x0f, 0x07, 0x24, 0x0d, 0x1e, 0x15, 0x24, 0x33, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x0e,
+ 0x0f, 0x1d, 0x07, 0x05, 0x27, 0x25, 0x08, 0x40, 0x40, 0x4d, 0x40, 0x40, 0x40, 0x07, 0x07,
+ 0x06, 0x0d, 0x1d, 0x0d, 0x15, 0x24, 0x06, 0x05, 0x0d, 0x2c, 0x0d, 0x05, 0x05, 0x15, 0x1d,
+ 0x1d, 0x16, 0x1d, 0x06, 0x0d, 0x1d, 0x0d, 0x15, 0x24, 0x06, 0x05, 0x0d, 0x2c, 0x0d, 0x05,
+ 0x05, 0x15, 0x1d, 0x1d, 0x16, 0x1d, 0x26, 0x40, 0x3e, 0x40, 0x48, 0x40, 0x07, 0x07, 0x07,
+ 0x16, 0x16, 0x33, 0x07, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x46, 0x40, 0x1f, 0x07,
+ 0x40, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x16, 0x16, 0x25, 0x26, 0x25, 0x26,
+ 0x08, 0x17, 0x46, 0x40, 0x17, 0x46, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x08, 0x40, 0x0f,
+ 0x08, 0x01, 0x01, 0x2f, 0x27, 0x1f, 0x07, 0x26, 0x1f, 0x17, 0x47, 0x12, 0x10, 0x08, 0x40,
+ 0x08, 0x17, 0x01, 0x25, 0x08, 0x35, 0x1e, 0x25, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x57, 0x26,
+ 0x07, 0x40, 0x40, 0x46, 0x57, 0x66, 0x27, 0x40, 0x07, 0x40, 0x40, 0x40, 0x0f, 0x07, 0x26,
+ 0x0e, 0x1f, 0x16, 0x26, 0x35, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x0f, 0x0f, 0x1e, 0x07, 0x06,
+ 0x27, 0x26, 0x08, 0x40, 0x40, 0x4e, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0e, 0x1e, 0x0e,
+ 0x16, 0x26, 0x07, 0x06, 0x0e, 0x2e, 0x0e, 0x06, 0x06, 0x16, 0x1e, 0x1e, 0x17, 0x1e, 0x07,
+ 0x0e, 0x1e, 0x0e, 0x16, 0x26, 0x07, 0x06, 0x0e, 0x2e, 0x0e, 0x06, 0x06, 0x16, 0x1e, 0x1e,
+ 0x17, 0x1e, 0x27, 0x40, 0x3e, 0x40, 0x48, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17, 0x35, 0x07,
+ 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40,
+ 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x17, 0x17, 0x26, 0x27, 0x26, 0x27, 0x08, 0x17, 0x47, 0x40,
+ 0x17, 0x47, 0x40, 0x40, 0x40, 0x40, 0x01, 0x01, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x00, 0x2f,
+ 0x27, 0x1f, 0x07, 0x27, 0x1f, 0x17, 0x47, 0x11, 0x10, 0x08, 0x40, 0x08, 0x17, 0x00, 0x26,
+ 0x08, 0x36, 0x1f, 0x26, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x57, 0x27, 0x07, 0x40, 0x40, 0x47,
+ 0x57, 0x67, 0x27, 0x40, 0x07, 0x40, 0x40, 0x40, 0x0f, 0x07, 0x27, 0x0f, 0x1f, 0x17, 0x27,
+ 0x37, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x0f, 0x0f, 0x1f, 0x07, 0x07, 0x27, 0x27, 0x08, 0x40,
+ 0x40, 0x4f, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0f, 0x1f, 0x0f, 0x17, 0x27, 0x07, 0x07,
+ 0x0f, 0x2f, 0x0f, 0x07, 0x07, 0x17, 0x1f, 0x1f, 0x17, 0x1f, 0x07, 0x0f, 0x1f, 0x0f, 0x17,
+ 0x27, 0x07, 0x07, 0x0f, 0x2f, 0x0f, 0x07, 0x07, 0x17, 0x1f, 0x1f, 0x17, 0x1f, 0x27, 0x40,
+ 0x3e, 0x40, 0x48, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17, 0x37, 0x07, 0x10, 0x47, 0x40, 0x1f,
+ 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x4f,
+ 0x07, 0x17, 0x17, 0x27, 0x27, 0x27, 0x27, 0x08, 0x17, 0x47, 0x40, 0x17, 0x47, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x00, 0x2f, 0x27, 0x1f, 0x07, 0x27,
+ 0x1f, 0x17, 0x47, 0x10, 0x10, 0x08, 0x40, 0x08, 0x17, 0x00, 0x27, 0x08, 0x37, 0x1f, 0x27,
+ 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x48, 0x48, 0x60, 0x40, 0x27, 0x07, 0x07, 0x1f, 0x40,
+ 0x48, 0x40, 0x40, 0x17, 0x0f, 0x48, 0x68, 0x40, 0x07, 0x68, 0x68, 0x68, 0x68, 0x68, 0x07,
+ 0x07, 0x0f, 0x3e, 0x17, 0x40, 0x07, 0x68, 0x27, 0x50, 0x17, 0x40, 0x07, 0x1f, 0x40, 0x40,
+ 0x40, 0x48, 0x48, 0x58, 0x60, 0x50, 0x60, 0x68, 0x60, 0x58, 0x68, 0x68, 0x68, 0x58, 0x60,
+ 0x68, 0x68, 0x68, 0x50, 0x48, 0x58, 0x58, 0x60, 0x50, 0x60, 0x68, 0x60, 0x58, 0x68, 0x68,
+ 0x68, 0x58, 0x60, 0x68, 0x68, 0x68, 0x50, 0x48, 0x58, 0x07, 0x50, 0x58, 0x40, 0x40, 0x40,
+ 0x48, 0x07, 0x48, 0x48, 0x48, 0x68, 0x50, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x1f, 0x17,
+ 0x50, 0x0f, 0x07, 0x40, 0x1f, 0x17, 0x50, 0x0f, 0x07, 0x40, 0x40, 0x07, 0x40, 0x40, 0x40,
+ 0x07, 0x40, 0x07, 0x17, 0x17, 0x17, 0x50, 0x17, 0x17, 0x50, 0x40, 0x40, 0x40, 0x2f, 0x17,
+ 0x17, 0x40, 0x0f, 0x17, 0x1f, 0x1f, 0x1f, 0x27, 0x0f, 0x07, 0x07, 0x0f, 0x40, 0x07, 0x3e,
+ 0x1f, 0x17, 0x40, 0x0f, 0x17, 0x1f, 0x48, 0x17, 0x48, 0x48, 0x48, 0x17, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x3e, 0x47, 0x47, 0x5f, 0x40, 0x27, 0x07, 0x07, 0x20, 0x40, 0x47, 0x40, 0x40, 0x17,
+ 0x0f, 0x47, 0x66, 0x40, 0x08, 0x66, 0x66, 0x66, 0x65, 0x65, 0x07, 0x07, 0x0f, 0x3e, 0x17,
+ 0x00, 0x07, 0x67, 0x27, 0x4e, 0x17, 0x40, 0x07, 0x1f, 0x40, 0x40, 0x40, 0x47, 0x47, 0x57,
+ 0x5f, 0x4f, 0x5f, 0x66, 0x5e, 0x57, 0x67, 0x67, 0x66, 0x57, 0x5f, 0x67, 0x67, 0x66, 0x4f,
+ 0x47, 0x56, 0x57, 0x5f, 0x4f, 0x5f, 0x66, 0x5e, 0x57, 0x67, 0x67, 0x66, 0x57, 0x5f, 0x67,
+ 0x67, 0x66, 0x4f, 0x47, 0x56, 0x08, 0x4f, 0x56, 0x40, 0x40, 0x40, 0x47, 0x07, 0x47, 0x47,
+ 0x47, 0x66, 0x4f, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40,
+ 0x1f, 0x17, 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x08, 0x17,
+ 0x17, 0x17, 0x4f, 0x17, 0x17, 0x4f, 0x40, 0x40, 0x40, 0x2f, 0x17, 0x17, 0x40, 0x0f, 0x17,
+ 0x1f, 0x1f, 0x20, 0x27, 0x10, 0x07, 0x08, 0x10, 0x00, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x0f,
+ 0x17, 0x1f, 0x47, 0x17, 0x46, 0x47, 0x47, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x46, 0x47,
+ 0x5e, 0x40, 0x26, 0x06, 0x06, 0x20, 0x40, 0x47, 0x40, 0x40, 0x16, 0x0f, 0x47, 0x64, 0x40,
+ 0x08, 0x65, 0x64, 0x64, 0x63, 0x63, 0x07, 0x07, 0x0f, 0x3e, 0x17, 0x01, 0x07, 0x66, 0x27,
+ 0x4d, 0x17, 0x40, 0x07, 0x1e, 0x40, 0x40, 0x40, 0x47, 0x47, 0x56, 0x5e, 0x4e, 0x5e, 0x65,
+ 0x5d, 0x56, 0x66, 0x66, 0x64, 0x56, 0x5e, 0x66, 0x66, 0x64, 0x4e, 0x46, 0x55, 0x56, 0x5e,
+ 0x4e, 0x5e, 0x65, 0x5d, 0x56, 0x66, 0x66, 0x64, 0x56, 0x5e, 0x66, 0x66, 0x64, 0x4e, 0x46,
+ 0x55, 0x09, 0x4f, 0x54, 0x40, 0x40, 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x64, 0x4e, 0x1f,
+ 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10,
+ 0x07, 0x40, 0x40, 0x07, 0x00, 0x00, 0x01, 0x09, 0x01, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17,
+ 0x16, 0x4f, 0x40, 0x40, 0x40, 0x2e, 0x17, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27,
+ 0x10, 0x07, 0x09, 0x10, 0x01, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x46, 0x17,
+ 0x45, 0x46, 0x46, 0x17, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x45, 0x47, 0x5e, 0x40, 0x25, 0x06,
+ 0x05, 0x20, 0x40, 0x47, 0x40, 0x40, 0x16, 0x0f, 0x47, 0x63, 0x40, 0x08, 0x64, 0x63, 0x62,
+ 0x60, 0x60, 0x07, 0x07, 0x0f, 0x3e, 0x17, 0x01, 0x07, 0x65, 0x27, 0x4c, 0x17, 0x40, 0x07,
+ 0x1d, 0x40, 0x40, 0x40, 0x47, 0x47, 0x56, 0x5d, 0x4e, 0x5d, 0x64, 0x5c, 0x56, 0x65, 0x65,
+ 0x63, 0x56, 0x5e, 0x65, 0x65, 0x63, 0x4d, 0x46, 0x54, 0x56, 0x5d, 0x4e, 0x5d, 0x64, 0x5c,
+ 0x56, 0x65, 0x65, 0x63, 0x56, 0x5e, 0x65, 0x65, 0x63, 0x4d, 0x46, 0x54, 0x09, 0x4f, 0x52,
+ 0x40, 0x40, 0x40, 0x47, 0x07, 0x47, 0x46, 0x46, 0x62, 0x4e, 0x1f, 0x16, 0x4f, 0x10, 0x07,
+ 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x1f, 0x16, 0x4f, 0x10, 0x07, 0x40, 0x40, 0x07,
+ 0x00, 0x00, 0x01, 0x09, 0x01, 0x09, 0x17, 0x17, 0x16, 0x4f, 0x17, 0x16, 0x4f, 0x40, 0x40,
+ 0x40, 0x2d, 0x17, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x1e, 0x20, 0x27, 0x10, 0x07, 0x09, 0x10,
+ 0x01, 0x07, 0x3e, 0x1f, 0x17, 0x40, 0x0f, 0x17, 0x1e, 0x45, 0x17, 0x44, 0x45, 0x45, 0x17,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x3e, 0x44, 0x46, 0x5d, 0x40, 0x24, 0x05, 0x04, 0x21, 0x40, 0x46,
+ 0x40, 0x40, 0x15, 0x0f, 0x46, 0x61, 0x40, 0x09, 0x63, 0x61, 0x60, 0x5e, 0x5e, 0x07, 0x07,
+ 0x0e, 0x3e, 0x16, 0x02, 0x07, 0x64, 0x27, 0x4b, 0x16, 0x40, 0x06, 0x1c, 0x40, 0x40, 0x40,
+ 0x46, 0x46, 0x55, 0x5c, 0x4d, 0x5c, 0x63, 0x5b, 0x55, 0x64, 0x64, 0x61, 0x55, 0x5d, 0x64,
+ 0x64, 0x61, 0x4c, 0x45, 0x53, 0x55, 0x5c, 0x4d, 0x5c, 0x63, 0x5b, 0x55, 0x64, 0x64, 0x61,
+ 0x55, 0x5d, 0x64, 0x64, 0x61, 0x4c, 0x45, 0x53, 0x0a, 0x4e, 0x50, 0x40, 0x41, 0x40, 0x46,
+ 0x07, 0x46, 0x45, 0x45, 0x60, 0x4d, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x15, 0x4e,
+ 0x11, 0x07, 0x40, 0x1e, 0x15, 0x4e, 0x11, 0x07, 0x40, 0x41, 0x07, 0x01, 0x01, 0x02, 0x0a,
+ 0x02, 0x0a, 0x16, 0x17, 0x15, 0x4e, 0x17, 0x15, 0x4e, 0x40, 0x40, 0x40, 0x2c, 0x16, 0x16,
+ 0x40, 0x0f, 0x16, 0x1d, 0x1d, 0x21, 0x27, 0x11, 0x07, 0x0a, 0x11, 0x02, 0x06, 0x3e, 0x1e,
+ 0x16, 0x40, 0x0f, 0x16, 0x1d, 0x44, 0x16, 0x43, 0x44, 0x44, 0x16, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x3e, 0x43, 0x46, 0x5c, 0x40, 0x23, 0x04, 0x03, 0x21, 0x40, 0x46, 0x40, 0x40, 0x14, 0x0f,
+ 0x46, 0x60, 0x40, 0x09, 0x61, 0x60, 0x5e, 0x5b, 0x5b, 0x07, 0x07, 0x0e, 0x3e, 0x16, 0x03,
+ 0x07, 0x63, 0x27, 0x49, 0x16, 0x40, 0x06, 0x1b, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5b,
+ 0x4c, 0x5b, 0x61, 0x59, 0x54, 0x63, 0x63, 0x60, 0x54, 0x5c, 0x63, 0x63, 0x60, 0x4b, 0x44,
+ 0x51, 0x54, 0x5b, 0x4c, 0x5b, 0x61, 0x59, 0x54, 0x63, 0x63, 0x60, 0x54, 0x5c, 0x63, 0x63,
+ 0x60, 0x4b, 0x44, 0x51, 0x0b, 0x4e, 0x4e, 0x40, 0x41, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44,
+ 0x5e, 0x4c, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e,
+ 0x14, 0x4e, 0x11, 0x07, 0x40, 0x41, 0x07, 0x01, 0x01, 0x03, 0x0b, 0x03, 0x0b, 0x16, 0x17,
+ 0x14, 0x4e, 0x17, 0x14, 0x4e, 0x40, 0x40, 0x40, 0x2b, 0x16, 0x16, 0x40, 0x0f, 0x16, 0x1c,
+ 0x1c, 0x21, 0x27, 0x11, 0x07, 0x0b, 0x11, 0x03, 0x06, 0x3e, 0x1e, 0x16, 0x40, 0x0f, 0x16,
+ 0x1c, 0x43, 0x16, 0x41, 0x43, 0x43, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x42, 0x46, 0x5c,
+ 0x40, 0x22, 0x04, 0x02, 0x21, 0x40, 0x46, 0x40, 0x40, 0x14, 0x0f, 0x46, 0x5e, 0x40, 0x09,
+ 0x60, 0x5e, 0x5c, 0x59, 0x59, 0x07, 0x07, 0x0e, 0x3e, 0x16, 0x03, 0x07, 0x62, 0x27, 0x48,
+ 0x16, 0x40, 0x06, 0x1a, 0x40, 0x40, 0x40, 0x46, 0x46, 0x54, 0x5a, 0x4c, 0x5a, 0x60, 0x58,
+ 0x54, 0x62, 0x62, 0x5e, 0x54, 0x5c, 0x62, 0x62, 0x5e, 0x4a, 0x44, 0x50, 0x54, 0x5a, 0x4c,
+ 0x5a, 0x60, 0x58, 0x54, 0x62, 0x62, 0x5e, 0x54, 0x5c, 0x62, 0x62, 0x5e, 0x4a, 0x44, 0x50,
+ 0x0b, 0x4e, 0x4c, 0x40, 0x41, 0x40, 0x46, 0x07, 0x46, 0x44, 0x44, 0x5c, 0x4c, 0x1e, 0x14,
+ 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07, 0x40, 0x1e, 0x14, 0x4e, 0x11, 0x07,
+ 0x40, 0x41, 0x07, 0x01, 0x01, 0x03, 0x0b, 0x03, 0x0b, 0x16, 0x17, 0x14, 0x4e, 0x17, 0x14,
+ 0x4e, 0x40, 0x40, 0x40, 0x2a, 0x16, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x1c, 0x21, 0x27, 0x11,
+ 0x07, 0x0b, 0x11, 0x03, 0x06, 0x3e, 0x1e, 0x16, 0x40, 0x0f, 0x16, 0x1c, 0x42, 0x16, 0x40,
+ 0x42, 0x42, 0x16, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x41, 0x45, 0x5b, 0x40, 0x21, 0x03, 0x01,
+ 0x22, 0x40, 0x45, 0x40, 0x40, 0x13, 0x0f, 0x45, 0x5d, 0x40, 0x0a, 0x5f, 0x5d, 0x5a, 0x56,
+ 0x56, 0x07, 0x07, 0x0d, 0x3e, 0x15, 0x04, 0x07, 0x61, 0x27, 0x47, 0x15, 0x40, 0x05, 0x19,
+ 0x40, 0x40, 0x40, 0x45, 0x45, 0x53, 0x59, 0x4b, 0x59, 0x5f, 0x57, 0x53, 0x61, 0x61, 0x5d,
+ 0x53, 0x5b, 0x61, 0x61, 0x5d, 0x49, 0x43, 0x4f, 0x53, 0x59, 0x4b, 0x59, 0x5f, 0x57, 0x53,
+ 0x61, 0x61, 0x5d, 0x53, 0x5b, 0x61, 0x61, 0x5d, 0x49, 0x43, 0x4f, 0x0c, 0x4d, 0x4a, 0x40,
+ 0x42, 0x40, 0x45, 0x07, 0x45, 0x43, 0x43, 0x5a, 0x4b, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40,
+ 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x13, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x02,
+ 0x02, 0x04, 0x0c, 0x04, 0x0c, 0x15, 0x17, 0x13, 0x4d, 0x17, 0x13, 0x4d, 0x40, 0x40, 0x40,
+ 0x29, 0x15, 0x15, 0x40, 0x0f, 0x15, 0x1b, 0x1b, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x04,
+ 0x05, 0x3e, 0x1d, 0x15, 0x40, 0x0f, 0x15, 0x1b, 0x41, 0x15, 0x00, 0x41, 0x41, 0x15, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x3e, 0x40, 0x45, 0x5b, 0x40, 0x20, 0x02, 0x00, 0x22, 0x40, 0x45, 0x40,
+ 0x40, 0x12, 0x0f, 0x45, 0x5b, 0x40, 0x0a, 0x5e, 0x5b, 0x59, 0x54, 0x54, 0x07, 0x07, 0x0d,
+ 0x3e, 0x15, 0x04, 0x07, 0x60, 0x27, 0x46, 0x15, 0x40, 0x05, 0x18, 0x40, 0x40, 0x40, 0x45,
+ 0x45, 0x53, 0x58, 0x4b, 0x58, 0x5e, 0x56, 0x53, 0x60, 0x60, 0x5b, 0x53, 0x5b, 0x60, 0x60,
+ 0x5b, 0x48, 0x43, 0x4e, 0x53, 0x58, 0x4b, 0x58, 0x5e, 0x56, 0x53, 0x60, 0x60, 0x5b, 0x53,
+ 0x5b, 0x60, 0x60, 0x5b, 0x48, 0x43, 0x4e, 0x0c, 0x4d, 0x49, 0x40, 0x42, 0x40, 0x45, 0x07,
+ 0x45, 0x43, 0x43, 0x59, 0x4b, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12,
+ 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x02, 0x02, 0x04, 0x0c, 0x04,
+ 0x0c, 0x15, 0x17, 0x12, 0x4d, 0x17, 0x12, 0x4d, 0x40, 0x40, 0x40, 0x28, 0x15, 0x15, 0x40,
+ 0x0f, 0x15, 0x1a, 0x1a, 0x22, 0x27, 0x12, 0x07, 0x0c, 0x12, 0x04, 0x05, 0x3e, 0x1d, 0x15,
+ 0x40, 0x0f, 0x15, 0x1a, 0x40, 0x15, 0x01, 0x40, 0x40, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e,
+ 0x00, 0x45, 0x5a, 0x40, 0x1f, 0x02, 0x40, 0x22, 0x40, 0x45, 0x40, 0x40, 0x12, 0x0f, 0x45,
+ 0x59, 0x40, 0x0a, 0x5c, 0x59, 0x57, 0x51, 0x51, 0x07, 0x07, 0x0d, 0x3e, 0x15, 0x05, 0x07,
+ 0x5f, 0x27, 0x44, 0x15, 0x40, 0x05, 0x17, 0x40, 0x40, 0x40, 0x45, 0x45, 0x52, 0x57, 0x4a,
+ 0x57, 0x5c, 0x54, 0x52, 0x5f, 0x5f, 0x59, 0x52, 0x5a, 0x5f, 0x5f, 0x59, 0x47, 0x42, 0x4c,
+ 0x52, 0x57, 0x4a, 0x57, 0x5c, 0x54, 0x52, 0x5f, 0x5f, 0x59, 0x52, 0x5a, 0x5f, 0x5f, 0x59,
+ 0x47, 0x42, 0x4c, 0x0d, 0x4d, 0x47, 0x40, 0x42, 0x40, 0x45, 0x07, 0x45, 0x42, 0x42, 0x57,
+ 0x4a, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12, 0x4d, 0x12, 0x07, 0x40, 0x1d, 0x12,
+ 0x4d, 0x12, 0x07, 0x40, 0x42, 0x07, 0x02, 0x02, 0x05, 0x0d, 0x05, 0x0d, 0x15, 0x17, 0x12,
+ 0x4d, 0x17, 0x12, 0x4d, 0x40, 0x40, 0x40, 0x27, 0x15, 0x15, 0x40, 0x0f, 0x15, 0x1a, 0x1a,
+ 0x22, 0x27, 0x12, 0x07, 0x0d, 0x12, 0x05, 0x05, 0x3e, 0x1d, 0x15, 0x40, 0x0f, 0x15, 0x1a,
+ 0x00, 0x15, 0x03, 0x00, 0x00, 0x15, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x01, 0x44, 0x59, 0x40,
+ 0x1e, 0x01, 0x41, 0x23, 0x40, 0x44, 0x40, 0x40, 0x11, 0x0f, 0x44, 0x58, 0x40, 0x0b, 0x5b,
+ 0x58, 0x55, 0x4f, 0x4f, 0x07, 0x07, 0x0c, 0x3e, 0x14, 0x06, 0x07, 0x5e, 0x27, 0x43, 0x14,
+ 0x40, 0x04, 0x16, 0x40, 0x40, 0x40, 0x44, 0x44, 0x51, 0x56, 0x49, 0x56, 0x5b, 0x53, 0x51,
+ 0x5e, 0x5e, 0x58, 0x51, 0x59, 0x5e, 0x5e, 0x58, 0x46, 0x41, 0x4b, 0x51, 0x56, 0x49, 0x56,
+ 0x5b, 0x53, 0x51, 0x5e, 0x5e, 0x58, 0x51, 0x59, 0x5e, 0x5e, 0x58, 0x46, 0x41, 0x4b, 0x0e,
+ 0x4c, 0x45, 0x40, 0x43, 0x40, 0x44, 0x07, 0x44, 0x41, 0x41, 0x55, 0x49, 0x1c, 0x11, 0x4c,
+ 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40,
+ 0x43, 0x07, 0x03, 0x03, 0x06, 0x0e, 0x06, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c,
+ 0x40, 0x40, 0x40, 0x26, 0x14, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07,
+ 0x0e, 0x13, 0x06, 0x04, 0x3e, 0x1c, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x01, 0x14, 0x04, 0x01,
+ 0x01, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x02, 0x44, 0x59, 0x40, 0x1d, 0x01, 0x42, 0x23,
+ 0x40, 0x44, 0x40, 0x40, 0x11, 0x0f, 0x44, 0x56, 0x40, 0x0b, 0x5a, 0x56, 0x53, 0x4c, 0x4c,
+ 0x07, 0x07, 0x0c, 0x3e, 0x14, 0x06, 0x07, 0x5d, 0x27, 0x42, 0x14, 0x40, 0x04, 0x15, 0x40,
+ 0x40, 0x40, 0x44, 0x44, 0x51, 0x55, 0x49, 0x55, 0x5a, 0x52, 0x51, 0x5d, 0x5d, 0x56, 0x51,
+ 0x59, 0x5d, 0x5d, 0x56, 0x45, 0x41, 0x4a, 0x51, 0x55, 0x49, 0x55, 0x5a, 0x52, 0x51, 0x5d,
+ 0x5d, 0x56, 0x51, 0x59, 0x5d, 0x5d, 0x56, 0x45, 0x41, 0x4a, 0x0e, 0x4c, 0x43, 0x40, 0x43,
+ 0x40, 0x44, 0x07, 0x44, 0x41, 0x41, 0x53, 0x49, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c,
+ 0x11, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x11, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x03, 0x03,
+ 0x06, 0x0e, 0x06, 0x0e, 0x14, 0x17, 0x11, 0x4c, 0x17, 0x11, 0x4c, 0x40, 0x40, 0x40, 0x25,
+ 0x14, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x19, 0x23, 0x27, 0x13, 0x07, 0x0e, 0x13, 0x06, 0x04,
+ 0x3e, 0x1c, 0x14, 0x40, 0x0f, 0x14, 0x19, 0x02, 0x14, 0x05, 0x02, 0x02, 0x14, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x3e, 0x03, 0x44, 0x58, 0x40, 0x1c, 0x00, 0x43, 0x23, 0x40, 0x44, 0x40, 0x40,
+ 0x10, 0x0f, 0x44, 0x55, 0x40, 0x0b, 0x59, 0x55, 0x51, 0x4a, 0x4a, 0x07, 0x07, 0x0c, 0x3d,
+ 0x14, 0x07, 0x07, 0x5c, 0x27, 0x41, 0x14, 0x40, 0x04, 0x14, 0x40, 0x40, 0x40, 0x44, 0x44,
+ 0x50, 0x54, 0x48, 0x54, 0x59, 0x51, 0x50, 0x5c, 0x5c, 0x55, 0x50, 0x58, 0x5c, 0x5c, 0x55,
+ 0x44, 0x40, 0x49, 0x50, 0x54, 0x48, 0x54, 0x59, 0x51, 0x50, 0x5c, 0x5c, 0x55, 0x50, 0x58,
+ 0x5c, 0x5c, 0x55, 0x44, 0x40, 0x49, 0x0f, 0x4c, 0x41, 0x40, 0x43, 0x40, 0x44, 0x07, 0x44,
+ 0x40, 0x40, 0x51, 0x48, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x1c, 0x10, 0x4c, 0x13, 0x07,
+ 0x40, 0x1c, 0x10, 0x4c, 0x13, 0x07, 0x40, 0x43, 0x07, 0x03, 0x03, 0x07, 0x0f, 0x07, 0x0f,
+ 0x14, 0x17, 0x10, 0x4c, 0x17, 0x10, 0x4c, 0x40, 0x40, 0x40, 0x24, 0x14, 0x14, 0x40, 0x0f,
+ 0x14, 0x18, 0x18, 0x23, 0x27, 0x13, 0x07, 0x0f, 0x13, 0x07, 0x04, 0x3e, 0x1c, 0x14, 0x40,
+ 0x0f, 0x14, 0x18, 0x03, 0x14, 0x06, 0x03, 0x03, 0x14, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x04,
+ 0x43, 0x57, 0x40, 0x1b, 0x40, 0x44, 0x24, 0x40, 0x43, 0x40, 0x40, 0x0f, 0x0f, 0x43, 0x53,
+ 0x40, 0x0c, 0x57, 0x53, 0x4f, 0x47, 0x47, 0x07, 0x07, 0x0b, 0x3b, 0x13, 0x08, 0x07, 0x5b,
+ 0x27, 0x00, 0x13, 0x40, 0x03, 0x13, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x53, 0x47, 0x53,
+ 0x57, 0x4f, 0x4f, 0x5b, 0x5b, 0x53, 0x4f, 0x57, 0x5b, 0x5b, 0x53, 0x43, 0x00, 0x47, 0x4f,
+ 0x53, 0x47, 0x53, 0x57, 0x4f, 0x4f, 0x5b, 0x5b, 0x53, 0x4f, 0x57, 0x5b, 0x5b, 0x53, 0x43,
+ 0x00, 0x47, 0x10, 0x4b, 0x00, 0x40, 0x44, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4f, 0x47,
+ 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b,
+ 0x14, 0x07, 0x40, 0x44, 0x07, 0x04, 0x04, 0x08, 0x10, 0x08, 0x10, 0x13, 0x17, 0x0f, 0x4b,
+ 0x17, 0x0f, 0x4b, 0x40, 0x40, 0x40, 0x23, 0x13, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24,
+ 0x27, 0x14, 0x07, 0x10, 0x14, 0x08, 0x03, 0x3e, 0x1b, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x04,
+ 0x13, 0x08, 0x04, 0x04, 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x05, 0x43, 0x57, 0x40, 0x1a,
+ 0x40, 0x45, 0x24, 0x40, 0x43, 0x40, 0x40, 0x0f, 0x0f, 0x43, 0x52, 0x40, 0x0c, 0x56, 0x52,
+ 0x4d, 0x45, 0x45, 0x07, 0x07, 0x0b, 0x3a, 0x13, 0x08, 0x07, 0x5a, 0x27, 0x01, 0x13, 0x40,
+ 0x03, 0x12, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4f, 0x52, 0x47, 0x52, 0x56, 0x4e, 0x4f, 0x5a,
+ 0x5a, 0x52, 0x4f, 0x57, 0x5a, 0x5a, 0x52, 0x42, 0x00, 0x46, 0x4f, 0x52, 0x47, 0x52, 0x56,
+ 0x4e, 0x4f, 0x5a, 0x5a, 0x52, 0x4f, 0x57, 0x5a, 0x5a, 0x52, 0x42, 0x00, 0x46, 0x10, 0x4b,
+ 0x02, 0x40, 0x44, 0x40, 0x43, 0x07, 0x43, 0x00, 0x00, 0x4d, 0x47, 0x1b, 0x0f, 0x4b, 0x14,
+ 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0f, 0x4b, 0x14, 0x07, 0x40, 0x44,
+ 0x07, 0x04, 0x04, 0x08, 0x10, 0x08, 0x10, 0x13, 0x17, 0x0f, 0x4b, 0x17, 0x0f, 0x4b, 0x40,
+ 0x40, 0x40, 0x22, 0x13, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x17, 0x24, 0x27, 0x14, 0x07, 0x10,
+ 0x14, 0x08, 0x03, 0x3e, 0x1b, 0x13, 0x40, 0x0f, 0x13, 0x17, 0x05, 0x13, 0x09, 0x05, 0x05,
+ 0x13, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x06, 0x43, 0x56, 0x40, 0x19, 0x41, 0x46, 0x24, 0x40,
+ 0x43, 0x40, 0x40, 0x0e, 0x0f, 0x43, 0x50, 0x40, 0x0c, 0x55, 0x50, 0x4b, 0x42, 0x42, 0x07,
+ 0x07, 0x0b, 0x38, 0x13, 0x09, 0x07, 0x59, 0x27, 0x02, 0x13, 0x40, 0x03, 0x11, 0x40, 0x40,
+ 0x40, 0x43, 0x43, 0x4e, 0x51, 0x46, 0x51, 0x55, 0x4d, 0x4e, 0x59, 0x59, 0x50, 0x4e, 0x56,
+ 0x59, 0x59, 0x50, 0x41, 0x01, 0x45, 0x4e, 0x51, 0x46, 0x51, 0x55, 0x4d, 0x4e, 0x59, 0x59,
+ 0x50, 0x4e, 0x56, 0x59, 0x59, 0x50, 0x41, 0x01, 0x45, 0x11, 0x4b, 0x04, 0x40, 0x44, 0x40,
+ 0x43, 0x07, 0x43, 0x01, 0x01, 0x4b, 0x46, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0e,
+ 0x4b, 0x14, 0x07, 0x40, 0x1b, 0x0e, 0x4b, 0x14, 0x07, 0x40, 0x44, 0x07, 0x04, 0x04, 0x09,
+ 0x11, 0x09, 0x11, 0x13, 0x17, 0x0e, 0x4b, 0x17, 0x0e, 0x4b, 0x40, 0x40, 0x40, 0x21, 0x13,
+ 0x13, 0x40, 0x0f, 0x13, 0x16, 0x16, 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x09, 0x03, 0x3d,
+ 0x1b, 0x13, 0x40, 0x0f, 0x13, 0x16, 0x06, 0x13, 0x0a, 0x06, 0x06, 0x13, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x3e, 0x06, 0x43, 0x56, 0x40, 0x18, 0x42, 0x47, 0x24, 0x40, 0x43, 0x40, 0x40, 0x0d,
+ 0x0f, 0x43, 0x4f, 0x40, 0x0c, 0x54, 0x4f, 0x4a, 0x40, 0x40, 0x07, 0x07, 0x0a, 0x36, 0x12,
+ 0x09, 0x07, 0x59, 0x27, 0x03, 0x12, 0x40, 0x02, 0x10, 0x40, 0x40, 0x40, 0x43, 0x43, 0x4e,
+ 0x51, 0x46, 0x51, 0x54, 0x4c, 0x4e, 0x59, 0x59, 0x4f, 0x4e, 0x56, 0x59, 0x59, 0x4f, 0x41,
+ 0x01, 0x44, 0x4e, 0x51, 0x46, 0x51, 0x54, 0x4c, 0x4e, 0x59, 0x59, 0x4f, 0x4e, 0x56, 0x59,
+ 0x59, 0x4f, 0x41, 0x01, 0x44, 0x11, 0x4b, 0x05, 0x40, 0x45, 0x40, 0x43, 0x07, 0x43, 0x01,
+ 0x01, 0x4a, 0x46, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40,
+ 0x1a, 0x0d, 0x4b, 0x14, 0x07, 0x40, 0x45, 0x07, 0x04, 0x04, 0x09, 0x11, 0x09, 0x11, 0x12,
+ 0x17, 0x0d, 0x4b, 0x17, 0x0d, 0x4b, 0x40, 0x40, 0x40, 0x20, 0x12, 0x12, 0x40, 0x0f, 0x12,
+ 0x15, 0x15, 0x24, 0x27, 0x14, 0x07, 0x11, 0x14, 0x09, 0x02, 0x3b, 0x1a, 0x12, 0x40, 0x0f,
+ 0x12, 0x15, 0x06, 0x12, 0x0b, 0x06, 0x06, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x07, 0x42,
+ 0x55, 0x40, 0x18, 0x42, 0x47, 0x25, 0x40, 0x42, 0x40, 0x40, 0x0d, 0x0f, 0x42, 0x4d, 0x40,
+ 0x0d, 0x52, 0x4d, 0x48, 0x02, 0x02, 0x07, 0x07, 0x0a, 0x35, 0x12, 0x0a, 0x07, 0x58, 0x27,
+ 0x05, 0x12, 0x40, 0x02, 0x10, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4d, 0x50, 0x45, 0x50, 0x52,
+ 0x4a, 0x4d, 0x58, 0x58, 0x4d, 0x4d, 0x55, 0x58, 0x58, 0x4d, 0x40, 0x02, 0x42, 0x4d, 0x50,
+ 0x45, 0x50, 0x52, 0x4a, 0x4d, 0x58, 0x58, 0x4d, 0x4d, 0x55, 0x58, 0x58, 0x4d, 0x40, 0x02,
+ 0x42, 0x12, 0x4a, 0x07, 0x40, 0x45, 0x40, 0x42, 0x07, 0x42, 0x02, 0x02, 0x48, 0x45, 0x1a,
+ 0x0d, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0d, 0x4a, 0x15,
+ 0x07, 0x40, 0x45, 0x07, 0x05, 0x05, 0x0a, 0x12, 0x0a, 0x12, 0x12, 0x17, 0x0d, 0x4a, 0x17,
+ 0x0d, 0x4a, 0x40, 0x40, 0x40, 0x20, 0x12, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x15, 0x25, 0x27,
+ 0x15, 0x07, 0x12, 0x15, 0x0a, 0x02, 0x3a, 0x1a, 0x12, 0x40, 0x0f, 0x12, 0x15, 0x07, 0x12,
+ 0x0d, 0x07, 0x07, 0x12, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x08, 0x42, 0x54, 0x40, 0x17, 0x43,
+ 0x48, 0x25, 0x40, 0x42, 0x40, 0x40, 0x0c, 0x0f, 0x42, 0x4b, 0x40, 0x0d, 0x51, 0x4b, 0x46,
+ 0x04, 0x04, 0x07, 0x07, 0x0a, 0x33, 0x12, 0x0b, 0x07, 0x57, 0x27, 0x06, 0x12, 0x40, 0x02,
+ 0x0f, 0x40, 0x40, 0x40, 0x42, 0x42, 0x4c, 0x4f, 0x44, 0x4f, 0x51, 0x49, 0x4c, 0x57, 0x57,
+ 0x4b, 0x4c, 0x54, 0x57, 0x57, 0x4b, 0x00, 0x03, 0x41, 0x4c, 0x4f, 0x44, 0x4f, 0x51, 0x49,
+ 0x4c, 0x57, 0x57, 0x4b, 0x4c, 0x54, 0x57, 0x57, 0x4b, 0x00, 0x03, 0x41, 0x13, 0x4a, 0x09,
+ 0x40, 0x45, 0x40, 0x42, 0x07, 0x42, 0x03, 0x03, 0x46, 0x44, 0x1a, 0x0c, 0x4a, 0x15, 0x07,
+ 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07,
+ 0x05, 0x05, 0x0b, 0x13, 0x0b, 0x13, 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40,
+ 0x40, 0x1f, 0x12, 0x12, 0x40, 0x0f, 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15,
+ 0x0b, 0x02, 0x39, 0x1a, 0x12, 0x40, 0x0f, 0x12, 0x14, 0x08, 0x12, 0x0e, 0x08, 0x08, 0x12,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x3e, 0x09, 0x42, 0x54, 0x40, 0x16, 0x43, 0x49, 0x25, 0x40, 0x42,
+ 0x40, 0x40, 0x0c, 0x0f, 0x42, 0x4a, 0x40, 0x0d, 0x50, 0x4a, 0x44, 0x07, 0x07, 0x07, 0x07,
+ 0x0a, 0x32, 0x12, 0x0b, 0x07, 0x56, 0x27, 0x07, 0x12, 0x40, 0x02, 0x0e, 0x40, 0x40, 0x40,
+ 0x42, 0x42, 0x4c, 0x4e, 0x44, 0x4e, 0x50, 0x48, 0x4c, 0x56, 0x56, 0x4a, 0x4c, 0x54, 0x56,
+ 0x56, 0x4a, 0x01, 0x03, 0x40, 0x4c, 0x4e, 0x44, 0x4e, 0x50, 0x48, 0x4c, 0x56, 0x56, 0x4a,
+ 0x4c, 0x54, 0x56, 0x56, 0x4a, 0x01, 0x03, 0x40, 0x13, 0x4a, 0x0b, 0x40, 0x45, 0x40, 0x42,
+ 0x07, 0x42, 0x03, 0x03, 0x44, 0x44, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a,
+ 0x15, 0x07, 0x40, 0x1a, 0x0c, 0x4a, 0x15, 0x07, 0x40, 0x45, 0x07, 0x05, 0x05, 0x0b, 0x13,
+ 0x0b, 0x13, 0x12, 0x17, 0x0c, 0x4a, 0x17, 0x0c, 0x4a, 0x40, 0x40, 0x40, 0x1e, 0x12, 0x12,
+ 0x40, 0x0f, 0x12, 0x14, 0x14, 0x25, 0x27, 0x15, 0x07, 0x13, 0x15, 0x0b, 0x02, 0x38, 0x1a,
+ 0x12, 0x40, 0x0f, 0x12, 0x14, 0x09, 0x12, 0x0f, 0x09, 0x09, 0x12, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x3e, 0x0a, 0x41, 0x53, 0x40, 0x15, 0x44, 0x4a, 0x26, 0x40, 0x41, 0x40, 0x40, 0x0b, 0x0f,
+ 0x41, 0x48, 0x40, 0x0e, 0x4f, 0x48, 0x42, 0x09, 0x09, 0x07, 0x07, 0x09, 0x30, 0x11, 0x0c,
+ 0x07, 0x55, 0x27, 0x08, 0x11, 0x40, 0x01, 0x0d, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4b, 0x4d,
+ 0x43, 0x4d, 0x4f, 0x47, 0x4b, 0x55, 0x55, 0x48, 0x4b, 0x53, 0x55, 0x55, 0x48, 0x02, 0x04,
+ 0x00, 0x4b, 0x4d, 0x43, 0x4d, 0x4f, 0x47, 0x4b, 0x55, 0x55, 0x48, 0x4b, 0x53, 0x55, 0x55,
+ 0x48, 0x02, 0x04, 0x00, 0x14, 0x49, 0x0d, 0x40, 0x46, 0x40, 0x41, 0x07, 0x41, 0x04, 0x04,
+ 0x42, 0x43, 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0b, 0x49, 0x16, 0x07, 0x40, 0x19,
+ 0x0b, 0x49, 0x16, 0x07, 0x40, 0x46, 0x07, 0x06, 0x06, 0x0c, 0x14, 0x0c, 0x14, 0x11, 0x17,
+ 0x0b, 0x49, 0x17, 0x0b, 0x49, 0x40, 0x40, 0x40, 0x1d, 0x11, 0x11, 0x40, 0x0f, 0x11, 0x13,
+ 0x13, 0x26, 0x27, 0x16, 0x07, 0x14, 0x16, 0x0c, 0x01, 0x36, 0x19, 0x11, 0x40, 0x0f, 0x11,
+ 0x13, 0x0a, 0x11, 0x10, 0x0a, 0x0a, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x0b, 0x41, 0x52,
+ 0x40, 0x14, 0x45, 0x4b, 0x26, 0x40, 0x41, 0x40, 0x40, 0x0a, 0x0f, 0x41, 0x47, 0x40, 0x0e,
+ 0x4d, 0x47, 0x40, 0x0c, 0x0c, 0x07, 0x07, 0x09, 0x2f, 0x11, 0x0d, 0x07, 0x54, 0x27, 0x0a,
+ 0x11, 0x40, 0x01, 0x0c, 0x40, 0x40, 0x40, 0x41, 0x41, 0x4a, 0x4c, 0x42, 0x4c, 0x4d, 0x45,
+ 0x4a, 0x54, 0x54, 0x47, 0x4a, 0x52, 0x54, 0x54, 0x47, 0x03, 0x05, 0x02, 0x4a, 0x4c, 0x42,
+ 0x4c, 0x4d, 0x45, 0x4a, 0x54, 0x54, 0x47, 0x4a, 0x52, 0x54, 0x54, 0x47, 0x03, 0x05, 0x02,
+ 0x15, 0x49, 0x0f, 0x40, 0x46, 0x40, 0x41, 0x07, 0x41, 0x05, 0x05, 0x40, 0x42, 0x19, 0x0a,
+ 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07,
+ 0x40, 0x46, 0x07, 0x06, 0x06, 0x0d, 0x15, 0x0d, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a,
+ 0x49, 0x40, 0x40, 0x40, 0x1c, 0x11, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16,
+ 0x07, 0x15, 0x16, 0x0d, 0x01, 0x35, 0x19, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x0b, 0x11, 0x12,
+ 0x0b, 0x0b, 0x11, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x0c, 0x41, 0x52, 0x40, 0x13, 0x45, 0x4c,
+ 0x26, 0x40, 0x41, 0x40, 0x40, 0x0a, 0x0f, 0x41, 0x45, 0x40, 0x0e, 0x4c, 0x45, 0x01, 0x0e,
+ 0x0e, 0x07, 0x07, 0x09, 0x2d, 0x11, 0x0d, 0x07, 0x53, 0x27, 0x0b, 0x11, 0x40, 0x01, 0x0b,
+ 0x40, 0x40, 0x40, 0x41, 0x41, 0x4a, 0x4b, 0x42, 0x4b, 0x4c, 0x44, 0x4a, 0x53, 0x53, 0x45,
+ 0x4a, 0x52, 0x53, 0x53, 0x45, 0x04, 0x05, 0x03, 0x4a, 0x4b, 0x42, 0x4b, 0x4c, 0x44, 0x4a,
+ 0x53, 0x53, 0x45, 0x4a, 0x52, 0x53, 0x53, 0x45, 0x04, 0x05, 0x03, 0x15, 0x49, 0x11, 0x40,
+ 0x46, 0x40, 0x41, 0x07, 0x41, 0x05, 0x05, 0x01, 0x42, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40,
+ 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x19, 0x0a, 0x49, 0x16, 0x07, 0x40, 0x46, 0x07, 0x06,
+ 0x06, 0x0d, 0x15, 0x0d, 0x15, 0x11, 0x17, 0x0a, 0x49, 0x17, 0x0a, 0x49, 0x40, 0x40, 0x40,
+ 0x1b, 0x11, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x12, 0x26, 0x27, 0x16, 0x07, 0x15, 0x16, 0x0d,
+ 0x01, 0x34, 0x19, 0x11, 0x40, 0x0f, 0x11, 0x12, 0x0c, 0x11, 0x13, 0x0c, 0x0c, 0x11, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x3e, 0x0d, 0x40, 0x51, 0x40, 0x12, 0x46, 0x4d, 0x27, 0x40, 0x40, 0x40,
+ 0x40, 0x09, 0x0f, 0x40, 0x44, 0x40, 0x0f, 0x4b, 0x44, 0x03, 0x11, 0x11, 0x07, 0x07, 0x08,
+ 0x2c, 0x10, 0x0e, 0x07, 0x52, 0x27, 0x0c, 0x10, 0x40, 0x00, 0x0a, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x49, 0x4a, 0x41, 0x4a, 0x4b, 0x43, 0x49, 0x52, 0x52, 0x44, 0x49, 0x51, 0x52, 0x52,
+ 0x44, 0x05, 0x06, 0x04, 0x49, 0x4a, 0x41, 0x4a, 0x4b, 0x43, 0x49, 0x52, 0x52, 0x44, 0x49,
+ 0x51, 0x52, 0x52, 0x44, 0x05, 0x06, 0x04, 0x16, 0x48, 0x13, 0x40, 0x47, 0x40, 0x40, 0x07,
+ 0x40, 0x06, 0x06, 0x03, 0x41, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x18, 0x09, 0x48, 0x17,
+ 0x07, 0x40, 0x18, 0x09, 0x48, 0x17, 0x07, 0x40, 0x47, 0x07, 0x07, 0x07, 0x0e, 0x16, 0x0e,
+ 0x16, 0x10, 0x17, 0x09, 0x48, 0x17, 0x09, 0x48, 0x40, 0x40, 0x40, 0x1a, 0x10, 0x10, 0x40,
+ 0x0f, 0x10, 0x11, 0x11, 0x27, 0x27, 0x17, 0x07, 0x16, 0x17, 0x0e, 0x00, 0x33, 0x18, 0x10,
+ 0x40, 0x0f, 0x10, 0x11, 0x0d, 0x10, 0x14, 0x0d, 0x0d, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e,
+ 0x0e, 0x40, 0x51, 0x40, 0x11, 0x47, 0x4e, 0x27, 0x40, 0x40, 0x40, 0x40, 0x08, 0x0f, 0x40,
+ 0x42, 0x40, 0x0f, 0x4a, 0x42, 0x04, 0x13, 0x13, 0x07, 0x07, 0x08, 0x2a, 0x10, 0x0e, 0x07,
+ 0x51, 0x27, 0x0d, 0x10, 0x40, 0x00, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x41,
+ 0x49, 0x4a, 0x42, 0x49, 0x51, 0x51, 0x42, 0x49, 0x51, 0x51, 0x51, 0x42, 0x06, 0x06, 0x05,
+ 0x49, 0x49, 0x41, 0x49, 0x4a, 0x42, 0x49, 0x51, 0x51, 0x42, 0x49, 0x51, 0x51, 0x51, 0x42,
+ 0x06, 0x06, 0x05, 0x16, 0x48, 0x14, 0x40, 0x47, 0x40, 0x40, 0x07, 0x40, 0x06, 0x06, 0x04,
+ 0x41, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08,
+ 0x48, 0x17, 0x07, 0x40, 0x47, 0x07, 0x07, 0x07, 0x0e, 0x16, 0x0e, 0x16, 0x10, 0x17, 0x08,
+ 0x48, 0x17, 0x08, 0x48, 0x40, 0x40, 0x40, 0x19, 0x10, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10,
+ 0x27, 0x27, 0x17, 0x07, 0x16, 0x17, 0x0e, 0x00, 0x31, 0x18, 0x10, 0x40, 0x0f, 0x10, 0x10,
+ 0x0e, 0x10, 0x15, 0x0e, 0x0e, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x0f, 0x40, 0x50, 0x40,
+ 0x10, 0x47, 0x4f, 0x27, 0x40, 0x40, 0x40, 0x40, 0x08, 0x0f, 0x40, 0x40, 0x40, 0x0f, 0x48,
+ 0x40, 0x06, 0x16, 0x16, 0x07, 0x07, 0x08, 0x28, 0x10, 0x0f, 0x07, 0x50, 0x27, 0x0f, 0x10,
+ 0x40, 0x00, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x48, 0x48, 0x40, 0x48, 0x48, 0x40, 0x48,
+ 0x50, 0x50, 0x40, 0x48, 0x50, 0x50, 0x50, 0x40, 0x07, 0x07, 0x07, 0x48, 0x48, 0x40, 0x48,
+ 0x48, 0x40, 0x48, 0x50, 0x50, 0x40, 0x48, 0x50, 0x50, 0x50, 0x40, 0x07, 0x07, 0x07, 0x17,
+ 0x48, 0x16, 0x40, 0x47, 0x40, 0x40, 0x07, 0x40, 0x07, 0x07, 0x06, 0x40, 0x18, 0x08, 0x48,
+ 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40, 0x18, 0x08, 0x48, 0x17, 0x07, 0x40,
+ 0x47, 0x07, 0x07, 0x07, 0x0f, 0x17, 0x0f, 0x17, 0x10, 0x17, 0x08, 0x48, 0x17, 0x08, 0x48,
+ 0x40, 0x40, 0x40, 0x18, 0x10, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x10, 0x27, 0x27, 0x17, 0x07,
+ 0x17, 0x17, 0x0f, 0x00, 0x30, 0x18, 0x10, 0x40, 0x0f, 0x10, 0x10, 0x0f, 0x10, 0x17, 0x0f,
+ 0x0f, 0x10, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x10, 0x00, 0x4f, 0x40, 0x0f, 0x48, 0x50, 0x28,
+ 0x40, 0x00, 0x40, 0x40, 0x07, 0x0f, 0x00, 0x00, 0x40, 0x10, 0x47, 0x00, 0x08, 0x18, 0x18,
+ 0x07, 0x07, 0x07, 0x27, 0x0f, 0x10, 0x07, 0x4f, 0x27, 0x10, 0x0f, 0x40, 0x40, 0x07, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f, 0x4f, 0x00, 0x47,
+ 0x4f, 0x4f, 0x4f, 0x00, 0x08, 0x08, 0x08, 0x47, 0x47, 0x00, 0x47, 0x47, 0x00, 0x47, 0x4f,
+ 0x4f, 0x00, 0x47, 0x4f, 0x4f, 0x4f, 0x00, 0x08, 0x08, 0x08, 0x18, 0x47, 0x18, 0x40, 0x48,
+ 0x40, 0x00, 0x07, 0x00, 0x08, 0x08, 0x08, 0x00, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17,
+ 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08,
+ 0x10, 0x18, 0x10, 0x18, 0x0f, 0x17, 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x17,
+ 0x0f, 0x0f, 0x40, 0x0f, 0x0f, 0x0f, 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40,
+ 0x2f, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x0f, 0x10, 0x0f, 0x18, 0x10, 0x10, 0x0f, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x3e, 0x11, 0x00, 0x4f, 0x40, 0x0e, 0x48, 0x51, 0x28, 0x40, 0x00, 0x40, 0x40,
+ 0x07, 0x0f, 0x00, 0x02, 0x40, 0x10, 0x46, 0x02, 0x0a, 0x1b, 0x1b, 0x07, 0x07, 0x07, 0x25,
+ 0x0f, 0x10, 0x07, 0x4e, 0x27, 0x11, 0x0f, 0x40, 0x40, 0x06, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x47, 0x46, 0x00, 0x46, 0x46, 0x01, 0x47, 0x4e, 0x4e, 0x02, 0x47, 0x4f, 0x4e, 0x4e, 0x02,
+ 0x09, 0x08, 0x09, 0x47, 0x46, 0x00, 0x46, 0x46, 0x01, 0x47, 0x4e, 0x4e, 0x02, 0x47, 0x4f,
+ 0x4e, 0x4e, 0x02, 0x09, 0x08, 0x09, 0x18, 0x47, 0x1a, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00,
+ 0x08, 0x08, 0x0a, 0x00, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x17, 0x07, 0x47, 0x18, 0x07,
+ 0x40, 0x17, 0x07, 0x47, 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x10, 0x18, 0x10, 0x18,
+ 0x0f, 0x17, 0x07, 0x47, 0x17, 0x07, 0x47, 0x40, 0x40, 0x40, 0x16, 0x0f, 0x0f, 0x40, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x28, 0x27, 0x18, 0x07, 0x18, 0x18, 0x10, 0x40, 0x2e, 0x17, 0x0f, 0x40,
+ 0x0f, 0x0f, 0x0f, 0x11, 0x0f, 0x19, 0x11, 0x11, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x12,
+ 0x00, 0x4e, 0x40, 0x0d, 0x49, 0x52, 0x28, 0x40, 0x00, 0x40, 0x40, 0x06, 0x0f, 0x00, 0x03,
+ 0x40, 0x10, 0x45, 0x03, 0x0c, 0x1d, 0x1d, 0x07, 0x07, 0x07, 0x24, 0x0f, 0x11, 0x07, 0x4d,
+ 0x27, 0x12, 0x0f, 0x40, 0x40, 0x05, 0x40, 0x40, 0x40, 0x00, 0x00, 0x46, 0x45, 0x01, 0x45,
+ 0x45, 0x02, 0x46, 0x4d, 0x4d, 0x03, 0x46, 0x4e, 0x4d, 0x4d, 0x03, 0x0a, 0x09, 0x0a, 0x46,
+ 0x45, 0x01, 0x45, 0x45, 0x02, 0x46, 0x4d, 0x4d, 0x03, 0x46, 0x4e, 0x4d, 0x4d, 0x03, 0x0a,
+ 0x09, 0x0a, 0x19, 0x47, 0x1c, 0x40, 0x48, 0x40, 0x00, 0x07, 0x00, 0x09, 0x09, 0x0c, 0x01,
+ 0x17, 0x06, 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47, 0x18, 0x07, 0x40, 0x17, 0x06, 0x47,
+ 0x18, 0x07, 0x40, 0x48, 0x07, 0x08, 0x08, 0x11, 0x19, 0x11, 0x19, 0x0f, 0x17, 0x06, 0x47,
+ 0x17, 0x06, 0x47, 0x40, 0x40, 0x40, 0x15, 0x0f, 0x0f, 0x40, 0x0f, 0x0f, 0x0e, 0x0e, 0x28,
+ 0x27, 0x18, 0x07, 0x19, 0x18, 0x11, 0x40, 0x2c, 0x17, 0x0f, 0x40, 0x0f, 0x0f, 0x0e, 0x12,
+ 0x0f, 0x1a, 0x12, 0x12, 0x0f, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x13, 0x01, 0x4d, 0x40, 0x0c,
+ 0x4a, 0x53, 0x29, 0x40, 0x01, 0x40, 0x40, 0x05, 0x0f, 0x01, 0x05, 0x40, 0x11, 0x43, 0x05,
+ 0x0e, 0x20, 0x20, 0x07, 0x07, 0x06, 0x22, 0x0e, 0x12, 0x07, 0x4c, 0x27, 0x14, 0x0e, 0x40,
+ 0x41, 0x04, 0x40, 0x40, 0x40, 0x01, 0x01, 0x45, 0x44, 0x02, 0x44, 0x43, 0x04, 0x45, 0x4c,
+ 0x4c, 0x05, 0x45, 0x4d, 0x4c, 0x4c, 0x05, 0x0b, 0x0a, 0x0c, 0x45, 0x44, 0x02, 0x44, 0x43,
+ 0x04, 0x45, 0x4c, 0x4c, 0x05, 0x45, 0x4d, 0x4c, 0x4c, 0x05, 0x0b, 0x0a, 0x0c, 0x1a, 0x46,
+ 0x1e, 0x40, 0x49, 0x40, 0x01, 0x07, 0x01, 0x0a, 0x0a, 0x0e, 0x02, 0x16, 0x05, 0x46, 0x19,
+ 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49,
+ 0x07, 0x09, 0x09, 0x12, 0x1a, 0x12, 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40,
+ 0x40, 0x40, 0x14, 0x0e, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a,
+ 0x19, 0x12, 0x41, 0x2b, 0x16, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x13, 0x0e, 0x1c, 0x13, 0x13,
+ 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x14, 0x01, 0x4d, 0x40, 0x0b, 0x4a, 0x54, 0x29, 0x40,
+ 0x01, 0x40, 0x40, 0x05, 0x0f, 0x01, 0x06, 0x40, 0x11, 0x42, 0x06, 0x10, 0x22, 0x22, 0x07,
+ 0x07, 0x06, 0x21, 0x0e, 0x12, 0x07, 0x4b, 0x27, 0x15, 0x0e, 0x40, 0x41, 0x03, 0x40, 0x40,
+ 0x40, 0x01, 0x01, 0x45, 0x43, 0x02, 0x43, 0x42, 0x05, 0x45, 0x4b, 0x4b, 0x06, 0x45, 0x4d,
+ 0x4b, 0x4b, 0x06, 0x0c, 0x0a, 0x0d, 0x45, 0x43, 0x02, 0x43, 0x42, 0x05, 0x45, 0x4b, 0x4b,
+ 0x06, 0x45, 0x4d, 0x4b, 0x4b, 0x06, 0x0c, 0x0a, 0x0d, 0x1a, 0x46, 0x20, 0x40, 0x49, 0x40,
+ 0x01, 0x07, 0x01, 0x0a, 0x0a, 0x10, 0x02, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x16, 0x05,
+ 0x46, 0x19, 0x07, 0x40, 0x16, 0x05, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x09, 0x09, 0x12,
+ 0x1a, 0x12, 0x1a, 0x0e, 0x17, 0x05, 0x46, 0x17, 0x05, 0x46, 0x40, 0x40, 0x40, 0x13, 0x0e,
+ 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x0d, 0x29, 0x27, 0x19, 0x07, 0x1a, 0x19, 0x12, 0x41, 0x2a,
+ 0x16, 0x0e, 0x40, 0x0f, 0x0e, 0x0d, 0x14, 0x0e, 0x1d, 0x14, 0x14, 0x0e, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x3e, 0x15, 0x01, 0x4c, 0x40, 0x0a, 0x4b, 0x55, 0x29, 0x40, 0x01, 0x40, 0x40, 0x04,
+ 0x0f, 0x01, 0x08, 0x40, 0x11, 0x41, 0x08, 0x12, 0x25, 0x25, 0x07, 0x07, 0x06, 0x1f, 0x0e,
+ 0x13, 0x07, 0x4a, 0x27, 0x16, 0x0e, 0x40, 0x41, 0x02, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44,
+ 0x42, 0x03, 0x42, 0x41, 0x06, 0x44, 0x4a, 0x4a, 0x08, 0x44, 0x4c, 0x4a, 0x4a, 0x08, 0x0d,
+ 0x0b, 0x0e, 0x44, 0x42, 0x03, 0x42, 0x41, 0x06, 0x44, 0x4a, 0x4a, 0x08, 0x44, 0x4c, 0x4a,
+ 0x4a, 0x08, 0x0d, 0x0b, 0x0e, 0x1b, 0x46, 0x22, 0x40, 0x49, 0x40, 0x01, 0x07, 0x01, 0x0b,
+ 0x0b, 0x12, 0x03, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x16, 0x04, 0x46, 0x19, 0x07, 0x40,
+ 0x16, 0x04, 0x46, 0x19, 0x07, 0x40, 0x49, 0x07, 0x09, 0x09, 0x13, 0x1b, 0x13, 0x1b, 0x0e,
+ 0x17, 0x04, 0x46, 0x17, 0x04, 0x46, 0x40, 0x40, 0x40, 0x12, 0x0e, 0x0e, 0x40, 0x0f, 0x0e,
+ 0x0c, 0x0c, 0x29, 0x27, 0x19, 0x07, 0x1b, 0x19, 0x13, 0x41, 0x29, 0x16, 0x0e, 0x40, 0x0f,
+ 0x0e, 0x0c, 0x15, 0x0e, 0x1e, 0x15, 0x15, 0x0e, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x15, 0x01,
+ 0x4c, 0x40, 0x09, 0x4c, 0x56, 0x29, 0x40, 0x01, 0x40, 0x40, 0x03, 0x0f, 0x01, 0x09, 0x40,
+ 0x11, 0x40, 0x09, 0x13, 0x27, 0x27, 0x07, 0x07, 0x05, 0x1d, 0x0d, 0x13, 0x07, 0x4a, 0x27,
+ 0x17, 0x0d, 0x40, 0x42, 0x01, 0x40, 0x40, 0x40, 0x01, 0x01, 0x44, 0x42, 0x03, 0x42, 0x40,
+ 0x07, 0x44, 0x4a, 0x4a, 0x09, 0x44, 0x4c, 0x4a, 0x4a, 0x09, 0x0d, 0x0b, 0x0f, 0x44, 0x42,
+ 0x03, 0x42, 0x40, 0x07, 0x44, 0x4a, 0x4a, 0x09, 0x44, 0x4c, 0x4a, 0x4a, 0x09, 0x0d, 0x0b,
+ 0x0f, 0x1b, 0x46, 0x23, 0x40, 0x4a, 0x40, 0x01, 0x07, 0x01, 0x0b, 0x0b, 0x13, 0x03, 0x15,
+ 0x03, 0x46, 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19, 0x07, 0x40, 0x15, 0x03, 0x46, 0x19,
+ 0x07, 0x40, 0x4a, 0x07, 0x09, 0x09, 0x13, 0x1b, 0x13, 0x1b, 0x0d, 0x17, 0x03, 0x46, 0x17,
+ 0x03, 0x46, 0x40, 0x40, 0x40, 0x11, 0x0d, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x29, 0x27,
+ 0x19, 0x07, 0x1b, 0x19, 0x13, 0x42, 0x27, 0x15, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x15, 0x0d,
+ 0x1f, 0x15, 0x15, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x16, 0x02, 0x4b, 0x40, 0x09, 0x4c,
+ 0x56, 0x2a, 0x40, 0x02, 0x40, 0x40, 0x03, 0x0f, 0x02, 0x0b, 0x40, 0x12, 0x01, 0x0b, 0x15,
+ 0x2a, 0x2a, 0x07, 0x07, 0x05, 0x1c, 0x0d, 0x14, 0x07, 0x49, 0x27, 0x19, 0x0d, 0x40, 0x42,
+ 0x01, 0x40, 0x40, 0x40, 0x02, 0x02, 0x43, 0x41, 0x04, 0x41, 0x01, 0x09, 0x43, 0x49, 0x49,
+ 0x0b, 0x43, 0x4b, 0x49, 0x49, 0x0b, 0x0e, 0x0c, 0x11, 0x43, 0x41, 0x04, 0x41, 0x01, 0x09,
+ 0x43, 0x49, 0x49, 0x0b, 0x43, 0x4b, 0x49, 0x49, 0x0b, 0x0e, 0x0c, 0x11, 0x1c, 0x45, 0x25,
+ 0x40, 0x4a, 0x40, 0x02, 0x07, 0x02, 0x0c, 0x0c, 0x15, 0x04, 0x15, 0x03, 0x45, 0x1a, 0x07,
+ 0x40, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x03, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07,
+ 0x0a, 0x0a, 0x14, 0x1c, 0x14, 0x1c, 0x0d, 0x17, 0x03, 0x45, 0x17, 0x03, 0x45, 0x40, 0x40,
+ 0x40, 0x11, 0x0d, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x0b, 0x2a, 0x27, 0x1a, 0x07, 0x1c, 0x1a,
+ 0x14, 0x42, 0x26, 0x15, 0x0d, 0x40, 0x0f, 0x0d, 0x0b, 0x16, 0x0d, 0x21, 0x16, 0x16, 0x0d,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x3e, 0x17, 0x02, 0x4a, 0x40, 0x08, 0x4d, 0x57, 0x2a, 0x40, 0x02,
+ 0x40, 0x40, 0x02, 0x0f, 0x02, 0x0d, 0x40, 0x12, 0x02, 0x0d, 0x17, 0x2c, 0x2c, 0x07, 0x07,
+ 0x05, 0x1a, 0x0d, 0x15, 0x07, 0x48, 0x27, 0x1a, 0x0d, 0x40, 0x42, 0x00, 0x40, 0x40, 0x40,
+ 0x02, 0x02, 0x42, 0x40, 0x05, 0x40, 0x02, 0x0a, 0x42, 0x48, 0x48, 0x0d, 0x42, 0x4a, 0x48,
+ 0x48, 0x0d, 0x0f, 0x0d, 0x12, 0x42, 0x40, 0x05, 0x40, 0x02, 0x0a, 0x42, 0x48, 0x48, 0x0d,
+ 0x42, 0x4a, 0x48, 0x48, 0x0d, 0x0f, 0x0d, 0x12, 0x1d, 0x45, 0x27, 0x40, 0x4a, 0x40, 0x02,
+ 0x07, 0x02, 0x0d, 0x0d, 0x17, 0x05, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45,
+ 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0a, 0x0a, 0x15, 0x1d,
+ 0x15, 0x1d, 0x0d, 0x17, 0x02, 0x45, 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x10, 0x0d, 0x0d,
+ 0x40, 0x0f, 0x0d, 0x0a, 0x0a, 0x2a, 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x15, 0x42, 0x25, 0x15,
+ 0x0d, 0x40, 0x0f, 0x0d, 0x0a, 0x17, 0x0d, 0x22, 0x17, 0x17, 0x0d, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x3e, 0x18, 0x02, 0x4a, 0x40, 0x07, 0x4d, 0x58, 0x2a, 0x40, 0x02, 0x40, 0x40, 0x02, 0x0f,
+ 0x02, 0x0e, 0x40, 0x12, 0x03, 0x0e, 0x19, 0x2f, 0x2f, 0x07, 0x07, 0x05, 0x19, 0x0d, 0x15,
+ 0x07, 0x47, 0x27, 0x1b, 0x0d, 0x40, 0x42, 0x40, 0x40, 0x40, 0x40, 0x02, 0x02, 0x42, 0x00,
+ 0x05, 0x00, 0x03, 0x0b, 0x42, 0x47, 0x47, 0x0e, 0x42, 0x4a, 0x47, 0x47, 0x0e, 0x10, 0x0d,
+ 0x13, 0x42, 0x00, 0x05, 0x00, 0x03, 0x0b, 0x42, 0x47, 0x47, 0x0e, 0x42, 0x4a, 0x47, 0x47,
+ 0x0e, 0x10, 0x0d, 0x13, 0x1d, 0x45, 0x29, 0x40, 0x4a, 0x40, 0x02, 0x07, 0x02, 0x0d, 0x0d,
+ 0x19, 0x05, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15, 0x02, 0x45, 0x1a, 0x07, 0x40, 0x15,
+ 0x02, 0x45, 0x1a, 0x07, 0x40, 0x4a, 0x07, 0x0a, 0x0a, 0x15, 0x1d, 0x15, 0x1d, 0x0d, 0x17,
+ 0x02, 0x45, 0x17, 0x02, 0x45, 0x40, 0x40, 0x40, 0x0f, 0x0d, 0x0d, 0x40, 0x0f, 0x0d, 0x0a,
+ 0x0a, 0x2a, 0x27, 0x1a, 0x07, 0x1d, 0x1a, 0x15, 0x42, 0x24, 0x15, 0x0d, 0x40, 0x0f, 0x0d,
+ 0x0a, 0x18, 0x0d, 0x23, 0x18, 0x18, 0x0d, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x19, 0x03, 0x49,
+ 0x40, 0x06, 0x4e, 0x59, 0x2b, 0x40, 0x03, 0x40, 0x40, 0x01, 0x0f, 0x03, 0x10, 0x40, 0x13,
+ 0x04, 0x10, 0x1b, 0x31, 0x31, 0x07, 0x07, 0x04, 0x17, 0x0c, 0x16, 0x07, 0x46, 0x27, 0x1c,
+ 0x0c, 0x40, 0x43, 0x41, 0x40, 0x40, 0x40, 0x03, 0x03, 0x41, 0x01, 0x06, 0x01, 0x04, 0x0c,
+ 0x41, 0x46, 0x46, 0x10, 0x41, 0x49, 0x46, 0x46, 0x10, 0x11, 0x0e, 0x14, 0x41, 0x01, 0x06,
+ 0x01, 0x04, 0x0c, 0x41, 0x46, 0x46, 0x10, 0x41, 0x49, 0x46, 0x46, 0x10, 0x11, 0x0e, 0x14,
+ 0x1e, 0x44, 0x2b, 0x40, 0x4b, 0x40, 0x03, 0x07, 0x03, 0x0e, 0x0e, 0x1b, 0x06, 0x14, 0x01,
+ 0x44, 0x1b, 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x01, 0x44, 0x1b, 0x07,
+ 0x40, 0x4b, 0x07, 0x0b, 0x0b, 0x16, 0x1e, 0x16, 0x1e, 0x0c, 0x17, 0x01, 0x44, 0x17, 0x01,
+ 0x44, 0x40, 0x40, 0x40, 0x0e, 0x0c, 0x0c, 0x40, 0x0f, 0x0c, 0x09, 0x09, 0x2b, 0x27, 0x1b,
+ 0x07, 0x1e, 0x1b, 0x16, 0x43, 0x22, 0x14, 0x0c, 0x40, 0x0f, 0x0c, 0x09, 0x19, 0x0c, 0x24,
+ 0x19, 0x19, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x1a, 0x03, 0x48, 0x40, 0x05, 0x4f, 0x5a,
+ 0x2b, 0x40, 0x03, 0x40, 0x40, 0x00, 0x0f, 0x03, 0x11, 0x40, 0x13, 0x06, 0x11, 0x1d, 0x34,
+ 0x34, 0x07, 0x07, 0x04, 0x16, 0x0c, 0x17, 0x07, 0x45, 0x27, 0x1e, 0x0c, 0x40, 0x43, 0x42,
+ 0x40, 0x40, 0x40, 0x03, 0x03, 0x40, 0x02, 0x07, 0x02, 0x06, 0x0e, 0x40, 0x45, 0x45, 0x11,
+ 0x40, 0x48, 0x45, 0x45, 0x11, 0x12, 0x0f, 0x16, 0x40, 0x02, 0x07, 0x02, 0x06, 0x0e, 0x40,
+ 0x45, 0x45, 0x11, 0x40, 0x48, 0x45, 0x45, 0x11, 0x12, 0x0f, 0x16, 0x1f, 0x44, 0x2d, 0x40,
+ 0x4b, 0x40, 0x03, 0x07, 0x03, 0x0f, 0x0f, 0x1d, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40,
+ 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0b,
+ 0x0b, 0x17, 0x1f, 0x17, 0x1f, 0x0c, 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40,
+ 0x0d, 0x0c, 0x0c, 0x40, 0x0f, 0x0c, 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x17,
+ 0x43, 0x21, 0x14, 0x0c, 0x40, 0x0f, 0x0c, 0x08, 0x1a, 0x0c, 0x26, 0x1a, 0x1a, 0x0c, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x07, 0x3e, 0x1b, 0x03, 0x48, 0x40, 0x04, 0x4f, 0x5b, 0x2b, 0x40, 0x03, 0x40,
+ 0x40, 0x00, 0x0f, 0x03, 0x13, 0x40, 0x13, 0x07, 0x13, 0x1f, 0x36, 0x36, 0x07, 0x07, 0x04,
+ 0x14, 0x0c, 0x17, 0x07, 0x44, 0x27, 0x1f, 0x0c, 0x40, 0x43, 0x43, 0x40, 0x40, 0x40, 0x03,
+ 0x03, 0x40, 0x03, 0x07, 0x03, 0x07, 0x0f, 0x40, 0x44, 0x44, 0x13, 0x40, 0x48, 0x44, 0x44,
+ 0x13, 0x13, 0x0f, 0x17, 0x40, 0x03, 0x07, 0x03, 0x07, 0x0f, 0x40, 0x44, 0x44, 0x13, 0x40,
+ 0x48, 0x44, 0x44, 0x13, 0x13, 0x0f, 0x17, 0x1f, 0x44, 0x2f, 0x40, 0x4b, 0x40, 0x03, 0x07,
+ 0x03, 0x0f, 0x0f, 0x1f, 0x07, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b,
+ 0x07, 0x40, 0x14, 0x00, 0x44, 0x1b, 0x07, 0x40, 0x4b, 0x07, 0x0b, 0x0b, 0x17, 0x1f, 0x17,
+ 0x1f, 0x0c, 0x17, 0x00, 0x44, 0x17, 0x00, 0x44, 0x40, 0x40, 0x40, 0x0c, 0x0c, 0x0c, 0x40,
+ 0x0f, 0x0c, 0x08, 0x08, 0x2b, 0x27, 0x1b, 0x07, 0x1f, 0x1b, 0x17, 0x43, 0x20, 0x14, 0x0c,
+ 0x40, 0x0f, 0x0c, 0x08, 0x1b, 0x0c, 0x27, 0x1b, 0x1b, 0x0c, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e,
+ 0x1c, 0x04, 0x47, 0x40, 0x03, 0x50, 0x5c, 0x2c, 0x40, 0x04, 0x40, 0x40, 0x40, 0x0f, 0x04,
+ 0x14, 0x40, 0x14, 0x08, 0x14, 0x21, 0x39, 0x39, 0x07, 0x07, 0x03, 0x13, 0x0b, 0x18, 0x07,
+ 0x43, 0x27, 0x20, 0x0b, 0x40, 0x44, 0x44, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x04, 0x08,
+ 0x04, 0x08, 0x10, 0x00, 0x43, 0x43, 0x14, 0x00, 0x47, 0x43, 0x43, 0x14, 0x14, 0x10, 0x18,
+ 0x00, 0x04, 0x08, 0x04, 0x08, 0x10, 0x00, 0x43, 0x43, 0x14, 0x00, 0x47, 0x43, 0x43, 0x14,
+ 0x14, 0x10, 0x18, 0x20, 0x43, 0x31, 0x40, 0x4c, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x21,
+ 0x08, 0x13, 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x40,
+ 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07, 0x0c, 0x0c, 0x18, 0x20, 0x18, 0x20, 0x0b, 0x17, 0x40,
+ 0x43, 0x17, 0x40, 0x43, 0x40, 0x40, 0x40, 0x0b, 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x07, 0x07,
+ 0x2c, 0x27, 0x1c, 0x07, 0x20, 0x1c, 0x18, 0x44, 0x1f, 0x13, 0x0b, 0x40, 0x0f, 0x0b, 0x07,
+ 0x1c, 0x0b, 0x28, 0x1c, 0x1c, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x1d, 0x04, 0x47, 0x40,
+ 0x02, 0x51, 0x5d, 0x2c, 0x40, 0x04, 0x40, 0x40, 0x41, 0x0f, 0x04, 0x16, 0x40, 0x14, 0x09,
+ 0x16, 0x22, 0x3b, 0x3b, 0x07, 0x07, 0x03, 0x11, 0x0b, 0x18, 0x07, 0x42, 0x27, 0x21, 0x0b,
+ 0x40, 0x44, 0x45, 0x40, 0x40, 0x40, 0x04, 0x04, 0x00, 0x05, 0x08, 0x05, 0x09, 0x11, 0x00,
+ 0x42, 0x42, 0x16, 0x00, 0x47, 0x42, 0x42, 0x16, 0x15, 0x10, 0x19, 0x00, 0x05, 0x08, 0x05,
+ 0x09, 0x11, 0x00, 0x42, 0x42, 0x16, 0x00, 0x47, 0x42, 0x42, 0x16, 0x15, 0x10, 0x19, 0x20,
+ 0x43, 0x32, 0x40, 0x4c, 0x40, 0x04, 0x07, 0x04, 0x10, 0x10, 0x22, 0x08, 0x13, 0x41, 0x43,
+ 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40,
+ 0x4c, 0x07, 0x0c, 0x0c, 0x18, 0x20, 0x18, 0x20, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43,
+ 0x40, 0x40, 0x40, 0x0a, 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07,
+ 0x20, 0x1c, 0x18, 0x44, 0x1d, 0x13, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x1d, 0x0b, 0x29, 0x1d,
+ 0x1d, 0x0b, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x1e, 0x04, 0x46, 0x40, 0x01, 0x51, 0x5e, 0x2c,
+ 0x40, 0x04, 0x40, 0x40, 0x41, 0x0f, 0x04, 0x18, 0x40, 0x14, 0x0b, 0x18, 0x24, 0x3e, 0x3e,
+ 0x07, 0x07, 0x03, 0x0f, 0x0b, 0x19, 0x07, 0x41, 0x27, 0x23, 0x0b, 0x40, 0x44, 0x46, 0x40,
+ 0x40, 0x40, 0x04, 0x04, 0x01, 0x06, 0x09, 0x06, 0x0b, 0x13, 0x01, 0x41, 0x41, 0x18, 0x01,
+ 0x46, 0x41, 0x41, 0x18, 0x16, 0x11, 0x1b, 0x01, 0x06, 0x09, 0x06, 0x0b, 0x13, 0x01, 0x41,
+ 0x41, 0x18, 0x01, 0x46, 0x41, 0x41, 0x18, 0x16, 0x11, 0x1b, 0x21, 0x43, 0x34, 0x40, 0x4c,
+ 0x40, 0x04, 0x07, 0x04, 0x11, 0x11, 0x24, 0x09, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13,
+ 0x41, 0x43, 0x1c, 0x07, 0x40, 0x13, 0x41, 0x43, 0x1c, 0x07, 0x40, 0x4c, 0x07, 0x0c, 0x0c,
+ 0x19, 0x21, 0x19, 0x21, 0x0b, 0x17, 0x41, 0x43, 0x17, 0x41, 0x43, 0x40, 0x40, 0x40, 0x09,
+ 0x0b, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x06, 0x2c, 0x27, 0x1c, 0x07, 0x21, 0x1c, 0x19, 0x44,
+ 0x1c, 0x13, 0x0b, 0x40, 0x0f, 0x0b, 0x06, 0x1e, 0x0b, 0x2b, 0x1e, 0x1e, 0x0b, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x07, 0x3e, 0x1f, 0x05, 0x45, 0x40, 0x00, 0x52, 0x5f, 0x2d, 0x40, 0x05, 0x40, 0x40,
+ 0x42, 0x0f, 0x05, 0x19, 0x40, 0x15, 0x0c, 0x19, 0x26, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0e,
+ 0x0a, 0x1a, 0x07, 0x40, 0x27, 0x24, 0x0a, 0x40, 0x45, 0x47, 0x40, 0x40, 0x40, 0x05, 0x05,
+ 0x02, 0x07, 0x0a, 0x07, 0x0c, 0x14, 0x02, 0x40, 0x40, 0x19, 0x02, 0x45, 0x40, 0x40, 0x19,
+ 0x17, 0x12, 0x1c, 0x02, 0x07, 0x0a, 0x07, 0x0c, 0x14, 0x02, 0x40, 0x40, 0x19, 0x02, 0x45,
+ 0x40, 0x40, 0x19, 0x17, 0x12, 0x1c, 0x22, 0x42, 0x36, 0x40, 0x4d, 0x40, 0x05, 0x07, 0x05,
+ 0x12, 0x12, 0x26, 0x0a, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07,
+ 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x0d, 0x0d, 0x1a, 0x22, 0x1a, 0x22,
+ 0x0a, 0x17, 0x42, 0x42, 0x17, 0x42, 0x42, 0x40, 0x40, 0x40, 0x08, 0x0a, 0x0a, 0x40, 0x0f,
+ 0x0a, 0x05, 0x05, 0x2d, 0x27, 0x1d, 0x07, 0x22, 0x1d, 0x1a, 0x45, 0x1b, 0x12, 0x0a, 0x40,
+ 0x0f, 0x0a, 0x05, 0x1f, 0x0a, 0x2c, 0x1f, 0x1f, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x20,
+ 0x05, 0x45, 0x40, 0x40, 0x52, 0x60, 0x2d, 0x40, 0x05, 0x40, 0x40, 0x42, 0x0f, 0x05, 0x1b,
+ 0x40, 0x15, 0x0d, 0x1b, 0x28, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0c, 0x0a, 0x1a, 0x07, 0x00,
+ 0x27, 0x25, 0x0a, 0x40, 0x45, 0x48, 0x40, 0x40, 0x40, 0x05, 0x05, 0x02, 0x08, 0x0a, 0x08,
+ 0x0d, 0x15, 0x02, 0x00, 0x00, 0x1b, 0x02, 0x45, 0x00, 0x00, 0x1b, 0x18, 0x12, 0x1d, 0x02,
+ 0x08, 0x0a, 0x08, 0x0d, 0x15, 0x02, 0x00, 0x00, 0x1b, 0x02, 0x45, 0x00, 0x00, 0x1b, 0x18,
+ 0x12, 0x1d, 0x22, 0x42, 0x38, 0x40, 0x4d, 0x40, 0x05, 0x07, 0x05, 0x12, 0x12, 0x28, 0x0a,
+ 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x42, 0x42,
+ 0x1d, 0x07, 0x40, 0x4d, 0x07, 0x0d, 0x0d, 0x1a, 0x22, 0x1a, 0x22, 0x0a, 0x17, 0x42, 0x42,
+ 0x17, 0x42, 0x42, 0x40, 0x40, 0x40, 0x07, 0x0a, 0x0a, 0x40, 0x0f, 0x0a, 0x05, 0x05, 0x2d,
+ 0x27, 0x1d, 0x07, 0x22, 0x1d, 0x1a, 0x45, 0x1a, 0x12, 0x0a, 0x40, 0x0f, 0x0a, 0x05, 0x20,
+ 0x0a, 0x2d, 0x20, 0x20, 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x21, 0x05, 0x44, 0x40, 0x41,
+ 0x53, 0x61, 0x2d, 0x40, 0x05, 0x40, 0x40, 0x43, 0x0f, 0x05, 0x1c, 0x40, 0x15, 0x0e, 0x1c,
+ 0x2a, 0x3e, 0x3e, 0x07, 0x07, 0x02, 0x0b, 0x0a, 0x1b, 0x07, 0x01, 0x27, 0x26, 0x0a, 0x40,
+ 0x45, 0x49, 0x40, 0x40, 0x40, 0x05, 0x05, 0x03, 0x09, 0x0b, 0x09, 0x0e, 0x16, 0x03, 0x01,
+ 0x01, 0x1c, 0x03, 0x44, 0x01, 0x01, 0x1c, 0x19, 0x13, 0x1e, 0x03, 0x09, 0x0b, 0x09, 0x0e,
+ 0x16, 0x03, 0x01, 0x01, 0x1c, 0x03, 0x44, 0x01, 0x01, 0x1c, 0x19, 0x13, 0x1e, 0x23, 0x42,
+ 0x3a, 0x40, 0x4d, 0x40, 0x05, 0x07, 0x05, 0x13, 0x13, 0x2a, 0x0b, 0x12, 0x43, 0x42, 0x1d,
+ 0x07, 0x40, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x12, 0x43, 0x42, 0x1d, 0x07, 0x40, 0x4d,
+ 0x07, 0x0d, 0x0d, 0x1b, 0x23, 0x1b, 0x23, 0x0a, 0x17, 0x43, 0x42, 0x17, 0x43, 0x42, 0x40,
+ 0x40, 0x40, 0x06, 0x0a, 0x0a, 0x40, 0x0f, 0x0a, 0x04, 0x04, 0x2d, 0x27, 0x1d, 0x07, 0x23,
+ 0x1d, 0x1b, 0x45, 0x18, 0x12, 0x0a, 0x40, 0x0f, 0x0a, 0x04, 0x21, 0x0a, 0x2e, 0x21, 0x21,
+ 0x0a, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x22, 0x06, 0x43, 0x40, 0x42, 0x54, 0x62, 0x2e, 0x40,
+ 0x06, 0x40, 0x40, 0x44, 0x0f, 0x06, 0x1e, 0x40, 0x16, 0x10, 0x1e, 0x2c, 0x3e, 0x3e, 0x07,
+ 0x07, 0x01, 0x09, 0x09, 0x1c, 0x07, 0x02, 0x27, 0x28, 0x09, 0x40, 0x46, 0x4a, 0x40, 0x40,
+ 0x40, 0x06, 0x06, 0x04, 0x0a, 0x0c, 0x0a, 0x10, 0x18, 0x04, 0x02, 0x02, 0x1e, 0x04, 0x43,
+ 0x02, 0x02, 0x1e, 0x1a, 0x14, 0x20, 0x04, 0x0a, 0x0c, 0x0a, 0x10, 0x18, 0x04, 0x02, 0x02,
+ 0x1e, 0x04, 0x43, 0x02, 0x02, 0x1e, 0x1a, 0x14, 0x20, 0x24, 0x41, 0x3c, 0x40, 0x4e, 0x40,
+ 0x06, 0x07, 0x06, 0x14, 0x14, 0x2c, 0x0c, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44,
+ 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x0e, 0x0e, 0x1c,
+ 0x24, 0x1c, 0x24, 0x09, 0x17, 0x44, 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x05, 0x09,
+ 0x09, 0x40, 0x0f, 0x09, 0x03, 0x03, 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x1c, 0x46, 0x17,
+ 0x11, 0x09, 0x40, 0x0f, 0x09, 0x03, 0x22, 0x09, 0x30, 0x22, 0x22, 0x09, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x07, 0x3e, 0x23, 0x06, 0x43, 0x40, 0x43, 0x54, 0x63, 0x2e, 0x40, 0x06, 0x40, 0x40, 0x44,
+ 0x0f, 0x06, 0x1f, 0x40, 0x16, 0x11, 0x1f, 0x2e, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x08, 0x09,
+ 0x1c, 0x07, 0x03, 0x27, 0x29, 0x09, 0x40, 0x46, 0x4b, 0x40, 0x40, 0x40, 0x06, 0x06, 0x04,
+ 0x0b, 0x0c, 0x0b, 0x11, 0x19, 0x04, 0x03, 0x03, 0x1f, 0x04, 0x43, 0x03, 0x03, 0x1f, 0x1b,
+ 0x14, 0x21, 0x04, 0x0b, 0x0c, 0x0b, 0x11, 0x19, 0x04, 0x03, 0x03, 0x1f, 0x04, 0x43, 0x03,
+ 0x03, 0x1f, 0x1b, 0x14, 0x21, 0x24, 0x41, 0x3e, 0x40, 0x4e, 0x40, 0x06, 0x07, 0x06, 0x14,
+ 0x14, 0x2e, 0x0c, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40,
+ 0x11, 0x44, 0x41, 0x1e, 0x07, 0x40, 0x4e, 0x07, 0x0e, 0x0e, 0x1c, 0x24, 0x1c, 0x24, 0x09,
+ 0x17, 0x44, 0x41, 0x17, 0x44, 0x41, 0x40, 0x40, 0x40, 0x04, 0x09, 0x09, 0x40, 0x0f, 0x09,
+ 0x03, 0x03, 0x2e, 0x27, 0x1e, 0x07, 0x24, 0x1e, 0x1c, 0x46, 0x16, 0x11, 0x09, 0x40, 0x0f,
+ 0x09, 0x03, 0x23, 0x09, 0x31, 0x23, 0x23, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x24, 0x06,
+ 0x42, 0x40, 0x44, 0x55, 0x64, 0x2e, 0x40, 0x06, 0x40, 0x40, 0x45, 0x0f, 0x06, 0x21, 0x40,
+ 0x16, 0x12, 0x21, 0x30, 0x3e, 0x3e, 0x07, 0x07, 0x01, 0x06, 0x09, 0x1d, 0x07, 0x04, 0x27,
+ 0x2a, 0x09, 0x40, 0x46, 0x4c, 0x40, 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x0d, 0x0c, 0x12,
+ 0x1a, 0x05, 0x04, 0x04, 0x21, 0x05, 0x42, 0x04, 0x04, 0x21, 0x1c, 0x15, 0x22, 0x05, 0x0c,
+ 0x0d, 0x0c, 0x12, 0x1a, 0x05, 0x04, 0x04, 0x21, 0x05, 0x42, 0x04, 0x04, 0x21, 0x1c, 0x15,
+ 0x22, 0x25, 0x41, 0x3e, 0x40, 0x4e, 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x30, 0x0d, 0x11,
+ 0x45, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e, 0x07, 0x40, 0x11, 0x45, 0x41, 0x1e,
+ 0x07, 0x40, 0x4e, 0x07, 0x0e, 0x0e, 0x1d, 0x25, 0x1d, 0x25, 0x09, 0x17, 0x45, 0x41, 0x17,
+ 0x45, 0x41, 0x40, 0x40, 0x40, 0x03, 0x09, 0x09, 0x40, 0x0f, 0x09, 0x02, 0x02, 0x2e, 0x27,
+ 0x1e, 0x07, 0x25, 0x1e, 0x1d, 0x46, 0x15, 0x11, 0x09, 0x40, 0x0f, 0x09, 0x02, 0x24, 0x09,
+ 0x32, 0x24, 0x24, 0x09, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x24, 0x06, 0x42, 0x40, 0x45, 0x56,
+ 0x65, 0x2e, 0x40, 0x06, 0x40, 0x40, 0x46, 0x0f, 0x06, 0x22, 0x40, 0x16, 0x13, 0x22, 0x31,
+ 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x04, 0x08, 0x1d, 0x07, 0x04, 0x27, 0x2b, 0x08, 0x40, 0x47,
+ 0x4d, 0x40, 0x40, 0x40, 0x06, 0x06, 0x05, 0x0c, 0x0d, 0x0c, 0x13, 0x1b, 0x05, 0x04, 0x04,
+ 0x22, 0x05, 0x42, 0x04, 0x04, 0x22, 0x1c, 0x15, 0x23, 0x05, 0x0c, 0x0d, 0x0c, 0x13, 0x1b,
+ 0x05, 0x04, 0x04, 0x22, 0x05, 0x42, 0x04, 0x04, 0x22, 0x1c, 0x15, 0x23, 0x25, 0x41, 0x3e,
+ 0x40, 0x4f, 0x40, 0x06, 0x07, 0x06, 0x15, 0x15, 0x31, 0x0d, 0x10, 0x46, 0x41, 0x1e, 0x07,
+ 0x40, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x10, 0x46, 0x41, 0x1e, 0x07, 0x40, 0x4f, 0x07,
+ 0x0e, 0x0e, 0x1d, 0x25, 0x1d, 0x25, 0x08, 0x17, 0x46, 0x41, 0x17, 0x46, 0x41, 0x40, 0x40,
+ 0x40, 0x02, 0x08, 0x08, 0x40, 0x0f, 0x08, 0x01, 0x01, 0x2e, 0x27, 0x1e, 0x07, 0x25, 0x1e,
+ 0x1d, 0x47, 0x13, 0x10, 0x08, 0x40, 0x0f, 0x08, 0x01, 0x24, 0x08, 0x33, 0x24, 0x24, 0x08,
+ 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, 0x3e, 0x25, 0x07, 0x41, 0x40, 0x45, 0x56, 0x65, 0x2f, 0x40, 0x07,
+ 0x40, 0x40, 0x46, 0x0f, 0x07, 0x24, 0x40, 0x17, 0x15, 0x24, 0x33, 0x3e, 0x3e, 0x07, 0x07,
+ 0x00, 0x03, 0x08, 0x1e, 0x07, 0x05, 0x27, 0x2d, 0x08, 0x40, 0x47, 0x4d, 0x40, 0x40, 0x40,
+ 0x07, 0x07, 0x06, 0x0d, 0x0e, 0x0d, 0x15, 0x1d, 0x06, 0x05, 0x05, 0x24, 0x06, 0x41, 0x05,
+ 0x05, 0x24, 0x1d, 0x16, 0x25, 0x06, 0x0d, 0x0e, 0x0d, 0x15, 0x1d, 0x06, 0x05, 0x05, 0x24,
+ 0x06, 0x41, 0x05, 0x05, 0x24, 0x1d, 0x16, 0x25, 0x26, 0x40, 0x3e, 0x40, 0x4f, 0x40, 0x07,
+ 0x07, 0x07, 0x16, 0x16, 0x33, 0x0e, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x46, 0x40,
+ 0x1f, 0x07, 0x40, 0x10, 0x46, 0x40, 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x0f, 0x0f, 0x1e, 0x26,
+ 0x1e, 0x26, 0x08, 0x17, 0x46, 0x40, 0x17, 0x46, 0x40, 0x40, 0x40, 0x40, 0x02, 0x08, 0x08,
+ 0x40, 0x0f, 0x08, 0x01, 0x01, 0x2f, 0x27, 0x1f, 0x07, 0x26, 0x1f, 0x1e, 0x47, 0x12, 0x10,
+ 0x08, 0x40, 0x0f, 0x08, 0x01, 0x25, 0x08, 0x35, 0x25, 0x25, 0x08, 0x40, 0x40, 0x40, 0x40,
+ 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
+ 0x3e, 0x26, 0x07, 0x40, 0x40, 0x46, 0x57, 0x66, 0x2f, 0x40, 0x07, 0x40, 0x40, 0x47, 0x0f,
+ 0x07, 0x26, 0x40, 0x17, 0x16, 0x26, 0x35, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x01, 0x08, 0x1f,
+ 0x07, 0x06, 0x27, 0x2e, 0x08, 0x40, 0x47, 0x4e, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0e,
+ 0x0f, 0x0e, 0x16, 0x1e, 0x07, 0x06, 0x06, 0x26, 0x07, 0x40, 0x06, 0x06, 0x26, 0x1e, 0x17,
+ 0x26, 0x07, 0x0e, 0x0f, 0x0e, 0x16, 0x1e, 0x07, 0x06, 0x06, 0x26, 0x07, 0x40, 0x06, 0x06,
+ 0x26, 0x1e, 0x17, 0x26, 0x27, 0x40, 0x3e, 0x40, 0x4f, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17,
+ 0x35, 0x0f, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10,
+ 0x47, 0x40, 0x1f, 0x07, 0x40, 0x4f, 0x07, 0x0f, 0x0f, 0x1f, 0x27, 0x1f, 0x27, 0x08, 0x17,
+ 0x47, 0x40, 0x17, 0x47, 0x40, 0x40, 0x40, 0x40, 0x01, 0x08, 0x08, 0x40, 0x0f, 0x08, 0x00,
+ 0x00, 0x2f, 0x27, 0x1f, 0x07, 0x27, 0x1f, 0x1f, 0x47, 0x11, 0x10, 0x08, 0x40, 0x0f, 0x08,
+ 0x00, 0x26, 0x08, 0x36, 0x26, 0x26, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3e, 0x27, 0x07, 0x40,
+ 0x40, 0x47, 0x57, 0x67, 0x2f, 0x40, 0x07, 0x40, 0x40, 0x47, 0x0f, 0x07, 0x27, 0x40, 0x17,
+ 0x17, 0x27, 0x37, 0x3e, 0x3e, 0x07, 0x07, 0x00, 0x00, 0x08, 0x1f, 0x07, 0x07, 0x27, 0x2f,
+ 0x08, 0x40, 0x47, 0x4f, 0x40, 0x40, 0x40, 0x07, 0x07, 0x07, 0x0f, 0x0f, 0x0f, 0x17, 0x1f,
+ 0x07, 0x07, 0x07, 0x27, 0x07, 0x40, 0x07, 0x07, 0x27, 0x1f, 0x17, 0x27, 0x07, 0x0f, 0x0f,
+ 0x0f, 0x17, 0x1f, 0x07, 0x07, 0x07, 0x27, 0x07, 0x40, 0x07, 0x07, 0x27, 0x1f, 0x17, 0x27,
+ 0x27, 0x40, 0x3e, 0x40, 0x4f, 0x40, 0x07, 0x07, 0x07, 0x17, 0x17, 0x37, 0x0f, 0x10, 0x47,
+ 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07, 0x40, 0x10, 0x47, 0x40, 0x1f, 0x07,
+ 0x40, 0x4f, 0x07, 0x0f, 0x0f, 0x1f, 0x27, 0x1f, 0x27, 0x08, 0x17, 0x47, 0x40, 0x17, 0x47,
+ 0x40, 0x40, 0x40, 0x40, 0x00, 0x08, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x00, 0x2f, 0x27, 0x1f,
+ 0x07, 0x27, 0x1f, 0x1f, 0x47, 0x10, 0x10, 0x08, 0x40, 0x0f, 0x08, 0x00, 0x27, 0x08, 0x37,
+ 0x27, 0x27, 0x08, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
new file mode 100644
index 000000000000..fc7e6a260b0a
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
@@ -0,0 +1,820 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Rockchip Video Decoder HEVC backend
+ *
+ * Copyright (C) 2023 Collabora, Ltd.
+ * Sebastian Fricke <sebastian.fricke@collabora.com>
+ *
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ *
+ * Copyright (C) 2016 Rockchip Electronics Co., Ltd.
+ * Jeffy Chen <jeffy.chen@rock-chips.com>
+ */
+
+#include <media/v4l2-mem2mem.h>
+
+#include "rkvdec.h"
+#include "rkvdec-regs.h"
+#include "rkvdec-hevc-data.c"
+
+/* Size in u8/u32 units. */
+#define RKV_SCALING_LIST_SIZE 1360
+#define RKV_PPS_SIZE (80 / 4)
+#define RKV_PPS_LEN 64
+#define RKV_RPS_SIZE (32 / 4)
+#define RKV_RPS_LEN 600
+
+struct rkvdec_sps_pps_packet {
+ u32 info[RKV_PPS_SIZE];
+};
+
+struct rkvdec_rps_packet {
+ u32 info[RKV_RPS_SIZE];
+};
+
+struct rkvdec_ps_field {
+ u16 offset;
+ u8 len;
+};
+
+#define PS_FIELD(_offset, _len) \
+ ((struct rkvdec_ps_field){ _offset, _len })
+
+/* SPS */
+#define VIDEO_PARAMETER_SET_ID PS_FIELD(0, 4)
+#define SEQ_PARAMETER_SET_ID PS_FIELD(4, 4)
+#define CHROMA_FORMAT_IDC PS_FIELD(8, 2)
+#define PIC_WIDTH_IN_LUMA_SAMPLES PS_FIELD(10, 13)
+#define PIC_HEIGHT_IN_LUMA_SAMPLES PS_FIELD(23, 13)
+#define BIT_DEPTH_LUMA PS_FIELD(36, 4)
+#define BIT_DEPTH_CHROMA PS_FIELD(40, 4)
+#define LOG2_MAX_PIC_ORDER_CNT_LSB PS_FIELD(44, 5)
+#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(49, 2)
+#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(51, 3)
+#define LOG2_MIN_TRANSFORM_BLOCK_SIZE PS_FIELD(54, 3)
+#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE PS_FIELD(57, 2)
+#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER PS_FIELD(59, 3)
+#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA PS_FIELD(62, 3)
+#define SCALING_LIST_ENABLED_FLAG PS_FIELD(65, 1)
+#define AMP_ENABLED_FLAG PS_FIELD(66, 1)
+#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG PS_FIELD(67, 1)
+#define PCM_ENABLED_FLAG PS_FIELD(68, 1)
+#define PCM_SAMPLE_BIT_DEPTH_LUMA PS_FIELD(69, 4)
+#define PCM_SAMPLE_BIT_DEPTH_CHROMA PS_FIELD(73, 4)
+#define PCM_LOOP_FILTER_DISABLED_FLAG PS_FIELD(77, 1)
+#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(78, 3)
+#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(81, 3)
+#define NUM_SHORT_TERM_REF_PIC_SETS PS_FIELD(84, 7)
+#define LONG_TERM_REF_PICS_PRESENT_FLAG PS_FIELD(91, 1)
+#define NUM_LONG_TERM_REF_PICS_SPS PS_FIELD(92, 6)
+#define SPS_TEMPORAL_MVP_ENABLED_FLAG PS_FIELD(98, 1)
+#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG PS_FIELD(99, 1)
+/* PPS */
+#define PIC_PARAMETER_SET_ID PS_FIELD(128, 6)
+#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(134, 4)
+#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG PS_FIELD(138, 1)
+#define OUTPUT_FLAG_PRESENT_FLAG PS_FIELD(139, 1)
+#define NUM_EXTRA_SLICE_HEADER_BITS PS_FIELD(140, 13)
+#define SIGN_DATA_HIDING_ENABLED_FLAG PS_FIELD(153, 1)
+#define CABAC_INIT_PRESENT_FLAG PS_FIELD(154, 1)
+#define NUM_REF_IDX_L0_DEFAULT_ACTIVE PS_FIELD(155, 4)
+#define NUM_REF_IDX_L1_DEFAULT_ACTIVE PS_FIELD(159, 4)
+#define INIT_QP_MINUS26 PS_FIELD(163, 7)
+#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(170, 1)
+#define TRANSFORM_SKIP_ENABLED_FLAG PS_FIELD(171, 1)
+#define CU_QP_DELTA_ENABLED_FLAG PS_FIELD(172, 1)
+#define LOG2_MIN_CU_QP_DELTA_SIZE PS_FIELD(173, 3)
+#define PPS_CB_QP_OFFSET PS_FIELD(176, 5)
+#define PPS_CR_QP_OFFSET PS_FIELD(181, 5)
+#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG PS_FIELD(186, 1)
+#define WEIGHTED_PRED_FLAG PS_FIELD(187, 1)
+#define WEIGHTED_BIPRED_FLAG PS_FIELD(188, 1)
+#define TRANSQUANT_BYPASS_ENABLED_FLAG PS_FIELD(189, 1)
+#define TILES_ENABLED_FLAG PS_FIELD(190, 1)
+#define ENTROPY_CODING_SYNC_ENABLED_FLAG PS_FIELD(191, 1)
+#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG PS_FIELD(192, 1)
+#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG PS_FIELD(193, 1)
+#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG PS_FIELD(194, 1)
+#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG PS_FIELD(195, 1)
+#define PPS_BETA_OFFSET_DIV2 PS_FIELD(196, 4)
+#define PPS_TC_OFFSET_DIV2 PS_FIELD(200, 4)
+#define LISTS_MODIFICATION_PRESENT_FLAG PS_FIELD(204, 1)
+#define LOG2_PARALLEL_MERGE_LEVEL PS_FIELD(205, 3)
+#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG PS_FIELD(208, 1)
+#define NUM_TILE_COLUMNS PS_FIELD(212, 5)
+#define NUM_TILE_ROWS PS_FIELD(217, 5)
+#define COLUMN_WIDTH(i) PS_FIELD(256 + ((i) * 8), 8)
+#define ROW_HEIGHT(i) PS_FIELD(416 + ((i) * 8), 8)
+#define SCALING_LIST_ADDRESS PS_FIELD(592, 32)
+
+/* Data structure describing auxiliary buffer format. */
+struct rkvdec_hevc_priv_tbl {
+ u8 cabac_table[RKV_CABAC_TABLE_SIZE];
+ u8 scaling_list[RKV_SCALING_LIST_SIZE];
+ struct rkvdec_sps_pps_packet param_set[RKV_PPS_LEN];
+ struct rkvdec_rps_packet rps[RKV_RPS_LEN];
+};
+
+struct rkvdec_hevc_run {
+ struct rkvdec_run base;
+ const struct v4l2_ctrl_hevc_slice_params *slices_params;
+ const struct v4l2_ctrl_hevc_decode_params *decode_params;
+ const struct v4l2_ctrl_hevc_sps *sps;
+ const struct v4l2_ctrl_hevc_pps *pps;
+ const struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix;
+ int num_slices;
+};
+
+struct rkvdec_hevc_ctx {
+ struct rkvdec_aux_buf priv_tbl;
+ struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix_cache;
+};
+
+struct scaling_factor {
+ u8 scalingfactor0[1248];
+ u8 scalingfactor1[96]; /*4X4 TU Rotate, total 16X4*/
+ u8 scalingdc[12]; /*N1005 Vienna Meeting*/
+ u8 reserved[4]; /*16Bytes align*/
+};
+
+static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value)
+{
+ u8 bit = field.offset % 32, word = field.offset / 32;
+ u64 mask = GENMASK_ULL(bit + field.len - 1, bit);
+ u64 val = ((u64)value << bit) & mask;
+
+ buf[word] &= ~mask;
+ buf[word] |= val;
+ if (bit + field.len > 32) {
+ buf[word + 1] &= ~(mask >> 32);
+ buf[word + 1] |= val >> 32;
+ }
+}
+
+static void assemble_hw_pps(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+ const struct v4l2_ctrl_hevc_pps *pps = run->pps;
+ struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu;
+ struct rkvdec_sps_pps_packet *hw_ps;
+ u32 min_cb_log2_size_y, ctb_log2_size_y, ctb_size_y;
+ u32 log2_min_cu_qp_delta_size, scaling_distance;
+ dma_addr_t scaling_list_address;
+ int i;
+
+ /*
+ * HW read the SPS/PPS information from PPS packet index by PPS id.
+ * offset from the base can be calculated by PPS_id * 80 (size per PPS
+ * packet unit). so the driver copy SPS/PPS information to the exact PPS
+ * packet unit for HW accessing.
+ */
+ hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
+ memset(hw_ps, 0, sizeof(*hw_ps));
+
+#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value)
+ /* write sps */
+ WRITE_PPS(sps->video_parameter_set_id, VIDEO_PARAMETER_SET_ID);
+ WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID);
+ WRITE_PPS(sps->chroma_format_idc, CHROMA_FORMAT_IDC);
+ WRITE_PPS(sps->pic_width_in_luma_samples, PIC_WIDTH_IN_LUMA_SAMPLES);
+ WRITE_PPS(sps->pic_height_in_luma_samples, PIC_HEIGHT_IN_LUMA_SAMPLES);
+ WRITE_PPS(sps->bit_depth_luma_minus8 + 8, BIT_DEPTH_LUMA);
+ WRITE_PPS(sps->bit_depth_chroma_minus8 + 8, BIT_DEPTH_CHROMA);
+ WRITE_PPS(sps->log2_max_pic_order_cnt_lsb_minus4 + 4,
+ LOG2_MAX_PIC_ORDER_CNT_LSB);
+ WRITE_PPS(sps->log2_diff_max_min_luma_coding_block_size,
+ LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE);
+ WRITE_PPS(sps->log2_min_luma_coding_block_size_minus3 + 3,
+ LOG2_MIN_LUMA_CODING_BLOCK_SIZE);
+ WRITE_PPS(sps->log2_min_luma_transform_block_size_minus2 + 2,
+ LOG2_MIN_TRANSFORM_BLOCK_SIZE);
+ WRITE_PPS(sps->log2_diff_max_min_luma_transform_block_size,
+ LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE);
+ WRITE_PPS(sps->max_transform_hierarchy_depth_inter,
+ MAX_TRANSFORM_HIERARCHY_DEPTH_INTER);
+ WRITE_PPS(sps->max_transform_hierarchy_depth_intra,
+ MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA);
+ WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED),
+ SCALING_LIST_ENABLED_FLAG);
+ WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED),
+ AMP_ENABLED_FLAG);
+ WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET),
+ SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG);
+ if (sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED) {
+ WRITE_PPS(1, PCM_ENABLED_FLAG);
+ WRITE_PPS(sps->pcm_sample_bit_depth_luma_minus1 + 1,
+ PCM_SAMPLE_BIT_DEPTH_LUMA);
+ WRITE_PPS(sps->pcm_sample_bit_depth_chroma_minus1 + 1,
+ PCM_SAMPLE_BIT_DEPTH_CHROMA);
+ WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED),
+ PCM_LOOP_FILTER_DISABLED_FLAG);
+ WRITE_PPS(sps->log2_diff_max_min_pcm_luma_coding_block_size,
+ LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE);
+ WRITE_PPS(sps->log2_min_pcm_luma_coding_block_size_minus3 + 3,
+ LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE);
+ }
+ WRITE_PPS(sps->num_short_term_ref_pic_sets, NUM_SHORT_TERM_REF_PIC_SETS);
+ WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT),
+ LONG_TERM_REF_PICS_PRESENT_FLAG);
+ WRITE_PPS(sps->num_long_term_ref_pics_sps, NUM_LONG_TERM_REF_PICS_SPS);
+ WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED),
+ SPS_TEMPORAL_MVP_ENABLED_FLAG);
+ WRITE_PPS(!!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED),
+ STRONG_INTRA_SMOOTHING_ENABLED_FLAG);
+
+ /* write pps */
+ WRITE_PPS(pps->pic_parameter_set_id, PIC_PARAMETER_SET_ID);
+ WRITE_PPS(sps->seq_parameter_set_id, PPS_SEQ_PARAMETER_SET_ID);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED),
+ DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT),
+ OUTPUT_FLAG_PRESENT_FLAG);
+ WRITE_PPS(pps->num_extra_slice_header_bits, NUM_EXTRA_SLICE_HEADER_BITS);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED),
+ SIGN_DATA_HIDING_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT),
+ CABAC_INIT_PRESENT_FLAG);
+ WRITE_PPS(pps->num_ref_idx_l0_default_active_minus1 + 1,
+ NUM_REF_IDX_L0_DEFAULT_ACTIVE);
+ WRITE_PPS(pps->num_ref_idx_l1_default_active_minus1 + 1,
+ NUM_REF_IDX_L1_DEFAULT_ACTIVE);
+ WRITE_PPS(pps->init_qp_minus26, INIT_QP_MINUS26);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED),
+ CONSTRAINED_INTRA_PRED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED),
+ TRANSFORM_SKIP_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED),
+ CU_QP_DELTA_ENABLED_FLAG);
+
+ min_cb_log2_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3;
+ ctb_log2_size_y = min_cb_log2_size_y +
+ sps->log2_diff_max_min_luma_coding_block_size;
+ ctb_size_y = 1 << ctb_log2_size_y;
+ log2_min_cu_qp_delta_size = ctb_log2_size_y - pps->diff_cu_qp_delta_depth;
+ WRITE_PPS(log2_min_cu_qp_delta_size, LOG2_MIN_CU_QP_DELTA_SIZE);
+ WRITE_PPS(pps->pps_cb_qp_offset, PPS_CB_QP_OFFSET);
+ WRITE_PPS(pps->pps_cr_qp_offset, PPS_CR_QP_OFFSET);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT),
+ PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED),
+ WEIGHTED_PRED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED),
+ WEIGHTED_BIPRED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED),
+ TRANSQUANT_BYPASS_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED),
+ TILES_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED),
+ ENTROPY_CODING_SYNC_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED),
+ PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED),
+ LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED),
+ DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER),
+ PPS_DEBLOCKING_FILTER_DISABLED_FLAG);
+ WRITE_PPS(pps->pps_beta_offset_div2, PPS_BETA_OFFSET_DIV2);
+ WRITE_PPS(pps->pps_tc_offset_div2, PPS_TC_OFFSET_DIV2);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT),
+ LISTS_MODIFICATION_PRESENT_FLAG);
+ WRITE_PPS(pps->log2_parallel_merge_level_minus2 + 2, LOG2_PARALLEL_MERGE_LEVEL);
+ WRITE_PPS(!!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT),
+ SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG);
+ WRITE_PPS(pps->num_tile_columns_minus1 + 1, NUM_TILE_COLUMNS);
+ WRITE_PPS(pps->num_tile_rows_minus1 + 1, NUM_TILE_ROWS);
+
+ if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) {
+ /* Userspace also provide column width and row height for uniform spacing */
+ for (i = 0; i <= pps->num_tile_columns_minus1; i++)
+ WRITE_PPS(pps->column_width_minus1[i], COLUMN_WIDTH(i));
+ for (i = 0; i <= pps->num_tile_rows_minus1; i++)
+ WRITE_PPS(pps->row_height_minus1[i], ROW_HEIGHT(i));
+ } else {
+ WRITE_PPS(((sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1,
+ COLUMN_WIDTH(0));
+ WRITE_PPS(((sps->pic_height_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1,
+ ROW_HEIGHT(0));
+ }
+
+ scaling_distance = offsetof(struct rkvdec_hevc_priv_tbl, scaling_list);
+ scaling_list_address = hevc_ctx->priv_tbl.dma + scaling_distance;
+ WRITE_PPS(scaling_list_address, SCALING_LIST_ADDRESS);
+}
+
+/*
+ * Creation of the Reference Picture Set memory blob for the hardware.
+ * The layout looks like this:
+ * [0] 32 bits for L0 (6 references + 2 bits of the 7th reference)
+ * [1] 32 bits for L0 (remaining 3 bits of the 7th reference + 5 references
+ * + 4 bits of the 13th reference)
+ * [2] 11 bits for L0 (remaining bit for 13 and 2 references) and
+ * 21 bits for L1 (4 references + first bit of 5)
+ * [3] 32 bits of padding with 0s
+ * [4] 32 bits for L1 (remaining 4 bits for 5 + 5 references + 3 bits of 11)
+ * [5] 22 bits for L1 (remaining 2 bits of 11 and 4 references)
+ * lowdelay flag (bit 23), rps bit offset long term (bit 24 - 32)
+ * [6] rps bit offset long term (bit 1 - 3), rps bit offset short term (bit 4 - 12)
+ * number of references (bit 13 - 16), remaining 16 bits of padding with 0s
+ * [7] 32 bits of padding with 0s
+ *
+ * Thus we have to set up padding in between reference 5 of the L1 list.
+ */
+static void assemble_sw_rps(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+ const struct v4l2_ctrl_hevc_slice_params *sl_params;
+ const struct v4l2_hevc_dpb_entry *dpb;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_hevc_priv_tbl *priv_tbl = hevc_ctx->priv_tbl.cpu;
+ struct rkvdec_rps_packet *hw_ps;
+ int i, j;
+ unsigned int lowdelay;
+
+#define WRITE_RPS(value, field) set_ps_field(hw_ps->info, field, value)
+
+#define REF_PIC_LONG_TERM_L0(i) PS_FIELD((i) * 5, 1)
+#define REF_PIC_IDX_L0(i) PS_FIELD(1 + ((i) * 5), 4)
+#define REF_PIC_LONG_TERM_L1(i) PS_FIELD(((i) < 5 ? 75 : 132) + ((i) * 5), 1)
+#define REF_PIC_IDX_L1(i) PS_FIELD(((i) < 4 ? 76 : 128) + ((i) * 5), 4)
+
+#define LOWDELAY PS_FIELD(182, 1)
+#define LONG_TERM_RPS_BIT_OFFSET PS_FIELD(183, 10)
+#define SHORT_TERM_RPS_BIT_OFFSET PS_FIELD(193, 9)
+#define NUM_RPS_POC PS_FIELD(202, 4)
+
+ for (j = 0; j < run->num_slices; j++) {
+ uint st_bit_offset = 0;
+ uint num_l0_refs = 0;
+ uint num_l1_refs = 0;
+
+ sl_params = &run->slices_params[j];
+ dpb = decode_params->dpb;
+
+ if (sl_params->slice_type != V4L2_HEVC_SLICE_TYPE_I) {
+ num_l0_refs = sl_params->num_ref_idx_l0_active_minus1 + 1;
+
+ if (sl_params->slice_type == V4L2_HEVC_SLICE_TYPE_B)
+ num_l1_refs = sl_params->num_ref_idx_l1_active_minus1 + 1;
+
+ lowdelay = 1;
+ } else {
+ lowdelay = 0;
+ }
+
+ hw_ps = &priv_tbl->rps[j];
+ memset(hw_ps, 0, sizeof(*hw_ps));
+
+ for (i = 0; i < num_l0_refs; i++) {
+ const struct v4l2_hevc_dpb_entry dpb_l0 = dpb[sl_params->ref_idx_l0[i]];
+
+ WRITE_RPS(!!(dpb_l0.flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE),
+ REF_PIC_LONG_TERM_L0(i));
+ WRITE_RPS(sl_params->ref_idx_l0[i], REF_PIC_IDX_L0(i));
+
+ if (dpb_l0.pic_order_cnt_val > sl_params->slice_pic_order_cnt)
+ lowdelay = 0;
+ }
+
+ for (i = 0; i < num_l1_refs; i++) {
+ const struct v4l2_hevc_dpb_entry dpb_l1 = dpb[sl_params->ref_idx_l1[i]];
+ int is_long_term =
+ !!(dpb_l1.flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE);
+
+ WRITE_RPS(is_long_term, REF_PIC_LONG_TERM_L1(i));
+ WRITE_RPS(sl_params->ref_idx_l1[i], REF_PIC_IDX_L1(i));
+
+ if (dpb_l1.pic_order_cnt_val > sl_params->slice_pic_order_cnt)
+ lowdelay = 0;
+ }
+
+ WRITE_RPS(lowdelay, LOWDELAY);
+
+ if (!(decode_params->flags & V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC)) {
+ if (sl_params->short_term_ref_pic_set_size)
+ st_bit_offset = sl_params->short_term_ref_pic_set_size;
+ else if (sps->num_short_term_ref_pic_sets > 1)
+ st_bit_offset = fls(sps->num_short_term_ref_pic_sets - 1);
+ }
+
+ WRITE_RPS(st_bit_offset + sl_params->long_term_ref_pic_set_size,
+ LONG_TERM_RPS_BIT_OFFSET);
+ WRITE_RPS(sl_params->short_term_ref_pic_set_size,
+ SHORT_TERM_RPS_BIT_OFFSET);
+
+ WRITE_RPS(decode_params->num_poc_st_curr_before +
+ decode_params->num_poc_st_curr_after +
+ decode_params->num_poc_lt_curr,
+ NUM_RPS_POC);
+ }
+}
+
+/*
+ * Flip one or more matrices along their main diagonal and flatten them
+ * before writing it to the memory.
+ * Convert:
+ * ABCD AEIM
+ * EFGH => BFJN => AEIMBFJNCGKODHLP
+ * IJKL CGKO
+ * MNOP DHLP
+ */
+static void transpose_and_flatten_matrices(u8 *output, const u8 *input,
+ int matrices, int row_length)
+{
+ int i, j, row, x_offset, matrix_offset, rot_index, y_offset, matrix_size, new_value;
+
+ matrix_size = row_length * row_length;
+ for (i = 0; i < matrices; i++) {
+ row = 0;
+ x_offset = 0;
+ matrix_offset = i * matrix_size;
+ for (j = 0; j < matrix_size; j++) {
+ y_offset = j - (row * row_length);
+ rot_index = y_offset * row_length + x_offset;
+ new_value = *(input + i * matrix_size + j);
+ output[matrix_offset + rot_index] = new_value;
+ if ((j + 1) % row_length == 0) {
+ row += 1;
+ x_offset += 1;
+ }
+ }
+ }
+}
+
+static void assemble_scalingfactor0(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input)
+{
+ int offset = 0;
+
+ transpose_and_flatten_matrices(output, (const u8 *)input->scaling_list_4x4, 6, 4);
+ offset = 6 * 16 * sizeof(u8);
+ transpose_and_flatten_matrices(output + offset, (const u8 *)input->scaling_list_8x8, 6, 8);
+ offset += 6 * 64 * sizeof(u8);
+ transpose_and_flatten_matrices(output + offset,
+ (const u8 *)input->scaling_list_16x16, 6, 8);
+ offset += 6 * 64 * sizeof(u8);
+ /* Add a 128 byte padding with 0s between the two 32x32 matrices */
+ transpose_and_flatten_matrices(output + offset,
+ (const u8 *)input->scaling_list_32x32, 1, 8);
+ offset += 64 * sizeof(u8);
+ memset(output + offset, 0, 128);
+ offset += 128 * sizeof(u8);
+ transpose_and_flatten_matrices(output + offset,
+ (const u8 *)input->scaling_list_32x32 + (64 * sizeof(u8)),
+ 1, 8);
+ offset += 64 * sizeof(u8);
+ memset(output + offset, 0, 128);
+}
+
+/*
+ * Required layout:
+ * A = scaling_list_dc_coef_16x16
+ * B = scaling_list_dc_coef_32x32
+ * 0 = Padding
+ *
+ * A, A, A, A, A, A, B, 0, 0, B, 0, 0
+ */
+static void assemble_scalingdc(u8 *output, const struct v4l2_ctrl_hevc_scaling_matrix *input)
+{
+ u8 list_32x32[6] = {0};
+
+ memcpy(output, input->scaling_list_dc_coef_16x16, 6 * sizeof(u8));
+ list_32x32[0] = input->scaling_list_dc_coef_32x32[0];
+ list_32x32[3] = input->scaling_list_dc_coef_32x32[1];
+ memcpy(output + 6 * sizeof(u8), list_32x32, 6 * sizeof(u8));
+}
+
+static void translate_scaling_list(struct scaling_factor *output,
+ const struct v4l2_ctrl_hevc_scaling_matrix *input)
+{
+ assemble_scalingfactor0(output->scalingfactor0, input);
+ memcpy(output->scalingfactor1, (const u8 *)input->scaling_list_4x4, 96);
+ assemble_scalingdc(output->scalingdc, input);
+ memset(output->reserved, 0, 4 * sizeof(u8));
+}
+
+static void assemble_hw_scaling_list(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ const struct v4l2_ctrl_hevc_scaling_matrix *scaling = run->scaling_matrix;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_hevc_priv_tbl *tbl = hevc_ctx->priv_tbl.cpu;
+ u8 *dst;
+
+ if (!memcmp((void *)&hevc_ctx->scaling_matrix_cache, scaling,
+ sizeof(struct v4l2_ctrl_hevc_scaling_matrix)))
+ return;
+
+ dst = tbl->scaling_list;
+ translate_scaling_list((struct scaling_factor *)dst, scaling);
+
+ memcpy((void *)&hevc_ctx->scaling_matrix_cache, scaling,
+ sizeof(struct v4l2_ctrl_hevc_scaling_matrix));
+}
+
+static struct vb2_buffer *
+get_ref_buf(struct rkvdec_ctx *ctx, struct rkvdec_hevc_run *run,
+ unsigned int dpb_idx)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
+ const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
+ struct vb2_queue *cap_q = &m2m_ctx->cap_q_ctx.q;
+ struct vb2_buffer *buf = NULL;
+
+ if (dpb_idx < decode_params->num_active_dpb_entries)
+ buf = vb2_find_buffer(cap_q, dpb[dpb_idx].timestamp);
+
+ /*
+ * If a DPB entry is unused or invalid, the address of current destination
+ * buffer is returned.
+ */
+ if (!buf)
+ return &run->base.bufs.dst->vb2_buf;
+
+ return buf;
+}
+
+static void config_registers(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ const struct v4l2_ctrl_hevc_decode_params *decode_params = run->decode_params;
+ const struct v4l2_ctrl_hevc_sps *sps = run->sps;
+ const struct v4l2_ctrl_hevc_slice_params *sl_params = &run->slices_params[0];
+ const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ dma_addr_t priv_start_addr = hevc_ctx->priv_tbl.dma;
+ const struct v4l2_pix_format_mplane *dst_fmt;
+ struct vb2_v4l2_buffer *src_buf = run->base.bufs.src;
+ struct vb2_v4l2_buffer *dst_buf = run->base.bufs.dst;
+ const struct v4l2_format *f;
+ dma_addr_t rlc_addr;
+ dma_addr_t refer_addr;
+ u32 rlc_len;
+ u32 hor_virstride;
+ u32 ver_virstride;
+ u32 y_virstride;
+ u32 yuv_virstride = 0;
+ u32 offset;
+ dma_addr_t dst_addr;
+ u32 reg, i;
+
+ reg = RKVDEC_MODE(RKVDEC_MODE_HEVC);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_SYSCTRL);
+
+ f = &ctx->decoded_fmt;
+ dst_fmt = &f->fmt.pix_mp;
+ hor_virstride = dst_fmt->plane_fmt[0].bytesperline;
+ ver_virstride = dst_fmt->height;
+ y_virstride = hor_virstride * ver_virstride;
+
+ if (sps->chroma_format_idc == 0)
+ yuv_virstride = y_virstride;
+ else if (sps->chroma_format_idc == 1)
+ yuv_virstride = y_virstride + y_virstride / 2;
+ else if (sps->chroma_format_idc == 2)
+ yuv_virstride = 2 * y_virstride;
+
+ reg = RKVDEC_Y_HOR_VIRSTRIDE(hor_virstride / 16) |
+ RKVDEC_UV_HOR_VIRSTRIDE(hor_virstride / 16) |
+ RKVDEC_SLICE_NUM_LOWBITS(run->num_slices);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_PICPAR);
+
+ /* config rlc base address */
+ rlc_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0);
+ writel_relaxed(rlc_addr, rkvdec->regs + RKVDEC_REG_STRM_RLC_BASE);
+
+ rlc_len = vb2_get_plane_payload(&src_buf->vb2_buf, 0);
+ reg = RKVDEC_STRM_LEN(round_up(rlc_len, 16) + 64);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_STRM_LEN);
+
+ /* config cabac table */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, cabac_table);
+ writel_relaxed(priv_start_addr + offset,
+ rkvdec->regs + RKVDEC_REG_CABACTBL_PROB_BASE);
+
+ /* config output base address */
+ dst_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0);
+ writel_relaxed(dst_addr, rkvdec->regs + RKVDEC_REG_DECOUT_BASE);
+
+ reg = RKVDEC_Y_VIRSTRIDE(y_virstride / 16);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_Y_VIRSTRIDE);
+
+ reg = RKVDEC_YUV_VIRSTRIDE(yuv_virstride / 16);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_YUV_VIRSTRIDE);
+
+ /* config ref pic address */
+ for (i = 0; i < 15; i++) {
+ struct vb2_buffer *vb_buf = get_ref_buf(ctx, run, i);
+
+ if (i < 4 && decode_params->num_active_dpb_entries) {
+ reg = GENMASK(decode_params->num_active_dpb_entries - 1, 0);
+ reg = (reg >> (i * 4)) & 0xf;
+ } else {
+ reg = 0;
+ }
+
+ refer_addr = vb2_dma_contig_plane_dma_addr(vb_buf, 0);
+ writel_relaxed(refer_addr | reg,
+ rkvdec->regs + RKVDEC_REG_H264_BASE_REFER(i));
+
+ reg = RKVDEC_POC_REFER(i < decode_params->num_active_dpb_entries ?
+ dpb[i].pic_order_cnt_val : 0);
+ writel_relaxed(reg,
+ rkvdec->regs + RKVDEC_REG_H264_POC_REFER0(i));
+ }
+
+ reg = RKVDEC_CUR_POC(sl_params->slice_pic_order_cnt);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_CUR_POC0);
+
+ /* config hw pps address */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, param_set);
+ writel_relaxed(priv_start_addr + offset,
+ rkvdec->regs + RKVDEC_REG_PPS_BASE);
+
+ /* config hw rps address */
+ offset = offsetof(struct rkvdec_hevc_priv_tbl, rps);
+ writel_relaxed(priv_start_addr + offset,
+ rkvdec->regs + RKVDEC_REG_RPS_BASE);
+
+ reg = RKVDEC_AXI_DDR_RDATA(0);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_RDATA);
+
+ reg = RKVDEC_AXI_DDR_WDATA(0);
+ writel_relaxed(reg, rkvdec->regs + RKVDEC_REG_AXI_DDR_WDATA);
+}
+
+#define RKVDEC_HEVC_MAX_DEPTH_IN_BYTES 2
+
+static int rkvdec_hevc_adjust_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *fmt = &f->fmt.pix_mp;
+
+ fmt->num_planes = 1;
+ if (!fmt->plane_fmt[0].sizeimage)
+ fmt->plane_fmt[0].sizeimage = fmt->width * fmt->height *
+ RKVDEC_HEVC_MAX_DEPTH_IN_BYTES;
+ return 0;
+}
+
+static enum rkvdec_image_fmt rkvdec_hevc_get_image_fmt(struct rkvdec_ctx *ctx,
+ struct v4l2_ctrl *ctrl)
+{
+ const struct v4l2_ctrl_hevc_sps *sps = ctrl->p_new.p_hevc_sps;
+
+ if (ctrl->id != V4L2_CID_STATELESS_HEVC_SPS)
+ return RKVDEC_IMG_FMT_ANY;
+
+ if (sps->bit_depth_luma_minus8 == 0) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_8BIT;
+ else
+ return RKVDEC_IMG_FMT_420_8BIT;
+ } else if (sps->bit_depth_luma_minus8 == 2) {
+ if (sps->chroma_format_idc == 2)
+ return RKVDEC_IMG_FMT_422_10BIT;
+ else
+ return RKVDEC_IMG_FMT_420_10BIT;
+ }
+
+ return RKVDEC_IMG_FMT_ANY;
+}
+
+static int rkvdec_hevc_validate_sps(struct rkvdec_ctx *ctx,
+ const struct v4l2_ctrl_hevc_sps *sps)
+{
+ if (sps->chroma_format_idc > 1)
+ /* Only 4:0:0 and 4:2:0 are supported */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != sps->bit_depth_chroma_minus8)
+ /* Luma and chroma bit depth mismatch */
+ return -EINVAL;
+ if (sps->bit_depth_luma_minus8 != 0 && sps->bit_depth_luma_minus8 != 2)
+ /* Only 8-bit and 10-bit is supported */
+ return -EINVAL;
+
+ if (sps->pic_width_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.width ||
+ sps->pic_height_in_luma_samples > ctx->coded_fmt.fmt.pix_mp.height)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rkvdec_hevc_start(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_priv_tbl *priv_tbl;
+ struct rkvdec_hevc_ctx *hevc_ctx;
+
+ hevc_ctx = kzalloc(sizeof(*hevc_ctx), GFP_KERNEL);
+ if (!hevc_ctx)
+ return -ENOMEM;
+
+ priv_tbl = dma_alloc_coherent(rkvdec->dev, sizeof(*priv_tbl),
+ &hevc_ctx->priv_tbl.dma, GFP_KERNEL);
+ if (!priv_tbl) {
+ kfree(hevc_ctx);
+ return -ENOMEM;
+ }
+
+ hevc_ctx->priv_tbl.size = sizeof(*priv_tbl);
+ hevc_ctx->priv_tbl.cpu = priv_tbl;
+ memcpy(priv_tbl->cabac_table, rkvdec_hevc_cabac_table,
+ sizeof(rkvdec_hevc_cabac_table));
+
+ ctx->priv = hevc_ctx;
+ return 0;
+}
+
+static void rkvdec_hevc_stop(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_hevc_ctx *hevc_ctx = ctx->priv;
+ struct rkvdec_dev *rkvdec = ctx->dev;
+
+ dma_free_coherent(rkvdec->dev, hevc_ctx->priv_tbl.size,
+ hevc_ctx->priv_tbl.cpu, hevc_ctx->priv_tbl.dma);
+ kfree(hevc_ctx);
+}
+
+static void rkvdec_hevc_run_preamble(struct rkvdec_ctx *ctx,
+ struct rkvdec_hevc_run *run)
+{
+ struct v4l2_ctrl *ctrl;
+
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_DECODE_PARAMS);
+ run->decode_params = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SLICE_PARAMS);
+ run->slices_params = ctrl ? ctrl->p_cur.p : NULL;
+ run->num_slices = ctrl ? ctrl->new_elems : 0;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SPS);
+ run->sps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_PPS);
+ run->pps = ctrl ? ctrl->p_cur.p : NULL;
+ ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl,
+ V4L2_CID_STATELESS_HEVC_SCALING_MATRIX);
+ run->scaling_matrix = ctrl ? ctrl->p_cur.p : NULL;
+
+ rkvdec_run_preamble(ctx, &run->base);
+}
+
+static int rkvdec_hevc_run(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ struct rkvdec_hevc_run run;
+ u32 reg;
+
+ rkvdec_hevc_run_preamble(ctx, &run);
+
+ assemble_hw_scaling_list(ctx, &run);
+ assemble_hw_pps(ctx, &run);
+ assemble_sw_rps(ctx, &run);
+ config_registers(ctx, &run);
+
+ rkvdec_run_postamble(ctx, &run.base);
+
+ schedule_delayed_work(&rkvdec->watchdog_work, msecs_to_jiffies(2000));
+
+ writel(0, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN);
+ writel(0, rkvdec->regs + RKVDEC_REG_H264_ERR_E);
+ writel(1, rkvdec->regs + RKVDEC_REG_PREF_LUMA_CACHE_COMMAND);
+ writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
+
+ if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS)
+ rkvdec_quirks_disable_qos(ctx);
+
+ /* Start decoding! */
+ reg = (run.pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) ?
+ 0 : RKVDEC_WR_DDR_ALIGN_EN;
+ writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
+ RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E | reg,
+ rkvdec->regs + RKVDEC_REG_INTERRUPT);
+
+ return 0;
+}
+
+static int rkvdec_hevc_try_ctrl(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_STATELESS_HEVC_SPS)
+ return rkvdec_hevc_validate_sps(ctx, ctrl->p_new.p_hevc_sps);
+
+ return 0;
+}
+
+const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops = {
+ .adjust_fmt = rkvdec_hevc_adjust_fmt,
+ .start = rkvdec_hevc_start,
+ .stop = rkvdec_hevc_stop,
+ .run = rkvdec_hevc_run,
+ .try_ctrl = rkvdec_hevc_try_ctrl,
+ .get_image_fmt = rkvdec_hevc_get_image_fmt,
+};
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
index 15b9bee92016..c627b6b6f53a 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
@@ -28,6 +28,7 @@
#define RKVDEC_SOFTRST_EN_P BIT(20)
#define RKVDEC_FORCE_SOFTRESET_VALID BIT(21)
#define RKVDEC_SOFTRESET_RDY BIT(22)
+#define RKVDEC_WR_DDR_ALIGN_EN BIT(23)
#define RKVDEC_REG_SYSCTRL 0x008
#define RKVDEC_IN_ENDIAN BIT(0)
@@ -43,6 +44,7 @@
#define RKVDEC_RLC_MODE BIT(11)
#define RKVDEC_STRM_START_BIT(x) (((x) & 0x7f) << 12)
#define RKVDEC_MODE(x) (((x) & 0x03) << 20)
+#define RKVDEC_MODE_HEVC 0
#define RKVDEC_MODE_H264 1
#define RKVDEC_MODE_VP9 2
#define RKVDEC_RPS_MODE BIT(24)
@@ -217,6 +219,8 @@
#define RKVDEC_REG_H264_ERR_E 0x134
#define RKVDEC_H264_ERR_EN_HIGHBITS(x) ((x) & 0x3fffffff)
+#define RKVDEC_REG_QOS_CTRL 0x18C
+
#define RKVDEC_REG_PREF_LUMA_CACHE_COMMAND 0x410
#define RKVDEC_REG_PREF_CHR_CACHE_COMMAND 0x450
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
index 0e7e16f20eeb..b4bf01e839ef 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
@@ -824,6 +824,10 @@ static int rkvdec_vp9_run(struct rkvdec_ctx *ctx)
writel(1, rkvdec->regs + RKVDEC_REG_PREF_CHR_CACHE_COMMAND);
writel(0xe, rkvdec->regs + RKVDEC_REG_STRMD_ERR_EN);
+
+ if (rkvdec->variant->quirks & RKVDEC_QUIRK_DISABLE_QOS)
+ rkvdec_quirks_disable_qos(ctx);
+
/* Start decoding! */
writel(RKVDEC_INTERRUPT_DEC_E | RKVDEC_CONFIG_DEC_CLK_GATE_E |
RKVDEC_TIMEOUT_E | RKVDEC_BUF_EMPTY_E,
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index 6e606d73ff51..5af9aa5ab353 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -14,6 +14,7 @@
#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
@@ -158,6 +159,67 @@ static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = {
.s_ctrl = rkvdec_s_ctrl,
};
+static const struct rkvdec_ctrl_desc rkvdec_hevc_ctrl_descs[] = {
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,
+ .cfg.flags = V4L2_CTRL_FLAG_DYNAMIC_ARRAY,
+ .cfg.type = V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS,
+ .cfg.dims = { 600 },
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_SPS,
+ .cfg.ops = &rkvdec_ctrl_ops,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_PPS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
+ .cfg.min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
+ .cfg.max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
+ .cfg.def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
+ },
+ {
+ .cfg.id = V4L2_CID_STATELESS_HEVC_START_CODE,
+ .cfg.min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
+ .cfg.def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
+ .cfg.max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ .cfg.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .cfg.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
+ .cfg.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ },
+ {
+ .cfg.id = V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ .cfg.min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .cfg.max = V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1,
+ },
+};
+
+static const struct rkvdec_ctrls rkvdec_hevc_ctrls = {
+ .ctrls = rkvdec_hevc_ctrl_descs,
+ .num_ctrls = ARRAY_SIZE(rkvdec_hevc_ctrl_descs),
+};
+
+static const struct rkvdec_decoded_fmt_desc rkvdec_hevc_decoded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .image_fmt = RKVDEC_IMG_FMT_420_8BIT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV15,
+ .image_fmt = RKVDEC_IMG_FMT_420_10BIT,
+ },
+};
+
static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = {
{
.cfg.id = V4L2_CID_STATELESS_H264_DECODE_PARAMS,
@@ -253,6 +315,22 @@ static const struct rkvdec_decoded_fmt_desc rkvdec_vp9_decoded_fmts[] = {
static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
{
+ .fourcc = V4L2_PIX_FMT_HEVC_SLICE,
+ .frmsize = {
+ .min_width = 64,
+ .max_width = 4096,
+ .step_width = 64,
+ .min_height = 64,
+ .max_height = 2304,
+ .step_height = 16,
+ },
+ .ctrls = &rkvdec_hevc_ctrls,
+ .ops = &rkvdec_hevc_fmt_ops,
+ .num_decoded_fmts = ARRAY_SIZE(rkvdec_hevc_decoded_fmts),
+ .decoded_fmts = rkvdec_hevc_decoded_fmts,
+ .capability = RKVDEC_CAPABILITY_HEVC,
+ },
+ {
.fourcc = V4L2_PIX_FMT_H264_SLICE,
.frmsize = {
.min_width = 64,
@@ -267,6 +345,7 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
.num_decoded_fmts = ARRAY_SIZE(rkvdec_h264_decoded_fmts),
.decoded_fmts = rkvdec_h264_decoded_fmts,
.subsystem_flags = VB2_V4L2_FL_SUPPORTS_M2M_HOLD_CAPTURE_BUF,
+ .capability = RKVDEC_CAPABILITY_H264,
},
{
.fourcc = V4L2_PIX_FMT_VP9_FRAME,
@@ -282,16 +361,40 @@ static const struct rkvdec_coded_fmt_desc rkvdec_coded_fmts[] = {
.ops = &rkvdec_vp9_fmt_ops,
.num_decoded_fmts = ARRAY_SIZE(rkvdec_vp9_decoded_fmts),
.decoded_fmts = rkvdec_vp9_decoded_fmts,
+ .capability = RKVDEC_CAPABILITY_VP9,
}
};
+static bool rkvdec_is_capable(struct rkvdec_ctx *ctx, unsigned int capability)
+{
+ return (ctx->dev->variant->capabilities & capability) == capability;
+}
+
+static const struct rkvdec_coded_fmt_desc *
+rkvdec_enum_coded_fmt_desc(struct rkvdec_ctx *ctx, int index)
+{
+ int fmt_idx = -1;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
+ if (!rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability))
+ continue;
+ fmt_idx++;
+ if (index == fmt_idx)
+ return &rkvdec_coded_fmts[i];
+ }
+
+ return NULL;
+}
+
static const struct rkvdec_coded_fmt_desc *
-rkvdec_find_coded_fmt_desc(u32 fourcc)
+rkvdec_find_coded_fmt_desc(struct rkvdec_ctx *ctx, u32 fourcc)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
- if (rkvdec_coded_fmts[i].fourcc == fourcc)
+ if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability) &&
+ rkvdec_coded_fmts[i].fourcc == fourcc)
return &rkvdec_coded_fmts[i];
}
@@ -302,7 +405,7 @@ static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx)
{
struct v4l2_format *f = &ctx->coded_fmt;
- ctx->coded_fmt_desc = &rkvdec_coded_fmts[0];
+ ctx->coded_fmt_desc = rkvdec_enum_coded_fmt_desc(ctx, 0);
rkvdec_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc);
f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
@@ -316,21 +419,22 @@ static void rkvdec_reset_coded_fmt(struct rkvdec_ctx *ctx)
static int rkvdec_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
- const struct rkvdec_coded_fmt_desc *fmt;
+ struct rkvdec_ctx *ctx = file_to_rkvdec_ctx(file);
+ const struct rkvdec_coded_fmt_desc *desc;
if (fsize->index != 0)
return -EINVAL;
- fmt = rkvdec_find_coded_fmt_desc(fsize->pixel_format);
- if (!fmt)
+ desc = rkvdec_find_coded_fmt_desc(ctx, fsize->pixel_format);
+ if (!desc)
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
fsize->stepwise.min_width = 1;
- fsize->stepwise.max_width = fmt->frmsize.max_width;
+ fsize->stepwise.max_width = desc->frmsize.max_width;
fsize->stepwise.step_width = 1;
fsize->stepwise.min_height = 1;
- fsize->stepwise.max_height = fmt->frmsize.max_height;
+ fsize->stepwise.max_height = desc->frmsize.max_height;
fsize->stepwise.step_height = 1;
return 0;
@@ -390,10 +494,10 @@ static int rkvdec_try_output_fmt(struct file *file, void *priv,
struct rkvdec_ctx *ctx = file_to_rkvdec_ctx(file);
const struct rkvdec_coded_fmt_desc *desc;
- desc = rkvdec_find_coded_fmt_desc(pix_mp->pixelformat);
+ desc = rkvdec_find_coded_fmt_desc(ctx, pix_mp->pixelformat);
if (!desc) {
- pix_mp->pixelformat = rkvdec_coded_fmts[0].fourcc;
- desc = &rkvdec_coded_fmts[0];
+ desc = rkvdec_enum_coded_fmt_desc(ctx, 0);
+ pix_mp->pixelformat = desc->fourcc;
}
v4l2_apply_frmsize_constraints(&pix_mp->width,
@@ -470,7 +574,7 @@ static int rkvdec_s_output_fmt(struct file *file, void *priv,
if (ret)
return ret;
- desc = rkvdec_find_coded_fmt_desc(f->fmt.pix_mp.pixelformat);
+ desc = rkvdec_find_coded_fmt_desc(ctx, f->fmt.pix_mp.pixelformat);
if (!desc)
return -EINVAL;
ctx->coded_fmt_desc = desc;
@@ -522,10 +626,14 @@ static int rkvdec_g_capture_fmt(struct file *file, void *priv,
static int rkvdec_enum_output_fmt(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- if (f->index >= ARRAY_SIZE(rkvdec_coded_fmts))
+ struct rkvdec_ctx *ctx = file_to_rkvdec_ctx(file);
+ const struct rkvdec_coded_fmt_desc *desc;
+
+ desc = rkvdec_enum_coded_fmt_desc(ctx, f->index);
+ if (!desc)
return -EINVAL;
- f->pixelformat = rkvdec_coded_fmts[f->index].fourcc;
+ f->pixelformat = desc->fourcc;
return 0;
}
@@ -783,7 +891,7 @@ void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run)
if (src_req)
v4l2_ctrl_request_setup(src_req, &ctx->ctrl_hdl);
- v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst, true);
+ v4l2_m2m_buf_copy_metadata(run->bufs.src, run->bufs.dst);
}
void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run)
@@ -794,6 +902,18 @@ void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run)
v4l2_ctrl_request_complete(src_req, &ctx->ctrl_hdl);
}
+void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx)
+{
+ struct rkvdec_dev *rkvdec = ctx->dev;
+ u32 reg;
+
+ /* Set undocumented swreg_block_gating_e field */
+ reg = readl(rkvdec->regs + RKVDEC_REG_QOS_CTRL);
+ reg &= GENMASK(31, 16);
+ reg |= 0xEFFF;
+ writel(reg, rkvdec->regs + RKVDEC_REG_QOS_CTRL);
+}
+
static void rkvdec_device_run(void *priv)
{
struct rkvdec_ctx *ctx = priv;
@@ -889,14 +1009,17 @@ static int rkvdec_init_ctrls(struct rkvdec_ctx *ctx)
int ret;
for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++)
- nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls;
+ if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability))
+ nctrls += rkvdec_coded_fmts[i].ctrls->num_ctrls;
v4l2_ctrl_handler_init(&ctx->ctrl_hdl, nctrls);
for (i = 0; i < ARRAY_SIZE(rkvdec_coded_fmts); i++) {
- ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls);
- if (ret)
- goto err_free_handler;
+ if (rkvdec_is_capable(ctx, rkvdec_coded_fmts[i].capability)) {
+ ret = rkvdec_add_ctrls(ctx, rkvdec_coded_fmts[i].ctrls);
+ if (ret)
+ goto err_free_handler;
+ }
}
ret = v4l2_ctrl_handler_setup(&ctx->ctrl_hdl);
@@ -1109,8 +1232,39 @@ static void rkvdec_watchdog_func(struct work_struct *work)
}
}
+static const struct rkvdec_variant rk3288_rkvdec_variant = {
+ .num_regs = 68,
+ .capabilities = RKVDEC_CAPABILITY_HEVC,
+};
+
+static const struct rkvdec_variant rk3328_rkvdec_variant = {
+ .num_regs = 109,
+ .capabilities = RKVDEC_CAPABILITY_HEVC |
+ RKVDEC_CAPABILITY_H264 |
+ RKVDEC_CAPABILITY_VP9,
+ .quirks = RKVDEC_QUIRK_DISABLE_QOS,
+};
+
+static const struct rkvdec_variant rk3399_rkvdec_variant = {
+ .num_regs = 78,
+ .capabilities = RKVDEC_CAPABILITY_HEVC |
+ RKVDEC_CAPABILITY_H264 |
+ RKVDEC_CAPABILITY_VP9,
+};
+
static const struct of_device_id of_rkvdec_match[] = {
- { .compatible = "rockchip,rk3399-vdec" },
+ {
+ .compatible = "rockchip,rk3288-vdec",
+ .data = &rk3288_rkvdec_variant,
+ },
+ {
+ .compatible = "rockchip,rk3328-vdec",
+ .data = &rk3328_rkvdec_variant,
+ },
+ {
+ .compatible = "rockchip,rk3399-vdec",
+ .data = &rk3399_rkvdec_variant,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_rkvdec_match);
@@ -1121,16 +1275,22 @@ static const char * const rkvdec_clk_names[] = {
static int rkvdec_probe(struct platform_device *pdev)
{
+ const struct rkvdec_variant *variant;
struct rkvdec_dev *rkvdec;
unsigned int i;
int ret, irq;
+ variant = of_device_get_match_data(&pdev->dev);
+ if (!variant)
+ return -EINVAL;
+
rkvdec = devm_kzalloc(&pdev->dev, sizeof(*rkvdec), GFP_KERNEL);
if (!rkvdec)
return -ENOMEM;
platform_set_drvdata(pdev, rkvdec);
rkvdec->dev = &pdev->dev;
+ rkvdec->variant = variant;
mutex_init(&rkvdec->vdev_lock);
INIT_DELAYED_WORK(&rkvdec->watchdog_work, rkvdec_watchdog_func);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
index 481aaa4bffe9..566e06fa2b1e 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -22,6 +22,12 @@
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
+#define RKVDEC_CAPABILITY_HEVC BIT(0)
+#define RKVDEC_CAPABILITY_H264 BIT(1)
+#define RKVDEC_CAPABILITY_VP9 BIT(2)
+
+#define RKVDEC_QUIRK_DISABLE_QOS BIT(0)
+
struct rkvdec_ctx;
struct rkvdec_ctrl_desc {
@@ -63,6 +69,12 @@ vb2_to_rkvdec_decoded_buf(struct vb2_buffer *buf)
base.vb.vb2_buf);
}
+struct rkvdec_variant {
+ unsigned int num_regs;
+ unsigned int capabilities;
+ unsigned int quirks;
+};
+
struct rkvdec_coded_fmt_ops {
int (*adjust_fmt)(struct rkvdec_ctx *ctx,
struct v4l2_format *f);
@@ -98,6 +110,7 @@ struct rkvdec_coded_fmt_desc {
unsigned int num_decoded_fmts;
const struct rkvdec_decoded_fmt_desc *decoded_fmts;
u32 subsystem_flags;
+ unsigned int capability;
};
struct rkvdec_dev {
@@ -111,6 +124,7 @@ struct rkvdec_dev {
struct mutex vdev_lock; /* serializes ioctls */
struct delayed_work watchdog_work;
struct iommu_domain *empty_domain;
+ const struct rkvdec_variant *variant;
};
struct rkvdec_ctx {
@@ -138,7 +152,10 @@ struct rkvdec_aux_buf {
void rkvdec_run_preamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
void rkvdec_run_postamble(struct rkvdec_ctx *ctx, struct rkvdec_run *run);
+void rkvdec_quirks_disable_qos(struct rkvdec_ctx *ctx);
+
extern const struct rkvdec_coded_fmt_ops rkvdec_h264_fmt_ops;
+extern const struct rkvdec_coded_fmt_ops rkvdec_hevc_fmt_ops;
extern const struct rkvdec_coded_fmt_ops rkvdec_vp9_fmt_ops;
#endif /* RKVDEC_H_ */
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
index ed7b7ca16f71..0827fdaf455a 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
@@ -996,7 +996,6 @@ static void fimc_is_module_exit(void)
module_init(fimc_is_module_init);
module_exit(fimc_is_module_exit);
-MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME);
MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
MODULE_DESCRIPTION("Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver");
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
index 0ce293b0718b..8be20fd32d1c 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
@@ -1662,4 +1662,3 @@ static struct platform_driver fimc_lite_driver = {
module_platform_driver(fimc_lite_driver);
MODULE_DESCRIPTION("Samsung EXYNOS FIMC-LITE (camera host interface) driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME);
diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c
index c781853586fd..bc7087eb761a 100644
--- a/drivers/media/platform/samsung/exynos4-is/media-dev.c
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c
@@ -1333,8 +1333,8 @@ static int fimc_md_register_clk_provider(struct fimc_md *fmd)
cp->clks[i] = clk_register(NULL, &camclk->hw);
if (IS_ERR(cp->clks[i])) {
- dev_err(dev, "failed to register clock: %s (%ld)\n",
- init.name, PTR_ERR(cp->clks[i]));
+ dev_err(dev, "failed to register clock: %s (%pe)\n",
+ init.name, cp->clks[i]);
ret = PTR_ERR(cp->clks[i]);
goto err;
}
@@ -1399,12 +1399,14 @@ static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
mutex_lock(&fmd->media_dev.graph_mutex);
ret = fimc_md_create_links(fmd);
- if (ret < 0)
- goto unlock;
+ if (ret < 0) {
+ mutex_unlock(&fmd->media_dev.graph_mutex);
+ return ret;
+ }
- ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
-unlock:
mutex_unlock(&fmd->media_dev.graph_mutex);
+
+ ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/samsung/s5p-g2d/g2d.c b/drivers/media/platform/samsung/s5p-g2d/g2d.c
index ffb9bee6cb9d..e765dfcc2830 100644
--- a/drivers/media/platform/samsung/s5p-g2d/g2d.c
+++ b/drivers/media/platform/samsung/s5p-g2d/g2d.c
@@ -308,12 +308,8 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct g2d_ctx *ctx = file2ctx(file);
- struct vb2_queue *vq;
struct g2d_frame *frm;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
frm = get_frame(ctx, f->type);
if (IS_ERR(frm))
return PTR_ERR(frm);
diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
index 81792f7f8b16..ff28482759ec 100644
--- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
@@ -1332,15 +1332,10 @@ static struct s5p_jpeg_q_data *get_q_data(struct s5p_jpeg_ctx *ctx,
static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct s5p_jpeg_q_data *q_data = NULL;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct s5p_jpeg_ctx *ct = file_to_ctx(file);
- vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
ct->mode == S5P_JPEG_DECODE && !ct->hdr_parsed)
return -EINVAL;
@@ -1593,8 +1588,6 @@ static int s5p_jpeg_s_fmt(struct s5p_jpeg_ctx *ct, struct v4l2_format *f)
unsigned int f_type;
vq = v4l2_m2m_get_vq(ct->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = get_q_data(ct, f->type);
BUG_ON(q_data == NULL);
diff --git a/drivers/media/platform/st/Makefile b/drivers/media/platform/st/Makefile
index a1f75b2a8225..615a93d62662 100644
--- a/drivers/media/platform/st/Makefile
+++ b/drivers/media/platform/st/Makefile
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += sti/bdisp/
-obj-y += sti/c8sectpfe/
obj-y += sti/delta/
obj-y += sti/hva/
obj-y += stm32/
diff --git a/drivers/media/platform/st/sti/Kconfig b/drivers/media/platform/st/sti/Kconfig
index 60068e8b47b8..91ca0950ff73 100644
--- a/drivers/media/platform/st/sti/Kconfig
+++ b/drivers/media/platform/st/sti/Kconfig
@@ -1,5 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
source "drivers/media/platform/st/sti/bdisp/Kconfig"
-source "drivers/media/platform/st/sti/c8sectpfe/Kconfig"
source "drivers/media/platform/st/sti/delta/Kconfig"
source "drivers/media/platform/st/sti/hva/Kconfig"
diff --git a/drivers/media/platform/st/sti/Makefile b/drivers/media/platform/st/sti/Makefile
index f9ce8169b040..3328d50fb6cf 100644
--- a/drivers/media/platform/st/sti/Makefile
+++ b/drivers/media/platform/st/sti/Makefile
@@ -1,6 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += bdisp/
-obj-y += c8sectpfe/
obj-y += delta/
obj-y += hva/
obj-y += stm32/
diff --git a/drivers/media/platform/st/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig
deleted file mode 100644
index 01c33d9c9ec3..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/Kconfig
+++ /dev/null
@@ -1,28 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-config DVB_C8SECTPFE
- tristate "STMicroelectronics C8SECTPFE DVB support"
- depends on DVB_PLATFORM_DRIVERS
- depends on PINCTRL && DVB_CORE && I2C
- depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST
- select FW_LOADER
- select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
- select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
- select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
- select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
- select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
- select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
- select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
-
- help
- This adds support for DVB front-end cards connected
- to TS inputs of STiH407/410 SoC.
-
- The driver currently supports C8SECTPFE's TS input block,
- memdma engine, and HW PID filtering.
-
- Supported DVB front-end cards are:
- - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212)
- - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board
-
- To compile this driver as a module, choose M here: the
- module will be called c8sectpfe.
diff --git a/drivers/media/platform/st/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile
deleted file mode 100644
index 99425137ee0a..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o
-
-ifneq ($(CONFIG_DEBUG_FS),)
-c8sectpfe-y += c8sectpfe-debugfs.o
-endif
-
-obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
-
-ccflags-y += -I $(srctree)/drivers/media/dvb-frontends/
-ccflags-y += -I $(srctree)/drivers/media/tuners/
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c
deleted file mode 100644
index 5df67da25525..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c
+++ /dev/null
@@ -1,262 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * c8sectpfe-common.c - C8SECTPFE STi DVB driver
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Author: Peter Griffin <peter.griffin@linaro.org>
- *
- */
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dvb/dmx.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/wait.h>
-
-#include <media/dmxdev.h>
-#include <media/dvbdev.h>
-#include <media/dvb_demux.h>
-#include <media/dvb_frontend.h>
-#include <media/dvb_net.h>
-
-#include "c8sectpfe-common.h"
-#include "c8sectpfe-core.h"
-#include "c8sectpfe-dvb.h"
-
-static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap,
- void *start_feed, void *stop_feed,
- struct c8sectpfei *fei)
-{
- int result;
-
- demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING |
- DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING;
-
- demux->dvb_demux.priv = demux;
- demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL;
- demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL;
-
- demux->dvb_demux.start_feed = start_feed;
- demux->dvb_demux.stop_feed = stop_feed;
- demux->dvb_demux.write_to_decoder = NULL;
-
- result = dvb_dmx_init(&demux->dvb_demux);
- if (result < 0) {
- dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n",
- result);
- goto err_dmx;
- }
-
- demux->dmxdev.filternum = demux->dvb_demux.filternum;
- demux->dmxdev.demux = &demux->dvb_demux.dmx;
- demux->dmxdev.capabilities = 0;
-
- result = dvb_dmxdev_init(&demux->dmxdev, adap);
- if (result < 0) {
- dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n",
- result);
-
- goto err_dmxdev;
- }
-
- demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index;
-
- result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
- &demux->hw_frontend);
- if (result < 0) {
- dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result);
- goto err_fe_hw;
- }
-
- demux->mem_frontend.source = DMX_MEMORY_FE;
- result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
- &demux->mem_frontend);
- if (result < 0) {
- dev_err(fei->dev, "add_frontend failed (%d)\n", result);
- goto err_fe_mem;
- }
-
- result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx,
- &demux->hw_frontend);
- if (result < 0) {
- dev_err(fei->dev, "connect_frontend (%d)\n", result);
- goto err_fe_con;
- }
-
- return 0;
-
-err_fe_con:
- demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
- &demux->mem_frontend);
-err_fe_mem:
- demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
- &demux->hw_frontend);
-err_fe_hw:
- dvb_dmxdev_release(&demux->dmxdev);
-err_dmxdev:
- dvb_dmx_release(&demux->dvb_demux);
-err_dmx:
- return result;
-
-}
-
-static void unregister_dvb(struct stdemux *demux)
-{
-
- demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
- &demux->mem_frontend);
-
- demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
- &demux->hw_frontend);
-
- dvb_dmxdev_release(&demux->dmxdev);
-
- dvb_dmx_release(&demux->dvb_demux);
-}
-
-static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei,
- void *start_feed,
- void *stop_feed)
-{
- struct c8sectpfe *c8sectpfe;
- int result;
- int i, j;
-
- short int ids[] = { -1 };
-
- c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL);
- if (!c8sectpfe)
- goto err1;
-
- mutex_init(&c8sectpfe->lock);
-
- c8sectpfe->device = fei->dev;
-
- result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe",
- THIS_MODULE, fei->dev, ids);
- if (result < 0) {
- dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n",
- result);
- goto err2;
- }
-
- c8sectpfe->adapter.priv = fei;
-
- for (i = 0; i < fei->tsin_count; i++) {
-
- c8sectpfe->demux[i].tsin_index = i;
- c8sectpfe->demux[i].c8sectpfei = fei;
-
- result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter,
- start_feed, stop_feed, fei);
- if (result < 0) {
- dev_err(fei->dev,
- "register_dvb feed=%d failed (errno = %d)\n",
- result, i);
-
- /* we take a all or nothing approach */
- for (j = 0; j < i; j++)
- unregister_dvb(&c8sectpfe->demux[j]);
- goto err3;
- }
- }
-
- c8sectpfe->num_feeds = fei->tsin_count;
-
- return c8sectpfe;
-err3:
- dvb_unregister_adapter(&c8sectpfe->adapter);
-err2:
- kfree(c8sectpfe);
-err1:
- return NULL;
-};
-
-static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe)
-{
- int i;
-
- if (!c8sectpfe)
- return;
-
- for (i = 0; i < c8sectpfe->num_feeds; i++)
- unregister_dvb(&c8sectpfe->demux[i]);
-
- dvb_unregister_adapter(&c8sectpfe->adapter);
-
- kfree(c8sectpfe);
-};
-
-void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
- struct c8sectpfei *fei)
-{
- int n;
- struct channel_info *tsin;
-
- for (n = 0; n < fei->tsin_count; n++) {
-
- tsin = fei->channel_data[n];
-
- if (tsin) {
- if (tsin->frontend) {
- dvb_unregister_frontend(tsin->frontend);
- dvb_frontend_detach(tsin->frontend);
- }
-
- i2c_put_adapter(tsin->i2c_adapter);
-
- if (tsin->i2c_client) {
- module_put(tsin->i2c_client->dev.driver->owner);
- i2c_unregister_device(tsin->i2c_client);
- }
- }
- }
-
- c8sectpfe_delete(c8sectpfe);
-};
-
-int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,
- struct c8sectpfei *fei,
- void *start_feed,
- void *stop_feed)
-{
- struct channel_info *tsin;
- struct dvb_frontend *frontend;
- int n, res;
-
- *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed);
- if (!*c8sectpfe)
- return -ENOMEM;
-
- for (n = 0; n < fei->tsin_count; n++) {
- tsin = fei->channel_data[n];
-
- res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n);
- if (res)
- goto err;
-
- res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend);
- if (res < 0) {
- dev_err(fei->dev, "dvb_register_frontend failed (%d)\n",
- res);
- goto err;
- }
-
- tsin->frontend = frontend;
- }
-
- return 0;
-
-err:
- c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei);
- return res;
-}
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h
deleted file mode 100644
index f8d97841f366..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * c8sectpfe-common.h - C8SECTPFE STi DVB driver
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Author: Peter Griffin <peter.griffin@linaro.org>
- *
- */
-#ifndef _C8SECTPFE_COMMON_H_
-#define _C8SECTPFE_COMMON_H_
-
-#include <linux/dvb/dmx.h>
-#include <linux/dvb/frontend.h>
-#include <linux/gpio.h>
-
-#include <media/dmxdev.h>
-#include <media/dvb_demux.h>
-#include <media/dvb_frontend.h>
-#include <media/dvb_net.h>
-
-/* Maximum number of channels */
-#define C8SECTPFE_MAXADAPTER (4)
-#define C8SECTPFE_MAXCHANNEL 64
-#define STPTI_MAXCHANNEL 64
-
-#define MAX_INPUTBLOCKS 7
-
-struct c8sectpfe;
-struct stdemux;
-
-struct stdemux {
- struct dvb_demux dvb_demux;
- struct dmxdev dmxdev;
- struct dmx_frontend hw_frontend;
- struct dmx_frontend mem_frontend;
- int tsin_index;
- int running_feed_count;
- struct c8sectpfei *c8sectpfei;
-};
-
-struct c8sectpfe {
- struct stdemux demux[MAX_INPUTBLOCKS];
- struct mutex lock;
- struct dvb_adapter adapter;
- struct device *device;
- int mapping;
- int num_feeds;
-};
-
-/* Channel registration */
-int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,
- struct c8sectpfei *fei,
- void *start_feed,
- void *stop_feed);
-
-void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
- struct c8sectpfei *fei);
-
-#endif
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
deleted file mode 100644
index 89bd15a4d26a..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
+++ /dev/null
@@ -1,1158 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * c8sectpfe-core.c - C8SECTPFE STi DVB driver
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Author:Peter Bennett <peter.bennett@st.com>
- * Peter Griffin <peter.griffin@linaro.org>
- *
- */
-#include <linux/atomic.h>
-#include <linux/clk.h>
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/dvb/dmx.h>
-#include <linux/dvb/frontend.h>
-#include <linux/err.h>
-#include <linux/errno.h>
-#include <linux/firmware.h>
-#include <linux/gpio/consumer.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of_platform.h>
-#include <linux/pinctrl/consumer.h>
-#include <linux/pinctrl/pinctrl.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/usb.h>
-#include <linux/wait.h>
-
-#include "c8sectpfe-common.h"
-#include "c8sectpfe-core.h"
-#include "c8sectpfe-debugfs.h"
-
-#include <media/dmxdev.h>
-#include <media/dvb_demux.h>
-#include <media/dvb_frontend.h>
-#include <media/dvb_net.h>
-
-#define FIRMWARE_MEMDMA "pti_memdma_h407.elf"
-MODULE_FIRMWARE(FIRMWARE_MEMDMA);
-
-#define PID_TABLE_SIZE 1024
-#define POLL_MSECS 50
-
-static int load_c8sectpfe_fw(struct c8sectpfei *fei);
-
-#define TS_PKT_SIZE 188
-#define HEADER_SIZE (4)
-#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE)
-
-#define FEI_ALIGNMENT (32)
-/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */
-#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340)
-
-#define FIFO_LEN 1024
-
-static void c8sectpfe_timer_interrupt(struct timer_list *t)
-{
- struct c8sectpfei *fei = timer_container_of(fei, t, timer);
- struct channel_info *channel;
- int chan_num;
-
- /* iterate through input block channels */
- for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) {
- channel = fei->channel_data[chan_num];
-
- /* is this descriptor initialised and TP enabled */
- if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE))
- queue_work(system_bh_wq, &channel->bh_work);
- }
-
- fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS);
- add_timer(&fei->timer);
-}
-
-static void channel_swdemux_bh_work(struct work_struct *t)
-{
- struct channel_info *channel = from_work(channel, t, bh_work);
- struct c8sectpfei *fei;
- unsigned long wp, rp;
- int pos, num_packets, n, size;
- u8 *buf;
-
- if (unlikely(!channel || !channel->irec))
- return;
-
- fei = channel->fei;
-
- wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0));
- rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0));
-
- pos = rp - channel->back_buffer_busaddr;
-
- /* has it wrapped */
- if (wp < rp)
- wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE;
-
- size = wp - rp;
- num_packets = size / PACKET_SIZE;
-
- /* manage cache so data is visible to CPU */
- dma_sync_single_for_cpu(fei->dev,
- rp,
- size,
- DMA_FROM_DEVICE);
-
- buf = channel->back_buffer_aligned;
-
- dev_dbg(fei->dev,
- "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\trp=0x%lx, wp=0x%lx\n",
- channel->tsin_id, channel, num_packets, buf, pos, rp, wp);
-
- for (n = 0; n < num_packets; n++) {
- dvb_dmx_swfilter_packets(
- &fei->c8sectpfe[0]->
- demux[channel->demux_mapping].dvb_demux,
- &buf[pos], 1);
-
- pos += PACKET_SIZE;
- }
-
- /* advance the read pointer */
- if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE))
- writel(channel->back_buffer_busaddr, channel->irec +
- DMA_PRDS_BUSRP_TP(0));
- else
- writel(wp, channel->irec + DMA_PRDS_BUSRP_TP(0));
-}
-
-static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
- struct dvb_demux *demux = dvbdmxfeed->demux;
- struct stdemux *stdemux = demux->priv;
- struct c8sectpfei *fei = stdemux->c8sectpfei;
- struct channel_info *channel;
- u32 tmp;
- unsigned long *bitmap;
- int ret;
-
- switch (dvbdmxfeed->type) {
- case DMX_TYPE_TS:
- break;
- case DMX_TYPE_SEC:
- break;
- default:
- dev_err(fei->dev, "%s:%d Error bailing\n"
- , __func__, __LINE__);
- return -EINVAL;
- }
-
- if (dvbdmxfeed->type == DMX_TYPE_TS) {
- switch (dvbdmxfeed->pes_type) {
- case DMX_PES_VIDEO:
- case DMX_PES_AUDIO:
- case DMX_PES_TELETEXT:
- case DMX_PES_PCR:
- case DMX_PES_OTHER:
- break;
- default:
- dev_err(fei->dev, "%s:%d Error bailing\n"
- , __func__, __LINE__);
- return -EINVAL;
- }
- }
-
- if (!atomic_read(&fei->fw_loaded)) {
- ret = load_c8sectpfe_fw(fei);
- if (ret)
- return ret;
- }
-
- mutex_lock(&fei->lock);
-
- channel = fei->channel_data[stdemux->tsin_index];
-
- bitmap = channel->pid_buffer_aligned;
-
- /* 8192 is a special PID */
- if (dvbdmxfeed->pid == 8192) {
- tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
- tmp &= ~C8SECTPFE_PID_ENABLE;
- writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
-
- } else {
- bitmap_set(bitmap, dvbdmxfeed->pid, 1);
- }
-
- /* manage cache so PID bitmap is visible to HW */
- dma_sync_single_for_device(fei->dev,
- channel->pid_buffer_busaddr,
- PID_TABLE_SIZE,
- DMA_TO_DEVICE);
-
- channel->active = 1;
-
- if (fei->global_feed_count == 0) {
- fei->timer.expires = jiffies +
- msecs_to_jiffies(msecs_to_jiffies(POLL_MSECS));
-
- add_timer(&fei->timer);
- }
-
- if (stdemux->running_feed_count == 0) {
-
- dev_dbg(fei->dev, "Starting channel=%p\n", channel);
-
- INIT_WORK(&channel->bh_work, channel_swdemux_bh_work);
-
- /* Reset the internal inputblock sram pointers */
- writel(channel->fifo,
- fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id));
- writel(channel->fifo + FIFO_LEN - 1,
- fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id));
-
- writel(channel->fifo,
- fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id));
- writel(channel->fifo,
- fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id));
-
-
- /* reset read / write memdma ptrs for this channel */
- writel(channel->back_buffer_busaddr, channel->irec +
- DMA_PRDS_BUSBASE_TP(0));
-
- tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
- writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0));
-
- writel(channel->back_buffer_busaddr, channel->irec +
- DMA_PRDS_BUSWP_TP(0));
-
- /* Issue a reset and enable InputBlock */
- writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET
- , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
-
- /* and enable the tp */
- writel(0x1, channel->irec + DMA_PRDS_TPENABLE);
-
- dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n"
- , __func__, __LINE__, stdemux);
- }
-
- stdemux->running_feed_count++;
- fei->global_feed_count++;
-
- mutex_unlock(&fei->lock);
-
- return 0;
-}
-
-static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
-{
-
- struct dvb_demux *demux = dvbdmxfeed->demux;
- struct stdemux *stdemux = demux->priv;
- struct c8sectpfei *fei = stdemux->c8sectpfei;
- struct channel_info *channel;
- int idlereq;
- u32 tmp;
- int ret;
- unsigned long *bitmap;
-
- if (!atomic_read(&fei->fw_loaded)) {
- ret = load_c8sectpfe_fw(fei);
- if (ret)
- return ret;
- }
-
- mutex_lock(&fei->lock);
-
- channel = fei->channel_data[stdemux->tsin_index];
-
- bitmap = channel->pid_buffer_aligned;
-
- if (dvbdmxfeed->pid == 8192) {
- tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
- tmp |= C8SECTPFE_PID_ENABLE;
- writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
- } else {
- bitmap_clear(bitmap, dvbdmxfeed->pid, 1);
- }
-
- /* manage cache so data is visible to HW */
- dma_sync_single_for_device(fei->dev,
- channel->pid_buffer_busaddr,
- PID_TABLE_SIZE,
- DMA_TO_DEVICE);
-
- if (--stdemux->running_feed_count == 0) {
-
- channel = fei->channel_data[stdemux->tsin_index];
-
- /* TP re-configuration on page 168 of functional spec */
-
- /* disable IB (prevents more TS data going to memdma) */
- writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
-
- /* disable this channels descriptor */
- writel(0, channel->irec + DMA_PRDS_TPENABLE);
-
- disable_work_sync(&channel->bh_work);
-
- /* now request memdma channel goes idle */
- idlereq = (1 << channel->tsin_id) | IDLEREQ;
- writel(idlereq, fei->io + DMA_IDLE_REQ);
-
- /* wait for idle irq handler to signal completion */
- ret = wait_for_completion_timeout(&channel->idle_completion,
- msecs_to_jiffies(100));
-
- if (ret == 0)
- dev_warn(fei->dev,
- "Timeout waiting for idle irq on tsin%d\n",
- channel->tsin_id);
-
- reinit_completion(&channel->idle_completion);
-
- /* reset read / write ptrs for this channel */
-
- writel(channel->back_buffer_busaddr,
- channel->irec + DMA_PRDS_BUSBASE_TP(0));
-
- tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
- writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0));
-
- writel(channel->back_buffer_busaddr,
- channel->irec + DMA_PRDS_BUSWP_TP(0));
-
- dev_dbg(fei->dev,
- "%s:%d stopping DMA feed on stdemux=%p channel=%d\n",
- __func__, __LINE__, stdemux, channel->tsin_id);
-
- /* turn off all PIDS in the bitmap */
- memset(channel->pid_buffer_aligned, 0, PID_TABLE_SIZE);
-
- /* manage cache so data is visible to HW */
- dma_sync_single_for_device(fei->dev,
- channel->pid_buffer_busaddr,
- PID_TABLE_SIZE,
- DMA_TO_DEVICE);
-
- channel->active = 0;
- }
-
- if (--fei->global_feed_count == 0) {
- dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n"
- , __func__, __LINE__, fei->global_feed_count);
-
- timer_delete(&fei->timer);
- }
-
- mutex_unlock(&fei->lock);
-
- return 0;
-}
-
-static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num)
-{
- int i;
-
- for (i = 0; i < C8SECTPFE_MAX_TSIN_CHAN; i++) {
- if (!fei->channel_data[i])
- continue;
-
- if (fei->channel_data[i]->tsin_id == tsin_num)
- return fei->channel_data[i];
- }
-
- return NULL;
-}
-
-static void c8sectpfe_getconfig(struct c8sectpfei *fei)
-{
- struct c8sectpfe_hw *hw = &fei->hw_stats;
-
- hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB);
- hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB);
- hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS);
- hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT);
- hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC);
- hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM);
- hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP);
-
- dev_info(fei->dev, "C8SECTPFE hw supports the following:\n");
- dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib);
- dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib);
- dev_info(fei->dev, "Software Transport Stream Inputs: %d\n"
- , hw->num_swts);
- dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout);
- dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc);
- dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram);
- dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n"
- , hw->num_tp);
-}
-
-static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv)
-{
- struct c8sectpfei *fei = priv;
- struct channel_info *chan;
- int bit;
- unsigned long tmp = readl(fei->io + DMA_IDLE_REQ);
-
- /* page 168 of functional spec: Clear the idle request
- by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */
-
- /* signal idle completion */
- for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) {
-
- chan = find_channel(fei, bit);
-
- if (chan)
- complete(&chan->idle_completion);
- }
-
- writel(0, fei->io + DMA_IDLE_REQ);
-
- return IRQ_HANDLED;
-}
-
-
-static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin)
-{
- if (!fei || !tsin)
- return;
-
- if (tsin->back_buffer_busaddr)
- if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr))
- dma_unmap_single(fei->dev, tsin->back_buffer_busaddr,
- FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL);
-
- kfree(tsin->back_buffer_start);
-
- if (tsin->pid_buffer_busaddr)
- if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr))
- dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr,
- PID_TABLE_SIZE, DMA_BIDIRECTIONAL);
-
- kfree(tsin->pid_buffer_start);
-}
-
-#define MAX_NAME 20
-
-static int configure_memdma_and_inputblock(struct c8sectpfei *fei,
- struct channel_info *tsin)
-{
- int ret;
- u32 tmp;
- char tsin_pin_name[MAX_NAME];
-
- if (!fei || !tsin)
- return -EINVAL;
-
- dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n"
- , __func__, __LINE__, tsin, tsin->tsin_id);
-
- init_completion(&tsin->idle_completion);
-
- tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE + FEI_ALIGNMENT, GFP_KERNEL);
- if (!tsin->back_buffer_start) {
- ret = -ENOMEM;
- goto err_unmap;
- }
-
- /* Ensure backbuffer is 32byte aligned */
- tsin->back_buffer_aligned = tsin->back_buffer_start + FEI_ALIGNMENT;
-
- tsin->back_buffer_aligned = PTR_ALIGN(tsin->back_buffer_aligned, FEI_ALIGNMENT);
-
- tsin->back_buffer_busaddr = dma_map_single(fei->dev,
- tsin->back_buffer_aligned,
- FEI_BUFFER_SIZE,
- DMA_BIDIRECTIONAL);
-
- if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) {
- dev_err(fei->dev, "failed to map back_buffer\n");
- ret = -EFAULT;
- goto err_unmap;
- }
-
- /*
- * The pid buffer can be configured (in hw) for byte or bit
- * per pid. By powers of deduction we conclude stih407 family
- * is configured (at SoC design stage) for bit per pid.
- */
- tsin->pid_buffer_start = kzalloc(PID_TABLE_SIZE + PID_TABLE_SIZE, GFP_KERNEL);
- if (!tsin->pid_buffer_start) {
- ret = -ENOMEM;
- goto err_unmap;
- }
-
- /*
- * PID buffer needs to be aligned to size of the pid table
- * which at bit per pid is 1024 bytes (8192 pids / 8).
- * PIDF_BASE register enforces this alignment when writing
- * the register.
- */
-
- tsin->pid_buffer_aligned = tsin->pid_buffer_start + PID_TABLE_SIZE;
-
- tsin->pid_buffer_aligned = PTR_ALIGN(tsin->pid_buffer_aligned, PID_TABLE_SIZE);
-
- tsin->pid_buffer_busaddr = dma_map_single(fei->dev,
- tsin->pid_buffer_aligned,
- PID_TABLE_SIZE,
- DMA_BIDIRECTIONAL);
-
- if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) {
- dev_err(fei->dev, "failed to map pid_bitmap\n");
- ret = -EFAULT;
- goto err_unmap;
- }
-
- /* manage cache so pid bitmap is visible to HW */
- dma_sync_single_for_device(fei->dev,
- tsin->pid_buffer_busaddr,
- PID_TABLE_SIZE,
- DMA_TO_DEVICE);
-
- snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id,
- (tsin->serial_not_parallel ? "serial" : "parallel"));
-
- tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name);
- if (IS_ERR(tsin->pstate)) {
- dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n"
- , __func__, tsin_pin_name);
- ret = PTR_ERR(tsin->pstate);
- goto err_unmap;
- }
-
- ret = pinctrl_select_state(fei->pinctrl, tsin->pstate);
-
- if (ret) {
- dev_err(fei->dev, "%s: pinctrl_select_state failed\n"
- , __func__);
- goto err_unmap;
- }
-
- /* Enable this input block */
- tmp = readl(fei->io + SYS_INPUT_CLKEN);
- tmp |= BIT(tsin->tsin_id);
- writel(tmp, fei->io + SYS_INPUT_CLKEN);
-
- if (tsin->serial_not_parallel)
- tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL;
-
- if (tsin->invert_ts_clk)
- tmp |= C8SECTPFE_INVERT_TSCLK;
-
- if (tsin->async_not_sync)
- tmp |= C8SECTPFE_ASYNC_NOT_SYNC;
-
- tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB;
-
- writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id));
-
- writel(C8SECTPFE_SYNC(0x9) |
- C8SECTPFE_DROP(0x9) |
- C8SECTPFE_TOKEN(0x47),
- fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id));
-
- writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id));
-
- /* Place the FIFO's at the end of the irec descriptors */
-
- tsin->fifo = (tsin->tsin_id * FIFO_LEN);
-
- writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id));
- writel(tsin->fifo + FIFO_LEN - 1,
- fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id));
-
- writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id));
- writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id));
-
- writel(tsin->pid_buffer_busaddr,
- fei->io + PIDF_BASE(tsin->tsin_id));
-
- dev_dbg(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n",
- tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)),
- &tsin->pid_buffer_busaddr);
-
- /* Configure and enable HW PID filtering */
-
- /*
- * The PID value is created by assembling the first 8 bytes of
- * the TS packet into a 64-bit word in big-endian format. A
- * slice of that 64-bit word is taken from
- * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET.
- */
- tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13)
- | C8SECTPFE_PID_OFFSET(40));
-
- writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id));
-
- dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n",
- tsin->tsin_id,
- readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)),
- readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)),
- readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)),
- readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)));
-
- /* Get base addpress of pointer record block from DMEM */
- tsin->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET +
- readl(fei->io + DMA_PTRREC_BASE);
-
- /* fill out pointer record data structure */
-
- /* advance pointer record block to our channel */
- tsin->irec += (tsin->tsin_id * DMA_PRDS_SIZE);
-
- writel(tsin->fifo, tsin->irec + DMA_PRDS_MEMBASE);
-
- writel(tsin->fifo + FIFO_LEN - 1, tsin->irec + DMA_PRDS_MEMTOP);
-
- writel((188 + 7)&~7, tsin->irec + DMA_PRDS_PKTSIZE);
-
- writel(0x1, tsin->irec + DMA_PRDS_TPENABLE);
-
- /* read/write pointers with physical bus address */
-
- writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSBASE_TP(0));
-
- tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
- writel(tmp, tsin->irec + DMA_PRDS_BUSTOP_TP(0));
-
- writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0));
- writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0));
-
- /* initialize bh work */
- INIT_WORK(&tsin->bh_work, channel_swdemux_bh_work);
-
- return 0;
-
-err_unmap:
- free_input_block(fei, tsin);
- return ret;
-}
-
-static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
-{
- struct c8sectpfei *fei = priv;
-
- dev_err(fei->dev, "%s: error handling not yet implemented\n"
- , __func__);
-
- /*
- * TODO FIXME we should detect some error conditions here
- * and ideally do something about them!
- */
-
- return IRQ_HANDLED;
-}
-
-static int c8sectpfe_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct c8sectpfei *fei;
- struct resource *res;
- int ret, index = 0;
- struct channel_info *tsin;
-
- /* Allocate the c8sectpfei structure */
- fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL);
- if (!fei)
- return -ENOMEM;
-
- fei->dev = dev;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe");
- fei->io = devm_ioremap_resource(dev, res);
- if (IS_ERR(fei->io))
- return PTR_ERR(fei->io);
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
- "c8sectpfe-ram");
- fei->sram = devm_ioremap_resource(dev, res);
- if (IS_ERR(fei->sram))
- return PTR_ERR(fei->sram);
-
- fei->sram_size = resource_size(res);
-
- fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq");
- if (fei->idle_irq < 0)
- return fei->idle_irq;
-
- fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq");
- if (fei->error_irq < 0)
- return fei->error_irq;
-
- platform_set_drvdata(pdev, fei);
-
- fei->c8sectpfeclk = devm_clk_get_enabled(dev, "c8sectpfe");
- if (IS_ERR(fei->c8sectpfeclk)) {
- dev_err(dev, "Failed to enable c8sectpfe clock\n");
- return PTR_ERR(fei->c8sectpfeclk);
- }
-
- /* to save power disable all IP's (on by default) */
- writel(0, fei->io + SYS_INPUT_CLKEN);
-
- /* Enable memdma clock */
- writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN);
-
- /* clear internal sram */
- memset_io(fei->sram, 0x0, fei->sram_size);
-
- c8sectpfe_getconfig(fei);
-
- ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler,
- 0, "c8sectpfe-idle-irq", fei);
- if (ret) {
- dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n");
- return ret;
- }
-
- ret = devm_request_irq(dev, fei->error_irq,
- c8sectpfe_error_irq_handler, 0,
- "c8sectpfe-error-irq", fei);
- if (ret) {
- dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n");
- return ret;
- }
-
- fei->tsin_count = of_get_child_count(np);
-
- if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN ||
- fei->tsin_count > fei->hw_stats.num_ib) {
-
- dev_err(dev, "More tsin declared than exist on SoC!\n");
- return -EINVAL;
- }
-
- fei->pinctrl = devm_pinctrl_get(dev);
-
- if (IS_ERR(fei->pinctrl)) {
- dev_err(dev, "Error getting tsin pins\n");
- return PTR_ERR(fei->pinctrl);
- }
-
- for_each_child_of_node_scoped(np, child) {
- struct device_node *i2c_bus;
-
- fei->channel_data[index] = devm_kzalloc(dev,
- sizeof(struct channel_info),
- GFP_KERNEL);
-
- if (!fei->channel_data[index])
- return -ENOMEM;
-
- tsin = fei->channel_data[index];
-
- tsin->fei = fei;
-
- ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id);
- if (ret) {
- dev_err(&pdev->dev, "No tsin_num found\n");
- return ret;
- }
-
- /* sanity check value */
- if (tsin->tsin_id > fei->hw_stats.num_ib) {
- dev_err(&pdev->dev,
- "tsin-num %d specified greater than number\n\tof input block hw in SoC! (%d)",
- tsin->tsin_id, fei->hw_stats.num_ib);
- return -EINVAL;
- }
-
- tsin->invert_ts_clk = of_property_read_bool(child,
- "invert-ts-clk");
-
- tsin->serial_not_parallel = of_property_read_bool(child,
- "serial-not-parallel");
-
- tsin->async_not_sync = of_property_read_bool(child,
- "async-not-sync");
-
- ret = of_property_read_u32(child, "dvb-card",
- &tsin->dvb_card);
- if (ret) {
- dev_err(&pdev->dev, "No dvb-card found\n");
- return ret;
- }
-
- i2c_bus = of_parse_phandle(child, "i2c-bus", 0);
- if (!i2c_bus) {
- dev_err(&pdev->dev, "No i2c-bus found\n");
- return -ENODEV;
- }
- tsin->i2c_adapter =
- of_find_i2c_adapter_by_node(i2c_bus);
- of_node_put(i2c_bus);
- if (!tsin->i2c_adapter) {
- dev_err(&pdev->dev, "No i2c adapter found\n");
- return -ENODEV;
- }
-
- /* Acquire reset GPIO and activate it */
- tsin->rst_gpio = devm_fwnode_gpiod_get(dev,
- of_fwnode_handle(child),
- "reset", GPIOD_OUT_HIGH,
- "NIM reset");
- ret = PTR_ERR_OR_ZERO(tsin->rst_gpio);
- if (ret && ret != -EBUSY) {
- dev_err(dev, "Can't request tsin%d reset gpio\n",
- fei->channel_data[index]->tsin_id);
- return ret;
- }
-
- if (!ret) {
- /* wait for the chip to reset */
- usleep_range(3500, 5000);
- /* release the reset line */
- gpiod_set_value_cansleep(tsin->rst_gpio, 0);
- usleep_range(3000, 5000);
- }
-
- tsin->demux_mapping = index;
-
- dev_dbg(fei->dev,
- "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\tserial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n",
- fei->channel_data[index], index,
- tsin->tsin_id, tsin->invert_ts_clk,
- tsin->serial_not_parallel, tsin->async_not_sync,
- tsin->dvb_card);
-
- index++;
- }
-
- /* Setup timer interrupt */
- timer_setup(&fei->timer, c8sectpfe_timer_interrupt, 0);
-
- mutex_init(&fei->lock);
-
- /* Get the configuration information about the tuners */
- ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0],
- (void *)fei,
- c8sectpfe_start_feed,
- c8sectpfe_stop_feed);
- if (ret) {
- dev_err(dev, "c8sectpfe_tuner_register_frontend failed (%d)\n",
- ret);
- return ret;
- }
-
- c8sectpfe_debugfs_init(fei);
-
- return 0;
-}
-
-static void c8sectpfe_remove(struct platform_device *pdev)
-{
- struct c8sectpfei *fei = platform_get_drvdata(pdev);
- struct channel_info *channel;
- int i;
-
- wait_for_completion(&fei->fw_ack);
-
- c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei);
-
- /*
- * Now loop through and un-configure each of the InputBlock resources
- */
- for (i = 0; i < fei->tsin_count; i++) {
- channel = fei->channel_data[i];
- free_input_block(fei, channel);
- }
-
- c8sectpfe_debugfs_exit(fei);
-
- dev_info(fei->dev, "Stopping memdma SLIM core\n");
- if (readl(fei->io + DMA_CPU_RUN))
- writel(0x0, fei->io + DMA_CPU_RUN);
-
- /* unclock all internal IP's */
- if (readl(fei->io + SYS_INPUT_CLKEN))
- writel(0, fei->io + SYS_INPUT_CLKEN);
-
- if (readl(fei->io + SYS_OTHER_CLKEN))
- writel(0, fei->io + SYS_OTHER_CLKEN);
-}
-
-
-static int configure_channels(struct c8sectpfei *fei)
-{
- int index = 0, ret;
- struct device_node *np = fei->dev->of_node;
-
- /* iterate round each tsin and configure memdma descriptor and IB hw */
- for_each_child_of_node_scoped(np, child) {
- ret = configure_memdma_and_inputblock(fei,
- fei->channel_data[index]);
- if (ret) {
- dev_err(fei->dev,
- "configure_memdma_and_inputblock failed\n");
- goto err_unmap;
- }
- index++;
- }
-
- return 0;
-
-err_unmap:
- while (--index >= 0)
- free_input_block(fei, fei->channel_data[index]);
-
- return ret;
-}
-
-static int
-c8sectpfe_elf_sanity_check(struct c8sectpfei *fei, const struct firmware *fw)
-{
- struct elf32_hdr *ehdr;
- char class;
-
- if (!fw) {
- dev_err(fei->dev, "failed to load %s\n", FIRMWARE_MEMDMA);
- return -EINVAL;
- }
-
- if (fw->size < sizeof(struct elf32_hdr)) {
- dev_err(fei->dev, "Image is too small\n");
- return -EINVAL;
- }
-
- ehdr = (struct elf32_hdr *)fw->data;
-
- /* We only support ELF32 at this point */
- class = ehdr->e_ident[EI_CLASS];
- if (class != ELFCLASS32) {
- dev_err(fei->dev, "Unsupported class: %d\n", class);
- return -EINVAL;
- }
-
- if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
- dev_err(fei->dev, "Unsupported firmware endianness\n");
- return -EINVAL;
- }
-
- if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
- dev_err(fei->dev, "Image is too small\n");
- return -EINVAL;
- }
-
- if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
- dev_err(fei->dev, "Image is corrupted (bad magic)\n");
- return -EINVAL;
- }
-
- /* Check ELF magic */
- ehdr = (Elf32_Ehdr *)fw->data;
- if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
- ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
- ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
- ehdr->e_ident[EI_MAG3] != ELFMAG3) {
- dev_err(fei->dev, "Invalid ELF magic\n");
- return -EINVAL;
- }
-
- if (ehdr->e_type != ET_EXEC) {
- dev_err(fei->dev, "Unsupported ELF header type\n");
- return -EINVAL;
- }
-
- if (ehdr->e_phoff > fw->size) {
- dev_err(fei->dev, "Firmware size is too small\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
-
-static void load_imem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr,
- const struct firmware *fw, u8 __iomem *dest,
- int seg_num)
-{
- const u8 *imem_src = fw->data + phdr->p_offset;
- int i;
-
- /*
- * For IMEM segments, the segment contains 24-bit
- * instructions which must be padded to 32-bit
- * instructions before being written. The written
- * segment is padded with NOP instructions.
- */
-
- dev_dbg(fei->dev,
- "Loading IMEM segment %d 0x%08x\n\t (0x%x bytes) -> 0x%p (0x%x bytes)\n",
- seg_num, phdr->p_paddr, phdr->p_filesz, dest,
- phdr->p_memsz + phdr->p_memsz / 3);
-
- for (i = 0; i < phdr->p_filesz; i++) {
-
- writeb(readb((void __iomem *)imem_src), (void __iomem *)dest);
-
- /* Every 3 bytes, add an additional
- * padding zero in destination */
- if (i % 3 == 2) {
- dest++;
- writeb(0x00, (void __iomem *)dest);
- }
-
- dest++;
- imem_src++;
- }
-}
-
-static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr,
- const struct firmware *fw, u8 __iomem *dst, int seg_num)
-{
- /*
- * For DMEM segments copy the segment data from the ELF
- * file and pad segment with zeroes
- */
-
- dev_dbg(fei->dev,
- "Loading DMEM segment %d 0x%08x\n\t(0x%x bytes) -> 0x%p (0x%x bytes)\n",
- seg_num, phdr->p_paddr, phdr->p_filesz,
- dst, phdr->p_memsz);
-
- memcpy((void __force *)dst, (void *)fw->data + phdr->p_offset,
- phdr->p_filesz);
-
- memset((void __force *)dst + phdr->p_filesz, 0,
- phdr->p_memsz - phdr->p_filesz);
-}
-
-static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei)
-{
- Elf32_Ehdr *ehdr;
- Elf32_Phdr *phdr;
- u8 __iomem *dst;
- int err = 0, i;
-
- if (!fw || !fei)
- return -EINVAL;
-
- ehdr = (Elf32_Ehdr *)fw->data;
- phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff);
-
- /* go through the available ELF segments */
- for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
-
- /* Only consider LOAD segments */
- if (phdr->p_type != PT_LOAD)
- continue;
-
- /*
- * Check segment is contained within the fw->data buffer
- */
- if (phdr->p_offset + phdr->p_filesz > fw->size) {
- dev_err(fei->dev,
- "Segment %d is outside of firmware file\n", i);
- err = -EINVAL;
- break;
- }
-
- /*
- * MEMDMA IMEM has executable flag set, otherwise load
- * this segment into DMEM.
- *
- */
-
- if (phdr->p_flags & PF_X) {
- dst = (u8 __iomem *) fei->io + DMA_MEMDMA_IMEM;
- /*
- * The Slim ELF file uses 32-bit word addressing for
- * load offsets.
- */
- dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int);
- load_imem_segment(fei, phdr, fw, dst, i);
- } else {
- dst = (u8 __iomem *) fei->io + DMA_MEMDMA_DMEM;
- /*
- * The Slim ELF file uses 32-bit word addressing for
- * load offsets.
- */
- dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int);
- load_dmem_segment(fei, phdr, fw, dst, i);
- }
- }
-
- return err;
-}
-
-static int load_c8sectpfe_fw(struct c8sectpfei *fei)
-{
- const struct firmware *fw;
- int err;
-
- dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
-
- err = request_firmware(&fw, FIRMWARE_MEMDMA, fei->dev);
- if (err)
- return err;
-
- err = c8sectpfe_elf_sanity_check(fei, fw);
- if (err) {
- dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n"
- , err);
- release_firmware(fw);
- return err;
- }
-
- err = load_slim_core_fw(fw, fei);
- release_firmware(fw);
- if (err) {
- dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err);
- return err;
- }
-
- /* now the firmware is loaded configure the input blocks */
- err = configure_channels(fei);
- if (err) {
- dev_err(fei->dev, "configure_channels failed err=(%d)\n", err);
- return err;
- }
-
- /*
- * STBus target port can access IMEM and DMEM ports
- * without waiting for CPU
- */
- writel(0x1, fei->io + DMA_PER_STBUS_SYNC);
-
- dev_info(fei->dev, "Boot the memdma SLIM core\n");
- writel(0x1, fei->io + DMA_CPU_RUN);
-
- atomic_set(&fei->fw_loaded, 1);
-
- return 0;
-}
-
-static const struct of_device_id c8sectpfe_match[] = {
- { .compatible = "st,stih407-c8sectpfe" },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, c8sectpfe_match);
-
-static struct platform_driver c8sectpfe_driver = {
- .driver = {
- .name = "c8sectpfe",
- .of_match_table = c8sectpfe_match,
- },
- .probe = c8sectpfe_probe,
- .remove = c8sectpfe_remove,
-};
-
-module_platform_driver(c8sectpfe_driver);
-
-MODULE_AUTHOR("Peter Bennett <peter.bennett@st.com>");
-MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
-MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h
deleted file mode 100644
index c1b124c6ef12..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h
+++ /dev/null
@@ -1,287 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * c8sectpfe-core.h - C8SECTPFE STi DVB driver
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Author:Peter Bennett <peter.bennett@st.com>
- * Peter Griffin <peter.griffin@linaro.org>
- *
- */
-#ifndef _C8SECTPFE_CORE_H_
-#define _C8SECTPFE_CORE_H_
-
-#define C8SECTPFEI_MAXCHANNEL 16
-#define C8SECTPFEI_MAXADAPTER 3
-
-#define C8SECTPFE_MAX_TSIN_CHAN 8
-
-struct gpio_desc;
-
-struct channel_info {
-
- int tsin_id;
- bool invert_ts_clk;
- bool serial_not_parallel;
- bool async_not_sync;
- int i2c;
- int dvb_card;
-
- struct gpio_desc *rst_gpio;
-
- struct i2c_adapter *i2c_adapter;
- struct i2c_adapter *tuner_i2c;
- struct i2c_adapter *lnb_i2c;
- struct i2c_client *i2c_client;
- struct dvb_frontend *frontend;
-
- struct pinctrl_state *pstate;
-
- int demux_mapping;
- int active;
-
- void *back_buffer_start;
- void *back_buffer_aligned;
- dma_addr_t back_buffer_busaddr;
-
- void *pid_buffer_start;
- void *pid_buffer_aligned;
- dma_addr_t pid_buffer_busaddr;
-
- unsigned long fifo;
-
- struct completion idle_completion;
- struct work_struct bh_work;
-
- struct c8sectpfei *fei;
- void __iomem *irec;
-
-};
-
-struct c8sectpfe_hw {
- int num_ib;
- int num_mib;
- int num_swts;
- int num_tsout;
- int num_ccsc;
- int num_ram;
- int num_tp;
-};
-
-struct c8sectpfei {
-
- struct device *dev;
- struct pinctrl *pinctrl;
-
- struct dentry *root;
- struct debugfs_regset32 *regset;
- struct completion fw_ack;
- atomic_t fw_loaded;
-
- int tsin_count;
-
- struct c8sectpfe_hw hw_stats;
-
- struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER];
-
- int mapping[C8SECTPFEI_MAXCHANNEL];
-
- struct mutex lock;
-
- struct timer_list timer; /* timer interrupts for outputs */
-
- void __iomem *io;
- void __iomem *sram;
-
- unsigned long sram_size;
-
- struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN];
-
- struct clk *c8sectpfeclk;
- int nima_rst_gpio;
- int nimb_rst_gpio;
-
- int idle_irq;
- int error_irq;
-
- int global_feed_count;
-};
-
-/* C8SECTPFE SYS Regs list */
-
-#define SYS_INPUT_ERR_STATUS 0x0
-#define SYS_OTHER_ERR_STATUS 0x8
-#define SYS_INPUT_ERR_MASK 0x10
-#define SYS_OTHER_ERR_MASK 0x18
-#define SYS_DMA_ROUTE 0x20
-#define SYS_INPUT_CLKEN 0x30
-#define IBENABLE_MASK 0x7F
-
-#define SYS_OTHER_CLKEN 0x38
-#define TSDMAENABLE BIT(1)
-#define MEMDMAENABLE BIT(0)
-
-#define SYS_CFG_NUM_IB 0x200
-#define SYS_CFG_NUM_MIB 0x204
-#define SYS_CFG_NUM_SWTS 0x208
-#define SYS_CFG_NUM_TSOUT 0x20C
-#define SYS_CFG_NUM_CCSC 0x210
-#define SYS_CFG_NUM_RAM 0x214
-#define SYS_CFG_NUM_TP 0x218
-
-/* Input Block Regs */
-
-#define C8SECTPFE_INPUTBLK_OFFSET 0x1000
-#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET)
-
-#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00)
-#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7)
-#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6)
-#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5)
-#define C8SECTPFE_INVERT_TSCLK BIT(4)
-#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3)
-#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2)
-#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1)
-#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0)
-
-#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04)
-#define C8SECTPFE_SYNC(x) (x & 0xf)
-#define C8SECTPFE_DROP(x) ((x<<4) & 0xf)
-#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00)
-#define C8SECTPFE_SLDENDIANNESS BIT(16)
-
-#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08)
-#define C8SECTPFE_TAG_HEADER(x) (x << 16)
-#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff)
-#define C8SECTPFE_TAG_ENABLE BIT(0)
-
-#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C)
-#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f)
-#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff)
-#define C8SECTPFE_PID_ENABLE BIT(31)
-
-#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10)
-
-#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14)
-#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18)
-#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C)
-#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20)
-
-#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24)
-#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff)
-#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24)
-#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28)
-
-#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28)
-#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1)
-#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2)
-#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4)
-#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8)
-#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10)
-#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf)
-#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf)
-
-#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C)
-#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0)
-#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1)
-#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2)
-#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3)
-#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4)
-#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8)
-#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12)
-
-#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30)
-#define C8SECTPFE_SYS_RESET BIT(1)
-#define C8SECTPFE_SYS_ENABLE BIT(0)
-
-/*
- * Pointer record data structure required for each input block
- * see Table 82 on page 167 of functional specification.
- */
-
-#define DMA_PRDS_MEMBASE 0x0 /* Internal sram base address */
-#define DMA_PRDS_MEMTOP 0x4 /* Internal sram top address */
-
-/*
- * TS packet size, including tag bytes added by input block,
- * rounded up to the next multiple of 8 bytes. The packet size,
- * including any tagging bytes and rounded up to the nearest
- * multiple of 8 bytes must be less than 255 bytes.
- */
-#define DMA_PRDS_PKTSIZE 0x8
-#define DMA_PRDS_TPENABLE 0xc
-
-#define TP0_OFFSET 0x10
-#define DMA_PRDS_BUSBASE_TP(x) ((0x10*x) + TP0_OFFSET)
-#define DMA_PRDS_BUSTOP_TP(x) ((0x10*x) + TP0_OFFSET + 0x4)
-#define DMA_PRDS_BUSWP_TP(x) ((0x10*x) + TP0_OFFSET + 0x8)
-#define DMA_PRDS_BUSRP_TP(x) ((0x10*x) + TP0_OFFSET + 0xc)
-
-#define DMA_PRDS_SIZE (0x20)
-
-#define DMA_MEMDMA_OFFSET 0x4000
-#define DMA_IMEM_OFFSET 0x0
-#define DMA_DMEM_OFFSET 0x4000
-#define DMA_CPU 0x8000
-#define DMA_PER_OFFSET 0xb000
-
-#define DMA_MEMDMA_DMEM (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET)
-#define DMA_MEMDMA_IMEM (DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET)
-
-/* XP70 Slim core regs */
-#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0)
-#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4)
-#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8)
-#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc)
-#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20)
-
-/* Enable Interrupt for a IB */
-#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00)
-/* Ack interrupt by setting corresponding bit */
-#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80)
-#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00)
-#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80)
-#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80)
-#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88)
-#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c)
-#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90)
-#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8)
-#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac)
-#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0)
-#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4)
-#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc)
-#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0)
-#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8)
-#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0)
-#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8)
-#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0)
-#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4)
-#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8)
-#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec)
-#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0)
-#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4)
-#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8)
-#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc)
-/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */
-
-/* The following are from DMA_DMEM_BaseAddress */
-#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0)
-#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4)
-#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8)
-#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc)
-#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4)
-#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10)
-#define IDLEREQ BIT(31)
-
-#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14)
-
-/* Regs for PID Filter */
-
-#define PIDF_OFFSET 0x2800
-#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET)
-#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100)
-#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108)
-#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110)
-#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114)
-
-#endif /* _C8SECTPFE_CORE_H_ */
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c
deleted file mode 100644
index 301fa10f419b..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c
+++ /dev/null
@@ -1,244 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * c8sectpfe-debugfs.c - C8SECTPFE STi DVB driver
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Author: Peter Griffin <peter.griffin@linaro.org>
- *
- */
-#include <linux/debugfs.h>
-#include <linux/device.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#include "c8sectpfe-debugfs.h"
-
-#define dump_register(nm ...) \
-{ \
- .name = #nm, \
- .offset = nm, \
-}
-
-static const struct debugfs_reg32 fei_sys_regs[] = {
- dump_register(SYS_INPUT_ERR_STATUS),
- dump_register(SYS_OTHER_ERR_STATUS),
- dump_register(SYS_INPUT_ERR_MASK),
- dump_register(SYS_DMA_ROUTE),
- dump_register(SYS_INPUT_CLKEN),
- dump_register(IBENABLE_MASK),
- dump_register(SYS_OTHER_CLKEN),
- dump_register(SYS_CFG_NUM_IB),
- dump_register(SYS_CFG_NUM_MIB),
- dump_register(SYS_CFG_NUM_SWTS),
- dump_register(SYS_CFG_NUM_TSOUT),
- dump_register(SYS_CFG_NUM_CCSC),
- dump_register(SYS_CFG_NUM_RAM),
- dump_register(SYS_CFG_NUM_TP),
-
- dump_register(C8SECTPFE_IB_IP_FMT_CFG(0)),
- dump_register(C8SECTPFE_IB_TAGBYTES_CFG(0)),
- dump_register(C8SECTPFE_IB_PID_SET(0)),
- dump_register(C8SECTPFE_IB_PKT_LEN(0)),
- dump_register(C8SECTPFE_IB_BUFF_STRT(0)),
- dump_register(C8SECTPFE_IB_BUFF_END(0)),
- dump_register(C8SECTPFE_IB_READ_PNT(0)),
- dump_register(C8SECTPFE_IB_WRT_PNT(0)),
- dump_register(C8SECTPFE_IB_PRI_THRLD(0)),
- dump_register(C8SECTPFE_IB_STAT(0)),
- dump_register(C8SECTPFE_IB_MASK(0)),
- dump_register(C8SECTPFE_IB_SYS(0)),
-
- dump_register(C8SECTPFE_IB_IP_FMT_CFG(1)),
- dump_register(C8SECTPFE_IB_TAGBYTES_CFG(1)),
- dump_register(C8SECTPFE_IB_PID_SET(1)),
- dump_register(C8SECTPFE_IB_PKT_LEN(1)),
- dump_register(C8SECTPFE_IB_BUFF_STRT(1)),
- dump_register(C8SECTPFE_IB_BUFF_END(1)),
- dump_register(C8SECTPFE_IB_READ_PNT(1)),
- dump_register(C8SECTPFE_IB_WRT_PNT(1)),
- dump_register(C8SECTPFE_IB_PRI_THRLD(1)),
- dump_register(C8SECTPFE_IB_STAT(1)),
- dump_register(C8SECTPFE_IB_MASK(1)),
- dump_register(C8SECTPFE_IB_SYS(1)),
-
- dump_register(C8SECTPFE_IB_IP_FMT_CFG(2)),
- dump_register(C8SECTPFE_IB_TAGBYTES_CFG(2)),
- dump_register(C8SECTPFE_IB_PID_SET(2)),
- dump_register(C8SECTPFE_IB_PKT_LEN(2)),
- dump_register(C8SECTPFE_IB_BUFF_STRT(2)),
- dump_register(C8SECTPFE_IB_BUFF_END(2)),
- dump_register(C8SECTPFE_IB_READ_PNT(2)),
- dump_register(C8SECTPFE_IB_WRT_PNT(2)),
- dump_register(C8SECTPFE_IB_PRI_THRLD(2)),
- dump_register(C8SECTPFE_IB_STAT(2)),
- dump_register(C8SECTPFE_IB_MASK(2)),
- dump_register(C8SECTPFE_IB_SYS(2)),
-
- dump_register(C8SECTPFE_IB_IP_FMT_CFG(3)),
- dump_register(C8SECTPFE_IB_TAGBYTES_CFG(3)),
- dump_register(C8SECTPFE_IB_PID_SET(3)),
- dump_register(C8SECTPFE_IB_PKT_LEN(3)),
- dump_register(C8SECTPFE_IB_BUFF_STRT(3)),
- dump_register(C8SECTPFE_IB_BUFF_END(3)),
- dump_register(C8SECTPFE_IB_READ_PNT(3)),
- dump_register(C8SECTPFE_IB_WRT_PNT(3)),
- dump_register(C8SECTPFE_IB_PRI_THRLD(3)),
- dump_register(C8SECTPFE_IB_STAT(3)),
- dump_register(C8SECTPFE_IB_MASK(3)),
- dump_register(C8SECTPFE_IB_SYS(3)),
-
- dump_register(C8SECTPFE_IB_IP_FMT_CFG(4)),
- dump_register(C8SECTPFE_IB_TAGBYTES_CFG(4)),
- dump_register(C8SECTPFE_IB_PID_SET(4)),
- dump_register(C8SECTPFE_IB_PKT_LEN(4)),
- dump_register(C8SECTPFE_IB_BUFF_STRT(4)),
- dump_register(C8SECTPFE_IB_BUFF_END(4)),
- dump_register(C8SECTPFE_IB_READ_PNT(4)),
- dump_register(C8SECTPFE_IB_WRT_PNT(4)),
- dump_register(C8SECTPFE_IB_PRI_THRLD(4)),
- dump_register(C8SECTPFE_IB_STAT(4)),
- dump_register(C8SECTPFE_IB_MASK(4)),
- dump_register(C8SECTPFE_IB_SYS(4)),
-
- dump_register(C8SECTPFE_IB_IP_FMT_CFG(5)),
- dump_register(C8SECTPFE_IB_TAGBYTES_CFG(5)),
- dump_register(C8SECTPFE_IB_PID_SET(5)),
- dump_register(C8SECTPFE_IB_PKT_LEN(5)),
- dump_register(C8SECTPFE_IB_BUFF_STRT(5)),
- dump_register(C8SECTPFE_IB_BUFF_END(5)),
- dump_register(C8SECTPFE_IB_READ_PNT(5)),
- dump_register(C8SECTPFE_IB_WRT_PNT(5)),
- dump_register(C8SECTPFE_IB_PRI_THRLD(5)),
- dump_register(C8SECTPFE_IB_STAT(5)),
- dump_register(C8SECTPFE_IB_MASK(5)),
- dump_register(C8SECTPFE_IB_SYS(5)),
-
- dump_register(C8SECTPFE_IB_IP_FMT_CFG(6)),
- dump_register(C8SECTPFE_IB_TAGBYTES_CFG(6)),
- dump_register(C8SECTPFE_IB_PID_SET(6)),
- dump_register(C8SECTPFE_IB_PKT_LEN(6)),
- dump_register(C8SECTPFE_IB_BUFF_STRT(6)),
- dump_register(C8SECTPFE_IB_BUFF_END(6)),
- dump_register(C8SECTPFE_IB_READ_PNT(6)),
- dump_register(C8SECTPFE_IB_WRT_PNT(6)),
- dump_register(C8SECTPFE_IB_PRI_THRLD(6)),
- dump_register(C8SECTPFE_IB_STAT(6)),
- dump_register(C8SECTPFE_IB_MASK(6)),
- dump_register(C8SECTPFE_IB_SYS(6)),
-
- dump_register(DMA_CPU_ID),
- dump_register(DMA_CPU_VCR),
- dump_register(DMA_CPU_RUN),
- dump_register(DMA_CPU_PC),
-
- dump_register(DMA_PER_TPn_DREQ_MASK),
- dump_register(DMA_PER_TPn_DACK_SET),
- dump_register(DMA_PER_TPn_DREQ),
- dump_register(DMA_PER_TPn_DACK),
- dump_register(DMA_PER_DREQ_MODE),
- dump_register(DMA_PER_STBUS_SYNC),
- dump_register(DMA_PER_STBUS_ACCESS),
- dump_register(DMA_PER_STBUS_ADDRESS),
- dump_register(DMA_PER_IDLE_INT),
- dump_register(DMA_PER_PRIORITY),
- dump_register(DMA_PER_MAX_OPCODE),
- dump_register(DMA_PER_MAX_CHUNK),
- dump_register(DMA_PER_PAGE_SIZE),
- dump_register(DMA_PER_MBOX_STATUS),
- dump_register(DMA_PER_MBOX_SET),
- dump_register(DMA_PER_MBOX_CLEAR),
- dump_register(DMA_PER_MBOX_MASK),
- dump_register(DMA_PER_INJECT_PKT_SRC),
- dump_register(DMA_PER_INJECT_PKT_DEST),
- dump_register(DMA_PER_INJECT_PKT_ADDR),
- dump_register(DMA_PER_INJECT_PKT),
- dump_register(DMA_PER_PAT_PTR_INIT),
- dump_register(DMA_PER_PAT_PTR),
- dump_register(DMA_PER_SLEEP_MASK),
- dump_register(DMA_PER_SLEEP_COUNTER),
-
- dump_register(DMA_FIRMWARE_VERSION),
- dump_register(DMA_PTRREC_BASE),
- dump_register(DMA_PTRREC_INPUT_OFFSET),
- dump_register(DMA_ERRREC_BASE),
-
- dump_register(DMA_ERROR_RECORD(0)),
- dump_register(DMA_ERROR_RECORD(1)),
- dump_register(DMA_ERROR_RECORD(2)),
- dump_register(DMA_ERROR_RECORD(3)),
- dump_register(DMA_ERROR_RECORD(4)),
- dump_register(DMA_ERROR_RECORD(5)),
- dump_register(DMA_ERROR_RECORD(6)),
- dump_register(DMA_ERROR_RECORD(7)),
- dump_register(DMA_ERROR_RECORD(8)),
- dump_register(DMA_ERROR_RECORD(9)),
- dump_register(DMA_ERROR_RECORD(10)),
- dump_register(DMA_ERROR_RECORD(11)),
- dump_register(DMA_ERROR_RECORD(12)),
- dump_register(DMA_ERROR_RECORD(13)),
- dump_register(DMA_ERROR_RECORD(14)),
- dump_register(DMA_ERROR_RECORD(15)),
- dump_register(DMA_ERROR_RECORD(16)),
- dump_register(DMA_ERROR_RECORD(17)),
- dump_register(DMA_ERROR_RECORD(18)),
- dump_register(DMA_ERROR_RECORD(19)),
- dump_register(DMA_ERROR_RECORD(20)),
- dump_register(DMA_ERROR_RECORD(21)),
- dump_register(DMA_ERROR_RECORD(22)),
-
- dump_register(DMA_IDLE_REQ),
- dump_register(DMA_FIRMWARE_CONFIG),
-
- dump_register(PIDF_BASE(0)),
- dump_register(PIDF_BASE(1)),
- dump_register(PIDF_BASE(2)),
- dump_register(PIDF_BASE(3)),
- dump_register(PIDF_BASE(4)),
- dump_register(PIDF_BASE(5)),
- dump_register(PIDF_BASE(6)),
- dump_register(PIDF_BASE(7)),
- dump_register(PIDF_BASE(8)),
- dump_register(PIDF_BASE(9)),
- dump_register(PIDF_BASE(10)),
- dump_register(PIDF_BASE(11)),
- dump_register(PIDF_BASE(12)),
- dump_register(PIDF_BASE(13)),
- dump_register(PIDF_BASE(14)),
- dump_register(PIDF_BASE(15)),
- dump_register(PIDF_BASE(16)),
- dump_register(PIDF_BASE(17)),
- dump_register(PIDF_BASE(18)),
- dump_register(PIDF_BASE(19)),
- dump_register(PIDF_BASE(20)),
- dump_register(PIDF_BASE(21)),
- dump_register(PIDF_BASE(22)),
- dump_register(PIDF_LEAK_ENABLE),
- dump_register(PIDF_LEAK_STATUS),
- dump_register(PIDF_LEAK_COUNT_RESET),
- dump_register(PIDF_LEAK_COUNTER),
-};
-
-void c8sectpfe_debugfs_init(struct c8sectpfei *fei)
-{
- fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL);
- if (!fei->regset)
- return;
-
- fei->regset->regs = fei_sys_regs;
- fei->regset->nregs = ARRAY_SIZE(fei_sys_regs);
- fei->regset->base = fei->io;
-
- fei->root = debugfs_create_dir("c8sectpfe", NULL);
- debugfs_create_regset32("registers", S_IRUGO, fei->root, fei->regset);
-}
-
-void c8sectpfe_debugfs_exit(struct c8sectpfei *fei)
-{
- debugfs_remove_recursive(fei->root);
- fei->root = NULL;
-}
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
deleted file mode 100644
index 3fe177b59b16..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Authors: Peter Griffin <peter.griffin@linaro.org>
- */
-
-#ifndef __C8SECTPFE_DEBUG_H
-#define __C8SECTPFE_DEBUG_H
-
-#include "c8sectpfe-core.h"
-
-#if defined(CONFIG_DEBUG_FS)
-void c8sectpfe_debugfs_init(struct c8sectpfei *);
-void c8sectpfe_debugfs_exit(struct c8sectpfei *);
-#else
-static inline void c8sectpfe_debugfs_init(struct c8sectpfei *fei) {};
-static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) {};
-#endif
-
-#endif /* __C8SECTPFE_DEBUG_H */
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c
deleted file mode 100644
index feb48cb546d7..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c
+++ /dev/null
@@ -1,235 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Author Peter Griffin <peter.griffin@linaro.org>
- *
- */
-#include <linux/completion.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/interrupt.h>
-
-#include <dt-bindings/media/c8sectpfe.h>
-
-#include "c8sectpfe-common.h"
-#include "c8sectpfe-core.h"
-#include "c8sectpfe-dvb.h"
-
-#include "dvb-pll.h"
-#include "lnbh24.h"
-#include "stv0367.h"
-#include "stv0367_priv.h"
-#include "stv6110x.h"
-#include "stv090x.h"
-#include "tda18212.h"
-
-static inline const char *dvb_card_str(unsigned int c)
-{
- switch (c) {
- case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1";
- case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2";
- case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1";
- case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2";
- case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA";
- case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB";
- default: return "unknown dvb frontend card";
- }
-}
-
-static struct stv090x_config stv090x_config = {
- .device = STV0903,
- .demod_mode = STV090x_SINGLE,
- .clk_mode = STV090x_CLK_EXT,
- .xtal = 16000000,
- .address = 0x69,
-
- .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
- .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
-
- .repeater_level = STV090x_RPTLEVEL_64,
-
- .tuner_init = NULL,
- .tuner_set_mode = NULL,
- .tuner_set_frequency = NULL,
- .tuner_get_frequency = NULL,
- .tuner_set_bandwidth = NULL,
- .tuner_get_bandwidth = NULL,
- .tuner_set_bbgain = NULL,
- .tuner_get_bbgain = NULL,
- .tuner_set_refclk = NULL,
- .tuner_get_status = NULL,
-};
-
-static struct stv6110x_config stv6110x_config = {
- .addr = 0x60,
- .refclk = 16000000,
-};
-
-#define NIMA 0
-#define NIMB 1
-
-static struct stv0367_config stv0367_tda18212_config[] = {
- {
- .demod_address = 0x1c,
- .xtal = 16000000,
- .if_khz = 4500,
- .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
- .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
- .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
- }, {
- .demod_address = 0x1d,
- .xtal = 16000000,
- .if_khz = 4500,
- .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
- .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
- .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
- }, {
- .demod_address = 0x1e,
- .xtal = 16000000,
- .if_khz = 4500,
- .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
- .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
- .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
- },
-};
-
-static struct tda18212_config tda18212_conf = {
- .if_dvbt_6 = 4150,
- .if_dvbt_7 = 4150,
- .if_dvbt_8 = 4500,
- .if_dvbc = 5000,
-};
-
-int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
- struct c8sectpfe *c8sectpfe,
- struct channel_info *tsin, int chan_num)
-{
- struct tda18212_config *tda18212;
- const struct stv6110x_devctl *fe2;
- struct i2c_client *client;
- struct i2c_board_info tda18212_info = {
- .type = "tda18212",
- .addr = 0x60,
- };
-
- if (!tsin)
- return -EINVAL;
-
- switch (tsin->dvb_card) {
-
- case STV0367_TDA18212_NIMA_1:
- case STV0367_TDA18212_NIMA_2:
- case STV0367_TDA18212_NIMB_1:
- case STV0367_TDA18212_NIMB_2:
- if (tsin->dvb_card == STV0367_TDA18212_NIMA_1)
- *fe = dvb_attach(stv0367ter_attach,
- &stv0367_tda18212_config[0],
- tsin->i2c_adapter);
- else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1)
- *fe = dvb_attach(stv0367ter_attach,
- &stv0367_tda18212_config[1],
- tsin->i2c_adapter);
- else
- *fe = dvb_attach(stv0367ter_attach,
- &stv0367_tda18212_config[2],
- tsin->i2c_adapter);
-
- if (!*fe) {
- dev_err(c8sectpfe->device,
- "%s: stv0367ter_attach failed for NIM card %s\n"
- , __func__, dvb_card_str(tsin->dvb_card));
- return -ENODEV;
- }
-
- /*
- * init the demod so that i2c gate_ctrl
- * to the tuner works correctly
- */
- (*fe)->ops.init(*fe);
-
- /* Allocate the tda18212 structure */
- tda18212 = devm_kzalloc(c8sectpfe->device,
- sizeof(struct tda18212_config),
- GFP_KERNEL);
- if (!tda18212) {
- dev_err(c8sectpfe->device,
- "%s: devm_kzalloc failed\n", __func__);
- return -ENOMEM;
- }
-
- memcpy(tda18212, &tda18212_conf,
- sizeof(struct tda18212_config));
-
- tda18212->fe = (*fe);
-
- tda18212_info.platform_data = tda18212;
-
- /* attach tuner */
- request_module("tda18212");
- client = i2c_new_client_device(tsin->i2c_adapter,
- &tda18212_info);
- if (!i2c_client_has_driver(client)) {
- dvb_frontend_detach(*fe);
- return -ENODEV;
- }
-
- if (!try_module_get(client->dev.driver->owner)) {
- i2c_unregister_device(client);
- dvb_frontend_detach(*fe);
- return -ENODEV;
- }
-
- tsin->i2c_client = client;
-
- break;
-
- case STV0903_6110_LNB24_NIMA:
- *fe = dvb_attach(stv090x_attach, &stv090x_config,
- tsin->i2c_adapter, STV090x_DEMODULATOR_0);
- if (!*fe) {
- dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n"
- "\tfor NIM card %s\n",
- __func__, dvb_card_str(tsin->dvb_card));
- return -ENODEV;
- }
-
- fe2 = dvb_attach(stv6110x_attach, *fe,
- &stv6110x_config, tsin->i2c_adapter);
- if (!fe2) {
- dev_err(c8sectpfe->device,
- "%s: stv6110x_attach failed for NIM card %s\n"
- , __func__, dvb_card_str(tsin->dvb_card));
- return -ENODEV;
- }
-
- stv090x_config.tuner_init = fe2->tuner_init;
- stv090x_config.tuner_set_mode = fe2->tuner_set_mode;
- stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency;
- stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency;
- stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth;
- stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth;
- stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain;
- stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain;
- stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk;
- stv090x_config.tuner_get_status = fe2->tuner_get_status;
-
- dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9);
- break;
-
- default:
- dev_err(c8sectpfe->device,
- "%s: DVB frontend card %s not yet supported\n",
- __func__, dvb_card_str(tsin->dvb_card));
- return -ENODEV;
- }
-
- (*fe)->id = chan_num;
-
- dev_info(c8sectpfe->device,
- "DVB frontend card %s successfully attached",
- dvb_card_str(tsin->dvb_card));
- return 0;
-}
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h
deleted file mode 100644
index 3d87a9ae8702..000000000000
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * c8sectpfe-common.h - C8SECTPFE STi DVB driver
- *
- * Copyright (c) STMicroelectronics 2015
- *
- * Author: Peter Griffin <peter.griffin@linaro.org>
- *
- */
-#ifndef _C8SECTPFE_DVB_H_
-#define _C8SECTPFE_DVB_H_
-
-int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
- struct c8sectpfe *c8sectpfe, struct channel_info *tsin,
- int chan_num);
-
-#endif
diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c
index 468c247ba328..72488aa922fc 100644
--- a/drivers/media/platform/st/stm32/dma2d/dma2d.c
+++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c
@@ -355,13 +355,8 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct dma2d_ctx *ctx = file2ctx(file);
- struct vb2_queue *vq;
struct dma2d_frame *frm;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
frm = get_frame(ctx, f->type);
f->fmt.pix.width = frm->width;
f->fmt.pix.height = frm->height;
@@ -490,7 +485,7 @@ static void device_run(void *prv)
src->sequence = frm_out->sequence++;
dst->sequence = frm_cap->sequence++;
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
if (clk_enable(dev->gate))
goto end;
diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
index eb519afb30ca..7c4dd1ac772d 100644
--- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
+++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
@@ -71,7 +71,7 @@ static void deinterlace_device_run(void *priv)
src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
deinterlace_write(dev, DEINTERLACE_MOD_ENABLE,
DEINTERLACE_MOD_ENABLE_EN);
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
index 89992feaab60..2deab920884a 100644
--- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
+++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
@@ -70,7 +70,7 @@ static void rotate_device_run(void *priv)
src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
val = ROTATE_GLB_CTL_MODE(ROTATE_MODE_COPY_ROTATE);
if (ctx->hflip)
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index b7d278b3889f..c3007e09bc9f 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -237,7 +237,7 @@ static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
break;
}
- ret = (cnt >= detection_threshold) ? true : false;
+ ret = cnt >= detection_threshold;
v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: %d\n", __func__, ret);
return ret;
diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c
index b644ed890412..3e25ce0c3c3b 100644
--- a/drivers/media/platform/ti/cal/cal.c
+++ b/drivers/media/platform/ti/cal/cal.c
@@ -1107,8 +1107,7 @@ static int cal_init_camerarx_regmap(struct cal_dev *cal)
return 0;
}
- dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n",
- PTR_ERR(syscon));
+ dev_warn(cal->dev, "failed to get ti,camerrx-control: %pe\n", syscon);
/*
* Backward DTS compatibility. If syscon entry is not present then
diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index d053972888d1..243c6196b024 100644
--- a/drivers/media/platform/ti/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -1600,7 +1600,7 @@ err_cleanup:
* This creates device entries by register itself to the V4L2 driver and
* initializes fields of each channel objects
*/
-static __init int vpif_probe(struct platform_device *pdev)
+static int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
struct i2c_adapter *i2c_adap;
@@ -1807,7 +1807,7 @@ static int vpif_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume);
-static __refdata struct platform_driver vpif_driver = {
+static struct platform_driver vpif_driver = {
.driver = {
.name = VPIF_DRIVER_NAME,
.pm = &vpif_pm_ops,
diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c
index 70c89549f4b6..1e7815e9f8e0 100644
--- a/drivers/media/platform/ti/davinci/vpif_display.c
+++ b/drivers/media/platform/ti/davinci/vpif_display.c
@@ -1214,7 +1214,7 @@ probe_out:
* vpif_probe: This function creates device entries by register itself to the
* V4L2 driver and initializes fields of each channel objects
*/
-static __init int vpif_probe(struct platform_device *pdev)
+static int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
struct i2c_adapter *i2c_adap;
@@ -1390,7 +1390,7 @@ static int vpif_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(vpif_pm_ops, vpif_suspend, vpif_resume);
-static __refdata struct platform_driver vpif_driver = {
+static struct platform_driver vpif_driver = {
.driver = {
.name = VPIF_DRIVER_NAME,
.pm = &vpif_pm_ops,
diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c
index f51cf6119e97..8ac2bdcdf87b 100644
--- a/drivers/media/platform/ti/omap3isp/isp.c
+++ b/drivers/media/platform/ti/omap3isp/isp.c
@@ -240,11 +240,11 @@ static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate)
return divider;
}
-static long isp_xclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int isp_xclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- isp_xclk_calc_divider(&rate, *parent_rate);
- return rate;
+ isp_xclk_calc_divider(&req->rate, req->best_parent_rate);
+ return 0;
}
static int isp_xclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -275,7 +275,7 @@ static const struct clk_ops isp_xclk_ops = {
.enable = isp_xclk_enable,
.disable = isp_xclk_disable,
.recalc_rate = isp_xclk_recalc_rate,
- .round_rate = isp_xclk_round_rate,
+ .determine_rate = isp_xclk_determine_rate,
.set_rate = isp_xclk_set_rate,
};
diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c
index 6029d4e8e0bd..1a549775cabe 100644
--- a/drivers/media/platform/ti/vpe/vpe.c
+++ b/drivers/media/platform/ti/vpe/vpe.c
@@ -1567,13 +1567,8 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp;
struct vpe_ctx *ctx = to_vpe_ctx(file);
- struct vb2_queue *vq;
struct vpe_q_data *q_data;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = get_q_data(ctx, f->type);
if (!q_data)
return -EINVAL;
@@ -1740,8 +1735,6 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f)
struct vb2_queue *vq;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
if (vb2_is_busy(vq)) {
vpe_err(ctx->dev, "queue busy\n");
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index e0c11fe8b55c..60b95b5d8565 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -183,7 +183,7 @@ static void device_run(void *priv)
if (ret)
goto err_cancel_job;
- v4l2_m2m_buf_copy_metadata(src, dst, true);
+ v4l2_m2m_buf_copy_metadata(src, dst);
if (ctx->codec_ops->run(ctx))
goto err_cancel_job;
diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c
index aae0b562fabb..318673b66da8 100644
--- a/drivers/media/platform/verisilicon/hantro_g2.c
+++ b/drivers/media/platform/verisilicon/hantro_g2.c
@@ -5,43 +5,93 @@
* Copyright (C) 2021 Collabora Ltd, Andrzej Pietrasiewicz <andrzej.p@collabora.com>
*/
+#include <linux/delay.h>
#include "hantro_hw.h"
#include "hantro_g2_regs.h"
#define G2_ALIGN 16
-void hantro_g2_check_idle(struct hantro_dev *vpu)
+static bool hantro_g2_active(struct hantro_ctx *ctx)
{
- int i;
-
- for (i = 0; i < 3; i++) {
- u32 status;
-
- /* Make sure the VPU is idle */
- status = vdpu_read(vpu, G2_REG_INTERRUPT);
- if (status & G2_REG_INTERRUPT_DEC_E) {
- dev_warn(vpu->dev, "device still running, aborting");
- status |= G2_REG_INTERRUPT_DEC_ABORT_E | G2_REG_INTERRUPT_DEC_IRQ_DIS;
- vdpu_write(vpu, status, G2_REG_INTERRUPT);
- }
+ struct hantro_dev *vpu = ctx->dev;
+ u32 status;
+
+ status = vdpu_read(vpu, G2_REG_INTERRUPT);
+
+ return (status & G2_REG_INTERRUPT_DEC_E);
+}
+
+/**
+ * hantro_g2_reset:
+ * @ctx: the hantro context
+ *
+ * Emulates a reset using Hantro abort function. Failing this procedure would
+ * results in programming a running IP which leads to CPU hang.
+ *
+ * Using a hard reset procedure instead is prefferred.
+ */
+void hantro_g2_reset(struct hantro_ctx *ctx)
+{
+ struct hantro_dev *vpu = ctx->dev;
+ u32 status;
+
+ status = vdpu_read(vpu, G2_REG_INTERRUPT);
+ if (status & G2_REG_INTERRUPT_DEC_E) {
+ dev_warn_ratelimited(vpu->dev, "device still running, aborting");
+ status |= G2_REG_INTERRUPT_DEC_ABORT_E | G2_REG_INTERRUPT_DEC_IRQ_DIS;
+ vdpu_write(vpu, status, G2_REG_INTERRUPT);
+
+ do {
+ mdelay(1);
+ } while (hantro_g2_active(ctx));
}
}
irqreturn_t hantro_g2_irq(int irq, void *dev_id)
{
struct hantro_dev *vpu = dev_id;
- enum vb2_buffer_state state;
u32 status;
status = vdpu_read(vpu, G2_REG_INTERRUPT);
- state = (status & G2_REG_INTERRUPT_DEC_RDY_INT) ?
- VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
- vdpu_write(vpu, 0, G2_REG_INTERRUPT);
- vdpu_write(vpu, G2_REG_CONFIG_DEC_CLK_GATE_E, G2_REG_CONFIG);
+ if (!(status & G2_REG_INTERRUPT_DEC_IRQ))
+ return IRQ_NONE;
+
+ hantro_reg_write(vpu, &g2_dec_irq, 0);
+ hantro_reg_write(vpu, &g2_dec_int_stat, 0);
+ hantro_reg_write(vpu, &g2_clk_gate_e, 1);
+
+ if (status & G2_REG_INTERRUPT_DEC_RDY_INT) {
+ hantro_irq_done(vpu, VB2_BUF_STATE_DONE);
+ return IRQ_HANDLED;
+ }
+
+ if (status & G2_REG_INTERRUPT_DEC_ABORT_INT) {
+ /* disabled on abort, though lets be safe and handle it */
+ dev_warn_ratelimited(vpu->dev, "decode operation aborted.");
+ return IRQ_HANDLED;
+ }
+
+ if (status & G2_REG_INTERRUPT_DEC_LAST_SLICE_INT)
+ dev_warn_ratelimited(vpu->dev, "not all macroblocks were decoded.");
+
+ if (status & G2_REG_INTERRUPT_DEC_BUS_INT)
+ dev_warn_ratelimited(vpu->dev, "bus error detected.");
+
+ if (status & G2_REG_INTERRUPT_DEC_ERROR_INT)
+ dev_warn_ratelimited(vpu->dev, "decode error detected.");
+
+ if (status & G2_REG_INTERRUPT_DEC_TIMEOUT)
+ dev_warn_ratelimited(vpu->dev, "frame decode timed out.");
- hantro_irq_done(vpu, state);
+ /**
+ * If the decoding haven't stopped, let it continue. The hardware timeout
+ * will trigger if it is trully stuck.
+ */
+ if (status & G2_REG_INTERRUPT_DEC_E)
+ return IRQ_HANDLED;
+ hantro_irq_done(vpu, VB2_BUF_STATE_ERROR);
return IRQ_HANDLED;
}
diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
index 0e212198dd65..e8c2e83379de 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
@@ -283,6 +283,15 @@ static void set_params(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &g2_apf_threshold, 8);
}
+static u32 get_dpb_index(const struct v4l2_ctrl_hevc_decode_params *decode_params,
+ const u32 index)
+{
+ if (index > decode_params->num_active_dpb_entries)
+ return 0;
+
+ return index;
+}
+
static void set_ref_pic_list(struct hantro_ctx *ctx)
{
const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
@@ -355,8 +364,10 @@ static void set_ref_pic_list(struct hantro_ctx *ctx)
list1[j++] = list1[i++];
for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) {
- hantro_reg_write(vpu, &ref_pic_regs0[i], list0[i]);
- hantro_reg_write(vpu, &ref_pic_regs1[i], list1[i]);
+ hantro_reg_write(vpu, &ref_pic_regs0[i],
+ get_dpb_index(decode_params, list0[i]));
+ hantro_reg_write(vpu, &ref_pic_regs1[i],
+ get_dpb_index(decode_params, list1[i]));
}
}
@@ -582,8 +593,6 @@ int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx)
struct hantro_dev *vpu = ctx->dev;
int ret;
- hantro_g2_check_idle(vpu);
-
/* Prepare HEVC decoder context. */
ret = hantro_hevc_dec_prepare_run(ctx);
if (ret)
diff --git a/drivers/media/platform/verisilicon/hantro_g2_regs.h b/drivers/media/platform/verisilicon/hantro_g2_regs.h
index b943b1816db7..c614951121c7 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_regs.h
+++ b/drivers/media/platform/verisilicon/hantro_g2_regs.h
@@ -22,7 +22,14 @@
#define G2_REG_VERSION G2_SWREG(0)
#define G2_REG_INTERRUPT G2_SWREG(1)
+#define G2_REG_INTERRUPT_DEC_LAST_SLICE_INT BIT(19)
+#define G2_REG_INTERRUPT_DEC_TIMEOUT BIT(18)
+#define G2_REG_INTERRUPT_DEC_ERROR_INT BIT(16)
+#define G2_REG_INTERRUPT_DEC_BUF_INT BIT(14)
+#define G2_REG_INTERRUPT_DEC_BUS_INT BIT(13)
#define G2_REG_INTERRUPT_DEC_RDY_INT BIT(12)
+#define G2_REG_INTERRUPT_DEC_ABORT_INT BIT(11)
+#define G2_REG_INTERRUPT_DEC_IRQ BIT(8)
#define G2_REG_INTERRUPT_DEC_ABORT_E BIT(5)
#define G2_REG_INTERRUPT_DEC_IRQ_DIS BIT(4)
#define G2_REG_INTERRUPT_DEC_E BIT(0)
@@ -35,6 +42,9 @@
#define BUS_WIDTH_128 2
#define BUS_WIDTH_256 3
+#define g2_dec_int_stat G2_DEC_REG(1, 11, 0xf)
+#define g2_dec_irq G2_DEC_REG(1, 8, 0x1)
+
#define g2_strm_swap G2_DEC_REG(2, 28, 0xf)
#define g2_strm_swap_old G2_DEC_REG(2, 27, 0x1f)
#define g2_pic_swap G2_DEC_REG(2, 22, 0x1f)
@@ -225,6 +235,9 @@
#define vp9_filt_level_seg5 G2_DEC_REG(19, 8, 0x3f)
#define vp9_quant_seg5 G2_DEC_REG(19, 0, 0xff)
+#define g2_timemout_override_e G2_DEC_REG(45, 31, 0x1)
+#define g2_timemout_cycles G2_DEC_REG(45, 0, 0x7fffffff)
+
#define hevc_cur_poc_00 G2_DEC_REG(46, 24, 0xff)
#define hevc_cur_poc_01 G2_DEC_REG(46, 16, 0xff)
#define hevc_cur_poc_02 G2_DEC_REG(46, 8, 0xff)
diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
index 82a478ac645e..56c79e339030 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
@@ -893,8 +893,6 @@ int hantro_g2_vp9_dec_run(struct hantro_ctx *ctx)
struct vb2_v4l2_buffer *dst;
int ret;
- hantro_g2_check_idle(ctx->dev);
-
ret = start_prepare_run(ctx, &decode_params);
if (ret) {
hantro_end_prepare_run(ctx);
diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h
index c9b6556f8b2b..5f2011529f02 100644
--- a/drivers/media/platform/verisilicon/hantro_hw.h
+++ b/drivers/media/platform/verisilicon/hantro_hw.h
@@ -583,6 +583,7 @@ void hantro_g2_vp9_dec_done(struct hantro_ctx *ctx);
int hantro_vp9_dec_init(struct hantro_ctx *ctx);
void hantro_vp9_dec_exit(struct hantro_ctx *ctx);
void hantro_g2_check_idle(struct hantro_dev *vpu);
+void hantro_g2_reset(struct hantro_ctx *ctx);
irqreturn_t hantro_g2_irq(int irq, void *dev_id);
#endif /* HANTRO_HW_H_ */
diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
index f9f276385c11..5be0e2e76882 100644
--- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
@@ -294,11 +294,13 @@ static const struct hantro_codec_ops imx8mq_vpu_g1_codec_ops[] = {
static const struct hantro_codec_ops imx8mq_vpu_g2_codec_ops[] = {
[HANTRO_MODE_HEVC_DEC] = {
.run = hantro_g2_hevc_dec_run,
+ .reset = hantro_g2_reset,
.init = hantro_hevc_dec_init,
.exit = hantro_hevc_dec_exit,
},
[HANTRO_MODE_VP9_DEC] = {
.run = hantro_g2_vp9_dec_run,
+ .reset = hantro_g2_reset,
.done = hantro_g2_vp9_dec_done,
.init = hantro_vp9_dec_init,
.exit = hantro_vp9_dec_exit,
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index afd80d2350c6..edc46828509c 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -402,4 +402,3 @@ module_platform_driver(hix5hd2_ir_driver);
MODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms");
MODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:hix5hd2-ir");
diff --git a/drivers/media/rc/st_rc.c b/drivers/media/rc/st_rc.c
index 6539fa0a6e79..6b70bac5f45d 100644
--- a/drivers/media/rc/st_rc.c
+++ b/drivers/media/rc/st_rc.c
@@ -284,7 +284,7 @@ static int st_rc_probe(struct platform_device *pdev)
else
rc_dev->rx_base = rc_dev->base;
- rc_dev->rstc = reset_control_get_optional_exclusive(dev, NULL);
+ rc_dev->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(rc_dev->rstc)) {
ret = PTR_ERR(rc_dev->rstc);
goto err;
diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c
index a3df3a33237e..a7ab668ce70b 100644
--- a/drivers/media/test-drivers/vicodec/vicodec-core.c
+++ b/drivers/media/test-drivers/vicodec/vicodec-core.c
@@ -421,7 +421,7 @@ static void device_run(void *priv)
else
dst_buf->sequence = q_dst->sequence++;
dst_buf->flags &= ~V4L2_BUF_FLAG_LAST;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, false);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
spin_lock(ctx->lock);
if (!ctx->comp_has_next_frame &&
@@ -555,7 +555,7 @@ static void set_last_buffer(struct vb2_v4l2_buffer *dst_buf,
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0);
dst_buf->sequence = q_dst->sequence++;
- v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, !ctx->is_enc);
+ v4l2_m2m_buf_copy_metadata(src_buf, dst_buf);
dst_buf->flags |= V4L2_BUF_FLAG_LAST;
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
}
@@ -760,16 +760,11 @@ static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
static int vidioc_g_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct vicodec_q_data *q_data;
struct v4l2_pix_format_mplane *pix_mp;
struct v4l2_pix_format *pix;
const struct v4l2_fwht_pixfmt_info *info;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = get_q_data(ctx, f->type);
info = q_data->info;
@@ -976,8 +971,6 @@ static int vidioc_s_fmt(struct vicodec_ctx *ctx, struct v4l2_format *f)
struct v4l2_pix_format *pix;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = get_q_data(ctx, f->type);
if (!q_data)
diff --git a/drivers/media/test-drivers/vidtv/vidtv_channel.c b/drivers/media/test-drivers/vidtv/vidtv_channel.c
index f3023e91b3eb..3541155c6fc6 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_channel.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_channel.c
@@ -461,12 +461,15 @@ int vidtv_channel_si_init(struct vidtv_mux *m)
/* assemble all programs and assign to PAT */
vidtv_psi_pat_program_assign(m->si.pat, programs);
+ programs = NULL;
/* assemble all services and assign to SDT */
vidtv_psi_sdt_service_assign(m->si.sdt, services);
+ services = NULL;
/* assemble all events and assign to EIT */
vidtv_psi_eit_event_assign(m->si.eit, events);
+ events = NULL;
m->si.pmt_secs = vidtv_psi_pmt_create_sec_for_each_pat_entry(m->si.pat,
m->pcr_pid);
diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c
index 86c32699111a..c33c18ea5210 100644
--- a/drivers/media/test-drivers/vim2m.c
+++ b/drivers/media/test-drivers/vim2m.c
@@ -477,7 +477,7 @@ static int device_process(struct vim2m_ctx *ctx,
out_vb->sequence = q_data_out->sequence++;
in_vb->sequence = q_data_in->sequence++;
- v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true);
+ v4l2_m2m_buf_copy_metadata(in_vb, out_vb);
if (ctx->mode & MEM2MEM_VFLIP) {
start = height - 1;
@@ -724,14 +724,9 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct vim2m_q_data *q_data;
int ret;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = get_q_data(ctx, f->type);
if (!q_data)
return -EINVAL;
@@ -752,14 +747,9 @@ static int vidioc_g_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
static int vidioc_g_fmt_mplane(struct vim2m_ctx *ctx, struct v4l2_format *f)
{
- struct vb2_queue *vq;
struct vim2m_q_data *q_data;
int ret;
- vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
-
q_data = get_q_data(ctx, f->type);
if (!q_data)
return -EINVAL;
@@ -971,8 +961,6 @@ static int vidioc_s_fmt(struct vim2m_ctx *ctx, struct v4l2_format *f)
u32 height = (is_mplane) ? f->fmt.pix_mp.height : f->fmt.pix.height;
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
- if (!vq)
- return -EINVAL;
q_data = get_q_data(ctx, f->type);
if (!q_data)
diff --git a/drivers/media/test-drivers/visl/visl-dec.c b/drivers/media/test-drivers/visl/visl-dec.c
index 6a9639bd4d61..d90b79de8384 100644
--- a/drivers/media/test-drivers/visl/visl-dec.c
+++ b/drivers/media/test-drivers/visl/visl-dec.c
@@ -572,7 +572,7 @@ void visl_device_run(void *priv)
if (src_req)
v4l2_ctrl_request_setup(src_req, &ctx->hdl);
- v4l2_m2m_buf_copy_metadata(run.src, run.dst, true);
+ v4l2_m2m_buf_copy_metadata(run.src, run.dst);
run.dst->sequence = ctx->q_data[V4L2_M2M_DST].sequence++;
run.src->sequence = ctx->q_data[V4L2_M2M_SRC].sequence++;
run.dst->field = ctx->decoded_fmt.fmt.pix.field;
diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index 86506be36acb..9c0b1a32b5c9 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -1856,15 +1856,15 @@ static int vivid_create_instance(struct platform_device *pdev, int inst)
tpg_init(&dev->tpg, 640, 360);
if (tpg_alloc(&dev->tpg, array_size(MAX_WIDTH, MAX_ZOOM)))
goto free_dev;
- dev->scaled_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM));
+ dev->scaled_line = vcalloc(MAX_WIDTH, MAX_ZOOM);
if (!dev->scaled_line)
goto free_dev;
- dev->blended_line = vzalloc(array_size(MAX_WIDTH, MAX_ZOOM));
+ dev->blended_line = vcalloc(MAX_WIDTH, MAX_ZOOM);
if (!dev->blended_line)
goto free_dev;
/* load the edid */
- dev->edid = vmalloc(array_size(256, 128));
+ dev->edid = vmalloc_array(256, 128);
if (!dev->edid)
goto free_dev;
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index 8b3162e82032..b95f06a9b5ae 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -302,8 +302,10 @@ void vivid_update_quality(struct vivid_dev *dev)
*/
freq_modulus = (dev->tv_freq - 676 /* (43.25-1) * 16 */) % (6 * 16);
if (freq_modulus > 2 * 16) {
+ struct rnd_state prng;
+ prandom_seed_state(&prng, dev->tv_freq ^ 0x55);
tpg_s_quality(&dev->tpg, TPG_QUAL_NOISE,
- next_pseudo_random32(dev->tv_freq ^ 0x55) & 0x3f);
+ prandom_u32_state(&prng) & 0x3f);
return;
}
if (freq_modulus < 12 /*0.75 * 16*/ || freq_modulus > 20 /*1.25 * 16*/)
diff --git a/drivers/media/tuners/xc2028.c b/drivers/media/tuners/xc2028.c
index 8e6638e5f688..807585d2dfde 100644
--- a/drivers/media/tuners/xc2028.c
+++ b/drivers/media/tuners/xc2028.c
@@ -1361,16 +1361,9 @@ static void load_firmware_cb(const struct firmware *fw,
void *context)
{
struct dvb_frontend *fe = context;
- struct xc2028_data *priv;
+ struct xc2028_data *priv = fe->tuner_priv;
int rc;
- if (!fe) {
- pr_warn("xc2028: No frontend in %s\n", __func__);
- return;
- }
-
- priv = fe->tuner_priv;
-
tuner_dbg("request_firmware_nowait(): %s\n", fw ? "OK" : "error");
if (!fw) {
tuner_err("Could not load firmware %s.\n", priv->fname);
diff --git a/drivers/media/usb/dvb-usb/dtv5100.c b/drivers/media/usb/dvb-usb/dtv5100.c
index 3d85c6f7f6ec..c448e2ebda1a 100644
--- a/drivers/media/usb/dvb-usb/dtv5100.c
+++ b/drivers/media/usb/dvb-usb/dtv5100.c
@@ -55,6 +55,11 @@ static int dtv5100_i2c_msg(struct dvb_usb_device *d, u8 addr,
}
index = (addr << 8) + wbuf[0];
+ if (rlen > sizeof(st->data)) {
+ warn("rlen = %x is too big!\n", rlen);
+ return -EINVAL;
+ }
+
memcpy(st->data, rbuf, rlen);
msleep(1); /* avoid I2C errors */
return usb_control_msg(d->udev, pipe, request,
diff --git a/drivers/media/usb/dvb-usb/pctv452e.c b/drivers/media/usb/dvb-usb/pctv452e.c
index 5094de9a312e..bc7a224d829e 100644
--- a/drivers/media/usb/dvb-usb/pctv452e.c
+++ b/drivers/media/usb/dvb-usb/pctv452e.c
@@ -422,16 +422,15 @@ static int pctv452e_i2c_msg(struct dvb_usb_device *d, u8 addr,
u8 id;
int ret;
+ if (snd_len > 64 - 7 || rcv_len > 64 - 7)
+ return -EINVAL;
+
buf = kmalloc(64, GFP_KERNEL);
if (!buf)
return -ENOMEM;
id = state->c++;
- ret = -EINVAL;
- if (snd_len > 64 - 7 || rcv_len > 64 - 7)
- goto failed;
-
buf[0] = SYNC_BYTE_OUT;
buf[1] = id;
buf[2] = PCTV_CMD_I2C;
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index f21c2806eb9f..b32bb906a9de 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -3622,7 +3622,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Attempted to execute %d byte control-read transfer (limit=%d)",
- write_len,PVR2_CTL_BUFFSIZE);
+ read_len, PVR2_CTL_BUFFSIZE);
return -EINVAL;
}
if ((!write_len) && (!read_len)) {
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index fb6afb8e84f0..ee4f54d68349 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -167,13 +167,26 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev,
static struct uvc_streaming *uvc_stream_by_id(struct uvc_device *dev, int id)
{
- struct uvc_streaming *stream;
+ struct uvc_streaming *stream, *last_stream;
+ unsigned int count = 0;
list_for_each_entry(stream, &dev->streams, list) {
+ count += 1;
+ last_stream = stream;
if (stream->header.bTerminalLink == id)
return stream;
}
+ /*
+ * If the streaming entity is referenced by an invalid ID, notify the
+ * user and use heuristics to guess the correct entity.
+ */
+ if (count == 1 && id == UVC_INVALID_ENTITY_ID) {
+ dev_warn(&dev->intf->dev,
+ "UVC non compliance: Invalid USB header. The streaming entity has an invalid ID, guessing the correct one.");
+ return last_stream;
+ }
+
return NULL;
}
diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index 331b8e535e5b..d50ccac9733c 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -82,3 +82,7 @@ config V4L2_CCI_I2C
depends on I2C
select REGMAP_I2C
select V4L2_CCI
+
+config V4L2_ISP
+ tristate
+ depends on VIDEOBUF2_CORE
diff --git a/drivers/media/v4l2-core/Makefile b/drivers/media/v4l2-core/Makefile
index 2177b9d63a8f..329f0eadce99 100644
--- a/drivers/media/v4l2-core/Makefile
+++ b/drivers/media/v4l2-core/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
+obj-$(CONFIG_V4L2_ISP) += v4l2-isp.o
obj-$(CONFIG_V4L2_JPEG_HELPER) += v4l2-jpeg.o
obj-$(CONFIG_V4L2_MEM2MEM_DEV) += v4l2-mem2mem.o
obj-$(CONFIG_V4L2_VP9) += v4l2-vp9.o
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index b367d479d6b3..554c591e1113 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -573,6 +573,35 @@ s64 v4l2_get_link_freq(const struct media_pad *pad, unsigned int mul,
return v4l2_get_link_freq_ctrl(sd->ctrl_handler, mul, div);
}
EXPORT_SYMBOL_GPL(v4l2_get_link_freq);
+
+int v4l2_get_active_data_lanes(const struct media_pad *pad,
+ unsigned int max_data_lanes)
+{
+ struct v4l2_mbus_config mbus_config = {};
+ struct v4l2_subdev *sd;
+ unsigned int lanes;
+ int ret;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ ret = v4l2_subdev_call(sd, pad, get_mbus_config, pad->index,
+ &mbus_config);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* This relies on the mbus_config being zeroed at init time */
+ lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
+ if (!lanes)
+ return max_data_lanes;
+
+ if (lanes > max_data_lanes) {
+ dev_dbg(sd->dev, "Active data lanes (%u) exceeds max (%u)\n",
+ lanes, max_data_lanes);
+ return -EINVAL;
+ }
+
+ return lanes;
+}
+EXPORT_SYMBOL_GPL(v4l2_get_active_data_lanes);
#endif
/*
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 85d07ef44f62..209bc05883bb 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -160,7 +160,13 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
break;
case V4L2_CTRL_TYPE_AV1_SEQUENCE:
p_av1_sequence = p;
+ /*
+ * The initial profile is 0 which only allows YUV 420 subsampled
+ * data. Set the subsampling flags accordingly.
+ */
p_av1_sequence->bit_depth = 8;
+ p_av1_sequence->flags |= V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y;
break;
case V4L2_CTRL_TYPE_FWHT_PARAMS:
p_fwht_params = p;
@@ -827,39 +833,114 @@ static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f)
return 0;
}
+/**
+ * validate_av1_sequence - validate AV1 sequence header fields
+ * @s: control struct from userspace
+ *
+ * Implements AV1 spec §5.5.2 color_config() checks that are
+ * possible with the current v4l2_ctrl_av1_sequence definition.
+ *
+ * TODO: extend validation once additional fields such as
+ * color_primaries, transfer_characteristics,
+ * matrix_coefficients, and chroma_sample_position
+ * are added to the uAPI.
+ *
+ * Returns 0 if valid, -EINVAL otherwise.
+ */
static int validate_av1_sequence(struct v4l2_ctrl_av1_sequence *s)
{
- if (s->flags &
- ~(V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE |
- V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF |
- V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION |
- V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME |
- V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE |
- V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X |
- V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y |
- V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT |
- V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q))
- return -EINVAL;
+ const bool mono = s->flags & V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME;
+ const bool sx = s->flags & V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X;
+ const bool sy = s->flags & V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y;
+ const bool uv_dq = s->flags & V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q;
- if (s->seq_profile == 1 && s->flags & V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME)
+ /* 1. Reject unknown flags */
+ if (s->flags &
+ ~(V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE |
+ V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF |
+ V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION |
+ V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME |
+ V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X |
+ V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y |
+ V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT |
+ V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q))
return -EINVAL;
- /* reserved */
+ /* 2. Profile range */
if (s->seq_profile > 2)
return -EINVAL;
- /* TODO: PROFILES */
+ /* 3. Monochrome shortcut */
+ if (mono) {
+ /* Profile 1 forbids monochrome */
+ if (s->seq_profile == 1)
+ return -EINVAL;
+
+ /* Mono → subsampling must look like 4:0:0: sx=1, sy=1 */
+ if (!sx || !sy)
+ return -EINVAL;
+
+ /* separate_uv_delta_q must be 0 */
+ if (uv_dq)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* 4. Profile-specific rules */
+ switch (s->seq_profile) {
+ case 0:
+ /* Profile 0: only 8/10-bit, subsampling=4:2:0 (sx=1, sy=1) */
+ if (s->bit_depth != 8 && s->bit_depth != 10)
+ return -EINVAL;
+ if (!(sx && sy))
+ return -EINVAL;
+ break;
+
+ case 1:
+ /* Profile 1: only 8/10-bit, subsampling=4:4:4 (sx=0, sy=0) */
+ if (s->bit_depth != 8 && s->bit_depth != 10)
+ return -EINVAL;
+ if (sx || sy)
+ return -EINVAL;
+ break;
+
+ case 2:
+ /* Profile 2: 8/10/12-bit allowed */
+ if (s->bit_depth != 8 && s->bit_depth != 10 &&
+ s->bit_depth != 12)
+ return -EINVAL;
+
+ if (s->bit_depth == 12) {
+ if (!sx) {
+ /* 4:4:4 → sy must be 0 */
+ if (sy)
+ return -EINVAL;
+ } else {
+ /* sx=1 → sy=0 (4:2:2) or sy=1 (4:2:0) */
+ if (sy != 0 && sy != 1)
+ return -EINVAL;
+ }
+ } else {
+ /* 8/10-bit → only 4:2:2 allowed (sx=1, sy=0) */
+ if (!(sx && !sy))
+ return -EINVAL;
+ }
+ break;
+ }
+
return 0;
}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 01cf52c3ea33..98512ea4cc5b 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1469,6 +1469,8 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_RK_ISP1_EXT_PARAMS: descr = "Rockchip ISP1 Ext 3A Params"; break;
case V4L2_META_FMT_C3ISP_PARAMS: descr = "Amlogic C3 ISP Parameters"; break;
case V4L2_META_FMT_C3ISP_STATS: descr = "Amlogic C3 ISP Statistics"; break;
+ case V4L2_META_FMT_MALI_C55_PARAMS: descr = "ARM Mali-C55 ISP Parameters"; break;
+ case V4L2_META_FMT_MALI_C55_STATS: descr = "ARM Mali-C55 ISP 3A Statistics"; break;
case V4L2_PIX_FMT_NV12_8L128: descr = "NV12 (8x128 Linear)"; break;
case V4L2_PIX_FMT_NV12M_8L128: descr = "NV12M (8x128 Linear)"; break;
case V4L2_PIX_FMT_NV12_10BE_8L128: descr = "10-bit NV12 (8x128 Linear, BE)"; break;
diff --git a/drivers/media/v4l2-core/v4l2-isp.c b/drivers/media/v4l2-core/v4l2-isp.c
new file mode 100644
index 000000000000..29831f7032e9
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-isp.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Video4Linux2 generic ISP parameters and statistics support
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Author: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+ */
+
+#include <media/v4l2-isp.h>
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+
+#include <media/videobuf2-core.h>
+
+int v4l2_isp_params_validate_buffer_size(struct device *dev,
+ struct vb2_buffer *vb,
+ size_t max_size)
+{
+ size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+
+ /* Payload size can't be greater than the destination buffer size */
+ if (payload_size > max_size) {
+ dev_dbg(dev, "Payload size is too large: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ /* Payload size can't be smaller than the header size */
+ if (payload_size < header_size) {
+ dev_dbg(dev, "Payload size is too small: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer_size);
+
+int v4l2_isp_params_validate_buffer(struct device *dev, struct vb2_buffer *vb,
+ const struct v4l2_isp_params_buffer *buffer,
+ const struct v4l2_isp_params_block_type_info *type_info,
+ size_t num_block_types)
+{
+ size_t header_size = offsetof(struct v4l2_isp_params_buffer, data);
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+ size_t block_offset = 0;
+ size_t buffer_size;
+
+ /*
+ * Currently only the first version of the V4L2 ISP parameters format is
+ * supported. We accept both V0 and V1 to support existing drivers
+ * compatible with V4L2 ISP that use either 0 or 1 as their "first
+ * version" identifiers.
+ */
+ if (buffer->version != V4L2_ISP_PARAMS_VERSION_V0 &&
+ buffer->version != V4L2_ISP_PARAMS_VERSION_V1) {
+ dev_dbg(dev,
+ "Unsupported V4L2 ISP parameters format version: %u\n",
+ buffer->version);
+ return -EINVAL;
+ }
+
+ /* Validate the size reported in the header */
+ buffer_size = header_size + buffer->data_size;
+ if (buffer_size != payload_size) {
+ dev_dbg(dev, "Data size %zu and payload size %zu are different\n",
+ buffer_size, payload_size);
+ return -EINVAL;
+ }
+
+ /* Walk the list of ISP configuration blocks and validate them. */
+ buffer_size = buffer->data_size;
+ while (buffer_size >= sizeof(struct v4l2_isp_params_block_header)) {
+ const struct v4l2_isp_params_block_type_info *info;
+ const struct v4l2_isp_params_block_header *block;
+
+ block = (const struct v4l2_isp_params_block_header *)
+ (buffer->data + block_offset);
+
+ if (block->type >= num_block_types) {
+ dev_dbg(dev,
+ "Invalid block type %u at offset %zu\n",
+ block->type, block_offset);
+ return -EINVAL;
+ }
+
+ if (block->size > buffer_size) {
+ dev_dbg(dev, "Premature end of parameters data\n");
+ return -EINVAL;
+ }
+
+ /* It's invalid to specify both ENABLE and DISABLE. */
+ if ((block->flags & (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
+ V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) ==
+ (V4L2_ISP_PARAMS_FL_BLOCK_ENABLE |
+ V4L2_ISP_PARAMS_FL_BLOCK_DISABLE)) {
+ dev_dbg(dev, "Invalid block flags %x at offset %zu\n",
+ block->flags, block_offset);
+ return -EINVAL;
+ }
+
+ /*
+ * Match the block reported size against the type info provided
+ * one, but allow the block to only contain the header in
+ * case it is going to be disabled.
+ */
+ info = &type_info[block->type];
+ if (block->size != info->size &&
+ (!(block->flags & V4L2_ISP_PARAMS_FL_BLOCK_DISABLE) ||
+ block->size != sizeof(*block))) {
+ dev_dbg(dev,
+ "Invalid block size %u (expected %zu) at offset %zu\n",
+ block->size, info->size, block_offset);
+ return -EINVAL;
+ }
+
+ block_offset += block->size;
+ buffer_size -= block->size;
+ }
+
+ if (buffer_size) {
+ dev_dbg(dev, "Unexpected data after the parameters buffer end\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(v4l2_isp_params_validate_buffer);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com");
+MODULE_DESCRIPTION("V4L2 generic ISP parameters and statistics helpers");
diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c
index 21acd9bc8607..fec93c1a9231 100644
--- a/drivers/media/v4l2-core/v4l2-mem2mem.c
+++ b/drivers/media/v4l2-core/v4l2-mem2mem.c
@@ -123,13 +123,7 @@ static struct v4l2_m2m_queue_ctx *get_queue_ctx(struct v4l2_m2m_ctx *m2m_ctx,
struct vb2_queue *v4l2_m2m_get_vq(struct v4l2_m2m_ctx *m2m_ctx,
enum v4l2_buf_type type)
{
- struct v4l2_m2m_queue_ctx *q_ctx;
-
- q_ctx = get_queue_ctx(m2m_ctx, type);
- if (!q_ctx)
- return NULL;
-
- return &q_ctx->q;
+ return &get_queue_ctx(m2m_ctx, type)->q;
}
EXPORT_SYMBOL(v4l2_m2m_get_vq);
@@ -1285,8 +1279,6 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
unsigned long flags;
q_ctx = get_queue_ctx(m2m_ctx, vbuf->vb2_buf.vb2_queue->type);
- if (!q_ctx)
- return;
spin_lock_irqsave(&q_ctx->rdy_spinlock, flags);
list_add_tail(&b->list, &q_ctx->rdy_queue);
@@ -1296,14 +1288,9 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx,
EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue);
void v4l2_m2m_buf_copy_metadata(const struct vb2_v4l2_buffer *out_vb,
- struct vb2_v4l2_buffer *cap_vb,
- bool copy_frame_flags)
+ struct vb2_v4l2_buffer *cap_vb)
{
- u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
-
- if (copy_frame_flags)
- mask |= V4L2_BUF_FLAG_KEYFRAME | V4L2_BUF_FLAG_PFRAME |
- V4L2_BUF_FLAG_BFRAME;
+ const u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
cap_vb->vb2_buf.timestamp = out_vb->vb2_buf.timestamp;
@@ -1388,8 +1375,6 @@ int v4l2_m2m_ioctl_remove_bufs(struct file *file, void *priv,
struct v4l2_fh *fh = file_to_v4l2_fh(file);
struct vb2_queue *q = v4l2_m2m_get_vq(fh->m2m_ctx, remove->type);
- if (!q)
- return -EINVAL;
if (q->type != remove->type)
return -EINVAL;
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 1da953629010..25e66bf18f5f 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -2608,7 +2608,7 @@ EXPORT_SYMBOL_GPL(v4l2_subdev_is_streaming);
int v4l2_subdev_get_privacy_led(struct v4l2_subdev *sd)
{
#if IS_REACHABLE(CONFIG_LEDS_CLASS)
- sd->privacy_led = led_get(sd->dev, "privacy-led");
+ sd->privacy_led = led_get(sd->dev, "privacy");
if (IS_ERR(sd->privacy_led) && PTR_ERR(sd->privacy_led) != -ENOENT)
return dev_err_probe(sd->dev, PTR_ERR(sd->privacy_led),
"getting privacy LED\n");
diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c
index cfa61dd88557..3c2949c16fde 100644
--- a/drivers/memory/tegra/tegra210.c
+++ b/drivers/memory/tegra/tegra210.c
@@ -1015,7 +1015,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
},
},
}, {
- .id = TEGRA210_MC_SESRD,
+ .id = TEGRA210_MC_SESWR,
.name = "seswr",
.swgroup = TEGRA_SWGROUP_SE,
.regs = {
@@ -1079,7 +1079,7 @@ static const struct tegra_mc_client tegra210_mc_clients[] = {
},
},
}, {
- .id = TEGRA210_MC_ETRR,
+ .id = TEGRA210_MC_ETRW,
.name = "etrw",
.swgroup = TEGRA_SWGROUP_ETR,
.regs = {
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 6cec1858947b..aace5766b38a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -45,7 +45,8 @@ config MFD_ALTERA_A10SR
config MFD_ALTERA_SYSMGR
bool "Altera SOCFPGA System Manager"
- depends on ARCH_INTEL_SOCFPGA && OF
+ depends on ARCH_INTEL_SOCFPGA || COMPILE_TEST
+ depends on OF
select MFD_SYSCON
help
Select this to get System Manager support for all Altera branded
@@ -605,6 +606,22 @@ config MFD_MX25_TSADC
i.MX25 processors. They consist of a conversion queue for general
purpose ADC and a queue for Touchscreens.
+config MFD_PF1550
+ tristate "NXP PF1550 PMIC Support"
+ depends on I2C=y && OF
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Say yes here to add support for NXP PF1550. This is a companion Power
+ Management IC with regulators, onkey, and charger control on chip.
+ This driver provides common support for accessing the device;
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
+ This driver can also be built as a module and if so will be called
+ pf1550.
+
config MFD_HI6421_PMIC
tristate "HiSilicon Hi6421 PMU/Codec IC"
depends on OF
@@ -879,7 +896,7 @@ config MFD_88PM886_PMIC
config MFD_MAX5970
tristate "Maxim 5970/5978 power switch and monitor"
- depends on I2C && OF
+ depends on I2C
select MFD_SIMPLE_MFD_I2C
help
This driver controls a Maxim 5970/5978 switch via I2C bus.
@@ -1258,7 +1275,6 @@ config MFD_SPACEMIT_P1
tristate "SpacemiT P1 PMIC"
depends on ARCH_SPACEMIT || COMPILE_TEST
depends on I2C
- select I2C_K1
select MFD_SIMPLE_MFD_I2C
help
This option supports the I2C-based SpacemiT P1 PMIC, which
@@ -1986,16 +2002,6 @@ config MENELAUS
and other features that are often used in portable devices like
cell phones and PDAs.
-config MFD_WL1273_CORE
- tristate "TI WL1273 FM radio"
- depends on I2C
- select MFD_CORE
- default n
- help
- This is the core driver for the TI WL1273 FM radio. This MFD
- driver connects the radio-wl1273 V4L2 module and the wl1273
- audio codec.
-
config MFD_LM3533
tristate "TI/National Semiconductor LM3533 Lighting Power chip"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 865e9f12faff..e75e8045c28a 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -122,6 +122,8 @@ obj-$(CONFIG_MFD_MC13XXX) += mc13xxx-core.o
obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o
obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o
+obj-$(CONFIG_MFD_PF1550) += pf1550.o
+
obj-$(CONFIG_MFD_NCT6694) += nct6694.o
obj-$(CONFIG_MFD_CORE) += mfd-core.o
@@ -205,7 +207,6 @@ obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o
obj-$(CONFIG_MFD_VX855) += vx855.o
-obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o
si476x-core-y := si476x-cmd.o si476x-prop.o si476x-i2c.o
obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o
diff --git a/drivers/mfd/altera-sysmgr.c b/drivers/mfd/altera-sysmgr.c
index fb5f988e61f3..90c6902d537d 100644
--- a/drivers/mfd/altera-sysmgr.c
+++ b/drivers/mfd/altera-sysmgr.c
@@ -117,6 +117,8 @@ struct regmap *altr_sysmgr_regmap_lookup_by_phandle(struct device_node *np,
sysmgr = dev_get_drvdata(dev);
+ put_device(dev);
+
return sysmgr->regmap;
}
EXPORT_SYMBOL_GPL(altr_sysmgr_regmap_lookup_by_phandle);
diff --git a/drivers/mfd/bcm2835-pm.c b/drivers/mfd/bcm2835-pm.c
index 3cb2b9423121..8bed59816e82 100644
--- a/drivers/mfd/bcm2835-pm.c
+++ b/drivers/mfd/bcm2835-pm.c
@@ -108,6 +108,7 @@ static const struct of_device_id bcm2835_pm_of_match[] = {
{ .compatible = "brcm,bcm2835-pm-wdt", },
{ .compatible = "brcm,bcm2835-pm", },
{ .compatible = "brcm,bcm2711-pm", },
+ { .compatible = "brcm,bcm2712-pm", },
{},
};
MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
diff --git a/drivers/mfd/da9055-core.c b/drivers/mfd/da9055-core.c
index 1f727ef60d63..158590ad37d4 100644
--- a/drivers/mfd/da9055-core.c
+++ b/drivers/mfd/da9055-core.c
@@ -387,7 +387,7 @@ int da9055_device_init(struct da9055 *da9055)
return 0;
err:
- mfd_remove_devices(da9055->dev);
+ regmap_del_irq_chip(da9055->chip_irq, da9055->irq_data);
return ret;
}
diff --git a/drivers/mfd/da9063-i2c.c b/drivers/mfd/da9063-i2c.c
index 1ec9ab56442d..a803b7440f09 100644
--- a/drivers/mfd/da9063-i2c.c
+++ b/drivers/mfd/da9063-i2c.c
@@ -469,6 +469,9 @@ static int da9063_i2c_probe(struct i2c_client *i2c)
}
}
+ /* Reserve our unused second address so userspace won't interfere */
+ devm_i2c_new_dummy_device(&i2c->dev, i2c->adapter, i2c->addr + 1);
+
return da9063_device_init(da9063, i2c->irq);
}
diff --git a/drivers/mfd/ls2k-bmc-core.c b/drivers/mfd/ls2k-bmc-core.c
index 69387dad6661..e42f1de9e641 100644
--- a/drivers/mfd/ls2k-bmc-core.c
+++ b/drivers/mfd/ls2k-bmc-core.c
@@ -265,7 +265,7 @@ static int ls2k_bmc_recover_pci_data(void *data)
if (!ls2k_bmc_bar0_addr_is_set(parent))
break;
mdelay(1);
- };
+ }
if (i == 0)
return false;
diff --git a/drivers/mfd/macsmc.c b/drivers/mfd/macsmc.c
index e6cdae221f1d..e3893e255ce5 100644
--- a/drivers/mfd/macsmc.c
+++ b/drivers/mfd/macsmc.c
@@ -173,7 +173,7 @@ int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size)
}
EXPORT_SYMBOL(apple_smc_read);
-int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size)
+int apple_smc_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
{
guard(mutex)(&smc->mutex);
@@ -181,7 +181,7 @@ int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size)
}
EXPORT_SYMBOL(apple_smc_write);
-int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize,
+int apple_smc_rw(struct apple_smc *smc, smc_key key, const void *wbuf, size_t wsize,
void *rbuf, size_t rsize)
{
guard(mutex)(&smc->mutex);
@@ -239,7 +239,7 @@ int apple_smc_enter_atomic(struct apple_smc *smc)
}
EXPORT_SYMBOL(apple_smc_enter_atomic);
-int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size)
+int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, size_t size)
{
guard(spinlock_irqsave)(&smc->lock);
u8 result;
diff --git a/drivers/mfd/max77620.c b/drivers/mfd/max77620.c
index 21d2ab3db254..3af2974b3023 100644
--- a/drivers/mfd/max77620.c
+++ b/drivers/mfd/max77620.c
@@ -254,7 +254,7 @@ static int max77620_irq_global_unmask(void *irq_drv_data)
return ret;
}
-static struct regmap_irq_chip max77620_top_irq_chip = {
+static const struct regmap_irq_chip max77620_top_irq_chip = {
.name = "max77620-top",
.irqs = max77620_top_irqs,
.num_irqs = ARRAY_SIZE(max77620_top_irqs),
@@ -498,6 +498,7 @@ static int max77620_probe(struct i2c_client *client)
const struct i2c_device_id *id = i2c_client_get_device_id(client);
const struct regmap_config *rmap_config;
struct max77620_chip *chip;
+ struct regmap_irq_chip *chip_desc;
const struct mfd_cell *mfd_cells;
int n_mfd_cells;
bool pm_off;
@@ -508,6 +509,14 @@ static int max77620_probe(struct i2c_client *client)
return -ENOMEM;
i2c_set_clientdata(client, chip);
+
+ chip_desc = devm_kmemdup(&client->dev, &max77620_top_irq_chip,
+ sizeof(max77620_top_irq_chip),
+ GFP_KERNEL);
+ if (!chip_desc)
+ return -ENOMEM;
+ chip_desc->irq_drv_data = chip;
+
chip->dev = &client->dev;
chip->chip_irq = client->irq;
chip->chip_id = (enum max77620_chip_id)id->driver_data;
@@ -544,11 +553,9 @@ static int max77620_probe(struct i2c_client *client)
if (ret < 0)
return ret;
- max77620_top_irq_chip.irq_drv_data = chip;
ret = devm_regmap_add_irq_chip(chip->dev, chip->rmap, client->irq,
IRQF_ONESHOT | IRQF_SHARED, 0,
- &max77620_top_irq_chip,
- &chip->top_irq_data);
+ chip_desc, &chip->top_irq_data);
if (ret < 0) {
dev_err(chip->dev, "Failed to add regmap irq: %d\n", ret);
return ret;
diff --git a/drivers/mfd/mt6358-irq.c b/drivers/mfd/mt6358-irq.c
index f467b00d2366..74cf20843044 100644
--- a/drivers/mfd/mt6358-irq.c
+++ b/drivers/mfd/mt6358-irq.c
@@ -285,6 +285,7 @@ int mt6358_irq_init(struct mt6397_chip *chip)
if (ret) {
dev_err(chip->dev, "Failed to register IRQ=%d, ret=%d\n",
chip->irq, ret);
+ irq_domain_remove(chip->irq_domain);
return ret;
}
diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c
index 0e463026c5a9..5d2e5459f744 100644
--- a/drivers/mfd/mt6397-irq.c
+++ b/drivers/mfd/mt6397-irq.c
@@ -229,6 +229,7 @@ int mt6397_irq_init(struct mt6397_chip *chip)
if (ret) {
dev_err(chip->dev, "failed to register irq=%d; err: %d\n",
chip->irq, ret);
+ irq_domain_remove(chip->irq_domain);
return ret;
}
diff --git a/drivers/mfd/pf1550.c b/drivers/mfd/pf1550.c
new file mode 100644
index 000000000000..c4f567c05564
--- /dev/null
+++ b/drivers/mfd/pf1550.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Core driver for the PF1550
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Robin Gong <yibin.gong@freescale.com>
+ *
+ * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
+ * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/pf1550.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+static const struct regmap_config pf1550_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = PF1550_PMIC_REG_END,
+};
+
+static const struct regmap_irq pf1550_irqs[] = {
+ REGMAP_IRQ_REG(PF1550_IRQ_CHG, 0, IRQ_CHG),
+ REGMAP_IRQ_REG(PF1550_IRQ_REGULATOR, 0, IRQ_REGULATOR),
+ REGMAP_IRQ_REG(PF1550_IRQ_ONKEY, 0, IRQ_ONKEY),
+};
+
+static const struct regmap_irq_chip pf1550_irq_chip = {
+ .name = "pf1550",
+ .status_base = PF1550_PMIC_REG_INT_CATEGORY,
+ .init_ack_masked = 1,
+ .num_regs = 1,
+ .irqs = pf1550_irqs,
+ .num_irqs = ARRAY_SIZE(pf1550_irqs),
+};
+
+static const struct regmap_irq pf1550_regulator_irqs[] = {
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_LS, 0, PMIC_IRQ_SW1_LS),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_LS, 0, PMIC_IRQ_SW2_LS),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_LS, 0, PMIC_IRQ_SW3_LS),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW1_HS, 3, PMIC_IRQ_SW1_HS),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW2_HS, 3, PMIC_IRQ_SW2_HS),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_SW3_HS, 3, PMIC_IRQ_SW3_HS),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO1_FAULT, 16, PMIC_IRQ_LDO1_FAULT),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO2_FAULT, 16, PMIC_IRQ_LDO2_FAULT),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_LDO3_FAULT, 16, PMIC_IRQ_LDO3_FAULT),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_110, 24, PMIC_IRQ_TEMP_110),
+ REGMAP_IRQ_REG(PF1550_PMIC_IRQ_TEMP_125, 24, PMIC_IRQ_TEMP_125),
+};
+
+static const struct regmap_irq_chip pf1550_regulator_irq_chip = {
+ .name = "pf1550-regulator",
+ .status_base = PF1550_PMIC_REG_SW_INT_STAT0,
+ .ack_base = PF1550_PMIC_REG_SW_INT_STAT0,
+ .mask_base = PF1550_PMIC_REG_SW_INT_MASK0,
+ .use_ack = 1,
+ .init_ack_masked = 1,
+ .num_regs = 25,
+ .irqs = pf1550_regulator_irqs,
+ .num_irqs = ARRAY_SIZE(pf1550_regulator_irqs),
+};
+
+static const struct resource regulator_resources[] = {
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_LS),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_LS),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_LS),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW1_HS),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW2_HS),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_SW3_HS),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO1_FAULT),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO2_FAULT),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_LDO3_FAULT),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_110),
+ DEFINE_RES_IRQ(PF1550_PMIC_IRQ_TEMP_125),
+};
+
+static const struct regmap_irq pf1550_onkey_irqs[] = {
+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_PUSHI, 0, ONKEY_IRQ_PUSHI),
+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_1SI, 0, ONKEY_IRQ_1SI),
+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_2SI, 0, ONKEY_IRQ_2SI),
+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_3SI, 0, ONKEY_IRQ_3SI),
+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_4SI, 0, ONKEY_IRQ_4SI),
+ REGMAP_IRQ_REG(PF1550_ONKEY_IRQ_8SI, 0, ONKEY_IRQ_8SI),
+};
+
+static const struct regmap_irq_chip pf1550_onkey_irq_chip = {
+ .name = "pf1550-onkey",
+ .status_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
+ .ack_base = PF1550_PMIC_REG_ONKEY_INT_STAT0,
+ .mask_base = PF1550_PMIC_REG_ONKEY_INT_MASK0,
+ .use_ack = 1,
+ .init_ack_masked = 1,
+ .num_regs = 1,
+ .irqs = pf1550_onkey_irqs,
+ .num_irqs = ARRAY_SIZE(pf1550_onkey_irqs),
+};
+
+static const struct resource onkey_resources[] = {
+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_PUSHI),
+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_1SI),
+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_2SI),
+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_3SI),
+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_4SI),
+ DEFINE_RES_IRQ(PF1550_ONKEY_IRQ_8SI),
+};
+
+static const struct regmap_irq pf1550_charger_irqs[] = {
+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BAT2SOCI, 0, CHARG_IRQ_BAT2SOCI),
+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_BATI, 0, CHARG_IRQ_BATI),
+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_CHGI, 0, CHARG_IRQ_CHGI),
+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_VBUSI, 0, CHARG_IRQ_VBUSI),
+ REGMAP_IRQ_REG(PF1550_CHARG_IRQ_THMI, 0, CHARG_IRQ_THMI),
+};
+
+static const struct regmap_irq_chip pf1550_charger_irq_chip = {
+ .name = "pf1550-charger",
+ .status_base = PF1550_CHARG_REG_CHG_INT,
+ .ack_base = PF1550_CHARG_REG_CHG_INT,
+ .mask_base = PF1550_CHARG_REG_CHG_INT_MASK,
+ .use_ack = 1,
+ .init_ack_masked = 1,
+ .num_regs = 1,
+ .irqs = pf1550_charger_irqs,
+ .num_irqs = ARRAY_SIZE(pf1550_charger_irqs),
+};
+
+static const struct resource charger_resources[] = {
+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BAT2SOCI),
+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_BATI),
+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_CHGI),
+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_VBUSI),
+ DEFINE_RES_IRQ(PF1550_CHARG_IRQ_THMI),
+};
+
+static const struct mfd_cell pf1550_regulator_cell = {
+ .name = "pf1550-regulator",
+ .num_resources = ARRAY_SIZE(regulator_resources),
+ .resources = regulator_resources,
+};
+
+static const struct mfd_cell pf1550_onkey_cell = {
+ .name = "pf1550-onkey",
+ .num_resources = ARRAY_SIZE(onkey_resources),
+ .resources = onkey_resources,
+};
+
+static const struct mfd_cell pf1550_charger_cell = {
+ .name = "pf1550-charger",
+ .num_resources = ARRAY_SIZE(charger_resources),
+ .resources = charger_resources,
+};
+
+/*
+ * The PF1550 is shipped in variants of A0, A1,...A9. Each variant defines a
+ * configuration of the PMIC in a One-Time Programmable (OTP) memory.
+ * This memory is accessed indirectly by writing valid keys to specific
+ * registers of the PMIC. To read the OTP memory after writing the valid keys,
+ * the OTP register address to be read is written to pf1550 register 0xc4 and
+ * its value read from pf1550 register 0xc5.
+ */
+static int pf1550_read_otp(const struct pf1550_ddata *pf1550, unsigned int index,
+ unsigned int *val)
+{
+ int ret = 0;
+
+ ret = regmap_write(pf1550->regmap, PF1550_PMIC_REG_KEY, PF1550_OTP_PMIC_KEY);
+ if (ret)
+ goto read_err;
+
+ ret = regmap_write(pf1550->regmap, PF1550_CHARG_REG_CHGR_KEY2, PF1550_OTP_CHGR_KEY);
+ if (ret)
+ goto read_err;
+
+ ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_KEY3, PF1550_OTP_TEST_KEY);
+ if (ret)
+ goto read_err;
+
+ ret = regmap_write(pf1550->regmap, PF1550_TEST_REG_FMRADDR, index);
+ if (ret)
+ goto read_err;
+
+ ret = regmap_read(pf1550->regmap, PF1550_TEST_REG_FMRDATA, val);
+ if (ret)
+ goto read_err;
+
+ return 0;
+
+read_err:
+ return dev_err_probe(pf1550->dev, ret, "OTP reg %x not found!\n", index);
+}
+
+static int pf1550_i2c_probe(struct i2c_client *i2c)
+{
+ const struct mfd_cell *regulator = &pf1550_regulator_cell;
+ const struct mfd_cell *charger = &pf1550_charger_cell;
+ const struct mfd_cell *onkey = &pf1550_onkey_cell;
+ unsigned int reg_data = 0, otp_data = 0;
+ struct pf1550_ddata *pf1550;
+ struct irq_domain *domain;
+ int irq, ret = 0;
+
+ pf1550 = devm_kzalloc(&i2c->dev, sizeof(*pf1550), GFP_KERNEL);
+ if (!pf1550)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, pf1550);
+ pf1550->dev = &i2c->dev;
+ pf1550->irq = i2c->irq;
+
+ pf1550->regmap = devm_regmap_init_i2c(i2c, &pf1550_regmap_config);
+ if (IS_ERR(pf1550->regmap))
+ return dev_err_probe(pf1550->dev, PTR_ERR(pf1550->regmap),
+ "failed to allocate register map\n");
+
+ ret = regmap_read(pf1550->regmap, PF1550_PMIC_REG_DEVICE_ID, &reg_data);
+ if (ret < 0)
+ return dev_err_probe(pf1550->dev, ret, "cannot read chip ID\n");
+ if (reg_data != PF1550_DEVICE_ID)
+ return dev_err_probe(pf1550->dev, -ENODEV, "invalid device ID: 0x%02x\n", reg_data);
+
+ /* Regulator DVS for SW2 */
+ ret = pf1550_read_otp(pf1550, PF1550_OTP_SW2_SW3, &otp_data);
+ if (ret)
+ return ret;
+
+ /* When clear, DVS should be enabled */
+ if (!(otp_data & OTP_SW2_DVS_ENB))
+ pf1550->dvs2_enable = true;
+
+ /* Regulator DVS for SW1 */
+ ret = pf1550_read_otp(pf1550, PF1550_OTP_SW1_SW2, &otp_data);
+ if (ret)
+ return ret;
+
+ if (!(otp_data & OTP_SW1_DVS_ENB))
+ pf1550->dvs1_enable = true;
+
+ /* Add top level interrupts */
+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, pf1550->irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING,
+ 0, &pf1550_irq_chip,
+ &pf1550->irq_data);
+ if (ret)
+ return ret;
+
+ /* Add regulator */
+ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_REGULATOR);
+ if (irq < 0)
+ return dev_err_probe(pf1550->dev, irq,
+ "Failed to get parent vIRQ(%d) for chip %s\n",
+ PF1550_IRQ_REGULATOR, pf1550_irq_chip.name);
+
+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING, 0,
+ &pf1550_regulator_irq_chip,
+ &pf1550->irq_data_regulator);
+ if (ret)
+ return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",
+ pf1550_regulator_irq_chip.name);
+
+ domain = regmap_irq_get_domain(pf1550->irq_data_regulator);
+
+ ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, regulator, 1, NULL, 0, domain);
+ if (ret)
+ return ret;
+
+ /* Add onkey */
+ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_ONKEY);
+ if (irq < 0)
+ return dev_err_probe(pf1550->dev, irq,
+ "Failed to get parent vIRQ(%d) for chip %s\n",
+ PF1550_IRQ_ONKEY, pf1550_irq_chip.name);
+
+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING, 0,
+ &pf1550_onkey_irq_chip,
+ &pf1550->irq_data_onkey);
+ if (ret)
+ return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",
+ pf1550_onkey_irq_chip.name);
+
+ domain = regmap_irq_get_domain(pf1550->irq_data_onkey);
+
+ ret = devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, onkey, 1, NULL, 0, domain);
+ if (ret)
+ return ret;
+
+ /* Add battery charger */
+ irq = regmap_irq_get_virq(pf1550->irq_data, PF1550_IRQ_CHG);
+ if (irq < 0)
+ return dev_err_probe(pf1550->dev, irq,
+ "Failed to get parent vIRQ(%d) for chip %s\n",
+ PF1550_IRQ_CHG, pf1550_irq_chip.name);
+
+ ret = devm_regmap_add_irq_chip(pf1550->dev, pf1550->regmap, irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING, 0,
+ &pf1550_charger_irq_chip,
+ &pf1550->irq_data_charger);
+ if (ret)
+ return dev_err_probe(pf1550->dev, ret, "Failed to add %s IRQ chip\n",
+ pf1550_charger_irq_chip.name);
+
+ domain = regmap_irq_get_domain(pf1550->irq_data_charger);
+
+ return devm_mfd_add_devices(pf1550->dev, PLATFORM_DEVID_NONE, charger, 1, NULL, 0, domain);
+}
+
+static int pf1550_suspend(struct device *dev)
+{
+ struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(pf1550->irq);
+ disable_irq(pf1550->irq);
+ }
+
+ return 0;
+}
+
+static int pf1550_resume(struct device *dev)
+{
+ struct pf1550_ddata *pf1550 = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(pf1550->irq);
+ enable_irq(pf1550->irq);
+ }
+
+ return 0;
+}
+static DEFINE_SIMPLE_DEV_PM_OPS(pf1550_pm, pf1550_suspend, pf1550_resume);
+
+static const struct i2c_device_id pf1550_i2c_id[] = {
+ { "pf1550" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(i2c, pf1550_i2c_id);
+
+static const struct of_device_id pf1550_dt_match[] = {
+ { .compatible = "nxp,pf1550" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pf1550_dt_match);
+
+static struct i2c_driver pf1550_i2c_driver = {
+ .driver = {
+ .name = "pf1550",
+ .pm = pm_sleep_ptr(&pf1550_pm),
+ .of_match_table = pf1550_dt_match,
+ },
+ .probe = pf1550_i2c_probe,
+ .id_table = pf1550_i2c_id,
+};
+module_i2c_driver(pf1550_i2c_driver);
+
+MODULE_DESCRIPTION("NXP PF1550 core driver");
+MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/qnap-mcu.c b/drivers/mfd/qnap-mcu.c
index 4ec1f4cf902f..f81c69f22254 100644
--- a/drivers/mfd/qnap-mcu.c
+++ b/drivers/mfd/qnap-mcu.c
@@ -19,6 +19,7 @@
/* The longest command found so far is 5 bytes long */
#define QNAP_MCU_MAX_CMD_SIZE 5
#define QNAP_MCU_MAX_DATA_SIZE 36
+#define QNAP_MCU_ERROR_SIZE 2
#define QNAP_MCU_CHECKSUM_SIZE 1
#define QNAP_MCU_RX_BUFFER_SIZE \
@@ -78,6 +79,13 @@ static u8 qnap_mcu_csum(const u8 *buf, size_t size)
return csum;
}
+static bool qnap_mcu_verify_checksum(const u8 *buf, size_t size)
+{
+ u8 crc = qnap_mcu_csum(buf, size - QNAP_MCU_CHECKSUM_SIZE);
+
+ return crc == buf[size - QNAP_MCU_CHECKSUM_SIZE];
+}
+
static int qnap_mcu_write(struct qnap_mcu *mcu, const u8 *data, u8 data_size)
{
unsigned char tx[QNAP_MCU_TX_BUFFER_SIZE];
@@ -96,6 +104,48 @@ static int qnap_mcu_write(struct qnap_mcu *mcu, const u8 *data, u8 data_size)
return serdev_device_write(mcu->serdev, tx, length, HZ);
}
+static bool qnap_mcu_is_error_msg(size_t size)
+{
+ return (size == QNAP_MCU_ERROR_SIZE + QNAP_MCU_CHECKSUM_SIZE);
+}
+
+static bool qnap_mcu_reply_is_generic_error(unsigned char *buf, size_t size)
+{
+ if (!qnap_mcu_is_error_msg(size))
+ return false;
+
+ if (buf[0] == '@' && buf[1] == '9')
+ return true;
+
+ return false;
+}
+
+static bool qnap_mcu_reply_is_checksum_error(unsigned char *buf, size_t size)
+{
+ if (!qnap_mcu_is_error_msg(size))
+ return false;
+
+ if (buf[0] == '@' && buf[1] == '8')
+ return true;
+
+ return false;
+}
+
+static bool qnap_mcu_reply_is_any_error(struct qnap_mcu *mcu, unsigned char *buf, size_t size)
+{
+ if (qnap_mcu_reply_is_generic_error(buf, size)) {
+ dev_err(&mcu->serdev->dev, "Controller sent generic error response\n");
+ return true;
+ }
+
+ if (qnap_mcu_reply_is_checksum_error(buf, size)) {
+ dev_err(&mcu->serdev->dev, "Controller received invalid checksum for the command\n");
+ return true;
+ }
+
+ return false;
+}
+
static size_t qnap_mcu_receive_buf(struct serdev_device *serdev, const u8 *buf, size_t size)
{
struct device *dev = &serdev->dev;
@@ -130,6 +180,24 @@ static size_t qnap_mcu_receive_buf(struct serdev_device *serdev, const u8 *buf,
}
/*
+ * We received everything the uart had to offer for now.
+ * This could mean that either the uart will send more in a 2nd
+ * receive run, or that the MCU cut the reply short because it
+ * sent an error code instead of the expected reply.
+ *
+ * So check if the received data has the correct size for an error
+ * reply and if it matches, is an actual error code.
+ */
+ if (qnap_mcu_is_error_msg(reply->received) &&
+ qnap_mcu_verify_checksum(reply->data, reply->received) &&
+ qnap_mcu_reply_is_any_error(mcu, reply->data, reply->received)) {
+ /* The reply was an error code, we're done */
+ reply->length = 0;
+
+ complete(&reply->done);
+ }
+
+ /*
* The only way to get out of the above loop and end up here
* is through consuming all of the supplied data, so here we
* report that we processed it all.
@@ -150,7 +218,6 @@ int qnap_mcu_exec(struct qnap_mcu *mcu,
size_t length = reply_data_size + QNAP_MCU_CHECKSUM_SIZE;
struct qnap_mcu_reply *reply = &mcu->reply;
int ret = 0;
- u8 crc;
if (length > sizeof(rx)) {
dev_err(&mcu->serdev->dev, "expected data too big for receive buffer");
@@ -175,12 +242,14 @@ int qnap_mcu_exec(struct qnap_mcu *mcu,
return -ETIMEDOUT;
}
- crc = qnap_mcu_csum(rx, reply_data_size);
- if (crc != rx[reply_data_size]) {
- dev_err(&mcu->serdev->dev, "Invalid Checksum received\n");
- return -EIO;
+ if (!qnap_mcu_verify_checksum(rx, reply->received)) {
+ dev_err(&mcu->serdev->dev, "Invalid Checksum received from controller\n");
+ return -EPROTO;
}
+ if (qnap_mcu_reply_is_any_error(mcu, rx, reply->received))
+ return -EPROTO;
+
memcpy(reply_data, rx, reply_data_size);
return 0;
@@ -264,6 +333,7 @@ static const struct qnap_mcu_variant qnap_ts433_mcu = {
};
static struct mfd_cell qnap_mcu_cells[] = {
+ { .name = "qnap-mcu-eeprom", },
{ .name = "qnap-mcu-input", },
{ .name = "qnap-mcu-leds", },
{ .name = "qnap-mcu-hwmon", }
diff --git a/drivers/mfd/rohm-bd718x7.c b/drivers/mfd/rohm-bd718x7.c
index 25e494a93d48..ff714fd4f54d 100644
--- a/drivers/mfd/rohm-bd718x7.c
+++ b/drivers/mfd/rohm-bd718x7.c
@@ -72,14 +72,13 @@ static const struct regmap_irq_chip bd718xx_irq_chip = {
.init_ack_masked = true,
};
-static const struct regmap_range pmic_status_range = {
- .range_min = BD718XX_REG_IRQ,
- .range_max = BD718XX_REG_POW_STATE,
+static const struct regmap_range pmic_status_range[] = {
+ regmap_reg_range(BD718XX_REG_IRQ, BD718XX_REG_POW_STATE),
};
static const struct regmap_access_table volatile_regs = {
- .yes_ranges = &pmic_status_range,
- .n_yes_ranges = 1,
+ .yes_ranges = &pmic_status_range[0],
+ .n_yes_ranges = ARRAY_SIZE(pmic_status_range),
};
static const struct regmap_config bd718xx_regmap_config = {
diff --git a/drivers/mfd/sec-acpm.c b/drivers/mfd/sec-acpm.c
index 8b31c816d65b..36622069a788 100644
--- a/drivers/mfd/sec-acpm.c
+++ b/drivers/mfd/sec-acpm.c
@@ -325,11 +325,6 @@ static struct regmap *sec_pmic_acpm_regmap_init(struct device *dev,
return regmap;
}
-static void sec_pmic_acpm_mask_common_irqs(void *regmap_common)
-{
- regmap_write(regmap_common, S2MPG10_COMMON_INT_MASK, S2MPG10_COMMON_INT_SRC);
-}
-
static int sec_pmic_acpm_probe(struct platform_device *pdev)
{
struct regmap *regmap_common, *regmap_pmic, *regmap;
@@ -360,15 +355,10 @@ static int sec_pmic_acpm_probe(struct platform_device *pdev)
shared_ctx->speedy_channel = pdata->speedy_channel;
regmap_common = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_COMMON,
- pdata->regmap_cfg_common, false);
+ pdata->regmap_cfg_common, true);
if (IS_ERR(regmap_common))
return PTR_ERR(regmap_common);
- /* Mask all interrupts from 'common' block, until successful init */
- ret = regmap_write(regmap_common, S2MPG10_COMMON_INT_MASK, S2MPG10_COMMON_INT_SRC);
- if (ret)
- return dev_err_probe(dev, ret, "failed to mask common block interrupts\n");
-
regmap_pmic = sec_pmic_acpm_regmap_init(dev, shared_ctx, SEC_PMIC_ACPM_ACCESSTYPE_PMIC,
pdata->regmap_cfg_pmic, false);
if (IS_ERR(regmap_pmic))
@@ -391,17 +381,6 @@ static int sec_pmic_acpm_probe(struct platform_device *pdev)
if (device_property_read_bool(dev, "wakeup-source"))
devm_device_init_wakeup(dev);
- /* Unmask PMIC interrupt from 'common' block, now that everything is in place. */
- ret = regmap_clear_bits(regmap_common, S2MPG10_COMMON_INT_MASK,
- S2MPG10_COMMON_INT_SRC_PMIC);
- if (ret)
- return dev_err_probe(dev, ret, "failed to unmask PMIC interrupt\n");
-
- /* Mask all interrupts from 'common' block on shutdown */
- ret = devm_add_action_or_reset(dev, sec_pmic_acpm_mask_common_irqs, regmap_common);
- if (ret)
- return ret;
-
return 0;
}
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c
index c5c80b1ba104..74ac70002d1f 100644
--- a/drivers/mfd/sec-irq.c
+++ b/drivers/mfd/sec-irq.c
@@ -20,6 +20,12 @@
#include "sec-core.h"
static const struct regmap_irq s2mpg10_irqs[] = {
+ REGMAP_IRQ_REG(S2MPG10_COMMON_IRQ_PMIC, 0, S2MPG10_COMMON_INT_SRC_PMIC),
+ /* No documentation or other reference for remaining bits */
+ REGMAP_IRQ_REG(S2MPG10_COMMON_IRQ_UNUSED, 0, GENMASK(7, 1)),
+};
+
+static const struct regmap_irq s2mpg10_pmic_irqs[] = {
REGMAP_IRQ_REG(S2MPG10_IRQ_PWRONF, 0, S2MPG10_IRQ_PWRONF_MASK),
REGMAP_IRQ_REG(S2MPG10_IRQ_PWRONR, 0, S2MPG10_IRQ_PWRONR_MASK),
REGMAP_IRQ_REG(S2MPG10_IRQ_JIGONBF, 0, S2MPG10_IRQ_JIGONBF_MASK),
@@ -183,11 +189,20 @@ static const struct regmap_irq s5m8767_irqs[] = {
/* All S2MPG10 interrupt sources are read-only and don't require clearing */
static const struct regmap_irq_chip s2mpg10_irq_chip = {
.name = "s2mpg10",
+ .status_base = S2MPG10_COMMON_INT,
+ .mask_base = S2MPG10_COMMON_INT_MASK,
+ .num_regs = 1,
.irqs = s2mpg10_irqs,
.num_irqs = ARRAY_SIZE(s2mpg10_irqs),
- .num_regs = 6,
+};
+
+static const struct regmap_irq_chip s2mpg10_irq_chip_pmic = {
+ .name = "s2mpg10-pmic",
.status_base = S2MPG10_PMIC_INT1,
.mask_base = S2MPG10_PMIC_INT1M,
+ .num_regs = 6,
+ .irqs = s2mpg10_pmic_irqs,
+ .num_irqs = ARRAY_SIZE(s2mpg10_pmic_irqs),
};
static const struct regmap_irq_chip s2mps11_irq_chip = {
@@ -253,6 +268,59 @@ static const struct regmap_irq_chip s5m8767_irq_chip = {
.ack_base = S5M8767_REG_INT1,
};
+static int s2mpg1x_add_chained_irq_chip(struct device *dev, struct regmap *regmap, int pirq,
+ struct regmap_irq_chip_data *parent,
+ const struct regmap_irq_chip *chip,
+ struct regmap_irq_chip_data **data)
+{
+ int irq, ret;
+
+ irq = regmap_irq_get_virq(parent, pirq);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed to get parent vIRQ(%d) for chip %s\n", pirq,
+ chip->name);
+
+ ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT | IRQF_SHARED, 0, chip, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add %s IRQ chip\n", chip->name);
+
+ return 0;
+}
+
+static int sec_irq_init_s2mpg1x(struct sec_pmic_dev *sec_pmic)
+{
+ const struct regmap_irq_chip *irq_chip, *chained_irq_chip;
+ struct regmap_irq_chip_data *irq_data;
+ struct regmap *regmap_common;
+ int chained_pirq;
+ int ret;
+
+ switch (sec_pmic->device_type) {
+ case S2MPG10:
+ irq_chip = &s2mpg10_irq_chip;
+ chained_irq_chip = &s2mpg10_irq_chip_pmic;
+ chained_pirq = S2MPG10_COMMON_IRQ_PMIC;
+ break;
+ default:
+ return dev_err_probe(sec_pmic->dev, -EINVAL, "Unsupported device type %d\n",
+ sec_pmic->device_type);
+ }
+
+ regmap_common = dev_get_regmap(sec_pmic->dev, "common");
+ if (!regmap_common)
+ return dev_err_probe(sec_pmic->dev, -EINVAL, "No 'common' regmap %d\n",
+ sec_pmic->device_type);
+
+ ret = devm_regmap_add_irq_chip(sec_pmic->dev, regmap_common, sec_pmic->irq, IRQF_ONESHOT, 0,
+ irq_chip, &irq_data);
+ if (ret)
+ return dev_err_probe(sec_pmic->dev, ret, "Failed to add %s IRQ chip\n",
+ irq_chip->name);
+
+ return s2mpg1x_add_chained_irq_chip(sec_pmic->dev, sec_pmic->regmap_pmic, chained_pirq,
+ irq_data, chained_irq_chip, &sec_pmic->irq_data);
+}
+
int sec_irq_init(struct sec_pmic_dev *sec_pmic)
{
const struct regmap_irq_chip *sec_irq_chip;
@@ -268,8 +336,7 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic)
sec_irq_chip = &s2mps14_irq_chip;
break;
case S2MPG10:
- sec_irq_chip = &s2mpg10_irq_chip;
- break;
+ return sec_irq_init_s2mpg1x(sec_pmic);
case S2MPS11X:
sec_irq_chip = &s2mps11_irq_chip;
break;
diff --git a/drivers/mfd/simple-mfd-i2c.c b/drivers/mfd/simple-mfd-i2c.c
index 0a607a1e3ca1..8b751d8e3b5a 100644
--- a/drivers/mfd/simple-mfd-i2c.c
+++ b/drivers/mfd/simple-mfd-i2c.c
@@ -15,12 +15,18 @@
* will be subsequently registered.
*/
+#include <linux/array_size.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
#include <linux/i2c.h>
-#include <linux/kernel.h>
#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/stddef.h>
#include "simple-mfd-i2c.h"
@@ -114,11 +120,11 @@ static const struct of_device_id simple_mfd_i2c_of_match[] = {
{ .compatible = "fsl,lx2160aqds-fpga" },
{ .compatible = "fsl,lx2160ardb-fpga" },
{ .compatible = "kontron,sl28cpld" },
- { .compatible = "maxim,max5970", .data = &maxim_max5970},
- { .compatible = "maxim,max5978", .data = &maxim_max5970},
- { .compatible = "maxim,max77705-battery", .data = &maxim_mon_max77705},
- { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a},
- { .compatible = "spacemit,p1", .data = &spacemit_p1, },
+ { .compatible = "maxim,max5970", .data = &maxim_max5970 },
+ { .compatible = "maxim,max5978", .data = &maxim_max5970 },
+ { .compatible = "maxim,max77705-battery", .data = &maxim_mon_max77705 },
+ { .compatible = "silergy,sy7636a", .data = &silergy_sy7636a },
+ { .compatible = "spacemit,p1", .data = &spacemit_p1 },
{}
};
MODULE_DEVICE_TABLE(of, simple_mfd_i2c_of_match);
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index ae71a2710bed..e5d5def594f6 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -183,7 +183,7 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
if (create_regmap)
syscon = of_syscon_register(np, check_res);
else
- syscon = ERR_PTR(-EINVAL);
+ syscon = ERR_PTR(-EPROBE_DEFER);
}
mutex_unlock(&syscon_list_lock);
diff --git a/drivers/mfd/tqmx86.c b/drivers/mfd/tqmx86.c
index 1cba3b67b0fb..1c2fe3f91238 100644
--- a/drivers/mfd/tqmx86.c
+++ b/drivers/mfd/tqmx86.c
@@ -43,6 +43,8 @@
#define TQMX86_REG_BOARD_ID_E40C2 15
#define TQMX86_REG_BOARD_ID_130UC 16
#define TQMX86_REG_BOARD_ID_E41S 19
+#define TQMX86_REG_BOARD_ID_CU1_HPCM 24
+#define TQMX86_REG_BOARD_ID_CU2_HPCM 25
#define TQMX86_REG_BOARD_REV 0x01
#define TQMX86_REG_IO_EXT_INT 0x06
#define TQMX86_REG_IO_EXT_INT_NONE 0
@@ -165,6 +167,10 @@ static const char *tqmx86_board_id_to_name(u8 board_id, u8 sauc)
return "TQMx130UC";
case TQMX86_REG_BOARD_ID_E41S:
return "TQMxE41S";
+ case TQMX86_REG_BOARD_ID_CU1_HPCM:
+ return "TQMxCU1-HPCM";
+ case TQMX86_REG_BOARD_ID_CU2_HPCM:
+ return "TQMxCU2-HPCM";
default:
return "Unknown";
}
@@ -185,6 +191,8 @@ static int tqmx86_board_id_to_clk_rate(struct device *dev, u8 board_id)
case TQMX86_REG_BOARD_ID_E40C2:
case TQMX86_REG_BOARD_ID_130UC:
case TQMX86_REG_BOARD_ID_E41S:
+ case TQMX86_REG_BOARD_ID_CU1_HPCM:
+ case TQMX86_REG_BOARD_ID_CU2_HPCM:
return 24000;
case TQMX86_REG_BOARD_ID_E39MS:
case TQMX86_REG_BOARD_ID_E39C1:
diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c
deleted file mode 100644
index 2f185e93318e..000000000000
--- a/drivers/mfd/wl1273-core.c
+++ /dev/null
@@ -1,262 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * MFD driver for wl1273 FM radio and audio codec submodules.
- *
- * Copyright (C) 2011 Nokia Corporation
- * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com>
- */
-
-#include <linux/mfd/wl1273-core.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#define DRIVER_DESC "WL1273 FM Radio Core"
-
-static const struct i2c_device_id wl1273_driver_id_table[] = {
- { WL1273_FM_DRIVER_NAME },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table);
-
-static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value)
-{
- struct i2c_client *client = core->client;
- u8 b[2];
- int r;
-
- r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b);
- if (r != 2) {
- dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg);
- return -EREMOTEIO;
- }
-
- *value = (u16)b[0] << 8 | b[1];
-
- return 0;
-}
-
-static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param)
-{
- struct i2c_client *client = core->client;
- u8 buf[] = { (param >> 8) & 0xff, param & 0xff };
- int r;
-
- r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf);
- if (r) {
- dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd);
- return r;
- }
-
- return 0;
-}
-
-static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len)
-{
- struct i2c_client *client = core->client;
- struct i2c_msg msg;
- int r;
-
- msg.addr = client->addr;
- msg.flags = 0;
- msg.buf = data;
- msg.len = len;
-
- r = i2c_transfer(client->adapter, &msg, 1);
- if (r != 1) {
- dev_err(&client->dev, "%s: write error.\n", __func__);
- return -EREMOTEIO;
- }
-
- return 0;
-}
-
-/**
- * wl1273_fm_set_audio() - Set audio mode.
- * @core: A pointer to the device struct.
- * @new_mode: The new audio mode.
- *
- * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG.
- */
-static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode)
-{
- int r = 0;
-
- if (core->mode == WL1273_MODE_OFF ||
- core->mode == WL1273_MODE_SUSPENDED)
- return -EPERM;
-
- if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) {
- r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET,
- WL1273_PCM_DEF_MODE);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
- core->i2s_mode);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
- WL1273_AUDIO_ENABLE_I2S);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_RX &&
- new_mode == WL1273_AUDIO_ANALOG) {
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
- WL1273_AUDIO_ENABLE_ANALOG);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_TX &&
- new_mode == WL1273_AUDIO_DIGITAL) {
- r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
- core->i2s_mode);
- if (r)
- goto out;
-
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
- WL1273_AUDIO_IO_SET_I2S);
- if (r)
- goto out;
-
- } else if (core->mode == WL1273_MODE_TX &&
- new_mode == WL1273_AUDIO_ANALOG) {
- r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
- WL1273_AUDIO_IO_SET_ANALOG);
- if (r)
- goto out;
- }
-
- core->audio_mode = new_mode;
-out:
- return r;
-}
-
-/**
- * wl1273_fm_set_volume() - Set volume.
- * @core: A pointer to the device struct.
- * @volume: The new volume value.
- */
-static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume)
-{
- int r;
-
- if (volume > WL1273_MAX_VOLUME)
- return -EINVAL;
-
- if (core->volume == volume)
- return 0;
-
- r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume);
- if (r)
- return r;
-
- core->volume = volume;
- return 0;
-}
-
-static int wl1273_core_probe(struct i2c_client *client)
-{
- struct wl1273_fm_platform_data *pdata = dev_get_platdata(&client->dev);
- struct wl1273_core *core;
- struct mfd_cell *cell;
- int children = 0;
- int r = 0;
-
- dev_dbg(&client->dev, "%s\n", __func__);
-
- if (!pdata) {
- dev_err(&client->dev, "No platform data.\n");
- return -EINVAL;
- }
-
- if (!(pdata->children & WL1273_RADIO_CHILD)) {
- dev_err(&client->dev, "Cannot function without radio child.\n");
- return -EINVAL;
- }
-
- core = devm_kzalloc(&client->dev, sizeof(*core), GFP_KERNEL);
- if (!core)
- return -ENOMEM;
-
- core->pdata = pdata;
- core->client = client;
- mutex_init(&core->lock);
-
- i2c_set_clientdata(client, core);
-
- dev_dbg(&client->dev, "%s: Have V4L2.\n", __func__);
-
- cell = &core->cells[children];
- cell->name = "wl1273_fm_radio";
- cell->platform_data = &core;
- cell->pdata_size = sizeof(core);
- children++;
-
- core->read = wl1273_fm_read_reg;
- core->write = wl1273_fm_write_cmd;
- core->write_data = wl1273_fm_write_data;
- core->set_audio = wl1273_fm_set_audio;
- core->set_volume = wl1273_fm_set_volume;
-
- if (pdata->children & WL1273_CODEC_CHILD) {
- cell = &core->cells[children];
-
- dev_dbg(&client->dev, "%s: Have codec.\n", __func__);
- cell->name = "wl1273-codec";
- cell->platform_data = &core;
- cell->pdata_size = sizeof(core);
- children++;
- }
-
- dev_dbg(&client->dev, "%s: number of children: %d.\n",
- __func__, children);
-
- r = devm_mfd_add_devices(&client->dev, -1, core->cells,
- children, NULL, 0, NULL);
- if (r)
- goto err;
-
- return 0;
-
-err:
- pdata->free_resources();
-
- dev_dbg(&client->dev, "%s\n", __func__);
-
- return r;
-}
-
-static struct i2c_driver wl1273_core_driver = {
- .driver = {
- .name = WL1273_FM_DRIVER_NAME,
- },
- .probe = wl1273_core_probe,
- .id_table = wl1273_driver_id_table,
-};
-
-static int __init wl1273_core_init(void)
-{
- int r;
-
- r = i2c_add_driver(&wl1273_core_driver);
- if (r) {
- pr_err(WL1273_FM_DRIVER_NAME
- ": driver registration failed\n");
- return r;
- }
-
- return r;
-}
-
-static void __exit wl1273_core_exit(void)
-{
- i2c_del_driver(&wl1273_core_driver);
-}
-late_initcall(wl1273_core_init);
-module_exit(wl1273_core_exit);
-
-MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
diff --git a/drivers/misc/amd-sbi/Kconfig b/drivers/misc/amd-sbi/Kconfig
index 4aae0733d0fc..ab594908cb4a 100644
--- a/drivers/misc/amd-sbi/Kconfig
+++ b/drivers/misc/amd-sbi/Kconfig
@@ -2,9 +2,11 @@
config AMD_SBRMI_I2C
tristate "AMD side band RMI support"
depends on I2C
+ depends on ARM || ARM64 || COMPILE_TEST
select REGMAP_I2C
help
Side band RMI over I2C support for AMD out of band management.
+ This driver is intended to run on the BMC, not the managed node.
This driver can also be built as a module. If so, the module will
be called sbrmi-i2c.
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index 621bce7e101c..ee652ef01534 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -381,6 +381,8 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
}
spin_unlock(&fl->lock);
+ dma_buf_put(buf);
+
return ret;
}
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index bc40b940ae21..a4f75dc36929 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -120,6 +120,8 @@
#define MEI_DEV_ID_PTL_H 0xE370 /* Panther Lake H */
#define MEI_DEV_ID_PTL_P 0xE470 /* Panther Lake P */
+#define MEI_DEV_ID_WCL_P 0x4D70 /* Wildcat Lake P */
+
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c
index 77686b108d3c..78717ee8ac9a 100644
--- a/drivers/misc/mei/mei_lb.c
+++ b/drivers/misc/mei/mei_lb.c
@@ -134,8 +134,7 @@ static bool mei_lb_check_response(const struct device *dev, ssize_t bytes,
return true;
}
-static int mei_lb_push_payload(struct device *dev,
- enum intel_lb_type type, u32 flags,
+static int mei_lb_push_payload(struct device *dev, u32 type, u32 flags,
const void *payload, size_t payload_size)
{
struct mei_cl_device *cldev;
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index b108a7c22388..73cad914be9f 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -127,6 +127,8 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_PTL_H, MEI_ME_PCH15_CFG)},
{MEI_PCI_DEVICE(MEI_DEV_ID_PTL_P, MEI_ME_PCH15_CFG)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_WCL_P, MEI_ME_PCH15_CFG)},
+
/* required last entry */
{0, }
};
@@ -221,6 +223,10 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw->mem_addr = pcim_iomap_table(pdev)[0];
hw->read_fws = mei_me_read_fws;
+ err = mei_register(dev, &pdev->dev);
+ if (err)
+ goto end;
+
pci_enable_msi(pdev);
hw->irq = pdev->irq;
@@ -235,13 +241,9 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err) {
dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n",
pdev->irq);
- goto end;
+ goto deregister;
}
- err = mei_register(dev, &pdev->dev);
- if (err)
- goto release_irq;
-
if (mei_start(dev)) {
dev_err(&pdev->dev, "init hw failure.\n");
err = -ENODEV;
@@ -281,11 +283,10 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
deregister:
- mei_deregister(dev);
-release_irq:
mei_cancel_work(dev);
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
+ mei_deregister(dev);
end:
dev_err(&pdev->dev, "initialization failed.\n");
return err;
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index c9eb5c5393e4..98d1bc2c7f4b 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -87,6 +87,10 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw = to_txe_hw(dev);
hw->mem_addr = pcim_iomap_table(pdev);
+ err = mei_register(dev, &pdev->dev);
+ if (err)
+ goto end;
+
pci_enable_msi(pdev);
/* clear spurious interrupts */
@@ -106,22 +110,18 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err) {
dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n",
pdev->irq);
- goto end;
+ goto deregister;
}
if (mei_start(dev)) {
dev_err(&pdev->dev, "init hw failure.\n");
err = -ENODEV;
- goto release_irq;
+ goto deregister;
}
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
- err = mei_register(dev, &pdev->dev);
- if (err)
- goto stop;
-
pci_set_drvdata(pdev, dev);
/*
@@ -144,12 +144,11 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
-stop:
- mei_stop(dev);
-release_irq:
+deregister:
mei_cancel_work(dev);
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
+ mei_deregister(dev);
end:
dev_err(&pdev->dev, "initialization failed.\n");
return err;
diff --git a/drivers/misc/mei/platform-vsc.c b/drivers/misc/mei/platform-vsc.c
index 288e7b72e942..9787b9cee71c 100644
--- a/drivers/misc/mei/platform-vsc.c
+++ b/drivers/misc/mei/platform-vsc.c
@@ -362,28 +362,27 @@ static int mei_vsc_probe(struct platform_device *pdev)
ret = mei_register(mei_dev, dev);
if (ret)
- goto err_dereg;
+ goto err;
ret = mei_start(mei_dev);
if (ret) {
dev_err_probe(dev, ret, "init hw failed\n");
- goto err_cancel;
+ goto err;
}
pm_runtime_enable(mei_dev->parent);
return 0;
-err_dereg:
- mei_deregister(mei_dev);
-
-err_cancel:
+err:
mei_cancel_work(mei_dev);
vsc_tp_register_event_cb(tp, NULL, NULL);
mei_disable_interrupts(mei_dev);
+ mei_deregister(mei_dev);
+
return ret;
}
diff --git a/drivers/misc/ntsync.c b/drivers/misc/ntsync.c
index 999026a1ae04..9087f045e362 100644
--- a/drivers/misc/ntsync.c
+++ b/drivers/misc/ntsync.c
@@ -721,21 +721,12 @@ static struct ntsync_obj *ntsync_alloc_obj(struct ntsync_device *dev,
static int ntsync_obj_get_fd(struct ntsync_obj *obj)
{
- struct file *file;
- int fd;
-
- fd = get_unused_fd_flags(O_CLOEXEC);
- if (fd < 0)
- return fd;
- file = anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR);
- if (IS_ERR(file)) {
- put_unused_fd(fd);
- return PTR_ERR(file);
- }
- obj->file = file;
- fd_install(fd, file);
-
- return fd;
+ FD_PREPARE(fdf, O_CLOEXEC,
+ anon_inode_getfile("ntsync", &ntsync_obj_fops, obj, O_RDWR));
+ if (fdf.err)
+ return fdf.err;
+ obj->file = fd_prepare_file(fdf);
+ return fd_publish(fdf);
}
static int ntsync_create_sem(struct ntsync_device *dev, void __user *argp)
diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c
index 6df51ee8db62..cc1d18b3df5c 100644
--- a/drivers/misc/vmw_balloon.c
+++ b/drivers/misc/vmw_balloon.c
@@ -1737,7 +1737,7 @@ static int vmballoon_migratepage(struct balloon_dev_info *b_dev_info,
{
unsigned long status, flags;
struct vmballoon *b;
- int ret;
+ int ret = 0;
b = container_of(b_dev_info, struct vmballoon, b_dev_info);
@@ -1796,17 +1796,15 @@ static int vmballoon_migratepage(struct balloon_dev_info *b_dev_info,
* A failure happened. While we can deflate the page we just
* inflated, this deflation can also encounter an error. Instead
* we will decrease the size of the balloon to reflect the
- * change and report failure.
+ * change.
*/
atomic64_dec(&b->size);
- ret = -EBUSY;
} else {
/*
* Success. Take a reference for the page, and we will add it to
* the list after acquiring the lock.
*/
get_page(newpage);
- ret = 0;
}
/* Update the balloon list under the @pages_lock */
@@ -1817,7 +1815,7 @@ static int vmballoon_migratepage(struct balloon_dev_info *b_dev_info,
* If we succeed just insert it to the list and update the statistics
* under the lock.
*/
- if (!ret) {
+ if (status == VMW_BALLOON_SUCCESS) {
balloon_page_insert(&b->b_dev_info, newpage);
__count_vm_event(BALLOON_MIGRATE);
}
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index c0ffe0817fd4..fb6eb2d79b4f 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -349,10 +349,10 @@ static umode_t mmc_disk_attrs_is_visible(struct kobject *kobj,
if (a == &dev_attr_ro_lock_until_next_power_on.attr &&
(md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
md->queue.card->ext_csd.boot_ro_lockable) {
- mode = S_IRUGO;
+ mode = 0444;
if (!(md->queue.card->ext_csd.boot_ro_lock &
EXT_CSD_BOOT_WP_B_PWR_WP_DIS))
- mode |= S_IWUSR;
+ mode |= 0200;
}
mmc_blk_put(md);
@@ -957,7 +957,6 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
u32 result;
__be32 *blocks;
u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4;
- unsigned int noio_flag;
struct mmc_request mrq = {};
struct mmc_command cmd = {};
@@ -982,9 +981,7 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
mrq.cmd = &cmd;
mrq.data = &data;
- noio_flag = memalloc_noio_save();
- blocks = kmalloc(resp_sz, GFP_KERNEL);
- memalloc_noio_restore(noio_flag);
+ blocks = kmalloc(resp_sz, GFP_NOIO);
if (!blocks)
return -ENOMEM;
@@ -3194,7 +3191,7 @@ static void mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
if (mmc_card_mmc(card)) {
md->ext_csd_dentry =
- debugfs_create_file("ext_csd", S_IRUSR, root, card,
+ debugfs_create_file("ext_csd", 0400, root, card,
&mmc_dbg_ext_csd_fops);
}
}
@@ -3275,7 +3272,8 @@ static int mmc_blk_probe(struct mmc_card *card)
mmc_fixup_device(card, mmc_blk_fixups);
card->complete_wq = alloc_workqueue("mmc_complete",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
+ 0);
if (!card->complete_wq) {
pr_err("Failed to create mmc completion workqueue");
return -ENOMEM;
diff --git a/drivers/mmc/core/bus.h b/drivers/mmc/core/bus.h
index cfd0d02d3420..8b69624fa46e 100644
--- a/drivers/mmc/core/bus.h
+++ b/drivers/mmc/core/bus.h
@@ -20,7 +20,7 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a
struct mmc_card *card = mmc_dev_to_card(dev); \
return sysfs_emit(buf, fmt, args); \
} \
-static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
+static DEVICE_ATTR(name, 0444, mmc_##name##_show, NULL)
struct mmc_card *mmc_alloc_card(struct mmc_host *host,
const struct device_type *type);
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index f10a4dcf1f95..91ea00a0f61d 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -315,7 +315,10 @@ static int mmc_caps_set(void *data, u64 val)
MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_UHS |
- MMC_CAP_DDR;
+ MMC_CAP_DDR |
+ MMC_CAP_4_BIT_DATA |
+ MMC_CAP_8_BIT_DATA |
+ MMC_CAP_CMD23;
if (diff & ~allowed)
return -EINVAL;
@@ -327,7 +330,10 @@ static int mmc_caps_set(void *data, u64 val)
static int mmc_caps2_set(void *data, u64 val)
{
- u32 allowed = MMC_CAP2_HSX00_1_8V | MMC_CAP2_HSX00_1_2V;
+ u32 allowed = MMC_CAP2_HSX00_1_8V |
+ MMC_CAP2_HSX00_1_2V |
+ MMC_CAP2_CQE |
+ MMC_CAP2_CQE_DCMD;
u32 *caps = data;
u32 diff = *caps ^ val;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 3e7d9437477c..7c86efb1044a 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -831,7 +831,7 @@ static ssize_t mmc_fwrev_show(struct device *dev,
card->ext_csd.fwrev);
}
-static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
+static DEVICE_ATTR(fwrev, 0444, mmc_fwrev_show, NULL);
static ssize_t mmc_dsr_show(struct device *dev,
struct device_attribute *attr,
@@ -847,7 +847,7 @@ static ssize_t mmc_dsr_show(struct device *dev,
return sysfs_emit(buf, "0x%x\n", 0x404);
}
-static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
+static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr,
diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c
index 67d4a301895c..01d1e62c2ce7 100644
--- a/drivers/mmc/core/mmc_test.c
+++ b/drivers/mmc/core/mmc_test.c
@@ -586,14 +586,11 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes,
rate = mmc_test_rate(tot, &ts);
iops = mmc_test_rate(count * 100, &ts); /* I/O ops per sec x 100 */
- pr_info("%s: Transfer of %u x %u sectors (%u x %u%s KiB) took "
- "%llu.%09u seconds (%u kB/s, %u KiB/s, "
- "%u.%02u IOPS, sg_len %d)\n",
- mmc_hostname(test->card->host), count, sectors, count,
- sectors >> 1, (sectors & 1 ? ".5" : ""),
- (u64)ts.tv_sec, (u32)ts.tv_nsec,
- rate / 1000, rate / 1024, iops / 100, iops % 100,
- test->area.sg_len);
+ pr_info("%s: Transfer of %u x %u sectors (%u x %u%s KiB) took %ptSp seconds (%u kB/s, %u KiB/s, %u.%02u IOPS, sg_len %d)\n",
+ mmc_hostname(test->card->host), count, sectors, count,
+ sectors >> 1, (sectors & 1 ? ".5" : ""), &ts,
+ rate / 1000, rate / 1024, iops / 100, iops % 100,
+ test->area.sg_len);
mmc_test_save_transfer_result(test, count, sectors, ts, rate, iops);
}
@@ -3074,10 +3071,9 @@ static int mtf_test_show(struct seq_file *sf, void *data)
seq_printf(sf, "Test %d: %d\n", gr->testcase + 1, gr->result);
list_for_each_entry(tr, &gr->tr_lst, link) {
- seq_printf(sf, "%u %d %llu.%09u %u %u.%02u\n",
- tr->count, tr->sectors,
- (u64)tr->ts.tv_sec, (u32)tr->ts.tv_nsec,
- tr->rate, tr->iops / 100, tr->iops % 100);
+ seq_printf(sf, "%u %d %ptSp %u %u.%02u\n",
+ tr->count, tr->sectors, &tr->ts, tr->rate,
+ tr->iops / 100, tr->iops % 100);
}
}
@@ -3212,12 +3208,12 @@ static int mmc_test_register_dbgfs_file(struct mmc_card *card)
mutex_lock(&mmc_test_lock);
- ret = __mmc_test_register_dbgfs_file(card, "test", S_IWUSR | S_IRUGO,
+ ret = __mmc_test_register_dbgfs_file(card, "test", 0644,
&mmc_test_fops_test);
if (ret)
goto err;
- ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO,
+ ret = __mmc_test_register_dbgfs_file(card, "testlist", 0444,
&mtf_testlist_fops);
if (ret)
goto err;
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 67cd63004829..948948ca9b4a 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -554,7 +554,7 @@ static u32 sd_get_host_max_current(struct mmc_host *host)
static int sd_set_current_limit(struct mmc_card *card, u8 *status)
{
- int current_limit = SD_SET_CURRENT_NO_CHANGE;
+ int current_limit = SD_SET_CURRENT_LIMIT_200;
int err;
u32 max_current;
@@ -598,11 +598,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
else if (max_current >= 400 &&
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
current_limit = SD_SET_CURRENT_LIMIT_400;
- else if (max_current >= 200 &&
- card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
- current_limit = SD_SET_CURRENT_LIMIT_200;
- if (current_limit != SD_SET_CURRENT_NO_CHANGE) {
+ if (current_limit != SD_SET_CURRENT_LIMIT_200) {
err = mmc_sd_switch(card, SD_SWITCH_SET, 3,
current_limit, status);
if (err)
@@ -744,7 +741,7 @@ static ssize_t mmc_dsr_show(struct device *dev, struct device_attribute *attr,
return sysfs_emit(buf, "0x%x\n", 0x404);
}
-static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
+static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 2c963cb6724b..24f07df32a1a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -504,6 +504,7 @@ config MMC_MESON_MX_SDIO
depends on ARCH_MESON || COMPILE_TEST
depends on COMMON_CLK
depends on OF_ADDRESS
+ select REGMAP_MMIO
help
This selects support for the SD/MMC Host Controller on
Amlogic Meson6, Meson8 and Meson8b SoCs.
@@ -950,7 +951,7 @@ config MMC_USHC
config MMC_WMT
tristate "Wondermedia SD/MMC Host Controller support"
depends on ARCH_VT8500 || COMPILE_TEST
- default y
+ default ARCH_VT8500
help
This selects support for the SD/MMC Host Controller on
Wondermedia WM8505/WM8650 based SoCs.
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index d1fbc6811563..fdf6926ea468 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -609,12 +609,12 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
if (!root)
return;
- debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops);
- debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
- debugfs_create_u32("state", S_IRUSR, root, &host->state);
- debugfs_create_xul("pending_events", S_IRUSR, root,
+ debugfs_create_file("regs", 0400, root, host, &atmci_regs_fops);
+ debugfs_create_file("req", 0400, root, slot, &atmci_req_fops);
+ debugfs_create_u32("state", 0400, root, &host->state);
+ debugfs_create_xul("pending_events", 0400, root,
&host->pending_events);
- debugfs_create_xul("completed_events", S_IRUSR, root,
+ debugfs_create_xul("completed_events", 0400, root,
&host->completed_events);
}
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index ce189a1866b9..3668856531c1 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -93,6 +93,7 @@
/* send status config 1 */
#define CQHCI_SSC1 0x40
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
+#define CQHCI_SSC1_CIT_MASK GENMASK(15, 0)
/* send status config 2 */
#define CQHCI_SSC2 0x44
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 2b7d6d9bcde5..42b0118a45a8 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -145,17 +145,17 @@
#define MAX_NR_SG 16
static unsigned rw_threshold = 32;
-module_param(rw_threshold, uint, S_IRUGO);
+module_param(rw_threshold, uint, 0444);
MODULE_PARM_DESC(rw_threshold,
"Read/Write threshold. Default = 32");
static unsigned poll_threshold = 128;
-module_param(poll_threshold, uint, S_IRUGO);
+module_param(poll_threshold, uint, 0444);
MODULE_PARM_DESC(poll_threshold,
"Polling transaction size threshold. Default = 128");
static unsigned poll_loopcount = 32;
-module_param(poll_loopcount, uint, S_IRUGO);
+module_param(poll_loopcount, uint, 0444);
MODULE_PARM_DESC(poll_loopcount,
"Maximum polling loop count. Default = 32");
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 82dd906bb002..62c68cda1e21 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -19,6 +19,8 @@
#define RK3288_CLKGEN_DIV 2
#define SDMMC_TIMING_CON0 0x130
#define SDMMC_TIMING_CON1 0x134
+#define SDMMC_MISC_CON 0x138
+#define MEM_CLK_AUTOGATE_ENABLE BIT(5)
#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
#define ROCKCHIP_MMC_DEGREE_OFFSET 1
@@ -42,7 +44,7 @@ struct dw_mci_rockchip_priv_data {
*/
static int rockchip_mmc_get_internal_phase(struct dw_mci *host, bool sample)
{
- unsigned long rate = clk_get_rate(host->ciu_clk);
+ unsigned long rate = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV;
u32 raw_value;
u16 degrees;
u32 delay_num = 0;
@@ -85,7 +87,7 @@ static int rockchip_mmc_get_phase(struct dw_mci *host, bool sample)
static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int degrees)
{
- unsigned long rate = clk_get_rate(host->ciu_clk);
+ unsigned long rate = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV;
u8 nineties, remainder;
u8 delay_num;
u32 raw_value;
@@ -470,6 +472,7 @@ static int dw_mci_rk3576_parse_dt(struct dw_mci *host)
static int dw_mci_rockchip_init(struct dw_mci *host)
{
+ struct dw_mci_rockchip_priv_data *priv = host->priv;
int ret, i;
/* It is slot 8 on Rockchip SoCs */
@@ -494,6 +497,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
dev_warn(host->dev, "no valid minimum freq: %d\n", ret);
}
+ if (priv->internal_phase)
+ mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE);
+
return 0;
}
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index c5db92bbb094..9e74b675e92d 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -175,12 +175,12 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
if (!root)
return;
- debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops);
- debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops);
- debugfs_create_u32("state", S_IRUSR, root, &host->state);
- debugfs_create_xul("pending_events", S_IRUSR, root,
+ debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops);
+ debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops);
+ debugfs_create_u32("state", 0400, root, &host->state);
+ debugfs_create_xul("pending_events", 0400, root,
&host->pending_events);
- debugfs_create_xul("completed_events", S_IRUSR, root,
+ debugfs_create_xul("completed_events", 0400, root,
&host->completed_events);
#ifdef CONFIG_FAULT_INJECTION
fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc);
@@ -3120,9 +3120,8 @@ static void dw_mci_init_dma(struct dw_mci *host)
host->dma_64bit_address = 1;
dev_info(host->dev,
"IDMAC supports 64-bit address mode.\n");
- if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
- dma_set_coherent_mask(host->dev,
- DMA_BIT_MASK(64));
+ if (dma_set_mask_and_coherent(host->dev, DMA_BIT_MASK(64)))
+ dev_info(host->dev, "Fail to set 64-bit DMA mask");
} else {
/* host supports IDMAC in 32-bit address mode */
host->dma_64bit_address = 0;
diff --git a/drivers/mmc/host/meson-mx-sdio.c b/drivers/mmc/host/meson-mx-sdio.c
index 8a49c32fd3f9..5921e2cb2180 100644
--- a/drivers/mmc/host/meson-mx-sdio.c
+++ b/drivers/mmc/host/meson-mx-sdio.c
@@ -19,6 +19,7 @@
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
+#include <linux/regmap.h>
#include <linux/timer.h>
#include <linux/types.h>
@@ -98,17 +99,16 @@
#define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
#define MESON_MX_SDIO_MAX_SLOTS 3
+struct meson_mx_mmc_host_clkc {
+ struct clk_divider cfg_div;
+ struct clk_fixed_factor fixed_div2;
+};
+
struct meson_mx_mmc_host {
struct device *controller_dev;
- struct clk *parent_clk;
- struct clk *core_clk;
- struct clk_divider cfg_div;
struct clk *cfg_div_clk;
- struct clk_fixed_factor fixed_factor;
- struct clk *fixed_factor_clk;
-
- void __iomem *base;
+ struct regmap *regmap;
int irq;
spinlock_t irq_lock;
@@ -122,22 +122,10 @@ struct meson_mx_mmc_host {
int error;
};
-static void meson_mx_mmc_mask_bits(struct mmc_host *mmc, char reg, u32 mask,
- u32 val)
-{
- struct meson_mx_mmc_host *host = mmc_priv(mmc);
- u32 regval;
-
- regval = readl(host->base + reg);
- regval &= ~mask;
- regval |= (val & mask);
-
- writel(regval, host->base + reg);
-}
-
static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host)
{
- writel(MESON_MX_SDIO_IRQC_SOFT_RESET, host->base + MESON_MX_SDIO_IRQC);
+ regmap_write(host->regmap, MESON_MX_SDIO_IRQC,
+ MESON_MX_SDIO_IRQC_SOFT_RESET);
udelay(2);
}
@@ -158,7 +146,7 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
struct meson_mx_mmc_host *host = mmc_priv(mmc);
unsigned int pack_size;
unsigned long irqflags, timeout;
- u32 mult, send = 0, ext = 0;
+ u32 send = 0, ext = 0;
host->cmd = cmd;
@@ -215,25 +203,22 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
spin_lock_irqsave(&host->irq_lock, irqflags);
- mult = readl(host->base + MESON_MX_SDIO_MULT);
- mult &= ~MESON_MX_SDIO_MULT_PORT_SEL_MASK;
- mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id);
- mult |= BIT(31);
- writel(mult, host->base + MESON_MX_SDIO_MULT);
+ regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
+ MESON_MX_SDIO_MULT_PORT_SEL_MASK | BIT(31),
+ FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK,
+ host->slot_id) | BIT(31));
/* enable the CMD done interrupt */
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC,
- MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN,
- MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
+ regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQC,
+ MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
/* clear pending interrupts */
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS,
- MESON_MX_SDIO_IRQS_CMD_INT,
- MESON_MX_SDIO_IRQS_CMD_INT);
+ regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQS,
+ MESON_MX_SDIO_IRQS_CMD_INT);
- writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU);
- writel(ext, host->base + MESON_MX_SDIO_EXT);
- writel(send, host->base + MESON_MX_SDIO_SEND);
+ regmap_write(host->regmap, MESON_MX_SDIO_ARGU, cmd->arg);
+ regmap_write(host->regmap, MESON_MX_SDIO_EXT, ext);
+ regmap_write(host->regmap, MESON_MX_SDIO_SEND, send);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
@@ -263,14 +248,13 @@ static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
- MESON_MX_SDIO_CONF_BUS_WIDTH, 0);
+ regmap_clear_bits(host->regmap, MESON_MX_SDIO_CONF,
+ MESON_MX_SDIO_CONF_BUS_WIDTH);
break;
case MMC_BUS_WIDTH_4:
- meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
- MESON_MX_SDIO_CONF_BUS_WIDTH,
- MESON_MX_SDIO_CONF_BUS_WIDTH);
+ regmap_set_bits(host->regmap, MESON_MX_SDIO_CONF,
+ MESON_MX_SDIO_CONF_BUS_WIDTH);
break;
case MMC_BUS_WIDTH_8:
@@ -351,8 +335,8 @@ static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq;
if (mrq->data)
- writel(sg_dma_address(mrq->data->sg),
- host->base + MESON_MX_SDIO_ADDR);
+ regmap_write(host->regmap, MESON_MX_SDIO_ADDR,
+ sg_dma_address(mrq->data->sg));
if (mrq->sbc)
meson_mx_mmc_start_cmd(mmc, mrq->sbc);
@@ -364,24 +348,26 @@ static void meson_mx_mmc_read_response(struct mmc_host *mmc,
struct mmc_command *cmd)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
- u32 mult;
- int i, resp[4];
+ unsigned int i, resp[4];
- mult = readl(host->base + MESON_MX_SDIO_MULT);
- mult |= MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX;
- mult &= ~MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK;
- mult |= FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, 0);
- writel(mult, host->base + MESON_MX_SDIO_MULT);
+ regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
+ MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
+ MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
+ MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
+ FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
+ 0));
if (cmd->flags & MMC_RSP_136) {
for (i = 0; i <= 3; i++)
- resp[3 - i] = readl(host->base + MESON_MX_SDIO_ARGU);
+ regmap_read(host->regmap, MESON_MX_SDIO_ARGU,
+ &resp[3 - i]);
+
cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff);
cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff);
cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff);
cmd->resp[3] = (resp[3] << 8);
} else if (cmd->flags & MMC_RSP_PRESENT) {
- cmd->resp[0] = readl(host->base + MESON_MX_SDIO_ARGU);
+ regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &cmd->resp[0]);
}
}
@@ -422,8 +408,8 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
spin_lock(&host->irq_lock);
- irqs = readl(host->base + MESON_MX_SDIO_IRQS);
- send = readl(host->base + MESON_MX_SDIO_SEND);
+ regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
+ regmap_read(host->regmap, MESON_MX_SDIO_SEND, &send);
if (irqs & MESON_MX_SDIO_IRQS_CMD_INT)
ret = meson_mx_mmc_process_cmd_irq(host, irqs, send);
@@ -431,7 +417,7 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
ret = IRQ_HANDLED;
/* finally ACK all pending interrupts */
- writel(irqs, host->base + MESON_MX_SDIO_IRQS);
+ regmap_write(host->regmap, MESON_MX_SDIO_IRQS, irqs);
spin_unlock(&host->irq_lock);
@@ -450,8 +436,7 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
if (cmd->data) {
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
- cmd->data->sg_len,
- mmc_get_dma_dir(cmd->data));
+ cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
}
@@ -470,14 +455,13 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
struct meson_mx_mmc_host *host = timer_container_of(host, t,
cmd_timeout);
unsigned long irqflags;
- u32 irqc;
+ u32 irqs, argu;
spin_lock_irqsave(&host->irq_lock, irqflags);
/* disable the CMD interrupt */
- irqc = readl(host->base + MESON_MX_SDIO_IRQC);
- irqc &= ~MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN;
- writel(irqc, host->base + MESON_MX_SDIO_IRQC);
+ regmap_clear_bits(host->regmap, MESON_MX_SDIO_IRQC,
+ MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
spin_unlock_irqrestore(&host->irq_lock, irqflags);
@@ -488,10 +472,12 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
if (!host->cmd)
return;
+ regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
+ regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &argu);
+
dev_dbg(mmc_dev(host->mmc),
"Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n",
- host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS),
- readl(host->base + MESON_MX_SDIO_ARGU));
+ host->cmd->opcode, irqs, argu);
host->cmd->error = -ETIMEDOUT;
@@ -507,23 +493,30 @@ static struct mmc_host_ops meson_mx_mmc_ops = {
static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent)
{
- struct device_node *slot_node;
- struct platform_device *pdev;
+ struct platform_device *pdev = NULL;
+
+ for_each_available_child_of_node_scoped(parent->of_node, slot_node) {
+ if (!of_device_is_compatible(slot_node, "mmc-slot"))
+ continue;
+
+ /*
+ * TODO: the MMC core framework currently does not support
+ * controllers with multiple slots properly. So we only
+ * register the first slot for now.
+ */
+ if (pdev) {
+ dev_warn(parent,
+ "more than one 'mmc-slot' compatible child found - using the first one and ignoring all subsequent ones\n");
+ break;
+ }
- /*
- * TODO: the MMC core framework currently does not support
- * controllers with multiple slots properly. So we only register
- * the first slot for now
- */
- slot_node = of_get_compatible_child(parent->of_node, "mmc-slot");
- if (!slot_node) {
- dev_warn(parent, "no 'mmc-slot' sub-node found\n");
- return ERR_PTR(-ENOENT);
+ pdev = of_platform_device_create(slot_node, NULL, parent);
+ if (!pdev)
+ dev_err(parent,
+ "Failed to create platform device for mmc-slot node '%pOF'\n",
+ slot_node);
}
- pdev = of_platform_device_create(slot_node, NULL, parent);
- of_node_put(slot_node);
-
return pdev;
}
@@ -533,16 +526,14 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
struct device *slot_dev = mmc_dev(mmc);
int ret;
- if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) {
- dev_err(slot_dev, "missing 'reg' property\n");
- return -EINVAL;
- }
+ if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id))
+ return dev_err_probe(slot_dev, -EINVAL,
+ "missing 'reg' property\n");
- if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) {
- dev_err(slot_dev, "invalid 'reg' property value %d\n",
- host->slot_id);
- return -EINVAL;
- }
+ if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS)
+ return dev_err_probe(slot_dev, -EINVAL,
+ "invalid 'reg' property value %d\n",
+ host->slot_id);
/* Get regulators and the supported OCR mask */
ret = mmc_regulator_get_supply(mmc);
@@ -561,8 +552,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
/* Get the min and max supported clock rates */
mmc->f_min = clk_round_rate(host->cfg_div_clk, 1);
- mmc->f_max = clk_round_rate(host->cfg_div_clk,
- clk_get_rate(host->parent_clk));
+ mmc->f_max = clk_round_rate(host->cfg_div_clk, ULONG_MAX);
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
mmc->ops = &meson_mx_mmc_ops;
@@ -578,70 +568,89 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
return 0;
}
-static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host)
+static struct clk *meson_mx_mmc_register_clk(struct device *dev,
+ void __iomem *base)
{
- struct clk_init_data init;
- const char *clk_div_parent, *clk_fixed_factor_parent;
-
- clk_fixed_factor_parent = __clk_get_name(host->parent_clk);
- init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
- "%s#fixed_factor",
- dev_name(host->controller_dev));
- if (!init.name)
- return -ENOMEM;
+ const char *fixed_div2_name, *cfg_div_name;
+ struct meson_mx_mmc_host_clkc *host_clkc;
+ struct clk *clk;
+ int ret;
- init.ops = &clk_fixed_factor_ops;
- init.flags = 0;
- init.parent_names = &clk_fixed_factor_parent;
- init.num_parents = 1;
- host->fixed_factor.div = 2;
- host->fixed_factor.mult = 1;
- host->fixed_factor.hw.init = &init;
-
- host->fixed_factor_clk = devm_clk_register(host->controller_dev,
- &host->fixed_factor.hw);
- if (WARN_ON(IS_ERR(host->fixed_factor_clk)))
- return PTR_ERR(host->fixed_factor_clk);
-
- clk_div_parent = __clk_get_name(host->fixed_factor_clk);
- init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
- "%s#div", dev_name(host->controller_dev));
- if (!init.name)
- return -ENOMEM;
+ /* use a dedicated memory allocation for the clock controller to
+ * prevent use-after-free as meson_mx_mmc_host is free'd before
+ * dev (controller dev, not mmc_host->dev) is free'd.
+ */
+ host_clkc = devm_kzalloc(dev, sizeof(*host_clkc), GFP_KERNEL);
+ if (!host_clkc)
+ return ERR_PTR(-ENOMEM);
+
+ fixed_div2_name = devm_kasprintf(dev, GFP_KERNEL, "%s#fixed_div2",
+ dev_name(dev));
+ if (!fixed_div2_name)
+ return ERR_PTR(-ENOMEM);
+
+ host_clkc->fixed_div2.div = 2;
+ host_clkc->fixed_div2.mult = 1;
+ host_clkc->fixed_div2.hw.init = CLK_HW_INIT_FW_NAME(fixed_div2_name,
+ "clkin",
+ &clk_fixed_factor_ops,
+ 0);
+ ret = devm_clk_hw_register(dev, &host_clkc->fixed_div2.hw);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret,
+ "Failed to register %s clock\n",
+ fixed_div2_name);
+
+ cfg_div_name = devm_kasprintf(dev, GFP_KERNEL, "%s#div", dev_name(dev));
+ if (!cfg_div_name)
+ return ERR_PTR(-ENOMEM);
+
+ host_clkc->cfg_div.reg = base + MESON_MX_SDIO_CONF;
+ host_clkc->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
+ host_clkc->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
+ host_clkc->cfg_div.hw.init = CLK_HW_INIT_HW(cfg_div_name,
+ &host_clkc->fixed_div2.hw,
+ &clk_divider_ops,
+ CLK_DIVIDER_ALLOW_ZERO);
+ ret = devm_clk_hw_register(dev, &host_clkc->cfg_div.hw);
+ if (ret)
+ return dev_err_ptr_probe(dev, ret,
+ "Failed to register %s clock\n",
+ cfg_div_name);
- init.ops = &clk_divider_ops;
- init.flags = CLK_SET_RATE_PARENT;
- init.parent_names = &clk_div_parent;
- init.num_parents = 1;
- host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF;
- host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
- host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
- host->cfg_div.hw.init = &init;
- host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO;
-
- host->cfg_div_clk = devm_clk_register(host->controller_dev,
- &host->cfg_div.hw);
- if (WARN_ON(IS_ERR(host->cfg_div_clk)))
- return PTR_ERR(host->cfg_div_clk);
+ clk = devm_clk_hw_get_clk(dev, &host_clkc->cfg_div.hw, "cfg_div_clk");
+ if (IS_ERR(clk))
+ return dev_err_ptr_probe(dev, PTR_ERR(clk),
+ "Failed to get the cfg_div clock\n");
- return 0;
+ return clk;
}
static int meson_mx_mmc_probe(struct platform_device *pdev)
{
+ const struct regmap_config meson_mx_sdio_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = MESON_MX_SDIO_EXT,
+ };
struct platform_device *slot_pdev;
struct mmc_host *mmc;
struct meson_mx_mmc_host *host;
+ struct clk *core_clk;
+ void __iomem *base;
int ret, irq;
u32 conf;
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev);
if (!slot_pdev)
return -ENODEV;
- else if (IS_ERR(slot_pdev))
- return PTR_ERR(slot_pdev);
- mmc = mmc_alloc_host(sizeof(*host), &slot_pdev->dev);
+ mmc = devm_mmc_alloc_host(&slot_pdev->dev, sizeof(*host));
if (!mmc) {
ret = -ENOMEM;
goto error_unregister_slot_pdev;
@@ -656,51 +665,48 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
- host->base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(host->base)) {
- ret = PTR_ERR(host->base);
- goto error_free_mmc;
+ host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &meson_mx_sdio_regmap_config);
+ if (IS_ERR(host->regmap)) {
+ ret = dev_err_probe(host->controller_dev, PTR_ERR(host->regmap),
+ "Failed to initialize regmap\n");
+ goto error_unregister_slot_pdev;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
- goto error_free_mmc;
+ goto error_unregister_slot_pdev;
}
ret = devm_request_threaded_irq(host->controller_dev, irq,
meson_mx_mmc_irq,
meson_mx_mmc_irq_thread, IRQF_ONESHOT,
NULL, host);
- if (ret)
- goto error_free_mmc;
-
- host->core_clk = devm_clk_get(host->controller_dev, "core");
- if (IS_ERR(host->core_clk)) {
- ret = PTR_ERR(host->core_clk);
- goto error_free_mmc;
+ if (ret) {
+ dev_err_probe(host->controller_dev, ret,
+ "Failed to request IRQ\n");
+ goto error_unregister_slot_pdev;
}
- host->parent_clk = devm_clk_get(host->controller_dev, "clkin");
- if (IS_ERR(host->parent_clk)) {
- ret = PTR_ERR(host->parent_clk);
- goto error_free_mmc;
+ core_clk = devm_clk_get_enabled(host->controller_dev, "core");
+ if (IS_ERR(core_clk)) {
+ ret = dev_err_probe(host->controller_dev, PTR_ERR(core_clk),
+ "Failed to get and enable 'core' clock\n");
+ goto error_unregister_slot_pdev;
}
- ret = meson_mx_mmc_register_clks(host);
- if (ret)
- goto error_free_mmc;
-
- ret = clk_prepare_enable(host->core_clk);
- if (ret) {
- dev_err(host->controller_dev, "Failed to enable core clock\n");
- goto error_free_mmc;
+ host->cfg_div_clk = meson_mx_mmc_register_clk(&pdev->dev, base);
+ if (IS_ERR(host->cfg_div_clk)) {
+ ret = PTR_ERR(host->cfg_div_clk);
+ goto error_unregister_slot_pdev;
}
ret = clk_prepare_enable(host->cfg_div_clk);
if (ret) {
- dev_err(host->controller_dev, "Failed to enable MMC clock\n");
- goto error_disable_core_clk;
+ dev_err_probe(host->controller_dev, ret,
+ "Failed to enable MMC (cfg div) clock\n");
+ goto error_unregister_slot_pdev;
}
conf = 0;
@@ -708,22 +714,18 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2);
- writel(conf, host->base + MESON_MX_SDIO_CONF);
+ regmap_write(host->regmap, MESON_MX_SDIO_CONF, conf);
meson_mx_mmc_soft_reset(host);
ret = meson_mx_mmc_add_host(host);
if (ret)
- goto error_disable_clks;
+ goto error_disable_div_clk;
return 0;
-error_disable_clks:
+error_disable_div_clk:
clk_disable_unprepare(host->cfg_div_clk);
-error_disable_core_clk:
- clk_disable_unprepare(host->core_clk);
-error_free_mmc:
- mmc_free_host(mmc);
error_unregister_slot_pdev:
of_platform_device_destroy(&slot_pdev->dev, NULL);
return ret;
@@ -741,9 +743,6 @@ static void meson_mx_mmc_remove(struct platform_device *pdev)
of_platform_device_destroy(slot_dev, NULL);
clk_disable_unprepare(host->cfg_div_clk);
- clk_disable_unprepare(host->core_clk);
-
- mmc_free_host(host->mmc);
}
static const struct of_device_id meson_mx_mmc_of_match[] = {
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 79074291e9d2..daed659f63f6 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -1214,7 +1214,7 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
host->data = data;
read = data->flags & MMC_DATA_READ;
- mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+ mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
msdc_dma_setup(host, &host->dma, data);
sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
@@ -1444,7 +1444,7 @@ static void msdc_start_command(struct msdc_host *host,
WARN_ON(host->cmd);
host->cmd = cmd;
- mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
+ mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
if (!msdc_cmd_is_ready(host, mrq, cmd))
return;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 52ac3f128a1c..527b89a5ed70 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -326,7 +326,7 @@ mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
"closed");
}
-static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+static DEVICE_ATTR(cover_switch, 0444, mmc_omap_show_cover_switch, NULL);
static ssize_t
mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
@@ -338,7 +338,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", slot->pdata->name);
}
-static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
+static DEVICE_ATTR(slot_name, 0444, mmc_omap_show_slot_name, NULL);
static void
mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
@@ -1477,7 +1477,7 @@ static int mmc_omap_probe(struct platform_device *pdev)
host->nr_slots = pdata->nr_slots;
host->reg_shift = (mmc_omap7xx() ? 1 : 2);
- host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
+ host->mmc_omap_wq = alloc_workqueue("mmc_omap", WQ_PERCPU, 0);
if (!host->mmc_omap_wq) {
ret = -ENOMEM;
goto err_plat_cleanup;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 09e4354d1f1d..58c881f2725b 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -746,7 +746,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", mmc_pdata(host)->name);
}
-static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
+static DEVICE_ATTR(slot_name, 0444, omap_hsmmc_show_slot_name, NULL);
/*
* Configure the response type and send the cmd.
@@ -1672,7 +1672,7 @@ DEFINE_SHOW_ATTRIBUTE(mmc_regs);
static void omap_hsmmc_debugfs(struct mmc_host *mmc)
{
if (mmc->debugfs_root)
- debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root,
+ debugfs_create_file("regs", 0400, mmc->debugfs_root,
mmc, &mmc_regs_fops);
}
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index 26d03352af63..b5ea058ed467 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -652,10 +652,9 @@ static int pxamci_probe(struct platform_device *pdev)
host->clkrt = CLKRT_OFF;
host->clk = devm_clk_get(dev, NULL);
- if (IS_ERR(host->clk)) {
- host->clk = NULL;
- return PTR_ERR(host->clk);
- }
+ if (IS_ERR(host->clk))
+ return dev_err_probe(dev, PTR_ERR(host->clk),
+ "Failed to acquire clock\n");
host->clkrate = clk_get_rate(host->clk);
@@ -703,46 +702,37 @@ static int pxamci_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mmc);
- host->dma_chan_rx = dma_request_chan(dev, "rx");
- if (IS_ERR(host->dma_chan_rx)) {
- host->dma_chan_rx = NULL;
+ host->dma_chan_rx = devm_dma_request_chan(dev, "rx");
+ if (IS_ERR(host->dma_chan_rx))
return dev_err_probe(dev, PTR_ERR(host->dma_chan_rx),
"unable to request rx dma channel\n");
- }
- host->dma_chan_tx = dma_request_chan(dev, "tx");
- if (IS_ERR(host->dma_chan_tx)) {
- dev_err(dev, "unable to request tx dma channel\n");
- ret = PTR_ERR(host->dma_chan_tx);
- host->dma_chan_tx = NULL;
- goto out;
- }
+
+ host->dma_chan_tx = devm_dma_request_chan(dev, "tx");
+ if (IS_ERR(host->dma_chan_tx))
+ return dev_err_probe(dev, PTR_ERR(host->dma_chan_tx),
+ "unable to request tx dma channel\n");
if (host->pdata) {
host->detect_delay_ms = host->pdata->detect_delay_ms;
host->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
- if (IS_ERR(host->power)) {
- ret = PTR_ERR(host->power);
- dev_err(dev, "Failed requesting gpio_power\n");
- goto out;
- }
+ if (IS_ERR(host->power))
+ return dev_err_probe(dev, PTR_ERR(host->power),
+ "Failed requesting gpio_power\n");
/* FIXME: should we pass detection delay to debounce? */
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
- if (ret && ret != -ENOENT) {
- dev_err(dev, "Failed requesting gpio_cd\n");
- goto out;
- }
+ if (ret && ret != -ENOENT)
+ return dev_err_probe(dev, ret, "Failed requesting gpio_cd\n");
if (!host->pdata->gpio_card_ro_invert)
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0);
- if (ret && ret != -ENOENT) {
- dev_err(dev, "Failed requesting gpio_ro\n");
- goto out;
- }
+ if (ret && ret != -ENOENT)
+ return dev_err_probe(dev, ret, "Failed requesting gpio_ro\n");
+
if (!ret)
host->use_ro_gpio = true;
@@ -759,16 +749,8 @@ static int pxamci_probe(struct platform_device *pdev)
if (ret) {
if (host->pdata && host->pdata->exit)
host->pdata->exit(dev, mmc);
- goto out;
}
- return 0;
-
-out:
- if (host->dma_chan_rx)
- dma_release_channel(host->dma_chan_rx);
- if (host->dma_chan_tx)
- dma_release_channel(host->dma_chan_tx);
return ret;
}
@@ -791,8 +773,6 @@ static void pxamci_remove(struct platform_device *pdev)
dmaengine_terminate_all(host->dma_chan_rx);
dmaengine_terminate_all(host->dma_chan_tx);
- dma_release_channel(host->dma_chan_rx);
- dma_release_channel(host->dma_chan_tx);
}
}
diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index 084964cecf9d..afc36a407c2c 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -9,6 +9,7 @@
#ifndef RENESAS_SDHI_H
#define RENESAS_SDHI_H
+#include <linux/device.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
@@ -107,4 +108,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
const struct renesas_sdhi_of_data *of_data,
const struct renesas_sdhi_quirks *quirks);
void renesas_sdhi_remove(struct platform_device *pdev);
+int renesas_sdhi_suspend(struct device *dev);
+int renesas_sdhi_resume(struct device *dev);
#endif
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index f56fa2cd208d..2a310a145785 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -31,6 +31,7 @@
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
@@ -1103,7 +1104,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (IS_ERR(priv->clk_cd))
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock");
- priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
+ priv->rstc = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
if (IS_ERR(priv->rstc))
return PTR_ERR(priv->rstc);
@@ -1317,5 +1318,41 @@ void renesas_sdhi_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(renesas_sdhi_remove);
+int renesas_sdhi_suspend(struct device *dev)
+{
+ struct tmio_mmc_host *host = dev_get_drvdata(dev);
+ struct renesas_sdhi *priv = host_to_priv(host);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ ret = reset_control_assert(priv->rstc);
+ if (ret)
+ pm_runtime_force_resume(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(renesas_sdhi_suspend);
+
+int renesas_sdhi_resume(struct device *dev)
+{
+ struct tmio_mmc_host *host = dev_get_drvdata(dev);
+ struct renesas_sdhi *priv = host_to_priv(host);
+ int ret;
+
+ ret = reset_control_deassert(priv->rstc);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ reset_control_assert(priv->rstc);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(renesas_sdhi_resume);
+
MODULE_DESCRIPTION("Renesas SDHI core driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 9e3ed0bcddd6..f6ebb7bc7ede 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -18,7 +18,6 @@
#include <linux/pagemap.h>
#include <linux/platform_data/tmio.h>
#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
#include <linux/scatterlist.h>
#include <linux/sys_soc.h>
@@ -124,7 +123,8 @@ static const struct renesas_sdhi_of_data of_data_rcar_gen3 = {
static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
- TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
+ TMIO_MMC_64BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
@@ -599,18 +599,17 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
}
static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
- tmio_mmc_host_runtime_resume,
- NULL)
+ SYSTEM_SLEEP_PM_OPS(renesas_sdhi_suspend, renesas_sdhi_resume)
+ RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+ tmio_mmc_host_runtime_resume,
+ NULL)
};
static struct platform_driver renesas_internal_dmac_sdhi_driver = {
.driver = {
.name = "renesas_sdhi_internal_dmac",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &renesas_sdhi_internal_dmac_dev_pm_ops,
+ .pm = pm_ptr(&renesas_sdhi_internal_dmac_dev_pm_ops),
.of_match_table = renesas_sdhi_internal_dmac_of_match,
},
.probe = renesas_sdhi_internal_dmac_probe,
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 822a310c9bba..543ad1d0ed1c 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -60,7 +60,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
- TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
+ TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
+ TMIO_MMC_32BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index 15705e85417f..c9442499876c 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -31,35 +31,116 @@
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
-#define SDIO_CFG_CQ_CAPABILITY 0x4c
-#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
-
#define SDIO_CFG_CTRL 0x0
#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
-
+#define SDIO_CFG_OP_DLY 0x34
+#define SDIO_CFG_OP_DLY_DEFAULT 0x80000003
+#define SDIO_CFG_CQ_CAPABILITY 0x4c
+#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
+#define SDIO_CFG_SD_PIN_SEL 0x44
+#define SDIO_CFG_V1_SD_PIN_SEL 0x54
+#define SDIO_CFG_PHY_SW_MODE_0_RX_CTRL 0x7C
#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
+#define SDIO_BOOT_MAIN_CTL 0x0
+
#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V)
/* Select all SD UHS type I SDR speed above 50MB/s */
#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104)
-struct sdhci_brcmstb_priv {
- void __iomem *cfg_regs;
- unsigned int flags;
- struct clk *base_clk;
- u32 base_freq_hz;
+enum cfg_core_ver {
+ SDIO_CFG_CORE_V1 = 1,
+ SDIO_CFG_CORE_V2,
+};
+
+struct sdhci_brcmstb_saved_regs {
+ u32 sd_pin_sel;
+ u32 phy_sw_mode0_rxctrl;
+ u32 max_50mhz_mode;
+ u32 boot_main_ctl;
};
struct brcmstb_match_priv {
void (*cfginit)(struct sdhci_host *host);
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
+ void (*save_restore_regs)(struct mmc_host *mmc, int save);
struct sdhci_ops *ops;
const unsigned int flags;
};
+struct sdhci_brcmstb_priv {
+ void __iomem *cfg_regs;
+ void __iomem *boot_regs;
+ struct sdhci_brcmstb_saved_regs saved_regs;
+ unsigned int flags;
+ struct clk *base_clk;
+ u32 base_freq_hz;
+ const struct brcmstb_match_priv *match_priv;
+};
+
+static void sdhci_brcmstb_save_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
+ void __iomem *cr = priv->cfg_regs;
+ bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
+
+ if (is_emmc && priv->boot_regs)
+ sr->boot_main_ctl = readl(priv->boot_regs + SDIO_BOOT_MAIN_CTL);
+
+ if (ver == SDIO_CFG_CORE_V1) {
+ sr->sd_pin_sel = readl(cr + SDIO_CFG_V1_SD_PIN_SEL);
+ return;
+ }
+
+ sr->sd_pin_sel = readl(cr + SDIO_CFG_SD_PIN_SEL);
+ sr->phy_sw_mode0_rxctrl = readl(cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
+ sr->max_50mhz_mode = readl(cr + SDIO_CFG_MAX_50MHZ_MODE);
+}
+
+static void sdhci_brcmstb_restore_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
+ void __iomem *cr = priv->cfg_regs;
+ bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
+
+ if (is_emmc && priv->boot_regs)
+ writel(sr->boot_main_ctl, priv->boot_regs + SDIO_BOOT_MAIN_CTL);
+
+ if (ver == SDIO_CFG_CORE_V1) {
+ writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
+ return;
+ }
+
+ writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
+ writel(sr->phy_sw_mode0_rxctrl, cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
+ writel(sr->max_50mhz_mode, cr + SDIO_CFG_MAX_50MHZ_MODE);
+}
+
+static void sdhci_brcmstb_save_restore_regs_v1(struct mmc_host *mmc, int save)
+{
+ if (save)
+ sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V1);
+ else
+ sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V1);
+}
+
+static void sdhci_brcmstb_save_restore_regs_v2(struct mmc_host *mmc, int save)
+{
+ if (save)
+ sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V2);
+ else
+ sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V2);
+}
+
static inline void enable_clock_gating(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -212,6 +293,21 @@ static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
}
}
+static void sdhci_brcmstb_set_72116_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /* no change to SDIO_CFG_OP_DLY_DEFAULT when using preset clk rate */
+ if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
+ return;
+
+ reg = (timing == MMC_TIMING_MMC_HS200) ? 0 : SDIO_CFG_OP_DLY_DEFAULT;
+ writel(reg, priv->cfg_regs + SDIO_CFG_OP_DLY);
+ sdhci_set_uhs_signaling(host, timing);
+}
+
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
{
sdhci_dumpregs(mmc_priv(mmc));
@@ -252,6 +348,13 @@ static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
+static struct sdhci_ops sdhci_brcmstb_ops_72116 = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_brcmstb_set_72116_uhs_signaling,
+};
+
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
.set_clock = sdhci_brcmstb_set_clock,
.set_bus_width = sdhci_set_bus_width,
@@ -277,19 +380,33 @@ static struct brcmstb_match_priv match_priv_7425 = {
.ops = &sdhci_brcmstb_ops,
};
+static struct brcmstb_match_priv match_priv_74371 = {
+ .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
+ .ops = &sdhci_brcmstb_ops,
+};
+
static struct brcmstb_match_priv match_priv_7445 = {
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
.ops = &sdhci_brcmstb_ops,
};
+static struct brcmstb_match_priv match_priv_72116 = {
+ .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
+ .ops = &sdhci_brcmstb_ops_72116,
+};
+
static const struct brcmstb_match_priv match_priv_7216 = {
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
.hs400es = sdhci_brcmstb_hs400es,
.ops = &sdhci_brcmstb_ops_7216,
};
static struct brcmstb_match_priv match_priv_74165b0 = {
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
+ .save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
.hs400es = sdhci_brcmstb_hs400es,
.ops = &sdhci_brcmstb_ops_74165b0,
};
@@ -297,7 +414,9 @@ static struct brcmstb_match_priv match_priv_74165b0 = {
static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
+ { .compatible = "brcm,bcm74371-sdhci", .data = &match_priv_74371 },
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
+ { .compatible = "brcm,bcm72116-sdhci", .data = &match_priv_72116 },
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
{ .compatible = "brcm,bcm74165b0-sdhci", .data = &match_priv_74165b0 },
{},
@@ -395,6 +514,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host);
priv = sdhci_pltfm_priv(pltfm_host);
+ priv->match_priv = match->data;
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE;
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
@@ -412,6 +532,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
if (res)
goto err;
+ /* map non-standard BOOT registers if present */
+ if (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
+ priv->boot_regs = devm_platform_get_and_ioremap_resource(pdev, 2, NULL);
+ if (IS_ERR(priv->boot_regs))
+ priv->boot_regs = NULL;
+ }
+
/*
* Automatic clock gating does not work for SD cards that may
* voltage switch so only enable it for non-removable devices.
@@ -501,8 +628,13 @@ static int sdhci_brcmstb_suspend(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ const struct brcmstb_match_priv *match_priv = priv->match_priv;
+
int ret;
+ if (match_priv->save_restore_regs)
+ match_priv->save_restore_regs(host->mmc, 1);
+
clk_disable_unprepare(priv->base_clk);
if (host->mmc->caps2 & MMC_CAP2_CQE) {
ret = cqhci_suspend(host->mmc);
@@ -518,6 +650,7 @@ static int sdhci_brcmstb_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ const struct brcmstb_match_priv *match_priv = priv->match_priv;
int ret;
ret = sdhci_pltfm_resume(dev);
@@ -534,6 +667,9 @@ static int sdhci_brcmstb_resume(struct device *dev)
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
}
+ if (match_priv->save_restore_regs)
+ match_priv->save_restore_regs(host->mmc, 0);
+
if (host->mmc->caps2 & MMC_CAP2_CQE)
ret = cqhci_resume(host->mmc);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 4e5edbf2fc9b..3b85233131b3 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -344,41 +344,43 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val,
writel_relaxed(val, host->ioaddr + offset);
}
-static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host)
+static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host,
+ unsigned int clock,
+ unsigned int timing)
{
- struct mmc_ios ios = host->mmc->ios;
/*
* The SDHC requires internal clock frequency to be double the
* actual clock that will be set for DDR mode. The controller
* uses the faster clock(100/400MHz) for some of its parts and
* send the actual required clock (50/200MHz) to the card.
*/
- if (ios.timing == MMC_TIMING_UHS_DDR50 ||
- ios.timing == MMC_TIMING_MMC_DDR52 ||
- ios.timing == MMC_TIMING_MMC_HS400 ||
+ if (timing == MMC_TIMING_UHS_DDR50 ||
+ timing == MMC_TIMING_MMC_DDR52 ||
+ (timing == MMC_TIMING_MMC_HS400 &&
+ clock == MMC_HS200_MAX_DTR) ||
host->flags & SDHCI_HS400_TUNING)
return 2;
return 1;
}
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
- unsigned int clock)
+ unsigned int clock,
+ unsigned int timing)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
- struct mmc_ios curr_ios = host->mmc->ios;
struct clk *core_clk = msm_host->bulk_clks[0].clk;
unsigned long achieved_rate;
unsigned int desired_rate;
unsigned int mult;
int rc;
- mult = msm_get_clock_mult_for_bus_mode(host);
+ mult = msm_get_clock_mult_for_bus_mode(host, clock, timing);
desired_rate = clock * mult;
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
if (rc) {
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
- mmc_hostname(host->mmc), desired_rate, curr_ios.timing);
+ mmc_hostname(host->mmc), desired_rate, timing);
return;
}
@@ -397,7 +399,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
msm_host->clk_rate = desired_rate;
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
- mmc_hostname(host->mmc), achieved_rate, curr_ios.timing);
+ mmc_hostname(host->mmc), achieved_rate, timing);
}
/* Platform specific tuning */
@@ -1239,7 +1241,7 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/
if (host->flags & SDHCI_HS400_TUNING) {
sdhci_msm_hc_select_mode(host);
- msm_set_clock_rate_for_bus_mode(host, ios.clock);
+ msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
host->flags &= ~SDHCI_HS400_TUNING;
}
@@ -1864,6 +1866,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ struct mmc_ios ios = host->mmc->ios;
if (!clock) {
host->mmc->actual_clock = msm_host->clk_rate = 0;
@@ -1872,7 +1875,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
sdhci_msm_hc_select_mode(host);
- msm_set_clock_rate_for_bus_mode(host, clock);
+ msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
out:
__sdhci_msm_set_clock(host, clock);
}
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index c6f09b53325d..b97d042897ad 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -1991,7 +1991,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret) {
- ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
+ dev_err_probe(dev, ret, "parsing dt failed.\n");
goto unreg_clk;
}
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index eebd45389956..51949cde0958 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -11,6 +11,7 @@
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -19,11 +20,15 @@
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sizes.h>
+#include <linux/mfd/syscon.h>
+#include <linux/units.h>
#include "sdhci-pltfm.h"
#include "cqhci.h"
+#include "sdhci-cqhci.h"
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
@@ -39,6 +44,7 @@
#define DWCMSHC_CARD_IS_EMMC BIT(0)
#define DWCMSHC_ENHANCED_STROBE BIT(8)
#define DWCMSHC_EMMC_ATCTRL 0x40
+#define DWCMSHC_AT_STAT 0x44
/* Tuning and auto-tuning fields in AT_CTRL_R control register */
#define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */
#define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */
@@ -82,6 +88,8 @@
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
+#define DECMSHC_EMMC_MISC_CON 0x81C
+#define MISC_INTCLK_EN BIT(1)
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
#define DWCMSHC_EMMC_DLL_START BIT(0)
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
@@ -94,7 +102,7 @@
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24)
-#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
+#define DLL_STRBIN_TAPNUM_DEFAULT 0x4
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
#define DLL_STRBIN_DELAY_NUM_SEL BIT(26)
#define DLL_STRBIN_DELAY_NUM_OFFSET 16
@@ -194,6 +202,19 @@
#define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */
#define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */
+/* PHY DLL offset setting register */
+#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29)
+/* DLL LBT setting register */
+#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c)
+/* DLL Status register */
+#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e)
+#define DLL_LOCK_STS BIT(0)/* DLL is locked and ready */
+/*
+ * Captures the value of DLL's lock error status information. Value is valid
+ * only when LOCK_STS is set.
+ */
+#define DLL_ERROR_STS BIT(1)
+
#define FLAG_IO_FIXED_1V8 BIT(0)
#define BOUNDARY_OK(addr, len) \
@@ -206,6 +227,31 @@
/* SMC call for BlueField-3 eMMC RST_N */
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
+/* Eswin specific Registers */
+#define EIC7700_CARD_CLK_STABLE BIT(28)
+#define EIC7700_INT_BCLK_STABLE BIT(16)
+#define EIC7700_INT_ACLK_STABLE BIT(8)
+#define EIC7700_INT_TMCLK_STABLE BIT(0)
+#define EIC7700_INT_CLK_STABLE (EIC7700_CARD_CLK_STABLE | \
+ EIC7700_INT_ACLK_STABLE | \
+ EIC7700_INT_BCLK_STABLE | \
+ EIC7700_INT_TMCLK_STABLE)
+#define EIC7700_HOST_VAL_STABLE BIT(0)
+
+/* strength definition */
+#define PHYCTRL_DR_33OHM 0xee
+#define PHYCTRL_DR_40OHM 0xcc
+#define PHYCTRL_DR_50OHM 0x88
+#define PHYCTRL_DR_66OHM 0x44
+#define PHYCTRL_DR_100OHM 0x00
+
+#define MAX_PHASE_CODE 0xff
+#define TUNING_RANGE_THRESHOLD 40
+#define PHY_CLK_MAX_DELAY_MASK 0x7f
+#define PHY_DELAY_CODE_MAX 0x7f
+#define PHY_DELAY_CODE_EMMC 0x17
+#define PHY_DELAY_CODE_SD 0x55
+
enum dwcmshc_rk_type {
DWCMSHC_RK3568,
DWCMSHC_RK3588,
@@ -217,6 +263,11 @@ struct rk35xx_priv {
u8 txclk_tapnum;
};
+struct eic7700_priv {
+ struct reset_control *reset;
+ unsigned int drive_impedance;
+};
+
#define DWCMSHC_MAX_OTHER_CLKS 3
struct dwcmshc_priv {
@@ -234,10 +285,22 @@ struct dwcmshc_priv {
struct dwcmshc_pltfm_data {
const struct sdhci_pltfm_data pdata;
+ const struct cqhci_host_ops *cqhci_host_ops;
int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
};
+static void dwcmshc_enable_card_clk(struct sdhci_host *host)
+{
+ u16 ctrl;
+
+ ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
+ ctrl |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
+ }
+}
+
static int dwcmshc_get_enable_other_clks(struct device *dev,
struct dwcmshc_priv *priv,
int num_clks,
@@ -289,6 +352,19 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
+static void dwcmshc_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ /* The dwcmshc does not comply with the SDHCI specification
+ * regarding the "Software Reset for CMD line should clear 'Command
+ * Complete' in the Normal Interrupt Status Register." Clear the bit
+ * here to compensate for this quirk.
+ */
+ if (mask & SDHCI_RESET_CMD)
+ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+}
+
static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -561,6 +637,73 @@ static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc)
sdhci_dumpregs(mmc_priv(mmc));
}
+static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /* Set Send Status Command Idle Timer to 10.66us (256 * 1 / 24) */
+ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
+ reg = (reg & ~CQHCI_SSC1_CIT_MASK) | 0x0100;
+ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
+
+ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+ reg |= CQHCI_ENABLE;
+ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+}
+
+static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ while (reg & SDHCI_DATA_AVAILABLE) {
+ sdhci_readl(host, SDHCI_BUFFER);
+ reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
+ }
+
+ sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
+
+ sdhci_cqe_enable(mmc);
+}
+
+static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ unsigned long flags;
+ u32 ctrl;
+
+ /*
+ * During CQE command transfers, command complete bit gets latched.
+ * So s/w should clear command complete interrupt status when CQE is
+ * either halted or disabled. Otherwise unexpected SDCHI legacy
+ * interrupt gets triggered when CQE is halted/disabled.
+ */
+ spin_lock_irqsave(&host->lock, flags);
+ ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
+ ctrl |= SDHCI_INT_RESPONSE;
+ sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
+ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ sdhci_cqe_disable(mmc, recovery);
+}
+
+static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 ctrl;
+
+ ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+ ctrl &= ~CQHCI_ENABLE;
+ sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
+}
+
static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -588,10 +731,11 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
sdhci_set_clock(host, clock);
- /* Disable cmd conflict check */
+ /* Disable cmd conflict check and internal clock gate */
reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
extra = sdhci_readl(host, reg);
extra &= ~BIT(0);
+ extra |= BIT(4);
sdhci_writel(host, extra, reg);
if (clock <= 52000000) {
@@ -679,6 +823,10 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *priv = dwc_priv->priv;
+ u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
+
+ if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
+ cqhci_deactivate(host->mmc);
if (mask & SDHCI_RESET_ALL && priv->reset) {
reset_control_assert(priv->reset);
@@ -687,6 +835,9 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
}
sdhci_reset(host, mask);
+
+ /* Enable INTERNAL CLOCK */
+ sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
}
static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
@@ -832,15 +983,7 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask)
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u16 ctrl_2;
- sdhci_reset(host, mask);
-
- /* The T-Head 1520 SoC does not comply with the SDHCI specification
- * regarding the "Software Reset for CMD line should clear 'Command
- * Complete' in the Normal Interrupt Status Register." Clear the bit
- * here to compensate for this quirk.
- */
- if (mask & SDHCI_RESET_CMD)
- sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
+ dwcmshc_reset(host, mask);
if (priv->flags & FLAG_IO_FIXED_1V8) {
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
@@ -886,7 +1029,7 @@ static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask)
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 val, emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
- sdhci_reset(host, mask);
+ dwcmshc_reset(host, mask);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL);
@@ -958,7 +1101,7 @@ static void cv18xx_sdhci_post_tuning(struct sdhci_host *host)
val |= SDHCI_INT_DATA_AVAIL;
sdhci_writel(host, val, SDHCI_INT_STATUS);
- sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ dwcmshc_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
}
static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
@@ -1095,12 +1238,417 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
ARRAY_SIZE(clk_ids), clk_ids);
}
+static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ u16 clk;
+
+ host->mmc->actual_clock = clock;
+
+ if (clock == 0) {
+ sdhci_set_clock(host, clock);
+ return;
+ }
+
+ clk_set_rate(pltfm_host->clk, clock);
+
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+ dwcmshc_enable_card_clk(host);
+}
+
+static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay)
+{
+ delay &= PHY_CLK_MAX_DELAY_MASK;
+
+ /* phy clk delay line config */
+ sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
+ sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
+ sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
+}
+
+static void sdhci_eic7700_config_phy(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+ struct eic7700_priv *priv = dwc_priv->priv;
+ unsigned int val, drv;
+
+ drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF);
+ drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF);
+
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+ val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+ val |= DWCMSHC_CARD_IS_EMMC;
+ sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
+ }
+
+ /* reset phy, config phy's pad */
+ sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
+
+ /* configure phy pads */
+ val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
+ val |= PHY_PAD_RXSEL_1V8;
+ sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
+ sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
+
+ /* Clock PAD Setting */
+ val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
+
+ /* PHY strobe PAD setting (EMMC only) */
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+ val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
+ val |= PHY_PAD_RXSEL_1V8;
+ sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
+ }
+ usleep_range(2000, 3000);
+ sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
+ sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
+}
+
+static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask)
+{
+ sdhci_reset(host, mask);
+
+ /* after reset all, the phy's config will be clear */
+ if (mask == SDHCI_RESET_ALL)
+ sdhci_eic7700_config_phy(host);
+}
+
+static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv)
+{
+ int ret;
+
+ priv->reset = devm_reset_control_array_get_optional_exclusive(dev);
+ if (IS_ERR(priv->reset)) {
+ ret = PTR_ERR(priv->reset);
+ dev_err(dev, "failed to get reset control %d\n", ret);
+ return ret;
+ }
+
+ ret = reset_control_assert(priv->reset);
+ if (ret) {
+ dev_err(dev, "Failed to assert reset signals: %d\n", ret);
+ return ret;
+ }
+ usleep_range(2000, 2100);
+ ret = reset_control_deassert(priv->reset);
+ if (ret) {
+ dev_err(dev, "Failed to deassert reset signals: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, unsigned int dr_ohm)
+{
+ switch (dr_ohm) {
+ case 100:
+ return PHYCTRL_DR_100OHM;
+ case 66:
+ return PHYCTRL_DR_66OHM;
+ case 50:
+ return PHYCTRL_DR_50OHM;
+ case 40:
+ return PHYCTRL_DR_40OHM;
+ case 33:
+ return PHYCTRL_DR_33OHM;
+ }
+
+ dev_warn(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm);
+ return PHYCTRL_DR_50OHM;
+}
+
+static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ int delay_min = -1;
+ int delay_max = -1;
+ int cmd_error = 0;
+ int delay = 0;
+ int i = 0;
+ int ret;
+
+ for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
+ sdhci_eic7700_config_phy_delay(host, i);
+ ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+ if (ret) {
+ host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ usleep_range(200, 210);
+ if (delay_min != -1 && delay_max != -1)
+ break;
+ } else {
+ if (delay_min == -1) {
+ delay_min = i;
+ continue;
+ } else {
+ delay_max = i;
+ continue;
+ }
+ }
+ }
+ if (delay_min == -1 && delay_max == -1) {
+ pr_err("%s: delay code tuning failed!\n", mmc_hostname(host->mmc));
+ sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
+ return ret;
+ }
+
+ delay = (delay_min + delay_max) / 2;
+ sdhci_eic7700_config_phy_delay(host, delay);
+
+ return 0;
+}
+
+static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
+ int phase_code = -1;
+ int code_range = -1;
+ bool is_sd = false;
+ int code_min = -1;
+ int code_max = -1;
+ int cmd_error = 0;
+ int ret = 0;
+ int i = 0;
+
+ if ((host->mmc->caps2 & sd_caps) == sd_caps)
+ is_sd = true;
+
+ for (i = 0; i <= MAX_PHASE_CODE; i++) {
+ /* Centered Phase code */
+ sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+ ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+ host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ if (ret) {
+ /* SD specific range tracking */
+ if (is_sd && code_min != -1 && code_max != -1) {
+ if (code_max - code_min > code_range) {
+ code_range = code_max - code_min;
+ phase_code = (code_min + code_max) / 2;
+ if (code_range > TUNING_RANGE_THRESHOLD)
+ break;
+ }
+ code_min = -1;
+ code_max = -1;
+ }
+ /* EMMC breaks after first valid range */
+ if (!is_sd && code_min != -1 && code_max != -1)
+ break;
+ } else {
+ /* Track valid phase code range */
+ if (code_min == -1) {
+ code_min = i;
+ if (!is_sd)
+ continue;
+ }
+ code_max = i;
+ if (is_sd && i == MAX_PHASE_CODE) {
+ if (code_max - code_min > code_range) {
+ code_range = code_max - code_min;
+ phase_code = (code_min + code_max) / 2;
+ }
+ }
+ }
+ }
+
+ /* Handle tuning failure case */
+ if ((is_sd && phase_code == -1) ||
+ (!is_sd && code_min == -1 && code_max == -1)) {
+ pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc));
+ sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+ return -EIO;
+ }
+ if (!is_sd)
+ phase_code = (code_min + code_max) / 2;
+
+ sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+
+ /* SD specific final verification */
+ if (is_sd) {
+ ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
+ host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ if (ret) {
+ pr_err("%s: Final phase code 0x%x verification failed!\n",
+ mmc_hostname(host->mmc), phase_code);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+ int ret = 0;
+ u16 ctrl;
+ u32 val;
+
+ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
+
+ val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+ val |= AT_CTRL_SW_TUNE_EN;
+ sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+
+ sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
+ sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
+
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
+ ret = sdhci_eic7700_delay_tuning(host, opcode);
+ if (ret)
+ return ret;
+ }
+
+ ret = sdhci_eic7700_phase_code_tuning(host, opcode);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u8 status;
+ u32 val;
+ int ret;
+
+ dwcmshc_set_uhs_signaling(host, timing);
+
+ /* here need make dll locked when in hs400 at 200MHz */
+ if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) {
+ val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+ val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY));
+ /* 2-cycle latency */
+ val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2);
+ sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
+
+ sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) |
+ 0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */
+ /* DLL jump step input */
+ sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
+ sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK,
+ PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R);
+ /* Sets the value of DLL's offset input */
+ sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
+ /*
+ * Sets the value of DLL's olbt loadval input. Controls the Ibt
+ * timer's timeout value at which DLL runs a revalidation cycle.
+ */
+ sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
+ sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
+ usleep_range(100, 110);
+
+ ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000,
+ false, host, PHY_DLL_STATUS_R);
+ if (ret) {
+ pr_err("%s: DLL lock timeout! status: 0x%x\n",
+ mmc_hostname(host->mmc), status);
+ return;
+ }
+
+ status = sdhci_readb(host, PHY_DLL_STATUS_R);
+ if (status & DLL_ERROR_STS) {
+ pr_err("%s: DLL lock failed!err_status:0x%x\n",
+ mmc_hostname(host->mmc), status);
+ }
+ }
+}
+
+static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing)
+{
+ u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
+
+ if ((host->mmc->caps2 & sd_caps) == sd_caps)
+ sdhci_set_uhs_signaling(host, timing);
+ else
+ sdhci_eic7700_set_uhs_signaling(host, timing);
+}
+
+static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+{
+ u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
+ unsigned int val, hsp_int_status, hsp_pwr_ctrl;
+ struct of_phandle_args args;
+ struct eic7700_priv *priv;
+ struct regmap *hsp_regmap;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dwc_priv->priv = priv;
+
+ ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
+ if (ret) {
+ dev_err(dev, "failed to reset\n");
+ return ret;
+ }
+
+ ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
+ if (ret) {
+ dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
+ return ret;
+ }
+
+ hsp_regmap = syscon_node_to_regmap(args.np);
+ if (IS_ERR(hsp_regmap)) {
+ dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
+ of_node_put(args.np);
+ return PTR_ERR(hsp_regmap);
+ }
+ hsp_int_status = args.args[0];
+ hsp_pwr_ctrl = args.args[1];
+ of_node_put(args.np);
+ /*
+ * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status.
+ * This signals to the eMMC controller that platform clocks (card, ACLK,
+ * BCLK, TMCLK) are enabled and stable.
+ */
+ regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE);
+ /*
+ * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl.
+ * This signals that VDD is stable and permits transition to high-speed
+ * modes (e.g., UHS-I).
+ */
+ regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE);
+
+ if ((host->mmc->caps2 & emmc_caps) == emmc_caps)
+ dwc_priv->delay_line = PHY_DELAY_CODE_EMMC;
+ else
+ dwc_priv->delay_line = PHY_DELAY_CODE_SD;
+
+ if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val))
+ priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val);
+ return 0;
+}
+
static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
.get_max_clock = dwcmshc_get_max_clock,
- .reset = sdhci_reset,
+ .reset = dwcmshc_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
.irq = dwcmshc_cqe_irq_handler,
};
@@ -1169,6 +1717,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
.platform_execute_tuning = th1520_execute_tuning,
};
+static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
+ .set_clock = sdhci_eic7700_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_eic7700_reset,
+ .set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper,
+ .set_power = sdhci_set_power_and_bus_voltage,
+ .irq = dwcmshc_cqe_irq_handler,
+ .platform_execute_tuning = sdhci_eic7700_executing_tuning,
+};
+
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_ops,
@@ -1188,6 +1748,15 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = {
};
#endif
+static const struct cqhci_host_ops rk35xx_cqhci_ops = {
+ .pre_enable = rk35xx_sdhci_cqe_pre_enable,
+ .enable = rk35xx_sdhci_cqe_enable,
+ .disable = rk35xx_sdhci_cqe_disable,
+ .post_disable = rk35xx_sdhci_cqe_post_disable,
+ .dumpregs = dwcmshc_cqhci_dumpregs,
+ .set_tran_desc = dwcmshc_set_tran_desc,
+};
+
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_rk35xx_ops,
@@ -1196,6 +1765,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
},
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
.init = dwcmshc_rk35xx_init,
.postinit = dwcmshc_rk35xx_postinit,
};
@@ -1208,6 +1778,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
},
+ .cqhci_host_ops = &rk35xx_cqhci_ops,
.init = dwcmshc_rk35xx_init,
.postinit = dwcmshc_rk3576_postinit,
};
@@ -1238,6 +1809,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
.init = sg2042_init,
};
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
+ .pdata = {
+ .ops = &sdhci_dwcmshc_eic7700_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+ },
+ .init = eic7700_init,
+};
+
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.enable = dwcmshc_sdhci_cqe_enable,
.disable = sdhci_cqe_disable,
@@ -1245,7 +1827,8 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.set_tran_desc = dwcmshc_set_tran_desc,
};
-static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
+static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev,
+ const struct dwcmshc_pltfm_data *pltfm_data)
{
struct cqhci_host *cq_host;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -1275,7 +1858,10 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
}
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
- cq_host->ops = &dwcmshc_cqhci_ops;
+ if (pltfm_data->cqhci_host_ops)
+ cq_host->ops = pltfm_data->cqhci_host_ops;
+ else
+ cq_host->ops = &dwcmshc_cqhci_ops;
/* Enable using of 128-bit task descriptors */
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
@@ -1338,6 +1924,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "sophgo,sg2042-dwcmshc",
.data = &sdhci_dwcmshc_sg2042_pdata,
},
+ {
+ .compatible = "eswin,eic7700-dwcmshc",
+ .data = &sdhci_dwcmshc_eic7700_pdata,
+ },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
@@ -1443,7 +2033,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
priv->vendor_specific_area2 =
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
- dwcmshc_cqhci_init(host, pdev);
+ dwcmshc_cqhci_init(host, pdev, pltfm_data);
}
if (pltfm_data->postinit)
@@ -1570,17 +2160,6 @@ disable_clk:
return ret;
}
-static void dwcmshc_enable_card_clk(struct sdhci_host *host)
-{
- u16 ctrl;
-
- ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
- if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
- ctrl |= SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
- }
-}
-
static int dwcmshc_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index c8cdb1c0722e..b9de03325c58 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -209,10 +209,8 @@ void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
irqreturn_t tmio_mmc_irq(int irq, void *devid);
-#ifdef CONFIG_PM
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
-#endif
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
{
diff --git a/drivers/most/most_usb.c b/drivers/most/most_usb.c
index cf5be9c449a5..41ee169f80c5 100644
--- a/drivers/most/most_usb.c
+++ b/drivers/most/most_usb.c
@@ -929,6 +929,10 @@ static void release_mdev(struct device *dev)
{
struct most_dev *mdev = to_mdev_from_dev(dev);
+ kfree(mdev->busy_urbs);
+ kfree(mdev->cap);
+ kfree(mdev->conf);
+ kfree(mdev->ep_address);
kfree(mdev);
}
/**
@@ -1054,7 +1058,7 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
ret = most_register_interface(&mdev->iface);
if (ret)
- goto err_free_busy_urbs;
+ return ret;
mutex_lock(&mdev->io_mutex);
if (le16_to_cpu(usb_dev->descriptor.idProduct) == USB_DEV_ID_OS81118 ||
@@ -1064,8 +1068,7 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
if (!mdev->dci) {
mutex_unlock(&mdev->io_mutex);
most_deregister_interface(&mdev->iface);
- ret = -ENOMEM;
- goto err_free_busy_urbs;
+ return -ENOMEM;
}
mdev->dci->dev.init_name = "dci";
@@ -1074,18 +1077,15 @@ hdm_probe(struct usb_interface *interface, const struct usb_device_id *id)
mdev->dci->dev.release = release_dci;
if (device_register(&mdev->dci->dev)) {
mutex_unlock(&mdev->io_mutex);
+ put_device(&mdev->dci->dev);
most_deregister_interface(&mdev->iface);
- ret = -ENOMEM;
- goto err_free_dci;
+ return -ENOMEM;
}
mdev->dci->usb_device = mdev->usb_device;
}
mutex_unlock(&mdev->io_mutex);
return 0;
-err_free_dci:
- put_device(&mdev->dci->dev);
-err_free_busy_urbs:
- kfree(mdev->busy_urbs);
+
err_free_ep_address:
kfree(mdev->ep_address);
err_free_cap:
@@ -1093,7 +1093,7 @@ err_free_cap:
err_free_conf:
kfree(mdev->conf);
err_free_mdev:
- put_device(&mdev->dev);
+ kfree(mdev);
return ret;
}
@@ -1121,13 +1121,6 @@ static void hdm_disconnect(struct usb_interface *interface)
if (mdev->dci)
device_unregister(&mdev->dci->dev);
most_deregister_interface(&mdev->iface);
-
- kfree(mdev->busy_urbs);
- kfree(mdev->cap);
- kfree(mdev->conf);
- kfree(mdev->ep_address);
- put_device(&mdev->dci->dev);
- put_device(&mdev->dev);
}
static int hdm_suspend(struct usb_interface *interface, pm_message_t message)
diff --git a/drivers/mtd/devices/docg3.h b/drivers/mtd/devices/docg3.h
index 2c0c5114e4e6..af6ef0ac1f11 100644
--- a/drivers/mtd/devices/docg3.h
+++ b/drivers/mtd/devices/docg3.h
@@ -274,11 +274,11 @@ struct docg3_cascade {
* @cascade: the cascade this device belongs to
* @device_id: number of the cascaded DoCG3 device (0, 1, 2 or 3)
* @if_cfg: if true, reads are on 16bits, else reads are on 8bits
-
* @reliable: if 0, docg3 in normal mode, if 1 docg3 in fast mode, if 2 in
* reliable mode
* Fast mode implies more errors than normal mode.
* Reliable mode implies that page 2*n and 2*n+1 are clones.
+ * @max_block: maximum block number for this device
* @bbt: bad block table cache
* @oob_write_ofs: offset of the MTD where this OOB should belong (ie. in next
* page_write)
diff --git a/drivers/mtd/devices/mtd_intel_dg.c b/drivers/mtd/devices/mtd_intel_dg.c
index b438ee5aacc3..2bab30dcd35f 100644
--- a/drivers/mtd/devices/mtd_intel_dg.c
+++ b/drivers/mtd/devices/mtd_intel_dg.c
@@ -15,14 +15,18 @@
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/pm_runtime.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/sizes.h>
#include <linux/types.h>
+#define INTEL_DG_NVM_RPM_TIMEOUT_MS 500
+
struct intel_dg_nvm {
struct kref refcnt;
struct mtd_info mtd;
+ struct device *dev;
struct mutex lock; /* region access lock */
void __iomem *base;
void __iomem *base2;
@@ -421,6 +425,8 @@ static int intel_dg_nvm_init(struct intel_dg_nvm *nvm, struct device *device,
unsigned int i, n;
int ret;
+ nvm->dev = device;
+
/* clean error register, previous errors are ignored */
idg_nvm_error(nvm);
@@ -498,6 +504,7 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
size_t len;
u8 region;
u64 addr;
+ int ret;
if (WARN_ON(!nvm))
return -EINVAL;
@@ -512,20 +519,29 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
total_len = info->len;
addr = info->addr;
+ ret = pm_runtime_resume_and_get(nvm->dev);
+ if (ret < 0) {
+ dev_err(&mtd->dev, "rpm: get failed %d\n", ret);
+ return ret;
+ }
+
+ ret = 0;
guard(mutex)(&nvm->lock);
while (total_len > 0) {
if (!IS_ALIGNED(addr, SZ_4K) || !IS_ALIGNED(total_len, SZ_4K)) {
dev_err(&mtd->dev, "unaligned erase %llx %zx\n", addr, total_len);
info->fail_addr = addr;
- return -ERANGE;
+ ret = -ERANGE;
+ break;
}
idx = idg_nvm_get_region(nvm, addr);
if (idx >= nvm->nregions) {
dev_err(&mtd->dev, "out of range");
info->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
- return -ERANGE;
+ ret = -ERANGE;
+ break;
}
from = addr - nvm->regions[idx].offset;
@@ -541,14 +557,16 @@ static int intel_dg_mtd_erase(struct mtd_info *mtd, struct erase_info *info)
if (bytes < 0) {
dev_dbg(&mtd->dev, "erase failed with %zd\n", bytes);
info->fail_addr += nvm->regions[idx].offset;
- return bytes;
+ ret = bytes;
+ break;
}
addr += len;
total_len -= len;
}
- return 0;
+ pm_runtime_put_autosuspend(nvm->dev);
+ return ret;
}
static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
@@ -577,17 +595,24 @@ static int intel_dg_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
if (len > nvm->regions[idx].size - from)
len = nvm->regions[idx].size - from;
+ ret = pm_runtime_resume_and_get(nvm->dev);
+ if (ret < 0) {
+ dev_err(&mtd->dev, "rpm: get failed %zd\n", ret);
+ return ret;
+ }
+
guard(mutex)(&nvm->lock);
ret = idg_read(nvm, region, from, len, buf);
if (ret < 0) {
dev_dbg(&mtd->dev, "read failed with %zd\n", ret);
- return ret;
+ } else {
+ *retlen = ret;
+ ret = 0;
}
- *retlen = ret;
-
- return 0;
+ pm_runtime_put_autosuspend(nvm->dev);
+ return ret;
}
static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
@@ -616,17 +641,24 @@ static int intel_dg_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
if (len > nvm->regions[idx].size - to)
len = nvm->regions[idx].size - to;
+ ret = pm_runtime_resume_and_get(nvm->dev);
+ if (ret < 0) {
+ dev_err(&mtd->dev, "rpm: get failed %zd\n", ret);
+ return ret;
+ }
+
guard(mutex)(&nvm->lock);
ret = idg_write(nvm, region, to, len, buf);
if (ret < 0) {
dev_dbg(&mtd->dev, "write failed with %zd\n", ret);
- return ret;
+ } else {
+ *retlen = ret;
+ ret = 0;
}
- *retlen = ret;
-
- return 0;
+ pm_runtime_put_autosuspend(nvm->dev);
+ return ret;
}
static void intel_dg_nvm_release(struct kref *kref)
@@ -753,6 +785,21 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
}
nvm->nregions = n; /* in case where kasprintf fail */
+ ret = devm_pm_runtime_enable(device);
+ if (ret < 0) {
+ dev_err(device, "rpm: enable failed %d\n", ret);
+ goto err_norpm;
+ }
+
+ pm_runtime_set_autosuspend_delay(device, INTEL_DG_NVM_RPM_TIMEOUT_MS);
+ pm_runtime_use_autosuspend(device);
+
+ ret = pm_runtime_resume_and_get(device);
+ if (ret < 0) {
+ dev_err(device, "rpm: get failed %d\n", ret);
+ goto err_norpm;
+ }
+
nvm->base = devm_ioremap_resource(device, &invm->bar);
if (IS_ERR(nvm->base)) {
ret = PTR_ERR(nvm->base);
@@ -781,9 +828,12 @@ static int intel_dg_mtd_probe(struct auxiliary_device *aux_dev,
dev_set_drvdata(&aux_dev->dev, nvm);
+ pm_runtime_put(device);
return 0;
err:
+ pm_runtime_put(device);
+err_norpm:
kref_put(&nvm->refcnt, intel_dg_nvm_release);
return ret;
}
diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c
index 290fd0119e98..cd37d58abacb 100644
--- a/drivers/mtd/lpddr/lpddr_cmds.c
+++ b/drivers/mtd/lpddr/lpddr_cmds.c
@@ -79,7 +79,7 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
mutex_init(&shared[i].lock);
for (j = 0; j < lpddr->qinfo->HWPartsNum; j++) {
*chip = lpddr->chips[i];
- chip->start += j << lpddr->chipshift;
+ chip->start += (unsigned long)j << lpddr->chipshift;
chip->oldstate = chip->state = FL_READY;
chip->priv = &shared[i];
/* those should be reset too since
@@ -559,7 +559,7 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
break;
if ((len + ofs - 1) >> lpddr->chipshift)
- thislen = (1<<lpddr->chipshift) - ofs;
+ thislen = (1UL << lpddr->chipshift) - ofs;
else
thislen = len;
/* get the chip */
@@ -575,7 +575,7 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len,
len -= thislen;
ofs = 0;
- last_end += 1 << lpddr->chipshift;
+ last_end += 1UL << lpddr->chipshift;
chipnum++;
chip = &lpddr->chips[chipnum];
}
@@ -601,7 +601,7 @@ static int lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len)
break;
if ((len + ofs - 1) >> lpddr->chipshift)
- thislen = (1<<lpddr->chipshift) - ofs;
+ thislen = (1UL << lpddr->chipshift) - ofs;
else
thislen = len;
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index 2ac79e1cedd9..206a3c463e6e 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -665,6 +665,7 @@ static void pcmciamtd_detach(struct pcmcia_device *link)
}
pcmciamtd_release(link);
+ kfree(dev);
}
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 8dc4f5c493fc..335c702633ff 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -599,6 +599,7 @@ mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp)
uint8_t *datbuf = NULL, *oobbuf = NULL;
size_t datbuf_len, oobbuf_len;
int ret = 0;
+ u64 end;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
@@ -618,7 +619,7 @@ mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp)
req.len &= 0xffffffff;
req.ooblen &= 0xffffffff;
- if (req.start + req.len > mtd->size)
+ if (check_add_overflow(req.start, req.len, &end) || end > mtd->size)
return -EINVAL;
datbuf_len = min_t(size_t, req.len, mtd->erasesize);
@@ -698,6 +699,7 @@ mtdchar_read_ioctl(struct mtd_info *mtd, struct mtd_read_req __user *argp)
size_t datbuf_len, oobbuf_len;
size_t orig_len, orig_ooblen;
int ret = 0;
+ u64 end;
if (copy_from_user(&req, argp, sizeof(req)))
return -EFAULT;
@@ -724,7 +726,7 @@ mtdchar_read_ioctl(struct mtd_info *mtd, struct mtd_read_req __user *argp)
req.len &= 0xffffffff;
req.ooblen &= 0xffffffff;
- if (req.start + req.len > mtd->size) {
+ if (check_add_overflow(req.start, req.len, &end) || end > mtd->size) {
ret = -EINVAL;
goto out;
}
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
index 994e8c51e674..2876501a7814 100644
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -425,9 +425,12 @@ int add_mtd_partitions(struct mtd_info *parent,
mtd_add_partition_attrs(child);
- /* Look for subpartitions */
+ /* Look for subpartitions (skip if no maching parser found) */
ret = parse_mtd_partitions(child, parts[i].types, NULL);
- if (ret < 0) {
+ if (ret < 0 && ret == -ENOENT) {
+ pr_debug("Skip parsing subpartitions: %d\n", ret);
+ continue;
+ } else if (ret < 0) {
pr_err("Failed to parse subpartitions: %d\n", ret);
goto err_del_partitions;
}
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 4a17271076bc..1e57c8de8578 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -63,7 +63,7 @@ config MTD_NAND_ECC_MEDIATEK
config MTD_NAND_ECC_REALTEK
tristate "Realtek RTL93xx hardware ECC engine"
- depends on HAS_IOMEM
+ depends on HAS_IOMEM && HAS_DMA
depends on MACH_REALTEK_RTL || COMPILE_TEST
select MTD_NAND_ECC
help
diff --git a/drivers/mtd/nand/ecc-realtek.c b/drivers/mtd/nand/ecc-realtek.c
index 7d718934c909..0046da37ea3e 100644
--- a/drivers/mtd/nand/ecc-realtek.c
+++ b/drivers/mtd/nand/ecc-realtek.c
@@ -380,7 +380,7 @@ static void rtl_ecc_cleanup_ctx(struct nand_device *nand)
nand_ecc_cleanup_req_tweaking(&ctx->req_ctx);
}
-static struct nand_ecc_engine_ops rtl_ecc_engine_ops = {
+static const struct nand_ecc_engine_ops rtl_ecc_engine_ops = {
.init_ctx = rtl_ecc_init_ctx,
.cleanup_ctx = rtl_ecc_cleanup_ctx,
.prepare_io_req = rtl_ecc_prepare_io_req,
@@ -418,8 +418,8 @@ static int rtl_ecc_probe(struct platform_device *pdev)
rtlc->buf = dma_alloc_noncoherent(dev, RTL_ECC_DMA_SIZE, &rtlc->buf_dma,
DMA_BIDIRECTIONAL, GFP_KERNEL);
- if (IS_ERR(rtlc->buf))
- return PTR_ERR(rtlc->buf);
+ if (!rtlc->buf)
+ return -ENOMEM;
rtlc->dev = dev;
rtlc->engine.dev = dev;
diff --git a/drivers/mtd/nand/onenand/onenand_samsung.c b/drivers/mtd/nand/onenand/onenand_samsung.c
index f37a6138e461..6d6aa709a21f 100644
--- a/drivers/mtd/nand/onenand/onenand_samsung.c
+++ b/drivers/mtd/nand/onenand/onenand_samsung.c
@@ -906,7 +906,7 @@ static int s3c_onenand_probe(struct platform_device *pdev)
err = devm_request_irq(&pdev->dev, r->start,
s5pc110_onenand_irq,
IRQF_SHARED, "onenand",
- &onenand);
+ onenand);
if (err) {
dev_err(&pdev->dev, "failed to get irq\n");
return err;
diff --git a/drivers/mtd/nand/raw/cadence-nand-controller.c b/drivers/mtd/nand/raw/cadence-nand-controller.c
index 6667eea95597..5f037753f78c 100644
--- a/drivers/mtd/nand/raw/cadence-nand-controller.c
+++ b/drivers/mtd/nand/raw/cadence-nand-controller.c
@@ -199,6 +199,7 @@
/* Common settings. */
#define COMMON_SET 0x1008
+#define OPR_MODE_NVDDR BIT(0)
/* 16 bit device connected to the NAND Flash interface. */
#define COMMON_SET_DEVICE_16BIT BIT(8)
@@ -211,12 +212,20 @@
#define SKIP_BYTES_OFFSET_VALUE GENMASK(23, 0)
/* Timings configuration. */
+#define TOGGLE_TIMINGS_0 0x1014
+#define TOGGLE_TIMINGS_1 0x1018
+
#define ASYNC_TOGGLE_TIMINGS 0x101c
#define ASYNC_TOGGLE_TIMINGS_TRH GENMASK(28, 24)
#define ASYNC_TOGGLE_TIMINGS_TRP GENMASK(20, 16)
#define ASYNC_TOGGLE_TIMINGS_TWH GENMASK(12, 8)
#define ASYNC_TOGGLE_TIMINGS_TWP GENMASK(4, 0)
+#define SYNC_TIMINGS 0x1020
+#define SYNC_TCKWR GENMASK(21, 16)
+#define SYNC_TWRCK GENMASK(13, 8)
+#define SYNC_TCAD GENMASK(5, 0)
+
#define TIMINGS0 0x1024
#define TIMINGS0_TADL GENMASK(31, 24)
#define TIMINGS0_TCCS GENMASK(23, 16)
@@ -226,6 +235,7 @@
#define TIMINGS1 0x1028
#define TIMINGS1_TRHZ GENMASK(31, 24)
#define TIMINGS1_TWB GENMASK(23, 16)
+#define TIMINGS1_TCWAW GENMASK(15, 8)
#define TIMINGS1_TVDLY GENMASK(7, 0)
#define TIMINGS2 0x102c
@@ -243,14 +253,23 @@
/* Register controlling DQ related timing. */
#define PHY_DQ_TIMING 0x2000
+#define PHY_DQ_TIMING_OE_END GENMASK(2, 0)
+#define PHY_DQ_TIMING_OE_START GENMASK(6, 4)
+#define PHY_DQ_TIMING_TSEL_END GENMASK(11, 8)
+#define PHY_DQ_TIMING_TSEL_START GENMASK(15, 12)
+
/* Register controlling DSQ related timing. */
#define PHY_DQS_TIMING 0x2004
#define PHY_DQS_TIMING_DQS_SEL_OE_END GENMASK(3, 0)
+#define PHY_DQS_TIMING_DQS_SEL_OE_START GENMASK(7, 4)
+#define PHY_DQS_TIMING_DQS_SEL_TSEL_END GENMASK(11, 8)
#define PHY_DQS_TIMING_PHONY_DQS_SEL BIT(16)
#define PHY_DQS_TIMING_USE_PHONY_DQS BIT(20)
/* Register controlling the gate and loopback control related timing. */
#define PHY_GATE_LPBK_CTRL 0x2008
+#define PHY_GATE_LPBK_CTRL_GATE_CFG GENMASK(3, 0)
+#define PHY_GATE_LPBK_CTRL_GATE_CFG_CLOSE GENMASK(5, 4)
#define PHY_GATE_LPBK_CTRL_RDS GENMASK(24, 19)
/* Register holds the control for the master DLL logic. */
@@ -260,6 +279,12 @@
/* Register holds the control for the slave DLL logic. */
#define PHY_DLL_SLAVE_CTRL 0x2010
+/* Register controls the DQS related timing. */
+#define PHY_IE_TIMING 0x2014
+#define PHY_IE_TIMING_DQS_IE_START GENMASK(10, 8)
+#define PHY_IE_TIMING_DQ_IE_START GENMASK(18, 16)
+#define PHY_IE_TIMING_IE_ALWAYS_ON BIT(20)
+
/* This register handles the global control settings for the PHY. */
#define PHY_CTRL 0x2080
#define PHY_CTRL_SDR_DQS BIT(14)
@@ -375,15 +400,41 @@
#define BCH_MAX_NUM_CORR_CAPS 8
#define BCH_MAX_NUM_SECTOR_SIZES 2
+/* NVDDR mode specific parameters and register values based on cadence specs */
+#define NVDDR_PHY_RD_DELAY 29
+#define NVDDR_PHY_RD_DELAY_MAX 31
+#define NVDDR_GATE_CFG_OPT 14
+#define NVDDR_GATE_CFG_STD 7
+#define NVDDR_GATE_CFG_MAX 15
+#define NVDDR_DATA_SEL_OE_START 1
+#define NVDDR_DATA_SEL_OE_START_MAX 7
+#define NVDDR_DATA_SEL_OE_END 6
+#define NVDDR_DATA_SEL_OE_END_MIN 4
+#define NVDDR_DATA_SEL_OE_END_MAX 15
+#define NVDDR_RS_HIGH_WAIT_CNT 7
+#define NVDDR_RS_IDLE_CNT 7
+#define NVDDR_TCWAW_DELAY 250000
+#define NVDDR_TVDLY_DELAY 500000
+#define NVDDR_TOGGLE_TIMINGS_0 0x00000301
+#define NVDDR_TOGGLE_TIMINGS_1 0x0a060102
+#define NVDDR_ASYNC_TOGGLE_TIMINGS 0
+#define NVDDR_PHY_CTRL 0x00004000
+#define NVDDR_PHY_TSEL 0
+#define NVDDR_PHY_DLL_MASTER_CTRL 0x00140004
+#define NVDDR_PHY_DLL_SLAVE_CTRL 0x00003c3c
+
struct cadence_nand_timings {
u32 async_toggle_timings;
+ u32 sync_timings;
u32 timings0;
u32 timings1;
u32 timings2;
u32 dll_phy_ctrl;
u32 phy_ctrl;
+ u32 phy_dq_timing;
u32 phy_dqs_timing;
u32 phy_gate_lpbk_ctrl;
+ u32 phy_ie_timing;
};
/* Command DMA descriptor. */
@@ -2345,11 +2396,9 @@ static inline u32 calc_tdvw(u32 trp_cnt, u32 clk_period, u32 trhoh_min,
return (trp_cnt + 1) * clk_period + trhoh_min - trea_max;
}
-static int
-cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
- const struct nand_interface_config *conf)
+static int cadence_nand_setup_sdr_interface(struct nand_chip *chip,
+ const struct nand_sdr_timings *sdr)
{
- const struct nand_sdr_timings *sdr;
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
struct cadence_nand_timings *t = &cdns_chip->timings;
@@ -2370,13 +2419,8 @@ cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
u32 dll_phy_dqs_timing = 0, phony_dqs_timing = 0, rd_del_sel = 0;
u32 sampling_point;
- sdr = nand_get_sdr_timings(conf);
- if (IS_ERR(sdr))
- return PTR_ERR(sdr);
-
memset(t, 0, sizeof(*t));
/* Sampling point calculation. */
-
if (cdns_ctrl->caps2.is_phy_type_dll)
phony_dqs_mod = 2;
else
@@ -2633,10 +2677,221 @@ cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
PHY_DLL_MASTER_CTRL_BYPASS_MODE);
dev_dbg(cdns_ctrl->dev, "PHY_DLL_SLAVE_CTRL_REG_SDR\t%x\n", 0);
}
+ return 0;
+}
+
+static int
+cadence_nand_setup_nvddr_interface(struct nand_chip *chip,
+ const struct nand_nvddr_timings *nvddr)
+{
+ struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
+ struct cdns_nand_chip *cdns_chip = to_cdns_nand_chip(chip);
+ struct cadence_nand_timings *t = &cdns_chip->timings;
+ u32 board_delay = cdns_ctrl->board_delay;
+ u32 clk_period = DIV_ROUND_DOWN_ULL(1000000000000ULL,
+ cdns_ctrl->nf_clk_rate);
+ u32 ddr_clk_ctrl_period = clk_period * 2;
+ u32 if_skew = cdns_ctrl->caps1->if_skew;
+ u32 tceh_cnt, tcs_cnt, tadl_cnt, tccs_cnt;
+ u32 twrck_cnt, tcad_cnt, tckwr_cnt = 0;
+ u32 tfeat_cnt, trhz_cnt, tvdly_cnt, tcwaw_cnt;
+ u32 trhw_cnt, twb_cnt, twhr_cnt;
+ u32 oe_start, oe_end, oe_end_dqsd;
+ u32 rd_del_sel = 0;
+ u32 dqs_driven_by_device, dqs_toogle_by_device, gate_open_delay;
+ u32 dll_phy_gate_open_delay, gate_close_delay, ie_start;
+ u32 dll_phy_rd_delay;
+ u32 reg;
+
+ memset(t, 0, sizeof(*t));
+ twrck_cnt = calc_cycl(nvddr->tWRCK_min, ddr_clk_ctrl_period);
+ tcad_cnt = calc_cycl(nvddr->tCAD_min, ddr_clk_ctrl_period);
+
+ reg = FIELD_PREP(SYNC_TWRCK, twrck_cnt);
+ reg |= FIELD_PREP(SYNC_TCAD, tcad_cnt);
+ t->sync_timings = reg;
+ dev_dbg(cdns_ctrl->dev, "SYNC_TIMINGS_NVDDR\t%08x\n", reg);
+
+ tadl_cnt = calc_cycl((nvddr->tADL_min + if_skew), ddr_clk_ctrl_period);
+ tccs_cnt = calc_cycl((nvddr->tCCS_min + if_skew), ddr_clk_ctrl_period);
+ twhr_cnt = calc_cycl((nvddr->tWHR_min + if_skew), ddr_clk_ctrl_period);
+ trhw_cnt = calc_cycl((nvddr->tRHW_min + if_skew), ddr_clk_ctrl_period);
+ reg = FIELD_PREP(TIMINGS0_TADL, tadl_cnt);
+ reg |= FIELD_PREP(TIMINGS0_TCCS, tccs_cnt);
+ reg |= FIELD_PREP(TIMINGS0_TWHR, twhr_cnt);
+ reg |= FIELD_PREP(TIMINGS0_TRHW, trhw_cnt);
+ t->timings0 = reg;
+ dev_dbg(cdns_ctrl->dev, "TIMINGS0_NVDDR\t%08x\n", reg);
+ twb_cnt = calc_cycl((nvddr->tWB_max + board_delay),
+ ddr_clk_ctrl_period);
+ /*
+ * Because of the two stage syncflop the value must be increased by 3
+ * first value is related with sync, second value is related
+ * with output if delay.
+ */
+ twb_cnt = twb_cnt + 3 + 5;
+ tvdly_cnt = calc_cycl(NVDDR_TVDLY_DELAY + if_skew, ddr_clk_ctrl_period);
+ tcwaw_cnt = calc_cycl(NVDDR_TCWAW_DELAY, ddr_clk_ctrl_period);
+ trhz_cnt = 1;
+ reg = FIELD_PREP(TIMINGS1_TWB, twb_cnt);
+ reg |= FIELD_PREP(TIMINGS1_TVDLY, tvdly_cnt);
+ reg |= FIELD_PREP(TIMINGS1_TRHZ, trhz_cnt);
+ reg |= FIELD_PREP(TIMINGS1_TCWAW, tcwaw_cnt);
+ t->timings1 = reg;
+ dev_dbg(cdns_ctrl->dev, "TIMINGS1_NVDDR\t%08x\n", reg);
+
+ tfeat_cnt = calc_cycl(nvddr->tFEAT_max, ddr_clk_ctrl_period);
+ if (tfeat_cnt < twb_cnt)
+ tfeat_cnt = twb_cnt;
+
+ tceh_cnt = calc_cycl(nvddr->tCEH_min, ddr_clk_ctrl_period);
+ tcs_cnt = calc_cycl((nvddr->tCS_min + if_skew), ddr_clk_ctrl_period);
+ reg = FIELD_PREP(TIMINGS2_TFEAT, tfeat_cnt);
+ reg |= FIELD_PREP(TIMINGS2_CS_HOLD_TIME, tceh_cnt);
+ reg |= FIELD_PREP(TIMINGS2_CS_SETUP_TIME, tcs_cnt);
+ t->timings2 = reg;
+ dev_dbg(cdns_ctrl->dev, "TIMINGS2_NVDDR\t%08x\n", reg);
+
+ reg = FIELD_PREP(DLL_PHY_CTRL_RS_HIGH_WAIT_CNT, NVDDR_RS_HIGH_WAIT_CNT);
+ reg |= FIELD_PREP(DLL_PHY_CTRL_RS_IDLE_CNT, NVDDR_RS_IDLE_CNT);
+ t->dll_phy_ctrl = reg;
+ dev_dbg(cdns_ctrl->dev, "DLL_PHY_CTRL_NVDDR\t%08x\n", reg);
+
+ reg = PHY_CTRL_SDR_DQS;
+ t->phy_ctrl = reg;
+ dev_dbg(cdns_ctrl->dev, "PHY_CTRL_REG_NVDDR\t%08x\n", reg);
+
+ dqs_driven_by_device = (nvddr->tDQSD_max + board_delay) / 1000 +
+ if_skew;
+ dqs_toogle_by_device = (nvddr->tDQSCK_max + board_delay) / 1000 -
+ if_skew;
+ gate_open_delay = dqs_toogle_by_device / (clk_period / 1000);
+ if (dqs_toogle_by_device > clk_period / 1000) {
+ if (gate_open_delay > NVDDR_GATE_CFG_OPT)
+ dll_phy_gate_open_delay = NVDDR_GATE_CFG_MAX;
+ else
+ dll_phy_gate_open_delay = gate_open_delay + 1;
+ gate_close_delay = 0;
+ } else {
+ twrck_cnt = calc_cycl(dqs_driven_by_device * 1000, clk_period);
+ dll_phy_gate_open_delay = 1;
+ gate_close_delay = 0;
+
+ reg = FIELD_PREP(SYNC_TCKWR, tckwr_cnt);
+ reg |= FIELD_PREP(SYNC_TWRCK, twrck_cnt);
+ reg |= FIELD_PREP(SYNC_TCAD, tcad_cnt);
+ t->sync_timings = reg;
+ dev_dbg(cdns_ctrl->dev, "SYNC_TIMINGS_NVDDR\t%08x\n", reg);
+ }
+
+ if (dll_phy_gate_open_delay > NVDDR_GATE_CFG_STD)
+ ie_start = NVDDR_GATE_CFG_STD;
+ else
+ ie_start = dll_phy_gate_open_delay;
+
+ dll_phy_rd_delay = ((nvddr->tDQSCK_max + board_delay) +
+ (clk_period / 2)) / clk_period;
+ if (dll_phy_rd_delay <= NVDDR_PHY_RD_DELAY)
+ rd_del_sel = dll_phy_rd_delay + 2;
+ else
+ rd_del_sel = NVDDR_PHY_RD_DELAY_MAX;
+
+ reg = FIELD_PREP(PHY_GATE_LPBK_CTRL_GATE_CFG, dll_phy_gate_open_delay);
+ reg |= FIELD_PREP(PHY_GATE_LPBK_CTRL_GATE_CFG_CLOSE, gate_close_delay);
+ reg |= FIELD_PREP(PHY_GATE_LPBK_CTRL_RDS, rd_del_sel);
+ t->phy_gate_lpbk_ctrl = reg;
+ dev_dbg(cdns_ctrl->dev, "PHY_GATE_LPBK_CTRL_REG_NVDDR\t%08x\n", reg);
+
+ oe_end_dqsd = ((nvddr->tDQSD_max / 1000) / ((clk_period / 2) / 1000))
+ + NVDDR_DATA_SEL_OE_END_MIN;
+ oe_end = (NVDDR_DATA_SEL_OE_END_MIN + oe_end_dqsd) / 2;
+ if (oe_end > NVDDR_DATA_SEL_OE_END_MAX)
+ oe_end = NVDDR_DATA_SEL_OE_END_MAX;
+
+ oe_start = ((nvddr->tDQSHZ_max / 1000) / ((clk_period / 2) / 1000)) + 1;
+ if (oe_start > NVDDR_DATA_SEL_OE_START_MAX)
+ oe_start = NVDDR_DATA_SEL_OE_START_MAX;
+
+ reg = FIELD_PREP(PHY_DQ_TIMING_OE_END, NVDDR_DATA_SEL_OE_END);
+ reg |= FIELD_PREP(PHY_DQ_TIMING_OE_START, NVDDR_DATA_SEL_OE_START);
+ reg |= FIELD_PREP(PHY_DQ_TIMING_TSEL_END, NVDDR_DATA_SEL_OE_END);
+ reg |= FIELD_PREP(PHY_DQ_TIMING_TSEL_START, NVDDR_DATA_SEL_OE_START);
+ t->phy_dq_timing = reg;
+ dev_dbg(cdns_ctrl->dev, "PHY_DQ_TIMING_REG_NVDDR\t%08x\n", reg);
+
+ reg = FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_END, oe_end);
+ reg |= FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_OE_START, oe_start);
+ reg |= FIELD_PREP(PHY_DQS_TIMING_DQS_SEL_TSEL_END, oe_end);
+ t->phy_dqs_timing = reg;
+ dev_dbg(cdns_ctrl->dev, "PHY_DQS_TIMING_REG_NVDDR\t%08x\n", reg);
+
+ reg = FIELD_PREP(PHY_IE_TIMING_DQS_IE_START, ie_start);
+ reg |= FIELD_PREP(PHY_IE_TIMING_DQ_IE_START, ie_start);
+ reg |= FIELD_PREP(PHY_IE_TIMING_IE_ALWAYS_ON, 0);
+ t->phy_ie_timing = reg;
+ dev_dbg(cdns_ctrl->dev, "PHY_IE_TIMING_REG_NVDDR\t%08x\n", reg);
+
+ reg = readl_relaxed(cdns_ctrl->reg + DLL_PHY_CTRL);
+ reg &= ~(DLL_PHY_CTRL_DLL_RST_N |
+ DLL_PHY_CTRL_EXTENDED_RD_MODE |
+ DLL_PHY_CTRL_EXTENDED_WR_MODE);
+ writel_relaxed(reg, cdns_ctrl->reg + DLL_PHY_CTRL);
+ writel_relaxed(OPR_MODE_NVDDR, cdns_ctrl->reg + COMMON_SET);
+ writel_relaxed(NVDDR_TOGGLE_TIMINGS_0,
+ cdns_ctrl->reg + TOGGLE_TIMINGS_0);
+ writel_relaxed(NVDDR_TOGGLE_TIMINGS_1,
+ cdns_ctrl->reg + TOGGLE_TIMINGS_1);
+ writel_relaxed(NVDDR_ASYNC_TOGGLE_TIMINGS,
+ cdns_ctrl->reg + ASYNC_TOGGLE_TIMINGS);
+ writel_relaxed(t->sync_timings, cdns_ctrl->reg + SYNC_TIMINGS);
+ writel_relaxed(t->timings0, cdns_ctrl->reg + TIMINGS0);
+ writel_relaxed(t->timings1, cdns_ctrl->reg + TIMINGS1);
+ writel_relaxed(t->timings2, cdns_ctrl->reg + TIMINGS2);
+ writel_relaxed(t->dll_phy_ctrl, cdns_ctrl->reg + DLL_PHY_CTRL);
+ writel_relaxed(t->phy_ctrl, cdns_ctrl->reg + PHY_CTRL);
+ writel_relaxed(NVDDR_PHY_TSEL, cdns_ctrl->reg + PHY_TSEL);
+ writel_relaxed(t->phy_dq_timing, cdns_ctrl->reg + PHY_DQ_TIMING);
+ writel_relaxed(t->phy_dqs_timing, cdns_ctrl->reg + PHY_DQS_TIMING);
+ writel_relaxed(t->phy_gate_lpbk_ctrl,
+ cdns_ctrl->reg + PHY_GATE_LPBK_CTRL);
+ writel_relaxed(NVDDR_PHY_DLL_MASTER_CTRL,
+ cdns_ctrl->reg + PHY_DLL_MASTER_CTRL);
+ writel_relaxed(NVDDR_PHY_DLL_SLAVE_CTRL,
+ cdns_ctrl->reg + PHY_DLL_SLAVE_CTRL);
+ writel_relaxed(t->phy_ie_timing, cdns_ctrl->reg + PHY_IE_TIMING);
+ writel_relaxed((reg | DLL_PHY_CTRL_DLL_RST_N),
+ cdns_ctrl->reg + DLL_PHY_CTRL);
return 0;
}
+static int
+cadence_nand_setup_interface(struct nand_chip *chip, int chipnr,
+ const struct nand_interface_config *conf)
+{
+ int ret = 0;
+
+ if (chipnr < 0)
+ return ret;
+
+ if (nand_interface_is_sdr(conf)) {
+ const struct nand_sdr_timings *sdr = nand_get_sdr_timings(conf);
+
+ if (IS_ERR(sdr))
+ return PTR_ERR(sdr);
+
+ ret = cadence_nand_setup_sdr_interface(chip, sdr);
+ } else {
+ const struct nand_nvddr_timings *nvddr = nand_get_nvddr_timings(conf);
+
+ if (IS_ERR(nvddr))
+ return PTR_ERR(nvddr);
+
+ ret = cadence_nand_setup_nvddr_interface(chip, nvddr);
+ }
+ return ret;
+}
+
static int cadence_nand_attach_chip(struct nand_chip *chip)
{
struct cdns_nand_ctrl *cdns_ctrl = to_cdns_nand_ctrl(chip->controller);
@@ -2871,7 +3126,7 @@ cadence_nand_irq_cleanup(int irqnum, struct cdns_nand_ctrl *cdns_ctrl)
static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
{
dma_cap_mask_t mask;
- struct dma_device *dma_dev = cdns_ctrl->dmac->device;
+ struct dma_device *dma_dev;
int ret;
cdns_ctrl->cdma_desc = dma_alloc_coherent(cdns_ctrl->dev,
@@ -2915,6 +3170,7 @@ static int cadence_nand_init(struct cdns_nand_ctrl *cdns_ctrl)
}
}
+ dma_dev = cdns_ctrl->dmac->device;
cdns_ctrl->io.iova_dma = dma_map_resource(dma_dev->dev, cdns_ctrl->io.dma,
cdns_ctrl->io.size,
DMA_BIDIRECTIONAL, 0);
diff --git a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
index a750f5839e34..51f595fbc834 100644
--- a/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
@@ -191,7 +191,6 @@ static int gpmi_init(struct gpmi_nand_data *this)
r->gpmi_regs + HW_GPMI_CTRL1_SET);
err_out:
- pm_runtime_mark_last_busy(this->dev);
pm_runtime_put_autosuspend(this->dev);
return ret;
}
@@ -761,7 +760,6 @@ static int bch_set_geometry(struct gpmi_nand_data *this)
ret = 0;
err_out:
- pm_runtime_mark_last_busy(this->dev);
pm_runtime_put_autosuspend(this->dev);
return ret;
@@ -2667,7 +2665,6 @@ unmap:
this->bch = false;
out_pm:
- pm_runtime_mark_last_busy(this->dev);
pm_runtime_put_autosuspend(this->dev);
return ret;
diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c
index b54d76547ffb..3ca30e7dce33 100644
--- a/drivers/mtd/nand/raw/lpc32xx_slc.c
+++ b/drivers/mtd/nand/raw/lpc32xx_slc.c
@@ -854,7 +854,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
}
/* Start with WP disabled, if available */
- host->wp_gpio = gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
+ host->wp_gpio = devm_gpiod_get_optional(&pdev->dev, NULL, GPIOD_OUT_LOW);
res = PTR_ERR_OR_ZERO(host->wp_gpio);
if (res) {
if (res != -EPROBE_DEFER)
diff --git a/drivers/mtd/nand/raw/marvell_nand.c b/drivers/mtd/nand/raw/marvell_nand.c
index 303b3016a070..38b7eb5b992c 100644
--- a/drivers/mtd/nand/raw/marvell_nand.c
+++ b/drivers/mtd/nand/raw/marvell_nand.c
@@ -290,13 +290,16 @@ static const struct marvell_hw_ecc_layout marvell_nfc_layouts[] = {
MARVELL_LAYOUT( 2048, 512, 4, 1, 1, 2048, 32, 30, 0, 0, 0),
MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,32, 30),
MARVELL_LAYOUT( 2048, 512, 8, 2, 1, 1024, 0, 30,1024,64, 30),
- MARVELL_LAYOUT( 2048, 512, 16, 4, 4, 512, 0, 30, 0, 32, 30),
+ MARVELL_LAYOUT( 2048, 512, 12, 3, 2, 704, 0, 30,640, 0, 30),
+ MARVELL_LAYOUT( 2048, 512, 16, 5, 4, 512, 0, 30, 0, 32, 30),
MARVELL_LAYOUT( 4096, 512, 4, 2, 2, 2048, 32, 30, 0, 0, 0),
- MARVELL_LAYOUT( 4096, 512, 8, 4, 4, 1024, 0, 30, 0, 64, 30),
- MARVELL_LAYOUT( 4096, 512, 16, 8, 8, 512, 0, 30, 0, 32, 30),
+ MARVELL_LAYOUT( 4096, 512, 8, 5, 4, 1024, 0, 30, 0, 64, 30),
+ MARVELL_LAYOUT( 4096, 512, 12, 6, 5, 704, 0, 30,576, 32, 30),
+ MARVELL_LAYOUT( 4096, 512, 16, 9, 8, 512, 0, 30, 0, 32, 30),
MARVELL_LAYOUT( 8192, 512, 4, 4, 4, 2048, 0, 30, 0, 0, 0),
- MARVELL_LAYOUT( 8192, 512, 8, 8, 8, 1024, 0, 30, 0, 160, 30),
- MARVELL_LAYOUT( 8192, 512, 16, 16, 16, 512, 0, 30, 0, 32, 30),
+ MARVELL_LAYOUT( 8192, 512, 8, 9, 8, 1024, 0, 30, 0, 160, 30),
+ MARVELL_LAYOUT( 8192, 512, 12, 12, 11, 704, 0, 30,448, 64, 30),
+ MARVELL_LAYOUT( 8192, 512, 16, 17, 16, 512, 0, 30, 0, 32, 30),
};
/**
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c
index c7d9501f646b..ad6d66309597 100644
--- a/drivers/mtd/nand/raw/nand_base.c
+++ b/drivers/mtd/nand/raw/nand_base.c
@@ -6338,11 +6338,14 @@ static int nand_scan_tail(struct nand_chip *chip)
ecc->steps = mtd->writesize / ecc->size;
if (!base->ecc.ctx.nsteps)
base->ecc.ctx.nsteps = ecc->steps;
- if (ecc->steps * ecc->size != mtd->writesize) {
- WARN(1, "Invalid ECC parameters\n");
- ret = -EINVAL;
- goto err_nand_manuf_cleanup;
- }
+
+ /*
+ * Validity check: Warn if ECC parameters are not compatible with page size.
+ * Due to the custom handling of ECC blocks in certain controllers the check
+ * may result in an expected failure.
+ */
+ if (ecc->steps * ecc->size != mtd->writesize)
+ pr_warn("ECC parameters may be invalid in reference to underlying NAND chip\n");
if (!ecc->total) {
ecc->total = ecc->steps * ecc->bytes;
diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c
index ac8c1b80d7be..201dd62b9990 100644
--- a/drivers/mtd/nand/raw/renesas-nand-controller.c
+++ b/drivers/mtd/nand/raw/renesas-nand-controller.c
@@ -1336,7 +1336,10 @@ static int rnandc_probe(struct platform_device *pdev)
if (IS_ERR(rnandc->regs))
return PTR_ERR(rnandc->regs);
- devm_pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(&pdev->dev);
+ if (ret)
+ return ret;
+
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret < 0)
return ret;
diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c
index f6a8e8ae819d..9dcdc93734cb 100644
--- a/drivers/mtd/nand/raw/sunxi_nand.c
+++ b/drivers/mtd/nand/raw/sunxi_nand.c
@@ -29,6 +29,12 @@
#include <linux/iopoll.h>
#include <linux/reset.h>
+/* non compile-time field get/prep */
+#undef field_get
+#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
+#undef field_prep
+#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
+
#define NFC_REG_CTL 0x0000
#define NFC_REG_ST 0x0004
#define NFC_REG_INT 0x0008
@@ -45,13 +51,40 @@
#define NFC_REG_A23_IO_DATA 0x0300
#define NFC_REG_ECC_CTL 0x0034
#define NFC_REG_ECC_ST 0x0038
-#define NFC_REG_DEBUG 0x003C
-#define NFC_REG_ECC_ERR_CNT(x) ((0x0040 + (x)) & ~0x3)
-#define NFC_REG_USER_DATA(x) (0x0050 + ((x) * 4))
-#define NFC_REG_SPARE_AREA 0x00A0
-#define NFC_REG_PAT_ID 0x00A4
+#define NFC_REG_H6_PAT_FOUND 0x003C
+#define NFC_REG_A10_ECC_ERR_CNT 0x0040
+#define NFC_REG_H6_ECC_ERR_CNT 0x0050
+#define NFC_REG_ECC_ERR_CNT(nfc, x) ((nfc->caps->reg_ecc_err_cnt + (x)) & ~0x3)
+#define NFC_REG_H6_RDATA_CTL 0x0044
+#define NFC_REG_H6_RDATA_0 0x0048
+#define NFC_REG_H6_RDATA_1 0x004C
+#define NFC_REG_A10_USER_DATA 0x0050
+#define NFC_REG_H6_USER_DATA 0x0080
+#define NFC_REG_USER_DATA(nfc, x) (nfc->caps->reg_user_data + ((x) * 4))
+#define NFC_REG_H6_USER_DATA_LEN 0x0070
+/* A USER_DATA_LEN register can hold the length of 8 USER_DATA registers */
+#define NFC_REG_USER_DATA_LEN_CAPACITY 8
+#define NFC_REG_USER_DATA_LEN(nfc, step) \
+ (nfc->caps->reg_user_data_len + \
+ ((step) / NFC_REG_USER_DATA_LEN_CAPACITY) * 4)
+#define NFC_REG_SPARE_AREA(nfc) (nfc->caps->reg_spare_area)
+#define NFC_REG_A10_SPARE_AREA 0x00A0
+#define NFC_REG_PAT_ID(nfc) (nfc->caps->reg_pat_id)
+#define NFC_REG_A10_PAT_ID 0x00A4
#define NFC_REG_MDMA_ADDR 0x00C0
#define NFC_REG_MDMA_CNT 0x00C4
+#define NFC_REG_H6_EFNAND_STATUS 0x0110
+#define NFC_REG_H6_SPARE_AREA 0x0114
+#define NFC_REG_H6_PAT_ID 0x0118
+#define NFC_REG_H6_DDR2_SPEC_CTL 0x011C
+#define NFC_REG_H6_NDMA_MODE_CTL 0x0120
+#define NFC_REG_H6_MDMA_DLBA_REG 0x0200
+#define NFC_REG_H6_MDMA_STA 0x0204
+#define NFC_REG_H6_MDMA_INT_MAS 0x0208
+#define NFC_REG_H6_MDMA_DESC_ADDR 0x020C
+#define NFC_REG_H6_MDMA_BUF_ADDR 0x0210
+#define NFC_REG_H6_MDMA_CNT 0x0214
+
#define NFC_RAM0_BASE 0x0400
#define NFC_RAM1_BASE 0x0800
@@ -63,6 +96,7 @@
#define NFC_BUS_WIDTH_16 (1 << 2)
#define NFC_RB_SEL_MSK BIT(3)
#define NFC_RB_SEL(x) ((x) << 3)
+/* CE_SEL BIT 27 is meant to be used for GPIO chipselect */
#define NFC_CE_SEL_MSK GENMASK(26, 24)
#define NFC_CE_SEL(x) ((x) << 24)
#define NFC_CE_CTL BIT(6)
@@ -81,6 +115,9 @@
#define NFC_STA BIT(4)
#define NFC_NATCH_INT_FLAG BIT(5)
#define NFC_RB_STATE(x) BIT(x + 8)
+#define NFC_RB_STATE_MSK GENMASK(11, 8)
+#define NDFC_RDATA_STA_1 BIT(12)
+#define NDFC_RDATA_STA_0 BIT(13)
/* define bit use in NFC_INT */
#define NFC_B2R_INT_ENABLE BIT(0)
@@ -92,6 +129,7 @@
/* define bit use in NFC_TIMING_CTL */
#define NFC_TIMING_CTL_EDO BIT(8)
+#define NFC_TIMING_CTL_E_EDO BIT(9)
/* define NFC_TIMING_CFG register layout */
#define NFC_TIMING_CFG(tWB, tADL, tWHR, tRHW, tCAD) \
@@ -99,9 +137,15 @@
(((tWHR) & 0x3) << 4) | (((tRHW) & 0x3) << 6) | \
(((tCAD) & 0x7) << 8))
+#define NFC_TIMING_CFG2(tCDQSS, tSC, tCLHZ, tCSS, tWC) \
+ ((((tCDQSS) & 0x1) << 11) | (((tSC) & 0x3) << 12) | \
+ (((tCLHZ) & 0x3) << 14) | (((tCSS) & 0x3) << 16) | \
+ (((tWC) & 0x3) << 18))
+
/* define bit use in NFC_CMD */
#define NFC_CMD_LOW_BYTE_MSK GENMASK(7, 0)
-#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8)
+#define NFC_CMD_HIGH_BYTE_MSK GENMASK(15, 8) /* 15-10 reserved on H6 */
+#define NFC_CMD_ADR_NUM_MSK GENMASK(9, 8)
#define NFC_CMD(x) (x)
#define NFC_ADR_NUM_MSK GENMASK(18, 16)
#define NFC_ADR_NUM(x) (((x) - 1) << 16)
@@ -114,6 +158,7 @@
#define NFC_SEQ BIT(25)
#define NFC_DATA_SWAP_METHOD BIT(26)
#define NFC_ROW_AUTO_INC BIT(27)
+#define NFC_H6_SEND_RND_CMD2 BIT(27)
#define NFC_SEND_CMD3 BIT(28)
#define NFC_SEND_CMD4 BIT(29)
#define NFC_CMD_TYPE_MSK GENMASK(31, 30)
@@ -125,6 +170,7 @@
#define NFC_READ_CMD_MSK GENMASK(7, 0)
#define NFC_RND_READ_CMD0_MSK GENMASK(15, 8)
#define NFC_RND_READ_CMD1_MSK GENMASK(23, 16)
+#define NFC_RND_READ_CMD2_MSK GENMASK(31, 24)
/* define bit use in NFC_WCMD_SET */
#define NFC_PROGRAM_CMD_MSK GENMASK(7, 0)
@@ -138,25 +184,46 @@
#define NFC_ECC_EXCEPTION BIT(4)
#define NFC_ECC_BLOCK_SIZE_MSK BIT(5)
#define NFC_ECC_BLOCK_512 BIT(5)
-#define NFC_RANDOM_EN BIT(9)
-#define NFC_RANDOM_DIRECTION BIT(10)
-#define NFC_ECC_MODE_MSK GENMASK(15, 12)
-#define NFC_ECC_MODE(x) ((x) << 12)
+#define NFC_RANDOM_EN(nfc) (nfc->caps->random_en_mask)
+#define NFC_RANDOM_DIRECTION(nfc) (nfc->caps->random_dir_mask)
+#define NFC_ECC_MODE_MSK(nfc) (nfc->caps->ecc_mode_mask)
+#define NFC_ECC_MODE(nfc, x) field_prep(NFC_ECC_MODE_MSK(nfc), (x))
+/* RANDOM_PAGE_SIZE: 0: ECC block size 1: page size */
+#define NFC_A23_RANDOM_PAGE_SIZE BIT(11)
+#define NFC_H6_RANDOM_PAGE_SIZE BIT(7)
#define NFC_RANDOM_SEED_MSK GENMASK(30, 16)
#define NFC_RANDOM_SEED(x) ((x) << 16)
/* define bit use in NFC_ECC_ST */
#define NFC_ECC_ERR(x) BIT(x)
-#define NFC_ECC_ERR_MSK GENMASK(15, 0)
-#define NFC_ECC_PAT_FOUND(x) BIT(x + 16)
+#define NFC_ECC_ERR_MSK(nfc) (nfc->caps->ecc_err_mask)
+
+/*
+ * define bit use in NFC_REG_PAT_FOUND
+ * For A10/A23, NFC_REG_PAT_FOUND == NFC_ECC_ST register
+ */
+#define NFC_ECC_PAT_FOUND_MSK(nfc) (nfc->caps->pat_found_mask)
+
#define NFC_ECC_ERR_CNT(b, x) (((x) >> (((b) % 4) * 8)) & 0xff)
-#define NFC_DEFAULT_TIMEOUT_MS 1000
+#define NFC_USER_DATA_LEN_MSK(step) \
+ (0xf << (((step) % NFC_REG_USER_DATA_LEN_CAPACITY) * 4))
-#define NFC_SRAM_SIZE 1024
+#define NFC_DEFAULT_TIMEOUT_MS 1000
#define NFC_MAX_CS 7
+/*
+ * On A10/A23, this is the size of the NDFC User Data Register, containing the
+ * mandatory user data bytes following the ECC for each ECC step.
+ * Thus, for each ECC step, we need the ECC bytes + USER_DATA_SZ.
+ * Those bits are currently unsused, and kept as default value 0xffffffff.
+ *
+ * On H6/H616, this size became configurable, from 0 bytes to 32, via the
+ * USER_DATA_LEN registers.
+ */
+#define USER_DATA_SZ 4
+
/**
* struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
*
@@ -211,13 +278,57 @@ static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
*
* @has_mdma: Use mbus dma mode, otherwise general dma
* through MBUS on A23/A33 needs extra configuration.
+ * @has_ecc_block_512: If the ECC can handle 512B or only 1024B chuncks
+ * @has_ecc_clk: If the controller needs an ECC clock.
+ * @has_mbus_clk: If the controller needs a mbus clock.
* @reg_io_data: I/O data register
+ * @reg_ecc_err_cnt: ECC error counter register
+ * @reg_user_data: User data register
+ * @reg_user_data_len: User data length register
+ * @reg_spare_area: Spare Area Register
+ * @reg_pat_id: Pattern ID Register
+ * @reg_pat_found: Data Pattern Status Register
+ * @random_en_mask: RANDOM_EN mask in NFC_ECC_CTL register
+ * @random_dir_mask: RANDOM_DIRECTION mask in NFC_ECC_CTL register
+ * @ecc_mode_mask: ECC_MODE mask in NFC_ECC_CTL register
+ * @ecc_err_mask: NFC_ECC_ERR mask in NFC_ECC_ST register
+ * @pat_found_mask: ECC_PAT_FOUND mask in NFC_REG_PAT_FOUND register
* @dma_maxburst: DMA maxburst
+ * @ecc_strengths: Available ECC strengths array
+ * @nstrengths: Size of @ecc_strengths
+ * @max_ecc_steps: Maximum supported steps for ECC, this is also the
+ * number of user data registers
+ * @user_data_len_tab: Table of lenghts supported by USER_DATA_LEN register
+ * The table index is the value to set in NFC_USER_DATA_LEN
+ * registers, and the corresponding value is the number of
+ * bytes to write
+ * @nuser_data_tab: Size of @user_data_len_tab
+ * @sram_size: Size of the NAND controller SRAM
*/
struct sunxi_nfc_caps {
bool has_mdma;
+ bool has_ecc_block_512;
+ bool has_ecc_clk;
+ bool has_mbus_clk;
unsigned int reg_io_data;
+ unsigned int reg_ecc_err_cnt;
+ unsigned int reg_user_data;
+ unsigned int reg_user_data_len;
+ unsigned int reg_spare_area;
+ unsigned int reg_pat_id;
+ unsigned int reg_pat_found;
+ unsigned int random_en_mask;
+ unsigned int random_dir_mask;
+ unsigned int ecc_mode_mask;
+ unsigned int ecc_err_mask;
+ unsigned int pat_found_mask;
unsigned int dma_maxburst;
+ const u8 *ecc_strengths;
+ unsigned int nstrengths;
+ const u8 *user_data_len_tab;
+ unsigned int nuser_data_tab;
+ unsigned int max_ecc_steps;
+ int sram_size;
};
/**
@@ -228,6 +339,8 @@ struct sunxi_nfc_caps {
* @regs: NAND controller registers
* @ahb_clk: NAND controller AHB clock
* @mod_clk: NAND controller mod clock
+ * @ecc_clk: NAND controller ECC clock
+ * @mbus_clk: NAND controller MBUS clock
* @reset: NAND controller reset line
* @assigned_cs: bitmask describing already assigned CS lines
* @clk_rate: NAND controller current clock rate
@@ -243,6 +356,8 @@ struct sunxi_nfc {
void __iomem *regs;
struct clk *ahb_clk;
struct clk *mod_clk;
+ struct clk *ecc_clk;
+ struct clk *mbus_clk;
struct reset_control *reset;
unsigned long assigned_cs;
unsigned long clk_rate;
@@ -431,7 +546,7 @@ static void sunxi_nfc_select_chip(struct nand_chip *nand, unsigned int cs)
if (sel->rb >= 0)
ctl |= NFC_RB_SEL(sel->rb);
- writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
+ writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA(nfc));
if (nfc->clk_rate != sunxi_nand->clk_rate) {
clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
@@ -455,7 +570,7 @@ static void sunxi_nfc_read_buf(struct nand_chip *nand, uint8_t *buf, int len)
while (len > offs) {
bool poll = false;
- cnt = min(len - offs, NFC_SRAM_SIZE);
+ cnt = min(len - offs, nfc->caps->sram_size);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
@@ -493,7 +608,7 @@ static void sunxi_nfc_write_buf(struct nand_chip *nand, const uint8_t *buf,
while (len > offs) {
bool poll = false;
- cnt = min(len - offs, NFC_SRAM_SIZE);
+ cnt = min(len - offs, nfc->caps->sram_size);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
@@ -623,13 +738,12 @@ static void sunxi_nfc_randomizer_config(struct nand_chip *nand, int page,
bool ecc)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- u32 ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
+ u32 ecc_ctl;
u16 state;
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
- ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL);
state = sunxi_nfc_randomizer_state(nand, page, ecc);
ecc_ctl = readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_SEED_MSK;
writel(ecc_ctl | NFC_RANDOM_SEED(state), nfc->regs + NFC_REG_ECC_CTL);
@@ -642,7 +756,7 @@ static void sunxi_nfc_randomizer_enable(struct nand_chip *nand)
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
- writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN,
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) | NFC_RANDOM_EN(nfc),
nfc->regs + NFC_REG_ECC_CTL);
}
@@ -653,7 +767,7 @@ static void sunxi_nfc_randomizer_disable(struct nand_chip *nand)
if (!(nand->options & NAND_NEED_SCRAMBLING))
return;
- writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
+ writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN(nfc),
nfc->regs + NFC_REG_ECC_CTL);
}
@@ -717,20 +831,66 @@ static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob,
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(step)),
- oob);
+ sunxi_nfc_user_data_to_buf(readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)), oob);
/* De-randomize the Bad Block Marker. */
if (bbm && (nand->options & NAND_NEED_SCRAMBLING))
sunxi_nfc_randomize_bbm(nand, page, oob);
}
+/*
+ * On H6/H6 the user_data length has to be set in specific registers
+ * before writing.
+ */
+static void sunxi_nfc_reset_user_data_len(struct sunxi_nfc *nfc)
+{
+ int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY;
+
+ /* not all SoCs have this register */
+ if (!nfc->caps->reg_user_data_len)
+ return;
+
+ for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step)
+ writel(0, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, i));
+}
+
+static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc,
+ int len, int step)
+{
+ bool found = false;
+ u32 val;
+ int i;
+
+ /* not all SoCs have this register */
+ if (!nfc->caps->reg_user_data_len)
+ return;
+
+ for (i = 0; i < nfc->caps->nuser_data_tab; i++) {
+ if (len == nfc->caps->user_data_len_tab[i]) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ dev_warn(nfc->dev,
+ "Unsupported length for user data reg: %d\n", len);
+ return;
+ }
+
+ val = readl(nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
+
+ val &= ~NFC_USER_DATA_LEN_MSK(step);
+ val |= field_prep(NFC_USER_DATA_LEN_MSK(step), i);
+ writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step));
+}
+
static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
const u8 *oob, int step,
bool bbm, int page)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
- u8 user_data[4];
+ u8 user_data[USER_DATA_SZ];
/* Randomize the Bad Block Marker. */
if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
@@ -740,7 +900,7 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
}
writel(sunxi_nfc_buf_to_user_data(oob),
- nfc->regs + NFC_REG_USER_DATA(step));
+ nfc->regs + NFC_REG_USER_DATA(nfc, step));
}
static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
@@ -757,7 +917,8 @@ static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
}
static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
- int step, u32 status, bool *erased)
+ int step, u32 status, u32 pattern_found,
+ bool *erased)
{
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
@@ -768,10 +929,10 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
if (status & NFC_ECC_ERR(step))
return -EBADMSG;
- if (status & NFC_ECC_PAT_FOUND(step)) {
+ if (pattern_found & BIT(step)) {
u8 pattern;
- if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID) & 0x1))) {
+ if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1))) {
pattern = 0x0;
} else {
pattern = 0xff;
@@ -782,12 +943,12 @@ static int sunxi_nfc_hw_ecc_correct(struct nand_chip *nand, u8 *data, u8 *oob,
memset(data, pattern, ecc->size);
if (oob)
- memset(oob, pattern, ecc->bytes + 4);
+ memset(oob, pattern, ecc->bytes + USER_DATA_SZ);
return 0;
}
- tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(step));
+ tmp = readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, step));
return NFC_ECC_ERR_CNT(step, tmp);
}
@@ -802,6 +963,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct nand_ecc_ctrl *ecc = &nand->ecc;
int raw_mode = 0;
+ u32 pattern_found;
bool erased;
int ret;
@@ -817,6 +979,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
if (ret)
return ret;
+ sunxi_nfc_reset_user_data_len(nfc);
+ sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
sunxi_nfc_randomizer_config(nand, page, false);
sunxi_nfc_randomizer_enable(nand);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
@@ -827,10 +991,14 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
if (ret)
return ret;
- *cur_off = oob_off + ecc->bytes + 4;
+ *cur_off = oob_off + ecc->bytes + USER_DATA_SZ;
+
+ pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
+ pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
ret = sunxi_nfc_hw_ecc_correct(nand, data, oob_required ? oob : NULL, 0,
readl(nfc->regs + NFC_REG_ECC_ST),
+ pattern_found,
&erased);
if (erased)
return 1;
@@ -847,11 +1015,11 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE,
ecc->size);
- nand_change_read_column_op(nand, oob_off, oob, ecc->bytes + 4,
- false);
+ nand_change_read_column_op(nand, oob_off, oob,
+ ecc->bytes + USER_DATA_SZ, false);
- ret = nand_check_erased_ecc_chunk(data, ecc->size,
- oob, ecc->bytes + 4,
+ ret = nand_check_erased_ecc_chunk(data, ecc->size, oob,
+ ecc->bytes + USER_DATA_SZ,
NULL, 0, ecc->strength);
if (ret >= 0)
raw_mode = 1;
@@ -861,7 +1029,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct nand_chip *nand,
if (oob_required) {
nand_change_read_column_op(nand, oob_off, NULL, 0,
false);
- sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + 4,
+ sunxi_nfc_randomizer_read_buf(nand, oob, ecc->bytes + USER_DATA_SZ,
true, page);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, 0,
@@ -911,7 +1079,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
unsigned int max_bitflips = 0;
int ret, i, raw_mode = 0;
struct scatterlist sg;
- u32 status, wait;
+ u32 status, pattern_found, wait;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
@@ -923,6 +1091,8 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
return ret;
sunxi_nfc_hw_ecc_enable(nand);
+ sunxi_nfc_reset_user_data_len(nfc);
+ sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
sunxi_nfc_randomizer_config(nand, page, false);
sunxi_nfc_randomizer_enable(nand);
@@ -952,17 +1122,20 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
return ret;
status = readl(nfc->regs + NFC_REG_ECC_ST);
+ pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found);
+ pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found);
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = i * (ecc->bytes + USER_DATA_SZ);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
bool erased;
ret = sunxi_nfc_hw_ecc_correct(nand, randomized ? data : NULL,
oob_required ? oob : NULL,
- i, status, &erased);
+ i, status, pattern_found,
+ &erased);
/* ECC errors are handled in the second loop. */
if (ret < 0)
@@ -972,7 +1145,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
/* TODO: use DMA to retrieve OOB */
nand_change_read_column_op(nand,
mtd->writesize + oob_off,
- oob, ecc->bytes + 4, false);
+ oob, ecc->bytes + USER_DATA_SZ, false);
sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, i,
!i, page);
@@ -984,10 +1157,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
sunxi_nfc_hw_ecc_update_stats(nand, &max_bitflips, ret);
}
- if (status & NFC_ECC_ERR_MSK) {
+ if (status & NFC_ECC_ERR_MSK(nfc)) {
for (i = 0; i < nchunks; i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = i * (ecc->bytes + USER_DATA_SZ);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
@@ -1007,10 +1180,10 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct nand_chip *nand, uint8_t *buf
/* TODO: use DMA to retrieve OOB */
nand_change_read_column_op(nand,
mtd->writesize + oob_off,
- oob, ecc->bytes + 4, false);
+ oob, ecc->bytes + USER_DATA_SZ, false);
- ret = nand_check_erased_ecc_chunk(data, ecc->size,
- oob, ecc->bytes + 4,
+ ret = nand_check_erased_ecc_chunk(data, ecc->size, oob,
+ ecc->bytes + USER_DATA_SZ,
NULL, 0,
ecc->strength);
if (ret >= 0)
@@ -1052,6 +1225,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
sunxi_nfc_randomizer_config(nand, page, false);
sunxi_nfc_randomizer_enable(nand);
+ sunxi_nfc_reset_user_data_len(nfc);
+ sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0);
sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, 0, bbm, page);
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
@@ -1063,7 +1238,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct nand_chip *nand,
if (ret)
return ret;
- *cur_off = oob_off + ecc->bytes + 4;
+ *cur_off = oob_off + ecc->bytes + USER_DATA_SZ;
return 0;
}
@@ -1074,7 +1249,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct nand_chip *nand,
{
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_ecc_ctrl *ecc = &nand->ecc;
- int offset = ((ecc->bytes + 4) * ecc->steps);
+ int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps);
int len = mtd->oobsize - offset;
if (len <= 0)
@@ -1107,7 +1282,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct nand_chip *nand, uint8_t *buf,
for (i = 0; i < ecc->steps; i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = i * (ecc->bytes + USER_DATA_SZ);
u8 *data = buf + data_off;
u8 *oob = nand->oob_poi + oob_off;
@@ -1166,7 +1341,7 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct nand_chip *nand,
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = i * (ecc->bytes + USER_DATA_SZ);
u8 *data = bufpoi + data_off;
u8 *oob = nand->oob_poi + oob_off;
@@ -1220,7 +1395,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct nand_chip *nand,
for (i = 0; i < ecc->steps; i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = i * (ecc->bytes + USER_DATA_SZ);
const u8 *data = buf + data_off;
const u8 *oob = nand->oob_poi + oob_off;
@@ -1258,7 +1433,7 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct nand_chip *nand,
for (i = data_offs / ecc->size;
i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) {
int data_off = i * ecc->size;
- int oob_off = i * (ecc->bytes + 4);
+ int oob_off = i * (ecc->bytes + USER_DATA_SZ);
const u8 *data = buf + data_off;
const u8 *oob = nand->oob_poi + oob_off;
@@ -1296,10 +1471,12 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *nand,
if (ret)
goto pio_fallback;
+ sunxi_nfc_reset_user_data_len(nfc);
for (i = 0; i < ecc->steps; i++) {
- const u8 *oob = nand->oob_poi + (i * (ecc->bytes + 4));
+ const u8 *oob = nand->oob_poi + (i * (ecc->bytes + USER_DATA_SZ));
sunxi_nfc_hw_ecc_set_prot_oob_bytes(nand, oob, i, !i, page);
+ sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, i);
}
nand_prog_page_begin_op(nand, page, 0, NULL, 0);
@@ -1567,7 +1744,7 @@ static int sunxi_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
if (section >= ecc->steps)
return -ERANGE;
- oobregion->offset = section * (ecc->bytes + 4) + 4;
+ oobregion->offset = section * (ecc->bytes + USER_DATA_SZ) + 4;
oobregion->length = ecc->bytes;
return 0;
@@ -1601,10 +1778,10 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
if (section == ecc->steps && ecc->engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST)
return -ERANGE;
- oobregion->offset = section * (ecc->bytes + 4);
+ oobregion->offset = section * (ecc->bytes + USER_DATA_SZ);
if (section < ecc->steps)
- oobregion->length = 4;
+ oobregion->length = USER_DATA_SZ;
else
oobregion->length = mtd->oobsize - oobregion->offset;
@@ -1620,9 +1797,9 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
struct nand_ecc_ctrl *ecc,
struct device_node *np)
{
- static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
+ const u8 *strengths = nfc->caps->ecc_strengths;
struct mtd_info *mtd = nand_to_mtd(nand);
struct nand_device *nanddev = mtd_to_nanddev(mtd);
int nsectors;
@@ -1638,7 +1815,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
bytes = (mtd->oobsize - 2) / nsectors;
/* 4 non-ECC bytes are added before each ECC bytes section */
- bytes -= 4;
+ bytes -= USER_DATA_SZ;
/* and bytes has to be even. */
if (bytes % 2)
@@ -1646,7 +1823,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
ecc->strength = bytes * 8 / fls(8 * ecc->size);
- for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+ for (i = 0; i < nfc->caps->nstrengths; i++) {
if (strengths[i] > ecc->strength)
break;
}
@@ -1667,7 +1844,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
}
/* Add ECC info retrieval from DT */
- for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+ for (i = 0; i < nfc->caps->nstrengths; i++) {
if (ecc->strength <= strengths[i]) {
/*
* Update ecc->strength value with the actual strength
@@ -1678,7 +1855,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
}
}
- if (i >= ARRAY_SIZE(strengths)) {
+ if (i >= nfc->caps->nstrengths) {
dev_err(nfc->dev, "unsupported strength\n");
return -ENOTSUPP;
}
@@ -1691,7 +1868,7 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
nsectors = mtd->writesize / ecc->size;
- if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
+ if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors))
return -EINVAL;
ecc->read_oob = sunxi_nfc_hw_ecc_read_oob;
@@ -1714,11 +1891,17 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
ecc->read_oob_raw = nand_read_oob_std;
ecc->write_oob_raw = nand_write_oob_std;
- sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(i) | NFC_ECC_EXCEPTION |
+ sunxi_nand->ecc.ecc_ctl = NFC_ECC_MODE(nfc, i) | NFC_ECC_EXCEPTION |
NFC_ECC_PIPELINE | NFC_ECC_EN;
- if (ecc->size == 512)
- sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512;
+ if (ecc->size == 512) {
+ if (nfc->caps->has_ecc_block_512) {
+ sunxi_nand->ecc.ecc_ctl |= NFC_ECC_BLOCK_512;
+ } else {
+ dev_err(nfc->dev, "512B ECC block not supported\n");
+ return -EOPNOTSUPP;
+ }
+ }
return 0;
}
@@ -1807,7 +1990,7 @@ static int sunxi_nfc_exec_subop(struct nand_chip *nand,
case NAND_OP_DATA_OUT_INSTR:
start = nand_subop_get_data_start_off(subop, i);
remaining = nand_subop_get_data_len(subop, i);
- cnt = min_t(u32, remaining, NFC_SRAM_SIZE);
+ cnt = min_t(u32, remaining, nfc->caps->sram_size);
cmd |= NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
if (instr->type == NAND_OP_DATA_OUT_INSTR) {
@@ -2094,6 +2277,10 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ nfc->caps = of_device_get_match_data(dev);
+ if (!nfc->caps)
+ return -EINVAL;
+
nfc->ahb_clk = devm_clk_get_enabled(dev, "ahb");
if (IS_ERR(nfc->ahb_clk)) {
dev_err(dev, "failed to retrieve ahb clk\n");
@@ -2106,6 +2293,22 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
return PTR_ERR(nfc->mod_clk);
}
+ if (nfc->caps->has_ecc_clk) {
+ nfc->ecc_clk = devm_clk_get_enabled(dev, "ecc");
+ if (IS_ERR(nfc->ecc_clk)) {
+ dev_err(dev, "failed to retrieve ecc clk\n");
+ return PTR_ERR(nfc->ecc_clk);
+ }
+ }
+
+ if (nfc->caps->has_mbus_clk) {
+ nfc->mbus_clk = devm_clk_get_enabled(dev, "mbus");
+ if (IS_ERR(nfc->mbus_clk)) {
+ dev_err(dev, "failed to retrieve mbus clk\n");
+ return PTR_ERR(nfc->mbus_clk);
+ }
+ }
+
nfc->reset = devm_reset_control_get_optional_exclusive(dev, "ahb");
if (IS_ERR(nfc->reset))
return PTR_ERR(nfc->reset);
@@ -2116,12 +2319,6 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
return ret;
}
- nfc->caps = of_device_get_match_data(&pdev->dev);
- if (!nfc->caps) {
- ret = -EINVAL;
- goto out_ahb_reset_reassert;
- }
-
ret = sunxi_nfc_rst(nfc);
if (ret)
goto out_ahb_reset_reassert;
@@ -2168,15 +2365,81 @@ static void sunxi_nfc_remove(struct platform_device *pdev)
dma_release_channel(nfc->dmac);
}
+static const u8 sunxi_ecc_strengths_a10[] = {
+ 16, 24, 28, 32, 40, 48, 56, 60, 64
+};
+
+static const u8 sunxi_ecc_strengths_h6[] = {
+ 16, 24, 28, 32, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80
+};
+
+static const u8 sunxi_user_data_len_h6[] = {
+ 0, 4, 8, 12, 16, 20, 24, 28, 32
+};
+
static const struct sunxi_nfc_caps sunxi_nfc_a10_caps = {
+ .has_ecc_block_512 = true,
.reg_io_data = NFC_REG_A10_IO_DATA,
+ .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
+ .reg_user_data = NFC_REG_A10_USER_DATA,
+ .reg_spare_area = NFC_REG_A10_SPARE_AREA,
+ .reg_pat_id = NFC_REG_A10_PAT_ID,
+ .reg_pat_found = NFC_REG_ECC_ST,
+ .random_en_mask = BIT(9),
+ .random_dir_mask = BIT(10),
+ .ecc_mode_mask = GENMASK(15, 12),
+ .ecc_err_mask = GENMASK(15, 0),
+ .pat_found_mask = GENMASK(31, 16),
.dma_maxburst = 4,
+ .ecc_strengths = sunxi_ecc_strengths_a10,
+ .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
+ .max_ecc_steps = 16,
+ .sram_size = 1024,
};
static const struct sunxi_nfc_caps sunxi_nfc_a23_caps = {
.has_mdma = true,
+ .has_ecc_block_512 = true,
+ .reg_io_data = NFC_REG_A23_IO_DATA,
+ .reg_ecc_err_cnt = NFC_REG_A10_ECC_ERR_CNT,
+ .reg_user_data = NFC_REG_A10_USER_DATA,
+ .reg_spare_area = NFC_REG_A10_SPARE_AREA,
+ .reg_pat_id = NFC_REG_A10_PAT_ID,
+ .reg_pat_found = NFC_REG_ECC_ST,
+ .random_en_mask = BIT(9),
+ .random_dir_mask = BIT(10),
+ .ecc_mode_mask = GENMASK(15, 12),
+ .ecc_err_mask = GENMASK(15, 0),
+ .pat_found_mask = GENMASK(31, 16),
+ .dma_maxburst = 8,
+ .ecc_strengths = sunxi_ecc_strengths_a10,
+ .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_a10),
+ .max_ecc_steps = 16,
+ .sram_size = 1024,
+};
+
+static const struct sunxi_nfc_caps sunxi_nfc_h616_caps = {
+ .has_ecc_clk = true,
+ .has_mbus_clk = true,
.reg_io_data = NFC_REG_A23_IO_DATA,
+ .reg_ecc_err_cnt = NFC_REG_H6_ECC_ERR_CNT,
+ .reg_user_data = NFC_REG_H6_USER_DATA,
+ .reg_user_data_len = NFC_REG_H6_USER_DATA_LEN,
+ .reg_spare_area = NFC_REG_H6_SPARE_AREA,
+ .reg_pat_id = NFC_REG_H6_PAT_ID,
+ .reg_pat_found = NFC_REG_H6_PAT_FOUND,
+ .random_en_mask = BIT(5),
+ .random_dir_mask = BIT(6),
+ .ecc_mode_mask = GENMASK(15, 8),
+ .ecc_err_mask = GENMASK(31, 0),
+ .pat_found_mask = GENMASK(31, 0),
.dma_maxburst = 8,
+ .ecc_strengths = sunxi_ecc_strengths_h6,
+ .nstrengths = ARRAY_SIZE(sunxi_ecc_strengths_h6),
+ .user_data_len_tab = sunxi_user_data_len_h6,
+ .nuser_data_tab = ARRAY_SIZE(sunxi_user_data_len_h6),
+ .max_ecc_steps = 32,
+ .sram_size = 8192,
};
static const struct of_device_id sunxi_nfc_ids[] = {
@@ -2188,6 +2451,10 @@ static const struct of_device_id sunxi_nfc_ids[] = {
.compatible = "allwinner,sun8i-a23-nand-controller",
.data = &sunxi_nfc_a23_caps,
},
+ {
+ .compatible = "allwinner,sun50i-h616-nand-controller",
+ .data = &sunxi_nfc_h616_caps,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index f92133b8e1a6..d207286572d8 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -1227,6 +1227,7 @@ static const struct nand_ops spinand_ops = {
static const struct spinand_manufacturer *spinand_manufacturers[] = {
&alliancememory_spinand_manufacturer,
&ato_spinand_manufacturer,
+ &esmt_8c_spinand_manufacturer,
&esmt_c8_spinand_manufacturer,
&fmsh_spinand_manufacturer,
&foresee_spinand_manufacturer,
diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c
index 9a9325c0bc49..e60e4ac1fd6f 100644
--- a/drivers/mtd/nand/spi/esmt.c
+++ b/drivers/mtd/nand/spi/esmt.c
@@ -12,6 +12,7 @@
/* ESMT uses GigaDevice 0xc8 JECDEC ID on some SPI NANDs */
#define SPINAND_MFR_ESMT_C8 0xc8
+#define SPINAND_MFR_ESMT_8C 0x8c
#define ESMT_F50L1G41LB_CFG_OTP_PROTECT BIT(7)
#define ESMT_F50L1G41LB_CFG_OTP_LOCK \
@@ -184,6 +185,21 @@ static const struct spinand_fact_otp_ops f50l1g41lb_fact_otp_ops = {
.read = spinand_fact_otp_read,
};
+
+static const struct spinand_info esmt_8c_spinand_table[] = {
+ SPINAND_INFO("F50L1G41LC",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x2C),
+ NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+ NAND_ECCREQ(1, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL),
+ SPINAND_USER_OTP_INFO(28, 2, &f50l1g41lb_user_otp_ops),
+ SPINAND_FACT_OTP_INFO(2, 0, &f50l1g41lb_fact_otp_ops)),
+};
+
static const struct spinand_info esmt_c8_spinand_table[] = {
SPINAND_INFO("F50L1G41LB",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
@@ -224,6 +240,14 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
};
+const struct spinand_manufacturer esmt_8c_spinand_manufacturer = {
+ .id = SPINAND_MFR_ESMT_8C,
+ .name = "ESMT",
+ .chips = esmt_8c_spinand_table,
+ .nchips = ARRAY_SIZE(esmt_8c_spinand_table),
+ .ops = &esmt_spinand_manuf_ops,
+};
+
const struct spinand_manufacturer esmt_c8_spinand_manufacturer = {
.id = SPINAND_MFR_ESMT_C8,
.name = "ESMT",
diff --git a/drivers/mtd/nand/spi/fmsh.c b/drivers/mtd/nand/spi/fmsh.c
index 8b2097bfc771..f417955f7d1c 100644
--- a/drivers/mtd/nand/spi/fmsh.c
+++ b/drivers/mtd/nand/spi/fmsh.c
@@ -9,6 +9,13 @@
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
+#define FM25S01BI3_STATUS_ECC_MASK (7 << 4)
+ #define FM25S01BI3_STATUS_ECC_NO_BITFLIPS (0 << 4)
+ #define FM25S01BI3_STATUS_ECC_1_3_BITFLIPS (1 << 4)
+ #define FM25S01BI3_STATUS_ECC_UNCOR_ERROR (2 << 4)
+ #define FM25S01BI3_STATUS_ECC_4_6_BITFLIPS (3 << 4)
+ #define FM25S01BI3_STATUS_ECC_7_8_BITFLIPS (5 << 4)
+
#define SPINAND_MFR_FMSH 0xA1
static SPINAND_OP_VARIANTS(read_cache_variants,
@@ -45,11 +52,66 @@ static int fm25s01a_ooblayout_free(struct mtd_info *mtd, int section,
return 0;
}
+static int fm25s01bi3_ecc_get_status(struct spinand_device *spinand,
+ u8 status)
+{
+ switch (status & FM25S01BI3_STATUS_ECC_MASK) {
+ case FM25S01BI3_STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case FM25S01BI3_STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+
+ case FM25S01BI3_STATUS_ECC_1_3_BITFLIPS:
+ return 3;
+
+ case FM25S01BI3_STATUS_ECC_4_6_BITFLIPS:
+ return 6;
+
+ case FM25S01BI3_STATUS_ECC_7_8_BITFLIPS:
+ return 8;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int fm25s01bi3_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section)
+ return -ERANGE;
+
+ region->offset = 64;
+ region->length = 64;
+
+ return 0;
+}
+
+static int fm25s01bi3_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 3)
+ return -ERANGE;
+
+ region->offset = (16 * section) + 4;
+ region->length = 12;
+
+ return 0;
+}
+
static const struct mtd_ooblayout_ops fm25s01a_ooblayout = {
.ecc = fm25s01a_ooblayout_ecc,
.free = fm25s01a_ooblayout_free,
};
+static const struct mtd_ooblayout_ops fm25s01bi3_ooblayout = {
+ .ecc = fm25s01bi3_ooblayout_ecc,
+ .free = fm25s01bi3_ooblayout_free,
+};
+
static const struct spinand_info fmsh_spinand_table[] = {
SPINAND_INFO("FM25S01A",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
@@ -58,8 +120,18 @@ static const struct spinand_info fmsh_spinand_table[] = {
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
- SPINAND_HAS_QE_BIT,
+ 0,
SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
+ SPINAND_INFO("FM25S01BI3",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xd4),
+ NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&fm25s01bi3_ooblayout,
+ fm25s01bi3_ecc_get_status)),
};
static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
index abc7b186353f..5988cba30eb3 100644
--- a/drivers/mtd/sm_ftl.c
+++ b/drivers/mtd/sm_ftl.c
@@ -44,8 +44,7 @@ static ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr,
struct sm_sysfs_attribute *sm_attr =
container_of(attr, struct sm_sysfs_attribute, dev_attr);
- strncpy(buf, sm_attr->data, sm_attr->len);
- return sm_attr->len;
+ return sysfs_emit(buf, "%.*s", sm_attr->len, sm_attr->data);
}
@@ -157,7 +156,7 @@ static int sm_read_lba(struct sm_oob *oob)
if (!memcmp(oob, erased_pattern, SM_OOB_SIZE))
return -1;
- /* Now check is both copies of the LBA differ too much */
+ /* Now check if both copies of the LBA differ too much */
lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2;
if (lba_test && !is_power_of_2(lba_test))
return -2;
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 20ea80450f22..d3f8a78efd3b 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2459,6 +2459,16 @@ spi_nor_spimem_adjust_hwcaps(struct spi_nor *nor, u32 *hwcaps)
&params->page_programs[ppidx]))
*hwcaps &= ~BIT(cap);
}
+
+ /* Some SPI controllers might not support CR read opcode. */
+ if (!(nor->flags & SNOR_F_NO_READ_CR)) {
+ struct spi_mem_op op = SPI_NOR_RDCR_OP(nor->bouncebuf);
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ if (spi_nor_spimem_check_op(nor, &op))
+ nor->flags |= SNOR_F_NO_READ_CR;
+ }
}
/**
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index ceff412f7d65..16b382d4f04f 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -409,6 +409,10 @@ struct spi_nor_flash_parameter {
* flash parameters when information provided by the flash_info
* table is incomplete or wrong.
* @post_bfpt: called after the BFPT table has been parsed
+ * @smpt_read_dummy: called during SMPT table is being parsed. Used to fix the
+ * number of dummy cycles in read register ops.
+ * @smpt_map_id: called after map ID in SMPT table has been determined for the
+ * case the map ID is wrong and needs to be fixed.
* @post_sfdp: called after SFDP has been parsed (is also called for SPI NORs
* that do not support RDSFDP). Typically used to tweak various
* parameters that could not be extracted by other means (i.e.
@@ -426,6 +430,8 @@ struct spi_nor_fixups {
int (*post_bfpt)(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt);
+ void (*smpt_read_dummy)(const struct spi_nor *nor, u8 *read_dummy);
+ void (*smpt_map_id)(const struct spi_nor *nor, u8 *map_id);
int (*post_sfdp)(struct spi_nor *nor);
int (*late_init)(struct spi_nor *nor);
};
diff --git a/drivers/mtd/spi-nor/micron-st.c b/drivers/mtd/spi-nor/micron-st.c
index 187239ccd549..88033384a71e 100644
--- a/drivers/mtd/spi-nor/micron-st.c
+++ b/drivers/mtd/spi-nor/micron-st.c
@@ -127,9 +127,36 @@ static int micron_st_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
micron_st_nor_octal_dtr_dis(nor);
}
-static void mt35xu512aba_default_init(struct spi_nor *nor)
+static int micron_st_nor_four_die_late_init(struct spi_nor *nor)
{
- nor->params->set_octal_dtr = micron_st_nor_set_octal_dtr;
+ struct spi_nor_flash_parameter *params = nor->params;
+
+ params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
+ params->n_dice = 4;
+
+ /*
+ * Unfortunately the die erase opcode does not have a 4-byte opcode
+ * correspondent for these flashes. The SFDP 4BAIT table fails to
+ * consider the die erase too. We're forced to enter in the 4 byte
+ * address mode in order to benefit of the die erase.
+ */
+ return spi_nor_set_4byte_addr_mode(nor, true);
+}
+
+static int micron_st_nor_two_die_late_init(struct spi_nor *nor)
+{
+ struct spi_nor_flash_parameter *params = nor->params;
+
+ params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
+ params->n_dice = 2;
+
+ /*
+ * Unfortunately the die erase opcode does not have a 4-byte opcode
+ * correspondent for these flashes. The SFDP 4BAIT table fails to
+ * consider the die erase too. We're forced to enter in the 4 byte
+ * address mode in order to benefit of the die erase.
+ */
+ return spi_nor_set_4byte_addr_mode(nor, true);
}
static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
@@ -155,22 +182,38 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
}
static const struct spi_nor_fixups mt35xu512aba_fixups = {
- .default_init = mt35xu512aba_default_init,
.post_sfdp = mt35xu512aba_post_sfdp_fixup,
};
+static const struct spi_nor_fixups mt35xu01gbba_fixups = {
+ .post_sfdp = mt35xu512aba_post_sfdp_fixup,
+ .late_init = micron_st_nor_two_die_late_init,
+};
+
static const struct flash_info micron_nor_parts[] = {
{
+ /* MT35XU512ABA */
.id = SNOR_ID(0x2c, 0x5b, 0x1a),
- .name = "mt35xu512aba",
- .sector_size = SZ_128K,
- .size = SZ_64M,
- .no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ |
- SPI_NOR_OCTAL_DTR_READ | SPI_NOR_OCTAL_DTR_PP,
.mfr_flags = USE_FSR,
- .fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
+ .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE,
.fixups = &mt35xu512aba_fixups,
}, {
+ /* MT35XU01GBBA */
+ .id = SNOR_ID(0x2c, 0x5b, 0x1b),
+ .mfr_flags = USE_FSR,
+ .fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE,
+ .fixups = &mt35xu01gbba_fixups,
+ }, {
+ /*
+ * The MT35XU02GCBA flash device does not support chip erase,
+ * according to its datasheet. It supports die erase, which
+ * means the current driver implementation will likely need to
+ * be converted to use die erase. Furthermore, similar to the
+ * MT35XU01GBBA, the SPI_NOR_IO_MODE_EN_VOLATILE flag probably
+ * needs to be enabled.
+ *
+ * TODO: Fix these and test on real hardware.
+ */
.id = SNOR_ID(0x2c, 0x5b, 0x1c),
.name = "mt35xu02g",
.sector_size = SZ_128K,
@@ -193,48 +236,16 @@ static const struct spi_nor_fixups mt25qu512a_fixups = {
.post_bfpt = mt25qu512a_post_bfpt_fixup,
};
-static int st_nor_four_die_late_init(struct spi_nor *nor)
-{
- struct spi_nor_flash_parameter *params = nor->params;
-
- params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
- params->n_dice = 4;
-
- /*
- * Unfortunately the die erase opcode does not have a 4-byte opcode
- * correspondent for these flashes. The SFDP 4BAIT table fails to
- * consider the die erase too. We're forced to enter in the 4 byte
- * address mode in order to benefit of the die erase.
- */
- return spi_nor_set_4byte_addr_mode(nor, true);
-}
-
-static int st_nor_two_die_late_init(struct spi_nor *nor)
-{
- struct spi_nor_flash_parameter *params = nor->params;
-
- params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
- params->n_dice = 2;
-
- /*
- * Unfortunately the die erase opcode does not have a 4-byte opcode
- * correspondent for these flashes. The SFDP 4BAIT table fails to
- * consider the die erase too. We're forced to enter in the 4 byte
- * address mode in order to benefit of the die erase.
- */
- return spi_nor_set_4byte_addr_mode(nor, true);
-}
-
static const struct spi_nor_fixups n25q00_fixups = {
- .late_init = st_nor_four_die_late_init,
+ .late_init = micron_st_nor_four_die_late_init,
};
static const struct spi_nor_fixups mt25q01_fixups = {
- .late_init = st_nor_two_die_late_init,
+ .late_init = micron_st_nor_two_die_late_init,
};
static const struct spi_nor_fixups mt25q02_fixups = {
- .late_init = st_nor_four_die_late_init,
+ .late_init = micron_st_nor_four_die_late_init,
};
static const struct flash_info st_nor_parts[] = {
@@ -635,6 +646,8 @@ static int micron_st_nor_late_init(struct spi_nor *nor)
if (!params->set_4byte_addr_mode)
params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_wren_en4b_ex4b;
+ params->set_octal_dtr = micron_st_nor_set_octal_dtr;
+
return 0;
}
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 21727f9a4ac6..a8324c2da0ac 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -699,6 +699,17 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings
}
}
+static void spi_nor_smpt_read_dummy_fixups(const struct spi_nor *nor,
+ u8 *read_dummy)
+{
+ if (nor->manufacturer && nor->manufacturer->fixups &&
+ nor->manufacturer->fixups->smpt_read_dummy)
+ nor->manufacturer->fixups->smpt_read_dummy(nor, read_dummy);
+
+ if (nor->info->fixups && nor->info->fixups->smpt_read_dummy)
+ nor->info->fixups->smpt_read_dummy(nor, read_dummy);
+}
+
/**
* spi_nor_smpt_read_dummy() - return the configuration detection command read
* latency, in clock cycles.
@@ -711,11 +722,24 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
{
u8 read_dummy = SMPT_CMD_READ_DUMMY(settings);
- if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE)
- return nor->read_dummy;
+ if (read_dummy == SMPT_CMD_READ_DUMMY_IS_VARIABLE) {
+ read_dummy = nor->read_dummy;
+ spi_nor_smpt_read_dummy_fixups(nor, &read_dummy);
+ }
+
return read_dummy;
}
+static void spi_nor_smpt_map_id_fixups(const struct spi_nor *nor, u8 *map_id)
+{
+ if (nor->manufacturer && nor->manufacturer->fixups &&
+ nor->manufacturer->fixups->smpt_map_id)
+ nor->manufacturer->fixups->smpt_map_id(nor, map_id);
+
+ if (nor->info->fixups && nor->info->fixups->smpt_map_id)
+ nor->info->fixups->smpt_map_id(nor, map_id);
+}
+
/**
* spi_nor_get_map_in_use() - get the configuration map in use
* @nor: pointer to a 'struct spi_nor'
@@ -769,6 +793,8 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
map_id = map_id << 1 | !!(*buf & read_data_mask);
}
+ spi_nor_smpt_map_id_fixups(nor, &map_id);
+
/*
* If command descriptors are provided, they always precede map
* descriptors in the table. There is no need to start the iteration
diff --git a/drivers/mtd/spi-nor/spansion.c b/drivers/mtd/spi-nor/spansion.c
index a0296c871634..8498c7003d88 100644
--- a/drivers/mtd/spi-nor/spansion.c
+++ b/drivers/mtd/spi-nor/spansion.c
@@ -785,8 +785,46 @@ s25fs_s_nor_post_bfpt_fixups(struct spi_nor *nor,
return 0;
}
+static void s25fs_s_nor_smpt_read_dummy(const struct spi_nor *nor,
+ u8 *read_dummy)
+{
+ /*
+ * The configuration detection dwords in S25FS-S SMPT has 65h as
+ * command instruction and 'variable' as configuration detection command
+ * latency. Set 8 dummy cycles as it is factory default for 65h (read
+ * any register) op.
+ */
+ *read_dummy = 8;
+}
+
+static void s25fs_s_nor_smpt_map_id_dummy(const struct spi_nor *nor, u8 *map_id)
+{
+ /*
+ * The S25FS512S chip supports:
+ * - Hybrid sector option which has physical set of eight 4-KB sectors
+ * and one 224-KB sector at the top or bottom of address space with
+ * all remaining sectors of 256-KB
+ * - Uniform sector option which has uniform 256-KB sectors
+ *
+ * On the other hand, the datasheet rev.O Table 71 on page 153 JEDEC
+ * Sector Map Parameter Dword-6 Config. Detect-3 does use CR3NV[1] to
+ * discern 64-KB(CR3NV[1]=0) and 256-KB(CR3NV[1]=1) uniform sectors
+ * device configuration. And in section 7.5.5.1 Configuration Register 3
+ * Non-volatile (CR3NV) page 61, the CR3NV[1] is RFU Reserved for Future
+ * Use and set to 0, which means 64-KB uniform. Since the device does
+ * not support 64-KB uniform sectors in any configuration, parsing SMPT
+ * table cannot find a valid sector map entry and fails. Fix this up by
+ * setting SMPT by overwriting the CR3NV[1] value to 1, as the table
+ * expects.
+ */
+ if (nor->params->size == SZ_64M)
+ *map_id |= BIT(0);
+}
+
static const struct spi_nor_fixups s25fs_s_nor_fixups = {
.post_bfpt = s25fs_s_nor_post_bfpt_fixups,
+ .smpt_read_dummy = s25fs_s_nor_smpt_read_dummy,
+ .smpt_map_id = s25fs_s_nor_smpt_map_id_dummy,
};
static const struct flash_info spansion_nor_parts[] = {
diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c
index 63a93c9eb917..fb855fe44733 100644
--- a/drivers/mtd/spi-nor/winbond.c
+++ b/drivers/mtd/spi-nor/winbond.c
@@ -343,6 +343,30 @@ static const struct flash_info winbond_nor_parts[] = {
.id = SNOR_ID(0xef, 0x80, 0x20),
.name = "w25q512nwm",
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
+ }, {
+ /* W25Q01NWxxIQ */
+ .id = SNOR_ID(0xef, 0x60, 0x21),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25Q01NWxxIM */
+ .id = SNOR_ID(0xef, 0x80, 0x21),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25Q02NWxxIM */
+ .id = SNOR_ID(0xef, 0x80, 0x22),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25H512NWxxAM */
+ .id = SNOR_ID(0xef, 0xa0, 0x20),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25H01NWxxAM */
+ .id = SNOR_ID(0xef, 0xa0, 0x21),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
+ }, {
+ /* W25H02NWxxAM */
+ .id = SNOR_ID(0xef, 0xa0, 0x22),
+ .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
},
};
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 49717b7b82a2..1a8de2bf8655 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -76,6 +76,7 @@ enum ad_link_speed_type {
AD_LINK_SPEED_200000MBPS,
AD_LINK_SPEED_400000MBPS,
AD_LINK_SPEED_800000MBPS,
+ AD_LINK_SPEED_1600000MBPS,
};
/* compare MAC addresses */
@@ -300,6 +301,7 @@ static inline int __check_agg_selection_timer(struct port *port)
* %AD_LINK_SPEED_200000MBPS
* %AD_LINK_SPEED_400000MBPS
* %AD_LINK_SPEED_800000MBPS
+ * %AD_LINK_SPEED_1600000MBPS
*/
static u16 __get_link_speed(struct port *port)
{
@@ -379,6 +381,10 @@ static u16 __get_link_speed(struct port *port)
speed = AD_LINK_SPEED_800000MBPS;
break;
+ case SPEED_1600000:
+ speed = AD_LINK_SPEED_1600000MBPS;
+ break;
+
default:
/* unknown speed value from ethtool. shouldn't happen */
if (slave->speed != SPEED_UNKNOWN)
@@ -822,6 +828,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator)
case AD_LINK_SPEED_800000MBPS:
bandwidth = nports * 800000;
break;
+ case AD_LINK_SPEED_1600000MBPS:
+ bandwidth = nports * 1600000;
+ break;
default:
bandwidth = 0; /* to silence the compiler */
}
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 4da619210c1f..3d56339a8a10 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1468,97 +1468,6 @@ static netdev_features_t bond_fix_features(struct net_device *dev,
return features;
}
-#define BOND_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
- NETIF_F_GSO_ENCAP_ALL | \
- NETIF_F_HIGHDMA | NETIF_F_LRO)
-
-#define BOND_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE | \
- NETIF_F_GSO_PARTIAL)
-
-#define BOND_MPLS_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_GSO_SOFTWARE)
-
-#define BOND_GSO_PARTIAL_FEATURES (NETIF_F_GSO_ESP)
-
-
-static void bond_compute_features(struct bonding *bond)
-{
- netdev_features_t gso_partial_features = BOND_GSO_PARTIAL_FEATURES;
- unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
- IFF_XMIT_DST_RELEASE_PERM;
- netdev_features_t vlan_features = BOND_VLAN_FEATURES;
- netdev_features_t enc_features = BOND_ENC_FEATURES;
-#ifdef CONFIG_XFRM_OFFLOAD
- netdev_features_t xfrm_features = BOND_XFRM_FEATURES;
-#endif /* CONFIG_XFRM_OFFLOAD */
- netdev_features_t mpls_features = BOND_MPLS_FEATURES;
- struct net_device *bond_dev = bond->dev;
- struct list_head *iter;
- struct slave *slave;
- unsigned short max_hard_header_len = ETH_HLEN;
- unsigned int tso_max_size = TSO_MAX_SIZE;
- u16 tso_max_segs = TSO_MAX_SEGS;
-
- if (!bond_has_slaves(bond))
- goto done;
-
- vlan_features = netdev_base_features(vlan_features);
- mpls_features = netdev_base_features(mpls_features);
-
- bond_for_each_slave(bond, slave, iter) {
- vlan_features = netdev_increment_features(vlan_features,
- slave->dev->vlan_features, BOND_VLAN_FEATURES);
-
- enc_features = netdev_increment_features(enc_features,
- slave->dev->hw_enc_features,
- BOND_ENC_FEATURES);
-
-#ifdef CONFIG_XFRM_OFFLOAD
- xfrm_features = netdev_increment_features(xfrm_features,
- slave->dev->hw_enc_features,
- BOND_XFRM_FEATURES);
-#endif /* CONFIG_XFRM_OFFLOAD */
-
- gso_partial_features = netdev_increment_features(gso_partial_features,
- slave->dev->gso_partial_features,
- BOND_GSO_PARTIAL_FEATURES);
-
- mpls_features = netdev_increment_features(mpls_features,
- slave->dev->mpls_features,
- BOND_MPLS_FEATURES);
-
- dst_release_flag &= slave->dev->priv_flags;
- if (slave->dev->hard_header_len > max_hard_header_len)
- max_hard_header_len = slave->dev->hard_header_len;
-
- tso_max_size = min(tso_max_size, slave->dev->tso_max_size);
- tso_max_segs = min(tso_max_segs, slave->dev->tso_max_segs);
- }
- bond_dev->hard_header_len = max_hard_header_len;
-
-done:
- bond_dev->gso_partial_features = gso_partial_features;
- bond_dev->vlan_features = vlan_features;
- bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
- NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_STAG_TX;
-#ifdef CONFIG_XFRM_OFFLOAD
- bond_dev->hw_enc_features |= xfrm_features;
-#endif /* CONFIG_XFRM_OFFLOAD */
- bond_dev->mpls_features = mpls_features;
- netif_set_tso_max_segs(bond_dev, tso_max_segs);
- netif_set_tso_max_size(bond_dev, tso_max_size);
-
- bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
- if ((bond_dev->priv_flags & IFF_XMIT_DST_RELEASE_PERM) &&
- dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
- bond_dev->priv_flags |= IFF_XMIT_DST_RELEASE;
-
- netdev_change_features(bond_dev);
-}
-
static void bond_setup_by_slave(struct net_device *bond_dev,
struct net_device *slave_dev)
{
@@ -2120,7 +2029,7 @@ skip_mac_set:
/* check for initial state */
new_slave->link = BOND_LINK_NOCHANGE;
if (bond->params.miimon) {
- if (netif_carrier_ok(slave_dev)) {
+ if (netif_running(slave_dev) && netif_carrier_ok(slave_dev)) {
if (bond->params.updelay) {
bond_set_slave_link_state(new_slave,
BOND_LINK_BACK,
@@ -2273,7 +2182,7 @@ skip_mac_set:
}
bond->slave_cnt++;
- bond_compute_features(bond);
+ netdev_compute_master_upper_features(bond->dev, true);
bond_set_carrier(bond);
/* Needs to be called before bond_select_active_slave(), which will
@@ -2287,7 +2196,9 @@ skip_mac_set:
unblock_netpoll_tx();
}
- if (bond_mode_can_use_xmit_hash(bond))
+ /* broadcast mode uses the all_slaves to loop through slaves. */
+ if (bond_mode_can_use_xmit_hash(bond) ||
+ BOND_MODE(bond) == BOND_MODE_BROADCAST)
bond_update_slave_arr(bond, NULL);
if (!slave_dev->netdev_ops->ndo_bpf ||
@@ -2463,7 +2374,8 @@ static int __bond_release_one(struct net_device *bond_dev,
bond_upper_dev_unlink(bond, slave);
- if (bond_mode_can_use_xmit_hash(bond))
+ if (bond_mode_can_use_xmit_hash(bond) ||
+ BOND_MODE(bond) == BOND_MODE_BROADCAST)
bond_update_slave_arr(bond, slave);
slave_info(bond_dev, slave_dev, "Releasing %s interface\n",
@@ -2525,7 +2437,7 @@ static int __bond_release_one(struct net_device *bond_dev,
call_netdevice_notifiers(NETDEV_RELEASE, bond->dev);
}
- bond_compute_features(bond);
+ netdev_compute_master_upper_features(bond->dev, true);
if (!(bond_dev->features & NETIF_F_VLAN_CHALLENGED) &&
(old_features & NETIF_F_VLAN_CHALLENGED))
slave_info(bond_dev, slave_dev, "last VLAN challenged slave left bond - VLAN blocking is removed\n");
@@ -2662,7 +2574,8 @@ static int bond_miimon_inspect(struct bonding *bond)
bond_for_each_slave_rcu(bond, slave, iter) {
bond_propose_link_state(slave, BOND_LINK_NOCHANGE);
- link_state = netif_carrier_ok(slave->dev);
+ link_state = netif_running(slave->dev) &&
+ netif_carrier_ok(slave->dev);
switch (slave->link) {
case BOND_LINK_UP:
@@ -2871,7 +2784,7 @@ static void bond_mii_monitor(struct work_struct *work)
{
struct bonding *bond = container_of(work, struct bonding,
mii_work.work);
- bool should_notify_peers = false;
+ bool should_notify_peers;
bool commit;
unsigned long delay;
struct slave *slave;
@@ -2883,30 +2796,33 @@ static void bond_mii_monitor(struct work_struct *work)
goto re_arm;
rcu_read_lock();
+
should_notify_peers = bond_should_notify_peers(bond);
commit = !!bond_miimon_inspect(bond);
- if (bond->send_peer_notif) {
- rcu_read_unlock();
- if (rtnl_trylock()) {
- bond->send_peer_notif--;
- rtnl_unlock();
- }
- } else {
- rcu_read_unlock();
- }
- if (commit) {
+ rcu_read_unlock();
+
+ if (commit || bond->send_peer_notif) {
/* Race avoidance with bond_close cancel of workqueue */
if (!rtnl_trylock()) {
delay = 1;
- should_notify_peers = false;
goto re_arm;
}
- bond_for_each_slave(bond, slave, iter) {
- bond_commit_link_state(slave, BOND_SLAVE_NOTIFY_LATER);
+ if (commit) {
+ bond_for_each_slave(bond, slave, iter) {
+ bond_commit_link_state(slave,
+ BOND_SLAVE_NOTIFY_LATER);
+ }
+ bond_miimon_commit(bond);
+ }
+
+ if (bond->send_peer_notif) {
+ bond->send_peer_notif--;
+ if (should_notify_peers)
+ call_netdevice_notifiers(NETDEV_NOTIFY_PEERS,
+ bond->dev);
}
- bond_miimon_commit(bond);
rtnl_unlock(); /* might sleep, hold no other locks */
}
@@ -2914,13 +2830,6 @@ static void bond_mii_monitor(struct work_struct *work)
re_arm:
if (bond->params.miimon)
queue_delayed_work(bond->wq, &bond->mii_work, delay);
-
- if (should_notify_peers) {
- if (!rtnl_trylock())
- return;
- call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, bond->dev);
- rtnl_unlock();
- }
}
static int bond_upper_dev_walk(struct net_device *upper,
@@ -4028,7 +3937,7 @@ static int bond_slave_netdev_event(unsigned long event,
case NETDEV_FEAT_CHANGE:
if (!bond->notifier_ctx) {
bond->notifier_ctx = true;
- bond_compute_features(bond);
+ netdev_compute_master_upper_features(bond->dev, true);
bond->notifier_ctx = false;
}
break;
@@ -6011,7 +5920,7 @@ void bond_setup(struct net_device *bond_dev)
* capable
*/
- bond_dev->hw_features = BOND_VLAN_FEATURES |
+ bond_dev->hw_features = MASTER_UPPER_DEV_VLAN_FEATURES |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_HW_VLAN_STAG_RX |
diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c
index 495a87f2ea7c..384499c869b8 100644
--- a/drivers/net/bonding/bond_options.c
+++ b/drivers/net/bonding/bond_options.c
@@ -225,13 +225,6 @@ static const struct bond_opt_value bond_ad_actor_sys_prio_tbl[] = {
{ NULL, -1, 0},
};
-static const struct bond_opt_value bond_actor_port_prio_tbl[] = {
- { "minval", 0, BOND_VALFLAG_MIN},
- { "maxval", 65535, BOND_VALFLAG_MAX},
- { "default", 255, BOND_VALFLAG_DEFAULT},
- { NULL, -1, 0},
-};
-
static const struct bond_opt_value bond_ad_user_port_key_tbl[] = {
{ "minval", 0, BOND_VALFLAG_MIN | BOND_VALFLAG_DEFAULT},
{ "maxval", 1023, BOND_VALFLAG_MAX},
@@ -497,7 +490,7 @@ static const struct bond_option bond_opts[BOND_OPT_LAST] = {
.id = BOND_OPT_ACTOR_PORT_PRIO,
.name = "actor_port_prio",
.unsuppmodes = BOND_MODE_ALL_EX(BIT(BOND_MODE_8023AD)),
- .values = bond_actor_port_prio_tbl,
+ .flags = BOND_OPTFLAG_RAWVAL,
.set = bond_option_actor_port_prio_set,
},
[BOND_OPT_AD_ACTOR_SYSTEM] = {
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index d43d56694667..e15e320db476 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -124,6 +124,23 @@ config CAN_CAN327
If this driver is built as a module, it will be called can327.
+config CAN_DUMMY
+ tristate "Dummy CAN"
+ help
+ A dummy CAN module supporting Classical CAN, CAN FD and CAN XL. It
+ exposes bittiming values which can be configured through the netlink
+ interface.
+
+ The module will simply echo any frame sent to it. If debug messages
+ are activated, it prints all the CAN bittiming information in the
+ kernel log. Aside from that it does nothing.
+
+ This is convenient for testing the CAN netlink interface. Most of the
+ users will never need this. If unsure, say NO.
+
+ To compile this driver as a module, choose M here: the module will be
+ called dummy-can.
+
config CAN_FLEXCAN
tristate "Support for Freescale FLEXCAN based chips"
depends on OF || COLDFIRE || COMPILE_TEST
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 56138d8ddfd2..d7bc10a6b8ea 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_CAN_CAN327) += can327.o
obj-$(CONFIG_CAN_CC770) += cc770/
obj-$(CONFIG_CAN_C_CAN) += c_can/
obj-$(CONFIG_CAN_CTUCANFD) += ctucanfd/
+obj-$(CONFIG_CAN_DUMMY) += dummy_can.o
obj-$(CONFIG_CAN_FLEXCAN) += flexcan/
obj-$(CONFIG_CAN_GRCAN) += grcan.o
obj-$(CONFIG_CAN_IFI_CANFD) += ifi_canfd/
diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c
index 191707d7e3da..c2a3a4eef5b2 100644
--- a/drivers/net/can/at91_can.c
+++ b/drivers/net/can/at91_can.c
@@ -948,7 +948,6 @@ static const struct net_device_ops at91_netdev_ops = {
.ndo_open = at91_open,
.ndo_stop = at91_close,
.ndo_start_xmit = at91_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops at91_ethtool_ops = {
diff --git a/drivers/net/can/bxcan.c b/drivers/net/can/bxcan.c
index bfc60eb33dc3..baf494d20bef 100644
--- a/drivers/net/can/bxcan.c
+++ b/drivers/net/can/bxcan.c
@@ -227,7 +227,7 @@ static void bxcan_enable_filters(struct bxcan_priv *priv, enum bxcan_cfg cfg)
* mask mode with 32 bits width.
*/
- /* Enter filter initialization mode and assing filters to CAN
+ /* Enter filter initialization mode and assign filters to CAN
* controllers.
*/
regmap_update_bits(priv->gcan, BXCAN_FMR_REG,
@@ -842,7 +842,7 @@ static netdev_tx_t bxcan_start_xmit(struct sk_buff *skb,
u32 id;
int i, j;
- if (can_dropped_invalid_skb(ndev, skb))
+ if (can_dev_dropped_skb(ndev, skb))
return NETDEV_TX_OK;
if (bxcan_tx_busy(priv))
@@ -881,7 +881,6 @@ static const struct net_device_ops bxcan_netdev_ops = {
.ndo_open = bxcan_open,
.ndo_stop = bxcan_stop,
.ndo_start_xmit = bxcan_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops bxcan_ethtool_ops = {
diff --git a/drivers/net/can/c_can/c_can_main.c b/drivers/net/can/c_can/c_can_main.c
index cc371d0c9f3c..3702cac7fbf0 100644
--- a/drivers/net/can/c_can/c_can_main.c
+++ b/drivers/net/can/c_can/c_can_main.c
@@ -1362,7 +1362,6 @@ static const struct net_device_ops c_can_netdev_ops = {
.ndo_open = c_can_open,
.ndo_stop = c_can_close,
.ndo_start_xmit = c_can_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
int register_c_can_dev(struct net_device *dev)
diff --git a/drivers/net/can/can327.c b/drivers/net/can/can327.c
index 24af63961030..b66fc16aedd2 100644
--- a/drivers/net/can/can327.c
+++ b/drivers/net/can/can327.c
@@ -849,7 +849,6 @@ static const struct net_device_ops can327_netdev_ops = {
.ndo_open = can327_netdev_open,
.ndo_stop = can327_netdev_close,
.ndo_start_xmit = can327_netdev_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops can327_ethtool_ops = {
diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c
index 30909f3aab57..8d5abd643c06 100644
--- a/drivers/net/can/cc770/cc770.c
+++ b/drivers/net/can/cc770/cc770.c
@@ -834,7 +834,6 @@ static const struct net_device_ops cc770_netdev_ops = {
.ndo_open = cc770_open,
.ndo_stop = cc770_close,
.ndo_start_xmit = cc770_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops cc770_ethtool_ops = {
diff --git a/drivers/net/can/ctucanfd/ctucanfd_base.c b/drivers/net/can/ctucanfd/ctucanfd_base.c
index 8bd3f0fc385c..1e6b9e3dc2fe 100644
--- a/drivers/net/can/ctucanfd/ctucanfd_base.c
+++ b/drivers/net/can/ctucanfd/ctucanfd_base.c
@@ -1301,7 +1301,6 @@ static const struct net_device_ops ctucan_netdev_ops = {
.ndo_open = ctucan_open,
.ndo_stop = ctucan_close,
.ndo_start_xmit = ctucan_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops ctucan_ethtool_ops = {
diff --git a/drivers/net/can/dev/bittiming.c b/drivers/net/can/dev/bittiming.c
index 0b93900b1dfa..8f82418230ce 100644
--- a/drivers/net/can/dev/bittiming.c
+++ b/drivers/net/can/dev/bittiming.c
@@ -2,6 +2,7 @@
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (c) 2025 Vincent Mailhol <mailhol@kernel.org>
*/
#include <linux/can/dev.h>
@@ -151,3 +152,65 @@ int can_get_bittiming(const struct net_device *dev, struct can_bittiming *bt,
return -EINVAL;
}
+
+int can_validate_pwm_bittiming(const struct net_device *dev,
+ const struct can_pwm *pwm,
+ struct netlink_ext_ack *extack)
+{
+ const struct can_priv *priv = netdev_priv(dev);
+ u32 xl_bit_time_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
+ u32 nom_bit_time_tqmin = can_bit_time_tqmin(&priv->bittiming);
+ u32 pwms_ns = can_tqmin_to_ns(pwm->pwms, priv->clock.freq);
+ u32 pwml_ns = can_tqmin_to_ns(pwm->pwml, priv->clock.freq);
+
+ if (pwms_ns + pwml_ns > CAN_PWM_NS_MAX) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "The PWM symbol duration: %u ns may not exceed %u ns",
+ pwms_ns + pwml_ns, CAN_PWM_NS_MAX);
+ return -EINVAL;
+ }
+
+ if (pwms_ns < CAN_PWM_DECODE_NS) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "PWMS: %u ns shall be at least %u ns",
+ pwms_ns, CAN_PWM_DECODE_NS);
+ return -EINVAL;
+ }
+
+ if (pwm->pwms >= pwm->pwml) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "PWMS: %u tqmin shall be smaller than PWML: %u tqmin",
+ pwm->pwms, pwm->pwml);
+ return -EINVAL;
+ }
+
+ if (pwml_ns - pwms_ns < 2 * CAN_PWM_DECODE_NS) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "At least %u ns shall separate PWMS: %u ns from PMWL: %u ns",
+ 2 * CAN_PWM_DECODE_NS, pwms_ns, pwml_ns);
+ return -EINVAL;
+ }
+
+ if (xl_bit_time_tqmin % (pwm->pwms + pwm->pwml) != 0) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "PWM duration: %u tqmin does not divide XL's bit time: %u tqmin",
+ pwm->pwms + pwm->pwml, xl_bit_time_tqmin);
+ return -EINVAL;
+ }
+
+ if (pwm->pwmo >= pwm->pwms + pwm->pwml) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "PWMO: %u tqmin can not be greater than PWMS + PWML: %u tqmin",
+ pwm->pwmo, pwm->pwms + pwm->pwml);
+ return -EINVAL;
+ }
+
+ if (nom_bit_time_tqmin % (pwm->pwms + pwm->pwml) != pwm->pwmo) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "Can not assemble nominal bit time: %u tqmin out of PWMS + PMWL and PWMO",
+ nom_bit_time_tqmin);
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/can/dev/calc_bittiming.c b/drivers/net/can/dev/calc_bittiming.c
index 394d6974f481..cc4022241553 100644
--- a/drivers/net/can/dev/calc_bittiming.c
+++ b/drivers/net/can/dev/calc_bittiming.c
@@ -2,6 +2,7 @@
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
+ * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
*/
#include <linux/units.h>
@@ -9,6 +10,33 @@
#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */
+/* CiA recommended sample points for Non Return to Zero encoding. */
+static int can_calc_sample_point_nrz(const struct can_bittiming *bt)
+{
+ if (bt->bitrate > 800 * KILO /* BPS */)
+ return 750;
+
+ if (bt->bitrate > 500 * KILO /* BPS */)
+ return 800;
+
+ return 875;
+}
+
+/* Sample points for Pulse-Width Modulation encoding. */
+static int can_calc_sample_point_pwm(const struct can_bittiming *bt)
+{
+ if (bt->bitrate > 15 * MEGA /* BPS */)
+ return 625;
+
+ if (bt->bitrate > 9 * MEGA /* BPS */)
+ return 600;
+
+ if (bt->bitrate > 4 * MEGA /* BPS */)
+ return 560;
+
+ return 520;
+}
+
/* Bit-timing calculation derived from:
*
* Code based on LinCAN sources and H8S2638 project
@@ -23,7 +51,7 @@
*/
static int
can_update_sample_point(const struct can_bittiming_const *btc,
- const unsigned int sample_point_nominal, const unsigned int tseg,
+ const unsigned int sample_point_reference, const unsigned int tseg,
unsigned int *tseg1_ptr, unsigned int *tseg2_ptr,
unsigned int *sample_point_error_ptr)
{
@@ -34,7 +62,7 @@ can_update_sample_point(const struct can_bittiming_const *btc,
for (i = 0; i <= 1; i++) {
tseg2 = tseg + CAN_SYNC_SEG -
- (sample_point_nominal * (tseg + CAN_SYNC_SEG)) /
+ (sample_point_reference * (tseg + CAN_SYNC_SEG)) /
1000 - i;
tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max);
tseg1 = tseg - tseg2;
@@ -45,9 +73,9 @@ can_update_sample_point(const struct can_bittiming_const *btc,
sample_point = 1000 * (tseg + CAN_SYNC_SEG - tseg2) /
(tseg + CAN_SYNC_SEG);
- sample_point_error = abs(sample_point_nominal - sample_point);
+ sample_point_error = abs(sample_point_reference - sample_point);
- if (sample_point <= sample_point_nominal &&
+ if (sample_point <= sample_point_reference &&
sample_point_error < best_sample_point_error) {
best_sample_point = sample_point;
best_sample_point_error = sample_point_error;
@@ -67,28 +95,24 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
{
struct can_priv *priv = netdev_priv(dev);
unsigned int bitrate; /* current bitrate */
- unsigned int bitrate_error; /* difference between current and nominal value */
+ unsigned int bitrate_error; /* diff between calculated and reference value */
unsigned int best_bitrate_error = UINT_MAX;
- unsigned int sample_point_error; /* difference between current and nominal value */
+ unsigned int sample_point_error; /* diff between calculated and reference value */
unsigned int best_sample_point_error = UINT_MAX;
- unsigned int sample_point_nominal; /* nominal sample point */
+ unsigned int sample_point_reference; /* reference sample point */
unsigned int best_tseg = 0; /* current best value for tseg */
unsigned int best_brp = 0; /* current best value for brp */
unsigned int brp, tsegall, tseg, tseg1 = 0, tseg2 = 0;
u64 v64;
int err;
- /* Use CiA recommended sample points */
- if (bt->sample_point) {
- sample_point_nominal = bt->sample_point;
- } else {
- if (bt->bitrate > 800 * KILO /* BPS */)
- sample_point_nominal = 750;
- else if (bt->bitrate > 500 * KILO /* BPS */)
- sample_point_nominal = 800;
- else
- sample_point_nominal = 875;
- }
+ if (bt->sample_point)
+ sample_point_reference = bt->sample_point;
+ else if (btc == priv->xl.data_bittiming_const &&
+ (priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
+ sample_point_reference = can_calc_sample_point_pwm(bt);
+ else
+ sample_point_reference = can_calc_sample_point_nrz(bt);
/* tseg even = round down, odd = round up */
for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1;
@@ -114,7 +138,7 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
if (bitrate_error < best_bitrate_error)
best_sample_point_error = UINT_MAX;
- can_update_sample_point(btc, sample_point_nominal, tseg / 2,
+ can_update_sample_point(btc, sample_point_reference, tseg / 2,
&tseg1, &tseg2, &sample_point_error);
if (sample_point_error >= best_sample_point_error)
continue;
@@ -129,23 +153,26 @@ int can_calc_bittiming(const struct net_device *dev, struct can_bittiming *bt,
}
if (best_bitrate_error) {
- /* Error in one-tenth of a percent */
- v64 = (u64)best_bitrate_error * 1000;
+ /* Error in one-hundredth of a percent */
+ v64 = (u64)best_bitrate_error * 10000;
do_div(v64, bt->bitrate);
bitrate_error = (u32)v64;
+ /* print at least 0.01% if the error is smaller */
+ bitrate_error = max(bitrate_error, 1U);
if (bitrate_error > CAN_CALC_MAX_ERROR) {
NL_SET_ERR_MSG_FMT(extack,
- "bitrate error: %u.%u%% too high",
- bitrate_error / 10, bitrate_error % 10);
+ "bitrate error: %u.%02u%% too high",
+ bitrate_error / 100,
+ bitrate_error % 100);
return -EINVAL;
}
NL_SET_ERR_MSG_FMT(extack,
- "bitrate error: %u.%u%%",
- bitrate_error / 10, bitrate_error % 10);
+ "bitrate error: %u.%02u%%",
+ bitrate_error / 100, bitrate_error % 100);
}
/* real sample point */
- bt->sample_point = can_update_sample_point(btc, sample_point_nominal,
+ bt->sample_point = can_update_sample_point(btc, sample_point_reference,
best_tseg, &tseg1, &tseg2,
NULL);
@@ -198,3 +225,38 @@ void can_calc_tdco(struct can_tdc *tdc, const struct can_tdc_const *tdc_const,
*ctrlmode |= tdc_auto;
}
}
+
+int can_calc_pwm(struct net_device *dev, struct netlink_ext_ack *extack)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
+ struct can_pwm *pwm = &priv->xl.pwm;
+ u32 xl_tqmin = can_bit_time_tqmin(&priv->xl.data_bittiming);
+ u32 xl_ns = can_tqmin_to_ns(xl_tqmin, priv->clock.freq);
+ u32 nom_tqmin = can_bit_time_tqmin(&priv->bittiming);
+ int pwm_per_bit_max = xl_tqmin / (pwm_const->pwms_min + pwm_const->pwml_min);
+ int pwm_per_bit;
+ u32 pwm_tqmin;
+
+ /* For 5 MB/s databitrate or greater, xl_ns < CAN_PWM_NS_MAX
+ * giving us a pwm_per_bit of 1 and the loop immediately breaks
+ */
+ for (pwm_per_bit = DIV_ROUND_UP(xl_ns, CAN_PWM_NS_MAX);
+ pwm_per_bit <= pwm_per_bit_max; pwm_per_bit++)
+ if (xl_tqmin % pwm_per_bit == 0)
+ break;
+
+ if (pwm_per_bit > pwm_per_bit_max) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "Can not divide the XL data phase's bit time: %u tqmin into multiple PWM symbols",
+ xl_tqmin);
+ return -EINVAL;
+ }
+
+ pwm_tqmin = xl_tqmin / pwm_per_bit;
+ pwm->pwms = DIV_ROUND_UP_POW2(pwm_tqmin, 4);
+ pwm->pwml = pwm_tqmin - pwm->pwms;
+ pwm->pwmo = nom_tqmin % pwm_tqmin;
+
+ return 0;
+}
diff --git a/drivers/net/can/dev/dev.c b/drivers/net/can/dev/dev.c
index 15ccedbb3f8d..091f30e94c61 100644
--- a/drivers/net/can/dev/dev.c
+++ b/drivers/net/can/dev/dev.c
@@ -92,29 +92,39 @@ const char *can_get_ctrlmode_str(u32 ctrlmode)
{
switch (ctrlmode & ~(ctrlmode - 1)) {
case 0:
- return "none";
+ return "(none)";
case CAN_CTRLMODE_LOOPBACK:
- return "loopback";
+ return "LOOPBACK";
case CAN_CTRLMODE_LISTENONLY:
- return "listen-only";
+ return "LISTEN-ONLY";
case CAN_CTRLMODE_3_SAMPLES:
- return "triple-sampling";
+ return "TRIPLE-SAMPLING";
case CAN_CTRLMODE_ONE_SHOT:
- return "one-shot";
+ return "ONE-SHOT";
case CAN_CTRLMODE_BERR_REPORTING:
- return "berr-reporting";
+ return "BERR-REPORTING";
case CAN_CTRLMODE_FD:
- return "fd";
+ return "FD";
case CAN_CTRLMODE_PRESUME_ACK:
- return "presume-ack";
+ return "PRESUME-ACK";
case CAN_CTRLMODE_FD_NON_ISO:
- return "fd-non-iso";
+ return "FD-NON-ISO";
case CAN_CTRLMODE_CC_LEN8_DLC:
- return "cc-len8-dlc";
+ return "CC-LEN8-DLC";
case CAN_CTRLMODE_TDC_AUTO:
- return "fd-tdc-auto";
+ return "TDC-AUTO";
case CAN_CTRLMODE_TDC_MANUAL:
- return "fd-tdc-manual";
+ return "TDC-MANUAL";
+ case CAN_CTRLMODE_RESTRICTED:
+ return "RESTRICTED";
+ case CAN_CTRLMODE_XL:
+ return "XL";
+ case CAN_CTRLMODE_XL_TDC_AUTO:
+ return "XL-TDC-AUTO";
+ case CAN_CTRLMODE_XL_TDC_MANUAL:
+ return "XL-TDC-MANUAL";
+ case CAN_CTRLMODE_XL_TMS:
+ return "TMS";
default:
return "<unknown>";
}
@@ -348,7 +358,13 @@ void can_set_default_mtu(struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
- if (priv->ctrlmode & CAN_CTRLMODE_FD) {
+ if (priv->ctrlmode & CAN_CTRLMODE_XL) {
+ if (can_is_canxl_dev_mtu(dev->mtu))
+ return;
+ dev->mtu = CANXL_MTU;
+ dev->min_mtu = CANXL_MIN_MTU;
+ dev->max_mtu = CANXL_MAX_MTU;
+ } else if (priv->ctrlmode & CAN_CTRLMODE_FD) {
dev->mtu = CANFD_MTU;
dev->min_mtu = CANFD_MTU;
dev->max_mtu = CANFD_MTU;
@@ -359,44 +375,6 @@ void can_set_default_mtu(struct net_device *dev)
}
}
-/* changing MTU and control mode for CAN/CANFD devices */
-int can_change_mtu(struct net_device *dev, int new_mtu)
-{
- struct can_priv *priv = netdev_priv(dev);
- u32 ctrlmode_static = can_get_static_ctrlmode(priv);
-
- /* Do not allow changing the MTU while running */
- if (dev->flags & IFF_UP)
- return -EBUSY;
-
- /* allow change of MTU according to the CANFD ability of the device */
- switch (new_mtu) {
- case CAN_MTU:
- /* 'CANFD-only' controllers can not switch to CAN_MTU */
- if (ctrlmode_static & CAN_CTRLMODE_FD)
- return -EINVAL;
-
- priv->ctrlmode &= ~CAN_CTRLMODE_FD;
- break;
-
- case CANFD_MTU:
- /* check for potential CANFD ability */
- if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) &&
- !(ctrlmode_static & CAN_CTRLMODE_FD))
- return -EINVAL;
-
- priv->ctrlmode |= CAN_CTRLMODE_FD;
- break;
-
- default:
- return -EINVAL;
- }
-
- WRITE_ONCE(dev->mtu, new_mtu);
- return 0;
-}
-EXPORT_SYMBOL_GPL(can_change_mtu);
-
/* helper to define static CAN controller features at device creation time */
int can_set_static_ctrlmode(struct net_device *dev, u32 static_mode)
{
@@ -417,34 +395,33 @@ int can_set_static_ctrlmode(struct net_device *dev, u32 static_mode)
}
EXPORT_SYMBOL_GPL(can_set_static_ctrlmode);
-/* generic implementation of netdev_ops::ndo_eth_ioctl for CAN devices
+/* generic implementation of netdev_ops::ndo_hwtstamp_get for CAN devices
* supporting hardware timestamps
*/
-int can_eth_ioctl_hwts(struct net_device *netdev, struct ifreq *ifr, int cmd)
+int can_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *cfg)
{
- struct hwtstamp_config hwts_cfg = { 0 };
-
- switch (cmd) {
- case SIOCSHWTSTAMP: /* set */
- if (copy_from_user(&hwts_cfg, ifr->ifr_data, sizeof(hwts_cfg)))
- return -EFAULT;
- if (hwts_cfg.tx_type == HWTSTAMP_TX_ON &&
- hwts_cfg.rx_filter == HWTSTAMP_FILTER_ALL)
- return 0;
- return -ERANGE;
-
- case SIOCGHWTSTAMP: /* get */
- hwts_cfg.tx_type = HWTSTAMP_TX_ON;
- hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL;
- if (copy_to_user(ifr->ifr_data, &hwts_cfg, sizeof(hwts_cfg)))
- return -EFAULT;
- return 0;
+ cfg->tx_type = HWTSTAMP_TX_ON;
+ cfg->rx_filter = HWTSTAMP_FILTER_ALL;
- default:
- return -EOPNOTSUPP;
- }
+ return 0;
+}
+EXPORT_SYMBOL(can_hwtstamp_get);
+
+/* generic implementation of netdev_ops::ndo_hwtstamp_set for CAN devices
+ * supporting hardware timestamps
+ */
+int can_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
+{
+ if (cfg->tx_type == HWTSTAMP_TX_ON &&
+ cfg->rx_filter == HWTSTAMP_FILTER_ALL)
+ return 0;
+ NL_SET_ERR_MSG_MOD(extack, "Only TX on and RX all packets filter supported");
+ return -ERANGE;
}
-EXPORT_SYMBOL(can_eth_ioctl_hwts);
+EXPORT_SYMBOL(can_hwtstamp_set);
/* generic implementation of ethtool_ops::get_ts_info for CAN devices
* supporting hardware timestamps
diff --git a/drivers/net/can/dev/netlink.c b/drivers/net/can/dev/netlink.c
index 0591406b6f32..d6b0e686fb11 100644
--- a/drivers/net/can/dev/netlink.c
+++ b/drivers/net/can/dev/netlink.c
@@ -2,7 +2,7 @@
/* Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
* Copyright (C) 2006 Andrey Volkov, Varma Electronics
* Copyright (C) 2008-2009 Wolfgang Grandegger <wg@grandegger.com>
- * Copyright (C) 2021 Vincent Mailhol <mailhol.vincent@wanadoo.fr>
+ * Copyright (C) 2021-2025 Vincent Mailhol <mailhol@kernel.org>
*/
#include <linux/can/dev.h>
@@ -22,6 +22,10 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
[IFLA_CAN_TERMINATION] = { .type = NLA_U16 },
[IFLA_CAN_TDC] = { .type = NLA_NESTED },
[IFLA_CAN_CTRLMODE_EXT] = { .type = NLA_NESTED },
+ [IFLA_CAN_XL_DATA_BITTIMING] = { .len = sizeof(struct can_bittiming) },
+ [IFLA_CAN_XL_DATA_BITTIMING_CONST] = { .len = sizeof(struct can_bittiming_const) },
+ [IFLA_CAN_XL_TDC] = { .type = NLA_NESTED },
+ [IFLA_CAN_XL_PWM] = { .type = NLA_NESTED },
};
static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
@@ -36,6 +40,18 @@ static const struct nla_policy can_tdc_policy[IFLA_CAN_TDC_MAX + 1] = {
[IFLA_CAN_TDC_TDCF] = { .type = NLA_U32 },
};
+static const struct nla_policy can_pwm_policy[IFLA_CAN_PWM_MAX + 1] = {
+ [IFLA_CAN_PWM_PWMS_MIN] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWMS_MAX] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWML_MIN] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWML_MAX] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWMO_MIN] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWMO_MAX] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWMS] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWML] = { .type = NLA_U32 },
+ [IFLA_CAN_PWM_PWMO] = { .type = NLA_U32 },
+};
+
static int can_validate_bittiming(struct nlattr *data[],
struct netlink_ext_ack *extack,
int ifla_can_bittiming)
@@ -70,7 +86,7 @@ static int can_validate_tdc(struct nlattr *data_tdc,
return -EOPNOTSUPP;
}
- /* If one of the CAN_CTRLMODE_TDC_* flag is set then TDC
+ /* If one of the CAN_CTRLMODE_{,XL}_TDC_* flags is set then TDC
* must be set and vice-versa
*/
if ((tdc_auto || tdc_manual) && !data_tdc) {
@@ -82,8 +98,8 @@ static int can_validate_tdc(struct nlattr *data_tdc,
return -EOPNOTSUPP;
}
- /* If providing TDC parameters, at least TDCO is needed. TDCV
- * is needed if and only if CAN_CTRLMODE_TDC_MANUAL is set
+ /* If providing TDC parameters, at least TDCO is needed. TDCV is
+ * needed if and only if CAN_CTRLMODE_{,XL}_TDC_MANUAL is set
*/
if (data_tdc) {
struct nlattr *tb_tdc[IFLA_CAN_TDC_MAX + 1];
@@ -116,6 +132,40 @@ static int can_validate_tdc(struct nlattr *data_tdc,
return 0;
}
+static int can_validate_pwm(struct nlattr *data[],
+ struct netlink_ext_ack *extack, u32 flags)
+{
+ struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
+ int err;
+
+ if (!data[IFLA_CAN_XL_PWM])
+ return 0;
+
+ if (!(flags & CAN_CTRLMODE_XL_TMS)) {
+ NL_SET_ERR_MSG(extack, "PWM requires TMS");
+ return -EOPNOTSUPP;
+ }
+
+ err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, data[IFLA_CAN_XL_PWM],
+ can_pwm_policy, extack);
+ if (err)
+ return err;
+
+ if (!tb_pwm[IFLA_CAN_PWM_PWMS] != !tb_pwm[IFLA_CAN_PWM_PWML]) {
+ NL_SET_ERR_MSG(extack,
+ "Provide either both PWMS and PWML, or none for automatic calculation");
+ return -EOPNOTSUPP;
+ }
+
+ if (tb_pwm[IFLA_CAN_PWM_PWMO] &&
+ (!tb_pwm[IFLA_CAN_PWM_PWMS] || !tb_pwm[IFLA_CAN_PWM_PWML])) {
+ NL_SET_ERR_MSG(extack, "PWMO requires both PWMS and PWML");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int can_validate_databittiming(struct nlattr *data[],
struct netlink_ext_ack *extack,
int ifla_can_data_bittiming, u32 flags)
@@ -126,10 +176,10 @@ static int can_validate_databittiming(struct nlattr *data[],
bool is_on;
int err;
- /* Make sure that valid CAN FD configurations always consist of
+ /* Make sure that valid CAN FD/XL configurations always consist of
* - nominal/arbitration bittiming
* - data bittiming
- * - control mode with CAN_CTRLMODE_FD set
+ * - control mode with CAN_CTRLMODE_{FD,XL} set
* - TDC parameters are coherent (details in can_validate_tdc())
*/
@@ -139,7 +189,10 @@ static int can_validate_databittiming(struct nlattr *data[],
is_on = flags & CAN_CTRLMODE_FD;
type = "FD";
} else {
- return -EOPNOTSUPP; /* Place holder for CAN XL */
+ data_tdc = data[IFLA_CAN_XL_TDC];
+ tdc_flags = flags & CAN_CTRLMODE_XL_TDC_MASK;
+ is_on = flags & CAN_CTRLMODE_XL;
+ type = "XL";
}
if (is_on) {
@@ -175,6 +228,32 @@ static int can_validate_databittiming(struct nlattr *data[],
return 0;
}
+static int can_validate_xl_flags(struct netlink_ext_ack *extack,
+ u32 masked_flags, u32 mask)
+{
+ if (masked_flags & CAN_CTRLMODE_XL) {
+ if (masked_flags & CAN_CTRLMODE_XL_TMS) {
+ const u32 tms_conflicts_mask = CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_XL_TDC_MASK;
+ u32 tms_conflicts = masked_flags & tms_conflicts_mask;
+
+ if (tms_conflicts) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "TMS and %s are mutually exclusive",
+ can_get_ctrlmode_str(tms_conflicts));
+ return -EOPNOTSUPP;
+ }
+ }
+ } else {
+ if (mask & CAN_CTRLMODE_XL_TMS) {
+ NL_SET_ERR_MSG(extack, "TMS requires CAN XL");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
static int can_validate(struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
@@ -188,6 +267,17 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[],
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
flags = cm->flags & cm->mask;
+
+ if ((flags & CAN_CTRLMODE_LISTENONLY) &&
+ (flags & CAN_CTRLMODE_RESTRICTED)) {
+ NL_SET_ERR_MSG(extack,
+ "LISTEN-ONLY and RESTRICTED modes are mutually exclusive");
+ return -EOPNOTSUPP;
+ }
+
+ err = can_validate_xl_flags(extack, flags, cm->mask);
+ if (err)
+ return err;
}
err = can_validate_bittiming(data, extack, IFLA_CAN_BITTIMING);
@@ -199,6 +289,15 @@ static int can_validate(struct nlattr *tb[], struct nlattr *data[],
if (err)
return err;
+ err = can_validate_databittiming(data, extack,
+ IFLA_CAN_XL_DATA_BITTIMING, flags);
+ if (err)
+ return err;
+
+ err = can_validate_pwm(data, extack, flags);
+ if (err)
+ return err;
+
return 0;
}
@@ -208,7 +307,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
{
struct can_priv *priv = netdev_priv(dev);
struct can_ctrlmode *cm;
- u32 ctrlstatic, maskedflags, notsupp, ctrlstatic_missing;
+ u32 ctrlstatic, maskedflags, deactivated, notsupp, ctrlstatic_missing;
if (!data[IFLA_CAN_CTRLMODE])
return 0;
@@ -220,6 +319,7 @@ static int can_ctrlmode_changelink(struct net_device *dev,
cm = nla_data(data[IFLA_CAN_CTRLMODE]);
ctrlstatic = can_get_static_ctrlmode(priv);
maskedflags = cm->flags & cm->mask;
+ deactivated = ~cm->flags & cm->mask;
notsupp = maskedflags & ~(priv->ctrlmode_supported | ctrlstatic);
ctrlstatic_missing = (maskedflags & ctrlstatic) ^ ctrlstatic;
@@ -241,21 +341,40 @@ static int can_ctrlmode_changelink(struct net_device *dev,
return -EOPNOTSUPP;
}
+ /* If FD was active and is not turned off, check for XL conflicts */
+ if (priv->ctrlmode & CAN_CTRLMODE_FD & ~deactivated) {
+ if (maskedflags & CAN_CTRLMODE_XL_TMS) {
+ NL_SET_ERR_MSG(extack,
+ "TMS can not be activated while CAN FD is on");
+ return -EOPNOTSUPP;
+ }
+ }
+
/* If a top dependency flag is provided, reset all its dependencies */
if (cm->mask & CAN_CTRLMODE_FD)
priv->ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
+ if (cm->mask & CAN_CTRLMODE_XL)
+ priv->ctrlmode &= ~(CAN_CTRLMODE_XL_TDC_MASK |
+ CAN_CTRLMODE_XL_TMS);
/* clear bits to be modified and copy the flag values */
priv->ctrlmode &= ~cm->mask;
priv->ctrlmode |= maskedflags;
- /* Wipe potential leftovers from previous CAN FD config */
+ /* Wipe potential leftovers from previous CAN FD/XL config */
if (!(priv->ctrlmode & CAN_CTRLMODE_FD)) {
memset(&priv->fd.data_bittiming, 0,
sizeof(priv->fd.data_bittiming));
priv->ctrlmode &= ~CAN_CTRLMODE_FD_TDC_MASK;
memset(&priv->fd.tdc, 0, sizeof(priv->fd.tdc));
}
+ if (!(priv->ctrlmode & CAN_CTRLMODE_XL)) {
+ memset(&priv->xl.data_bittiming, 0,
+ sizeof(priv->fd.data_bittiming));
+ priv->ctrlmode &= ~CAN_CTRLMODE_XL_TDC_MASK;
+ memset(&priv->xl.tdc, 0, sizeof(priv->xl.tdc));
+ memset(&priv->xl.pwm, 0, sizeof(priv->xl.pwm));
+ }
can_set_default_mtu(dev);
@@ -330,7 +449,10 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
dbt_params = &priv->fd;
tdc_mask = CAN_CTRLMODE_FD_TDC_MASK;
} else {
- return -EOPNOTSUPP; /* Place holder for CAN XL */
+ data_bittiming = data[IFLA_CAN_XL_DATA_BITTIMING];
+ data_tdc = data[IFLA_CAN_XL_TDC];
+ dbt_params = &priv->xl;
+ tdc_mask = CAN_CTRLMODE_XL_TDC_MASK;
}
if (!data_bittiming)
@@ -366,7 +488,8 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
if (data[IFLA_CAN_CTRLMODE]) {
struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]);
- need_tdc_calc = !(cm->mask & tdc_mask);
+ if (fd || !(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
+ need_tdc_calc = !(cm->mask & tdc_mask);
}
if (data_tdc) {
/* TDC parameters are provided: use them */
@@ -381,7 +504,7 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
*/
can_calc_tdco(&dbt_params->tdc, dbt_params->tdc_const, &dbt,
tdc_mask, &priv->ctrlmode, priv->ctrlmode_supported);
- } /* else: both CAN_CTRLMODE_TDC_{AUTO,MANUAL} are explicitly
+ } /* else: both CAN_CTRLMODE_{,XL}_TDC_{AUTO,MANUAL} are explicitly
* turned off. TDC is disabled: do nothing
*/
@@ -397,6 +520,76 @@ static int can_dbt_changelink(struct net_device *dev, struct nlattr *data[],
return 0;
}
+static int can_pwm_changelink(struct net_device *dev,
+ const struct nlattr *pwm_nla,
+ struct netlink_ext_ack *extack)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
+ struct nlattr *tb_pwm[IFLA_CAN_PWM_MAX + 1];
+ struct can_pwm pwm = { 0 };
+ int err;
+
+ if (!(priv->ctrlmode & CAN_CTRLMODE_XL_TMS))
+ return 0;
+
+ if (!pwm_const) {
+ NL_SET_ERR_MSG(extack, "The device does not support PWM");
+ return -EOPNOTSUPP;
+ }
+
+ if (!pwm_nla)
+ return can_calc_pwm(dev, extack);
+
+ err = nla_parse_nested(tb_pwm, IFLA_CAN_PWM_MAX, pwm_nla,
+ can_pwm_policy, extack);
+ if (err)
+ return err;
+
+ if (tb_pwm[IFLA_CAN_PWM_PWMS]) {
+ pwm.pwms = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMS]);
+ if (pwm.pwms < pwm_const->pwms_min ||
+ pwm.pwms > pwm_const->pwms_max) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "PWMS: %u tqmin is out of range: %u...%u",
+ pwm.pwms, pwm_const->pwms_min,
+ pwm_const->pwms_max);
+ return -EINVAL;
+ }
+ }
+
+ if (tb_pwm[IFLA_CAN_PWM_PWML]) {
+ pwm.pwml = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWML]);
+ if (pwm.pwml < pwm_const->pwml_min ||
+ pwm.pwml > pwm_const->pwml_max) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "PWML: %u tqmin is out of range: %u...%u",
+ pwm.pwml, pwm_const->pwml_min,
+ pwm_const->pwml_max);
+ return -EINVAL;
+ }
+ }
+
+ if (tb_pwm[IFLA_CAN_PWM_PWMO]) {
+ pwm.pwmo = nla_get_u32(tb_pwm[IFLA_CAN_PWM_PWMO]);
+ if (pwm.pwmo < pwm_const->pwmo_min ||
+ pwm.pwmo > pwm_const->pwmo_max) {
+ NL_SET_ERR_MSG_FMT(extack,
+ "PWMO: %u tqmin is out of range: %u...%u",
+ pwm.pwmo, pwm_const->pwmo_min,
+ pwm_const->pwmo_max);
+ return -EINVAL;
+ }
+ }
+
+ err = can_validate_pwm_bittiming(dev, &pwm, extack);
+ if (err)
+ return err;
+
+ priv->xl.pwm = pwm;
+ return 0;
+}
+
static int can_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
@@ -452,7 +645,9 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
}
if (data[IFLA_CAN_RESTART_MS]) {
- if (!priv->do_set_mode) {
+ unsigned int restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]);
+
+ if (restart_ms != 0 && !priv->do_set_mode) {
NL_SET_ERR_MSG(extack,
"Device doesn't support restart from Bus Off");
return -EOPNOTSUPP;
@@ -461,7 +656,7 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
/* Do not allow changing restart delay while running */
if (dev->flags & IFF_UP)
return -EBUSY;
- priv->restart_ms = nla_get_u32(data[IFLA_CAN_RESTART_MS]);
+ priv->restart_ms = restart_ms;
}
if (data[IFLA_CAN_RESTART]) {
@@ -484,6 +679,14 @@ static int can_changelink(struct net_device *dev, struct nlattr *tb[],
if (err)
return err;
+ /* CAN XL */
+ err = can_dbt_changelink(dev, data, false, extack);
+ if (err)
+ return err;
+ err = can_pwm_changelink(dev, data[IFLA_CAN_XL_PWM], extack);
+ if (err)
+ return err;
+
if (data[IFLA_CAN_TERMINATION]) {
const u16 termval = nla_get_u16(data[IFLA_CAN_TERMINATION]);
const unsigned int num_term = priv->termination_const_cnt;
@@ -551,14 +754,14 @@ static size_t can_data_bittiming_get_size(struct data_bittiming_params *dbt_para
{
size_t size = 0;
- if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_DATA_BITTIMING */
+ if (dbt_params->data_bittiming.bitrate) /* IFLA_CAN_{,XL}_DATA_BITTIMING */
size += nla_total_size(sizeof(dbt_params->data_bittiming));
- if (dbt_params->data_bittiming_const) /* IFLA_CAN_DATA_BITTIMING_CONST */
+ if (dbt_params->data_bittiming_const) /* IFLA_CAN_{,XL}_DATA_BITTIMING_CONST */
size += nla_total_size(sizeof(*dbt_params->data_bittiming_const));
- if (dbt_params->data_bitrate_const) /* IFLA_CAN_DATA_BITRATE_CONST */
+ if (dbt_params->data_bitrate_const) /* IFLA_CAN_{,XL}_DATA_BITRATE_CONST */
size += nla_total_size(sizeof(*dbt_params->data_bitrate_const) *
dbt_params->data_bitrate_const_cnt);
- size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_TDC */
+ size += can_tdc_get_size(dbt_params, tdc_flags);/* IFLA_CAN_{,XL}_TDC */
return size;
}
@@ -569,6 +772,30 @@ static size_t can_ctrlmode_ext_get_size(void)
nla_total_size(sizeof(u32)); /* IFLA_CAN_CTRLMODE_SUPPORTED */
}
+static size_t can_pwm_get_size(const struct can_pwm_const *pwm_const,
+ bool pwm_on)
+{
+ size_t size;
+
+ if (!pwm_const || !pwm_on)
+ return 0;
+
+ size = nla_total_size(0); /* nest IFLA_CAN_PWM */
+
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MIN */
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS_MAX */
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MIN */
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML_MAX */
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MIN */
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO_MAX */
+
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMS */
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWML */
+ size += nla_total_size(sizeof(u32)); /* IFLA_CAN_PWM_PWMO */
+
+ return size;
+}
+
static size_t can_get_size(const struct net_device *dev)
{
struct can_priv *priv = netdev_priv(dev);
@@ -598,6 +825,11 @@ static size_t can_get_size(const struct net_device *dev)
size += can_data_bittiming_get_size(&priv->fd,
priv->ctrlmode & CAN_CTRLMODE_FD_TDC_MASK);
+ size += can_data_bittiming_get_size(&priv->xl,
+ priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MASK);
+ size += can_pwm_get_size(priv->xl.pwm_const, /* IFLA_CAN_XL_PWM */
+ priv->ctrlmode & CAN_CTRLMODE_XL_TMS);
+
return size;
}
@@ -642,7 +874,9 @@ static int can_tdc_fill_info(struct sk_buff *skb, const struct net_device *dev,
tdc_is_enabled = can_fd_tdc_is_enabled(priv);
tdc_manual = priv->ctrlmode & CAN_CTRLMODE_TDC_MANUAL;
} else {
- return -EOPNOTSUPP; /* Place holder for CAN XL */
+ dbt_params = &priv->xl;
+ tdc_is_enabled = can_xl_tdc_is_enabled(priv);
+ tdc_manual = priv->ctrlmode & CAN_CTRLMODE_XL_TDC_MANUAL;
}
tdc_const = dbt_params->tdc_const;
tdc = &dbt_params->tdc;
@@ -693,6 +927,42 @@ err_cancel:
return -EMSGSIZE;
}
+static int can_pwm_fill_info(struct sk_buff *skb, const struct can_priv *priv)
+{
+ const struct can_pwm_const *pwm_const = priv->xl.pwm_const;
+ const struct can_pwm *pwm = &priv->xl.pwm;
+ struct nlattr *nest;
+
+ if (!pwm_const)
+ return 0;
+
+ nest = nla_nest_start(skb, IFLA_CAN_XL_PWM);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MIN, pwm_const->pwms_min) ||
+ nla_put_u32(skb, IFLA_CAN_PWM_PWMS_MAX, pwm_const->pwms_max) ||
+ nla_put_u32(skb, IFLA_CAN_PWM_PWML_MIN, pwm_const->pwml_min) ||
+ nla_put_u32(skb, IFLA_CAN_PWM_PWML_MAX, pwm_const->pwml_max) ||
+ nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MIN, pwm_const->pwmo_min) ||
+ nla_put_u32(skb, IFLA_CAN_PWM_PWMO_MAX, pwm_const->pwmo_max))
+ goto err_cancel;
+
+ if (priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
+ if (nla_put_u32(skb, IFLA_CAN_PWM_PWMS, pwm->pwms) ||
+ nla_put_u32(skb, IFLA_CAN_PWM_PWML, pwm->pwml) ||
+ nla_put_u32(skb, IFLA_CAN_PWM_PWMO, pwm->pwmo))
+ goto err_cancel;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+
+err_cancel:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
static int can_ctrlmode_ext_fill_info(struct sk_buff *skb,
const struct can_priv *priv)
{
@@ -764,9 +1034,22 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev)
can_tdc_fill_info(skb, dev, IFLA_CAN_TDC) ||
- can_ctrlmode_ext_fill_info(skb, priv)
- )
+ can_ctrlmode_ext_fill_info(skb, priv) ||
+
+ can_bittiming_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING,
+ &priv->xl.data_bittiming) ||
+ can_bittiming_const_fill_info(skb, IFLA_CAN_XL_DATA_BITTIMING_CONST,
+ priv->xl.data_bittiming_const) ||
+
+ can_bitrate_const_fill_info(skb, IFLA_CAN_XL_DATA_BITRATE_CONST,
+ priv->xl.data_bitrate_const,
+ priv->xl.data_bitrate_const_cnt) ||
+
+ can_tdc_fill_info(skb, dev, IFLA_CAN_XL_TDC) ||
+
+ can_pwm_fill_info(skb, priv)
+ )
return -EMSGSIZE;
return 0;
diff --git a/drivers/net/can/dummy_can.c b/drivers/net/can/dummy_can.c
new file mode 100644
index 000000000000..41953655e3d3
--- /dev/null
+++ b/drivers/net/can/dummy_can.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Copyright (c) 2025 Vincent Mailhol <mailhol@kernel.org> */
+
+#include <linux/array_size.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/units.h>
+#include <linux/string_choices.h>
+
+#include <linux/can.h>
+#include <linux/can/bittiming.h>
+#include <linux/can/dev.h>
+#include <linux/can/skb.h>
+
+struct dummy_can {
+ struct can_priv can;
+ struct net_device *dev;
+};
+
+static struct dummy_can *dummy_can;
+
+static const struct can_bittiming_const dummy_can_bittiming_const = {
+ .name = "dummy_can CC",
+ .tseg1_min = 2,
+ .tseg1_max = 256,
+ .tseg2_min = 2,
+ .tseg2_max = 128,
+ .sjw_max = 128,
+ .brp_min = 1,
+ .brp_max = 512,
+ .brp_inc = 1
+};
+
+static const struct can_bittiming_const dummy_can_fd_databittiming_const = {
+ .name = "dummy_can FD",
+ .tseg1_min = 2,
+ .tseg1_max = 256,
+ .tseg2_min = 2,
+ .tseg2_max = 128,
+ .sjw_max = 128,
+ .brp_min = 1,
+ .brp_max = 512,
+ .brp_inc = 1
+};
+
+static const struct can_tdc_const dummy_can_fd_tdc_const = {
+ .tdcv_min = 0,
+ .tdcv_max = 0, /* Manual mode not supported. */
+ .tdco_min = 0,
+ .tdco_max = 127,
+ .tdcf_min = 0,
+ .tdcf_max = 127
+};
+
+static const struct can_bittiming_const dummy_can_xl_databittiming_const = {
+ .name = "dummy_can XL",
+ .tseg1_min = 2,
+ .tseg1_max = 256,
+ .tseg2_min = 2,
+ .tseg2_max = 128,
+ .sjw_max = 128,
+ .brp_min = 1,
+ .brp_max = 512,
+ .brp_inc = 1
+};
+
+static const struct can_tdc_const dummy_can_xl_tdc_const = {
+ .tdcv_min = 0,
+ .tdcv_max = 0, /* Manual mode not supported. */
+ .tdco_min = 0,
+ .tdco_max = 127,
+ .tdcf_min = 0,
+ .tdcf_max = 127
+};
+
+static const struct can_pwm_const dummy_can_pwm_const = {
+ .pwms_min = 1,
+ .pwms_max = 8,
+ .pwml_min = 2,
+ .pwml_max = 24,
+ .pwmo_min = 0,
+ .pwmo_max = 16,
+};
+
+static void dummy_can_print_bittiming(struct net_device *dev,
+ struct can_bittiming *bt)
+{
+ netdev_dbg(dev, "\tbitrate: %u\n", bt->bitrate);
+ netdev_dbg(dev, "\tsample_point: %u\n", bt->sample_point);
+ netdev_dbg(dev, "\ttq: %u\n", bt->tq);
+ netdev_dbg(dev, "\tprop_seg: %u\n", bt->prop_seg);
+ netdev_dbg(dev, "\tphase_seg1: %u\n", bt->phase_seg1);
+ netdev_dbg(dev, "\tphase_seg2: %u\n", bt->phase_seg2);
+ netdev_dbg(dev, "\tsjw: %u\n", bt->sjw);
+ netdev_dbg(dev, "\tbrp: %u\n", bt->brp);
+}
+
+static void dummy_can_print_tdc(struct net_device *dev, struct can_tdc *tdc)
+{
+ netdev_dbg(dev, "\t\ttdcv: %u\n", tdc->tdcv);
+ netdev_dbg(dev, "\t\ttdco: %u\n", tdc->tdco);
+ netdev_dbg(dev, "\t\ttdcf: %u\n", tdc->tdcf);
+}
+
+static void dummy_can_print_pwm(struct net_device *dev, struct can_pwm *pwm,
+ struct can_bittiming *dbt)
+{
+ netdev_dbg(dev, "\t\tpwms: %u\n", pwm->pwms);
+ netdev_dbg(dev, "\t\tpwml: %u\n", pwm->pwml);
+ netdev_dbg(dev, "\t\tpwmo: %u\n", pwm->pwmo);
+}
+
+static void dummy_can_print_ctrlmode(struct net_device *dev)
+{
+ struct dummy_can *priv = netdev_priv(dev);
+ struct can_priv *can_priv = &priv->can;
+ unsigned long supported = can_priv->ctrlmode_supported;
+ u32 enabled = can_priv->ctrlmode;
+
+ netdev_dbg(dev, "Control modes:\n");
+ netdev_dbg(dev, "\tsupported: 0x%08x\n", (u32)supported);
+ netdev_dbg(dev, "\tenabled: 0x%08x\n", enabled);
+
+ if (supported) {
+ int idx;
+
+ netdev_dbg(dev, "\tlist:");
+ for_each_set_bit(idx, &supported, BITS_PER_TYPE(u32))
+ netdev_dbg(dev, "\t\t%s: %s\n",
+ can_get_ctrlmode_str(BIT(idx)),
+ enabled & BIT(idx) ? "on" : "off");
+ }
+}
+
+static void dummy_can_print_bittiming_info(struct net_device *dev)
+{
+ struct dummy_can *priv = netdev_priv(dev);
+ struct can_priv *can_priv = &priv->can;
+
+ netdev_dbg(dev, "Clock frequency: %u\n", can_priv->clock.freq);
+ netdev_dbg(dev, "Maximum bitrate: %u\n", can_priv->bitrate_max);
+ netdev_dbg(dev, "MTU: %u\n", dev->mtu);
+ netdev_dbg(dev, "\n");
+
+ dummy_can_print_ctrlmode(dev);
+ netdev_dbg(dev, "\n");
+
+ netdev_dbg(dev, "Classical CAN nominal bittiming:\n");
+ dummy_can_print_bittiming(dev, &can_priv->bittiming);
+ netdev_dbg(dev, "\n");
+
+ if (can_priv->ctrlmode & CAN_CTRLMODE_FD) {
+ netdev_dbg(dev, "CAN FD databittiming:\n");
+ dummy_can_print_bittiming(dev, &can_priv->fd.data_bittiming);
+ if (can_fd_tdc_is_enabled(can_priv)) {
+ netdev_dbg(dev, "\tCAN FD TDC:\n");
+ dummy_can_print_tdc(dev, &can_priv->fd.tdc);
+ }
+ }
+ netdev_dbg(dev, "\n");
+
+ if (can_priv->ctrlmode & CAN_CTRLMODE_XL) {
+ netdev_dbg(dev, "CAN XL databittiming:\n");
+ dummy_can_print_bittiming(dev, &can_priv->xl.data_bittiming);
+ if (can_xl_tdc_is_enabled(can_priv)) {
+ netdev_dbg(dev, "\tCAN XL TDC:\n");
+ dummy_can_print_tdc(dev, &can_priv->xl.tdc);
+ }
+ if (can_priv->ctrlmode & CAN_CTRLMODE_XL_TMS) {
+ netdev_dbg(dev, "\tCAN XL PWM:\n");
+ dummy_can_print_pwm(dev, &can_priv->xl.pwm,
+ &can_priv->xl.data_bittiming);
+ }
+ }
+ netdev_dbg(dev, "\n");
+}
+
+static int dummy_can_netdev_open(struct net_device *dev)
+{
+ int ret;
+ struct can_priv *priv = netdev_priv(dev);
+
+ dummy_can_print_bittiming_info(dev);
+ netdev_dbg(dev, "error-signalling is %s\n",
+ str_enabled_disabled(!can_dev_in_xl_only_mode(priv)));
+
+ ret = open_candev(dev);
+ if (ret)
+ return ret;
+ netif_start_queue(dev);
+ netdev_dbg(dev, "dummy-can is up\n");
+
+ return 0;
+}
+
+static int dummy_can_netdev_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ close_candev(dev);
+ netdev_dbg(dev, "dummy-can is down\n");
+
+ return 0;
+}
+
+static netdev_tx_t dummy_can_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ if (can_dev_dropped_skb(dev, skb))
+ return NETDEV_TX_OK;
+
+ can_put_echo_skb(skb, dev, 0, 0);
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += can_get_echo_skb(dev, 0, NULL);
+
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops dummy_can_netdev_ops = {
+ .ndo_open = dummy_can_netdev_open,
+ .ndo_stop = dummy_can_netdev_close,
+ .ndo_start_xmit = dummy_can_start_xmit,
+};
+
+static const struct ethtool_ops dummy_can_ethtool_ops = {
+ .get_ts_info = ethtool_op_get_ts_info,
+};
+
+static int __init dummy_can_init(void)
+{
+ struct net_device *dev;
+ struct dummy_can *priv;
+ int ret;
+
+ dev = alloc_candev(sizeof(*priv), 1);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->netdev_ops = &dummy_can_netdev_ops;
+ dev->ethtool_ops = &dummy_can_ethtool_ops;
+ priv = netdev_priv(dev);
+ priv->can.bittiming_const = &dummy_can_bittiming_const;
+ priv->can.bitrate_max = 20 * MEGA /* BPS */;
+ priv->can.clock.freq = 160 * MEGA /* Hz */;
+ priv->can.fd.data_bittiming_const = &dummy_can_fd_databittiming_const;
+ priv->can.fd.tdc_const = &dummy_can_fd_tdc_const;
+ priv->can.xl.data_bittiming_const = &dummy_can_xl_databittiming_const;
+ priv->can.xl.tdc_const = &dummy_can_xl_tdc_const;
+ priv->can.xl.pwm_const = &dummy_can_pwm_const;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_FD | CAN_CTRLMODE_TDC_AUTO |
+ CAN_CTRLMODE_RESTRICTED | CAN_CTRLMODE_XL |
+ CAN_CTRLMODE_XL_TDC_AUTO | CAN_CTRLMODE_XL_TMS;
+ priv->dev = dev;
+
+ ret = register_candev(priv->dev);
+ if (ret) {
+ free_candev(priv->dev);
+ return ret;
+ }
+
+ dummy_can = priv;
+ netdev_dbg(dev, "dummy-can ready\n");
+
+ return 0;
+}
+
+static void __exit dummy_can_exit(void)
+{
+ struct net_device *dev = dummy_can->dev;
+
+ netdev_dbg(dev, "dummy-can bye bye\n");
+ unregister_candev(dev);
+ free_candev(dev);
+}
+
+module_init(dummy_can_init);
+module_exit(dummy_can_exit);
+
+MODULE_DESCRIPTION("A dummy CAN driver, mainly to test the netlink interface");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vincent Mailhol <mailhol@kernel.org>");
diff --git a/drivers/net/can/esd/esd_402_pci-core.c b/drivers/net/can/esd/esd_402_pci-core.c
index 5d6d2828cd04..c826f00c551b 100644
--- a/drivers/net/can/esd/esd_402_pci-core.c
+++ b/drivers/net/can/esd/esd_402_pci-core.c
@@ -86,8 +86,8 @@ static const struct net_device_ops pci402_acc_netdev_ops = {
.ndo_open = acc_open,
.ndo_stop = acc_close,
.ndo_start_xmit = acc_start_xmit,
- .ndo_change_mtu = can_change_mtu,
- .ndo_eth_ioctl = can_eth_ioctl_hwts,
+ .ndo_hwtstamp_get = can_hwtstamp_get,
+ .ndo_hwtstamp_set = can_hwtstamp_set,
};
static const struct ethtool_ops pci402_acc_ethtool_ops = {
diff --git a/drivers/net/can/esd/esdacc.c b/drivers/net/can/esd/esdacc.c
index c80032bc1a52..73e66f9a3781 100644
--- a/drivers/net/can/esd/esdacc.c
+++ b/drivers/net/can/esd/esdacc.c
@@ -254,7 +254,7 @@ netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev)
u32 acc_id;
u32 acc_dlc;
- if (can_dropped_invalid_skb(netdev, skb))
+ if (can_dev_dropped_skb(netdev, skb))
return NETDEV_TX_OK;
/* Access core->tx_fifo_tail only once because it may be changed
diff --git a/drivers/net/can/flexcan/flexcan-core.c b/drivers/net/can/flexcan/flexcan-core.c
index 06d5d35fc1b5..f5d22c61503f 100644
--- a/drivers/net/can/flexcan/flexcan-core.c
+++ b/drivers/net/can/flexcan/flexcan-core.c
@@ -1867,7 +1867,6 @@ static const struct net_device_ops flexcan_netdev_ops = {
.ndo_open = flexcan_open,
.ndo_stop = flexcan_close,
.ndo_start_xmit = flexcan_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static int register_flexcandev(struct net_device *dev)
diff --git a/drivers/net/can/grcan.c b/drivers/net/can/grcan.c
index c5784d9779ef..3b1b09943436 100644
--- a/drivers/net/can/grcan.c
+++ b/drivers/net/can/grcan.c
@@ -1561,7 +1561,6 @@ static const struct net_device_ops grcan_netdev_ops = {
.ndo_open = grcan_open,
.ndo_stop = grcan_close,
.ndo_start_xmit = grcan_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops grcan_ethtool_ops = {
diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c
index 2eeee65f606f..0f83335e4d07 100644
--- a/drivers/net/can/ifi_canfd/ifi_canfd.c
+++ b/drivers/net/can/ifi_canfd/ifi_canfd.c
@@ -944,7 +944,6 @@ static const struct net_device_ops ifi_canfd_netdev_ops = {
.ndo_open = ifi_canfd_open,
.ndo_stop = ifi_canfd_close,
.ndo_start_xmit = ifi_canfd_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops ifi_canfd_ethtool_ops = {
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index bfa5cbe88017..1efdd1fd8caa 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -1752,7 +1752,6 @@ static const struct net_device_ops ican3_netdev_ops = {
.ndo_open = ican3_open,
.ndo_stop = ican3_stop,
.ndo_start_xmit = ican3_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops ican3_ethtool_ops = {
diff --git a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c
index 0880023611be..d8c9bfb20230 100644
--- a/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c
+++ b/drivers/net/can/kvaser_pciefd/kvaser_pciefd_core.c
@@ -902,9 +902,9 @@ static void kvaser_pciefd_bec_poll_timer(struct timer_list *data)
static const struct net_device_ops kvaser_pciefd_netdev_ops = {
.ndo_open = kvaser_pciefd_open,
.ndo_stop = kvaser_pciefd_stop,
- .ndo_eth_ioctl = can_eth_ioctl_hwts,
.ndo_start_xmit = kvaser_pciefd_start_xmit,
- .ndo_change_mtu = can_change_mtu,
+ .ndo_hwtstamp_get = can_hwtstamp_get,
+ .ndo_hwtstamp_set = can_hwtstamp_set,
};
static int kvaser_pciefd_set_phys_id(struct net_device *netdev,
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index ad4f577c1ef7..eb856547ae7d 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -23,6 +23,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include "m_can.h"
@@ -386,8 +387,8 @@ static int m_can_cccr_update_bits(struct m_can_classdev *cdev, u32 mask, u32 val
size_t tries = 10;
if (!(mask & CCCR_INIT) && !(val_before & CCCR_INIT)) {
- dev_err(cdev->dev,
- "refusing to configure device when in normal mode\n");
+ netdev_err(cdev->net,
+ "refusing to configure device when in normal mode\n");
return -EBUSY;
}
@@ -451,7 +452,7 @@ static void m_can_interrupt_enable(struct m_can_classdev *cdev, u32 interrupts)
{
if (cdev->active_interrupts == interrupts)
return;
- cdev->ops->write_reg(cdev, M_CAN_IE, interrupts);
+ m_can_write(cdev, M_CAN_IE, interrupts);
cdev->active_interrupts = interrupts;
}
@@ -469,7 +470,7 @@ static void m_can_coalescing_disable(struct m_can_classdev *cdev)
static inline void m_can_enable_all_interrupts(struct m_can_classdev *cdev)
{
if (!cdev->net->irq) {
- dev_dbg(cdev->dev, "Start hrtimer\n");
+ netdev_dbg(cdev->net, "Start hrtimer\n");
hrtimer_start(&cdev->hrtimer,
ms_to_ktime(HRTIMER_POLL_INTERVAL_MS),
HRTIMER_MODE_REL_PINNED);
@@ -485,7 +486,7 @@ static inline void m_can_disable_all_interrupts(struct m_can_classdev *cdev)
m_can_write(cdev, M_CAN_ILE, 0x0);
if (!cdev->net->irq) {
- dev_dbg(cdev->dev, "Stop hrtimer\n");
+ netdev_dbg(cdev->net, "Stop hrtimer\n");
hrtimer_try_to_cancel(&cdev->hrtimer);
}
}
@@ -790,6 +791,10 @@ static int m_can_get_berr_counter(const struct net_device *dev,
struct m_can_classdev *cdev = netdev_priv(dev);
int err;
+ /* Avoid waking up the controller if the interface is down */
+ if (!(dev->flags & IFF_UP))
+ return 0;
+
err = m_can_clk_start(cdev);
if (err)
return err;
@@ -1379,6 +1384,27 @@ static const struct can_bittiming_const m_can_data_bittiming_const_31X = {
.brp_inc = 1,
};
+static int m_can_init_ram(struct m_can_classdev *cdev)
+{
+ int end, i, start;
+ int err = 0;
+
+ /* initialize the entire Message RAM in use to avoid possible
+ * ECC/parity checksum errors when reading an uninitialized buffer
+ */
+ start = cdev->mcfg[MRAM_SIDF].off;
+ end = cdev->mcfg[MRAM_TXB].off +
+ cdev->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
+
+ for (i = start; i < end; i += 4) {
+ err = m_can_fifo_write_no_off(cdev, i, 0x0);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+
static int m_can_set_bittiming(struct net_device *dev)
{
struct m_can_classdev *cdev = netdev_priv(dev);
@@ -1464,7 +1490,7 @@ static int m_can_chip_config(struct net_device *dev)
err = m_can_init_ram(cdev);
if (err) {
- dev_err(cdev->dev, "Message RAM configuration failed\n");
+ netdev_err(dev, "Message RAM configuration failed\n");
return err;
}
@@ -1694,7 +1720,7 @@ static int m_can_niso_supported(struct m_can_classdev *cdev)
/* Then clear the it again. */
ret = m_can_cccr_update_bits(cdev, CCCR_NISO, 0);
if (ret) {
- dev_err(cdev->dev, "failed to revert the NON-ISO bit in CCCR\n");
+ netdev_err(cdev->net, "failed to revert the NON-ISO bit in CCCR\n");
return ret;
}
@@ -1713,8 +1739,8 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
m_can_version = m_can_check_core_release(cdev);
/* return if unsupported version */
if (!m_can_version) {
- dev_err(cdev->dev, "Unsupported version number: %2d",
- m_can_version);
+ netdev_err(cdev->net, "Unsupported version number: %2d",
+ m_can_version);
return -EINVAL;
}
@@ -1772,8 +1798,8 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
cdev->can.ctrlmode_supported |= CAN_CTRLMODE_FD_NON_ISO;
break;
default:
- dev_err(cdev->dev, "Unsupported version number: %2d",
- cdev->version);
+ netdev_err(cdev->net, "Unsupported version number: %2d",
+ cdev->version);
return -EINVAL;
}
@@ -1827,6 +1853,7 @@ static int m_can_close(struct net_device *dev)
close_candev(dev);
+ reset_control_assert(cdev->rst);
m_can_clk_stop(cdev);
phy_power_off(cdev->transceiver);
@@ -1950,11 +1977,6 @@ out_fail:
static void m_can_tx_submit(struct m_can_classdev *cdev)
{
- if (cdev->version == 30)
- return;
- if (!cdev->is_peripheral)
- return;
-
m_can_write(cdev, M_CAN_TXBAR, cdev->tx_peripheral_submit);
cdev->tx_peripheral_submit = 0;
}
@@ -2035,7 +2057,7 @@ static netdev_tx_t m_can_start_xmit(struct sk_buff *skb,
return ret;
}
-static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
+static enum hrtimer_restart m_can_polling_timer(struct hrtimer *timer)
{
struct m_can_classdev *cdev = container_of(timer, struct
m_can_classdev, hrtimer);
@@ -2069,11 +2091,15 @@ static int m_can_open(struct net_device *dev)
if (err)
goto out_phy_power_off;
+ err = reset_control_deassert(cdev->rst);
+ if (err)
+ goto exit_disable_clks;
+
/* open the can device */
err = open_candev(dev);
if (err) {
netdev_err(dev, "failed to open can device\n");
- goto exit_disable_clks;
+ goto out_reset_control_assert;
}
if (cdev->is_peripheral)
@@ -2129,6 +2155,8 @@ out_wq_fail:
else
napi_disable(&cdev->napi);
close_candev(dev);
+out_reset_control_assert:
+ reset_control_assert(cdev->rst);
exit_disable_clks:
m_can_clk_stop(cdev);
out_phy_power_off:
@@ -2140,7 +2168,6 @@ static const struct net_device_ops m_can_netdev_ops = {
.ndo_open = m_can_open,
.ndo_stop = m_can_close,
.ndo_start_xmit = m_can_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static int m_can_get_coalesce(struct net_device *dev,
@@ -2231,6 +2258,55 @@ static int m_can_set_coalesce(struct net_device *dev,
return 0;
}
+static void m_can_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct m_can_classdev *cdev = netdev_priv(dev);
+
+ wol->supported = device_can_wakeup(cdev->dev) ? WAKE_PHY : 0;
+ wol->wolopts = device_may_wakeup(cdev->dev) ? WAKE_PHY : 0;
+}
+
+static int m_can_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
+{
+ struct m_can_classdev *cdev = netdev_priv(dev);
+ bool wol_enable = !!(wol->wolopts & WAKE_PHY);
+ int ret;
+
+ if (wol->wolopts & ~WAKE_PHY)
+ return -EINVAL;
+
+ if (wol_enable == device_may_wakeup(cdev->dev))
+ return 0;
+
+ ret = device_set_wakeup_enable(cdev->dev, wol_enable);
+ if (ret) {
+ netdev_err(cdev->net, "Failed to set wakeup enable %pE\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ if (!IS_ERR_OR_NULL(cdev->pinctrl_state_wakeup)) {
+ if (wol_enable)
+ ret = pinctrl_select_state(cdev->pinctrl, cdev->pinctrl_state_wakeup);
+ else
+ ret = pinctrl_pm_select_default_state(cdev->dev);
+
+ if (ret) {
+ netdev_err(cdev->net, "Failed to select pinctrl state %pE\n",
+ ERR_PTR(ret));
+ goto err_wakeup_enable;
+ }
+ }
+
+ return 0;
+
+err_wakeup_enable:
+ /* Revert wakeup enable */
+ device_set_wakeup_enable(cdev->dev, !wol_enable);
+
+ return ret;
+}
+
static const struct ethtool_ops m_can_ethtool_ops_coalescing = {
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ |
ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ |
@@ -2240,10 +2316,14 @@ static const struct ethtool_ops m_can_ethtool_ops_coalescing = {
.get_ts_info = ethtool_op_get_ts_info,
.get_coalesce = m_can_get_coalesce,
.set_coalesce = m_can_set_coalesce,
+ .get_wol = m_can_get_wol,
+ .set_wol = m_can_set_wol,
};
static const struct ethtool_ops m_can_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info,
+ .get_wol = m_can_get_wol,
+ .set_wol = m_can_set_wol,
};
static int register_m_can_dev(struct m_can_classdev *cdev)
@@ -2267,8 +2347,8 @@ int m_can_check_mram_cfg(struct m_can_classdev *cdev, u32 mram_max_size)
total_size = cdev->mcfg[MRAM_TXB].off - cdev->mcfg[MRAM_SIDF].off +
cdev->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
if (total_size > mram_max_size) {
- dev_err(cdev->dev, "Total size of mram config(%u) exceeds mram(%u)\n",
- total_size, mram_max_size);
+ netdev_err(cdev->net, "Total size of mram config(%u) exceeds mram(%u)\n",
+ total_size, mram_max_size);
return -EINVAL;
}
@@ -2303,39 +2383,17 @@ static void m_can_of_parse_mram(struct m_can_classdev *cdev,
cdev->mcfg[MRAM_TXB].num = mram_config_vals[7] &
FIELD_MAX(TXBC_NDTB_MASK);
- dev_dbg(cdev->dev,
- "sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
- cdev->mcfg[MRAM_SIDF].off, cdev->mcfg[MRAM_SIDF].num,
- cdev->mcfg[MRAM_XIDF].off, cdev->mcfg[MRAM_XIDF].num,
- cdev->mcfg[MRAM_RXF0].off, cdev->mcfg[MRAM_RXF0].num,
- cdev->mcfg[MRAM_RXF1].off, cdev->mcfg[MRAM_RXF1].num,
- cdev->mcfg[MRAM_RXB].off, cdev->mcfg[MRAM_RXB].num,
- cdev->mcfg[MRAM_TXE].off, cdev->mcfg[MRAM_TXE].num,
- cdev->mcfg[MRAM_TXB].off, cdev->mcfg[MRAM_TXB].num);
+ netdev_dbg(cdev->net,
+ "sidf 0x%x %d xidf 0x%x %d rxf0 0x%x %d rxf1 0x%x %d rxb 0x%x %d txe 0x%x %d txb 0x%x %d\n",
+ cdev->mcfg[MRAM_SIDF].off, cdev->mcfg[MRAM_SIDF].num,
+ cdev->mcfg[MRAM_XIDF].off, cdev->mcfg[MRAM_XIDF].num,
+ cdev->mcfg[MRAM_RXF0].off, cdev->mcfg[MRAM_RXF0].num,
+ cdev->mcfg[MRAM_RXF1].off, cdev->mcfg[MRAM_RXF1].num,
+ cdev->mcfg[MRAM_RXB].off, cdev->mcfg[MRAM_RXB].num,
+ cdev->mcfg[MRAM_TXE].off, cdev->mcfg[MRAM_TXE].num,
+ cdev->mcfg[MRAM_TXB].off, cdev->mcfg[MRAM_TXB].num);
}
-int m_can_init_ram(struct m_can_classdev *cdev)
-{
- int end, i, start;
- int err = 0;
-
- /* initialize the entire Message RAM in use to avoid possible
- * ECC/parity checksum errors when reading an uninitialized buffer
- */
- start = cdev->mcfg[MRAM_SIDF].off;
- end = cdev->mcfg[MRAM_TXB].off +
- cdev->mcfg[MRAM_TXB].num * TXB_ELEMENT_SIZE;
-
- for (i = start; i < end; i += 4) {
- err = m_can_fifo_write_no_off(cdev, i, 0x0);
- if (err)
- break;
- }
-
- return err;
-}
-EXPORT_SYMBOL_GPL(m_can_init_ram);
-
int m_can_class_get_clocks(struct m_can_classdev *cdev)
{
int ret = 0;
@@ -2344,7 +2402,7 @@ int m_can_class_get_clocks(struct m_can_classdev *cdev)
cdev->cclk = devm_clk_get(cdev->dev, "cclk");
if (IS_ERR(cdev->hclk) || IS_ERR(cdev->cclk)) {
- dev_err(cdev->dev, "no clock found\n");
+ netdev_err(cdev->net, "no clock found\n");
ret = -ENODEV;
}
@@ -2352,6 +2410,42 @@ int m_can_class_get_clocks(struct m_can_classdev *cdev)
}
EXPORT_SYMBOL_GPL(m_can_class_get_clocks);
+static bool m_can_class_wakeup_pinctrl_enabled(struct m_can_classdev *class_dev)
+{
+ return device_may_wakeup(class_dev->dev) && class_dev->pinctrl_state_wakeup;
+}
+
+static int m_can_class_parse_pinctrl(struct m_can_classdev *class_dev)
+{
+ struct device *dev = class_dev->dev;
+ int ret;
+
+ class_dev->pinctrl = devm_pinctrl_get(dev);
+ if (IS_ERR(class_dev->pinctrl)) {
+ ret = PTR_ERR(class_dev->pinctrl);
+ class_dev->pinctrl = NULL;
+
+ if (ret == -ENODEV)
+ return 0;
+
+ return dev_err_probe(dev, ret, "Failed to get pinctrl\n");
+ }
+
+ class_dev->pinctrl_state_wakeup =
+ pinctrl_lookup_state(class_dev->pinctrl, "wakeup");
+ if (IS_ERR(class_dev->pinctrl_state_wakeup)) {
+ ret = PTR_ERR(class_dev->pinctrl_state_wakeup);
+ class_dev->pinctrl_state_wakeup = NULL;
+
+ if (ret == -ENODEV)
+ return 0;
+
+ return dev_err_probe(dev, ret, "Failed to lookup pinctrl wakeup state\n");
+ }
+
+ return 0;
+}
+
struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,
int sizeof_priv)
{
@@ -2367,9 +2461,12 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,
sizeof(mram_config_vals) / 4);
if (ret) {
dev_err(dev, "Could not get Message RAM configuration.");
- goto out;
+ return ERR_PTR(ret);
}
+ if (dev->of_node && of_property_read_bool(dev->of_node, "wakeup-source"))
+ device_set_wakeup_capable(dev, true);
+
/* Get TX FIFO size
* Defines the total amount of echo buffers for loopback
*/
@@ -2379,7 +2476,7 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,
net_dev = alloc_candev(sizeof_priv, tx_fifo_size);
if (!net_dev) {
dev_err(dev, "Failed to allocate CAN device");
- goto out;
+ return ERR_PTR(-ENOMEM);
}
class_dev = netdev_priv(net_dev);
@@ -2389,8 +2486,16 @@ struct m_can_classdev *m_can_class_allocate_dev(struct device *dev,
m_can_of_parse_mram(class_dev, mram_config_vals);
spin_lock_init(&class_dev->tx_handling_spinlock);
-out:
+
+ ret = m_can_class_parse_pinctrl(class_dev);
+ if (ret)
+ goto err_free_candev;
+
return class_dev;
+
+err_free_candev:
+ free_candev(net_dev);
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(m_can_class_allocate_dev);
@@ -2411,26 +2516,33 @@ int m_can_class_register(struct m_can_classdev *cdev)
devm_kzalloc(cdev->dev,
cdev->tx_fifo_size * sizeof(*cdev->tx_ops),
GFP_KERNEL);
- if (!cdev->tx_ops) {
- dev_err(cdev->dev, "Failed to allocate tx_ops for workqueue\n");
+ if (!cdev->tx_ops)
return -ENOMEM;
- }
}
+ cdev->rst = devm_reset_control_get_optional_shared(cdev->dev, NULL);
+ if (IS_ERR(cdev->rst))
+ return dev_err_probe(cdev->dev, PTR_ERR(cdev->rst),
+ "Failed to get reset line\n");
+
ret = m_can_clk_start(cdev);
if (ret)
return ret;
+ ret = reset_control_deassert(cdev->rst);
+ if (ret)
+ goto clk_disable;
+
if (cdev->is_peripheral) {
ret = can_rx_offload_add_manual(cdev->net, &cdev->offload,
NAPI_POLL_WEIGHT);
if (ret)
- goto clk_disable;
+ goto out_reset_control_assert;
}
if (!cdev->net->irq) {
- dev_dbg(cdev->dev, "Polling enabled, initialize hrtimer");
- hrtimer_setup(&cdev->hrtimer, &hrtimer_callback, CLOCK_MONOTONIC,
+ netdev_dbg(cdev->net, "Polling enabled, initialize hrtimer");
+ hrtimer_setup(&cdev->hrtimer, m_can_polling_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL_PINNED);
} else {
hrtimer_setup(&cdev->hrtimer, m_can_coalescing_timer, CLOCK_MONOTONIC,
@@ -2443,19 +2555,21 @@ int m_can_class_register(struct m_can_classdev *cdev)
ret = register_m_can_dev(cdev);
if (ret) {
- dev_err(cdev->dev, "registering %s failed (err=%d)\n",
- cdev->net->name, ret);
+ netdev_err(cdev->net, "registering %s failed (err=%d)\n",
+ cdev->net->name, ret);
goto rx_offload_del;
}
of_can_transceiver(cdev->net);
- dev_info(cdev->dev, "%s device registered (irq=%d, version=%d)\n",
- KBUILD_MODNAME, cdev->net->irq, cdev->version);
+ netdev_info(cdev->net, "device registered (irq=%d, version=%d)\n",
+ cdev->net->irq, cdev->version);
/* Probe finished
- * Stop clocks. They will be reactivated once the M_CAN device is opened
+ * Assert reset and stop clocks.
+ * They will be reactivated once the M_CAN device is opened
*/
+ reset_control_assert(cdev->rst);
m_can_clk_stop(cdev);
return 0;
@@ -2463,6 +2577,8 @@ int m_can_class_register(struct m_can_classdev *cdev)
rx_offload_del:
if (cdev->is_peripheral)
can_rx_offload_del(&cdev->offload);
+out_reset_control_assert:
+ reset_control_assert(cdev->rst);
clk_disable:
m_can_clk_stop(cdev);
@@ -2506,7 +2622,8 @@ int m_can_class_suspend(struct device *dev)
cdev->can.state = CAN_STATE_SLEEPING;
}
- pinctrl_pm_select_sleep_state(dev);
+ if (!m_can_class_wakeup_pinctrl_enabled(cdev))
+ pinctrl_pm_select_sleep_state(dev);
return ret;
}
@@ -2518,7 +2635,8 @@ int m_can_class_resume(struct device *dev)
struct net_device *ndev = cdev->net;
int ret = 0;
- pinctrl_pm_select_default_state(dev);
+ if (!m_can_class_wakeup_pinctrl_enabled(cdev))
+ pinctrl_pm_select_default_state(dev);
if (netif_running(ndev)) {
ret = m_can_clk_start(cdev);
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index bd4746c63af3..4743342b2fba 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -86,6 +86,7 @@ struct m_can_classdev {
struct device *dev;
struct clk *hclk;
struct clk *cclk;
+ struct reset_control *rst;
struct workqueue_struct *tx_wq;
struct phy *transceiver;
@@ -128,6 +129,9 @@ struct m_can_classdev {
struct mram_cfg mcfg[MRAM_CFG_NUM];
struct hrtimer hrtimer;
+
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pinctrl_state_wakeup;
};
struct m_can_classdev *m_can_class_allocate_dev(struct device *dev, int sizeof_priv);
@@ -135,7 +139,6 @@ void m_can_class_free_dev(struct net_device *net);
int m_can_class_register(struct m_can_classdev *cdev);
void m_can_class_unregister(struct m_can_classdev *cdev);
int m_can_class_get_clocks(struct m_can_classdev *cdev);
-int m_can_init_ram(struct m_can_classdev *priv);
int m_can_check_mram_cfg(struct m_can_classdev *cdev, u32 mram_max_size);
int m_can_class_suspend(struct device *dev);
diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c
index 9ad7419f88f8..eb31ed1f9644 100644
--- a/drivers/net/can/m_can/m_can_pci.c
+++ b/drivers/net/can/m_can/m_can_pci.c
@@ -111,8 +111,8 @@ static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
mcan_class = m_can_class_allocate_dev(&pci->dev,
sizeof(struct m_can_pci_priv));
- if (!mcan_class)
- return -ENOMEM;
+ if (IS_ERR(mcan_class))
+ return PTR_ERR(mcan_class);
priv = cdev_to_priv(mcan_class);
diff --git a/drivers/net/can/m_can/m_can_platform.c b/drivers/net/can/m_can/m_can_platform.c
index 4a412add2b8d..56da411878af 100644
--- a/drivers/net/can/m_can/m_can_platform.c
+++ b/drivers/net/can/m_can/m_can_platform.c
@@ -87,8 +87,8 @@ static int m_can_plat_probe(struct platform_device *pdev)
mcan_class = m_can_class_allocate_dev(&pdev->dev,
sizeof(struct m_can_plat_priv));
- if (!mcan_class)
- return -ENOMEM;
+ if (IS_ERR(mcan_class))
+ return PTR_ERR(mcan_class);
priv = cdev_to_priv(mcan_class);
diff --git a/drivers/net/can/m_can/tcan4x5x-core.c b/drivers/net/can/m_can/tcan4x5x-core.c
index 39b0b5277b11..31cc9d0abd45 100644
--- a/drivers/net/can/m_can/tcan4x5x-core.c
+++ b/drivers/net/can/m_can/tcan4x5x-core.c
@@ -416,8 +416,8 @@ static int tcan4x5x_can_probe(struct spi_device *spi)
mcan_class = m_can_class_allocate_dev(&spi->dev,
sizeof(struct tcan4x5x_priv));
- if (!mcan_class)
- return -ENOMEM;
+ if (IS_ERR(mcan_class))
+ return PTR_ERR(mcan_class);
ret = m_can_check_mram_cfg(mcan_class, TCAN4X5X_MRAM_SIZE);
if (ret)
diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c
index 8c2a7bc64d3d..39c7aa2a0b2f 100644
--- a/drivers/net/can/mscan/mscan.c
+++ b/drivers/net/can/mscan/mscan.c
@@ -607,7 +607,6 @@ static const struct net_device_ops mscan_netdev_ops = {
.ndo_open = mscan_open,
.ndo_stop = mscan_close,
.ndo_start_xmit = mscan_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops mscan_ethtool_ops = {
diff --git a/drivers/net/can/peak_canfd/peak_canfd.c b/drivers/net/can/peak_canfd/peak_canfd.c
index b5bc80ac7876..06cb2629f66a 100644
--- a/drivers/net/can/peak_canfd/peak_canfd.c
+++ b/drivers/net/can/peak_canfd/peak_canfd.c
@@ -743,37 +743,33 @@ static netdev_tx_t peak_canfd_start_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
-static int peak_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+static int peak_eth_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
- struct hwtstamp_config hwts_cfg = { 0 };
+ config->tx_type = HWTSTAMP_TX_OFF;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
- switch (cmd) {
- case SIOCSHWTSTAMP: /* set */
- if (copy_from_user(&hwts_cfg, ifr->ifr_data, sizeof(hwts_cfg)))
- return -EFAULT;
- if (hwts_cfg.tx_type == HWTSTAMP_TX_OFF &&
- hwts_cfg.rx_filter == HWTSTAMP_FILTER_ALL)
- return 0;
- return -ERANGE;
+ return 0;
+}
- case SIOCGHWTSTAMP: /* get */
- hwts_cfg.tx_type = HWTSTAMP_TX_OFF;
- hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL;
- if (copy_to_user(ifr->ifr_data, &hwts_cfg, sizeof(hwts_cfg)))
- return -EFAULT;
+static int peak_eth_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ if (config->tx_type == HWTSTAMP_TX_OFF &&
+ config->rx_filter == HWTSTAMP_FILTER_ALL)
return 0;
- default:
- return -EOPNOTSUPP;
- }
+ NL_SET_ERR_MSG_MOD(extack, "Only RX HWTSTAMP_FILTER_ALL is supported");
+ return -ERANGE;
}
static const struct net_device_ops peak_canfd_netdev_ops = {
.ndo_open = peak_canfd_open,
.ndo_stop = peak_canfd_close,
- .ndo_eth_ioctl = peak_eth_ioctl,
.ndo_start_xmit = peak_canfd_start_xmit,
- .ndo_change_mtu = can_change_mtu,
+ .ndo_hwtstamp_get = peak_eth_hwtstamp_get,
+ .ndo_hwtstamp_set = peak_eth_hwtstamp_set,
};
static int peak_get_ts_info(struct net_device *dev,
diff --git a/drivers/net/can/rcar/rcar_can.c b/drivers/net/can/rcar/rcar_can.c
index 5f85f4e27205..fc3df328e877 100644
--- a/drivers/net/can/rcar/rcar_can.c
+++ b/drivers/net/can/rcar/rcar_can.c
@@ -635,7 +635,6 @@ static const struct net_device_ops rcar_can_netdev_ops = {
.ndo_open = rcar_can_open,
.ndo_stop = rcar_can_close,
.ndo_start_xmit = rcar_can_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops rcar_can_ethtool_ops = {
diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c
index 45d36adb51b7..7895e1fdea1c 100644
--- a/drivers/net/can/rcar/rcar_canfd.c
+++ b/drivers/net/can/rcar/rcar_canfd.c
@@ -468,6 +468,7 @@ struct rcar_canfd_global {
struct platform_device *pdev; /* Respective platform device */
struct clk *clkp; /* Peripheral clock */
struct clk *can_clk; /* fCAN clock */
+ struct clk *clk_ram; /* Clock RAM */
unsigned long channels_mask; /* Enabled channels mask */
bool extclk; /* CANFD or Ext clock */
bool fdmode; /* CAN FD or Classical CAN only mode */
@@ -709,6 +710,11 @@ static void rcar_canfd_set_bit_reg(void __iomem *addr, u32 val)
rcar_canfd_update(val, val, addr);
}
+static void rcar_canfd_clear_bit_reg(void __iomem *addr, u32 val)
+{
+ rcar_canfd_update(val, 0, addr);
+}
+
static void rcar_canfd_update_bit_reg(void __iomem *addr, u32 mask, u32 val)
{
rcar_canfd_update(mask, val, addr);
@@ -755,25 +761,6 @@ static void rcar_canfd_set_rnc(struct rcar_canfd_global *gpriv, unsigned int ch,
rcar_canfd_set_bit(gpriv->base, RCANFD_GAFLCFG(w), rnc);
}
-static void rcar_canfd_set_mode(struct rcar_canfd_global *gpriv)
-{
- if (gpriv->info->ch_interface_mode) {
- u32 ch, val = gpriv->fdmode ? RCANFD_GEN4_FDCFG_FDOE
- : RCANFD_GEN4_FDCFG_CLOE;
-
- for_each_set_bit(ch, &gpriv->channels_mask,
- gpriv->info->max_channels)
- rcar_canfd_set_bit_reg(&gpriv->fcbase[ch].cfdcfg, val);
- } else {
- if (gpriv->fdmode)
- rcar_canfd_set_bit(gpriv->base, RCANFD_GRMCFG,
- RCANFD_GRMCFG_RCMC);
- else
- rcar_canfd_clear_bit(gpriv->base, RCANFD_GRMCFG,
- RCANFD_GRMCFG_RCMC);
- }
-}
-
static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv)
{
struct device *dev = &gpriv->pdev->dev;
@@ -806,6 +793,16 @@ static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv)
/* Reset Global error flags */
rcar_canfd_write(gpriv->base, RCANFD_GERFL, 0x0);
+ /* Set the controller into appropriate mode */
+ if (!gpriv->info->ch_interface_mode) {
+ if (gpriv->fdmode)
+ rcar_canfd_set_bit(gpriv->base, RCANFD_GRMCFG,
+ RCANFD_GRMCFG_RCMC);
+ else
+ rcar_canfd_clear_bit(gpriv->base, RCANFD_GRMCFG,
+ RCANFD_GRMCFG_RCMC);
+ }
+
/* Transition all Channels to reset mode */
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
rcar_canfd_clear_bit(gpriv->base,
@@ -823,10 +820,23 @@ static int rcar_canfd_reset_controller(struct rcar_canfd_global *gpriv)
dev_dbg(dev, "channel %u reset failed\n", ch);
return err;
}
- }
- /* Set the controller into appropriate mode */
- rcar_canfd_set_mode(gpriv);
+ /* Set the controller into appropriate mode */
+ if (gpriv->info->ch_interface_mode) {
+ /* Do not set CLOE and FDOE simultaneously */
+ if (!gpriv->fdmode) {
+ rcar_canfd_clear_bit_reg(&gpriv->fcbase[ch].cfdcfg,
+ RCANFD_GEN4_FDCFG_FDOE);
+ rcar_canfd_set_bit_reg(&gpriv->fcbase[ch].cfdcfg,
+ RCANFD_GEN4_FDCFG_CLOE);
+ } else {
+ rcar_canfd_clear_bit_reg(&gpriv->fcbase[ch].cfdcfg,
+ RCANFD_GEN4_FDCFG_FDOE);
+ rcar_canfd_clear_bit_reg(&gpriv->fcbase[ch].cfdcfg,
+ RCANFD_GEN4_FDCFG_CLOE);
+ }
+ }
+ }
return 0;
}
@@ -1569,8 +1579,8 @@ static int rcar_canfd_close(struct net_device *ndev)
netif_stop_queue(ndev);
rcar_canfd_stop(ndev);
napi_disable(&priv->napi);
- clk_disable_unprepare(gpriv->can_clk);
close_candev(ndev);
+ clk_disable_unprepare(gpriv->can_clk);
phy_power_off(priv->transceiver);
return 0;
}
@@ -1818,7 +1828,6 @@ static const struct net_device_ops rcar_canfd_netdev_ops = {
.ndo_open = rcar_canfd_open,
.ndo_stop = rcar_canfd_close,
.ndo_start_xmit = rcar_canfd_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops rcar_canfd_ethtool_ops = {
@@ -1961,22 +1970,120 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch)
}
}
+static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv)
+{
+ struct device *dev = &gpriv->pdev->dev;
+ u32 rule_entry = 0;
+ u32 ch, sts;
+ int err;
+
+ err = reset_control_reset(gpriv->rstc1);
+ if (err)
+ return err;
+
+ err = reset_control_reset(gpriv->rstc2);
+ if (err)
+ goto fail_reset1;
+
+ /* Enable peripheral clock for register access */
+ err = clk_prepare_enable(gpriv->clkp);
+ if (err) {
+ dev_err(dev, "failed to enable peripheral clock: %pe\n",
+ ERR_PTR(err));
+ goto fail_reset2;
+ }
+
+ /* Enable RAM clock */
+ err = clk_prepare_enable(gpriv->clk_ram);
+ if (err) {
+ dev_err(dev,
+ "failed to enable RAM clock, error %d\n", err);
+ goto fail_clk;
+ }
+
+ err = rcar_canfd_reset_controller(gpriv);
+ if (err) {
+ dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
+ goto fail_ram_clk;
+ }
+
+ /* Controller in Global reset & Channel reset mode */
+ rcar_canfd_configure_controller(gpriv);
+
+ /* Configure per channel attributes */
+ for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
+ /* Configure Channel's Rx fifo */
+ rcar_canfd_configure_rx(gpriv, ch);
+
+ /* Configure Channel's Tx (Common) fifo */
+ rcar_canfd_configure_tx(gpriv, ch);
+
+ /* Configure receive rules */
+ rcar_canfd_configure_afl_rules(gpriv, ch, rule_entry);
+ rule_entry += RCANFD_CHANNEL_NUMRULES;
+ }
+
+ /* Configure common interrupts */
+ rcar_canfd_enable_global_interrupts(gpriv);
+
+ /* Start Global operation mode */
+ rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GMDC_MASK,
+ RCANFD_GCTR_GMDC_GOPM);
+
+ /* Verify mode change */
+ err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts,
+ !(sts & RCANFD_GSTS_GNOPM), 2, 500000);
+ if (err) {
+ dev_err(dev, "global operational mode failed\n");
+ goto fail_mode;
+ }
+
+ return 0;
+
+fail_mode:
+ rcar_canfd_disable_global_interrupts(gpriv);
+fail_ram_clk:
+ clk_disable_unprepare(gpriv->clk_ram);
+fail_clk:
+ clk_disable_unprepare(gpriv->clkp);
+fail_reset2:
+ reset_control_assert(gpriv->rstc2);
+fail_reset1:
+ reset_control_assert(gpriv->rstc1);
+ return err;
+}
+
+static void rcar_canfd_global_deinit(struct rcar_canfd_global *gpriv, bool full)
+{
+ rcar_canfd_disable_global_interrupts(gpriv);
+
+ if (full) {
+ rcar_canfd_reset_controller(gpriv);
+
+ /* Enter global sleep mode */
+ rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
+ }
+
+ clk_disable_unprepare(gpriv->clk_ram);
+ clk_disable_unprepare(gpriv->clkp);
+ reset_control_assert(gpriv->rstc2);
+ reset_control_assert(gpriv->rstc1);
+}
+
static int rcar_canfd_probe(struct platform_device *pdev)
{
struct phy *transceivers[RCANFD_NUM_CHANNELS] = { NULL, };
const struct rcar_canfd_hw_info *info;
struct device *dev = &pdev->dev;
void __iomem *addr;
- u32 sts, ch, fcan_freq;
struct rcar_canfd_global *gpriv;
struct device_node *of_child;
unsigned long channels_mask = 0;
int err, ch_irq, g_irq;
int g_err_irq, g_recc_irq;
- u32 rule_entry = 0;
bool fdmode = true; /* CAN FD only mode - default */
char name[9] = "channelX";
- struct clk *clk_ram;
+ u32 ch, fcan_freq;
int i;
info = of_device_get_match_data(dev);
@@ -2066,10 +2173,10 @@ static int rcar_canfd_probe(struct platform_device *pdev)
gpriv->extclk = gpriv->info->external_clk;
}
- clk_ram = devm_clk_get_optional_enabled(dev, "ram_clk");
- if (IS_ERR(clk_ram))
- return dev_err_probe(dev, PTR_ERR(clk_ram),
- "cannot get enabled ram clock\n");
+ gpriv->clk_ram = devm_clk_get_optional(dev, "ram_clk");
+ if (IS_ERR(gpriv->clk_ram))
+ return dev_err_probe(dev, PTR_ERR(gpriv->clk_ram),
+ "cannot get ram clock\n");
addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(addr)) {
@@ -2118,59 +2225,9 @@ static int rcar_canfd_probe(struct platform_device *pdev)
}
}
- err = reset_control_reset(gpriv->rstc1);
+ err = rcar_canfd_global_init(gpriv);
if (err)
- goto fail_dev;
- err = reset_control_reset(gpriv->rstc2);
- if (err) {
- reset_control_assert(gpriv->rstc1);
- goto fail_dev;
- }
-
- /* Enable peripheral clock for register access */
- err = clk_prepare_enable(gpriv->clkp);
- if (err) {
- dev_err(dev, "failed to enable peripheral clock: %pe\n",
- ERR_PTR(err));
- goto fail_reset;
- }
-
- err = rcar_canfd_reset_controller(gpriv);
- if (err) {
- dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err));
- goto fail_clk;
- }
-
- /* Controller in Global reset & Channel reset mode */
- rcar_canfd_configure_controller(gpriv);
-
- /* Configure per channel attributes */
- for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
- /* Configure Channel's Rx fifo */
- rcar_canfd_configure_rx(gpriv, ch);
-
- /* Configure Channel's Tx (Common) fifo */
- rcar_canfd_configure_tx(gpriv, ch);
-
- /* Configure receive rules */
- rcar_canfd_configure_afl_rules(gpriv, ch, rule_entry);
- rule_entry += RCANFD_CHANNEL_NUMRULES;
- }
-
- /* Configure common interrupts */
- rcar_canfd_enable_global_interrupts(gpriv);
-
- /* Start Global operation mode */
- rcar_canfd_update_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GMDC_MASK,
- RCANFD_GCTR_GMDC_GOPM);
-
- /* Verify mode change */
- err = readl_poll_timeout((gpriv->base + RCANFD_GSTS), sts,
- !(sts & RCANFD_GSTS_GNOPM), 2, 500000);
- if (err) {
- dev_err(dev, "global operational mode failed\n");
goto fail_mode;
- }
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels) {
err = rcar_canfd_channel_probe(gpriv, ch, fcan_freq,
@@ -2189,12 +2246,7 @@ fail_channel:
for_each_set_bit(ch, &gpriv->channels_mask, info->max_channels)
rcar_canfd_channel_remove(gpriv, ch);
fail_mode:
- rcar_canfd_disable_global_interrupts(gpriv);
-fail_clk:
- clk_disable_unprepare(gpriv->clkp);
-fail_reset:
- reset_control_assert(gpriv->rstc1);
- reset_control_assert(gpriv->rstc2);
+ rcar_canfd_global_deinit(gpriv, false);
fail_dev:
return err;
}
@@ -2204,33 +2256,79 @@ static void rcar_canfd_remove(struct platform_device *pdev)
struct rcar_canfd_global *gpriv = platform_get_drvdata(pdev);
u32 ch;
- rcar_canfd_reset_controller(gpriv);
- rcar_canfd_disable_global_interrupts(gpriv);
-
for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
rcar_canfd_disable_channel_interrupts(gpriv->ch[ch]);
rcar_canfd_channel_remove(gpriv, ch);
}
- /* Enter global sleep mode */
- rcar_canfd_set_bit(gpriv->base, RCANFD_GCTR, RCANFD_GCTR_GSLPR);
- clk_disable_unprepare(gpriv->clkp);
- reset_control_assert(gpriv->rstc1);
- reset_control_assert(gpriv->rstc2);
+ rcar_canfd_global_deinit(gpriv, true);
}
-static int __maybe_unused rcar_canfd_suspend(struct device *dev)
+static int rcar_canfd_suspend(struct device *dev)
{
+ struct rcar_canfd_global *gpriv = dev_get_drvdata(dev);
+ int err;
+ u32 ch;
+
+ for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
+ struct rcar_canfd_channel *priv = gpriv->ch[ch];
+ struct net_device *ndev = priv->ndev;
+
+ if (!netif_running(ndev))
+ continue;
+
+ netif_device_detach(ndev);
+
+ err = rcar_canfd_close(ndev);
+ if (err) {
+ netdev_err(ndev, "rcar_canfd_close() failed %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ priv->can.state = CAN_STATE_SLEEPING;
+ }
+
+ /* TODO Skip if wake-up (which is not yet supported) is enabled */
+ rcar_canfd_global_deinit(gpriv, false);
+
return 0;
}
-static int __maybe_unused rcar_canfd_resume(struct device *dev)
+static int rcar_canfd_resume(struct device *dev)
{
+ struct rcar_canfd_global *gpriv = dev_get_drvdata(dev);
+ int err;
+ u32 ch;
+
+ err = rcar_canfd_global_init(gpriv);
+ if (err) {
+ dev_err(dev, "rcar_canfd_global_init() failed %pe\n", ERR_PTR(err));
+ return err;
+ }
+
+ for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) {
+ struct rcar_canfd_channel *priv = gpriv->ch[ch];
+ struct net_device *ndev = priv->ndev;
+
+ if (!netif_running(ndev))
+ continue;
+
+ err = rcar_canfd_open(ndev);
+ if (err) {
+ netdev_err(ndev, "rcar_canfd_open() failed %pe\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ netif_device_attach(ndev);
+ }
+
return 0;
}
-static SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
- rcar_canfd_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(rcar_canfd_pm_ops, rcar_canfd_suspend,
+ rcar_canfd_resume);
static const __maybe_unused struct of_device_id rcar_canfd_of_table[] = {
{ .compatible = "renesas,r8a779a0-canfd", .data = &rcar_gen4_hw_info },
@@ -2247,7 +2345,7 @@ static struct platform_driver rcar_canfd_driver = {
.driver = {
.name = RCANFD_DRV_NAME,
.of_match_table = of_match_ptr(rcar_canfd_of_table),
- .pm = &rcar_canfd_pm_ops,
+ .pm = pm_sleep_ptr(&rcar_canfd_pm_ops),
},
.probe = rcar_canfd_probe,
.remove = rcar_canfd_remove,
diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c
index 046f0a0ae4d4..29de0c01e4ed 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-core.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-core.c
@@ -761,7 +761,6 @@ static const struct net_device_ops rkcanfd_netdev_ops = {
.ndo_open = rkcanfd_open,
.ndo_stop = rkcanfd_stop,
.ndo_start_xmit = rkcanfd_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static int __maybe_unused rkcanfd_runtime_suspend(struct device *dev)
diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c
index 865a15e033a9..12200dcfd338 100644
--- a/drivers/net/can/rockchip/rockchip_canfd-tx.c
+++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c
@@ -72,7 +72,7 @@ netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
int err;
u8 i;
- if (can_dropped_invalid_skb(ndev, skb))
+ if (can_dev_dropped_skb(ndev, skb))
return NETDEV_TX_OK;
if (!netif_subqueue_maybe_stop(priv->ndev, 0,
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
index 4d245857ef1c..a8fa0d6516b9 100644
--- a/drivers/net/can/sja1000/sja1000.c
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -548,8 +548,8 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
if (priv->read_reg(priv, SJA1000_IER) == IRQ_OFF)
goto out;
- while ((isrc = priv->read_reg(priv, SJA1000_IR)) &&
- (n < SJA1000_MAX_IRQ)) {
+ while ((n < SJA1000_MAX_IRQ) &&
+ (isrc = priv->read_reg(priv, SJA1000_IR))) {
status = priv->read_reg(priv, SJA1000_SR);
/* check for absent controller due to hw unplug */
@@ -697,7 +697,6 @@ static const struct net_device_ops sja1000_netdev_ops = {
.ndo_open = sja1000_open,
.ndo_stop = sja1000_close,
.ndo_start_xmit = sja1000_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops sja1000_ethtool_ops = {
diff --git a/drivers/net/can/slcan/slcan-core.c b/drivers/net/can/slcan/slcan-core.c
index 58ff2ec1d975..cd789e178d34 100644
--- a/drivers/net/can/slcan/slcan-core.c
+++ b/drivers/net/can/slcan/slcan-core.c
@@ -774,7 +774,6 @@ static const struct net_device_ops slcan_netdev_ops = {
.ndo_open = slcan_netdev_open,
.ndo_stop = slcan_netdev_close,
.ndo_start_xmit = slcan_netdev_xmit,
- .ndo_change_mtu = can_change_mtu,
};
/******************************************
diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c
index 278ee8722770..79bc64395ac4 100644
--- a/drivers/net/can/softing/softing_main.c
+++ b/drivers/net/can/softing/softing_main.c
@@ -609,7 +609,6 @@ static const struct net_device_ops softing_netdev_ops = {
.ndo_open = softing_netdev_open,
.ndo_stop = softing_netdev_stop,
.ndo_start_xmit = softing_netdev_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops softing_ethtool_ops = {
diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c
index 6d4b643e135f..e00d3dbc4cf4 100644
--- a/drivers/net/can/spi/hi311x.c
+++ b/drivers/net/can/spi/hi311x.c
@@ -799,7 +799,6 @@ static const struct net_device_ops hi3110_netdev_ops = {
.ndo_open = hi3110_open,
.ndo_stop = hi3110_stop,
.ndo_start_xmit = hi3110_hard_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops hi3110_ethtool_ops = {
diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c
index b797e08499d7..fa97adf25b73 100644
--- a/drivers/net/can/spi/mcp251x.c
+++ b/drivers/net/can/spi/mcp251x.c
@@ -1270,7 +1270,6 @@ static const struct net_device_ops mcp251x_netdev_ops = {
.ndo_open = mcp251x_open,
.ndo_stop = mcp251x_stop,
.ndo_start_xmit = mcp251x_hard_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops mcp251x_ethtool_ops = {
@@ -1321,7 +1320,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
clk = devm_clk_get_optional(&spi->dev, NULL);
if (IS_ERR(clk))
- return PTR_ERR(clk);
+ return dev_err_probe(&spi->dev, PTR_ERR(clk), "Cannot get clock\n");
freq = clk_get_rate(clk);
if (freq == 0)
@@ -1329,7 +1328,7 @@ static int mcp251x_can_probe(struct spi_device *spi)
/* Sanity check */
if (freq < 1000000 || freq > 25000000)
- return -ERANGE;
+ return dev_err_probe(&spi->dev, -ERANGE, "clock frequency out of range\n");
/* Allocate can/net device */
net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX);
@@ -1337,8 +1336,10 @@ static int mcp251x_can_probe(struct spi_device *spi)
return -ENOMEM;
ret = clk_prepare_enable(clk);
- if (ret)
+ if (ret) {
+ dev_err_probe(&spi->dev, ret, "Cannot enable clock\n");
goto out_free;
+ }
net->netdev_ops = &mcp251x_netdev_ops;
net->ethtool_ops = &mcp251x_ethtool_ops;
@@ -1363,20 +1364,25 @@ static int mcp251x_can_probe(struct spi_device *spi)
else
spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000;
ret = spi_setup(spi);
- if (ret)
+ if (ret) {
+ dev_err_probe(&spi->dev, ret, "Cannot set up spi\n");
goto out_clk;
+ }
priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver");
if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
(PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) {
ret = -EPROBE_DEFER;
+ dev_err_probe(&spi->dev, ret, "supply deferred\n");
goto out_clk;
}
ret = mcp251x_power_enable(priv->power, 1);
- if (ret)
+ if (ret) {
+ dev_err_probe(&spi->dev, ret, "Cannot enable power\n");
goto out_clk;
+ }
priv->wq = alloc_workqueue("mcp251x_wq",
WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
@@ -1410,21 +1416,24 @@ static int mcp251x_can_probe(struct spi_device *spi)
/* Here is OK to not lock the MCP, no one knows about it yet */
ret = mcp251x_hw_probe(spi);
if (ret) {
- if (ret == -ENODEV)
- dev_err(&spi->dev, "Cannot initialize MCP%x. Wrong wiring?\n",
- priv->model);
+ dev_err_probe(&spi->dev, ret, "Cannot initialize MCP%x. Wrong wiring?\n",
+ priv->model);
goto error_probe;
}
mcp251x_hw_sleep(spi);
ret = register_candev(net);
- if (ret)
+ if (ret) {
+ dev_err_probe(&spi->dev, ret, "Cannot register CAN device\n");
goto error_probe;
+ }
ret = mcp251x_gpio_setup(priv);
- if (ret)
+ if (ret) {
+ dev_err_probe(&spi->dev, ret, "Cannot set up gpios\n");
goto out_unregister_candev;
+ }
netdev_info(net, "MCP%x successfully initialized.\n", priv->model);
return 0;
@@ -1443,7 +1452,6 @@ out_clk:
out_free:
free_candev(net);
- dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
return ret;
}
diff --git a/drivers/net/can/spi/mcp251xfd/Kconfig b/drivers/net/can/spi/mcp251xfd/Kconfig
index 877e4356010d..7c29846e6051 100644
--- a/drivers/net/can/spi/mcp251xfd/Kconfig
+++ b/drivers/net/can/spi/mcp251xfd/Kconfig
@@ -5,6 +5,7 @@ config CAN_MCP251XFD
select CAN_RX_OFFLOAD
select REGMAP
select WANT_DEV_COREDUMP
+ select GPIOLIB
help
Driver for the Microchip MCP251XFD SPI FD-CAN controller
family.
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
index 7450ea42c1ea..5134ebb85880 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c
@@ -608,23 +608,21 @@ static int mcp251xfd_set_bittiming(const struct mcp251xfd_priv *priv)
static int mcp251xfd_chip_rx_int_enable(const struct mcp251xfd_priv *priv)
{
- u32 val;
+ u32 val, mask;
if (!priv->rx_int)
return 0;
- /* Configure GPIOs:
- * - PIN0: GPIO Input
- * - PIN1: GPIO Input/RX Interrupt
+ /* Configure PIN1 as RX Interrupt:
*
* PIN1 must be Input, otherwise there is a glitch on the
* rx-INT line. It happens between setting the PIN as output
* (in the first byte of the SPI transfer) and configuring the
* PIN as interrupt (in the last byte of the SPI transfer).
*/
- val = MCP251XFD_REG_IOCON_PM0 | MCP251XFD_REG_IOCON_TRIS1 |
- MCP251XFD_REG_IOCON_TRIS0;
- return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val);
+ val = MCP251XFD_REG_IOCON_TRIS(1);
+ mask = MCP251XFD_REG_IOCON_TRIS(1) | MCP251XFD_REG_IOCON_PM(1);
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, mask, val);
}
static int mcp251xfd_chip_rx_int_disable(const struct mcp251xfd_priv *priv)
@@ -634,13 +632,9 @@ static int mcp251xfd_chip_rx_int_disable(const struct mcp251xfd_priv *priv)
if (!priv->rx_int)
return 0;
- /* Configure GPIOs:
- * - PIN0: GPIO Input
- * - PIN1: GPIO Input
- */
- val = MCP251XFD_REG_IOCON_PM1 | MCP251XFD_REG_IOCON_PM0 |
- MCP251XFD_REG_IOCON_TRIS1 | MCP251XFD_REG_IOCON_TRIS0;
- return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val);
+ /* Configure PIN1 as GPIO Input */
+ val = MCP251XFD_REG_IOCON_PM(1) | MCP251XFD_REG_IOCON_TRIS(1);
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val, val);
}
static int mcp251xfd_chip_ecc_init(struct mcp251xfd_priv *priv)
@@ -767,21 +761,13 @@ static void mcp251xfd_chip_stop(struct mcp251xfd_priv *priv,
mcp251xfd_chip_interrupts_disable(priv);
mcp251xfd_chip_rx_int_disable(priv);
mcp251xfd_timestamp_stop(priv);
- mcp251xfd_chip_sleep(priv);
+ mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG);
}
static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv)
{
int err;
- err = mcp251xfd_chip_softreset(priv);
- if (err)
- goto out_chip_stop;
-
- err = mcp251xfd_chip_clock_init(priv);
- if (err)
- goto out_chip_stop;
-
err = mcp251xfd_chip_timestamp_init(priv);
if (err)
goto out_chip_stop;
@@ -1625,8 +1611,11 @@ static int mcp251xfd_open(struct net_device *ndev)
return err;
err = pm_runtime_resume_and_get(ndev->dev.parent);
- if (err)
+ if (err) {
+ if (err == -ETIMEDOUT || err == -ENODEV)
+ pm_runtime_set_suspended(ndev->dev.parent);
goto out_close_candev;
+ }
err = mcp251xfd_ring_alloc(priv);
if (err)
@@ -1714,8 +1703,8 @@ static const struct net_device_ops mcp251xfd_netdev_ops = {
.ndo_open = mcp251xfd_open,
.ndo_stop = mcp251xfd_stop,
.ndo_start_xmit = mcp251xfd_start_xmit,
- .ndo_eth_ioctl = can_eth_ioctl_hwts,
- .ndo_change_mtu = can_change_mtu,
+ .ndo_hwtstamp_get = can_hwtstamp_get,
+ .ndo_hwtstamp_set = can_hwtstamp_set,
};
static void
@@ -1808,6 +1797,160 @@ static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv)
return 0;
}
+static const char * const mcp251xfd_gpio_names[] = { "GPIO0", "GPIO1" };
+
+static int mcp251xfd_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 pin_mask = MCP251XFD_REG_IOCON_PM(offset);
+ int ret;
+
+ if (priv->rx_int && offset == 1) {
+ netdev_err(priv->ndev, "Can't use GPIO 1 with RX-INT!\n");
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(priv->ndev->dev.parent);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, pin_mask, pin_mask);
+}
+
+static void mcp251xfd_gpio_free(struct gpio_chip *chip, unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+
+ pm_runtime_put(priv->ndev->dev.parent);
+}
+
+static int mcp251xfd_gpio_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask = MCP251XFD_REG_IOCON_TRIS(offset);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
+ if (ret)
+ return ret;
+
+ if (mask & val)
+ return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int mcp251xfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 mask = MCP251XFD_REG_IOCON_GPIO(offset);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
+ if (ret)
+ return ret;
+
+ return !!(mask & val);
+}
+
+static int mcp251xfd_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bit)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 val;
+ int ret;
+
+ ret = regmap_read(priv->map_reg, MCP251XFD_REG_IOCON, &val);
+ if (ret)
+ return ret;
+
+ *bit = FIELD_GET(MCP251XFD_REG_IOCON_GPIO_MASK, val) & *mask;
+
+ return 0;
+}
+
+static int mcp251xfd_gpio_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
+ u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
+ u32 val;
+
+ if (value)
+ val = val_mask;
+ else
+ val = 0;
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
+ dir_mask | val_mask, val);
+}
+
+static int mcp251xfd_gpio_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 dir_mask = MCP251XFD_REG_IOCON_TRIS(offset);
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, dir_mask, dir_mask);
+}
+
+static int mcp251xfd_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 val_mask = MCP251XFD_REG_IOCON_LAT(offset);
+ u32 val;
+
+ if (value)
+ val = val_mask;
+ else
+ val = 0;
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON, val_mask, val);
+}
+
+static int mcp251xfd_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
+{
+ struct mcp251xfd_priv *priv = gpiochip_get_data(chip);
+ u32 val;
+
+ val = FIELD_PREP(MCP251XFD_REG_IOCON_LAT_MASK, *bits);
+
+ return regmap_update_bits(priv->map_reg, MCP251XFD_REG_IOCON,
+ MCP251XFD_REG_IOCON_LAT_MASK, val);
+}
+
+static int mcp251fdx_gpio_setup(struct mcp251xfd_priv *priv)
+{
+ struct gpio_chip *gc = &priv->gc;
+
+ if (!device_property_present(&priv->spi->dev, "gpio-controller"))
+ return 0;
+
+ gc->label = dev_name(&priv->spi->dev);
+ gc->parent = &priv->spi->dev;
+ gc->owner = THIS_MODULE;
+ gc->request = mcp251xfd_gpio_request;
+ gc->free = mcp251xfd_gpio_free;
+ gc->get_direction = mcp251xfd_gpio_get_direction;
+ gc->direction_output = mcp251xfd_gpio_direction_output;
+ gc->direction_input = mcp251xfd_gpio_direction_input;
+ gc->get = mcp251xfd_gpio_get;
+ gc->get_multiple = mcp251xfd_gpio_get_multiple;
+ gc->set = mcp251xfd_gpio_set;
+ gc->set_multiple = mcp251xfd_gpio_set_multiple;
+ gc->base = -1;
+ gc->can_sleep = true;
+ gc->ngpio = ARRAY_SIZE(mcp251xfd_gpio_names);
+ gc->names = mcp251xfd_gpio_names;
+
+ return devm_gpiochip_add_data(&priv->spi->dev, gc, priv);
+}
+
static int
mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id,
u32 *effective_speed_hz_slow,
@@ -1907,53 +2050,59 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
struct net_device *ndev = priv->ndev;
int err;
+ mcp251xfd_register_quirks(priv);
+
err = mcp251xfd_clks_and_vdd_enable(priv);
if (err)
return err;
- pm_runtime_get_noresume(ndev->dev.parent);
- err = pm_runtime_set_active(ndev->dev.parent);
- if (err)
- goto out_runtime_put_noidle;
- pm_runtime_enable(ndev->dev.parent);
-
- mcp251xfd_register_quirks(priv);
-
err = mcp251xfd_chip_softreset(priv);
if (err == -ENODEV)
- goto out_runtime_disable;
+ goto out_clks_and_vdd_disable;
if (err)
goto out_chip_sleep;
err = mcp251xfd_chip_clock_init(priv);
if (err == -ENODEV)
- goto out_runtime_disable;
+ goto out_clks_and_vdd_disable;
if (err)
goto out_chip_sleep;
+ pm_runtime_get_noresume(ndev->dev.parent);
+ err = pm_runtime_set_active(ndev->dev.parent);
+ if (err)
+ goto out_runtime_put_noidle;
+ pm_runtime_enable(ndev->dev.parent);
+
err = mcp251xfd_register_chip_detect(priv);
if (err)
- goto out_chip_sleep;
+ goto out_runtime_disable;
err = mcp251xfd_register_check_rx_int(priv);
if (err)
- goto out_chip_sleep;
+ goto out_runtime_disable;
mcp251xfd_ethtool_init(priv);
+ err = mcp251fdx_gpio_setup(priv);
+ if (err) {
+ dev_err_probe(&priv->spi->dev, err, "Failed to register gpio-controller.\n");
+ goto out_runtime_disable;
+ }
+
err = register_candev(ndev);
if (err)
- goto out_chip_sleep;
+ goto out_runtime_disable;
err = mcp251xfd_register_done(priv);
if (err)
goto out_unregister_candev;
- /* Put controller into sleep mode and let pm_runtime_put()
- * disable the clocks and vdd. If CONFIG_PM is not enabled,
- * the clocks and vdd will stay powered.
+ /* Put controller into Config mode and let pm_runtime_put()
+ * put in sleep mode, disable the clocks and vdd. If CONFIG_PM
+ * is not enabled, the clocks and vdd will stay powered.
*/
- err = mcp251xfd_chip_sleep(priv);
+ err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG);
if (err)
goto out_unregister_candev;
@@ -1963,12 +2112,13 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
out_unregister_candev:
unregister_candev(ndev);
-out_chip_sleep:
- mcp251xfd_chip_sleep(priv);
out_runtime_disable:
pm_runtime_disable(ndev->dev.parent);
out_runtime_put_noidle:
pm_runtime_put_noidle(ndev->dev.parent);
+out_chip_sleep:
+ mcp251xfd_chip_sleep(priv);
+out_clks_and_vdd_disable:
mcp251xfd_clks_and_vdd_disable(priv);
return err;
@@ -1980,10 +2130,12 @@ static inline void mcp251xfd_unregister(struct mcp251xfd_priv *priv)
unregister_candev(ndev);
- if (pm_runtime_enabled(ndev->dev.parent))
+ if (pm_runtime_enabled(ndev->dev.parent)) {
pm_runtime_disable(ndev->dev.parent);
- else
+ } else {
+ mcp251xfd_chip_sleep(priv);
mcp251xfd_clks_and_vdd_disable(priv);
+ }
}
static const struct of_device_id mcp251xfd_of_match[] = {
@@ -2206,16 +2358,41 @@ static void mcp251xfd_remove(struct spi_device *spi)
static int __maybe_unused mcp251xfd_runtime_suspend(struct device *device)
{
- const struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ mcp251xfd_chip_sleep(priv);
return mcp251xfd_clks_and_vdd_disable(priv);
}
static int __maybe_unused mcp251xfd_runtime_resume(struct device *device)
{
- const struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ struct mcp251xfd_priv *priv = dev_get_drvdata(device);
+ int err;
+
+ err = mcp251xfd_clks_and_vdd_enable(priv);
+ if (err)
+ return err;
- return mcp251xfd_clks_and_vdd_enable(priv);
+ err = mcp251xfd_chip_softreset(priv);
+ if (err == -ENODEV)
+ goto out_clks_and_vdd_disable;
+ if (err)
+ goto out_chip_sleep;
+
+ err = mcp251xfd_chip_clock_init(priv);
+ if (err == -ENODEV)
+ goto out_clks_and_vdd_disable;
+ if (err)
+ goto out_chip_sleep;
+
+ return 0;
+
+out_chip_sleep:
+ mcp251xfd_chip_sleep(priv);
+out_clks_and_vdd_disable:
+ mcp251xfd_clks_and_vdd_disable(priv);
+
+ return err;
}
static const struct dev_pm_ops mcp251xfd_pm_ops = {
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
index 8c5be8d1c519..70d5ff0ae7ac 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c
@@ -13,17 +13,9 @@
static const struct regmap_config mcp251xfd_regmap_crc;
static int
-mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count)
-{
- struct spi_device *spi = context;
-
- return spi_write(spi, data, count);
-}
-
-static int
-mcp251xfd_regmap_nocrc_gather_write(void *context,
- const void *reg, size_t reg_len,
- const void *val, size_t val_len)
+_mcp251xfd_regmap_nocrc_gather_write(void *context,
+ const void *reg, size_t reg_len,
+ const void *val, size_t val_len)
{
struct spi_device *spi = context;
struct mcp251xfd_priv *priv = spi_get_drvdata(spi);
@@ -47,6 +39,54 @@ mcp251xfd_regmap_nocrc_gather_write(void *context,
return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
}
+static int
+mcp251xfd_regmap_nocrc_gather_write(void *context,
+ const void *reg_p, size_t reg_len,
+ const void *val, size_t val_len)
+{
+ const u16 byte_exclude = MCP251XFD_REG_IOCON +
+ mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK);
+ u16 reg = be16_to_cpu(*(__be16 *)reg_p) & MCP251XFD_SPI_ADDRESS_MASK;
+ int ret;
+
+ /* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1
+ *
+ * According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one
+ * SPI write command clears LAT0/LAT1.
+ *
+ * Errata Fix/Work Around suggests to write registers with single byte
+ * write instructions. However, it seems that the byte at 0xe06(IOCON[23:16])
+ * is for read-only access and writing to it causes the clearing of LAT0/LAT1.
+ */
+ if (reg <= byte_exclude && reg + val_len > byte_exclude) {
+ size_t len = byte_exclude - reg;
+
+ /* Write up to 0xe05 */
+ ret = _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len, val, len);
+ if (ret)
+ return ret;
+
+ /* Write from 0xe07 on */
+ reg += len + 1;
+ reg = (__force unsigned short)cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE | reg);
+ return _mcp251xfd_regmap_nocrc_gather_write(context, &reg, reg_len,
+ val + len + 1,
+ val_len - len - 1);
+ }
+
+ return _mcp251xfd_regmap_nocrc_gather_write(context, reg_p, reg_len,
+ val, val_len);
+}
+
+static int
+mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count)
+{
+ const size_t data_offset = sizeof(__be16);
+
+ return mcp251xfd_regmap_nocrc_gather_write(context, data, data_offset,
+ data + data_offset, count - data_offset);
+}
+
static inline bool
mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv,
unsigned int reg)
@@ -64,6 +104,7 @@ mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv,
case MCP251XFD_REG_CON:
case MCP251XFD_REG_OSC:
case MCP251XFD_REG_ECCCON:
+ case MCP251XFD_REG_IOCON:
return true;
default:
mcp251xfd_for_each_rx_ring(priv, ring, n) {
@@ -139,10 +180,9 @@ mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg,
tmp_le32 = orig_le32 & ~mask_le32;
tmp_le32 |= val_le32 & mask_le32;
- mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg + first_byte);
- memcpy(buf_tx->data, &tmp_le32, len);
-
- return spi_write(spi, buf_tx, sizeof(buf_tx->cmd) + len);
+ reg += first_byte;
+ mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg);
+ return mcp251xfd_regmap_nocrc_gather_write(context, &buf_tx->cmd, 2, &tmp_le32, len);
}
static int
@@ -196,9 +236,9 @@ mcp251xfd_regmap_nocrc_read(void *context,
}
static int
-mcp251xfd_regmap_crc_gather_write(void *context,
- const void *reg_p, size_t reg_len,
- const void *val, size_t val_len)
+_mcp251xfd_regmap_crc_gather_write(void *context,
+ const void *reg_p, size_t reg_len,
+ const void *val, size_t val_len)
{
struct spi_device *spi = context;
struct mcp251xfd_priv *priv = spi_get_drvdata(spi);
@@ -230,6 +270,44 @@ mcp251xfd_regmap_crc_gather_write(void *context,
}
static int
+mcp251xfd_regmap_crc_gather_write(void *context,
+ const void *reg_p, size_t reg_len,
+ const void *val, size_t val_len)
+{
+ const u16 byte_exclude = MCP251XFD_REG_IOCON +
+ mcp251xfd_first_byte_set(MCP251XFD_REG_IOCON_GPIO_MASK);
+ u16 reg = *(u16 *)reg_p;
+ int ret;
+
+ /* Never write to bits 16..23 of IOCON register to avoid clearing of LAT0/LAT1
+ *
+ * According to MCP2518FD Errata DS80000789E 5 writing IOCON register using one
+ * SPI write command clears LAT0/LAT1.
+ *
+ * Errata Fix/Work Around suggests to write registers with single byte
+ * write instructions. However, it seems that the byte at 0xe06(IOCON[23:16])
+ * is for read-only access and writing to it causes the clearing of LAT0/LAT1.
+ */
+ if (reg <= byte_exclude && reg + val_len > byte_exclude) {
+ size_t len = byte_exclude - reg;
+
+ /* Write up to 0xe05 */
+ ret = _mcp251xfd_regmap_crc_gather_write(context, &reg, reg_len, val, len);
+ if (ret)
+ return ret;
+
+ /* Write from 0xe07 on */
+ reg += len + 1;
+ return _mcp251xfd_regmap_crc_gather_write(context, &reg, reg_len,
+ val + len + 1,
+ val_len - len - 1);
+ }
+
+ return _mcp251xfd_regmap_crc_gather_write(context, reg_p, reg_len,
+ val, val_len);
+}
+
+static int
mcp251xfd_regmap_crc_write(void *context,
const void *data, size_t count)
{
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
index dcbbd2b2fae8..085d7101e595 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h
@@ -15,6 +15,7 @@
#include <linux/can/dev.h>
#include <linux/can/rx-offload.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/regmap.h>
@@ -335,13 +336,19 @@
#define MCP251XFD_REG_IOCON_TXCANOD BIT(28)
#define MCP251XFD_REG_IOCON_PM1 BIT(25)
#define MCP251XFD_REG_IOCON_PM0 BIT(24)
+#define MCP251XFD_REG_IOCON_PM(n) (MCP251XFD_REG_IOCON_PM0 << (n))
#define MCP251XFD_REG_IOCON_GPIO1 BIT(17)
#define MCP251XFD_REG_IOCON_GPIO0 BIT(16)
+#define MCP251XFD_REG_IOCON_GPIO(n) (MCP251XFD_REG_IOCON_GPIO0 << (n))
+#define MCP251XFD_REG_IOCON_GPIO_MASK GENMASK(17, 16)
#define MCP251XFD_REG_IOCON_LAT1 BIT(9)
#define MCP251XFD_REG_IOCON_LAT0 BIT(8)
+#define MCP251XFD_REG_IOCON_LAT(n) (MCP251XFD_REG_IOCON_LAT0 << (n))
+#define MCP251XFD_REG_IOCON_LAT_MASK GENMASK(9, 8)
#define MCP251XFD_REG_IOCON_XSTBYEN BIT(6)
#define MCP251XFD_REG_IOCON_TRIS1 BIT(1)
#define MCP251XFD_REG_IOCON_TRIS0 BIT(0)
+#define MCP251XFD_REG_IOCON_TRIS(n) (MCP251XFD_REG_IOCON_TRIS0 << (n))
#define MCP251XFD_REG_CRC 0xe08
#define MCP251XFD_REG_CRC_FERRIE BIT(25)
@@ -670,6 +677,7 @@ struct mcp251xfd_priv {
struct mcp251xfd_devtype_data devtype_data;
struct can_berr_counter bec;
+ struct gpio_chip gc;
};
#define MCP251XFD_IS(_model) \
diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c
index 53bfd873de9b..af52285d5a4e 100644
--- a/drivers/net/can/sun4i_can.c
+++ b/drivers/net/can/sun4i_can.c
@@ -657,8 +657,8 @@ static irqreturn_t sun4i_can_interrupt(int irq, void *dev_id)
u8 isrc, status;
int n = 0;
- while ((isrc = readl(priv->base + SUN4I_REG_INT_ADDR)) &&
- (n < SUN4I_CAN_MAX_IRQ)) {
+ while ((n < SUN4I_CAN_MAX_IRQ) &&
+ (isrc = readl(priv->base + SUN4I_REG_INT_ADDR))) {
n++;
status = readl(priv->base + SUN4I_REG_STA_ADDR);
@@ -768,7 +768,6 @@ static const struct net_device_ops sun4ican_netdev_ops = {
.ndo_open = sun4ican_open,
.ndo_stop = sun4ican_close,
.ndo_start_xmit = sun4ican_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops sun4ican_ethtool_ops = {
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index e6d6661a908a..1d3dbf28b105 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -829,7 +829,6 @@ static const struct net_device_ops ti_hecc_netdev_ops = {
.ndo_open = ti_hecc_open,
.ndo_stop = ti_hecc_close,
.ndo_start_xmit = ti_hecc_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops ti_hecc_ethtool_ops = {
diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c
index 5355bac4dccb..de8e212a1366 100644
--- a/drivers/net/can/usb/ems_usb.c
+++ b/drivers/net/can/usb/ems_usb.c
@@ -885,7 +885,6 @@ static const struct net_device_ops ems_usb_netdev_ops = {
.ndo_open = ems_usb_open,
.ndo_stop = ems_usb_close,
.ndo_start_xmit = ems_usb_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops ems_usb_ethtool_ops = {
diff --git a/drivers/net/can/usb/esd_usb.c b/drivers/net/can/usb/esd_usb.c
index 9bc1824d7be6..08da507faef4 100644
--- a/drivers/net/can/usb/esd_usb.c
+++ b/drivers/net/can/usb/esd_usb.c
@@ -1011,7 +1011,6 @@ static const struct net_device_ops esd_usb_netdev_ops = {
.ndo_open = esd_usb_open,
.ndo_stop = esd_usb_close,
.ndo_start_xmit = esd_usb_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops esd_usb_ethtool_ops = {
diff --git a/drivers/net/can/usb/etas_es58x/es58x_core.c b/drivers/net/can/usb/etas_es58x/es58x_core.c
index adc91873c083..f799233c2b72 100644
--- a/drivers/net/can/usb/etas_es58x/es58x_core.c
+++ b/drivers/net/can/usb/etas_es58x/es58x_core.c
@@ -1976,8 +1976,8 @@ static const struct net_device_ops es58x_netdev_ops = {
.ndo_open = es58x_open,
.ndo_stop = es58x_stop,
.ndo_start_xmit = es58x_start_xmit,
- .ndo_eth_ioctl = can_eth_ioctl_hwts,
- .ndo_change_mtu = can_change_mtu,
+ .ndo_hwtstamp_get = can_hwtstamp_get,
+ .ndo_hwtstamp_set = can_hwtstamp_set,
};
static const struct ethtool_ops es58x_ethtool_ops = {
diff --git a/drivers/net/can/usb/f81604.c b/drivers/net/can/usb/f81604.c
index e0cfa1460b0b..efe61ece79ea 100644
--- a/drivers/net/can/usb/f81604.c
+++ b/drivers/net/can/usb/f81604.c
@@ -1052,7 +1052,6 @@ static const struct net_device_ops f81604_netdev_ops = {
.ndo_open = f81604_open,
.ndo_stop = f81604_close,
.ndo_start_xmit = f81604_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct can_bittiming_const f81604_bittiming_const = {
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index 69b8d6da651b..e29e85b67fd4 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -261,14 +261,21 @@ struct canfd_quirk {
u8 quirk;
} __packed;
+/* struct gs_host_frame::echo_id == GS_HOST_FRAME_ECHO_ID_RX indicates
+ * a regular RX'ed CAN frame
+ */
+#define GS_HOST_FRAME_ECHO_ID_RX 0xffffffff
+
struct gs_host_frame {
- u32 echo_id;
- __le32 can_id;
+ struct_group(header,
+ u32 echo_id;
+ __le32 can_id;
- u8 can_dlc;
- u8 channel;
- u8 flags;
- u8 reserved;
+ u8 can_dlc;
+ u8 channel;
+ u8 flags;
+ u8 reserved;
+ );
union {
DECLARE_FLEX_ARRAY(struct classic_can, classic_can);
@@ -568,6 +575,37 @@ gs_usb_get_echo_skb(struct gs_can *dev, struct sk_buff *skb,
return len;
}
+static unsigned int
+gs_usb_get_minimum_rx_length(const struct gs_can *dev, const struct gs_host_frame *hf,
+ unsigned int *data_length_p)
+{
+ unsigned int minimum_length, data_length = 0;
+
+ if (hf->flags & GS_CAN_FLAG_FD) {
+ if (hf->echo_id == GS_HOST_FRAME_ECHO_ID_RX)
+ data_length = can_fd_dlc2len(hf->can_dlc);
+
+ if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
+ /* timestamp follows data field of max size */
+ minimum_length = struct_size(hf, canfd_ts, 1);
+ else
+ minimum_length = sizeof(hf->header) + data_length;
+ } else {
+ if (hf->echo_id == GS_HOST_FRAME_ECHO_ID_RX &&
+ !(hf->can_id & cpu_to_le32(CAN_RTR_FLAG)))
+ data_length = can_cc_dlc2len(hf->can_dlc);
+
+ if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
+ /* timestamp follows data field of max size */
+ minimum_length = struct_size(hf, classic_can_ts, 1);
+ else
+ minimum_length = sizeof(hf->header) + data_length;
+ }
+
+ *data_length_p = data_length;
+ return minimum_length;
+}
+
static void gs_usb_receive_bulk_callback(struct urb *urb)
{
struct gs_usb *parent = urb->context;
@@ -576,6 +614,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
int rc;
struct net_device_stats *stats;
struct gs_host_frame *hf = urb->transfer_buffer;
+ unsigned int minimum_length, data_length;
struct gs_tx_context *txc;
struct can_frame *cf;
struct canfd_frame *cfd;
@@ -594,6 +633,15 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
return;
}
+ minimum_length = sizeof(hf->header);
+ if (urb->actual_length < minimum_length) {
+ dev_err_ratelimited(&parent->udev->dev,
+ "short read (actual_length=%u, minimum_length=%u)\n",
+ urb->actual_length, minimum_length);
+
+ goto resubmit_urb;
+ }
+
/* device reports out of range channel id */
if (hf->channel >= parent->channel_cnt)
goto device_detach;
@@ -609,20 +657,33 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
if (!netif_running(netdev))
goto resubmit_urb;
- if (hf->echo_id == -1) { /* normal rx */
+ minimum_length = gs_usb_get_minimum_rx_length(dev, hf, &data_length);
+ if (urb->actual_length < minimum_length) {
+ stats->rx_errors++;
+ stats->rx_length_errors++;
+
+ if (net_ratelimit())
+ netdev_err(netdev,
+ "short read (actual_length=%u, minimum_length=%u)\n",
+ urb->actual_length, minimum_length);
+
+ goto resubmit_urb;
+ }
+
+ if (hf->echo_id == GS_HOST_FRAME_ECHO_ID_RX) { /* normal rx */
if (hf->flags & GS_CAN_FLAG_FD) {
skb = alloc_canfd_skb(netdev, &cfd);
if (!skb)
return;
cfd->can_id = le32_to_cpu(hf->can_id);
- cfd->len = can_fd_dlc2len(hf->can_dlc);
+ cfd->len = data_length;
if (hf->flags & GS_CAN_FLAG_BRS)
cfd->flags |= CANFD_BRS;
if (hf->flags & GS_CAN_FLAG_ESI)
cfd->flags |= CANFD_ESI;
- memcpy(cfd->data, hf->canfd->data, cfd->len);
+ memcpy(cfd->data, hf->canfd->data, data_length);
} else {
skb = alloc_can_skb(netdev, &cf);
if (!skb)
@@ -631,7 +692,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
cf->can_id = le32_to_cpu(hf->can_id);
can_frame_set_cc_len(cf, hf->can_dlc, dev->can.ctrlmode);
- memcpy(cf->data, hf->classic_can->data, 8);
+ memcpy(cf->data, hf->classic_can->data, data_length);
/* ERROR frames tell us information about the controller */
if (le32_to_cpu(hf->can_id) & CAN_ERR_FLAG)
@@ -687,7 +748,7 @@ static void gs_usb_receive_bulk_callback(struct urb *urb)
resubmit_urb:
usb_fill_bulk_urb(urb, parent->udev,
parent->pipe_in,
- hf, dev->parent->hf_size_rx,
+ hf, parent->hf_size_rx,
gs_usb_receive_bulk_callback, parent);
rc = usb_submit_urb(urb, GFP_ATOMIC);
@@ -750,8 +811,21 @@ static void gs_usb_xmit_callback(struct urb *urb)
struct gs_can *dev = txc->dev;
struct net_device *netdev = dev->netdev;
- if (urb->status)
- netdev_info(netdev, "usb xmit fail %u\n", txc->echo_id);
+ if (!urb->status)
+ return;
+
+ if (urb->status != -ESHUTDOWN && net_ratelimit())
+ netdev_info(netdev, "failed to xmit URB %u: %pe\n",
+ txc->echo_id, ERR_PTR(urb->status));
+
+ netdev->stats.tx_dropped++;
+ netdev->stats.tx_errors++;
+
+ can_free_echo_skb(netdev, txc->echo_id, NULL);
+ gs_free_tx_context(txc);
+ atomic_dec(&dev->active_tx_urbs);
+
+ netif_wake_queue(netdev);
}
static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb,
@@ -1087,12 +1161,25 @@ static int gs_can_close(struct net_device *netdev)
return 0;
}
-static int gs_can_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+static int gs_can_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *cfg)
+{
+ const struct gs_can *dev = netdev_priv(netdev);
+
+ if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
+ return can_hwtstamp_get(netdev, cfg);
+
+ return -EOPNOTSUPP;
+}
+
+static int gs_can_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
const struct gs_can *dev = netdev_priv(netdev);
if (dev->feature & GS_CAN_FEATURE_HW_TIMESTAMP)
- return can_eth_ioctl_hwts(netdev, ifr, cmd);
+ return can_hwtstamp_set(netdev, cfg, extack);
return -EOPNOTSUPP;
}
@@ -1101,8 +1188,8 @@ static const struct net_device_ops gs_usb_netdev_ops = {
.ndo_open = gs_can_open,
.ndo_stop = gs_can_close,
.ndo_start_xmit = gs_can_start_xmit,
- .ndo_change_mtu = can_change_mtu,
- .ndo_eth_ioctl = gs_can_eth_ioctl,
+ .ndo_hwtstamp_get = gs_can_hwtstamp_get,
+ .ndo_hwtstamp_set = gs_can_hwtstamp_set,
};
static int gs_usb_set_identify(struct net_device *netdev, bool do_identify)
diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
index 90e77fa0ff4a..62701ec34272 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
@@ -784,9 +784,9 @@ static int kvaser_usb_set_phys_id(struct net_device *netdev,
static const struct net_device_ops kvaser_usb_netdev_ops = {
.ndo_open = kvaser_usb_open,
.ndo_stop = kvaser_usb_close,
- .ndo_eth_ioctl = can_eth_ioctl_hwts,
.ndo_start_xmit = kvaser_usb_start_xmit,
- .ndo_change_mtu = can_change_mtu,
+ .ndo_hwtstamp_get = can_hwtstamp_get,
+ .ndo_hwtstamp_set = can_hwtstamp_set,
};
static const struct ethtool_ops kvaser_usb_ethtool_ops = {
diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
index c29828a94ad0..1167d38344f1 100644
--- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
+++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_leaf.c
@@ -685,7 +685,7 @@ static int kvaser_usb_leaf_wait_cmd(const struct kvaser_usb *dev, u8 id,
* for further details.
*/
if (tmp->len == 0) {
- pos = round_up(pos,
+ pos = round_up(pos + 1,
le16_to_cpu
(dev->bulk_in->wMaxPacketSize));
continue;
@@ -1732,7 +1732,7 @@ static void kvaser_usb_leaf_read_bulk_callback(struct kvaser_usb *dev,
* number of events in case of a heavy rx load on the bus.
*/
if (cmd->len == 0) {
- pos = round_up(pos, le16_to_cpu
+ pos = round_up(pos + 1, le16_to_cpu
(dev->bulk_in->wMaxPacketSize));
continue;
}
diff --git a/drivers/net/can/usb/mcba_usb.c b/drivers/net/can/usb/mcba_usb.c
index 1f9b915094e6..41c0a1c399bf 100644
--- a/drivers/net/can/usb/mcba_usb.c
+++ b/drivers/net/can/usb/mcba_usb.c
@@ -761,7 +761,6 @@ static const struct net_device_ops mcba_netdev_ops = {
.ndo_open = mcba_usb_open,
.ndo_stop = mcba_usb_close,
.ndo_start_xmit = mcba_usb_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops mcba_ethtool_ops = {
diff --git a/drivers/net/can/usb/nct6694_canfd.c b/drivers/net/can/usb/nct6694_canfd.c
index 8deff16491a1..dd6df2ec3742 100644
--- a/drivers/net/can/usb/nct6694_canfd.c
+++ b/drivers/net/can/usb/nct6694_canfd.c
@@ -690,7 +690,6 @@ static const struct net_device_ops nct6694_canfd_netdev_ops = {
.ndo_open = nct6694_canfd_open,
.ndo_stop = nct6694_canfd_close,
.ndo_start_xmit = nct6694_canfd_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops nct6694_canfd_ethtool_ops = {
diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
index c74302ca7cee..cf48bb26d46d 100644
--- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c
+++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c
@@ -784,37 +784,33 @@ static int peak_usb_set_data_bittiming(struct net_device *netdev)
return 0;
}
-static int peak_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+static int peak_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
- struct hwtstamp_config hwts_cfg = { 0 };
-
- switch (cmd) {
- case SIOCSHWTSTAMP: /* set */
- if (copy_from_user(&hwts_cfg, ifr->ifr_data, sizeof(hwts_cfg)))
- return -EFAULT;
- if (hwts_cfg.tx_type == HWTSTAMP_TX_OFF &&
- hwts_cfg.rx_filter == HWTSTAMP_FILTER_ALL)
- return 0;
- return -ERANGE;
-
- case SIOCGHWTSTAMP: /* get */
- hwts_cfg.tx_type = HWTSTAMP_TX_OFF;
- hwts_cfg.rx_filter = HWTSTAMP_FILTER_ALL;
- if (copy_to_user(ifr->ifr_data, &hwts_cfg, sizeof(hwts_cfg)))
- return -EFAULT;
+ config->tx_type = HWTSTAMP_TX_OFF;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+
+ return 0;
+}
+
+static int peak_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ if (config->tx_type == HWTSTAMP_TX_OFF &&
+ config->rx_filter == HWTSTAMP_FILTER_ALL)
return 0;
- default:
- return -EOPNOTSUPP;
- }
+ NL_SET_ERR_MSG_MOD(extack, "Only RX HWTSTAMP_FILTER_ALL is supported");
+ return -ERANGE;
}
static const struct net_device_ops peak_usb_netdev_ops = {
.ndo_open = peak_usb_ndo_open,
.ndo_stop = peak_usb_ndo_stop,
- .ndo_eth_ioctl = peak_eth_ioctl,
.ndo_start_xmit = peak_usb_ndo_start_xmit,
- .ndo_change_mtu = can_change_mtu,
+ .ndo_hwtstamp_get = peak_hwtstamp_get,
+ .ndo_hwtstamp_set = peak_hwtstamp_set,
};
/* CAN-USB devices generally handle 32-bit CAN channel IDs.
diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c
index 07406daf7c88..de61d9da99e3 100644
--- a/drivers/net/can/usb/ucan.c
+++ b/drivers/net/can/usb/ucan.c
@@ -1233,7 +1233,6 @@ static const struct net_device_ops ucan_netdev_ops = {
.ndo_open = ucan_open,
.ndo_stop = ucan_close,
.ndo_start_xmit = ucan_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops ucan_ethtool_ops = {
diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c
index 8a5596ce4e46..7449328f7cd7 100644
--- a/drivers/net/can/usb/usb_8dev.c
+++ b/drivers/net/can/usb/usb_8dev.c
@@ -868,7 +868,6 @@ static const struct net_device_ops usb_8dev_netdev_ops = {
.ndo_open = usb_8dev_open,
.ndo_stop = usb_8dev_close,
.ndo_start_xmit = usb_8dev_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops usb_8dev_ethtool_ops = {
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
index a25a3ca62c12..43d7f22820b8 100644
--- a/drivers/net/can/xilinx_can.c
+++ b/drivers/net/can/xilinx_can.c
@@ -1702,7 +1702,6 @@ static const struct net_device_ops xcan_netdev_ops = {
.ndo_open = xcan_open,
.ndo_stop = xcan_close,
.ndo_start_xmit = xcan_start_xmit,
- .ndo_change_mtu = can_change_mtu,
};
static const struct ethtool_ops xcan_ethtool_ops = {
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4d9af691b989..7eb301fd987d 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -154,4 +154,11 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM
This enables support for the Vitesse VSC7385, VSC7388, VSC7395
and VSC7398 SparX integrated ethernet switches, connected over
a CPU-attached address bus and work in memory-mapped I/O mode.
+
+config NET_DSA_YT921X
+ tristate "Motorcomm YT9215 ethernet switch chip support"
+ select NET_DSA_TAG_YT921X
+ help
+ This enables support for the Motorcomm YT9215 ethernet switch
+ chip.
endmenu
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 0f8ff4a1a313..16de4ba3fa38 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o
obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o
+obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o
obj-y += b53/
obj-y += hirschmann/
obj-y += lantiq/
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 2f846381d5a7..a1a177713d99 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -371,11 +371,11 @@ static void b53_set_forwarding(struct b53_device *dev, int enable)
* frames should be flooded or not.
*/
b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt);
- mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN | B53_IPMC_FWD_EN;
+ mgmt |= B53_UC_FWD_EN | B53_MC_FWD_EN | B53_IP_MC;
b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt);
} else {
b53_read8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, &mgmt);
- mgmt |= B53_IP_MCAST_25;
+ mgmt |= B53_IP_MC;
b53_write8(dev, B53_CTRL_PAGE, B53_IP_MULTICAST_CTRL, mgmt);
}
}
@@ -632,6 +632,25 @@ static void b53_port_set_learning(struct b53_device *dev, int port,
b53_write16(dev, B53_CTRL_PAGE, B53_DIS_LEARNING, reg);
}
+static void b53_port_set_isolated(struct b53_device *dev, int port,
+ bool isolated)
+{
+ u8 offset;
+ u16 reg;
+
+ if (is5325(dev))
+ offset = B53_PROTECTED_PORT_SEL_25;
+ else
+ offset = B53_PROTECTED_PORT_SEL;
+
+ b53_read16(dev, B53_CTRL_PAGE, offset, &reg);
+ if (isolated)
+ reg |= BIT(port);
+ else
+ reg &= ~BIT(port);
+ b53_write16(dev, B53_CTRL_PAGE, offset, reg);
+}
+
static void b53_eee_enable_set(struct dsa_switch *ds, int port, bool enable)
{
struct b53_device *dev = ds->priv;
@@ -652,6 +671,7 @@ int b53_setup_port(struct dsa_switch *ds, int port)
b53_port_set_ucast_flood(dev, port, true);
b53_port_set_mcast_flood(dev, port, true);
b53_port_set_learning(dev, port, false);
+ b53_port_set_isolated(dev, port, false);
/* Force all traffic to go to the CPU port to prevent the ASIC from
* trying to forward to bridged ports on matching FDB entries, then
@@ -852,10 +872,7 @@ static void b53_enable_stp(struct b53_device *dev)
static u16 b53_default_pvid(struct b53_device *dev)
{
- if (is5325(dev) || is5365(dev))
- return 1;
- else
- return 0;
+ return 0;
}
static bool b53_vlan_port_needs_forced_tagged(struct dsa_switch *ds, int port)
@@ -1372,6 +1389,10 @@ static void b53_force_port_config(struct b53_device *dev, int port,
else
reg &= ~PORT_OVERRIDE_FULL_DUPLEX;
+ reg &= ~(0x3 << GMII_PO_SPEED_S);
+ if (is5301x(dev) || is58xx(dev))
+ reg &= ~PORT_OVERRIDE_SPEED_2000M;
+
switch (speed) {
case 2000:
reg |= PORT_OVERRIDE_SPEED_2000M;
@@ -1390,6 +1411,11 @@ static void b53_force_port_config(struct b53_device *dev, int port,
return;
}
+ if (is5325(dev))
+ reg &= ~PORT_OVERRIDE_LP_FLOW_25;
+ else
+ reg &= ~(PORT_OVERRIDE_RX_FLOW | PORT_OVERRIDE_TX_FLOW);
+
if (rx_pause) {
if (is5325(dev))
reg |= PORT_OVERRIDE_LP_FLOW_25;
@@ -1593,8 +1619,11 @@ static void b53_phylink_mac_link_down(struct phylink_config *config,
struct b53_device *dev = dp->ds->priv;
int port = dp->index;
- if (mode == MLO_AN_PHY)
+ if (mode == MLO_AN_PHY) {
+ if (is63xx(dev) && in_range(port, B53_63XX_RGMII0, 4))
+ b53_force_link(dev, port, false);
return;
+ }
if (mode == MLO_AN_FIXED) {
b53_force_link(dev, port, false);
@@ -1622,6 +1651,13 @@ static void b53_phylink_mac_link_up(struct phylink_config *config,
if (mode == MLO_AN_PHY) {
/* Re-negotiate EEE if it was enabled already */
p->eee_enabled = b53_eee_init(ds, port, phydev);
+
+ if (is63xx(dev) && in_range(port, B53_63XX_RGMII0, 4)) {
+ b53_force_port_config(dev, port, speed, duplex,
+ tx_pause, rx_pause);
+ b53_force_link(dev, port, true);
+ }
+
return;
}
@@ -1660,9 +1696,6 @@ static int b53_vlan_prepare(struct dsa_switch *ds, int port,
{
struct b53_device *dev = ds->priv;
- if ((is5325(dev) || is5365(dev)) && vlan->vid == 0)
- return -EOPNOTSUPP;
-
/* Port 7 on 7278 connects to the ASP's UniMAC which is not capable of
* receiving VLAN tagged frames at all, we can still allow the port to
* be configured for egress untagged.
@@ -1811,49 +1844,83 @@ static int b53_arl_rw_op(struct b53_device *dev, unsigned int op)
return b53_arl_op_wait(dev);
}
-static int b53_arl_read(struct b53_device *dev, u64 mac,
- u16 vid, struct b53_arl_entry *ent, u8 *idx)
+static void b53_arl_read_entry_25(struct b53_device *dev,
+ struct b53_arl_entry *ent, u8 idx)
{
- DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES);
- unsigned int i;
- int ret;
+ u8 vid_entry;
+ u64 mac_vid;
- ret = b53_arl_op_wait(dev);
- if (ret)
- return ret;
+ b53_read8(dev, B53_ARLIO_PAGE, B53_ARLTBL_VID_ENTRY_25(idx),
+ &vid_entry);
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx),
+ &mac_vid);
+ b53_arl_to_entry_25(ent, mac_vid, vid_entry);
+}
- bitmap_zero(free_bins, dev->num_arl_bins);
+static void b53_arl_write_entry_25(struct b53_device *dev,
+ const struct b53_arl_entry *ent, u8 idx)
+{
+ u8 vid_entry;
+ u64 mac_vid;
- /* Read the bins */
- for (i = 0; i < dev->num_arl_bins; i++) {
- u64 mac_vid;
- u32 fwd_entry;
+ b53_arl_from_entry_25(&mac_vid, &vid_entry, ent);
+ b53_write8(dev, B53_ARLIO_PAGE, B53_ARLTBL_VID_ENTRY_25(idx), vid_entry);
+ b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx),
+ mac_vid);
+}
- b53_read64(dev, B53_ARLIO_PAGE,
- B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid);
- b53_read32(dev, B53_ARLIO_PAGE,
- B53_ARLTBL_DATA_ENTRY(i), &fwd_entry);
- b53_arl_to_entry(ent, mac_vid, fwd_entry);
+static void b53_arl_read_entry_89(struct b53_device *dev,
+ struct b53_arl_entry *ent, u8 idx)
+{
+ u64 mac_vid;
+ u16 fwd_entry;
- if (!(fwd_entry & ARLTBL_VALID)) {
- set_bit(i, free_bins);
- continue;
- }
- if ((mac_vid & ARLTBL_MAC_MASK) != mac)
- continue;
- if (dev->vlan_enabled &&
- ((mac_vid >> ARLTBL_VID_S) & ARLTBL_VID_MASK) != vid)
- continue;
- *idx = i;
- return 0;
- }
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx),
+ &mac_vid);
+ b53_read16(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx), &fwd_entry);
+ b53_arl_to_entry_89(ent, mac_vid, fwd_entry);
+}
- *idx = find_first_bit(free_bins, dev->num_arl_bins);
- return *idx >= dev->num_arl_bins ? -ENOSPC : -ENOENT;
+static void b53_arl_write_entry_89(struct b53_device *dev,
+ const struct b53_arl_entry *ent, u8 idx)
+{
+ u32 fwd_entry;
+ u64 mac_vid;
+
+ b53_arl_from_entry_89(&mac_vid, &fwd_entry, ent);
+ b53_write64(dev, B53_ARLIO_PAGE,
+ B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid);
+ b53_write16(dev, B53_ARLIO_PAGE,
+ B53_ARLTBL_DATA_ENTRY(idx), fwd_entry);
+}
+
+static void b53_arl_read_entry_95(struct b53_device *dev,
+ struct b53_arl_entry *ent, u8 idx)
+{
+ u32 fwd_entry;
+ u64 mac_vid;
+
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx),
+ &mac_vid);
+ b53_read32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx), &fwd_entry);
+ b53_arl_to_entry(ent, mac_vid, fwd_entry);
}
-static int b53_arl_read_25(struct b53_device *dev, u64 mac,
- u16 vid, struct b53_arl_entry *ent, u8 *idx)
+static void b53_arl_write_entry_95(struct b53_device *dev,
+ const struct b53_arl_entry *ent, u8 idx)
+{
+ u32 fwd_entry;
+ u64 mac_vid;
+
+ b53_arl_from_entry(&mac_vid, &fwd_entry, ent);
+ b53_write64(dev, B53_ARLIO_PAGE, B53_ARLTBL_MAC_VID_ENTRY(idx),
+ mac_vid);
+ b53_write32(dev, B53_ARLIO_PAGE, B53_ARLTBL_DATA_ENTRY(idx),
+ fwd_entry);
+}
+
+static int b53_arl_read(struct b53_device *dev, const u8 *mac,
+ u16 vid, struct b53_arl_entry *ent, u8 *idx)
{
DECLARE_BITMAP(free_bins, B53_ARLTBL_MAX_BIN_ENTRIES);
unsigned int i;
@@ -1867,21 +1934,15 @@ static int b53_arl_read_25(struct b53_device *dev, u64 mac,
/* Read the bins */
for (i = 0; i < dev->num_arl_bins; i++) {
- u64 mac_vid;
-
- b53_read64(dev, B53_ARLIO_PAGE,
- B53_ARLTBL_MAC_VID_ENTRY(i), &mac_vid);
+ b53_arl_read_entry(dev, ent, i);
- b53_arl_to_entry_25(ent, mac_vid);
-
- if (!(mac_vid & ARLTBL_VALID_25)) {
+ if (!ent->is_valid) {
set_bit(i, free_bins);
continue;
}
- if ((mac_vid & ARLTBL_MAC_MASK) != mac)
+ if (!ether_addr_equal(ent->mac, mac))
continue;
- if (dev->vlan_enabled &&
- ((mac_vid >> ARLTBL_VID_S_65) & ARLTBL_VID_MASK_25) != vid)
+ if (dev->vlan_enabled && ent->vid != vid)
continue;
*idx = i;
return 0;
@@ -1895,9 +1956,8 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
const unsigned char *addr, u16 vid, bool is_valid)
{
struct b53_arl_entry ent;
- u32 fwd_entry;
- u64 mac, mac_vid = 0;
u8 idx = 0;
+ u64 mac;
int ret;
/* Convert the array into a 64-bit MAC */
@@ -1905,18 +1965,19 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
/* Perform a read for the given MAC and VID */
b53_write48(dev, B53_ARLIO_PAGE, B53_MAC_ADDR_IDX, mac);
- if (!is5325m(dev))
- b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid);
+ if (!is5325m(dev)) {
+ if (is5325(dev) || is5365(dev))
+ b53_write8(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid);
+ else
+ b53_write16(dev, B53_ARLIO_PAGE, B53_VLAN_ID_IDX, vid);
+ }
/* Issue a read operation for this MAC */
ret = b53_arl_rw_op(dev, 1);
if (ret)
return ret;
- if (is5325(dev) || is5365(dev))
- ret = b53_arl_read_25(dev, mac, vid, &ent, &idx);
- else
- ret = b53_arl_read(dev, mac, vid, &ent, &idx);
+ ret = b53_arl_read(dev, addr, vid, &ent, &idx);
/* If this is a read, just finish now */
if (op)
@@ -1933,7 +1994,6 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
/* We could not find a matching MAC, so reset to a new entry */
dev_dbg(dev->dev, "{%pM,%.4d} not found, using idx: %d\n",
addr, vid, idx);
- fwd_entry = 0;
break;
default:
dev_dbg(dev->dev, "{%pM,%.4d} found, using idx: %d\n",
@@ -1960,17 +2020,7 @@ static int b53_arl_op(struct b53_device *dev, int op, int port,
ent.is_static = true;
ent.is_age = false;
memcpy(ent.mac, addr, ETH_ALEN);
- if (is5325(dev) || is5365(dev))
- b53_arl_from_entry_25(&mac_vid, &ent);
- else
- b53_arl_from_entry(&mac_vid, &fwd_entry, &ent);
-
- b53_write64(dev, B53_ARLIO_PAGE,
- B53_ARLTBL_MAC_VID_ENTRY(idx), mac_vid);
-
- if (!is5325(dev) && !is5365(dev))
- b53_write32(dev, B53_ARLIO_PAGE,
- B53_ARLTBL_DATA_ENTRY(idx), fwd_entry);
+ b53_arl_write_entry(dev, &ent, idx);
return b53_arl_rw_op(dev, 0);
}
@@ -2005,20 +2055,55 @@ int b53_fdb_del(struct dsa_switch *ds, int port,
}
EXPORT_SYMBOL(b53_fdb_del);
-static int b53_arl_search_wait(struct b53_device *dev)
+static void b53_read_arl_srch_ctl(struct b53_device *dev, u8 *val)
{
- unsigned int timeout = 1000;
- u8 reg, offset;
+ u8 offset;
+
+ if (is5325(dev) || is5365(dev))
+ offset = B53_ARL_SRCH_CTL_25;
+ else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev) ||
+ is63xx(dev))
+ offset = B53_ARL_SRCH_CTL_89;
+ else
+ offset = B53_ARL_SRCH_CTL;
+
+ if (is63xx(dev)) {
+ u16 val16;
+
+ b53_read16(dev, B53_ARLIO_PAGE, offset, &val16);
+ *val = val16 & 0xff;
+ } else {
+ b53_read8(dev, B53_ARLIO_PAGE, offset, val);
+ }
+}
+
+static void b53_write_arl_srch_ctl(struct b53_device *dev, u8 val)
+{
+ u8 offset;
if (is5325(dev) || is5365(dev))
offset = B53_ARL_SRCH_CTL_25;
+ else if (dev->chip_id == BCM5389_DEVICE_ID || is5397_98(dev) ||
+ is63xx(dev))
+ offset = B53_ARL_SRCH_CTL_89;
else
offset = B53_ARL_SRCH_CTL;
+ if (is63xx(dev))
+ b53_write16(dev, B53_ARLIO_PAGE, offset, val);
+ else
+ b53_write8(dev, B53_ARLIO_PAGE, offset, val);
+}
+
+static int b53_arl_search_wait(struct b53_device *dev)
+{
+ unsigned int timeout = 1000;
+ u8 reg;
+
do {
- b53_read8(dev, B53_ARLIO_PAGE, offset, &reg);
+ b53_read_arl_srch_ctl(dev, &reg);
if (!(reg & ARL_SRCH_STDN))
- return 0;
+ return -ENOENT;
if (reg & ARL_SRCH_VLID)
return 0;
@@ -2029,28 +2114,53 @@ static int b53_arl_search_wait(struct b53_device *dev)
return -ETIMEDOUT;
}
-static void b53_arl_search_rd(struct b53_device *dev, u8 idx,
- struct b53_arl_entry *ent)
+static void b53_arl_search_read_25(struct b53_device *dev, u8 idx,
+ struct b53_arl_entry *ent)
{
u64 mac_vid;
+ u8 ext;
- if (is5325(dev)) {
- b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25,
- &mac_vid);
- b53_arl_to_entry_25(ent, mac_vid);
- } else if (is5365(dev)) {
- b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_65,
- &mac_vid);
- b53_arl_to_entry_25(ent, mac_vid);
- } else {
- u32 fwd_entry;
+ b53_read8(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_EXT_25, &ext);
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_0_MACVID_25,
+ &mac_vid);
+ b53_arl_search_to_entry_25(ent, mac_vid, ext);
+}
- b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_MACVID(idx),
- &mac_vid);
- b53_read32(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL(idx),
- &fwd_entry);
- b53_arl_to_entry(ent, mac_vid, fwd_entry);
- }
+static void b53_arl_search_read_89(struct b53_device *dev, u8 idx,
+ struct b53_arl_entry *ent)
+{
+ u16 fwd_entry;
+ u64 mac_vid;
+
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_MACVID_89,
+ &mac_vid);
+ b53_read16(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_89, &fwd_entry);
+ b53_arl_to_entry_89(ent, mac_vid, fwd_entry);
+}
+
+static void b53_arl_search_read_63xx(struct b53_device *dev, u8 idx,
+ struct b53_arl_entry *ent)
+{
+ u16 fwd_entry;
+ u64 mac_vid;
+
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_MACVID_63XX,
+ &mac_vid);
+ b53_read16(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSLT_63XX, &fwd_entry);
+ b53_arl_search_to_entry_63xx(ent, mac_vid, fwd_entry);
+}
+
+static void b53_arl_search_read_95(struct b53_device *dev, u8 idx,
+ struct b53_arl_entry *ent)
+{
+ u32 fwd_entry;
+ u64 mac_vid;
+
+ b53_read64(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL_MACVID(idx),
+ &mac_vid);
+ b53_read32(dev, B53_ARLIO_PAGE, B53_ARL_SRCH_RSTL(idx),
+ &fwd_entry);
+ b53_arl_to_entry(ent, mac_vid, fwd_entry);
}
static int b53_fdb_copy(int port, const struct b53_arl_entry *ent,
@@ -2068,36 +2178,31 @@ static int b53_fdb_copy(int port, const struct b53_arl_entry *ent,
int b53_fdb_dump(struct dsa_switch *ds, int port,
dsa_fdb_dump_cb_t *cb, void *data)
{
+ unsigned int count = 0, results_per_hit = 1;
struct b53_device *priv = ds->priv;
struct b53_arl_entry results[2];
- unsigned int count = 0;
- u8 offset;
int ret;
- u8 reg;
- mutex_lock(&priv->arl_mutex);
+ if (priv->num_arl_bins > 2)
+ results_per_hit = 2;
- if (is5325(priv) || is5365(priv))
- offset = B53_ARL_SRCH_CTL_25;
- else
- offset = B53_ARL_SRCH_CTL;
+ mutex_lock(&priv->arl_mutex);
/* Start search operation */
- reg = ARL_SRCH_STDN;
- b53_write8(priv, B53_ARLIO_PAGE, offset, reg);
+ b53_write_arl_srch_ctl(priv, ARL_SRCH_STDN);
do {
ret = b53_arl_search_wait(priv);
if (ret)
break;
- b53_arl_search_rd(priv, 0, &results[0]);
+ b53_arl_search_read(priv, 0, &results[0]);
ret = b53_fdb_copy(port, &results[0], cb, data);
if (ret)
break;
- if (priv->num_arl_bins > 2) {
- b53_arl_search_rd(priv, 1, &results[1]);
+ if (results_per_hit == 2) {
+ b53_arl_search_read(priv, 1, &results[1]);
ret = b53_fdb_copy(port, &results[1], cb, data);
if (ret)
break;
@@ -2106,7 +2211,7 @@ int b53_fdb_dump(struct dsa_switch *ds, int port,
break;
}
- } while (count++ < b53_max_arl_entries(priv) / 2);
+ } while (count++ < b53_max_arl_entries(priv) / results_per_hit);
mutex_unlock(&priv->arl_mutex);
@@ -2318,7 +2423,7 @@ int b53_br_flags_pre(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack)
{
struct b53_device *dev = ds->priv;
- unsigned long mask = (BR_FLOOD | BR_MCAST_FLOOD);
+ unsigned long mask = (BR_FLOOD | BR_MCAST_FLOOD | BR_ISOLATED);
if (!is5325(dev))
mask |= BR_LEARNING;
@@ -2343,6 +2448,9 @@ int b53_br_flags(struct dsa_switch *ds, int port,
if (flags.mask & BR_LEARNING)
b53_port_set_learning(ds->priv, port,
!!(flags.val & BR_LEARNING));
+ if (flags.mask & BR_ISOLATED)
+ b53_port_set_isolated(ds->priv, port,
+ !!(flags.val & BR_ISOLATED));
return 0;
}
@@ -2623,6 +2731,30 @@ static const struct dsa_switch_ops b53_switch_ops = {
.port_change_mtu = b53_change_mtu,
};
+static const struct b53_arl_ops b53_arl_ops_25 = {
+ .arl_read_entry = b53_arl_read_entry_25,
+ .arl_write_entry = b53_arl_write_entry_25,
+ .arl_search_read = b53_arl_search_read_25,
+};
+
+static const struct b53_arl_ops b53_arl_ops_89 = {
+ .arl_read_entry = b53_arl_read_entry_89,
+ .arl_write_entry = b53_arl_write_entry_89,
+ .arl_search_read = b53_arl_search_read_89,
+};
+
+static const struct b53_arl_ops b53_arl_ops_63xx = {
+ .arl_read_entry = b53_arl_read_entry_89,
+ .arl_write_entry = b53_arl_write_entry_89,
+ .arl_search_read = b53_arl_search_read_63xx,
+};
+
+static const struct b53_arl_ops b53_arl_ops_95 = {
+ .arl_read_entry = b53_arl_read_entry_95,
+ .arl_write_entry = b53_arl_write_entry_95,
+ .arl_search_read = b53_arl_search_read_95,
+};
+
struct b53_chip_data {
u32 chip_id;
const char *dev_name;
@@ -2636,6 +2768,7 @@ struct b53_chip_data {
u8 duplex_reg;
u8 jumbo_pm_reg;
u8 jumbo_size_reg;
+ const struct b53_arl_ops *arl_ops;
};
#define B53_VTA_REGS \
@@ -2655,6 +2788,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.arl_buckets = 1024,
.imp_port = 5,
.duplex_reg = B53_DUPLEX_STAT_FE,
+ .arl_ops = &b53_arl_ops_25,
},
{
.chip_id = BCM5365_DEVICE_ID,
@@ -2665,6 +2799,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.arl_buckets = 1024,
.imp_port = 5,
.duplex_reg = B53_DUPLEX_STAT_FE,
+ .arl_ops = &b53_arl_ops_25,
},
{
.chip_id = BCM5389_DEVICE_ID,
@@ -2678,6 +2813,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_89,
},
{
.chip_id = BCM5395_DEVICE_ID,
@@ -2691,6 +2827,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM5397_DEVICE_ID,
@@ -2704,6 +2841,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_89,
},
{
.chip_id = BCM5398_DEVICE_ID,
@@ -2717,6 +2855,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_89,
},
{
.chip_id = BCM53101_DEVICE_ID,
@@ -2730,6 +2869,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53115_DEVICE_ID,
@@ -2743,6 +2883,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53125_DEVICE_ID,
@@ -2756,6 +2897,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53128_DEVICE_ID,
@@ -2769,19 +2911,21 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM63XX_DEVICE_ID,
.dev_name = "BCM63xx",
.vlans = 4096,
.enabled_ports = 0, /* pdata must provide them */
- .arl_bins = 4,
- .arl_buckets = 1024,
+ .arl_bins = 1,
+ .arl_buckets = 4096,
.imp_port = 8,
.vta_regs = B53_VTA_REGS_63XX,
.duplex_reg = B53_DUPLEX_STAT_63XX,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
+ .arl_ops = &b53_arl_ops_63xx,
},
{
.chip_id = BCM53010_DEVICE_ID,
@@ -2795,6 +2939,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53011_DEVICE_ID,
@@ -2808,6 +2953,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53012_DEVICE_ID,
@@ -2821,6 +2967,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53018_DEVICE_ID,
@@ -2834,6 +2981,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53019_DEVICE_ID,
@@ -2847,6 +2995,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM58XX_DEVICE_ID,
@@ -2860,6 +3009,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM583XX_DEVICE_ID,
@@ -2873,6 +3023,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
/* Starfighter 2 */
{
@@ -2887,6 +3038,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM7445_DEVICE_ID,
@@ -2900,6 +3052,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM7278_DEVICE_ID,
@@ -2913,6 +3066,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
{
.chip_id = BCM53134_DEVICE_ID,
@@ -2927,6 +3081,7 @@ static const struct b53_chip_data b53_switch_chips[] = {
.duplex_reg = B53_DUPLEX_STAT_GE,
.jumbo_pm_reg = B53_JUMBO_PORT_MASK,
.jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .arl_ops = &b53_arl_ops_95,
},
};
@@ -2955,6 +3110,7 @@ static int b53_switch_init(struct b53_device *dev)
dev->num_vlans = chip->vlans;
dev->num_arl_bins = chip->arl_bins;
dev->num_arl_buckets = chip->arl_buckets;
+ dev->arl_ops = chip->arl_ops;
break;
}
}
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index 458775f95164..bd6849e5bb93 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -58,6 +58,17 @@ struct b53_io_ops {
bool link_up);
};
+struct b53_arl_entry;
+
+struct b53_arl_ops {
+ void (*arl_read_entry)(struct b53_device *dev,
+ struct b53_arl_entry *ent, u8 idx);
+ void (*arl_write_entry)(struct b53_device *dev,
+ const struct b53_arl_entry *ent, u8 idx);
+ void (*arl_search_read)(struct b53_device *dev, u8 idx,
+ struct b53_arl_entry *ent);
+};
+
#define B53_INVALID_LANE 0xff
enum {
@@ -127,6 +138,7 @@ struct b53_device {
struct mutex stats_mutex;
struct mutex arl_mutex;
const struct b53_io_ops *ops;
+ const struct b53_arl_ops *arl_ops;
/* chip specific data */
u32 chip_id;
@@ -329,16 +341,30 @@ static inline void b53_arl_to_entry(struct b53_arl_entry *ent,
}
static inline void b53_arl_to_entry_25(struct b53_arl_entry *ent,
- u64 mac_vid)
+ u64 mac_vid, u8 vid_entry)
{
memset(ent, 0, sizeof(*ent));
- ent->port = (mac_vid >> ARLTBL_DATA_PORT_ID_S_25) &
- ARLTBL_DATA_PORT_ID_MASK_25;
ent->is_valid = !!(mac_vid & ARLTBL_VALID_25);
ent->is_age = !!(mac_vid & ARLTBL_AGE_25);
ent->is_static = !!(mac_vid & ARLTBL_STATIC_25);
u64_to_ether_addr(mac_vid, ent->mac);
- ent->vid = mac_vid >> ARLTBL_VID_S_65;
+ ent->port = (mac_vid & ARLTBL_DATA_PORT_ID_MASK_25) >>
+ ARLTBL_DATA_PORT_ID_S_25;
+ if (is_unicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT)
+ ent->port = B53_CPU_PORT_25;
+ ent->vid = vid_entry;
+}
+
+static inline void b53_arl_to_entry_89(struct b53_arl_entry *ent,
+ u64 mac_vid, u16 fwd_entry)
+{
+ memset(ent, 0, sizeof(*ent));
+ ent->port = fwd_entry & ARLTBL_DATA_PORT_ID_MASK_89;
+ ent->is_valid = !!(fwd_entry & ARLTBL_VALID_89);
+ ent->is_age = !!(fwd_entry & ARLTBL_AGE_89);
+ ent->is_static = !!(fwd_entry & ARLTBL_STATIC_89);
+ u64_to_ether_addr(mac_vid, ent->mac);
+ ent->vid = mac_vid >> ARLTBL_VID_S;
}
static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
@@ -355,20 +381,87 @@ static inline void b53_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
*fwd_entry |= ARLTBL_AGE;
}
-static inline void b53_arl_from_entry_25(u64 *mac_vid,
+static inline void b53_arl_from_entry_25(u64 *mac_vid, u8 *vid_entry,
const struct b53_arl_entry *ent)
{
*mac_vid = ether_addr_to_u64(ent->mac);
- *mac_vid |= (u64)(ent->port & ARLTBL_DATA_PORT_ID_MASK_25) <<
- ARLTBL_DATA_PORT_ID_S_25;
- *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK_25) <<
- ARLTBL_VID_S_65;
+ if (is_unicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT_25)
+ *mac_vid |= (u64)B53_CPU_PORT << ARLTBL_DATA_PORT_ID_S_25;
+ else
+ *mac_vid |= ((u64)ent->port << ARLTBL_DATA_PORT_ID_S_25) &
+ ARLTBL_DATA_PORT_ID_MASK_25;
if (ent->is_valid)
*mac_vid |= ARLTBL_VALID_25;
if (ent->is_static)
*mac_vid |= ARLTBL_STATIC_25;
if (ent->is_age)
*mac_vid |= ARLTBL_AGE_25;
+ *vid_entry = ent->vid;
+}
+
+static inline void b53_arl_from_entry_89(u64 *mac_vid, u32 *fwd_entry,
+ const struct b53_arl_entry *ent)
+{
+ *mac_vid = ether_addr_to_u64(ent->mac);
+ *mac_vid |= (u64)(ent->vid & ARLTBL_VID_MASK) << ARLTBL_VID_S;
+ *fwd_entry = ent->port & ARLTBL_DATA_PORT_ID_MASK_89;
+ if (ent->is_valid)
+ *fwd_entry |= ARLTBL_VALID_89;
+ if (ent->is_static)
+ *fwd_entry |= ARLTBL_STATIC_89;
+ if (ent->is_age)
+ *fwd_entry |= ARLTBL_AGE_89;
+}
+
+static inline void b53_arl_search_to_entry_25(struct b53_arl_entry *ent,
+ u64 mac_vid, u8 ext)
+{
+ memset(ent, 0, sizeof(*ent));
+ ent->is_valid = !!(mac_vid & ARLTBL_VALID_25);
+ ent->is_age = !!(mac_vid & ARLTBL_AGE_25);
+ ent->is_static = !!(mac_vid & ARLTBL_STATIC_25);
+ u64_to_ether_addr(mac_vid, ent->mac);
+ ent->vid = (mac_vid & ARL_SRCH_RSLT_VID_MASK_25) >>
+ ARL_SRCH_RSLT_VID_S_25;
+ ent->port = (mac_vid & ARL_SRCH_RSLT_PORT_ID_MASK_25) >>
+ ARL_SRCH_RSLT_PORT_ID_S_25;
+ if (is_multicast_ether_addr(ent->mac) && (ext & ARL_SRCH_RSLT_EXT_MC_MII))
+ ent->port |= BIT(B53_CPU_PORT_25);
+ else if (!is_multicast_ether_addr(ent->mac) && ent->port == B53_CPU_PORT)
+ ent->port = B53_CPU_PORT_25;
+}
+
+static inline void b53_arl_search_to_entry_63xx(struct b53_arl_entry *ent,
+ u64 mac_vid, u16 fwd_entry)
+{
+ memset(ent, 0, sizeof(*ent));
+ u64_to_ether_addr(mac_vid, ent->mac);
+ ent->vid = mac_vid >> ARLTBL_VID_S;
+
+ ent->port = fwd_entry & ARL_SRST_PORT_ID_MASK_63XX;
+ ent->port >>= 1;
+
+ ent->is_age = !!(fwd_entry & ARL_SRST_AGE_63XX);
+ ent->is_static = !!(fwd_entry & ARL_SRST_STATIC_63XX);
+ ent->is_valid = 1;
+}
+
+static inline void b53_arl_read_entry(struct b53_device *dev,
+ struct b53_arl_entry *ent, u8 idx)
+{
+ dev->arl_ops->arl_read_entry(dev, ent, idx);
+}
+
+static inline void b53_arl_write_entry(struct b53_device *dev,
+ const struct b53_arl_entry *ent, u8 idx)
+{
+ dev->arl_ops->arl_write_entry(dev, ent, idx);
+}
+
+static inline void b53_arl_search_read(struct b53_device *dev, u8 idx,
+ struct b53_arl_entry *ent)
+{
+ dev->arl_ops->arl_search_read(dev, idx, ent);
}
#ifdef CONFIG_BCM47XX
diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h
index 309fe0e46dad..54a278db67c9 100644
--- a/drivers/net/dsa/b53/b53_regs.h
+++ b/drivers/net/dsa/b53/b53_regs.h
@@ -111,8 +111,7 @@
/* IP Multicast control (8 bit) */
#define B53_IP_MULTICAST_CTRL 0x21
-#define B53_IP_MCAST_25 BIT(0)
-#define B53_IPMC_FWD_EN BIT(1)
+#define B53_IP_MC BIT(0)
#define B53_UC_FWD_EN BIT(6)
#define B53_MC_FWD_EN BIT(7)
@@ -120,6 +119,10 @@
#define B53_SWITCH_CTRL 0x22
#define B53_MII_DUMB_FWDG_EN BIT(6)
+/* Protected Port Selection (16 bit) */
+#define B53_PROTECTED_PORT_SEL 0x24
+#define B53_PROTECTED_PORT_SEL_25 0x26
+
/* (16 bit) */
#define B53_UC_FLOOD_MASK 0x32
#define B53_MC_FLOOD_MASK 0x34
@@ -326,11 +329,9 @@
#define B53_ARLTBL_MAC_VID_ENTRY(n) ((0x10 * (n)) + 0x10)
#define ARLTBL_MAC_MASK 0xffffffffffffULL
#define ARLTBL_VID_S 48
-#define ARLTBL_VID_MASK_25 0xff
#define ARLTBL_VID_MASK 0xfff
#define ARLTBL_DATA_PORT_ID_S_25 48
-#define ARLTBL_DATA_PORT_ID_MASK_25 0xf
-#define ARLTBL_VID_S_65 53
+#define ARLTBL_DATA_PORT_ID_MASK_25 GENMASK_ULL(53, 48)
#define ARLTBL_AGE_25 BIT_ULL(61)
#define ARLTBL_STATIC_25 BIT_ULL(62)
#define ARLTBL_VALID_25 BIT_ULL(63)
@@ -343,12 +344,23 @@
#define ARLTBL_STATIC BIT(15)
#define ARLTBL_VALID BIT(16)
+/* BCM5389 ARL Table Data Entry N Register format (16 bit) */
+#define ARLTBL_DATA_PORT_ID_MASK_89 GENMASK(8, 0)
+#define ARLTBL_TC_MASK_89 GENMASK(12, 10)
+#define ARLTBL_AGE_89 BIT(13)
+#define ARLTBL_STATIC_89 BIT(14)
+#define ARLTBL_VALID_89 BIT(15)
+
+/* BCM5325/BCM565 ARL Table VID Entry N Registers (8 bit) */
+#define B53_ARLTBL_VID_ENTRY_25(n) ((0x2 * (n)) + 0x30)
+
/* Maximum number of bin entries in the ARL for all switches */
#define B53_ARLTBL_MAX_BIN_ENTRIES 4
/* ARL Search Control Register (8 bit) */
#define B53_ARL_SRCH_CTL 0x50
#define B53_ARL_SRCH_CTL_25 0x20
+#define B53_ARL_SRCH_CTL_89 0x30
#define ARL_SRCH_VLID BIT(0)
#define ARL_SRCH_STDN BIT(7)
@@ -356,22 +368,42 @@
#define B53_ARL_SRCH_ADDR 0x51
#define B53_ARL_SRCH_ADDR_25 0x22
#define B53_ARL_SRCH_ADDR_65 0x24
+#define B53_ARL_SRCH_ADDR_89 0x31
+#define B53_ARL_SRCH_ADDR_63XX 0x32
#define ARL_ADDR_MASK GENMASK(14, 0)
/* ARL Search MAC/VID Result (64 bit) */
#define B53_ARL_SRCH_RSTL_0_MACVID 0x60
+#define B53_ARL_SRCH_RSLT_MACVID_89 0x33
+#define B53_ARL_SRCH_RSLT_MACVID_63XX 0x34
-/* Single register search result on 5325 */
+/* Single register search result on 5325/5365 */
#define B53_ARL_SRCH_RSTL_0_MACVID_25 0x24
-/* Single register search result on 5365 */
-#define B53_ARL_SRCH_RSTL_0_MACVID_65 0x30
+#define ARL_SRCH_RSLT_PORT_ID_S_25 48
+#define ARL_SRCH_RSLT_PORT_ID_MASK_25 GENMASK_ULL(52, 48)
+#define ARL_SRCH_RSLT_VID_S_25 53
+#define ARL_SRCH_RSLT_VID_MASK_25 GENMASK_ULL(60, 53)
+
+/* BCM5325/5365 Search result extend register (8 bit) */
+#define B53_ARL_SRCH_RSLT_EXT_25 0x2c
+#define ARL_SRCH_RSLT_EXT_MC_MII BIT(2)
/* ARL Search Data Result (32 bit) */
#define B53_ARL_SRCH_RSTL_0 0x68
+/* BCM5389 ARL Search Data Result (16 bit) */
+#define B53_ARL_SRCH_RSLT_89 0x3b
+
#define B53_ARL_SRCH_RSTL_MACVID(x) (B53_ARL_SRCH_RSTL_0_MACVID + ((x) * 0x10))
#define B53_ARL_SRCH_RSTL(x) (B53_ARL_SRCH_RSTL_0 + ((x) * 0x10))
+/* 63XX ARL Search Data Result (16 bit) */
+#define B53_ARL_SRCH_RSLT_63XX 0x3c
+#define ARL_SRST_PORT_ID_MASK_63XX GENMASK(9, 1)
+#define ARL_SRST_TC_MASK_63XX GENMASK(13, 11)
+#define ARL_SRST_AGE_63XX BIT(14)
+#define ARL_SRST_STATIC_63XX BIT(15)
+
/*************************************************************************
* IEEE 802.1X Registers
*************************************************************************/
diff --git a/drivers/net/dsa/dsa_loop.c b/drivers/net/dsa/dsa_loop.c
index 650d93226d9f..4a416f2717ba 100644
--- a/drivers/net/dsa/dsa_loop.c
+++ b/drivers/net/dsa/dsa_loop.c
@@ -441,11 +441,6 @@ out:
static int __init dsa_loop_init(void)
{
- struct fixed_phy_status status = {
- .link = 1,
- .speed = SPEED_100,
- .duplex = DUPLEX_FULL,
- };
unsigned int i;
int ret;
@@ -454,7 +449,7 @@ static int __init dsa_loop_init(void)
return ret;
for (i = 0; i < NUM_FIXED_PHYS; i++)
- phydevs[i] = fixed_phy_register(&status, NULL);
+ phydevs[i] = fixed_phy_register_100fd();
ret = mdio_driver_register(&dsa_loop_drv);
if (ret) {
diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c
index e0b4758ca583..dd5f263ab984 100644
--- a/drivers/net/dsa/hirschmann/hellcreek.c
+++ b/drivers/net/dsa/hirschmann/hellcreek.c
@@ -1926,6 +1926,8 @@ static const struct dsa_switch_ops hellcreek_ds_ops = {
.port_vlan_filtering = hellcreek_vlan_filtering,
.setup = hellcreek_setup,
.teardown = hellcreek_teardown,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
};
static int hellcreek_probe(struct platform_device *pdev)
diff --git a/drivers/net/dsa/hirschmann/hellcreek_ptp.c b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
index bfe21f9f7dcd..cb23bea9c21b 100644
--- a/drivers/net/dsa/hirschmann/hellcreek_ptp.c
+++ b/drivers/net/dsa/hirschmann/hellcreek_ptp.c
@@ -376,8 +376,18 @@ static int hellcreek_led_setup(struct hellcreek *hellcreek)
hellcreek_set_brightness(hellcreek, STATUS_OUT_IS_GM, 1);
/* Register both leds */
- led_classdev_register(hellcreek->dev, &hellcreek->led_sync_good);
- led_classdev_register(hellcreek->dev, &hellcreek->led_is_gm);
+ ret = led_classdev_register(hellcreek->dev, &hellcreek->led_sync_good);
+ if (ret) {
+ dev_err(hellcreek->dev, "Failed to register sync_good LED\n");
+ goto out;
+ }
+
+ ret = led_classdev_register(hellcreek->dev, &hellcreek->led_is_gm);
+ if (ret) {
+ dev_err(hellcreek->dev, "Failed to register is_gm LED\n");
+ led_classdev_unregister(&hellcreek->led_sync_good);
+ goto out;
+ }
ret = 0;
diff --git a/drivers/net/dsa/ks8995.c b/drivers/net/dsa/ks8995.c
index 5c4c83e00477..77d8b842693c 100644
--- a/drivers/net/dsa/ks8995.c
+++ b/drivers/net/dsa/ks8995.c
@@ -203,13 +203,13 @@ static const struct spi_device_id ks8995_id[] = {
};
MODULE_DEVICE_TABLE(spi, ks8995_id);
-static const struct of_device_id ks8895_spi_of_match[] = {
+static const struct of_device_id ks8995_spi_of_match[] = {
{ .compatible = "micrel,ks8995" },
{ .compatible = "micrel,ksz8864" },
{ .compatible = "micrel,ksz8795" },
{ },
};
-MODULE_DEVICE_TABLE(of, ks8895_spi_of_match);
+MODULE_DEVICE_TABLE(of, ks8995_spi_of_match);
static inline u8 get_chip_id(u8 val)
{
@@ -842,7 +842,7 @@ static void ks8995_remove(struct spi_device *spi)
static struct spi_driver ks8995_driver = {
.driver = {
.name = "spi-ks8995",
- .of_match_table = ks8895_spi_of_match,
+ .of_match_table = ks8995_spi_of_match,
},
.probe = ks8995_probe,
.remove = ks8995_remove,
diff --git a/drivers/net/dsa/lantiq/Kconfig b/drivers/net/dsa/lantiq/Kconfig
index 1cb053c823f7..4a9771be5d58 100644
--- a/drivers/net/dsa/lantiq/Kconfig
+++ b/drivers/net/dsa/lantiq/Kconfig
@@ -1,7 +1,24 @@
+config NET_DSA_LANTIQ_COMMON
+ tristate
+ select REGMAP
+
config NET_DSA_LANTIQ_GSWIP
tristate "Lantiq / Intel GSWIP"
depends on HAS_IOMEM
select NET_DSA_TAG_GSWIP
+ select NET_DSA_LANTIQ_COMMON
help
This enables support for the Lantiq / Intel GSWIP 2.1 found in
the xrx200 / VR9 SoC.
+
+config NET_DSA_MXL_GSW1XX
+ tristate "MaxLinear GSW1xx Ethernet switch support"
+ select NET_DSA_TAG_MXL_GSW1XX
+ select NET_DSA_LANTIQ_COMMON
+ help
+ This enables support for the MaxLinear GSW1xx family of 1GE switches
+ GSW120 4 port, 2 PHYs, RGMII & SGMII/2500Base-X
+ GSW125 4 port, 2 PHYs, RGMII & SGMII/2500Base-X, industrial temperature
+ GSW140 6 port, 4 PHYs, RGMII & SGMII/2500Base-X
+ GSW141 6 port, 4 PHYs, RGMII & SGMII
+ GSW145 6 port, 4 PHYs, RGMII & SGMII/2500Base-X, industrial temperature
diff --git a/drivers/net/dsa/lantiq/Makefile b/drivers/net/dsa/lantiq/Makefile
index 849f85ebebd6..85fce605310b 100644
--- a/drivers/net/dsa/lantiq/Makefile
+++ b/drivers/net/dsa/lantiq/Makefile
@@ -1 +1,3 @@
obj-$(CONFIG_NET_DSA_LANTIQ_GSWIP) += lantiq_gswip.o
+obj-$(CONFIG_NET_DSA_LANTIQ_COMMON) += lantiq_gswip_common.o
+obj-$(CONFIG_NET_DSA_MXL_GSW1XX) += mxl-gsw1xx.o
diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.c b/drivers/net/dsa/lantiq/lantiq_gswip.c
index 2169c0814a48..57dd063c0740 100644
--- a/drivers/net/dsa/lantiq/lantiq_gswip.c
+++ b/drivers/net/dsa/lantiq/lantiq_gswip.c
@@ -2,1282 +2,33 @@
/*
* Lantiq / Intel GSWIP switch driver for VRX200, xRX300 and xRX330 SoCs
*
- * Copyright (C) 2010 Lantiq Deutschland
- * Copyright (C) 2012 John Crispin <john@phrozen.org>
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
* Copyright (C) 2017 - 2019 Hauke Mehrtens <hauke@hauke-m.de>
- *
- * The VLAN and bridge model the GSWIP hardware uses does not directly
- * matches the model DSA uses.
- *
- * The hardware has 64 possible table entries for bridges with one VLAN
- * ID, one flow id and a list of ports for each bridge. All entries which
- * match the same flow ID are combined in the mac learning table, they
- * act as one global bridge.
- * The hardware does not support VLAN filter on the port, but on the
- * bridge, this driver converts the DSA model to the hardware.
- *
- * The CPU gets all the exception frames which do not match any forwarding
- * rule and the CPU port is also added to all bridges. This makes it possible
- * to handle all the special cases easily in software.
- * At the initialization the driver allocates one bridge table entry for
- * each switch port which is used when the port is used without an
- * explicit bridge. This prevents the frames from being forwarded
- * between all LAN ports by default.
+ * Copyright (C) 2012 John Crispin <john@phrozen.org>
+ * Copyright (C) 2010 Lantiq Deutschland
*/
#include "lantiq_gswip.h"
#include "lantiq_pce.h"
+#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/etherdevice.h>
#include <linux/firmware.h>
-#include <linux/if_bridge.h>
-#include <linux/if_vlan.h>
-#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
-#include <linux/of_mdio.h>
-#include <linux/of_net.h>
#include <linux/of_platform.h>
-#include <linux/phy.h>
-#include <linux/phylink.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
#include <dt-bindings/mips/lantiq_rcu_gphy.h>
+#include <net/dsa.h>
+
struct xway_gphy_match_data {
char *fe_firmware_name;
char *ge_firmware_name;
};
-struct gswip_pce_table_entry {
- u16 index; // PCE_TBL_ADDR.ADDR = pData->table_index
- u16 table; // PCE_TBL_CTRL.ADDR = pData->table
- u16 key[8];
- u16 val[5];
- u16 mask;
- u8 gmap;
- bool type;
- bool valid;
- bool key_mode;
-};
-
-struct gswip_rmon_cnt_desc {
- unsigned int size;
- unsigned int offset;
- const char *name;
-};
-
-#define MIB_DESC(_size, _offset, _name) {.size = _size, .offset = _offset, .name = _name}
-
-static const struct gswip_rmon_cnt_desc gswip_rmon_cnt[] = {
- /** Receive Packet Count (only packets that are accepted and not discarded). */
- MIB_DESC(1, 0x1F, "RxGoodPkts"),
- MIB_DESC(1, 0x23, "RxUnicastPkts"),
- MIB_DESC(1, 0x22, "RxMulticastPkts"),
- MIB_DESC(1, 0x21, "RxFCSErrorPkts"),
- MIB_DESC(1, 0x1D, "RxUnderSizeGoodPkts"),
- MIB_DESC(1, 0x1E, "RxUnderSizeErrorPkts"),
- MIB_DESC(1, 0x1B, "RxOversizeGoodPkts"),
- MIB_DESC(1, 0x1C, "RxOversizeErrorPkts"),
- MIB_DESC(1, 0x20, "RxGoodPausePkts"),
- MIB_DESC(1, 0x1A, "RxAlignErrorPkts"),
- MIB_DESC(1, 0x12, "Rx64BytePkts"),
- MIB_DESC(1, 0x13, "Rx127BytePkts"),
- MIB_DESC(1, 0x14, "Rx255BytePkts"),
- MIB_DESC(1, 0x15, "Rx511BytePkts"),
- MIB_DESC(1, 0x16, "Rx1023BytePkts"),
- /** Receive Size 1024-1522 (or more, if configured) Packet Count. */
- MIB_DESC(1, 0x17, "RxMaxBytePkts"),
- MIB_DESC(1, 0x18, "RxDroppedPkts"),
- MIB_DESC(1, 0x19, "RxFilteredPkts"),
- MIB_DESC(2, 0x24, "RxGoodBytes"),
- MIB_DESC(2, 0x26, "RxBadBytes"),
- MIB_DESC(1, 0x11, "TxAcmDroppedPkts"),
- MIB_DESC(1, 0x0C, "TxGoodPkts"),
- MIB_DESC(1, 0x06, "TxUnicastPkts"),
- MIB_DESC(1, 0x07, "TxMulticastPkts"),
- MIB_DESC(1, 0x00, "Tx64BytePkts"),
- MIB_DESC(1, 0x01, "Tx127BytePkts"),
- MIB_DESC(1, 0x02, "Tx255BytePkts"),
- MIB_DESC(1, 0x03, "Tx511BytePkts"),
- MIB_DESC(1, 0x04, "Tx1023BytePkts"),
- /** Transmit Size 1024-1522 (or more, if configured) Packet Count. */
- MIB_DESC(1, 0x05, "TxMaxBytePkts"),
- MIB_DESC(1, 0x08, "TxSingleCollCount"),
- MIB_DESC(1, 0x09, "TxMultCollCount"),
- MIB_DESC(1, 0x0A, "TxLateCollCount"),
- MIB_DESC(1, 0x0B, "TxExcessCollCount"),
- MIB_DESC(1, 0x0D, "TxPauseCount"),
- MIB_DESC(1, 0x10, "TxDroppedPkts"),
- MIB_DESC(2, 0x0E, "TxGoodBytes"),
-};
-
-static u32 gswip_switch_r(struct gswip_priv *priv, u32 offset)
-{
- return __raw_readl(priv->gswip + (offset * 4));
-}
-
-static void gswip_switch_w(struct gswip_priv *priv, u32 val, u32 offset)
-{
- __raw_writel(val, priv->gswip + (offset * 4));
-}
-
-static void gswip_switch_mask(struct gswip_priv *priv, u32 clear, u32 set,
- u32 offset)
-{
- u32 val = gswip_switch_r(priv, offset);
-
- val &= ~(clear);
- val |= set;
- gswip_switch_w(priv, val, offset);
-}
-
-static u32 gswip_switch_r_timeout(struct gswip_priv *priv, u32 offset,
- u32 cleared)
-{
- u32 val;
-
- return readx_poll_timeout(__raw_readl, priv->gswip + (offset * 4), val,
- (val & cleared) == 0, 20, 50000);
-}
-
-static u32 gswip_mdio_r(struct gswip_priv *priv, u32 offset)
-{
- return __raw_readl(priv->mdio + (offset * 4));
-}
-
-static void gswip_mdio_w(struct gswip_priv *priv, u32 val, u32 offset)
-{
- __raw_writel(val, priv->mdio + (offset * 4));
-}
-
-static void gswip_mdio_mask(struct gswip_priv *priv, u32 clear, u32 set,
- u32 offset)
-{
- u32 val = gswip_mdio_r(priv, offset);
-
- val &= ~(clear);
- val |= set;
- gswip_mdio_w(priv, val, offset);
-}
-
-static u32 gswip_mii_r(struct gswip_priv *priv, u32 offset)
-{
- return __raw_readl(priv->mii + (offset * 4));
-}
-
-static void gswip_mii_w(struct gswip_priv *priv, u32 val, u32 offset)
-{
- __raw_writel(val, priv->mii + (offset * 4));
-}
-
-static void gswip_mii_mask(struct gswip_priv *priv, u32 clear, u32 set,
- u32 offset)
-{
- u32 val = gswip_mii_r(priv, offset);
-
- val &= ~(clear);
- val |= set;
- gswip_mii_w(priv, val, offset);
-}
-
-static void gswip_mii_mask_cfg(struct gswip_priv *priv, u32 clear, u32 set,
- int port)
-{
- int reg_port;
-
- /* MII_CFG register only exists for MII ports */
- if (!(priv->hw_info->mii_ports & BIT(port)))
- return;
-
- reg_port = port + priv->hw_info->mii_port_reg_offset;
-
- gswip_mii_mask(priv, clear, set, GSWIP_MII_CFGp(reg_port));
-}
-
-static void gswip_mii_mask_pcdu(struct gswip_priv *priv, u32 clear, u32 set,
- int port)
-{
- int reg_port;
-
- /* MII_PCDU register only exists for MII ports */
- if (!(priv->hw_info->mii_ports & BIT(port)))
- return;
-
- reg_port = port + priv->hw_info->mii_port_reg_offset;
-
- switch (reg_port) {
- case 0:
- gswip_mii_mask(priv, clear, set, GSWIP_MII_PCDU0);
- break;
- case 1:
- gswip_mii_mask(priv, clear, set, GSWIP_MII_PCDU1);
- break;
- case 5:
- gswip_mii_mask(priv, clear, set, GSWIP_MII_PCDU5);
- break;
- }
-}
-
-static int gswip_mdio_poll(struct gswip_priv *priv)
-{
- int cnt = 100;
-
- while (likely(cnt--)) {
- u32 ctrl = gswip_mdio_r(priv, GSWIP_MDIO_CTRL);
-
- if ((ctrl & GSWIP_MDIO_CTRL_BUSY) == 0)
- return 0;
- usleep_range(20, 40);
- }
-
- return -ETIMEDOUT;
-}
-
-static int gswip_mdio_wr(struct mii_bus *bus, int addr, int reg, u16 val)
-{
- struct gswip_priv *priv = bus->priv;
- int err;
-
- err = gswip_mdio_poll(priv);
- if (err) {
- dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n");
- return err;
- }
-
- gswip_mdio_w(priv, val, GSWIP_MDIO_WRITE);
- gswip_mdio_w(priv, GSWIP_MDIO_CTRL_BUSY | GSWIP_MDIO_CTRL_WR |
- ((addr & GSWIP_MDIO_CTRL_PHYAD_MASK) << GSWIP_MDIO_CTRL_PHYAD_SHIFT) |
- (reg & GSWIP_MDIO_CTRL_REGAD_MASK),
- GSWIP_MDIO_CTRL);
-
- return 0;
-}
-
-static int gswip_mdio_rd(struct mii_bus *bus, int addr, int reg)
-{
- struct gswip_priv *priv = bus->priv;
- int err;
-
- err = gswip_mdio_poll(priv);
- if (err) {
- dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n");
- return err;
- }
-
- gswip_mdio_w(priv, GSWIP_MDIO_CTRL_BUSY | GSWIP_MDIO_CTRL_RD |
- ((addr & GSWIP_MDIO_CTRL_PHYAD_MASK) << GSWIP_MDIO_CTRL_PHYAD_SHIFT) |
- (reg & GSWIP_MDIO_CTRL_REGAD_MASK),
- GSWIP_MDIO_CTRL);
-
- err = gswip_mdio_poll(priv);
- if (err) {
- dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n");
- return err;
- }
-
- return gswip_mdio_r(priv, GSWIP_MDIO_READ);
-}
-
-static int gswip_mdio(struct gswip_priv *priv)
-{
- struct device_node *mdio_np, *switch_np = priv->dev->of_node;
- struct device *dev = priv->dev;
- struct mii_bus *bus;
- int err = 0;
-
- mdio_np = of_get_compatible_child(switch_np, "lantiq,xrx200-mdio");
- if (!mdio_np)
- mdio_np = of_get_child_by_name(switch_np, "mdio");
-
- if (!of_device_is_available(mdio_np))
- goto out_put_node;
-
- bus = devm_mdiobus_alloc(dev);
- if (!bus) {
- err = -ENOMEM;
- goto out_put_node;
- }
-
- bus->priv = priv;
- bus->read = gswip_mdio_rd;
- bus->write = gswip_mdio_wr;
- bus->name = "lantiq,xrx200-mdio";
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(priv->dev));
- bus->parent = priv->dev;
-
- err = devm_of_mdiobus_register(dev, bus, mdio_np);
-
-out_put_node:
- of_node_put(mdio_np);
-
- return err;
-}
-
-static int gswip_pce_table_entry_read(struct gswip_priv *priv,
- struct gswip_pce_table_entry *tbl)
-{
- int i;
- int err;
- u16 crtl;
- u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSRD :
- GSWIP_PCE_TBL_CTRL_OPMOD_ADRD;
-
- mutex_lock(&priv->pce_table_lock);
-
- err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
- GSWIP_PCE_TBL_CTRL_BAS);
- if (err) {
- mutex_unlock(&priv->pce_table_lock);
- return err;
- }
-
- gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR);
- gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
- GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
- tbl->table | addr_mode | GSWIP_PCE_TBL_CTRL_BAS,
- GSWIP_PCE_TBL_CTRL);
-
- err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
- GSWIP_PCE_TBL_CTRL_BAS);
- if (err) {
- mutex_unlock(&priv->pce_table_lock);
- return err;
- }
-
- for (i = 0; i < ARRAY_SIZE(tbl->key); i++)
- tbl->key[i] = gswip_switch_r(priv, GSWIP_PCE_TBL_KEY(i));
-
- for (i = 0; i < ARRAY_SIZE(tbl->val); i++)
- tbl->val[i] = gswip_switch_r(priv, GSWIP_PCE_TBL_VAL(i));
-
- tbl->mask = gswip_switch_r(priv, GSWIP_PCE_TBL_MASK);
-
- crtl = gswip_switch_r(priv, GSWIP_PCE_TBL_CTRL);
-
- tbl->type = !!(crtl & GSWIP_PCE_TBL_CTRL_TYPE);
- tbl->valid = !!(crtl & GSWIP_PCE_TBL_CTRL_VLD);
- tbl->gmap = (crtl & GSWIP_PCE_TBL_CTRL_GMAP_MASK) >> 7;
-
- mutex_unlock(&priv->pce_table_lock);
-
- return 0;
-}
-
-static int gswip_pce_table_entry_write(struct gswip_priv *priv,
- struct gswip_pce_table_entry *tbl)
-{
- int i;
- int err;
- u16 crtl;
- u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSWR :
- GSWIP_PCE_TBL_CTRL_OPMOD_ADWR;
-
- mutex_lock(&priv->pce_table_lock);
-
- err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
- GSWIP_PCE_TBL_CTRL_BAS);
- if (err) {
- mutex_unlock(&priv->pce_table_lock);
- return err;
- }
-
- gswip_switch_w(priv, tbl->index, GSWIP_PCE_TBL_ADDR);
- gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
- GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
- tbl->table | addr_mode,
- GSWIP_PCE_TBL_CTRL);
-
- for (i = 0; i < ARRAY_SIZE(tbl->key); i++)
- gswip_switch_w(priv, tbl->key[i], GSWIP_PCE_TBL_KEY(i));
-
- for (i = 0; i < ARRAY_SIZE(tbl->val); i++)
- gswip_switch_w(priv, tbl->val[i], GSWIP_PCE_TBL_VAL(i));
-
- gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
- GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
- tbl->table | addr_mode,
- GSWIP_PCE_TBL_CTRL);
-
- gswip_switch_w(priv, tbl->mask, GSWIP_PCE_TBL_MASK);
-
- crtl = gswip_switch_r(priv, GSWIP_PCE_TBL_CTRL);
- crtl &= ~(GSWIP_PCE_TBL_CTRL_TYPE | GSWIP_PCE_TBL_CTRL_VLD |
- GSWIP_PCE_TBL_CTRL_GMAP_MASK);
- if (tbl->type)
- crtl |= GSWIP_PCE_TBL_CTRL_TYPE;
- if (tbl->valid)
- crtl |= GSWIP_PCE_TBL_CTRL_VLD;
- crtl |= (tbl->gmap << 7) & GSWIP_PCE_TBL_CTRL_GMAP_MASK;
- crtl |= GSWIP_PCE_TBL_CTRL_BAS;
- gswip_switch_w(priv, crtl, GSWIP_PCE_TBL_CTRL);
-
- err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
- GSWIP_PCE_TBL_CTRL_BAS);
-
- mutex_unlock(&priv->pce_table_lock);
-
- return err;
-}
-
-/* Add the LAN port into a bridge with the CPU port by
- * default. This prevents automatic forwarding of
- * packages between the LAN ports when no explicit
- * bridge is configured.
- */
-static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add)
-{
- struct gswip_pce_table_entry vlan_active = {0,};
- struct gswip_pce_table_entry vlan_mapping = {0,};
- int err;
-
- vlan_active.index = port + 1;
- vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
- vlan_active.key[0] = 0; /* vid */
- vlan_active.val[0] = port + 1 /* fid */;
- vlan_active.valid = add;
- err = gswip_pce_table_entry_write(priv, &vlan_active);
- if (err) {
- dev_err(priv->dev, "failed to write active VLAN: %d\n", err);
- return err;
- }
-
- if (!add)
- return 0;
-
- vlan_mapping.index = port + 1;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- vlan_mapping.val[0] = 0 /* vid */;
- vlan_mapping.val[1] = BIT(port) | dsa_cpu_ports(priv->ds);
- vlan_mapping.val[2] = 0;
- err = gswip_pce_table_entry_write(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
- return err;
- }
-
- return 0;
-}
-
-static int gswip_port_setup(struct dsa_switch *ds, int port)
-{
- struct gswip_priv *priv = ds->priv;
- int err;
-
- if (!dsa_is_cpu_port(ds, port)) {
- err = gswip_add_single_port_br(priv, port, true);
- if (err)
- return err;
- }
-
- return 0;
-}
-
-static int gswip_port_enable(struct dsa_switch *ds, int port,
- struct phy_device *phydev)
-{
- struct gswip_priv *priv = ds->priv;
-
- if (!dsa_is_cpu_port(ds, port)) {
- u32 mdio_phy = 0;
-
- if (phydev)
- mdio_phy = phydev->mdio.addr & GSWIP_MDIO_PHY_ADDR_MASK;
-
- gswip_mdio_mask(priv, GSWIP_MDIO_PHY_ADDR_MASK, mdio_phy,
- GSWIP_MDIO_PHYp(port));
- }
-
- /* RMON Counter Enable for port */
- gswip_switch_w(priv, GSWIP_BM_PCFG_CNTEN, GSWIP_BM_PCFGp(port));
-
- /* enable port fetch/store dma & VLAN Modification */
- gswip_switch_mask(priv, 0, GSWIP_FDMA_PCTRL_EN |
- GSWIP_FDMA_PCTRL_VLANMOD_BOTH,
- GSWIP_FDMA_PCTRLp(port));
- gswip_switch_mask(priv, 0, GSWIP_SDMA_PCTRL_EN,
- GSWIP_SDMA_PCTRLp(port));
-
- return 0;
-}
-
-static void gswip_port_disable(struct dsa_switch *ds, int port)
-{
- struct gswip_priv *priv = ds->priv;
-
- gswip_switch_mask(priv, GSWIP_FDMA_PCTRL_EN, 0,
- GSWIP_FDMA_PCTRLp(port));
- gswip_switch_mask(priv, GSWIP_SDMA_PCTRL_EN, 0,
- GSWIP_SDMA_PCTRLp(port));
-}
-
-static int gswip_pce_load_microcode(struct gswip_priv *priv)
-{
- int i;
- int err;
-
- gswip_switch_mask(priv, GSWIP_PCE_TBL_CTRL_ADDR_MASK |
- GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
- GSWIP_PCE_TBL_CTRL_OPMOD_ADWR, GSWIP_PCE_TBL_CTRL);
- gswip_switch_w(priv, 0, GSWIP_PCE_TBL_MASK);
-
- for (i = 0; i < priv->hw_info->pce_microcode_size; i++) {
- gswip_switch_w(priv, i, GSWIP_PCE_TBL_ADDR);
- gswip_switch_w(priv, (*priv->hw_info->pce_microcode)[i].val_0,
- GSWIP_PCE_TBL_VAL(0));
- gswip_switch_w(priv, (*priv->hw_info->pce_microcode)[i].val_1,
- GSWIP_PCE_TBL_VAL(1));
- gswip_switch_w(priv, (*priv->hw_info->pce_microcode)[i].val_2,
- GSWIP_PCE_TBL_VAL(2));
- gswip_switch_w(priv, (*priv->hw_info->pce_microcode)[i].val_3,
- GSWIP_PCE_TBL_VAL(3));
-
- /* start the table access: */
- gswip_switch_mask(priv, 0, GSWIP_PCE_TBL_CTRL_BAS,
- GSWIP_PCE_TBL_CTRL);
- err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
- GSWIP_PCE_TBL_CTRL_BAS);
- if (err)
- return err;
- }
-
- /* tell the switch that the microcode is loaded */
- gswip_switch_mask(priv, 0, GSWIP_PCE_GCTRL_0_MC_VALID,
- GSWIP_PCE_GCTRL_0);
-
- return 0;
-}
-
-static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,
- bool vlan_filtering,
- struct netlink_ext_ack *extack)
-{
- struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
- struct gswip_priv *priv = ds->priv;
-
- /* Do not allow changing the VLAN filtering options while in bridge */
- if (bridge && !!(priv->port_vlan_filter & BIT(port)) != vlan_filtering) {
- NL_SET_ERR_MSG_MOD(extack,
- "Dynamic toggling of vlan_filtering not supported");
- return -EIO;
- }
-
- if (vlan_filtering) {
- /* Use tag based VLAN */
- gswip_switch_mask(priv,
- GSWIP_PCE_VCTRL_VSR,
- GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR |
- GSWIP_PCE_VCTRL_VEMR,
- GSWIP_PCE_VCTRL(port));
- gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_TVM, 0,
- GSWIP_PCE_PCTRL_0p(port));
- } else {
- /* Use port based VLAN */
- gswip_switch_mask(priv,
- GSWIP_PCE_VCTRL_UVR | GSWIP_PCE_VCTRL_VIMR |
- GSWIP_PCE_VCTRL_VEMR,
- GSWIP_PCE_VCTRL_VSR,
- GSWIP_PCE_VCTRL(port));
- gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_TVM,
- GSWIP_PCE_PCTRL_0p(port));
- }
-
- return 0;
-}
-
-static int gswip_setup(struct dsa_switch *ds)
-{
- unsigned int cpu_ports = dsa_cpu_ports(ds);
- struct gswip_priv *priv = ds->priv;
- struct dsa_port *cpu_dp;
- int err, i;
-
- gswip_switch_w(priv, GSWIP_SWRES_R0, GSWIP_SWRES);
- usleep_range(5000, 10000);
- gswip_switch_w(priv, 0, GSWIP_SWRES);
-
- /* disable port fetch/store dma on all ports */
- for (i = 0; i < priv->hw_info->max_ports; i++) {
- gswip_port_disable(ds, i);
- gswip_port_vlan_filtering(ds, i, false, NULL);
- }
-
- /* enable Switch */
- gswip_mdio_mask(priv, 0, GSWIP_MDIO_GLOB_ENABLE, GSWIP_MDIO_GLOB);
-
- err = gswip_pce_load_microcode(priv);
- if (err) {
- dev_err(priv->dev, "writing PCE microcode failed, %i\n", err);
- return err;
- }
-
- /* Default unknown Broadcast/Multicast/Unicast port maps */
- gswip_switch_w(priv, cpu_ports, GSWIP_PCE_PMAP1);
- gswip_switch_w(priv, cpu_ports, GSWIP_PCE_PMAP2);
- gswip_switch_w(priv, cpu_ports, GSWIP_PCE_PMAP3);
-
- /* Deactivate MDIO PHY auto polling. Some PHYs as the AR8030 have an
- * interoperability problem with this auto polling mechanism because
- * their status registers think that the link is in a different state
- * than it actually is. For the AR8030 it has the BMSR_ESTATEN bit set
- * as well as ESTATUS_1000_TFULL and ESTATUS_1000_XFULL. This makes the
- * auto polling state machine consider the link being negotiated with
- * 1Gbit/s. Since the PHY itself is a Fast Ethernet RMII PHY this leads
- * to the switch port being completely dead (RX and TX are both not
- * working).
- * Also with various other PHY / port combinations (PHY11G GPHY, PHY22F
- * GPHY, external RGMII PEF7071/7072) any traffic would stop. Sometimes
- * it would work fine for a few minutes to hours and then stop, on
- * other device it would no traffic could be sent or received at all.
- * Testing shows that when PHY auto polling is disabled these problems
- * go away.
- */
- gswip_mdio_w(priv, 0x0, GSWIP_MDIO_MDC_CFG0);
-
- /* Configure the MDIO Clock 2.5 MHz */
- gswip_mdio_mask(priv, 0xff, 0x09, GSWIP_MDIO_MDC_CFG1);
-
- /* bring up the mdio bus */
- err = gswip_mdio(priv);
- if (err) {
- dev_err(priv->dev, "mdio bus setup failed\n");
- return err;
- }
-
- /* Disable the xMII interface and clear it's isolation bit */
- for (i = 0; i < priv->hw_info->max_ports; i++)
- gswip_mii_mask_cfg(priv,
- GSWIP_MII_CFG_EN | GSWIP_MII_CFG_ISOLATE,
- 0, i);
-
- dsa_switch_for_each_cpu_port(cpu_dp, ds) {
- /* enable special tag insertion on cpu port */
- gswip_switch_mask(priv, 0, GSWIP_FDMA_PCTRL_STEN,
- GSWIP_FDMA_PCTRLp(cpu_dp->index));
-
- /* accept special tag in ingress direction */
- gswip_switch_mask(priv, 0, GSWIP_PCE_PCTRL_0_INGRESS,
- GSWIP_PCE_PCTRL_0p(cpu_dp->index));
- }
-
- gswip_switch_mask(priv, 0, GSWIP_BM_QUEUE_GCTRL_GL_MOD,
- GSWIP_BM_QUEUE_GCTRL);
-
- /* VLAN aware Switching */
- gswip_switch_mask(priv, 0, GSWIP_PCE_GCTRL_0_VLAN, GSWIP_PCE_GCTRL_0);
-
- /* Flush MAC Table */
- gswip_switch_mask(priv, 0, GSWIP_PCE_GCTRL_0_MTFL, GSWIP_PCE_GCTRL_0);
-
- err = gswip_switch_r_timeout(priv, GSWIP_PCE_GCTRL_0,
- GSWIP_PCE_GCTRL_0_MTFL);
- if (err) {
- dev_err(priv->dev, "MAC flushing didn't finish\n");
- return err;
- }
-
- ds->mtu_enforcement_ingress = true;
-
- ds->configure_vlan_while_not_filtering = false;
-
- return 0;
-}
-
-static enum dsa_tag_protocol gswip_get_tag_protocol(struct dsa_switch *ds,
- int port,
- enum dsa_tag_protocol mp)
-{
- struct gswip_priv *priv = ds->priv;
-
- return priv->hw_info->tag_protocol;
-}
-
-static int gswip_vlan_active_create(struct gswip_priv *priv,
- struct net_device *bridge,
- int fid, u16 vid)
-{
- struct gswip_pce_table_entry vlan_active = {0,};
- unsigned int max_ports = priv->hw_info->max_ports;
- int idx = -1;
- int err;
- int i;
-
- /* Look for a free slot */
- for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
- if (!priv->vlans[i].bridge) {
- idx = i;
- break;
- }
- }
-
- if (idx == -1)
- return -ENOSPC;
-
- if (fid == -1)
- fid = idx;
-
- vlan_active.index = idx;
- vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
- vlan_active.key[0] = vid;
- vlan_active.val[0] = fid;
- vlan_active.valid = true;
-
- err = gswip_pce_table_entry_write(priv, &vlan_active);
- if (err) {
- dev_err(priv->dev, "failed to write active VLAN: %d\n", err);
- return err;
- }
-
- priv->vlans[idx].bridge = bridge;
- priv->vlans[idx].vid = vid;
- priv->vlans[idx].fid = fid;
-
- return idx;
-}
-
-static int gswip_vlan_active_remove(struct gswip_priv *priv, int idx)
-{
- struct gswip_pce_table_entry vlan_active = {0,};
- int err;
-
- vlan_active.index = idx;
- vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
- vlan_active.valid = false;
- err = gswip_pce_table_entry_write(priv, &vlan_active);
- if (err)
- dev_err(priv->dev, "failed to delete active VLAN: %d\n", err);
- priv->vlans[idx].bridge = NULL;
-
- return err;
-}
-
-static int gswip_vlan_add_unaware(struct gswip_priv *priv,
- struct net_device *bridge, int port)
-{
- struct gswip_pce_table_entry vlan_mapping = {0,};
- unsigned int max_ports = priv->hw_info->max_ports;
- bool active_vlan_created = false;
- int idx = -1;
- int i;
- int err;
-
- /* Check if there is already a page for this bridge */
- for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
- if (priv->vlans[i].bridge == bridge) {
- idx = i;
- break;
- }
- }
-
- /* If this bridge is not programmed yet, add a Active VLAN table
- * entry in a free slot and prepare the VLAN mapping table entry.
- */
- if (idx == -1) {
- idx = gswip_vlan_active_create(priv, bridge, -1, 0);
- if (idx < 0)
- return idx;
- active_vlan_created = true;
-
- vlan_mapping.index = idx;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- /* VLAN ID byte, maps to the VLAN ID of vlan active table */
- vlan_mapping.val[0] = 0;
- } else {
- /* Read the existing VLAN mapping entry from the switch */
- vlan_mapping.index = idx;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- err = gswip_pce_table_entry_read(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to read VLAN mapping: %d\n",
- err);
- return err;
- }
- }
-
- /* Update the VLAN mapping entry and write it to the switch */
- vlan_mapping.val[1] |= dsa_cpu_ports(priv->ds);
- vlan_mapping.val[1] |= BIT(port);
- err = gswip_pce_table_entry_write(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
- /* In case an Active VLAN was creaetd delete it again */
- if (active_vlan_created)
- gswip_vlan_active_remove(priv, idx);
- return err;
- }
-
- gswip_switch_w(priv, 0, GSWIP_PCE_DEFPVID(port));
- return 0;
-}
-
-static int gswip_vlan_add_aware(struct gswip_priv *priv,
- struct net_device *bridge, int port,
- u16 vid, bool untagged,
- bool pvid)
-{
- struct gswip_pce_table_entry vlan_mapping = {0,};
- unsigned int max_ports = priv->hw_info->max_ports;
- unsigned int cpu_ports = dsa_cpu_ports(priv->ds);
- bool active_vlan_created = false;
- int idx = -1;
- int fid = -1;
- int i;
- int err;
-
- /* Check if there is already a page for this bridge */
- for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
- if (priv->vlans[i].bridge == bridge) {
- if (fid != -1 && fid != priv->vlans[i].fid)
- dev_err(priv->dev, "one bridge with multiple flow ids\n");
- fid = priv->vlans[i].fid;
- if (priv->vlans[i].vid == vid) {
- idx = i;
- break;
- }
- }
- }
-
- /* If this bridge is not programmed yet, add a Active VLAN table
- * entry in a free slot and prepare the VLAN mapping table entry.
- */
- if (idx == -1) {
- idx = gswip_vlan_active_create(priv, bridge, fid, vid);
- if (idx < 0)
- return idx;
- active_vlan_created = true;
-
- vlan_mapping.index = idx;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- /* VLAN ID byte, maps to the VLAN ID of vlan active table */
- vlan_mapping.val[0] = vid;
- } else {
- /* Read the existing VLAN mapping entry from the switch */
- vlan_mapping.index = idx;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- err = gswip_pce_table_entry_read(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to read VLAN mapping: %d\n",
- err);
- return err;
- }
- }
-
- vlan_mapping.val[0] = vid;
- /* Update the VLAN mapping entry and write it to the switch */
- vlan_mapping.val[1] |= cpu_ports;
- vlan_mapping.val[2] |= cpu_ports;
- vlan_mapping.val[1] |= BIT(port);
- if (untagged)
- vlan_mapping.val[2] &= ~BIT(port);
- else
- vlan_mapping.val[2] |= BIT(port);
- err = gswip_pce_table_entry_write(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
- /* In case an Active VLAN was creaetd delete it again */
- if (active_vlan_created)
- gswip_vlan_active_remove(priv, idx);
- return err;
- }
-
- if (pvid)
- gswip_switch_w(priv, idx, GSWIP_PCE_DEFPVID(port));
-
- return 0;
-}
-
-static int gswip_vlan_remove(struct gswip_priv *priv,
- struct net_device *bridge, int port,
- u16 vid, bool pvid, bool vlan_aware)
-{
- struct gswip_pce_table_entry vlan_mapping = {0,};
- unsigned int max_ports = priv->hw_info->max_ports;
- int idx = -1;
- int i;
- int err;
-
- /* Check if there is already a page for this bridge */
- for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
- if (priv->vlans[i].bridge == bridge &&
- (!vlan_aware || priv->vlans[i].vid == vid)) {
- idx = i;
- break;
- }
- }
-
- if (idx == -1) {
- dev_err(priv->dev, "bridge to leave does not exists\n");
- return -ENOENT;
- }
-
- vlan_mapping.index = idx;
- vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
- err = gswip_pce_table_entry_read(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to read VLAN mapping: %d\n", err);
- return err;
- }
-
- vlan_mapping.val[1] &= ~BIT(port);
- vlan_mapping.val[2] &= ~BIT(port);
- err = gswip_pce_table_entry_write(priv, &vlan_mapping);
- if (err) {
- dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
- return err;
- }
-
- /* In case all ports are removed from the bridge, remove the VLAN */
- if (!(vlan_mapping.val[1] & ~dsa_cpu_ports(priv->ds))) {
- err = gswip_vlan_active_remove(priv, idx);
- if (err) {
- dev_err(priv->dev, "failed to write active VLAN: %d\n",
- err);
- return err;
- }
- }
-
- /* GSWIP 2.2 (GRX300) and later program here the VID directly. */
- if (pvid)
- gswip_switch_w(priv, 0, GSWIP_PCE_DEFPVID(port));
-
- return 0;
-}
-
-static int gswip_port_bridge_join(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge,
- bool *tx_fwd_offload,
- struct netlink_ext_ack *extack)
-{
- struct net_device *br = bridge.dev;
- struct gswip_priv *priv = ds->priv;
- int err;
-
- /* When the bridge uses VLAN filtering we have to configure VLAN
- * specific bridges. No bridge is configured here.
- */
- if (!br_vlan_enabled(br)) {
- err = gswip_vlan_add_unaware(priv, br, port);
- if (err)
- return err;
- priv->port_vlan_filter &= ~BIT(port);
- } else {
- priv->port_vlan_filter |= BIT(port);
- }
- return gswip_add_single_port_br(priv, port, false);
-}
-
-static void gswip_port_bridge_leave(struct dsa_switch *ds, int port,
- struct dsa_bridge bridge)
-{
- struct net_device *br = bridge.dev;
- struct gswip_priv *priv = ds->priv;
-
- gswip_add_single_port_br(priv, port, true);
-
- /* When the bridge uses VLAN filtering we have to configure VLAN
- * specific bridges. No bridge is configured here.
- */
- if (!br_vlan_enabled(br))
- gswip_vlan_remove(priv, br, port, 0, true, false);
-}
-
-static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct netlink_ext_ack *extack)
-{
- struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
- struct gswip_priv *priv = ds->priv;
- unsigned int max_ports = priv->hw_info->max_ports;
- int pos = max_ports;
- int i, idx = -1;
-
- /* We only support VLAN filtering on bridges */
- if (!dsa_is_cpu_port(ds, port) && !bridge)
- return -EOPNOTSUPP;
-
- /* Check if there is already a page for this VLAN */
- for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
- if (priv->vlans[i].bridge == bridge &&
- priv->vlans[i].vid == vlan->vid) {
- idx = i;
- break;
- }
- }
-
- /* If this VLAN is not programmed yet, we have to reserve
- * one entry in the VLAN table. Make sure we start at the
- * next position round.
- */
- if (idx == -1) {
- /* Look for a free slot */
- for (; pos < ARRAY_SIZE(priv->vlans); pos++) {
- if (!priv->vlans[pos].bridge) {
- idx = pos;
- pos++;
- break;
- }
- }
-
- if (idx == -1) {
- NL_SET_ERR_MSG_MOD(extack, "No slot in VLAN table");
- return -ENOSPC;
- }
- }
-
- return 0;
-}
-
-static int gswip_port_vlan_add(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan,
- struct netlink_ext_ack *extack)
-{
- struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
- struct gswip_priv *priv = ds->priv;
- bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
- int err;
-
- err = gswip_port_vlan_prepare(ds, port, vlan, extack);
- if (err)
- return err;
-
- /* We have to receive all packets on the CPU port and should not
- * do any VLAN filtering here. This is also called with bridge
- * NULL and then we do not know for which bridge to configure
- * this.
- */
- if (dsa_is_cpu_port(ds, port))
- return 0;
-
- return gswip_vlan_add_aware(priv, bridge, port, vlan->vid,
- untagged, pvid);
-}
-
-static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
- const struct switchdev_obj_port_vlan *vlan)
-{
- struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
- struct gswip_priv *priv = ds->priv;
- bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
-
- /* We have to receive all packets on the CPU port and should not
- * do any VLAN filtering here. This is also called with bridge
- * NULL and then we do not know for which bridge to configure
- * this.
- */
- if (dsa_is_cpu_port(ds, port))
- return 0;
-
- return gswip_vlan_remove(priv, bridge, port, vlan->vid, pvid, true);
-}
-
-static void gswip_port_fast_age(struct dsa_switch *ds, int port)
-{
- struct gswip_priv *priv = ds->priv;
- struct gswip_pce_table_entry mac_bridge = {0,};
- int i;
- int err;
-
- for (i = 0; i < 2048; i++) {
- mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
- mac_bridge.index = i;
-
- err = gswip_pce_table_entry_read(priv, &mac_bridge);
- if (err) {
- dev_err(priv->dev, "failed to read mac bridge: %d\n",
- err);
- return;
- }
-
- if (!mac_bridge.valid)
- continue;
-
- if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC)
- continue;
-
- if (port != FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT,
- mac_bridge.val[0]))
- continue;
-
- mac_bridge.valid = false;
- err = gswip_pce_table_entry_write(priv, &mac_bridge);
- if (err) {
- dev_err(priv->dev, "failed to write mac bridge: %d\n",
- err);
- return;
- }
- }
-}
-
-static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
-{
- struct gswip_priv *priv = ds->priv;
- u32 stp_state;
-
- switch (state) {
- case BR_STATE_DISABLED:
- gswip_switch_mask(priv, GSWIP_SDMA_PCTRL_EN, 0,
- GSWIP_SDMA_PCTRLp(port));
- return;
- case BR_STATE_BLOCKING:
- case BR_STATE_LISTENING:
- stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LISTEN;
- break;
- case BR_STATE_LEARNING:
- stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LEARNING;
- break;
- case BR_STATE_FORWARDING:
- stp_state = GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING;
- break;
- default:
- dev_err(priv->dev, "invalid STP state: %d\n", state);
- return;
- }
-
- gswip_switch_mask(priv, 0, GSWIP_SDMA_PCTRL_EN,
- GSWIP_SDMA_PCTRLp(port));
- gswip_switch_mask(priv, GSWIP_PCE_PCTRL_0_PSTATE_MASK, stp_state,
- GSWIP_PCE_PCTRL_0p(port));
-}
-
-static int gswip_port_fdb(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid, bool add)
-{
- struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
- struct gswip_priv *priv = ds->priv;
- struct gswip_pce_table_entry mac_bridge = {0,};
- unsigned int max_ports = priv->hw_info->max_ports;
- int fid = -1;
- int i;
- int err;
-
- /* Operation not supported on the CPU port, don't throw errors */
- if (!bridge)
- return 0;
-
- for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
- if (priv->vlans[i].bridge == bridge) {
- fid = priv->vlans[i].fid;
- break;
- }
- }
-
- if (fid == -1) {
- dev_err(priv->dev, "no FID found for bridge %s\n",
- bridge->name);
- return -EINVAL;
- }
-
- mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
- mac_bridge.key_mode = true;
- mac_bridge.key[0] = addr[5] | (addr[4] << 8);
- mac_bridge.key[1] = addr[3] | (addr[2] << 8);
- mac_bridge.key[2] = addr[1] | (addr[0] << 8);
- mac_bridge.key[3] = FIELD_PREP(GSWIP_TABLE_MAC_BRIDGE_KEY3_FID, fid);
- mac_bridge.val[0] = add ? BIT(port) : 0; /* port map */
- mac_bridge.val[1] = GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC;
- mac_bridge.valid = add;
-
- err = gswip_pce_table_entry_write(priv, &mac_bridge);
- if (err)
- dev_err(priv->dev, "failed to write mac bridge: %d\n", err);
-
- return err;
-}
-
-static int gswip_port_fdb_add(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid,
- struct dsa_db db)
-{
- return gswip_port_fdb(ds, port, addr, vid, true);
-}
-
-static int gswip_port_fdb_del(struct dsa_switch *ds, int port,
- const unsigned char *addr, u16 vid,
- struct dsa_db db)
-{
- return gswip_port_fdb(ds, port, addr, vid, false);
-}
-
-static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,
- dsa_fdb_dump_cb_t *cb, void *data)
-{
- struct gswip_priv *priv = ds->priv;
- struct gswip_pce_table_entry mac_bridge = {0,};
- unsigned char addr[ETH_ALEN];
- int i;
- int err;
-
- for (i = 0; i < 2048; i++) {
- mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
- mac_bridge.index = i;
-
- err = gswip_pce_table_entry_read(priv, &mac_bridge);
- if (err) {
- dev_err(priv->dev,
- "failed to read mac bridge entry %d: %d\n",
- i, err);
- return err;
- }
-
- if (!mac_bridge.valid)
- continue;
-
- addr[5] = mac_bridge.key[0] & 0xff;
- addr[4] = (mac_bridge.key[0] >> 8) & 0xff;
- addr[3] = mac_bridge.key[1] & 0xff;
- addr[2] = (mac_bridge.key[1] >> 8) & 0xff;
- addr[1] = mac_bridge.key[2] & 0xff;
- addr[0] = (mac_bridge.key[2] >> 8) & 0xff;
- if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC) {
- if (mac_bridge.val[0] & BIT(port)) {
- err = cb(addr, 0, true, data);
- if (err)
- return err;
- }
- } else {
- if (port == FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT,
- mac_bridge.val[0])) {
- err = cb(addr, 0, false, data);
- if (err)
- return err;
- }
- }
- }
- return 0;
-}
-
-static int gswip_port_max_mtu(struct dsa_switch *ds, int port)
-{
- /* Includes 8 bytes for special header. */
- return GSWIP_MAX_PACKET_LENGTH - VLAN_ETH_HLEN - ETH_FCS_LEN;
-}
-
-static int gswip_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
-{
- struct gswip_priv *priv = ds->priv;
-
- /* CPU port always has maximum mtu of user ports, so use it to set
- * switch frame size, including 8 byte special header.
- */
- if (dsa_is_cpu_port(ds, port)) {
- new_mtu += 8;
- gswip_switch_w(priv, VLAN_ETH_HLEN + new_mtu + ETH_FCS_LEN,
- GSWIP_MAC_FLEN);
- }
-
- /* Enable MLEN for ports with non-standard MTUs, including the special
- * header on the CPU port added above.
- */
- if (new_mtu != ETH_DATA_LEN)
- gswip_switch_mask(priv, 0, GSWIP_MAC_CTRL_2_MLEN,
- GSWIP_MAC_CTRL_2p(port));
- else
- gswip_switch_mask(priv, GSWIP_MAC_CTRL_2_MLEN, 0,
- GSWIP_MAC_CTRL_2p(port));
-
- return 0;
-}
-
static void gswip_xrx200_phylink_get_caps(struct dsa_switch *ds, int port,
struct phylink_config *config)
{
@@ -1346,327 +97,6 @@ static void gswip_xrx300_phylink_get_caps(struct dsa_switch *ds, int port,
MAC_10 | MAC_100 | MAC_1000;
}
-static void gswip_phylink_get_caps(struct dsa_switch *ds, int port,
- struct phylink_config *config)
-{
- struct gswip_priv *priv = ds->priv;
-
- priv->hw_info->phylink_get_caps(ds, port, config);
-}
-
-static void gswip_port_set_link(struct gswip_priv *priv, int port, bool link)
-{
- u32 mdio_phy;
-
- if (link)
- mdio_phy = GSWIP_MDIO_PHY_LINK_UP;
- else
- mdio_phy = GSWIP_MDIO_PHY_LINK_DOWN;
-
- gswip_mdio_mask(priv, GSWIP_MDIO_PHY_LINK_MASK, mdio_phy,
- GSWIP_MDIO_PHYp(port));
-}
-
-static void gswip_port_set_speed(struct gswip_priv *priv, int port, int speed,
- phy_interface_t interface)
-{
- u32 mdio_phy = 0, mii_cfg = 0, mac_ctrl_0 = 0;
-
- switch (speed) {
- case SPEED_10:
- mdio_phy = GSWIP_MDIO_PHY_SPEED_M10;
-
- if (interface == PHY_INTERFACE_MODE_RMII)
- mii_cfg = GSWIP_MII_CFG_RATE_M50;
- else
- mii_cfg = GSWIP_MII_CFG_RATE_M2P5;
-
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_MII;
- break;
-
- case SPEED_100:
- mdio_phy = GSWIP_MDIO_PHY_SPEED_M100;
-
- if (interface == PHY_INTERFACE_MODE_RMII)
- mii_cfg = GSWIP_MII_CFG_RATE_M50;
- else
- mii_cfg = GSWIP_MII_CFG_RATE_M25;
-
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_MII;
- break;
-
- case SPEED_1000:
- mdio_phy = GSWIP_MDIO_PHY_SPEED_G1;
-
- mii_cfg = GSWIP_MII_CFG_RATE_M125;
-
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_RGMII;
- break;
- }
-
- gswip_mdio_mask(priv, GSWIP_MDIO_PHY_SPEED_MASK, mdio_phy,
- GSWIP_MDIO_PHYp(port));
- gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_RATE_MASK, mii_cfg, port);
- gswip_switch_mask(priv, GSWIP_MAC_CTRL_0_GMII_MASK, mac_ctrl_0,
- GSWIP_MAC_CTRL_0p(port));
-}
-
-static void gswip_port_set_duplex(struct gswip_priv *priv, int port, int duplex)
-{
- u32 mac_ctrl_0, mdio_phy;
-
- if (duplex == DUPLEX_FULL) {
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_FDUP_EN;
- mdio_phy = GSWIP_MDIO_PHY_FDUP_EN;
- } else {
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_FDUP_DIS;
- mdio_phy = GSWIP_MDIO_PHY_FDUP_DIS;
- }
-
- gswip_switch_mask(priv, GSWIP_MAC_CTRL_0_FDUP_MASK, mac_ctrl_0,
- GSWIP_MAC_CTRL_0p(port));
- gswip_mdio_mask(priv, GSWIP_MDIO_PHY_FDUP_MASK, mdio_phy,
- GSWIP_MDIO_PHYp(port));
-}
-
-static void gswip_port_set_pause(struct gswip_priv *priv, int port,
- bool tx_pause, bool rx_pause)
-{
- u32 mac_ctrl_0, mdio_phy;
-
- if (tx_pause && rx_pause) {
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_RXTX;
- mdio_phy = GSWIP_MDIO_PHY_FCONTX_EN |
- GSWIP_MDIO_PHY_FCONRX_EN;
- } else if (tx_pause) {
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_TX;
- mdio_phy = GSWIP_MDIO_PHY_FCONTX_EN |
- GSWIP_MDIO_PHY_FCONRX_DIS;
- } else if (rx_pause) {
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_RX;
- mdio_phy = GSWIP_MDIO_PHY_FCONTX_DIS |
- GSWIP_MDIO_PHY_FCONRX_EN;
- } else {
- mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_NONE;
- mdio_phy = GSWIP_MDIO_PHY_FCONTX_DIS |
- GSWIP_MDIO_PHY_FCONRX_DIS;
- }
-
- gswip_switch_mask(priv, GSWIP_MAC_CTRL_0_FCON_MASK,
- mac_ctrl_0, GSWIP_MAC_CTRL_0p(port));
- gswip_mdio_mask(priv,
- GSWIP_MDIO_PHY_FCONTX_MASK |
- GSWIP_MDIO_PHY_FCONRX_MASK,
- mdio_phy, GSWIP_MDIO_PHYp(port));
-}
-
-static void gswip_phylink_mac_config(struct phylink_config *config,
- unsigned int mode,
- const struct phylink_link_state *state)
-{
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct gswip_priv *priv = dp->ds->priv;
- int port = dp->index;
- u32 miicfg = 0;
-
- miicfg |= GSWIP_MII_CFG_LDCLKDIS;
-
- switch (state->interface) {
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_2500BASEX:
- return;
- case PHY_INTERFACE_MODE_MII:
- case PHY_INTERFACE_MODE_INTERNAL:
- miicfg |= GSWIP_MII_CFG_MODE_MIIM;
- break;
- case PHY_INTERFACE_MODE_REVMII:
- miicfg |= GSWIP_MII_CFG_MODE_MIIP;
- break;
- case PHY_INTERFACE_MODE_RMII:
- miicfg |= GSWIP_MII_CFG_MODE_RMIIM;
- break;
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- miicfg |= GSWIP_MII_CFG_MODE_RGMII;
- break;
- case PHY_INTERFACE_MODE_GMII:
- miicfg |= GSWIP_MII_CFG_MODE_GMII;
- break;
- default:
- dev_err(dp->ds->dev,
- "Unsupported interface: %d\n", state->interface);
- return;
- }
-
- gswip_mii_mask_cfg(priv,
- GSWIP_MII_CFG_MODE_MASK | GSWIP_MII_CFG_RMII_CLK |
- GSWIP_MII_CFG_RGMII_IBS | GSWIP_MII_CFG_LDCLKDIS,
- miicfg, port);
-
- switch (state->interface) {
- case PHY_INTERFACE_MODE_RGMII_ID:
- gswip_mii_mask_pcdu(priv, GSWIP_MII_PCDU_TXDLY_MASK |
- GSWIP_MII_PCDU_RXDLY_MASK, 0, port);
- break;
- case PHY_INTERFACE_MODE_RGMII_RXID:
- gswip_mii_mask_pcdu(priv, GSWIP_MII_PCDU_RXDLY_MASK, 0, port);
- break;
- case PHY_INTERFACE_MODE_RGMII_TXID:
- gswip_mii_mask_pcdu(priv, GSWIP_MII_PCDU_TXDLY_MASK, 0, port);
- break;
- default:
- break;
- }
-}
-
-static void gswip_phylink_mac_link_down(struct phylink_config *config,
- unsigned int mode,
- phy_interface_t interface)
-{
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct gswip_priv *priv = dp->ds->priv;
- int port = dp->index;
-
- gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, port);
-
- if (!dsa_port_is_cpu(dp))
- gswip_port_set_link(priv, port, false);
-}
-
-static void gswip_phylink_mac_link_up(struct phylink_config *config,
- struct phy_device *phydev,
- unsigned int mode,
- phy_interface_t interface,
- int speed, int duplex,
- bool tx_pause, bool rx_pause)
-{
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct gswip_priv *priv = dp->ds->priv;
- int port = dp->index;
-
- if (!dsa_port_is_cpu(dp)) {
- gswip_port_set_link(priv, port, true);
- gswip_port_set_speed(priv, port, speed, interface);
- gswip_port_set_duplex(priv, port, duplex);
- gswip_port_set_pause(priv, port, tx_pause, rx_pause);
- }
-
- gswip_mii_mask_cfg(priv, 0, GSWIP_MII_CFG_EN, port);
-}
-
-static void gswip_get_strings(struct dsa_switch *ds, int port, u32 stringset,
- uint8_t *data)
-{
- int i;
-
- if (stringset != ETH_SS_STATS)
- return;
-
- for (i = 0; i < ARRAY_SIZE(gswip_rmon_cnt); i++)
- ethtool_puts(&data, gswip_rmon_cnt[i].name);
-}
-
-static u32 gswip_bcm_ram_entry_read(struct gswip_priv *priv, u32 table,
- u32 index)
-{
- u32 result;
- int err;
-
- gswip_switch_w(priv, index, GSWIP_BM_RAM_ADDR);
- gswip_switch_mask(priv, GSWIP_BM_RAM_CTRL_ADDR_MASK |
- GSWIP_BM_RAM_CTRL_OPMOD,
- table | GSWIP_BM_RAM_CTRL_BAS,
- GSWIP_BM_RAM_CTRL);
-
- err = gswip_switch_r_timeout(priv, GSWIP_BM_RAM_CTRL,
- GSWIP_BM_RAM_CTRL_BAS);
- if (err) {
- dev_err(priv->dev, "timeout while reading table: %u, index: %u\n",
- table, index);
- return 0;
- }
-
- result = gswip_switch_r(priv, GSWIP_BM_RAM_VAL(0));
- result |= gswip_switch_r(priv, GSWIP_BM_RAM_VAL(1)) << 16;
-
- return result;
-}
-
-static void gswip_get_ethtool_stats(struct dsa_switch *ds, int port,
- uint64_t *data)
-{
- struct gswip_priv *priv = ds->priv;
- const struct gswip_rmon_cnt_desc *rmon_cnt;
- int i;
- u64 high;
-
- for (i = 0; i < ARRAY_SIZE(gswip_rmon_cnt); i++) {
- rmon_cnt = &gswip_rmon_cnt[i];
-
- data[i] = gswip_bcm_ram_entry_read(priv, port,
- rmon_cnt->offset);
- if (rmon_cnt->size == 2) {
- high = gswip_bcm_ram_entry_read(priv, port,
- rmon_cnt->offset + 1);
- data[i] |= high << 32;
- }
- }
-}
-
-static int gswip_get_sset_count(struct dsa_switch *ds, int port, int sset)
-{
- if (sset != ETH_SS_STATS)
- return 0;
-
- return ARRAY_SIZE(gswip_rmon_cnt);
-}
-
-static struct phylink_pcs *gswip_phylink_mac_select_pcs(struct phylink_config *config,
- phy_interface_t interface)
-{
- struct dsa_port *dp = dsa_phylink_to_port(config);
- struct gswip_priv *priv = dp->ds->priv;
-
- if (priv->hw_info->mac_select_pcs)
- return priv->hw_info->mac_select_pcs(config, interface);
-
- return NULL;
-}
-
-static const struct phylink_mac_ops gswip_phylink_mac_ops = {
- .mac_config = gswip_phylink_mac_config,
- .mac_link_down = gswip_phylink_mac_link_down,
- .mac_link_up = gswip_phylink_mac_link_up,
- .mac_select_pcs = gswip_phylink_mac_select_pcs,
-};
-
-static const struct dsa_switch_ops gswip_switch_ops = {
- .get_tag_protocol = gswip_get_tag_protocol,
- .setup = gswip_setup,
- .port_setup = gswip_port_setup,
- .port_enable = gswip_port_enable,
- .port_disable = gswip_port_disable,
- .port_bridge_join = gswip_port_bridge_join,
- .port_bridge_leave = gswip_port_bridge_leave,
- .port_fast_age = gswip_port_fast_age,
- .port_vlan_filtering = gswip_port_vlan_filtering,
- .port_vlan_add = gswip_port_vlan_add,
- .port_vlan_del = gswip_port_vlan_del,
- .port_stp_state_set = gswip_port_stp_state_set,
- .port_fdb_add = gswip_port_fdb_add,
- .port_fdb_del = gswip_port_fdb_del,
- .port_fdb_dump = gswip_port_fdb_dump,
- .port_change_mtu = gswip_port_change_mtu,
- .port_max_mtu = gswip_port_max_mtu,
- .phylink_get_caps = gswip_phylink_get_caps,
- .get_strings = gswip_get_strings,
- .get_ethtool_stats = gswip_get_ethtool_stats,
- .get_sset_count = gswip_get_sset_count,
-};
-
static const struct xway_gphy_match_data xrx200a1x_gphy_data = {
.fe_firmware_name = "lantiq/xrx200_phy22f_a14.bin",
.ge_firmware_name = "lantiq/xrx200_phy11g_a14.bin",
@@ -1887,33 +317,37 @@ remove_gphy:
return err;
}
-static int gswip_validate_cpu_port(struct dsa_switch *ds)
-{
- struct gswip_priv *priv = ds->priv;
- struct dsa_port *cpu_dp;
- int cpu_port = -1;
-
- dsa_switch_for_each_cpu_port(cpu_dp, ds) {
- if (cpu_port != -1)
- return dev_err_probe(ds->dev, -EINVAL,
- "only a single CPU port is supported\n");
-
- cpu_port = cpu_dp->index;
- }
-
- if (cpu_port == -1)
- return dev_err_probe(ds->dev, -EINVAL, "no CPU port defined\n");
+static const struct regmap_config sw_regmap_config = {
+ .name = "switch",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_shift = REGMAP_UPSHIFT(2),
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .max_register = GSWIP_SDMA_PCTRLp(6),
+};
- if (BIT(cpu_port) & ~priv->hw_info->allowed_cpu_ports)
- return dev_err_probe(ds->dev, -EINVAL,
- "unsupported CPU port defined\n");
+static const struct regmap_config mdio_regmap_config = {
+ .name = "mdio",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_shift = REGMAP_UPSHIFT(2),
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .max_register = GSWIP_MDIO_PHYp(0),
+};
- return 0;
-}
+static const struct regmap_config mii_regmap_config = {
+ .name = "mii",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_shift = REGMAP_UPSHIFT(2),
+ .val_format_endian = REGMAP_ENDIAN_NATIVE,
+ .max_register = GSWIP_MII_CFGp(6),
+};
static int gswip_probe(struct platform_device *pdev)
{
struct device_node *np, *gphy_fw_np;
+ __iomem void *gswip, *mdio, *mii;
struct device *dev = &pdev->dev;
struct gswip_priv *priv;
int err;
@@ -1924,15 +358,27 @@ static int gswip_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->gswip = devm_platform_ioremap_resource(pdev, 0);
+ gswip = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(gswip))
+ return PTR_ERR(gswip);
+
+ mdio = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(mdio))
+ return PTR_ERR(mdio);
+
+ mii = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(mii))
+ return PTR_ERR(mii);
+
+ priv->gswip = devm_regmap_init_mmio(dev, gswip, &sw_regmap_config);
if (IS_ERR(priv->gswip))
return PTR_ERR(priv->gswip);
- priv->mdio = devm_platform_ioremap_resource(pdev, 1);
+ priv->mdio = devm_regmap_init_mmio(dev, mdio, &mdio_regmap_config);
if (IS_ERR(priv->mdio))
return PTR_ERR(priv->mdio);
- priv->mii = devm_platform_ioremap_resource(pdev, 2);
+ priv->mii = devm_regmap_init_mmio(dev, mii, &mii_regmap_config);
if (IS_ERR(priv->mii))
return PTR_ERR(priv->mii);
@@ -1944,24 +390,9 @@ static int gswip_probe(struct platform_device *pdev)
if (!priv->ds)
return -ENOMEM;
- priv->ds->dev = dev;
- priv->ds->num_ports = priv->hw_info->max_ports;
- priv->ds->priv = priv;
- priv->ds->ops = &gswip_switch_ops;
- priv->ds->phylink_mac_ops = &gswip_phylink_mac_ops;
priv->dev = dev;
- mutex_init(&priv->pce_table_lock);
- version = gswip_switch_r(priv, GSWIP_VERSION);
-
- /* The hardware has the 'major/minor' version bytes in the wrong order
- * preventing numerical comparisons. Construct a 16-bit unsigned integer
- * having the REV field as most significant byte and the MOD field as
- * least significant byte. This is effectively swapping the two bytes of
- * the version variable, but other than using swab16 it doesn't affect
- * the source variable.
- */
- priv->version = GSWIP_VERSION_REV(version) << 8 |
- GSWIP_VERSION_MOD(version);
+
+ regmap_read(priv->gswip, GSWIP_VERSION, &version);
np = dev->of_node;
switch (version) {
@@ -1991,25 +422,14 @@ static int gswip_probe(struct platform_device *pdev)
"gphy fw probe failed\n");
}
- err = dsa_register_switch(priv->ds);
- if (err) {
- dev_err_probe(dev, err, "dsa switch registration failed\n");
- goto gphy_fw_remove;
- }
-
- err = gswip_validate_cpu_port(priv->ds);
+ err = gswip_probe_common(priv, version);
if (err)
- goto disable_switch;
+ goto gphy_fw_remove;
platform_set_drvdata(pdev, priv);
- dev_info(dev, "probed GSWIP version %lx mod %lx\n",
- GSWIP_VERSION_REV(version), GSWIP_VERSION_MOD(version));
return 0;
-disable_switch:
- gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0, GSWIP_MDIO_GLOB);
- dsa_unregister_switch(priv->ds);
gphy_fw_remove:
for (i = 0; i < priv->num_gphy_fw; i++)
gswip_gphy_fw_remove(priv, &priv->gphy_fw[i]);
@@ -2025,7 +445,7 @@ static void gswip_remove(struct platform_device *pdev)
return;
/* disable the switch */
- gswip_mdio_mask(priv, GSWIP_MDIO_GLOB_ENABLE, 0, GSWIP_MDIO_GLOB);
+ gswip_disable_switch(priv);
dsa_unregister_switch(priv->ds);
diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.h b/drivers/net/dsa/lantiq/lantiq_gswip.h
index 2df9c8e8cfd0..9c38e51a75e8 100644
--- a/drivers/net/dsa/lantiq/lantiq_gswip.h
+++ b/drivers/net/dsa/lantiq/lantiq_gswip.h
@@ -2,6 +2,7 @@
#ifndef __LANTIQ_GSWIP_H
#define __LANTIQ_GSWIP_H
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include <linux/phylink.h>
@@ -81,6 +82,10 @@
#define GSWIP_MII_PCDU5 0x05
#define GSWIP_MII_PCDU_TXDLY_MASK GENMASK(2, 0)
#define GSWIP_MII_PCDU_RXDLY_MASK GENMASK(9, 7)
+#define GSWIP_MII_PCDU_TXDLY(x) u16_encode_bits(((x) / 500), GSWIP_MII_PCDU_TXDLY_MASK)
+#define GSWIP_MII_PCDU_RXDLY(x) u16_encode_bits(((x) / 500), GSWIP_MII_PCDU_RXDLY_MASK)
+#define GSWIP_MII_PCDU_RXDLY_DEFAULT 2000 /* picoseconds */
+#define GSWIP_MII_PCDU_TXDLY_DEFAULT 2000 /* picoseconds */
/* GSWIP Core Registers */
#define GSWIP_SWRES 0x000
@@ -157,8 +162,15 @@
#define GSWIP_PCE_PCTRL_0_PSTATE_LEARNING 0x3
#define GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING 0x7
#define GSWIP_PCE_PCTRL_0_PSTATE_MASK GENMASK(2, 0)
+/* Ethernet Switch PCE Port Control Register 3 */
+#define GSWIP_PCE_PCTRL_3p(p) (0x483 + ((p) * 0xA))
+#define GSWIP_PCE_PCTRL_3_LNDIS BIT(15) /* Learning Disable */
#define GSWIP_PCE_VCTRL(p) (0x485 + ((p) * 0xA))
#define GSWIP_PCE_VCTRL_UVR BIT(0) /* Unknown VLAN Rule */
+#define GSWIP_PCE_VCTRL_VINR GENMASK(2, 1) /* VLAN Ingress Tag Rule */
+#define GSWIP_PCE_VCTRL_VINR_ALL 0 /* Admit tagged and untagged packets */
+#define GSWIP_PCE_VCTRL_VINR_TAGGED 1 /* Admit only tagged packets */
+#define GSWIP_PCE_VCTRL_VINR_UNTAGGED 2 /* Admit only untagged packets */
#define GSWIP_PCE_VCTRL_VIMR BIT(3) /* VLAN Ingress Member violation rule */
#define GSWIP_PCE_VCTRL_VEMR BIT(4) /* VLAN Egress Member violation rule */
#define GSWIP_PCE_VCTRL_VSR BIT(5) /* VLAN Security */
@@ -186,6 +198,12 @@
#define GSWIP_MAC_CTRL_2p(p) (0x905 + ((p) * 0xC))
#define GSWIP_MAC_CTRL_2_LCHKL BIT(2) /* Frame Length Check Long Enable */
#define GSWIP_MAC_CTRL_2_MLEN BIT(3) /* Maximum Untagged Frame Lnegth */
+#define GSWIP_MAC_CTRL_4p(p) (0x907 + ((p) * 0xC))
+#define GSWIP_MAC_CTRL_4_LPIEN BIT(7) /* LPI Mode Enable */
+#define GSWIP_MAC_CTRL_4_GWAIT_MASK GENMASK(14, 8) /* LPI Wait Time 1G */
+#define GSWIP_MAC_CTRL_4_GWAIT(t) u16_encode_bits((t), GSWIP_MAC_CTRL_4_GWAIT_MASK)
+#define GSWIP_MAC_CTRL_4_WAIT_MASK GENMASK(6, 0) /* LPI Wait Time 100M */
+#define GSWIP_MAC_CTRL_4_WAIT(t) u16_encode_bits((t), GSWIP_MAC_CTRL_4_WAIT_MASK)
/* Ethernet Switch Fetch DMA Port Control Register */
#define GSWIP_FDMA_PCTRLp(p) (0xA80 + ((p) * 0x6))
@@ -210,6 +228,7 @@
#define GSWIP_TABLE_MAC_BRIDGE_KEY3_FID GENMASK(5, 0) /* Filtering identifier */
#define GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT GENMASK(7, 4) /* Port on learned entries */
#define GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC BIT(0) /* Static, non-aging entry */
+#define GSWIP_TABLE_MAC_BRIDGE_VAL1_VALID BIT(1) /* Valid bit */
#define XRX200_GPHY_FW_ALIGN (16 * 1024)
@@ -222,6 +241,8 @@
*/
#define GSWIP_MAX_PACKET_LENGTH 2400
+#define GSWIP_VLAN_UNAWARE_PVID 0
+
struct gswip_pce_microcode {
u16 val_3;
u16 val_2;
@@ -234,6 +255,7 @@ struct gswip_hw_info {
unsigned int allowed_cpu_ports;
unsigned int mii_ports;
int mii_port_reg_offset;
+ bool supports_2500m;
const struct gswip_pce_microcode (*pce_microcode)[];
size_t pce_microcode_size;
enum dsa_tag_protocol tag_protocol;
@@ -257,9 +279,9 @@ struct gswip_vlan {
};
struct gswip_priv {
- __iomem void *gswip;
- __iomem void *mdio;
- __iomem void *mii;
+ struct regmap *gswip;
+ struct regmap *mdio;
+ struct regmap *mii;
const struct gswip_hw_info *hw_info;
const struct xway_gphy_match_data *gphy_fw_name_cfg;
struct dsa_switch *ds;
@@ -268,9 +290,12 @@ struct gswip_priv {
struct gswip_vlan vlans[64];
int num_gphy_fw;
struct gswip_gphy_fw *gphy_fw;
- u32 port_vlan_filter;
struct mutex pce_table_lock;
u16 version;
};
+void gswip_disable_switch(struct gswip_priv *priv);
+
+int gswip_probe_common(struct gswip_priv *priv, u32 version);
+
#endif /* __LANTIQ_GSWIP_H */
diff --git a/drivers/net/dsa/lantiq/lantiq_gswip_common.c b/drivers/net/dsa/lantiq/lantiq_gswip_common.c
new file mode 100644
index 000000000000..9da39edf8f57
--- /dev/null
+++ b/drivers/net/dsa/lantiq/lantiq_gswip_common.c
@@ -0,0 +1,1739 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lantiq / Intel / MaxLinear GSWIP common function library
+ *
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
+ * Copyright (C) 2023 - 2024 MaxLinear Inc.
+ * Copyright (C) 2022 Snap One, LLC. All rights reserved.
+ * Copyright (C) 2017 - 2019 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (C) 2012 John Crispin <john@phrozen.org>
+ * Copyright (C) 2010 Lantiq Deutschland
+ *
+ * The VLAN and bridge model the GSWIP hardware uses does not directly
+ * matches the model DSA uses.
+ *
+ * The hardware has 64 possible table entries for bridges with one VLAN
+ * ID, one flow id and a list of ports for each bridge. All entries which
+ * match the same flow ID are combined in the mac learning table, they
+ * act as one global bridge.
+ * The hardware does not support VLAN filter on the port, but on the
+ * bridge, this driver converts the DSA model to the hardware.
+ *
+ * The CPU gets all the exception frames which do not match any forwarding
+ * rule and the CPU port is also added to all bridges. This makes it possible
+ * to handle all the special cases easily in software.
+ * At the initialization the driver allocates one bridge table entry for
+ * each switch port which is used when the port is used without an
+ * explicit bridge. This prevents the frames from being forwarded
+ * between all LAN ports by default.
+ */
+
+#include "lantiq_gswip.h"
+
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/phy.h>
+#include <linux/phylink.h>
+#include <linux/regmap.h>
+#include <net/dsa.h>
+
+struct gswip_pce_table_entry {
+ u16 index; // PCE_TBL_ADDR.ADDR = pData->table_index
+ u16 table; // PCE_TBL_CTRL.ADDR = pData->table
+ u16 key[8];
+ u16 val[5];
+ u16 mask;
+ u8 gmap;
+ bool type;
+ bool valid;
+ bool key_mode;
+};
+
+struct gswip_rmon_cnt_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+#define MIB_DESC(_size, _offset, _name) {.size = _size, .offset = _offset, .name = _name}
+
+static const struct gswip_rmon_cnt_desc gswip_rmon_cnt[] = {
+ /** Receive Packet Count (only packets that are accepted and not discarded). */
+ MIB_DESC(1, 0x1F, "RxGoodPkts"),
+ MIB_DESC(1, 0x23, "RxUnicastPkts"),
+ MIB_DESC(1, 0x22, "RxMulticastPkts"),
+ MIB_DESC(1, 0x21, "RxFCSErrorPkts"),
+ MIB_DESC(1, 0x1D, "RxUnderSizeGoodPkts"),
+ MIB_DESC(1, 0x1E, "RxUnderSizeErrorPkts"),
+ MIB_DESC(1, 0x1B, "RxOversizeGoodPkts"),
+ MIB_DESC(1, 0x1C, "RxOversizeErrorPkts"),
+ MIB_DESC(1, 0x20, "RxGoodPausePkts"),
+ MIB_DESC(1, 0x1A, "RxAlignErrorPkts"),
+ MIB_DESC(1, 0x12, "Rx64BytePkts"),
+ MIB_DESC(1, 0x13, "Rx127BytePkts"),
+ MIB_DESC(1, 0x14, "Rx255BytePkts"),
+ MIB_DESC(1, 0x15, "Rx511BytePkts"),
+ MIB_DESC(1, 0x16, "Rx1023BytePkts"),
+ /** Receive Size 1024-1522 (or more, if configured) Packet Count. */
+ MIB_DESC(1, 0x17, "RxMaxBytePkts"),
+ MIB_DESC(1, 0x18, "RxDroppedPkts"),
+ MIB_DESC(1, 0x19, "RxFilteredPkts"),
+ MIB_DESC(2, 0x24, "RxGoodBytes"),
+ MIB_DESC(2, 0x26, "RxBadBytes"),
+ MIB_DESC(1, 0x11, "TxAcmDroppedPkts"),
+ MIB_DESC(1, 0x0C, "TxGoodPkts"),
+ MIB_DESC(1, 0x06, "TxUnicastPkts"),
+ MIB_DESC(1, 0x07, "TxMulticastPkts"),
+ MIB_DESC(1, 0x00, "Tx64BytePkts"),
+ MIB_DESC(1, 0x01, "Tx127BytePkts"),
+ MIB_DESC(1, 0x02, "Tx255BytePkts"),
+ MIB_DESC(1, 0x03, "Tx511BytePkts"),
+ MIB_DESC(1, 0x04, "Tx1023BytePkts"),
+ /** Transmit Size 1024-1522 (or more, if configured) Packet Count. */
+ MIB_DESC(1, 0x05, "TxMaxBytePkts"),
+ MIB_DESC(1, 0x08, "TxSingleCollCount"),
+ MIB_DESC(1, 0x09, "TxMultCollCount"),
+ MIB_DESC(1, 0x0A, "TxLateCollCount"),
+ MIB_DESC(1, 0x0B, "TxExcessCollCount"),
+ MIB_DESC(1, 0x0D, "TxPauseCount"),
+ MIB_DESC(1, 0x10, "TxDroppedPkts"),
+ MIB_DESC(2, 0x0E, "TxGoodBytes"),
+};
+
+static u32 gswip_switch_r_timeout(struct gswip_priv *priv, u32 offset,
+ u32 cleared)
+{
+ u32 val;
+
+ return regmap_read_poll_timeout(priv->gswip, offset, val,
+ !(val & cleared), 20, 50000);
+}
+
+static void gswip_mii_mask_cfg(struct gswip_priv *priv, u32 mask, u32 set,
+ int port)
+{
+ int reg_port;
+
+ /* MII_CFG register only exists for MII ports */
+ if (!(priv->hw_info->mii_ports & BIT(port)))
+ return;
+
+ reg_port = port + priv->hw_info->mii_port_reg_offset;
+
+ regmap_write_bits(priv->mii, GSWIP_MII_CFGp(reg_port), mask,
+ set);
+}
+
+static int gswip_mdio_poll(struct gswip_priv *priv)
+{
+ u32 ctrl;
+
+ return regmap_read_poll_timeout(priv->mdio, GSWIP_MDIO_CTRL, ctrl,
+ !(ctrl & GSWIP_MDIO_CTRL_BUSY), 40, 4000);
+}
+
+static int gswip_mdio_wr(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+ struct gswip_priv *priv = bus->priv;
+ int err;
+
+ err = gswip_mdio_poll(priv);
+ if (err) {
+ dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n");
+ return err;
+ }
+
+ regmap_write(priv->mdio, GSWIP_MDIO_WRITE, val);
+ regmap_write(priv->mdio, GSWIP_MDIO_CTRL,
+ GSWIP_MDIO_CTRL_BUSY | GSWIP_MDIO_CTRL_WR |
+ ((addr & GSWIP_MDIO_CTRL_PHYAD_MASK) << GSWIP_MDIO_CTRL_PHYAD_SHIFT) |
+ (reg & GSWIP_MDIO_CTRL_REGAD_MASK));
+
+ return 0;
+}
+
+static int gswip_mdio_rd(struct mii_bus *bus, int addr, int reg)
+{
+ struct gswip_priv *priv = bus->priv;
+ u32 val;
+ int err;
+
+ err = gswip_mdio_poll(priv);
+ if (err) {
+ dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n");
+ return err;
+ }
+
+ regmap_write(priv->mdio, GSWIP_MDIO_CTRL,
+ GSWIP_MDIO_CTRL_BUSY | GSWIP_MDIO_CTRL_RD |
+ ((addr & GSWIP_MDIO_CTRL_PHYAD_MASK) << GSWIP_MDIO_CTRL_PHYAD_SHIFT) |
+ (reg & GSWIP_MDIO_CTRL_REGAD_MASK));
+
+ err = gswip_mdio_poll(priv);
+ if (err) {
+ dev_err(&bus->dev, "waiting for MDIO bus busy timed out\n");
+ return err;
+ }
+
+ err = regmap_read(priv->mdio, GSWIP_MDIO_READ, &val);
+ if (err)
+ return err;
+
+ return val;
+}
+
+static int gswip_mdio(struct gswip_priv *priv)
+{
+ struct device_node *mdio_np, *switch_np = priv->dev->of_node;
+ struct device *dev = priv->dev;
+ struct mii_bus *bus;
+ int err = 0;
+
+ mdio_np = of_get_compatible_child(switch_np, "lantiq,xrx200-mdio");
+ if (!mdio_np)
+ mdio_np = of_get_child_by_name(switch_np, "mdio");
+
+ if (!of_device_is_available(mdio_np))
+ goto out_put_node;
+
+ bus = devm_mdiobus_alloc(dev);
+ if (!bus) {
+ err = -ENOMEM;
+ goto out_put_node;
+ }
+
+ bus->priv = priv;
+ bus->read = gswip_mdio_rd;
+ bus->write = gswip_mdio_wr;
+ bus->name = "lantiq,xrx200-mdio";
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(priv->dev));
+ bus->parent = priv->dev;
+
+ err = devm_of_mdiobus_register(dev, bus, mdio_np);
+
+out_put_node:
+ of_node_put(mdio_np);
+
+ return err;
+}
+
+static int gswip_pce_table_entry_read(struct gswip_priv *priv,
+ struct gswip_pce_table_entry *tbl)
+{
+ int i;
+ int err;
+ u32 crtl;
+ u32 tmp;
+ u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSRD :
+ GSWIP_PCE_TBL_CTRL_OPMOD_ADRD;
+
+ mutex_lock(&priv->pce_table_lock);
+
+ err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_BAS);
+ if (err)
+ goto out_unlock;
+
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_ADDR, tbl->index);
+ regmap_write_bits(priv->gswip, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_ADDR_MASK |
+ GSWIP_PCE_TBL_CTRL_OPMOD_MASK |
+ GSWIP_PCE_TBL_CTRL_BAS,
+ tbl->table | addr_mode | GSWIP_PCE_TBL_CTRL_BAS);
+
+ err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_BAS);
+ if (err)
+ goto out_unlock;
+
+ for (i = 0; i < ARRAY_SIZE(tbl->key); i++) {
+ err = regmap_read(priv->gswip, GSWIP_PCE_TBL_KEY(i), &tmp);
+ if (err)
+ goto out_unlock;
+ tbl->key[i] = tmp;
+ }
+ for (i = 0; i < ARRAY_SIZE(tbl->val); i++) {
+ err = regmap_read(priv->gswip, GSWIP_PCE_TBL_VAL(i), &tmp);
+ if (err)
+ goto out_unlock;
+ tbl->val[i] = tmp;
+ }
+
+ err = regmap_read(priv->gswip, GSWIP_PCE_TBL_MASK, &tmp);
+ if (err)
+ goto out_unlock;
+
+ tbl->mask = tmp;
+ err = regmap_read(priv->gswip, GSWIP_PCE_TBL_CTRL, &crtl);
+ if (err)
+ goto out_unlock;
+
+ tbl->type = !!(crtl & GSWIP_PCE_TBL_CTRL_TYPE);
+ tbl->valid = !!(crtl & GSWIP_PCE_TBL_CTRL_VLD);
+ tbl->gmap = (crtl & GSWIP_PCE_TBL_CTRL_GMAP_MASK) >> 7;
+
+out_unlock:
+ mutex_unlock(&priv->pce_table_lock);
+
+ return err;
+}
+
+static int gswip_pce_table_entry_write(struct gswip_priv *priv,
+ struct gswip_pce_table_entry *tbl)
+{
+ int i;
+ int err;
+ u32 crtl;
+ u16 addr_mode = tbl->key_mode ? GSWIP_PCE_TBL_CTRL_OPMOD_KSWR :
+ GSWIP_PCE_TBL_CTRL_OPMOD_ADWR;
+
+ mutex_lock(&priv->pce_table_lock);
+
+ err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_BAS);
+ if (err) {
+ mutex_unlock(&priv->pce_table_lock);
+ return err;
+ }
+
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_ADDR, tbl->index);
+ regmap_write_bits(priv->gswip, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_ADDR_MASK |
+ GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
+ tbl->table | addr_mode);
+
+ for (i = 0; i < ARRAY_SIZE(tbl->key); i++)
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_KEY(i), tbl->key[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tbl->val); i++)
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_VAL(i), tbl->val[i]);
+
+ regmap_write_bits(priv->gswip, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_ADDR_MASK |
+ GSWIP_PCE_TBL_CTRL_OPMOD_MASK,
+ tbl->table | addr_mode);
+
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_MASK, tbl->mask);
+
+ regmap_read(priv->gswip, GSWIP_PCE_TBL_CTRL, &crtl);
+ crtl &= ~(GSWIP_PCE_TBL_CTRL_TYPE | GSWIP_PCE_TBL_CTRL_VLD |
+ GSWIP_PCE_TBL_CTRL_GMAP_MASK);
+ if (tbl->type)
+ crtl |= GSWIP_PCE_TBL_CTRL_TYPE;
+ if (tbl->valid)
+ crtl |= GSWIP_PCE_TBL_CTRL_VLD;
+ crtl |= (tbl->gmap << 7) & GSWIP_PCE_TBL_CTRL_GMAP_MASK;
+ crtl |= GSWIP_PCE_TBL_CTRL_BAS;
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_CTRL, crtl);
+
+ err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_BAS);
+
+ mutex_unlock(&priv->pce_table_lock);
+
+ return err;
+}
+
+/* Add the LAN port into a bridge with the CPU port by
+ * default. This prevents automatic forwarding of
+ * packages between the LAN ports when no explicit
+ * bridge is configured.
+ */
+static int gswip_add_single_port_br(struct gswip_priv *priv, int port, bool add)
+{
+ struct gswip_pce_table_entry vlan_active = {0,};
+ struct gswip_pce_table_entry vlan_mapping = {0,};
+ int err;
+
+ vlan_active.index = port + 1;
+ vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
+ vlan_active.key[0] = GSWIP_VLAN_UNAWARE_PVID;
+ vlan_active.val[0] = port + 1 /* fid */;
+ vlan_active.valid = add;
+ err = gswip_pce_table_entry_write(priv, &vlan_active);
+ if (err) {
+ dev_err(priv->dev, "failed to write active VLAN: %d\n", err);
+ return err;
+ }
+
+ if (!add)
+ return 0;
+
+ vlan_mapping.index = port + 1;
+ vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
+ vlan_mapping.val[0] = GSWIP_VLAN_UNAWARE_PVID;
+ vlan_mapping.val[1] = BIT(port) | dsa_cpu_ports(priv->ds);
+ vlan_mapping.val[2] = 0;
+ err = gswip_pce_table_entry_write(priv, &vlan_mapping);
+ if (err) {
+ dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int gswip_port_set_learning(struct gswip_priv *priv, int port,
+ bool enable)
+{
+ if (!GSWIP_VERSION_GE(priv, GSWIP_VERSION_2_2))
+ return -EOPNOTSUPP;
+
+ /* learning disable bit */
+ return regmap_update_bits(priv->gswip, GSWIP_PCE_PCTRL_3p(port),
+ GSWIP_PCE_PCTRL_3_LNDIS,
+ enable ? 0 : GSWIP_PCE_PCTRL_3_LNDIS);
+}
+
+static int gswip_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags flags,
+ struct netlink_ext_ack *extack)
+{
+ struct gswip_priv *priv = ds->priv;
+ unsigned long supported = 0;
+
+ if (GSWIP_VERSION_GE(priv, GSWIP_VERSION_2_2))
+ supported |= BR_LEARNING;
+
+ if (flags.mask & ~supported)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int gswip_port_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags flags,
+ struct netlink_ext_ack *extack)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ if (flags.mask & BR_LEARNING)
+ return gswip_port_set_learning(priv, port,
+ !!(flags.val & BR_LEARNING));
+
+ return 0;
+}
+
+static int gswip_port_setup(struct dsa_switch *ds, int port)
+{
+ struct gswip_priv *priv = ds->priv;
+ int err;
+
+ if (!dsa_is_cpu_port(ds, port)) {
+ err = gswip_add_single_port_br(priv, port, true);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int gswip_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phydev)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ if (!dsa_is_cpu_port(ds, port)) {
+ u32 mdio_phy = 0;
+
+ if (phydev)
+ mdio_phy = phydev->mdio.addr & GSWIP_MDIO_PHY_ADDR_MASK;
+
+ regmap_write_bits(priv->mdio, GSWIP_MDIO_PHYp(port),
+ GSWIP_MDIO_PHY_ADDR_MASK,
+ mdio_phy);
+ }
+
+ /* RMON Counter Enable for port */
+ regmap_write(priv->gswip, GSWIP_BM_PCFGp(port), GSWIP_BM_PCFG_CNTEN);
+
+ /* enable port fetch/store dma & VLAN Modification */
+ regmap_set_bits(priv->gswip, GSWIP_FDMA_PCTRLp(port),
+ GSWIP_FDMA_PCTRL_EN | GSWIP_FDMA_PCTRL_VLANMOD_BOTH);
+ regmap_set_bits(priv->gswip, GSWIP_SDMA_PCTRLp(port),
+ GSWIP_SDMA_PCTRL_EN);
+
+ return 0;
+}
+
+static void gswip_port_disable(struct dsa_switch *ds, int port)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ regmap_clear_bits(priv->gswip, GSWIP_FDMA_PCTRLp(port),
+ GSWIP_FDMA_PCTRL_EN);
+ regmap_clear_bits(priv->gswip, GSWIP_SDMA_PCTRLp(port),
+ GSWIP_SDMA_PCTRL_EN);
+}
+
+static int gswip_pce_load_microcode(struct gswip_priv *priv)
+{
+ int i;
+ int err;
+
+ regmap_write_bits(priv->gswip, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_ADDR_MASK |
+ GSWIP_PCE_TBL_CTRL_OPMOD_MASK |
+ GSWIP_PCE_TBL_CTRL_OPMOD_ADWR,
+ GSWIP_PCE_TBL_CTRL_OPMOD_ADWR);
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_MASK, 0);
+
+ for (i = 0; i < priv->hw_info->pce_microcode_size; i++) {
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_ADDR, i);
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_VAL(0),
+ (*priv->hw_info->pce_microcode)[i].val_0);
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_VAL(1),
+ (*priv->hw_info->pce_microcode)[i].val_1);
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_VAL(2),
+ (*priv->hw_info->pce_microcode)[i].val_2);
+ regmap_write(priv->gswip, GSWIP_PCE_TBL_VAL(3),
+ (*priv->hw_info->pce_microcode)[i].val_3);
+
+ /* start the table access: */
+ regmap_set_bits(priv->gswip, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_BAS);
+ err = gswip_switch_r_timeout(priv, GSWIP_PCE_TBL_CTRL,
+ GSWIP_PCE_TBL_CTRL_BAS);
+ if (err)
+ return err;
+ }
+
+ /* tell the switch that the microcode is loaded */
+ regmap_set_bits(priv->gswip, GSWIP_PCE_GCTRL_0,
+ GSWIP_PCE_GCTRL_0_MC_VALID);
+
+ return 0;
+}
+
+static void gswip_port_commit_pvid(struct gswip_priv *priv, int port)
+{
+ struct dsa_port *dp = dsa_to_port(priv->ds, port);
+ struct net_device *br = dsa_port_bridge_dev_get(dp);
+ u32 vinr;
+ int idx;
+
+ if (!dsa_port_is_user(dp))
+ return;
+
+ if (br) {
+ u16 pvid = GSWIP_VLAN_UNAWARE_PVID;
+
+ if (br_vlan_enabled(br))
+ br_vlan_get_pvid(br, &pvid);
+
+ /* VLAN-aware bridge ports with no PVID will use Active VLAN
+ * index 0. The expectation is that this drops all untagged and
+ * VID-0 tagged ingress traffic.
+ */
+ idx = 0;
+ for (int i = priv->hw_info->max_ports;
+ i < ARRAY_SIZE(priv->vlans); i++) {
+ if (priv->vlans[i].bridge == br &&
+ priv->vlans[i].vid == pvid) {
+ idx = i;
+ break;
+ }
+ }
+ } else {
+ /* The Active VLAN table index as configured by
+ * gswip_add_single_port_br()
+ */
+ idx = port + 1;
+ }
+
+ vinr = idx ? GSWIP_PCE_VCTRL_VINR_ALL : GSWIP_PCE_VCTRL_VINR_TAGGED;
+ regmap_write_bits(priv->gswip, GSWIP_PCE_VCTRL(port),
+ GSWIP_PCE_VCTRL_VINR,
+ FIELD_PREP(GSWIP_PCE_VCTRL_VINR, vinr));
+
+ /* Note that in GSWIP 2.2 VLAN mode the VID needs to be programmed
+ * directly instead of referencing the index in the Active VLAN Tablet.
+ * However, without the VLANMD bit (9) in PCE_GCTRL_1 (0x457) even
+ * GSWIP 2.2 and newer hardware maintain the GSWIP 2.1 behavior.
+ */
+ regmap_write(priv->gswip, GSWIP_PCE_DEFPVID(port), idx);
+}
+
+static int gswip_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ if (vlan_filtering) {
+ /* Use tag based VLAN */
+ regmap_write_bits(priv->gswip, GSWIP_PCE_VCTRL(port),
+ GSWIP_PCE_VCTRL_VSR |
+ GSWIP_PCE_VCTRL_UVR |
+ GSWIP_PCE_VCTRL_VIMR |
+ GSWIP_PCE_VCTRL_VEMR |
+ GSWIP_PCE_VCTRL_VID0,
+ GSWIP_PCE_VCTRL_UVR |
+ GSWIP_PCE_VCTRL_VIMR |
+ GSWIP_PCE_VCTRL_VEMR |
+ GSWIP_PCE_VCTRL_VID0);
+ regmap_clear_bits(priv->gswip, GSWIP_PCE_PCTRL_0p(port),
+ GSWIP_PCE_PCTRL_0_TVM);
+ } else {
+ /* Use port based VLAN */
+ regmap_write_bits(priv->gswip, GSWIP_PCE_VCTRL(port),
+ GSWIP_PCE_VCTRL_UVR |
+ GSWIP_PCE_VCTRL_VIMR |
+ GSWIP_PCE_VCTRL_VEMR |
+ GSWIP_PCE_VCTRL_VID0 |
+ GSWIP_PCE_VCTRL_VSR,
+ GSWIP_PCE_VCTRL_VSR);
+ regmap_set_bits(priv->gswip, GSWIP_PCE_PCTRL_0p(port),
+ GSWIP_PCE_PCTRL_0_TVM);
+ }
+
+ gswip_port_commit_pvid(priv, port);
+
+ return 0;
+}
+
+static void gswip_mii_delay_setup(struct gswip_priv *priv, struct dsa_port *dp,
+ phy_interface_t interface)
+{
+ u32 tx_delay = GSWIP_MII_PCDU_TXDLY_DEFAULT;
+ u32 rx_delay = GSWIP_MII_PCDU_RXDLY_DEFAULT;
+ struct device_node *port_dn = dp->dn;
+ u16 mii_pcdu_reg;
+
+ /* As MII_PCDU registers only exist for MII ports, silently return
+ * unless the port is an MII port
+ */
+ if (!(priv->hw_info->mii_ports & BIT(dp->index)))
+ return;
+
+ switch (dp->index + priv->hw_info->mii_port_reg_offset) {
+ case 0:
+ mii_pcdu_reg = GSWIP_MII_PCDU0;
+ break;
+ case 1:
+ mii_pcdu_reg = GSWIP_MII_PCDU1;
+ break;
+ case 5:
+ mii_pcdu_reg = GSWIP_MII_PCDU5;
+ break;
+ default:
+ return;
+ }
+
+ /* legacy code to set default delays according to the interface mode */
+ switch (interface) {
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ tx_delay = 0;
+ rx_delay = 0;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ rx_delay = 0;
+ break;
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ tx_delay = 0;
+ break;
+ default:
+ break;
+ }
+
+ /* allow settings delays using device tree properties */
+ of_property_read_u32(port_dn, "rx-internal-delay-ps", &rx_delay);
+ of_property_read_u32(port_dn, "tx-internal-delay-ps", &tx_delay);
+
+ regmap_write_bits(priv->mii, mii_pcdu_reg,
+ GSWIP_MII_PCDU_TXDLY_MASK |
+ GSWIP_MII_PCDU_RXDLY_MASK,
+ GSWIP_MII_PCDU_TXDLY(tx_delay) |
+ GSWIP_MII_PCDU_RXDLY(rx_delay));
+}
+
+static int gswip_setup(struct dsa_switch *ds)
+{
+ unsigned int cpu_ports = dsa_cpu_ports(ds);
+ struct gswip_priv *priv = ds->priv;
+ struct dsa_port *cpu_dp;
+ int err, i;
+
+ regmap_write(priv->gswip, GSWIP_SWRES, GSWIP_SWRES_R0);
+ usleep_range(5000, 10000);
+ regmap_write(priv->gswip, GSWIP_SWRES, 0);
+
+ /* disable port fetch/store dma on all ports */
+ for (i = 0; i < priv->hw_info->max_ports; i++) {
+ gswip_port_disable(ds, i);
+ gswip_port_vlan_filtering(ds, i, false, NULL);
+ }
+
+ /* enable Switch */
+ regmap_set_bits(priv->mdio, GSWIP_MDIO_GLOB, GSWIP_MDIO_GLOB_ENABLE);
+
+ err = gswip_pce_load_microcode(priv);
+ if (err) {
+ dev_err(priv->dev, "writing PCE microcode failed, %i\n", err);
+ return err;
+ }
+
+ /* Default unknown Broadcast/Multicast/Unicast port maps */
+ regmap_write(priv->gswip, GSWIP_PCE_PMAP1, cpu_ports);
+ regmap_write(priv->gswip, GSWIP_PCE_PMAP2, cpu_ports);
+ regmap_write(priv->gswip, GSWIP_PCE_PMAP3, cpu_ports);
+
+ /* Deactivate MDIO PHY auto polling. Some PHYs as the AR8030 have an
+ * interoperability problem with this auto polling mechanism because
+ * their status registers think that the link is in a different state
+ * than it actually is. For the AR8030 it has the BMSR_ESTATEN bit set
+ * as well as ESTATUS_1000_TFULL and ESTATUS_1000_XFULL. This makes the
+ * auto polling state machine consider the link being negotiated with
+ * 1Gbit/s. Since the PHY itself is a Fast Ethernet RMII PHY this leads
+ * to the switch port being completely dead (RX and TX are both not
+ * working).
+ * Also with various other PHY / port combinations (PHY11G GPHY, PHY22F
+ * GPHY, external RGMII PEF7071/7072) any traffic would stop. Sometimes
+ * it would work fine for a few minutes to hours and then stop, on
+ * other device it would no traffic could be sent or received at all.
+ * Testing shows that when PHY auto polling is disabled these problems
+ * go away.
+ */
+ regmap_write(priv->mdio, GSWIP_MDIO_MDC_CFG0, 0x0);
+
+ /* Configure the MDIO Clock 2.5 MHz */
+ regmap_write_bits(priv->mdio, GSWIP_MDIO_MDC_CFG1, 0xff, 0x09);
+
+ /* bring up the mdio bus */
+ err = gswip_mdio(priv);
+ if (err) {
+ dev_err(priv->dev, "mdio bus setup failed\n");
+ return err;
+ }
+
+ /* Disable the xMII interface and clear it's isolation bit */
+ for (i = 0; i < priv->hw_info->max_ports; i++)
+ gswip_mii_mask_cfg(priv,
+ GSWIP_MII_CFG_EN | GSWIP_MII_CFG_ISOLATE,
+ 0, i);
+
+ dsa_switch_for_each_cpu_port(cpu_dp, ds) {
+ /* enable special tag insertion on cpu port */
+ regmap_set_bits(priv->gswip, GSWIP_FDMA_PCTRLp(cpu_dp->index),
+ GSWIP_FDMA_PCTRL_STEN);
+
+ /* accept special tag in ingress direction */
+ regmap_set_bits(priv->gswip,
+ GSWIP_PCE_PCTRL_0p(cpu_dp->index),
+ GSWIP_PCE_PCTRL_0_INGRESS);
+ }
+
+ regmap_set_bits(priv->gswip, GSWIP_BM_QUEUE_GCTRL,
+ GSWIP_BM_QUEUE_GCTRL_GL_MOD);
+
+ /* VLAN aware Switching */
+ regmap_set_bits(priv->gswip, GSWIP_PCE_GCTRL_0,
+ GSWIP_PCE_GCTRL_0_VLAN);
+
+ /* Flush MAC Table */
+ regmap_set_bits(priv->gswip, GSWIP_PCE_GCTRL_0,
+ GSWIP_PCE_GCTRL_0_MTFL);
+
+ err = gswip_switch_r_timeout(priv, GSWIP_PCE_GCTRL_0,
+ GSWIP_PCE_GCTRL_0_MTFL);
+ if (err) {
+ dev_err(priv->dev, "MAC flushing didn't finish\n");
+ return err;
+ }
+
+ ds->mtu_enforcement_ingress = true;
+
+ return 0;
+}
+
+static enum dsa_tag_protocol gswip_get_tag_protocol(struct dsa_switch *ds,
+ int port,
+ enum dsa_tag_protocol mp)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ return priv->hw_info->tag_protocol;
+}
+
+static int gswip_vlan_active_create(struct gswip_priv *priv,
+ struct net_device *bridge,
+ int fid, u16 vid)
+{
+ struct gswip_pce_table_entry vlan_active = {0,};
+ unsigned int max_ports = priv->hw_info->max_ports;
+ int idx = -1;
+ int err;
+ int i;
+
+ /* Look for a free slot */
+ for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
+ if (!priv->vlans[i].bridge) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx == -1)
+ return -ENOSPC;
+
+ if (fid == -1)
+ fid = idx;
+
+ vlan_active.index = idx;
+ vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
+ vlan_active.key[0] = vid;
+ vlan_active.val[0] = fid;
+ vlan_active.valid = true;
+
+ err = gswip_pce_table_entry_write(priv, &vlan_active);
+ if (err) {
+ dev_err(priv->dev, "failed to write active VLAN: %d\n", err);
+ return err;
+ }
+
+ priv->vlans[idx].bridge = bridge;
+ priv->vlans[idx].vid = vid;
+ priv->vlans[idx].fid = fid;
+
+ return idx;
+}
+
+static int gswip_vlan_active_remove(struct gswip_priv *priv, int idx)
+{
+ struct gswip_pce_table_entry vlan_active = {0,};
+ int err;
+
+ vlan_active.index = idx;
+ vlan_active.table = GSWIP_TABLE_ACTIVE_VLAN;
+ vlan_active.valid = false;
+ err = gswip_pce_table_entry_write(priv, &vlan_active);
+ if (err)
+ dev_err(priv->dev, "failed to delete active VLAN: %d\n", err);
+ priv->vlans[idx].bridge = NULL;
+
+ return err;
+}
+
+static int gswip_vlan_add(struct gswip_priv *priv, struct net_device *bridge,
+ int port, u16 vid, bool untagged, bool pvid,
+ bool vlan_aware)
+{
+ struct gswip_pce_table_entry vlan_mapping = {0,};
+ unsigned int max_ports = priv->hw_info->max_ports;
+ unsigned int cpu_ports = dsa_cpu_ports(priv->ds);
+ bool active_vlan_created = false;
+ int fid = -1, idx = -1;
+ int i, err;
+
+ /* Check if there is already a page for this bridge */
+ for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
+ if (priv->vlans[i].bridge == bridge) {
+ if (vlan_aware) {
+ if (fid != -1 && fid != priv->vlans[i].fid)
+ dev_err(priv->dev, "one bridge with multiple flow ids\n");
+ fid = priv->vlans[i].fid;
+ }
+ if (priv->vlans[i].vid == vid) {
+ idx = i;
+ break;
+ }
+ }
+ }
+
+ /* If this bridge is not programmed yet, add a Active VLAN table
+ * entry in a free slot and prepare the VLAN mapping table entry.
+ */
+ if (idx == -1) {
+ idx = gswip_vlan_active_create(priv, bridge, fid, vid);
+ if (idx < 0)
+ return idx;
+ active_vlan_created = true;
+
+ vlan_mapping.index = idx;
+ vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
+ } else {
+ /* Read the existing VLAN mapping entry from the switch */
+ vlan_mapping.index = idx;
+ vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
+ err = gswip_pce_table_entry_read(priv, &vlan_mapping);
+ if (err) {
+ dev_err(priv->dev, "failed to read VLAN mapping: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ /* VLAN ID byte, maps to the VLAN ID of vlan active table */
+ vlan_mapping.val[0] = vid;
+ /* Update the VLAN mapping entry and write it to the switch */
+ vlan_mapping.val[1] |= cpu_ports;
+ vlan_mapping.val[1] |= BIT(port);
+ if (vlan_aware)
+ vlan_mapping.val[2] |= cpu_ports;
+ if (untagged)
+ vlan_mapping.val[2] &= ~BIT(port);
+ else
+ vlan_mapping.val[2] |= BIT(port);
+ err = gswip_pce_table_entry_write(priv, &vlan_mapping);
+ if (err) {
+ dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
+ /* In case an Active VLAN was creaetd delete it again */
+ if (active_vlan_created)
+ gswip_vlan_active_remove(priv, idx);
+ return err;
+ }
+
+ gswip_port_commit_pvid(priv, port);
+
+ return 0;
+}
+
+static int gswip_vlan_remove(struct gswip_priv *priv,
+ struct net_device *bridge, int port,
+ u16 vid)
+{
+ struct gswip_pce_table_entry vlan_mapping = {0,};
+ unsigned int max_ports = priv->hw_info->max_ports;
+ int idx = -1;
+ int i;
+ int err;
+
+ /* Check if there is already a page for this bridge */
+ for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
+ if (priv->vlans[i].bridge == bridge &&
+ priv->vlans[i].vid == vid) {
+ idx = i;
+ break;
+ }
+ }
+
+ if (idx == -1) {
+ dev_err(priv->dev, "Port %d cannot find VID %u of bridge %s\n",
+ port, vid, bridge ? bridge->name : "(null)");
+ return -ENOENT;
+ }
+
+ vlan_mapping.index = idx;
+ vlan_mapping.table = GSWIP_TABLE_VLAN_MAPPING;
+ err = gswip_pce_table_entry_read(priv, &vlan_mapping);
+ if (err) {
+ dev_err(priv->dev, "failed to read VLAN mapping: %d\n", err);
+ return err;
+ }
+
+ vlan_mapping.val[1] &= ~BIT(port);
+ vlan_mapping.val[2] &= ~BIT(port);
+ err = gswip_pce_table_entry_write(priv, &vlan_mapping);
+ if (err) {
+ dev_err(priv->dev, "failed to write VLAN mapping: %d\n", err);
+ return err;
+ }
+
+ /* In case all ports are removed from the bridge, remove the VLAN */
+ if (!(vlan_mapping.val[1] & ~dsa_cpu_ports(priv->ds))) {
+ err = gswip_vlan_active_remove(priv, idx);
+ if (err) {
+ dev_err(priv->dev, "failed to write active VLAN: %d\n",
+ err);
+ return err;
+ }
+ }
+
+ gswip_port_commit_pvid(priv, port);
+
+ return 0;
+}
+
+static int gswip_port_bridge_join(struct dsa_switch *ds, int port,
+ struct dsa_bridge bridge,
+ bool *tx_fwd_offload,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *br = bridge.dev;
+ struct gswip_priv *priv = ds->priv;
+ int err;
+
+ /* Set up the VLAN for VLAN-unaware bridging for this port, and remove
+ * it from the "single-port bridge" through which it was operating as
+ * standalone.
+ */
+ err = gswip_vlan_add(priv, br, port, GSWIP_VLAN_UNAWARE_PVID,
+ true, true, false);
+ if (err)
+ return err;
+
+ return gswip_add_single_port_br(priv, port, false);
+}
+
+static void gswip_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct dsa_bridge bridge)
+{
+ struct net_device *br = bridge.dev;
+ struct gswip_priv *priv = ds->priv;
+
+ /* Add the port back to the "single-port bridge", and remove it from
+ * the VLAN-unaware PVID created for this bridge.
+ */
+ gswip_add_single_port_br(priv, port, true);
+ gswip_vlan_remove(priv, br, port, GSWIP_VLAN_UNAWARE_PVID);
+}
+
+static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
+ struct gswip_priv *priv = ds->priv;
+ unsigned int max_ports = priv->hw_info->max_ports;
+ int pos = max_ports;
+ int i, idx = -1;
+
+ /* We only support VLAN filtering on bridges */
+ if (!dsa_is_cpu_port(ds, port) && !bridge)
+ return -EOPNOTSUPP;
+
+ /* Check if there is already a page for this VLAN */
+ for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
+ if (priv->vlans[i].bridge == bridge &&
+ priv->vlans[i].vid == vlan->vid) {
+ idx = i;
+ break;
+ }
+ }
+
+ /* If this VLAN is not programmed yet, we have to reserve
+ * one entry in the VLAN table. Make sure we start at the
+ * next position round.
+ */
+ if (idx == -1) {
+ /* Look for a free slot */
+ for (; pos < ARRAY_SIZE(priv->vlans); pos++) {
+ if (!priv->vlans[pos].bridge) {
+ idx = pos;
+ pos++;
+ break;
+ }
+ }
+
+ if (idx == -1) {
+ NL_SET_ERR_MSG_MOD(extack, "No slot in VLAN table");
+ return -ENOSPC;
+ }
+ }
+
+ return 0;
+}
+
+static int gswip_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
+ struct gswip_priv *priv = ds->priv;
+ bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+ bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+ int err;
+
+ if (vlan->vid == GSWIP_VLAN_UNAWARE_PVID)
+ return 0;
+
+ err = gswip_port_vlan_prepare(ds, port, vlan, extack);
+ if (err)
+ return err;
+
+ /* We have to receive all packets on the CPU port and should not
+ * do any VLAN filtering here. This is also called with bridge
+ * NULL and then we do not know for which bridge to configure
+ * this.
+ */
+ if (dsa_is_cpu_port(ds, port))
+ return 0;
+
+ return gswip_vlan_add(priv, bridge, port, vlan->vid, untagged, pvid,
+ true);
+}
+
+static int gswip_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct net_device *bridge = dsa_port_bridge_dev_get(dsa_to_port(ds, port));
+ struct gswip_priv *priv = ds->priv;
+
+ if (vlan->vid == GSWIP_VLAN_UNAWARE_PVID)
+ return 0;
+
+ /* We have to receive all packets on the CPU port and should not
+ * do any VLAN filtering here. This is also called with bridge
+ * NULL and then we do not know for which bridge to configure
+ * this.
+ */
+ if (dsa_is_cpu_port(ds, port))
+ return 0;
+
+ return gswip_vlan_remove(priv, bridge, port, vlan->vid);
+}
+
+static void gswip_port_fast_age(struct dsa_switch *ds, int port)
+{
+ struct gswip_priv *priv = ds->priv;
+ struct gswip_pce_table_entry mac_bridge = {0,};
+ int i;
+ int err;
+
+ for (i = 0; i < 2048; i++) {
+ mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
+ mac_bridge.index = i;
+
+ err = gswip_pce_table_entry_read(priv, &mac_bridge);
+ if (err) {
+ dev_err(priv->dev, "failed to read mac bridge: %d\n",
+ err);
+ return;
+ }
+
+ if (!mac_bridge.valid)
+ continue;
+
+ if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC)
+ continue;
+
+ if (port != FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT,
+ mac_bridge.val[0]))
+ continue;
+
+ mac_bridge.valid = false;
+ err = gswip_pce_table_entry_write(priv, &mac_bridge);
+ if (err) {
+ dev_err(priv->dev, "failed to write mac bridge: %d\n",
+ err);
+ return;
+ }
+ }
+}
+
+static void gswip_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct gswip_priv *priv = ds->priv;
+ u32 stp_state;
+
+ switch (state) {
+ case BR_STATE_DISABLED:
+ regmap_clear_bits(priv->gswip, GSWIP_SDMA_PCTRLp(port),
+ GSWIP_SDMA_PCTRL_EN);
+ return;
+ case BR_STATE_BLOCKING:
+ case BR_STATE_LISTENING:
+ stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LISTEN;
+ break;
+ case BR_STATE_LEARNING:
+ stp_state = GSWIP_PCE_PCTRL_0_PSTATE_LEARNING;
+ break;
+ case BR_STATE_FORWARDING:
+ stp_state = GSWIP_PCE_PCTRL_0_PSTATE_FORWARDING;
+ break;
+ default:
+ dev_err(priv->dev, "invalid STP state: %d\n", state);
+ return;
+ }
+
+ regmap_set_bits(priv->gswip, GSWIP_SDMA_PCTRLp(port),
+ GSWIP_SDMA_PCTRL_EN);
+ regmap_write_bits(priv->gswip, GSWIP_PCE_PCTRL_0p(port),
+ GSWIP_PCE_PCTRL_0_PSTATE_MASK,
+ stp_state);
+}
+
+static int gswip_port_fdb(struct dsa_switch *ds, int port,
+ struct net_device *bridge, const unsigned char *addr,
+ u16 vid, bool add)
+{
+ struct gswip_priv *priv = ds->priv;
+ struct gswip_pce_table_entry mac_bridge = {0,};
+ unsigned int max_ports = priv->hw_info->max_ports;
+ int fid = -1;
+ int i;
+ int err;
+
+ for (i = max_ports; i < ARRAY_SIZE(priv->vlans); i++) {
+ if (priv->vlans[i].bridge == bridge) {
+ fid = priv->vlans[i].fid;
+ break;
+ }
+ }
+
+ if (fid == -1) {
+ dev_err(priv->dev, "no FID found for bridge %s\n",
+ bridge->name);
+ return -EINVAL;
+ }
+
+ mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
+ mac_bridge.key_mode = true;
+ mac_bridge.key[0] = addr[5] | (addr[4] << 8);
+ mac_bridge.key[1] = addr[3] | (addr[2] << 8);
+ mac_bridge.key[2] = addr[1] | (addr[0] << 8);
+ mac_bridge.key[3] = FIELD_PREP(GSWIP_TABLE_MAC_BRIDGE_KEY3_FID, fid);
+ mac_bridge.val[0] = add ? BIT(port) : 0; /* port map */
+ if (GSWIP_VERSION_GE(priv, GSWIP_VERSION_2_2_ETC))
+ mac_bridge.val[1] = add ? (GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC |
+ GSWIP_TABLE_MAC_BRIDGE_VAL1_VALID) : 0;
+ else
+ mac_bridge.val[1] = GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC;
+
+ mac_bridge.valid = add;
+
+ err = gswip_pce_table_entry_write(priv, &mac_bridge);
+ if (err)
+ dev_err(priv->dev, "failed to write mac bridge: %d\n", err);
+
+ return err;
+}
+
+static int gswip_port_fdb_add(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db)
+{
+ if (db.type != DSA_DB_BRIDGE)
+ return -EOPNOTSUPP;
+
+ return gswip_port_fdb(ds, port, db.bridge.dev, addr, vid, true);
+}
+
+static int gswip_port_fdb_del(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid,
+ struct dsa_db db)
+{
+ if (db.type != DSA_DB_BRIDGE)
+ return -EOPNOTSUPP;
+
+ return gswip_port_fdb(ds, port, db.bridge.dev, addr, vid, false);
+}
+
+static int gswip_port_fdb_dump(struct dsa_switch *ds, int port,
+ dsa_fdb_dump_cb_t *cb, void *data)
+{
+ struct gswip_priv *priv = ds->priv;
+ struct gswip_pce_table_entry mac_bridge = {0,};
+ unsigned char addr[ETH_ALEN];
+ int i;
+ int err;
+
+ for (i = 0; i < 2048; i++) {
+ mac_bridge.table = GSWIP_TABLE_MAC_BRIDGE;
+ mac_bridge.index = i;
+
+ err = gswip_pce_table_entry_read(priv, &mac_bridge);
+ if (err) {
+ dev_err(priv->dev,
+ "failed to read mac bridge entry %d: %d\n",
+ i, err);
+ return err;
+ }
+
+ if (!mac_bridge.valid)
+ continue;
+
+ addr[5] = mac_bridge.key[0] & 0xff;
+ addr[4] = (mac_bridge.key[0] >> 8) & 0xff;
+ addr[3] = mac_bridge.key[1] & 0xff;
+ addr[2] = (mac_bridge.key[1] >> 8) & 0xff;
+ addr[1] = mac_bridge.key[2] & 0xff;
+ addr[0] = (mac_bridge.key[2] >> 8) & 0xff;
+ if (mac_bridge.val[1] & GSWIP_TABLE_MAC_BRIDGE_VAL1_STATIC) {
+ if (mac_bridge.val[0] & BIT(port)) {
+ err = cb(addr, 0, true, data);
+ if (err)
+ return err;
+ }
+ } else {
+ if (port == FIELD_GET(GSWIP_TABLE_MAC_BRIDGE_VAL0_PORT,
+ mac_bridge.val[0])) {
+ err = cb(addr, 0, false, data);
+ if (err)
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
+static int gswip_port_max_mtu(struct dsa_switch *ds, int port)
+{
+ /* Includes 8 bytes for special header. */
+ return GSWIP_MAX_PACKET_LENGTH - VLAN_ETH_HLEN - ETH_FCS_LEN;
+}
+
+static int gswip_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ /* CPU port always has maximum mtu of user ports, so use it to set
+ * switch frame size, including 8 byte special header.
+ */
+ if (dsa_is_cpu_port(ds, port)) {
+ new_mtu += 8;
+ regmap_write(priv->gswip, GSWIP_MAC_FLEN,
+ VLAN_ETH_HLEN + new_mtu + ETH_FCS_LEN);
+ }
+
+ /* Enable MLEN for ports with non-standard MTUs, including the special
+ * header on the CPU port added above.
+ */
+ if (new_mtu != ETH_DATA_LEN)
+ regmap_set_bits(priv->gswip, GSWIP_MAC_CTRL_2p(port),
+ GSWIP_MAC_CTRL_2_MLEN);
+ else
+ regmap_clear_bits(priv->gswip, GSWIP_MAC_CTRL_2p(port),
+ GSWIP_MAC_CTRL_2_MLEN);
+
+ return 0;
+}
+
+static void gswip_phylink_get_caps(struct dsa_switch *ds, int port,
+ struct phylink_config *config)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ priv->hw_info->phylink_get_caps(ds, port, config);
+}
+
+static void gswip_port_set_link(struct gswip_priv *priv, int port, bool link)
+{
+ u32 mdio_phy;
+
+ if (link)
+ mdio_phy = GSWIP_MDIO_PHY_LINK_UP;
+ else
+ mdio_phy = GSWIP_MDIO_PHY_LINK_DOWN;
+
+ regmap_write_bits(priv->mdio, GSWIP_MDIO_PHYp(port),
+ GSWIP_MDIO_PHY_LINK_MASK, mdio_phy);
+}
+
+static void gswip_port_set_speed(struct gswip_priv *priv, int port, int speed,
+ phy_interface_t interface)
+{
+ u32 mdio_phy = 0, mii_cfg = 0, mac_ctrl_0 = 0;
+
+ switch (speed) {
+ case SPEED_10:
+ mdio_phy = GSWIP_MDIO_PHY_SPEED_M10;
+
+ if (interface == PHY_INTERFACE_MODE_RMII)
+ mii_cfg = GSWIP_MII_CFG_RATE_M50;
+ else
+ mii_cfg = GSWIP_MII_CFG_RATE_M2P5;
+
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_MII;
+ break;
+
+ case SPEED_100:
+ mdio_phy = GSWIP_MDIO_PHY_SPEED_M100;
+
+ if (interface == PHY_INTERFACE_MODE_RMII)
+ mii_cfg = GSWIP_MII_CFG_RATE_M50;
+ else
+ mii_cfg = GSWIP_MII_CFG_RATE_M25;
+
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_MII;
+ break;
+
+ case SPEED_1000:
+ mdio_phy = GSWIP_MDIO_PHY_SPEED_G1;
+
+ mii_cfg = GSWIP_MII_CFG_RATE_M125;
+
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_GMII_RGMII;
+ break;
+ }
+
+ regmap_write_bits(priv->mdio, GSWIP_MDIO_PHYp(port),
+ GSWIP_MDIO_PHY_SPEED_MASK, mdio_phy);
+ gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_RATE_MASK, mii_cfg, port);
+ regmap_write_bits(priv->gswip, GSWIP_MAC_CTRL_0p(port),
+ GSWIP_MAC_CTRL_0_GMII_MASK, mac_ctrl_0);
+}
+
+static void gswip_port_set_duplex(struct gswip_priv *priv, int port, int duplex)
+{
+ u32 mac_ctrl_0, mdio_phy;
+
+ if (duplex == DUPLEX_FULL) {
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_FDUP_EN;
+ mdio_phy = GSWIP_MDIO_PHY_FDUP_EN;
+ } else {
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_FDUP_DIS;
+ mdio_phy = GSWIP_MDIO_PHY_FDUP_DIS;
+ }
+
+ regmap_write_bits(priv->gswip, GSWIP_MAC_CTRL_0p(port),
+ GSWIP_MAC_CTRL_0_FDUP_MASK, mac_ctrl_0);
+ regmap_write_bits(priv->mdio, GSWIP_MDIO_PHYp(port),
+ GSWIP_MDIO_PHY_FDUP_MASK, mdio_phy);
+}
+
+static void gswip_port_set_pause(struct gswip_priv *priv, int port,
+ bool tx_pause, bool rx_pause)
+{
+ u32 mac_ctrl_0, mdio_phy;
+
+ if (tx_pause && rx_pause) {
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_RXTX;
+ mdio_phy = GSWIP_MDIO_PHY_FCONTX_EN |
+ GSWIP_MDIO_PHY_FCONRX_EN;
+ } else if (tx_pause) {
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_TX;
+ mdio_phy = GSWIP_MDIO_PHY_FCONTX_EN |
+ GSWIP_MDIO_PHY_FCONRX_DIS;
+ } else if (rx_pause) {
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_RX;
+ mdio_phy = GSWIP_MDIO_PHY_FCONTX_DIS |
+ GSWIP_MDIO_PHY_FCONRX_EN;
+ } else {
+ mac_ctrl_0 = GSWIP_MAC_CTRL_0_FCON_NONE;
+ mdio_phy = GSWIP_MDIO_PHY_FCONTX_DIS |
+ GSWIP_MDIO_PHY_FCONRX_DIS;
+ }
+
+ regmap_write_bits(priv->gswip, GSWIP_MAC_CTRL_0p(port),
+ GSWIP_MAC_CTRL_0_FCON_MASK, mac_ctrl_0);
+ regmap_write_bits(priv->mdio, GSWIP_MDIO_PHYp(port),
+ GSWIP_MDIO_PHY_FCONTX_MASK | GSWIP_MDIO_PHY_FCONRX_MASK,
+ mdio_phy);
+}
+
+static void gswip_phylink_mac_config(struct phylink_config *config,
+ unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+ int port = dp->index;
+ u32 miicfg = 0;
+
+ miicfg |= GSWIP_MII_CFG_LDCLKDIS;
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ return;
+ case PHY_INTERFACE_MODE_MII:
+ case PHY_INTERFACE_MODE_INTERNAL:
+ miicfg |= GSWIP_MII_CFG_MODE_MIIM;
+ break;
+ case PHY_INTERFACE_MODE_REVMII:
+ miicfg |= GSWIP_MII_CFG_MODE_MIIP;
+ break;
+ case PHY_INTERFACE_MODE_RMII:
+ miicfg |= GSWIP_MII_CFG_MODE_RMIIM;
+ if (of_property_read_bool(dp->dn, "maxlinear,rmii-refclk-out"))
+ miicfg |= GSWIP_MII_CFG_RMII_CLK;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ miicfg |= GSWIP_MII_CFG_MODE_RGMII;
+ break;
+ case PHY_INTERFACE_MODE_GMII:
+ miicfg |= GSWIP_MII_CFG_MODE_GMII;
+ break;
+ default:
+ dev_err(dp->ds->dev,
+ "Unsupported interface: %d\n", state->interface);
+ return;
+ }
+
+ gswip_mii_mask_cfg(priv,
+ GSWIP_MII_CFG_MODE_MASK | GSWIP_MII_CFG_RMII_CLK |
+ GSWIP_MII_CFG_RGMII_IBS | GSWIP_MII_CFG_LDCLKDIS,
+ miicfg, port);
+
+ gswip_mii_delay_setup(priv, dp, state->interface);
+}
+
+static void gswip_phylink_mac_link_down(struct phylink_config *config,
+ unsigned int mode,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+ int port = dp->index;
+
+ gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, 0, port);
+
+ if (!dsa_port_is_cpu(dp))
+ gswip_port_set_link(priv, port, false);
+}
+
+static void gswip_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev,
+ unsigned int mode,
+ phy_interface_t interface,
+ int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+ int port = dp->index;
+
+ if (!dsa_port_is_cpu(dp) || interface != PHY_INTERFACE_MODE_INTERNAL) {
+ gswip_port_set_link(priv, port, true);
+ gswip_port_set_speed(priv, port, speed, interface);
+ gswip_port_set_duplex(priv, port, duplex);
+ gswip_port_set_pause(priv, port, tx_pause, rx_pause);
+ }
+
+ gswip_mii_mask_cfg(priv, GSWIP_MII_CFG_EN, GSWIP_MII_CFG_EN, port);
+}
+
+static void gswip_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data)
+{
+ int i;
+
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(gswip_rmon_cnt); i++)
+ ethtool_puts(&data, gswip_rmon_cnt[i].name);
+}
+
+static u32 gswip_bcm_ram_entry_read(struct gswip_priv *priv, u32 table,
+ u32 index)
+{
+ u32 result, val;
+ int err;
+
+ regmap_write(priv->gswip, GSWIP_BM_RAM_ADDR, index);
+ regmap_write_bits(priv->gswip, GSWIP_BM_RAM_CTRL,
+ GSWIP_BM_RAM_CTRL_ADDR_MASK | GSWIP_BM_RAM_CTRL_OPMOD |
+ GSWIP_BM_RAM_CTRL_BAS,
+ table | GSWIP_BM_RAM_CTRL_BAS);
+
+ err = gswip_switch_r_timeout(priv, GSWIP_BM_RAM_CTRL,
+ GSWIP_BM_RAM_CTRL_BAS);
+ if (err) {
+ dev_err(priv->dev, "timeout while reading table: %u, index: %u\n",
+ table, index);
+ return 0;
+ }
+
+ regmap_read(priv->gswip, GSWIP_BM_RAM_VAL(0), &result);
+ regmap_read(priv->gswip, GSWIP_BM_RAM_VAL(1), &val);
+ result |= val << 16;
+
+ return result;
+}
+
+static void gswip_get_ethtool_stats(struct dsa_switch *ds, int port,
+ uint64_t *data)
+{
+ struct gswip_priv *priv = ds->priv;
+ const struct gswip_rmon_cnt_desc *rmon_cnt;
+ int i;
+ u64 high;
+
+ for (i = 0; i < ARRAY_SIZE(gswip_rmon_cnt); i++) {
+ rmon_cnt = &gswip_rmon_cnt[i];
+
+ data[i] = gswip_bcm_ram_entry_read(priv, port,
+ rmon_cnt->offset);
+ if (rmon_cnt->size == 2) {
+ high = gswip_bcm_ram_entry_read(priv, port,
+ rmon_cnt->offset + 1);
+ data[i] |= high << 32;
+ }
+ }
+}
+
+static int gswip_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+ return ARRAY_SIZE(gswip_rmon_cnt);
+}
+
+static int gswip_set_mac_eee(struct dsa_switch *ds, int port,
+ struct ethtool_keee *e)
+{
+ if (e->tx_lpi_timer > 0x7f)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void gswip_phylink_mac_disable_tx_lpi(struct phylink_config *config)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+
+ regmap_clear_bits(priv->gswip, GSWIP_MAC_CTRL_4p(dp->index),
+ GSWIP_MAC_CTRL_4_LPIEN);
+}
+
+static int gswip_phylink_mac_enable_tx_lpi(struct phylink_config *config,
+ u32 timer, bool tx_clock_stop)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+
+ return regmap_update_bits(priv->gswip, GSWIP_MAC_CTRL_4p(dp->index),
+ GSWIP_MAC_CTRL_4_LPIEN |
+ GSWIP_MAC_CTRL_4_GWAIT_MASK |
+ GSWIP_MAC_CTRL_4_WAIT_MASK,
+ GSWIP_MAC_CTRL_4_LPIEN |
+ GSWIP_MAC_CTRL_4_GWAIT(timer) |
+ GSWIP_MAC_CTRL_4_WAIT(timer));
+}
+
+static bool gswip_support_eee(struct dsa_switch *ds, int port)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ if (GSWIP_VERSION_GE(priv, GSWIP_VERSION_2_2))
+ return true;
+
+ return false;
+}
+
+static struct phylink_pcs *gswip_phylink_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *priv = dp->ds->priv;
+
+ if (priv->hw_info->mac_select_pcs)
+ return priv->hw_info->mac_select_pcs(config, interface);
+
+ return NULL;
+}
+
+static const struct phylink_mac_ops gswip_phylink_mac_ops = {
+ .mac_config = gswip_phylink_mac_config,
+ .mac_link_down = gswip_phylink_mac_link_down,
+ .mac_link_up = gswip_phylink_mac_link_up,
+ .mac_disable_tx_lpi = gswip_phylink_mac_disable_tx_lpi,
+ .mac_enable_tx_lpi = gswip_phylink_mac_enable_tx_lpi,
+ .mac_select_pcs = gswip_phylink_mac_select_pcs,
+};
+
+static const struct dsa_switch_ops gswip_switch_ops = {
+ .get_tag_protocol = gswip_get_tag_protocol,
+ .setup = gswip_setup,
+ .port_setup = gswip_port_setup,
+ .port_enable = gswip_port_enable,
+ .port_disable = gswip_port_disable,
+ .port_pre_bridge_flags = gswip_port_pre_bridge_flags,
+ .port_bridge_flags = gswip_port_bridge_flags,
+ .port_bridge_join = gswip_port_bridge_join,
+ .port_bridge_leave = gswip_port_bridge_leave,
+ .port_fast_age = gswip_port_fast_age,
+ .port_vlan_filtering = gswip_port_vlan_filtering,
+ .port_vlan_add = gswip_port_vlan_add,
+ .port_vlan_del = gswip_port_vlan_del,
+ .port_stp_state_set = gswip_port_stp_state_set,
+ .port_fdb_add = gswip_port_fdb_add,
+ .port_fdb_del = gswip_port_fdb_del,
+ .port_fdb_dump = gswip_port_fdb_dump,
+ .port_change_mtu = gswip_port_change_mtu,
+ .port_max_mtu = gswip_port_max_mtu,
+ .phylink_get_caps = gswip_phylink_get_caps,
+ .get_strings = gswip_get_strings,
+ .get_ethtool_stats = gswip_get_ethtool_stats,
+ .get_sset_count = gswip_get_sset_count,
+ .set_mac_eee = gswip_set_mac_eee,
+ .support_eee = gswip_support_eee,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
+};
+
+void gswip_disable_switch(struct gswip_priv *priv)
+{
+ regmap_clear_bits(priv->mdio, GSWIP_MDIO_GLOB, GSWIP_MDIO_GLOB_ENABLE);
+}
+EXPORT_SYMBOL_GPL(gswip_disable_switch);
+
+static int gswip_validate_cpu_port(struct dsa_switch *ds)
+{
+ struct gswip_priv *priv = ds->priv;
+ struct dsa_port *cpu_dp;
+ int cpu_port = -1;
+
+ dsa_switch_for_each_cpu_port(cpu_dp, ds) {
+ if (cpu_port != -1)
+ return dev_err_probe(ds->dev, -EINVAL,
+ "only a single CPU port is supported\n");
+
+ cpu_port = cpu_dp->index;
+ }
+
+ if (cpu_port == -1)
+ return dev_err_probe(ds->dev, -EINVAL, "no CPU port defined\n");
+
+ if (BIT(cpu_port) & ~priv->hw_info->allowed_cpu_ports)
+ return dev_err_probe(ds->dev, -EINVAL,
+ "unsupported CPU port defined\n");
+
+ return 0;
+}
+
+int gswip_probe_common(struct gswip_priv *priv, u32 version)
+{
+ int err;
+
+ mutex_init(&priv->pce_table_lock);
+
+ priv->ds = devm_kzalloc(priv->dev, sizeof(*priv->ds), GFP_KERNEL);
+ if (!priv->ds)
+ return -ENOMEM;
+
+ priv->ds->dev = priv->dev;
+ priv->ds->num_ports = priv->hw_info->max_ports;
+ priv->ds->ops = &gswip_switch_ops;
+ priv->ds->phylink_mac_ops = &gswip_phylink_mac_ops;
+ priv->ds->priv = priv;
+
+ /* The hardware has the 'major/minor' version bytes in the wrong order
+ * preventing numerical comparisons. Construct a 16-bit unsigned integer
+ * having the REV field as most significant byte and the MOD field as
+ * least significant byte. This is effectively swapping the two bytes of
+ * the version variable, but other than using swab16 it doesn't affect
+ * the source variable.
+ */
+ priv->version = GSWIP_VERSION_REV(version) << 8 |
+ GSWIP_VERSION_MOD(version);
+
+ err = dsa_register_switch(priv->ds);
+ if (err)
+ return dev_err_probe(priv->dev, err, "dsa switch registration failed\n");
+
+ err = gswip_validate_cpu_port(priv->ds);
+ if (err)
+ goto disable_switch;
+
+ dev_info(priv->dev, "probed GSWIP version %lx mod %lx\n",
+ GSWIP_VERSION_REV(version), GSWIP_VERSION_MOD(version));
+
+ return 0;
+
+disable_switch:
+ gswip_disable_switch(priv);
+ dsa_unregister_switch(priv->ds);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(gswip_probe_common);
+
+MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_DESCRIPTION("Lantiq / Intel / MaxLinear GSWIP common functions");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.c b/drivers/net/dsa/lantiq/mxl-gsw1xx.c
new file mode 100644
index 000000000000..0816c61a47f1
--- /dev/null
+++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.c
@@ -0,0 +1,733 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* DSA Driver for MaxLinear GSW1xx switch devices
+ *
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
+ * Copyright (C) 2023 - 2024 MaxLinear Inc.
+ * Copyright (C) 2022 Snap One, LLC. All rights reserved.
+ * Copyright (C) 2017 - 2019 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (C) 2012 John Crispin <john@phrozen.org>
+ * Copyright (C) 2010 Lantiq Deutschland
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_mdio.h>
+#include <linux/regmap.h>
+#include <net/dsa.h>
+
+#include "lantiq_gswip.h"
+#include "mxl-gsw1xx.h"
+#include "mxl-gsw1xx_pce.h"
+
+struct gsw1xx_priv {
+ struct mdio_device *mdio_dev;
+ int smdio_badr;
+ struct regmap *sgmii;
+ struct regmap *gpio;
+ struct regmap *clk;
+ struct regmap *shell;
+ struct phylink_pcs pcs;
+ phy_interface_t tbi_interface;
+ struct gswip_priv gswip;
+};
+
+static int gsw1xx_config_smdio_badr(struct gsw1xx_priv *priv,
+ unsigned int reg)
+{
+ struct mii_bus *bus = priv->mdio_dev->bus;
+ int sw_addr = priv->mdio_dev->addr;
+ int smdio_badr = priv->smdio_badr;
+ int res;
+
+ if (smdio_badr == GSW1XX_SMDIO_BADR_UNKNOWN ||
+ reg - smdio_badr >= GSW1XX_SMDIO_BADR ||
+ smdio_badr > reg) {
+ /* Configure the Switch Base Address */
+ smdio_badr = reg & ~GENMASK(3, 0);
+ res = __mdiobus_write(bus, sw_addr, GSW1XX_SMDIO_BADR, smdio_badr);
+ if (res < 0) {
+ dev_err(&priv->mdio_dev->dev,
+ "%s: Error %d, configuring switch base\n",
+ __func__, res);
+ return res;
+ }
+ priv->smdio_badr = smdio_badr;
+ }
+
+ return smdio_badr;
+}
+
+static int gsw1xx_regmap_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct gsw1xx_priv *priv = context;
+ struct mii_bus *bus = priv->mdio_dev->bus;
+ int sw_addr = priv->mdio_dev->addr;
+ int smdio_badr;
+ int res;
+
+ smdio_badr = gsw1xx_config_smdio_badr(priv, reg);
+ if (smdio_badr < 0)
+ return smdio_badr;
+
+ res = __mdiobus_read(bus, sw_addr, reg - smdio_badr);
+ if (res < 0) {
+ dev_err(&priv->mdio_dev->dev, "%s: Error %d reading 0x%x\n",
+ __func__, res, reg);
+ return res;
+ }
+
+ *val = res;
+
+ return 0;
+}
+
+static int gsw1xx_regmap_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct gsw1xx_priv *priv = context;
+ struct mii_bus *bus = priv->mdio_dev->bus;
+ int sw_addr = priv->mdio_dev->addr;
+ int smdio_badr;
+ int res;
+
+ smdio_badr = gsw1xx_config_smdio_badr(priv, reg);
+ if (smdio_badr < 0)
+ return smdio_badr;
+
+ res = __mdiobus_write(bus, sw_addr, reg - smdio_badr, val);
+ if (res < 0)
+ dev_err(&priv->mdio_dev->dev,
+ "%s: Error %d, writing 0x%x:0x%x\n", __func__, res, reg,
+ val);
+
+ return res;
+}
+
+static const struct regmap_bus gsw1xx_regmap_bus = {
+ .reg_write = gsw1xx_regmap_write,
+ .reg_read = gsw1xx_regmap_read,
+};
+
+static void gsw1xx_mdio_regmap_lock(void *mdio_lock)
+{
+ mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);
+}
+
+static void gsw1xx_mdio_regmap_unlock(void *mdio_lock)
+{
+ mutex_unlock(mdio_lock);
+}
+
+static unsigned int gsw1xx_pcs_inband_caps(struct phylink_pcs *pcs,
+ phy_interface_t interface)
+{
+ return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
+}
+
+static struct gsw1xx_priv *pcs_to_gsw1xx(struct phylink_pcs *pcs)
+{
+ return container_of(pcs, struct gsw1xx_priv, pcs);
+}
+
+static int gsw1xx_pcs_enable(struct phylink_pcs *pcs)
+{
+ struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs);
+
+ /* Deassert SGMII shell reset */
+ return regmap_clear_bits(priv->shell, GSW1XX_SHELL_RST_REQ,
+ GSW1XX_RST_REQ_SGMII_SHELL);
+}
+
+static void gsw1xx_pcs_disable(struct phylink_pcs *pcs)
+{
+ struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs);
+
+ /* Assert SGMII shell reset */
+ regmap_set_bits(priv->shell, GSW1XX_SHELL_RST_REQ,
+ GSW1XX_RST_REQ_SGMII_SHELL);
+
+ priv->tbi_interface = PHY_INTERFACE_MODE_NA;
+}
+
+static void gsw1xx_pcs_get_state(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs);
+ int ret;
+ u32 val;
+
+ ret = regmap_read(priv->sgmii, GSW1XX_SGMII_TBI_TBISTAT, &val);
+ if (ret < 0)
+ return;
+
+ state->link = !!(val & GSW1XX_SGMII_TBI_TBISTAT_LINK);
+ state->an_complete = !!(val & GSW1XX_SGMII_TBI_TBISTAT_AN_COMPLETE);
+
+ ret = regmap_read(priv->sgmii, GSW1XX_SGMII_TBI_LPSTAT, &val);
+ if (ret < 0)
+ return;
+
+ state->duplex = (val & GSW1XX_SGMII_TBI_LPSTAT_DUPLEX) ?
+ DUPLEX_FULL : DUPLEX_HALF;
+ if (val & GSW1XX_SGMII_TBI_LPSTAT_PAUSE_RX)
+ state->pause |= MLO_PAUSE_RX;
+
+ if (val & GSW1XX_SGMII_TBI_LPSTAT_PAUSE_TX)
+ state->pause |= MLO_PAUSE_TX;
+
+ switch (FIELD_GET(GSW1XX_SGMII_TBI_LPSTAT_SPEED, val)) {
+ case GSW1XX_SGMII_TBI_LPSTAT_SPEED_10:
+ state->speed = SPEED_10;
+ break;
+ case GSW1XX_SGMII_TBI_LPSTAT_SPEED_100:
+ state->speed = SPEED_100;
+ break;
+ case GSW1XX_SGMII_TBI_LPSTAT_SPEED_1000:
+ state->speed = SPEED_1000;
+ break;
+ case GSW1XX_SGMII_TBI_LPSTAT_SPEED_NOSGMII:
+ if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
+ state->speed = SPEED_1000;
+ else if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+ state->speed = SPEED_2500;
+ else
+ state->speed = SPEED_UNKNOWN;
+ break;
+ }
+}
+
+static int gsw1xx_pcs_phy_xaui_write(struct gsw1xx_priv *priv, u16 addr,
+ u16 data)
+{
+ int ret, val;
+
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_PHY_D, data);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_PHY_A, addr);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_PHY_C,
+ GSW1XX_SGMII_PHY_WRITE |
+ GSW1XX_SGMII_PHY_RESET_N);
+ if (ret < 0)
+ return ret;
+
+ return regmap_read_poll_timeout(priv->sgmii, GSW1XX_SGMII_PHY_C,
+ val, val & GSW1XX_SGMII_PHY_STATUS,
+ 1000, 100000);
+}
+
+static int gsw1xx_pcs_reset(struct gsw1xx_priv *priv)
+{
+ int ret;
+ u16 val;
+
+ /* Assert and deassert SGMII shell reset */
+ ret = regmap_set_bits(priv->shell, GSW1XX_SHELL_RST_REQ,
+ GSW1XX_RST_REQ_SGMII_SHELL);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_clear_bits(priv->shell, GSW1XX_SHELL_RST_REQ,
+ GSW1XX_RST_REQ_SGMII_SHELL);
+ if (ret < 0)
+ return ret;
+
+ /* Hardware Bringup FSM Enable */
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_PHY_HWBU_CTRL,
+ GSW1XX_SGMII_PHY_HWBU_CTRL_EN_HWBU_FSM |
+ GSW1XX_SGMII_PHY_HWBU_CTRL_HW_FSM_EN);
+ if (ret < 0)
+ return ret;
+
+ /* Configure SGMII PHY Receiver */
+ val = FIELD_PREP(GSW1XX_SGMII_PHY_RX0_CFG2_EQ,
+ GSW1XX_SGMII_PHY_RX0_CFG2_EQ_DEF) |
+ GSW1XX_SGMII_PHY_RX0_CFG2_LOS_EN |
+ GSW1XX_SGMII_PHY_RX0_CFG2_TERM_EN |
+ FIELD_PREP(GSW1XX_SGMII_PHY_RX0_CFG2_FILT_CNT,
+ GSW1XX_SGMII_PHY_RX0_CFG2_FILT_CNT_DEF);
+
+ /* TODO: Take care of inverted RX pair once generic property is
+ * available
+ */
+
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_PHY_RX0_CFG2, val);
+ if (ret < 0)
+ return ret;
+
+ val = FIELD_PREP(GSW1XX_SGMII_PHY_TX0_CFG3_VBOOST_LEVEL,
+ GSW1XX_SGMII_PHY_TX0_CFG3_VBOOST_LEVEL_DEF);
+
+ /* TODO: Take care of inverted TX pair once generic property is
+ * available
+ */
+
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_PHY_TX0_CFG3, val);
+ if (ret < 0)
+ return ret;
+
+ /* Reset and Release TBI */
+ val = GSW1XX_SGMII_TBI_TBICTL_INITTBI | GSW1XX_SGMII_TBI_TBICTL_ENTBI |
+ GSW1XX_SGMII_TBI_TBICTL_CRSTRR | GSW1XX_SGMII_TBI_TBICTL_CRSOFF;
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_TBI_TBICTL, val);
+ if (ret < 0)
+ return ret;
+ val &= ~GSW1XX_SGMII_TBI_TBICTL_INITTBI;
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_TBI_TBICTL, val);
+ if (ret < 0)
+ return ret;
+
+ /* Release Tx Data Buffers */
+ ret = regmap_set_bits(priv->sgmii, GSW1XX_SGMII_PCS_TXB_CTL,
+ GSW1XX_SGMII_PCS_TXB_CTL_INIT_TX_TXB);
+ if (ret < 0)
+ return ret;
+ ret = regmap_clear_bits(priv->sgmii, GSW1XX_SGMII_PCS_TXB_CTL,
+ GSW1XX_SGMII_PCS_TXB_CTL_INIT_TX_TXB);
+ if (ret < 0)
+ return ret;
+
+ /* Release Rx Data Buffers */
+ ret = regmap_set_bits(priv->sgmii, GSW1XX_SGMII_PCS_RXB_CTL,
+ GSW1XX_SGMII_PCS_RXB_CTL_INIT_RX_RXB);
+ if (ret < 0)
+ return ret;
+ return regmap_clear_bits(priv->sgmii, GSW1XX_SGMII_PCS_RXB_CTL,
+ GSW1XX_SGMII_PCS_RXB_CTL_INIT_RX_RXB);
+}
+
+static int gsw1xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs);
+ u16 txaneg, anegctl, nco_ctrl;
+ bool reconf = false;
+ int ret = 0;
+
+ /* do not unnecessarily disrupt link and skip resetting the hardware in
+ * case the PCS has previously been successfully configured for this
+ * interface mode
+ */
+ if (priv->tbi_interface == interface)
+ reconf = true;
+
+ /* mark PCS configuration as incomplete */
+ priv->tbi_interface = PHY_INTERFACE_MODE_NA;
+
+ if (!reconf)
+ ret = gsw1xx_pcs_reset(priv);
+
+ if (ret)
+ return ret;
+
+ /* override bootstrap pin settings
+ * OVRANEG sets ANEG Mode, Enable ANEG and restart ANEG to be
+ * taken from bits ANMODE, ANEGEN, RANEG of the ANEGCTL register.
+ * OVERABL sets ability bits in tx_config_reg to be taken from
+ * the TXANEGH and TXANEGL registers.
+ */
+ anegctl = GSW1XX_SGMII_TBI_ANEGCTL_OVRANEG |
+ GSW1XX_SGMII_TBI_ANEGCTL_OVRABL;
+
+ switch (phylink_get_link_timer_ns(interface)) {
+ case 10000:
+ anegctl |= FIELD_PREP(GSW1XX_SGMII_TBI_ANEGCTL_LT,
+ GSW1XX_SGMII_TBI_ANEGCTL_LT_10US);
+ break;
+ case 1600000:
+ anegctl |= FIELD_PREP(GSW1XX_SGMII_TBI_ANEGCTL_LT,
+ GSW1XX_SGMII_TBI_ANEGCTL_LT_1_6MS);
+ break;
+ case 5000000:
+ anegctl |= FIELD_PREP(GSW1XX_SGMII_TBI_ANEGCTL_LT,
+ GSW1XX_SGMII_TBI_ANEGCTL_LT_5MS);
+ break;
+ case 10000000:
+ anegctl |= FIELD_PREP(GSW1XX_SGMII_TBI_ANEGCTL_LT,
+ GSW1XX_SGMII_TBI_ANEGCTL_LT_10MS);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (neg_mode & PHYLINK_PCS_NEG_INBAND)
+ anegctl |= GSW1XX_SGMII_TBI_ANEGCTL_ANEGEN;
+
+ txaneg = phylink_mii_c22_pcs_encode_advertisement(interface, advertising);
+
+ if (interface == PHY_INTERFACE_MODE_SGMII) {
+ /* lacking a defined reverse-SGMII interface mode this
+ * driver only supports SGMII (MAC side) for now
+ */
+ anegctl |= FIELD_PREP(GSW1XX_SGMII_TBI_ANEGCTL_ANMODE,
+ GSW1XX_SGMII_TBI_ANEGCTL_ANMODE_SGMII_MAC);
+ txaneg |= ADVERTISE_LPACK;
+ } else if (interface == PHY_INTERFACE_MODE_1000BASEX ||
+ interface == PHY_INTERFACE_MODE_2500BASEX) {
+ anegctl |= FIELD_PREP(GSW1XX_SGMII_TBI_ANEGCTL_ANMODE,
+ GSW1XX_SGMII_TBI_ANEGCTL_ANMODE_1000BASEX);
+ } else {
+ dev_err(priv->gswip.dev, "%s: wrong interface mode %s\n",
+ __func__, phy_modes(interface));
+ return -EINVAL;
+ }
+
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_TBI_TXANEGH,
+ FIELD_GET(GENMASK(15, 8), txaneg));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_TBI_TXANEGL,
+ FIELD_GET(GENMASK(7, 0), txaneg));
+ if (ret < 0)
+ return ret;
+ ret = regmap_write(priv->sgmii, GSW1XX_SGMII_TBI_ANEGCTL, anegctl);
+ if (ret < 0)
+ return ret;
+
+ if (!reconf) {
+ /* setup SerDes clock speed */
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ nco_ctrl = GSW1XX_SGMII_2G5 | GSW1XX_SGMII_2G5_NCO2;
+ else
+ nco_ctrl = GSW1XX_SGMII_1G | GSW1XX_SGMII_1G_NCO1;
+
+ ret = regmap_update_bits(priv->clk, GSW1XX_CLK_NCO_CTRL,
+ GSW1XX_SGMII_HSP_MASK |
+ GSW1XX_SGMII_SEL,
+ nco_ctrl);
+ if (ret)
+ return ret;
+
+ ret = gsw1xx_pcs_phy_xaui_write(priv, 0x30, 0x80);
+ if (ret)
+ return ret;
+ }
+
+ /* PCS configuration has now been completed, store mode to prevent
+ * disrupting the link in case of future calls of this function for the
+ * same interface mode.
+ */
+ priv->tbi_interface = interface;
+
+ return 0;
+}
+
+static void gsw1xx_pcs_an_restart(struct phylink_pcs *pcs)
+{
+ struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs);
+
+ regmap_set_bits(priv->sgmii, GSW1XX_SGMII_TBI_ANEGCTL,
+ GSW1XX_SGMII_TBI_ANEGCTL_RANEG);
+}
+
+static void gsw1xx_pcs_link_up(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ phy_interface_t interface, int speed,
+ int duplex)
+{
+ struct gsw1xx_priv *priv = pcs_to_gsw1xx(pcs);
+ u16 lpstat;
+
+ /* When in-band AN is enabled hardware will set lpstat */
+ if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED)
+ return;
+
+ /* Force speed and duplex settings */
+ if (interface == PHY_INTERFACE_MODE_SGMII) {
+ if (speed == SPEED_10)
+ lpstat = FIELD_PREP(GSW1XX_SGMII_TBI_LPSTAT_SPEED,
+ GSW1XX_SGMII_TBI_LPSTAT_SPEED_10);
+ else if (speed == SPEED_100)
+ lpstat = FIELD_PREP(GSW1XX_SGMII_TBI_LPSTAT_SPEED,
+ GSW1XX_SGMII_TBI_LPSTAT_SPEED_100);
+ else
+ lpstat = FIELD_PREP(GSW1XX_SGMII_TBI_LPSTAT_SPEED,
+ GSW1XX_SGMII_TBI_LPSTAT_SPEED_1000);
+ } else {
+ lpstat = FIELD_PREP(GSW1XX_SGMII_TBI_LPSTAT_SPEED,
+ GSW1XX_SGMII_TBI_LPSTAT_SPEED_NOSGMII);
+ }
+
+ if (duplex == DUPLEX_FULL)
+ lpstat |= GSW1XX_SGMII_TBI_LPSTAT_DUPLEX;
+
+ regmap_write(priv->sgmii, GSW1XX_SGMII_TBI_LPSTAT, lpstat);
+}
+
+static const struct phylink_pcs_ops gsw1xx_pcs_ops = {
+ .pcs_inband_caps = gsw1xx_pcs_inband_caps,
+ .pcs_enable = gsw1xx_pcs_enable,
+ .pcs_disable = gsw1xx_pcs_disable,
+ .pcs_get_state = gsw1xx_pcs_get_state,
+ .pcs_config = gsw1xx_pcs_config,
+ .pcs_an_restart = gsw1xx_pcs_an_restart,
+ .pcs_link_up = gsw1xx_pcs_link_up,
+};
+
+static void gsw1xx_phylink_get_caps(struct dsa_switch *ds, int port,
+ struct phylink_config *config)
+{
+ struct gswip_priv *priv = ds->priv;
+
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000;
+
+ switch (port) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+ config->supported_interfaces);
+ break;
+ case 4: /* port 4: SGMII */
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ config->supported_interfaces);
+ if (priv->hw_info->supports_2500m) {
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ config->supported_interfaces);
+ config->mac_capabilities |= MAC_2500FD;
+ }
+ return; /* no support for EEE on SGMII port */
+ case 5: /* port 5: RGMII or RMII */
+ __set_bit(PHY_INTERFACE_MODE_RMII,
+ config->supported_interfaces);
+ phy_interface_set_rgmii(config->supported_interfaces);
+ break;
+ }
+
+ config->lpi_capabilities = MAC_100FD | MAC_1000FD;
+ config->lpi_timer_default = 20;
+ memcpy(config->lpi_interfaces, config->supported_interfaces,
+ sizeof(config->lpi_interfaces));
+}
+
+static struct phylink_pcs *gsw1xx_phylink_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct gswip_priv *gswip_priv = dp->ds->priv;
+ struct gsw1xx_priv *gsw1xx_priv = container_of(gswip_priv,
+ struct gsw1xx_priv,
+ gswip);
+
+ switch (dp->index) {
+ case GSW1XX_SGMII_PORT:
+ return &gsw1xx_priv->pcs;
+ default:
+ return NULL;
+ }
+}
+
+static struct regmap *gsw1xx_regmap_init(struct gsw1xx_priv *priv,
+ const char *name,
+ unsigned int reg_base,
+ unsigned int max_register)
+{
+ const struct regmap_config config = {
+ .name = name,
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_base = reg_base,
+ .max_register = max_register,
+ .lock = gsw1xx_mdio_regmap_lock,
+ .unlock = gsw1xx_mdio_regmap_unlock,
+ .lock_arg = &priv->mdio_dev->bus->mdio_lock,
+ };
+
+ return devm_regmap_init(&priv->mdio_dev->dev, &gsw1xx_regmap_bus,
+ priv, &config);
+}
+
+static int gsw1xx_probe(struct mdio_device *mdiodev)
+{
+ struct device *dev = &mdiodev->dev;
+ struct gsw1xx_priv *priv;
+ u32 version;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->mdio_dev = mdiodev;
+ priv->smdio_badr = GSW1XX_SMDIO_BADR_UNKNOWN;
+
+ priv->gswip.dev = dev;
+ priv->gswip.hw_info = of_device_get_match_data(dev);
+ if (!priv->gswip.hw_info)
+ return -EINVAL;
+
+ priv->gswip.gswip = gsw1xx_regmap_init(priv, "switch",
+ GSW1XX_SWITCH_BASE, 0xfff);
+ if (IS_ERR(priv->gswip.gswip))
+ return PTR_ERR(priv->gswip.gswip);
+
+ priv->gswip.mdio = gsw1xx_regmap_init(priv, "mdio", GSW1XX_MMDIO_BASE,
+ 0xff);
+ if (IS_ERR(priv->gswip.mdio))
+ return PTR_ERR(priv->gswip.mdio);
+
+ priv->gswip.mii = gsw1xx_regmap_init(priv, "mii", GSW1XX_RGMII_BASE,
+ 0xff);
+ if (IS_ERR(priv->gswip.mii))
+ return PTR_ERR(priv->gswip.mii);
+
+ priv->sgmii = gsw1xx_regmap_init(priv, "sgmii", GSW1XX_SGMII_BASE,
+ 0xfff);
+ if (IS_ERR(priv->sgmii))
+ return PTR_ERR(priv->sgmii);
+
+ priv->gpio = gsw1xx_regmap_init(priv, "gpio", GSW1XX_GPIO_BASE, 0xff);
+ if (IS_ERR(priv->gpio))
+ return PTR_ERR(priv->gpio);
+
+ priv->clk = gsw1xx_regmap_init(priv, "clk", GSW1XX_CLK_BASE, 0xff);
+ if (IS_ERR(priv->clk))
+ return PTR_ERR(priv->clk);
+
+ priv->shell = gsw1xx_regmap_init(priv, "shell", GSW1XX_SHELL_BASE,
+ 0xff);
+ if (IS_ERR(priv->shell))
+ return PTR_ERR(priv->shell);
+
+ priv->pcs.ops = &gsw1xx_pcs_ops;
+ priv->pcs.poll = true;
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ priv->pcs.supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ priv->pcs.supported_interfaces);
+ if (priv->gswip.hw_info->supports_2500m)
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ priv->pcs.supported_interfaces);
+ priv->tbi_interface = PHY_INTERFACE_MODE_NA;
+
+ /* assert SGMII reset to power down SGMII unit */
+ ret = regmap_set_bits(priv->shell, GSW1XX_SHELL_RST_REQ,
+ GSW1XX_RST_REQ_SGMII_SHELL);
+ if (ret < 0)
+ return ret;
+
+ /* configure GPIO pin-mux for MMDIO in case of external PHY connected to
+ * SGMII or RGMII as slave interface
+ */
+ regmap_set_bits(priv->gpio, GPIO_ALTSEL0, 3);
+ regmap_set_bits(priv->gpio, GPIO_ALTSEL1, 3);
+
+ ret = regmap_read(priv->gswip.gswip, GSWIP_VERSION, &version);
+ if (ret)
+ return ret;
+
+ ret = gswip_probe_common(&priv->gswip, version);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, &priv->gswip);
+
+ return 0;
+}
+
+static void gsw1xx_remove(struct mdio_device *mdiodev)
+{
+ struct gswip_priv *priv = dev_get_drvdata(&mdiodev->dev);
+
+ if (!priv)
+ return;
+
+ gswip_disable_switch(priv);
+
+ dsa_unregister_switch(priv->ds);
+}
+
+static void gsw1xx_shutdown(struct mdio_device *mdiodev)
+{
+ struct gswip_priv *priv = dev_get_drvdata(&mdiodev->dev);
+
+ if (!priv)
+ return;
+
+ dev_set_drvdata(&mdiodev->dev, NULL);
+
+ gswip_disable_switch(priv);
+}
+
+static const struct gswip_hw_info gsw12x_data = {
+ .max_ports = GSW1XX_PORTS,
+ .allowed_cpu_ports = BIT(GSW1XX_MII_PORT) | BIT(GSW1XX_SGMII_PORT),
+ .mii_ports = BIT(GSW1XX_MII_PORT),
+ .mii_port_reg_offset = -GSW1XX_MII_PORT,
+ .mac_select_pcs = gsw1xx_phylink_mac_select_pcs,
+ .phylink_get_caps = &gsw1xx_phylink_get_caps,
+ .supports_2500m = true,
+ .pce_microcode = &gsw1xx_pce_microcode,
+ .pce_microcode_size = ARRAY_SIZE(gsw1xx_pce_microcode),
+ .tag_protocol = DSA_TAG_PROTO_MXL_GSW1XX,
+};
+
+static const struct gswip_hw_info gsw140_data = {
+ .max_ports = GSW1XX_PORTS,
+ .allowed_cpu_ports = BIT(GSW1XX_MII_PORT) | BIT(GSW1XX_SGMII_PORT),
+ .mii_ports = BIT(GSW1XX_MII_PORT),
+ .mii_port_reg_offset = -GSW1XX_MII_PORT,
+ .mac_select_pcs = gsw1xx_phylink_mac_select_pcs,
+ .phylink_get_caps = &gsw1xx_phylink_get_caps,
+ .supports_2500m = true,
+ .pce_microcode = &gsw1xx_pce_microcode,
+ .pce_microcode_size = ARRAY_SIZE(gsw1xx_pce_microcode),
+ .tag_protocol = DSA_TAG_PROTO_MXL_GSW1XX,
+};
+
+static const struct gswip_hw_info gsw141_data = {
+ .max_ports = GSW1XX_PORTS,
+ .allowed_cpu_ports = BIT(GSW1XX_MII_PORT) | BIT(GSW1XX_SGMII_PORT),
+ .mii_ports = BIT(GSW1XX_MII_PORT),
+ .mii_port_reg_offset = -GSW1XX_MII_PORT,
+ .mac_select_pcs = gsw1xx_phylink_mac_select_pcs,
+ .phylink_get_caps = gsw1xx_phylink_get_caps,
+ .pce_microcode = &gsw1xx_pce_microcode,
+ .pce_microcode_size = ARRAY_SIZE(gsw1xx_pce_microcode),
+ .tag_protocol = DSA_TAG_PROTO_MXL_GSW1XX,
+};
+
+/*
+ * GSW125 is the industrial temperature version of GSW120.
+ * GSW145 is the industrial temperature version of GSW140.
+ */
+static const struct of_device_id gsw1xx_of_match[] = {
+ { .compatible = "maxlinear,gsw120", .data = &gsw12x_data },
+ { .compatible = "maxlinear,gsw125", .data = &gsw12x_data },
+ { .compatible = "maxlinear,gsw140", .data = &gsw140_data },
+ { .compatible = "maxlinear,gsw141", .data = &gsw141_data },
+ { .compatible = "maxlinear,gsw145", .data = &gsw140_data },
+ { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, gsw1xx_of_match);
+
+static struct mdio_driver gsw1xx_driver = {
+ .probe = gsw1xx_probe,
+ .remove = gsw1xx_remove,
+ .shutdown = gsw1xx_shutdown,
+ .mdiodrv.driver = {
+ .name = "mxl-gsw1xx",
+ .of_match_table = gsw1xx_of_match,
+ },
+};
+
+mdio_module_driver(gsw1xx_driver);
+
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_DESCRIPTION("Driver for MaxLinear GSW1xx ethernet switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.h b/drivers/net/dsa/lantiq/mxl-gsw1xx.h
new file mode 100644
index 000000000000..38e03c048a26
--- /dev/null
+++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Register definitions for MaxLinear GSW1xx series switches
+ *
+ * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org>
+ * Copyright (C) 2023 - 2024 MaxLinear Inc.
+ */
+#ifndef __MXL_GSW1XX_H
+#define __MXL_GSW1XX_H
+
+#include <linux/bitfield.h>
+
+#define GSW1XX_PORTS 6
+/* Port used for RGMII or optional RMII */
+#define GSW1XX_MII_PORT 5
+/* Port used for SGMII */
+#define GSW1XX_SGMII_PORT 4
+
+#define GSW1XX_SYS_CLK_FREQ 340000000
+
+/* SMDIO switch register base address */
+#define GSW1XX_SMDIO_BADR 0x1f
+#define GSW1XX_SMDIO_BADR_UNKNOWN -1
+
+/* GSW1XX SGMII PCS */
+#define GSW1XX_SGMII_BASE 0xd000
+#define GSW1XX_SGMII_PHY_HWBU_CTRL 0x009
+#define GSW1XX_SGMII_PHY_HWBU_CTRL_EN_HWBU_FSM BIT(0)
+#define GSW1XX_SGMII_PHY_HWBU_CTRL_HW_FSM_EN BIT(3)
+#define GSW1XX_SGMII_TBI_TXANEGH 0x300
+#define GSW1XX_SGMII_TBI_TXANEGL 0x301
+#define GSW1XX_SGMII_TBI_ANEGCTL 0x304
+#define GSW1XX_SGMII_TBI_ANEGCTL_LT GENMASK(1, 0)
+#define GSW1XX_SGMII_TBI_ANEGCTL_LT_10US 0
+#define GSW1XX_SGMII_TBI_ANEGCTL_LT_1_6MS 1
+#define GSW1XX_SGMII_TBI_ANEGCTL_LT_5MS 2
+#define GSW1XX_SGMII_TBI_ANEGCTL_LT_10MS 3
+#define GSW1XX_SGMII_TBI_ANEGCTL_ANEGEN BIT(2)
+#define GSW1XX_SGMII_TBI_ANEGCTL_RANEG BIT(3)
+#define GSW1XX_SGMII_TBI_ANEGCTL_OVRABL BIT(4)
+#define GSW1XX_SGMII_TBI_ANEGCTL_OVRANEG BIT(5)
+#define GSW1XX_SGMII_TBI_ANEGCTL_ANMODE GENMASK(7, 6)
+#define GSW1XX_SGMII_TBI_ANEGCTL_ANMODE_1000BASEX 1
+#define GSW1XX_SGMII_TBI_ANEGCTL_ANMODE_SGMII_PHY 2
+#define GSW1XX_SGMII_TBI_ANEGCTL_ANMODE_SGMII_MAC 3
+#define GSW1XX_SGMII_TBI_ANEGCTL_BCOMP BIT(15)
+
+#define GSW1XX_SGMII_TBI_TBICTL 0x305
+#define GSW1XX_SGMII_TBI_TBICTL_INITTBI BIT(0)
+#define GSW1XX_SGMII_TBI_TBICTL_ENTBI BIT(1)
+#define GSW1XX_SGMII_TBI_TBICTL_CRSTRR BIT(4)
+#define GSW1XX_SGMII_TBI_TBICTL_CRSOFF BIT(5)
+#define GSW1XX_SGMII_TBI_TBISTAT 0x309
+#define GSW1XX_SGMII_TBI_TBISTAT_LINK BIT(0)
+#define GSW1XX_SGMII_TBI_TBISTAT_AN_COMPLETE BIT(1)
+#define GSW1XX_SGMII_TBI_LPSTAT 0x30a
+#define GSW1XX_SGMII_TBI_LPSTAT_DUPLEX BIT(0)
+#define GSW1XX_SGMII_TBI_LPSTAT_PAUSE_RX BIT(1)
+#define GSW1XX_SGMII_TBI_LPSTAT_PAUSE_TX BIT(2)
+#define GSW1XX_SGMII_TBI_LPSTAT_SPEED GENMASK(6, 5)
+#define GSW1XX_SGMII_TBI_LPSTAT_SPEED_10 0
+#define GSW1XX_SGMII_TBI_LPSTAT_SPEED_100 1
+#define GSW1XX_SGMII_TBI_LPSTAT_SPEED_1000 2
+#define GSW1XX_SGMII_TBI_LPSTAT_SPEED_NOSGMII 3
+#define GSW1XX_SGMII_PHY_D 0x100
+#define GSW1XX_SGMII_PHY_A 0x101
+#define GSW1XX_SGMII_PHY_C 0x102
+#define GSW1XX_SGMII_PHY_STATUS BIT(0)
+#define GSW1XX_SGMII_PHY_READ BIT(4)
+#define GSW1XX_SGMII_PHY_WRITE BIT(8)
+#define GSW1XX_SGMII_PHY_RESET_N BIT(12)
+#define GSW1XX_SGMII_PCS_RXB_CTL 0x401
+#define GSW1XX_SGMII_PCS_RXB_CTL_INIT_RX_RXB BIT(1)
+#define GSW1XX_SGMII_PCS_TXB_CTL 0x404
+#define GSW1XX_SGMII_PCS_TXB_CTL_INIT_TX_TXB BIT(1)
+
+#define GSW1XX_SGMII_PHY_RX0_CFG2 0x004
+#define GSW1XX_SGMII_PHY_RX0_CFG2_EQ GENMASK(2, 0)
+#define GSW1XX_SGMII_PHY_RX0_CFG2_EQ_DEF 2
+#define GSW1XX_SGMII_PHY_RX0_CFG2_INVERT BIT(3)
+#define GSW1XX_SGMII_PHY_RX0_CFG2_LOS_EN BIT(4)
+#define GSW1XX_SGMII_PHY_RX0_CFG2_TERM_EN BIT(5)
+#define GSW1XX_SGMII_PHY_RX0_CFG2_FILT_CNT GENMASK(12, 6)
+#define GSW1XX_SGMII_PHY_RX0_CFG2_FILT_CNT_DEF 20
+
+#define GSW1XX_SGMII_PHY_TX0_CFG3 0x007
+#define GSW1XX_SGMII_PHY_TX0_CFG3_VBOOST_EN BIT(12)
+#define GSW1XX_SGMII_PHY_TX0_CFG3_VBOOST_LEVEL GENMASK(11, 9)
+#define GSW1XX_SGMII_PHY_TX0_CFG3_VBOOST_LEVEL_DEF 4
+#define GSW1XX_SGMII_PHY_TX0_CFG3_INVERT BIT(8)
+
+/* GSW1XX PDI Registers */
+#define GSW1XX_SWITCH_BASE 0xe000
+
+/* GSW1XX MII Registers */
+#define GSW1XX_RGMII_BASE 0xf100
+
+/* GSW1XX GPIO Registers */
+#define GSW1XX_GPIO_BASE 0xf300
+#define GPIO_ALTSEL0 0x83
+#define GPIO_ALTSEL0_EXTPHY_MUX_VAL 0x03c3
+#define GPIO_ALTSEL1 0x84
+#define GPIO_ALTSEL1_EXTPHY_MUX_VAL 0x003f
+
+/* MDIO bus controller */
+#define GSW1XX_MMDIO_BASE 0xf400
+
+/* generic IC registers */
+#define GSW1XX_SHELL_BASE 0xfa00
+#define GSW1XX_SHELL_RST_REQ 0x01
+#define GSW1XX_RST_REQ_SGMII_SHELL BIT(5)
+/* RGMII PAD Slew Control Register */
+#define GSW1XX_SHELL_RGMII_SLEW_CFG 0x78
+#define RGMII_SLEW_CFG_RX_2_5_V BIT(4)
+#define RGMII_SLEW_CFG_TX_2_5_V BIT(5)
+
+/* SGMII clock related settings */
+#define GSW1XX_CLK_BASE 0xf900
+#define GSW1XX_CLK_NCO_CTRL 0x68
+#define GSW1XX_SGMII_HSP_MASK GENMASK(3, 2)
+#define GSW1XX_SGMII_SEL BIT(1)
+#define GSW1XX_SGMII_1G 0x0
+#define GSW1XX_SGMII_2G5 0xc
+#define GSW1XX_SGMII_1G_NCO1 0x0
+#define GSW1XX_SGMII_2G5_NCO2 0x2
+
+#endif /* __MXL_GSW1XX_H */
diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx_pce.h b/drivers/net/dsa/lantiq/mxl-gsw1xx_pce.h
new file mode 100644
index 000000000000..eefcd411a340
--- /dev/null
+++ b/drivers/net/dsa/lantiq/mxl-gsw1xx_pce.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PCE microcode code update for driver for MaxLinear GSW1xx switch chips
+ *
+ * Copyright (C) 2023 - 2024 MaxLinear Inc.
+ * Copyright (C) 2022 Snap One, LLC. All rights reserved.
+ * Copyright (C) 2017 - 2019 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (C) 2012 John Crispin <john@phrozen.org>
+ * Copyright (C) 2010 Lantiq Deutschland
+ */
+
+#include "lantiq_gswip.h"
+
+#define INSTR 0
+#define IPV6 1
+#define LENACCU 2
+
+/* GSWIP_2.X */
+enum {
+ OUT_MAC0 = 0,
+ OUT_MAC1,
+ OUT_MAC2,
+ OUT_MAC3,
+ OUT_MAC4,
+ OUT_MAC5,
+ OUT_ETHTYP,
+ OUT_VTAG0,
+ OUT_VTAG1,
+ OUT_ITAG0,
+ OUT_ITAG1, /* 10 */
+ OUT_ITAG2,
+ OUT_ITAG3,
+ OUT_IP0,
+ OUT_IP1,
+ OUT_IP2,
+ OUT_IP3,
+ OUT_SIP0,
+ OUT_SIP1,
+ OUT_SIP2,
+ OUT_SIP3, /* 20 */
+ OUT_SIP4,
+ OUT_SIP5,
+ OUT_SIP6,
+ OUT_SIP7,
+ OUT_DIP0,
+ OUT_DIP1,
+ OUT_DIP2,
+ OUT_DIP3,
+ OUT_DIP4,
+ OUT_DIP5, /* 30 */
+ OUT_DIP6,
+ OUT_DIP7,
+ OUT_SESID,
+ OUT_PROT,
+ OUT_APP0,
+ OUT_APP1,
+ OUT_IGMP0,
+ OUT_IGMP1,
+ OUT_STAG0 = 61,
+ OUT_STAG1 = 62,
+ OUT_NONE = 63,
+};
+
+/* parser's microcode flag type */
+enum {
+ FLAG_ITAG = 0,
+ FLAG_VLAN,
+ FLAG_SNAP,
+ FLAG_PPPOE,
+ FLAG_IPV6,
+ FLAG_IPV6FL,
+ FLAG_IPV4,
+ FLAG_IGMP,
+ FLAG_TU,
+ FLAG_HOP,
+ FLAG_NN1, /* 10 */
+ FLAG_NN2,
+ FLAG_END,
+ FLAG_NO, /* 13 */
+ FLAG_SVLAN, /* 14 */
+};
+
+#define PCE_MC_M(val, msk, ns, out, len, type, flags, ipv4_len) \
+ { (val), (msk), ((ns) << 10 | (out) << 4 | (len) >> 1),\
+ ((len) & 1) << 15 | (type) << 13 | (flags) << 9 | (ipv4_len) << 8 }
+
+/* V22_2X (IPv6 issue fixed) */
+static const struct gswip_pce_microcode gsw1xx_pce_microcode[] = {
+ /* value mask ns fields L type flags ipv4_len */
+ PCE_MC_M(0x88c3, 0xFFFF, 1, OUT_ITAG0, 4, INSTR, FLAG_ITAG, 0),
+ PCE_MC_M(0x8100, 0xFFFF, 4, OUT_STAG0, 2, INSTR, FLAG_SVLAN, 0),
+ PCE_MC_M(0x88A8, 0xFFFF, 4, OUT_STAG0, 2, INSTR, FLAG_SVLAN, 0),
+ PCE_MC_M(0x9100, 0xFFFF, 4, OUT_STAG0, 2, INSTR, FLAG_SVLAN, 0),
+ PCE_MC_M(0x8100, 0xFFFF, 5, OUT_VTAG0, 2, INSTR, FLAG_VLAN, 0),
+ PCE_MC_M(0x88A8, 0xFFFF, 6, OUT_VTAG0, 2, INSTR, FLAG_VLAN, 0),
+ PCE_MC_M(0x9100, 0xFFFF, 4, OUT_VTAG0, 2, INSTR, FLAG_VLAN, 0),
+ PCE_MC_M(0x8864, 0xFFFF, 20, OUT_ETHTYP, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0800, 0xFFFF, 24, OUT_ETHTYP, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x86DD, 0xFFFF, 25, OUT_ETHTYP, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x8863, 0xFFFF, 19, OUT_ETHTYP, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0xF800, 13, OUT_NONE, 0, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 44, OUT_ETHTYP, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0600, 0x0600, 44, OUT_ETHTYP, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 15, OUT_NONE, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0xAAAA, 0xFFFF, 17, OUT_NONE, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0300, 0xFF00, 45, OUT_NONE, 0, INSTR, FLAG_SNAP, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_DIP7, 3, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 21, OUT_DIP7, 3, INSTR, FLAG_PPPOE, 0),
+ PCE_MC_M(0x0021, 0xFFFF, 24, OUT_NONE, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0057, 0xFFFF, 25, OUT_NONE, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 44, OUT_NONE, 0, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x4000, 0xF000, 27, OUT_IP0, 4, INSTR, FLAG_IPV4, 1),
+ PCE_MC_M(0x6000, 0xF000, 30, OUT_IP0, 3, INSTR, FLAG_IPV6, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 28, OUT_IP3, 2, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 29, OUT_SIP0, 4, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 44, OUT_NONE, 0, LENACCU, FLAG_NO, 0),
+ PCE_MC_M(0x1100, 0xFF00, 43, OUT_PROT, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0600, 0xFF00, 43, OUT_PROT, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0xFF00, 36, OUT_IP3, 17, INSTR, FLAG_HOP, 0),
+ PCE_MC_M(0x2B00, 0xFF00, 36, OUT_IP3, 17, INSTR, FLAG_NN1, 0),
+ PCE_MC_M(0x3C00, 0xFF00, 36, OUT_IP3, 17, INSTR, FLAG_NN2, 0),
+ PCE_MC_M(0x0000, 0x0000, 43, OUT_PROT, 1, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x00F0, 38, OUT_NONE, 0, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 44, OUT_NONE, 0, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0xFF00, 36, OUT_NONE, 0, IPV6, FLAG_HOP, 0),
+ PCE_MC_M(0x2B00, 0xFF00, 36, OUT_NONE, 0, IPV6, FLAG_NN1, 0),
+ PCE_MC_M(0x3C00, 0xFF00, 36, OUT_NONE, 0, IPV6, FLAG_NN2, 0),
+ PCE_MC_M(0x0000, 0x00FC, 44, OUT_PROT, 0, IPV6, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 44, OUT_NONE, 0, IPV6, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 44, OUT_SIP0, 16, INSTR, FLAG_NO, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_APP0, 4, INSTR, FLAG_IGMP, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+ PCE_MC_M(0x0000, 0x0000, 45, OUT_NONE, 0, INSTR, FLAG_END, 0),
+};
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index d747ea1c41a7..5facffbb9c9a 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -244,7 +244,7 @@ static int ksz9477_pcs_read(struct mii_bus *bus, int phy, int mmd, int reg)
p->phydev.link = 0;
}
} else if (reg == MII_BMSR) {
- p->phydev.link = (val & BMSR_LSTATUS);
+ p->phydev.link = !!(val & BMSR_LSTATUS);
}
}
@@ -1355,9 +1355,15 @@ void ksz9477_config_cpu_port(struct dsa_switch *ds)
}
}
+#define RESV_MCAST_CNT 8
+
+static u8 reserved_mcast_map[RESV_MCAST_CNT] = { 0, 1, 3, 16, 32, 33, 2, 17 };
+
int ksz9477_enable_stp_addr(struct ksz_device *dev)
{
+ u8 i, ports, update;
const u32 *masks;
+ bool override;
u32 data;
int ret;
@@ -1366,23 +1372,87 @@ int ksz9477_enable_stp_addr(struct ksz_device *dev)
/* Enable Reserved multicast table */
ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_RESV_MCAST_ENABLE, true);
- /* Set the Override bit for forwarding BPDU packet to CPU */
- ret = ksz_write32(dev, REG_SW_ALU_VAL_B,
- ALU_V_OVERRIDE | BIT(dev->cpu_port));
- if (ret < 0)
- return ret;
+ /* The reserved multicast address table has 8 entries. Each entry has
+ * a default value of which port to forward. It is assumed the host
+ * port is the last port in most of the switches, but that is not the
+ * case for KSZ9477 or maybe KSZ9897. For LAN937X family the default
+ * port is port 5, the first RGMII port. It is okay for LAN9370, a
+ * 5-port switch, but may not be correct for the other 8-port
+ * versions. It is necessary to update the whole table to forward to
+ * the right ports.
+ * Furthermore PTP messages can use a reserved multicast address and
+ * the host will not receive them if this table is not correct.
+ */
+ for (i = 0; i < RESV_MCAST_CNT; i++) {
+ data = reserved_mcast_map[i] <<
+ dev->info->shifts[ALU_STAT_INDEX];
+ data |= ALU_STAT_START |
+ masks[ALU_STAT_DIRECT] |
+ masks[ALU_RESV_MCAST_ADDR] |
+ masks[ALU_STAT_READ];
+ ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+ if (ret < 0)
+ return ret;
- data = ALU_STAT_START | ALU_RESV_MCAST_ADDR | masks[ALU_STAT_WRITE];
+ /* wait to be finished */
+ ret = ksz9477_wait_alu_sta_ready(dev);
+ if (ret < 0)
+ return ret;
- ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
- if (ret < 0)
- return ret;
+ ret = ksz_read32(dev, REG_SW_ALU_VAL_B, &data);
+ if (ret < 0)
+ return ret;
- /* wait to be finished */
- ret = ksz9477_wait_alu_sta_ready(dev);
- if (ret < 0) {
- dev_err(dev->dev, "Failed to update Reserved Multicast table\n");
- return ret;
+ override = false;
+ ports = data & dev->port_mask;
+ switch (i) {
+ case 0:
+ case 6:
+ /* Change the host port. */
+ update = BIT(dev->cpu_port);
+ override = true;
+ break;
+ case 2:
+ /* Change the host port. */
+ update = BIT(dev->cpu_port);
+ break;
+ case 4:
+ case 5:
+ case 7:
+ /* Skip the host port. */
+ update = dev->port_mask & ~BIT(dev->cpu_port);
+ break;
+ default:
+ update = ports;
+ break;
+ }
+ if (update != ports || override) {
+ data &= ~dev->port_mask;
+ data |= update;
+ /* Set Override bit to receive frame even when port is
+ * closed.
+ */
+ if (override)
+ data |= ALU_V_OVERRIDE;
+ ret = ksz_write32(dev, REG_SW_ALU_VAL_B, data);
+ if (ret < 0)
+ return ret;
+
+ data = reserved_mcast_map[i] <<
+ dev->info->shifts[ALU_STAT_INDEX];
+ data |= ALU_STAT_START |
+ masks[ALU_STAT_DIRECT] |
+ masks[ALU_RESV_MCAST_ADDR] |
+ masks[ALU_STAT_WRITE];
+ ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data);
+ if (ret < 0)
+ return ret;
+
+ /* wait to be finished */
+ ret = ksz9477_wait_alu_sta_ready(dev);
+ if (ret < 0)
+ return ret;
+ }
}
return 0;
diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h
index ff579920078e..61ea11e3338e 100644
--- a/drivers/net/dsa/microchip/ksz9477_reg.h
+++ b/drivers/net/dsa/microchip/ksz9477_reg.h
@@ -2,7 +2,7 @@
/*
* Microchip KSZ9477 register definitions
*
- * Copyright (C) 2017-2024 Microchip Technology Inc.
+ * Copyright (C) 2017-2025 Microchip Technology Inc.
*/
#ifndef __KSZ9477_REGS_H
@@ -397,7 +397,6 @@
#define ALU_RESV_MCAST_INDEX_M (BIT(6) - 1)
#define ALU_STAT_START BIT(7)
-#define ALU_RESV_MCAST_ADDR BIT(1)
#define REG_SW_ALU_VAL_A 0x0420
diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index a962055bfdbd..0c10351fe5eb 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -808,6 +808,8 @@ static const u16 ksz9477_regs[] = {
static const u32 ksz9477_masks[] = {
[ALU_STAT_WRITE] = 0,
[ALU_STAT_READ] = 1,
+ [ALU_STAT_DIRECT] = 0,
+ [ALU_RESV_MCAST_ADDR] = BIT(1),
[P_MII_TX_FLOW_CTRL] = BIT(5),
[P_MII_RX_FLOW_CTRL] = BIT(3),
};
@@ -835,6 +837,8 @@ static const u8 ksz9477_xmii_ctrl1[] = {
static const u32 lan937x_masks[] = {
[ALU_STAT_WRITE] = 1,
[ALU_STAT_READ] = 2,
+ [ALU_STAT_DIRECT] = BIT(3),
+ [ALU_RESV_MCAST_ADDR] = BIT(2),
[P_MII_TX_FLOW_CTRL] = BIT(5),
[P_MII_RX_FLOW_CTRL] = BIT(3),
};
@@ -2583,8 +2587,8 @@ static int ksz_irq_phy_setup(struct ksz_device *dev)
irq = irq_find_mapping(dev->ports[port].pirq.domain,
PORT_SRC_PHY_INT);
- if (irq < 0) {
- ret = irq;
+ if (!irq) {
+ ret = -EINVAL;
goto out;
}
ds->user_mii_bus->irq[phy] = irq;
@@ -2948,8 +2952,8 @@ static int ksz_pirq_setup(struct ksz_device *dev, u8 p)
snprintf(pirq->name, sizeof(pirq->name), "port_irq-%d", p);
pirq->irq_num = irq_find_mapping(dev->girq.domain, p);
- if (pirq->irq_num < 0)
- return pirq->irq_num;
+ if (!pirq->irq_num)
+ return -EINVAL;
return ksz_irq_common_setup(dev, pirq);
}
@@ -3034,12 +3038,12 @@ static int ksz_setup(struct dsa_switch *ds)
dsa_switch_for_each_user_port(dp, dev->ds) {
ret = ksz_pirq_setup(dev, dp->index);
if (ret)
- goto out_girq;
+ goto port_release;
if (dev->info->ptp_capable) {
ret = ksz_ptp_irq_setup(ds, dp->index);
if (ret)
- goto out_pirq;
+ goto pirq_release;
}
}
}
@@ -3049,7 +3053,7 @@ static int ksz_setup(struct dsa_switch *ds)
if (ret) {
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
ret);
- goto out_ptpirq;
+ goto port_release;
}
}
@@ -3072,17 +3076,16 @@ static int ksz_setup(struct dsa_switch *ds)
out_ptp_clock_unregister:
if (dev->info->ptp_capable)
ksz_ptp_clock_unregister(ds);
-out_ptpirq:
- if (dev->irq > 0 && dev->info->ptp_capable)
- dsa_switch_for_each_user_port(dp, dev->ds)
- ksz_ptp_irq_free(ds, dp->index);
-out_pirq:
- if (dev->irq > 0)
- dsa_switch_for_each_user_port(dp, dev->ds)
+port_release:
+ if (dev->irq > 0) {
+ dsa_switch_for_each_user_port_continue_reverse(dp, dev->ds) {
+ if (dev->info->ptp_capable)
+ ksz_ptp_irq_free(ds, dp->index);
+pirq_release:
ksz_irq_free(&dev->ports[dp->index].pirq);
-out_girq:
- if (dev->irq > 0)
+ }
ksz_irq_free(&dev->girq);
+ }
return ret;
}
diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h
index a1eb39771bb9..c65188cd3c0a 100644
--- a/drivers/net/dsa/microchip/ksz_common.h
+++ b/drivers/net/dsa/microchip/ksz_common.h
@@ -294,6 +294,8 @@ enum ksz_masks {
DYNAMIC_MAC_TABLE_TIMESTAMP,
ALU_STAT_WRITE,
ALU_STAT_READ,
+ ALU_STAT_DIRECT,
+ ALU_RESV_MCAST_ADDR,
P_MII_TX_FLOW_CTRL,
P_MII_RX_FLOW_CTRL,
};
diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c
index 35fc21b1ee48..997e4a76d0a6 100644
--- a/drivers/net/dsa/microchip/ksz_ptp.c
+++ b/drivers/net/dsa/microchip/ksz_ptp.c
@@ -1093,19 +1093,19 @@ static int ksz_ptp_msg_irq_setup(struct ksz_port *port, u8 n)
static const char * const name[] = {"pdresp-msg", "xdreq-msg",
"sync-msg"};
const struct ksz_dev_ops *ops = port->ksz_dev->dev_ops;
+ struct ksz_irq *ptpirq = &port->ptpirq;
struct ksz_ptp_irq *ptpmsg_irq;
ptpmsg_irq = &port->ptpmsg_irq[n];
+ ptpmsg_irq->num = irq_create_mapping(ptpirq->domain, n);
+ if (!ptpmsg_irq->num)
+ return -EINVAL;
ptpmsg_irq->port = port;
ptpmsg_irq->ts_reg = ops->get_port_addr(port->num, ts_reg[n]);
strscpy(ptpmsg_irq->name, name[n]);
- ptpmsg_irq->num = irq_find_mapping(port->ptpirq.domain, n);
- if (ptpmsg_irq->num < 0)
- return ptpmsg_irq->num;
-
return request_threaded_irq(ptpmsg_irq->num, NULL,
ksz_ptp_msg_thread_fn, IRQF_ONESHOT,
ptpmsg_irq->name, ptpmsg_irq);
@@ -1135,12 +1135,9 @@ int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
if (!ptpirq->domain)
return -ENOMEM;
- for (irq = 0; irq < ptpirq->nirqs; irq++)
- irq_create_mapping(ptpirq->domain, irq);
-
ptpirq->irq_num = irq_find_mapping(port->pirq.domain, PORT_SRC_PTP_INT);
- if (ptpirq->irq_num < 0) {
- ret = ptpirq->irq_num;
+ if (!ptpirq->irq_num) {
+ ret = -EINVAL;
goto out;
}
@@ -1159,12 +1156,11 @@ int ksz_ptp_irq_setup(struct dsa_switch *ds, u8 p)
out_ptp_msg:
free_irq(ptpirq->irq_num, ptpirq);
- while (irq--)
+ while (irq--) {
free_irq(port->ptpmsg_irq[irq].num, &port->ptpmsg_irq[irq]);
-out:
- for (irq = 0; irq < ptpirq->nirqs; irq++)
irq_dispose_mapping(port->ptpmsg_irq[irq].num);
-
+ }
+out:
irq_domain_remove(ptpirq->domain);
return ret;
diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c
index b1ae3b9de3d1..5a1496fff445 100644
--- a/drivers/net/dsa/microchip/lan937x_main.c
+++ b/drivers/net/dsa/microchip/lan937x_main.c
@@ -540,6 +540,7 @@ static void lan937x_set_tune_adj(struct ksz_device *dev, int port,
ksz_pread16(dev, port, reg, &data16);
/* Update tune Adjust */
+ data16 &= ~PORT_TUNE_ADJ;
data16 |= FIELD_PREP(PORT_TUNE_ADJ, val);
ksz_pwrite16(dev, port, reg, data16);
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 548b85befbf4..b9423389c2ef 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -3254,7 +3254,7 @@ static int mt7988_setup(struct dsa_switch *ds)
return mt7531_setup_common(ds);
}
-const struct dsa_switch_ops mt7530_switch_ops = {
+static const struct dsa_switch_ops mt7530_switch_ops = {
.get_tag_protocol = mtk_get_tag_protocol,
.setup = mt753x_setup,
.preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port,
@@ -3290,8 +3290,9 @@ const struct dsa_switch_ops mt7530_switch_ops = {
.set_mac_eee = mt753x_set_mac_eee,
.conduit_state_change = mt753x_conduit_state_change,
.port_setup_tc = mt753x_setup_tc,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
};
-EXPORT_SYMBOL_GPL(mt7530_switch_ops);
static const struct phylink_mac_ops mt753x_phylink_mac_ops = {
.mac_select_pcs = mt753x_phylink_mac_select_pcs,
diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h
index 7e47cd9af256..3e0090bed298 100644
--- a/drivers/net/dsa/mt7530.h
+++ b/drivers/net/dsa/mt7530.h
@@ -939,7 +939,6 @@ static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p,
int mt7530_probe_common(struct mt7530_priv *priv);
void mt7530_remove_common(struct mt7530_priv *priv);
-extern const struct dsa_switch_ops mt7530_switch_ops;
extern const struct mt753x_info mt753x_table[];
#endif /* __MT7530_H */
diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c
index 294312b58e4f..9c8ac14cd4f5 100644
--- a/drivers/net/dsa/mv88e6060.c
+++ b/drivers/net/dsa/mv88e6060.c
@@ -297,6 +297,8 @@ static const struct dsa_switch_ops mv88e6060_switch_ops = {
.phy_read = mv88e6060_phy_read,
.phy_write = mv88e6060_phy_write,
.phylink_get_caps = mv88e6060_phylink_get_caps,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
};
static int mv88e6060_probe(struct mdio_device *mdiodev)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 20ab558fde24..9e5ede932b42 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -1233,6 +1233,7 @@ static int felix_port_enable(struct dsa_switch *ds, int port,
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct ocelot *ocelot = ds->priv;
+ struct felix *felix = ocelot_to_felix(ocelot);
if (!dsa_port_is_user(dp))
return 0;
@@ -1246,7 +1247,25 @@ static int felix_port_enable(struct dsa_switch *ds, int port,
}
}
- return 0;
+ if (!dp->hsr_dev || felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q)
+ return 0;
+
+ return dsa_port_simple_hsr_join(ds, port, dp->hsr_dev, NULL);
+}
+
+static void felix_port_disable(struct dsa_switch *ds, int port)
+{
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct ocelot *ocelot = ds->priv;
+ struct felix *felix = ocelot_to_felix(ocelot);
+
+ if (!dsa_port_is_user(dp))
+ return;
+
+ if (!dp->hsr_dev || felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q)
+ return;
+
+ dsa_port_simple_hsr_leave(ds, port, dp->hsr_dev);
}
static void felix_port_qos_map_init(struct ocelot *ocelot, int port)
@@ -2232,6 +2251,52 @@ static void felix_get_mm_stats(struct dsa_switch *ds, int port,
ocelot_port_get_mm_stats(ocelot, port, stats);
}
+/* Depending on port type, we may be able to support the offload later (with
+ * the "ocelot"/"seville" tagging protocols), or never.
+ * If we return 0, the dp->hsr_dev reference is kept for later; if we return
+ * -EOPNOTSUPP, it is cleared (which helps to not bother
+ * dsa_port_simple_hsr_leave() with an offload that didn't pass validation).
+ */
+static int felix_port_hsr_join(struct dsa_switch *ds, int port,
+ struct net_device *hsr,
+ struct netlink_ext_ack *extack)
+{
+ struct ocelot *ocelot = ds->priv;
+ struct felix *felix = ocelot_to_felix(ocelot);
+
+ if (felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q) {
+ int err;
+
+ err = dsa_port_simple_hsr_validate(ds, port, hsr, extack);
+ if (err)
+ return err;
+
+ NL_SET_ERR_MSG_MOD(extack,
+ "Offloading not supported with \"ocelot-8021q\"");
+ return 0;
+ }
+
+ if (!(dsa_to_port(ds, port)->user->flags & IFF_UP))
+ return 0;
+
+ return dsa_port_simple_hsr_join(ds, port, hsr, extack);
+}
+
+static int felix_port_hsr_leave(struct dsa_switch *ds, int port,
+ struct net_device *hsr)
+{
+ struct ocelot *ocelot = ds->priv;
+ struct felix *felix = ocelot_to_felix(ocelot);
+
+ if (felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q)
+ return 0;
+
+ if (!(dsa_to_port(ds, port)->user->flags & IFF_UP))
+ return 0;
+
+ return dsa_port_simple_hsr_leave(ds, port, hsr);
+}
+
static const struct phylink_mac_ops felix_phylink_mac_ops = {
.mac_select_pcs = felix_phylink_mac_select_pcs,
.mac_config = felix_phylink_mac_config,
@@ -2262,6 +2327,7 @@ static const struct dsa_switch_ops felix_switch_ops = {
.get_ts_info = felix_get_ts_info,
.phylink_get_caps = felix_phylink_get_caps,
.port_enable = felix_port_enable,
+ .port_disable = felix_port_disable,
.port_fast_age = felix_port_fast_age,
.port_fdb_dump = felix_fdb_dump,
.port_fdb_add = felix_fdb_add,
@@ -2318,6 +2384,8 @@ static const struct dsa_switch_ops felix_switch_ops = {
.port_del_dscp_prio = felix_port_del_dscp_prio,
.port_set_host_flood = felix_port_set_host_flood,
.port_change_conduit = felix_port_change_conduit,
+ .port_hsr_join = felix_port_hsr_join,
+ .port_hsr_leave = felix_port_hsr_leave,
};
int felix_register_switch(struct device *dev, resource_size_t switch_base,
diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c
index 964a56ee16cc..c575e164368c 100644
--- a/drivers/net/dsa/realtek/rtl8365mb.c
+++ b/drivers/net/dsa/realtek/rtl8365mb.c
@@ -2134,6 +2134,8 @@ static const struct dsa_switch_ops rtl8365mb_switch_ops = {
.get_stats64 = rtl8365mb_get_stats64,
.port_change_mtu = rtl8365mb_port_change_mtu,
.port_max_mtu = rtl8365mb_port_max_mtu,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
};
static const struct realtek_ops rtl8365mb_ops = {
diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c
index 8bdb52b5fdcb..d96ae72b0a5c 100644
--- a/drivers/net/dsa/realtek/rtl8366rb.c
+++ b/drivers/net/dsa/realtek/rtl8366rb.c
@@ -1815,6 +1815,8 @@ static const struct dsa_switch_ops rtl8366rb_switch_ops = {
.port_fast_age = rtl8366rb_port_fast_age,
.port_change_mtu = rtl8366rb_change_mtu,
.port_max_mtu = rtl8366rb_max_mtu,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
};
static const struct realtek_ops rtl8366rb_ops = {
diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c
index 1635255f58e4..4d857e3be10b 100644
--- a/drivers/net/dsa/rzn1_a5psw.c
+++ b/drivers/net/dsa/rzn1_a5psw.c
@@ -1035,6 +1035,8 @@ static const struct dsa_switch_ops a5psw_switch_ops = {
.port_fdb_add = a5psw_port_fdb_add,
.port_fdb_del = a5psw_port_fdb_del,
.port_fdb_dump = a5psw_port_fdb_dump,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
};
static int a5psw_mdio_wait_busy(struct a5psw *a5psw)
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c
index f674c400f05b..aa2145cf29a6 100644
--- a/drivers/net/dsa/sja1105/sja1105_main.c
+++ b/drivers/net/dsa/sja1105/sja1105_main.c
@@ -1302,14 +1302,7 @@ static int sja1105_set_port_speed(struct sja1105_private *priv, int port,
* table, since this will be used for the clocking setup, and we no
* longer need to store it in the static config (already told hardware
* we want auto during upload phase).
- * Actually for the SGMII port, the MAC is fixed at 1 Gbps and
- * we need to configure the PCS only (if even that).
*/
- if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII)
- speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS];
- else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX)
- speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS];
-
mac[port].speed = speed;
return 0;
diff --git a/drivers/net/dsa/sja1105/sja1105_tas.c b/drivers/net/dsa/sja1105/sja1105_tas.c
index d7818710bc02..d5949d2c3e71 100644
--- a/drivers/net/dsa/sja1105/sja1105_tas.c
+++ b/drivers/net/dsa/sja1105/sja1105_tas.c
@@ -775,9 +775,8 @@ static void sja1105_tas_state_machine(struct work_struct *work)
base_time_ts = ns_to_timespec64(base_time);
now_ts = ns_to_timespec64(now);
- dev_dbg(ds->dev, "OPER base time %lld.%09ld (now %lld.%09ld)\n",
- base_time_ts.tv_sec, base_time_ts.tv_nsec,
- now_ts.tv_sec, now_ts.tv_nsec);
+ dev_dbg(ds->dev, "OPER base time %ptSp (now %ptSp)\n",
+ &base_time_ts, &now_ts);
break;
@@ -798,8 +797,7 @@ static void sja1105_tas_state_machine(struct work_struct *work)
if (now < tas_data->oper_base_time) {
/* TAS has not started yet */
diff = ns_to_timespec64(tas_data->oper_base_time - now);
- dev_dbg(ds->dev, "time to start: [%lld.%09ld]",
- diff.tv_sec, diff.tv_nsec);
+ dev_dbg(ds->dev, "time to start: [%ptSp]", &diff);
break;
}
diff --git a/drivers/net/dsa/xrs700x/xrs700x.c b/drivers/net/dsa/xrs700x/xrs700x.c
index 4dbcc49a9e52..0a05f4156ef4 100644
--- a/drivers/net/dsa/xrs700x/xrs700x.c
+++ b/drivers/net/dsa/xrs700x/xrs700x.c
@@ -566,6 +566,7 @@ static int xrs700x_hsr_join(struct dsa_switch *ds, int port,
struct xrs700x *priv = ds->priv;
struct net_device *user;
int ret, i, hsr_pair[2];
+ enum hsr_port_type type;
enum hsr_version ver;
bool fwd = false;
@@ -589,6 +590,16 @@ static int xrs700x_hsr_join(struct dsa_switch *ds, int port,
return -EOPNOTSUPP;
}
+ ret = hsr_get_port_type(hsr, dsa_to_port(ds, port)->user, &type);
+ if (ret)
+ return ret;
+
+ if (type != HSR_PT_SLAVE_A && type != HSR_PT_SLAVE_B) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only HSR slave ports can be offloaded");
+ return -EOPNOTSUPP;
+ }
+
dsa_hsr_foreach_port(dp, ds, hsr) {
if (dp->index != port) {
partner = dp;
diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c
new file mode 100644
index 000000000000..1c511f5dc6ab
--- /dev/null
+++ b/drivers/net/dsa/yt921x.c
@@ -0,0 +1,3006 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Driver for Motorcomm YT921x Switch
+ *
+ * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII,
+ * be sure to do your own checks before porting to another chip.
+ *
+ * Copyright (c) 2025 David Yang
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/if_hsr.h>
+#include <linux/if_vlan.h>
+#include <linux/iopoll.h>
+#include <linux/mdio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+
+#include <net/dsa.h>
+
+#include "yt921x.h"
+
+struct yt921x_mib_desc {
+ unsigned int size;
+ unsigned int offset;
+ const char *name;
+};
+
+#define MIB_DESC(_size, _offset, _name) \
+ {_size, _offset, _name}
+
+/* Must agree with yt921x_mib
+ *
+ * Unstructured fields (name != NULL) will appear in get_ethtool_stats(),
+ * structured go to their *_stats() methods, but we need their sizes and offsets
+ * to perform 32bit MIB overflow wraparound.
+ */
+static const struct yt921x_mib_desc yt921x_mib_descs[] = {
+ MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL),
+ MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL),
+
+ MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL),
+
+ MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL),
+ MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"),
+ MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"),
+};
+
+struct yt921x_info {
+ const char *name;
+ u16 major;
+ /* Unknown, seems to be plain enumeration */
+ u8 mode;
+ u8 extmode;
+ /* Ports with integral GbE PHYs, not including MCU Port 10 */
+ u16 internal_mask;
+ /* TODO: see comments in yt921x_dsa_phylink_get_caps() */
+ u16 external_mask;
+};
+
+#define YT921X_PORT_MASK_INTn(port) BIT(port)
+#define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0)
+#define YT921X_PORT_MASK_EXT0 BIT(8)
+#define YT921X_PORT_MASK_EXT1 BIT(9)
+
+static const struct yt921x_info yt921x_infos[] = {
+ {
+ "YT9215SC", YT9215_MAJOR, 1, 0,
+ YT921X_PORT_MASK_INT0_n(5),
+ YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ },
+ {
+ "YT9215S", YT9215_MAJOR, 2, 0,
+ YT921X_PORT_MASK_INT0_n(5),
+ YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ },
+ {
+ "YT9215RB", YT9215_MAJOR, 3, 0,
+ YT921X_PORT_MASK_INT0_n(5),
+ YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ },
+ {
+ "YT9214NB", YT9215_MAJOR, 3, 2,
+ YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
+ YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ },
+ {
+ "YT9213NB", YT9215_MAJOR, 3, 3,
+ YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3),
+ YT921X_PORT_MASK_EXT1,
+ },
+ {
+ "YT9218N", YT9218_MAJOR, 0, 0,
+ YT921X_PORT_MASK_INT0_n(8),
+ 0,
+ },
+ {
+ "YT9218MB", YT9218_MAJOR, 1, 0,
+ YT921X_PORT_MASK_INT0_n(8),
+ YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1,
+ },
+ {}
+};
+
+#define YT921X_NAME "yt921x"
+
+#define YT921X_VID_UNWARE 4095
+
+#define YT921X_POLL_SLEEP_US 10000
+#define YT921X_POLL_TIMEOUT_US 100000
+
+/* The interval should be small enough to avoid overflow of 32bit MIBs.
+ *
+ * Until we can read MIBs from stats64 call directly (i.e. sleep
+ * there), we have to poll stats more frequently then it is actually needed.
+ * For overflow protection, normally, 100 sec interval should have been OK.
+ */
+#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ)
+
+struct yt921x_reg_mdio {
+ struct mii_bus *bus;
+ int addr;
+ /* SWITCH_ID_1 / SWITCH_ID_0 of the device
+ *
+ * This is a way to multiplex multiple devices on the same MII phyaddr
+ * and should be configurable in DT. However, MDIO core simply doesn't
+ * allow multiple devices over one reg addr, so this is a fixed value
+ * for now until a solution is found.
+ *
+ * Keep this because we need switchid to form MII regaddrs anyway.
+ */
+ unsigned char switchid;
+};
+
+/* TODO: SPI/I2C */
+
+#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds)
+#define to_device(priv) ((priv)->ds.dev)
+
+static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp)
+{
+ WARN_ON(!mutex_is_locked(&priv->reg_lock));
+
+ return priv->reg_ops->read(priv->reg_ctx, reg, valp);
+}
+
+static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val)
+{
+ WARN_ON(!mutex_is_locked(&priv->reg_lock));
+
+ return priv->reg_ops->write(priv->reg_ctx, reg, val);
+}
+
+static int
+yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp)
+{
+ u32 val;
+ int res;
+ int ret;
+
+ ret = read_poll_timeout(yt921x_reg_read, res,
+ res || (val & mask) == *valp,
+ YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US,
+ false, priv, reg, &val);
+ if (ret)
+ return ret;
+ if (res)
+ return res;
+
+ *valp = val;
+ return 0;
+}
+
+static int
+yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val)
+{
+ int res;
+ u32 v;
+ u32 u;
+
+ res = yt921x_reg_read(priv, reg, &v);
+ if (res)
+ return res;
+
+ u = v;
+ u &= ~mask;
+ u |= val;
+ if (u == v)
+ return 0;
+
+ return yt921x_reg_write(priv, reg, u);
+}
+
+static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
+{
+ return yt921x_reg_update_bits(priv, reg, 0, mask);
+}
+
+static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask)
+{
+ return yt921x_reg_update_bits(priv, reg, mask, 0);
+}
+
+static int
+yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set)
+{
+ return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask);
+}
+
+/* Some registers, like VLANn_CTRL, should always be written in 64-bit, even if
+ * you are to write only the lower / upper 32 bits.
+ *
+ * There is no such restriction for reading, but we still provide 64-bit read
+ * wrappers so that we always handle u64 values.
+ */
+
+static int yt921x_reg64_read(struct yt921x_priv *priv, u32 reg, u64 *valp)
+{
+ u32 lo;
+ u32 hi;
+ int res;
+
+ res = yt921x_reg_read(priv, reg, &lo);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, reg + 4, &hi);
+ if (res)
+ return res;
+
+ *valp = ((u64)hi << 32) | lo;
+ return 0;
+}
+
+static int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, u64 val)
+{
+ int res;
+
+ res = yt921x_reg_write(priv, reg, (u32)val);
+ if (res)
+ return res;
+ return yt921x_reg_write(priv, reg + 4, (u32)(val >> 32));
+}
+
+static int
+yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, u64 mask, u64 val)
+{
+ int res;
+ u64 v;
+ u64 u;
+
+ res = yt921x_reg64_read(priv, reg, &v);
+ if (res)
+ return res;
+
+ u = v;
+ u &= ~mask;
+ u |= val;
+ if (u == v)
+ return 0;
+
+ return yt921x_reg64_write(priv, reg, u);
+}
+
+static int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, u64 mask)
+{
+ return yt921x_reg64_update_bits(priv, reg, mask, 0);
+}
+
+static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp)
+{
+ struct yt921x_reg_mdio *mdio = context;
+ struct mii_bus *bus = mdio->bus;
+ int addr = mdio->addr;
+ u32 reg_addr;
+ u32 reg_data;
+ u32 val;
+ int res;
+
+ /* Hold the mdio bus lock to avoid (un)locking for 4 times */
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
+ YT921X_SMI_READ;
+ res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
+ if (res)
+ goto end;
+ res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
+ if (res)
+ goto end;
+
+ reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
+ YT921X_SMI_READ;
+ res = __mdiobus_read(bus, addr, reg_data);
+ if (res < 0)
+ goto end;
+ val = (u16)res;
+ res = __mdiobus_read(bus, addr, reg_data);
+ if (res < 0)
+ goto end;
+ val = (val << 16) | (u16)res;
+
+ *valp = val;
+ res = 0;
+
+end:
+ mutex_unlock(&bus->mdio_lock);
+ return res;
+}
+
+static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val)
+{
+ struct yt921x_reg_mdio *mdio = context;
+ struct mii_bus *bus = mdio->bus;
+ int addr = mdio->addr;
+ u32 reg_addr;
+ u32 reg_data;
+ int res;
+
+ mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+ reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR |
+ YT921X_SMI_WRITE;
+ res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16));
+ if (res)
+ goto end;
+ res = __mdiobus_write(bus, addr, reg_addr, (u16)reg);
+ if (res)
+ goto end;
+
+ reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA |
+ YT921X_SMI_WRITE;
+ res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16));
+ if (res)
+ goto end;
+ res = __mdiobus_write(bus, addr, reg_data, (u16)val);
+ if (res)
+ goto end;
+
+ res = 0;
+
+end:
+ mutex_unlock(&bus->mdio_lock);
+ return res;
+}
+
+static const struct yt921x_reg_ops yt921x_reg_ops_mdio = {
+ .read = yt921x_reg_mdio_read,
+ .write = yt921x_reg_mdio_write,
+};
+
+/* TODO: SPI/I2C */
+
+static int yt921x_intif_wait(struct yt921x_priv *priv)
+{
+ u32 val = 0;
+
+ return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,
+ &val);
+}
+
+static int
+yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
+{
+ struct device *dev = to_device(priv);
+ u32 mask;
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ res = yt921x_intif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_READ;
+ res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ res = yt921x_intif_wait(priv);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val);
+ if (res)
+ return res;
+
+ if ((u16)val != val)
+ dev_info(dev,
+ "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
+ __func__, port, reg, val);
+ *valp = (u16)val;
+ return 0;
+}
+
+static int
+yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
+{
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ res = yt921x_intif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_WRITE;
+ res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ return yt921x_intif_wait(priv);
+}
+
+static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ u16 val;
+ int res;
+
+ if (port >= YT921X_PORT_NUM)
+ return U16_MAX;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_intif_read(priv, port, reg, &val);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ return res;
+ return val;
+}
+
+static int
+yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ int res;
+
+ if (port >= YT921X_PORT_NUM)
+ return -ENODEV;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_intif_write(priv, port, reg, data);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
+{
+ struct device *dev = to_device(priv);
+ struct mii_bus *mbus;
+ int res;
+
+ mbus = devm_mdiobus_alloc(dev);
+ if (!mbus)
+ return -ENOMEM;
+
+ mbus->name = "YT921x internal MDIO bus";
+ snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
+ mbus->priv = priv;
+ mbus->read = yt921x_mbus_int_read;
+ mbus->write = yt921x_mbus_int_write;
+ mbus->parent = dev;
+ mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0);
+
+ res = devm_of_mdiobus_register(dev, mbus, mnp);
+ if (res)
+ return res;
+
+ priv->mbus_int = mbus;
+
+ return 0;
+}
+
+static int yt921x_extif_wait(struct yt921x_priv *priv)
+{
+ u32 val = 0;
+
+ return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,
+ &val);
+}
+
+static int
+yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
+{
+ struct device *dev = to_device(priv);
+ u32 mask;
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ res = yt921x_extif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;
+ res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ res = yt921x_extif_wait(priv);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val);
+ if (res)
+ return res;
+
+ if ((u16)val != val)
+ dev_info(dev,
+ "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
+ __func__, port, reg, val);
+ *valp = (u16)val;
+ return 0;
+}
+
+static int
+yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
+{
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ res = yt921x_extif_wait(priv);
+ if (res)
+ return res;
+
+ mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
+ YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
+ ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
+ YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;
+ res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
+ if (res)
+ return res;
+
+ return yt921x_extif_wait(priv);
+}
+
+static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ u16 val;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_extif_read(priv, port, reg, &val);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ return res;
+ return val;
+}
+
+static int
+yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)
+{
+ struct yt921x_priv *priv = mbus->priv;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_extif_write(priv, port, reg, data);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
+{
+ struct device *dev = to_device(priv);
+ struct mii_bus *mbus;
+ int res;
+
+ mbus = devm_mdiobus_alloc(dev);
+ if (!mbus)
+ return -ENOMEM;
+
+ mbus->name = "YT921x external MDIO bus";
+ snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
+ mbus->priv = priv;
+ /* TODO: c45? */
+ mbus->read = yt921x_mbus_ext_read;
+ mbus->write = yt921x_mbus_ext_write;
+ mbus->parent = dev;
+
+ res = devm_of_mdiobus_register(dev, mbus, mnp);
+ if (res)
+ return res;
+
+ priv->mbus_ext = mbus;
+
+ return 0;
+}
+
+/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */
+static int yt921x_read_mib(struct yt921x_priv *priv, int port)
+{
+ struct yt921x_port *pp = &priv->ports[port];
+ struct device *dev = to_device(priv);
+ struct yt921x_mib *mib = &pp->mib;
+ int res = 0;
+
+ /* Reading of yt921x_port::mib is not protected by a lock and it's vain
+ * to keep its consistency, since we have to read registers one by one
+ * and there is no way to make a snapshot of MIB stats.
+ *
+ * Writing (by this function only) is and should be protected by
+ * reg_lock.
+ */
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+ u32 reg = YT921X_MIBn_DATA0(port) + desc->offset;
+ u64 *valp = &((u64 *)mib)[i];
+ u64 val = *valp;
+ u32 val0;
+ u32 val1;
+
+ res = yt921x_reg_read(priv, reg, &val0);
+ if (res)
+ break;
+
+ if (desc->size <= 1) {
+ if (val < (u32)val)
+ /* overflow */
+ val += (u64)U32_MAX + 1;
+ val &= ~U32_MAX;
+ val |= val0;
+ } else {
+ res = yt921x_reg_read(priv, reg + 4, &val1);
+ if (res)
+ break;
+ val = ((u64)val1 << 32) | val0;
+ }
+
+ WRITE_ONCE(*valp, val);
+ }
+
+ pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte +
+ mib->rx_128_255byte + mib->rx_256_511byte +
+ mib->rx_512_1023byte + mib->rx_1024_1518byte +
+ mib->rx_jumbo;
+ pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte +
+ mib->tx_128_255byte + mib->tx_256_511byte +
+ mib->tx_512_1023byte + mib->tx_1024_1518byte +
+ mib->tx_jumbo;
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "read stats for",
+ port, res);
+ return res;
+}
+
+static void yt921x_poll_mib(struct work_struct *work)
+{
+ struct yt921x_port *pp = container_of_const(work, struct yt921x_port,
+ mib_read.work);
+ struct yt921x_priv *priv = (void *)(pp - pp->index) -
+ offsetof(struct yt921x_priv, ports);
+ unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES;
+ int port = pp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_read_mib(priv, port);
+ mutex_unlock(&priv->reg_lock);
+ if (res)
+ delay *= 4;
+
+ schedule_delayed_work(&pp->mib_read, delay);
+}
+
+static void
+yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
+ uint8_t *data)
+{
+ if (stringset != ETH_SS_STATS)
+ return;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ ethtool_puts(&data, desc->name);
+ }
+}
+
+static void
+yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *mib = &pp->mib;
+ size_t j;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_read_mib(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ j = 0;
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (!desc->name)
+ continue;
+
+ data[j] = ((u64 *)mib)[i];
+ j++;
+ }
+}
+
+static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+ int cnt = 0;
+
+ if (sset != ETH_SS_STATS)
+ return 0;
+
+ for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) {
+ const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i];
+
+ if (desc->name)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+static void
+yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *mib = &pp->mib;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_read_mib(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ mac_stats->FramesTransmittedOK = pp->tx_frames;
+ mac_stats->SingleCollisionFrames = mib->tx_single_collisions;
+ mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions;
+ mac_stats->FramesReceivedOK = pp->rx_frames;
+ mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors;
+ mac_stats->AlignmentErrors = mib->rx_alignment_errors;
+ mac_stats->OctetsTransmittedOK = mib->tx_good_bytes;
+ mac_stats->FramesWithDeferredXmissions = mib->tx_deferred;
+ mac_stats->LateCollisions = mib->tx_late_collisions;
+ mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors;
+ /* mac_stats->FramesLostDueToIntMACXmitError */
+ /* mac_stats->CarrierSenseErrors */
+ mac_stats->OctetsReceivedOK = mib->rx_good_bytes;
+ /* mac_stats->FramesLostDueToIntMACRcvError */
+ mac_stats->MulticastFramesXmittedOK = mib->tx_multicast;
+ mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast;
+ /* mac_stats->FramesWithExcessiveDeferral */
+ mac_stats->MulticastFramesReceivedOK = mib->rx_multicast;
+ mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast;
+ /* mac_stats->InRangeLengthErrors */
+ /* mac_stats->OutOfRangeLengthField */
+ mac_stats->FrameTooLongErrors = mib->rx_oversize_errors;
+}
+
+static void
+yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
+ struct ethtool_eth_ctrl_stats *ctrl_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *mib = &pp->mib;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_read_mib(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ ctrl_stats->MACControlFramesTransmitted = mib->tx_pause;
+ ctrl_stats->MACControlFramesReceived = mib->rx_pause;
+ /* ctrl_stats->UnsupportedOpcodesReceived */
+}
+
+static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = {
+ { 0, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1518 },
+ { 1519, YT921X_FRAME_SIZE_MAX },
+ {}
+};
+
+static void
+yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port,
+ struct ethtool_rmon_stats *rmon_stats,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *mib = &pp->mib;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_read_mib(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ *ranges = yt921x_rmon_ranges;
+
+ rmon_stats->undersize_pkts = mib->rx_undersize_errors;
+ rmon_stats->oversize_pkts = mib->rx_oversize_errors;
+ rmon_stats->fragments = mib->rx_alignment_errors;
+ /* rmon_stats->jabbers */
+
+ rmon_stats->hist[0] = mib->rx_64byte;
+ rmon_stats->hist[1] = mib->rx_65_127byte;
+ rmon_stats->hist[2] = mib->rx_128_255byte;
+ rmon_stats->hist[3] = mib->rx_256_511byte;
+ rmon_stats->hist[4] = mib->rx_512_1023byte;
+ rmon_stats->hist[5] = mib->rx_1024_1518byte;
+ rmon_stats->hist[6] = mib->rx_jumbo;
+
+ rmon_stats->hist_tx[0] = mib->tx_64byte;
+ rmon_stats->hist_tx[1] = mib->tx_65_127byte;
+ rmon_stats->hist_tx[2] = mib->tx_128_255byte;
+ rmon_stats->hist_tx[3] = mib->tx_256_511byte;
+ rmon_stats->hist_tx[4] = mib->tx_512_1023byte;
+ rmon_stats->hist_tx[5] = mib->tx_1024_1518byte;
+ rmon_stats->hist_tx[6] = mib->tx_jumbo;
+}
+
+static void
+yt921x_dsa_get_stats64(struct dsa_switch *ds, int port,
+ struct rtnl_link_stats64 *stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *mib = &pp->mib;
+
+ stats->rx_length_errors = mib->rx_undersize_errors +
+ mib->rx_fragment_errors;
+ stats->rx_over_errors = mib->rx_oversize_errors;
+ stats->rx_crc_errors = mib->rx_crc_errors;
+ stats->rx_frame_errors = mib->rx_alignment_errors;
+ /* stats->rx_fifo_errors */
+ /* stats->rx_missed_errors */
+
+ stats->tx_aborted_errors = mib->tx_aborted_errors;
+ /* stats->tx_carrier_errors */
+ stats->tx_fifo_errors = mib->tx_undersize_errors;
+ /* stats->tx_heartbeat_errors */
+ stats->tx_window_errors = mib->tx_late_collisions;
+
+ stats->rx_packets = pp->rx_frames;
+ stats->tx_packets = pp->tx_frames;
+ stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets;
+ stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets;
+ stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
+ stats->rx_crc_errors + stats->rx_frame_errors;
+ stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors +
+ stats->tx_window_errors;
+ stats->rx_dropped = mib->rx_dropped;
+ /* stats->tx_dropped */
+ stats->multicast = mib->rx_multicast;
+ stats->collisions = mib->tx_collisions;
+}
+
+static void
+yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port,
+ struct ethtool_pause_stats *pause_stats)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct yt921x_port *pp = &priv->ports[port];
+ struct yt921x_mib *mib = &pp->mib;
+
+ mutex_lock(&priv->reg_lock);
+ yt921x_read_mib(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ pause_stats->tx_pause_frames = mib->tx_pause;
+ pause_stats->rx_pause_frames = mib->rx_pause;
+}
+
+static int
+yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e)
+{
+ /* Poor datasheet for EEE operations; don't ask if you are confused */
+
+ bool enable = e->eee_enabled;
+ u16 new_mask;
+ int res;
+
+ /* Enable / disable global EEE */
+ new_mask = priv->eee_ports_mask;
+ new_mask &= ~BIT(port);
+ new_mask |= !enable ? 0 : BIT(port);
+
+ if (!!new_mask != !!priv->eee_ports_mask) {
+ res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC,
+ YT921X_PON_STRAP_EEE, !!new_mask);
+ if (res)
+ return res;
+ res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL,
+ YT921X_PON_STRAP_EEE, !!new_mask);
+ if (res)
+ return res;
+ }
+
+ priv->eee_ports_mask = new_mask;
+
+ /* Enable / disable port EEE */
+ res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL,
+ YT921X_EEE_CTRL_ENn(port), enable);
+ if (res)
+ return res;
+ res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port),
+ YT921X_EEE_VAL_DATA, enable);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int
+yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_set_eee(priv, port, e);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu)
+{
+ /* Only serves as packet filter, since the frame size is always set to
+ * maximum after reset
+ */
+
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ int frame_size;
+ int res;
+
+ frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+ if (dsa_port_is_cpu(dp))
+ frame_size += YT921X_TAG_LEN;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port),
+ YT921X_MAC_FRAME_SIZE_M,
+ YT921X_MAC_FRAME_SIZE(frame_size));
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port)
+{
+ /* Only called for user ports, exclude tag len here */
+ return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN;
+}
+
+static int
+yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress)
+{
+ u32 mask;
+
+ if (ingress)
+ mask = YT921X_MIRROR_IGR_PORTn(port);
+ else
+ mask = YT921X_MIRROR_EGR_PORTn(port);
+ return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask);
+}
+
+static int
+yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress,
+ int to_local_port, struct netlink_ext_ack *extack)
+{
+ u32 srcs;
+ u32 ctrl;
+ u32 val;
+ u32 dst;
+ int res;
+
+ if (ingress)
+ srcs = YT921X_MIRROR_IGR_PORTn(port);
+ else
+ srcs = YT921X_MIRROR_EGR_PORTn(port);
+ dst = YT921X_MIRROR_PORT(to_local_port);
+
+ res = yt921x_reg_read(priv, YT921X_MIRROR, &val);
+ if (res)
+ return res;
+
+ /* other mirror tasks & different dst port -> conflict */
+ if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M |
+ YT921X_MIRROR_IGR_PORTS_M)) &&
+ (val & YT921X_MIRROR_PORT_M) != dst) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Sniffer port is already configured, delete existing rules & retry");
+ return -EBUSY;
+ }
+
+ ctrl = val & ~YT921X_MIRROR_PORT_M;
+ ctrl |= srcs;
+ ctrl |= dst;
+
+ if (ctrl == val)
+ return 0;
+
+ return yt921x_reg_write(priv, YT921X_MIRROR, ctrl);
+}
+
+static void
+yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct device *dev = to_device(priv);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_mirror_del(priv, port, mirror->ingress);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "unmirror",
+ port, res);
+}
+
+static int
+yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port,
+ struct dsa_mall_mirror_tc_entry *mirror,
+ bool ingress, struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_mirror_add(priv, port, ingress,
+ mirror->to_local_port, extack);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp)
+{
+ struct device *dev = to_device(priv);
+ u32 val = YT921X_FDB_RESULT_DONE;
+ int res;
+
+ res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE,
+ &val);
+ if (res) {
+ dev_err(dev, "FDB probably stuck\n");
+ return res;
+ }
+
+ *valp = val;
+ return 0;
+}
+
+static int
+yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr,
+ u16 vid, u32 ctrl1)
+{
+ u32 ctrl;
+ int res;
+
+ ctrl = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3];
+ res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl);
+ if (res)
+ return res;
+
+ ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | (addr[4] << 8) | addr[5];
+ return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
+}
+
+static int
+yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
+ u16 *indexp)
+{
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ res = yt921x_fdb_in01(priv, addr, vid, 0);
+ if (res)
+ return res;
+
+ ctrl = 0;
+ res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
+ if (res)
+ return res;
+
+ ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_fdb_wait(priv, &val);
+ if (res)
+ return res;
+ if (val & YT921X_FDB_RESULT_NOTFOUND) {
+ *indexp = YT921X_FDB_NUM;
+ return 0;
+ }
+
+ *indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
+ return 0;
+}
+
+static int
+yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp,
+ u16 *ports_maskp, u16 *indexp, u8 *statusp)
+{
+ struct device *dev = to_device(priv);
+ u16 index;
+ u32 data0;
+ u32 data1;
+ u32 data2;
+ u32 val;
+ int res;
+
+ res = yt921x_fdb_wait(priv, &val);
+ if (res)
+ return res;
+ if (val & YT921X_FDB_RESULT_NOTFOUND) {
+ *ports_maskp = 0;
+ return 0;
+ }
+ index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val);
+
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1);
+ if (res)
+ return res;
+ if ((data1 & YT921X_FDB_IO1_STATUS_M) ==
+ YT921X_FDB_IO1_STATUS_INVALID) {
+ *ports_maskp = 0;
+ return 0;
+ }
+
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2);
+ if (res)
+ return res;
+
+ addr[0] = data0 >> 24;
+ addr[1] = data0 >> 16;
+ addr[2] = data0 >> 8;
+ addr[3] = data0;
+ addr[4] = data1 >> 8;
+ addr[5] = data1;
+ *vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1);
+ *indexp = index;
+ *ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2);
+ *statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1);
+
+ dev_dbg(dev,
+ "%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n",
+ __func__, *indexp, addr[0], addr[1], addr[2], addr[3],
+ addr[4], addr[5], *vidp, *ports_maskp, *statusp);
+ return 0;
+}
+
+static int
+yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask,
+ dsa_fdb_dump_cb_t *cb, void *data)
+{
+ unsigned char addr[ETH_ALEN];
+ u8 status;
+ u16 pmask;
+ u16 index;
+ u32 ctrl;
+ u16 vid;
+ int res;
+
+ ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX |
+ YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+ res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status);
+ if (res)
+ return res;
+ if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
+ res = cb(addr, vid,
+ status == YT921X_FDB_ENTRY_STATUS_STATIC, data);
+ if (res)
+ return res;
+ }
+
+ ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
+ res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
+ if (res)
+ return res;
+
+ index = 0;
+ do {
+ ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
+ YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT |
+ YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index,
+ &status);
+ if (res)
+ return res;
+ if (!pmask)
+ break;
+
+ if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) {
+ res = cb(addr, vid,
+ status == YT921X_FDB_ENTRY_STATUS_STATIC,
+ data);
+ if (res)
+ return res;
+ }
+
+ /* Never call GET_NEXT with 4095, otherwise it will hang
+ * forever until a reset!
+ */
+ } while (index < YT921X_FDB_NUM - 1);
+
+ return 0;
+}
+
+static int
+yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid,
+ bool flush_static)
+{
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ if (vid < 4096) {
+ ctrl = YT921X_FDB_IO1_FID(vid);
+ res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl);
+ if (res)
+ return res;
+ }
+
+ ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
+ res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
+ if (res)
+ return res;
+
+ ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START;
+ if (vid >= 4096)
+ ctrl |= YT921X_FDB_OP_FLUSH_PORT;
+ else
+ ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID;
+ if (flush_static)
+ ctrl |= YT921X_FDB_OP_FLUSH_STATIC;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_fdb_wait(priv, &val);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int
+yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static)
+{
+ return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static);
+}
+
+static int
+yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1,
+ u16 ctrl2)
+{
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2);
+ if (res)
+ return res;
+
+ ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX |
+ YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ return yt921x_fdb_wait(priv, &val);
+}
+
+static int
+yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
+ u16 ports_mask)
+{
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ ctrl = YT921X_FDB_IO1_STATUS_STATIC;
+ res = yt921x_fdb_in01(priv, addr, vid, ctrl);
+ if (res)
+ return res;
+
+ ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask);
+ res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl);
+ if (res)
+ return res;
+
+ ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ return yt921x_fdb_wait(priv, &val);
+}
+
+static int
+yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
+ u16 vid, u16 ports_mask)
+{
+ u16 index;
+ u32 ctrl1;
+ u32 ctrl2;
+ u32 ctrl;
+ u32 val2;
+ u32 val;
+ int res;
+
+ /* Check for presence */
+ res = yt921x_fdb_has(priv, addr, vid, &index);
+ if (res)
+ return res;
+ if (index >= YT921X_FDB_NUM)
+ return 0;
+
+ /* Check if action required */
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
+ if (res)
+ return res;
+
+ ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask);
+ if (ctrl2 == val2)
+ return 0;
+ if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) {
+ ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ return yt921x_fdb_wait(priv, &val);
+ }
+
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1);
+ if (res)
+ return res;
+
+ return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
+}
+
+static int
+yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
+ u16 ports_mask)
+{
+ u16 index;
+ u32 ctrl1;
+ u32 ctrl2;
+ u32 val1;
+ u32 val2;
+ int res;
+
+ /* Check for presence */
+ res = yt921x_fdb_has(priv, addr, vid, &index);
+ if (res)
+ return res;
+ if (index >= YT921X_FDB_NUM)
+ return yt921x_fdb_add(priv, addr, vid, ports_mask);
+
+ /* Check if action required */
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1);
+ if (res)
+ return res;
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
+ if (res)
+ return res;
+
+ ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M;
+ ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC;
+ ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask);
+ if (ctrl1 == val1 && ctrl2 == val2)
+ return 0;
+
+ return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
+}
+
+static int
+yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port,
+ dsa_fdb_dump_cb_t *cb, void *data)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ /* Hardware FDB is shared for fdb and mdb, "bridge fdb show"
+ * only wants to see unicast
+ */
+ res = yt921x_fdb_dump(priv, BIT(port), cb, data);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct device *dev = to_device(priv);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_fdb_flush_port(priv, port, false);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for",
+ port, res);
+}
+
+static int
+yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ u32 ctrl;
+ int res;
+
+ /* AGEING reg is set in 5s step */
+ ctrl = clamp(msecs / 5000, 1, U16_MAX);
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg_write(priv, YT921X_AGEING, ctrl);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid, struct dsa_db db)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port,
+ const unsigned char *addr, u16 vid, struct dsa_db db)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_fdb_join(priv, addr, vid, BIT(port));
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ const unsigned char *addr = mdb->addr;
+ u16 vid = mdb->vid;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_fdb_leave(priv, addr, vid, BIT(port));
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_mdb *mdb,
+ struct dsa_db db)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ const unsigned char *addr = mdb->addr;
+ u16 vid = mdb->vid;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_fdb_join(priv, addr, vid, BIT(port));
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid)
+{
+ u32 mask;
+ u32 ctrl;
+
+ mask = YT921X_PORT_VLAN_CTRL_CVID_M;
+ ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid);
+ return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port),
+ mask, ctrl);
+}
+
+static int
+yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering)
+{
+ struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+ struct net_device *bdev;
+ u16 pvid;
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ bdev = dsa_port_bridge_dev_get(dp);
+
+ if (!bdev || !vlan_filtering)
+ pvid = YT921X_VID_UNWARE;
+ else
+ br_vlan_get_pvid(bdev, &pvid);
+ res = yt921x_port_set_pvid(priv, port, pvid);
+ if (res)
+ return res;
+
+ mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED |
+ YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
+ ctrl = 0;
+ /* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */
+ if (vlan_filtering && !pvid)
+ ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
+ res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
+ mask, ctrl);
+ if (res)
+ return res;
+
+ res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER,
+ YT921X_VLAN_IGR_FILTER_PORTn(port),
+ vlan_filtering);
+ if (res)
+ return res;
+
+ /* Turn on / off VLAN awareness */
+ mask = YT921X_PORT_IGR_TPIDn_CTAG_M;
+ if (!vlan_filtering)
+ ctrl = 0;
+ else
+ ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0);
+ res = yt921x_reg_update_bits(priv, YT921X_PORTn_IGR_TPID(port),
+ mask, ctrl);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int
+yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid)
+{
+ u64 mask64;
+
+ mask64 = YT921X_VLAN_CTRL_PORTS(port) |
+ YT921X_VLAN_CTRL_UNTAG_PORTn(port);
+
+ return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), mask64);
+}
+
+static int
+yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged)
+{
+ u64 mask64;
+ u64 ctrl64;
+
+ mask64 = YT921X_VLAN_CTRL_PORTn(port) |
+ YT921X_VLAN_CTRL_PORTS(priv->cpu_ports_mask);
+ ctrl64 = mask64;
+
+ mask64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
+ if (untagged)
+ ctrl64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port);
+
+ return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid),
+ mask64, ctrl64);
+}
+
+static int
+yt921x_pvid_clear(struct yt921x_priv *priv, int port)
+{
+ struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+ bool vlan_filtering;
+ u32 mask;
+ int res;
+
+ vlan_filtering = dsa_port_is_vlan_filtering(dp);
+
+ res = yt921x_port_set_pvid(priv, port,
+ vlan_filtering ? 0 : YT921X_VID_UNWARE);
+ if (res)
+ return res;
+
+ if (vlan_filtering) {
+ mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
+ res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port),
+ mask);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int
+yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid)
+{
+ struct dsa_port *dp = dsa_to_port(&priv->ds, port);
+ bool vlan_filtering;
+ u32 mask;
+ int res;
+
+ vlan_filtering = dsa_port_is_vlan_filtering(dp);
+
+ if (vlan_filtering) {
+ res = yt921x_port_set_pvid(priv, port, vid);
+ if (res)
+ return res;
+ }
+
+ mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED;
+ res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int
+yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port,
+ bool vlan_filtering,
+ struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ if (dsa_is_cpu_port(ds, port))
+ return 0;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_vlan_filtering(priv, port, vlan_filtering);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ u16 vid = vlan->vid;
+ u16 pvid;
+ int res;
+
+ if (dsa_is_cpu_port(ds, port))
+ return 0;
+
+ mutex_lock(&priv->reg_lock);
+ do {
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct net_device *bdev;
+
+ res = yt921x_vlan_del(priv, port, vid);
+ if (res)
+ break;
+
+ bdev = dsa_port_bridge_dev_get(dp);
+ if (bdev) {
+ br_vlan_get_pvid(bdev, &pvid);
+ if (pvid == vid)
+ res = yt921x_pvid_clear(priv, port);
+ }
+ } while (0);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port,
+ const struct switchdev_obj_port_vlan *vlan,
+ struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ u16 vid = vlan->vid;
+ u16 pvid;
+ int res;
+
+ /* CPU port is supposed to be a member of every VLAN; see
+ * yt921x_vlan_add() and yt921x_port_setup()
+ */
+ if (dsa_is_cpu_port(ds, port))
+ return 0;
+
+ mutex_lock(&priv->reg_lock);
+ do {
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct net_device *bdev;
+
+ res = yt921x_vlan_add(priv, port, vid,
+ vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
+ if (res)
+ break;
+
+ bdev = dsa_port_bridge_dev_get(dp);
+ if (bdev) {
+ if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
+ res = yt921x_pvid_set(priv, port, vid);
+ } else {
+ br_vlan_get_pvid(bdev, &pvid);
+ if (pvid == vid)
+ res = yt921x_pvid_clear(priv, port);
+ }
+ }
+ } while (0);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
+{
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ ctrl = ~priv->cpu_ports_mask;
+ res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl);
+ if (res)
+ return res;
+
+ /* Turn off FDB learning to prevent FDB pollution */
+ mask = YT921X_PORT_LEARN_DIS;
+ res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask);
+ if (res)
+ return res;
+
+ /* Turn off VLAN awareness */
+ mask = YT921X_PORT_IGR_TPIDn_CTAG_M;
+ res = yt921x_reg_clear_bits(priv, YT921X_PORTn_IGR_TPID(port), mask);
+ if (res)
+ return res;
+
+ /* Unrelated since learning is off and all packets are trapped;
+ * set it anyway
+ */
+ res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int yt921x_userport_bridge(struct yt921x_priv *priv, int port)
+{
+ u32 mask;
+ int res;
+
+ mask = YT921X_PORT_LEARN_DIS;
+ res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int yt921x_isolate(struct yt921x_priv *priv, int port)
+{
+ u32 mask;
+ int res;
+
+ mask = BIT(port);
+ for (int i = 0; i < YT921X_PORT_NUM; i++) {
+ if ((BIT(i) & priv->cpu_ports_mask) || i == port)
+ continue;
+
+ res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i),
+ mask);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+/* Make sure to include the CPU port in ports_mask, or your bridge will
+ * not have it.
+ */
+static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask)
+{
+ unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask;
+ u32 isolated_mask;
+ u32 ctrl;
+ int port;
+ int res;
+
+ isolated_mask = 0;
+ for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
+ struct yt921x_port *pp = &priv->ports[port];
+
+ if (pp->isolated)
+ isolated_mask |= BIT(port);
+ }
+
+ /* Block from non-cpu bridge ports ... */
+ for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) {
+ struct yt921x_port *pp = &priv->ports[port];
+
+ /* to non-bridge ports */
+ ctrl = ~ports_mask;
+ /* to isolated ports when isolated */
+ if (pp->isolated)
+ ctrl |= isolated_mask;
+ /* to itself when non-hairpin */
+ if (!pp->hairpin)
+ ctrl |= BIT(port);
+ else
+ ctrl &= ~BIT(port);
+
+ res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
+ ctrl);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int yt921x_bridge_leave(struct yt921x_priv *priv, int port)
+{
+ int res;
+
+ res = yt921x_userport_standalone(priv, port);
+ if (res)
+ return res;
+
+ res = yt921x_isolate(priv, port);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int
+yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask)
+{
+ int res;
+
+ res = yt921x_userport_bridge(priv, port);
+ if (res)
+ return res;
+
+ res = yt921x_bridge(priv, ports_mask);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static u32
+dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev)
+{
+ struct dsa_port *dp;
+ u32 mask = 0;
+
+ dsa_switch_for_each_user_port(dp, ds)
+ if (dsa_port_offloads_bridge_dev(dp, bdev))
+ mask |= BIT(dp->index);
+
+ return mask;
+}
+
+static int
+yt921x_bridge_flags(struct yt921x_priv *priv, int port,
+ struct switchdev_brport_flags flags)
+{
+ struct yt921x_port *pp = &priv->ports[port];
+ bool do_flush;
+ u32 mask;
+ int res;
+
+ if (flags.mask & BR_LEARNING) {
+ bool learning = flags.val & BR_LEARNING;
+
+ mask = YT921X_PORT_LEARN_DIS;
+ res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port),
+ mask, !learning);
+ if (res)
+ return res;
+ }
+
+ /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP
+ * is set
+ */
+
+ /* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */
+
+ do_flush = false;
+ if (flags.mask & BR_HAIRPIN_MODE) {
+ pp->hairpin = flags.val & BR_HAIRPIN_MODE;
+ do_flush = true;
+ }
+ if (flags.mask & BR_ISOLATED) {
+ pp->isolated = flags.val & BR_ISOLATED;
+ do_flush = true;
+ }
+ if (do_flush) {
+ struct dsa_switch *ds = &priv->ds;
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct net_device *bdev;
+
+ bdev = dsa_port_bridge_dev_get(dp);
+ if (bdev) {
+ u32 ports_mask;
+
+ ports_mask = dsa_bridge_ports(ds, bdev);
+ ports_mask |= priv->cpu_ports_mask;
+ res = yt921x_bridge(priv, ports_mask);
+ if (res)
+ return res;
+ }
+ }
+
+ return 0;
+}
+
+static int
+yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags flags,
+ struct netlink_ext_ack *extack)
+{
+ if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD |
+ BR_MCAST_FLOOD | BR_ISOLATED))
+ return -EINVAL;
+ return 0;
+}
+
+static int
+yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags flags,
+ struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ if (dsa_is_cpu_port(ds, port))
+ return 0;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_bridge_flags(priv, port, flags);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static void
+yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port,
+ struct dsa_bridge bridge)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct device *dev = to_device(priv);
+ int res;
+
+ if (dsa_is_cpu_port(ds, port))
+ return;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_bridge_leave(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "unbridge",
+ port, res);
+}
+
+static int
+yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port,
+ struct dsa_bridge bridge, bool *tx_fwd_offload,
+ struct netlink_ext_ack *extack)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ u16 ports_mask;
+ int res;
+
+ if (dsa_is_cpu_port(ds, port))
+ return 0;
+
+ ports_mask = dsa_bridge_ports(ds, bridge.dev);
+ ports_mask |= priv->cpu_ports_mask;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_bridge_join(priv, port, ports_mask);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port,
+ const struct switchdev_mst_state *st)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ mask = YT921X_STP_PORTn_M(port);
+ switch (st->state) {
+ case BR_STATE_DISABLED:
+ ctrl = YT921X_STP_PORTn_DISABLED(port);
+ break;
+ case BR_STATE_LISTENING:
+ case BR_STATE_LEARNING:
+ ctrl = YT921X_STP_PORTn_LEARNING(port);
+ break;
+ case BR_STATE_FORWARDING:
+ default:
+ ctrl = YT921X_STP_PORTn_FORWARD(port);
+ break;
+ case BR_STATE_BLOCKING:
+ ctrl = YT921X_STP_PORTn_BLOCKING(port);
+ break;
+ }
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int
+yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge,
+ const struct switchdev_vlan_msti *msti)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ u64 mask64;
+ u64 ctrl64;
+ int res;
+
+ if (!msti->vid)
+ return -EINVAL;
+ if (!msti->msti || msti->msti >= YT921X_MSTI_NUM)
+ return -EINVAL;
+
+ mask64 = YT921X_VLAN_CTRL_STP_ID_M;
+ ctrl64 = YT921X_VLAN_CTRL_STP_ID(msti->msti);
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid),
+ mask64, ctrl64);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static void
+yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ struct device *dev = to_device(priv);
+ bool learning;
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ mask = YT921X_STP_PORTn_M(port);
+ learning = false;
+ switch (state) {
+ case BR_STATE_DISABLED:
+ ctrl = YT921X_STP_PORTn_DISABLED(port);
+ break;
+ case BR_STATE_LISTENING:
+ ctrl = YT921X_STP_PORTn_LEARNING(port);
+ break;
+ case BR_STATE_LEARNING:
+ ctrl = YT921X_STP_PORTn_LEARNING(port);
+ learning = dp->learning;
+ break;
+ case BR_STATE_FORWARDING:
+ default:
+ ctrl = YT921X_STP_PORTn_FORWARD(port);
+ learning = dp->learning;
+ break;
+ case BR_STATE_BLOCKING:
+ ctrl = YT921X_STP_PORTn_BLOCKING(port);
+ break;
+ }
+
+ mutex_lock(&priv->reg_lock);
+ do {
+ res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl);
+ if (res)
+ break;
+
+ mask = YT921X_PORT_LEARN_DIS;
+ ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0;
+ res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port),
+ mask, ctrl);
+ } while (0);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for",
+ port, res);
+}
+
+static int yt921x_port_down(struct yt921x_priv *priv, int port)
+{
+ u32 mask;
+ int res;
+
+ mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
+ res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask);
+ if (res)
+ return res;
+
+ if (yt921x_port_is_external(port)) {
+ mask = YT921X_SERDES_LINK;
+ res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask);
+ if (res)
+ return res;
+
+ mask = YT921X_XMII_LINK;
+ res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int
+yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode,
+ phy_interface_t interface, int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ switch (speed) {
+ case SPEED_10:
+ ctrl = YT921X_PORT_SPEED_10;
+ break;
+ case SPEED_100:
+ ctrl = YT921X_PORT_SPEED_100;
+ break;
+ case SPEED_1000:
+ ctrl = YT921X_PORT_SPEED_1000;
+ break;
+ case SPEED_2500:
+ ctrl = YT921X_PORT_SPEED_2500;
+ break;
+ case SPEED_10000:
+ ctrl = YT921X_PORT_SPEED_10000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (duplex == DUPLEX_FULL)
+ ctrl |= YT921X_PORT_DUPLEX_FULL;
+ if (tx_pause)
+ ctrl |= YT921X_PORT_TX_PAUSE;
+ if (rx_pause)
+ ctrl |= YT921X_PORT_RX_PAUSE;
+ ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN;
+ res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl);
+ if (res)
+ return res;
+
+ if (yt921x_port_is_external(port)) {
+ mask = YT921X_SERDES_SPEED_M;
+ switch (speed) {
+ case SPEED_10:
+ ctrl = YT921X_SERDES_SPEED_10;
+ break;
+ case SPEED_100:
+ ctrl = YT921X_SERDES_SPEED_100;
+ break;
+ case SPEED_1000:
+ ctrl = YT921X_SERDES_SPEED_1000;
+ break;
+ case SPEED_2500:
+ ctrl = YT921X_SERDES_SPEED_2500;
+ break;
+ case SPEED_10000:
+ ctrl = YT921X_SERDES_SPEED_10000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mask |= YT921X_SERDES_DUPLEX_FULL;
+ if (duplex == DUPLEX_FULL)
+ ctrl |= YT921X_SERDES_DUPLEX_FULL;
+ mask |= YT921X_SERDES_TX_PAUSE;
+ if (tx_pause)
+ ctrl |= YT921X_SERDES_TX_PAUSE;
+ mask |= YT921X_SERDES_RX_PAUSE;
+ if (rx_pause)
+ ctrl |= YT921X_SERDES_RX_PAUSE;
+ mask |= YT921X_SERDES_LINK;
+ ctrl |= YT921X_SERDES_LINK;
+ res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
+ mask, ctrl);
+ if (res)
+ return res;
+
+ mask = YT921X_XMII_LINK;
+ res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask);
+ if (res)
+ return res;
+
+ switch (speed) {
+ case SPEED_10:
+ ctrl = YT921X_MDIO_POLLING_SPEED_10;
+ break;
+ case SPEED_100:
+ ctrl = YT921X_MDIO_POLLING_SPEED_100;
+ break;
+ case SPEED_1000:
+ ctrl = YT921X_MDIO_POLLING_SPEED_1000;
+ break;
+ case SPEED_2500:
+ ctrl = YT921X_MDIO_POLLING_SPEED_2500;
+ break;
+ case SPEED_10000:
+ ctrl = YT921X_MDIO_POLLING_SPEED_10000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (duplex == DUPLEX_FULL)
+ ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL;
+ ctrl |= YT921X_MDIO_POLLING_LINK;
+ res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl);
+ if (res)
+ return res;
+ }
+
+ return 0;
+}
+
+static int
+yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct device *dev = to_device(priv);
+ u32 mask;
+ u32 ctrl;
+ int res;
+
+ if (!yt921x_port_is_external(port)) {
+ if (interface != PHY_INTERFACE_MODE_INTERNAL) {
+ dev_err(dev, "Wrong mode %d on port %d\n",
+ interface, port);
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ switch (interface) {
+ /* SERDES */
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_100BASEX:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ mask = YT921X_SERDES_CTRL_PORTn(port);
+ res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask);
+ if (res)
+ return res;
+
+ mask = YT921X_XMII_CTRL_PORTn(port);
+ res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask);
+ if (res)
+ return res;
+
+ mask = YT921X_SERDES_MODE_M;
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ ctrl = YT921X_SERDES_MODE_SGMII;
+ break;
+ case PHY_INTERFACE_MODE_100BASEX:
+ ctrl = YT921X_SERDES_MODE_100BASEX;
+ break;
+ case PHY_INTERFACE_MODE_1000BASEX:
+ ctrl = YT921X_SERDES_MODE_1000BASEX;
+ break;
+ case PHY_INTERFACE_MODE_2500BASEX:
+ ctrl = YT921X_SERDES_MODE_2500BASEX;
+ break;
+ default:
+ return -EINVAL;
+ }
+ res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port),
+ mask, ctrl);
+ if (res)
+ return res;
+
+ break;
+ /* add XMII support here */
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void
+yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ int port = dp->index;
+ int res;
+
+ /* No need to sync; port control block is hold until device remove */
+ cancel_delayed_work(&priv->ports[port].mib_read);
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_port_down(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down",
+ port, res);
+}
+
+static void
+yt921x_phylink_mac_link_up(struct phylink_config *config,
+ struct phy_device *phydev, unsigned int mode,
+ phy_interface_t interface, int speed, int duplex,
+ bool tx_pause, bool rx_pause)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ int port = dp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_port_up(priv, port, mode, interface, speed, duplex,
+ tx_pause, rx_pause);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up",
+ port, res);
+
+ schedule_delayed_work(&priv->ports[port].mib_read, 0);
+}
+
+static void
+yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct dsa_port *dp = dsa_phylink_to_port(config);
+ struct yt921x_priv *priv = to_yt921x_priv(dp->ds);
+ int port = dp->index;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_port_config(priv, port, mode, state->interface);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config",
+ port, res);
+}
+
+static void
+yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port,
+ struct phylink_config *config)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ const struct yt921x_info *info = priv->info;
+
+ config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
+ MAC_10 | MAC_100 | MAC_1000;
+
+ if (info->internal_mask & BIT(port)) {
+ /* Port 10 for MCU should probably go here too. But since that
+ * is untested yet, turn it down for the moment by letting it
+ * fall to the default branch.
+ */
+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+ config->supported_interfaces);
+ } else if (info->external_mask & BIT(port)) {
+ /* TODO: external ports may support SERDES only, XMII only, or
+ * SERDES + XMII depending on the chip. However, we can't get
+ * the accurate config table due to lack of document, thus
+ * we simply declare SERDES + XMII and rely on the correctness
+ * of devicetree for now.
+ */
+
+ /* SERDES */
+ __set_bit(PHY_INTERFACE_MODE_SGMII,
+ config->supported_interfaces);
+ /* REVSGMII (SGMII in PHY role) should go here, once
+ * PHY_INTERFACE_MODE_REVSGMII is introduced.
+ */
+ __set_bit(PHY_INTERFACE_MODE_100BASEX,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+ config->supported_interfaces);
+ __set_bit(PHY_INTERFACE_MODE_2500BASEX,
+ config->supported_interfaces);
+ config->mac_capabilities |= MAC_2500FD;
+
+ /* XMII */
+
+ /* Not tested. To add support for XMII:
+ * - Add proper interface modes below
+ * - Handle them in yt921x_port_config()
+ */
+ }
+ /* no such port: empty supported_interfaces causes phylink to turn it
+ * down
+ */
+}
+
+static int yt921x_port_setup(struct yt921x_priv *priv, int port)
+{
+ struct dsa_switch *ds = &priv->ds;
+ u32 ctrl;
+ int res;
+
+ res = yt921x_userport_standalone(priv, port);
+ if (res)
+ return res;
+
+ if (dsa_is_cpu_port(ds, port)) {
+ /* Egress of CPU port is supposed to be completely controlled
+ * via tagging, so set to oneway isolated (drop all packets
+ * without tag).
+ */
+ ctrl = ~(u32)0;
+ res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port),
+ ctrl);
+ if (res)
+ return res;
+
+ /* To simplify FDB "isolation" simulation, we also disable
+ * learning on the CPU port, and let software identify packets
+ * towarding CPU (either trapped or a static FDB entry is
+ * matched, no matter which bridge that entry is for), which is
+ * already done by yt921x_userport_standalone(). As a result,
+ * VLAN-awareness becomes unrelated on the CPU port (set to
+ * VLAN-unaware by the way).
+ */
+ }
+
+ return 0;
+}
+
+static enum dsa_tag_protocol
+yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port,
+ enum dsa_tag_protocol m)
+{
+ return DSA_TAG_PROTO_YT921X;
+}
+
+static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_port_setup(priv, port);
+ mutex_unlock(&priv->reg_lock);
+
+ return res;
+}
+
+static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp)
+{
+ u32 val = YT921X_EDATA_DATA_IDLE;
+ int res;
+
+ res = yt921x_reg_wait(priv, YT921X_EDATA_DATA,
+ YT921X_EDATA_DATA_STATUS_M, &val);
+ if (res)
+ return res;
+
+ *valp = val;
+ return 0;
+}
+
+static int
+yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp)
+{
+ u32 ctrl;
+ u32 val;
+ int res;
+
+ ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ;
+ res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl);
+ if (res)
+ return res;
+ res = yt921x_edata_wait(priv, &val);
+ if (res)
+ return res;
+
+ *valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val);
+ return 0;
+}
+
+static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp)
+{
+ u32 val;
+ int res;
+
+ res = yt921x_edata_wait(priv, &val);
+ if (res)
+ return res;
+ return yt921x_edata_read_cont(priv, addr, valp);
+}
+
+static int yt921x_chip_detect(struct yt921x_priv *priv)
+{
+ struct device *dev = to_device(priv);
+ const struct yt921x_info *info;
+ u8 extmode;
+ u32 chipid;
+ u32 major;
+ u32 mode;
+ int res;
+
+ res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid);
+ if (res)
+ return res;
+
+ major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid);
+
+ for (info = yt921x_infos; info->name; info++)
+ if (info->major == major)
+ break;
+ if (!info->name) {
+ dev_err(dev, "Unexpected chipid 0x%x\n", chipid);
+ return -ENODEV;
+ }
+
+ res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode);
+ if (res)
+ return res;
+ res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode);
+ if (res)
+ return res;
+
+ for (; info->name; info++)
+ if (info->major == major && info->mode == mode &&
+ info->extmode == extmode)
+ break;
+ if (!info->name) {
+ dev_err(dev,
+ "Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n",
+ chipid, mode, extmode);
+ return -ENODEV;
+ }
+
+ /* Print chipid here since we are interested in lower 16 bits */
+ dev_info(dev,
+ "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n",
+ info->name, chipid, mode, extmode);
+
+ priv->info = info;
+ return 0;
+}
+
+static int yt921x_chip_reset(struct yt921x_priv *priv)
+{
+ struct device *dev = to_device(priv);
+ u16 eth_p_tag;
+ u32 val;
+ int res;
+
+ res = yt921x_chip_detect(priv);
+ if (res)
+ return res;
+
+ /* Reset */
+ res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW);
+ if (res)
+ return res;
+
+ /* RST_HW is almost same as GPIO hard reset, so we need this delay. */
+ fsleep(YT921X_RST_DELAY_US);
+
+ val = 0;
+ res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val);
+ if (res)
+ return res;
+
+ /* Check for tag EtherType; do it after reset in case you messed it up
+ * before.
+ */
+ res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val);
+ if (res)
+ return res;
+ eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val);
+ if (eth_p_tag != ETH_P_YT921X) {
+ dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag,
+ ETH_P_YT921X);
+ /* Despite being possible, we choose not to set CPU_TAG_TPID,
+ * since there is no way it can be different unless you have the
+ * wrong chip.
+ */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int yt921x_chip_setup(struct yt921x_priv *priv)
+{
+ struct dsa_switch *ds = &priv->ds;
+ unsigned long cpu_ports_mask;
+ u64 ctrl64;
+ u32 ctrl;
+ int port;
+ int res;
+
+ /* Enable DSA */
+ priv->cpu_ports_mask = dsa_cpu_ports(ds);
+
+ ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN |
+ YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask));
+ res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl);
+ if (res)
+ return res;
+
+ /* Enable and clear MIB */
+ res = yt921x_reg_set_bits(priv, YT921X_FUNC, YT921X_FUNC_MIB);
+ if (res)
+ return res;
+
+ ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT;
+ res = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl);
+ if (res)
+ return res;
+
+ /* Setup software switch */
+ ctrl = YT921X_CPU_COPY_TO_EXT_CPU;
+ res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl);
+ if (res)
+ return res;
+
+ ctrl = GENMASK(10, 0);
+ res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl);
+ if (res)
+ return res;
+
+ /* YT921x does not support native DSA port bridging, so we use port
+ * isolation to emulate it. However, be especially careful that port
+ * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from
+ * another bridge) is matched and the destination port (in another
+ * bridge) is blocked, the packet will be dropped instead of flooding to
+ * the "bridged" ports, thus we need to trap and handle those packets by
+ * software.
+ *
+ * If there is no more than one bridge, we might be able to drop them
+ * directly given some conditions are met, but we trap them in all cases
+ * for now.
+ */
+ ctrl = 0;
+ for (int i = 0; i < YT921X_PORT_NUM; i++)
+ ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i);
+ /* Except for CPU ports, if any packets are sent via CPU ports without
+ * tag, they should be dropped.
+ */
+ cpu_ports_mask = priv->cpu_ports_mask;
+ for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) {
+ ctrl &= ~YT921X_ACT_UNK_ACTn_M(port);
+ ctrl |= YT921X_ACT_UNK_ACTn_DROP(port);
+ }
+ res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl);
+ if (res)
+ return res;
+ res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl);
+ if (res)
+ return res;
+
+ /* Tagged VID 0 should be treated as untagged, which confuses the
+ * hardware a lot
+ */
+ ctrl64 = YT921X_VLAN_CTRL_LEARN_DIS | YT921X_VLAN_CTRL_PORTS_M;
+ res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrl64);
+ if (res)
+ return res;
+
+ /* Miscellaneous */
+ res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP);
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int yt921x_dsa_setup(struct dsa_switch *ds)
+{
+ struct yt921x_priv *priv = to_yt921x_priv(ds);
+ struct device *dev = to_device(priv);
+ struct device_node *np = dev->of_node;
+ struct device_node *child;
+ int res;
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_chip_reset(priv);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ return res;
+
+ /* Register the internal mdio bus. Nodes for internal ports should have
+ * proper phy-handle pointing to their PHYs. Not enabling the internal
+ * bus is possible, though pretty wired, if internal ports are not used.
+ */
+ child = of_get_child_by_name(np, "mdio");
+ if (child) {
+ res = yt921x_mbus_int_init(priv, child);
+ of_node_put(child);
+ if (res)
+ return res;
+ }
+
+ /* External mdio bus is optional */
+ child = of_get_child_by_name(np, "mdio-external");
+ if (child) {
+ res = yt921x_mbus_ext_init(priv, child);
+ of_node_put(child);
+ if (res)
+ return res;
+
+ dev_err(dev, "Untested external mdio bus\n");
+ return -ENODEV;
+ }
+
+ mutex_lock(&priv->reg_lock);
+ res = yt921x_chip_setup(priv);
+ mutex_unlock(&priv->reg_lock);
+
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static const struct phylink_mac_ops yt921x_phylink_mac_ops = {
+ .mac_link_down = yt921x_phylink_mac_link_down,
+ .mac_link_up = yt921x_phylink_mac_link_up,
+ .mac_config = yt921x_phylink_mac_config,
+};
+
+static const struct dsa_switch_ops yt921x_dsa_switch_ops = {
+ /* mib */
+ .get_strings = yt921x_dsa_get_strings,
+ .get_ethtool_stats = yt921x_dsa_get_ethtool_stats,
+ .get_sset_count = yt921x_dsa_get_sset_count,
+ .get_eth_mac_stats = yt921x_dsa_get_eth_mac_stats,
+ .get_eth_ctrl_stats = yt921x_dsa_get_eth_ctrl_stats,
+ .get_rmon_stats = yt921x_dsa_get_rmon_stats,
+ .get_stats64 = yt921x_dsa_get_stats64,
+ .get_pause_stats = yt921x_dsa_get_pause_stats,
+ /* eee */
+ .support_eee = dsa_supports_eee,
+ .set_mac_eee = yt921x_dsa_set_mac_eee,
+ /* mtu */
+ .port_change_mtu = yt921x_dsa_port_change_mtu,
+ .port_max_mtu = yt921x_dsa_port_max_mtu,
+ /* hsr */
+ .port_hsr_leave = dsa_port_simple_hsr_leave,
+ .port_hsr_join = dsa_port_simple_hsr_join,
+ /* mirror */
+ .port_mirror_del = yt921x_dsa_port_mirror_del,
+ .port_mirror_add = yt921x_dsa_port_mirror_add,
+ /* fdb */
+ .port_fdb_dump = yt921x_dsa_port_fdb_dump,
+ .port_fast_age = yt921x_dsa_port_fast_age,
+ .set_ageing_time = yt921x_dsa_set_ageing_time,
+ .port_fdb_del = yt921x_dsa_port_fdb_del,
+ .port_fdb_add = yt921x_dsa_port_fdb_add,
+ .port_mdb_del = yt921x_dsa_port_mdb_del,
+ .port_mdb_add = yt921x_dsa_port_mdb_add,
+ /* vlan */
+ .port_vlan_filtering = yt921x_dsa_port_vlan_filtering,
+ .port_vlan_del = yt921x_dsa_port_vlan_del,
+ .port_vlan_add = yt921x_dsa_port_vlan_add,
+ /* bridge */
+ .port_pre_bridge_flags = yt921x_dsa_port_pre_bridge_flags,
+ .port_bridge_flags = yt921x_dsa_port_bridge_flags,
+ .port_bridge_leave = yt921x_dsa_port_bridge_leave,
+ .port_bridge_join = yt921x_dsa_port_bridge_join,
+ /* mst */
+ .port_mst_state_set = yt921x_dsa_port_mst_state_set,
+ .vlan_msti_set = yt921x_dsa_vlan_msti_set,
+ .port_stp_state_set = yt921x_dsa_port_stp_state_set,
+ /* port */
+ .get_tag_protocol = yt921x_dsa_get_tag_protocol,
+ .phylink_get_caps = yt921x_dsa_phylink_get_caps,
+ .port_setup = yt921x_dsa_port_setup,
+ /* chip */
+ .setup = yt921x_dsa_setup,
+};
+
+static void yt921x_mdio_shutdown(struct mdio_device *mdiodev)
+{
+ struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
+
+ if (!priv)
+ return;
+
+ dsa_switch_shutdown(&priv->ds);
+}
+
+static void yt921x_mdio_remove(struct mdio_device *mdiodev)
+{
+ struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev);
+
+ if (!priv)
+ return;
+
+ for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) {
+ struct yt921x_port *pp = &priv->ports[i];
+
+ disable_delayed_work_sync(&pp->mib_read);
+ }
+
+ dsa_unregister_switch(&priv->ds);
+
+ mutex_destroy(&priv->reg_lock);
+}
+
+static int yt921x_mdio_probe(struct mdio_device *mdiodev)
+{
+ struct device *dev = &mdiodev->dev;
+ struct yt921x_reg_mdio *mdio;
+ struct yt921x_priv *priv;
+ struct dsa_switch *ds;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL);
+ if (!mdio)
+ return -ENOMEM;
+
+ mdio->bus = mdiodev->bus;
+ mdio->addr = mdiodev->addr;
+ mdio->switchid = 0;
+
+ mutex_init(&priv->reg_lock);
+
+ priv->reg_ops = &yt921x_reg_ops_mdio;
+ priv->reg_ctx = mdio;
+
+ for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) {
+ struct yt921x_port *pp = &priv->ports[i];
+
+ pp->index = i;
+ INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib);
+ }
+
+ ds = &priv->ds;
+ ds->dev = dev;
+ ds->assisted_learning_on_cpu_port = true;
+ ds->priv = priv;
+ ds->ops = &yt921x_dsa_switch_ops;
+ ds->ageing_time_min = 1 * 5000;
+ ds->ageing_time_max = U16_MAX * 5000;
+ ds->phylink_mac_ops = &yt921x_phylink_mac_ops;
+ ds->num_ports = YT921X_PORT_NUM;
+
+ mdiodev_set_drvdata(mdiodev, priv);
+
+ return dsa_register_switch(ds);
+}
+
+static const struct of_device_id yt921x_of_match[] = {
+ { .compatible = "motorcomm,yt9215" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, yt921x_of_match);
+
+static struct mdio_driver yt921x_mdio_driver = {
+ .probe = yt921x_mdio_probe,
+ .remove = yt921x_mdio_remove,
+ .shutdown = yt921x_mdio_shutdown,
+ .mdiodrv.driver = {
+ .name = YT921X_NAME,
+ .of_match_table = yt921x_of_match,
+ },
+};
+
+mdio_module_driver(yt921x_mdio_driver);
+
+MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>");
+MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h
new file mode 100644
index 000000000000..61bb0ab3b09a
--- /dev/null
+++ b/drivers/net/dsa/yt921x.h
@@ -0,0 +1,567 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025 David Yang
+ */
+
+#ifndef __YT921X_H
+#define __YT921X_H
+
+#include <net/dsa.h>
+
+#define YT921X_SMI_SWITCHID_M GENMASK(3, 2)
+#define YT921X_SMI_SWITCHID(x) FIELD_PREP(YT921X_SMI_SWITCHID_M, (x))
+#define YT921X_SMI_AD BIT(1)
+#define YT921X_SMI_ADDR 0
+#define YT921X_SMI_DATA YT921X_SMI_AD
+#define YT921X_SMI_RW BIT(0)
+#define YT921X_SMI_WRITE 0
+#define YT921X_SMI_READ YT921X_SMI_RW
+
+#define YT921X_SWITCHID_NUM 4
+
+#define YT921X_RST 0x80000
+#define YT921X_RST_HW BIT(31)
+#define YT921X_RST_SW BIT(1)
+#define YT921X_FUNC 0x80004
+#define YT921X_FUNC_MIB BIT(1)
+#define YT921X_CHIP_ID 0x80008
+#define YT921X_CHIP_ID_MAJOR GENMASK(31, 16)
+#define YT921X_EXT_CPU_PORT 0x8000c
+#define YT921X_EXT_CPU_PORT_TAG_EN BIT(15)
+#define YT921X_EXT_CPU_PORT_PORT_EN BIT(14)
+#define YT921X_EXT_CPU_PORT_PORT_M GENMASK(3, 0)
+#define YT921X_EXT_CPU_PORT_PORT(x) FIELD_PREP(YT921X_EXT_CPU_PORT_PORT_M, (x))
+#define YT921X_CPU_TAG_TPID 0x80010
+#define YT921X_CPU_TAG_TPID_TPID_M GENMASK(15, 0)
+/* Same as ETH_P_YT921X, but this represents the true HW default, while the
+ * former is a local convention chosen by us.
+ */
+#define YT921X_CPU_TAG_TPID_TPID_DEFAULT 0x9988
+#define YT921X_PVID_SEL 0x80014
+#define YT921X_PVID_SEL_SVID_PORTn(port) BIT(port)
+#define YT921X_SERDES_CTRL 0x80028
+#define YT921X_SERDES_CTRL_PORTn_TEST(port) BIT((port) - 3)
+#define YT921X_SERDES_CTRL_PORTn(port) BIT((port) - 8)
+#define YT921X_IO_LEVEL 0x80030
+#define YT9215_IO_LEVEL_NORMAL_M GENMASK(5, 4)
+#define YT9215_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9215_IO_LEVEL_NORMAL_M, (x))
+#define YT9215_IO_LEVEL_NORMAL_3V3 YT9215_IO_LEVEL_NORMAL(0)
+#define YT9215_IO_LEVEL_NORMAL_1V8 YT9215_IO_LEVEL_NORMAL(3)
+#define YT9215_IO_LEVEL_RGMII1_M GENMASK(3, 2)
+#define YT9215_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII1_M, (x))
+#define YT9215_IO_LEVEL_RGMII1_3V3 YT9215_IO_LEVEL_RGMII1(0)
+#define YT9215_IO_LEVEL_RGMII1_2V5 YT9215_IO_LEVEL_RGMII1(1)
+#define YT9215_IO_LEVEL_RGMII1_1V8 YT9215_IO_LEVEL_RGMII1(2)
+#define YT9215_IO_LEVEL_RGMII0_M GENMASK(1, 0)
+#define YT9215_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII0_M, (x))
+#define YT9215_IO_LEVEL_RGMII0_3V3 YT9215_IO_LEVEL_RGMII0(0)
+#define YT9215_IO_LEVEL_RGMII0_2V5 YT9215_IO_LEVEL_RGMII0(1)
+#define YT9215_IO_LEVEL_RGMII0_1V8 YT9215_IO_LEVEL_RGMII0(2)
+#define YT9218_IO_LEVEL_RGMII1_M GENMASK(5, 4)
+#define YT9218_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII1_M, (x))
+#define YT9218_IO_LEVEL_RGMII1_3V3 YT9218_IO_LEVEL_RGMII1(0)
+#define YT9218_IO_LEVEL_RGMII1_2V5 YT9218_IO_LEVEL_RGMII1(1)
+#define YT9218_IO_LEVEL_RGMII1_1V8 YT9218_IO_LEVEL_RGMII1(2)
+#define YT9218_IO_LEVEL_RGMII0_M GENMASK(3, 2)
+#define YT9218_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII0_M, (x))
+#define YT9218_IO_LEVEL_RGMII0_3V3 YT9218_IO_LEVEL_RGMII0(0)
+#define YT9218_IO_LEVEL_RGMII0_2V5 YT9218_IO_LEVEL_RGMII0(1)
+#define YT9218_IO_LEVEL_RGMII0_1V8 YT9218_IO_LEVEL_RGMII0(2)
+#define YT9218_IO_LEVEL_NORMAL_M GENMASK(1, 0)
+#define YT9218_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9218_IO_LEVEL_NORMAL_M, (x))
+#define YT9218_IO_LEVEL_NORMAL_3V3 YT9218_IO_LEVEL_NORMAL(0)
+#define YT9218_IO_LEVEL_NORMAL_1V8 YT9218_IO_LEVEL_NORMAL(3)
+#define YT921X_MAC_ADDR_HI2 0x80080
+#define YT921X_MAC_ADDR_LO4 0x80084
+#define YT921X_SERDESn(port) (0x8008c + 4 * ((port) - 8))
+#define YT921X_SERDES_MODE_M GENMASK(9, 7)
+#define YT921X_SERDES_MODE(x) FIELD_PREP(YT921X_SERDES_MODE_M, (x))
+#define YT921X_SERDES_MODE_SGMII YT921X_SERDES_MODE(0)
+#define YT921X_SERDES_MODE_REVSGMII YT921X_SERDES_MODE(1)
+#define YT921X_SERDES_MODE_1000BASEX YT921X_SERDES_MODE(2)
+#define YT921X_SERDES_MODE_100BASEX YT921X_SERDES_MODE(3)
+#define YT921X_SERDES_MODE_2500BASEX YT921X_SERDES_MODE(4)
+#define YT921X_SERDES_RX_PAUSE BIT(6)
+#define YT921X_SERDES_TX_PAUSE BIT(5)
+#define YT921X_SERDES_LINK BIT(4) /* force link */
+#define YT921X_SERDES_DUPLEX_FULL BIT(3)
+#define YT921X_SERDES_SPEED_M GENMASK(2, 0)
+#define YT921X_SERDES_SPEED(x) FIELD_PREP(YT921X_SERDES_SPEED_M, (x))
+#define YT921X_SERDES_SPEED_10 YT921X_SERDES_SPEED(0)
+#define YT921X_SERDES_SPEED_100 YT921X_SERDES_SPEED(1)
+#define YT921X_SERDES_SPEED_1000 YT921X_SERDES_SPEED(2)
+#define YT921X_SERDES_SPEED_10000 YT921X_SERDES_SPEED(3)
+#define YT921X_SERDES_SPEED_2500 YT921X_SERDES_SPEED(4)
+#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port))
+#define YT921X_PORT_CTRL_PAUSE_AN BIT(10)
+#define YT921X_PORTn_STATUS(port) (0x80200 + 4 * (port))
+#define YT921X_PORT_LINK BIT(9) /* CTRL: auto negotiation */
+#define YT921X_PORT_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */
+#define YT921X_PORT_DUPLEX_FULL BIT(7)
+#define YT921X_PORT_RX_PAUSE BIT(6)
+#define YT921X_PORT_TX_PAUSE BIT(5)
+#define YT921X_PORT_RX_MAC_EN BIT(4)
+#define YT921X_PORT_TX_MAC_EN BIT(3)
+#define YT921X_PORT_SPEED_M GENMASK(2, 0)
+#define YT921X_PORT_SPEED(x) FIELD_PREP(YT921X_PORT_SPEED_M, (x))
+#define YT921X_PORT_SPEED_10 YT921X_PORT_SPEED(0)
+#define YT921X_PORT_SPEED_100 YT921X_PORT_SPEED(1)
+#define YT921X_PORT_SPEED_1000 YT921X_PORT_SPEED(2)
+#define YT921X_PORT_SPEED_10000 YT921X_PORT_SPEED(3)
+#define YT921X_PORT_SPEED_2500 YT921X_PORT_SPEED(4)
+#define YT921X_PON_STRAP_FUNC 0x80320
+#define YT921X_PON_STRAP_VAL 0x80324
+#define YT921X_PON_STRAP_CAP 0x80328
+#define YT921X_PON_STRAP_EEE BIT(16)
+#define YT921X_PON_STRAP_LOOP_DETECT BIT(7)
+#define YT921X_MDIO_POLLINGn(port) (0x80364 + 4 * ((port) - 8))
+#define YT921X_MDIO_POLLING_DUPLEX_FULL BIT(4)
+#define YT921X_MDIO_POLLING_LINK BIT(3)
+#define YT921X_MDIO_POLLING_SPEED_M GENMASK(2, 0)
+#define YT921X_MDIO_POLLING_SPEED(x) FIELD_PREP(YT921X_MDIO_POLLING_SPEED_M, (x))
+#define YT921X_MDIO_POLLING_SPEED_10 YT921X_MDIO_POLLING_SPEED(0)
+#define YT921X_MDIO_POLLING_SPEED_100 YT921X_MDIO_POLLING_SPEED(1)
+#define YT921X_MDIO_POLLING_SPEED_1000 YT921X_MDIO_POLLING_SPEED(2)
+#define YT921X_MDIO_POLLING_SPEED_10000 YT921X_MDIO_POLLING_SPEED(3)
+#define YT921X_MDIO_POLLING_SPEED_2500 YT921X_MDIO_POLLING_SPEED(4)
+#define YT921X_SENSOR 0x8036c
+#define YT921X_SENSOR_TEMP BIT(18)
+#define YT921X_TEMP 0x80374
+#define YT921X_CHIP_MODE 0x80388
+#define YT921X_CHIP_MODE_MODE GENMASK(1, 0)
+#define YT921X_XMII_CTRL 0x80394
+#define YT921X_XMII_CTRL_PORTn(port) BIT(9 - (port)) /* Yes, it's reversed */
+#define YT921X_XMIIn(port) (0x80400 + 8 * ((port) - 8))
+#define YT921X_XMII_MODE_M GENMASK(31, 29)
+#define YT921X_XMII_MODE(x) FIELD_PREP(YT921X_XMII_MODE_M, (x))
+#define YT921X_XMII_MODE_MII YT921X_XMII_MODE(0)
+#define YT921X_XMII_MODE_REVMII YT921X_XMII_MODE(1)
+#define YT921X_XMII_MODE_RMII YT921X_XMII_MODE(2)
+#define YT921X_XMII_MODE_REVRMII YT921X_XMII_MODE(3)
+#define YT921X_XMII_MODE_RGMII YT921X_XMII_MODE(4)
+#define YT921X_XMII_MODE_DISABLE YT921X_XMII_MODE(5)
+#define YT921X_XMII_LINK BIT(19) /* force link */
+#define YT921X_XMII_EN BIT(18)
+#define YT921X_XMII_SOFT_RST BIT(17)
+#define YT921X_XMII_RGMII_TX_DELAY_150PS_M GENMASK(16, 13)
+#define YT921X_XMII_RGMII_TX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMII_TX_DELAY_150PS_M, (x))
+#define YT921X_XMII_TX_CLK_IN BIT(11)
+#define YT921X_XMII_RX_CLK_IN BIT(10)
+#define YT921X_XMII_RGMII_TX_DELAY_2NS BIT(8)
+#define YT921X_XMII_RGMII_TX_CLK_OUT BIT(7)
+#define YT921X_XMII_RGMII_RX_DELAY_150PS_M GENMASK(6, 3)
+#define YT921X_XMII_RGMII_RX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMII_RX_DELAY_150PS_M, (x))
+#define YT921X_XMII_RMII_PHY_TX_CLK_OUT BIT(2)
+#define YT921X_XMII_REVMII_TX_CLK_OUT BIT(1)
+#define YT921X_XMII_REVMII_RX_CLK_OUT BIT(0)
+
+#define YT921X_MACn_FRAME(port) (0x81008 + 0x1000 * (port))
+#define YT921X_MAC_FRAME_SIZE_M GENMASK(21, 8)
+#define YT921X_MAC_FRAME_SIZE(x) FIELD_PREP(YT921X_MAC_FRAME_SIZE_M, (x))
+
+#define YT921X_EEEn_VAL(port) (0xa0000 + 0x40 * (port))
+#define YT921X_EEE_VAL_DATA BIT(1)
+
+#define YT921X_EEE_CTRL 0xb0000
+#define YT921X_EEE_CTRL_ENn(port) BIT(port)
+
+#define YT921X_MIB_CTRL 0xc0004
+#define YT921X_MIB_CTRL_CLEAN BIT(30)
+#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3)
+#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x))
+#define YT921X_MIB_CTRL_ONE_PORT BIT(1)
+#define YT921X_MIB_CTRL_ALL_PORT BIT(0)
+#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port))
+#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x))
+#define YT921X_MIB_DATA_RX_BROADCAST 0x00
+#define YT921X_MIB_DATA_RX_PAUSE 0x04
+#define YT921X_MIB_DATA_RX_MULTICAST 0x08
+#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c
+
+#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10
+#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14
+#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18
+#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20
+#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24
+#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28
+#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c
+
+#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30
+#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34
+/* 0x38: unused */
+#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c
+
+/* 0x40: 64 bytes */
+#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44
+/* 0x48: 64 bytes */
+#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c
+
+#define YT921X_MIB_DATA_RX_DROPPED 0x50
+#define YT921X_MIB_DATA_TX_BROADCAST 0x54
+#define YT921X_MIB_DATA_TX_PAUSE 0x58
+#define YT921X_MIB_DATA_TX_MULTICAST 0x5c
+
+#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60
+#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64
+#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68
+#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c
+
+#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70
+#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74
+#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78
+#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c
+
+/* 0x80: unused */
+#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84
+/* 0x88: 64 bytes */
+#define YT921X_MIB_DATA_TX_COLLISION 0x8c
+
+#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90
+#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94
+#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98
+#define YT921X_MIB_DATA_TX_PKT 0x9c
+
+#define YT921X_MIB_DATA_TX_DEFERRED 0xa0
+#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4
+#define YT921X_MIB_DATA_RX_OAM 0xa8
+#define YT921X_MIB_DATA_TX_OAM 0xac
+
+#define YT921X_EDATA_CTRL 0xe0000
+#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8)
+#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x))
+#define YT921X_EDATA_CTRL_OP_M GENMASK(3, 0)
+#define YT921X_EDATA_CTRL_OP(x) FIELD_PREP(YT921X_EDATA_CTRL_OP_M, (x))
+#define YT921X_EDATA_CTRL_READ YT921X_EDATA_CTRL_OP(5)
+#define YT921X_EDATA_DATA 0xe0004
+#define YT921X_EDATA_DATA_DATA_M GENMASK(31, 24)
+#define YT921X_EDATA_DATA_STATUS_M GENMASK(3, 0)
+#define YT921X_EDATA_DATA_STATUS(x) FIELD_PREP(YT921X_EDATA_DATA_STATUS_M, (x))
+#define YT921X_EDATA_DATA_IDLE YT921X_EDATA_DATA_STATUS(3)
+
+#define YT921X_EXT_MBUS_OP 0x6a000
+#define YT921X_INT_MBUS_OP 0xf0000
+#define YT921X_MBUS_OP_START BIT(0)
+#define YT921X_EXT_MBUS_CTRL 0x6a004
+#define YT921X_INT_MBUS_CTRL 0xf0004
+#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21)
+#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x))
+#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16)
+#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x))
+#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */
+#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x))
+#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4)
+#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */
+#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x))
+#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1)
+#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2)
+#define YT921X_EXT_MBUS_DOUT 0x6a008
+#define YT921X_INT_MBUS_DOUT 0xf0008
+#define YT921X_EXT_MBUS_DIN 0x6a00c
+#define YT921X_INT_MBUS_DIN 0xf000c
+
+#define YT921X_PORTn_EGR(port) (0x100000 + 4 * (port))
+#define YT921X_PORT_EGR_TPID_CTAG_M GENMASK(5, 4)
+#define YT921X_PORT_EGR_TPID_CTAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_CTAG_M, (x))
+#define YT921X_PORT_EGR_TPID_STAG_M GENMASK(3, 2)
+#define YT921X_PORT_EGR_TPID_STAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_STAG_M, (x))
+#define YT921X_TPID_EGRn(x) (0x100300 + 4 * (x)) /* [0, 3] */
+#define YT921X_TPID_EGR_TPID_M GENMASK(15, 0)
+
+#define YT921X_VLAN_IGR_FILTER 0x180280
+#define YT921X_VLAN_IGR_FILTER_PORTn_BYPASS_IGMP(port) BIT((port) + 11)
+#define YT921X_VLAN_IGR_FILTER_PORTn(port) BIT(port)
+#define YT921X_PORTn_ISOLATION(port) (0x180294 + 4 * (port))
+#define YT921X_PORT_ISOLATION_BLOCKn(port) BIT(port)
+#define YT921X_STPn(n) (0x18038c + 4 * (n))
+#define YT921X_STP_PORTn_M(port) GENMASK(2 * (port) + 1, 2 * (port))
+#define YT921X_STP_PORTn(port, x) ((x) << (2 * (port)))
+#define YT921X_STP_PORTn_DISABLED(port) YT921X_STP_PORTn(port, 0)
+#define YT921X_STP_PORTn_LEARNING(port) YT921X_STP_PORTn(port, 1)
+#define YT921X_STP_PORTn_BLOCKING(port) YT921X_STP_PORTn(port, 2)
+#define YT921X_STP_PORTn_FORWARD(port) YT921X_STP_PORTn(port, 3)
+#define YT921X_PORTn_LEARN(port) (0x1803d0 + 4 * (port))
+#define YT921X_PORT_LEARN_VID_LEARN_MULTI_EN BIT(22)
+#define YT921X_PORT_LEARN_VID_LEARN_MODE BIT(21)
+#define YT921X_PORT_LEARN_VID_LEARN_EN BIT(20)
+#define YT921X_PORT_LEARN_SUSPEND_COPY_EN BIT(19)
+#define YT921X_PORT_LEARN_SUSPEND_DROP_EN BIT(18)
+#define YT921X_PORT_LEARN_DIS BIT(17)
+#define YT921X_PORT_LEARN_LIMIT_EN BIT(16)
+#define YT921X_PORT_LEARN_LIMIT_M GENMASK(15, 8)
+#define YT921X_PORT_LEARN_LIMIT(x) FIELD_PREP(YT921X_PORT_LEARN_LIMIT_M, (x))
+#define YT921X_PORT_LEARN_DROP_ON_EXCEEDED BIT(2)
+#define YT921X_PORT_LEARN_MODE_M GENMASK(1, 0)
+#define YT921X_PORT_LEARN_MODE(x) FIELD_PREP(YT921X_PORT_LEARN_MODE_M, (x))
+#define YT921X_PORT_LEARN_MODE_AUTO YT921X_PORT_LEARN_MODE(0)
+#define YT921X_PORT_LEARN_MODE_AUTO_AND_COPY YT921X_PORT_LEARN_MODE(1)
+#define YT921X_PORT_LEARN_MODE_CPU_CONTROL YT921X_PORT_LEARN_MODE(2)
+#define YT921X_AGEING 0x180440
+#define YT921X_AGEING_INTERVAL_M GENMASK(15, 0)
+#define YT921X_FDB_IN0 0x180454
+#define YT921X_FDB_IN1 0x180458
+#define YT921X_FDB_IN2 0x18045c
+#define YT921X_FDB_OP 0x180460
+#define YT921X_FDB_OP_INDEX_M GENMASK(22, 11)
+#define YT921X_FDB_OP_INDEX(x) FIELD_PREP(YT921X_FDB_OP_INDEX_M, (x))
+#define YT921X_FDB_OP_MODE_INDEX BIT(10) /* mac+fid / index */
+#define YT921X_FDB_OP_FLUSH_MCAST BIT(9) /* ucast / mcast */
+#define YT921X_FDB_OP_FLUSH_M GENMASK(8, 7)
+#define YT921X_FDB_OP_FLUSH(x) FIELD_PREP(YT921X_FDB_OP_FLUSH_M, (x))
+#define YT921X_FDB_OP_FLUSH_ALL YT921X_FDB_OP_FLUSH(0)
+#define YT921X_FDB_OP_FLUSH_PORT YT921X_FDB_OP_FLUSH(1)
+#define YT921X_FDB_OP_FLUSH_PORT_VID YT921X_FDB_OP_FLUSH(2)
+#define YT921X_FDB_OP_FLUSH_VID YT921X_FDB_OP_FLUSH(3)
+#define YT921X_FDB_OP_FLUSH_STATIC BIT(6)
+#define YT921X_FDB_OP_NEXT_TYPE_M GENMASK(5, 4)
+#define YT921X_FDB_OP_NEXT_TYPE(x) FIELD_PREP(YT921X_FDB_OP_NEXT_TYPE_M, (x))
+#define YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT YT921X_FDB_OP_NEXT_TYPE(0)
+#define YT921X_FDB_OP_NEXT_TYPE_UCAST_VID YT921X_FDB_OP_NEXT_TYPE(1)
+#define YT921X_FDB_OP_NEXT_TYPE_UCAST YT921X_FDB_OP_NEXT_TYPE(2)
+#define YT921X_FDB_OP_NEXT_TYPE_MCAST YT921X_FDB_OP_NEXT_TYPE(3)
+#define YT921X_FDB_OP_OP_M GENMASK(3, 1)
+#define YT921X_FDB_OP_OP(x) FIELD_PREP(YT921X_FDB_OP_OP_M, (x))
+#define YT921X_FDB_OP_OP_ADD YT921X_FDB_OP_OP(0)
+#define YT921X_FDB_OP_OP_DEL YT921X_FDB_OP_OP(1)
+#define YT921X_FDB_OP_OP_GET_ONE YT921X_FDB_OP_OP(2)
+#define YT921X_FDB_OP_OP_GET_NEXT YT921X_FDB_OP_OP(3)
+#define YT921X_FDB_OP_OP_FLUSH YT921X_FDB_OP_OP(4)
+#define YT921X_FDB_OP_START BIT(0)
+#define YT921X_FDB_RESULT 0x180464
+#define YT921X_FDB_RESULT_DONE BIT(15)
+#define YT921X_FDB_RESULT_NOTFOUND BIT(14)
+#define YT921X_FDB_RESULT_OVERWRITED BIT(13)
+#define YT921X_FDB_RESULT_INDEX_M GENMASK(11, 0)
+#define YT921X_FDB_RESULT_INDEX(x) FIELD_PREP(YT921X_FDB_RESULT_INDEX_M, (x))
+#define YT921X_FDB_OUT0 0x1804b0
+#define YT921X_FDB_IO0_ADDR_HI4_M GENMASK(31, 0)
+#define YT921X_FDB_OUT1 0x1804b4
+#define YT921X_FDB_IO1_EGR_INT_PRI_EN BIT(31)
+#define YT921X_FDB_IO1_STATUS_M GENMASK(30, 28)
+#define YT921X_FDB_IO1_STATUS(x) FIELD_PREP(YT921X_FDB_IO1_STATUS_M, (x))
+#define YT921X_FDB_IO1_STATUS_INVALID YT921X_FDB_IO1_STATUS(0)
+#define YT921X_FDB_IO1_STATUS_MIN_TIME YT921X_FDB_IO1_STATUS(1)
+#define YT921X_FDB_IO1_STATUS_MOVE_AGING_MAX_TIME YT921X_FDB_IO1_STATUS(3)
+#define YT921X_FDB_IO1_STATUS_MAX_TIME YT921X_FDB_IO1_STATUS(5)
+#define YT921X_FDB_IO1_STATUS_PENDING YT921X_FDB_IO1_STATUS(6)
+#define YT921X_FDB_IO1_STATUS_STATIC YT921X_FDB_IO1_STATUS(7)
+#define YT921X_FDB_IO1_FID_M GENMASK(27, 16) /* filtering ID (VID) */
+#define YT921X_FDB_IO1_FID(x) FIELD_PREP(YT921X_FDB_IO1_FID_M, (x))
+#define YT921X_FDB_IO1_ADDR_LO2_M GENMASK(15, 0)
+#define YT921X_FDB_OUT2 0x1804b8
+#define YT921X_FDB_IO2_MOVE_AGING_STATUS_M GENMASK(31, 30)
+#define YT921X_FDB_IO2_IGR_DROP BIT(29)
+#define YT921X_FDB_IO2_EGR_PORTS_M GENMASK(28, 18)
+#define YT921X_FDB_IO2_EGR_PORTS(x) FIELD_PREP(YT921X_FDB_IO2_EGR_PORTS_M, (x))
+#define YT921X_FDB_IO2_EGR_DROP BIT(17)
+#define YT921X_FDB_IO2_COPY_TO_CPU BIT(16)
+#define YT921X_FDB_IO2_IGR_INT_PRI_EN BIT(15)
+#define YT921X_FDB_IO2_INT_PRI_M GENMASK(14, 12)
+#define YT921X_FDB_IO2_INT_PRI(x) FIELD_PREP(YT921X_FDB_IO2_INT_PRI_M, (x))
+#define YT921X_FDB_IO2_NEW_VID_M GENMASK(11, 0)
+#define YT921X_FDB_IO2_NEW_VID(x) FIELD_PREP(YT921X_FDB_IO2_NEW_VID_M, (x))
+#define YT921X_FILTER_UNK_UCAST 0x180508
+#define YT921X_FILTER_UNK_MCAST 0x18050c
+#define YT921X_FILTER_MCAST 0x180510
+#define YT921X_FILTER_BCAST 0x180514
+#define YT921X_FILTER_PORTS_M GENMASK(10, 0)
+#define YT921X_FILTER_PORTS(x) FIELD_PREP(YT921X_FILTER_PORTS_M, (x))
+#define YT921X_FILTER_PORTn(port) BIT(port)
+#define YT921X_VLAN_EGR_FILTER 0x180598
+#define YT921X_VLAN_EGR_FILTER_PORTn(port) BIT(port)
+#define YT921X_CPU_COPY 0x180690
+#define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2)
+#define YT921X_CPU_COPY_TO_INT_CPU BIT(1)
+#define YT921X_CPU_COPY_TO_EXT_CPU BIT(0)
+#define YT921X_ACT_UNK_UCAST 0x180734
+#define YT921X_ACT_UNK_MCAST 0x180738
+#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_RMA BIT(23)
+#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_IGMP BIT(22)
+#define YT921X_ACT_UNK_ACTn_M(port) GENMASK(2 * (port) + 1, 2 * (port))
+#define YT921X_ACT_UNK_ACTn(port, x) ((x) << (2 * (port)))
+#define YT921X_ACT_UNK_ACTn_FORWARD(port) YT921X_ACT_UNK_ACTn(port, 0) /* flood */
+#define YT921X_ACT_UNK_ACTn_TRAP(port) YT921X_ACT_UNK_ACTn(port, 1) /* steer to CPU */
+#define YT921X_ACT_UNK_ACTn_DROP(port) YT921X_ACT_UNK_ACTn(port, 2) /* discard */
+/* NEVER use this action; see comments in the tag driver */
+#define YT921X_ACT_UNK_ACTn_COPY(port) YT921X_ACT_UNK_ACTn(port, 3) /* flood and copy */
+#define YT921X_FDB_HW_FLUSH 0x180958
+#define YT921X_FDB_HW_FLUSH_ON_LINKDOWN BIT(0)
+
+#define YT921X_VLANn_CTRL(vlan) (0x188000 + 8 * (vlan))
+#define YT921X_VLAN_CTRL_UNTAG_PORTS_M GENMASK_ULL(50, 40)
+#define YT921X_VLAN_CTRL_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_UNTAG_PORTS_M, (x))
+#define YT921X_VLAN_CTRL_UNTAG_PORTn(port) BIT_ULL((port) + 40)
+#define YT921X_VLAN_CTRL_STP_ID_M GENMASK_ULL(39, 36)
+#define YT921X_VLAN_CTRL_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRL_STP_ID_M, (x))
+#define YT921X_VLAN_CTRL_SVLAN_EN BIT_ULL(35)
+#define YT921X_VLAN_CTRL_FID_M GENMASK_ULL(34, 23)
+#define YT921X_VLAN_CTRL_FID(x) FIELD_PREP(YT921X_VLAN_CTRL_FID_M, (x))
+#define YT921X_VLAN_CTRL_LEARN_DIS BIT_ULL(22)
+#define YT921X_VLAN_CTRL_INT_PRI_EN BIT_ULL(21)
+#define YT921X_VLAN_CTRL_INT_PRI_M GENMASK_ULL(20, 18)
+#define YT921X_VLAN_CTRL_PORTS_M GENMASK_ULL(17, 7)
+#define YT921X_VLAN_CTRL_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRL_PORTS_M, (x))
+#define YT921X_VLAN_CTRL_PORTn(port) BIT_ULL((port) + 7)
+#define YT921X_VLAN_CTRL_BYPASS_1X_AC BIT_ULL(6)
+#define YT921X_VLAN_CTRL_METER_EN BIT_ULL(5)
+#define YT921X_VLAN_CTRL_METER_ID_M GENMASK_ULL(4, 0)
+
+#define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */
+#define YT921X_TPID_IGR_TPID_M GENMASK(15, 0)
+#define YT921X_PORTn_IGR_TPID(port) (0x210010 + 4 * (port))
+#define YT921X_PORT_IGR_TPIDn_STAG_M GENMASK(7, 4)
+#define YT921X_PORT_IGR_TPIDn_STAG(x) BIT((x) + 4)
+#define YT921X_PORT_IGR_TPIDn_CTAG_M GENMASK(3, 0)
+#define YT921X_PORT_IGR_TPIDn_CTAG(x) BIT(x)
+
+#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port))
+#define YT921X_PORT_VLAN_CTRL_SVLAN_PRI_EN BIT(31)
+#define YT921X_PORT_VLAN_CTRL_CVLAN_PRI_EN BIT(30)
+#define YT921X_PORT_VLAN_CTRL_SVID_M GENMASK(29, 18)
+#define YT921X_PORT_VLAN_CTRL_SVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL_SVID_M, (x))
+#define YT921X_PORT_VLAN_CTRL_CVID_M GENMASK(17, 6)
+#define YT921X_PORT_VLAN_CTRL_CVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL_CVID_M, (x))
+#define YT921X_PORT_VLAN_CTRL_SVLAN_PRI_M GENMASK(5, 3)
+#define YT921X_PORT_VLAN_CTRL_CVLAN_PRI_M GENMASK(2, 0)
+#define YT921X_PORTn_VLAN_CTRL1(port) (0x230080 + 4 * (port))
+#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_EN BIT(8)
+#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_PROFILE_ID_M GENMASK(7, 4)
+#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_TAGGED BIT(3)
+#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_UNTAGGED BIT(2)
+#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED BIT(1)
+#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED BIT(0)
+
+#define YT921X_MIRROR 0x300300
+#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16)
+#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x))
+#define YT921X_MIRROR_IGR_PORTn(port) BIT((port) + 16)
+#define YT921X_MIRROR_EGR_PORTS_M GENMASK(14, 4)
+#define YT921X_MIRROR_EGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_EGR_PORTS_M, (x))
+#define YT921X_MIRROR_EGR_PORTn(port) BIT((port) + 4)
+#define YT921X_MIRROR_PORT_M GENMASK(3, 0)
+#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x))
+
+#define YT921X_EDATA_EXTMODE 0xfb
+#define YT921X_EDATA_LEN 0x100
+
+#define YT921X_FDB_NUM 4096
+
+enum yt921x_fdb_entry_status {
+ YT921X_FDB_ENTRY_STATUS_INVALID = 0,
+ YT921X_FDB_ENTRY_STATUS_MIN_TIME = 1,
+ YT921X_FDB_ENTRY_STATUS_MOVE_AGING_MAX_TIME = 3,
+ YT921X_FDB_ENTRY_STATUS_MAX_TIME = 5,
+ YT921X_FDB_ENTRY_STATUS_PENDING = 6,
+ YT921X_FDB_ENTRY_STATUS_STATIC = 7,
+};
+
+#define YT921X_MSTI_NUM 16
+
+#define YT9215_MAJOR 0x9002
+#define YT9218_MAJOR 0x9001
+
+/* required for a hard reset */
+#define YT921X_RST_DELAY_US 10000
+
+#define YT921X_FRAME_SIZE_MAX 0x2400 /* 9216 */
+
+#define YT921X_TAG_LEN 8
+
+/* 8 internal + 2 external + 1 mcu */
+#define YT921X_PORT_NUM 11
+
+#define yt921x_port_is_internal(port) ((port) < 8)
+#define yt921x_port_is_external(port) (8 <= (port) && (port) < 9)
+
+struct yt921x_mib {
+ u64 rx_broadcast;
+ u64 rx_pause;
+ u64 rx_multicast;
+ u64 rx_crc_errors;
+
+ u64 rx_alignment_errors;
+ u64 rx_undersize_errors;
+ u64 rx_fragment_errors;
+ u64 rx_64byte;
+
+ u64 rx_65_127byte;
+ u64 rx_128_255byte;
+ u64 rx_256_511byte;
+ u64 rx_512_1023byte;
+
+ u64 rx_1024_1518byte;
+ u64 rx_jumbo;
+ u64 rx_good_bytes;
+
+ u64 rx_bad_bytes;
+ u64 rx_oversize_errors;
+
+ u64 rx_dropped;
+ u64 tx_broadcast;
+ u64 tx_pause;
+ u64 tx_multicast;
+
+ u64 tx_undersize_errors;
+ u64 tx_64byte;
+ u64 tx_65_127byte;
+ u64 tx_128_255byte;
+
+ u64 tx_256_511byte;
+ u64 tx_512_1023byte;
+ u64 tx_1024_1518byte;
+ u64 tx_jumbo;
+
+ u64 tx_good_bytes;
+ u64 tx_collisions;
+
+ u64 tx_aborted_errors;
+ u64 tx_multiple_collisions;
+ u64 tx_single_collisions;
+ u64 tx_good;
+
+ u64 tx_deferred;
+ u64 tx_late_collisions;
+ u64 rx_oam;
+ u64 tx_oam;
+};
+
+struct yt921x_port {
+ unsigned char index;
+
+ bool hairpin;
+ bool isolated;
+
+ struct delayed_work mib_read;
+ struct yt921x_mib mib;
+ u64 rx_frames;
+ u64 tx_frames;
+};
+
+struct yt921x_reg_ops {
+ int (*read)(void *context, u32 reg, u32 *valp);
+ int (*write)(void *context, u32 reg, u32 val);
+};
+
+struct yt921x_priv {
+ struct dsa_switch ds;
+
+ const struct yt921x_info *info;
+ /* cache of dsa_cpu_ports(ds) */
+ u16 cpu_ports_mask;
+
+ /* protect the access to the switch registers */
+ struct mutex reg_lock;
+ const struct yt921x_reg_ops *reg_ops;
+ void *reg_ctx;
+
+ /* mdio master bus */
+ struct mii_bus *mbus_int;
+ struct mii_bus *mbus_ext;
+
+ struct yt921x_port ports[YT921X_PORT_NUM];
+
+ u16 eee_ports_mask;
+};
+
+#endif
diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c
index ecdea58e6a21..2227c83a4862 100644
--- a/drivers/net/ethernet/3com/3c515.c
+++ b/drivers/net/ethernet/3com/3c515.c
@@ -1547,9 +1547,8 @@ static const struct ethtool_ops netdev_ethtool_ops = {
.set_msglevel = netdev_set_msglevel,
};
-
#ifdef MODULE
-void cleanup_module(void)
+static void __exit corkscrew_exit_module(void)
{
while (!list_empty(&root_corkscrew_dev)) {
struct net_device *dev;
@@ -1563,4 +1562,5 @@ void cleanup_module(void)
free_netdev(dev);
}
}
+module_exit(corkscrew_exit_module);
#endif /* MODULE */
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index aead145dd91d..4a1b368ca7e6 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -129,6 +129,7 @@ source "drivers/net/ethernet/microchip/Kconfig"
source "drivers/net/ethernet/mscc/Kconfig"
source "drivers/net/ethernet/microsoft/Kconfig"
source "drivers/net/ethernet/moxa/Kconfig"
+source "drivers/net/ethernet/mucse/Kconfig"
source "drivers/net/ethernet/myricom/Kconfig"
config FEALNX
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 998dd628b202..2e18df8ca8ec 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/
obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
+obj-$(CONFIG_NET_VENDOR_MUCSE) += mucse/
obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
obj-$(CONFIG_FEALNX) += fealnx.o
obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 433a646e9831..75893c90a0a1 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -137,11 +137,11 @@ static void airoha_fe_maccr_init(struct airoha_eth *eth)
for (p = 1; p <= ARRAY_SIZE(eth->ports); p++)
airoha_fe_set(eth, REG_GDM_FWD_CFG(p),
- GDM_TCP_CKSUM | GDM_UDP_CKSUM | GDM_IP4_CKSUM |
- GDM_DROP_CRC_ERR);
+ GDM_TCP_CKSUM_MASK | GDM_UDP_CKSUM_MASK |
+ GDM_IP4_CKSUM_MASK | GDM_DROP_CRC_ERR_MASK);
- airoha_fe_rmw(eth, REG_CDM1_VLAN_CTRL, CDM1_VLAN_MASK,
- FIELD_PREP(CDM1_VLAN_MASK, 0x8100));
+ airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK,
+ FIELD_PREP(CDM_VLAN_MASK, 0x8100));
airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PAD);
}
@@ -297,8 +297,11 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth)
int q;
all_rsv = airoha_fe_get_pse_all_rsv(eth);
- /* hw misses PPE2 oq rsv */
- all_rsv += PSE_RSV_PAGES * pse_port_num_queues[FE_PSE_PORT_PPE2];
+ if (airoha_ppe_is_enabled(eth, 1)) {
+ /* hw misses PPE2 oq rsv */
+ all_rsv += PSE_RSV_PAGES *
+ pse_port_num_queues[FE_PSE_PORT_PPE2];
+ }
airoha_fe_set(eth, REG_FE_PSE_BUF_SET, all_rsv);
/* CMD1 */
@@ -335,13 +338,17 @@ static void airoha_fe_pse_ports_init(struct airoha_eth *eth)
for (q = 4; q < pse_port_num_queues[FE_PSE_PORT_CDM4]; q++)
airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_CDM4, q,
PSE_QUEUE_RSV_PAGES);
- /* PPE2 */
- for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) {
- if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2)
- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q,
- PSE_QUEUE_RSV_PAGES);
- else
- airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2, q, 0);
+ if (airoha_ppe_is_enabled(eth, 1)) {
+ /* PPE2 */
+ for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_PPE2]; q++) {
+ if (q < pse_port_num_queues[FE_PSE_PORT_PPE2] / 2)
+ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2,
+ q,
+ PSE_QUEUE_RSV_PAGES);
+ else
+ airoha_fe_set_pse_oq_rsv(eth, FE_PSE_PORT_PPE2,
+ q, 0);
+ }
}
/* GMD4 */
for (q = 0; q < pse_port_num_queues[FE_PSE_PORT_GDM4]; q++)
@@ -396,46 +403,46 @@ static int airoha_fe_mc_vlan_clear(struct airoha_eth *eth)
static void airoha_fe_crsn_qsel_init(struct airoha_eth *eth)
{
/* CDM1_CRSN_QSEL */
- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_22 >> 2),
- CDM1_CRSN_QSEL_REASON_MASK(CRSN_22),
- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_22),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_22 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_22),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_22),
CDM_CRSN_QSEL_Q1));
- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_08 >> 2),
- CDM1_CRSN_QSEL_REASON_MASK(CRSN_08),
- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_08),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_08 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_08),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_08),
CDM_CRSN_QSEL_Q1));
- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_21 >> 2),
- CDM1_CRSN_QSEL_REASON_MASK(CRSN_21),
- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_21),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_21 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_21),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_21),
CDM_CRSN_QSEL_Q1));
- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_24 >> 2),
- CDM1_CRSN_QSEL_REASON_MASK(CRSN_24),
- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_24),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_24 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_24),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_24),
CDM_CRSN_QSEL_Q6));
- airoha_fe_rmw(eth, REG_CDM1_CRSN_QSEL(CRSN_25 >> 2),
- CDM1_CRSN_QSEL_REASON_MASK(CRSN_25),
- FIELD_PREP(CDM1_CRSN_QSEL_REASON_MASK(CRSN_25),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(1, CRSN_25 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_25),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_25),
CDM_CRSN_QSEL_Q1));
/* CDM2_CRSN_QSEL */
- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_08 >> 2),
- CDM2_CRSN_QSEL_REASON_MASK(CRSN_08),
- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_08),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_08 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_08),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_08),
CDM_CRSN_QSEL_Q1));
- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_21 >> 2),
- CDM2_CRSN_QSEL_REASON_MASK(CRSN_21),
- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_21),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_21 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_21),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_21),
CDM_CRSN_QSEL_Q1));
- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_22 >> 2),
- CDM2_CRSN_QSEL_REASON_MASK(CRSN_22),
- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_22),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_22 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_22),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_22),
CDM_CRSN_QSEL_Q1));
- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_24 >> 2),
- CDM2_CRSN_QSEL_REASON_MASK(CRSN_24),
- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_24),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_24 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_24),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_24),
CDM_CRSN_QSEL_Q6));
- airoha_fe_rmw(eth, REG_CDM2_CRSN_QSEL(CRSN_25 >> 2),
- CDM2_CRSN_QSEL_REASON_MASK(CRSN_25),
- FIELD_PREP(CDM2_CRSN_QSEL_REASON_MASK(CRSN_25),
+ airoha_fe_rmw(eth, REG_CDM_CRSN_QSEL(2, CRSN_25 >> 2),
+ CDM_CRSN_QSEL_REASON_MASK(CRSN_25),
+ FIELD_PREP(CDM_CRSN_QSEL_REASON_MASK(CRSN_25),
CDM_CRSN_QSEL_Q1));
}
@@ -455,18 +462,18 @@ static int airoha_fe_init(struct airoha_eth *eth)
airoha_fe_wr(eth, REG_FE_PCE_CFG,
PCE_DPI_EN_MASK | PCE_KA_EN_MASK | PCE_MC_EN_MASK);
/* set vip queue selection to ring 1 */
- airoha_fe_rmw(eth, REG_CDM1_FWD_CFG, CDM1_VIP_QSEL_MASK,
- FIELD_PREP(CDM1_VIP_QSEL_MASK, 0x4));
- airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_VIP_QSEL_MASK,
- FIELD_PREP(CDM2_VIP_QSEL_MASK, 0x4));
+ airoha_fe_rmw(eth, REG_CDM_FWD_CFG(1), CDM_VIP_QSEL_MASK,
+ FIELD_PREP(CDM_VIP_QSEL_MASK, 0x4));
+ airoha_fe_rmw(eth, REG_CDM_FWD_CFG(2), CDM_VIP_QSEL_MASK,
+ FIELD_PREP(CDM_VIP_QSEL_MASK, 0x4));
/* set GDM4 source interface offset to 8 */
- airoha_fe_rmw(eth, REG_GDM4_SRC_PORT_SET,
- GDM4_SPORT_OFF2_MASK |
- GDM4_SPORT_OFF1_MASK |
- GDM4_SPORT_OFF0_MASK,
- FIELD_PREP(GDM4_SPORT_OFF2_MASK, 8) |
- FIELD_PREP(GDM4_SPORT_OFF1_MASK, 8) |
- FIELD_PREP(GDM4_SPORT_OFF0_MASK, 8));
+ airoha_fe_rmw(eth, REG_GDM_SRC_PORT_SET(4),
+ GDM_SPORT_OFF2_MASK |
+ GDM_SPORT_OFF1_MASK |
+ GDM_SPORT_OFF0_MASK,
+ FIELD_PREP(GDM_SPORT_OFF2_MASK, 8) |
+ FIELD_PREP(GDM_SPORT_OFF1_MASK, 8) |
+ FIELD_PREP(GDM_SPORT_OFF0_MASK, 8));
/* set PSE Page as 128B */
airoha_fe_rmw(eth, REG_FE_DMA_GLO_CFG,
@@ -492,8 +499,8 @@ static int airoha_fe_init(struct airoha_eth *eth)
airoha_fe_set(eth, REG_GDM_MISC_CFG,
GDM2_RDM_ACK_WAIT_PREF_MASK |
GDM2_CHN_VLD_MODE_MASK);
- airoha_fe_rmw(eth, REG_CDM2_FWD_CFG, CDM2_OAM_QSEL_MASK,
- FIELD_PREP(CDM2_OAM_QSEL_MASK, 15));
+ airoha_fe_rmw(eth, REG_CDM_FWD_CFG(2), CDM_OAM_QSEL_MASK,
+ FIELD_PREP(CDM_OAM_QSEL_MASK, 15));
/* init fragment and assemble Force Port */
/* NPU Core-3, NPU Bridge Channel-3 */
@@ -507,8 +514,8 @@ static int airoha_fe_init(struct airoha_eth *eth)
FIELD_PREP(IP_ASSEMBLE_PORT_MASK, 0) |
FIELD_PREP(IP_ASSEMBLE_NBQ_MASK, 22));
- airoha_fe_set(eth, REG_GDM3_FWD_CFG, GDM3_PAD_EN_MASK);
- airoha_fe_set(eth, REG_GDM4_FWD_CFG, GDM4_PAD_EN_MASK);
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(3), GDM_PAD_EN_MASK);
+ airoha_fe_set(eth, REG_GDM_FWD_CFG(4), GDM_PAD_EN_MASK);
airoha_fe_crsn_qsel_init(eth);
@@ -516,7 +523,7 @@ static int airoha_fe_init(struct airoha_eth *eth)
airoha_fe_set(eth, REG_FE_CPORT_CFG, FE_CPORT_PORT_XFC_MASK);
/* default aging mode for mbi unlock issue */
- airoha_fe_rmw(eth, REG_GDM2_CHN_RLS,
+ airoha_fe_rmw(eth, REG_GDM_CHN_RLS(2),
MBI_RX_AGE_SEL_MASK | MBI_TX_AGE_SEL_MASK,
FIELD_PREP(MBI_RX_AGE_SEL_MASK, 3) |
FIELD_PREP(MBI_TX_AGE_SEL_MASK, 3));
@@ -524,25 +531,6 @@ static int airoha_fe_init(struct airoha_eth *eth)
/* disable IFC by default */
airoha_fe_clear(eth, REG_FE_CSR_IFC_CFG, FE_IFC_EN_MASK);
- airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(0),
- FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM1) |
- FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM1) |
- FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM1) |
- FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM1) |
- FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM1) |
- FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM1) |
- FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM1) |
- FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM1));
- airoha_fe_wr(eth, REG_PPE_DFT_CPORT0(1),
- FIELD_PREP(DFT_CPORT_MASK(7), FE_PSE_PORT_CDM2) |
- FIELD_PREP(DFT_CPORT_MASK(6), FE_PSE_PORT_CDM2) |
- FIELD_PREP(DFT_CPORT_MASK(5), FE_PSE_PORT_CDM2) |
- FIELD_PREP(DFT_CPORT_MASK(4), FE_PSE_PORT_CDM2) |
- FIELD_PREP(DFT_CPORT_MASK(3), FE_PSE_PORT_CDM2) |
- FIELD_PREP(DFT_CPORT_MASK(2), FE_PSE_PORT_CDM2) |
- FIELD_PREP(DFT_CPORT_MASK(1), FE_PSE_PORT_CDM2) |
- FIELD_PREP(DFT_CPORT_MASK(0), FE_PSE_PORT_CDM2));
-
/* enable 1:N vlan action, init vlan table */
airoha_fe_set(eth, REG_MC_VLAN_EN, MC_VLAN_EN_MASK);
@@ -904,19 +892,13 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget)
dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
- memset(e, 0, sizeof(*e));
+ e->dma_addr = 0;
+ list_add_tail(&e->list, &q->tx_list);
+
WRITE_ONCE(desc->msg0, 0);
WRITE_ONCE(desc->msg1, 0);
q->queued--;
- /* completion ring can report out-of-order indexes if hw QoS
- * is enabled and packets with different priority are queued
- * to same DMA ring. Take into account possible out-of-order
- * reports incrementing DMA ring tail pointer
- */
- while (q->tail != q->head && !q->entry[q->tail].dma_addr)
- q->tail = (q->tail + 1) % q->ndesc;
-
if (skb) {
u16 queue = skb_get_queue_mapping(skb);
struct netdev_queue *txq;
@@ -961,6 +943,7 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q,
q->ndesc = size;
q->qdma = qdma;
q->free_thr = 1 + MAX_SKB_FRAGS;
+ INIT_LIST_HEAD(&q->tx_list);
q->entry = devm_kzalloc(eth->dev, q->ndesc * sizeof(*q->entry),
GFP_KERNEL);
@@ -973,9 +956,9 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q,
return -ENOMEM;
for (i = 0; i < q->ndesc; i++) {
- u32 val;
+ u32 val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1);
- val = FIELD_PREP(QDMA_DESC_DONE_MASK, 1);
+ list_add_tail(&q->entry[i].list, &q->tx_list);
WRITE_ONCE(q->desc[i].ctrl, cpu_to_le32(val));
}
@@ -985,9 +968,9 @@ static int airoha_qdma_init_tx_queue(struct airoha_queue *q,
airoha_qdma_wr(qdma, REG_TX_RING_BASE(qid), dma_addr);
airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid), TX_RING_CPU_IDX_MASK,
- FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head));
+ FIELD_PREP(TX_RING_CPU_IDX_MASK, 0));
airoha_qdma_rmw(qdma, REG_TX_DMA_IDX(qid), TX_RING_DMA_IDX_MASK,
- FIELD_PREP(TX_RING_DMA_IDX_MASK, q->head));
+ FIELD_PREP(TX_RING_DMA_IDX_MASK, 0));
return 0;
}
@@ -1043,17 +1026,21 @@ static int airoha_qdma_init_tx(struct airoha_qdma *qdma)
static void airoha_qdma_cleanup_tx_queue(struct airoha_queue *q)
{
struct airoha_eth *eth = q->qdma->eth;
+ int i;
spin_lock_bh(&q->lock);
- while (q->queued) {
- struct airoha_queue_entry *e = &q->entry[q->tail];
+ for (i = 0; i < q->ndesc; i++) {
+ struct airoha_queue_entry *e = &q->entry[i];
+
+ if (!e->dma_addr)
+ continue;
dma_unmap_single(eth->dev, e->dma_addr, e->dma_len,
DMA_TO_DEVICE);
dev_kfree_skb_any(e->skb);
+ e->dma_addr = 0;
e->skb = NULL;
-
- q->tail = (q->tail + 1) % q->ndesc;
+ list_add_tail(&e->list, &q->tx_list);
q->queued--;
}
spin_unlock_bh(&q->lock);
@@ -1387,8 +1374,7 @@ static int airoha_hw_init(struct platform_device *pdev,
int err, i;
/* disable xsi */
- err = reset_control_bulk_assert(ARRAY_SIZE(eth->xsi_rsts),
- eth->xsi_rsts);
+ err = reset_control_bulk_assert(eth->soc->num_xsi_rsts, eth->xsi_rsts);
if (err)
return err;
@@ -1695,19 +1681,23 @@ static int airoha_dev_set_macaddr(struct net_device *dev, void *p)
return 0;
}
-static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
+static int airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
{
- u32 pse_port = port->id == 3 ? FE_PSE_PORT_GDM3 : FE_PSE_PORT_GDM4;
struct airoha_eth *eth = port->qdma->eth;
- u32 chan = port->id == 3 ? 4 : 0;
+ u32 val, pse_port, chan, nbq;
+ int src_port;
/* Forward the traffic to the proper GDM port */
+ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
+ : FE_PSE_PORT_GDM4;
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(2), pse_port);
- airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC);
+ airoha_fe_clear(eth, REG_GDM_FWD_CFG(2), GDM_STRIP_CRC_MASK);
/* Enable GDM2 loopback */
airoha_fe_wr(eth, REG_GDM_TXCHN_EN(2), 0xffffffff);
airoha_fe_wr(eth, REG_GDM_RXCHN_EN(2), 0xffff);
+
+ chan = port->id == AIROHA_GDM3_IDX ? airoha_is_7581(eth) ? 4 : 3 : 0;
airoha_fe_rmw(eth, REG_GDM_LPBK_CFG(2),
LPBK_CHAN_MASK | LPBK_MODE_MASK | LPBK_EN_MASK,
FIELD_PREP(LPBK_CHAN_MASK, chan) |
@@ -1722,36 +1712,36 @@ static void airhoha_set_gdm2_loopback(struct airoha_gdm_port *port)
airoha_fe_clear(eth, REG_FE_VIP_PORT_EN, BIT(2));
airoha_fe_clear(eth, REG_FE_IFC_PORT_EN, BIT(2));
- if (port->id == 3) {
- /* FIXME: handle XSI_PCE1_PORT */
- airoha_fe_rmw(eth, REG_FE_WAN_PORT,
- WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
- FIELD_PREP(WAN0_MASK, HSGMII_LAN_PCIE0_SRCPORT));
- airoha_fe_rmw(eth,
- REG_SP_DFT_CPORT(HSGMII_LAN_PCIE0_SRCPORT >> 3),
- SP_CPORT_PCIE0_MASK,
- FIELD_PREP(SP_CPORT_PCIE0_MASK,
- FE_PSE_PORT_CDM2));
- } else {
- /* FIXME: handle XSI_USB_PORT */
+ /* XXX: handle XSI_USB_PORT and XSI_PCE1_PORT */
+ nbq = port->id == AIROHA_GDM3_IDX && airoha_is_7581(eth) ? 4 : 0;
+ src_port = eth->soc->ops.get_src_port_id(port, nbq);
+ if (src_port < 0)
+ return src_port;
+
+ airoha_fe_rmw(eth, REG_FE_WAN_PORT,
+ WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
+ FIELD_PREP(WAN0_MASK, src_port));
+ val = src_port & SP_CPORT_DFT_MASK;
+ airoha_fe_rmw(eth,
+ REG_SP_DFT_CPORT(src_port >> fls(SP_CPORT_DFT_MASK)),
+ SP_CPORT_MASK(val),
+ FE_PSE_PORT_CDM2 << __ffs(SP_CPORT_MASK(val)));
+
+ if (port->id != AIROHA_GDM3_IDX && airoha_is_7581(eth))
airoha_fe_rmw(eth, REG_SRC_PORT_FC_MAP6,
FC_ID_OF_SRC_PORT24_MASK,
FIELD_PREP(FC_ID_OF_SRC_PORT24_MASK, 2));
- airoha_fe_rmw(eth, REG_FE_WAN_PORT,
- WAN1_EN_MASK | WAN1_MASK | WAN0_MASK,
- FIELD_PREP(WAN0_MASK, HSGMII_LAN_ETH_SRCPORT));
- airoha_fe_rmw(eth,
- REG_SP_DFT_CPORT(HSGMII_LAN_ETH_SRCPORT >> 3),
- SP_CPORT_ETH_MASK,
- FIELD_PREP(SP_CPORT_ETH_MASK, FE_PSE_PORT_CDM2));
- }
+
+ return 0;
}
static int airoha_dev_init(struct net_device *dev)
{
struct airoha_gdm_port *port = netdev_priv(dev);
- struct airoha_eth *eth = port->qdma->eth;
- u32 pse_port;
+ struct airoha_qdma *qdma = port->qdma;
+ struct airoha_eth *eth = qdma->eth;
+ u32 pse_port, fe_cpu_port;
+ u8 ppe_id;
airoha_set_macaddr(port, dev->dev_addr);
@@ -1759,18 +1749,37 @@ static int airoha_dev_init(struct net_device *dev)
case 3:
case 4:
/* If GDM2 is active we can't enable loopback */
- if (!eth->ports[1])
- airhoha_set_gdm2_loopback(port);
+ if (!eth->ports[1]) {
+ int err;
+
+ err = airhoha_set_gdm2_loopback(port);
+ if (err)
+ return err;
+ }
fallthrough;
case 2:
- pse_port = FE_PSE_PORT_PPE2;
- break;
- default:
+ if (airoha_ppe_is_enabled(eth, 1)) {
+ /* For PPE2 always use secondary cpu port. */
+ fe_cpu_port = FE_PSE_PORT_CDM2;
+ pse_port = FE_PSE_PORT_PPE2;
+ break;
+ }
+ fallthrough;
+ default: {
+ u8 qdma_id = qdma - &eth->qdma[0];
+
+ /* For PPE1 select cpu port according to the running QDMA. */
+ fe_cpu_port = qdma_id ? FE_PSE_PORT_CDM2 : FE_PSE_PORT_CDM1;
pse_port = FE_PSE_PORT_PPE1;
break;
}
+ }
airoha_set_gdm_port_fwd_cfg(eth, REG_GDM_FWD_CFG(port->id), pse_port);
+ ppe_id = pse_port == FE_PSE_PORT_PPE2 ? 1 : 0;
+ airoha_fe_rmw(eth, REG_PPE_DFT_CPORT0(ppe_id),
+ DFT_CPORT_MASK(port->id),
+ fe_cpu_port << __ffs(DFT_CPORT_MASK(port->id)));
return 0;
}
@@ -1873,18 +1882,20 @@ static u32 airoha_get_dsa_tag(struct sk_buff *skb, struct net_device *dev)
#endif
}
-static bool airoha_dev_tx_queue_busy(struct airoha_queue *q, u32 nr_frags)
+static int airoha_get_fe_port(struct airoha_gdm_port *port)
{
- u32 tail = q->tail <= q->head ? q->tail + q->ndesc : q->tail;
- u32 index = q->head + nr_frags;
+ struct airoha_qdma *qdma = port->qdma;
+ struct airoha_eth *eth = qdma->eth;
- /* completion napi can free out-of-order tx descriptors if hw QoS is
- * enabled and packets with different priorities are queued to the same
- * DMA ring. Take into account possible out-of-order reports checking
- * if the tx queue is full using circular buffer head/tail pointers
- * instead of the number of queued packets.
- */
- return index >= tail;
+ switch (eth->soc->version) {
+ case 0x7583:
+ return port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3
+ : port->id;
+ case 0x7581:
+ default:
+ return port->id == AIROHA_GDM4_IDX ? FE_PSE_PORT_GDM4
+ : port->id;
+ }
}
static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
@@ -1893,8 +1904,10 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
struct airoha_gdm_port *port = netdev_priv(dev);
struct airoha_qdma *qdma = port->qdma;
u32 nr_frags, tag, msg0, msg1, len;
+ struct airoha_queue_entry *e;
struct netdev_queue *txq;
struct airoha_queue *q;
+ LIST_HEAD(tx_list);
void *data;
int i, qid;
u16 index;
@@ -1927,7 +1940,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
}
}
- fport = port->id == 4 ? FE_PSE_PORT_GDM4 : port->id;
+ fport = airoha_get_fe_port(port);
msg1 = FIELD_PREP(QDMA_ETH_TXMSG_FPORT_MASK, fport) |
FIELD_PREP(QDMA_ETH_TXMSG_METER_MASK, 0x7f);
@@ -1940,7 +1953,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
txq = netdev_get_tx_queue(dev, qid);
nr_frags = 1 + skb_shinfo(skb)->nr_frags;
- if (airoha_dev_tx_queue_busy(q, nr_frags)) {
+ if (q->queued + nr_frags >= q->ndesc) {
/* not enough space in the queue */
netif_tx_stop_queue(txq);
spin_unlock_bh(&q->lock);
@@ -1949,11 +1962,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
len = skb_headlen(skb);
data = skb->data;
- index = q->head;
+
+ e = list_first_entry(&q->tx_list, struct airoha_queue_entry,
+ list);
+ index = e - q->entry;
for (i = 0; i < nr_frags; i++) {
struct airoha_qdma_desc *desc = &q->desc[index];
- struct airoha_queue_entry *e = &q->entry[index];
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
dma_addr_t addr;
u32 val;
@@ -1963,7 +1978,14 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
if (unlikely(dma_mapping_error(dev->dev.parent, addr)))
goto error_unmap;
- index = (index + 1) % q->ndesc;
+ list_move_tail(&e->list, &tx_list);
+ e->skb = i ? NULL : skb;
+ e->dma_addr = addr;
+ e->dma_len = len;
+
+ e = list_first_entry(&q->tx_list, struct airoha_queue_entry,
+ list);
+ index = e - q->entry;
val = FIELD_PREP(QDMA_DESC_LEN_MASK, len);
if (i < nr_frags - 1)
@@ -1976,15 +1998,9 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
WRITE_ONCE(desc->msg1, cpu_to_le32(msg1));
WRITE_ONCE(desc->msg2, cpu_to_le32(0xffff));
- e->skb = i ? NULL : skb;
- e->dma_addr = addr;
- e->dma_len = len;
-
data = skb_frag_address(frag);
len = skb_frag_size(frag);
}
-
- q->head = index;
q->queued += i;
skb_tx_timestamp(skb);
@@ -1993,7 +2009,7 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
if (netif_xmit_stopped(txq) || !netdev_xmit_more())
airoha_qdma_rmw(qdma, REG_TX_CPU_IDX(qid),
TX_RING_CPU_IDX_MASK,
- FIELD_PREP(TX_RING_CPU_IDX_MASK, q->head));
+ FIELD_PREP(TX_RING_CPU_IDX_MASK, index));
if (q->ndesc - q->queued < q->free_thr)
netif_tx_stop_queue(txq);
@@ -2003,10 +2019,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
error_unmap:
- for (i--; i >= 0; i--) {
- index = (q->head + i) % q->ndesc;
- dma_unmap_single(dev->dev.parent, q->entry[index].dma_addr,
- q->entry[index].dma_len, DMA_TO_DEVICE);
+ while (!list_empty(&tx_list)) {
+ e = list_first_entry(&tx_list, struct airoha_queue_entry,
+ list);
+ dma_unmap_single(dev->dev.parent, e->dma_addr, e->dma_len,
+ DMA_TO_DEVICE);
+ e->dma_addr = 0;
+ list_move_tail(&e->list, &q->tx_list);
}
spin_unlock_bh(&q->lock);
@@ -2036,8 +2055,12 @@ static void airoha_ethtool_get_mac_stats(struct net_device *dev,
airoha_update_hw_stats(port);
do {
start = u64_stats_fetch_begin(&port->stats.syncp);
+ stats->FramesTransmittedOK = port->stats.tx_ok_pkts;
+ stats->OctetsTransmittedOK = port->stats.tx_ok_bytes;
stats->MulticastFramesXmittedOK = port->stats.tx_multicast;
stats->BroadcastFramesXmittedOK = port->stats.tx_broadcast;
+ stats->FramesReceivedOK = port->stats.rx_ok_pkts;
+ stats->OctetsReceivedOK = port->stats.rx_ok_bytes;
stats->BroadcastFramesReceivedOK = port->stats.rx_broadcast;
} while (u64_stats_fetch_retry(&port->stats.syncp, start));
}
@@ -2780,6 +2803,7 @@ static const struct ethtool_ops airoha_ethtool_ops = {
.get_drvinfo = airoha_ethtool_get_drvinfo,
.get_eth_mac_stats = airoha_ethtool_get_mac_stats,
.get_rmon_stats = airoha_ethtool_get_rmon_stats,
+ .get_link = ethtool_op_get_link,
};
static int airoha_metadata_dst_alloc(struct airoha_gdm_port *port)
@@ -2917,6 +2941,7 @@ free_metadata_dst:
static int airoha_probe(struct platform_device *pdev)
{
+ struct reset_control_bulk_data *xsi_rsts;
struct device_node *np;
struct airoha_eth *eth;
int i, err;
@@ -2925,6 +2950,10 @@ static int airoha_probe(struct platform_device *pdev)
if (!eth)
return -ENOMEM;
+ eth->soc = of_device_get_match_data(&pdev->dev);
+ if (!eth->soc)
+ return -EINVAL;
+
eth->dev = &pdev->dev;
err = dma_set_mask_and_coherent(eth->dev, DMA_BIT_MASK(32));
@@ -2949,13 +2978,18 @@ static int airoha_probe(struct platform_device *pdev)
return err;
}
- eth->xsi_rsts[0].id = "xsi-mac";
- eth->xsi_rsts[1].id = "hsi0-mac";
- eth->xsi_rsts[2].id = "hsi1-mac";
- eth->xsi_rsts[3].id = "hsi-mac";
- eth->xsi_rsts[4].id = "xfp-mac";
+ xsi_rsts = devm_kcalloc(eth->dev,
+ eth->soc->num_xsi_rsts, sizeof(*xsi_rsts),
+ GFP_KERNEL);
+ if (!xsi_rsts)
+ return -ENOMEM;
+
+ eth->xsi_rsts = xsi_rsts;
+ for (i = 0; i < eth->soc->num_xsi_rsts; i++)
+ eth->xsi_rsts[i].id = eth->soc->xsi_rsts_names[i];
+
err = devm_reset_control_bulk_get_exclusive(eth->dev,
- ARRAY_SIZE(eth->xsi_rsts),
+ eth->soc->num_xsi_rsts,
eth->xsi_rsts);
if (err) {
dev_err(eth->dev, "failed to get bulk xsi reset lines\n");
@@ -3043,8 +3077,90 @@ static void airoha_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
}
+static const char * const en7581_xsi_rsts_names[] = {
+ "xsi-mac",
+ "hsi0-mac",
+ "hsi1-mac",
+ "hsi-mac",
+ "xfp-mac",
+};
+
+static int airoha_en7581_get_src_port_id(struct airoha_gdm_port *port, int nbq)
+{
+ switch (port->id) {
+ case 3:
+ /* 7581 SoC supports PCIe serdes on GDM3 port */
+ if (nbq == 4)
+ return HSGMII_LAN_7581_PCIE0_SRCPORT;
+ if (nbq == 5)
+ return HSGMII_LAN_7581_PCIE1_SRCPORT;
+ break;
+ case 4:
+ /* 7581 SoC supports eth and usb serdes on GDM4 port */
+ if (!nbq)
+ return HSGMII_LAN_7581_ETH_SRCPORT;
+ if (nbq == 1)
+ return HSGMII_LAN_7581_USB_SRCPORT;
+ break;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const char * const an7583_xsi_rsts_names[] = {
+ "xsi-mac",
+ "hsi0-mac",
+ "hsi1-mac",
+ "xfp-mac",
+};
+
+static int airoha_an7583_get_src_port_id(struct airoha_gdm_port *port, int nbq)
+{
+ switch (port->id) {
+ case 3:
+ /* 7583 SoC supports eth serdes on GDM3 port */
+ if (!nbq)
+ return HSGMII_LAN_7583_ETH_SRCPORT;
+ break;
+ case 4:
+ /* 7583 SoC supports PCIe and USB serdes on GDM4 port */
+ if (!nbq)
+ return HSGMII_LAN_7583_PCIE_SRCPORT;
+ if (nbq == 1)
+ return HSGMII_LAN_7583_USB_SRCPORT;
+ break;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct airoha_eth_soc_data en7581_soc_data = {
+ .version = 0x7581,
+ .xsi_rsts_names = en7581_xsi_rsts_names,
+ .num_xsi_rsts = ARRAY_SIZE(en7581_xsi_rsts_names),
+ .num_ppe = 2,
+ .ops = {
+ .get_src_port_id = airoha_en7581_get_src_port_id,
+ },
+};
+
+static const struct airoha_eth_soc_data an7583_soc_data = {
+ .version = 0x7583,
+ .xsi_rsts_names = an7583_xsi_rsts_names,
+ .num_xsi_rsts = ARRAY_SIZE(an7583_xsi_rsts_names),
+ .num_ppe = 1,
+ .ops = {
+ .get_src_port_id = airoha_an7583_get_src_port_id,
+ },
+};
+
static const struct of_device_id of_airoha_match[] = {
- { .compatible = "airoha,en7581-eth" },
+ { .compatible = "airoha,en7581-eth", .data = &en7581_soc_data },
+ { .compatible = "airoha,an7583-eth", .data = &an7583_soc_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_airoha_match);
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index cd13c1c1224f..fbbc58133364 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -21,7 +21,6 @@
#define AIROHA_MAX_NUM_IRQ_BANKS 4
#define AIROHA_MAX_DSA_PORTS 7
#define AIROHA_MAX_NUM_RSTS 3
-#define AIROHA_MAX_NUM_XSI_RSTS 5
#define AIROHA_MAX_MTU 9216
#define AIROHA_MAX_PACKET_SIZE 2048
#define AIROHA_NUM_QOS_CHANNELS 4
@@ -48,20 +47,9 @@
#define QDMA_METER_IDX(_n) ((_n) & 0xff)
#define QDMA_METER_GROUP(_n) (((_n) >> 8) & 0x3)
-#define PPE_NUM 2
-#define PPE1_SRAM_NUM_ENTRIES (8 * 1024)
-#define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES)
-#ifdef CONFIG_NET_AIROHA_FLOW_STATS
-#define PPE1_STATS_NUM_ENTRIES (4 * 1024)
-#else
-#define PPE1_STATS_NUM_ENTRIES 0
-#endif /* CONFIG_NET_AIROHA_FLOW_STATS */
-#define PPE_STATS_NUM_ENTRIES (2 * PPE1_STATS_NUM_ENTRIES)
-#define PPE1_SRAM_NUM_DATA_ENTRIES (PPE1_SRAM_NUM_ENTRIES - PPE1_STATS_NUM_ENTRIES)
-#define PPE_SRAM_NUM_DATA_ENTRIES (2 * PPE1_SRAM_NUM_DATA_ENTRIES)
+#define PPE_SRAM_NUM_ENTRIES (8 * 1024)
+#define PPE_STATS_NUM_ENTRIES (4 * 1024)
#define PPE_DRAM_NUM_ENTRIES (16 * 1024)
-#define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES)
-#define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1)
#define PPE_ENTRY_SIZE 80
#define PPE_RAM_NUM_ENTRIES_SHIFT(_n) (__ffs((_n) >> 10))
@@ -79,10 +67,16 @@ enum {
};
enum {
- HSGMII_LAN_PCIE0_SRCPORT = 0x16,
- HSGMII_LAN_PCIE1_SRCPORT,
- HSGMII_LAN_ETH_SRCPORT,
- HSGMII_LAN_USB_SRCPORT,
+ HSGMII_LAN_7581_PCIE0_SRCPORT = 0x16,
+ HSGMII_LAN_7581_PCIE1_SRCPORT,
+ HSGMII_LAN_7581_ETH_SRCPORT,
+ HSGMII_LAN_7581_USB_SRCPORT,
+};
+
+enum {
+ HSGMII_LAN_7583_ETH_SRCPORT = 0x16,
+ HSGMII_LAN_7583_PCIE_SRCPORT = 0x18,
+ HSGMII_LAN_7583_USB_SRCPORT,
};
enum {
@@ -111,6 +105,13 @@ enum {
CRSN_25 = 0x19,
};
+enum airoha_gdm_index {
+ AIROHA_GDM1_IDX = 1,
+ AIROHA_GDM2_IDX = 2,
+ AIROHA_GDM3_IDX = 3,
+ AIROHA_GDM4_IDX = 4,
+};
+
enum {
FE_PSE_PORT_CDM1,
FE_PSE_PORT_GDM1,
@@ -168,7 +169,10 @@ enum trtcm_param {
struct airoha_queue_entry {
union {
void *buf;
- struct sk_buff *skb;
+ struct {
+ struct list_head list;
+ struct sk_buff *skb;
+ };
};
dma_addr_t dma_addr;
u16 dma_len;
@@ -192,6 +196,8 @@ struct airoha_queue {
struct napi_struct napi;
struct page_pool *page_pool;
struct sk_buff *skb;
+
+ struct list_head tx_list;
};
struct airoha_tx_irq_queue {
@@ -554,7 +560,7 @@ struct airoha_ppe {
struct rhashtable l2_flows;
struct hlist_head *foe_flow;
- u16 foe_check_time[PPE_NUM_ENTRIES];
+ u16 *foe_check_time;
struct airoha_foe_stats *foe_stats;
dma_addr_t foe_stats_dma;
@@ -562,9 +568,21 @@ struct airoha_ppe {
struct dentry *debugfs_dir;
};
+struct airoha_eth_soc_data {
+ u16 version;
+ const char * const *xsi_rsts_names;
+ int num_xsi_rsts;
+ int num_ppe;
+ struct {
+ int (*get_src_port_id)(struct airoha_gdm_port *port, int nbq);
+ } ops;
+};
+
struct airoha_eth {
struct device *dev;
+ const struct airoha_eth_soc_data *soc;
+
unsigned long state;
void __iomem *fe_regs;
@@ -574,7 +592,7 @@ struct airoha_eth {
struct rhashtable flow_table;
struct reset_control_bulk_data rsts[AIROHA_MAX_NUM_RSTS];
- struct reset_control_bulk_data xsi_rsts[AIROHA_MAX_NUM_XSI_RSTS];
+ struct reset_control_bulk_data *xsi_rsts;
struct net_device *napi_dev;
@@ -617,15 +635,27 @@ static inline bool airhoa_is_lan_gdm_port(struct airoha_gdm_port *port)
return port->id == 1;
}
+static inline bool airoha_is_7581(struct airoha_eth *eth)
+{
+ return eth->soc->version == 0x7581;
+}
+
+static inline bool airoha_is_7583(struct airoha_eth *eth)
+{
+ return eth->soc->version == 0x7583;
+}
+
bool airoha_is_valid_gdm_port(struct airoha_eth *eth,
struct airoha_gdm_port *port);
+bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index);
void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
u16 hash, bool rx_wlan);
int airoha_ppe_setup_tc_block_cb(struct airoha_ppe_dev *dev, void *type_data);
int airoha_ppe_init(struct airoha_eth *eth);
void airoha_ppe_deinit(struct airoha_eth *eth);
void airoha_ppe_init_upd_mem(struct airoha_gdm_port *port);
+u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe);
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
u32 hash);
void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash,
diff --git a/drivers/net/ethernet/airoha/airoha_npu.c b/drivers/net/ethernet/airoha/airoha_npu.c
index 8c883f2b2d36..68b7f9684dc7 100644
--- a/drivers/net/ethernet/airoha/airoha_npu.c
+++ b/drivers/net/ethernet/airoha/airoha_npu.c
@@ -16,6 +16,8 @@
#define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin"
#define NPU_EN7581_FIRMWARE_RV32 "airoha/en7581_npu_rv32.bin"
+#define NPU_AN7583_FIRMWARE_DATA "airoha/an7583_npu_data.bin"
+#define NPU_AN7583_FIRMWARE_RV32 "airoha/an7583_npu_rv32.bin"
#define NPU_EN7581_FIRMWARE_RV32_MAX_SIZE 0x200000
#define NPU_EN7581_FIRMWARE_DATA_MAX_SIZE 0x10000
#define NPU_DUMP_SIZE 512
@@ -103,6 +105,16 @@ enum {
QDMA_WAN_PON_XDSL,
};
+struct airoha_npu_fw {
+ const char *name;
+ int max_size;
+};
+
+struct airoha_npu_soc_data {
+ struct airoha_npu_fw fw_rv32;
+ struct airoha_npu_fw fw_data;
+};
+
#define MBOX_MSG_FUNC_ID GENMASK(14, 11)
#define MBOX_MSG_STATIC_BUF BIT(5)
#define MBOX_MSG_STATUS GENMASK(4, 2)
@@ -182,49 +194,53 @@ static int airoha_npu_send_msg(struct airoha_npu *npu, int func_id,
return ret;
}
-static int airoha_npu_run_firmware(struct device *dev, void __iomem *base,
- struct resource *res)
+static int airoha_npu_load_firmware(struct device *dev, void __iomem *addr,
+ const struct airoha_npu_fw *fw_info)
{
const struct firmware *fw;
- void __iomem *addr;
int ret;
- ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_RV32, dev);
+ ret = request_firmware(&fw, fw_info->name, dev);
if (ret)
return ret == -ENOENT ? -EPROBE_DEFER : ret;
- if (fw->size > NPU_EN7581_FIRMWARE_RV32_MAX_SIZE) {
+ if (fw->size > fw_info->max_size) {
dev_err(dev, "%s: fw size too overlimit (%zu)\n",
- NPU_EN7581_FIRMWARE_RV32, fw->size);
+ fw_info->name, fw->size);
ret = -E2BIG;
goto out;
}
- addr = devm_ioremap_resource(dev, res);
- if (IS_ERR(addr)) {
- ret = PTR_ERR(addr);
- goto out;
- }
-
memcpy_toio(addr, fw->data, fw->size);
+out:
release_firmware(fw);
- ret = request_firmware(&fw, NPU_EN7581_FIRMWARE_DATA, dev);
- if (ret)
- return ret == -ENOENT ? -EPROBE_DEFER : ret;
+ return ret;
+}
- if (fw->size > NPU_EN7581_FIRMWARE_DATA_MAX_SIZE) {
- dev_err(dev, "%s: fw size too overlimit (%zu)\n",
- NPU_EN7581_FIRMWARE_DATA, fw->size);
- ret = -E2BIG;
- goto out;
- }
+static int airoha_npu_run_firmware(struct device *dev, void __iomem *base,
+ struct resource *res)
+{
+ const struct airoha_npu_soc_data *soc;
+ void __iomem *addr;
+ int ret;
- memcpy_toio(base + REG_NPU_LOCAL_SRAM, fw->data, fw->size);
-out:
- release_firmware(fw);
+ soc = of_device_get_match_data(dev);
+ if (!soc)
+ return -EINVAL;
- return ret;
+ addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+
+ /* Load rv32 npu firmware */
+ ret = airoha_npu_load_firmware(dev, addr, &soc->fw_rv32);
+ if (ret)
+ return ret;
+
+ /* Load data npu firmware */
+ return airoha_npu_load_firmware(dev, base + REG_NPU_LOCAL_SRAM,
+ &soc->fw_data);
}
static irqreturn_t airoha_npu_mbox_handler(int irq, void *npu_instance)
@@ -597,8 +613,31 @@ void airoha_npu_put(struct airoha_npu *npu)
}
EXPORT_SYMBOL_GPL(airoha_npu_put);
+static const struct airoha_npu_soc_data en7581_npu_soc_data = {
+ .fw_rv32 = {
+ .name = NPU_EN7581_FIRMWARE_RV32,
+ .max_size = NPU_EN7581_FIRMWARE_RV32_MAX_SIZE,
+ },
+ .fw_data = {
+ .name = NPU_EN7581_FIRMWARE_DATA,
+ .max_size = NPU_EN7581_FIRMWARE_DATA_MAX_SIZE,
+ },
+};
+
+static const struct airoha_npu_soc_data an7583_npu_soc_data = {
+ .fw_rv32 = {
+ .name = NPU_AN7583_FIRMWARE_RV32,
+ .max_size = NPU_EN7581_FIRMWARE_RV32_MAX_SIZE,
+ },
+ .fw_data = {
+ .name = NPU_AN7583_FIRMWARE_DATA,
+ .max_size = NPU_EN7581_FIRMWARE_DATA_MAX_SIZE,
+ },
+};
+
static const struct of_device_id of_airoha_npu_match[] = {
- { .compatible = "airoha,en7581-npu" },
+ { .compatible = "airoha,en7581-npu", .data = &en7581_npu_soc_data },
+ { .compatible = "airoha,an7583-npu", .data = &an7583_npu_soc_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_airoha_npu_match);
@@ -737,6 +776,8 @@ module_platform_driver(airoha_npu_driver);
MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_DATA);
MODULE_FIRMWARE(NPU_EN7581_FIRMWARE_RV32);
+MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_DATA);
+MODULE_FIRMWARE(NPU_AN7583_FIRMWARE_RV32);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo@kernel.org>");
MODULE_DESCRIPTION("Airoha Network Processor Unit driver");
diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c
index 691361b25407..0caabb0c3aa0 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe.c
@@ -32,9 +32,50 @@ static const struct rhashtable_params airoha_l2_flow_table_params = {
.automatic_shrinking = true,
};
-static bool airoha_ppe2_is_enabled(struct airoha_eth *eth)
+static int airoha_ppe_get_num_stats_entries(struct airoha_ppe *ppe)
{
- return airoha_fe_rr(eth, REG_PPE_GLO_CFG(1)) & PPE_GLO_CFG_EN_MASK;
+ if (!IS_ENABLED(CONFIG_NET_AIROHA_FLOW_STATS))
+ return -EOPNOTSUPP;
+
+ if (airoha_is_7583(ppe->eth))
+ return -EOPNOTSUPP;
+
+ return PPE_STATS_NUM_ENTRIES;
+}
+
+static int airoha_ppe_get_total_num_stats_entries(struct airoha_ppe *ppe)
+{
+ int num_stats = airoha_ppe_get_num_stats_entries(ppe);
+
+ if (num_stats > 0) {
+ struct airoha_eth *eth = ppe->eth;
+
+ num_stats = num_stats * eth->soc->num_ppe;
+ }
+
+ return num_stats;
+}
+
+static u32 airoha_ppe_get_total_sram_num_entries(struct airoha_ppe *ppe)
+{
+ struct airoha_eth *eth = ppe->eth;
+
+ return PPE_SRAM_NUM_ENTRIES * eth->soc->num_ppe;
+}
+
+u32 airoha_ppe_get_total_num_entries(struct airoha_ppe *ppe)
+{
+ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
+
+ return sram_num_entries + PPE_DRAM_NUM_ENTRIES;
+}
+
+bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index)
+{
+ if (index >= eth->soc->num_ppe)
+ return false;
+
+ return airoha_fe_rr(eth, REG_PPE_GLO_CFG(index)) & PPE_GLO_CFG_EN_MASK;
}
static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
@@ -46,14 +87,22 @@ static u32 airoha_ppe_get_timestamp(struct airoha_ppe *ppe)
static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
{
- u32 sram_tb_size, sram_num_entries, dram_num_entries;
+ u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries;
+ u32 sram_tb_size, dram_num_entries;
struct airoha_eth *eth = ppe->eth;
- int i;
+ int i, sram_num_stats_entries;
- sram_tb_size = PPE_SRAM_NUM_ENTRIES * sizeof(struct airoha_foe_entry);
+ sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
+ sram_tb_size = sram_num_entries * sizeof(struct airoha_foe_entry);
dram_num_entries = PPE_RAM_NUM_ENTRIES_SHIFT(PPE_DRAM_NUM_ENTRIES);
- for (i = 0; i < PPE_NUM; i++) {
+ sram_num_stats_entries = airoha_ppe_get_num_stats_entries(ppe);
+ if (sram_num_stats_entries > 0)
+ sram_ppe_num_data_entries -= sram_num_stats_entries;
+ sram_ppe_num_data_entries =
+ PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries);
+
+ for (i = 0; i < eth->soc->num_ppe; i++) {
int p;
airoha_fe_wr(eth, REG_PPE_TB_BASE(i),
@@ -85,10 +134,16 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
airoha_fe_rmw(eth, REG_PPE_TB_CFG(i),
PPE_TB_CFG_SEARCH_MISS_MASK |
+ PPE_SRAM_TB_NUM_ENTRY_MASK |
+ PPE_DRAM_TB_NUM_ENTRY_MASK |
PPE_TB_CFG_KEEPALIVE_MASK |
PPE_TB_ENTRY_SIZE_MASK,
FIELD_PREP(PPE_TB_CFG_SEARCH_MISS_MASK, 3) |
- FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0));
+ FIELD_PREP(PPE_TB_ENTRY_SIZE_MASK, 0) |
+ FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
+ sram_ppe_num_data_entries) |
+ FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
+ dram_num_entries));
airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED);
@@ -101,35 +156,6 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
FIELD_PREP(FP1_EGRESS_MTU_MASK,
AIROHA_MAX_MTU));
}
-
- if (airoha_ppe2_is_enabled(eth)) {
- sram_num_entries =
- PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES);
- airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
- PPE_SRAM_TB_NUM_ENTRY_MASK |
- PPE_DRAM_TB_NUM_ENTRY_MASK,
- FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
- sram_num_entries) |
- FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
- dram_num_entries));
- airoha_fe_rmw(eth, REG_PPE_TB_CFG(1),
- PPE_SRAM_TB_NUM_ENTRY_MASK |
- PPE_DRAM_TB_NUM_ENTRY_MASK,
- FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
- sram_num_entries) |
- FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
- dram_num_entries));
- } else {
- sram_num_entries =
- PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES);
- airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
- PPE_SRAM_TB_NUM_ENTRY_MASK |
- PPE_DRAM_TB_NUM_ENTRY_MASK,
- FIELD_PREP(PPE_SRAM_TB_NUM_ENTRY_MASK,
- sram_num_entries) |
- FIELD_PREP(PPE_DRAM_TB_NUM_ENTRY_MASK,
- dram_num_entries));
- }
}
static void airoha_ppe_flow_mangle_eth(const struct flow_action_entry *act, void *eth)
@@ -282,7 +308,7 @@ static int airoha_ppe_foe_entry_prepare(struct airoha_eth *eth,
if (!airoha_is_valid_gdm_port(eth, port))
return -EINVAL;
- if (dsa_port >= 0)
+ if (dsa_port >= 0 || eth->ports[1])
pse_port = port->id == 4 ? FE_PSE_PORT_GDM4
: port->id;
else
@@ -428,9 +454,11 @@ static int airoha_ppe_foe_entry_set_ipv6_tuple(struct airoha_foe_entry *hwe,
return 0;
}
-static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe)
+static u32 airoha_ppe_foe_get_entry_hash(struct airoha_ppe *ppe,
+ struct airoha_foe_entry *hwe)
{
int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
+ u32 ppe_hash_mask = airoha_ppe_get_total_num_entries(ppe) - 1;
u32 hash, hv1, hv2, hv3;
switch (type) {
@@ -468,25 +496,31 @@ static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe)
case PPE_PKT_TYPE_IPV6_6RD:
default:
WARN_ON_ONCE(1);
- return PPE_HASH_MASK;
+ return ppe_hash_mask;
}
hash = (hv1 & hv2) | ((~hv1) & hv3);
hash = (hash >> 24) | ((hash & 0xffffff) << 8);
hash ^= hv1 ^ hv2 ^ hv3;
hash ^= hash >> 16;
- hash &= PPE_NUM_ENTRIES - 1;
+ hash &= ppe_hash_mask;
return hash;
}
-static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash)
+static int airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe,
+ u32 hash, u32 *index)
{
- if (!airoha_ppe2_is_enabled(ppe->eth))
- return hash;
+ int ppe_num_stats_entries;
- return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES
- : hash;
+ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
+ if (ppe_num_stats_entries < 0)
+ return ppe_num_stats_entries;
+
+ *index = hash >= ppe_num_stats_entries ? hash - PPE_STATS_NUM_ENTRIES
+ : hash;
+
+ return 0;
}
static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe,
@@ -500,9 +534,13 @@ static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe,
static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe,
struct airoha_npu *npu)
{
- int i;
+ int i, ppe_num_stats_entries;
+
+ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
+ if (ppe_num_stats_entries < 0)
+ return;
- for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++)
+ for (i = 0; i < ppe_num_stats_entries; i++)
airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i);
}
@@ -513,10 +551,17 @@ static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe,
{
int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
u32 index, pse_port, val, *data, *ib2, *meter;
+ int ppe_num_stats_entries;
u8 nbq;
- index = airoha_ppe_foe_get_flow_stats_index(ppe, hash);
- if (index >= PPE_STATS_NUM_ENTRIES)
+ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
+ if (ppe_num_stats_entries < 0)
+ return;
+
+ if (airoha_ppe_foe_get_flow_stats_index(ppe, hash, &index))
+ return;
+
+ if (index >= ppe_num_stats_entries)
return;
if (type == PPE_PKT_TYPE_BRIDGE) {
@@ -557,17 +602,17 @@ static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe,
static struct airoha_foe_entry *
airoha_ppe_foe_get_entry_locked(struct airoha_ppe *ppe, u32 hash)
{
+ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
+
lockdep_assert_held(&ppe_lock);
- if (hash < PPE_SRAM_NUM_ENTRIES) {
+ if (hash < sram_num_entries) {
u32 *hwe = ppe->foe + hash * sizeof(struct airoha_foe_entry);
+ bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES;
struct airoha_eth *eth = ppe->eth;
- bool ppe2;
u32 val;
int i;
- ppe2 = airoha_ppe2_is_enabled(ppe->eth) &&
- hash >= PPE1_SRAM_NUM_ENTRIES;
airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2),
FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) |
PPE_SRAM_CTRL_REQ_MASK);
@@ -577,7 +622,8 @@ airoha_ppe_foe_get_entry_locked(struct airoha_ppe *ppe, u32 hash)
REG_PPE_RAM_CTRL(ppe2)))
return NULL;
- for (i = 0; i < sizeof(struct airoha_foe_entry) / 4; i++)
+ for (i = 0; i < sizeof(struct airoha_foe_entry) / sizeof(*hwe);
+ i++)
hwe[i] = airoha_fe_rr(eth,
REG_PPE_RAM_ENTRY(ppe2, i));
}
@@ -614,10 +660,32 @@ static bool airoha_ppe_foe_compare_entry(struct airoha_flow_table_entry *e,
return !memcmp(&e->data.d, &hwe->d, len - sizeof(hwe->ib1));
}
+static int airoha_ppe_foe_commit_sram_entry(struct airoha_ppe *ppe, u32 hash)
+{
+ struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe);
+ bool ppe2 = hash >= PPE_SRAM_NUM_ENTRIES;
+ u32 *ptr = (u32 *)hwe, val;
+ int i;
+
+ for (i = 0; i < sizeof(*hwe) / sizeof(*ptr); i++)
+ airoha_fe_wr(ppe->eth, REG_PPE_RAM_ENTRY(ppe2, i), ptr[i]);
+
+ wmb();
+ airoha_fe_wr(ppe->eth, REG_PPE_RAM_CTRL(ppe2),
+ FIELD_PREP(PPE_SRAM_CTRL_ENTRY_MASK, hash) |
+ PPE_SRAM_CTRL_WR_MASK | PPE_SRAM_CTRL_REQ_MASK);
+
+ return read_poll_timeout_atomic(airoha_fe_rr, val,
+ val & PPE_SRAM_CTRL_ACK_MASK,
+ 10, 100, false, ppe->eth,
+ REG_PPE_RAM_CTRL(ppe2));
+}
+
static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe,
struct airoha_foe_entry *e,
u32 hash, bool rx_wlan)
{
+ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe);
u32 ts = airoha_ppe_get_timestamp(ppe);
struct airoha_eth *eth = ppe->eth;
@@ -642,14 +710,8 @@ static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe,
if (!rx_wlan)
airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash);
- if (hash < PPE_SRAM_NUM_ENTRIES) {
- dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe);
- bool ppe2 = airoha_ppe2_is_enabled(eth) &&
- hash >= PPE1_SRAM_NUM_ENTRIES;
-
- err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe),
- hash, ppe2);
- }
+ if (hash < sram_num_entries)
+ err = airoha_ppe_foe_commit_sram_entry(ppe, hash);
unlock:
rcu_read_unlock();
@@ -772,7 +834,7 @@ static void airoha_ppe_foe_insert_entry(struct airoha_ppe *ppe,
if (state == AIROHA_FOE_STATE_BIND)
goto unlock;
- index = airoha_ppe_foe_get_entry_hash(hwe);
+ index = airoha_ppe_foe_get_entry_hash(ppe, hwe);
hlist_for_each_entry_safe(e, n, &ppe->foe_flow[index], list) {
if (e->type == FLOW_TYPE_L2_SUBFLOW) {
state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, hwe->ib1);
@@ -832,7 +894,7 @@ static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe,
if (type == PPE_PKT_TYPE_BRIDGE)
return airoha_ppe_foe_l2_flow_commit_entry(ppe, e);
- hash = airoha_ppe_foe_get_entry_hash(&e->data);
+ hash = airoha_ppe_foe_get_entry_hash(ppe, &e->data);
e->type = FLOW_TYPE_L4;
e->hash = 0xffff;
@@ -1158,11 +1220,19 @@ static int airoha_ppe_flow_offload_destroy(struct airoha_eth *eth,
void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash,
struct airoha_foe_stats64 *stats)
{
- u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash);
struct airoha_eth *eth = ppe->eth;
+ int ppe_num_stats_entries;
struct airoha_npu *npu;
+ u32 index;
+
+ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
+ if (ppe_num_stats_entries < 0)
+ return;
- if (index >= PPE_STATS_NUM_ENTRIES)
+ if (airoha_ppe_foe_get_flow_stats_index(ppe, hash, &index))
+ return;
+
+ if (index >= ppe_num_stats_entries)
return;
rcu_read_lock();
@@ -1225,20 +1295,22 @@ static int airoha_ppe_flow_offload_cmd(struct airoha_eth *eth,
return -EOPNOTSUPP;
}
-static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe,
- struct airoha_npu *npu)
+static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe)
{
- int i, sram_num_entries = PPE_SRAM_NUM_ENTRIES;
+ u32 sram_num_entries = airoha_ppe_get_total_sram_num_entries(ppe);
struct airoha_foe_entry *hwe = ppe->foe;
+ int i, err = 0;
- if (airoha_ppe2_is_enabled(ppe->eth))
- sram_num_entries = sram_num_entries / 2;
+ for (i = 0; i < sram_num_entries; i++) {
+ int err;
- for (i = 0; i < sram_num_entries; i++)
memset(&hwe[i], 0, sizeof(*hwe));
+ err = airoha_ppe_foe_commit_sram_entry(ppe, i);
+ if (err)
+ break;
+ }
- return npu->ops.ppe_flush_sram_entries(npu, ppe->foe_dma,
- PPE_SRAM_NUM_ENTRIES);
+ return err;
}
static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth)
@@ -1257,7 +1329,7 @@ static int airoha_ppe_offload_setup(struct airoha_eth *eth)
{
struct airoha_npu *npu = airoha_ppe_npu_get(eth);
struct airoha_ppe *ppe = eth->ppe;
- int err;
+ int err, ppe_num_stats_entries;
if (IS_ERR(npu))
return PTR_ERR(npu);
@@ -1266,18 +1338,15 @@ static int airoha_ppe_offload_setup(struct airoha_eth *eth)
if (err)
goto error_npu_put;
- if (PPE_STATS_NUM_ENTRIES) {
+ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
+ if (ppe_num_stats_entries > 0) {
err = npu->ops.ppe_init_stats(npu, ppe->foe_stats_dma,
- PPE_STATS_NUM_ENTRIES);
+ ppe_num_stats_entries);
if (err)
goto error_npu_put;
}
airoha_ppe_hw_init(ppe);
- err = airoha_ppe_flush_sram_entries(ppe, npu);
- if (err)
- goto error_npu_put;
-
airoha_ppe_foe_flow_stats_reset(ppe, npu);
rcu_assign_pointer(eth->npu, npu);
@@ -1313,9 +1382,10 @@ void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb,
u16 hash, bool rx_wlan)
{
struct airoha_ppe *ppe = dev->priv;
+ u32 ppe_hash_mask = airoha_ppe_get_total_num_entries(ppe) - 1;
u16 now, diff;
- if (hash > PPE_HASH_MASK)
+ if (hash > ppe_hash_mask)
return;
now = (u16)jiffies;
@@ -1405,8 +1475,9 @@ EXPORT_SYMBOL_GPL(airoha_ppe_put_dev);
int airoha_ppe_init(struct airoha_eth *eth)
{
+ int foe_size, err, ppe_num_stats_entries;
+ u32 ppe_num_entries;
struct airoha_ppe *ppe;
- int foe_size, err;
ppe = devm_kzalloc(eth->dev, sizeof(*ppe), GFP_KERNEL);
if (!ppe)
@@ -1415,24 +1486,25 @@ int airoha_ppe_init(struct airoha_eth *eth)
ppe->dev.ops.setup_tc_block_cb = airoha_ppe_setup_tc_block_cb;
ppe->dev.ops.check_skb = airoha_ppe_check_skb;
ppe->dev.priv = ppe;
+ ppe->eth = eth;
+ eth->ppe = ppe;
- foe_size = PPE_NUM_ENTRIES * sizeof(struct airoha_foe_entry);
+ ppe_num_entries = airoha_ppe_get_total_num_entries(ppe);
+ foe_size = ppe_num_entries * sizeof(struct airoha_foe_entry);
ppe->foe = dmam_alloc_coherent(eth->dev, foe_size, &ppe->foe_dma,
GFP_KERNEL);
if (!ppe->foe)
return -ENOMEM;
- ppe->eth = eth;
- eth->ppe = ppe;
-
ppe->foe_flow = devm_kzalloc(eth->dev,
- PPE_NUM_ENTRIES * sizeof(*ppe->foe_flow),
+ ppe_num_entries * sizeof(*ppe->foe_flow),
GFP_KERNEL);
if (!ppe->foe_flow)
return -ENOMEM;
- foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats);
- if (foe_size) {
+ ppe_num_stats_entries = airoha_ppe_get_total_num_stats_entries(ppe);
+ if (ppe_num_stats_entries > 0) {
+ foe_size = ppe_num_stats_entries * sizeof(*ppe->foe_stats);
ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size,
&ppe->foe_stats_dma,
GFP_KERNEL);
@@ -1440,6 +1512,15 @@ int airoha_ppe_init(struct airoha_eth *eth)
return -ENOMEM;
}
+ ppe->foe_check_time = devm_kzalloc(eth->dev, ppe_num_entries,
+ GFP_KERNEL);
+ if (!ppe->foe_check_time)
+ return -ENOMEM;
+
+ err = airoha_ppe_flush_sram_entries(ppe);
+ if (err)
+ return err;
+
err = rhashtable_init(&eth->flow_table, &airoha_flow_table_params);
if (err)
return err;
diff --git a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
index 05a756233f6a..0112c41150bb 100644
--- a/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
+++ b/drivers/net/ethernet/airoha/airoha_ppe_debugfs.c
@@ -53,9 +53,10 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private,
[AIROHA_FOE_STATE_FIN] = "FIN",
};
struct airoha_ppe *ppe = m->private;
+ u32 ppe_num_entries = airoha_ppe_get_total_num_entries(ppe);
int i;
- for (i = 0; i < PPE_NUM_ENTRIES; i++) {
+ for (i = 0; i < ppe_num_entries; i++) {
const char *state_str, *type_str = "UNKNOWN";
void *src_addr = NULL, *dest_addr = NULL;
u16 *src_port = NULL, *dest_port = NULL;
diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h
index 69c5a143db8c..ed4e3407f4a0 100644
--- a/drivers/net/ethernet/airoha/airoha_regs.h
+++ b/drivers/net/ethernet/airoha/airoha_regs.h
@@ -23,6 +23,8 @@
#define GDM3_BASE 0x1100
#define GDM4_BASE 0x2500
+#define CDM_BASE(_n) \
+ ((_n) == 2 ? CDM2_BASE : CDM1_BASE)
#define GDM_BASE(_n) \
((_n) == 4 ? GDM4_BASE : \
(_n) == 3 ? GDM3_BASE : \
@@ -109,30 +111,24 @@
#define PATN_DP_MASK GENMASK(31, 16)
#define PATN_SP_MASK GENMASK(15, 0)
-#define REG_CDM1_VLAN_CTRL CDM1_BASE
-#define CDM1_VLAN_MASK GENMASK(31, 16)
+#define REG_CDM_VLAN_CTRL(_n) CDM_BASE(_n)
+#define CDM_VLAN_MASK GENMASK(31, 16)
-#define REG_CDM1_FWD_CFG (CDM1_BASE + 0x08)
-#define CDM1_VIP_QSEL_MASK GENMASK(24, 20)
+#define REG_CDM_FWD_CFG(_n) (CDM_BASE(_n) + 0x08)
+#define CDM_OAM_QSEL_MASK GENMASK(31, 27)
+#define CDM_VIP_QSEL_MASK GENMASK(24, 20)
-#define REG_CDM1_CRSN_QSEL(_n) (CDM1_BASE + 0x10 + ((_n) << 2))
-#define CDM1_CRSN_QSEL_REASON_MASK(_n) \
- GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3))
-
-#define REG_CDM2_FWD_CFG (CDM2_BASE + 0x08)
-#define CDM2_OAM_QSEL_MASK GENMASK(31, 27)
-#define CDM2_VIP_QSEL_MASK GENMASK(24, 20)
-
-#define REG_CDM2_CRSN_QSEL(_n) (CDM2_BASE + 0x10 + ((_n) << 2))
-#define CDM2_CRSN_QSEL_REASON_MASK(_n) \
+#define REG_CDM_CRSN_QSEL(_n, _m) (CDM_BASE(_n) + 0x10 + ((_m) << 2))
+#define CDM_CRSN_QSEL_REASON_MASK(_n) \
GENMASK(4 + (((_n) % 4) << 3), (((_n) % 4) << 3))
#define REG_GDM_FWD_CFG(_n) GDM_BASE(_n)
-#define GDM_DROP_CRC_ERR BIT(23)
-#define GDM_IP4_CKSUM BIT(22)
-#define GDM_TCP_CKSUM BIT(21)
-#define GDM_UDP_CKSUM BIT(20)
-#define GDM_STRIP_CRC BIT(16)
+#define GDM_PAD_EN_MASK BIT(28)
+#define GDM_DROP_CRC_ERR_MASK BIT(23)
+#define GDM_IP4_CKSUM_MASK BIT(22)
+#define GDM_TCP_CKSUM_MASK BIT(21)
+#define GDM_UDP_CKSUM_MASK BIT(20)
+#define GDM_STRIP_CRC_MASK BIT(16)
#define GDM_UCFQ_MASK GENMASK(15, 12)
#define GDM_BCFQ_MASK GENMASK(11, 8)
#define GDM_MCFQ_MASK GENMASK(7, 4)
@@ -156,6 +152,10 @@
#define LBK_CHAN_MODE_MASK BIT(1)
#define LPBK_EN_MASK BIT(0)
+#define REG_GDM_CHN_RLS(_n) (GDM_BASE(_n) + 0x20)
+#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25)
+#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17)
+
#define REG_GDM_TXCHN_EN(_n) (GDM_BASE(_n) + 0x24)
#define REG_GDM_RXCHN_EN(_n) (GDM_BASE(_n) + 0x28)
@@ -168,10 +168,10 @@
#define FE_GDM_MIB_RX_CLEAR_MASK BIT(1)
#define FE_GDM_MIB_TX_CLEAR_MASK BIT(0)
-#define REG_FE_GDM1_MIB_CFG (GDM1_BASE + 0xf4)
+#define REG_FE_GDM_MIB_CFG(_n) (GDM_BASE(_n) + 0xf4)
#define FE_STRICT_RFC2819_MODE_MASK BIT(31)
-#define FE_GDM1_TX_MIB_SPLIT_EN_MASK BIT(17)
-#define FE_GDM1_RX_MIB_SPLIT_EN_MASK BIT(16)
+#define FE_GDM_TX_MIB_SPLIT_EN_MASK BIT(17)
+#define FE_GDM_RX_MIB_SPLIT_EN_MASK BIT(16)
#define FE_TX_MIB_ID_MASK GENMASK(15, 8)
#define FE_RX_MIB_ID_MASK GENMASK(7, 0)
@@ -214,6 +214,33 @@
#define REG_FE_GDM_RX_ETH_L511_CNT_L(_n) (GDM_BASE(_n) + 0x198)
#define REG_FE_GDM_RX_ETH_L1023_CNT_L(_n) (GDM_BASE(_n) + 0x19c)
+#define REG_GDM_SRC_PORT_SET(_n) (GDM_BASE(_n) + 0x23c)
+#define GDM_SPORT_OFF2_MASK GENMASK(19, 16)
+#define GDM_SPORT_OFF1_MASK GENMASK(15, 12)
+#define GDM_SPORT_OFF0_MASK GENMASK(11, 8)
+
+#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280)
+#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284)
+#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288)
+#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c)
+
+#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290)
+#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294)
+#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298)
+#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c)
+#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8)
+#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc)
+#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0)
+#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4)
+#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8)
+#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc)
+#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8)
+#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec)
+#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0)
+#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4)
+#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8)
+#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc)
+
#define REG_PPE_GLO_CFG(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x200)
#define PPE_GLO_CFG_BUSY_MASK BIT(31)
#define PPE_GLO_CFG_FLOW_DROP_UPDATE_MASK BIT(9)
@@ -326,44 +353,6 @@
#define REG_UPDMEM_DATA(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x374)
-#define REG_FE_GDM_TX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x280)
-#define REG_FE_GDM_TX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x284)
-#define REG_FE_GDM_TX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x288)
-#define REG_FE_GDM_TX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x28c)
-
-#define REG_FE_GDM_RX_OK_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x290)
-#define REG_FE_GDM_RX_OK_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x294)
-#define REG_FE_GDM_RX_ETH_PKT_CNT_H(_n) (GDM_BASE(_n) + 0x298)
-#define REG_FE_GDM_RX_ETH_BYTE_CNT_H(_n) (GDM_BASE(_n) + 0x29c)
-#define REG_FE_GDM_TX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2b8)
-#define REG_FE_GDM_TX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2bc)
-#define REG_FE_GDM_TX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2c0)
-#define REG_FE_GDM_TX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2c4)
-#define REG_FE_GDM_TX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2c8)
-#define REG_FE_GDM_TX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2cc)
-#define REG_FE_GDM_RX_ETH_E64_CNT_H(_n) (GDM_BASE(_n) + 0x2e8)
-#define REG_FE_GDM_RX_ETH_L64_CNT_H(_n) (GDM_BASE(_n) + 0x2ec)
-#define REG_FE_GDM_RX_ETH_L127_CNT_H(_n) (GDM_BASE(_n) + 0x2f0)
-#define REG_FE_GDM_RX_ETH_L255_CNT_H(_n) (GDM_BASE(_n) + 0x2f4)
-#define REG_FE_GDM_RX_ETH_L511_CNT_H(_n) (GDM_BASE(_n) + 0x2f8)
-#define REG_FE_GDM_RX_ETH_L1023_CNT_H(_n) (GDM_BASE(_n) + 0x2fc)
-
-#define REG_GDM2_CHN_RLS (GDM2_BASE + 0x20)
-#define MBI_RX_AGE_SEL_MASK GENMASK(26, 25)
-#define MBI_TX_AGE_SEL_MASK GENMASK(18, 17)
-
-#define REG_GDM3_FWD_CFG GDM3_BASE
-#define GDM3_PAD_EN_MASK BIT(28)
-
-#define REG_GDM4_FWD_CFG GDM4_BASE
-#define GDM4_PAD_EN_MASK BIT(28)
-#define GDM4_SPORT_OFFSET0_MASK GENMASK(11, 8)
-
-#define REG_GDM4_SRC_PORT_SET (GDM4_BASE + 0x23c)
-#define GDM4_SPORT_OFF2_MASK GENMASK(19, 16)
-#define GDM4_SPORT_OFF1_MASK GENMASK(15, 12)
-#define GDM4_SPORT_OFF0_MASK GENMASK(11, 8)
-
#define REG_IP_FRAG_FP 0x2010
#define IP_ASSEMBLE_PORT_MASK GENMASK(24, 21)
#define IP_ASSEMBLE_NBQ_MASK GENMASK(20, 16)
@@ -383,10 +372,8 @@
#define REG_MC_VLAN_DATA 0x2108
#define REG_SP_DFT_CPORT(_n) (0x20e0 + ((_n) << 2))
-#define SP_CPORT_PCIE1_MASK GENMASK(31, 28)
-#define SP_CPORT_PCIE0_MASK GENMASK(27, 24)
-#define SP_CPORT_USB_MASK GENMASK(7, 4)
-#define SP_CPORT_ETH_MASK GENMASK(7, 4)
+#define SP_CPORT_DFT_MASK GENMASK(2, 0)
+#define SP_CPORT_MASK(_n) GENMASK(3 + ((_n) << 2), ((_n) << 2))
#define REG_SRC_PORT_FC_MAP6 0x2298
#define FC_ID_OF_SRC_PORT27_MASK GENMASK(28, 24)
diff --git a/drivers/net/ethernet/altera/altera_tse.h b/drivers/net/ethernet/altera/altera_tse.h
index 82f2363a45cd..e5a56bb989da 100644
--- a/drivers/net/ethernet/altera/altera_tse.h
+++ b/drivers/net/ethernet/altera/altera_tse.h
@@ -401,9 +401,6 @@ struct altera_tse_private {
/* MAC address space */
struct altera_tse_mac __iomem *mac_dev;
- /* TSE Revision */
- u32 revision;
-
/* mSGDMA Rx Dispatcher address space */
void __iomem *rx_dma_csr;
void __iomem *rx_dma_desc;
diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c
index 3f6204de9e6b..ca55c5fd11df 100644
--- a/drivers/net/ethernet/altera/altera_tse_main.c
+++ b/drivers/net/ethernet/altera/altera_tse_main.c
@@ -892,9 +892,6 @@ static int tse_open(struct net_device *dev)
netdev_warn(dev, "device MAC address %pM\n",
dev->dev_addr);
- if ((priv->revision < 0xd00) || (priv->revision > 0xe00))
- netdev_warn(dev, "TSE revision %x\n", priv->revision);
-
spin_lock(&priv->mac_cfg_lock);
ret = reset_mac(priv);
@@ -1142,6 +1139,7 @@ static int altera_tse_probe(struct platform_device *pdev)
struct net_device *ndev;
void __iomem *descmap;
int ret = -ENODEV;
+ u32 revision;
ndev = alloc_etherdev(sizeof(struct altera_tse_private));
if (!ndev) {
@@ -1150,6 +1148,7 @@ static int altera_tse_probe(struct platform_device *pdev)
}
SET_NETDEV_DEV(ndev, &pdev->dev);
+ platform_set_drvdata(pdev, ndev);
priv = netdev_priv(ndev);
priv->device = &pdev->dev;
@@ -1387,25 +1386,7 @@ static int altera_tse_probe(struct platform_device *pdev)
spin_lock_init(&priv->tx_lock);
spin_lock_init(&priv->rxdma_irq_lock);
- netif_carrier_off(ndev);
- ret = register_netdev(ndev);
- if (ret) {
- dev_err(&pdev->dev, "failed to register TSE net device\n");
- goto err_register_netdev;
- }
-
- platform_set_drvdata(pdev, ndev);
-
- priv->revision = ioread32(&priv->mac_dev->megacore_revision);
-
- if (netif_msg_probe(priv))
- dev_info(&pdev->dev, "Altera TSE MAC version %d.%d at 0x%08lx irq %d/%d\n",
- (priv->revision >> 8) & 0xff,
- priv->revision & 0xff,
- (unsigned long) control_port->start, priv->rx_irq,
- priv->tx_irq);
-
- snprintf(mrc.name, MII_BUS_ID_SIZE, "%s-pcs-mii", ndev->name);
+ snprintf(mrc.name, MII_BUS_ID_SIZE, "%s-pcs-mii", dev_name(&pdev->dev));
pcs_bus = devm_mdio_regmap_register(&pdev->dev, &mrc);
if (IS_ERR(pcs_bus)) {
ret = PTR_ERR(pcs_bus);
@@ -1442,12 +1423,30 @@ static int altera_tse_probe(struct platform_device *pdev)
goto err_init_phylink;
}
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register TSE net device\n");
+ goto err_register_netdev;
+ }
+
+ revision = ioread32(&priv->mac_dev->megacore_revision);
+
+ if (revision < 0xd00 || revision > 0xe00)
+ netdev_warn(ndev, "TSE revision %x\n", revision);
+
+ if (netif_msg_probe(priv))
+ dev_info(&pdev->dev, "Altera TSE MAC version %d.%d at 0x%08lx irq %d/%d\n",
+ (revision >> 8) & 0xff, revision & 0xff,
+ (unsigned long)control_port->start, priv->rx_irq,
+ priv->tx_irq);
+
return 0;
+
+err_register_netdev:
+ phylink_destroy(priv->phylink);
err_init_phylink:
lynx_pcs_destroy(priv->pcs);
err_init_pcs:
- unregister_netdev(ndev);
-err_register_netdev:
netif_napi_del(&priv->napi);
altera_tse_mdio_destroy(ndev);
err_free_netdev:
diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index b39c6f3e1eda..d54dca3074eb 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -165,6 +165,7 @@ config AMD_XGBE
select CRC32
select PHYLIB
select AMD_XGBE_HAVE_ECC if X86
+ select NET_SELFTESTS
help
This driver supports the AMD 10GbE Ethernet device found on an
AMD SoC.
diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h
index 0b53a1fab46d..4a6b35c84dab 100644
--- a/drivers/net/ethernet/amd/pds_core/core.h
+++ b/drivers/net/ethernet/amd/pds_core/core.h
@@ -255,7 +255,8 @@ int pdsc_dl_flash_update(struct devlink *dl,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack);
int pdsc_dl_enable_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx);
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack);
int pdsc_dl_enable_set(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx,
struct netlink_ext_ack *extack);
diff --git a/drivers/net/ethernet/amd/pds_core/devlink.c b/drivers/net/ethernet/amd/pds_core/devlink.c
index d8dc39da4161..b576be626a29 100644
--- a/drivers/net/ethernet/amd/pds_core/devlink.c
+++ b/drivers/net/ethernet/amd/pds_core/devlink.c
@@ -22,7 +22,8 @@ pdsc_viftype *pdsc_dl_find_viftype_by_id(struct pdsc *pdsc,
}
int pdsc_dl_enable_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct pdsc *pdsc = devlink_priv(dl);
struct pdsc_viftype *vt_entry;
diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile
index 980e27652237..5992f7fd4d9b 100644
--- a/drivers/net/ethernet/amd/xgbe/Makefile
+++ b/drivers/net/ethernet/amd/xgbe/Makefile
@@ -5,7 +5,7 @@ amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \
xgbe-hwtstamp.o xgbe-ptp.o xgbe-pps.o \
xgbe-i2c.o xgbe-phy-v1.o xgbe-phy-v2.o \
- xgbe-platform.o
+ xgbe-platform.o xgbe-selftest.o
amd-xgbe-$(CONFIG_PCI) += xgbe-pci.o
amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
index e5391a2eca51..b646ae575e6a 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
@@ -211,6 +211,7 @@ static void xgbe_config_sph_mode(struct xgbe_prv_data *pdata)
}
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, HDSMS, XGBE_SPH_HDSMS_SIZE);
+ pdata->sph = true;
}
static void xgbe_disable_sph_mode(struct xgbe_prv_data *pdata)
@@ -223,6 +224,7 @@ static void xgbe_disable_sph_mode(struct xgbe_prv_data *pdata)
XGMAC_DMA_IOWRITE_BITS(pdata->channel[i], DMA_CH_CR, SPH, 0);
}
+ pdata->sph = false;
}
static int xgbe_write_rss_reg(struct xgbe_prv_data *pdata, unsigned int type,
@@ -3578,3 +3580,20 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
DBGPR("<--xgbe_init_function_ptrs\n");
}
+
+int xgbe_enable_mac_loopback(struct xgbe_prv_data *pdata)
+{
+ /* Enable MAC loopback mode */
+ XGMAC_IOWRITE_BITS(pdata, MAC_RCR, LM, 1);
+
+ /* Wait for loopback to stabilize */
+ usleep_range(10, 15);
+
+ return 0;
+}
+
+void xgbe_disable_mac_loopback(struct xgbe_prv_data *pdata)
+{
+ /* Disable MAC loopback mode */
+ XGMAC_IOWRITE_BITS(pdata, MAC_RCR, LM, 0);
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 4dc631af7933..3ddd896d6987 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -367,10 +367,11 @@ static irqreturn_t xgbe_ecc_isr(int irq, void *data)
static void xgbe_isr_bh_work(struct work_struct *work)
{
struct xgbe_prv_data *pdata = from_work(pdata, work, dev_bh_work);
+ unsigned int mac_isr, mac_tssr, mac_mdioisr;
struct xgbe_hw_if *hw_if = &pdata->hw_if;
- struct xgbe_channel *channel;
+ bool per_ch_irq, ti, ri, rbu, fbe;
unsigned int dma_isr, dma_ch_isr;
- unsigned int mac_isr, mac_tssr, mac_mdioisr;
+ struct xgbe_channel *channel;
unsigned int i;
/* The DMA interrupt status register also reports MAC and MTL
@@ -384,43 +385,73 @@ static void xgbe_isr_bh_work(struct work_struct *work)
netif_dbg(pdata, intr, pdata->netdev, "DMA_ISR=%#010x\n", dma_isr);
for (i = 0; i < pdata->channel_count; i++) {
+ bool schedule_napi = false;
+ struct napi_struct *napi;
+
if (!(dma_isr & (1 << i)))
continue;
channel = pdata->channel[i];
dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR);
+
+ /* Precompute flags once */
+ ti = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI);
+ ri = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI);
+ rbu = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU);
+ fbe = !!XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE);
+
netif_dbg(pdata, intr, pdata->netdev, "DMA_CH%u_ISR=%#010x\n",
i, dma_ch_isr);
- /* The TI or RI interrupt bits may still be set even if using
- * per channel DMA interrupts. Check to be sure those are not
- * enabled before using the private data napi structure.
+ per_ch_irq = pdata->per_channel_irq;
+
+ /*
+ * Decide which NAPI to use and whether to schedule:
+ * - When not using per-channel IRQs: schedule on global NAPI
+ * if TI or RI are set.
+ * - RBU should also trigger NAPI (either per-channel or global)
+ * to allow refill.
*/
- if (!pdata->per_channel_irq &&
- (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI) ||
- XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI))) {
- if (napi_schedule_prep(&pdata->napi)) {
- /* Disable Tx and Rx interrupts */
- xgbe_disable_rx_tx_ints(pdata);
+ if (!per_ch_irq && (ti || ri))
+ schedule_napi = true;
+
+ if (rbu) {
+ schedule_napi = true;
+ pdata->ext_stats.rx_buffer_unavailable++;
+ }
+
+ napi = per_ch_irq ? &channel->napi : &pdata->napi;
- /* Turn on polling */
- __napi_schedule(&pdata->napi);
+ if (schedule_napi && napi_schedule_prep(napi)) {
+ /* Disable interrupts appropriately before polling */
+ if (per_ch_irq) {
+ if (pdata->channel_irq_mode)
+ xgbe_disable_rx_tx_int(pdata, channel);
+ else
+ disable_irq_nosync(channel->dma_irq);
+ } else {
+ xgbe_disable_rx_tx_ints(pdata);
}
+
+ /* Turn on polling */
+ __napi_schedule(napi);
} else {
- /* Don't clear Rx/Tx status if doing per channel DMA
- * interrupts, these will be cleared by the ISR for
- * per channel DMA interrupts.
+ /*
+ * Don't clear Rx/Tx status if doing per-channel DMA
+ * interrupts; those bits will be serviced/cleared by
+ * the per-channel ISR/NAPI. In non-per-channel mode
+ * when we're not scheduling NAPI here, ensure we don't
+ * accidentally clear TI/RI in HW: zero them in the
+ * local copy so that the eventual write-back does not
+ * clear TI/RI.
*/
XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, TI, 0);
XGMAC_SET_BITS(dma_ch_isr, DMA_CH_SR, RI, 0);
}
- if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RBU))
- pdata->ext_stats.rx_buffer_unavailable++;
-
/* Restart the device on a Fatal Bus Error */
- if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, FBE))
+ if (fbe)
schedule_work(&pdata->restart_work);
/* Clear interrupt signals */
@@ -1259,6 +1290,11 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
udp_tunnel_nic_reset_ntf(netdev);
+ /* Reset the phy settings */
+ ret = xgbe_phy_reset(pdata);
+ if (ret)
+ goto err_txrx;
+
netif_tx_start_all_queues(netdev);
xgbe_start_timers(pdata);
@@ -1268,6 +1304,10 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
return 0;
+err_txrx:
+ hw_if->disable_rx(pdata);
+ hw_if->disable_tx(pdata);
+
err_irqs:
xgbe_free_irqs(pdata);
@@ -1574,11 +1614,6 @@ static int xgbe_open(struct net_device *netdev)
goto err_dev_wq;
}
- /* Reset the phy settings */
- ret = xgbe_phy_reset(pdata);
- if (ret)
- goto err_an_wq;
-
/* Enable the clocks */
ret = clk_prepare_enable(pdata->sysclk);
if (ret) {
@@ -1754,27 +1789,6 @@ static int xgbe_set_mac_address(struct net_device *netdev, void *addr)
return 0;
}
-static int xgbe_ioctl(struct net_device *netdev, struct ifreq *ifreq, int cmd)
-{
- struct xgbe_prv_data *pdata = netdev_priv(netdev);
- int ret;
-
- switch (cmd) {
- case SIOCGHWTSTAMP:
- ret = xgbe_get_hwtstamp_settings(pdata, ifreq);
- break;
-
- case SIOCSHWTSTAMP:
- ret = xgbe_set_hwtstamp_settings(pdata, ifreq);
- break;
-
- default:
- ret = -EOPNOTSUPP;
- }
-
- return ret;
-}
-
static int xgbe_change_mtu(struct net_device *netdev, int mtu)
{
struct xgbe_prv_data *pdata = netdev_priv(netdev);
@@ -2020,7 +2034,6 @@ static const struct net_device_ops xgbe_netdev_ops = {
.ndo_set_rx_mode = xgbe_set_rx_mode,
.ndo_set_mac_address = xgbe_set_mac_address,
.ndo_validate_addr = eth_validate_addr,
- .ndo_eth_ioctl = xgbe_ioctl,
.ndo_change_mtu = xgbe_change_mtu,
.ndo_tx_timeout = xgbe_tx_timeout,
.ndo_get_stats64 = xgbe_get_stats64,
@@ -2033,6 +2046,8 @@ static const struct net_device_ops xgbe_netdev_ops = {
.ndo_fix_features = xgbe_fix_features,
.ndo_set_features = xgbe_set_features,
.ndo_features_check = xgbe_features_check,
+ .ndo_hwtstamp_get = xgbe_get_hwtstamp_settings,
+ .ndo_hwtstamp_set = xgbe_set_hwtstamp_settings,
};
const struct net_device_ops *xgbe_get_netdev_ops(void)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
index b6e1b67a2d0e..0d19b09497a0 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
@@ -85,6 +85,9 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
int i;
switch (stringset) {
+ case ETH_SS_TEST:
+ xgbe_selftest_get_strings(pdata, data);
+ break;
case ETH_SS_STATS:
for (i = 0; i < XGBE_STATS_COUNT; i++)
ethtool_puts(&data, xgbe_gstring_stats[i].stat_string);
@@ -131,6 +134,9 @@ static int xgbe_get_sset_count(struct net_device *netdev, int stringset)
int ret;
switch (stringset) {
+ case ETH_SS_TEST:
+ ret = xgbe_selftest_get_count(pdata);
+ break;
case ETH_SS_STATS:
ret = XGBE_STATS_COUNT +
(pdata->tx_ring_count * 2) +
@@ -760,6 +766,7 @@ static const struct ethtool_ops xgbe_ethtool_ops = {
.set_ringparam = xgbe_set_ringparam,
.get_channels = xgbe_get_channels,
.set_channels = xgbe_set_channels,
+ .self_test = xgbe_selftest_run,
};
const struct ethtool_ops *xgbe_get_ethtool_ops(void)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-hwtstamp.c b/drivers/net/ethernet/amd/xgbe/xgbe-hwtstamp.c
index bc52e5ec6420..0127988e10be 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-hwtstamp.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-hwtstamp.c
@@ -157,26 +157,24 @@ unlock:
spin_unlock_irqrestore(&pdata->tstamp_lock, flags);
}
-int xgbe_get_hwtstamp_settings(struct xgbe_prv_data *pdata, struct ifreq *ifreq)
+int xgbe_get_hwtstamp_settings(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
- if (copy_to_user(ifreq->ifr_data, &pdata->tstamp_config,
- sizeof(pdata->tstamp_config)))
- return -EFAULT;
+ struct xgbe_prv_data *pdata = netdev_priv(netdev);
+
+ *config = pdata->tstamp_config;
return 0;
}
-int xgbe_set_hwtstamp_settings(struct xgbe_prv_data *pdata, struct ifreq *ifreq)
+int xgbe_set_hwtstamp_settings(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config config;
- unsigned int mac_tscr;
-
- if (copy_from_user(&config, ifreq->ifr_data, sizeof(config)))
- return -EFAULT;
-
- mac_tscr = 0;
+ struct xgbe_prv_data *pdata = netdev_priv(netdev);
+ unsigned int mac_tscr = 0;
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
break;
@@ -188,7 +186,7 @@ int xgbe_set_hwtstamp_settings(struct xgbe_prv_data *pdata, struct ifreq *ifreq)
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
@@ -290,7 +288,7 @@ int xgbe_set_hwtstamp_settings(struct xgbe_prv_data *pdata, struct ifreq *ifreq)
xgbe_config_tstamp(pdata, mac_tscr);
- memcpy(&pdata->tstamp_config, &config, sizeof(config));
+ pdata->tstamp_config = *config;
return 0;
}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
index a56efc1bee33..a68757e8fd22 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
@@ -668,7 +668,7 @@ static int xgbe_phy_mii_read_c45(struct mii_bus *mii, int addr, int devad,
else if (phy_data->conn_type & XGBE_CONN_TYPE_MDIO)
ret = xgbe_phy_mdio_mii_read_c45(pdata, addr, devad, reg);
else
- ret = -ENOTSUPP;
+ ret = -EOPNOTSUPP;
xgbe_phy_put_comm_ownership(pdata);
@@ -989,6 +989,7 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata)
return ret;
}
phy_data->phydev = phydev;
+ phy_data->phydev->mac_managed_pm = true;
xgbe_phy_external_phy_quirks(pdata);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c
new file mode 100644
index 000000000000..55e5e467facd
--- /dev/null
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-selftest.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
+/*
+ * Copyright (c) 2014-2025, Advanced Micro Devices, Inc.
+ * Copyright (c) 2014, Synopsys, Inc.
+ * All rights reserved
+ *
+ * Author: Raju Rangoju <Raju.Rangoju@amd.com>
+ */
+#include <linux/crc32.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <net/checksum.h>
+#include <net/selftests.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+#define XGBE_LOOPBACK_NONE 0
+#define XGBE_LOOPBACK_MAC 1
+#define XGBE_LOOPBACK_PHY 2
+
+struct xgbe_test {
+ char name[ETH_GSTRING_LEN];
+ int lb;
+ int (*fn)(struct xgbe_prv_data *pdata);
+};
+
+static u8 xgbe_test_id;
+
+static int xgbe_test_loopback_validate(struct sk_buff *skb,
+ struct net_device *ndev,
+ struct packet_type *pt,
+ struct net_device *orig_ndev)
+{
+ struct net_test_priv *tdata = pt->af_packet_priv;
+ const unsigned char *dst = tdata->packet->dst;
+ const unsigned char *src = tdata->packet->src;
+ struct netsfhdr *hdr;
+ struct ethhdr *eh;
+ struct tcphdr *th;
+ struct udphdr *uh;
+ struct iphdr *ih;
+ int eat;
+
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+
+ eat = (skb->tail + skb->data_len) - skb->end;
+ if (eat > 0 && skb_shared(skb)) {
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ goto out;
+ }
+
+ if (skb_linearize(skb))
+ goto out;
+
+ if (skb_headlen(skb) < (NET_TEST_PKT_SIZE - ETH_HLEN))
+ goto out;
+
+ eh = (struct ethhdr *)skb_mac_header(skb);
+ if (dst) {
+ if (!ether_addr_equal_unaligned(eh->h_dest, dst))
+ goto out;
+ }
+ if (src) {
+ if (!ether_addr_equal_unaligned(eh->h_source, src))
+ goto out;
+ }
+
+ ih = ip_hdr(skb);
+
+ if (tdata->packet->tcp) {
+ if (ih->protocol != IPPROTO_TCP)
+ goto out;
+
+ th = (struct tcphdr *)((u8 *)ih + 4 * ih->ihl);
+ if (th->dest != htons(tdata->packet->dport))
+ goto out;
+
+ hdr = (struct netsfhdr *)((u8 *)th + sizeof(*th));
+ } else {
+ if (ih->protocol != IPPROTO_UDP)
+ goto out;
+
+ uh = (struct udphdr *)((u8 *)ih + 4 * ih->ihl);
+ if (uh->dest != htons(tdata->packet->dport))
+ goto out;
+
+ hdr = (struct netsfhdr *)((u8 *)uh + sizeof(*uh));
+ }
+
+ if (hdr->magic != cpu_to_be64(NET_TEST_PKT_MAGIC))
+ goto out;
+ if (tdata->packet->id != hdr->id)
+ goto out;
+
+ tdata->ok = true;
+ complete(&tdata->comp);
+out:
+ kfree_skb(skb);
+ return 0;
+}
+
+static int __xgbe_test_loopback(struct xgbe_prv_data *pdata,
+ struct net_packet_attrs *attr)
+{
+ struct net_test_priv *tdata;
+ struct sk_buff *skb = NULL;
+ int ret = 0;
+
+ tdata = kzalloc(sizeof(*tdata), GFP_KERNEL);
+ if (!tdata)
+ return -ENOMEM;
+
+ tdata->ok = false;
+ init_completion(&tdata->comp);
+
+ tdata->pt.type = htons(ETH_P_IP);
+ tdata->pt.func = xgbe_test_loopback_validate;
+ tdata->pt.dev = pdata->netdev;
+ tdata->pt.af_packet_priv = tdata;
+ tdata->packet = attr;
+
+ dev_add_pack(&tdata->pt);
+
+ skb = net_test_get_skb(pdata->netdev, xgbe_test_id, attr);
+ if (!skb) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+
+ xgbe_test_id++;
+ ret = dev_direct_xmit(skb, attr->queue_mapping);
+ if (ret)
+ goto cleanup;
+
+ if (!attr->timeout)
+ attr->timeout = NET_LB_TIMEOUT;
+
+ wait_for_completion_timeout(&tdata->comp, attr->timeout);
+ ret = tdata->ok ? 0 : -ETIMEDOUT;
+
+ if (ret)
+ netdev_err(pdata->netdev, "Response timedout: ret %d\n", ret);
+cleanup:
+ dev_remove_pack(&tdata->pt);
+ kfree(tdata);
+ return ret;
+}
+
+static int xgbe_test_mac_loopback(struct xgbe_prv_data *pdata)
+{
+ struct net_packet_attrs attr = {};
+
+ attr.dst = pdata->netdev->dev_addr;
+ return __xgbe_test_loopback(pdata, &attr);
+}
+
+static int xgbe_test_phy_loopback(struct xgbe_prv_data *pdata)
+{
+ struct net_packet_attrs attr = {};
+ int ret;
+
+ if (!pdata->netdev->phydev) {
+ netdev_err(pdata->netdev, "phydev not found: cannot start PHY loopback test\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = phy_loopback(pdata->netdev->phydev, true, 0);
+ if (ret)
+ return ret;
+
+ attr.dst = pdata->netdev->dev_addr;
+ ret = __xgbe_test_loopback(pdata, &attr);
+
+ phy_loopback(pdata->netdev->phydev, false, 0);
+ return ret;
+}
+
+static int xgbe_test_sph(struct xgbe_prv_data *pdata)
+{
+ struct net_packet_attrs attr = {};
+ unsigned long cnt_end, cnt_start;
+ int ret;
+
+ cnt_start = pdata->ext_stats.rx_split_header_packets;
+
+ if (!pdata->sph) {
+ netdev_err(pdata->netdev, "Split Header not enabled\n");
+ return -EOPNOTSUPP;
+ }
+
+ /* UDP test */
+ attr.dst = pdata->netdev->dev_addr;
+ attr.tcp = false;
+
+ ret = __xgbe_test_loopback(pdata, &attr);
+ if (ret)
+ return ret;
+
+ cnt_end = pdata->ext_stats.rx_split_header_packets;
+ if (cnt_end <= cnt_start)
+ return -EINVAL;
+
+ /* TCP test */
+ cnt_start = cnt_end;
+
+ attr.dst = pdata->netdev->dev_addr;
+ attr.tcp = true;
+
+ ret = __xgbe_test_loopback(pdata, &attr);
+ if (ret)
+ return ret;
+
+ cnt_end = pdata->ext_stats.rx_split_header_packets;
+ if (cnt_end <= cnt_start)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int xgbe_test_jumbo(struct xgbe_prv_data *pdata)
+{
+ struct net_packet_attrs attr = {};
+ int size = pdata->rx_buf_size;
+
+ attr.dst = pdata->netdev->dev_addr;
+ attr.max_size = size - ETH_FCS_LEN;
+
+ return __xgbe_test_loopback(pdata, &attr);
+}
+
+static const struct xgbe_test xgbe_selftests[] = {
+ {
+ .name = "MAC Loopback ",
+ .lb = XGBE_LOOPBACK_MAC,
+ .fn = xgbe_test_mac_loopback,
+ }, {
+ .name = "PHY Loopback ",
+ .lb = XGBE_LOOPBACK_NONE,
+ .fn = xgbe_test_phy_loopback,
+ }, {
+ .name = "Split Header ",
+ .lb = XGBE_LOOPBACK_PHY,
+ .fn = xgbe_test_sph,
+ }, {
+ .name = "Jumbo Frame ",
+ .lb = XGBE_LOOPBACK_PHY,
+ .fn = xgbe_test_jumbo,
+ },
+};
+
+void xgbe_selftest_run(struct net_device *dev,
+ struct ethtool_test *etest, u64 *buf)
+{
+ struct xgbe_prv_data *pdata = netdev_priv(dev);
+ int count = xgbe_selftest_get_count(pdata);
+ int i, ret;
+
+ memset(buf, 0, sizeof(*buf) * count);
+ xgbe_test_id = 0;
+
+ if (etest->flags != ETH_TEST_FL_OFFLINE) {
+ netdev_err(pdata->netdev, "Only offline tests are supported\n");
+ etest->flags |= ETH_TEST_FL_FAILED;
+ return;
+ } else if (!netif_carrier_ok(dev)) {
+ netdev_err(pdata->netdev,
+ "Invalid link, cannot execute tests\n");
+ etest->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
+
+ /* Wait for queues drain */
+ msleep(200);
+
+ for (i = 0; i < count; i++) {
+ ret = 0;
+
+ switch (xgbe_selftests[i].lb) {
+ case XGBE_LOOPBACK_PHY:
+ ret = -EOPNOTSUPP;
+ if (dev->phydev)
+ ret = phy_loopback(dev->phydev, true, 0);
+ if (!ret)
+ break;
+ fallthrough;
+ case XGBE_LOOPBACK_MAC:
+ ret = xgbe_enable_mac_loopback(pdata);
+ break;
+ case XGBE_LOOPBACK_NONE:
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ /*
+ * First tests will always be MAC / PHY loopback.
+ * If any of them is not supported we abort earlier.
+ */
+ if (ret) {
+ netdev_err(pdata->netdev, "Loopback not supported\n");
+ etest->flags |= ETH_TEST_FL_FAILED;
+ break;
+ }
+
+ ret = xgbe_selftests[i].fn(pdata);
+ if (ret && (ret != -EOPNOTSUPP))
+ etest->flags |= ETH_TEST_FL_FAILED;
+ buf[i] = ret;
+
+ switch (xgbe_selftests[i].lb) {
+ case XGBE_LOOPBACK_PHY:
+ ret = -EOPNOTSUPP;
+ if (dev->phydev)
+ ret = phy_loopback(dev->phydev, false, 0);
+ if (!ret)
+ break;
+ fallthrough;
+ case XGBE_LOOPBACK_MAC:
+ xgbe_disable_mac_loopback(pdata);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data)
+{
+ u8 *p = data;
+ int i;
+
+ for (i = 0; i < xgbe_selftest_get_count(pdata); i++)
+ ethtool_puts(&p, xgbe_selftests[i].name);
+}
+
+int xgbe_selftest_get_count(struct xgbe_prv_data *pdata)
+{
+ return ARRAY_SIZE(xgbe_selftests);
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index e8bbb6805901..03ef0f548483 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -1146,7 +1146,7 @@ struct xgbe_prv_data {
spinlock_t tstamp_lock;
struct ptp_clock_info ptp_clock_info;
struct ptp_clock *ptp_clock;
- struct hwtstamp_config tstamp_config;
+ struct kernel_hwtstamp_config tstamp_config;
unsigned int tstamp_addend;
struct work_struct tx_tstamp_work;
struct sk_buff *tx_tstamp_skb;
@@ -1246,6 +1246,7 @@ struct xgbe_prv_data {
int rx_adapt_retries;
bool rx_adapt_done;
bool mode_set;
+ bool sph;
};
/* Function prototypes*/
@@ -1307,10 +1308,11 @@ void xgbe_update_tstamp_addend(struct xgbe_prv_data *pdata,
void xgbe_set_tstamp_time(struct xgbe_prv_data *pdata, unsigned int sec,
unsigned int nsec);
void xgbe_tx_tstamp(struct work_struct *work);
-int xgbe_get_hwtstamp_settings(struct xgbe_prv_data *pdata,
- struct ifreq *ifreq);
-int xgbe_set_hwtstamp_settings(struct xgbe_prv_data *pdata,
- struct ifreq *ifreq);
+int xgbe_get_hwtstamp_settings(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config);
+int xgbe_set_hwtstamp_settings(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
void xgbe_prep_tx_tstamp(struct xgbe_prv_data *pdata,
struct sk_buff *skb,
struct xgbe_packet_data *packet);
@@ -1321,6 +1323,16 @@ void xgbe_update_tstamp_time(struct xgbe_prv_data *pdata, unsigned int sec,
int xgbe_pps_config(struct xgbe_prv_data *pdata, struct xgbe_pps_config *cfg,
int index, bool on);
+/* Selftest functions */
+void xgbe_selftest_run(struct net_device *dev,
+ struct ethtool_test *etest, u64 *buf);
+void xgbe_selftest_get_strings(struct xgbe_prv_data *pdata, u8 *data);
+int xgbe_selftest_get_count(struct xgbe_prv_data *pdata);
+
+/* Loopback control */
+int xgbe_enable_mac_loopback(struct xgbe_prv_data *pdata);
+void xgbe_disable_mac_loopback(struct xgbe_prv_data *pdata);
+
#ifdef CONFIG_DEBUG_FS
void xgbe_debugfs_init(struct xgbe_prv_data *);
void xgbe_debugfs_exit(struct xgbe_prv_data *);
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c
index 1921741f7311..18b08277d2e1 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c
@@ -15,6 +15,7 @@
#include "aq_hw.h"
#include "aq_nic.h"
+#include "hw_atl/hw_atl_llh.h"
void aq_hw_write_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk,
u32 shift, u32 val)
@@ -81,6 +82,27 @@ void aq_hw_write_reg64(struct aq_hw_s *hw, u32 reg, u64 value)
lo_hi_writeq(value, hw->mmio + reg);
}
+int aq_hw_invalidate_descriptor_cache(struct aq_hw_s *hw)
+{
+ int err;
+ u32 val;
+
+ /* Invalidate Descriptor Cache to prevent writing to the cached
+ * descriptors and to the data pointer of those descriptors
+ */
+ hw_atl_rdm_rx_dma_desc_cache_init_tgl(hw);
+
+ err = aq_hw_err_from_flags(hw);
+ if (err)
+ goto err_exit;
+
+ readx_poll_timeout_atomic(hw_atl_rdm_rx_dma_desc_cache_init_done_get,
+ hw, val, val == 1, 1000U, 10000U);
+
+err_exit:
+ return err;
+}
+
int aq_hw_err_from_flags(struct aq_hw_s *hw)
{
int err = 0;
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h
index ffa6e4067c21..d89c63d88e4a 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h
@@ -35,6 +35,7 @@ u32 aq_hw_read_reg(struct aq_hw_s *hw, u32 reg);
void aq_hw_write_reg(struct aq_hw_s *hw, u32 reg, u32 value);
u64 aq_hw_read_reg64(struct aq_hw_s *hw, u32 reg);
void aq_hw_write_reg64(struct aq_hw_s *hw, u32 reg, u64 value);
+int aq_hw_invalidate_descriptor_cache(struct aq_hw_s *hw);
int aq_hw_err_from_flags(struct aq_hw_s *hw);
int aq_hw_num_tcs(struct aq_hw_s *hw);
int aq_hw_q_per_tc(struct aq_hw_s *hw);
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_main.c b/drivers/net/ethernet/aquantia/atlantic/aq_main.c
index b565189e5913..4ef4fe64b8ac 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_main.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_main.c
@@ -258,10 +258,15 @@ static void aq_ndev_set_multicast_settings(struct net_device *ndev)
(void)aq_nic_set_multicast_list(aq_nic, ndev);
}
-#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
-static int aq_ndev_config_hwtstamp(struct aq_nic_s *aq_nic,
- struct hwtstamp_config *config)
+static int aq_ndev_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
+ struct aq_nic_s *aq_nic = netdev_priv(netdev);
+
+ if (!IS_REACHABLE(CONFIG_PTP_1588_CLOCK) || !aq_nic->aq_ptp)
+ return -EOPNOTSUPP;
+
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
@@ -290,59 +295,17 @@ static int aq_ndev_config_hwtstamp(struct aq_nic_s *aq_nic,
return aq_ptp_hwtstamp_config_set(aq_nic->aq_ptp, config);
}
-#endif
-
-static int aq_ndev_hwtstamp_set(struct aq_nic_s *aq_nic, struct ifreq *ifr)
-{
- struct hwtstamp_config config;
-#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
- int ret_val;
-#endif
-
- if (!aq_nic->aq_ptp)
- return -EOPNOTSUPP;
-
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
-#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
- ret_val = aq_ndev_config_hwtstamp(aq_nic, &config);
- if (ret_val)
- return ret_val;
-#endif
-
- return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
-}
-#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
-static int aq_ndev_hwtstamp_get(struct aq_nic_s *aq_nic, struct ifreq *ifr)
+static int aq_ndev_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
- struct hwtstamp_config config;
+ struct aq_nic_s *aq_nic = netdev_priv(netdev);
if (!aq_nic->aq_ptp)
return -EOPNOTSUPP;
- aq_ptp_hwtstamp_config_get(aq_nic->aq_ptp, &config);
- return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
-}
-#endif
-
-static int aq_ndev_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
-{
- struct aq_nic_s *aq_nic = netdev_priv(netdev);
-
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return aq_ndev_hwtstamp_set(aq_nic, ifr);
-
-#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
- case SIOCGHWTSTAMP:
- return aq_ndev_hwtstamp_get(aq_nic, ifr);
-#endif
- }
-
- return -EOPNOTSUPP;
+ aq_ptp_hwtstamp_config_get(aq_nic->aq_ptp, config);
+ return 0;
}
static int aq_ndo_vlan_rx_add_vid(struct net_device *ndev, __be16 proto,
@@ -500,12 +463,13 @@ static const struct net_device_ops aq_ndev_ops = {
.ndo_set_mac_address = aq_ndev_set_mac_address,
.ndo_set_features = aq_ndev_set_features,
.ndo_fix_features = aq_ndev_fix_features,
- .ndo_eth_ioctl = aq_ndev_ioctl,
.ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid,
.ndo_setup_tc = aq_ndo_setup_tc,
.ndo_bpf = aq_xdp,
.ndo_xdp_xmit = aq_xdp_xmit,
+ .ndo_hwtstamp_get = aq_ndev_hwtstamp_get,
+ .ndo_hwtstamp_set = aq_ndev_hwtstamp_set,
};
static int __init aq_ndev_init_module(void)
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
index 5acb3e16b567..0fa0f891c0e0 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
@@ -51,7 +51,7 @@ struct ptp_tx_timeout {
struct aq_ptp_s {
struct aq_nic_s *aq_nic;
- struct hwtstamp_config hwtstamp_config;
+ struct kernel_hwtstamp_config hwtstamp_config;
spinlock_t ptp_lock;
spinlock_t ptp_ring_lock;
struct ptp_clock *ptp_clock;
@@ -567,7 +567,7 @@ static void aq_ptp_rx_hwtstamp(struct aq_ptp_s *aq_ptp, struct skb_shared_hwtsta
}
void aq_ptp_hwtstamp_config_get(struct aq_ptp_s *aq_ptp,
- struct hwtstamp_config *config)
+ struct kernel_hwtstamp_config *config)
{
*config = aq_ptp->hwtstamp_config;
}
@@ -588,7 +588,7 @@ static void aq_ptp_prepare_filters(struct aq_ptp_s *aq_ptp)
}
int aq_ptp_hwtstamp_config_set(struct aq_ptp_s *aq_ptp,
- struct hwtstamp_config *config)
+ struct kernel_hwtstamp_config *config)
{
struct aq_nic_s *aq_nic = aq_ptp->aq_nic;
const struct aq_hw_ops *hw_ops;
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h
index 210b723f2207..5e643ec7cc06 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h
@@ -60,9 +60,9 @@ void aq_ptp_tx_hwtstamp(struct aq_nic_s *aq_nic, u64 timestamp);
/* Must be to check available of PTP before call */
void aq_ptp_hwtstamp_config_get(struct aq_ptp_s *aq_ptp,
- struct hwtstamp_config *config);
+ struct kernel_hwtstamp_config *config);
int aq_ptp_hwtstamp_config_set(struct aq_ptp_s *aq_ptp,
- struct hwtstamp_config *config);
+ struct kernel_hwtstamp_config *config);
/* Return either ring is belong to PTP or not*/
bool aq_ptp_ring(struct aq_nic_s *aq_nic, struct aq_ring_s *ring);
@@ -130,9 +130,9 @@ static inline int aq_ptp_xmit(struct aq_nic_s *aq_nic, struct sk_buff *skb)
static inline void aq_ptp_tx_hwtstamp(struct aq_nic_s *aq_nic, u64 timestamp) {}
static inline void aq_ptp_hwtstamp_config_get(struct aq_ptp_s *aq_ptp,
- struct hwtstamp_config *config) {}
+ struct kernel_hwtstamp_config *config) {}
static inline int aq_ptp_hwtstamp_config_set(struct aq_ptp_s *aq_ptp,
- struct hwtstamp_config *config)
+ struct kernel_hwtstamp_config *config)
{
return 0;
}
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
index f21de0c21e52..d23d23bed39f 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c
@@ -547,6 +547,11 @@ static int __aq_ring_rx_clean(struct aq_ring_s *self, struct napi_struct *napi,
if (!buff->is_eop) {
unsigned int frag_cnt = 0U;
+
+ /* There will be an extra fragment */
+ if (buff->len > AQ_CFG_RX_HDR_SIZE)
+ frag_cnt++;
+
buff_ = buff;
do {
bool is_rsc_completed = true;
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
index 493432d036b9..c7895bfb2ecf 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
@@ -1198,26 +1198,9 @@ static int hw_atl_b0_hw_interrupt_moderation_set(struct aq_hw_s *self)
static int hw_atl_b0_hw_stop(struct aq_hw_s *self)
{
- int err;
- u32 val;
-
hw_atl_b0_hw_irq_disable(self, HW_ATL_B0_INT_MASK);
- /* Invalidate Descriptor Cache to prevent writing to the cached
- * descriptors and to the data pointer of those descriptors
- */
- hw_atl_rdm_rx_dma_desc_cache_init_tgl(self);
-
- err = aq_hw_err_from_flags(self);
-
- if (err)
- goto err_exit;
-
- readx_poll_timeout_atomic(hw_atl_rdm_rx_dma_desc_cache_init_done_get,
- self, val, val == 1, 1000U, 10000U);
-
-err_exit:
- return err;
+ return aq_hw_invalidate_descriptor_cache(self);
}
int hw_atl_b0_hw_ring_tx_stop(struct aq_hw_s *self, struct aq_ring_s *ring)
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
index b0ed572e88c6..0ce9caae8799 100644
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c
@@ -759,7 +759,7 @@ static int hw_atl2_hw_stop(struct aq_hw_s *self)
{
hw_atl_b0_hw_irq_disable(self, HW_ATL2_INT_MASK);
- return 0;
+ return aq_hw_invalidate_descriptor_cache(self);
}
static struct aq_stats_s *hw_atl2_utils_get_hw_stats(struct aq_hw_s *self)
diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig
index 9fdef874f5ca..666522d64775 100644
--- a/drivers/net/ethernet/broadcom/Kconfig
+++ b/drivers/net/ethernet/broadcom/Kconfig
@@ -25,6 +25,7 @@ config B44
select SSB
select MII
select PHYLIB
+ select FIXED_PHY if BCM47XX
help
If you have a network (Ethernet) controller of this type, say Y
or M here.
diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c
index 63f1a8c3a7fb..dd80ccfca19d 100644
--- a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c
+++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c
@@ -163,11 +163,30 @@ static void bcmasp_set_msglevel(struct net_device *dev, u32 level)
static void bcmasp_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct bcmasp_intf *intf = netdev_priv(dev);
+ struct bcmasp_priv *priv = intf->parent;
+ struct device *kdev = &priv->pdev->dev;
+ u32 phy_wolopts = 0;
+
+ if (dev->phydev) {
+ phy_ethtool_get_wol(dev->phydev, wol);
+ phy_wolopts = wol->wolopts;
+ }
+
+ /* MAC is not wake-up capable, return what the PHY does */
+ if (!device_can_wakeup(kdev))
+ return;
+
+ /* Overlay MAC capabilities with that of the PHY queried before */
+ wol->supported |= BCMASP_SUPPORTED_WAKE;
+ wol->wolopts |= intf->wolopts;
+
+ /* Return the PHY configured magic password */
+ if (phy_wolopts & WAKE_MAGICSECURE)
+ return;
- wol->supported = BCMASP_SUPPORTED_WAKE;
- wol->wolopts = intf->wolopts;
memset(wol->sopass, 0, sizeof(wol->sopass));
+ /* Otherwise the MAC one */
if (wol->wolopts & WAKE_MAGICSECURE)
memcpy(wol->sopass, intf->sopass, sizeof(intf->sopass));
}
@@ -177,10 +196,21 @@ static int bcmasp_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
struct bcmasp_intf *intf = netdev_priv(dev);
struct bcmasp_priv *priv = intf->parent;
struct device *kdev = &priv->pdev->dev;
+ int ret = 0;
+
+ /* Try Wake-on-LAN from the PHY first */
+ if (dev->phydev) {
+ ret = phy_ethtool_set_wol(dev->phydev, wol);
+ if (ret != -EOPNOTSUPP && wol->wolopts)
+ return ret;
+ }
if (!device_can_wakeup(kdev))
return -EOPNOTSUPP;
+ if (wol->wolopts & ~BCMASP_SUPPORTED_WAKE)
+ return -EINVAL;
+
/* Interface Specific */
intf->wolopts = wol->wolopts;
if (intf->wolopts & WAKE_MAGICSECURE)
diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c
index 0353359c3fe9..888f28f11406 100644
--- a/drivers/net/ethernet/broadcom/b44.c
+++ b/drivers/net/ethernet/broadcom/b44.c
@@ -31,6 +31,7 @@
#include <linux/ssb/ssb.h>
#include <linux/slab.h>
#include <linux/phy.h>
+#include <linux/phy_fixed.h>
#include <linux/uaccess.h>
#include <asm/io.h>
@@ -2233,7 +2234,6 @@ static int b44_register_phy_one(struct b44 *bp)
struct mii_bus *mii_bus;
struct ssb_device *sdev = bp->sdev;
struct phy_device *phydev;
- char bus_id[MII_BUS_ID_SIZE + 3];
struct ssb_sprom *sprom = &sdev->bus->sprom;
int err;
@@ -2260,27 +2260,26 @@ static int b44_register_phy_one(struct b44 *bp)
goto err_out_mdiobus;
}
- if (!mdiobus_is_registered_device(bp->mii_bus, bp->phy_addr) &&
- (sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM))) {
-
+ phydev = mdiobus_get_phy(bp->mii_bus, bp->phy_addr);
+ if (!phydev &&
+ sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM)) {
dev_info(sdev->dev,
"could not find PHY at %i, use fixed one\n",
bp->phy_addr);
- bp->phy_addr = 0;
- snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, "fixed-0",
- bp->phy_addr);
- } else {
- snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, mii_bus->id,
- bp->phy_addr);
+ phydev = fixed_phy_register_100fd();
+ if (!IS_ERR(phydev))
+ bp->phy_addr = phydev->mdio.addr;
}
- phydev = phy_connect(bp->dev, bus_id, &b44_adjust_link,
- PHY_INTERFACE_MODE_MII);
- if (IS_ERR(phydev)) {
+ if (IS_ERR_OR_NULL(phydev))
+ err = -ENODEV;
+ else
+ err = phy_connect_direct(bp->dev, phydev, &b44_adjust_link,
+ PHY_INTERFACE_MODE_MII);
+ if (err) {
dev_err(sdev->dev, "could not attach PHY at %i\n",
bp->phy_addr);
- err = PTR_ERR(phydev);
goto err_out_mdiobus_unregister;
}
@@ -2293,7 +2292,6 @@ static int b44_register_phy_one(struct b44 *bp)
linkmode_copy(phydev->advertising, phydev->supported);
bp->old_link = 0;
- bp->phy_addr = phydev->mdio.addr;
phy_attached_info(phydev);
@@ -2311,10 +2309,15 @@ err_out:
static void b44_unregister_phy_one(struct b44 *bp)
{
- struct net_device *dev = bp->dev;
struct mii_bus *mii_bus = bp->mii_bus;
+ struct net_device *dev = bp->dev;
+ struct phy_device *phydev;
+
+ phydev = dev->phydev;
- phy_disconnect(dev->phydev);
+ phy_disconnect(phydev);
+ if (phy_is_pseudo_fixed_link(phydev))
+ fixed_phy_unregister(phydev);
mdiobus_unregister(mii_bus);
mdiobus_free(mii_bus);
}
diff --git a/drivers/net/ethernet/broadcom/bnge/Makefile b/drivers/net/ethernet/broadcom/bnge/Makefile
index 6142d9c57f49..ea6596854e5c 100644
--- a/drivers/net/ethernet/broadcom/bnge/Makefile
+++ b/drivers/net/ethernet/broadcom/bnge/Makefile
@@ -9,4 +9,5 @@ bng_en-y := bnge_core.o \
bnge_rmem.o \
bnge_resc.o \
bnge_netdev.o \
- bnge_ethtool.o
+ bnge_ethtool.o \
+ bnge_auxr.o
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge.h b/drivers/net/ethernet/broadcom/bnge/bnge.h
index 7aed5f81cd51..411744894349 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge.h
@@ -11,6 +11,7 @@
#include <linux/bnxt/hsi.h>
#include "bnge_rmem.h"
#include "bnge_resc.h"
+#include "bnge_auxr.h"
#define DRV_VER_MAJ 1
#define DRV_VER_MIN 15
@@ -22,6 +23,12 @@ enum board_idx {
BCM57708,
};
+struct bnge_auxr_priv {
+ struct auxiliary_device aux_dev;
+ struct bnge_auxr_dev *auxr_dev;
+ int id;
+};
+
struct bnge_pf_info {
u16 fw_fid;
u16 port_id;
@@ -197,6 +204,9 @@ struct bnge_dev {
struct bnge_irq *irq_tbl;
u16 irqs_acquired;
+
+ struct bnge_auxr_priv *aux_priv;
+ struct bnge_auxr_dev *auxr_dev;
};
static inline bool bnge_is_roce_en(struct bnge_dev *bd)
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
new file mode 100644
index 000000000000..d64592b64e17
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c
@@ -0,0 +1,258 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2025 Broadcom.
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/bitops.h>
+#include <linux/irq.h>
+#include <asm/byteorder.h>
+#include <linux/bitmap.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/bnxt/hsi.h>
+
+#include "bnge.h"
+#include "bnge_hwrm.h"
+#include "bnge_auxr.h"
+
+static DEFINE_IDA(bnge_aux_dev_ids);
+
+static void bnge_fill_msix_vecs(struct bnge_dev *bd,
+ struct bnge_msix_info *info)
+{
+ struct bnge_auxr_dev *auxr_dev = bd->auxr_dev;
+ int num_msix, i;
+
+ if (!auxr_dev->auxr_info->msix_requested) {
+ dev_warn(bd->dev, "Requested MSI-X vectors not allocated\n");
+ return;
+ }
+ num_msix = auxr_dev->auxr_info->msix_requested;
+ for (i = 0; i < num_msix; i++) {
+ info[i].vector = bd->irq_tbl[i].vector;
+ info[i].db_offset = bd->db_offset;
+ info[i].ring_idx = i;
+ }
+}
+
+int bnge_register_dev(struct bnge_auxr_dev *auxr_dev,
+ void *handle)
+{
+ struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev);
+ struct bnge_auxr_info *auxr_info;
+ int rc = 0;
+
+ netdev_lock(bd->netdev);
+ mutex_lock(&auxr_dev->auxr_dev_lock);
+ if (!bd->irq_tbl) {
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ if (!bnge_aux_has_enough_resources(bd)) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ auxr_info = auxr_dev->auxr_info;
+ auxr_info->handle = handle;
+
+ auxr_info->msix_requested = bd->aux_num_msix;
+
+ bnge_fill_msix_vecs(bd, bd->auxr_dev->msix_info);
+ auxr_dev->flags |= BNGE_ARDEV_MSIX_ALLOC;
+
+exit:
+ mutex_unlock(&auxr_dev->auxr_dev_lock);
+ netdev_unlock(bd->netdev);
+ return rc;
+}
+EXPORT_SYMBOL(bnge_register_dev);
+
+void bnge_unregister_dev(struct bnge_auxr_dev *auxr_dev)
+{
+ struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev);
+ struct bnge_auxr_info *auxr_info;
+
+ auxr_info = auxr_dev->auxr_info;
+ netdev_lock(bd->netdev);
+ mutex_lock(&auxr_dev->auxr_dev_lock);
+ if (auxr_info->msix_requested)
+ auxr_dev->flags &= ~BNGE_ARDEV_MSIX_ALLOC;
+ auxr_info->msix_requested = 0;
+
+ mutex_unlock(&auxr_dev->auxr_dev_lock);
+ netdev_unlock(bd->netdev);
+}
+EXPORT_SYMBOL(bnge_unregister_dev);
+
+int bnge_send_msg(struct bnge_auxr_dev *auxr_dev, struct bnge_fw_msg *fw_msg)
+{
+ struct bnge_dev *bd = pci_get_drvdata(auxr_dev->pdev);
+ struct output *resp;
+ struct input *req;
+ u32 resp_len;
+ int rc;
+
+ rc = bnge_hwrm_req_init(bd, req, 0 /* don't care */);
+ if (rc)
+ return rc;
+
+ rc = bnge_hwrm_req_replace(bd, req, fw_msg->msg, fw_msg->msg_len);
+ if (rc)
+ goto drop_req;
+
+ bnge_hwrm_req_timeout(bd, req, fw_msg->timeout);
+ resp = bnge_hwrm_req_hold(bd, req);
+ rc = bnge_hwrm_req_send(bd, req);
+ resp_len = le16_to_cpu(resp->resp_len);
+ if (resp_len) {
+ if (fw_msg->resp_max_len < resp_len)
+ resp_len = fw_msg->resp_max_len;
+
+ memcpy(fw_msg->resp, resp, resp_len);
+ }
+drop_req:
+ bnge_hwrm_req_drop(bd, req);
+ return rc;
+}
+EXPORT_SYMBOL(bnge_send_msg);
+
+void bnge_rdma_aux_device_uninit(struct bnge_dev *bd)
+{
+ struct bnge_auxr_priv *aux_priv;
+ struct auxiliary_device *adev;
+
+ /* Skip if no auxiliary device init was done. */
+ if (!bd->aux_priv)
+ return;
+
+ aux_priv = bd->aux_priv;
+ adev = &aux_priv->aux_dev;
+ auxiliary_device_uninit(adev);
+}
+
+static void bnge_aux_dev_release(struct device *dev)
+{
+ struct bnge_auxr_priv *aux_priv =
+ container_of(dev, struct bnge_auxr_priv, aux_dev.dev);
+ struct bnge_dev *bd = pci_get_drvdata(aux_priv->auxr_dev->pdev);
+
+ ida_free(&bnge_aux_dev_ids, aux_priv->id);
+ kfree(aux_priv->auxr_dev->auxr_info);
+ bd->auxr_dev = NULL;
+ kfree(aux_priv->auxr_dev);
+ kfree(aux_priv);
+ bd->aux_priv = NULL;
+}
+
+void bnge_rdma_aux_device_del(struct bnge_dev *bd)
+{
+ if (!bd->auxr_dev)
+ return;
+
+ auxiliary_device_delete(&bd->aux_priv->aux_dev);
+}
+
+static void bnge_set_auxr_dev_info(struct bnge_auxr_dev *auxr_dev,
+ struct bnge_dev *bd)
+{
+ auxr_dev->pdev = bd->pdev;
+ auxr_dev->l2_db_size = bd->db_size;
+ auxr_dev->l2_db_size_nc = bd->db_size;
+ auxr_dev->l2_db_offset = bd->db_offset;
+ mutex_init(&auxr_dev->auxr_dev_lock);
+
+ if (bd->flags & BNGE_EN_ROCE_V1)
+ auxr_dev->flags |= BNGE_ARDEV_ROCEV1_SUPP;
+ if (bd->flags & BNGE_EN_ROCE_V2)
+ auxr_dev->flags |= BNGE_ARDEV_ROCEV2_SUPP;
+
+ auxr_dev->chip_num = bd->chip_num;
+ auxr_dev->hw_ring_stats_size = bd->hw_ring_stats_size;
+ auxr_dev->pf_port_id = bd->pf.port_id;
+ auxr_dev->en_state = bd->state;
+ auxr_dev->bar0 = bd->bar0;
+}
+
+void bnge_rdma_aux_device_add(struct bnge_dev *bd)
+{
+ struct auxiliary_device *aux_dev;
+ int rc;
+
+ if (!bd->auxr_dev)
+ return;
+
+ aux_dev = &bd->aux_priv->aux_dev;
+ rc = auxiliary_device_add(aux_dev);
+ if (rc) {
+ dev_warn(bd->dev, "Failed to add auxiliary device for ROCE\n");
+ auxiliary_device_uninit(aux_dev);
+ bd->flags &= ~BNGE_EN_ROCE;
+ }
+
+ bd->auxr_dev->net = bd->netdev;
+}
+
+void bnge_rdma_aux_device_init(struct bnge_dev *bd)
+{
+ struct auxiliary_device *aux_dev;
+ struct bnge_auxr_info *auxr_info;
+ struct bnge_auxr_priv *aux_priv;
+ struct bnge_auxr_dev *auxr_dev;
+ int rc;
+
+ if (!bnge_is_roce_en(bd))
+ return;
+
+ aux_priv = kzalloc(sizeof(*aux_priv), GFP_KERNEL);
+ if (!aux_priv)
+ goto exit;
+
+ aux_priv->id = ida_alloc(&bnge_aux_dev_ids, GFP_KERNEL);
+ if (aux_priv->id < 0) {
+ dev_warn(bd->dev, "ida alloc failed for aux device\n");
+ kfree(aux_priv);
+ goto exit;
+ }
+
+ aux_dev = &aux_priv->aux_dev;
+ aux_dev->id = aux_priv->id;
+ aux_dev->name = "rdma";
+ aux_dev->dev.parent = &bd->pdev->dev;
+ aux_dev->dev.release = bnge_aux_dev_release;
+
+ rc = auxiliary_device_init(aux_dev);
+ if (rc) {
+ ida_free(&bnge_aux_dev_ids, aux_priv->id);
+ kfree(aux_priv);
+ goto exit;
+ }
+ bd->aux_priv = aux_priv;
+
+ auxr_dev = kzalloc(sizeof(*auxr_dev), GFP_KERNEL);
+ if (!auxr_dev)
+ goto aux_dev_uninit;
+
+ aux_priv->auxr_dev = auxr_dev;
+
+ auxr_info = kzalloc(sizeof(*auxr_info), GFP_KERNEL);
+ if (!auxr_info)
+ goto aux_dev_uninit;
+
+ auxr_dev->auxr_info = auxr_info;
+ bd->auxr_dev = auxr_dev;
+ bnge_set_auxr_dev_info(auxr_dev, bd);
+
+ return;
+
+aux_dev_uninit:
+ auxiliary_device_uninit(aux_dev);
+exit:
+ bd->flags &= ~BNGE_EN_ROCE;
+}
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.h b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.h
new file mode 100644
index 000000000000..6c5c15ef2b0a
--- /dev/null
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2025 Broadcom */
+
+#ifndef _BNGE_AUXR_H_
+#define _BNGE_AUXR_H_
+
+#include <linux/auxiliary_bus.h>
+
+#define BNGE_MIN_ROCE_CP_RINGS 2
+#define BNGE_MIN_ROCE_STAT_CTXS 1
+
+#define BNGE_MAX_ROCE_MSIX 64
+
+struct hwrm_async_event_cmpl;
+struct bnge;
+
+struct bnge_msix_info {
+ u32 vector;
+ u32 ring_idx;
+ u32 db_offset;
+};
+
+struct bnge_fw_msg {
+ void *msg;
+ int msg_len;
+ void *resp;
+ int resp_max_len;
+ int timeout;
+};
+
+struct bnge_auxr_info {
+ void *handle;
+ u16 msix_requested;
+};
+
+enum {
+ BNGE_ARDEV_ROCEV1_SUPP = BIT(0),
+ BNGE_ARDEV_ROCEV2_SUPP = BIT(1),
+ BNGE_ARDEV_MSIX_ALLOC = BIT(2),
+};
+
+#define BNGE_ARDEV_ROCE_SUPP (BNGE_ARDEV_ROCEV1_SUPP | \
+ BNGE_ARDEV_ROCEV2_SUPP)
+
+struct bnge_auxr_dev {
+ struct net_device *net;
+ struct pci_dev *pdev;
+ void __iomem *bar0;
+
+ struct bnge_msix_info msix_info[BNGE_MAX_ROCE_MSIX];
+
+ u32 flags;
+
+ struct bnge_auxr_info *auxr_info;
+
+ /* Doorbell BAR size in bytes mapped by L2 driver. */
+ int l2_db_size;
+ /* Doorbell BAR size in bytes mapped as non-cacheable. */
+ int l2_db_size_nc;
+ /* Doorbell offset in bytes within l2_db_size_nc. */
+ int l2_db_offset;
+
+ u16 chip_num;
+ u16 hw_ring_stats_size;
+ u16 pf_port_id;
+ unsigned long en_state;
+
+ u16 auxr_num_msix_vec;
+ u16 auxr_num_ctxs;
+
+ /* serialize auxr operations */
+ struct mutex auxr_dev_lock;
+};
+
+void bnge_rdma_aux_device_uninit(struct bnge_dev *bdev);
+void bnge_rdma_aux_device_del(struct bnge_dev *bdev);
+void bnge_rdma_aux_device_add(struct bnge_dev *bdev);
+void bnge_rdma_aux_device_init(struct bnge_dev *bdev);
+int bnge_register_dev(struct bnge_auxr_dev *adev,
+ void *handle);
+void bnge_unregister_dev(struct bnge_auxr_dev *adev);
+int bnge_send_msg(struct bnge_auxr_dev *adev, struct bnge_fw_msg *fw_msg);
+
+#endif /* _BNGE_AUXR_H_ */
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
index 2c72dd34d50d..c94e132bebc8 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
@@ -41,6 +41,11 @@ static void bnge_print_device_info(struct pci_dev *pdev, enum board_idx idx)
bool bnge_aux_registered(struct bnge_dev *bd)
{
+ struct bnge_auxr_dev *ba_dev = bd->auxr_dev;
+
+ if (ba_dev && ba_dev->auxr_info->msix_requested)
+ return true;
+
return false;
}
@@ -312,16 +317,20 @@ static int bnge_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
spin_lock_init(&bd->db_lock);
#endif
+ bnge_rdma_aux_device_init(bd);
+
rc = bnge_alloc_irqs(bd);
if (rc) {
dev_err(&pdev->dev, "Error IRQ allocation rc = %d\n", rc);
- goto err_config_uninit;
+ goto err_uninit_auxr;
}
rc = bnge_netdev_alloc(bd, max_irqs);
if (rc)
goto err_free_irq;
+ bnge_rdma_aux_device_add(bd);
+
pci_save_state(pdev);
return 0;
@@ -329,6 +338,9 @@ static int bnge_probe_one(struct pci_dev *pdev, const struct pci_device_id *ent)
err_free_irq:
bnge_free_irqs(bd);
+err_uninit_auxr:
+ bnge_rdma_aux_device_uninit(bd);
+
err_config_uninit:
bnge_net_uninit_dflt_config(bd);
@@ -354,10 +366,14 @@ static void bnge_remove_one(struct pci_dev *pdev)
{
struct bnge_dev *bd = pci_get_drvdata(pdev);
+ bnge_rdma_aux_device_del(bd);
+
bnge_netdev_free(bd);
bnge_free_irqs(bd);
+ bnge_rdma_aux_device_uninit(bd);
+
bnge_net_uninit_dflt_config(bd);
bnge_devlink_unregister(bd);
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
index 0f971af24142..c3087e5cd875 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.c
@@ -98,6 +98,46 @@ void bnge_hwrm_req_alloc_flags(struct bnge_dev *bd, void *req, gfp_t gfp)
ctx->gfp = gfp;
}
+int bnge_hwrm_req_replace(struct bnge_dev *bd, void *req, void *new_req,
+ u32 len)
+{
+ struct bnge_hwrm_ctx *ctx = __hwrm_ctx_get(bd, req);
+ struct input *internal_req = req;
+ u16 req_type;
+
+ if (!ctx)
+ return -EINVAL;
+
+ if (len > BNGE_HWRM_CTX_OFFSET)
+ return -E2BIG;
+
+ /* free any existing slices */
+ ctx->allocated = BNGE_HWRM_DMA_SIZE - BNGE_HWRM_CTX_OFFSET;
+ if (ctx->slice_addr) {
+ dma_free_coherent(bd->dev, ctx->slice_size,
+ ctx->slice_addr, ctx->slice_handle);
+ ctx->slice_addr = NULL;
+ }
+ ctx->gfp = GFP_KERNEL;
+
+ if ((bd->fw_cap & BNGE_FW_CAP_SHORT_CMD) || len > BNGE_HWRM_MAX_REQ_LEN) {
+ memcpy(internal_req, new_req, len);
+ } else {
+ internal_req->req_type = ((struct input *)new_req)->req_type;
+ ctx->req = new_req;
+ }
+
+ ctx->req_len = len;
+ ctx->req->resp_addr = cpu_to_le64(ctx->dma_handle +
+ BNGE_HWRM_RESP_OFFSET);
+
+ /* update sentinel for potentially new request type */
+ req_type = le16_to_cpu(internal_req->req_type);
+ ctx->sentinel = bnge_cal_sentinel(ctx, req_type);
+
+ return 0;
+}
+
void bnge_hwrm_req_flags(struct bnge_dev *bd, void *req,
enum bnge_hwrm_ctx_flags flags)
{
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h
index 83794a12cc81..6df629761d95 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_hwrm.h
@@ -107,4 +107,6 @@ int bnge_hwrm_req_send_silent(struct bnge_dev *bd, void *req);
void bnge_hwrm_req_alloc_flags(struct bnge_dev *bd, void *req, gfp_t flags);
void *bnge_hwrm_req_dma_slice(struct bnge_dev *bd, void *req, u32 size,
dma_addr_t *dma);
+int bnge_hwrm_req_replace(struct bnge_dev *bd, void *req, void *new_req,
+ u32 len);
#endif /* _BNGE_HWRM_H_ */
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
index 62ebe03a0dcf..943df5f60f01 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c
@@ -34,6 +34,18 @@ static unsigned int bnge_get_max_func_stat_ctxs(struct bnge_dev *bd)
return bd->hw_resc.max_stat_ctxs;
}
+bool bnge_aux_has_enough_resources(struct bnge_dev *bd)
+{
+ unsigned int max_stat_ctxs;
+
+ max_stat_ctxs = bnge_get_max_func_stat_ctxs(bd);
+ if (max_stat_ctxs <= BNGE_MIN_ROCE_STAT_CTXS ||
+ bd->nq_nr_rings == max_stat_ctxs)
+ return false;
+
+ return true;
+}
+
static unsigned int bnge_get_max_func_cp_rings(struct bnge_dev *bd)
{
return bd->hw_resc.max_cp_rings;
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h
index 0d6213b27580..b62a634669f6 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.h
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.h
@@ -74,6 +74,7 @@ void bnge_net_uninit_dflt_config(struct bnge_dev *bd);
void bnge_aux_init_dflt_config(struct bnge_dev *bd);
u32 bnge_get_rxfh_indir_size(struct bnge_dev *bd);
int bnge_cal_nr_rss_ctxs(u16 rx_rings);
+bool bnge_aux_has_enough_resources(struct bnge_dev *bd);
static inline u32
bnge_adjust_pow_two(u32 total_ent, u16 ent_per_blk)
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index cb1011f6fd30..805daae9dd36 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -6444,7 +6444,6 @@ bnx2_reset_task(struct work_struct *work)
if (!(pcicmd & PCI_COMMAND_MEMORY)) {
/* in case PCI block has reset */
pci_restore_state(bp->pdev);
- pci_save_state(bp->pdev);
}
rc = bnx2_init_nic(bp, 1);
if (rc) {
@@ -8718,7 +8717,6 @@ static pci_ers_result_t bnx2_io_slot_reset(struct pci_dev *pdev)
} else {
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (netif_running(dev))
err = bnx2_init_nic(bp, 1);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
index fc8dec37a9e4..3d853eeb976f 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c
@@ -3355,19 +3355,11 @@ static int bnx2x_get_rxfh_fields(struct net_device *dev,
return 0;
}
-static int bnx2x_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
- u32 *rules __always_unused)
+static u32 bnx2x_get_rx_ring_count(struct net_device *dev)
{
struct bnx2x *bp = netdev_priv(dev);
- switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- info->data = BNX2X_NUM_ETH_QUEUES(bp);
- return 0;
- default:
- DP(BNX2X_MSG_ETHTOOL, "Command parameters not supported\n");
- return -EOPNOTSUPP;
- }
+ return BNX2X_NUM_ETH_QUEUES(bp);
}
static int bnx2x_set_rxfh_fields(struct net_device *dev,
@@ -3674,7 +3666,7 @@ static const struct ethtool_ops bnx2x_ethtool_ops = {
.get_strings = bnx2x_get_strings,
.set_phys_id = bnx2x_set_phys_id,
.get_ethtool_stats = bnx2x_get_ethtool_stats,
- .get_rxnfc = bnx2x_get_rxnfc,
+ .get_rx_ring_count = bnx2x_get_rx_ring_count,
.get_rxfh_indir_size = bnx2x_get_rxfh_indir_size,
.get_rxfh = bnx2x_get_rxfh,
.set_rxfh = bnx2x_set_rxfh,
@@ -3702,7 +3694,7 @@ static const struct ethtool_ops bnx2x_vf_ethtool_ops = {
.get_sset_count = bnx2x_get_sset_count,
.get_strings = bnx2x_get_strings,
.get_ethtool_stats = bnx2x_get_ethtool_stats,
- .get_rxnfc = bnx2x_get_rxnfc,
+ .get_rx_ring_count = bnx2x_get_rx_ring_count,
.get_rxfh_indir_size = bnx2x_get_rxfh_indir_size,
.get_rxfh = bnx2x_get_rxfh,
.set_rxfh = bnx2x_set_rxfh,
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index f0f05d7315ac..6a1cc2032bf3 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -308,8 +308,11 @@ static int bnx2x_set_storm_rx_mode(struct bnx2x *bp);
/****************************************************************************
* General service functions
****************************************************************************/
-
-static int bnx2x_hwtstamp_ioctl(struct bnx2x *bp, struct ifreq *ifr);
+static int bnx2x_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
+static int bnx2x_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config);
static void __storm_memset_dma_mapping(struct bnx2x *bp,
u32 addr, dma_addr_t mapping)
@@ -12813,14 +12816,9 @@ static int bnx2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (!netif_running(dev))
return -EAGAIN;
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return bnx2x_hwtstamp_ioctl(bp, ifr);
- default:
- DP(NETIF_MSG_LINK, "ioctl: phy id 0x%x, reg 0x%x, val_in 0x%x\n",
- mdio->phy_id, mdio->reg_num, mdio->val_in);
- return mdio_mii_ioctl(&bp->mdio, mdio, cmd);
- }
+ DP(NETIF_MSG_LINK, "ioctl: phy id 0x%x, reg 0x%x, val_in 0x%x\n",
+ mdio->phy_id, mdio->reg_num, mdio->val_in);
+ return mdio_mii_ioctl(&bp->mdio, mdio, cmd);
}
static int bnx2x_validate_addr(struct net_device *dev)
@@ -13036,6 +13034,8 @@ static const struct net_device_ops bnx2x_netdev_ops = {
.ndo_get_phys_port_id = bnx2x_get_phys_port_id,
.ndo_set_vf_link_state = bnx2x_set_vf_link_state,
.ndo_features_check = bnx2x_features_check,
+ .ndo_hwtstamp_get = bnx2x_hwtstamp_get,
+ .ndo_hwtstamp_set = bnx2x_hwtstamp_set,
};
static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev,
@@ -14216,7 +14216,6 @@ static pci_ers_result_t bnx2x_io_slot_reset(struct pci_dev *pdev)
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (netif_running(dev))
bnx2x_set_power_state(bp, PCI_D0);
@@ -15350,31 +15349,57 @@ int bnx2x_configure_ptp_filters(struct bnx2x *bp)
return 0;
}
-static int bnx2x_hwtstamp_ioctl(struct bnx2x *bp, struct ifreq *ifr)
+static int bnx2x_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config config;
+ struct bnx2x *bp = netdev_priv(dev);
int rc;
- DP(BNX2X_MSG_PTP, "HWTSTAMP IOCTL called\n");
+ DP(BNX2X_MSG_PTP, "HWTSTAMP SET called\n");
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
+ if (!netif_running(dev)) {
+ NL_SET_ERR_MSG_MOD(extack, "Device is down");
+ return -EAGAIN;
+ }
DP(BNX2X_MSG_PTP, "Requested tx_type: %d, requested rx_filters = %d\n",
- config.tx_type, config.rx_filter);
+ config->tx_type, config->rx_filter);
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_ON:
+ case HWTSTAMP_TX_OFF:
+ break;
+ default:
+ NL_SET_ERR_MSG_MOD(extack,
+ "One-step timestamping is not supported");
+ return -ERANGE;
+ }
bp->hwtstamp_ioctl_called = true;
- bp->tx_type = config.tx_type;
- bp->rx_filter = config.rx_filter;
+ bp->tx_type = config->tx_type;
+ bp->rx_filter = config->rx_filter;
rc = bnx2x_configure_ptp_filters(bp);
- if (rc)
+ if (rc) {
+ NL_SET_ERR_MSG_MOD(extack, "HW configuration failure");
return rc;
+ }
+
+ config->rx_filter = bp->rx_filter;
+
+ return 0;
+}
+
+static int bnx2x_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct bnx2x *bp = netdev_priv(dev);
- config.rx_filter = bp->rx_filter;
+ config->rx_filter = bp->rx_filter;
+ config->tx_type = bp->tx_type;
- return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
+ return 0;
}
/* Configures HW for PTP */
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 3fc33b1b4dfb..d17d0ea89c36 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -877,7 +877,7 @@ static bool __bnxt_tx_int(struct bnxt *bp, struct bnxt_tx_ring_info *txr,
next_tx_int:
cons = NEXT_TX(cons);
- dev_consume_skb_any(skb);
+ napi_consume_skb(skb, budget);
}
WRITE_ONCE(txr->tx_cons, cons);
@@ -4479,7 +4479,14 @@ static void bnxt_init_one_rx_agg_ring_rxbd(struct bnxt *bp,
ring->fw_ring_id = INVALID_HW_RING_ID;
if ((bp->flags & BNXT_FLAG_AGG_RINGS)) {
type = ((u32)BNXT_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) |
- RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP;
+ RX_BD_TYPE_RX_AGG_BD;
+
+ /* On P7, setting EOP will cause the chip to disable
+ * Relaxed Ordering (RO) for TPA data. Disable EOP for
+ * potentially higher performance with RO.
+ */
+ if (BNXT_CHIP_P5_AND_MINUS(bp) || !(bp->flags & BNXT_FLAG_TPA))
+ type |= RX_BD_FLAGS_AGG_EOP;
bnxt_init_rxbd_pages(ring, type);
}
@@ -5688,6 +5695,10 @@ int bnxt_hwrm_func_drv_rgtr(struct bnxt *bp, unsigned long *bmap, int bmap_size,
u16 cmd = bnxt_vf_req_snif[i];
unsigned int bit, idx;
+ if ((bp->fw_cap & BNXT_FW_CAP_LINK_ADMIN) &&
+ cmd == HWRM_PORT_PHY_QCFG)
+ continue;
+
idx = cmd / 32;
bit = cmd % 32;
data[idx] |= 1 << bit;
@@ -8506,6 +8517,11 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp)
if (flags & FUNC_QCFG_RESP_FLAGS_ENABLE_RDMA_SRIOV)
bp->fw_cap |= BNXT_FW_CAP_ENABLE_RDMA_SRIOV;
+ if (resp->roce_bidi_opt_mode &
+ FUNC_QCFG_RESP_ROCE_BIDI_OPT_MODE_DEDICATED)
+ bp->cos0_cos1_shared = 1;
+ else
+ bp->cos0_cos1_shared = 0;
switch (resp->port_partition_type) {
case FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR1_0:
@@ -9653,6 +9669,8 @@ static int __bnxt_hwrm_func_qcaps(struct bnxt *bp)
bp->flags |= BNXT_FLAG_ROCEV1_CAP;
if (flags & FUNC_QCAPS_RESP_FLAGS_ROCE_V2_SUPPORTED)
bp->flags |= BNXT_FLAG_ROCEV2_CAP;
+ if (flags & FUNC_QCAPS_RESP_FLAGS_LINK_ADMIN_STATUS_SUPPORTED)
+ bp->fw_cap |= BNXT_FW_CAP_LINK_ADMIN;
if (flags & FUNC_QCAPS_RESP_FLAGS_PCIE_STATS_SUPPORTED)
bp->fw_cap |= BNXT_FW_CAP_PCIE_STATS_SUPPORTED;
if (flags & FUNC_QCAPS_RESP_FLAGS_HOT_RESET_CAPABLE)
@@ -12439,7 +12457,7 @@ static int bnxt_try_recover_fw(struct bnxt *bp)
return -ENODEV;
}
-static void bnxt_clear_reservations(struct bnxt *bp, bool fw_reset)
+void bnxt_clear_reservations(struct bnxt *bp, bool fw_reset)
{
struct bnxt_hw_resc *hw_resc = &bp->hw_resc;
@@ -14020,11 +14038,19 @@ static void bnxt_dump_rx_sw_state(struct bnxt_napi *bnapi)
static void bnxt_dump_cp_sw_state(struct bnxt_napi *bnapi)
{
- struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring;
- int i = bnapi->index;
+ struct bnxt_cp_ring_info *cpr = &bnapi->cp_ring, *cpr2;
+ int i = bnapi->index, j;
netdev_info(bnapi->bp->dev, "[%d]: cp{fw_ring: %d raw_cons: %x}\n",
i, cpr->cp_ring_struct.fw_ring_id, cpr->cp_raw_cons);
+ for (j = 0; j < cpr->cp_ring_count; j++) {
+ cpr2 = &cpr->cp_ring_arr[j];
+ if (!cpr2->bnapi)
+ continue;
+ netdev_info(bnapi->bp->dev, "[%d.%d]: cp{fw_ring: %d raw_cons: %x}\n",
+ i, j, cpr2->cp_ring_struct.fw_ring_id,
+ cpr2->cp_raw_cons);
+ }
}
static void bnxt_dbg_dump_states(struct bnxt *bp)
@@ -16892,6 +16918,10 @@ static void bnxt_shutdown(struct pci_dev *pdev)
if (netif_running(dev))
netif_close(dev);
+ if (bnxt_hwrm_func_drv_unrgtr(bp)) {
+ pcie_flr(pdev);
+ goto shutdown_exit;
+ }
bnxt_ptp_clear(bp);
bnxt_clear_int_mode(bp);
pci_disable_device(pdev);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 741b2d854789..f5f07a7e6b29 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -131,6 +131,7 @@ struct rx_bd {
#define RX_BD_TYPE_48B_BD_SIZE (2 << 4)
#define RX_BD_TYPE_64B_BD_SIZE (3 << 4)
#define RX_BD_FLAGS_SOP (1 << 6)
+ #define RX_BD_FLAGS_AGG_EOP (1 << 6)
#define RX_BD_FLAGS_EOP (1 << 7)
#define RX_BD_FLAGS_BUFFERS (3 << 8)
#define RX_BD_FLAGS_1_BUFFER_PACKET (0 << 8)
@@ -2149,7 +2150,7 @@ struct bnxt_bs_trace_info {
static inline void bnxt_bs_trace_check_wrap(struct bnxt_bs_trace_info *bs_trace,
u32 offset)
{
- if (!bs_trace->wrapped &&
+ if (!bs_trace->wrapped && bs_trace->magic_byte &&
*bs_trace->magic_byte != BNXT_TRACE_BUF_MAGIC_BYTE)
bs_trace->wrapped = 1;
bs_trace->last_offset = offset;
@@ -2424,6 +2425,7 @@ struct bnxt {
u8 tc_to_qidx[BNXT_MAX_QUEUE];
u8 q_ids[BNXT_MAX_QUEUE];
u8 max_q;
+ u8 cos0_cos1_shared;
u8 num_tc;
u16 max_pfcwd_tmo_ms;
@@ -2482,6 +2484,7 @@ struct bnxt {
#define BNXT_FW_CAP_ROCE_VF_RESC_MGMT_SUPPORTED BIT_ULL(6)
#define BNXT_FW_CAP_KONG_MB_CHNL BIT_ULL(7)
#define BNXT_FW_CAP_ROCE_VF_DYN_ALLOC_SUPPORT BIT_ULL(8)
+ #define BNXT_FW_CAP_LINK_ADMIN BIT_ULL(9)
#define BNXT_FW_CAP_OVS_64BIT_HANDLE BIT_ULL(10)
#define BNXT_FW_CAP_TRUSTED_VF BIT_ULL(11)
#define BNXT_FW_CAP_ERROR_RECOVERY BIT_ULL(13)
@@ -2941,6 +2944,7 @@ void bnxt_report_link(struct bnxt *bp);
int bnxt_update_link(struct bnxt *bp, bool chng_link_state);
int bnxt_hwrm_set_pause(struct bnxt *);
int bnxt_hwrm_set_link_setting(struct bnxt *, bool, bool);
+void bnxt_clear_reservations(struct bnxt *bp, bool fw_reset);
int bnxt_cancel_reservations(struct bnxt *bp, bool fw_reset);
int bnxt_hwrm_alloc_wol_fltr(struct bnxt *bp);
int bnxt_hwrm_free_wol_fltr(struct bnxt *bp);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_coredump.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_coredump.c
index 0181ab1f2dfd..ccb8b509662d 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_coredump.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_coredump.c
@@ -333,13 +333,14 @@ static void bnxt_fill_drv_seg_record(struct bnxt *bp,
u32 offset = 0;
int rc = 0;
+ record->max_entries = cpu_to_le32(ctxm->max_entries);
+ record->entry_size = cpu_to_le32(ctxm->entry_size);
+
rc = bnxt_dbg_hwrm_log_buffer_flush(bp, type, 0, &offset);
if (rc)
return;
bnxt_bs_trace_check_wrap(bs_trace, offset);
- record->max_entries = cpu_to_le32(ctxm->max_entries);
- record->entry_size = cpu_to_le32(ctxm->entry_size);
record->offset = cpu_to_le32(bs_trace->last_offset);
record->wrapped = bs_trace->wrapped;
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
index 02961d93ed35..15de802bbac4 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_devlink.c
@@ -461,7 +461,7 @@ static int bnxt_dl_reload_down(struct devlink *dl, bool netns_change,
rtnl_unlock();
break;
}
- bnxt_cancel_reservations(bp, false);
+ bnxt_clear_reservations(bp, false);
bnxt_free_ctx_mem(bp, false);
break;
}
@@ -1086,7 +1086,8 @@ static int bnxt_hwrm_nvm_req(struct bnxt *bp, u32 param_id, void *msg,
}
static int bnxt_dl_nvm_param_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
struct hwrm_nvm_get_variable_input *req;
@@ -1168,7 +1169,8 @@ static int bnxt_dl_msix_validate(struct devlink *dl, u32 id,
}
static int bnxt_remote_dev_reset_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct bnxt *bp = bnxt_get_bp_from_dl(dl);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index 41686a6f84b5..068e191ede19 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -688,16 +688,22 @@ skip_ring_stats:
buf[j] = *(rx_port_stats_ext + n);
}
for (i = 0; i < 8; i++, j++) {
- long n = bnxt_tx_bytes_pri_arr[i].base_off +
- bp->pri2cos_idx[i];
+ u8 cos_idx = bp->pri2cos_idx[i];
+ long n;
+ n = bnxt_tx_bytes_pri_arr[i].base_off + cos_idx;
buf[j] = *(tx_port_stats_ext + n);
+ if (bp->cos0_cos1_shared && !cos_idx)
+ buf[j] += *(tx_port_stats_ext + n + 1);
}
for (i = 0; i < 8; i++, j++) {
- long n = bnxt_tx_pkts_pri_arr[i].base_off +
- bp->pri2cos_idx[i];
+ u8 cos_idx = bp->pri2cos_idx[i];
+ long n;
+ n = bnxt_tx_pkts_pri_arr[i].base_off + cos_idx;
buf[j] = *(tx_port_stats_ext + n);
+ if (bp->cos0_cos1_shared && !cos_idx)
+ buf[j] += *(tx_port_stats_ext + n + 1);
}
}
}
@@ -1764,6 +1770,13 @@ static int bnxt_set_rxfh_fields(struct net_device *dev,
return rc;
}
+static u32 bnxt_get_rx_ring_count(struct net_device *dev)
+{
+ struct bnxt *bp = netdev_priv(dev);
+
+ return bp->rx_nr_rings;
+}
+
static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
@@ -1771,10 +1784,6 @@ static int bnxt_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
int rc = 0;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = bp->rx_nr_rings;
- break;
-
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = bp->ntp_fltr_count;
cmd->data = bp->max_fltr | RX_CLS_LOC_SPECIAL;
@@ -4617,6 +4626,11 @@ static int bnxt_get_module_status(struct bnxt *bp, struct netlink_ext_ack *extac
PORT_PHY_QCFG_RESP_MODULE_STATUS_WARNINGMSG)
return 0;
+ if (bp->link_info.phy_type == PORT_PHY_QCFG_RESP_PHY_TYPE_BASET ||
+ bp->link_info.phy_type == PORT_PHY_QCFG_RESP_PHY_TYPE_BASETE){
+ NL_SET_ERR_MSG_MOD(extack, "Operation not supported as PHY type is Base-T");
+ return -EOPNOTSUPP;
+ }
switch (bp->link_info.module_status) {
case PORT_PHY_QCFG_RESP_MODULE_STATUS_PWRDOWN:
NL_SET_ERR_MSG_MOD(extack, "Transceiver module is powering down");
@@ -5605,6 +5619,7 @@ const struct ethtool_ops bnxt_ethtool_ops = {
.set_channels = bnxt_set_channels,
.get_rxnfc = bnxt_get_rxnfc,
.set_rxnfc = bnxt_set_rxnfc,
+ .get_rx_ring_count = bnxt_get_rx_ring_count,
.get_rxfh_indir_size = bnxt_get_rxfh_indir_size,
.get_rxfh_key_size = bnxt_get_rxfh_key_size,
.get_rxfh = bnxt_get_rxfh,
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
index db81cf6d5289..a8a74f07bb54 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c
@@ -952,7 +952,6 @@ static int bnxt_ptp_pps_init(struct bnxt *bp)
snprintf(ptp_info->pin_config[i].name,
sizeof(ptp_info->pin_config[i].name), "bnxt_pps%d", i);
ptp_info->pin_config[i].index = i;
- ptp_info->pin_config[i].chan = i;
if (*pin_usg == BNXT_PPS_PIN_PPS_IN)
ptp_info->pin_config[i].func = PTP_PF_EXTTS;
else if (*pin_usg == BNXT_PPS_PIN_PPS_OUT)
@@ -969,6 +968,8 @@ static int bnxt_ptp_pps_init(struct bnxt *bp)
ptp_info->n_per_out = 1;
ptp_info->pps = 1;
ptp_info->verify = bnxt_ptp_verify;
+ ptp_info->supported_extts_flags = PTP_RISING_EDGE | PTP_STRICT_FLAGS;
+ ptp_info->supported_perout_flags = PTP_PEROUT_DUTY_CYCLE;
return 0;
}
@@ -1051,9 +1052,9 @@ static void bnxt_ptp_free(struct bnxt *bp)
if (ptp->ptp_clock) {
ptp_clock_unregister(ptp->ptp_clock);
ptp->ptp_clock = NULL;
- kfree(ptp->ptp_info.pin_config);
- ptp->ptp_info.pin_config = NULL;
}
+ kfree(ptp->ptp_info.pin_config);
+ ptp->ptp_info.pin_config = NULL;
}
int bnxt_ptp_init(struct bnxt *bp)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
index 80fed2c07b9e..be7deb9cc410 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c
@@ -332,6 +332,38 @@ int bnxt_set_vf_bw(struct net_device *dev, int vf_id, int min_tx_rate,
return rc;
}
+static int bnxt_set_vf_link_admin_state(struct bnxt *bp, int vf_id)
+{
+ struct hwrm_func_cfg_input *req;
+ struct bnxt_vf_info *vf;
+ int rc;
+
+ if (!(bp->fw_cap & BNXT_FW_CAP_LINK_ADMIN))
+ return 0;
+
+ vf = &bp->pf.vf[vf_id];
+
+ rc = bnxt_hwrm_func_cfg_short_req_init(bp, &req);
+ if (rc)
+ return rc;
+
+ req->fid = cpu_to_le16(vf->fw_fid);
+ switch (vf->flags & (BNXT_VF_LINK_FORCED | BNXT_VF_LINK_UP)) {
+ case BNXT_VF_LINK_FORCED:
+ req->options =
+ FUNC_CFG_REQ_OPTIONS_LINK_ADMIN_STATE_FORCED_DOWN;
+ break;
+ case (BNXT_VF_LINK_FORCED | BNXT_VF_LINK_UP):
+ req->options = FUNC_CFG_REQ_OPTIONS_LINK_ADMIN_STATE_FORCED_UP;
+ break;
+ default:
+ req->options = FUNC_CFG_REQ_OPTIONS_LINK_ADMIN_STATE_AUTO;
+ break;
+ }
+ req->enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_ADMIN_LINK_STATE);
+ return hwrm_req_send(bp, req);
+}
+
int bnxt_set_vf_link_state(struct net_device *dev, int vf_id, int link)
{
struct bnxt *bp = netdev_priv(dev);
@@ -357,10 +389,11 @@ int bnxt_set_vf_link_state(struct net_device *dev, int vf_id, int link)
break;
default:
netdev_err(bp->dev, "Invalid link option\n");
- rc = -EINVAL;
- break;
+ return -EINVAL;
}
- if (vf->flags & (BNXT_VF_LINK_UP | BNXT_VF_LINK_FORCED))
+ if (bp->fw_cap & BNXT_FW_CAP_LINK_ADMIN)
+ rc = bnxt_set_vf_link_admin_state(bp, vf_id);
+ else if (vf->flags & (BNXT_VF_LINK_UP | BNXT_VF_LINK_FORCED))
rc = bnxt_hwrm_fwd_async_event_cmpl(bp, vf,
ASYNC_EVENT_CMPL_EVENT_ID_LINK_STATUS_CHANGE);
return rc;
@@ -666,15 +699,21 @@ static int bnxt_hwrm_func_vf_resc_cfg(struct bnxt *bp, int num_vfs, bool reset)
hwrm_req_hold(bp, req);
for (i = 0; i < num_vfs; i++) {
+ struct bnxt_vf_info *vf = &pf->vf[i];
+
+ vf->fw_fid = pf->first_vf_id + i;
+ rc = bnxt_set_vf_link_admin_state(bp, i);
+ if (rc)
+ break;
+
if (reset)
__bnxt_set_vf_params(bp, i);
- req->vf_id = cpu_to_le16(pf->first_vf_id + i);
+ req->vf_id = cpu_to_le16(vf->fw_fid);
rc = hwrm_req_send(bp, req);
if (rc)
break;
pf->active_vfs = i + 1;
- pf->vf[i].fw_fid = pf->first_vf_id + i;
}
if (pf->active_vfs) {
@@ -741,6 +780,12 @@ static int bnxt_hwrm_func_cfg(struct bnxt *bp, int num_vfs)
FUNC_CFG_REQ_ENABLES_NUM_VNICS |
FUNC_CFG_REQ_ENABLES_NUM_HW_RING_GRPS);
+ if (bp->fw_cap & BNXT_FW_CAP_LINK_ADMIN) {
+ req->options = FUNC_CFG_REQ_OPTIONS_LINK_ADMIN_STATE_AUTO;
+ req->enables |=
+ cpu_to_le32(FUNC_CFG_REQ_ENABLES_ADMIN_LINK_STATE);
+ }
+
mtu = bp->dev->mtu + VLAN_ETH_HLEN;
req->mru = cpu_to_le16(mtu);
req->admin_mtu = cpu_to_le16(mtu);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
index f8c2c72b382d..927971c362f1 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
@@ -142,7 +142,6 @@ int bnxt_register_dev(struct bnxt_en_dev *edev,
edev->ulp_tbl->msix_requested = bnxt_get_ulp_msix_num(bp);
bnxt_fill_msix_vecs(bp, bp->edev->msix_entries);
- edev->flags |= BNXT_EN_FLAG_MSIX_REQUESTED;
exit:
mutex_unlock(&edev->en_dev_lock);
netdev_unlock(dev);
@@ -159,8 +158,6 @@ void bnxt_unregister_dev(struct bnxt_en_dev *edev)
ulp = edev->ulp_tbl;
netdev_lock(dev);
mutex_lock(&edev->en_dev_lock);
- if (ulp->msix_requested)
- edev->flags &= ~BNXT_EN_FLAG_MSIX_REQUESTED;
edev->ulp_tbl->msix_requested = 0;
if (ulp->max_async_event_id)
@@ -298,7 +295,7 @@ void bnxt_ulp_irq_stop(struct bnxt *bp)
struct bnxt_ulp_ops *ops;
bool reset = false;
- if (!edev || !(edev->flags & BNXT_EN_FLAG_MSIX_REQUESTED))
+ if (!edev)
return;
if (bnxt_ulp_registered(bp->edev)) {
@@ -321,7 +318,7 @@ void bnxt_ulp_irq_restart(struct bnxt *bp, int err)
struct bnxt_en_dev *edev = bp->edev;
struct bnxt_ulp_ops *ops;
- if (!edev || !(edev->flags & BNXT_EN_FLAG_MSIX_REQUESTED))
+ if (!edev)
return;
if (bnxt_ulp_registered(bp->edev)) {
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
index 7b9dd8ebe4bc..3c5b8a53f715 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.h
@@ -58,7 +58,6 @@ struct bnxt_en_dev {
#define BNXT_EN_FLAG_ROCEV2_CAP 0x2
#define BNXT_EN_FLAG_ROCE_CAP (BNXT_EN_FLAG_ROCEV1_CAP | \
BNXT_EN_FLAG_ROCEV2_CAP)
- #define BNXT_EN_FLAG_MSIX_REQUESTED 0x4
#define BNXT_EN_FLAG_ULP_STOPPED 0x8
#define BNXT_EN_FLAG_VF 0x10
#define BNXT_EN_VF(edev) ((edev)->flags & BNXT_EN_FLAG_VF)
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index 98971ae4f87d..05512aa10c20 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -35,7 +35,6 @@
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/phy.h>
-#include <linux/platform_data/bcmgenet.h>
#include <linux/unaligned.h>
@@ -1641,6 +1640,13 @@ static int bcmgenet_get_num_flows(struct bcmgenet_priv *priv)
return res;
}
+static u32 bcmgenet_get_rx_ring_count(struct net_device *dev)
+{
+ struct bcmgenet_priv *priv = netdev_priv(dev);
+
+ return priv->hw_params->rx_queues ?: 1;
+}
+
static int bcmgenet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
@@ -1650,9 +1656,6 @@ static int bcmgenet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
int i = 0;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = priv->hw_params->rx_queues ?: 1;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = bcmgenet_get_num_flows(priv);
cmd->data = MAX_NUM_OF_FS_RULES | RX_CLS_LOC_SPECIAL;
@@ -1701,6 +1704,7 @@ static const struct ethtool_ops bcmgenet_ethtool_ops = {
.get_ts_info = ethtool_op_get_ts_info,
.get_rxnfc = bcmgenet_get_rxnfc,
.set_rxnfc = bcmgenet_set_rxnfc,
+ .get_rx_ring_count = bcmgenet_get_rx_ring_count,
.get_pauseparam = bcmgenet_get_pauseparam,
.set_pauseparam = bcmgenet_set_pauseparam,
};
@@ -3926,7 +3930,6 @@ MODULE_DEVICE_TABLE(of, bcmgenet_match);
static int bcmgenet_probe(struct platform_device *pdev)
{
- struct bcmgenet_platform_data *pd = pdev->dev.platform_data;
const struct bcmgenet_plat_data *pdata;
struct bcmgenet_priv *priv;
struct net_device *dev;
@@ -4010,9 +4013,6 @@ static int bcmgenet_probe(struct platform_device *pdev)
priv->version = pdata->version;
priv->dma_max_burst_length = pdata->dma_max_burst_length;
priv->flags = pdata->flags;
- } else {
- priv->version = pd->genet_version;
- priv->dma_max_burst_length = DMA_MAX_BURST_LENGTH;
}
priv->clk = devm_clk_get_optional(&priv->pdev->dev, "enet");
@@ -4062,16 +4062,13 @@ static int bcmgenet_probe(struct platform_device *pdev)
if (device_get_phy_mode(&pdev->dev) == PHY_INTERFACE_MODE_INTERNAL)
bcmgenet_power_up(priv, GENET_POWER_PASSIVE);
- if (pd && !IS_ERR_OR_NULL(pd->mac_address))
- eth_hw_addr_set(dev, pd->mac_address);
- else
- if (device_get_ethdev_address(&pdev->dev, dev))
- if (has_acpi_companion(&pdev->dev)) {
- u8 addr[ETH_ALEN];
+ if (device_get_ethdev_address(&pdev->dev, dev))
+ if (has_acpi_companion(&pdev->dev)) {
+ u8 addr[ETH_ALEN];
- bcmgenet_get_hw_addr(priv, addr);
- eth_hw_addr_set(dev, addr);
- }
+ bcmgenet_get_hw_addr(priv, addr);
+ eth_hw_addr_set(dev, addr);
+ }
if (!is_valid_ether_addr(dev->dev_addr)) {
dev_warn(&pdev->dev, "using random Ethernet MAC\n");
diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c
index 573e8b279e52..38f854b94a79 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmmii.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c
@@ -20,7 +20,6 @@
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
-#include <linux/platform_data/bcmgenet.h>
#include <linux/platform_data/mdio-bcm-unimac.h>
#include "bcmgenet.h"
@@ -436,23 +435,6 @@ static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv)
return priv->mdio_dn;
}
-static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv,
- struct unimac_mdio_pdata *ppd)
-{
- struct device *kdev = &priv->pdev->dev;
- struct bcmgenet_platform_data *pd = kdev->platform_data;
-
- if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
- /*
- * Internal or external PHY with MDIO access
- */
- if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR)
- ppd->phy_mask = 1 << pd->phy_address;
- else
- ppd->phy_mask = 0;
- }
-}
-
static int bcmgenet_mii_wait(void *wait_func_data)
{
struct bcmgenet_priv *priv = wait_func_data;
@@ -467,7 +449,6 @@ static int bcmgenet_mii_wait(void *wait_func_data)
static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
{
struct platform_device *pdev = priv->pdev;
- struct bcmgenet_platform_data *pdata = pdev->dev.platform_data;
struct device_node *dn = pdev->dev.of_node;
struct unimac_mdio_pdata ppd;
struct platform_device *ppdev;
@@ -511,8 +492,6 @@ static int bcmgenet_mii_register(struct bcmgenet_priv *priv)
ppdev->dev.parent = &pdev->dev;
if (dn)
ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv);
- else if (pdata)
- bcmgenet_mii_pdata_init(priv, &ppd);
else
ppd.phy_mask = ~0;
@@ -594,58 +573,6 @@ static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv)
return 0;
}
-static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv)
-{
- struct device *kdev = &priv->pdev->dev;
- struct bcmgenet_platform_data *pd = kdev->platform_data;
- char phy_name[MII_BUS_ID_SIZE + 3];
- char mdio_bus_id[MII_BUS_ID_SIZE];
- struct phy_device *phydev;
-
- snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d",
- UNIMAC_MDIO_DRV_NAME, priv->pdev->id);
-
- if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) {
- snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT,
- mdio_bus_id, pd->phy_address);
-
- /*
- * Internal or external PHY with MDIO access
- */
- phydev = phy_attach(priv->dev, phy_name, pd->phy_interface);
- if (IS_ERR(phydev)) {
- dev_err(kdev, "failed to register PHY device\n");
- return PTR_ERR(phydev);
- }
- } else {
- /*
- * MoCA port or no MDIO access.
- * Use fixed PHY to represent the link layer.
- */
- struct fixed_phy_status fphy_status = {
- .link = 1,
- .speed = pd->phy_speed,
- .duplex = pd->phy_duplex,
- .pause = 0,
- .asym_pause = 0,
- };
-
- phydev = fixed_phy_register(&fphy_status, NULL);
- if (IS_ERR(phydev)) {
- dev_err(kdev, "failed to register fixed PHY device\n");
- return PTR_ERR(phydev);
- }
-
- /* Make sure we initialize MoCA PHYs with a link down */
- phydev->link = 0;
-
- }
-
- priv->phy_interface = pd->phy_interface;
-
- return 0;
-}
-
static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
{
struct device *kdev = &priv->pdev->dev;
@@ -656,7 +583,7 @@ static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv)
else if (has_acpi_companion(kdev))
return bcmgenet_phy_interface_init(priv);
else
- return bcmgenet_mii_pd_init(priv);
+ return -EINVAL;
}
int bcmgenet_mii_init(struct net_device *dev)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index d78cafdb2094..75f66587983d 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12719,29 +12719,17 @@ static int tg3_get_sset_count(struct net_device *dev, int sset)
}
}
-static int tg3_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
- u32 *rules __always_unused)
+static u32 tg3_get_rx_ring_count(struct net_device *dev)
{
struct tg3 *tp = netdev_priv(dev);
if (!tg3_flag(tp, SUPPORT_MSIX))
- return -EOPNOTSUPP;
-
- switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- if (netif_running(tp->dev))
- info->data = tp->rxq_cnt;
- else {
- info->data = num_online_cpus();
- if (info->data > TG3_RSS_MAX_NUM_QS)
- info->data = TG3_RSS_MAX_NUM_QS;
- }
+ return 1;
- return 0;
+ if (netif_running(tp->dev))
+ return tp->rxq_cnt;
- default:
- return -EOPNOTSUPP;
- }
+ return min_t(u32, netif_get_num_default_rss_queues(), tp->rxq_max);
}
static u32 tg3_get_rxfh_indir_size(struct net_device *dev)
@@ -14268,7 +14256,7 @@ static const struct ethtool_ops tg3_ethtool_ops = {
.get_coalesce = tg3_get_coalesce,
.set_coalesce = tg3_set_coalesce,
.get_sset_count = tg3_get_sset_count,
- .get_rxnfc = tg3_get_rxnfc,
+ .get_rx_ring_count = tg3_get_rx_ring_count,
.get_rxfh_indir_size = tg3_get_rxfh_indir_size,
.get_rxfh = tg3_get_rxfh,
.set_rxfh = tg3_set_rxfh,
@@ -18349,7 +18337,6 @@ static pci_ers_result_t tg3_io_slot_reset(struct pci_dev *pdev)
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (!netdev || !netif_running(netdev)) {
rc = PCI_ERS_RESULT_RECOVERED;
diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h
index 0830c48973aa..87414a2ddf6e 100644
--- a/drivers/net/ethernet/cadence/macb.h
+++ b/drivers/net/ethernet/cadence/macb.h
@@ -15,10 +15,6 @@
#include <linux/phy/phy.h>
#include <linux/workqueue.h>
-#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) || defined(CONFIG_MACB_USE_HWSTAMP)
-#define MACB_EXT_DESC
-#endif
-
#define MACB_GREGS_NBR 16
#define MACB_GREGS_VERSION 2
#define MACB_MAX_QUEUES 8
@@ -541,6 +537,8 @@
/* Bitfields in DCFG6. */
#define GEM_PBUF_LSO_OFFSET 27
#define GEM_PBUF_LSO_SIZE 1
+#define GEM_PBUF_RSC_OFFSET 26
+#define GEM_PBUF_RSC_SIZE 1
#define GEM_PBUF_CUTTHRU_OFFSET 25
#define GEM_PBUF_CUTTHRU_SIZE 1
#define GEM_DAW64_OFFSET 23
@@ -756,27 +754,31 @@
#define MACB_MAN_C45_CODE 2
/* Capability mask bits */
-#define MACB_CAPS_ISR_CLEAR_ON_WRITE 0x00000001
-#define MACB_CAPS_USRIO_HAS_CLKEN 0x00000002
-#define MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII 0x00000004
-#define MACB_CAPS_NO_GIGABIT_HALF 0x00000008
-#define MACB_CAPS_USRIO_DISABLED 0x00000010
-#define MACB_CAPS_JUMBO 0x00000020
-#define MACB_CAPS_GEM_HAS_PTP 0x00000040
-#define MACB_CAPS_BD_RD_PREFETCH 0x00000080
-#define MACB_CAPS_NEEDS_RSTONUBR 0x00000100
-#define MACB_CAPS_MIIONRGMII 0x00000200
-#define MACB_CAPS_NEED_TSUCLK 0x00000400
-#define MACB_CAPS_QUEUE_DISABLE 0x00000800
-#define MACB_CAPS_QBV 0x00001000
-#define MACB_CAPS_PCS 0x01000000
-#define MACB_CAPS_HIGH_SPEED 0x02000000
-#define MACB_CAPS_CLK_HW_CHG 0x04000000
-#define MACB_CAPS_MACB_IS_EMAC 0x08000000
-#define MACB_CAPS_FIFO_MODE 0x10000000
-#define MACB_CAPS_GIGABIT_MODE_AVAILABLE 0x20000000
-#define MACB_CAPS_SG_DISABLED 0x40000000
-#define MACB_CAPS_MACB_IS_GEM 0x80000000
+#define MACB_CAPS_ISR_CLEAR_ON_WRITE BIT(0)
+#define MACB_CAPS_USRIO_HAS_CLKEN BIT(1)
+#define MACB_CAPS_USRIO_DEFAULT_IS_MII_GMII BIT(2)
+#define MACB_CAPS_NO_GIGABIT_HALF BIT(3)
+#define MACB_CAPS_USRIO_DISABLED BIT(4)
+#define MACB_CAPS_JUMBO BIT(5)
+#define MACB_CAPS_GEM_HAS_PTP BIT(6)
+#define MACB_CAPS_BD_RD_PREFETCH BIT(7)
+#define MACB_CAPS_NEEDS_RSTONUBR BIT(8)
+#define MACB_CAPS_MIIONRGMII BIT(9)
+#define MACB_CAPS_NEED_TSUCLK BIT(10)
+#define MACB_CAPS_QUEUE_DISABLE BIT(11)
+#define MACB_CAPS_QBV BIT(12)
+#define MACB_CAPS_PCS BIT(13)
+#define MACB_CAPS_HIGH_SPEED BIT(14)
+#define MACB_CAPS_CLK_HW_CHG BIT(15)
+#define MACB_CAPS_MACB_IS_EMAC BIT(16)
+#define MACB_CAPS_FIFO_MODE BIT(17)
+#define MACB_CAPS_GIGABIT_MODE_AVAILABLE BIT(18)
+#define MACB_CAPS_SG_DISABLED BIT(19)
+#define MACB_CAPS_MACB_IS_GEM BIT(20)
+#define MACB_CAPS_DMA_64B BIT(21)
+#define MACB_CAPS_DMA_PTP BIT(22)
+#define MACB_CAPS_RSC BIT(23)
+#define MACB_CAPS_NO_LSO BIT(24)
/* LSO settings */
#define MACB_LSO_UFO_ENABLE 0x01
@@ -853,12 +855,6 @@ struct macb_dma_desc {
u32 ctrl;
};
-#ifdef MACB_EXT_DESC
-#define HW_DMA_CAP_32B 0
-#define HW_DMA_CAP_64B (1 << 0)
-#define HW_DMA_CAP_PTP (1 << 1)
-#define HW_DMA_CAP_64B_PTP (HW_DMA_CAP_64B | HW_DMA_CAP_PTP)
-
struct macb_dma_desc_64 {
u32 addrh;
u32 resvd;
@@ -868,7 +864,6 @@ struct macb_dma_desc_ptp {
u32 ts_1;
u32 ts_2;
};
-#endif
/* DMA descriptor bitfields */
#define MACB_RX_USED_OFFSET 0
@@ -1299,7 +1294,6 @@ struct macb {
unsigned int tx_ring_size;
unsigned int num_queues;
- unsigned int queue_mask;
struct macb_queue queues[MACB_MAX_QUEUES];
spinlock_t lock;
@@ -1347,11 +1341,8 @@ struct macb {
struct macb_ptp_info *ptp_info; /* macb-ptp interface */
- struct phy *sgmii_phy; /* for ZynqMP SGMII mode */
+ struct phy *phy;
-#ifdef MACB_EXT_DESC
- uint8_t hw_dma_cap;
-#endif
spinlock_t tsu_clk_lock; /* gem tsu clock locking */
unsigned int tsu_rate;
struct ptp_clock *ptp_clock;
@@ -1443,6 +1434,18 @@ static inline u64 enst_max_hw_interval(u32 speed_mbps)
ENST_TIME_GRANULARITY_NS * 1000, (speed_mbps));
}
+static inline bool macb_dma64(struct macb *bp)
+{
+ return IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
+ bp->caps & MACB_CAPS_DMA_64B;
+}
+
+static inline bool macb_dma_ptp(struct macb *bp)
+{
+ return IS_ENABLED(CONFIG_MACB_USE_HWSTAMP) &&
+ bp->caps & MACB_CAPS_DMA_PTP;
+}
+
/**
* struct macb_platform_data - platform data for MACB Ethernet used for PCI registration
* @pclk: platform clock
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index ca2386b83473..e461f5072884 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -6,36 +6,36 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/clk.h>
+#include <linux/circ_buf.h>
#include <linux/clk-provider.h>
+#include <linux/clk.h>
#include <linux/crc32.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/circ_buf.h>
-#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <linux/firmware/xlnx-zynqmp.h>
+#include <linux/inetdevice.h>
#include <linux/init.h>
-#include <linux/io.h>
#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/phylink.h>
#include <linux/of.h>
#include <linux/of_mdio.h>
#include <linux/of_net.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-#include <linux/tcp.h>
-#include <linux/iopoll.h>
#include <linux/phy/phy.h>
+#include <linux/phylink.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/ptp_classify.h>
#include <linux/reset.h>
-#include <linux/firmware/xlnx-zynqmp.h>
-#include <linux/inetdevice.h>
+#include <linux/slab.h>
+#include <linux/tcp.h>
+#include <linux/types.h>
+#include <linux/udp.h>
#include <net/pkt_sched.h>
#include "macb.h"
@@ -121,56 +121,26 @@ struct sifive_fu540_macb_mgmt {
*/
static unsigned int macb_dma_desc_get_size(struct macb *bp)
{
-#ifdef MACB_EXT_DESC
- unsigned int desc_size;
+ unsigned int desc_size = sizeof(struct macb_dma_desc);
+
+ if (macb_dma64(bp))
+ desc_size += sizeof(struct macb_dma_desc_64);
+ if (macb_dma_ptp(bp))
+ desc_size += sizeof(struct macb_dma_desc_ptp);
- switch (bp->hw_dma_cap) {
- case HW_DMA_CAP_64B:
- desc_size = sizeof(struct macb_dma_desc)
- + sizeof(struct macb_dma_desc_64);
- break;
- case HW_DMA_CAP_PTP:
- desc_size = sizeof(struct macb_dma_desc)
- + sizeof(struct macb_dma_desc_ptp);
- break;
- case HW_DMA_CAP_64B_PTP:
- desc_size = sizeof(struct macb_dma_desc)
- + sizeof(struct macb_dma_desc_64)
- + sizeof(struct macb_dma_desc_ptp);
- break;
- default:
- desc_size = sizeof(struct macb_dma_desc);
- }
return desc_size;
-#endif
- return sizeof(struct macb_dma_desc);
}
static unsigned int macb_adj_dma_desc_idx(struct macb *bp, unsigned int desc_idx)
{
-#ifdef MACB_EXT_DESC
- switch (bp->hw_dma_cap) {
- case HW_DMA_CAP_64B:
- case HW_DMA_CAP_PTP:
- desc_idx <<= 1;
- break;
- case HW_DMA_CAP_64B_PTP:
- desc_idx *= 3;
- break;
- default:
- break;
- }
-#endif
- return desc_idx;
+ return desc_idx * (1 + macb_dma64(bp) + macb_dma_ptp(bp));
}
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
static struct macb_dma_desc_64 *macb_64b_desc(struct macb *bp, struct macb_dma_desc *desc)
{
return (struct macb_dma_desc_64 *)((void *)desc
+ sizeof(struct macb_dma_desc));
}
-#endif
/* Ring buffer accessors */
static unsigned int macb_tx_ring_wrap(struct macb *bp, unsigned int index)
@@ -357,7 +327,6 @@ static int macb_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum)
status = MACB_BFEXT(DATA, macb_readl(bp, MAN));
mdio_read_exit:
- pm_runtime_mark_last_busy(&bp->pdev->dev);
pm_runtime_put_autosuspend(&bp->pdev->dev);
mdio_pm_exit:
return status;
@@ -403,7 +372,6 @@ static int macb_mdio_read_c45(struct mii_bus *bus, int mii_id, int devad,
status = MACB_BFEXT(DATA, macb_readl(bp, MAN));
mdio_read_exit:
- pm_runtime_mark_last_busy(&bp->pdev->dev);
pm_runtime_put_autosuspend(&bp->pdev->dev);
mdio_pm_exit:
return status;
@@ -435,7 +403,6 @@ static int macb_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,
goto mdio_write_exit;
mdio_write_exit:
- pm_runtime_mark_last_busy(&bp->pdev->dev);
pm_runtime_put_autosuspend(&bp->pdev->dev);
mdio_pm_exit:
return status;
@@ -481,7 +448,6 @@ static int macb_mdio_write_c45(struct mii_bus *bus, int mii_id,
goto mdio_write_exit;
mdio_write_exit:
- pm_runtime_mark_last_busy(&bp->pdev->dev);
pm_runtime_put_autosuspend(&bp->pdev->dev);
mdio_pm_exit:
return status;
@@ -492,15 +458,13 @@ static void macb_init_buffers(struct macb *bp)
struct macb_queue *queue;
unsigned int q;
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
/* Single register for all queues' high 32 bits. */
- if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
+ if (macb_dma64(bp)) {
macb_writel(bp, RBQPH,
upper_32_bits(bp->queues[0].rx_ring_dma));
macb_writel(bp, TBQPH,
upper_32_bits(bp->queues[0].tx_ring_dma));
}
-#endif
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
queue_writel(queue, RBQP, lower_32_bits(queue->rx_ring_dma));
@@ -1025,10 +989,9 @@ static void macb_tx_unmap(struct macb *bp, struct macb_tx_skb *tx_skb, int budge
static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, dma_addr_t addr)
{
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- struct macb_dma_desc_64 *desc_64;
+ if (macb_dma64(bp)) {
+ struct macb_dma_desc_64 *desc_64;
- if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
desc_64 = macb_64b_desc(bp, desc);
desc_64->addrh = upper_32_bits(addr);
/* The low bits of RX address contain the RX_USED bit, clearing
@@ -1037,26 +1000,23 @@ static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, dma_addr_
*/
dma_wmb();
}
-#endif
+
desc->addr = lower_32_bits(addr);
}
static dma_addr_t macb_get_addr(struct macb *bp, struct macb_dma_desc *desc)
{
dma_addr_t addr = 0;
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- struct macb_dma_desc_64 *desc_64;
- if (bp->hw_dma_cap & HW_DMA_CAP_64B) {
+ if (macb_dma64(bp)) {
+ struct macb_dma_desc_64 *desc_64;
+
desc_64 = macb_64b_desc(bp, desc);
addr = ((u64)(desc_64->addrh) << 32);
}
-#endif
addr |= MACB_BF(RX_WADDR, MACB_BFEXT(RX_WADDR, desc->addr));
-#ifdef CONFIG_MACB_USE_HWSTAMP
- if (bp->hw_dma_cap & HW_DMA_CAP_PTP)
+ if (macb_dma_ptp(bp))
addr &= ~GEM_BIT(DMA_RXVALID);
-#endif
return addr;
}
@@ -1336,8 +1296,19 @@ static void gem_rx_refill(struct macb_queue *queue)
dma_wmb();
macb_set_addr(bp, desc, paddr);
- /* properly align Ethernet header */
- skb_reserve(skb, NET_IP_ALIGN);
+ /* Properly align Ethernet header.
+ *
+ * Hardware can add dummy bytes if asked using the RBOF
+ * field inside the NCFGR register. That feature isn't
+ * available if hardware is RSC capable.
+ *
+ * We cannot fallback to doing the 2-byte shift before
+ * DMA mapping because the address field does not allow
+ * setting the low 2/3 bits.
+ * It is 3 bits if HW_DMA_CAP_PTP, else 2 bits.
+ */
+ if (!(bp->caps & MACB_CAPS_RSC))
+ skb_reserve(skb, NET_IP_ALIGN);
} else {
desc->ctrl = 0;
dma_wmb();
@@ -2024,14 +1995,14 @@ static unsigned int macb_tx_map(struct macb *bp,
struct sk_buff *skb,
unsigned int hdrlen)
{
- dma_addr_t mapping;
- unsigned int len, entry, i, tx_head = queue->tx_head;
- struct macb_tx_skb *tx_skb = NULL;
- struct macb_dma_desc *desc;
- unsigned int offset, size, count = 0;
unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags;
- unsigned int eof = 1, mss_mfs = 0;
+ unsigned int len, i, tx_head = queue->tx_head;
u32 ctrl, lso_ctrl = 0, seq_ctrl = 0;
+ unsigned int eof = 1, mss_mfs = 0;
+ struct macb_tx_skb *tx_skb = NULL;
+ struct macb_dma_desc *desc;
+ unsigned int offset, size;
+ dma_addr_t mapping;
/* LSO */
if (skb_shinfo(skb)->gso_size != 0) {
@@ -2051,8 +2022,7 @@ static unsigned int macb_tx_map(struct macb *bp,
offset = 0;
while (len) {
- entry = macb_tx_ring_wrap(bp, tx_head);
- tx_skb = &queue->tx_skb[entry];
+ tx_skb = macb_tx_skb(queue, tx_head);
mapping = dma_map_single(&bp->pdev->dev,
skb->data + offset,
@@ -2068,10 +2038,9 @@ static unsigned int macb_tx_map(struct macb *bp,
len -= size;
offset += size;
- count++;
tx_head++;
- size = min(len, bp->max_tx_length);
+ size = umin(len, bp->max_tx_length);
}
/* Then, map paged data from fragments */
@@ -2081,9 +2050,8 @@ static unsigned int macb_tx_map(struct macb *bp,
len = skb_frag_size(frag);
offset = 0;
while (len) {
- size = min(len, bp->max_tx_length);
- entry = macb_tx_ring_wrap(bp, tx_head);
- tx_skb = &queue->tx_skb[entry];
+ size = umin(len, bp->max_tx_length);
+ tx_skb = macb_tx_skb(queue, tx_head);
mapping = skb_frag_dma_map(&bp->pdev->dev, frag,
offset, size, DMA_TO_DEVICE);
@@ -2098,7 +2066,6 @@ static unsigned int macb_tx_map(struct macb *bp,
len -= size;
offset += size;
- count++;
tx_head++;
}
}
@@ -2120,9 +2087,8 @@ static unsigned int macb_tx_map(struct macb *bp,
* to set the end of TX queue
*/
i = tx_head;
- entry = macb_tx_ring_wrap(bp, i);
ctrl = MACB_BIT(TX_USED);
- desc = macb_tx_desc(queue, entry);
+ desc = macb_tx_desc(queue, i);
desc->ctrl = ctrl;
if (lso_ctrl) {
@@ -2142,16 +2108,15 @@ static unsigned int macb_tx_map(struct macb *bp,
do {
i--;
- entry = macb_tx_ring_wrap(bp, i);
- tx_skb = &queue->tx_skb[entry];
- desc = macb_tx_desc(queue, entry);
+ tx_skb = macb_tx_skb(queue, i);
+ desc = macb_tx_desc(queue, i);
ctrl = (u32)tx_skb->size;
if (eof) {
ctrl |= MACB_BIT(TX_LAST);
eof = 0;
}
- if (unlikely(entry == (bp->tx_ring_size - 1)))
+ if (unlikely(macb_tx_ring_wrap(bp, i) == bp->tx_ring_size - 1))
ctrl |= MACB_BIT(TX_WRAP);
/* First descriptor is header descriptor */
@@ -2179,7 +2144,7 @@ static unsigned int macb_tx_map(struct macb *bp,
queue->tx_head = tx_head;
- return count;
+ return 0;
dma_error:
netdev_err(bp->dev, "TX DMA map failed\n");
@@ -2190,7 +2155,7 @@ dma_error:
macb_tx_unmap(bp, tx_skb, 0);
}
- return 0;
+ return -ENOMEM;
}
static netdev_features_t macb_features_check(struct sk_buff *skb,
@@ -2318,11 +2283,9 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
return ret;
}
-#ifdef CONFIG_MACB_USE_HWSTAMP
- if ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
- (bp->hw_dma_cap & HW_DMA_CAP_PTP))
+ if (macb_dma_ptp(bp) &&
+ (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS;
-#endif
is_lso = (skb_shinfo(skb)->gso_size != 0);
@@ -2339,7 +2302,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
return NETDEV_TX_BUSY;
}
} else
- hdrlen = min(skb_headlen(skb), bp->max_tx_length);
+ hdrlen = umin(skb_headlen(skb), bp->max_tx_length);
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
netdev_vdbg(bp->dev,
@@ -2378,7 +2341,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
/* Map socket buffer for DMA transfer */
- if (!macb_tx_map(bp, queue, skb, hdrlen)) {
+ if (macb_tx_map(bp, queue, skb, hdrlen)) {
dev_kfree_skb_any(skb);
goto unlock;
}
@@ -2799,14 +2762,10 @@ static void macb_configure_dma(struct macb *bp)
dmacfg &= ~GEM_BIT(TXCOEN);
dmacfg &= ~GEM_BIT(ADDR64);
-#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
- if (bp->hw_dma_cap & HW_DMA_CAP_64B)
+ if (macb_dma64(bp))
dmacfg |= GEM_BIT(ADDR64);
-#endif
-#ifdef CONFIG_MACB_USE_HWSTAMP
- if (bp->hw_dma_cap & HW_DMA_CAP_PTP)
+ if (macb_dma_ptp(bp))
dmacfg |= GEM_BIT(RXEXT) | GEM_BIT(TXEXT);
-#endif
netdev_dbg(bp->dev, "Cadence configure DMA with 0x%08x\n",
dmacfg);
gem_writel(bp, DMACFG, dmacfg);
@@ -2821,7 +2780,11 @@ static void macb_init_hw(struct macb *bp)
macb_set_hwaddr(bp);
config = macb_mdc_clk_div(bp);
- config |= MACB_BF(RBOF, NET_IP_ALIGN); /* Make eth data aligned */
+ /* Make eth data aligned.
+ * If RSC capable, that offset is ignored by HW.
+ */
+ if (!(bp->caps & MACB_CAPS_RSC))
+ config |= MACB_BF(RBOF, NET_IP_ALIGN);
config |= MACB_BIT(DRFCS); /* Discard Rx FCS */
if (bp->caps & MACB_CAPS_JUMBO)
config |= MACB_BIT(JFRAME); /* Enable jumbo frames */
@@ -2998,7 +2961,11 @@ static int macb_open(struct net_device *dev)
macb_init_hw(bp);
- err = phy_power_on(bp->sgmii_phy);
+ err = phy_set_mode_ext(bp->phy, PHY_MODE_ETHERNET, bp->phy_interface);
+ if (err)
+ goto reset_hw;
+
+ err = phy_power_on(bp->phy);
if (err)
goto reset_hw;
@@ -3014,7 +2981,7 @@ static int macb_open(struct net_device *dev)
return 0;
phy_off:
- phy_power_off(bp->sgmii_phy);
+ phy_power_off(bp->phy);
reset_hw:
macb_reset_hw(bp);
@@ -3046,7 +3013,7 @@ static int macb_close(struct net_device *dev)
phylink_stop(bp->phylink);
phylink_disconnect_phy(bp->phylink);
- phy_power_off(bp->sgmii_phy);
+ phy_power_off(bp->phy);
spin_lock_irqsave(&bp->lock, flags);
macb_reset_hw(bp);
@@ -3582,7 +3549,7 @@ static int gem_get_ts_info(struct net_device *dev,
{
struct macb *bp = netdev_priv(dev);
- if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0) {
+ if (!macb_dma_ptp(bp)) {
ethtool_op_get_ts_info(dev, info);
return 0;
}
@@ -4108,6 +4075,8 @@ static int macb_taprio_setup_replace(struct net_device *ndev,
struct macb *bp = netdev_priv(ndev);
struct ethtool_link_ksettings kset;
struct macb_queue *queue;
+ u32 queue_mask;
+ u8 queue_id;
size_t i;
int err;
@@ -4159,8 +4128,9 @@ static int macb_taprio_setup_replace(struct net_device *ndev,
goto cleanup;
}
- /* gate_mask must not select queues outside the valid queue_mask */
- if (entry->gate_mask & ~bp->queue_mask) {
+ /* gate_mask must not select queues outside the valid queues */
+ queue_id = order_base_2(entry->gate_mask);
+ if (queue_id >= bp->num_queues) {
netdev_err(ndev, "Entry %zu: gate_mask 0x%x exceeds queue range (max_queues=%d)\n",
i, entry->gate_mask, bp->num_queues);
err = -EINVAL;
@@ -4194,7 +4164,7 @@ static int macb_taprio_setup_replace(struct net_device *ndev,
goto cleanup;
}
- enst_queue[i].queue_id = order_base_2(entry->gate_mask);
+ enst_queue[i].queue_id = queue_id;
enst_queue[i].start_time_mask =
(start_time_sec << GEM_START_TIME_SEC_OFFSET) |
start_time_nsec;
@@ -4222,8 +4192,9 @@ static int macb_taprio_setup_replace(struct net_device *ndev,
/* All validations passed - proceed with hardware configuration */
scoped_guard(spinlock_irqsave, &bp->lock) {
/* Disable ENST queues if running before configuring */
+ queue_mask = BIT_U32(bp->num_queues) - 1;
gem_writel(bp, ENST_CONTROL,
- bp->queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET);
+ queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET);
for (i = 0; i < conf->num_entries; i++) {
queue = &bp->queues[enst_queue[i].queue_id];
@@ -4252,15 +4223,16 @@ static void macb_taprio_destroy(struct net_device *ndev)
{
struct macb *bp = netdev_priv(ndev);
struct macb_queue *queue;
- u32 enst_disable_mask;
+ u32 queue_mask;
unsigned int q;
netdev_reset_tc(ndev);
- enst_disable_mask = bp->queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET;
+ queue_mask = BIT_U32(bp->num_queues) - 1;
scoped_guard(spinlock_irqsave, &bp->lock) {
/* Single disable command for all queues */
- gem_writel(bp, ENST_CONTROL, enst_disable_mask);
+ gem_writel(bp, ENST_CONTROL,
+ queue_mask << GEM_ENST_DISABLE_QUEUE_OFFSET);
/* Clear all queue ENST registers in batch */
for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
@@ -4364,13 +4336,15 @@ static void macb_configure_caps(struct macb *bp,
dcfg = gem_readl(bp, DCFG2);
if ((dcfg & (GEM_BIT(RX_PKT_BUFF) | GEM_BIT(TX_PKT_BUFF))) == 0)
bp->caps |= MACB_CAPS_FIFO_MODE;
+ if (GEM_BFEXT(PBUF_RSC, gem_readl(bp, DCFG6)))
+ bp->caps |= MACB_CAPS_RSC;
if (gem_has_ptp(bp)) {
if (!GEM_BFEXT(TSU, gem_readl(bp, DCFG5)))
dev_err(&bp->pdev->dev,
"GEM doesn't support hardware ptp.\n");
else {
#ifdef CONFIG_MACB_USE_HWSTAMP
- bp->hw_dma_cap |= HW_DMA_CAP_PTP;
+ bp->caps |= MACB_CAPS_DMA_PTP;
bp->ptp_info = &gem_ptp_info;
#endif
}
@@ -4383,26 +4357,25 @@ static void macb_configure_caps(struct macb *bp,
dev_dbg(&bp->pdev->dev, "Cadence caps 0x%08x\n", bp->caps);
}
-static void macb_probe_queues(void __iomem *mem,
- bool native_io,
- unsigned int *queue_mask,
- unsigned int *num_queues)
+static int macb_probe_queues(struct device *dev, void __iomem *mem, bool native_io)
{
- *queue_mask = 0x1;
- *num_queues = 1;
+ /* BIT(0) is never set but queue 0 always exists. */
+ unsigned int queue_mask = 0x1;
- /* is it macb or gem ?
- *
- * We need to read directly from the hardware here because
- * we are early in the probe process and don't have the
- * MACB_CAPS_MACB_IS_GEM flag positioned
- */
- if (!hw_is_gem(mem, native_io))
- return;
+ /* Use hw_is_gem() as MACB_CAPS_MACB_IS_GEM is not yet positioned. */
+ if (hw_is_gem(mem, native_io)) {
+ if (native_io)
+ queue_mask |= __raw_readl(mem + GEM_DCFG6) & 0xFF;
+ else
+ queue_mask |= readl_relaxed(mem + GEM_DCFG6) & 0xFF;
- /* bit 0 is never set but queue 0 always exists */
- *queue_mask |= readl_relaxed(mem + GEM_DCFG6) & 0xff;
- *num_queues = hweight32(*queue_mask);
+ if (fls(queue_mask) != ffz(queue_mask)) {
+ dev_err(dev, "queue mask %#x has a hole\n", queue_mask);
+ return -EINVAL;
+ }
+ }
+
+ return hweight32(queue_mask);
}
static void macb_clks_disable(struct clk *pclk, struct clk *hclk, struct clk *tx_clk,
@@ -4520,10 +4493,7 @@ static int macb_init(struct platform_device *pdev)
* register mapping but we don't want to test the queue index then
* compute the corresponding register offset at run time.
*/
- for (hw_q = 0, q = 0; hw_q < MACB_MAX_QUEUES; ++hw_q) {
- if (!(bp->queue_mask & (1 << hw_q)))
- continue;
-
+ for (hw_q = 0, q = 0; hw_q < bp->num_queues; ++hw_q) {
queue = &bp->queues[q];
queue->bp = bp;
spin_lock_init(&queue->tx_ptr_lock);
@@ -4594,8 +4564,11 @@ static int macb_init(struct platform_device *pdev)
/* Set features */
dev->hw_features = NETIF_F_SG;
- /* Check LSO capability */
- if (GEM_BFEXT(PBUF_LSO, gem_readl(bp, DCFG6)))
+ /* Check LSO capability; runtime detection can be overridden by a cap
+ * flag if the hardware is known to be buggy
+ */
+ if (!(bp->caps & MACB_CAPS_NO_LSO) &&
+ GEM_BFEXT(PBUF_LSO, gem_readl(bp, DCFG6)))
dev->hw_features |= MACB_NETIF_LSO;
/* Checksum offload is only available on gem with packet buffer */
@@ -4614,8 +4587,8 @@ static int macb_init(struct platform_device *pdev)
* each 4-tuple define requires 1 T2 screener reg + 3 compare regs
*/
reg = gem_readl(bp, DCFG8);
- bp->max_tuples = min((GEM_BFEXT(SCR2CMP, reg) / 3),
- GEM_BFEXT(T2SCR, reg));
+ bp->max_tuples = umin((GEM_BFEXT(SCR2CMP, reg) / 3),
+ GEM_BFEXT(T2SCR, reg));
INIT_LIST_HEAD(&bp->rx_fs_list.list);
if (bp->max_tuples > 0) {
/* also needs one ethtype match to check IPv4 */
@@ -5168,13 +5141,13 @@ static int init_reset_optional(struct platform_device *pdev)
if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) {
/* Ensure PHY device used in SGMII mode is ready */
- bp->sgmii_phy = devm_phy_optional_get(&pdev->dev, NULL);
+ bp->phy = devm_phy_optional_get(&pdev->dev, NULL);
- if (IS_ERR(bp->sgmii_phy))
- return dev_err_probe(&pdev->dev, PTR_ERR(bp->sgmii_phy),
+ if (IS_ERR(bp->phy))
+ return dev_err_probe(&pdev->dev, PTR_ERR(bp->phy),
"failed to get SGMII PHY\n");
- ret = phy_init(bp->sgmii_phy);
+ ret = phy_init(bp->phy);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to init SGMII PHY\n");
@@ -5203,7 +5176,7 @@ static int init_reset_optional(struct platform_device *pdev)
/* Fully reset controller at hardware level if mapped in device tree */
ret = device_reset_optional(&pdev->dev);
if (ret) {
- phy_exit(bp->sgmii_phy);
+ phy_exit(bp->phy);
return dev_err_probe(&pdev->dev, ret, "failed to reset controller");
}
@@ -5211,8 +5184,30 @@ static int init_reset_optional(struct platform_device *pdev)
err_out_phy_exit:
if (ret)
- phy_exit(bp->sgmii_phy);
+ phy_exit(bp->phy);
+
+ return ret;
+}
+
+static int eyeq5_init(struct platform_device *pdev)
+{
+ struct net_device *netdev = platform_get_drvdata(pdev);
+ struct macb *bp = netdev_priv(netdev);
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ bp->phy = devm_phy_get(dev, NULL);
+ if (IS_ERR(bp->phy))
+ return dev_err_probe(dev, PTR_ERR(bp->phy),
+ "failed to get PHY\n");
+
+ ret = phy_init(bp->phy);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init PHY\n");
+ ret = macb_init(pdev);
+ if (ret)
+ phy_exit(bp->phy);
return ret;
}
@@ -5370,6 +5365,17 @@ static const struct macb_config versal_config = {
.usrio = &macb_default_usrio,
};
+static const struct macb_config eyeq5_config = {
+ .caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_JUMBO |
+ MACB_CAPS_GEM_HAS_PTP | MACB_CAPS_QUEUE_DISABLE |
+ MACB_CAPS_NO_LSO,
+ .dma_burst_length = 16,
+ .clk_init = macb_clk_init,
+ .init = eyeq5_init,
+ .jumbo_max_len = 10240,
+ .usrio = &macb_default_usrio,
+};
+
static const struct macb_config raspberrypi_rp1_config = {
.caps = MACB_CAPS_GIGABIT_MODE_AVAILABLE | MACB_CAPS_CLK_HW_CHG |
MACB_CAPS_JUMBO |
@@ -5401,6 +5407,7 @@ static const struct of_device_id macb_dt_ids[] = {
{ .compatible = "microchip,mpfs-macb", .data = &mpfs_config },
{ .compatible = "microchip,sama7g5-gem", .data = &sama7g5_gem_config },
{ .compatible = "microchip,sama7g5-emac", .data = &sama7g5_emac_config },
+ { .compatible = "mobileye,eyeq5-gem", .data = &eyeq5_config },
{ .compatible = "raspberrypi,rp1-gem", .data = &raspberrypi_rp1_config },
{ .compatible = "xlnx,zynqmp-gem", .data = &zynqmp_config},
{ .compatible = "xlnx,zynq-gem", .data = &zynq_config },
@@ -5424,21 +5431,17 @@ static const struct macb_config default_gem_config = {
static int macb_probe(struct platform_device *pdev)
{
const struct macb_config *macb_config = &default_gem_config;
- int (*clk_init)(struct platform_device *, struct clk **,
- struct clk **, struct clk **, struct clk **,
- struct clk **) = macb_config->clk_init;
- int (*init)(struct platform_device *) = macb_config->init;
struct device_node *np = pdev->dev.of_node;
struct clk *pclk, *hclk = NULL, *tx_clk = NULL, *rx_clk = NULL;
struct clk *tsu_clk = NULL;
- unsigned int queue_mask, num_queues;
- bool native_io;
phy_interface_t interface;
struct net_device *dev;
struct resource *regs;
u32 wtrmrk_rst_val;
void __iomem *mem;
struct macb *bp;
+ int num_queues;
+ bool native_io;
int err, val;
mem = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
@@ -5449,14 +5452,11 @@ static int macb_probe(struct platform_device *pdev)
const struct of_device_id *match;
match = of_match_node(macb_dt_ids, np);
- if (match && match->data) {
+ if (match && match->data)
macb_config = match->data;
- clk_init = macb_config->clk_init;
- init = macb_config->init;
- }
}
- err = clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk);
+ err = macb_config->clk_init(pdev, &pclk, &hclk, &tx_clk, &rx_clk, &tsu_clk);
if (err)
return err;
@@ -5467,7 +5467,12 @@ static int macb_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
native_io = hw_is_native_io(mem);
- macb_probe_queues(mem, native_io, &queue_mask, &num_queues);
+ num_queues = macb_probe_queues(&pdev->dev, mem, native_io);
+ if (num_queues < 0) {
+ err = num_queues;
+ goto err_disable_clocks;
+ }
+
dev = alloc_etherdev_mq(sizeof(*bp), num_queues);
if (!dev) {
err = -ENOMEM;
@@ -5491,16 +5496,13 @@ static int macb_probe(struct platform_device *pdev)
bp->macb_reg_writel = hw_writel;
}
bp->num_queues = num_queues;
- bp->queue_mask = queue_mask;
- if (macb_config)
- bp->dma_burst_length = macb_config->dma_burst_length;
+ bp->dma_burst_length = macb_config->dma_burst_length;
bp->pclk = pclk;
bp->hclk = hclk;
bp->tx_clk = tx_clk;
bp->rx_clk = rx_clk;
bp->tsu_clk = tsu_clk;
- if (macb_config)
- bp->jumbo_max_len = macb_config->jumbo_max_len;
+ bp->jumbo_max_len = macb_config->jumbo_max_len;
if (!hw_is_gem(bp->regs, bp->native_io))
bp->max_tx_length = MACB_MAX_TX_LEN;
@@ -5546,7 +5548,7 @@ static int macb_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to set DMA mask\n");
goto err_out_free_netdev;
}
- bp->hw_dma_cap |= HW_DMA_CAP_64B;
+ bp->caps |= MACB_CAPS_DMA_64B;
}
#endif
platform_set_drvdata(pdev, dev);
@@ -5594,7 +5596,7 @@ static int macb_probe(struct platform_device *pdev)
bp->phy_interface = interface;
/* IP specific init */
- err = init(pdev);
+ err = macb_config->init(pdev);
if (err)
goto err_out_free_netdev;
@@ -5616,7 +5618,6 @@ static int macb_probe(struct platform_device *pdev)
macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID),
dev->base_addr, dev->irq, dev->dev_addr);
- pm_runtime_mark_last_busy(&bp->pdev->dev);
pm_runtime_put_autosuspend(&bp->pdev->dev);
return 0;
@@ -5626,7 +5627,7 @@ err_out_unregister_mdio:
mdiobus_free(bp->mii_bus);
err_out_phy_exit:
- phy_exit(bp->sgmii_phy);
+ phy_exit(bp->phy);
err_out_free_netdev:
free_netdev(dev);
@@ -5650,7 +5651,7 @@ static void macb_remove(struct platform_device *pdev)
if (dev) {
bp = netdev_priv(dev);
unregister_netdev(dev);
- phy_exit(bp->sgmii_phy);
+ phy_exit(bp->phy);
mdiobus_unregister(bp->mii_bus);
mdiobus_free(bp->mii_bus);
@@ -5677,7 +5678,7 @@ static int __maybe_unused macb_suspend(struct device *dev)
u32 tmp;
if (!device_may_wakeup(&bp->dev->dev))
- phy_exit(bp->sgmii_phy);
+ phy_exit(bp->phy);
if (!netif_running(netdev))
return 0;
@@ -5806,7 +5807,7 @@ static int __maybe_unused macb_resume(struct device *dev)
int err;
if (!device_may_wakeup(&bp->dev->dev))
- phy_init(bp->sgmii_phy);
+ phy_init(bp->phy);
if (!netif_running(netdev))
return 0;
diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c
index a63bf29c4fa8..c9e77819196e 100644
--- a/drivers/net/ethernet/cadence/macb_ptp.c
+++ b/drivers/net/ethernet/cadence/macb_ptp.c
@@ -28,14 +28,16 @@
static struct macb_dma_desc_ptp *macb_ptp_desc(struct macb *bp,
struct macb_dma_desc *desc)
{
- if (bp->hw_dma_cap == HW_DMA_CAP_PTP)
- return (struct macb_dma_desc_ptp *)
- ((u8 *)desc + sizeof(struct macb_dma_desc));
- if (bp->hw_dma_cap == HW_DMA_CAP_64B_PTP)
+ if (!macb_dma_ptp(bp))
+ return NULL;
+
+ if (macb_dma64(bp))
return (struct macb_dma_desc_ptp *)
((u8 *)desc + sizeof(struct macb_dma_desc)
+ sizeof(struct macb_dma_desc_64));
- return NULL;
+ else
+ return (struct macb_dma_desc_ptp *)
+ ((u8 *)desc + sizeof(struct macb_dma_desc));
}
static int gem_tsu_get_time(struct ptp_clock_info *ptp, struct timespec64 *ts,
@@ -380,7 +382,7 @@ int gem_get_hwtst(struct net_device *dev,
struct macb *bp = netdev_priv(dev);
*tstamp_config = bp->tstamp_config;
- if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0)
+ if (!macb_dma_ptp(bp))
return -EOPNOTSUPP;
return 0;
@@ -407,7 +409,7 @@ int gem_set_hwtst(struct net_device *dev,
struct macb *bp = netdev_priv(dev);
u32 regval;
- if ((bp->hw_dma_cap & HW_DMA_CAP_PTP) == 0)
+ if (!macb_dma_ptp(bp))
return -EOPNOTSUPP;
switch (tstamp_config->tx_type) {
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c
index 8e2fcec26ea1..0732440eeacd 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c
@@ -2107,20 +2107,16 @@ liquidio_get_stats64(struct net_device *netdev,
lstats->tx_fifo_errors;
}
-/**
- * hwtstamp_ioctl - Handler for SIOCSHWTSTAMP ioctl
- * @netdev: network device
- * @ifr: interface request
- */
-static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
+static int liquidio_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *conf,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config conf;
struct lio *lio = GET_LIO(netdev);
- if (copy_from_user(&conf, ifr->ifr_data, sizeof(conf)))
- return -EFAULT;
+ if (!lio->oct_dev->ptp_enable)
+ return -EOPNOTSUPP;
- switch (conf.tx_type) {
+ switch (conf->tx_type) {
case HWTSTAMP_TX_ON:
case HWTSTAMP_TX_OFF:
break;
@@ -2128,7 +2124,7 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
return -ERANGE;
}
- switch (conf.rx_filter) {
+ switch (conf->rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_ALL:
@@ -2146,39 +2142,32 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
- conf.rx_filter = HWTSTAMP_FILTER_ALL;
+ conf->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
- if (conf.rx_filter == HWTSTAMP_FILTER_ALL)
+ if (conf->rx_filter == HWTSTAMP_FILTER_ALL)
ifstate_set(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
else
ifstate_reset(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
- return copy_to_user(ifr->ifr_data, &conf, sizeof(conf)) ? -EFAULT : 0;
+ return 0;
}
-/**
- * liquidio_ioctl - ioctl handler
- * @netdev: network device
- * @ifr: interface request
- * @cmd: command
- */
-static int liquidio_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+static int liquidio_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *conf)
{
struct lio *lio = GET_LIO(netdev);
- switch (cmd) {
- case SIOCSHWTSTAMP:
- if (lio->oct_dev->ptp_enable)
- return hwtstamp_ioctl(netdev, ifr);
- fallthrough;
- default:
- return -EOPNOTSUPP;
- }
+ /* TX timestamping is technically always on */
+ conf->tx_type = HWTSTAMP_TX_ON;
+ conf->rx_filter = ifstate_check(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED) ?
+ HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
+
+ return 0;
}
/**
@@ -3227,7 +3216,6 @@ static const struct net_device_ops lionetdevops = {
.ndo_vlan_rx_add_vid = liquidio_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = liquidio_vlan_rx_kill_vid,
.ndo_change_mtu = liquidio_change_mtu,
- .ndo_eth_ioctl = liquidio_ioctl,
.ndo_fix_features = liquidio_fix_features,
.ndo_set_features = liquidio_set_features,
.ndo_set_vf_mac = liquidio_set_vf_mac,
@@ -3238,6 +3226,8 @@ static const struct net_device_ops lionetdevops = {
.ndo_set_vf_link_state = liquidio_set_vf_link_state,
.ndo_get_vf_stats = liquidio_get_vf_stats,
.ndo_get_port_parent_id = liquidio_get_port_parent_id,
+ .ndo_hwtstamp_get = liquidio_hwtstamp_get,
+ .ndo_hwtstamp_set = liquidio_hwtstamp_set,
};
/**
diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
index 3230dff5ba05..e02942dbbcce 100644
--- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
+++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c
@@ -1236,20 +1236,13 @@ liquidio_get_stats64(struct net_device *netdev,
lstats->tx_carrier_errors;
}
-/**
- * hwtstamp_ioctl - Handler for SIOCSHWTSTAMP ioctl
- * @netdev: network device
- * @ifr: interface request
- */
-static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
+static int liquidio_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *conf,
+ struct netlink_ext_ack *extack)
{
struct lio *lio = GET_LIO(netdev);
- struct hwtstamp_config conf;
-
- if (copy_from_user(&conf, ifr->ifr_data, sizeof(conf)))
- return -EFAULT;
- switch (conf.tx_type) {
+ switch (conf->tx_type) {
case HWTSTAMP_TX_ON:
case HWTSTAMP_TX_OFF:
break;
@@ -1257,7 +1250,7 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
return -ERANGE;
}
- switch (conf.rx_filter) {
+ switch (conf->rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_ALL:
@@ -1275,35 +1268,31 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
- conf.rx_filter = HWTSTAMP_FILTER_ALL;
+ conf->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
- if (conf.rx_filter == HWTSTAMP_FILTER_ALL)
+ if (conf->rx_filter == HWTSTAMP_FILTER_ALL)
ifstate_set(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
else
ifstate_reset(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED);
- return copy_to_user(ifr->ifr_data, &conf, sizeof(conf)) ? -EFAULT : 0;
+ return 0;
}
-/**
- * liquidio_ioctl - ioctl handler
- * @netdev: network device
- * @ifr: interface request
- * @cmd: command
- */
-static int liquidio_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+static int liquidio_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *conf)
{
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return hwtstamp_ioctl(netdev, ifr);
- default:
- return -EOPNOTSUPP;
- }
+ struct lio *lio = GET_LIO(netdev);
+
+ /* TX timestamping is techically always on */
+ conf->tx_type = HWTSTAMP_TX_ON;
+ conf->rx_filter = ifstate_check(lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED) ?
+ HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
+ return 0;
}
static void handle_timestamp(struct octeon_device *oct, u32 status, void *buf)
@@ -1881,9 +1870,10 @@ static const struct net_device_ops lionetdevops = {
.ndo_vlan_rx_add_vid = liquidio_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = liquidio_vlan_rx_kill_vid,
.ndo_change_mtu = liquidio_change_mtu,
- .ndo_eth_ioctl = liquidio_ioctl,
.ndo_fix_features = liquidio_fix_features,
.ndo_set_features = liquidio_set_features,
+ .ndo_hwtstamp_get = liquidio_hwtstamp_get,
+ .ndo_hwtstamp_set = liquidio_hwtstamp_set,
};
static int lio_nic_info(struct octeon_recv_info *recv_info, void *buf)
diff --git a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
index 393b9951490a..c190fc6538d4 100644
--- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c
@@ -690,19 +690,16 @@ static irqreturn_t octeon_mgmt_interrupt(int cpl, void *dev_id)
return IRQ_HANDLED;
}
-static int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
- struct ifreq *rq, int cmd)
+static int octeon_mgmt_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct octeon_mgmt *p = netdev_priv(netdev);
- struct hwtstamp_config config;
- union cvmx_mio_ptp_clock_cfg ptp;
union cvmx_agl_gmx_rxx_frm_ctl rxx_frm_ctl;
+ union cvmx_mio_ptp_clock_cfg ptp;
bool have_hw_timestamps = false;
- if (copy_from_user(&config, rq->ifr_data, sizeof(config)))
- return -EFAULT;
-
- /* Check the status of hardware for tiemstamps */
+ /* Check the status of hardware for timestamps */
if (OCTEON_IS_MODEL(OCTEON_CN6XXX)) {
/* Get the current state of the PTP clock */
ptp.u64 = cvmx_read_csr(CVMX_MIO_PTP_CLOCK_CFG);
@@ -733,10 +730,12 @@ static int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
have_hw_timestamps = true;
}
- if (!have_hw_timestamps)
+ if (!have_hw_timestamps) {
+ NL_SET_ERR_MSG_MOD(extack, "HW doesn't support timestamping");
return -EINVAL;
+ }
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
break;
@@ -744,7 +743,7 @@ static int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
p->has_rx_tstamp = false;
rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
@@ -766,33 +765,34 @@ static int octeon_mgmt_ioctl_hwtstamp(struct net_device *netdev,
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
- p->has_rx_tstamp = have_hw_timestamps;
- config.rx_filter = HWTSTAMP_FILTER_ALL;
- if (p->has_rx_tstamp) {
- rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
- rxx_frm_ctl.s.ptp_mode = 1;
- cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
- }
+ p->has_rx_tstamp = true;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ rxx_frm_ctl.u64 = cvmx_read_csr(p->agl + AGL_GMX_RX_FRM_CTL);
+ rxx_frm_ctl.s.ptp_mode = 1;
+ cvmx_write_csr(p->agl + AGL_GMX_RX_FRM_CTL, rxx_frm_ctl.u64);
break;
default:
return -ERANGE;
}
- if (copy_to_user(rq->ifr_data, &config, sizeof(config)))
- return -EFAULT;
-
return 0;
}
-static int octeon_mgmt_ioctl(struct net_device *netdev,
- struct ifreq *rq, int cmd)
+static int octeon_mgmt_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return octeon_mgmt_ioctl_hwtstamp(netdev, rq, cmd);
- default:
- return phy_do_ioctl(netdev, rq, cmd);
- }
+ struct octeon_mgmt *p = netdev_priv(netdev);
+
+ /* Check the status of hardware for timestamps */
+ if (!OCTEON_IS_MODEL(OCTEON_CN6XXX))
+ return -EINVAL;
+
+ config->tx_type = HWTSTAMP_TX_ON;
+ config->rx_filter = p->has_rx_tstamp ?
+ HWTSTAMP_FILTER_ALL :
+ HWTSTAMP_FILTER_NONE;
+
+ return 0;
}
static void octeon_mgmt_disable_link(struct octeon_mgmt *p)
@@ -1370,11 +1370,13 @@ static const struct net_device_ops octeon_mgmt_ops = {
.ndo_start_xmit = octeon_mgmt_xmit,
.ndo_set_rx_mode = octeon_mgmt_set_rx_filtering,
.ndo_set_mac_address = octeon_mgmt_set_mac_address,
- .ndo_eth_ioctl = octeon_mgmt_ioctl,
+ .ndo_eth_ioctl = phy_do_ioctl,
.ndo_change_mtu = octeon_mgmt_change_mtu,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = octeon_mgmt_poll_controller,
#endif
+ .ndo_hwtstamp_get = octeon_mgmt_hwtstamp_get,
+ .ndo_hwtstamp_set = octeon_mgmt_hwtstamp_set,
};
static int octeon_mgmt_probe(struct platform_device *pdev)
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
index fc6053414b7d..413028bdcacb 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c
@@ -541,21 +541,11 @@ static int nicvf_get_rxfh_fields(struct net_device *dev,
return 0;
}
-static int nicvf_get_rxnfc(struct net_device *dev,
- struct ethtool_rxnfc *info, u32 *rules)
+static u32 nicvf_get_rx_ring_count(struct net_device *dev)
{
struct nicvf *nic = netdev_priv(dev);
- int ret = -EOPNOTSUPP;
- switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- info->data = nic->rx_queues;
- ret = 0;
- break;
- default:
- break;
- }
- return ret;
+ return nic->rx_queues;
}
static int nicvf_set_rxfh_fields(struct net_device *dev,
@@ -861,7 +851,7 @@ static const struct ethtool_ops nicvf_ethtool_ops = {
.get_coalesce = nicvf_get_coalesce,
.get_ringparam = nicvf_get_ringparam,
.set_ringparam = nicvf_set_ringparam,
- .get_rxnfc = nicvf_get_rxnfc,
+ .get_rx_ring_count = nicvf_get_rx_ring_count,
.get_rxfh_key_size = nicvf_get_rxfh_key_size,
.get_rxfh_indir_size = nicvf_get_rxfh_indir_size,
.get_rxfh = nicvf_get_rxfh,
diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
index 1be2dc40a1a6..0b6e30a8feb0 100644
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c
@@ -1899,18 +1899,18 @@ static int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp)
}
}
-static int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
+static int nicvf_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config config;
struct nicvf *nic = netdev_priv(netdev);
- if (!nic->ptp_clock)
+ if (!nic->ptp_clock) {
+ NL_SET_ERR_MSG_MOD(extack, "HW timestamping is not supported");
return -ENODEV;
+ }
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
-
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
break;
@@ -1918,7 +1918,7 @@ static int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
nic->hw_rx_tstamp = false;
break;
@@ -1937,7 +1937,7 @@ static int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
nic->hw_rx_tstamp = true;
- config.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
@@ -1946,20 +1946,24 @@ static int nicvf_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
if (netif_running(netdev))
nicvf_config_hw_rx_tstamp(nic, nic->hw_rx_tstamp);
- if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
- return -EFAULT;
-
return 0;
}
-static int nicvf_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
+static int nicvf_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return nicvf_config_hwtstamp(netdev, req);
- default:
- return -EOPNOTSUPP;
- }
+ struct nicvf *nic = netdev_priv(netdev);
+
+ if (!nic->ptp_clock)
+ return -ENODEV;
+
+ /* TX timestamping is technically always on */
+ config->tx_type = HWTSTAMP_TX_ON;
+ config->rx_filter = nic->hw_rx_tstamp ?
+ HWTSTAMP_FILTER_ALL :
+ HWTSTAMP_FILTER_NONE;
+
+ return 0;
}
static void __nicvf_set_rx_mode_task(u8 mode, struct xcast_addr_list *mc_addrs,
@@ -2081,8 +2085,9 @@ static const struct net_device_ops nicvf_netdev_ops = {
.ndo_fix_features = nicvf_fix_features,
.ndo_set_features = nicvf_set_features,
.ndo_bpf = nicvf_xdp,
- .ndo_eth_ioctl = nicvf_ioctl,
.ndo_set_rx_mode = nicvf_set_rx_mode,
+ .ndo_hwtstamp_get = nicvf_hwtstamp_get,
+ .ndo_hwtstamp_set = nicvf_hwtstamp_set,
};
static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index f92a3550e480..3b1321c8ed14 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -2933,7 +2933,6 @@ static int t3_reenable_adapter(struct adapter *adapter)
}
pci_set_master(adapter->pdev);
pci_restore_state(adapter->pdev);
- pci_save_state(adapter->pdev);
/* Free sge resources */
t3_free_sge_resources(adapter);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 0d85198fb03d..f20f4bc58492 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -674,7 +674,7 @@ struct port_info {
struct cxgb_fcoe fcoe;
#endif /* CONFIG_CHELSIO_T4_FCOE */
bool rxtstamp; /* Enable TS */
- struct hwtstamp_config tstamp_config;
+ struct kernel_hwtstamp_config tstamp_config;
bool ptp_enable;
struct sched_table *sched_tbl;
u32 eth_flags;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 392723ef14e5..043733c5c812 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -3042,12 +3042,87 @@ static void cxgb_get_stats(struct net_device *dev,
ns->rx_length_errors + stats.rx_len_err + ns->rx_fifo_errors;
}
+static int cxgb_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct port_info *pi = netdev_priv(dev);
+
+ *config = pi->tstamp_config;
+ return 0;
+}
+
+static int cxgb_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct port_info *pi = netdev_priv(dev);
+ struct adapter *adapter = pi->adapter;
+
+ if (is_t4(adapter->params.chip)) {
+ /* For T4 Adapters */
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ pi->rxtstamp = false;
+ break;
+ case HWTSTAMP_FILTER_ALL:
+ pi->rxtstamp = true;
+ break;
+ default:
+ return -ERANGE;
+ }
+ pi->tstamp_config = *config;
+ return 0;
+ }
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ case HWTSTAMP_TX_ON:
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ pi->rxtstamp = false;
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ cxgb4_ptprx_timestamping(pi, pi->port_id, PTP_TS_L4);
+ break;
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ cxgb4_ptprx_timestamping(pi, pi->port_id, PTP_TS_L2_L4);
+ break;
+ case HWTSTAMP_FILTER_ALL:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ pi->rxtstamp = true;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ if (config->tx_type == HWTSTAMP_TX_OFF &&
+ config->rx_filter == HWTSTAMP_FILTER_NONE) {
+ if (cxgb4_ptp_txtype(adapter, pi->port_id) >= 0)
+ pi->ptp_enable = false;
+ }
+
+ if (config->rx_filter != HWTSTAMP_FILTER_NONE) {
+ if (cxgb4_ptp_redirect_rx_packet(adapter, pi) >= 0)
+ pi->ptp_enable = true;
+ }
+ pi->tstamp_config = *config;
+ return 0;
+}
+
static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
unsigned int mbox;
int ret = 0, prtad, devad;
struct port_info *pi = netdev_priv(dev);
- struct adapter *adapter = pi->adapter;
struct mii_ioctl_data *data = (struct mii_ioctl_data *)&req->ifr_data;
switch (cmd) {
@@ -3076,81 +3151,6 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
ret = t4_mdio_wr(pi->adapter, mbox, prtad, devad,
data->reg_num, data->val_in);
break;
- case SIOCGHWTSTAMP:
- return copy_to_user(req->ifr_data, &pi->tstamp_config,
- sizeof(pi->tstamp_config)) ?
- -EFAULT : 0;
- case SIOCSHWTSTAMP:
- if (copy_from_user(&pi->tstamp_config, req->ifr_data,
- sizeof(pi->tstamp_config)))
- return -EFAULT;
-
- if (!is_t4(adapter->params.chip)) {
- switch (pi->tstamp_config.tx_type) {
- case HWTSTAMP_TX_OFF:
- case HWTSTAMP_TX_ON:
- break;
- default:
- return -ERANGE;
- }
-
- switch (pi->tstamp_config.rx_filter) {
- case HWTSTAMP_FILTER_NONE:
- pi->rxtstamp = false;
- break;
- case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- cxgb4_ptprx_timestamping(pi, pi->port_id,
- PTP_TS_L4);
- break;
- case HWTSTAMP_FILTER_PTP_V2_EVENT:
- cxgb4_ptprx_timestamping(pi, pi->port_id,
- PTP_TS_L2_L4);
- break;
- case HWTSTAMP_FILTER_ALL:
- case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
- pi->rxtstamp = true;
- break;
- default:
- pi->tstamp_config.rx_filter =
- HWTSTAMP_FILTER_NONE;
- return -ERANGE;
- }
-
- if ((pi->tstamp_config.tx_type == HWTSTAMP_TX_OFF) &&
- (pi->tstamp_config.rx_filter ==
- HWTSTAMP_FILTER_NONE)) {
- if (cxgb4_ptp_txtype(adapter, pi->port_id) >= 0)
- pi->ptp_enable = false;
- }
-
- if (pi->tstamp_config.rx_filter !=
- HWTSTAMP_FILTER_NONE) {
- if (cxgb4_ptp_redirect_rx_packet(adapter,
- pi) >= 0)
- pi->ptp_enable = true;
- }
- } else {
- /* For T4 Adapters */
- switch (pi->tstamp_config.rx_filter) {
- case HWTSTAMP_FILTER_NONE:
- pi->rxtstamp = false;
- break;
- case HWTSTAMP_FILTER_ALL:
- pi->rxtstamp = true;
- break;
- default:
- pi->tstamp_config.rx_filter =
- HWTSTAMP_FILTER_NONE;
- return -ERANGE;
- }
- }
- return copy_to_user(req->ifr_data, &pi->tstamp_config,
- sizeof(pi->tstamp_config)) ?
- -EFAULT : 0;
default:
return -EOPNOTSUPP;
}
@@ -3485,7 +3485,7 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate)
struct adapter *adap = pi->adapter;
struct ch_sched_queue qe = { 0 };
struct ch_sched_params p = { 0 };
- struct sched_class *e;
+ struct ch_sched_class *e;
u32 req_rate;
int err = 0;
@@ -3875,6 +3875,8 @@ static const struct net_device_ops cxgb4_netdev_ops = {
.ndo_setup_tc = cxgb_setup_tc,
.ndo_features_check = cxgb_features_check,
.ndo_fix_features = cxgb_fix_features,
+ .ndo_hwtstamp_get = cxgb_hwtstamp_get,
+ .ndo_hwtstamp_set = cxgb_hwtstamp_set,
};
#ifdef CONFIG_PCI_IOV
@@ -5456,7 +5458,6 @@ static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
if (!adap) {
pci_restore_state(pdev);
- pci_save_state(pdev);
return PCI_ERS_RESULT_RECOVERED;
}
@@ -5471,7 +5472,6 @@ static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (t4_wait_dev_ready(adap->regs) < 0)
return PCI_ERS_RESULT_DISCONNECT;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
index 0765d000eaef..e2b5554531b5 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c
@@ -161,20 +161,9 @@ static struct ch_tc_flower_entry *ch_flower_lookup(struct adapter *adap,
static void cxgb4_process_flow_match(struct net_device *dev,
struct flow_rule *rule,
+ u16 addr_type,
struct ch_filter_specification *fs)
{
- u16 addr_type = 0;
-
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
- struct flow_match_control match;
-
- flow_rule_match_control(rule, &match);
- addr_type = match.key->addr_type;
- } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
- addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
- } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
- addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
- }
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
struct flow_match_basic match;
@@ -327,9 +316,6 @@ static int cxgb4_validate_flow_match(struct netlink_ext_ack *extack,
return -EOPNOTSUPP;
}
- if (flow_rule_match_has_control_flags(rule, extack))
- return -EOPNOTSUPP;
-
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
struct flow_match_basic match;
@@ -858,6 +844,7 @@ int cxgb4_flow_rule_replace(struct net_device *dev, struct flow_rule *rule,
{
struct adapter *adap = netdev2adap(dev);
struct filter_ctx ctx;
+ u16 addr_type = 0;
u8 inet_family;
int fidx, ret;
@@ -867,7 +854,28 @@ int cxgb4_flow_rule_replace(struct net_device *dev, struct flow_rule *rule,
if (cxgb4_validate_flow_match(extack, rule))
return -EOPNOTSUPP;
- cxgb4_process_flow_match(dev, rule, fs);
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+ struct flow_match_control match;
+
+ flow_rule_match_control(rule, &match);
+ addr_type = match.key->addr_type;
+
+ if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) {
+ fs->val.frag = match.key->flags & FLOW_DIS_IS_FRAGMENT;
+ fs->mask.frag = true;
+ }
+
+ if (!flow_rule_is_supp_control_flags(FLOW_DIS_IS_FRAGMENT,
+ match.mask->flags, extack))
+ return -EOPNOTSUPP;
+
+ } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
+ addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
+ } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
+ addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
+ }
+
+ cxgb4_process_flow_match(dev, rule, addr_type, fs);
cxgb4_process_flow_actions(dev, &rule->action, fs);
fs->hash = is_filter_exact_match(adap, fs);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c
index 1672d3afe5be..f8dcf0b4abcd 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c
@@ -56,7 +56,7 @@ static int cxgb4_matchall_egress_validate(struct net_device *dev,
struct port_info *pi = netdev2pinfo(dev);
struct flow_action_entry *entry;
struct ch_sched_queue qe;
- struct sched_class *e;
+ struct ch_sched_class *e;
u64 max_link_rate;
u32 i, speed;
int ret;
@@ -180,7 +180,7 @@ static int cxgb4_matchall_alloc_tc(struct net_device *dev,
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
struct flow_action_entry *entry;
- struct sched_class *e;
+ struct ch_sched_class *e;
int ret;
u32 i;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c
index 338b04f339b3..a2dcd2e24263 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_mqprio.c
@@ -330,7 +330,7 @@ static int cxgb4_mqprio_alloc_tc(struct net_device *dev,
struct cxgb4_tc_port_mqprio *tc_port_mqprio;
struct port_info *pi = netdev2pinfo(dev);
struct adapter *adap = netdev2adap(dev);
- struct sched_class *e;
+ struct ch_sched_class *e;
int ret;
u8 i;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sched.c b/drivers/net/ethernet/chelsio/cxgb4/sched.c
index a1b14468d1ff..38a30aeee122 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sched.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sched.c
@@ -44,7 +44,7 @@ static int t4_sched_class_fw_cmd(struct port_info *pi,
{
struct adapter *adap = pi->adapter;
struct sched_table *s = pi->sched_tbl;
- struct sched_class *e;
+ struct ch_sched_class *e;
int err = 0;
e = &s->tab[p->u.params.class];
@@ -122,7 +122,7 @@ static void *t4_sched_entry_lookup(struct port_info *pi,
const u32 val)
{
struct sched_table *s = pi->sched_tbl;
- struct sched_class *e, *end;
+ struct ch_sched_class *e, *end;
void *found = NULL;
/* Look for an entry with matching @val */
@@ -166,8 +166,8 @@ static void *t4_sched_entry_lookup(struct port_info *pi,
return found;
}
-struct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev,
- struct ch_sched_queue *p)
+struct ch_sched_class *cxgb4_sched_queue_lookup(struct net_device *dev,
+ struct ch_sched_queue *p)
{
struct port_info *pi = netdev2pinfo(dev);
struct sched_queue_entry *qe = NULL;
@@ -187,7 +187,7 @@ static int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p)
struct sched_queue_entry *qe = NULL;
struct adapter *adap = pi->adapter;
struct sge_eth_txq *txq;
- struct sched_class *e;
+ struct ch_sched_class *e;
int err = 0;
if (p->queue < 0 || p->queue >= pi->nqsets)
@@ -218,7 +218,7 @@ static int t4_sched_queue_bind(struct port_info *pi, struct ch_sched_queue *p)
struct sched_queue_entry *qe = NULL;
struct adapter *adap = pi->adapter;
struct sge_eth_txq *txq;
- struct sched_class *e;
+ struct ch_sched_class *e;
unsigned int qid;
int err = 0;
@@ -260,7 +260,7 @@ static int t4_sched_flowc_unbind(struct port_info *pi, struct ch_sched_flowc *p)
{
struct sched_flowc_entry *fe = NULL;
struct adapter *adap = pi->adapter;
- struct sched_class *e;
+ struct ch_sched_class *e;
int err = 0;
if (p->tid < 0 || p->tid >= adap->tids.neotids)
@@ -288,7 +288,7 @@ static int t4_sched_flowc_bind(struct port_info *pi, struct ch_sched_flowc *p)
struct sched_table *s = pi->sched_tbl;
struct sched_flowc_entry *fe = NULL;
struct adapter *adap = pi->adapter;
- struct sched_class *e;
+ struct ch_sched_class *e;
int err = 0;
if (p->tid < 0 || p->tid >= adap->tids.neotids)
@@ -322,7 +322,7 @@ out_err:
}
static void t4_sched_class_unbind_all(struct port_info *pi,
- struct sched_class *e,
+ struct ch_sched_class *e,
enum sched_bind_type type)
{
if (!e)
@@ -476,12 +476,12 @@ int cxgb4_sched_class_unbind(struct net_device *dev, void *arg,
}
/* If @p is NULL, fetch any available unused class */
-static struct sched_class *t4_sched_class_lookup(struct port_info *pi,
- const struct ch_sched_params *p)
+static struct ch_sched_class *t4_sched_class_lookup(struct port_info *pi,
+ const struct ch_sched_params *p)
{
struct sched_table *s = pi->sched_tbl;
- struct sched_class *found = NULL;
- struct sched_class *e, *end;
+ struct ch_sched_class *found = NULL;
+ struct ch_sched_class *e, *end;
if (!p) {
/* Get any available unused class */
@@ -522,10 +522,10 @@ static struct sched_class *t4_sched_class_lookup(struct port_info *pi,
return found;
}
-static struct sched_class *t4_sched_class_alloc(struct port_info *pi,
- struct ch_sched_params *p)
+static struct ch_sched_class *t4_sched_class_alloc(struct port_info *pi,
+ struct ch_sched_params *p)
{
- struct sched_class *e = NULL;
+ struct ch_sched_class *e = NULL;
u8 class_id;
int err;
@@ -579,8 +579,8 @@ static struct sched_class *t4_sched_class_alloc(struct port_info *pi,
* scheduling class with matching @p is found, then the matching class is
* returned.
*/
-struct sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
- struct ch_sched_params *p)
+struct ch_sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
+ struct ch_sched_params *p)
{
struct port_info *pi = netdev2pinfo(dev);
u8 class_id;
@@ -607,7 +607,7 @@ void cxgb4_sched_class_free(struct net_device *dev, u8 classid)
struct port_info *pi = netdev2pinfo(dev);
struct sched_table *s = pi->sched_tbl;
struct ch_sched_params p;
- struct sched_class *e;
+ struct ch_sched_class *e;
u32 speed;
int ret;
@@ -640,7 +640,7 @@ void cxgb4_sched_class_free(struct net_device *dev, u8 classid)
}
}
-static void t4_sched_class_free(struct net_device *dev, struct sched_class *e)
+static void t4_sched_class_free(struct net_device *dev, struct ch_sched_class *e)
{
struct port_info *pi = netdev2pinfo(dev);
@@ -660,7 +660,7 @@ struct sched_table *t4_init_sched(unsigned int sched_size)
s->sched_size = sched_size;
for (i = 0; i < s->sched_size; i++) {
- memset(&s->tab[i], 0, sizeof(struct sched_class));
+ memset(&s->tab[i], 0, sizeof(struct ch_sched_class));
s->tab[i].idx = i;
s->tab[i].state = SCHED_STATE_UNUSED;
INIT_LIST_HEAD(&s->tab[i].entry_list);
@@ -682,7 +682,7 @@ void t4_cleanup_sched(struct adapter *adap)
continue;
for (i = 0; i < s->sched_size; i++) {
- struct sched_class *e;
+ struct ch_sched_class *e;
e = &s->tab[i];
if (e->state == SCHED_STATE_ACTIVE)
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sched.h b/drivers/net/ethernet/chelsio/cxgb4/sched.h
index 6b3c778815f0..4d3b5a757536 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sched.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/sched.h
@@ -71,7 +71,7 @@ struct sched_flowc_entry {
struct ch_sched_flowc param;
};
-struct sched_class {
+struct ch_sched_class {
u8 state;
u8 idx;
struct ch_sched_params info;
@@ -82,7 +82,7 @@ struct sched_class {
struct sched_table { /* per port scheduling table */
u8 sched_size;
- struct sched_class tab[] __counted_by(sched_size);
+ struct ch_sched_class tab[] __counted_by(sched_size);
};
static inline bool can_sched(struct net_device *dev)
@@ -103,15 +103,15 @@ static inline bool valid_class_id(struct net_device *dev, u8 class_id)
return true;
}
-struct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev,
- struct ch_sched_queue *p);
+struct ch_sched_class *cxgb4_sched_queue_lookup(struct net_device *dev,
+ struct ch_sched_queue *p);
int cxgb4_sched_class_bind(struct net_device *dev, void *arg,
enum sched_bind_type type);
int cxgb4_sched_class_unbind(struct net_device *dev, void *arg,
enum sched_bind_type type);
-struct sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
- struct ch_sched_params *p);
+struct ch_sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
+ struct ch_sched_params *p);
void cxgb4_sched_class_free(struct net_device *dev, u8 classid);
struct sched_table *t4_init_sched(unsigned int size);
diff --git a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c
index ecd9a0bd5e18..49b57bb5fac1 100644
--- a/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c
+++ b/drivers/net/ethernet/chelsio/inline_crypto/ch_ipsec/chcr_ipsec.c
@@ -290,9 +290,15 @@ static int ch_ipsec_xfrm_add_state(struct net_device *dev,
return -EINVAL;
}
+ if (unlikely(!try_module_get(THIS_MODULE))) {
+ NL_SET_ERR_MSG_MOD(extack, "Failed to acquire module reference");
+ return -ENODEV;
+ }
+
sa_entry = kzalloc(sizeof(*sa_entry), GFP_KERNEL);
if (!sa_entry) {
res = -ENOMEM;
+ module_put(THIS_MODULE);
goto out;
}
@@ -301,7 +307,6 @@ static int ch_ipsec_xfrm_add_state(struct net_device *dev,
sa_entry->esn = 1;
ch_ipsec_setkey(x, sa_entry);
x->xso.offload_handle = (unsigned long)sa_entry;
- try_module_get(THIS_MODULE);
out:
return res;
}
diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c
index 4ee970f3bad6..ee0154337a9c 100644
--- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c
+++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_cm.c
@@ -1199,12 +1199,12 @@ static struct sock *chtls_recv_sock(struct sock *lsk,
struct ipv6_pinfo *newnp = inet6_sk(newsk);
struct ipv6_pinfo *np = inet6_sk(lsk);
- inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
+ newinet->pinet6 = &newtcp6sk->inet6;
+ newinet->ipv6_fl_list = NULL;
memcpy(newnp, np, sizeof(struct ipv6_pinfo));
newsk->sk_v6_daddr = treq->ir_v6_rmt_addr;
newsk->sk_v6_rcv_saddr = treq->ir_v6_loc_addr;
inet6_sk(newsk)->saddr = treq->ir_v6_loc_addr;
- newnp->ipv6_fl_list = NULL;
newnp->pktoptions = NULL;
newsk->sk_bound_dev_if = treq->ir_iif;
newinet->inet_opt = NULL;
diff --git a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c
index 4036db466e18..ee19933e2cca 100644
--- a/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c
+++ b/drivers/net/ethernet/chelsio/inline_crypto/chtls/chtls_io.c
@@ -159,19 +159,13 @@ static u8 tcp_state_to_flowc_state(u8 state)
int send_tx_flowc_wr(struct sock *sk, int compl,
u32 snd_nxt, u32 rcv_nxt)
{
- struct flowc_packed {
- struct fw_flowc_wr fc;
- struct fw_flowc_mnemval mnemval[FW_FLOWC_MNEM_MAX];
- } __packed sflowc;
+ DEFINE_RAW_FLEX(struct fw_flowc_wr, flowc, mnemval, FW_FLOWC_MNEM_MAX);
int nparams, paramidx, flowclen16, flowclen;
- struct fw_flowc_wr *flowc;
struct chtls_sock *csk;
struct tcp_sock *tp;
csk = rcu_dereference_sk_user_data(sk);
tp = tcp_sk(sk);
- memset(&sflowc, 0, sizeof(sflowc));
- flowc = &sflowc.fc;
#define FLOWC_PARAM(__m, __v) \
do { \
diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c
index 7077d705e471..846d58c769ea 100644
--- a/drivers/net/ethernet/dlink/dl2k.c
+++ b/drivers/net/ethernet/dlink/dl2k.c
@@ -41,7 +41,7 @@ module_param(tx_flow, int, 0);
module_param(rx_flow, int, 0);
module_param(copy_thresh, int, 0);
module_param(rx_coalesce, int, 0); /* Rx frame count each interrupt */
-module_param(rx_timeout, int, 0); /* Rx DMA wait time in 64ns increments */
+module_param(rx_timeout, int, 0); /* Rx DMA wait time in 640ns increments */
module_param(tx_coalesce, int, 0); /* HW xmit count each TxDMAComplete */
@@ -262,7 +262,7 @@ rio_probe1 (struct pci_dev *pdev, const struct pci_device_id *ent)
np->link_status = 0;
/* Set media and reset PHY */
if (np->phy_media) {
- /* default Auto-Negotiation for fiber deivices */
+ /* default Auto-Negotiation for fiber devices */
if (np->an_enable == 2) {
np->an_enable = 1;
}
@@ -733,7 +733,7 @@ start_xmit (struct sk_buff *skb, struct net_device *dev)
u64 tfc_vlan_tag = 0;
if (np->link_status == 0) { /* Link Down */
- dev_kfree_skb(skb);
+ dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
entry = np->cur_tx % TX_RING_SIZE;
@@ -887,7 +887,7 @@ tx_error (struct net_device *dev, int tx_status)
frame_id = (tx_status & 0xffff0000);
printk (KERN_ERR "%s: Transmit error, TxStatus %4.4x, FrameId %d.\n",
dev->name, tx_status, frame_id);
- /* Ttransmit Underrun */
+ /* Transmit Underrun */
if (tx_status & 0x10) {
dev->stats.tx_fifo_errors++;
dw16(TxStartThresh, dr16(TxStartThresh) + 0x10);
@@ -1083,7 +1083,7 @@ rio_error (struct net_device *dev, int int_status)
get_stats (dev);
}
- /* PCI Error, a catastronphic error related to the bus interface
+ /* PCI Error, a catastrophic error related to the bus interface
occurs, set GlobalReset and HostReset to reset. */
if (int_status & HostError) {
printk (KERN_ERR "%s: HostError! IntStatus %4.4x.\n",
diff --git a/drivers/net/ethernet/dlink/dl2k.h b/drivers/net/ethernet/dlink/dl2k.h
index 4788cc94639d..9ebf7a6db93e 100644
--- a/drivers/net/ethernet/dlink/dl2k.h
+++ b/drivers/net/ethernet/dlink/dl2k.h
@@ -270,7 +270,7 @@ enum _pcs_reg {
PCS_ESR = 15,
};
-/* IEEE Extened Status Register */
+/* IEEE Extended Status Register */
enum _mii_esr {
MII_ESR_1000BX_FD = 0x8000,
MII_ESR_1000BX_HD = 0x4000,
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index cb004fd16252..5bb31c8fab39 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -1296,7 +1296,8 @@ static void be_xmit_flush(struct be_adapter *adapter, struct be_tx_obj *txo)
(adapter->bmc_filt_mask & BMC_FILT_MULTICAST)
static bool be_send_pkt_to_bmc(struct be_adapter *adapter,
- struct sk_buff **skb)
+ struct sk_buff **skb,
+ struct be_wrb_params *wrb_params)
{
struct ethhdr *eh = (struct ethhdr *)(*skb)->data;
bool os2bmc = false;
@@ -1360,7 +1361,7 @@ done:
* to BMC, asic expects the vlan to be inline in the packet.
*/
if (os2bmc)
- *skb = be_insert_vlan_in_pkt(adapter, *skb, NULL);
+ *skb = be_insert_vlan_in_pkt(adapter, *skb, wrb_params);
return os2bmc;
}
@@ -1387,7 +1388,7 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev)
/* if os2bmc is enabled and if the pkt is destined to bmc,
* enqueue the pkt a 2nd time with mgmt bit set.
*/
- if (be_send_pkt_to_bmc(adapter, &skb)) {
+ if (be_send_pkt_to_bmc(adapter, &skb, &wrb_params)) {
BE_WRB_F_SET(wrb_params.features, OS2BMC, 1);
wrb_cnt = be_xmit_enqueue(adapter, txo, skb, &wrb_params);
if (unlikely(!wrb_cnt))
diff --git a/drivers/net/ethernet/engleder/tsnep.h b/drivers/net/ethernet/engleder/tsnep.h
index f188fba021a6..03e19aea9ea4 100644
--- a/drivers/net/ethernet/engleder/tsnep.h
+++ b/drivers/net/ethernet/engleder/tsnep.h
@@ -176,7 +176,7 @@ struct tsnep_adapter {
struct tsnep_gcl gcl[2];
int next_gcl;
- struct hwtstamp_config hwtstamp_config;
+ struct kernel_hwtstamp_config hwtstamp_config;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_info;
/* ptp clock lock */
@@ -203,7 +203,11 @@ extern const struct ethtool_ops tsnep_ethtool_ops;
int tsnep_ptp_init(struct tsnep_adapter *adapter);
void tsnep_ptp_cleanup(struct tsnep_adapter *adapter);
-int tsnep_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
+int tsnep_ptp_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config);
+int tsnep_ptp_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
int tsnep_tc_init(struct tsnep_adapter *adapter);
void tsnep_tc_cleanup(struct tsnep_adapter *adapter);
diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c
index eba73246f986..b118407c30e8 100644
--- a/drivers/net/ethernet/engleder/tsnep_main.c
+++ b/drivers/net/ethernet/engleder/tsnep_main.c
@@ -2168,16 +2168,6 @@ static netdev_tx_t tsnep_netdev_xmit_frame(struct sk_buff *skb,
return tsnep_xmit_frame_ring(skb, &adapter->tx[queue_mapping]);
}
-static int tsnep_netdev_ioctl(struct net_device *netdev, struct ifreq *ifr,
- int cmd)
-{
- if (!netif_running(netdev))
- return -EINVAL;
- if (cmd == SIOCSHWTSTAMP || cmd == SIOCGHWTSTAMP)
- return tsnep_ptp_ioctl(netdev, ifr, cmd);
- return phy_mii_ioctl(netdev->phydev, ifr, cmd);
-}
-
static void tsnep_netdev_set_multicast(struct net_device *netdev)
{
struct tsnep_adapter *adapter = netdev_priv(netdev);
@@ -2384,7 +2374,7 @@ static const struct net_device_ops tsnep_netdev_ops = {
.ndo_open = tsnep_netdev_open,
.ndo_stop = tsnep_netdev_close,
.ndo_start_xmit = tsnep_netdev_xmit_frame,
- .ndo_eth_ioctl = tsnep_netdev_ioctl,
+ .ndo_eth_ioctl = phy_do_ioctl_running,
.ndo_set_rx_mode = tsnep_netdev_set_multicast,
.ndo_get_stats64 = tsnep_netdev_get_stats64,
.ndo_set_mac_address = tsnep_netdev_set_mac_address,
@@ -2394,6 +2384,8 @@ static const struct net_device_ops tsnep_netdev_ops = {
.ndo_bpf = tsnep_netdev_bpf,
.ndo_xdp_xmit = tsnep_netdev_xdp_xmit,
.ndo_xsk_wakeup = tsnep_netdev_xsk_wakeup,
+ .ndo_hwtstamp_get = tsnep_ptp_hwtstamp_get,
+ .ndo_hwtstamp_set = tsnep_ptp_hwtstamp_set,
};
static int tsnep_mac_init(struct tsnep_adapter *adapter)
diff --git a/drivers/net/ethernet/engleder/tsnep_ptp.c b/drivers/net/ethernet/engleder/tsnep_ptp.c
index 54fbf0126815..ae1308eb813d 100644
--- a/drivers/net/ethernet/engleder/tsnep_ptp.c
+++ b/drivers/net/ethernet/engleder/tsnep_ptp.c
@@ -19,57 +19,53 @@ void tsnep_get_system_time(struct tsnep_adapter *adapter, u64 *time)
*time = (((u64)high) << 32) | ((u64)low);
}
-int tsnep_ptp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+int tsnep_ptp_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
struct tsnep_adapter *adapter = netdev_priv(netdev);
- struct hwtstamp_config config;
-
- if (!ifr)
- return -EINVAL;
-
- if (cmd == SIOCSHWTSTAMP) {
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
-
- switch (config.tx_type) {
- case HWTSTAMP_TX_OFF:
- case HWTSTAMP_TX_ON:
- break;
- default:
- return -ERANGE;
- }
-
- switch (config.rx_filter) {
- case HWTSTAMP_FILTER_NONE:
- break;
- case HWTSTAMP_FILTER_ALL:
- case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
- case HWTSTAMP_FILTER_PTP_V2_EVENT:
- case HWTSTAMP_FILTER_PTP_V2_SYNC:
- case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
- case HWTSTAMP_FILTER_NTP_ALL:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
- break;
- default:
- return -ERANGE;
- }
-
- memcpy(&adapter->hwtstamp_config, &config,
- sizeof(adapter->hwtstamp_config));
+
+ *config = adapter->hwtstamp_config;
+ return 0;
+}
+
+int tsnep_ptp_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct tsnep_adapter *adapter = netdev_priv(netdev);
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_OFF:
+ case HWTSTAMP_TX_ON:
+ break;
+ default:
+ return -ERANGE;
}
- if (copy_to_user(ifr->ifr_data, &adapter->hwtstamp_config,
- sizeof(adapter->hwtstamp_config)))
- return -EFAULT;
+ switch (config->rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ break;
+ case HWTSTAMP_FILTER_ALL:
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ case HWTSTAMP_FILTER_NTP_ALL:
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ break;
+ default:
+ return -ERANGE;
+ }
+ adapter->hwtstamp_config = *config;
return 0;
}
diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c
index 6ac8547ef9b8..3c9961806f75 100644
--- a/drivers/net/ethernet/fealnx.c
+++ b/drivers/net/ethernet/fealnx.c
@@ -196,7 +196,7 @@ enum intr_status_bits {
ERI = 0x00000080, /* receive early int */
CNTOVF = 0x00000040, /* counter overflow */
RBU = 0x00000020, /* receive buffer unavailable */
- TBU = 0x00000010, /* transmit buffer unavilable */
+ TBU = 0x00000010, /* transmit buffer unavailable */
TI = 0x00000008, /* transmit interrupt */
RI = 0x00000004, /* receive interrupt */
RxErr = 0x00000002, /* receive error */
@@ -215,7 +215,7 @@ enum rx_mode_bits {
CR_W_RXMODEMASK = 0x000000e0,
CR_W_PROM = 0x00000080, /* promiscuous mode */
CR_W_AB = 0x00000040, /* accept broadcast */
- CR_W_AM = 0x00000020, /* accept mutlicast */
+ CR_W_AM = 0x00000020, /* accept multicast */
CR_W_ARP = 0x00000008, /* receive runt pkt */
CR_W_ALP = 0x00000004, /* receive long pkt */
CR_W_SEP = 0x00000002, /* receive error pkt */
diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig
index bbef47c3480c..e2a591cf9601 100644
--- a/drivers/net/ethernet/freescale/Kconfig
+++ b/drivers/net/ethernet/freescale/Kconfig
@@ -28,6 +28,7 @@ config FEC
depends on PTP_1588_CLOCK_OPTIONAL
select CRC32
select PHYLIB
+ select FIXED_PHY if M5272
select PAGE_POOL
imply PAGE_POOL_STATS
imply NET_SELFTESTS
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
index d09e456f14c0..ed3fa80af8c3 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c
@@ -467,6 +467,47 @@ revert_values:
return res;
}
+static void dpaa_get_pause_stats(struct net_device *net_dev,
+ struct ethtool_pause_stats *s)
+{
+ struct dpaa_priv *priv = netdev_priv(net_dev);
+ struct mac_device *mac_dev = priv->mac_dev;
+
+ if (mac_dev->get_pause_stats)
+ mac_dev->get_pause_stats(mac_dev->fman_mac, s);
+}
+
+static void dpaa_get_rmon_stats(struct net_device *net_dev,
+ struct ethtool_rmon_stats *s,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct dpaa_priv *priv = netdev_priv(net_dev);
+ struct mac_device *mac_dev = priv->mac_dev;
+
+ if (mac_dev->get_rmon_stats)
+ mac_dev->get_rmon_stats(mac_dev->fman_mac, s, ranges);
+}
+
+static void dpaa_get_eth_ctrl_stats(struct net_device *net_dev,
+ struct ethtool_eth_ctrl_stats *s)
+{
+ struct dpaa_priv *priv = netdev_priv(net_dev);
+ struct mac_device *mac_dev = priv->mac_dev;
+
+ if (mac_dev->get_eth_ctrl_stats)
+ mac_dev->get_eth_ctrl_stats(mac_dev->fman_mac, s);
+}
+
+static void dpaa_get_eth_mac_stats(struct net_device *net_dev,
+ struct ethtool_eth_mac_stats *s)
+{
+ struct dpaa_priv *priv = netdev_priv(net_dev);
+ struct mac_device *mac_dev = priv->mac_dev;
+
+ if (mac_dev->get_eth_mac_stats)
+ mac_dev->get_eth_mac_stats(mac_dev->fman_mac, s);
+}
+
const struct ethtool_ops dpaa_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
ETHTOOL_COALESCE_RX_MAX_FRAMES,
@@ -487,4 +528,8 @@ const struct ethtool_ops dpaa_ethtool_ops = {
.get_ts_info = dpaa_get_ts_info,
.get_coalesce = dpaa_get_coalesce,
.set_coalesce = dpaa_set_coalesce,
+ .get_pause_stats = dpaa_get_pause_stats,
+ .get_rmon_stats = dpaa_get_rmon_stats,
+ .get_eth_ctrl_stats = dpaa_get_eth_ctrl_stats,
+ .get_eth_mac_stats = dpaa_get_eth_mac_stats,
};
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index c96d1d6ba8fe..18d86badd6ea 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -1077,8 +1077,7 @@ static int dpaa2_eth_build_single_fd(struct dpaa2_eth_priv *priv,
dma_addr_t addr;
buffer_start = skb->data - dpaa2_eth_needed_headroom(skb);
- aligned_start = PTR_ALIGN(buffer_start - DPAA2_ETH_TX_BUF_ALIGN,
- DPAA2_ETH_TX_BUF_ALIGN);
+ aligned_start = PTR_ALIGN(buffer_start, DPAA2_ETH_TX_BUF_ALIGN);
if (aligned_start >= skb->head)
buffer_start = aligned_start;
else
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
index 00474ed11d53..baab4f1c908d 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
@@ -711,6 +711,13 @@ static int dpaa2_eth_update_cls_rule(struct net_device *net_dev,
return 0;
}
+static u32 dpaa2_eth_get_rx_ring_count(struct net_device *net_dev)
+{
+ struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
+
+ return dpaa2_eth_queue_count(priv);
+}
+
static int dpaa2_eth_get_rxnfc(struct net_device *net_dev,
struct ethtool_rxnfc *rxnfc, u32 *rule_locs)
{
@@ -719,9 +726,6 @@ static int dpaa2_eth_get_rxnfc(struct net_device *net_dev,
int i, j = 0;
switch (rxnfc->cmd) {
- case ETHTOOL_GRXRINGS:
- rxnfc->data = dpaa2_eth_queue_count(priv);
- break;
case ETHTOOL_GRXCLSRLCNT:
rxnfc->rule_cnt = 0;
rxnfc->rule_cnt = dpaa2_eth_num_cls_rules(priv);
@@ -949,6 +953,7 @@ const struct ethtool_ops dpaa2_ethtool_ops = {
.get_strings = dpaa2_eth_get_strings,
.get_rxnfc = dpaa2_eth_get_rxnfc,
.set_rxnfc = dpaa2_eth_set_rxnfc,
+ .get_rx_ring_count = dpaa2_eth_get_rx_ring_count,
.get_rxfh_fields = dpaa2_eth_get_rxfh_fields,
.set_rxfh_fields = dpaa2_eth_set_rxfh_fields,
.get_ts_info = dpaa2_eth_get_ts_info,
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index aae462a0cf5a..d5e5800b84ef 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -14,12 +14,21 @@
u32 enetc_port_mac_rd(struct enetc_si *si, u32 reg)
{
+ /* ENETC with pseudo MAC does not have Ethernet MAC
+ * port registers.
+ */
+ if (enetc_is_pseudo_mac(si))
+ return 0;
+
return enetc_port_rd(&si->hw, reg);
}
EXPORT_SYMBOL_GPL(enetc_port_mac_rd);
void enetc_port_mac_wr(struct enetc_si *si, u32 reg, u32 val)
{
+ if (enetc_is_pseudo_mac(si))
+ return;
+
enetc_port_wr(&si->hw, reg, val);
if (si->hw_features & ENETC_SI_F_QBU)
enetc_port_wr(&si->hw, reg + si->drvdata->pmac_offset, val);
@@ -1595,6 +1604,8 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
/* next descriptor to process */
i = rx_ring->next_to_clean;
+ enetc_lock_mdio();
+
while (likely(rx_frm_cnt < work_limit)) {
union enetc_rx_bd *rxbd;
struct sk_buff *skb;
@@ -1630,7 +1641,9 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
rx_byte_cnt += skb->len + ETH_HLEN;
rx_frm_cnt++;
+ enetc_unlock_mdio();
napi_gro_receive(napi, skb);
+ enetc_lock_mdio();
}
rx_ring->next_to_clean = i;
@@ -1638,6 +1651,8 @@ static int enetc_clean_rx_ring(struct enetc_bdr *rx_ring,
rx_ring->stats.packets += rx_frm_cnt;
rx_ring->stats.bytes += rx_byte_cnt;
+ enetc_unlock_mdio();
+
return rx_frm_cnt;
}
@@ -1947,6 +1962,8 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
/* next descriptor to process */
i = rx_ring->next_to_clean;
+ enetc_lock_mdio();
+
while (likely(rx_frm_cnt < work_limit)) {
union enetc_rx_bd *rxbd, *orig_rxbd;
struct xdp_buff xdp_buff;
@@ -2010,7 +2027,9 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
*/
enetc_bulk_flip_buff(rx_ring, orig_i, i);
+ enetc_unlock_mdio();
napi_gro_receive(napi, skb);
+ enetc_lock_mdio();
break;
case XDP_TX:
tx_ring = priv->xdp_tx_ring[rx_ring->index];
@@ -2045,7 +2064,9 @@ static int enetc_clean_rx_ring_xdp(struct enetc_bdr *rx_ring,
}
break;
case XDP_REDIRECT:
+ enetc_unlock_mdio();
err = xdp_do_redirect(rx_ring->ndev, &xdp_buff, prog);
+ enetc_lock_mdio();
if (unlikely(err)) {
enetc_xdp_drop(rx_ring, orig_i, i);
rx_ring->stats.xdp_redirect_failures++;
@@ -2065,8 +2086,11 @@ out:
rx_ring->stats.packets += rx_frm_cnt;
rx_ring->stats.bytes += rx_byte_cnt;
- if (xdp_redirect_frm_cnt)
+ if (xdp_redirect_frm_cnt) {
+ enetc_unlock_mdio();
xdp_do_flush();
+ enetc_lock_mdio();
+ }
if (xdp_tx_frm_cnt)
enetc_update_tx_ring_tail(tx_ring);
@@ -2075,6 +2099,8 @@ out:
enetc_refill_rx_ring(rx_ring, enetc_bd_unused(rx_ring) -
rx_ring->xdp.xdp_tx_in_flight);
+ enetc_unlock_mdio();
+
return rx_frm_cnt;
}
@@ -2093,6 +2119,7 @@ static int enetc_poll(struct napi_struct *napi, int budget)
for (i = 0; i < v->count_tx_rings; i++)
if (!enetc_clean_tx_ring(&v->tx_ring[i], budget))
complete = false;
+ enetc_unlock_mdio();
prog = rx_ring->xdp.prog;
if (prog)
@@ -2104,10 +2131,8 @@ static int enetc_poll(struct napi_struct *napi, int budget)
if (work_done)
v->rx_napi_work = true;
- if (!complete) {
- enetc_unlock_mdio();
+ if (!complete)
return budget;
- }
napi_complete_done(napi, work_done);
@@ -2116,6 +2141,7 @@ static int enetc_poll(struct napi_struct *napi, int budget)
v->rx_napi_work = false;
+ enetc_lock_mdio();
/* enable interrupts */
enetc_wr_reg_hot(v->rbier, ENETC_RBIER_RXTIE);
@@ -3350,7 +3376,8 @@ int enetc_hwtstamp_set(struct net_device *ndev,
new_offloads |= ENETC_F_TX_TSTAMP;
break;
case HWTSTAMP_TX_ONESTEP_SYNC:
- if (!enetc_si_is_pf(priv->si))
+ if (!enetc_si_is_pf(priv->si) ||
+ enetc_is_pseudo_mac(priv->si))
return -EOPNOTSUPP;
new_offloads &= ~ENETC_F_TX_TSTAMP_MASK;
@@ -3691,6 +3718,13 @@ static const struct enetc_drvdata enetc4_pf_data = {
.eth_ops = &enetc4_pf_ethtool_ops,
};
+static const struct enetc_drvdata enetc4_ppm_data = {
+ .sysclk_freq = ENETC_CLK_333M,
+ .tx_csum = true,
+ .max_frags = ENETC4_MAX_SKB_FRAGS,
+ .eth_ops = &enetc4_ppm_ethtool_ops,
+};
+
static const struct enetc_drvdata enetc_vf_data = {
.sysclk_freq = ENETC_CLK_400M,
.max_frags = ENETC_MAX_SKB_FRAGS,
@@ -3710,6 +3744,15 @@ static const struct enetc_platform_info enetc_info[] = {
.dev_id = ENETC_DEV_ID_VF,
.data = &enetc_vf_data,
},
+ {
+ .revision = ENETC_REV_4_3,
+ .dev_id = NXP_ENETC_PPM_DEV_ID,
+ .data = &enetc4_ppm_data,
+ },
+ { .revision = ENETC_REV_4_3,
+ .dev_id = NXP_ENETC_PF_DEV_ID,
+ .data = &enetc4_pf_data,
+ },
};
int enetc_get_driver_data(struct enetc_si *si)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.h b/drivers/net/ethernet/freescale/enetc/enetc.h
index 0ec010a7d640..dce27bd67a7d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc.h
@@ -76,7 +76,7 @@ struct enetc_lso_t {
#define ENETC_LSO_MAX_DATA_LEN SZ_256K
#define ENETC_RX_MAXFRM_SIZE ENETC_MAC_MAXFRM_SIZE
-#define ENETC_RXB_TRUESIZE 2048 /* PAGE_SIZE >> 1 */
+#define ENETC_RXB_TRUESIZE (PAGE_SIZE >> 1)
#define ENETC_RXB_PAD NET_SKB_PAD /* add extra space if needed */
#define ENETC_RXB_DMA_SIZE \
(SKB_WITH_OVERHEAD(ENETC_RXB_TRUESIZE) - ENETC_RXB_PAD)
@@ -273,6 +273,7 @@ enum enetc_errata {
#define ENETC_SI_F_QBV BIT(1)
#define ENETC_SI_F_QBU BIT(2)
#define ENETC_SI_F_LSO BIT(3)
+#define ENETC_SI_F_PPM BIT(4) /* pseudo MAC */
struct enetc_drvdata {
u32 pmac_offset; /* Only valid for PSI which supports 802.1Qbu */
@@ -362,6 +363,11 @@ static inline int enetc_pf_to_port(struct pci_dev *pf_pdev)
}
}
+static inline bool enetc_is_pseudo_mac(struct enetc_si *si)
+{
+ return si->hw_features & ENETC_SI_F_PPM;
+}
+
#define ENETC_MAX_NUM_TXQS 8
#define ENETC_INT_NAME_MAX (IFNAMSIZ + 8)
@@ -534,6 +540,8 @@ int enetc_hwtstamp_set(struct net_device *ndev,
extern const struct ethtool_ops enetc_pf_ethtool_ops;
extern const struct ethtool_ops enetc4_pf_ethtool_ops;
extern const struct ethtool_ops enetc_vf_ethtool_ops;
+extern const struct ethtool_ops enetc4_ppm_ethtool_ops;
+
void enetc_set_ethtool_ops(struct net_device *ndev);
void enetc_mm_link_state_update(struct enetc_ndev_priv *priv, bool link);
void enetc_mm_commit_preemptible_tcs(struct enetc_ndev_priv *priv);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
index 19bf0e89cdc2..3ed0f7a02767 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_hw.h
@@ -11,6 +11,7 @@
#define NXP_ENETC_VENDOR_ID 0x1131
#define NXP_ENETC_PF_DEV_ID 0xe101
+#define NXP_ENETC_PPM_DEV_ID 0xe110
/**********************Station interface registers************************/
/* Station interface LSO segmentation flag mask register 0/1 */
@@ -115,6 +116,10 @@
#define PMCAPR_HD BIT(8)
#define PMCAPR_FP GENMASK(10, 9)
+/* Port capability register */
+#define ENETC4_PCAPR 0x4000
+#define PCAPR_LINK_TYPE BIT(4)
+
/* Port configuration register */
#define ENETC4_PCR 0x4010
#define PCR_HDR_FMT BIT(0)
@@ -165,6 +170,9 @@
/* Port MAC 0/1 Maximum Frame Length Register */
#define ENETC4_PM_MAXFRM(mac) (0x5014 + (mac) * 0x400)
+/* Port internal MDIO base address, use to access PCS */
+#define ENETC4_PM_IMDIO_BASE 0x5030
+
/* Port MAC 0/1 Pause Quanta Register */
#define ENETC4_PM_PAUSE_QUANTA(mac) (0x5054 + (mac) * 0x400)
@@ -193,4 +201,32 @@
#define SSP_1G 2
#define PM_IF_MODE_ENA BIT(15)
+/* Port external MDIO Base address, use to access off-chip PHY */
+#define ENETC4_EMDIO_BASE 0x5c00
+
+/**********************ENETC Pseudo MAC port registers************************/
+/* Port pseudo MAC receive octets counter (64-bit) */
+#define ENETC4_PPMROCR 0x5080
+
+/* Port pseudo MAC receive unicast frame counter register (64-bit) */
+#define ENETC4_PPMRUFCR 0x5088
+
+/* Port pseudo MAC receive multicast frame counter register (64-bit) */
+#define ENETC4_PPMRMFCR 0x5090
+
+/* Port pseudo MAC receive broadcast frame counter register (64-bit) */
+#define ENETC4_PPMRBFCR 0x5098
+
+/* Port pseudo MAC transmit octets counter (64-bit) */
+#define ENETC4_PPMTOCR 0x50c0
+
+/* Port pseudo MAC transmit unicast frame counter register (64-bit) */
+#define ENETC4_PPMTUFCR 0x50c8
+
+/* Port pseudo MAC transmit multicast frame counter register (64-bit) */
+#define ENETC4_PPMTMFCR 0x50d0
+
+/* Port pseudo MAC transmit broadcast frame counter register (64-bit) */
+#define ENETC4_PPMTBFCR 0x50d8
+
#endif
diff --git a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
index 82c443b28b15..498346dd996a 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc4_pf.c
@@ -41,6 +41,16 @@ static void enetc4_get_port_caps(struct enetc_pf *pf)
pf->caps.mac_filter_num = val & PSIMAFCAPR_NUM_MAC_AFTE;
}
+static void enetc4_get_psi_hw_features(struct enetc_si *si)
+{
+ struct enetc_hw *hw = &si->hw;
+ u32 val;
+
+ val = enetc_port_rd(hw, ENETC4_PCAPR);
+ if (val & PCAPR_LINK_TYPE)
+ si->hw_features |= ENETC_SI_F_PPM;
+}
+
static void enetc4_pf_set_si_primary_mac(struct enetc_hw *hw, int si,
const u8 *addr)
{
@@ -277,6 +287,7 @@ static int enetc4_pf_struct_init(struct enetc_si *si)
pf->ops = &enetc4_pf_ops;
enetc4_get_port_caps(pf);
+ enetc4_get_psi_hw_features(si);
return 0;
}
@@ -589,6 +600,9 @@ static void enetc4_mac_config(struct enetc_pf *pf, unsigned int mode,
struct enetc_si *si = pf->si;
u32 val;
+ if (enetc_is_pseudo_mac(si))
+ return;
+
val = enetc_port_mac_rd(si, ENETC4_PM_IF_MODE(0));
val &= ~(PM_IF_MODE_IFMODE | PM_IF_MODE_ENA);
@@ -1071,6 +1085,7 @@ static void enetc4_pf_remove(struct pci_dev *pdev)
static const struct pci_device_id enetc4_pf_id_table[] = {
{ PCI_DEVICE(NXP_ENETC_VENDOR_ID, NXP_ENETC_PF_DEV_ID) },
+ { PCI_DEVICE(NXP_ENETC_VENDOR_ID, NXP_ENETC_PPM_DEV_ID) },
{ 0, } /* End of table. */
};
MODULE_DEVICE_TABLE(pci, enetc4_pf_id_table);
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 71d052de669a..fed89d4f1e1d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -435,6 +435,48 @@ static void enetc_get_eth_mac_stats(struct net_device *ndev,
}
}
+static void enetc_ppm_mac_stats(struct enetc_si *si,
+ struct ethtool_eth_mac_stats *s)
+{
+ struct enetc_hw *hw = &si->hw;
+ u64 rufcr, rmfcr, rbfcr;
+ u64 tufcr, tmfcr, tbfcr;
+
+ rufcr = enetc_port_rd64(hw, ENETC4_PPMRUFCR);
+ rmfcr = enetc_port_rd64(hw, ENETC4_PPMRMFCR);
+ rbfcr = enetc_port_rd64(hw, ENETC4_PPMRBFCR);
+
+ tufcr = enetc_port_rd64(hw, ENETC4_PPMTUFCR);
+ tmfcr = enetc_port_rd64(hw, ENETC4_PPMTMFCR);
+ tbfcr = enetc_port_rd64(hw, ENETC4_PPMTBFCR);
+
+ s->FramesTransmittedOK = tufcr + tmfcr + tbfcr;
+ s->FramesReceivedOK = rufcr + rmfcr + rbfcr;
+ s->OctetsTransmittedOK = enetc_port_rd64(hw, ENETC4_PPMTOCR);
+ s->OctetsReceivedOK = enetc_port_rd64(hw, ENETC4_PPMROCR);
+ s->MulticastFramesXmittedOK = tmfcr;
+ s->BroadcastFramesXmittedOK = tbfcr;
+ s->MulticastFramesReceivedOK = rmfcr;
+ s->BroadcastFramesReceivedOK = rbfcr;
+}
+
+static void enetc_ppm_get_eth_mac_stats(struct net_device *ndev,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(ndev);
+
+ switch (mac_stats->src) {
+ case ETHTOOL_MAC_STATS_SRC_EMAC:
+ enetc_ppm_mac_stats(priv->si, mac_stats);
+ break;
+ case ETHTOOL_MAC_STATS_SRC_PMAC:
+ break;
+ case ETHTOOL_MAC_STATS_SRC_AGGREGATE:
+ ethtool_aggregate_mac_stats(ndev, mac_stats);
+ break;
+ }
+}
+
static void enetc_get_eth_ctrl_stats(struct net_device *ndev,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
@@ -591,6 +633,13 @@ done:
return enetc_set_fs_entry(si, &rfse, fs->location);
}
+static u32 enetc_get_rx_ring_count(struct net_device *ndev)
+{
+ struct enetc_ndev_priv *priv = netdev_priv(ndev);
+
+ return priv->num_rx_rings;
+}
+
static int enetc_get_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc,
u32 *rule_locs)
{
@@ -598,9 +647,6 @@ static int enetc_get_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc,
int i, j;
switch (rxnfc->cmd) {
- case ETHTOOL_GRXRINGS:
- rxnfc->data = priv->num_rx_rings;
- break;
case ETHTOOL_GRXCLSRLCNT:
/* total number of entries */
rxnfc->data = priv->si->num_fs_entries;
@@ -639,27 +685,6 @@ static int enetc_get_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc,
return 0;
}
-/* i.MX95 ENETC does not support RFS table, but we can use ingress port
- * filter table to implement Wake-on-LAN filter or drop the matched flow,
- * so the implementation will be different from enetc_get_rxnfc() and
- * enetc_set_rxnfc(). Therefore, add enetc4_get_rxnfc() for ENETC v4 PF.
- */
-static int enetc4_get_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc,
- u32 *rule_locs)
-{
- struct enetc_ndev_priv *priv = netdev_priv(ndev);
-
- switch (rxnfc->cmd) {
- case ETHTOOL_GRXRINGS:
- rxnfc->data = priv->num_rx_rings;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
static int enetc_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *rxnfc)
{
struct enetc_ndev_priv *priv = netdev_priv(ndev);
@@ -894,6 +919,9 @@ static int enetc_get_phc_index_by_pdev(struct enetc_si *si)
case ENETC_REV_4_1:
devfn = PCI_DEVFN(24, 0);
break;
+ case ENETC_REV_4_3:
+ devfn = PCI_DEVFN(0, 1);
+ break;
default:
return -1;
}
@@ -1290,6 +1318,7 @@ const struct ethtool_ops enetc_pf_ethtool_ops = {
.get_rmon_stats = enetc_get_rmon_stats,
.get_eth_ctrl_stats = enetc_get_eth_ctrl_stats,
.get_eth_mac_stats = enetc_get_eth_mac_stats,
+ .get_rx_ring_count = enetc_get_rx_ring_count,
.get_rxnfc = enetc_get_rxnfc,
.set_rxnfc = enetc_set_rxnfc,
.get_rxfh_key_size = enetc_get_rxfh_key_size,
@@ -1313,6 +1342,25 @@ const struct ethtool_ops enetc_pf_ethtool_ops = {
.get_mm_stats = enetc_get_mm_stats,
};
+const struct ethtool_ops enetc4_ppm_ethtool_ops = {
+ .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
+ ETHTOOL_COALESCE_MAX_FRAMES |
+ ETHTOOL_COALESCE_USE_ADAPTIVE_RX,
+ .get_eth_mac_stats = enetc_ppm_get_eth_mac_stats,
+ .get_rx_ring_count = enetc_get_rx_ring_count,
+ .get_rxfh_key_size = enetc_get_rxfh_key_size,
+ .get_rxfh_indir_size = enetc_get_rxfh_indir_size,
+ .get_rxfh = enetc_get_rxfh,
+ .set_rxfh = enetc_set_rxfh,
+ .get_rxfh_fields = enetc_get_rxfh_fields,
+ .get_ringparam = enetc_get_ringparam,
+ .get_coalesce = enetc_get_coalesce,
+ .set_coalesce = enetc_set_coalesce,
+ .get_link_ksettings = enetc_get_link_ksettings,
+ .set_link_ksettings = enetc_set_link_ksettings,
+ .get_link = ethtool_op_get_link,
+};
+
const struct ethtool_ops enetc_vf_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_MAX_FRAMES |
@@ -1322,6 +1370,7 @@ const struct ethtool_ops enetc_vf_ethtool_ops = {
.get_sset_count = enetc_get_sset_count,
.get_strings = enetc_get_strings,
.get_ethtool_stats = enetc_get_ethtool_stats,
+ .get_rx_ring_count = enetc_get_rx_ring_count,
.get_rxnfc = enetc_get_rxnfc,
.set_rxnfc = enetc_set_rxnfc,
.get_rxfh_indir_size = enetc_get_rxfh_indir_size,
@@ -1349,7 +1398,7 @@ const struct ethtool_ops enetc4_pf_ethtool_ops = {
.set_wol = enetc_set_wol,
.get_pauseparam = enetc_get_pauseparam,
.set_pauseparam = enetc_set_pauseparam,
- .get_rxnfc = enetc4_get_rxnfc,
+ .get_rx_ring_count = enetc_get_rx_ring_count,
.get_rxfh_key_size = enetc_get_rxfh_key_size,
.get_rxfh_indir_size = enetc_get_rxfh_indir_size,
.get_rxfh = enetc_get_rxfh,
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index 377c96325814..7b882b8921fe 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -378,6 +378,7 @@ enum enetc_bdr_type {TX, RX};
#define EIPBRR0_REVISION GENMASK(15, 0)
#define ENETC_REV_1_0 0x0100
#define ENETC_REV_4_1 0X0401
+#define ENETC_REV_4_3 0x0403
#define ENETC_G_EIPBRR1 0x0bfc
#define ENETC_G_EPFBLPR(n) (0xd00 + 4 * (n))
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
index edf14a95cab7..76263b8566bb 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_pf_common.c
@@ -109,7 +109,7 @@ void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
ndev->hw_features = NETIF_F_SG | NETIF_F_RXCSUM |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX |
- NETIF_F_HW_VLAN_CTAG_FILTER | NETIF_F_LOOPBACK |
+ NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_HW_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_GSO_UDP_L4;
ndev->features = NETIF_F_HIGHDMA | NETIF_F_SG | NETIF_F_RXCSUM |
@@ -133,6 +133,9 @@ void enetc_pf_netdev_setup(struct enetc_si *si, struct net_device *ndev,
ndev->features |= NETIF_F_RXHASH;
}
+ if (!enetc_is_pseudo_mac(si))
+ ndev->hw_features |= NETIF_F_LOOPBACK;
+
/* TODO: currently, i.MX95 ENETC driver does not support advanced features */
if (!is_enetc_rev1(si))
goto end;
@@ -173,7 +176,12 @@ static int enetc_mdio_probe(struct enetc_pf *pf, struct device_node *np)
bus->parent = dev;
mdio_priv = bus->priv;
mdio_priv->hw = &pf->si->hw;
- mdio_priv->mdio_base = ENETC_EMDIO_BASE;
+
+ if (is_enetc_rev1(pf->si))
+ mdio_priv->mdio_base = ENETC_EMDIO_BASE;
+ else
+ mdio_priv->mdio_base = ENETC4_EMDIO_BASE;
+
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
err = of_mdiobus_register(bus, np);
@@ -218,7 +226,12 @@ static int enetc_imdio_create(struct enetc_pf *pf)
bus->phy_mask = ~0;
mdio_priv = bus->priv;
mdio_priv->hw = &pf->si->hw;
- mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE;
+
+ if (is_enetc_rev1(pf->si))
+ mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE;
+ else
+ mdio_priv->mdio_base = ENETC4_PM_IMDIO_BASE;
+
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
err = mdiobus_register(bus);
diff --git a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c
index bcb8eefeb93c..443983fdecd9 100644
--- a/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c
+++ b/drivers/net/ethernet/freescale/enetc/netc_blk_ctrl.c
@@ -47,6 +47,13 @@
#define PCS_PROT_SFI BIT(4)
#define PCS_PROT_10G_SXGMII BIT(6)
+#define IMX94_EXT_PIN_CONTROL 0x10
+#define MAC2_MAC3_SEL BIT(1)
+
+#define IMX94_NETC_LINK_CFG(a) (0x4c + (a) * 4)
+#define NETC_LINK_CFG_MII_PROT GENMASK(3, 0)
+#define NETC_LINK_CFG_IO_VAR GENMASK(19, 16)
+
/* NETC privileged register block register */
#define PRB_NETCRR 0x100
#define NETCRR_SR BIT(0)
@@ -59,6 +66,10 @@
/* NETC integrated endpoint register block register */
#define IERB_EMDIOFAUXR 0x344
#define IERB_T0FAUXR 0x444
+#define IERB_ETBCR(a) (0x300c + 0x100 * (a))
+#define IERB_LBCR(a) (0x1010 + 0x40 * (a))
+#define LBCR_MDIO_PHYAD_PRTAD(addr) (((addr) & 0x1f) << 8)
+
#define IERB_EFAUXR(a) (0x3044 + 0x100 * (a))
#define IERB_VFAUXR(a) (0x4004 + 0x40 * (a))
#define FAUXR_LDID GENMASK(3, 0)
@@ -68,6 +79,19 @@
#define IMX95_ENETC1_BUS_DEVFN 0x40
#define IMX95_ENETC2_BUS_DEVFN 0x80
+#define IMX94_ENETC0_BUS_DEVFN 0x100
+#define IMX94_ENETC1_BUS_DEVFN 0x140
+#define IMX94_ENETC2_BUS_DEVFN 0x180
+#define IMX94_TIMER0_BUS_DEVFN 0x1
+#define IMX94_TIMER1_BUS_DEVFN 0x101
+#define IMX94_TIMER2_BUS_DEVFN 0x181
+#define IMX94_ENETC0_LINK 3
+#define IMX94_ENETC1_LINK 4
+#define IMX94_ENETC2_LINK 5
+
+#define NETC_ENETC_ID(a) (a)
+#define NETC_TIMER_ID(a) (a)
+
/* Flags for different platforms */
#define NETC_HAS_NETCMIX BIT(0)
@@ -192,6 +216,90 @@ static int imx95_netcmix_init(struct platform_device *pdev)
return 0;
}
+static int imx94_enetc_get_link_id(struct device_node *np)
+{
+ int bus_devfn = netc_of_pci_get_bus_devfn(np);
+
+ /* Parse ENETC link number */
+ switch (bus_devfn) {
+ case IMX94_ENETC0_BUS_DEVFN:
+ return IMX94_ENETC0_LINK;
+ case IMX94_ENETC1_BUS_DEVFN:
+ return IMX94_ENETC1_LINK;
+ case IMX94_ENETC2_BUS_DEVFN:
+ return IMX94_ENETC2_LINK;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx94_link_config(struct netc_blk_ctrl *priv,
+ struct device_node *np, int link_id)
+{
+ phy_interface_t interface;
+ int mii_proto;
+ u32 val;
+
+ /* The node may be disabled and does not have a 'phy-mode'
+ * or 'phy-connection-type' property.
+ */
+ if (of_get_phy_mode(np, &interface))
+ return 0;
+
+ mii_proto = netc_get_link_mii_protocol(interface);
+ if (mii_proto < 0)
+ return mii_proto;
+
+ val = mii_proto & NETC_LINK_CFG_MII_PROT;
+ if (val == MII_PROT_SERIAL)
+ val = u32_replace_bits(val, IO_VAR_16FF_16G_SERDES,
+ NETC_LINK_CFG_IO_VAR);
+
+ netc_reg_write(priv->netcmix, IMX94_NETC_LINK_CFG(link_id), val);
+
+ return 0;
+}
+
+static int imx94_enetc_link_config(struct netc_blk_ctrl *priv,
+ struct device_node *np)
+{
+ int link_id = imx94_enetc_get_link_id(np);
+
+ if (link_id < 0)
+ return link_id;
+
+ return imx94_link_config(priv, np, link_id);
+}
+
+static int imx94_netcmix_init(struct platform_device *pdev)
+{
+ struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ u32 val;
+ int err;
+
+ for_each_child_of_node_scoped(np, child) {
+ for_each_child_of_node_scoped(child, gchild) {
+ if (!of_device_is_compatible(gchild, "pci1131,e101"))
+ continue;
+
+ err = imx94_enetc_link_config(priv, gchild);
+ if (err)
+ return err;
+ }
+ }
+
+ /* ENETC 0 and switch port 2 share the same parallel interface.
+ * Currently, the switch is not supported, so this interface is
+ * used by ENETC 0 by default.
+ */
+ val = netc_reg_read(priv->netcmix, IMX94_EXT_PIN_CONTROL);
+ val |= MAC2_MAC3_SEL;
+ netc_reg_write(priv->netcmix, IMX94_EXT_PIN_CONTROL, val);
+
+ return 0;
+}
+
static bool netc_ierb_is_locked(struct netc_blk_ctrl *priv)
{
return !!(netc_reg_read(priv->prb, PRB_NETCRR) & NETCRR_LOCK);
@@ -217,6 +325,142 @@ static int netc_unlock_ierb_with_warm_reset(struct netc_blk_ctrl *priv)
1000, 100000, true, priv->prb, PRB_NETCRR);
}
+static int netc_get_phy_addr(struct device_node *np)
+{
+ struct device_node *mdio_node, *phy_node;
+ u32 addr = 0;
+ int err = 0;
+
+ mdio_node = of_get_child_by_name(np, "mdio");
+ if (!mdio_node)
+ return 0;
+
+ phy_node = of_get_next_child(mdio_node, NULL);
+ if (!phy_node)
+ goto of_put_mdio_node;
+
+ err = of_property_read_u32(phy_node, "reg", &addr);
+ if (err)
+ goto of_put_phy_node;
+
+ if (addr >= PHY_MAX_ADDR)
+ err = -EINVAL;
+
+of_put_phy_node:
+ of_node_put(phy_node);
+
+of_put_mdio_node:
+ of_node_put(mdio_node);
+
+ return err ? err : addr;
+}
+
+static int netc_parse_emdio_phy_mask(struct device_node *np, u32 *phy_mask)
+{
+ u32 mask = 0;
+
+ for_each_child_of_node_scoped(np, child) {
+ u32 addr;
+ int err;
+
+ err = of_property_read_u32(child, "reg", &addr);
+ if (err)
+ return err;
+
+ if (addr >= PHY_MAX_ADDR)
+ return -EINVAL;
+
+ mask |= BIT(addr);
+ }
+
+ *phy_mask = mask;
+
+ return 0;
+}
+
+static int netc_get_emdio_phy_mask(struct device_node *np, u32 *phy_mask)
+{
+ for_each_child_of_node_scoped(np, child) {
+ for_each_child_of_node_scoped(child, gchild) {
+ if (!of_device_is_compatible(gchild, "pci1131,ee00"))
+ continue;
+
+ return netc_parse_emdio_phy_mask(gchild, phy_mask);
+ }
+ }
+
+ return 0;
+}
+
+static int imx95_enetc_mdio_phyaddr_config(struct platform_device *pdev)
+{
+ struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ int bus_devfn, addr, err;
+ u32 phy_mask = 0;
+
+ err = netc_get_emdio_phy_mask(np, &phy_mask);
+ if (err) {
+ dev_err(dev, "Failed to get PHY address mask\n");
+ return err;
+ }
+
+ /* Update the port EMDIO PHY address through parsing phy properties.
+ * This is needed when using the port EMDIO but it's harmless when
+ * using the central EMDIO. So apply it on all cases.
+ */
+ for_each_child_of_node_scoped(np, child) {
+ for_each_child_of_node_scoped(child, gchild) {
+ if (!of_device_is_compatible(gchild, "pci1131,e101"))
+ continue;
+
+ bus_devfn = netc_of_pci_get_bus_devfn(gchild);
+ if (bus_devfn < 0) {
+ dev_err(dev, "Failed to get BDF number\n");
+ return bus_devfn;
+ }
+
+ addr = netc_get_phy_addr(gchild);
+ if (addr < 0) {
+ dev_err(dev, "Failed to get PHY address\n");
+ return addr;
+ }
+
+ if (phy_mask & BIT(addr)) {
+ dev_err(dev,
+ "Find same PHY address in EMDIO and ENETC node\n");
+ return -EINVAL;
+ }
+
+ /* The default value of LaBCR[MDIO_PHYAD_PRTAD ] is
+ * 0, so no need to set the register.
+ */
+ if (!addr)
+ continue;
+
+ switch (bus_devfn) {
+ case IMX95_ENETC0_BUS_DEVFN:
+ netc_reg_write(priv->ierb, IERB_LBCR(0),
+ LBCR_MDIO_PHYAD_PRTAD(addr));
+ break;
+ case IMX95_ENETC1_BUS_DEVFN:
+ netc_reg_write(priv->ierb, IERB_LBCR(1),
+ LBCR_MDIO_PHYAD_PRTAD(addr));
+ break;
+ case IMX95_ENETC2_BUS_DEVFN:
+ netc_reg_write(priv->ierb, IERB_LBCR(2),
+ LBCR_MDIO_PHYAD_PRTAD(addr));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int imx95_ierb_init(struct platform_device *pdev)
{
struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
@@ -244,6 +488,155 @@ static int imx95_ierb_init(struct platform_device *pdev)
/* NETC TIMER */
netc_reg_write(priv->ierb, IERB_T0FAUXR, 7);
+ return imx95_enetc_mdio_phyaddr_config(pdev);
+}
+
+static int imx94_get_enetc_id(struct device_node *np)
+{
+ int bus_devfn = netc_of_pci_get_bus_devfn(np);
+
+ /* Parse ENETC offset */
+ switch (bus_devfn) {
+ case IMX94_ENETC0_BUS_DEVFN:
+ return NETC_ENETC_ID(0);
+ case IMX94_ENETC1_BUS_DEVFN:
+ return NETC_ENETC_ID(1);
+ case IMX94_ENETC2_BUS_DEVFN:
+ return NETC_ENETC_ID(2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx94_get_timer_id(struct device_node *np)
+{
+ int bus_devfn = netc_of_pci_get_bus_devfn(np);
+
+ /* Parse NETC PTP timer ID, the timer0 is on bus 0,
+ * the timer 1 and timer2 is on bus 1.
+ */
+ switch (bus_devfn) {
+ case IMX94_TIMER0_BUS_DEVFN:
+ return NETC_TIMER_ID(0);
+ case IMX94_TIMER1_BUS_DEVFN:
+ return NETC_TIMER_ID(1);
+ case IMX94_TIMER2_BUS_DEVFN:
+ return NETC_TIMER_ID(2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx94_enetc_update_tid(struct netc_blk_ctrl *priv,
+ struct device_node *np)
+{
+ struct device *dev = &priv->pdev->dev;
+ struct device_node *timer_np;
+ int eid, tid;
+
+ eid = imx94_get_enetc_id(np);
+ if (eid < 0) {
+ dev_err(dev, "Failed to get ENETC ID\n");
+ return eid;
+ }
+
+ timer_np = of_parse_phandle(np, "ptp-timer", 0);
+ if (!timer_np) {
+ /* If 'ptp-timer' is not present, the timer1 is the default
+ * timer of all standalone ENETCs, which is on the same PCIe
+ * bus as these ENETCs.
+ */
+ tid = NETC_TIMER_ID(1);
+ goto end;
+ }
+
+ tid = imx94_get_timer_id(timer_np);
+ of_node_put(timer_np);
+ if (tid < 0) {
+ dev_err(dev, "Failed to get NETC Timer ID\n");
+ return tid;
+ }
+
+end:
+ netc_reg_write(priv->ierb, IERB_ETBCR(eid), tid);
+
+ return 0;
+}
+
+static int imx94_enetc_mdio_phyaddr_config(struct netc_blk_ctrl *priv,
+ struct device_node *np,
+ u32 phy_mask)
+{
+ struct device *dev = &priv->pdev->dev;
+ int bus_devfn, addr;
+
+ bus_devfn = netc_of_pci_get_bus_devfn(np);
+ if (bus_devfn < 0) {
+ dev_err(dev, "Failed to get BDF number\n");
+ return bus_devfn;
+ }
+
+ addr = netc_get_phy_addr(np);
+ if (addr <= 0) {
+ dev_err(dev, "Failed to get PHY address\n");
+ return addr;
+ }
+
+ if (phy_mask & BIT(addr)) {
+ dev_err(dev,
+ "Find same PHY address in EMDIO and ENETC node\n");
+ return -EINVAL;
+ }
+
+ switch (bus_devfn) {
+ case IMX94_ENETC0_BUS_DEVFN:
+ netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC0_LINK),
+ LBCR_MDIO_PHYAD_PRTAD(addr));
+ break;
+ case IMX94_ENETC1_BUS_DEVFN:
+ netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC1_LINK),
+ LBCR_MDIO_PHYAD_PRTAD(addr));
+ break;
+ case IMX94_ENETC2_BUS_DEVFN:
+ netc_reg_write(priv->ierb, IERB_LBCR(IMX94_ENETC2_LINK),
+ LBCR_MDIO_PHYAD_PRTAD(addr));
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int imx94_ierb_init(struct platform_device *pdev)
+{
+ struct netc_blk_ctrl *priv = platform_get_drvdata(pdev);
+ struct device_node *np = pdev->dev.of_node;
+ u32 phy_mask = 0;
+ int err;
+
+ err = netc_get_emdio_phy_mask(np, &phy_mask);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to get PHY address mask\n");
+ return err;
+ }
+
+ for_each_child_of_node_scoped(np, child) {
+ for_each_child_of_node_scoped(child, gchild) {
+ if (!of_device_is_compatible(gchild, "pci1131,e101"))
+ continue;
+
+ err = imx94_enetc_update_tid(priv, gchild);
+ if (err)
+ return err;
+
+ err = imx94_enetc_mdio_phyaddr_config(priv, gchild,
+ phy_mask);
+ if (err)
+ return err;
+ }
+ }
+
return 0;
}
@@ -340,8 +733,15 @@ static const struct netc_devinfo imx95_devinfo = {
.ierb_init = imx95_ierb_init,
};
+static const struct netc_devinfo imx94_devinfo = {
+ .flags = NETC_HAS_NETCMIX,
+ .netcmix_init = imx94_netcmix_init,
+ .ierb_init = imx94_ierb_init,
+};
+
static const struct of_device_id netc_blk_ctrl_match[] = {
{ .compatible = "nxp,imx95-netc-blk-ctrl", .data = &imx95_devinfo },
+ { .compatible = "nxp,imx94-netc-blk-ctrl", .data = &imx94_devinfo },
{},
};
MODULE_DEVICE_TABLE(of, netc_blk_ctrl_match);
diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h
index 41e0d85d15da..fd9a93d02f8e 100644
--- a/drivers/net/ethernet/freescale/fec.h
+++ b/drivers/net/ethernet/freescale/fec.h
@@ -24,9 +24,7 @@
#include <linux/timecounter.h>
#include <net/xdp.h>
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
- defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
+#if !defined(CONFIG_M5272) || defined(CONFIG_COMPILE_TEST)
/*
* Just figures, Motorola would have to change the offsets for
* registers in the same peripheral device on different models
@@ -242,23 +240,6 @@ struct bufdesc_ex {
__fec16 res0[4];
};
-/*
- * The following definitions courtesy of commproc.h, which where
- * Copyright (c) 1997 Dan Malek (dmalek@jlc.net).
- */
-#define BD_SC_EMPTY ((ushort)0x8000) /* Receive is empty */
-#define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */
-#define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */
-#define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */
-#define BD_SC_CM ((ushort)0x0200) /* Continuous mode */
-#define BD_SC_ID ((ushort)0x0100) /* Rec'd too many idles */
-#define BD_SC_P ((ushort)0x0100) /* xmt preamble */
-#define BD_SC_BR ((ushort)0x0020) /* Break received */
-#define BD_SC_FR ((ushort)0x0010) /* Framing error */
-#define BD_SC_PR ((ushort)0x0008) /* Parity error */
-#define BD_SC_OV ((ushort)0x0002) /* Overrun */
-#define BD_SC_CD ((ushort)0x0001) /* ?? */
-
/* Buffer descriptor control/status used by Ethernet receive.
*/
#define BD_ENET_RX_EMPTY ((ushort)0x8000)
@@ -530,12 +511,6 @@ struct bufdesc_prop {
unsigned char dsize_log2;
};
-struct fec_enet_priv_txrx_info {
- int offset;
- struct page *page;
- struct sk_buff *skb;
-};
-
enum {
RX_XDP_REDIRECT = 0,
RX_XDP_PASS,
@@ -575,7 +550,7 @@ struct fec_enet_priv_tx_q {
struct fec_enet_priv_rx_q {
struct bufdesc_prop bd;
- struct fec_enet_priv_txrx_info rx_skb_info[RX_RING_SIZE];
+ struct page *rx_buf[RX_RING_SIZE];
/* page_pool */
struct page_pool *page_pool;
@@ -668,7 +643,6 @@ struct fec_enet_private {
struct pm_qos_request pm_qos_req;
unsigned int tx_align;
- unsigned int rx_align;
/* hw interrupt coalesce */
unsigned int rx_pkts_itr;
@@ -687,6 +661,7 @@ struct fec_enet_private {
unsigned int reload_period;
int pps_enable;
unsigned int next_counter;
+ bool perout_enable;
struct hrtimer perout_timer;
u64 perout_stime;
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 1edcfaee6819..c685a5c0cc51 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -52,6 +52,7 @@
#include <linux/of_net.h>
#include <linux/phy.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/phy_fixed.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/prefetch.h>
@@ -252,9 +253,7 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
* size bits. Other FEC hardware does not, so we need to take that into
* account when setting it.
*/
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
- defined(CONFIG_ARM64)
+#ifndef CONFIG_M5272
#define OPT_ARCH_HAS_MAX_FL 1
#else
#define OPT_ARCH_HAS_MAX_FL 0
@@ -1011,7 +1010,7 @@ static void fec_enet_bd_init(struct net_device *dev)
/* Set the last buffer to wrap */
bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
- bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
+ bdp->cbd_sc |= cpu_to_fec16(BD_ENET_RX_WRAP);
rxq->bd.cur = rxq->bd.base;
}
@@ -1061,7 +1060,7 @@ static void fec_enet_bd_init(struct net_device *dev)
/* Set the last buffer to wrap */
bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
- bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
+ bdp->cbd_sc |= cpu_to_fec16(BD_ENET_TX_WRAP);
txq->dirty_tx = bdp;
}
}
@@ -1656,8 +1655,7 @@ static int fec_enet_update_cbd(struct fec_enet_priv_rx_q *rxq,
if (unlikely(!new_page))
return -ENOMEM;
- rxq->rx_skb_info[index].page = new_page;
- rxq->rx_skb_info[index].offset = FEC_ENET_XDP_HEADROOM;
+ rxq->rx_buf[index] = new_page;
phys_addr = page_pool_get_dma_addr(new_page) + FEC_ENET_XDP_HEADROOM;
bdp->cbd_bufaddr = cpu_to_fec32(phys_addr);
@@ -1772,7 +1770,6 @@ fec_enet_rx_queue(struct net_device *ndev, u16 queue_id, int budget)
__fec32 cbd_bufaddr;
u32 sub_len = 4;
-#if !defined(CONFIG_M5272)
/*If it has the FEC_QUIRK_HAS_RACC quirk property, the bit of
* FEC_RACC_SHIFT16 is set by default in the probe function.
*/
@@ -1780,7 +1777,6 @@ fec_enet_rx_queue(struct net_device *ndev, u16 queue_id, int budget)
data_start += 2;
sub_len += 2;
}
-#endif
#if defined(CONFIG_COLDFIRE) && !defined(CONFIG_COLDFIRE_COHERENT_DMA)
/*
@@ -1835,9 +1831,11 @@ fec_enet_rx_queue(struct net_device *ndev, u16 queue_id, int budget)
ndev->stats.rx_packets++;
pkt_len = fec16_to_cpu(bdp->cbd_datlen);
ndev->stats.rx_bytes += pkt_len;
+ if (fep->quirks & FEC_QUIRK_HAS_RACC)
+ ndev->stats.rx_bytes -= 2;
index = fec_enet_get_bd_index(bdp, &rxq->bd);
- page = rxq->rx_skb_info[index].page;
+ page = rxq->rx_buf[index];
cbd_bufaddr = bdp->cbd_bufaddr;
if (fec_enet_update_cbd(rxq, bdp, index)) {
ndev->stats.rx_dropped++;
@@ -2231,7 +2229,6 @@ static int fec_enet_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum)
ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
out:
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
@@ -2280,7 +2277,6 @@ static int fec_enet_mdio_read_c45(struct mii_bus *bus, int mii_id,
ret = FEC_MMFR_DATA(readl(fep->hwp + FEC_MII_DATA));
out:
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
@@ -2312,7 +2308,6 @@ static int fec_enet_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum,
if (ret)
netdev_err(fep->netdev, "MDIO write timeout\n");
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
@@ -2356,7 +2351,6 @@ static int fec_enet_mdio_write_c45(struct mii_bus *bus, int mii_id,
netdev_err(fep->netdev, "MDIO write timeout\n");
out:
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
@@ -2476,11 +2470,8 @@ static int fec_enet_parse_rgmii_delay(struct fec_enet_private *fep,
static int fec_enet_mii_probe(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
- struct phy_device *phy_dev = NULL;
- char mdio_bus_id[MII_BUS_ID_SIZE];
- char phy_name[MII_BUS_ID_SIZE + 3];
- int phy_id;
- int dev_id = fep->dev_id;
+ struct phy_device *phy_dev;
+ int ret;
if (fep->phy_node) {
phy_dev = of_phy_connect(ndev, fep->phy_node,
@@ -2492,30 +2483,28 @@ static int fec_enet_mii_probe(struct net_device *ndev)
}
} else {
/* check for attached phy */
- for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
- if (!mdiobus_is_registered_device(fep->mii_bus, phy_id))
- continue;
- if (dev_id--)
- continue;
- strscpy(mdio_bus_id, fep->mii_bus->id, MII_BUS_ID_SIZE);
- break;
- }
+ phy_dev = phy_find_first(fep->mii_bus);
+ if (fep->dev_id && phy_dev)
+ phy_dev = phy_find_next(fep->mii_bus, phy_dev);
- if (phy_id >= PHY_MAX_ADDR) {
+ if (!phy_dev) {
netdev_info(ndev, "no PHY, assuming direct connection to switch\n");
- strscpy(mdio_bus_id, "fixed-0", MII_BUS_ID_SIZE);
- phy_id = 0;
+ phy_dev = fixed_phy_register_100fd();
+ if (IS_ERR(phy_dev)) {
+ netdev_err(ndev, "could not register fixed PHY\n");
+ return PTR_ERR(phy_dev);
+ }
}
- snprintf(phy_name, sizeof(phy_name),
- PHY_ID_FMT, mdio_bus_id, phy_id);
- phy_dev = phy_connect(ndev, phy_name, &fec_enet_adjust_link,
- fep->phy_interface);
- }
+ ret = phy_connect_direct(ndev, phy_dev, &fec_enet_adjust_link,
+ fep->phy_interface);
+ if (ret) {
+ if (phy_is_pseudo_fixed_link(phy_dev))
+ fixed_phy_unregister(phy_dev);
+ netdev_err(ndev, "could not attach to PHY\n");
+ return ret;
+ }
- if (IS_ERR(phy_dev)) {
- netdev_err(ndev, "could not attach to PHY\n");
- return PTR_ERR(phy_dev);
}
/* mask with MAC supported features */
@@ -2523,9 +2512,7 @@ static int fec_enet_mii_probe(struct net_device *ndev)
phy_set_max_speed(phy_dev, 1000);
phy_remove_link_mode(phy_dev,
ETHTOOL_LINK_MODE_1000baseT_Half_BIT);
-#if !defined(CONFIG_M5272)
phy_support_sym_pause(phy_dev);
-#endif
}
else
phy_set_max_speed(phy_dev, 100);
@@ -2552,7 +2539,6 @@ static int fec_enet_mii_init(struct platform_device *pdev)
int err = -ENXIO;
u32 mii_speed, holdtime;
u32 bus_freq;
- int addr;
/*
* The i.MX28 dual fec interfaces are not equal.
@@ -2667,11 +2653,8 @@ static int fec_enet_mii_init(struct platform_device *pdev)
of_node_put(node);
/* find all the PHY devices on the bus and set mac_managed_pm to true */
- for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- phydev = mdiobus_get_phy(fep->mii_bus, addr);
- if (phydev)
- phydev->mac_managed_pm = true;
- }
+ mdiobus_for_each_phy(fep->mii_bus, phydev)
+ phydev->mac_managed_pm = true;
mii_cnt++;
@@ -2720,9 +2703,7 @@ static int fec_enet_get_regs_len(struct net_device *ndev)
}
/* List of registers that can be safety be read to dump them with ethtool */
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
- defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
+#if !defined(CONFIG_M5272) || defined(CONFIG_COMPILE_TEST)
static __u32 fec_enet_register_version = 2;
static u32 fec_enet_register_offset[] = {
FEC_IEVENT, FEC_IMASK, FEC_R_DES_ACTIVE_0, FEC_X_DES_ACTIVE_0,
@@ -2796,30 +2777,22 @@ static u32 fec_enet_register_offset[] = {
static void fec_enet_get_regs(struct net_device *ndev,
struct ethtool_regs *regs, void *regbuf)
{
+ u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset);
struct fec_enet_private *fep = netdev_priv(ndev);
u32 __iomem *theregs = (u32 __iomem *)fep->hwp;
+ u32 *reg_list = fec_enet_register_offset;
struct device *dev = &fep->pdev->dev;
u32 *buf = (u32 *)regbuf;
u32 i, off;
int ret;
-#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x) || defined(CONFIG_M532x) || defined(CONFIG_ARM) || \
- defined(CONFIG_ARM64) || defined(CONFIG_COMPILE_TEST)
- u32 *reg_list;
- u32 reg_cnt;
-
- if (!of_machine_is_compatible("fsl,imx6ul")) {
- reg_list = fec_enet_register_offset;
- reg_cnt = ARRAY_SIZE(fec_enet_register_offset);
- } else {
+
+#if !defined(CONFIG_M5272) || defined(CONFIG_COMPILE_TEST)
+ if (of_machine_is_compatible("fsl,imx6ul")) {
reg_list = fec_enet_register_offset_6ul;
reg_cnt = ARRAY_SIZE(fec_enet_register_offset_6ul);
}
-#else
- /* coldfire */
- static u32 *reg_list = fec_enet_register_offset;
- static const u32 reg_cnt = ARRAY_SIZE(fec_enet_register_offset);
#endif
+
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return;
@@ -2839,7 +2812,6 @@ static void fec_enet_get_regs(struct net_device *ndev,
buf[off] = readl(&theregs[off]);
}
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
@@ -3339,7 +3311,8 @@ 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_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false);
+ page_pool_put_full_page(rxq->page_pool, rxq->rx_buf[i],
+ false);
for (i = 0; i < XDP_STATS_TOTAL; i++)
rxq->stats[i] = 0;
@@ -3465,6 +3438,19 @@ fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue)
return err;
}
+ /* Some platforms require the RX buffer must be 64 bytes alignment.
+ * Some platforms require 16 bytes alignment. And some platforms
+ * require 4 bytes alignment. But since the page pool have been
+ * introduced into the driver, the address of RX buffer is always
+ * the page address plus FEC_ENET_XDP_HEADROOM, and
+ * FEC_ENET_XDP_HEADROOM is 256 bytes. Therefore, this address can
+ * satisfy all platforms. To prevent future modifications to
+ * FEC_ENET_XDP_HEADROOM from ignoring this hardware limitation, a
+ * BUILD_BUG_ON() test has been added, which ensures that
+ * FEC_ENET_XDP_HEADROOM provides the required alignment.
+ */
+ BUILD_BUG_ON(FEC_ENET_XDP_HEADROOM & 0x3f);
+
for (i = 0; i < rxq->bd.ring_size; i++) {
page = page_pool_dev_alloc_pages(rxq->page_pool);
if (!page)
@@ -3473,8 +3459,7 @@ fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue)
phys_addr = page_pool_get_dma_addr(page) + FEC_ENET_XDP_HEADROOM;
bdp->cbd_bufaddr = cpu_to_fec32(phys_addr);
- rxq->rx_skb_info[i].page = page;
- rxq->rx_skb_info[i].offset = FEC_ENET_XDP_HEADROOM;
+ rxq->rx_buf[i] = page;
bdp->cbd_sc = cpu_to_fec16(BD_ENET_RX_EMPTY);
if (fep->bufdesc_ex) {
@@ -3487,7 +3472,7 @@ fec_enet_alloc_rxq_buffers(struct net_device *ndev, unsigned int queue)
/* Set the last buffer to wrap. */
bdp = fec_enet_get_prevdesc(bdp, &rxq->bd);
- bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
+ bdp->cbd_sc |= cpu_to_fec16(BD_ENET_RX_WRAP);
return 0;
err_alloc:
@@ -3523,7 +3508,7 @@ fec_enet_alloc_txq_buffers(struct net_device *ndev, unsigned int queue)
/* Set the last buffer to wrap. */
bdp = fec_enet_get_prevdesc(bdp, &txq->bd);
- bdp->cbd_sc |= cpu_to_fec16(BD_SC_WRAP);
+ bdp->cbd_sc |= cpu_to_fec16(BD_ENET_TX_WRAP);
return 0;
@@ -3616,7 +3601,6 @@ err_enet_mii_probe:
err_enet_alloc:
fec_enet_clk_enable(ndev, false);
clk_enable:
- pm_runtime_mark_last_busy(&fep->pdev->dev);
pm_runtime_put_autosuspend(&fep->pdev->dev);
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
return ret;
@@ -3626,8 +3610,9 @@ static int
fec_enet_close(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
+ struct phy_device *phy_dev = ndev->phydev;
- phy_stop(ndev->phydev);
+ phy_stop(phy_dev);
if (netif_device_present(ndev)) {
napi_disable(&fep->napi);
@@ -3635,7 +3620,10 @@ fec_enet_close(struct net_device *ndev)
fec_stop(ndev);
}
- phy_disconnect(ndev->phydev);
+ phy_disconnect(phy_dev);
+
+ if (!fep->phy_node && phy_is_pseudo_fixed_link(phy_dev))
+ fixed_phy_unregister(phy_dev);
if (fep->quirks & FEC_QUIRK_ERR006687)
imx6q_cpuidle_fec_irqs_unused();
@@ -3647,7 +3635,6 @@ fec_enet_close(struct net_device *ndev)
cpu_latency_qos_remove_request(&fep->pm_qos_req);
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
- pm_runtime_mark_last_busy(&fep->pdev->dev);
pm_runtime_put_autosuspend(&fep->pdev->dev);
fec_enet_free_buffers(ndev);
@@ -4098,10 +4085,8 @@ static int fec_enet_init(struct net_device *ndev)
WARN_ON(dsize != (1 << dsize_log2));
#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
- fep->rx_align = 0xf;
fep->tx_align = 0xf;
#else
- fep->rx_align = 0x3;
fep->tx_align = 0x3;
#endif
fep->rx_pkts_itr = FEC_ITR_ICFT_DEFAULT;
@@ -4190,10 +4175,8 @@ static int fec_enet_init(struct net_device *ndev)
fep->csum_flags |= FLAG_RX_CSUM_ENABLED;
}
- if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES) {
+ if (fep->quirks & FEC_QUIRK_HAS_MULTI_QUEUES)
fep->tx_align = 0;
- fep->rx_align = 0x3f;
- }
ndev->hw_features = ndev->features;
@@ -4411,11 +4394,9 @@ fec_probe(struct platform_device *pdev)
fep->num_rx_queues = num_rx_qs;
fep->num_tx_queues = num_tx_qs;
-#if !defined(CONFIG_M5272)
/* default enable pause frame auto negotiation */
if (fep->quirks & FEC_QUIRK_HAS_GBIT)
fep->pause_flag |= FEC_PAUSE_FLAG_AUTONEG;
-#endif
/* Select default pin state */
pinctrl_pm_select_default_state(&pdev->dev);
@@ -4616,7 +4597,6 @@ fec_probe(struct platform_device *pdev)
INIT_WORK(&fep->tx_timeout_work, fec_enet_timeout_work);
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c
index fa88b47d526c..4b7bad9a485d 100644
--- a/drivers/net/ethernet/freescale/fec_ptp.c
+++ b/drivers/net/ethernet/freescale/fec_ptp.c
@@ -128,6 +128,12 @@ static int fec_ptp_enable_pps(struct fec_enet_private *fep, uint enable)
spin_lock_irqsave(&fep->tmreg_lock, flags);
+ if (fep->perout_enable) {
+ spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+ dev_err(&fep->pdev->dev, "PEROUT is running");
+ return -EBUSY;
+ }
+
if (fep->pps_enable == enable) {
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
return 0;
@@ -243,6 +249,7 @@ static int fec_ptp_pps_perout(struct fec_enet_private *fep)
* the FEC_TCCR register in time and missed the start time.
*/
if (fep->perout_stime < curr_time + 100 * NSEC_PER_MSEC) {
+ fep->perout_enable = false;
dev_err(&fep->pdev->dev, "Current time is too close to the start time!\n");
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
return -1;
@@ -497,7 +504,10 @@ static int fec_ptp_pps_disable(struct fec_enet_private *fep, uint channel)
{
unsigned long flags;
+ hrtimer_cancel(&fep->perout_timer);
+
spin_lock_irqsave(&fep->tmreg_lock, flags);
+ fep->perout_enable = false;
writel(0, fep->hwp + FEC_TCSR(channel));
spin_unlock_irqrestore(&fep->tmreg_lock, flags);
@@ -529,6 +539,8 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp,
return ret;
} else if (rq->type == PTP_CLK_REQ_PEROUT) {
+ u32 reload_period;
+
/* Reject requests with unsupported flags */
if (rq->perout.flags)
return -EOPNOTSUPP;
@@ -548,12 +560,14 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp,
return -EOPNOTSUPP;
}
- fep->reload_period = div_u64(period_ns, 2);
- if (on && fep->reload_period) {
+ reload_period = div_u64(period_ns, 2);
+ if (on && reload_period) {
+ u64 perout_stime;
+
/* Convert 1588 timestamp to ns*/
start_time.tv_sec = rq->perout.start.sec;
start_time.tv_nsec = rq->perout.start.nsec;
- fep->perout_stime = timespec64_to_ns(&start_time);
+ perout_stime = timespec64_to_ns(&start_time);
mutex_lock(&fep->ptp_clk_mutex);
if (!fep->ptp_clk_on) {
@@ -562,18 +576,41 @@ static int fec_ptp_enable(struct ptp_clock_info *ptp,
return -EOPNOTSUPP;
}
spin_lock_irqsave(&fep->tmreg_lock, flags);
+
+ if (fep->pps_enable) {
+ dev_err(&fep->pdev->dev, "PPS is running");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (fep->perout_enable) {
+ dev_err(&fep->pdev->dev,
+ "PEROUT has been enabled\n");
+ ret = -EBUSY;
+ goto unlock;
+ }
+
/* Read current timestamp */
curr_time = timecounter_read(&fep->tc);
- spin_unlock_irqrestore(&fep->tmreg_lock, flags);
- mutex_unlock(&fep->ptp_clk_mutex);
+ if (perout_stime <= curr_time) {
+ dev_err(&fep->pdev->dev,
+ "Start time must be greater than current time\n");
+ ret = -EINVAL;
+ goto unlock;
+ }
/* Calculate time difference */
- delta = fep->perout_stime - curr_time;
+ delta = perout_stime - curr_time;
+ fep->reload_period = reload_period;
+ fep->perout_stime = perout_stime;
+ fep->perout_enable = true;
- if (fep->perout_stime <= curr_time) {
- dev_err(&fep->pdev->dev, "Start time must larger than current time!\n");
- return -EINVAL;
- }
+unlock:
+ spin_unlock_irqrestore(&fep->tmreg_lock, flags);
+ mutex_unlock(&fep->ptp_clk_mutex);
+
+ if (ret)
+ return ret;
/* Because the timer counter of FEC only has 31-bits, correspondingly,
* the time comparison register FEC_TCCR also only low 31 bits can be
@@ -681,8 +718,11 @@ static irqreturn_t fec_pps_interrupt(int irq, void *dev_id)
fep->next_counter = (fep->next_counter + fep->reload_period) &
fep->cc.mask;
- event.type = PTP_CLOCK_PPS;
- ptp_clock_event(fep->ptp_clock, &event);
+ if (fep->pps_enable) {
+ event.type = PTP_CLOCK_PPS;
+ ptp_clock_event(fep->ptp_clock, &event);
+ }
+
return IRQ_HANDLED;
}
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c
index 0291093f2e4e..c84f0336c94c 100644
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
@@ -649,6 +649,7 @@ static u32 memac_if_mode(phy_interface_t interface)
return IF_MODE_GMII | IF_MODE_RGMII;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_QSGMII:
return IF_MODE_GMII;
case PHY_INTERFACE_MODE_10GBASER:
@@ -667,6 +668,7 @@ static struct phylink_pcs *memac_select_pcs(struct phylink_config *config,
switch (iface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
return memac->sgmii_pcs;
case PHY_INTERFACE_MODE_QSGMII:
return memac->qsgmii_pcs;
@@ -685,6 +687,7 @@ static int memac_prepare(struct phylink_config *config, unsigned int mode,
switch (iface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_QSGMII:
case PHY_INTERFACE_MODE_10GBASER:
return phy_set_mode_ext(memac->serdes, PHY_MODE_ETHERNET,
@@ -897,6 +900,89 @@ static int memac_set_exception(struct fman_mac *memac,
return 0;
}
+static u64 memac_read64(void __iomem *reg)
+{
+ u32 low, high, tmp;
+
+ do {
+ high = ioread32be(reg + 4);
+ low = ioread32be(reg);
+ tmp = ioread32be(reg + 4);
+ } while (high != tmp);
+
+ return ((u64)high << 32) | low;
+}
+
+static void memac_get_pause_stats(struct fman_mac *memac,
+ struct ethtool_pause_stats *s)
+{
+ s->tx_pause_frames = memac_read64(&memac->regs->txpf_l);
+ s->rx_pause_frames = memac_read64(&memac->regs->rxpf_l);
+}
+
+static const struct ethtool_rmon_hist_range memac_rmon_ranges[] = {
+ { 64, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1518 },
+ { 1519, 9600 },
+ {},
+};
+
+static void memac_get_rmon_stats(struct fman_mac *memac,
+ struct ethtool_rmon_stats *s,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ s->undersize_pkts = memac_read64(&memac->regs->rund_l);
+ s->oversize_pkts = memac_read64(&memac->regs->rovr_l);
+ s->fragments = memac_read64(&memac->regs->rfrg_l);
+ s->jabbers = memac_read64(&memac->regs->rjbr_l);
+
+ s->hist[0] = memac_read64(&memac->regs->r64_l);
+ s->hist[1] = memac_read64(&memac->regs->r127_l);
+ s->hist[2] = memac_read64(&memac->regs->r255_l);
+ s->hist[3] = memac_read64(&memac->regs->r511_l);
+ s->hist[4] = memac_read64(&memac->regs->r1023_l);
+ s->hist[5] = memac_read64(&memac->regs->r1518_l);
+ s->hist[6] = memac_read64(&memac->regs->r1519x_l);
+
+ s->hist_tx[0] = memac_read64(&memac->regs->t64_l);
+ s->hist_tx[1] = memac_read64(&memac->regs->t127_l);
+ s->hist_tx[2] = memac_read64(&memac->regs->t255_l);
+ s->hist_tx[3] = memac_read64(&memac->regs->t511_l);
+ s->hist_tx[4] = memac_read64(&memac->regs->t1023_l);
+ s->hist_tx[5] = memac_read64(&memac->regs->t1518_l);
+ s->hist_tx[6] = memac_read64(&memac->regs->t1519x_l);
+
+ *ranges = memac_rmon_ranges;
+}
+
+static void memac_get_eth_ctrl_stats(struct fman_mac *memac,
+ struct ethtool_eth_ctrl_stats *s)
+{
+ s->MACControlFramesTransmitted = memac_read64(&memac->regs->tcnp_l);
+ s->MACControlFramesReceived = memac_read64(&memac->regs->rcnp_l);
+}
+
+static void memac_get_eth_mac_stats(struct fman_mac *memac,
+ struct ethtool_eth_mac_stats *s)
+{
+ s->FramesTransmittedOK = memac_read64(&memac->regs->tfrm_l);
+ s->FramesReceivedOK = memac_read64(&memac->regs->rfrm_l);
+ s->FrameCheckSequenceErrors = memac_read64(&memac->regs->rfcs_l);
+ s->AlignmentErrors = memac_read64(&memac->regs->raln_l);
+ s->OctetsTransmittedOK = memac_read64(&memac->regs->teoct_l);
+ s->FramesLostDueToIntMACXmitError = memac_read64(&memac->regs->terr_l);
+ s->OctetsReceivedOK = memac_read64(&memac->regs->reoct_l);
+ s->FramesLostDueToIntMACRcvError = memac_read64(&memac->regs->rdrntp_l);
+ s->MulticastFramesXmittedOK = memac_read64(&memac->regs->tmca_l);
+ s->BroadcastFramesXmittedOK = memac_read64(&memac->regs->tbca_l);
+ s->MulticastFramesReceivedOK = memac_read64(&memac->regs->rmca_l);
+ s->BroadcastFramesReceivedOK = memac_read64(&memac->regs->rbca_l);
+}
+
static int memac_init(struct fman_mac *memac)
{
struct memac_cfg *memac_drv_param;
@@ -1089,6 +1175,10 @@ int memac_initialization(struct mac_device *mac_dev,
mac_dev->set_tstamp = memac_set_tstamp;
mac_dev->enable = memac_enable;
mac_dev->disable = memac_disable;
+ mac_dev->get_pause_stats = memac_get_pause_stats;
+ mac_dev->get_rmon_stats = memac_get_rmon_stats;
+ mac_dev->get_eth_ctrl_stats = memac_get_eth_ctrl_stats;
+ mac_dev->get_eth_mac_stats = memac_get_eth_mac_stats;
mac_dev->fman_mac = memac_config(mac_dev, params);
if (!mac_dev->fman_mac)
@@ -1226,6 +1316,7 @@ int memac_initialization(struct mac_device *mac_dev,
* those configurations modes don't use in-band autonegotiation.
*/
if (!of_property_present(mac_node, "managed") &&
+ mac_dev->phy_if != PHY_INTERFACE_MODE_2500BASEX &&
mac_dev->phy_if != PHY_INTERFACE_MODE_MII &&
!phy_interface_mode_is_rgmii(mac_dev->phy_if))
mac_dev->phylink_config.default_an_inband = true;
diff --git a/drivers/net/ethernet/freescale/fman/mac.h b/drivers/net/ethernet/freescale/fman/mac.h
index 955ace338965..63c2c5b4f99e 100644
--- a/drivers/net/ethernet/freescale/fman/mac.h
+++ b/drivers/net/ethernet/freescale/fman/mac.h
@@ -16,6 +16,11 @@
#include "fman.h"
#include "fman_mac.h"
+struct ethtool_eth_ctrl_stats;
+struct ethtool_eth_mac_stats;
+struct ethtool_pause_stats;
+struct ethtool_rmon_stats;
+struct ethtool_rmon_hist_range;
struct fman_mac;
struct mac_priv_s;
@@ -46,6 +51,15 @@ struct mac_device {
enet_addr_t *eth_addr);
int (*remove_hash_mac_addr)(struct fman_mac *mac_dev,
enet_addr_t *eth_addr);
+ void (*get_pause_stats)(struct fman_mac *memac,
+ struct ethtool_pause_stats *s);
+ void (*get_rmon_stats)(struct fman_mac *memac,
+ struct ethtool_rmon_stats *s,
+ const struct ethtool_rmon_hist_range **ranges);
+ void (*get_eth_ctrl_stats)(struct fman_mac *memac,
+ struct ethtool_eth_ctrl_stats *s);
+ void (*get_eth_mac_stats)(struct fman_mac *memac,
+ struct ethtool_eth_mac_stats *s);
void (*update_speed)(struct mac_device *mac_dev, int speed);
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 5fd1f7327680..6fa752d3b60d 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -1431,6 +1431,13 @@ static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
return ret;
}
+static u32 gfar_get_rx_ring_count(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+
+ return priv->num_rx_queues;
+}
+
static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
@@ -1438,9 +1445,6 @@ static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
int ret = 0;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = priv->num_rx_queues;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = priv->rx_list.count;
break;
@@ -1519,6 +1523,7 @@ const struct ethtool_ops gfar_ethtool_ops = {
#endif
.set_rxnfc = gfar_set_nfc,
.get_rxnfc = gfar_get_nfc,
+ .get_rx_ring_count = gfar_get_rx_ring_count,
.set_rxfh_fields = gfar_set_rxfh_fields,
.get_ts_info = gfar_get_ts_info,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
diff --git a/drivers/net/ethernet/fungible/funeth/funeth.h b/drivers/net/ethernet/fungible/funeth/funeth.h
index 1250e10d21db..55e705e239f8 100644
--- a/drivers/net/ethernet/fungible/funeth/funeth.h
+++ b/drivers/net/ethernet/fungible/funeth/funeth.h
@@ -4,7 +4,7 @@
#define _FUNETH_H
#include <uapi/linux/if_ether.h>
-#include <uapi/linux/net_tstamp.h>
+#include <linux/net_tstamp.h>
#include <linux/mutex.h>
#include <linux/seqlock.h>
#include <linux/xarray.h>
@@ -121,7 +121,7 @@ struct funeth_priv {
u8 rx_coal_usec;
u8 rx_coal_count;
- struct hwtstamp_config hwtstamp_cfg;
+ struct kernel_hwtstamp_config hwtstamp_cfg;
/* cumulative queue stats from earlier queue instances */
u64 tx_packets;
diff --git a/drivers/net/ethernet/fungible/funeth/funeth_main.c b/drivers/net/ethernet/fungible/funeth/funeth_main.c
index ac86179a0a81..792cddac6f1b 100644
--- a/drivers/net/ethernet/fungible/funeth/funeth_main.c
+++ b/drivers/net/ethernet/fungible/funeth/funeth_main.c
@@ -1014,26 +1014,25 @@ static int fun_get_port_attributes(struct net_device *netdev)
return 0;
}
-static int fun_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
+static int fun_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
{
const struct funeth_priv *fp = netdev_priv(dev);
- return copy_to_user(ifr->ifr_data, &fp->hwtstamp_cfg,
- sizeof(fp->hwtstamp_cfg)) ? -EFAULT : 0;
+ *config = fp->hwtstamp_cfg;
+ return 0;
}
-static int fun_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
+static int fun_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct funeth_priv *fp = netdev_priv(dev);
- struct hwtstamp_config cfg;
-
- if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
- return -EFAULT;
/* no TX HW timestamps */
- cfg.tx_type = HWTSTAMP_TX_OFF;
+ config->tx_type = HWTSTAMP_TX_OFF;
- switch (cfg.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_ALL:
@@ -1051,26 +1050,14 @@ static int fun_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
- cfg.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
- fp->hwtstamp_cfg = cfg;
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
-}
-
-static int fun_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return fun_hwtstamp_set(dev, ifr);
- case SIOCGHWTSTAMP:
- return fun_hwtstamp_get(dev, ifr);
- default:
- return -EOPNOTSUPP;
- }
+ fp->hwtstamp_cfg = *config;
+ return 0;
}
/* Prepare the queues for XDP. */
@@ -1340,7 +1327,6 @@ static const struct net_device_ops fun_netdev_ops = {
.ndo_change_mtu = fun_change_mtu,
.ndo_set_mac_address = fun_set_macaddr,
.ndo_validate_addr = eth_validate_addr,
- .ndo_eth_ioctl = fun_ioctl,
.ndo_uninit = fun_uninit,
.ndo_bpf = fun_xdp,
.ndo_xdp_xmit = fun_xdp_xmit_frames,
@@ -1348,6 +1334,8 @@ static const struct net_device_ops fun_netdev_ops = {
.ndo_set_vf_vlan = fun_set_vf_vlan,
.ndo_set_vf_rate = fun_set_vf_rate,
.ndo_get_vf_config = fun_get_vf_config,
+ .ndo_hwtstamp_get = fun_hwtstamp_get,
+ .ndo_hwtstamp_set = fun_hwtstamp_set,
};
#define GSO_ENCAP_FLAGS (NETIF_F_GSO_GRE | NETIF_F_GSO_IPXIP4 | \
diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h
index 4cc6dcbfd367..970d5ca8cdde 100644
--- a/drivers/net/ethernet/google/gve/gve.h
+++ b/drivers/net/ethernet/google/gve/gve.h
@@ -59,8 +59,6 @@
#define GVE_DEFAULT_RX_BUFFER_SIZE 2048
-#define GVE_MAX_RX_BUFFER_SIZE 4096
-
#define GVE_XDP_RX_BUFFER_SIZE_DQO 4096
#define GVE_DEFAULT_RX_BUFFER_OFFSET 2048
@@ -207,6 +205,13 @@ struct gve_rx_buf_state_dqo {
s16 next;
};
+/* Wrapper for XDP Rx metadata */
+struct gve_xdp_buff {
+ struct xdp_buff xdp;
+ struct gve_priv *gve;
+ const struct gve_rx_compl_desc_dqo *compl_desc;
+};
+
/* `head` and `tail` are indices into an array, or -1 if empty. */
struct gve_index_list {
s16 head;
@@ -1169,6 +1174,12 @@ static inline bool gve_is_gqi(struct gve_priv *priv)
priv->queue_format == GVE_GQI_QPL_FORMAT;
}
+static inline bool gve_is_dqo(struct gve_priv *priv)
+{
+ return priv->queue_format == GVE_DQO_RDA_FORMAT ||
+ priv->queue_format == GVE_DQO_QPL_FORMAT;
+}
+
static inline u32 gve_num_tx_queues(struct gve_priv *priv)
{
return priv->tx_cfg.num_queues + priv->tx_cfg.num_xdp_queues;
@@ -1249,9 +1260,12 @@ void gve_rx_free_rings_gqi(struct gve_priv *priv,
struct gve_rx_alloc_rings_cfg *cfg);
void gve_rx_start_ring_gqi(struct gve_priv *priv, int idx);
void gve_rx_stop_ring_gqi(struct gve_priv *priv, int idx);
-u16 gve_get_pkt_buf_size(const struct gve_priv *priv, bool enable_hplit);
bool gve_header_split_supported(const struct gve_priv *priv);
-int gve_set_hsplit_config(struct gve_priv *priv, u8 tcp_data_split);
+int gve_set_rx_buf_len_config(struct gve_priv *priv, u32 rx_buf_len,
+ struct netlink_ext_ack *extack,
+ struct gve_rx_alloc_rings_cfg *rx_alloc_cfg);
+int gve_set_hsplit_config(struct gve_priv *priv, u8 tcp_data_split,
+ struct gve_rx_alloc_rings_cfg *rx_alloc_cfg);
/* rx buffer handling */
int gve_buf_ref_cnt(struct gve_rx_buf_state_dqo *bs);
void gve_free_page_dqo(struct gve_priv *priv, struct gve_rx_buf_state_dqo *bs,
diff --git a/drivers/net/ethernet/google/gve/gve_adminq.c b/drivers/net/ethernet/google/gve/gve_adminq.c
index 4f33d094a2ef..b72cc0fa2ba2 100644
--- a/drivers/net/ethernet/google/gve/gve_adminq.c
+++ b/drivers/net/ethernet/google/gve/gve_adminq.c
@@ -987,6 +987,10 @@ static void gve_enable_supported_features(struct gve_priv *priv,
dev_info(&priv->pdev->dev,
"BUFFER SIZES device option enabled with max_rx_buffer_size of %u, header_buf_size of %u.\n",
priv->max_rx_buffer_size, priv->header_buf_size);
+ if (gve_is_dqo(priv) &&
+ priv->max_rx_buffer_size > GVE_DEFAULT_RX_BUFFER_SIZE)
+ priv->rx_cfg.packet_buffer_size =
+ priv->max_rx_buffer_size;
}
/* Read and store ring size ranges given by device */
diff --git a/drivers/net/ethernet/google/gve/gve_dqo.h b/drivers/net/ethernet/google/gve/gve_dqo.h
index 6eb442096e02..5871f773f0c7 100644
--- a/drivers/net/ethernet/google/gve/gve_dqo.h
+++ b/drivers/net/ethernet/google/gve/gve_dqo.h
@@ -36,6 +36,7 @@ netdev_tx_t gve_tx_dqo(struct sk_buff *skb, struct net_device *dev);
netdev_features_t gve_features_check_dqo(struct sk_buff *skb,
struct net_device *dev,
netdev_features_t features);
+int gve_xdp_rx_timestamp(const struct xdp_md *_ctx, u64 *timestamp);
bool gve_tx_poll_dqo(struct gve_notify_block *block, bool do_clean);
bool gve_xdp_poll_dqo(struct gve_notify_block *block);
bool gve_xsk_tx_poll_dqo(struct gve_notify_block *block, int budget);
diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c
index d0a223250845..52500ae8348e 100644
--- a/drivers/net/ethernet/google/gve/gve_ethtool.c
+++ b/drivers/net/ethernet/google/gve/gve_ethtool.c
@@ -529,6 +529,8 @@ static void gve_get_ringparam(struct net_device *netdev,
cmd->rx_pending = priv->rx_desc_cnt;
cmd->tx_pending = priv->tx_desc_cnt;
+ kernel_cmd->rx_buf_len = priv->rx_cfg.packet_buffer_size;
+
if (!gve_header_split_supported(priv))
kernel_cmd->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_UNKNOWN;
else if (priv->header_split_enabled)
@@ -537,34 +539,6 @@ static void gve_get_ringparam(struct net_device *netdev,
kernel_cmd->tcp_data_split = ETHTOOL_TCP_DATA_SPLIT_DISABLED;
}
-static int gve_adjust_ring_sizes(struct gve_priv *priv,
- u16 new_tx_desc_cnt,
- u16 new_rx_desc_cnt)
-{
- struct gve_tx_alloc_rings_cfg tx_alloc_cfg = {0};
- struct gve_rx_alloc_rings_cfg rx_alloc_cfg = {0};
- int err;
-
- /* get current queue configuration */
- gve_get_curr_alloc_cfgs(priv, &tx_alloc_cfg, &rx_alloc_cfg);
-
- /* copy over the new ring_size from ethtool */
- tx_alloc_cfg.ring_size = new_tx_desc_cnt;
- rx_alloc_cfg.ring_size = new_rx_desc_cnt;
-
- if (netif_running(priv->dev)) {
- err = gve_adjust_config(priv, &tx_alloc_cfg, &rx_alloc_cfg);
- if (err)
- return err;
- }
-
- /* Set new ring_size for the next up */
- priv->tx_desc_cnt = new_tx_desc_cnt;
- priv->rx_desc_cnt = new_rx_desc_cnt;
-
- return 0;
-}
-
static int gve_validate_req_ring_size(struct gve_priv *priv, u16 new_tx_desc_cnt,
u16 new_rx_desc_cnt)
{
@@ -584,34 +558,68 @@ static int gve_validate_req_ring_size(struct gve_priv *priv, u16 new_tx_desc_cnt
return 0;
}
+static int gve_set_ring_sizes_config(struct gve_priv *priv, u16 new_tx_desc_cnt,
+ u16 new_rx_desc_cnt,
+ struct gve_tx_alloc_rings_cfg *tx_alloc_cfg,
+ struct gve_rx_alloc_rings_cfg *rx_alloc_cfg)
+{
+ if (new_tx_desc_cnt == priv->tx_desc_cnt &&
+ new_rx_desc_cnt == priv->rx_desc_cnt)
+ return 0;
+
+ if (!priv->modify_ring_size_enabled) {
+ dev_err(&priv->pdev->dev, "Modify ring size is not supported.\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (gve_validate_req_ring_size(priv, new_tx_desc_cnt, new_rx_desc_cnt))
+ return -EINVAL;
+
+ tx_alloc_cfg->ring_size = new_tx_desc_cnt;
+ rx_alloc_cfg->ring_size = new_rx_desc_cnt;
+ return 0;
+}
+
static int gve_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *cmd,
struct kernel_ethtool_ringparam *kernel_cmd,
struct netlink_ext_ack *extack)
{
+ struct gve_tx_alloc_rings_cfg tx_alloc_cfg = {0};
+ struct gve_rx_alloc_rings_cfg rx_alloc_cfg = {0};
struct gve_priv *priv = netdev_priv(netdev);
- u16 new_tx_cnt, new_rx_cnt;
int err;
- err = gve_set_hsplit_config(priv, kernel_cmd->tcp_data_split);
+ gve_get_curr_alloc_cfgs(priv, &tx_alloc_cfg, &rx_alloc_cfg);
+
+ err = gve_set_rx_buf_len_config(priv, kernel_cmd->rx_buf_len, extack,
+ &rx_alloc_cfg);
if (err)
return err;
- if (cmd->tx_pending == priv->tx_desc_cnt && cmd->rx_pending == priv->rx_desc_cnt)
- return 0;
-
- if (!priv->modify_ring_size_enabled) {
- dev_err(&priv->pdev->dev, "Modify ring size is not supported.\n");
- return -EOPNOTSUPP;
- }
-
- new_tx_cnt = cmd->tx_pending;
- new_rx_cnt = cmd->rx_pending;
+ err = gve_set_hsplit_config(priv, kernel_cmd->tcp_data_split,
+ &rx_alloc_cfg);
+ if (err)
+ return err;
- if (gve_validate_req_ring_size(priv, new_tx_cnt, new_rx_cnt))
- return -EINVAL;
+ err = gve_set_ring_sizes_config(priv, cmd->tx_pending, cmd->rx_pending,
+ &tx_alloc_cfg, &rx_alloc_cfg);
+ if (err)
+ return err;
- return gve_adjust_ring_sizes(priv, new_tx_cnt, new_rx_cnt);
+ if (netif_running(priv->dev)) {
+ err = gve_adjust_config(priv, &tx_alloc_cfg, &rx_alloc_cfg);
+ if (err)
+ return err;
+ } else {
+ /* Set ring params for the next up */
+ priv->rx_cfg.packet_buffer_size =
+ rx_alloc_cfg.packet_buffer_size;
+ priv->header_split_enabled = rx_alloc_cfg.enable_header_split;
+ priv->tx_desc_cnt = tx_alloc_cfg.ring_size;
+ priv->rx_desc_cnt = rx_alloc_cfg.ring_size;
+ }
+ return 0;
}
static int gve_user_reset(struct net_device *netdev, u32 *flags)
@@ -946,7 +954,8 @@ static int gve_get_ts_info(struct net_device *netdev,
const struct ethtool_ops gve_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
- .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT,
+ .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT |
+ ETHTOOL_RING_USE_RX_BUF_LEN,
.get_drvinfo = gve_get_drvinfo,
.get_strings = gve_get_strings,
.get_sset_count = gve_get_sset_count,
diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c
index 1be1b1ef31ee..a5a2b18d309b 100644
--- a/drivers/net/ethernet/google/gve/gve_main.c
+++ b/drivers/net/ethernet/google/gve/gve_main.c
@@ -1707,18 +1707,28 @@ static int gve_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags)
return 0;
}
-static int verify_xdp_configuration(struct net_device *dev)
+static int gve_verify_xdp_configuration(struct net_device *dev,
+ struct netlink_ext_ack *extack)
{
struct gve_priv *priv = netdev_priv(dev);
u16 max_xdp_mtu;
if (dev->features & NETIF_F_LRO) {
- netdev_warn(dev, "XDP is not supported when LRO is on.\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "XDP is not supported when LRO is on.");
return -EOPNOTSUPP;
}
if (priv->header_split_enabled) {
- netdev_warn(dev, "XDP is not supported when header-data split is enabled.\n");
+ NL_SET_ERR_MSG_MOD(extack,
+ "XDP is not supported when header-data split is enabled.");
+ return -EOPNOTSUPP;
+ }
+
+ if (priv->rx_cfg.packet_buffer_size != SZ_2K) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "XDP is not supported for Rx buf len %d, only %d supported.",
+ priv->rx_cfg.packet_buffer_size, SZ_2K);
return -EOPNOTSUPP;
}
@@ -1727,17 +1737,20 @@ static int verify_xdp_configuration(struct net_device *dev)
max_xdp_mtu -= GVE_RX_PAD;
if (dev->mtu > max_xdp_mtu) {
- netdev_warn(dev, "XDP is not supported for mtu %d.\n",
- dev->mtu);
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "XDP is not supported for mtu %d.",
+ dev->mtu);
return -EOPNOTSUPP;
}
if (priv->rx_cfg.num_queues != priv->tx_cfg.num_queues ||
(2 * priv->tx_cfg.num_queues > priv->tx_cfg.max_queues)) {
- netdev_warn(dev, "XDP load failed: The number of configured RX queues %d should be equal to the number of configured TX queues %d and the number of configured RX/TX queues should be less than or equal to half the maximum number of RX/TX queues %d",
- priv->rx_cfg.num_queues,
- priv->tx_cfg.num_queues,
+ netdev_warn(dev,
+ "XDP load failed: The number of configured RX queues %d should be equal to the number of configured TX queues %d and the number of configured RX/TX queues should be less than or equal to half the maximum number of RX/TX queues %d.",
+ priv->rx_cfg.num_queues, priv->tx_cfg.num_queues,
priv->tx_cfg.max_queues);
+ NL_SET_ERR_MSG_MOD(extack,
+ "XDP load failed: The number of configured RX queues should be equal to the number of configured TX queues and the number of configured RX/TX queues should be less than or equal to half the maximum number of RX/TX queues");
return -EINVAL;
}
return 0;
@@ -1748,7 +1761,7 @@ static int gve_xdp(struct net_device *dev, struct netdev_bpf *xdp)
struct gve_priv *priv = netdev_priv(dev);
int err;
- err = verify_xdp_configuration(dev);
+ err = gve_verify_xdp_configuration(dev, xdp->extack);
if (err)
return err;
switch (xdp->command) {
@@ -2041,14 +2054,6 @@ static void gve_tx_timeout(struct net_device *dev, unsigned int txqueue)
priv->tx_timeo_cnt++;
}
-u16 gve_get_pkt_buf_size(const struct gve_priv *priv, bool enable_hsplit)
-{
- if (enable_hsplit && priv->max_rx_buffer_size >= GVE_MAX_RX_BUFFER_SIZE)
- return GVE_MAX_RX_BUFFER_SIZE;
- else
- return GVE_DEFAULT_RX_BUFFER_SIZE;
-}
-
/* Header split is only supported on DQ RDA queue format. If XDP is enabled,
* header split is not allowed.
*/
@@ -2058,12 +2063,42 @@ bool gve_header_split_supported(const struct gve_priv *priv)
priv->queue_format == GVE_DQO_RDA_FORMAT && !priv->xdp_prog;
}
-int gve_set_hsplit_config(struct gve_priv *priv, u8 tcp_data_split)
+int gve_set_rx_buf_len_config(struct gve_priv *priv, u32 rx_buf_len,
+ struct netlink_ext_ack *extack,
+ struct gve_rx_alloc_rings_cfg *rx_alloc_cfg)
+{
+ u32 old_rx_buf_len = rx_alloc_cfg->packet_buffer_size;
+
+ if (rx_buf_len == old_rx_buf_len)
+ return 0;
+
+ /* device options may not always contain support for 4K buffers */
+ if (!gve_is_dqo(priv) || priv->max_rx_buffer_size < SZ_4K) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Modifying Rx buf len is not supported");
+ return -EOPNOTSUPP;
+ }
+
+ if (priv->xdp_prog && rx_buf_len != SZ_2K) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Rx buf len can only be 2048 when XDP is on");
+ return -EINVAL;
+ }
+
+ if (rx_buf_len != SZ_2K && rx_buf_len != SZ_4K) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Rx buf len can only be 2048 or 4096");
+ return -EINVAL;
+ }
+ rx_alloc_cfg->packet_buffer_size = rx_buf_len;
+
+ return 0;
+}
+
+int gve_set_hsplit_config(struct gve_priv *priv, u8 tcp_data_split,
+ struct gve_rx_alloc_rings_cfg *rx_alloc_cfg)
{
- struct gve_tx_alloc_rings_cfg tx_alloc_cfg = {0};
- struct gve_rx_alloc_rings_cfg rx_alloc_cfg = {0};
bool enable_hdr_split;
- int err = 0;
if (tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_UNKNOWN)
return 0;
@@ -2081,14 +2116,9 @@ int gve_set_hsplit_config(struct gve_priv *priv, u8 tcp_data_split)
if (enable_hdr_split == priv->header_split_enabled)
return 0;
- gve_get_curr_alloc_cfgs(priv, &tx_alloc_cfg, &rx_alloc_cfg);
-
- rx_alloc_cfg.enable_header_split = enable_hdr_split;
- rx_alloc_cfg.packet_buffer_size = gve_get_pkt_buf_size(priv, enable_hdr_split);
+ rx_alloc_cfg->enable_header_split = enable_hdr_split;
- if (netif_running(priv->dev))
- err = gve_adjust_config(priv, &tx_alloc_cfg, &rx_alloc_cfg);
- return err;
+ return 0;
}
static int gve_set_features(struct net_device *netdev,
@@ -2158,10 +2188,6 @@ static int gve_set_ts_config(struct net_device *dev,
}
kernel_config->rx_filter = HWTSTAMP_FILTER_ALL;
- gve_clock_nic_ts_read(priv);
- ptp_schedule_worker(priv->ptp->clock, 0);
- } else {
- ptp_cancel_worker_sync(priv->ptp->clock);
}
priv->ts_config.rx_filter = kernel_config->rx_filter;
@@ -2322,6 +2348,10 @@ static void gve_set_netdev_xdp_features(struct gve_priv *priv)
xdp_set_features_flag_locked(priv->dev, xdp_features);
}
+static const struct xdp_metadata_ops gve_xdp_metadata_ops = {
+ .xmo_rx_timestamp = gve_xdp_rx_timestamp,
+};
+
static int gve_init_priv(struct gve_priv *priv, bool skip_describe_device)
{
int num_ntfy;
@@ -2417,6 +2447,9 @@ setup_device:
}
gve_set_netdev_xdp_features(priv);
+ if (!gve_is_gqi(priv))
+ priv->dev->xdp_metadata_ops = &gve_xdp_metadata_ops;
+
err = gve_setup_device_resources(priv);
if (err)
goto err_free_xsk_bitmap;
diff --git a/drivers/net/ethernet/google/gve/gve_ptp.c b/drivers/net/ethernet/google/gve/gve_ptp.c
index e96247c9d68d..073677d82ee8 100644
--- a/drivers/net/ethernet/google/gve/gve_ptp.c
+++ b/drivers/net/ethernet/google/gve/gve_ptp.c
@@ -26,6 +26,19 @@ int gve_clock_nic_ts_read(struct gve_priv *priv)
return 0;
}
+static int gve_ptp_gettimex64(struct ptp_clock_info *info,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ return -EOPNOTSUPP;
+}
+
+static int gve_ptp_settime64(struct ptp_clock_info *info,
+ const struct timespec64 *ts)
+{
+ return -EOPNOTSUPP;
+}
+
static long gve_ptp_do_aux_work(struct ptp_clock_info *info)
{
const struct gve_ptp *ptp = container_of(info, struct gve_ptp, info);
@@ -47,6 +60,8 @@ out:
static const struct ptp_clock_info gve_ptp_caps = {
.owner = THIS_MODULE,
.name = "gve clock",
+ .gettimex64 = gve_ptp_gettimex64,
+ .settime64 = gve_ptp_settime64,
.do_aux_work = gve_ptp_do_aux_work,
};
@@ -118,9 +133,21 @@ int gve_init_clock(struct gve_priv *priv)
err = -ENOMEM;
goto release_ptp;
}
+ err = gve_clock_nic_ts_read(priv);
+ if (err) {
+ dev_err(&priv->pdev->dev, "failed to read NIC clock %d\n", err);
+ goto release_nic_ts_report;
+ }
+ ptp_schedule_worker(priv->ptp->clock,
+ msecs_to_jiffies(GVE_NIC_TS_SYNC_INTERVAL_MS));
return 0;
+release_nic_ts_report:
+ dma_free_coherent(&priv->pdev->dev,
+ sizeof(struct gve_nic_ts_report),
+ priv->nic_ts_report, priv->nic_ts_report_bus);
+ priv->nic_ts_report = NULL;
release_ptp:
gve_ptp_release(priv);
return err;
diff --git a/drivers/net/ethernet/google/gve/gve_rx_dqo.c b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
index 1aff3bbb8cfc..f1bd8f5d5732 100644
--- a/drivers/net/ethernet/google/gve/gve_rx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_rx_dqo.c
@@ -240,6 +240,11 @@ int gve_rx_alloc_ring_dqo(struct gve_priv *priv,
rx->rx_headroom = 0;
}
+ /* struct gve_xdp_buff is overlaid on struct xdp_buff_xsk and utilizes
+ * the 24 byte field cb to store gve specific data.
+ */
+ XSK_CHECK_PRIV_TYPE(struct gve_xdp_buff);
+
rx->dqo.num_buf_states = cfg->raw_addressing ? buffer_queue_slots :
gve_get_rx_pages_per_qpl_dqo(cfg->ring_size);
rx->dqo.buf_states = kvcalloc_node(rx->dqo.num_buf_states,
@@ -456,20 +461,38 @@ static void gve_rx_skb_hash(struct sk_buff *skb,
* Note that this means if the time delta between packet reception and the last
* clock read is greater than ~2 seconds, this will provide invalid results.
*/
+static ktime_t gve_rx_get_hwtstamp(struct gve_priv *gve, u32 hwts)
+{
+ u64 last_read = READ_ONCE(gve->last_sync_nic_counter);
+ u32 low = (u32)last_read;
+ s32 diff = hwts - low;
+
+ return ns_to_ktime(last_read + diff);
+}
+
static void gve_rx_skb_hwtstamp(struct gve_rx_ring *rx,
const struct gve_rx_compl_desc_dqo *desc)
{
- u64 last_read = READ_ONCE(rx->gve->last_sync_nic_counter);
struct sk_buff *skb = rx->ctx.skb_head;
- u32 ts, low;
- s32 diff;
-
- if (desc->ts_sub_nsecs_low & GVE_DQO_RX_HWTSTAMP_VALID) {
- ts = le32_to_cpu(desc->ts);
- low = (u32)last_read;
- diff = ts - low;
- skb_hwtstamps(skb)->hwtstamp = ns_to_ktime(last_read + diff);
- }
+
+ if (desc->ts_sub_nsecs_low & GVE_DQO_RX_HWTSTAMP_VALID)
+ skb_hwtstamps(skb)->hwtstamp =
+ gve_rx_get_hwtstamp(rx->gve, le32_to_cpu(desc->ts));
+}
+
+int gve_xdp_rx_timestamp(const struct xdp_md *_ctx, u64 *timestamp)
+{
+ const struct gve_xdp_buff *ctx = (void *)_ctx;
+
+ if (!ctx->gve->nic_ts_report)
+ return -ENODATA;
+
+ if (!(ctx->compl_desc->ts_sub_nsecs_low & GVE_DQO_RX_HWTSTAMP_VALID))
+ return -ENODATA;
+
+ *timestamp = gve_rx_get_hwtstamp(ctx->gve,
+ le32_to_cpu(ctx->compl_desc->ts));
+ return 0;
}
static void gve_rx_free_skb(struct napi_struct *napi, struct gve_rx_ring *rx)
@@ -683,16 +706,23 @@ err:
}
static int gve_rx_xsk_dqo(struct napi_struct *napi, struct gve_rx_ring *rx,
- struct gve_rx_buf_state_dqo *buf_state, int buf_len,
+ const struct gve_rx_compl_desc_dqo *compl_desc,
+ struct gve_rx_buf_state_dqo *buf_state,
struct bpf_prog *xprog)
{
struct xdp_buff *xdp = buf_state->xsk_buff;
+ int buf_len = compl_desc->packet_len;
struct gve_priv *priv = rx->gve;
+ struct gve_xdp_buff *gve_xdp;
int xdp_act;
xdp->data_end = xdp->data + buf_len;
xsk_buff_dma_sync_for_cpu(xdp);
+ gve_xdp = (void *)xdp;
+ gve_xdp->gve = priv;
+ gve_xdp->compl_desc = compl_desc;
+
if (xprog) {
xdp_act = bpf_prog_run_xdp(xprog, xdp);
buf_len = xdp->data_end - xdp->data;
@@ -782,7 +812,7 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx,
xprog = READ_ONCE(priv->xdp_prog);
if (buf_state->xsk_buff)
- return gve_rx_xsk_dqo(napi, rx, buf_state, buf_len, xprog);
+ return gve_rx_xsk_dqo(napi, rx, compl_desc, buf_state, xprog);
/* Page might have not been used for awhile and was likely last written
* by a different thread.
@@ -840,23 +870,26 @@ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx,
}
if (xprog) {
- struct xdp_buff xdp;
+ struct gve_xdp_buff gve_xdp;
void *old_data;
int xdp_act;
- xdp_init_buff(&xdp, buf_state->page_info.buf_size,
+ xdp_init_buff(&gve_xdp.xdp, buf_state->page_info.buf_size,
&rx->xdp_rxq);
- xdp_prepare_buff(&xdp,
+ xdp_prepare_buff(&gve_xdp.xdp,
buf_state->page_info.page_address +
buf_state->page_info.page_offset,
buf_state->page_info.pad,
buf_len, false);
- old_data = xdp.data;
- xdp_act = bpf_prog_run_xdp(xprog, &xdp);
- buf_state->page_info.pad += xdp.data - old_data;
- buf_len = xdp.data_end - xdp.data;
+ gve_xdp.gve = priv;
+ gve_xdp.compl_desc = compl_desc;
+
+ old_data = gve_xdp.xdp.data;
+ xdp_act = bpf_prog_run_xdp(xprog, &gve_xdp.xdp);
+ buf_state->page_info.pad += gve_xdp.xdp.data - old_data;
+ buf_len = gve_xdp.xdp.data_end - gve_xdp.xdp.data;
if (xdp_act != XDP_PASS) {
- gve_xdp_done_dqo(priv, rx, &xdp, xprog, xdp_act,
+ gve_xdp_done_dqo(priv, rx, &gve_xdp.xdp, xprog, xdp_act,
buf_state);
return 0;
}
diff --git a/drivers/net/ethernet/google/gve/gve_tx.c b/drivers/net/ethernet/google/gve/gve_tx.c
index c6ff0968929d..97efc8d27e6f 100644
--- a/drivers/net/ethernet/google/gve/gve_tx.c
+++ b/drivers/net/ethernet/google/gve/gve_tx.c
@@ -730,7 +730,9 @@ unmap_drop:
gve_tx_unmap_buf(tx->dev, &tx->info[idx & tx->mask]);
}
drop:
+ u64_stats_update_begin(&tx->statss);
tx->dropped_pkt++;
+ u64_stats_update_end(&tx->statss);
return 0;
}
diff --git a/drivers/net/ethernet/google/gve/gve_tx_dqo.c b/drivers/net/ethernet/google/gve/gve_tx_dqo.c
index 6f1d515673d2..40b89b3e5a31 100644
--- a/drivers/net/ethernet/google/gve/gve_tx_dqo.c
+++ b/drivers/net/ethernet/google/gve/gve_tx_dqo.c
@@ -1002,7 +1002,9 @@ static int gve_try_tx_skb(struct gve_priv *priv, struct gve_tx_ring *tx,
return 0;
drop:
+ u64_stats_update_begin(&tx->statss);
tx->dropped_pkt++;
+ u64_stats_update_end(&tx->statss);
dev_kfree_skb_any(skb);
return 0;
}
@@ -1324,7 +1326,11 @@ static void remove_miss_completions(struct gve_priv *priv,
/* This indicates the packet was dropped. */
dev_kfree_skb_any(pending_packet->skb);
pending_packet->skb = NULL;
+
+ u64_stats_update_begin(&tx->statss);
tx->dropped_pkt++;
+ u64_stats_update_end(&tx->statss);
+
net_err_ratelimited("%s: No reinjection completion was received for: %d.\n",
priv->dev->name,
(int)(pending_packet - tx->dqo.pending_packets));
diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig
index 65302c41bfb1..18eca7d12c20 100644
--- a/drivers/net/ethernet/hisilicon/Kconfig
+++ b/drivers/net/ethernet/hisilicon/Kconfig
@@ -148,8 +148,10 @@ config HIBMCGE
tristate "Hisilicon BMC Gigabit Ethernet Device Support"
depends on PCI && PCI_MSI
select PHYLIB
+ select FIXED_PHY
select MOTORCOMM_PHY
select REALTEK_PHY
+ select PAGE_POOL
help
If you wish to compile a kernel for a BMC with HIBMC-xx_gmac
then you should answer Y to this. This makes this driver suitable for use
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/Makefile b/drivers/net/ethernet/hisilicon/hibmcge/Makefile
index 1a9da564b306..d6610ba16855 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/Makefile
+++ b/drivers/net/ethernet/hisilicon/hibmcge/Makefile
@@ -3,6 +3,7 @@
# Makefile for the HISILICON BMC GE network device drivers.
#
+ccflags-y += -I$(src)
obj-$(CONFIG_HIBMCGE) += hibmcge.o
hibmcge-objs = hbg_main.o hbg_hw.o hbg_mdio.o hbg_irq.o hbg_txrx.o hbg_ethtool.o \
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
index ea09a09c451b..8e134da3e217 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
@@ -7,6 +7,7 @@
#include <linux/ethtool.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
+#include <net/page_pool/helpers.h>
#include "hbg_reg.h"
#define HBG_STATUS_DISABLE 0x0
@@ -17,6 +18,7 @@
#define HBG_PCU_CACHE_LINE_SIZE 32
#define HBG_TX_TIMEOUT_BUF_LEN 1024
#define HBG_RX_DESCR 0x01
+#define HBG_NO_PHY 0xFF
#define HBG_PACKET_HEAD_SIZE ((HBG_RX_SKIP1 + HBG_RX_SKIP2 + \
HBG_RX_DESCR) * HBG_PCU_CACHE_LINE_SIZE)
@@ -54,6 +56,12 @@ struct hbg_buffer {
dma_addr_t skb_dma;
u32 skb_len;
+ struct page *page;
+ void *page_addr;
+ dma_addr_t page_dma;
+ u32 page_size;
+ u32 page_offset;
+
enum hbg_dir dir;
struct hbg_ring *ring;
struct hbg_priv *priv;
@@ -77,6 +85,7 @@ struct hbg_ring {
struct hbg_priv *priv;
struct napi_struct napi;
char *tout_log_buf; /* tx timeout log buffer */
+ struct page_pool *page_pool; /* only for rx */
};
enum hbg_hw_event_type {
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c
index 83cf75bf7a17..7234618e8e81 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c
@@ -136,12 +136,11 @@ static pci_ers_result_t hbg_pci_err_detected(struct pci_dev *pdev,
{
struct net_device *netdev = pci_get_drvdata(pdev);
- netif_device_detach(netdev);
-
- if (state == pci_channel_io_perm_failure)
+ if (state == pci_channel_io_perm_failure) {
+ netif_device_detach(netdev);
return PCI_ERS_RESULT_DISCONNECT;
+ }
- pci_disable_device(pdev);
return PCI_ERS_RESULT_NEED_RESET;
}
@@ -150,6 +149,9 @@ static pci_ers_result_t hbg_pci_err_slot_reset(struct pci_dev *pdev)
struct net_device *netdev = pci_get_drvdata(pdev);
struct hbg_priv *priv = netdev_priv(netdev);
+ netif_device_detach(netdev);
+ pci_disable_device(pdev);
+
if (pci_enable_device(pdev)) {
dev_err(&pdev->dev,
"failed to re-enable PCI device after reset\n");
@@ -158,7 +160,6 @@ static pci_ers_result_t hbg_pci_err_slot_reset(struct pci_dev *pdev)
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
hbg_err_reset(priv);
return PCI_ERS_RESULT_RECOVERED;
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
index d0aa0661ecd4..d6e8ce8e351a 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_hw.c
@@ -244,6 +244,9 @@ void hbg_hw_adjust_link(struct hbg_priv *priv, u32 speed, u32 duplex)
hbg_hw_mac_enable(priv, HBG_STATUS_ENABLE);
+ if (priv->mac.phy_addr == HBG_NO_PHY)
+ return;
+
/* wait MAC link up */
ret = readl_poll_timeout(priv->io_base + HBG_REG_AN_NEG_STATE_ADDR,
link_status,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
index 8af0bc4cca21..ae4cb35186d8 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
@@ -32,6 +32,7 @@ static void hbg_irq_handle_rx_buf_val(struct hbg_priv *priv,
const struct hbg_irq_info *irq_info)
{
priv->stats.rx_fifo_less_empty_thrsld_cnt++;
+ hbg_hw_irq_enable(priv, irq_info->mask, true);
}
#define HBG_IRQ_I(name, handle) \
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
index 0b92a2e5e986..068da2fd1fea 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
@@ -472,6 +472,22 @@ static int hbg_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
}
+static void hbg_shutdown(struct pci_dev *pdev)
+{
+ struct net_device *netdev = pci_get_drvdata(pdev);
+
+ rtnl_lock();
+ if (netif_running(netdev))
+ dev_close(netdev);
+ rtnl_unlock();
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+
+ if (system_state == SYSTEM_POWER_OFF)
+ pci_set_power_state(pdev, PCI_D3hot);
+}
+
static const struct pci_device_id hbg_pci_tbl[] = {
{PCI_VDEVICE(HUAWEI, 0x3730), 0},
{ }
@@ -482,6 +498,7 @@ static struct pci_driver hbg_driver = {
.name = "hibmcge",
.id_table = hbg_pci_tbl,
.probe = hbg_probe,
+ .shutdown = hbg_shutdown,
};
static int __init hbg_module_init(void)
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
index 37791de47f6f..b6f0a2780ea8 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_mdio.c
@@ -20,7 +20,6 @@
#define HBG_MDIO_OP_INTERVAL_US (5 * 1000)
#define HBG_NP_LINK_FAIL_RETRY_TIMES 5
-#define HBG_NO_PHY 0xFF
static void hbg_mdio_set_command(struct hbg_mac *mac, u32 cmd)
{
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
index a39d1e796e4a..30b3903c8f2d 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
@@ -252,6 +252,8 @@ struct hbg_rx_desc {
#define HBG_RX_DESC_W2_PKT_LEN_M GENMASK(31, 16)
#define HBG_RX_DESC_W2_PORT_NUM_M GENMASK(15, 12)
+#define HBG_RX_DESC_W3_IP_OFFSET_M GENMASK(23, 16)
+#define HBG_RX_DESC_W3_VLAN_M GENMASK(15, 0)
#define HBG_RX_DESC_W4_IP_TCP_UDP_M GENMASK(31, 30)
#define HBG_RX_DESC_W4_IPSEC_B BIT(29)
#define HBG_RX_DESC_W4_IP_VERSION_B BIT(28)
@@ -269,6 +271,8 @@ struct hbg_rx_desc {
#define HBG_RX_DESC_W4_L3_ERR_CODE_M GENMASK(12, 9)
#define HBG_RX_DESC_W4_L2_ERR_B BIT(8)
#define HBG_RX_DESC_W4_IDX_MATCH_B BIT(7)
+#define HBG_RX_DESC_W4_PARSE_MODE_M GENMASK(6, 5)
+#define HBG_RX_DESC_W5_VALID_SIZE_M GENMASK(15, 0)
enum hbg_l3_err_code {
HBG_L3_OK = 0,
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_trace.h b/drivers/net/ethernet/hisilicon/hibmcge/hbg_trace.h
new file mode 100644
index 000000000000..b70fd960da8d
--- /dev/null
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_trace.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/* Copyright (c) 2025 Hisilicon Limited. */
+
+/* This must be outside ifdef _HBG_TRACE_H */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM hibmcge
+
+#if !defined(_HBG_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _HBG_TRACE_H_
+
+#include <linux/bitfield.h>
+#include <linux/pci.h>
+#include <linux/tracepoint.h>
+#include <linux/types.h>
+#include "hbg_reg.h"
+
+TRACE_EVENT(hbg_rx_desc,
+ TP_PROTO(struct hbg_priv *priv, u32 index,
+ struct hbg_rx_desc *rx_desc),
+ TP_ARGS(priv, index, rx_desc),
+
+ TP_STRUCT__entry(__field(u32, index)
+ __field(u8, port_num)
+ __field(u8, ip_offset)
+ __field(u8, parse_mode)
+ __field(u8, l4_error_code)
+ __field(u8, l3_error_code)
+ __field(u8, l2_error_code)
+ __field(u16, packet_len)
+ __field(u16, valid_size)
+ __field(u16, vlan)
+ __string(pciname, pci_name(priv->pdev))
+ __string(devname, priv->netdev->name)
+ ),
+
+ TP_fast_assign(__entry->index = index,
+ __entry->packet_len =
+ FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M,
+ rx_desc->word2);
+ __entry->port_num =
+ FIELD_GET(HBG_RX_DESC_W2_PORT_NUM_M,
+ rx_desc->word2);
+ __entry->ip_offset =
+ FIELD_GET(HBG_RX_DESC_W3_IP_OFFSET_M,
+ rx_desc->word3);
+ __entry->vlan =
+ FIELD_GET(HBG_RX_DESC_W3_VLAN_M,
+ rx_desc->word3);
+ __entry->parse_mode =
+ FIELD_GET(HBG_RX_DESC_W4_PARSE_MODE_M,
+ rx_desc->word4);
+ __entry->l4_error_code =
+ FIELD_GET(HBG_RX_DESC_W4_L4_ERR_CODE_M,
+ rx_desc->word4);
+ __entry->l3_error_code =
+ FIELD_GET(HBG_RX_DESC_W4_L3_ERR_CODE_M,
+ rx_desc->word4);
+ __entry->l2_error_code =
+ FIELD_GET(HBG_RX_DESC_W4_L2_ERR_B,
+ rx_desc->word4);
+ __entry->valid_size =
+ FIELD_GET(HBG_RX_DESC_W5_VALID_SIZE_M,
+ rx_desc->word5);
+ __assign_str(pciname);
+ __assign_str(devname);
+ ),
+
+ TP_printk("%s %s index:%u, port num:%u, len:%u, valid size:%u, ip_offset:%u, vlan:0x%04x, parse mode:%u, l4_err:0x%x, l3_err:0x%x, l2_err:0x%x",
+ __get_str(pciname), __get_str(devname), __entry->index,
+ __entry->port_num, __entry->packet_len,
+ __entry->valid_size, __entry->ip_offset, __entry->vlan,
+ __entry->parse_mode, __entry->l4_error_code,
+ __entry->l3_error_code, __entry->l2_error_code
+ )
+);
+
+#endif /* _HBG_TRACE_H_ */
+
+/* This must be outside ifdef _HBG_TRACE_H */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE hbg_trace
+#include <trace/define_trace.h>
diff --git a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
index 8d814c8f19ea..a4ea92c31c2f 100644
--- a/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
+++ b/drivers/net/ethernet/hisilicon/hibmcge/hbg_txrx.c
@@ -7,6 +7,9 @@
#include "hbg_reg.h"
#include "hbg_txrx.h"
+#define CREATE_TRACE_POINTS
+#include "hbg_trace.h"
+
#define netdev_get_tx_ring(netdev) \
(&(((struct hbg_priv *)netdev_priv(netdev))->tx_ring))
@@ -28,6 +31,11 @@
typeof(ring) _ring = (ring); \
_ring->p = hbg_queue_next_prt(_ring->p, _ring); })
+#define hbg_get_page_order(ring) ({ \
+ typeof(ring) _ring = (ring); \
+ get_order(hbg_spec_max_frame_len(_ring->priv, _ring->dir)); })
+#define hbg_get_page_size(ring) (PAGE_SIZE << hbg_get_page_order((ring)))
+
#define HBG_TX_STOP_THRS 2
#define HBG_TX_START_THRS (2 * HBG_TX_STOP_THRS)
@@ -62,6 +70,43 @@ static void hbg_dma_unmap(struct hbg_buffer *buffer)
buffer->skb_dma = 0;
}
+static void hbg_buffer_free_page(struct hbg_buffer *buffer)
+{
+ struct hbg_ring *ring = buffer->ring;
+
+ if (unlikely(!buffer->page))
+ return;
+
+ page_pool_put_full_page(ring->page_pool, buffer->page, false);
+
+ buffer->page = NULL;
+ buffer->page_dma = 0;
+ buffer->page_addr = NULL;
+ buffer->page_size = 0;
+ buffer->page_offset = 0;
+}
+
+static int hbg_buffer_alloc_page(struct hbg_buffer *buffer)
+{
+ struct hbg_ring *ring = buffer->ring;
+ u32 len = hbg_get_page_size(ring);
+ u32 offset;
+
+ if (unlikely(!ring->page_pool))
+ return 0;
+
+ buffer->page = page_pool_dev_alloc_frag(ring->page_pool, &offset, len);
+ if (unlikely(!buffer->page))
+ return -ENOMEM;
+
+ buffer->page_dma = page_pool_get_dma_addr(buffer->page) + offset;
+ buffer->page_addr = page_address(buffer->page) + offset;
+ buffer->page_size = len;
+ buffer->page_offset = offset;
+
+ return 0;
+}
+
static void hbg_init_tx_desc(struct hbg_buffer *buffer,
struct hbg_tx_desc *tx_desc)
{
@@ -135,24 +180,14 @@ static void hbg_buffer_free_skb(struct hbg_buffer *buffer)
buffer->skb = NULL;
}
-static int hbg_buffer_alloc_skb(struct hbg_buffer *buffer)
-{
- u32 len = hbg_spec_max_frame_len(buffer->priv, buffer->dir);
- struct hbg_priv *priv = buffer->priv;
-
- buffer->skb = netdev_alloc_skb(priv->netdev, len);
- if (unlikely(!buffer->skb))
- return -ENOMEM;
-
- buffer->skb_len = len;
- memset(buffer->skb->data, 0, HBG_PACKET_HEAD_SIZE);
- return 0;
-}
-
static void hbg_buffer_free(struct hbg_buffer *buffer)
{
- hbg_dma_unmap(buffer);
- hbg_buffer_free_skb(buffer);
+ if (buffer->skb) {
+ hbg_dma_unmap(buffer);
+ return hbg_buffer_free_skb(buffer);
+ }
+
+ hbg_buffer_free_page(buffer);
}
static int hbg_napi_tx_recycle(struct napi_struct *napi, int budget)
@@ -374,25 +409,44 @@ static int hbg_rx_fill_one_buffer(struct hbg_priv *priv)
struct hbg_buffer *buffer;
int ret;
- if (hbg_queue_is_full(ring->ntc, ring->ntu, ring))
+ if (hbg_queue_is_full(ring->ntc, ring->ntu, ring) ||
+ hbg_fifo_is_full(priv, ring->dir))
return 0;
buffer = &ring->queue[ring->ntu];
- ret = hbg_buffer_alloc_skb(buffer);
+ ret = hbg_buffer_alloc_page(buffer);
if (unlikely(ret))
return ret;
- ret = hbg_dma_map(buffer);
- if (unlikely(ret)) {
- hbg_buffer_free_skb(buffer);
- return ret;
- }
+ memset(buffer->page_addr, 0, HBG_PACKET_HEAD_SIZE);
+ dma_sync_single_for_device(&priv->pdev->dev, buffer->page_dma,
+ HBG_PACKET_HEAD_SIZE, DMA_TO_DEVICE);
- hbg_hw_fill_buffer(priv, buffer->skb_dma);
+ hbg_hw_fill_buffer(priv, buffer->page_dma);
hbg_queue_move_next(ntu, ring);
return 0;
}
+static int hbg_rx_fill_buffers(struct hbg_priv *priv)
+{
+ u32 remained = hbg_hw_get_fifo_used_num(priv, HBG_DIR_RX);
+ u32 max_count = priv->dev_specs.rx_fifo_num;
+ u32 refill_count;
+ int ret;
+
+ if (unlikely(remained >= max_count))
+ return 0;
+
+ refill_count = max_count - remained;
+ while (refill_count--) {
+ ret = hbg_rx_fill_one_buffer(priv);
+ if (unlikely(ret))
+ break;
+ }
+
+ return ret;
+}
+
static bool hbg_sync_data_from_hw(struct hbg_priv *priv,
struct hbg_buffer *buffer)
{
@@ -401,13 +455,29 @@ static bool hbg_sync_data_from_hw(struct hbg_priv *priv,
/* make sure HW write desc complete */
dma_rmb();
- dma_sync_single_for_cpu(&priv->pdev->dev, buffer->skb_dma,
- buffer->skb_len, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(&priv->pdev->dev, buffer->page_dma,
+ buffer->page_size, DMA_FROM_DEVICE);
- rx_desc = (struct hbg_rx_desc *)buffer->skb->data;
+ rx_desc = (struct hbg_rx_desc *)buffer->page_addr;
return FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2) != 0;
}
+static int hbg_build_skb(struct hbg_priv *priv,
+ struct hbg_buffer *buffer, u32 pkt_len)
+{
+ net_prefetch(buffer->page_addr);
+
+ buffer->skb = napi_build_skb(buffer->page_addr, buffer->page_size);
+ if (unlikely(!buffer->skb))
+ return -ENOMEM;
+ skb_mark_for_recycle(buffer->skb);
+
+ /* page will be freed together with the skb */
+ buffer->page = NULL;
+
+ return 0;
+}
+
static int hbg_napi_rx_poll(struct napi_struct *napi, int budget)
{
struct hbg_ring *ring = container_of(napi, struct hbg_ring, napi);
@@ -417,33 +487,39 @@ static int hbg_napi_rx_poll(struct napi_struct *napi, int budget)
u32 packet_done = 0;
u32 pkt_len;
+ hbg_rx_fill_buffers(priv);
while (packet_done < budget) {
if (unlikely(hbg_queue_is_empty(ring->ntc, ring->ntu, ring)))
break;
buffer = &ring->queue[ring->ntc];
- if (unlikely(!buffer->skb))
+ if (unlikely(!buffer->page))
goto next_buffer;
if (unlikely(!hbg_sync_data_from_hw(priv, buffer)))
break;
- rx_desc = (struct hbg_rx_desc *)buffer->skb->data;
+ rx_desc = (struct hbg_rx_desc *)buffer->page_addr;
pkt_len = FIELD_GET(HBG_RX_DESC_W2_PKT_LEN_M, rx_desc->word2);
+ trace_hbg_rx_desc(priv, ring->ntc, rx_desc);
+
+ if (unlikely(hbg_build_skb(priv, buffer, pkt_len))) {
+ hbg_buffer_free_page(buffer);
+ goto next_buffer;
+ }
if (unlikely(!hbg_rx_pkt_check(priv, rx_desc, buffer->skb))) {
- hbg_buffer_free(buffer);
+ hbg_buffer_free_skb(buffer);
goto next_buffer;
}
- hbg_dma_unmap(buffer);
skb_reserve(buffer->skb, HBG_PACKET_HEAD_SIZE + NET_IP_ALIGN);
skb_put(buffer->skb, pkt_len);
buffer->skb->protocol = eth_type_trans(buffer->skb,
priv->netdev);
-
dev_sw_netstats_rx_add(priv->netdev, pkt_len);
napi_gro_receive(napi, buffer->skb);
buffer->skb = NULL;
+ buffer->page = NULL;
next_buffer:
hbg_rx_fill_one_buffer(priv);
@@ -458,6 +534,42 @@ next_buffer:
return packet_done;
}
+static void hbg_ring_page_pool_destory(struct hbg_ring *ring)
+{
+ if (!ring->page_pool)
+ return;
+
+ page_pool_destroy(ring->page_pool);
+ ring->page_pool = NULL;
+}
+
+static int hbg_ring_page_pool_init(struct hbg_priv *priv, struct hbg_ring *ring)
+{
+ u32 buf_size = hbg_spec_max_frame_len(priv, ring->dir);
+ struct page_pool_params pp_params = {
+ .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
+ .order = hbg_get_page_order(ring),
+ .pool_size = ring->len * buf_size / hbg_get_page_size(ring),
+ .nid = dev_to_node(&priv->pdev->dev),
+ .dev = &priv->pdev->dev,
+ .napi = &ring->napi,
+ .dma_dir = DMA_FROM_DEVICE,
+ .offset = 0,
+ .max_len = hbg_get_page_size(ring),
+ };
+ int ret = 0;
+
+ ring->page_pool = page_pool_create(&pp_params);
+ if (IS_ERR(ring->page_pool)) {
+ ret = PTR_ERR(ring->page_pool);
+ dev_err(&priv->pdev->dev,
+ "failed to create page pool, ret = %d\n", ret);
+ ring->page_pool = NULL;
+ }
+
+ return ret;
+}
+
static void hbg_ring_uninit(struct hbg_ring *ring)
{
struct hbg_buffer *buffer;
@@ -476,6 +588,7 @@ static void hbg_ring_uninit(struct hbg_ring *ring)
buffer->priv = NULL;
}
+ hbg_ring_page_pool_destory(ring);
dma_free_coherent(&ring->priv->pdev->dev,
ring->len * sizeof(*ring->queue),
ring->queue, ring->queue_dma);
@@ -491,8 +604,19 @@ static int hbg_ring_init(struct hbg_priv *priv, struct hbg_ring *ring,
{
struct hbg_buffer *buffer;
u32 i, len;
+ int ret;
len = hbg_get_spec_fifo_max_num(priv, dir) + 1;
+ /* To improve receiving performance under high-stress scenarios,
+ * in the `hbg_napi_rx_poll()`, we first use the other half of
+ * the buffer to receive packets from the hardware via the
+ * `hbg_rx_fill_buffers()`, and then process the packets in the
+ * original half of the buffer to avoid packet loss caused by
+ * hardware overflow as much as possible.
+ */
+ if (dir == HBG_DIR_RX)
+ len += hbg_get_spec_fifo_max_num(priv, dir);
+
ring->queue = dma_alloc_coherent(&priv->pdev->dev,
len * sizeof(*ring->queue),
&ring->queue_dma, GFP_KERNEL);
@@ -514,11 +638,23 @@ static int hbg_ring_init(struct hbg_priv *priv, struct hbg_ring *ring,
ring->ntu = 0;
ring->len = len;
- if (dir == HBG_DIR_TX)
+ if (dir == HBG_DIR_TX) {
netif_napi_add_tx(priv->netdev, &ring->napi, napi_poll);
- else
+ } else {
netif_napi_add(priv->netdev, &ring->napi, napi_poll);
+ ret = hbg_ring_page_pool_init(priv, ring);
+ if (ret) {
+ netif_napi_del(&ring->napi);
+ dma_free_coherent(&ring->priv->pdev->dev,
+ ring->len * sizeof(*ring->queue),
+ ring->queue, ring->queue_dma);
+ ring->queue = NULL;
+ ring->len = 0;
+ return ret;
+ }
+ }
+
napi_enable(&ring->napi);
return 0;
}
@@ -541,21 +677,16 @@ static int hbg_tx_ring_init(struct hbg_priv *priv)
static int hbg_rx_ring_init(struct hbg_priv *priv)
{
int ret;
- u32 i;
ret = hbg_ring_init(priv, &priv->rx_ring, hbg_napi_rx_poll, HBG_DIR_RX);
if (ret)
return ret;
- for (i = 0; i < priv->rx_ring.len - 1; i++) {
- ret = hbg_rx_fill_one_buffer(priv);
- if (ret) {
- hbg_ring_uninit(&priv->rx_ring);
- return ret;
- }
- }
+ ret = hbg_rx_fill_buffers(priv);
+ if (ret)
+ hbg_ring_uninit(&priv->rx_ring);
- return 0;
+ return ret;
}
int hbg_txrx_init(struct hbg_priv *priv)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
index 3b548f71fa8a..d7c3df1958f3 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h
@@ -804,6 +804,11 @@ struct hnae3_ae_ops {
int (*dbg_get_read_func)(struct hnae3_handle *handle,
enum hnae3_dbg_cmd cmd,
read_func *func);
+ int (*hwtstamp_get)(struct hnae3_handle *handle,
+ struct kernel_hwtstamp_config *config);
+ int (*hwtstamp_set)(struct hnae3_handle *handle,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
};
struct hnae3_dcb_ops {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index bfa5568baa92..7a0654e2d3dd 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -2419,6 +2419,35 @@ static int hns3_nic_do_ioctl(struct net_device *netdev,
return h->ae_algo->ops->do_ioctl(h, ifr, cmd);
}
+static int hns3_nic_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct hnae3_handle *h = hns3_get_handle(netdev);
+
+ if (!netif_running(netdev))
+ return -EINVAL;
+
+ if (!h->ae_algo->ops->hwtstamp_get)
+ return -EOPNOTSUPP;
+
+ return h->ae_algo->ops->hwtstamp_get(h, config);
+}
+
+static int hns3_nic_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct hnae3_handle *h = hns3_get_handle(netdev);
+
+ if (!netif_running(netdev))
+ return -EINVAL;
+
+ if (!h->ae_algo->ops->hwtstamp_set)
+ return -EOPNOTSUPP;
+
+ return h->ae_algo->ops->hwtstamp_set(h, config, extack);
+}
+
static int hns3_nic_set_features(struct net_device *netdev,
netdev_features_t features)
{
@@ -3048,6 +3077,8 @@ static const struct net_device_ops hns3_nic_netdev_ops = {
.ndo_set_vf_rate = hns3_nic_set_vf_rate,
.ndo_set_vf_mac = hns3_nic_set_vf_mac,
.ndo_select_queue = hns3_nic_select_queue,
+ .ndo_hwtstamp_get = hns3_nic_hwtstamp_get,
+ .ndo_hwtstamp_set = hns3_nic_hwtstamp_set,
};
bool hns3_is_phys_func(struct pci_dev *pdev)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 9d34d28ff168..cf8abbe01840 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -9429,8 +9429,7 @@ static int hclge_mii_ioctl(struct hclge_dev *hdev, struct ifreq *ifr, int cmd)
/* this command reads phy id and register at the same time */
fallthrough;
case SIOCGMIIREG:
- data->val_out = hclge_read_phy_reg(hdev, data->reg_num);
- return 0;
+ return hclge_read_phy_reg(hdev, data->reg_num, &data->val_out);
case SIOCSMIIREG:
return hclge_write_phy_reg(hdev, data->reg_num, data->val_in);
@@ -9445,15 +9444,8 @@ static int hclge_do_ioctl(struct hnae3_handle *handle, struct ifreq *ifr,
struct hclge_vport *vport = hclge_get_vport(handle);
struct hclge_dev *hdev = vport->back;
- switch (cmd) {
- case SIOCGHWTSTAMP:
- return hclge_ptp_get_cfg(hdev, ifr);
- case SIOCSHWTSTAMP:
- return hclge_ptp_set_cfg(hdev, ifr);
- default:
- if (!hdev->hw.mac.phydev)
- return hclge_mii_ioctl(hdev, ifr, cmd);
- }
+ if (!hdev->hw.mac.phydev)
+ return hclge_mii_ioctl(hdev, ifr, cmd);
return phy_mii_ioctl(hdev->hw.mac.phydev, ifr, cmd);
}
@@ -12901,6 +12893,8 @@ static const struct hnae3_ae_ops hclge_ops = {
.get_dscp_prio = hclge_get_dscp_prio,
.get_wol = hclge_get_wol,
.set_wol = hclge_set_wol,
+ .hwtstamp_get = hclge_ptp_get_cfg,
+ .hwtstamp_set = hclge_ptp_set_cfg,
};
static struct hnae3_ae_algo ae_algo = {
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
index 96553109f44c..cf881108fa57 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c
@@ -274,7 +274,7 @@ void hclge_mac_stop_phy(struct hclge_dev *hdev)
phy_stop(phydev);
}
-u16 hclge_read_phy_reg(struct hclge_dev *hdev, u16 reg_addr)
+int hclge_read_phy_reg(struct hclge_dev *hdev, u16 reg_addr, u16 *val)
{
struct hclge_phy_reg_cmd *req;
struct hclge_desc desc;
@@ -286,11 +286,14 @@ u16 hclge_read_phy_reg(struct hclge_dev *hdev, u16 reg_addr)
req->reg_addr = cpu_to_le16(reg_addr);
ret = hclge_cmd_send(&hdev->hw, &desc, 1);
- if (ret)
+ if (ret) {
dev_err(&hdev->pdev->dev,
"failed to read phy reg, ret = %d.\n", ret);
+ return ret;
+ }
- return le16_to_cpu(req->reg_val);
+ *val = le16_to_cpu(req->reg_val);
+ return 0;
}
int hclge_write_phy_reg(struct hclge_dev *hdev, u16 reg_addr, u16 val)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h
index 4200d0b6d931..21d434c82475 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h
@@ -13,7 +13,7 @@ int hclge_mac_connect_phy(struct hnae3_handle *handle);
void hclge_mac_disconnect_phy(struct hnae3_handle *handle);
void hclge_mac_start_phy(struct hclge_dev *hdev);
void hclge_mac_stop_phy(struct hclge_dev *hdev);
-u16 hclge_read_phy_reg(struct hclge_dev *hdev, u16 reg_addr);
+int hclge_read_phy_reg(struct hclge_dev *hdev, u16 reg_addr, u16 *val);
int hclge_write_phy_reg(struct hclge_dev *hdev, u16 reg_addr, u16 val);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
index 4bd52eab3914..0081c5281455 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.c
@@ -204,13 +204,17 @@ static int hclge_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
return 0;
}
-int hclge_ptp_get_cfg(struct hclge_dev *hdev, struct ifreq *ifr)
+int hclge_ptp_get_cfg(struct hnae3_handle *handle,
+ struct kernel_hwtstamp_config *config)
{
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
+
if (!test_bit(HCLGE_STATE_PTP_EN, &hdev->state))
return -EOPNOTSUPP;
- return copy_to_user(ifr->ifr_data, &hdev->ptp->ts_cfg,
- sizeof(struct hwtstamp_config)) ? -EFAULT : 0;
+ *config = hdev->ptp->ts_cfg;
+ return 0;
}
static int hclge_ptp_int_en(struct hclge_dev *hdev, bool en)
@@ -269,7 +273,7 @@ static int hclge_ptp_cfg(struct hclge_dev *hdev, u32 cfg)
return ret;
}
-static int hclge_ptp_set_tx_mode(struct hwtstamp_config *cfg,
+static int hclge_ptp_set_tx_mode(struct kernel_hwtstamp_config *cfg,
unsigned long *flags, u32 *ptp_cfg)
{
switch (cfg->tx_type) {
@@ -287,7 +291,7 @@ static int hclge_ptp_set_tx_mode(struct hwtstamp_config *cfg,
return 0;
}
-static int hclge_ptp_set_rx_mode(struct hwtstamp_config *cfg,
+static int hclge_ptp_set_rx_mode(struct kernel_hwtstamp_config *cfg,
unsigned long *flags, u32 *ptp_cfg)
{
int rx_filter = cfg->rx_filter;
@@ -332,7 +336,7 @@ static int hclge_ptp_set_rx_mode(struct hwtstamp_config *cfg,
}
static int hclge_ptp_set_ts_mode(struct hclge_dev *hdev,
- struct hwtstamp_config *cfg)
+ struct kernel_hwtstamp_config *cfg)
{
unsigned long flags = hdev->ptp->flags;
u32 ptp_cfg = 0;
@@ -359,9 +363,12 @@ static int hclge_ptp_set_ts_mode(struct hclge_dev *hdev,
return 0;
}
-int hclge_ptp_set_cfg(struct hclge_dev *hdev, struct ifreq *ifr)
+int hclge_ptp_set_cfg(struct hnae3_handle *handle,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config cfg;
+ struct hclge_vport *vport = hclge_get_vport(handle);
+ struct hclge_dev *hdev = vport->back;
int ret;
if (!test_bit(HCLGE_STATE_PTP_EN, &hdev->state)) {
@@ -369,16 +376,13 @@ int hclge_ptp_set_cfg(struct hclge_dev *hdev, struct ifreq *ifr)
return -EOPNOTSUPP;
}
- if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
- return -EFAULT;
-
- ret = hclge_ptp_set_ts_mode(hdev, &cfg);
+ ret = hclge_ptp_set_ts_mode(hdev, config);
if (ret)
return ret;
- hdev->ptp->ts_cfg = cfg;
+ hdev->ptp->ts_cfg = *config;
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+ return 0;
}
int hclge_ptp_get_ts_info(struct hnae3_handle *handle,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.h
index 61faddcc3dd0..0162fa5ac146 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ptp.h
@@ -62,7 +62,7 @@ struct hclge_ptp {
unsigned long flags;
void __iomem *io_base;
struct ptp_clock_info info;
- struct hwtstamp_config ts_cfg;
+ struct kernel_hwtstamp_config ts_cfg;
spinlock_t lock; /* protects ptp registers */
u32 ptp_cfg;
u32 last_tx_seqid;
@@ -133,8 +133,11 @@ bool hclge_ptp_set_tx_info(struct hnae3_handle *handle, struct sk_buff *skb);
void hclge_ptp_clean_tx_hwts(struct hclge_dev *hdev);
void hclge_ptp_get_rx_hwts(struct hnae3_handle *handle, struct sk_buff *skb,
u32 nsec, u32 sec);
-int hclge_ptp_get_cfg(struct hclge_dev *hdev, struct ifreq *ifr);
-int hclge_ptp_set_cfg(struct hclge_dev *hdev, struct ifreq *ifr);
+int hclge_ptp_get_cfg(struct hnae3_handle *handle,
+ struct kernel_hwtstamp_config *config);
+int hclge_ptp_set_cfg(struct hnae3_handle *handle,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
int hclge_ptp_init(struct hclge_dev *hdev);
void hclge_ptp_uninit(struct hclge_dev *hdev);
int hclge_ptp_get_ts_info(struct hnae3_handle *handle,
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
index 0fa3c7900225..bbf22811a029 100644
--- a/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
+++ b/drivers/net/ethernet/huawei/hinic3/hinic3_netdev_ops.c
@@ -304,7 +304,7 @@ static int hinic3_open_channel(struct net_device *netdev)
err = hinic3_configure(netdev);
if (err) {
- netdev_err(netdev, "Failed to init txrxq irq\n");
+ netdev_err(netdev, "Failed to configure device resources\n");
goto err_uninit_qps_irq;
}
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index a563a94e2780..288fa8ce53af 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -146,7 +146,7 @@ config IXGBE
tristate "Intel(R) 10GbE PCI Express adapters support"
depends on PCI
depends on PTP_1588_CLOCK_OPTIONAL
- select LIBIE_FWLOG
+ select LIBIE_FWLOG if DEBUG_FS
select MDIO
select NET_DEVLINK
select PLDMFW
@@ -296,9 +296,10 @@ config ICE
depends on GNSS || GNSS = n
select AUXILIARY_BUS
select DIMLIB
+ select LIBETH_XDP
select LIBIE
select LIBIE_ADMINQ
- select LIBIE_FWLOG
+ select LIBIE_FWLOG if DEBUG_FS
select NET_DEVLINK
select PACKING
select PLDMFW
diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h
index 018e61aea787..aa08f397988e 100644
--- a/drivers/net/ethernet/intel/e1000e/e1000.h
+++ b/drivers/net/ethernet/intel/e1000e/e1000.h
@@ -461,6 +461,7 @@ s32 e1000e_get_base_timinca(struct e1000_adapter *adapter, u32 *timinca);
#define FLAG2_CHECK_RX_HWTSTAMP BIT(13)
#define FLAG2_CHECK_SYSTIM_OVERFLOW BIT(14)
#define FLAG2_ENABLE_S0IX_FLOWS BIT(15)
+#define FLAG2_DISABLE_K1 BIT(16)
#define E1000_RX_DESC_PS(R, i) \
(&(((union e1000_rx_desc_packet_split *)((R).desc))[i]))
diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c
index 8e40bb50a01e..7b1ac90b3de4 100644
--- a/drivers/net/ethernet/intel/e1000e/ethtool.c
+++ b/drivers/net/ethernet/intel/e1000e/ethtool.c
@@ -26,6 +26,8 @@ struct e1000_stats {
static const char e1000e_priv_flags_strings[][ETH_GSTRING_LEN] = {
#define E1000E_PRIV_FLAGS_S0IX_ENABLED BIT(0)
"s0ix-enabled",
+#define E1000E_PRIV_FLAGS_DISABLE_K1 BIT(1)
+ "disable-k1",
};
#define E1000E_PRIV_FLAGS_STR_LEN ARRAY_SIZE(e1000e_priv_flags_strings)
@@ -549,9 +551,9 @@ static int e1000_set_eeprom(struct net_device *netdev,
{
struct e1000_adapter *adapter = netdev_priv(netdev);
struct e1000_hw *hw = &adapter->hw;
- size_t total_len, max_len;
u16 *eeprom_buff;
int ret_val = 0;
+ size_t max_len;
int first_word;
int last_word;
void *ptr;
@@ -569,10 +571,6 @@ static int e1000_set_eeprom(struct net_device *netdev,
max_len = hw->nvm.word_size * 2;
- if (check_add_overflow(eeprom->offset, eeprom->len, &total_len) ||
- total_len > max_len)
- return -EFBIG;
-
first_word = eeprom->offset >> 1;
last_word = (eeprom->offset + eeprom->len - 1) >> 1;
eeprom_buff = kmalloc(max_len, GFP_KERNEL);
@@ -2301,26 +2299,59 @@ static u32 e1000e_get_priv_flags(struct net_device *netdev)
if (adapter->flags2 & FLAG2_ENABLE_S0IX_FLOWS)
priv_flags |= E1000E_PRIV_FLAGS_S0IX_ENABLED;
+ if (adapter->flags2 & FLAG2_DISABLE_K1)
+ priv_flags |= E1000E_PRIV_FLAGS_DISABLE_K1;
+
return priv_flags;
}
static int e1000e_set_priv_flags(struct net_device *netdev, u32 priv_flags)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
unsigned int flags2 = adapter->flags2;
+ unsigned int changed;
- flags2 &= ~FLAG2_ENABLE_S0IX_FLOWS;
- if (priv_flags & E1000E_PRIV_FLAGS_S0IX_ENABLED) {
- struct e1000_hw *hw = &adapter->hw;
+ flags2 &= ~(FLAG2_ENABLE_S0IX_FLOWS | FLAG2_DISABLE_K1);
- if (hw->mac.type < e1000_pch_cnp)
+ if (priv_flags & E1000E_PRIV_FLAGS_S0IX_ENABLED) {
+ if (hw->mac.type < e1000_pch_cnp) {
+ e_err("S0ix is not supported on this device\n");
return -EINVAL;
+ }
+
flags2 |= FLAG2_ENABLE_S0IX_FLOWS;
}
- if (flags2 != adapter->flags2)
+ if (priv_flags & E1000E_PRIV_FLAGS_DISABLE_K1) {
+ if (hw->mac.type < e1000_ich8lan) {
+ e_err("Disabling K1 is not supported on this device\n");
+ return -EINVAL;
+ }
+
+ flags2 |= FLAG2_DISABLE_K1;
+ }
+
+ changed = adapter->flags2 ^ flags2;
+ if (changed)
adapter->flags2 = flags2;
+ if (changed & FLAG2_DISABLE_K1) {
+ /* reset the hardware to apply the changes */
+ while (test_and_set_bit(__E1000_RESETTING,
+ &adapter->state))
+ usleep_range(1000, 2000);
+
+ if (netif_running(adapter->netdev)) {
+ e1000e_down(adapter, true);
+ e1000e_up(adapter);
+ } else {
+ e1000e_reset(adapter);
+ }
+
+ clear_bit(__E1000_RESETTING, &adapter->state);
+ }
+
return 0;
}
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index df4e7d781cb1..0ff8688ac3b8 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -286,21 +286,26 @@ static void e1000_toggle_lanphypc_pch_lpt(struct e1000_hw *hw)
}
/**
- * e1000_reconfigure_k1_exit_timeout - reconfigure K1 exit timeout to
- * align to MTP and later platform requirements.
+ * e1000_reconfigure_k1_params - reconfigure Kumeran K1 parameters.
* @hw: pointer to the HW structure
*
+ * By default K1 is enabled after MAC reset, so this function only
+ * disables it.
+ *
* Context: PHY semaphore must be held by caller.
* Return: 0 on success, negative on failure
*/
-static s32 e1000_reconfigure_k1_exit_timeout(struct e1000_hw *hw)
+static s32 e1000_reconfigure_k1_params(struct e1000_hw *hw)
{
u16 phy_timeout;
u32 fextnvm12;
s32 ret_val;
- if (hw->mac.type < e1000_pch_mtp)
+ if (hw->mac.type < e1000_pch_mtp) {
+ if (hw->adapter->flags2 & FLAG2_DISABLE_K1)
+ return e1000_configure_k1_ich8lan(hw, false);
return 0;
+ }
/* Change Kumeran K1 power down state from P0s to P1 */
fextnvm12 = er32(FEXTNVM12);
@@ -310,6 +315,8 @@ static s32 e1000_reconfigure_k1_exit_timeout(struct e1000_hw *hw)
/* Wait for the interface the settle */
usleep_range(1000, 1100);
+ if (hw->adapter->flags2 & FLAG2_DISABLE_K1)
+ return e1000_configure_k1_ich8lan(hw, false);
/* Change K1 exit timeout */
ret_val = e1e_rphy_locked(hw, I217_PHY_TIMEOUTS_REG,
@@ -373,8 +380,8 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
/* At this point the PHY might be inaccessible so don't
* propagate the failure
*/
- if (e1000_reconfigure_k1_exit_timeout(hw))
- e_dbg("Failed to reconfigure K1 exit timeout\n");
+ if (e1000_reconfigure_k1_params(hw))
+ e_dbg("Failed to reconfigure K1 parameters\n");
fallthrough;
case e1000_pch_lpt:
@@ -473,10 +480,10 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw)
if (hw->mac.type >= e1000_pch_mtp) {
ret_val = hw->phy.ops.acquire(hw);
if (ret_val) {
- e_err("Failed to reconfigure K1 exit timeout\n");
+ e_err("Failed to reconfigure K1 parameters\n");
goto out;
}
- ret_val = e1000_reconfigure_k1_exit_timeout(hw);
+ ret_val = e1000_reconfigure_k1_params(hw);
hw->phy.ops.release(hw);
}
}
@@ -4948,17 +4955,15 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw)
u16 i;
e1000_initialize_hw_bits_ich8lan(hw);
- if (hw->mac.type >= e1000_pch_mtp) {
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- return ret_val;
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
- ret_val = e1000_reconfigure_k1_exit_timeout(hw);
- hw->phy.ops.release(hw);
- if (ret_val) {
- e_dbg("Error failed to reconfigure K1 exit timeout\n");
- return ret_val;
- }
+ ret_val = e1000_reconfigure_k1_params(hw);
+ hw->phy.ops.release(hw);
+ if (ret_val) {
+ e_dbg("Error failed to reconfigure K1 parameters\n");
+ return ret_val;
}
/* Initialize identification LED */
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 201322dac233..ddbe2f7d8112 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -7195,7 +7195,6 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
"Cannot re-enable PCI device after reset.\n");
result = PCI_ERS_RESULT_DISCONNECT;
} else {
- pdev->state_saved = true;
pci_restore_state(pdev);
pci_set_master(pdev);
@@ -7675,6 +7674,9 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* init PTP hardware clock */
e1000e_ptp_init(adapter);
+ if (hw->mac.type >= e1000_pch_mtp)
+ adapter->flags2 |= FLAG2_DISABLE_K1;
+
/* reset the hardware with the new settings */
e1000e_reset(adapter);
diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c
index ea3c3eb2ef20..ec39e35f3857 100644
--- a/drivers/net/ethernet/intel/e1000e/ptp.c
+++ b/drivers/net/ethernet/intel/e1000e/ptp.c
@@ -229,14 +229,11 @@ static void e1000e_systim_overflow_work(struct work_struct *work)
systim_overflow_work.work);
struct e1000_hw *hw = &adapter->hw;
struct timespec64 ts;
- u64 ns;
/* Update the timecounter */
- ns = timecounter_read(&adapter->tc);
+ ts = ns_to_timespec64(timecounter_read(&adapter->tc));
- ts = ns_to_timespec64(ns);
- e_dbg("SYSTIM overflow check at %lld.%09lu\n",
- (long long) ts.tv_sec, ts.tv_nsec);
+ e_dbg("SYSTIM overflow check at %ptSp\n", &ts);
schedule_delayed_work(&adapter->systim_overflow_work,
E1000_SYSTIM_OVERFLOW_PERIOD);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index bf2029144c1d..76e42abca965 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -734,22 +734,11 @@ static int fm10k_get_rssh_fields(struct net_device *dev,
return 0;
}
-static int fm10k_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
- u32 __always_unused *rule_locs)
+static u32 fm10k_get_rx_ring_count(struct net_device *dev)
{
struct fm10k_intfc *interface = netdev_priv(dev);
- int ret = -EOPNOTSUPP;
- switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = interface->num_rx_queues;
- ret = 0;
- break;
- default:
- break;
- }
-
- return ret;
+ return interface->num_rx_queues;
}
static int fm10k_set_rssh_fields(struct net_device *dev,
@@ -1160,7 +1149,7 @@ static const struct ethtool_ops fm10k_ethtool_ops = {
.set_ringparam = fm10k_set_ringparam,
.get_coalesce = fm10k_get_coalesce,
.set_coalesce = fm10k_set_coalesce,
- .get_rxnfc = fm10k_get_rxnfc,
+ .get_rx_ring_count = fm10k_get_rx_ring_count,
.get_regs = fm10k_get_regs,
.get_regs_len = fm10k_get_regs_len,
.self_test = fm10k_self_test,
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
index ae5fe34659cf..d75b8a50413d 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
@@ -2423,12 +2423,6 @@ static pci_ers_result_t fm10k_io_slot_reset(struct pci_dev *pdev)
} else {
pci_set_master(pdev);
pci_restore_state(pdev);
-
- /* After second error pci->state_saved is false, this
- * resets it so EEH doesn't break.
- */
- pci_save_state(pdev);
-
pci_wake_from_d3(pdev, false);
result = PCI_ERS_RESULT_RECOVERED;
diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h
index 801a57a925da..d2d03db2acec 100644
--- a/drivers/net/ethernet/intel/i40e/i40e.h
+++ b/drivers/net/ethernet/intel/i40e/i40e.h
@@ -574,6 +574,10 @@ struct i40e_pf {
struct i40e_vf *vf;
int num_alloc_vfs; /* actual number of VFs allocated */
u32 vf_aq_requests;
+ /* If set to non-zero, the device uses this value
+ * as maximum number of MAC filters per VF.
+ */
+ u32 max_mac_per_vf;
u32 arq_overflows; /* Not fatal, possibly indicative of problems */
struct ratelimit_state mdd_message_rate_limit;
/* DCBx/DCBNL capability for PF that indicates
diff --git a/drivers/net/ethernet/intel/i40e/i40e_devlink.c b/drivers/net/ethernet/intel/i40e/i40e_devlink.c
index cc4e9e2addb7..229179ccc131 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_devlink.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_devlink.c
@@ -5,6 +5,42 @@
#include "i40e.h"
#include "i40e_devlink.h"
+static int i40e_max_mac_per_vf_set(struct devlink *devlink,
+ u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct i40e_pf *pf = devlink_priv(devlink);
+
+ if (pf->num_alloc_vfs > 0) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot change max_mac_per_vf while SR-IOV is enabled");
+ return -EBUSY;
+ }
+
+ pf->max_mac_per_vf = ctx->val.vu32;
+ return 0;
+}
+
+static int i40e_max_mac_per_vf_get(struct devlink *devlink,
+ u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct i40e_pf *pf = devlink_priv(devlink);
+
+ ctx->val.vu32 = pf->max_mac_per_vf;
+ return 0;
+}
+
+static const struct devlink_param i40e_dl_params[] = {
+ DEVLINK_PARAM_GENERIC(MAX_MAC_PER_VF,
+ BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+ i40e_max_mac_per_vf_get,
+ i40e_max_mac_per_vf_set,
+ NULL),
+};
+
static void i40e_info_get_dsn(struct i40e_pf *pf, char *buf, size_t len)
{
u8 dsn[8];
@@ -165,7 +201,18 @@ void i40e_free_pf(struct i40e_pf *pf)
**/
void i40e_devlink_register(struct i40e_pf *pf)
{
- devlink_register(priv_to_devlink(pf));
+ struct devlink *dl = priv_to_devlink(pf);
+ struct device *dev = &pf->pdev->dev;
+ int err;
+
+ err = devlink_params_register(dl, i40e_dl_params,
+ ARRAY_SIZE(i40e_dl_params));
+ if (err)
+ dev_err(dev,
+ "devlink params register failed with error %d", err);
+
+ devlink_register(dl);
+
}
/**
@@ -176,7 +223,11 @@ void i40e_devlink_register(struct i40e_pf *pf)
**/
void i40e_devlink_unregister(struct i40e_pf *pf)
{
- devlink_unregister(priv_to_devlink(pf));
+ struct devlink *dl = priv_to_devlink(pf);
+
+ devlink_unregister(dl);
+ devlink_params_unregister(dl, i40e_dl_params,
+ ARRAY_SIZE(i40e_dl_params));
}
/**
diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
index 86c72596617a..f2c2646ea298 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c
@@ -3522,6 +3522,20 @@ no_input_set:
}
/**
+ * i40e_get_rx_ring_count - get RX ring count
+ * @netdev: network interface device structure
+ *
+ * Return: number of RX rings.
+ **/
+static u32 i40e_get_rx_ring_count(struct net_device *netdev)
+{
+ struct i40e_netdev_priv *np = netdev_priv(netdev);
+ struct i40e_vsi *vsi = np->vsi;
+
+ return vsi->rss_size;
+}
+
+/**
* i40e_get_rxnfc - command to get RX flow classification rules
* @netdev: network interface device structure
* @cmd: ethtool rxnfc command
@@ -3538,10 +3552,6 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = vsi->rss_size;
- ret = 0;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = pf->fdir_pf_active_filters;
/* report total rule count */
@@ -5819,6 +5829,7 @@ static const struct ethtool_ops i40e_ethtool_ops = {
.set_msglevel = i40e_set_msglevel,
.get_rxnfc = i40e_get_rxnfc,
.set_rxnfc = i40e_set_rxnfc,
+ .get_rx_ring_count = i40e_get_rx_ring_count,
.self_test = i40e_diag_test,
.get_strings = i40e_get_strings,
.get_eee = i40e_get_eee,
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
index 50be0a60ae13..d8192aa23254 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
@@ -16455,7 +16455,6 @@ static pci_ers_result_t i40e_pci_error_slot_reset(struct pci_dev *pdev)
} else {
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
pci_wake_from_d3(pdev, false);
reg = rd32(&pf->hw, I40E_GLGEN_RTRIG);
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
index 081a4526a2f0..8b30a3accd31 100644
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
@@ -2935,33 +2935,48 @@ static inline int i40e_check_vf_permission(struct i40e_vf *vf,
if (!f)
++mac_add_cnt;
}
-
- /* If this VF is not privileged, then we can't add more than a limited
- * number of addresses.
+ /* Determine the maximum number of MAC addresses this VF may use.
+ *
+ * - For untrusted VFs: use a fixed small limit.
+ *
+ * - For trusted VFs: limit is calculated by dividing total MAC
+ * filter pool across all VFs/ports.
*
- * If this VF is trusted, it can use more resources than untrusted.
- * However to ensure that every trusted VF has appropriate number of
- * resources, divide whole pool of resources per port and then across
- * all VFs.
+ * - User can override this by devlink param "max_mac_per_vf".
+ * If set its value is used as a strict cap for both trusted and
+ * untrusted VFs.
+ * Note:
+ * even when overridden, this is a theoretical maximum; hardware
+ * may reject additional MACs if the absolute HW limit is reached.
*/
if (!vf_trusted)
mac_add_max = I40E_VC_MAX_MAC_ADDR_PER_VF;
else
mac_add_max = I40E_VC_MAX_MACVLAN_PER_TRUSTED_VF(pf->num_alloc_vfs, hw->num_ports);
+ if (pf->max_mac_per_vf > 0)
+ mac_add_max = pf->max_mac_per_vf;
+
/* VF can replace all its filters in one step, in this case mac_add_max
* will be added as active and another mac_add_max will be in
* a to-be-removed state. Account for that.
*/
if ((i40e_count_active_filters(vsi) + mac_add_cnt) > mac_add_max ||
(i40e_count_all_filters(vsi) + mac_add_cnt) > 2 * mac_add_max) {
+ if (pf->max_mac_per_vf == mac_add_max && mac_add_max > 0) {
+ dev_err(&pf->pdev->dev,
+ "Cannot add more MAC addresses: VF reached its maximum allowed limit (%d)\n",
+ mac_add_max);
+ return -EPERM;
+ }
if (!vf_trusted) {
dev_err(&pf->pdev->dev,
"Cannot add more MAC addresses, VF is not trusted, switch the VF to trusted to add more functionality\n");
return -EPERM;
} else {
dev_err(&pf->pdev->dev,
- "Cannot add more MAC addresses, trusted VF exhausted it's resources\n");
+ "Cannot add more MAC addresses: trusted VF reached its maximum allowed limit (%d)\n",
+ mac_add_max);
return -EPERM;
}
}
@@ -4788,6 +4803,7 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
unsigned long q_map;
struct i40e_vf *vf;
int abs_vf_id;
+ int old_link;
int ret = 0;
int tmp;
@@ -4806,6 +4822,17 @@ int i40e_ndo_set_vf_link_state(struct net_device *netdev, int vf_id, int link)
vf = &pf->vf[vf_id];
abs_vf_id = vf->vf_id + hw->func_caps.vf_base_id;
+ /* skip VF link state change if requested state is already set */
+ if (!vf->link_forced)
+ old_link = IFLA_VF_LINK_STATE_AUTO;
+ else if (vf->link_up)
+ old_link = IFLA_VF_LINK_STATE_ENABLE;
+ else
+ old_link = IFLA_VF_LINK_STATE_DISABLE;
+
+ if (link == old_link)
+ goto error_out;
+
pfe.event = VIRTCHNL_EVENT_LINK_CHANGE;
pfe.severity = PF_EVENT_SEVERITY_INFO;
diff --git a/drivers/net/ethernet/intel/iavf/iavf_adv_rss.c b/drivers/net/ethernet/intel/iavf/iavf_adv_rss.c
index a9e1da35e248..4d12dfe1b481 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_adv_rss.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_adv_rss.c
@@ -91,6 +91,55 @@ iavf_fill_adv_rss_sctp_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
}
/**
+ * iavf_fill_adv_rss_gtp_hdr - Fill GTP-related RSS protocol headers
+ * @proto_hdrs: pointer to the virtchnl protocol headers structure to populate
+ * @packet_hdrs: bitmask of packet header types to configure
+ * @hash_flds: RSS hash field configuration
+ *
+ * This function populates the virtchnl protocol header structure with
+ * appropriate GTP-related header types based on the specified packet_hdrs.
+ * It supports GTPC, GTPU with extension headers, and uplink/downlink PDU
+ * types. For certain GTPU types, it also appends an IPv4 header to enable
+ * hashing on the destination IP address.
+ *
+ * Return: 0 on success or -EOPNOTSUPP if the packet_hdrs value is unsupported.
+ */
+static int
+iavf_fill_adv_rss_gtp_hdr(struct virtchnl_proto_hdrs *proto_hdrs,
+ u32 packet_hdrs, u64 hash_flds)
+{
+ struct virtchnl_proto_hdr *hdr;
+
+ hdr = &proto_hdrs->proto_hdr[proto_hdrs->count - 1];
+
+ switch (packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_GTP) {
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC_TEID:
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC:
+ VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, GTPC);
+ break;
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_EH:
+ VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, GTPU_EH);
+ break;
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_UP:
+ VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, GTPU_EH_PDU_UP);
+ hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+ iavf_fill_adv_rss_ip4_hdr(hdr, IAVF_ADV_RSS_HASH_FLD_IPV4_DA);
+ break;
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_DWN:
+ VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, GTPU_EH_PDU_DWN);
+ fallthrough;
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_IP:
+ hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+ iavf_fill_adv_rss_ip4_hdr(hdr, IAVF_ADV_RSS_HASH_FLD_IPV4_DA);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/**
* iavf_fill_adv_rss_cfg_msg - fill the RSS configuration into virtchnl message
* @rss_cfg: the virtchnl message to be filled with RSS configuration setting
* @packet_hdrs: the RSS configuration protocol header types
@@ -103,6 +152,8 @@ int
iavf_fill_adv_rss_cfg_msg(struct virtchnl_rss_cfg *rss_cfg,
u32 packet_hdrs, u64 hash_flds, bool symm)
{
+ const u32 packet_l3_hdrs = packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_L3;
+ const u32 packet_l4_hdrs = packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_L4;
struct virtchnl_proto_hdrs *proto_hdrs = &rss_cfg->proto_hdrs;
struct virtchnl_proto_hdr *hdr;
@@ -113,31 +164,41 @@ iavf_fill_adv_rss_cfg_msg(struct virtchnl_rss_cfg *rss_cfg,
proto_hdrs->tunnel_level = 0; /* always outer layer */
- hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
- switch (packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_L3) {
- case IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4:
- iavf_fill_adv_rss_ip4_hdr(hdr, hash_flds);
- break;
- case IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6:
- iavf_fill_adv_rss_ip6_hdr(hdr, hash_flds);
- break;
- default:
- return -EINVAL;
+ if (packet_l3_hdrs) {
+ hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+ switch (packet_l3_hdrs) {
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4:
+ iavf_fill_adv_rss_ip4_hdr(hdr, hash_flds);
+ break;
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6:
+ iavf_fill_adv_rss_ip6_hdr(hdr, hash_flds);
+ break;
+ default:
+ return -EINVAL;
+ }
}
- hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
- switch (packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_L4) {
- case IAVF_ADV_RSS_FLOW_SEG_HDR_TCP:
- iavf_fill_adv_rss_tcp_hdr(hdr, hash_flds);
- break;
- case IAVF_ADV_RSS_FLOW_SEG_HDR_UDP:
- iavf_fill_adv_rss_udp_hdr(hdr, hash_flds);
- break;
- case IAVF_ADV_RSS_FLOW_SEG_HDR_SCTP:
- iavf_fill_adv_rss_sctp_hdr(hdr, hash_flds);
- break;
- default:
- return -EINVAL;
+ if (packet_l4_hdrs) {
+ hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+ switch (packet_l4_hdrs) {
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_TCP:
+ iavf_fill_adv_rss_tcp_hdr(hdr, hash_flds);
+ break;
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_UDP:
+ iavf_fill_adv_rss_udp_hdr(hdr, hash_flds);
+ break;
+ case IAVF_ADV_RSS_FLOW_SEG_HDR_SCTP:
+ iavf_fill_adv_rss_sctp_hdr(hdr, hash_flds);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ if (packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_GTP) {
+ hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+ if (iavf_fill_adv_rss_gtp_hdr(proto_hdrs, packet_hdrs, hash_flds))
+ return -EINVAL;
}
return 0;
@@ -186,6 +247,8 @@ iavf_print_adv_rss_cfg(struct iavf_adapter *adapter, struct iavf_adv_rss *rss,
proto = "UDP";
else if (packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_SCTP)
proto = "SCTP";
+ else if (packet_hdrs & IAVF_ADV_RSS_FLOW_SEG_HDR_GTP)
+ proto = "GTP";
else
return;
@@ -211,6 +274,16 @@ iavf_print_adv_rss_cfg(struct iavf_adapter *adapter, struct iavf_adv_rss *rss,
IAVF_ADV_RSS_HASH_FLD_UDP_DST_PORT |
IAVF_ADV_RSS_HASH_FLD_SCTP_DST_PORT))
strcat(hash_opt, "dst port,");
+ if (hash_flds & IAVF_ADV_RSS_HASH_FLD_GTPC_TEID)
+ strcat(hash_opt, "gtp-c,");
+ if (hash_flds & IAVF_ADV_RSS_HASH_FLD_GTPU_IP_TEID)
+ strcat(hash_opt, "gtp-u ip,");
+ if (hash_flds & IAVF_ADV_RSS_HASH_FLD_GTPU_EH_TEID)
+ strcat(hash_opt, "gtp-u ext,");
+ if (hash_flds & IAVF_ADV_RSS_HASH_FLD_GTPU_UP_TEID)
+ strcat(hash_opt, "gtp-u ul,");
+ if (hash_flds & IAVF_ADV_RSS_HASH_FLD_GTPU_DWN_TEID)
+ strcat(hash_opt, "gtp-u dl,");
if (!action)
action = "";
diff --git a/drivers/net/ethernet/intel/iavf/iavf_adv_rss.h b/drivers/net/ethernet/intel/iavf/iavf_adv_rss.h
index e31eb2afebea..74cc9e0d528c 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_adv_rss.h
+++ b/drivers/net/ethernet/intel/iavf/iavf_adv_rss.h
@@ -22,6 +22,12 @@ enum iavf_adv_rss_flow_seg_hdr {
IAVF_ADV_RSS_FLOW_SEG_HDR_TCP = 0x00000004,
IAVF_ADV_RSS_FLOW_SEG_HDR_UDP = 0x00000008,
IAVF_ADV_RSS_FLOW_SEG_HDR_SCTP = 0x00000010,
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC = 0x00000400,
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC_TEID = 0x00000800,
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_IP = 0x00001000,
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_EH = 0x00002000,
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_DWN = 0x00004000,
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_UP = 0x00008000,
};
#define IAVF_ADV_RSS_FLOW_SEG_HDR_L3 \
@@ -33,6 +39,14 @@ enum iavf_adv_rss_flow_seg_hdr {
IAVF_ADV_RSS_FLOW_SEG_HDR_UDP | \
IAVF_ADV_RSS_FLOW_SEG_HDR_SCTP)
+#define IAVF_ADV_RSS_FLOW_SEG_HDR_GTP \
+ (IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC | \
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC_TEID | \
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_IP | \
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_EH | \
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_DWN | \
+ IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_UP)
+
enum iavf_adv_rss_flow_field {
/* L3 */
IAVF_ADV_RSS_FLOW_FIELD_IDX_IPV4_SA,
@@ -46,6 +60,17 @@ enum iavf_adv_rss_flow_field {
IAVF_ADV_RSS_FLOW_FIELD_IDX_UDP_DST_PORT,
IAVF_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT,
IAVF_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT,
+ /* GTPC_TEID */
+ IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPC_TEID,
+ /* GTPU_IP */
+ IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_IP_TEID,
+ /* GTPU_EH */
+ IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_EH_TEID,
+ IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_EH_QFI,
+ /* GTPU_UP */
+ IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_UP_TEID,
+ /* GTPU_DWN */
+ IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_DWN_TEID,
/* The total number of enums must not exceed 64 */
IAVF_ADV_RSS_FLOW_FIELD_IDX_MAX
@@ -72,6 +97,12 @@ enum iavf_adv_rss_flow_field {
BIT_ULL(IAVF_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT)
#define IAVF_ADV_RSS_HASH_FLD_SCTP_DST_PORT \
BIT_ULL(IAVF_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT)
+#define IAVF_ADV_RSS_HASH_FLD_GTPC_TEID BIT_ULL(IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPC_TEID)
+#define IAVF_ADV_RSS_HASH_FLD_GTPU_IP_TEID BIT_ULL(IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_IP_TEID)
+#define IAVF_ADV_RSS_HASH_FLD_GTPU_EH_TEID BIT_ULL(IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_EH_TEID)
+#define IAVF_ADV_RSS_HASH_FLD_GTPU_UP_TEID BIT_ULL(IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_UP_TEID)
+#define IAVF_ADV_RSS_HASH_FLD_GTPU_DWN_TEID \
+ BIT_ULL(IAVF_ADV_RSS_FLOW_FIELD_IDX_GTPU_DWN_TEID)
/* bookkeeping of advanced RSS configuration */
struct iavf_adv_rss {
diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
index 05d72be3fe80..2cc21289a707 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
@@ -1336,6 +1336,56 @@ static u32 iavf_adv_rss_parse_hdrs(const struct ethtool_rxfh_fields *cmd)
hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_SCTP |
IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6;
break;
+ case GTPU_V4_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_IP |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4;
+ break;
+ case GTPC_V4_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_UDP |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4;
+ break;
+ case GTPC_TEID_V4_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC_TEID |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_UDP |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4;
+ break;
+ case GTPU_EH_V4_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_EH |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4;
+ break;
+ case GTPU_UL_V4_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_UP |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4;
+ break;
+ case GTPU_DL_V4_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_DWN |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV4;
+ break;
+ case GTPU_V6_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_IP |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6;
+ break;
+ case GTPC_V6_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6;
+ break;
+ case GTPC_TEID_V6_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPC_TEID |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6;
+ break;
+ case GTPU_EH_V6_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_EH |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6;
+ break;
+ case GTPU_UL_V6_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_UP |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6;
+ break;
+ case GTPU_DL_V6_FLOW:
+ hdrs |= IAVF_ADV_RSS_FLOW_SEG_HDR_GTPU_DWN |
+ IAVF_ADV_RSS_FLOW_SEG_HDR_IPV6;
+ break;
default:
break;
}
@@ -1353,6 +1403,12 @@ iavf_adv_rss_parse_hash_flds(const struct ethtool_rxfh_fields *cmd, bool symm)
case TCP_V4_FLOW:
case UDP_V4_FLOW:
case SCTP_V4_FLOW:
+ case GTPU_V4_FLOW:
+ case GTPC_V4_FLOW:
+ case GTPC_TEID_V4_FLOW:
+ case GTPU_EH_V4_FLOW:
+ case GTPU_UL_V4_FLOW:
+ case GTPU_DL_V4_FLOW:
if (cmd->data & RXH_IP_SRC)
hfld |= IAVF_ADV_RSS_HASH_FLD_IPV4_SA;
if (cmd->data & RXH_IP_DST)
@@ -1361,6 +1417,12 @@ iavf_adv_rss_parse_hash_flds(const struct ethtool_rxfh_fields *cmd, bool symm)
case TCP_V6_FLOW:
case UDP_V6_FLOW:
case SCTP_V6_FLOW:
+ case GTPU_V6_FLOW:
+ case GTPC_V6_FLOW:
+ case GTPC_TEID_V6_FLOW:
+ case GTPU_EH_V6_FLOW:
+ case GTPU_UL_V6_FLOW:
+ case GTPU_DL_V6_FLOW:
if (cmd->data & RXH_IP_SRC)
hfld |= IAVF_ADV_RSS_HASH_FLD_IPV6_SA;
if (cmd->data & RXH_IP_DST)
@@ -1382,6 +1444,7 @@ iavf_adv_rss_parse_hash_flds(const struct ethtool_rxfh_fields *cmd, bool symm)
break;
case UDP_V4_FLOW:
case UDP_V6_FLOW:
+ case GTPC_V4_FLOW:
if (cmd->data & RXH_L4_B_0_1)
hfld |= IAVF_ADV_RSS_HASH_FLD_UDP_SRC_PORT;
if (cmd->data & RXH_L4_B_2_3)
@@ -1398,6 +1461,32 @@ iavf_adv_rss_parse_hash_flds(const struct ethtool_rxfh_fields *cmd, bool symm)
break;
}
}
+ if (cmd->data & RXH_GTP_TEID) {
+ switch (cmd->flow_type) {
+ case GTPC_TEID_V4_FLOW:
+ case GTPC_TEID_V6_FLOW:
+ hfld |= IAVF_ADV_RSS_HASH_FLD_GTPC_TEID;
+ break;
+ case GTPU_V4_FLOW:
+ case GTPU_V6_FLOW:
+ hfld |= IAVF_ADV_RSS_HASH_FLD_GTPU_IP_TEID;
+ break;
+ case GTPU_EH_V4_FLOW:
+ case GTPU_EH_V6_FLOW:
+ hfld |= IAVF_ADV_RSS_HASH_FLD_GTPU_EH_TEID;
+ break;
+ case GTPU_UL_V4_FLOW:
+ case GTPU_UL_V6_FLOW:
+ hfld |= IAVF_ADV_RSS_HASH_FLD_GTPU_UP_TEID;
+ break;
+ case GTPU_DL_V4_FLOW:
+ case GTPU_DL_V6_FLOW:
+ hfld |= IAVF_ADV_RSS_HASH_FLD_GTPU_DWN_TEID;
+ break;
+ default:
+ break;
+ }
+ }
return hfld;
}
@@ -1550,6 +1639,19 @@ static int iavf_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
}
/**
+ * iavf_get_rx_ring_count - get RX ring count
+ * @netdev: network interface device structure
+ *
+ * Return: number of RX rings.
+ **/
+static u32 iavf_get_rx_ring_count(struct net_device *netdev)
+{
+ struct iavf_adapter *adapter = netdev_priv(netdev);
+
+ return adapter->num_active_queues;
+}
+
+/**
* iavf_get_rxnfc - command to get RX flow classification rules
* @netdev: network interface device structure
* @cmd: ethtool rxnfc command
@@ -1564,10 +1666,6 @@ static int iavf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = adapter->num_active_queues;
- ret = 0;
- break;
case ETHTOOL_GRXCLSRLCNT:
if (!(adapter->flags & IAVF_FLAG_FDIR_ENABLED))
break;
@@ -1777,6 +1875,7 @@ static const struct ethtool_ops iavf_ethtool_ops = {
.set_per_queue_coalesce = iavf_set_per_queue_coalesce,
.set_rxnfc = iavf_set_rxnfc,
.get_rxnfc = iavf_get_rxnfc,
+ .get_rx_ring_count = iavf_get_rx_ring_count,
.get_rxfh_indir_size = iavf_get_rxfh_indir_size,
.get_rxfh = iavf_get_rxfh,
.set_rxfh = iavf_set_rxfh,
diff --git a/drivers/net/ethernet/intel/iavf/iavf_ptp.c b/drivers/net/ethernet/intel/iavf/iavf_ptp.c
index b4d5eda2e84f..9cbd8c154031 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_ptp.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_ptp.c
@@ -252,6 +252,12 @@ static int iavf_ptp_gettimex64(struct ptp_clock_info *info,
return iavf_read_phc_indirect(adapter, ts, sts);
}
+static int iavf_ptp_settime64(struct ptp_clock_info *info,
+ const struct timespec64 *ts)
+{
+ return -EOPNOTSUPP;
+}
+
/**
* iavf_ptp_cache_phc_time - Cache PHC time for performing timestamp extension
* @adapter: private adapter structure
@@ -320,6 +326,7 @@ static int iavf_ptp_register_clock(struct iavf_adapter *adapter)
KBUILD_MODNAME, dev_name(dev));
ptp_info->owner = THIS_MODULE;
ptp_info->gettimex64 = iavf_ptp_gettimex64;
+ ptp_info->settime64 = iavf_ptp_settime64;
ptp_info->do_aux_work = iavf_ptp_do_aux_work;
clock = ptp_clock_register(ptp_info, dev);
diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
index 34a422a4a29c..88156082a41d 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
@@ -793,7 +793,8 @@ void iavf_add_vlans(struct iavf_adapter *adapter)
len = virtchnl_struct_size(vvfl, vlan_id, count);
if (len > IAVF_MAX_AQ_BUF_SIZE) {
- dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n");
+ dev_info(&adapter->pdev->dev,
+ "virtchnl: Too many VLAN add (v1) requests; splitting into multiple messages to PF\n");
while (len > IAVF_MAX_AQ_BUF_SIZE)
len = virtchnl_struct_size(vvfl, vlan_id,
--count);
@@ -838,7 +839,8 @@ void iavf_add_vlans(struct iavf_adapter *adapter)
len = virtchnl_struct_size(vvfl_v2, filters, count);
if (len > IAVF_MAX_AQ_BUF_SIZE) {
- dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n");
+ dev_info(&adapter->pdev->dev,
+ "virtchnl: Too many VLAN add (v2) requests; splitting into multiple messages to PF\n");
while (len > IAVF_MAX_AQ_BUF_SIZE)
len = virtchnl_struct_size(vvfl_v2, filters,
--count);
@@ -941,7 +943,8 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
len = virtchnl_struct_size(vvfl, vlan_id, count);
if (len > IAVF_MAX_AQ_BUF_SIZE) {
- dev_warn(&adapter->pdev->dev, "Too many delete VLAN changes in one request\n");
+ dev_info(&adapter->pdev->dev,
+ "virtchnl: Too many VLAN delete (v1) requests; splitting into multiple messages to PF\n");
while (len > IAVF_MAX_AQ_BUF_SIZE)
len = virtchnl_struct_size(vvfl, vlan_id,
--count);
@@ -987,7 +990,8 @@ void iavf_del_vlans(struct iavf_adapter *adapter)
len = virtchnl_struct_size(vvfl_v2, filters, count);
if (len > IAVF_MAX_AQ_BUF_SIZE) {
- dev_warn(&adapter->pdev->dev, "Too many add VLAN changes in one request\n");
+ dev_info(&adapter->pdev->dev,
+ "virtchnl: Too many VLAN delete (v2) requests; splitting into multiple messages to PF\n");
while (len > IAVF_MAX_AQ_BUF_SIZE)
len = virtchnl_struct_size(vvfl_v2, filters,
--count);
diff --git a/drivers/net/ethernet/intel/ice/devlink/devlink.c b/drivers/net/ethernet/intel/ice/devlink/devlink.c
index fb2de521731a..d88b7f3fd1f9 100644
--- a/drivers/net/ethernet/intel/ice/devlink/devlink.c
+++ b/drivers/net/ethernet/intel/ice/devlink/devlink.c
@@ -459,6 +459,7 @@ static void ice_devlink_reinit_down(struct ice_pf *pf)
rtnl_lock();
ice_vsi_decfg(ice_get_main_vsi(pf));
rtnl_unlock();
+ ice_deinit_pf(pf);
ice_deinit_dev(pf);
}
@@ -609,11 +610,13 @@ exit_release_res:
* @devlink: pointer to the devlink instance
* @id: the parameter ID to set
* @ctx: context to store the parameter value
+ * @extack: netlink extended ACK structure
*
* Return: zero on success and negative value on failure.
*/
static int ice_devlink_tx_sched_layers_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
int err;
@@ -1231,11 +1234,13 @@ static void ice_set_min_max_msix(struct ice_pf *pf)
static int ice_devlink_reinit_up(struct ice_pf *pf)
{
struct ice_vsi *vsi = ice_get_main_vsi(pf);
+ struct device *dev = ice_pf_to_dev(pf);
+ bool need_dev_deinit = false;
int err;
err = ice_init_hw(&pf->hw);
if (err) {
- dev_err(ice_pf_to_dev(pf), "ice_init_hw failed: %d\n", err);
+ dev_err(dev, "ice_init_hw failed: %d\n", err);
return err;
}
@@ -1246,13 +1251,19 @@ static int ice_devlink_reinit_up(struct ice_pf *pf)
if (err)
goto unroll_hw_init;
+ err = ice_init_pf(pf);
+ if (err) {
+ dev_err(dev, "ice_init_pf failed: %d\n", err);
+ goto unroll_dev_init;
+ }
+
vsi->flags = ICE_VSI_FLAG_INIT;
rtnl_lock();
err = ice_vsi_cfg(vsi);
rtnl_unlock();
if (err)
- goto err_vsi_cfg;
+ goto unroll_pf_init;
/* No need to take devl_lock, it's already taken by devlink API */
err = ice_load(pf);
@@ -1265,10 +1276,14 @@ err_load:
rtnl_lock();
ice_vsi_decfg(vsi);
rtnl_unlock();
-err_vsi_cfg:
- ice_deinit_dev(pf);
+unroll_pf_init:
+ ice_deinit_pf(pf);
+unroll_dev_init:
+ need_dev_deinit = true;
unroll_hw_init:
ice_deinit_hw(&pf->hw);
+ if (need_dev_deinit)
+ ice_deinit_dev(pf);
return err;
}
@@ -1336,7 +1351,8 @@ static const struct devlink_ops ice_sf_devlink_ops;
static int
ice_devlink_enable_roce_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
struct iidc_rdma_core_dev_info *cdev;
@@ -1402,7 +1418,8 @@ ice_devlink_enable_roce_validate(struct devlink *devlink, u32 id,
static int
ice_devlink_enable_iw_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
struct iidc_rdma_core_dev_info *cdev;
@@ -1509,11 +1526,13 @@ static int ice_devlink_local_fwd_str_to_mode(const char *mode_str)
* @devlink: Pointer to the devlink instance.
* @id: The parameter ID to set.
* @ctx: Context to store the parameter value.
+ * @extack: netlink extended ACK structure
*
* Return: Zero.
*/
static int ice_devlink_local_fwd_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct ice_pf *pf = devlink_priv(devlink);
struct ice_port_info *pi;
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 22b8323ff0d0..147aaee192a7 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -351,6 +351,7 @@ struct ice_vsi {
u16 num_q_vectors;
/* tell if only dynamic irq allocation is allowed */
bool irq_dyn_alloc;
+ bool hsplit:1;
u16 vsi_num; /* HW (absolute) index of this VSI */
u16 idx; /* software index in pf->vsi[] */
@@ -374,6 +375,8 @@ struct ice_vsi {
spinlock_t arfs_lock; /* protects aRFS hash table and filter state */
atomic_t *arfs_last_fltr_id;
+ u16 max_frame;
+
struct ice_aqc_vsi_props info; /* VSI properties */
struct ice_vsi_vlan_info vlan_info; /* vlan config to be restored */
@@ -509,7 +512,6 @@ enum ice_pf_flags {
ICE_FLAG_MOD_POWER_UNSUPPORTED,
ICE_FLAG_PHY_FW_LOAD_FAILED,
ICE_FLAG_ETHTOOL_CTXT, /* set when ethtool holds RTNL lock */
- ICE_FLAG_LEGACY_RX,
ICE_FLAG_VF_TRUE_PROMISC_ENA,
ICE_FLAG_MDD_AUTO_RESET_VF,
ICE_FLAG_VF_VLAN_PRUNING,
@@ -1029,11 +1031,15 @@ int ice_open(struct net_device *netdev);
int ice_open_internal(struct net_device *netdev);
int ice_stop(struct net_device *netdev);
void ice_service_task_schedule(struct ice_pf *pf);
+void ice_start_service_task(struct ice_pf *pf);
int ice_load(struct ice_pf *pf);
void ice_unload(struct ice_pf *pf);
void ice_adv_lnk_speed_maps_init(void);
+void ice_init_dev_hw(struct ice_pf *pf);
int ice_init_dev(struct ice_pf *pf);
void ice_deinit_dev(struct ice_pf *pf);
+int ice_init_pf(struct ice_pf *pf);
+void ice_deinit_pf(struct ice_pf *pf);
int ice_change_mtu(struct net_device *netdev, int new_mtu);
void ice_tx_timeout(struct net_device *netdev, unsigned int txqueue);
int ice_xdp(struct net_device *dev, struct netdev_bpf *xdp);
diff --git a/drivers/net/ethernet/intel/ice/ice_base.c b/drivers/net/ethernet/intel/ice/ice_base.c
index 2d35a278c555..eadb1e3d12b3 100644
--- a/drivers/net/ethernet/intel/ice/ice_base.c
+++ b/drivers/net/ethernet/intel/ice/ice_base.c
@@ -2,6 +2,7 @@
/* Copyright (c) 2019, Intel Corporation. */
#include <net/xdp_sock_drv.h>
+#include <linux/net/intel/libie/rx.h>
#include "ice_base.h"
#include "ice_lib.h"
#include "ice_dcb_lib.h"
@@ -462,19 +463,6 @@ u16 ice_calc_ts_ring_count(struct ice_tx_ring *tx_ring)
}
/**
- * ice_rx_offset - Return expected offset into page to access data
- * @rx_ring: Ring we are requesting offset of
- *
- * Returns the offset value for ring into the data buffer.
- */
-static unsigned int ice_rx_offset(struct ice_rx_ring *rx_ring)
-{
- if (ice_ring_uses_build_skb(rx_ring))
- return ICE_SKB_PAD;
- return 0;
-}
-
-/**
* ice_setup_rx_ctx - Configure a receive ring context
* @ring: The Rx ring to configure
*
@@ -536,8 +524,29 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring)
else
rlan_ctx.l2tsel = 1;
- rlan_ctx.dtype = ICE_RX_DTYPE_NO_SPLIT;
- rlan_ctx.hsplit_0 = ICE_RLAN_RX_HSPLIT_0_NO_SPLIT;
+ if (ring->hdr_pp) {
+ rlan_ctx.hbuf = ring->rx_hdr_len >> ICE_RLAN_CTX_HBUF_S;
+ rlan_ctx.dtype = ICE_RX_DTYPE_HEADER_SPLIT;
+
+ /*
+ * If the frame is TCP/UDP/SCTP, it will be split by the
+ * payload.
+ * If not, but it's an IPv4/IPv6 frame, it will be split by
+ * the IP header.
+ * If not IP, it will be split by the Ethernet header.
+ *
+ * In any case, the header buffer will never be left empty.
+ */
+ rlan_ctx.hsplit_0 = ICE_RLAN_RX_HSPLIT_0_SPLIT_L2 |
+ ICE_RLAN_RX_HSPLIT_0_SPLIT_IP |
+ ICE_RLAN_RX_HSPLIT_0_SPLIT_TCP_UDP |
+ ICE_RLAN_RX_HSPLIT_0_SPLIT_SCTP;
+ } else {
+ rlan_ctx.hbuf = 0;
+ rlan_ctx.dtype = ICE_RX_DTYPE_NO_SPLIT;
+ rlan_ctx.hsplit_0 = ICE_RLAN_RX_HSPLIT_0_NO_SPLIT;
+ }
+
rlan_ctx.hsplit_1 = ICE_RLAN_RX_HSPLIT_1_NO_SPLIT;
/* This controls whether VLAN is stripped from inner headers
@@ -549,7 +558,7 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring)
/* Max packet size for this queue - must not be set to a larger value
* than 5 x DBUF
*/
- rlan_ctx.rxmax = min_t(u32, ring->max_frame,
+ rlan_ctx.rxmax = min_t(u32, vsi->max_frame,
ICE_MAX_CHAINED_RX_BUFS * ring->rx_buf_len);
/* Rx queue threshold in units of 64 */
@@ -586,14 +595,6 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring)
if (vsi->type == ICE_VSI_VF)
return 0;
- /* configure Rx buffer alignment */
- if (!vsi->netdev || test_bit(ICE_FLAG_LEGACY_RX, vsi->back->flags))
- ice_clear_ring_build_skb_ena(ring);
- else
- ice_set_ring_build_skb_ena(ring);
-
- ring->rx_offset = ice_rx_offset(ring);
-
/* init queue specific tail register */
ring->tail = hw->hw_addr + QRX_TAIL(pf_q);
writel(0, ring->tail);
@@ -601,36 +602,51 @@ static int ice_setup_rx_ctx(struct ice_rx_ring *ring)
return 0;
}
-static void ice_xsk_pool_fill_cb(struct ice_rx_ring *ring)
+static int ice_rxq_pp_create(struct ice_rx_ring *rq)
{
- void *ctx_ptr = &ring->pkt_ctx;
- struct xsk_cb_desc desc = {};
-
- XSK_CHECK_PRIV_TYPE(struct ice_xdp_buff);
- desc.src = &ctx_ptr;
- desc.off = offsetof(struct ice_xdp_buff, pkt_ctx) -
- sizeof(struct xdp_buff);
- desc.bytes = sizeof(ctx_ptr);
- xsk_pool_fill_cb(ring->xsk_pool, &desc);
-}
+ struct libeth_fq fq = {
+ .count = rq->count,
+ .nid = NUMA_NO_NODE,
+ .hsplit = rq->vsi->hsplit,
+ .xdp = ice_is_xdp_ena_vsi(rq->vsi),
+ .buf_len = LIBIE_MAX_RX_BUF_LEN,
+ };
+ int err;
-/**
- * ice_get_frame_sz - calculate xdp_buff::frame_sz
- * @rx_ring: the ring being configured
- *
- * Return frame size based on underlying PAGE_SIZE
- */
-static unsigned int ice_get_frame_sz(struct ice_rx_ring *rx_ring)
-{
- unsigned int frame_sz;
+ err = libeth_rx_fq_create(&fq, &rq->q_vector->napi);
+ if (err)
+ return err;
+
+ rq->pp = fq.pp;
+ rq->rx_fqes = fq.fqes;
+ rq->truesize = fq.truesize;
+ rq->rx_buf_len = fq.buf_len;
-#if (PAGE_SIZE >= 8192)
- frame_sz = rx_ring->rx_buf_len;
-#else
- frame_sz = ice_rx_pg_size(rx_ring) / 2;
-#endif
+ if (!fq.hsplit)
+ return 0;
+
+ fq = (struct libeth_fq){
+ .count = rq->count,
+ .type = LIBETH_FQE_HDR,
+ .nid = NUMA_NO_NODE,
+ .xdp = ice_is_xdp_ena_vsi(rq->vsi),
+ };
- return frame_sz;
+ err = libeth_rx_fq_create(&fq, &rq->q_vector->napi);
+ if (err)
+ goto destroy;
+
+ rq->hdr_pp = fq.pp;
+ rq->hdr_fqes = fq.fqes;
+ rq->hdr_truesize = fq.truesize;
+ rq->rx_hdr_len = fq.buf_len;
+
+ return 0;
+
+destroy:
+ ice_rxq_pp_destroy(rq);
+
+ return err;
}
/**
@@ -642,7 +658,8 @@ static unsigned int ice_get_frame_sz(struct ice_rx_ring *rx_ring)
static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
{
struct device *dev = ice_pf_to_dev(ring->vsi->back);
- u32 num_bufs = ICE_RX_DESC_UNUSED(ring);
+ u32 num_bufs = ICE_DESC_UNUSED(ring);
+ u32 rx_buf_len;
int err;
if (ring->vsi->type == ICE_VSI_PF || ring->vsi->type == ICE_VSI_SF) {
@@ -656,15 +673,19 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
}
ice_rx_xsk_pool(ring);
+ err = ice_realloc_rx_xdp_bufs(ring, ring->xsk_pool);
+ if (err)
+ return err;
+
if (ring->xsk_pool) {
xdp_rxq_info_unreg(&ring->xdp_rxq);
- ring->rx_buf_len =
+ rx_buf_len =
xsk_pool_get_rx_frame_size(ring->xsk_pool);
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->q_index,
ring->q_vector->napi.napi_id,
- ring->rx_buf_len);
+ rx_buf_len);
if (err)
return err;
err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
@@ -673,36 +694,33 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
if (err)
return err;
xsk_pool_set_rxq_info(ring->xsk_pool, &ring->xdp_rxq);
- ice_xsk_pool_fill_cb(ring);
dev_info(dev, "Registered XDP mem model MEM_TYPE_XSK_BUFF_POOL on Rx ring %d\n",
ring->q_index);
} else {
+ err = ice_rxq_pp_create(ring);
+ if (err)
+ return err;
+
if (!xdp_rxq_info_is_reg(&ring->xdp_rxq)) {
err = __xdp_rxq_info_reg(&ring->xdp_rxq, ring->netdev,
ring->q_index,
ring->q_vector->napi.napi_id,
ring->rx_buf_len);
if (err)
- return err;
+ goto err_destroy_fq;
}
-
- err = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
- MEM_TYPE_PAGE_SHARED,
- NULL);
- if (err)
- return err;
+ xdp_rxq_info_attach_page_pool(&ring->xdp_rxq,
+ ring->pp);
}
}
- xdp_init_buff(&ring->xdp, ice_get_frame_sz(ring), &ring->xdp_rxq);
ring->xdp.data = NULL;
- ring->xdp_ext.pkt_ctx = &ring->pkt_ctx;
err = ice_setup_rx_ctx(ring);
if (err) {
dev_err(dev, "ice_setup_rx_ctx failed for RxQ %d, err %d\n",
ring->q_index, err);
- return err;
+ goto err_destroy_fq;
}
if (ring->xsk_pool) {
@@ -730,9 +748,17 @@ static int ice_vsi_cfg_rxq(struct ice_rx_ring *ring)
if (ring->vsi->type == ICE_VSI_CTRL)
ice_init_ctrl_rx_descs(ring, num_bufs);
else
- ice_alloc_rx_bufs(ring, num_bufs);
+ err = ice_alloc_rx_bufs(ring, num_bufs);
+
+ if (err)
+ goto err_destroy_fq;
return 0;
+
+err_destroy_fq:
+ ice_rxq_pp_destroy(ring);
+
+ return err;
}
int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx)
@@ -753,18 +779,10 @@ int ice_vsi_cfg_single_rxq(struct ice_vsi *vsi, u16 q_idx)
*/
static void ice_vsi_cfg_frame_size(struct ice_vsi *vsi, struct ice_rx_ring *ring)
{
- if (!vsi->netdev || test_bit(ICE_FLAG_LEGACY_RX, vsi->back->flags)) {
- ring->max_frame = ICE_MAX_FRAME_LEGACY_RX;
- ring->rx_buf_len = ICE_RXBUF_1664;
-#if (PAGE_SIZE < 8192)
- } else if (!ICE_2K_TOO_SMALL_WITH_PADDING &&
- (vsi->netdev->mtu <= ETH_DATA_LEN)) {
- ring->max_frame = ICE_RXBUF_1536 - NET_IP_ALIGN;
- ring->rx_buf_len = ICE_RXBUF_1536 - NET_IP_ALIGN;
-#endif
+ if (!vsi->netdev) {
+ vsi->max_frame = ICE_MAX_FRAME_LEGACY_RX;
} else {
- ring->max_frame = ICE_AQ_SET_MAC_FRAME_SIZE_MAX;
- ring->rx_buf_len = ICE_RXBUF_3072;
+ vsi->max_frame = ICE_AQ_SET_MAC_FRAME_SIZE_MAX;
}
}
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index 2250426ec91b..046bc9c65c51 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -1161,6 +1161,9 @@ int ice_init_hw(struct ice_hw *hw)
status = ice_init_hw_tbls(hw);
if (status)
goto err_unroll_fltr_mgmt_struct;
+
+ ice_init_dev_hw(hw->back);
+
mutex_init(&hw->tnl_lock);
ice_init_chk_recipe_reuse_support(hw);
@@ -3389,6 +3392,7 @@ bool ice_is_100m_speed_supported(struct ice_hw *hw)
case ICE_DEV_ID_E822L_SGMII:
case ICE_DEV_ID_E823L_1GBE:
case ICE_DEV_ID_E823C_SGMII:
+ case ICE_DEV_ID_E825C_SGMII:
return true;
default:
return false;
@@ -4382,6 +4386,15 @@ int ice_get_phy_lane_number(struct ice_hw *hw)
unsigned int lane;
int err;
+ /* E82X does not have sequential IDs, lane number is PF ID.
+ * For E825 device, the exception is the variant with external
+ * PHY (0x579F), in which there is also 1:1 pf_id -> lane_number
+ * mapping.
+ */
+ if (hw->mac_type == ICE_MAC_GENERIC ||
+ hw->device_id == ICE_DEV_ID_E825C_SGMII)
+ return hw->pf_id;
+
options = kcalloc(ICE_AQC_PORT_OPT_MAX, sizeof(*options), GFP_KERNEL);
if (!options)
return -ENOMEM;
@@ -6497,6 +6510,28 @@ u32 ice_get_link_speed(u16 index)
}
/**
+ * ice_get_dest_cgu - get destination CGU dev for given HW
+ * @hw: pointer to the HW struct
+ *
+ * Get CGU client id for CGU register read/write operations.
+ *
+ * Return: CGU device id to use in SBQ transactions.
+ */
+static enum ice_sbq_dev_id ice_get_dest_cgu(struct ice_hw *hw)
+{
+ /* On dual complex E825 only complex 0 has functional CGU powering all
+ * the PHYs.
+ * SBQ destination device cgu points to CGU on a current complex and to
+ * access primary CGU from the secondary complex, the driver should use
+ * cgu_peer as a destination device.
+ */
+ if (hw->mac_type == ICE_MAC_GENERIC_3K_E825 && ice_is_dual(hw) &&
+ !ice_is_primary(hw))
+ return ice_sbq_dev_cgu_peer;
+ return ice_sbq_dev_cgu;
+}
+
+/**
* ice_read_cgu_reg - Read a CGU register
* @hw: Pointer to the HW struct
* @addr: Register address to read
@@ -6510,8 +6545,8 @@ u32 ice_get_link_speed(u16 index)
int ice_read_cgu_reg(struct ice_hw *hw, u32 addr, u32 *val)
{
struct ice_sbq_msg_input cgu_msg = {
+ .dest_dev = ice_get_dest_cgu(hw),
.opcode = ice_sbq_msg_rd,
- .dest_dev = ice_sbq_dev_cgu,
.msg_addr_low = addr
};
int err;
@@ -6542,8 +6577,8 @@ int ice_read_cgu_reg(struct ice_hw *hw, u32 addr, u32 *val)
int ice_write_cgu_reg(struct ice_hw *hw, u32 addr, u32 val)
{
struct ice_sbq_msg_input cgu_msg = {
+ .dest_dev = ice_get_dest_cgu(hw),
.opcode = ice_sbq_msg_wr,
- .dest_dev = ice_sbq_dev_cgu,
.msg_addr_low = addr,
.data = val
};
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index dc131779d426..969d4f8f9c02 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -10,6 +10,7 @@
#include "ice_lib.h"
#include "ice_dcb_lib.h"
#include <net/dcbnl.h>
+#include <net/libeth/rx.h>
struct ice_stats {
char stat_string[ETH_GSTRING_LEN];
@@ -340,7 +341,6 @@ static const struct ice_priv_flag ice_gstrings_priv_flags[] = {
ICE_FLAG_VF_TRUE_PROMISC_ENA),
ICE_PRIV_FLAG("mdd-auto-reset-vf", ICE_FLAG_MDD_AUTO_RESET_VF),
ICE_PRIV_FLAG("vf-vlan-pruning", ICE_FLAG_VF_VLAN_PRUNING),
- ICE_PRIV_FLAG("legacy-rx", ICE_FLAG_LEGACY_RX),
};
#define ICE_PRIV_FLAG_ARRAY_SIZE ARRAY_SIZE(ice_gstrings_priv_flags)
@@ -794,8 +794,7 @@ static int ice_get_extended_regs(struct net_device *netdev, void *p)
static void
ice_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_hw *hw = &pf->hw;
u32 *regs_buf = (u32 *)p;
unsigned int i;
@@ -810,8 +809,7 @@ ice_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p)
static u32 ice_get_msglevel(struct net_device *netdev)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
#ifndef CONFIG_DYNAMIC_DEBUG
if (pf->hw.debug_mask)
@@ -824,8 +822,7 @@ static u32 ice_get_msglevel(struct net_device *netdev)
static void ice_set_msglevel(struct net_device *netdev, u32 data)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
#ifndef CONFIG_DYNAMIC_DEBUG
if (ICE_DBG_USER & data)
@@ -840,16 +837,14 @@ static void ice_set_msglevel(struct net_device *netdev, u32 data)
static void ice_get_link_ext_stats(struct net_device *netdev,
struct ethtool_link_ext_stats *stats)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
stats->link_down_events = pf->link_down_events;
}
static int ice_get_eeprom_len(struct net_device *netdev)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
return (int)pf->hw.flash.flash_size;
}
@@ -858,9 +853,7 @@ static int
ice_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
u8 *bytes)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_vsi *vsi = np->vsi;
- struct ice_pf *pf = vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_hw *hw = &pf->hw;
struct device *dev;
int ret;
@@ -959,8 +952,7 @@ static u64 ice_link_test(struct net_device *netdev)
*/
static u64 ice_eeprom_test(struct net_device *netdev)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
netdev_info(netdev, "EEPROM test\n");
return !!(ice_nvm_validate_checksum(&pf->hw));
@@ -1239,8 +1231,9 @@ static int ice_diag_send(struct ice_tx_ring *tx_ring, u8 *data, u16 size)
*/
static int ice_lbtest_receive_frames(struct ice_rx_ring *rx_ring)
{
- struct ice_rx_buf *rx_buf;
+ struct libeth_fqe *rx_buf;
int valid_frames, i;
+ struct page *page;
u8 *received_buf;
valid_frames = 0;
@@ -1255,8 +1248,10 @@ static int ice_lbtest_receive_frames(struct ice_rx_ring *rx_ring)
cpu_to_le16(BIT(ICE_RX_FLEX_DESC_STATUS0_EOF_S)))))
continue;
- rx_buf = &rx_ring->rx_buf[i];
- received_buf = page_address(rx_buf->page) + rx_buf->page_offset;
+ rx_buf = &rx_ring->rx_fqes[i];
+ page = __netmem_to_page(rx_buf->netmem);
+ received_buf = page_address(page) + rx_buf->offset +
+ page->pp->p.offset;
if (ice_lbtest_check_frame(received_buf))
valid_frames++;
@@ -1274,9 +1269,8 @@ static int ice_lbtest_receive_frames(struct ice_rx_ring *rx_ring)
*/
static u64 ice_loopback_test(struct net_device *netdev)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_vsi *orig_vsi = np->vsi, *test_vsi;
- struct ice_pf *pf = orig_vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
+ struct ice_vsi *test_vsi;
u8 *tx_frame __free(kfree) = NULL;
u8 broadcast[ETH_ALEN], ret = 0;
int num_frames, valid_frames;
@@ -1365,8 +1359,7 @@ lbtest_vsi_close:
*/
static u64 ice_intr_test(struct net_device *netdev)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
u16 swic_old = pf->sw_int_count;
netdev_info(netdev, "interrupt test\n");
@@ -1394,9 +1387,8 @@ static void
ice_self_test(struct net_device *netdev, struct ethtool_test *eth_test,
u64 *data)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
bool if_running = netif_running(netdev);
- struct ice_pf *pf = np->vsi->back;
struct device *dev;
dev = ice_pf_to_dev(pf);
@@ -1720,9 +1712,7 @@ static int ice_nway_reset(struct net_device *netdev)
*/
static u32 ice_get_priv_flags(struct net_device *netdev)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_vsi *vsi = np->vsi;
- struct ice_pf *pf = vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
u32 i, ret_flags = 0;
for (i = 0; i < ICE_PRIV_FLAG_ARRAY_SIZE; i++) {
@@ -1869,10 +1859,6 @@ static int ice_set_priv_flags(struct net_device *netdev, u32 flags)
ice_nway_reset(netdev);
}
}
- if (test_bit(ICE_FLAG_LEGACY_RX, change_flags)) {
- /* down and up VSI so that changes of Rx cfg are reflected. */
- ice_down_up(vsi);
- }
/* don't allow modification of this flag when a single VF is in
* promiscuous mode because it's not supported
*/
@@ -3098,6 +3084,20 @@ static int ice_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
}
/**
+ * ice_get_rx_ring_count - get RX ring count
+ * @netdev: network interface device structure
+ *
+ * Return: number of RX rings.
+ */
+static u32 ice_get_rx_ring_count(struct net_device *netdev)
+{
+ struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_vsi *vsi = np->vsi;
+
+ return vsi->rss_size;
+}
+
+/**
* ice_get_rxnfc - command to get Rx flow classification rules
* @netdev: network interface device structure
* @cmd: ethtool rxnfc command
@@ -3117,10 +3117,6 @@ ice_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
hw = &vsi->back->hw;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = vsi->rss_size;
- ret = 0;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = hw->fdir_active_fltr;
/* report total rule count */
@@ -3165,6 +3161,10 @@ ice_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
ring->rx_jumbo_max_pending = 0;
ring->rx_mini_pending = 0;
ring->rx_jumbo_pending = 0;
+
+ kernel_ring->tcp_data_split = vsi->hsplit ?
+ ETHTOOL_TCP_DATA_SPLIT_ENABLED :
+ ETHTOOL_TCP_DATA_SPLIT_DISABLED;
}
static int
@@ -3181,6 +3181,7 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
int i, timeout = 50, err = 0;
struct ice_hw *hw = &pf->hw;
u16 new_rx_cnt, new_tx_cnt;
+ bool hsplit;
if (ring->tx_pending > ICE_MAX_NUM_DESC_BY_MAC(hw) ||
ring->tx_pending < ICE_MIN_NUM_DESC ||
@@ -3206,9 +3207,12 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
netdev_info(netdev, "Requested Rx descriptor count rounded up to %d\n",
new_rx_cnt);
+ hsplit = kernel_ring->tcp_data_split == ETHTOOL_TCP_DATA_SPLIT_ENABLED;
+
/* if nothing to do return success */
if (new_tx_cnt == vsi->tx_rings[0]->count &&
- new_rx_cnt == vsi->rx_rings[0]->count) {
+ new_rx_cnt == vsi->rx_rings[0]->count &&
+ hsplit == vsi->hsplit) {
netdev_dbg(netdev, "Nothing to change, descriptor count is same as requested\n");
return 0;
}
@@ -3238,6 +3242,8 @@ ice_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring,
vsi->xdp_rings[i]->count = new_tx_cnt;
vsi->num_tx_desc = (u16)new_tx_cnt;
vsi->num_rx_desc = (u16)new_rx_cnt;
+ vsi->hsplit = hsplit;
+
netdev_dbg(netdev, "Link is down, descriptor count change happens when link is brought up\n");
goto done;
}
@@ -3321,7 +3327,8 @@ process_rx:
rx_rings[i].count = new_rx_cnt;
rx_rings[i].cached_phctime = pf->ptp.cached_phc_time;
rx_rings[i].desc = NULL;
- rx_rings[i].rx_buf = NULL;
+ rx_rings[i].xdp_buf = NULL;
+
/* this is to allow wr32 to have something to write to
* during early allocation of Rx buffers
*/
@@ -3330,10 +3337,6 @@ process_rx:
err = ice_setup_rx_ring(&rx_rings[i]);
if (err)
goto rx_unwind;
-
- /* allocate Rx buffers */
- err = ice_alloc_rx_bufs(&rx_rings[i],
- ICE_RX_DESC_UNUSED(&rx_rings[i]));
rx_unwind:
if (err) {
while (i) {
@@ -3347,6 +3350,8 @@ rx_unwind:
}
process_link:
+ vsi->hsplit = hsplit;
+
/* Bring interface down, copy in the new ring info, then restore the
* interface. if VSI is up, bring it down and then back up
*/
@@ -4417,9 +4422,7 @@ static int
ice_get_module_info(struct net_device *netdev,
struct ethtool_modinfo *modinfo)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_vsi *vsi = np->vsi;
- struct ice_pf *pf = vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_hw *hw = &pf->hw;
u8 sff8472_comp = 0;
u8 sff8472_swap = 0;
@@ -4491,12 +4494,10 @@ static int
ice_get_module_eeprom(struct net_device *netdev,
struct ethtool_eeprom *ee, u8 *data)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
#define SFF_READ_BLOCK_SIZE 8
u8 value[SFF_READ_BLOCK_SIZE] = { 0 };
u8 addr = ICE_I2C_EEPROM_DEV_ADDR;
- struct ice_vsi *vsi = np->vsi;
- struct ice_pf *pf = vsi->back;
struct ice_hw *hw = &pf->hw;
bool is_sfp = false;
unsigned int i, j;
@@ -4661,6 +4662,98 @@ static void ice_get_fec_stats(struct net_device *netdev,
pi->lport, err);
}
+static void ice_get_eth_mac_stats(struct net_device *netdev,
+ struct ethtool_eth_mac_stats *mac_stats)
+{
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
+ struct ice_hw_port_stats *ps = &pf->stats;
+
+ mac_stats->FramesTransmittedOK = ps->eth.tx_unicast +
+ ps->eth.tx_multicast +
+ ps->eth.tx_broadcast;
+ mac_stats->FramesReceivedOK = ps->eth.rx_unicast +
+ ps->eth.rx_multicast +
+ ps->eth.rx_broadcast;
+ mac_stats->FrameCheckSequenceErrors = ps->crc_errors;
+ mac_stats->OctetsTransmittedOK = ps->eth.tx_bytes;
+ mac_stats->OctetsReceivedOK = ps->eth.rx_bytes;
+ mac_stats->MulticastFramesXmittedOK = ps->eth.tx_multicast;
+ mac_stats->BroadcastFramesXmittedOK = ps->eth.tx_broadcast;
+ mac_stats->MulticastFramesReceivedOK = ps->eth.rx_multicast;
+ mac_stats->BroadcastFramesReceivedOK = ps->eth.rx_broadcast;
+ mac_stats->InRangeLengthErrors = ps->rx_len_errors;
+ mac_stats->FrameTooLongErrors = ps->rx_oversize;
+}
+
+static void ice_get_pause_stats(struct net_device *netdev,
+ struct ethtool_pause_stats *pause_stats)
+{
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
+ struct ice_hw_port_stats *ps = &pf->stats;
+
+ pause_stats->tx_pause_frames = ps->link_xon_tx + ps->link_xoff_tx;
+ pause_stats->rx_pause_frames = ps->link_xon_rx + ps->link_xoff_rx;
+}
+
+static const struct ethtool_rmon_hist_range ice_rmon_ranges[] = {
+ { 0, 64 },
+ { 65, 127 },
+ { 128, 255 },
+ { 256, 511 },
+ { 512, 1023 },
+ { 1024, 1522 },
+ { 1523, 9522 },
+ {}
+};
+
+static void ice_get_rmon_stats(struct net_device *netdev,
+ struct ethtool_rmon_stats *rmon,
+ const struct ethtool_rmon_hist_range **ranges)
+{
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
+ struct ice_hw_port_stats *ps = &pf->stats;
+
+ rmon->undersize_pkts = ps->rx_undersize;
+ rmon->oversize_pkts = ps->rx_oversize;
+ rmon->fragments = ps->rx_fragments;
+ rmon->jabbers = ps->rx_jabber;
+
+ rmon->hist[0] = ps->rx_size_64;
+ rmon->hist[1] = ps->rx_size_127;
+ rmon->hist[2] = ps->rx_size_255;
+ rmon->hist[3] = ps->rx_size_511;
+ rmon->hist[4] = ps->rx_size_1023;
+ rmon->hist[5] = ps->rx_size_1522;
+ rmon->hist[6] = ps->rx_size_big;
+
+ rmon->hist_tx[0] = ps->tx_size_64;
+ rmon->hist_tx[1] = ps->tx_size_127;
+ rmon->hist_tx[2] = ps->tx_size_255;
+ rmon->hist_tx[3] = ps->tx_size_511;
+ rmon->hist_tx[4] = ps->tx_size_1023;
+ rmon->hist_tx[5] = ps->tx_size_1522;
+ rmon->hist_tx[6] = ps->tx_size_big;
+
+ *ranges = ice_rmon_ranges;
+}
+
+/* ice_get_ts_stats - provide timestamping stats
+ * @netdev: the netdevice pointer from ethtool
+ * @ts_stats: the ethtool data structure to fill in
+ */
+static void ice_get_ts_stats(struct net_device *netdev,
+ struct ethtool_ts_stats *ts_stats)
+{
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
+ struct ice_ptp *ptp = &pf->ptp;
+
+ ts_stats->pkts = ptp->tx_hwtstamp_good;
+ ts_stats->err = ptp->tx_hwtstamp_skipped +
+ ptp->tx_hwtstamp_flushed +
+ ptp->tx_hwtstamp_discarded;
+ ts_stats->lost = ptp->tx_hwtstamp_timeouts;
+}
+
#define ICE_ETHTOOL_PFR (ETH_RESET_IRQ | ETH_RESET_DMA | \
ETH_RESET_FILTER | ETH_RESET_OFFLOAD)
@@ -4682,8 +4775,7 @@ static void ice_get_fec_stats(struct net_device *netdev,
*/
static int ice_ethtool_reset(struct net_device *dev, u32 *flags)
{
- struct ice_netdev_priv *np = netdev_priv(dev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(dev);
enum ice_reset_req reset;
switch (*flags) {
@@ -4741,9 +4833,14 @@ static const struct ethtool_ops ice_ethtool_ops = {
ETHTOOL_COALESCE_USE_ADAPTIVE |
ETHTOOL_COALESCE_RX_USECS_HIGH,
.supported_input_xfrm = RXH_XFRM_SYM_XOR,
+ .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT,
.get_link_ksettings = ice_get_link_ksettings,
.set_link_ksettings = ice_set_link_ksettings,
.get_fec_stats = ice_get_fec_stats,
+ .get_eth_mac_stats = ice_get_eth_mac_stats,
+ .get_pause_stats = ice_get_pause_stats,
+ .get_rmon_stats = ice_get_rmon_stats,
+ .get_ts_stats = ice_get_ts_stats,
.get_drvinfo = ice_get_drvinfo,
.get_regs_len = ice_get_regs_len,
.get_regs = ice_get_regs,
@@ -4766,6 +4863,7 @@ static const struct ethtool_ops ice_ethtool_ops = {
.get_sset_count = ice_get_sset_count,
.get_rxnfc = ice_get_rxnfc,
.set_rxnfc = ice_set_rxnfc,
+ .get_rx_ring_count = ice_get_rx_ring_count,
.get_ringparam = ice_get_ringparam,
.set_ringparam = ice_set_ringparam,
.nway_reset = ice_nway_reset,
diff --git a/drivers/net/ethernet/intel/ice/ice_fdir.c b/drivers/net/ethernet/intel/ice/ice_fdir.c
index 26b357c0ae15..b29fbdec9442 100644
--- a/drivers/net/ethernet/intel/ice/ice_fdir.c
+++ b/drivers/net/ethernet/intel/ice/ice_fdir.c
@@ -1121,7 +1121,7 @@ ice_fdir_get_gen_prgm_pkt(struct ice_hw *hw, struct ice_fdir_fltr *input,
* ice_fdir_has_frag - does flow type have 2 ptypes
* @flow: flow ptype
*
- * returns true is there is a fragment packet for this ptype
+ * Return: true if there is a fragment packet for this ptype
*/
bool ice_fdir_has_frag(enum ice_fltr_ptype flow)
{
diff --git a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
index 363ae79a3620..c0dbec369366 100644
--- a/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
+++ b/drivers/net/ethernet/intel/ice/ice_flex_pipe.c
@@ -574,9 +574,7 @@ ice_destroy_tunnel_end:
int ice_udp_tunnel_set_port(struct net_device *netdev, unsigned int table,
unsigned int idx, struct udp_tunnel_info *ti)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_vsi *vsi = np->vsi;
- struct ice_pf *pf = vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
enum ice_tunnel_type tnl_type;
int status;
u16 index;
@@ -598,9 +596,7 @@ int ice_udp_tunnel_set_port(struct net_device *netdev, unsigned int table,
int ice_udp_tunnel_unset_port(struct net_device *netdev, unsigned int table,
unsigned int idx, struct udp_tunnel_info *ti)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_vsi *vsi = np->vsi;
- struct ice_pf *pf = vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
enum ice_tunnel_type tnl_type;
int status;
@@ -1479,7 +1475,7 @@ static void ice_init_prof_masks(struct ice_hw *hw, enum ice_block blk)
per_pf = ICE_PROF_MASK_COUNT / hw->dev_caps.num_funcs;
hw->blk[blk].masks.count = per_pf;
- hw->blk[blk].masks.first = hw->pf_id * per_pf;
+ hw->blk[blk].masks.first = hw->logical_pf_id * per_pf;
memset(hw->blk[blk].masks.masks, 0, sizeof(hw->blk[blk].masks.masks));
@@ -3582,6 +3578,19 @@ ice_move_vsi(struct ice_hw *hw, enum ice_block blk, u16 vsi, u16 vsig,
}
/**
+ * ice_set_tcam_flags - set TCAM flag don't care mask
+ * @mask: mask for flags
+ * @dc_mask: pointer to the don't care mask
+ */
+static void ice_set_tcam_flags(u16 mask, u8 dc_mask[ICE_TCAM_KEY_VAL_SZ])
+{
+ u16 inverted_mask = ~mask;
+
+ /* flags are lowest u16 */
+ put_unaligned_le16(inverted_mask, dc_mask);
+}
+
+/**
* ice_rem_chg_tcam_ent - remove a specific TCAM entry from change list
* @hw: pointer to the HW struct
* @idx: the index of the TCAM entry to remove
@@ -3651,6 +3660,9 @@ ice_prof_tcam_ena_dis(struct ice_hw *hw, enum ice_block blk, bool enable,
if (!p)
return -ENOMEM;
+ /* set don't care masks for TCAM flags */
+ ice_set_tcam_flags(tcam->attr.mask, dc_msk);
+
status = ice_tcam_write_entry(hw, blk, tcam->tcam_idx, tcam->prof_id,
tcam->ptg, vsig, 0, tcam->attr.flags,
vl_msk, dc_msk, nm_msk);
@@ -3677,6 +3689,34 @@ err_ice_prof_tcam_ena_dis:
}
/**
+ * ice_ptg_attr_in_use - determine if PTG and attribute pair is in use
+ * @ptg_attr: pointer to the PTG and attribute pair to check
+ * @ptgs_used: bitmap that denotes which PTGs are in use
+ * @attr_used: array of PTG and attributes pairs already used
+ * @attr_cnt: count of entries in the attr_used array
+ *
+ * Return: true if the PTG and attribute pair is in use, false otherwise.
+ */
+static bool
+ice_ptg_attr_in_use(struct ice_tcam_inf *ptg_attr, unsigned long *ptgs_used,
+ struct ice_tcam_inf *attr_used[], u16 attr_cnt)
+{
+ u16 i;
+
+ if (!test_bit(ptg_attr->ptg, ptgs_used))
+ return false;
+
+ /* the PTG is used, so now look for correct attributes */
+ for (i = 0; i < attr_cnt; i++)
+ if (attr_used[i]->ptg == ptg_attr->ptg &&
+ attr_used[i]->attr.flags == ptg_attr->attr.flags &&
+ attr_used[i]->attr.mask == ptg_attr->attr.mask)
+ return true;
+
+ return false;
+}
+
+/**
* ice_adj_prof_priorities - adjust profile based on priorities
* @hw: pointer to the HW struct
* @blk: hardware block
@@ -3688,10 +3728,16 @@ ice_adj_prof_priorities(struct ice_hw *hw, enum ice_block blk, u16 vsig,
struct list_head *chg)
{
DECLARE_BITMAP(ptgs_used, ICE_XLT1_CNT);
+ struct ice_tcam_inf **attr_used;
struct ice_vsig_prof *t;
- int status;
+ u16 attr_used_cnt = 0;
+ int status = 0;
u16 idx;
+ attr_used = kcalloc(ICE_MAX_PTG_ATTRS, sizeof(*attr_used), GFP_KERNEL);
+ if (!attr_used)
+ return -ENOMEM;
+
bitmap_zero(ptgs_used, ICE_XLT1_CNT);
idx = vsig & ICE_VSIG_IDX_M;
@@ -3709,11 +3755,15 @@ ice_adj_prof_priorities(struct ice_hw *hw, enum ice_block blk, u16 vsig,
u16 i;
for (i = 0; i < t->tcam_count; i++) {
+ bool used;
+
/* Scan the priorities from newest to oldest.
* Make sure that the newest profiles take priority.
*/
- if (test_bit(t->tcam[i].ptg, ptgs_used) &&
- t->tcam[i].in_use) {
+ used = ice_ptg_attr_in_use(&t->tcam[i], ptgs_used,
+ attr_used, attr_used_cnt);
+
+ if (used && t->tcam[i].in_use) {
/* need to mark this PTG as never match, as it
* was already in use and therefore duplicate
* (and lower priority)
@@ -3723,9 +3773,8 @@ ice_adj_prof_priorities(struct ice_hw *hw, enum ice_block blk, u16 vsig,
&t->tcam[i],
chg);
if (status)
- return status;
- } else if (!test_bit(t->tcam[i].ptg, ptgs_used) &&
- !t->tcam[i].in_use) {
+ goto free_attr_used;
+ } else if (!used && !t->tcam[i].in_use) {
/* need to enable this PTG, as it in not in use
* and not enabled (highest priority)
*/
@@ -3734,15 +3783,21 @@ ice_adj_prof_priorities(struct ice_hw *hw, enum ice_block blk, u16 vsig,
&t->tcam[i],
chg);
if (status)
- return status;
+ goto free_attr_used;
}
/* keep track of used ptgs */
- __set_bit(t->tcam[i].ptg, ptgs_used);
+ set_bit(t->tcam[i].ptg, ptgs_used);
+ if (attr_used_cnt < ICE_MAX_PTG_ATTRS)
+ attr_used[attr_used_cnt++] = &t->tcam[i];
+ else
+ ice_debug(hw, ICE_DBG_INIT, "Warn: ICE_MAX_PTG_ATTRS exceeded\n");
}
}
- return 0;
+free_attr_used:
+ kfree(attr_used);
+ return status;
}
/**
@@ -3825,11 +3880,15 @@ ice_add_prof_id_vsig(struct ice_hw *hw, enum ice_block blk, u16 vsig, u64 hdl,
p->vsig = vsig;
p->tcam_idx = t->tcam[i].tcam_idx;
+ /* set don't care masks for TCAM flags */
+ ice_set_tcam_flags(t->tcam[i].attr.mask, dc_msk);
+
/* write the TCAM entry */
status = ice_tcam_write_entry(hw, blk, t->tcam[i].tcam_idx,
t->tcam[i].prof_id,
- t->tcam[i].ptg, vsig, 0, 0,
- vl_msk, dc_msk, nm_msk);
+ t->tcam[i].ptg, vsig, 0,
+ t->tcam[i].attr.flags, vl_msk,
+ dc_msk, nm_msk);
if (status) {
devm_kfree(ice_hw_to_dev(hw), p);
goto err_ice_add_prof_id_vsig;
@@ -4143,9 +4202,6 @@ ice_flow_assoc_fdir_prof(struct ice_hw *hw, enum ice_block blk,
u16 vsi_num;
int status;
- if (blk != ICE_BLK_FD)
- return -EINVAL;
-
vsi_num = ice_get_hw_vsi_num(hw, dest_vsi);
status = ice_add_prof_id_flow(hw, blk, vsi_num, hdl);
if (status) {
@@ -4154,6 +4210,9 @@ ice_flow_assoc_fdir_prof(struct ice_hw *hw, enum ice_block blk,
return status;
}
+ if (blk != ICE_BLK_FD)
+ return 0;
+
vsi_num = ice_get_hw_vsi_num(hw, fdir_vsi);
status = ice_add_prof_id_flow(hw, blk, vsi_num, hdl);
if (status) {
diff --git a/drivers/net/ethernet/intel/ice/ice_flex_type.h b/drivers/net/ethernet/intel/ice/ice_flex_type.h
index 817beca591e0..80c9e7c749c2 100644
--- a/drivers/net/ethernet/intel/ice/ice_flex_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_flex_type.h
@@ -187,6 +187,7 @@ struct ice_prof_map {
};
#define ICE_INVALID_TCAM 0xFFFF
+#define ICE_MAX_PTG_ATTRS 1024
struct ice_tcam_inf {
u16 tcam_idx;
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.c b/drivers/net/ethernet/intel/ice/ice_flow.c
index 6d5c939dc8a5..c9b6d0a84bd1 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.c
+++ b/drivers/net/ethernet/intel/ice/ice_flow.c
@@ -5,6 +5,38 @@
#include "ice_flow.h"
#include <net/gre.h>
+/* Size of known protocol header fields */
+#define ICE_FLOW_FLD_SZ_ETH_TYPE 2
+#define ICE_FLOW_FLD_SZ_VLAN 2
+#define ICE_FLOW_FLD_SZ_IPV4_ADDR 4
+#define ICE_FLOW_FLD_SZ_IPV6_ADDR 16
+#define ICE_FLOW_FLD_SZ_IPV6_PRE32_ADDR 4
+#define ICE_FLOW_FLD_SZ_IPV6_PRE48_ADDR 6
+#define ICE_FLOW_FLD_SZ_IPV6_PRE64_ADDR 8
+#define ICE_FLOW_FLD_SZ_IPV4_ID 2
+#define ICE_FLOW_FLD_SZ_IPV6_ID 4
+#define ICE_FLOW_FLD_SZ_IP_CHKSUM 2
+#define ICE_FLOW_FLD_SZ_TCP_CHKSUM 2
+#define ICE_FLOW_FLD_SZ_UDP_CHKSUM 2
+#define ICE_FLOW_FLD_SZ_SCTP_CHKSUM 4
+#define ICE_FLOW_FLD_SZ_IP_DSCP 1
+#define ICE_FLOW_FLD_SZ_IP_TTL 1
+#define ICE_FLOW_FLD_SZ_IP_PROT 1
+#define ICE_FLOW_FLD_SZ_PORT 2
+#define ICE_FLOW_FLD_SZ_TCP_FLAGS 1
+#define ICE_FLOW_FLD_SZ_ICMP_TYPE 1
+#define ICE_FLOW_FLD_SZ_ICMP_CODE 1
+#define ICE_FLOW_FLD_SZ_ARP_OPER 2
+#define ICE_FLOW_FLD_SZ_GRE_KEYID 4
+#define ICE_FLOW_FLD_SZ_GTP_TEID 4
+#define ICE_FLOW_FLD_SZ_GTP_QFI 2
+#define ICE_FLOW_FLD_SZ_PFCP_SEID 8
+#define ICE_FLOW_FLD_SZ_ESP_SPI 4
+#define ICE_FLOW_FLD_SZ_AH_SPI 4
+#define ICE_FLOW_FLD_SZ_NAT_T_ESP_SPI 4
+#define ICE_FLOW_FLD_SZ_L2TPV2_SESS_ID 2
+#define ICE_FLOW_FLD_SZ_L2TPV2_LEN_SESS_ID 2
+
/* Describe properties of a protocol header field */
struct ice_flow_field_info {
enum ice_flow_seg_hdr hdr;
@@ -20,6 +52,7 @@ struct ice_flow_field_info {
.mask = 0, \
}
+/* QFI: 6-bit field in GTP-U PDU Session Container (3GPP TS 38.415) */
#define ICE_FLOW_FLD_INFO_MSK(_hdr, _offset_bytes, _size_bytes, _mask) { \
.hdr = _hdr, \
.off = (_offset_bytes) * BITS_PER_BYTE, \
@@ -61,7 +94,33 @@ struct ice_flow_field_info ice_flds_info[ICE_FLOW_FIELD_IDX_MAX] = {
/* ICE_FLOW_FIELD_IDX_IPV6_SA */
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 8, sizeof(struct in6_addr)),
/* ICE_FLOW_FIELD_IDX_IPV6_DA */
- ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 24, sizeof(struct in6_addr)),
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 24, ICE_FLOW_FLD_SZ_IPV6_ADDR),
+ /* ICE_FLOW_FIELD_IDX_IPV4_CHKSUM */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV4, 10, ICE_FLOW_FLD_SZ_IP_CHKSUM),
+ /* ICE_FLOW_FIELD_IDX_IPV4_FRAG */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV_FRAG, 4,
+ ICE_FLOW_FLD_SZ_IPV4_ID),
+ /* ICE_FLOW_FIELD_IDX_IPV6_FRAG */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV_FRAG, 4,
+ ICE_FLOW_FLD_SZ_IPV6_ID),
+ /* ICE_FLOW_FIELD_IDX_IPV6_PRE32_SA */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 8,
+ ICE_FLOW_FLD_SZ_IPV6_PRE32_ADDR),
+ /* ICE_FLOW_FIELD_IDX_IPV6_PRE32_DA */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 24,
+ ICE_FLOW_FLD_SZ_IPV6_PRE32_ADDR),
+ /* ICE_FLOW_FIELD_IDX_IPV6_PRE48_SA */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 8,
+ ICE_FLOW_FLD_SZ_IPV6_PRE48_ADDR),
+ /* ICE_FLOW_FIELD_IDX_IPV6_PRE48_DA */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 24,
+ ICE_FLOW_FLD_SZ_IPV6_PRE48_ADDR),
+ /* ICE_FLOW_FIELD_IDX_IPV6_PRE64_SA */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 8,
+ ICE_FLOW_FLD_SZ_IPV6_PRE64_ADDR),
+ /* ICE_FLOW_FIELD_IDX_IPV6_PRE64_DA */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_IPV6, 24,
+ ICE_FLOW_FLD_SZ_IPV6_PRE64_ADDR),
/* Transport */
/* ICE_FLOW_FIELD_IDX_TCP_SRC_PORT */
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 0, sizeof(__be16)),
@@ -76,7 +135,14 @@ struct ice_flow_field_info ice_flds_info[ICE_FLOW_FIELD_IDX_MAX] = {
/* ICE_FLOW_FIELD_IDX_SCTP_DST_PORT */
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_SCTP, 2, sizeof(__be16)),
/* ICE_FLOW_FIELD_IDX_TCP_FLAGS */
- ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 13, 1),
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 13, ICE_FLOW_FLD_SZ_TCP_FLAGS),
+ /* ICE_FLOW_FIELD_IDX_TCP_CHKSUM */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_TCP, 16, ICE_FLOW_FLD_SZ_TCP_CHKSUM),
+ /* ICE_FLOW_FIELD_IDX_UDP_CHKSUM */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_UDP, 6, ICE_FLOW_FLD_SZ_UDP_CHKSUM),
+ /* ICE_FLOW_FIELD_IDX_SCTP_CHKSUM */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_SCTP, 8,
+ ICE_FLOW_FLD_SZ_SCTP_CHKSUM),
/* ARP */
/* ICE_FLOW_FIELD_IDX_ARP_SIP */
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_ARP, 14, sizeof(struct in_addr)),
@@ -108,9 +174,17 @@ struct ice_flow_field_info ice_flds_info[ICE_FLOW_FIELD_IDX_MAX] = {
ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_GTPU_EH, 22, sizeof(__be16),
0x3f00),
/* ICE_FLOW_FIELD_IDX_GTPU_UP_TEID */
- ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_UP, 12, sizeof(__be32)),
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_UP, 12,
+ ICE_FLOW_FLD_SZ_GTP_TEID),
+ /* ICE_FLOW_FIELD_IDX_GTPU_UP_QFI */
+ ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_GTPU_UP, 22,
+ ICE_FLOW_FLD_SZ_GTP_QFI, 0x3f00),
/* ICE_FLOW_FIELD_IDX_GTPU_DWN_TEID */
- ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_DWN, 12, sizeof(__be32)),
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_GTPU_DWN, 12,
+ ICE_FLOW_FLD_SZ_GTP_TEID),
+ /* ICE_FLOW_FIELD_IDX_GTPU_DWN_QFI */
+ ICE_FLOW_FLD_INFO_MSK(ICE_FLOW_SEG_HDR_GTPU_DWN, 22,
+ ICE_FLOW_FLD_SZ_GTP_QFI, 0x3f00),
/* PPPoE */
/* ICE_FLOW_FIELD_IDX_PPPOE_SESS_ID */
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_PPPOE, 2, sizeof(__be16)),
@@ -128,7 +202,16 @@ struct ice_flow_field_info ice_flds_info[ICE_FLOW_FIELD_IDX_MAX] = {
ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_AH, 4, sizeof(__be32)),
/* NAT_T_ESP */
/* ICE_FLOW_FIELD_IDX_NAT_T_ESP_SPI */
- ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_NAT_T_ESP, 8, sizeof(__be32)),
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_NAT_T_ESP, 8,
+ ICE_FLOW_FLD_SZ_NAT_T_ESP_SPI),
+ /* L2TPV2 */
+ /* ICE_FLOW_FIELD_IDX_L2TPV2_SESS_ID */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_L2TPV2, 12,
+ ICE_FLOW_FLD_SZ_L2TPV2_SESS_ID),
+ /* L2TPV2_LEN */
+ /* ICE_FLOW_FIELD_IDX_L2TPV2_LEN_SESS_ID */
+ ICE_FLOW_FLD_INFO(ICE_FLOW_SEG_HDR_L2TPV2, 14,
+ ICE_FLOW_FLD_SZ_L2TPV2_LEN_SESS_ID),
};
/* Bitmaps indicating relevant packet types for a particular protocol header
@@ -137,9 +220,9 @@ struct ice_flow_field_info ice_flds_info[ICE_FLOW_FIELD_IDX_MAX] = {
*/
static const u32 ice_ptypes_mac_ofos[] = {
0xFDC00846, 0xBFBF7F7E, 0xF70001DF, 0xFEFDFDFB,
- 0x0000077E, 0x00000000, 0x00000000, 0x00000000,
- 0x00400000, 0x03FFF000, 0x7FFFFFE0, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x0000077E, 0x000003FF, 0x00000000, 0x00000000,
+ 0x00400000, 0x03FFF000, 0xFFFFFFE0, 0x00000707,
+ 0xFFFFF000, 0x000003FF, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -162,10 +245,10 @@ static const u32 ice_ptypes_macvlan_il[] = {
* include IPv4 other PTYPEs
*/
static const u32 ice_ptypes_ipv4_ofos[] = {
- 0x1DC00000, 0x04000800, 0x00000000, 0x00000000,
+ 0x1D800000, 0xBFBF7800, 0x000001DF, 0x00000000,
0x00000000, 0x00000155, 0x00000000, 0x00000000,
- 0x00000000, 0x000FC000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x000FC000, 0x000002A0, 0x00000000,
+ 0x00015000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -176,10 +259,10 @@ static const u32 ice_ptypes_ipv4_ofos[] = {
* IPv4 other PTYPEs
*/
static const u32 ice_ptypes_ipv4_ofos_all[] = {
- 0x1DC00000, 0x04000800, 0x00000000, 0x00000000,
+ 0x1D800000, 0x27BF7800, 0x00000000, 0x00000000,
0x00000000, 0x00000155, 0x00000000, 0x00000000,
- 0x00000000, 0x000FC000, 0x83E0F800, 0x00000101,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x000FC000, 0x83E0FAA0, 0x00000101,
+ 0x3FFD5000, 0x00000000, 0x02FBEFBC, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -191,7 +274,7 @@ static const u32 ice_ptypes_ipv4_il[] = {
0xE0000000, 0xB807700E, 0x80000003, 0xE01DC03B,
0x0000000E, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x001FF800, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0xC0FC0000, 0x0000000F, 0xBC0BC0BC, 0x00000BC0,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -202,10 +285,10 @@ static const u32 ice_ptypes_ipv4_il[] = {
* include IPv6 other PTYPEs
*/
static const u32 ice_ptypes_ipv6_ofos[] = {
- 0x00000000, 0x00000000, 0x77000000, 0x10002000,
+ 0x00000000, 0x00000000, 0x76000000, 0x10002000,
0x00000000, 0x000002AA, 0x00000000, 0x00000000,
- 0x00000000, 0x03F00000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x03F00000, 0x00000540, 0x00000000,
+ 0x0002A000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -216,10 +299,10 @@ static const u32 ice_ptypes_ipv6_ofos[] = {
* IPv6 other PTYPEs
*/
static const u32 ice_ptypes_ipv6_ofos_all[] = {
- 0x00000000, 0x00000000, 0x77000000, 0x10002000,
- 0x00000000, 0x000002AA, 0x00000000, 0x00000000,
- 0x00080F00, 0x03F00000, 0x7C1F0000, 0x00000206,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x76000000, 0xFEFDE000,
+ 0x0000077E, 0x000002AA, 0x00000000, 0x00000000,
+ 0x00000000, 0x03F00000, 0x7C1F0540, 0x00000206,
+ 0xC002A000, 0x000003FF, 0xBC000000, 0x0002FBEF,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -231,7 +314,7 @@ static const u32 ice_ptypes_ipv6_il[] = {
0x00000000, 0x03B80770, 0x000001DC, 0x0EE00000,
0x00000770, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x7FE00000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x3F000000, 0x000003F0, 0x02F02F00, 0x0002F02F,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -304,8 +387,8 @@ static const u32 ice_ptypes_ipv6_il_no_l4[] = {
static const u32 ice_ptypes_udp_il[] = {
0x81000000, 0x20204040, 0x04000010, 0x80810102,
0x00000040, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00410000, 0x90842000, 0x00000007,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00410000, 0x908427E0, 0x00000007,
+ 0x0413F000, 0x00000041, 0x10410410, 0x00004104,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -317,7 +400,7 @@ static const u32 ice_ptypes_tcp_il[] = {
0x04000000, 0x80810102, 0x10000040, 0x02040408,
0x00000102, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00820000, 0x21084000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x08200000, 0x00000082, 0x20820820, 0x00008208,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -329,7 +412,7 @@ static const u32 ice_ptypes_sctp_il[] = {
0x08000000, 0x01020204, 0x20000081, 0x04080810,
0x00000204, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x01040000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x10400000, 0x00000104, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -353,7 +436,7 @@ static const u32 ice_ptypes_icmp_il[] = {
0x00000000, 0x02040408, 0x40000102, 0x08101020,
0x00000408, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x42108000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x20800000, 0x00000208, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -365,7 +448,7 @@ static const u32 ice_ptypes_gre_of[] = {
0x00000000, 0xBFBF7800, 0x000001DF, 0xFEFDE000,
0x0000017E, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0xBEFBEFBC, 0x0002FBEF,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -374,7 +457,7 @@ static const u32 ice_ptypes_gre_of[] = {
/* Packet types for packets with an Innermost/Last MAC header */
static const u32 ice_ptypes_mac_il[] = {
- 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x20000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -388,7 +471,7 @@ static const u32 ice_ptypes_mac_il[] = {
static const u32 ice_ptypes_gtpc[] = {
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000, 0x00000000, 0x00000180, 0x00000000,
+ 0x00000000, 0x00000000, 0x000001E0, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000,
@@ -2325,6 +2408,130 @@ static void ice_rss_set_symm(struct ice_hw *hw, struct ice_flow_prof *prof)
}
/**
+ * ice_rss_cfg_raw_symm - Configure symmetric RSS for a raw parser profile
+ * @hw: device HW
+ * @prof: parser profile describing extracted FV (field vector) entries
+ * @prof_id: RSS profile identifier used to program symmetry registers
+ *
+ * The routine scans the parser profile's FV entries and looks for
+ * direction-sensitive pairs (L3 src/dst, L4 src/dst). When a pair is found,
+ * it programs XOR-based symmetry so that flows hash identically regardless
+ * of packet direction. This preserves CPU affinity for the same 5-tuple.
+ *
+ * Notes:
+ * - The size of each logical field (IPv4/IPv6 address, L4 port) is expressed
+ * in units of ICE_FLOW_FV_EXTRACT_SZ so we can step across fv[] correctly.
+ * - We guard against out-of-bounds access before looking at fv[i + len].
+ */
+static void ice_rss_cfg_raw_symm(struct ice_hw *hw,
+ const struct ice_parser_profile *prof,
+ u64 prof_id)
+{
+ for (size_t i = 0; i < prof->fv_num; i++) {
+ u8 proto_id = prof->fv[i].proto_id;
+ u16 src_off = 0, dst_off = 0;
+ size_t src_idx, dst_idx;
+ bool is_matched = false;
+ unsigned int len = 0;
+
+ switch (proto_id) {
+ /* IPv4 address pairs (outer/inner variants) */
+ case ICE_PROT_IPV4_OF_OR_S:
+ case ICE_PROT_IPV4_IL:
+ case ICE_PROT_IPV4_IL_IL:
+ len = ICE_FLOW_FLD_SZ_IPV4_ADDR /
+ ICE_FLOW_FV_EXTRACT_SZ;
+ src_off = ICE_FLOW_FIELD_IPV4_SRC_OFFSET;
+ dst_off = ICE_FLOW_FIELD_IPV4_DST_OFFSET;
+ break;
+
+ /* IPv6 address pairs (outer/inner variants) */
+ case ICE_PROT_IPV6_OF_OR_S:
+ case ICE_PROT_IPV6_IL:
+ case ICE_PROT_IPV6_IL_IL:
+ len = ICE_FLOW_FLD_SZ_IPV6_ADDR /
+ ICE_FLOW_FV_EXTRACT_SZ;
+ src_off = ICE_FLOW_FIELD_IPV6_SRC_OFFSET;
+ dst_off = ICE_FLOW_FIELD_IPV6_DST_OFFSET;
+ break;
+
+ /* L4 port pairs (TCP/UDP/SCTP) */
+ case ICE_PROT_TCP_IL:
+ case ICE_PROT_UDP_IL_OR_S:
+ case ICE_PROT_SCTP_IL:
+ len = ICE_FLOW_FLD_SZ_PORT / ICE_FLOW_FV_EXTRACT_SZ;
+ src_off = ICE_FLOW_FIELD_SRC_PORT_OFFSET;
+ dst_off = ICE_FLOW_FIELD_DST_PORT_OFFSET;
+ break;
+
+ default:
+ continue;
+ }
+
+ /* Bounds check before accessing fv[i + len]. */
+ if (i + len >= prof->fv_num)
+ continue;
+
+ /* Verify src/dst pairing for this protocol id. */
+ is_matched = prof->fv[i].offset == src_off &&
+ prof->fv[i + len].proto_id == proto_id &&
+ prof->fv[i + len].offset == dst_off;
+ if (!is_matched)
+ continue;
+
+ /* Program XOR symmetry for this field pair. */
+ src_idx = i;
+ dst_idx = i + len;
+
+ ice_rss_config_xor(hw, prof_id, src_idx, dst_idx, len);
+
+ /* Skip over the pair we just handled; the loop's ++i advances
+ * one more element, hence the --i after the jump.
+ */
+ i += (2 * len);
+ /* not strictly needed; keeps static analyzers happy */
+ if (i == 0)
+ break;
+ --i;
+ }
+}
+
+/* Max registers index per packet profile */
+#define ICE_SYMM_REG_INDEX_MAX 6
+
+/**
+ * ice_rss_update_raw_symm - update symmetric hash configuration
+ * for raw pattern
+ * @hw: pointer to the hardware structure
+ * @cfg: configure parameters for raw pattern
+ * @id: profile tracking ID
+ *
+ * Update symmetric hash configuration for raw pattern if required.
+ * Otherwise only clear to default.
+ */
+void
+ice_rss_update_raw_symm(struct ice_hw *hw,
+ struct ice_rss_raw_cfg *cfg, u64 id)
+{
+ struct ice_prof_map *map;
+ u8 prof_id, m;
+
+ mutex_lock(&hw->blk[ICE_BLK_RSS].es.prof_map_lock);
+ map = ice_search_prof_id(hw, ICE_BLK_RSS, id);
+ if (map)
+ prof_id = map->prof_id;
+ mutex_unlock(&hw->blk[ICE_BLK_RSS].es.prof_map_lock);
+ if (!map)
+ return;
+ /* clear to default */
+ for (m = 0; m < ICE_SYMM_REG_INDEX_MAX; m++)
+ wr32(hw, GLQF_HSYMM(prof_id, m), 0);
+
+ if (cfg->symm)
+ ice_rss_cfg_raw_symm(hw, &cfg->prof, prof_id);
+}
+
+/**
* ice_add_rss_cfg_sync - add an RSS configuration
* @hw: pointer to the hardware structure
* @vsi_handle: software VSI handle
diff --git a/drivers/net/ethernet/intel/ice/ice_flow.h b/drivers/net/ethernet/intel/ice/ice_flow.h
index 52f906d89eca..6c6cdc8addb1 100644
--- a/drivers/net/ethernet/intel/ice/ice_flow.h
+++ b/drivers/net/ethernet/intel/ice/ice_flow.h
@@ -22,6 +22,15 @@
#define ICE_FLOW_HASH_IPV6 \
(BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA) | \
BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_DA))
+#define ICE_FLOW_HASH_IPV6_PRE32 \
+ (BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE32_SA) | \
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE32_DA))
+#define ICE_FLOW_HASH_IPV6_PRE48 \
+ (BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE48_SA) | \
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE48_DA))
+#define ICE_FLOW_HASH_IPV6_PRE64 \
+ (BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_SA) | \
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_DA))
#define ICE_FLOW_HASH_TCP_PORT \
(BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_SRC_PORT) | \
BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_DST_PORT))
@@ -40,6 +49,33 @@
#define ICE_HASH_SCTP_IPV4 (ICE_FLOW_HASH_IPV4 | ICE_FLOW_HASH_SCTP_PORT)
#define ICE_HASH_SCTP_IPV6 (ICE_FLOW_HASH_IPV6 | ICE_FLOW_HASH_SCTP_PORT)
+#define ICE_HASH_TCP_IPV6_PRE32 \
+ (ICE_FLOW_HASH_IPV6_PRE32 | ICE_FLOW_HASH_TCP_PORT)
+#define ICE_HASH_UDP_IPV6_PRE32 \
+ (ICE_FLOW_HASH_IPV6_PRE32 | ICE_FLOW_HASH_UDP_PORT)
+#define ICE_HASH_SCTP_IPV6_PRE32 \
+ (ICE_FLOW_HASH_IPV6_PRE32 | ICE_FLOW_HASH_SCTP_PORT)
+#define ICE_HASH_TCP_IPV6_PRE48 \
+ (ICE_FLOW_HASH_IPV6_PRE48 | ICE_FLOW_HASH_TCP_PORT)
+#define ICE_HASH_UDP_IPV6_PRE48 \
+ (ICE_FLOW_HASH_IPV6_PRE48 | ICE_FLOW_HASH_UDP_PORT)
+#define ICE_HASH_SCTP_IPV6_PRE48 \
+ (ICE_FLOW_HASH_IPV6_PRE48 | ICE_FLOW_HASH_SCTP_PORT)
+#define ICE_HASH_TCP_IPV6_PRE64 \
+ (ICE_FLOW_HASH_IPV6_PRE64 | ICE_FLOW_HASH_TCP_PORT)
+#define ICE_HASH_UDP_IPV6_PRE64 \
+ (ICE_FLOW_HASH_IPV6_PRE64 | ICE_FLOW_HASH_UDP_PORT)
+#define ICE_HASH_SCTP_IPV6_PRE64 \
+ (ICE_FLOW_HASH_IPV6_PRE64 | ICE_FLOW_HASH_SCTP_PORT)
+
+#define ICE_FLOW_HASH_GTP_TEID \
+ (BIT_ULL(ICE_FLOW_FIELD_IDX_GTPC_TEID))
+
+#define ICE_FLOW_HASH_GTP_IPV4_TEID \
+ (ICE_FLOW_HASH_IPV4 | ICE_FLOW_HASH_GTP_TEID)
+#define ICE_FLOW_HASH_GTP_IPV6_TEID \
+ (ICE_FLOW_HASH_IPV6 | ICE_FLOW_HASH_GTP_TEID)
+
#define ICE_FLOW_HASH_GTP_C_TEID \
(BIT_ULL(ICE_FLOW_FIELD_IDX_GTPC_TEID))
@@ -128,6 +164,23 @@
#define ICE_FLOW_HASH_NAT_T_ESP_IPV6_SPI \
(ICE_FLOW_HASH_IPV6 | ICE_FLOW_HASH_NAT_T_ESP_SPI)
+#define ICE_FLOW_HASH_L2TPV2_SESS_ID \
+ (BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV2_SESS_ID))
+#define ICE_FLOW_HASH_L2TPV2_SESS_ID_ETH \
+ (ICE_FLOW_HASH_ETH | ICE_FLOW_HASH_L2TPV2_SESS_ID)
+
+#define ICE_FLOW_HASH_L2TPV2_LEN_SESS_ID \
+ (BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV2_LEN_SESS_ID))
+#define ICE_FLOW_HASH_L2TPV2_LEN_SESS_ID_ETH \
+ (ICE_FLOW_HASH_ETH | ICE_FLOW_HASH_L2TPV2_LEN_SESS_ID)
+
+#define ICE_FLOW_FIELD_IPV4_SRC_OFFSET 12
+#define ICE_FLOW_FIELD_IPV4_DST_OFFSET 16
+#define ICE_FLOW_FIELD_IPV6_SRC_OFFSET 8
+#define ICE_FLOW_FIELD_IPV6_DST_OFFSET 24
+#define ICE_FLOW_FIELD_SRC_PORT_OFFSET 0
+#define ICE_FLOW_FIELD_DST_PORT_OFFSET 2
+
/* Protocol header fields within a packet segment. A segment consists of one or
* more protocol headers that make up a logical group of protocol headers. Each
* logical group of protocol headers encapsulates or is encapsulated using/by
@@ -160,10 +213,13 @@ enum ice_flow_seg_hdr {
ICE_FLOW_SEG_HDR_AH = 0x00200000,
ICE_FLOW_SEG_HDR_NAT_T_ESP = 0x00400000,
ICE_FLOW_SEG_HDR_ETH_NON_IP = 0x00800000,
+ ICE_FLOW_SEG_HDR_GTPU_NON_IP = 0x01000000,
+ ICE_FLOW_SEG_HDR_L2TPV2 = 0x10000000,
/* The following is an additive bit for ICE_FLOW_SEG_HDR_IPV4 and
- * ICE_FLOW_SEG_HDR_IPV6 which include the IPV4 other PTYPEs
+ * ICE_FLOW_SEG_HDR_IPV6.
*/
- ICE_FLOW_SEG_HDR_IPV_OTHER = 0x20000000,
+ ICE_FLOW_SEG_HDR_IPV_FRAG = 0x40000000,
+ ICE_FLOW_SEG_HDR_IPV_OTHER = 0x80000000,
};
/* These segments all have the same PTYPES, but are otherwise distinguished by
@@ -200,6 +256,15 @@ enum ice_flow_field {
ICE_FLOW_FIELD_IDX_IPV4_DA,
ICE_FLOW_FIELD_IDX_IPV6_SA,
ICE_FLOW_FIELD_IDX_IPV6_DA,
+ ICE_FLOW_FIELD_IDX_IPV4_CHKSUM,
+ ICE_FLOW_FIELD_IDX_IPV4_ID,
+ ICE_FLOW_FIELD_IDX_IPV6_ID,
+ ICE_FLOW_FIELD_IDX_IPV6_PRE32_SA,
+ ICE_FLOW_FIELD_IDX_IPV6_PRE32_DA,
+ ICE_FLOW_FIELD_IDX_IPV6_PRE48_SA,
+ ICE_FLOW_FIELD_IDX_IPV6_PRE48_DA,
+ ICE_FLOW_FIELD_IDX_IPV6_PRE64_SA,
+ ICE_FLOW_FIELD_IDX_IPV6_PRE64_DA,
/* L4 */
ICE_FLOW_FIELD_IDX_TCP_SRC_PORT,
ICE_FLOW_FIELD_IDX_TCP_DST_PORT,
@@ -208,6 +273,9 @@ enum ice_flow_field {
ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT,
ICE_FLOW_FIELD_IDX_SCTP_DST_PORT,
ICE_FLOW_FIELD_IDX_TCP_FLAGS,
+ ICE_FLOW_FIELD_IDX_TCP_CHKSUM,
+ ICE_FLOW_FIELD_IDX_UDP_CHKSUM,
+ ICE_FLOW_FIELD_IDX_SCTP_CHKSUM,
/* ARP */
ICE_FLOW_FIELD_IDX_ARP_SIP,
ICE_FLOW_FIELD_IDX_ARP_DIP,
@@ -228,13 +296,13 @@ enum ice_flow_field {
ICE_FLOW_FIELD_IDX_GTPU_EH_QFI,
/* GTPU_UP */
ICE_FLOW_FIELD_IDX_GTPU_UP_TEID,
+ ICE_FLOW_FIELD_IDX_GTPU_UP_QFI,
/* GTPU_DWN */
ICE_FLOW_FIELD_IDX_GTPU_DWN_TEID,
- /* PPPoE */
+ ICE_FLOW_FIELD_IDX_GTPU_DWN_QFI,
ICE_FLOW_FIELD_IDX_PPPOE_SESS_ID,
/* PFCP */
ICE_FLOW_FIELD_IDX_PFCP_SEID,
- /* L2TPv3 */
ICE_FLOW_FIELD_IDX_L2TPV3_SESS_ID,
/* ESP */
ICE_FLOW_FIELD_IDX_ESP_SPI,
@@ -242,10 +310,16 @@ enum ice_flow_field {
ICE_FLOW_FIELD_IDX_AH_SPI,
/* NAT_T ESP */
ICE_FLOW_FIELD_IDX_NAT_T_ESP_SPI,
+ /* L2TPV2 SESSION ID*/
+ ICE_FLOW_FIELD_IDX_L2TPV2_SESS_ID,
+ /* L2TPV2_LEN SESSION ID */
+ ICE_FLOW_FIELD_IDX_L2TPV2_LEN_SESS_ID,
/* The total number of enums must not exceed 64 */
ICE_FLOW_FIELD_IDX_MAX
};
+static_assert(ICE_FLOW_FIELD_IDX_MAX <= 64, "The total number of enums must not exceed 64");
+
#define ICE_FLOW_HASH_FLD_IPV4_SA BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA)
#define ICE_FLOW_HASH_FLD_IPV6_SA BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA)
#define ICE_FLOW_HASH_FLD_IPV4_DA BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA)
@@ -296,6 +370,10 @@ enum ice_rss_cfg_hdr_type {
/* take inner headers as inputset for packet with outer ipv6. */
ICE_RSS_INNER_HEADERS_W_OUTER_IPV6,
/* take outer headers first then inner headers as inputset */
+ /* take inner as inputset for GTPoGRE with outer IPv4 + GRE. */
+ ICE_RSS_INNER_HEADERS_W_OUTER_IPV4_GRE,
+ /* take inner as inputset for GTPoGRE with outer IPv6 + GRE. */
+ ICE_RSS_INNER_HEADERS_W_OUTER_IPV6_GRE,
ICE_RSS_ANY_HEADERS
};
@@ -406,6 +484,12 @@ struct ice_flow_prof {
bool symm; /* Symmetric Hash for RSS */
};
+struct ice_rss_raw_cfg {
+ struct ice_parser_profile prof;
+ bool raw_ena;
+ bool symm;
+};
+
struct ice_rss_cfg {
struct list_head l_entry;
/* bitmap of VSIs added to the RSS entry */
@@ -444,4 +528,6 @@ int ice_add_rss_cfg(struct ice_hw *hw, struct ice_vsi *vsi,
int ice_rem_rss_cfg(struct ice_hw *hw, u16 vsi_handle,
const struct ice_rss_hash_cfg *cfg);
u64 ice_get_rss_cfg(struct ice_hw *hw, u16 vsi_handle, u32 hdrs, bool *symm);
+void ice_rss_update_raw_symm(struct ice_hw *hw,
+ struct ice_rss_raw_cfg *cfg, u64 id);
#endif /* _ICE_FLOW_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.c b/drivers/net/ethernet/intel/ice/ice_fw_update.c
index d86db081579f..973a13d3d92a 100644
--- a/drivers/net/ethernet/intel/ice/ice_fw_update.c
+++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c
@@ -534,7 +534,7 @@ ice_erase_nvm_module(struct ice_pf *pf, u16 module, const char *component,
}
if (completion_retval) {
- dev_err(dev, "Firmware failed to erase %s (module 0x02%x), aq_err %s\n",
+ dev_err(dev, "Firmware failed to erase %s (module 0x%02x), aq_err %s\n",
component, module,
libie_aq_str((enum libie_aq_err)completion_retval));
NL_SET_ERR_MSG_MOD(extack, "Firmware failed to erase flash");
diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c
index aebf8e08a297..d2576d606e10 100644
--- a/drivers/net/ethernet/intel/ice/ice_lag.c
+++ b/drivers/net/ethernet/intel/ice/ice_lag.c
@@ -2177,8 +2177,7 @@ static void ice_lag_chk_disabled_bond(struct ice_lag *lag, void *ptr)
*/
static void ice_lag_disable_sriov_bond(struct ice_lag *lag)
{
- struct ice_netdev_priv *np = netdev_priv(lag->netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(lag->netdev);
ice_clear_feature_support(pf, ICE_F_SRIOV_LAG);
ice_clear_feature_support(pf, ICE_F_SRIOV_AA_LAG);
diff --git a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
index 10c312d49e05..185672c7e17d 100644
--- a/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
+++ b/drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
@@ -342,6 +342,9 @@ enum ice_flg64_bits {
/* for ice_32byte_rx_flex_desc.pkt_length member */
#define ICE_RX_FLX_DESC_PKT_LEN_M (0x3FFF) /* 14-bits */
+/* ice_32byte_rx_flex_desc::hdr_len_sph_flex_flags1 */
+#define ICE_RX_FLEX_DESC_HDR_LEN_M GENMASK(10, 0)
+
enum ice_rx_flex_desc_status_error_0_bits {
/* Note: These are predefined bit offsets */
ICE_RX_FLEX_DESC_STATUS0_DD_S = 0,
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 4479c824561e..15621707fbf8 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -1427,7 +1427,6 @@ static int ice_vsi_alloc_rings(struct ice_vsi *vsi)
ring->reg_idx = vsi->rxq_map[i];
ring->vsi = vsi;
ring->netdev = vsi->netdev;
- ring->dev = dev;
ring->count = vsi->num_rx_desc;
ring->cached_phctime = pf->ptp.cached_phc_time;
@@ -2769,7 +2768,6 @@ void ice_dis_vsi(struct ice_vsi *vsi, bool locked)
* @vsi: VSI pointer
*
* Associate queue[s] with napi for all vectors.
- * The caller must hold rtnl_lock.
*/
void ice_vsi_set_napi_queues(struct ice_vsi *vsi)
{
@@ -2779,6 +2777,7 @@ void ice_vsi_set_napi_queues(struct ice_vsi *vsi)
if (!netdev)
return;
+ ASSERT_RTNL();
ice_for_each_rxq(vsi, q_idx)
netif_queue_set_napi(netdev, q_idx, NETDEV_QUEUE_TYPE_RX,
&vsi->rx_rings[q_idx]->q_vector->napi);
@@ -2799,7 +2798,6 @@ void ice_vsi_set_napi_queues(struct ice_vsi *vsi)
* @vsi: VSI pointer
*
* Clear the association between all VSI queues queue[s] and napi.
- * The caller must hold rtnl_lock.
*/
void ice_vsi_clear_napi_queues(struct ice_vsi *vsi)
{
@@ -2809,6 +2807,7 @@ void ice_vsi_clear_napi_queues(struct ice_vsi *vsi)
if (!netdev)
return;
+ ASSERT_RTNL();
/* Clear the NAPI's interrupt number */
ice_for_each_q_vector(vsi, v_idx) {
struct ice_q_vector *q_vector = vsi->q_vectors[v_idx];
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index 86f5859e88ef..4bb68e7a00f5 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -37,6 +37,8 @@ static const char ice_copyright[] = "Copyright (c) 2018, Intel Corporation.";
#define ICE_DDP_PKG_FILE ICE_DDP_PKG_PATH "ice.pkg"
MODULE_DESCRIPTION(DRV_SUMMARY);
+MODULE_IMPORT_NS("LIBETH");
+MODULE_IMPORT_NS("LIBETH_XDP");
MODULE_IMPORT_NS("LIBIE");
MODULE_IMPORT_NS("LIBIE_ADMINQ");
MODULE_IMPORT_NS("LIBIE_FWLOG");
@@ -2957,10 +2959,7 @@ int ice_vsi_determine_xdp_res(struct ice_vsi *vsi)
*/
static int ice_max_xdp_frame_size(struct ice_vsi *vsi)
{
- if (test_bit(ICE_FLAG_LEGACY_RX, vsi->back->flags))
- return ICE_RXBUF_1664;
- else
- return ICE_RXBUF_3072;
+ return ICE_RXBUF_3072;
}
/**
@@ -3018,19 +3017,11 @@ ice_xdp_setup_prog(struct ice_vsi *vsi, struct bpf_prog *prog,
}
}
xdp_features_set_redirect_target(vsi->netdev, true);
- /* reallocate Rx queues that are used for zero-copy */
- xdp_ring_err = ice_realloc_zc_buf(vsi, true);
- if (xdp_ring_err)
- NL_SET_ERR_MSG_MOD(extack, "Setting up XDP Rx resources failed");
} else if (ice_is_xdp_ena_vsi(vsi) && !prog) {
xdp_features_clear_redirect_target(vsi->netdev);
xdp_ring_err = ice_destroy_xdp_rings(vsi, ICE_XDP_CFG_FULL);
if (xdp_ring_err)
NL_SET_ERR_MSG_MOD(extack, "Freeing XDP Tx resources failed");
- /* reallocate Rx queues that were used for zero-copy */
- xdp_ring_err = ice_realloc_zc_buf(vsi, false);
- if (xdp_ring_err)
- NL_SET_ERR_MSG_MOD(extack, "Freeing XDP Rx resources failed");
}
resume_if:
@@ -3949,9 +3940,10 @@ u16 ice_get_avail_rxq_count(struct ice_pf *pf)
* ice_deinit_pf - Unrolls initialziations done by ice_init_pf
* @pf: board private structure to initialize
*/
-static void ice_deinit_pf(struct ice_pf *pf)
+void ice_deinit_pf(struct ice_pf *pf)
{
- ice_service_task_stop(pf);
+ /* note that we unroll also on ice_init_pf() failure here */
+
mutex_destroy(&pf->lag_mutex);
mutex_destroy(&pf->adev_mutex);
mutex_destroy(&pf->sw_mutex);
@@ -3977,6 +3969,9 @@ static void ice_deinit_pf(struct ice_pf *pf)
if (pf->ptp.clock)
ptp_clock_unregister(pf->ptp.clock);
+ if (!xa_empty(&pf->irq_tracker.entries))
+ ice_free_irq_msix_misc(pf);
+
xa_destroy(&pf->dyn_ports);
xa_destroy(&pf->sf_nums);
}
@@ -4030,13 +4025,25 @@ static void ice_set_pf_caps(struct ice_pf *pf)
pf->max_pf_rxqs = func_caps->common_cap.num_rxq;
}
+void ice_start_service_task(struct ice_pf *pf)
+{
+ timer_setup(&pf->serv_tmr, ice_service_timer, 0);
+ pf->serv_tmr_period = HZ;
+ INIT_WORK(&pf->serv_task, ice_service_task);
+ clear_bit(ICE_SERVICE_SCHED, pf->state);
+}
+
/**
* ice_init_pf - Initialize general software structures (struct ice_pf)
* @pf: board private structure to initialize
+ * Return: 0 on success, negative errno otherwise.
*/
-static int ice_init_pf(struct ice_pf *pf)
+int ice_init_pf(struct ice_pf *pf)
{
- ice_set_pf_caps(pf);
+ struct udp_tunnel_nic_info *udp_tunnel_nic = &pf->hw.udp_tunnel_nic;
+ struct device *dev = ice_pf_to_dev(pf);
+ struct ice_hw *hw = &pf->hw;
+ int err = -ENOMEM;
mutex_init(&pf->sw_mutex);
mutex_init(&pf->tc_mutex);
@@ -4049,32 +4056,7 @@ static int ice_init_pf(struct ice_pf *pf)
init_waitqueue_head(&pf->reset_wait_queue);
- /* setup service timer and periodic service task */
- timer_setup(&pf->serv_tmr, ice_service_timer, 0);
- pf->serv_tmr_period = HZ;
- INIT_WORK(&pf->serv_task, ice_service_task);
- clear_bit(ICE_SERVICE_SCHED, pf->state);
-
mutex_init(&pf->avail_q_mutex);
- pf->avail_txqs = bitmap_zalloc(pf->max_pf_txqs, GFP_KERNEL);
- if (!pf->avail_txqs)
- return -ENOMEM;
-
- pf->avail_rxqs = bitmap_zalloc(pf->max_pf_rxqs, GFP_KERNEL);
- if (!pf->avail_rxqs) {
- bitmap_free(pf->avail_txqs);
- pf->avail_txqs = NULL;
- return -ENOMEM;
- }
-
- pf->txtime_txqs = bitmap_zalloc(pf->max_pf_txqs, GFP_KERNEL);
- if (!pf->txtime_txqs) {
- bitmap_free(pf->avail_txqs);
- pf->avail_txqs = NULL;
- bitmap_free(pf->avail_rxqs);
- pf->avail_rxqs = NULL;
- return -ENOMEM;
- }
mutex_init(&pf->vfs.table_lock);
hash_init(pf->vfs.table);
@@ -4087,7 +4069,36 @@ static int ice_init_pf(struct ice_pf *pf)
xa_init(&pf->dyn_ports);
xa_init(&pf->sf_nums);
+ pf->avail_txqs = bitmap_zalloc(pf->max_pf_txqs, GFP_KERNEL);
+ pf->avail_rxqs = bitmap_zalloc(pf->max_pf_rxqs, GFP_KERNEL);
+ pf->txtime_txqs = bitmap_zalloc(pf->max_pf_txqs, GFP_KERNEL);
+ if (!pf->avail_txqs || !pf->avail_rxqs || !pf->txtime_txqs)
+ goto undo_init;
+
+ udp_tunnel_nic->set_port = ice_udp_tunnel_set_port;
+ udp_tunnel_nic->unset_port = ice_udp_tunnel_unset_port;
+ udp_tunnel_nic->shared = &hw->udp_tunnel_shared;
+ udp_tunnel_nic->tables[0].n_entries = hw->tnl.valid_count[TNL_VXLAN];
+ udp_tunnel_nic->tables[0].tunnel_types = UDP_TUNNEL_TYPE_VXLAN;
+ udp_tunnel_nic->tables[1].n_entries = hw->tnl.valid_count[TNL_GENEVE];
+ udp_tunnel_nic->tables[1].tunnel_types = UDP_TUNNEL_TYPE_GENEVE;
+
+ /* In case of MSIX we are going to setup the misc vector right here
+ * to handle admin queue events etc. In case of legacy and MSI
+ * the misc functionality and queue processing is combined in
+ * the same vector and that gets setup at open.
+ */
+ err = ice_req_irq_msix_misc(pf);
+ if (err) {
+ dev_err(dev, "setup of misc vector failed: %d\n", err);
+ goto undo_init;
+ }
+
return 0;
+undo_init:
+ /* deinit handles half-initialized pf just fine */
+ ice_deinit_pf(pf);
+ return err;
}
/**
@@ -4722,9 +4733,8 @@ static void ice_decfg_netdev(struct ice_vsi *vsi)
vsi->netdev = NULL;
}
-int ice_init_dev(struct ice_pf *pf)
+void ice_init_dev_hw(struct ice_pf *pf)
{
- struct device *dev = ice_pf_to_dev(pf);
struct ice_hw *hw = &pf->hw;
int err;
@@ -4744,61 +4754,28 @@ int ice_init_dev(struct ice_pf *pf)
*/
ice_set_safe_mode_caps(hw);
}
+}
- err = ice_init_pf(pf);
- if (err) {
- dev_err(dev, "ice_init_pf failed: %d\n", err);
- return err;
- }
-
- pf->hw.udp_tunnel_nic.set_port = ice_udp_tunnel_set_port;
- pf->hw.udp_tunnel_nic.unset_port = ice_udp_tunnel_unset_port;
- pf->hw.udp_tunnel_nic.shared = &pf->hw.udp_tunnel_shared;
- if (pf->hw.tnl.valid_count[TNL_VXLAN]) {
- pf->hw.udp_tunnel_nic.tables[0].n_entries =
- pf->hw.tnl.valid_count[TNL_VXLAN];
- pf->hw.udp_tunnel_nic.tables[0].tunnel_types =
- UDP_TUNNEL_TYPE_VXLAN;
- }
- if (pf->hw.tnl.valid_count[TNL_GENEVE]) {
- pf->hw.udp_tunnel_nic.tables[1].n_entries =
- pf->hw.tnl.valid_count[TNL_GENEVE];
- pf->hw.udp_tunnel_nic.tables[1].tunnel_types =
- UDP_TUNNEL_TYPE_GENEVE;
- }
+int ice_init_dev(struct ice_pf *pf)
+{
+ struct device *dev = ice_pf_to_dev(pf);
+ int err;
+ ice_set_pf_caps(pf);
err = ice_init_interrupt_scheme(pf);
if (err) {
dev_err(dev, "ice_init_interrupt_scheme failed: %d\n", err);
- err = -EIO;
- goto unroll_pf_init;
+ return -EIO;
}
- /* In case of MSIX we are going to setup the misc vector right here
- * to handle admin queue events etc. In case of legacy and MSI
- * the misc functionality and queue processing is combined in
- * the same vector and that gets setup at open.
- */
- err = ice_req_irq_msix_misc(pf);
- if (err) {
- dev_err(dev, "setup of misc vector failed: %d\n", err);
- goto unroll_irq_scheme_init;
- }
+ ice_start_service_task(pf);
return 0;
-
-unroll_irq_scheme_init:
- ice_clear_interrupt_scheme(pf);
-unroll_pf_init:
- ice_deinit_pf(pf);
- return err;
}
void ice_deinit_dev(struct ice_pf *pf)
{
- ice_free_irq_msix_misc(pf);
- ice_deinit_pf(pf);
- ice_deinit_hw(&pf->hw);
+ ice_service_task_stop(pf);
/* Service task is already stopped, so call reset directly. */
ice_reset(&pf->hw, ICE_RESET_PFR);
@@ -5038,21 +5015,24 @@ static void ice_deinit_devlink(struct ice_pf *pf)
static int ice_init(struct ice_pf *pf)
{
+ struct device *dev = ice_pf_to_dev(pf);
int err;
- err = ice_init_dev(pf);
- if (err)
+ err = ice_init_pf(pf);
+ if (err) {
+ dev_err(dev, "ice_init_pf failed: %d\n", err);
return err;
+ }
if (pf->hw.mac_type == ICE_MAC_E830) {
err = pci_enable_ptm(pf->pdev, NULL);
if (err)
- dev_dbg(ice_pf_to_dev(pf), "PCIe PTM not supported by PCIe bus/controller\n");
+ dev_dbg(dev, "PCIe PTM not supported by PCIe bus/controller\n");
}
err = ice_alloc_vsis(pf);
if (err)
- goto err_alloc_vsis;
+ goto unroll_pf_init;
err = ice_init_pf_sw(pf);
if (err)
@@ -5089,8 +5069,8 @@ err_init_link:
ice_deinit_pf_sw(pf);
err_init_pf_sw:
ice_dealloc_vsis(pf);
-err_alloc_vsis:
- ice_deinit_dev(pf);
+unroll_pf_init:
+ ice_deinit_pf(pf);
return err;
}
@@ -5101,7 +5081,7 @@ static void ice_deinit(struct ice_pf *pf)
ice_deinit_pf_sw(pf);
ice_dealloc_vsis(pf);
- ice_deinit_dev(pf);
+ ice_deinit_pf(pf);
}
/**
@@ -5235,6 +5215,7 @@ static int
ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
{
struct device *dev = &pdev->dev;
+ bool need_dev_deinit = false;
struct ice_adapter *adapter;
struct ice_pf *pf;
struct ice_hw *hw;
@@ -5331,10 +5312,14 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
}
pf->adapter = adapter;
- err = ice_init(pf);
+ err = ice_init_dev(pf);
if (err)
goto unroll_adapter;
+ err = ice_init(pf);
+ if (err)
+ goto unroll_dev_init;
+
devl_lock(priv_to_devlink(pf));
err = ice_load(pf);
if (err)
@@ -5352,10 +5337,14 @@ unroll_load:
unroll_init:
devl_unlock(priv_to_devlink(pf));
ice_deinit(pf);
+unroll_dev_init:
+ need_dev_deinit = true;
unroll_adapter:
ice_adapter_put(pdev);
unroll_hw_init:
ice_deinit_hw(hw);
+ if (need_dev_deinit)
+ ice_deinit_dev(pf);
return err;
}
@@ -5450,10 +5439,6 @@ static void ice_remove(struct pci_dev *pdev)
ice_hwmon_exit(pf);
- ice_service_task_stop(pf);
- ice_aq_cancel_waiting_tasks(pf);
- set_bit(ICE_DOWN, pf->state);
-
if (!ice_is_safe_mode(pf))
ice_remove_arfs(pf);
@@ -5471,6 +5456,11 @@ static void ice_remove(struct pci_dev *pdev)
ice_set_wake(pf);
ice_adapter_put(pdev);
+ ice_deinit_hw(&pf->hw);
+
+ ice_deinit_dev(pf);
+ ice_aq_cancel_waiting_tasks(pf);
+ set_bit(ICE_DOWN, pf->state);
}
/**
@@ -5663,7 +5653,6 @@ static int ice_resume(struct device *dev)
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (!pci_device_is_present(pdev))
return -ENODEV;
@@ -5763,7 +5752,6 @@ static pci_ers_result_t ice_pci_err_slot_reset(struct pci_dev *pdev)
} else {
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
pci_wake_from_d3(pdev, false);
/* Check for life */
@@ -7138,6 +7126,9 @@ void ice_update_pf_stats(struct ice_pf *pf)
&prev_ps->mac_remote_faults,
&cur_ps->mac_remote_faults);
+ ice_stat_update32(hw, GLPRT_RLEC(port), pf->stat_prev_loaded,
+ &prev_ps->rx_len_errors, &cur_ps->rx_len_errors);
+
ice_stat_update32(hw, GLPRT_RUC(port), pf->stat_prev_loaded,
&prev_ps->rx_undersize, &cur_ps->rx_undersize);
@@ -7862,12 +7853,6 @@ int ice_change_mtu(struct net_device *netdev, int new_mtu)
frame_size - ICE_ETH_PKT_HDR_PAD);
return -EINVAL;
}
- } else if (test_bit(ICE_FLAG_LEGACY_RX, pf->flags)) {
- if (new_mtu + ICE_ETH_PKT_HDR_PAD > ICE_MAX_FRAME_LEGACY_RX) {
- netdev_err(netdev, "Too big MTU for legacy-rx; Max is %d\n",
- ICE_MAX_FRAME_LEGACY_RX - ICE_ETH_PKT_HDR_PAD);
- return -EINVAL;
- }
}
/* if a reset is in progress, wait for some time for it to complete */
@@ -8071,9 +8056,7 @@ static int
ice_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct net_device *dev, u32 filter_mask, int nlflags)
{
- struct ice_netdev_priv *np = netdev_priv(dev);
- struct ice_vsi *vsi = np->vsi;
- struct ice_pf *pf = vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(dev);
u16 bmode;
bmode = pf->first_sw->bridge_mode;
@@ -8143,8 +8126,7 @@ ice_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh,
u16 __always_unused flags,
struct netlink_ext_ack __always_unused *extack)
{
- struct ice_netdev_priv *np = netdev_priv(dev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(dev);
struct nlattr *attr, *br_spec;
struct ice_hw *hw = &pf->hw;
struct ice_sw *pf_sw;
@@ -9578,8 +9560,7 @@ ice_indr_setup_tc_cb(struct net_device *netdev, struct Qdisc *sch,
*/
int ice_open(struct net_device *netdev)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
if (ice_is_reset_in_progress(pf->state)) {
netdev_err(netdev, "can't open net device while reset is in progress");
diff --git a/drivers/net/ethernet/intel/ice/ice_protocol_type.h b/drivers/net/ethernet/intel/ice/ice_protocol_type.h
index 7c09ea0f03ba..725167d557a8 100644
--- a/drivers/net/ethernet/intel/ice/ice_protocol_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_protocol_type.h
@@ -82,26 +82,46 @@ enum ice_sw_tunnel_type {
enum ice_prot_id {
ICE_PROT_ID_INVAL = 0,
ICE_PROT_MAC_OF_OR_S = 1,
+ ICE_PROT_MAC_O2 = 2,
ICE_PROT_MAC_IL = 4,
+ ICE_PROT_MAC_IN_MAC = 7,
ICE_PROT_ETYPE_OL = 9,
ICE_PROT_ETYPE_IL = 10,
+ ICE_PROT_PAY = 15,
+ ICE_PROT_EVLAN_O = 16,
+ ICE_PROT_VLAN_O = 17,
+ ICE_PROT_VLAN_IF = 18,
+ ICE_PROT_MPLS_OL_MINUS_1 = 27,
+ ICE_PROT_MPLS_OL_OR_OS = 28,
+ ICE_PROT_MPLS_IL = 29,
ICE_PROT_IPV4_OF_OR_S = 32,
ICE_PROT_IPV4_IL = 33,
+ ICE_PROT_IPV4_IL_IL = 34,
ICE_PROT_IPV6_OF_OR_S = 40,
ICE_PROT_IPV6_IL = 41,
+ ICE_PROT_IPV6_IL_IL = 42,
+ ICE_PROT_IPV6_NEXT_PROTO = 43,
+ ICE_PROT_IPV6_FRAG = 47,
ICE_PROT_TCP_IL = 49,
ICE_PROT_UDP_OF = 52,
ICE_PROT_UDP_IL_OR_S = 53,
ICE_PROT_GRE_OF = 64,
+ ICE_PROT_NSH_F = 84,
ICE_PROT_ESP_F = 88,
ICE_PROT_ESP_2 = 89,
ICE_PROT_SCTP_IL = 96,
ICE_PROT_ICMP_IL = 98,
ICE_PROT_ICMPV6_IL = 100,
+ ICE_PROT_VRRP_F = 101,
+ ICE_PROT_OSPF = 102,
ICE_PROT_PPPOE = 103,
ICE_PROT_L2TPV3 = 104,
+ ICE_PROT_ATAOE_OF = 114,
+ ICE_PROT_CTRL_OF = 116,
+ ICE_PROT_LLDP_OF = 117,
ICE_PROT_ARP_OF = 118,
ICE_PROT_META_ID = 255, /* when offset == metadata */
+ ICE_PROT_EAPOL_OF = 120,
ICE_PROT_INVALID = 255 /* when offset == ICE_FV_OFFSET_INVAL */
};
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.c b/drivers/net/ethernet/intel/ice/ice_ptp.c
index fb0f6365a6d6..4c8d20f2d2c0 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.c
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.c
@@ -500,6 +500,9 @@ void ice_ptp_complete_tx_single_tstamp(struct ice_ptp_tx *tx)
if (tstamp) {
shhwtstamps.hwtstamp = ns_to_ktime(tstamp);
ice_trace(tx_tstamp_complete, skb, idx);
+
+ /* Count the number of Tx timestamps that succeeded */
+ pf->ptp.tx_hwtstamp_good++;
}
skb_tstamp_tx(skb, &shhwtstamps);
@@ -558,6 +561,7 @@ static void ice_ptp_process_tx_tstamp(struct ice_ptp_tx *tx)
{
struct ice_ptp_port *ptp_port;
unsigned long flags;
+ u32 tstamp_good = 0;
struct ice_pf *pf;
struct ice_hw *hw;
u64 tstamp_ready;
@@ -658,11 +662,16 @@ skip_ts_read:
if (tstamp) {
shhwtstamps.hwtstamp = ns_to_ktime(tstamp);
ice_trace(tx_tstamp_complete, skb, idx);
+
+ /* Count the number of Tx timestamps that succeeded */
+ tstamp_good++;
}
skb_tstamp_tx(skb, &shhwtstamps);
dev_kfree_skb_any(skb);
}
+
+ pf->ptp.tx_hwtstamp_good += tstamp_good;
}
/**
@@ -2206,8 +2215,7 @@ static int ice_ptp_getcrosststamp(struct ptp_clock_info *info,
int ice_ptp_hwtstamp_get(struct net_device *netdev,
struct kernel_hwtstamp_config *config)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
if (pf->ptp.state != ICE_PTP_READY)
return -EIO;
@@ -2278,8 +2286,7 @@ int ice_ptp_hwtstamp_set(struct net_device *netdev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
int err;
if (pf->ptp.state != ICE_PTP_READY)
@@ -3246,7 +3253,7 @@ void ice_ptp_init(struct ice_pf *pf)
err = ice_ptp_init_port(pf, &ptp->port);
if (err)
- goto err_exit;
+ goto err_clean_pf;
/* Start the PHY timestamping block */
ice_ptp_reset_phy_timestamping(pf);
@@ -3263,13 +3270,19 @@ void ice_ptp_init(struct ice_pf *pf)
dev_info(ice_pf_to_dev(pf), "PTP init successful\n");
return;
+err_clean_pf:
+ mutex_destroy(&ptp->port.ps_lock);
+ ice_ptp_cleanup_pf(pf);
err_exit:
/* If we registered a PTP clock, release it */
if (pf->ptp.clock) {
ptp_clock_unregister(ptp->clock);
pf->ptp.clock = NULL;
}
- ptp->state = ICE_PTP_ERROR;
+ /* Keep ICE_PTP_UNINIT state to avoid ambiguity at driver unload
+ * and to avoid duplicated resources release.
+ */
+ ptp->state = ICE_PTP_UNINIT;
dev_err(ice_pf_to_dev(pf), "PTP failed %d\n", err);
}
@@ -3282,8 +3295,18 @@ err_exit:
*/
void ice_ptp_release(struct ice_pf *pf)
{
- if (pf->ptp.state != ICE_PTP_READY)
+ if (pf->ptp.state == ICE_PTP_UNINIT)
+ return;
+
+ if (pf->ptp.state != ICE_PTP_READY) {
+ mutex_destroy(&pf->ptp.port.ps_lock);
+ ice_ptp_cleanup_pf(pf);
+ if (pf->ptp.clock) {
+ ptp_clock_unregister(pf->ptp.clock);
+ pf->ptp.clock = NULL;
+ }
return;
+ }
pf->ptp.state = ICE_PTP_UNINIT;
diff --git a/drivers/net/ethernet/intel/ice/ice_ptp.h b/drivers/net/ethernet/intel/ice/ice_ptp.h
index 137f2070a2d9..27016aac4f1e 100644
--- a/drivers/net/ethernet/intel/ice/ice_ptp.h
+++ b/drivers/net/ethernet/intel/ice/ice_ptp.h
@@ -237,6 +237,7 @@ struct ice_ptp_pin_desc {
* @clock: pointer to registered PTP clock device
* @tstamp_config: hardware timestamping configuration
* @reset_time: kernel time after clock stop on reset
+ * @tx_hwtstamp_good: number of completed Tx timestamp requests
* @tx_hwtstamp_skipped: number of Tx time stamp requests skipped
* @tx_hwtstamp_timeouts: number of Tx skbs discarded with no time stamp
* @tx_hwtstamp_flushed: number of Tx skbs flushed due to interface closed
@@ -261,6 +262,7 @@ struct ice_ptp {
struct ptp_clock *clock;
struct kernel_hwtstamp_config tstamp_config;
u64 reset_time;
+ u64 tx_hwtstamp_good;
u32 tx_hwtstamp_skipped;
u32 tx_hwtstamp_timeouts;
u32 tx_hwtstamp_flushed;
diff --git a/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h b/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h
index 183dd5457d6a..21bb861febbf 100644
--- a/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_sbq_cmd.h
@@ -50,6 +50,7 @@ enum ice_sbq_dev_id {
ice_sbq_dev_phy_0 = 0x02,
ice_sbq_dev_cgu = 0x06,
ice_sbq_dev_phy_0_peer = 0x0D,
+ ice_sbq_dev_cgu_peer = 0x0F,
};
enum ice_sbq_msg_opcode {
diff --git a/drivers/net/ethernet/intel/ice/ice_sriov.c b/drivers/net/ethernet/intel/ice/ice_sriov.c
index 843e82fd3bf9..6b1126ddb561 100644
--- a/drivers/net/ethernet/intel/ice/ice_sriov.c
+++ b/drivers/net/ethernet/intel/ice/ice_sriov.c
@@ -1190,8 +1190,7 @@ ice_vf_lan_overflow_event(struct ice_pf *pf, struct ice_rq_event_info *event)
*/
int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena)
{
- struct ice_netdev_priv *np = netdev_priv(netdev);
- struct ice_pf *pf = np->vsi->back;
+ struct ice_pf *pf = ice_netdev_to_pf(netdev);
struct ice_vsi *vf_vsi;
struct device *dev;
struct ice_vf *vf;
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.c b/drivers/net/ethernet/intel/ice/ice_txrx.c
index 73f08d02f9c7..ad76768a4232 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.c
@@ -7,6 +7,8 @@
#include <linux/netdevice.h>
#include <linux/prefetch.h>
#include <linux/bpf_trace.h>
+#include <linux/net/intel/libie/rx.h>
+#include <net/libeth/xdp.h>
#include <net/dsfield.h>
#include <net/mpls.h>
#include <net/xdp.h>
@@ -111,7 +113,7 @@ ice_prgm_fdir_fltr(struct ice_vsi *vsi, struct ice_fltr_desc *fdir_desc,
static void
ice_unmap_and_free_tx_buf(struct ice_tx_ring *ring, struct ice_tx_buf *tx_buf)
{
- if (dma_unmap_len(tx_buf, len))
+ if (tx_buf->type != ICE_TX_BUF_XDP_TX && dma_unmap_len(tx_buf, len))
dma_unmap_page(ring->dev,
dma_unmap_addr(tx_buf, dma),
dma_unmap_len(tx_buf, len),
@@ -125,7 +127,7 @@ ice_unmap_and_free_tx_buf(struct ice_tx_ring *ring, struct ice_tx_buf *tx_buf)
dev_kfree_skb_any(tx_buf->skb);
break;
case ICE_TX_BUF_XDP_TX:
- page_frag_free(tx_buf->raw_buf);
+ libeth_xdp_return_va(tx_buf->raw_buf, false);
break;
case ICE_TX_BUF_XDP_XMIT:
xdp_return_frame(tx_buf->xdpf);
@@ -506,61 +508,67 @@ err:
return -ENOMEM;
}
+void ice_rxq_pp_destroy(struct ice_rx_ring *rq)
+{
+ struct libeth_fq fq = {
+ .fqes = rq->rx_fqes,
+ .pp = rq->pp,
+ };
+
+ libeth_rx_fq_destroy(&fq);
+ rq->rx_fqes = NULL;
+ rq->pp = NULL;
+
+ if (!rq->hdr_pp)
+ return;
+
+ fq.fqes = rq->hdr_fqes;
+ fq.pp = rq->hdr_pp;
+
+ libeth_rx_fq_destroy(&fq);
+ rq->hdr_fqes = NULL;
+ rq->hdr_pp = NULL;
+}
+
/**
* ice_clean_rx_ring - Free Rx buffers
* @rx_ring: ring to be cleaned
*/
void ice_clean_rx_ring(struct ice_rx_ring *rx_ring)
{
- struct xdp_buff *xdp = &rx_ring->xdp;
- struct device *dev = rx_ring->dev;
u32 size;
- u16 i;
-
- /* ring already cleared, nothing to do */
- if (!rx_ring->rx_buf)
- return;
if (rx_ring->xsk_pool) {
ice_xsk_clean_rx_ring(rx_ring);
goto rx_skip_free;
}
- if (xdp->data) {
- xdp_return_buff(xdp);
- xdp->data = NULL;
- }
+ /* ring already cleared, nothing to do */
+ if (!rx_ring->rx_fqes)
+ return;
+
+ libeth_xdp_return_stash(&rx_ring->xdp);
/* Free all the Rx ring sk_buffs */
- for (i = 0; i < rx_ring->count; i++) {
- struct ice_rx_buf *rx_buf = &rx_ring->rx_buf[i];
+ for (u32 i = rx_ring->next_to_clean; i != rx_ring->next_to_use; ) {
+ libeth_rx_recycle_slow(rx_ring->rx_fqes[i].netmem);
- if (!rx_buf->page)
- continue;
+ if (rx_ring->hdr_pp)
+ libeth_rx_recycle_slow(rx_ring->hdr_fqes[i].netmem);
- /* Invalidate cache lines that may have been written to by
- * device so that we avoid corrupting memory.
- */
- dma_sync_single_range_for_cpu(dev, rx_buf->dma,
- rx_buf->page_offset,
- rx_ring->rx_buf_len,
- DMA_FROM_DEVICE);
-
- /* free resources associated with mapping */
- dma_unmap_page_attrs(dev, rx_buf->dma, ice_rx_pg_size(rx_ring),
- DMA_FROM_DEVICE, ICE_RX_DMA_ATTR);
- __page_frag_cache_drain(rx_buf->page, rx_buf->pagecnt_bias);
-
- rx_buf->page = NULL;
- rx_buf->page_offset = 0;
+ if (unlikely(++i == rx_ring->count))
+ i = 0;
}
-rx_skip_free:
- if (rx_ring->xsk_pool)
- memset(rx_ring->xdp_buf, 0, array_size(rx_ring->count, sizeof(*rx_ring->xdp_buf)));
- else
- memset(rx_ring->rx_buf, 0, array_size(rx_ring->count, sizeof(*rx_ring->rx_buf)));
+ if (rx_ring->vsi->type == ICE_VSI_PF &&
+ xdp_rxq_info_is_reg(&rx_ring->xdp_rxq)) {
+ xdp_rxq_info_detach_mem_model(&rx_ring->xdp_rxq);
+ xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
+ }
+ ice_rxq_pp_destroy(rx_ring);
+
+rx_skip_free:
/* Zero out the descriptor ring */
size = ALIGN(rx_ring->count * sizeof(union ice_32byte_rx_desc),
PAGE_SIZE);
@@ -568,7 +576,6 @@ rx_skip_free:
rx_ring->next_to_alloc = 0;
rx_ring->next_to_clean = 0;
- rx_ring->first_desc = 0;
rx_ring->next_to_use = 0;
}
@@ -580,26 +587,20 @@ rx_skip_free:
*/
void ice_free_rx_ring(struct ice_rx_ring *rx_ring)
{
+ struct device *dev = ice_pf_to_dev(rx_ring->vsi->back);
u32 size;
ice_clean_rx_ring(rx_ring);
- if (rx_ring->vsi->type == ICE_VSI_PF)
- if (xdp_rxq_info_is_reg(&rx_ring->xdp_rxq))
- xdp_rxq_info_unreg(&rx_ring->xdp_rxq);
WRITE_ONCE(rx_ring->xdp_prog, NULL);
if (rx_ring->xsk_pool) {
kfree(rx_ring->xdp_buf);
rx_ring->xdp_buf = NULL;
- } else {
- kfree(rx_ring->rx_buf);
- rx_ring->rx_buf = NULL;
}
if (rx_ring->desc) {
size = ALIGN(rx_ring->count * sizeof(union ice_32byte_rx_desc),
PAGE_SIZE);
- dmam_free_coherent(rx_ring->dev, size,
- rx_ring->desc, rx_ring->dma);
+ dmam_free_coherent(dev, size, rx_ring->desc, rx_ring->dma);
rx_ring->desc = NULL;
}
}
@@ -612,19 +613,9 @@ void ice_free_rx_ring(struct ice_rx_ring *rx_ring)
*/
int ice_setup_rx_ring(struct ice_rx_ring *rx_ring)
{
- struct device *dev = rx_ring->dev;
+ struct device *dev = ice_pf_to_dev(rx_ring->vsi->back);
u32 size;
- if (!dev)
- return -ENOMEM;
-
- /* warn if we are about to overwrite the pointer */
- WARN_ON(rx_ring->rx_buf);
- rx_ring->rx_buf =
- kcalloc(rx_ring->count, sizeof(*rx_ring->rx_buf), GFP_KERNEL);
- if (!rx_ring->rx_buf)
- return -ENOMEM;
-
/* round up to nearest page */
size = ALIGN(rx_ring->count * sizeof(union ice_32byte_rx_desc),
PAGE_SIZE);
@@ -633,22 +624,16 @@ int ice_setup_rx_ring(struct ice_rx_ring *rx_ring)
if (!rx_ring->desc) {
dev_err(dev, "Unable to allocate memory for the Rx descriptor ring, size=%d\n",
size);
- goto err;
+ return -ENOMEM;
}
rx_ring->next_to_use = 0;
rx_ring->next_to_clean = 0;
- rx_ring->first_desc = 0;
if (ice_is_xdp_ena_vsi(rx_ring->vsi))
WRITE_ONCE(rx_ring->xdp_prog, rx_ring->vsi->xdp_prog);
return 0;
-
-err:
- kfree(rx_ring->rx_buf);
- rx_ring->rx_buf = NULL;
- return -ENOMEM;
}
/**
@@ -662,7 +647,7 @@ err:
* Returns any of ICE_XDP_{PASS, CONSUMED, TX, REDIR}
*/
static u32
-ice_run_xdp(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
+ice_run_xdp(struct ice_rx_ring *rx_ring, struct libeth_xdp_buff *xdp,
struct bpf_prog *xdp_prog, struct ice_tx_ring *xdp_ring,
union ice_32b_rx_flex_desc *eop_desc)
{
@@ -672,23 +657,23 @@ ice_run_xdp(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
if (!xdp_prog)
goto exit;
- ice_xdp_meta_set_desc(xdp, eop_desc);
+ xdp->desc = eop_desc;
- act = bpf_prog_run_xdp(xdp_prog, xdp);
+ act = bpf_prog_run_xdp(xdp_prog, &xdp->base);
switch (act) {
case XDP_PASS:
break;
case XDP_TX:
if (static_branch_unlikely(&ice_xdp_locking_key))
spin_lock(&xdp_ring->tx_lock);
- ret = __ice_xmit_xdp_ring(xdp, xdp_ring, false);
+ ret = __ice_xmit_xdp_ring(&xdp->base, xdp_ring, false);
if (static_branch_unlikely(&ice_xdp_locking_key))
spin_unlock(&xdp_ring->tx_lock);
if (ret == ICE_XDP_CONSUMED)
goto out_failure;
break;
case XDP_REDIRECT:
- if (xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog))
+ if (xdp_do_redirect(rx_ring->netdev, &xdp->base, xdp_prog))
goto out_failure;
ret = ICE_XDP_REDIR;
break;
@@ -700,8 +685,10 @@ out_failure:
trace_xdp_exception(rx_ring->netdev, xdp_prog, act);
fallthrough;
case XDP_DROP:
+ libeth_xdp_return_buff(xdp);
ret = ICE_XDP_CONSUMED;
}
+
exit:
return ret;
}
@@ -790,53 +777,6 @@ ice_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
}
/**
- * ice_alloc_mapped_page - recycle or make a new page
- * @rx_ring: ring to use
- * @bi: rx_buf struct to modify
- *
- * Returns true if the page was successfully allocated or
- * reused.
- */
-static bool
-ice_alloc_mapped_page(struct ice_rx_ring *rx_ring, struct ice_rx_buf *bi)
-{
- struct page *page = bi->page;
- dma_addr_t dma;
-
- /* since we are recycling buffers we should seldom need to alloc */
- if (likely(page))
- return true;
-
- /* alloc new page for storage */
- page = dev_alloc_pages(ice_rx_pg_order(rx_ring));
- if (unlikely(!page)) {
- rx_ring->ring_stats->rx_stats.alloc_page_failed++;
- return false;
- }
-
- /* map page for use */
- dma = dma_map_page_attrs(rx_ring->dev, page, 0, ice_rx_pg_size(rx_ring),
- DMA_FROM_DEVICE, ICE_RX_DMA_ATTR);
-
- /* if mapping failed free memory back to system since
- * there isn't much point in holding memory we can't use
- */
- if (dma_mapping_error(rx_ring->dev, dma)) {
- __free_pages(page, ice_rx_pg_order(rx_ring));
- rx_ring->ring_stats->rx_stats.alloc_page_failed++;
- return false;
- }
-
- bi->dma = dma;
- bi->page = page;
- bi->page_offset = rx_ring->rx_offset;
- page_ref_add(page, USHRT_MAX - 1);
- bi->pagecnt_bias = USHRT_MAX;
-
- return true;
-}
-
-/**
* ice_init_ctrl_rx_descs - Initialize Rx descriptors for control vsi.
* @rx_ring: ring to init descriptors on
* @count: number of descriptors to initialize
@@ -882,9 +822,20 @@ void ice_init_ctrl_rx_descs(struct ice_rx_ring *rx_ring, u32 count)
*/
bool ice_alloc_rx_bufs(struct ice_rx_ring *rx_ring, unsigned int cleaned_count)
{
+ const struct libeth_fq_fp hdr_fq = {
+ .pp = rx_ring->hdr_pp,
+ .fqes = rx_ring->hdr_fqes,
+ .truesize = rx_ring->hdr_truesize,
+ .count = rx_ring->count,
+ };
+ const struct libeth_fq_fp fq = {
+ .pp = rx_ring->pp,
+ .fqes = rx_ring->rx_fqes,
+ .truesize = rx_ring->truesize,
+ .count = rx_ring->count,
+ };
union ice_32b_rx_flex_desc *rx_desc;
u16 ntu = rx_ring->next_to_use;
- struct ice_rx_buf *bi;
/* do nothing if no valid netdev defined */
if (!rx_ring->netdev || !cleaned_count)
@@ -892,30 +843,39 @@ bool ice_alloc_rx_bufs(struct ice_rx_ring *rx_ring, unsigned int cleaned_count)
/* get the Rx descriptor and buffer based on next_to_use */
rx_desc = ICE_RX_DESC(rx_ring, ntu);
- bi = &rx_ring->rx_buf[ntu];
do {
- /* if we fail here, we have work remaining */
- if (!ice_alloc_mapped_page(rx_ring, bi))
- break;
+ dma_addr_t addr;
- /* sync the buffer for use by the device */
- dma_sync_single_range_for_device(rx_ring->dev, bi->dma,
- bi->page_offset,
- rx_ring->rx_buf_len,
- DMA_FROM_DEVICE);
+ addr = libeth_rx_alloc(&fq, ntu);
+ if (addr == DMA_MAPPING_ERROR) {
+ rx_ring->ring_stats->rx_stats.alloc_page_failed++;
+ break;
+ }
/* Refresh the desc even if buffer_addrs didn't change
* because each write-back erases this info.
*/
- rx_desc->read.pkt_addr = cpu_to_le64(bi->dma + bi->page_offset);
+ rx_desc->read.pkt_addr = cpu_to_le64(addr);
+
+ if (!hdr_fq.pp)
+ goto next;
+
+ addr = libeth_rx_alloc(&hdr_fq, ntu);
+ if (addr == DMA_MAPPING_ERROR) {
+ rx_ring->ring_stats->rx_stats.alloc_page_failed++;
+
+ libeth_rx_recycle_slow(fq.fqes[ntu].netmem);
+ break;
+ }
+
+ rx_desc->read.hdr_addr = cpu_to_le64(addr);
+next:
rx_desc++;
- bi++;
ntu++;
if (unlikely(ntu == rx_ring->count)) {
rx_desc = ICE_RX_DESC(rx_ring, 0);
- bi = rx_ring->rx_buf;
ntu = 0;
}
@@ -932,402 +892,6 @@ bool ice_alloc_rx_bufs(struct ice_rx_ring *rx_ring, unsigned int cleaned_count)
}
/**
- * ice_rx_buf_adjust_pg_offset - Prepare Rx buffer for reuse
- * @rx_buf: Rx buffer to adjust
- * @size: Size of adjustment
- *
- * Update the offset within page so that Rx buf will be ready to be reused.
- * For systems with PAGE_SIZE < 8192 this function will flip the page offset
- * so the second half of page assigned to Rx buffer will be used, otherwise
- * the offset is moved by "size" bytes
- */
-static void
-ice_rx_buf_adjust_pg_offset(struct ice_rx_buf *rx_buf, unsigned int size)
-{
-#if (PAGE_SIZE < 8192)
- /* flip page offset to other buffer */
- rx_buf->page_offset ^= size;
-#else
- /* move offset up to the next cache line */
- rx_buf->page_offset += size;
-#endif
-}
-
-/**
- * ice_can_reuse_rx_page - Determine if page can be reused for another Rx
- * @rx_buf: buffer containing the page
- *
- * If page is reusable, we have a green light for calling ice_reuse_rx_page,
- * which will assign the current buffer to the buffer that next_to_alloc is
- * pointing to; otherwise, the DMA mapping needs to be destroyed and
- * page freed
- */
-static bool
-ice_can_reuse_rx_page(struct ice_rx_buf *rx_buf)
-{
- unsigned int pagecnt_bias = rx_buf->pagecnt_bias;
- struct page *page = rx_buf->page;
-
- /* avoid re-using remote and pfmemalloc pages */
- if (!dev_page_is_reusable(page))
- return false;
-
- /* if we are only owner of page we can reuse it */
- if (unlikely(rx_buf->pgcnt - pagecnt_bias > 1))
- return false;
-#if (PAGE_SIZE >= 8192)
-#define ICE_LAST_OFFSET \
- (SKB_WITH_OVERHEAD(PAGE_SIZE) - ICE_RXBUF_3072)
- if (rx_buf->page_offset > ICE_LAST_OFFSET)
- return false;
-#endif /* PAGE_SIZE >= 8192) */
-
- /* If we have drained the page fragment pool we need to update
- * the pagecnt_bias and page count so that we fully restock the
- * number of references the driver holds.
- */
- if (unlikely(pagecnt_bias == 1)) {
- page_ref_add(page, USHRT_MAX - 1);
- rx_buf->pagecnt_bias = USHRT_MAX;
- }
-
- return true;
-}
-
-/**
- * ice_add_xdp_frag - Add contents of Rx buffer to xdp buf as a frag
- * @rx_ring: Rx descriptor ring to transact packets on
- * @xdp: xdp buff to place the data into
- * @rx_buf: buffer containing page to add
- * @size: packet length from rx_desc
- *
- * This function will add the data contained in rx_buf->page to the xdp buf.
- * It will just attach the page as a frag.
- */
-static int
-ice_add_xdp_frag(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
- struct ice_rx_buf *rx_buf, const unsigned int size)
-{
- struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp);
-
- if (!size)
- return 0;
-
- if (!xdp_buff_has_frags(xdp)) {
- sinfo->nr_frags = 0;
- sinfo->xdp_frags_size = 0;
- xdp_buff_set_frags_flag(xdp);
- }
-
- if (unlikely(sinfo->nr_frags == MAX_SKB_FRAGS))
- return -ENOMEM;
-
- __skb_fill_page_desc_noacc(sinfo, sinfo->nr_frags++, rx_buf->page,
- rx_buf->page_offset, size);
- sinfo->xdp_frags_size += size;
-
- if (page_is_pfmemalloc(rx_buf->page))
- xdp_buff_set_frag_pfmemalloc(xdp);
-
- return 0;
-}
-
-/**
- * ice_reuse_rx_page - page flip buffer and store it back on the ring
- * @rx_ring: Rx descriptor ring to store buffers on
- * @old_buf: donor buffer to have page reused
- *
- * Synchronizes page for reuse by the adapter
- */
-static void
-ice_reuse_rx_page(struct ice_rx_ring *rx_ring, struct ice_rx_buf *old_buf)
-{
- u16 nta = rx_ring->next_to_alloc;
- struct ice_rx_buf *new_buf;
-
- new_buf = &rx_ring->rx_buf[nta];
-
- /* update, and store next to alloc */
- nta++;
- rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0;
-
- /* Transfer page from old buffer to new buffer.
- * Move each member individually to avoid possible store
- * forwarding stalls and unnecessary copy of skb.
- */
- new_buf->dma = old_buf->dma;
- new_buf->page = old_buf->page;
- new_buf->page_offset = old_buf->page_offset;
- new_buf->pagecnt_bias = old_buf->pagecnt_bias;
-}
-
-/**
- * ice_get_rx_buf - Fetch Rx buffer and synchronize data for use
- * @rx_ring: Rx descriptor ring to transact packets on
- * @size: size of buffer to add to skb
- * @ntc: index of next to clean element
- *
- * This function will pull an Rx buffer from the ring and synchronize it
- * for use by the CPU.
- */
-static struct ice_rx_buf *
-ice_get_rx_buf(struct ice_rx_ring *rx_ring, const unsigned int size,
- const unsigned int ntc)
-{
- struct ice_rx_buf *rx_buf;
-
- rx_buf = &rx_ring->rx_buf[ntc];
- prefetchw(rx_buf->page);
-
- if (!size)
- return rx_buf;
- /* we are reusing so sync this buffer for CPU use */
- dma_sync_single_range_for_cpu(rx_ring->dev, rx_buf->dma,
- rx_buf->page_offset, size,
- DMA_FROM_DEVICE);
-
- /* We have pulled a buffer for use, so decrement pagecnt_bias */
- rx_buf->pagecnt_bias--;
-
- return rx_buf;
-}
-
-/**
- * ice_get_pgcnts - grab page_count() for gathered fragments
- * @rx_ring: Rx descriptor ring to store the page counts on
- * @ntc: the next to clean element (not included in this frame!)
- *
- * This function is intended to be called right before running XDP
- * program so that the page recycling mechanism will be able to take
- * a correct decision regarding underlying pages; this is done in such
- * way as XDP program can change the refcount of page
- */
-static void ice_get_pgcnts(struct ice_rx_ring *rx_ring, unsigned int ntc)
-{
- u32 idx = rx_ring->first_desc;
- struct ice_rx_buf *rx_buf;
- u32 cnt = rx_ring->count;
-
- while (idx != ntc) {
- rx_buf = &rx_ring->rx_buf[idx];
- rx_buf->pgcnt = page_count(rx_buf->page);
-
- if (++idx == cnt)
- idx = 0;
- }
-}
-
-/**
- * ice_build_skb - Build skb around an existing buffer
- * @rx_ring: Rx descriptor ring to transact packets on
- * @xdp: xdp_buff pointing to the data
- *
- * This function builds an skb around an existing XDP buffer, taking care
- * to set up the skb correctly and avoid any memcpy overhead. Driver has
- * already combined frags (if any) to skb_shared_info.
- */
-static struct sk_buff *
-ice_build_skb(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp)
-{
- u8 metasize = xdp->data - xdp->data_meta;
- struct skb_shared_info *sinfo = NULL;
- unsigned int nr_frags;
- struct sk_buff *skb;
-
- if (unlikely(xdp_buff_has_frags(xdp))) {
- sinfo = xdp_get_shared_info_from_buff(xdp);
- nr_frags = sinfo->nr_frags;
- }
-
- /* Prefetch first cache line of first page. If xdp->data_meta
- * is unused, this points exactly as xdp->data, otherwise we
- * likely have a consumer accessing first few bytes of meta
- * data, and then actual data.
- */
- net_prefetch(xdp->data_meta);
- /* build an skb around the page buffer */
- skb = napi_build_skb(xdp->data_hard_start, xdp->frame_sz);
- if (unlikely(!skb))
- return NULL;
-
- /* must to record Rx queue, otherwise OS features such as
- * symmetric queue won't work
- */
- skb_record_rx_queue(skb, rx_ring->q_index);
-
- /* update pointers within the skb to store the data */
- skb_reserve(skb, xdp->data - xdp->data_hard_start);
- __skb_put(skb, xdp->data_end - xdp->data);
- if (metasize)
- skb_metadata_set(skb, metasize);
-
- if (unlikely(xdp_buff_has_frags(xdp)))
- xdp_update_skb_frags_info(skb, nr_frags, sinfo->xdp_frags_size,
- nr_frags * xdp->frame_sz,
- xdp_buff_get_skb_flags(xdp));
-
- return skb;
-}
-
-/**
- * ice_construct_skb - Allocate skb and populate it
- * @rx_ring: Rx descriptor ring to transact packets on
- * @xdp: xdp_buff pointing to the data
- *
- * This function allocates an skb. It then populates it with the page
- * data from the current receive descriptor, taking care to set up the
- * skb correctly.
- */
-static struct sk_buff *
-ice_construct_skb(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp)
-{
- unsigned int size = xdp->data_end - xdp->data;
- struct skb_shared_info *sinfo = NULL;
- struct ice_rx_buf *rx_buf;
- unsigned int nr_frags = 0;
- unsigned int headlen;
- struct sk_buff *skb;
-
- /* prefetch first cache line of first page */
- net_prefetch(xdp->data);
-
- if (unlikely(xdp_buff_has_frags(xdp))) {
- sinfo = xdp_get_shared_info_from_buff(xdp);
- nr_frags = sinfo->nr_frags;
- }
-
- /* allocate a skb to store the frags */
- skb = napi_alloc_skb(&rx_ring->q_vector->napi, ICE_RX_HDR_SIZE);
- if (unlikely(!skb))
- return NULL;
-
- rx_buf = &rx_ring->rx_buf[rx_ring->first_desc];
- skb_record_rx_queue(skb, rx_ring->q_index);
- /* Determine available headroom for copy */
- headlen = size;
- if (headlen > ICE_RX_HDR_SIZE)
- headlen = eth_get_headlen(skb->dev, xdp->data, ICE_RX_HDR_SIZE);
-
- /* align pull length to size of long to optimize memcpy performance */
- memcpy(__skb_put(skb, headlen), xdp->data, ALIGN(headlen,
- sizeof(long)));
-
- /* if we exhaust the linear part then add what is left as a frag */
- size -= headlen;
- if (size) {
- /* besides adding here a partial frag, we are going to add
- * frags from xdp_buff, make sure there is enough space for
- * them
- */
- if (unlikely(nr_frags >= MAX_SKB_FRAGS - 1)) {
- dev_kfree_skb(skb);
- return NULL;
- }
- skb_add_rx_frag(skb, 0, rx_buf->page,
- rx_buf->page_offset + headlen, size,
- xdp->frame_sz);
- } else {
- /* buffer is unused, restore biased page count in Rx buffer;
- * data was copied onto skb's linear part so there's no
- * need for adjusting page offset and we can reuse this buffer
- * as-is
- */
- rx_buf->pagecnt_bias++;
- }
-
- if (unlikely(xdp_buff_has_frags(xdp))) {
- struct skb_shared_info *skinfo = skb_shinfo(skb);
-
- memcpy(&skinfo->frags[skinfo->nr_frags], &sinfo->frags[0],
- sizeof(skb_frag_t) * nr_frags);
-
- xdp_update_skb_frags_info(skb, skinfo->nr_frags + nr_frags,
- sinfo->xdp_frags_size,
- nr_frags * xdp->frame_sz,
- xdp_buff_get_skb_flags(xdp));
- }
-
- return skb;
-}
-
-/**
- * ice_put_rx_buf - Clean up used buffer and either recycle or free
- * @rx_ring: Rx descriptor ring to transact packets on
- * @rx_buf: Rx buffer to pull data from
- *
- * This function will clean up the contents of the rx_buf. It will either
- * recycle the buffer or unmap it and free the associated resources.
- */
-static void
-ice_put_rx_buf(struct ice_rx_ring *rx_ring, struct ice_rx_buf *rx_buf)
-{
- if (!rx_buf)
- return;
-
- if (ice_can_reuse_rx_page(rx_buf)) {
- /* hand second half of page back to the ring */
- ice_reuse_rx_page(rx_ring, rx_buf);
- } else {
- /* we are not reusing the buffer so unmap it */
- dma_unmap_page_attrs(rx_ring->dev, rx_buf->dma,
- ice_rx_pg_size(rx_ring), DMA_FROM_DEVICE,
- ICE_RX_DMA_ATTR);
- __page_frag_cache_drain(rx_buf->page, rx_buf->pagecnt_bias);
- }
-
- /* clear contents of buffer_info */
- rx_buf->page = NULL;
-}
-
-/**
- * ice_put_rx_mbuf - ice_put_rx_buf() caller, for all buffers in frame
- * @rx_ring: Rx ring with all the auxiliary data
- * @xdp: XDP buffer carrying linear + frags part
- * @ntc: the next to clean element (not included in this frame!)
- * @verdict: return code from XDP program execution
- *
- * Called after XDP program is completed, or on error with verdict set to
- * ICE_XDP_CONSUMED.
- *
- * Walk through buffers from first_desc to the end of the frame, releasing
- * buffers and satisfying internal page recycle mechanism. The action depends
- * on verdict from XDP program.
- */
-static void ice_put_rx_mbuf(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp,
- u32 ntc, u32 verdict)
-{
- u32 idx = rx_ring->first_desc;
- u32 cnt = rx_ring->count;
- struct ice_rx_buf *buf;
- u32 xdp_frags = 0;
- int i = 0;
-
- if (unlikely(xdp_buff_has_frags(xdp)))
- xdp_frags = xdp_get_shared_info_from_buff(xdp)->nr_frags;
-
- while (idx != ntc) {
- buf = &rx_ring->rx_buf[idx];
- if (++idx == cnt)
- idx = 0;
-
- /* An XDP program could release fragments from the end of the
- * buffer. For these, we need to keep the pagecnt_bias as-is.
- * To do this, only adjust pagecnt_bias for fragments up to
- * the total remaining after the XDP program has run.
- */
- if (verdict != ICE_XDP_CONSUMED)
- ice_rx_buf_adjust_pg_offset(buf, xdp->frame_sz);
- else if (i++ <= xdp_frags)
- buf->pagecnt_bias++;
-
- ice_put_rx_buf(rx_ring, buf);
- }
-
- xdp->data = NULL;
- rx_ring->first_desc = ntc;
-}
-
-/**
* ice_clean_ctrl_rx_irq - Clean descriptors from flow director Rx ring
* @rx_ring: Rx descriptor ring for ctrl_vsi to transact packets on
*
@@ -1361,9 +925,8 @@ void ice_clean_ctrl_rx_irq(struct ice_rx_ring *rx_ring)
total_rx_pkts++;
}
- rx_ring->first_desc = ntc;
rx_ring->next_to_clean = ntc;
- ice_init_ctrl_rx_descs(rx_ring, ICE_RX_DESC_UNUSED(rx_ring));
+ ice_init_ctrl_rx_descs(rx_ring, ICE_DESC_UNUSED(rx_ring));
}
/**
@@ -1381,16 +944,17 @@ void ice_clean_ctrl_rx_irq(struct ice_rx_ring *rx_ring)
static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
{
unsigned int total_rx_bytes = 0, total_rx_pkts = 0;
- unsigned int offset = rx_ring->rx_offset;
- struct xdp_buff *xdp = &rx_ring->xdp;
struct ice_tx_ring *xdp_ring = NULL;
struct bpf_prog *xdp_prog = NULL;
u32 ntc = rx_ring->next_to_clean;
+ LIBETH_XDP_ONSTACK_BUFF(xdp);
u32 cached_ntu, xdp_verdict;
u32 cnt = rx_ring->count;
u32 xdp_xmit = 0;
bool failure;
+ libeth_xdp_init_buff(xdp, &rx_ring->xdp, &rx_ring->xdp_rxq);
+
xdp_prog = READ_ONCE(rx_ring->xdp_prog);
if (xdp_prog) {
xdp_ring = rx_ring->xdp_ring;
@@ -1400,19 +964,21 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
/* start the loop to process Rx packets bounded by 'budget' */
while (likely(total_rx_pkts < (unsigned int)budget)) {
union ice_32b_rx_flex_desc *rx_desc;
- struct ice_rx_buf *rx_buf;
+ struct libeth_fqe *rx_buf;
struct sk_buff *skb;
unsigned int size;
u16 stat_err_bits;
u16 vlan_tci;
+ bool rxe;
/* get the Rx desc from Rx ring based on 'next_to_clean' */
rx_desc = ICE_RX_DESC(rx_ring, ntc);
- /* status_error_len will always be zero for unused descriptors
- * because it's cleared in cleanup, and overlaps with hdr_addr
- * which is always zero because packet split isn't used, if the
- * hardware wrote DD then it will be non-zero
+ /*
+ * The DD bit will always be zero for unused descriptors
+ * because it's cleared in cleanup or when setting the DMA
+ * address of the header buffer, which never uses the DD bit.
+ * If the hardware wrote the descriptor, it will be non-zero.
*/
stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_DD_S);
if (!ice_test_staterr(rx_desc->wb.status_error0, stat_err_bits))
@@ -1426,71 +992,65 @@ static int ice_clean_rx_irq(struct ice_rx_ring *rx_ring, int budget)
ice_trace(clean_rx_irq, rx_ring, rx_desc);
+ stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_HBO_S) |
+ BIT(ICE_RX_FLEX_DESC_STATUS0_RXE_S);
+ rxe = ice_test_staterr(rx_desc->wb.status_error0,
+ stat_err_bits);
+
+ if (!rx_ring->hdr_pp)
+ goto payload;
+
+ size = le16_get_bits(rx_desc->wb.hdr_len_sph_flex_flags1,
+ ICE_RX_FLEX_DESC_HDR_LEN_M);
+ if (unlikely(rxe))
+ size = 0;
+
+ rx_buf = &rx_ring->hdr_fqes[ntc];
+ libeth_xdp_process_buff(xdp, rx_buf, size);
+ rx_buf->netmem = 0;
+
+payload:
size = le16_to_cpu(rx_desc->wb.pkt_len) &
ICE_RX_FLX_DESC_PKT_LEN_M;
+ if (unlikely(rxe))
+ size = 0;
/* retrieve a buffer from the ring */
- rx_buf = ice_get_rx_buf(rx_ring, size, ntc);
+ rx_buf = &rx_ring->rx_fqes[ntc];
+ libeth_xdp_process_buff(xdp, rx_buf, size);
- /* Increment ntc before calls to ice_put_rx_mbuf() */
if (++ntc == cnt)
ntc = 0;
- if (!xdp->data) {
- void *hard_start;
-
- hard_start = page_address(rx_buf->page) + rx_buf->page_offset -
- offset;
- xdp_prepare_buff(xdp, hard_start, offset, size, !!offset);
- xdp_buff_clear_frags_flag(xdp);
- } else if (ice_add_xdp_frag(rx_ring, xdp, rx_buf, size)) {
- ice_put_rx_mbuf(rx_ring, xdp, ntc, ICE_XDP_CONSUMED);
- break;
- }
-
/* skip if it is NOP desc */
- if (ice_is_non_eop(rx_ring, rx_desc))
+ if (ice_is_non_eop(rx_ring, rx_desc) || unlikely(!xdp->data))
continue;
- ice_get_pgcnts(rx_ring, ntc);
xdp_verdict = ice_run_xdp(rx_ring, xdp, xdp_prog, xdp_ring, rx_desc);
if (xdp_verdict == ICE_XDP_PASS)
goto construct_skb;
- total_rx_bytes += xdp_get_buff_len(xdp);
- total_rx_pkts++;
- ice_put_rx_mbuf(rx_ring, xdp, ntc, xdp_verdict);
- xdp_xmit |= xdp_verdict & (ICE_XDP_TX | ICE_XDP_REDIR);
+ if (xdp_verdict & (ICE_XDP_TX | ICE_XDP_REDIR))
+ xdp_xmit |= xdp_verdict;
+ total_rx_bytes += xdp_get_buff_len(&xdp->base);
+ total_rx_pkts++;
+ xdp->data = NULL;
continue;
+
construct_skb:
- if (likely(ice_ring_uses_build_skb(rx_ring)))
- skb = ice_build_skb(rx_ring, xdp);
- else
- skb = ice_construct_skb(rx_ring, xdp);
+ skb = xdp_build_skb_from_buff(&xdp->base);
+ xdp->data = NULL;
+
/* exit if we failed to retrieve a buffer */
if (!skb) {
+ libeth_xdp_return_buff_slow(xdp);
rx_ring->ring_stats->rx_stats.alloc_buf_failed++;
- xdp_verdict = ICE_XDP_CONSUMED;
- }
- ice_put_rx_mbuf(rx_ring, xdp, ntc, xdp_verdict);
-
- if (!skb)
- break;
-
- stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_RXE_S);
- if (unlikely(ice_test_staterr(rx_desc->wb.status_error0,
- stat_err_bits))) {
- dev_kfree_skb_any(skb);
continue;
}
vlan_tci = ice_get_vlan_tci(rx_desc);
- /* pad the skb if needed, to make a valid ethernet frame */
- if (eth_skb_pad(skb))
- continue;
-
/* probably a little skewed due to removing CRC */
total_rx_bytes += skb->len;
@@ -1507,11 +1067,13 @@ construct_skb:
rx_ring->next_to_clean = ntc;
/* return up to cleaned_count buffers to hardware */
- failure = ice_alloc_rx_bufs(rx_ring, ICE_RX_DESC_UNUSED(rx_ring));
+ failure = ice_alloc_rx_bufs(rx_ring, ICE_DESC_UNUSED(rx_ring));
if (xdp_xmit)
ice_finalize_xdp_rx(xdp_ring, xdp_xmit, cached_ntu);
+ libeth_xdp_save_buff(&rx_ring->xdp, xdp);
+
if (rx_ring->ring_stats)
ice_update_rx_ring_stats(rx_ring, total_rx_pkts,
total_rx_bytes);
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx.h b/drivers/net/ethernet/intel/ice/ice_txrx.h
index 841a07bfba54..e440c55d9e9f 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx.h
+++ b/drivers/net/ethernet/intel/ice/ice_txrx.h
@@ -4,6 +4,8 @@
#ifndef _ICE_TXRX_H_
#define _ICE_TXRX_H_
+#include <net/libeth/types.h>
+
#include "ice_type.h"
#define ICE_DFLT_IRQ_WORK 256
@@ -27,72 +29,6 @@
#define ICE_MAX_TXQ_PER_TXQG 128
-/* Attempt to maximize the headroom available for incoming frames. We use a 2K
- * buffer for MTUs <= 1500 and need 1536/1534 to store the data for the frame.
- * This leaves us with 512 bytes of room. From that we need to deduct the
- * space needed for the shared info and the padding needed to IP align the
- * frame.
- *
- * Note: For cache line sizes 256 or larger this value is going to end
- * up negative. In these cases we should fall back to the legacy
- * receive path.
- */
-#if (PAGE_SIZE < 8192)
-#define ICE_2K_TOO_SMALL_WITH_PADDING \
- ((unsigned int)(NET_SKB_PAD + ICE_RXBUF_1536) > \
- SKB_WITH_OVERHEAD(ICE_RXBUF_2048))
-
-/**
- * ice_compute_pad - compute the padding
- * @rx_buf_len: buffer length
- *
- * Figure out the size of half page based on given buffer length and
- * then subtract the skb_shared_info followed by subtraction of the
- * actual buffer length; this in turn results in the actual space that
- * is left for padding usage
- */
-static inline int ice_compute_pad(int rx_buf_len)
-{
- int half_page_size;
-
- half_page_size = ALIGN(rx_buf_len, PAGE_SIZE / 2);
- return SKB_WITH_OVERHEAD(half_page_size) - rx_buf_len;
-}
-
-/**
- * ice_skb_pad - determine the padding that we can supply
- *
- * Figure out the right Rx buffer size and based on that calculate the
- * padding
- */
-static inline int ice_skb_pad(void)
-{
- int rx_buf_len;
-
- /* If a 2K buffer cannot handle a standard Ethernet frame then
- * optimize padding for a 3K buffer instead of a 1.5K buffer.
- *
- * For a 3K buffer we need to add enough padding to allow for
- * tailroom due to NET_IP_ALIGN possibly shifting us out of
- * cache-line alignment.
- */
- if (ICE_2K_TOO_SMALL_WITH_PADDING)
- rx_buf_len = ICE_RXBUF_3072 + SKB_DATA_ALIGN(NET_IP_ALIGN);
- else
- rx_buf_len = ICE_RXBUF_1536;
-
- /* if needed make room for NET_IP_ALIGN */
- rx_buf_len -= NET_IP_ALIGN;
-
- return ice_compute_pad(rx_buf_len);
-}
-
-#define ICE_SKB_PAD ice_skb_pad()
-#else
-#define ICE_2K_TOO_SMALL_WITH_PADDING false
-#define ICE_SKB_PAD (NET_SKB_PAD + NET_IP_ALIGN)
-#endif
-
/* We are assuming that the cache line is always 64 Bytes here for ice.
* In order to make sure that is a correct assumption there is a check in probe
* to print a warning if the read from GLPCI_CNF2 tells us that the cache line
@@ -112,10 +48,6 @@ static inline int ice_skb_pad(void)
(u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
(R)->next_to_clean - (R)->next_to_use - 1)
-#define ICE_RX_DESC_UNUSED(R) \
- ((((R)->first_desc > (R)->next_to_use) ? 0 : (R)->count) + \
- (R)->first_desc - (R)->next_to_use - 1)
-
#define ICE_RING_QUARTER(R) ((R)->count >> 2)
#define ICE_TX_FLAGS_TSO BIT(0)
@@ -197,14 +129,6 @@ struct ice_tx_offload_params {
u8 header_len;
};
-struct ice_rx_buf {
- dma_addr_t dma;
- struct page *page;
- unsigned int page_offset;
- unsigned int pgcnt;
- unsigned int pagecnt_bias;
-};
-
struct ice_q_stats {
u64 pkts;
u64 bytes;
@@ -262,15 +186,6 @@ struct ice_pkt_ctx {
__be16 vlan_proto;
};
-struct ice_xdp_buff {
- struct xdp_buff xdp_buff;
- const union ice_32b_rx_flex_desc *eop_desc;
- const struct ice_pkt_ctx *pkt_ctx;
-};
-
-/* Required for compatibility with xdp_buffs from xsk_pool */
-static_assert(offsetof(struct ice_xdp_buff, xdp_buff) == 0);
-
/* indices into GLINT_ITR registers */
#define ICE_RX_ITR ICE_IDX_ITR0
#define ICE_TX_ITR ICE_IDX_ITR1
@@ -323,7 +238,7 @@ struct ice_tstamp_ring {
struct ice_rx_ring {
/* CL1 - 1st cacheline starts here */
void *desc; /* Descriptor ring memory */
- struct device *dev; /* Used for DMA mapping */
+ struct page_pool *pp;
struct net_device *netdev; /* netdev ring maps to */
struct ice_vsi *vsi; /* Backreference to associated VSI */
struct ice_q_vector *q_vector; /* Backreference to associated vector */
@@ -335,14 +250,19 @@ struct ice_rx_ring {
u16 next_to_alloc;
union {
- struct ice_rx_buf *rx_buf;
+ struct libeth_fqe *rx_fqes;
struct xdp_buff **xdp_buf;
};
+
/* CL2 - 2nd cacheline starts here */
+ struct libeth_fqe *hdr_fqes;
+ struct page_pool *hdr_pp;
+
union {
- struct ice_xdp_buff xdp_ext;
- struct xdp_buff xdp;
+ struct libeth_xdp_buff_stash xdp;
+ struct libeth_xdp_buff *xsk;
};
+
/* CL3 - 3rd cacheline starts here */
union {
struct ice_pkt_ctx pkt_ctx;
@@ -352,12 +272,13 @@ struct ice_rx_ring {
};
};
struct bpf_prog *xdp_prog;
- u16 rx_offset;
/* used in interrupt processing */
u16 next_to_use;
u16 next_to_clean;
- u16 first_desc;
+
+ u32 hdr_truesize;
+ u32 truesize;
/* stats structs */
struct ice_ring_stats *ring_stats;
@@ -368,12 +289,11 @@ struct ice_rx_ring {
struct ice_tx_ring *xdp_ring;
struct ice_rx_ring *next; /* pointer to next ring in q_vector */
struct xsk_buff_pool *xsk_pool;
- u16 max_frame;
+ u16 rx_hdr_len;
u16 rx_buf_len;
dma_addr_t dma; /* physical address of ring */
u8 dcb_tc; /* Traffic class of ring */
u8 ptp_rx;
-#define ICE_RX_FLAGS_RING_BUILD_SKB BIT(1)
#define ICE_RX_FLAGS_CRC_STRIP_DIS BIT(2)
#define ICE_RX_FLAGS_MULTIDEV BIT(3)
#define ICE_RX_FLAGS_RING_GCS BIT(4)
@@ -422,21 +342,6 @@ struct ice_tx_ring {
u16 quanta_prof_id;
} ____cacheline_internodealigned_in_smp;
-static inline bool ice_ring_uses_build_skb(struct ice_rx_ring *ring)
-{
- return !!(ring->flags & ICE_RX_FLAGS_RING_BUILD_SKB);
-}
-
-static inline void ice_set_ring_build_skb_ena(struct ice_rx_ring *ring)
-{
- ring->flags |= ICE_RX_FLAGS_RING_BUILD_SKB;
-}
-
-static inline void ice_clear_ring_build_skb_ena(struct ice_rx_ring *ring)
-{
- ring->flags &= ~ICE_RX_FLAGS_RING_BUILD_SKB;
-}
-
static inline bool ice_ring_ch_enabled(struct ice_tx_ring *ring)
{
return !!ring->ch;
@@ -491,18 +396,13 @@ struct ice_coalesce_stored {
static inline unsigned int ice_rx_pg_order(struct ice_rx_ring *ring)
{
-#if (PAGE_SIZE < 8192)
- if (ring->rx_buf_len > (PAGE_SIZE / 2))
- return 1;
-#endif
return 0;
}
-#define ice_rx_pg_size(_ring) (PAGE_SIZE << ice_rx_pg_order(_ring))
-
union ice_32b_rx_flex_desc;
void ice_init_ctrl_rx_descs(struct ice_rx_ring *rx_ring, u32 num_descs);
+void ice_rxq_pp_destroy(struct ice_rx_ring *rq);
bool ice_alloc_rx_bufs(struct ice_rx_ring *rxr, unsigned int cleaned_count);
netdev_tx_t ice_start_xmit(struct sk_buff *skb, struct net_device *netdev);
u16
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c
index 45cfaabc41cb..956da38d63b0 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.c
@@ -3,6 +3,7 @@
#include <linux/filter.h>
#include <linux/net/intel/libie/rx.h>
+#include <net/libeth/xdp.h>
#include "ice_txrx_lib.h"
#include "ice_eswitch.h"
@@ -230,9 +231,12 @@ ice_process_skb_fields(struct ice_rx_ring *rx_ring,
if (ice_is_port_repr_netdev(netdev))
ice_repr_inc_rx_stats(netdev, skb->len);
+
+ /* __skb_push() is needed because xdp_build_skb_from_buff()
+ * calls eth_type_trans()
+ */
+ __skb_push(skb, ETH_HLEN);
skb->protocol = eth_type_trans(skb, netdev);
- } else {
- skb->protocol = eth_type_trans(skb, rx_ring->netdev);
}
ice_rx_csum(rx_ring, skb, rx_desc, ptype);
@@ -270,19 +274,18 @@ static void
ice_clean_xdp_tx_buf(struct device *dev, struct ice_tx_buf *tx_buf,
struct xdp_frame_bulk *bq)
{
- dma_unmap_single(dev, dma_unmap_addr(tx_buf, dma),
- dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
- dma_unmap_len_set(tx_buf, len, 0);
-
switch (tx_buf->type) {
case ICE_TX_BUF_XDP_TX:
- page_frag_free(tx_buf->raw_buf);
+ libeth_xdp_return_va(tx_buf->raw_buf, true);
break;
case ICE_TX_BUF_XDP_XMIT:
+ dma_unmap_single(dev, dma_unmap_addr(tx_buf, dma),
+ dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
xdp_return_frame_bulk(tx_buf->xdpf, bq);
break;
}
+ dma_unmap_len_set(tx_buf, len, 0);
tx_buf->type = ICE_TX_BUF_EMPTY;
}
@@ -377,9 +380,11 @@ int __ice_xmit_xdp_ring(struct xdp_buff *xdp, struct ice_tx_ring *xdp_ring,
struct ice_tx_buf *tx_buf;
u32 cnt = xdp_ring->count;
void *data = xdp->data;
+ struct page *page;
u32 nr_frags = 0;
u32 free_space;
u32 frag = 0;
+ u32 offset;
free_space = ICE_DESC_UNUSED(xdp_ring);
if (free_space < ICE_RING_QUARTER(xdp_ring))
@@ -399,24 +404,28 @@ int __ice_xmit_xdp_ring(struct xdp_buff *xdp, struct ice_tx_ring *xdp_ring,
tx_head = &xdp_ring->tx_buf[ntu];
tx_buf = tx_head;
+ page = virt_to_page(data);
+ offset = offset_in_page(xdp->data);
+
for (;;) {
dma_addr_t dma;
- dma = dma_map_single(dev, data, size, DMA_TO_DEVICE);
- if (dma_mapping_error(dev, dma))
- goto dma_unmap;
-
- /* record length, and DMA address */
- dma_unmap_len_set(tx_buf, len, size);
- dma_unmap_addr_set(tx_buf, dma, dma);
-
if (frame) {
+ dma = dma_map_single(dev, data, size, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma))
+ goto dma_unmap;
tx_buf->type = ICE_TX_BUF_FRAG;
} else {
+ dma = page_pool_get_dma_addr(page) + offset;
+ dma_sync_single_for_device(dev, dma, size, DMA_BIDIRECTIONAL);
tx_buf->type = ICE_TX_BUF_XDP_TX;
tx_buf->raw_buf = data;
}
+ /* record length, and DMA address */
+ dma_unmap_len_set(tx_buf, len, size);
+ dma_unmap_addr_set(tx_buf, dma, dma);
+
tx_desc->buf_addr = cpu_to_le64(dma);
tx_desc->cmd_type_offset_bsz = ice_build_ctob(0, 0, size, 0);
@@ -430,6 +439,8 @@ int __ice_xmit_xdp_ring(struct xdp_buff *xdp, struct ice_tx_ring *xdp_ring,
tx_desc = ICE_TX_DESC(xdp_ring, ntu);
tx_buf = &xdp_ring->tx_buf[ntu];
+ page = skb_frag_page(&sinfo->frags[frag]);
+ offset = skb_frag_off(&sinfo->frags[frag]);
data = skb_frag_address(&sinfo->frags[frag]);
size = skb_frag_size(&sinfo->frags[frag]);
frag++;
@@ -514,10 +525,13 @@ void ice_finalize_xdp_rx(struct ice_tx_ring *xdp_ring, unsigned int xdp_res,
*/
static int ice_xdp_rx_hw_ts(const struct xdp_md *ctx, u64 *ts_ns)
{
- const struct ice_xdp_buff *xdp_ext = (void *)ctx;
+ const struct libeth_xdp_buff *xdp_ext = (void *)ctx;
+ struct ice_rx_ring *rx_ring;
- *ts_ns = ice_ptp_get_rx_hwts(xdp_ext->eop_desc,
- xdp_ext->pkt_ctx);
+ rx_ring = libeth_xdp_buff_to_rq(xdp_ext, typeof(*rx_ring), xdp_rxq);
+
+ *ts_ns = ice_ptp_get_rx_hwts(xdp_ext->desc,
+ &rx_ring->pkt_ctx);
if (!*ts_ns)
return -ENODATA;
@@ -545,10 +559,10 @@ ice_xdp_rx_hash_type(const union ice_32b_rx_flex_desc *eop_desc)
static int ice_xdp_rx_hash(const struct xdp_md *ctx, u32 *hash,
enum xdp_rss_hash_type *rss_type)
{
- const struct ice_xdp_buff *xdp_ext = (void *)ctx;
+ const struct libeth_xdp_buff *xdp_ext = (void *)ctx;
- *hash = ice_get_rx_hash(xdp_ext->eop_desc);
- *rss_type = ice_xdp_rx_hash_type(xdp_ext->eop_desc);
+ *hash = ice_get_rx_hash(xdp_ext->desc);
+ *rss_type = ice_xdp_rx_hash_type(xdp_ext->desc);
if (!likely(*hash))
return -ENODATA;
@@ -567,13 +581,16 @@ static int ice_xdp_rx_hash(const struct xdp_md *ctx, u32 *hash,
static int ice_xdp_rx_vlan_tag(const struct xdp_md *ctx, __be16 *vlan_proto,
u16 *vlan_tci)
{
- const struct ice_xdp_buff *xdp_ext = (void *)ctx;
+ const struct libeth_xdp_buff *xdp_ext = (void *)ctx;
+ struct ice_rx_ring *rx_ring;
+
+ rx_ring = libeth_xdp_buff_to_rq(xdp_ext, typeof(*rx_ring), xdp_rxq);
- *vlan_proto = xdp_ext->pkt_ctx->vlan_proto;
+ *vlan_proto = rx_ring->pkt_ctx.vlan_proto;
if (!*vlan_proto)
return -ENODATA;
- *vlan_tci = ice_get_vlan_tci(xdp_ext->eop_desc);
+ *vlan_tci = ice_get_vlan_tci(xdp_ext->desc);
if (!*vlan_tci)
return -ENODATA;
diff --git a/drivers/net/ethernet/intel/ice/ice_txrx_lib.h b/drivers/net/ethernet/intel/ice/ice_txrx_lib.h
index 99717730f21a..6a3f10f7a53f 100644
--- a/drivers/net/ethernet/intel/ice/ice_txrx_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_txrx_lib.h
@@ -135,13 +135,4 @@ ice_process_skb_fields(struct ice_rx_ring *rx_ring,
void
ice_receive_skb(struct ice_rx_ring *rx_ring, struct sk_buff *skb, u16 vlan_tci);
-static inline void
-ice_xdp_meta_set_desc(struct xdp_buff *xdp,
- union ice_32b_rx_flex_desc *eop_desc)
-{
- struct ice_xdp_buff *xdp_ext = container_of(xdp, struct ice_xdp_buff,
- xdp_buff);
-
- xdp_ext->eop_desc = eop_desc;
-}
#endif /* !_ICE_TXRX_LIB_H_ */
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index b0a1b67071c5..6a2ec8389a8f 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -1063,6 +1063,7 @@ struct ice_hw_port_stats {
u64 error_bytes; /* errbc */
u64 mac_local_faults; /* mlfc */
u64 mac_remote_faults; /* mrfc */
+ u64 rx_len_errors; /* rlec */
u64 link_xon_rx; /* lxonrxc */
u64 link_xoff_rx; /* lxoffrxc */
u64 link_xon_tx; /* lxontxc */
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_lib.h b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
index b00708907176..7a9c75d1d07c 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_vf_lib.h
@@ -53,6 +53,46 @@ struct ice_mdd_vf_events {
u16 last_printed;
};
+enum ice_hash_ip_ctx_type {
+ ICE_HASH_IP_CTX_IP = 0,
+ ICE_HASH_IP_CTX_IP_ESP,
+ ICE_HASH_IP_CTX_IP_UDP_ESP,
+ ICE_HASH_IP_CTX_IP_AH,
+ ICE_HASH_IP_CTX_IP_PFCP,
+ ICE_HASH_IP_CTX_IP_UDP,
+ ICE_HASH_IP_CTX_IP_TCP,
+ ICE_HASH_IP_CTX_IP_SCTP,
+ ICE_HASH_IP_CTX_MAX,
+};
+
+struct ice_vf_hash_ip_ctx {
+ struct ice_rss_hash_cfg ctx[ICE_HASH_IP_CTX_MAX];
+};
+
+enum ice_hash_gtpu_ctx_type {
+ ICE_HASH_GTPU_CTX_EH_IP = 0,
+ ICE_HASH_GTPU_CTX_EH_IP_UDP,
+ ICE_HASH_GTPU_CTX_EH_IP_TCP,
+ ICE_HASH_GTPU_CTX_UP_IP,
+ ICE_HASH_GTPU_CTX_UP_IP_UDP,
+ ICE_HASH_GTPU_CTX_UP_IP_TCP,
+ ICE_HASH_GTPU_CTX_DW_IP,
+ ICE_HASH_GTPU_CTX_DW_IP_UDP,
+ ICE_HASH_GTPU_CTX_DW_IP_TCP,
+ ICE_HASH_GTPU_CTX_MAX,
+};
+
+struct ice_vf_hash_gtpu_ctx {
+ struct ice_rss_hash_cfg ctx[ICE_HASH_GTPU_CTX_MAX];
+};
+
+struct ice_vf_hash_ctx {
+ struct ice_vf_hash_ip_ctx v4;
+ struct ice_vf_hash_ip_ctx v6;
+ struct ice_vf_hash_gtpu_ctx ipv4;
+ struct ice_vf_hash_gtpu_ctx ipv6;
+};
+
/* Structure to store fdir fv entry */
struct ice_fdir_prof_info {
struct ice_parser_profile prof;
@@ -66,6 +106,12 @@ struct ice_vf_qs_bw {
u8 tc;
};
+/* Structure to store RSS field vector entry */
+struct ice_rss_prof_info {
+ struct ice_parser_profile prof;
+ bool symm;
+};
+
/* VF operations */
struct ice_vf_ops {
enum ice_disq_rst_src reset_type;
@@ -106,6 +152,8 @@ struct ice_vf {
u16 ctrl_vsi_idx;
struct ice_vf_fdir fdir;
struct ice_fdir_prof_info fdir_prof_info[ICE_MAX_PTGS];
+ struct ice_rss_prof_info rss_prof_info[ICE_MAX_PTGS];
+ struct ice_vf_hash_ctx hash_ctx;
u64 rss_hashcfg; /* RSS hash configuration */
struct ice_sw *vf_sw_id; /* switch ID the VF VSIs connect to */
struct virtchnl_version_info vf_ver;
diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c
index 575fd48f485f..989ff1fd9110 100644
--- a/drivers/net/ethernet/intel/ice/ice_xsk.c
+++ b/drivers/net/ethernet/intel/ice/ice_xsk.c
@@ -3,6 +3,7 @@
#include <linux/bpf_trace.h>
#include <linux/unroll.h>
+#include <net/libeth/xdp.h>
#include <net/xdp_sock_drv.h>
#include <net/xdp.h>
#include "ice.h"
@@ -169,50 +170,18 @@ ice_xsk_pool_enable(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid)
* If allocation was successful, substitute buffer with allocated one.
* Returns 0 on success, negative on failure
*/
-static int
+int
ice_realloc_rx_xdp_bufs(struct ice_rx_ring *rx_ring, bool pool_present)
{
- size_t elem_size = pool_present ? sizeof(*rx_ring->xdp_buf) :
- sizeof(*rx_ring->rx_buf);
- void *sw_ring = kcalloc(rx_ring->count, elem_size, GFP_KERNEL);
-
- if (!sw_ring)
- return -ENOMEM;
-
if (pool_present) {
- kfree(rx_ring->rx_buf);
- rx_ring->rx_buf = NULL;
- rx_ring->xdp_buf = sw_ring;
+ rx_ring->xdp_buf = kcalloc(rx_ring->count,
+ sizeof(*rx_ring->xdp_buf),
+ GFP_KERNEL);
+ if (!rx_ring->xdp_buf)
+ return -ENOMEM;
} else {
kfree(rx_ring->xdp_buf);
rx_ring->xdp_buf = NULL;
- rx_ring->rx_buf = sw_ring;
- }
-
- return 0;
-}
-
-/**
- * ice_realloc_zc_buf - reallocate XDP ZC queue pairs
- * @vsi: Current VSI
- * @zc: is zero copy set
- *
- * Reallocate buffer for rx_rings that might be used by XSK.
- * XDP requires more memory, than rx_buf provides.
- * Returns 0 on success, negative on failure
- */
-int ice_realloc_zc_buf(struct ice_vsi *vsi, bool zc)
-{
- struct ice_rx_ring *rx_ring;
- uint i;
-
- ice_for_each_rxq(vsi, i) {
- rx_ring = vsi->rx_rings[i];
- if (!rx_ring->xsk_pool)
- continue;
-
- if (ice_realloc_rx_xdp_bufs(rx_ring, zc))
- return -ENOMEM;
}
return 0;
@@ -228,6 +197,7 @@ int ice_realloc_zc_buf(struct ice_vsi *vsi, bool zc)
*/
int ice_xsk_pool_setup(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid)
{
+ struct ice_rx_ring *rx_ring = vsi->rx_rings[qid];
bool if_running, pool_present = !!pool;
int ret = 0, pool_failure = 0;
@@ -241,8 +211,6 @@ int ice_xsk_pool_setup(struct ice_vsi *vsi, struct xsk_buff_pool *pool, u16 qid)
ice_is_xdp_ena_vsi(vsi);
if (if_running) {
- struct ice_rx_ring *rx_ring = vsi->rx_rings[qid];
-
ret = ice_qp_dis(vsi, qid);
if (ret) {
netdev_err(vsi->netdev, "ice_qp_dis error = %d\n", ret);
@@ -303,11 +271,6 @@ static u16 ice_fill_rx_descs(struct xsk_buff_pool *pool, struct xdp_buff **xdp,
rx_desc->read.pkt_addr = cpu_to_le64(dma);
rx_desc->wb.status_error0 = 0;
- /* Put private info that changes on a per-packet basis
- * into xdp_buff_xsk->cb.
- */
- ice_xdp_meta_set_desc(*xdp, rx_desc);
-
rx_desc++;
xdp++;
}
@@ -393,69 +356,6 @@ bool ice_alloc_rx_bufs_zc(struct ice_rx_ring *rx_ring,
}
/**
- * ice_construct_skb_zc - Create an sk_buff from zero-copy buffer
- * @rx_ring: Rx ring
- * @xdp: Pointer to XDP buffer
- *
- * This function allocates a new skb from a zero-copy Rx buffer.
- *
- * Returns the skb on success, NULL on failure.
- */
-static struct sk_buff *
-ice_construct_skb_zc(struct ice_rx_ring *rx_ring, struct xdp_buff *xdp)
-{
- unsigned int totalsize = xdp->data_end - xdp->data_meta;
- unsigned int metasize = xdp->data - xdp->data_meta;
- struct skb_shared_info *sinfo = NULL;
- struct sk_buff *skb;
- u32 nr_frags = 0;
-
- if (unlikely(xdp_buff_has_frags(xdp))) {
- sinfo = xdp_get_shared_info_from_buff(xdp);
- nr_frags = sinfo->nr_frags;
- }
- net_prefetch(xdp->data_meta);
-
- skb = napi_alloc_skb(&rx_ring->q_vector->napi, totalsize);
- if (unlikely(!skb))
- return NULL;
-
- memcpy(__skb_put(skb, totalsize), xdp->data_meta,
- ALIGN(totalsize, sizeof(long)));
-
- if (metasize) {
- skb_metadata_set(skb, metasize);
- __skb_pull(skb, metasize);
- }
-
- if (likely(!xdp_buff_has_frags(xdp)))
- goto out;
-
- for (int i = 0; i < nr_frags; i++) {
- struct skb_shared_info *skinfo = skb_shinfo(skb);
- skb_frag_t *frag = &sinfo->frags[i];
- struct page *page;
- void *addr;
-
- page = dev_alloc_page();
- if (!page) {
- dev_kfree_skb(skb);
- return NULL;
- }
- addr = page_to_virt(page);
-
- memcpy(addr, skb_frag_page(frag), skb_frag_size(frag));
-
- __skb_fill_page_desc_noacc(skinfo, skinfo->nr_frags++,
- addr, 0, skb_frag_size(frag));
- }
-
-out:
- xsk_buff_free(xdp);
- return skb;
-}
-
-/**
* ice_clean_xdp_irq_zc - produce AF_XDP descriptors to CQ
* @xdp_ring: XDP Tx ring
* @xsk_pool: AF_XDP buffer pool pointer
@@ -669,10 +569,10 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring,
struct xsk_buff_pool *xsk_pool,
int budget)
{
+ struct xdp_buff *first = (struct xdp_buff *)rx_ring->xsk;
unsigned int total_rx_bytes = 0, total_rx_packets = 0;
u32 ntc = rx_ring->next_to_clean;
u32 ntu = rx_ring->next_to_use;
- struct xdp_buff *first = NULL;
struct ice_tx_ring *xdp_ring;
unsigned int xdp_xmit = 0;
struct bpf_prog *xdp_prog;
@@ -686,9 +586,6 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring,
xdp_prog = READ_ONCE(rx_ring->xdp_prog);
xdp_ring = rx_ring->xdp_ring;
- if (ntc != rx_ring->first_desc)
- first = *ice_xdp_buf(rx_ring, rx_ring->first_desc);
-
while (likely(total_rx_packets < (unsigned int)budget)) {
union ice_32b_rx_flex_desc *rx_desc;
unsigned int size, xdp_res = 0;
@@ -724,15 +621,17 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring,
first = xdp;
} else if (likely(size) && !xsk_buff_add_frag(first, xdp)) {
xsk_buff_free(first);
- break;
+ first = NULL;
}
if (++ntc == cnt)
ntc = 0;
- if (ice_is_non_eop(rx_ring, rx_desc))
+ if (ice_is_non_eop(rx_ring, rx_desc) || unlikely(!first))
continue;
+ ((struct libeth_xdp_buff *)first)->desc = rx_desc;
+
xdp_res = ice_run_xdp_zc(rx_ring, first, xdp_prog, xdp_ring,
xsk_pool);
if (likely(xdp_res & (ICE_XDP_TX | ICE_XDP_REDIR))) {
@@ -740,7 +639,6 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring,
} else if (xdp_res == ICE_XDP_EXIT) {
failure = true;
first = NULL;
- rx_ring->first_desc = ntc;
break;
} else if (xdp_res == ICE_XDP_CONSUMED) {
xsk_buff_free(first);
@@ -752,24 +650,20 @@ int ice_clean_rx_irq_zc(struct ice_rx_ring *rx_ring,
total_rx_packets++;
first = NULL;
- rx_ring->first_desc = ntc;
continue;
construct_skb:
/* XDP_PASS path */
- skb = ice_construct_skb_zc(rx_ring, first);
+ skb = xdp_build_skb_from_zc(first);
if (!skb) {
+ xsk_buff_free(first);
+ first = NULL;
+
rx_ring->ring_stats->rx_stats.alloc_buf_failed++;
- break;
+ continue;
}
first = NULL;
- rx_ring->first_desc = ntc;
-
- if (eth_skb_pad(skb)) {
- skb = NULL;
- continue;
- }
total_rx_bytes += skb->len;
total_rx_packets++;
@@ -781,7 +675,9 @@ construct_skb:
}
rx_ring->next_to_clean = ntc;
- entries_to_alloc = ICE_RX_DESC_UNUSED(rx_ring);
+ rx_ring->xsk = (struct libeth_xdp_buff *)first;
+
+ entries_to_alloc = ICE_DESC_UNUSED(rx_ring);
if (entries_to_alloc > ICE_RING_QUARTER(rx_ring))
failure |= !ice_alloc_rx_bufs_zc(rx_ring, xsk_pool,
entries_to_alloc);
diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.h b/drivers/net/ethernet/intel/ice/ice_xsk.h
index 600cbeeaa203..5275fcedc9e1 100644
--- a/drivers/net/ethernet/intel/ice/ice_xsk.h
+++ b/drivers/net/ethernet/intel/ice/ice_xsk.h
@@ -22,7 +22,7 @@ bool ice_xsk_any_rx_ring_ena(struct ice_vsi *vsi);
void ice_xsk_clean_rx_ring(struct ice_rx_ring *rx_ring);
void ice_xsk_clean_xdp_ring(struct ice_tx_ring *xdp_ring);
bool ice_xmit_zc(struct ice_tx_ring *xdp_ring, struct xsk_buff_pool *xsk_pool);
-int ice_realloc_zc_buf(struct ice_vsi *vsi, bool zc);
+int ice_realloc_rx_xdp_bufs(struct ice_rx_ring *rx_ring, bool pool_present);
void ice_qvec_cfg_msix(struct ice_vsi *vsi, struct ice_q_vector *q_vector,
u16 qid);
void ice_qvec_toggle_napi(struct ice_vsi *vsi, struct ice_q_vector *q_vector,
@@ -77,8 +77,8 @@ static inline void ice_xsk_clean_rx_ring(struct ice_rx_ring *rx_ring) { }
static inline void ice_xsk_clean_xdp_ring(struct ice_tx_ring *xdp_ring) { }
static inline int
-ice_realloc_zc_buf(struct ice_vsi __always_unused *vsi,
- bool __always_unused zc)
+ice_realloc_rx_xdp_bufs(struct ice_rx_ring *rx_ring,
+ bool __always_unused pool_present)
{
return 0;
}
diff --git a/drivers/net/ethernet/intel/ice/virt/queues.c b/drivers/net/ethernet/intel/ice/virt/queues.c
index 370f6ec2a374..f73d5a3e83d4 100644
--- a/drivers/net/ethernet/intel/ice/virt/queues.c
+++ b/drivers/net/ethernet/intel/ice/virt/queues.c
@@ -842,18 +842,20 @@ int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
(qpi->rxq.databuffer_size > ((16 * 1024) - 128) ||
qpi->rxq.databuffer_size < 1024))
goto error_param;
+
ring->rx_buf_len = qpi->rxq.databuffer_size;
+
if (qpi->rxq.max_pkt_size > max_frame_size ||
qpi->rxq.max_pkt_size < 64)
goto error_param;
- ring->max_frame = qpi->rxq.max_pkt_size;
+ vsi->max_frame = qpi->rxq.max_pkt_size;
/* add space for the port VLAN since the VF driver is
* not expected to account for it in the MTU
* calculation
*/
if (ice_vf_is_port_vlan_ena(vf))
- ring->max_frame += VLAN_HLEN;
+ vsi->max_frame += VLAN_HLEN;
if (ice_vsi_cfg_single_rxq(vsi, q_idx)) {
dev_warn(ice_pf_to_dev(pf), "VF-%d failed to configure RX queue %d\n",
diff --git a/drivers/net/ethernet/intel/ice/virt/rss.c b/drivers/net/ethernet/intel/ice/virt/rss.c
index cbdbb32d512b..085e69ec0cfc 100644
--- a/drivers/net/ethernet/intel/ice/virt/rss.c
+++ b/drivers/net/ethernet/intel/ice/virt/rss.c
@@ -36,6 +36,11 @@ static const struct ice_vc_hdr_match_type ice_vc_hdr_list[] = {
{VIRTCHNL_PROTO_HDR_ESP, ICE_FLOW_SEG_HDR_ESP},
{VIRTCHNL_PROTO_HDR_AH, ICE_FLOW_SEG_HDR_AH},
{VIRTCHNL_PROTO_HDR_PFCP, ICE_FLOW_SEG_HDR_PFCP_SESSION},
+ {VIRTCHNL_PROTO_HDR_GTPC, ICE_FLOW_SEG_HDR_GTPC},
+ {VIRTCHNL_PROTO_HDR_L2TPV2, ICE_FLOW_SEG_HDR_L2TPV2},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG, ICE_FLOW_SEG_HDR_IPV_FRAG},
+ {VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG, ICE_FLOW_SEG_HDR_IPV_FRAG},
+ {VIRTCHNL_PROTO_HDR_GRE, ICE_FLOW_SEG_HDR_GRE},
};
struct ice_vc_hash_field_match_type {
@@ -87,8 +92,125 @@ ice_vc_hash_field_match_type ice_vc_hash_field_list[] = {
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
- {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
+ {VIRTCHNL_PROTO_HDR_IPV4,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_ID)},
+ {VIRTCHNL_PROTO_HDR_IPV4,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST),
+ ICE_FLOW_HASH_IPV4},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) |
BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
+ ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_ID)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_SA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_DA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ ICE_FLOW_HASH_IPV4 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_PROT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV4_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_PROT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_CHKSUM)},
{VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_SRC),
BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_SA)},
{VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_DST),
@@ -110,6 +232,35 @@ ice_vc_hash_field_match_type ice_vc_hash_field_list[] = {
ICE_FLOW_HASH_IPV6 | BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
{VIRTCHNL_PROTO_HDR_IPV6, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG_PKID),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_ID)},
+ {VIRTCHNL_PROTO_HDR_IPV6,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST),
+ ICE_FLOW_HASH_IPV6_PRE64},
+ {VIRTCHNL_PROTO_HDR_IPV6,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_SA)},
+ {VIRTCHNL_PROTO_HDR_IPV6,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_DA)},
+ {VIRTCHNL_PROTO_HDR_IPV6,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
+ ICE_FLOW_HASH_IPV6_PRE64 |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV6,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_SA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
+ {VIRTCHNL_PROTO_HDR_IPV6,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_IPV6_PROT),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PRE64_DA) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_PROT)},
{VIRTCHNL_PROTO_HDR_TCP,
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT),
BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_SRC_PORT)},
@@ -120,6 +271,25 @@ ice_vc_hash_field_match_type ice_vc_hash_field_list[] = {
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT) |
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT),
ICE_FLOW_HASH_TCP_PORT},
+ {VIRTCHNL_PROTO_HDR_TCP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_TCP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_SRC_PORT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_TCP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_DST_PORT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_TCP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_SRC_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_DST_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_TCP_CHKSUM),
+ ICE_FLOW_HASH_TCP_PORT |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_TCP_CHKSUM)},
{VIRTCHNL_PROTO_HDR_UDP,
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT),
BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_SRC_PORT)},
@@ -130,6 +300,25 @@ ice_vc_hash_field_match_type ice_vc_hash_field_list[] = {
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT) |
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT),
ICE_FLOW_HASH_UDP_PORT},
+ {VIRTCHNL_PROTO_HDR_UDP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_UDP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_SRC_PORT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_UDP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_DST_PORT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_UDP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_SRC_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_DST_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_UDP_CHKSUM),
+ ICE_FLOW_HASH_UDP_PORT |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_UDP_CHKSUM)},
{VIRTCHNL_PROTO_HDR_SCTP,
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT),
BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT)},
@@ -140,6 +329,25 @@ ice_vc_hash_field_match_type ice_vc_hash_field_list[] = {
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT) |
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT),
ICE_FLOW_HASH_SCTP_PORT},
+ {VIRTCHNL_PROTO_HDR_SCTP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_SCTP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_SRC_PORT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_SCTP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_DST_PORT) |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)},
+ {VIRTCHNL_PROTO_HDR_SCTP,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_DST_PORT) |
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_SCTP_CHKSUM),
+ ICE_FLOW_HASH_SCTP_PORT |
+ BIT_ULL(ICE_FLOW_FIELD_IDX_SCTP_CHKSUM)},
{VIRTCHNL_PROTO_HDR_PPPOE,
FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_PPPOE_SESS_ID),
BIT_ULL(ICE_FLOW_FIELD_IDX_PPPOE_SESS_ID)},
@@ -155,8 +363,54 @@ ice_vc_hash_field_match_type ice_vc_hash_field_list[] = {
BIT_ULL(ICE_FLOW_FIELD_IDX_AH_SPI)},
{VIRTCHNL_PROTO_HDR_PFCP, FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_PFCP_SEID),
BIT_ULL(ICE_FLOW_FIELD_IDX_PFCP_SEID)},
+ {VIRTCHNL_PROTO_HDR_GTPC,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_GTPC_TEID),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_GTPC_TEID)},
+ {VIRTCHNL_PROTO_HDR_L2TPV2,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_L2TPV2_SESS_ID),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV2_SESS_ID)},
+ {VIRTCHNL_PROTO_HDR_L2TPV2,
+ FIELD_SELECTOR(VIRTCHNL_PROTO_HDR_L2TPV2_LEN_SESS_ID),
+ BIT_ULL(ICE_FLOW_FIELD_IDX_L2TPV2_LEN_SESS_ID)},
};
+static int
+ice_vc_rss_hash_update(struct ice_hw *hw, struct ice_vsi *vsi, u8 hash_type)
+{
+ struct ice_vsi_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ /* clear previous hash_type */
+ ctx->info.q_opt_rss = vsi->info.q_opt_rss &
+ ~ICE_AQ_VSI_Q_OPT_RSS_HASH_M;
+ /* hash_type is passed in as ICE_AQ_VSI_Q_OPT_RSS_<XOR|TPLZ|SYM_TPLZ */
+ ctx->info.q_opt_rss |= FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M,
+ hash_type);
+
+ /* Preserve existing queueing option setting */
+ ctx->info.q_opt_tc = vsi->info.q_opt_tc;
+ ctx->info.q_opt_flags = vsi->info.q_opt_flags;
+
+ ctx->info.valid_sections =
+ cpu_to_le16(ICE_AQ_VSI_PROP_Q_OPT_VALID);
+
+ ret = ice_update_vsi(hw, vsi->idx, ctx, NULL);
+ if (ret) {
+ dev_err(ice_hw_to_dev(hw), "update VSI for RSS failed, err %d aq_err %s\n",
+ ret, libie_aq_str(hw->adminq.sq_last_status));
+ } else {
+ vsi->info.q_opt_rss = ctx->info.q_opt_rss;
+ }
+
+ kfree(ctx);
+
+ return ret;
+}
+
/**
* ice_vc_validate_pattern
* @vf: pointer to the VF info
@@ -271,6 +525,11 @@ static bool ice_vc_parse_rss_cfg(struct ice_hw *hw,
const struct ice_vc_hash_field_match_type *hf_list;
const struct ice_vc_hdr_match_type *hdr_list;
int i, hf_list_len, hdr_list_len;
+ bool outer_ipv4 = false;
+ bool outer_ipv6 = false;
+ bool inner_hdr = false;
+ bool has_gre = false;
+
u32 *addl_hdrs = &hash_cfg->addl_hdrs;
u64 *hash_flds = &hash_cfg->hash_flds;
@@ -290,17 +549,17 @@ static bool ice_vc_parse_rss_cfg(struct ice_hw *hw,
for (i = 0; i < rss_cfg->proto_hdrs.count; i++) {
struct virtchnl_proto_hdr *proto_hdr =
&rss_cfg->proto_hdrs.proto_hdr[i];
- bool hdr_found = false;
+ u32 hdr_found = 0;
int j;
- /* Find matched ice headers according to virtchnl headers. */
+ /* Find matched ice headers according to virtchnl headers.
+ * Also figure out the outer type of GTPU headers.
+ */
for (j = 0; j < hdr_list_len; j++) {
struct ice_vc_hdr_match_type hdr_map = hdr_list[j];
- if (proto_hdr->type == hdr_map.vc_hdr) {
- *addl_hdrs |= hdr_map.ice_hdr;
- hdr_found = true;
- }
+ if (proto_hdr->type == hdr_map.vc_hdr)
+ hdr_found = hdr_map.ice_hdr;
}
if (!hdr_found)
@@ -318,8 +577,98 @@ static bool ice_vc_parse_rss_cfg(struct ice_hw *hw,
break;
}
}
+
+ if (proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV4 && !inner_hdr)
+ outer_ipv4 = true;
+ else if (proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV6 &&
+ !inner_hdr)
+ outer_ipv6 = true;
+ /* for GRE and L2TPv2, take inner header as input set if no
+ * any field is selected from outer headers.
+ * for GTPU, take inner header and GTPU teid as input set.
+ */
+ else if ((proto_hdr->type == VIRTCHNL_PROTO_HDR_GTPU_IP ||
+ proto_hdr->type == VIRTCHNL_PROTO_HDR_GTPU_EH ||
+ proto_hdr->type == VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_DWN ||
+ proto_hdr->type ==
+ VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_UP) ||
+ ((proto_hdr->type == VIRTCHNL_PROTO_HDR_L2TPV2 ||
+ proto_hdr->type == VIRTCHNL_PROTO_HDR_GRE) &&
+ *hash_flds == 0)) {
+ /* set inner_hdr flag, and clean up outer header */
+ inner_hdr = true;
+
+ /* clear outer headers */
+ *addl_hdrs = 0;
+
+ if (outer_ipv4 && outer_ipv6)
+ return false;
+
+ if (outer_ipv4)
+ hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS_W_OUTER_IPV4;
+ else if (outer_ipv6)
+ hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS_W_OUTER_IPV6;
+ else
+ hash_cfg->hdr_type = ICE_RSS_INNER_HEADERS;
+
+ if (has_gre && outer_ipv4)
+ hash_cfg->hdr_type =
+ ICE_RSS_INNER_HEADERS_W_OUTER_IPV4_GRE;
+ if (has_gre && outer_ipv6)
+ hash_cfg->hdr_type =
+ ICE_RSS_INNER_HEADERS_W_OUTER_IPV6_GRE;
+
+ if (proto_hdr->type == VIRTCHNL_PROTO_HDR_GRE)
+ has_gre = true;
+ }
+
+ *addl_hdrs |= hdr_found;
+
+ /* refine hash hdrs and fields for IP fragment */
+ if (VIRTCHNL_TEST_PROTO_HDR_FIELD(proto_hdr,
+ VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID) &&
+ proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV4_FRAG) {
+ *addl_hdrs |= ICE_FLOW_SEG_HDR_IPV_FRAG;
+ *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_IPV_OTHER);
+ *hash_flds |= BIT_ULL(ICE_FLOW_FIELD_IDX_IPV4_ID);
+ VIRTCHNL_DEL_PROTO_HDR_FIELD(proto_hdr,
+ VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID);
+ }
+ if (VIRTCHNL_TEST_PROTO_HDR_FIELD(proto_hdr,
+ VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG_PKID) &&
+ proto_hdr->type == VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG) {
+ *addl_hdrs |= ICE_FLOW_SEG_HDR_IPV_FRAG;
+ *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_IPV_OTHER);
+ *hash_flds |= BIT_ULL(ICE_FLOW_FIELD_IDX_IPV6_ID);
+ VIRTCHNL_DEL_PROTO_HDR_FIELD(proto_hdr,
+ VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG_PKID);
+ }
+ }
+
+ /* refine gtpu header if we take outer as input set for a no inner
+ * ip gtpu flow.
+ */
+ if (hash_cfg->hdr_type == ICE_RSS_OUTER_HEADERS &&
+ *addl_hdrs & ICE_FLOW_SEG_HDR_GTPU_IP) {
+ *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_GTPU_IP);
+ *addl_hdrs |= ICE_FLOW_SEG_HDR_GTPU_NON_IP;
}
+ /* refine hash field for esp and nat-t-esp. */
+ if ((*addl_hdrs & ICE_FLOW_SEG_HDR_UDP) &&
+ (*addl_hdrs & ICE_FLOW_SEG_HDR_ESP)) {
+ *addl_hdrs &= ~(ICE_FLOW_SEG_HDR_ESP | ICE_FLOW_SEG_HDR_UDP);
+ *addl_hdrs |= ICE_FLOW_SEG_HDR_NAT_T_ESP;
+ *hash_flds &= ~(BIT_ULL(ICE_FLOW_FIELD_IDX_ESP_SPI));
+ *hash_flds |= BIT_ULL(ICE_FLOW_FIELD_IDX_NAT_T_ESP_SPI);
+ }
+
+ /* refine hash hdrs for L4 udp/tcp/sctp. */
+ if (*addl_hdrs & (ICE_FLOW_SEG_HDR_TCP | ICE_FLOW_SEG_HDR_UDP |
+ ICE_FLOW_SEG_HDR_SCTP) &&
+ *addl_hdrs & ICE_FLOW_SEG_HDR_IPV_OTHER)
+ *addl_hdrs &= ~ICE_FLOW_SEG_HDR_IPV_OTHER;
+
return true;
}
@@ -337,6 +686,874 @@ static bool ice_vf_adv_rss_offload_ena(u32 caps)
}
/**
+ * ice_is_hash_cfg_valid - Check whether an RSS hash context is valid
+ * @cfg: RSS hash configuration to test
+ *
+ * Return: true if both @cfg->hash_flds and @cfg->addl_hdrs are non-zero; false otherwise.
+ */
+static bool ice_is_hash_cfg_valid(struct ice_rss_hash_cfg *cfg)
+{
+ return cfg->hash_flds && cfg->addl_hdrs;
+}
+
+/**
+ * ice_hash_cfg_reset - Reset an RSS hash context
+ * @cfg: RSS hash configuration to reset
+ *
+ * Reset fields of @cfg that store the active rule information.
+ */
+static void ice_hash_cfg_reset(struct ice_rss_hash_cfg *cfg)
+{
+ cfg->hash_flds = 0;
+ cfg->addl_hdrs = 0;
+ cfg->hdr_type = ICE_RSS_OUTER_HEADERS;
+ cfg->symm = 0;
+}
+
+/**
+ * ice_hash_cfg_record - Record an RSS hash context
+ * @ctx: destination (global) RSS hash configuration
+ * @cfg: source RSS hash configuration to record
+ *
+ * Copy the active rule information from @cfg into @ctx.
+ */
+static void ice_hash_cfg_record(struct ice_rss_hash_cfg *ctx,
+ struct ice_rss_hash_cfg *cfg)
+{
+ ctx->hash_flds = cfg->hash_flds;
+ ctx->addl_hdrs = cfg->addl_hdrs;
+ ctx->hdr_type = cfg->hdr_type;
+ ctx->symm = cfg->symm;
+}
+
+/**
+ * ice_hash_moveout - Delete an RSS configuration (keep context)
+ * @vf: VF pointer
+ * @cfg: RSS hash configuration
+ *
+ * Return: 0 on success (including when already absent); -ENOENT if @cfg is
+ * invalid or VSI is missing; -EBUSY on hardware removal failure.
+ */
+static int
+ice_hash_moveout(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ struct device *dev = ice_pf_to_dev(vf->pf);
+ struct ice_vsi *vsi = ice_get_vf_vsi(vf);
+ struct ice_hw *hw = &vf->pf->hw;
+ int ret;
+
+ if (!ice_is_hash_cfg_valid(cfg) || !vsi)
+ return -ENOENT;
+
+ ret = ice_rem_rss_cfg(hw, vsi->idx, cfg);
+ if (ret && ret != -ENOENT) {
+ dev_err(dev, "ice_rem_rss_cfg failed for VF %d, VSI %d, error:%d\n",
+ vf->vf_id, vf->lan_vsi_idx, ret);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_hash_moveback - Add an RSS hash configuration for a VF
+ * @vf: VF pointer
+ * @cfg: RSS hash configuration to apply
+ *
+ * Add @cfg to @vf if the context is valid and VSI exists; programs HW.
+ *
+ * Return:
+ * * 0 on success
+ * * -ENOENT if @cfg is invalid or VSI is missing
+ * * -EBUSY if hardware programming fails
+ */
+static int
+ice_hash_moveback(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ struct device *dev = ice_pf_to_dev(vf->pf);
+ struct ice_vsi *vsi = ice_get_vf_vsi(vf);
+ struct ice_hw *hw = &vf->pf->hw;
+ int ret;
+
+ if (!ice_is_hash_cfg_valid(cfg) || !vsi)
+ return -ENOENT;
+
+ ret = ice_add_rss_cfg(hw, vsi, cfg);
+ if (ret) {
+ dev_err(dev, "ice_add_rss_cfg failed for VF %d, VSI %d, error:%d\n",
+ vf->vf_id, vf->lan_vsi_idx, ret);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_hash_remove - remove a RSS configuration
+ * @vf: pointer to the VF info
+ * @cfg: pointer to the RSS hash configuration
+ *
+ * This function will delete a RSS hash configuration and also delete the
+ * hash context which stores the rule info.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int
+ice_hash_remove(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ int ret;
+
+ ret = ice_hash_moveout(vf, cfg);
+ if (ret && ret != -ENOENT)
+ return ret;
+
+ ice_hash_cfg_reset(cfg);
+
+ return 0;
+}
+
+struct ice_gtpu_ctx_action {
+ u32 ctx_idx;
+ const u32 *remove_list;
+ int remove_count;
+ const u32 *moveout_list;
+ int moveout_count;
+};
+
+/**
+ * ice_add_rss_cfg_pre_gtpu - Pre-process the GTPU RSS configuration
+ * @vf: pointer to the VF info
+ * @ctx: pointer to the context of the GTPU hash
+ * @ctx_idx: index of the hash context
+ *
+ * Pre-processes the GTPU hash configuration before adding a new
+ * hash context. It removes or reorders existing hash configurations that may
+ * conflict with the new one. For example, if a GTPU_UP or GTPU_DWN rule is
+ * configured after a GTPU_EH rule, the GTPU_EH hash will be matched first due
+ * to TCAM write and match order (top-down). In such cases, the GTPU_EH rule
+ * must be moved after the GTPU_UP/DWN rule. Conversely, if a GTPU_EH rule is
+ * configured after a GTPU_UP/DWN rule, the UP/DWN rules should be removed to
+ * avoid conflict.
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+static int ice_add_rss_cfg_pre_gtpu(struct ice_vf *vf,
+ struct ice_vf_hash_gtpu_ctx *ctx,
+ u32 ctx_idx)
+{
+ int ret, i;
+
+ static const u32 remove_eh_ip[] = {
+ ICE_HASH_GTPU_CTX_EH_IP_UDP, ICE_HASH_GTPU_CTX_EH_IP_TCP,
+ ICE_HASH_GTPU_CTX_UP_IP, ICE_HASH_GTPU_CTX_UP_IP_UDP,
+ ICE_HASH_GTPU_CTX_UP_IP_TCP, ICE_HASH_GTPU_CTX_DW_IP,
+ ICE_HASH_GTPU_CTX_DW_IP_UDP, ICE_HASH_GTPU_CTX_DW_IP_TCP,
+ };
+
+ static const u32 remove_eh_ip_udp[] = {
+ ICE_HASH_GTPU_CTX_UP_IP_UDP,
+ ICE_HASH_GTPU_CTX_DW_IP_UDP,
+ };
+ static const u32 moveout_eh_ip_udp[] = {
+ ICE_HASH_GTPU_CTX_UP_IP,
+ ICE_HASH_GTPU_CTX_UP_IP_TCP,
+ ICE_HASH_GTPU_CTX_DW_IP,
+ ICE_HASH_GTPU_CTX_DW_IP_TCP,
+ };
+
+ static const u32 remove_eh_ip_tcp[] = {
+ ICE_HASH_GTPU_CTX_UP_IP_TCP,
+ ICE_HASH_GTPU_CTX_DW_IP_TCP,
+ };
+ static const u32 moveout_eh_ip_tcp[] = {
+ ICE_HASH_GTPU_CTX_UP_IP,
+ ICE_HASH_GTPU_CTX_UP_IP_UDP,
+ ICE_HASH_GTPU_CTX_DW_IP,
+ ICE_HASH_GTPU_CTX_DW_IP_UDP,
+ };
+
+ static const u32 remove_up_ip[] = {
+ ICE_HASH_GTPU_CTX_UP_IP_UDP,
+ ICE_HASH_GTPU_CTX_UP_IP_TCP,
+ };
+ static const u32 moveout_up_ip[] = {
+ ICE_HASH_GTPU_CTX_EH_IP,
+ ICE_HASH_GTPU_CTX_EH_IP_UDP,
+ ICE_HASH_GTPU_CTX_EH_IP_TCP,
+ };
+
+ static const u32 moveout_up_ip_udp_tcp[] = {
+ ICE_HASH_GTPU_CTX_EH_IP,
+ ICE_HASH_GTPU_CTX_EH_IP_UDP,
+ ICE_HASH_GTPU_CTX_EH_IP_TCP,
+ };
+
+ static const u32 remove_dw_ip[] = {
+ ICE_HASH_GTPU_CTX_DW_IP_UDP,
+ ICE_HASH_GTPU_CTX_DW_IP_TCP,
+ };
+ static const u32 moveout_dw_ip[] = {
+ ICE_HASH_GTPU_CTX_EH_IP,
+ ICE_HASH_GTPU_CTX_EH_IP_UDP,
+ ICE_HASH_GTPU_CTX_EH_IP_TCP,
+ };
+
+ static const struct ice_gtpu_ctx_action actions[] = {
+ { ICE_HASH_GTPU_CTX_EH_IP, remove_eh_ip,
+ ARRAY_SIZE(remove_eh_ip), NULL, 0 },
+ { ICE_HASH_GTPU_CTX_EH_IP_UDP, remove_eh_ip_udp,
+ ARRAY_SIZE(remove_eh_ip_udp), moveout_eh_ip_udp,
+ ARRAY_SIZE(moveout_eh_ip_udp) },
+ { ICE_HASH_GTPU_CTX_EH_IP_TCP, remove_eh_ip_tcp,
+ ARRAY_SIZE(remove_eh_ip_tcp), moveout_eh_ip_tcp,
+ ARRAY_SIZE(moveout_eh_ip_tcp) },
+ { ICE_HASH_GTPU_CTX_UP_IP, remove_up_ip,
+ ARRAY_SIZE(remove_up_ip), moveout_up_ip,
+ ARRAY_SIZE(moveout_up_ip) },
+ { ICE_HASH_GTPU_CTX_UP_IP_UDP, NULL, 0, moveout_up_ip_udp_tcp,
+ ARRAY_SIZE(moveout_up_ip_udp_tcp) },
+ { ICE_HASH_GTPU_CTX_UP_IP_TCP, NULL, 0, moveout_up_ip_udp_tcp,
+ ARRAY_SIZE(moveout_up_ip_udp_tcp) },
+ { ICE_HASH_GTPU_CTX_DW_IP, remove_dw_ip,
+ ARRAY_SIZE(remove_dw_ip), moveout_dw_ip,
+ ARRAY_SIZE(moveout_dw_ip) },
+ { ICE_HASH_GTPU_CTX_DW_IP_UDP, NULL, 0, moveout_dw_ip,
+ ARRAY_SIZE(moveout_dw_ip) },
+ { ICE_HASH_GTPU_CTX_DW_IP_TCP, NULL, 0, moveout_dw_ip,
+ ARRAY_SIZE(moveout_dw_ip) },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(actions); i++) {
+ if (actions[i].ctx_idx != ctx_idx)
+ continue;
+
+ if (actions[i].remove_list) {
+ for (int j = 0; j < actions[i].remove_count; j++) {
+ u16 rm = actions[i].remove_list[j];
+
+ ret = ice_hash_remove(vf, &ctx->ctx[rm]);
+ if (ret && ret != -ENOENT)
+ return ret;
+ }
+ }
+
+ if (actions[i].moveout_list) {
+ for (int j = 0; j < actions[i].moveout_count; j++) {
+ u16 mv = actions[i].moveout_list[j];
+
+ ret = ice_hash_moveout(vf, &ctx->ctx[mv]);
+ if (ret && ret != -ENOENT)
+ return ret;
+ }
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_add_rss_cfg_pre_ip - Pre-process IP-layer RSS configuration
+ * @vf: VF pointer
+ * @ctx: IP L4 hash context (ESP/UDP-ESP/AH/PFCP and UDP/TCP/SCTP)
+ *
+ * Remove covered/recorded IP RSS configurations prior to adding a new one.
+ *
+ * Return: 0 on success; negative error code on failure.
+ */
+static int
+ice_add_rss_cfg_pre_ip(struct ice_vf *vf, struct ice_vf_hash_ip_ctx *ctx)
+{
+ int i, ret;
+
+ for (i = 1; i < ICE_HASH_IP_CTX_MAX; i++)
+ if (ice_is_hash_cfg_valid(&ctx->ctx[i])) {
+ ret = ice_hash_remove(vf, &ctx->ctx[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * ice_calc_gtpu_ctx_idx - Calculate GTPU hash context index
+ * @hdrs: Bitmask of protocol headers prefixed with ICE_FLOW_SEG_HDR_*
+ *
+ * Determine the GTPU hash context index based on the combination of
+ * encapsulation headers (GTPU_EH, GTPU_UP, GTPU_DWN) and transport
+ * protocols (UDP, TCP) within IPv4 or IPv6 flows.
+ *
+ * Return: A valid context index (0-8) if the header combination is supported,
+ * or ICE_HASH_GTPU_CTX_MAX if the combination is invalid.
+ */
+static enum ice_hash_gtpu_ctx_type ice_calc_gtpu_ctx_idx(u32 hdrs)
+{
+ u32 eh_idx, ip_idx;
+
+ if (hdrs & ICE_FLOW_SEG_HDR_GTPU_EH)
+ eh_idx = 0;
+ else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_UP)
+ eh_idx = 1;
+ else if (hdrs & ICE_FLOW_SEG_HDR_GTPU_DWN)
+ eh_idx = 2;
+ else
+ return ICE_HASH_GTPU_CTX_MAX;
+
+ ip_idx = 0;
+ if (hdrs & ICE_FLOW_SEG_HDR_UDP)
+ ip_idx = 1;
+ else if (hdrs & ICE_FLOW_SEG_HDR_TCP)
+ ip_idx = 2;
+
+ if (hdrs & (ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV6))
+ return eh_idx * 3 + ip_idx;
+ else
+ return ICE_HASH_GTPU_CTX_MAX;
+}
+
+/**
+ * ice_map_ip_ctx_idx - map the index of the IP L4 hash context
+ * @hdrs: protocol headers prefix with ICE_FLOW_SEG_HDR_XXX.
+ *
+ * The IP L4 hash context use the index to classify for IPv4/IPv6 with
+ * ESP/UDP_ESP/AH/PFCP and non-tunnel UDP/TCP/SCTP
+ * this function map the index based on the protocol headers.
+ *
+ * Return: The mapped IP context index on success, or ICE_HASH_IP_CTX_MAX
+ * if no matching context is found.
+ */
+static u8 ice_map_ip_ctx_idx(u32 hdrs)
+{
+ u8 i;
+
+ static struct {
+ u32 hdrs;
+ u8 ctx_idx;
+ } ip_ctx_idx_map[] = {
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_ESP,
+ ICE_HASH_IP_CTX_IP_ESP },
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_NAT_T_ESP,
+ ICE_HASH_IP_CTX_IP_UDP_ESP },
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_AH,
+ ICE_HASH_IP_CTX_IP_AH },
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_PFCP_SESSION,
+ ICE_HASH_IP_CTX_IP_PFCP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_UDP,
+ ICE_HASH_IP_CTX_IP_UDP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_TCP,
+ ICE_HASH_IP_CTX_IP_TCP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_SCTP,
+ ICE_HASH_IP_CTX_IP_SCTP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER,
+ ICE_HASH_IP_CTX_IP },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_ESP,
+ ICE_HASH_IP_CTX_IP_ESP },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_NAT_T_ESP,
+ ICE_HASH_IP_CTX_IP_UDP_ESP },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_AH,
+ ICE_HASH_IP_CTX_IP_AH },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER |
+ ICE_FLOW_SEG_HDR_PFCP_SESSION,
+ ICE_HASH_IP_CTX_IP_PFCP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_UDP,
+ ICE_HASH_IP_CTX_IP_UDP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_TCP,
+ ICE_HASH_IP_CTX_IP_TCP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_SCTP,
+ ICE_HASH_IP_CTX_IP_SCTP },
+ { ICE_FLOW_SEG_HDR_ETH | ICE_FLOW_SEG_HDR_VLAN |
+ ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER,
+ ICE_HASH_IP_CTX_IP },
+ /* the remaining mappings are used for default RSS */
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_UDP,
+ ICE_HASH_IP_CTX_IP_UDP },
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_TCP,
+ ICE_HASH_IP_CTX_IP_TCP },
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_SCTP,
+ ICE_HASH_IP_CTX_IP_SCTP },
+ { ICE_FLOW_SEG_HDR_IPV4 | ICE_FLOW_SEG_HDR_IPV_OTHER,
+ ICE_HASH_IP_CTX_IP },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_UDP,
+ ICE_HASH_IP_CTX_IP_UDP },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_TCP,
+ ICE_HASH_IP_CTX_IP_TCP },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_SCTP,
+ ICE_HASH_IP_CTX_IP_SCTP },
+ { ICE_FLOW_SEG_HDR_IPV6 | ICE_FLOW_SEG_HDR_IPV_OTHER,
+ ICE_HASH_IP_CTX_IP },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(ip_ctx_idx_map); i++) {
+ if (hdrs == ip_ctx_idx_map[i].hdrs)
+ return ip_ctx_idx_map[i].ctx_idx;
+ }
+
+ return ICE_HASH_IP_CTX_MAX;
+}
+
+/**
+ * ice_add_rss_cfg_pre - Prepare RSS configuration context for a VF
+ * @vf: pointer to the VF structure
+ * @cfg: pointer to the RSS hash configuration
+ *
+ * Prepare the RSS hash context for a given VF based on the additional
+ * protocol headers specified in @cfg. This includes pre-configuration
+ * for IP and GTPU-based flows.
+ *
+ * If the configuration matches a known IP context, the function sets up
+ * the appropriate IP hash context. If the configuration includes GTPU
+ * headers, it prepares the GTPU-specific context accordingly.
+ *
+ * Return: 0 on success, or a negative error code on failure.
+ */
+static int
+ice_add_rss_cfg_pre(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ u32 ice_gtpu_ctx_idx = ice_calc_gtpu_ctx_idx(cfg->addl_hdrs);
+ u8 ip_ctx_idx = ice_map_ip_ctx_idx(cfg->addl_hdrs);
+
+ if (ip_ctx_idx == ICE_HASH_IP_CTX_IP) {
+ int ret = 0;
+
+ if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4)
+ ret = ice_add_rss_cfg_pre_ip(vf, &vf->hash_ctx.v4);
+ else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6)
+ ret = ice_add_rss_cfg_pre_ip(vf, &vf->hash_ctx.v6);
+
+ if (ret)
+ return ret;
+ }
+
+ if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) {
+ return ice_add_rss_cfg_pre_gtpu(vf, &vf->hash_ctx.ipv4,
+ ice_gtpu_ctx_idx);
+ } else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) {
+ return ice_add_rss_cfg_pre_gtpu(vf, &vf->hash_ctx.ipv6,
+ ice_gtpu_ctx_idx);
+ }
+
+ return 0;
+}
+
+/**
+ * ice_add_rss_cfg_post_gtpu - Post-process GTPU RSS configuration
+ * @vf: pointer to the VF info
+ * @ctx: pointer to the context of the GTPU hash
+ * @cfg: pointer to the RSS hash configuration
+ * @ctx_idx: index of the hash context
+ *
+ * Post-processes the GTPU hash configuration after a new hash
+ * context has been successfully added. It updates the context with the new
+ * configuration and restores any previously removed hash contexts that need
+ * to be re-applied. This ensures proper TCAM rule ordering and avoids
+ * conflicts between overlapping GTPU rules.
+ *
+ * Return: 0 on success or a negative error code on failure
+ */
+static int ice_add_rss_cfg_post_gtpu(struct ice_vf *vf,
+ struct ice_vf_hash_gtpu_ctx *ctx,
+ struct ice_rss_hash_cfg *cfg, u32 ctx_idx)
+{
+ /* GTPU hash moveback lookup table indexed by context ID.
+ * Each entry is a bitmap indicating which contexts need moveback
+ * operations when the corresponding context index is processed.
+ */
+ static const unsigned long
+ ice_gtpu_moveback_tbl[ICE_HASH_GTPU_CTX_MAX] = {
+ [ICE_HASH_GTPU_CTX_EH_IP] = 0,
+ [ICE_HASH_GTPU_CTX_EH_IP_UDP] =
+ BIT(ICE_HASH_GTPU_CTX_UP_IP) |
+ BIT(ICE_HASH_GTPU_CTX_UP_IP_TCP) |
+ BIT(ICE_HASH_GTPU_CTX_DW_IP) |
+ BIT(ICE_HASH_GTPU_CTX_DW_IP_TCP),
+ [ICE_HASH_GTPU_CTX_EH_IP_TCP] =
+ BIT(ICE_HASH_GTPU_CTX_UP_IP) |
+ BIT(ICE_HASH_GTPU_CTX_UP_IP_UDP) |
+ BIT(ICE_HASH_GTPU_CTX_DW_IP) |
+ BIT(ICE_HASH_GTPU_CTX_DW_IP_UDP),
+ [ICE_HASH_GTPU_CTX_UP_IP] =
+ BIT(ICE_HASH_GTPU_CTX_EH_IP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP),
+ [ICE_HASH_GTPU_CTX_UP_IP_UDP] =
+ BIT(ICE_HASH_GTPU_CTX_EH_IP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP),
+ [ICE_HASH_GTPU_CTX_UP_IP_TCP] =
+ BIT(ICE_HASH_GTPU_CTX_EH_IP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP),
+ [ICE_HASH_GTPU_CTX_DW_IP] =
+ BIT(ICE_HASH_GTPU_CTX_EH_IP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP),
+ [ICE_HASH_GTPU_CTX_DW_IP_UDP] =
+ BIT(ICE_HASH_GTPU_CTX_EH_IP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP),
+ [ICE_HASH_GTPU_CTX_DW_IP_TCP] =
+ BIT(ICE_HASH_GTPU_CTX_EH_IP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_UDP) |
+ BIT(ICE_HASH_GTPU_CTX_EH_IP_TCP),
+ };
+ unsigned long moveback_mask;
+ int ret;
+ int i;
+
+ if (unlikely(ctx_idx >= ICE_HASH_GTPU_CTX_MAX))
+ return 0;
+
+ ctx->ctx[ctx_idx].addl_hdrs = cfg->addl_hdrs;
+ ctx->ctx[ctx_idx].hash_flds = cfg->hash_flds;
+ ctx->ctx[ctx_idx].hdr_type = cfg->hdr_type;
+ ctx->ctx[ctx_idx].symm = cfg->symm;
+
+ moveback_mask = ice_gtpu_moveback_tbl[ctx_idx];
+ for_each_set_bit(i, &moveback_mask, ICE_HASH_GTPU_CTX_MAX) {
+ ret = ice_hash_moveback(vf, &ctx->ctx[i]);
+ if (ret && ret != -ENOENT)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+ice_add_rss_cfg_post(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ u32 ice_gtpu_ctx_idx = ice_calc_gtpu_ctx_idx(cfg->addl_hdrs);
+ u8 ip_ctx_idx = ice_map_ip_ctx_idx(cfg->addl_hdrs);
+
+ if (ip_ctx_idx && ip_ctx_idx < ICE_HASH_IP_CTX_MAX) {
+ if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4)
+ ice_hash_cfg_record(&vf->hash_ctx.v4.ctx[ip_ctx_idx], cfg);
+ else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6)
+ ice_hash_cfg_record(&vf->hash_ctx.v6.ctx[ip_ctx_idx], cfg);
+ }
+
+ if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4) {
+ return ice_add_rss_cfg_post_gtpu(vf, &vf->hash_ctx.ipv4,
+ cfg, ice_gtpu_ctx_idx);
+ } else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6) {
+ return ice_add_rss_cfg_post_gtpu(vf, &vf->hash_ctx.ipv6,
+ cfg, ice_gtpu_ctx_idx);
+ }
+
+ return 0;
+}
+
+/**
+ * ice_rem_rss_cfg_post - post-process the RSS configuration
+ * @vf: pointer to the VF info
+ * @cfg: pointer to the RSS hash configuration
+ *
+ * Post process the RSS hash configuration after deleting a hash
+ * config. Such as, it will reset the hash context for the GTPU hash.
+ */
+static void
+ice_rem_rss_cfg_post(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ u32 ice_gtpu_ctx_idx = ice_calc_gtpu_ctx_idx(cfg->addl_hdrs);
+ u8 ip_ctx_idx = ice_map_ip_ctx_idx(cfg->addl_hdrs);
+
+ if (ip_ctx_idx && ip_ctx_idx < ICE_HASH_IP_CTX_MAX) {
+ if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4)
+ ice_hash_cfg_reset(&vf->hash_ctx.v4.ctx[ip_ctx_idx]);
+ else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6)
+ ice_hash_cfg_reset(&vf->hash_ctx.v6.ctx[ip_ctx_idx]);
+ }
+
+ if (ice_gtpu_ctx_idx >= ICE_HASH_GTPU_CTX_MAX)
+ return;
+
+ if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV4)
+ ice_hash_cfg_reset(&vf->hash_ctx.ipv4.ctx[ice_gtpu_ctx_idx]);
+ else if (cfg->addl_hdrs & ICE_FLOW_SEG_HDR_IPV6)
+ ice_hash_cfg_reset(&vf->hash_ctx.ipv6.ctx[ice_gtpu_ctx_idx]);
+}
+
+/**
+ * ice_rem_rss_cfg_wrap - Wrapper for deleting an RSS configuration
+ * @vf: pointer to the VF info
+ * @cfg: pointer to the RSS hash configuration
+ *
+ * Wrapper function to delete a flow profile base on an RSS configuration,
+ * and also post process the hash context base on the rollback mechanism
+ * which handle some rules conflict by ice_add_rss_cfg_wrap.
+ *
+ * Return: 0 on success; negative error code on failure.
+ */
+static int
+ice_rem_rss_cfg_wrap(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ struct device *dev = ice_pf_to_dev(vf->pf);
+ struct ice_vsi *vsi = ice_get_vf_vsi(vf);
+ struct ice_hw *hw = &vf->pf->hw;
+ int ret;
+
+ ret = ice_rem_rss_cfg(hw, vsi->idx, cfg);
+ /* We just ignore -ENOENT, because if two configurations share the same
+ * profile remove one of them actually removes both, since the
+ * profile is deleted.
+ */
+ if (ret && ret != -ENOENT) {
+ dev_err(dev, "ice_rem_rss_cfg failed for VF %d, VSI %d, error:%d\n",
+ vf->vf_id, vf->lan_vsi_idx, ret);
+ return ret;
+ }
+
+ ice_rem_rss_cfg_post(vf, cfg);
+
+ return 0;
+}
+
+/**
+ * ice_add_rss_cfg_wrap - Wrapper for adding an RSS configuration
+ * @vf: pointer to the VF info
+ * @cfg: pointer to the RSS hash configuration
+ *
+ * Add a flow profile based on an RSS configuration. Use a rollback
+ * mechanism to handle rule conflicts due to TCAM
+ * write sequence from top to down.
+ *
+ * Return: 0 on success; negative error code on failure.
+ */
+static int
+ice_add_rss_cfg_wrap(struct ice_vf *vf, struct ice_rss_hash_cfg *cfg)
+{
+ struct device *dev = ice_pf_to_dev(vf->pf);
+ struct ice_vsi *vsi = ice_get_vf_vsi(vf);
+ struct ice_hw *hw = &vf->pf->hw;
+ int ret;
+
+ if (ice_add_rss_cfg_pre(vf, cfg))
+ return -EINVAL;
+
+ ret = ice_add_rss_cfg(hw, vsi, cfg);
+ if (ret) {
+ dev_err(dev, "ice_add_rss_cfg failed for VF %d, VSI %d, error:%d\n",
+ vf->vf_id, vf->lan_vsi_idx, ret);
+ return ret;
+ }
+
+ if (ice_add_rss_cfg_post(vf, cfg))
+ ret = -EINVAL;
+
+ return ret;
+}
+
+/**
+ * ice_parse_raw_rss_pattern - Parse raw pattern spec and mask for RSS
+ * @vf: pointer to the VF info
+ * @proto: pointer to the virtchnl protocol header
+ * @raw_cfg: pointer to the RSS raw pattern configuration
+ *
+ * Parser function to get spec and mask from virtchnl message, and parse
+ * them to get the corresponding profile and offset. The profile is used
+ * to add RSS configuration.
+ *
+ * Return: 0 on success; negative error code on failure.
+ */
+static int
+ice_parse_raw_rss_pattern(struct ice_vf *vf, struct virtchnl_proto_hdrs *proto,
+ struct ice_rss_raw_cfg *raw_cfg)
+{
+ struct ice_parser_result pkt_parsed;
+ struct ice_hw *hw = &vf->pf->hw;
+ struct ice_parser_profile prof;
+ struct ice_parser *psr;
+ u8 *pkt_buf, *msk_buf;
+ u16 pkt_len;
+ int ret = 0;
+
+ pkt_len = proto->raw.pkt_len;
+ if (!pkt_len)
+ return -EINVAL;
+ if (pkt_len > VIRTCHNL_MAX_SIZE_RAW_PACKET)
+ pkt_len = VIRTCHNL_MAX_SIZE_RAW_PACKET;
+
+ pkt_buf = kzalloc(pkt_len, GFP_KERNEL);
+ msk_buf = kzalloc(pkt_len, GFP_KERNEL);
+ if (!pkt_buf || !msk_buf) {
+ ret = -ENOMEM;
+ goto free_alloc;
+ }
+
+ memcpy(pkt_buf, proto->raw.spec, pkt_len);
+ memcpy(msk_buf, proto->raw.mask, pkt_len);
+
+ psr = ice_parser_create(hw);
+ if (IS_ERR(psr)) {
+ ret = PTR_ERR(psr);
+ goto free_alloc;
+ }
+
+ ret = ice_parser_run(psr, pkt_buf, pkt_len, &pkt_parsed);
+ if (ret)
+ goto parser_destroy;
+
+ ret = ice_parser_profile_init(&pkt_parsed, pkt_buf, msk_buf,
+ pkt_len, ICE_BLK_RSS, &prof);
+ if (ret)
+ goto parser_destroy;
+
+ memcpy(&raw_cfg->prof, &prof, sizeof(prof));
+
+parser_destroy:
+ ice_parser_destroy(psr);
+free_alloc:
+ kfree(pkt_buf);
+ kfree(msk_buf);
+ return ret;
+}
+
+/**
+ * ice_add_raw_rss_cfg - add RSS configuration for raw pattern
+ * @vf: pointer to the VF info
+ * @cfg: pointer to the RSS raw pattern configuration
+ *
+ * This function adds the RSS configuration for raw pattern.
+ * Check if current profile is matched. If not, remove the old
+ * one and add the new profile to HW directly. Update the symmetric
+ * hash configuration as well.
+ *
+ * Return: 0 on success; negative error code on failure.
+ */
+static int
+ice_add_raw_rss_cfg(struct ice_vf *vf, struct ice_rss_raw_cfg *cfg)
+{
+ struct ice_parser_profile *prof = &cfg->prof;
+ struct device *dev = ice_pf_to_dev(vf->pf);
+ struct ice_rss_prof_info *rss_prof;
+ struct ice_hw *hw = &vf->pf->hw;
+ int i, ptg, ret = 0;
+ u16 vsi_handle;
+ u64 id;
+
+ vsi_handle = vf->lan_vsi_idx;
+ id = find_first_bit(prof->ptypes, ICE_FLOW_PTYPE_MAX);
+
+ ptg = hw->blk[ICE_BLK_RSS].xlt1.t[id];
+ rss_prof = &vf->rss_prof_info[ptg];
+
+ /* check if ptg already has a profile */
+ if (rss_prof->prof.fv_num) {
+ for (i = 0; i < ICE_MAX_FV_WORDS; i++) {
+ if (rss_prof->prof.fv[i].proto_id !=
+ prof->fv[i].proto_id ||
+ rss_prof->prof.fv[i].offset !=
+ prof->fv[i].offset)
+ break;
+ }
+
+ /* current profile is matched, check symmetric hash */
+ if (i == ICE_MAX_FV_WORDS) {
+ if (rss_prof->symm != cfg->symm)
+ goto update_symm;
+ return ret;
+ }
+
+ /* current profile is not matched, remove it */
+ ret =
+ ice_rem_prof_id_flow(hw, ICE_BLK_RSS,
+ ice_get_hw_vsi_num(hw, vsi_handle),
+ id);
+ if (ret) {
+ dev_err(dev, "remove RSS flow failed\n");
+ return ret;
+ }
+
+ ret = ice_rem_prof(hw, ICE_BLK_RSS, id);
+ if (ret) {
+ dev_err(dev, "remove RSS profile failed\n");
+ return ret;
+ }
+ }
+
+ /* add new profile */
+ ret = ice_flow_set_parser_prof(hw, vsi_handle, 0, prof, ICE_BLK_RSS);
+ if (ret) {
+ dev_err(dev, "HW profile add failed\n");
+ return ret;
+ }
+
+ memcpy(&rss_prof->prof, prof, sizeof(struct ice_parser_profile));
+
+update_symm:
+ rss_prof->symm = cfg->symm;
+ ice_rss_update_raw_symm(hw, cfg, id);
+ return ret;
+}
+
+/**
+ * ice_rem_raw_rss_cfg - remove RSS configuration for raw pattern
+ * @vf: pointer to the VF info
+ * @cfg: pointer to the RSS raw pattern configuration
+ *
+ * This function removes the RSS configuration for raw pattern.
+ * Check if vsi group is already removed first. If not, remove the
+ * profile.
+ *
+ * Return: 0 on success; negative error code on failure.
+ */
+static int
+ice_rem_raw_rss_cfg(struct ice_vf *vf, struct ice_rss_raw_cfg *cfg)
+{
+ struct ice_parser_profile *prof = &cfg->prof;
+ struct device *dev = ice_pf_to_dev(vf->pf);
+ struct ice_hw *hw = &vf->pf->hw;
+ int ptg, ret = 0;
+ u16 vsig, vsi;
+ u64 id;
+
+ id = find_first_bit(prof->ptypes, ICE_FLOW_PTYPE_MAX);
+
+ ptg = hw->blk[ICE_BLK_RSS].xlt1.t[id];
+
+ memset(&vf->rss_prof_info[ptg], 0,
+ sizeof(struct ice_rss_prof_info));
+
+ /* check if vsig is already removed */
+ vsi = ice_get_hw_vsi_num(hw, vf->lan_vsi_idx);
+ if (vsi >= ICE_MAX_VSI) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ vsig = hw->blk[ICE_BLK_RSS].xlt2.vsis[vsi].vsig;
+ if (vsig) {
+ ret = ice_rem_prof_id_flow(hw, ICE_BLK_RSS, vsi, id);
+ if (ret)
+ goto err;
+
+ ret = ice_rem_prof(hw, ICE_BLK_RSS, id);
+ if (ret)
+ goto err;
+ }
+
+ return ret;
+
+err:
+ dev_err(dev, "HW profile remove failed\n");
+ return ret;
+}
+
+/**
* ice_vc_handle_rss_cfg
* @vf: pointer to the VF info
* @msg: pointer to the message buffer
@@ -352,6 +1569,9 @@ int ice_vc_handle_rss_cfg(struct ice_vf *vf, u8 *msg, bool add)
struct device *dev = ice_pf_to_dev(vf->pf);
struct ice_hw *hw = &vf->pf->hw;
struct ice_vsi *vsi;
+ u8 hash_type;
+ bool symm;
+ int ret;
if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) {
dev_dbg(dev, "VF %d attempting to configure RSS, but RSS is not supported by the PF\n",
@@ -387,49 +1607,44 @@ int ice_vc_handle_rss_cfg(struct ice_vf *vf, u8 *msg, bool add)
goto error_param;
}
- if (!ice_vc_validate_pattern(vf, &rss_cfg->proto_hdrs)) {
- v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+ if (rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_R_ASYMMETRIC) {
+ hash_type = add ? ICE_AQ_VSI_Q_OPT_RSS_HASH_XOR :
+ ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ;
+
+ ret = ice_vc_rss_hash_update(hw, vsi, hash_type);
+ if (ret)
+ v_ret = ice_err_to_virt_err(ret);
goto error_param;
}
- if (rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_R_ASYMMETRIC) {
- struct ice_vsi_ctx *ctx;
- u8 lut_type, hash_type;
- int status;
+ hash_type = add ? ICE_AQ_VSI_Q_OPT_RSS_HASH_SYM_TPLZ :
+ ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ;
+ ret = ice_vc_rss_hash_update(hw, vsi, hash_type);
+ if (ret) {
+ v_ret = ice_err_to_virt_err(ret);
+ goto error_param;
+ }
- lut_type = ICE_AQ_VSI_Q_OPT_RSS_LUT_VSI;
- hash_type = add ? ICE_AQ_VSI_Q_OPT_RSS_HASH_XOR :
- ICE_AQ_VSI_Q_OPT_RSS_HASH_TPLZ;
+ symm = rss_cfg->rss_algorithm == VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC;
+ /* Configure RSS hash for raw pattern */
+ if (rss_cfg->proto_hdrs.tunnel_level == 0 &&
+ rss_cfg->proto_hdrs.count == 0) {
+ struct ice_rss_raw_cfg raw_cfg;
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx) {
- v_ret = VIRTCHNL_STATUS_ERR_NO_MEMORY;
+ if (ice_parse_raw_rss_pattern(vf, &rss_cfg->proto_hdrs,
+ &raw_cfg)) {
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
goto error_param;
}
- ctx->info.q_opt_rss =
- FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_LUT_M, lut_type) |
- FIELD_PREP(ICE_AQ_VSI_Q_OPT_RSS_HASH_M, hash_type);
-
- /* Preserve existing queueing option setting */
- ctx->info.q_opt_rss |= (vsi->info.q_opt_rss &
- ICE_AQ_VSI_Q_OPT_RSS_GBL_LUT_M);
- ctx->info.q_opt_tc = vsi->info.q_opt_tc;
- ctx->info.q_opt_flags = vsi->info.q_opt_rss;
-
- ctx->info.valid_sections =
- cpu_to_le16(ICE_AQ_VSI_PROP_Q_OPT_VALID);
-
- status = ice_update_vsi(hw, vsi->idx, ctx, NULL);
- if (status) {
- dev_err(dev, "update VSI for RSS failed, err %d aq_err %s\n",
- status, libie_aq_str(hw->adminq.sq_last_status));
- v_ret = VIRTCHNL_STATUS_ERR_PARAM;
+ if (add) {
+ raw_cfg.symm = symm;
+ if (ice_add_raw_rss_cfg(vf, &raw_cfg))
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
} else {
- vsi->info.q_opt_rss = ctx->info.q_opt_rss;
+ if (ice_rem_raw_rss_cfg(vf, &raw_cfg))
+ v_ret = VIRTCHNL_STATUS_ERR_PARAM;
}
-
- kfree(ctx);
} else {
struct ice_rss_hash_cfg cfg;
@@ -448,24 +1663,12 @@ int ice_vc_handle_rss_cfg(struct ice_vf *vf, u8 *msg, bool add)
}
if (add) {
- if (ice_add_rss_cfg(hw, vsi, &cfg)) {
+ cfg.symm = symm;
+ if (ice_add_rss_cfg_wrap(vf, &cfg))
v_ret = VIRTCHNL_STATUS_ERR_PARAM;
- dev_err(dev, "ice_add_rss_cfg failed for vsi = %d, v_ret = %d\n",
- vsi->vsi_num, v_ret);
- }
} else {
- int status;
-
- status = ice_rem_rss_cfg(hw, vsi->idx, &cfg);
- /* We just ignore -ENOENT, because if two configurations
- * share the same profile remove one of them actually
- * removes both, since the profile is deleted.
- */
- if (status && status != -ENOENT) {
+ if (ice_rem_rss_cfg_wrap(vf, &cfg))
v_ret = VIRTCHNL_STATUS_ERR_PARAM;
- dev_err(dev, "ice_rem_rss_cfg failed for VF ID:%d, error:%d\n",
- vf->vf_id, status);
- }
}
}
diff --git a/drivers/net/ethernet/intel/idpf/idpf.h b/drivers/net/ethernet/intel/idpf/idpf.h
index ca4da0c89979..8cfc68cbfa06 100644
--- a/drivers/net/ethernet/intel/idpf/idpf.h
+++ b/drivers/net/ethernet/intel/idpf/idpf.h
@@ -131,14 +131,12 @@ enum idpf_cap_field {
/**
* enum idpf_vport_state - Current vport state
- * @__IDPF_VPORT_DOWN: Vport is down
- * @__IDPF_VPORT_UP: Vport is up
- * @__IDPF_VPORT_STATE_LAST: Must be last, number of states
+ * @IDPF_VPORT_UP: Vport is up
+ * @IDPF_VPORT_STATE_NBITS: Must be last, number of states
*/
enum idpf_vport_state {
- __IDPF_VPORT_DOWN,
- __IDPF_VPORT_UP,
- __IDPF_VPORT_STATE_LAST,
+ IDPF_VPORT_UP,
+ IDPF_VPORT_STATE_NBITS
};
/**
@@ -162,7 +160,7 @@ struct idpf_netdev_priv {
u16 vport_idx;
u16 max_tx_hdr_size;
u16 tx_max_bufs;
- enum idpf_vport_state state;
+ DECLARE_BITMAP(state, IDPF_VPORT_STATE_NBITS);
struct rtnl_link_stats64 netstats;
spinlock_t stats_lock;
};
@@ -735,12 +733,10 @@ static inline bool idpf_is_rdma_cap_ena(struct idpf_adapter *adapter)
#define IDPF_CAP_RSS (\
VIRTCHNL2_FLOW_IPV4_TCP |\
- VIRTCHNL2_FLOW_IPV4_TCP |\
VIRTCHNL2_FLOW_IPV4_UDP |\
VIRTCHNL2_FLOW_IPV4_SCTP |\
VIRTCHNL2_FLOW_IPV4_OTHER |\
VIRTCHNL2_FLOW_IPV6_TCP |\
- VIRTCHNL2_FLOW_IPV6_TCP |\
VIRTCHNL2_FLOW_IPV6_UDP |\
VIRTCHNL2_FLOW_IPV6_SCTP |\
VIRTCHNL2_FLOW_IPV6_OTHER)
diff --git a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
index a5a1eec9ade8..2589e124e41c 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_ethtool.c
@@ -6,6 +6,25 @@
#include "idpf_virtchnl.h"
/**
+ * idpf_get_rx_ring_count - get RX ring count
+ * @netdev: network interface device structure
+ *
+ * Return: number of RX rings.
+ */
+static u32 idpf_get_rx_ring_count(struct net_device *netdev)
+{
+ struct idpf_vport *vport;
+ u32 num_rxq;
+
+ idpf_vport_ctrl_lock(netdev);
+ vport = idpf_netdev_to_vport(netdev);
+ num_rxq = vport->num_rxq;
+ idpf_vport_ctrl_unlock(netdev);
+
+ return num_rxq;
+}
+
+/**
* idpf_get_rxnfc - command to get RX flow classification rules
* @netdev: network interface device structure
* @cmd: ethtool rxnfc command
@@ -28,9 +47,6 @@ static int idpf_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
user_config = &np->adapter->vport_config[np->vport_idx]->user_config;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = vport->num_rxq;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = user_config->num_fsteer_fltrs;
cmd->data = idpf_fsteer_max_rules(vport);
@@ -386,7 +402,7 @@ static int idpf_get_rxfh(struct net_device *netdev,
}
rss_data = &adapter->vport_config[np->vport_idx]->user_config.rss_data;
- if (np->state != __IDPF_VPORT_UP)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
goto unlock_mutex;
rxfh->hfunc = ETH_RSS_HASH_TOP;
@@ -436,7 +452,7 @@ static int idpf_set_rxfh(struct net_device *netdev,
}
rss_data = &adapter->vport_config[vport->idx]->user_config.rss_data;
- if (np->state != __IDPF_VPORT_UP)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
goto unlock_mutex;
if (rxfh->hfunc != ETH_RSS_HASH_NO_CHANGE &&
@@ -1167,7 +1183,7 @@ static void idpf_get_ethtool_stats(struct net_device *netdev,
idpf_vport_ctrl_lock(netdev);
vport = idpf_netdev_to_vport(netdev);
- if (np->state != __IDPF_VPORT_UP) {
+ if (!test_bit(IDPF_VPORT_UP, np->state)) {
idpf_vport_ctrl_unlock(netdev);
return;
@@ -1319,7 +1335,7 @@ static int idpf_get_q_coalesce(struct net_device *netdev,
idpf_vport_ctrl_lock(netdev);
vport = idpf_netdev_to_vport(netdev);
- if (np->state != __IDPF_VPORT_UP)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
goto unlock_mutex;
if (q_num >= vport->num_rxq && q_num >= vport->num_txq) {
@@ -1507,7 +1523,7 @@ static int idpf_set_coalesce(struct net_device *netdev,
idpf_vport_ctrl_lock(netdev);
vport = idpf_netdev_to_vport(netdev);
- if (np->state != __IDPF_VPORT_UP)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
goto unlock_mutex;
for (i = 0; i < vport->num_txq; i++) {
@@ -1710,7 +1726,7 @@ static void idpf_get_ts_stats(struct net_device *netdev,
ts_stats->err = u64_stats_read(&vport->tstamp_stats.discarded);
} while (u64_stats_fetch_retry(&vport->tstamp_stats.stats_sync, start));
- if (np->state != __IDPF_VPORT_UP)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
goto exit;
for (u16 i = 0; i < vport->num_txq_grp; i++) {
@@ -1757,6 +1773,7 @@ static const struct ethtool_ops idpf_ethtool_ops = {
.get_channels = idpf_get_channels,
.get_rxnfc = idpf_get_rxnfc,
.set_rxnfc = idpf_set_rxnfc,
+ .get_rx_ring_count = idpf_get_rx_ring_count,
.get_rxfh_key_size = idpf_get_rxfh_key_size,
.get_rxfh_indir_size = idpf_get_rxfh_indir_size,
.get_rxfh = idpf_get_rxfh,
diff --git a/drivers/net/ethernet/intel/idpf/idpf_lib.c b/drivers/net/ethernet/intel/idpf/idpf_lib.c
index 8a941f0fb048..7a7e101afeb6 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_lib.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_lib.c
@@ -519,7 +519,7 @@ static int idpf_del_mac_filter(struct idpf_vport *vport,
}
spin_unlock_bh(&vport_config->mac_filter_list_lock);
- if (np->state == __IDPF_VPORT_UP) {
+ if (test_bit(IDPF_VPORT_UP, np->state)) {
int err;
err = idpf_add_del_mac_filters(vport, np, false, async);
@@ -590,7 +590,7 @@ static int idpf_add_mac_filter(struct idpf_vport *vport,
if (err)
return err;
- if (np->state == __IDPF_VPORT_UP)
+ if (test_bit(IDPF_VPORT_UP, np->state))
err = idpf_add_del_mac_filters(vport, np, true, async);
return err;
@@ -894,7 +894,7 @@ static void idpf_vport_stop(struct idpf_vport *vport, bool rtnl)
{
struct idpf_netdev_priv *np = netdev_priv(vport->netdev);
- if (np->state <= __IDPF_VPORT_DOWN)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
return;
if (rtnl)
@@ -921,7 +921,7 @@ static void idpf_vport_stop(struct idpf_vport *vport, bool rtnl)
idpf_xdp_rxq_info_deinit_all(vport);
idpf_vport_queues_rel(vport);
idpf_vport_intr_rel(vport);
- np->state = __IDPF_VPORT_DOWN;
+ clear_bit(IDPF_VPORT_UP, np->state);
if (rtnl)
rtnl_unlock();
@@ -1345,7 +1345,7 @@ static int idpf_up_complete(struct idpf_vport *vport)
netif_tx_start_all_queues(vport->netdev);
}
- np->state = __IDPF_VPORT_UP;
+ set_bit(IDPF_VPORT_UP, np->state);
return 0;
}
@@ -1391,7 +1391,7 @@ static int idpf_vport_open(struct idpf_vport *vport, bool rtnl)
struct idpf_vport_config *vport_config;
int err;
- if (np->state != __IDPF_VPORT_DOWN)
+ if (test_bit(IDPF_VPORT_UP, np->state))
return -EBUSY;
if (rtnl)
@@ -1602,7 +1602,7 @@ void idpf_init_task(struct work_struct *work)
/* Once state is put into DOWN, driver is ready for dev_open */
np = netdev_priv(vport->netdev);
- np->state = __IDPF_VPORT_DOWN;
+ clear_bit(IDPF_VPORT_UP, np->state);
if (test_and_clear_bit(IDPF_VPORT_UP_REQUESTED, vport_config->flags))
idpf_vport_open(vport, true);
@@ -1801,7 +1801,7 @@ static void idpf_set_vport_state(struct idpf_adapter *adapter)
continue;
np = netdev_priv(adapter->netdevs[i]);
- if (np->state == __IDPF_VPORT_UP)
+ if (test_bit(IDPF_VPORT_UP, np->state))
set_bit(IDPF_VPORT_UP_REQUESTED,
adapter->vport_config[i]->flags);
}
@@ -1939,7 +1939,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport,
enum idpf_vport_reset_cause reset_cause)
{
struct idpf_netdev_priv *np = netdev_priv(vport->netdev);
- enum idpf_vport_state current_state = np->state;
+ bool vport_is_up = test_bit(IDPF_VPORT_UP, np->state);
struct idpf_adapter *adapter = vport->adapter;
struct idpf_vport *new_vport;
int err;
@@ -1990,7 +1990,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport,
goto free_vport;
}
- if (current_state <= __IDPF_VPORT_DOWN) {
+ if (!vport_is_up) {
idpf_send_delete_queues_msg(vport);
} else {
set_bit(IDPF_VPORT_DEL_QUEUES, vport->flags);
@@ -2023,7 +2023,7 @@ int idpf_initiate_soft_reset(struct idpf_vport *vport,
if (err)
goto err_open;
- if (current_state == __IDPF_VPORT_UP)
+ if (vport_is_up)
err = idpf_vport_open(vport, false);
goto free_vport;
@@ -2033,7 +2033,7 @@ err_reset:
vport->num_rxq, vport->num_bufq);
err_open:
- if (current_state == __IDPF_VPORT_UP)
+ if (vport_is_up)
idpf_vport_open(vport, false);
free_vport:
diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
index 8c46481d2e1f..de5d722cc21d 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_main.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
@@ -3,16 +3,94 @@
#include "idpf.h"
#include "idpf_devids.h"
+#include "idpf_lan_vf_regs.h"
#include "idpf_virtchnl.h"
#define DRV_SUMMARY "Intel(R) Infrastructure Data Path Function Linux Driver"
+#define IDPF_NETWORK_ETHERNET_PROGIF 0x01
+#define IDPF_CLASS_NETWORK_ETHERNET_PROGIF \
+ (PCI_CLASS_NETWORK_ETHERNET << 8 | IDPF_NETWORK_ETHERNET_PROGIF)
+#define IDPF_VF_TEST_VAL 0xfeed0000u
+
MODULE_DESCRIPTION(DRV_SUMMARY);
MODULE_IMPORT_NS("LIBETH");
MODULE_IMPORT_NS("LIBETH_XDP");
MODULE_LICENSE("GPL");
/**
+ * idpf_get_device_type - Helper to find if it is a VF or PF device
+ * @pdev: PCI device information struct
+ *
+ * Return: PF/VF device ID or -%errno on failure.
+ */
+static int idpf_get_device_type(struct pci_dev *pdev)
+{
+ void __iomem *addr;
+ int ret;
+
+ addr = ioremap(pci_resource_start(pdev, 0) + VF_ARQBAL, 4);
+ if (!addr) {
+ pci_err(pdev, "Failed to allocate BAR0 mbx region\n");
+ return -EIO;
+ }
+
+ writel(IDPF_VF_TEST_VAL, addr);
+ if (readl(addr) == IDPF_VF_TEST_VAL)
+ ret = IDPF_DEV_ID_VF;
+ else
+ ret = IDPF_DEV_ID_PF;
+
+ iounmap(addr);
+
+ return ret;
+}
+
+/**
+ * idpf_dev_init - Initialize device specific parameters
+ * @adapter: adapter to initialize
+ * @ent: entry in idpf_pci_tbl
+ *
+ * Return: %0 on success, -%errno on failure.
+ */
+static int idpf_dev_init(struct idpf_adapter *adapter,
+ const struct pci_device_id *ent)
+{
+ int ret;
+
+ if (ent->class == IDPF_CLASS_NETWORK_ETHERNET_PROGIF) {
+ ret = idpf_get_device_type(adapter->pdev);
+ switch (ret) {
+ case IDPF_DEV_ID_VF:
+ idpf_vf_dev_ops_init(adapter);
+ adapter->crc_enable = true;
+ break;
+ case IDPF_DEV_ID_PF:
+ idpf_dev_ops_init(adapter);
+ break;
+ default:
+ return ret;
+ }
+
+ return 0;
+ }
+
+ switch (ent->device) {
+ case IDPF_DEV_ID_PF:
+ idpf_dev_ops_init(adapter);
+ break;
+ case IDPF_DEV_ID_VF:
+ idpf_vf_dev_ops_init(adapter);
+ adapter->crc_enable = true;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/**
* idpf_remove - Device removal routine
* @pdev: PCI device information struct
*/
@@ -63,6 +141,8 @@ destroy_wqs:
destroy_workqueue(adapter->vc_event_wq);
for (i = 0; i < adapter->max_vports; i++) {
+ if (!adapter->vport_config[i])
+ continue;
kfree(adapter->vport_config[i]->user_config.q_coalesce);
kfree(adapter->vport_config[i]);
adapter->vport_config[i] = NULL;
@@ -165,21 +245,6 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
adapter->req_tx_splitq = true;
adapter->req_rx_splitq = true;
- switch (ent->device) {
- case IDPF_DEV_ID_PF:
- idpf_dev_ops_init(adapter);
- break;
- case IDPF_DEV_ID_VF:
- idpf_vf_dev_ops_init(adapter);
- adapter->crc_enable = true;
- break;
- default:
- err = -ENODEV;
- dev_err(&pdev->dev, "Unexpected dev ID 0x%x in idpf probe\n",
- ent->device);
- goto err_free;
- }
-
adapter->pdev = pdev;
err = pcim_enable_device(pdev);
if (err)
@@ -259,11 +324,18 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
/* setup msglvl */
adapter->msg_enable = netif_msg_init(-1, IDPF_AVAIL_NETIF_M);
+ err = idpf_dev_init(adapter, ent);
+ if (err) {
+ dev_err(&pdev->dev, "Unexpected dev ID 0x%x in idpf probe\n",
+ ent->device);
+ goto destroy_vc_event_wq;
+ }
+
err = idpf_cfg_hw(adapter);
if (err) {
dev_err(dev, "Failed to configure HW structure for adapter: %d\n",
err);
- goto err_cfg_hw;
+ goto destroy_vc_event_wq;
}
mutex_init(&adapter->vport_ctrl_lock);
@@ -284,7 +356,7 @@ static int idpf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
-err_cfg_hw:
+destroy_vc_event_wq:
destroy_workqueue(adapter->vc_event_wq);
err_vc_event_wq_alloc:
destroy_workqueue(adapter->stats_wq);
@@ -304,6 +376,7 @@ err_free:
static const struct pci_device_id idpf_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, IDPF_DEV_ID_PF)},
{ PCI_VDEVICE(INTEL, IDPF_DEV_ID_VF)},
+ { PCI_DEVICE_CLASS(IDPF_CLASS_NETWORK_ETHERNET_PROGIF, ~0)},
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(pci, idpf_pci_tbl);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c
index 61e613066140..e3ddf18dcbf5 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_singleq_txrx.c
@@ -570,7 +570,7 @@ fetch_next_txq_desc:
np = netdev_priv(tx_q->netdev);
nq = netdev_get_tx_queue(tx_q->netdev, tx_q->idx);
- dont_wake = np->state != __IDPF_VPORT_UP ||
+ dont_wake = !test_bit(IDPF_VPORT_UP, np->state) ||
!netif_carrier_ok(tx_q->netdev);
__netif_txq_completed_wake(nq, ss.packets, ss.bytes,
IDPF_DESC_UNUSED(tx_q), IDPF_TX_WAKE_THRESH,
diff --git a/drivers/net/ethernet/intel/idpf/idpf_txrx.c b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
index 828f7c444d30..1d91c56f7469 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_txrx.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_txrx.c
@@ -134,7 +134,7 @@ static void idpf_compl_desc_rel(struct idpf_compl_queue *complq)
{
idpf_xsk_clear_queue(complq, VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION);
- if (!complq->comp)
+ if (!complq->desc_ring)
return;
dma_free_coherent(complq->netdev->dev.parent, complq->size,
@@ -922,8 +922,8 @@ static int idpf_rx_desc_alloc_all(struct idpf_vport *vport)
err = idpf_rx_desc_alloc(vport, q);
if (err) {
pci_err(vport->adapter->pdev,
- "Memory allocation for Rx Queue %u failed\n",
- i);
+ "Memory allocation for Rx queue %u from queue group %u failed\n",
+ j, i);
goto err_out;
}
}
@@ -939,8 +939,8 @@ static int idpf_rx_desc_alloc_all(struct idpf_vport *vport)
err = idpf_bufq_desc_alloc(vport, q);
if (err) {
pci_err(vport->adapter->pdev,
- "Memory allocation for Rx Buffer Queue %u failed\n",
- i);
+ "Memory allocation for Rx Buffer Queue %u from queue group %u failed\n",
+ j, i);
goto err_out;
}
}
@@ -2275,7 +2275,7 @@ fetch_next_desc:
/* Update BQL */
nq = netdev_get_tx_queue(tx_q->netdev, tx_q->idx);
- dont_wake = !complq_ok || np->state != __IDPF_VPORT_UP ||
+ dont_wake = !complq_ok || !test_bit(IDPF_VPORT_UP, np->state) ||
!netif_carrier_ok(tx_q->netdev);
/* Check if the TXQ needs to and can be restarted */
__netif_txq_completed_wake(nq, tx_q->cleaned_pkts, tx_q->cleaned_bytes,
diff --git a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
index cbb5fa30f5a0..44cd4b466c48 100644
--- a/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
+++ b/drivers/net/ethernet/intel/idpf/idpf_virtchnl.c
@@ -68,7 +68,7 @@ static void idpf_handle_event_link(struct idpf_adapter *adapter,
vport->link_up = v2e->link_status;
- if (np->state != __IDPF_VPORT_UP)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
return;
if (vport->link_up) {
@@ -2755,7 +2755,7 @@ int idpf_send_get_stats_msg(struct idpf_vport *vport)
/* Don't send get_stats message if the link is down */
- if (np->state <= __IDPF_VPORT_DOWN)
+ if (!test_bit(IDPF_VPORT_UP, np->state))
return 0;
stats_msg.vport_id = cpu_to_le32(vport->vport_id);
diff --git a/drivers/net/ethernet/intel/idpf/xdp.c b/drivers/net/ethernet/intel/idpf/xdp.c
index 21ce25b0567f..958d16f87424 100644
--- a/drivers/net/ethernet/intel/idpf/xdp.c
+++ b/drivers/net/ethernet/intel/idpf/xdp.c
@@ -418,7 +418,7 @@ static int idpf_xdp_setup_prog(struct idpf_vport *vport,
if (test_bit(IDPF_REMOVE_IN_PROG, vport->adapter->flags) ||
!test_bit(IDPF_VPORT_REG_NETDEV, cfg->flags) ||
!!vport->xdp_prog == !!prog) {
- if (np->state == __IDPF_VPORT_UP)
+ if (test_bit(IDPF_VPORT_UP, np->state))
idpf_xdp_copy_prog_to_rqs(vport, prog);
old = xchg(&vport->xdp_prog, prog);
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index f8a208c84f15..b507576b28b2 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -2281,7 +2281,7 @@ static int igb_get_sset_count(struct net_device *netdev, int sset)
case ETH_SS_PRIV_FLAGS:
return IGB_PRIV_FLAGS_STR_LEN;
default:
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
}
@@ -2541,6 +2541,13 @@ static int igb_get_rxfh_fields(struct net_device *dev,
return 0;
}
+static u32 igb_get_rx_ring_count(struct net_device *dev)
+{
+ struct igb_adapter *adapter = netdev_priv(dev);
+
+ return adapter->num_rx_queues;
+}
+
static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
@@ -2548,10 +2555,6 @@ static int igb_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = adapter->num_rx_queues;
- ret = 0;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = adapter->nfc_filter_count;
ret = 0;
@@ -3473,6 +3476,7 @@ static const struct ethtool_ops igb_ethtool_ops = {
.get_ts_info = igb_get_ts_info,
.get_rxnfc = igb_get_rxnfc,
.set_rxnfc = igb_set_rxnfc,
+ .get_rx_ring_count = igb_get_rx_ring_count,
.get_eee = igb_get_eee,
.set_eee = igb_set_eee,
.get_module_info = igb_get_module_info,
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 85f9589cc568..dbea37269d2c 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -9599,7 +9599,6 @@ static int __igb_resume(struct device *dev, bool rpm)
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (!pci_device_is_present(pdev))
return -ENODEV;
@@ -9754,7 +9753,6 @@ static pci_ers_result_t igb_io_slot_reset(struct pci_dev *pdev)
} else {
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
pci_enable_wake(pdev, PCI_D3hot, 0);
pci_enable_wake(pdev, PCI_D3cold, 0);
diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c
index a7876882aeaf..bd85d02ecadd 100644
--- a/drivers/net/ethernet/intel/igb/igb_ptp.c
+++ b/drivers/net/ethernet/intel/igb/igb_ptp.c
@@ -840,14 +840,11 @@ static void igb_ptp_overflow_check(struct work_struct *work)
struct igb_adapter *igb =
container_of(work, struct igb_adapter, ptp_overflow_work.work);
struct timespec64 ts;
- u64 ns;
/* Update the timecounter */
- ns = timecounter_read(&igb->tc);
+ ts = ns_to_timespec64(timecounter_read(&igb->tc));
- ts = ns_to_timespec64(ns);
- pr_debug("igb overflow check at %lld.%09lu\n",
- (long long) ts.tv_sec, ts.tv_nsec);
+ pr_debug("igb overflow check at %ptSp\n", &ts);
schedule_delayed_work(&igb->ptp_overflow_work,
IGB_SYSTIM_OVERFLOW_PERIOD);
diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c
index 61dfcd8cb370..ac57212ab02b 100644
--- a/drivers/net/ethernet/intel/igbvf/netdev.c
+++ b/drivers/net/ethernet/intel/igbvf/netdev.c
@@ -1235,7 +1235,7 @@ static int igbvf_vlan_rx_add_vid(struct net_device *netdev,
spin_lock_bh(&hw->mbx_lock);
if (hw->mac.ops.set_vfta(hw, vid, true)) {
- dev_warn(&adapter->pdev->dev, "Vlan id %d\n is not added", vid);
+ dev_warn(&adapter->pdev->dev, "Vlan id %d is not added\n", vid);
spin_unlock_bh(&hw->mbx_lock);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
index f3e7218ba6f3..e94c1922b97a 100644
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
@@ -810,7 +810,7 @@ static int igc_ethtool_get_sset_count(struct net_device *netdev, int sset)
case ETH_SS_PRIV_FLAGS:
return IGC_PRIV_FLAGS_STR_LEN;
default:
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
}
@@ -1091,15 +1091,19 @@ static int igc_ethtool_get_rxfh_fields(struct net_device *dev,
return 0;
}
+static u32 igc_ethtool_get_rx_ring_count(struct net_device *dev)
+{
+ struct igc_adapter *adapter = netdev_priv(dev);
+
+ return adapter->num_rx_queues;
+}
+
static int igc_ethtool_get_rxnfc(struct net_device *dev,
struct ethtool_rxnfc *cmd, u32 *rule_locs)
{
struct igc_adapter *adapter = netdev_priv(dev);
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = adapter->num_rx_queues;
- return 0;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = adapter->nfc_rule_count;
return 0;
@@ -2094,6 +2098,9 @@ static void igc_ethtool_diag_test(struct net_device *netdev,
netdev_info(adapter->netdev, "Offline testing starting");
set_bit(__IGC_TESTING, &adapter->state);
+ /* power up PHY for link test */
+ igc_power_up_phy_copper(&adapter->hw);
+
/* Link test performed before hardware reset so autoneg doesn't
* interfere with test result
*/
@@ -2167,6 +2174,7 @@ static const struct ethtool_ops igc_ethtool_ops = {
.set_coalesce = igc_ethtool_set_coalesce,
.get_rxnfc = igc_ethtool_get_rxnfc,
.set_rxnfc = igc_ethtool_set_rxnfc,
+ .get_rx_ring_count = igc_ethtool_get_rx_ring_count,
.get_rxfh_indir_size = igc_ethtool_get_rxfh_indir_size,
.get_rxfh = igc_ethtool_get_rxfh,
.set_rxfh = igc_ethtool_set_rxfh,
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 728d7ca5338b..7aafa60ba0c8 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -7530,7 +7530,6 @@ static int __igc_resume(struct device *dev, bool rpm)
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (!pci_device_is_present(pdev))
return -ENODEV;
@@ -7667,7 +7666,6 @@ static pci_ers_result_t igc_io_slot_reset(struct pci_dev *pdev)
} else {
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
pci_enable_wake(pdev, PCI_D3hot, 0);
pci_enable_wake(pdev, PCI_D3cold, 0);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
index 14d275270123..dce4936708eb 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
@@ -821,9 +821,7 @@ struct ixgbe_adapter {
#ifdef CONFIG_IXGBE_HWMON
struct hwmon_buff *ixgbe_hwmon_buff;
#endif /* CONFIG_IXGBE_HWMON */
-#ifdef CONFIG_DEBUG_FS
struct dentry *ixgbe_dbg_adapter;
-#endif /*CONFIG_DEBUG_FS*/
u8 default_up;
/* Bitmask indicating in use pools */
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index d5b1b974b4a3..3069b583fd81 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -198,7 +198,7 @@ static int prot_autoc_read_82599(struct ixgbe_hw *hw, bool *locked,
* @hw: pointer to hardware structure
* @autoc: value to write to AUTOC
* @locked: bool to indicate whether the SW/FW lock was already taken by
- * previous proc_autoc_read_82599.
+ * previous prot_autoc_read_82599.
*
* This part (82599) may need to hold a the SW/FW lock around all writes to
* AUTOC. Likewise after a write we need to do a pipeline reset.
@@ -1622,7 +1622,7 @@ int ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw,
break;
}
- /* store source and destination IP masks (big-enian) */
+ /* store source and destination IP masks (big-endian) */
IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRSIP4M,
~input_mask->formatted.src_ip[0]);
IXGBE_WRITE_REG_BE32(hw, IXGBE_FDIRDIP4M,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
index 2d660e9edb80..2ad81f687a84 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c
@@ -2805,6 +2805,14 @@ static int ixgbe_rss_indir_tbl_max(struct ixgbe_adapter *adapter)
return 64;
}
+static u32 ixgbe_get_rx_ring_count(struct net_device *dev)
+{
+ struct ixgbe_adapter *adapter = ixgbe_from_netdev(dev);
+
+ return min_t(u32, adapter->num_rx_queues,
+ ixgbe_rss_indir_tbl_max(adapter));
+}
+
static int ixgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
@@ -2812,11 +2820,6 @@ static int ixgbe_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
int ret = -EOPNOTSUPP;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = min_t(int, adapter->num_rx_queues,
- ixgbe_rss_indir_tbl_max(adapter));
- ret = 0;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = adapter->fdir_filter_count;
ret = 0;
@@ -3743,6 +3746,7 @@ static const struct ethtool_ops ixgbe_ethtool_ops = {
.get_ethtool_stats = ixgbe_get_ethtool_stats,
.get_coalesce = ixgbe_get_coalesce,
.set_coalesce = ixgbe_set_coalesce,
+ .get_rx_ring_count = ixgbe_get_rx_ring_count,
.get_rxnfc = ixgbe_get_rxnfc,
.set_rxnfc = ixgbe_set_rxnfc,
.get_rxfh_indir_size = ixgbe_rss_indir_size,
@@ -3791,6 +3795,7 @@ static const struct ethtool_ops ixgbe_ethtool_ops_e610 = {
.get_ethtool_stats = ixgbe_get_ethtool_stats,
.get_coalesce = ixgbe_get_coalesce,
.set_coalesce = ixgbe_set_coalesce,
+ .get_rx_ring_count = ixgbe_get_rx_ring_count,
.get_rxnfc = ixgbe_get_rxnfc,
.set_rxnfc = ixgbe_set_rxnfc,
.get_rxfh_indir_size = ixgbe_rss_indir_size,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
index 170a29d162c6..a1d04914fbbc 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c
@@ -318,7 +318,7 @@ static int ixgbe_xdp_queues(struct ixgbe_adapter *adapter)
* ixgbe_set_dcb_sriov_queues: Allocate queues for SR-IOV devices w/ DCB
* @adapter: board private structure to initialize
*
- * When SR-IOV (Single Root IO Virtualiztion) is enabled, allocate queues
+ * When SR-IOV (Single Root IO Virtualization) is enabled, allocate queues
* and VM pools where appropriate. Also assign queues based on DCB
* priorities and map accordingly..
*
@@ -492,7 +492,7 @@ static bool ixgbe_set_dcb_queues(struct ixgbe_adapter *adapter)
* ixgbe_set_sriov_queues - Allocate queues for SR-IOV devices
* @adapter: board private structure to initialize
*
- * When SR-IOV (Single Root IO Virtualiztion) is enabled, allocate queues
+ * When SR-IOV (Single Root IO Virtualization) is enabled, allocate queues
* and VM pools where appropriate. If RSS is available, then also try and
* enable RSS and map accordingly.
*
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index ca1ccc630001..034618e79169 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7449,7 +7449,7 @@ int ixgbe_open(struct net_device *netdev)
adapter->hw.link.link_info.link_cfg_err);
err = ixgbe_non_sfp_link_config(&adapter->hw);
- if (ixgbe_non_sfp_link_config(&adapter->hw))
+ if (err)
e_dev_err("Link setup failed, err %d.\n", err);
}
@@ -11507,10 +11507,10 @@ static int ixgbe_recovery_probe(struct ixgbe_adapter *adapter)
shutdown_aci:
mutex_destroy(&adapter->hw.aci.lock);
ixgbe_release_hw_control(adapter);
- devlink_free(adapter->devlink);
clean_up_probe:
disable_dev = !test_and_set_bit(__IXGBE_DISABLED, &adapter->state);
free_netdev(netdev);
+ devlink_free(adapter->devlink);
pci_release_mem_regions(pdev);
if (disable_dev)
pci_disable_device(pdev);
@@ -12046,7 +12046,7 @@ err_dma:
* @pdev: PCI device information struct
*
* ixgbe_remove is called by the PCI subsystem to alert the driver
- * that it should release a PCI device. The could be caused by a
+ * that it should release a PCI device. This could be caused by a
* Hot-Plug event, or because the driver is going to be removed from
* memory.
**/
@@ -12298,7 +12298,6 @@ static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
adapter->hw.hw_addr = adapter->io_addr;
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
pci_wake_from_d3(pdev, false);
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
index 114dd88fc71c..6885d2343c48 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c
@@ -641,7 +641,7 @@ static int ixgbe_ptp_feature_enable(struct ptp_clock_info *ptp,
* disabled
*/
if (rq->type != PTP_CLK_REQ_PPS || !adapter->ptp_setup_sdp)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
if (on)
adapter->flags2 |= IXGBE_FLAG2_PTP_PPS_ENABLED;
diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
index bebad564188e..537a60d5276f 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c
@@ -867,19 +867,11 @@ static int ixgbevf_set_coalesce(struct net_device *netdev,
return 0;
}
-static int ixgbevf_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
- u32 *rules __always_unused)
+static u32 ixgbevf_get_rx_ring_count(struct net_device *dev)
{
struct ixgbevf_adapter *adapter = netdev_priv(dev);
- switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- info->data = adapter->num_rx_queues;
- return 0;
- default:
- hw_dbg(&adapter->hw, "Command parameters not supported\n");
- return -EOPNOTSUPP;
- }
+ return adapter->num_rx_queues;
}
static u32 ixgbevf_get_rxfh_indir_size(struct net_device *netdev)
@@ -987,7 +979,7 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = {
.get_ethtool_stats = ixgbevf_get_ethtool_stats,
.get_coalesce = ixgbevf_get_coalesce,
.set_coalesce = ixgbevf_set_coalesce,
- .get_rxnfc = ixgbevf_get_rxnfc,
+ .get_rx_ring_count = ixgbevf_get_rx_ring_count,
.get_rxfh_indir_size = ixgbevf_get_rxfh_indir_size,
.get_rxfh_key_size = ixgbevf_get_rxfh_key_size,
.get_rxfh = ixgbevf_get_rxfh,
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
index 039187607e98..516a6fdd23d0 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h
@@ -241,23 +241,7 @@ struct ixgbevf_q_vector {
char name[IFNAMSIZ + 9];
/* for dynamic allocation of rings associated with this q_vector */
- struct ixgbevf_ring ring[0] ____cacheline_internodealigned_in_smp;
-#ifdef CONFIG_NET_RX_BUSY_POLL
- unsigned int state;
-#define IXGBEVF_QV_STATE_IDLE 0
-#define IXGBEVF_QV_STATE_NAPI 1 /* NAPI owns this QV */
-#define IXGBEVF_QV_STATE_POLL 2 /* poll owns this QV */
-#define IXGBEVF_QV_STATE_DISABLED 4 /* QV is disabled */
-#define IXGBEVF_QV_OWNED (IXGBEVF_QV_STATE_NAPI | IXGBEVF_QV_STATE_POLL)
-#define IXGBEVF_QV_LOCKED (IXGBEVF_QV_OWNED | IXGBEVF_QV_STATE_DISABLED)
-#define IXGBEVF_QV_STATE_NAPI_YIELD 8 /* NAPI yielded this QV */
-#define IXGBEVF_QV_STATE_POLL_YIELD 16 /* poll yielded this QV */
-#define IXGBEVF_QV_YIELD (IXGBEVF_QV_STATE_NAPI_YIELD | \
- IXGBEVF_QV_STATE_POLL_YIELD)
-#define IXGBEVF_QV_USER_PEND (IXGBEVF_QV_STATE_POLL | \
- IXGBEVF_QV_STATE_POLL_YIELD)
- spinlock_t lock;
-#endif /* CONFIG_NET_RX_BUSY_POLL */
+ struct ixgbevf_ring ring[] ____cacheline_internodealigned_in_smp;
};
/* microsecond values for various ITR rates shifted by 2 to fit itr register
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 89ccb8eb82c7..7af44f858fa3 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -5012,17 +5012,9 @@ static u32 mvneta_ethtool_get_rxfh_indir_size(struct net_device *dev)
return MVNETA_RSS_LU_TABLE_SIZE;
}
-static int mvneta_ethtool_get_rxnfc(struct net_device *dev,
- struct ethtool_rxnfc *info,
- u32 *rules __always_unused)
+static u32 mvneta_ethtool_get_rx_ring_count(struct net_device *dev)
{
- switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- info->data = rxq_number;
- return 0;
- default:
- return -EOPNOTSUPP;
- }
+ return rxq_number;
}
static int mvneta_config_rss(struct mvneta_port *pp)
@@ -5356,7 +5348,7 @@ static const struct ethtool_ops mvneta_eth_tool_ops = {
.get_ethtool_stats = mvneta_ethtool_get_stats,
.get_sset_count = mvneta_ethtool_get_sset_count,
.get_rxfh_indir_size = mvneta_ethtool_get_rxfh_indir_size,
- .get_rxnfc = mvneta_ethtool_get_rxnfc,
+ .get_rx_ring_count = mvneta_ethtool_get_rx_ring_count,
.get_rxfh = mvneta_ethtool_get_rxfh,
.set_rxfh = mvneta_ethtool_set_rxfh,
.get_link_ksettings = mvneta_ethtool_get_link_ksettings,
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index ab0c99aa9f9a..33426fded919 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -5580,6 +5580,13 @@ static int mvpp2_ethtool_set_link_ksettings(struct net_device *dev,
return phylink_ethtool_ksettings_set(port->phylink, cmd);
}
+static u32 mvpp2_ethtool_get_rx_ring_count(struct net_device *dev)
+{
+ struct mvpp2_port *port = netdev_priv(dev);
+
+ return port->nrxqs;
+}
+
static int mvpp2_ethtool_get_rxnfc(struct net_device *dev,
struct ethtool_rxnfc *info, u32 *rules)
{
@@ -5590,9 +5597,6 @@ static int mvpp2_ethtool_get_rxnfc(struct net_device *dev,
return -EOPNOTSUPP;
switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- info->data = port->nrxqs;
- break;
case ETHTOOL_GRXCLSRLCNT:
info->rule_cnt = port->n_rfs_rules;
break;
@@ -5827,6 +5831,7 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = {
.set_pauseparam = mvpp2_ethtool_set_pause_param,
.get_link_ksettings = mvpp2_ethtool_get_link_ksettings,
.set_link_ksettings = mvpp2_ethtool_set_link_ksettings,
+ .get_rx_ring_count = mvpp2_ethtool_get_rx_ring_count,
.get_rxnfc = mvpp2_ethtool_get_rxnfc,
.set_rxnfc = mvpp2_ethtool_set_rxnfc,
.get_rxfh_indir_size = mvpp2_ethtool_get_rxfh_indir_size,
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
index 532813d8d028..244de500963e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile
+++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile
@@ -12,4 +12,5 @@ rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \
rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \
rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \
rvu_sdp.o rvu_npc_hash.o mcs.o mcs_rvu_if.o mcs_cnf10kb.o \
- rvu_rep.o cn20k/mbox_init.o
+ rvu_rep.o cn20k/mbox_init.o cn20k/nix.o cn20k/debugfs.o \
+ cn20k/npa.o
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
index ec0e11c77cbf..42044cd810b1 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
@@ -1994,7 +1994,7 @@ static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id)
nvec = pci_msix_vec_count(cgx->pdev);
err = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX);
- if (err < 0 || err != nvec) {
+ if (err < 0) {
dev_err(dev, "Request for %d msix vectors failed, err %d\n",
nvec, err);
goto err_release_regions;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
new file mode 100644
index 000000000000..498968bf4cf5
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2024 Marvell.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "struct.h"
+#include "debugfs.h"
+
+void print_nix_cn20k_sq_ctx(struct seq_file *m,
+ struct nix_cn20k_sq_ctx_s *sq_ctx)
+{
+ seq_printf(m, "W0: ena \t\t\t%d\nW0: qint_idx \t\t\t%d\n",
+ sq_ctx->ena, sq_ctx->qint_idx);
+ seq_printf(m, "W0: substream \t\t\t0x%03x\nW0: sdp_mcast \t\t\t%d\n",
+ sq_ctx->substream, sq_ctx->sdp_mcast);
+ seq_printf(m, "W0: cq \t\t\t\t%d\nW0: sqe_way_mask \t\t%d\n\n",
+ sq_ctx->cq, sq_ctx->sqe_way_mask);
+
+ seq_printf(m, "W1: smq \t\t\t%d\nW1: cq_ena \t\t\t%d\nW1: xoff\t\t\t%d\n",
+ sq_ctx->smq, sq_ctx->cq_ena, sq_ctx->xoff);
+ seq_printf(m, "W1: sso_ena \t\t\t%d\nW1: smq_rr_weight\t\t%d\n",
+ sq_ctx->sso_ena, sq_ctx->smq_rr_weight);
+ seq_printf(m, "W1: default_chan\t\t%d\nW1: sqb_count\t\t\t%d\n\n",
+ sq_ctx->default_chan, sq_ctx->sqb_count);
+
+ seq_printf(m, "W1: smq_rr_count_lb \t\t%d\n", sq_ctx->smq_rr_count_lb);
+ seq_printf(m, "W2: smq_rr_count_ub \t\t%d\n", sq_ctx->smq_rr_count_ub);
+ seq_printf(m, "W2: sqb_aura \t\t\t%d\nW2: sq_int \t\t\t%d\n",
+ sq_ctx->sqb_aura, sq_ctx->sq_int);
+ seq_printf(m, "W2: sq_int_ena \t\t\t%d\nW2: sqe_stype \t\t\t%d\n",
+ sq_ctx->sq_int_ena, sq_ctx->sqe_stype);
+
+ seq_printf(m, "W3: max_sqe_size\t\t%d\nW3: cq_limit\t\t\t%d\n",
+ sq_ctx->max_sqe_size, sq_ctx->cq_limit);
+ seq_printf(m, "W3: lmt_dis \t\t\t%d\nW3: mnq_dis \t\t\t%d\n",
+ sq_ctx->lmt_dis, sq_ctx->mnq_dis);
+ seq_printf(m, "W3: smq_next_sq\t\t\t%d\nW3: smq_lso_segnum\t\t%d\n",
+ sq_ctx->smq_next_sq, sq_ctx->smq_lso_segnum);
+ seq_printf(m, "W3: tail_offset \t\t%d\nW3: smenq_offset\t\t%d\n",
+ sq_ctx->tail_offset, sq_ctx->smenq_offset);
+ seq_printf(m, "W3: head_offset\t\t\t%d\nW3: smenq_next_sqb_vld\t\t%d\n\n",
+ sq_ctx->head_offset, sq_ctx->smenq_next_sqb_vld);
+
+ seq_printf(m, "W3: smq_next_sq_vld\t\t%d\nW3: smq_pend\t\t\t%d\n",
+ sq_ctx->smq_next_sq_vld, sq_ctx->smq_pend);
+ seq_printf(m, "W4: next_sqb \t\t\t%llx\n\n", sq_ctx->next_sqb);
+ seq_printf(m, "W5: tail_sqb \t\t\t%llx\n\n", sq_ctx->tail_sqb);
+ seq_printf(m, "W6: smenq_sqb \t\t\t%llx\n\n", sq_ctx->smenq_sqb);
+ seq_printf(m, "W7: smenq_next_sqb \t\t%llx\n\n",
+ sq_ctx->smenq_next_sqb);
+
+ seq_printf(m, "W8: head_sqb\t\t\t%llx\n\n", sq_ctx->head_sqb);
+
+ seq_printf(m, "W9: vfi_lso_total\t\t%d\n", sq_ctx->vfi_lso_total);
+ seq_printf(m, "W9: vfi_lso_sizem1\t\t%d\nW9: vfi_lso_sb\t\t\t%d\n",
+ sq_ctx->vfi_lso_sizem1, sq_ctx->vfi_lso_sb);
+ seq_printf(m, "W9: vfi_lso_mps\t\t\t%d\nW9: vfi_lso_vlan0_ins_ena\t%d\n",
+ sq_ctx->vfi_lso_mps, sq_ctx->vfi_lso_vlan0_ins_ena);
+ seq_printf(m, "W9: vfi_lso_vlan1_ins_ena\t%d\nW9: vfi_lso_vld \t\t%d\n\n",
+ sq_ctx->vfi_lso_vld, sq_ctx->vfi_lso_vlan1_ins_ena);
+
+ seq_printf(m, "W10: scm_lso_rem \t\t%llu\n\n",
+ (u64)sq_ctx->scm_lso_rem);
+ seq_printf(m, "W11: octs \t\t\t%llu\n\n", (u64)sq_ctx->octs);
+ seq_printf(m, "W12: pkts \t\t\t%llu\n\n", (u64)sq_ctx->pkts);
+ seq_printf(m, "W13: aged_drop_octs \t\t\t%llu\n\n",
+ (u64)sq_ctx->aged_drop_octs);
+ seq_printf(m, "W13: aged_drop_pkts \t\t\t%llu\n\n",
+ (u64)sq_ctx->aged_drop_pkts);
+ seq_printf(m, "W14: dropped_octs \t\t%llu\n\n",
+ (u64)sq_ctx->dropped_octs);
+ seq_printf(m, "W15: dropped_pkts \t\t%llu\n\n",
+ (u64)sq_ctx->dropped_pkts);
+}
+
+void print_nix_cn20k_cq_ctx(struct seq_file *m,
+ struct nix_cn20k_aq_enq_rsp *rsp)
+{
+ struct nix_cn20k_cq_ctx_s *cq_ctx = &rsp->cq;
+
+ seq_printf(m, "W0: base \t\t\t%llx\n\n", cq_ctx->base);
+
+ seq_printf(m, "W1: wrptr \t\t\t%llx\n", (u64)cq_ctx->wrptr);
+ seq_printf(m, "W1: avg_con \t\t\t%d\nW1: cint_idx \t\t\t%d\n",
+ cq_ctx->avg_con, cq_ctx->cint_idx);
+ seq_printf(m, "W1: cq_err \t\t\t%d\nW1: qint_idx \t\t\t%d\n",
+ cq_ctx->cq_err, cq_ctx->qint_idx);
+ seq_printf(m, "W1: bpid \t\t\t%d\nW1: bp_ena \t\t\t%d\n\n",
+ cq_ctx->bpid, cq_ctx->bp_ena);
+
+ seq_printf(m, "W1: lbpid_high \t\t\t0x%03x\n", cq_ctx->lbpid_high);
+ seq_printf(m, "W1: lbpid_med \t\t\t0x%03x\n", cq_ctx->lbpid_med);
+ seq_printf(m, "W1: lbpid_low \t\t\t0x%03x\n", cq_ctx->lbpid_low);
+ seq_printf(m, "(W1: lbpid) \t\t\t0x%03x\n",
+ cq_ctx->lbpid_high << 6 | cq_ctx->lbpid_med << 3 |
+ cq_ctx->lbpid_low);
+ seq_printf(m, "W1: lbp_ena \t\t\t\t%d\n\n", cq_ctx->lbp_ena);
+
+ seq_printf(m, "W2: update_time \t\t%d\nW2:avg_level \t\t\t%d\n",
+ cq_ctx->update_time, cq_ctx->avg_level);
+ seq_printf(m, "W2: head \t\t\t%d\nW2:tail \t\t\t%d\n\n",
+ cq_ctx->head, cq_ctx->tail);
+
+ seq_printf(m, "W3: cq_err_int_ena \t\t%d\nW3:cq_err_int \t\t\t%d\n",
+ cq_ctx->cq_err_int_ena, cq_ctx->cq_err_int);
+ seq_printf(m, "W3: qsize \t\t\t%d\nW3:stashing \t\t\t%d\n",
+ cq_ctx->qsize, cq_ctx->stashing);
+
+ seq_printf(m, "W3: caching \t\t\t%d\n", cq_ctx->caching);
+ seq_printf(m, "W3: lbp_frac \t\t\t%d\n", cq_ctx->lbp_frac);
+ seq_printf(m, "W3: stash_thresh \t\t\t%d\n",
+ cq_ctx->stash_thresh);
+
+ seq_printf(m, "W3: msh_valid \t\t\t%d\nW3:msh_dst \t\t\t%d\n",
+ cq_ctx->msh_valid, cq_ctx->msh_dst);
+
+ seq_printf(m, "W3: cpt_drop_err_en \t\t\t%d\n",
+ cq_ctx->cpt_drop_err_en);
+ seq_printf(m, "W3: ena \t\t\t%d\n",
+ cq_ctx->ena);
+ seq_printf(m, "W3: drop_ena \t\t\t%d\nW3: drop \t\t\t%d\n",
+ cq_ctx->drop_ena, cq_ctx->drop);
+ seq_printf(m, "W3: bp \t\t\t\t%d\n\n", cq_ctx->bp);
+
+ seq_printf(m, "W4: lbpid_ext \t\t\t\t%d\n\n", cq_ctx->lbpid_ext);
+ seq_printf(m, "W4: bpid_ext \t\t\t\t%d\n\n", cq_ctx->bpid_ext);
+}
+
+void print_npa_cn20k_aura_ctx(struct seq_file *m,
+ struct npa_cn20k_aq_enq_rsp *rsp)
+{
+ struct npa_cn20k_aura_s *aura = &rsp->aura;
+
+ seq_printf(m, "W0: Pool addr\t\t%llx\n", aura->pool_addr);
+
+ seq_printf(m, "W1: ena\t\t\t%d\nW1: pool caching\t%d\n",
+ aura->ena, aura->pool_caching);
+ seq_printf(m, "W1: avg con\t\t%d\n", aura->avg_con);
+ seq_printf(m, "W1: pool drop ena\t%d\nW1: aura drop ena\t%d\n",
+ aura->pool_drop_ena, aura->aura_drop_ena);
+ seq_printf(m, "W1: bp_ena\t\t%d\nW1: aura drop\t\t%d\n",
+ aura->bp_ena, aura->aura_drop);
+ seq_printf(m, "W1: aura shift\t\t%d\nW1: avg_level\t\t%d\n",
+ aura->shift, aura->avg_level);
+
+ seq_printf(m, "W2: count\t\t%llu\nW2: nix_bpid\t\t%d\n",
+ (u64)aura->count, aura->bpid);
+
+ seq_printf(m, "W3: limit\t\t%llu\nW3: bp\t\t\t%d\nW3: fc_ena\t\t%d\n",
+ (u64)aura->limit, aura->bp, aura->fc_ena);
+
+ seq_printf(m, "W3: fc_up_crossing\t%d\nW3: fc_stype\t\t%d\n",
+ aura->fc_up_crossing, aura->fc_stype);
+ seq_printf(m, "W3: fc_hyst_bits\t%d\n", aura->fc_hyst_bits);
+
+ seq_printf(m, "W4: fc_addr\t\t%llx\n", aura->fc_addr);
+
+ seq_printf(m, "W5: pool_drop\t\t%d\nW5: update_time\t\t%d\n",
+ aura->pool_drop, aura->update_time);
+ seq_printf(m, "W5: err_int \t\t%d\nW5: err_int_ena\t\t%d\n",
+ aura->err_int, aura->err_int_ena);
+ seq_printf(m, "W5: thresh_int\t\t%d\nW5: thresh_int_ena \t%d\n",
+ aura->thresh_int, aura->thresh_int_ena);
+ seq_printf(m, "W5: thresh_up\t\t%d\nW5: thresh_qint_idx\t%d\n",
+ aura->thresh_up, aura->thresh_qint_idx);
+ seq_printf(m, "W5: err_qint_idx \t%d\n", aura->err_qint_idx);
+
+ seq_printf(m, "W6: thresh\t\t%llu\n", (u64)aura->thresh);
+ seq_printf(m, "W6: fc_msh_dst\t\t%d\n", aura->fc_msh_dst);
+}
+
+void print_npa_cn20k_pool_ctx(struct seq_file *m,
+ struct npa_cn20k_aq_enq_rsp *rsp)
+{
+ struct npa_cn20k_pool_s *pool = &rsp->pool;
+
+ seq_printf(m, "W0: Stack base\t\t%llx\n", pool->stack_base);
+
+ seq_printf(m, "W1: ena \t\t%d\nW1: nat_align \t\t%d\n",
+ pool->ena, pool->nat_align);
+ seq_printf(m, "W1: stack_caching\t%d\n",
+ pool->stack_caching);
+ seq_printf(m, "W1: buf_offset\t\t%d\nW1: buf_size\t\t%d\n",
+ pool->buf_offset, pool->buf_size);
+
+ seq_printf(m, "W2: stack_max_pages \t%d\nW2: stack_pages\t\t%d\n",
+ pool->stack_max_pages, pool->stack_pages);
+
+ seq_printf(m, "W4: stack_offset\t%d\nW4: shift\t\t%d\nW4: avg_level\t\t%d\n",
+ pool->stack_offset, pool->shift, pool->avg_level);
+ seq_printf(m, "W4: avg_con \t\t%d\nW4: fc_ena\t\t%d\nW4: fc_stype\t\t%d\n",
+ pool->avg_con, pool->fc_ena, pool->fc_stype);
+ seq_printf(m, "W4: fc_hyst_bits\t%d\nW4: fc_up_crossing\t%d\n",
+ pool->fc_hyst_bits, pool->fc_up_crossing);
+ seq_printf(m, "W4: update_time\t\t%d\n", pool->update_time);
+
+ seq_printf(m, "W5: fc_addr\t\t%llx\n", pool->fc_addr);
+
+ seq_printf(m, "W6: ptr_start\t\t%llx\n", pool->ptr_start);
+
+ seq_printf(m, "W7: ptr_end\t\t%llx\n", pool->ptr_end);
+
+ seq_printf(m, "W8: err_int\t\t%d\nW8: err_int_ena\t\t%d\n",
+ pool->err_int, pool->err_int_ena);
+ seq_printf(m, "W8: thresh_int\t\t%d\n", pool->thresh_int);
+ seq_printf(m, "W8: thresh_int_ena\t%d\nW8: thresh_up\t\t%d\n",
+ pool->thresh_int_ena, pool->thresh_up);
+ seq_printf(m, "W8: thresh_qint_idx\t%d\nW8: err_qint_idx\t%d\n",
+ pool->thresh_qint_idx, pool->err_qint_idx);
+ seq_printf(m, "W8: fc_msh_dst\t\t%d\n", pool->fc_msh_dst);
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.h
new file mode 100644
index 000000000000..a2e3a2cd6edb
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/debugfs.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Marvell OcteonTx2 CGX driver
+ *
+ * Copyright (C) 2024 Marvell.
+ *
+ */
+
+#ifndef DEBUFS_H
+#define DEBUFS_H
+
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "struct.h"
+#include "../mbox.h"
+
+void print_nix_cn20k_sq_ctx(struct seq_file *m,
+ struct nix_cn20k_sq_ctx_s *sq_ctx);
+void print_nix_cn20k_cq_ctx(struct seq_file *m,
+ struct nix_cn20k_aq_enq_rsp *rsp);
+void print_npa_cn20k_aura_ctx(struct seq_file *m,
+ struct npa_cn20k_aq_enq_rsp *rsp);
+void print_npa_cn20k_pool_ctx(struct seq_file *m,
+ struct npa_cn20k_aq_enq_rsp *rsp);
+
+#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/nix.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/nix.c
new file mode 100644
index 000000000000..aa2016fd1bba
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/nix.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2024 Marvell.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "struct.h"
+#include "../rvu.h"
+
+int rvu_mbox_handler_nix_cn20k_aq_enq(struct rvu *rvu,
+ struct nix_cn20k_aq_enq_req *req,
+ struct nix_cn20k_aq_enq_rsp *rsp)
+{
+ return rvu_nix_aq_enq_inst(rvu, (struct nix_aq_enq_req *)req,
+ (struct nix_aq_enq_rsp *)rsp);
+}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npa.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npa.c
new file mode 100644
index 000000000000..fe8f926c8b75
--- /dev/null
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npa.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Marvell RVU Admin Function driver
+ *
+ * Copyright (C) 2024 Marvell.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+
+#include "struct.h"
+#include "../rvu.h"
+
+int rvu_mbox_handler_npa_cn20k_aq_enq(struct rvu *rvu,
+ struct npa_cn20k_aq_enq_req *req,
+ struct npa_cn20k_aq_enq_rsp *rsp)
+{
+ return rvu_npa_aq_enq_inst(rvu, (struct npa_aq_enq_req *)req,
+ (struct npa_aq_enq_rsp *)rsp);
+}
+EXPORT_SYMBOL(rvu_mbox_handler_npa_cn20k_aq_enq);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/struct.h b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/struct.h
index 76ce3ec6da9c..763f6cabd7c2 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/struct.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/struct.h
@@ -8,6 +8,8 @@
#ifndef STRUCT_H
#define STRUCT_H
+#define NIX_MAX_CTX_SIZE 128
+
/*
* CN20k RVU PF MBOX Interrupt Vector Enumeration
*
@@ -37,4 +39,342 @@ enum rvu_af_cn20k_int_vec_e {
RVU_AF_CN20K_INT_VEC_PFAF1_MBOX1 = 0x9,
RVU_AF_CN20K_INT_VEC_CNT = 0xa,
};
+
+struct nix_cn20k_sq_ctx_s {
+ u64 ena : 1; /* W0 */
+ u64 qint_idx : 6;
+ u64 substream : 20;
+ u64 sdp_mcast : 1;
+ u64 cq : 20;
+ u64 sqe_way_mask : 16;
+ u64 smq : 11; /* W1 */
+ u64 cq_ena : 1;
+ u64 xoff : 1;
+ u64 sso_ena : 1;
+ u64 smq_rr_weight : 14;
+ u64 default_chan : 12;
+ u64 sqb_count : 16;
+ u64 reserved_120_120 : 1;
+ u64 smq_rr_count_lb : 7;
+ u64 smq_rr_count_ub : 25; /* W2 */
+ u64 sqb_aura : 20;
+ u64 sq_int : 8;
+ u64 sq_int_ena : 8;
+ u64 sqe_stype : 2;
+ u64 reserved_191_191 : 1;
+ u64 max_sqe_size : 2; /* W3 */
+ u64 cq_limit : 8;
+ u64 lmt_dis : 1;
+ u64 mnq_dis : 1;
+ u64 smq_next_sq : 20;
+ u64 smq_lso_segnum : 8;
+ u64 tail_offset : 6;
+ u64 smenq_offset : 6;
+ u64 head_offset : 6;
+ u64 smenq_next_sqb_vld : 1;
+ u64 smq_pend : 1;
+ u64 smq_next_sq_vld : 1;
+ u64 reserved_253_255 : 3;
+ u64 next_sqb : 64; /* W4 */
+ u64 tail_sqb : 64; /* W5 */
+ u64 smenq_sqb : 64; /* W6 */
+ u64 smenq_next_sqb : 64; /* W7 */
+ u64 head_sqb : 64; /* W8 */
+ u64 reserved_576_583 : 8; /* W9 */
+ u64 vfi_lso_total : 18;
+ u64 vfi_lso_sizem1 : 3;
+ u64 vfi_lso_sb : 8;
+ u64 vfi_lso_mps : 14;
+ u64 vfi_lso_vlan0_ins_ena : 1;
+ u64 vfi_lso_vlan1_ins_ena : 1;
+ u64 vfi_lso_vld : 1;
+ u64 reserved_630_639 : 10;
+ u64 scm_lso_rem : 18; /* W10 */
+ u64 reserved_658_703 : 46;
+ u64 octs : 48; /* W11 */
+ u64 reserved_752_767 : 16;
+ u64 pkts : 48; /* W12 */
+ u64 reserved_816_831 : 16;
+ u64 aged_drop_octs : 32; /* W13 */
+ u64 aged_drop_pkts : 32;
+ u64 dropped_octs : 48; /* W14 */
+ u64 reserved_944_959 : 16;
+ u64 dropped_pkts : 48; /* W15 */
+ u64 reserved_1008_1023 : 16;
+};
+
+static_assert(sizeof(struct nix_cn20k_sq_ctx_s) == NIX_MAX_CTX_SIZE);
+
+struct nix_cn20k_cq_ctx_s {
+ u64 base : 64; /* W0 */
+ u64 lbp_ena : 1; /* W1 */
+ u64 lbpid_low : 3;
+ u64 bp_ena : 1;
+ u64 lbpid_med : 3;
+ u64 bpid : 9;
+ u64 lbpid_high : 3;
+ u64 qint_idx : 7;
+ u64 cq_err : 1;
+ u64 cint_idx : 7;
+ u64 avg_con : 9;
+ u64 wrptr : 20;
+ u64 tail : 20; /* W2 */
+ u64 head : 20;
+ u64 avg_level : 8;
+ u64 update_time : 16;
+ u64 bp : 8; /* W3 */
+ u64 drop : 8;
+ u64 drop_ena : 1;
+ u64 ena : 1;
+ u64 cpt_drop_err_en : 1;
+ u64 reserved_211_211 : 1;
+ u64 msh_dst : 11;
+ u64 msh_valid : 1;
+ u64 stash_thresh : 4;
+ u64 lbp_frac : 4;
+ u64 caching : 1;
+ u64 stashing : 1;
+ u64 reserved_234_235 : 2;
+ u64 qsize : 4;
+ u64 cq_err_int : 8;
+ u64 cq_err_int_ena : 8;
+ u64 bpid_ext : 2; /* W4 */
+ u64 reserved_258_259 : 2;
+ u64 lbpid_ext : 2;
+ u64 reserved_262_319 : 58;
+ u64 reserved_320_383 : 64; /* W5 */
+ u64 reserved_384_447 : 64; /* W6 */
+ u64 reserved_448_511 : 64; /* W7 */
+ u64 padding[8];
+};
+
+static_assert(sizeof(struct nix_cn20k_sq_ctx_s) == NIX_MAX_CTX_SIZE);
+
+struct nix_cn20k_rq_ctx_s {
+ u64 ena : 1;
+ u64 sso_ena : 1;
+ u64 ipsech_ena : 1;
+ u64 ena_wqwd : 1;
+ u64 cq : 20;
+ u64 reserved_24_34 : 11;
+ u64 port_il4_dis : 1;
+ u64 port_ol4_dis : 1;
+ u64 lenerr_dis : 1;
+ u64 csum_il4_dis : 1;
+ u64 csum_ol4_dis : 1;
+ u64 len_il4_dis : 1;
+ u64 len_il3_dis : 1;
+ u64 len_ol4_dis : 1;
+ u64 len_ol3_dis : 1;
+ u64 wqe_aura : 20;
+ u64 spb_aura : 20;
+ u64 lpb_aura : 20;
+ u64 sso_grp : 10;
+ u64 sso_tt : 2;
+ u64 pb_caching : 2;
+ u64 wqe_caching : 1;
+ u64 xqe_drop_ena : 1;
+ u64 spb_drop_ena : 1;
+ u64 lpb_drop_ena : 1;
+ u64 pb_stashing : 1;
+ u64 ipsecd_drop_en : 1;
+ u64 chi_ena : 1;
+ u64 reserved_125_127 : 3;
+ u64 band_prof_id_l : 10;
+ u64 sso_fc_ena : 1;
+ u64 policer_ena : 1;
+ u64 spb_sizem1 : 6;
+ u64 wqe_skip : 2;
+ u64 spb_high_sizem1 : 3;
+ u64 spb_ena : 1;
+ u64 lpb_sizem1 : 12;
+ u64 first_skip : 7;
+ u64 reserved_171_171 : 1;
+ u64 later_skip : 6;
+ u64 xqe_imm_size : 6;
+ u64 band_prof_id_h : 4;
+ u64 reserved_188_189 : 2;
+ u64 xqe_imm_copy : 1;
+ u64 xqe_hdr_split : 1;
+ u64 xqe_drop : 8;
+ u64 xqe_pass : 8;
+ u64 wqe_pool_drop : 8;
+ u64 wqe_pool_pass : 8;
+ u64 spb_aura_drop : 8;
+ u64 spb_aura_pass : 8;
+ u64 spb_pool_drop : 8;
+ u64 spb_pool_pass : 8;
+ u64 lpb_aura_drop : 8;
+ u64 lpb_aura_pass : 8;
+ u64 lpb_pool_drop : 8;
+ u64 lpb_pool_pass : 8;
+ u64 reserved_288_291 : 4;
+ u64 rq_int : 8;
+ u64 rq_int_ena : 8;
+ u64 qint_idx : 7;
+ u64 reserved_315_319 : 5;
+ u64 ltag : 24;
+ u64 good_utag : 8;
+ u64 bad_utag : 8;
+ u64 flow_tagw : 6;
+ u64 ipsec_vwqe : 1;
+ u64 vwqe_ena : 1;
+ u64 vtime_wait : 8;
+ u64 max_vsize_exp : 4;
+ u64 vwqe_skip : 2;
+ u64 reserved_382_383 : 2;
+ u64 octs : 48;
+ u64 reserved_432_447 : 16;
+ u64 pkts : 48;
+ u64 reserved_496_511 : 16;
+ u64 drop_octs : 48;
+ u64 reserved_560_575 : 16;
+ u64 drop_pkts : 48;
+ u64 reserved_624_639 : 16;
+ u64 re_pkts : 48;
+ u64 reserved_688_703 : 16;
+ u64 reserved_704_767 : 64;
+ u64 reserved_768_831 : 64;
+ u64 reserved_832_895 : 64;
+ u64 reserved_896_959 : 64;
+ u64 reserved_960_1023 : 64;
+};
+
+static_assert(sizeof(struct nix_cn20k_rq_ctx_s) == NIX_MAX_CTX_SIZE);
+
+struct npa_cn20k_aura_s {
+ u64 pool_addr; /* W0 */
+ u64 ena : 1; /* W1 */
+ u64 reserved_65 : 2;
+ u64 pool_caching : 1;
+ u64 reserved_68 : 16;
+ u64 avg_con : 9;
+ u64 reserved_93 : 1;
+ u64 pool_drop_ena : 1;
+ u64 aura_drop_ena : 1;
+ u64 bp_ena : 1;
+ u64 reserved_97_103 : 7;
+ u64 aura_drop : 8;
+ u64 shift : 6;
+ u64 reserved_118_119 : 2;
+ u64 avg_level : 8;
+ u64 count : 36; /* W2 */
+ u64 reserved_164_167 : 4;
+ u64 bpid : 12;
+ u64 reserved_180_191 : 12;
+ u64 limit : 36; /* W3 */
+ u64 reserved_228_231 : 4;
+ u64 bp : 7;
+ u64 reserved_239_243 : 5;
+ u64 fc_ena : 1;
+ u64 fc_up_crossing : 1;
+ u64 fc_stype : 2;
+ u64 fc_hyst_bits : 4;
+ u64 reserved_252_255 : 4;
+ u64 fc_addr; /* W4 */
+ u64 pool_drop : 8; /* W5 */
+ u64 update_time : 16;
+ u64 err_int : 8;
+ u64 err_int_ena : 8;
+ u64 thresh_int : 1;
+ u64 thresh_int_ena : 1;
+ u64 thresh_up : 1;
+ u64 reserved_363 : 1;
+ u64 thresh_qint_idx : 7;
+ u64 reserved_371 : 1;
+ u64 err_qint_idx : 7;
+ u64 reserved_379_383 : 5;
+ u64 thresh : 36; /* W6*/
+ u64 rsvd_423_420 : 4;
+ u64 fc_msh_dst : 11;
+ u64 reserved_435_438 : 4;
+ u64 op_dpc_ena : 1;
+ u64 op_dpc_set : 5;
+ u64 reserved_445_445 : 1;
+ u64 stream_ctx : 1;
+ u64 unified_ctx : 1;
+ u64 reserved_448_511; /* W7 */
+ u64 padding[8];
+};
+
+static_assert(sizeof(struct npa_cn20k_aura_s) == NIX_MAX_CTX_SIZE);
+
+struct npa_cn20k_pool_s {
+ u64 stack_base; /* W0 */
+ u64 ena : 1;
+ u64 nat_align : 1;
+ u64 reserved_66_67 : 2;
+ u64 stack_caching : 1;
+ u64 reserved_69_87 : 19;
+ u64 buf_offset : 12;
+ u64 reserved_100_103 : 4;
+ u64 buf_size : 12;
+ u64 reserved_116_119 : 4;
+ u64 ref_cnt_prof : 3;
+ u64 reserved_123_127 : 5;
+ u64 stack_max_pages : 32;
+ u64 stack_pages : 32;
+ uint64_t bp_0 : 7;
+ uint64_t bp_1 : 7;
+ uint64_t bp_2 : 7;
+ uint64_t bp_3 : 7;
+ uint64_t bp_4 : 7;
+ uint64_t bp_5 : 7;
+ uint64_t bp_6 : 7;
+ uint64_t bp_7 : 7;
+ uint64_t bp_ena_0 : 1;
+ uint64_t bp_ena_1 : 1;
+ uint64_t bp_ena_2 : 1;
+ uint64_t bp_ena_3 : 1;
+ uint64_t bp_ena_4 : 1;
+ uint64_t bp_ena_5 : 1;
+ uint64_t bp_ena_6 : 1;
+ uint64_t bp_ena_7 : 1;
+ u64 stack_offset : 4;
+ u64 reserved_260_263 : 4;
+ u64 shift : 6;
+ u64 reserved_270_271 : 2;
+ u64 avg_level : 8;
+ u64 avg_con : 9;
+ u64 fc_ena : 1;
+ u64 fc_stype : 2;
+ u64 fc_hyst_bits : 4;
+ u64 fc_up_crossing : 1;
+ u64 reserved_297_299 : 3;
+ u64 update_time : 16;
+ u64 reserved_316_319 : 4;
+ u64 fc_addr; /* W5 */
+ u64 ptr_start; /* W6 */
+ u64 ptr_end; /* W7 */
+ u64 bpid_0 : 12;
+ u64 reserved_524_535 : 12;
+ u64 err_int : 8;
+ u64 err_int_ena : 8;
+ u64 thresh_int : 1;
+ u64 thresh_int_ena : 1;
+ u64 thresh_up : 1;
+ u64 reserved_555 : 1;
+ u64 thresh_qint_idx : 7;
+ u64 reserved_563 : 1;
+ u64 err_qint_idx : 7;
+ u64 reserved_571_575 : 5;
+ u64 thresh : 36;
+ u64 rsvd_612_615 : 4;
+ u64 fc_msh_dst : 11;
+ u64 reserved_627_630 : 4;
+ u64 op_dpc_ena : 1;
+ u64 op_dpc_set : 5;
+ u64 reserved_637_637 : 1;
+ u64 stream_ctx : 1;
+ u64 reserved_639 : 1;
+ u64 reserved_640_703; /* W10 */
+ u64 reserved_704_767; /* W11 */
+ u64 reserved_768_831; /* W12 */
+ u64 reserved_832_895; /* W13 */
+ u64 reserved_896_959; /* W14 */
+ u64 reserved_960_1023; /* W15 */
+};
+
+static_assert(sizeof(struct npa_cn20k_pool_s) == NIX_MAX_CTX_SIZE);
+
#endif
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
index 933073cd2280..a3e273126e4e 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h
@@ -203,6 +203,8 @@ M(NPA_LF_ALLOC, 0x400, npa_lf_alloc, \
M(NPA_LF_FREE, 0x401, npa_lf_free, msg_req, msg_rsp) \
M(NPA_AQ_ENQ, 0x402, npa_aq_enq, npa_aq_enq_req, npa_aq_enq_rsp) \
M(NPA_HWCTX_DISABLE, 0x403, npa_hwctx_disable, hwctx_disable_req, msg_rsp)\
+M(NPA_CN20K_AQ_ENQ, 0x404, npa_cn20k_aq_enq, npa_cn20k_aq_enq_req, \
+ npa_cn20k_aq_enq_rsp) \
/* SSO/SSOW mbox IDs (range 0x600 - 0x7FF) */ \
/* TIM mbox IDs (range 0x800 - 0x9FF) */ \
/* CPT mbox IDs (range 0xA00 - 0xBFF) */ \
@@ -336,6 +338,8 @@ M(NIX_MCAST_GRP_UPDATE, 0x802d, nix_mcast_grp_update, \
nix_mcast_grp_update_req, \
nix_mcast_grp_update_rsp) \
M(NIX_LF_STATS, 0x802e, nix_lf_stats, nix_stats_req, nix_stats_rsp) \
+M(NIX_CN20K_AQ_ENQ, 0x802f, nix_cn20k_aq_enq, nix_cn20k_aq_enq_req, \
+ nix_cn20k_aq_enq_rsp) \
/* MCS mbox IDs (range 0xA000 - 0xBFFF) */ \
M(MCS_ALLOC_RESOURCES, 0xa000, mcs_alloc_resources, mcs_alloc_rsrc_req, \
mcs_alloc_rsrc_rsp) \
@@ -832,6 +836,39 @@ struct npa_aq_enq_rsp {
};
};
+struct npa_cn20k_aq_enq_req {
+ struct mbox_msghdr hdr;
+ u32 aura_id;
+ u8 ctype;
+ u8 op;
+ union {
+ /* Valid when op == WRITE/INIT and ctype == AURA.
+ * LF fills the pool_id in aura.pool_addr. AF will translate
+ * the pool_id to pool context pointer.
+ */
+ struct npa_cn20k_aura_s aura;
+ /* Valid when op == WRITE/INIT and ctype == POOL */
+ struct npa_cn20k_pool_s pool;
+ };
+ /* Mask data when op == WRITE (1=write, 0=don't write) */
+ union {
+ /* Valid when op == WRITE and ctype == AURA */
+ struct npa_cn20k_aura_s aura_mask;
+ /* Valid when op == WRITE and ctype == POOL */
+ struct npa_cn20k_pool_s pool_mask;
+ };
+};
+
+struct npa_cn20k_aq_enq_rsp {
+ struct mbox_msghdr hdr;
+ union {
+ /* Valid when op == READ and ctype == AURA */
+ struct npa_cn20k_aura_s aura;
+ /* Valid when op == READ and ctype == POOL */
+ struct npa_cn20k_pool_s pool;
+ };
+};
+
/* Disable all contexts of type 'ctype' */
struct hwctx_disable_req {
struct mbox_msghdr hdr;
@@ -940,6 +977,42 @@ struct nix_lf_free_req {
u64 flags;
};
+/* CN20K NIX AQ enqueue msg */
+struct nix_cn20k_aq_enq_req {
+ struct mbox_msghdr hdr;
+ u32 qidx;
+ u8 ctype;
+ u8 op;
+ union {
+ struct nix_cn20k_rq_ctx_s rq;
+ struct nix_cn20k_sq_ctx_s sq;
+ struct nix_cn20k_cq_ctx_s cq;
+ struct nix_rsse_s rss;
+ struct nix_rx_mce_s mce;
+ struct nix_bandprof_s prof;
+ };
+ union {
+ struct nix_cn20k_rq_ctx_s rq_mask;
+ struct nix_cn20k_sq_ctx_s sq_mask;
+ struct nix_cn20k_cq_ctx_s cq_mask;
+ struct nix_rsse_s rss_mask;
+ struct nix_rx_mce_s mce_mask;
+ struct nix_bandprof_s prof_mask;
+ };
+};
+
+struct nix_cn20k_aq_enq_rsp {
+ struct mbox_msghdr hdr;
+ union {
+ struct nix_cn20k_rq_ctx_s rq;
+ struct nix_cn20k_sq_ctx_s sq;
+ struct nix_cn20k_cq_ctx_s cq;
+ struct nix_rsse_s rss;
+ struct nix_rx_mce_s mce;
+ struct nix_bandprof_s prof;
+ };
+};
+
/* CN10K NIX AQ enqueue msg */
struct nix_cn10k_aq_enq_req {
struct mbox_msghdr hdr;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
index b58283341923..e85dac2c806d 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
@@ -498,6 +498,14 @@ struct channel_fwdata {
u8 reserved[RVU_CHANL_INFO_RESERVED];
};
+struct altaf_intr_notify {
+ unsigned long flr_pf_bmap[2];
+ unsigned long flr_vf_bmap[2];
+ unsigned long gint_paddr;
+ unsigned long gint_iova_addr;
+ unsigned long reserved[6];
+};
+
struct rvu_fwdata {
#define RVU_FWDATA_HEADER_MAGIC 0xCFDA /* Custom Firmware Data*/
#define RVU_FWDATA_VERSION 0x0001
@@ -517,7 +525,8 @@ struct rvu_fwdata {
u32 ptp_ext_clk_rate;
u32 ptp_ext_tstamp;
struct channel_fwdata channel_data;
-#define FWDATA_RESERVED_MEM 958
+ struct altaf_intr_notify altaf_intr_info;
+#define FWDATA_RESERVED_MEM 946
u64 reserved[FWDATA_RESERVED_MEM];
#define CGX_MAX 9
#define CGX_LMACS_MAX 4
@@ -648,6 +657,7 @@ struct rvu {
struct mutex mbox_lock; /* Serialize mbox up and down msgs */
u16 rep_pcifunc;
+ bool altaf_ready;
int rep_cnt;
u16 *rep2pfvf_map;
u8 rep_mode;
@@ -1032,6 +1042,9 @@ void rvu_nix_flr_free_bpids(struct rvu *rvu, u16 pcifunc);
int rvu_alloc_cint_qint_mem(struct rvu *rvu, struct rvu_pfvf *pfvf,
int blkaddr, int nixlf);
void rvu_block_bcast_xon(struct rvu *rvu, int blkaddr);
+int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
+ struct nix_aq_enq_rsp *rsp);
+
/* NPC APIs */
void rvu_npc_freemem(struct rvu *rvu);
int rvu_npc_get_pkind(struct rvu *rvu, u16 pf);
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
index 8375f18c8e07..15d3cb0b9da6 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
@@ -21,6 +21,8 @@
#include "rvu_npc_hash.h"
#include "mcs.h"
+#include "cn20k/debugfs.h"
+
#define DEBUGFS_DIR_NAME "octeontx2"
enum {
@@ -1101,6 +1103,11 @@ static void print_npa_aura_ctx(struct seq_file *m, struct npa_aq_enq_rsp *rsp)
struct npa_aura_s *aura = &rsp->aura;
struct rvu *rvu = m->private;
+ if (is_cn20k(rvu->pdev)) {
+ print_npa_cn20k_aura_ctx(m, (struct npa_cn20k_aq_enq_rsp *)rsp);
+ return;
+ }
+
seq_printf(m, "W0: Pool addr\t\t%llx\n", aura->pool_addr);
seq_printf(m, "W1: ena\t\t\t%d\nW1: pool caching\t%d\n",
@@ -1149,6 +1156,11 @@ static void print_npa_pool_ctx(struct seq_file *m, struct npa_aq_enq_rsp *rsp)
struct npa_pool_s *pool = &rsp->pool;
struct rvu *rvu = m->private;
+ if (is_cn20k(rvu->pdev)) {
+ print_npa_cn20k_pool_ctx(m, (struct npa_cn20k_aq_enq_rsp *)rsp);
+ return;
+ }
+
seq_printf(m, "W0: Stack base\t\t%llx\n", pool->stack_base);
seq_printf(m, "W1: ena \t\t%d\nW1: nat_align \t\t%d\n",
@@ -1651,6 +1663,9 @@ static void print_tm_tree(struct seq_file *m,
int blkaddr;
u64 cfg;
+ if (!sq_ctx->ena)
+ return;
+
blkaddr = nix_hw->blkaddr;
schq = sq_ctx->smq;
@@ -2009,10 +2024,16 @@ static void print_nix_sq_ctx(struct seq_file *m, struct nix_aq_enq_rsp *rsp)
struct nix_hw *nix_hw = m->private;
struct rvu *rvu = nix_hw->rvu;
+ if (is_cn20k(rvu->pdev)) {
+ print_nix_cn20k_sq_ctx(m, (struct nix_cn20k_sq_ctx_s *)sq_ctx);
+ return;
+ }
+
if (!is_rvu_otx2(rvu)) {
print_nix_cn10k_sq_ctx(m, (struct nix_cn10k_sq_ctx_s *)sq_ctx);
return;
}
+
seq_printf(m, "W0: sqe_way_mask \t\t%d\nW0: cq \t\t\t\t%d\n",
sq_ctx->sqe_way_mask, sq_ctx->cq);
seq_printf(m, "W0: sdp_mcast \t\t\t%d\nW0: substream \t\t\t0x%03x\n",
@@ -2103,7 +2124,9 @@ static void print_nix_cn10k_rq_ctx(struct seq_file *m,
seq_printf(m, "W1: ipsecd_drop_ena \t\t%d\nW1: chi_ena \t\t\t%d\n\n",
rq_ctx->ipsecd_drop_ena, rq_ctx->chi_ena);
- seq_printf(m, "W2: band_prof_id \t\t%d\n", rq_ctx->band_prof_id);
+ seq_printf(m, "W2: band_prof_id \t\t%d\n",
+ (u16)rq_ctx->band_prof_id_h << 10 | rq_ctx->band_prof_id);
+
seq_printf(m, "W2: policer_ena \t\t%d\n", rq_ctx->policer_ena);
seq_printf(m, "W2: spb_sizem1 \t\t\t%d\n", rq_ctx->spb_sizem1);
seq_printf(m, "W2: wqe_skip \t\t\t%d\nW2: sqb_ena \t\t\t%d\n",
@@ -2225,6 +2248,11 @@ static void print_nix_cq_ctx(struct seq_file *m, struct nix_aq_enq_rsp *rsp)
struct nix_hw *nix_hw = m->private;
struct rvu *rvu = nix_hw->rvu;
+ if (is_cn20k(rvu->pdev)) {
+ print_nix_cn20k_cq_ctx(m, (struct nix_cn20k_aq_enq_rsp *)rsp);
+ return;
+ }
+
seq_printf(m, "W0: base \t\t\t%llx\n\n", cq_ctx->base);
seq_printf(m, "W1: wrptr \t\t\t%llx\n", (u64)cq_ctx->wrptr);
@@ -2254,6 +2282,7 @@ static void print_nix_cq_ctx(struct seq_file *m, struct nix_aq_enq_rsp *rsp)
cq_ctx->cq_err_int_ena, cq_ctx->cq_err_int);
seq_printf(m, "W3: qsize \t\t\t%d\nW3:caching \t\t\t%d\n",
cq_ctx->qsize, cq_ctx->caching);
+
seq_printf(m, "W3: substream \t\t\t0x%03x\nW3: ena \t\t\t%d\n",
cq_ctx->substream, cq_ctx->ena);
if (!is_rvu_otx2(rvu)) {
@@ -2615,7 +2644,10 @@ static void print_band_prof_ctx(struct seq_file *m,
(prof->rc_action == 1) ? "DROP" : "RED";
seq_printf(m, "W1: rc_action\t\t%s\n", str);
seq_printf(m, "W1: meter_algo\t\t%d\n", prof->meter_algo);
- seq_printf(m, "W1: band_prof_id\t%d\n", prof->band_prof_id);
+
+ seq_printf(m, "W1: band_prof_id\t%d\n",
+ (u16)prof->band_prof_id_h << 7 | prof->band_prof_id);
+
seq_printf(m, "W1: hl_en\t\t%d\n", prof->hl_en);
seq_printf(m, "W2: ts\t\t\t%lld\n", (u64)prof->ts);
@@ -2784,6 +2816,9 @@ static void rvu_dbg_npa_init(struct rvu *rvu)
&rvu_dbg_npa_aura_ctx_fops);
debugfs_create_file("pool_ctx", 0600, rvu->rvu_dbg.npa, rvu,
&rvu_dbg_npa_pool_ctx_fops);
+
+ if (is_cn20k(rvu->pdev)) /* NDC not appliable for cn20k */
+ return;
debugfs_create_file("ndc_cache", 0600, rvu->rvu_dbg.npa, rvu,
&rvu_dbg_npa_ndc_cache_fops);
debugfs_create_file("ndc_hits_miss", 0600, rvu->rvu_dbg.npa, rvu,
@@ -3950,6 +3985,9 @@ static void rvu_dbg_cpt_init(struct rvu *rvu, int blkaddr)
static const char *rvu_get_dbg_dir_name(struct rvu *rvu)
{
+ if (is_cn20k(rvu->pdev))
+ return "cn20k";
+
if (!is_rvu_otx2(rvu))
return "cn10k";
else
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
index 3735372539bd..0f9953eaf1b0 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
@@ -1233,7 +1233,8 @@ static int rvu_af_dl_dwrr_mtu_set(struct devlink *devlink, u32 id,
}
static int rvu_af_dl_dwrr_mtu_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
@@ -1259,7 +1260,8 @@ enum rvu_af_dl_param_id {
};
static int rvu_af_npc_exact_feature_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
@@ -1314,7 +1316,8 @@ static int rvu_af_npc_exact_feature_validate(struct devlink *devlink, u32 id,
}
static int rvu_af_dl_npc_mcam_high_zone_percent_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
@@ -1376,7 +1379,8 @@ static int rvu_af_dl_npc_mcam_high_zone_percent_validate(struct devlink *devlink
}
static int rvu_af_dl_npc_def_rule_cntr_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
@@ -1402,7 +1406,8 @@ static int rvu_af_dl_npc_def_rule_cntr_set(struct devlink *devlink, u32 id,
}
static int rvu_af_dl_nix_maxlf_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct rvu_devlink *rvu_dl = devlink_priv(devlink);
struct rvu *rvu = rvu_dl->rvu;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
index 828316211b24..2f485a930edd 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
@@ -1019,6 +1019,12 @@ static void nix_get_aq_req_smq(struct rvu *rvu, struct nix_aq_enq_req *req,
{
struct nix_cn10k_aq_enq_req *aq_req;
+ if (is_cn20k(rvu->pdev)) {
+ *smq = ((struct nix_cn20k_aq_enq_req *)req)->sq.smq;
+ *smq_mask = ((struct nix_cn20k_aq_enq_req *)req)->sq_mask.smq;
+ return;
+ }
+
if (!is_rvu_otx2(rvu)) {
aq_req = (struct nix_cn10k_aq_enq_req *)req;
*smq = aq_req->sq.smq;
@@ -1149,36 +1155,36 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw,
case NIX_AQ_INSTOP_WRITE:
if (req->ctype == NIX_AQ_CTYPE_RQ)
memcpy(mask, &req->rq_mask,
- sizeof(struct nix_rq_ctx_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_SQ)
memcpy(mask, &req->sq_mask,
- sizeof(struct nix_sq_ctx_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_CQ)
memcpy(mask, &req->cq_mask,
- sizeof(struct nix_cq_ctx_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_RSS)
memcpy(mask, &req->rss_mask,
- sizeof(struct nix_rsse_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_MCE)
memcpy(mask, &req->mce_mask,
- sizeof(struct nix_rx_mce_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_BANDPROF)
memcpy(mask, &req->prof_mask,
- sizeof(struct nix_bandprof_s));
+ NIX_MAX_CTX_SIZE);
fallthrough;
case NIX_AQ_INSTOP_INIT:
if (req->ctype == NIX_AQ_CTYPE_RQ)
- memcpy(ctx, &req->rq, sizeof(struct nix_rq_ctx_s));
+ memcpy(ctx, &req->rq, NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_SQ)
- memcpy(ctx, &req->sq, sizeof(struct nix_sq_ctx_s));
+ memcpy(ctx, &req->sq, NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_CQ)
- memcpy(ctx, &req->cq, sizeof(struct nix_cq_ctx_s));
+ memcpy(ctx, &req->cq, NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_RSS)
- memcpy(ctx, &req->rss, sizeof(struct nix_rsse_s));
+ memcpy(ctx, &req->rss, NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_MCE)
- memcpy(ctx, &req->mce, sizeof(struct nix_rx_mce_s));
+ memcpy(ctx, &req->mce, NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_BANDPROF)
- memcpy(ctx, &req->prof, sizeof(struct nix_bandprof_s));
+ memcpy(ctx, &req->prof, NIX_MAX_CTX_SIZE);
break;
case NIX_AQ_INSTOP_NOP:
case NIX_AQ_INSTOP_READ:
@@ -1243,22 +1249,22 @@ static int rvu_nix_blk_aq_enq_inst(struct rvu *rvu, struct nix_hw *nix_hw,
if (req->op == NIX_AQ_INSTOP_READ) {
if (req->ctype == NIX_AQ_CTYPE_RQ)
memcpy(&rsp->rq, ctx,
- sizeof(struct nix_rq_ctx_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_SQ)
memcpy(&rsp->sq, ctx,
- sizeof(struct nix_sq_ctx_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_CQ)
memcpy(&rsp->cq, ctx,
- sizeof(struct nix_cq_ctx_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_RSS)
memcpy(&rsp->rss, ctx,
- sizeof(struct nix_rsse_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_MCE)
memcpy(&rsp->mce, ctx,
- sizeof(struct nix_rx_mce_s));
+ NIX_MAX_CTX_SIZE);
else if (req->ctype == NIX_AQ_CTYPE_BANDPROF)
memcpy(&rsp->prof, ctx,
- sizeof(struct nix_bandprof_s));
+ NIX_MAX_CTX_SIZE);
}
}
@@ -1289,8 +1295,8 @@ static int rvu_nix_verify_aq_ctx(struct rvu *rvu, struct nix_hw *nix_hw,
/* Make copy of original context & mask which are required
* for resubmission
*/
- memcpy(&aq_req.cq_mask, &req->cq_mask, sizeof(struct nix_cq_ctx_s));
- memcpy(&aq_req.cq, &req->cq, sizeof(struct nix_cq_ctx_s));
+ memcpy(&aq_req.cq_mask, &req->cq_mask, NIX_MAX_CTX_SIZE);
+ memcpy(&aq_req.cq, &req->cq, NIX_MAX_CTX_SIZE);
/* exclude fields which HW can update */
aq_req.cq_mask.cq_err = 0;
@@ -1309,7 +1315,7 @@ static int rvu_nix_verify_aq_ctx(struct rvu *rvu, struct nix_hw *nix_hw,
* updated fields are masked out for request and response
* comparison
*/
- for (word = 0; word < sizeof(struct nix_cq_ctx_s) / sizeof(u64);
+ for (word = 0; word < NIX_MAX_CTX_SIZE / sizeof(u64);
word++) {
*(u64 *)((u8 *)&aq_rsp.cq + word * 8) &=
(*(u64 *)((u8 *)&aq_req.cq_mask + word * 8));
@@ -1317,14 +1323,14 @@ static int rvu_nix_verify_aq_ctx(struct rvu *rvu, struct nix_hw *nix_hw,
(*(u64 *)((u8 *)&aq_req.cq_mask + word * 8));
}
- if (memcmp(&aq_req.cq, &aq_rsp.cq, sizeof(struct nix_cq_ctx_s)))
+ if (memcmp(&aq_req.cq, &aq_rsp.cq, NIX_MAX_CTX_SIZE))
return NIX_AF_ERR_AQ_CTX_RETRY_WRITE;
return 0;
}
-static int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
- struct nix_aq_enq_rsp *rsp)
+int rvu_nix_aq_enq_inst(struct rvu *rvu, struct nix_aq_enq_req *req,
+ struct nix_aq_enq_rsp *rsp)
{
struct nix_hw *nix_hw;
int err, retries = 5;
@@ -5812,6 +5818,8 @@ static void nix_ipolicer_freemem(struct rvu *rvu, struct nix_hw *nix_hw)
}
}
+#define NIX_BW_PROF_HI_MASK GENMASK(10, 7)
+
static int nix_verify_bandprof(struct nix_cn10k_aq_enq_req *req,
struct nix_hw *nix_hw, u16 pcifunc)
{
@@ -5850,7 +5858,8 @@ static int nix_verify_bandprof(struct nix_cn10k_aq_enq_req *req,
return -EINVAL;
ipolicer = &nix_hw->ipolicer[hi_layer];
- prof_idx = req->prof.band_prof_id;
+ prof_idx = FIELD_PREP(NIX_BW_PROF_HI_MASK, req->prof.band_prof_id_h);
+ prof_idx |= req->prof.band_prof_id;
if (prof_idx >= ipolicer->band_prof.max ||
ipolicer->pfvf_map[prof_idx] != pcifunc)
return -EINVAL;
@@ -6015,8 +6024,10 @@ static int nix_ipolicer_map_leaf_midprofs(struct rvu *rvu,
aq_req->op = NIX_AQ_INSTOP_WRITE;
aq_req->qidx = leaf_prof;
- aq_req->prof.band_prof_id = mid_prof;
+ aq_req->prof.band_prof_id = mid_prof & 0x7F;
aq_req->prof_mask.band_prof_id = GENMASK(6, 0);
+ aq_req->prof.band_prof_id_h = FIELD_GET(NIX_BW_PROF_HI_MASK, mid_prof);
+ aq_req->prof_mask.band_prof_id_h = GENMASK(3, 0);
aq_req->prof.hl_en = 1;
aq_req->prof_mask.hl_en = 1;
@@ -6025,6 +6036,8 @@ static int nix_ipolicer_map_leaf_midprofs(struct rvu *rvu,
(struct nix_aq_enq_rsp *)aq_rsp);
}
+#define NIX_RQ_PROF_HI_MASK GENMASK(13, 10)
+
int rvu_nix_setup_ratelimit_aggr(struct rvu *rvu, u16 pcifunc,
u16 rq_idx, u16 match_id)
{
@@ -6056,7 +6069,8 @@ int rvu_nix_setup_ratelimit_aggr(struct rvu *rvu, u16 pcifunc,
return 0;
/* Get the bandwidth profile ID mapped to this RQ */
- leaf_prof = aq_rsp.rq.band_prof_id;
+ leaf_prof = FIELD_PREP(NIX_RQ_PROF_HI_MASK, aq_rsp.rq.band_prof_id_h);
+ leaf_prof |= aq_rsp.rq.band_prof_id;
ipolicer = &nix_hw->ipolicer[BAND_PROF_LEAF_LAYER];
ipolicer->match_id[leaf_prof] = match_id;
@@ -6094,7 +6108,10 @@ int rvu_nix_setup_ratelimit_aggr(struct rvu *rvu, u16 pcifunc,
* to different RQs and marked with same match_id
* are rate limited in a aggregate fashion
*/
- mid_prof = aq_rsp.prof.band_prof_id;
+ mid_prof = FIELD_PREP(NIX_BW_PROF_HI_MASK,
+ aq_rsp.prof.band_prof_id_h);
+ mid_prof |= aq_rsp.prof.band_prof_id;
+
rc = nix_ipolicer_map_leaf_midprofs(rvu, nix_hw,
&aq_req, &aq_rsp,
leaf_prof, mid_prof);
@@ -6216,7 +6233,8 @@ static void nix_clear_ratelimit_aggr(struct rvu *rvu, struct nix_hw *nix_hw,
if (!aq_rsp.prof.hl_en)
return;
- mid_prof = aq_rsp.prof.band_prof_id;
+ mid_prof = FIELD_PREP(NIX_BW_PROF_HI_MASK, aq_rsp.prof.band_prof_id_h);
+ mid_prof |= aq_rsp.prof.band_prof_id;
ipolicer = &nix_hw->ipolicer[BAND_PROF_MID_LAYER];
ipolicer->ref_count[mid_prof]--;
/* If ref_count is zero, free mid layer profile */
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c
index 4f5ca5ab13a4..e2a33e46b48a 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c
@@ -464,6 +464,23 @@ int rvu_mbox_handler_npa_lf_free(struct rvu *rvu, struct msg_req *req,
return 0;
}
+static void npa_aq_ndc_config(struct rvu *rvu, struct rvu_block *block)
+{
+ u64 cfg;
+
+ if (is_cn20k(rvu->pdev)) /* NDC not applicable to cn20k */
+ return;
+
+ /* Do not bypass NDC cache */
+ cfg = rvu_read64(rvu, block->addr, NPA_AF_NDC_CFG);
+ cfg &= ~0x03DULL;
+#ifdef CONFIG_NDC_DIS_DYNAMIC_CACHING
+ /* Disable caching of stack pages */
+ cfg |= 0x10ULL;
+#endif
+ rvu_write64(rvu, block->addr, NPA_AF_NDC_CFG, cfg);
+}
+
static int npa_aq_init(struct rvu *rvu, struct rvu_block *block)
{
u64 cfg;
@@ -479,14 +496,7 @@ static int npa_aq_init(struct rvu *rvu, struct rvu_block *block)
rvu_write64(rvu, block->addr, NPA_AF_GEN_CFG, cfg);
#endif
- /* Do not bypass NDC cache */
- cfg = rvu_read64(rvu, block->addr, NPA_AF_NDC_CFG);
- cfg &= ~0x03DULL;
-#ifdef CONFIG_NDC_DIS_DYNAMIC_CACHING
- /* Disable caching of stack pages */
- cfg |= 0x10ULL;
-#endif
- rvu_write64(rvu, block->addr, NPA_AF_NDC_CFG, cfg);
+ npa_aq_ndc_config(rvu, block);
/* For CN10K NPA BATCH DMA set 35 cache lines */
if (!is_rvu_otx2(rvu)) {
@@ -567,6 +577,9 @@ int rvu_ndc_fix_locked_cacheline(struct rvu *rvu, int blkaddr)
int bank, max_bank, line, max_line, err;
u64 reg, ndc_af_const;
+ if (is_cn20k(rvu->pdev)) /* NDC not applicable to cn20k */
+ return 0;
+
/* Set the ENABLE bit(63) to '0' */
reg = rvu_read64(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL);
rvu_write64(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL, reg & GENMASK_ULL(62, 0));
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h
index 0596a3ac4c12..8e868f815de1 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_struct.h
@@ -13,6 +13,8 @@
#define RVU_MULTI_BLK_VER 0x7ULL
+#define NIX_MAX_CTX_SIZE 128
+
/* RVU Block Address Enumeration */
enum rvu_block_addr_e {
BLKADDR_RVUM = 0x0ULL,
@@ -370,8 +372,12 @@ struct nix_cq_ctx_s {
u64 qsize : 4;
u64 cq_err_int : 8;
u64 cq_err_int_ena : 8;
+ /* Ensure all context sizes are 128 bytes */
+ u64 padding[12];
};
+static_assert(sizeof(struct nix_cq_ctx_s) == NIX_MAX_CTX_SIZE);
+
/* CN10K NIX Receive queue context structure */
struct nix_cn10k_rq_ctx_s {
u64 ena : 1;
@@ -413,7 +419,8 @@ struct nix_cn10k_rq_ctx_s {
u64 rsvd_171 : 1;
u64 later_skip : 6;
u64 xqe_imm_size : 6;
- u64 rsvd_189_184 : 6;
+ u64 band_prof_id_h : 4;
+ u64 rsvd_189_188 : 2;
u64 xqe_imm_copy : 1;
u64 xqe_hdr_split : 1;
u64 xqe_drop : 8; /* W3 */
@@ -460,6 +467,8 @@ struct nix_cn10k_rq_ctx_s {
u64 rsvd_1023_960; /* W15 */
};
+static_assert(sizeof(struct nix_cn10k_rq_ctx_s) == NIX_MAX_CTX_SIZE);
+
/* CN10K NIX Send queue context structure */
struct nix_cn10k_sq_ctx_s {
u64 ena : 1;
@@ -523,6 +532,8 @@ struct nix_cn10k_sq_ctx_s {
u64 rsvd_1023_1008 : 16;
};
+static_assert(sizeof(struct nix_cn10k_sq_ctx_s) == NIX_MAX_CTX_SIZE);
+
/* NIX Receive queue context structure */
struct nix_rq_ctx_s {
u64 ena : 1;
@@ -594,6 +605,8 @@ struct nix_rq_ctx_s {
u64 rsvd_1023_960; /* W15 */
};
+static_assert(sizeof(struct nix_rq_ctx_s) == NIX_MAX_CTX_SIZE);
+
/* NIX sqe sizes */
enum nix_maxsqesz {
NIX_MAXSQESZ_W16 = 0x0,
@@ -668,13 +681,18 @@ struct nix_sq_ctx_s {
u64 rsvd_1023_1008 : 16;
};
+static_assert(sizeof(struct nix_sq_ctx_s) == NIX_MAX_CTX_SIZE);
+
/* NIX Receive side scaling entry structure*/
struct nix_rsse_s {
uint32_t rq : 20;
uint32_t reserved_20_31 : 12;
-
+ /* Ensure all context sizes are minimum 128 bytes */
+ u64 padding[15];
};
+static_assert(sizeof(struct nix_rsse_s) == NIX_MAX_CTX_SIZE);
+
/* NIX receive multicast/mirror entry structure */
struct nix_rx_mce_s {
uint64_t op : 2;
@@ -684,8 +702,12 @@ struct nix_rx_mce_s {
uint64_t rsvd_31_24 : 8;
uint64_t pf_func : 16;
uint64_t next : 16;
+ /* Ensure all context sizes are minimum 128 bytes */
+ u64 padding[15];
};
+static_assert(sizeof(struct nix_rx_mce_s) == NIX_MAX_CTX_SIZE);
+
enum nix_band_prof_layers {
BAND_PROF_LEAF_LAYER = 0,
BAND_PROF_INVAL_LAYER = 1,
@@ -736,7 +758,8 @@ struct nix_bandprof_s {
uint64_t rc_action : 2;
uint64_t meter_algo : 2;
uint64_t band_prof_id : 7;
- uint64_t reserved_111_118 : 8;
+ uint64_t band_prof_id_h : 4;
+ uint64_t reserved_115_118 : 4;
uint64_t hl_en : 1;
uint64_t reserved_120_127 : 8;
uint64_t ts : 48; /* W2 */
@@ -769,6 +792,8 @@ struct nix_bandprof_s {
uint64_t reserved_1008_1023 : 16;
};
+static_assert(sizeof(struct nix_bandprof_s) == NIX_MAX_CTX_SIZE);
+
enum nix_lsoalg {
NIX_LSOALG_NOP,
NIX_LSOALG_ADD_SEGNUM,
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c
index bec7d5b4d7cc..3e1bf22cba69 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c
@@ -15,6 +15,8 @@ static struct dev_hw_ops otx2_hw_ops = {
.aura_freeptr = otx2_aura_freeptr,
.refill_pool_ptrs = otx2_refill_pool_ptrs,
.pfaf_mbox_intr_handler = otx2_pfaf_mbox_intr_handler,
+ .aura_aq_init = otx2_aura_aq_init,
+ .pool_aq_init = otx2_pool_aq_init,
};
static struct dev_hw_ops cn10k_hw_ops = {
@@ -23,6 +25,8 @@ static struct dev_hw_ops cn10k_hw_ops = {
.aura_freeptr = cn10k_aura_freeptr,
.refill_pool_ptrs = cn10k_refill_pool_ptrs,
.pfaf_mbox_intr_handler = otx2_pfaf_mbox_intr_handler,
+ .aura_aq_init = otx2_aura_aq_init,
+ .pool_aq_init = otx2_pool_aq_init,
};
void otx2_init_hw_ops(struct otx2_nic *pfvf)
@@ -337,6 +341,12 @@ int cn10k_map_unmap_rq_policer(struct otx2_nic *pfvf, int rq_idx,
aq->rq.band_prof_id = policer;
aq->rq_mask.band_prof_id = GENMASK(9, 0);
+ /* If policer id is greater than 1023 then it implies hardware supports
+ * more leaf profiles. In that case use band_prof_id_h for 4 MSBs.
+ */
+ aq->rq.band_prof_id_h = policer >> 10;
+ aq->rq_mask.band_prof_id_h = GENMASK(3, 0);
+
/* Fill AQ info */
aq->qidx = rq_idx;
aq->ctype = NIX_AQ_CTYPE_RQ;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
index ec8cde98076d..a60f8cf53feb 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn20k.c
@@ -10,17 +10,6 @@
#include "otx2_struct.h"
#include "cn10k.h"
-static struct dev_hw_ops cn20k_hw_ops = {
- .pfaf_mbox_intr_handler = cn20k_pfaf_mbox_intr_handler,
- .vfaf_mbox_intr_handler = cn20k_vfaf_mbox_intr_handler,
- .pfvf_mbox_intr_handler = cn20k_pfvf_mbox_intr_handler,
-};
-
-void cn20k_init(struct otx2_nic *pfvf)
-{
- pfvf->hw_ops = &cn20k_hw_ops;
-}
-EXPORT_SYMBOL(cn20k_init);
/* CN20K mbox AF => PFx irq handler */
irqreturn_t cn20k_pfaf_mbox_intr_handler(int irq, void *pf_irq)
{
@@ -250,3 +239,212 @@ int cn20k_register_pfvf_mbox_intr(struct otx2_nic *pf, int numvfs)
return 0;
}
+
+#define RQ_BP_LVL_AURA (255 - ((85 * 256) / 100)) /* BP when 85% is full */
+
+static u8 cn20k_aura_bpid_idx(struct otx2_nic *pfvf, int aura_id)
+{
+#ifdef CONFIG_DCB
+ return pfvf->queue_to_pfc_map[aura_id];
+#else
+ return 0;
+#endif
+}
+
+static int cn20k_aura_aq_init(struct otx2_nic *pfvf, int aura_id,
+ int pool_id, int numptrs)
+{
+ struct npa_cn20k_aq_enq_req *aq;
+ struct otx2_pool *pool;
+ u8 bpid_idx;
+ int err;
+
+ pool = &pfvf->qset.pool[pool_id];
+
+ /* Allocate memory for HW to update Aura count.
+ * Alloc one cache line, so that it fits all FC_STYPE modes.
+ */
+ if (!pool->fc_addr) {
+ err = qmem_alloc(pfvf->dev, &pool->fc_addr, 1, OTX2_ALIGN);
+ if (err)
+ return err;
+ }
+
+ /* Initialize this aura's context via AF */
+ aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
+ if (!aq) {
+ /* Shared mbox memory buffer is full, flush it and retry */
+ err = otx2_sync_mbox_msg(&pfvf->mbox);
+ if (err)
+ return err;
+ aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
+ if (!aq)
+ return -ENOMEM;
+ }
+
+ aq->aura_id = aura_id;
+
+ /* Will be filled by AF with correct pool context address */
+ aq->aura.pool_addr = pool_id;
+ aq->aura.pool_caching = 1;
+ aq->aura.shift = ilog2(numptrs) - 8;
+ aq->aura.count = numptrs;
+ aq->aura.limit = numptrs;
+ aq->aura.avg_level = 255;
+ aq->aura.ena = 1;
+ aq->aura.fc_ena = 1;
+ aq->aura.fc_addr = pool->fc_addr->iova;
+ aq->aura.fc_hyst_bits = 0; /* Store count on all updates */
+
+ /* Enable backpressure for RQ aura */
+ if (aura_id < pfvf->hw.rqpool_cnt && !is_otx2_lbkvf(pfvf->pdev)) {
+ aq->aura.bp_ena = 0;
+ /* If NIX1 LF is attached then specify NIX1_RX.
+ *
+ * Below NPA_AURA_S[BP_ENA] is set according to the
+ * NPA_BPINTF_E enumeration given as:
+ * 0x0 + a*0x1 where 'a' is 0 for NIX0_RX and 1 for NIX1_RX so
+ * NIX0_RX is 0x0 + 0*0x1 = 0
+ * NIX1_RX is 0x0 + 1*0x1 = 1
+ * But in HRM it is given that
+ * "NPA_AURA_S[BP_ENA](w1[33:32]) - Enable aura backpressure to
+ * NIX-RX based on [BP] level. One bit per NIX-RX; index
+ * enumerated by NPA_BPINTF_E."
+ */
+ if (pfvf->nix_blkaddr == BLKADDR_NIX1)
+ aq->aura.bp_ena = 1;
+
+ bpid_idx = cn20k_aura_bpid_idx(pfvf, aura_id);
+ aq->aura.bpid = pfvf->bpid[bpid_idx];
+
+ /* Set backpressure level for RQ's Aura */
+ aq->aura.bp = RQ_BP_LVL_AURA;
+ }
+
+ /* Fill AQ info */
+ aq->ctype = NPA_AQ_CTYPE_AURA;
+ aq->op = NPA_AQ_INSTOP_INIT;
+
+ return 0;
+}
+
+static int cn20k_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
+ int stack_pages, int numptrs, int buf_size,
+ int type)
+{
+ struct page_pool_params pp_params = { 0 };
+ struct npa_cn20k_aq_enq_req *aq;
+ struct otx2_pool *pool;
+ int err, sz;
+
+ pool = &pfvf->qset.pool[pool_id];
+ /* Alloc memory for stack which is used to store buffer pointers */
+ err = qmem_alloc(pfvf->dev, &pool->stack,
+ stack_pages, pfvf->hw.stack_pg_bytes);
+ if (err)
+ return err;
+
+ pool->rbsize = buf_size;
+
+ /* Initialize this pool's context via AF */
+ aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
+ if (!aq) {
+ /* Shared mbox memory buffer is full, flush it and retry */
+ err = otx2_sync_mbox_msg(&pfvf->mbox);
+ if (err) {
+ qmem_free(pfvf->dev, pool->stack);
+ return err;
+ }
+ aq = otx2_mbox_alloc_msg_npa_cn20k_aq_enq(&pfvf->mbox);
+ if (!aq) {
+ qmem_free(pfvf->dev, pool->stack);
+ return -ENOMEM;
+ }
+ }
+
+ aq->aura_id = pool_id;
+ aq->pool.stack_base = pool->stack->iova;
+ aq->pool.stack_caching = 1;
+ aq->pool.ena = 1;
+ aq->pool.buf_size = buf_size / 128;
+ aq->pool.stack_max_pages = stack_pages;
+ aq->pool.shift = ilog2(numptrs) - 8;
+ aq->pool.ptr_start = 0;
+ aq->pool.ptr_end = ~0ULL;
+
+ /* Fill AQ info */
+ aq->ctype = NPA_AQ_CTYPE_POOL;
+ aq->op = NPA_AQ_INSTOP_INIT;
+
+ if (type != AURA_NIX_RQ) {
+ pool->page_pool = NULL;
+ return 0;
+ }
+
+ sz = ALIGN(ALIGN(SKB_DATA_ALIGN(buf_size), OTX2_ALIGN), PAGE_SIZE);
+ pp_params.order = get_order(sz);
+ pp_params.flags = PP_FLAG_DMA_MAP;
+ pp_params.pool_size = min(OTX2_PAGE_POOL_SZ, numptrs);
+ pp_params.nid = NUMA_NO_NODE;
+ pp_params.dev = pfvf->dev;
+ pp_params.dma_dir = DMA_FROM_DEVICE;
+ pool->page_pool = page_pool_create(&pp_params);
+ if (IS_ERR(pool->page_pool)) {
+ netdev_err(pfvf->netdev, "Creation of page pool failed\n");
+ return PTR_ERR(pool->page_pool);
+ }
+
+ return 0;
+}
+
+static int cn20k_sq_aq_init(void *dev, u16 qidx, u8 chan_offset, u16 sqb_aura)
+{
+ struct nix_cn20k_aq_enq_req *aq;
+ struct otx2_nic *pfvf = dev;
+
+ /* Get memory to put this msg */
+ aq = otx2_mbox_alloc_msg_nix_cn20k_aq_enq(&pfvf->mbox);
+ if (!aq)
+ return -ENOMEM;
+
+ aq->sq.cq = pfvf->hw.rx_queues + qidx;
+ aq->sq.max_sqe_size = NIX_MAXSQESZ_W16; /* 128 byte */
+ aq->sq.cq_ena = 1;
+ aq->sq.ena = 1;
+ aq->sq.smq = otx2_get_smq_idx(pfvf, qidx);
+ aq->sq.smq_rr_weight = mtu_to_dwrr_weight(pfvf, pfvf->tx_max_pktlen);
+ aq->sq.default_chan = pfvf->hw.tx_chan_base + chan_offset;
+ aq->sq.sqe_stype = NIX_STYPE_STF; /* Cache SQB */
+ aq->sq.sqb_aura = sqb_aura;
+ aq->sq.sq_int_ena = NIX_SQINT_BITS;
+ aq->sq.qint_idx = 0;
+ /* Due pipelining impact minimum 2000 unused SQ CQE's
+ * need to maintain to avoid CQ overflow.
+ */
+ aq->sq.cq_limit = (SEND_CQ_SKID * 256) / (pfvf->qset.sqe_cnt);
+
+ /* Fill AQ info */
+ aq->qidx = qidx;
+ aq->ctype = NIX_AQ_CTYPE_SQ;
+ aq->op = NIX_AQ_INSTOP_INIT;
+
+ return otx2_sync_mbox_msg(&pfvf->mbox);
+}
+
+static struct dev_hw_ops cn20k_hw_ops = {
+ .pfaf_mbox_intr_handler = cn20k_pfaf_mbox_intr_handler,
+ .vfaf_mbox_intr_handler = cn20k_vfaf_mbox_intr_handler,
+ .pfvf_mbox_intr_handler = cn20k_pfvf_mbox_intr_handler,
+ .sq_aq_init = cn20k_sq_aq_init,
+ .sqe_flush = cn10k_sqe_flush,
+ .aura_freeptr = cn10k_aura_freeptr,
+ .refill_pool_ptrs = cn10k_refill_pool_ptrs,
+ .aura_aq_init = cn20k_aura_aq_init,
+ .pool_aq_init = cn20k_pool_aq_init,
+};
+
+void cn20k_init(struct otx2_nic *pfvf)
+{
+ pfvf->hw_ops = &cn20k_hw_ops;
+}
+EXPORT_SYMBOL(cn20k_init);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index aff17c37ddde..75ebb17419c4 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -1369,6 +1369,13 @@ void otx2_aura_pool_free(struct otx2_nic *pfvf)
int otx2_aura_init(struct otx2_nic *pfvf, int aura_id,
int pool_id, int numptrs)
{
+ return pfvf->hw_ops->aura_aq_init(pfvf, aura_id, pool_id,
+ numptrs);
+}
+
+int otx2_aura_aq_init(struct otx2_nic *pfvf, int aura_id,
+ int pool_id, int numptrs)
+{
struct npa_aq_enq_req *aq;
struct otx2_pool *pool;
int err;
@@ -1446,6 +1453,13 @@ int otx2_aura_init(struct otx2_nic *pfvf, int aura_id,
int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id,
int stack_pages, int numptrs, int buf_size, int type)
{
+ return pfvf->hw_ops->pool_aq_init(pfvf, pool_id, stack_pages, numptrs,
+ buf_size, type);
+}
+
+int otx2_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
+ int stack_pages, int numptrs, int buf_size, int type)
+{
struct page_pool_params pp_params = { 0 };
struct xsk_buff_pool *xsk_pool;
struct npa_aq_enq_req *aq;
@@ -1516,10 +1530,8 @@ int otx2_pool_init(struct otx2_nic *pfvf, u16 pool_id,
pool->xdp_cnt = numptrs;
pool->xdp = devm_kcalloc(pfvf->dev,
numptrs, sizeof(struct xdp_buff *), GFP_KERNEL);
- if (IS_ERR(pool->xdp)) {
- netdev_err(pfvf->netdev, "Creation of xsk pool failed\n");
- return PTR_ERR(pool->xdp);
- }
+ if (!pool->xdp)
+ return -ENOMEM;
}
return 0;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index 1c8a3c078a64..e616a727a3a9 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -14,6 +14,7 @@
#include <linux/net_tstamp.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
+#include <linux/soc/marvell/silicons.h>
#include <linux/soc/marvell/octeontx2/asm.h>
#include <net/macsec.h>
#include <net/pkt_cls.h>
@@ -375,6 +376,11 @@ struct dev_hw_ops {
irqreturn_t (*pfaf_mbox_intr_handler)(int irq, void *pf_irq);
irqreturn_t (*vfaf_mbox_intr_handler)(int irq, void *pf_irq);
irqreturn_t (*pfvf_mbox_intr_handler)(int irq, void *pf_irq);
+ int (*aura_aq_init)(struct otx2_nic *pfvf, int aura_id,
+ int pool_id, int numptrs);
+ int (*pool_aq_init)(struct otx2_nic *pfvf, u16 pool_id,
+ int stack_pages, int numptrs, int buf_size,
+ int type);
};
#define CN10K_MCS_SA_PER_SC 4
@@ -527,7 +533,7 @@ struct otx2_nic {
u32 nix_lmt_size;
struct otx2_ptp *ptp;
- struct hwtstamp_config tstamp;
+ struct kernel_hwtstamp_config tstamp;
unsigned long rq_bmap;
@@ -1059,6 +1065,10 @@ irqreturn_t otx2_cq_intr_handler(int irq, void *cq_irq);
int otx2_rq_init(struct otx2_nic *pfvf, u16 qidx, u16 lpb_aura);
int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx);
int otx2_set_hw_capabilities(struct otx2_nic *pfvf);
+int otx2_aura_aq_init(struct otx2_nic *pfvf, int aura_id,
+ int pool_id, int numptrs);
+int otx2_pool_aq_init(struct otx2_nic *pfvf, u16 pool_id,
+ int stack_pages, int numptrs, int buf_size, int type);
/* RSS configuration APIs*/
int otx2_rss_init(struct otx2_nic *pfvf);
@@ -1098,8 +1108,11 @@ int otx2_open(struct net_device *netdev);
int otx2_stop(struct net_device *netdev);
int otx2_set_real_num_queues(struct net_device *netdev,
int tx_queues, int rx_queues);
-int otx2_ioctl(struct net_device *netdev, struct ifreq *req, int cmd);
-int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr);
+int otx2_config_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config);
+int otx2_config_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
/* MCAM filter related APIs */
int otx2_mcam_flow_init(struct otx2_nic *pf);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
index e13ae5484c19..a72694219df4 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_devlink.c
@@ -48,7 +48,8 @@ static int otx2_dl_mcam_count_set(struct devlink *devlink, u32 id,
}
static int otx2_dl_mcam_count_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
@@ -84,7 +85,8 @@ static int otx2_dl_ucast_flt_cnt_set(struct devlink *devlink, u32 id,
}
static int otx2_dl_ucast_flt_cnt_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct otx2_devlink *otx2_dl = devlink_priv(devlink);
struct otx2_nic *pfvf = otx2_dl->pfvf;
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
index e808995703cf..a7feb4c392b3 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c
@@ -2445,18 +2445,26 @@ static int otx2_config_hw_tx_tstamp(struct otx2_nic *pfvf, bool enable)
return 0;
}
-int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
+int otx2_config_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct otx2_nic *pfvf = netdev_priv(netdev);
+
+ *config = pfvf->tstamp;
+ return 0;
+}
+EXPORT_SYMBOL(otx2_config_hwtstamp_get);
+
+int otx2_config_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct otx2_nic *pfvf = netdev_priv(netdev);
- struct hwtstamp_config config;
if (!pfvf->ptp)
return -ENODEV;
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
-
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
if (pfvf->flags & OTX2_FLAG_PTP_ONESTEP_SYNC)
pfvf->flags &= ~OTX2_FLAG_PTP_ONESTEP_SYNC;
@@ -2465,8 +2473,11 @@ int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
otx2_config_hw_tx_tstamp(pfvf, false);
break;
case HWTSTAMP_TX_ONESTEP_SYNC:
- if (!test_bit(CN10K_PTP_ONESTEP, &pfvf->hw.cap_flag))
+ if (!test_bit(CN10K_PTP_ONESTEP, &pfvf->hw.cap_flag)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "One-step time stamping is not supported");
return -ERANGE;
+ }
pfvf->flags |= OTX2_FLAG_PTP_ONESTEP_SYNC;
schedule_delayed_work(&pfvf->ptp->synctstamp_work,
msecs_to_jiffies(500));
@@ -2478,7 +2489,7 @@ int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
otx2_config_hw_rx_tstamp(pfvf, false);
break;
@@ -2497,35 +2508,17 @@ int otx2_config_hwtstamp(struct net_device *netdev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
otx2_config_hw_rx_tstamp(pfvf, true);
- config.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
- memcpy(&pfvf->tstamp, &config, sizeof(config));
+ pfvf->tstamp = *config;
- return copy_to_user(ifr->ifr_data, &config,
- sizeof(config)) ? -EFAULT : 0;
-}
-EXPORT_SYMBOL(otx2_config_hwtstamp);
-
-int otx2_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
-{
- struct otx2_nic *pfvf = netdev_priv(netdev);
- struct hwtstamp_config *cfg = &pfvf->tstamp;
-
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return otx2_config_hwtstamp(netdev, req);
- case SIOCGHWTSTAMP:
- return copy_to_user(req->ifr_data, cfg,
- sizeof(*cfg)) ? -EFAULT : 0;
- default:
- return -EOPNOTSUPP;
- }
+ return 0;
}
-EXPORT_SYMBOL(otx2_ioctl);
+EXPORT_SYMBOL(otx2_config_hwtstamp_set);
static int otx2_do_set_vf_mac(struct otx2_nic *pf, int vf, const u8 *mac)
{
@@ -2942,7 +2935,6 @@ static const struct net_device_ops otx2_netdev_ops = {
.ndo_set_features = otx2_set_features,
.ndo_tx_timeout = otx2_tx_timeout,
.ndo_get_stats64 = otx2_get_stats64,
- .ndo_eth_ioctl = otx2_ioctl,
.ndo_set_vf_mac = otx2_set_vf_mac,
.ndo_set_vf_vlan = otx2_set_vf_vlan,
.ndo_get_vf_config = otx2_get_vf_config,
@@ -2951,6 +2943,8 @@ static const struct net_device_ops otx2_netdev_ops = {
.ndo_xdp_xmit = otx2_xdp_xmit,
.ndo_setup_tc = otx2_setup_tc,
.ndo_set_vf_trust = otx2_ndo_set_vf_trust,
+ .ndo_hwtstamp_get = otx2_config_hwtstamp_get,
+ .ndo_hwtstamp_set = otx2_config_hwtstamp_set,
};
int otx2_wq_init(struct otx2_nic *pf)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
index 25381f079b97..f4fdbfba8667 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
@@ -534,8 +534,9 @@ static const struct net_device_ops otx2vf_netdev_ops = {
.ndo_set_features = otx2vf_set_features,
.ndo_get_stats64 = otx2_get_stats64,
.ndo_tx_timeout = otx2_tx_timeout,
- .ndo_eth_ioctl = otx2_ioctl,
.ndo_setup_tc = otx2_setup_tc,
+ .ndo_hwtstamp_get = otx2_config_hwtstamp_get,
+ .ndo_hwtstamp_set = otx2_config_hwtstamp_set,
};
static int otx2_vf_wq_init(struct otx2_nic *vf)
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
index a68cd3f0304c..ad6298456639 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c
@@ -1727,6 +1727,13 @@ static int mlx4_en_get_num_flows(struct mlx4_en_priv *priv)
}
+static u32 mlx4_en_get_rx_ring_count(struct net_device *dev)
+{
+ struct mlx4_en_priv *priv = netdev_priv(dev);
+
+ return priv->rx_ring_num;
+}
+
static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
u32 *rule_locs)
{
@@ -1743,9 +1750,6 @@ static int mlx4_en_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
return -EINVAL;
switch (cmd->cmd) {
- case ETHTOOL_GRXRINGS:
- cmd->data = priv->rx_ring_num;
- break;
case ETHTOOL_GRXCLSRLCNT:
cmd->rule_cnt = mlx4_en_get_num_flows(priv);
break;
@@ -2154,6 +2158,7 @@ const struct ethtool_ops mlx4_en_ethtool_ops = {
.set_ringparam = mlx4_en_set_ringparam,
.get_rxnfc = mlx4_en_get_rxnfc,
.set_rxnfc = mlx4_en_set_rxnfc,
+ .get_rx_ring_count = mlx4_en_get_rx_ring_count,
.get_rxfh_indir_size = mlx4_en_get_rxfh_indir_size,
.get_rxfh_key_size = mlx4_en_get_rxfh_key_size,
.get_rxfh = mlx4_en_get_rxfh,
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
index 308b4458e0d4..81bf8908b897 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c
@@ -2420,21 +2420,22 @@ static int mlx4_en_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-static int mlx4_en_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
+static int mlx4_en_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
struct mlx4_en_dev *mdev = priv->mdev;
- struct hwtstamp_config config;
-
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
/* device doesn't support time stamping */
- if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS))
+ if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "device doesn't support time stamping");
return -EINVAL;
+ }
/* TX HW timestamp */
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
break;
@@ -2443,7 +2444,7 @@ static int mlx4_en_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
}
/* RX HW timestamp */
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
break;
case HWTSTAMP_FILTER_ALL:
@@ -2461,39 +2462,27 @@ static int mlx4_en_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
if (mlx4_en_reset_config(dev, config, dev->features)) {
- config.tx_type = HWTSTAMP_TX_OFF;
- config.rx_filter = HWTSTAMP_FILTER_NONE;
+ config->tx_type = HWTSTAMP_TX_OFF;
+ config->rx_filter = HWTSTAMP_FILTER_NONE;
}
- return copy_to_user(ifr->ifr_data, &config,
- sizeof(config)) ? -EFAULT : 0;
+ return 0;
}
-static int mlx4_en_hwtstamp_get(struct net_device *dev, struct ifreq *ifr)
+static int mlx4_en_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
- return copy_to_user(ifr->ifr_data, &priv->hwtstamp_config,
- sizeof(priv->hwtstamp_config)) ? -EFAULT : 0;
-}
-
-static int mlx4_en_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return mlx4_en_hwtstamp_set(dev, ifr);
- case SIOCGHWTSTAMP:
- return mlx4_en_hwtstamp_get(dev, ifr);
- default:
- return -EOPNOTSUPP;
- }
+ *config = priv->hwtstamp_config;
+ return 0;
}
static netdev_features_t mlx4_en_fix_features(struct net_device *netdev,
@@ -2560,7 +2549,7 @@ static int mlx4_en_set_features(struct net_device *netdev,
}
if (reset) {
- ret = mlx4_en_reset_config(netdev, priv->hwtstamp_config,
+ ret = mlx4_en_reset_config(netdev, &priv->hwtstamp_config,
features);
if (ret)
return ret;
@@ -2844,7 +2833,6 @@ static const struct net_device_ops mlx4_netdev_ops = {
.ndo_set_mac_address = mlx4_en_set_mac,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = mlx4_en_change_mtu,
- .ndo_eth_ioctl = mlx4_en_ioctl,
.ndo_tx_timeout = mlx4_en_tx_timeout,
.ndo_vlan_rx_add_vid = mlx4_en_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = mlx4_en_vlan_rx_kill_vid,
@@ -2858,6 +2846,8 @@ static const struct net_device_ops mlx4_netdev_ops = {
.ndo_features_check = mlx4_en_features_check,
.ndo_set_tx_maxrate = mlx4_en_set_tx_maxrate,
.ndo_bpf = mlx4_xdp,
+ .ndo_hwtstamp_get = mlx4_en_hwtstamp_get,
+ .ndo_hwtstamp_set = mlx4_en_hwtstamp_set,
};
static const struct net_device_ops mlx4_netdev_ops_master = {
@@ -3512,7 +3502,7 @@ out:
}
int mlx4_en_reset_config(struct net_device *dev,
- struct hwtstamp_config ts_config,
+ struct kernel_hwtstamp_config *ts_config,
netdev_features_t features)
{
struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -3522,8 +3512,8 @@ int mlx4_en_reset_config(struct net_device *dev,
int port_up = 0;
int err = 0;
- if (priv->hwtstamp_config.tx_type == ts_config.tx_type &&
- priv->hwtstamp_config.rx_filter == ts_config.rx_filter &&
+ if (priv->hwtstamp_config.tx_type == ts_config->tx_type &&
+ priv->hwtstamp_config.rx_filter == ts_config->rx_filter &&
!DEV_FEATURE_CHANGED(dev, features, NETIF_F_HW_VLAN_CTAG_RX) &&
!DEV_FEATURE_CHANGED(dev, features, NETIF_F_RXFCS))
return 0; /* Nothing to change */
@@ -3542,7 +3532,7 @@ int mlx4_en_reset_config(struct net_device *dev,
mutex_lock(&mdev->state_lock);
memcpy(&new_prof, priv->prof, sizeof(struct mlx4_en_port_profile));
- memcpy(&new_prof.hwtstamp_config, &ts_config, sizeof(ts_config));
+ memcpy(&new_prof.hwtstamp_config, ts_config, sizeof(*ts_config));
err = mlx4_en_try_alloc_resources(priv, tmp, &new_prof, true);
if (err)
@@ -3560,7 +3550,7 @@ int mlx4_en_reset_config(struct net_device *dev,
dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
else
dev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
- } else if (ts_config.rx_filter == HWTSTAMP_FILTER_NONE) {
+ } else if (ts_config->rx_filter == HWTSTAMP_FILTER_NONE) {
/* RX time-stamping is OFF, update the RX vlan offload
* to the latest wanted state
*/
@@ -3581,7 +3571,7 @@ int mlx4_en_reset_config(struct net_device *dev,
* Regardless of the caller's choice,
* Turn Off RX vlan offload in case of time-stamping is ON
*/
- if (ts_config.rx_filter != HWTSTAMP_FILTER_NONE) {
+ if (ts_config->rx_filter != HWTSTAMP_FILTER_NONE) {
if (dev->features & NETIF_F_HW_VLAN_CTAG_RX)
en_warn(priv, "Turning off RX vlan offload since RX time-stamping is ON\n");
dev->features &= ~NETIF_F_HW_VLAN_CTAG_RX;
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 03d2fc7d9b09..4293f8e33f44 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -174,7 +174,8 @@ MODULE_PARM_DESC(port_type_array, "Array of port types: HW_DEFAULT (0) is defaul
static atomic_t pf_loading = ATOMIC_INIT(0);
static int mlx4_devlink_ierr_reset_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
ctx->val.vbool = !!mlx4_internal_err_reset;
return 0;
@@ -189,7 +190,8 @@ static int mlx4_devlink_ierr_reset_set(struct devlink *devlink, u32 id,
}
static int mlx4_devlink_crdump_snapshot_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx4_priv *priv = devlink_priv(devlink);
struct mlx4_dev *dev = &priv->dev;
@@ -4366,7 +4368,6 @@ static pci_ers_result_t mlx4_pci_slot_reset(struct pci_dev *pdev)
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
return PCI_ERS_RESULT_RECOVERED;
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index ad0d91a75184..aab97694f86b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -388,7 +388,7 @@ struct mlx4_en_port_profile {
u8 num_up;
int rss_rings;
int inline_thold;
- struct hwtstamp_config hwtstamp_config;
+ struct kernel_hwtstamp_config hwtstamp_config;
};
struct mlx4_en_profile {
@@ -612,7 +612,7 @@ struct mlx4_en_priv {
bool wol;
struct device *ddev;
struct hlist_head mac_hash[MLX4_EN_MAC_HASH_SIZE];
- struct hwtstamp_config hwtstamp_config;
+ struct kernel_hwtstamp_config hwtstamp_config;
u32 counter_index;
#ifdef CONFIG_MLX4_EN_DCB
@@ -780,7 +780,7 @@ void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev);
int mlx4_en_moderation_update(struct mlx4_en_priv *priv);
int mlx4_en_reset_config(struct net_device *dev,
- struct hwtstamp_config ts_config,
+ struct kernel_hwtstamp_config *ts_config,
netdev_features_t new_features);
void mlx4_en_update_pfc_stats_bitmap(struct mlx4_dev *dev,
struct mlx4_en_stats_bitmap *stats_bitmap,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index 722282cebce9..5b08e5ffe0e2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -181,6 +181,7 @@ static int cmd_alloc_index(struct mlx5_cmd *cmd, struct mlx5_cmd_work_ent *ent)
static void cmd_free_index(struct mlx5_cmd *cmd, int idx)
{
lockdep_assert_held(&cmd->alloc_lock);
+ cmd->ent_arr[idx] = NULL;
set_bit(idx, &cmd->vars.bitmask);
}
@@ -1200,6 +1201,44 @@ out_err:
return err;
}
+/* Check if all command slots are stalled (timed out and not recovered).
+ * returns true if all slots timed out on a recent command and have not been
+ * completed by FW yet. (stalled state)
+ * false otherwise (at least one slot is not stalled).
+ *
+ * In such odd situation "all_stalled", this serves as a protection mechanism
+ * to avoid blocking the kernel for long periods of time in case FW is not
+ * responding to commands.
+ */
+static bool mlx5_cmd_all_stalled(struct mlx5_core_dev *dev)
+{
+ struct mlx5_cmd *cmd = &dev->cmd;
+ bool all_stalled = true;
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&cmd->alloc_lock, flags);
+
+ /* at least one command slot is free */
+ if (bitmap_weight(&cmd->vars.bitmask, cmd->vars.max_reg_cmds) > 0) {
+ all_stalled = false;
+ goto out;
+ }
+
+ for_each_clear_bit(i, &cmd->vars.bitmask, cmd->vars.max_reg_cmds) {
+ struct mlx5_cmd_work_ent *ent = dev->cmd.ent_arr[i];
+
+ if (!test_bit(MLX5_CMD_ENT_STATE_TIMEDOUT, &ent->state)) {
+ all_stalled = false;
+ break;
+ }
+ }
+out:
+ spin_unlock_irqrestore(&cmd->alloc_lock, flags);
+
+ return all_stalled;
+}
+
/* Notes:
* 1. Callback functions may not sleep
* 2. page queue commands do not support asynchrous completion
@@ -1230,6 +1269,15 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in,
if (callback && page_queue)
return -EINVAL;
+ if (!page_queue && mlx5_cmd_all_stalled(dev)) {
+ mlx5_core_err_rl(dev,
+ "All CMD slots are stalled, aborting command\n");
+ /* there's no reason to wait and block the whole kernel if FW
+ * isn't currently responding to all slots, fail immediately
+ */
+ return -EAGAIN;
+ }
+
ent = cmd_alloc_ent(cmd, in, out, uout, uout_size,
callback, context, page_queue);
if (IS_ERR(ent))
@@ -1700,6 +1748,13 @@ static void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool force
if (test_bit(i, &vector)) {
ent = cmd->ent_arr[i];
+ if (forced && ent->ret == -ETIMEDOUT)
+ set_bit(MLX5_CMD_ENT_STATE_TIMEDOUT,
+ &ent->state);
+ else if (!forced) /* real FW completion */
+ clear_bit(MLX5_CMD_ENT_STATE_TIMEDOUT,
+ &ent->state);
+
/* if we already completed the command, ignore it */
if (!test_and_clear_bit(MLX5_CMD_ENT_STATE_PENDING_COMP,
&ent->state)) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
index e9f319a9bdd6..60f7ab1d72e7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c
@@ -66,8 +66,8 @@ void mlx5_cq_tasklet_cb(struct tasklet_struct *t)
tasklet_schedule(&ctx->task);
}
-static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq,
- struct mlx5_eqe *eqe)
+void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq,
+ struct mlx5_eqe *eqe)
{
unsigned long flags;
struct mlx5_eq_tasklet *tasklet_ctx = cq->tasklet_ctx.priv;
@@ -95,7 +95,15 @@ static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq,
if (schedule_tasklet)
tasklet_schedule(&tasklet_ctx->task);
}
+EXPORT_SYMBOL(mlx5_add_cq_to_tasklet);
+static void mlx5_core_cq_dummy_cb(struct mlx5_core_cq *cq, struct mlx5_eqe *eqe)
+{
+ mlx5_core_err(cq->eq->core.dev,
+ "CQ default completion callback, CQ #%u\n", cq->cqn);
+}
+
+#define MLX5_CQ_INIT_CMD_SN cpu_to_be32(2 << 28)
/* Callers must verify outbox status in case of err */
int mlx5_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
u32 *in, int inlen, u32 *out, int outlen)
@@ -121,10 +129,19 @@ int mlx5_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq,
cq->arm_sn = 0;
cq->eq = eq;
cq->uid = MLX5_GET(create_cq_in, in, uid);
+
+ /* Kernel CQs must set the arm_db address prior to calling
+ * this function, allowing for the proper value to be
+ * initialized. User CQs are responsible for their own
+ * initialization since they do not use the arm_db field.
+ */
+ if (cq->arm_db)
+ *cq->arm_db = MLX5_CQ_INIT_CMD_SN;
+
refcount_set(&cq->refcount, 1);
init_completion(&cq->free);
if (!cq->comp)
- cq->comp = mlx5_add_cq_to_tasklet;
+ cq->comp = mlx5_core_cq_dummy_cb;
/* assuming CQ will be deleted before the EQ */
cq->tasklet_ctx.priv = &eq->tasklet_ctx;
INIT_LIST_HEAD(&cq->tasklet_ctx.list);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
index 891bbbbfbbf1..64c04f52990f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c
@@ -564,10 +564,14 @@ int mlx5_rescan_drivers_locked(struct mlx5_core_dev *dev)
bool mlx5_same_hw_devs(struct mlx5_core_dev *dev, struct mlx5_core_dev *peer_dev)
{
- u64 fsystem_guid, psystem_guid;
+ u8 fsystem_guid[MLX5_SW_IMAGE_GUID_MAX_BYTES];
+ u8 psystem_guid[MLX5_SW_IMAGE_GUID_MAX_BYTES];
+ u8 flen;
+ u8 plen;
- fsystem_guid = mlx5_query_nic_system_image_guid(dev);
- psystem_guid = mlx5_query_nic_system_image_guid(peer_dev);
+ mlx5_query_nic_sw_system_image_guid(dev, fsystem_guid, &flen);
+ mlx5_query_nic_sw_system_image_guid(peer_dev, psystem_guid, &plen);
- return (fsystem_guid && psystem_guid && fsystem_guid == psystem_guid);
+ return plen && flen && flen == plen &&
+ !memcmp(fsystem_guid, psystem_guid, flen);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index fceea83abbd7..887adf4807d1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -541,7 +541,7 @@ static int mlx5_devlink_num_doorbells_validate(struct devlink *devlink, u32 id,
max_num_channels = mlx5e_get_max_num_channels(mdev);
if (val32 > max_num_channels) {
NL_SET_ERR_MSG_FMT_MOD(extack,
- "Requested num_doorbells (%u) exceeds maximum number of channels (%u)",
+ "Requested num_doorbells (%u) exceeds max number of channels (%u)",
val32, max_num_channels);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h
index c9555119a661..43b9bf8829cf 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.h
@@ -26,7 +26,8 @@ enum mlx5_devlink_param_id {
MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH,
MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW,
MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH,
- MLX5_DEVLINK_PARAM_ID_CQE_COMPRESSION_TYPE
+ MLX5_DEVLINK_PARAM_ID_CQE_COMPRESSION_TYPE,
+ MLX5_DEVLINK_PARAM_ID_SWP_L4_CSUM_MODE,
};
struct mlx5_trap_ctx {
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 080e7eab52c7..7bcf822a89f9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
@@ -54,7 +54,7 @@ static int mlx5_query_mtrc_caps(struct mlx5_fw_tracer *tracer)
if (!MLX5_GET(mtrc_cap, out, trace_to_memory)) {
mlx5_core_dbg(dev, "FWTracer: Device does not support logging traces to memory\n");
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
tracer->trc_ver = MLX5_GET(mtrc_cap, out, trc_ver);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h
index 14e3207b14e7..811178d8976c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h
@@ -634,7 +634,10 @@ struct mlx5e_dma_info {
struct mlx5e_shampo_hd {
struct mlx5e_frag_page *pages;
u32 hd_per_wq;
+ u32 hd_per_page;
u16 hd_per_wqe;
+ u8 log_hd_per_page;
+ u8 log_hd_entry_size;
unsigned long *bitmap;
u16 pi;
u16 ci;
@@ -696,7 +699,7 @@ struct mlx5e_rq {
struct mlx5e_rq_stats *stats;
struct mlx5e_cq cq;
struct mlx5e_cq_decomp cqd;
- struct hwtstamp_config *tstamp;
+ struct kernel_hwtstamp_config *hwtstamp_config;
struct mlx5_clock *clock;
struct mlx5e_icosq *icosq;
struct mlx5e_priv *priv;
@@ -784,7 +787,6 @@ struct mlx5e_channel {
/* control */
struct mlx5e_priv *priv;
struct mlx5_core_dev *mdev;
- struct hwtstamp_config *tstamp;
DECLARE_BITMAP(state, MLX5E_CHANNEL_NUM_STATES);
int ix;
int vec_ix;
@@ -918,7 +920,7 @@ struct mlx5e_priv {
u8 max_opened_tc;
bool tx_ptp_opened;
bool rx_ptp_opened;
- struct hwtstamp_config tstamp;
+ struct kernel_hwtstamp_config hwtstamp_config;
u16 q_counter[MLX5_SD_MAX_GROUP_SZ];
u16 drop_rq_q_counter;
struct notifier_block events_nb;
@@ -1027,8 +1029,11 @@ void mlx5e_self_test(struct net_device *ndev, struct ethtool_test *etest,
u64 *buf);
void mlx5e_set_rx_mode_work(struct work_struct *work);
-int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr);
-int mlx5e_hwstamp_get(struct mlx5e_priv *priv, struct ifreq *ifr);
+int mlx5e_hwtstamp_set(struct mlx5e_priv *priv,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
+int mlx5e_hwtstamp_get(struct mlx5e_priv *priv,
+ struct kernel_hwtstamp_config *config);
int mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool val, bool rx_filter);
int mlx5e_vlan_rx_add_vid(struct net_device *dev, __always_unused __be16 proto,
@@ -1154,7 +1159,9 @@ extern const struct ethtool_ops mlx5e_ethtool_ops;
int mlx5e_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, u32 *mkey);
int mlx5e_create_mdev_resources(struct mlx5_core_dev *mdev, bool create_tises);
void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev);
-int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb,
+int mlx5e_modify_tirs_lb(struct mlx5_core_dev *mdev, bool enable_uc_lb,
+ bool enable_mc_lb);
+int mlx5e_refresh_tirs(struct mlx5_core_dev *mdev, bool enable_uc_lb,
bool enable_mc_lb);
void mlx5e_mkey_set_relaxed_ordering(struct mlx5_core_dev *mdev, void *mkc);
@@ -1242,7 +1249,7 @@ void mlx5e_netdev_attach_nic_profile(struct mlx5e_priv *priv);
void mlx5e_set_netdev_mtu_boundaries(struct mlx5e_priv *priv);
void mlx5e_build_nic_params(struct mlx5e_priv *priv, struct mlx5e_xsk *xsk, u16 mtu);
-void mlx5e_set_xdp_feature(struct net_device *netdev);
+void mlx5e_set_xdp_feature(struct mlx5e_priv *priv);
netdev_features_t mlx5e_features_check(struct sk_buff *skb,
struct net_device *netdev,
netdev_features_t features);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c
index 0b1ac6e5c890..8818f65d1fbc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/devlink.c
@@ -40,11 +40,8 @@ void mlx5e_destroy_devlink(struct mlx5e_dev *mlx5e_dev)
static void
mlx5e_devlink_get_port_parent_id(struct mlx5_core_dev *dev, struct netdev_phys_item_id *ppid)
{
- u64 parent_id;
-
- parent_id = mlx5_query_nic_system_image_guid(dev);
- ppid->id_len = sizeof(parent_id);
- memcpy(ppid->id, &parent_id, sizeof(parent_id));
+ BUILD_BUG_ON(MLX5_SW_IMAGE_GUID_MAX_BYTES > MAX_PHYS_ITEM_ID_LEN);
+ mlx5_query_nic_sw_system_image_guid(dev, ppid->id, &ppid->id_len);
}
int mlx5e_devlink_port_register(struct mlx5e_dev *mlx5e_dev,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.c b/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.c
index 4e72ca8070e2..1de18c7e96ec 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.c
@@ -6,6 +6,7 @@
#include <linux/xarray.h>
#include <linux/hashtable.h>
#include <linux/refcount.h>
+#include <linux/mlx5/driver.h>
#include "mapping.h"
@@ -24,7 +25,8 @@ struct mapping_ctx {
struct delayed_work dwork;
struct list_head pending_list;
spinlock_t pending_list_lock; /* Guards pending list */
- u64 id;
+ u8 id[MLX5_SW_IMAGE_GUID_MAX_BYTES];
+ u8 id_len;
u8 type;
struct list_head list;
refcount_t refcount;
@@ -220,13 +222,15 @@ mapping_create(size_t data_size, u32 max_id, bool delayed_removal)
}
struct mapping_ctx *
-mapping_create_for_id(u64 id, u8 type, size_t data_size, u32 max_id, bool delayed_removal)
+mapping_create_for_id(u8 *id, u8 id_len, u8 type, size_t data_size, u32 max_id,
+ bool delayed_removal)
{
struct mapping_ctx *ctx;
mutex_lock(&shared_ctx_lock);
list_for_each_entry(ctx, &shared_ctx_list, list) {
- if (ctx->id == id && ctx->type == type) {
+ if (ctx->type == type && ctx->id_len == id_len &&
+ !memcmp(id, ctx->id, id_len)) {
if (refcount_inc_not_zero(&ctx->refcount))
goto unlock;
break;
@@ -237,7 +241,8 @@ mapping_create_for_id(u64 id, u8 type, size_t data_size, u32 max_id, bool delaye
if (IS_ERR(ctx))
goto unlock;
- ctx->id = id;
+ memcpy(ctx->id, id, id_len);
+ ctx->id_len = id_len;
ctx->type = type;
list_add(&ctx->list, &shared_ctx_list);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.h b/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.h
index 4e2119f0f4c1..e86a103d58b9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/mapping.h
@@ -27,6 +27,7 @@ void mapping_destroy(struct mapping_ctx *ctx);
/* adds mapping with an id or get an existing mapping with the same id
*/
struct mapping_ctx *
-mapping_create_for_id(u64 id, u8 type, size_t data_size, u32 max_id, bool delayed_removal);
+mapping_create_for_id(u8 *id, u8 id_len, u8 type, size_t data_size, u32 max_id,
+ bool delayed_removal);
#endif /* __MLX5_MAPPING_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
index 3692298e10f2..c9bdee9a8b30 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
@@ -100,7 +100,7 @@ u8 mlx5e_mpwrq_umr_entry_size(enum mlx5e_mpwrq_umr_mode mode)
return sizeof(struct mlx5_ksm) * 4;
}
WARN_ONCE(1, "MPWRQ UMR mode %d is not known\n", mode);
- return 0;
+ return 1;
}
u8 mlx5e_mpwrq_log_wqe_sz(struct mlx5_core_dev *mdev, u8 page_shift,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
index c93ee969ea64..424f8a2728a3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c
@@ -82,7 +82,7 @@ static struct mlx5e_skb_cb_hwtstamp *mlx5e_skb_cb_get_hwts(struct sk_buff *skb)
}
static void mlx5e_skb_cb_hwtstamp_tx(struct sk_buff *skb,
- struct mlx5e_ptp_cq_stats *cq_stats)
+ struct mlx5e_ptpsq *ptpsq)
{
struct skb_shared_hwtstamps hwts = {};
ktime_t diff;
@@ -92,8 +92,17 @@ static void mlx5e_skb_cb_hwtstamp_tx(struct sk_buff *skb,
/* Maximal allowed diff is 1 / 128 second */
if (diff > (NSEC_PER_SEC >> 7)) {
- cq_stats->abort++;
- cq_stats->abort_abs_diff_ns += diff;
+ struct mlx5e_txqsq *sq = &ptpsq->txqsq;
+
+ ptpsq->cq_stats->abort++;
+ ptpsq->cq_stats->abort_abs_diff_ns += diff;
+ if (diff > (NSEC_PER_SEC >> 1) &&
+ !test_and_set_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state)) {
+ netdev_warn(sq->channel->netdev,
+ "PTP TX timestamp difference between CQE and port exceeds threshold: %lld ns, recovering SQ %u\n",
+ (s64)diff, sq->sqn);
+ queue_work(sq->priv->wq, &ptpsq->report_unhealthy_work);
+ }
return;
}
@@ -103,7 +112,7 @@ static void mlx5e_skb_cb_hwtstamp_tx(struct sk_buff *skb,
void mlx5e_skb_cb_hwtstamp_handler(struct sk_buff *skb, int hwtstamp_type,
ktime_t hwtstamp,
- struct mlx5e_ptp_cq_stats *cq_stats)
+ struct mlx5e_ptpsq *ptpsq)
{
switch (hwtstamp_type) {
case (MLX5E_SKB_CB_CQE_HWTSTAMP):
@@ -121,7 +130,7 @@ void mlx5e_skb_cb_hwtstamp_handler(struct sk_buff *skb, int hwtstamp_type,
!mlx5e_skb_cb_get_hwts(skb)->port_hwtstamp)
return;
- mlx5e_skb_cb_hwtstamp_tx(skb, cq_stats);
+ mlx5e_skb_cb_hwtstamp_tx(skb, ptpsq);
memset(skb->cb, 0, sizeof(struct mlx5e_skb_cb_hwtstamp));
}
@@ -209,7 +218,7 @@ static void mlx5e_ptp_handle_ts_cqe(struct mlx5e_ptpsq *ptpsq,
hwtstamp = mlx5e_cqe_ts_to_ns(sq->ptp_cyc2time, sq->clock, get_cqe_ts(cqe));
mlx5e_skb_cb_hwtstamp_handler(skb, MLX5E_SKB_CB_PORT_HWTSTAMP,
- hwtstamp, ptpsq->cq_stats);
+ hwtstamp, ptpsq);
ptpsq->cq_stats->cqe++;
mlx5e_ptpsq_mark_ts_cqes_undelivered(ptpsq, hwtstamp);
@@ -713,7 +722,7 @@ static int mlx5e_init_ptp_rq(struct mlx5e_ptp *c, struct mlx5e_params *params,
rq->netdev = priv->netdev;
rq->priv = priv;
rq->clock = mdev->clock;
- rq->tstamp = &priv->tstamp;
+ rq->hwtstamp_config = &priv->hwtstamp_config;
rq->mdev = mdev;
rq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu);
rq->stats = &c->priv->ptp_stats.rq;
@@ -896,7 +905,6 @@ int mlx5e_ptp_open(struct mlx5e_priv *priv, struct mlx5e_params *params,
c->priv = priv;
c->mdev = priv->mdev;
- c->tstamp = &priv->tstamp;
c->pdev = mlx5_core_dma_dev(priv->mdev);
c->netdev = priv->netdev;
c->mkey_be = cpu_to_be32(priv->mdev->mlx5e_res.hw_objs.mkey);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h
index 1b3c9648220b..2a457a2ed707 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.h
@@ -64,7 +64,6 @@ struct mlx5e_ptp {
/* control */
struct mlx5e_priv *priv;
struct mlx5_core_dev *mdev;
- struct hwtstamp_config *tstamp;
DECLARE_BITMAP(state, MLX5E_PTP_STATE_NUM_STATES);
struct mlx5_sq_bfreg *bfreg;
};
@@ -148,7 +147,7 @@ enum {
void mlx5e_skb_cb_hwtstamp_handler(struct sk_buff *skb, int hwtstamp_type,
ktime_t hwtstamp,
- struct mlx5e_ptp_cq_stats *cq_stats);
+ struct mlx5e_ptpsq *ptpsq);
void mlx5e_skb_cb_hwtstamp_init(struct sk_buff *skb);
#endif /* __MLX5_EN_PTP_H__ */
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 9d1c677814e0..87a2ad69526d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c
@@ -30,15 +30,11 @@ static bool mlx5_esw_bridge_dev_same_hw(struct net_device *dev, struct mlx5_eswi
{
struct mlx5e_priv *priv = netdev_priv(dev);
struct mlx5_core_dev *mdev, *esw_mdev;
- u64 system_guid, esw_system_guid;
mdev = priv->mdev;
esw_mdev = esw->dev;
- system_guid = mlx5_query_nic_system_image_guid(mdev);
- esw_system_guid = mlx5_query_nic_system_image_guid(esw_mdev);
-
- return system_guid == esw_system_guid;
+ return mlx5_same_hw_devs(mdev, esw_mdev);
}
static struct net_device *
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 b1415992ffa2..0686fbdd5a05 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
@@ -318,7 +318,8 @@ mlx5e_rx_reporter_diagnose_common_ptp_config(struct mlx5e_priv *priv, struct mlx
struct devlink_fmsg *fmsg)
{
mlx5e_health_fmsg_named_obj_nest_start(fmsg, "PTP");
- devlink_fmsg_u32_pair_put(fmsg, "filter_type", priv->tstamp.rx_filter);
+ devlink_fmsg_u32_pair_put(fmsg, "filter_type",
+ priv->hwtstamp_config.rx_filter);
mlx5e_rx_reporter_diagnose_generic_rq(&ptp_ch->rq, fmsg);
mlx5e_health_fmsg_named_obj_nest_end(fmsg);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c
index c96cbc4b0dbf..88b0e1050d1a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c
@@ -231,6 +231,8 @@ mlx5e_rss_create_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
rqtn, rss_inner);
mlx5e_tir_builder_build_packet_merge(builder, pkt_merge_param);
rss_tt = mlx5e_rss_get_tt_config(rss, tt);
+ mlx5e_tir_builder_build_self_lb_block(builder, rss->params.self_lb_blk,
+ rss->params.self_lb_blk);
mlx5e_tir_builder_build_rss(builder, &rss->hash, &rss_tt, inner);
err = mlx5e_tir_init(tir, builder, rss->mdev, true);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h
index 5fb03cd0a411..17664757a561 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rss.h
@@ -23,6 +23,7 @@ struct mlx5e_rss_init_params {
struct mlx5e_rss_params {
bool inner_ft_support;
u32 drop_rqn;
+ bool self_lb_blk;
};
struct mlx5e_rss_params_traffic_type
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c
index ac26a32845d0..55c117b7d8c4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.c
@@ -71,6 +71,8 @@ static int mlx5e_rx_res_rss_init_def(struct mlx5e_rx_res *res,
rss_params = (struct mlx5e_rss_params) {
.inner_ft_support = inner_ft_support,
.drop_rqn = res->drop_rqn,
+ .self_lb_blk =
+ res->features & MLX5E_RX_RES_FEATURE_SELF_LB_BLOCK,
};
rss = mlx5e_rss_init(res->mdev, &rss_params, &init_params);
@@ -104,6 +106,8 @@ int mlx5e_rx_res_rss_init(struct mlx5e_rx_res *res, u32 rss_idx, unsigned int in
rss_params = (struct mlx5e_rss_params) {
.inner_ft_support = inner_ft_support,
.drop_rqn = res->drop_rqn,
+ .self_lb_blk =
+ res->features & MLX5E_RX_RES_FEATURE_SELF_LB_BLOCK,
};
rss = mlx5e_rss_init(res->mdev, &rss_params, &init_params);
@@ -346,6 +350,7 @@ static struct mlx5e_rx_res *mlx5e_rx_res_alloc(struct mlx5_core_dev *mdev, unsig
static int mlx5e_rx_res_channels_init(struct mlx5e_rx_res *res)
{
bool inner_ft_support = res->features & MLX5E_RX_RES_FEATURE_INNER_FT;
+ bool self_lb_blk = res->features & MLX5E_RX_RES_FEATURE_SELF_LB_BLOCK;
struct mlx5e_tir_builder *builder;
int err = 0;
int ix;
@@ -376,6 +381,8 @@ static int mlx5e_rx_res_channels_init(struct mlx5e_rx_res *res)
mlx5e_rqt_get_rqtn(&res->channels[ix].direct_rqt),
inner_ft_support);
mlx5e_tir_builder_build_packet_merge(builder, &res->pkt_merge_param);
+ mlx5e_tir_builder_build_self_lb_block(builder, self_lb_blk,
+ self_lb_blk);
mlx5e_tir_builder_build_direct(builder);
err = mlx5e_tir_init(&res->channels[ix].direct_tir, builder, res->mdev, true);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h
index 65a857c215e1..675780120a20 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rx_res.h
@@ -21,6 +21,7 @@ enum mlx5e_rx_res_features {
MLX5E_RX_RES_FEATURE_INNER_FT = BIT(0),
MLX5E_RX_RES_FEATURE_PTP = BIT(1),
MLX5E_RX_RES_FEATURE_MULTI_VHCA = BIT(2),
+ MLX5E_RX_RES_FEATURE_SELF_LB_BLOCK = BIT(3),
};
/* Setup */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/int_port.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/int_port.c
index 896f718483c3..991f47050643 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/int_port.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/int_port.c
@@ -307,7 +307,8 @@ mlx5e_tc_int_port_init(struct mlx5e_priv *priv)
{
struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
struct mlx5e_tc_int_port_priv *int_port_priv;
- u64 mapping_id;
+ u8 mapping_id[MLX5_SW_IMAGE_GUID_MAX_BYTES];
+ u8 id_len;
if (!mlx5e_tc_int_port_supported(esw))
return NULL;
@@ -316,9 +317,10 @@ mlx5e_tc_int_port_init(struct mlx5e_priv *priv)
if (!int_port_priv)
return NULL;
- mapping_id = mlx5_query_nic_system_image_guid(priv->mdev);
+ mlx5_query_nic_sw_system_image_guid(priv->mdev, mapping_id, &id_len);
- int_port_priv->metadata_mapping = mapping_create_for_id(mapping_id, MAPPING_TYPE_INT_PORT,
+ int_port_priv->metadata_mapping = mapping_create_for_id(mapping_id, id_len,
+ MAPPING_TYPE_INT_PORT,
sizeof(u32) * 2,
(1 << ESW_VPORT_BITS) - 1, true);
if (IS_ERR(int_port_priv->metadata_mapping)) {
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 870d12364f99..fc0e57403d25 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
@@ -2287,9 +2287,10 @@ mlx5_tc_ct_init(struct mlx5e_priv *priv, struct mlx5_fs_chains *chains,
enum mlx5_flow_namespace_type ns_type,
struct mlx5e_post_act *post_act)
{
+ u8 mapping_id[MLX5_SW_IMAGE_GUID_MAX_BYTES];
struct mlx5_tc_ct_priv *ct_priv;
struct mlx5_core_dev *dev;
- u64 mapping_id;
+ u8 id_len;
int err;
dev = priv->mdev;
@@ -2301,16 +2302,18 @@ mlx5_tc_ct_init(struct mlx5e_priv *priv, struct mlx5_fs_chains *chains,
if (!ct_priv)
goto err_alloc;
- mapping_id = mlx5_query_nic_system_image_guid(dev);
+ mlx5_query_nic_sw_system_image_guid(dev, mapping_id, &id_len);
- ct_priv->zone_mapping = mapping_create_for_id(mapping_id, MAPPING_TYPE_ZONE,
+ ct_priv->zone_mapping = mapping_create_for_id(mapping_id, id_len,
+ MAPPING_TYPE_ZONE,
sizeof(u16), 0, true);
if (IS_ERR(ct_priv->zone_mapping)) {
err = PTR_ERR(ct_priv->zone_mapping);
goto err_mapping_zone;
}
- ct_priv->labels_mapping = mapping_create_for_id(mapping_id, MAPPING_TYPE_LABELS,
+ ct_priv->labels_mapping = mapping_create_for_id(mapping_id, id_len,
+ MAPPING_TYPE_LABELS,
sizeof(u32) * 4, 0, true);
if (IS_ERR(ct_priv->labels_mapping)) {
err = PTR_ERR(ct_priv->labels_mapping);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tir.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tir.c
index 19499072f67f..0b55e77f19c8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tir.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tir.c
@@ -146,6 +146,31 @@ void mlx5e_tir_builder_build_direct(struct mlx5e_tir_builder *builder)
MLX5_SET(tirc, tirc, rx_hash_fn, MLX5_RX_HASH_FN_INVERTED_XOR8);
}
+static void mlx5e_tir_context_self_lb_block(void *tirc, bool enable_uc_lb,
+ bool enable_mc_lb)
+{
+ u8 lb_flags = 0;
+
+ if (enable_uc_lb)
+ lb_flags = MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST;
+ if (enable_mc_lb)
+ lb_flags |= MLX5_TIRC_SELF_LB_BLOCK_BLOCK_MULTICAST;
+
+ MLX5_SET(tirc, tirc, self_lb_block, lb_flags);
+}
+
+void mlx5e_tir_builder_build_self_lb_block(struct mlx5e_tir_builder *builder,
+ bool enable_uc_lb,
+ bool enable_mc_lb)
+{
+ void *tirc = mlx5e_tir_builder_get_tirc(builder);
+
+ if (builder->modify)
+ MLX5_SET(modify_tir_in, builder->in, bitmask.self_lb_en, 1);
+
+ mlx5e_tir_context_self_lb_block(tirc, enable_uc_lb, enable_mc_lb);
+}
+
void mlx5e_tir_builder_build_tls(struct mlx5e_tir_builder *builder)
{
void *tirc = mlx5e_tir_builder_get_tirc(builder);
@@ -153,9 +178,7 @@ void mlx5e_tir_builder_build_tls(struct mlx5e_tir_builder *builder)
WARN_ON(builder->modify);
MLX5_SET(tirc, tirc, tls_en, 1);
- MLX5_SET(tirc, tirc, self_lb_block,
- MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST |
- MLX5_TIRC_SELF_LB_BLOCK_BLOCK_MULTICAST);
+ mlx5e_tir_context_self_lb_block(tirc, true, true);
}
int mlx5e_tir_init(struct mlx5e_tir *tir, struct mlx5e_tir_builder *builder,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tir.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tir.h
index e8df3aaf6562..958eeb959a19 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tir.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tir.h
@@ -35,6 +35,9 @@ void mlx5e_tir_builder_build_rss(struct mlx5e_tir_builder *builder,
const struct mlx5e_rss_params_traffic_type *rss_tt,
bool inner);
void mlx5e_tir_builder_build_direct(struct mlx5e_tir_builder *builder);
+void mlx5e_tir_builder_build_self_lb_block(struct mlx5e_tir_builder *builder,
+ bool enable_uc_lb,
+ bool enable_mc_lb);
void mlx5e_tir_builder_build_tls(struct mlx5e_tir_builder *builder);
struct mlx5_core_dev;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c
index 996fcdb5a29d..da8c44f46edb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.c
@@ -47,7 +47,7 @@ static void mlx5e_init_trap_rq(struct mlx5e_trap *t, struct mlx5e_params *params
rq->netdev = priv->netdev;
rq->priv = priv;
rq->clock = mdev->clock;
- rq->tstamp = &priv->tstamp;
+ rq->hwtstamp_config = &priv->hwtstamp_config;
rq->mdev = mdev;
rq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu);
rq->stats = &priv->trap_stats.rq;
@@ -144,7 +144,6 @@ static struct mlx5e_trap *mlx5e_open_trap(struct mlx5e_priv *priv)
t->priv = priv;
t->mdev = priv->mdev;
- t->tstamp = &priv->tstamp;
t->pdev = mlx5_core_dma_dev(priv->mdev);
t->netdev = priv->netdev;
t->mkey_be = cpu_to_be32(priv->mdev->mlx5e_res.hw_objs.mkey);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h
index aa3f17658c6d..394e917ea2b0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/trap.h
@@ -22,7 +22,6 @@ struct mlx5e_trap {
/* control */
struct mlx5e_priv *priv;
struct mlx5_core_dev *mdev;
- struct hwtstamp_config *tstamp;
DECLARE_BITMAP(state, MLX5E_CHANNEL_NUM_STATES);
struct mlx5e_params params;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
index 6760bb0336df..7e191e1569e8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/txrx.h
@@ -92,7 +92,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget);
void mlx5e_free_rx_descs(struct mlx5e_rq *rq);
void mlx5e_free_rx_missing_descs(struct mlx5e_rq *rq);
-static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config)
+static inline bool mlx5e_rx_hw_stamp(struct kernel_hwtstamp_config *config)
{
return config->rx_filter == HWTSTAMP_FILTER_ALL;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
index 5d51600935a6..80f9fc10877a 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c
@@ -179,7 +179,7 @@ static int mlx5e_xdp_rx_timestamp(const struct xdp_md *ctx, u64 *timestamp)
{
const struct mlx5e_xdp_buff *_ctx = (void *)ctx;
- if (unlikely(!mlx5e_rx_hw_stamp(_ctx->rq->tstamp)))
+ if (unlikely(!mlx5e_rx_hw_stamp(_ctx->rq->hwtstamp_config)))
return -ENODATA;
*timestamp = mlx5e_cqe_ts_to_ns(_ctx->rq->ptp_cyc2time,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c
index dbd88eb5c082..5981c71cae2d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/setup.c
@@ -71,7 +71,7 @@ static int mlx5e_init_xsk_rq(struct mlx5e_channel *c,
rq->pdev = c->pdev;
rq->netdev = c->netdev;
rq->priv = c->priv;
- rq->tstamp = c->tstamp;
+ rq->hwtstamp_config = &c->priv->hwtstamp_config;
rq->clock = mdev->clock;
rq->icosq = &c->icosq;
rq->ix = c->ix;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
index 0a4fb8c92268..35d9530037a6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.c
@@ -804,7 +804,8 @@ static int mlx5e_xfrm_add_state(struct net_device *dev,
goto err_xfrm;
}
- if (mlx5_eswitch_block_mode(priv->mdev))
+ err = mlx5_eswitch_block_mode(priv->mdev);
+ if (err)
goto unblock_ipsec;
if (x->props.mode == XFRM_MODE_TUNNEL &&
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 5d7c15abfcaf..f8eaaf37963b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
@@ -342,6 +342,7 @@ void mlx5e_ipsec_build_accel_xfrm_attrs(struct mlx5e_ipsec_sa_entry *sa_entry,
void mlx5e_ipsec_handle_mpv_event(int event, struct mlx5e_priv *slave_priv,
struct mlx5e_priv *master_priv);
void mlx5e_ipsec_send_event(struct mlx5e_priv *priv, int event);
+void mlx5e_ipsec_disable_events(struct mlx5e_priv *priv);
static inline struct mlx5_core_dev *
mlx5e_ipsec_sa2dev(struct mlx5e_ipsec_sa_entry *sa_entry)
@@ -387,6 +388,10 @@ static inline void mlx5e_ipsec_handle_mpv_event(int event, struct mlx5e_priv *sl
static inline void mlx5e_ipsec_send_event(struct mlx5e_priv *priv, int event)
{
}
+
+static inline void mlx5e_ipsec_disable_events(struct mlx5e_priv *priv)
+{
+}
#endif
#endif /* __MLX5E_IPSEC_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
index bf1d2769d4f1..feef86fff4bf 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_fs.c
@@ -2893,9 +2893,30 @@ void mlx5e_ipsec_handle_mpv_event(int event, struct mlx5e_priv *slave_priv,
void mlx5e_ipsec_send_event(struct mlx5e_priv *priv, int event)
{
- if (!priv->ipsec)
- return; /* IPsec not supported */
+ if (!priv->ipsec || mlx5_devcom_comp_get_size(priv->devcom) < 2)
+ return; /* IPsec not supported or no peers */
mlx5_devcom_send_event(priv->devcom, event, event, priv);
wait_for_completion(&priv->ipsec->comp);
}
+
+void mlx5e_ipsec_disable_events(struct mlx5e_priv *priv)
+{
+ struct mlx5_devcom_comp_dev *tmp = NULL;
+ struct mlx5e_priv *peer_priv;
+
+ if (!priv->devcom)
+ return;
+
+ if (!mlx5_devcom_for_each_peer_begin(priv->devcom))
+ goto out;
+
+ peer_priv = mlx5_devcom_get_next_peer_data(priv->devcom, &tmp);
+ if (peer_priv)
+ complete_all(&peer_priv->ipsec->comp);
+
+ mlx5_devcom_for_each_peer_end(priv->devcom);
+out:
+ mlx5_devcom_unregister_component(priv->devcom);
+ priv->devcom = NULL;
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
index d7a11ff9bbdb..da2d1eb52c13 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_rx.c
@@ -320,7 +320,6 @@ err_dma_unmap:
err_free:
kfree(buf);
err_out:
- priv_rx->rq_stats->tls_resync_req_skip++;
return err;
}
@@ -339,14 +338,19 @@ static void resync_handle_work(struct work_struct *work)
if (unlikely(test_bit(MLX5E_PRIV_RX_FLAG_DELETING, priv_rx->flags))) {
mlx5e_ktls_priv_rx_put(priv_rx);
+ priv_rx->rq_stats->tls_resync_req_skip++;
+ tls_offload_rx_resync_async_request_cancel(&resync->core);
return;
}
c = resync->priv->channels.c[priv_rx->rxq];
sq = &c->async_icosq;
- if (resync_post_get_progress_params(sq, priv_rx))
+ if (resync_post_get_progress_params(sq, priv_rx)) {
+ priv_rx->rq_stats->tls_resync_req_skip++;
+ tls_offload_rx_resync_async_request_cancel(&resync->core);
mlx5e_ktls_priv_rx_put(priv_rx);
+ }
}
static void resync_init(struct mlx5e_ktls_rx_resync_ctx *resync,
@@ -425,14 +429,21 @@ void mlx5e_ktls_handle_get_psv_completion(struct mlx5e_icosq_wqe_info *wi,
{
struct mlx5e_ktls_rx_resync_buf *buf = wi->tls_get_params.buf;
struct mlx5e_ktls_offload_context_rx *priv_rx;
+ struct tls_offload_resync_async *async_resync;
+ struct tls_offload_context_rx *rx_ctx;
u8 tracker_state, auth_state, *ctx;
struct device *dev;
u32 hw_seq;
priv_rx = buf->priv_rx;
dev = mlx5_core_dma_dev(sq->channel->mdev);
- if (unlikely(test_bit(MLX5E_PRIV_RX_FLAG_DELETING, priv_rx->flags)))
+ rx_ctx = tls_offload_ctx_rx(tls_get_ctx(priv_rx->sk));
+ async_resync = rx_ctx->resync_async;
+ if (unlikely(test_bit(MLX5E_PRIV_RX_FLAG_DELETING, priv_rx->flags))) {
+ priv_rx->rq_stats->tls_resync_req_skip++;
+ tls_offload_rx_resync_async_request_cancel(async_resync);
goto out;
+ }
dma_sync_single_for_cpu(dev, buf->dma_addr, PROGRESS_PARAMS_PADDED_SIZE,
DMA_FROM_DEVICE);
@@ -443,11 +454,13 @@ void mlx5e_ktls_handle_get_psv_completion(struct mlx5e_icosq_wqe_info *wi,
if (tracker_state != MLX5E_TLS_PROGRESS_PARAMS_RECORD_TRACKER_STATE_TRACKING ||
auth_state != MLX5E_TLS_PROGRESS_PARAMS_AUTH_STATE_NO_OFFLOAD) {
priv_rx->rq_stats->tls_resync_req_skip++;
+ tls_offload_rx_resync_async_request_cancel(async_resync);
goto out;
}
hw_seq = MLX5_GET(tls_progress_params, ctx, hw_resync_tcp_sn);
- tls_offload_rx_resync_async_request_end(priv_rx->sk, cpu_to_be32(hw_seq));
+ tls_offload_rx_resync_async_request_end(async_resync,
+ cpu_to_be32(hw_seq));
priv_rx->rq_stats->tls_resync_req_end++;
out:
mlx5e_ktls_priv_rx_put(priv_rx);
@@ -472,8 +485,10 @@ static bool resync_queue_get_psv(struct sock *sk)
resync = &priv_rx->resync;
mlx5e_ktls_priv_rx_get(priv_rx);
- if (unlikely(!queue_work(resync->priv->tls->rx_wq, &resync->work)))
+ if (unlikely(!queue_work(resync->priv->tls->rx_wq, &resync->work))) {
mlx5e_ktls_priv_rx_put(priv_rx);
+ return false;
+ }
return true;
}
@@ -482,6 +497,7 @@ static bool resync_queue_get_psv(struct sock *sk)
static void resync_update_sn(struct mlx5e_rq *rq, struct sk_buff *skb)
{
struct ethhdr *eth = (struct ethhdr *)(skb->data);
+ struct tls_offload_resync_async *resync_async;
struct net_device *netdev = rq->netdev;
struct net *net = dev_net(netdev);
struct sock *sk = NULL;
@@ -527,7 +543,8 @@ static void resync_update_sn(struct mlx5e_rq *rq, struct sk_buff *skb)
seq = th->seq;
datalen = skb->len - depth;
- tls_offload_rx_resync_async_request_start(sk, seq, datalen);
+ resync_async = tls_offload_ctx_rx(tls_get_ctx(sk))->resync_async;
+ tls_offload_rx_resync_async_request_start(resync_async, seq, datalen);
rq->stats->tls_resync_req_start++;
unref:
@@ -556,6 +573,18 @@ void mlx5e_ktls_rx_resync(struct net_device *netdev, struct sock *sk,
resync_handle_seq_match(priv_rx, c);
}
+void
+mlx5e_ktls_rx_resync_async_request_cancel(struct mlx5e_icosq_wqe_info *wi)
+{
+ struct mlx5e_ktls_offload_context_rx *priv_rx;
+ struct mlx5e_ktls_rx_resync_buf *buf;
+
+ buf = wi->tls_get_params.buf;
+ priv_rx = buf->priv_rx;
+ priv_rx->rq_stats->tls_resync_req_skip++;
+ tls_offload_rx_resync_async_request_cancel(&priv_rx->resync.core);
+}
+
/* End of resync section */
void mlx5e_ktls_handle_rx_skb(struct mlx5e_rq *rq, struct sk_buff *skb,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h
index f87b65c560ea..cb08799769ee 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ktls_txrx.h
@@ -29,6 +29,10 @@ void mlx5e_ktls_handle_get_psv_completion(struct mlx5e_icosq_wqe_info *wi,
void mlx5e_ktls_tx_handle_resync_dump_comp(struct mlx5e_txqsq *sq,
struct mlx5e_tx_wqe_info *wi,
u32 *dma_fifo_cc);
+
+void
+mlx5e_ktls_rx_resync_async_request_cancel(struct mlx5e_icosq_wqe_info *wi);
+
static inline bool
mlx5e_ktls_tx_try_handle_resync_dump_comp(struct mlx5e_txqsq *sq,
struct mlx5e_tx_wqe_info *wi,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
index 8565cfe8d7dc..38e7c77cc851 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
@@ -28,12 +28,15 @@ struct mlx5e_psp_tx {
struct mlx5_flow_handle *rule;
struct mutex mutex; /* Protect PSP TX steering */
u32 refcnt;
+ struct mlx5_fc *tx_counter;
};
struct mlx5e_psp_rx_err {
struct mlx5_flow_table *ft;
struct mlx5_flow_handle *rule;
- struct mlx5_flow_handle *drop_rule;
+ struct mlx5_flow_handle *auth_fail_rule;
+ struct mlx5_flow_handle *err_rule;
+ struct mlx5_flow_handle *bad_rule;
struct mlx5_modify_hdr *copy_modify_hdr;
};
@@ -50,6 +53,10 @@ struct mlx5e_accel_fs_psp_prot {
struct mlx5e_accel_fs_psp {
struct mlx5e_accel_fs_psp_prot fs_prot[ACCEL_FS_PSP_NUM_TYPES];
+ struct mlx5_fc *rx_counter;
+ struct mlx5_fc *rx_auth_fail_counter;
+ struct mlx5_fc *rx_err_counter;
+ struct mlx5_fc *rx_bad_counter;
};
struct mlx5e_psp_fs {
@@ -72,9 +79,19 @@ static enum mlx5_traffic_types fs_psp2tt(enum accel_fs_psp_type i)
static void accel_psp_fs_rx_err_del_rules(struct mlx5e_psp_fs *fs,
struct mlx5e_psp_rx_err *rx_err)
{
- if (rx_err->drop_rule) {
- mlx5_del_flow_rules(rx_err->drop_rule);
- rx_err->drop_rule = NULL;
+ if (rx_err->bad_rule) {
+ mlx5_del_flow_rules(rx_err->bad_rule);
+ rx_err->bad_rule = NULL;
+ }
+
+ if (rx_err->err_rule) {
+ mlx5_del_flow_rules(rx_err->err_rule);
+ rx_err->err_rule = NULL;
+ }
+
+ if (rx_err->auth_fail_rule) {
+ mlx5_del_flow_rules(rx_err->auth_fail_rule);
+ rx_err->auth_fail_rule = NULL;
}
if (rx_err->rule) {
@@ -117,6 +134,7 @@ static int accel_psp_fs_rx_err_add_rule(struct mlx5e_psp_fs *fs,
{
u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
struct mlx5_core_dev *mdev = fs->mdev;
+ struct mlx5_flow_destination dest[2];
struct mlx5_flow_act flow_act = {};
struct mlx5_modify_hdr *modify_hdr;
struct mlx5_flow_handle *fte;
@@ -147,10 +165,14 @@ static int accel_psp_fs_rx_err_add_rule(struct mlx5e_psp_fs *fs,
accel_psp_setup_syndrome_match(spec, PSP_OK);
/* create fte */
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR |
- MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
+ MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
flow_act.modify_hdr = modify_hdr;
- fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act,
- &fs_prot->default_dest, 1);
+ dest[0].type = fs_prot->default_dest.type;
+ dest[0].ft = fs_prot->default_dest.ft;
+ dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[1].counter = fs->rx_fs->rx_counter;
+ fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 2);
if (IS_ERR(fte)) {
err = PTR_ERR(fte);
mlx5_core_err(mdev, "fail to add psp rx err copy rule err=%d\n", err);
@@ -158,22 +180,69 @@ static int accel_psp_fs_rx_err_add_rule(struct mlx5e_psp_fs *fs,
}
rx_err->rule = fte;
- /* add default drop rule */
+ /* add auth fail drop rule */
memset(spec, 0, sizeof(*spec));
memset(&flow_act, 0, sizeof(flow_act));
+ accel_psp_setup_syndrome_match(spec, PSP_ICV_FAIL);
/* create fte */
- flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
- fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, NULL, 0);
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[0].counter = fs->rx_fs->rx_auth_fail_counter;
+ fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 1);
if (IS_ERR(fte)) {
err = PTR_ERR(fte);
- mlx5_core_err(mdev, "fail to add psp rx err drop rule err=%d\n", err);
+ mlx5_core_err(mdev, "fail to add psp rx auth fail drop rule err=%d\n",
+ err);
goto out_drop_rule;
}
- rx_err->drop_rule = fte;
+ rx_err->auth_fail_rule = fte;
+
+ /* add framing drop rule */
+ memset(spec, 0, sizeof(*spec));
+ memset(&flow_act, 0, sizeof(flow_act));
+ accel_psp_setup_syndrome_match(spec, PSP_BAD_TRAILER);
+ /* create fte */
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[0].counter = fs->rx_fs->rx_err_counter;
+ fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 1);
+ if (IS_ERR(fte)) {
+ err = PTR_ERR(fte);
+ mlx5_core_err(mdev, "fail to add psp rx framing err drop rule err=%d\n",
+ err);
+ goto out_drop_auth_fail_rule;
+ }
+ rx_err->err_rule = fte;
+
+ /* add misc. errors drop rule */
+ memset(spec, 0, sizeof(*spec));
+ memset(&flow_act, 0, sizeof(flow_act));
+ /* create fte */
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest[0].counter = fs->rx_fs->rx_bad_counter;
+ fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 1);
+ if (IS_ERR(fte)) {
+ err = PTR_ERR(fte);
+ mlx5_core_err(mdev, "fail to add psp rx misc. err drop rule err=%d\n",
+ err);
+ goto out_drop_error_rule;
+ }
+ rx_err->bad_rule = fte;
+
rx_err->copy_modify_hdr = modify_hdr;
goto out_spec;
+out_drop_error_rule:
+ mlx5_del_flow_rules(rx_err->err_rule);
+ rx_err->err_rule = NULL;
+out_drop_auth_fail_rule:
+ mlx5_del_flow_rules(rx_err->auth_fail_rule);
+ rx_err->auth_fail_rule = NULL;
out_drop_rule:
mlx5_del_flow_rules(rx_err->rule);
rx_err->rule = NULL;
@@ -461,6 +530,10 @@ static void accel_psp_fs_cleanup_rx(struct mlx5e_psp_fs *fs)
return;
accel_psp = fs->rx_fs;
+ mlx5_fc_destroy(fs->mdev, accel_psp->rx_bad_counter);
+ mlx5_fc_destroy(fs->mdev, accel_psp->rx_err_counter);
+ mlx5_fc_destroy(fs->mdev, accel_psp->rx_auth_fail_counter);
+ mlx5_fc_destroy(fs->mdev, accel_psp->rx_counter);
for (i = 0; i < ACCEL_FS_PSP_NUM_TYPES; i++) {
fs_prot = &accel_psp->fs_prot[i];
mutex_destroy(&fs_prot->prot_mutex);
@@ -474,7 +547,10 @@ static int accel_psp_fs_init_rx(struct mlx5e_psp_fs *fs)
{
struct mlx5e_accel_fs_psp_prot *fs_prot;
struct mlx5e_accel_fs_psp *accel_psp;
+ struct mlx5_core_dev *mdev = fs->mdev;
+ struct mlx5_fc *flow_counter;
enum accel_fs_psp_type i;
+ int err;
accel_psp = kzalloc(sizeof(*accel_psp), GFP_KERNEL);
if (!accel_psp)
@@ -485,9 +561,68 @@ static int accel_psp_fs_init_rx(struct mlx5e_psp_fs *fs)
mutex_init(&fs_prot->prot_mutex);
}
+ flow_counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(flow_counter)) {
+ mlx5_core_warn(mdev,
+ "fail to create psp rx flow counter err=%pe\n",
+ flow_counter);
+ err = PTR_ERR(flow_counter);
+ goto out_err;
+ }
+ accel_psp->rx_counter = flow_counter;
+
+ flow_counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(flow_counter)) {
+ mlx5_core_warn(mdev,
+ "fail to create psp rx auth fail flow counter err=%pe\n",
+ flow_counter);
+ err = PTR_ERR(flow_counter);
+ goto out_counter_err;
+ }
+ accel_psp->rx_auth_fail_counter = flow_counter;
+
+ flow_counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(flow_counter)) {
+ mlx5_core_warn(mdev,
+ "fail to create psp rx error flow counter err=%pe\n",
+ flow_counter);
+ err = PTR_ERR(flow_counter);
+ goto out_auth_fail_counter_err;
+ }
+ accel_psp->rx_err_counter = flow_counter;
+
+ flow_counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(flow_counter)) {
+ mlx5_core_warn(mdev,
+ "fail to create psp rx bad flow counter err=%pe\n",
+ flow_counter);
+ err = PTR_ERR(flow_counter);
+ goto out_err_counter_err;
+ }
+ accel_psp->rx_bad_counter = flow_counter;
+
fs->rx_fs = accel_psp;
return 0;
+
+out_err_counter_err:
+ mlx5_fc_destroy(mdev, accel_psp->rx_err_counter);
+ accel_psp->rx_err_counter = NULL;
+out_auth_fail_counter_err:
+ mlx5_fc_destroy(mdev, accel_psp->rx_auth_fail_counter);
+ accel_psp->rx_auth_fail_counter = NULL;
+out_counter_err:
+ mlx5_fc_destroy(mdev, accel_psp->rx_counter);
+ accel_psp->rx_counter = NULL;
+out_err:
+ for (i = 0; i < ACCEL_FS_PSP_NUM_TYPES; i++) {
+ fs_prot = &accel_psp->fs_prot[i];
+ mutex_destroy(&fs_prot->prot_mutex);
+ }
+ kfree(accel_psp);
+ fs->rx_fs = NULL;
+
+ return err;
}
void mlx5_accel_psp_fs_cleanup_rx_tables(struct mlx5e_priv *priv)
@@ -532,6 +667,7 @@ static int accel_psp_fs_tx_create_ft_table(struct mlx5e_psp_fs *fs)
{
int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
struct mlx5_flow_table_attr ft_attr = {};
+ struct mlx5_flow_destination dest = {};
struct mlx5_core_dev *mdev = fs->mdev;
struct mlx5_flow_act flow_act = {};
u32 *in, *mc, *outer_headers_c;
@@ -580,8 +716,11 @@ static int accel_psp_fs_tx_create_ft_table(struct mlx5e_psp_fs *fs)
flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP;
flow_act.flags |= FLOW_ACT_NO_APPEND;
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW |
- MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT;
- rule = mlx5_add_flow_rules(ft, spec, &flow_act, NULL, 0);
+ MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT |
+ MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ dest.counter = tx_fs->tx_counter;
+ rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1);
if (IS_ERR(rule)) {
err = PTR_ERR(rule);
mlx5_core_err(mdev, "PSP: fail to add psp tx flow rule, err = %d\n", err);
@@ -650,6 +789,7 @@ static void accel_psp_fs_cleanup_tx(struct mlx5e_psp_fs *fs)
if (!tx_fs)
return;
+ mlx5_fc_destroy(fs->mdev, tx_fs->tx_counter);
mutex_destroy(&tx_fs->mutex);
WARN_ON(tx_fs->refcnt);
kfree(tx_fs);
@@ -658,10 +798,12 @@ static void accel_psp_fs_cleanup_tx(struct mlx5e_psp_fs *fs)
static int accel_psp_fs_init_tx(struct mlx5e_psp_fs *fs)
{
+ struct mlx5_core_dev *mdev = fs->mdev;
struct mlx5_flow_namespace *ns;
+ struct mlx5_fc *flow_counter;
struct mlx5e_psp_tx *tx_fs;
- ns = mlx5_get_flow_namespace(fs->mdev, MLX5_FLOW_NAMESPACE_EGRESS_IPSEC);
+ ns = mlx5_get_flow_namespace(mdev, MLX5_FLOW_NAMESPACE_EGRESS_IPSEC);
if (!ns)
return -EOPNOTSUPP;
@@ -669,12 +811,55 @@ static int accel_psp_fs_init_tx(struct mlx5e_psp_fs *fs)
if (!tx_fs)
return -ENOMEM;
+ flow_counter = mlx5_fc_create(mdev, false);
+ if (IS_ERR(flow_counter)) {
+ mlx5_core_warn(mdev,
+ "fail to create psp tx flow counter err=%pe\n",
+ flow_counter);
+ kfree(tx_fs);
+ return PTR_ERR(flow_counter);
+ }
+ tx_fs->tx_counter = flow_counter;
mutex_init(&tx_fs->mutex);
tx_fs->ns = ns;
fs->tx_fs = tx_fs;
return 0;
}
+static void
+mlx5e_accel_psp_fs_get_stats_fill(struct mlx5e_priv *priv,
+ struct mlx5e_psp_stats *stats)
+{
+ struct mlx5e_psp_tx *tx_fs = priv->psp->fs->tx_fs;
+ struct mlx5_core_dev *mdev = priv->mdev;
+ struct mlx5e_accel_fs_psp *accel_psp;
+
+ accel_psp = (struct mlx5e_accel_fs_psp *)priv->psp->fs->rx_fs;
+
+ if (tx_fs->tx_counter)
+ mlx5_fc_query(mdev, tx_fs->tx_counter, &stats->psp_tx_pkts,
+ &stats->psp_tx_bytes);
+
+ if (accel_psp->rx_counter)
+ mlx5_fc_query(mdev, accel_psp->rx_counter, &stats->psp_rx_pkts,
+ &stats->psp_rx_bytes);
+
+ if (accel_psp->rx_auth_fail_counter)
+ mlx5_fc_query(mdev, accel_psp->rx_auth_fail_counter,
+ &stats->psp_rx_pkts_auth_fail,
+ &stats->psp_rx_bytes_auth_fail);
+
+ if (accel_psp->rx_err_counter)
+ mlx5_fc_query(mdev, accel_psp->rx_err_counter,
+ &stats->psp_rx_pkts_frame_err,
+ &stats->psp_rx_bytes_frame_err);
+
+ if (accel_psp->rx_bad_counter)
+ mlx5_fc_query(mdev, accel_psp->rx_bad_counter,
+ &stats->psp_rx_pkts_drop,
+ &stats->psp_rx_bytes_drop);
+}
+
void mlx5_accel_psp_fs_cleanup_tx_tables(struct mlx5e_priv *priv)
{
if (!priv->psp)
@@ -849,12 +1034,30 @@ mlx5e_psp_key_rotate(struct psp_dev *psd, struct netlink_ext_ack *exack)
return mlx5e_psp_rotate_key(priv->mdev);
}
+static void
+mlx5e_psp_get_stats(struct psp_dev *psd, struct psp_dev_stats *stats)
+{
+ struct mlx5e_priv *priv = netdev_priv(psd->main_netdev);
+ struct mlx5e_psp_stats nstats;
+
+ mlx5e_accel_psp_fs_get_stats_fill(priv, &nstats);
+ stats->rx_packets = nstats.psp_rx_pkts;
+ stats->rx_bytes = nstats.psp_rx_bytes;
+ stats->rx_auth_fail = nstats.psp_rx_pkts_auth_fail;
+ stats->rx_error = nstats.psp_rx_pkts_frame_err;
+ stats->rx_bad = nstats.psp_rx_pkts_drop;
+ stats->tx_packets = nstats.psp_tx_pkts;
+ stats->tx_bytes = nstats.psp_tx_bytes;
+ stats->tx_error = atomic_read(&priv->psp->tx_drop);
+}
+
static struct psp_dev_ops mlx5_psp_ops = {
.set_config = mlx5e_psp_set_config,
.rx_spi_alloc = mlx5e_psp_rx_spi_alloc,
.tx_key_add = mlx5e_psp_assoc_add,
.tx_key_del = mlx5e_psp_assoc_del,
.key_rotate = mlx5e_psp_key_rotate,
+ .get_stats = mlx5e_psp_get_stats,
};
void mlx5e_psp_unregister(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h
index 42bb671fb2cb..6b62fef0d9a7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.h
@@ -7,11 +7,27 @@
#include <net/psp/types.h>
#include "en.h"
+struct mlx5e_psp_stats {
+ u64 psp_rx_pkts;
+ u64 psp_rx_bytes;
+ u64 psp_rx_pkts_auth_fail;
+ u64 psp_rx_bytes_auth_fail;
+ u64 psp_rx_pkts_frame_err;
+ u64 psp_rx_bytes_frame_err;
+ u64 psp_rx_pkts_drop;
+ u64 psp_rx_bytes_drop;
+ u64 psp_tx_pkts;
+ u64 psp_tx_bytes;
+ u64 psp_tx_pkts_drop;
+ u64 psp_tx_bytes_drop;
+};
+
struct mlx5e_psp {
struct psp_dev *psp;
struct psp_dev_caps caps;
struct mlx5e_psp_fs *fs;
atomic_t tx_key_cnt;
+ atomic_t tx_drop;
};
static inline bool mlx5_is_psp_device(struct mlx5_core_dev *mdev)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
index 828bff1137af..c17ea0fcd8ef 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp_rxtx.c
@@ -186,6 +186,7 @@ bool mlx5e_psp_handle_tx_skb(struct net_device *netdev,
/* psp_encap of the packet */
if (!psp_dev_encapsulate(net, skb, psp_st->spi, psp_st->ver, 0)) {
kfree_skb_reason(skb, SKB_DROP_REASON_PSP_OUTPUT);
+ atomic_inc(&priv->psp->tx_drop);
return false;
}
if (skb_is_gso(skb)) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c
index 30424ccad584..5a2ac7b6f260 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_common.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_common.c
@@ -247,45 +247,43 @@ void mlx5e_destroy_mdev_resources(struct mlx5_core_dev *mdev)
memset(res, 0, sizeof(*res));
}
-int mlx5e_refresh_tirs(struct mlx5e_priv *priv, bool enable_uc_lb,
- bool enable_mc_lb)
+int mlx5e_modify_tirs_lb(struct mlx5_core_dev *mdev, bool enable_uc_lb,
+ bool enable_mc_lb)
{
- struct mlx5_core_dev *mdev = priv->mdev;
+ struct mlx5e_tir_builder *builder;
struct mlx5e_tir *tir;
- u8 lb_flags = 0;
- int err = 0;
- u32 tirn = 0;
- int inlen;
- void *in;
+ int err = 0;
- inlen = MLX5_ST_SZ_BYTES(modify_tir_in);
- in = kvzalloc(inlen, GFP_KERNEL);
- if (!in)
+ builder = mlx5e_tir_builder_alloc(true);
+ if (!builder)
return -ENOMEM;
- if (enable_uc_lb)
- lb_flags = MLX5_TIRC_SELF_LB_BLOCK_BLOCK_UNICAST;
-
- if (enable_mc_lb)
- lb_flags |= MLX5_TIRC_SELF_LB_BLOCK_BLOCK_MULTICAST;
-
- if (lb_flags)
- MLX5_SET(modify_tir_in, in, ctx.self_lb_block, lb_flags);
-
- MLX5_SET(modify_tir_in, in, bitmask.self_lb_en, 1);
+ mlx5e_tir_builder_build_self_lb_block(builder, enable_uc_lb,
+ enable_mc_lb);
mutex_lock(&mdev->mlx5e_res.hw_objs.td.list_lock);
list_for_each_entry(tir, &mdev->mlx5e_res.hw_objs.td.tirs_list, list) {
- tirn = tir->tirn;
- err = mlx5_core_modify_tir(mdev, tirn, in);
- if (err)
+ err = mlx5e_tir_modify(tir, builder);
+ if (err) {
+ mlx5_core_err(mdev,
+ "modify tir(0x%x) enable_lb uc(%d) mc(%d) failed, %d\n",
+ mlx5e_tir_get_tirn(tir),
+ enable_uc_lb, enable_mc_lb, err);
break;
+ }
}
mutex_unlock(&mdev->mlx5e_res.hw_objs.td.list_lock);
- kvfree(in);
- if (err)
- netdev_err(priv->netdev, "refresh tir(0x%x) failed, %d\n", tirn, err);
+ mlx5e_tir_builder_free(builder);
return err;
}
+
+int mlx5e_refresh_tirs(struct mlx5_core_dev *mdev, bool enable_uc_lb,
+ bool enable_mc_lb)
+{
+ if (MLX5_CAP_GEN(mdev, tis_tir_td_order))
+ return 0; /* refresh not needed */
+
+ return mlx5e_modify_tirs_lb(mdev, enable_uc_lb, enable_mc_lb);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
index d166c0d5189e..fddf7c207f8e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_dcbnl.c
@@ -31,14 +31,15 @@
*/
#include <linux/device.h>
#include <linux/netdevice.h>
+#include <linux/units.h>
#include "en.h"
#include "en/port.h"
#include "en/port_buffer.h"
#define MLX5E_MAX_BW_ALLOC 100 /* Max percentage of BW allocation */
-#define MLX5E_100MB (100000)
-#define MLX5E_1GB (1000000)
+#define MLX5E_100MB_TO_KB (100 * MEGA / KILO)
+#define MLX5E_1GB_TO_KB (GIGA / KILO)
#define MLX5E_CEE_STATE_UP 1
#define MLX5E_CEE_STATE_DOWN 0
@@ -572,10 +573,10 @@ static int mlx5e_dcbnl_ieee_getmaxrate(struct net_device *netdev,
for (i = 0; i <= mlx5_max_tc(mdev); i++) {
switch (max_bw_unit[i]) {
case MLX5_100_MBPS_UNIT:
- maxrate->tc_maxrate[i] = max_bw_value[i] * MLX5E_100MB;
+ maxrate->tc_maxrate[i] = max_bw_value[i] * MLX5E_100MB_TO_KB;
break;
case MLX5_GBPS_UNIT:
- maxrate->tc_maxrate[i] = max_bw_value[i] * MLX5E_1GB;
+ maxrate->tc_maxrate[i] = max_bw_value[i] * MLX5E_1GB_TO_KB;
break;
case MLX5_BW_NO_LIMIT:
break;
@@ -595,32 +596,55 @@ static int mlx5e_dcbnl_ieee_setmaxrate(struct net_device *netdev,
struct mlx5_core_dev *mdev = priv->mdev;
u8 max_bw_value[IEEE_8021QAZ_MAX_TCS];
u8 max_bw_unit[IEEE_8021QAZ_MAX_TCS];
- __u64 upper_limit_mbps = roundup(255 * MLX5E_100MB, MLX5E_1GB);
+ u64 upper_limit_100mbps;
+ u64 upper_limit_gbps;
int i;
+ struct {
+ int scale;
+ const char *units_str;
+ } units[] = {
+ [MLX5_100_MBPS_UNIT] = {
+ .scale = 100,
+ .units_str = "Mbps",
+ },
+ [MLX5_GBPS_UNIT] = {
+ .scale = 1,
+ .units_str = "Gbps",
+ },
+ };
memset(max_bw_value, 0, sizeof(max_bw_value));
memset(max_bw_unit, 0, sizeof(max_bw_unit));
+ upper_limit_100mbps = U8_MAX * MLX5E_100MB_TO_KB;
+ upper_limit_gbps = U8_MAX * MLX5E_1GB_TO_KB;
for (i = 0; i <= mlx5_max_tc(mdev); i++) {
if (!maxrate->tc_maxrate[i]) {
max_bw_unit[i] = MLX5_BW_NO_LIMIT;
continue;
}
- if (maxrate->tc_maxrate[i] < upper_limit_mbps) {
+ if (maxrate->tc_maxrate[i] <= upper_limit_100mbps) {
max_bw_value[i] = div_u64(maxrate->tc_maxrate[i],
- MLX5E_100MB);
+ MLX5E_100MB_TO_KB);
max_bw_value[i] = max_bw_value[i] ? max_bw_value[i] : 1;
max_bw_unit[i] = MLX5_100_MBPS_UNIT;
- } else {
+ } else if (maxrate->tc_maxrate[i] <= upper_limit_gbps) {
max_bw_value[i] = div_u64(maxrate->tc_maxrate[i],
- MLX5E_1GB);
+ MLX5E_1GB_TO_KB);
max_bw_unit[i] = MLX5_GBPS_UNIT;
+ } else {
+ netdev_err(netdev,
+ "tc_%d maxrate %llu Kbps exceeds limit %llu\n",
+ i, maxrate->tc_maxrate[i],
+ upper_limit_gbps);
+ return -EINVAL;
}
}
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
- netdev_dbg(netdev, "%s: tc_%d <=> max_bw %d Gbps\n",
- __func__, i, max_bw_value[i]);
+ netdev_dbg(netdev, "%s: tc_%d <=> max_bw %u %s\n", __func__, i,
+ max_bw_value[i] * units[max_bw_unit[i]].scale,
+ units[max_bw_unit[i]].units_str);
}
return mlx5_modify_port_ets_rate_limit(mdev, max_bw_value, max_bw_unit);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
index 53e5ae252eac..d3fef1e7e2f7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
@@ -261,6 +261,11 @@ void mlx5e_build_ptys2ethtool_map(void)
ETHTOOL_LINK_MODE_800000baseDR4_2_Full_BIT,
ETHTOOL_LINK_MODE_800000baseSR4_Full_BIT,
ETHTOOL_LINK_MODE_800000baseVR4_Full_BIT);
+ MLX5_BUILD_PTYS2ETHTOOL_CONFIG(MLX5E_1600TAUI_8_1600TBASE_CR8_KR8, ext,
+ ETHTOOL_LINK_MODE_1600000baseCR8_Full_BIT,
+ ETHTOOL_LINK_MODE_1600000baseKR8_Full_BIT,
+ ETHTOOL_LINK_MODE_1600000baseDR8_Full_BIT,
+ ETHTOOL_LINK_MODE_1600000baseDR8_2_Full_BIT);
}
static void mlx5e_ethtool_get_speed_arr(bool ext,
@@ -2027,7 +2032,7 @@ static int mlx5e_get_module_info(struct net_device *netdev,
int size_read = 0;
u8 data[4] = {0};
- size_read = mlx5_query_module_eeprom(dev, 0, 2, data);
+ size_read = mlx5_query_module_eeprom(dev, 0, 2, data, NULL);
if (size_read < 2)
return -EIO;
@@ -2069,6 +2074,7 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev,
struct mlx5_core_dev *mdev = priv->mdev;
int offset = ee->offset;
int size_read;
+ u8 status = 0;
int i = 0;
if (!ee->len)
@@ -2078,15 +2084,15 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev,
while (i < ee->len) {
size_read = mlx5_query_module_eeprom(mdev, offset, ee->len - i,
- data + i);
-
+ data + i, &status);
if (!size_read)
/* Done reading */
return 0;
if (size_read < 0) {
- netdev_err(priv->netdev, "%s: mlx5_query_eeprom failed:0x%x\n",
- __func__, size_read);
+ netdev_err(netdev,
+ "%s: mlx5_query_eeprom failed:0x%x, status %u\n",
+ __func__, size_read, status);
return size_read;
}
@@ -2106,6 +2112,7 @@ static int mlx5e_get_module_eeprom_by_page(struct net_device *netdev,
struct mlx5_core_dev *mdev = priv->mdev;
u8 *data = page_data->data;
int size_read;
+ u8 status = 0;
int i = 0;
if (!page_data->length)
@@ -2119,20 +2126,19 @@ static int mlx5e_get_module_eeprom_by_page(struct net_device *netdev,
query.page = page_data->page;
while (i < page_data->length) {
query.size = page_data->length - i;
- size_read = mlx5_query_module_eeprom_by_page(mdev, &query, data + i);
+ size_read = mlx5_query_module_eeprom_by_page(mdev, &query,
+ data + i, &status);
/* Done reading, return how many bytes was read */
if (!size_read)
return i;
- if (size_read == -EINVAL)
- return -EINVAL;
if (size_read < 0) {
NL_SET_ERR_MSG_FMT_MOD(
extack,
- "Query module eeprom by page failed, read %u bytes, err %d",
- i, size_read);
- return i;
+ "Query module eeprom by page failed, read %u bytes, err %d, status %u",
+ i, size_read, status);
+ return size_read;
}
i += size_read;
@@ -2273,7 +2279,7 @@ static int set_pflag_rx_cqe_compress(struct net_device *netdev,
if (!MLX5_CAP_GEN(mdev, cqe_compression))
return -EOPNOTSUPP;
- rx_filter = priv->tstamp.rx_filter != HWTSTAMP_FILTER_NONE;
+ rx_filter = priv->hwtstamp_config.rx_filter != HWTSTAMP_FILTER_NONE;
err = mlx5e_modify_rx_cqe_compression_locked(priv, enable, rx_filter);
if (err)
return err;
@@ -2288,7 +2294,6 @@ static int set_pflag_rx_striding_rq(struct net_device *netdev, bool enable)
struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5_core_dev *mdev = priv->mdev;
struct mlx5e_params new_params;
- int err;
if (enable) {
/* Checking the regular RQ here; mlx5e_validate_xsk_param called
@@ -2309,14 +2314,7 @@ static int set_pflag_rx_striding_rq(struct net_device *netdev, bool enable)
MLX5E_SET_PFLAG(&new_params, MLX5E_PFLAG_RX_STRIDING_RQ, enable);
mlx5e_set_rq_type(mdev, &new_params);
- err = mlx5e_safe_switch_params(priv, &new_params, NULL, NULL, true);
- if (err)
- return err;
-
- /* update XDP supported features */
- mlx5e_set_xdp_feature(netdev);
-
- return 0;
+ return mlx5e_safe_switch_params(priv, &new_params, NULL, NULL, true);
}
static int set_pflag_rx_no_csum_complete(struct net_device *netdev, bool enable)
@@ -2494,21 +2492,18 @@ static int mlx5e_set_rxfh_fields(struct net_device *dev,
return mlx5e_ethtool_set_rxfh_fields(priv, cmd, extack);
}
+static u32 mlx5e_get_rx_ring_count(struct net_device *dev)
+{
+ struct mlx5e_priv *priv = netdev_priv(dev);
+
+ return priv->channels.params.num_channels;
+}
+
static int mlx5e_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
u32 *rule_locs)
{
struct mlx5e_priv *priv = netdev_priv(dev);
- /* ETHTOOL_GRXRINGS is needed by ethtool -x which is not part
- * of rxnfc. We keep this logic out of mlx5e_ethtool_get_rxnfc,
- * to avoid breaking "ethtool -x" when mlx5e_ethtool_get_rxnfc
- * is compiled out via CONFIG_MLX5_EN_RXNFC=n.
- */
- if (info->cmd == ETHTOOL_GRXRINGS) {
- info->data = priv->channels.params.num_channels;
- return 0;
- }
-
return mlx5e_ethtool_get_rxnfc(priv, info, rule_locs);
}
@@ -2768,6 +2763,7 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
.remove_rxfh_context = mlx5e_remove_rxfh_context,
.get_rxnfc = mlx5e_get_rxnfc,
.set_rxnfc = mlx5e_set_rxnfc,
+ .get_rx_ring_count = mlx5e_get_rx_ring_count,
.get_tunable = mlx5e_get_tunable,
.set_tunable = mlx5e_set_tunable,
.get_pause_stats = mlx5e_get_pause_stats,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
index 79916f1abd14..63bdef5b4ba5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
@@ -704,7 +704,7 @@ static int validate_flow(struct mlx5e_priv *priv,
num_tuples += ret;
break;
default:
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
}
if ((fs->flow_type & FLOW_EXT)) {
ret = validate_vlan(fs);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index a56825921c23..6168f0814414 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -242,8 +242,8 @@ static int mlx5e_devcom_init_mpv(struct mlx5e_priv *priv, u64 *data)
&attr,
mlx5e_devcom_event_mpv,
priv);
- if (IS_ERR(priv->devcom))
- return PTR_ERR(priv->devcom);
+ if (!priv->devcom)
+ return -EINVAL;
if (mlx5_core_is_mp_master(priv->mdev)) {
mlx5_devcom_send_event(priv->devcom, MPV_DEVCOM_MASTER_UP,
@@ -256,7 +256,7 @@ static int mlx5e_devcom_init_mpv(struct mlx5e_priv *priv, u64 *data)
static void mlx5e_devcom_cleanup_mpv(struct mlx5e_priv *priv)
{
- if (IS_ERR_OR_NULL(priv->devcom))
+ if (!priv->devcom)
return;
if (mlx5_core_is_mp_master(priv->mdev)) {
@@ -266,6 +266,7 @@ static void mlx5e_devcom_cleanup_mpv(struct mlx5e_priv *priv)
}
mlx5_devcom_unregister_component(priv->devcom);
+ priv->devcom = NULL;
}
static int blocking_event(struct notifier_block *nb, unsigned long event, void *data)
@@ -734,7 +735,7 @@ static int mlx5e_init_rxq_rq(struct mlx5e_channel *c, struct mlx5e_params *param
rq->pdev = c->pdev;
rq->netdev = c->netdev;
rq->priv = c->priv;
- rq->tstamp = c->tstamp;
+ rq->hwtstamp_config = &c->priv->hwtstamp_config;
rq->clock = mdev->clock;
rq->icosq = &c->icosq;
rq->ix = c->ix;
@@ -790,8 +791,9 @@ static int mlx5_rq_shampo_alloc(struct mlx5_core_dev *mdev,
int node)
{
void *wqc = MLX5_ADDR_OF(rqc, rqp->rqc, wq);
+ u8 log_hd_per_page, log_hd_entry_size;
+ u16 hd_per_wq, hd_per_wqe;
u32 hd_pool_size;
- u16 hd_per_wq;
int wq_size;
int err;
@@ -814,11 +816,24 @@ static int mlx5_rq_shampo_alloc(struct mlx5_core_dev *mdev,
if (err)
goto err_umr_mkey;
- rq->mpwqe.shampo->hd_per_wqe =
- mlx5e_shampo_hd_per_wqe(mdev, params, rqp);
+ hd_per_wqe = mlx5e_shampo_hd_per_wqe(mdev, params, rqp);
wq_size = BIT(MLX5_GET(wq, wqc, log_wq_sz));
- hd_pool_size = (rq->mpwqe.shampo->hd_per_wqe * wq_size) /
- MLX5E_SHAMPO_WQ_HEADER_PER_PAGE;
+
+ BUILD_BUG_ON(MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE > PAGE_SHIFT);
+ if (hd_per_wqe >= MLX5E_SHAMPO_WQ_HEADER_PER_PAGE) {
+ log_hd_per_page = MLX5E_SHAMPO_LOG_WQ_HEADER_PER_PAGE;
+ log_hd_entry_size = MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE;
+ } else {
+ log_hd_per_page = order_base_2(hd_per_wqe);
+ log_hd_entry_size = order_base_2(PAGE_SIZE / hd_per_wqe);
+ }
+
+ rq->mpwqe.shampo->hd_per_wqe = hd_per_wqe;
+ rq->mpwqe.shampo->hd_per_page = BIT(log_hd_per_page);
+ rq->mpwqe.shampo->log_hd_per_page = log_hd_per_page;
+ rq->mpwqe.shampo->log_hd_entry_size = log_hd_entry_size;
+
+ hd_pool_size = (hd_per_wqe * wq_size) >> log_hd_per_page;
if (netif_rxq_has_unreadable_mp(rq->netdev, rq->ix)) {
/* Separate page pool for shampo headers */
@@ -2204,7 +2219,6 @@ static int mlx5e_alloc_cq_common(struct mlx5_core_dev *mdev,
mcq->set_ci_db = cq->wq_ctrl.db.db;
mcq->arm_db = cq->wq_ctrl.db.db + 1;
*mcq->set_ci_db = 0;
- *mcq->arm_db = 0;
mcq->vector = param->eq_ix;
mcq->comp = mlx5e_completion_event;
mcq->event = mlx5e_cq_error_event;
@@ -2598,7 +2612,7 @@ static int mlx5e_open_queues(struct mlx5e_channel *c,
if (err)
goto err_close_icosq_cq;
- if (netdev_ops->ndo_xdp_xmit) {
+ if (netdev_ops->ndo_xdp_xmit && c->xdp) {
c->xdpsq = mlx5e_open_xdpredirect_sq(c, params, cparam, &ccp);
if (IS_ERR(c->xdpsq)) {
err = PTR_ERR(c->xdpsq);
@@ -2802,7 +2816,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix,
c->priv = priv;
c->mdev = mdev;
- c->tstamp = &priv->tstamp;
c->ix = ix;
c->vec_ix = vec_ix;
c->sd_ix = mlx5_sd_ch_ix_get_dev_ix(mdev, ix);
@@ -3352,16 +3365,17 @@ static int mlx5e_switch_priv_params(struct mlx5e_priv *priv,
}
}
+ mlx5e_set_xdp_feature(priv);
return 0;
}
static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
+ struct mlx5e_channels *old_chs,
struct mlx5e_channels *new_chs,
mlx5e_fp_preactivate preactivate,
void *context)
{
struct net_device *netdev = priv->netdev;
- struct mlx5e_channels old_chs;
int carrier_ok;
int err = 0;
@@ -3370,7 +3384,6 @@ static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
mlx5e_deactivate_priv_channels(priv);
- old_chs = priv->channels;
priv->channels = *new_chs;
/* New channels are ready to roll, call the preactivate hook if needed
@@ -3379,12 +3392,14 @@ static int mlx5e_switch_priv_channels(struct mlx5e_priv *priv,
if (preactivate) {
err = preactivate(priv, context);
if (err) {
- priv->channels = old_chs;
+ priv->channels = *old_chs;
goto out;
}
}
- mlx5e_close_channels(&old_chs);
+ mlx5e_set_xdp_feature(priv);
+ if (!MLX5_CAP_GEN(priv->mdev, tis_tir_td_order))
+ mlx5e_close_channels(old_chs);
priv->profile->update_rx(priv);
mlx5e_selq_apply(&priv->selq);
@@ -3403,16 +3418,20 @@ int mlx5e_safe_switch_params(struct mlx5e_priv *priv,
mlx5e_fp_preactivate preactivate,
void *context, bool reset)
{
- struct mlx5e_channels *new_chs;
+ struct mlx5e_channels *old_chs, *new_chs;
int err;
reset &= test_bit(MLX5E_STATE_OPENED, &priv->state);
if (!reset)
return mlx5e_switch_priv_params(priv, params, preactivate, context);
+ old_chs = kzalloc(sizeof(*old_chs), GFP_KERNEL);
new_chs = kzalloc(sizeof(*new_chs), GFP_KERNEL);
- if (!new_chs)
- return -ENOMEM;
+ if (!old_chs || !new_chs) {
+ err = -ENOMEM;
+ goto err_free_chs;
+ }
+
new_chs->params = *params;
mlx5e_selq_prepare_params(&priv->selq, &new_chs->params);
@@ -3421,11 +3440,18 @@ int mlx5e_safe_switch_params(struct mlx5e_priv *priv,
if (err)
goto err_cancel_selq;
- err = mlx5e_switch_priv_channels(priv, new_chs, preactivate, context);
+ *old_chs = priv->channels;
+
+ err = mlx5e_switch_priv_channels(priv, old_chs, new_chs,
+ preactivate, context);
if (err)
goto err_close;
+ if (MLX5_CAP_GEN(priv->mdev, tis_tir_td_order))
+ mlx5e_close_channels(old_chs);
+
kfree(new_chs);
+ kfree(old_chs);
return 0;
err_close:
@@ -3433,7 +3459,9 @@ err_close:
err_cancel_selq:
mlx5e_selq_cancel(&priv->selq);
+err_free_chs:
kfree(new_chs);
+ kfree(old_chs);
return err;
}
@@ -3444,8 +3472,8 @@ int mlx5e_safe_reopen_channels(struct mlx5e_priv *priv)
void mlx5e_timestamp_init(struct mlx5e_priv *priv)
{
- priv->tstamp.tx_type = HWTSTAMP_TX_OFF;
- priv->tstamp.rx_filter = HWTSTAMP_FILTER_NONE;
+ priv->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF;
+ priv->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE;
}
static void mlx5e_modify_admin_state(struct mlx5_core_dev *mdev,
@@ -3998,6 +4026,11 @@ void mlx5e_fold_sw_stats64(struct mlx5e_priv *priv, struct rtnl_link_stats64 *s)
s->rx_bytes += rq_stats->bytes;
s->multicast += rq_stats->mcast_packets;
}
+
+#ifdef CONFIG_MLX5_EN_PSP
+ if (priv->psp)
+ s->tx_dropped += atomic_read(&priv->psp->tx_drop);
+#endif
}
void
@@ -4378,23 +4411,22 @@ static int mlx5e_handle_feature(struct net_device *netdev,
return 0;
}
-void mlx5e_set_xdp_feature(struct net_device *netdev)
+void mlx5e_set_xdp_feature(struct mlx5e_priv *priv)
{
- struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5e_params *params = &priv->channels.params;
- xdp_features_t val;
+ struct net_device *netdev = priv->netdev;
+ xdp_features_t val = 0;
- if (!netdev->netdev_ops->ndo_bpf ||
- params->packet_merge.type != MLX5E_PACKET_MERGE_NONE) {
- xdp_set_features_flag_locked(netdev, 0);
- return;
- }
+ if (netdev->netdev_ops->ndo_bpf &&
+ params->packet_merge.type == MLX5E_PACKET_MERGE_NONE)
+ val = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
+ NETDEV_XDP_ACT_XSK_ZEROCOPY |
+ NETDEV_XDP_ACT_RX_SG;
+
+ if (netdev->netdev_ops->ndo_xdp_xmit && params->xdp_prog)
+ val |= NETDEV_XDP_ACT_NDO_XMIT |
+ NETDEV_XDP_ACT_NDO_XMIT_SG;
- val = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |
- NETDEV_XDP_ACT_XSK_ZEROCOPY |
- NETDEV_XDP_ACT_RX_SG |
- NETDEV_XDP_ACT_NDO_XMIT |
- NETDEV_XDP_ACT_NDO_XMIT_SG;
xdp_set_features_flag_locked(netdev, val);
}
@@ -4430,9 +4462,6 @@ int mlx5e_set_features(struct net_device *netdev, netdev_features_t features)
return -EINVAL;
}
- /* update XDP supported features */
- mlx5e_set_xdp_feature(netdev);
-
return 0;
}
@@ -4740,22 +4769,23 @@ static int mlx5e_hwstamp_config_ptp_rx(struct mlx5e_priv *priv, bool ptp_rx)
&new_params.ptp_rx, true);
}
-int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr)
+int mlx5e_hwtstamp_set(struct mlx5e_priv *priv,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config config;
bool rx_cqe_compress_def;
bool ptp_rx;
int err;
if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz) ||
- (mlx5_clock_get_ptp_index(priv->mdev) == -1))
+ (mlx5_clock_get_ptp_index(priv->mdev) == -1)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Timestamps are not supported on this device");
return -EOPNOTSUPP;
-
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
+ }
/* TX HW timestamp */
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
break;
@@ -4767,7 +4797,7 @@ int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr)
rx_cqe_compress_def = priv->channels.params.rx_cqe_compress_def;
/* RX HW timestamp */
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
ptp_rx = false;
break;
@@ -4786,7 +4816,7 @@ int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
/* ptp_rx is set if both HW TS is set and CQE
* compression is set
*/
@@ -4799,47 +4829,50 @@ int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr)
if (!mlx5e_profile_feature_cap(priv->profile, PTP_RX))
err = mlx5e_hwstamp_config_no_ptp_rx(priv,
- config.rx_filter != HWTSTAMP_FILTER_NONE);
+ config->rx_filter != HWTSTAMP_FILTER_NONE);
else
err = mlx5e_hwstamp_config_ptp_rx(priv, ptp_rx);
if (err)
goto err_unlock;
- memcpy(&priv->tstamp, &config, sizeof(config));
+ priv->hwtstamp_config = *config;
mutex_unlock(&priv->state_lock);
/* might need to fix some features */
netdev_update_features(priv->netdev);
- return copy_to_user(ifr->ifr_data, &config,
- sizeof(config)) ? -EFAULT : 0;
+ return 0;
err_unlock:
mutex_unlock(&priv->state_lock);
return err;
}
-int mlx5e_hwstamp_get(struct mlx5e_priv *priv, struct ifreq *ifr)
+static int mlx5e_hwtstamp_set_ndo(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config *cfg = &priv->tstamp;
+ struct mlx5e_priv *priv = netdev_priv(netdev);
+ return mlx5e_hwtstamp_set(priv, config, extack);
+}
+
+int mlx5e_hwtstamp_get(struct mlx5e_priv *priv,
+ struct kernel_hwtstamp_config *config)
+{
if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz))
return -EOPNOTSUPP;
- return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0;
+ *config = priv->hwtstamp_config;
+
+ return 0;
}
-static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+static int mlx5e_hwtstamp_get_ndo(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
{
struct mlx5e_priv *priv = netdev_priv(dev);
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return mlx5e_hwstamp_set(priv, ifr);
- case SIOCGHWTSTAMP:
- return mlx5e_hwstamp_get(priv, ifr);
- default:
- return -EOPNOTSUPP;
- }
+ return mlx5e_hwtstamp_get(priv, config);
}
#ifdef CONFIG_MLX5_ESWITCH
@@ -5280,13 +5313,14 @@ const struct net_device_ops mlx5e_netdev_ops = {
.ndo_set_features = mlx5e_set_features,
.ndo_fix_features = mlx5e_fix_features,
.ndo_change_mtu = mlx5e_change_nic_mtu,
- .ndo_eth_ioctl = mlx5e_ioctl,
.ndo_set_tx_maxrate = mlx5e_set_tx_maxrate,
.ndo_features_check = mlx5e_features_check,
.ndo_tx_timeout = mlx5e_tx_timeout,
.ndo_bpf = mlx5e_xdp,
.ndo_xdp_xmit = mlx5e_xdp_xmit,
.ndo_xsk_wakeup = mlx5e_xsk_wakeup,
+ .ndo_hwtstamp_get = mlx5e_hwtstamp_get_ndo,
+ .ndo_hwtstamp_set = mlx5e_hwtstamp_set_ndo,
#ifdef CONFIG_MLX5_EN_ARFS
.ndo_rx_flow_steer = mlx5e_rx_flow_steer,
#endif
@@ -5823,7 +5857,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev)
netdev->netmem_tx = true;
netif_set_tso_max_size(netdev, GSO_MAX_SIZE);
- mlx5e_set_xdp_feature(netdev);
+ mlx5e_set_xdp_feature(priv);
mlx5e_set_netdev_dev_addr(netdev);
mlx5e_macsec_build_netdev(priv);
mlx5e_ipsec_build_netdev(priv);
@@ -5921,7 +5955,7 @@ static int mlx5e_nic_init(struct mlx5_core_dev *mdev,
mlx5e_psp_register(priv);
/* update XDP supported features */
- mlx5e_set_xdp_feature(netdev);
+ mlx5e_set_xdp_feature(priv);
if (take_rtnl)
rtnl_unlock();
@@ -6120,6 +6154,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
if (mlx5e_monitor_counter_supported(priv))
mlx5e_monitor_counter_cleanup(priv);
+ mlx5e_ipsec_disable_events(priv);
mlx5e_disable_blocking_events(priv);
mlx5e_disable_async_events(priv);
mlx5_lag_remove_netdev(mdev, priv->netdev);
@@ -6130,7 +6165,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv)
static int mlx5e_update_nic_rx(struct mlx5e_priv *priv)
{
- return mlx5e_refresh_tirs(priv, false, false);
+ return mlx5e_refresh_tirs(priv->mdev, false, false);
}
static const struct mlx5e_profile mlx5e_nic_profile = {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
index 0335ca8277ef..ee9595109649 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
@@ -867,7 +867,7 @@ static void mlx5e_build_rep_params(struct net_device *netdev)
if (take_rtnl)
rtnl_lock();
/* update XDP supported features */
- mlx5e_set_xdp_feature(netdev);
+ mlx5e_set_xdp_feature(priv);
if (take_rtnl)
rtnl_unlock();
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index 263d5628ee44..1f6930c77437 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -648,17 +648,20 @@ static void build_ksm_umr(struct mlx5e_icosq *sq, struct mlx5e_umr_wqe *umr_wqe,
umr_wqe->hdr.uctrl.mkey_mask = cpu_to_be64(MLX5_MKEY_MASK_FREE);
}
-static struct mlx5e_frag_page *mlx5e_shampo_hd_to_frag_page(struct mlx5e_rq *rq, int header_index)
+static struct mlx5e_frag_page *mlx5e_shampo_hd_to_frag_page(struct mlx5e_rq *rq,
+ int header_index)
{
- BUILD_BUG_ON(MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE > PAGE_SHIFT);
+ struct mlx5e_shampo_hd *shampo = rq->mpwqe.shampo;
- return &rq->mpwqe.shampo->pages[header_index >> MLX5E_SHAMPO_LOG_WQ_HEADER_PER_PAGE];
+ return &shampo->pages[header_index >> shampo->log_hd_per_page];
}
-static u64 mlx5e_shampo_hd_offset(int header_index)
+static u64 mlx5e_shampo_hd_offset(struct mlx5e_rq *rq, int header_index)
{
- return (header_index & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1)) <<
- MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE;
+ struct mlx5e_shampo_hd *shampo = rq->mpwqe.shampo;
+ u32 hd_per_page = shampo->hd_per_page;
+
+ return (header_index & (hd_per_page - 1)) << shampo->log_hd_entry_size;
}
static void mlx5e_free_rx_shampo_hd_entry(struct mlx5e_rq *rq, u16 header_index);
@@ -671,7 +674,7 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq,
u16 pi, header_offset, err, wqe_bbs;
u32 lkey = rq->mdev->mlx5e_res.hw_objs.mkey;
struct mlx5e_umr_wqe *umr_wqe;
- int headroom, i = 0;
+ int headroom, i;
headroom = rq->buff.headroom;
wqe_bbs = MLX5E_KSM_UMR_WQEBBS(ksm_entries);
@@ -679,25 +682,24 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq,
umr_wqe = mlx5_wq_cyc_get_wqe(&sq->wq, pi);
build_ksm_umr(sq, umr_wqe, shampo->mkey_be, index, ksm_entries);
- WARN_ON_ONCE(ksm_entries & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1));
- while (i < ksm_entries) {
- struct mlx5e_frag_page *frag_page = mlx5e_shampo_hd_to_frag_page(rq, index);
+ for (i = 0; i < ksm_entries; i++, index++) {
+ struct mlx5e_frag_page *frag_page;
u64 addr;
- err = mlx5e_page_alloc_fragmented(rq->hd_page_pool, frag_page);
- if (unlikely(err))
- goto err_unmap;
+ frag_page = mlx5e_shampo_hd_to_frag_page(rq, index);
+ header_offset = mlx5e_shampo_hd_offset(rq, index);
+ if (!header_offset) {
+ err = mlx5e_page_alloc_fragmented(rq->hd_page_pool,
+ frag_page);
+ if (err)
+ goto err_unmap;
+ }
addr = page_pool_get_dma_addr_netmem(frag_page->netmem);
-
- for (int j = 0; j < MLX5E_SHAMPO_WQ_HEADER_PER_PAGE; j++) {
- header_offset = mlx5e_shampo_hd_offset(index++);
-
- umr_wqe->inline_ksms[i++] = (struct mlx5_ksm) {
- .key = cpu_to_be32(lkey),
- .va = cpu_to_be64(addr + header_offset + headroom),
- };
- }
+ umr_wqe->inline_ksms[i] = (struct mlx5_ksm) {
+ .key = cpu_to_be32(lkey),
+ .va = cpu_to_be64(addr + header_offset + headroom),
+ };
}
sq->db.wqe_info[pi] = (struct mlx5e_icosq_wqe_info) {
@@ -713,9 +715,9 @@ static int mlx5e_build_shampo_hd_umr(struct mlx5e_rq *rq,
return 0;
err_unmap:
- while (--i) {
+ while (--i >= 0) {
--index;
- header_offset = mlx5e_shampo_hd_offset(index);
+ header_offset = mlx5e_shampo_hd_offset(rq, index);
if (!header_offset) {
struct mlx5e_frag_page *frag_page = mlx5e_shampo_hd_to_frag_page(rq, index);
@@ -735,12 +737,11 @@ static int mlx5e_alloc_rx_hd_mpwqe(struct mlx5e_rq *rq)
struct mlx5e_icosq *sq = rq->icosq;
int i, err, max_ksm_entries, len;
- max_ksm_entries = ALIGN_DOWN(MLX5E_MAX_KSM_PER_WQE(rq->mdev),
- MLX5E_SHAMPO_WQ_HEADER_PER_PAGE);
+ max_ksm_entries = MLX5E_MAX_KSM_PER_WQE(rq->mdev);
ksm_entries = bitmap_find_window(shampo->bitmap,
shampo->hd_per_wqe,
shampo->hd_per_wq, shampo->pi);
- ksm_entries = ALIGN_DOWN(ksm_entries, MLX5E_SHAMPO_WQ_HEADER_PER_PAGE);
+ ksm_entries = ALIGN_DOWN(ksm_entries, shampo->hd_per_page);
if (!ksm_entries)
return 0;
@@ -858,7 +859,7 @@ mlx5e_free_rx_shampo_hd_entry(struct mlx5e_rq *rq, u16 header_index)
{
struct mlx5e_shampo_hd *shampo = rq->mpwqe.shampo;
- if (((header_index + 1) & (MLX5E_SHAMPO_WQ_HEADER_PER_PAGE - 1)) == 0) {
+ if (((header_index + 1) & (shampo->hd_per_page - 1)) == 0) {
struct mlx5e_frag_page *frag_page = mlx5e_shampo_hd_to_frag_page(rq, header_index);
mlx5e_page_release_fragmented(rq->hd_page_pool, frag_page);
@@ -1036,6 +1037,10 @@ int mlx5e_poll_ico_cq(struct mlx5e_cq *cq)
netdev_WARN_ONCE(cq->netdev,
"Bad OP in ICOSQ CQE: 0x%x\n",
get_cqe_opcode(cqe));
+#ifdef CONFIG_MLX5_EN_TLS
+ if (wi->wqe_type == MLX5E_ICOSQ_WQE_GET_PSV_TLS)
+ mlx5e_ktls_rx_resync_async_request_cancel(wi);
+#endif
mlx5e_dump_error_cqe(&sq->cq, sq->sqn,
(struct mlx5_err_cqe *)cqe);
mlx5_wq_cyc_wqe_dump(&sq->wq, ci, wi->num_wqebbs);
@@ -1221,9 +1226,10 @@ static unsigned int mlx5e_lro_update_hdr(struct sk_buff *skb,
static void *mlx5e_shampo_get_packet_hd(struct mlx5e_rq *rq, u16 header_index)
{
struct mlx5e_frag_page *frag_page = mlx5e_shampo_hd_to_frag_page(rq, header_index);
- u16 head_offset = mlx5e_shampo_hd_offset(header_index) + rq->buff.headroom;
+ u16 head_offset = mlx5e_shampo_hd_offset(rq, header_index);
+ void *addr = netmem_address(frag_page->netmem);
- return netmem_address(frag_page->netmem) + head_offset;
+ return addr + head_offset + rq->buff.headroom;
}
static void mlx5e_shampo_update_ipv4_udp_hdr(struct mlx5e_rq *rq, struct iphdr *ipv4)
@@ -1598,7 +1604,7 @@ static inline bool mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe,
stats->lro_bytes += cqe_bcnt;
}
- if (unlikely(mlx5e_rx_hw_stamp(rq->tstamp)))
+ if (unlikely(mlx5e_rx_hw_stamp(rq->hwtstamp_config)))
skb_hwtstamps(skb)->hwtstamp = mlx5e_cqe_ts_to_ns(rq->ptp_cyc2time,
rq->clock, get_cqe_ts(cqe));
skb_record_rx_queue(skb, rq->ix);
@@ -1794,14 +1800,27 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi
}
prog = rcu_dereference(rq->xdp_prog);
- if (prog && mlx5e_xdp_handle(rq, prog, mxbuf)) {
- if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) {
- struct mlx5e_wqe_frag_info *pwi;
+ if (prog) {
+ u8 nr_frags_free, old_nr_frags = sinfo->nr_frags;
- for (pwi = head_wi; pwi < wi; pwi++)
- pwi->frag_page->frags++;
+ if (mlx5e_xdp_handle(rq, prog, mxbuf)) {
+ if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT,
+ rq->flags)) {
+ struct mlx5e_wqe_frag_info *pwi;
+
+ wi -= old_nr_frags - sinfo->nr_frags;
+
+ for (pwi = head_wi; pwi < wi; pwi++)
+ pwi->frag_page->frags++;
+ }
+ return NULL; /* page/packet was consumed by XDP */
+ }
+
+ nr_frags_free = old_nr_frags - sinfo->nr_frags;
+ if (unlikely(nr_frags_free)) {
+ wi -= nr_frags_free;
+ truesize -= nr_frags_free * frag_info->frag_stride;
}
- return NULL; /* page/packet was consumed by XDP */
}
skb = mlx5e_build_linear_skb(
@@ -2027,6 +2046,7 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w
u32 byte_cnt = cqe_bcnt;
struct skb_shared_info *sinfo;
unsigned int truesize = 0;
+ u32 pg_consumed_bytes;
struct bpf_prog *prog;
struct sk_buff *skb;
u32 linear_frame_sz;
@@ -2080,7 +2100,8 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w
while (byte_cnt) {
/* Non-linear mode, hence non-XSK, which always uses PAGE_SIZE. */
- u32 pg_consumed_bytes = min_t(u32, PAGE_SIZE - frag_offset, byte_cnt);
+ pg_consumed_bytes =
+ min_t(u32, PAGE_SIZE - frag_offset, byte_cnt);
if (test_bit(MLX5E_RQ_STATE_SHAMPO, &rq->state))
truesize += pg_consumed_bytes;
@@ -2096,10 +2117,15 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w
}
if (prog) {
+ u8 nr_frags_free, old_nr_frags = sinfo->nr_frags;
+ u32 len;
+
if (mlx5e_xdp_handle(rq, prog, mxbuf)) {
if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) {
struct mlx5e_frag_page *pfp;
+ frag_page -= old_nr_frags - sinfo->nr_frags;
+
for (pfp = head_page; pfp < frag_page; pfp++)
pfp->frags++;
@@ -2110,9 +2136,19 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w
return NULL; /* page/packet was consumed by XDP */
}
+ nr_frags_free = old_nr_frags - sinfo->nr_frags;
+ if (unlikely(nr_frags_free)) {
+ frag_page -= nr_frags_free;
+ truesize -= (nr_frags_free - 1) * PAGE_SIZE +
+ ALIGN(pg_consumed_bytes,
+ BIT(rq->mpwqe.log_stride_sz));
+ }
+
+ len = mxbuf->xdp.data_end - mxbuf->xdp.data;
+
skb = mlx5e_build_linear_skb(
rq, mxbuf->xdp.data_hard_start, linear_frame_sz,
- mxbuf->xdp.data - mxbuf->xdp.data_hard_start, 0,
+ mxbuf->xdp.data - mxbuf->xdp.data_hard_start, len,
mxbuf->xdp.data - mxbuf->xdp.data_meta);
if (unlikely(!skb)) {
mlx5e_page_release_fragmented(rq->page_pool,
@@ -2137,8 +2173,11 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w
do
pagep->frags++;
while (++pagep < frag_page);
+
+ headlen = min_t(u16, MLX5E_RX_MAX_HEAD - len,
+ skb->data_len);
+ __pskb_pull_tail(skb, headlen);
}
- __pskb_pull_tail(skb, headlen);
} else {
dma_addr_t addr;
@@ -2230,7 +2269,8 @@ mlx5e_skb_from_cqe_shampo(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
struct mlx5_cqe64 *cqe, u16 header_index)
{
struct mlx5e_frag_page *frag_page = mlx5e_shampo_hd_to_frag_page(rq, header_index);
- u16 head_offset = mlx5e_shampo_hd_offset(header_index);
+ u16 head_offset = mlx5e_shampo_hd_offset(rq, header_index);
+ struct mlx5e_shampo_hd *shampo = rq->mpwqe.shampo;
u16 head_size = cqe->shampo.header_size;
u16 rx_headroom = rq->buff.headroom;
struct sk_buff *skb = NULL;
@@ -2246,7 +2286,7 @@ mlx5e_skb_from_cqe_shampo(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi,
data = hdr + rx_headroom;
frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + head_size);
- if (likely(frag_size <= BIT(MLX5E_SHAMPO_LOG_MAX_HEADER_ENTRY_SIZE))) {
+ if (likely(frag_size <= BIT(shampo->log_hd_entry_size))) {
/* build SKB around header */
dma_sync_single_range_for_cpu(rq->pdev, dma_addr, 0, frag_size, rq->buff.map_dir);
net_prefetchw(hdr);
@@ -2319,7 +2359,10 @@ mlx5e_hw_gro_skb_has_enough_space(struct sk_buff *skb, u16 data_bcnt)
{
int nr_frags = skb_shinfo(skb)->nr_frags;
- return PAGE_SIZE * nr_frags + data_bcnt <= GRO_LEGACY_MAX_SIZE;
+ if (PAGE_SIZE >= GRO_LEGACY_MAX_SIZE)
+ return skb->len + data_bcnt <= GRO_LEGACY_MAX_SIZE;
+ else
+ return PAGE_SIZE * nr_frags + data_bcnt <= GRO_LEGACY_MAX_SIZE;
}
static void mlx5e_handle_rx_cqe_mpwrq_shampo(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
@@ -2613,7 +2656,6 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq,
u32 cqe_bcnt,
struct sk_buff *skb)
{
- struct hwtstamp_config *tstamp;
struct mlx5e_rq_stats *stats;
struct net_device *netdev;
struct mlx5e_priv *priv;
@@ -2637,7 +2679,6 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq,
}
priv = mlx5i_epriv(netdev);
- tstamp = &priv->tstamp;
stats = &priv->channel_stats[rq->ix]->rq;
flags_rqpn = be32_to_cpu(cqe->flags_rqpn);
@@ -2673,7 +2714,7 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq,
stats->csum_none++;
}
- if (unlikely(mlx5e_rx_hw_stamp(tstamp)))
+ if (unlikely(mlx5e_rx_hw_stamp(&priv->hwtstamp_config)))
skb_hwtstamps(skb)->hwtstamp = mlx5e_cqe_ts_to_ns(rq->ptp_cyc2time,
rq->clock, get_cqe_ts(cqe));
skb_record_rx_queue(skb, rq->ix);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
index 2f7a543feca6..fcad464bc4d5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c
@@ -214,7 +214,7 @@ static int mlx5e_test_loopback_setup(struct mlx5e_priv *priv,
return err;
}
- err = mlx5e_refresh_tirs(priv, true, false);
+ err = mlx5e_modify_tirs_lb(priv->mdev, true, false);
if (err)
goto out;
@@ -243,7 +243,7 @@ static void mlx5e_test_loopback_cleanup(struct mlx5e_priv *priv,
mlx5_nic_vport_update_local_lb(priv->mdev, false);
dev_remove_pack(&lbtp->pt);
- mlx5e_refresh_tirs(priv, false, false);
+ mlx5e_modify_tirs_lb(priv->mdev, false, false);
}
static int mlx5e_cond_loopback(struct mlx5e_priv *priv)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
index 7c029a7d0fd7..a2802cfc9b98 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c
@@ -1614,7 +1614,9 @@ void mlx5e_stats_fec_get(struct mlx5e_priv *priv,
fec_set_corrected_bits_total(priv, fec_stats);
fec_set_block_stats(priv, mode, fec_stats);
- fec_set_histograms_stats(priv, mode, hist);
+
+ if (MLX5_CAP_PCAM_REG(priv->mdev, pphcr))
+ fec_set_histograms_stats(priv, mode, hist);
}
#define PPORT_ETH_EXT_OFF(c) \
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 00c2763e57ca..a8773b2342c2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -3614,15 +3614,11 @@ static bool same_port_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv
bool mlx5e_same_hw_devs(struct mlx5e_priv *priv, struct mlx5e_priv *peer_priv)
{
struct mlx5_core_dev *fmdev, *pmdev;
- u64 fsystem_guid, psystem_guid;
fmdev = priv->mdev;
pmdev = peer_priv->mdev;
- fsystem_guid = mlx5_query_nic_system_image_guid(fmdev);
- psystem_guid = mlx5_query_nic_system_image_guid(pmdev);
-
- return (fsystem_guid == psystem_guid);
+ return mlx5_same_hw_devs(fmdev, pmdev);
}
static int
@@ -5237,10 +5233,11 @@ static void mlx5e_tc_nic_destroy_miss_table(struct mlx5e_priv *priv)
int mlx5e_tc_nic_init(struct mlx5e_priv *priv)
{
struct mlx5e_tc_table *tc = mlx5e_fs_get_tc(priv->fs);
+ u8 mapping_id[MLX5_SW_IMAGE_GUID_MAX_BYTES];
struct mlx5_core_dev *dev = priv->mdev;
struct mapping_ctx *chains_mapping;
struct mlx5_chains_attr attr = {};
- u64 mapping_id;
+ u8 id_len;
int err;
mlx5e_mod_hdr_tbl_init(&tc->mod_hdr);
@@ -5256,11 +5253,13 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv)
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);
+ mlx5_query_nic_sw_system_image_guid(dev, mapping_id, &id_len);
- chains_mapping = mapping_create_for_id(mapping_id, MAPPING_TYPE_CHAIN,
+ chains_mapping = mapping_create_for_id(mapping_id, id_len,
+ MAPPING_TYPE_CHAIN,
sizeof(struct mlx5_mapped_obj),
- MLX5E_TC_TABLE_CHAIN_TAG_MASK, true);
+ MLX5E_TC_TABLE_CHAIN_TAG_MASK,
+ true);
if (IS_ERR(chains_mapping)) {
err = PTR_ERR(chains_mapping);
@@ -5391,14 +5390,15 @@ void mlx5e_tc_ht_cleanup(struct rhashtable *tc_ht)
int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv)
{
const size_t sz_enc_opts = sizeof(struct tunnel_match_enc_opts);
+ u8 mapping_id[MLX5_SW_IMAGE_GUID_MAX_BYTES];
struct mlx5_devcom_match_attr attr = {};
struct netdev_phys_item_id ppid;
struct mlx5e_rep_priv *rpriv;
struct mapping_ctx *mapping;
struct mlx5_eswitch *esw;
struct mlx5e_priv *priv;
- u64 mapping_id;
int err = 0;
+ u8 id_len;
rpriv = container_of(uplink_priv, struct mlx5e_rep_priv, uplink_priv);
priv = netdev_priv(rpriv->netdev);
@@ -5416,9 +5416,9 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv)
uplink_priv->tc_psample = mlx5e_tc_sample_init(esw, uplink_priv->post_act);
- mapping_id = mlx5_query_nic_system_image_guid(esw->dev);
+ mlx5_query_nic_sw_system_image_guid(esw->dev, mapping_id, &id_len);
- mapping = mapping_create_for_id(mapping_id, MAPPING_TYPE_TUNNEL,
+ mapping = mapping_create_for_id(mapping_id, id_len, MAPPING_TYPE_TUNNEL,
sizeof(struct tunnel_match_key),
TUNNEL_INFO_BITS_MASK, true);
@@ -5431,8 +5431,10 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv)
/* Two last values are reserved for stack devices slow path table mark
* and bridge ingress push mark.
*/
- mapping = mapping_create_for_id(mapping_id, MAPPING_TYPE_TUNNEL_ENC_OPTS,
- sz_enc_opts, ENC_OPTS_BITS_MASK - 2, true);
+ mapping = mapping_create_for_id(mapping_id, id_len,
+ MAPPING_TYPE_TUNNEL_ENC_OPTS,
+ sz_enc_opts, ENC_OPTS_BITS_MASK - 2,
+ true);
if (IS_ERR(mapping)) {
err = PTR_ERR(mapping);
goto err_enc_opts_mapping;
@@ -5453,7 +5455,7 @@ int mlx5e_tc_esw_init(struct mlx5_rep_uplink_priv *uplink_priv)
err = netif_get_port_parent_id(priv->netdev, &ppid, false);
if (!err) {
- memcpy(&attr.key.val, &ppid.id, sizeof(attr.key.val));
+ memcpy(&attr.key.buf, &ppid.id, ppid.id_len);
attr.flags = MLX5_DEVCOM_MATCH_FLAGS_NS;
attr.net = mlx5_core_net(esw->dev);
mlx5_esw_offloads_devcom_init(esw, &attr);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
index b7227afcb51d..14884b9ea7f3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c
@@ -256,7 +256,7 @@ mlx5e_tx_wqe_inline_mode(struct mlx5e_txqsq *sq, struct sk_buff *skb,
u8 mode;
#ifdef CONFIG_MLX5_EN_TLS
- if (accel && accel->tls.tls_tisn)
+ if (accel->tls.tls_tisn)
return MLX5_INLINE_MODE_TCP_UDP;
#endif
@@ -755,7 +755,7 @@ static void mlx5e_consume_skb(struct mlx5e_txqsq *sq, struct sk_buff *skb,
hwts.hwtstamp = mlx5e_cqe_ts_to_ns(sq->ptp_cyc2time, sq->clock, ts);
if (sq->ptpsq) {
mlx5e_skb_cb_hwtstamp_handler(skb, MLX5E_SKB_CB_CQE_HWTSTAMP,
- hwts.hwtstamp, sq->ptpsq->cq_stats);
+ hwts.hwtstamp, sq->ptpsq);
} else {
skb_tstamp_tx(skb, &hwts);
sq->stats->timestamps++;
@@ -982,6 +982,7 @@ void mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
struct mlx5e_tx_attr attr;
struct mlx5i_tx_wqe *wqe;
+ struct mlx5e_accel_tx_state accel = {};
struct mlx5_wqe_datagram_seg *datagram;
struct mlx5_wqe_ctrl_seg *cseg;
struct mlx5_wqe_eth_seg *eseg;
@@ -992,7 +993,7 @@ void mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
int num_dma;
u16 pi;
- mlx5e_sq_xmit_prepare(sq, skb, NULL, &attr);
+ mlx5e_sq_xmit_prepare(sq, skb, &accel, &attr);
mlx5i_sq_calc_wqe_attr(skb, &attr, &wqe_attr);
pi = mlx5e_txqsq_get_next_pi(sq, wqe_attr.num_wqebbs);
@@ -1009,7 +1010,7 @@ void mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
mlx5i_txwqe_build_datagram(av, dqpn, dqkey, datagram);
- mlx5e_txwqe_build_eseg_csum(sq, skb, NULL, eseg);
+ mlx5e_txwqe_build_eseg_csum(sq, skb, &accel, eseg);
eseg->mss = attr.mss;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c
index 0091ba697bae..250af09b5af2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/adj_vport.c
@@ -4,13 +4,8 @@
#include "fs_core.h"
#include "eswitch.h"
-enum {
- MLX5_ADJ_VPORT_DISCONNECT = 0x0,
- MLX5_ADJ_VPORT_CONNECT = 0x1,
-};
-
-static int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev,
- u16 vport, bool connect)
+int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev, u16 vport,
+ bool connect)
{
u32 in[MLX5_ST_SZ_DW(modify_vport_state_in)] = {};
@@ -24,7 +19,7 @@ static int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev,
MLX5_SET(modify_vport_state_in, in, egress_connect_valid, 1);
MLX5_SET(modify_vport_state_in, in, ingress_connect, connect);
MLX5_SET(modify_vport_state_in, in, egress_connect, connect);
-
+ MLX5_SET(modify_vport_state_in, in, admin_state, connect);
return mlx5_cmd_exec_in(dev, modify_vport_state, in);
}
@@ -96,7 +91,6 @@ static int mlx5_esw_adj_vport_create(struct mlx5_eswitch *esw, u16 vhca_id,
if (err)
goto acl_ns_remove;
- mlx5_esw_adj_vport_modify(esw->dev, vport_num, MLX5_ADJ_VPORT_CONNECT);
return 0;
acl_ns_remove:
@@ -117,8 +111,7 @@ static void mlx5_esw_adj_vport_destroy(struct mlx5_eswitch *esw,
esw_debug(esw->dev, "Destroying adjacent vport %d for vhca_id 0x%x\n",
vport_num, vport->vhca_id);
- mlx5_esw_adj_vport_modify(esw->dev, vport_num,
- MLX5_ADJ_VPORT_DISCONNECT);
+
mlx5_esw_offloads_rep_remove(esw, vport);
mlx5_fs_vport_egress_acl_ns_remove(esw->dev->priv.steering,
vport->index);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c
index cf88a106d80d..89a58dee50b3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/devlink_port.c
@@ -7,11 +7,7 @@
static void
mlx5_esw_get_port_parent_id(struct mlx5_core_dev *dev, struct netdev_phys_item_id *ppid)
{
- u64 parent_id;
-
- parent_id = mlx5_query_nic_system_image_guid(dev);
- ppid->id_len = sizeof(parent_id);
- memcpy(ppid->id, &parent_id, sizeof(parent_id));
+ mlx5_query_nic_sw_system_image_guid(dev, ppid->id, &ppid->id_len);
}
static bool mlx5_esw_devlink_port_supported(struct mlx5_eswitch *esw, u16 vport_num)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/legacy.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/legacy.c
index 76382626ad41..929adeb50a98 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/legacy.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/legacy.c
@@ -66,7 +66,6 @@ static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw)
esw->fdb_table.legacy.addr_grp = NULL;
esw->fdb_table.legacy.allmulti_grp = NULL;
esw->fdb_table.legacy.promisc_grp = NULL;
- atomic64_set(&esw->user_count, 0);
}
static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c
index 56e6f54b1e2e..4278bcb04c72 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c
@@ -341,13 +341,6 @@ static u32 esw_qos_calculate_min_rate_divider(struct mlx5_eswitch *esw,
if (max_guarantee)
return max_t(u32, max_guarantee / fw_max_bw_share, 1);
- /* If nodes max min_rate divider is 0 but their parent has bw_share
- * configured, then set bw_share for nodes to minimal value.
- */
-
- if (parent && parent->bw_share)
- return 1;
-
/* If the node nodes has min_rate configured, a divider of 0 sets all
* nodes' bw_share to 0, effectively disabling min guarantees.
*/
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index e2ffb87b94cb..4b7a1ce7f406 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -875,13 +875,10 @@ static int esw_vport_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
vport_num, 1,
vport->info.link_state);
- /* Host PF has its own mac/guid. */
- if (vport_num) {
- mlx5_modify_nic_vport_mac_address(esw->dev, vport_num,
- vport->info.mac);
- mlx5_modify_nic_vport_node_guid(esw->dev, vport_num,
- vport->info.node_guid);
- }
+ mlx5_query_nic_vport_mac_address(esw->dev, vport_num, true,
+ vport->info.mac);
+ mlx5_query_nic_vport_node_guid(esw->dev, vport_num, true,
+ &vport->info.node_guid);
flags = (vport->info.vlan || vport->info.qos) ?
SET_VLAN_STRIP | SET_VLAN_INSERT : 0;
@@ -947,12 +944,6 @@ int mlx5_esw_vport_enable(struct mlx5_eswitch *esw, struct mlx5_vport *vport,
goto err_vhca_mapping;
}
- /* External controller host PF has factory programmed MAC.
- * Read it from the device.
- */
- if (mlx5_core_is_ecpf(esw->dev) && vport_num == MLX5_VPORT_PF)
- mlx5_query_nic_vport_mac_address(esw->dev, vport_num, true, vport->info.mac);
-
esw_vport_change_handle_locked(vport);
esw->enabled_vports++;
@@ -1483,7 +1474,7 @@ static void mlx5_esw_mode_change_notify(struct mlx5_eswitch *esw, u16 mode)
info.new_mode = mode;
- blocking_notifier_call_chain(&esw->n_head, 0, &info);
+ blocking_notifier_call_chain(&esw->dev->priv.esw_n_head, 0, &info);
}
static int mlx5_esw_egress_acls_init(struct mlx5_core_dev *dev)
@@ -1978,7 +1969,8 @@ static int mlx5_devlink_esw_multiport_set(struct devlink *devlink, u32 id,
}
static int mlx5_devlink_esw_multiport_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
@@ -2059,7 +2051,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
esw->offloads.encap = DEVLINK_ESWITCH_ENCAP_MODE_BASIC;
else
esw->offloads.encap = DEVLINK_ESWITCH_ENCAP_MODE_NONE;
- BLOCKING_INIT_NOTIFIER_HEAD(&esw->n_head);
esw_info(dev,
"Total vports %d, per vport: max uc(%d) max mc(%d)\n",
@@ -2235,6 +2226,9 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
ivi->vf = vport - 1;
mutex_lock(&esw->state_lock);
+
+ mlx5_query_nic_vport_mac_address(esw->dev, vport, true,
+ evport->info.mac);
ether_addr_copy(ivi->mac, evport->info.mac);
ivi->linkstate = evport->info.link_state;
ivi->vlan = evport->info.vlan;
@@ -2385,14 +2379,16 @@ bool mlx5_esw_multipath_prereq(struct mlx5_core_dev *dev0,
dev1->priv.eswitch->mode == MLX5_ESWITCH_OFFLOADS);
}
-int mlx5_esw_event_notifier_register(struct mlx5_eswitch *esw, struct notifier_block *nb)
+int mlx5_esw_event_notifier_register(struct mlx5_core_dev *dev,
+ struct notifier_block *nb)
{
- return blocking_notifier_chain_register(&esw->n_head, nb);
+ return blocking_notifier_chain_register(&dev->priv.esw_n_head, nb);
}
-void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifier_block *nb)
+void mlx5_esw_event_notifier_unregister(struct mlx5_core_dev *dev,
+ struct notifier_block *nb)
{
- blocking_notifier_chain_unregister(&esw->n_head, nb);
+ blocking_notifier_chain_unregister(&dev->priv.esw_n_head, nb);
}
/**
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index 16eb99aba2a7..ad1073f7b79f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -264,6 +264,9 @@ struct mlx5_eswitch_fdb {
struct offloads_fdb {
struct mlx5_flow_namespace *ns;
+ struct mlx5_flow_table *drop_root;
+ struct mlx5_flow_handle *drop_root_rule;
+ struct mlx5_fc *drop_root_fc;
struct mlx5_flow_table *tc_miss_table;
struct mlx5_flow_table *slow_fdb;
struct mlx5_flow_group *send_to_vport_grp;
@@ -392,6 +395,7 @@ struct mlx5_eswitch {
struct mlx5_esw_offload offloads;
u32 last_vport_idx;
int mode;
+ bool offloads_inactive;
u16 manager_vport;
u16 first_host_vport;
u8 num_peers;
@@ -399,7 +403,6 @@ struct mlx5_eswitch {
struct {
u32 large_group_num;
} params;
- struct blocking_notifier_head n_head;
struct xarray paired;
struct mlx5_devcom_comp_dev *devcom;
u16 enabled_ipsec_vf_count;
@@ -634,6 +637,8 @@ const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev);
void mlx5_esw_adjacent_vhcas_setup(struct mlx5_eswitch *esw);
void mlx5_esw_adjacent_vhcas_cleanup(struct mlx5_eswitch *esw);
+int mlx5_esw_adj_vport_modify(struct mlx5_core_dev *dev, u16 vport,
+ bool connect);
#define MLX5_DEBUG_ESWITCH_MASK BIT(3)
@@ -858,8 +863,10 @@ struct mlx5_esw_event_info {
u16 new_mode;
};
-int mlx5_esw_event_notifier_register(struct mlx5_eswitch *esw, struct notifier_block *n);
-void mlx5_esw_event_notifier_unregister(struct mlx5_eswitch *esw, struct notifier_block *n);
+int mlx5_esw_event_notifier_register(struct mlx5_core_dev *dev,
+ struct notifier_block *n);
+void mlx5_esw_event_notifier_unregister(struct mlx5_core_dev *dev,
+ struct notifier_block *n);
bool mlx5_esw_hold(struct mlx5_core_dev *dev);
void mlx5_esw_release(struct mlx5_core_dev *dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 4cf995be127d..8de6c7f6c294 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -1577,6 +1577,7 @@ esw_chains_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *miss_fdb)
attr.max_grp_num = esw->params.large_group_num;
attr.default_ft = miss_fdb;
attr.mapping = esw->offloads.reg_c0_obj_pool;
+ attr.fs_base_prio = FDB_BYPASS_PATH;
chains = mlx5_chains_create(dev, &attr);
if (IS_ERR(chains)) {
@@ -1978,7 +1979,6 @@ static void esw_destroy_offloads_fdb_tables(struct mlx5_eswitch *esw)
/* Holds true only as long as DMFS is the default */
mlx5_flow_namespace_set_mode(esw->fdb_table.offloads.ns,
MLX5_FLOW_STEERING_MODE_DMFS);
- atomic64_set(&esw->user_count, 0);
}
static int esw_get_nr_ft_offloads_steering_src_ports(struct mlx5_eswitch *esw)
@@ -2356,6 +2356,131 @@ static void esw_mode_change(struct mlx5_eswitch *esw, u16 mode)
mlx5_devcom_comp_unlock(esw->dev->priv.hca_devcom_comp);
}
+static void mlx5_esw_fdb_drop_destroy(struct mlx5_eswitch *esw)
+{
+ if (!esw->fdb_table.offloads.drop_root)
+ return;
+
+ esw_debug(esw->dev, "Destroying FDB drop root table %#x fc %#x\n",
+ esw->fdb_table.offloads.drop_root->id,
+ esw->fdb_table.offloads.drop_root_fc->id);
+ mlx5_del_flow_rules(esw->fdb_table.offloads.drop_root_rule);
+ /* Don't free flow counter here, can be reused on a later activation */
+ mlx5_destroy_flow_table(esw->fdb_table.offloads.drop_root);
+ esw->fdb_table.offloads.drop_root_rule = NULL;
+ esw->fdb_table.offloads.drop_root = NULL;
+}
+
+static int mlx5_esw_fdb_drop_create(struct mlx5_eswitch *esw)
+{
+ struct mlx5_flow_destination drop_fc_dst = {};
+ struct mlx5_flow_table_attr ft_attr = {};
+ struct mlx5_flow_destination *dst = NULL;
+ struct mlx5_core_dev *dev = esw->dev;
+ struct mlx5_flow_namespace *root_ns;
+ struct mlx5_flow_act flow_act = {};
+ struct mlx5_flow_handle *flow_rule;
+ struct mlx5_flow_table *table;
+ int err = 0, dst_num = 0;
+
+ if (esw->fdb_table.offloads.drop_root)
+ return 0;
+
+ root_ns = esw->fdb_table.offloads.ns;
+
+ ft_attr.prio = FDB_DROP_ROOT;
+ ft_attr.max_fte = 1;
+ ft_attr.autogroup.max_num_groups = 1;
+ table = mlx5_create_auto_grouped_flow_table(root_ns, &ft_attr);
+ if (IS_ERR(table)) {
+ esw_warn(dev, "Failed to create fdb drop root table, err %pe\n",
+ table);
+ return PTR_ERR(table);
+ }
+
+ /* Drop FC reusable, create once on first deactivation of FDB */
+ if (!esw->fdb_table.offloads.drop_root_fc) {
+ struct mlx5_fc *counter = mlx5_fc_create(dev, 0);
+
+ err = PTR_ERR_OR_ZERO(counter);
+ if (err)
+ esw_warn(esw->dev, "create fdb drop fc err %d\n", err);
+ else
+ esw->fdb_table.offloads.drop_root_fc = counter;
+ }
+
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
+
+ if (esw->fdb_table.offloads.drop_root_fc) {
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_COUNT;
+ drop_fc_dst.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
+ drop_fc_dst.counter = esw->fdb_table.offloads.drop_root_fc;
+ dst = &drop_fc_dst;
+ dst_num++;
+ }
+
+ flow_rule = mlx5_add_flow_rules(table, NULL, &flow_act, dst, dst_num);
+ err = PTR_ERR_OR_ZERO(flow_rule);
+ if (err) {
+ esw_warn(esw->dev,
+ "fs offloads: Failed to add vport rx drop rule err %d\n",
+ err);
+ goto err_flow_rule;
+ }
+
+ esw->fdb_table.offloads.drop_root = table;
+ esw->fdb_table.offloads.drop_root_rule = flow_rule;
+ esw_debug(esw->dev, "Created FDB drop root table %#x fc %#x\n",
+ table->id, dst ? dst->counter->id : 0);
+ return 0;
+
+err_flow_rule:
+ /* no need to free drop fc, esw_offloads_steering_cleanup will do it */
+ mlx5_destroy_flow_table(table);
+ return err;
+}
+
+static void mlx5_esw_fdb_active(struct mlx5_eswitch *esw)
+{
+ struct mlx5_vport *vport;
+ unsigned long i;
+
+ mlx5_esw_fdb_drop_destroy(esw);
+ mlx5_mpfs_enable(esw->dev);
+
+ mlx5_esw_for_each_vf_vport(esw, i, vport, U16_MAX) {
+ if (!vport->adjacent)
+ continue;
+ esw_debug(esw->dev, "Connecting vport %d to eswitch\n",
+ vport->vport);
+ mlx5_esw_adj_vport_modify(esw->dev, vport->vport, true);
+ }
+
+ esw->offloads_inactive = false;
+ esw_warn(esw->dev, "MPFS/FDB active\n");
+}
+
+static void mlx5_esw_fdb_inactive(struct mlx5_eswitch *esw)
+{
+ struct mlx5_vport *vport;
+ unsigned long i;
+
+ mlx5_mpfs_disable(esw->dev);
+ mlx5_esw_fdb_drop_create(esw);
+
+ mlx5_esw_for_each_vf_vport(esw, i, vport, U16_MAX) {
+ if (!vport->adjacent)
+ continue;
+ esw_debug(esw->dev, "Disconnecting vport %u from eswitch\n",
+ vport->vport);
+
+ mlx5_esw_adj_vport_modify(esw->dev, vport->vport, false);
+ }
+
+ esw->offloads_inactive = true;
+ esw_warn(esw->dev, "MPFS/FDB inactive\n");
+}
+
static int esw_offloads_start(struct mlx5_eswitch *esw,
struct netlink_ext_ack *extack)
{
@@ -2493,7 +2618,8 @@ done:
}
static int esw_port_metadata_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
@@ -3129,7 +3255,7 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
attr,
mlx5_esw_offloads_devcom_event,
esw);
- if (IS_ERR(esw->devcom))
+ if (!esw->devcom)
return;
mlx5_devcom_send_event(esw->devcom,
@@ -3140,7 +3266,7 @@ void mlx5_esw_offloads_devcom_init(struct mlx5_eswitch *esw,
void mlx5_esw_offloads_devcom_cleanup(struct mlx5_eswitch *esw)
{
- if (IS_ERR_OR_NULL(esw->devcom))
+ if (!esw->devcom)
return;
mlx5_devcom_send_event(esw->devcom,
@@ -3439,6 +3565,10 @@ create_indir_err:
static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw)
{
+ mlx5_esw_fdb_drop_destroy(esw);
+ if (esw->fdb_table.offloads.drop_root_fc)
+ mlx5_fc_destroy(esw->dev, esw->fdb_table.offloads.drop_root_fc);
+ esw->fdb_table.offloads.drop_root_fc = NULL;
esw_destroy_vport_rx_drop_rule(esw);
esw_destroy_vport_rx_drop_group(esw);
esw_destroy_vport_rx_group(esw);
@@ -3557,10 +3687,11 @@ bool mlx5_esw_offloads_controller_valid(const struct mlx5_eswitch *esw, u32 cont
int esw_offloads_enable(struct mlx5_eswitch *esw)
{
+ u8 mapping_id[MLX5_SW_IMAGE_GUID_MAX_BYTES];
struct mapping_ctx *reg_c0_obj_pool;
struct mlx5_vport *vport;
unsigned long i;
- u64 mapping_id;
+ u8 id_len;
int err;
mutex_init(&esw->offloads.termtbl_mutex);
@@ -3582,9 +3713,10 @@ int esw_offloads_enable(struct mlx5_eswitch *esw)
if (err)
goto err_vport_metadata;
- mapping_id = mlx5_query_nic_system_image_guid(esw->dev);
+ mlx5_query_nic_sw_system_image_guid(esw->dev, mapping_id, &id_len);
- reg_c0_obj_pool = mapping_create_for_id(mapping_id, MAPPING_TYPE_CHAIN,
+ reg_c0_obj_pool = mapping_create_for_id(mapping_id, id_len,
+ MAPPING_TYPE_CHAIN,
sizeof(struct mlx5_mapped_obj),
ESW_REG_C0_USER_DATA_METADATA_MASK,
true);
@@ -3599,6 +3731,11 @@ int esw_offloads_enable(struct mlx5_eswitch *esw)
if (err)
goto err_steering_init;
+ if (esw->offloads_inactive)
+ mlx5_esw_fdb_inactive(esw);
+ else
+ mlx5_esw_fdb_active(esw);
+
/* Representor will control the vport link state */
mlx5_esw_for_each_vf_vport(esw, i, vport, esw->esw_funcs.num_vfs)
vport->info.link_state = MLX5_VPORT_ADMIN_STATE_DOWN;
@@ -3665,6 +3802,9 @@ void esw_offloads_disable(struct mlx5_eswitch *esw)
esw_offloads_metadata_uninit(esw);
mlx5_rdma_disable_roce(esw->dev);
mlx5_esw_adjacent_vhcas_cleanup(esw);
+ /* must be done after vhcas cleanup to avoid adjacent vports connect */
+ if (esw->offloads_inactive)
+ mlx5_esw_fdb_active(esw); /* legacy mode always active */
mutex_destroy(&esw->offloads.termtbl_mutex);
}
@@ -3675,6 +3815,7 @@ static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode)
*mlx5_mode = MLX5_ESWITCH_LEGACY;
break;
case DEVLINK_ESWITCH_MODE_SWITCHDEV:
+ case DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE:
*mlx5_mode = MLX5_ESWITCH_OFFLOADS;
break;
default:
@@ -3684,14 +3825,17 @@ static int esw_mode_from_devlink(u16 mode, u16 *mlx5_mode)
return 0;
}
-static int esw_mode_to_devlink(u16 mlx5_mode, u16 *mode)
+static int esw_mode_to_devlink(struct mlx5_eswitch *esw, u16 *mode)
{
- switch (mlx5_mode) {
+ switch (esw->mode) {
case MLX5_ESWITCH_LEGACY:
*mode = DEVLINK_ESWITCH_MODE_LEGACY;
break;
case MLX5_ESWITCH_OFFLOADS:
- *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
+ if (esw->offloads_inactive)
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE;
+ else
+ *mode = DEVLINK_ESWITCH_MODE_SWITCHDEV;
break;
default:
return -EINVAL;
@@ -3797,6 +3941,45 @@ static bool mlx5_devlink_netdev_netns_immutable_set(struct devlink *devlink,
return ret;
}
+/* Returns true when only changing between active and inactive switchdev mode */
+static bool mlx5_devlink_switchdev_active_mode_change(struct mlx5_eswitch *esw,
+ u16 devlink_mode)
+{
+ /* current mode is not switchdev */
+ if (esw->mode != MLX5_ESWITCH_OFFLOADS)
+ return false;
+
+ /* new mode is not switchdev */
+ if (devlink_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV &&
+ devlink_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE)
+ return false;
+
+ /* already inactive: no change in current state */
+ if (devlink_mode == DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE &&
+ esw->offloads_inactive)
+ return false;
+
+ /* already active: no change in current state */
+ if (devlink_mode == DEVLINK_ESWITCH_MODE_SWITCHDEV &&
+ !esw->offloads_inactive)
+ return false;
+
+ down_write(&esw->mode_lock);
+ esw->offloads_inactive = !esw->offloads_inactive;
+ esw->eswitch_operation_in_progress = true;
+ up_write(&esw->mode_lock);
+
+ if (esw->offloads_inactive)
+ mlx5_esw_fdb_inactive(esw);
+ else
+ mlx5_esw_fdb_active(esw);
+
+ down_write(&esw->mode_lock);
+ esw->eswitch_operation_in_progress = false;
+ up_write(&esw->mode_lock);
+ return true;
+}
+
int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
struct netlink_ext_ack *extack)
{
@@ -3811,12 +3994,16 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
if (esw_mode_from_devlink(mode, &mlx5_mode))
return -EINVAL;
- if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV && mlx5_get_sd(esw->dev)) {
+ if (mlx5_mode == MLX5_ESWITCH_OFFLOADS && mlx5_get_sd(esw->dev)) {
NL_SET_ERR_MSG_MOD(extack,
"Can't change E-Switch mode to switchdev when multi-PF netdev (Socket Direct) is configured.");
return -EPERM;
}
+ /* Avoid try_lock, active/inactive mode change is not restricted */
+ if (mlx5_devlink_switchdev_active_mode_change(esw, mode))
+ return 0;
+
mlx5_lag_disable_change(esw->dev);
err = mlx5_esw_try_lock(esw);
if (err < 0) {
@@ -3839,7 +4026,7 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
esw->eswitch_operation_in_progress = true;
up_write(&esw->mode_lock);
- if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV &&
+ if (mlx5_mode == MLX5_ESWITCH_OFFLOADS &&
!mlx5_devlink_netdev_netns_immutable_set(devlink, true)) {
NL_SET_ERR_MSG_MOD(extack,
"Can't change E-Switch mode to switchdev when netdev net namespace has diverged from the devlink's.");
@@ -3847,25 +4034,27 @@ int mlx5_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode,
goto skip;
}
- if (mode == DEVLINK_ESWITCH_MODE_LEGACY)
+ if (mlx5_mode == MLX5_ESWITCH_LEGACY)
esw->dev->priv.flags |= MLX5_PRIV_FLAGS_SWITCH_LEGACY;
mlx5_eswitch_disable_locked(esw);
- if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV) {
+ if (mlx5_mode == MLX5_ESWITCH_OFFLOADS) {
if (mlx5_devlink_trap_get_num_active(esw->dev)) {
NL_SET_ERR_MSG_MOD(extack,
"Can't change mode while devlink traps are active");
err = -EOPNOTSUPP;
goto skip;
}
+ esw->offloads_inactive =
+ (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV_INACTIVE);
err = esw_offloads_start(esw, extack);
- } else if (mode == DEVLINK_ESWITCH_MODE_LEGACY) {
+ } else if (mlx5_mode == MLX5_ESWITCH_LEGACY) {
err = esw_offloads_stop(esw, extack);
} else {
err = -EINVAL;
}
skip:
- if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV && err)
+ if (mlx5_mode == MLX5_ESWITCH_OFFLOADS && err)
mlx5_devlink_netdev_netns_immutable_set(devlink, false);
down_write(&esw->mode_lock);
esw->eswitch_operation_in_progress = false;
@@ -3884,7 +4073,7 @@ int mlx5_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode)
if (IS_ERR(esw))
return PTR_ERR(esw);
- return esw_mode_to_devlink(esw->mode, mode);
+ return esw_mode_to_devlink(esw, mode);
}
static int mlx5_esw_vports_inline_set(struct mlx5_eswitch *esw, u8 mlx5_mode,
@@ -4303,6 +4492,9 @@ int mlx5_devlink_port_fn_hw_addr_get(struct devlink_port *port,
struct mlx5_vport *vport = mlx5_devlink_port_vport_get(port);
mutex_lock(&esw->state_lock);
+
+ mlx5_query_nic_vport_mac_address(esw->dev, vport->vport, true,
+ vport->info.mac);
ether_addr_copy(hw_addr, vport->info.mac);
*hw_addr_len = ETH_ALEN;
mutex_unlock(&esw->state_lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
index cb1319974f83..ccef64fb40b6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/conn.c
@@ -421,6 +421,13 @@ static int mlx5_fpga_conn_create_cq(struct mlx5_fpga_conn *conn, int cq_size)
__be64 *pas;
u32 i;
+ conn->cq.mcq.cqe_sz = 64;
+ conn->cq.mcq.set_ci_db = conn->cq.wq_ctrl.db.db;
+ conn->cq.mcq.arm_db = conn->cq.wq_ctrl.db.db + 1;
+ *conn->cq.mcq.set_ci_db = 0;
+ conn->cq.mcq.vector = 0;
+ conn->cq.mcq.comp = mlx5_fpga_conn_cq_complete;
+
cq_size = roundup_pow_of_two(cq_size);
MLX5_SET(cqc, temp_cqc, log_cq_size, ilog2(cq_size));
@@ -468,15 +475,7 @@ static int mlx5_fpga_conn_create_cq(struct mlx5_fpga_conn *conn, int cq_size)
if (err)
goto err_cqwq;
- conn->cq.mcq.cqe_sz = 64;
- conn->cq.mcq.set_ci_db = conn->cq.wq_ctrl.db.db;
- conn->cq.mcq.arm_db = conn->cq.wq_ctrl.db.db + 1;
- *conn->cq.mcq.set_ci_db = 0;
- *conn->cq.mcq.arm_db = 0;
- conn->cq.mcq.vector = 0;
- conn->cq.mcq.comp = mlx5_fpga_conn_cq_complete;
tasklet_setup(&conn->cq.tasklet, mlx5_fpga_conn_cq_tasklet);
-
mlx5_fpga_dbg(fdev, "Created CQ #0x%x\n", conn->cq.mcq.cqn);
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
index e5c1012921d2..1ec61164e6b5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/core.c
@@ -211,7 +211,7 @@ int mlx5_fpga_device_start(struct mlx5_core_dev *mdev)
max_num_qps = MLX5_CAP_FPGA(mdev, shell_caps.max_num_qps);
if (!max_num_qps) {
mlx5_fpga_err(fdev, "FPGA reports 0 QPs in SHELL_CAPS\n");
- err = -ENOTSUPP;
+ err = -EOPNOTSUPP;
goto out;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
index 1af76da8b132..ced747bef641 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c
@@ -239,6 +239,10 @@ static int mlx5_cmd_update_root_ft(struct mlx5_flow_root_namespace *ns,
MLX5_SET(set_flow_table_root_in, in, vport_number, ft->vport);
MLX5_SET(set_flow_table_root_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(set_flow_table_root_in, in, eswitch_owner_vhca_id,
+ ft->esw_owner_vhca_id);
+ MLX5_SET(set_flow_table_root_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
err = mlx5_cmd_exec_in(dev, set_flow_table_root, in);
if (!err &&
@@ -302,6 +306,10 @@ static int mlx5_cmd_create_flow_table(struct mlx5_flow_root_namespace *ns,
MLX5_SET(create_flow_table_in, in, vport_number, ft->vport);
MLX5_SET(create_flow_table_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(create_flow_table_in, in, eswitch_owner_vhca_id,
+ ft->esw_owner_vhca_id);
+ MLX5_SET(create_flow_table_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
MLX5_SET(create_flow_table_in, in, flow_table_context.decap_en,
en_decap);
@@ -360,6 +368,10 @@ static int mlx5_cmd_destroy_flow_table(struct mlx5_flow_root_namespace *ns,
MLX5_SET(destroy_flow_table_in, in, vport_number, ft->vport);
MLX5_SET(destroy_flow_table_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(destroy_flow_table_in, in, eswitch_owner_vhca_id,
+ ft->esw_owner_vhca_id);
+ MLX5_SET(destroy_flow_table_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
err = mlx5_cmd_exec_in(dev, destroy_flow_table, in);
if (!err)
@@ -394,6 +406,10 @@ static int mlx5_cmd_modify_flow_table(struct mlx5_flow_root_namespace *ns,
MLX5_SET(modify_flow_table_in, in, vport_number, ft->vport);
MLX5_SET(modify_flow_table_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(modify_flow_table_in, in, eswitch_owner_vhca_id,
+ ft->esw_owner_vhca_id);
+ MLX5_SET(modify_flow_table_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
MLX5_SET(modify_flow_table_in, in, modify_field_select,
MLX5_MODIFY_FLOW_TABLE_MISS_TABLE_ID);
if (next_ft) {
@@ -429,6 +445,10 @@ static int mlx5_cmd_create_flow_group(struct mlx5_flow_root_namespace *ns,
MLX5_SET(create_flow_group_in, in, vport_number, ft->vport);
MLX5_SET(create_flow_group_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(create_flow_group_in, in, eswitch_owner_vhca_id,
+ ft->esw_owner_vhca_id);
+ MLX5_SET(create_flow_group_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
err = mlx5_cmd_exec_inout(dev, create_flow_group, in, out);
if (!err)
fg->id = MLX5_GET(create_flow_group_out, out,
@@ -451,6 +471,10 @@ static int mlx5_cmd_destroy_flow_group(struct mlx5_flow_root_namespace *ns,
MLX5_SET(destroy_flow_group_in, in, vport_number, ft->vport);
MLX5_SET(destroy_flow_group_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(destroy_flow_group_in, in, eswitch_owner_vhca_id,
+ ft->esw_owner_vhca_id);
+ MLX5_SET(destroy_flow_group_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
return mlx5_cmd_exec_in(dev, destroy_flow_group, in);
}
@@ -559,6 +583,9 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev,
MLX5_SET(set_fte_in, in, vport_number, ft->vport);
MLX5_SET(set_fte_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(set_fte_in, in, eswitch_owner_vhca_id, ft->esw_owner_vhca_id);
+ MLX5_SET(set_fte_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
in_flow_context = MLX5_ADDR_OF(set_fte_in, in, flow_context);
MLX5_SET(flow_context, in_flow_context, group_id, group_id);
@@ -788,6 +815,10 @@ static int mlx5_cmd_delete_fte(struct mlx5_flow_root_namespace *ns,
MLX5_SET(delete_fte_in, in, vport_number, ft->vport);
MLX5_SET(delete_fte_in, in, other_vport,
!!(ft->flags & MLX5_FLOW_TABLE_OTHER_VPORT));
+ MLX5_SET(delete_fte_in, in, eswitch_owner_vhca_id,
+ ft->esw_owner_vhca_id);
+ MLX5_SET(delete_fte_in, in, other_eswitch,
+ !!(ft->flags & MLX5_FLOW_TABLE_OTHER_ESWITCH));
return mlx5_cmd_exec_in(dev, delete_fte, in);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
index 2db3ffb0a2b2..0a6031a64c6f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c
@@ -939,10 +939,10 @@ static struct mlx5_flow_group *alloc_insert_flow_group(struct mlx5_flow_table *f
return fg;
}
-static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport,
- enum fs_flow_table_type table_type,
- enum fs_flow_table_op_mod op_mod,
- u32 flags)
+static struct mlx5_flow_table *
+alloc_flow_table(struct mlx5_flow_table_attr *ft_attr, u16 vport,
+ enum fs_flow_table_type table_type,
+ enum fs_flow_table_op_mod op_mod)
{
struct mlx5_flow_table *ft;
int ret;
@@ -957,12 +957,13 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport,
return ERR_PTR(ret);
}
- ft->level = level;
+ ft->level = ft_attr->level;
ft->node.type = FS_TYPE_FLOW_TABLE;
ft->op_mod = op_mod;
ft->type = table_type;
ft->vport = vport;
- ft->flags = flags;
+ ft->esw_owner_vhca_id = ft_attr->esw_owner_vhca_id;
+ ft->flags = ft_attr->flags;
INIT_LIST_HEAD(&ft->fwd_rules);
mutex_init(&ft->lock);
@@ -1370,10 +1371,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa
/* The level is related to the
* priority level range.
*/
- ft = alloc_flow_table(ft_attr->level,
- vport,
- root->table_type,
- op_mod, ft_attr->flags);
+ ft = alloc_flow_table(ft_attr, vport, root->table_type, op_mod);
if (IS_ERR(ft)) {
err = PTR_ERR(ft);
goto unlock_root;
@@ -3310,6 +3308,62 @@ err:
return ret;
}
+static bool mlx5_fs_ns_is_empty(struct mlx5_flow_namespace *ns)
+{
+ struct fs_prio *iter_prio;
+
+ fs_for_each_prio(iter_prio, ns) {
+ if (iter_prio->num_ft)
+ return false;
+ }
+
+ return true;
+}
+
+int mlx5_fs_set_root_dev(struct mlx5_core_dev *dev,
+ struct mlx5_core_dev *new_dev,
+ enum fs_flow_table_type table_type)
+{
+ struct mlx5_flow_root_namespace **root;
+ int total_vports;
+ int i;
+
+ switch (table_type) {
+ case FS_FT_RDMA_TRANSPORT_TX:
+ root = dev->priv.steering->rdma_transport_tx_root_ns;
+ total_vports = dev->priv.steering->rdma_transport_tx_vports;
+ break;
+ case FS_FT_RDMA_TRANSPORT_RX:
+ root = dev->priv.steering->rdma_transport_rx_root_ns;
+ total_vports = dev->priv.steering->rdma_transport_rx_vports;
+ break;
+ default:
+ WARN_ON_ONCE(true);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < total_vports; i++) {
+ mutex_lock(&root[i]->chain_lock);
+ if (!mlx5_fs_ns_is_empty(&root[i]->ns)) {
+ mutex_unlock(&root[i]->chain_lock);
+ goto err;
+ }
+ root[i]->dev = new_dev;
+ mutex_unlock(&root[i]->chain_lock);
+ }
+ return 0;
+err:
+ while (i--) {
+ mutex_lock(&root[i]->chain_lock);
+ root[i]->dev = dev;
+ mutex_unlock(&root[i]->chain_lock);
+ }
+ /* If you hit this error try destroying all flow tables and try again */
+ mlx5_core_err(dev, "Failed to set root device for RDMA TRANSPORT\n");
+ return -EINVAL;
+}
+EXPORT_SYMBOL(mlx5_fs_set_root_dev);
+
static int init_rdma_transport_rx_root_ns(struct mlx5_flow_steering *steering)
{
struct mlx5_core_dev *dev = steering->dev;
@@ -3520,6 +3574,11 @@ static int init_fdb_root_ns(struct mlx5_flow_steering *steering)
if (!steering->fdb_root_ns)
return -ENOMEM;
+ maj_prio = fs_create_prio(&steering->fdb_root_ns->ns, FDB_DROP_ROOT, 1);
+ err = PTR_ERR_OR_ZERO(maj_prio);
+ if (err)
+ goto out_err;
+
err = create_fdb_bypass(steering);
if (err)
goto out_err;
@@ -3774,7 +3833,8 @@ static int mlx5_fs_mode_set(struct devlink *devlink, u32 id,
}
static int mlx5_fs_mode_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
index 8458ce203dac..1c6591425260 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h
@@ -103,24 +103,6 @@ enum fs_node_type {
FS_TYPE_FLOW_DEST
};
-enum fs_flow_table_type {
- FS_FT_NIC_RX = 0x0,
- FS_FT_NIC_TX = 0x1,
- FS_FT_ESW_EGRESS_ACL = 0x2,
- FS_FT_ESW_INGRESS_ACL = 0x3,
- FS_FT_FDB = 0X4,
- FS_FT_SNIFFER_RX = 0X5,
- FS_FT_SNIFFER_TX = 0X6,
- FS_FT_RDMA_RX = 0X7,
- FS_FT_RDMA_TX = 0X8,
- FS_FT_PORT_SEL = 0X9,
- FS_FT_FDB_RX = 0xa,
- FS_FT_FDB_TX = 0xb,
- FS_FT_RDMA_TRANSPORT_RX = 0xd,
- FS_FT_RDMA_TRANSPORT_TX = 0xe,
- FS_FT_MAX_TYPE = FS_FT_RDMA_TRANSPORT_TX,
-};
-
enum fs_flow_table_op_mod {
FS_FT_OP_MOD_NORMAL,
FS_FT_OP_MOD_LAG_DEMUX,
@@ -205,6 +187,7 @@ struct mlx5_flow_table {
};
u32 id;
u16 vport;
+ u16 esw_owner_vhca_id;
unsigned int max_fte;
unsigned int level;
enum fs_flow_table_type type;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
index 89e399606877..2bceb42c98cc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/fw_reset.c
@@ -73,7 +73,8 @@ static int mlx5_fw_reset_enable_remote_dev_reset_set(struct devlink *devlink, u3
}
static int mlx5_fw_reset_enable_remote_dev_reset_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
struct mlx5_fw_reset *fw_reset;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c
index 4b3430ac3905..3b2f54ca30a8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c
@@ -266,21 +266,18 @@ static int mlx5i_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
return mlx5e_ethtool_set_rxnfc(priv, cmd);
}
+static u32 mlx5i_get_rx_ring_count(struct net_device *dev)
+{
+ struct mlx5e_priv *priv = mlx5i_epriv(dev);
+
+ return priv->channels.params.num_channels;
+}
+
static int mlx5i_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
u32 *rule_locs)
{
struct mlx5e_priv *priv = mlx5i_epriv(dev);
- /* ETHTOOL_GRXRINGS is needed by ethtool -x which is not part
- * of rxnfc. We keep this logic out of mlx5e_ethtool_get_rxnfc,
- * to avoid breaking "ethtool -x" when mlx5e_ethtool_get_rxnfc
- * is compiled out via CONFIG_MLX5_EN_RXNFC=n.
- */
- if (info->cmd == ETHTOOL_GRXRINGS) {
- info->data = priv->channels.params.num_channels;
- return 0;
- }
-
return mlx5e_ethtool_get_rxnfc(priv, info, rule_locs);
}
@@ -304,6 +301,7 @@ const struct ethtool_ops mlx5i_ethtool_ops = {
.set_rxfh_fields = mlx5i_set_rxfh_fields,
.get_rxnfc = mlx5i_get_rxnfc,
.set_rxnfc = mlx5i_set_rxnfc,
+ .get_rx_ring_count = mlx5i_get_rx_ring_count,
.get_link_ksettings = mlx5i_get_link_ksettings,
.get_link = ethtool_op_get_link,
};
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
index 79ae3a51a4b3..0a6003fe60e9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
@@ -45,6 +45,23 @@ static int mlx5i_open(struct net_device *netdev);
static int mlx5i_close(struct net_device *netdev);
static int mlx5i_change_mtu(struct net_device *netdev, int new_mtu);
+int mlx5i_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct mlx5e_priv *epriv = mlx5i_epriv(dev);
+
+ return mlx5e_hwtstamp_set(epriv, config, extack);
+}
+
+int mlx5i_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct mlx5e_priv *epriv = mlx5i_epriv(dev);
+
+ return mlx5e_hwtstamp_get(epriv, config);
+}
+
static const struct net_device_ops mlx5i_netdev_ops = {
.ndo_open = mlx5i_open,
.ndo_stop = mlx5i_close,
@@ -52,7 +69,8 @@ static const struct net_device_ops mlx5i_netdev_ops = {
.ndo_init = mlx5i_dev_init,
.ndo_uninit = mlx5i_dev_cleanup,
.ndo_change_mtu = mlx5i_change_mtu,
- .ndo_eth_ioctl = mlx5i_ioctl,
+ .ndo_hwtstamp_get = mlx5i_hwtstamp_get,
+ .ndo_hwtstamp_set = mlx5i_hwtstamp_set,
};
/* IPoIB mlx5 netdev profile */
@@ -316,7 +334,7 @@ void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, u32 qpn)
int mlx5i_update_nic_rx(struct mlx5e_priv *priv)
{
- return mlx5e_refresh_tirs(priv, true, true);
+ return mlx5e_refresh_tirs(priv->mdev, true, true);
}
int mlx5i_create_tis(struct mlx5_core_dev *mdev, u32 underlay_qpn, u32 *tisn)
@@ -409,6 +427,7 @@ static void mlx5i_destroy_flow_steering(struct mlx5e_priv *priv)
static int mlx5i_init_rx(struct mlx5e_priv *priv)
{
struct mlx5_core_dev *mdev = priv->mdev;
+ enum mlx5e_rx_res_features features;
int err;
priv->fs = mlx5e_fs_init(priv->profile, mdev,
@@ -427,7 +446,9 @@ static int mlx5i_init_rx(struct mlx5e_priv *priv)
goto err_destroy_q_counters;
}
- priv->rx_res = mlx5e_rx_res_create(priv->mdev, 0, priv->max_nch, priv->drop_rq.rqn,
+ features = MLX5E_RX_RES_FEATURE_SELF_LB_BLOCK;
+ priv->rx_res = mlx5e_rx_res_create(priv->mdev, features, priv->max_nch,
+ priv->drop_rq.rqn,
&priv->channels.params.packet_merge,
priv->channels.params.num_channels);
if (IS_ERR(priv->rx_res)) {
@@ -557,20 +578,6 @@ int mlx5i_dev_init(struct net_device *dev)
return 0;
}
-int mlx5i_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct mlx5e_priv *priv = mlx5i_epriv(dev);
-
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return mlx5e_hwstamp_set(priv, ifr);
- case SIOCGHWTSTAMP:
- return mlx5e_hwstamp_get(priv, ifr);
- default:
- return -EOPNOTSUPP;
- }
-}
-
void mlx5i_dev_cleanup(struct net_device *dev)
{
struct mlx5e_priv *priv = mlx5i_epriv(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
index 2ab6437a1c49..d67d5a72bb41 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
@@ -88,7 +88,11 @@ struct net_device *mlx5i_pkey_get_netdev(struct net_device *netdev, u32 qpn);
/* Shared ndo functions */
int mlx5i_dev_init(struct net_device *dev);
void mlx5i_dev_cleanup(struct net_device *dev);
-int mlx5i_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+int mlx5i_hwtstamp_set(struct net_device *dev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
+int mlx5i_hwtstamp_get(struct net_device *dev,
+ struct kernel_hwtstamp_config *config);
/* Parent profile functions */
int mlx5i_init(struct mlx5_core_dev *mdev, struct net_device *netdev);
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 028a76944d82..04444dad3a0d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
@@ -140,7 +140,6 @@ static int mlx5i_pkey_close(struct net_device *netdev);
static int mlx5i_pkey_dev_init(struct net_device *dev);
static void mlx5i_pkey_dev_cleanup(struct net_device *netdev);
static int mlx5i_pkey_change_mtu(struct net_device *netdev, int new_mtu);
-static int mlx5i_pkey_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static const struct net_device_ops mlx5i_pkey_netdev_ops = {
.ndo_open = mlx5i_pkey_open,
@@ -149,7 +148,8 @@ static const struct net_device_ops mlx5i_pkey_netdev_ops = {
.ndo_get_stats64 = mlx5i_get_stats,
.ndo_uninit = mlx5i_pkey_dev_cleanup,
.ndo_change_mtu = mlx5i_pkey_change_mtu,
- .ndo_eth_ioctl = mlx5i_pkey_ioctl,
+ .ndo_hwtstamp_get = mlx5i_hwtstamp_get,
+ .ndo_hwtstamp_set = mlx5i_hwtstamp_set,
};
/* Child NDOs */
@@ -184,11 +184,6 @@ static int mlx5i_pkey_dev_init(struct net_device *dev)
return mlx5i_dev_init(dev);
}
-static int mlx5i_pkey_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- return mlx5i_ioctl(dev, ifr, cmd);
-}
-
static void mlx5i_pkey_dev_cleanup(struct net_device *netdev)
{
mlx5i_parent_put(netdev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index 59c00c911275..1ac933cd8f02 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -1418,10 +1418,12 @@ static void mlx5_lag_unregister_hca_devcom_comp(struct mlx5_core_dev *dev)
static int mlx5_lag_register_hca_devcom_comp(struct mlx5_core_dev *dev)
{
struct mlx5_devcom_match_attr attr = {
- .key.val = mlx5_query_nic_system_image_guid(dev),
.flags = MLX5_DEVCOM_MATCH_FLAGS_NS,
.net = mlx5_core_net(dev),
};
+ u8 len __always_unused;
+
+ mlx5_query_nic_sw_system_image_guid(dev, attr.key.buf, &len);
/* This component is use to sync adding core_dev to lag_dev and to sync
* changes of mlx5_adev_devices between LAG layer and other layers.
@@ -1430,11 +1432,10 @@ static int mlx5_lag_register_hca_devcom_comp(struct mlx5_core_dev *dev)
mlx5_devcom_register_component(dev->priv.devc,
MLX5_DEVCOM_HCA_PORTS,
&attr, NULL, dev);
- if (IS_ERR(dev->priv.hca_devcom_comp)) {
+ if (!dev->priv.hca_devcom_comp) {
mlx5_core_err(dev,
- "Failed to register devcom HCA component, err: %ld\n",
- PTR_ERR(dev->priv.hca_devcom_comp));
- return PTR_ERR(dev->priv.hca_devcom_comp);
+ "Failed to register devcom HCA component.");
+ return -EINVAL;
}
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
index d0ba83d77cd1..0ba0ef8bae42 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
@@ -1432,19 +1432,21 @@ static int mlx5_clock_alloc(struct mlx5_core_dev *mdev, bool shared)
return 0;
}
-static void mlx5_shared_clock_register(struct mlx5_core_dev *mdev, u64 key)
+static void mlx5_shared_clock_register(struct mlx5_core_dev *mdev,
+ u8 identity[MLX5_RT_CLOCK_IDENTITY_SIZE])
{
struct mlx5_core_dev *peer_dev, *next = NULL;
- struct mlx5_devcom_match_attr attr = {
- .key.val = key,
- };
+ struct mlx5_devcom_match_attr attr = {};
struct mlx5_devcom_comp_dev *compd;
struct mlx5_devcom_comp_dev *pos;
+ BUILD_BUG_ON(MLX5_RT_CLOCK_IDENTITY_SIZE > MLX5_DEVCOM_MATCH_KEY_MAX);
+ memcpy(attr.key.buf, identity, MLX5_RT_CLOCK_IDENTITY_SIZE);
+
compd = mlx5_devcom_register_component(mdev->priv.devc,
MLX5_DEVCOM_SHARED_CLOCK,
&attr, NULL, mdev);
- if (IS_ERR(compd))
+ if (!compd)
return;
mdev->clock_state->compdev = compd;
@@ -1594,7 +1596,6 @@ int mlx5_init_clock(struct mlx5_core_dev *mdev)
{
u8 identity[MLX5_RT_CLOCK_IDENTITY_SIZE];
struct mlx5_clock_dev_state *clock_state;
- u64 key;
int err;
if (!MLX5_CAP_GEN(mdev, device_frequency_khz)) {
@@ -1610,12 +1611,10 @@ int mlx5_init_clock(struct mlx5_core_dev *mdev)
mdev->clock_state = clock_state;
if (MLX5_CAP_MCAM_REG3(mdev, mrtcq) && mlx5_real_time_mode(mdev)) {
- if (mlx5_clock_identity_get(mdev, identity)) {
+ if (mlx5_clock_identity_get(mdev, identity))
mlx5_core_warn(mdev, "failed to get rt clock identity, create ptp dev per function\n");
- } else {
- memcpy(&key, &identity, sizeof(key));
- mlx5_shared_clock_register(mdev, key);
- }
+ else
+ mlx5_shared_clock_register(mdev, identity);
}
if (!mdev->clock) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h
index c18a652c0faa..aff3aed62c74 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h
@@ -54,7 +54,6 @@ struct mlx5_timer {
struct mlx5_clock {
seqlock_t lock;
- struct hwtstamp_config hwtstamp_config;
struct ptp_clock *ptp;
struct ptp_clock_info ptp_info;
struct mlx5_pps pps_info;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
index faa2833602c8..e749618229bc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.c
@@ -76,20 +76,18 @@ mlx5_devcom_dev_alloc(struct mlx5_core_dev *dev)
struct mlx5_devcom_dev *
mlx5_devcom_register_device(struct mlx5_core_dev *dev)
{
- struct mlx5_devcom_dev *devc;
+ struct mlx5_devcom_dev *devc = NULL;
mutex_lock(&dev_list_lock);
if (devcom_dev_exists(dev)) {
- devc = ERR_PTR(-EEXIST);
+ mlx5_core_err(dev, "devcom device already exists");
goto out;
}
devc = mlx5_devcom_dev_alloc(dev);
- if (!devc) {
- devc = ERR_PTR(-ENOMEM);
+ if (!devc)
goto out;
- }
list_add_tail(&devc->list, &devcom_dev_list);
out:
@@ -110,8 +108,10 @@ mlx5_devcom_dev_release(struct kref *ref)
void mlx5_devcom_unregister_device(struct mlx5_devcom_dev *devc)
{
- if (!IS_ERR_OR_NULL(devc))
- kref_put(&devc->ref, mlx5_devcom_dev_release);
+ if (!devc)
+ return;
+
+ kref_put(&devc->ref, mlx5_devcom_dev_release);
}
static struct mlx5_devcom_comp *
@@ -122,7 +122,7 @@ mlx5_devcom_comp_alloc(u64 id, const struct mlx5_devcom_match_attr *attr,
comp = kzalloc(sizeof(*comp), GFP_KERNEL);
if (!comp)
- return ERR_PTR(-ENOMEM);
+ return NULL;
comp->id = id;
comp->key.key = attr->key;
@@ -160,7 +160,7 @@ devcom_alloc_comp_dev(struct mlx5_devcom_dev *devc,
devcom = kzalloc(sizeof(*devcom), GFP_KERNEL);
if (!devcom)
- return ERR_PTR(-ENOMEM);
+ return NULL;
kref_get(&devc->ref);
devcom->devc = devc;
@@ -240,31 +240,28 @@ mlx5_devcom_register_component(struct mlx5_devcom_dev *devc,
mlx5_devcom_event_handler_t handler,
void *data)
{
- struct mlx5_devcom_comp_dev *devcom;
+ struct mlx5_devcom_comp_dev *devcom = NULL;
struct mlx5_devcom_comp *comp;
- if (IS_ERR_OR_NULL(devc))
- return ERR_PTR(-EINVAL);
+ if (!devc)
+ return NULL;
mutex_lock(&comp_list_lock);
comp = devcom_component_get(devc, id, attr, handler);
- if (IS_ERR(comp)) {
- devcom = ERR_PTR(-EINVAL);
+ if (IS_ERR(comp))
goto out_unlock;
- }
if (!comp) {
comp = mlx5_devcom_comp_alloc(id, attr, handler);
- if (IS_ERR(comp)) {
- devcom = ERR_CAST(comp);
+ if (!comp)
goto out_unlock;
- }
+
list_add_tail(&comp->comp_list, &devcom_comp_list);
}
mutex_unlock(&comp_list_lock);
devcom = devcom_alloc_comp_dev(devc, comp, data);
- if (IS_ERR(devcom))
+ if (!devcom)
kref_put(&comp->ref, mlx5_devcom_comp_release);
return devcom;
@@ -276,8 +273,10 @@ out_unlock:
void mlx5_devcom_unregister_component(struct mlx5_devcom_comp_dev *devcom)
{
- if (!IS_ERR_OR_NULL(devcom))
- devcom_free_comp_dev(devcom);
+ if (!devcom)
+ return;
+
+ devcom_free_comp_dev(devcom);
}
int mlx5_devcom_comp_get_size(struct mlx5_devcom_comp_dev *devcom)
@@ -296,7 +295,7 @@ int mlx5_devcom_send_event(struct mlx5_devcom_comp_dev *devcom,
int err = 0;
void *data;
- if (IS_ERR_OR_NULL(devcom))
+ if (!devcom)
return -ENODEV;
comp = devcom->comp;
@@ -338,7 +337,7 @@ void mlx5_devcom_comp_set_ready(struct mlx5_devcom_comp_dev *devcom, bool ready)
bool mlx5_devcom_comp_is_ready(struct mlx5_devcom_comp_dev *devcom)
{
- if (IS_ERR_OR_NULL(devcom))
+ if (!devcom)
return false;
return READ_ONCE(devcom->comp->ready);
@@ -348,7 +347,7 @@ bool mlx5_devcom_for_each_peer_begin(struct mlx5_devcom_comp_dev *devcom)
{
struct mlx5_devcom_comp *comp;
- if (IS_ERR_OR_NULL(devcom))
+ if (!devcom)
return false;
comp = devcom->comp;
@@ -421,21 +420,21 @@ void *mlx5_devcom_get_next_peer_data_rcu(struct mlx5_devcom_comp_dev *devcom,
void mlx5_devcom_comp_lock(struct mlx5_devcom_comp_dev *devcom)
{
- if (IS_ERR_OR_NULL(devcom))
+ if (!devcom)
return;
down_write(&devcom->comp->sem);
}
void mlx5_devcom_comp_unlock(struct mlx5_devcom_comp_dev *devcom)
{
- if (IS_ERR_OR_NULL(devcom))
+ if (!devcom)
return;
up_write(&devcom->comp->sem);
}
int mlx5_devcom_comp_trylock(struct mlx5_devcom_comp_dev *devcom)
{
- if (IS_ERR_OR_NULL(devcom))
+ if (!devcom)
return 0;
return down_write_trylock(&devcom->comp->sem);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h
index 609c85f47917..91e5ae529d5c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/devcom.h
@@ -10,8 +10,10 @@ enum mlx5_devom_match_flags {
MLX5_DEVCOM_MATCH_FLAGS_NS = BIT(0),
};
+#define MLX5_DEVCOM_MATCH_KEY_MAX 32
union mlx5_devcom_match_key {
u64 val;
+ u8 buf[MLX5_DEVCOM_MATCH_KEY_MAX];
};
struct mlx5_devcom_match_attr {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c
index 4450091e181a..4a88a42ae4f7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c
@@ -65,13 +65,14 @@ static int del_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index)
/* UC L2 table hash node */
struct l2table_node {
struct l2addr_node node;
- u32 index; /* index in HW l2 table */
+ int index; /* index in HW l2 table */
int ref_count;
};
struct mlx5_mpfs {
struct hlist_head hash[MLX5_L2_ADDR_HASH_SIZE];
struct mutex lock; /* Synchronize l2 table access */
+ bool enabled;
u32 size;
unsigned long *bitmap;
};
@@ -114,6 +115,8 @@ int mlx5_mpfs_init(struct mlx5_core_dev *dev)
return -ENOMEM;
}
+ mpfs->enabled = true;
+
dev->priv.mpfs = mpfs;
return 0;
}
@@ -135,7 +138,7 @@ int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac)
struct mlx5_mpfs *mpfs = dev->priv.mpfs;
struct l2table_node *l2addr;
int err = 0;
- u32 index;
+ int index;
if (!mpfs)
return 0;
@@ -148,30 +151,34 @@ int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac)
goto out;
}
- err = alloc_l2table_index(mpfs, &index);
- if (err)
- goto out;
-
l2addr = l2addr_hash_add(mpfs->hash, mac, struct l2table_node, GFP_KERNEL);
if (!l2addr) {
err = -ENOMEM;
- goto hash_add_err;
+ goto out;
}
- err = set_l2table_entry_cmd(dev, index, mac);
- if (err)
- goto set_table_entry_err;
+ index = -1;
+
+ if (mpfs->enabled) {
+ err = alloc_l2table_index(mpfs, &index);
+ if (err)
+ goto hash_del;
+ err = set_l2table_entry_cmd(dev, index, mac);
+ if (err)
+ goto free_l2table_index;
+ mlx5_core_dbg(dev, "MPFS entry %pM, set @index (%d)\n",
+ l2addr->node.addr, index);
+ }
l2addr->index = index;
l2addr->ref_count = 1;
mlx5_core_dbg(dev, "MPFS mac added %pM, index (%d)\n", mac, index);
goto out;
-
-set_table_entry_err:
- l2addr_hash_del(l2addr);
-hash_add_err:
+free_l2table_index:
free_l2table_index(mpfs, index);
+hash_del:
+ l2addr_hash_del(l2addr);
out:
mutex_unlock(&mpfs->lock);
return err;
@@ -183,7 +190,7 @@ int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac)
struct mlx5_mpfs *mpfs = dev->priv.mpfs;
struct l2table_node *l2addr;
int err = 0;
- u32 index;
+ int index;
if (!mpfs)
return 0;
@@ -200,12 +207,87 @@ int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac)
goto unlock;
index = l2addr->index;
- del_l2table_entry_cmd(dev, index);
+ if (index >= 0) {
+ del_l2table_entry_cmd(dev, index);
+ free_l2table_index(mpfs, index);
+ mlx5_core_dbg(dev, "MPFS entry %pM, deleted @index (%d)\n",
+ mac, index);
+ }
l2addr_hash_del(l2addr);
- free_l2table_index(mpfs, index);
mlx5_core_dbg(dev, "MPFS mac deleted %pM, index (%d)\n", mac, index);
unlock:
mutex_unlock(&mpfs->lock);
return err;
}
EXPORT_SYMBOL(mlx5_mpfs_del_mac);
+
+int mlx5_mpfs_enable(struct mlx5_core_dev *dev)
+{
+ struct mlx5_mpfs *mpfs = dev->priv.mpfs;
+ struct l2table_node *l2addr;
+ struct hlist_node *n;
+ int err = 0, i;
+
+ if (!mpfs)
+ return -ENODEV;
+
+ mutex_lock(&mpfs->lock);
+ if (mpfs->enabled)
+ goto out;
+ mpfs->enabled = true;
+ mlx5_core_dbg(dev, "MPFS enabling mpfs\n");
+
+ mlx5_mpfs_foreach(l2addr, n, mpfs, i) {
+ u32 index;
+
+ err = alloc_l2table_index(mpfs, &index);
+ if (err) {
+ mlx5_core_err(dev, "Failed to allocated MPFS index for %pM, err(%d)\n",
+ l2addr->node.addr, err);
+ goto out;
+ }
+
+ err = set_l2table_entry_cmd(dev, index, l2addr->node.addr);
+ if (err) {
+ mlx5_core_err(dev, "Failed to set MPFS l2table entry for %pM index=%d, err(%d)\n",
+ l2addr->node.addr, index, err);
+ free_l2table_index(mpfs, index);
+ goto out;
+ }
+
+ l2addr->index = index;
+ mlx5_core_dbg(dev, "MPFS entry %pM, set @index (%d)\n",
+ l2addr->node.addr, l2addr->index);
+ }
+out:
+ mutex_unlock(&mpfs->lock);
+ return err;
+}
+
+void mlx5_mpfs_disable(struct mlx5_core_dev *dev)
+{
+ struct mlx5_mpfs *mpfs = dev->priv.mpfs;
+ struct l2table_node *l2addr;
+ struct hlist_node *n;
+ int i;
+
+ if (!mpfs)
+ return;
+
+ mutex_lock(&mpfs->lock);
+ if (!mpfs->enabled)
+ goto unlock;
+ mlx5_mpfs_foreach(l2addr, n, mpfs, i) {
+ if (l2addr->index < 0)
+ continue;
+ del_l2table_entry_cmd(dev, l2addr->index);
+ free_l2table_index(mpfs, l2addr->index);
+ mlx5_core_dbg(dev, "MPFS entry %pM, deleted @index (%d)\n",
+ l2addr->node.addr, l2addr->index);
+ l2addr->index = -1;
+ }
+ mpfs->enabled = false;
+ mlx5_core_dbg(dev, "MPFS disabled\n");
+unlock:
+ mutex_unlock(&mpfs->lock);
+}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h
index 4a293542a7aa..9c63838ce1f3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h
@@ -45,6 +45,10 @@ struct l2addr_node {
u8 addr[ETH_ALEN];
};
+#define mlx5_mpfs_foreach(hs, tmp, mpfs, i) \
+ for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \
+ hlist_for_each_entry_safe(hs, tmp, &(mpfs)->hash[i], node.hlist)
+
#define for_each_l2hash_node(hn, tmp, hash, i) \
for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \
hlist_for_each_entry_safe(hn, tmp, &(hash)[i], hlist)
@@ -82,11 +86,16 @@ struct l2addr_node {
})
#ifdef CONFIG_MLX5_MPFS
+struct mlx5_core_dev;
int mlx5_mpfs_init(struct mlx5_core_dev *dev);
void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev);
+int mlx5_mpfs_enable(struct mlx5_core_dev *dev);
+void mlx5_mpfs_disable(struct mlx5_core_dev *dev);
#else /* #ifndef CONFIG_MLX5_MPFS */
static inline int mlx5_mpfs_init(struct mlx5_core_dev *dev) { return 0; }
static inline void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev) {}
+static inline int mlx5_mpfs_enable(struct mlx5_core_dev *dev) { return 0; }
+static inline void mlx5_mpfs_disable(struct mlx5_core_dev *dev) {}
#endif
#endif
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c
index 459a0b4d08e6..19bb620b7436 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/nv_param.c
@@ -8,6 +8,8 @@ enum {
MLX5_CLASS_0_CTRL_ID_NV_GLOBAL_PCI_CONF = 0x80,
MLX5_CLASS_0_CTRL_ID_NV_GLOBAL_PCI_CAP = 0x81,
MLX5_CLASS_0_CTRL_ID_NV_SW_OFFLOAD_CONFIG = 0x10a,
+ MLX5_CLASS_0_CTRL_ID_NV_SW_OFFLOAD_CAP = 0x10b,
+ MLX5_CLASS_0_CTRL_ID_NV_SW_ACCELERATE_CONF = 0x11d,
MLX5_CLASS_3_CTRL_ID_NV_PF_PCI_CONF = 0x80,
};
@@ -32,6 +34,12 @@ union mlx5_ifc_config_item_type_auto_bits {
u8 reserved_at_0[0x20];
};
+enum {
+ MLX5_ACCESS_MODE_NEXT = 0,
+ MLX5_ACCESS_MODE_CURRENT,
+ MLX5_ACCESS_MODE_DEFAULT,
+};
+
struct mlx5_ifc_config_item_bits {
u8 valid[0x2];
u8 priority[0x2];
@@ -123,6 +131,17 @@ struct mlx5_ifc_nv_sw_offload_conf_bits {
u8 lro_log_timeout0[0x4];
};
+struct mlx5_ifc_nv_sw_offload_cap_bits {
+ u8 reserved_at_0[0x19];
+ u8 swp_l4_csum_mode_l4_only[0x1];
+ u8 reserved_at_1a[0x6];
+};
+
+struct mlx5_ifc_nv_sw_accelerate_conf_bits {
+ u8 swp_l4_csum_mode[0x2];
+ u8 reserved_at_2[0x3e];
+};
+
#define MNVDA_HDR_SZ \
(MLX5_ST_SZ_BYTES(mnvda_reg) - \
MLX5_BYTE_OFF(mnvda_reg, configuration_item_data))
@@ -195,12 +214,39 @@ mlx5_nv_param_read_sw_offload_conf(struct mlx5_core_dev *dev, void *mnvda,
return mlx5_nv_param_read(dev, mnvda, len);
}
+static int
+mlx5_nv_param_read_sw_offload_cap(struct mlx5_core_dev *dev, void *mnvda,
+ size_t len)
+{
+ MLX5_SET_CFG_ITEM_TYPE(global, mnvda, type_class, 0);
+ MLX5_SET_CFG_ITEM_TYPE(global, mnvda, parameter_index,
+ MLX5_CLASS_0_CTRL_ID_NV_SW_OFFLOAD_CAP);
+ MLX5_SET_CFG_HDR_LEN(mnvda, nv_sw_offload_cap);
+
+ return mlx5_nv_param_read(dev, mnvda, len);
+}
+
+static int
+mlx5_nv_param_read_sw_accelerate_conf(struct mlx5_core_dev *dev, void *mnvda,
+ size_t len, int access_mode)
+{
+ MLX5_SET_CFG_ITEM_TYPE(global, mnvda, type_class, 0);
+ MLX5_SET_CFG_ITEM_TYPE(global, mnvda, parameter_index,
+ MLX5_CLASS_0_CTRL_ID_NV_SW_ACCELERATE_CONF);
+ MLX5_SET_CFG_HDR_LEN(mnvda, nv_sw_accelerate_conf);
+ MLX5_SET(mnvda_reg, mnvda, configuration_item_header.access_mode,
+ access_mode);
+
+ return mlx5_nv_param_read(dev, mnvda, len);
+}
+
static const char *const
cqe_compress_str[] = { "balanced", "aggressive" };
static int
mlx5_nv_param_devlink_cqe_compress_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
u32 mnvda[MLX5_ST_SZ_DW(mnvda_reg)] = {};
@@ -268,6 +314,182 @@ mlx5_nv_param_devlink_cqe_compress_set(struct devlink *devlink, u32 id,
return mlx5_nv_param_write(dev, mnvda, sizeof(mnvda));
}
+enum swp_l4_csum_mode {
+ SWP_L4_CSUM_MODE_DEFAULT = 0,
+ SWP_L4_CSUM_MODE_FULL_CSUM = 1,
+ SWP_L4_CSUM_MODE_L4_ONLY = 2,
+};
+
+static const char *const
+ swp_l4_csum_mode_str[] = { "default", "full_csum", "l4_only" };
+
+static int
+mlx5_swp_l4_csum_mode_get(struct devlink *devlink, u32 id,
+ int access_mode, u8 *value,
+ struct netlink_ext_ack *extack)
+{
+ struct mlx5_core_dev *dev = devlink_priv(devlink);
+ u32 mnvda[MLX5_ST_SZ_DW(mnvda_reg)] = {};
+ void *data;
+ int err;
+
+ err = mlx5_nv_param_read_sw_accelerate_conf(dev, mnvda, sizeof(mnvda),
+ access_mode);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed to read sw_accelerate_conf mnvda reg");
+ return err;
+ }
+
+ data = MLX5_ADDR_OF(mnvda_reg, mnvda, configuration_item_data);
+ *value = MLX5_GET(nv_sw_accelerate_conf, data, swp_l4_csum_mode);
+
+ if (*value >= ARRAY_SIZE(swp_l4_csum_mode_str)) {
+ NL_SET_ERR_MSG_FMT_MOD(extack,
+ "Invalid swp_l4_csum_mode value %u read from device",
+ *value);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+mlx5_devlink_swp_l4_csum_mode_get(struct devlink *devlink, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ u8 value;
+ int err;
+
+ err = mlx5_swp_l4_csum_mode_get(devlink, id, MLX5_ACCESS_MODE_NEXT,
+ &value, extack);
+ if (err)
+ return err;
+
+ strscpy(ctx->val.vstr, swp_l4_csum_mode_str[value],
+ sizeof(ctx->val.vstr));
+ return 0;
+}
+
+static int
+mlx5_devlink_swp_l4_csum_mode_validate(struct devlink *devlink, u32 id,
+ union devlink_param_value val,
+ struct netlink_ext_ack *extack)
+{
+ struct mlx5_core_dev *dev = devlink_priv(devlink);
+ u32 cap[MLX5_ST_SZ_DW(mnvda_reg)] = {};
+ void *data;
+ int err, i;
+
+ for (i = 0; i < ARRAY_SIZE(swp_l4_csum_mode_str); i++) {
+ if (!strcmp(val.vstr, swp_l4_csum_mode_str[i]))
+ break;
+ }
+
+ if (i >= ARRAY_SIZE(swp_l4_csum_mode_str) ||
+ i == SWP_L4_CSUM_MODE_DEFAULT) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Invalid value, supported values are full_csum/l4_only");
+ return -EINVAL;
+ }
+
+ if (i == SWP_L4_CSUM_MODE_L4_ONLY) {
+ err = mlx5_nv_param_read_sw_offload_cap(dev, cap, sizeof(cap));
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed to read sw_offload_cap");
+ return err;
+ }
+
+ data = MLX5_ADDR_OF(mnvda_reg, cap, configuration_item_data);
+ if (!MLX5_GET(nv_sw_offload_cap, data, swp_l4_csum_mode_l4_only)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "l4_only mode is not supported on this device");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+static int
+mlx5_swp_l4_csum_mode_set(struct devlink *devlink, u32 id, u8 value,
+ struct netlink_ext_ack *extack)
+{
+ struct mlx5_core_dev *dev = devlink_priv(devlink);
+ u32 mnvda[MLX5_ST_SZ_DW(mnvda_reg)] = {};
+ void *data;
+ int err;
+
+ err = mlx5_nv_param_read_sw_accelerate_conf(dev, mnvda, sizeof(mnvda),
+ MLX5_ACCESS_MODE_NEXT);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed to read sw_accelerate_conf mnvda reg");
+ return err;
+ }
+
+ data = MLX5_ADDR_OF(mnvda_reg, mnvda, configuration_item_data);
+ MLX5_SET(nv_sw_accelerate_conf, data, swp_l4_csum_mode, value);
+
+ err = mlx5_nv_param_write(dev, mnvda, sizeof(mnvda));
+ if (err)
+ NL_SET_ERR_MSG_MOD(extack,
+ "Failed to write sw_accelerate_conf mnvda reg");
+
+ return err;
+}
+
+static int
+mlx5_devlink_swp_l4_csum_mode_set(struct devlink *devlink, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ u8 value;
+
+ if (!strcmp(ctx->val.vstr, "full_csum"))
+ value = SWP_L4_CSUM_MODE_FULL_CSUM;
+ else
+ value = SWP_L4_CSUM_MODE_L4_ONLY;
+
+ return mlx5_swp_l4_csum_mode_set(devlink, id, value, extack);
+}
+
+static int
+mlx5_devlink_swp_l4_csum_mode_get_default(struct devlink *devlink, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ u8 value;
+ int err;
+
+ err = mlx5_swp_l4_csum_mode_get(devlink, id, MLX5_ACCESS_MODE_DEFAULT,
+ &value, extack);
+ if (err)
+ return err;
+
+ strscpy(ctx->val.vstr, swp_l4_csum_mode_str[value],
+ sizeof(ctx->val.vstr));
+ return 0;
+}
+
+static int
+mlx5_devlink_swp_l4_csum_mode_set_default(struct devlink *devlink, u32 id,
+ enum devlink_param_cmode cmode,
+ struct netlink_ext_ack *extack)
+{
+ u8 value;
+ int err;
+
+ err = mlx5_swp_l4_csum_mode_get(devlink, id, MLX5_ACCESS_MODE_DEFAULT,
+ &value, extack);
+ if (err)
+ return err;
+
+ return mlx5_swp_l4_csum_mode_set(devlink, id, value, extack);
+}
+
static int mlx5_nv_param_read_global_pci_conf(struct mlx5_core_dev *dev,
void *mnvda, size_t len)
{
@@ -302,7 +524,8 @@ static int mlx5_nv_param_read_per_host_pf_conf(struct mlx5_core_dev *dev,
}
static int mlx5_devlink_enable_sriov_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
u32 mnvda[MLX5_ST_SZ_DW(mnvda_reg)] = {};
@@ -413,7 +636,8 @@ static int mlx5_devlink_enable_sriov_set(struct devlink *devlink, u32 id,
}
static int mlx5_devlink_total_vfs_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlx5_core_dev *dev = devlink_priv(devlink);
u32 mnvda[MLX5_ST_SZ_DW(mnvda_reg)] = {};
@@ -545,6 +769,14 @@ static const struct devlink_param mlx5_nv_param_devlink_params[] = {
mlx5_nv_param_devlink_cqe_compress_get,
mlx5_nv_param_devlink_cqe_compress_set,
mlx5_nv_param_devlink_cqe_compress_validate),
+ DEVLINK_PARAM_DRIVER_WITH_DEFAULTS(MLX5_DEVLINK_PARAM_ID_SWP_L4_CSUM_MODE,
+ "swp_l4_csum_mode", DEVLINK_PARAM_TYPE_STRING,
+ BIT(DEVLINK_PARAM_CMODE_PERMANENT),
+ mlx5_devlink_swp_l4_csum_mode_get,
+ mlx5_devlink_swp_l4_csum_mode_set,
+ mlx5_devlink_swp_l4_csum_mode_validate,
+ mlx5_devlink_swp_l4_csum_mode_get_default,
+ mlx5_devlink_swp_l4_csum_mode_set_default),
};
int mlx5_nv_param_register_dl_params(struct devlink *devlink)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
index f5c2701f6e87..8e17daae48af 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/sd.c
@@ -221,8 +221,8 @@ static int sd_register(struct mlx5_core_dev *dev)
attr.net = mlx5_core_net(dev);
devcom = mlx5_devcom_register_component(dev->priv.devc, MLX5_DEVCOM_SD_GROUP,
&attr, NULL, dev);
- if (IS_ERR(devcom))
- return PTR_ERR(devcom);
+ if (!devcom)
+ return -EINVAL;
sd->devcom = devcom;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c
index 47fe215f66bf..ef06fe6cbb51 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/st.c
@@ -19,13 +19,16 @@ struct mlx5_st {
struct mutex lock;
struct xa_limit index_limit;
struct xarray idx_xa; /* key == index, value == struct mlx5_st_idx_data */
+ u8 direct_mode : 1;
};
struct mlx5_st *mlx5_st_create(struct mlx5_core_dev *dev)
{
struct pci_dev *pdev = dev->pdev;
struct mlx5_st *st;
+ u8 direct_mode = 0;
u16 num_entries;
+ u32 tbl_loc;
int ret;
if (!MLX5_CAP_GEN(dev, mkey_pcie_tph))
@@ -40,10 +43,16 @@ struct mlx5_st *mlx5_st_create(struct mlx5_core_dev *dev)
if (!pdev->tph_cap)
return NULL;
- num_entries = pcie_tph_get_st_table_size(pdev);
- /* We need a reserved entry for non TPH cases */
- if (num_entries < 2)
- return NULL;
+ tbl_loc = pcie_tph_get_st_table_loc(pdev);
+ if (tbl_loc == PCI_TPH_LOC_NONE)
+ direct_mode = 1;
+
+ if (!direct_mode) {
+ num_entries = pcie_tph_get_st_table_size(pdev);
+ /* We need a reserved entry for non TPH cases */
+ if (num_entries < 2)
+ return NULL;
+ }
/* The OS doesn't support ST */
ret = pcie_enable_tph(pdev, PCI_TPH_ST_DS_MODE);
@@ -56,6 +65,10 @@ struct mlx5_st *mlx5_st_create(struct mlx5_core_dev *dev)
mutex_init(&st->lock);
xa_init_flags(&st->idx_xa, XA_FLAGS_ALLOC);
+ st->direct_mode = direct_mode;
+ if (st->direct_mode)
+ return st;
+
/* entry 0 is reserved for non TPH cases */
st->index_limit.min = MLX5_MKC_PCIE_TPH_NO_STEERING_TAG_INDEX + 1;
st->index_limit.max = num_entries - 1;
@@ -96,6 +109,11 @@ int mlx5_st_alloc_index(struct mlx5_core_dev *dev, enum tph_mem_type mem_type,
if (ret)
return ret;
+ if (st->direct_mode) {
+ *st_index = tag;
+ return 0;
+ }
+
mutex_lock(&st->lock);
xa_for_each(&st->idx_xa, index, idx_data) {
@@ -145,6 +163,9 @@ int mlx5_st_dealloc_index(struct mlx5_core_dev *dev, u16 st_index)
if (!st)
return -EOPNOTSUPP;
+ if (st->direct_mode)
+ return 0;
+
mutex_lock(&st->lock);
idx_data = xa_load(&st->idx_xa, st_index);
if (WARN_ON_ONCE(!idx_data)) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c
index d55e15c1f380..304912637c35 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/vxlan.c
@@ -149,7 +149,7 @@ struct mlx5_vxlan *mlx5_vxlan_create(struct mlx5_core_dev *mdev)
struct mlx5_vxlan *vxlan;
if (!MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan) || !mlx5_core_is_pf(mdev))
- return ERR_PTR(-ENOTSUPP);
+ return ERR_PTR(-EOPNOTSUPP);
vxlan = kzalloc(sizeof(*vxlan), GFP_KERNEL);
if (!vxlan)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index df93625c9dfa..1ab569ce3fcf 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -553,6 +553,7 @@ EXPORT_SYMBOL(mlx5_is_roce_on);
static int handle_hca_cap_2(struct mlx5_core_dev *dev, void *set_ctx)
{
+ bool do_set = false;
void *set_hca_cap;
int err;
@@ -563,17 +564,27 @@ static int handle_hca_cap_2(struct mlx5_core_dev *dev, void *set_ctx)
if (err)
return err;
- if (!MLX5_CAP_GEN_2_MAX(dev, sw_vhca_id_valid) ||
- !(dev->priv.sw_vhca_id > 0))
- return 0;
-
set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx,
capability);
memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_GENERAL_2]->cur,
MLX5_ST_SZ_BYTES(cmd_hca_cap_2));
- MLX5_SET(cmd_hca_cap_2, set_hca_cap, sw_vhca_id_valid, 1);
- return set_caps(dev, set_ctx, MLX5_CAP_GENERAL_2);
+ if (MLX5_CAP_GEN_2_MAX(dev, sw_vhca_id_valid) &&
+ dev->priv.sw_vhca_id > 0) {
+ MLX5_SET(cmd_hca_cap_2, set_hca_cap, sw_vhca_id_valid, 1);
+ do_set = true;
+ }
+
+ if (MLX5_CAP_GEN_2_MAX(dev, lag_per_mp_group)) {
+ MLX5_SET(cmd_hca_cap_2, set_hca_cap, lag_per_mp_group, 1);
+ do_set = true;
+ }
+
+ /* some FW versions that support querying MLX5_CAP_GENERAL_2
+ * capabilities but don't support setting them.
+ * Skip unnecessary update to hca_cap_2 when no changes were introduced
+ */
+ return do_set ? set_caps(dev, set_ctx, MLX5_CAP_GENERAL_2) : 0;
}
static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
@@ -978,9 +989,8 @@ static int mlx5_init_once(struct mlx5_core_dev *dev)
int err;
dev->priv.devc = mlx5_devcom_register_device(dev);
- if (IS_ERR(dev->priv.devc))
- mlx5_core_warn(dev, "failed to register devcom device %pe\n",
- dev->priv.devc);
+ if (!dev->priv.devc)
+ mlx5_core_warn(dev, "failed to register devcom device\n");
err = mlx5_query_board_id(dev);
if (err) {
@@ -1000,16 +1010,10 @@ static int mlx5_init_once(struct mlx5_core_dev *dev)
goto err_irq_cleanup;
}
- err = mlx5_events_init(dev);
- if (err) {
- mlx5_core_err(dev, "failed to initialize events\n");
- goto err_eq_cleanup;
- }
-
err = mlx5_fw_reset_init(dev);
if (err) {
mlx5_core_err(dev, "failed to initialize fw reset events\n");
- goto err_events_cleanup;
+ goto err_eq_cleanup;
}
mlx5_cq_debugfs_init(dev);
@@ -1111,8 +1115,6 @@ err_tables_cleanup:
mlx5_cleanup_reserved_gids(dev);
mlx5_cq_debugfs_cleanup(dev);
mlx5_fw_reset_cleanup(dev);
-err_events_cleanup:
- mlx5_events_cleanup(dev);
err_eq_cleanup:
mlx5_eq_table_cleanup(dev);
err_irq_cleanup:
@@ -1145,7 +1147,6 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev)
mlx5_cleanup_reserved_gids(dev);
mlx5_cq_debugfs_cleanup(dev);
mlx5_fw_reset_cleanup(dev);
- mlx5_events_cleanup(dev);
mlx5_eq_table_cleanup(dev);
mlx5_irq_table_cleanup(dev);
mlx5_devcom_unregister_device(dev->priv.devc);
@@ -1376,12 +1377,6 @@ static int mlx5_load(struct mlx5_core_dev *dev)
mlx5_vhca_event_start(dev);
- err = mlx5_sf_hw_table_create(dev);
- if (err) {
- mlx5_core_err(dev, "sf table create failed %d\n", err);
- goto err_vhca;
- }
-
err = mlx5_ec_init(dev);
if (err) {
mlx5_core_err(dev, "Failed to init embedded CPU\n");
@@ -1410,8 +1405,6 @@ err_sriov:
mlx5_lag_remove_mdev(dev);
mlx5_ec_cleanup(dev);
err_ec:
- mlx5_sf_hw_table_destroy(dev);
-err_vhca:
mlx5_vhca_event_stop(dev);
err_set_hca:
mlx5_fs_core_cleanup(dev);
@@ -1437,12 +1430,12 @@ static void mlx5_unload(struct mlx5_core_dev *dev)
{
mlx5_eswitch_disable(dev->priv.eswitch);
mlx5_devlink_traps_unregister(priv_to_devlink(dev));
+ mlx5_vhca_event_stop(dev);
mlx5_sf_dev_table_destroy(dev);
mlx5_sriov_detach(dev);
mlx5_lag_remove_mdev(dev);
mlx5_ec_cleanup(dev);
mlx5_sf_hw_table_destroy(dev);
- mlx5_vhca_event_stop(dev);
mlx5_fs_core_cleanup(dev);
mlx5_fpga_device_stop(dev);
mlx5_rsc_dump_cleanup(dev);
@@ -1823,6 +1816,50 @@ static int vhca_id_show(struct seq_file *file, void *priv)
DEFINE_SHOW_ATTRIBUTE(vhca_id);
+static int mlx5_notifiers_init(struct mlx5_core_dev *dev)
+{
+ int err;
+
+ err = mlx5_events_init(dev);
+ if (err) {
+ mlx5_core_err(dev, "failed to initialize events\n");
+ return err;
+ }
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&dev->priv.esw_n_head);
+ mlx5_vhca_state_notifier_init(dev);
+
+ err = mlx5_sf_hw_notifier_init(dev);
+ if (err)
+ goto err_sf_hw_notifier;
+
+ err = mlx5_sf_notifiers_init(dev);
+ if (err)
+ goto err_sf_notifiers;
+
+ err = mlx5_sf_dev_notifier_init(dev);
+ if (err)
+ goto err_sf_dev_notifier;
+
+ return 0;
+
+err_sf_dev_notifier:
+ mlx5_sf_notifiers_cleanup(dev);
+err_sf_notifiers:
+ mlx5_sf_hw_notifier_cleanup(dev);
+err_sf_hw_notifier:
+ mlx5_events_cleanup(dev);
+ return err;
+}
+
+static void mlx5_notifiers_cleanup(struct mlx5_core_dev *dev)
+{
+ mlx5_sf_dev_notifier_cleanup(dev);
+ mlx5_sf_notifiers_cleanup(dev);
+ mlx5_sf_hw_notifier_cleanup(dev);
+ mlx5_events_cleanup(dev);
+}
+
int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
{
struct mlx5_priv *priv = &dev->priv;
@@ -1878,6 +1915,10 @@ int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx)
if (err)
goto err_hca_caps;
+ err = mlx5_notifiers_init(dev);
+ if (err)
+ goto err_hca_caps;
+
/* The conjunction of sw_vhca_id with sw_owner_id will be a global
* unique id per function which uses mlx5_core.
* Those values are supplied to FW as part of the init HCA command to
@@ -1920,6 +1961,7 @@ void mlx5_mdev_uninit(struct mlx5_core_dev *dev)
if (priv->sw_vhca_id > 0)
ida_free(&sw_vhca_ida, dev->priv.sw_vhca_id);
+ mlx5_notifiers_cleanup(dev);
mlx5_hca_caps_free(dev);
mlx5_adev_cleanup(dev);
mlx5_pagealloc_cleanup(dev);
@@ -2095,7 +2137,6 @@ static pci_ers_result_t mlx5_pci_slot_reset(struct pci_dev *pdev)
pci_set_master(pdev);
pci_restore_state(pdev);
- pci_save_state(pdev);
err = wait_vital(pdev);
if (err) {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
index 082259b56816..cfebc110c02f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h
@@ -357,11 +357,11 @@ int mlx5_set_port_fcs(struct mlx5_core_dev *mdev, u8 enable);
void mlx5_query_port_fcs(struct mlx5_core_dev *mdev, bool *supported,
bool *enabled);
int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
- u16 offset, u16 size, u8 *data);
+ u16 offset, u16 size, u8 *data, u8 *status);
int
mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev,
struct mlx5_module_eeprom_query_params *params,
- u8 *data);
+ u8 *data, u8 *status);
int mlx5_query_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *out);
int mlx5_set_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *in);
@@ -444,6 +444,8 @@ int mlx5_init_one_light(struct mlx5_core_dev *dev);
void mlx5_uninit_one_light(struct mlx5_core_dev *dev);
void mlx5_unload_one_light(struct mlx5_core_dev *dev);
+void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf,
+ u8 *len);
int mlx5_vport_set_other_func_cap(struct mlx5_core_dev *dev, const void *hca_cap, u16 vport,
u16 opmod);
#define mlx5_vport_get_other_func_general_cap(dev, vport, out) \
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c
index e18a850c615c..aa3b5878e3da 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c
@@ -324,10 +324,8 @@ err_xa:
free_irq(irq->map.virq, &irq->nh);
err_req_irq:
#ifdef CONFIG_RFS_ACCEL
- if (i && rmap && *rmap) {
- free_irq_cpu_rmap(*rmap);
- *rmap = NULL;
- }
+ if (i && rmap && *rmap)
+ irq_cpu_rmap_remove(*rmap, irq->map.virq);
err_irq_rmap:
#endif
if (i && pci_msix_can_alloc_dyn(dev->pdev))
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c
index aa9f2b0a77d3..85a9e534f442 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/port.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c
@@ -289,11 +289,11 @@ int mlx5_query_module_num(struct mlx5_core_dev *dev, int *module_num)
}
static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num,
- u8 *module_id)
+ u8 *module_id, u8 *status)
{
u32 in[MLX5_ST_SZ_DW(mcia_reg)] = {};
u32 out[MLX5_ST_SZ_DW(mcia_reg)];
- int err, status;
+ int err;
u8 *ptr;
MLX5_SET(mcia_reg, in, i2c_device_address, MLX5_I2C_ADDR_LOW);
@@ -308,12 +308,12 @@ static int mlx5_query_module_id(struct mlx5_core_dev *dev, int module_num,
if (err)
return err;
- status = MLX5_GET(mcia_reg, out, status);
- if (status) {
- mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n",
- status);
+ if (MLX5_GET(mcia_reg, out, status)) {
+ if (status)
+ *status = MLX5_GET(mcia_reg, out, status);
return -EIO;
}
+
ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
*module_id = ptr[0];
@@ -370,13 +370,14 @@ static int mlx5_mcia_max_bytes(struct mlx5_core_dev *dev)
}
static int mlx5_query_mcia(struct mlx5_core_dev *dev,
- struct mlx5_module_eeprom_query_params *params, u8 *data)
+ struct mlx5_module_eeprom_query_params *params,
+ u8 *data, u8 *status)
{
u32 in[MLX5_ST_SZ_DW(mcia_reg)] = {};
u32 out[MLX5_ST_SZ_DW(mcia_reg)];
- int status, err;
void *ptr;
u16 size;
+ int err;
size = min_t(int, params->size, mlx5_mcia_max_bytes(dev));
@@ -392,12 +393,9 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev,
if (err)
return err;
- status = MLX5_GET(mcia_reg, out, status);
- if (status) {
- mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n",
- status);
+ *status = MLX5_GET(mcia_reg, out, status);
+ if (*status)
return -EIO;
- }
ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
memcpy(data, ptr, size);
@@ -406,7 +404,7 @@ static int mlx5_query_mcia(struct mlx5_core_dev *dev,
}
int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
- u16 offset, u16 size, u8 *data)
+ u16 offset, u16 size, u8 *data, u8 *status)
{
struct mlx5_module_eeprom_query_params query = {0};
u8 module_id;
@@ -416,7 +414,8 @@ int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
if (err)
return err;
- err = mlx5_query_module_id(dev, query.module_number, &module_id);
+ err = mlx5_query_module_id(dev, query.module_number, &module_id,
+ status);
if (err)
return err;
@@ -441,12 +440,12 @@ int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
query.size = size;
query.offset = offset;
- return mlx5_query_mcia(dev, &query, data);
+ return mlx5_query_mcia(dev, &query, data, status);
}
int mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev,
struct mlx5_module_eeprom_query_params *params,
- u8 *data)
+ u8 *data, u8 *status)
{
int err;
@@ -460,7 +459,7 @@ int mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev,
return -EINVAL;
}
- return mlx5_query_mcia(dev, params, data);
+ return mlx5_query_mcia(dev, params, data, status);
}
static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc,
@@ -1109,6 +1108,7 @@ mlx5e_ext_link_info[MLX5E_EXT_LINK_MODES_NUMBER] = {
[MLX5E_200GAUI_1_200GBASE_CR1_KR1] = {.speed = 200000, .lanes = 1},
[MLX5E_400GAUI_2_400GBASE_CR2_KR2] = {.speed = 400000, .lanes = 2},
[MLX5E_800GAUI_4_800GBASE_CR4_KR4] = {.speed = 800000, .lanes = 4},
+ [MLX5E_1600TAUI_8_1600TBASE_CR8_KR8] = {.speed = 1600000, .lanes = 8},
};
int mlx5_port_query_eth_proto(struct mlx5_core_dev *dev, u8 port, bool ext,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c
index 99219ea52c4b..f310bde3d11f 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.c
@@ -16,7 +16,6 @@ struct mlx5_sf_dev_table {
struct xarray devices;
phys_addr_t base_address;
u64 sf_bar_length;
- struct notifier_block nb;
struct workqueue_struct *active_wq;
struct work_struct work;
u8 stop_active_wq:1;
@@ -156,18 +155,23 @@ static void mlx5_sf_dev_del(struct mlx5_core_dev *dev, struct mlx5_sf_dev *sf_de
static int
mlx5_sf_dev_state_change_handler(struct notifier_block *nb, unsigned long event_code, void *data)
{
- struct mlx5_sf_dev_table *table = container_of(nb, struct mlx5_sf_dev_table, nb);
+ struct mlx5_core_dev *dev = container_of(nb, struct mlx5_core_dev,
+ priv.sf_dev_nb);
+ struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
const struct mlx5_vhca_state_event *event = data;
struct mlx5_sf_dev *sf_dev;
u16 max_functions;
u16 sf_index;
u16 base_id;
- max_functions = mlx5_sf_max_functions(table->dev);
+ if (!table)
+ return 0;
+
+ max_functions = mlx5_sf_max_functions(dev);
if (!max_functions)
return 0;
- base_id = mlx5_sf_start_function_id(table->dev);
+ base_id = mlx5_sf_start_function_id(dev);
if (event->function_id < base_id || event->function_id >= (base_id + max_functions))
return 0;
@@ -177,19 +181,19 @@ mlx5_sf_dev_state_change_handler(struct notifier_block *nb, unsigned long event_
case MLX5_VHCA_STATE_INVALID:
case MLX5_VHCA_STATE_ALLOCATED:
if (sf_dev)
- mlx5_sf_dev_del(table->dev, sf_dev, sf_index);
+ mlx5_sf_dev_del(dev, sf_dev, sf_index);
break;
case MLX5_VHCA_STATE_TEARDOWN_REQUEST:
if (sf_dev)
- mlx5_sf_dev_del(table->dev, sf_dev, sf_index);
+ mlx5_sf_dev_del(dev, sf_dev, sf_index);
else
- mlx5_core_err(table->dev,
+ mlx5_core_err(dev,
"SF DEV: teardown state for invalid dev index=%d sfnum=0x%x\n",
sf_index, event->sw_function_id);
break;
case MLX5_VHCA_STATE_ACTIVE:
if (!sf_dev)
- mlx5_sf_dev_add(table->dev, sf_index, event->function_id,
+ mlx5_sf_dev_add(dev, sf_index, event->function_id,
event->sw_function_id);
break;
default:
@@ -315,6 +319,15 @@ static void mlx5_sf_dev_destroy_active_works(struct mlx5_sf_dev_table *table)
}
}
+int mlx5_sf_dev_notifier_init(struct mlx5_core_dev *dev)
+{
+ if (mlx5_core_is_sf(dev))
+ return 0;
+
+ dev->priv.sf_dev_nb.notifier_call = mlx5_sf_dev_state_change_handler;
+ return mlx5_vhca_event_notifier_register(dev, &dev->priv.sf_dev_nb);
+}
+
void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
{
struct mlx5_sf_dev_table *table;
@@ -329,17 +342,12 @@ void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
goto table_err;
}
- table->nb.notifier_call = mlx5_sf_dev_state_change_handler;
table->dev = dev;
table->sf_bar_length = 1 << (MLX5_CAP_GEN(dev, log_min_sf_size) + 12);
table->base_address = pci_resource_start(dev->pdev, 2);
xa_init(&table->devices);
dev->priv.sf_dev_table = table;
- err = mlx5_vhca_event_notifier_register(dev, &table->nb);
- if (err)
- goto vhca_err;
-
err = mlx5_sf_dev_create_active_works(table);
if (err)
goto add_active_err;
@@ -351,10 +359,8 @@ void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
arm_err:
mlx5_sf_dev_destroy_active_works(table);
-add_active_err:
- mlx5_vhca_event_notifier_unregister(dev, &table->nb);
mlx5_vhca_event_work_queues_flush(dev);
-vhca_err:
+add_active_err:
kfree(table);
dev->priv.sf_dev_table = NULL;
table_err:
@@ -372,6 +378,14 @@ static void mlx5_sf_dev_destroy_all(struct mlx5_sf_dev_table *table)
}
}
+void mlx5_sf_dev_notifier_cleanup(struct mlx5_core_dev *dev)
+{
+ if (mlx5_core_is_sf(dev))
+ return;
+
+ mlx5_vhca_event_notifier_unregister(dev, &dev->priv.sf_dev_nb);
+}
+
void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev)
{
struct mlx5_sf_dev_table *table = dev->priv.sf_dev_table;
@@ -380,8 +394,6 @@ void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev)
return;
mlx5_sf_dev_destroy_active_works(table);
- mlx5_vhca_event_notifier_unregister(dev, &table->nb);
- mlx5_vhca_event_work_queues_flush(dev);
/* Now that event handler is not running, it is safe to destroy
* the sf device without race.
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h
index b99131e95e37..3ab0449c770c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/dev/dev.h
@@ -25,7 +25,9 @@ struct mlx5_sf_peer_devlink_event_ctx {
int err;
};
+int mlx5_sf_dev_notifier_init(struct mlx5_core_dev *dev);
void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev);
+void mlx5_sf_dev_notifier_cleanup(struct mlx5_core_dev *dev);
void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev);
int mlx5_sf_driver_register(void);
@@ -35,10 +37,19 @@ bool mlx5_sf_dev_allocated(const struct mlx5_core_dev *dev);
#else
+static inline int mlx5_sf_dev_notifier_init(struct mlx5_core_dev *dev)
+{
+ return 0;
+}
+
static inline void mlx5_sf_dev_table_create(struct mlx5_core_dev *dev)
{
}
+static inline void mlx5_sf_dev_notifier_cleanup(struct mlx5_core_dev *dev)
+{
+}
+
static inline void mlx5_sf_dev_table_destroy(struct mlx5_core_dev *dev)
{
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c
index 3304f25cc805..b82323b8449e 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c
@@ -31,9 +31,6 @@ struct mlx5_sf_table {
struct mlx5_core_dev *dev; /* To refer from notifier context. */
struct xarray function_ids; /* function id based lookup. */
struct mutex sf_state_lock; /* Serializes sf state among user cmds & vhca event handler. */
- struct notifier_block esw_nb;
- struct notifier_block vhca_nb;
- struct notifier_block mdev_nb;
};
static struct mlx5_sf *
@@ -391,11 +388,16 @@ static bool mlx5_sf_state_update_check(const struct mlx5_sf *sf, u8 new_state)
static int mlx5_sf_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data)
{
- struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, vhca_nb);
+ struct mlx5_core_dev *dev = container_of(nb, struct mlx5_core_dev,
+ priv.sf_table_vhca_nb);
+ struct mlx5_sf_table *table = dev->priv.sf_table;
const struct mlx5_vhca_state_event *event = data;
bool update = false;
struct mlx5_sf *sf;
+ if (!table)
+ return 0;
+
mutex_lock(&table->sf_state_lock);
sf = mlx5_sf_lookup_by_function_id(table, event->function_id);
if (!sf)
@@ -407,7 +409,7 @@ static int mlx5_sf_vhca_event(struct notifier_block *nb, unsigned long opcode, v
update = mlx5_sf_state_update_check(sf, event->new_vhca_state);
if (update)
sf->hw_state = event->new_vhca_state;
- trace_mlx5_sf_update_state(table->dev, sf->port_index, sf->controller,
+ trace_mlx5_sf_update_state(dev, sf->port_index, sf->controller,
sf->hw_fn_id, sf->hw_state);
unlock:
mutex_unlock(&table->sf_state_lock);
@@ -425,12 +427,16 @@ static void mlx5_sf_del_all(struct mlx5_sf_table *table)
static int mlx5_sf_esw_event(struct notifier_block *nb, unsigned long event, void *data)
{
- struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, esw_nb);
+ struct mlx5_core_dev *dev = container_of(nb, struct mlx5_core_dev,
+ priv.sf_table_esw_nb);
const struct mlx5_esw_event_info *mode = data;
+ if (!dev->priv.sf_table)
+ return 0;
+
switch (mode->new_mode) {
case MLX5_ESWITCH_LEGACY:
- mlx5_sf_del_all(table);
+ mlx5_sf_del_all(dev->priv.sf_table);
break;
default:
break;
@@ -441,15 +447,16 @@ static int mlx5_sf_esw_event(struct notifier_block *nb, unsigned long event, voi
static int mlx5_sf_mdev_event(struct notifier_block *nb, unsigned long event, void *data)
{
- struct mlx5_sf_table *table = container_of(nb, struct mlx5_sf_table, mdev_nb);
+ struct mlx5_core_dev *dev = container_of(nb, struct mlx5_core_dev,
+ priv.sf_table_mdev_nb);
struct mlx5_sf_peer_devlink_event_ctx *event_ctx = data;
+ struct mlx5_sf_table *table = dev->priv.sf_table;
int ret = NOTIFY_DONE;
struct mlx5_sf *sf;
- if (event != MLX5_DRIVER_EVENT_SF_PEER_DEVLINK)
+ if (!table || event != MLX5_DRIVER_EVENT_SF_PEER_DEVLINK)
return NOTIFY_DONE;
-
mutex_lock(&table->sf_state_lock);
sf = mlx5_sf_lookup_by_function_id(table, event_ctx->fn_id);
if (!sf)
@@ -464,10 +471,40 @@ out:
return ret;
}
+int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev)
+{
+ int err;
+
+ if (mlx5_core_is_sf(dev))
+ return 0;
+
+ dev->priv.sf_table_esw_nb.notifier_call = mlx5_sf_esw_event;
+ err = mlx5_esw_event_notifier_register(dev, &dev->priv.sf_table_esw_nb);
+ if (err)
+ return err;
+
+ dev->priv.sf_table_vhca_nb.notifier_call = mlx5_sf_vhca_event;
+ err = mlx5_vhca_event_notifier_register(dev,
+ &dev->priv.sf_table_vhca_nb);
+ if (err)
+ goto vhca_err;
+
+ dev->priv.sf_table_mdev_nb.notifier_call = mlx5_sf_mdev_event;
+ err = mlx5_blocking_notifier_register(dev, &dev->priv.sf_table_mdev_nb);
+ if (err)
+ goto mdev_err;
+
+ return 0;
+mdev_err:
+ mlx5_vhca_event_notifier_unregister(dev, &dev->priv.sf_table_vhca_nb);
+vhca_err:
+ mlx5_esw_event_notifier_unregister(dev, &dev->priv.sf_table_esw_nb);
+ return err;
+}
+
int mlx5_sf_table_init(struct mlx5_core_dev *dev)
{
struct mlx5_sf_table *table;
- int err;
if (!mlx5_sf_table_supported(dev) || !mlx5_vhca_event_supported(dev))
return 0;
@@ -480,28 +517,18 @@ int mlx5_sf_table_init(struct mlx5_core_dev *dev)
table->dev = dev;
xa_init(&table->function_ids);
dev->priv.sf_table = table;
- table->esw_nb.notifier_call = mlx5_sf_esw_event;
- err = mlx5_esw_event_notifier_register(dev->priv.eswitch, &table->esw_nb);
- if (err)
- goto reg_err;
-
- table->vhca_nb.notifier_call = mlx5_sf_vhca_event;
- err = mlx5_vhca_event_notifier_register(table->dev, &table->vhca_nb);
- if (err)
- goto vhca_err;
-
- table->mdev_nb.notifier_call = mlx5_sf_mdev_event;
- mlx5_blocking_notifier_register(dev, &table->mdev_nb);
return 0;
+}
-vhca_err:
- mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
-reg_err:
- mutex_destroy(&table->sf_state_lock);
- kfree(table);
- dev->priv.sf_table = NULL;
- return err;
+void mlx5_sf_notifiers_cleanup(struct mlx5_core_dev *dev)
+{
+ if (mlx5_core_is_sf(dev))
+ return;
+
+ mlx5_blocking_notifier_unregister(dev, &dev->priv.sf_table_mdev_nb);
+ mlx5_vhca_event_notifier_unregister(dev, &dev->priv.sf_table_vhca_nb);
+ mlx5_esw_event_notifier_unregister(dev, &dev->priv.sf_table_esw_nb);
}
void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev)
@@ -511,9 +538,6 @@ void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev)
if (!table)
return;
- mlx5_blocking_notifier_unregister(dev, &table->mdev_nb);
- mlx5_vhca_event_notifier_unregister(table->dev, &table->vhca_nb);
- mlx5_esw_event_notifier_unregister(dev->priv.eswitch, &table->esw_nb);
mutex_destroy(&table->sf_state_lock);
WARN_ON(!xa_empty(&table->function_ids));
kfree(table);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
index 1f613320fe07..bd968f3b3855 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c
@@ -30,9 +30,7 @@ enum mlx5_sf_hwc_index {
};
struct mlx5_sf_hw_table {
- struct mlx5_core_dev *dev;
struct mutex table_lock; /* Serializes sf deletion and vhca state change handler. */
- struct notifier_block vhca_nb;
struct mlx5_sf_hwc_table hwc[MLX5_SF_HWC_MAX];
};
@@ -71,14 +69,16 @@ mlx5_sf_table_fn_to_hwc(struct mlx5_sf_hw_table *table, u16 fn_id)
return NULL;
}
-static int mlx5_sf_hw_table_id_alloc(struct mlx5_sf_hw_table *table, u32 controller,
+static int mlx5_sf_hw_table_id_alloc(struct mlx5_core_dev *dev,
+ struct mlx5_sf_hw_table *table,
+ u32 controller,
u32 usr_sfnum)
{
struct mlx5_sf_hwc_table *hwc;
int free_idx = -1;
int i;
- hwc = mlx5_sf_controller_to_hwc(table->dev, controller);
+ hwc = mlx5_sf_controller_to_hwc(dev, controller);
if (!hwc->sfs)
return -ENOSPC;
@@ -100,11 +100,13 @@ static int mlx5_sf_hw_table_id_alloc(struct mlx5_sf_hw_table *table, u32 control
return free_idx;
}
-static void mlx5_sf_hw_table_id_free(struct mlx5_sf_hw_table *table, u32 controller, int id)
+static void mlx5_sf_hw_table_id_free(struct mlx5_core_dev *dev,
+ struct mlx5_sf_hw_table *table,
+ u32 controller, int id)
{
struct mlx5_sf_hwc_table *hwc;
- hwc = mlx5_sf_controller_to_hwc(table->dev, controller);
+ hwc = mlx5_sf_controller_to_hwc(dev, controller);
hwc->sfs[id].allocated = false;
hwc->sfs[id].pending_delete = false;
}
@@ -120,7 +122,7 @@ int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 controller, u32 usr
return -EOPNOTSUPP;
mutex_lock(&table->table_lock);
- sw_id = mlx5_sf_hw_table_id_alloc(table, controller, usr_sfnum);
+ sw_id = mlx5_sf_hw_table_id_alloc(dev, table, controller, usr_sfnum);
if (sw_id < 0) {
err = sw_id;
goto exist_err;
@@ -151,7 +153,7 @@ int mlx5_sf_hw_table_sf_alloc(struct mlx5_core_dev *dev, u32 controller, u32 usr
vhca_err:
mlx5_cmd_dealloc_sf(dev, hw_fn_id);
err:
- mlx5_sf_hw_table_id_free(table, controller, sw_id);
+ mlx5_sf_hw_table_id_free(dev, table, controller, sw_id);
exist_err:
mutex_unlock(&table->table_lock);
return err;
@@ -165,7 +167,7 @@ void mlx5_sf_hw_table_sf_free(struct mlx5_core_dev *dev, u32 controller, u16 id)
mutex_lock(&table->table_lock);
hw_fn_id = mlx5_sf_sw_to_hw_id(dev, controller, id);
mlx5_cmd_dealloc_sf(dev, hw_fn_id);
- mlx5_sf_hw_table_id_free(table, controller, id);
+ mlx5_sf_hw_table_id_free(dev, table, controller, id);
mutex_unlock(&table->table_lock);
}
@@ -216,10 +218,12 @@ static void mlx5_sf_hw_table_hwc_dealloc_all(struct mlx5_core_dev *dev,
}
}
-static void mlx5_sf_hw_table_dealloc_all(struct mlx5_sf_hw_table *table)
+static void mlx5_sf_hw_table_dealloc_all(struct mlx5_core_dev *dev,
+ struct mlx5_sf_hw_table *table)
{
- mlx5_sf_hw_table_hwc_dealloc_all(table->dev, &table->hwc[MLX5_SF_HWC_EXTERNAL]);
- mlx5_sf_hw_table_hwc_dealloc_all(table->dev, &table->hwc[MLX5_SF_HWC_LOCAL]);
+ mlx5_sf_hw_table_hwc_dealloc_all(dev,
+ &table->hwc[MLX5_SF_HWC_EXTERNAL]);
+ mlx5_sf_hw_table_hwc_dealloc_all(dev, &table->hwc[MLX5_SF_HWC_LOCAL]);
}
static int mlx5_sf_hw_table_hwc_init(struct mlx5_sf_hwc_table *hwc, u16 max_fn, u16 base_id)
@@ -301,7 +305,6 @@ int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev)
}
mutex_init(&table->table_lock);
- table->dev = dev;
dev->priv.sf_hw_table = table;
base_id = mlx5_sf_start_function_id(dev);
@@ -338,19 +341,22 @@ void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev)
mlx5_sf_hw_table_hwc_cleanup(&table->hwc[MLX5_SF_HWC_LOCAL]);
mutex_destroy(&table->table_lock);
kfree(table);
+ dev->priv.sf_hw_table = NULL;
res_unregister:
mlx5_sf_hw_table_res_unregister(dev);
}
static int mlx5_sf_hw_vhca_event(struct notifier_block *nb, unsigned long opcode, void *data)
{
- struct mlx5_sf_hw_table *table = container_of(nb, struct mlx5_sf_hw_table, vhca_nb);
+ struct mlx5_core_dev *dev = container_of(nb, struct mlx5_core_dev,
+ priv.sf_hw_table_vhca_nb);
+ struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
const struct mlx5_vhca_state_event *event = data;
struct mlx5_sf_hwc_table *hwc;
struct mlx5_sf_hw *sf_hw;
u16 sw_id;
- if (event->new_vhca_state != MLX5_VHCA_STATE_ALLOCATED)
+ if (!table || event->new_vhca_state != MLX5_VHCA_STATE_ALLOCATED)
return 0;
hwc = mlx5_sf_table_fn_to_hwc(table, event->function_id);
@@ -365,20 +371,28 @@ static int mlx5_sf_hw_vhca_event(struct notifier_block *nb, unsigned long opcode
* Hence recycle the sf hardware id for reuse.
*/
if (sf_hw->allocated && sf_hw->pending_delete)
- mlx5_sf_hw_table_hwc_sf_free(table->dev, hwc, sw_id);
+ mlx5_sf_hw_table_hwc_sf_free(dev, hwc, sw_id);
mutex_unlock(&table->table_lock);
return 0;
}
-int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev)
+int mlx5_sf_hw_notifier_init(struct mlx5_core_dev *dev)
{
- struct mlx5_sf_hw_table *table = dev->priv.sf_hw_table;
-
- if (!table)
+ if (mlx5_core_is_sf(dev))
return 0;
- table->vhca_nb.notifier_call = mlx5_sf_hw_vhca_event;
- return mlx5_vhca_event_notifier_register(dev, &table->vhca_nb);
+ dev->priv.sf_hw_table_vhca_nb.notifier_call = mlx5_sf_hw_vhca_event;
+ return mlx5_vhca_event_notifier_register(dev,
+ &dev->priv.sf_hw_table_vhca_nb);
+}
+
+void mlx5_sf_hw_notifier_cleanup(struct mlx5_core_dev *dev)
+{
+ if (mlx5_core_is_sf(dev))
+ return;
+
+ mlx5_vhca_event_notifier_unregister(dev,
+ &dev->priv.sf_hw_table_vhca_nb);
}
void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev)
@@ -388,9 +402,8 @@ void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev)
if (!table)
return;
- mlx5_vhca_event_notifier_unregister(dev, &table->vhca_nb);
/* Dealloc SFs whose firmware event has been missed. */
- mlx5_sf_hw_table_dealloc_all(table);
+ mlx5_sf_hw_table_dealloc_all(dev, table);
}
bool mlx5_sf_hw_table_supported(const struct mlx5_core_dev *dev)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h
index 89559a37997a..d8a934a0e968 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h
@@ -12,10 +12,13 @@
int mlx5_sf_hw_table_init(struct mlx5_core_dev *dev);
void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev);
-int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev);
+int mlx5_sf_hw_notifier_init(struct mlx5_core_dev *dev);
+void mlx5_sf_hw_notifier_cleanup(struct mlx5_core_dev *dev);
void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev);
+int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev);
int mlx5_sf_table_init(struct mlx5_core_dev *dev);
+void mlx5_sf_notifiers_cleanup(struct mlx5_core_dev *dev);
void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev);
bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev);
@@ -44,20 +47,33 @@ static inline void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev)
{
}
-static inline int mlx5_sf_hw_table_create(struct mlx5_core_dev *dev)
+static inline int mlx5_sf_hw_notifier_init(struct mlx5_core_dev *dev)
{
return 0;
}
+static inline void mlx5_sf_hw_notifier_cleanup(struct mlx5_core_dev *dev)
+{
+}
+
static inline void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev)
{
}
+static inline int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev)
+{
+ return 0;
+}
+
static inline int mlx5_sf_table_init(struct mlx5_core_dev *dev)
{
return 0;
}
+static inline void mlx5_sf_notifiers_cleanup(struct mlx5_core_dev *dev)
+{
+}
+
static inline void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev)
{
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c
index cda01ba441ae..b04cf6cf8956 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.c
@@ -9,15 +9,9 @@
#define CREATE_TRACE_POINTS
#include "diag/vhca_tracepoint.h"
-struct mlx5_vhca_state_notifier {
- struct mlx5_core_dev *dev;
- struct mlx5_nb nb;
- struct blocking_notifier_head n_head;
-};
-
struct mlx5_vhca_event_work {
struct work_struct work;
- struct mlx5_vhca_state_notifier *notifier;
+ struct mlx5_core_dev *dev;
struct mlx5_vhca_state_event event;
};
@@ -95,16 +89,14 @@ mlx5_vhca_event_notify(struct mlx5_core_dev *dev, struct mlx5_vhca_state_event *
mlx5_vhca_event_arm(dev, event->function_id);
trace_mlx5_sf_vhca_event(dev, event);
- blocking_notifier_call_chain(&dev->priv.vhca_state_notifier->n_head, 0, event);
+ blocking_notifier_call_chain(&dev->priv.vhca_state_n_head, 0, event);
}
static void mlx5_vhca_state_work_handler(struct work_struct *_work)
{
struct mlx5_vhca_event_work *work = container_of(_work, struct mlx5_vhca_event_work, work);
- struct mlx5_vhca_state_notifier *notifier = work->notifier;
- struct mlx5_core_dev *dev = notifier->dev;
- mlx5_vhca_event_notify(dev, &work->event);
+ mlx5_vhca_event_notify(work->dev, &work->event);
kfree(work);
}
@@ -116,8 +108,8 @@ void mlx5_vhca_events_work_enqueue(struct mlx5_core_dev *dev, int idx, struct wo
static int
mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, void *data)
{
- struct mlx5_vhca_state_notifier *notifier =
- mlx5_nb_cof(nb, struct mlx5_vhca_state_notifier, nb);
+ struct mlx5_core_dev *dev = mlx5_nb_cof(nb, struct mlx5_core_dev,
+ priv.vhca_state_nb);
struct mlx5_vhca_event_work *work;
struct mlx5_eqe *eqe = data;
int wq_idx;
@@ -126,10 +118,10 @@ mlx5_vhca_state_change_notifier(struct notifier_block *nb, unsigned long type, v
if (!work)
return NOTIFY_DONE;
INIT_WORK(&work->work, &mlx5_vhca_state_work_handler);
- work->notifier = notifier;
+ work->dev = dev;
work->event.function_id = be16_to_cpu(eqe->data.vhca_state.function_id);
wq_idx = work->event.function_id % MLX5_DEV_MAX_WQS;
- mlx5_vhca_events_work_enqueue(notifier->dev, wq_idx, &work->work);
+ mlx5_vhca_events_work_enqueue(dev, wq_idx, &work->work);
return NOTIFY_OK;
}
@@ -145,9 +137,15 @@ void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap)
MLX5_SET(cmd_hca_cap, set_hca_cap, event_on_vhca_state_teardown_request, 1);
}
+void mlx5_vhca_state_notifier_init(struct mlx5_core_dev *dev)
+{
+ BLOCKING_INIT_NOTIFIER_HEAD(&dev->priv.vhca_state_n_head);
+ MLX5_NB_INIT(&dev->priv.vhca_state_nb, mlx5_vhca_state_change_notifier,
+ VHCA_STATE_CHANGE);
+}
+
int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
{
- struct mlx5_vhca_state_notifier *notifier;
char wq_name[MLX5_CMD_WQ_MAX_NAME];
struct mlx5_vhca_events *events;
int err, i;
@@ -160,7 +158,6 @@ int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
return -ENOMEM;
events->dev = dev;
- dev->priv.vhca_events = events;
for (i = 0; i < MLX5_DEV_MAX_WQS; i++) {
snprintf(wq_name, MLX5_CMD_WQ_MAX_NAME, "mlx5_vhca_event%d", i);
events->handler[i].wq = create_singlethread_workqueue(wq_name);
@@ -169,20 +166,10 @@ int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
goto err_create_wq;
}
}
+ dev->priv.vhca_events = events;
- notifier = kzalloc(sizeof(*notifier), GFP_KERNEL);
- if (!notifier) {
- err = -ENOMEM;
- goto err_notifier;
- }
-
- dev->priv.vhca_state_notifier = notifier;
- notifier->dev = dev;
- BLOCKING_INIT_NOTIFIER_HEAD(&notifier->n_head);
- MLX5_NB_INIT(&notifier->nb, mlx5_vhca_state_change_notifier, VHCA_STATE_CHANGE);
return 0;
-err_notifier:
err_create_wq:
for (--i; i >= 0; i--)
destroy_workqueue(events->handler[i].wq);
@@ -211,8 +198,6 @@ void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev)
if (!mlx5_vhca_event_supported(dev))
return;
- kfree(dev->priv.vhca_state_notifier);
- dev->priv.vhca_state_notifier = NULL;
vhca_events = dev->priv.vhca_events;
for (i = 0; i < MLX5_DEV_MAX_WQS; i++)
destroy_workqueue(vhca_events->handler[i].wq);
@@ -221,34 +206,30 @@ void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev)
void mlx5_vhca_event_start(struct mlx5_core_dev *dev)
{
- struct mlx5_vhca_state_notifier *notifier;
-
- if (!dev->priv.vhca_state_notifier)
+ if (!mlx5_vhca_event_supported(dev))
return;
- notifier = dev->priv.vhca_state_notifier;
- mlx5_eq_notifier_register(dev, &notifier->nb);
+ mlx5_eq_notifier_register(dev, &dev->priv.vhca_state_nb);
}
void mlx5_vhca_event_stop(struct mlx5_core_dev *dev)
{
- struct mlx5_vhca_state_notifier *notifier;
-
- if (!dev->priv.vhca_state_notifier)
+ if (!mlx5_vhca_event_supported(dev))
return;
- notifier = dev->priv.vhca_state_notifier;
- mlx5_eq_notifier_unregister(dev, &notifier->nb);
+ mlx5_eq_notifier_unregister(dev, &dev->priv.vhca_state_nb);
+
+ /* Flush workqueues of all pending events. */
+ mlx5_vhca_event_work_queues_flush(dev);
}
int mlx5_vhca_event_notifier_register(struct mlx5_core_dev *dev, struct notifier_block *nb)
{
- if (!dev->priv.vhca_state_notifier)
- return -EOPNOTSUPP;
- return blocking_notifier_chain_register(&dev->priv.vhca_state_notifier->n_head, nb);
+ return blocking_notifier_chain_register(&dev->priv.vhca_state_n_head,
+ nb);
}
void mlx5_vhca_event_notifier_unregister(struct mlx5_core_dev *dev, struct notifier_block *nb)
{
- blocking_notifier_chain_unregister(&dev->priv.vhca_state_notifier->n_head, nb);
+ blocking_notifier_chain_unregister(&dev->priv.vhca_state_n_head, nb);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h
index 1725ba64f8af..52790423874c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/vhca_event.h
@@ -18,6 +18,7 @@ static inline bool mlx5_vhca_event_supported(const struct mlx5_core_dev *dev)
}
void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *set_hca_cap);
+void mlx5_vhca_state_notifier_init(struct mlx5_core_dev *dev);
int mlx5_vhca_event_init(struct mlx5_core_dev *dev);
void mlx5_vhca_event_cleanup(struct mlx5_core_dev *dev);
void mlx5_vhca_event_start(struct mlx5_core_dev *dev);
@@ -37,6 +38,10 @@ static inline void mlx5_vhca_state_cap_handle(struct mlx5_core_dev *dev, void *s
{
}
+static inline void mlx5_vhca_state_notifier_init(struct mlx5_core_dev *dev)
+{
+}
+
static inline int mlx5_vhca_event_init(struct mlx5_core_dev *dev)
{
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/send.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/send.c
index 24ef7d66fa8a..7510c46e58a5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/send.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/send.c
@@ -873,12 +873,6 @@ err_free_sqc:
return err;
}
-static void hws_cq_complete(struct mlx5_core_cq *mcq,
- struct mlx5_eqe *eqe)
-{
- pr_err("CQ completion CQ: #%u\n", mcq->cqn);
-}
-
static int hws_send_ring_alloc_cq(struct mlx5_core_dev *mdev,
int numa_node,
struct mlx5hws_send_engine *queue,
@@ -901,7 +895,6 @@ static int hws_send_ring_alloc_cq(struct mlx5_core_dev *mdev,
mcq->cqe_sz = 64;
mcq->set_ci_db = cq->wq_ctrl.db.db;
mcq->arm_db = cq->wq_ctrl.db.db + 1;
- mcq->comp = hws_cq_complete;
for (i = 0; i < mlx5_cqwq_get_size(&cq->wq); i++) {
cqe = mlx5_cqwq_get_wqe(&cq->wq, i);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_domain.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_domain.c
index 65740bb68b09..e8c67ed9f748 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_domain.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_domain.c
@@ -410,7 +410,7 @@ static int dr_domain_caps_init(struct mlx5_core_dev *mdev,
switch (dmn->type) {
case MLX5DR_DOMAIN_TYPE_NIC_RX:
if (!DR_DOMAIN_SW_STEERING_SUPPORTED(dmn, rx))
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
dmn->info.supp_sw_steering = true;
dmn->info.rx.type = DR_DOMAIN_NIC_TYPE_RX;
@@ -419,7 +419,7 @@ static int dr_domain_caps_init(struct mlx5_core_dev *mdev,
break;
case MLX5DR_DOMAIN_TYPE_NIC_TX:
if (!DR_DOMAIN_SW_STEERING_SUPPORTED(dmn, tx))
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
dmn->info.supp_sw_steering = true;
dmn->info.tx.type = DR_DOMAIN_NIC_TYPE_TX;
@@ -428,10 +428,10 @@ static int dr_domain_caps_init(struct mlx5_core_dev *mdev,
break;
case MLX5DR_DOMAIN_TYPE_FDB:
if (!dmn->info.caps.eswitch_manager)
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
if (!DR_DOMAIN_SW_STEERING_SUPPORTED(dmn, fdb))
- return -ENOTSUPP;
+ return -EOPNOTSUPP;
dmn->info.rx.type = DR_DOMAIN_NIC_TYPE_RX;
dmn->info.tx.type = DR_DOMAIN_NIC_TYPE_TX;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_send.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_send.c
index 077a77fde670..d034372fa047 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_send.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/sws/dr_send.c
@@ -1049,12 +1049,6 @@ static int dr_prepare_qp_to_rts(struct mlx5dr_domain *dmn)
return 0;
}
-static void dr_cq_complete(struct mlx5_core_cq *mcq,
- struct mlx5_eqe *eqe)
-{
- pr_err("CQ completion CQ: #%u\n", mcq->cqn);
-}
-
static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev,
struct mlx5_uars_page *uar,
size_t ncqe)
@@ -1089,6 +1083,13 @@ static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev,
cqe->op_own = MLX5_CQE_INVALID << 4 | MLX5_CQE_OWNER_MASK;
}
+ cq->mcq.cqe_sz = 64;
+ cq->mcq.set_ci_db = cq->wq_ctrl.db.db;
+ cq->mcq.arm_db = cq->wq_ctrl.db.db + 1;
+ *cq->mcq.set_ci_db = 0;
+ cq->mcq.vector = 0;
+ cq->mdev = mdev;
+
inlen = MLX5_ST_SZ_BYTES(create_cq_in) +
sizeof(u64) * cq->wq_ctrl.buf.npages;
in = kvzalloc(inlen, GFP_KERNEL);
@@ -1112,27 +1113,12 @@ static struct mlx5dr_cq *dr_create_cq(struct mlx5_core_dev *mdev,
pas = (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas);
mlx5_fill_page_frag_array(&cq->wq_ctrl.buf, pas);
- cq->mcq.comp = dr_cq_complete;
-
err = mlx5_core_create_cq(mdev, &cq->mcq, in, inlen, out, sizeof(out));
kvfree(in);
if (err)
goto err_cqwq;
- cq->mcq.cqe_sz = 64;
- cq->mcq.set_ci_db = cq->wq_ctrl.db.db;
- cq->mcq.arm_db = cq->wq_ctrl.db.db + 1;
- *cq->mcq.set_ci_db = 0;
-
- /* set no-zero value, in order to avoid the HW to run db-recovery on
- * CQ that used in polling mode.
- */
- *cq->mcq.arm_db = cpu_to_be32(2 << 28);
-
- cq->mcq.vector = 0;
- cq->mdev = mdev;
-
return cq;
err_cqwq:
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vport.c b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
index 2ed2e530b07d..306affbcfd3b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vport.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vport.c
@@ -78,15 +78,14 @@ int mlx5_modify_vport_admin_state(struct mlx5_core_dev *mdev, u8 opmod,
}
static int mlx5_query_nic_vport_context(struct mlx5_core_dev *mdev, u16 vport,
- u32 *out)
+ bool other_vport, u32 *out)
{
u32 in[MLX5_ST_SZ_DW(query_nic_vport_context_in)] = {};
MLX5_SET(query_nic_vport_context_in, in, opcode,
MLX5_CMD_OP_QUERY_NIC_VPORT_CONTEXT);
MLX5_SET(query_nic_vport_context_in, in, vport_number, vport);
- if (vport)
- MLX5_SET(query_nic_vport_context_in, in, other_vport, 1);
+ MLX5_SET(query_nic_vport_context_in, in, other_vport, other_vport);
return mlx5_cmd_exec_inout(mdev, query_nic_vport_context, in, out);
}
@@ -97,7 +96,7 @@ int mlx5_query_nic_vport_min_inline(struct mlx5_core_dev *mdev,
u32 out[MLX5_ST_SZ_DW(query_nic_vport_context_out)] = {};
int err;
- err = mlx5_query_nic_vport_context(mdev, vport, out);
+ err = mlx5_query_nic_vport_context(mdev, vport, vport > 0, out);
if (!err)
*min_inline = MLX5_GET(query_nic_vport_context_out, out,
nic_vport_context.min_wqe_inline_mode);
@@ -219,7 +218,7 @@ int mlx5_query_nic_vport_mtu(struct mlx5_core_dev *mdev, u16 *mtu)
if (!out)
return -ENOMEM;
- err = mlx5_query_nic_vport_context(mdev, 0, out);
+ err = mlx5_query_nic_vport_context(mdev, 0, false, out);
if (!err)
*mtu = MLX5_GET(query_nic_vport_context_out, out,
nic_vport_context.mtu);
@@ -429,7 +428,7 @@ int mlx5_query_nic_vport_system_image_guid(struct mlx5_core_dev *mdev,
if (!out)
return -ENOMEM;
- err = mlx5_query_nic_vport_context(mdev, 0, out);
+ err = mlx5_query_nic_vport_context(mdev, 0, false, out);
if (err)
goto out;
@@ -451,7 +450,7 @@ int mlx5_query_nic_vport_sd_group(struct mlx5_core_dev *mdev, u8 *sd_group)
if (!out)
return -ENOMEM;
- err = mlx5_query_nic_vport_context(mdev, 0, out);
+ err = mlx5_query_nic_vport_context(mdev, 0, false, out);
if (err)
goto out;
@@ -462,7 +461,8 @@ out:
return err;
}
-int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev, u64 *node_guid)
+int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev,
+ u16 vport, bool other_vport, u64 *node_guid)
{
u32 *out;
int outlen = MLX5_ST_SZ_BYTES(query_nic_vport_context_out);
@@ -472,7 +472,7 @@ int mlx5_query_nic_vport_node_guid(struct mlx5_core_dev *mdev, u64 *node_guid)
if (!out)
return -ENOMEM;
- err = mlx5_query_nic_vport_context(mdev, 0, out);
+ err = mlx5_query_nic_vport_context(mdev, vport, other_vport, out);
if (err)
goto out;
@@ -529,7 +529,7 @@ int mlx5_query_nic_vport_qkey_viol_cntr(struct mlx5_core_dev *mdev,
if (!out)
return -ENOMEM;
- err = mlx5_query_nic_vport_context(mdev, 0, out);
+ err = mlx5_query_nic_vport_context(mdev, 0, false, out);
if (err)
goto out;
@@ -804,7 +804,7 @@ int mlx5_query_nic_vport_promisc(struct mlx5_core_dev *mdev,
if (!out)
return -ENOMEM;
- err = mlx5_query_nic_vport_context(mdev, vport, out);
+ err = mlx5_query_nic_vport_context(mdev, vport, vport > 0, out);
if (err)
goto out;
@@ -908,7 +908,7 @@ int mlx5_nic_vport_query_local_lb(struct mlx5_core_dev *mdev, bool *status)
if (!out)
return -ENOMEM;
- err = mlx5_query_nic_vport_context(mdev, 0, out);
+ err = mlx5_query_nic_vport_context(mdev, 0, false, out);
if (err)
goto out;
@@ -1190,6 +1190,25 @@ u64 mlx5_query_nic_system_image_guid(struct mlx5_core_dev *mdev)
}
EXPORT_SYMBOL_GPL(mlx5_query_nic_system_image_guid);
+void mlx5_query_nic_sw_system_image_guid(struct mlx5_core_dev *mdev, u8 *buf,
+ u8 *len)
+{
+ u64 fw_system_image_guid;
+
+ *len = 0;
+
+ fw_system_image_guid = mlx5_query_nic_system_image_guid(mdev);
+ if (!fw_system_image_guid)
+ return;
+
+ memcpy(buf, &fw_system_image_guid, sizeof(fw_system_image_guid));
+ *len += sizeof(fw_system_image_guid);
+
+ if (MLX5_CAP_GEN_2(mdev, load_balance_id) &&
+ MLX5_CAP_GEN_2(mdev, lag_per_mp_group))
+ buf[(*len)++] = MLX5_CAP_GEN_2(mdev, load_balance_id);
+}
+
static bool mlx5_vport_use_vhca_id_as_func_id(struct mlx5_core_dev *dev,
u16 vport_num, u16 *vhca_id)
{
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/wc.c b/drivers/net/ethernet/mellanox/mlx5/core/wc.c
index 05e5fd777d4f..815a7c97d6b0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/wc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/wc.c
@@ -9,6 +9,7 @@
#if IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && IS_ENABLED(CONFIG_ARM64)
#include <asm/neon.h>
+#include <asm/simd.h>
#endif
#define TEST_WC_NUM_WQES 255
@@ -264,15 +265,15 @@ static void mlx5_iowrite64_copy(struct mlx5_wc_sq *sq, __be32 mmio_wqe[16],
{
#if IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && IS_ENABLED(CONFIG_ARM64)
if (cpu_has_neon()) {
- kernel_neon_begin();
- asm volatile
- (".arch_extension simd\n\t"
- "ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [%0]\n\t"
- "st1 {v0.16b, v1.16b, v2.16b, v3.16b}, [%1]"
- :
- : "r"(mmio_wqe), "r"(sq->bfreg.map + offset)
- : "memory", "v0", "v1", "v2", "v3");
- kernel_neon_end();
+ scoped_ksimd() {
+ asm volatile(
+ ".arch_extension simd\n\t"
+ "ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [%0]\n\t"
+ "st1 {v0.16b, v1.16b, v2.16b, v3.16b}, [%1]"
+ :
+ : "r"(mmio_wqe), "r"(sq->bfreg.map + offset)
+ : "memory", "v0", "v1", "v2", "v3");
+ }
return;
}
#endif
diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
index b032d5a4b3b8..10f5bc4892fc 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/core_linecards.c
@@ -601,6 +601,8 @@ int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,
err = devlink_info_version_fixed_put(req,
DEVLINK_INFO_VERSION_GENERIC_FW_PSID,
info->psid);
+ if (err)
+ goto unlock;
sprintf(buf, "%u.%u.%u", info->fw_major, info->fw_minor,
info->fw_sub_minor);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
index b1d08e958bf9..69f9da9fb305 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c
@@ -1489,7 +1489,8 @@ mlxsw_sp_acl_tcam_vregion_rehash(struct mlxsw_sp *mlxsw_sp,
static int
mlxsw_sp_acl_tcam_region_rehash_intrvl_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_sp_acl_tcam *tcam;
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
index 6a4a81c63451..353fd9ca89a6 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c
@@ -830,8 +830,10 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp,
return -EINVAL;
rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie);
- if (!rule)
- return -EINVAL;
+ if (!rule) {
+ err = -EINVAL;
+ goto err_rule_get_stats;
+ }
err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes,
&drops, &lastuse, &used_hw_stats);
diff --git a/drivers/net/ethernet/meta/Kconfig b/drivers/net/ethernet/meta/Kconfig
index 3ba527514f1e..ca5c7ac2a5bc 100644
--- a/drivers/net/ethernet/meta/Kconfig
+++ b/drivers/net/ethernet/meta/Kconfig
@@ -19,13 +19,14 @@ if NET_VENDOR_META
config FBNIC
tristate "Meta Platforms Host Network Interface"
- depends on X86_64 || COMPILE_TEST
+ depends on 64BIT || COMPILE_TEST
depends on !S390
depends on MAX_SKB_FRAGS < 22
depends on PCI_MSI
depends on PTP_1588_CLOCK_OPTIONAL
select NET_DEVLINK
select PAGE_POOL
+ select PCS_XPCS
select PHYLINK
select PLDMFW
help
diff --git a/drivers/net/ethernet/meta/fbnic/Makefile b/drivers/net/ethernet/meta/fbnic/Makefile
index 15e8ff649615..72c41af65364 100644
--- a/drivers/net/ethernet/meta/fbnic/Makefile
+++ b/drivers/net/ethernet/meta/fbnic/Makefile
@@ -21,6 +21,7 @@ fbnic-y := fbnic_csr.o \
fbnic_pci.o \
fbnic_phylink.o \
fbnic_rpc.o \
+ fbnic_mdio.o \
fbnic_time.o \
fbnic_tlv.o \
fbnic_txrx.o \
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic.h b/drivers/net/ethernet/meta/fbnic/fbnic.h
index b03e5a3d5144..779a083b9215 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic.h
@@ -34,7 +34,7 @@ struct fbnic_dev {
u32 __iomem *uc_addr4;
const struct fbnic_mac *mac;
unsigned int fw_msix_vector;
- unsigned int pcs_msix_vector;
+ unsigned int mac_msix_vector;
unsigned short num_irqs;
struct {
@@ -83,6 +83,10 @@ struct fbnic_dev {
/* Last @time_high refresh time in jiffies (to catch stalls) */
unsigned long last_read;
+ /* PMD specific data */
+ unsigned long end_of_pmd_training;
+ u8 pmd_state;
+
/* Local copy of hardware statistics */
struct fbnic_hw_stats hw_stats;
@@ -91,6 +95,9 @@ struct fbnic_dev {
u64 prev_firmware_time;
struct fbnic_fw_log fw_log;
+
+ /* MDIO bus for PHYs */
+ struct mii_bus *mdio_bus;
};
/* Reserve entry 0 in the MSI-X "others" array until we have filled all
@@ -175,8 +182,8 @@ void fbnic_fw_free_mbx(struct fbnic_dev *fbd);
void fbnic_hwmon_register(struct fbnic_dev *fbd);
void fbnic_hwmon_unregister(struct fbnic_dev *fbd);
-int fbnic_pcs_request_irq(struct fbnic_dev *fbd);
-void fbnic_pcs_free_irq(struct fbnic_dev *fbd);
+int fbnic_mac_request_irq(struct fbnic_dev *fbd);
+void fbnic_mac_free_irq(struct fbnic_dev *fbd);
void fbnic_napi_name_irqs(struct fbnic_dev *fbd);
int fbnic_napi_request_irq(struct fbnic_dev *fbd,
@@ -200,6 +207,8 @@ void fbnic_dbg_exit(void);
void fbnic_rpc_reset_valid_entries(struct fbnic_dev *fbd);
+int fbnic_mdiobus_create(struct fbnic_dev *fbd);
+
void fbnic_csr_get_regs(struct fbnic_dev *fbd, u32 *data, u32 *regs_version);
int fbnic_csr_regs_len(struct fbnic_dev *fbd);
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h
index d3a7ad921f18..422265dc7abd 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_csr.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_csr.h
@@ -787,6 +787,8 @@ enum {
/* MAC PCS registers */
#define FBNIC_CSR_START_PCS 0x10000 /* CSR section delimiter */
+#define FBNIC_PCS_PAGE(n) (0x10000 + 0x400 * (n)) /* 0x40000 + 1024*n */
+#define FBNIC_PCS(reg, n) ((reg) + FBNIC_PCS_PAGE(n))
#define FBNIC_CSR_END_PCS 0x10668 /* CSR section delimiter */
#define FBNIC_CSR_START_RSFEC 0x10800 /* CSR section delimiter */
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c
index 95fac020eb93..693ebdf38705 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c
@@ -1863,6 +1863,14 @@ fbnic_get_rmon_stats(struct net_device *netdev,
*ranges = fbnic_rmon_ranges;
}
+static void fbnic_get_link_ext_stats(struct net_device *netdev,
+ struct ethtool_link_ext_stats *stats)
+{
+ struct fbnic_net *fbn = netdev_priv(netdev);
+
+ stats->link_down_events = fbn->link_down_events;
+}
+
static const struct ethtool_ops fbnic_ethtool_ops = {
.cap_link_lanes_supported = true,
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
@@ -1874,6 +1882,7 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
.get_regs_len = fbnic_get_regs_len,
.get_regs = fbnic_get_regs,
.get_link = ethtool_op_get_link,
+ .get_link_ext_stats = fbnic_get_link_ext_stats,
.get_coalesce = fbnic_get_coalesce,
.set_coalesce = fbnic_set_coalesce,
.get_ringparam = fbnic_get_ringparam,
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
index c87cb9ed09e7..d8d9b6cfde82 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_fw.c
@@ -201,7 +201,7 @@ static int fbnic_mbx_alloc_rx_msgs(struct fbnic_dev *fbd)
return -ENODEV;
/* Fill all but 1 unused descriptors in the Rx queue. */
- count = (head - tail - 1) % FBNIC_IPC_MBX_DESC_LEN;
+ count = (head - tail - 1) & (FBNIC_IPC_MBX_DESC_LEN - 1);
while (!err && count--) {
struct fbnic_tlv_msg *msg;
@@ -878,11 +878,11 @@ msg_err:
* @fbd: FBNIC device structure
* @cmpl_data: Completion struct to store coredump
* @offset: Offset into coredump requested
- * @length: Length of section of cordeump to fetch
+ * @length: Length of section of coredump to fetch
*
* Return: zero on success, negative errno on failure
*
- * Asks the firmware to provide a section of the cordeump back in a message.
+ * Asks the firmware to provide a section of the coredump back in a message.
* The response will have an offset and size matching the values provided.
*/
int fbnic_fw_xmit_coredump_read_msg(struct fbnic_dev *fbd,
@@ -1868,7 +1868,7 @@ int fbnic_fw_xmit_rpc_macda_sync(struct fbnic_dev *fbd)
if (err)
goto free_message;
- /* Send message of to FW notifying it of current RPC config */
+ /* Send message off to FW notifying it of current RPC config */
err = fbnic_mbx_map_tlv_msg(fbd, msg);
if (err)
goto free_message;
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c
index 1c88a2bf3a7a..02e8b0b257fe 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_irq.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_irq.c
@@ -118,12 +118,12 @@ void fbnic_fw_free_mbx(struct fbnic_dev *fbd)
fbd->fw_msix_vector = 0;
}
-static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data)
+static irqreturn_t fbnic_mac_msix_intr(int __always_unused irq, void *data)
{
struct fbnic_dev *fbd = data;
struct fbnic_net *fbn;
- if (fbd->mac->pcs_get_link_event(fbd) == FBNIC_LINK_EVENT_NONE) {
+ if (fbd->mac->get_link_event(fbd) == FBNIC_LINK_EVENT_NONE) {
fbnic_wr32(fbd, FBNIC_INTR_MASK_CLEAR(0),
1u << FBNIC_PCS_MSIX_ENTRY);
return IRQ_HANDLED;
@@ -131,26 +131,28 @@ static irqreturn_t fbnic_pcs_msix_intr(int __always_unused irq, void *data)
fbn = netdev_priv(fbd->netdev);
- phylink_pcs_change(&fbn->phylink_pcs, false);
+ /* Record link down events */
+ if (!fbd->mac->get_link(fbd, fbn->aui, fbn->fec))
+ phylink_pcs_change(fbn->pcs, false);
return IRQ_HANDLED;
}
/**
- * fbnic_pcs_request_irq - Configure the PCS to enable it to advertise link
+ * fbnic_mac_request_irq - Configure the MAC to enable it to advertise link
* @fbd: Pointer to device to initialize
*
- * This function provides basic bringup for the MAC/PCS IRQ. For now the IRQ
+ * This function provides basic bringup for the MAC/PHY IRQ. For now the IRQ
* will remain disabled until we start the MAC/PCS/PHY logic via phylink.
*
* Return: non-zero on failure.
**/
-int fbnic_pcs_request_irq(struct fbnic_dev *fbd)
+int fbnic_mac_request_irq(struct fbnic_dev *fbd)
{
struct pci_dev *pdev = to_pci_dev(fbd->dev);
int vector, err;
- WARN_ON(fbd->pcs_msix_vector);
+ WARN_ON(fbd->mac_msix_vector);
vector = pci_irq_vector(pdev, FBNIC_PCS_MSIX_ENTRY);
if (vector < 0)
@@ -159,7 +161,7 @@ int fbnic_pcs_request_irq(struct fbnic_dev *fbd)
/* Request the IRQ for PCS link vector.
* Map PCS cause to it, and unmask it
*/
- err = request_irq(vector, &fbnic_pcs_msix_intr, 0,
+ err = request_irq(vector, &fbnic_mac_msix_intr, 0,
fbd->netdev->name, fbd);
if (err)
return err;
@@ -168,22 +170,22 @@ int fbnic_pcs_request_irq(struct fbnic_dev *fbd)
fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
- fbd->pcs_msix_vector = vector;
+ fbd->mac_msix_vector = vector;
return 0;
}
/**
- * fbnic_pcs_free_irq - Teardown the PCS IRQ to prepare for stopping
+ * fbnic_mac_free_irq - Teardown the MAC IRQ to prepare for stopping
* @fbd: Pointer to device that is stopping
*
- * This function undoes the work done in fbnic_pcs_request_irq and prepares
+ * This function undoes the work done in fbnic_mac_request_irq and prepares
* the device to no longer receive traffic on the host interface.
**/
-void fbnic_pcs_free_irq(struct fbnic_dev *fbd)
+void fbnic_mac_free_irq(struct fbnic_dev *fbd)
{
/* Vector has already been freed */
- if (!fbd->pcs_msix_vector)
+ if (!fbd->mac_msix_vector)
return;
/* Disable interrupt */
@@ -192,14 +194,14 @@ void fbnic_pcs_free_irq(struct fbnic_dev *fbd)
fbnic_wrfl(fbd);
/* Synchronize IRQ to prevent race that would unmask vector */
- synchronize_irq(fbd->pcs_msix_vector);
+ synchronize_irq(fbd->mac_msix_vector);
/* Mask the vector */
fbnic_wr32(fbd, FBNIC_INTR_MASK_SET(0), 1u << FBNIC_PCS_MSIX_ENTRY);
/* Free the vector */
- free_irq(fbd->pcs_msix_vector, fbd);
- fbd->pcs_msix_vector = 0;
+ free_irq(fbd->mac_msix_vector, fbd);
+ fbd->mac_msix_vector = 0;
}
void fbnic_synchronize_irq(struct fbnic_dev *fbd, int nr)
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c
index 2a84bd1d7e26..fc7abea4ef5b 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.c
@@ -434,14 +434,14 @@ static void fbnic_mac_tx_pause_config(struct fbnic_dev *fbd, bool tx_pause)
wr32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL, rxb_pause_ctrl);
}
-static int fbnic_pcs_get_link_event_asic(struct fbnic_dev *fbd)
+static int fbnic_mac_get_link_event(struct fbnic_dev *fbd)
{
- u32 pcs_intr_mask = rd32(fbd, FBNIC_SIG_PCS_INTR_STS);
+ u32 intr_mask = rd32(fbd, FBNIC_SIG_PCS_INTR_STS);
- if (pcs_intr_mask & FBNIC_SIG_PCS_INTR_LINK_DOWN)
+ if (intr_mask & FBNIC_SIG_PCS_INTR_LINK_DOWN)
return FBNIC_LINK_EVENT_DOWN;
- return (pcs_intr_mask & FBNIC_SIG_PCS_INTR_LINK_UP) ?
+ return (intr_mask & FBNIC_SIG_PCS_INTR_LINK_UP) ?
FBNIC_LINK_EVENT_UP : FBNIC_LINK_EVENT_NONE;
}
@@ -466,9 +466,8 @@ static u32 __fbnic_mac_cmd_config_asic(struct fbnic_dev *fbd,
return command_config;
}
-static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd)
+static bool fbnic_mac_get_link_status(struct fbnic_dev *fbd, u8 aui, u8 fec)
{
- struct fbnic_net *fbn = netdev_priv(fbd->netdev);
u32 pcs_status, lane_mask = ~0;
pcs_status = rd32(fbd, FBNIC_SIG_PCS_OUT0);
@@ -476,7 +475,7 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd)
return false;
/* Define the expected lane mask for the status bits we need to check */
- switch (fbn->aui) {
+ switch (aui) {
case FBNIC_AUI_100GAUI2:
lane_mask = 0xf;
break;
@@ -484,7 +483,7 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd)
lane_mask = 3;
break;
case FBNIC_AUI_LAUI2:
- switch (fbn->fec) {
+ switch (fec) {
case FBNIC_FEC_OFF:
lane_mask = 0x63;
break;
@@ -502,7 +501,7 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd)
}
/* Use an XOR to remove the bits we expect to see set */
- switch (fbn->fec) {
+ switch (fec) {
case FBNIC_FEC_OFF:
lane_mask ^= FIELD_GET(FBNIC_SIG_PCS_OUT0_BLOCK_LOCK,
pcs_status);
@@ -521,7 +520,46 @@ static bool fbnic_mac_get_pcs_link_status(struct fbnic_dev *fbd)
return !lane_mask;
}
-static bool fbnic_pcs_get_link_asic(struct fbnic_dev *fbd)
+static bool fbnic_pmd_update_state(struct fbnic_dev *fbd, bool signal_detect)
+{
+ /* Delay link up for 4 seconds to allow for link training.
+ * The state transitions for this are as follows:
+ *
+ * All states have the following two transitions in common:
+ * Loss of signal -> FBNIC_PMD_INITIALIZE
+ * The condition handled below (!signal)
+ * Reconfiguration -> FBNIC_PMD_INITIALIZE
+ * Occurs when mac_prepare starts a PHY reconfig
+ * FBNIC_PMD_TRAINING:
+ * signal still detected && 4s have passed -> Report link up
+ * When link is brought up in link_up -> FBNIC_PMD_SEND_DATA
+ * FBNIC_PMD_INITIALIZE:
+ * signal detected -> FBNIC_PMD_TRAINING
+ */
+ if (!signal_detect) {
+ fbd->pmd_state = FBNIC_PMD_INITIALIZE;
+ return false;
+ }
+
+ switch (fbd->pmd_state) {
+ case FBNIC_PMD_TRAINING:
+ return time_before(fbd->end_of_pmd_training, jiffies);
+ case FBNIC_PMD_LINK_READY:
+ case FBNIC_PMD_SEND_DATA:
+ return true;
+ }
+
+ fbd->end_of_pmd_training = jiffies + 4 * HZ;
+
+ /* Ensure end_of_training is visible before the state change */
+ smp_wmb();
+
+ fbd->pmd_state = FBNIC_PMD_TRAINING;
+
+ return false;
+}
+
+static bool fbnic_mac_get_link(struct fbnic_dev *fbd, u8 aui, u8 fec)
{
bool link;
@@ -538,7 +576,8 @@ static bool fbnic_pcs_get_link_asic(struct fbnic_dev *fbd)
wr32(fbd, FBNIC_SIG_PCS_INTR_STS,
FBNIC_SIG_PCS_INTR_LINK_DOWN | FBNIC_SIG_PCS_INTR_LINK_UP);
- link = fbnic_mac_get_pcs_link_status(fbd);
+ link = fbnic_mac_get_link_status(fbd, aui, fec);
+ link = fbnic_pmd_update_state(fbd, link);
/* Enable interrupt to only capture changes in link state */
wr32(fbd, FBNIC_SIG_PCS_INTR_MASK,
@@ -586,20 +625,15 @@ void fbnic_mac_get_fw_settings(struct fbnic_dev *fbd, u8 *aui, u8 *fec)
}
}
-static int fbnic_pcs_enable_asic(struct fbnic_dev *fbd)
+static void fbnic_mac_prepare(struct fbnic_dev *fbd, u8 aui, u8 fec)
{
/* Mask and clear the PCS interrupt, will be enabled by link handler */
wr32(fbd, FBNIC_SIG_PCS_INTR_MASK, ~0);
wr32(fbd, FBNIC_SIG_PCS_INTR_STS, ~0);
- return 0;
-}
-
-static void fbnic_pcs_disable_asic(struct fbnic_dev *fbd)
-{
- /* Mask and clear the PCS interrupt */
- wr32(fbd, FBNIC_SIG_PCS_INTR_MASK, ~0);
- wr32(fbd, FBNIC_SIG_PCS_INTR_STS, ~0);
+ /* If we don't have link tear it all down and start over */
+ if (!fbnic_mac_get_link_status(fbd, aui, fec))
+ fbd->pmd_state = FBNIC_PMD_INITIALIZE;
}
static void fbnic_mac_link_down_asic(struct fbnic_dev *fbd)
@@ -867,10 +901,9 @@ exit_free:
static const struct fbnic_mac fbnic_mac_asic = {
.init_regs = fbnic_mac_init_regs,
- .pcs_enable = fbnic_pcs_enable_asic,
- .pcs_disable = fbnic_pcs_disable_asic,
- .pcs_get_link = fbnic_pcs_get_link_asic,
- .pcs_get_link_event = fbnic_pcs_get_link_event_asic,
+ .get_link = fbnic_mac_get_link,
+ .get_link_event = fbnic_mac_get_link_event,
+ .prepare = fbnic_mac_prepare,
.get_fec_stats = fbnic_mac_get_fec_stats,
.get_pcs_stats = fbnic_mac_get_pcs_stats,
.get_eth_mac_stats = fbnic_mac_get_eth_mac_stats,
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h
index ede5ff0dae22..f08fe8b7c497 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_mac.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_mac.h
@@ -10,6 +10,24 @@ struct fbnic_dev;
#define FBNIC_MAX_JUMBO_FRAME_SIZE 9742
+/* States loosely based on section 136.8.11.7.5 of IEEE 802.3-2022 Ethernet
+ * Standard. These are needed to track the state of the PHY as it has a delay
+ * of several seconds from the time link comes up until it has completed
+ * training that we need to wait to report the link.
+ *
+ * Currently we treat training as a single block as this is managed by the
+ * firmware.
+ *
+ * We have FBNIC_PMD_SEND_DATA set to 0 as the expected default at driver load
+ * and we initialize the structure containing it to zero at allocation.
+ */
+enum {
+ FBNIC_PMD_SEND_DATA = 0x0,
+ FBNIC_PMD_INITIALIZE = 0x1,
+ FBNIC_PMD_TRAINING = 0x2,
+ FBNIC_PMD_LINK_READY = 0x3,
+};
+
enum {
FBNIC_LINK_EVENT_NONE = 0,
FBNIC_LINK_EVENT_UP = 1,
@@ -38,6 +56,7 @@ enum {
FBNIC_AUI_50GAUI1 = 2, /* 53.125GBd 53.125 * 1 */
FBNIC_AUI_100GAUI2 = 3, /* 106.25GBd 53.125 * 2 */
FBNIC_AUI_UNKNOWN = 4,
+ __FBNIC_AUI_MAX__
};
#define FBNIC_AUI_MODE_R2 (FBNIC_AUI_LAUI2)
@@ -55,15 +74,15 @@ enum fbnic_sensor_id {
* void (*init_regs)(struct fbnic_dev *fbd);
* Initialize MAC registers to enable Tx/Rx paths and FIFOs.
*
- * void (*pcs_enable)(struct fbnic_dev *fbd);
- * Configure and enable PCS to enable link if not already enabled
- * void (*pcs_disable)(struct fbnic_dev *fbd);
- * Shutdown the link if we are the only consumer of it.
- * bool (*pcs_get_link)(struct fbnic_dev *fbd);
- * Check PCS link status
- * int (*pcs_get_link_event)(struct fbnic_dev *fbd)
+ * int (*get_link_event)(struct fbnic_dev *fbd)
* Get the current link event status, reports true if link has
* changed to either FBNIC_LINK_EVENT_DOWN or FBNIC_LINK_EVENT_UP
+ * bool (*get_link)(struct fbnic_dev *fbd, u8 aui, u8 fec);
+ * Check link status
+ *
+ * void (*prepare)(struct fbnic_dev *fbd, u8 aui, u8 fec);
+ * Prepare PHY for init by fetching settings, disabling interrupts,
+ * and sending an updated PHY config to FW if needed.
*
* void (*link_down)(struct fbnic_dev *fbd);
* Configure MAC for link down event
@@ -74,10 +93,10 @@ enum fbnic_sensor_id {
struct fbnic_mac {
void (*init_regs)(struct fbnic_dev *fbd);
- int (*pcs_enable)(struct fbnic_dev *fbd);
- void (*pcs_disable)(struct fbnic_dev *fbd);
- bool (*pcs_get_link)(struct fbnic_dev *fbd);
- int (*pcs_get_link_event)(struct fbnic_dev *fbd);
+ int (*get_link_event)(struct fbnic_dev *fbd);
+ bool (*get_link)(struct fbnic_dev *fbd, u8 aui, u8 fec);
+
+ void (*prepare)(struct fbnic_dev *fbd, u8 aui, u8 fec);
void (*get_fec_stats)(struct fbnic_dev *fbd, bool reset,
struct fbnic_fec_stats *fec_stats);
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c
new file mode 100644
index 000000000000..709041f7fc43
--- /dev/null
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_mdio.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) Meta Platforms, Inc. and affiliates. */
+
+#include <linux/mdio.h>
+#include <linux/pcs/pcs-xpcs.h>
+
+#include "fbnic.h"
+#include "fbnic_netdev.h"
+
+#define DW_VENDOR BIT(15)
+#define FBNIC_PCS_VENDOR BIT(9)
+#define FBNIC_PCS_ZERO_MASK (DW_VENDOR - FBNIC_PCS_VENDOR)
+
+static int
+fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum)
+{
+ u8 aui = FBNIC_AUI_UNKNOWN;
+ struct fbnic_net *fbn;
+ int ret = 0;
+
+ /* We don't need a second PMD, just one can handle both lanes */
+ if (addr)
+ return 0;
+
+ if (fbd->netdev) {
+ fbn = netdev_priv(fbd->netdev);
+ if (fbn->aui < FBNIC_AUI_UNKNOWN)
+ aui = fbn->aui;
+ }
+
+ switch (regnum) {
+ case MDIO_DEVID1:
+ ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16;
+ break;
+ case MDIO_DEVID2:
+ ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff;
+ break;
+ case MDIO_DEVS1:
+ ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;
+ break;
+ case MDIO_STAT2:
+ ret = MDIO_STAT2_DEVPRST_VAL;
+ break;
+ case MDIO_PMA_RXDET:
+ /* If training isn't complete default to 0 */
+ if (fbd->pmd_state != FBNIC_PMD_SEND_DATA)
+ break;
+ /* Report either 1 or 2 lanes detected depending on config */
+ ret = (MDIO_PMD_RXDET_GLOBAL | MDIO_PMD_RXDET_0) |
+ ((aui & FBNIC_AUI_MODE_R2) *
+ (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2));
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(fbd->dev,
+ "SWMII PMD Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
+ addr, regnum, ret);
+
+ return ret;
+}
+
+static int
+fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum)
+{
+ int ret, offset = 0;
+
+ /* We will need access to both PCS instances to get config info */
+ if (addr >= 2)
+ return 0;
+
+ /* Report 0 for reserved registers */
+ if (regnum & FBNIC_PCS_ZERO_MASK)
+ return 0;
+
+ /* Intercept and return correct ID for PCS */
+ if (regnum == MDIO_DEVID1)
+ return DW_XPCS_ID >> 16;
+ if (regnum == MDIO_DEVID2)
+ return DW_XPCS_ID & 0xffff;
+ if (regnum == MDIO_DEVS1)
+ return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS;
+
+ /* Swap vendor page bit for FBNIC PCS vendor page bit */
+ if (regnum & DW_VENDOR)
+ offset ^= DW_VENDOR | FBNIC_PCS_VENDOR;
+
+ ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset));
+
+ dev_dbg(fbd->dev,
+ "SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n",
+ addr, regnum, ret);
+
+ return ret;
+}
+
+static int
+fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
+{
+ struct fbnic_dev *fbd = bus->priv;
+
+ if (devnum == MDIO_MMD_PMAPMD)
+ return fbnic_mdio_read_pmd(fbd, addr, regnum);
+
+ if (devnum == MDIO_MMD_PCS)
+ return fbnic_mdio_read_pcs(fbd, addr, regnum);
+
+ return 0;
+}
+
+static void
+fbnic_mdio_write_pmd(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
+{
+ dev_dbg(fbd->dev,
+ "SWMII PMD Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
+ addr, regnum, val);
+}
+
+static void
+fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val)
+{
+ dev_dbg(fbd->dev,
+ "SWMII PCS Wr: Addr: %d RegNum: %d Value: 0x%04x\n",
+ addr, regnum, val);
+
+ /* Allow access to both halves of PCS for 50R2 config */
+ if (addr > 2)
+ return;
+
+ /* Skip write for reserved registers */
+ if (regnum & FBNIC_PCS_ZERO_MASK)
+ return;
+
+ /* Swap vendor page bit for FBNIC PCS vendor page bit */
+ if (regnum & DW_VENDOR)
+ regnum ^= DW_VENDOR | FBNIC_PCS_VENDOR;
+
+ fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val);
+}
+
+static int
+fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum,
+ int regnum, u16 val)
+{
+ struct fbnic_dev *fbd = bus->priv;
+
+ if (devnum == MDIO_MMD_PMAPMD)
+ fbnic_mdio_write_pmd(fbd, addr, regnum, val);
+
+ if (devnum == MDIO_MMD_PCS)
+ fbnic_mdio_write_pcs(fbd, addr, regnum, val);
+
+ return 0;
+}
+
+/**
+ * fbnic_mdiobus_create - Create an MDIO bus to allow interfacing w/ PHYs
+ * @fbd: Pointer to FBNIC device structure to populate bus on
+ *
+ * Initialize an MDIO bus and place a pointer to it on the fbd struct. This bus
+ * will be used to interface with the PMA/PMD and PCS.
+ *
+ * Return: 0 on success, negative on failure
+ **/
+int fbnic_mdiobus_create(struct fbnic_dev *fbd)
+{
+ struct mii_bus *bus;
+ int err;
+
+ bus = devm_mdiobus_alloc(fbd->dev);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->name = "fbnic_mii_bus";
+ bus->read_c45 = &fbnic_mdio_read_c45;
+ bus->write_c45 = &fbnic_mdio_write_c45;
+
+ /* Disable PHY auto probing. We will add PCS manually */
+ bus->phy_mask = ~0;
+
+ bus->parent = fbd->dev;
+ bus->priv = fbd;
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(fbd->dev));
+
+ err = devm_mdiobus_register(fbd->dev, bus);
+ if (err) {
+ dev_err(fbd->dev, "Failed to create MDIO bus: %d\n", err);
+ return err;
+ }
+
+ fbd->mdio_bus = bus;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
index e95be0e7bd9e..81c9d5c9a4b2 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.c
@@ -44,7 +44,7 @@ int __fbnic_open(struct fbnic_net *fbn)
if (err)
goto time_stop;
- err = fbnic_pcs_request_irq(fbd);
+ err = fbnic_mac_request_irq(fbd);
if (err)
goto time_stop;
@@ -86,10 +86,10 @@ static int fbnic_stop(struct net_device *netdev)
{
struct fbnic_net *fbn = netdev_priv(netdev);
+ fbnic_mac_free_irq(fbn->fbd);
phylink_suspend(fbn->phylink, fbnic_bmc_present(fbn->fbd));
fbnic_down(fbn);
- fbnic_pcs_free_irq(fbn->fbd);
fbnic_time_stop(fbn);
fbnic_fw_xmit_ownership_msg(fbn->fbd, false);
@@ -697,10 +697,7 @@ void fbnic_reset_queues(struct fbnic_net *fbn,
**/
void fbnic_netdev_free(struct fbnic_dev *fbd)
{
- struct fbnic_net *fbn = netdev_priv(fbd->netdev);
-
- if (fbn->phylink)
- phylink_destroy(fbn->phylink);
+ fbnic_phylink_destroy(fbd->netdev);
free_netdev(fbd->netdev);
fbd->netdev = NULL;
@@ -802,7 +799,7 @@ struct net_device *fbnic_netdev_alloc(struct fbnic_dev *fbd)
netif_tx_stop_all_queues(netdev);
- if (fbnic_phylink_init(netdev)) {
+ if (fbnic_phylink_create(netdev)) {
fbnic_netdev_free(fbd);
return NULL;
}
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h
index b0a87c57910f..9129a658f8fa 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_netdev.h
@@ -44,7 +44,7 @@ struct fbnic_net {
struct phylink *phylink;
struct phylink_config phylink_config;
- struct phylink_pcs phylink_pcs;
+ struct phylink_pcs *pcs;
u8 aui;
u8 fec;
@@ -73,6 +73,8 @@ struct fbnic_net {
/* Time stamping filter config */
struct kernel_hwtstamp_config hwtstamp_config;
+
+ bool tx_pause;
};
int __fbnic_open(struct fbnic_net *fbn);
@@ -106,8 +108,10 @@ int fbnic_phylink_ethtool_ksettings_get(struct net_device *netdev,
struct ethtool_link_ksettings *cmd);
int fbnic_phylink_get_fecparam(struct net_device *netdev,
struct ethtool_fecparam *fecparam);
+int fbnic_phylink_create(struct net_device *netdev);
+void fbnic_phylink_destroy(struct net_device *netdev);
int fbnic_phylink_init(struct net_device *netdev);
-
+void fbnic_phylink_pmd_training_complete_notify(struct net_device *netdev);
bool fbnic_check_split_frames(struct bpf_prog *prog,
unsigned int mtu, u32 hds_threshold);
#endif /* _FBNIC_NETDEV_H_ */
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
index a7a6b4db8016..9240673c7533 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_pci.c
@@ -185,7 +185,7 @@ static void fbnic_health_check(struct fbnic_dev *fbd)
{
struct fbnic_fw_mbx *tx_mbx = &fbd->mbx[FBNIC_IPC_MBX_TX_IDX];
- /* As long as the heart is beating the FW is healty */
+ /* As long as the heart is beating the FW is healthy */
if (fbd->fw_heartbeat_enabled)
return;
@@ -196,7 +196,7 @@ static void fbnic_health_check(struct fbnic_dev *fbd)
if (tx_mbx->head != tx_mbx->tail)
return;
- fbnic_devlink_fw_report(fbd, "Firmware crashed detected!");
+ fbnic_devlink_fw_report(fbd, "Firmware crash detected!");
fbnic_devlink_otp_check(fbd, "error detected after firmware recovery");
if (fbnic_fw_config_after_crash(fbd))
@@ -207,6 +207,10 @@ static void fbnic_service_task(struct work_struct *work)
{
struct fbnic_dev *fbd = container_of(to_delayed_work(work),
struct fbnic_dev, service_task);
+ struct net_device *netdev = fbd->netdev;
+
+ if (netif_running(netdev))
+ fbnic_phylink_pmd_training_complete_notify(netdev);
rtnl_lock();
@@ -224,7 +228,7 @@ static void fbnic_service_task(struct work_struct *work)
netdev_unlock(fbd->netdev);
}
- if (netif_running(fbd->netdev))
+ if (netif_running(netdev))
schedule_delayed_work(&fbd->service_task, HZ);
rtnl_unlock();
@@ -335,6 +339,9 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
goto init_failure_mode;
}
+ if (fbnic_mdiobus_create(fbd))
+ goto init_failure_mode;
+
netdev = fbnic_netdev_alloc(fbd);
if (!netdev) {
dev_err(&pdev->dev, "Netdev allocation failed\n");
@@ -378,7 +385,7 @@ free_fbd:
* @pdev: PCI device information struct
*
* Called by the PCI subsystem to alert the driver that it should release
- * a PCI device. The could be caused by a Hot-Plug event, or because the
+ * a PCI device. This could be caused by a Hot-Plug event, or because the
* driver is going to be removed from memory.
**/
static void fbnic_remove(struct pci_dev *pdev)
@@ -574,7 +581,6 @@ static pci_ers_result_t fbnic_err_slot_reset(struct pci_dev *pdev)
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_save_state(pdev);
if (pci_enable_device_mem(pdev)) {
dev_err(&pdev->dev,
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c b/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c
index 7ce3fdd25282..09c5225111be 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_phylink.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) Meta Platforms, Inc. and affiliates. */
+#include <linux/pcs/pcs-xpcs.h>
#include <linux/phy.h>
#include <linux/phylink.h>
@@ -101,88 +102,47 @@ int fbnic_phylink_get_fecparam(struct net_device *netdev,
return 0;
}
-static struct fbnic_net *
-fbnic_pcs_to_net(struct phylink_pcs *pcs)
-{
- return container_of(pcs, struct fbnic_net, phylink_pcs);
-}
-
-static void
-fbnic_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
- struct phylink_link_state *state)
+static struct phylink_pcs *
+fbnic_phylink_mac_select_pcs(struct phylink_config *config,
+ phy_interface_t interface)
{
- struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
- struct fbnic_dev *fbd = fbn->fbd;
-
- switch (fbn->aui) {
- case FBNIC_AUI_25GAUI:
- state->speed = SPEED_25000;
- break;
- case FBNIC_AUI_LAUI2:
- case FBNIC_AUI_50GAUI1:
- state->speed = SPEED_50000;
- break;
- case FBNIC_AUI_100GAUI2:
- state->speed = SPEED_100000;
- break;
- default:
- state->link = 0;
- return;
- }
-
- state->duplex = DUPLEX_FULL;
+ struct net_device *netdev = to_net_dev(config->dev);
+ struct fbnic_net *fbn = netdev_priv(netdev);
- state->link = fbd->mac->pcs_get_link(fbd);
+ return fbn->pcs;
}
static int
-fbnic_phylink_pcs_enable(struct phylink_pcs *pcs)
+fbnic_phylink_mac_prepare(struct phylink_config *config, unsigned int mode,
+ phy_interface_t iface)
{
- struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
+ struct net_device *netdev = to_net_dev(config->dev);
+ struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_dev *fbd = fbn->fbd;
- return fbd->mac->pcs_enable(fbd);
+ fbd->mac->prepare(fbd, fbn->aui, fbn->fec);
+
+ return 0;
}
static void
-fbnic_phylink_pcs_disable(struct phylink_pcs *pcs)
+fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode,
+ const struct phylink_link_state *state)
{
- struct fbnic_net *fbn = fbnic_pcs_to_net(pcs);
- struct fbnic_dev *fbd = fbn->fbd;
-
- return fbd->mac->pcs_disable(fbd);
}
static int
-fbnic_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
- phy_interface_t interface,
- const unsigned long *advertising,
- bool permit_pause_to_mac)
-{
- return 0;
-}
-
-static const struct phylink_pcs_ops fbnic_phylink_pcs_ops = {
- .pcs_config = fbnic_phylink_pcs_config,
- .pcs_enable = fbnic_phylink_pcs_enable,
- .pcs_disable = fbnic_phylink_pcs_disable,
- .pcs_get_state = fbnic_phylink_pcs_get_state,
-};
-
-static struct phylink_pcs *
-fbnic_phylink_mac_select_pcs(struct phylink_config *config,
- phy_interface_t interface)
+fbnic_phylink_mac_finish(struct phylink_config *config, unsigned int mode,
+ phy_interface_t iface)
{
struct net_device *netdev = to_net_dev(config->dev);
struct fbnic_net *fbn = netdev_priv(netdev);
+ struct fbnic_dev *fbd = fbn->fbd;
- return &fbn->phylink_pcs;
-}
+ /* Retest the link state and restart interrupts */
+ fbd->mac->get_link(fbd, fbn->aui, fbn->fec);
-static void
-fbnic_phylink_mac_config(struct phylink_config *config, unsigned int mode,
- const struct phylink_link_state *state)
-{
+ return 0;
}
static void
@@ -208,23 +168,48 @@ fbnic_phylink_mac_link_up(struct phylink_config *config,
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_dev *fbd = fbn->fbd;
+ fbn->tx_pause = tx_pause;
+ fbnic_config_drop_mode(fbn, tx_pause);
+
fbd->mac->link_up(fbd, tx_pause, rx_pause);
}
static const struct phylink_mac_ops fbnic_phylink_mac_ops = {
.mac_select_pcs = fbnic_phylink_mac_select_pcs,
+ .mac_prepare = fbnic_phylink_mac_prepare,
.mac_config = fbnic_phylink_mac_config,
+ .mac_finish = fbnic_phylink_mac_finish,
.mac_link_down = fbnic_phylink_mac_link_down,
.mac_link_up = fbnic_phylink_mac_link_up,
};
-int fbnic_phylink_init(struct net_device *netdev)
+/**
+ * fbnic_phylink_create - Phylink device creation
+ * @netdev: Network Device struct to attach phylink device
+ *
+ * Initialize and attach a phylink instance to the device. The phylink
+ * device will make use of the netdev struct to track carrier and will
+ * eventually be used to expose the current state of the MAC and PCS
+ * setup.
+ *
+ * Return: 0 on success, negative on failure
+ **/
+int fbnic_phylink_create(struct net_device *netdev)
{
struct fbnic_net *fbn = netdev_priv(netdev);
struct fbnic_dev *fbd = fbn->fbd;
+ struct phylink_pcs *pcs;
struct phylink *phylink;
+ int err;
+
+ pcs = xpcs_create_pcs_mdiodev(fbd->mdio_bus, 0);
+ if (IS_ERR(pcs)) {
+ err = PTR_ERR(pcs);
+ dev_err(fbd->dev, "Failed to create PCS device: %d\n", err);
+ return err;
+ }
- fbn->phylink_pcs.ops = &fbnic_phylink_pcs_ops;
+ fbn->pcs = pcs;
fbn->phylink_config.dev = &netdev->dev;
fbn->phylink_config.type = PHYLINK_NETDEV;
@@ -247,10 +232,80 @@ int fbnic_phylink_init(struct net_device *netdev)
phylink = phylink_create(&fbn->phylink_config, NULL,
fbnic_phylink_select_interface(fbn->aui),
&fbnic_phylink_mac_ops);
- if (IS_ERR(phylink))
- return PTR_ERR(phylink);
+ if (IS_ERR(phylink)) {
+ err = PTR_ERR(phylink);
+ dev_err(netdev->dev.parent,
+ "Failed to create Phylink interface, err: %d\n", err);
+ xpcs_destroy_pcs(pcs);
+ return err;
+ }
fbn->phylink = phylink;
return 0;
}
+
+/**
+ * fbnic_phylink_destroy - Teardown phylink related interfaces
+ * @netdev: Network Device struct containing phylink device
+ *
+ * Detach and free resources related to phylink interface.
+ **/
+void fbnic_phylink_destroy(struct net_device *netdev)
+{
+ struct fbnic_net *fbn = netdev_priv(netdev);
+
+ if (fbn->phylink)
+ phylink_destroy(fbn->phylink);
+ if (fbn->pcs)
+ xpcs_destroy_pcs(fbn->pcs);
+}
+
+/**
+ * fbnic_phylink_pmd_training_complete_notify - PMD training complete notifier
+ * @netdev: Netdev struct phylink device attached to
+ *
+ * When the link first comes up the PMD will have a period of 2 to 3 seconds
+ * where the link will flutter due to link training. To avoid spamming the
+ * kernel log with messages about this we add a delay of 4 seconds from the
+ * time of the last PCS report of link so that we can guarantee we are unlikely
+ * to see any further link loss events due to link training.
+ **/
+void fbnic_phylink_pmd_training_complete_notify(struct net_device *netdev)
+{
+ struct fbnic_net *fbn = netdev_priv(netdev);
+ struct fbnic_dev *fbd = fbn->fbd;
+
+ if (fbd->pmd_state != FBNIC_PMD_TRAINING)
+ return;
+
+ /* Prevent reading end_of_pmd_training until we verified state */
+ smp_rmb();
+
+ if (!time_before(READ_ONCE(fbd->end_of_pmd_training), jiffies))
+ return;
+
+ /* At this point we have verified that the link has been up for
+ * the full training duration. As a first step we will try
+ * transitioning to link ready.
+ */
+ if (cmpxchg(&fbd->pmd_state, FBNIC_PMD_TRAINING,
+ FBNIC_PMD_LINK_READY) != FBNIC_PMD_TRAINING)
+ return;
+
+ /* Perform a follow-up check to verify that the link didn't flap
+ * just before our transition by rechecking the training timer.
+ */
+ if (!time_before(READ_ONCE(fbd->end_of_pmd_training), jiffies))
+ return;
+
+ /* The training timeout has been completed. We are good to swap out
+ * link_ready for send_data assuming no other events have occurred
+ * that would have pulled us back into initialization or training.
+ */
+ if (cmpxchg(&fbd->pmd_state, FBNIC_PMD_LINK_READY,
+ FBNIC_PMD_SEND_DATA) != FBNIC_PMD_LINK_READY)
+ return;
+
+ phylink_pcs_change(fbn->pcs, false);
+}
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_time.c b/drivers/net/ethernet/meta/fbnic/fbnic_time.c
index 39d99677b71e..db7748189f45 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_time.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_time.c
@@ -253,7 +253,7 @@ static void fbnic_ptp_reset(struct fbnic_dev *fbd)
void fbnic_time_init(struct fbnic_net *fbn)
{
- /* This is not really a statistic, but the lockng primitive fits
+ /* This is not really a statistic, but the locking primitive fits
* our usecase perfectly, we need an atomic 8 bytes READ_ONCE() /
* WRITE_ONCE() behavior.
*/
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h
index c34bf87eeec9..3508b46ebdd0 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_tlv.h
@@ -80,7 +80,7 @@ struct fbnic_tlv_index {
enum fbnic_tlv_type type;
};
-#define TLV_MAX_DATA (PAGE_SIZE - 512)
+#define TLV_MAX_DATA ((PAGE_SIZE - 512) & 0xFFFF)
#define FBNIC_TLV_ATTR_ID_UNKNOWN USHRT_MAX
#define FBNIC_TLV_ATTR_STRING(id, len) { id, len, FBNIC_TLV_STRING }
#define FBNIC_TLV_ATTR_FLAG(id) { id, 0, FBNIC_TLV_FLAG }
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
index b1e8ce89870f..13d508ce637f 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c
@@ -653,7 +653,8 @@ static void fbnic_clean_twq1(struct fbnic_napi_vector *nv, bool pp_allow_direct,
FBNIC_TWD_TYPE_AL;
total_bytes += FIELD_GET(FBNIC_TWD_LEN_MASK, twd);
- page_pool_put_page(page->pp, page, -1, pp_allow_direct);
+ page_pool_put_page(pp_page_to_nmdesc(page)->pp, page, -1,
+ pp_allow_direct);
next_desc:
head++;
head &= ring->size_mask;
@@ -887,6 +888,7 @@ static void fbnic_bd_prep(struct fbnic_ring *bdq, u16 id, netmem_ref netmem)
*bdq_desc = cpu_to_le64(bd);
bd += FIELD_PREP(FBNIC_BD_DESC_ADDR_MASK, 1) |
FIELD_PREP(FBNIC_BD_DESC_ID_MASK, 1);
+ bdq_desc++;
} while (--i);
}
@@ -1806,7 +1808,7 @@ int fbnic_alloc_napi_vectors(struct fbnic_net *fbn)
free_vectors:
fbnic_free_napi_vectors(fbn);
- return -ENOMEM;
+ return err;
}
static void fbnic_free_ring_resources(struct device *dev,
@@ -2573,11 +2575,15 @@ write_ctl:
}
static void fbnic_config_drop_mode_rcq(struct fbnic_napi_vector *nv,
- struct fbnic_ring *rcq)
+ struct fbnic_ring *rcq, bool tx_pause)
{
+ struct fbnic_net *fbn = netdev_priv(nv->napi.dev);
u32 drop_mode, rcq_ctl;
- drop_mode = FBNIC_QUEUE_RDE_CTL0_DROP_IMMEDIATE;
+ if (!tx_pause && fbn->num_rx_queues > 1)
+ drop_mode = FBNIC_QUEUE_RDE_CTL0_DROP_IMMEDIATE;
+ else
+ drop_mode = FBNIC_QUEUE_RDE_CTL0_DROP_NEVER;
/* Specify packet layout */
rcq_ctl = FIELD_PREP(FBNIC_QUEUE_RDE_CTL0_DROP_MODE_MASK, drop_mode) |
@@ -2587,6 +2593,21 @@ static void fbnic_config_drop_mode_rcq(struct fbnic_napi_vector *nv,
fbnic_ring_wr32(rcq, FBNIC_QUEUE_RDE_CTL0, rcq_ctl);
}
+void fbnic_config_drop_mode(struct fbnic_net *fbn, bool tx_pause)
+{
+ int i, t;
+
+ for (i = 0; i < fbn->num_napi; i++) {
+ struct fbnic_napi_vector *nv = fbn->napi[i];
+
+ for (t = 0; t < nv->rxt_count; t++) {
+ struct fbnic_q_triad *qt = &nv->qt[nv->txt_count + t];
+
+ fbnic_config_drop_mode_rcq(nv, &qt->cmpl, tx_pause);
+ }
+ }
+}
+
static void fbnic_config_rim_threshold(struct fbnic_ring *rcq, u16 nv_idx, u32 rx_desc)
{
u32 threshold;
@@ -2636,7 +2657,7 @@ static void fbnic_enable_rcq(struct fbnic_napi_vector *nv,
u32 hds_thresh = fbn->hds_thresh;
u32 rcq_ctl = 0;
- fbnic_config_drop_mode_rcq(nv, rcq);
+ fbnic_config_drop_mode_rcq(nv, rcq, fbn->tx_pause);
/* Force lower bound on MAX_HEADER_BYTES. Below this, all frames should
* be split at L4. It would also result in the frames being split at
@@ -2699,7 +2720,6 @@ static void __fbnic_nv_enable(struct fbnic_napi_vector *nv)
&nv->napi);
fbnic_enable_bdq(&qt->sub0, &qt->sub1);
- fbnic_config_drop_mode_rcq(nv, &qt->cmpl);
fbnic_enable_rcq(nv, &qt->cmpl);
}
}
diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h
index ca37da5a0b17..27776e844e29 100644
--- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h
+++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.h
@@ -184,6 +184,7 @@ void fbnic_reset_netif_queues(struct fbnic_net *fbn);
irqreturn_t fbnic_msix_clean_rings(int irq, void *data);
void fbnic_napi_enable(struct fbnic_net *fbn);
void fbnic_napi_disable(struct fbnic_net *fbn);
+void fbnic_config_drop_mode(struct fbnic_net *fbn, bool tx_pause);
void fbnic_enable(struct fbnic_net *fbn);
void fbnic_disable(struct fbnic_net *fbn);
void fbnic_flush(struct fbnic_net *fbn);
diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c
index 9d70b51ca91d..e4c542fc6c2b 100644
--- a/drivers/net/ethernet/microchip/lan743x_main.c
+++ b/drivers/net/ethernet/microchip/lan743x_main.c
@@ -3915,7 +3915,6 @@ static int lan743x_pm_resume(struct device *dev)
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_save_state(pdev);
/* Restore HW_CFG that was saved during pm suspend */
if (adapter->is_pci11x1x)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c
index 2474dfd330f4..fe4e61405284 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ethtool.c
@@ -294,7 +294,7 @@ static void lan966x_stats_update(struct lan966x *lan966x)
{
int i, j;
- mutex_lock(&lan966x->stats_lock);
+ spin_lock(&lan966x->stats_lock);
for (i = 0; i < lan966x->num_phys_ports; i++) {
uint idx = i * lan966x->num_stats;
@@ -310,7 +310,7 @@ static void lan966x_stats_update(struct lan966x *lan966x)
}
}
- mutex_unlock(&lan966x->stats_lock);
+ spin_unlock(&lan966x->stats_lock);
}
static int lan966x_get_sset_count(struct net_device *dev, int sset)
@@ -365,7 +365,7 @@ static void lan966x_get_eth_mac_stats(struct net_device *dev,
idx = port->chip_port * lan966x->num_stats;
- mutex_lock(&lan966x->stats_lock);
+ spin_lock(&lan966x->stats_lock);
mac_stats->FramesTransmittedOK =
lan966x->stats[idx + SYS_COUNT_TX_UC] +
@@ -416,7 +416,7 @@ static void lan966x_get_eth_mac_stats(struct net_device *dev,
lan966x->stats[idx + SYS_COUNT_RX_LONG] +
lan966x->stats[idx + SYS_COUNT_RX_PMAC_LONG];
- mutex_unlock(&lan966x->stats_lock);
+ spin_unlock(&lan966x->stats_lock);
}
static const struct ethtool_rmon_hist_range lan966x_rmon_ranges[] = {
@@ -442,7 +442,7 @@ static void lan966x_get_eth_rmon_stats(struct net_device *dev,
idx = port->chip_port * lan966x->num_stats;
- mutex_lock(&lan966x->stats_lock);
+ spin_lock(&lan966x->stats_lock);
rmon_stats->undersize_pkts =
lan966x->stats[idx + SYS_COUNT_RX_SHORT] +
@@ -500,7 +500,7 @@ static void lan966x_get_eth_rmon_stats(struct net_device *dev,
lan966x->stats[idx + SYS_COUNT_TX_SZ_1024_1526] +
lan966x->stats[idx + SYS_COUNT_TX_PMAC_SZ_1024_1526];
- mutex_unlock(&lan966x->stats_lock);
+ spin_unlock(&lan966x->stats_lock);
*ranges = lan966x_rmon_ranges;
}
@@ -603,7 +603,7 @@ void lan966x_stats_get(struct net_device *dev,
idx = port->chip_port * lan966x->num_stats;
- mutex_lock(&lan966x->stats_lock);
+ spin_lock(&lan966x->stats_lock);
stats->rx_bytes = lan966x->stats[idx + SYS_COUNT_RX_OCT] +
lan966x->stats[idx + SYS_COUNT_RX_PMAC_OCT];
@@ -685,7 +685,7 @@ void lan966x_stats_get(struct net_device *dev,
stats->collisions = lan966x->stats[idx + SYS_COUNT_TX_COL];
- mutex_unlock(&lan966x->stats_lock);
+ spin_unlock(&lan966x->stats_lock);
}
int lan966x_stats_init(struct lan966x *lan966x)
@@ -701,7 +701,7 @@ int lan966x_stats_init(struct lan966x *lan966x)
return -ENOMEM;
/* Init stats worker */
- mutex_init(&lan966x->stats_lock);
+ spin_lock_init(&lan966x->stats_lock);
snprintf(queue_name, sizeof(queue_name), "%s-stats",
dev_name(lan966x->dev));
lan966x->stats_queue = create_singlethread_workqueue(queue_name);
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index 7001584f1b7a..47752d3fde0b 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -1261,7 +1261,6 @@ cleanup_ports:
cancel_delayed_work_sync(&lan966x->stats_work);
destroy_workqueue(lan966x->stats_queue);
- mutex_destroy(&lan966x->stats_lock);
debugfs_remove_recursive(lan966x->debugfs_root);
@@ -1279,7 +1278,6 @@ static void lan966x_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&lan966x->stats_work);
destroy_workqueue(lan966x->stats_queue);
- mutex_destroy(&lan966x->stats_lock);
lan966x_mac_purge_entries(lan966x);
lan966x_mdb_deinit(lan966x);
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
index 4f75f0688369..eea286c29474 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h
@@ -295,8 +295,8 @@ struct lan966x {
const struct lan966x_stat_layout *stats_layout;
u32 num_stats;
- /* workqueue for reading stats */
- struct mutex stats_lock;
+ /* lock for reading stats */
+ spinlock_t stats_lock;
u64 *stats;
struct delayed_work stats_work;
struct workqueue_struct *stats_queue;
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
index b4377b8613c3..8c40db90ee8f 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
@@ -1,11 +1,14 @@
// SPDX-License-Identifier: GPL-2.0+
#include <linux/ptp_classify.h>
+#include <linux/units.h>
#include "lan966x_main.h"
#include "vcap_api.h"
#include "vcap_api_client.h"
+#define LAN9X66_CLOCK_RATE 165617754
+
#define LAN966X_MAX_PTP_ID 512
/* Represents 1ppm adjustment in 2^59 format with 6.037735849ns as reference
@@ -1126,5 +1129,5 @@ void lan966x_ptp_rxtstamp(struct lan966x *lan966x, struct sk_buff *skb,
u32 lan966x_ptp_get_period_ps(void)
{
/* This represents the system clock period in picoseconds */
- return 15125;
+ return PICO / LAN9X66_CLOCK_RATE;
}
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
index a1471e38d118..2a37fc1ba4bc 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
@@ -403,11 +403,11 @@ static void lan966x_es0_read_esdx_counter(struct lan966x *lan966x,
u32 counter;
id = id & 0xff; /* counter limit */
- mutex_lock(&lan966x->stats_lock);
+ spin_lock(&lan966x->stats_lock);
lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(id), lan966x, SYS_STAT_CFG);
counter = lan_rd(lan966x, SYS_CNT(LAN966X_STAT_ESDX_GRN_PKTS)) +
lan_rd(lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_PKTS));
- mutex_unlock(&lan966x->stats_lock);
+ spin_unlock(&lan966x->stats_lock);
if (counter)
admin->cache.counter = counter;
}
@@ -417,14 +417,14 @@ static void lan966x_es0_write_esdx_counter(struct lan966x *lan966x,
{
id = id & 0xff; /* counter limit */
- mutex_lock(&lan966x->stats_lock);
+ spin_lock(&lan966x->stats_lock);
lan_wr(SYS_STAT_CFG_STAT_VIEW_SET(id), lan966x, SYS_STAT_CFG);
lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_GRN_BYTES));
lan_wr(admin->cache.counter, lan966x,
SYS_CNT(LAN966X_STAT_ESDX_GRN_PKTS));
lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_BYTES));
lan_wr(0, lan966x, SYS_CNT(LAN966X_STAT_ESDX_YEL_PKTS));
- mutex_unlock(&lan966x->stats_lock);
+ spin_unlock(&lan966x->stats_lock);
}
static void lan966x_vcap_cache_write(struct net_device *dev,
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index 40b1bfc600a7..582145713cfd 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -395,6 +395,8 @@ static int sparx5_create_port(struct sparx5 *sparx5,
spx5_port->phylink = phylink;
+ spx5_port->ndev->dev.of_node = spx5_port->of_node;
+
return 0;
}
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index 43f034e180c4..efb4e412ec7e 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -15,6 +15,20 @@
struct dentry *mana_debugfs_root;
+struct mana_dev_recovery {
+ struct list_head list;
+ struct pci_dev *pdev;
+ enum gdma_eqe_type type;
+};
+
+static struct mana_dev_recovery_work {
+ struct list_head dev_list;
+ struct delayed_work work;
+
+ /* Lock for dev_list above */
+ spinlock_t lock;
+} mana_dev_recovery_work;
+
static u32 mana_gd_r32(struct gdma_context *g, u64 offset)
{
return readl(g->bar0_va + offset);
@@ -387,6 +401,25 @@ EXPORT_SYMBOL_NS(mana_gd_ring_cq, "NET_MANA");
#define MANA_SERVICE_PERIOD 10
+static void mana_serv_rescan(struct pci_dev *pdev)
+{
+ struct pci_bus *parent;
+
+ pci_lock_rescan_remove();
+
+ parent = pdev->bus;
+ if (!parent) {
+ dev_err(&pdev->dev, "MANA service: no parent bus\n");
+ goto out;
+ }
+
+ pci_stop_and_remove_bus_device(pdev);
+ pci_rescan_bus(parent);
+
+out:
+ pci_unlock_rescan_remove();
+}
+
static void mana_serv_fpga(struct pci_dev *pdev)
{
struct pci_bus *bus, *parent;
@@ -419,9 +452,12 @@ static void mana_serv_reset(struct pci_dev *pdev)
{
struct gdma_context *gc = pci_get_drvdata(pdev);
struct hw_channel_context *hwc;
+ int ret;
if (!gc) {
- dev_err(&pdev->dev, "MANA service: no GC\n");
+ /* Perform PCI rescan on device if GC is not set up */
+ dev_err(&pdev->dev, "MANA service: GC not setup, rescanning\n");
+ mana_serv_rescan(pdev);
return;
}
@@ -440,9 +476,18 @@ static void mana_serv_reset(struct pci_dev *pdev)
msleep(MANA_SERVICE_PERIOD * 1000);
- mana_gd_resume(pdev);
+ ret = mana_gd_resume(pdev);
+ if (ret == -ETIMEDOUT || ret == -EPROTO) {
+ /* Perform PCI rescan on device if we failed on HWC */
+ dev_err(&pdev->dev, "MANA service: resume failed, rescanning\n");
+ mana_serv_rescan(pdev);
+ goto out;
+ }
- dev_info(&pdev->dev, "MANA reset cycle completed\n");
+ if (ret)
+ dev_info(&pdev->dev, "MANA reset cycle failed err %d\n", ret);
+ else
+ dev_info(&pdev->dev, "MANA reset cycle completed\n");
out:
gc->in_service = false;
@@ -454,18 +499,9 @@ struct mana_serv_work {
enum gdma_eqe_type type;
};
-static void mana_serv_func(struct work_struct *w)
+static void mana_do_service(enum gdma_eqe_type type, struct pci_dev *pdev)
{
- struct mana_serv_work *mns_wk;
- struct pci_dev *pdev;
-
- mns_wk = container_of(w, struct mana_serv_work, serv_work);
- pdev = mns_wk->pdev;
-
- if (!pdev)
- goto out;
-
- switch (mns_wk->type) {
+ switch (type) {
case GDMA_EQE_HWC_FPGA_RECONFIG:
mana_serv_fpga(pdev);
break;
@@ -475,12 +511,48 @@ static void mana_serv_func(struct work_struct *w)
break;
default:
- dev_err(&pdev->dev, "MANA service: unknown type %d\n",
- mns_wk->type);
+ dev_err(&pdev->dev, "MANA service: unknown type %d\n", type);
break;
}
+}
+
+static void mana_recovery_delayed_func(struct work_struct *w)
+{
+ struct mana_dev_recovery_work *work;
+ struct mana_dev_recovery *dev;
+ unsigned long flags;
+
+ work = container_of(w, struct mana_dev_recovery_work, work.work);
+
+ spin_lock_irqsave(&work->lock, flags);
+
+ while (!list_empty(&work->dev_list)) {
+ dev = list_first_entry(&work->dev_list,
+ struct mana_dev_recovery, list);
+ list_del(&dev->list);
+ spin_unlock_irqrestore(&work->lock, flags);
+
+ mana_do_service(dev->type, dev->pdev);
+ pci_dev_put(dev->pdev);
+ kfree(dev);
+
+ spin_lock_irqsave(&work->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&work->lock, flags);
+}
+
+static void mana_serv_func(struct work_struct *w)
+{
+ struct mana_serv_work *mns_wk;
+ struct pci_dev *pdev;
+
+ mns_wk = container_of(w, struct mana_serv_work, serv_work);
+ pdev = mns_wk->pdev;
+
+ if (pdev)
+ mana_do_service(mns_wk->type, pdev);
-out:
pci_dev_put(pdev);
kfree(mns_wk);
module_put(THIS_MODULE);
@@ -528,6 +600,7 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)
case GDMA_EQE_HWC_INIT_DONE:
case GDMA_EQE_HWC_SOC_SERVICE:
case GDMA_EQE_RNIC_QP_FATAL:
+ case GDMA_EQE_HWC_SOC_RECONFIG_DATA:
if (!eq->eq.callback)
break;
@@ -540,6 +613,17 @@ static void mana_gd_process_eqe(struct gdma_queue *eq)
case GDMA_EQE_HWC_RESET_REQUEST:
dev_info(gc->dev, "Recv MANA service type:%d\n", type);
+ if (!test_and_set_bit(GC_PROBE_SUCCEEDED, &gc->flags)) {
+ /*
+ * Device is in probe and we received a hardware reset
+ * event, the probe function will detect that the flag
+ * has changed and perform service procedure.
+ */
+ dev_info(gc->dev,
+ "Service is to be processed in probe\n");
+ break;
+ }
+
if (gc->in_service) {
dev_info(gc->dev, "Already in service\n");
break;
@@ -1299,7 +1383,6 @@ int mana_gd_post_work_request(struct gdma_queue *wq,
struct gdma_posted_wqe_info *wqe_info)
{
u32 client_oob_size = wqe_req->inline_oob_size;
- struct gdma_context *gc;
u32 sgl_data_size;
u32 max_wqe_size;
u32 wqe_size;
@@ -1329,11 +1412,8 @@ int mana_gd_post_work_request(struct gdma_queue *wq,
if (wqe_size > max_wqe_size)
return -EINVAL;
- if (wq->monitor_avl_buf && wqe_size > mana_gd_wq_avail_space(wq)) {
- gc = wq->gdma_dev->gdma_context;
- dev_err(gc->dev, "unsuccessful flow control!\n");
+ if (wq->monitor_avl_buf && wqe_size > mana_gd_wq_avail_space(wq))
return -ENOSPC;
- }
if (wqe_info)
wqe_info->wqe_size_in_bu = wqe_size / GDMA_WQE_BU_SIZE;
@@ -1941,8 +2021,19 @@ static int mana_gd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err)
goto cleanup_mana;
+ /*
+ * If a hardware reset event has occurred over HWC during probe,
+ * rollback and perform hardware reset procedure.
+ */
+ if (test_and_set_bit(GC_PROBE_SUCCEEDED, &gc->flags)) {
+ err = -EPROTO;
+ goto cleanup_mana_rdma;
+ }
+
return 0;
+cleanup_mana_rdma:
+ mana_rdma_remove(&gc->mana_ib);
cleanup_mana:
mana_remove(&gc->mana, false);
cleanup_gd:
@@ -1966,6 +2057,35 @@ release_region:
disable_dev:
pci_disable_device(pdev);
dev_err(&pdev->dev, "gdma probe failed: err = %d\n", err);
+
+ /*
+ * Hardware could be in recovery mode and the HWC returns TIMEDOUT or
+ * EPROTO from mana_gd_setup(), mana_probe() or mana_rdma_probe(), or
+ * we received a hardware reset event over HWC interrupt. In this case,
+ * perform the device recovery procedure after MANA_SERVICE_PERIOD
+ * seconds.
+ */
+ if (err == -ETIMEDOUT || err == -EPROTO) {
+ struct mana_dev_recovery *dev;
+ unsigned long flags;
+
+ dev_info(&pdev->dev, "Start MANA recovery mode\n");
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return err;
+
+ dev->pdev = pci_dev_get(pdev);
+ dev->type = GDMA_EQE_HWC_RESET_REQUEST;
+
+ spin_lock_irqsave(&mana_dev_recovery_work.lock, flags);
+ list_add_tail(&dev->list, &mana_dev_recovery_work.dev_list);
+ spin_unlock_irqrestore(&mana_dev_recovery_work.lock, flags);
+
+ schedule_delayed_work(&mana_dev_recovery_work.work,
+ secs_to_jiffies(MANA_SERVICE_PERIOD));
+ }
+
return err;
}
@@ -2070,6 +2190,10 @@ static int __init mana_driver_init(void)
{
int err;
+ INIT_LIST_HEAD(&mana_dev_recovery_work.dev_list);
+ spin_lock_init(&mana_dev_recovery_work.lock);
+ INIT_DELAYED_WORK(&mana_dev_recovery_work.work, mana_recovery_delayed_func);
+
mana_debugfs_root = debugfs_create_dir("mana", NULL);
err = pci_register_driver(&mana_driver);
@@ -2083,6 +2207,21 @@ static int __init mana_driver_init(void)
static void __exit mana_driver_exit(void)
{
+ struct mana_dev_recovery *dev;
+ unsigned long flags;
+
+ disable_delayed_work_sync(&mana_dev_recovery_work.work);
+
+ spin_lock_irqsave(&mana_dev_recovery_work.lock, flags);
+ while (!list_empty(&mana_dev_recovery_work.dev_list)) {
+ dev = list_first_entry(&mana_dev_recovery_work.dev_list,
+ struct mana_dev_recovery, list);
+ list_del(&dev->list);
+ pci_dev_put(dev->pdev);
+ kfree(dev);
+ }
+ spin_unlock_irqrestore(&mana_dev_recovery_work.lock, flags);
+
pci_unregister_driver(&mana_driver);
debugfs_remove(mana_debugfs_root);
diff --git a/drivers/net/ethernet/microsoft/mana/hw_channel.c b/drivers/net/ethernet/microsoft/mana/hw_channel.c
index ada6c78a2bef..aa4e2731e2ba 100644
--- a/drivers/net/ethernet/microsoft/mana/hw_channel.c
+++ b/drivers/net/ethernet/microsoft/mana/hw_channel.c
@@ -118,6 +118,7 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
struct gdma_dev *gd = hwc->gdma_dev;
union hwc_init_type_data type_data;
union hwc_init_eq_id_db eq_db;
+ struct mana_context *ac;
u32 type, val;
int ret;
@@ -196,6 +197,17 @@ static void mana_hwc_init_event_handler(void *ctx, struct gdma_queue *q_self,
hwc->hwc_timeout = val;
break;
+ case HWC_DATA_HW_LINK_CONNECT:
+ case HWC_DATA_HW_LINK_DISCONNECT:
+ ac = gd->gdma_context->mana.driver_data;
+ if (!ac)
+ break;
+
+ WRITE_ONCE(ac->link_event, type);
+ schedule_work(&ac->link_change_work);
+
+ break;
+
default:
dev_warn(hwc->dev, "Received unknown reconfig type %u\n", type);
break;
diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c
index 0142fd98392c..1ad154f9db1a 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_en.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_en.c
@@ -11,6 +11,7 @@
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/export.h>
+#include <linux/skbuff.h>
#include <net/checksum.h>
#include <net/ip6_checksum.h>
@@ -20,6 +21,7 @@
#include <net/mana/mana.h>
#include <net/mana/mana_auxiliary.h>
+#include <net/mana/hw_channel.h>
static DEFINE_IDA(mana_adev_ida);
@@ -84,7 +86,6 @@ static int mana_open(struct net_device *ndev)
/* Ensure port state updated before txq state */
smp_wmb();
- netif_carrier_on(ndev);
netif_tx_wake_all_queues(ndev);
netdev_dbg(ndev, "%s successful\n", __func__);
return 0;
@@ -100,6 +101,46 @@ static int mana_close(struct net_device *ndev)
return mana_detach(ndev, true);
}
+static void mana_link_state_handle(struct work_struct *w)
+{
+ struct mana_context *ac;
+ struct net_device *ndev;
+ u32 link_event;
+ bool link_up;
+ int i;
+
+ ac = container_of(w, struct mana_context, link_change_work);
+
+ rtnl_lock();
+
+ link_event = READ_ONCE(ac->link_event);
+
+ if (link_event == HWC_DATA_HW_LINK_CONNECT)
+ link_up = true;
+ else if (link_event == HWC_DATA_HW_LINK_DISCONNECT)
+ link_up = false;
+ else
+ goto out;
+
+ /* Process all ports */
+ for (i = 0; i < ac->num_ports; i++) {
+ ndev = ac->ports[i];
+ if (!ndev)
+ continue;
+
+ if (link_up) {
+ netif_carrier_on(ndev);
+
+ __netdev_notify_peers(ndev);
+ } else {
+ netif_carrier_off(ndev);
+ }
+ }
+
+out:
+ rtnl_unlock();
+}
+
static bool mana_can_tx(struct gdma_queue *wq)
{
return mana_gd_wq_avail_space(wq) >= MAX_TX_WQE_SIZE;
@@ -289,6 +330,21 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
cq = &apc->tx_qp[txq_idx].tx_cq;
tx_stats = &txq->stats;
+ BUILD_BUG_ON(MAX_TX_WQE_SGL_ENTRIES != MANA_MAX_TX_WQE_SGL_ENTRIES);
+ if (MAX_SKB_FRAGS + 2 > MAX_TX_WQE_SGL_ENTRIES &&
+ skb_shinfo(skb)->nr_frags + 2 > MAX_TX_WQE_SGL_ENTRIES) {
+ /* GSO skb with Hardware SGE limit exceeded is not expected here
+ * as they are handled in mana_features_check() callback
+ */
+ if (skb_linearize(skb)) {
+ netdev_warn_once(ndev, "Failed to linearize skb with nr_frags=%d and is_gso=%d\n",
+ skb_shinfo(skb)->nr_frags,
+ skb_is_gso(skb));
+ goto tx_drop_count;
+ }
+ apc->eth_stats.tx_linear_pkt_cnt++;
+ }
+
pkg.tx_oob.s_oob.vcq_num = cq->gdma_id;
pkg.tx_oob.s_oob.vsq_frame = txq->vsq_frame;
@@ -402,8 +458,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
}
}
- WARN_ON_ONCE(pkg.wqe_req.num_sge > MAX_TX_WQE_SGL_ENTRIES);
-
if (pkg.wqe_req.num_sge <= ARRAY_SIZE(pkg.sgl_array)) {
pkg.wqe_req.sgl = pkg.sgl_array;
} else {
@@ -438,9 +492,9 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
if (err) {
(void)skb_dequeue_tail(&txq->pending_skbs);
+ mana_unmap_skb(skb, apc);
netdev_warn(ndev, "Failed to post TX OOB: %d\n", err);
- err = NETDEV_TX_BUSY;
- goto tx_busy;
+ goto free_sgl_ptr;
}
err = NETDEV_TX_OK;
@@ -460,7 +514,6 @@ netdev_tx_t mana_start_xmit(struct sk_buff *skb, struct net_device *ndev)
tx_stats->bytes += len + ((num_gso_seg - 1) * gso_hs);
u64_stats_update_end(&tx_stats->syncp);
-tx_busy:
if (netif_tx_queue_stopped(net_txq) && mana_can_tx(gdma_sq)) {
netif_tx_wake_queue(net_txq);
apc->eth_stats.wake_queue++;
@@ -478,6 +531,25 @@ tx_drop:
return NETDEV_TX_OK;
}
+#if (MAX_SKB_FRAGS + 2 > MANA_MAX_TX_WQE_SGL_ENTRIES)
+static netdev_features_t mana_features_check(struct sk_buff *skb,
+ struct net_device *ndev,
+ netdev_features_t features)
+{
+ if (skb_shinfo(skb)->nr_frags + 2 > MAX_TX_WQE_SGL_ENTRIES) {
+ /* Exceeds HW SGE limit.
+ * GSO case:
+ * Disable GSO so the stack will software-segment the skb
+ * into smaller skbs that fit the SGE budget.
+ * Non-GSO case:
+ * The xmit path will attempt skb_linearize() as a fallback.
+ */
+ features &= ~NETIF_F_GSO_MASK;
+ }
+ return features;
+}
+#endif
+
static void mana_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *st)
{
@@ -494,6 +566,11 @@ static void mana_get_stats64(struct net_device *ndev,
netdev_stats_to_stats64(st, &ndev->stats);
+ if (apc->ac->hwc_timeout_occurred)
+ netdev_warn_once(ndev, "HWC timeout occurred\n");
+
+ st->rx_missed_errors = apc->ac->hc_stats.hc_rx_discards_no_wqe;
+
for (q = 0; q < num_queues; q++) {
rx_stats = &apc->rxqs[q]->stats;
@@ -814,7 +891,7 @@ static int mana_shaper_del(struct net_shaper_binding *binding,
/* Reset mana port context parameters */
apc->handle.id = 0;
apc->handle.scope = NET_SHAPER_SCOPE_UNSPEC;
- apc->speed = 0;
+ apc->speed = apc->max_speed;
}
return err;
@@ -838,6 +915,9 @@ static const struct net_device_ops mana_devops = {
.ndo_open = mana_open,
.ndo_stop = mana_close,
.ndo_select_queue = mana_select_queue,
+#if (MAX_SKB_FRAGS + 2 > MANA_MAX_TX_WQE_SGL_ENTRIES)
+ .ndo_features_check = mana_features_check,
+#endif
.ndo_start_xmit = mana_start_xmit,
.ndo_validate_addr = eth_validate_addr,
.ndo_get_stats64 = mana_get_stats64,
@@ -1606,7 +1686,7 @@ static int mana_move_wq_tail(struct gdma_queue *wq, u32 num_units)
return 0;
}
-static void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc)
+void mana_unmap_skb(struct sk_buff *skb, struct mana_port_context *apc)
{
struct mana_skb_head *ash = (struct mana_skb_head *)skb->head;
struct gdma_context *gc = apc->ac->gdma_dev->gdma_context;
@@ -2769,11 +2849,12 @@ int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx,
return 0;
}
-void mana_query_gf_stats(struct mana_port_context *apc)
+int mana_query_gf_stats(struct mana_context *ac)
{
+ struct gdma_context *gc = ac->gdma_dev->gdma_context;
struct mana_query_gf_stat_resp resp = {};
struct mana_query_gf_stat_req req = {};
- struct net_device *ndev = apc->ndev;
+ struct device *dev = gc->dev;
int err;
mana_gd_init_req_hdr(&req.hdr, MANA_QUERY_GF_STAT,
@@ -2807,52 +2888,54 @@ void mana_query_gf_stats(struct mana_port_context *apc)
STATISTICS_FLAGS_HC_TX_BCAST_BYTES |
STATISTICS_FLAGS_TX_ERRORS_GDMA_ERROR;
- err = mana_send_request(apc->ac, &req, sizeof(req), &resp,
+ err = mana_send_request(ac, &req, sizeof(req), &resp,
sizeof(resp));
if (err) {
- netdev_err(ndev, "Failed to query GF stats: %d\n", err);
- return;
+ dev_err(dev, "Failed to query GF stats: %d\n", err);
+ return err;
}
err = mana_verify_resp_hdr(&resp.hdr, MANA_QUERY_GF_STAT,
sizeof(resp));
if (err || resp.hdr.status) {
- netdev_err(ndev, "Failed to query GF stats: %d, 0x%x\n", err,
- resp.hdr.status);
- return;
+ dev_err(dev, "Failed to query GF stats: %d, 0x%x\n", err,
+ resp.hdr.status);
+ return err;
}
- apc->eth_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe;
- apc->eth_stats.hc_rx_err_vport_disabled = resp.rx_err_vport_disabled;
- apc->eth_stats.hc_rx_bytes = resp.hc_rx_bytes;
- apc->eth_stats.hc_rx_ucast_pkts = resp.hc_rx_ucast_pkts;
- apc->eth_stats.hc_rx_ucast_bytes = resp.hc_rx_ucast_bytes;
- apc->eth_stats.hc_rx_bcast_pkts = resp.hc_rx_bcast_pkts;
- apc->eth_stats.hc_rx_bcast_bytes = resp.hc_rx_bcast_bytes;
- apc->eth_stats.hc_rx_mcast_pkts = resp.hc_rx_mcast_pkts;
- apc->eth_stats.hc_rx_mcast_bytes = resp.hc_rx_mcast_bytes;
- apc->eth_stats.hc_tx_err_gf_disabled = resp.tx_err_gf_disabled;
- apc->eth_stats.hc_tx_err_vport_disabled = resp.tx_err_vport_disabled;
- apc->eth_stats.hc_tx_err_inval_vportoffset_pkt =
+ ac->hc_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe;
+ ac->hc_stats.hc_rx_err_vport_disabled = resp.rx_err_vport_disabled;
+ ac->hc_stats.hc_rx_bytes = resp.hc_rx_bytes;
+ ac->hc_stats.hc_rx_ucast_pkts = resp.hc_rx_ucast_pkts;
+ ac->hc_stats.hc_rx_ucast_bytes = resp.hc_rx_ucast_bytes;
+ ac->hc_stats.hc_rx_bcast_pkts = resp.hc_rx_bcast_pkts;
+ ac->hc_stats.hc_rx_bcast_bytes = resp.hc_rx_bcast_bytes;
+ ac->hc_stats.hc_rx_mcast_pkts = resp.hc_rx_mcast_pkts;
+ ac->hc_stats.hc_rx_mcast_bytes = resp.hc_rx_mcast_bytes;
+ ac->hc_stats.hc_tx_err_gf_disabled = resp.tx_err_gf_disabled;
+ ac->hc_stats.hc_tx_err_vport_disabled = resp.tx_err_vport_disabled;
+ ac->hc_stats.hc_tx_err_inval_vportoffset_pkt =
resp.tx_err_inval_vport_offset_pkt;
- apc->eth_stats.hc_tx_err_vlan_enforcement =
+ ac->hc_stats.hc_tx_err_vlan_enforcement =
resp.tx_err_vlan_enforcement;
- apc->eth_stats.hc_tx_err_eth_type_enforcement =
+ ac->hc_stats.hc_tx_err_eth_type_enforcement =
resp.tx_err_ethtype_enforcement;
- apc->eth_stats.hc_tx_err_sa_enforcement = resp.tx_err_SA_enforcement;
- apc->eth_stats.hc_tx_err_sqpdid_enforcement =
+ ac->hc_stats.hc_tx_err_sa_enforcement = resp.tx_err_SA_enforcement;
+ ac->hc_stats.hc_tx_err_sqpdid_enforcement =
resp.tx_err_SQPDID_enforcement;
- apc->eth_stats.hc_tx_err_cqpdid_enforcement =
+ ac->hc_stats.hc_tx_err_cqpdid_enforcement =
resp.tx_err_CQPDID_enforcement;
- apc->eth_stats.hc_tx_err_mtu_violation = resp.tx_err_mtu_violation;
- apc->eth_stats.hc_tx_err_inval_oob = resp.tx_err_inval_oob;
- apc->eth_stats.hc_tx_bytes = resp.hc_tx_bytes;
- apc->eth_stats.hc_tx_ucast_pkts = resp.hc_tx_ucast_pkts;
- apc->eth_stats.hc_tx_ucast_bytes = resp.hc_tx_ucast_bytes;
- apc->eth_stats.hc_tx_bcast_pkts = resp.hc_tx_bcast_pkts;
- apc->eth_stats.hc_tx_bcast_bytes = resp.hc_tx_bcast_bytes;
- apc->eth_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts;
- apc->eth_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes;
- apc->eth_stats.hc_tx_err_gdma = resp.tx_err_gdma;
+ ac->hc_stats.hc_tx_err_mtu_violation = resp.tx_err_mtu_violation;
+ ac->hc_stats.hc_tx_err_inval_oob = resp.tx_err_inval_oob;
+ ac->hc_stats.hc_tx_bytes = resp.hc_tx_bytes;
+ ac->hc_stats.hc_tx_ucast_pkts = resp.hc_tx_ucast_pkts;
+ ac->hc_stats.hc_tx_ucast_bytes = resp.hc_tx_ucast_bytes;
+ ac->hc_stats.hc_tx_bcast_pkts = resp.hc_tx_bcast_pkts;
+ ac->hc_stats.hc_tx_bcast_bytes = resp.hc_tx_bcast_bytes;
+ ac->hc_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts;
+ ac->hc_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes;
+ ac->hc_stats.hc_tx_err_gdma = resp.tx_err_gdma;
+
+ return 0;
}
void mana_query_phy_stats(struct mana_port_context *apc)
@@ -3059,9 +3142,6 @@ int mana_attach(struct net_device *ndev)
/* Ensure port state updated before txq state */
smp_wmb();
- if (apc->port_is_up)
- netif_carrier_on(ndev);
-
netif_device_attach(ndev);
return 0;
@@ -3154,7 +3234,6 @@ int mana_detach(struct net_device *ndev, bool from_close)
smp_wmb();
netif_tx_disable(ndev);
- netif_carrier_off(ndev);
if (apc->port_st_save) {
err = mana_dealloc_queues(ndev);
@@ -3243,6 +3322,8 @@ static int mana_probe_port(struct mana_context *ac, int port_idx,
goto free_indir;
}
+ netif_carrier_on(ndev);
+
debugfs_create_u32("current_speed", 0400, apc->mana_port_debugfs, &apc->speed);
return 0;
@@ -3389,6 +3470,24 @@ int mana_rdma_service_event(struct gdma_context *gc, enum gdma_service_type even
return 0;
}
+#define MANA_GF_STATS_PERIOD (2 * HZ)
+
+static void mana_gf_stats_work_handler(struct work_struct *work)
+{
+ struct mana_context *ac =
+ container_of(to_delayed_work(work), struct mana_context, gf_stats_work);
+ int err;
+
+ err = mana_query_gf_stats(ac);
+ if (err == -ETIMEDOUT) {
+ /* HWC timeout detected - reset stats and stop rescheduling */
+ ac->hwc_timeout_occurred = true;
+ memset(&ac->hc_stats, 0, sizeof(ac->hc_stats));
+ return;
+ }
+ schedule_delayed_work(&ac->gf_stats_work, MANA_GF_STATS_PERIOD);
+}
+
int mana_probe(struct gdma_dev *gd, bool resuming)
{
struct gdma_context *gc = gd->gdma_context;
@@ -3431,6 +3530,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
if (!resuming) {
ac->num_ports = num_ports;
+
+ INIT_WORK(&ac->link_change_work, mana_link_state_handle);
} else {
if (ac->num_ports != num_ports) {
dev_err(dev, "The number of vPorts changed: %d->%d\n",
@@ -3438,6 +3539,8 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
err = -EPROTO;
goto out;
}
+
+ enable_work(&ac->link_change_work);
}
if (ac->num_ports == 0)
@@ -3477,6 +3580,10 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
}
err = add_adev(gd, "eth");
+
+ INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
+ schedule_delayed_work(&ac->gf_stats_work, MANA_GF_STATS_PERIOD);
+
out:
if (err) {
mana_remove(gd, false);
@@ -3500,6 +3607,9 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
int err;
int i;
+ disable_work_sync(&ac->link_change_work);
+ cancel_delayed_work_sync(&ac->gf_stats_work);
+
/* adev currently doesn't support suspending, always remove it */
if (gd->adev)
remove_adev(gd);
diff --git a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
index a1afa75a9463..0e2f4343ac67 100644
--- a/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
+++ b/drivers/net/ethernet/microsoft/mana/mana_ethtool.c
@@ -15,66 +15,71 @@ struct mana_stats_desc {
static const struct mana_stats_desc mana_eth_stats[] = {
{"stop_queue", offsetof(struct mana_ethtool_stats, stop_queue)},
{"wake_queue", offsetof(struct mana_ethtool_stats, wake_queue)},
- {"hc_rx_discards_no_wqe", offsetof(struct mana_ethtool_stats,
+ {"tx_cq_err", offsetof(struct mana_ethtool_stats, tx_cqe_err)},
+ {"tx_cqe_unknown_type", offsetof(struct mana_ethtool_stats,
+ tx_cqe_unknown_type)},
+ {"tx_linear_pkt_cnt", offsetof(struct mana_ethtool_stats,
+ tx_linear_pkt_cnt)},
+ {"rx_coalesced_err", offsetof(struct mana_ethtool_stats,
+ rx_coalesced_err)},
+ {"rx_cqe_unknown_type", offsetof(struct mana_ethtool_stats,
+ rx_cqe_unknown_type)},
+};
+
+static const struct mana_stats_desc mana_hc_stats[] = {
+ {"hc_rx_discards_no_wqe", offsetof(struct mana_ethtool_hc_stats,
hc_rx_discards_no_wqe)},
- {"hc_rx_err_vport_disabled", offsetof(struct mana_ethtool_stats,
+ {"hc_rx_err_vport_disabled", offsetof(struct mana_ethtool_hc_stats,
hc_rx_err_vport_disabled)},
- {"hc_rx_bytes", offsetof(struct mana_ethtool_stats, hc_rx_bytes)},
- {"hc_rx_ucast_pkts", offsetof(struct mana_ethtool_stats,
+ {"hc_rx_bytes", offsetof(struct mana_ethtool_hc_stats, hc_rx_bytes)},
+ {"hc_rx_ucast_pkts", offsetof(struct mana_ethtool_hc_stats,
hc_rx_ucast_pkts)},
- {"hc_rx_ucast_bytes", offsetof(struct mana_ethtool_stats,
+ {"hc_rx_ucast_bytes", offsetof(struct mana_ethtool_hc_stats,
hc_rx_ucast_bytes)},
- {"hc_rx_bcast_pkts", offsetof(struct mana_ethtool_stats,
+ {"hc_rx_bcast_pkts", offsetof(struct mana_ethtool_hc_stats,
hc_rx_bcast_pkts)},
- {"hc_rx_bcast_bytes", offsetof(struct mana_ethtool_stats,
+ {"hc_rx_bcast_bytes", offsetof(struct mana_ethtool_hc_stats,
hc_rx_bcast_bytes)},
- {"hc_rx_mcast_pkts", offsetof(struct mana_ethtool_stats,
- hc_rx_mcast_pkts)},
- {"hc_rx_mcast_bytes", offsetof(struct mana_ethtool_stats,
+ {"hc_rx_mcast_pkts", offsetof(struct mana_ethtool_hc_stats,
+ hc_rx_mcast_pkts)},
+ {"hc_rx_mcast_bytes", offsetof(struct mana_ethtool_hc_stats,
hc_rx_mcast_bytes)},
- {"hc_tx_err_gf_disabled", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_err_gf_disabled", offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_gf_disabled)},
- {"hc_tx_err_vport_disabled", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_err_vport_disabled", offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_vport_disabled)},
{"hc_tx_err_inval_vportoffset_pkt",
- offsetof(struct mana_ethtool_stats,
+ offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_inval_vportoffset_pkt)},
- {"hc_tx_err_vlan_enforcement", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_err_vlan_enforcement", offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_vlan_enforcement)},
{"hc_tx_err_eth_type_enforcement",
- offsetof(struct mana_ethtool_stats, hc_tx_err_eth_type_enforcement)},
- {"hc_tx_err_sa_enforcement", offsetof(struct mana_ethtool_stats,
+ offsetof(struct mana_ethtool_hc_stats, hc_tx_err_eth_type_enforcement)},
+ {"hc_tx_err_sa_enforcement", offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_sa_enforcement)},
{"hc_tx_err_sqpdid_enforcement",
- offsetof(struct mana_ethtool_stats, hc_tx_err_sqpdid_enforcement)},
+ offsetof(struct mana_ethtool_hc_stats, hc_tx_err_sqpdid_enforcement)},
{"hc_tx_err_cqpdid_enforcement",
- offsetof(struct mana_ethtool_stats, hc_tx_err_cqpdid_enforcement)},
- {"hc_tx_err_mtu_violation", offsetof(struct mana_ethtool_stats,
+ offsetof(struct mana_ethtool_hc_stats, hc_tx_err_cqpdid_enforcement)},
+ {"hc_tx_err_mtu_violation", offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_mtu_violation)},
- {"hc_tx_err_inval_oob", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_err_inval_oob", offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_inval_oob)},
- {"hc_tx_err_gdma", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_err_gdma", offsetof(struct mana_ethtool_hc_stats,
hc_tx_err_gdma)},
- {"hc_tx_bytes", offsetof(struct mana_ethtool_stats, hc_tx_bytes)},
- {"hc_tx_ucast_pkts", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_bytes", offsetof(struct mana_ethtool_hc_stats, hc_tx_bytes)},
+ {"hc_tx_ucast_pkts", offsetof(struct mana_ethtool_hc_stats,
hc_tx_ucast_pkts)},
- {"hc_tx_ucast_bytes", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_ucast_bytes", offsetof(struct mana_ethtool_hc_stats,
hc_tx_ucast_bytes)},
- {"hc_tx_bcast_pkts", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_bcast_pkts", offsetof(struct mana_ethtool_hc_stats,
hc_tx_bcast_pkts)},
- {"hc_tx_bcast_bytes", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_bcast_bytes", offsetof(struct mana_ethtool_hc_stats,
hc_tx_bcast_bytes)},
- {"hc_tx_mcast_pkts", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_mcast_pkts", offsetof(struct mana_ethtool_hc_stats,
hc_tx_mcast_pkts)},
- {"hc_tx_mcast_bytes", offsetof(struct mana_ethtool_stats,
+ {"hc_tx_mcast_bytes", offsetof(struct mana_ethtool_hc_stats,
hc_tx_mcast_bytes)},
- {"tx_cq_err", offsetof(struct mana_ethtool_stats, tx_cqe_err)},
- {"tx_cqe_unknown_type", offsetof(struct mana_ethtool_stats,
- tx_cqe_unknown_type)},
- {"rx_coalesced_err", offsetof(struct mana_ethtool_stats,
- rx_coalesced_err)},
- {"rx_cqe_unknown_type", offsetof(struct mana_ethtool_stats,
- rx_cqe_unknown_type)},
};
static const struct mana_stats_desc mana_phy_stats[] = {
@@ -138,7 +143,7 @@ static int mana_get_sset_count(struct net_device *ndev, int stringset)
if (stringset != ETH_SS_STATS)
return -EINVAL;
- return ARRAY_SIZE(mana_eth_stats) + ARRAY_SIZE(mana_phy_stats) +
+ return ARRAY_SIZE(mana_eth_stats) + ARRAY_SIZE(mana_phy_stats) + ARRAY_SIZE(mana_hc_stats) +
num_queues * (MANA_STATS_RX_COUNT + MANA_STATS_TX_COUNT);
}
@@ -150,10 +155,12 @@ static void mana_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
if (stringset != ETH_SS_STATS)
return;
-
for (i = 0; i < ARRAY_SIZE(mana_eth_stats); i++)
ethtool_puts(&data, mana_eth_stats[i].name);
+ for (i = 0; i < ARRAY_SIZE(mana_hc_stats); i++)
+ ethtool_puts(&data, mana_hc_stats[i].name);
+
for (i = 0; i < ARRAY_SIZE(mana_phy_stats); i++)
ethtool_puts(&data, mana_phy_stats[i].name);
@@ -186,6 +193,7 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
struct mana_port_context *apc = netdev_priv(ndev);
unsigned int num_queues = apc->num_queues;
void *eth_stats = &apc->eth_stats;
+ void *hc_stats = &apc->ac->hc_stats;
void *phy_stats = &apc->phy_stats;
struct mana_stats_rx *rx_stats;
struct mana_stats_tx *tx_stats;
@@ -207,8 +215,6 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
if (!apc->port_is_up)
return;
- /* we call mana function to update stats from GDMA */
- mana_query_gf_stats(apc);
/* We call this mana function to get the phy stats from GDMA and includes
* aggregate tx/rx drop counters, Per-TC(Traffic Channel) tx/rx and pause
@@ -219,6 +225,9 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
for (q = 0; q < ARRAY_SIZE(mana_eth_stats); q++)
data[i++] = *(u64 *)(eth_stats + mana_eth_stats[q].offset);
+ for (q = 0; q < ARRAY_SIZE(mana_hc_stats); q++)
+ data[i++] = *(u64 *)(hc_stats + mana_hc_stats[q].offset);
+
for (q = 0; q < ARRAY_SIZE(mana_phy_stats); q++)
data[i++] = *(u64 *)(phy_stats + mana_phy_stats[q].offset);
diff --git a/drivers/net/ethernet/mucse/Kconfig b/drivers/net/ethernet/mucse/Kconfig
new file mode 100644
index 000000000000..0b3e853d625f
--- /dev/null
+++ b/drivers/net/ethernet/mucse/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Mucse network device configuration
+#
+
+config NET_VENDOR_MUCSE
+ bool "Mucse devices"
+ default y
+ help
+ If you have a network (Ethernet) card from Mucse(R), say Y.
+
+ Note that the answer to this question doesn't directly affect the
+ kernel: saying N will just cause the configurator to skip all
+ the questions about Mucse(R) cards. If you say Y, you will
+ be asked for your specific card in the following questions.
+
+if NET_VENDOR_MUCSE
+
+config MGBE
+ tristate "Mucse(R) 1GbE PCI Express adapters support"
+ depends on PCI
+ help
+ This driver supports Mucse(R) 1GbE PCI Express family of
+ adapters.
+
+ More specific information on configuring the driver is in
+ <file:Documentation/networking/device_drivers/ethernet/mucse/rnpgbe.rst>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called rnpgbe.
+
+endif # NET_VENDOR_MUCSE
+
diff --git a/drivers/net/ethernet/mucse/Makefile b/drivers/net/ethernet/mucse/Makefile
new file mode 100644
index 000000000000..675173fa05f7
--- /dev/null
+++ b/drivers/net/ethernet/mucse/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright(c) 2020 - 2025 MUCSE Corporation.
+#
+# Makefile for the MUCSE(R) network device drivers
+#
+
+obj-$(CONFIG_MGBE) += rnpgbe/
diff --git a/drivers/net/ethernet/mucse/rnpgbe/Makefile b/drivers/net/ethernet/mucse/rnpgbe/Makefile
new file mode 100644
index 000000000000..de8bcb7772ab
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright(c) 2020 - 2025 MUCSE Corporation.
+#
+# Makefile for the MUCSE(R) 1GbE PCI Express ethernet driver
+#
+
+obj-$(CONFIG_MGBE) += rnpgbe.o
+rnpgbe-objs := rnpgbe_main.o\
+ rnpgbe_chip.o\
+ rnpgbe_mbx.o\
+ rnpgbe_mbx_fw.o
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
new file mode 100644
index 000000000000..5b024f9f7e17
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#ifndef _RNPGBE_H
+#define _RNPGBE_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+
+enum rnpgbe_boards {
+ board_n500,
+ board_n210
+};
+
+struct mucse_mbx_info {
+ u32 timeout_us;
+ u32 delay_us;
+ u16 fw_req;
+ u16 fw_ack;
+ /* lock for only one use mbx */
+ struct mutex lock;
+ /* fw <--> pf mbx */
+ u32 fwpf_shm_base;
+ u32 pf2fw_mbx_ctrl;
+ u32 fwpf_mbx_mask;
+ u32 fwpf_ctrl_base;
+};
+
+/* Enum for firmware notification modes,
+ * more modes (e.g., portup, link_report) will be added in future
+ **/
+enum {
+ mucse_fw_powerup,
+};
+
+struct mucse_hw {
+ void __iomem *hw_addr;
+ struct pci_dev *pdev;
+ struct mucse_mbx_info mbx;
+ int port;
+ u8 pfvfnum;
+};
+
+struct mucse_stats {
+ u64 tx_dropped;
+};
+
+struct mucse {
+ struct net_device *netdev;
+ struct pci_dev *pdev;
+ struct mucse_hw hw;
+ struct mucse_stats stats;
+};
+
+int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr);
+int rnpgbe_reset_hw(struct mucse_hw *hw);
+int rnpgbe_send_notify(struct mucse_hw *hw,
+ bool enable,
+ int mode);
+int rnpgbe_init_hw(struct mucse_hw *hw, int board_type);
+
+/* Device IDs */
+#define PCI_VENDOR_ID_MUCSE 0x8848
+#define RNPGBE_DEVICE_ID_N500_QUAD_PORT 0x8308
+#define RNPGBE_DEVICE_ID_N500_DUAL_PORT 0x8318
+#define RNPGBE_DEVICE_ID_N210 0x8208
+#define RNPGBE_DEVICE_ID_N210L 0x820a
+
+#define mucse_hw_wr32(hw, reg, val) \
+ writel((val), (hw)->hw_addr + (reg))
+#endif /* _RNPGBE_H */
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
new file mode 100644
index 000000000000..ebc7b3750157
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_chip.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+
+#include "rnpgbe.h"
+#include "rnpgbe_hw.h"
+#include "rnpgbe_mbx.h"
+#include "rnpgbe_mbx_fw.h"
+
+/**
+ * rnpgbe_get_permanent_mac - Get permanent mac
+ * @hw: hw information structure
+ * @perm_addr: pointer to store perm_addr
+ *
+ * rnpgbe_get_permanent_mac tries to get mac from hw
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int rnpgbe_get_permanent_mac(struct mucse_hw *hw, u8 *perm_addr)
+{
+ struct device *dev = &hw->pdev->dev;
+ int err;
+
+ err = mucse_mbx_get_macaddr(hw, hw->pfvfnum, perm_addr, hw->port);
+ if (err) {
+ dev_err(dev, "Failed to get MAC from FW %d\n", err);
+ return err;
+ }
+
+ if (!is_valid_ether_addr(perm_addr)) {
+ dev_err(dev, "Failed to get valid MAC from FW\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * rnpgbe_reset_hw - Do a hardware reset
+ * @hw: hw information structure
+ *
+ * rnpgbe_reset_hw calls fw to do a hardware
+ * reset, and cleans some regs to default.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int rnpgbe_reset_hw(struct mucse_hw *hw)
+{
+ mucse_hw_wr32(hw, RNPGBE_DMA_AXI_EN, 0);
+ return mucse_mbx_reset_hw(hw);
+}
+
+/**
+ * rnpgbe_send_notify - Echo fw status
+ * @hw: hw information structure
+ * @enable: true or false status
+ * @mode: status mode
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int rnpgbe_send_notify(struct mucse_hw *hw,
+ bool enable,
+ int mode)
+{
+ int err;
+ /* Keep switch struct to support more modes in the future */
+ switch (mode) {
+ case mucse_fw_powerup:
+ err = mucse_mbx_powerup(hw, enable);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+/**
+ * rnpgbe_init_n500 - Setup n500 hw info
+ * @hw: hw information structure
+ *
+ * rnpgbe_init_n500 initializes all private
+ * structure for n500
+ **/
+static void rnpgbe_init_n500(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+
+ mbx->fwpf_ctrl_base = MUCSE_N500_FWPF_CTRL_BASE;
+ mbx->fwpf_shm_base = MUCSE_N500_FWPF_SHM_BASE;
+}
+
+/**
+ * rnpgbe_init_n210 - Setup n210 hw info
+ * @hw: hw information structure
+ *
+ * rnpgbe_init_n210 initializes all private
+ * structure for n210
+ **/
+static void rnpgbe_init_n210(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+
+ mbx->fwpf_ctrl_base = MUCSE_N210_FWPF_CTRL_BASE;
+ mbx->fwpf_shm_base = MUCSE_N210_FWPF_SHM_BASE;
+}
+
+/**
+ * rnpgbe_init_hw - Setup hw info according to board_type
+ * @hw: hw information structure
+ * @board_type: board type
+ *
+ * rnpgbe_init_hw initializes all hw data
+ *
+ * Return: 0 on success, -EINVAL on failure
+ **/
+int rnpgbe_init_hw(struct mucse_hw *hw, int board_type)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+
+ hw->port = 0;
+
+ mbx->pf2fw_mbx_ctrl = MUCSE_GBE_PFFW_MBX_CTRL_OFFSET;
+ mbx->fwpf_mbx_mask = MUCSE_GBE_FWPF_MBX_MASK_OFFSET;
+
+ switch (board_type) {
+ case board_n500:
+ rnpgbe_init_n500(hw);
+ break;
+ case board_n210:
+ rnpgbe_init_n210(hw);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* init_params with mbx base */
+ mucse_init_mbx_params_pf(hw);
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
new file mode 100644
index 000000000000..e77e6bc3d3e3
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_hw.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#ifndef _RNPGBE_HW_H
+#define _RNPGBE_HW_H
+
+#define MUCSE_N500_FWPF_CTRL_BASE 0x28b00
+#define MUCSE_N500_FWPF_SHM_BASE 0x2d000
+#define MUCSE_GBE_PFFW_MBX_CTRL_OFFSET 0x5500
+#define MUCSE_GBE_FWPF_MBX_MASK_OFFSET 0x5700
+#define MUCSE_N210_FWPF_CTRL_BASE 0x29400
+#define MUCSE_N210_FWPF_SHM_BASE 0x2d900
+
+#define RNPGBE_DMA_AXI_EN 0x0010
+
+#define RNPGBE_MAX_QUEUES 8
+#endif /* _RNPGBE_HW_H */
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
new file mode 100644
index 000000000000..316f941629d4
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_main.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#include <linux/pci.h>
+#include <net/rtnetlink.h>
+#include <linux/etherdevice.h>
+
+#include "rnpgbe.h"
+#include "rnpgbe_hw.h"
+#include "rnpgbe_mbx_fw.h"
+
+static const char rnpgbe_driver_name[] = "rnpgbe";
+
+/* rnpgbe_pci_tbl - PCI Device ID Table
+ *
+ * { PCI_VDEVICE(Vendor ID, Device ID),
+ * private_data (used for different hw chip) }
+ */
+static struct pci_device_id rnpgbe_pci_tbl[] = {
+ { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210), board_n210 },
+ { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N210L), board_n210 },
+ { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_DUAL_PORT), board_n500 },
+ { PCI_VDEVICE(MUCSE, RNPGBE_DEVICE_ID_N500_QUAD_PORT), board_n500 },
+ /* required last entry */
+ {0, },
+};
+
+/**
+ * rnpgbe_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP).
+ *
+ * Return: 0
+ **/
+static int rnpgbe_open(struct net_device *netdev)
+{
+ return 0;
+}
+
+/**
+ * rnpgbe_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS.
+ *
+ * Return: 0, this is not allowed to fail
+ **/
+static int rnpgbe_close(struct net_device *netdev)
+{
+ return 0;
+}
+
+/**
+ * rnpgbe_xmit_frame - Send a skb to driver
+ * @skb: skb structure to be sent
+ * @netdev: network interface device structure
+ *
+ * Return: NETDEV_TX_OK
+ **/
+static netdev_tx_t rnpgbe_xmit_frame(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct mucse *mucse = netdev_priv(netdev);
+
+ dev_kfree_skb_any(skb);
+ mucse->stats.tx_dropped++;
+
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops rnpgbe_netdev_ops = {
+ .ndo_open = rnpgbe_open,
+ .ndo_stop = rnpgbe_close,
+ .ndo_start_xmit = rnpgbe_xmit_frame,
+};
+
+/**
+ * rnpgbe_add_adapter - Add netdev for this pci_dev
+ * @pdev: PCI device information structure
+ * @board_type: board type
+ *
+ * rnpgbe_add_adapter initializes a netdev for this pci_dev
+ * structure. Initializes Bar map, private structure, and a
+ * hardware reset occur.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+static int rnpgbe_add_adapter(struct pci_dev *pdev,
+ int board_type)
+{
+ struct net_device *netdev;
+ u8 perm_addr[ETH_ALEN];
+ void __iomem *hw_addr;
+ struct mucse *mucse;
+ struct mucse_hw *hw;
+ int err, err_notify;
+
+ netdev = alloc_etherdev_mq(sizeof(struct mucse), RNPGBE_MAX_QUEUES);
+ if (!netdev)
+ return -ENOMEM;
+
+ SET_NETDEV_DEV(netdev, &pdev->dev);
+ mucse = netdev_priv(netdev);
+ mucse->netdev = netdev;
+ mucse->pdev = pdev;
+ pci_set_drvdata(pdev, mucse);
+
+ hw = &mucse->hw;
+ hw_addr = devm_ioremap(&pdev->dev,
+ pci_resource_start(pdev, 2),
+ pci_resource_len(pdev, 2));
+ if (!hw_addr) {
+ err = -EIO;
+ goto err_free_net;
+ }
+
+ hw->hw_addr = hw_addr;
+ hw->pdev = pdev;
+
+ err = rnpgbe_init_hw(hw, board_type);
+ if (err) {
+ dev_err(&pdev->dev, "Init hw err %d\n", err);
+ goto err_free_net;
+ }
+ /* Step 1: Send power-up notification to firmware (no response expected)
+ * This informs firmware to initialize hardware power state, but
+ * firmware only acknowledges receipt without returning data. Must be
+ * done before synchronization as firmware may be in low-power idle
+ * state initially.
+ */
+ err_notify = rnpgbe_send_notify(hw, true, mucse_fw_powerup);
+ if (err_notify) {
+ dev_warn(&pdev->dev, "Send powerup to hw failed %d\n",
+ err_notify);
+ dev_warn(&pdev->dev, "Maybe low performance\n");
+ }
+ /* Step 2: Synchronize mailbox communication with firmware (requires
+ * response) After power-up, confirm firmware is ready to process
+ * requests with responses. This ensures subsequent request/response
+ * interactions work reliably.
+ */
+ err = mucse_mbx_sync_fw(hw);
+ if (err) {
+ dev_err(&pdev->dev, "Sync fw failed! %d\n", err);
+ goto err_powerdown;
+ }
+
+ netdev->netdev_ops = &rnpgbe_netdev_ops;
+ err = rnpgbe_reset_hw(hw);
+ if (err) {
+ dev_err(&pdev->dev, "Hw reset failed %d\n", err);
+ goto err_powerdown;
+ }
+
+ err = rnpgbe_get_permanent_mac(hw, perm_addr);
+ if (!err) {
+ eth_hw_addr_set(netdev, perm_addr);
+ } else if (err == -EINVAL) {
+ dev_warn(&pdev->dev, "Using random MAC\n");
+ eth_hw_addr_random(netdev);
+ } else if (err) {
+ dev_err(&pdev->dev, "get perm_addr failed %d\n", err);
+ goto err_powerdown;
+ }
+
+ err = register_netdev(netdev);
+ if (err)
+ goto err_powerdown;
+
+ return 0;
+err_powerdown:
+ /* notify powerdown only powerup ok */
+ if (!err_notify) {
+ err_notify = rnpgbe_send_notify(hw, false, mucse_fw_powerup);
+ if (err_notify)
+ dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n",
+ err_notify);
+ }
+err_free_net:
+ free_netdev(netdev);
+ return err;
+}
+
+/**
+ * rnpgbe_probe - Device initialization routine
+ * @pdev: PCI device information struct
+ * @id: entry in rnpgbe_pci_tbl
+ *
+ * rnpgbe_probe initializes a PF adapter identified by a pci_dev
+ * structure.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+static int rnpgbe_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int board_type = id->driver_data;
+ int err;
+
+ err = pci_enable_device_mem(pdev);
+ if (err)
+ return err;
+
+ err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(56));
+ if (err) {
+ dev_err(&pdev->dev,
+ "No usable DMA configuration, aborting %d\n", err);
+ goto err_disable_dev;
+ }
+
+ err = pci_request_mem_regions(pdev, rnpgbe_driver_name);
+ if (err) {
+ dev_err(&pdev->dev,
+ "pci_request_selected_regions failed %d\n", err);
+ goto err_disable_dev;
+ }
+
+ pci_set_master(pdev);
+ err = pci_save_state(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_save_state failed %d\n", err);
+ goto err_free_regions;
+ }
+
+ err = rnpgbe_add_adapter(pdev, board_type);
+ if (err)
+ goto err_free_regions;
+
+ return 0;
+err_free_regions:
+ pci_release_mem_regions(pdev);
+err_disable_dev:
+ pci_disable_device(pdev);
+ return err;
+}
+
+/**
+ * rnpgbe_rm_adapter - Remove netdev for this mucse structure
+ * @pdev: PCI device information struct
+ *
+ * rnpgbe_rm_adapter remove a netdev for this mucse structure
+ **/
+static void rnpgbe_rm_adapter(struct pci_dev *pdev)
+{
+ struct mucse *mucse = pci_get_drvdata(pdev);
+ struct mucse_hw *hw = &mucse->hw;
+ struct net_device *netdev;
+ int err;
+
+ if (!mucse)
+ return;
+ netdev = mucse->netdev;
+ unregister_netdev(netdev);
+ err = rnpgbe_send_notify(hw, false, mucse_fw_powerup);
+ if (err)
+ dev_warn(&pdev->dev, "Send powerdown to hw failed %d\n", err);
+ free_netdev(netdev);
+}
+
+/**
+ * rnpgbe_remove - Device removal routine
+ * @pdev: PCI device information struct
+ *
+ * rnpgbe_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device. This could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ **/
+static void rnpgbe_remove(struct pci_dev *pdev)
+{
+ rnpgbe_rm_adapter(pdev);
+ pci_release_mem_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+/**
+ * rnpgbe_dev_shutdown - Device shutdown routine
+ * @pdev: PCI device information struct
+ **/
+static void rnpgbe_dev_shutdown(struct pci_dev *pdev)
+{
+ struct mucse *mucse = pci_get_drvdata(pdev);
+ struct net_device *netdev = mucse->netdev;
+
+ rtnl_lock();
+ netif_device_detach(netdev);
+ if (netif_running(netdev))
+ rnpgbe_close(netdev);
+ rtnl_unlock();
+ pci_disable_device(pdev);
+}
+
+/**
+ * rnpgbe_shutdown - Device shutdown routine
+ * @pdev: PCI device information struct
+ *
+ * rnpgbe_shutdown is called by the PCI subsystem to alert the driver
+ * that os shutdown. Device should setup wakeup state here.
+ **/
+static void rnpgbe_shutdown(struct pci_dev *pdev)
+{
+ rnpgbe_dev_shutdown(pdev);
+}
+
+static struct pci_driver rnpgbe_driver = {
+ .name = rnpgbe_driver_name,
+ .id_table = rnpgbe_pci_tbl,
+ .probe = rnpgbe_probe,
+ .remove = rnpgbe_remove,
+ .shutdown = rnpgbe_shutdown,
+};
+
+module_pci_driver(rnpgbe_driver);
+
+MODULE_DEVICE_TABLE(pci, rnpgbe_pci_tbl);
+MODULE_AUTHOR("Yibo Dong, <dong100@mucse.com>");
+MODULE_DESCRIPTION("Mucse(R) 1 Gigabit PCI Express Network Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
new file mode 100644
index 000000000000..de5e29230b3c
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2022 - 2025 Mucse Corporation. */
+
+#include <linux/errno.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+
+#include "rnpgbe_mbx.h"
+
+/**
+ * mbx_data_rd32 - Reads reg with base mbx->fwpf_shm_base
+ * @mbx: pointer to the MBX structure
+ * @reg: register offset
+ *
+ * Return: register value
+ **/
+static u32 mbx_data_rd32(struct mucse_mbx_info *mbx, u32 reg)
+{
+ struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
+
+ return readl(hw->hw_addr + mbx->fwpf_shm_base + reg);
+}
+
+/**
+ * mbx_data_wr32 - Writes value to reg with base mbx->fwpf_shm_base
+ * @mbx: pointer to the MBX structure
+ * @reg: register offset
+ * @value: value to be written
+ *
+ **/
+static void mbx_data_wr32(struct mucse_mbx_info *mbx, u32 reg, u32 value)
+{
+ struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
+
+ writel(value, hw->hw_addr + mbx->fwpf_shm_base + reg);
+}
+
+/**
+ * mbx_ctrl_rd32 - Reads reg with base mbx->fwpf_ctrl_base
+ * @mbx: pointer to the MBX structure
+ * @reg: register offset
+ *
+ * Return: register value
+ **/
+static u32 mbx_ctrl_rd32(struct mucse_mbx_info *mbx, u32 reg)
+{
+ struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
+
+ return readl(hw->hw_addr + mbx->fwpf_ctrl_base + reg);
+}
+
+/**
+ * mbx_ctrl_wr32 - Writes value to reg with base mbx->fwpf_ctrl_base
+ * @mbx: pointer to the MBX structure
+ * @reg: register offset
+ * @value: value to be written
+ *
+ **/
+static void mbx_ctrl_wr32(struct mucse_mbx_info *mbx, u32 reg, u32 value)
+{
+ struct mucse_hw *hw = container_of(mbx, struct mucse_hw, mbx);
+
+ writel(value, hw->hw_addr + mbx->fwpf_ctrl_base + reg);
+}
+
+/**
+ * mucse_mbx_get_lock_pf - Write ctrl and read back lock status
+ * @hw: pointer to the HW structure
+ *
+ * Return: register value after write
+ **/
+static u32 mucse_mbx_get_lock_pf(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u32 reg = MUCSE_MBX_PF2FW_CTRL(mbx);
+
+ mbx_ctrl_wr32(mbx, reg, MUCSE_MBX_PFU);
+
+ return mbx_ctrl_rd32(mbx, reg);
+}
+
+/**
+ * mucse_obtain_mbx_lock_pf - Obtain mailbox lock
+ * @hw: pointer to the HW structure
+ *
+ * Pair with mucse_release_mbx_lock_pf()
+ * This function maybe used in an irq handler.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+static int mucse_obtain_mbx_lock_pf(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u32 val;
+
+ return read_poll_timeout_atomic(mucse_mbx_get_lock_pf,
+ val, val & MUCSE_MBX_PFU,
+ mbx->delay_us,
+ mbx->timeout_us,
+ false, hw);
+}
+
+/**
+ * mucse_release_mbx_lock_pf - Release mailbox lock
+ * @hw: pointer to the HW structure
+ * @req: send a request or not
+ *
+ * Pair with mucse_obtain_mbx_lock_pf():
+ * - Releases the mailbox lock by clearing MUCSE_MBX_PFU bit
+ * - Simultaneously sends the request by setting MUCSE_MBX_REQ bit
+ * if req is true
+ * (Both bits are in the same mailbox control register,
+ * so operations are combined)
+ **/
+static void mucse_release_mbx_lock_pf(struct mucse_hw *hw, bool req)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u32 reg = MUCSE_MBX_PF2FW_CTRL(mbx);
+
+ mbx_ctrl_wr32(mbx, reg, req ? MUCSE_MBX_REQ : 0);
+}
+
+/**
+ * mucse_mbx_get_fwreq - Read fw req from reg
+ * @mbx: pointer to the mbx structure
+ *
+ * Return: the fwreq value
+ **/
+static u16 mucse_mbx_get_fwreq(struct mucse_mbx_info *mbx)
+{
+ u32 val = mbx_data_rd32(mbx, MUCSE_MBX_FW2PF_CNT);
+
+ return FIELD_GET(GENMASK_U32(15, 0), val);
+}
+
+/**
+ * mucse_mbx_inc_pf_ack - Increase ack
+ * @hw: pointer to the HW structure
+ *
+ * mucse_mbx_inc_pf_ack reads pf_ack from hw, then writes
+ * new value back after increase
+ **/
+static void mucse_mbx_inc_pf_ack(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u16 ack;
+ u32 val;
+
+ val = mbx_data_rd32(mbx, MUCSE_MBX_PF2FW_CNT);
+ ack = FIELD_GET(GENMASK_U32(31, 16), val);
+ ack++;
+ val &= ~GENMASK_U32(31, 16);
+ val |= FIELD_PREP(GENMASK_U32(31, 16), ack);
+ mbx_data_wr32(mbx, MUCSE_MBX_PF2FW_CNT, val);
+}
+
+/**
+ * mucse_read_mbx_pf - Read a message from the mailbox
+ * @hw: pointer to the HW structure
+ * @msg: the message buffer
+ * @size: length of buffer
+ *
+ * mucse_read_mbx_pf copies a message from the mbx buffer to the caller's
+ * memory buffer. The presumption is that the caller knows that there was
+ * a message due to a fw request so no polling for message is needed.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+static int mucse_read_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
+{
+ const int size_in_words = size / sizeof(u32);
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ int err;
+
+ err = mucse_obtain_mbx_lock_pf(hw);
+ if (err)
+ return err;
+
+ for (int i = 0; i < size_in_words; i++)
+ msg[i] = mbx_data_rd32(mbx, MUCSE_MBX_FWPF_SHM + 4 * i);
+ /* Hw needs write data_reg at last */
+ mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM, 0);
+ /* flush reqs as we have read this request data */
+ hw->mbx.fw_req = mucse_mbx_get_fwreq(mbx);
+ mucse_mbx_inc_pf_ack(hw);
+ mucse_release_mbx_lock_pf(hw, false);
+
+ return 0;
+}
+
+/**
+ * mucse_check_for_msg_pf - Check to see if the fw has sent mail
+ * @hw: pointer to the HW structure
+ *
+ * Return: 0 if the fw has set the Status bit or else -EIO
+ **/
+static int mucse_check_for_msg_pf(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u16 fw_req;
+
+ fw_req = mucse_mbx_get_fwreq(mbx);
+ /* chip's register is reset to 0 when rc send reset
+ * mbx command. Return -EIO if in this state, others
+ * fw == hw->mbx.fw_req means no new msg.
+ **/
+ if (fw_req == 0 || fw_req == hw->mbx.fw_req)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * mucse_poll_for_msg - Wait for message notification
+ * @hw: pointer to the HW structure
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+static int mucse_poll_for_msg(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ int val;
+
+ return read_poll_timeout(mucse_check_for_msg_pf,
+ val, !val, mbx->delay_us,
+ mbx->timeout_us,
+ false, hw);
+}
+
+/**
+ * mucse_poll_and_read_mbx - Wait for message notification and receive message
+ * @hw: pointer to the HW structure
+ * @msg: the message buffer
+ * @size: length of buffer
+ *
+ * Return: 0 if it successfully received a message notification and
+ * copied it into the receive buffer, negative errno on failure
+ **/
+int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
+{
+ int err;
+
+ err = mucse_poll_for_msg(hw);
+ if (err)
+ return err;
+
+ return mucse_read_mbx_pf(hw, msg, size);
+}
+
+/**
+ * mucse_mbx_get_fwack - Read fw ack from reg
+ * @mbx: pointer to the MBX structure
+ *
+ * Return: the fwack value
+ **/
+static u16 mucse_mbx_get_fwack(struct mucse_mbx_info *mbx)
+{
+ u32 val = mbx_data_rd32(mbx, MUCSE_MBX_FW2PF_CNT);
+
+ return FIELD_GET(GENMASK_U32(31, 16), val);
+}
+
+/**
+ * mucse_mbx_inc_pf_req - Increase req
+ * @hw: pointer to the HW structure
+ *
+ * mucse_mbx_inc_pf_req reads pf_req from hw, then writes
+ * new value back after increase
+ **/
+static void mucse_mbx_inc_pf_req(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u16 req;
+ u32 val;
+
+ val = mbx_data_rd32(mbx, MUCSE_MBX_PF2FW_CNT);
+ req = FIELD_GET(GENMASK_U32(15, 0), val);
+ req++;
+ val &= ~GENMASK_U32(15, 0);
+ val |= FIELD_PREP(GENMASK_U32(15, 0), req);
+ mbx_data_wr32(mbx, MUCSE_MBX_PF2FW_CNT, val);
+}
+
+/**
+ * mucse_write_mbx_pf - Place a message in the mailbox
+ * @hw: pointer to the HW structure
+ * @msg: the message buffer
+ * @size: length of buffer
+ *
+ * Return: 0 if it successfully copied message into the buffer,
+ * negative errno on failure
+ **/
+static int mucse_write_mbx_pf(struct mucse_hw *hw, u32 *msg, u16 size)
+{
+ const int size_in_words = size / sizeof(u32);
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ int err;
+
+ err = mucse_obtain_mbx_lock_pf(hw);
+ if (err)
+ return err;
+
+ for (int i = 0; i < size_in_words; i++)
+ mbx_data_wr32(mbx, MUCSE_MBX_FWPF_SHM + i * 4, msg[i]);
+
+ /* flush acks as we are overwriting the message buffer */
+ hw->mbx.fw_ack = mucse_mbx_get_fwack(mbx);
+ mucse_mbx_inc_pf_req(hw);
+ mucse_release_mbx_lock_pf(hw, true);
+
+ return 0;
+}
+
+/**
+ * mucse_check_for_ack_pf - Check to see if the fw has ACKed
+ * @hw: pointer to the HW structure
+ *
+ * Return: 0 if the fw has set the Status bit or else -EIO
+ **/
+static int mucse_check_for_ack_pf(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u16 fw_ack;
+
+ fw_ack = mucse_mbx_get_fwack(mbx);
+ /* chip's register is reset to 0 when rc send reset
+ * mbx command. Return -EIO if in this state, others
+ * fw_ack == hw->mbx.fw_ack means no new ack.
+ **/
+ if (fw_ack == 0 || fw_ack == hw->mbx.fw_ack)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * mucse_poll_for_ack - Wait for message acknowledgment
+ * @hw: pointer to the HW structure
+ *
+ * Return: 0 if it successfully received a message acknowledgment,
+ * else negative errno
+ **/
+static int mucse_poll_for_ack(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ int val;
+
+ return read_poll_timeout(mucse_check_for_ack_pf,
+ val, !val, mbx->delay_us,
+ mbx->timeout_us,
+ false, hw);
+}
+
+/**
+ * mucse_write_and_wait_ack_mbx - Write a message to the mailbox, wait for ack
+ * @hw: pointer to the HW structure
+ * @msg: the message buffer
+ * @size: length of buffer
+ *
+ * Return: 0 if it successfully copied message into the buffer and
+ * received an ack to that message within delay * timeout_cnt period
+ **/
+int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size)
+{
+ int err;
+
+ err = mucse_write_mbx_pf(hw, msg, size);
+ if (err)
+ return err;
+
+ return mucse_poll_for_ack(hw);
+}
+
+/**
+ * mucse_mbx_reset - Reset mbx info, sync info from regs
+ * @hw: pointer to the HW structure
+ *
+ * mucse_mbx_reset resets all mbx variables to default.
+ **/
+static void mucse_mbx_reset(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+ u32 val;
+
+ val = mbx_data_rd32(mbx, MUCSE_MBX_FW2PF_CNT);
+ hw->mbx.fw_req = FIELD_GET(GENMASK_U32(15, 0), val);
+ hw->mbx.fw_ack = FIELD_GET(GENMASK_U32(31, 16), val);
+ mbx_ctrl_wr32(mbx, MUCSE_MBX_PF2FW_CTRL(mbx), 0);
+ mbx_ctrl_wr32(mbx, MUCSE_MBX_FWPF_MASK(mbx), GENMASK_U32(31, 16));
+}
+
+/**
+ * mucse_init_mbx_params_pf - Set initial values for pf mailbox
+ * @hw: pointer to the HW structure
+ *
+ * Initializes the hw->mbx struct to correct values for pf mailbox
+ */
+void mucse_init_mbx_params_pf(struct mucse_hw *hw)
+{
+ struct mucse_mbx_info *mbx = &hw->mbx;
+
+ mbx->delay_us = 100;
+ mbx->timeout_us = 4 * USEC_PER_SEC;
+ mutex_init(&mbx->lock);
+ mucse_mbx_reset(hw);
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
new file mode 100644
index 000000000000..e6fcc8d1d3ca
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#ifndef _RNPGBE_MBX_H
+#define _RNPGBE_MBX_H
+
+#include "rnpgbe.h"
+
+#define MUCSE_MBX_FW2PF_CNT 0
+#define MUCSE_MBX_PF2FW_CNT 4
+#define MUCSE_MBX_FWPF_SHM 8
+#define MUCSE_MBX_PF2FW_CTRL(mbx) ((mbx)->pf2fw_mbx_ctrl)
+#define MUCSE_MBX_FWPF_MASK(mbx) ((mbx)->fwpf_mbx_mask)
+#define MUCSE_MBX_REQ BIT(0) /* Request a req to mailbox */
+#define MUCSE_MBX_PFU BIT(3) /* PF owns the mailbox buffer */
+
+int mucse_write_and_wait_ack_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
+void mucse_init_mbx_params_pf(struct mucse_hw *hw);
+int mucse_poll_and_read_mbx(struct mucse_hw *hw, u32 *msg, u16 size);
+#endif /* _RNPGBE_MBX_H */
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
new file mode 100644
index 000000000000..8c8bd5e8e1db
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#include <linux/if_ether.h>
+#include <linux/bitfield.h>
+
+#include "rnpgbe.h"
+#include "rnpgbe_mbx.h"
+#include "rnpgbe_mbx_fw.h"
+
+/**
+ * mucse_fw_send_cmd_wait_resp - Send cmd req and wait for response
+ * @hw: pointer to the HW structure
+ * @req: pointer to the cmd req structure
+ * @reply: pointer to the fw reply structure
+ *
+ * mucse_fw_send_cmd_wait_resp sends req to pf-fw mailbox and wait
+ * reply from fw.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+static int mucse_fw_send_cmd_wait_resp(struct mucse_hw *hw,
+ struct mbx_fw_cmd_req *req,
+ struct mbx_fw_cmd_reply *reply)
+{
+ int len = le16_to_cpu(req->datalen);
+ int retry_cnt = 3;
+ int err;
+
+ mutex_lock(&hw->mbx.lock);
+ err = mucse_write_and_wait_ack_mbx(hw, (u32 *)req, len);
+ if (err)
+ goto out;
+ do {
+ err = mucse_poll_and_read_mbx(hw, (u32 *)reply,
+ sizeof(*reply));
+ if (err)
+ goto out;
+ /* mucse_write_and_wait_ack_mbx return 0 means fw has
+ * received request, wait for the expect opcode
+ * reply with 'retry_cnt' times.
+ */
+ } while (--retry_cnt >= 0 && reply->opcode != req->opcode);
+out:
+ mutex_unlock(&hw->mbx.lock);
+ if (!err && retry_cnt < 0)
+ return -ETIMEDOUT;
+ if (!err && reply->error_code)
+ return -EIO;
+
+ return err;
+}
+
+/**
+ * mucse_mbx_get_info - Get hw info from fw
+ * @hw: pointer to the HW structure
+ *
+ * mucse_mbx_get_info tries to get hw info from hw.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+static int mucse_mbx_get_info(struct mucse_hw *hw)
+{
+ struct mbx_fw_cmd_req req = {
+ .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
+ .opcode = cpu_to_le16(GET_HW_INFO),
+ };
+ struct mbx_fw_cmd_reply reply = {};
+ int err;
+
+ err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
+ if (!err)
+ hw->pfvfnum = FIELD_GET(GENMASK_U16(7, 0),
+ le16_to_cpu(reply.hw_info.pfnum));
+
+ return err;
+}
+
+/**
+ * mucse_mbx_sync_fw - Try to sync with fw
+ * @hw: pointer to the HW structure
+ *
+ * mucse_mbx_sync_fw tries to sync with fw. It is only called in
+ * probe. Nothing (register network) todo if failed.
+ * Try more times to do sync.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int mucse_mbx_sync_fw(struct mucse_hw *hw)
+{
+ int try_cnt = 3;
+ int err;
+
+ do {
+ err = mucse_mbx_get_info(hw);
+ } while (err == -ETIMEDOUT && try_cnt--);
+
+ return err;
+}
+
+/**
+ * mucse_mbx_powerup - Echo fw to powerup
+ * @hw: pointer to the HW structure
+ * @is_powerup: true for powerup, false for powerdown
+ *
+ * mucse_mbx_powerup echo fw to change working frequency
+ * to normal after received true, and reduce working frequency
+ * if false.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup)
+{
+ struct mbx_fw_cmd_req req = {
+ .datalen = cpu_to_le16(sizeof(req.powerup) +
+ MUCSE_MBX_REQ_HDR_LEN),
+ .opcode = cpu_to_le16(POWER_UP),
+ .powerup = {
+ /* fw needs this to reply correct cmd */
+ .version = cpu_to_le32(GENMASK_U32(31, 0)),
+ .status = cpu_to_le32(is_powerup ? 1 : 0),
+ },
+ };
+ int len, err;
+
+ len = le16_to_cpu(req.datalen);
+ mutex_lock(&hw->mbx.lock);
+ err = mucse_write_and_wait_ack_mbx(hw, (u32 *)&req, len);
+ mutex_unlock(&hw->mbx.lock);
+
+ return err;
+}
+
+/**
+ * mucse_mbx_reset_hw - Posts a mbx req to reset hw
+ * @hw: pointer to the HW structure
+ *
+ * mucse_mbx_reset_hw posts a mbx req to firmware to reset hw.
+ * We use mucse_fw_send_cmd_wait_resp to wait hw reset ok.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int mucse_mbx_reset_hw(struct mucse_hw *hw)
+{
+ struct mbx_fw_cmd_req req = {
+ .datalen = cpu_to_le16(MUCSE_MBX_REQ_HDR_LEN),
+ .opcode = cpu_to_le16(RESET_HW),
+ };
+ struct mbx_fw_cmd_reply reply = {};
+
+ return mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
+}
+
+/**
+ * mucse_mbx_get_macaddr - Posts a mbx req to request macaddr
+ * @hw: pointer to the HW structure
+ * @pfvfnum: index of pf/vf num
+ * @mac_addr: pointer to store mac_addr
+ * @port: port index
+ *
+ * mucse_mbx_get_macaddr posts a mbx req to firmware to get mac_addr.
+ *
+ * Return: 0 on success, negative errno on failure
+ **/
+int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
+ u8 *mac_addr,
+ int port)
+{
+ struct mbx_fw_cmd_req req = {
+ .datalen = cpu_to_le16(sizeof(req.get_mac_addr) +
+ MUCSE_MBX_REQ_HDR_LEN),
+ .opcode = cpu_to_le16(GET_MAC_ADDRESS),
+ .get_mac_addr = {
+ .port_mask = cpu_to_le32(BIT(port)),
+ .pfvf_num = cpu_to_le32(pfvfnum),
+ },
+ };
+ struct mbx_fw_cmd_reply reply = {};
+ int err;
+
+ err = mucse_fw_send_cmd_wait_resp(hw, &req, &reply);
+ if (err)
+ return err;
+
+ if (le32_to_cpu(reply.mac_addr.ports) & BIT(port))
+ memcpy(mac_addr, reply.mac_addr.addrs[port].mac, ETH_ALEN);
+ else
+ return -ENODATA;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
new file mode 100644
index 000000000000..fb24fc12b613
--- /dev/null
+++ b/drivers/net/ethernet/mucse/rnpgbe/rnpgbe_mbx_fw.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2020 - 2025 Mucse Corporation. */
+
+#ifndef _RNPGBE_MBX_FW_H
+#define _RNPGBE_MBX_FW_H
+
+#include <linux/types.h>
+
+#include "rnpgbe.h"
+
+#define MUCSE_MBX_REQ_HDR_LEN 24
+
+enum MUCSE_FW_CMD {
+ GET_HW_INFO = 0x0601,
+ GET_MAC_ADDRESS = 0x0602,
+ RESET_HW = 0x0603,
+ POWER_UP = 0x0803,
+};
+
+struct mucse_hw_info {
+ u8 link_stat;
+ u8 port_mask;
+ __le32 speed;
+ __le16 phy_type;
+ __le16 nic_mode;
+ __le16 pfnum;
+ __le32 fw_version;
+ __le32 axi_mhz;
+ union {
+ u8 port_id[4];
+ __le32 port_ids;
+ };
+ __le32 bd_uid;
+ __le32 phy_id;
+ __le32 wol_status;
+ __le32 ext_info;
+} __packed;
+
+struct mbx_fw_cmd_req {
+ __le16 flags;
+ __le16 opcode;
+ __le16 datalen;
+ __le16 ret_value;
+ __le32 cookie_lo;
+ __le32 cookie_hi;
+ __le32 reply_lo;
+ __le32 reply_hi;
+ union {
+ u8 data[32];
+ struct {
+ __le32 version;
+ __le32 status;
+ } powerup;
+ struct {
+ __le32 port_mask;
+ __le32 pfvf_num;
+ } get_mac_addr;
+ };
+} __packed;
+
+struct mbx_fw_cmd_reply {
+ __le16 flags;
+ __le16 opcode;
+ __le16 error_code;
+ __le16 datalen;
+ __le32 cookie_lo;
+ __le32 cookie_hi;
+ union {
+ u8 data[40];
+ struct mac_addr {
+ __le32 ports;
+ struct _addr {
+ /* for macaddr:01:02:03:04:05:06
+ * mac-hi=0x01020304 mac-lo=0x05060000
+ */
+ u8 mac[8];
+ } addrs[4];
+ } mac_addr;
+ struct mucse_hw_info hw_info;
+ };
+} __packed;
+
+int mucse_mbx_sync_fw(struct mucse_hw *hw);
+int mucse_mbx_powerup(struct mucse_hw *hw, bool is_powerup);
+int mucse_mbx_reset_hw(struct mucse_hw *hw);
+int mucse_mbx_get_macaddr(struct mucse_hw *hw, int pfvfnum,
+ u8 *mac_addr, int port);
+#endif /* _RNPGBE_MBX_FW_H */
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index e611ff7fa3fa..7be30a8df268 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -3416,10 +3416,6 @@ static void myri10ge_watchdog(struct work_struct *work)
* nic was resumed from power saving mode.
*/
pci_restore_state(mgp->pdev);
-
- /* save state again for accounting reasons */
- pci_save_state(mgp->pdev);
-
} else {
/* if we get back -1's from our slot, perhaps somebody
* powered off our card. Don't try to reset it in
diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c
index 5026b0263d43..1e55ccb4822b 100644
--- a/drivers/net/ethernet/neterion/s2io.c
+++ b/drivers/net/ethernet/neterion/s2io.c
@@ -3425,7 +3425,6 @@ static void s2io_reset(struct s2io_nic *sp)
/* Restore the PCI state saved during initialization. */
pci_restore_state(sp->pdev);
- pci_save_state(sp->pdev);
pci_read_config_word(sp->pdev, 0x2, &val16);
if (check_pci_device_id(val16) != (u16)PCI_ANY_ID)
break;
diff --git a/drivers/net/ethernet/netronome/nfp/devlink_param.c b/drivers/net/ethernet/netronome/nfp/devlink_param.c
index 0e1a3800f371..85e3b19e6165 100644
--- a/drivers/net/ethernet/netronome/nfp/devlink_param.c
+++ b/drivers/net/ethernet/netronome/nfp/devlink_param.c
@@ -81,7 +81,8 @@ static const struct nfp_devlink_param_u8_arg nfp_devlink_u8_args[] = {
static int
nfp_devlink_param_u8_get(struct devlink *devlink, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
const struct nfp_devlink_param_u8_arg *arg;
struct nfp_pf *pf = devlink_priv(devlink);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 132626a3f9f7..9ef72f294117 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -2557,14 +2557,16 @@ nfp_net_alloc(struct pci_dev *pdev, const struct nfp_dev_info *dev_info,
err = nfp_net_tlv_caps_parse(&nn->pdev->dev, nn->dp.ctrl_bar,
&nn->tlv_caps);
if (err)
- goto err_free_nn;
+ goto err_free_xsk_pools;
err = nfp_ccm_mbox_alloc(nn);
if (err)
- goto err_free_nn;
+ goto err_free_xsk_pools;
return nn;
+err_free_xsk_pools:
+ kfree(nn->dp.xsk_pools);
err_free_nn:
if (nn->dp.netdev)
free_netdev(nn->dp.netdev);
diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
index e5a6f59af0b6..62f05f4569b1 100644
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c
@@ -198,23 +198,21 @@ pch_tx_timestamp(struct pch_gbe_adapter *adapter, struct sk_buff *skb)
pch_ch_event_write(pdev, TX_SNAPSHOT_LOCKED);
}
-static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+static int pch_gbe_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config cfg;
struct pch_gbe_adapter *adapter = netdev_priv(netdev);
struct pci_dev *pdev;
u8 station[20];
- if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
- return -EFAULT;
-
/* Get ieee1588's dev information */
pdev = adapter->ptp_pdev;
- if (cfg.tx_type != HWTSTAMP_TX_OFF && cfg.tx_type != HWTSTAMP_TX_ON)
+ if (cfg->tx_type != HWTSTAMP_TX_OFF && cfg->tx_type != HWTSTAMP_TX_ON)
return -ERANGE;
- switch (cfg.rx_filter) {
+ switch (cfg->rx_filter) {
case HWTSTAMP_FILTER_NONE:
adapter->hwts_rx_en = 0;
break;
@@ -223,17 +221,17 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
pch_ch_control_write(pdev, SLAVE_MODE | CAP_MODE0);
break;
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- adapter->hwts_rx_en = 1;
+ adapter->hwts_rx_en = cfg->rx_filter;
pch_ch_control_write(pdev, MASTER_MODE | CAP_MODE0);
break;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
- adapter->hwts_rx_en = 1;
+ adapter->hwts_rx_en = cfg->rx_filter;
pch_ch_control_write(pdev, V2_MODE | CAP_MODE2);
strcpy(station, PTP_L4_MULTICAST_SA);
pch_set_station_address(station, pdev);
break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- adapter->hwts_rx_en = 1;
+ adapter->hwts_rx_en = cfg->rx_filter;
pch_ch_control_write(pdev, V2_MODE | CAP_MODE2);
strcpy(station, PTP_L2_MULTICAST_SA);
pch_set_station_address(station, pdev);
@@ -242,12 +240,23 @@ static int hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
return -ERANGE;
}
- adapter->hwts_tx_en = cfg.tx_type == HWTSTAMP_TX_ON;
+ adapter->hwts_tx_en = cfg->tx_type == HWTSTAMP_TX_ON;
/* Clear out any old time stamps. */
pch_ch_event_write(pdev, TX_SNAPSHOT_LOCKED | RX_SNAPSHOT_LOCKED);
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+ return 0;
+}
+
+static int pch_gbe_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *cfg)
+{
+ struct pch_gbe_adapter *adapter = netdev_priv(netdev);
+
+ cfg->tx_type = adapter->hwts_tx_en ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+ cfg->rx_filter = adapter->hwts_rx_en;
+
+ return 0;
}
static inline void pch_gbe_mac_load_mac_addr(struct pch_gbe_hw *hw)
@@ -2234,9 +2243,6 @@ static int pch_gbe_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
netdev_dbg(netdev, "cmd : 0x%04x\n", cmd);
- if (cmd == SIOCSHWTSTAMP)
- return hwtstamp_ioctl(netdev, ifr, cmd);
-
return generic_mii_ioctl(&adapter->mii, if_mii(ifr), cmd, NULL);
}
@@ -2328,6 +2334,8 @@ static const struct net_device_ops pch_gbe_netdev_ops = {
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_poll_controller = pch_gbe_netpoll,
#endif
+ .ndo_hwtstamp_get = pch_gbe_hwtstamp_get,
+ .ndo_hwtstamp_set = pch_gbe_hwtstamp_set,
};
static pci_ers_result_t pch_gbe_io_error_detected(struct pci_dev *pdev,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index b28966ae50c2..058eea86e141 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -2335,20 +2335,6 @@ static int ionic_stop(struct net_device *netdev)
return 0;
}
-static int ionic_eth_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
-{
- struct ionic_lif *lif = netdev_priv(netdev);
-
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return ionic_lif_hwstamp_set(lif, ifr);
- case SIOCGHWTSTAMP:
- return ionic_lif_hwstamp_get(lif, ifr);
- default:
- return -EOPNOTSUPP;
- }
-}
-
static int ionic_get_vf_config(struct net_device *netdev,
int vf, struct ifla_vf_info *ivf)
{
@@ -2812,7 +2798,6 @@ static int ionic_xdp(struct net_device *netdev, struct netdev_bpf *bpf)
static const struct net_device_ops ionic_netdev_ops = {
.ndo_open = ionic_open,
.ndo_stop = ionic_stop,
- .ndo_eth_ioctl = ionic_eth_ioctl,
.ndo_start_xmit = ionic_start_xmit,
.ndo_bpf = ionic_xdp,
.ndo_xdp_xmit = ionic_xdp_xmit,
@@ -2833,6 +2818,8 @@ static const struct net_device_ops ionic_netdev_ops = {
.ndo_get_vf_config = ionic_get_vf_config,
.ndo_set_vf_link_state = ionic_set_vf_link_state,
.ndo_get_vf_stats = ionic_get_vf_stats,
+ .ndo_hwtstamp_get = ionic_hwstamp_get,
+ .ndo_hwtstamp_set = ionic_hwstamp_set,
};
static int ionic_cmb_reconfig(struct ionic_lif *lif,
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index 43bdd0fb8733..8e10f66dc50e 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -6,7 +6,7 @@
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
-#include <uapi/linux/net_tstamp.h>
+#include <linux/net_tstamp.h>
#include <linux/dim.h>
#include <linux/pci.h>
#include "ionic_rx_filter.h"
@@ -254,7 +254,7 @@ struct ionic_phc {
struct timecounter tc;
struct mutex config_lock; /* lock for ts_config */
- struct hwtstamp_config ts_config;
+ struct kernel_hwtstamp_config ts_config;
u64 ts_config_rx_filt;
u32 ts_config_tx_mode;
@@ -362,8 +362,11 @@ int ionic_lif_size(struct ionic *ionic);
#if IS_ENABLED(CONFIG_PTP_1588_CLOCK)
void ionic_lif_hwstamp_replay(struct ionic_lif *lif);
void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif);
-int ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr);
-int ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr);
+int ionic_hwstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
+int ionic_hwstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config);
ktime_t ionic_lif_phc_ktime(struct ionic_lif *lif, u64 counter);
void ionic_lif_register_phc(struct ionic_lif *lif);
void ionic_lif_unregister_phc(struct ionic_lif *lif);
@@ -373,12 +376,15 @@ void ionic_lif_free_phc(struct ionic_lif *lif);
static inline void ionic_lif_hwstamp_replay(struct ionic_lif *lif) {}
static inline void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif) {}
-static inline int ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr)
+static inline int ionic_hwstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
-static inline int ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr)
+static inline int ionic_hwstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_phc.c b/drivers/net/ethernet/pensando/ionic/ionic_phc.c
index 9f5c81d44f99..05b44fc482f8 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_phc.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_phc.c
@@ -65,11 +65,12 @@ static u64 ionic_hwstamp_rx_filt(int config_rx_filter)
}
static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif,
- struct hwtstamp_config *new_ts)
+ struct kernel_hwtstamp_config *new_ts,
+ struct netlink_ext_ack *extack)
{
+ struct kernel_hwtstamp_config *config;
+ struct kernel_hwtstamp_config ts = {};
struct ionic *ionic = lif->ionic;
- struct hwtstamp_config *config;
- struct hwtstamp_config ts;
int tx_mode = 0;
u64 rx_filt = 0;
int err, err2;
@@ -99,12 +100,16 @@ static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif,
tx_mode = ionic_hwstamp_tx_mode(config->tx_type);
if (tx_mode < 0) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "TX time stamping mode isn't supported");
err = tx_mode;
goto err_queues;
}
mask = cpu_to_le64(BIT_ULL(tx_mode));
if ((ionic->ident.lif.eth.hwstamp_tx_modes & mask) != mask) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "TX time stamping mode isn't supported");
err = -ERANGE;
goto err_queues;
}
@@ -124,32 +129,47 @@ static int ionic_lif_hwstamp_set_ts_config(struct ionic_lif *lif,
if (tx_mode) {
err = ionic_lif_create_hwstamp_txq(lif);
- if (err)
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Error creating TX timestamp queue");
goto err_queues;
+ }
}
if (rx_filt) {
err = ionic_lif_create_hwstamp_rxq(lif);
- if (err)
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Error creating RX timestamp queue");
goto err_queues;
+ }
}
if (tx_mode != lif->phc->ts_config_tx_mode) {
err = ionic_lif_set_hwstamp_txmode(lif, tx_mode);
- if (err)
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Error enabling TX timestamp mode");
goto err_txmode;
+ }
}
if (rx_filt != lif->phc->ts_config_rx_filt) {
err = ionic_lif_set_hwstamp_rxfilt(lif, rx_filt);
- if (err)
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Error enabling RX timestamp mode");
goto err_rxfilt;
+ }
}
if (rx_all != (lif->phc->ts_config.rx_filter == HWTSTAMP_FILTER_ALL)) {
err = ionic_lif_config_hwstamp_rxq_all(lif, rx_all);
- if (err)
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Error enabling RX timestamp mode");
goto err_rxall;
+ }
}
memcpy(&lif->phc->ts_config, config, sizeof(*config));
@@ -183,28 +203,24 @@ err_queues:
return err;
}
-int ionic_lif_hwstamp_set(struct ionic_lif *lif, struct ifreq *ifr)
+int ionic_hwstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config config;
+ struct ionic_lif *lif = netdev_priv(netdev);
int err;
if (!lif->phc || !lif->phc->ptp)
return -EOPNOTSUPP;
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
-
mutex_lock(&lif->queue_lock);
- err = ionic_lif_hwstamp_set_ts_config(lif, &config);
+ err = ionic_lif_hwstamp_set_ts_config(lif, config, extack);
mutex_unlock(&lif->queue_lock);
if (err) {
netdev_info(lif->netdev, "hwstamp set failed: %d\n", err);
return err;
}
- if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
- return -EFAULT;
-
return 0;
}
@@ -216,7 +232,7 @@ void ionic_lif_hwstamp_replay(struct ionic_lif *lif)
return;
mutex_lock(&lif->queue_lock);
- err = ionic_lif_hwstamp_set_ts_config(lif, NULL);
+ err = ionic_lif_hwstamp_set_ts_config(lif, NULL, NULL);
mutex_unlock(&lif->queue_lock);
if (err)
netdev_info(lif->netdev, "hwstamp replay failed: %d\n", err);
@@ -246,19 +262,18 @@ void ionic_lif_hwstamp_recreate_queues(struct ionic_lif *lif)
mutex_unlock(&lif->phc->config_lock);
}
-int ionic_lif_hwstamp_get(struct ionic_lif *lif, struct ifreq *ifr)
+int ionic_hwstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
{
- struct hwtstamp_config config;
+ struct ionic_lif *lif = netdev_priv(netdev);
if (!lif->phc || !lif->phc->ptp)
return -EOPNOTSUPP;
mutex_lock(&lif->phc->config_lock);
- memcpy(&config, &lif->phc->ts_config, sizeof(config));
+ memcpy(config, &lif->phc->ts_config, sizeof(*config));
mutex_unlock(&lif->phc->config_lock);
- if (copy_to_user(ifr->ifr_data, &config, sizeof(config)))
- return -EFAULT;
return 0;
}
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
index d10b58ebf603..301ebee2fdc5 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
@@ -29,6 +29,10 @@ static void ionic_tx_clean(struct ionic_queue *q,
static inline void ionic_txq_post(struct ionic_queue *q, bool ring_dbell)
{
+ /* Ensure TX descriptor writes reach memory before NIC reads them.
+ * Prevents device from fetching stale descriptors.
+ */
+ dma_wmb();
ionic_q_post(q, ring_dbell);
}
@@ -1444,19 +1448,6 @@ static int ionic_tx_tso(struct net_device *netdev, struct ionic_queue *q,
bool encap;
int err;
- desc_info = &q->tx_info[q->head_idx];
-
- if (unlikely(ionic_tx_map_skb(q, skb, desc_info)))
- return -EIO;
-
- len = skb->len;
- mss = skb_shinfo(skb)->gso_size;
- outer_csum = (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE |
- SKB_GSO_GRE_CSUM |
- SKB_GSO_IPXIP4 |
- SKB_GSO_IPXIP6 |
- SKB_GSO_UDP_TUNNEL |
- SKB_GSO_UDP_TUNNEL_CSUM));
has_vlan = !!skb_vlan_tag_present(skb);
vlan_tci = skb_vlan_tag_get(skb);
encap = skb->encapsulation;
@@ -1470,12 +1461,21 @@ static int ionic_tx_tso(struct net_device *netdev, struct ionic_queue *q,
err = ionic_tx_tcp_inner_pseudo_csum(skb);
else
err = ionic_tx_tcp_pseudo_csum(skb);
- if (unlikely(err)) {
- /* clean up mapping from ionic_tx_map_skb */
- ionic_tx_desc_unmap_bufs(q, desc_info);
+ if (unlikely(err))
return err;
- }
+ desc_info = &q->tx_info[q->head_idx];
+ if (unlikely(ionic_tx_map_skb(q, skb, desc_info)))
+ return -EIO;
+
+ len = skb->len;
+ mss = skb_shinfo(skb)->gso_size;
+ outer_csum = (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE |
+ SKB_GSO_GRE_CSUM |
+ SKB_GSO_IPXIP4 |
+ SKB_GSO_IPXIP6 |
+ SKB_GSO_UDP_TUNNEL |
+ SKB_GSO_UDP_TUNNEL_CSUM));
if (encap)
hdrlen = skb_inner_tcp_all_headers(skb);
else
diff --git a/drivers/net/ethernet/qlogic/qed/qed_devlink.c b/drivers/net/ethernet/qlogic/qed/qed_devlink.c
index 94c5689b5abd..0c5278c0598c 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_devlink.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_devlink.c
@@ -121,7 +121,8 @@ void qed_fw_reporters_destroy(struct devlink *devlink)
}
static int qed_dl_param_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct qed_devlink *qed_dl = devlink_priv(dl);
struct qed_dev *cdev;
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index 847fa62c80df..e338bfc8b7b2 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -4,6 +4,7 @@
* Copyright (c) 2019-2020 Marvell International Ltd.
*/
+#include <linux/array_size.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
@@ -960,7 +961,7 @@ static inline void qede_tpa_cont(struct qede_dev *edev,
{
int i;
- for (i = 0; cqe->len_list[i]; i++)
+ for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++)
qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
le16_to_cpu(cqe->len_list[i]));
@@ -985,7 +986,7 @@ static int qede_tpa_end(struct qede_dev *edev,
dma_unmap_page(rxq->dev, tpa_info->buffer.mapping,
PAGE_SIZE, rxq->data_direction);
- for (i = 0; cqe->len_list[i]; i++)
+ for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++)
qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
le16_to_cpu(cqe->len_list[i]));
if (unlikely(i > 1))
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
index b5d744d2586f..66ab1b9d65a1 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_main.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_main.c
@@ -506,25 +506,6 @@ static int qede_set_vf_trust(struct net_device *dev, int vfidx, bool setting)
}
#endif
-static int qede_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct qede_dev *edev = netdev_priv(dev);
-
- if (!netif_running(dev))
- return -EAGAIN;
-
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return qede_ptp_hw_ts(edev, ifr);
- default:
- DP_VERBOSE(edev, QED_MSG_DEBUG,
- "default IOCTL cmd 0x%x\n", cmd);
- return -EOPNOTSUPP;
- }
-
- return 0;
-}
-
static void qede_fp_sb_dump(struct qede_dev *edev, struct qede_fastpath *fp)
{
char *p_sb = (char *)fp->sb_info->sb_virt;
@@ -717,7 +698,6 @@ static const struct net_device_ops qede_netdev_ops = {
.ndo_set_mac_address = qede_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_change_mtu = qede_change_mtu,
- .ndo_eth_ioctl = qede_ioctl,
.ndo_tx_timeout = qede_tx_timeout,
#ifdef CONFIG_QED_SRIOV
.ndo_set_vf_mac = qede_set_vf_mac,
@@ -742,6 +722,8 @@ static const struct net_device_ops qede_netdev_ops = {
#endif
.ndo_xdp_xmit = qede_xdp_transmit,
.ndo_setup_tc = qede_setup_tc_offload,
+ .ndo_hwtstamp_get = qede_hwtstamp_get,
+ .ndo_hwtstamp_set = qede_hwtstamp_set,
};
static const struct net_device_ops qede_netdev_vf_ops = {
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.c b/drivers/net/ethernet/qlogic/qede/qede_ptp.c
index a38f1e72c62b..d351be5fbda1 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ptp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.c
@@ -199,18 +199,15 @@ static u64 qede_ptp_read_cc(struct cyclecounter *cc)
return phc_cycles;
}
-static int qede_ptp_cfg_filters(struct qede_dev *edev)
+static void qede_ptp_cfg_filters(struct qede_dev *edev)
{
enum qed_ptp_hwtstamp_tx_type tx_type = QED_PTP_HWTSTAMP_TX_ON;
enum qed_ptp_filter_type rx_filter = QED_PTP_FILTER_NONE;
struct qede_ptp *ptp = edev->ptp;
- if (!ptp)
- return -EIO;
-
if (!ptp->hw_ts_ioctl_called) {
DP_INFO(edev, "TS IOCTL not called\n");
- return 0;
+ return;
}
switch (ptp->tx_type) {
@@ -223,11 +220,6 @@ static int qede_ptp_cfg_filters(struct qede_dev *edev)
clear_bit(QEDE_FLAGS_TX_TIMESTAMPING_EN, &edev->flags);
tx_type = QED_PTP_HWTSTAMP_TX_OFF;
break;
-
- case HWTSTAMP_TX_ONESTEP_SYNC:
- case HWTSTAMP_TX_ONESTEP_P2P:
- DP_ERR(edev, "One-step timestamping is not supported\n");
- return -ERANGE;
}
spin_lock_bh(&ptp->lock);
@@ -286,39 +278,65 @@ static int qede_ptp_cfg_filters(struct qede_dev *edev)
ptp->ops->cfg_filters(edev->cdev, rx_filter, tx_type);
spin_unlock_bh(&ptp->lock);
-
- return 0;
}
-int qede_ptp_hw_ts(struct qede_dev *edev, struct ifreq *ifr)
+int qede_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
- struct hwtstamp_config config;
+ struct qede_dev *edev = netdev_priv(netdev);
struct qede_ptp *ptp;
- int rc;
+
+ if (!netif_running(netdev)) {
+ NL_SET_ERR_MSG_MOD(extack, "Device is down");
+ return -EAGAIN;
+ }
ptp = edev->ptp;
- if (!ptp)
+ if (!ptp) {
+ NL_SET_ERR_MSG_MOD(extack, "HW timestamping is not supported");
return -EIO;
-
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
+ }
DP_VERBOSE(edev, QED_MSG_DEBUG,
- "HWTSTAMP IOCTL: Requested tx_type = %d, requested rx_filters = %d\n",
- config.tx_type, config.rx_filter);
+ "HWTSTAMP SET: Requested tx_type = %d, requested rx_filters = %d\n",
+ config->tx_type, config->rx_filter);
+
+ switch (config->tx_type) {
+ case HWTSTAMP_TX_ON:
+ case HWTSTAMP_TX_OFF:
+ break;
+ default:
+ NL_SET_ERR_MSG_MOD(extack,
+ "One-step timestamping is not supported");
+ return -ERANGE;
+ }
ptp->hw_ts_ioctl_called = 1;
- ptp->tx_type = config.tx_type;
- ptp->rx_filter = config.rx_filter;
+ ptp->tx_type = config->tx_type;
+ ptp->rx_filter = config->rx_filter;
- rc = qede_ptp_cfg_filters(edev);
- if (rc)
- return rc;
+ qede_ptp_cfg_filters(edev);
+
+ config->rx_filter = ptp->rx_filter;
+
+ return 0;
+}
- config.rx_filter = ptp->rx_filter;
+int qede_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct qede_dev *edev = netdev_priv(netdev);
+ struct qede_ptp *ptp;
- return copy_to_user(ifr->ifr_data, &config,
- sizeof(config)) ? -EFAULT : 0;
+ ptp = edev->ptp;
+ if (!ptp)
+ return -EIO;
+
+ config->tx_type = ptp->tx_type;
+ config->rx_filter = ptp->rx_filter;
+
+ return 0;
}
int qede_ptp_get_ts_info(struct qede_dev *edev, struct kernel_ethtool_ts_info *info)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ptp.h b/drivers/net/ethernet/qlogic/qede/qede_ptp.h
index adafc894797e..88f168395812 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ptp.h
+++ b/drivers/net/ethernet/qlogic/qede/qede_ptp.h
@@ -14,7 +14,11 @@
void qede_ptp_rx_ts(struct qede_dev *edev, struct sk_buff *skb);
void qede_ptp_tx_ts(struct qede_dev *edev, struct sk_buff *skb);
-int qede_ptp_hw_ts(struct qede_dev *edev, struct ifreq *req);
+int qede_hwtstamp_get(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config);
+int qede_hwtstamp_set(struct net_device *netdev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
void qede_ptp_disable(struct qede_dev *edev);
int qede_ptp_enable(struct qede_dev *edev);
int qede_ptp_get_ts_info(struct qede_dev *edev, struct kernel_ethtool_ts_info *ts);
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index d18734fe12e4..405e91eb3141 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -57,7 +57,9 @@
#define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw"
#define FIRMWARE_8125D_1 "rtl_nic/rtl8125d-1.fw"
#define FIRMWARE_8125D_2 "rtl_nic/rtl8125d-2.fw"
+#define FIRMWARE_8125K_1 "rtl_nic/rtl8125k-1.fw"
#define FIRMWARE_8125BP_2 "rtl_nic/rtl8125bp-2.fw"
+#define FIRMWARE_9151A_1 "rtl_nic/rtl9151a-1.fw"
#define FIRMWARE_8126A_2 "rtl_nic/rtl8126a-2.fw"
#define FIRMWARE_8126A_3 "rtl_nic/rtl8126a-3.fw"
#define FIRMWARE_8127A_1 "rtl_nic/rtl8127a-1.fw"
@@ -110,6 +112,8 @@ static const struct rtl_chip_info {
{ 0x7cf, 0x681, RTL_GIGA_MAC_VER_66, "RTL8125BP", FIRMWARE_8125BP_2 },
/* 8125D family. */
+ { 0x7cf, 0x68b, RTL_GIGA_MAC_VER_64, "RTL9151A", FIRMWARE_9151A_1 },
+ { 0x7cf, 0x68a, RTL_GIGA_MAC_VER_64, "RTL8125K", FIRMWARE_8125K_1 },
{ 0x7cf, 0x689, RTL_GIGA_MAC_VER_64, "RTL8125D", FIRMWARE_8125D_2 },
{ 0x7cf, 0x688, RTL_GIGA_MAC_VER_64, "RTL8125D", FIRMWARE_8125D_1 },
@@ -770,7 +774,9 @@ MODULE_FIRMWARE(FIRMWARE_8125A_3);
MODULE_FIRMWARE(FIRMWARE_8125B_2);
MODULE_FIRMWARE(FIRMWARE_8125D_1);
MODULE_FIRMWARE(FIRMWARE_8125D_2);
+MODULE_FIRMWARE(FIRMWARE_8125K_1);
MODULE_FIRMWARE(FIRMWARE_8125BP_2);
+MODULE_FIRMWARE(FIRMWARE_9151A_1);
MODULE_FIRMWARE(FIRMWARE_8126A_2);
MODULE_FIRMWARE(FIRMWARE_8126A_3);
MODULE_FIRMWARE(FIRMWARE_8127A_1);
@@ -1514,11 +1520,20 @@ static enum rtl_dash_type rtl_get_dash_type(struct rtl8169_private *tp)
static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable)
{
- if (tp->mac_version >= RTL_GIGA_MAC_VER_25 &&
- tp->mac_version != RTL_GIGA_MAC_VER_28 &&
- tp->mac_version != RTL_GIGA_MAC_VER_31 &&
- tp->mac_version != RTL_GIGA_MAC_VER_38)
- r8169_mod_reg8_cond(tp, PMCH, D3_NO_PLL_DOWN, !enable);
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_24:
+ case RTL_GIGA_MAC_VER_28:
+ case RTL_GIGA_MAC_VER_31:
+ case RTL_GIGA_MAC_VER_38:
+ break;
+ case RTL_GIGA_MAC_VER_80:
+ r8169_mod_reg8_cond(tp, PMCH, D3_NO_PLL_DOWN, true);
+ break;
+ default:
+ r8169_mod_reg8_cond(tp, PMCH, D3HOT_NO_PLL_DOWN, true);
+ r8169_mod_reg8_cond(tp, PMCH, D3COLD_NO_PLL_DOWN, !enable);
+ break;
+ }
}
static void rtl_reset_packet_filter(struct rtl8169_private *tp)
@@ -2373,26 +2388,6 @@ void r8169_apply_firmware(struct rtl8169_private *tp)
}
}
-static void rtl8168_config_eee_mac(struct rtl8169_private *tp)
-{
- /* Adjust EEE LED frequency */
- if (tp->mac_version != RTL_GIGA_MAC_VER_38)
- RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
-
- rtl_eri_set_bits(tp, 0x1b0, 0x0003);
-}
-
-static void rtl8125a_config_eee_mac(struct rtl8169_private *tp)
-{
- r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
- r8168_mac_ocp_modify(tp, 0xeb62, 0, BIT(2) | BIT(1));
-}
-
-static void rtl8125b_config_eee_mac(struct rtl8169_private *tp)
-{
- r8168_mac_ocp_modify(tp, 0xe040, 0, BIT(1) | BIT(0));
-}
-
static void rtl_rar_exgmac_set(struct rtl8169_private *tp, const u8 *addr)
{
rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, get_unaligned_le32(addr));
@@ -3170,8 +3165,6 @@ static void rtl_hw_start_8168e_2(struct rtl8169_private *tp)
RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB);
- rtl8168_config_eee_mac(tp);
-
RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
rtl_mod_config5(tp, Spi_en, 0);
@@ -3196,8 +3189,6 @@ static void rtl_hw_start_8168f(struct rtl8169_private *tp)
RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN);
RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN);
rtl_mod_config5(tp, Spi_en, 0);
-
- rtl8168_config_eee_mac(tp);
}
static void rtl_hw_start_8168f_1(struct rtl8169_private *tp)
@@ -3247,8 +3238,6 @@ static void rtl_hw_start_8168g(struct rtl8169_private *tp)
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
- rtl8168_config_eee_mac(tp);
-
rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06);
rtl_eri_clear_bits(tp, 0x1b0, BIT(12));
@@ -3389,8 +3378,6 @@ static void rtl_hw_start_8168h_1(struct rtl8169_private *tp)
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
- rtl8168_config_eee_mac(tp);
-
RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
@@ -3438,8 +3425,6 @@ static void rtl_hw_start_8168ep(struct rtl8169_private *tp)
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
- rtl8168_config_eee_mac(tp);
-
rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06);
RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN);
@@ -3495,8 +3480,6 @@ static void rtl_hw_start_8117(struct rtl8169_private *tp)
rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000);
rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000);
- rtl8168_config_eee_mac(tp);
-
RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN);
RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN);
@@ -3737,11 +3720,6 @@ static void rtl_hw_start_8125_common(struct rtl8169_private *tp)
rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10);
- if (tp->mac_version == RTL_GIGA_MAC_VER_61)
- rtl8125a_config_eee_mac(tp);
- else
- rtl8125b_config_eee_mac(tp);
-
rtl_disable_rxdvgate(tp);
}
@@ -4744,6 +4722,41 @@ static int rtl8169_poll(struct napi_struct *napi, int budget)
return work_done;
}
+static void rtl_enable_tx_lpi(struct rtl8169_private *tp, bool enable)
+{
+ if (!rtl_supports_eee(tp))
+ return;
+
+ switch (tp->mac_version) {
+ case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_52:
+ /* Adjust EEE LED frequency */
+ if (tp->mac_version != RTL_GIGA_MAC_VER_38)
+ RTL_W8(tp, EEE_LED, RTL_R8(tp, EEE_LED) & ~0x07);
+ if (enable)
+ rtl_eri_set_bits(tp, 0x1b0, 0x0003);
+ else
+ rtl_eri_clear_bits(tp, 0x1b0, 0x0003);
+ break;
+ case RTL_GIGA_MAC_VER_61:
+ if (enable) {
+ r8168_mac_ocp_modify(tp, 0xe040, 0, 0x0003);
+ r8168_mac_ocp_modify(tp, 0xeb62, 0, 0x0006);
+ } else {
+ r8168_mac_ocp_modify(tp, 0xe040, 0x0003, 0);
+ r8168_mac_ocp_modify(tp, 0xeb62, 0x0006, 0);
+ }
+ break;
+ case RTL_GIGA_MAC_VER_63 ... RTL_GIGA_MAC_VER_LAST:
+ if (enable)
+ r8168_mac_ocp_modify(tp, 0xe040, 0, 0x0003);
+ else
+ r8168_mac_ocp_modify(tp, 0xe040, 0x0003, 0);
+ break;
+ default:
+ break;
+ }
+}
+
static void r8169_phylink_handler(struct net_device *ndev)
{
struct rtl8169_private *tp = netdev_priv(ndev);
@@ -4751,6 +4764,7 @@ static void r8169_phylink_handler(struct net_device *ndev)
if (netif_carrier_ok(ndev)) {
rtl_link_chg_patch(tp);
+ rtl_enable_tx_lpi(tp, tp->phydev->enable_tx_lpi);
pm_request_resume(d);
} else {
pm_runtime_idle(d);
@@ -4995,9 +5009,7 @@ static int rtl8169_resume(struct device *device)
clk_prepare_enable(tp->clk);
/* Some chip versions may truncate packets without this initialization */
- if (tp->mac_version == RTL_GIGA_MAC_VER_37 ||
- tp->mac_version == RTL_GIGA_MAC_VER_46)
- rtl_init_rxcfg(tp);
+ rtl_init_rxcfg(tp);
return rtl8169_runtime_resume(device);
}
@@ -5450,6 +5462,15 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
}
tp->aspm_manageable = !rc;
+ /* Fiber mode on RTL8127AF isn't supported */
+ if (rtl_is_8125(tp)) {
+ u16 data = r8168_mac_ocp_read(tp, 0xd006);
+
+ if ((data & 0xff) == 0x07)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "Fiber mode not supported\n");
+ }
+
tp->dash_type = rtl_get_dash_type(tp);
tp->dash_enabled = rtl_dash_is_enabled(tp);
diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h
index 7b48060c250b..5e56ec9b1013 100644
--- a/drivers/net/ethernet/renesas/ravb.h
+++ b/drivers/net/ethernet/renesas/ravb.h
@@ -35,16 +35,6 @@
/* Driver's parameters */
#define RAVB_ALIGN 128
-/* Hardware time stamp */
-#define RAVB_TXTSTAMP_VALID 0x00000001 /* TX timestamp valid */
-#define RAVB_TXTSTAMP_ENABLED 0x00000010 /* Enable TX timestamping */
-
-#define RAVB_RXTSTAMP_VALID 0x00000001 /* RX timestamp valid */
-#define RAVB_RXTSTAMP_TYPE 0x00000006 /* RX type mask */
-#define RAVB_RXTSTAMP_TYPE_V2_L2_EVENT 0x00000002
-#define RAVB_RXTSTAMP_TYPE_ALL 0x00000006
-#define RAVB_RXTSTAMP_ENABLED 0x00000010 /* Enable RX timestamping */
-
enum ravb_reg {
/* AVB-DMAC registers */
CCC = 0x0000,
@@ -1017,7 +1007,6 @@ enum CSR2_BIT {
#define CSR2_CSUM_ENABLE (CSR2_RTCP4 | CSR2_RUDP4 | CSR2_RICMP4 | \
CSR2_RTCP6 | CSR2_RUDP6 | CSR2_RICMP6)
-#define DBAT_ENTRY_NUM 22
#define RX_QUEUE_OFFSET 4
#define NUM_RX_QUEUE 2
#define NUM_TX_QUEUE 2
@@ -1062,6 +1051,7 @@ struct ravb_hw_info {
u32 rx_max_frame_size;
u32 rx_buffer_size;
u32 rx_desc_size;
+ u32 dbat_entry_num;
unsigned aligned_tx: 1;
unsigned coalesce_irqs:1; /* Needs software IRQ coalescing */
@@ -1114,8 +1104,8 @@ struct ravb_private {
u32 rx_over_errors;
u32 rx_fifo_errors;
struct net_device_stats stats[NUM_RX_QUEUE];
- u32 tstamp_tx_ctrl;
- u32 tstamp_rx_ctrl;
+ enum hwtstamp_tx_types tstamp_tx_ctrl;
+ enum hwtstamp_rx_filters tstamp_rx_ctrl;
struct list_head ts_skb_list;
u32 ts_skb_tag;
struct ravb_ptp ptp;
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index 9d3bd65b85ff..57b0db314fb5 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -946,6 +946,30 @@ refill:
return rx_packets;
}
+static void ravb_rx_rcar_hwstamp(struct ravb_private *priv, int q,
+ struct ravb_ex_rx_desc *desc,
+ struct sk_buff *skb)
+{
+ struct skb_shared_hwtstamps *shhwtstamps;
+ struct timespec64 ts;
+ bool get_ts;
+
+ if (q == RAVB_NC)
+ get_ts = priv->tstamp_rx_ctrl != HWTSTAMP_FILTER_NONE;
+ else
+ get_ts = priv->tstamp_rx_ctrl == HWTSTAMP_FILTER_ALL;
+
+ if (!get_ts)
+ return;
+
+ shhwtstamps = skb_hwtstamps(skb);
+ memset(shhwtstamps, 0, sizeof(*shhwtstamps));
+ ts.tv_sec = ((u64)le16_to_cpu(desc->ts_sh) << 32)
+ | le32_to_cpu(desc->ts_sl);
+ ts.tv_nsec = le32_to_cpu(desc->ts_n);
+ shhwtstamps->hwtstamp = timespec64_to_ktime(ts);
+}
+
/* Packet receive function for Ethernet AVB */
static int ravb_rx_rcar(struct net_device *ndev, int budget, int q)
{
@@ -955,7 +979,6 @@ static int ravb_rx_rcar(struct net_device *ndev, int budget, int q)
struct ravb_ex_rx_desc *desc;
unsigned int limit, i;
struct sk_buff *skb;
- struct timespec64 ts;
int rx_packets = 0;
u8 desc_status;
u16 pkt_len;
@@ -992,7 +1015,6 @@ static int ravb_rx_rcar(struct net_device *ndev, int budget, int q)
if (desc_status & MSC_CEEF)
stats->rx_missed_errors++;
} else {
- u32 get_ts = priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE;
struct ravb_rx_buffer *rx_buff;
void *rx_addr;
@@ -1010,19 +1032,8 @@ static int ravb_rx_rcar(struct net_device *ndev, int budget, int q)
break;
}
skb_mark_for_recycle(skb);
- get_ts &= (q == RAVB_NC) ?
- RAVB_RXTSTAMP_TYPE_V2_L2_EVENT :
- ~RAVB_RXTSTAMP_TYPE_V2_L2_EVENT;
- if (get_ts) {
- struct skb_shared_hwtstamps *shhwtstamps;
-
- shhwtstamps = skb_hwtstamps(skb);
- memset(shhwtstamps, 0, sizeof(*shhwtstamps));
- ts.tv_sec = ((u64) le16_to_cpu(desc->ts_sh) <<
- 32) | le32_to_cpu(desc->ts_sl);
- ts.tv_nsec = le32_to_cpu(desc->ts_n);
- shhwtstamps->hwtstamp = timespec64_to_ktime(ts);
- }
+
+ ravb_rx_rcar_hwstamp(priv, q, desc, skb);
skb_put(skb, pkt_len);
skb->protocol = eth_type_trans(skb, ndev);
@@ -1975,7 +1986,6 @@ out_ptp_stop:
out_set_reset:
ravb_set_opmode(ndev, CCC_OPC_RESET);
out_rpm_put:
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
out_napi_off:
if (info->nc_queues)
@@ -2211,15 +2221,35 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev)
skb_tx_timestamp(skb);
}
- /* Descriptor type must be set after all the above writes */
- dma_wmb();
+
if (num_tx_desc > 1) {
desc->die_dt = DT_FEND;
desc--;
+ /* When using multi-descriptors, DT_FEND needs to get written
+ * before DT_FSTART, but the compiler may reorder the memory
+ * writes in an attempt to optimize the code.
+ * Use a dma_wmb() barrier to make sure DT_FEND and DT_FSTART
+ * are written exactly in the order shown in the code.
+ * This is particularly important for cases where the DMA engine
+ * is already running when we are running this code. If the DMA
+ * sees DT_FSTART without the corresponding DT_FEND it will enter
+ * an error condition.
+ */
+ dma_wmb();
desc->die_dt = DT_FSTART;
} else {
+ /* Descriptor type must be set after all the above writes */
+ dma_wmb();
desc->die_dt = DT_FSINGLE;
}
+
+ /* Before ringing the doorbell we need to make sure that the latest
+ * writes have been committed to memory, otherwise it could delay
+ * things until the doorbell is rang again.
+ * This is in replacement of the read operation mentioned in the HW
+ * manuals.
+ */
+ dma_wmb();
ravb_modify(ndev, TCCR, TCCR_TSRQ0 << q, TCCR_TSRQ0 << q);
priv->cur_tx[q] += num_tx_desc;
@@ -2384,95 +2414,55 @@ static int ravb_close(struct net_device *ndev)
if (error)
return error;
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
}
-static int ravb_hwtstamp_get(struct net_device *ndev, struct ifreq *req)
+static int ravb_hwtstamp_get(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config)
{
struct ravb_private *priv = netdev_priv(ndev);
- struct hwtstamp_config config;
- config.flags = 0;
- config.tx_type = priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON :
- HWTSTAMP_TX_OFF;
- switch (priv->tstamp_rx_ctrl & RAVB_RXTSTAMP_TYPE) {
- case RAVB_RXTSTAMP_TYPE_V2_L2_EVENT:
- config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
- break;
- case RAVB_RXTSTAMP_TYPE_ALL:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
- break;
- default:
- config.rx_filter = HWTSTAMP_FILTER_NONE;
- }
+ config->flags = 0;
+ config->tx_type = priv->tstamp_tx_ctrl;
+ config->rx_filter = priv->tstamp_rx_ctrl;
- return copy_to_user(req->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
+ return 0;
}
/* Control hardware time stamping */
-static int ravb_hwtstamp_set(struct net_device *ndev, struct ifreq *req)
+static int ravb_hwtstamp_set(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct ravb_private *priv = netdev_priv(ndev);
- struct hwtstamp_config config;
- u32 tstamp_rx_ctrl = RAVB_RXTSTAMP_ENABLED;
- u32 tstamp_tx_ctrl;
+ enum hwtstamp_rx_filters tstamp_rx_ctrl;
+ enum hwtstamp_tx_types tstamp_tx_ctrl;
- if (copy_from_user(&config, req->ifr_data, sizeof(config)))
- return -EFAULT;
-
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
- tstamp_tx_ctrl = 0;
- break;
case HWTSTAMP_TX_ON:
- tstamp_tx_ctrl = RAVB_TXTSTAMP_ENABLED;
+ tstamp_tx_ctrl = config->tx_type;
break;
default:
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
- tstamp_rx_ctrl = 0;
- break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- tstamp_rx_ctrl |= RAVB_RXTSTAMP_TYPE_V2_L2_EVENT;
+ tstamp_rx_ctrl = config->rx_filter;
break;
default:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
- tstamp_rx_ctrl |= RAVB_RXTSTAMP_TYPE_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ tstamp_rx_ctrl = HWTSTAMP_FILTER_ALL;
}
priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
- return copy_to_user(req->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
-}
-
-/* ioctl to device function */
-static int ravb_do_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
-{
- struct phy_device *phydev = ndev->phydev;
-
- if (!netif_running(ndev))
- return -EINVAL;
-
- if (!phydev)
- return -ENODEV;
-
- switch (cmd) {
- case SIOCGHWTSTAMP:
- return ravb_hwtstamp_get(ndev, req);
- case SIOCSHWTSTAMP:
- return ravb_hwtstamp_set(ndev, req);
- }
-
- return phy_mii_ioctl(phydev, req, cmd);
+ return 0;
}
static int ravb_change_mtu(struct net_device *ndev, int new_mtu)
@@ -2608,11 +2598,13 @@ static const struct net_device_ops ravb_netdev_ops = {
.ndo_get_stats = ravb_get_stats,
.ndo_set_rx_mode = ravb_set_rx_mode,
.ndo_tx_timeout = ravb_tx_timeout,
- .ndo_eth_ioctl = ravb_do_ioctl,
+ .ndo_eth_ioctl = phy_do_ioctl_running,
.ndo_change_mtu = ravb_change_mtu,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
.ndo_set_features = ravb_set_features,
+ .ndo_hwtstamp_get = ravb_hwtstamp_get,
+ .ndo_hwtstamp_set = ravb_hwtstamp_set,
};
/* MDIO bus init function */
@@ -2694,6 +2686,7 @@ static const struct ravb_hw_info ravb_gen2_hw_info = {
.rx_buffer_size = SZ_2K +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
.rx_desc_size = sizeof(struct ravb_ex_rx_desc),
+ .dbat_entry_num = 22,
.aligned_tx = 1,
.gptp = 1,
.nc_queues = 1,
@@ -2717,6 +2710,7 @@ static const struct ravb_hw_info ravb_gen3_hw_info = {
.rx_buffer_size = SZ_2K +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
.rx_desc_size = sizeof(struct ravb_ex_rx_desc),
+ .dbat_entry_num = 22,
.internal_delay = 1,
.tx_counters = 1,
.multi_irqs = 1,
@@ -2743,6 +2737,7 @@ static const struct ravb_hw_info ravb_gen4_hw_info = {
.rx_buffer_size = SZ_2K +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
.rx_desc_size = sizeof(struct ravb_ex_rx_desc),
+ .dbat_entry_num = 22,
.internal_delay = 1,
.tx_counters = 1,
.multi_irqs = 1,
@@ -2769,6 +2764,7 @@ static const struct ravb_hw_info ravb_rzv2m_hw_info = {
.rx_buffer_size = SZ_2K +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
.rx_desc_size = sizeof(struct ravb_ex_rx_desc),
+ .dbat_entry_num = 22,
.multi_irqs = 1,
.err_mgmt_irqs = 1,
.gptp = 1,
@@ -2794,6 +2790,7 @@ static const struct ravb_hw_info gbeth_hw_info = {
.rx_max_frame_size = SZ_8K,
.rx_buffer_size = SZ_2K,
.rx_desc_size = sizeof(struct ravb_rx_desc),
+ .dbat_entry_num = 2,
.aligned_tx = 1,
.coalesce_irqs = 1,
.tx_counters = 1,
@@ -2921,13 +2918,14 @@ static int ravb_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
"failed to get cpg reset\n");
+ info = of_device_get_match_data(&pdev->dev);
+
ndev = alloc_etherdev_mqs(sizeof(struct ravb_private),
- NUM_TX_QUEUE, NUM_RX_QUEUE);
+ info->nc_queues ? NUM_TX_QUEUE : 1,
+ info->nc_queues ? NUM_RX_QUEUE : 1);
if (!ndev)
return -ENOMEM;
- info = of_device_get_match_data(&pdev->dev);
-
ndev->features = info->net_features;
ndev->hw_features = info->net_hw_features;
ndev->vlan_features = info->vlan_features;
@@ -3025,7 +3023,7 @@ static int ravb_probe(struct platform_device *pdev)
ravb_parse_delay_mode(np, ndev);
/* Allocate descriptor base address table */
- priv->desc_bat_size = sizeof(struct ravb_desc) * DBAT_ENTRY_NUM;
+ priv->desc_bat_size = sizeof(struct ravb_desc) * info->dbat_entry_num;
priv->desc_bat = dma_alloc_coherent(ndev->dev.parent, priv->desc_bat_size,
&priv->desc_bat_dma, GFP_KERNEL);
if (!priv->desc_bat) {
@@ -3035,7 +3033,7 @@ static int ravb_probe(struct platform_device *pdev)
error = -ENOMEM;
goto out_rpm_put;
}
- for (q = RAVB_BE; q < DBAT_ENTRY_NUM; q++)
+ for (q = RAVB_BE; q < info->dbat_entry_num; q++)
priv->desc_bat[q].die_dt = DT_EOS;
/* Initialise HW timestamp list */
@@ -3090,7 +3088,6 @@ static int ravb_probe(struct platform_device *pdev)
netdev_info(ndev, "Base address at %#x, %pM, IRQ %d.\n",
(u32)ndev->base_addr, ndev->dev_addr, ndev->irq);
- pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
return 0;
@@ -3274,10 +3271,8 @@ static int ravb_resume(struct device *dev)
return 0;
out_rpm_put:
- if (!priv->wol_enabled) {
- pm_runtime_mark_last_busy(dev);
+ if (!priv->wol_enabled)
pm_runtime_put_autosuspend(dev);
- }
return ret;
}
diff --git a/drivers/net/ethernet/renesas/rcar_gen4_ptp.h b/drivers/net/ethernet/renesas/rcar_gen4_ptp.h
index f77e79e47357..9a9c232c854e 100644
--- a/drivers/net/ethernet/renesas/rcar_gen4_ptp.h
+++ b/drivers/net/ethernet/renesas/rcar_gen4_ptp.h
@@ -9,24 +9,11 @@
#include <linux/ptp_clock_kernel.h>
-#define RCAR_GEN4_GPTP_OFFSET_S4 0x00018000
-
-/* driver's definitions */
-#define RCAR_GEN4_RXTSTAMP_ENABLED BIT(0)
-#define RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT BIT(1)
-#define RCAR_GEN4_RXTSTAMP_TYPE_ALL (RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT | BIT(2))
-#define RCAR_GEN4_RXTSTAMP_TYPE RCAR_GEN4_RXTSTAMP_TYPE_ALL
-
-#define RCAR_GEN4_TXTSTAMP_ENABLED BIT(0)
-
-
struct rcar_gen4_ptp_private {
void __iomem *addr;
struct ptp_clock *clock;
struct ptp_clock_info info;
spinlock_t lock; /* For multiple registers access */
- u32 tstamp_tx_ctrl;
- u32 tstamp_rx_ctrl;
s64 default_addend;
bool initialized;
};
diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h
index a1d4a877e5bd..aa605304fed0 100644
--- a/drivers/net/ethernet/renesas/rswitch.h
+++ b/drivers/net/ethernet/renesas/rswitch.h
@@ -1063,6 +1063,9 @@ struct rswitch_private {
bool etha_no_runtime_change;
bool gwca_halt;
struct net_device *offload_brdev;
+
+ enum hwtstamp_tx_types tstamp_tx_ctrl;
+ enum hwtstamp_rx_filters tstamp_rx_ctrl;
};
bool is_rdev(const struct net_device *ndev);
diff --git a/drivers/net/ethernet/renesas/rswitch_main.c b/drivers/net/ethernet/renesas/rswitch_main.c
index 8d8acc2124b8..e14b21148f27 100644
--- a/drivers/net/ethernet/renesas/rswitch_main.c
+++ b/drivers/net/ethernet/renesas/rswitch_main.c
@@ -30,6 +30,8 @@
#include "rswitch.h"
#include "rswitch_l2.h"
+#define RSWITCH_GPTP_OFFSET_S4 0x00018000
+
static int rswitch_reg_wait(void __iomem *addr, u32 offs, u32 mask, u32 expected)
{
u32 val;
@@ -843,7 +845,7 @@ static bool rswitch_rx(struct net_device *ndev, int *quota)
if (!skb)
goto out;
- get_ts = rdev->priv->ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
+ get_ts = rdev->priv->tstamp_rx_ctrl != HWTSTAMP_FILTER_NONE;
if (get_ts) {
struct skb_shared_hwtstamps *shhwtstamps;
struct timespec64 ts;
@@ -1793,88 +1795,54 @@ static struct net_device_stats *rswitch_get_stats(struct net_device *ndev)
return &ndev->stats;
}
-static int rswitch_hwstamp_get(struct net_device *ndev, struct ifreq *req)
+static int rswitch_hwstamp_get(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config)
{
struct rswitch_device *rdev = netdev_priv(ndev);
- struct rcar_gen4_ptp_private *ptp_priv;
- struct hwtstamp_config config;
-
- ptp_priv = rdev->priv->ptp_priv;
+ struct rswitch_private *priv = rdev->priv;
- config.flags = 0;
- config.tx_type = ptp_priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON :
- HWTSTAMP_TX_OFF;
- switch (ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE) {
- case RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT:
- config.rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
- break;
- case RCAR_GEN4_RXTSTAMP_TYPE_ALL:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
- break;
- default:
- config.rx_filter = HWTSTAMP_FILTER_NONE;
- break;
- }
+ config->flags = 0;
+ config->tx_type = priv->tstamp_tx_ctrl;
+ config->rx_filter = priv->tstamp_rx_ctrl;
- return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0;
+ return 0;
}
-static int rswitch_hwstamp_set(struct net_device *ndev, struct ifreq *req)
+static int rswitch_hwstamp_set(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct rswitch_device *rdev = netdev_priv(ndev);
- u32 tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED;
- struct hwtstamp_config config;
- u32 tstamp_tx_ctrl;
-
- if (copy_from_user(&config, req->ifr_data, sizeof(config)))
- return -EFAULT;
+ enum hwtstamp_rx_filters tstamp_rx_ctrl;
+ enum hwtstamp_tx_types tstamp_tx_ctrl;
- if (config.flags)
+ if (config->flags)
return -EINVAL;
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
- tstamp_tx_ctrl = 0;
- break;
case HWTSTAMP_TX_ON:
- tstamp_tx_ctrl = RCAR_GEN4_TXTSTAMP_ENABLED;
+ tstamp_tx_ctrl = config->tx_type;
break;
default:
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
- tstamp_rx_ctrl = 0;
- break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- tstamp_rx_ctrl |= RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
+ tstamp_rx_ctrl = config->rx_filter;
break;
default:
- config.rx_filter = HWTSTAMP_FILTER_ALL;
- tstamp_rx_ctrl |= RCAR_GEN4_RXTSTAMP_TYPE_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
+ tstamp_rx_ctrl = HWTSTAMP_FILTER_ALL;
break;
}
- rdev->priv->ptp_priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
- rdev->priv->ptp_priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
-
- return copy_to_user(req->ifr_data, &config, sizeof(config)) ? -EFAULT : 0;
-}
-
-static int rswitch_eth_ioctl(struct net_device *ndev, struct ifreq *req, int cmd)
-{
- if (!netif_running(ndev))
- return -EINVAL;
+ rdev->priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
+ rdev->priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
- switch (cmd) {
- case SIOCGHWTSTAMP:
- return rswitch_hwstamp_get(ndev, req);
- case SIOCSHWTSTAMP:
- return rswitch_hwstamp_set(ndev, req);
- default:
- return phy_mii_ioctl(ndev->phydev, req, cmd);
- }
+ return 0;
}
static int rswitch_get_port_parent_id(struct net_device *ndev,
@@ -1905,11 +1873,13 @@ static const struct net_device_ops rswitch_netdev_ops = {
.ndo_stop = rswitch_stop,
.ndo_start_xmit = rswitch_start_xmit,
.ndo_get_stats = rswitch_get_stats,
- .ndo_eth_ioctl = rswitch_eth_ioctl,
+ .ndo_eth_ioctl = phy_do_ioctl_running,
.ndo_get_port_parent_id = rswitch_get_port_parent_id,
.ndo_get_phys_port_name = rswitch_get_phys_port_name,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
+ .ndo_hwtstamp_get = rswitch_hwstamp_get,
+ .ndo_hwtstamp_set = rswitch_hwstamp_set,
};
bool is_rdev(const struct net_device *ndev)
@@ -2190,7 +2160,7 @@ static int renesas_eth_sw_probe(struct platform_device *pdev)
if (IS_ERR(priv->addr))
return PTR_ERR(priv->addr);
- priv->ptp_priv->addr = priv->addr + RCAR_GEN4_GPTP_OFFSET_S4;
+ priv->ptp_priv->addr = priv->addr + RSWITCH_GPTP_OFFSET_S4;
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
if (ret < 0) {
diff --git a/drivers/net/ethernet/renesas/rtsn.c b/drivers/net/ethernet/renesas/rtsn.c
index 15a043e85431..fdb1e7b7fb06 100644
--- a/drivers/net/ethernet/renesas/rtsn.c
+++ b/drivers/net/ethernet/renesas/rtsn.c
@@ -62,6 +62,9 @@ struct rtsn_private {
int tx_data_irq;
int rx_data_irq;
+
+ u32 tstamp_tx_ctrl;
+ u32 tstamp_rx_ctrl;
};
static u32 rtsn_read(struct rtsn_private *priv, enum rtsn_reg reg)
@@ -162,8 +165,7 @@ static int rtsn_rx(struct net_device *ndev, int budget)
unsigned int i;
bool get_ts;
- get_ts = priv->ptp_priv->tstamp_rx_ctrl &
- RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
+ get_ts = priv->tstamp_rx_ctrl != HWTSTAMP_FILTER_NONE;
ndescriptors = priv->dirty_rx + priv->num_rx_ring - priv->cur_rx;
rx_packets = 0;
@@ -1122,31 +1124,16 @@ static int rtsn_do_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
static int rtsn_hwtstamp_get(struct net_device *ndev,
struct kernel_hwtstamp_config *config)
{
- struct rcar_gen4_ptp_private *ptp_priv;
struct rtsn_private *priv;
if (!netif_running(ndev))
return -ENODEV;
priv = netdev_priv(ndev);
- ptp_priv = priv->ptp_priv;
config->flags = 0;
-
- config->tx_type =
- ptp_priv->tstamp_tx_ctrl ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-
- switch (ptp_priv->tstamp_rx_ctrl & RCAR_GEN4_RXTSTAMP_TYPE) {
- case RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT:
- config->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
- break;
- case RCAR_GEN4_RXTSTAMP_TYPE_ALL:
- config->rx_filter = HWTSTAMP_FILTER_ALL;
- break;
- default:
- config->rx_filter = HWTSTAMP_FILTER_NONE;
- break;
- }
+ config->tx_type = priv->tstamp_tx_ctrl;
+ config->rx_filter = priv->tstamp_rx_ctrl;
return 0;
}
@@ -1155,26 +1142,22 @@ static int rtsn_hwtstamp_set(struct net_device *ndev,
struct kernel_hwtstamp_config *config,
struct netlink_ext_ack *extack)
{
- struct rcar_gen4_ptp_private *ptp_priv;
+ enum hwtstamp_rx_filters tstamp_rx_ctrl;
+ enum hwtstamp_tx_types tstamp_tx_ctrl;
struct rtsn_private *priv;
- u32 tstamp_rx_ctrl;
- u32 tstamp_tx_ctrl;
if (!netif_running(ndev))
return -ENODEV;
priv = netdev_priv(ndev);
- ptp_priv = priv->ptp_priv;
if (config->flags)
return -EINVAL;
switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
- tstamp_tx_ctrl = 0;
- break;
case HWTSTAMP_TX_ON:
- tstamp_tx_ctrl = RCAR_GEN4_TXTSTAMP_ENABLED;
+ tstamp_tx_ctrl = config->tx_type;
break;
default:
return -ERANGE;
@@ -1182,21 +1165,17 @@ static int rtsn_hwtstamp_set(struct net_device *ndev,
switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
- tstamp_rx_ctrl = 0;
- break;
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
- tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED |
- RCAR_GEN4_RXTSTAMP_TYPE_V2_L2_EVENT;
+ tstamp_rx_ctrl = config->rx_filter;
break;
default:
config->rx_filter = HWTSTAMP_FILTER_ALL;
- tstamp_rx_ctrl = RCAR_GEN4_RXTSTAMP_ENABLED |
- RCAR_GEN4_RXTSTAMP_TYPE_ALL;
+ tstamp_rx_ctrl = HWTSTAMP_FILTER_ALL;
break;
}
- ptp_priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
- ptp_priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
+ priv->tstamp_tx_ctrl = tstamp_tx_ctrl;
+ priv->tstamp_rx_ctrl = tstamp_rx_ctrl;
return 0;
}
diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
index 75bad561b352..849c5a6c2af1 100644
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c
@@ -1521,8 +1521,10 @@ static int sxgbe_rx(struct sxgbe_priv_data *priv, int limit)
skb = priv->rxq[qnum]->rx_skbuff[entry];
- if (unlikely(!skb))
+ if (unlikely(!skb)) {
netdev_err(priv->dev, "rx descriptor is not consistent\n");
+ break;
+ }
prefetch(skb->data - NET_IP_ALIGN);
priv->rxq[qnum]->rx_skbuff[entry] = NULL;
diff --git a/drivers/net/ethernet/sfc/mae.c b/drivers/net/ethernet/sfc/mae.c
index 6fd0c1e9a7d5..7cfd9000f79d 100644
--- a/drivers/net/ethernet/sfc/mae.c
+++ b/drivers/net/ethernet/sfc/mae.c
@@ -1090,6 +1090,9 @@ void efx_mae_remove_mport(void *desc, void *arg)
kfree(mport);
}
+/*
+ * Takes ownership of @desc, even if it returns an error
+ */
static int efx_mae_process_mport(struct efx_nic *efx,
struct mae_mport_desc *desc)
{
@@ -1100,6 +1103,7 @@ static int efx_mae_process_mport(struct efx_nic *efx,
if (!IS_ERR_OR_NULL(mport)) {
netif_err(efx, drv, efx->net_dev,
"mport with id %u does exist!!!\n", desc->mport_id);
+ kfree(desc);
return -EEXIST;
}
diff --git a/drivers/net/ethernet/spacemit/k1_emac.c b/drivers/net/ethernet/spacemit/k1_emac.c
index e1c5faff3b71..220eb5ce7583 100644
--- a/drivers/net/ethernet/spacemit/k1_emac.c
+++ b/drivers/net/ethernet/spacemit/k1_emac.c
@@ -1441,6 +1441,9 @@ static int emac_set_pauseparam(struct net_device *dev,
struct emac_priv *priv = netdev_priv(dev);
u8 fc = 0;
+ if (!netif_running(dev))
+ return -ENETDOWN;
+
priv->flow_control_autoneg = pause->autoneg;
if (pause->autoneg) {
diff --git a/drivers/net/ethernet/spacemit/k1_emac.h b/drivers/net/ethernet/spacemit/k1_emac.h
index 5a09e946a276..577efe66573e 100644
--- a/drivers/net/ethernet/spacemit/k1_emac.h
+++ b/drivers/net/ethernet/spacemit/k1_emac.h
@@ -363,7 +363,7 @@ struct emac_desc {
/* Keep stats in this order, index used for accessing hardware */
union emac_hw_tx_stats {
- struct {
+ struct individual_tx_stats {
u64 tx_ok_pkts;
u64 tx_total_pkts;
u64 tx_ok_bytes;
@@ -378,11 +378,11 @@ union emac_hw_tx_stats {
u64 tx_pause_pkts;
} stats;
- DECLARE_FLEX_ARRAY(u64, array);
+ u64 array[sizeof(struct individual_tx_stats) / sizeof(u64)];
};
union emac_hw_rx_stats {
- struct {
+ struct individual_rx_stats {
u64 rx_ok_pkts;
u64 rx_total_pkts;
u64 rx_crc_err_pkts;
@@ -410,7 +410,7 @@ union emac_hw_rx_stats {
u64 rx_truncate_fifo_full_pkts;
} stats;
- DECLARE_FLEX_ARRAY(u64, array);
+ u64 array[sizeof(struct individual_rx_stats) / sizeof(u64)];
};
#endif /* _K1_EMAC_H_ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 9507131875b2..907fe2e927f0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -10,6 +10,7 @@ config STMMAC_ETH
select PHYLINK
select CRC32
select RESET_CONTROLLER
+ select NET_DEVLINK
help
This is the driver for the Ethernet IPs built around a
Synopsys IP Core.
@@ -67,6 +68,15 @@ config DWMAC_ANARION
This selects the Anarion SoC glue layer support for the stmmac driver.
+config DWMAC_EIC7700
+ tristate "Support for Eswin eic7700 ethernet driver"
+ depends on OF && HAS_DMA && ARCH_ESWIN || COMPILE_TEST
+ help
+ This driver supports the Eswin EIC7700 Ethernet controller,
+ which integrates Synopsys DesignWare QoS features. It enables
+ high-speed networking with DMA acceleration and is optimized
+ for embedded systems.
+
config DWMAC_INGENIC
tristate "Ingenic MAC support"
default MACH_INGENIC
@@ -339,6 +349,11 @@ config DWMAC_VISCONTI
endif
+config STMMAC_LIBPCI
+ tristate
+ help
+ This option enables the PCI bus helpers for the stmmac driver.
+
config DWMAC_INTEL
tristate "Intel GMAC support"
default X86
@@ -352,16 +367,18 @@ config DWMAC_INTEL
config DWMAC_LOONGSON
tristate "Loongson PCI DWMAC support"
default MACH_LOONGSON64
- depends on (MACH_LOONGSON64 || COMPILE_TEST) && STMMAC_ETH && PCI
+ depends on (MACH_LOONGSON64 || COMPILE_TEST) && PCI
depends on COMMON_CLK
+ select STMMAC_LIBPCI
help
This selects the LOONGSON PCI bus support for the stmmac driver,
Support for ethernet controller on Loongson-2K1000 SoC and LS7A1000 bridge.
config STMMAC_PCI
tristate "STMMAC PCI bus support"
- depends on STMMAC_ETH && PCI
+ depends on PCI
depends on COMMON_CLK
+ select STMMAC_LIBPCI
help
This selects the platform specific bus support for the stmmac driver.
This driver was tested on XLINX XC2V3000 FF1152AMT0221
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index 51e068e26ce4..7bf528731034 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -7,13 +7,14 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
dwmac4_dma.o dwmac4_lib.o dwmac4_core.o dwmac5.o hwif.o \
stmmac_tc.o dwxgmac2_core.o dwxgmac2_dma.o dwxgmac2_descs.o \
stmmac_xdp.o stmmac_est.o stmmac_fpe.o stmmac_vlan.o \
- $(stmmac-y)
+ stmmac_pcs.o $(stmmac-y)
stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
# Ordering matters. Generic driver must be last.
obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o
+obj-$(CONFIG_DWMAC_EIC7700) += dwmac-eic7700.o
obj-$(CONFIG_DWMAC_INGENIC) += dwmac-ingenic.o
obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o
obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o
@@ -43,6 +44,7 @@ obj-$(CONFIG_DWMAC_VISCONTI) += dwmac-visconti.o
stmmac-platform-objs:= stmmac_platform.o
dwmac-altr-socfpga-objs := dwmac-socfpga.o
+obj-$(CONFIG_STMMAC_LIBPCI) += stmmac_libpci.o
obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o
obj-$(CONFIG_DWMAC_LOONGSON) += dwmac-loongson.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
index fb55efd52240..120a009c9992 100644
--- a/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/chain_mode.c
@@ -83,14 +83,13 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
return entry;
}
-static unsigned int is_jumbo_frm(int len, int enh_desc)
+static bool is_jumbo_frm(unsigned int len, bool enh_desc)
{
- unsigned int ret = 0;
+ bool ret = false;
if ((enh_desc && (len > BUF_SIZE_8KiB)) ||
- (!enh_desc && (len > BUF_SIZE_2KiB))) {
- ret = 1;
- }
+ (!enh_desc && (len > BUF_SIZE_2KiB)))
+ ret = true;
return ret;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h
index 8f34c9ad457f..49df46be3669 100644
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
@@ -26,6 +26,9 @@
#include "hwif.h"
#include "mmc.h"
+#define DWMAC_SNPSVER GENMASK_U32(7, 0)
+#define DWMAC_USERVER GENMASK_U32(15, 8)
+
/* Synopsys Core versions */
#define DWMAC_CORE_3_40 0x34
#define DWMAC_CORE_3_50 0x35
@@ -43,6 +46,11 @@
#define DWXGMAC_ID 0x76
#define DWXLGMAC_ID 0x27
+static inline bool dwmac_is_xmac(enum dwmac_core_type core_type)
+{
+ return core_type == DWMAC_CORE_GMAC4 || core_type == DWMAC_CORE_XGMAC;
+}
+
#define STMMAC_CHAN0 0 /* Always supported and default for all chips */
/* TX and RX Descriptor Length, these need to be power of two.
@@ -192,9 +200,6 @@ struct stmmac_extra_stats {
unsigned long irq_pcs_ane_n;
unsigned long irq_pcs_link_n;
unsigned long irq_rgmii_n;
- unsigned long pcs_link;
- unsigned long pcs_duplex;
- unsigned long pcs_speed;
/* debug register */
unsigned long mtl_tx_status_fifo_full;
unsigned long mtl_tx_fifo_not_empty;
@@ -273,7 +278,6 @@ struct stmmac_safety_stats {
#define FLOW_AUTO (FLOW_TX | FLOW_RX)
/* PCS defines */
-#define STMMAC_PCS_RGMII (1 << 0)
#define STMMAC_PCS_SGMII (1 << 1)
#define SF_DMA_MODE 1 /* DMA STORE-AND-FORWARD Operation Mode */
@@ -309,6 +313,16 @@ struct stmmac_safety_stats {
#define DMA_HW_FEAT_ACTPHYIF 0x70000000 /* Active/selected PHY iface */
#define DEFAULT_DMA_PBL 8
+/* phy_intf_sel_i and ACTPHYIF encodings */
+#define PHY_INTF_SEL_GMII_MII 0
+#define PHY_INTF_SEL_RGMII 1
+#define PHY_INTF_SEL_SGMII 2
+#define PHY_INTF_SEL_TBI 3
+#define PHY_INTF_SEL_RMII 4
+#define PHY_INTF_SEL_RTBI 5
+#define PHY_INTF_SEL_SMII 6
+#define PHY_INTF_SEL_REVMII 7
+
/* MSI defines */
#define STMMAC_MSI_VEC_MAX 32
@@ -534,6 +548,19 @@ struct dma_features {
#define LPI_CTRL_STATUS_TLPIEX BIT(1) /* Transmit LPI Exit */
#define LPI_CTRL_STATUS_TLPIEN BIT(0) /* Transmit LPI Entry */
+/* Common definitions for AXI Master Bus Mode */
+#define DMA_AXI_AAL BIT(12)
+#define DMA_AXI_BLEN256 BIT(7)
+#define DMA_AXI_BLEN128 BIT(6)
+#define DMA_AXI_BLEN64 BIT(5)
+#define DMA_AXI_BLEN32 BIT(4)
+#define DMA_AXI_BLEN16 BIT(3)
+#define DMA_AXI_BLEN8 BIT(2)
+#define DMA_AXI_BLEN4 BIT(1)
+#define DMA_AXI_BLEN_MASK GENMASK(7, 1)
+
+void stmmac_axi_blen_to_mask(u32 *regval, const u32 *blen, size_t len);
+
#define STMMAC_CHAIN_MODE 0x1
#define STMMAC_RING_MODE 0x2
@@ -603,13 +630,18 @@ struct mac_device_info {
unsigned int mcast_bits_log2;
unsigned int rx_csum;
unsigned int pcs;
- unsigned int ps;
unsigned int xlgmac;
unsigned int num_vlan;
u32 vlan_filter[32];
bool vlan_fail_q_en;
u8 vlan_fail_q;
bool hw_vlan_en;
+ bool reverse_sgmii_enable;
+
+ /* This spinlock protects read-modify-write of the interrupt
+ * mask/enable registers.
+ */
+ spinlock_t irq_ctrl_lock;
};
struct stmmac_rx_routing {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c
index 84072c8ed741..5e0fc31762d9 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c
@@ -34,7 +34,7 @@ static void gmac_write_reg(struct anarion_gmac *gmac, uint8_t reg, uint32_t val)
writel(val, gmac->ctl_block + reg);
}
-static int anarion_gmac_init(struct platform_device *pdev, void *priv)
+static int anarion_gmac_init(struct device *dev, void *priv)
{
uint32_t sw_config;
struct anarion_gmac *gmac = priv;
@@ -52,7 +52,7 @@ static int anarion_gmac_init(struct platform_device *pdev, void *priv)
return 0;
}
-static void anarion_gmac_exit(struct platform_device *pdev, void *priv)
+static void anarion_gmac_exit(struct device *dev, void *priv)
{
struct anarion_gmac *gmac = priv;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
index e8539cad4602..d043bad4a862 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.c
@@ -38,8 +38,6 @@ static int dwc_eth_dwmac_config_dt(struct platform_device *pdev,
{
struct device *dev = &pdev->dev;
u32 burst_map = 0;
- u32 bit_index = 0;
- u32 a_index = 0;
if (!plat_dat->axi) {
plat_dat->axi = devm_kzalloc(&pdev->dev,
@@ -83,33 +81,11 @@ static int dwc_eth_dwmac_config_dt(struct platform_device *pdev,
}
device_property_read_u32(dev, "snps,burst-map", &burst_map);
- /* converts burst-map bitmask to burst array */
- for (bit_index = 0; bit_index < 7; bit_index++) {
- if (burst_map & (1 << bit_index)) {
- switch (bit_index) {
- case 0:
- plat_dat->axi->axi_blen[a_index] = 4; break;
- case 1:
- plat_dat->axi->axi_blen[a_index] = 8; break;
- case 2:
- plat_dat->axi->axi_blen[a_index] = 16; break;
- case 3:
- plat_dat->axi->axi_blen[a_index] = 32; break;
- case 4:
- plat_dat->axi->axi_blen[a_index] = 64; break;
- case 5:
- plat_dat->axi->axi_blen[a_index] = 128; break;
- case 6:
- plat_dat->axi->axi_blen[a_index] = 256; break;
- default:
- break;
- }
- a_index++;
- }
- }
+ plat_dat->axi->axi_blen_regval = FIELD_PREP(DMA_AXI_BLEN_MASK,
+ burst_map);
/* dwc-qos needs GMAC4, AAL, TSO and PMT */
- plat_dat->has_gmac4 = 1;
+ plat_dat->core_type = DWMAC_CORE_GMAC4;
plat_dat->dma_cfg->aal = 1;
plat_dat->flags |= STMMAC_FLAG_TSO_EN;
plat_dat->pmt = 1;
@@ -162,7 +138,7 @@ static void tegra_eqos_fix_speed(void *bsp_priv, int speed, unsigned int mode)
priv = netdev_priv(dev_get_drvdata(eqos->dev));
/* Calibration should be done with the MDIO bus idle */
- mutex_lock(&priv->mii->mdio_lock);
+ stmmac_mdio_lock(priv);
/* calibrate */
value = readl(eqos->regs + SDMEMCOMPPADCTRL);
@@ -198,7 +174,7 @@ static void tegra_eqos_fix_speed(void *bsp_priv, int speed, unsigned int mode)
value &= ~SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD;
writel(value, eqos->regs + SDMEMCOMPPADCTRL);
- mutex_unlock(&priv->mii->mdio_lock);
+ stmmac_mdio_unlock(priv);
} else {
value = readl(eqos->regs + AUTO_CAL_CONFIG);
value &= ~AUTO_CAL_CONFIG_ENABLE;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
new file mode 100644
index 000000000000..bcb8e000e720
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-eic7700.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Eswin DWC Ethernet linux driver
+ *
+ * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd.
+ *
+ * Authors:
+ * Zhi Li <lizhi2@eswincomputing.com>
+ * Shuang Liang <liangshuang@eswincomputing.com>
+ * Shangjuan Wei <weishangjuan@eswincomputing.com>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/pm_runtime.h>
+#include <linux/stmmac.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+#include "stmmac_platform.h"
+
+/* eth_phy_ctrl_offset eth0:0x100 */
+#define EIC7700_ETH_TX_CLK_SEL BIT(16)
+#define EIC7700_ETH_PHY_INTF_SELI BIT(0)
+
+/* eth_axi_lp_ctrl_offset eth0:0x108 */
+#define EIC7700_ETH_CSYSREQ_VAL BIT(0)
+
+/*
+ * TX/RX Clock Delay Bit Masks:
+ * - TX Delay: bits [14:8] — TX_CLK delay (unit: 0.1ns per bit)
+ * - RX Delay: bits [30:24] — RX_CLK delay (unit: 0.1ns per bit)
+ */
+#define EIC7700_ETH_TX_ADJ_DELAY GENMASK(14, 8)
+#define EIC7700_ETH_RX_ADJ_DELAY GENMASK(30, 24)
+
+#define EIC7700_MAX_DELAY_UNIT 0x7F
+
+static const char * const eic7700_clk_names[] = {
+ "tx", "axi", "cfg",
+};
+
+struct eic7700_qos_priv {
+ struct plat_stmmacenet_data *plat_dat;
+};
+
+static int eic7700_clks_config(void *priv, bool enabled)
+{
+ struct eic7700_qos_priv *dwc = (struct eic7700_qos_priv *)priv;
+ struct plat_stmmacenet_data *plat = dwc->plat_dat;
+ int ret = 0;
+
+ if (enabled)
+ ret = clk_bulk_prepare_enable(plat->num_clks, plat->clks);
+ else
+ clk_bulk_disable_unprepare(plat->num_clks, plat->clks);
+
+ return ret;
+}
+
+static int eic7700_dwmac_init(struct device *dev, void *priv)
+{
+ struct eic7700_qos_priv *dwc = priv;
+
+ return eic7700_clks_config(dwc, true);
+}
+
+static void eic7700_dwmac_exit(struct device *dev, void *priv)
+{
+ struct eic7700_qos_priv *dwc = priv;
+
+ eic7700_clks_config(dwc, false);
+}
+
+static int eic7700_dwmac_suspend(struct device *dev, void *priv)
+{
+ return pm_runtime_force_suspend(dev);
+}
+
+static int eic7700_dwmac_resume(struct device *dev, void *priv)
+{
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ dev_err(dev, "%s failed: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int eic7700_dwmac_probe(struct platform_device *pdev)
+{
+ struct plat_stmmacenet_data *plat_dat;
+ struct stmmac_resources stmmac_res;
+ struct eic7700_qos_priv *dwc_priv;
+ struct regmap *eic7700_hsp_regmap;
+ u32 eth_axi_lp_ctrl_offset;
+ u32 eth_phy_ctrl_offset;
+ u32 eth_phy_ctrl_regset;
+ u32 eth_rxd_dly_offset;
+ u32 eth_dly_param = 0;
+ u32 delay_ps;
+ int i, ret;
+
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to get resources\n");
+
+ plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);
+ if (IS_ERR(plat_dat))
+ return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat),
+ "dt configuration failed\n");
+
+ dwc_priv = devm_kzalloc(&pdev->dev, sizeof(*dwc_priv), GFP_KERNEL);
+ if (!dwc_priv)
+ return -ENOMEM;
+
+ /* Read rx-internal-delay-ps and update rx_clk delay */
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "rx-internal-delay-ps", &delay_ps)) {
+ u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT);
+
+ eth_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY;
+ eth_dly_param |= FIELD_PREP(EIC7700_ETH_RX_ADJ_DELAY, val);
+ } else {
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "missing required property rx-internal-delay-ps\n");
+ }
+
+ /* Read tx-internal-delay-ps and update tx_clk delay */
+ if (!of_property_read_u32(pdev->dev.of_node,
+ "tx-internal-delay-ps", &delay_ps)) {
+ u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT);
+
+ eth_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY;
+ eth_dly_param |= FIELD_PREP(EIC7700_ETH_TX_ADJ_DELAY, val);
+ } else {
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "missing required property tx-internal-delay-ps\n");
+ }
+
+ eic7700_hsp_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "eswin,hsp-sp-csr");
+ if (IS_ERR(eic7700_hsp_regmap))
+ return dev_err_probe(&pdev->dev,
+ PTR_ERR(eic7700_hsp_regmap),
+ "Failed to get hsp-sp-csr regmap\n");
+
+ ret = of_property_read_u32_index(pdev->dev.of_node,
+ "eswin,hsp-sp-csr",
+ 1, &eth_phy_ctrl_offset);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "can't get eth_phy_ctrl_offset\n");
+
+ regmap_read(eic7700_hsp_regmap, eth_phy_ctrl_offset,
+ &eth_phy_ctrl_regset);
+ eth_phy_ctrl_regset |=
+ (EIC7700_ETH_TX_CLK_SEL | EIC7700_ETH_PHY_INTF_SELI);
+ regmap_write(eic7700_hsp_regmap, eth_phy_ctrl_offset,
+ eth_phy_ctrl_regset);
+
+ ret = of_property_read_u32_index(pdev->dev.of_node,
+ "eswin,hsp-sp-csr",
+ 2, &eth_axi_lp_ctrl_offset);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "can't get eth_axi_lp_ctrl_offset\n");
+
+ regmap_write(eic7700_hsp_regmap, eth_axi_lp_ctrl_offset,
+ EIC7700_ETH_CSYSREQ_VAL);
+
+ ret = of_property_read_u32_index(pdev->dev.of_node,
+ "eswin,hsp-sp-csr",
+ 3, &eth_rxd_dly_offset);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "can't get eth_rxd_dly_offset\n");
+
+ regmap_write(eic7700_hsp_regmap, eth_rxd_dly_offset,
+ eth_dly_param);
+
+ plat_dat->num_clks = ARRAY_SIZE(eic7700_clk_names);
+ plat_dat->clks = devm_kcalloc(&pdev->dev,
+ plat_dat->num_clks,
+ sizeof(*plat_dat->clks),
+ GFP_KERNEL);
+ if (!plat_dat->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(eic7700_clk_names); i++)
+ plat_dat->clks[i].id = eic7700_clk_names[i];
+
+ ret = devm_clk_bulk_get_optional(&pdev->dev,
+ plat_dat->num_clks,
+ plat_dat->clks);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to get clocks\n");
+
+ plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, "tx");
+ plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
+ plat_dat->clks_config = eic7700_clks_config;
+ plat_dat->bsp_priv = dwc_priv;
+ dwc_priv->plat_dat = plat_dat;
+ plat_dat->init = eic7700_dwmac_init;
+ plat_dat->exit = eic7700_dwmac_exit;
+ plat_dat->suspend = eic7700_dwmac_suspend;
+ plat_dat->resume = eic7700_dwmac_resume;
+
+ return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
+}
+
+static const struct of_device_id eic7700_dwmac_match[] = {
+ { .compatible = "eswin,eic7700-qos-eth" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, eic7700_dwmac_match);
+
+static struct platform_driver eic7700_dwmac_driver = {
+ .probe = eic7700_dwmac_probe,
+ .driver = {
+ .name = "eic7700-eth-dwmac",
+ .pm = &stmmac_pltfr_pm_ops,
+ .of_match_table = eic7700_dwmac_match,
+ },
+};
+module_platform_driver(eic7700_dwmac_driver);
+
+MODULE_AUTHOR("Zhi Li <lizhi2@eswincomputing.com>");
+MODULE_AUTHOR("Shuang Liang <liangshuang@eswincomputing.com>");
+MODULE_AUTHOR("Shangjuan Wei <weishangjuan@eswincomputing.com>");
+MODULE_DESCRIPTION("Eswin eic7700 qos ethernet driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
index 4268b9987237..db288fbd5a4d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
@@ -23,18 +23,13 @@
#include "stmmac_platform.h"
#define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16)
-#define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16)
-#define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16)
-#define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16)
+#define GPR_ENET_QOS_INTF_SEL_MASK GENMASK(20, 16)
#define GPR_ENET_QOS_CLK_GEN_EN (0x1 << 19)
#define GPR_ENET_QOS_CLK_TX_CLK_SEL (0x1 << 20)
#define GPR_ENET_QOS_RGMII_EN (0x1 << 21)
#define MX93_GPR_ENET_QOS_INTF_MODE_MASK GENMASK(3, 0)
-#define MX93_GPR_ENET_QOS_INTF_MASK GENMASK(3, 1)
-#define MX93_GPR_ENET_QOS_INTF_SEL_MII (0x0 << 1)
-#define MX93_GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 1)
-#define MX93_GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 1)
+#define MX93_GPR_ENET_QOS_INTF_SEL_MASK GENMASK(3, 1)
#define MX93_GPR_ENET_QOS_CLK_GEN_EN (0x1 << 0)
#define MX93_GPR_ENET_QOS_CLK_SEL_MASK BIT_MASK(0)
#define MX93_GPR_CLK_SEL_OFFSET (4)
@@ -44,13 +39,15 @@
#define RMII_RESET_SPEED (0x3 << 14)
#define CTRL_SPEED_MASK GENMASK(15, 14)
+struct imx_priv_data;
+
struct imx_dwmac_ops {
u32 addr_width;
u32 flags;
bool mac_rgmii_txclk_auto_adj;
int (*fix_soc_reset)(struct stmmac_priv *priv, void __iomem *ioaddr);
- int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat);
+ int (*set_intf_mode)(struct imx_priv_data *dwmac, u8 phy_intf_sel);
void (*fix_mac_speed)(void *priv, int speed, unsigned int mode);
};
@@ -67,79 +64,46 @@ struct imx_priv_data {
struct plat_stmmacenet_data *plat_dat;
};
-static int imx8mp_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
+static int imx8mp_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel)
{
- struct imx_priv_data *dwmac = plat_dat->bsp_priv;
- int val;
-
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_MII:
- val = GPR_ENET_QOS_INTF_SEL_MII;
- break;
- case PHY_INTERFACE_MODE_RMII:
- val = GPR_ENET_QOS_INTF_SEL_RMII;
- val |= (dwmac->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL);
- break;
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- val = GPR_ENET_QOS_INTF_SEL_RGMII |
- GPR_ENET_QOS_RGMII_EN;
- break;
- default:
- pr_debug("imx dwmac doesn't support %s interface\n",
- phy_modes(plat_dat->phy_interface));
- return -EINVAL;
- }
+ unsigned int val;
+
+ val = FIELD_PREP(GPR_ENET_QOS_INTF_SEL_MASK, phy_intf_sel) |
+ GPR_ENET_QOS_CLK_GEN_EN;
+
+ if (phy_intf_sel == PHY_INTF_SEL_RMII && !dwmac->rmii_refclk_ext)
+ val |= GPR_ENET_QOS_CLK_TX_CLK_SEL;
+ else if (phy_intf_sel == PHY_INTF_SEL_RGMII)
+ val |= GPR_ENET_QOS_RGMII_EN;
- val |= GPR_ENET_QOS_CLK_GEN_EN;
return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
GPR_ENET_QOS_INTF_MODE_MASK, val);
};
static int
-imx8dxl_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
+imx8dxl_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel)
{
- int ret = 0;
-
/* TBD: depends on imx8dxl scu interfaces to be upstreamed */
- return ret;
+ return 0;
}
-static int imx93_set_intf_mode(struct plat_stmmacenet_data *plat_dat)
+static int imx93_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel)
{
- struct imx_priv_data *dwmac = plat_dat->bsp_priv;
- int val, ret;
-
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_MII:
- val = MX93_GPR_ENET_QOS_INTF_SEL_MII;
- break;
- case PHY_INTERFACE_MODE_RMII:
- if (dwmac->rmii_refclk_ext) {
- ret = regmap_clear_bits(dwmac->intf_regmap,
- dwmac->intf_reg_off +
- MX93_GPR_CLK_SEL_OFFSET,
- MX93_GPR_ENET_QOS_CLK_SEL_MASK);
- if (ret)
- return ret;
- }
- val = MX93_GPR_ENET_QOS_INTF_SEL_RMII;
- break;
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- val = MX93_GPR_ENET_QOS_INTF_SEL_RGMII;
- break;
- default:
- dev_dbg(dwmac->dev, "imx dwmac doesn't support %s interface\n",
- phy_modes(plat_dat->phy_interface));
- return -EINVAL;
+ unsigned int val;
+ int ret;
+
+ if (phy_intf_sel == PHY_INTF_SEL_RMII && dwmac->rmii_refclk_ext) {
+ ret = regmap_clear_bits(dwmac->intf_regmap,
+ dwmac->intf_reg_off +
+ MX93_GPR_CLK_SEL_OFFSET,
+ MX93_GPR_ENET_QOS_CLK_SEL_MASK);
+ if (ret)
+ return ret;
}
- val |= MX93_GPR_ENET_QOS_CLK_GEN_EN;
+ val = FIELD_PREP(MX93_GPR_ENET_QOS_INTF_SEL_MASK, phy_intf_sel) |
+ MX93_GPR_ENET_QOS_CLK_GEN_EN;
+
return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
MX93_GPR_ENET_QOS_INTF_MODE_MASK, val);
};
@@ -170,34 +134,24 @@ static int imx_dwmac_clks_config(void *priv, bool enabled)
return ret;
}
-static int imx_dwmac_init(struct platform_device *pdev, void *priv)
+static int imx_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel)
{
- struct plat_stmmacenet_data *plat_dat;
- struct imx_priv_data *dwmac = priv;
- int ret;
-
- plat_dat = dwmac->plat_dat;
+ struct imx_priv_data *dwmac = bsp_priv;
- if (dwmac->ops->set_intf_mode) {
- ret = dwmac->ops->set_intf_mode(plat_dat);
- if (ret)
- return ret;
- }
+ if (!dwmac->ops->set_intf_mode)
+ return 0;
- return 0;
-}
+ if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
+ phy_intf_sel != PHY_INTF_SEL_RGMII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII)
+ return -EINVAL;
-static void imx_dwmac_exit(struct platform_device *pdev, void *priv)
-{
- /* nothing to do now */
+ return dwmac->ops->set_intf_mode(dwmac, phy_intf_sel);
}
static int imx_dwmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
phy_interface_t interface, int speed)
{
- struct imx_priv_data *dwmac = bsp_priv;
-
- interface = dwmac->plat_dat->phy_interface;
if (interface == PHY_INTERFACE_MODE_RMII ||
interface == PHY_INTERFACE_MODE_MII)
return 0;
@@ -244,8 +198,8 @@ static void imx93_dwmac_fix_speed(void *priv, int speed, unsigned int mode)
if (regmap_read(dwmac->intf_regmap, dwmac->intf_reg_off, &iface))
return;
- iface &= MX93_GPR_ENET_QOS_INTF_MASK;
- if (iface != MX93_GPR_ENET_QOS_INTF_SEL_RGMII)
+ if (FIELD_GET(MX93_GPR_ENET_QOS_INTF_SEL_MASK, iface) !=
+ PHY_INTF_SEL_RGMII)
return;
old_ctrl = readl(dwmac->base_addr + MAC_CTRL_REG);
@@ -258,6 +212,7 @@ static void imx93_dwmac_fix_speed(void *priv, int speed, unsigned int mode)
readl(dwmac->base_addr + MAC_CTRL_REG);
usleep_range(10, 20);
+ iface &= MX93_GPR_ENET_QOS_INTF_SEL_MASK;
iface |= MX93_GPR_ENET_QOS_CLK_GEN_EN;
regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off,
MX93_GPR_ENET_QOS_INTF_MODE_MASK, iface);
@@ -370,8 +325,7 @@ static int imx_dwmac_probe(struct platform_device *pdev)
plat_dat->tx_queues_cfg[i].tbs_en = 1;
plat_dat->host_dma_width = dwmac->ops->addr_width;
- plat_dat->init = imx_dwmac_init;
- plat_dat->exit = imx_dwmac_exit;
+ plat_dat->set_phy_intf_sel = imx_set_phy_intf_sel;
plat_dat->clks_config = imx_dwmac_clks_config;
plat_dat->bsp_priv = dwmac;
dwmac->plat_dat = plat_dat;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c
index c1670f6bae14..8e4a30c11db0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ingenic.c
@@ -35,10 +35,6 @@
#define MACPHYC_RX_DELAY_MASK GENMASK(10, 4)
#define MACPHYC_SOFT_RST_MASK GENMASK(3, 3)
#define MACPHYC_PHY_INFT_MASK GENMASK(2, 0)
-#define MACPHYC_PHY_INFT_RMII 0x4
-#define MACPHYC_PHY_INFT_RGMII 0x1
-#define MACPHYC_PHY_INFT_GMII 0x0
-#define MACPHYC_PHY_INFT_MII 0x0
#define MACPHYC_TX_DELAY_PS_MAX 2496
#define MACPHYC_TX_DELAY_PS_MIN 20
@@ -68,172 +64,93 @@ struct ingenic_soc_info {
enum ingenic_mac_version version;
u32 mask;
- int (*set_mode)(struct plat_stmmacenet_data *plat_dat);
-};
-
-static int ingenic_mac_init(struct platform_device *pdev, void *bsp_priv)
-{
- struct ingenic_mac *mac = bsp_priv;
- int ret;
+ int (*set_mode)(struct ingenic_mac *mac, u8 phy_intf_sel);
- if (mac->soc_info->set_mode) {
- ret = mac->soc_info->set_mode(mac->plat_dat);
- if (ret)
- return ret;
- }
-
- return 0;
-}
+ u8 valid_phy_intf_sel;
+};
-static int jz4775_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
+static int jz4775_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
{
- struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_MII:
- val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
- FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_MII);
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_MII\n");
- break;
-
- case PHY_INTERFACE_MODE_GMII:
- val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
- FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_GMII);
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_GMII\n");
- break;
-
- case PHY_INTERFACE_MODE_RMII:
- val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
- FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
- break;
-
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) |
- FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII);
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n");
- break;
-
- default:
- dev_err(mac->dev, "Unsupported interface %s\n",
- phy_modes(plat_dat->phy_interface));
- return -EINVAL;
- }
+ val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel) |
+ FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT);
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
-static int x1000_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
+static int x1000_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
{
- struct ingenic_mac *mac = plat_dat->bsp_priv;
-
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_RMII:
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
- break;
-
- default:
- dev_err(mac->dev, "Unsupported interface %s\n",
- phy_modes(plat_dat->phy_interface));
- return -EINVAL;
- }
-
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, 0);
}
-static int x1600_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
+static int x1600_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
{
- struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_RMII:
- val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
- break;
-
- default:
- dev_err(mac->dev, "Unsupported interface %s\n",
- phy_modes(plat_dat->phy_interface));
- return -EINVAL;
- }
+ val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel);
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
-static int x1830_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
+static int x1830_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
{
- struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_RMII:
- val = FIELD_PREP(MACPHYC_MODE_SEL_MASK, MACPHYC_MODE_SEL_RMII) |
- FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
- break;
-
- default:
- dev_err(mac->dev, "Unsupported interface %s\n",
- phy_modes(plat_dat->phy_interface));
- return -EINVAL;
- }
+ val = FIELD_PREP(MACPHYC_MODE_SEL_MASK, MACPHYC_MODE_SEL_RMII) |
+ FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel);
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
-static int x2000_mac_set_mode(struct plat_stmmacenet_data *plat_dat)
+static int x2000_mac_set_mode(struct ingenic_mac *mac, u8 phy_intf_sel)
{
- struct ingenic_mac *mac = plat_dat->bsp_priv;
unsigned int val;
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_RMII:
- val = FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN) |
- FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN) |
- FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII);
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n");
- break;
-
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII);
+ val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, phy_intf_sel);
+ if (phy_intf_sel == PHY_INTF_SEL_RMII) {
+ val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN) |
+ FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN);
+ } else if (phy_intf_sel == PHY_INTF_SEL_RGMII) {
if (mac->tx_delay == 0)
val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN);
else
val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_DELAY) |
- FIELD_PREP(MACPHYC_TX_DELAY_MASK, (mac->tx_delay + 9750) / 19500 - 1);
+ FIELD_PREP(MACPHYC_TX_DELAY_MASK, (mac->tx_delay + 9750) / 19500 - 1);
if (mac->rx_delay == 0)
val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN);
else
val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_DELAY) |
FIELD_PREP(MACPHYC_RX_DELAY_MASK, (mac->rx_delay + 9750) / 19500 - 1);
-
- dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n");
- break;
-
- default:
- dev_err(mac->dev, "Unsupported interface %s\n",
- phy_modes(plat_dat->phy_interface));
- return -EINVAL;
}
/* Update MAC PHY control register */
return regmap_update_bits(mac->regmap, 0, mac->soc_info->mask, val);
}
+static int ingenic_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel)
+{
+ struct ingenic_mac *mac = bsp_priv;
+
+ if (!mac->soc_info->set_mode)
+ return 0;
+
+ if (phy_intf_sel >= BITS_PER_BYTE ||
+ ~mac->soc_info->valid_phy_intf_sel & BIT(phy_intf_sel))
+ return -EINVAL;
+
+ dev_dbg(mac->dev, "MAC PHY control register: interface %s\n",
+ phy_modes(mac->plat_dat->phy_interface));
+
+ return mac->soc_info->set_mode(mac, phy_intf_sel);
+}
+
static int ingenic_mac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
@@ -293,7 +210,7 @@ static int ingenic_mac_probe(struct platform_device *pdev)
mac->plat_dat = plat_dat;
plat_dat->bsp_priv = mac;
- plat_dat->init = ingenic_mac_init;
+ plat_dat->set_phy_intf_sel = ingenic_set_phy_intf_sel;
return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
}
@@ -303,6 +220,9 @@ static struct ingenic_soc_info jz4775_soc_info = {
.mask = MACPHYC_TXCLK_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = jz4775_mac_set_mode,
+ .valid_phy_intf_sel = BIT(PHY_INTF_SEL_GMII_MII) |
+ BIT(PHY_INTF_SEL_RGMII) |
+ BIT(PHY_INTF_SEL_RMII),
};
static struct ingenic_soc_info x1000_soc_info = {
@@ -310,6 +230,7 @@ static struct ingenic_soc_info x1000_soc_info = {
.mask = MACPHYC_SOFT_RST_MASK,
.set_mode = x1000_mac_set_mode,
+ .valid_phy_intf_sel = BIT(PHY_INTF_SEL_RMII),
};
static struct ingenic_soc_info x1600_soc_info = {
@@ -317,6 +238,7 @@ static struct ingenic_soc_info x1600_soc_info = {
.mask = MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = x1600_mac_set_mode,
+ .valid_phy_intf_sel = BIT(PHY_INTF_SEL_RMII),
};
static struct ingenic_soc_info x1830_soc_info = {
@@ -324,6 +246,7 @@ static struct ingenic_soc_info x1830_soc_info = {
.mask = MACPHYC_MODE_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = x1830_mac_set_mode,
+ .valid_phy_intf_sel = BIT(PHY_INTF_SEL_RMII),
};
static struct ingenic_soc_info x2000_soc_info = {
@@ -332,6 +255,8 @@ static struct ingenic_soc_info x2000_soc_info = {
MACPHYC_RX_DELAY_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK,
.set_mode = x2000_mac_set_mode,
+ .valid_phy_intf_sel = BIT(PHY_INTF_SEL_RGMII) |
+ BIT(PHY_INTF_SEL_RMII),
};
static const struct of_device_id ingenic_mac_of_matches[] = {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
index e74d00984b88..aad1be1ec4c1 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.c
@@ -565,30 +565,10 @@ static void common_default_data(struct plat_stmmacenet_data *plat)
{
/* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
plat->clk_csr = STMMAC_CSR_20_35M;
- plat->has_gmac = 1;
+ plat->core_type = DWMAC_CORE_GMAC;
plat->force_sf_dma_mode = 1;
plat->mdio_bus_data->needs_reset = true;
-
- /* Set default value for multicast hash bins */
- plat->multicast_filter_bins = HASH_TABLE_SIZE;
-
- /* Set default value for unicast filter entries */
- plat->unicast_filter_entries = 1;
-
- /* Set the maxmtu to a default of JUMBO_LEN */
- plat->maxmtu = JUMBO_LEN;
-
- /* Set default number of RX and TX queues to use */
- plat->tx_queues_to_use = 1;
- plat->rx_queues_to_use = 1;
-
- /* Disable Priority config by default */
- plat->tx_queues_cfg[0].use_prio = false;
- plat->rx_queues_cfg[0].use_prio = false;
-
- /* Disable RX queues routing by default */
- plat->rx_queues_cfg[0].pkt_route = 0x0;
}
static struct phylink_pcs *intel_mgbe_select_pcs(struct stmmac_priv *priv,
@@ -612,8 +592,7 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
plat->pdev = pdev;
plat->phy_addr = -1;
plat->clk_csr = STMMAC_CSR_250_300M;
- plat->has_gmac = 0;
- plat->has_gmac4 = 1;
+ plat->core_type = DWMAC_CORE_GMAC4;
plat->force_sf_dma_mode = 0;
plat->flags |= (STMMAC_FLAG_TSO_EN | STMMAC_FLAG_SPH_DISABLE);
@@ -630,22 +609,12 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP;
- for (i = 0; i < plat->rx_queues_to_use; i++) {
+ for (i = 0; i < plat->rx_queues_to_use; i++)
plat->rx_queues_cfg[i].mode_to_use = MTL_QUEUE_DCB;
- plat->rx_queues_cfg[i].chan = i;
-
- /* Disable Priority config by default */
- plat->rx_queues_cfg[i].use_prio = false;
-
- /* Disable RX queues routing by default */
- plat->rx_queues_cfg[i].pkt_route = 0x0;
- }
for (i = 0; i < plat->tx_queues_to_use; i++) {
plat->tx_queues_cfg[i].mode_to_use = MTL_QUEUE_DCB;
- /* Disable Priority config by default */
- plat->tx_queues_cfg[i].use_prio = false;
/* Default TX Q0 to use TSO and rest TXQ for TBS */
if (i > 0)
plat->tx_queues_cfg[i].tbs_en = 1;
@@ -681,9 +650,8 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
plat->axi->axi_xit_frm = 0;
plat->axi->axi_wr_osr_lmt = 1;
plat->axi->axi_rd_osr_lmt = 1;
- plat->axi->axi_blen[0] = 4;
- plat->axi->axi_blen[1] = 8;
- plat->axi->axi_blen[2] = 16;
+ plat->axi->axi_blen_regval = DMA_AXI_BLEN4 | DMA_AXI_BLEN8 |
+ DMA_AXI_BLEN16;
plat->ptp_max_adj = plat->clk_ptp_rate;
@@ -707,15 +675,6 @@ static int intel_mgbe_common_data(struct pci_dev *pdev,
plat->ptp_clk_freq_config = intel_mgbe_ptp_clk_freq_config;
- /* Set default value for multicast hash bins */
- plat->multicast_filter_bins = HASH_TABLE_SIZE;
-
- /* Set default value for unicast filter entries */
- plat->unicast_filter_entries = 1;
-
- /* Set the maxmtu to a default of JUMBO_LEN */
- plat->maxmtu = JUMBO_LEN;
-
plat->flags |= STMMAC_FLAG_VLAN_FAIL_Q_EN;
/* Use the last Rx queue */
@@ -1287,7 +1246,7 @@ static int intel_eth_pci_probe(struct pci_dev *pdev,
if (!intel_priv)
return -ENOMEM;
- plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
+ plat = stmmac_plat_dat_alloc(&pdev->dev);
if (!plat)
return -ENOMEM;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
index ca4035cbb55b..c05f85534f0c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
@@ -473,7 +473,7 @@ static int ipq806x_gmac_probe(struct platform_device *pdev)
return err;
}
- plat_dat->has_gmac = true;
+ plat_dat->core_type = DWMAC_CORE_GMAC;
plat_dat->bsp_priv = gmac;
plat_dat->set_clk_tx_rate = ipq806x_gmac_set_clk_tx_rate;
plat_dat->multicast_filter_bins = 0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
index 592aa9d636e5..107a7c84ace8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/of_irq.h>
#include "stmmac.h"
+#include "stmmac_libpci.h"
#include "dwmac_dma.h"
#include "dwmac1000.h"
@@ -92,31 +93,15 @@ static void loongson_default_data(struct pci_dev *pdev,
/* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
plat->clk_csr = STMMAC_CSR_20_35M;
- plat->has_gmac = 1;
+ plat->core_type = DWMAC_CORE_GMAC;
plat->force_sf_dma_mode = 1;
- /* Set default value for multicast hash bins */
+ /* Increase the default value for multicast hash bins */
plat->multicast_filter_bins = 256;
- /* Set default value for unicast filter entries */
- plat->unicast_filter_entries = 1;
-
- /* Set the maxmtu to a default of JUMBO_LEN */
- plat->maxmtu = JUMBO_LEN;
-
- /* Disable Priority config by default */
- plat->tx_queues_cfg[0].use_prio = false;
- plat->rx_queues_cfg[0].use_prio = false;
-
- /* Disable RX queues routing by default */
- plat->rx_queues_cfg[0].pkt_route = 0x0;
-
plat->clk_ref_rate = 125000000;
plat->clk_ptp_rate = 125000000;
- /* Default to phy auto-detection */
- plat->phy_addr = -1;
-
plat->dma_cfg->pbl = 32;
plat->dma_cfg->pblx8 = true;
@@ -140,8 +125,6 @@ static void loongson_default_data(struct pci_dev *pdev,
break;
default:
ld->multichan = 0;
- plat->tx_queues_to_use = 1;
- plat->rx_queues_to_use = 1;
break;
}
}
@@ -320,10 +303,9 @@ static int loongson_dwmac_dma_interrupt(struct stmmac_priv *priv,
return ret;
}
-static struct mac_device_info *loongson_dwmac_setup(void *apriv)
+static int loongson_dwmac_setup(void *apriv, struct mac_device_info *mac)
{
struct stmmac_priv *priv = apriv;
- struct mac_device_info *mac;
struct stmmac_dma_ops *dma;
struct loongson_data *ld;
struct pci_dev *pdev;
@@ -331,13 +313,9 @@ static struct mac_device_info *loongson_dwmac_setup(void *apriv)
ld = priv->plat->bsp_priv;
pdev = to_pci_dev(priv->device);
- mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
- if (!mac)
- return NULL;
-
dma = devm_kzalloc(priv->device, sizeof(*dma), GFP_KERNEL);
if (!dma)
- return NULL;
+ return -ENOMEM;
/* The Loongson GMAC and GNET devices are based on the DW GMAC
* v3.50a and v3.73a IP-cores. But the HW designers have changed
@@ -396,7 +374,7 @@ static struct mac_device_info *loongson_dwmac_setup(void *apriv)
mac->mii.clk_csr_shift = 2;
mac->mii.clk_csr_mask = GENMASK(5, 2);
- return mac;
+ return 0;
}
static int loongson_dwmac_msi_config(struct pci_dev *pdev,
@@ -525,37 +503,6 @@ static int loongson_dwmac_fix_reset(struct stmmac_priv *priv, void __iomem *ioad
10000, 2000000);
}
-static int loongson_dwmac_suspend(struct device *dev, void *bsp_priv)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- int ret;
-
- ret = pci_save_state(pdev);
- if (ret)
- return ret;
-
- pci_disable_device(pdev);
- pci_wake_from_d3(pdev, true);
- return 0;
-}
-
-static int loongson_dwmac_resume(struct device *dev, void *bsp_priv)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- int ret;
-
- pci_restore_state(pdev);
- pci_set_power_state(pdev, PCI_D0);
-
- ret = pci_enable_device(pdev);
- if (ret)
- return ret;
-
- pci_set_master(pdev);
-
- return 0;
-}
-
static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct plat_stmmacenet_data *plat;
@@ -564,7 +511,7 @@ static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id
struct loongson_data *ld;
int ret;
- plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
+ plat = stmmac_plat_dat_alloc(&pdev->dev);
if (!plat)
return -ENOMEM;
@@ -598,10 +545,10 @@ static int loongson_dwmac_probe(struct pci_dev *pdev, const struct pci_device_id
goto err_disable_device;
plat->bsp_priv = ld;
- plat->setup = loongson_dwmac_setup;
+ plat->mac_setup = loongson_dwmac_setup;
plat->fix_soc_reset = loongson_dwmac_fix_reset;
- plat->suspend = loongson_dwmac_suspend;
- plat->resume = loongson_dwmac_resume;
+ plat->suspend = stmmac_pci_plat_suspend;
+ plat->resume = stmmac_pci_plat_resume;
ld->dev = &pdev->dev;
ld->loongson_id = readl(res.addr + GMAC_VERSION) & 0xff;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c
index 32b5d1492e2e..de9aba756aac 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c
@@ -38,8 +38,6 @@
#define GMAC_SHUT BIT(6)
#define PHY_INTF_SELI GENMASK(30, 28)
-#define PHY_INTF_MII FIELD_PREP(PHY_INTF_SELI, 0)
-#define PHY_INTF_RMII FIELD_PREP(PHY_INTF_SELI, 4)
struct ls1x_dwmac {
struct plat_stmmacenet_data *plat_dat;
@@ -50,7 +48,7 @@ struct ls1x_dwmac {
struct ls1x_data {
int (*setup)(struct platform_device *pdev,
struct plat_stmmacenet_data *plat_dat);
- int (*init)(struct platform_device *pdev, void *bsp_priv);
+ int (*init)(struct device *dev, void *bsp_priv);
};
static int ls1b_dwmac_setup(struct platform_device *pdev,
@@ -81,7 +79,7 @@ static int ls1b_dwmac_setup(struct platform_device *pdev,
return 0;
}
-static int ls1b_dwmac_syscon_init(struct platform_device *pdev, void *priv)
+static int ls1b_dwmac_syscon_init(struct device *dev, void *priv)
{
struct ls1x_dwmac *dwmac = priv;
struct plat_stmmacenet_data *plat = dwmac->plat_dat;
@@ -100,7 +98,7 @@ static int ls1b_dwmac_syscon_init(struct platform_device *pdev, void *priv)
GMAC0_USE_TXCLK | GMAC0_USE_PWM01);
break;
default:
- dev_err(&pdev->dev, "Unsupported PHY mode %u\n",
+ dev_err(dev, "Unsupported PHY mode %u\n",
plat->phy_interface);
return -EOPNOTSUPP;
}
@@ -124,7 +122,7 @@ static int ls1b_dwmac_syscon_init(struct platform_device *pdev, void *priv)
GMAC1_USE_TXCLK | GMAC1_USE_PWM23);
break;
default:
- dev_err(&pdev->dev, "Unsupported PHY mode %u\n",
+ dev_err(dev, "Unsupported PHY mode %u\n",
plat->phy_interface);
return -EOPNOTSUPP;
}
@@ -135,27 +133,23 @@ static int ls1b_dwmac_syscon_init(struct platform_device *pdev, void *priv)
return 0;
}
-static int ls1c_dwmac_syscon_init(struct platform_device *pdev, void *priv)
+static int ls1c_dwmac_syscon_init(struct device *dev, void *priv)
{
struct ls1x_dwmac *dwmac = priv;
struct plat_stmmacenet_data *plat = dwmac->plat_dat;
struct regmap *regmap = dwmac->regmap;
+ int phy_intf_sel;
- switch (plat->phy_interface) {
- case PHY_INTERFACE_MODE_MII:
- regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI,
- PHY_INTF_MII);
- break;
- case PHY_INTERFACE_MODE_RMII:
- regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI,
- PHY_INTF_RMII);
- break;
- default:
- dev_err(&pdev->dev, "Unsupported PHY-mode %u\n",
+ phy_intf_sel = stmmac_get_phy_intf_sel(plat->phy_interface);
+ if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII) {
+ dev_err(dev, "Unsupported PHY-mode %u\n",
plat->phy_interface);
return -EOPNOTSUPP;
}
+ regmap_update_bits(regmap, LS1X_SYSCON1, PHY_INTF_SELI,
+ FIELD_PREP(PHY_INTF_SELI, phy_intf_sel));
regmap_update_bits(regmap, LS1X_SYSCON0, GMAC0_SHUT, 0);
return 0;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c
index 2562a6d036a2..c68d7de1f8ac 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c
@@ -21,16 +21,29 @@
/* Register defines for CREG syscon */
#define LPC18XX_CREG_CREG6 0x12c
-# define LPC18XX_CREG_CREG6_ETHMODE_MASK 0x7
-# define LPC18XX_CREG_CREG6_ETHMODE_MII 0x0
-# define LPC18XX_CREG_CREG6_ETHMODE_RMII 0x4
+# define LPC18XX_CREG_CREG6_ETHMODE_MASK GENMASK(2, 0)
+
+static int lpc18xx_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel)
+{
+ struct regmap *reg = bsp_priv;
+
+ if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII)
+ return -EINVAL;
+
+ regmap_update_bits(reg, LPC18XX_CREG_CREG6,
+ LPC18XX_CREG_CREG6_ETHMODE_MASK,
+ FIELD_PREP(LPC18XX_CREG_CREG6_ETHMODE_MASK,
+ phy_intf_sel));
+
+ return 0;
+}
static int lpc18xx_dwmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
struct stmmac_resources stmmac_res;
- struct regmap *reg;
- u8 ethmode;
+ struct regmap *regmap;
int ret;
ret = stmmac_get_platform_resources(pdev, &stmmac_res);
@@ -41,25 +54,16 @@ static int lpc18xx_dwmac_probe(struct platform_device *pdev)
if (IS_ERR(plat_dat))
return PTR_ERR(plat_dat);
- plat_dat->has_gmac = true;
+ plat_dat->core_type = DWMAC_CORE_GMAC;
- reg = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg");
- if (IS_ERR(reg)) {
+ regmap = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg");
+ if (IS_ERR(regmap)) {
dev_err(&pdev->dev, "syscon lookup failed\n");
- return PTR_ERR(reg);
- }
-
- if (plat_dat->phy_interface == PHY_INTERFACE_MODE_MII) {
- ethmode = LPC18XX_CREG_CREG6_ETHMODE_MII;
- } else if (plat_dat->phy_interface == PHY_INTERFACE_MODE_RMII) {
- ethmode = LPC18XX_CREG_CREG6_ETHMODE_RMII;
- } else {
- dev_err(&pdev->dev, "Only MII and RMII mode supported\n");
- return -EINVAL;
+ return PTR_ERR(regmap);
}
- regmap_update_bits(reg, LPC18XX_CREG_CREG6,
- LPC18XX_CREG_CREG6_ETHMODE_MASK, ethmode);
+ plat_dat->bsp_priv = regmap;
+ plat_dat->set_phy_intf_sel = lpc18xx_set_phy_intf_sel;
return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
index f1b36f0a401d..1f2d7d19ca56 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
@@ -17,9 +17,6 @@
/* Peri Configuration register for mt2712 */
#define PERI_ETH_PHY_INTF_SEL 0x418
-#define PHY_INTF_MII 0
-#define PHY_INTF_RGMII 1
-#define PHY_INTF_RMII 4
#define RMII_CLK_SRC_RXC BIT(4)
#define RMII_CLK_SRC_INTERNAL BIT(5)
@@ -88,7 +85,8 @@ struct mediatek_dwmac_plat_data {
};
struct mediatek_dwmac_variant {
- int (*dwmac_set_phy_interface)(struct mediatek_dwmac_plat_data *plat);
+ int (*dwmac_set_phy_interface)(struct mediatek_dwmac_plat_data *plat,
+ u8 phy_intf_sel);
int (*dwmac_set_delay)(struct mediatek_dwmac_plat_data *plat);
/* clock ids to be requested */
@@ -109,29 +107,16 @@ static const char * const mt8195_dwmac_clk_l[] = {
"axi", "apb", "mac_cg", "mac_main", "ptp_ref"
};
-static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat)
+static int mt2712_set_interface(struct mediatek_dwmac_plat_data *plat,
+ u8 phy_intf_sel)
{
- int rmii_clk_from_mac = plat->rmii_clk_from_mac ? RMII_CLK_SRC_INTERNAL : 0;
- int rmii_rxc = plat->rmii_rxc ? RMII_CLK_SRC_RXC : 0;
- u32 intf_val = 0;
+ u32 intf_val = phy_intf_sel;
- /* select phy interface in top control domain */
- switch (plat->phy_mode) {
- case PHY_INTERFACE_MODE_MII:
- intf_val |= PHY_INTF_MII;
- break;
- case PHY_INTERFACE_MODE_RMII:
- intf_val |= (PHY_INTF_RMII | rmii_rxc | rmii_clk_from_mac);
- break;
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_ID:
- intf_val |= PHY_INTF_RGMII;
- break;
- default:
- dev_err(plat->dev, "phy interface not supported\n");
- return -EINVAL;
+ if (phy_intf_sel == PHY_INTF_SEL_RMII) {
+ if (plat->rmii_clk_from_mac)
+ intf_val |= RMII_CLK_SRC_INTERNAL;
+ if (plat->rmii_rxc)
+ intf_val |= RMII_CLK_SRC_RXC;
}
regmap_write(plat->peri_regmap, PERI_ETH_PHY_INTF_SEL, intf_val);
@@ -288,30 +273,16 @@ static const struct mediatek_dwmac_variant mt2712_gmac_variant = {
.tx_delay_max = 17600,
};
-static int mt8195_set_interface(struct mediatek_dwmac_plat_data *plat)
+static int mt8195_set_interface(struct mediatek_dwmac_plat_data *plat,
+ u8 phy_intf_sel)
{
- int rmii_clk_from_mac = plat->rmii_clk_from_mac ? MT8195_RMII_CLK_SRC_INTERNAL : 0;
- int rmii_rxc = plat->rmii_rxc ? MT8195_RMII_CLK_SRC_RXC : 0;
- u32 intf_val = 0;
+ u32 intf_val = FIELD_PREP(MT8195_ETH_INTF_SEL, phy_intf_sel);
- /* select phy interface in top control domain */
- switch (plat->phy_mode) {
- case PHY_INTERFACE_MODE_MII:
- intf_val |= FIELD_PREP(MT8195_ETH_INTF_SEL, PHY_INTF_MII);
- break;
- case PHY_INTERFACE_MODE_RMII:
- intf_val |= (rmii_rxc | rmii_clk_from_mac);
- intf_val |= FIELD_PREP(MT8195_ETH_INTF_SEL, PHY_INTF_RMII);
- break;
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_ID:
- intf_val |= FIELD_PREP(MT8195_ETH_INTF_SEL, PHY_INTF_RGMII);
- break;
- default:
- dev_err(plat->dev, "phy interface not supported\n");
- return -EINVAL;
+ if (phy_intf_sel == PHY_INTF_SEL_RMII) {
+ if (plat->rmii_clk_from_mac)
+ intf_val |= MT8195_RMII_CLK_SRC_INTERNAL;
+ if (plat->rmii_rxc)
+ intf_val |= MT8195_RMII_CLK_SRC_RXC;
}
/* MT8195 only support external PHY */
@@ -527,10 +498,18 @@ static int mediatek_dwmac_init(struct device *dev, void *priv)
{
struct mediatek_dwmac_plat_data *plat = priv;
const struct mediatek_dwmac_variant *variant = plat->variant;
- int ret;
+ int phy_intf_sel, ret;
if (variant->dwmac_set_phy_interface) {
- ret = variant->dwmac_set_phy_interface(plat);
+ phy_intf_sel = stmmac_get_phy_intf_sel(plat->phy_mode);
+ if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
+ phy_intf_sel != PHY_INTF_SEL_RGMII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII) {
+ dev_err(plat->dev, "phy interface not supported\n");
+ return phy_intf_sel < 0 ? phy_intf_sel : -EINVAL;
+ }
+
+ ret = variant->dwmac_set_phy_interface(plat, phy_intf_sel);
if (ret) {
dev_err(dev, "failed to set phy interface, err = %d\n", ret);
return ret;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
index a50782994b97..e4d5c41294f4 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
@@ -26,8 +26,6 @@
#define PRG_ETH0_RGMII_MODE BIT(0)
#define PRG_ETH0_EXT_PHY_MODE_MASK GENMASK(2, 0)
-#define PRG_ETH0_EXT_RGMII_MODE 1
-#define PRG_ETH0_EXT_RMII_MODE 4
/* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
#define PRG_ETH0_CLK_M250_SEL_MASK GENMASK(4, 4)
@@ -238,28 +236,20 @@ static int meson8b_set_phy_mode(struct meson8b_dwmac *dwmac)
static int meson_axg_set_phy_mode(struct meson8b_dwmac *dwmac)
{
- switch (dwmac->phy_mode) {
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- /* enable RGMII mode */
- meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
- PRG_ETH0_EXT_PHY_MODE_MASK,
- PRG_ETH0_EXT_RGMII_MODE);
- break;
- case PHY_INTERFACE_MODE_RMII:
- /* disable RGMII mode -> enables RMII mode */
- meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
- PRG_ETH0_EXT_PHY_MODE_MASK,
- PRG_ETH0_EXT_RMII_MODE);
- break;
- default:
+ int phy_intf_sel;
+
+ phy_intf_sel = stmmac_get_phy_intf_sel(dwmac->phy_mode);
+ if (phy_intf_sel != PHY_INTF_SEL_RGMII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII) {
dev_err(dwmac->dev, "fail to set phy-mode %s\n",
phy_modes(dwmac->phy_mode));
- return -EINVAL;
+ return phy_intf_sel < 0 ? phy_intf_sel : -EINVAL;
}
+ meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_EXT_PHY_MODE_MASK,
+ FIELD_PREP(PRG_ETH0_EXT_PHY_MODE_MASK,
+ phy_intf_sel));
+
return 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index d8fd4d8f6ced..0826a7bd32ff 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -76,10 +76,6 @@
#define RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL BIT(6)
#define RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN BIT(5)
-/* MAC_CTRL_REG bits */
-#define ETHQOS_MAC_CTRL_SPEED_MODE BIT(14)
-#define ETHQOS_MAC_CTRL_PORT_SEL BIT(15)
-
/* EMAC_WRAPPER_SGMII_PHY_CNTRL1 bits */
#define SGMII_PHY_CNTRL1_SGMII_TX_TO_RX_LOOPBACK_EN BIT(3)
@@ -96,7 +92,6 @@ struct ethqos_emac_driver_data {
bool rgmii_config_loopback_en;
bool has_emac_ge_3;
const char *link_clk_name;
- bool has_integrated_pcs;
u32 dma_addr_width;
struct dwmac4_addrs dwmac4_addrs;
bool needs_sgmii_loopback;
@@ -121,27 +116,39 @@ struct qcom_ethqos {
bool needs_sgmii_loopback;
};
-static int rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
+static u32 rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
{
return readl(ethqos->rgmii_base + offset);
}
-static void rgmii_writel(struct qcom_ethqos *ethqos,
- int value, unsigned int offset)
+static void rgmii_writel(struct qcom_ethqos *ethqos, u32 value,
+ unsigned int offset)
{
writel(value, ethqos->rgmii_base + offset);
}
-static void rgmii_updatel(struct qcom_ethqos *ethqos,
- int mask, int val, unsigned int offset)
+static void rgmii_updatel(struct qcom_ethqos *ethqos, u32 mask, u32 val,
+ unsigned int offset)
{
- unsigned int temp;
+ u32 temp;
temp = rgmii_readl(ethqos, offset);
temp = (temp & ~(mask)) | val;
rgmii_writel(ethqos, temp, offset);
}
+static void rgmii_setmask(struct qcom_ethqos *ethqos, u32 mask,
+ unsigned int offset)
+{
+ rgmii_updatel(ethqos, mask, mask, offset);
+}
+
+static void rgmii_clrmask(struct qcom_ethqos *ethqos, u32 mask,
+ unsigned int offset)
+{
+ rgmii_updatel(ethqos, mask, 0, offset);
+}
+
static void rgmii_dump(void *priv)
{
struct qcom_ethqos *ethqos = priv;
@@ -199,8 +206,7 @@ qcom_ethqos_set_sgmii_loopback(struct qcom_ethqos *ethqos, bool enable)
static void ethqos_set_func_clk_en(struct qcom_ethqos *ethqos)
{
qcom_ethqos_set_sgmii_loopback(ethqos, true);
- rgmii_updatel(ethqos, RGMII_CONFIG_FUNC_CLK_EN,
- RGMII_CONFIG_FUNC_CLK_EN, RGMII_IO_MACRO_CONFIG);
+ rgmii_setmask(ethqos, RGMII_CONFIG_FUNC_CLK_EN, RGMII_IO_MACRO_CONFIG);
}
static const struct ethqos_emac_por emac_v2_3_0_por[] = {
@@ -282,7 +288,6 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = {
.rgmii_config_loopback_en = false,
.has_emac_ge_3 = true,
.link_clk_name = "phyaux",
- .has_integrated_pcs = true,
.needs_sgmii_loopback = true,
.dma_addr_width = 36,
.dwmac4_addrs = {
@@ -306,69 +311,55 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = {
static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
{
struct device *dev = &ethqos->pdev->dev;
- unsigned int val;
- int retry = 1000;
+ u32 val;
/* Set CDR_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CDR_EN,
- SDCC_DLL_CONFIG_CDR_EN, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_CDR_EN, SDCC_HC_REG_DLL_CONFIG);
/* Set CDR_EXT_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CDR_EXT_EN,
- SDCC_DLL_CONFIG_CDR_EXT_EN, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_CDR_EXT_EN,
+ SDCC_HC_REG_DLL_CONFIG);
/* Clear CK_OUT_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
- 0, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_clrmask(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
+ SDCC_HC_REG_DLL_CONFIG);
/* Set DLL_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_EN,
- SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG);
if (!ethqos->has_emac_ge_3) {
- rgmii_updatel(ethqos, SDCC_DLL_MCLK_GATING_EN,
- 0, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_clrmask(ethqos, SDCC_DLL_MCLK_GATING_EN,
+ SDCC_HC_REG_DLL_CONFIG);
- rgmii_updatel(ethqos, SDCC_DLL_CDR_FINE_PHASE,
- 0, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_clrmask(ethqos, SDCC_DLL_CDR_FINE_PHASE,
+ SDCC_HC_REG_DLL_CONFIG);
}
/* Wait for CK_OUT_EN clear */
- do {
- val = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG);
- val &= SDCC_DLL_CONFIG_CK_OUT_EN;
- if (!val)
- break;
- mdelay(1);
- retry--;
- } while (retry > 0);
- if (!retry)
+ if (read_poll_timeout_atomic(rgmii_readl, val,
+ !(val & SDCC_DLL_CONFIG_CK_OUT_EN),
+ 1000, 1000000, false,
+ ethqos, SDCC_HC_REG_DLL_CONFIG))
dev_err(dev, "Clear CK_OUT_EN timedout\n");
/* Set CK_OUT_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
- SDCC_DLL_CONFIG_CK_OUT_EN, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
+ SDCC_HC_REG_DLL_CONFIG);
/* Wait for CK_OUT_EN set */
- retry = 1000;
- do {
- val = rgmii_readl(ethqos, SDCC_HC_REG_DLL_CONFIG);
- val &= SDCC_DLL_CONFIG_CK_OUT_EN;
- if (val)
- break;
- mdelay(1);
- retry--;
- } while (retry > 0);
- if (!retry)
+ if (read_poll_timeout_atomic(rgmii_readl, val,
+ val & SDCC_DLL_CONFIG_CK_OUT_EN,
+ 1000, 1000000, false,
+ ethqos, SDCC_HC_REG_DLL_CONFIG))
dev_err(dev, "Set CK_OUT_EN timedout\n");
/* Set DDR_CAL_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_CAL_EN,
- SDCC_DLL_CONFIG2_DDR_CAL_EN, SDCC_HC_REG_DLL_CONFIG2);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG2_DDR_CAL_EN,
+ SDCC_HC_REG_DLL_CONFIG2);
if (!ethqos->has_emac_ge_3) {
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DLL_CLOCK_DIS,
- 0, SDCC_HC_REG_DLL_CONFIG2);
+ rgmii_clrmask(ethqos, SDCC_DLL_CONFIG2_DLL_CLOCK_DIS,
+ SDCC_HC_REG_DLL_CONFIG2);
rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_MCLK_FREQ_CALC,
0x1A << 10, SDCC_HC_REG_DLL_CONFIG2);
@@ -376,8 +367,7 @@ static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SEL,
BIT(2), SDCC_HC_REG_DLL_CONFIG2);
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW,
- SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW,
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG2_DDR_TRAFFIC_INIT_SW,
SDCC_HC_REG_DLL_CONFIG2);
}
@@ -398,8 +388,8 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
phase_shift = RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN;
/* Disable loopback mode */
- rgmii_updatel(ethqos, RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN,
+ RGMII_IO_MACRO_CONFIG2);
/* Determine if this platform wants loopback enabled after programming */
if (ethqos->rgmii_config_loopback_en)
@@ -408,29 +398,26 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
loopback = 0;
/* Select RGMII, write 0 to interface select */
- rgmii_updatel(ethqos, RGMII_CONFIG_INTF_SEL,
- 0, RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_INTF_SEL, RGMII_IO_MACRO_CONFIG);
switch (speed) {
case SPEED_1000:
- rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE,
- RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
- 0, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
- RGMII_CONFIG_POS_NEG_DATA_SEL,
+ rgmii_setmask(ethqos, RGMII_CONFIG_DDR_MODE,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_setmask(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP,
- RGMII_CONFIG_PROG_SWAP, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_setmask(ethqos, RGMII_CONFIG_PROG_SWAP,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+ RGMII_IO_MACRO_CONFIG2);
rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
phase_shift, RGMII_IO_MACRO_CONFIG2);
- rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
- 0, RGMII_IO_MACRO_CONFIG2);
- rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
- RGMII_CONFIG2_RX_PROG_SWAP,
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
+ RGMII_IO_MACRO_CONFIG2);
+ rgmii_setmask(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
RGMII_IO_MACRO_CONFIG2);
/* PRG_RCLK_DLY = TCXO period * TCXO_CYCLES_CNT / 2 * RX delay ns,
@@ -445,87 +432,78 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
rgmii_updatel(ethqos, SDCC_DDR_CONFIG_PRG_RCLK_DLY,
57, SDCC_HC_REG_DDR_CONFIG);
}
- rgmii_updatel(ethqos, SDCC_DDR_CONFIG_PRG_DLY_EN,
- SDCC_DDR_CONFIG_PRG_DLY_EN,
+ rgmii_setmask(ethqos, SDCC_DDR_CONFIG_PRG_DLY_EN,
SDCC_HC_REG_DDR_CONFIG);
rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN,
loopback, RGMII_IO_MACRO_CONFIG);
break;
case SPEED_100:
- rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE,
- RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
- RGMII_CONFIG_BYPASS_TX_ID_EN,
+ rgmii_setmask(ethqos, RGMII_CONFIG_DDR_MODE,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_setmask(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
- 0, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP,
- 0, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_PROG_SWAP,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+ RGMII_IO_MACRO_CONFIG2);
rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
phase_shift, RGMII_IO_MACRO_CONFIG2);
rgmii_updatel(ethqos, RGMII_CONFIG_MAX_SPD_PRG_2,
BIT(6), RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
+ RGMII_IO_MACRO_CONFIG2);
if (ethqos->has_emac_ge_3)
- rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
- RGMII_CONFIG2_RX_PROG_SWAP,
+ rgmii_setmask(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
RGMII_IO_MACRO_CONFIG2);
else
- rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
+ RGMII_IO_MACRO_CONFIG2);
/* Write 0x5 to PRG_RCLK_DLY_CODE */
rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
(BIT(29) | BIT(27)), SDCC_HC_REG_DDR_CONFIG);
- rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
- SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
+ rgmii_setmask(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
SDCC_HC_REG_DDR_CONFIG);
- rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
- SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
+ rgmii_setmask(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
SDCC_HC_REG_DDR_CONFIG);
rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN,
loopback, RGMII_IO_MACRO_CONFIG);
break;
case SPEED_10:
- rgmii_updatel(ethqos, RGMII_CONFIG_DDR_MODE,
- RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
- RGMII_CONFIG_BYPASS_TX_ID_EN,
+ rgmii_setmask(ethqos, RGMII_CONFIG_DDR_MODE,
RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
- 0, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG_PROG_SWAP,
- 0, RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_setmask(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_PROG_SWAP,
+ RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+ RGMII_IO_MACRO_CONFIG2);
rgmii_updatel(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
phase_shift, RGMII_IO_MACRO_CONFIG2);
rgmii_updatel(ethqos, RGMII_CONFIG_MAX_SPD_PRG_9,
BIT(12) | GENMASK(9, 8),
RGMII_IO_MACRO_CONFIG);
- rgmii_updatel(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_RSVD_CONFIG15,
+ RGMII_IO_MACRO_CONFIG2);
if (ethqos->has_emac_ge_3)
- rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
- RGMII_CONFIG2_RX_PROG_SWAP,
+ rgmii_setmask(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
RGMII_IO_MACRO_CONFIG2);
else
- rgmii_updatel(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
- 0, RGMII_IO_MACRO_CONFIG2);
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_RX_PROG_SWAP,
+ RGMII_IO_MACRO_CONFIG2);
/* Write 0x5 to PRG_RCLK_DLY_CODE */
rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_CODE,
(BIT(29) | BIT(27)), SDCC_HC_REG_DDR_CONFIG);
- rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
- SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
+ rgmii_setmask(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY,
SDCC_HC_REG_DDR_CONFIG);
- rgmii_updatel(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
- SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
+ rgmii_setmask(ethqos, SDCC_DDR_CONFIG_EXT_PRG_RCLK_DLY_EN,
SDCC_HC_REG_DDR_CONFIG);
rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN,
loopback, RGMII_IO_MACRO_CONFIG);
@@ -541,8 +519,8 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
static int ethqos_configure_rgmii(struct qcom_ethqos *ethqos, int speed)
{
struct device *dev = &ethqos->pdev->dev;
- volatile unsigned int dll_lock;
- unsigned int i, retry = 1000;
+ unsigned int i;
+ u32 val;
/* Reset to POR values and enable clk */
for (i = 0; i < ethqos->num_por; i++)
@@ -553,12 +531,12 @@ static int ethqos_configure_rgmii(struct qcom_ethqos *ethqos, int speed)
/* Initialize the DLL first */
/* Set DLL_RST */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_RST,
- SDCC_DLL_CONFIG_DLL_RST, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_DLL_RST,
+ SDCC_HC_REG_DLL_CONFIG);
/* Set PDN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_PDN,
- SDCC_DLL_CONFIG_PDN, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_PDN,
+ SDCC_HC_REG_DLL_CONFIG);
if (ethqos->has_emac_ge_3) {
if (speed == SPEED_1000) {
@@ -572,21 +550,18 @@ static int ethqos_configure_rgmii(struct qcom_ethqos *ethqos, int speed)
}
/* Clear DLL_RST */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_RST, 0,
- SDCC_HC_REG_DLL_CONFIG);
+ rgmii_clrmask(ethqos, SDCC_DLL_CONFIG_DLL_RST, SDCC_HC_REG_DLL_CONFIG);
/* Clear PDN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_PDN, 0,
- SDCC_HC_REG_DLL_CONFIG);
+ rgmii_clrmask(ethqos, SDCC_DLL_CONFIG_PDN, SDCC_HC_REG_DLL_CONFIG);
if (speed != SPEED_100 && speed != SPEED_10) {
/* Set DLL_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_DLL_EN,
- SDCC_DLL_CONFIG_DLL_EN, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_DLL_EN,
+ SDCC_HC_REG_DLL_CONFIG);
/* Set CK_OUT_EN */
- rgmii_updatel(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
- SDCC_DLL_CONFIG_CK_OUT_EN,
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_CK_OUT_EN,
SDCC_HC_REG_DLL_CONFIG);
/* Set USR_CTL bit 26 with mask of 3 bits */
@@ -595,14 +570,10 @@ static int ethqos_configure_rgmii(struct qcom_ethqos *ethqos, int speed)
SDCC_USR_CTL);
/* wait for DLL LOCK */
- do {
- mdelay(1);
- dll_lock = rgmii_readl(ethqos, SDC4_STATUS);
- if (dll_lock & SDC4_STATUS_DLL_LOCK)
- break;
- retry--;
- } while (retry > 0);
- if (!retry)
+ if (read_poll_timeout_atomic(rgmii_readl, val,
+ val & SDC4_STATUS_DLL_LOCK,
+ 1000, 1000000, true,
+ ethqos, SDC4_STATUS))
dev_err(dev, "Timeout while waiting for DLL lock\n");
}
@@ -624,7 +595,7 @@ static void ethqos_set_serdes_speed(struct qcom_ethqos *ethqos, int speed)
static void ethqos_pcs_set_inband(struct stmmac_priv *priv, bool enable)
{
- stmmac_pcs_ctrl_ane(priv, enable, 0, 0);
+ stmmac_pcs_ctrl_ane(priv, enable, 0);
}
/* On interface toggle MAC registers gets reset.
@@ -634,35 +605,25 @@ static int ethqos_configure_sgmii(struct qcom_ethqos *ethqos, int speed)
{
struct net_device *dev = platform_get_drvdata(ethqos->pdev);
struct stmmac_priv *priv = netdev_priv(dev);
- int val;
-
- val = readl(ethqos->mac_base + MAC_CTRL_REG);
switch (speed) {
case SPEED_2500:
- val &= ~ETHQOS_MAC_CTRL_PORT_SEL;
- rgmii_updatel(ethqos, RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
- RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
+ rgmii_setmask(ethqos, RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
RGMII_IO_MACRO_CONFIG2);
ethqos_set_serdes_speed(ethqos, SPEED_2500);
ethqos_pcs_set_inband(priv, false);
break;
case SPEED_1000:
- val &= ~ETHQOS_MAC_CTRL_PORT_SEL;
- rgmii_updatel(ethqos, RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
- RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
+ rgmii_setmask(ethqos, RGMII_CONFIG2_RGMII_CLK_SEL_CFG,
RGMII_IO_MACRO_CONFIG2);
ethqos_set_serdes_speed(ethqos, SPEED_1000);
ethqos_pcs_set_inband(priv, true);
break;
case SPEED_100:
- val |= ETHQOS_MAC_CTRL_PORT_SEL | ETHQOS_MAC_CTRL_SPEED_MODE;
ethqos_set_serdes_speed(ethqos, SPEED_1000);
ethqos_pcs_set_inband(priv, true);
break;
case SPEED_10:
- val |= ETHQOS_MAC_CTRL_PORT_SEL;
- val &= ~ETHQOS_MAC_CTRL_SPEED_MODE;
rgmii_updatel(ethqos, RGMII_CONFIG_SGMII_CLK_DVDR,
FIELD_PREP(RGMII_CONFIG_SGMII_CLK_DVDR,
SGMII_10M_RX_CLK_DVDR),
@@ -672,9 +633,7 @@ static int ethqos_configure_sgmii(struct qcom_ethqos *ethqos, int speed)
break;
}
- writel(val, ethqos->mac_base + MAC_CTRL_REG);
-
- return val;
+ return 0;
}
static int ethqos_configure(struct qcom_ethqos *ethqos, int speed)
@@ -848,7 +807,7 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
plat_dat->fix_mac_speed = ethqos_fix_mac_speed;
plat_dat->dump_debug_regs = rgmii_dump;
plat_dat->ptp_clk_freq_config = ethqos_ptp_clk_freq_config;
- plat_dat->has_gmac4 = 1;
+ plat_dat->core_type = DWMAC_CORE_GMAC4;
if (ethqos->has_emac_ge_3)
plat_dat->dwmac4_addrs = &data->dwmac4_addrs;
plat_dat->pmt = 1;
@@ -856,8 +815,6 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
plat_dat->flags |= STMMAC_FLAG_TSO_EN;
if (of_device_is_compatible(np, "qcom,qcs404-ethqos"))
plat_dat->flags |= STMMAC_FLAG_RX_CLK_RUNS_IN_LPI;
- if (data->has_integrated_pcs)
- plat_dat->flags |= STMMAC_FLAG_HAS_INTEGRATED_PCS;
if (data->dma_addr_width)
plat_dat->host_dma_width = data->dma_addr_width;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
index bc7bb975803c..be7f5eb2cdcf 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
@@ -91,7 +91,7 @@ static struct phylink_pcs *renesas_gmac_select_pcs(struct stmmac_priv *priv,
return priv->hw->phylink_pcs;
}
-static int renesas_gbeth_init(struct platform_device *pdev, void *priv)
+static int renesas_gbeth_init(struct device *dev, void *priv)
{
struct plat_stmmacenet_data *plat_dat;
struct renesas_gbeth *gbeth = priv;
@@ -113,7 +113,7 @@ static int renesas_gbeth_init(struct platform_device *pdev, void *priv)
return ret;
}
-static void renesas_gbeth_exit(struct platform_device *pdev, void *priv)
+static void renesas_gbeth_exit(struct device *dev, void *priv)
{
struct plat_stmmacenet_data *plat_dat;
struct renesas_gbeth *gbeth = priv;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index 51ea0caf16c1..0a95f54e725e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -149,11 +149,13 @@ static int rk_set_clk_mac_speed(struct rk_priv_data *bsp_priv,
return clk_set_rate(clk_mac_speed, rate);
}
-#define HIWORD_UPDATE(val, mask, shift) \
- (FIELD_PREP_WM16((mask) << (shift), (val)))
+#define GRF_FIELD(hi, lo, val) \
+ FIELD_PREP_WM16(GENMASK_U16(hi, lo), val)
+#define GRF_FIELD_CONST(hi, lo, val) \
+ FIELD_PREP_WM16_CONST(GENMASK_U16(hi, lo), val)
-#define GRF_BIT(nr) (BIT(nr) | BIT(nr+16))
-#define GRF_CLR_BIT(nr) (BIT(nr+16))
+#define GRF_BIT(nr) (BIT(nr) | BIT(nr+16))
+#define GRF_CLR_BIT(nr) (BIT(nr+16))
#define DELAY_ENABLE(soc, tx, rx) \
(((tx) ? soc##_GMAC_TXCLK_DLY_ENABLE : soc##_GMAC_TXCLK_DLY_DISABLE) | \
@@ -167,9 +169,9 @@ static int rk_set_clk_mac_speed(struct rk_priv_data *bsp_priv,
#define RK_MACPHY_ENABLE GRF_BIT(0)
#define RK_MACPHY_DISABLE GRF_CLR_BIT(0)
#define RK_MACPHY_CFG_CLK_50M GRF_BIT(14)
-#define RK_GMAC2PHY_RMII_MODE (GRF_BIT(6) | GRF_CLR_BIT(7))
-#define RK_GRF_CON2_MACPHY_ID HIWORD_UPDATE(0x1234, 0xffff, 0)
-#define RK_GRF_CON3_MACPHY_ID HIWORD_UPDATE(0x35, 0x3f, 0)
+#define RK_GMAC2PHY_RMII_MODE GRF_FIELD(7, 6, 1)
+#define RK_GRF_CON2_MACPHY_ID GRF_FIELD(15, 0, 0x1234)
+#define RK_GRF_CON3_MACPHY_ID GRF_FIELD(5, 0, 0x35)
static void rk_gmac_integrated_ephy_powerup(struct rk_priv_data *priv)
{
@@ -203,7 +205,7 @@ static void rk_gmac_integrated_ephy_powerdown(struct rk_priv_data *priv)
#define RK_FEPHY_SHUTDOWN GRF_BIT(1)
#define RK_FEPHY_POWERUP GRF_CLR_BIT(1)
#define RK_FEPHY_INTERNAL_RMII_SEL GRF_BIT(6)
-#define RK_FEPHY_24M_CLK_SEL (GRF_BIT(8) | GRF_BIT(9))
+#define RK_FEPHY_24M_CLK_SEL GRF_FIELD(9, 8, 3)
#define RK_FEPHY_PHY_ID GRF_BIT(11)
static void rk_gmac_integrated_fephy_powerup(struct rk_priv_data *priv,
@@ -232,15 +234,14 @@ static void rk_gmac_integrated_fephy_powerdown(struct rk_priv_data *priv,
#define PX30_GRF_GMAC_CON1 0x0904
/* PX30_GRF_GMAC_CON1 */
-#define PX30_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | \
- GRF_BIT(6))
+#define PX30_GMAC_PHY_INTF_SEL(val) GRF_FIELD(6, 4, val)
#define PX30_GMAC_SPEED_10M GRF_CLR_BIT(2)
#define PX30_GMAC_SPEED_100M GRF_BIT(2)
static void px30_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, PX30_GRF_GMAC_CON1,
- PX30_GMAC_PHY_INTF_SEL_RMII);
+ PX30_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII));
}
static int px30_set_speed(struct rk_priv_data *bsp_priv,
@@ -285,23 +286,20 @@ static const struct rk_gmac_ops px30_ops = {
#define RK3128_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14)
#define RK3128_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
#define RK3128_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
-#define RK3128_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7)
-#define RK3128_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3128_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(13, 7, val)
+#define RK3128_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
/* RK3128_GRF_MAC_CON1 */
-#define RK3128_GMAC_PHY_INTF_SEL_RGMII \
- (GRF_BIT(6) | GRF_CLR_BIT(7) | GRF_CLR_BIT(8))
-#define RK3128_GMAC_PHY_INTF_SEL_RMII \
- (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | GRF_BIT(8))
+#define RK3128_GMAC_PHY_INTF_SEL(val) GRF_FIELD(8, 6, val)
#define RK3128_GMAC_FLOW_CTRL GRF_BIT(9)
#define RK3128_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9)
#define RK3128_GMAC_SPEED_10M GRF_CLR_BIT(10)
#define RK3128_GMAC_SPEED_100M GRF_BIT(10)
#define RK3128_GMAC_RMII_CLK_25M GRF_BIT(11)
#define RK3128_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11)
-#define RK3128_GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13))
-#define RK3128_GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13))
-#define RK3128_GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13))
+#define RK3128_GMAC_CLK_125M GRF_FIELD_CONST(13, 12, 0)
+#define RK3128_GMAC_CLK_25M GRF_FIELD_CONST(13, 12, 3)
+#define RK3128_GMAC_CLK_2_5M GRF_FIELD_CONST(13, 12, 2)
#define RK3128_GMAC_RMII_MODE GRF_BIT(14)
#define RK3128_GMAC_RMII_MODE_CLR GRF_CLR_BIT(14)
@@ -309,7 +307,7 @@ static void rk3128_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RK3128_GRF_MAC_CON1,
- RK3128_GMAC_PHY_INTF_SEL_RGMII |
+ RK3128_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3128_GMAC_RMII_MODE_CLR);
regmap_write(bsp_priv->grf, RK3128_GRF_MAC_CON0,
DELAY_ENABLE(RK3128, tx_delay, rx_delay) |
@@ -320,7 +318,8 @@ static void rk3128_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rk3128_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RK3128_GRF_MAC_CON1,
- RK3128_GMAC_PHY_INTF_SEL_RMII | RK3128_GMAC_RMII_MODE);
+ RK3128_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII) |
+ RK3128_GMAC_RMII_MODE);
}
static const struct rk_reg_speed_data rk3128_reg_speed_data = {
@@ -350,23 +349,20 @@ static const struct rk_gmac_ops rk3128_ops = {
#define RK3228_GRF_CON_MUX 0x50
/* RK3228_GRF_MAC_CON0 */
-#define RK3228_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7)
-#define RK3228_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3228_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(13, 7, val)
+#define RK3228_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
/* RK3228_GRF_MAC_CON1 */
-#define RK3228_GMAC_PHY_INTF_SEL_RGMII \
- (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
-#define RK3228_GMAC_PHY_INTF_SEL_RMII \
- (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RK3228_GMAC_PHY_INTF_SEL(val) GRF_FIELD(6, 4, val)
#define RK3228_GMAC_FLOW_CTRL GRF_BIT(3)
#define RK3228_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
#define RK3228_GMAC_SPEED_10M GRF_CLR_BIT(2)
#define RK3228_GMAC_SPEED_100M GRF_BIT(2)
#define RK3228_GMAC_RMII_CLK_25M GRF_BIT(7)
#define RK3228_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(7)
-#define RK3228_GMAC_CLK_125M (GRF_CLR_BIT(8) | GRF_CLR_BIT(9))
-#define RK3228_GMAC_CLK_25M (GRF_BIT(8) | GRF_BIT(9))
-#define RK3228_GMAC_CLK_2_5M (GRF_CLR_BIT(8) | GRF_BIT(9))
+#define RK3228_GMAC_CLK_125M GRF_FIELD_CONST(9, 8, 0)
+#define RK3228_GMAC_CLK_25M GRF_FIELD_CONST(9, 8, 3)
+#define RK3228_GMAC_CLK_2_5M GRF_FIELD_CONST(9, 8, 2)
#define RK3228_GMAC_RMII_MODE GRF_BIT(10)
#define RK3228_GMAC_RMII_MODE_CLR GRF_CLR_BIT(10)
#define RK3228_GMAC_TXCLK_DLY_ENABLE GRF_BIT(0)
@@ -381,7 +377,7 @@ static void rk3228_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1,
- RK3228_GMAC_PHY_INTF_SEL_RGMII |
+ RK3228_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3228_GMAC_RMII_MODE_CLR |
DELAY_ENABLE(RK3228, tx_delay, rx_delay));
@@ -393,7 +389,7 @@ static void rk3228_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rk3228_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1,
- RK3228_GMAC_PHY_INTF_SEL_RMII |
+ RK3228_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII) |
RK3228_GMAC_RMII_MODE);
/* set MAC to RMII mode */
@@ -435,19 +431,16 @@ static const struct rk_gmac_ops rk3228_ops = {
#define RK3288_GRF_SOC_CON3 0x0250
/*RK3288_GRF_SOC_CON1*/
-#define RK3288_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | \
- GRF_CLR_BIT(8))
-#define RK3288_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | \
- GRF_BIT(8))
+#define RK3288_GMAC_PHY_INTF_SEL(val) GRF_FIELD(8, 6, val)
#define RK3288_GMAC_FLOW_CTRL GRF_BIT(9)
#define RK3288_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9)
#define RK3288_GMAC_SPEED_10M GRF_CLR_BIT(10)
#define RK3288_GMAC_SPEED_100M GRF_BIT(10)
#define RK3288_GMAC_RMII_CLK_25M GRF_BIT(11)
#define RK3288_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11)
-#define RK3288_GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13))
-#define RK3288_GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13))
-#define RK3288_GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13))
+#define RK3288_GMAC_CLK_125M GRF_FIELD_CONST(13, 12, 0)
+#define RK3288_GMAC_CLK_25M GRF_FIELD_CONST(13, 12, 3)
+#define RK3288_GMAC_CLK_2_5M GRF_FIELD_CONST(13, 12, 2)
#define RK3288_GMAC_RMII_MODE GRF_BIT(14)
#define RK3288_GMAC_RMII_MODE_CLR GRF_CLR_BIT(14)
@@ -456,14 +449,14 @@ static const struct rk_gmac_ops rk3228_ops = {
#define RK3288_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14)
#define RK3288_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
#define RK3288_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
-#define RK3288_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7)
-#define RK3288_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3288_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(13, 7, val)
+#define RK3288_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
static void rk3288_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
- RK3288_GMAC_PHY_INTF_SEL_RGMII |
+ RK3288_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3288_GMAC_RMII_MODE_CLR);
regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
DELAY_ENABLE(RK3288, tx_delay, rx_delay) |
@@ -474,7 +467,8 @@ static void rk3288_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rk3288_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
- RK3288_GMAC_PHY_INTF_SEL_RMII | RK3288_GMAC_RMII_MODE);
+ RK3288_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII) |
+ RK3288_GMAC_RMII_MODE);
}
static const struct rk_reg_speed_data rk3288_reg_speed_data = {
@@ -501,8 +495,7 @@ static const struct rk_gmac_ops rk3288_ops = {
#define RK3308_GRF_MAC_CON0 0x04a0
/* RK3308_GRF_MAC_CON0 */
-#define RK3308_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(2) | GRF_CLR_BIT(3) | \
- GRF_BIT(4))
+#define RK3308_GMAC_PHY_INTF_SEL(val) GRF_FIELD(4, 2, val)
#define RK3308_GMAC_FLOW_CTRL GRF_BIT(3)
#define RK3308_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
#define RK3308_GMAC_SPEED_10M GRF_CLR_BIT(0)
@@ -511,7 +504,7 @@ static const struct rk_gmac_ops rk3288_ops = {
static void rk3308_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RK3308_GRF_MAC_CON0,
- RK3308_GMAC_PHY_INTF_SEL_RMII);
+ RK3308_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII));
}
static const struct rk_reg_speed_data rk3308_reg_speed_data = {
@@ -537,23 +530,20 @@ static const struct rk_gmac_ops rk3308_ops = {
#define RK3328_GRF_MACPHY_CON1 0xb04
/* RK3328_GRF_MAC_CON0 */
-#define RK3328_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7)
-#define RK3328_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3328_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(13, 7, val)
+#define RK3328_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
/* RK3328_GRF_MAC_CON1 */
-#define RK3328_GMAC_PHY_INTF_SEL_RGMII \
- (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
-#define RK3328_GMAC_PHY_INTF_SEL_RMII \
- (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RK3328_GMAC_PHY_INTF_SEL(val) GRF_FIELD(6, 4, val)
#define RK3328_GMAC_FLOW_CTRL GRF_BIT(3)
#define RK3328_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
#define RK3328_GMAC_SPEED_10M GRF_CLR_BIT(2)
#define RK3328_GMAC_SPEED_100M GRF_BIT(2)
#define RK3328_GMAC_RMII_CLK_25M GRF_BIT(7)
#define RK3328_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(7)
-#define RK3328_GMAC_CLK_125M (GRF_CLR_BIT(11) | GRF_CLR_BIT(12))
-#define RK3328_GMAC_CLK_25M (GRF_BIT(11) | GRF_BIT(12))
-#define RK3328_GMAC_CLK_2_5M (GRF_CLR_BIT(11) | GRF_BIT(12))
+#define RK3328_GMAC_CLK_125M GRF_FIELD_CONST(12, 11, 0)
+#define RK3328_GMAC_CLK_25M GRF_FIELD_CONST(12, 11, 3)
+#define RK3328_GMAC_CLK_2_5M GRF_FIELD_CONST(12, 11, 2)
#define RK3328_GMAC_RMII_MODE GRF_BIT(9)
#define RK3328_GMAC_RMII_MODE_CLR GRF_CLR_BIT(9)
#define RK3328_GMAC_TXCLK_DLY_ENABLE GRF_BIT(0)
@@ -566,7 +556,7 @@ static void rk3328_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1,
- RK3328_GMAC_PHY_INTF_SEL_RGMII |
+ RK3328_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3328_GMAC_RMII_MODE_CLR |
RK3328_GMAC_RXCLK_DLY_ENABLE |
RK3328_GMAC_TXCLK_DLY_ENABLE);
@@ -584,7 +574,7 @@ static void rk3328_set_to_rmii(struct rk_priv_data *bsp_priv)
RK3328_GRF_MAC_CON1;
regmap_write(bsp_priv->grf, reg,
- RK3328_GMAC_PHY_INTF_SEL_RMII |
+ RK3328_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII) |
RK3328_GMAC_RMII_MODE);
}
@@ -630,19 +620,16 @@ static const struct rk_gmac_ops rk3328_ops = {
#define RK3366_GRF_SOC_CON7 0x041c
/* RK3366_GRF_SOC_CON6 */
-#define RK3366_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(9) | GRF_CLR_BIT(10) | \
- GRF_CLR_BIT(11))
-#define RK3366_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \
- GRF_BIT(11))
+#define RK3366_GMAC_PHY_INTF_SEL(val) GRF_FIELD(11, 9, val)
#define RK3366_GMAC_FLOW_CTRL GRF_BIT(8)
#define RK3366_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(8)
#define RK3366_GMAC_SPEED_10M GRF_CLR_BIT(7)
#define RK3366_GMAC_SPEED_100M GRF_BIT(7)
#define RK3366_GMAC_RMII_CLK_25M GRF_BIT(3)
#define RK3366_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(3)
-#define RK3366_GMAC_CLK_125M (GRF_CLR_BIT(4) | GRF_CLR_BIT(5))
-#define RK3366_GMAC_CLK_25M (GRF_BIT(4) | GRF_BIT(5))
-#define RK3366_GMAC_CLK_2_5M (GRF_CLR_BIT(4) | GRF_BIT(5))
+#define RK3366_GMAC_CLK_125M GRF_FIELD_CONST(5, 4, 0)
+#define RK3366_GMAC_CLK_25M GRF_FIELD_CONST(5, 4, 3)
+#define RK3366_GMAC_CLK_2_5M GRF_FIELD_CONST(5, 4, 2)
#define RK3366_GMAC_RMII_MODE GRF_BIT(6)
#define RK3366_GMAC_RMII_MODE_CLR GRF_CLR_BIT(6)
@@ -651,14 +638,14 @@ static const struct rk_gmac_ops rk3328_ops = {
#define RK3366_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7)
#define RK3366_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
#define RK3366_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
-#define RK3366_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
-#define RK3366_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3366_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(14, 8, val)
+#define RK3366_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
static void rk3366_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
- RK3366_GMAC_PHY_INTF_SEL_RGMII |
+ RK3366_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3366_GMAC_RMII_MODE_CLR);
regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON7,
DELAY_ENABLE(RK3366, tx_delay, rx_delay) |
@@ -669,7 +656,8 @@ static void rk3366_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rk3366_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
- RK3366_GMAC_PHY_INTF_SEL_RMII | RK3366_GMAC_RMII_MODE);
+ RK3366_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII) |
+ RK3366_GMAC_RMII_MODE);
}
static const struct rk_reg_speed_data rk3366_reg_speed_data = {
@@ -697,19 +685,16 @@ static const struct rk_gmac_ops rk3366_ops = {
#define RK3368_GRF_SOC_CON16 0x0440
/* RK3368_GRF_SOC_CON15 */
-#define RK3368_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(9) | GRF_CLR_BIT(10) | \
- GRF_CLR_BIT(11))
-#define RK3368_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \
- GRF_BIT(11))
+#define RK3368_GMAC_PHY_INTF_SEL(val) GRF_FIELD(11, 9, val)
#define RK3368_GMAC_FLOW_CTRL GRF_BIT(8)
#define RK3368_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(8)
#define RK3368_GMAC_SPEED_10M GRF_CLR_BIT(7)
#define RK3368_GMAC_SPEED_100M GRF_BIT(7)
#define RK3368_GMAC_RMII_CLK_25M GRF_BIT(3)
#define RK3368_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(3)
-#define RK3368_GMAC_CLK_125M (GRF_CLR_BIT(4) | GRF_CLR_BIT(5))
-#define RK3368_GMAC_CLK_25M (GRF_BIT(4) | GRF_BIT(5))
-#define RK3368_GMAC_CLK_2_5M (GRF_CLR_BIT(4) | GRF_BIT(5))
+#define RK3368_GMAC_CLK_125M GRF_FIELD_CONST(5, 4, 0)
+#define RK3368_GMAC_CLK_25M GRF_FIELD_CONST(5, 4, 3)
+#define RK3368_GMAC_CLK_2_5M GRF_FIELD_CONST(5, 4, 2)
#define RK3368_GMAC_RMII_MODE GRF_BIT(6)
#define RK3368_GMAC_RMII_MODE_CLR GRF_CLR_BIT(6)
@@ -718,14 +703,14 @@ static const struct rk_gmac_ops rk3366_ops = {
#define RK3368_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7)
#define RK3368_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
#define RK3368_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
-#define RK3368_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
-#define RK3368_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3368_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(14, 8, val)
+#define RK3368_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
static void rk3368_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
- RK3368_GMAC_PHY_INTF_SEL_RGMII |
+ RK3368_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3368_GMAC_RMII_MODE_CLR);
regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON16,
DELAY_ENABLE(RK3368, tx_delay, rx_delay) |
@@ -736,7 +721,8 @@ static void rk3368_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rk3368_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
- RK3368_GMAC_PHY_INTF_SEL_RMII | RK3368_GMAC_RMII_MODE);
+ RK3368_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII) |
+ RK3368_GMAC_RMII_MODE);
}
static const struct rk_reg_speed_data rk3368_reg_speed_data = {
@@ -764,19 +750,16 @@ static const struct rk_gmac_ops rk3368_ops = {
#define RK3399_GRF_SOC_CON6 0xc218
/* RK3399_GRF_SOC_CON5 */
-#define RK3399_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(9) | GRF_CLR_BIT(10) | \
- GRF_CLR_BIT(11))
-#define RK3399_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \
- GRF_BIT(11))
+#define RK3399_GMAC_PHY_INTF_SEL(val) GRF_FIELD(11, 9, val)
#define RK3399_GMAC_FLOW_CTRL GRF_BIT(8)
#define RK3399_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(8)
#define RK3399_GMAC_SPEED_10M GRF_CLR_BIT(7)
#define RK3399_GMAC_SPEED_100M GRF_BIT(7)
#define RK3399_GMAC_RMII_CLK_25M GRF_BIT(3)
#define RK3399_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(3)
-#define RK3399_GMAC_CLK_125M (GRF_CLR_BIT(4) | GRF_CLR_BIT(5))
-#define RK3399_GMAC_CLK_25M (GRF_BIT(4) | GRF_BIT(5))
-#define RK3399_GMAC_CLK_2_5M (GRF_CLR_BIT(4) | GRF_BIT(5))
+#define RK3399_GMAC_CLK_125M GRF_FIELD_CONST(5, 4, 0)
+#define RK3399_GMAC_CLK_25M GRF_FIELD_CONST(5, 4, 3)
+#define RK3399_GMAC_CLK_2_5M GRF_FIELD_CONST(5, 4, 2)
#define RK3399_GMAC_RMII_MODE GRF_BIT(6)
#define RK3399_GMAC_RMII_MODE_CLR GRF_CLR_BIT(6)
@@ -785,14 +768,14 @@ static const struct rk_gmac_ops rk3368_ops = {
#define RK3399_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7)
#define RK3399_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
#define RK3399_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
-#define RK3399_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
-#define RK3399_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3399_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(14, 8, val)
+#define RK3399_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
static void rk3399_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
- RK3399_GMAC_PHY_INTF_SEL_RGMII |
+ RK3399_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3399_GMAC_RMII_MODE_CLR);
regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON6,
DELAY_ENABLE(RK3399, tx_delay, rx_delay) |
@@ -803,7 +786,8 @@ static void rk3399_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rk3399_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
- RK3399_GMAC_PHY_INTF_SEL_RMII | RK3399_GMAC_RMII_MODE);
+ RK3399_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII) |
+ RK3399_GMAC_RMII_MODE);
}
static const struct rk_reg_speed_data rk3399_reg_speed_data = {
@@ -827,6 +811,69 @@ static const struct rk_gmac_ops rk3399_ops = {
.set_speed = rk3399_set_speed,
};
+#define RK3506_GRF_SOC_CON8 0x0020
+#define RK3506_GRF_SOC_CON11 0x002c
+
+#define RK3506_GMAC_RMII_MODE GRF_BIT(1)
+
+#define RK3506_GMAC_CLK_RMII_DIV2 GRF_BIT(3)
+#define RK3506_GMAC_CLK_RMII_DIV20 GRF_CLR_BIT(3)
+
+#define RK3506_GMAC_CLK_SELECT_CRU GRF_CLR_BIT(5)
+#define RK3506_GMAC_CLK_SELECT_IO GRF_BIT(5)
+
+#define RK3506_GMAC_CLK_RMII_GATE GRF_BIT(2)
+#define RK3506_GMAC_CLK_RMII_NOGATE GRF_CLR_BIT(2)
+
+static void rk3506_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+ unsigned int id = bsp_priv->id, offset;
+
+ offset = (id == 1) ? RK3506_GRF_SOC_CON11 : RK3506_GRF_SOC_CON8;
+ regmap_write(bsp_priv->grf, offset, RK3506_GMAC_RMII_MODE);
+}
+
+static const struct rk_reg_speed_data rk3506_reg_speed_data = {
+ .rmii_10 = RK3506_GMAC_CLK_RMII_DIV20,
+ .rmii_100 = RK3506_GMAC_CLK_RMII_DIV2,
+};
+
+static int rk3506_set_speed(struct rk_priv_data *bsp_priv,
+ phy_interface_t interface, int speed)
+{
+ unsigned int id = bsp_priv->id, offset;
+
+ offset = (id == 1) ? RK3506_GRF_SOC_CON11 : RK3506_GRF_SOC_CON8;
+ return rk_set_reg_speed(bsp_priv, &rk3506_reg_speed_data,
+ offset, interface, speed);
+}
+
+static void rk3506_set_clock_selection(struct rk_priv_data *bsp_priv,
+ bool input, bool enable)
+{
+ unsigned int value, offset, id = bsp_priv->id;
+
+ offset = (id == 1) ? RK3506_GRF_SOC_CON11 : RK3506_GRF_SOC_CON8;
+
+ value = input ? RK3506_GMAC_CLK_SELECT_IO :
+ RK3506_GMAC_CLK_SELECT_CRU;
+ value |= enable ? RK3506_GMAC_CLK_RMII_NOGATE :
+ RK3506_GMAC_CLK_RMII_GATE;
+ regmap_write(bsp_priv->grf, offset, value);
+}
+
+static const struct rk_gmac_ops rk3506_ops = {
+ .set_to_rmii = rk3506_set_to_rmii,
+ .set_speed = rk3506_set_speed,
+ .set_clock_selection = rk3506_set_clock_selection,
+ .regs_valid = true,
+ .regs = {
+ 0xff4c8000, /* gmac0 */
+ 0xff4d0000, /* gmac1 */
+ 0x0, /* sentinel */
+ },
+};
+
#define RK3528_VO_GRF_GMAC_CON 0x0018
#define RK3528_VO_GRF_MACPHY_CON0 0x001c
#define RK3528_VO_GRF_MACPHY_CON1 0x0020
@@ -838,8 +885,8 @@ static const struct rk_gmac_ops rk3399_ops = {
#define RK3528_GMAC_TXCLK_DLY_ENABLE GRF_BIT(14)
#define RK3528_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14)
-#define RK3528_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 8)
-#define RK3528_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 0)
+#define RK3528_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(15, 8, val)
+#define RK3528_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(7, 0, val)
#define RK3528_GMAC0_PHY_INTF_SEL_RMII GRF_BIT(1)
#define RK3528_GMAC1_PHY_INTF_SEL_RGMII GRF_CLR_BIT(8)
@@ -853,9 +900,9 @@ static const struct rk_gmac_ops rk3399_ops = {
#define RK3528_GMAC1_CLK_RMII_DIV2 GRF_BIT(10)
#define RK3528_GMAC1_CLK_RMII_DIV20 GRF_CLR_BIT(10)
-#define RK3528_GMAC1_CLK_RGMII_DIV1 (GRF_CLR_BIT(11) | GRF_CLR_BIT(10))
-#define RK3528_GMAC1_CLK_RGMII_DIV5 (GRF_BIT(11) | GRF_BIT(10))
-#define RK3528_GMAC1_CLK_RGMII_DIV50 (GRF_BIT(11) | GRF_CLR_BIT(10))
+#define RK3528_GMAC1_CLK_RGMII_DIV1 GRF_FIELD_CONST(11, 10, 0)
+#define RK3528_GMAC1_CLK_RGMII_DIV5 GRF_FIELD_CONST(11, 10, 3)
+#define RK3528_GMAC1_CLK_RGMII_DIV50 GRF_FIELD_CONST(11, 10, 2)
#define RK3528_GMAC0_CLK_RMII_GATE GRF_BIT(2)
#define RK3528_GMAC0_CLK_RMII_NOGATE GRF_CLR_BIT(2)
@@ -966,10 +1013,7 @@ static const struct rk_gmac_ops rk3528_ops = {
#define RK3568_GRF_GMAC1_CON1 0x038c
/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */
-#define RK3568_GMAC_PHY_INTF_SEL_RGMII \
- (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
-#define RK3568_GMAC_PHY_INTF_SEL_RMII \
- (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RK3568_GMAC_PHY_INTF_SEL(val) GRF_FIELD(6, 4, val)
#define RK3568_GMAC_FLOW_CTRL GRF_BIT(3)
#define RK3568_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
#define RK3568_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1)
@@ -978,8 +1022,8 @@ static const struct rk_gmac_ops rk3528_ops = {
#define RK3568_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(0)
/* RK3568_GRF_GMAC0_CON0 && RK3568_GRF_GMAC1_CON0 */
-#define RK3568_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
-#define RK3568_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3568_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(14, 8, val)
+#define RK3568_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
static void rk3568_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
@@ -996,7 +1040,7 @@ static void rk3568_set_to_rgmii(struct rk_priv_data *bsp_priv,
RK3568_GMAC_CLK_TX_DL_CFG(tx_delay));
regmap_write(bsp_priv->grf, con1,
- RK3568_GMAC_PHY_INTF_SEL_RGMII |
+ RK3568_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RK3568_GMAC_RXCLK_DLY_ENABLE |
RK3568_GMAC_TXCLK_DLY_ENABLE);
}
@@ -1007,7 +1051,8 @@ static void rk3568_set_to_rmii(struct rk_priv_data *bsp_priv)
con1 = (bsp_priv->id == 1) ? RK3568_GRF_GMAC1_CON1 :
RK3568_GRF_GMAC0_CON1;
- regmap_write(bsp_priv->grf, con1, RK3568_GMAC_PHY_INTF_SEL_RMII);
+ regmap_write(bsp_priv->grf, con1,
+ RK3568_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII));
}
static const struct rk_gmac_ops rk3568_ops = {
@@ -1033,8 +1078,8 @@ static const struct rk_gmac_ops rk3568_ops = {
#define RK3576_GMAC_TXCLK_DLY_ENABLE GRF_BIT(7)
#define RK3576_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7)
-#define RK3576_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
-#define RK3576_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RK3576_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(14, 8, val)
+#define RK3576_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
/* SDGMAC_GRF */
#define RK3576_GRF_GMAC_CON0 0X0020
@@ -1049,12 +1094,9 @@ static const struct rk_gmac_ops rk3568_ops = {
#define RK3576_GMAC_CLK_RMII_DIV2 GRF_BIT(5)
#define RK3576_GMAC_CLK_RMII_DIV20 GRF_CLR_BIT(5)
-#define RK3576_GMAC_CLK_RGMII_DIV1 \
- (GRF_CLR_BIT(6) | GRF_CLR_BIT(5))
-#define RK3576_GMAC_CLK_RGMII_DIV5 \
- (GRF_BIT(6) | GRF_BIT(5))
-#define RK3576_GMAC_CLK_RGMII_DIV50 \
- (GRF_BIT(6) | GRF_CLR_BIT(5))
+#define RK3576_GMAC_CLK_RGMII_DIV1 GRF_FIELD_CONST(6, 5, 0)
+#define RK3576_GMAC_CLK_RGMII_DIV5 GRF_FIELD_CONST(6, 5, 3)
+#define RK3576_GMAC_CLK_RGMII_DIV50 GRF_FIELD_CONST(6, 5, 2)
#define RK3576_GMAC_CLK_RMII_GATE GRF_BIT(4)
#define RK3576_GMAC_CLK_RMII_NOGATE GRF_CLR_BIT(4)
@@ -1157,17 +1199,15 @@ static const struct rk_gmac_ops rk3576_ops = {
#define RK3588_GMAC_TXCLK_DLY_ENABLE(id) GRF_BIT(2 * (id) + 2)
#define RK3588_GMAC_TXCLK_DLY_DISABLE(id) GRF_CLR_BIT(2 * (id) + 2)
-#define RK3588_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 8)
-#define RK3588_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0xFF, 0)
+#define RK3588_GMAC_CLK_RX_DL_CFG(val) GRF_FIELD(15, 8, val)
+#define RK3588_GMAC_CLK_TX_DL_CFG(val) GRF_FIELD(7, 0, val)
/* php_grf */
#define RK3588_GRF_GMAC_CON0 0X0008
#define RK3588_GRF_CLK_CON1 0X0070
-#define RK3588_GMAC_PHY_INTF_SEL_RGMII(id) \
- (GRF_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_CLR_BIT(5 + (id) * 6))
-#define RK3588_GMAC_PHY_INTF_SEL_RMII(id) \
- (GRF_CLR_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_BIT(5 + (id) * 6))
+#define RK3588_GMAC_PHY_INTF_SEL(id, val) \
+ (GRF_FIELD(5, 3, val) << ((id) * 6))
#define RK3588_GMAC_CLK_RMII_MODE(id) GRF_BIT(5 * (id))
#define RK3588_GMAC_CLK_RGMII_MODE(id) GRF_CLR_BIT(5 * (id))
@@ -1179,11 +1219,11 @@ static const struct rk_gmac_ops rk3576_ops = {
#define RK3588_GMA_CLK_RMII_DIV20(id) GRF_CLR_BIT(5 * (id) + 2)
#define RK3588_GMAC_CLK_RGMII_DIV1(id) \
- (GRF_CLR_BIT(5 * (id) + 2) | GRF_CLR_BIT(5 * (id) + 3))
+ (GRF_FIELD_CONST(3, 2, 0) << ((id) * 5))
#define RK3588_GMAC_CLK_RGMII_DIV5(id) \
- (GRF_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3))
+ (GRF_FIELD_CONST(3, 2, 3) << ((id) * 5))
#define RK3588_GMAC_CLK_RGMII_DIV50(id) \
- (GRF_CLR_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3))
+ (GRF_FIELD_CONST(3, 2, 2) << ((id) * 5))
#define RK3588_GMAC_CLK_RMII_GATE(id) GRF_BIT(5 * (id) + 1)
#define RK3588_GMAC_CLK_RMII_NOGATE(id) GRF_CLR_BIT(5 * (id) + 1)
@@ -1197,7 +1237,7 @@ static void rk3588_set_to_rgmii(struct rk_priv_data *bsp_priv,
RK3588_GRF_GMAC_CON8;
regmap_write(bsp_priv->php_grf, RK3588_GRF_GMAC_CON0,
- RK3588_GMAC_PHY_INTF_SEL_RGMII(id));
+ RK3588_GMAC_PHY_INTF_SEL(id, PHY_INTF_SEL_RGMII));
regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1,
RK3588_GMAC_CLK_RGMII_MODE(id));
@@ -1214,7 +1254,7 @@ static void rk3588_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rk3588_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->php_grf, RK3588_GRF_GMAC_CON0,
- RK3588_GMAC_PHY_INTF_SEL_RMII(bsp_priv->id));
+ RK3588_GMAC_PHY_INTF_SEL(bsp_priv->id, PHY_INTF_SEL_RMII));
regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1,
RK3588_GMAC_CLK_RMII_MODE(bsp_priv->id));
@@ -1284,8 +1324,7 @@ static const struct rk_gmac_ops rk3588_ops = {
#define RV1108_GRF_GMAC_CON0 0X0900
/* RV1108_GRF_GMAC_CON0 */
-#define RV1108_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | \
- GRF_BIT(6))
+#define RV1108_GMAC_PHY_INTF_SEL(val) GRF_FIELD(6, 4, val)
#define RV1108_GMAC_FLOW_CTRL GRF_BIT(3)
#define RV1108_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3)
#define RV1108_GMAC_SPEED_10M GRF_CLR_BIT(2)
@@ -1296,7 +1335,7 @@ static const struct rk_gmac_ops rk3588_ops = {
static void rv1108_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RV1108_GRF_GMAC_CON0,
- RV1108_GMAC_PHY_INTF_SEL_RMII);
+ RV1108_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII));
}
static const struct rk_reg_speed_data rv1108_reg_speed_data = {
@@ -1321,10 +1360,7 @@ static const struct rk_gmac_ops rv1108_ops = {
#define RV1126_GRF_GMAC_CON2 0X0078
/* RV1126_GRF_GMAC_CON0 */
-#define RV1126_GMAC_PHY_INTF_SEL_RGMII \
- (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
-#define RV1126_GMAC_PHY_INTF_SEL_RMII \
- (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RV1126_GMAC_PHY_INTF_SEL(val) GRF_FIELD(6, 4, val)
#define RV1126_GMAC_FLOW_CTRL GRF_BIT(7)
#define RV1126_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(7)
#define RV1126_GMAC_M0_RXCLK_DLY_ENABLE GRF_BIT(1)
@@ -1337,17 +1373,17 @@ static const struct rk_gmac_ops rv1108_ops = {
#define RV1126_GMAC_M1_TXCLK_DLY_DISABLE GRF_CLR_BIT(2)
/* RV1126_GRF_GMAC_CON1 */
-#define RV1126_GMAC_M0_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
-#define RV1126_GMAC_M0_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RV1126_GMAC_M0_CLK_RX_DL_CFG(val) GRF_FIELD(14, 8, val)
+#define RV1126_GMAC_M0_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
/* RV1126_GRF_GMAC_CON2 */
-#define RV1126_GMAC_M1_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
-#define RV1126_GMAC_M1_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
+#define RV1126_GMAC_M1_CLK_RX_DL_CFG(val) GRF_FIELD(14, 8, val)
+#define RV1126_GMAC_M1_CLK_TX_DL_CFG(val) GRF_FIELD(6, 0, val)
static void rv1126_set_to_rgmii(struct rk_priv_data *bsp_priv,
int tx_delay, int rx_delay)
{
regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON0,
- RV1126_GMAC_PHY_INTF_SEL_RGMII |
+ RV1126_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RGMII) |
RV1126_GMAC_M0_RXCLK_DLY_ENABLE |
RV1126_GMAC_M0_TXCLK_DLY_ENABLE |
RV1126_GMAC_M1_RXCLK_DLY_ENABLE |
@@ -1365,7 +1401,7 @@ static void rv1126_set_to_rgmii(struct rk_priv_data *bsp_priv,
static void rv1126_set_to_rmii(struct rk_priv_data *bsp_priv)
{
regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON0,
- RV1126_GMAC_PHY_INTF_SEL_RMII);
+ RV1126_GMAC_PHY_INTF_SEL(PHY_INTF_SEL_RMII));
}
static const struct rk_gmac_ops rv1126_ops = {
@@ -1446,14 +1482,15 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
}
} else {
if (bsp_priv->clk_enabled) {
+ if (bsp_priv->ops && bsp_priv->ops->set_clock_selection) {
+ bsp_priv->ops->set_clock_selection(bsp_priv,
+ bsp_priv->clock_input, false);
+ }
+
clk_bulk_disable_unprepare(bsp_priv->num_clks,
bsp_priv->clks);
clk_disable_unprepare(bsp_priv->clk_phy);
- if (bsp_priv->ops && bsp_priv->ops->set_clock_selection)
- bsp_priv->ops->set_clock_selection(bsp_priv,
- bsp_priv->clock_input, false);
-
bsp_priv->clk_enabled = false;
}
}
@@ -1698,8 +1735,7 @@ static int rk_set_clk_tx_rate(void *bsp_priv_, struct clk *clk_tx_i,
struct rk_priv_data *bsp_priv = bsp_priv_;
if (bsp_priv->ops->set_speed)
- return bsp_priv->ops->set_speed(bsp_priv, bsp_priv->phy_iface,
- speed);
+ return bsp_priv->ops->set_speed(bsp_priv, interface, speed);
return -EINVAL;
}
@@ -1726,6 +1762,22 @@ static int rk_gmac_resume(struct device *dev, void *bsp_priv_)
return 0;
}
+static int rk_gmac_init(struct device *dev, void *bsp_priv)
+{
+ return rk_gmac_powerup(bsp_priv);
+}
+
+static void rk_gmac_exit(struct device *dev, void *bsp_priv_)
+{
+ struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(dev));
+ struct rk_priv_data *bsp_priv = bsp_priv_;
+
+ rk_gmac_powerdown(bsp_priv);
+
+ if (priv->plat->phy_node && bsp_priv->integrated_phy)
+ clk_put(bsp_priv->clk_phy);
+}
+
static int rk_gmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
@@ -1750,14 +1802,16 @@ static int rk_gmac_probe(struct platform_device *pdev)
/* If the stmmac is not already selected as gmac4,
* then make sure we fallback to gmac.
*/
- if (!plat_dat->has_gmac4) {
- plat_dat->has_gmac = true;
+ if (plat_dat->core_type != DWMAC_CORE_GMAC4) {
+ plat_dat->core_type = DWMAC_CORE_GMAC;
plat_dat->rx_fifo_size = 4096;
plat_dat->tx_fifo_size = 2048;
}
plat_dat->get_interfaces = rk_get_interfaces;
plat_dat->set_clk_tx_rate = rk_set_clk_tx_rate;
+ plat_dat->init = rk_gmac_init;
+ plat_dat->exit = rk_gmac_exit;
plat_dat->suspend = rk_gmac_suspend;
plat_dat->resume = rk_gmac_resume;
@@ -1769,33 +1823,7 @@ static int rk_gmac_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = rk_gmac_powerup(plat_dat->bsp_priv);
- if (ret)
- return ret;
-
- ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
- if (ret)
- goto err_gmac_powerdown;
-
- return 0;
-
-err_gmac_powerdown:
- rk_gmac_powerdown(plat_dat->bsp_priv);
-
- return ret;
-}
-
-static void rk_gmac_remove(struct platform_device *pdev)
-{
- struct stmmac_priv *priv = netdev_priv(platform_get_drvdata(pdev));
- struct rk_priv_data *bsp_priv = priv->plat->bsp_priv;
-
- stmmac_dvr_remove(&pdev->dev);
-
- rk_gmac_powerdown(bsp_priv);
-
- if (priv->plat->phy_node && bsp_priv->integrated_phy)
- clk_put(bsp_priv->clk_phy);
+ return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
}
static const struct of_device_id rk_gmac_dwmac_match[] = {
@@ -1808,6 +1836,7 @@ static const struct of_device_id rk_gmac_dwmac_match[] = {
{ .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops },
{ .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops },
{ .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops },
+ { .compatible = "rockchip,rk3506-gmac", .data = &rk3506_ops },
{ .compatible = "rockchip,rk3528-gmac", .data = &rk3528_ops },
{ .compatible = "rockchip,rk3568-gmac", .data = &rk3568_ops },
{ .compatible = "rockchip,rk3576-gmac", .data = &rk3576_ops },
@@ -1820,7 +1849,6 @@ MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);
static struct platform_driver rk_gmac_dwmac_driver = {
.probe = rk_gmac_probe,
- .remove = rk_gmac_remove,
.driver = {
.name = "rk_gmac-dwmac",
.pm = &stmmac_simple_pm_ops,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c
index 221539d760bc..5a485ee98fa7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-s32.c
@@ -24,10 +24,10 @@
#define GMAC_INTF_RATE_125M 125000000 /* 125MHz */
/* SoC PHY interface control register */
-#define PHY_INTF_SEL_MII 0x00
-#define PHY_INTF_SEL_SGMII 0x01
-#define PHY_INTF_SEL_RGMII 0x02
-#define PHY_INTF_SEL_RMII 0x08
+#define S32_PHY_INTF_SEL_MII 0x00
+#define S32_PHY_INTF_SEL_SGMII 0x01
+#define S32_PHY_INTF_SEL_RGMII 0x02
+#define S32_PHY_INTF_SEL_RMII 0x08
struct s32_priv_data {
void __iomem *ioaddr;
@@ -40,14 +40,14 @@ struct s32_priv_data {
static int s32_gmac_write_phy_intf_select(struct s32_priv_data *gmac)
{
- writel(PHY_INTF_SEL_RGMII, gmac->ctrl_sts);
+ writel(S32_PHY_INTF_SEL_RGMII, gmac->ctrl_sts);
dev_dbg(gmac->dev, "PHY mode set to %s\n", phy_modes(*gmac->intf_mode));
return 0;
}
-static int s32_gmac_init(struct platform_device *pdev, void *priv)
+static int s32_gmac_init(struct device *dev, void *priv)
{
struct s32_priv_data *gmac = priv;
int ret;
@@ -55,31 +55,31 @@ static int s32_gmac_init(struct platform_device *pdev, void *priv)
/* Set initial TX interface clock */
ret = clk_prepare_enable(gmac->tx_clk);
if (ret) {
- dev_err(&pdev->dev, "Can't enable tx clock\n");
+ dev_err(dev, "Can't enable tx clock\n");
return ret;
}
ret = clk_set_rate(gmac->tx_clk, GMAC_INTF_RATE_125M);
if (ret) {
- dev_err(&pdev->dev, "Can't set tx clock\n");
+ dev_err(dev, "Can't set tx clock\n");
goto err_tx_disable;
}
/* Set initial RX interface clock */
ret = clk_prepare_enable(gmac->rx_clk);
if (ret) {
- dev_err(&pdev->dev, "Can't enable rx clock\n");
+ dev_err(dev, "Can't enable rx clock\n");
goto err_tx_disable;
}
ret = clk_set_rate(gmac->rx_clk, GMAC_INTF_RATE_125M);
if (ret) {
- dev_err(&pdev->dev, "Can't set rx clock\n");
+ dev_err(dev, "Can't set rx clock\n");
goto err_txrx_disable;
}
/* Set interface mode */
ret = s32_gmac_write_phy_intf_select(gmac);
if (ret) {
- dev_err(&pdev->dev, "Can't set PHY interface mode\n");
+ dev_err(dev, "Can't set PHY interface mode\n");
goto err_txrx_disable;
}
@@ -92,7 +92,7 @@ err_tx_disable:
return ret;
}
-static void s32_gmac_exit(struct platform_device *pdev, void *priv)
+static void s32_gmac_exit(struct device *dev, void *priv)
{
struct s32_priv_data *gmac = priv;
@@ -146,7 +146,7 @@ static int s32_dwmac_probe(struct platform_device *pdev)
gmac->ioaddr = res.addr;
/* S32CC core feature set */
- plat->has_gmac4 = true;
+ plat->core_type = DWMAC_CORE_GMAC4;
plat->pmt = 1;
plat->flags |= STMMAC_FLAG_SPH_DISABLE;
plat->rx_fifo_size = 20480;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
index 354f01184e6c..a2b52d2c4eb6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
@@ -5,6 +5,7 @@
*/
#include <linux/mfd/altera-sysmgr.h>
+#include <linux/clocksource_ids.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_net.h>
@@ -15,8 +16,10 @@
#include <linux/reset.h>
#include <linux/stmmac.h>
+#include "dwxgmac2.h"
#include "stmmac.h"
#include "stmmac_platform.h"
+#include "stmmac_ptp.h"
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
@@ -41,9 +44,17 @@
#define SGMII_ADAPTER_ENABLE 0x0000
#define SGMII_ADAPTER_DISABLE 0x0001
+#define SMTG_MDIO_ADDR 0x15
+#define SMTG_TSC_WORD0 0xC
+#define SMTG_TSC_WORD1 0xD
+#define SMTG_TSC_WORD2 0xE
+#define SMTG_TSC_WORD3 0xF
+#define SMTG_TSC_SHIFT 16
+
struct socfpga_dwmac;
struct socfpga_dwmac_ops {
int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv);
+ void (*setup_plat_dat)(struct socfpga_dwmac *dwmac_priv);
};
struct socfpga_dwmac {
@@ -268,6 +279,112 @@ static int socfpga_set_phy_mode_common(int phymode, u32 *val)
return 0;
}
+static void get_smtgtime(struct mii_bus *mii, int smtg_addr, u64 *smtg_time)
+{
+ u64 ns;
+
+ ns = mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD3);
+ ns <<= SMTG_TSC_SHIFT;
+ ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD2);
+ ns <<= SMTG_TSC_SHIFT;
+ ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD1);
+ ns <<= SMTG_TSC_SHIFT;
+ ns |= mdiobus_read(mii, smtg_addr, SMTG_TSC_WORD0);
+
+ *smtg_time = ns;
+}
+
+static int smtg_crosststamp(ktime_t *device, struct system_counterval_t *system,
+ void *ctx)
+{
+ struct stmmac_priv *priv = (struct stmmac_priv *)ctx;
+ u32 num_snapshot, gpio_value, acr_value;
+ void __iomem *ptpaddr = priv->ptpaddr;
+ void __iomem *ioaddr = priv->hw->pcsr;
+ unsigned long flags;
+ u64 smtg_time = 0;
+ u64 ptp_time = 0;
+ int i, ret;
+ u32 v;
+
+ /* Both internal crosstimestamping and external triggered event
+ * timestamping cannot be run concurrently.
+ */
+ if (priv->plat->flags & STMMAC_FLAG_EXT_SNAPSHOT_EN)
+ return -EBUSY;
+
+ mutex_lock(&priv->aux_ts_lock);
+ /* Enable Internal snapshot trigger */
+ acr_value = readl(ptpaddr + PTP_ACR);
+ acr_value &= ~PTP_ACR_MASK;
+ switch (priv->plat->int_snapshot_num) {
+ case AUX_SNAPSHOT0:
+ acr_value |= PTP_ACR_ATSEN0;
+ break;
+ case AUX_SNAPSHOT1:
+ acr_value |= PTP_ACR_ATSEN1;
+ break;
+ case AUX_SNAPSHOT2:
+ acr_value |= PTP_ACR_ATSEN2;
+ break;
+ case AUX_SNAPSHOT3:
+ acr_value |= PTP_ACR_ATSEN3;
+ break;
+ default:
+ mutex_unlock(&priv->aux_ts_lock);
+ return -EINVAL;
+ }
+ writel(acr_value, ptpaddr + PTP_ACR);
+
+ /* Clear FIFO */
+ acr_value = readl(ptpaddr + PTP_ACR);
+ acr_value |= PTP_ACR_ATSFC;
+ writel(acr_value, ptpaddr + PTP_ACR);
+ /* Release the mutex */
+ mutex_unlock(&priv->aux_ts_lock);
+
+ /* Trigger Internal snapshot signal. Create a rising edge by just toggle
+ * the GPO0 to low and back to high.
+ */
+ gpio_value = readl(ioaddr + XGMAC_GPIO_STATUS);
+ gpio_value &= ~XGMAC_GPIO_GPO0;
+ writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
+ gpio_value |= XGMAC_GPIO_GPO0;
+ writel(gpio_value, ioaddr + XGMAC_GPIO_STATUS);
+
+ /* Poll for time sync operation done */
+ ret = readl_poll_timeout(priv->ioaddr + XGMAC_INT_STATUS, v,
+ (v & XGMAC_INT_TSIS), 100, 10000);
+ if (ret) {
+ netdev_err(priv->dev, "%s: Wait for time sync operation timeout\n",
+ __func__);
+ return ret;
+ }
+
+ *system = (struct system_counterval_t) {
+ .cycles = 0,
+ .cs_id = CSID_ARM_ARCH_COUNTER,
+ .use_nsecs = false,
+ };
+
+ num_snapshot = (readl(ioaddr + XGMAC_TIMESTAMP_STATUS) &
+ XGMAC_TIMESTAMP_ATSNS_MASK) >>
+ XGMAC_TIMESTAMP_ATSNS_SHIFT;
+
+ /* Repeat until the timestamps are from the FIFO last segment */
+ for (i = 0; i < num_snapshot; i++) {
+ read_lock_irqsave(&priv->ptp_lock, flags);
+ stmmac_get_ptptime(priv, ptpaddr, &ptp_time);
+ *device = ns_to_ktime(ptp_time);
+ read_unlock_irqrestore(&priv->ptp_lock, flags);
+ }
+
+ get_smtgtime(priv->mii, SMTG_MDIO_ADDR, &smtg_time);
+ system->cycles = smtg_time;
+
+ return 0;
+}
+
static int socfpga_gen5_set_phy_mode(struct socfpga_dwmac *dwmac)
{
struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
@@ -434,13 +551,50 @@ static struct phylink_pcs *socfpga_dwmac_select_pcs(struct stmmac_priv *priv,
return priv->hw->phylink_pcs;
}
-static int socfpga_dwmac_init(struct platform_device *pdev, void *bsp_priv)
+static int socfpga_dwmac_init(struct device *dev, void *bsp_priv)
{
struct socfpga_dwmac *dwmac = bsp_priv;
return dwmac->ops->set_phy_mode(dwmac);
}
+static void socfpga_gen5_setup_plat_dat(struct socfpga_dwmac *dwmac)
+{
+ struct plat_stmmacenet_data *plat_dat = dwmac->plat_dat;
+
+ plat_dat->core_type = DWMAC_CORE_GMAC;
+
+ /* Rx watchdog timer in dwmac is buggy in this hw */
+ plat_dat->riwt_off = 1;
+}
+
+static void socfpga_agilex5_setup_plat_dat(struct socfpga_dwmac *dwmac)
+{
+ struct plat_stmmacenet_data *plat_dat = dwmac->plat_dat;
+
+ plat_dat->core_type = DWMAC_CORE_XGMAC;
+
+ /* Enable TSO */
+ plat_dat->flags |= STMMAC_FLAG_TSO_EN;
+
+ /* Enable TBS */
+ switch (plat_dat->tx_queues_to_use) {
+ case 8:
+ plat_dat->tx_queues_cfg[7].tbs_en = true;
+ fallthrough;
+ case 7:
+ plat_dat->tx_queues_cfg[6].tbs_en = true;
+ break;
+ default:
+ /* Tx Queues 0 - 5 doesn't support TBS on Agilex5 */
+ break;
+ }
+
+ /* Hw supported cross-timestamp */
+ plat_dat->int_snapshot_num = AUX_SNAPSHOT0;
+ plat_dat->crosststamp = smtg_crosststamp;
+}
+
static int socfpga_dwmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
@@ -497,25 +651,31 @@ static int socfpga_dwmac_probe(struct platform_device *pdev)
plat_dat->pcs_init = socfpga_dwmac_pcs_init;
plat_dat->pcs_exit = socfpga_dwmac_pcs_exit;
plat_dat->select_pcs = socfpga_dwmac_select_pcs;
- plat_dat->has_gmac = true;
- plat_dat->riwt_off = 1;
+ ops->setup_plat_dat(dwmac);
return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res);
}
static const struct socfpga_dwmac_ops socfpga_gen5_ops = {
.set_phy_mode = socfpga_gen5_set_phy_mode,
+ .setup_plat_dat = socfpga_gen5_setup_plat_dat,
};
static const struct socfpga_dwmac_ops socfpga_gen10_ops = {
.set_phy_mode = socfpga_gen10_set_phy_mode,
+ .setup_plat_dat = socfpga_gen5_setup_plat_dat,
+};
+
+static const struct socfpga_dwmac_ops socfpga_agilex5_ops = {
+ .set_phy_mode = socfpga_gen10_set_phy_mode,
+ .setup_plat_dat = socfpga_agilex5_setup_plat_dat,
};
static const struct of_device_id socfpga_dwmac_match[] = {
{ .compatible = "altr,socfpga-stmmac", .data = &socfpga_gen5_ops },
{ .compatible = "altr,socfpga-stmmac-a10-s10", .data = &socfpga_gen10_ops },
- { .compatible = "altr,socfpga-stmmac-agilex5", .data = &socfpga_gen10_ops },
+ { .compatible = "altr,socfpga-stmmac-agilex5", .data = &socfpga_agilex5_ops },
{ }
};
MODULE_DEVICE_TABLE(of, socfpga_dwmac_match);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c
index 3b7947a7a7ba..44d4ceb8415f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sophgo.c
@@ -7,11 +7,16 @@
#include <linux/clk.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "stmmac_platform.h"
+struct sophgo_dwmac_data {
+ bool has_internal_rx_delay;
+};
+
static int sophgo_sg2044_dwmac_init(struct platform_device *pdev,
struct plat_stmmacenet_data *plat_dat,
struct stmmac_resources *stmmac_res)
@@ -24,7 +29,6 @@ static int sophgo_sg2044_dwmac_init(struct platform_device *pdev,
plat_dat->flags |= STMMAC_FLAG_SPH_DISABLE;
plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate;
plat_dat->multicast_filter_bins = 0;
- plat_dat->unicast_filter_entries = 1;
return 0;
}
@@ -32,6 +36,7 @@ static int sophgo_sg2044_dwmac_init(struct platform_device *pdev,
static int sophgo_dwmac_probe(struct platform_device *pdev)
{
struct plat_stmmacenet_data *plat_dat;
+ const struct sophgo_dwmac_data *data;
struct stmmac_resources stmmac_res;
struct device *dev = &pdev->dev;
int ret;
@@ -50,11 +55,23 @@ static int sophgo_dwmac_probe(struct platform_device *pdev)
if (ret)
return ret;
+ data = device_get_match_data(&pdev->dev);
+ if (data && data->has_internal_rx_delay) {
+ plat_dat->phy_interface = phy_fix_phy_mode_for_mac_delays(plat_dat->phy_interface,
+ false, true);
+ if (plat_dat->phy_interface == PHY_INTERFACE_MODE_NA)
+ return -EINVAL;
+ }
+
return stmmac_dvr_probe(dev, plat_dat, &stmmac_res);
}
+static const struct sophgo_dwmac_data sg2042_dwmac_data = {
+ .has_internal_rx_delay = true,
+};
+
static const struct of_device_id sophgo_dwmac_match[] = {
- { .compatible = "sophgo,sg2042-dwmac" },
+ { .compatible = "sophgo,sg2042-dwmac", .data = &sg2042_dwmac_data },
{ .compatible = "sophgo,sg2044-dwmac" },
{ /* sentinel */ }
};
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
index 6938dd2a79b7..16b955a6d77b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c
@@ -15,8 +15,6 @@
#include "stmmac_platform.h"
-#define STARFIVE_DWMAC_PHY_INFT_RGMII 0x1
-#define STARFIVE_DWMAC_PHY_INFT_RMII 0x4
#define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U
#define JH7100_SYSMAIN_REGISTER49_DLYCHAIN 0xc8
@@ -35,25 +33,15 @@ static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat)
struct starfive_dwmac *dwmac = plat_dat->bsp_priv;
struct regmap *regmap;
unsigned int args[2];
- unsigned int mode;
+ int phy_intf_sel;
int err;
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_RMII:
- mode = STARFIVE_DWMAC_PHY_INFT_RMII;
- break;
-
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- mode = STARFIVE_DWMAC_PHY_INFT_RGMII;
- break;
-
- default:
+ phy_intf_sel = stmmac_get_phy_intf_sel(plat_dat->phy_interface);
+ if (phy_intf_sel != PHY_INTF_SEL_RGMII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII) {
dev_err(dwmac->dev, "unsupported interface %s\n",
phy_modes(plat_dat->phy_interface));
- return -EINVAL;
+ return phy_intf_sel < 0 ? phy_intf_sel : -EINVAL;
}
regmap = syscon_regmap_lookup_by_phandle_args(dwmac->dev->of_node,
@@ -65,7 +53,7 @@ static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat)
/* args[0]:offset args[1]: shift */
err = regmap_update_bits(regmap, args[0],
STARFIVE_DWMAC_PHY_INFT_FIELD << args[1],
- mode << args[1]);
+ phy_intf_sel << args[1]);
if (err)
return dev_err_probe(dwmac->dev, err, "error setting phy mode\n");
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
index 53d5ce1f6dc6..f50547b67fbc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
@@ -77,13 +77,9 @@
* 001-RGMII
* 010-SGMII
* 100-RMII
+ * These are the DW MAC phy_intf_sel values.
*/
#define MII_PHY_SEL_MASK GENMASK(4, 2)
-#define ETH_PHY_SEL_RMII BIT(4)
-#define ETH_PHY_SEL_SGMII BIT(3)
-#define ETH_PHY_SEL_RGMII BIT(2)
-#define ETH_PHY_SEL_GMII 0x0
-#define ETH_PHY_SEL_MII 0x0
struct sti_dwmac {
phy_interface_t interface; /* MII interface */
@@ -102,15 +98,6 @@ struct sti_dwmac_of_data {
void (*fix_retime_src)(void *priv, int speed, unsigned int mode);
};
-static u32 phy_intf_sels[] = {
- [PHY_INTERFACE_MODE_MII] = ETH_PHY_SEL_MII,
- [PHY_INTERFACE_MODE_GMII] = ETH_PHY_SEL_GMII,
- [PHY_INTERFACE_MODE_RGMII] = ETH_PHY_SEL_RGMII,
- [PHY_INTERFACE_MODE_RGMII_ID] = ETH_PHY_SEL_RGMII,
- [PHY_INTERFACE_MODE_SGMII] = ETH_PHY_SEL_SGMII,
- [PHY_INTERFACE_MODE_RMII] = ETH_PHY_SEL_RMII,
-};
-
enum {
TX_RETIME_SRC_NA = 0,
TX_RETIME_SRC_TXCLK = 1,
@@ -159,19 +146,28 @@ static void stih4xx_fix_retime_src(void *priv, int spd, unsigned int mode)
stih4xx_tx_retime_val[src]);
}
-static int sti_dwmac_set_mode(struct sti_dwmac *dwmac)
+static int sti_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel)
{
- struct regmap *regmap = dwmac->regmap;
- int iface = dwmac->interface;
- u32 reg = dwmac->ctrl_reg;
- u32 val;
+ struct sti_dwmac *dwmac = bsp_priv;
+ struct regmap *regmap;
+ u32 reg, val;
+
+ regmap = dwmac->regmap;
+ reg = dwmac->ctrl_reg;
if (dwmac->gmac_en)
regmap_update_bits(regmap, reg, EN_MASK, EN);
- regmap_update_bits(regmap, reg, MII_PHY_SEL_MASK, phy_intf_sels[iface]);
+ if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
+ phy_intf_sel != PHY_INTF_SEL_RGMII &&
+ phy_intf_sel != PHY_INTF_SEL_SGMII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII)
+ phy_intf_sel = PHY_INTF_SEL_GMII_MII;
+
+ regmap_update_bits(regmap, reg, MII_PHY_SEL_MASK,
+ FIELD_PREP(MII_PHY_SEL_MASK, phy_intf_sel));
- val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII;
+ val = (dwmac->interface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII;
regmap_update_bits(regmap, reg, ENMII_MASK, val);
dwmac->fix_retime_src(dwmac, dwmac->speed, 0);
@@ -233,23 +229,14 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac,
return 0;
}
-static int sti_dwmac_init(struct platform_device *pdev, void *bsp_priv)
+static int sti_dwmac_init(struct device *dev, void *bsp_priv)
{
struct sti_dwmac *dwmac = bsp_priv;
- int ret;
-
- ret = clk_prepare_enable(dwmac->clk);
- if (ret)
- return ret;
-
- ret = sti_dwmac_set_mode(dwmac);
- if (ret)
- clk_disable_unprepare(dwmac->clk);
- return ret;
+ return clk_prepare_enable(dwmac->clk);
}
-static void sti_dwmac_exit(struct platform_device *pdev, void *bsp_priv)
+static void sti_dwmac_exit(struct device *dev, void *bsp_priv)
{
struct sti_dwmac *dwmac = bsp_priv;
@@ -291,6 +278,7 @@ static int sti_dwmac_probe(struct platform_device *pdev)
dwmac->fix_retime_src = data->fix_retime_src;
plat_dat->bsp_priv = dwmac;
+ plat_dat->set_phy_intf_sel = sti_set_phy_intf_sel;
plat_dat->fix_mac_speed = data->fix_retime_src;
plat_dat->init = sti_dwmac_init;
plat_dat->exit = sti_dwmac_exit;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
index 6c179911ef3f..e1b260ed4790 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-stm32.c
@@ -47,23 +47,18 @@
*------------------------------------------
*/
#define SYSCFG_PMCR_ETH_SEL_MII BIT(20)
-#define SYSCFG_PMCR_ETH_SEL_RGMII BIT(21)
-#define SYSCFG_PMCR_ETH_SEL_RMII BIT(23)
-#define SYSCFG_PMCR_ETH_SEL_GMII 0
+#define SYSCFG_PMCR_PHY_INTF_SEL_MASK GENMASK(23, 21)
#define SYSCFG_MCU_ETH_SEL_MII 0
#define SYSCFG_MCU_ETH_SEL_RMII 1
/* STM32MP2 register definitions */
#define SYSCFG_MP2_ETH_MASK GENMASK(31, 0)
+#define SYSCFG_ETHCR_ETH_SEL_MASK GENMASK(6, 4)
#define SYSCFG_ETHCR_ETH_PTP_CLK_SEL BIT(2)
#define SYSCFG_ETHCR_ETH_CLK_SEL BIT(1)
#define SYSCFG_ETHCR_ETH_REF_CLK_SEL BIT(0)
-#define SYSCFG_ETHCR_ETH_SEL_MII 0
-#define SYSCFG_ETHCR_ETH_SEL_RGMII BIT(4)
-#define SYSCFG_ETHCR_ETH_SEL_RMII BIT(6)
-
/* STM32MPx register definitions
*
* Below table summarizes the clock requirement and clock sources for
@@ -232,11 +227,14 @@ static int stm32mp1_validate_ethck_rate(struct plat_stmmacenet_data *plat_dat)
return -EINVAL;
}
-static int stm32mp1_configure_pmcr(struct plat_stmmacenet_data *plat_dat)
+static int stm32mp1_configure_pmcr(struct plat_stmmacenet_data *plat_dat,
+ u8 phy_intf_sel)
{
struct stm32_dwmac *dwmac = plat_dat->bsp_priv;
u32 reg = dwmac->mode_reg;
- int val = 0;
+ int val;
+
+ val = FIELD_PREP(SYSCFG_PMCR_PHY_INTF_SEL_MASK, phy_intf_sel);
switch (plat_dat->phy_interface) {
case PHY_INTERFACE_MODE_MII:
@@ -250,12 +248,10 @@ static int stm32mp1_configure_pmcr(struct plat_stmmacenet_data *plat_dat)
val |= SYSCFG_PMCR_ETH_SEL_MII;
break;
case PHY_INTERFACE_MODE_GMII:
- val = SYSCFG_PMCR_ETH_SEL_GMII;
if (dwmac->enable_eth_ck)
val |= SYSCFG_PMCR_ETH_CLK_SEL;
break;
case PHY_INTERFACE_MODE_RMII:
- val = SYSCFG_PMCR_ETH_SEL_RMII;
if (dwmac->enable_eth_ck)
val |= SYSCFG_PMCR_ETH_REF_CLK_SEL;
break;
@@ -263,7 +259,6 @@ static int stm32mp1_configure_pmcr(struct plat_stmmacenet_data *plat_dat)
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
- val = SYSCFG_PMCR_ETH_SEL_RGMII;
if (dwmac->enable_eth_ck)
val |= SYSCFG_PMCR_ETH_CLK_SEL;
break;
@@ -288,18 +283,20 @@ static int stm32mp1_configure_pmcr(struct plat_stmmacenet_data *plat_dat)
dwmac->mode_mask, val);
}
-static int stm32mp2_configure_syscfg(struct plat_stmmacenet_data *plat_dat)
+static int stm32mp2_configure_syscfg(struct plat_stmmacenet_data *plat_dat,
+ u8 phy_intf_sel)
{
struct stm32_dwmac *dwmac = plat_dat->bsp_priv;
u32 reg = dwmac->mode_reg;
- int val = 0;
+ int val;
+
+ val = FIELD_PREP(SYSCFG_ETHCR_ETH_SEL_MASK, phy_intf_sel);
switch (plat_dat->phy_interface) {
case PHY_INTERFACE_MODE_MII:
/* ETH_REF_CLK_SEL bit in SYSCFG register is not applicable in MII mode */
break;
case PHY_INTERFACE_MODE_RMII:
- val = SYSCFG_ETHCR_ETH_SEL_RMII;
if (dwmac->enable_eth_ck) {
/* Internal clock ETH_CLK of 50MHz from RCC is used */
val |= SYSCFG_ETHCR_ETH_REF_CLK_SEL;
@@ -309,8 +306,6 @@ static int stm32mp2_configure_syscfg(struct plat_stmmacenet_data *plat_dat)
case PHY_INTERFACE_MODE_RGMII_ID:
case PHY_INTERFACE_MODE_RGMII_RXID:
case PHY_INTERFACE_MODE_RGMII_TXID:
- val = SYSCFG_ETHCR_ETH_SEL_RGMII;
- fallthrough;
case PHY_INTERFACE_MODE_GMII:
if (dwmac->enable_eth_ck) {
/* Internal clock ETH_CLK of 125MHz from RCC is used */
@@ -337,7 +332,7 @@ static int stm32mp2_configure_syscfg(struct plat_stmmacenet_data *plat_dat)
static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
{
struct stm32_dwmac *dwmac = plat_dat->bsp_priv;
- int ret;
+ int phy_intf_sel, ret;
ret = stm32mp1_select_ethck_external(plat_dat);
if (ret)
@@ -347,10 +342,19 @@ static int stm32mp1_set_mode(struct plat_stmmacenet_data *plat_dat)
if (ret)
return ret;
+ phy_intf_sel = stmmac_get_phy_intf_sel(plat_dat->phy_interface);
+ if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
+ phy_intf_sel != PHY_INTF_SEL_RGMII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII) {
+ dev_err(dwmac->dev, "Mode %s not supported\n",
+ phy_modes(plat_dat->phy_interface));
+ return phy_intf_sel < 0 ? phy_intf_sel : -EINVAL;
+ }
+
if (!dwmac->ops->is_mp2)
- return stm32mp1_configure_pmcr(plat_dat);
+ return stm32mp1_configure_pmcr(plat_dat, phy_intf_sel);
else
- return stm32mp2_configure_syscfg(plat_dat);
+ return stm32mp2_configure_syscfg(plat_dat, phy_intf_sel);
}
static int stm32mcu_set_mode(struct plat_stmmacenet_data *plat_dat)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
index 5d871b2cd111..8aa496ac85cc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
@@ -571,16 +571,16 @@ static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = {
static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv);
-static int sun8i_dwmac_init(struct platform_device *pdev, void *priv)
+static int sun8i_dwmac_init(struct device *dev, void *priv)
{
- struct net_device *ndev = platform_get_drvdata(pdev);
+ struct net_device *ndev = dev_get_drvdata(dev);
struct sunxi_priv_data *gmac = priv;
int ret;
if (gmac->regulator) {
ret = regulator_enable(gmac->regulator);
if (ret) {
- dev_err(&pdev->dev, "Fail to enable regulator\n");
+ dev_err(dev, "Fail to enable regulator\n");
return ret;
}
}
@@ -1005,7 +1005,7 @@ static void sun8i_dwmac_unset_syscon(struct sunxi_priv_data *gmac)
(H3_EPHY_SHUTDOWN | H3_EPHY_SELECT));
}
-static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
+static void sun8i_dwmac_exit(struct device *dev, void *priv)
{
struct sunxi_priv_data *gmac = priv;
@@ -1040,15 +1040,10 @@ static const struct stmmac_ops sun8i_dwmac_ops = {
.set_mac_loopback = sun8i_dwmac_set_mac_loopback,
};
-static struct mac_device_info *sun8i_dwmac_setup(void *ppriv)
+static int sun8i_dwmac_setup(void *ppriv, struct mac_device_info *mac)
{
- struct mac_device_info *mac;
struct stmmac_priv *priv = ppriv;
- mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
- if (!mac)
- return NULL;
-
mac->pcsr = priv->ioaddr;
mac->mac = &sun8i_dwmac_ops;
mac->dma = &sun8i_dwmac_dma_ops;
@@ -1079,7 +1074,7 @@ static struct mac_device_info *sun8i_dwmac_setup(void *ppriv)
/* Synopsys Id is not available */
priv->synopsys_id = 0;
- return mac;
+ return 0;
}
static struct regmap *sun8i_dwmac_get_syscon_from_dev(struct device_node *node)
@@ -1192,7 +1187,7 @@ static int sun8i_dwmac_probe(struct platform_device *pdev)
plat_dat->bsp_priv = gmac;
plat_dat->init = sun8i_dwmac_init;
plat_dat->exit = sun8i_dwmac_exit;
- plat_dat->setup = sun8i_dwmac_setup;
+ plat_dat->mac_setup = sun8i_dwmac_setup;
plat_dat->tx_fifo_size = 4096;
plat_dat->rx_fifo_size = 16384;
@@ -1270,7 +1265,7 @@ static void sun8i_dwmac_shutdown(struct platform_device *pdev)
struct stmmac_priv *priv = netdev_priv(ndev);
struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
- sun8i_dwmac_exit(pdev, gmac);
+ sun8i_dwmac_exit(&pdev->dev, gmac);
}
static const struct of_device_id sun8i_dwmac_match[] = {
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
index 1eadcf5d1ad6..52593ba3a3a3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
@@ -27,7 +27,7 @@ struct sunxi_priv_data {
#define SUN7I_GMAC_GMII_RGMII_RATE 125000000
#define SUN7I_GMAC_MII_RATE 25000000
-static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
+static int sun7i_gmac_init(struct device *dev, void *priv)
{
struct sunxi_priv_data *gmac = priv;
int ret = 0;
@@ -58,7 +58,7 @@ static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
return ret;
}
-static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
+static void sun7i_gmac_exit(struct device *dev, void *priv)
{
struct sunxi_priv_data *gmac = priv;
@@ -136,7 +136,7 @@ static int sun7i_gmac_probe(struct platform_device *pdev)
/* platform data specifying hardware features and callbacks.
* hardware features were copied from Allwinner drivers. */
plat_dat->tx_coe = 1;
- plat_dat->has_gmac = true;
+ plat_dat->core_type = DWMAC_CORE_GMAC;
plat_dat->bsp_priv = gmac;
plat_dat->init = sun7i_gmac_init;
plat_dat->exit = sun7i_gmac_exit;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c
index dc903b846b1b..d765acbe3754 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.c
@@ -308,7 +308,7 @@ static int tegra_mgbe_probe(struct platform_device *pdev)
goto disable_clks;
}
- plat->has_xgmac = 1;
+ plat->core_type = DWMAC_CORE_XGMAC;
plat->flags |= STMMAC_FLAG_TSO_EN;
plat->pmt = 1;
plat->bsp_priv = mgbe;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
index a3378046b061..e291028ba56e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c
@@ -186,7 +186,7 @@ static int thead_dwmac_enable_clk(struct plat_stmmacenet_data *plat)
return 0;
}
-static int thead_dwmac_init(struct platform_device *pdev, void *priv)
+static int thead_dwmac_init(struct device *dev, void *priv)
{
struct thead_dwmac *dwmac = priv;
unsigned int reg;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
index bd65d4239054..9497b13a5753 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-visconti.c
@@ -42,10 +42,6 @@
#define ETHER_CLK_SEL_RX_TX_CLK_EN (ETHER_CLK_SEL_RX_CLK_EN | ETHER_CLK_SEL_TX_CLK_EN)
-#define ETHER_CONFIG_INTF_MII 0
-#define ETHER_CONFIG_INTF_RGMII BIT(0)
-#define ETHER_CONFIG_INTF_RMII BIT(2)
-
struct visconti_eth {
void __iomem *reg;
struct clk *phy_ref_clk;
@@ -150,22 +146,12 @@ static int visconti_eth_init_hw(struct platform_device *pdev, struct plat_stmmac
{
struct visconti_eth *dwmac = plat_dat->bsp_priv;
unsigned int clk_sel_val;
- u32 phy_intf_sel;
-
- switch (plat_dat->phy_interface) {
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- phy_intf_sel = ETHER_CONFIG_INTF_RGMII;
- break;
- case PHY_INTERFACE_MODE_MII:
- phy_intf_sel = ETHER_CONFIG_INTF_MII;
- break;
- case PHY_INTERFACE_MODE_RMII:
- phy_intf_sel = ETHER_CONFIG_INTF_RMII;
- break;
- default:
+ int phy_intf_sel;
+
+ phy_intf_sel = stmmac_get_phy_intf_sel(plat_dat->phy_interface);
+ if (phy_intf_sel != PHY_INTF_SEL_GMII_MII &&
+ phy_intf_sel != PHY_INTF_SEL_RGMII &&
+ phy_intf_sel != PHY_INTF_SEL_RMII) {
dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", plat_dat->phy_interface);
return -EOPNOTSUPP;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
index 0c011a47d5a3..697bba641e05 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
@@ -38,11 +38,10 @@
#define GMAC_INT_DISABLE_PCSAN BIT(2)
#define GMAC_INT_DISABLE_PMT BIT(3)
#define GMAC_INT_DISABLE_TIMESTAMP BIT(9)
-#define GMAC_INT_DISABLE_PCS (GMAC_INT_DISABLE_RGMII | \
+#define GMAC_INT_DEFAULT_MASK (GMAC_INT_DISABLE_RGMII | \
GMAC_INT_DISABLE_PCSLINK | \
- GMAC_INT_DISABLE_PCSAN)
-#define GMAC_INT_DEFAULT_MASK (GMAC_INT_DISABLE_TIMESTAMP | \
- GMAC_INT_DISABLE_PCS)
+ GMAC_INT_DISABLE_PCSAN | \
+ GMAC_INT_DISABLE_TIMESTAMP)
/* PMT Control and Status */
#define GMAC_PMT 0x0000002c
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
index fe776ddf6889..a2ae136d2c0e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
@@ -22,47 +22,35 @@
#include "stmmac_ptp.h"
#include "dwmac1000.h"
+static int dwmac1000_pcs_init(struct stmmac_priv *priv)
+{
+ if (!priv->dma_cap.pcs)
+ return 0;
+
+ return stmmac_integrated_pcs_init(priv, GMAC_PCS_BASE,
+ GMAC_INT_DISABLE_PCSLINK |
+ GMAC_INT_DISABLE_PCSAN);
+}
+
static void dwmac1000_core_init(struct mac_device_info *hw,
struct net_device *dev)
{
void __iomem *ioaddr = hw->pcsr;
- u32 value = readl(ioaddr + GMAC_CONTROL);
int mtu = dev->mtu;
+ u32 value;
/* Configure GMAC core */
- value |= GMAC_CORE_INIT;
+ value = readl(ioaddr + GMAC_CONTROL);
if (mtu > 1500)
value |= GMAC_CONTROL_2K;
if (mtu > 2000)
value |= GMAC_CONTROL_JE;
- if (hw->ps) {
- value |= GMAC_CONTROL_TE;
-
- value &= ~hw->link.speed_mask;
- switch (hw->ps) {
- case SPEED_1000:
- value |= hw->link.speed1000;
- break;
- case SPEED_100:
- value |= hw->link.speed100;
- break;
- case SPEED_10:
- value |= hw->link.speed10;
- break;
- }
- }
-
- writel(value, ioaddr + GMAC_CONTROL);
+ writel(value | GMAC_CORE_INIT, ioaddr + GMAC_CONTROL);
/* Mask GMAC interrupts */
- value = GMAC_INT_DEFAULT_MASK;
-
- if (hw->pcs)
- value &= ~GMAC_INT_DISABLE_PCS;
-
- writel(value, ioaddr + GMAC_INT_MASK);
+ writel(GMAC_INT_DEFAULT_MASK, ioaddr + GMAC_INT_MASK);
#ifdef STMMAC_VLAN_TAG_USED
/* Tag detection without filtering */
@@ -70,6 +58,20 @@ static void dwmac1000_core_init(struct mac_device_info *hw,
#endif
}
+static void dwmac1000_irq_modify(struct mac_device_info *hw, u32 disable,
+ u32 enable)
+{
+ void __iomem *int_mask = hw->pcsr + GMAC_INT_MASK;
+ unsigned long flags;
+ u32 value;
+
+ spin_lock_irqsave(&hw->irq_ctrl_lock, flags);
+ value = readl(int_mask) | disable;
+ value &= ~enable;
+ writel(value, int_mask);
+ spin_unlock_irqrestore(&hw->irq_ctrl_lock, flags);
+}
+
static int dwmac1000_rx_ipc_enable(struct mac_device_info *hw)
{
void __iomem *ioaddr = hw->pcsr;
@@ -263,39 +265,6 @@ static void dwmac1000_pmt(struct mac_device_info *hw, unsigned long mode)
writel(pmt, ioaddr + GMAC_PMT);
}
-/* RGMII or SMII interface */
-static void dwmac1000_rgsmii(void __iomem *ioaddr, struct stmmac_extra_stats *x)
-{
- u32 status;
-
- status = readl(ioaddr + GMAC_RGSMIIIS);
- x->irq_rgmii_n++;
-
- /* Check the link status */
- if (status & GMAC_RGSMIIIS_LNKSTS) {
- int speed_value;
-
- x->pcs_link = 1;
-
- speed_value = ((status & GMAC_RGSMIIIS_SPEED) >>
- GMAC_RGSMIIIS_SPEED_SHIFT);
- if (speed_value == GMAC_RGSMIIIS_SPEED_125)
- x->pcs_speed = SPEED_1000;
- else if (speed_value == GMAC_RGSMIIIS_SPEED_25)
- x->pcs_speed = SPEED_100;
- else
- x->pcs_speed = SPEED_10;
-
- x->pcs_duplex = (status & GMAC_RGSMIIIS_LNKMOD_MASK);
-
- pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed,
- x->pcs_duplex ? "Full" : "Half");
- } else {
- x->pcs_link = 0;
- pr_info("Link is Down\n");
- }
-}
-
static int dwmac1000_irq_status(struct mac_device_info *hw,
struct stmmac_extra_stats *x)
{
@@ -337,9 +306,6 @@ static int dwmac1000_irq_status(struct mac_device_info *hw,
dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x);
- if (intr_status & PCS_RGSMIIIS_IRQ)
- dwmac1000_rgsmii(ioaddr, x);
-
return ret;
}
@@ -394,9 +360,9 @@ static void dwmac1000_set_eee_timer(struct mac_device_info *hw, int ls, int tw)
}
static void dwmac1000_ctrl_ane(struct stmmac_priv *priv, bool ane,
- bool srgmi_ral, bool loopback)
+ bool srgmi_ral)
{
- dwmac_ctrl_ane(priv->ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback);
+ dwmac_ctrl_ane(priv->ioaddr, GMAC_PCS_BASE, ane, srgmi_ral);
}
static void dwmac1000_debug(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -488,7 +454,9 @@ static void dwmac1000_set_mac_loopback(void __iomem *ioaddr, bool enable)
}
const struct stmmac_ops dwmac1000_ops = {
+ .pcs_init = dwmac1000_pcs_init,
.core_init = dwmac1000_core_init,
+ .irq_modify = dwmac1000_irq_modify,
.set_mac = stmmac_set_mac,
.rx_ipc = dwmac1000_rx_ipc_enable,
.dump_regs = dwmac1000_dump_regs,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index 118a22406a2e..5877fec9f6c3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -19,7 +19,6 @@
static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
{
u32 value = readl(ioaddr + DMA_AXI_BUS_MODE);
- int i;
pr_info("dwmac1000: Master AXI performs %s burst length\n",
!(value & DMA_AXI_UNDEF) ? "fixed" : "any");
@@ -39,33 +38,10 @@ static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
/* Depending on the UNDEF bit the Master AXI will perform any burst
* length according to the BLEN programmed (by default all BLEN are
- * set).
+ * set). Note that the UNDEF bit is readonly, and is the inverse of
+ * Bus Mode bit 16.
*/
- for (i = 0; i < AXI_BLEN; i++) {
- switch (axi->axi_blen[i]) {
- case 256:
- value |= DMA_AXI_BLEN256;
- break;
- case 128:
- value |= DMA_AXI_BLEN128;
- break;
- case 64:
- value |= DMA_AXI_BLEN64;
- break;
- case 32:
- value |= DMA_AXI_BLEN32;
- break;
- case 16:
- value |= DMA_AXI_BLEN16;
- break;
- case 8:
- value |= DMA_AXI_BLEN8;
- break;
- case 4:
- value |= DMA_AXI_BLEN4;
- break;
- }
- }
+ value = (value & ~DMA_AXI_BLEN_MASK) | axi->axi_blen_regval;
writel(value, ioaddr + DMA_AXI_BUS_MODE);
}
@@ -159,10 +135,10 @@ static void dwmac1000_dma_operation_mode_rx(struct stmmac_priv *priv,
if (mode == SF_DMA_MODE) {
pr_debug("GMAC: enable RX store and forward mode\n");
- csr6 |= DMA_CONTROL_RSF;
+ csr6 |= DMA_CONTROL_RSF | DMA_CONTROL_DFF;
} else {
pr_debug("GMAC: disable RX SF mode (threshold %d)\n", mode);
- csr6 &= ~DMA_CONTROL_RSF;
+ csr6 &= ~(DMA_CONTROL_RSF | DMA_CONTROL_DFF);
csr6 &= DMA_CONTROL_TC_RX_MASK;
if (mode <= 32)
csr6 |= DMA_CONTROL_RTC_32;
@@ -286,6 +262,7 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = {
.dma_rx_mode = dwmac1000_dma_operation_mode_rx,
.dma_tx_mode = dwmac1000_dma_operation_mode_tx,
.enable_dma_transmission = dwmac_enable_dma_transmission,
+ .enable_dma_reception = dwmac_enable_dma_reception,
.enable_dma_irq = dwmac_enable_dma_irq,
.disable_dma_irq = dwmac_disable_dma_irq,
.start_tx = dwmac_dma_start_tx,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
index 3dec1a264cf6..3cb733781e1e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h
@@ -106,9 +106,6 @@
#define GMAC_INT_LPI_EN BIT(5)
#define GMAC_INT_TSIE BIT(12)
-#define GMAC_PCS_IRQ_DEFAULT (GMAC_INT_RGSMIIS | GMAC_INT_PCS_LINK | \
- GMAC_INT_PCS_ANE)
-
#define GMAC_INT_DEFAULT_ENABLE (GMAC_INT_PMT_EN | GMAC_INT_LPI_EN | \
GMAC_INT_TSIE)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
index d85bc0bb5c3c..a4282fd7c3c7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
@@ -22,51 +22,51 @@
#include "dwmac4.h"
#include "dwmac5.h"
+static int dwmac4_pcs_init(struct stmmac_priv *priv)
+{
+ if (!priv->dma_cap.pcs)
+ return 0;
+
+ return stmmac_integrated_pcs_init(priv, GMAC_PCS_BASE,
+ GMAC_INT_PCS_LINK | GMAC_INT_PCS_ANE);
+}
+
static void dwmac4_core_init(struct mac_device_info *hw,
struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
void __iomem *ioaddr = hw->pcsr;
- u32 value = readl(ioaddr + GMAC_CONFIG);
unsigned long clk_rate;
+ u32 value;
- value |= GMAC_CORE_INIT;
-
- if (hw->ps) {
- value |= GMAC_CONFIG_TE;
-
- value &= hw->link.speed_mask;
- switch (hw->ps) {
- case SPEED_1000:
- value |= hw->link.speed1000;
- break;
- case SPEED_100:
- value |= hw->link.speed100;
- break;
- case SPEED_10:
- value |= hw->link.speed10;
- break;
- }
- }
-
- writel(value, ioaddr + GMAC_CONFIG);
+ value = readl(ioaddr + GMAC_CONFIG);
+ writel(value | GMAC_CORE_INIT, ioaddr + GMAC_CONFIG);
/* Configure LPI 1us counter to number of CSR clock ticks in 1us - 1 */
clk_rate = clk_get_rate(priv->plat->stmmac_clk);
writel((clk_rate / 1000000) - 1, ioaddr + GMAC4_MAC_ONEUS_TIC_COUNTER);
/* Enable GMAC interrupts */
- value = GMAC_INT_DEFAULT_ENABLE;
-
- if (hw->pcs)
- value |= GMAC_PCS_IRQ_DEFAULT;
-
- writel(value, ioaddr + GMAC_INT_EN);
+ writel(GMAC_INT_DEFAULT_ENABLE, ioaddr + GMAC_INT_EN);
if (GMAC_INT_DEFAULT_ENABLE & GMAC_INT_TSIE)
init_waitqueue_head(&priv->tstamp_busy_wait);
}
+static void dwmac4_irq_modify(struct mac_device_info *hw, u32 disable,
+ u32 enable)
+{
+ void __iomem *int_mask = hw->pcsr + GMAC_INT_EN;
+ unsigned long flags;
+ u32 value;
+
+ spin_lock_irqsave(&hw->irq_ctrl_lock, flags);
+ value = readl(int_mask) & ~disable;
+ value |= enable;
+ writel(value, int_mask);
+ spin_unlock_irqrestore(&hw->irq_ctrl_lock, flags);
+}
+
static void dwmac4_update_caps(struct stmmac_priv *priv)
{
if (priv->plat->tx_queues_to_use > 1)
@@ -583,43 +583,9 @@ static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex,
}
}
-static void dwmac4_ctrl_ane(struct stmmac_priv *priv, bool ane, bool srgmi_ral,
- bool loopback)
-{
- dwmac_ctrl_ane(priv->ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback);
-}
-
-/* RGMII or SMII interface */
-static void dwmac4_phystatus(void __iomem *ioaddr, struct stmmac_extra_stats *x)
+static void dwmac4_ctrl_ane(struct stmmac_priv *priv, bool ane, bool srgmi_ral)
{
- u32 status;
-
- status = readl(ioaddr + GMAC_PHYIF_CONTROL_STATUS);
- x->irq_rgmii_n++;
-
- /* Check the link status */
- if (status & GMAC_PHYIF_CTRLSTATUS_LNKSTS) {
- int speed_value;
-
- x->pcs_link = 1;
-
- speed_value = ((status & GMAC_PHYIF_CTRLSTATUS_SPEED) >>
- GMAC_PHYIF_CTRLSTATUS_SPEED_SHIFT);
- if (speed_value == GMAC_PHYIF_CTRLSTATUS_SPEED_125)
- x->pcs_speed = SPEED_1000;
- else if (speed_value == GMAC_PHYIF_CTRLSTATUS_SPEED_25)
- x->pcs_speed = SPEED_100;
- else
- x->pcs_speed = SPEED_10;
-
- x->pcs_duplex = (status & GMAC_PHYIF_CTRLSTATUS_LNKMOD);
-
- pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed,
- x->pcs_duplex ? "Full" : "Half");
- } else {
- x->pcs_link = 0;
- pr_info("Link is Down\n");
- }
+ dwmac_ctrl_ane(priv->ioaddr, GMAC_PCS_BASE, ane, srgmi_ral);
}
static int dwmac4_irq_mtl_status(struct stmmac_priv *priv,
@@ -693,8 +659,6 @@ static int dwmac4_irq_status(struct mac_device_info *hw,
}
dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x);
- if (intr_status & PCS_RGSMIIIS_IRQ)
- dwmac4_phystatus(ioaddr, x);
return ret;
}
@@ -929,7 +893,9 @@ static int dwmac4_config_l4_filter(struct mac_device_info *hw, u32 filter_no,
}
const struct stmmac_ops dwmac4_ops = {
+ .pcs_init = dwmac4_pcs_init,
.core_init = dwmac4_core_init,
+ .irq_modify = dwmac4_irq_modify,
.update_caps = dwmac4_update_caps,
.set_mac = stmmac_set_mac,
.rx_ipc = dwmac4_rx_ipc_enable,
@@ -963,7 +929,9 @@ const struct stmmac_ops dwmac4_ops = {
};
const struct stmmac_ops dwmac410_ops = {
+ .pcs_init = dwmac4_pcs_init,
.core_init = dwmac4_core_init,
+ .irq_modify = dwmac4_irq_modify,
.update_caps = dwmac4_update_caps,
.set_mac = stmmac_dwmac4_set_mac,
.rx_ipc = dwmac4_rx_ipc_enable,
@@ -999,7 +967,9 @@ const struct stmmac_ops dwmac410_ops = {
};
const struct stmmac_ops dwmac510_ops = {
+ .pcs_init = dwmac4_pcs_init,
.core_init = dwmac4_core_init,
+ .irq_modify = dwmac4_irq_modify,
.update_caps = dwmac4_update_caps,
.set_mac = stmmac_dwmac4_set_mac,
.rx_ipc = dwmac4_rx_ipc_enable,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index d87a8b595e6a..7b513324cfb0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -18,7 +18,6 @@
static void dwmac4_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
{
u32 value = readl(ioaddr + DMA_SYS_BUS_MODE);
- int i;
pr_info("dwmac4: Master AXI performs %s burst length\n",
(value & DMA_SYS_BUS_FB) ? "fixed" : "any");
@@ -38,33 +37,10 @@ static void dwmac4_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
/* Depending on the UNDEF bit the Master AXI will perform any burst
* length according to the BLEN programmed (by default all BLEN are
- * set).
+ * set). Note that the UNDEF bit is readonly, and is the inverse of
+ * Bus Mode bit 16.
*/
- for (i = 0; i < AXI_BLEN; i++) {
- switch (axi->axi_blen[i]) {
- case 256:
- value |= DMA_AXI_BLEN256;
- break;
- case 128:
- value |= DMA_AXI_BLEN128;
- break;
- case 64:
- value |= DMA_AXI_BLEN64;
- break;
- case 32:
- value |= DMA_AXI_BLEN32;
- break;
- case 16:
- value |= DMA_AXI_BLEN16;
- break;
- case 8:
- value |= DMA_AXI_BLEN8;
- break;
- case 4:
- value |= DMA_AXI_BLEN4;
- break;
- }
- }
+ value = (value & ~DMA_AXI_BLEN_MASK) | axi->axi_blen_regval;
writel(value, ioaddr + DMA_SYS_BUS_MODE);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
index 4f980dcd3958..f27126f05551 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.h
@@ -69,15 +69,8 @@
#define DMA_SYS_BUS_MB BIT(14)
#define DMA_AXI_1KBBE BIT(13)
-#define DMA_SYS_BUS_AAL BIT(12)
+#define DMA_SYS_BUS_AAL DMA_AXI_AAL
#define DMA_SYS_BUS_EAME BIT(11)
-#define DMA_AXI_BLEN256 BIT(7)
-#define DMA_AXI_BLEN128 BIT(6)
-#define DMA_AXI_BLEN64 BIT(5)
-#define DMA_AXI_BLEN32 BIT(4)
-#define DMA_AXI_BLEN16 BIT(3)
-#define DMA_AXI_BLEN8 BIT(2)
-#define DMA_AXI_BLEN4 BIT(1)
#define DMA_SYS_BUS_FB BIT(0)
#define DMA_BURST_LEN_DEFAULT (DMA_AXI_BLEN256 | DMA_AXI_BLEN128 | \
@@ -85,8 +78,6 @@
DMA_AXI_BLEN16 | DMA_AXI_BLEN8 | \
DMA_AXI_BLEN4)
-#define DMA_AXI_BURST_LEN_MASK 0x000000FE
-
/* DMA TBS Control */
#define DMA_TBS_FTOS GENMASK(31, 8)
#define DMA_TBS_FTOV BIT(0)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
index 5d9c18f5bbf5..054ecb20ce3f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
@@ -68,23 +68,14 @@ static inline u32 dma_chan_base_addr(u32 base, u32 chan)
#define DMA_AXI_OSR_MAX 0xf
#define DMA_AXI_MAX_OSR_LIMIT ((DMA_AXI_OSR_MAX << DMA_AXI_WR_OSR_LMT_SHIFT) | \
(DMA_AXI_OSR_MAX << DMA_AXI_RD_OSR_LMT_SHIFT))
-#define DMA_AXI_1KBBE BIT(13)
-#define DMA_AXI_AAL BIT(12)
-#define DMA_AXI_BLEN256 BIT(7)
-#define DMA_AXI_BLEN128 BIT(6)
-#define DMA_AXI_BLEN64 BIT(5)
-#define DMA_AXI_BLEN32 BIT(4)
-#define DMA_AXI_BLEN16 BIT(3)
-#define DMA_AXI_BLEN8 BIT(2)
-#define DMA_AXI_BLEN4 BIT(1)
#define DMA_BURST_LEN_DEFAULT (DMA_AXI_BLEN256 | DMA_AXI_BLEN128 | \
DMA_AXI_BLEN64 | DMA_AXI_BLEN32 | \
DMA_AXI_BLEN16 | DMA_AXI_BLEN8 | \
DMA_AXI_BLEN4)
-#define DMA_AXI_UNDEF BIT(0)
+#define DMA_AXI_1KBBE BIT(13)
-#define DMA_AXI_BURST_LEN_MASK 0x000000FE
+#define DMA_AXI_UNDEF BIT(0)
#define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */
#define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */
@@ -178,6 +169,7 @@ static inline u32 dma_chan_base_addr(u32 base, u32 chan)
#define NUM_DWMAC4_DMA_REGS 27
void dwmac_enable_dma_transmission(void __iomem *ioaddr, u32 chan);
+void dwmac_enable_dma_reception(void __iomem *ioaddr, u32 chan);
void dwmac_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx);
void dwmac_disable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
index 467f1a05747e..97a803d68e3a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
@@ -33,6 +33,11 @@ void dwmac_enable_dma_transmission(void __iomem *ioaddr, u32 chan)
writel(1, ioaddr + DMA_CHAN_XMT_POLL_DEMAND(chan));
}
+void dwmac_enable_dma_reception(void __iomem *ioaddr, u32 chan)
+{
+ writel(1, ioaddr + DMA_CHAN_RCV_POLL_DEMAND(chan));
+}
+
void dwmac_enable_dma_irq(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx)
{
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
index 0d408ee17f33..fecda3034d36 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2.h
@@ -79,6 +79,7 @@
#define XGMAC_PSRQ(x) GENMASK((x) * 8 + 7, (x) * 8)
#define XGMAC_PSRQ_SHIFT(x) ((x) * 8)
#define XGMAC_INT_STATUS 0x000000b0
+#define XGMAC_INT_TSIS BIT(12)
#define XGMAC_LPIIS BIT(5)
#define XGMAC_PMTIS BIT(4)
#define XGMAC_INT_EN 0x000000b4
@@ -173,6 +174,8 @@
#define XGMAC_MDIO_ADDR 0x00000200
#define XGMAC_MDIO_DATA 0x00000204
#define XGMAC_MDIO_C22P 0x00000220
+#define XGMAC_GPIO_STATUS 0x0000027c
+#define XGMAC_GPIO_GPO0 BIT(16)
#define XGMAC_ADDRx_HIGH(x) (0x00000300 + (x) * 0x8)
#define XGMAC_ADDR_MAX 32
#define XGMAC_AE BIT(31)
@@ -220,6 +223,8 @@
#define XGMAC_OB BIT(0)
#define XGMAC_RSS_DATA 0x00000c8c
#define XGMAC_TIMESTAMP_STATUS 0x00000d20
+#define XGMAC_TIMESTAMP_ATSNS_MASK GENMASK(29, 25)
+#define XGMAC_TIMESTAMP_ATSNS_SHIFT 25
#define XGMAC_TXTSC BIT(15)
#define XGMAC_TXTIMESTAMP_NSEC 0x00000d30
#define XGMAC_TXTSSTSLO GENMASK(30, 0)
@@ -333,16 +338,9 @@
#define XGMAC_RD_OSR_LMT_SHIFT 16
#define XGMAC_EN_LPI BIT(15)
#define XGMAC_LPI_XIT_PKT BIT(14)
-#define XGMAC_AAL BIT(12)
+#define XGMAC_AAL DMA_AXI_AAL
#define XGMAC_EAME BIT(11)
-#define XGMAC_BLEN GENMASK(7, 1)
-#define XGMAC_BLEN256 BIT(7)
-#define XGMAC_BLEN128 BIT(6)
-#define XGMAC_BLEN64 BIT(5)
-#define XGMAC_BLEN32 BIT(4)
-#define XGMAC_BLEN16 BIT(3)
-#define XGMAC_BLEN8 BIT(2)
-#define XGMAC_BLEN4 BIT(1)
+/* XGMAC_BLEN* are now defined as DMA_AXI_BLEN* in common.h */
#define XGMAC_UNDEF BIT(0)
#define XGMAC_TX_EDMA_CTRL 0x00003040
#define XGMAC_TDPS GENMASK(29, 0)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
index 00e929bf280b..b40b3ea50e25 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_core.c
@@ -23,30 +23,23 @@ static void dwxgmac2_core_init(struct mac_device_info *hw,
tx = readl(ioaddr + XGMAC_TX_CONFIG);
rx = readl(ioaddr + XGMAC_RX_CONFIG);
- tx |= XGMAC_CORE_INIT_TX;
- rx |= XGMAC_CORE_INIT_RX;
-
- if (hw->ps) {
- tx |= XGMAC_CONFIG_TE;
- tx &= ~hw->link.speed_mask;
+ writel(tx | XGMAC_CORE_INIT_TX, ioaddr + XGMAC_TX_CONFIG);
+ writel(rx | XGMAC_CORE_INIT_RX, ioaddr + XGMAC_RX_CONFIG);
+ writel(XGMAC_INT_DEFAULT_EN, ioaddr + XGMAC_INT_EN);
+}
- switch (hw->ps) {
- case SPEED_10000:
- tx |= hw->link.xgmii.speed10000;
- break;
- case SPEED_2500:
- tx |= hw->link.speed2500;
- break;
- case SPEED_1000:
- default:
- tx |= hw->link.speed1000;
- break;
- }
- }
+static void dwxgmac2_irq_modify(struct mac_device_info *hw, u32 disable,
+ u32 enable)
+{
+ void __iomem *int_mask = hw->pcsr + XGMAC_INT_EN;
+ unsigned long flags;
+ u32 value;
- writel(tx, ioaddr + XGMAC_TX_CONFIG);
- writel(rx, ioaddr + XGMAC_RX_CONFIG);
- writel(XGMAC_INT_DEFAULT_EN, ioaddr + XGMAC_INT_EN);
+ spin_lock_irqsave(&hw->irq_ctrl_lock, flags);
+ value = readl(int_mask) & ~disable;
+ value |= enable;
+ writel(value, int_mask);
+ spin_unlock_irqrestore(&hw->irq_ctrl_lock, flags);
}
static void dwxgmac2_update_caps(struct stmmac_priv *priv)
@@ -1432,6 +1425,7 @@ static void dwxgmac2_set_arp_offload(struct mac_device_info *hw, bool en,
const struct stmmac_ops dwxgmac210_ops = {
.core_init = dwxgmac2_core_init,
+ .irq_modify = dwxgmac2_irq_modify,
.update_caps = dwxgmac2_update_caps,
.set_mac = dwxgmac2_set_mac,
.rx_ipc = dwxgmac2_rx_ipc,
@@ -1487,6 +1481,7 @@ static void dwxlgmac2_rx_queue_enable(struct mac_device_info *hw, u8 mode,
const struct stmmac_ops dwxlgmac2_ops = {
.core_init = dwxgmac2_core_init,
+ .irq_modify = dwxgmac2_irq_modify,
.set_mac = dwxgmac2_set_mac,
.rx_ipc = dwxgmac2_rx_ipc,
.rx_queue_enable = dwxlgmac2_rx_queue_enable,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index 4d6bb995d8d8..cc1bdc0975d5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
@@ -84,7 +84,6 @@ static void dwxgmac2_dma_init_tx_chan(struct stmmac_priv *priv,
static void dwxgmac2_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
{
u32 value = readl(ioaddr + XGMAC_DMA_SYSBUS_MODE);
- int i;
if (axi->axi_lpi_en)
value |= XGMAC_EN_LPI;
@@ -102,32 +101,12 @@ static void dwxgmac2_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
if (!axi->axi_fb)
value |= XGMAC_UNDEF;
- value &= ~XGMAC_BLEN;
- for (i = 0; i < AXI_BLEN; i++) {
- switch (axi->axi_blen[i]) {
- case 256:
- value |= XGMAC_BLEN256;
- break;
- case 128:
- value |= XGMAC_BLEN128;
- break;
- case 64:
- value |= XGMAC_BLEN64;
- break;
- case 32:
- value |= XGMAC_BLEN32;
- break;
- case 16:
- value |= XGMAC_BLEN16;
- break;
- case 8:
- value |= XGMAC_BLEN8;
- break;
- case 4:
- value |= XGMAC_BLEN4;
- break;
- }
- }
+ /* Depending on the UNDEF bit the Master AXI will perform any burst
+ * length according to the BLEN programmed (by default all BLEN are
+ * set). Note that the UNDEF bit is readonly, and is the inverse of
+ * Bus Mode bit 16.
+ */
+ value = (value & ~DMA_AXI_BLEN_MASK) | axi->axi_blen_regval;
writel(value, ioaddr + XGMAC_DMA_SYSBUS_MODE);
writel(XGMAC_TDPS, ioaddr + XGMAC_TX_EDMA_CTRL);
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.c b/drivers/net/ethernet/stmicro/stmmac/hwif.c
index 3f7c765dcb79..014f7cd79a3c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.c
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.c
@@ -13,31 +13,42 @@
#include "dwmac4_descs.h"
#include "dwxgmac2.h"
-static u32 stmmac_get_id(struct stmmac_priv *priv, u32 id_reg)
+struct stmmac_version {
+ u8 snpsver;
+ u8 dev_id;
+};
+
+static void stmmac_get_version(struct stmmac_priv *priv,
+ struct stmmac_version *ver)
{
- u32 reg = readl(priv->ioaddr + id_reg);
+ enum dwmac_core_type core_type = priv->plat->core_type;
+ unsigned int version_offset;
+ u32 version;
- if (!reg) {
- dev_info(priv->device, "Version ID not available\n");
- return 0x0;
- }
+ ver->snpsver = 0;
+ ver->dev_id = 0;
- dev_info(priv->device, "User ID: 0x%x, Synopsys ID: 0x%x\n",
- (unsigned int)(reg & GENMASK(15, 8)) >> 8,
- (unsigned int)(reg & GENMASK(7, 0)));
- return reg & GENMASK(7, 0);
-}
+ if (core_type == DWMAC_CORE_MAC100)
+ return;
-static u32 stmmac_get_dev_id(struct stmmac_priv *priv, u32 id_reg)
-{
- u32 reg = readl(priv->ioaddr + id_reg);
+ if (core_type == DWMAC_CORE_GMAC)
+ version_offset = GMAC_VERSION;
+ else
+ version_offset = GMAC4_VERSION;
- if (!reg) {
+ version = readl(priv->ioaddr + version_offset);
+ if (version == 0) {
dev_info(priv->device, "Version ID not available\n");
- return 0x0;
+ return;
}
- return (reg & GENMASK(15, 8)) >> 8;
+ dev_info(priv->device, "User ID: 0x%x, Synopsys ID: 0x%x\n",
+ FIELD_GET(DWMAC_USERVER, version),
+ FIELD_GET(DWMAC_SNPSVER, version));
+
+ ver->snpsver = FIELD_GET(DWMAC_SNPSVER, version);
+ if (core_type == DWMAC_CORE_XGMAC)
+ ver->dev_id = FIELD_GET(DWMAC_USERVER, version);
}
static void stmmac_dwmac_mode_quirk(struct stmmac_priv *priv)
@@ -92,12 +103,10 @@ static int stmmac_dwxlgmac_quirks(struct stmmac_priv *priv)
return 0;
}
-int stmmac_reset(struct stmmac_priv *priv, void __iomem *ioaddr)
+int stmmac_reset(struct stmmac_priv *priv)
{
- struct plat_stmmacenet_data *plat = priv ? priv->plat : NULL;
-
- if (!priv)
- return -EINVAL;
+ struct plat_stmmacenet_data *plat = priv->plat;
+ void __iomem *ioaddr = priv->ioaddr;
if (plat && plat->fix_soc_reset)
return plat->fix_soc_reset(priv, ioaddr);
@@ -106,9 +115,7 @@ int stmmac_reset(struct stmmac_priv *priv, void __iomem *ioaddr)
}
static const struct stmmac_hwif_entry {
- bool gmac;
- bool gmac4;
- bool xgmac;
+ enum dwmac_core_type core_type;
u32 min_id;
u32 dev_id;
const struct stmmac_regs_off regs;
@@ -127,9 +134,7 @@ static const struct stmmac_hwif_entry {
} stmmac_hw[] = {
/* NOTE: New HW versions shall go to the end of this table */
{
- .gmac = false,
- .gmac4 = false,
- .xgmac = false,
+ .core_type = DWMAC_CORE_MAC100,
.min_id = 0,
.regs = {
.ptp_off = PTP_GMAC3_X_OFFSET,
@@ -146,9 +151,7 @@ static const struct stmmac_hwif_entry {
.setup = dwmac100_setup,
.quirks = stmmac_dwmac1_quirks,
}, {
- .gmac = true,
- .gmac4 = false,
- .xgmac = false,
+ .core_type = DWMAC_CORE_GMAC,
.min_id = 0,
.regs = {
.ptp_off = PTP_GMAC3_X_OFFSET,
@@ -165,9 +168,7 @@ static const struct stmmac_hwif_entry {
.setup = dwmac1000_setup,
.quirks = stmmac_dwmac1_quirks,
}, {
- .gmac = false,
- .gmac4 = true,
- .xgmac = false,
+ .core_type = DWMAC_CORE_GMAC4,
.min_id = 0,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -187,9 +188,7 @@ static const struct stmmac_hwif_entry {
.setup = dwmac4_setup,
.quirks = stmmac_dwmac4_quirks,
}, {
- .gmac = false,
- .gmac4 = true,
- .xgmac = false,
+ .core_type = DWMAC_CORE_GMAC4,
.min_id = DWMAC_CORE_4_00,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -210,9 +209,7 @@ static const struct stmmac_hwif_entry {
.setup = dwmac4_setup,
.quirks = NULL,
}, {
- .gmac = false,
- .gmac4 = true,
- .xgmac = false,
+ .core_type = DWMAC_CORE_GMAC4,
.min_id = DWMAC_CORE_4_10,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -233,9 +230,7 @@ static const struct stmmac_hwif_entry {
.setup = dwmac4_setup,
.quirks = NULL,
}, {
- .gmac = false,
- .gmac4 = true,
- .xgmac = false,
+ .core_type = DWMAC_CORE_GMAC4,
.min_id = DWMAC_CORE_5_10,
.regs = {
.ptp_off = PTP_GMAC4_OFFSET,
@@ -256,9 +251,7 @@ static const struct stmmac_hwif_entry {
.setup = dwmac4_setup,
.quirks = NULL,
}, {
- .gmac = false,
- .gmac4 = false,
- .xgmac = true,
+ .core_type = DWMAC_CORE_XGMAC,
.min_id = DWXGMAC_CORE_2_10,
.dev_id = DWXGMAC_ID,
.regs = {
@@ -280,9 +273,7 @@ static const struct stmmac_hwif_entry {
.setup = dwxgmac2_setup,
.quirks = NULL,
}, {
- .gmac = false,
- .gmac4 = false,
- .xgmac = true,
+ .core_type = DWMAC_CORE_XGMAC,
.min_id = DWXLGMAC_CORE_2_00,
.dev_id = DWXLGMAC_ID,
.regs = {
@@ -306,100 +297,114 @@ static const struct stmmac_hwif_entry {
},
};
+static const struct stmmac_hwif_entry *
+stmmac_hwif_find(enum dwmac_core_type core_type, u8 snpsver, u8 dev_id)
+{
+ const struct stmmac_hwif_entry *entry;
+ int i;
+
+ for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) {
+ entry = &stmmac_hw[i];
+
+ if (core_type != entry->core_type)
+ continue;
+ /* Use synopsys_id var because some setups can override this */
+ if (snpsver < entry->min_id)
+ continue;
+ if (core_type == DWMAC_CORE_XGMAC &&
+ dev_id != entry->dev_id)
+ continue;
+
+ return entry;
+ }
+
+ return NULL;
+}
+
int stmmac_hwif_init(struct stmmac_priv *priv)
{
- bool needs_xgmac = priv->plat->has_xgmac;
- bool needs_gmac4 = priv->plat->has_gmac4;
- bool needs_gmac = priv->plat->has_gmac;
+ enum dwmac_core_type core_type = priv->plat->core_type;
const struct stmmac_hwif_entry *entry;
+ struct stmmac_version version;
struct mac_device_info *mac;
bool needs_setup = true;
- u32 id, dev_id = 0;
- int i, ret;
-
- if (needs_gmac) {
- id = stmmac_get_id(priv, GMAC_VERSION);
- } else if (needs_gmac4 || needs_xgmac) {
- id = stmmac_get_id(priv, GMAC4_VERSION);
- if (needs_xgmac)
- dev_id = stmmac_get_dev_id(priv, GMAC4_VERSION);
- } else {
- id = 0;
- }
+ int ret;
+
+ stmmac_get_version(priv, &version);
/* Save ID for later use */
- priv->synopsys_id = id;
+ priv->synopsys_id = version.snpsver;
/* Lets assume some safe values first */
- priv->ptpaddr = priv->ioaddr +
- (needs_gmac4 ? PTP_GMAC4_OFFSET : PTP_GMAC3_X_OFFSET);
- priv->mmcaddr = priv->ioaddr +
- (needs_gmac4 ? MMC_GMAC4_OFFSET : MMC_GMAC3_X_OFFSET);
- if (needs_gmac4)
+ if (core_type == DWMAC_CORE_GMAC4) {
+ priv->ptpaddr = priv->ioaddr + PTP_GMAC4_OFFSET;
+ priv->mmcaddr = priv->ioaddr + MMC_GMAC4_OFFSET;
priv->estaddr = priv->ioaddr + EST_GMAC4_OFFSET;
- else if (needs_xgmac)
- priv->estaddr = priv->ioaddr + EST_XGMAC_OFFSET;
-
- /* Check for HW specific setup first */
- if (priv->plat->setup) {
- mac = priv->plat->setup(priv);
- needs_setup = false;
} else {
- mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
+ priv->ptpaddr = priv->ioaddr + PTP_GMAC3_X_OFFSET;
+ priv->mmcaddr = priv->ioaddr + MMC_GMAC3_X_OFFSET;
+ if (core_type == DWMAC_CORE_XGMAC)
+ priv->estaddr = priv->ioaddr + EST_XGMAC_OFFSET;
}
+ mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
if (!mac)
return -ENOMEM;
+ /* Check for HW specific setup first */
+ if (priv->plat->mac_setup) {
+ ret = priv->plat->mac_setup(priv, mac);
+ if (ret)
+ return ret;
+
+ needs_setup = false;
+ }
+
+ spin_lock_init(&mac->irq_ctrl_lock);
+
/* Fallback to generic HW */
- for (i = ARRAY_SIZE(stmmac_hw) - 1; i >= 0; i--) {
- entry = &stmmac_hw[i];
- if (needs_gmac ^ entry->gmac)
- continue;
- if (needs_gmac4 ^ entry->gmac4)
- continue;
- if (needs_xgmac ^ entry->xgmac)
- continue;
- /* Use synopsys_id var because some setups can override this */
- if (priv->synopsys_id < entry->min_id)
- continue;
- if (needs_xgmac && (dev_id ^ entry->dev_id))
- continue;
+ /* Use synopsys_id var because some setups can override this */
+ entry = stmmac_hwif_find(core_type, priv->synopsys_id, version.dev_id);
+ if (!entry) {
+ dev_err(priv->device,
+ "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n",
+ version.snpsver, core_type == DWMAC_CORE_GMAC,
+ core_type == DWMAC_CORE_GMAC4);
- /* Only use generic HW helpers if needed */
- mac->desc = mac->desc ? : entry->desc;
- mac->dma = mac->dma ? : entry->dma;
- mac->mac = mac->mac ? : entry->mac;
- mac->ptp = mac->ptp ? : entry->hwtimestamp;
- mac->mode = mac->mode ? : entry->mode;
- mac->tc = mac->tc ? : entry->tc;
- mac->mmc = mac->mmc ? : entry->mmc;
- mac->est = mac->est ? : entry->est;
- mac->vlan = mac->vlan ? : entry->vlan;
-
- priv->hw = mac;
- priv->fpe_cfg.reg = entry->regs.fpe_reg;
- priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
- priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
- memcpy(&priv->ptp_clock_ops, entry->ptp,
- sizeof(struct ptp_clock_info));
- if (entry->est)
- priv->estaddr = priv->ioaddr + entry->regs.est_off;
-
- /* Entry found */
- if (needs_setup) {
- ret = entry->setup(priv);
- if (ret)
- return ret;
- }
+ return -EINVAL;
+ }
- /* Save quirks, if needed for posterior use */
- priv->hwif_quirks = entry->quirks;
- return 0;
+ /* Only use generic HW helpers if needed */
+ mac->desc = mac->desc ? : entry->desc;
+ mac->dma = mac->dma ? : entry->dma;
+ mac->mac = mac->mac ? : entry->mac;
+ mac->ptp = mac->ptp ? : entry->hwtimestamp;
+ mac->mode = mac->mode ? : entry->mode;
+ mac->tc = mac->tc ? : entry->tc;
+ mac->mmc = mac->mmc ? : entry->mmc;
+ mac->est = mac->est ? : entry->est;
+ mac->vlan = mac->vlan ? : entry->vlan;
+
+ priv->hw = mac;
+ priv->fpe_cfg.reg = entry->regs.fpe_reg;
+ priv->ptpaddr = priv->ioaddr + entry->regs.ptp_off;
+ priv->mmcaddr = priv->ioaddr + entry->regs.mmc_off;
+ memcpy(&priv->ptp_clock_ops, entry->ptp,
+ sizeof(struct ptp_clock_info));
+
+ if (entry->est)
+ priv->estaddr = priv->ioaddr + entry->regs.est_off;
+
+ /* Entry found */
+ if (needs_setup) {
+ ret = entry->setup(priv);
+ if (ret)
+ return ret;
}
- dev_err(priv->device, "Failed to find HW IF (id=0x%x, gmac=%d/%d)\n",
- id, needs_gmac, needs_gmac4);
- return -EINVAL;
+ /* Save quirks, if needed for posterior use */
+ priv->hwif_quirks = entry->quirks;
+
+ return 0;
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 14dbe0685997..df6e8a567b1f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -201,6 +201,7 @@ struct stmmac_dma_ops {
void (*dma_diagnostic_fr)(struct stmmac_extra_stats *x,
void __iomem *ioaddr);
void (*enable_dma_transmission)(void __iomem *ioaddr, u32 chan);
+ void (*enable_dma_reception)(void __iomem *ioaddr, u32 chan);
void (*enable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr,
u32 chan, bool rx, bool tx);
void (*disable_dma_irq)(struct stmmac_priv *priv, void __iomem *ioaddr,
@@ -261,6 +262,8 @@ struct stmmac_dma_ops {
stmmac_do_void_callback(__priv, dma, dma_diagnostic_fr, __args)
#define stmmac_enable_dma_transmission(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, enable_dma_transmission, __args)
+#define stmmac_enable_dma_reception(__priv, __args...) \
+ stmmac_do_void_callback(__priv, dma, enable_dma_reception, __args)
#define stmmac_enable_dma_irq(__priv, __args...) \
stmmac_do_void_callback(__priv, dma, enable_dma_irq, __priv, __args)
#define stmmac_disable_dma_irq(__priv, __args...) \
@@ -313,10 +316,14 @@ enum stmmac_lpi_mode {
/* Helpers to program the MAC core */
struct stmmac_ops {
+ /* Initialise any PCS instances */
+ int (*pcs_init)(struct stmmac_priv *priv);
/* MAC core initialization */
void (*core_init)(struct mac_device_info *hw, struct net_device *dev);
/* Update MAC capabilities */
void (*update_caps)(struct stmmac_priv *priv);
+ /* Change the interrupt enable setting. Enable takes precedence. */
+ void (*irq_modify)(struct mac_device_info *hw, u32 disable, u32 enable);
/* Enable the MAC RX/TX */
void (*set_mac)(void __iomem *ioaddr, bool enable);
/* Enable and verify that the IPC module is supported */
@@ -374,8 +381,8 @@ struct stmmac_ops {
struct stmmac_extra_stats *x, u32 rx_queues,
u32 tx_queues);
/* PCS calls */
- void (*pcs_ctrl_ane)(struct stmmac_priv *priv, bool ane, bool srgmi_ral,
- bool loopback);
+ void (*pcs_ctrl_ane)(struct stmmac_priv *priv, bool ane,
+ bool srgmi_ral);
/* Safety Features */
int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp,
struct stmmac_safety_feature_cfg *safety_cfg);
@@ -413,10 +420,14 @@ struct stmmac_ops {
u32 pclass);
};
+#define stmmac_mac_pcs_init(__priv) \
+ stmmac_do_callback(__priv, mac, pcs_init, __priv)
#define stmmac_core_init(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, core_init, __args)
#define stmmac_mac_update_caps(__priv) \
stmmac_do_void_callback(__priv, mac, update_caps, __priv)
+#define stmmac_mac_irq_modify(__priv, __args...) \
+ stmmac_do_void_callback(__priv, mac, irq_modify, (__priv)->hw, __args)
#define stmmac_mac_set(__priv, __args...) \
stmmac_do_void_callback(__priv, mac, set_mac, __args)
#define stmmac_rx_ipc(__priv, __args...) \
@@ -533,7 +544,7 @@ struct stmmac_rx_queue;
struct stmmac_mode_ops {
void (*init) (void *des, dma_addr_t phy_addr, unsigned int size,
unsigned int extend_desc);
- unsigned int (*is_jumbo_frm) (int len, int ehn_desc);
+ bool (*is_jumbo_frm)(unsigned int len, bool enh_desc);
int (*jumbo_frm)(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
int csum);
int (*set_16kib_bfsize)(int mtu);
@@ -690,7 +701,7 @@ extern const struct stmmac_tc_ops dwmac510_tc_ops;
#define GMAC_VERSION 0x00000020 /* GMAC CORE Version */
#define GMAC4_VERSION 0x00000110 /* GMAC4+ CORE Version */
-int stmmac_reset(struct stmmac_priv *priv, void __iomem *ioaddr);
+int stmmac_reset(struct stmmac_priv *priv);
int stmmac_hwif_init(struct stmmac_priv *priv);
#endif /* __STMMAC_HWIF_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
index d218412ca832..382d94a3b972 100644
--- a/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
+++ b/drivers/net/ethernet/stmicro/stmmac/ring_mode.c
@@ -91,14 +91,9 @@ static int jumbo_frm(struct stmmac_tx_queue *tx_q, struct sk_buff *skb,
return entry;
}
-static unsigned int is_jumbo_frm(int len, int enh_desc)
+static bool is_jumbo_frm(unsigned int len, bool enh_desc)
{
- unsigned int ret = 0;
-
- if (len >= BUF_SIZE_4KiB)
- ret = 1;
-
- return ret;
+ return len >= BUF_SIZE_4KiB;
}
static void refill_desc3(struct stmmac_rx_queue *rx_q, struct dma_desc *p)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 7ca5477be390..012b0a477255 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -25,6 +25,8 @@
#include <net/xdp.h>
#include <uapi/linux/bpf.h>
+struct stmmac_pcs;
+
struct stmmac_resources {
void __iomem *addr;
u8 mac[ETH_ALEN];
@@ -252,11 +254,12 @@ struct stmmac_priv {
int hwts_tx_en;
bool tx_path_in_lpi_mode;
bool tso;
- int sph;
- int sph_cap;
+ bool sph_active;
+ bool sph_capable;
u32 sarc_type;
u32 rx_riwt[MTL_MAX_RX_QUEUES];
int hwts_rx_en;
+ bool tsfupdt_coarse;
void __iomem *ioaddr;
struct net_device *dev;
@@ -273,6 +276,8 @@ struct stmmac_priv {
unsigned int pause_time;
struct mii_bus *mii;
+ struct stmmac_pcs *integrated_pcs;
+
struct phylink_config phylink_config;
struct phylink *phylink;
@@ -287,6 +292,7 @@ struct stmmac_priv {
int hw_cap_support;
int synopsys_id;
u32 msg_enable;
+ /* Our MAC Wake-on-Lan options */
int wolopts;
int wol_irq;
u32 gmii_address_bus_config;
@@ -364,6 +370,8 @@ struct stmmac_priv {
/* XDP BPF Program */
unsigned long *af_xdp_zc_qps;
struct bpf_prog *xdp_prog;
+
+ struct devlink *devlink;
};
enum stmmac_state {
@@ -375,19 +383,11 @@ enum stmmac_state {
extern const struct dev_pm_ops stmmac_simple_pm_ops;
-static inline bool stmmac_wol_enabled_mac(struct stmmac_priv *priv)
-{
- return priv->plat->pmt && device_may_wakeup(priv->device);
-}
-
-static inline bool stmmac_wol_enabled_phy(struct stmmac_priv *priv)
-{
- return !priv->plat->pmt && device_may_wakeup(priv->device);
-}
-
int stmmac_mdio_unregister(struct net_device *ndev);
int stmmac_mdio_register(struct net_device *ndev);
int stmmac_mdio_reset(struct mii_bus *mii);
+void stmmac_mdio_lock(struct stmmac_priv *priv);
+void stmmac_mdio_unlock(struct stmmac_priv *priv);
int stmmac_pcs_setup(struct net_device *ndev);
void stmmac_pcs_clean(struct net_device *ndev);
void stmmac_set_ethtool_ops(struct net_device *netdev);
@@ -396,6 +396,7 @@ void stmmac_ptp_register(struct stmmac_priv *priv);
void stmmac_ptp_unregister(struct stmmac_priv *priv);
int stmmac_xdp_open(struct net_device *dev);
void stmmac_xdp_release(struct net_device *dev);
+int stmmac_get_phy_intf_sel(phy_interface_t interface);
int stmmac_resume(struct device *dev);
int stmmac_suspend(struct device *dev);
void stmmac_dvr_remove(struct device *dev);
@@ -407,6 +408,8 @@ int stmmac_reinit_ringparam(struct net_device *dev, u32 rx_size, u32 tx_size);
int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
phy_interface_t interface, int speed);
+struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev);
+
static inline bool stmmac_xdp_is_enabled(struct stmmac_priv *priv)
{
return !!priv->xdp_prog;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c
index 4b513d27a988..afc516059b89 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_est.c
@@ -53,7 +53,7 @@ static int est_configure(struct stmmac_priv *priv, struct stmmac_est *cfg,
}
ctrl = readl(est_addr + EST_CONTROL);
- if (priv->plat->has_xgmac) {
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC) {
ctrl &= ~EST_XGMAC_PTOV;
ctrl |= ((NSEC_PER_SEC / ptp_rate) * EST_XGMAC_PTOV_MUL) <<
EST_XGMAC_PTOV_SHIFT;
@@ -148,7 +148,7 @@ static void est_irq_status(struct stmmac_priv *priv, struct net_device *dev,
}
if (status & EST_BTRE) {
- if (priv->plat->has_xgmac) {
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC) {
btrl = FIELD_GET(EST_XGMAC_BTRL, status);
btrl_max = FIELD_MAX(EST_XGMAC_BTRL);
} else {
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index 39fa1ec92f82..b155e71aac51 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -303,9 +303,10 @@ static void stmmac_ethtool_getdrvinfo(struct net_device *dev,
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (priv->plat->has_gmac || priv->plat->has_gmac4)
+ if (priv->plat->core_type == DWMAC_CORE_GMAC ||
+ priv->plat->core_type == DWMAC_CORE_GMAC4)
strscpy(info->driver, GMAC_ETHTOOL_NAME, sizeof(info->driver));
- else if (priv->plat->has_xgmac)
+ else if (priv->plat->core_type == DWMAC_CORE_XGMAC)
strscpy(info->driver, XGMAC_ETHTOOL_NAME, sizeof(info->driver));
else
strscpy(info->driver, MAC100_ETHTOOL_NAME,
@@ -322,47 +323,6 @@ static int stmmac_ethtool_get_link_ksettings(struct net_device *dev,
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (!(priv->plat->flags & STMMAC_FLAG_HAS_INTEGRATED_PCS) &&
- (priv->hw->pcs & STMMAC_PCS_RGMII ||
- priv->hw->pcs & STMMAC_PCS_SGMII)) {
- u32 supported, advertising, lp_advertising;
-
- if (!priv->xstats.pcs_link) {
- cmd->base.speed = SPEED_UNKNOWN;
- cmd->base.duplex = DUPLEX_UNKNOWN;
- return 0;
- }
- cmd->base.duplex = priv->xstats.pcs_duplex;
-
- cmd->base.speed = priv->xstats.pcs_speed;
-
- /* Encoding of PSE bits is defined in 802.3z, 37.2.1.4 */
-
- ethtool_convert_link_mode_to_legacy_u32(
- &supported, cmd->link_modes.supported);
- ethtool_convert_link_mode_to_legacy_u32(
- &advertising, cmd->link_modes.advertising);
- ethtool_convert_link_mode_to_legacy_u32(
- &lp_advertising, cmd->link_modes.lp_advertising);
-
- /* Reg49[3] always set because ANE is always supported */
- cmd->base.autoneg = ADVERTISED_Autoneg;
- supported |= SUPPORTED_Autoneg;
- advertising |= ADVERTISED_Autoneg;
- lp_advertising |= ADVERTISED_Autoneg;
-
- cmd->base.port = PORT_OTHER;
-
- ethtool_convert_legacy_u32_to_link_mode(
- cmd->link_modes.supported, supported);
- ethtool_convert_legacy_u32_to_link_mode(
- cmd->link_modes.advertising, advertising);
- ethtool_convert_legacy_u32_to_link_mode(
- cmd->link_modes.lp_advertising, lp_advertising);
-
- return 0;
- }
-
return phylink_ethtool_ksettings_get(priv->phylink, cmd);
}
@@ -372,20 +332,6 @@ stmmac_ethtool_set_link_ksettings(struct net_device *dev,
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (!(priv->plat->flags & STMMAC_FLAG_HAS_INTEGRATED_PCS) &&
- (priv->hw->pcs & STMMAC_PCS_RGMII ||
- priv->hw->pcs & STMMAC_PCS_SGMII)) {
- /* Only support ANE */
- if (cmd->base.autoneg != AUTONEG_ENABLE)
- return -EINVAL;
-
- mutex_lock(&priv->lock);
- stmmac_pcs_ctrl_ane(priv, 1, priv->hw->ps, 0);
- mutex_unlock(&priv->lock);
-
- return 0;
- }
-
return phylink_ethtool_ksettings_set(priv->phylink, cmd);
}
@@ -406,9 +352,9 @@ static int stmmac_ethtool_get_regs_len(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (priv->plat->has_xgmac)
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC)
return XGMAC_REGSIZE * 4;
- else if (priv->plat->has_gmac4)
+ else if (priv->plat->core_type == DWMAC_CORE_GMAC4)
return GMAC4_REG_SPACE_SIZE;
return REG_SPACE_SIZE;
}
@@ -423,12 +369,12 @@ static void stmmac_ethtool_gregs(struct net_device *dev,
stmmac_dump_dma_regs(priv, priv->ioaddr, reg_space);
/* Copy DMA registers to where ethtool expects them */
- if (priv->plat->has_gmac4) {
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4) {
/* GMAC4 dumps its DMA registers at its DMA_CHAN_BASE_ADDR */
memcpy(&reg_space[ETHTOOL_DMA_OFFSET],
&reg_space[GMAC4_DMA_CHAN_BASE_ADDR / 4],
NUM_DWMAC4_DMA_REGS * 4);
- } else if (!priv->plat->has_xgmac) {
+ } else if (priv->plat->core_type != DWMAC_CORE_XGMAC) {
memcpy(&reg_space[ETHTOOL_DMA_OFFSET],
&reg_space[DMA_BUS_MODE / 4],
NUM_DWMAC1000_DMA_REGS * 4);
@@ -479,11 +425,7 @@ stmmac_get_pauseparam(struct net_device *netdev,
{
struct stmmac_priv *priv = netdev_priv(netdev);
- if (priv->hw->pcs) {
- pause->autoneg = 1;
- } else {
- phylink_ethtool_get_pauseparam(priv->phylink, pause);
- }
+ phylink_ethtool_get_pauseparam(priv->phylink, pause);
}
static int
@@ -492,12 +434,7 @@ stmmac_set_pauseparam(struct net_device *netdev,
{
struct stmmac_priv *priv = netdev_priv(netdev);
- if (priv->hw->pcs) {
- pause->autoneg = 1;
- return 0;
- } else {
- return phylink_ethtool_set_pauseparam(priv->phylink, pause);
- }
+ return phylink_ethtool_set_pauseparam(priv->phylink, pause);
}
static u64 stmmac_get_rx_normal_irq_n(struct stmmac_priv *priv, int q)
@@ -787,41 +724,14 @@ static void stmmac_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (!priv->plat->pmt)
- return phylink_ethtool_get_wol(priv->phylink, wol);
-
- mutex_lock(&priv->lock);
- if (device_can_wakeup(priv->device)) {
- wol->supported = WAKE_MAGIC | WAKE_UCAST;
- if (priv->hw_cap_support && !priv->dma_cap.pmt_magic_frame)
- wol->supported &= ~WAKE_MAGIC;
- wol->wolopts = priv->wolopts;
- }
- mutex_unlock(&priv->lock);
+ return phylink_ethtool_get_wol(priv->phylink, wol);
}
static int stmmac_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct stmmac_priv *priv = netdev_priv(dev);
- if (!device_can_wakeup(priv->device))
- return -EOPNOTSUPP;
-
- if (!priv->plat->pmt) {
- int ret = phylink_ethtool_set_wol(priv->phylink, wol);
-
- if (!ret)
- device_set_wakeup_enable(priv->device, !!wol->wolopts);
- return ret;
- }
-
- device_set_wakeup_enable(priv->device, !!wol->wolopts);
-
- mutex_lock(&priv->lock);
- priv->wolopts = wol->wolopts;
- mutex_unlock(&priv->lock);
-
- return 0;
+ return phylink_ethtool_set_wol(priv->phylink, wol);
}
static int stmmac_ethtool_op_get_eee(struct net_device *dev,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c
index 75b470ee621a..c54c70224351 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_fpe.c
@@ -70,8 +70,10 @@ static void stmmac_fpe_configure_pmac(struct ethtool_mmsv *mmsv, bool pmac_enabl
struct stmmac_priv *priv = container_of(cfg, struct stmmac_priv, fpe_cfg);
const struct stmmac_fpe_reg *reg = cfg->reg;
void __iomem *ioaddr = priv->ioaddr;
+ unsigned long flags;
u32 value;
+ spin_lock_irqsave(&priv->hw->irq_ctrl_lock, flags);
value = readl(ioaddr + reg->int_en_reg);
if (pmac_enable) {
@@ -86,6 +88,7 @@ static void stmmac_fpe_configure_pmac(struct ethtool_mmsv *mmsv, bool pmac_enabl
}
writel(value, ioaddr + reg->int_en_reg);
+ spin_unlock_irqrestore(&priv->hw->irq_ctrl_lock, flags);
}
static void stmmac_fpe_send_mpacket(struct ethtool_mmsv *mmsv,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.c
new file mode 100644
index 000000000000..5c5dd502f79a
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCI bus helpers for STMMAC driver
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#include <linux/device.h>
+#include <linux/pci.h>
+
+#include "stmmac_libpci.h"
+
+int stmmac_pci_plat_suspend(struct device *dev, void *bsp_priv)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int ret;
+
+ ret = pci_save_state(pdev);
+ if (ret)
+ return ret;
+
+ pci_disable_device(pdev);
+ pci_wake_from_d3(pdev, true);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stmmac_pci_plat_suspend);
+
+int stmmac_pci_plat_resume(struct device *dev, void *bsp_priv)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ int ret;
+
+ pci_restore_state(pdev);
+ pci_set_power_state(pdev, PCI_D0);
+
+ ret = pci_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ pci_set_master(pdev);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stmmac_pci_plat_resume);
+
+MODULE_DESCRIPTION("STMMAC PCI helper library");
+MODULE_AUTHOR("Yao Zi <ziyao@disroot.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.h
new file mode 100644
index 000000000000..71553184f982
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_libpci.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#ifndef __STMMAC_LIBPCI_H__
+#define __STMMAC_LIBPCI_H__
+
+int stmmac_pci_plat_suspend(struct device *dev, void *bsp_priv);
+int stmmac_pci_plat_resume(struct device *dev, void *bsp_priv);
+
+#endif /* __STMMAC_LIBPCI_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 650d75b73e0b..da206b24aaed 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -40,12 +40,14 @@
#include <linux/phylink.h>
#include <linux/udp.h>
#include <linux/bpf_trace.h>
+#include <net/devlink.h>
#include <net/page_pool/helpers.h>
#include <net/pkt_cls.h>
#include <net/xdp_sock_drv.h>
#include "stmmac_ptp.h"
#include "stmmac_fpe.h"
#include "stmmac.h"
+#include "stmmac_pcs.h"
#include "stmmac_xdp.h"
#include <linux/reset.h>
#include <linux/of_mdio.h>
@@ -57,8 +59,7 @@
* with fine resolution and binary rollover. This avoid non-monotonic behavior
* (clock jumps) when changing timestamping settings at runtime.
*/
-#define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCFUPDT | \
- PTP_TCR_TSCTRLSSR)
+#define STMMAC_HWTS_ACTIVE (PTP_TCR_TSENA | PTP_TCR_TSCTRLSSR)
#define STMMAC_ALIGN(x) ALIGN(ALIGN(x, SMP_CACHE_BYTES), 16)
#define TSO_MAX_BUFF_SIZE (SZ_16K - 1)
@@ -147,6 +148,15 @@ static void stmmac_exit_fs(struct net_device *dev);
#define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))
+struct stmmac_devlink_priv {
+ struct stmmac_priv *stmmac_priv;
+};
+
+enum stmmac_dl_param_id {
+ STMMAC_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
+ STMMAC_DEVLINK_PARAM_ID_TS_COARSE,
+};
+
/**
* stmmac_set_clk_tx_rate() - set the clock rate for the MAC transmit clock
* @bsp_priv: BSP private data structure (unused)
@@ -180,6 +190,44 @@ int stmmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
EXPORT_SYMBOL_GPL(stmmac_set_clk_tx_rate);
/**
+ * stmmac_axi_blen_to_mask() - convert a burst length array to reg value
+ * @regval: pointer to a u32 for the resulting register value
+ * @blen: pointer to an array of u32 containing the burst length values in bytes
+ * @len: the number of entries in the @blen array
+ */
+void stmmac_axi_blen_to_mask(u32 *regval, const u32 *blen, size_t len)
+{
+ size_t i;
+ u32 val;
+
+ for (val = i = 0; i < len; i++) {
+ u32 burst = blen[i];
+
+ /* Burst values of zero must be skipped. */
+ if (!burst)
+ continue;
+
+ /* The valid range for the burst length is 4 to 256 inclusive,
+ * and it must be a power of two.
+ */
+ if (burst < 4 || burst > 256 || !is_power_of_2(burst)) {
+ pr_err("stmmac: invalid burst length %u at index %zu\n",
+ burst, i);
+ continue;
+ }
+
+ /* Since burst is a power of two, and the register field starts
+ * with burst = 4, shift right by two bits so bit 0 of the field
+ * corresponds with the minimum value.
+ */
+ val |= burst >> 2;
+ }
+
+ *regval = FIELD_PREP(DMA_AXI_BLEN_MASK, val);
+}
+EXPORT_SYMBOL_GPL(stmmac_axi_blen_to_mask);
+
+/**
* stmmac_verify_args - verify the driver parameters.
* Description: it checks the driver parameters and set a default in case of
* errors.
@@ -445,7 +493,7 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p,
if (!priv->hwts_rx_en)
return;
/* For GMAC4, the valid timestamp is from CTX next desc. */
- if (priv->plat->has_gmac4 || priv->plat->has_xgmac)
+ if (dwmac_is_xmac(priv->plat->core_type))
desc = np;
/* Check if timestamp is available */
@@ -463,6 +511,33 @@ static void stmmac_get_rx_hwtstamp(struct stmmac_priv *priv, struct dma_desc *p,
}
}
+static void stmmac_update_subsecond_increment(struct stmmac_priv *priv)
+{
+ bool xmac = dwmac_is_xmac(priv->plat->core_type);
+ u32 sec_inc = 0;
+ u64 temp = 0;
+
+ stmmac_config_hw_tstamping(priv, priv->ptpaddr, priv->systime_flags);
+
+ /* program Sub Second Increment reg */
+ stmmac_config_sub_second_increment(priv, priv->ptpaddr,
+ priv->plat->clk_ptp_rate,
+ xmac, &sec_inc);
+ temp = div_u64(1000000000ULL, sec_inc);
+
+ /* Store sub second increment for later use */
+ priv->sub_second_inc = sec_inc;
+
+ /* calculate default added value:
+ * formula is :
+ * addend = (2^32)/freq_div_ratio;
+ * where, freq_div_ratio = 1e9ns/sec_inc
+ */
+ temp = (u64)(temp << 32);
+ priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
+ stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);
+}
+
/**
* stmmac_hwtstamp_set - control hardware timestamping.
* @dev: device pointer.
@@ -647,6 +722,8 @@ static int stmmac_hwtstamp_set(struct net_device *dev,
priv->hwts_tx_en = config->tx_type == HWTSTAMP_TX_ON;
priv->systime_flags = STMMAC_HWTS_ACTIVE;
+ if (!priv->tsfupdt_coarse)
+ priv->systime_flags |= PTP_TCR_TSCFUPDT;
if (priv->hwts_tx_en || priv->hwts_rx_en) {
priv->systime_flags |= tstamp_all | ptp_v2 |
@@ -696,10 +773,7 @@ static int stmmac_hwtstamp_get(struct net_device *dev,
static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
u32 systime_flags)
{
- bool xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
struct timespec64 now;
- u32 sec_inc = 0;
- u64 temp = 0;
if (!priv->plat->clk_ptp_rate) {
netdev_err(priv->dev, "Invalid PTP clock rate");
@@ -709,23 +783,7 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
priv->systime_flags = systime_flags;
- /* program Sub Second Increment reg */
- stmmac_config_sub_second_increment(priv, priv->ptpaddr,
- priv->plat->clk_ptp_rate,
- xmac, &sec_inc);
- temp = div_u64(1000000000ULL, sec_inc);
-
- /* Store sub second increment for later use */
- priv->sub_second_inc = sec_inc;
-
- /* calculate default added value:
- * formula is :
- * addend = (2^32)/freq_div_ratio;
- * where, freq_div_ratio = 1e9ns/sec_inc
- */
- temp = (u64)(temp << 32);
- priv->default_addend = div_u64(temp, priv->plat->clk_ptp_rate);
- stmmac_config_addend(priv, priv->ptpaddr, priv->default_addend);
+ stmmac_update_subsecond_increment(priv);
/* initialize system time */
ktime_get_real_ts64(&now);
@@ -745,7 +803,7 @@ static int stmmac_init_tstamp_counter(struct stmmac_priv *priv,
*/
static int stmmac_init_timestamping(struct stmmac_priv *priv)
{
- bool xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
+ bool xmac = dwmac_is_xmac(priv->plat->core_type);
int ret;
if (priv->plat->ptp_clk_freq_config)
@@ -756,7 +814,8 @@ static int stmmac_init_timestamping(struct stmmac_priv *priv)
return -EOPNOTSUPP;
}
- ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE);
+ ret = stmmac_init_tstamp_counter(priv, STMMAC_HWTS_ACTIVE |
+ PTP_TCR_TSCFUPDT);
if (ret) {
netdev_warn(priv->dev, "PTP init failed\n");
return ret;
@@ -850,6 +909,13 @@ static struct phylink_pcs *stmmac_mac_select_pcs(struct phylink_config *config,
return pcs;
}
+ /* The PCS control register is only relevant for SGMII, TBI and RTBI
+ * modes. We no longer support TBI or RTBI, so only configure this
+ * register when operating in SGMII mode with the integrated PCS.
+ */
+ if (priv->hw->pcs & STMMAC_PCS_SGMII && priv->integrated_pcs)
+ return &priv->integrated_pcs->pcs;
+
return NULL;
}
@@ -859,6 +925,18 @@ static void stmmac_mac_config(struct phylink_config *config, unsigned int mode,
/* Nothing to do, xpcs_config() handles everything */
}
+static int stmmac_mac_finish(struct phylink_config *config, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct net_device *ndev = to_net_dev(config->dev);
+ struct stmmac_priv *priv = netdev_priv(ndev);
+
+ if (priv->plat->mac_finish)
+ priv->plat->mac_finish(ndev, priv->plat->bsp_priv, mode, interface);
+
+ return 0;
+}
+
static void stmmac_mac_link_down(struct phylink_config *config,
unsigned int mode, phy_interface_t interface)
{
@@ -1053,14 +1131,16 @@ static int stmmac_mac_enable_tx_lpi(struct phylink_config *config, u32 timer,
return 0;
}
-static int stmmac_mac_finish(struct phylink_config *config, unsigned int mode,
- phy_interface_t interface)
+static int stmmac_mac_wol_set(struct phylink_config *config, u32 wolopts,
+ const u8 *sopass)
{
- struct net_device *ndev = to_net_dev(config->dev);
- struct stmmac_priv *priv = netdev_priv(ndev);
+ struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev));
- if (priv->plat->mac_finish)
- priv->plat->mac_finish(ndev, priv->plat->bsp_priv, mode, interface);
+ device_set_wakeup_enable(priv->device, !!wolopts);
+
+ mutex_lock(&priv->lock);
+ priv->wolopts = wolopts;
+ mutex_unlock(&priv->lock);
return 0;
}
@@ -1069,11 +1149,12 @@ static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
.mac_get_caps = stmmac_mac_get_caps,
.mac_select_pcs = stmmac_mac_select_pcs,
.mac_config = stmmac_mac_config,
+ .mac_finish = stmmac_mac_finish,
.mac_link_down = stmmac_mac_link_down,
.mac_link_up = stmmac_mac_link_up,
.mac_disable_tx_lpi = stmmac_mac_disable_tx_lpi,
.mac_enable_tx_lpi = stmmac_mac_enable_tx_lpi,
- .mac_finish = stmmac_mac_finish,
+ .mac_wol_set = stmmac_mac_wol_set,
};
/**
@@ -1086,17 +1167,25 @@ static const struct phylink_mac_ops stmmac_phylink_mac_ops = {
static void stmmac_check_pcs_mode(struct stmmac_priv *priv)
{
int interface = priv->plat->phy_interface;
+ int speed = priv->plat->mac_port_sel_speed;
+
+ if (priv->dma_cap.pcs && interface == PHY_INTERFACE_MODE_SGMII) {
+ netdev_dbg(priv->dev, "PCS SGMII support enabled\n");
+ priv->hw->pcs = STMMAC_PCS_SGMII;
+
+ switch (speed) {
+ case SPEED_10:
+ case SPEED_100:
+ case SPEED_1000:
+ priv->hw->reverse_sgmii_enable = true;
+ break;
- if (priv->dma_cap.pcs) {
- if ((interface == PHY_INTERFACE_MODE_RGMII) ||
- (interface == PHY_INTERFACE_MODE_RGMII_ID) ||
- (interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
- (interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
- netdev_dbg(priv->dev, "PCS RGMII support enabled\n");
- priv->hw->pcs = STMMAC_PCS_RGMII;
- } else if (interface == PHY_INTERFACE_MODE_SGMII) {
- netdev_dbg(priv->dev, "PCS SGMII support enabled\n");
- priv->hw->pcs = STMMAC_PCS_SGMII;
+ default:
+ dev_warn(priv->device, "invalid port speed\n");
+ fallthrough;
+ case 0:
+ priv->hw->reverse_sgmii_enable = false;
+ break;
}
}
}
@@ -1174,18 +1263,10 @@ static int stmmac_init_phy(struct net_device *dev)
phylink_ethtool_set_eee(priv->phylink, &eee);
}
- if (!priv->plat->pmt) {
- struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL };
-
- phylink_ethtool_get_wol(priv->phylink, &wol);
- device_set_wakeup_capable(priv->device, !!wol.supported);
- device_set_wakeup_enable(priv->device, !!wol.wolopts);
- }
-
return 0;
}
-static int stmmac_phy_setup(struct stmmac_priv *priv)
+static int stmmac_phylink_setup(struct stmmac_priv *priv)
{
struct stmmac_mdio_bus_data *mdio_bus_data;
struct phylink_config *config;
@@ -1202,7 +1283,11 @@ static int stmmac_phy_setup(struct stmmac_priv *priv)
/* Stmmac always requires an RX clock for hardware initialization */
config->mac_requires_rxc = true;
- if (!(priv->plat->flags & STMMAC_FLAG_RX_CLK_RUNS_IN_LPI))
+ /* Disable EEE RX clock stop to ensure VLAN register access works
+ * correctly.
+ */
+ if (!(priv->plat->flags & STMMAC_FLAG_RX_CLK_RUNS_IN_LPI) &&
+ !(priv->dev->features & NETIF_F_VLAN_FEATURES))
config->eee_rx_clk_stop_enable = true;
/* Set the default transmit clock stop bit based on the platform glue */
@@ -1250,6 +1335,16 @@ static int stmmac_phy_setup(struct stmmac_priv *priv)
config->eee_enabled_default = true;
}
+ config->wol_phy_speed_ctrl = true;
+ if (priv->plat->flags & STMMAC_FLAG_USE_PHY_WOL) {
+ config->wol_phy_legacy = true;
+ } else {
+ if (priv->dma_cap.pmt_remote_wake_up)
+ config->wol_mac_support |= WAKE_UCAST;
+ if (priv->dma_cap.pmt_magic_frame)
+ config->wol_mac_support |= WAKE_MAGIC;
+ }
+
fwnode = priv->plat->port_node;
if (!fwnode)
fwnode = dev_fwnode(priv->device);
@@ -1339,9 +1434,9 @@ static unsigned int stmmac_rx_offset(struct stmmac_priv *priv)
return NET_SKB_PAD;
}
-static int stmmac_set_bfsize(int mtu, int bufsize)
+static int stmmac_set_bfsize(int mtu)
{
- int ret = bufsize;
+ int ret;
if (mtu >= BUF_SIZE_8KiB)
ret = BUF_SIZE_16KiB;
@@ -1470,7 +1565,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
buf->page_offset = stmmac_rx_offset(priv);
}
- if (priv->sph && !buf->sec_page) {
+ if (priv->sph_active && !buf->sec_page) {
buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
if (!buf->sec_page)
return -ENOMEM;
@@ -2056,7 +2151,7 @@ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv,
pp_params.offset = stmmac_rx_offset(priv);
pp_params.max_len = dma_conf->dma_buf_sz;
- if (priv->sph) {
+ if (priv->sph_active) {
pp_params.offset = 0;
pp_params.max_len += stmmac_rx_offset(priv);
}
@@ -2397,7 +2492,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
txfifosz = priv->dma_cap.tx_fifo_size;
/* Split up the shared Tx/Rx FIFO memory on DW QoS Eth and DW XGMAC */
- if (priv->plat->has_gmac4 || priv->plat->has_xgmac) {
+ if (dwmac_is_xmac(priv->plat->core_type)) {
rxfifosz /= rx_channels_count;
txfifosz /= tx_channels_count;
}
@@ -3029,6 +3124,56 @@ static void stmmac_check_ether_addr(struct stmmac_priv *priv)
}
}
+int stmmac_get_phy_intf_sel(phy_interface_t interface)
+{
+ int phy_intf_sel = -EINVAL;
+
+ if (interface == PHY_INTERFACE_MODE_MII ||
+ interface == PHY_INTERFACE_MODE_GMII)
+ phy_intf_sel = PHY_INTF_SEL_GMII_MII;
+ else if (phy_interface_mode_is_rgmii(interface))
+ phy_intf_sel = PHY_INTF_SEL_RGMII;
+ else if (interface == PHY_INTERFACE_MODE_SGMII)
+ phy_intf_sel = PHY_INTF_SEL_SGMII;
+ else if (interface == PHY_INTERFACE_MODE_RMII)
+ phy_intf_sel = PHY_INTF_SEL_RMII;
+ else if (interface == PHY_INTERFACE_MODE_REVMII)
+ phy_intf_sel = PHY_INTF_SEL_REVMII;
+
+ return phy_intf_sel;
+}
+EXPORT_SYMBOL_GPL(stmmac_get_phy_intf_sel);
+
+static int stmmac_prereset_configure(struct stmmac_priv *priv)
+{
+ struct plat_stmmacenet_data *plat_dat = priv->plat;
+ phy_interface_t interface;
+ int phy_intf_sel, ret;
+
+ if (!plat_dat->set_phy_intf_sel)
+ return 0;
+
+ interface = plat_dat->phy_interface;
+ phy_intf_sel = stmmac_get_phy_intf_sel(interface);
+ if (phy_intf_sel < 0) {
+ netdev_err(priv->dev,
+ "failed to get phy_intf_sel for %s: %pe\n",
+ phy_modes(interface), ERR_PTR(phy_intf_sel));
+ return phy_intf_sel;
+ }
+
+ ret = plat_dat->set_phy_intf_sel(plat_dat->bsp_priv, phy_intf_sel);
+ if (ret == -EINVAL)
+ netdev_err(priv->dev, "platform does not support %s\n",
+ phy_modes(interface));
+ else if (ret < 0)
+ netdev_err(priv->dev,
+ "platform failed to set interface %s: %pe\n",
+ phy_modes(interface), ERR_PTR(ret));
+
+ return ret;
+}
+
/**
* stmmac_init_dma_engine - DMA init.
* @priv: driver private structure
@@ -3055,7 +3200,11 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv)
if (priv->extend_desc && (priv->mode == STMMAC_RING_MODE))
priv->plat->dma_cfg->atds = 1;
- ret = stmmac_reset(priv, priv->ioaddr);
+ ret = stmmac_prereset_configure(priv);
+ if (ret)
+ return ret;
+
+ ret = stmmac_reset(priv);
if (ret) {
netdev_err(priv->dev, "Failed to reset the dma\n");
return ret;
@@ -3443,19 +3592,6 @@ static int stmmac_hw_setup(struct net_device *dev)
stmmac_set_umac_addr(priv, priv->hw, dev->dev_addr, 0);
phylink_rx_clk_stop_unblock(priv->phylink);
- /* PS and related bits will be programmed according to the speed */
- if (priv->hw->pcs) {
- int speed = priv->plat->mac_port_sel_speed;
-
- if ((speed == SPEED_10) || (speed == SPEED_100) ||
- (speed == SPEED_1000)) {
- priv->hw->ps = speed;
- } else {
- dev_warn(priv->device, "invalid port speed\n");
- priv->hw->ps = 0;
- }
- }
-
/* Initialize the MAC Core */
stmmac_core_init(priv, priv->hw, dev);
@@ -3492,9 +3628,6 @@ static int stmmac_hw_setup(struct net_device *dev)
}
}
- if (priv->hw->pcs)
- stmmac_pcs_ctrl_ane(priv, 1, priv->hw->ps, 0);
-
/* set TX and RX rings length */
stmmac_set_rings_length(priv);
@@ -3512,7 +3645,7 @@ static int stmmac_hw_setup(struct net_device *dev)
}
/* Enable Split Header */
- sph_en = (priv->hw->rx_csum > 0) && priv->sph;
+ sph_en = (priv->hw->rx_csum > 0) && priv->sph_active;
for (chan = 0; chan < rx_cnt; chan++)
stmmac_enable_sph(priv, priv->ioaddr, sph_en, chan);
@@ -3867,12 +4000,13 @@ stmmac_setup_dma_desc(struct stmmac_priv *priv, unsigned int mtu)
return ERR_PTR(-ENOMEM);
}
+ /* Returns 0 or BUF_SIZE_16KiB if mtu > 8KiB and dwmac4 or ring mode */
bfsize = stmmac_set_16kib_bfsize(priv, mtu);
if (bfsize < 0)
bfsize = 0;
if (bfsize < BUF_SIZE_16KiB)
- bfsize = stmmac_set_bfsize(mtu, 0);
+ bfsize = stmmac_set_bfsize(mtu);
dma_conf->dma_buf_sz = bfsize;
/* Chose the tx/rx size from the already defined one in the
@@ -3963,8 +4097,6 @@ static int __stmmac_open(struct net_device *dev,
stmmac_init_coalesce(priv);
phylink_start(priv->phylink);
- /* We may have called phylink_speed_down before */
- phylink_speed_up(priv->phylink);
ret = stmmac_request_irq(dev);
if (ret)
@@ -4015,6 +4147,9 @@ static int stmmac_open(struct net_device *dev)
kfree(dma_conf);
+ /* We may have called phylink_speed_down before */
+ phylink_speed_up(priv->phylink);
+
return ret;
err_disconnect_phy:
@@ -4032,13 +4167,6 @@ static void __stmmac_release(struct net_device *dev)
struct stmmac_priv *priv = netdev_priv(dev);
u32 chan;
- /* If the PHY or MAC has WoL enabled, then the PHY will not be
- * suspended when phylink_stop() is called below. Set the PHY
- * to its slowest speed to save power.
- */
- if (device_may_wakeup(priv->device))
- phylink_speed_down(priv->phylink, false);
-
/* Stop and disconnect the PHY */
phylink_stop(priv->phylink);
@@ -4078,6 +4206,13 @@ static int stmmac_release(struct net_device *dev)
{
struct stmmac_priv *priv = netdev_priv(dev);
+ /* If the PHY or MAC has WoL enabled, then the PHY will not be
+ * suspended when phylink_stop() is called below. Set the PHY
+ * to its slowest speed to save power.
+ */
+ if (device_may_wakeup(priv->device))
+ phylink_speed_down(priv->phylink, false);
+
__stmmac_release(dev);
phylink_disconnect_phy(priv->phylink);
@@ -4089,18 +4224,11 @@ static int stmmac_release(struct net_device *dev)
static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
struct stmmac_tx_queue *tx_q)
{
- u16 tag = 0x0, inner_tag = 0x0;
- u32 inner_type = 0x0;
struct dma_desc *p;
+ u16 tag = 0x0;
- if (!priv->dma_cap.vlins)
- return false;
- if (!skb_vlan_tag_present(skb))
+ if (!priv->dma_cap.vlins || !skb_vlan_tag_present(skb))
return false;
- if (skb->vlan_proto == htons(ETH_P_8021AD)) {
- inner_tag = skb_vlan_tag_get(skb);
- inner_type = STMMAC_VLAN_INSERT;
- }
tag = skb_vlan_tag_get(skb);
@@ -4109,7 +4237,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
else
p = &tx_q->dma_tx[tx_q->cur_tx];
- if (stmmac_set_desc_vlan_tag(priv, p, tag, inner_tag, inner_type))
+ if (stmmac_set_desc_vlan_tag(priv, p, tag, 0x0, 0x0))
return false;
stmmac_set_tx_owner(priv, p);
@@ -4493,20 +4621,21 @@ static bool stmmac_has_ip_ethertype(struct sk_buff *skb)
*/
static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
{
- unsigned int first_entry, tx_packets, enh_desc;
+ bool enh_desc, has_vlan, set_ic, is_jumbo = false;
struct stmmac_priv *priv = netdev_priv(dev);
unsigned int nopaged_len = skb_headlen(skb);
- int i, csum_insertion = 0, is_jumbo = 0;
u32 queue = skb_get_queue_mapping(skb);
int nfrags = skb_shinfo(skb)->nr_frags;
+ unsigned int first_entry, tx_packets;
int gso = skb_shinfo(skb)->gso_type;
struct stmmac_txq_stats *txq_stats;
struct dma_edesc *tbs_desc = NULL;
struct dma_desc *desc, *first;
struct stmmac_tx_queue *tx_q;
- bool has_vlan, set_ic;
+ int i, csum_insertion = 0;
int entry, first_tx;
dma_addr_t des;
+ u32 sdu_len;
tx_q = &priv->dma_conf.tx_queue[queue];
txq_stats = &priv->xstats.txq_stats[queue];
@@ -4519,15 +4648,21 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb_is_gso(skb) && priv->tso) {
if (gso & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
return stmmac_tso_xmit(skb, dev);
- if (priv->plat->has_gmac4 && (gso & SKB_GSO_UDP_L4))
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4 &&
+ (gso & SKB_GSO_UDP_L4))
return stmmac_tso_xmit(skb, dev);
}
if (priv->est && priv->est->enable &&
- priv->est->max_sdu[queue] &&
- skb->len > priv->est->max_sdu[queue]){
- priv->xstats.max_sdu_txq_drop[queue]++;
- goto max_sdu_err;
+ priv->est->max_sdu[queue]) {
+ sdu_len = skb->len;
+ /* Add VLAN tag length if VLAN tag insertion offload is requested */
+ if (priv->dma_cap.vlins && skb_vlan_tag_present(skb))
+ sdu_len += VLAN_HLEN;
+ if (sdu_len > priv->est->max_sdu[queue]) {
+ priv->xstats.max_sdu_txq_drop[queue]++;
+ goto max_sdu_err;
+ }
}
if (unlikely(stmmac_tx_avail(priv, queue) < nfrags + 1)) {
@@ -4802,7 +4937,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
break;
}
- if (priv->sph && !buf->sec_page) {
+ if (priv->sph_active && !buf->sec_page) {
buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
if (!buf->sec_page)
break;
@@ -4813,7 +4948,7 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
stmmac_set_desc_addr(priv, p, buf->addr);
- if (priv->sph)
+ if (priv->sph_active)
stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
else
stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, false);
@@ -4838,6 +4973,8 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
rx_q->rx_tail_addr = rx_q->dma_rx_phy +
(rx_q->dirty_rx * sizeof(struct dma_desc));
stmmac_set_rx_tail_ptr(priv, priv->ioaddr, rx_q->rx_tail_addr, queue);
+ /* Wake up Rx DMA from the suspend state if required */
+ stmmac_enable_dma_reception(priv, priv->ioaddr, queue);
}
static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,
@@ -4848,12 +4985,12 @@ static unsigned int stmmac_rx_buf1_len(struct stmmac_priv *priv,
int coe = priv->hw->rx_csum;
/* Not first descriptor, buffer is always zero */
- if (priv->sph && len)
+ if (priv->sph_active && len)
return 0;
/* First descriptor, get split header length */
stmmac_get_rx_header_len(priv, p, &hlen);
- if (priv->sph && hlen) {
+ if (priv->sph_active && hlen) {
priv->xstats.rx_split_hdr_pkt_n++;
return hlen;
}
@@ -4876,7 +5013,7 @@ static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv,
unsigned int plen = 0;
/* Not split header, buffer is not available */
- if (!priv->sph)
+ if (!priv->sph_active)
return 0;
/* Not last descriptor */
@@ -5259,10 +5396,10 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
len = 0;
}
+read_again:
if (count >= limit)
break;
-read_again:
buf1_len = 0;
entry = next_entry;
buf = &rx_q->buf_pool[entry];
@@ -5944,8 +6081,8 @@ static int stmmac_set_features(struct net_device *netdev,
*/
stmmac_rx_ipc(priv, priv->hw);
- if (priv->sph_cap) {
- bool sph_en = (priv->hw->rx_csum > 0) && priv->sph;
+ if (priv->sph_capable) {
+ bool sph_en = (priv->hw->rx_csum > 0) && priv->sph_active;
u32 chan;
for (chan = 0; chan < priv->plat->rx_queues_to_use; chan++)
@@ -5972,7 +6109,7 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv)
u32 queue;
bool xmac;
- xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
+ xmac = dwmac_is_xmac(priv->plat->core_type);
queues_count = (rx_cnt > tx_cnt) ? rx_cnt : tx_cnt;
if (priv->irq_wake)
@@ -5986,7 +6123,7 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv)
stmmac_fpe_irq_status(priv);
/* To handle GMAC own interrupts */
- if ((priv->plat->has_gmac) || xmac) {
+ if (priv->plat->core_type == DWMAC_CORE_GMAC || xmac) {
int status = stmmac_host_irq_status(priv, priv->hw, &priv->xstats);
if (unlikely(status)) {
@@ -6000,15 +6137,6 @@ static void stmmac_common_interrupt(struct stmmac_priv *priv)
for (queue = 0; queue < queues_count; queue++)
stmmac_host_mtl_irq_status(priv, priv->hw, queue);
- /* PCS link status */
- if (priv->hw->pcs &&
- !(priv->plat->flags & STMMAC_FLAG_HAS_INTEGRATED_PCS)) {
- if (priv->xstats.pcs_link)
- netif_carrier_on(priv->dev);
- else
- netif_carrier_off(priv->dev);
- }
-
stmmac_timestamp_interrupt(priv, priv);
}
}
@@ -6356,7 +6484,7 @@ static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
(priv->dma_cap.mbps_1000) ? "Y" : "N");
seq_printf(seq, "\tHalf duplex: %s\n",
(priv->dma_cap.half_duplex) ? "Y" : "N");
- if (priv->plat->has_xgmac) {
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC) {
seq_printf(seq,
"\tNumber of Additional MAC address registers: %d\n",
priv->dma_cap.multi_addr);
@@ -6380,7 +6508,7 @@ static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
(priv->dma_cap.time_stamp) ? "Y" : "N");
seq_printf(seq, "\tIEEE 1588-2008 Advanced Time Stamp: %s\n",
(priv->dma_cap.atime_stamp) ? "Y" : "N");
- if (priv->plat->has_xgmac)
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC)
seq_printf(seq, "\tTimestamp System Time Source: %s\n",
dwxgmac_timestamp_source[priv->dma_cap.tssrc]);
seq_printf(seq, "\t802.3az - Energy-Efficient Ethernet (EEE): %s\n",
@@ -6389,7 +6517,7 @@ static int stmmac_dma_cap_show(struct seq_file *seq, void *v)
seq_printf(seq, "\tChecksum Offload in TX: %s\n",
(priv->dma_cap.tx_coe) ? "Y" : "N");
if (priv->synopsys_id >= DWMAC_CORE_4_00 ||
- priv->plat->has_xgmac) {
+ priv->plat->core_type == DWMAC_CORE_XGMAC) {
seq_printf(seq, "\tIP Checksum Offload in RX: %s\n",
(priv->dma_cap.rx_coe) ? "Y" : "N");
} else {
@@ -6903,7 +7031,7 @@ int stmmac_xdp_open(struct net_device *dev)
}
/* Adjust Split header */
- sph_en = (priv->hw->rx_csum > 0) && priv->sph;
+ sph_en = (priv->hw->rx_csum > 0) && priv->sph_active;
/* DMA RX Channel Configuration */
for (chan = 0; chan < rx_cnt; chan++) {
@@ -7241,13 +7369,21 @@ static int stmmac_hw_init(struct stmmac_priv *priv)
* has to be disable and this can be done by passing the
* riwt_off field from the platform.
*/
- if (((priv->synopsys_id >= DWMAC_CORE_3_50) ||
- (priv->plat->has_xgmac)) && (!priv->plat->riwt_off)) {
+ if ((priv->synopsys_id >= DWMAC_CORE_3_50 ||
+ priv->plat->core_type == DWMAC_CORE_XGMAC) &&
+ !priv->plat->riwt_off) {
priv->use_riwt = 1;
dev_info(priv->device,
"Enable RX Mitigation via HW Watchdog Timer\n");
}
+ /* Unimplemented PCS init (as indicated by stmmac_do_callback()
+ * perversely returning -EINVAL) is non-fatal.
+ */
+ ret = stmmac_mac_pcs_init(priv);
+ if (ret != -EINVAL)
+ return ret;
+
return 0;
}
@@ -7356,7 +7492,7 @@ static int stmmac_xdp_rx_timestamp(const struct xdp_md *_ctx, u64 *timestamp)
return -ENODATA;
/* For GMAC4, the valid timestamp is from CTX next desc. */
- if (priv->plat->has_gmac4 || priv->plat->has_xgmac)
+ if (dwmac_is_xmac(priv->plat->core_type))
desc_contains_ts = ndesc;
/* Check if timestamp is available */
@@ -7374,19 +7510,133 @@ static const struct xdp_metadata_ops stmmac_xdp_metadata_ops = {
.xmo_rx_timestamp = stmmac_xdp_rx_timestamp,
};
-/**
- * stmmac_dvr_probe
- * @device: device pointer
- * @plat_dat: platform data pointer
- * @res: stmmac resource pointer
- * Description: this is the main probe function used to
- * call the alloc_etherdev, allocate the priv structure.
- * Return:
- * returns 0 on success, otherwise errno.
- */
-int stmmac_dvr_probe(struct device *device,
- struct plat_stmmacenet_data *plat_dat,
- struct stmmac_resources *res)
+static int stmmac_dl_ts_coarse_set(struct devlink *dl, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct stmmac_devlink_priv *dl_priv = devlink_priv(dl);
+ struct stmmac_priv *priv = dl_priv->stmmac_priv;
+
+ priv->tsfupdt_coarse = ctx->val.vbool;
+
+ if (priv->tsfupdt_coarse)
+ priv->systime_flags &= ~PTP_TCR_TSCFUPDT;
+ else
+ priv->systime_flags |= PTP_TCR_TSCFUPDT;
+
+ /* In Coarse mode, we can use a smaller subsecond increment, let's
+ * reconfigure the systime, subsecond increment and addend.
+ */
+ stmmac_update_subsecond_increment(priv);
+
+ return 0;
+}
+
+static int stmmac_dl_ts_coarse_get(struct devlink *dl, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct stmmac_devlink_priv *dl_priv = devlink_priv(dl);
+ struct stmmac_priv *priv = dl_priv->stmmac_priv;
+
+ ctx->val.vbool = priv->tsfupdt_coarse;
+
+ return 0;
+}
+
+static const struct devlink_param stmmac_devlink_params[] = {
+ DEVLINK_PARAM_DRIVER(STMMAC_DEVLINK_PARAM_ID_TS_COARSE, "phc_coarse_adj",
+ DEVLINK_PARAM_TYPE_BOOL,
+ BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+ stmmac_dl_ts_coarse_get,
+ stmmac_dl_ts_coarse_set, NULL),
+};
+
+/* None of the generic devlink parameters are implemented */
+static const struct devlink_ops stmmac_devlink_ops = {};
+
+static int stmmac_register_devlink(struct stmmac_priv *priv)
+{
+ struct stmmac_devlink_priv *dl_priv;
+ int ret;
+
+ /* For now, what is exposed over devlink is only relevant when
+ * timestamping is available and we have a valid ptp clock rate
+ */
+ if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp) ||
+ !priv->plat->clk_ptp_rate)
+ return 0;
+
+ priv->devlink = devlink_alloc(&stmmac_devlink_ops, sizeof(*dl_priv),
+ priv->device);
+ if (!priv->devlink)
+ return -ENOMEM;
+
+ dl_priv = devlink_priv(priv->devlink);
+ dl_priv->stmmac_priv = priv;
+
+ ret = devlink_params_register(priv->devlink, stmmac_devlink_params,
+ ARRAY_SIZE(stmmac_devlink_params));
+ if (ret)
+ goto dl_free;
+
+ devlink_register(priv->devlink);
+ return 0;
+
+dl_free:
+ devlink_free(priv->devlink);
+
+ return ret;
+}
+
+static void stmmac_unregister_devlink(struct stmmac_priv *priv)
+{
+ if (!priv->devlink)
+ return;
+
+ devlink_unregister(priv->devlink);
+ devlink_params_unregister(priv->devlink, stmmac_devlink_params,
+ ARRAY_SIZE(stmmac_devlink_params));
+ devlink_free(priv->devlink);
+}
+
+struct plat_stmmacenet_data *stmmac_plat_dat_alloc(struct device *dev)
+{
+ struct plat_stmmacenet_data *plat_dat;
+ int i;
+
+ plat_dat = devm_kzalloc(dev, sizeof(*plat_dat), GFP_KERNEL);
+ if (!plat_dat)
+ return NULL;
+
+ /* Set the defaults:
+ * - phy autodetection
+ * - determine GMII_Address CR field from CSR clock
+ * - allow MTU up to JUMBO_LEN
+ * - hash table size
+ * - one unicast filter entry
+ */
+ plat_dat->phy_addr = -1;
+ plat_dat->clk_csr = -1;
+ plat_dat->maxmtu = JUMBO_LEN;
+ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
+ plat_dat->unicast_filter_entries = 1;
+
+ /* Set the mtl defaults */
+ plat_dat->tx_queues_to_use = 1;
+ plat_dat->rx_queues_to_use = 1;
+
+ /* Setup the default RX queue channel map */
+ for (i = 0; i < ARRAY_SIZE(plat_dat->rx_queues_cfg); i++)
+ plat_dat->rx_queues_cfg[i].chan = i;
+
+ return plat_dat;
+}
+EXPORT_SYMBOL_GPL(stmmac_plat_dat_alloc);
+
+static int __stmmac_dvr_probe(struct device *device,
+ struct plat_stmmacenet_data *plat_dat,
+ struct stmmac_resources *res)
{
struct net_device *ndev = NULL;
struct stmmac_priv *priv;
@@ -7512,7 +7762,7 @@ int stmmac_dvr_probe(struct device *device,
if ((priv->plat->flags & STMMAC_FLAG_TSO_EN) && (priv->dma_cap.tsoen)) {
ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
- if (priv->plat->has_gmac4)
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4)
ndev->hw_features |= NETIF_F_GSO_UDP_L4;
priv->tso = true;
dev_info(priv->device, "TSO feature enabled\n");
@@ -7521,8 +7771,8 @@ int stmmac_dvr_probe(struct device *device,
if (priv->dma_cap.sphen &&
!(priv->plat->flags & STMMAC_FLAG_SPH_DISABLE)) {
ndev->hw_features |= NETIF_F_GRO;
- priv->sph_cap = true;
- priv->sph = priv->sph_cap;
+ priv->sph_capable = true;
+ priv->sph_active = priv->sph_capable;
dev_info(priv->device, "SPH feature enabled\n");
}
@@ -7565,7 +7815,7 @@ int stmmac_dvr_probe(struct device *device,
#ifdef STMMAC_VLAN_TAG_USED
/* Both mac100 and gmac support receive VLAN tag detection */
ndev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_STAG_RX;
- if (priv->plat->has_gmac4 || priv->plat->has_xgmac) {
+ if (dwmac_is_xmac(priv->plat->core_type)) {
ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX;
priv->hw->hw_vlan_en = true;
}
@@ -7573,11 +7823,8 @@ int stmmac_dvr_probe(struct device *device,
ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
ndev->features |= NETIF_F_HW_VLAN_STAG_FILTER;
}
- if (priv->dma_cap.vlins) {
+ if (priv->dma_cap.vlins)
ndev->features |= NETIF_F_HW_VLAN_CTAG_TX;
- if (priv->dma_cap.dvlan)
- ndev->features |= NETIF_F_HW_VLAN_STAG_TX;
- }
#endif
priv->msg_enable = netif_msg_init(debug, default_msg_level);
@@ -7596,22 +7843,23 @@ int stmmac_dvr_probe(struct device *device,
/* MTU range: 46 - hw-specific max */
ndev->min_mtu = ETH_ZLEN - ETH_HLEN;
- if (priv->plat->has_xgmac)
+
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC)
ndev->max_mtu = XGMAC_JUMBO_LEN;
- else if ((priv->plat->enh_desc) || (priv->synopsys_id >= DWMAC_CORE_4_00))
+ else if (priv->plat->enh_desc || priv->synopsys_id >= DWMAC_CORE_4_00)
ndev->max_mtu = JUMBO_LEN;
else
ndev->max_mtu = SKB_MAX_HEAD(NET_SKB_PAD + NET_IP_ALIGN);
- /* Will not overwrite ndev->max_mtu if plat->maxmtu > ndev->max_mtu
- * as well as plat->maxmtu < ndev->min_mtu which is a invalid range.
+
+ /* Warn if the platform's maxmtu is smaller than the minimum MTU,
+ * otherwise clamp the maximum MTU above to the platform's maxmtu.
*/
- if ((priv->plat->maxmtu < ndev->max_mtu) &&
- (priv->plat->maxmtu >= ndev->min_mtu))
- ndev->max_mtu = priv->plat->maxmtu;
- else if (priv->plat->maxmtu < ndev->min_mtu)
+ if (priv->plat->maxmtu < ndev->min_mtu)
dev_warn(priv->device,
"%s: warning: maxmtu having invalid value (%d)\n",
__func__, priv->plat->maxmtu);
+ else if (priv->plat->maxmtu < ndev->max_mtu)
+ ndev->max_mtu = priv->plat->maxmtu;
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
@@ -7641,12 +7889,16 @@ int stmmac_dvr_probe(struct device *device,
if (ret)
goto error_pcs_setup;
- ret = stmmac_phy_setup(priv);
+ ret = stmmac_phylink_setup(priv);
if (ret) {
netdev_err(ndev, "failed to setup phy (%d)\n", ret);
goto error_phy_setup;
}
+ ret = stmmac_register_devlink(priv);
+ if (ret)
+ goto error_devlink_setup;
+
ret = register_netdev(ndev);
if (ret) {
dev_err(priv->device, "%s: ERROR %i registering the device\n",
@@ -7669,6 +7921,8 @@ int stmmac_dvr_probe(struct device *device,
return ret;
error_netdev_register:
+ stmmac_unregister_devlink(priv);
+error_devlink_setup:
phylink_destroy(priv->phylink);
error_phy_setup:
stmmac_pcs_clean(ndev);
@@ -7683,6 +7937,34 @@ error_wq_init:
return ret;
}
+
+/**
+ * stmmac_dvr_probe
+ * @dev: device pointer
+ * @plat_dat: platform data pointer
+ * @res: stmmac resource pointer
+ * Description: this is the main probe function used to
+ * call the alloc_etherdev, allocate the priv structure.
+ * Return:
+ * returns 0 on success, otherwise errno.
+ */
+int stmmac_dvr_probe(struct device *dev, struct plat_stmmacenet_data *plat_dat,
+ struct stmmac_resources *res)
+{
+ int ret;
+
+ if (plat_dat->init) {
+ ret = plat_dat->init(dev, plat_dat->bsp_priv);
+ if (ret)
+ return ret;
+ }
+
+ ret = __stmmac_dvr_probe(dev, plat_dat, res);
+ if (ret && plat_dat->exit)
+ plat_dat->exit(dev, plat_dat->bsp_priv);
+
+ return ret;
+}
EXPORT_SYMBOL_GPL(stmmac_dvr_probe);
/**
@@ -7705,6 +7987,8 @@ void stmmac_dvr_remove(struct device *dev)
#ifdef CONFIG_DEBUG_FS
stmmac_exit_fs(ndev);
#endif
+ stmmac_unregister_devlink(priv);
+
phylink_destroy(priv->phylink);
if (priv->plat->stmmac_rst)
reset_control_assert(priv->plat->stmmac_rst);
@@ -7719,6 +8003,9 @@ void stmmac_dvr_remove(struct device *dev)
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
+
+ if (priv->plat->exit)
+ priv->plat->exit(dev, priv->plat->bsp_priv);
}
EXPORT_SYMBOL_GPL(stmmac_dvr_remove);
@@ -7759,7 +8046,7 @@ int stmmac_suspend(struct device *dev)
priv->plat->serdes_powerdown(ndev, priv->plat->bsp_priv);
/* Enable Power down mode by programming the PMT regs */
- if (stmmac_wol_enabled_mac(priv)) {
+ if (priv->wolopts) {
stmmac_pmt(priv, priv->hw, priv->wolopts);
priv->irq_wake = 1;
} else {
@@ -7770,10 +8057,7 @@ int stmmac_suspend(struct device *dev)
mutex_unlock(&priv->lock);
rtnl_lock();
- if (stmmac_wol_enabled_phy(priv))
- phylink_speed_down(priv->phylink, false);
-
- phylink_suspend(priv->phylink, stmmac_wol_enabled_mac(priv));
+ phylink_suspend(priv->phylink, !!priv->wolopts);
rtnl_unlock();
if (stmmac_fpe_supported(priv))
@@ -7849,7 +8133,7 @@ int stmmac_resume(struct device *dev)
* this bit because it can generate problems while resuming
* from another devices (e.g. serial console).
*/
- if (stmmac_wol_enabled_mac(priv)) {
+ if (priv->wolopts) {
mutex_lock(&priv->lock);
stmmac_pmt(priv, priv->hw, 0);
mutex_unlock(&priv->lock);
@@ -7911,9 +8195,6 @@ int stmmac_resume(struct device *dev)
* workqueue thread, which will race with initialisation.
*/
phylink_resume(priv->phylink);
- if (stmmac_wol_enabled_phy(priv))
- phylink_speed_up(priv->phylink);
-
rtnl_unlock();
netif_device_attach(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
index f408737f6fc7..1e82850f2a25 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
@@ -301,7 +301,7 @@ static int stmmac_mdio_read_c22(struct mii_bus *bus, int phyaddr, int phyreg)
struct stmmac_priv *priv = netdev_priv(bus->priv);
u32 cmd;
- if (priv->plat->has_gmac4)
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4)
cmd = MII_GMAC4_READ;
else
cmd = 0;
@@ -344,7 +344,7 @@ static int stmmac_mdio_write_c22(struct mii_bus *bus, int phyaddr, int phyreg,
struct stmmac_priv *priv = netdev_priv(bus->priv);
u32 cmd;
- if (priv->plat->has_gmac4)
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4)
cmd = MII_GMAC4_WRITE;
else
cmd = MII_ADDR_GWRITE;
@@ -417,7 +417,7 @@ int stmmac_mdio_reset(struct mii_bus *bus)
* on MDC, so perform a dummy mdio read. To be updated for GMAC4
* if needed.
*/
- if (!priv->plat->has_gmac4)
+ if (priv->plat->core_type != DWMAC_CORE_GMAC4)
writel(0, priv->ioaddr + mii_address);
#endif
return 0;
@@ -528,7 +528,7 @@ static u32 stmmac_clk_csr_set(struct stmmac_priv *priv)
value = 0;
}
- if (priv->plat->has_xgmac) {
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC) {
if (clk_rate > 400000000)
value = 0x5;
else if (clk_rate > 350000000)
@@ -583,8 +583,9 @@ int stmmac_mdio_register(struct net_device *ndev)
struct device_node *mdio_node = priv->plat->mdio_node;
struct device *dev = ndev->dev.parent;
struct fwnode_handle *fixed_node;
+ int max_addr = PHY_MAX_ADDR - 1;
struct fwnode_handle *fwnode;
- int addr, found, max_addr;
+ struct phy_device *phydev;
if (!mdio_bus_data)
return 0;
@@ -600,7 +601,7 @@ int stmmac_mdio_register(struct net_device *ndev)
new_bus->name = "stmmac";
- if (priv->plat->has_xgmac) {
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC) {
new_bus->read = &stmmac_xgmac2_mdio_read_c22;
new_bus->write = &stmmac_xgmac2_mdio_write_c22;
new_bus->read_c45 = &stmmac_xgmac2_mdio_read_c45;
@@ -608,25 +609,20 @@ int stmmac_mdio_register(struct net_device *ndev)
if (priv->synopsys_id < DWXGMAC_CORE_2_20) {
/* Right now only C22 phys are supported */
- max_addr = MII_XGMAC_MAX_C22ADDR + 1;
+ max_addr = MII_XGMAC_MAX_C22ADDR;
/* Check if DT specified an unsupported phy addr */
if (priv->plat->phy_addr > MII_XGMAC_MAX_C22ADDR)
dev_err(dev, "Unsupported phy_addr (max=%d)\n",
MII_XGMAC_MAX_C22ADDR);
- } else {
- /* XGMAC version 2.20 onwards support 32 phy addr */
- max_addr = PHY_MAX_ADDR;
}
} else {
new_bus->read = &stmmac_mdio_read_c22;
new_bus->write = &stmmac_mdio_write_c22;
- if (priv->plat->has_gmac4) {
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4) {
new_bus->read_c45 = &stmmac_mdio_read_c45;
new_bus->write_c45 = &stmmac_mdio_write_c45;
}
-
- max_addr = PHY_MAX_ADDR;
}
if (mdio_bus_data->needs_reset)
@@ -649,7 +645,7 @@ int stmmac_mdio_register(struct net_device *ndev)
}
/* Looks like we need a dummy read for XGMAC only and C45 PHYs */
- if (priv->plat->has_xgmac)
+ if (priv->plat->core_type == DWMAC_CORE_XGMAC)
stmmac_xgmac2_mdio_read_c45(new_bus, 0, 0, 0);
/* If fixed-link is set, skip PHY scanning */
@@ -668,41 +664,31 @@ int stmmac_mdio_register(struct net_device *ndev)
if (priv->plat->phy_node || mdio_node)
goto bus_register_done;
- found = 0;
- for (addr = 0; addr < max_addr; addr++) {
- struct phy_device *phydev = mdiobus_get_phy(new_bus, addr);
-
- if (!phydev)
- continue;
-
- /*
- * If an IRQ was provided to be assigned after
- * the bus probe, do it here.
- */
- if (!mdio_bus_data->irqs &&
- (mdio_bus_data->probed_phy_irq > 0)) {
- new_bus->irq[addr] = mdio_bus_data->probed_phy_irq;
- phydev->irq = mdio_bus_data->probed_phy_irq;
- }
-
- /*
- * If we're going to bind the MAC to this PHY bus,
- * and no PHY number was provided to the MAC,
- * use the one probed here.
- */
- if (priv->plat->phy_addr == -1)
- priv->plat->phy_addr = addr;
-
- phy_attached_info(phydev);
- found = 1;
- }
-
- if (!found && !mdio_node) {
+ phydev = phy_find_first(new_bus);
+ if (!phydev || phydev->mdio.addr > max_addr) {
dev_warn(dev, "No PHY found\n");
err = -ENODEV;
goto no_phy_found;
}
+ /*
+ * If an IRQ was provided to be assigned after
+ * the bus probe, do it here.
+ */
+ if (!mdio_bus_data->irqs && mdio_bus_data->probed_phy_irq > 0) {
+ new_bus->irq[phydev->mdio.addr] = mdio_bus_data->probed_phy_irq;
+ phydev->irq = mdio_bus_data->probed_phy_irq;
+ }
+
+ /*
+ * If we're going to bind the MAC to this PHY bus, and no PHY number
+ * was provided to the MAC, use the one probed here.
+ */
+ if (priv->plat->phy_addr == -1)
+ priv->plat->phy_addr = phydev->mdio.addr;
+
+ phy_attached_info(phydev);
+
bus_register_done:
priv->mii = new_bus;
@@ -734,3 +720,17 @@ int stmmac_mdio_unregister(struct net_device *ndev)
return 0;
}
+
+void stmmac_mdio_lock(struct stmmac_priv *priv)
+{
+ if (priv->mii)
+ mutex_lock(&priv->mii->mdio_lock);
+}
+EXPORT_SYMBOL_GPL(stmmac_mdio_lock);
+
+void stmmac_mdio_unlock(struct stmmac_priv *priv)
+{
+ if (priv->mii)
+ mutex_unlock(&priv->mii->mdio_lock);
+}
+EXPORT_SYMBOL_GPL(stmmac_mdio_unlock);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
index 4e3aa611fda8..270ad066ced3 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
@@ -14,6 +14,7 @@
#include <linux/dmi.h>
#include "stmmac.h"
+#include "stmmac_libpci.h"
struct stmmac_pci_info {
int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat);
@@ -23,30 +24,10 @@ static void common_default_data(struct plat_stmmacenet_data *plat)
{
/* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
plat->clk_csr = STMMAC_CSR_20_35M;
- plat->has_gmac = 1;
+ plat->core_type = DWMAC_CORE_GMAC;
plat->force_sf_dma_mode = 1;
plat->mdio_bus_data->needs_reset = true;
-
- /* Set default value for multicast hash bins */
- plat->multicast_filter_bins = HASH_TABLE_SIZE;
-
- /* Set default value for unicast filter entries */
- plat->unicast_filter_entries = 1;
-
- /* Set the maxmtu to a default of JUMBO_LEN */
- plat->maxmtu = JUMBO_LEN;
-
- /* Set default number of RX and TX queues to use */
- plat->tx_queues_to_use = 1;
- plat->rx_queues_to_use = 1;
-
- /* Disable Priority config by default */
- plat->tx_queues_cfg[0].use_prio = false;
- plat->rx_queues_cfg[0].use_prio = false;
-
- /* Disable RX queues routing by default */
- plat->rx_queues_cfg[0].pkt_route = 0x0;
}
static int stmmac_default_data(struct pci_dev *pdev,
@@ -76,27 +57,17 @@ static int snps_gmac5_default_data(struct pci_dev *pdev,
int i;
plat->clk_csr = STMMAC_CSR_250_300M;
- plat->has_gmac4 = 1;
+ plat->core_type = DWMAC_CORE_GMAC4;
plat->force_sf_dma_mode = 1;
plat->flags |= STMMAC_FLAG_TSO_EN;
plat->pmt = 1;
- /* Set default value for multicast hash bins */
- plat->multicast_filter_bins = HASH_TABLE_SIZE;
-
- /* Set default value for unicast filter entries */
- plat->unicast_filter_entries = 1;
-
- /* Set the maxmtu to a default of JUMBO_LEN */
- plat->maxmtu = JUMBO_LEN;
-
/* Set default number of RX and TX queues to use */
plat->tx_queues_to_use = 4;
plat->rx_queues_to_use = 4;
plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WRR;
for (i = 0; i < plat->tx_queues_to_use; i++) {
- plat->tx_queues_cfg[i].use_prio = false;
plat->tx_queues_cfg[i].mode_to_use = MTL_QUEUE_DCB;
plat->tx_queues_cfg[i].weight = 25;
if (i > 0)
@@ -104,15 +75,10 @@ static int snps_gmac5_default_data(struct pci_dev *pdev,
}
plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP;
- for (i = 0; i < plat->rx_queues_to_use; i++) {
- plat->rx_queues_cfg[i].use_prio = false;
+ for (i = 0; i < plat->rx_queues_to_use; i++)
plat->rx_queues_cfg[i].mode_to_use = MTL_QUEUE_DCB;
- plat->rx_queues_cfg[i].pkt_route = 0x0;
- plat->rx_queues_cfg[i].chan = i;
- }
plat->bus_id = 1;
- plat->phy_addr = -1;
plat->phy_interface = PHY_INTERFACE_MODE_GMII;
plat->dma_cfg->pbl = 32;
@@ -127,10 +93,8 @@ static int snps_gmac5_default_data(struct pci_dev *pdev,
plat->axi->axi_rd_osr_lmt = 31;
plat->axi->axi_fb = false;
- plat->axi->axi_blen[0] = 4;
- plat->axi->axi_blen[1] = 8;
- plat->axi->axi_blen[2] = 16;
- plat->axi->axi_blen[3] = 32;
+ plat->axi->axi_blen_regval = DMA_AXI_BLEN4 | DMA_AXI_BLEN8 |
+ DMA_AXI_BLEN16 | DMA_AXI_BLEN32;
return 0;
}
@@ -139,37 +103,6 @@ static const struct stmmac_pci_info snps_gmac5_pci_info = {
.setup = snps_gmac5_default_data,
};
-static int stmmac_pci_suspend(struct device *dev, void *bsp_priv)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- int ret;
-
- ret = pci_save_state(pdev);
- if (ret)
- return ret;
-
- pci_disable_device(pdev);
- pci_wake_from_d3(pdev, true);
- return 0;
-}
-
-static int stmmac_pci_resume(struct device *dev, void *bsp_priv)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- int ret;
-
- pci_restore_state(pdev);
- pci_set_power_state(pdev, PCI_D0);
-
- ret = pci_enable_device(pdev);
- if (ret)
- return ret;
-
- pci_set_master(pdev);
-
- return 0;
-}
-
/**
* stmmac_pci_probe
*
@@ -191,7 +124,7 @@ static int stmmac_pci_probe(struct pci_dev *pdev,
int ret;
int i;
- plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
+ plat = stmmac_plat_dat_alloc(&pdev->dev);
if (!plat)
return -ENOMEM;
@@ -249,8 +182,8 @@ static int stmmac_pci_probe(struct pci_dev *pdev,
plat->safety_feat_cfg->prtyen = 1;
plat->safety_feat_cfg->tmouten = 1;
- plat->suspend = stmmac_pci_suspend;
- plat->resume = stmmac_pci_resume;
+ plat->suspend = stmmac_pci_plat_suspend;
+ plat->resume = stmmac_pci_plat_resume;
return stmmac_dvr_probe(&pdev->dev, plat, &res);
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c
new file mode 100644
index 000000000000..e2f531c11986
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "stmmac.h"
+#include "stmmac_pcs.h"
+
+static int dwmac_integrated_pcs_enable(struct phylink_pcs *pcs)
+{
+ struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs);
+
+ stmmac_mac_irq_modify(spcs->priv, 0, spcs->int_mask);
+
+ return 0;
+}
+
+static void dwmac_integrated_pcs_disable(struct phylink_pcs *pcs)
+{
+ struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs);
+
+ stmmac_mac_irq_modify(spcs->priv, spcs->int_mask, 0);
+}
+
+static void dwmac_integrated_pcs_get_state(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ struct phylink_link_state *state)
+{
+ state->link = false;
+}
+
+static int dwmac_integrated_pcs_config(struct phylink_pcs *pcs,
+ unsigned int neg_mode,
+ phy_interface_t interface,
+ const unsigned long *advertising,
+ bool permit_pause_to_mac)
+{
+ struct stmmac_pcs *spcs = phylink_pcs_to_stmmac_pcs(pcs);
+
+ dwmac_ctrl_ane(spcs->base, 0, 1, spcs->priv->hw->reverse_sgmii_enable);
+
+ return 0;
+}
+
+static const struct phylink_pcs_ops dwmac_integrated_pcs_ops = {
+ .pcs_enable = dwmac_integrated_pcs_enable,
+ .pcs_disable = dwmac_integrated_pcs_disable,
+ .pcs_get_state = dwmac_integrated_pcs_get_state,
+ .pcs_config = dwmac_integrated_pcs_config,
+};
+
+int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset,
+ u32 int_mask)
+{
+ struct stmmac_pcs *spcs;
+
+ spcs = devm_kzalloc(priv->device, sizeof(*spcs), GFP_KERNEL);
+ if (!spcs)
+ return -ENOMEM;
+
+ spcs->priv = priv;
+ spcs->base = priv->ioaddr + offset;
+ spcs->int_mask = int_mask;
+ spcs->pcs.ops = &dwmac_integrated_pcs_ops;
+
+ __set_bit(PHY_INTERFACE_MODE_SGMII, spcs->pcs.supported_interfaces);
+
+ priv->integrated_pcs = spcs;
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
index 4a684c97dfae..cda93894168e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h
@@ -9,6 +9,7 @@
#ifndef __STMMAC_PCS_H__
#define __STMMAC_PCS_H__
+#include <linux/phylink.h>
#include <linux/slab.h>
#include <linux/io.h>
#include "common.h"
@@ -46,6 +47,24 @@
#define GMAC_ANE_RFE_SHIFT 12
#define GMAC_ANE_ACK BIT(14)
+struct stmmac_priv;
+
+struct stmmac_pcs {
+ struct stmmac_priv *priv;
+ void __iomem *base;
+ u32 int_mask;
+ struct phylink_pcs pcs;
+};
+
+static inline struct stmmac_pcs *
+phylink_pcs_to_stmmac_pcs(struct phylink_pcs *pcs)
+{
+ return container_of(pcs, struct stmmac_pcs, pcs);
+}
+
+int stmmac_integrated_pcs_init(struct stmmac_priv *priv, unsigned int offset,
+ u32 int_mask);
+
/**
* dwmac_pcs_isr - TBI, RTBI, or SGMII PHY ISR
* @ioaddr: IO registers pointer
@@ -82,13 +101,12 @@ static inline void dwmac_pcs_isr(void __iomem *ioaddr, u32 reg,
* @reg: Base address of the AN Control Register.
* @ane: to enable the auto-negotiation
* @srgmi_ral: to manage MAC-2-MAC SGMII connections.
- * @loopback: to cause the PHY to loopback tx data into rx path.
* Description: this is the main function to configure the AN control register
* and init the ANE, select loopback (usually for debugging purpose) and
* configure SGMII RAL.
*/
static inline void dwmac_ctrl_ane(void __iomem *ioaddr, u32 reg, bool ane,
- bool srgmi_ral, bool loopback)
+ bool srgmi_ral)
{
u32 value = readl(ioaddr + GMAC_AN_CTRL(reg));
@@ -104,9 +122,6 @@ static inline void dwmac_ctrl_ane(void __iomem *ioaddr, u32 reg, bool ane,
if (srgmi_ral)
value |= GMAC_AN_CTRL_SGMRAL;
- if (loopback)
- value |= GMAC_AN_CTRL_ELE;
-
writel(value, ioaddr + GMAC_AN_CTRL(reg));
}
#endif /* __STMMAC_PCS_H__ */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 27bcaae07a7f..8979a50b5507 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -95,6 +95,7 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
{
struct device_node *np;
struct stmmac_axi *axi;
+ u32 axi_blen[AXI_BLEN];
np = of_parse_phandle(pdev->dev.of_node, "snps,axi-config", 0);
if (!np)
@@ -117,7 +118,8 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
axi->axi_wr_osr_lmt = 1;
if (of_property_read_u32(np, "snps,rd_osr_lmt", &axi->axi_rd_osr_lmt))
axi->axi_rd_osr_lmt = 1;
- of_property_read_u32_array(np, "snps,blen", axi->axi_blen, AXI_BLEN);
+ of_property_read_u32_array(np, "snps,blen", axi_blen, AXI_BLEN);
+ stmmac_axi_blen_to_mask(&axi->axi_blen_regval, axi_blen, AXI_BLEN);
of_node_put(np);
return axi;
@@ -137,13 +139,6 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
u8 queue = 0;
int ret = 0;
- /* For backwards-compatibility with device trees that don't have any
- * snps,mtl-rx-config or snps,mtl-tx-config properties, we fall back
- * to one RX and TX queues each.
- */
- plat->rx_queues_to_use = 1;
- plat->tx_queues_to_use = 1;
-
/* First Queue must always be in DCB mode. As MTL_QUEUE_DCB = 1 we need
* to always set this, otherwise Queue will be classified as AVB
* (because MTL_QUEUE_AVB = 0).
@@ -162,9 +157,8 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
}
/* Processing RX queues common config */
- if (of_property_read_u32(rx_node, "snps,rx-queues-to-use",
- &plat->rx_queues_to_use))
- plat->rx_queues_to_use = 1;
+ of_property_read_u32(rx_node, "snps,rx-queues-to-use",
+ &plat->rx_queues_to_use);
if (of_property_read_bool(rx_node, "snps,rx-sched-sp"))
plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP;
@@ -185,18 +179,13 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
else
plat->rx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB;
- if (of_property_read_u32(q_node, "snps,map-to-dma-channel",
- &plat->rx_queues_cfg[queue].chan))
- plat->rx_queues_cfg[queue].chan = queue;
+ of_property_read_u32(q_node, "snps,map-to-dma-channel",
+ &plat->rx_queues_cfg[queue].chan);
/* TODO: Dynamic mapping to be included in the future */
- if (of_property_read_u32(q_node, "snps,priority",
- &plat->rx_queues_cfg[queue].prio)) {
- plat->rx_queues_cfg[queue].prio = 0;
- plat->rx_queues_cfg[queue].use_prio = false;
- } else {
+ if (!of_property_read_u32(q_node, "snps,priority",
+ &plat->rx_queues_cfg[queue].prio))
plat->rx_queues_cfg[queue].use_prio = true;
- }
/* RX queue specific packet type routing */
if (of_property_read_bool(q_node, "snps,route-avcp"))
@@ -209,8 +198,6 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
plat->rx_queues_cfg[queue].pkt_route = PACKET_UPQ;
else if (of_property_read_bool(q_node, "snps,route-multi-broad"))
plat->rx_queues_cfg[queue].pkt_route = PACKET_MCBCQ;
- else
- plat->rx_queues_cfg[queue].pkt_route = 0x0;
queue++;
}
@@ -221,9 +208,8 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
}
/* Processing TX queues common config */
- if (of_property_read_u32(tx_node, "snps,tx-queues-to-use",
- &plat->tx_queues_to_use))
- plat->tx_queues_to_use = 1;
+ of_property_read_u32(tx_node, "snps,tx-queues-to-use",
+ &plat->tx_queues_to_use);
if (of_property_read_bool(tx_node, "snps,tx-sched-wrr"))
plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WRR;
@@ -268,13 +254,9 @@ static int stmmac_mtl_setup(struct platform_device *pdev,
plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB;
}
- if (of_property_read_u32(q_node, "snps,priority",
- &plat->tx_queues_cfg[queue].prio)) {
- plat->tx_queues_cfg[queue].prio = 0;
- plat->tx_queues_cfg[queue].use_prio = false;
- } else {
+ if (!of_property_read_u32(q_node, "snps,priority",
+ &plat->tx_queues_cfg[queue].prio))
plat->tx_queues_cfg[queue].use_prio = true;
- }
plat->tx_queues_cfg[queue].coe_unsupported =
of_property_read_bool(q_node, "snps,coe-unsupported");
@@ -436,7 +418,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
void *ret;
int rc;
- plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
+ plat = stmmac_plat_dat_alloc(&pdev->dev);
if (!plat)
return ERR_PTR(-ENOMEM);
@@ -480,13 +462,6 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
plat->bus_id = ++bus_id;
}
- /* Default to phy auto-detection */
- plat->phy_addr = -1;
-
- /* Default to get clk_csr from stmmac_clk_csr_set(),
- * or get clk_csr from device tree.
- */
- plat->clk_csr = -1;
if (of_property_read_u32(np, "snps,clk-csr", &plat->clk_csr))
of_property_read_u32(np, "clk_csr", &plat->clk_csr);
@@ -515,17 +490,6 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
plat->flags |= STMMAC_FLAG_EN_TX_LPI_CLOCKGATING;
}
- /* Set the maxmtu to a default of JUMBO_LEN in case the
- * parameter is not present in the device tree.
- */
- plat->maxmtu = JUMBO_LEN;
-
- /* Set default value for multicast hash bins */
- plat->multicast_filter_bins = HASH_TABLE_SIZE;
-
- /* Set default value for unicast filter entries */
- plat->unicast_filter_entries = 1;
-
/*
* Currently only the properties needed on SPEAr600
* are provided. All other properties should be added
@@ -552,12 +516,12 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
&pdev->dev, plat->unicast_filter_entries);
plat->multicast_filter_bins = dwmac1000_validate_mcast_bins(
&pdev->dev, plat->multicast_filter_bins);
- plat->has_gmac = 1;
+ plat->core_type = DWMAC_CORE_GMAC;
plat->pmt = 1;
}
if (of_device_is_compatible(np, "snps,dwmac-3.40a")) {
- plat->has_gmac = 1;
+ plat->core_type = DWMAC_CORE_GMAC;
plat->enh_desc = 1;
plat->tx_coe = 1;
plat->bugged_jumbo = 1;
@@ -565,8 +529,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
}
if (of_device_compatible_match(np, stmmac_gmac4_compats)) {
- plat->has_gmac4 = 1;
- plat->has_gmac = 0;
+ plat->core_type = DWMAC_CORE_GMAC4;
plat->pmt = 1;
if (of_property_read_bool(np, "snps,tso"))
plat->flags |= STMMAC_FLAG_TSO_EN;
@@ -580,7 +543,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac)
}
if (of_device_is_compatible(np, "snps,dwxgmac")) {
- plat->has_xgmac = 1;
+ plat->core_type = DWMAC_CORE_XGMAC;
plat->pmt = 1;
if (of_property_read_bool(np, "snps,tso"))
plat->flags |= STMMAC_FLAG_TSO_EN;
@@ -786,40 +749,40 @@ EXPORT_SYMBOL_GPL(stmmac_get_platform_resources);
/**
* stmmac_pltfr_init
- * @pdev: pointer to the platform device
+ * @dev: pointer to the device structure
* @plat: driver data platform structure
* Description: Call the platform's init callback (if any) and propagate
* the return value.
*/
-static int stmmac_pltfr_init(struct platform_device *pdev,
+static int stmmac_pltfr_init(struct device *dev,
struct plat_stmmacenet_data *plat)
{
int ret = 0;
if (plat->init)
- ret = plat->init(pdev, plat->bsp_priv);
+ ret = plat->init(dev, plat->bsp_priv);
return ret;
}
/**
* stmmac_pltfr_exit
- * @pdev: pointer to the platform device
+ * @dev: pointer to the device structure
* @plat: driver data platform structure
* Description: Call the platform's exit callback (if any).
*/
-static void stmmac_pltfr_exit(struct platform_device *pdev,
+static void stmmac_pltfr_exit(struct device *dev,
struct plat_stmmacenet_data *plat)
{
if (plat->exit)
- plat->exit(pdev, plat->bsp_priv);
+ plat->exit(dev, plat->bsp_priv);
}
static int stmmac_plat_suspend(struct device *dev, void *bsp_priv)
{
struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(dev));
- stmmac_pltfr_exit(to_platform_device(dev), priv->plat);
+ stmmac_pltfr_exit(dev, priv->plat);
return 0;
}
@@ -828,7 +791,7 @@ static int stmmac_plat_resume(struct device *dev, void *bsp_priv)
{
struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(dev));
- return stmmac_pltfr_init(to_platform_device(dev), priv->plat);
+ return stmmac_pltfr_init(dev, priv->plat);
}
/**
@@ -843,24 +806,12 @@ int stmmac_pltfr_probe(struct platform_device *pdev,
struct plat_stmmacenet_data *plat,
struct stmmac_resources *res)
{
- int ret;
-
if (!plat->suspend && plat->exit)
plat->suspend = stmmac_plat_suspend;
if (!plat->resume && plat->init)
plat->resume = stmmac_plat_resume;
- ret = stmmac_pltfr_init(pdev, plat);
- if (ret)
- return ret;
-
- ret = stmmac_dvr_probe(&pdev->dev, plat, res);
- if (ret) {
- stmmac_pltfr_exit(pdev, plat);
- return ret;
- }
-
- return ret;
+ return stmmac_dvr_probe(&pdev->dev, plat, res);
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_probe);
@@ -902,12 +853,7 @@ EXPORT_SYMBOL_GPL(devm_stmmac_pltfr_probe);
*/
void stmmac_pltfr_remove(struct platform_device *pdev)
{
- struct net_device *ndev = platform_get_drvdata(pdev);
- struct stmmac_priv *priv = netdev_priv(ndev);
- struct plat_stmmacenet_data *plat = priv->plat;
-
stmmac_dvr_remove(&pdev->dev);
- stmmac_pltfr_exit(pdev, plat);
}
EXPORT_SYMBOL_GPL(stmmac_pltfr_remove);
@@ -970,7 +916,7 @@ static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev)
if (!netif_running(ndev))
return 0;
- if (!stmmac_wol_enabled_mac(priv)) {
+ if (!priv->wolopts) {
/* Disable clock in case of PWM is off */
clk_disable_unprepare(priv->plat->clk_ptp_ref);
@@ -991,7 +937,7 @@ static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev)
if (!netif_running(ndev))
return 0;
- if (!stmmac_wol_enabled_mac(priv)) {
+ if (!priv->wolopts) {
/* enable the clk previously disabled */
ret = pm_runtime_force_resume(dev);
if (ret)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
index 993ff4e87e55..3e30172fa129 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
@@ -57,7 +57,7 @@ static int stmmac_adjust_time(struct ptp_clock_info *ptp, s64 delta)
bool xmac, est_rst = false;
int ret;
- xmac = priv->plat->has_gmac4 || priv->plat->has_xgmac;
+ xmac = dwmac_is_xmac(priv->plat->core_type);
if (delta < 0) {
neg_adj = 1;
@@ -344,7 +344,7 @@ void stmmac_ptp_register(struct stmmac_priv *priv)
/* Calculate the clock domain crossing (CDC) error if necessary */
priv->plat->cdc_error_adj = 0;
- if (priv->plat->has_gmac4)
+ if (priv->plat->core_type == DWMAC_CORE_GMAC4)
priv->plat->cdc_error_adj = (2 * NSEC_PER_SEC) / priv->plat->clk_ptp_rate;
/* Update the ptp clock parameters based on feature discovery, when
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
index a01bc394d1ac..e90a2c469b9a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_selftests.c
@@ -1721,7 +1721,7 @@ static int stmmac_test_sph(struct stmmac_priv *priv)
struct stmmac_packet_attrs attr = { };
int ret;
- if (!priv->sph)
+ if (!priv->sph_active)
return -EOPNOTSUPP;
/* Check for UDP first */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
index 97e89a604abd..d78652718599 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c
@@ -262,10 +262,10 @@ static int tc_init(struct stmmac_priv *priv)
unsigned int count;
int ret, i;
- if (dma_cap->l3l4fnum) {
- priv->flow_entries_max = dma_cap->l3l4fnum;
+ priv->flow_entries_max = dma_cap->l3l4fnum;
+ if (priv->flow_entries_max) {
priv->flow_entries = devm_kcalloc(priv->device,
- dma_cap->l3l4fnum,
+ priv->flow_entries_max,
sizeof(*priv->flow_entries),
GFP_KERNEL);
if (!priv->flow_entries)
@@ -981,7 +981,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
if (qopt->cmd == TAPRIO_CMD_DESTROY)
goto disable;
- if (qopt->num_entries >= dep)
+ if (qopt->num_entries > dep)
return -EINVAL;
if (!qopt->cycle_time)
return -ERANGE;
@@ -1012,7 +1012,7 @@ static int tc_taprio_configure(struct stmmac_priv *priv,
s64 delta_ns = qopt->entries[i].interval;
u32 gates = qopt->entries[i].gate_mask;
- if (delta_ns > GENMASK(wid, 0))
+ if (delta_ns > GENMASK(wid - 1, 0))
return -ERANGE;
if (gates > GENMASK(31 - wid, 0))
return -ERANGE;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
index 0b6f6228ae35..b18404dd5a8b 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_vlan.c
@@ -122,7 +122,8 @@ static int vlan_del_hw_rx_fltr(struct net_device *dev,
/* Extended Rx VLAN Filter Enable */
for (i = 0; i < hw->num_vlan; i++) {
- if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) == vid) {
+ if ((hw->vlan_filter[i] & VLAN_TAG_DATA_VEN) &&
+ ((hw->vlan_filter[i] & VLAN_TAG_DATA_VID) == vid)) {
ret = vlan_write_filter(dev, hw, i, 0);
if (!ret)
@@ -212,7 +213,7 @@ static void vlan_enable(struct mac_device_info *hw, u32 type)
value = readl(ioaddr + VLAN_INCL);
value |= VLAN_VLTI;
- value |= VLAN_CSVL; /* Only use SVLAN */
+ value &= ~VLAN_CSVL; /* Only use CVLAN */
value &= ~VLAN_VLC;
value |= (type << VLAN_VLC_SHIFT) & VLAN_VLC;
writel(value, ioaddr + VLAN_INCL);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
index aa6f16d3df64..d7e4db7224b0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_xdp.c
@@ -129,7 +129,7 @@ int stmmac_xdp_set_prog(struct stmmac_priv *priv, struct bpf_prog *prog,
bpf_prog_put(old_prog);
/* Disable RX SPH for XDP operation */
- priv->sph = priv->sph_cap && !stmmac_xdp_is_enabled(priv);
+ priv->sph_active = priv->sph_capable && !stmmac_xdp_is_enabled(priv);
if (if_running && need_update)
stmmac_xdp_open(dev);
diff --git a/drivers/net/ethernet/ti/am65-cpsw-nuss.c b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
index 110eb2da8dbc..5924db6be3fe 100644
--- a/drivers/net/ethernet/ti/am65-cpsw-nuss.c
+++ b/drivers/net/ethernet/ti/am65-cpsw-nuss.c
@@ -1788,28 +1788,28 @@ static int am65_cpsw_nuss_ndo_slave_set_mac_address(struct net_device *ndev,
}
static int am65_cpsw_nuss_hwtstamp_set(struct net_device *ndev,
- struct ifreq *ifr)
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
u32 ts_ctrl, seq_id, ts_ctrl_ltype2, ts_vlan_ltype;
- struct hwtstamp_config cfg;
- if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
+ if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS)) {
+ NL_SET_ERR_MSG(extack, "Time stamping is not supported");
return -EOPNOTSUPP;
-
- if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
- return -EFAULT;
+ }
/* TX HW timestamp */
- switch (cfg.tx_type) {
+ switch (cfg->tx_type) {
case HWTSTAMP_TX_OFF:
case HWTSTAMP_TX_ON:
break;
default:
+ NL_SET_ERR_MSG(extack, "TX mode is not supported");
return -ERANGE;
}
- switch (cfg.rx_filter) {
+ switch (cfg->rx_filter) {
case HWTSTAMP_FILTER_NONE:
port->rx_ts_enabled = false;
break;
@@ -1826,17 +1826,19 @@ static int am65_cpsw_nuss_hwtstamp_set(struct net_device *ndev,
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
port->rx_ts_enabled = true;
- cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT | HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+ cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT | HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
break;
case HWTSTAMP_FILTER_ALL:
case HWTSTAMP_FILTER_SOME:
case HWTSTAMP_FILTER_NTP_ALL:
+ NL_SET_ERR_MSG(extack, "RX filter is not supported");
return -EOPNOTSUPP;
default:
+ NL_SET_ERR_MSG(extack, "RX filter is not supported");
return -ERANGE;
}
- port->tx_ts_enabled = (cfg.tx_type == HWTSTAMP_TX_ON);
+ port->tx_ts_enabled = (cfg->tx_type == HWTSTAMP_TX_ON);
/* cfg TX timestamp */
seq_id = (AM65_CPSW_TS_SEQ_ID_OFFSET <<
@@ -1872,25 +1874,24 @@ static int am65_cpsw_nuss_hwtstamp_set(struct net_device *ndev,
AM65_CPSW_PORTN_REG_TS_CTL_LTYPE2);
writel(ts_ctrl, port->port_base + AM65_CPSW_PORTN_REG_TS_CTL);
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+ return 0;
}
static int am65_cpsw_nuss_hwtstamp_get(struct net_device *ndev,
- struct ifreq *ifr)
+ struct kernel_hwtstamp_config *cfg)
{
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
- struct hwtstamp_config cfg;
if (!IS_ENABLED(CONFIG_TI_K3_AM65_CPTS))
return -EOPNOTSUPP;
- cfg.flags = 0;
- cfg.tx_type = port->tx_ts_enabled ?
+ cfg->flags = 0;
+ cfg->tx_type = port->tx_ts_enabled ?
HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
- cfg.rx_filter = port->rx_ts_enabled ? HWTSTAMP_FILTER_PTP_V2_EVENT |
+ cfg->rx_filter = port->rx_ts_enabled ? HWTSTAMP_FILTER_PTP_V2_EVENT |
HWTSTAMP_FILTER_PTP_V1_L4_EVENT : HWTSTAMP_FILTER_NONE;
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+ return 0;
}
static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev,
@@ -1901,13 +1902,6 @@ static int am65_cpsw_nuss_ndo_slave_ioctl(struct net_device *ndev,
if (!netif_running(ndev))
return -EINVAL;
- switch (cmd) {
- case SIOCSHWTSTAMP:
- return am65_cpsw_nuss_hwtstamp_set(ndev, req);
- case SIOCGHWTSTAMP:
- return am65_cpsw_nuss_hwtstamp_get(ndev, req);
- }
-
return phylink_mii_ioctl(port->slave.phylink, req, cmd);
}
@@ -1991,6 +1985,8 @@ static const struct net_device_ops am65_cpsw_nuss_netdev_ops = {
.ndo_set_tx_maxrate = am65_cpsw_qos_ndo_tx_p0_set_maxrate,
.ndo_bpf = am65_cpsw_ndo_bpf,
.ndo_xdp_xmit = am65_cpsw_ndo_xdp_xmit,
+ .ndo_hwtstamp_get = am65_cpsw_nuss_hwtstamp_get,
+ .ndo_hwtstamp_set = am65_cpsw_nuss_hwtstamp_set,
};
static void am65_cpsw_disable_phy(struct phy *phy)
@@ -3072,7 +3068,8 @@ static void am65_cpsw_init_host_port_emac(struct am65_cpsw_common *common)
}
static int am65_cpsw_dl_switch_mode_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct am65_cpsw_devlink *dl_priv = devlink_priv(dl);
struct am65_cpsw_common *common = dl_priv->common;
diff --git a/drivers/net/ethernet/ti/am65-cpsw-qos.c b/drivers/net/ethernet/ti/am65-cpsw-qos.c
index fa96db7c1a13..66e8b224827b 100644
--- a/drivers/net/ethernet/ti/am65-cpsw-qos.c
+++ b/drivers/net/ethernet/ti/am65-cpsw-qos.c
@@ -276,9 +276,31 @@ static int am65_cpsw_iet_set_verify_timeout_count(struct am65_cpsw_port *port)
/* The number of wireside clocks contained in the verify
* timeout counter. The default is 0x1312d0
* (10ms at 125Mhz in 1G mode).
+ * The frequency of the clock depends on the link speed
+ * and the PHY interface.
*/
- val = 125 * HZ_PER_MHZ; /* assuming 125MHz wireside clock */
+ switch (port->slave.phy_if) {
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ if (port->qos.link_speed == SPEED_1000)
+ val = 125 * HZ_PER_MHZ; /* 125 MHz at 1000Mbps*/
+ else if (port->qos.link_speed == SPEED_100)
+ val = 25 * HZ_PER_MHZ; /* 25 MHz at 100Mbps*/
+ else
+ val = (25 * HZ_PER_MHZ) / 10; /* 2.5 MHz at 10Mbps*/
+ break;
+
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_SGMII:
+ val = 125 * HZ_PER_MHZ; /* 125 MHz */
+ break;
+ default:
+ netdev_err(port->ndev, "selected mode does not supported IET\n");
+ return -EOPNOTSUPP;
+ }
val /= MILLIHZ_PER_HZ; /* count per ms timeout */
val *= verify_time_ms; /* count for timeout ms */
@@ -295,20 +317,21 @@ static int am65_cpsw_iet_verify_wait(struct am65_cpsw_port *port)
u32 ctrl, status;
int try;
- try = 20;
- do {
- /* Reset the verify state machine by writing 1
- * to LINKFAIL
- */
- ctrl = readl(port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
- ctrl |= AM65_CPSW_PN_IET_MAC_LINKFAIL;
- writel(ctrl, port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
+ try = 3;
- /* Clear MAC_LINKFAIL bit to start Verify. */
- ctrl = readl(port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
- ctrl &= ~AM65_CPSW_PN_IET_MAC_LINKFAIL;
- writel(ctrl, port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
+ /* Reset the verify state machine by writing 1
+ * to LINKFAIL
+ */
+ ctrl = readl(port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
+ ctrl |= AM65_CPSW_PN_IET_MAC_LINKFAIL;
+ writel(ctrl, port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
+ /* Clear MAC_LINKFAIL bit to start Verify. */
+ ctrl = readl(port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
+ ctrl &= ~AM65_CPSW_PN_IET_MAC_LINKFAIL;
+ writel(ctrl, port->port_base + AM65_CPSW_PN_REG_IET_CTRL);
+
+ do {
msleep(port->qos.iet.verify_time_ms);
status = readl(port->port_base + AM65_CPSW_PN_REG_IET_STATUS);
@@ -330,7 +353,7 @@ static int am65_cpsw_iet_verify_wait(struct am65_cpsw_port *port)
netdev_dbg(port->ndev, "MAC Merge verify error\n");
return -ENODEV;
}
- } while (try-- > 0);
+ } while (--try > 0);
netdev_dbg(port->ndev, "MAC Merge verify timeout\n");
return -ETIMEDOUT;
diff --git a/drivers/net/ethernet/ti/am65-cpts.c b/drivers/net/ethernet/ti/am65-cpts.c
index 59d6ab989c55..8ffbfaa3ab18 100644
--- a/drivers/net/ethernet/ti/am65-cpts.c
+++ b/drivers/net/ethernet/ti/am65-cpts.c
@@ -163,7 +163,9 @@ struct am65_cpts {
struct device_node *clk_mux_np;
struct clk *refclk;
u32 refclk_freq;
- struct list_head events;
+ /* separate lists to handle TX and RX timestamp independently */
+ struct list_head events_tx;
+ struct list_head events_rx;
struct list_head pool;
struct am65_cpts_event pool_data[AM65_CPTS_MAX_EVENTS];
spinlock_t lock; /* protects events lists*/
@@ -227,6 +229,24 @@ static void am65_cpts_disable(struct am65_cpts *cpts)
am65_cpts_write32(cpts, 0, int_enable);
}
+static int am65_cpts_purge_event_list(struct am65_cpts *cpts,
+ struct list_head *events)
+{
+ struct list_head *this, *next;
+ struct am65_cpts_event *event;
+ int removed = 0;
+
+ list_for_each_safe(this, next, events) {
+ event = list_entry(this, struct am65_cpts_event, list);
+ if (time_after(jiffies, event->tmo)) {
+ list_del_init(&event->list);
+ list_add(&event->list, &cpts->pool);
+ ++removed;
+ }
+ }
+ return removed;
+}
+
static int am65_cpts_event_get_port(struct am65_cpts_event *event)
{
return (event->event1 & AM65_CPTS_EVENT_1_PORT_NUMBER_MASK) >>
@@ -239,20 +259,12 @@ static int am65_cpts_event_get_type(struct am65_cpts_event *event)
AM65_CPTS_EVENT_1_EVENT_TYPE_SHIFT;
}
-static int am65_cpts_cpts_purge_events(struct am65_cpts *cpts)
+static int am65_cpts_purge_events(struct am65_cpts *cpts)
{
- struct list_head *this, *next;
- struct am65_cpts_event *event;
int removed = 0;
- list_for_each_safe(this, next, &cpts->events) {
- event = list_entry(this, struct am65_cpts_event, list);
- if (time_after(jiffies, event->tmo)) {
- list_del_init(&event->list);
- list_add(&event->list, &cpts->pool);
- ++removed;
- }
- }
+ removed += am65_cpts_purge_event_list(cpts, &cpts->events_tx);
+ removed += am65_cpts_purge_event_list(cpts, &cpts->events_rx);
if (removed)
dev_dbg(cpts->dev, "event pool cleaned up %d\n", removed);
@@ -287,7 +299,7 @@ static int __am65_cpts_fifo_read(struct am65_cpts *cpts)
struct am65_cpts_event, list);
if (!event) {
- if (am65_cpts_cpts_purge_events(cpts)) {
+ if (am65_cpts_purge_events(cpts)) {
dev_err(cpts->dev, "cpts: event pool empty\n");
ret = -1;
goto out;
@@ -306,11 +318,21 @@ static int __am65_cpts_fifo_read(struct am65_cpts *cpts)
cpts->timestamp);
break;
case AM65_CPTS_EV_RX:
+ event->tmo = jiffies +
+ msecs_to_jiffies(AM65_CPTS_EVENT_RX_TX_TIMEOUT);
+
+ list_move_tail(&event->list, &cpts->events_rx);
+
+ dev_dbg(cpts->dev,
+ "AM65_CPTS_EV_RX e1:%08x e2:%08x t:%lld\n",
+ event->event1, event->event2,
+ event->timestamp);
+ break;
case AM65_CPTS_EV_TX:
event->tmo = jiffies +
msecs_to_jiffies(AM65_CPTS_EVENT_RX_TX_TIMEOUT);
- list_move_tail(&event->list, &cpts->events);
+ list_move_tail(&event->list, &cpts->events_tx);
dev_dbg(cpts->dev,
"AM65_CPTS_EV_TX e1:%08x e2:%08x t:%lld\n",
@@ -828,7 +850,7 @@ static bool am65_cpts_match_tx_ts(struct am65_cpts *cpts,
return found;
}
-static void am65_cpts_find_ts(struct am65_cpts *cpts)
+static void am65_cpts_find_tx_ts(struct am65_cpts *cpts)
{
struct am65_cpts_event *event;
struct list_head *this, *next;
@@ -837,7 +859,7 @@ static void am65_cpts_find_ts(struct am65_cpts *cpts)
LIST_HEAD(events);
spin_lock_irqsave(&cpts->lock, flags);
- list_splice_init(&cpts->events, &events);
+ list_splice_init(&cpts->events_tx, &events);
spin_unlock_irqrestore(&cpts->lock, flags);
list_for_each_safe(this, next, &events) {
@@ -850,7 +872,7 @@ static void am65_cpts_find_ts(struct am65_cpts *cpts)
}
spin_lock_irqsave(&cpts->lock, flags);
- list_splice_tail(&events, &cpts->events);
+ list_splice_tail(&events, &cpts->events_tx);
list_splice_tail(&events_free, &cpts->pool);
spin_unlock_irqrestore(&cpts->lock, flags);
}
@@ -861,7 +883,7 @@ static long am65_cpts_ts_work(struct ptp_clock_info *ptp)
unsigned long flags;
long delay = -1;
- am65_cpts_find_ts(cpts);
+ am65_cpts_find_tx_ts(cpts);
spin_lock_irqsave(&cpts->txq.lock, flags);
if (!skb_queue_empty(&cpts->txq))
@@ -905,7 +927,7 @@ static u64 am65_cpts_find_rx_ts(struct am65_cpts *cpts, u32 skb_mtype_seqid)
spin_lock_irqsave(&cpts->lock, flags);
__am65_cpts_fifo_read(cpts);
- list_for_each_safe(this, next, &cpts->events) {
+ list_for_each_safe(this, next, &cpts->events_rx) {
event = list_entry(this, struct am65_cpts_event, list);
if (time_after(jiffies, event->tmo)) {
list_move(&event->list, &cpts->pool);
@@ -1155,7 +1177,8 @@ struct am65_cpts *am65_cpts_create(struct device *dev, void __iomem *regs,
return ERR_PTR(ret);
mutex_init(&cpts->ptp_clk_lock);
- INIT_LIST_HEAD(&cpts->events);
+ INIT_LIST_HEAD(&cpts->events_tx);
+ INIT_LIST_HEAD(&cpts->events_rx);
INIT_LIST_HEAD(&cpts->pool);
spin_lock_init(&cpts->lock);
skb_queue_head_init(&cpts->txq);
diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c
index 8b9e2078c602..ab88d4c02cbd 100644
--- a/drivers/net/ethernet/ti/cpsw_new.c
+++ b/drivers/net/ethernet/ti/cpsw_new.c
@@ -1618,7 +1618,8 @@ static const struct devlink_ops cpsw_devlink_ops = {
};
static int cpsw_dl_switch_mode_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct cpsw_devlink *dl_priv = devlink_priv(dl);
struct cpsw_common *cpsw = dl_priv->cpsw;
@@ -1753,7 +1754,8 @@ exit:
}
static int cpsw_dl_ale_ctrl_get(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct cpsw_devlink *dl_priv = devlink_priv(dl);
struct cpsw_common *cpsw = dl_priv->cpsw;
diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c
index 68507126be8e..48f85a3649b2 100644
--- a/drivers/net/ethernet/ti/davinci_mdio.c
+++ b/drivers/net/ethernet/ti/davinci_mdio.c
@@ -234,7 +234,6 @@ static int davinci_mdiobb_read_c22(struct mii_bus *bus, int phy, int reg)
ret = mdiobb_read_c22(bus, phy, reg);
- pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent);
return ret;
@@ -251,7 +250,6 @@ static int davinci_mdiobb_write_c22(struct mii_bus *bus, int phy, int reg,
ret = mdiobb_write_c22(bus, phy, reg, val);
- pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent);
return ret;
@@ -268,7 +266,6 @@ static int davinci_mdiobb_read_c45(struct mii_bus *bus, int phy, int devad,
ret = mdiobb_read_c45(bus, phy, devad, reg);
- pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent);
return ret;
@@ -285,7 +282,6 @@ static int davinci_mdiobb_write_c45(struct mii_bus *bus, int phy, int devad,
ret = mdiobb_write_c45(bus, phy, devad, reg, val);
- pm_runtime_mark_last_busy(bus->parent);
pm_runtime_put_autosuspend(bus->parent);
return ret;
@@ -332,7 +328,6 @@ static int davinci_mdio_common_reset(struct davinci_mdio_data *data)
data->bus->phy_mask = phy_mask;
done:
- pm_runtime_mark_last_busy(data->dev);
pm_runtime_put_autosuspend(data->dev);
return 0;
@@ -441,7 +436,6 @@ static int davinci_mdio_read(struct mii_bus *bus, int phy_id, int phy_reg)
break;
}
- pm_runtime_mark_last_busy(data->dev);
pm_runtime_put_autosuspend(data->dev);
return ret;
}
@@ -478,7 +472,6 @@ static int davinci_mdio_write(struct mii_bus *bus, int phy_id,
break;
}
- pm_runtime_mark_last_busy(data->dev);
pm_runtime_put_autosuspend(data->dev);
return ret;
@@ -548,8 +541,8 @@ static int davinci_mdio_probe(struct platform_device *pdev)
struct davinci_mdio_data *data;
struct resource *res;
struct phy_device *phy;
- int ret, addr;
int autosuspend_delay_ms = -1;
+ int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@@ -652,14 +645,10 @@ static int davinci_mdio_probe(struct platform_device *pdev)
goto bail_out;
/* scan and dump the bus */
- for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- phy = mdiobus_get_phy(data->bus, addr);
- if (phy) {
- dev_info(dev, "phy[%d]: device %s, driver %s\n",
- phy->mdio.addr, phydev_name(phy),
- phy->drv ? phy->drv->name : "unknown");
- }
- }
+ mdiobus_for_each_phy(data->bus, phy)
+ dev_info(dev, "phy[%d]: device %s, driver %s\n",
+ phy->mdio.addr, phydev_name(phy),
+ phy->drv ? phy->drv->name : "unknown");
return 0;
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index 57e5f1c88f50..090aa74d3ce7 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -93,15 +93,91 @@ void prueth_ndev_del_tx_napi(struct prueth_emac *emac, int num)
}
EXPORT_SYMBOL_GPL(prueth_ndev_del_tx_napi);
+static int emac_xsk_xmit_zc(struct prueth_emac *emac,
+ unsigned int q_idx)
+{
+ struct prueth_tx_chn *tx_chn = &emac->tx_chns[q_idx];
+ struct xsk_buff_pool *pool = tx_chn->xsk_pool;
+ struct net_device *ndev = emac->ndev;
+ struct cppi5_host_desc_t *host_desc;
+ dma_addr_t dma_desc, dma_buf;
+ struct prueth_swdata *swdata;
+ struct xdp_desc xdp_desc;
+ int num_tx = 0, pkt_len;
+ int descs_avail, ret;
+ u32 *epib;
+ int i;
+
+ descs_avail = k3_cppi_desc_pool_avail(tx_chn->desc_pool);
+ /* ensure that TX ring is not filled up by XDP, always MAX_SKB_FRAGS
+ * will be available for normal TX path and queue is stopped there if
+ * necessary
+ */
+ if (descs_avail <= MAX_SKB_FRAGS)
+ return 0;
+
+ descs_avail -= MAX_SKB_FRAGS;
+
+ for (i = 0; i < descs_avail; i++) {
+ if (!xsk_tx_peek_desc(pool, &xdp_desc))
+ break;
+
+ dma_buf = xsk_buff_raw_get_dma(pool, xdp_desc.addr);
+ pkt_len = xdp_desc.len;
+ xsk_buff_raw_dma_sync_for_device(pool, dma_buf, pkt_len);
+
+ host_desc = k3_cppi_desc_pool_alloc(tx_chn->desc_pool);
+ if (unlikely(!host_desc))
+ break;
+
+ cppi5_hdesc_init(host_desc, CPPI5_INFO0_HDESC_EPIB_PRESENT,
+ PRUETH_NAV_PS_DATA_SIZE);
+ cppi5_hdesc_set_pkttype(host_desc, 0);
+ epib = host_desc->epib;
+ epib[0] = 0;
+ epib[1] = 0;
+ cppi5_hdesc_set_pktlen(host_desc, pkt_len);
+ cppi5_desc_set_tags_ids(&host_desc->hdr, 0,
+ (emac->port_id | (q_idx << 8)));
+
+ k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &dma_buf);
+ cppi5_hdesc_attach_buf(host_desc, dma_buf, pkt_len, dma_buf,
+ pkt_len);
+
+ swdata = cppi5_hdesc_get_swdata(host_desc);
+ swdata->type = PRUETH_SWDATA_XSK;
+
+ dma_desc = k3_cppi_desc_pool_virt2dma(tx_chn->desc_pool,
+ host_desc);
+ ret = k3_udma_glue_push_tx_chn(tx_chn->tx_chn,
+ host_desc, dma_desc);
+
+ if (ret) {
+ ndev->stats.tx_errors++;
+ k3_cppi_desc_pool_free(tx_chn->desc_pool, host_desc);
+ break;
+ }
+
+ num_tx++;
+ }
+
+ xsk_tx_release(tx_chn->xsk_pool);
+ return num_tx;
+}
+
void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
struct cppi5_host_desc_t *desc)
{
struct cppi5_host_desc_t *first_desc, *next_desc;
dma_addr_t buf_dma, next_desc_dma;
+ struct prueth_swdata *swdata;
u32 buf_dma_len;
first_desc = desc;
next_desc = first_desc;
+ swdata = cppi5_hdesc_get_swdata(first_desc);
+ if (swdata->type == PRUETH_SWDATA_XSK)
+ goto free_pool;
cppi5_hdesc_get_obuf(first_desc, &buf_dma, &buf_dma_len);
k3_udma_glue_tx_cppi5_to_dma_addr(tx_chn->tx_chn, &buf_dma);
@@ -126,6 +202,7 @@ void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
k3_cppi_desc_pool_free(tx_chn->desc_pool, next_desc);
}
+free_pool:
k3_cppi_desc_pool_free(tx_chn->desc_pool, first_desc);
}
EXPORT_SYMBOL_GPL(prueth_xmit_free);
@@ -139,7 +216,9 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
struct prueth_swdata *swdata;
struct prueth_tx_chn *tx_chn;
unsigned int total_bytes = 0;
+ int xsk_frames_done = 0;
struct xdp_frame *xdpf;
+ unsigned int pkt_len;
struct sk_buff *skb;
dma_addr_t desc_dma;
int res, num_tx = 0;
@@ -176,6 +255,11 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
total_bytes += xdpf->len;
xdp_return_frame(xdpf);
break;
+ case PRUETH_SWDATA_XSK:
+ pkt_len = cppi5_hdesc_get_pktlen(desc_tx);
+ dev_sw_netstats_tx_add(ndev, 1, pkt_len);
+ xsk_frames_done++;
+ break;
default:
prueth_xmit_free(tx_chn, desc_tx);
ndev->stats.tx_dropped++;
@@ -204,6 +288,18 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
__netif_tx_unlock(netif_txq);
}
+ if (tx_chn->xsk_pool) {
+ if (xsk_frames_done)
+ xsk_tx_completed(tx_chn->xsk_pool, xsk_frames_done);
+
+ if (xsk_uses_need_wakeup(tx_chn->xsk_pool))
+ xsk_set_tx_need_wakeup(tx_chn->xsk_pool);
+
+ netif_txq = netdev_get_tx_queue(ndev, chn);
+ txq_trans_cond_update(netif_txq);
+ emac_xsk_xmit_zc(emac, chn);
+ }
+
return num_tx;
}
@@ -212,7 +308,10 @@ static enum hrtimer_restart emac_tx_timer_callback(struct hrtimer *timer)
struct prueth_tx_chn *tx_chns =
container_of(timer, struct prueth_tx_chn, tx_hrtimer);
- enable_irq(tx_chns->irq);
+ if (tx_chns->irq_disabled) {
+ tx_chns->irq_disabled = false;
+ enable_irq(tx_chns->irq);
+ }
return HRTIMER_NORESTART;
}
@@ -235,7 +334,10 @@ static int emac_napi_tx_poll(struct napi_struct *napi_tx, int budget)
ns_to_ktime(tx_chn->tx_pace_timeout_ns),
HRTIMER_MODE_REL_PINNED);
} else {
- enable_irq(tx_chn->irq);
+ if (tx_chn->irq_disabled) {
+ tx_chn->irq_disabled = false;
+ enable_irq(tx_chn->irq);
+ }
}
}
@@ -246,6 +348,7 @@ static irqreturn_t prueth_tx_irq(int irq, void *dev_id)
{
struct prueth_tx_chn *tx_chn = dev_id;
+ tx_chn->irq_disabled = true;
disable_irq_nosync(irq);
napi_schedule(&tx_chn->napi_tx);
@@ -362,6 +465,29 @@ fail:
}
EXPORT_SYMBOL_GPL(prueth_init_tx_chns);
+static struct page_pool *prueth_create_page_pool(struct prueth_emac *emac,
+ struct device *dma_dev,
+ int size)
+{
+ struct page_pool_params pp_params = { 0 };
+ struct page_pool *pool;
+
+ pp_params.order = 0;
+ pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
+ pp_params.pool_size = size;
+ pp_params.nid = dev_to_node(emac->prueth->dev);
+ pp_params.dma_dir = DMA_BIDIRECTIONAL;
+ pp_params.dev = dma_dev;
+ pp_params.napi = &emac->napi_rx;
+ pp_params.max_len = PAGE_SIZE;
+
+ pool = page_pool_create(&pp_params);
+ if (IS_ERR(pool))
+ netdev_err(emac->ndev, "cannot create rx page pool\n");
+
+ return pool;
+}
+
int prueth_init_rx_chns(struct prueth_emac *emac,
struct prueth_rx_chn *rx_chn,
char *name, u32 max_rflows,
@@ -371,6 +497,7 @@ int prueth_init_rx_chns(struct prueth_emac *emac,
struct device *dev = emac->prueth->dev;
struct net_device *ndev = emac->ndev;
u32 fdqring_id, hdesc_size;
+ struct page_pool *pool;
int i, ret = 0, slice;
int flow_id_base;
@@ -413,6 +540,14 @@ int prueth_init_rx_chns(struct prueth_emac *emac,
goto fail;
}
+ pool = prueth_create_page_pool(emac, rx_chn->dma_dev, rx_chn->descs_num);
+ if (IS_ERR(pool)) {
+ ret = PTR_ERR(pool);
+ goto fail;
+ }
+
+ rx_chn->pg_pool = pool;
+
flow_id_base = k3_udma_glue_rx_get_flow_id_base(rx_chn->rx_chn);
if (emac->is_sr1 && !strcmp(name, "rxmgm")) {
emac->rx_mgm_flow_id_base = flow_id_base;
@@ -544,15 +679,15 @@ void emac_rx_timestamp(struct prueth_emac *emac,
* emac_xmit_xdp_frame - transmits an XDP frame
* @emac: emac device
* @xdpf: data to transmit
- * @page: page from page pool if already DMA mapped
* @q_idx: queue id
+ * @buff_type: Type of buffer to be transmitted
*
* Return: XDP state
*/
u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
struct xdp_frame *xdpf,
- struct page *page,
- unsigned int q_idx)
+ unsigned int q_idx,
+ enum prueth_tx_buff_type buff_type)
{
struct cppi5_host_desc_t *first_desc;
struct net_device *ndev = emac->ndev;
@@ -560,6 +695,7 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
struct prueth_tx_chn *tx_chn;
dma_addr_t desc_dma, buf_dma;
struct prueth_swdata *swdata;
+ struct page *page;
u32 *epib;
int ret;
@@ -576,7 +712,12 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
return ICSSG_XDP_CONSUMED; /* drop */
}
- if (page) { /* already DMA mapped by page_pool */
+ if (buff_type == PRUETH_TX_BUFF_TYPE_XDP_TX) { /* already DMA mapped by page_pool */
+ page = virt_to_head_page(xdpf->data);
+ if (unlikely(!page)) {
+ netdev_err(ndev, "xdp tx: failed to get page from xdpf\n");
+ goto drop_free_descs;
+ }
buf_dma = page_pool_get_dma_addr(page);
buf_dma += xdpf->headroom + sizeof(struct xdp_frame);
} else { /* Map the linear buffer */
@@ -631,13 +772,11 @@ EXPORT_SYMBOL_GPL(emac_xmit_xdp_frame);
* emac_run_xdp - run an XDP program
* @emac: emac device
* @xdp: XDP buffer containing the frame
- * @page: page with RX data if already DMA mapped
* @len: Rx descriptor packet length
*
* Return: XDP state
*/
-static u32 emac_run_xdp(struct prueth_emac *emac, struct xdp_buff *xdp,
- struct page *page, u32 *len)
+static u32 emac_run_xdp(struct prueth_emac *emac, struct xdp_buff *xdp, u32 *len)
{
struct net_device *ndev = emac->ndev;
struct netdev_queue *netif_txq;
@@ -664,7 +803,8 @@ static u32 emac_run_xdp(struct prueth_emac *emac, struct xdp_buff *xdp,
q_idx = cpu % emac->tx_ch_num;
netif_txq = netdev_get_tx_queue(ndev, q_idx);
__netif_tx_lock(netif_txq, cpu);
- result = emac_xmit_xdp_frame(emac, xdpf, page, q_idx);
+ result = emac_xmit_xdp_frame(emac, xdpf, q_idx,
+ PRUETH_TX_BUFF_TYPE_XDP_TX);
__netif_tx_unlock(netif_txq);
if (result == ICSSG_XDP_CONSUMED) {
ndev->stats.tx_dropped++;
@@ -689,11 +829,188 @@ drop:
fallthrough; /* handle aborts by dropping packet */
case XDP_DROP:
ndev->stats.rx_dropped++;
- page_pool_recycle_direct(emac->rx_chns.pg_pool, page);
return ICSSG_XDP_CONSUMED;
}
}
+static int prueth_dma_rx_push_mapped_zc(struct prueth_emac *emac,
+ struct prueth_rx_chn *rx_chn,
+ struct xdp_buff *xdp)
+{
+ struct net_device *ndev = emac->ndev;
+ struct cppi5_host_desc_t *desc_rx;
+ struct prueth_swdata *swdata;
+ dma_addr_t desc_dma;
+ dma_addr_t buf_dma;
+ int buf_len;
+
+ buf_dma = xsk_buff_xdp_get_dma(xdp);
+ desc_rx = k3_cppi_desc_pool_alloc(rx_chn->desc_pool);
+ if (!desc_rx) {
+ netdev_err(ndev, "rx push: failed to allocate descriptor\n");
+ return -ENOMEM;
+ }
+ desc_dma = k3_cppi_desc_pool_virt2dma(rx_chn->desc_pool, desc_rx);
+
+ cppi5_hdesc_init(desc_rx, CPPI5_INFO0_HDESC_EPIB_PRESENT,
+ PRUETH_NAV_PS_DATA_SIZE);
+ k3_udma_glue_rx_dma_to_cppi5_addr(rx_chn->rx_chn, &buf_dma);
+ buf_len = xsk_pool_get_rx_frame_size(rx_chn->xsk_pool);
+ cppi5_hdesc_attach_buf(desc_rx, buf_dma, buf_len, buf_dma, buf_len);
+ swdata = cppi5_hdesc_get_swdata(desc_rx);
+ swdata->type = PRUETH_SWDATA_XSK;
+ swdata->data.xdp = xdp;
+
+ return k3_udma_glue_push_rx_chn(rx_chn->rx_chn, PRUETH_RX_FLOW_DATA,
+ desc_rx, desc_dma);
+}
+
+static int prueth_rx_alloc_zc(struct prueth_emac *emac, int budget)
+{
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+ struct xdp_buff *xdp;
+ int i, ret;
+
+ for (i = 0; i < budget; i++) {
+ xdp = xsk_buff_alloc(rx_chn->xsk_pool);
+ if (!xdp)
+ break;
+
+ ret = prueth_dma_rx_push_mapped_zc(emac, rx_chn, xdp);
+ if (ret) {
+ netdev_err(emac->ndev, "rx alloc: failed to map descriptors to xdp buff\n");
+ xsk_buff_free(xdp);
+ break;
+ }
+ }
+
+ return i;
+}
+
+static void emac_dispatch_skb_zc(struct prueth_emac *emac, struct xdp_buff *xdp, u32 *psdata)
+{
+ unsigned int headroom = xdp->data - xdp->data_hard_start;
+ unsigned int pkt_len = xdp->data_end - xdp->data;
+ struct net_device *ndev = emac->ndev;
+ struct sk_buff *skb;
+
+ skb = napi_alloc_skb(&emac->napi_rx, xdp->data_end - xdp->data_hard_start);
+ if (unlikely(!skb)) {
+ ndev->stats.rx_dropped++;
+ return;
+ }
+
+ skb_reserve(skb, headroom);
+ skb_put(skb, pkt_len);
+ skb->dev = ndev;
+
+ /* RX HW timestamp */
+ if (emac->rx_ts_enabled)
+ emac_rx_timestamp(emac, skb, psdata);
+
+ if (emac->prueth->is_switch_mode)
+ skb->offload_fwd_mark = emac->offload_fwd_mark;
+ skb->protocol = eth_type_trans(skb, ndev);
+
+ skb_mark_for_recycle(skb);
+ napi_gro_receive(&emac->napi_rx, skb);
+ ndev->stats.rx_bytes += pkt_len;
+ ndev->stats.rx_packets++;
+}
+
+static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id,
+ int budget)
+{
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+ u32 buf_dma_len, pkt_len, port_id = 0;
+ struct net_device *ndev = emac->ndev;
+ struct cppi5_host_desc_t *desc_rx;
+ struct prueth_swdata *swdata;
+ dma_addr_t desc_dma, buf_dma;
+ struct xdp_buff *xdp;
+ int xdp_status = 0;
+ int count = 0;
+ u32 *psdata;
+ int ret;
+
+ while (count < budget) {
+ ret = k3_udma_glue_pop_rx_chn(rx_chn->rx_chn, flow_id, &desc_dma);
+ if (ret) {
+ if (ret != -ENODATA)
+ netdev_err(ndev, "rx pop: failed: %d\n", ret);
+ break;
+ }
+
+ if (cppi5_desc_is_tdcm(desc_dma)) {
+ complete(&emac->tdown_complete);
+ break;
+ }
+
+ desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma);
+ swdata = cppi5_hdesc_get_swdata(desc_rx);
+ if (swdata->type != PRUETH_SWDATA_XSK) {
+ netdev_err(ndev, "rx_pkt: invalid swdata->type %d\n", swdata->type);
+ k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
+ break;
+ }
+
+ xdp = swdata->data.xdp;
+ cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len);
+ k3_udma_glue_rx_cppi5_to_dma_addr(rx_chn->rx_chn, &buf_dma);
+ pkt_len = cppi5_hdesc_get_pktlen(desc_rx);
+ /* firmware adds 4 CRC bytes, strip them */
+ pkt_len -= 4;
+ cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL);
+ psdata = cppi5_hdesc_get_psdata(desc_rx);
+ k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
+ count++;
+ xsk_buff_set_size(xdp, pkt_len);
+ xsk_buff_dma_sync_for_cpu(xdp);
+
+ if (prueth_xdp_is_enabled(emac)) {
+ ret = emac_run_xdp(emac, xdp, &pkt_len);
+ switch (ret) {
+ case ICSSG_XDP_PASS:
+ /* prepare skb and send to n/w stack */
+ emac_dispatch_skb_zc(emac, xdp, psdata);
+ xsk_buff_free(xdp);
+ break;
+ case ICSSG_XDP_CONSUMED:
+ xsk_buff_free(xdp);
+ break;
+ case ICSSG_XDP_TX:
+ case ICSSG_XDP_REDIR:
+ xdp_status |= ret;
+ break;
+ }
+ } else {
+ /* prepare skb and send to n/w stack */
+ emac_dispatch_skb_zc(emac, xdp, psdata);
+ xsk_buff_free(xdp);
+ }
+ }
+
+ if (xdp_status & ICSSG_XDP_REDIR)
+ xdp_do_flush();
+
+ /* Allocate xsk buffers from the pool for the "count" number of
+ * packets processed in order to be able to receive more packets.
+ */
+ ret = prueth_rx_alloc_zc(emac, count);
+
+ if (xsk_uses_need_wakeup(rx_chn->xsk_pool)) {
+ /* If the user space doesn't provide enough buffers then it must
+ * explicitly wake up the kernel when new buffers are available
+ */
+ if (ret < count)
+ xsk_set_rx_need_wakeup(rx_chn->xsk_pool);
+ else
+ xsk_clear_rx_need_wakeup(rx_chn->xsk_pool);
+ }
+
+ return count;
+}
+
static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id, u32 *xdp_state)
{
struct prueth_rx_chn *rx_chn = &emac->rx_chns;
@@ -719,8 +1036,10 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id, u32 *xdp_state)
return ret;
}
- if (cppi5_desc_is_tdcm(desc_dma)) /* Teardown ? */
+ if (cppi5_desc_is_tdcm(desc_dma)) {
+ complete(&emac->tdown_complete);
return 0;
+ }
desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma);
swdata = cppi5_hdesc_get_swdata(desc_rx);
@@ -738,7 +1057,6 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id, u32 *xdp_state)
/* firmware adds 4 CRC bytes, strip them */
pkt_len -= 4;
cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL);
-
k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
/* if allocation fails we drop the packet but push the
@@ -752,11 +1070,11 @@ static int emac_rx_packet(struct prueth_emac *emac, u32 flow_id, u32 *xdp_state)
}
pa = page_address(page);
- if (emac->xdp_prog) {
+ if (prueth_xdp_is_enabled(emac)) {
xdp_init_buff(&xdp, PAGE_SIZE, &rx_chn->xdp_rxq);
xdp_prepare_buff(&xdp, pa, PRUETH_HEADROOM, pkt_len, false);
- *xdp_state = emac_run_xdp(emac, &xdp, page, &pkt_len);
+ *xdp_state = emac_run_xdp(emac, &xdp, &pkt_len);
if (*xdp_state != ICSSG_XDP_PASS)
goto requeue;
headroom = xdp.data - xdp.data_hard_start;
@@ -804,24 +1122,29 @@ requeue:
return ret;
}
-static void prueth_rx_cleanup(void *data, dma_addr_t desc_dma)
+void prueth_rx_cleanup(void *data, dma_addr_t desc_dma)
{
struct prueth_rx_chn *rx_chn = data;
struct cppi5_host_desc_t *desc_rx;
struct prueth_swdata *swdata;
struct page_pool *pool;
+ struct xdp_buff *xdp;
struct page *page;
pool = rx_chn->pg_pool;
desc_rx = k3_cppi_desc_pool_dma2virt(rx_chn->desc_pool, desc_dma);
swdata = cppi5_hdesc_get_swdata(desc_rx);
- if (swdata->type == PRUETH_SWDATA_PAGE) {
+ if (rx_chn->xsk_pool) {
+ xdp = swdata->data.xdp;
+ xsk_buff_free(xdp);
+ } else {
page = swdata->data.page;
page_pool_recycle_direct(pool, page);
}
k3_cppi_desc_pool_free(rx_chn->desc_pool, desc_rx);
}
+EXPORT_SYMBOL_GPL(prueth_rx_cleanup);
static int prueth_tx_ts_cookie_get(struct prueth_emac *emac)
{
@@ -1025,10 +1348,11 @@ drop_stop_q_busy:
}
EXPORT_SYMBOL_GPL(icssg_ndo_start_xmit);
-static void prueth_tx_cleanup(void *data, dma_addr_t desc_dma)
+void prueth_tx_cleanup(void *data, dma_addr_t desc_dma)
{
struct prueth_tx_chn *tx_chn = data;
struct cppi5_host_desc_t *desc_tx;
+ struct xsk_buff_pool *xsk_pool;
struct prueth_swdata *swdata;
struct xdp_frame *xdpf;
struct sk_buff *skb;
@@ -1045,17 +1369,23 @@ static void prueth_tx_cleanup(void *data, dma_addr_t desc_dma)
xdpf = swdata->data.xdpf;
xdp_return_frame(xdpf);
break;
+ case PRUETH_SWDATA_XSK:
+ xsk_pool = tx_chn->xsk_pool;
+ xsk_tx_completed(xsk_pool, 1);
+ break;
default:
break;
}
prueth_xmit_free(tx_chn, desc_tx);
}
+EXPORT_SYMBOL_GPL(prueth_tx_cleanup);
irqreturn_t prueth_rx_irq(int irq, void *dev_id)
{
struct prueth_emac *emac = dev_id;
+ emac->rx_chns.irq_disabled = true;
disable_irq_nosync(irq);
napi_schedule(&emac->napi_rx);
@@ -1083,6 +1413,7 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
PRUETH_RX_FLOW_DATA_SR1 : PRUETH_RX_FLOW_DATA;
int flow = emac->is_sr1 ?
PRUETH_MAX_RX_FLOWS_SR1 : PRUETH_MAX_RX_FLOWS;
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
int xdp_state_or = 0;
int num_rx = 0;
int cur_budget;
@@ -1090,14 +1421,18 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
int ret;
while (flow--) {
- cur_budget = budget - num_rx;
-
- while (cur_budget--) {
- ret = emac_rx_packet(emac, flow, &xdp_state);
- xdp_state_or |= xdp_state;
- if (ret)
- break;
- num_rx++;
+ if (rx_chn->xsk_pool) {
+ num_rx = emac_rx_packet_zc(emac, flow, budget);
+ } else {
+ cur_budget = budget - num_rx;
+
+ while (cur_budget--) {
+ ret = emac_rx_packet(emac, flow, &xdp_state);
+ xdp_state_or |= xdp_state;
+ if (ret)
+ break;
+ num_rx++;
+ }
}
if (num_rx >= budget)
@@ -1113,7 +1448,11 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
ns_to_ktime(emac->rx_pace_timeout_ns),
HRTIMER_MODE_REL_PINNED);
} else {
- enable_irq(emac->rx_chns.irq[rx_flow]);
+ if (emac->rx_chns.irq_disabled) {
+ /* re-enable the RX IRQ */
+ emac->rx_chns.irq_disabled = false;
+ enable_irq(emac->rx_chns.irq[rx_flow]);
+ }
}
}
@@ -1121,62 +1460,48 @@ int icssg_napi_rx_poll(struct napi_struct *napi_rx, int budget)
}
EXPORT_SYMBOL_GPL(icssg_napi_rx_poll);
-static struct page_pool *prueth_create_page_pool(struct prueth_emac *emac,
- struct device *dma_dev,
- int size)
-{
- struct page_pool_params pp_params = { 0 };
- struct page_pool *pool;
-
- pp_params.order = 0;
- pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV;
- pp_params.pool_size = size;
- pp_params.nid = dev_to_node(emac->prueth->dev);
- pp_params.dma_dir = DMA_BIDIRECTIONAL;
- pp_params.dev = dma_dev;
- pp_params.napi = &emac->napi_rx;
- pp_params.max_len = PAGE_SIZE;
-
- pool = page_pool_create(&pp_params);
- if (IS_ERR(pool))
- netdev_err(emac->ndev, "cannot create rx page pool\n");
-
- return pool;
-}
-
int prueth_prepare_rx_chan(struct prueth_emac *emac,
struct prueth_rx_chn *chn,
int buf_size)
{
- struct page_pool *pool;
struct page *page;
+ int desc_avail;
int i, ret;
- pool = prueth_create_page_pool(emac, chn->dma_dev, chn->descs_num);
- if (IS_ERR(pool))
- return PTR_ERR(pool);
-
- chn->pg_pool = pool;
+ desc_avail = k3_cppi_desc_pool_avail(chn->desc_pool);
+ if (desc_avail < chn->descs_num)
+ netdev_warn(emac->ndev,
+ "not enough RX descriptors available %d < %d\n",
+ desc_avail, chn->descs_num);
- for (i = 0; i < chn->descs_num; i++) {
- /* NOTE: we're not using memory efficiently here.
- * 1 full page (4KB?) used here instead of
- * PRUETH_MAX_PKT_SIZE (~1.5KB?)
+ if (chn->xsk_pool) {
+ /* get pages from xsk_pool and push to RX ring
+ * queue as much as possible
*/
- page = page_pool_dev_alloc_pages(pool);
- if (!page) {
- netdev_err(emac->ndev, "couldn't allocate rx page\n");
- ret = -ENOMEM;
+ ret = prueth_rx_alloc_zc(emac, desc_avail);
+ if (!ret)
goto recycle_alloc_pg;
- }
+ } else {
+ for (i = 0; i < desc_avail; i++) {
+ /* NOTE: we're not using memory efficiently here.
+ * 1 full page (4KB?) used here instead of
+ * PRUETH_MAX_PKT_SIZE (~1.5KB?)
+ */
+ page = page_pool_dev_alloc_pages(chn->pg_pool);
+ if (!page) {
+ netdev_err(emac->ndev, "couldn't allocate rx page\n");
+ ret = -ENOMEM;
+ goto recycle_alloc_pg;
+ }
- ret = prueth_dma_rx_push_mapped(emac, chn, page, buf_size);
- if (ret < 0) {
- netdev_err(emac->ndev,
- "cannot submit page for rx chan %s ret %d\n",
- chn->name, ret);
- page_pool_recycle_direct(pool, page);
- goto recycle_alloc_pg;
+ ret = prueth_dma_rx_push_mapped(emac, chn, page, buf_size);
+ if (ret < 0) {
+ netdev_err(emac->ndev,
+ "cannot submit page for rx chan %s ret %d\n",
+ chn->name, ret);
+ page_pool_recycle_direct(chn->pg_pool, page);
+ goto recycle_alloc_pg;
+ }
}
}
@@ -1223,15 +1548,13 @@ void icssg_ndo_tx_timeout(struct net_device *ndev, unsigned int txqueue)
}
EXPORT_SYMBOL_GPL(icssg_ndo_tx_timeout);
-static int emac_set_ts_config(struct net_device *ndev, struct ifreq *ifr)
+int icssg_ndo_set_ts_config(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct prueth_emac *emac = netdev_priv(ndev);
- struct hwtstamp_config config;
-
- if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
- return -EFAULT;
- switch (config.tx_type) {
+ switch (config->tx_type) {
case HWTSTAMP_TX_OFF:
emac->tx_ts_enabled = 0;
break;
@@ -1242,7 +1565,7 @@ static int emac_set_ts_config(struct net_device *ndev, struct ifreq *ifr)
return -ERANGE;
}
- switch (config.rx_filter) {
+ switch (config->rx_filter) {
case HWTSTAMP_FILTER_NONE:
emac->rx_ts_enabled = 0;
break;
@@ -1262,43 +1585,28 @@ static int emac_set_ts_config(struct net_device *ndev, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
case HWTSTAMP_FILTER_NTP_ALL:
emac->rx_ts_enabled = 1;
- config.rx_filter = HWTSTAMP_FILTER_ALL;
+ config->rx_filter = HWTSTAMP_FILTER_ALL;
break;
default:
return -ERANGE;
}
- return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
+ return 0;
}
+EXPORT_SYMBOL_GPL(icssg_ndo_set_ts_config);
-static int emac_get_ts_config(struct net_device *ndev, struct ifreq *ifr)
+int icssg_ndo_get_ts_config(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config)
{
struct prueth_emac *emac = netdev_priv(ndev);
- struct hwtstamp_config config;
-
- config.flags = 0;
- config.tx_type = emac->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
- config.rx_filter = emac->rx_ts_enabled ? HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
- return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
- -EFAULT : 0;
-}
+ config->flags = 0;
+ config->tx_type = emac->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+ config->rx_filter = emac->rx_ts_enabled ? HWTSTAMP_FILTER_ALL : HWTSTAMP_FILTER_NONE;
-int icssg_ndo_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd)
-{
- switch (cmd) {
- case SIOCGHWTSTAMP:
- return emac_get_ts_config(ndev, ifr);
- case SIOCSHWTSTAMP:
- return emac_set_ts_config(ndev, ifr);
- default:
- break;
- }
-
- return phy_do_ioctl(ndev, ifr, cmd);
+ return 0;
}
-EXPORT_SYMBOL_GPL(icssg_ndo_ioctl);
+EXPORT_SYMBOL_GPL(icssg_ndo_get_ts_config);
void icssg_ndo_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *stats)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_config.c b/drivers/net/ethernet/ti/icssg/icssg_config.c
index da53eb04b0a4..3f8237c17d09 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_config.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_config.c
@@ -66,6 +66,9 @@
#define FDB_GEN_CFG1 0x60
#define SMEM_VLAN_OFFSET 8
#define SMEM_VLAN_OFFSET_MASK GENMASK(25, 8)
+#define FDB_HASH_SIZE_MASK GENMASK(6, 3)
+#define FDB_HASH_SIZE_SHIFT 3
+#define FDB_HASH_SIZE 3
#define FDB_GEN_CFG2 0x64
#define FDB_VLAN_EN BIT(6)
@@ -463,6 +466,8 @@ void icssg_init_emac_mode(struct prueth *prueth)
/* Set VLAN TABLE address base */
regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK,
addr << SMEM_VLAN_OFFSET);
+ regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, FDB_HASH_SIZE_MASK,
+ FDB_HASH_SIZE << FDB_HASH_SIZE_SHIFT);
/* Set enable VLAN aware mode, and FDBs for all PRUs */
regmap_write(prueth->miig_rt, FDB_GEN_CFG2, (FDB_PRU0_EN | FDB_PRU1_EN | FDB_HOST_EN));
prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va +
@@ -484,6 +489,8 @@ void icssg_init_fw_offload_mode(struct prueth *prueth)
/* Set VLAN TABLE address base */
regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK,
addr << SMEM_VLAN_OFFSET);
+ regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, FDB_HASH_SIZE_MASK,
+ FDB_HASH_SIZE << FDB_HASH_SIZE_SHIFT);
/* Set enable VLAN aware mode, and FDBs for all PRUs */
regmap_write(prueth->miig_rt, FDB_GEN_CFG2, FDB_EN_ALL);
prueth->vlan_tbl = (struct prueth_vlan_tbl __force *)(prueth->shram.va +
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.c b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
index e42d0fdefee1..f65041662173 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.c
@@ -47,6 +47,9 @@
NETIF_F_HW_HSR_TAG_INS | \
NETIF_F_HW_HSR_TAG_RM)
+#define PRUETH_RX_DMA_ATTR (DMA_ATTR_SKIP_CPU_SYNC |\
+ DMA_ATTR_WEAK_ORDERING)
+
/* CTRLMMR_ICSSG_RGMII_CTRL register bits */
#define ICSSG_CTRL_RGMII_ID_MODE BIT(24)
@@ -392,7 +395,11 @@ static enum hrtimer_restart emac_rx_timer_callback(struct hrtimer *timer)
container_of(timer, struct prueth_emac, rx_hrtimer);
int rx_flow = PRUETH_RX_FLOW_DATA;
- enable_irq(emac->rx_chns.irq[rx_flow]);
+ if (emac->rx_chns.irq_disabled) {
+ /* re-enable the RX IRQ */
+ emac->rx_chns.irq_disabled = false;
+ enable_irq(emac->rx_chns.irq[rx_flow]);
+ }
return HRTIMER_NORESTART;
}
@@ -566,31 +573,41 @@ const struct icss_iep_clockops prueth_iep_clockops = {
.perout_enable = prueth_perout_enable,
};
+static void prueth_destroy_xdp_rxqs(struct prueth_emac *emac)
+{
+ struct xdp_rxq_info *rxq = &emac->rx_chns.xdp_rxq;
+
+ if (xdp_rxq_info_is_reg(rxq))
+ xdp_rxq_info_unreg(rxq);
+}
+
static int prueth_create_xdp_rxqs(struct prueth_emac *emac)
{
struct xdp_rxq_info *rxq = &emac->rx_chns.xdp_rxq;
struct page_pool *pool = emac->rx_chns.pg_pool;
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
int ret;
ret = xdp_rxq_info_reg(rxq, emac->ndev, 0, emac->napi_rx.napi_id);
if (ret)
return ret;
- ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_PAGE_POOL, pool);
- if (ret)
- xdp_rxq_info_unreg(rxq);
-
- return ret;
-}
-
-static void prueth_destroy_xdp_rxqs(struct prueth_emac *emac)
-{
- struct xdp_rxq_info *rxq = &emac->rx_chns.xdp_rxq;
+ if (rx_chn->xsk_pool) {
+ ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_XSK_BUFF_POOL, NULL);
+ if (ret)
+ goto xdp_unreg;
+ xsk_pool_set_rxq_info(rx_chn->xsk_pool, rxq);
+ } else {
+ ret = xdp_rxq_info_reg_mem_model(rxq, MEM_TYPE_PAGE_POOL, pool);
+ if (ret)
+ goto xdp_unreg;
+ }
- if (!xdp_rxq_info_is_reg(rxq))
- return;
+ return 0;
- xdp_rxq_info_unreg(rxq);
+xdp_unreg:
+ prueth_destroy_xdp_rxqs(emac);
+ return ret;
}
static int icssg_prueth_add_mcast(struct net_device *ndev, const u8 *addr)
@@ -735,6 +752,128 @@ static int icssg_update_vlan_mcast(struct net_device *vdev, int vid,
return 0;
}
+static void prueth_set_xsk_pool(struct prueth_emac *emac, u16 queue_id)
+{
+ struct prueth_tx_chn *tx_chn = &emac->tx_chns[queue_id];
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+
+ if (emac->xsk_qid != queue_id) {
+ rx_chn->xsk_pool = NULL;
+ tx_chn->xsk_pool = NULL;
+ } else {
+ rx_chn->xsk_pool = xsk_get_pool_from_qid(emac->ndev, queue_id);
+ tx_chn->xsk_pool = xsk_get_pool_from_qid(emac->ndev, queue_id);
+ }
+}
+
+static void prueth_destroy_txq(struct prueth_emac *emac)
+{
+ int ret, i;
+
+ atomic_set(&emac->tdown_cnt, emac->tx_ch_num);
+ /* ensure new tdown_cnt value is visible */
+ smp_mb__after_atomic();
+ /* tear down and disable UDMA channels */
+ reinit_completion(&emac->tdown_complete);
+ for (i = 0; i < emac->tx_ch_num; i++)
+ k3_udma_glue_tdown_tx_chn(emac->tx_chns[i].tx_chn, false);
+
+ ret = wait_for_completion_timeout(&emac->tdown_complete,
+ msecs_to_jiffies(1000));
+ if (!ret)
+ netdev_err(emac->ndev, "tx teardown timeout\n");
+
+ for (i = 0; i < emac->tx_ch_num; i++) {
+ napi_disable(&emac->tx_chns[i].napi_tx);
+ hrtimer_cancel(&emac->tx_chns[i].tx_hrtimer);
+ k3_udma_glue_reset_tx_chn(emac->tx_chns[i].tx_chn,
+ &emac->tx_chns[i],
+ prueth_tx_cleanup);
+ k3_udma_glue_disable_tx_chn(emac->tx_chns[i].tx_chn);
+ }
+}
+
+static void prueth_destroy_rxq(struct prueth_emac *emac)
+{
+ int i, ret;
+
+ /* tear down and disable UDMA channels */
+ reinit_completion(&emac->tdown_complete);
+ k3_udma_glue_tdown_rx_chn(emac->rx_chns.rx_chn, true);
+
+ /* When RX DMA Channel Teardown is initiated, it will result in an
+ * interrupt and a Teardown Completion Marker (TDCM) is queued into
+ * the RX Completion queue. Acknowledging the interrupt involves
+ * popping the TDCM descriptor from the RX Completion queue via the
+ * RX NAPI Handler. To avoid timing out when waiting for the TDCM to
+ * be popped, schedule the RX NAPI handler to run immediately.
+ */
+ if (!napi_if_scheduled_mark_missed(&emac->napi_rx)) {
+ if (napi_schedule_prep(&emac->napi_rx))
+ __napi_schedule(&emac->napi_rx);
+ }
+
+ ret = wait_for_completion_timeout(&emac->tdown_complete,
+ msecs_to_jiffies(1000));
+ if (!ret)
+ netdev_err(emac->ndev, "rx teardown timeout\n");
+
+ for (i = 0; i < PRUETH_MAX_RX_FLOWS; i++) {
+ napi_disable(&emac->napi_rx);
+ hrtimer_cancel(&emac->rx_hrtimer);
+ k3_udma_glue_reset_rx_chn(emac->rx_chns.rx_chn, i,
+ &emac->rx_chns,
+ prueth_rx_cleanup);
+ }
+
+ prueth_destroy_xdp_rxqs(emac);
+ k3_udma_glue_disable_rx_chn(emac->rx_chns.rx_chn);
+}
+
+static int prueth_create_txq(struct prueth_emac *emac)
+{
+ int ret, i;
+
+ for (i = 0; i < emac->tx_ch_num; i++) {
+ ret = k3_udma_glue_enable_tx_chn(emac->tx_chns[i].tx_chn);
+ if (ret)
+ goto reset_tx_chan;
+ napi_enable(&emac->tx_chns[i].napi_tx);
+ }
+ return 0;
+
+reset_tx_chan:
+ /* Since interface is not yet up, there is wouldn't be
+ * any SKB for completion. So set false to free_skb
+ */
+ prueth_reset_tx_chan(emac, i, false);
+ return ret;
+}
+
+static int prueth_create_rxq(struct prueth_emac *emac)
+{
+ int ret;
+
+ ret = prueth_prepare_rx_chan(emac, &emac->rx_chns, PRUETH_MAX_PKT_SIZE);
+ if (ret)
+ return ret;
+
+ ret = k3_udma_glue_enable_rx_chn(emac->rx_chns.rx_chn);
+ if (ret)
+ goto reset_rx_chn;
+
+ ret = prueth_create_xdp_rxqs(emac);
+ if (ret)
+ goto reset_rx_chn;
+
+ napi_enable(&emac->napi_rx);
+ return 0;
+
+reset_rx_chn:
+ prueth_reset_rx_chan(&emac->rx_chns, PRUETH_MAX_RX_FLOWS, false);
+ return ret;
+}
+
/**
* emac_ndo_open - EMAC device open
* @ndev: network adapter device
@@ -746,7 +885,7 @@ static int icssg_update_vlan_mcast(struct net_device *vdev, int vid,
static int emac_ndo_open(struct net_device *ndev)
{
struct prueth_emac *emac = netdev_priv(ndev);
- int ret, i, num_data_chn = emac->tx_ch_num;
+ int ret, num_data_chn = emac->tx_ch_num;
struct icssg_flow_cfg __iomem *flow_cfg;
struct prueth *prueth = emac->prueth;
int slice = prueth_emac_slice(emac);
@@ -767,6 +906,7 @@ static int emac_ndo_open(struct net_device *ndev)
return ret;
}
+ emac->xsk_qid = -EINVAL;
init_completion(&emac->cmd_complete);
ret = prueth_init_tx_chns(emac);
if (ret) {
@@ -819,28 +959,13 @@ static int emac_ndo_open(struct net_device *ndev)
goto stop;
/* Prepare RX */
- ret = prueth_prepare_rx_chan(emac, &emac->rx_chns, PRUETH_MAX_PKT_SIZE);
+ ret = prueth_create_rxq(emac);
if (ret)
goto free_tx_ts_irq;
- ret = prueth_create_xdp_rxqs(emac);
- if (ret)
- goto reset_rx_chn;
-
- ret = k3_udma_glue_enable_rx_chn(emac->rx_chns.rx_chn);
+ ret = prueth_create_txq(emac);
if (ret)
- goto destroy_xdp_rxqs;
-
- for (i = 0; i < emac->tx_ch_num; i++) {
- ret = k3_udma_glue_enable_tx_chn(emac->tx_chns[i].tx_chn);
- if (ret)
- goto reset_tx_chan;
- }
-
- /* Enable NAPI in Tx and Rx direction */
- for (i = 0; i < emac->tx_ch_num; i++)
- napi_enable(&emac->tx_chns[i].napi_tx);
- napi_enable(&emac->napi_rx);
+ goto destroy_rxq;
/* start PHY */
phy_start(ndev->phydev);
@@ -851,15 +976,8 @@ static int emac_ndo_open(struct net_device *ndev)
return 0;
-reset_tx_chan:
- /* Since interface is not yet up, there is wouldn't be
- * any SKB for completion. So set false to free_skb
- */
- prueth_reset_tx_chan(emac, i, false);
-destroy_xdp_rxqs:
- prueth_destroy_xdp_rxqs(emac);
-reset_rx_chn:
- prueth_reset_rx_chan(&emac->rx_chns, max_rx_flows, false);
+destroy_rxq:
+ prueth_destroy_rxq(emac);
free_tx_ts_irq:
free_irq(emac->tx_ts_irq, emac);
stop:
@@ -889,9 +1007,6 @@ static int emac_ndo_stop(struct net_device *ndev)
{
struct prueth_emac *emac = netdev_priv(ndev);
struct prueth *prueth = emac->prueth;
- int rx_flow = PRUETH_RX_FLOW_DATA;
- int max_rx_flows;
- int ret, i;
/* inform the upper layers. */
netif_tx_stop_all_queues(ndev);
@@ -905,32 +1020,8 @@ static int emac_ndo_stop(struct net_device *ndev)
else
__dev_mc_unsync(ndev, icssg_prueth_del_mcast);
- atomic_set(&emac->tdown_cnt, emac->tx_ch_num);
- /* ensure new tdown_cnt value is visible */
- smp_mb__after_atomic();
- /* tear down and disable UDMA channels */
- reinit_completion(&emac->tdown_complete);
- for (i = 0; i < emac->tx_ch_num; i++)
- k3_udma_glue_tdown_tx_chn(emac->tx_chns[i].tx_chn, false);
-
- ret = wait_for_completion_timeout(&emac->tdown_complete,
- msecs_to_jiffies(1000));
- if (!ret)
- netdev_err(ndev, "tx teardown timeout\n");
-
- prueth_reset_tx_chan(emac, emac->tx_ch_num, true);
- for (i = 0; i < emac->tx_ch_num; i++) {
- napi_disable(&emac->tx_chns[i].napi_tx);
- hrtimer_cancel(&emac->tx_chns[i].tx_hrtimer);
- }
-
- max_rx_flows = PRUETH_MAX_RX_FLOWS;
- k3_udma_glue_tdown_rx_chn(emac->rx_chns.rx_chn, true);
-
- prueth_reset_rx_chan(&emac->rx_chns, max_rx_flows, true);
- prueth_destroy_xdp_rxqs(emac);
- napi_disable(&emac->napi_rx);
- hrtimer_cancel(&emac->rx_hrtimer);
+ prueth_destroy_txq(emac);
+ prueth_destroy_rxq(emac);
cancel_work_sync(&emac->rx_mode_work);
@@ -943,10 +1034,10 @@ static int emac_ndo_stop(struct net_device *ndev)
free_irq(emac->tx_ts_irq, emac);
- free_irq(emac->rx_chns.irq[rx_flow], emac);
+ free_irq(emac->rx_chns.irq[PRUETH_RX_FLOW_DATA], emac);
prueth_ndev_del_tx_napi(emac, emac->tx_ch_num);
- prueth_cleanup_rx_chns(emac, &emac->rx_chns, max_rx_flows);
+ prueth_cleanup_rx_chns(emac, &emac->rx_chns, PRUETH_MAX_RX_FLOWS);
prueth_cleanup_tx_chns(emac);
prueth->emacs_initialized--;
@@ -1108,7 +1199,8 @@ static int emac_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frame
__netif_tx_lock(netif_txq, cpu);
for (i = 0; i < n; i++) {
xdpf = frames[i];
- err = emac_xmit_xdp_frame(emac, xdpf, NULL, q_idx);
+ err = emac_xmit_xdp_frame(emac, xdpf, q_idx,
+ PRUETH_TX_BUFF_TYPE_XDP_NDO);
if (err != ICSSG_XDP_TX) {
ndev->stats.tx_dropped++;
break;
@@ -1141,6 +1233,109 @@ static int emac_xdp_setup(struct prueth_emac *emac, struct netdev_bpf *bpf)
return 0;
}
+static int prueth_xsk_pool_enable(struct prueth_emac *emac,
+ struct xsk_buff_pool *pool, u16 queue_id)
+{
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+ u32 frame_size;
+ int ret;
+
+ if (queue_id >= PRUETH_MAX_RX_FLOWS ||
+ queue_id >= emac->tx_ch_num) {
+ netdev_err(emac->ndev, "Invalid XSK queue ID %d\n", queue_id);
+ return -EINVAL;
+ }
+
+ frame_size = xsk_pool_get_rx_frame_size(pool);
+ if (frame_size < PRUETH_MAX_PKT_SIZE)
+ return -EOPNOTSUPP;
+
+ ret = xsk_pool_dma_map(pool, rx_chn->dma_dev, PRUETH_RX_DMA_ATTR);
+ if (ret) {
+ netdev_err(emac->ndev, "Failed to map XSK pool: %d\n", ret);
+ return ret;
+ }
+
+ if (netif_running(emac->ndev)) {
+ /* stop packets from wire for graceful teardown */
+ ret = icssg_set_port_state(emac, ICSSG_EMAC_PORT_DISABLE);
+ if (ret)
+ return ret;
+ prueth_destroy_rxq(emac);
+ }
+
+ emac->xsk_qid = queue_id;
+ prueth_set_xsk_pool(emac, queue_id);
+
+ if (netif_running(emac->ndev)) {
+ ret = prueth_create_rxq(emac);
+ if (ret) {
+ netdev_err(emac->ndev, "Failed to create RX queue: %d\n", ret);
+ return ret;
+ }
+ ret = icssg_set_port_state(emac, ICSSG_EMAC_PORT_FORWARD);
+ if (ret) {
+ prueth_destroy_rxq(emac);
+ return ret;
+ }
+ ret = prueth_xsk_wakeup(emac->ndev, queue_id, XDP_WAKEUP_RX);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int prueth_xsk_pool_disable(struct prueth_emac *emac, u16 queue_id)
+{
+ struct xsk_buff_pool *pool;
+ int ret;
+
+ if (queue_id >= PRUETH_MAX_RX_FLOWS ||
+ queue_id >= emac->tx_ch_num) {
+ netdev_err(emac->ndev, "Invalid XSK queue ID %d\n", queue_id);
+ return -EINVAL;
+ }
+
+ if (emac->xsk_qid != queue_id) {
+ netdev_err(emac->ndev, "XSK queue ID %d not registered\n", queue_id);
+ return -EINVAL;
+ }
+
+ pool = xsk_get_pool_from_qid(emac->ndev, queue_id);
+ if (!pool) {
+ netdev_err(emac->ndev, "No XSK pool registered for queue %d\n", queue_id);
+ return -EINVAL;
+ }
+
+ if (netif_running(emac->ndev)) {
+ /* stop packets from wire for graceful teardown */
+ ret = icssg_set_port_state(emac, ICSSG_EMAC_PORT_DISABLE);
+ if (ret)
+ return ret;
+ prueth_destroy_rxq(emac);
+ }
+
+ xsk_pool_dma_unmap(pool, PRUETH_RX_DMA_ATTR);
+ emac->xsk_qid = -EINVAL;
+ prueth_set_xsk_pool(emac, queue_id);
+
+ if (netif_running(emac->ndev)) {
+ ret = prueth_create_rxq(emac);
+ if (ret) {
+ netdev_err(emac->ndev, "Failed to create RX queue: %d\n", ret);
+ return ret;
+ }
+ ret = icssg_set_port_state(emac, ICSSG_EMAC_PORT_FORWARD);
+ if (ret) {
+ prueth_destroy_rxq(emac);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/**
* emac_ndo_bpf - implements ndo_bpf for icssg_prueth
* @ndev: network adapter device
@@ -1155,11 +1350,58 @@ static int emac_ndo_bpf(struct net_device *ndev, struct netdev_bpf *bpf)
switch (bpf->command) {
case XDP_SETUP_PROG:
return emac_xdp_setup(emac, bpf);
+ case XDP_SETUP_XSK_POOL:
+ return bpf->xsk.pool ?
+ prueth_xsk_pool_enable(emac, bpf->xsk.pool, bpf->xsk.queue_id) :
+ prueth_xsk_pool_disable(emac, bpf->xsk.queue_id);
default:
return -EINVAL;
}
}
+int prueth_xsk_wakeup(struct net_device *ndev, u32 qid, u32 flags)
+{
+ struct prueth_emac *emac = netdev_priv(ndev);
+ struct prueth_tx_chn *tx_chn = &emac->tx_chns[qid];
+ struct prueth_rx_chn *rx_chn = &emac->rx_chns;
+
+ if (emac->xsk_qid != qid) {
+ netdev_err(ndev, "XSK queue %d not registered\n", qid);
+ return -EINVAL;
+ }
+
+ if (qid >= PRUETH_MAX_RX_FLOWS || qid >= emac->tx_ch_num) {
+ netdev_err(ndev, "Invalid XSK queue ID %d\n", qid);
+ return -EINVAL;
+ }
+
+ if (!tx_chn->xsk_pool) {
+ netdev_err(ndev, "XSK pool not registered for queue %d\n", qid);
+ return -EINVAL;
+ }
+
+ if (!rx_chn->xsk_pool) {
+ netdev_err(ndev, "XSK pool not registered for RX queue %d\n", qid);
+ return -EINVAL;
+ }
+
+ if (flags & XDP_WAKEUP_TX) {
+ if (!napi_if_scheduled_mark_missed(&tx_chn->napi_tx)) {
+ if (likely(napi_schedule_prep(&tx_chn->napi_tx)))
+ __napi_schedule(&tx_chn->napi_tx);
+ }
+ }
+
+ if (flags & XDP_WAKEUP_RX) {
+ if (!napi_if_scheduled_mark_missed(&emac->napi_rx)) {
+ if (likely(napi_schedule_prep(&emac->napi_rx)))
+ __napi_schedule(&emac->napi_rx);
+ }
+ }
+
+ return 0;
+}
+
static const struct net_device_ops emac_netdev_ops = {
.ndo_open = emac_ndo_open,
.ndo_stop = emac_ndo_stop,
@@ -1168,7 +1410,7 @@ static const struct net_device_ops emac_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = icssg_ndo_tx_timeout,
.ndo_set_rx_mode = emac_ndo_set_rx_mode,
- .ndo_eth_ioctl = icssg_ndo_ioctl,
+ .ndo_eth_ioctl = phy_do_ioctl,
.ndo_get_stats64 = icssg_ndo_get_stats64,
.ndo_get_phys_port_name = icssg_ndo_get_phys_port_name,
.ndo_fix_features = emac_ndo_fix_features,
@@ -1176,6 +1418,9 @@ static const struct net_device_ops emac_netdev_ops = {
.ndo_vlan_rx_kill_vid = emac_ndo_vlan_rx_del_vid,
.ndo_bpf = emac_ndo_bpf,
.ndo_xdp_xmit = emac_xdp_xmit,
+ .ndo_hwtstamp_get = icssg_ndo_get_ts_config,
+ .ndo_hwtstamp_set = icssg_ndo_set_ts_config,
+ .ndo_xsk_wakeup = prueth_xsk_wakeup,
};
static int prueth_netdev_init(struct prueth *prueth,
@@ -1248,8 +1493,7 @@ static int prueth_netdev_init(struct prueth *prueth,
} else if (of_phy_is_fixed_link(eth_node)) {
ret = of_phy_register_fixed_link(eth_node);
if (ret) {
- ret = dev_err_probe(prueth->dev, ret,
- "failed to register fixed-link phy\n");
+ dev_err_probe(prueth->dev, ret, "failed to register fixed-link phy\n");
goto free;
}
@@ -1310,7 +1554,8 @@ static int prueth_netdev_init(struct prueth *prueth,
xdp_set_features_flag(ndev,
NETDEV_XDP_ACT_BASIC |
NETDEV_XDP_ACT_REDIRECT |
- NETDEV_XDP_ACT_NDO_XMIT);
+ NETDEV_XDP_ACT_NDO_XMIT |
+ NETDEV_XDP_ACT_XSK_ZEROCOPY);
netif_napi_add(ndev, &emac->napi_rx, icssg_napi_rx_poll);
hrtimer_setup(&emac->rx_hrtimer, &emac_rx_timer_callback, CLOCK_MONOTONIC,
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth.h b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
index ca8a22a4a5da..10eadd356650 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth.h
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth.h
@@ -38,6 +38,8 @@
#include <net/devlink.h>
#include <net/xdp.h>
#include <net/page_pool/helpers.h>
+#include <net/xsk_buff_pool.h>
+#include <net/xdp_sock_drv.h>
#include "icssg_config.h"
#include "icss_iep.h"
@@ -126,6 +128,8 @@ struct prueth_tx_chn {
char name[32];
struct hrtimer tx_hrtimer;
unsigned long tx_pace_timeout_ns;
+ struct xsk_buff_pool *xsk_pool;
+ bool irq_disabled;
};
struct prueth_rx_chn {
@@ -138,6 +142,8 @@ struct prueth_rx_chn {
char name[32];
struct page_pool *pg_pool;
struct xdp_rxq_info xdp_rxq;
+ struct xsk_buff_pool *xsk_pool;
+ bool irq_disabled;
};
enum prueth_swdata_type {
@@ -146,6 +152,12 @@ enum prueth_swdata_type {
PRUETH_SWDATA_PAGE,
PRUETH_SWDATA_CMD,
PRUETH_SWDATA_XDPF,
+ PRUETH_SWDATA_XSK,
+};
+
+enum prueth_tx_buff_type {
+ PRUETH_TX_BUFF_TYPE_XDP_TX,
+ PRUETH_TX_BUFF_TYPE_XDP_NDO,
};
struct prueth_swdata {
@@ -155,6 +167,7 @@ struct prueth_swdata {
struct page *page;
u32 cmd;
struct xdp_frame *xdpf;
+ struct xdp_buff *xdp;
} data;
};
@@ -241,6 +254,7 @@ struct prueth_emac {
struct netdev_hw_addr_list vlan_mcast_list[MAX_VLAN_ID];
struct bpf_prog *xdp_prog;
struct xdp_attachment_info xdpi;
+ int xsk_qid;
};
/* The buf includes headroom compatible with both skb and xdpf */
@@ -479,7 +493,11 @@ void prueth_reset_tx_chan(struct prueth_emac *emac, int ch_num,
void prueth_reset_rx_chan(struct prueth_rx_chn *chn,
int num_flows, bool disable);
void icssg_ndo_tx_timeout(struct net_device *ndev, unsigned int txqueue);
-int icssg_ndo_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd);
+int icssg_ndo_get_ts_config(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config);
+int icssg_ndo_set_ts_config(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack);
void icssg_ndo_get_stats64(struct net_device *ndev,
struct rtnl_link_stats64 *stats);
int icssg_ndo_get_phys_port_name(struct net_device *ndev, char *name,
@@ -495,7 +513,14 @@ void prueth_put_cores(struct prueth *prueth, int slice);
u64 icssg_ts_to_ns(u32 hi_sw, u32 hi, u32 lo, u32 cycle_time_ns);
u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
struct xdp_frame *xdpf,
- struct page *page,
- unsigned int q_idx);
+ unsigned int q_idx,
+ enum prueth_tx_buff_type buff_type);
+void prueth_rx_cleanup(void *data, dma_addr_t desc_dma);
+void prueth_tx_cleanup(void *data, dma_addr_t desc_dma);
+int prueth_xsk_wakeup(struct net_device *ndev, u32 qid, u32 flags);
+static inline bool prueth_xdp_is_enabled(struct prueth_emac *emac)
+{
+ return !!READ_ONCE(emac->xdp_prog);
+}
#endif /* __NET_TI_ICSSG_PRUETH_H */
diff --git a/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c b/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c
index 5e225310c9de..7bb4f0d850cc 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c
@@ -747,9 +747,11 @@ static const struct net_device_ops emac_netdev_ops = {
.ndo_validate_addr = eth_validate_addr,
.ndo_tx_timeout = icssg_ndo_tx_timeout,
.ndo_set_rx_mode = emac_ndo_set_rx_mode_sr1,
- .ndo_eth_ioctl = icssg_ndo_ioctl,
+ .ndo_eth_ioctl = phy_do_ioctl,
.ndo_get_stats64 = icssg_ndo_get_stats64,
.ndo_get_phys_port_name = icssg_ndo_get_phys_port_name,
+ .ndo_hwtstamp_get = icssg_ndo_get_ts_config,
+ .ndo_hwtstamp_set = icssg_ndo_set_ts_config,
};
static int prueth_netdev_init(struct prueth *prueth,
@@ -816,8 +818,7 @@ static int prueth_netdev_init(struct prueth *prueth,
} else if (of_phy_is_fixed_link(eth_node)) {
ret = of_phy_register_fixed_link(eth_node);
if (ret) {
- ret = dev_err_probe(prueth->dev, ret,
- "failed to register fixed-link phy\n");
+ dev_err_probe(prueth->dev, ret, "failed to register fixed-link phy\n");
goto free;
}
diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h
index 7007eb8bed36..b9cbd3b4a8a2 100644
--- a/drivers/net/ethernet/ti/netcp.h
+++ b/drivers/net/ethernet/ti/netcp.h
@@ -207,6 +207,11 @@ struct netcp_module {
int (*del_vid)(void *intf_priv, int vid);
int (*ioctl)(void *intf_priv, struct ifreq *req, int cmd);
int (*set_rx_mode)(void *intf_priv, bool promisc);
+ int (*hwtstamp_get)(void *intf_priv,
+ struct kernel_hwtstamp_config *cfg);
+ int (*hwtstamp_set)(void *intf_priv,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack);
/* used internally */
struct list_head module_list;
diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c
index 857820657bac..5ed1c46bbcb1 100644
--- a/drivers/net/ethernet/ti/netcp_core.c
+++ b/drivers/net/ethernet/ti/netcp_core.c
@@ -1338,10 +1338,10 @@ int netcp_txpipe_open(struct netcp_tx_pipe *tx_pipe)
tx_pipe->dma_channel = knav_dma_open_channel(dev,
tx_pipe->dma_chan_name, &config);
- if (IS_ERR(tx_pipe->dma_channel)) {
+ if (!tx_pipe->dma_channel) {
dev_err(dev, "failed opening tx chan(%s)\n",
tx_pipe->dma_chan_name);
- ret = PTR_ERR(tx_pipe->dma_channel);
+ ret = -EINVAL;
goto err;
}
@@ -1359,7 +1359,7 @@ int netcp_txpipe_open(struct netcp_tx_pipe *tx_pipe)
return 0;
err:
- if (!IS_ERR_OR_NULL(tx_pipe->dma_channel))
+ if (tx_pipe->dma_channel)
knav_dma_close_channel(tx_pipe->dma_channel);
tx_pipe->dma_channel = NULL;
return ret;
@@ -1678,10 +1678,10 @@ static int netcp_setup_navigator_resources(struct net_device *ndev)
netcp->rx_channel = knav_dma_open_channel(netcp->netcp_device->device,
netcp->dma_chan_name, &config);
- if (IS_ERR(netcp->rx_channel)) {
+ if (!netcp->rx_channel) {
dev_err(netcp->ndev_dev, "failed opening rx chan(%s\n",
netcp->dma_chan_name);
- ret = PTR_ERR(netcp->rx_channel);
+ ret = -EINVAL;
goto fail;
}
@@ -1781,6 +1781,62 @@ static int netcp_ndo_stop(struct net_device *ndev)
return 0;
}
+static int netcp_ndo_hwtstamp_get(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config)
+{
+ struct netcp_intf *netcp = netdev_priv(ndev);
+ struct netcp_intf_modpriv *intf_modpriv;
+ struct netcp_module *module;
+ int err = -EOPNOTSUPP;
+
+ if (!netif_running(ndev))
+ return -EINVAL;
+
+ for_each_module(netcp, intf_modpriv) {
+ module = intf_modpriv->netcp_module;
+ if (!module->hwtstamp_get)
+ continue;
+
+ err = module->hwtstamp_get(intf_modpriv->module_priv, config);
+ break;
+ }
+
+ return err;
+}
+
+static int netcp_ndo_hwtstamp_set(struct net_device *ndev,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
+{
+ struct netcp_intf *netcp = netdev_priv(ndev);
+ struct netcp_intf_modpriv *intf_modpriv;
+ struct netcp_module *module;
+ int ret = -1, err = -EOPNOTSUPP;
+
+ if (!netif_running(ndev))
+ return -EINVAL;
+
+ for_each_module(netcp, intf_modpriv) {
+ module = intf_modpriv->netcp_module;
+ if (!module->hwtstamp_set)
+ continue;
+
+ err = module->hwtstamp_set(intf_modpriv->module_priv, config,
+ extack);
+ if ((err < 0) && (err != -EOPNOTSUPP)) {
+ NL_SET_ERR_MSG_WEAK_MOD(extack,
+ "At least one module failed to setup HW timestamps");
+ ret = err;
+ goto out;
+ }
+ if (err == 0)
+ ret = err;
+ }
+
+out:
+ return (ret == 0) ? 0 : err;
+}
+
static int netcp_ndo_ioctl(struct net_device *ndev,
struct ifreq *req, int cmd)
{
@@ -1952,6 +2008,8 @@ static const struct net_device_ops netcp_netdev_ops = {
.ndo_tx_timeout = netcp_ndo_tx_timeout,
.ndo_select_queue = dev_pick_tx_zero,
.ndo_setup_tc = netcp_setup_tc,
+ .ndo_hwtstamp_get = netcp_ndo_hwtstamp_get,
+ .ndo_hwtstamp_set = netcp_ndo_hwtstamp_set,
};
static int netcp_create_interface(struct netcp_device *netcp_device,
diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c
index 55a1a96cd834..8f46e9be76b1 100644
--- a/drivers/net/ethernet/ti/netcp_ethss.c
+++ b/drivers/net/ethernet/ti/netcp_ethss.c
@@ -2591,20 +2591,26 @@ static int gbe_rxtstamp(struct gbe_intf *gbe_intf, struct netcp_packet *p_info)
return 0;
}
-static int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *ifr)
+static int gbe_hwtstamp_get(void *intf_priv, struct kernel_hwtstamp_config *cfg)
{
- struct gbe_priv *gbe_dev = gbe_intf->gbe_dev;
- struct cpts *cpts = gbe_dev->cpts;
- struct hwtstamp_config cfg;
+ struct gbe_intf *gbe_intf = intf_priv;
+ struct gbe_priv *gbe_dev;
+ struct phy_device *phy;
+
+ gbe_dev = gbe_intf->gbe_dev;
- if (!cpts)
+ if (!gbe_dev->cpts)
+ return -EOPNOTSUPP;
+
+ phy = gbe_intf->slave->phy;
+ if (phy_has_hwtstamp(phy))
return -EOPNOTSUPP;
- cfg.flags = 0;
- cfg.tx_type = gbe_dev->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
- cfg.rx_filter = gbe_dev->rx_ts_enabled;
+ cfg->flags = 0;
+ cfg->tx_type = gbe_dev->tx_ts_enabled ? HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+ cfg->rx_filter = gbe_dev->rx_ts_enabled;
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+ return 0;
}
static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
@@ -2637,19 +2643,23 @@ static void gbe_hwtstamp(struct gbe_intf *gbe_intf)
writel(ctl, GBE_REG_ADDR(slave, port_regs, ts_ctl_ltype2));
}
-static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
+static int gbe_hwtstamp_set(void *intf_priv, struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
- struct gbe_priv *gbe_dev = gbe_intf->gbe_dev;
- struct cpts *cpts = gbe_dev->cpts;
- struct hwtstamp_config cfg;
+ struct gbe_intf *gbe_intf = intf_priv;
+ struct gbe_priv *gbe_dev;
+ struct phy_device *phy;
- if (!cpts)
+ gbe_dev = gbe_intf->gbe_dev;
+
+ if (!gbe_dev->cpts)
return -EOPNOTSUPP;
- if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
- return -EFAULT;
+ phy = gbe_intf->slave->phy;
+ if (phy_has_hwtstamp(phy))
+ return phy->mii_ts->hwtstamp_set(phy->mii_ts, cfg, extack);
- switch (cfg.tx_type) {
+ switch (cfg->tx_type) {
case HWTSTAMP_TX_OFF:
gbe_dev->tx_ts_enabled = 0;
break;
@@ -2660,7 +2670,7 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
return -ERANGE;
}
- switch (cfg.rx_filter) {
+ switch (cfg->rx_filter) {
case HWTSTAMP_FILTER_NONE:
gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_NONE;
break;
@@ -2668,7 +2678,7 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
- cfg.rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
+ cfg->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
break;
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
@@ -2680,7 +2690,7 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
gbe_dev->rx_ts_enabled = HWTSTAMP_FILTER_PTP_V2_EVENT;
- cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
+ cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
break;
default:
return -ERANGE;
@@ -2688,7 +2698,7 @@ static int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *ifr)
gbe_hwtstamp(gbe_intf);
- return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0;
+ return 0;
}
static void gbe_register_cpts(struct gbe_priv *gbe_dev)
@@ -2745,12 +2755,15 @@ static inline void gbe_unregister_cpts(struct gbe_priv *gbe_dev)
{
}
-static inline int gbe_hwtstamp_get(struct gbe_intf *gbe_intf, struct ifreq *req)
+static inline int gbe_hwtstamp_get(void *intf_priv,
+ struct kernel_hwtstamp_config *cfg)
{
return -EOPNOTSUPP;
}
-static inline int gbe_hwtstamp_set(struct gbe_intf *gbe_intf, struct ifreq *req)
+static inline int gbe_hwtstamp_set(void *intf_priv,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
return -EOPNOTSUPP;
}
@@ -2816,15 +2829,6 @@ static int gbe_ioctl(void *intf_priv, struct ifreq *req, int cmd)
struct gbe_intf *gbe_intf = intf_priv;
struct phy_device *phy = gbe_intf->slave->phy;
- if (!phy_has_hwtstamp(phy)) {
- switch (cmd) {
- case SIOCGHWTSTAMP:
- return gbe_hwtstamp_get(gbe_intf, req);
- case SIOCSHWTSTAMP:
- return gbe_hwtstamp_set(gbe_intf, req);
- }
- }
-
if (phy)
return phy_mii_ioctl(phy, req, cmd);
@@ -3824,6 +3828,8 @@ static struct netcp_module gbe_module = {
.add_vid = gbe_add_vid,
.del_vid = gbe_del_vid,
.ioctl = gbe_ioctl,
+ .hwtstamp_get = gbe_hwtstamp_get,
+ .hwtstamp_set = gbe_hwtstamp_set,
};
static struct netcp_module xgbe_module = {
@@ -3841,6 +3847,8 @@ static struct netcp_module xgbe_module = {
.add_vid = gbe_add_vid,
.del_vid = gbe_del_vid,
.ioctl = gbe_ioctl,
+ .hwtstamp_get = gbe_hwtstamp_get,
+ .hwtstamp_set = gbe_hwtstamp_set,
};
static int __init keystone_gbe_init(void)
diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.c b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
index 5ee8e8980393..d35d1f3c10a1 100644
--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c
@@ -260,6 +260,7 @@ void gelic_card_down(struct gelic_card *card)
if (atomic_dec_if_positive(&card->users) == 0) {
pr_debug("%s: real do\n", __func__);
napi_disable(&card->napi);
+ timer_delete_sync(&card->rx_oom_timer);
/*
* Disable irq. Wireless interrupts will
* be disabled later if any
@@ -363,6 +364,7 @@ static int gelic_card_init_chain(struct gelic_card *card,
* gelic_descr_prepare_rx - reinitializes a rx descriptor
* @card: card structure
* @descr: descriptor to re-init
+ * @napi_mode: is it running in napi poll
*
* return 0 on success, <0 on failure
*
@@ -373,7 +375,8 @@ static int gelic_card_init_chain(struct gelic_card *card,
* must be a multiple of GELIC_NET_RXBUF_ALIGN.
*/
static int gelic_descr_prepare_rx(struct gelic_card *card,
- struct gelic_descr *descr)
+ struct gelic_descr *descr,
+ bool napi_mode)
{
static const unsigned int rx_skb_size =
ALIGN(GELIC_NET_MAX_FRAME, GELIC_NET_RXBUF_ALIGN) +
@@ -391,7 +394,10 @@ static int gelic_descr_prepare_rx(struct gelic_card *card,
descr->hw_regs.payload.dev_addr = 0;
descr->hw_regs.payload.size = 0;
- descr->skb = netdev_alloc_skb(*card->netdev, rx_skb_size);
+ if (napi_mode)
+ descr->skb = napi_alloc_skb(&card->napi, rx_skb_size);
+ else
+ descr->skb = netdev_alloc_skb(*card->netdev, rx_skb_size);
if (!descr->skb) {
descr->hw_regs.payload.dev_addr = 0; /* tell DMAC don't touch memory */
return -ENOMEM;
@@ -463,7 +469,7 @@ static int gelic_card_fill_rx_chain(struct gelic_card *card)
do {
if (!descr->skb) {
- ret = gelic_descr_prepare_rx(card, descr);
+ ret = gelic_descr_prepare_rx(card, descr, false);
if (ret)
goto rewind;
}
@@ -963,14 +969,15 @@ static void gelic_net_pass_skb_up(struct gelic_descr *descr,
netdev->stats.rx_bytes += skb->len;
/* pass skb up to stack */
- netif_receive_skb(skb);
+ napi_gro_receive(&card->napi, skb);
}
/**
* gelic_card_decode_one_descr - processes an rx descriptor
* @card: card structure
*
- * returns 1 if a packet has been sent to the stack, otherwise 0
+ * returns 1 if a packet has been sent to the stack, -ENOMEM on skb alloc
+ * failure, otherwise 0
*
* processes an rx descriptor by iommu-unmapping the data buffer and passing
* the packet up to the stack
@@ -981,16 +988,18 @@ static int gelic_card_decode_one_descr(struct gelic_card *card)
struct gelic_descr_chain *chain = &card->rx_chain;
struct gelic_descr *descr = chain->head;
struct net_device *netdev = NULL;
- int dmac_chain_ended;
+ int dmac_chain_ended = 0;
+ int prepare_rx_ret;
status = gelic_descr_get_status(descr);
if (status == GELIC_DESCR_DMA_CARDOWNED)
return 0;
- if (status == GELIC_DESCR_DMA_NOT_IN_USE) {
+ if (status == GELIC_DESCR_DMA_NOT_IN_USE || !descr->skb) {
dev_dbg(ctodev(card), "dormant descr? %p\n", descr);
- return 0;
+ dmac_chain_ended = 1;
+ goto refill;
}
/* netdevice select */
@@ -1048,9 +1057,10 @@ static int gelic_card_decode_one_descr(struct gelic_card *card)
refill:
/* is the current descriptor terminated with next_descr == NULL? */
- dmac_chain_ended =
- be32_to_cpu(descr->hw_regs.dmac_cmd_status) &
- GELIC_DESCR_RX_DMA_CHAIN_END;
+ if (!dmac_chain_ended)
+ dmac_chain_ended =
+ be32_to_cpu(descr->hw_regs.dmac_cmd_status) &
+ GELIC_DESCR_RX_DMA_CHAIN_END;
/*
* So that always DMAC can see the end
* of the descriptor chain to avoid
@@ -1062,10 +1072,11 @@ refill:
gelic_descr_set_status(descr, GELIC_DESCR_DMA_NOT_IN_USE);
/*
- * this call can fail, but for now, just leave this
- * descriptor without skb
+ * this call can fail, propagate the error
*/
- gelic_descr_prepare_rx(card, descr);
+ prepare_rx_ret = gelic_descr_prepare_rx(card, descr, true);
+ if (prepare_rx_ret)
+ return prepare_rx_ret;
chain->tail = descr;
chain->head = descr->next;
@@ -1087,6 +1098,13 @@ refill:
return 1;
}
+static void gelic_rx_oom_timer(struct timer_list *t)
+{
+ struct gelic_card *card = timer_container_of(card, t, rx_oom_timer);
+
+ napi_schedule(&card->napi);
+}
+
/**
* gelic_net_poll - NAPI poll function called by the stack to return packets
* @napi: napi structure
@@ -1099,14 +1117,22 @@ static int gelic_net_poll(struct napi_struct *napi, int budget)
{
struct gelic_card *card = container_of(napi, struct gelic_card, napi);
int packets_done = 0;
+ int work_result = 0;
while (packets_done < budget) {
- if (!gelic_card_decode_one_descr(card))
+ work_result = gelic_card_decode_one_descr(card);
+ if (work_result != 1)
break;
packets_done++;
}
+ if (work_result == -ENOMEM) {
+ napi_complete_done(napi, packets_done);
+ mod_timer(&card->rx_oom_timer, jiffies + 1);
+ return packets_done;
+ }
+
if (packets_done < budget) {
napi_complete_done(napi, packets_done);
gelic_card_rx_irq_on(card);
@@ -1576,6 +1602,8 @@ static struct gelic_card *gelic_alloc_card_net(struct net_device **netdev)
mutex_init(&card->updown_lock);
atomic_set(&card->users, 0);
+ timer_setup(&card->rx_oom_timer, gelic_rx_oom_timer, 0);
+
return card;
}
diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.h b/drivers/net/ethernet/toshiba/ps3_gelic_net.h
index f7d7931e51b7..c10f1984a5a1 100644
--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.h
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h
@@ -268,6 +268,7 @@ struct gelic_vlan_id {
struct gelic_card {
struct napi_struct napi;
struct net_device *netdev[GELIC_PORT_MAX];
+ struct timer_list rx_oom_timer;
/*
* hypervisor requires irq_status should be
* 8 bytes aligned, but u64 member is
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 06f401bd975c..f362e51c73ee 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -51,6 +51,11 @@ static const struct wx_stats wx_gstrings_fdir_stats[] = {
WX_STAT("fdir_miss", stats.fdirmiss),
};
+static const struct wx_stats wx_gstrings_rsc_stats[] = {
+ WX_STAT("rsc_aggregated", rsc_count),
+ WX_STAT("rsc_flushed", rsc_flush),
+};
+
/* drivers allocates num_tx_queues and num_rx_queues symmetrically so
* we set the num_rx_queues to evaluate to num_tx_queues. This is
* used because we do not have a good way to get the max number of
@@ -64,16 +69,21 @@ static const struct wx_stats wx_gstrings_fdir_stats[] = {
(sizeof(struct wx_queue_stats) / sizeof(u64)))
#define WX_GLOBAL_STATS_LEN ARRAY_SIZE(wx_gstrings_stats)
#define WX_FDIR_STATS_LEN ARRAY_SIZE(wx_gstrings_fdir_stats)
+#define WX_RSC_STATS_LEN ARRAY_SIZE(wx_gstrings_rsc_stats)
#define WX_STATS_LEN (WX_GLOBAL_STATS_LEN + WX_QUEUE_STATS_LEN)
int wx_get_sset_count(struct net_device *netdev, int sset)
{
struct wx *wx = netdev_priv(netdev);
+ int len = WX_STATS_LEN;
switch (sset) {
case ETH_SS_STATS:
- return (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)) ?
- WX_STATS_LEN + WX_FDIR_STATS_LEN : WX_STATS_LEN;
+ if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
+ len += WX_FDIR_STATS_LEN;
+ if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags))
+ len += WX_RSC_STATS_LEN;
+ return len;
default:
return -EOPNOTSUPP;
}
@@ -94,6 +104,10 @@ void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
for (i = 0; i < WX_FDIR_STATS_LEN; i++)
ethtool_puts(&p, wx_gstrings_fdir_stats[i].stat_string);
}
+ if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags)) {
+ for (i = 0; i < WX_RSC_STATS_LEN; i++)
+ ethtool_puts(&p, wx_gstrings_rsc_stats[i].stat_string);
+ }
for (i = 0; i < netdev->num_tx_queues; i++) {
ethtool_sprintf(&p, "tx_queue_%u_packets", i);
ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
@@ -131,6 +145,13 @@ void wx_get_ethtool_stats(struct net_device *netdev,
}
}
+ if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags)) {
+ for (k = 0; k < WX_RSC_STATS_LEN; k++) {
+ p = (char *)wx + wx_gstrings_rsc_stats[k].stat_offset;
+ data[i++] = *(u64 *)p;
+ }
+ }
+
for (j = 0; j < netdev->num_tx_queues; j++) {
ring = wx->tx_ring[j];
if (!ring) {
@@ -219,9 +240,6 @@ int wx_nway_reset(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
- if (wx->mac.type == wx_mac_aml40)
- return -EOPNOTSUPP;
-
return phylink_ethtool_nway_reset(wx->phylink);
}
EXPORT_SYMBOL(wx_nway_reset);
@@ -240,9 +258,6 @@ int wx_set_link_ksettings(struct net_device *netdev,
{
struct wx *wx = netdev_priv(netdev);
- if (wx->mac.type == wx_mac_aml40)
- return -EOPNOTSUPP;
-
return phylink_ethtool_ksettings_set(wx->phylink, cmd);
}
EXPORT_SYMBOL(wx_set_link_ksettings);
@@ -252,9 +267,6 @@ void wx_get_pauseparam(struct net_device *netdev,
{
struct wx *wx = netdev_priv(netdev);
- if (wx->mac.type == wx_mac_aml40)
- return;
-
phylink_ethtool_get_pauseparam(wx->phylink, pause);
}
EXPORT_SYMBOL(wx_get_pauseparam);
@@ -264,9 +276,6 @@ int wx_set_pauseparam(struct net_device *netdev,
{
struct wx *wx = netdev_priv(netdev);
- if (wx->mac.type == wx_mac_aml40)
- return -EOPNOTSUPP;
-
return phylink_ethtool_set_pauseparam(wx->phylink, pause);
}
EXPORT_SYMBOL(wx_set_pauseparam);
@@ -322,6 +331,40 @@ int wx_get_coalesce(struct net_device *netdev,
}
EXPORT_SYMBOL(wx_get_coalesce);
+static void wx_update_rsc(struct wx *wx)
+{
+ struct net_device *netdev = wx->netdev;
+ bool need_reset = false;
+
+ /* nothing to do if LRO or RSC are not enabled */
+ if (!test_bit(WX_FLAG_RSC_CAPABLE, wx->flags) ||
+ !(netdev->features & NETIF_F_LRO))
+ return;
+
+ /* check the feature flag value and enable RSC if necessary */
+ if (wx->rx_itr_setting == 1 ||
+ wx->rx_itr_setting > WX_MIN_RSC_ITR) {
+ if (!test_bit(WX_FLAG_RSC_ENABLED, wx->flags)) {
+ set_bit(WX_FLAG_RSC_ENABLED, wx->flags);
+ dev_info(&wx->pdev->dev,
+ "rx-usecs value high enough to re-enable RSC\n");
+
+ need_reset = true;
+ }
+ /* if interrupt rate is too high then disable RSC */
+ } else if (test_bit(WX_FLAG_RSC_ENABLED, wx->flags)) {
+ clear_bit(WX_FLAG_RSC_ENABLED, wx->flags);
+ dev_info(&wx->pdev->dev,
+ "rx-usecs set too low, disabling RSC\n");
+
+ need_reset = true;
+ }
+
+ /* reset the device to apply the new RSC setting */
+ if (need_reset && wx->do_reset)
+ wx->do_reset(netdev);
+}
+
int wx_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *ec,
struct kernel_ethtool_coalesce *kernel_coal,
@@ -414,6 +457,8 @@ int wx_set_coalesce(struct net_device *netdev,
wx_write_eitr(q_vector);
}
+ wx_update_rsc(wx);
+
return 0;
}
EXPORT_SYMBOL(wx_set_coalesce);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 1e2713f0c921..58b8300e3d2c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -1779,7 +1779,9 @@ EXPORT_SYMBOL(wx_set_rx_mode);
static void wx_set_rx_buffer_len(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
+ struct wx_ring *rx_ring;
u32 mhadd, max_frame;
+ int i;
max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
/* adjust max frame to be at least the size of a standard frame */
@@ -1789,6 +1791,19 @@ static void wx_set_rx_buffer_len(struct wx *wx)
mhadd = rd32(wx, WX_PSR_MAX_SZ);
if (max_frame != mhadd)
wr32(wx, WX_PSR_MAX_SZ, max_frame);
+
+ /*
+ * Setup the HW Rx Head and Tail Descriptor Pointers and
+ * the Base and Length of the Rx Descriptor Ring
+ */
+ for (i = 0; i < wx->num_rx_queues; i++) {
+ rx_ring = wx->rx_ring[i];
+ rx_ring->rx_buf_len = WX_RXBUFFER_2K;
+#if (PAGE_SIZE < 8192)
+ if (test_bit(WX_FLAG_RSC_ENABLED, wx->flags))
+ rx_ring->rx_buf_len = WX_RXBUFFER_3K;
+#endif
+ }
}
/**
@@ -1865,11 +1880,27 @@ static void wx_configure_srrctl(struct wx *wx,
srrctl |= WX_RXBUFFER_256 << WX_PX_RR_CFG_BHDRSIZE_SHIFT;
/* configure the packet buffer length */
- srrctl |= WX_RX_BUFSZ >> WX_PX_RR_CFG_BSIZEPKT_SHIFT;
+ srrctl |= rx_ring->rx_buf_len >> WX_PX_RR_CFG_BSIZEPKT_SHIFT;
wr32(wx, WX_PX_RR_CFG(reg_idx), srrctl);
}
+static void wx_configure_rscctl(struct wx *wx,
+ struct wx_ring *ring)
+{
+ u8 reg_idx = ring->reg_idx;
+ u32 rscctrl;
+
+ if (!test_bit(WX_FLAG_RSC_ENABLED, wx->flags))
+ return;
+
+ rscctrl = rd32(wx, WX_PX_RR_CFG(reg_idx));
+ rscctrl |= WX_PX_RR_CFG_RSC;
+ rscctrl |= WX_PX_RR_CFG_MAX_RSCBUF_16;
+
+ wr32(wx, WX_PX_RR_CFG(reg_idx), rscctrl);
+}
+
static void wx_configure_tx_ring(struct wx *wx,
struct wx_ring *ring)
{
@@ -1905,6 +1936,15 @@ static void wx_configure_tx_ring(struct wx *wx,
memset(ring->tx_buffer_info, 0,
sizeof(struct wx_tx_buffer) * ring->count);
+ if (ring->headwb_mem) {
+ wr32(wx, WX_PX_TR_HEAD_ADDRL(reg_idx),
+ ring->headwb_dma & DMA_BIT_MASK(32));
+ wr32(wx, WX_PX_TR_HEAD_ADDRH(reg_idx),
+ upper_32_bits(ring->headwb_dma));
+
+ txdctl |= WX_PX_TR_CFG_HEAD_WB;
+ }
+
/* enable queue */
wr32(wx, WX_PX_TR_CFG(reg_idx), txdctl);
@@ -1935,6 +1975,10 @@ static void wx_configure_rx_ring(struct wx *wx,
rxdctl |= (ring->count / 128) << WX_PX_RR_CFG_RR_SIZE_SHIFT;
rxdctl |= 0x1 << WX_PX_RR_CFG_RR_THER_SHIFT;
+
+ if (test_bit(WX_FLAG_RX_MERGE_ENABLED, wx->flags))
+ rxdctl |= WX_PX_RR_CFG_DESC_MERGE;
+
wr32(wx, WX_PX_RR_CFG(reg_idx), rxdctl);
/* reset head and tail pointers */
@@ -1943,6 +1987,7 @@ static void wx_configure_rx_ring(struct wx *wx,
ring->tail = wx->hw_addr + WX_PX_RR_WP(reg_idx);
wx_configure_srrctl(wx, ring);
+ wx_configure_rscctl(wx, ring);
/* initialize rx_buffer_info */
memset(ring->rx_buffer_info, 0,
@@ -2181,7 +2226,9 @@ void wx_configure_rx(struct wx *wx)
/* RSC Setup */
psrctl = rd32(wx, WX_PSR_CTL);
psrctl |= WX_PSR_CTL_RSC_ACK; /* Disable RSC for ACK packets */
- psrctl |= WX_PSR_CTL_RSC_DIS;
+ psrctl &= ~WX_PSR_CTL_RSC_DIS;
+ if (!test_bit(WX_FLAG_RSC_ENABLED, wx->flags))
+ psrctl |= WX_PSR_CTL_RSC_DIS;
wr32(wx, WX_PSR_CTL, psrctl);
}
@@ -2190,6 +2237,12 @@ void wx_configure_rx(struct wx *wx)
/* set_rx_buffer_len must be called before ring initialization */
wx_set_rx_buffer_len(wx);
+ if (test_bit(WX_FLAG_RX_MERGE_ENABLED, wx->flags)) {
+ wr32(wx, WX_RDM_DCACHE_CTL, WX_RDM_DCACHE_CTL_EN);
+ wr32m(wx, WX_RDM_RSC_CTL,
+ WX_RDM_RSC_CTL_FREE_CTL | WX_RDM_RSC_CTL_FREE_CNT_DIS,
+ WX_RDM_RSC_CTL_FREE_CTL);
+ }
/* Setup the HW Rx Head and Tail Descriptor Pointers and
* the Base and Length of the Rx Descriptor Ring
*/
@@ -2427,7 +2480,8 @@ int wx_sw_init(struct wx *wx)
wx->oem_svid = pdev->subsystem_vendor;
wx->oem_ssid = pdev->subsystem_device;
wx->bus.device = PCI_SLOT(pdev->devfn);
- wx->bus.func = PCI_FUNC(pdev->devfn);
+ wx->bus.func = FIELD_GET(WX_CFG_PORT_ST_LANID,
+ rd32(wx, WX_CFG_PORT_ST));
if (wx->oem_svid == PCI_VENDOR_ID_WANGXUN ||
pdev->is_virtfn) {
@@ -2805,6 +2859,18 @@ void wx_update_stats(struct wx *wx)
wx->hw_csum_rx_error = hw_csum_rx_error;
wx->hw_csum_rx_good = hw_csum_rx_good;
+ if (test_bit(WX_FLAG_RSC_ENABLED, wx->flags)) {
+ u64 rsc_count = 0;
+ u64 rsc_flush = 0;
+
+ for (i = 0; i < wx->num_rx_queues; i++) {
+ rsc_count += wx->rx_ring[i]->rx_stats.rsc_count;
+ rsc_flush += wx->rx_ring[i]->rx_stats.rsc_flush;
+ }
+ wx->rsc_count = rsc_count;
+ wx->rsc_flush = rsc_flush;
+ }
+
for (i = 0; i < wx->num_tx_queues; i++) {
struct wx_ring *tx_ring = wx->tx_ring[i];
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 3adf7048320a..32cadafa4b3b 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -235,7 +235,7 @@ static struct sk_buff *wx_build_skb(struct wx_ring *rx_ring,
{
unsigned int size = le16_to_cpu(rx_desc->wb.upper.length);
#if (PAGE_SIZE < 8192)
- unsigned int truesize = WX_RX_BUFSZ;
+ unsigned int truesize = wx_rx_pg_size(rx_ring) / 2;
#else
unsigned int truesize = ALIGN(size, L1_CACHE_BYTES);
#endif
@@ -341,7 +341,7 @@ void wx_alloc_rx_buffers(struct wx_ring *rx_ring, u16 cleaned_count)
/* sync the buffer for use by the device */
dma_sync_single_range_for_device(rx_ring->dev, bi->dma,
bi->page_offset,
- WX_RX_BUFSZ,
+ rx_ring->rx_buf_len,
DMA_FROM_DEVICE);
rx_desc->read.pkt_addr =
@@ -404,6 +404,7 @@ static bool wx_is_non_eop(struct wx_ring *rx_ring,
union wx_rx_desc *rx_desc,
struct sk_buff *skb)
{
+ struct wx *wx = rx_ring->q_vector->wx;
u32 ntc = rx_ring->next_to_clean + 1;
/* fetch, update, and store next to clean */
@@ -412,6 +413,24 @@ static bool wx_is_non_eop(struct wx_ring *rx_ring,
prefetch(WX_RX_DESC(rx_ring, ntc));
+ /* update RSC append count if present */
+ if (test_bit(WX_FLAG_RSC_ENABLED, wx->flags)) {
+ __le32 rsc_enabled = rx_desc->wb.lower.lo_dword.data &
+ cpu_to_le32(WX_RXD_RSCCNT_MASK);
+
+ if (unlikely(rsc_enabled)) {
+ u32 rsc_cnt = le32_to_cpu(rsc_enabled);
+
+ rsc_cnt >>= WX_RXD_RSCCNT_SHIFT;
+ WX_CB(skb)->append_cnt += rsc_cnt - 1;
+
+ /* update ntc based on RSC value */
+ ntc = le32_to_cpu(rx_desc->wb.upper.status_error);
+ ntc &= WX_RXD_NEXTP_MASK;
+ ntc >>= WX_RXD_NEXTP_SHIFT;
+ }
+ }
+
/* if we are the last buffer then there is nothing else to do */
if (likely(wx_test_staterr(rx_desc, WX_RXD_STAT_EOP)))
return false;
@@ -582,6 +601,33 @@ static void wx_rx_vlan(struct wx_ring *ring, union wx_rx_desc *rx_desc,
}
}
+static void wx_set_rsc_gso_size(struct wx_ring *ring,
+ struct sk_buff *skb)
+{
+ u16 hdr_len = skb_headlen(skb);
+
+ /* set gso_size to avoid messing up TCP MSS */
+ skb_shinfo(skb)->gso_size = DIV_ROUND_UP((skb->len - hdr_len),
+ WX_CB(skb)->append_cnt);
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+}
+
+static void wx_update_rsc_stats(struct wx_ring *rx_ring,
+ struct sk_buff *skb)
+{
+ /* if append_cnt is 0 then frame is not RSC */
+ if (!WX_CB(skb)->append_cnt)
+ return;
+
+ rx_ring->rx_stats.rsc_count += WX_CB(skb)->append_cnt;
+ rx_ring->rx_stats.rsc_flush++;
+
+ wx_set_rsc_gso_size(rx_ring, skb);
+
+ /* gso_size is computed using append_cnt so always clear it last */
+ WX_CB(skb)->append_cnt = 0;
+}
+
/**
* wx_process_skb_fields - Populate skb header fields from Rx descriptor
* @rx_ring: rx descriptor ring packet is being transacted on
@@ -598,6 +644,9 @@ static void wx_process_skb_fields(struct wx_ring *rx_ring,
{
struct wx *wx = netdev_priv(rx_ring->netdev);
+ if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags))
+ wx_update_rsc_stats(rx_ring, skb);
+
wx_rx_hash(rx_ring, rx_desc, skb);
wx_rx_checksum(rx_ring, rx_desc, skb);
@@ -735,9 +784,22 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector,
/* prevent any other reads prior to eop_desc */
smp_rmb();
- /* if DD is not set pending work has not been completed */
- if (!(eop_desc->wb.status & cpu_to_le32(WX_TXD_STAT_DD)))
+ if (tx_ring->headwb_mem) {
+ u32 head = *tx_ring->headwb_mem;
+
+ if (head == tx_ring->next_to_clean)
+ break;
+ else if (head > tx_ring->next_to_clean &&
+ !(tx_buffer->next_eop >= tx_ring->next_to_clean &&
+ tx_buffer->next_eop < head))
+ break;
+ else if (!(tx_buffer->next_eop >= tx_ring->next_to_clean ||
+ tx_buffer->next_eop < head))
+ break;
+ } else if (!(eop_desc->wb.status & cpu_to_le32(WX_TXD_STAT_DD))) {
+ /* if DD is not set pending work has not been completed */
break;
+ }
/* clear next_to_watch to prevent false hangs */
tx_buffer->next_to_watch = NULL;
@@ -1075,6 +1137,10 @@ static int wx_tx_map(struct wx_ring *tx_ring,
/* set next_to_watch value indicating a packet is present */
first->next_to_watch = tx_desc;
+ /* set next_eop for amlite tx head wb */
+ if (tx_ring->headwb_mem)
+ first->next_eop = i;
+
i++;
if (i == tx_ring->count)
i = 0;
@@ -2532,7 +2598,7 @@ static void wx_clean_rx_ring(struct wx_ring *rx_ring)
dma_sync_single_range_for_cpu(rx_ring->dev,
rx_buffer->dma,
rx_buffer->page_offset,
- WX_RX_BUFSZ,
+ rx_ring->rx_buf_len,
DMA_FROM_DEVICE);
/* free resources associated with mapping */
@@ -2683,6 +2749,16 @@ void wx_clean_all_tx_rings(struct wx *wx)
}
EXPORT_SYMBOL(wx_clean_all_tx_rings);
+static void wx_free_headwb_resources(struct wx_ring *tx_ring)
+{
+ if (!tx_ring->headwb_mem)
+ return;
+
+ dma_free_coherent(tx_ring->dev, sizeof(u32),
+ tx_ring->headwb_mem, tx_ring->headwb_dma);
+ tx_ring->headwb_mem = NULL;
+}
+
/**
* wx_free_tx_resources - Free Tx Resources per Queue
* @tx_ring: Tx descriptor ring for a specific queue
@@ -2702,6 +2778,8 @@ static void wx_free_tx_resources(struct wx_ring *tx_ring)
dma_free_coherent(tx_ring->dev, tx_ring->size,
tx_ring->desc, tx_ring->dma);
tx_ring->desc = NULL;
+
+ wx_free_headwb_resources(tx_ring);
}
/**
@@ -2731,13 +2809,14 @@ static int wx_alloc_page_pool(struct wx_ring *rx_ring)
struct page_pool_params pp_params = {
.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
- .order = 0,
- .pool_size = rx_ring->count,
+ .order = wx_rx_pg_order(rx_ring),
+ .pool_size = rx_ring->count * rx_ring->rx_buf_len /
+ wx_rx_pg_size(rx_ring),
.nid = dev_to_node(rx_ring->dev),
.dev = rx_ring->dev,
.dma_dir = DMA_FROM_DEVICE,
.offset = 0,
- .max_len = PAGE_SIZE,
+ .max_len = wx_rx_pg_size(rx_ring),
};
rx_ring->page_pool = page_pool_create(&pp_params);
@@ -2840,6 +2919,24 @@ err_setup_rx:
return err;
}
+static void wx_setup_headwb_resources(struct wx_ring *tx_ring)
+{
+ struct wx *wx = netdev_priv(tx_ring->netdev);
+
+ if (!test_bit(WX_FLAG_TXHEAD_WB_ENABLED, wx->flags))
+ return;
+
+ if (!tx_ring->q_vector)
+ return;
+
+ tx_ring->headwb_mem = dma_alloc_coherent(tx_ring->dev,
+ sizeof(u32),
+ &tx_ring->headwb_dma,
+ GFP_KERNEL);
+ if (!tx_ring->headwb_mem)
+ dev_info(tx_ring->dev, "Allocate headwb memory failed, disable it\n");
+}
+
/**
* wx_setup_tx_resources - allocate Tx resources (Descriptors)
* @tx_ring: tx descriptor ring (for a specific queue) to setup
@@ -2880,6 +2977,8 @@ static int wx_setup_tx_resources(struct wx_ring *tx_ring)
if (!tx_ring->desc)
goto err;
+ wx_setup_headwb_resources(tx_ring);
+
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
@@ -3026,8 +3125,25 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
else if (changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER))
wx_set_rx_mode(netdev);
+ if (test_bit(WX_FLAG_RSC_CAPABLE, wx->flags)) {
+ if (!(features & NETIF_F_LRO)) {
+ if (test_bit(WX_FLAG_RSC_ENABLED, wx->flags))
+ need_reset = true;
+ clear_bit(WX_FLAG_RSC_ENABLED, wx->flags);
+ } else if (!(test_bit(WX_FLAG_RSC_ENABLED, wx->flags))) {
+ if (wx->rx_itr_setting == 1 ||
+ wx->rx_itr_setting > WX_MIN_RSC_ITR) {
+ set_bit(WX_FLAG_RSC_ENABLED, wx->flags);
+ need_reset = true;
+ } else if (changed & NETIF_F_LRO) {
+ dev_info(&wx->pdev->dev,
+ "rx-usecs set too low, disable RSC\n");
+ }
+ }
+ }
+
if (!(test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)))
- return 0;
+ goto out;
/* Check if Flow Director n-tuple support was enabled or disabled. If
* the state changed, we need to reset.
@@ -3053,6 +3169,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
break;
}
+out:
if (need_reset && wx->do_reset)
wx->do_reset(netdev);
@@ -3102,6 +3219,14 @@ netdev_features_t wx_fix_features(struct net_device *netdev,
}
}
+ /* If Rx checksum is disabled, then RSC/LRO should also be disabled */
+ if (!(features & NETIF_F_RXCSUM))
+ features &= ~NETIF_F_LRO;
+
+ /* Turn off LRO if not RSC capable */
+ if (!test_bit(WX_FLAG_RSC_CAPABLE, wx->flags))
+ features &= ~NETIF_F_LRO;
+
return features;
}
EXPORT_SYMBOL(wx_fix_features);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c
index c6d158cd70da..493da5fffdb6 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c
@@ -122,6 +122,10 @@ static int __wx_enable_sriov(struct wx *wx, u8 num_vfs)
WX_CFG_PORT_CTL_NUM_VT_MASK,
value);
+ /* Disable RSC when in SR-IOV mode */
+ clear_bit(WX_FLAG_RSC_CAPABLE, wx->flags);
+ clear_bit(WX_FLAG_RSC_ENABLED, wx->flags);
+
return ret;
}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index d89b9b8a0a2c..29e5c5470c94 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -83,8 +83,13 @@
/*********************** Receive DMA registers **************************/
#define WX_RDM_VF_RE(_i) (0x12004 + ((_i) * 4))
+#define WX_RDM_RSC_CTL 0x1200C
+#define WX_RDM_RSC_CTL_FREE_CNT_DIS BIT(8)
+#define WX_RDM_RSC_CTL_FREE_CTL BIT(7)
#define WX_RDM_PF_QDE(_i) (0x12080 + ((_i) * 4))
#define WX_RDM_VFRE_CLR(_i) (0x120A0 + ((_i) * 4))
+#define WX_RDM_DCACHE_CTL 0x120A8
+#define WX_RDM_DCACHE_CTL_EN BIT(0)
#define WX_RDM_DRP_PKT 0x12500
#define WX_RDM_PKT_CNT 0x12504
#define WX_RDM_BYTE_CNT_LSB 0x12508
@@ -97,6 +102,8 @@
#define WX_CFG_PORT_CTL_DRV_LOAD BIT(3)
#define WX_CFG_PORT_CTL_QINQ BIT(2)
#define WX_CFG_PORT_CTL_D_VLAN BIT(0) /* double vlan*/
+#define WX_CFG_PORT_ST 0x14404
+#define WX_CFG_PORT_ST_LANID GENMASK(9, 8)
#define WX_CFG_TAG_TPID(_i) (0x14430 + ((_i) * 4))
#define WX_CFG_PORT_CTL_NUM_VT_MASK GENMASK(13, 12) /* number of TVs */
@@ -419,6 +426,7 @@ enum WX_MSCA_CMD_value {
#define WX_7K_ITR 595
#define WX_12K_ITR 336
#define WX_20K_ITR 200
+#define WX_MIN_RSC_ITR 24
#define WX_SP_MAX_EITR 0x00000FF8U
#define WX_AML_MAX_EITR 0x00000FFFU
#define WX_EM_MAX_EITR 0x00007FFCU
@@ -429,12 +437,15 @@ enum WX_MSCA_CMD_value {
#define WX_PX_TR_WP(_i) (0x03008 + ((_i) * 0x40))
#define WX_PX_TR_RP(_i) (0x0300C + ((_i) * 0x40))
#define WX_PX_TR_CFG(_i) (0x03010 + ((_i) * 0x40))
+#define WX_PX_TR_HEAD_ADDRL(_i) (0x03028 + ((_i) * 0x40))
+#define WX_PX_TR_HEAD_ADDRH(_i) (0x0302C + ((_i) * 0x40))
/* Transmit Config masks */
#define WX_PX_TR_CFG_ENABLE BIT(0) /* Ena specific Tx Queue */
#define WX_PX_TR_CFG_TR_SIZE_SHIFT 1 /* tx desc number per ring */
#define WX_PX_TR_CFG_SWFLSH BIT(26) /* Tx Desc. wr-bk flushing */
#define WX_PX_TR_CFG_WTHRESH_SHIFT 16 /* shift to WTHRESH bits */
#define WX_PX_TR_CFG_THRE_SHIFT 8
+#define WX_PX_TR_CFG_HEAD_WB BIT(27)
/* Receive DMA Registers */
#define WX_PX_RR_BAL(_i) (0x01000 + ((_i) * 0x40))
@@ -446,7 +457,10 @@ enum WX_MSCA_CMD_value {
/* PX_RR_CFG bit definitions */
#define WX_PX_RR_CFG_VLAN BIT(31)
#define WX_PX_RR_CFG_DROP_EN BIT(30)
+#define WX_PX_RR_CFG_RSC BIT(29)
#define WX_PX_RR_CFG_SPLIT_MODE BIT(26)
+#define WX_PX_RR_CFG_MAX_RSCBUF_16 FIELD_PREP(GENMASK(24, 23), 3)
+#define WX_PX_RR_CFG_DESC_MERGE BIT(19)
#define WX_PX_RR_CFG_RR_THER_SHIFT 16
#define WX_PX_RR_CFG_RR_HDR_SZ GENMASK(15, 12)
#define WX_PX_RR_CFG_RR_BUF_SZ GENMASK(11, 8)
@@ -542,14 +556,9 @@ enum WX_MSCA_CMD_value {
/* Supported Rx Buffer Sizes */
#define WX_RXBUFFER_256 256 /* Used for skb receive header */
#define WX_RXBUFFER_2K 2048
+#define WX_RXBUFFER_3K 3072
#define WX_MAX_RXBUFFER 16384 /* largest size for single descriptor */
-#if MAX_SKB_FRAGS < 8
-#define WX_RX_BUFSZ ALIGN(WX_MAX_RXBUFFER / MAX_SKB_FRAGS, 1024)
-#else
-#define WX_RX_BUFSZ WX_RXBUFFER_2K
-#endif
-
#define WX_RX_BUFFER_WRITE 16 /* Must be power of 2 */
#define WX_MAX_DATA_PER_TXD BIT(14)
@@ -557,8 +566,6 @@ enum WX_MSCA_CMD_value {
#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), WX_MAX_DATA_PER_TXD)
#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
-#define WX_CFG_PORT_ST 0x14404
-
/******************* Receive Descriptor bit definitions **********************/
#define WX_RXD_STAT_DD BIT(0) /* Done */
#define WX_RXD_STAT_EOP BIT(1) /* End of Packet */
@@ -643,6 +650,12 @@ enum wx_l2_ptypes {
#define WX_RXD_PKTTYPE(_rxd) \
((le32_to_cpu((_rxd)->wb.lower.lo_dword.data) >> 9) & 0xFF)
+
+#define WX_RXD_RSCCNT_MASK GENMASK(20, 17)
+#define WX_RXD_RSCCNT_SHIFT 17
+#define WX_RXD_NEXTP_MASK GENMASK(19, 4)
+#define WX_RXD_NEXTP_SHIFT 4
+
/*********************** Transmit Descriptor Config Masks ****************/
#define WX_TXD_STAT_DD BIT(0) /* Descriptor Done */
#define WX_TXD_DTYP_DATA 0 /* Adv Data Descriptor */
@@ -1005,6 +1018,7 @@ struct wx_tx_buffer {
DEFINE_DMA_UNMAP_LEN(len);
__be16 protocol;
u32 tx_flags;
+ u32 next_eop;
};
struct wx_rx_buffer {
@@ -1029,6 +1043,8 @@ struct wx_rx_queue_stats {
u64 csum_good_cnt;
u64 csum_err;
u64 alloc_rx_buff_failed;
+ u64 rsc_count;
+ u64 rsc_flush;
};
/* iterator for handling rings in ring container */
@@ -1056,6 +1072,8 @@ struct wx_ring {
};
u8 __iomem *tail;
dma_addr_t dma; /* phys. address of descriptor ring */
+ dma_addr_t headwb_dma;
+ u32 *headwb_mem;
unsigned int size; /* length in bytes */
u16 count; /* amount of descriptors */
@@ -1069,6 +1087,7 @@ struct wx_ring {
*/
u16 next_to_use;
u16 next_to_clean;
+ u16 rx_buf_len;
union {
u16 next_to_alloc;
struct {
@@ -1225,13 +1244,16 @@ enum wx_pf_flags {
WX_FLAG_FDIR_HASH,
WX_FLAG_FDIR_PERFECT,
WX_FLAG_RSC_CAPABLE,
+ WX_FLAG_RSC_ENABLED,
WX_FLAG_RX_HWTSTAMP_ENABLED,
WX_FLAG_RX_HWTSTAMP_IN_REGISTER,
WX_FLAG_PTP_PPS_ENABLED,
WX_FLAG_NEED_LINK_CONFIG,
- WX_FLAG_NEED_SFP_RESET,
+ WX_FLAG_NEED_MODULE_RESET,
WX_FLAG_NEED_UPDATE_LINK,
WX_FLAG_NEED_DO_RESET,
+ WX_FLAG_RX_MERGE_ENABLED,
+ WX_FLAG_TXHEAD_WB_ENABLED,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1271,8 +1293,6 @@ struct wx {
/* PHY stuff */
bool notify_down;
- int adv_speed;
- int adv_duplex;
unsigned int link;
int speed;
int duplex;
@@ -1340,6 +1360,8 @@ struct wx {
u64 hw_csum_rx_good;
u64 hw_csum_rx_error;
u64 alloc_rx_buff_failed;
+ u64 rsc_count;
+ u64 rsc_flush;
unsigned int num_vfs;
struct vf_data_storage *vfinfo;
struct vf_macvlans vf_mvs;
@@ -1471,4 +1493,15 @@ static inline int wx_set_state_reset(struct wx *wx)
return 0;
}
+static inline unsigned int wx_rx_pg_order(struct wx_ring *ring)
+{
+#if (PAGE_SIZE < 8192)
+ if (ring->rx_buf_len == WX_RXBUFFER_3K)
+ return 1;
+#endif
+ return 0;
+}
+
+#define wx_rx_pg_size(_ring) (PAGE_SIZE << wx_rx_pg_order(_ring))
+
#endif /* _WX_TYPE_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf.h b/drivers/net/ethernet/wangxun/libwx/wx_vf.h
index 3f16de0fa427..eb6ca3fe4e97 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf.h
@@ -74,6 +74,7 @@
#define WX_VXRXDCTL_BUFSZ(f) FIELD_PREP(GENMASK(11, 8), f)
#define WX_VXRXDCTL_HDRSZ_MASK GENMASK(15, 12)
#define WX_VXRXDCTL_HDRSZ(f) FIELD_PREP(GENMASK(15, 12), f)
+#define WX_VXRXDCTL_DESC_MERGE BIT(19)
#define WX_VXRXDCTL_RSCMAX_MASK GENMASK(24, 23)
#define WX_VXRXDCTL_RSCMAX(f) FIELD_PREP(GENMASK(24, 23), f)
#define WX_VXRXDCTL_RSCEN BIT(29)
@@ -91,6 +92,9 @@
#define WX_VXTXDCTL_PTHRESH(f) FIELD_PREP(GENMASK(11, 8), f)
#define WX_VXTXDCTL_WTHRESH(f) FIELD_PREP(GENMASK(22, 16), f)
#define WX_VXTXDCTL_FLUSH BIT(26)
+#define WX_VXTXDCTL_HEAD_WB BIT(27)
+#define WX_VXTXD_HEAD_ADDRL(r) (0x3028 + (0x40 * (r)))
+#define WX_VXTXD_HEAD_ADDRH(r) (0x302C + (0x40 * (r)))
#define WX_PFLINK_STATUS(g) FIELD_GET(BIT(0), g)
#define WX_PFLINK_SPEED(g) FIELD_GET(GENMASK(31, 1), g)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c
index a87887b9f8ee..aa8be036956c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c
@@ -132,6 +132,15 @@ static void wx_configure_tx_ring_vf(struct wx *wx, struct wx_ring *ring)
txdctl |= WX_VXTXDCTL_BUFLEN(wx_buf_len(ring->count));
txdctl |= WX_VXTXDCTL_ENABLE;
+ if (ring->headwb_mem) {
+ wr32(wx, WX_VXTXD_HEAD_ADDRL(reg_idx),
+ ring->headwb_dma & DMA_BIT_MASK(32));
+ wr32(wx, WX_VXTXD_HEAD_ADDRH(reg_idx),
+ upper_32_bits(ring->headwb_dma));
+
+ txdctl |= WX_VXTXDCTL_HEAD_WB;
+ }
+
/* reinitialize tx_buffer_info */
memset(ring->tx_buffer_info, 0,
sizeof(struct wx_tx_buffer) * ring->count);
@@ -272,6 +281,9 @@ void wx_configure_rx_ring_vf(struct wx *wx, struct wx_ring *ring)
rxdctl |= WX_VXRXDCTL_RSCMAX(0);
rxdctl |= WX_VXRXDCTL_RSCEN;
+ if (test_bit(WX_FLAG_RX_MERGE_ENABLED, wx->flags))
+ rxdctl |= WX_VXRXDCTL_DESC_MERGE;
+
wr32(wx, WX_VXRXDCTL(reg_idx), rxdctl);
/* pf/vf reuse */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
index dc87ccad9652..62d7f47d4f8d 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c
@@ -17,10 +17,15 @@
void txgbe_gpio_init_aml(struct wx *wx)
{
- u32 status;
+ u32 status, mod_rst;
+
+ if (wx->mac.type == wx_mac_aml40)
+ mod_rst = TXGBE_GPIOBIT_4;
+ else
+ mod_rst = TXGBE_GPIOBIT_2;
- wr32(wx, WX_GPIO_INTTYPE_LEVEL, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
- wr32(wx, WX_GPIO_INTEN, TXGBE_GPIOBIT_2 | TXGBE_GPIOBIT_3);
+ wr32(wx, WX_GPIO_INTTYPE_LEVEL, mod_rst);
+ wr32(wx, WX_GPIO_INTEN, mod_rst);
status = rd32(wx, WX_GPIO_INTSTATUS);
for (int i = 0; i < 6; i++) {
@@ -33,20 +38,20 @@ irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data)
{
struct txgbe *txgbe = data;
struct wx *wx = txgbe->wx;
- u32 status;
+ u32 status, mod_rst;
+
+ if (wx->mac.type == wx_mac_aml40)
+ mod_rst = TXGBE_GPIOBIT_4;
+ else
+ mod_rst = TXGBE_GPIOBIT_2;
wr32(wx, WX_GPIO_INTMASK, 0xFF);
status = rd32(wx, WX_GPIO_INTSTATUS);
- if (status & TXGBE_GPIOBIT_2) {
- set_bit(WX_FLAG_NEED_SFP_RESET, wx->flags);
- wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_2);
+ if (status & mod_rst) {
+ set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
+ wr32(wx, WX_GPIO_EOI, mod_rst);
wx_service_event_schedule(wx);
}
- if (status & TXGBE_GPIOBIT_3) {
- set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
- wx_service_event_schedule(wx);
- wr32(wx, WX_GPIO_EOI, TXGBE_GPIOBIT_3);
- }
wr32(wx, WX_GPIO_INTMASK, 0);
return IRQ_HANDLED;
@@ -56,7 +61,7 @@ int txgbe_test_hostif(struct wx *wx)
{
struct txgbe_hic_ephy_getlink buffer;
- if (wx->mac.type != wx_mac_aml)
+ if (wx->mac.type == wx_mac_sp)
return 0;
buffer.hdr.cmd = FW_PHY_GET_LINK_CMD;
@@ -68,15 +73,49 @@ int txgbe_test_hostif(struct wx *wx)
WX_HI_COMMAND_TIMEOUT, true);
}
-static int txgbe_identify_sfp_hostif(struct wx *wx, struct txgbe_hic_i2c_read *buffer)
+int txgbe_read_eeprom_hostif(struct wx *wx,
+ struct txgbe_hic_i2c_read *buffer,
+ u32 length, u8 *data)
{
- buffer->hdr.cmd = FW_READ_SFP_INFO_CMD;
+ u32 dword_len, offset, value, i;
+ int err;
+
+ buffer->hdr.cmd = FW_READ_EEPROM_CMD;
buffer->hdr.buf_len = sizeof(struct txgbe_hic_i2c_read) -
sizeof(struct wx_hic_hdr);
buffer->hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
+ err = wx_host_interface_command(wx, (u32 *)buffer,
+ sizeof(struct txgbe_hic_i2c_read),
+ WX_HI_COMMAND_TIMEOUT, false);
+ if (err != 0)
+ return err;
+
+ /* buffer length offset to read return data */
+ offset = sizeof(struct txgbe_hic_i2c_read) >> 2;
+ dword_len = round_up(length, 4) >> 2;
+
+ for (i = 0; i < dword_len; i++) {
+ value = rd32a(wx, WX_FW2SW_MBOX, i + offset);
+ le32_to_cpus(&value);
+
+ memcpy(data, &value, 4);
+ data += 4;
+ }
+
+ return 0;
+}
+
+static int txgbe_identify_module_hostif(struct wx *wx,
+ struct txgbe_hic_get_module_info *buffer)
+{
+ buffer->hdr.cmd = FW_GET_MODULE_INFO_CMD;
+ buffer->hdr.buf_len = sizeof(struct txgbe_hic_get_module_info) -
+ sizeof(struct wx_hic_hdr);
+ buffer->hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
+
return wx_host_interface_command(wx, (u32 *)buffer,
- sizeof(struct txgbe_hic_i2c_read),
+ sizeof(struct txgbe_hic_get_module_info),
WX_HI_COMMAND_TIMEOUT, true);
}
@@ -90,12 +129,18 @@ static int txgbe_set_phy_link_hostif(struct wx *wx, int speed, int autoneg, int
buffer.hdr.cmd_or_resp.cmd_resv = FW_CEM_CMD_RESERVED;
switch (speed) {
+ case SPEED_40000:
+ buffer.speed = TXGBE_LINK_SPEED_40GB_FULL;
+ break;
case SPEED_25000:
buffer.speed = TXGBE_LINK_SPEED_25GB_FULL;
break;
case SPEED_10000:
buffer.speed = TXGBE_LINK_SPEED_10GB_FULL;
break;
+ default:
+ buffer.speed = TXGBE_LINK_SPEED_UNKNOWN;
+ break;
}
buffer.fec_mode = TXGBE_PHY_FEC_AUTO;
@@ -106,28 +151,33 @@ static int txgbe_set_phy_link_hostif(struct wx *wx, int speed, int autoneg, int
WX_HI_COMMAND_TIMEOUT, true);
}
-static void txgbe_get_link_capabilities(struct wx *wx)
+static void txgbe_get_link_capabilities(struct wx *wx, int *speed,
+ int *autoneg, int *duplex)
{
struct txgbe *txgbe = wx->priv;
- if (test_bit(PHY_INTERFACE_MODE_25GBASER, txgbe->sfp_interfaces))
- wx->adv_speed = SPEED_25000;
- else if (test_bit(PHY_INTERFACE_MODE_10GBASER, txgbe->sfp_interfaces))
- wx->adv_speed = SPEED_10000;
+ if (test_bit(PHY_INTERFACE_MODE_XLGMII, txgbe->link_interfaces))
+ *speed = SPEED_40000;
+ else if (test_bit(PHY_INTERFACE_MODE_25GBASER, txgbe->link_interfaces))
+ *speed = SPEED_25000;
+ else if (test_bit(PHY_INTERFACE_MODE_10GBASER, txgbe->link_interfaces))
+ *speed = SPEED_10000;
else
- wx->adv_speed = SPEED_UNKNOWN;
+ *speed = SPEED_UNKNOWN;
- wx->adv_duplex = wx->adv_speed == SPEED_UNKNOWN ?
- DUPLEX_HALF : DUPLEX_FULL;
+ *autoneg = phylink_test(txgbe->advertising, Autoneg);
+ *duplex = *speed == SPEED_UNKNOWN ? DUPLEX_HALF : DUPLEX_FULL;
}
-static void txgbe_get_phy_link(struct wx *wx, int *speed)
+static void txgbe_get_mac_link(struct wx *wx, int *speed)
{
u32 status;
status = rd32(wx, TXGBE_CFG_PORT_ST);
if (!(status & TXGBE_CFG_PORT_ST_LINK_UP))
*speed = SPEED_UNKNOWN;
+ else if (status & TXGBE_CFG_PORT_ST_LINK_AML_40G)
+ *speed = SPEED_40000;
else if (status & TXGBE_CFG_PORT_ST_LINK_AML_25G)
*speed = SPEED_25000;
else if (status & TXGBE_CFG_PORT_ST_LINK_AML_10G)
@@ -138,23 +188,11 @@ static void txgbe_get_phy_link(struct wx *wx, int *speed)
int txgbe_set_phy_link(struct wx *wx)
{
- int speed, err;
- u32 gpio;
+ int speed, autoneg, duplex, err;
- /* Check RX signal */
- gpio = rd32(wx, WX_GPIO_EXT);
- if (gpio & TXGBE_GPIOBIT_3)
- return -ENODEV;
+ txgbe_get_link_capabilities(wx, &speed, &autoneg, &duplex);
- txgbe_get_link_capabilities(wx);
- if (wx->adv_speed == SPEED_UNKNOWN)
- return -ENODEV;
-
- txgbe_get_phy_link(wx, &speed);
- if (speed == wx->adv_speed)
- return 0;
-
- err = txgbe_set_phy_link_hostif(wx, wx->adv_speed, 0, wx->adv_duplex);
+ err = txgbe_set_phy_link_hostif(wx, speed, autoneg, duplex);
if (err) {
wx_err(wx, "Failed to setup link\n");
return err;
@@ -163,40 +201,128 @@ int txgbe_set_phy_link(struct wx *wx)
return 0;
}
-static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sfp_id *id)
+static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id)
{
__ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
DECLARE_PHY_INTERFACE_MASK(interfaces);
struct txgbe *txgbe = wx->priv;
- if (id->com_25g_code & (TXGBE_SFF_25GBASESR_CAPABLE |
- TXGBE_SFF_25GBASEER_CAPABLE |
- TXGBE_SFF_25GBASELR_CAPABLE)) {
- phylink_set(modes, 25000baseSR_Full);
+ if (id->cable_tech & TXGBE_SFF_DA_PASSIVE_CABLE) {
+ txgbe->link_port = PORT_DA;
+ phylink_set(modes, Autoneg);
+ if (id->com_25g_code == TXGBE_SFF_25GBASECR_91FEC ||
+ id->com_25g_code == TXGBE_SFF_25GBASECR_74FEC ||
+ id->com_25g_code == TXGBE_SFF_25GBASECR_NOFEC) {
+ phylink_set(modes, 25000baseCR_Full);
+ phylink_set(modes, 10000baseCR_Full);
+ __set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ } else {
+ phylink_set(modes, 10000baseCR_Full);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
+ } else if (id->cable_tech & TXGBE_SFF_DA_ACTIVE_CABLE) {
+ txgbe->link_port = PORT_DA;
+ phylink_set(modes, Autoneg);
+ phylink_set(modes, 25000baseCR_Full);
__set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
+ } else {
+ if (id->com_25g_code == TXGBE_SFF_25GBASESR_CAPABLE ||
+ id->com_25g_code == TXGBE_SFF_25GBASEER_CAPABLE ||
+ id->com_25g_code == TXGBE_SFF_25GBASELR_CAPABLE) {
+ txgbe->link_port = PORT_FIBRE;
+ phylink_set(modes, 25000baseSR_Full);
+ __set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
+ }
+ if (id->com_10g_code & TXGBE_SFF_10GBASESR_CAPABLE) {
+ txgbe->link_port = PORT_FIBRE;
+ phylink_set(modes, 10000baseSR_Full);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
+ if (id->com_10g_code & TXGBE_SFF_10GBASELR_CAPABLE) {
+ txgbe->link_port = PORT_FIBRE;
+ phylink_set(modes, 10000baseLR_Full);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
}
- if (id->com_10g_code & TXGBE_SFF_10GBASESR_CAPABLE) {
- phylink_set(modes, 10000baseSR_Full);
- __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+
+ if (phy_interface_empty(interfaces)) {
+ wx_err(wx, "unsupported SFP module\n");
+ return -EINVAL;
}
- if (id->com_10g_code & TXGBE_SFF_10GBASELR_CAPABLE) {
- phylink_set(modes, 10000baseLR_Full);
+
+ phylink_set(modes, Pause);
+ phylink_set(modes, Asym_Pause);
+ phylink_set(modes, FIBRE);
+
+ if (!linkmode_equal(txgbe->link_support, modes)) {
+ linkmode_copy(txgbe->link_support, modes);
+ phy_interface_and(txgbe->link_interfaces,
+ wx->phylink_config.supported_interfaces,
+ interfaces);
+ linkmode_copy(txgbe->advertising, modes);
+
+ set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
+ }
+
+ return 0;
+}
+
+static int txgbe_qsfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(modes) = { 0, };
+ DECLARE_PHY_INTERFACE_MASK(interfaces);
+ struct txgbe *txgbe = wx->priv;
+
+ if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_CR4) {
+ txgbe->link_port = PORT_DA;
+ phylink_set(modes, Autoneg);
+ phylink_set(modes, 40000baseCR4_Full);
+ phylink_set(modes, 10000baseCR_Full);
+ __set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
}
+ if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_SR4) {
+ txgbe->link_port = PORT_FIBRE;
+ phylink_set(modes, 40000baseSR4_Full);
+ __set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
+ }
+ if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_LR4) {
+ txgbe->link_port = PORT_FIBRE;
+ phylink_set(modes, 40000baseLR4_Full);
+ __set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
+ }
+ if (id->transceiver_type & TXGBE_SFF_ETHERNET_40G_ACTIVE) {
+ txgbe->link_port = PORT_DA;
+ phylink_set(modes, Autoneg);
+ phylink_set(modes, 40000baseCR4_Full);
+ __set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
+ }
+ if (id->transceiver_type & TXGBE_SFF_ETHERNET_RSRVD) {
+ if (id->sff_opt1 & TXGBE_SFF_ETHERNET_100G_CR4) {
+ txgbe->link_port = PORT_DA;
+ phylink_set(modes, Autoneg);
+ phylink_set(modes, 40000baseCR4_Full);
+ phylink_set(modes, 25000baseCR_Full);
+ phylink_set(modes, 10000baseCR_Full);
+ __set_bit(PHY_INTERFACE_MODE_XLGMII, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_25GBASER, interfaces);
+ __set_bit(PHY_INTERFACE_MODE_10GBASER, interfaces);
+ }
+ }
if (phy_interface_empty(interfaces)) {
- wx_err(wx, "unsupported SFP module\n");
+ wx_err(wx, "unsupported QSFP module\n");
return -EINVAL;
}
phylink_set(modes, Pause);
phylink_set(modes, Asym_Pause);
phylink_set(modes, FIBRE);
- txgbe->link_port = PORT_FIBRE;
- if (!linkmode_equal(txgbe->sfp_support, modes)) {
- linkmode_copy(txgbe->sfp_support, modes);
- phy_interface_and(txgbe->sfp_interfaces,
+ if (!linkmode_equal(txgbe->link_support, modes)) {
+ linkmode_copy(txgbe->link_support, modes);
+ phy_interface_and(txgbe->link_interfaces,
wx->phylink_config.supported_interfaces,
interfaces);
linkmode_copy(txgbe->advertising, modes);
@@ -207,47 +333,53 @@ static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sfp_id *id)
return 0;
}
-int txgbe_identify_sfp(struct wx *wx)
+int txgbe_identify_module(struct wx *wx)
{
- struct txgbe_hic_i2c_read buffer;
- struct txgbe_sfp_id *id;
+ struct txgbe_hic_get_module_info buffer;
+ struct txgbe_sff_id *id;
int err = 0;
+ u32 mod_abs;
u32 gpio;
+ if (wx->mac.type == wx_mac_aml40)
+ mod_abs = TXGBE_GPIOBIT_4;
+ else
+ mod_abs = TXGBE_GPIOBIT_2;
+
gpio = rd32(wx, WX_GPIO_EXT);
- if (gpio & TXGBE_GPIOBIT_2)
+ if (gpio & mod_abs)
return -ENODEV;
- err = txgbe_identify_sfp_hostif(wx, &buffer);
+ err = txgbe_identify_module_hostif(wx, &buffer);
if (err) {
- wx_err(wx, "Failed to identify SFP module\n");
+ wx_err(wx, "Failed to identify module\n");
return err;
}
id = &buffer.id;
- if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP) {
- wx_err(wx, "Invalid SFP module\n");
+ if (id->identifier != TXGBE_SFF_IDENTIFIER_SFP &&
+ id->identifier != TXGBE_SFF_IDENTIFIER_QSFP &&
+ id->identifier != TXGBE_SFF_IDENTIFIER_QSFP_PLUS &&
+ id->identifier != TXGBE_SFF_IDENTIFIER_QSFP28) {
+ wx_err(wx, "Invalid module\n");
return -ENODEV;
}
- err = txgbe_sfp_to_linkmodes(wx, id);
- if (err)
- return err;
-
- if (gpio & TXGBE_GPIOBIT_3)
- set_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags);
+ if (id->transceiver_type == 0xFF)
+ return txgbe_sfp_to_linkmodes(wx, id);
- return 0;
+ return txgbe_qsfp_to_linkmodes(wx, id);
}
void txgbe_setup_link(struct wx *wx)
{
struct txgbe *txgbe = wx->priv;
- phy_interface_zero(txgbe->sfp_interfaces);
- linkmode_zero(txgbe->sfp_support);
+ phy_interface_zero(txgbe->link_interfaces);
+ linkmode_zero(txgbe->link_support);
- txgbe_identify_sfp(wx);
+ set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
+ wx_service_event_schedule(wx);
}
static void txgbe_get_link_state(struct phylink_config *config,
@@ -256,7 +388,7 @@ static void txgbe_get_link_state(struct phylink_config *config,
struct wx *wx = phylink_to_wx(config);
int speed;
- txgbe_get_phy_link(wx, &speed);
+ txgbe_get_mac_link(wx, &speed);
state->link = speed != SPEED_UNKNOWN;
state->speed = speed;
state->duplex = state->link ? DUPLEX_FULL : DUPLEX_UNKNOWN;
@@ -300,6 +432,9 @@ static void txgbe_mac_link_up_aml(struct phylink_config *config,
txcfg &= ~TXGBE_AML_MAC_TX_CFG_SPEED_MASK;
switch (speed) {
+ case SPEED_40000:
+ txcfg |= TXGBE_AML_MAC_TX_CFG_SPEED_40G;
+ break;
case SPEED_25000:
txcfg |= TXGBE_AML_MAC_TX_CFG_SPEED_25G;
break;
@@ -364,7 +499,18 @@ int txgbe_phylink_init_aml(struct txgbe *txgbe)
MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
config->get_fixed_state = txgbe_get_link_state;
- phy_mode = PHY_INTERFACE_MODE_25GBASER;
+ if (wx->mac.type == wx_mac_aml40) {
+ config->mac_capabilities |= MAC_40000FD;
+ phy_mode = PHY_INTERFACE_MODE_XLGMII;
+ __set_bit(PHY_INTERFACE_MODE_XLGMII, config->supported_interfaces);
+ state.speed = SPEED_40000;
+ state.duplex = DUPLEX_FULL;
+ } else {
+ phy_mode = PHY_INTERFACE_MODE_25GBASER;
+ state.speed = SPEED_25000;
+ state.duplex = DUPLEX_FULL;
+ }
+
__set_bit(PHY_INTERFACE_MODE_25GBASER, config->supported_interfaces);
__set_bit(PHY_INTERFACE_MODE_10GBASER, config->supported_interfaces);
@@ -372,8 +518,6 @@ int txgbe_phylink_init_aml(struct txgbe *txgbe)
if (IS_ERR(phylink))
return PTR_ERR(phylink);
- state.speed = SPEED_25000;
- state.duplex = DUPLEX_FULL;
err = phylink_set_fixed_link(phylink, &state);
if (err) {
wx_err(wx, "Failed to set fixed link\n");
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h
index 25d4971ca0d9..4f6df0ee860b 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h
@@ -7,8 +7,11 @@
void txgbe_gpio_init_aml(struct wx *wx);
irqreturn_t txgbe_gpio_irq_handler_aml(int irq, void *data);
int txgbe_test_hostif(struct wx *wx);
+int txgbe_read_eeprom_hostif(struct wx *wx,
+ struct txgbe_hic_i2c_read *buffer,
+ u32 length, u8 *data);
int txgbe_set_phy_link(struct wx *wx);
-int txgbe_identify_sfp(struct wx *wx);
+int txgbe_identify_module(struct wx *wx);
void txgbe_setup_link(struct wx *wx);
int txgbe_phylink_init_aml(struct txgbe *txgbe);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
index e285b088c7b2..f3cb00109529 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
@@ -10,6 +10,7 @@
#include "../libwx/wx_lib.h"
#include "txgbe_type.h"
#include "txgbe_fdir.h"
+#include "txgbe_aml.h"
#include "txgbe_ethtool.h"
int txgbe_get_link_ksettings(struct net_device *netdev,
@@ -19,9 +20,6 @@ int txgbe_get_link_ksettings(struct net_device *netdev,
struct txgbe *txgbe = wx->priv;
int err;
- if (wx->mac.type == wx_mac_aml40)
- return -EOPNOTSUPP;
-
err = wx_get_link_ksettings(netdev, cmd);
if (err)
return err;
@@ -30,8 +28,9 @@ int txgbe_get_link_ksettings(struct net_device *netdev,
return 0;
cmd->base.port = txgbe->link_port;
- cmd->base.autoneg = AUTONEG_DISABLE;
- linkmode_copy(cmd->link_modes.supported, txgbe->sfp_support);
+ cmd->base.autoneg = phylink_test(txgbe->advertising, Autoneg) ?
+ AUTONEG_ENABLE : AUTONEG_DISABLE;
+ linkmode_copy(cmd->link_modes.supported, txgbe->link_support);
linkmode_copy(cmd->link_modes.advertising, txgbe->advertising);
return 0;
@@ -536,6 +535,34 @@ static int txgbe_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
return ret;
}
+static int
+txgbe_get_module_eeprom_by_page(struct net_device *netdev,
+ const struct ethtool_module_eeprom *page_data,
+ struct netlink_ext_ack *extack)
+{
+ struct wx *wx = netdev_priv(netdev);
+ struct txgbe_hic_i2c_read buffer;
+ int err;
+
+ if (!test_bit(WX_FLAG_SWFW_RING, wx->flags))
+ return -EOPNOTSUPP;
+
+ buffer.length = cpu_to_be32(page_data->length);
+ buffer.offset = cpu_to_be32(page_data->offset);
+ buffer.page = page_data->page;
+ buffer.bank = page_data->bank;
+ buffer.i2c_address = page_data->i2c_address;
+
+ err = txgbe_read_eeprom_hostif(wx, &buffer, page_data->length,
+ page_data->data);
+ if (err) {
+ wx_err(wx, "Failed to read module EEPROM\n");
+ return err;
+ }
+
+ return page_data->length;
+}
+
static const struct ethtool_ops txgbe_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ |
@@ -570,6 +597,7 @@ static const struct ethtool_ops txgbe_ethtool_ops = {
.set_msglevel = wx_set_msglevel,
.get_ts_info = wx_get_ts_info,
.get_ts_stats = wx_get_ptp_stats,
+ .get_module_eeprom_by_page = txgbe_get_module_eeprom_by_page,
};
void txgbe_set_ethtool_ops(struct net_device *netdev)
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
index 3885283681ec..aa14958d439a 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c
@@ -23,7 +23,7 @@ void txgbe_irq_enable(struct wx *wx, bool queues)
{
u32 misc_ien = TXGBE_PX_MISC_IEN_MASK;
- if (wx->mac.type == wx_mac_aml) {
+ if (wx->mac.type != wx_mac_sp) {
misc_ien |= TXGBE_PX_MISC_GPIO;
txgbe_gpio_init_aml(wx);
}
@@ -201,10 +201,7 @@ static void txgbe_del_irq_domain(struct txgbe *txgbe)
void txgbe_free_misc_irq(struct txgbe *txgbe)
{
- if (txgbe->wx->mac.type == wx_mac_aml40)
- return;
-
- if (txgbe->wx->mac.type == wx_mac_aml)
+ if (txgbe->wx->mac.type != wx_mac_sp)
free_irq(txgbe->gpio_irq, txgbe);
free_irq(txgbe->link_irq, txgbe);
@@ -219,9 +216,6 @@ int txgbe_setup_misc_irq(struct txgbe *txgbe)
struct wx *wx = txgbe->wx;
int hwirq, err;
- if (wx->mac.type == wx_mac_aml40)
- goto skip_sp_irq;
-
txgbe->misc.nirqs = TXGBE_IRQ_MAX;
txgbe->misc.domain = irq_domain_create_simple(NULL, txgbe->misc.nirqs, 0,
&txgbe_misc_irq_domain_ops, txgbe);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index c4c4d70d8466..0de051450a82 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -89,21 +89,21 @@ static int txgbe_enumerate_functions(struct wx *wx)
return physfns;
}
-static void txgbe_sfp_detection_subtask(struct wx *wx)
+static void txgbe_module_detection_subtask(struct wx *wx)
{
int err;
- if (!test_bit(WX_FLAG_NEED_SFP_RESET, wx->flags))
+ if (!test_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags))
return;
- /* wait for SFP module ready */
+ /* wait for SFF module ready */
msleep(200);
- err = txgbe_identify_sfp(wx);
+ err = txgbe_identify_module(wx);
if (err)
return;
- clear_bit(WX_FLAG_NEED_SFP_RESET, wx->flags);
+ clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
}
static void txgbe_link_config_subtask(struct wx *wx)
@@ -128,7 +128,7 @@ static void txgbe_service_task(struct work_struct *work)
{
struct wx *wx = container_of(work, struct wx, service_task);
- txgbe_sfp_detection_subtask(wx);
+ txgbe_module_detection_subtask(wx);
txgbe_link_config_subtask(wx);
wx_service_event_complete(wx);
@@ -144,7 +144,6 @@ static void txgbe_init_service(struct wx *wx)
static void txgbe_up_complete(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
- u32 reg;
wx_control_hw(wx, true);
wx_configure_vectors(wx);
@@ -155,12 +154,8 @@ static void txgbe_up_complete(struct wx *wx)
switch (wx->mac.type) {
case wx_mac_aml40:
- reg = rd32(wx, TXGBE_AML_MAC_TX_CFG);
- reg &= ~TXGBE_AML_MAC_TX_CFG_SPEED_MASK;
- reg |= TXGBE_AML_MAC_TX_CFG_SPEED_40G;
- wr32(wx, WX_MAC_TX_CFG, reg);
- txgbe_enable_sec_tx_path(wx);
- netif_carrier_on(wx->netdev);
+ txgbe_setup_link(wx);
+ phylink_start(wx->phylink);
break;
case wx_mac_aml:
/* Enable TX laser */
@@ -276,7 +271,7 @@ void txgbe_down(struct wx *wx)
switch (wx->mac.type) {
case wx_mac_aml40:
- netif_carrier_off(wx->netdev);
+ phylink_stop(wx->phylink);
break;
case wx_mac_aml:
phylink_stop(wx->phylink);
@@ -398,6 +393,7 @@ static int txgbe_sw_init(struct wx *wx)
wx->configure_fdir = txgbe_configure_fdir;
set_bit(WX_FLAG_RSC_CAPABLE, wx->flags);
+ set_bit(WX_FLAG_RSC_ENABLED, wx->flags);
set_bit(WX_FLAG_MULTI_64_FUNC, wx->flags);
/* enable itr by default in dynamic mode */
@@ -423,6 +419,8 @@ static int txgbe_sw_init(struct wx *wx)
break;
case wx_mac_aml:
case wx_mac_aml40:
+ set_bit(WX_FLAG_RX_MERGE_ENABLED, wx->flags);
+ set_bit(WX_FLAG_TXHEAD_WB_ENABLED, wx->flags);
set_bit(WX_FLAG_SWFW_RING, wx->flags);
wx->swfw_index = 0;
break;
@@ -801,6 +799,8 @@ static int txgbe_probe(struct pci_dev *pdev,
netdev->features |= NETIF_F_HIGHDMA;
netdev->hw_features |= NETIF_F_GRO;
netdev->features |= NETIF_F_GRO;
+ netdev->hw_features |= NETIF_F_LRO;
+ netdev->features |= NETIF_F_LRO;
netdev->features |= NETIF_F_RX_UDP_TUNNEL_PORT;
netdev->priv_flags |= IFF_UNICAST_FLT;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
index 03f1b9bc604d..8ea7aa07ae4e 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_phy.c
@@ -579,7 +579,6 @@ int txgbe_init_phy(struct txgbe *txgbe)
switch (wx->mac.type) {
case wx_mac_aml40:
- return 0;
case wx_mac_aml:
return txgbe_phylink_init_aml(txgbe);
case wx_mac_sp:
@@ -653,7 +652,6 @@ void txgbe_remove_phy(struct txgbe *txgbe)
{
switch (txgbe->wx->mac.type) {
case wx_mac_aml40:
- return;
case wx_mac_aml:
phylink_destroy(txgbe->wx->phylink);
return;
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 41915d7dd372..82433e9cb0e3 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -98,6 +98,7 @@
/* Port cfg registers */
#define TXGBE_CFG_PORT_ST 0x14404
#define TXGBE_CFG_PORT_ST_LINK_UP BIT(0)
+#define TXGBE_CFG_PORT_ST_LINK_AML_40G BIT(2)
#define TXGBE_CFG_PORT_ST_LINK_AML_25G BIT(3)
#define TXGBE_CFG_PORT_ST_LINK_AML_10G BIT(4)
#define TXGBE_CFG_VXLAN 0x14410
@@ -314,10 +315,15 @@ void txgbe_up(struct wx *wx);
int txgbe_setup_tc(struct net_device *dev, u8 tc);
void txgbe_do_reset(struct net_device *netdev);
+#define TXGBE_LINK_SPEED_UNKNOWN 0
#define TXGBE_LINK_SPEED_10GB_FULL 4
#define TXGBE_LINK_SPEED_25GB_FULL 0x10
+#define TXGBE_LINK_SPEED_40GB_FULL 0x20
#define TXGBE_SFF_IDENTIFIER_SFP 0x3
+#define TXGBE_SFF_IDENTIFIER_QSFP 0xC
+#define TXGBE_SFF_IDENTIFIER_QSFP_PLUS 0xD
+#define TXGBE_SFF_IDENTIFIER_QSFP28 0x11
#define TXGBE_SFF_DA_PASSIVE_CABLE 0x4
#define TXGBE_SFF_DA_ACTIVE_CABLE 0x8
#define TXGBE_SFF_DA_SPEC_ACTIVE_LIMIT 0x4
@@ -330,6 +336,12 @@ void txgbe_do_reset(struct net_device *netdev);
#define TXGBE_SFF_25GBASECR_91FEC 0xB
#define TXGBE_SFF_25GBASECR_74FEC 0xC
#define TXGBE_SFF_25GBASECR_NOFEC 0xD
+#define TXGBE_SFF_ETHERNET_RSRVD BIT(7)
+#define TXGBE_SFF_ETHERNET_40G_CR4 BIT(3)
+#define TXGBE_SFF_ETHERNET_40G_SR4 BIT(2)
+#define TXGBE_SFF_ETHERNET_40G_LR4 BIT(1)
+#define TXGBE_SFF_ETHERNET_40G_ACTIVE BIT(0)
+#define TXGBE_SFF_ETHERNET_100G_CR4 0xB
#define TXGBE_PHY_FEC_RS BIT(0)
#define TXGBE_PHY_FEC_BASER BIT(1)
@@ -340,9 +352,10 @@ void txgbe_do_reset(struct net_device *netdev);
#define FW_PHY_GET_LINK_CMD 0xC0
#define FW_PHY_SET_LINK_CMD 0xC1
-#define FW_READ_SFP_INFO_CMD 0xC5
+#define FW_GET_MODULE_INFO_CMD 0xC5
+#define FW_READ_EEPROM_CMD 0xC6
-struct txgbe_sfp_id {
+struct txgbe_sff_id {
u8 identifier; /* A0H 0x00 */
u8 com_1g_code; /* A0H 0x06 */
u8 com_10g_code; /* A0H 0x03 */
@@ -352,12 +365,14 @@ struct txgbe_sfp_id {
u8 vendor_oui0; /* A0H 0x25 */
u8 vendor_oui1; /* A0H 0x26 */
u8 vendor_oui2; /* A0H 0x27 */
- u8 reserved[3];
+ u8 transceiver_type; /* A0H 0x83 */
+ u8 sff_opt1; /* A0H 0xC0 */
+ u8 reserved[5];
};
-struct txgbe_hic_i2c_read {
+struct txgbe_hic_get_module_info {
struct wx_hic_hdr hdr;
- struct txgbe_sfp_id id;
+ struct txgbe_sff_id id;
};
struct txgbe_hic_ephy_setlink {
@@ -380,6 +395,16 @@ struct txgbe_hic_ephy_getlink {
u8 resv[6];
};
+struct txgbe_hic_i2c_read {
+ struct wx_hic_hdr hdr;
+ __be32 offset;
+ __be32 length;
+ u8 page;
+ u8 bank;
+ u8 i2c_address;
+ u8 resv;
+};
+
#define NODE_PROP(_NAME, _PROP) \
(const struct software_node) { \
.name = _NAME, \
@@ -448,8 +473,8 @@ struct txgbe {
int fdir_filter_count;
spinlock_t fdir_perfect_lock; /* spinlock for FDIR */
- DECLARE_PHY_INTERFACE_MASK(sfp_interfaces);
- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
+ DECLARE_PHY_INTERFACE_MASK(link_interfaces);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(link_support);
__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
u8 link_port;
};
diff --git a/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c b/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c
index 72663e3c4205..37e4ec487afd 100644
--- a/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c
+++ b/drivers/net/ethernet/wangxun/txgbevf/txgbevf_main.c
@@ -157,6 +157,18 @@ static int txgbevf_sw_init(struct wx *wx)
wx->set_num_queues = txgbevf_set_num_queues;
+ switch (wx->mac.type) {
+ case wx_mac_sp:
+ break;
+ case wx_mac_aml:
+ case wx_mac_aml40:
+ set_bit(WX_FLAG_RX_MERGE_ENABLED, wx->flags);
+ set_bit(WX_FLAG_TXHEAD_WB_ENABLED, wx->flags);
+ break;
+ default:
+ break;
+ }
+
return 0;
err_reset_hw:
kfree(wx->vfinfo);
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 5cb59d72bc82..4213c3b2d532 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -633,7 +633,7 @@ static void gtp1u_build_echo_msg(struct gtp1_header_long *hdr, __u8 msg_type)
hdr->tid = 0;
/* seq, npdu and next should be counted to the length of the GTP packet
- * that's why szie of gtp1_header should be subtracted,
+ * that's why size of gtp1_header should be subtracted,
* not size of gtp1_header_long.
*/
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index 39c892e46cb0..3d47d749ef9f 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -1624,22 +1624,15 @@ netvsc_get_rxfh_fields(struct net_device *ndev,
return 0;
}
-static int
-netvsc_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
- u32 *rules)
+static u32 netvsc_get_rx_ring_count(struct net_device *dev)
{
struct net_device_context *ndc = netdev_priv(dev);
struct netvsc_device *nvdev = rtnl_dereference(ndc->nvdev);
if (!nvdev)
- return -ENODEV;
-
- switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- info->data = nvdev->num_chn;
return 0;
- }
- return -EOPNOTSUPP;
+
+ return nvdev->num_chn;
}
static int
@@ -1969,7 +1962,7 @@ static const struct ethtool_ops ethtool_ops = {
.get_channels = netvsc_get_channels,
.set_channels = netvsc_set_channels,
.get_ts_info = ethtool_op_get_ts_info,
- .get_rxnfc = netvsc_get_rxnfc,
+ .get_rx_ring_count = netvsc_get_rx_ring_count,
.get_rxfh_key_size = netvsc_get_rxfh_key_size,
.get_rxfh_indir_size = netvsc_rss_indir_size,
.get_rxfh = netvsc_get_rxfh,
diff --git a/drivers/net/ipa/ipa_interrupt.c b/drivers/net/ipa/ipa_interrupt.c
index 245a06997055..8336596b1247 100644
--- a/drivers/net/ipa/ipa_interrupt.c
+++ b/drivers/net/ipa/ipa_interrupt.c
@@ -149,7 +149,6 @@ static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
iowrite32(pending, ipa->reg_virt + reg_offset(reg));
}
out_power_put:
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return IRQ_HANDLED;
diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c
index 25500c5a6928..95a61bae3124 100644
--- a/drivers/net/ipa/ipa_main.c
+++ b/drivers/net/ipa/ipa_main.c
@@ -903,7 +903,6 @@ static int ipa_probe(struct platform_device *pdev)
if (ret)
goto err_deconfig;
done:
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/drivers/net/ipa/ipa_modem.c b/drivers/net/ipa/ipa_modem.c
index 8fe0d0e1a00f..9b136f6b8b4a 100644
--- a/drivers/net/ipa/ipa_modem.c
+++ b/drivers/net/ipa/ipa_modem.c
@@ -71,7 +71,6 @@ static int ipa_open(struct net_device *netdev)
netif_start_queue(netdev);
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return 0;
@@ -102,7 +101,6 @@ static int ipa_stop(struct net_device *netdev)
ipa_endpoint_disable_one(priv->rx);
ipa_endpoint_disable_one(priv->tx);
out_power_put:
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return 0;
@@ -175,7 +173,6 @@ ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev)
ret = ipa_endpoint_skb_tx(endpoint, skb);
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
if (ret) {
@@ -432,7 +429,6 @@ static void ipa_modem_crashed(struct ipa *ipa)
dev_err(dev, "error %d zeroing modem memory regions\n", ret);
out_power_put:
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
}
diff --git a/drivers/net/ipa/ipa_smp2p.c b/drivers/net/ipa/ipa_smp2p.c
index fcaadd111a8a..420098796eec 100644
--- a/drivers/net/ipa/ipa_smp2p.c
+++ b/drivers/net/ipa/ipa_smp2p.c
@@ -171,7 +171,6 @@ static irqreturn_t ipa_smp2p_modem_setup_ready_isr(int irq, void *dev_id)
WARN(ret != 0, "error %d from ipa_setup()\n", ret);
out_power_put:
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
return IRQ_HANDLED;
@@ -213,7 +212,6 @@ static void ipa_smp2p_power_release(struct ipa *ipa)
if (!ipa->smp2p->power_on)
return;
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
ipa->smp2p->power_on = false;
}
diff --git a/drivers/net/ipa/ipa_uc.c b/drivers/net/ipa/ipa_uc.c
index 2963db83ab6b..dc7e92f2a4fb 100644
--- a/drivers/net/ipa/ipa_uc.c
+++ b/drivers/net/ipa/ipa_uc.c
@@ -158,7 +158,6 @@ static void ipa_uc_response_hdlr(struct ipa *ipa)
if (ipa->uc_powered) {
ipa->uc_loaded = true;
ipa_power_retention(ipa, true);
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
ipa->uc_powered = false;
} else {
@@ -203,7 +202,6 @@ void ipa_uc_deconfig(struct ipa *ipa)
if (!ipa->uc_powered)
return;
- pm_runtime_mark_last_busy(dev);
(void)pm_runtime_put_autosuspend(dev);
}
diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c
index d7e3ddbcab6f..dea411e132db 100644
--- a/drivers/net/ipvlan/ipvlan_core.c
+++ b/drivers/net/ipvlan/ipvlan_core.c
@@ -52,8 +52,8 @@ static u8 ipvlan_get_v4_hash(const void *iaddr)
{
const struct in_addr *ip4_addr = iaddr;
- return jhash_1word(ip4_addr->s_addr, ipvlan_jhash_secret) &
- IPVLAN_HASH_MASK;
+ return jhash_1word((__force u32)ip4_addr->s_addr, ipvlan_jhash_secret) &
+ IPVLAN_HASH_MASK;
}
static bool addr_equal(bool is_v6, struct ipvl_addr *addr, const void *iaddr)
diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c
index 36ccc53b1797..ef860cfc629f 100644
--- a/drivers/net/mctp/mctp-usb.c
+++ b/drivers/net/mctp/mctp-usb.c
@@ -96,11 +96,13 @@ static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb,
skb->data, skb->len,
mctp_usb_out_complete, skb);
+ /* Stops TX queue first to prevent race condition with URB complete */
+ netif_stop_queue(dev);
rc = usb_submit_urb(urb, GFP_ATOMIC);
- if (rc)
+ if (rc) {
+ netif_wake_queue(dev);
goto err_drop;
- else
- netif_stop_queue(dev);
+ }
return NETDEV_TX_OK;
diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c
index 9b41d4697a40..ba7091518265 100644
--- a/drivers/net/mdio/fwnode_mdio.c
+++ b/drivers/net/mdio/fwnode_mdio.c
@@ -92,11 +92,6 @@ int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio,
if (fwnode_property_read_bool(child, "broken-turn-around"))
mdio->phy_ignore_ta_mask |= 1 << addr;
- fwnode_property_read_u32(child, "reset-assert-us",
- &phy->mdio.reset_assert_delay);
- fwnode_property_read_u32(child, "reset-deassert-us",
- &phy->mdio.reset_deassert_delay);
-
/* Associate the fwnode with the device structure so it
* can be looked up later
*/
diff --git a/drivers/net/mdio/mdio-airoha.c b/drivers/net/mdio/mdio-airoha.c
index 1dc9939c8d7d..52e7475121ea 100644
--- a/drivers/net/mdio/mdio-airoha.c
+++ b/drivers/net/mdio/mdio-airoha.c
@@ -219,6 +219,8 @@ static int airoha_mdio_probe(struct platform_device *pdev)
priv = bus->priv;
priv->base_addr = addr;
priv->regmap = device_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
priv->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(priv->clk))
diff --git a/drivers/net/mdio/of_mdio.c b/drivers/net/mdio/of_mdio.c
index 1357348e01d5..b8d298c04d3f 100644
--- a/drivers/net/mdio/of_mdio.c
+++ b/drivers/net/mdio/of_mdio.c
@@ -63,14 +63,11 @@ static int of_mdiobus_register_device(struct mii_bus *mdio,
/* Associate the OF node with the device structure so it
* can be looked up later.
*/
- fwnode_handle_get(fwnode);
- device_set_node(&mdiodev->dev, fwnode);
+ device_set_node(&mdiodev->dev, fwnode_handle_get(fwnode));
/* All data is now stored in the mdiodev struct; register it. */
rc = mdio_device_register(mdiodev);
if (rc) {
- device_set_node(&mdiodev->dev, NULL);
- fwnode_handle_put(fwnode);
mdio_device_free(mdiodev);
return rc;
}
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 194570443493..9cb4dfc242f5 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -50,7 +50,7 @@ MODULE_LICENSE("GPL");
/* The number 3 comes from userdata entry format characters (' ', '=', '\n') */
#define MAX_EXTRADATA_NAME_LEN (MAX_EXTRADATA_ENTRY_LEN - \
MAX_EXTRADATA_VALUE_LEN - 3)
-#define MAX_EXTRADATA_ITEMS 16
+#define MAX_USERDATA_ITEMS 256
#define MAX_PRINT_CHUNK 1000
static char config[MAX_PARAM_LENGTH];
@@ -115,6 +115,8 @@ enum sysdata_feature {
SYSDATA_RELEASE = BIT(2),
/* Include a per-target message ID as part of sysdata */
SYSDATA_MSGID = BIT(3),
+ /* Sentinel: highest bit position */
+ MAX_SYSDATA_ITEMS = 4,
};
/**
@@ -122,8 +124,9 @@ enum sysdata_feature {
* @list: Links this target into the target_list.
* @group: Links us into the configfs subsystem hierarchy.
* @userdata_group: Links to the userdata configfs hierarchy
- * @extradata_complete: Cached, formatted string of append
- * @userdata_length: String length of usedata in extradata_complete.
+ * @userdata: Cached, formatted string of append
+ * @userdata_length: String length of userdata.
+ * @sysdata: Cached, formatted string of append
* @sysdata_fields: Sysdata features enabled.
* @msgcounter: Message sent counter.
* @stats: Packet send stats for the target. Used for debugging.
@@ -152,8 +155,10 @@ struct netconsole_target {
#ifdef CONFIG_NETCONSOLE_DYNAMIC
struct config_group group;
struct config_group userdata_group;
- char extradata_complete[MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS];
+ char *userdata;
size_t userdata_length;
+ char sysdata[MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS];
+
/* bit-wise with sysdata_feature bits */
u32 sysdata_fields;
/* protected by target_list_lock */
@@ -802,28 +807,14 @@ out_unlock:
return ret;
}
-/* Count number of entries we have in extradata.
- * This is important because the extradata_complete only supports
- * MAX_EXTRADATA_ITEMS entries. Before enabling any new {user,sys}data
- * feature, number of entries needs to checked for available space.
+/* Count number of entries we have in userdata.
+ * This is important because userdata only supports MAX_USERDATA_ITEMS
+ * entries. Before enabling any new userdata feature, number of entries needs
+ * to checked for available space.
*/
-static size_t count_extradata_entries(struct netconsole_target *nt)
+static size_t count_userdata_entries(struct netconsole_target *nt)
{
- size_t entries;
-
- /* Userdata entries */
- entries = list_count_nodes(&nt->userdata_group.cg_children);
- /* Plus sysdata entries */
- if (nt->sysdata_fields & SYSDATA_CPU_NR)
- entries += 1;
- if (nt->sysdata_fields & SYSDATA_TASKNAME)
- entries += 1;
- if (nt->sysdata_fields & SYSDATA_RELEASE)
- entries += 1;
- if (nt->sysdata_fields & SYSDATA_MSGID)
- entries += 1;
-
- return entries;
+ return list_count_nodes(&nt->userdata_group.cg_children);
}
static ssize_t remote_mac_store(struct config_item *item, const char *buf,
@@ -884,40 +875,77 @@ static ssize_t userdatum_value_show(struct config_item *item, char *buf)
return sysfs_emit(buf, "%s\n", &(to_userdatum(item)->value[0]));
}
-static void update_userdata(struct netconsole_target *nt)
+/* Navigate configfs and calculate the lentgh of the formatted string
+ * representing userdata.
+ * Must be called holding netconsole_subsys.su_mutex
+ */
+static int calc_userdata_len(struct netconsole_target *nt)
{
- int complete_idx = 0, child_count = 0;
+ struct userdatum *udm_item;
+ struct config_item *item;
struct list_head *entry;
-
- /* Clear the current string in case the last userdatum was deleted */
- nt->userdata_length = 0;
- nt->extradata_complete[0] = 0;
+ int len = 0;
list_for_each(entry, &nt->userdata_group.cg_children) {
- struct userdatum *udm_item;
- struct config_item *item;
+ item = container_of(entry, struct config_item, ci_entry);
+ udm_item = to_userdatum(item);
+ /* Skip userdata with no value set */
+ if (udm_item->value[0]) {
+ len += snprintf(NULL, 0, " %s=%s\n", item->ci_name,
+ udm_item->value);
+ }
+ }
+ return len;
+}
- if (WARN_ON_ONCE(child_count >= MAX_EXTRADATA_ITEMS))
- break;
- child_count++;
+static int update_userdata(struct netconsole_target *nt)
+{
+ struct userdatum *udm_item;
+ struct config_item *item;
+ struct list_head *entry;
+ char *old_buf = NULL;
+ char *new_buf = NULL;
+ unsigned long flags;
+ int offset = 0;
+ int len;
+
+ /* Calculate required buffer size */
+ len = calc_userdata_len(nt);
+ if (WARN_ON_ONCE(len > MAX_EXTRADATA_ENTRY_LEN * MAX_USERDATA_ITEMS))
+ return -ENOSPC;
+
+ /* Allocate new buffer */
+ if (len) {
+ new_buf = kmalloc(len + 1, GFP_KERNEL);
+ if (!new_buf)
+ return -ENOMEM;
+ }
+
+ /* Write userdata to new buffer */
+ list_for_each(entry, &nt->userdata_group.cg_children) {
item = container_of(entry, struct config_item, ci_entry);
udm_item = to_userdatum(item);
-
/* Skip userdata with no value set */
- if (strnlen(udm_item->value, MAX_EXTRADATA_VALUE_LEN) == 0)
- continue;
-
- /* This doesn't overflow extradata_complete since it will write
- * one entry length (1/MAX_EXTRADATA_ITEMS long), entry count is
- * checked to not exceed MAX items with child_count above
- */
- complete_idx += scnprintf(&nt->extradata_complete[complete_idx],
- MAX_EXTRADATA_ENTRY_LEN, " %s=%s\n",
- item->ci_name, udm_item->value);
+ if (udm_item->value[0]) {
+ offset += scnprintf(&new_buf[offset], len + 1 - offset,
+ " %s=%s\n", item->ci_name,
+ udm_item->value);
+ }
}
- nt->userdata_length = strnlen(nt->extradata_complete,
- sizeof(nt->extradata_complete));
+
+ WARN_ON_ONCE(offset != len);
+
+ /* Switch to new buffer and free old buffer */
+ spin_lock_irqsave(&target_list_lock, flags);
+ old_buf = nt->userdata;
+ nt->userdata = new_buf;
+ nt->userdata_length = offset;
+ spin_unlock_irqrestore(&target_list_lock, flags);
+
+ kfree(old_buf);
+
+ return 0;
}
static ssize_t userdatum_value_store(struct config_item *item, const char *buf,
@@ -931,6 +959,7 @@ static ssize_t userdatum_value_store(struct config_item *item, const char *buf,
if (count > MAX_EXTRADATA_VALUE_LEN)
return -EMSGSIZE;
+ mutex_lock(&netconsole_subsys.su_mutex);
mutex_lock(&dynamic_netconsole_mutex);
ret = strscpy(udm->value, buf, sizeof(udm->value));
@@ -940,10 +969,13 @@ static ssize_t userdatum_value_store(struct config_item *item, const char *buf,
ud = to_userdata(item->ci_parent);
nt = userdata_to_target(ud);
- update_userdata(nt);
+ ret = update_userdata(nt);
+ if (ret < 0)
+ goto out_unlock;
ret = count;
out_unlock:
mutex_unlock(&dynamic_netconsole_mutex);
+ mutex_unlock(&netconsole_subsys.su_mutex);
return ret;
}
@@ -955,7 +987,7 @@ static void disable_sysdata_feature(struct netconsole_target *nt,
enum sysdata_feature feature)
{
nt->sysdata_fields &= ~feature;
- nt->extradata_complete[nt->userdata_length] = 0;
+ nt->sysdata[0] = 0;
}
static ssize_t sysdata_msgid_enabled_store(struct config_item *item,
@@ -969,17 +1001,12 @@ static ssize_t sysdata_msgid_enabled_store(struct config_item *item,
if (ret)
return ret;
+ mutex_lock(&netconsole_subsys.su_mutex);
mutex_lock(&dynamic_netconsole_mutex);
curr = !!(nt->sysdata_fields & SYSDATA_MSGID);
if (msgid_enabled == curr)
goto unlock_ok;
- if (msgid_enabled &&
- count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
- ret = -ENOSPC;
- goto unlock;
- }
-
if (msgid_enabled)
nt->sysdata_fields |= SYSDATA_MSGID;
else
@@ -987,8 +1014,8 @@ static ssize_t sysdata_msgid_enabled_store(struct config_item *item,
unlock_ok:
ret = strnlen(buf, count);
-unlock:
mutex_unlock(&dynamic_netconsole_mutex);
+ mutex_unlock(&netconsole_subsys.su_mutex);
return ret;
}
@@ -1003,17 +1030,12 @@ static ssize_t sysdata_release_enabled_store(struct config_item *item,
if (ret)
return ret;
+ mutex_lock(&netconsole_subsys.su_mutex);
mutex_lock(&dynamic_netconsole_mutex);
curr = !!(nt->sysdata_fields & SYSDATA_RELEASE);
if (release_enabled == curr)
goto unlock_ok;
- if (release_enabled &&
- count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
- ret = -ENOSPC;
- goto unlock;
- }
-
if (release_enabled)
nt->sysdata_fields |= SYSDATA_RELEASE;
else
@@ -1021,8 +1043,8 @@ static ssize_t sysdata_release_enabled_store(struct config_item *item,
unlock_ok:
ret = strnlen(buf, count);
-unlock:
mutex_unlock(&dynamic_netconsole_mutex);
+ mutex_unlock(&netconsole_subsys.su_mutex);
return ret;
}
@@ -1037,17 +1059,12 @@ static ssize_t sysdata_taskname_enabled_store(struct config_item *item,
if (ret)
return ret;
+ mutex_lock(&netconsole_subsys.su_mutex);
mutex_lock(&dynamic_netconsole_mutex);
curr = !!(nt->sysdata_fields & SYSDATA_TASKNAME);
if (taskname_enabled == curr)
goto unlock_ok;
- if (taskname_enabled &&
- count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
- ret = -ENOSPC;
- goto unlock;
- }
-
if (taskname_enabled)
nt->sysdata_fields |= SYSDATA_TASKNAME;
else
@@ -1055,8 +1072,8 @@ static ssize_t sysdata_taskname_enabled_store(struct config_item *item,
unlock_ok:
ret = strnlen(buf, count);
-unlock:
mutex_unlock(&dynamic_netconsole_mutex);
+ mutex_unlock(&netconsole_subsys.su_mutex);
return ret;
}
@@ -1072,34 +1089,25 @@ static ssize_t sysdata_cpu_nr_enabled_store(struct config_item *item,
if (ret)
return ret;
+ mutex_lock(&netconsole_subsys.su_mutex);
mutex_lock(&dynamic_netconsole_mutex);
curr = !!(nt->sysdata_fields & SYSDATA_CPU_NR);
if (cpu_nr_enabled == curr)
/* no change requested */
goto unlock_ok;
- if (cpu_nr_enabled &&
- count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS) {
- /* user wants the new feature, but there is no space in the
- * buffer.
- */
- ret = -ENOSPC;
- goto unlock;
- }
-
if (cpu_nr_enabled)
nt->sysdata_fields |= SYSDATA_CPU_NR;
else
- /* This is special because extradata_complete might have
- * remaining data from previous sysdata, and it needs to be
- * cleaned.
+ /* This is special because sysdata might have remaining data
+ * from previous sysdata, and it needs to be cleaned.
*/
disable_sysdata_feature(nt, SYSDATA_CPU_NR);
unlock_ok:
ret = strnlen(buf, count);
-unlock:
mutex_unlock(&dynamic_netconsole_mutex);
+ mutex_unlock(&netconsole_subsys.su_mutex);
return ret;
}
@@ -1141,7 +1149,7 @@ static struct config_item *userdatum_make_item(struct config_group *group,
ud = to_userdata(&group->cg_item);
nt = userdata_to_target(ud);
- if (count_extradata_entries(nt) >= MAX_EXTRADATA_ITEMS)
+ if (count_userdata_entries(nt) >= MAX_USERDATA_ITEMS)
return ERR_PTR(-ENOSPC);
udm = kzalloc(sizeof(*udm), GFP_KERNEL);
@@ -1219,7 +1227,10 @@ static struct configfs_attribute *netconsole_target_attrs[] = {
static void netconsole_target_release(struct config_item *item)
{
- kfree(to_target(item));
+ struct netconsole_target *nt = to_target(item);
+
+ kfree(nt->userdata);
+ kfree(nt);
}
static struct configfs_item_operations netconsole_target_item_ops = {
@@ -1348,22 +1359,21 @@ static void populate_configfs_item(struct netconsole_target *nt,
static int sysdata_append_cpu_nr(struct netconsole_target *nt, int offset)
{
- /* Append cpu=%d at extradata_complete after userdata str */
- return scnprintf(&nt->extradata_complete[offset],
+ return scnprintf(&nt->sysdata[offset],
MAX_EXTRADATA_ENTRY_LEN, " cpu=%u\n",
raw_smp_processor_id());
}
static int sysdata_append_taskname(struct netconsole_target *nt, int offset)
{
- return scnprintf(&nt->extradata_complete[offset],
+ return scnprintf(&nt->sysdata[offset],
MAX_EXTRADATA_ENTRY_LEN, " taskname=%s\n",
current->comm);
}
static int sysdata_append_release(struct netconsole_target *nt, int offset)
{
- return scnprintf(&nt->extradata_complete[offset],
+ return scnprintf(&nt->sysdata[offset],
MAX_EXTRADATA_ENTRY_LEN, " release=%s\n",
init_utsname()->release);
}
@@ -1371,46 +1381,36 @@ static int sysdata_append_release(struct netconsole_target *nt, int offset)
static int sysdata_append_msgid(struct netconsole_target *nt, int offset)
{
wrapping_assign_add(nt->msgcounter, 1);
- return scnprintf(&nt->extradata_complete[offset],
+ return scnprintf(&nt->sysdata[offset],
MAX_EXTRADATA_ENTRY_LEN, " msgid=%u\n",
nt->msgcounter);
}
/*
- * prepare_extradata - append sysdata at extradata_complete in runtime
+ * prepare_sysdata - append sysdata in runtime
* @nt: target to send message to
*/
-static int prepare_extradata(struct netconsole_target *nt)
+static int prepare_sysdata(struct netconsole_target *nt)
{
- int extradata_len;
-
- /* userdata was appended when configfs write helper was called
- * by update_userdata().
- */
- extradata_len = nt->userdata_length;
+ int sysdata_len = 0;
if (!nt->sysdata_fields)
goto out;
if (nt->sysdata_fields & SYSDATA_CPU_NR)
- extradata_len += sysdata_append_cpu_nr(nt, extradata_len);
+ sysdata_len += sysdata_append_cpu_nr(nt, sysdata_len);
if (nt->sysdata_fields & SYSDATA_TASKNAME)
- extradata_len += sysdata_append_taskname(nt, extradata_len);
+ sysdata_len += sysdata_append_taskname(nt, sysdata_len);
if (nt->sysdata_fields & SYSDATA_RELEASE)
- extradata_len += sysdata_append_release(nt, extradata_len);
+ sysdata_len += sysdata_append_release(nt, sysdata_len);
if (nt->sysdata_fields & SYSDATA_MSGID)
- extradata_len += sysdata_append_msgid(nt, extradata_len);
+ sysdata_len += sysdata_append_msgid(nt, sysdata_len);
- WARN_ON_ONCE(extradata_len >
- MAX_EXTRADATA_ENTRY_LEN * MAX_EXTRADATA_ITEMS);
+ WARN_ON_ONCE(sysdata_len >
+ MAX_EXTRADATA_ENTRY_LEN * MAX_SYSDATA_ITEMS);
out:
- return extradata_len;
-}
-#else /* CONFIG_NETCONSOLE_DYNAMIC not set */
-static int prepare_extradata(struct netconsole_target *nt)
-{
- return 0;
+ return sysdata_len;
}
#endif /* CONFIG_NETCONSOLE_DYNAMIC */
@@ -1512,11 +1512,13 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt,
int msg_len,
int release_len)
{
- const char *extradata = NULL;
+ const char *userdata = NULL;
+ const char *sysdata = NULL;
const char *release;
#ifdef CONFIG_NETCONSOLE_DYNAMIC
- extradata = nt->extradata_complete;
+ userdata = nt->userdata;
+ sysdata = nt->sysdata;
#endif
if (release_len) {
@@ -1528,10 +1530,15 @@ static void send_msg_no_fragmentation(struct netconsole_target *nt,
memcpy(nt->buf, msg, msg_len);
}
- if (extradata)
+ if (userdata)
msg_len += scnprintf(&nt->buf[msg_len],
- MAX_PRINT_CHUNK - msg_len,
- "%s", extradata);
+ MAX_PRINT_CHUNK - msg_len, "%s",
+ userdata);
+
+ if (sysdata)
+ msg_len += scnprintf(&nt->buf[msg_len],
+ MAX_PRINT_CHUNK - msg_len, "%s",
+ sysdata);
send_udp(nt, nt->buf, msg_len);
}
@@ -1545,89 +1552,93 @@ static void append_release(char *buf)
}
static void send_fragmented_body(struct netconsole_target *nt,
- const char *msgbody, int header_len,
- int msgbody_len, int extradata_len)
+ const char *msgbody_ptr, int header_len,
+ int msgbody_len, int sysdata_len)
{
- int sent_extradata, preceding_bytes;
- const char *extradata = NULL;
- int body_len, offset = 0;
+ const char *userdata_ptr = NULL;
+ const char *sysdata_ptr = NULL;
+ int data_len, data_sent = 0;
+ int userdata_offset = 0;
+ int sysdata_offset = 0;
+ int msgbody_offset = 0;
+ int userdata_len = 0;
#ifdef CONFIG_NETCONSOLE_DYNAMIC
- extradata = nt->extradata_complete;
+ userdata_ptr = nt->userdata;
+ sysdata_ptr = nt->sysdata;
+ userdata_len = nt->userdata_length;
#endif
+ if (WARN_ON_ONCE(!userdata_ptr && userdata_len != 0))
+ return;
- /* body_len represents the number of bytes that will be sent. This is
+ if (WARN_ON_ONCE(!sysdata_ptr && sysdata_len != 0))
+ return;
+
+ /* data_len represents the number of bytes that will be sent. This is
* bigger than MAX_PRINT_CHUNK, thus, it will be split in multiple
* packets
*/
- body_len = msgbody_len + extradata_len;
+ data_len = msgbody_len + userdata_len + sysdata_len;
/* In each iteration of the while loop below, we send a packet
- * containing the header and a portion of the body. The body is
- * composed of two parts: msgbody and extradata. We keep track of how
- * many bytes have been sent so far using the offset variable, which
- * ranges from 0 to the total length of the body.
+ * containing the header and a portion of the data. The data is
+ * composed of three parts: msgbody, userdata, and sysdata.
+ * We keep track of how many bytes have been sent from each part using
+ * the *_offset variables.
+ * We keep track of how many bytes have been sent overall using the
+ * data_sent variable, which ranges from 0 to the total bytes to be
+ * sent.
*/
- while (offset < body_len) {
- int this_header = header_len;
- bool msgbody_written = false;
- int this_offset = 0;
+ while (data_sent < data_len) {
+ int userdata_left = userdata_len - userdata_offset;
+ int sysdata_left = sysdata_len - sysdata_offset;
+ int msgbody_left = msgbody_len - msgbody_offset;
+ int buf_offset = 0;
int this_chunk = 0;
- this_header += scnprintf(nt->buf + this_header,
- MAX_PRINT_CHUNK - this_header,
- ",ncfrag=%d/%d;", offset,
- body_len);
-
- /* Not all msgbody data has been written yet */
- if (offset < msgbody_len) {
- this_chunk = min(msgbody_len - offset,
- MAX_PRINT_CHUNK - this_header);
- if (WARN_ON_ONCE(this_chunk <= 0))
- return;
- memcpy(nt->buf + this_header, msgbody + offset,
- this_chunk);
- this_offset += this_chunk;
+ /* header is already populated in nt->buf, just append to it */
+ buf_offset = header_len;
+
+ buf_offset += scnprintf(nt->buf + buf_offset,
+ MAX_PRINT_CHUNK - buf_offset,
+ ",ncfrag=%d/%d;", data_sent,
+ data_len);
+
+ /* append msgbody first */
+ this_chunk = min(msgbody_left, MAX_PRINT_CHUNK - buf_offset);
+ memcpy(nt->buf + buf_offset, msgbody_ptr + msgbody_offset,
+ this_chunk);
+ msgbody_offset += this_chunk;
+ buf_offset += this_chunk;
+ data_sent += this_chunk;
+
+ /* after msgbody, append userdata */
+ if (userdata_ptr && userdata_left) {
+ this_chunk = min(userdata_left,
+ MAX_PRINT_CHUNK - buf_offset);
+ memcpy(nt->buf + buf_offset,
+ userdata_ptr + userdata_offset, this_chunk);
+ userdata_offset += this_chunk;
+ buf_offset += this_chunk;
+ data_sent += this_chunk;
}
- /* msgbody was finally written, either in the previous
- * messages and/or in the current buf. Time to write
- * the extradata.
- */
- msgbody_written |= offset + this_offset >= msgbody_len;
-
- /* Msg body is fully written and there is pending extradata to
- * write, append extradata in this chunk
- */
- if (msgbody_written && offset + this_offset < body_len) {
- /* Track how much user data was already sent. First
- * time here, sent_userdata is zero
- */
- sent_extradata = (offset + this_offset) - msgbody_len;
- /* offset of bytes used in current buf */
- preceding_bytes = this_chunk + this_header;
-
- if (WARN_ON_ONCE(sent_extradata < 0))
- return;
-
- this_chunk = min(extradata_len - sent_extradata,
- MAX_PRINT_CHUNK - preceding_bytes);
- if (WARN_ON_ONCE(this_chunk < 0))
- /* this_chunk could be zero if all the previous
- * message used all the buffer. This is not a
- * problem, extradata will be sent in the next
- * iteration
- */
- return;
-
- memcpy(nt->buf + this_header + this_offset,
- extradata + sent_extradata,
- this_chunk);
- this_offset += this_chunk;
+ /* after userdata, append sysdata */
+ if (sysdata_ptr && sysdata_left) {
+ this_chunk = min(sysdata_left,
+ MAX_PRINT_CHUNK - buf_offset);
+ memcpy(nt->buf + buf_offset,
+ sysdata_ptr + sysdata_offset, this_chunk);
+ sysdata_offset += this_chunk;
+ buf_offset += this_chunk;
+ data_sent += this_chunk;
}
- send_udp(nt, nt->buf, this_header + this_offset);
- offset += this_offset;
+ /* if all is good, send the packet out */
+ if (WARN_ON_ONCE(data_sent > data_len))
+ return;
+
+ send_udp(nt, nt->buf, buf_offset);
}
}
@@ -1635,7 +1646,7 @@ static void send_msg_fragmented(struct netconsole_target *nt,
const char *msg,
int msg_len,
int release_len,
- int extradata_len)
+ int sysdata_len)
{
int header_len, msgbody_len;
const char *msgbody;
@@ -1664,7 +1675,7 @@ static void send_msg_fragmented(struct netconsole_target *nt,
* will be replaced
*/
send_fragmented_body(nt, msgbody, header_len, msgbody_len,
- extradata_len);
+ sysdata_len);
}
/**
@@ -1680,19 +1691,22 @@ static void send_msg_fragmented(struct netconsole_target *nt,
static void send_ext_msg_udp(struct netconsole_target *nt, const char *msg,
int msg_len)
{
+ int userdata_len = 0;
int release_len = 0;
- int extradata_len;
-
- extradata_len = prepare_extradata(nt);
+ int sysdata_len = 0;
+#ifdef CONFIG_NETCONSOLE_DYNAMIC
+ sysdata_len = prepare_sysdata(nt);
+ userdata_len = nt->userdata_length;
+#endif
if (nt->release)
release_len = strlen(init_utsname()->release) + 1;
- if (msg_len + release_len + extradata_len <= MAX_PRINT_CHUNK)
+ if (msg_len + release_len + sysdata_len + userdata_len <= MAX_PRINT_CHUNK)
return send_msg_no_fragmentation(nt, msg, msg_len, release_len);
return send_msg_fragmented(nt, msg, msg_len, release_len,
- extradata_len);
+ sysdata_len);
}
static void write_ext_msg(struct console *con, const char *msg,
@@ -1897,6 +1911,9 @@ fail:
static void free_param_target(struct netconsole_target *nt)
{
netpoll_cleanup(&nt->np);
+#ifdef CONFIG_NETCONSOLE_DYNAMIC
+ kfree(nt->userdata);
+#endif
kfree(nt);
}
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c
index 95f66c1f59db..2683a989873e 100644
--- a/drivers/net/netdevsim/dev.c
+++ b/drivers/net/netdevsim/dev.c
@@ -320,6 +320,8 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
&nsim_dev->max_macs);
debugfs_create_bool("test1", 0600, nsim_dev->ddir,
&nsim_dev->test1);
+ debugfs_create_u32("test2", 0600, nsim_dev->ddir,
+ &nsim_dev->test2);
nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
0200,
nsim_dev->ddir,
@@ -521,8 +523,53 @@ err_out:
enum nsim_devlink_param_id {
NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
NSIM_DEVLINK_PARAM_ID_TEST1,
+ NSIM_DEVLINK_PARAM_ID_TEST2,
};
+static int
+nsim_devlink_param_test2_get(struct devlink *devlink, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct nsim_dev *nsim_dev = devlink_priv(devlink);
+
+ ctx->val.vu32 = nsim_dev->test2;
+ return 0;
+}
+
+static int
+nsim_devlink_param_test2_set(struct devlink *devlink, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ struct nsim_dev *nsim_dev = devlink_priv(devlink);
+
+ nsim_dev->test2 = ctx->val.vu32;
+ return 0;
+}
+
+#define NSIM_DEV_TEST2_DEFAULT 1234
+
+static int
+nsim_devlink_param_test2_get_default(struct devlink *devlink, u32 id,
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
+{
+ ctx->val.vu32 = NSIM_DEV_TEST2_DEFAULT;
+ return 0;
+}
+
+static int
+nsim_devlink_param_test2_reset_default(struct devlink *devlink, u32 id,
+ enum devlink_param_cmode cmode,
+ struct netlink_ext_ack *extack)
+{
+ struct nsim_dev *nsim_dev = devlink_priv(devlink);
+
+ nsim_dev->test2 = NSIM_DEV_TEST2_DEFAULT;
+ return 0;
+}
+
static const struct devlink_param nsim_devlink_params[] = {
DEVLINK_PARAM_GENERIC(MAX_MACS,
BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
@@ -531,6 +578,14 @@ static const struct devlink_param nsim_devlink_params[] = {
"test1", DEVLINK_PARAM_TYPE_BOOL,
BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
NULL, NULL, NULL),
+ DEVLINK_PARAM_DRIVER_WITH_DEFAULTS(NSIM_DEVLINK_PARAM_ID_TEST2,
+ "test2", DEVLINK_PARAM_TYPE_U32,
+ BIT(DEVLINK_PARAM_CMODE_RUNTIME),
+ nsim_devlink_param_test2_get,
+ nsim_devlink_param_test2_set,
+ NULL,
+ nsim_devlink_param_test2_get_default,
+ nsim_devlink_param_test2_reset_default),
};
static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev,
@@ -1590,6 +1645,7 @@ int nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev)
nsim_dev->fw_update_flash_chunk_time_ms = NSIM_DEV_FLASH_CHUNK_TIME_MS_DEFAULT;
nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT;
nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT;
+ nsim_dev->test2 = NSIM_DEV_TEST2_DEFAULT;
spin_lock_init(&nsim_dev->fa_cookie_lock);
dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
diff --git a/drivers/net/netdevsim/ipsec.c b/drivers/net/netdevsim/ipsec.c
index 47cdee5577d4..36a1be4923d6 100644
--- a/drivers/net/netdevsim/ipsec.c
+++ b/drivers/net/netdevsim/ipsec.c
@@ -277,6 +277,7 @@ void nsim_ipsec_init(struct netdevsim *ns)
NETIF_F_GSO_ESP)
ns->netdev->features |= NSIM_ESP_FEATURES;
+ ns->netdev->hw_features |= NSIM_ESP_FEATURES;
ns->netdev->hw_enc_features |= NSIM_ESP_FEATURES;
ns->ipsec.pfile = debugfs_create_file("ipsec", 0400,
diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c
index fa1d97885caa..6927c1962277 100644
--- a/drivers/net/netdevsim/netdev.c
+++ b/drivers/net/netdevsim/netdev.c
@@ -133,15 +133,21 @@ static netdev_tx_t nsim_start_xmit(struct sk_buff *skb, struct net_device *dev)
if (!nsim_ipsec_tx(ns, skb))
goto out_drop_any;
- peer_ns = rcu_dereference(ns->peer);
- if (!peer_ns)
- goto out_drop_any;
+ /* Check if loopback mode is enabled */
+ if (dev->features & NETIF_F_LOOPBACK) {
+ peer_ns = ns;
+ peer_dev = dev;
+ } else {
+ peer_ns = rcu_dereference(ns->peer);
+ if (!peer_ns)
+ goto out_drop_any;
+ peer_dev = peer_ns->netdev;
+ }
dr = nsim_do_psp(skb, ns, peer_ns, &psp_ext);
if (dr)
goto out_drop_free;
- peer_dev = peer_ns->netdev;
rxq = skb_get_queue_mapping(skb);
if (rxq >= peer_dev->num_rx_queues)
rxq = rxq % peer_dev->num_rx_queues;
@@ -433,13 +439,8 @@ static int nsim_rcv(struct nsim_rq *rq, int budget)
}
/* skb might be discard at netif_receive_skb, save the len */
- skblen = skb->len;
- skb_mark_napi_id(skb, &rq->napi);
- ret = netif_receive_skb(skb);
- if (ret == NET_RX_SUCCESS)
- dev_dstats_rx_add(dev, skblen);
- else
- dev_dstats_rx_dropped(dev);
+ dev_dstats_rx_add(dev, skb->len);
+ napi_gro_receive(&rq->napi, skb);
}
nsim_start_peer_tx_queue(dev, rq);
@@ -981,7 +982,8 @@ static void nsim_setup(struct net_device *dev)
NETIF_F_FRAGLIST |
NETIF_F_HW_CSUM |
NETIF_F_LRO |
- NETIF_F_TSO;
+ NETIF_F_TSO |
+ NETIF_F_LOOPBACK;
dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
dev->max_mtu = ETH_MAX_MTU;
dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_HW_OFFLOAD;
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index 02c1c97b7008..d1a941e2b18f 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -109,6 +109,11 @@ struct netdevsim {
int rq_reset_mode;
struct {
+ u64 rx_packets;
+ u64 rx_bytes;
+ u64 tx_packets;
+ u64 tx_bytes;
+ struct u64_stats_sync syncp;
struct psp_dev *dev;
u32 spi;
u32 assoc_cnt;
@@ -326,6 +331,7 @@ struct nsim_dev {
u32 fw_update_flash_chunk_time_ms;
u32 max_macs;
bool test1;
+ u32 test2;
bool dont_allow_reload;
bool fail_reload;
struct devlink_region *dummy_region;
diff --git a/drivers/net/netdevsim/psp.c b/drivers/net/netdevsim/psp.c
index 332b5b744f01..727da06101ca 100644
--- a/drivers/net/netdevsim/psp.c
+++ b/drivers/net/netdevsim/psp.c
@@ -70,6 +70,13 @@ nsim_do_psp(struct sk_buff *skb, struct netdevsim *ns,
*psp_ext = skb->extensions;
refcount_inc(&(*psp_ext)->refcnt);
skb->decrypted = 1;
+
+ u64_stats_update_begin(&ns->psp.syncp);
+ ns->psp.tx_packets++;
+ ns->psp.rx_packets++;
+ ns->psp.tx_bytes += skb->len - skb_inner_transport_offset(skb);
+ ns->psp.rx_bytes += skb->len - skb_inner_transport_offset(skb);
+ u64_stats_update_end(&ns->psp.syncp);
} else {
struct ipv6hdr *ip6h __maybe_unused;
struct iphdr *iph;
@@ -164,12 +171,32 @@ static void nsim_assoc_del(struct psp_dev *psd, struct psp_assoc *pas)
ns->psp.assoc_cnt--;
}
+static void nsim_get_stats(struct psp_dev *psd, struct psp_dev_stats *stats)
+{
+ struct netdevsim *ns = psd->drv_priv;
+ unsigned int start;
+
+ /* WARNING: do *not* blindly zero stats in real drivers!
+ * All required stats must be reported by the device!
+ */
+ memset(stats, 0, sizeof(struct psp_dev_stats));
+
+ do {
+ start = u64_stats_fetch_begin(&ns->psp.syncp);
+ stats->rx_bytes = ns->psp.rx_bytes;
+ stats->rx_packets = ns->psp.rx_packets;
+ stats->tx_bytes = ns->psp.tx_bytes;
+ stats->tx_packets = ns->psp.tx_packets;
+ } while (u64_stats_fetch_retry(&ns->psp.syncp, start));
+}
+
static struct psp_dev_ops nsim_psp_ops = {
.set_config = nsim_psp_set_config,
.rx_spi_alloc = nsim_rx_spi_alloc,
.tx_key_add = nsim_assoc_add,
.tx_key_del = nsim_assoc_del,
.key_rotate = nsim_key_rotate,
+ .get_stats = nsim_get_stats,
};
static struct psp_dev_caps nsim_psp_caps = {
diff --git a/drivers/net/netkit.c b/drivers/net/netkit.c
index 492be60f2e70..0a2fef7caccb 100644
--- a/drivers/net/netkit.c
+++ b/drivers/net/netkit.c
@@ -16,17 +16,19 @@
#define DRV_NAME "netkit"
struct netkit {
- /* Needed in fast-path */
+ __cacheline_group_begin(netkit_fastpath);
struct net_device __rcu *peer;
struct bpf_mprog_entry __rcu *active;
enum netkit_action policy;
enum netkit_scrub scrub;
struct bpf_mprog_bundle bundle;
+ __cacheline_group_end(netkit_fastpath);
- /* Needed in slow-path */
+ __cacheline_group_begin(netkit_slowpath);
enum netkit_mode mode;
bool primary;
u32 headroom;
+ __cacheline_group_end(netkit_slowpath);
};
struct netkit_link {
diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c
index 14298188c5f1..ecbe9dcf4f7d 100644
--- a/drivers/net/ovpn/netlink-gen.c
+++ b/drivers/net/ovpn/netlink-gen.c
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/ovpn.yaml */
/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#include <net/netlink.h>
#include <net/genetlink.h>
diff --git a/drivers/net/ovpn/netlink-gen.h b/drivers/net/ovpn/netlink-gen.h
index 220b5b2fdd4f..b2301580770f 100644
--- a/drivers/net/ovpn/netlink-gen.h
+++ b/drivers/net/ovpn/netlink-gen.h
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/ovpn.yaml */
/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_OVPN_GEN_H
#define _LINUX_OVPN_GEN_H
diff --git a/drivers/net/ovpn/tcp.c b/drivers/net/ovpn/tcp.c
index 289f62c5d2c7..0d7f30360d87 100644
--- a/drivers/net/ovpn/tcp.c
+++ b/drivers/net/ovpn/tcp.c
@@ -560,16 +560,34 @@ static void ovpn_tcp_close(struct sock *sk, long timeout)
static __poll_t ovpn_tcp_poll(struct file *file, struct socket *sock,
poll_table *wait)
{
- __poll_t mask = datagram_poll(file, sock, wait);
+ struct sk_buff_head *queue = &sock->sk->sk_receive_queue;
struct ovpn_socket *ovpn_sock;
+ struct ovpn_peer *peer = NULL;
+ __poll_t mask;
rcu_read_lock();
ovpn_sock = rcu_dereference_sk_user_data(sock->sk);
- if (ovpn_sock && ovpn_sock->peer &&
- !skb_queue_empty(&ovpn_sock->peer->tcp.user_queue))
- mask |= EPOLLIN | EPOLLRDNORM;
+ /* if we landed in this callback, we expect to have a
+ * meaningful state. The ovpn_socket lifecycle would
+ * prevent it otherwise.
+ */
+ if (WARN(!ovpn_sock || !ovpn_sock->peer,
+ "ovpn: null state in ovpn_tcp_poll!")) {
+ rcu_read_unlock();
+ return 0;
+ }
+
+ if (ovpn_peer_hold(ovpn_sock->peer)) {
+ peer = ovpn_sock->peer;
+ queue = &peer->tcp.user_queue;
+ }
rcu_read_unlock();
+ mask = datagram_poll_queue(file, sock, wait, queue);
+
+ if (peer)
+ ovpn_peer_put(peer);
+
return mask;
}
diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c
index 677f92883976..73e1364ad1ed 100644
--- a/drivers/net/pcs/pcs-lynx.c
+++ b/drivers/net/pcs/pcs-lynx.c
@@ -40,12 +40,12 @@ static unsigned int lynx_pcs_inband_caps(struct phylink_pcs *pcs,
{
switch (interface) {
case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
case PHY_INTERFACE_MODE_10GBASER:
- case PHY_INTERFACE_MODE_2500BASEX:
return LINK_INBAND_DISABLE;
case PHY_INTERFACE_MODE_USXGMII:
@@ -80,27 +80,6 @@ static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs,
phylink_decode_usxgmii_word(state, lpa);
}
-static void lynx_pcs_get_state_2500basex(struct mdio_device *pcs,
- struct phylink_link_state *state)
-{
- int bmsr;
-
- bmsr = mdiodev_read(pcs, MII_BMSR);
- if (bmsr < 0) {
- state->link = false;
- return;
- }
-
- state->link = !!(bmsr & BMSR_LSTATUS);
- state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
- if (!state->link)
- return;
-
- state->speed = SPEED_2500;
- state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
- state->duplex = DUPLEX_FULL;
-}
-
static void lynx_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
struct phylink_link_state *state)
{
@@ -108,13 +87,11 @@ static void lynx_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode,
switch (state->interface) {
case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
phylink_mii_c22_pcs_get_state(lynx->mdio, neg_mode, state);
break;
- case PHY_INTERFACE_MODE_2500BASEX:
- lynx_pcs_get_state_2500basex(lynx->mdio, state);
- break;
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10G_QXGMII:
lynx_pcs_get_state_usxgmii(lynx->mdio, state);
@@ -152,7 +129,8 @@ static int lynx_pcs_config_giga(struct mdio_device *pcs,
mdiodev_write(pcs, LINK_TIMER_HI, link_timer >> 16);
}
- if (interface == PHY_INTERFACE_MODE_1000BASEX) {
+ if (interface == PHY_INTERFACE_MODE_1000BASEX ||
+ interface == PHY_INTERFACE_MODE_2500BASEX) {
if_mode = 0;
} else {
/* SGMII and QSGMII */
@@ -202,15 +180,9 @@ static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
case PHY_INTERFACE_MODE_1000BASEX:
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_2500BASEX:
return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising,
neg_mode);
- case PHY_INTERFACE_MODE_2500BASEX:
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
- dev_err(&lynx->mdio->dev,
- "AN not supported on 3.125GHz SerDes lane\n");
- return -EOPNOTSUPP;
- }
- break;
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10G_QXGMII:
return lynx_pcs_config_usxgmii(lynx->mdio, ifmode, advertising,
@@ -271,42 +243,6 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs,
if_mode);
}
-/* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
- * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
- * auto-negotiation of any link parameters. Electrically it is compatible with
- * a single lane of XAUI.
- * The hardware reference manual wants to call this mode SGMII, but it isn't
- * really, since the fundamental features of SGMII:
- * - Downgrading the link speed by duplicating symbols
- * - Auto-negotiation
- * are not there.
- * The speed is configured at 1000 in the IF_MODE because the clock frequency
- * is actually given by a PLL configured in the Reset Configuration Word (RCW).
- * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
- * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
- * lower link speed on line side, the system-side interface remains fixed at
- * 2500 Mbps and we do rate adaptation through pause frames.
- */
-static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs,
- unsigned int neg_mode,
- int speed, int duplex)
-{
- u16 if_mode = 0;
-
- if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) {
- dev_err(&pcs->dev, "AN not supported for 2500BaseX\n");
- return;
- }
-
- if (duplex == DUPLEX_HALF)
- if_mode |= IF_MODE_HALF_DUPLEX;
- if_mode |= IF_MODE_SPEED(SGMII_SPEED_2500);
-
- mdiodev_modify(pcs, IF_MODE,
- IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK,
- if_mode);
-}
-
static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
int speed, int duplex)
@@ -318,9 +254,6 @@ static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
case PHY_INTERFACE_MODE_QSGMII:
lynx_pcs_link_up_sgmii(lynx->mdio, neg_mode, speed, duplex);
break;
- case PHY_INTERFACE_MODE_2500BASEX:
- lynx_pcs_link_up_2500basex(lynx->mdio, neg_mode, speed, duplex);
- break;
case PHY_INTERFACE_MODE_USXGMII:
case PHY_INTERFACE_MODE_10G_QXGMII:
/* At the moment, only in-band AN is supported for USXGMII
diff --git a/drivers/net/pcs/pcs-xpcs-plat.c b/drivers/net/pcs/pcs-xpcs-plat.c
index 9e1ccc319a1d..b8c48f9effbf 100644
--- a/drivers/net/pcs/pcs-xpcs-plat.c
+++ b/drivers/net/pcs/pcs-xpcs-plat.c
@@ -365,9 +365,6 @@ static int xpcs_plat_init_dev(struct dw_xpcs_plat *pxpcs)
err_clean_data:
mdiodev->dev.platform_data = NULL;
- fwnode_handle_put(dev_fwnode(&mdiodev->dev));
- device_set_node(&mdiodev->dev, NULL);
-
mdio_device_free(mdiodev);
return ret;
@@ -456,5 +453,5 @@ static struct platform_driver xpcs_plat_driver = {
module_platform_driver(xpcs_plat_driver);
MODULE_DESCRIPTION("Synopsys DesignWare XPCS platform device driver");
-MODULE_AUTHOR("Signed-off-by: Serge Semin <fancer.lancer@gmail.com>");
+MODULE_AUTHOR("Serge Semin <fancer.lancer@gmail.com>");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index 3d1bd5aac093..9679f2b35a44 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -37,6 +37,16 @@ static const int xpcs_10gkr_features[] = {
__ETHTOOL_LINK_MODE_MASK_NBITS,
};
+static const int xpcs_25gbaser_features[] = {
+ ETHTOOL_LINK_MODE_MII_BIT,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+};
+
static const int xpcs_xlgmii_features[] = {
ETHTOOL_LINK_MODE_Pause_BIT,
ETHTOOL_LINK_MODE_Asym_Pause_BIT,
@@ -67,6 +77,40 @@ static const int xpcs_xlgmii_features[] = {
__ETHTOOL_LINK_MODE_MASK_NBITS,
};
+static const int xpcs_50gbaser_features[] = {
+ ETHTOOL_LINK_MODE_MII_BIT,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseDR_Full_BIT,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+};
+
+static const int xpcs_50gbaser2_features[] = {
+ ETHTOOL_LINK_MODE_MII_BIT,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+};
+
+static const int xpcs_100gbasep_features[] = {
+ ETHTOOL_LINK_MODE_MII_BIT,
+ ETHTOOL_LINK_MODE_Pause_BIT,
+ ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+ ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT,
+ ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT,
+ __ETHTOOL_LINK_MODE_MASK_NBITS,
+};
+
static const int xpcs_10gbaser_features[] = {
ETHTOOL_LINK_MODE_Pause_BIT,
ETHTOOL_LINK_MODE_Asym_Pause_BIT,
@@ -523,9 +567,57 @@ static int xpcs_get_max_xlgmii_speed(struct dw_xpcs *xpcs,
return speed;
}
-static void xpcs_resolve_pma(struct dw_xpcs *xpcs,
- struct phylink_link_state *state)
+static int xpcs_c45_read_pcs_speed(struct dw_xpcs *xpcs,
+ struct phylink_link_state *state)
{
+ int pcs_ctrl1;
+
+ pcs_ctrl1 = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_CTRL1);
+ if (pcs_ctrl1 < 0)
+ return pcs_ctrl1;
+
+ switch (pcs_ctrl1 & MDIO_CTRL1_SPEEDSEL) {
+ case MDIO_PCS_CTRL1_SPEED25G:
+ state->speed = SPEED_25000;
+ break;
+ case MDIO_PCS_CTRL1_SPEED50G:
+ state->speed = SPEED_50000;
+ break;
+ case MDIO_PCS_CTRL1_SPEED100G:
+ state->speed = SPEED_100000;
+ break;
+ default:
+ state->speed = SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int xpcs_resolve_pma(struct dw_xpcs *xpcs,
+ struct phylink_link_state *state)
+{
+ int pmd_rxdet, err = 0;
+
+ /* The Meta Platforms FBNIC PMD will go into a training state for
+ * about 4 seconds when the link first comes up. During this time the
+ * PCS link will bounce. To avoid reporting link up too soon we include
+ * the PMD state provided by the driver.
+ */
+ if (xpcs->info.pma == MP_FBNIC_XPCS_PMA_100G_ID) {
+ pmd_rxdet = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_PMA_RXDET);
+ if (pmd_rxdet < 0) {
+ state->link = false;
+ return pmd_rxdet;
+ }
+
+ /* Verify Rx lanes are trained before reporting link up */
+ if (!(pmd_rxdet & MDIO_PMD_RXDET_GLOBAL)) {
+ state->link = false;
+ return 0;
+ }
+ }
+
state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
state->duplex = DUPLEX_FULL;
@@ -536,10 +628,18 @@ static void xpcs_resolve_pma(struct dw_xpcs *xpcs,
case PHY_INTERFACE_MODE_XLGMII:
state->speed = xpcs_get_max_xlgmii_speed(xpcs, state);
break;
+ case PHY_INTERFACE_MODE_100GBASEP:
+ case PHY_INTERFACE_MODE_LAUI:
+ case PHY_INTERFACE_MODE_50GBASER:
+ case PHY_INTERFACE_MODE_25GBASER:
+ err = xpcs_c45_read_pcs_speed(xpcs, state);
+ break;
default:
state->speed = SPEED_UNKNOWN;
break;
}
+
+ return err;
}
static int xpcs_validate(struct phylink_pcs *pcs, unsigned long *supported,
@@ -945,10 +1045,10 @@ static int xpcs_get_state_c73(struct dw_xpcs *xpcs,
phylink_resolve_c73(state);
} else {
- xpcs_resolve_pma(xpcs, state);
+ ret = xpcs_resolve_pma(xpcs, state);
}
- return 0;
+ return ret;
}
static int xpcs_get_state_c37_sgmii(struct dw_xpcs *xpcs,
@@ -1284,17 +1384,16 @@ static int xpcs_read_ids(struct dw_xpcs *xpcs)
if (ret < 0)
return ret;
- id = ret;
+ id = ret << 16;
ret = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_DEVID2);
if (ret < 0)
return ret;
- /* Note the inverted dword order and masked out Model/Revision numbers
- * with respect to what is done with the PCS ID...
+ /* For now we only record the OUI for the PMAPMD, we may want to
+ * add the model number at some point in the future.
*/
- ret = (ret >> 10) & 0x3F;
- id |= ret << 16;
+ id |= ret & MDIO_DEVID2_OUI;
/* Set the PMA ID if it hasn't been pre-initialized */
if (xpcs->info.pma == DW_XPCS_PMA_ID_NATIVE)
@@ -1313,10 +1412,26 @@ static const struct dw_xpcs_compat synopsys_xpcs_compat[] = {
.supported = xpcs_10gkr_features,
.an_mode = DW_AN_C73,
}, {
+ .interface = PHY_INTERFACE_MODE_25GBASER,
+ .supported = xpcs_25gbaser_features,
+ .an_mode = DW_AN_C73,
+ }, {
.interface = PHY_INTERFACE_MODE_XLGMII,
.supported = xpcs_xlgmii_features,
.an_mode = DW_AN_C73,
}, {
+ .interface = PHY_INTERFACE_MODE_50GBASER,
+ .supported = xpcs_50gbaser_features,
+ .an_mode = DW_AN_C73,
+ }, {
+ .interface = PHY_INTERFACE_MODE_LAUI,
+ .supported = xpcs_50gbaser2_features,
+ .an_mode = DW_AN_C73,
+ }, {
+ .interface = PHY_INTERFACE_MODE_100GBASEP,
+ .supported = xpcs_100gbasep_features,
+ .an_mode = DW_AN_C73,
+ }, {
.interface = PHY_INTERFACE_MODE_10GBASER,
.supported = xpcs_10gbaser_features,
.an_mode = DW_10GBASER,
@@ -1495,7 +1610,8 @@ static struct dw_xpcs *xpcs_create(struct mdio_device *mdiodev)
xpcs_get_interfaces(xpcs, xpcs->pcs.supported_interfaces);
- if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID)
+ if (xpcs->info.pma == WX_TXGBE_XPCS_PMA_10G_ID ||
+ xpcs->info.pma == MP_FBNIC_XPCS_PMA_100G_ID)
xpcs->pcs.poll = false;
else
xpcs->need_reset = true;
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 98700d069191..a7ade7b95a2e 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -308,7 +308,7 @@ config MICREL_PHY
config MICROCHIP_T1S_PHY
tristate "Microchip 10BASE-T1S Ethernet PHYs"
help
- Currently supports the LAN8670/1/2 Rev.B1/C1/C2 and
+ Currently supports the LAN8670/1/2 Rev.B1/C1/C2/D0 and
LAN8650/1 Rev.B0/B1 Internal PHYs.
config MICROCHIP_PHY
diff --git a/drivers/net/phy/adin1100.c b/drivers/net/phy/adin1100.c
index bd7a47a903ac..8f9753d4318c 100644
--- a/drivers/net/phy/adin1100.c
+++ b/drivers/net/phy/adin1100.c
@@ -192,16 +192,15 @@ static irqreturn_t adin_phy_handle_interrupt(struct phy_device *phydev)
static int adin_set_powerdown_mode(struct phy_device *phydev, bool en)
{
int ret;
- int val;
- val = en ? ADIN_CRSM_SFT_PD_CNTRL_EN : 0;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
- ADIN_CRSM_SFT_PD_CNTRL, val);
+ ADIN_CRSM_SFT_PD_CNTRL,
+ en ? ADIN_CRSM_SFT_PD_CNTRL_EN : 0);
if (ret < 0)
return ret;
return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, ADIN_CRSM_STAT, ret,
- (ret & ADIN_CRSM_SFT_PD_RDY) == val,
+ !!(ret & ADIN_CRSM_SFT_PD_RDY) == en,
1000, 30000, true);
}
diff --git a/drivers/net/phy/aquantia/aquantia_firmware.c b/drivers/net/phy/aquantia/aquantia_firmware.c
index bbbcc9736b00..569256152689 100644
--- a/drivers/net/phy/aquantia/aquantia_firmware.c
+++ b/drivers/net/phy/aquantia/aquantia_firmware.c
@@ -369,7 +369,7 @@ int aqr_firmware_load(struct phy_device *phydev)
* assume that, and load a new image.
*/
ret = aqr_firmware_load_nvmem(phydev);
- if (!ret)
+ if (ret == -EPROBE_DEFER || !ret)
return ret;
ret = aqr_firmware_load_fs(phydev);
diff --git a/drivers/net/phy/bcm-phy-ptp.c b/drivers/net/phy/bcm-phy-ptp.c
index d3501f8487d9..65d609ed69fb 100644
--- a/drivers/net/phy/bcm-phy-ptp.c
+++ b/drivers/net/phy/bcm-phy-ptp.c
@@ -780,9 +780,21 @@ out:
kfree_skb(skb);
}
-static int bcm_ptp_hwtstamp(struct mii_timestamper *mii_ts,
- struct kernel_hwtstamp_config *cfg,
- struct netlink_ext_ack *extack)
+static int bcm_ptp_hwtstamp_get(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg)
+{
+ struct bcm_ptp_private *priv = mii2priv(mii_ts);
+
+ cfg->rx_filter = priv->hwts_rx ? HWTSTAMP_FILTER_PTP_V2_EVENT
+ : HWTSTAMP_FILTER_NONE;
+ cfg->tx_type = priv->tx_type;
+
+ return 0;
+}
+
+static int bcm_ptp_hwtstamp_set(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
struct bcm_ptp_private *priv = mii2priv(mii_ts);
u16 mode, ctrl;
@@ -898,7 +910,8 @@ static void bcm_ptp_init(struct bcm_ptp_private *priv)
priv->mii_ts.rxtstamp = bcm_ptp_rxtstamp;
priv->mii_ts.txtstamp = bcm_ptp_txtstamp;
- priv->mii_ts.hwtstamp = bcm_ptp_hwtstamp;
+ priv->mii_ts.hwtstamp_set = bcm_ptp_hwtstamp_set;
+ priv->mii_ts.hwtstamp_get = bcm_ptp_hwtstamp_get;
priv->mii_ts.ts_info = bcm_ptp_ts_info;
priv->phydev->mii_ts = &priv->mii_ts;
diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c
index 74396453f5bb..b950acc9c49b 100644
--- a/drivers/net/phy/dp83640.c
+++ b/drivers/net/phy/dp83640.c
@@ -1176,9 +1176,21 @@ static irqreturn_t dp83640_handle_interrupt(struct phy_device *phydev)
return IRQ_HANDLED;
}
-static int dp83640_hwtstamp(struct mii_timestamper *mii_ts,
- struct kernel_hwtstamp_config *cfg,
- struct netlink_ext_ack *extack)
+static int dp83640_hwtstamp_get(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg)
+{
+ struct dp83640_private *dp83640 =
+ container_of(mii_ts, struct dp83640_private, mii_ts);
+
+ cfg->rx_filter = dp83640->hwts_rx_en;
+ cfg->tx_type = dp83640->hwts_tx_en;
+
+ return 0;
+}
+
+static int dp83640_hwtstamp_set(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
struct dp83640_private *dp83640 =
container_of(mii_ts, struct dp83640_private, mii_ts);
@@ -1198,7 +1210,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts,
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
- dp83640->hwts_rx_en = 1;
+ dp83640->hwts_rx_en = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
dp83640->layer = PTP_CLASS_L4;
dp83640->version = PTP_CLASS_V1;
cfg->rx_filter = HWTSTAMP_FILTER_PTP_V1_L4_EVENT;
@@ -1206,7 +1218,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts,
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
- dp83640->hwts_rx_en = 1;
+ dp83640->hwts_rx_en = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
dp83640->layer = PTP_CLASS_L4;
dp83640->version = PTP_CLASS_V2;
cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_L4_EVENT;
@@ -1214,7 +1226,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts,
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
- dp83640->hwts_rx_en = 1;
+ dp83640->hwts_rx_en = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
dp83640->layer = PTP_CLASS_L2;
dp83640->version = PTP_CLASS_V2;
cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_L2_EVENT;
@@ -1222,7 +1234,7 @@ static int dp83640_hwtstamp(struct mii_timestamper *mii_ts,
case HWTSTAMP_FILTER_PTP_V2_EVENT:
case HWTSTAMP_FILTER_PTP_V2_SYNC:
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
- dp83640->hwts_rx_en = 1;
+ dp83640->hwts_rx_en = HWTSTAMP_FILTER_PTP_V2_EVENT;
dp83640->layer = PTP_CLASS_L4 | PTP_CLASS_L2;
dp83640->version = PTP_CLASS_V2;
cfg->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
@@ -1407,7 +1419,8 @@ static int dp83640_probe(struct phy_device *phydev)
dp83640->phydev = phydev;
dp83640->mii_ts.rxtstamp = dp83640_rxtstamp;
dp83640->mii_ts.txtstamp = dp83640_txtstamp;
- dp83640->mii_ts.hwtstamp = dp83640_hwtstamp;
+ dp83640->mii_ts.hwtstamp_set = dp83640_hwtstamp_set;
+ dp83640->mii_ts.hwtstamp_get = dp83640_hwtstamp_get;
dp83640->mii_ts.ts_info = dp83640_ts_info;
INIT_DELAYED_WORK(&dp83640->ts_work, rx_timestamp_work);
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index deeefb962566..5f5de01c41e1 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -738,6 +738,12 @@ static int dp83867_config_init(struct phy_device *phydev)
return ret;
}
+ /* Although the DP83867 reports EEE capability through the
+ * MDIO_PCS_EEE_ABLE and MDIO_AN_EEE_ADV registers, the feature
+ * is not actually implemented in hardware.
+ */
+ phy_disable_eee(phydev);
+
if (phy_interface_is_rgmii(phydev) ||
phydev->interface == PHY_INTERFACE_MODE_SGMII) {
val = phy_read(phydev, MII_DP83867_PHYCTRL);
@@ -931,15 +937,15 @@ static void dp83867_link_change_notify(struct phy_device *phydev)
* whenever there is a link change.
*/
if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
- int val = 0;
+ int val;
- val = phy_clear_bits(phydev, DP83867_CFG2,
- DP83867_SGMII_AUTONEG_EN);
- if (val < 0)
- return;
+ val = phy_modify_changed(phydev, DP83867_CFG2,
+ DP83867_SGMII_AUTONEG_EN, 0);
- phy_set_bits(phydev, DP83867_CFG2,
- DP83867_SGMII_AUTONEG_EN);
+ /* Keep the in-band setting made by dp83867_config_inband() */
+ if (val != 0)
+ phy_set_bits(phydev, DP83867_CFG2,
+ DP83867_SGMII_AUTONEG_EN);
}
}
@@ -1110,6 +1116,25 @@ static int dp83867_led_polarity_set(struct phy_device *phydev, int index,
DP83867_LED_POLARITY(index), polarity);
}
+static unsigned int dp83867_inband_caps(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ if (interface == PHY_INTERFACE_MODE_SGMII)
+ return LINK_INBAND_ENABLE | LINK_INBAND_DISABLE;
+
+ return 0;
+}
+
+static int dp83867_config_inband(struct phy_device *phydev, unsigned int modes)
+{
+ int val = 0;
+
+ if (modes == LINK_INBAND_ENABLE)
+ val = DP83867_SGMII_AUTONEG_EN;
+
+ return phy_modify(phydev, DP83867_CFG2, DP83867_SGMII_AUTONEG_EN, val);
+}
+
static struct phy_driver dp83867_driver[] = {
{
.phy_id = DP83867_PHY_ID,
@@ -1143,6 +1168,9 @@ static struct phy_driver dp83867_driver[] = {
.led_hw_control_set = dp83867_led_hw_control_set,
.led_hw_control_get = dp83867_led_hw_control_get,
.led_polarity_set = dp83867_led_polarity_set,
+
+ .inband_caps = dp83867_inband_caps,
+ .config_inband = dp83867_config_inband,
},
};
module_phy_driver(dp83867_driver);
diff --git a/drivers/net/phy/dp83869.c b/drivers/net/phy/dp83869.c
index a2cd1cc35cde..1f381d7b13ff 100644
--- a/drivers/net/phy/dp83869.c
+++ b/drivers/net/phy/dp83869.c
@@ -84,7 +84,7 @@
#define DP83869_CLK_DELAY_DEF 7
/* STRAP_STS1 bits */
-#define DP83869_STRAP_OP_MODE_MASK GENMASK(2, 0)
+#define DP83869_STRAP_OP_MODE_MASK GENMASK(11, 9)
#define DP83869_STRAP_STS1_RESERVED BIT(11)
#define DP83869_STRAP_MIRROR_ENABLED BIT(12)
@@ -528,7 +528,7 @@ static int dp83869_set_strapped_mode(struct phy_device *phydev)
if (val < 0)
return val;
- dp83869->mode = val & DP83869_STRAP_OP_MODE_MASK;
+ dp83869->mode = FIELD_GET(DP83869_STRAP_OP_MODE_MASK, val);
return 0;
}
diff --git a/drivers/net/phy/dp83td510.c b/drivers/net/phy/dp83td510.c
index 23af1ac194fa..d75dae6071ad 100644
--- a/drivers/net/phy/dp83td510.c
+++ b/drivers/net/phy/dp83td510.c
@@ -61,6 +61,7 @@
#define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15)
#define DP83TD510E_MSE_DETECT 0xa85
+#define DP83TD510E_MSE_MAX U16_MAX
#define DP83TD510_SQI_MAX 7
@@ -249,6 +250,64 @@ struct dp83td510_priv {
#define DP83TD510E_ALCD_COMPLETE BIT(15)
#define DP83TD510E_ALCD_CABLE_LENGTH GENMASK(10, 0)
+static int dp83td510_get_mse_capability(struct phy_device *phydev,
+ struct phy_mse_capability *cap)
+{
+ /* DP83TD510E documents only a single (average) MSE register
+ * (used to derive SQI); no peak or worst-peak counters are
+ * described. Advertise only PHY_MSE_CAP_AVG.
+ */
+ cap->supported_caps = PHY_MSE_CAP_AVG;
+ /* 10BASE-T1L is a single-pair medium, so there are no B/C/D channels.
+ * We still advertise PHY_MSE_CAP_CHANNEL_A to indicate that the PHY
+ * can attribute the measurement to a specific pair (the only one),
+ * rather than exposing it only as a link-aggregate.
+ *
+ * Rationale:
+ * - Keeps the ethtool MSE_GET selection logic consistent: per-channel
+ * (A/B/C/D) is preferred over WORST/LINK, so userspace receives a
+ * CHANNEL_A nest instead of LINK.
+ * - Signals to tools that "per-pair" data is available (even if there's
+ * just one pair), avoiding the impression that only aggregate values
+ * are supported.
+ * - Remains compatible with multi-pair PHYs and uniform UI handling.
+ *
+ * Note: WORST and other channels are not advertised on 10BASE-T1L.
+ */
+ cap->supported_caps |= PHY_MSE_CHANNEL_A | PHY_MSE_CAP_LINK;
+ cap->max_average_mse = DP83TD510E_MSE_MAX;
+
+ /* The datasheet does not specify the refresh rate or symbol count,
+ * but based on similar PHYs and standards, we can assume a common
+ * value. For 10BASE-T1L, the symbol rate is 7.5 MBd. A common
+ * diagnostic interval is around 1ms.
+ * 7.5e6 symbols/sec * 0.001 sec = 7500 symbols.
+ */
+ cap->refresh_rate_ps = 1000000000; /* 1 ms */
+ cap->num_symbols = 7500;
+
+ return 0;
+}
+
+static int dp83td510_get_mse_snapshot(struct phy_device *phydev,
+ enum phy_mse_channel channel,
+ struct phy_mse_snapshot *snapshot)
+{
+ int ret;
+
+ if (channel != PHY_MSE_CHANNEL_LINK &&
+ channel != PHY_MSE_CHANNEL_A)
+ return -EOPNOTSUPP;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_MSE_DETECT);
+ if (ret < 0)
+ return ret;
+
+ snapshot->average_mse = ret;
+
+ return 0;
+}
+
static int dp83td510_led_brightness_set(struct phy_device *phydev, u8 index,
enum led_brightness brightness)
{
@@ -893,6 +952,9 @@ static struct phy_driver dp83td510_driver[] = {
.get_phy_stats = dp83td510_get_phy_stats,
.update_stats = dp83td510_update_stats,
+ .get_mse_capability = dp83td510_get_mse_capability,
+ .get_mse_snapshot = dp83td510_get_mse_snapshot,
+
.led_brightness_set = dp83td510_led_brightness_set,
.led_hw_is_supported = dp83td510_led_hw_is_supported,
.led_hw_control_set = dp83td510_led_hw_control_set,
diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c
index 0e1b28f06f18..50684271f81a 100644
--- a/drivers/net/phy/fixed_phy.c
+++ b/drivers/net/phy/fixed_phy.c
@@ -19,7 +19,6 @@
#include <linux/of.h>
#include <linux/idr.h>
#include <linux/netdevice.h>
-#include <linux/linkmode.h>
#include "swphy.h"
@@ -125,18 +124,13 @@ static int __fixed_phy_add(int phy_addr,
fp->addr = phy_addr;
fp->status = *status;
+ fp->status.link = true;
list_add_tail(&fp->node, &fmb_phys);
return 0;
}
-void fixed_phy_add(const struct fixed_phy_status *status)
-{
- __fixed_phy_add(0, status);
-}
-EXPORT_SYMBOL_GPL(fixed_phy_add);
-
static DEFINE_IDA(phy_fixed_ida);
static void fixed_phy_del(int phy_addr)
@@ -179,42 +173,10 @@ struct phy_device *fixed_phy_register(const struct fixed_phy_status *status,
return ERR_PTR(-EINVAL);
}
- /* propagate the fixed link values to struct phy_device */
- phy->link = status->link;
- if (status->link) {
- phy->speed = status->speed;
- phy->duplex = status->duplex;
- phy->pause = status->pause;
- phy->asym_pause = status->asym_pause;
- }
-
of_node_get(np);
phy->mdio.dev.of_node = np;
phy->is_pseudo_fixed_link = true;
- switch (status->speed) {
- case SPEED_1000:
- linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
- phy->supported);
- linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
- phy->supported);
- fallthrough;
- case SPEED_100:
- linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT,
- phy->supported);
- linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
- phy->supported);
- fallthrough;
- case SPEED_10:
- default:
- linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
- phy->supported);
- linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
- phy->supported);
- }
-
- phy_advertise_supported(phy);
-
ret = phy_device_register(phy);
if (ret) {
phy_device_free(phy);
@@ -227,6 +189,17 @@ struct phy_device *fixed_phy_register(const struct fixed_phy_status *status,
}
EXPORT_SYMBOL_GPL(fixed_phy_register);
+struct phy_device *fixed_phy_register_100fd(void)
+{
+ static const struct fixed_phy_status status = {
+ .speed = SPEED_100,
+ .duplex = DUPLEX_FULL,
+ };
+
+ return fixed_phy_register(&status, NULL);
+}
+EXPORT_SYMBOL_GPL(fixed_phy_register_100fd);
+
void fixed_phy_unregister(struct phy_device *phy)
{
phy_device_remove(phy);
diff --git a/drivers/net/phy/mdio-open-alliance.h b/drivers/net/phy/mdio-open-alliance.h
index 931e14660d75..449d0fb67093 100644
--- a/drivers/net/phy/mdio-open-alliance.h
+++ b/drivers/net/phy/mdio-open-alliance.h
@@ -43,4 +43,53 @@
/* Version Identifiers */
#define OATC14_IDM 0x0a00
+/*
+ * Open Alliance TC14 (10BASE-T1S) - Advanced Diagnostic Features Registers
+ *
+ * Refer to the OPEN Alliance documentation:
+ * https://opensig.org/automotive-ethernet-specifications/
+ *
+ * Specification:
+ * "10BASE-T1S Advanced Diagnostic PHY Features"
+ * https://opensig.org/wp-content/uploads/2025/06/OPEN_Alliance_10BASE-T1S_Advanced_PHY_features_for-automotive_Ethernet_V2.1b.pdf
+ */
+/* Advanced Diagnostic Features Capability Register*/
+#define MDIO_OATC14_ADFCAP 0xcc00
+#define OATC14_ADFCAP_HDD_CAPABILITY GENMASK(10, 8)
+#define OATC14_ADFCAP_SQIPLUS_CAPABILITY GENMASK(4, 1)
+#define OATC14_ADFCAP_SQI_CAPABILITY BIT(0)
+
+/* Harness Defect Detection Register */
+#define MDIO_OATC14_HDD 0xcc01
+#define OATC14_HDD_CONTROL BIT(15)
+#define OATC14_HDD_READY BIT(14)
+#define OATC14_HDD_START_CONTROL BIT(13)
+#define OATC14_HDD_VALID BIT(2)
+#define OATC14_HDD_SHORT_OPEN_STATUS GENMASK(1, 0)
+
+/* Dynamic Channel Quality SQI Register */
+#define MDIO_OATC14_DCQ_SQI 0xcc03
+#define OATC14_DCQ_SQI_VALUE GENMASK(2, 0)
+
+/* Dynamic Channel Quality SQI Plus Register */
+#define MDIO_OATC14_DCQ_SQIPLUS 0xcc04
+#define OATC14_DCQ_SQIPLUS_VALUE GENMASK(7, 0)
+
+/* SQI is supported using 3 bits means 8 levels (0-7) */
+#define OATC14_SQI_MAX_LEVEL 7
+
+/* Bus Short/Open Status:
+ * 0 0 - no fault; everything is ok. (Default)
+ * 0 1 - detected as an open or missing termination(s)
+ * 1 0 - detected as a short or extra termination(s)
+ * 1 1 - fault but fault type not detectable. More details can be available by
+ * vender specific register if supported.
+ */
+enum oatc14_hdd_status {
+ OATC14_HDD_STATUS_CABLE_OK = 0,
+ OATC14_HDD_STATUS_OPEN,
+ OATC14_HDD_STATUS_SHORT,
+ OATC14_HDD_STATUS_NOT_DETECTABLE,
+};
+
#endif /* __MDIO_OPEN_ALLIANCE__ */
diff --git a/drivers/net/phy/mdio-private.h b/drivers/net/phy/mdio-private.h
new file mode 100644
index 000000000000..8bc6d9088af1
--- /dev/null
+++ b/drivers/net/phy/mdio-private.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __MDIO_PRIVATE_H
+#define __MDIO_PRIVATE_H
+
+/* MDIO internal helpers
+ */
+
+int mdio_device_register_reset(struct mdio_device *mdiodev);
+void mdio_device_unregister_reset(struct mdio_device *mdiodev);
+
+#endif /* __MDIO_PRIVATE_H */
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index cad6ed3aa10b..afdf1ad6c0e6 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -29,37 +29,11 @@
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/unistd.h>
+#include "mdio-private.h"
#define CREATE_TRACE_POINTS
#include <trace/events/mdio.h>
-static int mdiobus_register_gpiod(struct mdio_device *mdiodev)
-{
- /* Deassert the optional reset signal */
- mdiodev->reset_gpio = gpiod_get_optional(&mdiodev->dev,
- "reset", GPIOD_OUT_LOW);
- if (IS_ERR(mdiodev->reset_gpio))
- return PTR_ERR(mdiodev->reset_gpio);
-
- if (mdiodev->reset_gpio)
- gpiod_set_consumer_name(mdiodev->reset_gpio, "PHY reset");
-
- return 0;
-}
-
-static int mdiobus_register_reset(struct mdio_device *mdiodev)
-{
- struct reset_control *reset;
-
- reset = reset_control_get_optional_exclusive(&mdiodev->dev, "phy");
- if (IS_ERR(reset))
- return PTR_ERR(reset);
-
- mdiodev->reset_ctrl = reset;
-
- return 0;
-}
-
int mdiobus_register_device(struct mdio_device *mdiodev)
{
int err;
@@ -68,11 +42,7 @@ int mdiobus_register_device(struct mdio_device *mdiodev)
return -EBUSY;
if (mdiodev->flags & MDIO_DEVICE_FLAG_PHY) {
- err = mdiobus_register_gpiod(mdiodev);
- if (err)
- return err;
-
- err = mdiobus_register_reset(mdiodev);
+ err = mdio_device_register_reset(mdiodev);
if (err)
return err;
@@ -91,8 +61,7 @@ int mdiobus_unregister_device(struct mdio_device *mdiodev)
if (mdiodev->bus->mdio_map[mdiodev->addr] != mdiodev)
return -EINVAL;
- gpiod_put(mdiodev->reset_gpio);
- reset_control_put(mdiodev->reset_ctrl);
+ mdio_device_unregister_reset(mdiodev);
mdiodev->bus->mdio_map[mdiodev->addr] = NULL;
@@ -370,7 +339,7 @@ EXPORT_SYMBOL_GPL(mdio_bus_class);
* mdio_find_bus - Given the name of a mdiobus, find the mii_bus.
* @mdio_name: The name of a mdiobus.
*
- * Returns a reference to the mii_bus, or NULL if none found. The
+ * Return: a reference to the mii_bus, or NULL if none found. The
* embedded struct device will have its reference count incremented,
* and this must be put_deviced'ed once the bus is finished with.
*/
@@ -388,7 +357,7 @@ EXPORT_SYMBOL(mdio_find_bus);
* of_mdio_find_bus - Given an mii_bus node, find the mii_bus.
* @mdio_bus_np: Pointer to the mii_bus.
*
- * Returns a reference to the mii_bus, or NULL if none found. The
+ * Return: a reference to the mii_bus, or NULL if none found. The
* embedded struct device will have its reference count incremented,
* and this must be put once the bus is finished with.
*
@@ -436,6 +405,8 @@ out:
* @addr: the phy address
* @regnum: register number to read
*
+ * Return: The register value if successful, negative error code on failure
+ *
* Read a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
@@ -468,6 +439,8 @@ EXPORT_SYMBOL(__mdiobus_read);
* @regnum: register number to write
* @val: value to write to @regnum
*
+ * Return: Zero if successful, negative error code on failure
+ *
* Write a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
@@ -501,8 +474,11 @@ EXPORT_SYMBOL(__mdiobus_write);
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*
+ * Return: 1 if the register was modified, 0 if no change was needed,
+ * negative on any error condition
+ *
* Read, modify, and if any change, write the register value back to the
- * device. Any error returns a negative number.
+ * device.
*
* NOTE: MUST NOT be called from interrupt context.
*/
@@ -532,6 +508,8 @@ EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
* @devad: device address to read
* @regnum: register number to read
*
+ * Return: The register value if successful, negative error code on failure
+ *
* Read a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
@@ -565,6 +543,8 @@ EXPORT_SYMBOL(__mdiobus_c45_read);
* @regnum: register number to write
* @val: value to write to @regnum
*
+ * Return: Zero if successful, negative error code on failure
+ *
* Write a MDIO bus register. Caller must hold the mdio bus lock.
*
* NOTE: MUST NOT be called from interrupt context.
@@ -600,6 +580,9 @@ EXPORT_SYMBOL(__mdiobus_c45_write);
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*
+ * Return: 1 if the register was modified, 0 if no change was needed,
+ * negative on any error condition
+ *
* Read, modify, and if any change, write the register value back to the
* device. Any error returns a negative number.
*
@@ -630,6 +613,8 @@ static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
* @addr: the phy address
* @regnum: register number to read
*
+ * Return: The register value if successful, negative error code on failure
+ *
* In case of nested MDIO bus access avoid lockdep false positives by
* using mutex_lock_nested().
*
@@ -655,6 +640,8 @@ EXPORT_SYMBOL(mdiobus_read_nested);
* @addr: the phy address
* @regnum: register number to read
*
+ * Return: The register value if successful, negative error code on failure
+ *
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
@@ -678,6 +665,8 @@ EXPORT_SYMBOL(mdiobus_read);
* @devad: device address to read
* @regnum: register number to read
*
+ * Return: The register value if successful, negative error code on failure
+ *
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
@@ -701,6 +690,8 @@ EXPORT_SYMBOL(mdiobus_c45_read);
* @devad: device address to read
* @regnum: register number to read
*
+ * Return: The register value if successful, negative error code on failure
+ *
* In case of nested MDIO bus access avoid lockdep false positives by
* using mutex_lock_nested().
*
@@ -728,6 +719,8 @@ EXPORT_SYMBOL(mdiobus_c45_read_nested);
* @regnum: register number to write
* @val: value to write to @regnum
*
+ * Return: Zero if successful, negative error code on failure
+ *
* In case of nested MDIO bus access avoid lockdep false positives by
* using mutex_lock_nested().
*
@@ -754,6 +747,8 @@ EXPORT_SYMBOL(mdiobus_write_nested);
* @regnum: register number to write
* @val: value to write to @regnum
*
+ * Return: Zero if successful, negative error code on failure
+ *
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
@@ -778,6 +773,8 @@ EXPORT_SYMBOL(mdiobus_write);
* @regnum: register number to write
* @val: value to write to @regnum
*
+ * Return: Zero if successful, negative error code on failure
+ *
* NOTE: MUST NOT be called from interrupt context,
* because the bus read/write functions may wait for an interrupt
* to conclude the operation.
@@ -803,6 +800,8 @@ EXPORT_SYMBOL(mdiobus_c45_write);
* @regnum: register number to write
* @val: value to write to @regnum
*
+ * Return: Zero if successful, negative error code on failure
+ *
* In case of nested MDIO bus access avoid lockdep false positives by
* using mutex_lock_nested().
*
@@ -831,6 +830,8 @@ EXPORT_SYMBOL(mdiobus_c45_write_nested);
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
+ *
+ * Return: 0 on success, negative on any error condition
*/
int __mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask,
u16 set)
@@ -851,6 +852,8 @@ EXPORT_SYMBOL_GPL(__mdiobus_modify);
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
+ *
+ * Return: 0 on success, negative on any error condition
*/
int mdiobus_modify(struct mii_bus *bus, int addr, u32 regnum, u16 mask, u16 set)
{
@@ -873,6 +876,8 @@ EXPORT_SYMBOL_GPL(mdiobus_modify);
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
+ *
+ * Return: 0 on success, negative on any error condition
*/
int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
u16 mask, u16 set)
@@ -896,6 +901,9 @@ EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
+ *
+ * Return: 1 if the register was modified, 0 if no change was needed,
+ * negative on any error condition
*/
int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
u16 mask, u16 set)
@@ -919,6 +927,9 @@ EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
* @regnum: register number to write
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
+ *
+ * Return: 1 if the register was modified, 0 if no change was needed,
+ * negative on any error condition
*/
int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
u32 regnum, u16 mask, u16 set)
@@ -939,10 +950,10 @@ EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
* @dev: target MDIO device
* @drv: given MDIO driver
*
- * Description: Given a MDIO device, and a MDIO driver, return 1 if
- * the driver supports the device. Otherwise, return 0. This may
- * require calling the devices own match function, since different classes
- * of MDIO devices have different match criteria.
+ * Return: 1 if the driver supports the device, 0 otherwise
+ *
+ * Description: This may require calling the devices own match function,
+ * since different classes of MDIO devices have different match criteria.
*/
static int mdio_bus_match(struct device *dev, const struct device_driver *drv)
{
diff --git a/drivers/net/phy/mdio_bus_provider.c b/drivers/net/phy/mdio_bus_provider.c
index a2391d4b7e5c..4b0637405740 100644
--- a/drivers/net/phy/mdio_bus_provider.c
+++ b/drivers/net/phy/mdio_bus_provider.c
@@ -249,20 +249,15 @@ static int mdiobus_scan_bus_c45(struct mii_bus *bus)
*/
static bool mdiobus_prevent_c45_scan(struct mii_bus *bus)
{
- int i;
+ struct phy_device *phydev;
- for (i = 0; i < PHY_MAX_ADDR; i++) {
- struct phy_device *phydev;
- u32 oui;
-
- phydev = mdiobus_get_phy(bus, i);
- if (!phydev)
- continue;
- oui = phydev->phy_id >> 10;
+ mdiobus_for_each_phy(bus, phydev) {
+ u32 oui = phydev->phy_id >> 10;
if (oui == MICREL_OUI)
return true;
}
+
return false;
}
diff --git a/drivers/net/phy/mdio_device.c b/drivers/net/phy/mdio_device.c
index f64176e0e197..6e90ed42cd98 100644
--- a/drivers/net/phy/mdio_device.c
+++ b/drivers/net/phy/mdio_device.c
@@ -22,6 +22,7 @@
#include <linux/string.h>
#include <linux/unistd.h>
#include <linux/property.h>
+#include "mdio-private.h"
void mdio_device_free(struct mdio_device *mdiodev)
{
@@ -77,6 +78,8 @@ EXPORT_SYMBOL(mdio_device_create);
/**
* mdio_device_register - Register the mdio device on the MDIO bus
* @mdiodev: mdio_device structure to be added to the MDIO bus
+ *
+ * Return: Zero if successful, negative error code on failure
*/
int mdio_device_register(struct mdio_device *mdiodev)
{
@@ -118,6 +121,59 @@ void mdio_device_remove(struct mdio_device *mdiodev)
}
EXPORT_SYMBOL(mdio_device_remove);
+/**
+ * mdio_device_register_reset - Read and initialize the reset properties of
+ * an mdio device
+ * @mdiodev: mdio_device structure
+ *
+ * Return: Zero if successful, negative error code on failure
+ */
+int mdio_device_register_reset(struct mdio_device *mdiodev)
+{
+ struct reset_control *reset;
+
+ /* Deassert the optional reset signal */
+ mdiodev->reset_gpio = gpiod_get_optional(&mdiodev->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(mdiodev->reset_gpio))
+ return PTR_ERR(mdiodev->reset_gpio);
+
+ if (mdiodev->reset_gpio)
+ gpiod_set_consumer_name(mdiodev->reset_gpio, "PHY reset");
+
+ reset = reset_control_get_optional_exclusive(&mdiodev->dev, "phy");
+ if (IS_ERR(reset)) {
+ gpiod_put(mdiodev->reset_gpio);
+ mdiodev->reset_gpio = NULL;
+ return PTR_ERR(reset);
+ }
+
+ mdiodev->reset_ctrl = reset;
+
+ /* Read optional firmware properties */
+ device_property_read_u32(&mdiodev->dev, "reset-assert-us",
+ &mdiodev->reset_assert_delay);
+ device_property_read_u32(&mdiodev->dev, "reset-deassert-us",
+ &mdiodev->reset_deassert_delay);
+
+ return 0;
+}
+
+/**
+ * mdio_device_unregister_reset - uninitialize the reset properties of
+ * an mdio device
+ * @mdiodev: mdio_device structure
+ */
+void mdio_device_unregister_reset(struct mdio_device *mdiodev)
+{
+ gpiod_put(mdiodev->reset_gpio);
+ mdiodev->reset_gpio = NULL;
+ reset_control_put(mdiodev->reset_ctrl);
+ mdiodev->reset_ctrl = NULL;
+ mdiodev->reset_assert_delay = 0;
+ mdiodev->reset_deassert_delay = 0;
+}
+
void mdio_device_reset(struct mdio_device *mdiodev, int value)
{
unsigned int d;
@@ -152,6 +208,8 @@ EXPORT_SYMBOL(mdio_device_reset);
*
* Description: Take care of setting up the mdio_device structure
* and calling the driver to probe the device.
+ *
+ * Return: Zero if successful, negative error code on failure
*/
static int mdio_probe(struct device *dev)
{
@@ -202,6 +260,8 @@ static void mdio_shutdown(struct device *dev)
/**
* mdio_driver_register - register an mdio_driver with the MDIO layer
* @drv: new mdio_driver to register
+ *
+ * Return: Zero if successful, negative error code on failure
*/
int mdio_driver_register(struct mdio_driver *drv)
{
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 79ce3eb6752b..05de68b9f719 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -101,6 +101,8 @@
#define LAN8814_CABLE_DIAG_VCT_DATA_MASK GENMASK(7, 0)
#define LAN8814_PAIR_BIT_SHIFT 12
+#define LAN8814_SKUS 0xB
+
#define LAN8814_WIRE_PAIR_MASK 0xF
/* Lan8814 general Interrupt control/status reg in GPHY specific block. */
@@ -367,6 +369,9 @@
#define LAN8842_REV_8832 0x8832
+#define LAN8814_REV_LAN8814 0x8814
+#define LAN8814_REV_LAN8818 0x8818
+
struct kszphy_hw_stat {
const char *string;
u8 reg;
@@ -449,6 +454,7 @@ struct kszphy_priv {
bool rmii_ref_clk_sel;
bool rmii_ref_clk_sel_val;
bool clk_enable;
+ bool is_ptp_available;
u64 stats[ARRAY_SIZE(kszphy_hw_stats)];
struct kszphy_phy_stats phy_stats;
};
@@ -466,6 +472,12 @@ struct lan8842_priv {
u16 rev;
};
+struct lanphy_reg_data {
+ int page;
+ u16 addr;
+ u16 val;
+};
+
static const struct kszphy_type lan8814_type = {
.led_mode_reg = ~LAN8814_LED_CTRL_1,
.cable_diag_reg = LAN8814_CABLE_DIAG,
@@ -1050,7 +1062,7 @@ static int ksz9021_config_init(struct phy_device *phydev)
#define TX_CLK_ID 0x1f
/* set tx and tx_clk to "No delay adjustment" to keep 0ns
- * dealy
+ * delay
*/
#define TX_ND 0x7
#define TX_CLK_ND 0xf
@@ -1913,7 +1925,7 @@ static int ksz886x_config_aneg(struct phy_device *phydev)
return ret;
if (phydev->autoneg != AUTONEG_ENABLE) {
- /* When autonegotation is disabled, we need to manually force
+ /* When autonegotiation is disabled, we need to manually force
* the link state. If we don't do this, the PHY will keep
* sending Fast Link Pulses (FLPs) which are part of the
* autonegotiation process. This is not desired when
@@ -2095,11 +2107,7 @@ static int ksz9477_phy_errata(struct phy_device *phydev)
return err;
}
- err = genphy_restart_aneg(phydev);
- if (err)
- return err;
-
- return err;
+ return genphy_restart_aneg(phydev);
}
static int ksz9477_config_init(struct phy_device *phydev)
@@ -2323,6 +2331,106 @@ static int kszphy_get_sqi_max(struct phy_device *phydev)
return KSZ9477_SQI_MAX;
}
+static int kszphy_get_mse_capability(struct phy_device *phydev,
+ struct phy_mse_capability *cap)
+{
+ /* Capabilities depend on link mode:
+ * - 1000BASE-T: per-pair SQI registers exist => expose A..D
+ * and a WORST selector.
+ * - 100BASE-TX: HW provides a single MSE/SQI reading in the "channel A"
+ * register, but with auto MDI-X there is no MDI-X resolution bit,
+ * so we cannot map that register to a specific wire pair reliably.
+ * To avoid misleading per-channel data, advertise only LINK.
+ * Other speeds: no MSE exposure via this driver.
+ *
+ * Note: WORST is *not* a hardware selector on this family.
+ * We expose it because the driver computes it in software
+ * by scanning per-channel readouts (A..D) and picking the
+ * maximum average MSE.
+ */
+ if (phydev->speed == SPEED_1000)
+ cap->supported_caps = PHY_MSE_CAP_CHANNEL_A |
+ PHY_MSE_CAP_CHANNEL_B |
+ PHY_MSE_CAP_CHANNEL_C |
+ PHY_MSE_CAP_CHANNEL_D |
+ PHY_MSE_CAP_WORST_CHANNEL;
+ else if (phydev->speed == SPEED_100)
+ cap->supported_caps = PHY_MSE_CAP_LINK;
+ else
+ return -EOPNOTSUPP;
+
+ cap->max_average_mse = FIELD_MAX(KSZ9477_MMD_SQI_MASK);
+ cap->refresh_rate_ps = 2000000; /* 2 us */
+ /* Estimated from link modulation (125 MBd per channel) and documented
+ * refresh rate of 2 us
+ */
+ cap->num_symbols = 250;
+
+ cap->supported_caps |= PHY_MSE_CAP_AVG;
+
+ return 0;
+}
+
+static int kszphy_get_mse_snapshot(struct phy_device *phydev,
+ enum phy_mse_channel channel,
+ struct phy_mse_snapshot *snapshot)
+{
+ u8 num_channels;
+ int ret;
+
+ if (phydev->speed == SPEED_1000)
+ num_channels = 4;
+ else if (phydev->speed == SPEED_100)
+ num_channels = 1;
+ else
+ return -EOPNOTSUPP;
+
+ if (channel == PHY_MSE_CHANNEL_WORST) {
+ u32 worst_val = 0;
+ int i;
+
+ /* WORST is implemented in software: select the maximum
+ * average MSE across the available per-channel registers.
+ * Only defined when multiple channels exist (1000BASE-T).
+ */
+ if (num_channels < 2)
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < num_channels; i++) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+ KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A + i);
+ if (ret < 0)
+ return ret;
+
+ ret = FIELD_GET(KSZ9477_MMD_SQI_MASK, ret);
+ if (ret > worst_val)
+ worst_val = ret;
+ }
+ snapshot->average_mse = worst_val;
+ } else if (channel == PHY_MSE_CHANNEL_LINK && num_channels == 1) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+ KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A);
+ if (ret < 0)
+ return ret;
+ snapshot->average_mse = FIELD_GET(KSZ9477_MMD_SQI_MASK, ret);
+ } else if (channel >= PHY_MSE_CHANNEL_A &&
+ channel <= PHY_MSE_CHANNEL_D) {
+ /* Per-channel readouts are valid only for 1000BASE-T. */
+ if (phydev->speed != SPEED_1000)
+ return -EOPNOTSUPP;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+ KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A + channel);
+ if (ret < 0)
+ return ret;
+ snapshot->average_mse = FIELD_GET(KSZ9477_MMD_SQI_MASK, ret);
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static void kszphy_enable_clk(struct phy_device *phydev)
{
struct kszphy_priv *priv = phydev->priv;
@@ -2836,6 +2944,13 @@ static int ksz886x_cable_test_get_status(struct phy_device *phydev,
#define LAN8814_PAGE_PCS_DIGITAL 2
/**
+ * LAN8814_PAGE_EEE - Selects Extended Page 3.
+ *
+ * This page contains EEE registers
+ */
+#define LAN8814_PAGE_EEE 3
+
+/**
* LAN8814_PAGE_COMMON_REGS - Selects Extended Page 4.
*
* This page contains device-common registers that affect the entire chip.
@@ -2854,6 +2969,13 @@ static int ksz886x_cable_test_get_status(struct phy_device *phydev,
#define LAN8814_PAGE_PORT_REGS 5
/**
+ * LAN8814_PAGE_POWER_REGS - Selects Extended Page 28.
+ *
+ * This page contains analog control registers and power mode registers.
+ */
+#define LAN8814_PAGE_POWER_REGS 28
+
+/**
* LAN8814_PAGE_SYSTEM_CTRL - Selects Extended Page 31.
*
* This page appears to hold fundamental system or global controls. In the
@@ -2866,6 +2988,8 @@ static int ksz886x_cable_test_get_status(struct phy_device *phydev,
#define LAN_EXT_PAGE_ACCESS_ADDRESS_DATA 0x17
#define LAN_EXT_PAGE_ACCESS_CTRL_EP_FUNC 0x4000
+#define LAN8814_QSGMII_TX_CONFIG 0x35
+#define LAN8814_QSGMII_TX_CONFIG_QSGMII BIT(3)
#define LAN8814_QSGMII_SOFT_RESET 0x43
#define LAN8814_QSGMII_SOFT_RESET_BIT BIT(0)
#define LAN8814_QSGMII_PCS1G_ANEG_CONFIG 0x13
@@ -3023,9 +3147,9 @@ static void lan8814_flush_fifo(struct phy_device *phydev, bool egress)
lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS, PTP_TSU_INT_STS);
}
-static int lan8814_hwtstamp(struct mii_timestamper *mii_ts,
- struct kernel_hwtstamp_config *config,
- struct netlink_ext_ack *extack)
+static int lan8814_hwtstamp_set(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct kszphy_ptp_priv *ptp_priv =
container_of(mii_ts, struct kszphy_ptp_priv, mii_ts);
@@ -3537,7 +3661,7 @@ static void lan8814_ptp_disable_event(struct phy_device *phydev, int event)
/* Set target to too far in the future, effectively disabling it */
lan8814_ptp_set_target(phydev, event, 0xFFFFFFFF, 0);
- /* And then reload once it recheas the target */
+ /* And then reload once it reaches the target */
lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS, LAN8814_PTP_GENERAL_CONFIG,
LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event),
LAN8814_PTP_GENERAL_CONFIG_RELOAD_ADD_X(event));
@@ -4130,6 +4254,17 @@ static int lan8804_config_intr(struct phy_device *phydev)
return 0;
}
+/* Check if the PHY has 1588 support. There are multiple skus of the PHY and
+ * some of them support PTP while others don't support it. This function will
+ * return true is the sku supports it, otherwise will return false.
+ */
+static bool lan8814_has_ptp(struct phy_device *phydev)
+{
+ struct kszphy_priv *priv = phydev->priv;
+
+ return priv->is_ptp_available;
+}
+
static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev)
{
int ret = IRQ_NONE;
@@ -4146,6 +4281,9 @@ static irqreturn_t lan8814_handle_interrupt(struct phy_device *phydev)
ret = IRQ_HANDLED;
}
+ if (!lan8814_has_ptp(phydev))
+ return ret;
+
while (true) {
irq_status = lanphy_read_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
PTP_TSU_INT_STS);
@@ -4207,6 +4345,9 @@ static void lan8814_ptp_init(struct phy_device *phydev)
!IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING))
return;
+ if (!lan8814_has_ptp(phydev))
+ return;
+
lanphy_write_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
TSU_HARD_RESET, TSU_HARD_RESET_);
@@ -4248,7 +4389,7 @@ static void lan8814_ptp_init(struct phy_device *phydev)
ptp_priv->mii_ts.rxtstamp = lan8814_rxtstamp;
ptp_priv->mii_ts.txtstamp = lan8814_txtstamp;
- ptp_priv->mii_ts.hwtstamp = lan8814_hwtstamp;
+ ptp_priv->mii_ts.hwtstamp_set = lan8814_hwtstamp_set;
ptp_priv->mii_ts.ts_info = lan8814_ts_info;
phydev->mii_ts = &ptp_priv->mii_ts;
@@ -4262,6 +4403,8 @@ static int __lan8814_ptp_probe_once(struct phy_device *phydev, char *pin_name,
{
struct lan8814_shared_priv *shared = phy_package_get_priv(phydev);
+ shared->phydev = phydev;
+
/* Initialise shared lock for clock*/
mutex_init(&shared->shared_lock);
@@ -4317,8 +4460,6 @@ static int __lan8814_ptp_probe_once(struct phy_device *phydev, char *pin_name,
phydev_dbg(phydev, "successfully registered ptp clock\n");
- shared->phydev = phydev;
-
/* The EP.4 is shared between all the PHYs in the package and also it
* can be accessed by any of the PHYs
*/
@@ -4336,6 +4477,9 @@ static int __lan8814_ptp_probe_once(struct phy_device *phydev, char *pin_name,
static int lan8814_ptp_probe_once(struct phy_device *phydev)
{
+ if (!lan8814_has_ptp(phydev))
+ return 0;
+
return __lan8814_ptp_probe_once(phydev, "lan8814_ptp_pin",
LAN8814_PTP_GPIO_NUM);
}
@@ -4359,18 +4503,24 @@ static void lan8814_setup_led(struct phy_device *phydev, int val)
static int lan8814_config_init(struct phy_device *phydev)
{
struct kszphy_priv *lan8814 = phydev->priv;
+ int ret;
- /* Reset the PHY */
- lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
- LAN8814_QSGMII_SOFT_RESET,
- LAN8814_QSGMII_SOFT_RESET_BIT,
- LAN8814_QSGMII_SOFT_RESET_BIT);
-
- /* Disable ANEG with QSGMII PCS Host side */
- lanphy_modify_page_reg(phydev, LAN8814_PAGE_PORT_REGS,
- LAN8814_QSGMII_PCS1G_ANEG_CONFIG,
- LAN8814_QSGMII_PCS1G_ANEG_CONFIG_ANEG_ENA,
- 0);
+ /* Based on the interface type select how the advertise ability is
+ * encoded, to set as SGMII or as USGMII.
+ */
+ if (phydev->interface == PHY_INTERFACE_MODE_QSGMII)
+ ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_QSGMII_TX_CONFIG,
+ LAN8814_QSGMII_TX_CONFIG_QSGMII,
+ LAN8814_QSGMII_TX_CONFIG_QSGMII);
+ else
+ ret = lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_QSGMII_TX_CONFIG,
+ LAN8814_QSGMII_TX_CONFIG_QSGMII,
+ 0);
+
+ if (ret < 0)
+ return ret;
/* MDI-X setting for swap A,B transmit */
lanphy_modify_page_reg(phydev, LAN8814_PAGE_PCS_DIGITAL, LAN8814_ALIGN_SWAP,
@@ -4407,7 +4557,7 @@ static int lan8814_release_coma_mode(struct phy_device *phydev)
static void lan8814_clear_2psp_bit(struct phy_device *phydev)
{
/* It was noticed that when traffic is passing through the PHY and the
- * cable is removed then the LED was still one even though there is no
+ * cable is removed then the LED was still on even though there is no
* link
*/
lanphy_modify_page_reg(phydev, LAN8814_PAGE_PCS_DIGITAL, LAN8814_EEE_STATE,
@@ -4450,7 +4600,25 @@ static int lan8814_probe(struct phy_device *phydev)
devm_phy_package_join(&phydev->mdio.dev, phydev,
addr, sizeof(struct lan8814_shared_priv));
+ /* There are lan8814 SKUs that don't support PTP. Make sure that for
+ * those skus no PTP device is created. Here we check if the SKU
+ * supports PTP.
+ */
+ err = lanphy_read_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_SKUS);
+ if (err < 0)
+ return err;
+
+ priv->is_ptp_available = err == LAN8814_REV_LAN8814 ||
+ err == LAN8814_REV_LAN8818;
+
if (phy_package_init_once(phydev)) {
+ /* Reset the PHY */
+ lanphy_modify_page_reg(phydev, LAN8814_PAGE_COMMON_REGS,
+ LAN8814_QSGMII_SOFT_RESET,
+ LAN8814_QSGMII_SOFT_RESET_BIT,
+ LAN8814_QSGMII_SOFT_RESET_BIT);
+
err = lan8814_release_coma_mode(phydev);
if (err)
return err;
@@ -4547,7 +4715,7 @@ static int lan8841_config_init(struct phy_device *phydev)
phy_write_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG,
LAN8841_PTP_TX_VERSION, 0xff00);
- /* 100BT Clause 40 improvenent errata */
+ /* 100BT Clause 40 improvement errata */
phy_write_mmd(phydev, LAN8841_MMD_ANALOG_REG,
LAN8841_ANALOG_CONTROL_1,
LAN8841_ANALOG_CONTROL_1_PLL_TRIM(0x2));
@@ -4874,9 +5042,9 @@ static void lan8841_ptp_enable_processing(struct kszphy_ptp_priv *ptp_priv,
#define LAN8841_PTP_TX_TIMESTAMP_EN 443
#define LAN8841_PTP_TX_MOD 445
-static int lan8841_hwtstamp(struct mii_timestamper *mii_ts,
- struct kernel_hwtstamp_config *config,
- struct netlink_ext_ack *extack)
+static int lan8841_hwtstamp_set(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct kszphy_ptp_priv *ptp_priv = container_of(mii_ts, struct kszphy_ptp_priv, mii_ts);
struct phy_device *phydev = ptp_priv->phydev;
@@ -5567,7 +5735,7 @@ static int lan8841_ptp_extts_on(struct kszphy_ptp_priv *ptp_priv, int pin,
u16 tmp = 0;
int ret;
- /* Set GPIO to be intput */
+ /* Set GPIO to be input */
ret = phy_set_bits_mmd(phydev, 2, LAN8841_GPIO_EN, BIT(pin));
if (ret)
return ret;
@@ -5756,7 +5924,7 @@ static int lan8841_probe(struct phy_device *phydev)
ptp_priv->mii_ts.rxtstamp = lan8841_rxtstamp;
ptp_priv->mii_ts.txtstamp = lan8814_txtstamp;
- ptp_priv->mii_ts.hwtstamp = lan8841_hwtstamp;
+ ptp_priv->mii_ts.hwtstamp_set = lan8841_hwtstamp_set;
ptp_priv->mii_ts.ts_info = lan8841_ts_info;
phydev->mii_ts = &ptp_priv->mii_ts;
@@ -5884,6 +6052,144 @@ static int lan8842_probe(struct phy_device *phydev)
return 0;
}
+#define LAN8814_POWER_MGMT_MODE_3_ANEG_MDI 0x13
+#define LAN8814_POWER_MGMT_MODE_4_ANEG_MDIX 0x14
+#define LAN8814_POWER_MGMT_MODE_5_10BT_MDI 0x15
+#define LAN8814_POWER_MGMT_MODE_6_10BT_MDIX 0x16
+#define LAN8814_POWER_MGMT_MODE_7_100BT_TRAIN 0x17
+#define LAN8814_POWER_MGMT_MODE_8_100BT_MDI 0x18
+#define LAN8814_POWER_MGMT_MODE_9_100BT_EEE_MDI_TX 0x19
+#define LAN8814_POWER_MGMT_MODE_10_100BT_EEE_MDI_RX 0x1a
+#define LAN8814_POWER_MGMT_MODE_11_100BT_MDIX 0x1b
+#define LAN8814_POWER_MGMT_MODE_12_100BT_EEE_MDIX_TX 0x1c
+#define LAN8814_POWER_MGMT_MODE_13_100BT_EEE_MDIX_RX 0x1d
+#define LAN8814_POWER_MGMT_MODE_14_100BTX_EEE_TX_RX 0x1e
+
+#define LAN8814_POWER_MGMT_DLLPD_D BIT(0)
+#define LAN8814_POWER_MGMT_ADCPD_D BIT(1)
+#define LAN8814_POWER_MGMT_PGAPD_D BIT(2)
+#define LAN8814_POWER_MGMT_TXPD_D BIT(3)
+#define LAN8814_POWER_MGMT_DLLPD_C BIT(4)
+#define LAN8814_POWER_MGMT_ADCPD_C BIT(5)
+#define LAN8814_POWER_MGMT_PGAPD_C BIT(6)
+#define LAN8814_POWER_MGMT_TXPD_C BIT(7)
+#define LAN8814_POWER_MGMT_DLLPD_B BIT(8)
+#define LAN8814_POWER_MGMT_ADCPD_B BIT(9)
+#define LAN8814_POWER_MGMT_PGAPD_B BIT(10)
+#define LAN8814_POWER_MGMT_TXPD_B BIT(11)
+#define LAN8814_POWER_MGMT_DLLPD_A BIT(12)
+#define LAN8814_POWER_MGMT_ADCPD_A BIT(13)
+#define LAN8814_POWER_MGMT_PGAPD_A BIT(14)
+#define LAN8814_POWER_MGMT_TXPD_A BIT(15)
+
+#define LAN8814_POWER_MGMT_C_D (LAN8814_POWER_MGMT_DLLPD_D | \
+ LAN8814_POWER_MGMT_ADCPD_D | \
+ LAN8814_POWER_MGMT_PGAPD_D | \
+ LAN8814_POWER_MGMT_DLLPD_C | \
+ LAN8814_POWER_MGMT_ADCPD_C | \
+ LAN8814_POWER_MGMT_PGAPD_C)
+
+#define LAN8814_POWER_MGMT_B_C_D (LAN8814_POWER_MGMT_C_D | \
+ LAN8814_POWER_MGMT_DLLPD_B | \
+ LAN8814_POWER_MGMT_ADCPD_B | \
+ LAN8814_POWER_MGMT_PGAPD_B)
+
+#define LAN8814_POWER_MGMT_VAL1 (LAN8814_POWER_MGMT_C_D | \
+ LAN8814_POWER_MGMT_ADCPD_B | \
+ LAN8814_POWER_MGMT_PGAPD_B | \
+ LAN8814_POWER_MGMT_ADCPD_A | \
+ LAN8814_POWER_MGMT_PGAPD_A)
+
+#define LAN8814_POWER_MGMT_VAL2 LAN8814_POWER_MGMT_C_D
+
+#define LAN8814_POWER_MGMT_VAL3 (LAN8814_POWER_MGMT_C_D | \
+ LAN8814_POWER_MGMT_DLLPD_B | \
+ LAN8814_POWER_MGMT_ADCPD_B | \
+ LAN8814_POWER_MGMT_PGAPD_A)
+
+#define LAN8814_POWER_MGMT_VAL4 (LAN8814_POWER_MGMT_B_C_D | \
+ LAN8814_POWER_MGMT_ADCPD_A | \
+ LAN8814_POWER_MGMT_PGAPD_A)
+
+#define LAN8814_POWER_MGMT_VAL5 LAN8814_POWER_MGMT_B_C_D
+
+#define LAN8814_EEE_WAKE_TX_TIMER 0x0e
+#define LAN8814_EEE_WAKE_TX_TIMER_MAX_VAL 0x1f
+
+static const struct lanphy_reg_data short_center_tap_errata[] = {
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_3_ANEG_MDI,
+ LAN8814_POWER_MGMT_VAL1 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_4_ANEG_MDIX,
+ LAN8814_POWER_MGMT_VAL1 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_5_10BT_MDI,
+ LAN8814_POWER_MGMT_VAL1 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_6_10BT_MDIX,
+ LAN8814_POWER_MGMT_VAL1 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_7_100BT_TRAIN,
+ LAN8814_POWER_MGMT_VAL2 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_8_100BT_MDI,
+ LAN8814_POWER_MGMT_VAL3 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_9_100BT_EEE_MDI_TX,
+ LAN8814_POWER_MGMT_VAL3 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_10_100BT_EEE_MDI_RX,
+ LAN8814_POWER_MGMT_VAL4 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_11_100BT_MDIX,
+ LAN8814_POWER_MGMT_VAL5 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_12_100BT_EEE_MDIX_TX,
+ LAN8814_POWER_MGMT_VAL5 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_13_100BT_EEE_MDIX_RX,
+ LAN8814_POWER_MGMT_VAL4 },
+ { LAN8814_PAGE_POWER_REGS,
+ LAN8814_POWER_MGMT_MODE_14_100BTX_EEE_TX_RX,
+ LAN8814_POWER_MGMT_VAL4 },
+};
+
+static const struct lanphy_reg_data waketx_timer_errata[] = {
+ { LAN8814_PAGE_EEE,
+ LAN8814_EEE_WAKE_TX_TIMER,
+ LAN8814_EEE_WAKE_TX_TIMER_MAX_VAL },
+};
+
+static int lanphy_write_reg_data(struct phy_device *phydev,
+ const struct lanphy_reg_data *data,
+ size_t num)
+{
+ int ret = 0;
+
+ while (num--) {
+ ret = lanphy_write_page_reg(phydev, data->page, data->addr,
+ data->val);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int lan8842_erratas(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = lanphy_write_reg_data(phydev, short_center_tap_errata,
+ ARRAY_SIZE(short_center_tap_errata));
+ if (ret)
+ return ret;
+
+ return lanphy_write_reg_data(phydev, waketx_timer_errata,
+ ARRAY_SIZE(waketx_timer_errata));
+}
+
static int lan8842_config_init(struct phy_device *phydev)
{
int ret;
@@ -5896,6 +6202,11 @@ static int lan8842_config_init(struct phy_device *phydev)
if (ret < 0)
return ret;
+ /* Apply the erratas for this device */
+ ret = lan8842_erratas(phydev);
+ if (ret < 0)
+ return ret;
+
/* Even if the GPIOs are set to control the LEDs the behaviour of the
* LEDs is wrong, they are not blinking when there is traffic.
* To fix this it is required to set extended LED mode
@@ -6343,6 +6654,8 @@ static struct phy_driver ksphy_driver[] = {
.suspend = genphy_suspend,
.resume = kszphy_resume,
.config_intr = lan8814_config_intr,
+ .inband_caps = lan8842_inband_caps,
+ .config_inband = lan8842_config_inband,
.handle_interrupt = lan8814_handle_interrupt,
.cable_test_start = lan8814_cable_test_start,
.cable_test_get_status = ksz886x_cable_test_get_status,
@@ -6463,6 +6776,8 @@ static struct phy_driver ksphy_driver[] = {
.cable_test_get_status = ksz9x31_cable_test_get_status,
.get_sqi = kszphy_get_sqi,
.get_sqi_max = kszphy_get_sqi_max,
+ .get_mse_capability = kszphy_get_mse_capability,
+ .get_mse_snapshot = kszphy_get_mse_snapshot,
} };
module_phy_driver(ksphy_driver);
diff --git a/drivers/net/phy/microchip_rds_ptp.c b/drivers/net/phy/microchip_rds_ptp.c
index e6514ce04c29..4c6326b0ceaf 100644
--- a/drivers/net/phy/microchip_rds_ptp.c
+++ b/drivers/net/phy/microchip_rds_ptp.c
@@ -476,9 +476,9 @@ static bool mchp_rds_ptp_rxtstamp(struct mii_timestamper *mii_ts,
return true;
}
-static int mchp_rds_ptp_hwtstamp(struct mii_timestamper *mii_ts,
- struct kernel_hwtstamp_config *config,
- struct netlink_ext_ack *extack)
+static int mchp_rds_ptp_hwtstamp_set(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *config,
+ struct netlink_ext_ack *extack)
{
struct mchp_rds_ptp_clock *clock =
container_of(mii_ts, struct mchp_rds_ptp_clock,
@@ -1281,7 +1281,7 @@ struct mchp_rds_ptp_clock *mchp_rds_ptp_probe(struct phy_device *phydev, u8 mmd,
clock->mii_ts.rxtstamp = mchp_rds_ptp_rxtstamp;
clock->mii_ts.txtstamp = mchp_rds_ptp_txtstamp;
- clock->mii_ts.hwtstamp = mchp_rds_ptp_hwtstamp;
+ clock->mii_ts.hwtstamp_set = mchp_rds_ptp_hwtstamp_set;
clock->mii_ts.ts_info = mchp_rds_ptp_ts_info;
phydev->mii_ts = &clock->mii_ts;
diff --git a/drivers/net/phy/microchip_t1s.c b/drivers/net/phy/microchip_t1s.c
index e50a0c102a86..e601d56b2507 100644
--- a/drivers/net/phy/microchip_t1s.c
+++ b/drivers/net/phy/microchip_t1s.c
@@ -3,7 +3,7 @@
* Driver for Microchip 10BASE-T1S PHYs
*
* Support: Microchip Phys:
- * lan8670/1/2 Rev.B1/C1/C2
+ * lan8670/1/2 Rev.B1/C1/C2/D0
* lan8650/1 Rev.B0/B1 Internal PHYs
*/
@@ -14,6 +14,7 @@
#define PHY_ID_LAN867X_REVB1 0x0007C162
#define PHY_ID_LAN867X_REVC1 0x0007C164
#define PHY_ID_LAN867X_REVC2 0x0007C165
+#define PHY_ID_LAN867X_REVD0 0x0007C166
/* Both Rev.B0 and B1 clause 22 PHYID's are same due to B1 chip limitation */
#define PHY_ID_LAN865X_REVB 0x0007C1B3
@@ -32,6 +33,17 @@
#define COL_DET_ENABLE BIT(15)
#define COL_DET_DISABLE 0x0000
+/* LAN8670/1/2 Rev.D0 Link Status Selection Register */
+#define LAN867X_REG_LINK_STATUS_CTRL 0x0012
+#define LINK_STATUS_CONFIGURATION GENMASK(12, 11)
+#define LINK_STATUS_SEMAPHORE BIT(0)
+
+/* Link Status Configuration */
+#define LINK_STATUS_CONFIG_PLCA_STATUS 0x1
+#define LINK_STATUS_CONFIG_SEMAPHORE 0x2
+
+#define LINK_STATUS_SEMAPHORE_SET 0x1
+
#define LAN865X_CFGPARAM_READ_ENABLE BIT(1)
/* The arrays below are pulled from the following table from AN1699
@@ -109,6 +121,21 @@ static const u16 lan865x_revb_sqi_fixup_cfg_regs[3] = {
0x00AD, 0x00AE, 0x00AF,
};
+/* LAN867x Rev.D0 configuration parameters from AN1699
+ * As per the Configuration Application Note AN1699 published in the below link,
+ * https://www.microchip.com/en-us/application-notes/an1699
+ * Revision G (DS60001699G - October 2025)
+ */
+static const u16 lan867x_revd0_fixup_regs[8] = {
+ 0x0037, 0x008A, 0x0118, 0x00D6,
+ 0x0082, 0x00FD, 0x00FD, 0x0091,
+};
+
+static const u16 lan867x_revd0_fixup_values[8] = {
+ 0x0800, 0xBFC0, 0x029C, 0x1001,
+ 0x001C, 0x0C0B, 0x8C07, 0x9660,
+};
+
/* Pulled from AN1760 describing 'indirect read'
*
* write_register(0x4, 0x00D8, addr)
@@ -377,6 +404,32 @@ static int lan867x_revb1_config_init(struct phy_device *phydev)
return 0;
}
+static int lan867x_revd0_link_active_selection(struct phy_device *phydev,
+ bool plca_enabled)
+{
+ u16 value;
+
+ if (plca_enabled) {
+ /* 0x1 - When PLCA is enabled: link status reflects plca_status.
+ */
+ value = FIELD_PREP(LINK_STATUS_CONFIGURATION,
+ LINK_STATUS_CONFIG_PLCA_STATUS);
+ } else {
+ /* 0x2 - Link status is controlled by the value written into the
+ * LINK_STATUS_SEMAPHORE bit written. Here the link semaphore
+ * bit is written with 0x1 to set the link always active in
+ * CSMA/CD mode as it doesn't support autoneg.
+ */
+ value = FIELD_PREP(LINK_STATUS_CONFIGURATION,
+ LINK_STATUS_CONFIG_SEMAPHORE) |
+ FIELD_PREP(LINK_STATUS_SEMAPHORE,
+ LINK_STATUS_SEMAPHORE_SET);
+ }
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ LAN867X_REG_LINK_STATUS_CTRL, value);
+}
+
/* As per LAN8650/1 Rev.B0/B1 AN1760 (Revision F (DS60001760G - June 2024)) and
* LAN8670/1/2 Rev.C1/C2 AN1699 (Revision E (DS60001699F - June 2024)), under
* normal operation, the device should be operated in PLCA mode. Disabling
@@ -393,6 +446,14 @@ static int lan86xx_plca_set_cfg(struct phy_device *phydev,
{
int ret;
+ /* Link status selection must be configured for LAN8670/1/2 Rev.D0 */
+ if (phydev->phy_id == PHY_ID_LAN867X_REVD0) {
+ ret = lan867x_revd0_link_active_selection(phydev,
+ plca_cfg->enabled);
+ if (ret)
+ return ret;
+ }
+
ret = genphy_c45_plca_set_cfg(phydev, plca_cfg);
if (ret)
return ret;
@@ -407,6 +468,29 @@ static int lan86xx_plca_set_cfg(struct phy_device *phydev,
COL_DET_CTRL0_ENABLE_BIT_MASK, COL_DET_ENABLE);
}
+static int lan867x_revd0_config_init(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = lan867x_check_reset_complete(phydev);
+ if (ret)
+ return ret;
+
+ for (int i = 0; i < ARRAY_SIZE(lan867x_revd0_fixup_regs); i++) {
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ lan867x_revd0_fixup_regs[i],
+ lan867x_revd0_fixup_values[i]);
+ if (ret)
+ return ret;
+ }
+
+ /* Initially the PHY will be in CSMA/CD mode by default. So it is
+ * required to set the link always active as it doesn't support
+ * autoneg.
+ */
+ return lan867x_revd0_link_active_selection(phydev, false);
+}
+
static int lan86xx_read_status(struct phy_device *phydev)
{
/* The phy has some limitations, namely:
@@ -482,6 +566,19 @@ static struct phy_driver microchip_t1s_driver[] = {
.get_plca_status = genphy_c45_plca_get_status,
},
{
+ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVD0),
+ .name = "LAN867X Rev.D0",
+ .features = PHY_BASIC_T1S_P2MP_FEATURES,
+ .config_init = lan867x_revd0_config_init,
+ .get_plca_cfg = genphy_c45_plca_get_cfg,
+ .set_plca_cfg = lan86xx_plca_set_cfg,
+ .get_plca_status = genphy_c45_plca_get_status,
+ .cable_test_start = genphy_c45_oatc14_cable_test_start,
+ .cable_test_get_status = genphy_c45_oatc14_cable_test_get_status,
+ .get_sqi = genphy_c45_oatc14_get_sqi,
+ .get_sqi_max = genphy_c45_oatc14_get_sqi_max,
+ },
+ {
PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB),
.name = "LAN865X Rev.B0/B1 Internal Phy",
.features = PHY_BASIC_T1S_P2MP_FEATURES,
@@ -501,6 +598,7 @@ static const struct mdio_device_id __maybe_unused tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVB1) },
{ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC1) },
{ PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVC2) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_LAN867X_REVD0) },
{ PHY_ID_MATCH_EXACT(PHY_ID_LAN865X_REVB) },
{ }
};
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index a3593e663059..89b5b19a9bd2 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -3048,6 +3048,9 @@ static struct phy_driver motorcomm_phy_drvs[] = {
.get_wol = ytphy_get_wol,
.set_wol = yt8531_set_wol,
.link_change_notify = yt8531_link_change_notify,
+ .led_hw_is_supported = yt8521_led_hw_is_supported,
+ .led_hw_control_set = yt8521_led_hw_control_set,
+ .led_hw_control_get = yt8521_led_hw_control_get,
},
{
PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h
index 2d8eca54c40a..65c9d7bd9315 100644
--- a/drivers/net/phy/mscc/mscc.h
+++ b/drivers/net/phy/mscc/mscc.h
@@ -85,6 +85,10 @@ enum rgmii_clock_delay {
#define LED_MODE_SEL_MASK(x) (GENMASK(3, 0) << LED_MODE_SEL_POS(x))
#define LED_MODE_SEL(x, mode) (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x))
+#define MSCC_PHY_LED_BEHAVIOR 30
+#define LED_COMBINE_DIS_MASK(x) BIT(x)
+#define LED_COMBINE_DIS(x, dis) (((dis) ? 1 : 0) << (x))
+
#define MSCC_EXT_PAGE_CSR_CNTL_17 17
#define MSCC_EXT_PAGE_CSR_CNTL_18 18
@@ -289,12 +293,12 @@ enum rgmii_clock_delay {
#define PHY_ID_VSC8540 0x00070760
#define PHY_ID_VSC8541 0x00070770
#define PHY_ID_VSC8552 0x000704e0
-#define PHY_ID_VSC856X 0x000707e0
+#define PHY_ID_VSC856X 0x000707e1
#define PHY_ID_VSC8572 0x000704d0
#define PHY_ID_VSC8574 0x000704a0
-#define PHY_ID_VSC8575 0x000707d0
-#define PHY_ID_VSC8582 0x000707b0
-#define PHY_ID_VSC8584 0x000707c0
+#define PHY_ID_VSC8575 0x000707d1
+#define PHY_ID_VSC8582 0x000707b1
+#define PHY_ID_VSC8584 0x000707c1
#define PHY_VENDOR_MSCC 0x00070400
#define MSCC_VDDMAC_1500 1500
diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c
index ef0ef1570d39..2b9fb8a675a6 100644
--- a/drivers/net/phy/mscc/mscc_main.c
+++ b/drivers/net/phy/mscc/mscc_main.c
@@ -22,6 +22,24 @@
#include "mscc_serdes.h"
#include "mscc.h"
+struct vsc85xx_probe_config {
+ const struct vsc85xx_hw_stat *hw_stats;
+ size_t shared_size;
+ size_t nstats;
+ u16 supp_led_modes;
+ u8 nleds;
+ bool check_rate_magic;
+ bool use_package;
+ bool has_ptp;
+};
+
+static const u32 vsc85xx_default_led_modes_4[] = {
+ VSC8531_LINK_1000_ACTIVITY,
+ VSC8531_LINK_100_ACTIVITY,
+ VSC8531_LINK_ACTIVITY,
+ VSC8531_DUPLEX_COLLISION
+};
+
static const struct vsc85xx_hw_stat vsc85xx_hw_stats[] = {
{
.string = "phy_receive_errors",
@@ -177,17 +195,19 @@ static int vsc85xx_led_cntl_set(struct phy_device *phydev,
u8 led_num,
u8 mode)
{
- int rc;
- u16 reg_val;
+ u16 mask = LED_MODE_SEL_MASK(led_num);
+ u16 val = LED_MODE_SEL(led_num, mode);
- mutex_lock(&phydev->lock);
- reg_val = phy_read(phydev, MSCC_PHY_LED_MODE_SEL);
- reg_val &= ~LED_MODE_SEL_MASK(led_num);
- reg_val |= LED_MODE_SEL(led_num, (u16)mode);
- rc = phy_write(phydev, MSCC_PHY_LED_MODE_SEL, reg_val);
- mutex_unlock(&phydev->lock);
+ return phy_modify(phydev, MSCC_PHY_LED_MODE_SEL, mask, val);
+}
- return rc;
+static int vsc85xx_led_combine_disable_set(struct phy_device *phydev,
+ u8 led_num, bool combine_disable)
+{
+ u16 val = LED_COMBINE_DIS(led_num, combine_disable);
+ u16 mask = LED_COMBINE_DIS_MASK(led_num);
+
+ return phy_modify(phydev, MSCC_PHY_LED_BEHAVIOR, mask, val);
}
static int vsc85xx_mdix_get(struct phy_device *phydev, u8 *mdix)
@@ -443,7 +463,7 @@ static int vsc85xx_dt_led_mode_get(struct phy_device *phydev,
#endif /* CONFIG_OF_MDIO */
static int vsc85xx_dt_led_modes_get(struct phy_device *phydev,
- u32 *default_mode)
+ const u32 *default_mode)
{
struct vsc8531_private *priv = phydev->priv;
char led_dt_prop[28];
@@ -1724,12 +1744,6 @@ static int vsc8584_config_init(struct phy_device *phydev)
* in this pre-init function.
*/
if (phy_package_init_once(phydev)) {
- /* The following switch statement assumes that the lowest
- * nibble of the phy_id_mask is always 0. This works because
- * the lowest nibble of the PHY_ID's below are also 0.
- */
- WARN_ON(phydev->drv->phy_id_mask & 0xf);
-
switch (phydev->phy_id & phydev->drv->phy_id_mask) {
case PHY_ID_VSC8504:
case PHY_ID_VSC8552:
@@ -2224,12 +2238,13 @@ static int vsc85xx_config_inband(struct phy_device *phydev, unsigned int modes)
reg_val);
}
-static int vsc8514_probe(struct phy_device *phydev)
+static int vsc85xx_probe_common(struct phy_device *phydev,
+ const struct vsc85xx_probe_config *cfg,
+ const u32 *default_led_mode)
{
struct vsc8531_private *vsc8531;
- u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY,
- VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY,
- VSC8531_DUPLEX_COLLISION};
+ struct device_node *np;
+ int ret;
vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
if (!vsc8531)
@@ -2237,124 +2252,291 @@ static int vsc8514_probe(struct phy_device *phydev)
phydev->priv = vsc8531;
- vsc8584_get_base_addr(phydev);
- devm_phy_package_join(&phydev->mdio.dev, phydev,
- vsc8531->base_addr, 0);
+ /* Check rate magic if needed (only for non-package PHYs) */
+ if (cfg->check_rate_magic) {
+ ret = vsc85xx_edge_rate_magic_get(phydev);
+ if (ret < 0)
+ return ret;
- vsc8531->nleds = 4;
- vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES;
- vsc8531->hw_stats = vsc85xx_hw_stats;
- vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats);
+ vsc8531->rate_magic = ret;
+ }
+
+ /* Set up package if needed */
+ if (cfg->use_package) {
+ vsc8584_get_base_addr(phydev);
+ ret = devm_phy_package_join(&phydev->mdio.dev, phydev,
+ vsc8531->base_addr,
+ cfg->shared_size);
+ if (ret)
+ return ret;
+ }
+
+ /* Configure LED settings */
+ vsc8531->nleds = cfg->nleds;
+ vsc8531->supp_led_modes = cfg->supp_led_modes;
+
+ /* Configure hardware stats */
+ vsc8531->hw_stats = cfg->hw_stats;
+ vsc8531->nstats = cfg->nstats;
vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats,
sizeof(u64), GFP_KERNEL);
if (!vsc8531->stats)
return -ENOMEM;
- return vsc85xx_dt_led_modes_get(phydev, default_mode);
+ /* PTP setup for VSC8584 */
+ if (cfg->has_ptp) {
+ if (phy_package_probe_once(phydev)) {
+ ret = vsc8584_ptp_probe_once(phydev);
+ if (ret)
+ return ret;
+ }
+
+ ret = vsc8584_ptp_probe(phydev);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Check for LED configuration in device tree if available
+ * or fall back to default `vsc8531,led-x-mode` DT properties.
+ */
+ np = of_get_child_by_name(phydev->mdio.dev.of_node, "leds");
+ if (np) {
+ of_node_put(np);
+
+ /* Force to defaults */
+ for (unsigned int i = 0; i < vsc8531->nleds; i++)
+ vsc8531->leds_mode[i] = default_led_mode[i];
+
+ return 0;
+ }
+
+ /* Parse LED modes from device tree */
+ return vsc85xx_dt_led_modes_get(phydev, default_led_mode);
}
-static int vsc8574_probe(struct phy_device *phydev)
+static int vsc85xx_led_brightness_set(struct phy_device *phydev,
+ u8 index, enum led_brightness value)
{
- struct vsc8531_private *vsc8531;
- u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY,
- VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY,
- VSC8531_DUPLEX_COLLISION};
+ struct vsc8531_private *vsc8531 = phydev->priv;
- vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
- if (!vsc8531)
- return -ENOMEM;
+ if (index >= vsc8531->nleds)
+ return -EINVAL;
- phydev->priv = vsc8531;
+ return vsc85xx_led_cntl_set(phydev, index, value == LED_OFF ?
+ VSC8531_FORCE_LED_OFF : VSC8531_FORCE_LED_ON);
+}
- vsc8584_get_base_addr(phydev);
- devm_phy_package_join(&phydev->mdio.dev, phydev,
- vsc8531->base_addr, 0);
+static int vsc85xx_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ static const unsigned long supported = BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX);
+ struct vsc8531_private *vsc8531 = phydev->priv;
- vsc8531->nleds = 4;
- vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES;
- vsc8531->hw_stats = vsc8584_hw_stats;
- vsc8531->nstats = ARRAY_SIZE(vsc8584_hw_stats);
- vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats,
- sizeof(u64), GFP_KERNEL);
- if (!vsc8531->stats)
- return -ENOMEM;
+ if (index >= vsc8531->nleds)
+ return -EINVAL;
- return vsc85xx_dt_led_modes_get(phydev, default_mode);
+ if (rules & ~supported)
+ return -EOPNOTSUPP;
+
+ return 0;
}
-static int vsc8584_probe(struct phy_device *phydev)
+static int vsc85xx_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules)
{
- struct vsc8531_private *vsc8531;
- u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY,
- VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY,
- VSC8531_DUPLEX_COLLISION};
- int ret;
+ struct vsc8531_private *vsc8531 = phydev->priv;
+ u8 mode, behavior;
+ int rc;
- if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) {
- dev_err(&phydev->mdio.dev, "Only VSC8584 revB is supported.\n");
- return -ENOTSUPP;
- }
+ if (index >= vsc8531->nleds)
+ return -EINVAL;
- vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
- if (!vsc8531)
- return -ENOMEM;
+ rc = phy_read(phydev, MSCC_PHY_LED_MODE_SEL);
+ if (rc < 0)
+ return rc;
+ mode = (rc & LED_MODE_SEL_MASK(index)) >> LED_MODE_SEL_POS(index);
- phydev->priv = vsc8531;
+ rc = phy_read(phydev, MSCC_PHY_LED_BEHAVIOR);
+ if (rc < 0)
+ return rc;
+ behavior = (rc & LED_COMBINE_DIS_MASK(index)) >> index;
- vsc8584_get_base_addr(phydev);
- devm_phy_package_join(&phydev->mdio.dev, phydev, vsc8531->base_addr,
- sizeof(struct vsc85xx_shared_private));
+ switch (mode) {
+ case VSC8531_LINK_ACTIVITY:
+ case VSC8531_ACTIVITY:
+ *rules = BIT(TRIGGER_NETDEV_LINK);
+ break;
- vsc8531->nleds = 4;
- vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES;
- vsc8531->hw_stats = vsc8584_hw_stats;
- vsc8531->nstats = ARRAY_SIZE(vsc8584_hw_stats);
- vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats,
- sizeof(u64), GFP_KERNEL);
- if (!vsc8531->stats)
- return -ENOMEM;
+ case VSC8531_LINK_1000_ACTIVITY:
+ *rules = BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK);
+ break;
- if (phy_package_probe_once(phydev)) {
- ret = vsc8584_ptp_probe_once(phydev);
- if (ret)
- return ret;
+ case VSC8531_LINK_100_ACTIVITY:
+ *rules = BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK);
+ break;
+
+ case VSC8531_LINK_10_ACTIVITY:
+ *rules = BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK);
+ break;
+
+ case VSC8531_LINK_100_1000_ACTIVITY:
+ *rules = BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK);
+ break;
+
+ case VSC8531_LINK_10_1000_ACTIVITY:
+ *rules = BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK);
+ break;
+
+ case VSC8531_LINK_10_100_ACTIVITY:
+ *rules = BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK);
+ break;
+
+ default:
+ *rules = 0;
+ break;
}
- ret = vsc8584_ptp_probe(phydev);
- if (ret)
+ if (!behavior && *rules)
+ *rules |= BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX);
+
+ return 0;
+}
+
+static int vsc85xx_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ struct vsc8531_private *vsc8531 = phydev->priv;
+ u8 mode = VSC8531_FORCE_LED_ON;
+ bool combine_disable = false;
+ bool has_rx, has_tx;
+ int ret;
+
+ if (index >= vsc8531->nleds)
+ return -EINVAL;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK))
+ mode = VSC8531_LINK_ACTIVITY;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK_10))
+ mode = VSC8531_LINK_10_ACTIVITY;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK_100))
+ mode = VSC8531_LINK_100_ACTIVITY;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK_1000))
+ mode = VSC8531_LINK_1000_ACTIVITY;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK_100) &&
+ rules & BIT(TRIGGER_NETDEV_LINK_1000))
+ mode = VSC8531_LINK_100_1000_ACTIVITY;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK_10) &&
+ rules & BIT(TRIGGER_NETDEV_LINK_1000))
+ mode = VSC8531_LINK_10_1000_ACTIVITY;
+
+ if (rules & BIT(TRIGGER_NETDEV_LINK_10) &&
+ rules & BIT(TRIGGER_NETDEV_LINK_100))
+ mode = VSC8531_LINK_10_100_ACTIVITY;
+
+ /*
+ * The VSC85xx PHYs provides an option to control LED behavior. By
+ * default, the LEDx combine function is enabled, meaning the LED
+ * will be on when there is link/activity or duplex/collision. If
+ * the combine function is disabled, the LED will be on only for
+ * link or duplex.
+ *
+ * To control this behavior, we check the selected rules. If both
+ * RX and TX activity are not selected, the LED combine function
+ * is disabled; otherwise, it remains enabled.
+ */
+ has_rx = !!(rules & BIT(TRIGGER_NETDEV_RX));
+ has_tx = !!(rules & BIT(TRIGGER_NETDEV_TX));
+ if (!has_rx && !has_tx)
+ combine_disable = true;
+
+ ret = vsc85xx_led_combine_disable_set(phydev, index, combine_disable);
+ if (ret < 0)
return ret;
- return vsc85xx_dt_led_modes_get(phydev, default_mode);
+ return vsc85xx_led_cntl_set(phydev, index, mode);
}
-static int vsc85xx_probe(struct phy_device *phydev)
+static int vsc8514_probe(struct phy_device *phydev)
{
- struct vsc8531_private *vsc8531;
- int rate_magic;
- u32 default_mode[2] = {VSC8531_LINK_1000_ACTIVITY,
- VSC8531_LINK_100_ACTIVITY};
+ static const struct vsc85xx_probe_config vsc8514_cfg = {
+ .nleds = 4,
+ .supp_led_modes = VSC85XX_SUPP_LED_MODES,
+ .hw_stats = vsc85xx_hw_stats,
+ .nstats = ARRAY_SIZE(vsc85xx_hw_stats),
+ .use_package = true,
+ .shared_size = 0,
+ .has_ptp = false,
+ .check_rate_magic = false,
+ };
- rate_magic = vsc85xx_edge_rate_magic_get(phydev);
- if (rate_magic < 0)
- return rate_magic;
+ return vsc85xx_probe_common(phydev, &vsc8514_cfg, vsc85xx_default_led_modes_4);
+}
- vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
- if (!vsc8531)
- return -ENOMEM;
+static int vsc8574_probe(struct phy_device *phydev)
+{
+ static const struct vsc85xx_probe_config vsc8574_cfg = {
+ .nleds = 4,
+ .supp_led_modes = VSC8584_SUPP_LED_MODES,
+ .hw_stats = vsc8584_hw_stats,
+ .nstats = ARRAY_SIZE(vsc8584_hw_stats),
+ .use_package = true,
+ .shared_size = 0,
+ .has_ptp = false,
+ .check_rate_magic = false,
+ };
- phydev->priv = vsc8531;
+ return vsc85xx_probe_common(phydev, &vsc8574_cfg, vsc85xx_default_led_modes_4);
+}
- vsc8531->rate_magic = rate_magic;
- vsc8531->nleds = 2;
- vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES;
- vsc8531->hw_stats = vsc85xx_hw_stats;
- vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats);
- vsc8531->stats = devm_kcalloc(&phydev->mdio.dev, vsc8531->nstats,
- sizeof(u64), GFP_KERNEL);
- if (!vsc8531->stats)
- return -ENOMEM;
+static int vsc8584_probe(struct phy_device *phydev)
+{
+ static const struct vsc85xx_probe_config vsc8584_cfg = {
+ .nleds = 4,
+ .supp_led_modes = VSC8584_SUPP_LED_MODES,
+ .hw_stats = vsc8584_hw_stats,
+ .nstats = ARRAY_SIZE(vsc8584_hw_stats),
+ .use_package = true,
+ .shared_size = sizeof(struct vsc85xx_shared_private),
+ .has_ptp = true,
+ .check_rate_magic = false,
+ };
+
+ return vsc85xx_probe_common(phydev, &vsc8584_cfg, vsc85xx_default_led_modes_4);
+}
+
+static int vsc85xx_probe(struct phy_device *phydev)
+{
+ static const struct vsc85xx_probe_config vsc85xx_cfg = {
+ .nleds = 2,
+ .supp_led_modes = VSC85XX_SUPP_LED_MODES,
+ .hw_stats = vsc85xx_hw_stats,
+ .nstats = ARRAY_SIZE(vsc85xx_hw_stats),
+ .use_package = false,
+ .has_ptp = false,
+ .check_rate_magic = true,
+ };
- return vsc85xx_dt_led_modes_get(phydev, default_mode);
+ return vsc85xx_probe_common(phydev, &vsc85xx_cfg, vsc85xx_default_led_modes_4);
}
static void vsc85xx_remove(struct phy_device *phydev)
@@ -2387,6 +2569,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8502,
@@ -2411,6 +2597,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8504,
@@ -2438,6 +2628,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8514,
@@ -2463,6 +2657,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8530,
@@ -2487,6 +2685,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8531,
@@ -2511,6 +2713,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8540,
@@ -2535,6 +2741,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8541,
@@ -2559,6 +2769,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_sset_count = &vsc85xx_get_sset_count,
.get_strings = &vsc85xx_get_strings,
.get_stats = &vsc85xx_get_stats,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8552,
@@ -2585,11 +2799,14 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
- .phy_id = PHY_ID_VSC856X,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC856X),
.name = "Microsemi GE VSC856X SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
@@ -2609,6 +2826,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8572,
@@ -2625,7 +2846,7 @@ static struct phy_driver vsc85xx_driver[] = {
.suspend = &genphy_suspend,
.resume = &genphy_resume,
.remove = &vsc85xx_remove,
- .probe = &vsc8574_probe,
+ .probe = &vsc8584_probe,
.set_wol = &vsc85xx_wol_set,
.get_wol = &vsc85xx_wol_get,
.get_tunable = &vsc85xx_get_tunable,
@@ -2637,6 +2858,10 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
.phy_id = PHY_ID_VSC8574,
@@ -2648,12 +2873,12 @@ static struct phy_driver vsc85xx_driver[] = {
.config_aneg = &vsc85xx_config_aneg,
.aneg_done = &genphy_aneg_done,
.read_status = &vsc85xx_read_status,
- .handle_interrupt = vsc85xx_handle_interrupt,
+ .handle_interrupt = vsc8584_handle_interrupt,
.config_intr = &vsc85xx_config_intr,
.suspend = &genphy_suspend,
.resume = &genphy_resume,
.remove = &vsc85xx_remove,
- .probe = &vsc8574_probe,
+ .probe = &vsc8584_probe,
.set_wol = &vsc85xx_wol_set,
.get_wol = &vsc85xx_wol_get,
.get_tunable = &vsc85xx_get_tunable,
@@ -2665,11 +2890,14 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
- .phy_id = PHY_ID_VSC8575,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC8575),
.name = "Microsemi GE VSC8575 SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
@@ -2691,11 +2919,14 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
- .phy_id = PHY_ID_VSC8582,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC8582),
.name = "Microsemi GE VSC8582 SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
@@ -2717,11 +2948,14 @@ static struct phy_driver vsc85xx_driver[] = {
.get_stats = &vsc85xx_get_stats,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
},
{
- .phy_id = PHY_ID_VSC8584,
+ PHY_ID_MATCH_EXACT(PHY_ID_VSC8584),
.name = "Microsemi GE VSC8584 SyncE",
- .phy_id_mask = 0xfffffff0,
/* PHY_GBIT_FEATURES */
.soft_reset = &genphy_soft_reset,
.config_init = &vsc8584_config_init,
@@ -2744,6 +2978,10 @@ static struct phy_driver vsc85xx_driver[] = {
.link_change_notify = &vsc85xx_link_change_notify,
.inband_caps = vsc85xx_inband_caps,
.config_inband = vsc85xx_config_inband,
+ .led_brightness_set = vsc85xx_led_brightness_set,
+ .led_hw_is_supported = vsc85xx_led_hw_is_supported,
+ .led_hw_control_get = vsc85xx_led_hw_control_get,
+ .led_hw_control_set = vsc85xx_led_hw_control_set,
}
};
diff --git a/drivers/net/phy/mscc/mscc_ptp.c b/drivers/net/phy/mscc/mscc_ptp.c
index d692df7d975c..4865eac74b0e 100644
--- a/drivers/net/phy/mscc/mscc_ptp.c
+++ b/drivers/net/phy/mscc/mscc_ptp.c
@@ -1051,9 +1051,21 @@ static void vsc85xx_ts_reset_fifo(struct phy_device *phydev)
val);
}
-static int vsc85xx_hwtstamp(struct mii_timestamper *mii_ts,
- struct kernel_hwtstamp_config *cfg,
- struct netlink_ext_ack *extack)
+static int vsc85xx_hwtstamp_get(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg)
+{
+ struct vsc8531_private *vsc8531 =
+ container_of(mii_ts, struct vsc8531_private, mii_ts);
+
+ cfg->tx_type = vsc8531->ptp->tx_type;
+ cfg->rx_filter = vsc8531->ptp->rx_filter;
+
+ return 0;
+}
+
+static int vsc85xx_hwtstamp_set(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
struct vsc8531_private *vsc8531 =
container_of(mii_ts, struct vsc8531_private, mii_ts);
@@ -1611,7 +1623,8 @@ int vsc8584_ptp_probe(struct phy_device *phydev)
vsc8531->mii_ts.rxtstamp = vsc85xx_rxtstamp;
vsc8531->mii_ts.txtstamp = vsc85xx_txtstamp;
- vsc8531->mii_ts.hwtstamp = vsc85xx_hwtstamp;
+ vsc8531->mii_ts.hwtstamp_set = vsc85xx_hwtstamp_set;
+ vsc8531->mii_ts.hwtstamp_get = vsc85xx_hwtstamp_get;
vsc8531->mii_ts.ts_info = vsc85xx_ts_info;
phydev->mii_ts = &vsc8531->mii_ts;
diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
index 0c8dc16ee7bd..8e2fd6b942b6 100644
--- a/drivers/net/phy/mxl-gpy.c
+++ b/drivers/net/phy/mxl-gpy.c
@@ -30,6 +30,9 @@
#define PHY_ID_GPY241B 0x67C9DE40
#define PHY_ID_GPY241BM 0x67C9DE80
#define PHY_ID_GPY245B 0x67C9DEC0
+#define PHY_ID_MXL86211C 0xC1335400
+#define PHY_ID_MXL86252 0xC1335520
+#define PHY_ID_MXL86282 0xC1335500
#define PHY_CTL1 0x13
#define PHY_CTL1_MDICD BIT(3)
@@ -199,6 +202,29 @@ static int gpy_hwmon_read(struct device *dev,
return 0;
}
+static int mxl862x2_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *value)
+{
+ struct phy_device *phydev = dev_get_drvdata(dev);
+ long tmp;
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_TEMP_STA);
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return -ENODATA;
+
+ tmp = (s16)ret;
+ tmp *= 78125;
+ tmp /= 10000;
+
+ *value = tmp;
+
+ return 0;
+}
+
static umode_t gpy_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
@@ -216,19 +242,35 @@ static const struct hwmon_ops gpy_hwmon_hwmon_ops = {
.read = gpy_hwmon_read,
};
+static const struct hwmon_ops mxl862x2_hwmon_hwmon_ops = {
+ .is_visible = gpy_hwmon_is_visible,
+ .read = mxl862x2_hwmon_read,
+};
+
static const struct hwmon_chip_info gpy_hwmon_chip_info = {
.ops = &gpy_hwmon_hwmon_ops,
.info = gpy_hwmon_info,
};
+static const struct hwmon_chip_info mxl862x2_hwmon_chip_info = {
+ .ops = &mxl862x2_hwmon_hwmon_ops,
+ .info = gpy_hwmon_info,
+};
+
static int gpy_hwmon_register(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
+ const struct hwmon_chip_info *info;
struct device *hwmon_dev;
+ if (phy_id_compare_model(phydev->phy_id, PHY_ID_MXL86252) ||
+ phy_id_compare_model(phydev->phy_id, PHY_ID_MXL86282))
+ info = &mxl862x2_hwmon_chip_info;
+ else
+ info = &gpy_hwmon_chip_info;
+
hwmon_dev = devm_hwmon_device_register_with_info(dev, NULL, phydev,
- &gpy_hwmon_chip_info,
- NULL);
+ info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
@@ -540,7 +582,7 @@ static int gpy_update_interface(struct phy_device *phydev)
/* Interface mode is fixed for USXGMII and integrated PHY */
if (phydev->interface == PHY_INTERFACE_MODE_USXGMII ||
phydev->interface == PHY_INTERFACE_MODE_INTERNAL)
- return -EINVAL;
+ return 0;
/* Automatically switch SERDES interface between SGMII and 2500-BaseX
* according to speed. Disable ANEG in 2500-BaseX mode.
@@ -578,13 +620,7 @@ static int gpy_update_interface(struct phy_device *phydev)
break;
}
- if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) {
- ret = genphy_read_master_slave(phydev);
- if (ret < 0)
- return ret;
- }
-
- return gpy_update_mdix(phydev);
+ return 0;
}
static int gpy_read_status(struct phy_device *phydev)
@@ -639,6 +675,16 @@ static int gpy_read_status(struct phy_device *phydev)
ret = gpy_update_interface(phydev);
if (ret < 0)
return ret;
+
+ if (phydev->speed == SPEED_2500 || phydev->speed == SPEED_1000) {
+ ret = genphy_read_master_slave(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = gpy_update_mdix(phydev);
+ if (ret < 0)
+ return ret;
}
return 0;
@@ -1268,6 +1314,72 @@ static struct phy_driver gpy_drivers[] = {
.get_wol = gpy_get_wol,
.set_loopback = gpy_loopback,
},
+ {
+ PHY_ID_MATCH_MODEL(PHY_ID_MXL86211C),
+ .name = "Maxlinear Ethernet MxL86211C",
+ .get_features = genphy_c45_pma_read_abilities,
+ .config_init = gpy_config_init,
+ .probe = gpy_probe,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .config_aneg = gpy_config_aneg,
+ .aneg_done = genphy_c45_aneg_done,
+ .read_status = gpy_read_status,
+ .config_intr = gpy_config_intr,
+ .handle_interrupt = gpy_handle_interrupt,
+ .set_wol = gpy_set_wol,
+ .get_wol = gpy_get_wol,
+ .set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
+ },
+ {
+ PHY_ID_MATCH_MODEL(PHY_ID_MXL86252),
+ .name = "MaxLinear Ethernet MxL86252",
+ .get_features = genphy_c45_pma_read_abilities,
+ .config_init = gpy_config_init,
+ .probe = gpy_probe,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .config_aneg = gpy_config_aneg,
+ .aneg_done = genphy_c45_aneg_done,
+ .read_status = gpy_read_status,
+ .config_intr = gpy_config_intr,
+ .handle_interrupt = gpy_handle_interrupt,
+ .set_wol = gpy_set_wol,
+ .get_wol = gpy_get_wol,
+ .set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
+ },
+ {
+ PHY_ID_MATCH_MODEL(PHY_ID_MXL86282),
+ .name = "MaxLinear Ethernet MxL86282",
+ .get_features = genphy_c45_pma_read_abilities,
+ .config_init = gpy_config_init,
+ .probe = gpy_probe,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .config_aneg = gpy_config_aneg,
+ .aneg_done = genphy_c45_aneg_done,
+ .read_status = gpy_read_status,
+ .config_intr = gpy_config_intr,
+ .handle_interrupt = gpy_handle_interrupt,
+ .set_wol = gpy_set_wol,
+ .get_wol = gpy_get_wol,
+ .set_loopback = gpy_loopback,
+ .led_brightness_set = gpy_led_brightness_set,
+ .led_hw_is_supported = gpy_led_hw_is_supported,
+ .led_hw_control_get = gpy_led_hw_control_get,
+ .led_hw_control_set = gpy_led_hw_control_set,
+ .led_polarity_set = gpy_led_polarity_set,
+ },
};
module_phy_driver(gpy_drivers);
@@ -1284,6 +1396,9 @@ static const struct mdio_device_id __maybe_unused gpy_tbl[] = {
{PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
{PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
{PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
+ {PHY_ID_MATCH_MODEL(PHY_ID_MXL86211C)},
+ {PHY_ID_MATCH_MODEL(PHY_ID_MXL86252)},
+ {PHY_ID_MATCH_MODEL(PHY_ID_MXL86282)},
{ }
};
MODULE_DEVICE_TABLE(mdio, gpy_tbl);
diff --git a/drivers/net/phy/nxp-c45-tja11xx.c b/drivers/net/phy/nxp-c45-tja11xx.c
index 87adb6508017..f526528d2e32 100644
--- a/drivers/net/phy/nxp-c45-tja11xx.c
+++ b/drivers/net/phy/nxp-c45-tja11xx.c
@@ -1012,9 +1012,22 @@ static bool nxp_c45_rxtstamp(struct mii_timestamper *mii_ts,
return true;
}
-static int nxp_c45_hwtstamp(struct mii_timestamper *mii_ts,
- struct kernel_hwtstamp_config *cfg,
- struct netlink_ext_ack *extack)
+static int nxp_c45_hwtstamp_get(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg)
+{
+ struct nxp_c45_phy *priv = container_of(mii_ts, struct nxp_c45_phy,
+ mii_ts);
+
+ cfg->tx_type = priv->hwts_tx;
+ cfg->rx_filter = priv->hwts_rx ? HWTSTAMP_FILTER_PTP_V2_L2_EVENT
+ : HWTSTAMP_FILTER_NONE;
+
+ return 0;
+}
+
+static int nxp_c45_hwtstamp_set(struct mii_timestamper *mii_ts,
+ struct kernel_hwtstamp_config *cfg,
+ struct netlink_ext_ack *extack)
{
struct nxp_c45_phy *priv = container_of(mii_ts, struct nxp_c45_phy,
mii_ts);
@@ -1749,7 +1762,8 @@ static int nxp_c45_probe(struct phy_device *phydev)
IS_ENABLED(CONFIG_NETWORK_PHY_TIMESTAMPING)) {
priv->mii_ts.rxtstamp = nxp_c45_rxtstamp;
priv->mii_ts.txtstamp = nxp_c45_txtstamp;
- priv->mii_ts.hwtstamp = nxp_c45_hwtstamp;
+ priv->mii_ts.hwtstamp_set = nxp_c45_hwtstamp_set;
+ priv->mii_ts.hwtstamp_get = nxp_c45_hwtstamp_get;
priv->mii_ts.ts_info = nxp_c45_ts_info;
phydev->mii_ts = &priv->mii_ts;
ret = nxp_c45_init_ptp_clock(priv);
diff --git a/drivers/net/phy/phy-c45.c b/drivers/net/phy/phy-c45.c
index 61670be0f095..d48aa7231b37 100644
--- a/drivers/net/phy/phy-c45.c
+++ b/drivers/net/phy/phy-c45.c
@@ -7,6 +7,7 @@
#include <linux/mdio.h>
#include <linux/mii.h>
#include <linux/phy.h>
+#include <linux/ethtool_netlink.h>
#include "mdio-open-alliance.h"
#include "phylib-internal.h"
@@ -147,12 +148,12 @@ int genphy_c45_pma_setup_forced(struct phy_device *phydev)
ctrl2 |= MDIO_PMA_CTRL2_1000BT;
break;
case SPEED_2500:
- ctrl1 |= MDIO_CTRL1_SPEED2_5G;
+ ctrl1 |= MDIO_PMA_CTRL1_SPEED2_5G;
/* Assume 2.5Gbase-T */
ctrl2 |= MDIO_PMA_CTRL2_2_5GBT;
break;
case SPEED_5000:
- ctrl1 |= MDIO_CTRL1_SPEED5G;
+ ctrl1 |= MDIO_PMA_CTRL1_SPEED5G;
/* Assume 5Gbase-T */
ctrl2 |= MDIO_PMA_CTRL2_5GBT;
break;
@@ -485,8 +486,8 @@ static int genphy_c45_baset1_read_lpa(struct phy_device *phydev)
mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, 0);
mii_t1_adv_m_mod_linkmode_t(phydev->lp_advertising, 0);
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
return 0;
}
@@ -498,8 +499,8 @@ static int genphy_c45_baset1_read_lpa(struct phy_device *phydev)
return val;
mii_t1_adv_l_mod_linkmode_t(phydev->lp_advertising, val);
- phydev->pause = val & MDIO_AN_T1_ADV_L_PAUSE_CAP ? 1 : 0;
- phydev->asym_pause = val & MDIO_AN_T1_ADV_L_PAUSE_ASYM ? 1 : 0;
+ phydev->pause = val & MDIO_AN_T1_ADV_L_PAUSE_CAP;
+ phydev->asym_pause = val & MDIO_AN_T1_ADV_L_PAUSE_ASYM;
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_T1_LP_M);
if (val < 0)
@@ -536,8 +537,8 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
phydev->lp_advertising);
mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, 0);
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
return 0;
}
@@ -551,8 +552,8 @@ int genphy_c45_read_lpa(struct phy_device *phydev)
return val;
mii_adv_mod_linkmode_adv_t(phydev->lp_advertising, val);
- phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
- phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
+ phydev->pause = val & LPA_PAUSE_CAP;
+ phydev->asym_pause = val & LPA_PAUSE_ASYM;
/* Read the link partner's 10G advertisement */
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
@@ -617,10 +618,10 @@ int genphy_c45_read_pma(struct phy_device *phydev)
case MDIO_PMA_CTRL1_SPEED1000:
phydev->speed = SPEED_1000;
break;
- case MDIO_CTRL1_SPEED2_5G:
+ case MDIO_PMA_CTRL1_SPEED2_5G:
phydev->speed = SPEED_2500;
break;
- case MDIO_CTRL1_SPEED5G:
+ case MDIO_PMA_CTRL1_SPEED5G:
phydev->speed = SPEED_5000;
break;
case MDIO_CTRL1_SPEED10G:
@@ -1171,8 +1172,8 @@ int genphy_c45_read_status(struct phy_device *phydev)
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
if (phydev->autoneg == AUTONEG_ENABLE) {
ret = genphy_c45_read_lpa(phydev);
@@ -1573,3 +1574,261 @@ int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
return ret;
}
EXPORT_SYMBOL(genphy_c45_ethtool_set_eee);
+
+/**
+ * oatc14_cable_test_get_result_code - Convert hardware cable test status to
+ * ethtool result code.
+ * @status: The hardware-reported cable test status
+ *
+ * This helper function maps the OATC14 HDD cable test status to the
+ * corresponding ethtool cable test result code. It provides a translation
+ * between the device-specific status values and the standardized ethtool
+ * result codes.
+ *
+ * Return:
+ * * ETHTOOL_A_CABLE_RESULT_CODE_OK - Cable is OK
+ * * ETHTOOL_A_CABLE_RESULT_CODE_OPEN - Open circuit detected
+ * * ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT - Short circuit detected
+ * * ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC - Status not detectable or invalid
+ */
+static int oatc14_cable_test_get_result_code(enum oatc14_hdd_status status)
+{
+ switch (status) {
+ case OATC14_HDD_STATUS_CABLE_OK:
+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+ case OATC14_HDD_STATUS_OPEN:
+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+ case OATC14_HDD_STATUS_SHORT:
+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+ case OATC14_HDD_STATUS_NOT_DETECTABLE:
+ default:
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+ }
+}
+
+/**
+ * genphy_c45_oatc14_cable_test_get_status - Get status of OATC14 10Base-T1S
+ * PHY cable test.
+ * @phydev: pointer to the PHY device structure
+ * @finished: pointer to a boolean set true if the test is complete
+ *
+ * Retrieves the current status of the OATC14 10Base-T1S PHY cable test.
+ * This function reads the OATC14 HDD register to determine whether the test
+ * results are valid and whether the test has finished.
+ *
+ * If the test is complete, the function reports the cable test result via
+ * the ethtool cable test interface using ethnl_cable_test_result(), and then
+ * clears the test control bit in the PHY register to reset the test state.
+ *
+ * Return: 0 on success, or a negative error code on failure (e.g. register
+ * read/write error).
+ */
+int genphy_c45_oatc14_cable_test_get_status(struct phy_device *phydev,
+ bool *finished)
+{
+ int ret;
+ u8 sts;
+
+ *finished = false;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & OATC14_HDD_VALID))
+ return 0;
+
+ *finished = true;
+
+ sts = FIELD_GET(OATC14_HDD_SHORT_OPEN_STATUS, ret);
+
+ ret = ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
+ oatc14_cable_test_get_result_code(sts));
+ if (ret)
+ return ret;
+
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
+ MDIO_OATC14_HDD, OATC14_HDD_CONTROL);
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_cable_test_get_status);
+
+/**
+ * genphy_c45_oatc14_cable_test_start - Start a cable test on an OATC14
+ * 10Base-T1S PHY.
+ * @phydev: Pointer to the PHY device structure
+ *
+ * This function initiates a cable diagnostic test on a Clause 45 OATC14
+ * 10Base-T1S capable PHY device. It first reads the PHY’s advanced diagnostic
+ * capability register to check if High Definition Diagnostics (HDD) mode is
+ * supported. If the PHY does not report HDD capability, cable testing is not
+ * supported and the function returns -EOPNOTSUPP.
+ *
+ * For PHYs that support HDD, the function sets the appropriate control bits in
+ * the OATC14_HDD register to enable and start the cable diagnostic test.
+ *
+ * Return:
+ * * 0 on success
+ * * -EOPNOTSUPP if the PHY does not support HDD capability
+ * * A negative error code on I/O or register access failures
+ */
+int genphy_c45_oatc14_cable_test_start(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_ADFCAP);
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & OATC14_ADFCAP_HDD_CAPABILITY))
+ return -EOPNOTSUPP;
+
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD,
+ OATC14_HDD_CONTROL);
+ if (ret)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD);
+ if (ret < 0)
+ return ret;
+
+ return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_HDD,
+ OATC14_HDD_START_CONTROL);
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_cable_test_start);
+
+/**
+ * oatc14_update_sqi_capability - Read and update OATC14 10Base-T1S PHY SQI/SQI+
+ * capability
+ * @phydev: Pointer to the PHY device structure
+ *
+ * This helper reads the OATC14 ADFCAP capability register to determine whether
+ * the PHY supports SQI or SQI+ reporting.
+ *
+ * SQI+ capability is detected first. The SQI+ field indicates the number of
+ * valid MSBs (3–8), corresponding to 8–256 SQI+ levels. When present, the
+ * function stores the number of SQI+ bits and computes the maximum SQI+ value
+ * as (2^bits - 1).
+ *
+ * If SQI+ is not supported, the function checks for basic SQI capability,
+ * which provides 0–7 SQI levels.
+ *
+ * On success, the capability information is stored in
+ * @phydev->oatc14_sqi_capability and marked as updated.
+ *
+ * Return:
+ * * 0 - capability successfully read and stored
+ * * -EOPNOTSUPP - SQI/SQI+ not supported by this PHY
+ * * Negative errno on read failure
+ */
+static int oatc14_update_sqi_capability(struct phy_device *phydev)
+{
+ u8 bits;
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_ADFCAP);
+ if (ret < 0)
+ return ret;
+
+ /* Check for SQI+ capability
+ * 0 - SQI+ is not supported
+ * (3-8) bits for (8-256) SQI+ levels supported
+ */
+ bits = FIELD_GET(OATC14_ADFCAP_SQIPLUS_CAPABILITY, ret);
+ if (bits) {
+ phydev->oatc14_sqi_capability.sqiplus_bits = bits;
+ /* Max sqi+ level supported: (2 ^ bits) - 1 */
+ phydev->oatc14_sqi_capability.sqi_max = BIT(bits) - 1;
+ goto update_done;
+ }
+
+ /* Check for SQI capability
+ * 0 - SQI is not supported
+ * 1 - SQI is supported (0-7 levels)
+ */
+ if (ret & OATC14_ADFCAP_SQI_CAPABILITY) {
+ phydev->oatc14_sqi_capability.sqi_max = OATC14_SQI_MAX_LEVEL;
+ goto update_done;
+ }
+
+ return -EOPNOTSUPP;
+
+update_done:
+ phydev->oatc14_sqi_capability.updated = true;
+ return 0;
+}
+
+/**
+ * genphy_c45_oatc14_get_sqi_max - Get maximum supported SQI or SQI+ level of
+ * OATC14 10Base-T1S PHY
+ * @phydev: pointer to the PHY device structure
+ *
+ * This function returns the maximum supported Signal Quality Indicator (SQI) or
+ * SQI+ level. The SQI capability is updated on first invocation if it has not
+ * already been updated.
+ *
+ * Return:
+ * * Maximum SQI/SQI+ level supported
+ * * Negative errno on capability read failure
+ */
+int genphy_c45_oatc14_get_sqi_max(struct phy_device *phydev)
+{
+ int ret;
+
+ if (!phydev->oatc14_sqi_capability.updated) {
+ ret = oatc14_update_sqi_capability(phydev);
+ if (ret)
+ return ret;
+ }
+
+ return phydev->oatc14_sqi_capability.sqi_max;
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_get_sqi_max);
+
+/**
+ * genphy_c45_oatc14_get_sqi - Get Signal Quality Indicator (SQI) from an OATC14
+ * 10Base-T1S PHY
+ * @phydev: pointer to the PHY device structure
+ *
+ * This function reads the SQI+ or SQI value from an OATC14-compatible
+ * 10Base-T1S PHY. If SQI+ capability is supported, the function returns the
+ * extended SQI+ value; otherwise, it returns the basic SQI value. The SQI
+ * capability is updated on first invocation if it has not already been updated.
+ *
+ * Return:
+ * * SQI/SQI+ value on success
+ * * Negative errno on read failure
+ */
+int genphy_c45_oatc14_get_sqi(struct phy_device *phydev)
+{
+ u8 shift;
+ int ret;
+
+ if (!phydev->oatc14_sqi_capability.updated) {
+ ret = oatc14_update_sqi_capability(phydev);
+ if (ret)
+ return ret;
+ }
+
+ /* Calculate and return SQI+ value if supported */
+ if (phydev->oatc14_sqi_capability.sqiplus_bits) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ MDIO_OATC14_DCQ_SQIPLUS);
+ if (ret < 0)
+ return ret;
+
+ /* SQI+ uses N MSBs out of 8 bits, left-aligned with padding 1's
+ * Calculate the right-shift needed to isolate the N bits.
+ */
+ shift = 8 - phydev->oatc14_sqi_capability.sqiplus_bits;
+
+ return (ret & OATC14_DCQ_SQIPLUS_VALUE) >> shift;
+ }
+
+ /* Read and return SQI value if SQI+ capability is not supported */
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, MDIO_OATC14_DCQ_SQI);
+ if (ret < 0)
+ return ret;
+
+ return ret & OATC14_DCQ_SQI_VALUE;
+}
+EXPORT_SYMBOL(genphy_c45_oatc14_get_sqi);
diff --git a/drivers/net/phy/phy-caps.h b/drivers/net/phy/phy-caps.h
index b7f0c6a3037a..4951a39f3828 100644
--- a/drivers/net/phy/phy-caps.h
+++ b/drivers/net/phy/phy-caps.h
@@ -29,6 +29,7 @@ enum {
LINK_CAPA_200000FD,
LINK_CAPA_400000FD,
LINK_CAPA_800000FD,
+ LINK_CAPA_1600000FD,
__LINK_CAPA_MAX,
};
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 605ca20ae192..277c034bc32f 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -17,7 +17,7 @@
*/
const char *phy_speed_to_str(int speed)
{
- BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 121,
+ BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 125,
"Enum ethtool_link_mode_bit_indices and phylib are out of sync. "
"If a speed or mode has been added please update phy_speed_to_str "
"and the PHY settings array.\n");
@@ -55,6 +55,8 @@ const char *phy_speed_to_str(int speed)
return "400Gbps";
case SPEED_800000:
return "800Gbps";
+ case SPEED_1600000:
+ return "1600Gbps";
case SPEED_UNKNOWN:
return "Unknown";
default:
@@ -102,6 +104,49 @@ const char *phy_rate_matching_to_str(int rate_matching)
EXPORT_SYMBOL_GPL(phy_rate_matching_to_str);
/**
+ * phy_fix_phy_mode_for_mac_delays - Convenience function for fixing PHY
+ * mode based on whether mac adds internal delay
+ *
+ * @interface: The current interface mode of the port
+ * @mac_txid: True if the mac adds internal tx delay
+ * @mac_rxid: True if the mac adds internal rx delay
+ *
+ * Return: fixed PHY mode, or PHY_INTERFACE_MODE_NA if the interface can
+ * not apply the internal delay
+ */
+phy_interface_t phy_fix_phy_mode_for_mac_delays(phy_interface_t interface,
+ bool mac_txid, bool mac_rxid)
+{
+ if (!phy_interface_mode_is_rgmii(interface))
+ return interface;
+
+ if (mac_txid && mac_rxid) {
+ if (interface == PHY_INTERFACE_MODE_RGMII_ID)
+ return PHY_INTERFACE_MODE_RGMII;
+ return PHY_INTERFACE_MODE_NA;
+ }
+
+ if (mac_txid) {
+ if (interface == PHY_INTERFACE_MODE_RGMII_ID)
+ return PHY_INTERFACE_MODE_RGMII_RXID;
+ if (interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ return PHY_INTERFACE_MODE_RGMII;
+ return PHY_INTERFACE_MODE_NA;
+ }
+
+ if (mac_rxid) {
+ if (interface == PHY_INTERFACE_MODE_RGMII_ID)
+ return PHY_INTERFACE_MODE_RGMII_TXID;
+ if (interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ return PHY_INTERFACE_MODE_RGMII;
+ return PHY_INTERFACE_MODE_NA;
+ }
+
+ return interface;
+}
+EXPORT_SYMBOL_GPL(phy_fix_phy_mode_for_mac_delays);
+
+/**
* phy_interface_num_ports - Return the number of links that can be carried by
* a given MAC-PHY physical link. Returns 0 if this is
* unknown, the number of links else.
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 02da4a203ddd..13dd1691886d 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -405,12 +405,14 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
return 0;
case SIOCSHWTSTAMP:
- if (phydev->mii_ts && phydev->mii_ts->hwtstamp) {
+ if (phydev->mii_ts && phydev->mii_ts->hwtstamp_set) {
if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg)))
return -EFAULT;
hwtstamp_config_to_kernel(&kernel_cfg, &cfg);
- ret = phydev->mii_ts->hwtstamp(phydev->mii_ts, &kernel_cfg, &extack);
+ ret = phydev->mii_ts->hwtstamp_set(phydev->mii_ts,
+ &kernel_cfg,
+ &extack);
if (ret)
return ret;
@@ -476,6 +478,9 @@ int __phy_hwtstamp_get(struct phy_device *phydev,
if (!phydev)
return -ENODEV;
+ if (phydev->mii_ts && phydev->mii_ts->hwtstamp_get)
+ return phydev->mii_ts->hwtstamp_get(phydev->mii_ts, config);
+
return -EOPNOTSUPP;
}
@@ -493,8 +498,9 @@ int __phy_hwtstamp_set(struct phy_device *phydev,
if (!phydev)
return -ENODEV;
- if (phydev->mii_ts && phydev->mii_ts->hwtstamp)
- return phydev->mii_ts->hwtstamp(phydev->mii_ts, config, extack);
+ if (phydev->mii_ts && phydev->mii_ts->hwtstamp_set)
+ return phydev->mii_ts->hwtstamp_set(phydev->mii_ts, config,
+ extack);
return -EOPNOTSUPP;
}
diff --git a/drivers/net/phy/phy_caps.c b/drivers/net/phy/phy_caps.c
index 23c808b59b6f..3a05982b39bf 100644
--- a/drivers/net/phy/phy_caps.c
+++ b/drivers/net/phy/phy_caps.c
@@ -25,6 +25,7 @@ static struct link_capabilities link_caps[__LINK_CAPA_MAX] __ro_after_init = {
{ SPEED_200000, DUPLEX_FULL, {0} }, /* LINK_CAPA_200000FD */
{ SPEED_400000, DUPLEX_FULL, {0} }, /* LINK_CAPA_400000FD */
{ SPEED_800000, DUPLEX_FULL, {0} }, /* LINK_CAPA_800000FD */
+ { SPEED_1600000, DUPLEX_FULL, {0} }, /* LINK_CAPA_1600000FD */
};
static int speed_duplex_to_capa(int speed, unsigned int duplex)
@@ -52,6 +53,7 @@ static int speed_duplex_to_capa(int speed, unsigned int duplex)
case SPEED_200000: return LINK_CAPA_200000FD;
case SPEED_400000: return LINK_CAPA_400000FD;
case SPEED_800000: return LINK_CAPA_800000FD;
+ case SPEED_1600000: return LINK_CAPA_1600000FD;
}
return -EINVAL;
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 7a67c900e79a..81984d4ebb7c 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -251,6 +251,16 @@ static bool phy_drv_wol_enabled(struct phy_device *phydev)
return wol.wolopts != 0;
}
+bool phy_may_wakeup(struct phy_device *phydev)
+{
+ /* If the PHY is using driver-model based wakeup, use that state. */
+ if (phy_can_wakeup(phydev))
+ return device_may_wakeup(&phydev->mdio.dev);
+
+ return phy_drv_wol_enabled(phydev);
+}
+EXPORT_SYMBOL_GPL(phy_may_wakeup);
+
static void phy_link_change(struct phy_device *phydev, bool up)
{
struct net_device *netdev = phydev->attached_dev;
@@ -302,7 +312,7 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev)
/* If the PHY on the mido bus is not attached but has WOL enabled
* we cannot suspend the PHY.
*/
- if (!netdev && phy_drv_wol_enabled(phydev))
+ if (!netdev && phy_may_wakeup(phydev))
return false;
/* PHY not attached? May suspend if the PHY has not already been
@@ -815,8 +825,8 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
dev->speed = SPEED_UNKNOWN;
dev->duplex = DUPLEX_UNKNOWN;
- dev->pause = 0;
- dev->asym_pause = 0;
+ dev->pause = false;
+ dev->asym_pause = false;
dev->link = 0;
dev->port = PORT_TP;
dev->interface = PHY_INTERFACE_MODE_GMII;
@@ -1214,22 +1224,24 @@ int phy_get_c45_ids(struct phy_device *phydev)
EXPORT_SYMBOL(phy_get_c45_ids);
/**
- * phy_find_first - finds the first PHY device on the bus
+ * phy_find_next - finds the next PHY device on the bus
* @bus: the target MII bus
+ * @pos: cursor
+ *
+ * Return: next phy_device on the bus, or NULL
*/
-struct phy_device *phy_find_first(struct mii_bus *bus)
+struct phy_device *phy_find_next(struct mii_bus *bus, struct phy_device *pos)
{
- struct phy_device *phydev;
- int addr;
+ for (int addr = pos ? pos->mdio.addr + 1 : 0;
+ addr < PHY_MAX_ADDR; addr++) {
+ struct phy_device *phydev = mdiobus_get_phy(bus, addr);
- for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
- phydev = mdiobus_get_phy(bus, addr);
if (phydev)
return phydev;
}
return NULL;
}
-EXPORT_SYMBOL(phy_find_first);
+EXPORT_SYMBOL_GPL(phy_find_next);
/**
* phy_prepare_link - prepares the PHY layer to monitor link status
@@ -1909,7 +1921,7 @@ int phy_suspend(struct phy_device *phydev)
if (phydev->suspended || !phydrv)
return 0;
- phydev->wol_enabled = phy_drv_wol_enabled(phydev) ||
+ phydev->wol_enabled = phy_may_wakeup(phydev) ||
(netdev && netdev->ethtool->wol_enabled);
/* If the device has WOL enabled, we cannot suspend the PHY */
if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND))
@@ -2080,8 +2092,8 @@ int genphy_setup_forced(struct phy_device *phydev)
{
u16 ctl;
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
@@ -2488,8 +2500,8 @@ int genphy_read_status(struct phy_device *phydev)
phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
if (phydev->is_gigabit_capable) {
err = genphy_read_master_slave(phydev);
@@ -2542,8 +2554,8 @@ int genphy_c37_read_status(struct phy_device *phydev, bool *changed)
/* Signal link has changed */
*changed = true;
phydev->duplex = DUPLEX_UNKNOWN;
- phydev->pause = 0;
- phydev->asym_pause = 0;
+ phydev->pause = false;
+ phydev->asym_pause = false;
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
lpa = phy_read(phydev, MII_LPA);
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 9d7799ea1c17..43d8380aaefb 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -93,6 +93,9 @@ struct phylink {
u8 sfp_port;
struct eee_config eee_cfg;
+
+ u32 wolopts_mac;
+ u8 wol_sopass[SOPASS_MAX];
};
#define phylink_printk(level, pl, fmt, ...) \
@@ -637,6 +640,9 @@ static int phylink_validate(struct phylink *pl, unsigned long *supported,
static void phylink_fill_fixedlink_supported(unsigned long *supported)
{
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, supported);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, supported);
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, supported);
@@ -2562,6 +2568,23 @@ void phylink_rx_clk_stop_unblock(struct phylink *pl)
}
EXPORT_SYMBOL_GPL(phylink_rx_clk_stop_unblock);
+static bool phylink_mac_supports_wol(struct phylink *pl)
+{
+ return !!pl->mac_ops->mac_wol_set;
+}
+
+static bool phylink_phy_supports_wol(struct phylink *pl,
+ struct phy_device *phydev)
+{
+ return phydev && (pl->config->wol_phy_legacy || phy_can_wakeup(phydev));
+}
+
+static bool phylink_phy_pm_speed_ctrl(struct phylink *pl)
+{
+ return pl->config->wol_phy_speed_ctrl && !pl->wolopts_mac &&
+ pl->phydev && phy_may_wakeup(pl->phydev);
+}
+
/**
* phylink_suspend() - handle a network device suspend event
* @pl: a pointer to a &struct phylink returned from phylink_create()
@@ -2575,11 +2598,17 @@ EXPORT_SYMBOL_GPL(phylink_rx_clk_stop_unblock);
* can also bring down the link between the MAC and PHY.
* - If Wake-on-Lan is active, but being handled by the MAC, the MAC
* still needs to receive packets, so we can not bring the link down.
+ *
+ * Note: when phylink managed Wake-on-Lan is in use, @mac_wol is ignored.
+ * (struct phylink_mac_ops.mac_set_wol populated.)
*/
void phylink_suspend(struct phylink *pl, bool mac_wol)
{
ASSERT_RTNL();
+ if (phylink_mac_supports_wol(pl))
+ mac_wol = !!pl->wolopts_mac;
+
if (mac_wol && (!pl->netdev || pl->netdev->ethtool->wol_enabled)) {
/* Wake-on-Lan enabled, MAC handling */
mutex_lock(&pl->state_mutex);
@@ -2605,6 +2634,9 @@ void phylink_suspend(struct phylink *pl, bool mac_wol)
} else {
phylink_stop(pl);
}
+
+ if (phylink_phy_pm_speed_ctrl(pl))
+ phylink_speed_down(pl, false);
}
EXPORT_SYMBOL_GPL(phylink_suspend);
@@ -2644,6 +2676,9 @@ void phylink_resume(struct phylink *pl)
{
ASSERT_RTNL();
+ if (phylink_phy_pm_speed_ctrl(pl))
+ phylink_speed_up(pl);
+
if (test_bit(PHYLINK_DISABLE_MAC_WOL, &pl->phylink_disable_state)) {
/* Wake-on-Lan enabled, MAC handling */
@@ -2689,8 +2724,24 @@ void phylink_ethtool_get_wol(struct phylink *pl, struct ethtool_wolinfo *wol)
wol->supported = 0;
wol->wolopts = 0;
- if (pl->phydev)
- phy_ethtool_get_wol(pl->phydev, wol);
+ if (phylink_mac_supports_wol(pl)) {
+ if (phylink_phy_supports_wol(pl, pl->phydev))
+ phy_ethtool_get_wol(pl->phydev, wol);
+
+ /* Where the MAC augments the WoL support, merge its support and
+ * current configuration.
+ */
+ if (~wol->wolopts & pl->wolopts_mac & WAKE_MAGICSECURE)
+ memcpy(wol->sopass, pl->wol_sopass,
+ sizeof(wol->sopass));
+
+ wol->supported |= pl->config->wol_mac_support;
+ wol->wolopts |= pl->wolopts_mac;
+ } else {
+ /* Legacy */
+ if (pl->phydev)
+ phy_ethtool_get_wol(pl->phydev, wol);
+ }
}
EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol);
@@ -2707,12 +2758,48 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_get_wol);
*/
int phylink_ethtool_set_wol(struct phylink *pl, struct ethtool_wolinfo *wol)
{
+ struct ethtool_wolinfo w = { .cmd = ETHTOOL_GWOL };
int ret = -EOPNOTSUPP;
+ bool changed;
+ u32 wolopts;
ASSERT_RTNL();
- if (pl->phydev)
- ret = phy_ethtool_set_wol(pl->phydev, wol);
+ if (phylink_mac_supports_wol(pl)) {
+ wolopts = wol->wolopts;
+
+ if (phylink_phy_supports_wol(pl, pl->phydev)) {
+ ret = phy_ethtool_set_wol(pl->phydev, wol);
+ if (ret != 0 && ret != -EOPNOTSUPP)
+ return ret;
+
+ phy_ethtool_get_wol(pl->phydev, &w);
+
+ /* Any Wake-on-Lan modes which the PHY is handling
+ * should not be passed on to the MAC.
+ */
+ wolopts &= ~w.wolopts;
+ }
+
+ wolopts &= pl->config->wol_mac_support;
+ changed = pl->wolopts_mac != wolopts;
+ if (wolopts & WAKE_MAGICSECURE)
+ changed |= !!memcmp(wol->sopass, pl->wol_sopass,
+ sizeof(wol->sopass));
+ memcpy(pl->wol_sopass, wol->sopass, sizeof(pl->wol_sopass));
+
+ if (changed) {
+ ret = pl->mac_ops->mac_wol_set(pl->config, wolopts,
+ wol->sopass);
+ if (!ret)
+ pl->wolopts_mac = wolopts;
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (pl->phydev)
+ ret = phy_ethtool_set_wol(pl->phydev, wol);
+ }
return ret;
}
diff --git a/drivers/net/phy/qt2025.rs b/drivers/net/phy/qt2025.rs
index 0b9400dcb4c1..aaaead6512a0 100644
--- a/drivers/net/phy/qt2025.rs
+++ b/drivers/net/phy/qt2025.rs
@@ -12,6 +12,7 @@
use kernel::c_str;
use kernel::error::code;
use kernel::firmware::Firmware;
+use kernel::io::poll::read_poll_timeout;
use kernel::net::phy::{
self,
reg::{Mmd, C45},
@@ -19,6 +20,7 @@ use kernel::net::phy::{
};
use kernel::prelude::*;
use kernel::sizes::{SZ_16K, SZ_8K};
+use kernel::time::Delta;
kernel::module_phy_driver! {
drivers: [PhyQT2025],
@@ -93,7 +95,13 @@ impl Driver for PhyQT2025 {
// The micro-controller will start running from SRAM.
dev.write(C45::new(Mmd::PCS, 0xe854), 0x0040)?;
- // TODO: sleep here until the hw becomes ready.
+ read_poll_timeout(
+ || dev.read(C45::new(Mmd::PCS, 0xd7fd)),
+ |val| *val != 0x00 && *val != 0x10,
+ Delta::from_millis(50),
+ Delta::from_secs(3),
+ )?;
+
Ok(())
}
diff --git a/drivers/net/phy/realtek/realtek_main.c b/drivers/net/phy/realtek/realtek_main.c
index a724b21b4fe7..67ecf3d4af2b 100644
--- a/drivers/net/phy/realtek/realtek_main.c
+++ b/drivers/net/phy/realtek/realtek_main.c
@@ -8,6 +8,7 @@
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*/
#include <linux/bitops.h>
+#include <linux/ethtool_netlink.h>
#include <linux/of.h>
#include <linux/phy.h>
#include <linux/pm_wakeirq.h>
@@ -89,6 +90,14 @@
#define RTL8211F_LEDCR_MASK GENMASK(4, 0)
#define RTL8211F_LEDCR_SHIFT 5
+/* RTL8211F(D)(I)-VD-CG CLKOUT configuration is specified via magic values
+ * to undocumented register pages. The names here do not reflect the datasheet.
+ * Unlike other PHY models, CLKOUT configuration does not go through PHYCR2.
+ */
+#define RTL8211FVD_CLKOUT_PAGE 0xd05
+#define RTL8211FVD_CLKOUT_REG 0x11
+#define RTL8211FVD_CLKOUT_EN BIT(8)
+
/* RTL8211F RGMII configuration */
#define RTL8211F_RGMII_PAGE 0xd08
@@ -127,6 +136,32 @@
*/
#define RTL822X_VND2_C22_REG(reg) (0xa400 + 2 * (reg))
+#define RTL8221B_VND2_INER 0xa4d2
+#define RTL8221B_VND2_INER_LINK_STATUS BIT(4)
+
+#define RTL8221B_VND2_INSR 0xa4d4
+
+#define RTL8224_MII_RTCT 0x11
+#define RTL8224_MII_RTCT_ENABLE BIT(0)
+#define RTL8224_MII_RTCT_PAIR_A BIT(4)
+#define RTL8224_MII_RTCT_PAIR_B BIT(5)
+#define RTL8224_MII_RTCT_PAIR_C BIT(6)
+#define RTL8224_MII_RTCT_PAIR_D BIT(7)
+#define RTL8224_MII_RTCT_DONE BIT(15)
+
+#define RTL8224_MII_SRAM_ADDR 0x1b
+#define RTL8224_MII_SRAM_DATA 0x1c
+
+#define RTL8224_SRAM_RTCT_FAULT(pair) (0x8026 + (pair) * 4)
+#define RTL8224_SRAM_RTCT_FAULT_BUSY BIT(0)
+#define RTL8224_SRAM_RTCT_FAULT_OPEN BIT(3)
+#define RTL8224_SRAM_RTCT_FAULT_SAME_SHORT BIT(4)
+#define RTL8224_SRAM_RTCT_FAULT_OK BIT(5)
+#define RTL8224_SRAM_RTCT_FAULT_DONE BIT(6)
+#define RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT BIT(7)
+
+#define RTL8224_SRAM_RTCT_LEN(pair) (0x8028 + (pair) * 4)
+
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
@@ -154,7 +189,7 @@
#define RTL_8211FVD_PHYID 0x001cc878
#define RTL_8221B 0x001cc840
#define RTL_8221B_VB_CG 0x001cc849
-#define RTL_8221B_VN_CG 0x001cc84a
+#define RTL_8221B_VM_CG 0x001cc84a
#define RTL_8251B 0x001cc862
#define RTL_8261C 0x001cc890
@@ -166,9 +201,8 @@ MODULE_AUTHOR("Johnson Leung");
MODULE_LICENSE("GPL");
struct rtl821x_priv {
- u16 phycr1;
- u16 phycr2;
- bool has_phycr2;
+ bool enable_aldps;
+ bool disable_clk_out;
struct clk *clk;
/* rtl8211f */
u16 iner;
@@ -218,8 +252,6 @@ static int rtl821x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
struct rtl821x_priv *priv;
- u32 phy_id = phydev->drv->phy_id;
- int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -230,24 +262,10 @@ static int rtl821x_probe(struct phy_device *phydev)
return dev_err_probe(dev, PTR_ERR(priv->clk),
"failed to get phy clock\n");
- ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1);
- if (ret < 0)
- return ret;
-
- priv->phycr1 = ret & (RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF);
- if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
- priv->phycr1 |= RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF;
-
- priv->has_phycr2 = !(phy_id == RTL_8211FVD_PHYID);
- if (priv->has_phycr2) {
- ret = phy_read_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2);
- if (ret < 0)
- return ret;
-
- priv->phycr2 = ret & RTL8211F_CLKOUT_EN;
- if (of_property_read_bool(dev->of_node, "realtek,clkout-disable"))
- priv->phycr2 &= ~RTL8211F_CLKOUT_EN;
- }
+ priv->enable_aldps = of_property_read_bool(dev->of_node,
+ "realtek,aldps-enable");
+ priv->disable_clk_out = of_property_read_bool(dev->of_node,
+ "realtek,clkout-disable");
phydev->priv = priv;
@@ -560,22 +578,11 @@ static int rtl8211c_config_init(struct phy_device *phydev)
CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
}
-static int rtl8211f_config_init(struct phy_device *phydev)
+static int rtl8211f_config_rgmii_delay(struct phy_device *phydev)
{
- struct rtl821x_priv *priv = phydev->priv;
- struct device *dev = &phydev->mdio.dev;
u16 val_txdly, val_rxdly;
int ret;
- ret = phy_modify_paged_changed(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1,
- RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_XTAL_OFF,
- priv->phycr1);
- if (ret < 0) {
- dev_err(dev, "aldps mode configuration failed: %pe\n",
- ERR_PTR(ret));
- return ret;
- }
-
switch (phydev->interface) {
case PHY_INTERFACE_MODE_RGMII:
val_txdly = 0;
@@ -605,53 +612,118 @@ static int rtl8211f_config_init(struct phy_device *phydev)
RTL8211F_TXCR, RTL8211F_TX_DELAY,
val_txdly);
if (ret < 0) {
- dev_err(dev, "Failed to update the TX delay register\n");
+ phydev_err(phydev, "Failed to update the TX delay register: %pe\n",
+ ERR_PTR(ret));
return ret;
} else if (ret) {
- dev_dbg(dev,
- "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n",
- str_enable_disable(val_txdly));
+ phydev_dbg(phydev,
+ "%s 2ns TX delay (and changing the value from pin-strapping RXD1 or the bootloader)\n",
+ str_enable_disable(val_txdly));
} else {
- dev_dbg(dev,
- "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
- str_enabled_disabled(val_txdly));
+ phydev_dbg(phydev,
+ "2ns TX delay was already %s (by pin-strapping RXD1 or bootloader configuration)\n",
+ str_enabled_disabled(val_txdly));
}
ret = phy_modify_paged_changed(phydev, RTL8211F_RGMII_PAGE,
RTL8211F_RXCR, RTL8211F_RX_DELAY,
val_rxdly);
if (ret < 0) {
- dev_err(dev, "Failed to update the RX delay register\n");
+ phydev_err(phydev, "Failed to update the RX delay register: %pe\n",
+ ERR_PTR(ret));
return ret;
} else if (ret) {
- dev_dbg(dev,
- "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n",
- str_enable_disable(val_rxdly));
+ phydev_dbg(phydev,
+ "%s 2ns RX delay (and changing the value from pin-strapping RXD0 or the bootloader)\n",
+ str_enable_disable(val_rxdly));
} else {
- dev_dbg(dev,
- "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n",
- str_enabled_disabled(val_rxdly));
+ phydev_dbg(phydev,
+ "2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\n",
+ str_enabled_disabled(val_rxdly));
}
- if (!priv->has_phycr2)
+ return 0;
+}
+
+static int rtl8211f_config_clk_out(struct phy_device *phydev)
+{
+ struct rtl821x_priv *priv = phydev->priv;
+ int ret;
+
+ /* The value is preserved if the device tree property is absent */
+ if (!priv->disable_clk_out)
+ return 0;
+
+ if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
+ ret = phy_modify_paged(phydev, RTL8211FVD_CLKOUT_PAGE,
+ RTL8211FVD_CLKOUT_REG,
+ RTL8211FVD_CLKOUT_EN, 0);
+ else
+ ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
+ RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN, 0);
+ if (ret)
+ return ret;
+
+ return genphy_soft_reset(phydev);
+}
+
+/* Advance Link Down Power Saving (ALDPS) mode changes crystal/clock behaviour,
+ * which causes the RXC clock signal to stop for tens to hundreds of
+ * milliseconds.
+ *
+ * Some MACs need the RXC clock to support their internal RX logic, so ALDPS is
+ * only enabled based on an opt-in device tree property.
+ */
+static int rtl8211f_config_aldps(struct phy_device *phydev)
+{
+ struct rtl821x_priv *priv = phydev->priv;
+ u16 mask = RTL8211F_ALDPS_PLL_OFF |
+ RTL8211F_ALDPS_ENABLE |
+ RTL8211F_ALDPS_XTAL_OFF;
+
+ /* The value is preserved if the device tree property is absent */
+ if (!priv->enable_aldps)
+ return 0;
+
+ return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR1,
+ mask, mask);
+}
+
+static int rtl8211f_config_phy_eee(struct phy_device *phydev)
+{
+ /* RTL8211FVD has no PHYCR2 register */
+ if (phydev->drv->phy_id == RTL_8211FVD_PHYID)
return 0;
/* Disable PHY-mode EEE so LPI is passed to the MAC */
- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2,
- RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0);
+ return phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE, RTL8211F_PHYCR2,
+ RTL8211F_PHYCR2_PHY_EEE_ENABLE, 0);
+}
+
+static int rtl8211f_config_init(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ int ret;
+
+ ret = rtl8211f_config_aldps(phydev);
+ if (ret) {
+ dev_err(dev, "aldps mode configuration failed: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ ret = rtl8211f_config_rgmii_delay(phydev);
if (ret)
return ret;
- ret = phy_modify_paged(phydev, RTL8211F_PHYCR_PAGE,
- RTL8211F_PHYCR2, RTL8211F_CLKOUT_EN,
- priv->phycr2);
- if (ret < 0) {
+ ret = rtl8211f_config_clk_out(phydev);
+ if (ret) {
dev_err(dev, "clkout configuration failed: %pe\n",
ERR_PTR(ret));
return ret;
}
- return genphy_soft_reset(phydev);
+ return rtl8211f_config_phy_eee(phydev);
}
static int rtl821x_suspend(struct phy_device *phydev)
@@ -1453,6 +1525,168 @@ static int rtl822xb_c45_read_status(struct phy_device *phydev)
return 0;
}
+static int rtl8224_cable_test_start(struct phy_device *phydev)
+{
+ u32 val;
+ int ret;
+
+ /* disable auto-negotiation and force 1000/Full */
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(MII_BMCR),
+ BMCR_ANENABLE | BMCR_SPEED100 | BMCR_SPEED10,
+ BMCR_SPEED1000 | BMCR_FULLDPLX);
+ if (ret)
+ return ret;
+
+ mdelay(500);
+
+ /* trigger cable test */
+ val = RTL8224_MII_RTCT_ENABLE;
+ val |= RTL8224_MII_RTCT_PAIR_A;
+ val |= RTL8224_MII_RTCT_PAIR_B;
+ val |= RTL8224_MII_RTCT_PAIR_C;
+ val |= RTL8224_MII_RTCT_PAIR_D;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT),
+ RTL8224_MII_RTCT_DONE, val);
+}
+
+static int rtl8224_sram_read(struct phy_device *phydev, u32 reg)
+{
+ int ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_ADDR),
+ reg);
+ if (ret)
+ return ret;
+
+ return phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(RTL8224_MII_SRAM_DATA));
+}
+
+static int rtl8224_pair_len_get(struct phy_device *phydev, u32 pair)
+{
+ int cable_len;
+ u32 reg_len;
+ int ret;
+ u32 cm;
+
+ reg_len = RTL8224_SRAM_RTCT_LEN(pair);
+
+ ret = rtl8224_sram_read(phydev, reg_len);
+ if (ret < 0)
+ return ret;
+
+ cable_len = ret & 0xff00;
+
+ ret = rtl8224_sram_read(phydev, reg_len + 1);
+ if (ret < 0)
+ return ret;
+
+ cable_len |= (ret & 0xff00) >> 8;
+
+ cable_len -= 620;
+ cable_len = max(cable_len, 0);
+
+ cm = cable_len * 100 / 78;
+
+ return cm;
+}
+
+static int rtl8224_cable_test_result_trans(u32 result)
+{
+ if (!(result & RTL8224_SRAM_RTCT_FAULT_DONE))
+ return -EBUSY;
+
+ if (result & RTL8224_SRAM_RTCT_FAULT_OK)
+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
+
+ if (result & RTL8224_SRAM_RTCT_FAULT_OPEN)
+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
+
+ if (result & RTL8224_SRAM_RTCT_FAULT_SAME_SHORT)
+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
+
+ if (result & RTL8224_SRAM_RTCT_FAULT_BUSY)
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+
+ if (result & RTL8224_SRAM_RTCT_FAULT_CROSS_SHORT)
+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
+
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
+}
+
+static int rtl8224_cable_test_report_pair(struct phy_device *phydev, unsigned int pair)
+{
+ int fault_rslt;
+ int ret;
+
+ ret = rtl8224_sram_read(phydev, RTL8224_SRAM_RTCT_FAULT(pair));
+ if (ret < 0)
+ return ret;
+
+ fault_rslt = rtl8224_cable_test_result_trans(ret);
+ if (fault_rslt < 0)
+ return 0;
+
+ ret = ethnl_cable_test_result(phydev, pair, fault_rslt);
+ if (ret < 0)
+ return ret;
+
+ switch (fault_rslt) {
+ case ETHTOOL_A_CABLE_RESULT_CODE_OPEN:
+ case ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT:
+ case ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT:
+ ret = rtl8224_pair_len_get(phydev, pair);
+ if (ret < 0)
+ return ret;
+
+ return ethnl_cable_test_fault_length(phydev, pair, ret);
+ default:
+ return 0;
+ }
+}
+
+static int rtl8224_cable_test_report(struct phy_device *phydev, bool *finished)
+{
+ unsigned int pair;
+ int ret;
+
+ for (pair = ETHTOOL_A_CABLE_PAIR_A; pair <= ETHTOOL_A_CABLE_PAIR_D; pair++) {
+ ret = rtl8224_cable_test_report_pair(phydev, pair);
+ if (ret == -EBUSY) {
+ *finished = false;
+ return 0;
+ }
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished)
+{
+ int ret;
+
+ *finished = false;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
+ RTL822X_VND2_C22_REG(RTL8224_MII_RTCT));
+ if (ret < 0)
+ return ret;
+
+ if (!(ret & RTL8224_MII_RTCT_DONE))
+ return 0;
+
+ *finished = true;
+
+ return rtl8224_cable_test_report(phydev, finished);
+}
+
static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
{
int val;
@@ -1523,16 +1757,16 @@ static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev,
return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
}
-static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev,
+static int rtl8221b_vm_cg_c22_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
+ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, false);
}
-static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev,
+static int rtl8221b_vm_cg_c45_match_phy_device(struct phy_device *phydev,
const struct phy_driver *phydrv)
{
- return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
+ return rtlgen_is_c45_match(phydev, RTL_8221B_VM_CG, true);
}
static int rtl_internal_nbaset_match_phy_device(struct phy_device *phydev,
@@ -1696,6 +1930,53 @@ static irqreturn_t rtl9000a_handle_interrupt(struct phy_device *phydev)
return IRQ_HANDLED;
}
+static int rtl8221b_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
+
+ return (err < 0) ? err : 0;
+}
+
+static int rtl8221b_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ err = rtl8221b_ack_interrupt(phydev);
+ if (err)
+ return err;
+
+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
+ RTL8221B_VND2_INER_LINK_STATUS);
+ } else {
+ err = phy_write_mmd(phydev, MDIO_MMD_VEND2,
+ RTL8221B_VND2_INER, 0);
+ if (err)
+ return err;
+
+ err = rtl8221b_ack_interrupt(phydev);
+ }
+
+ return err;
+}
+
+static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = rtl8221b_ack_interrupt(phydev);
+ if (err) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ phy_trigger_machine(phydev);
+
+ return IRQ_HANDLED;
+}
+
static struct phy_driver realtek_drvs[] = {
{
PHY_ID_MATCH_EXACT(0x00008201),
@@ -1870,6 +2151,8 @@ static struct phy_driver realtek_drvs[] = {
}, {
.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
+ .config_intr = rtl8221b_config_intr,
+ .handle_interrupt = rtl8221b_handle_interrupt,
.probe = rtl822x_probe,
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
@@ -1879,7 +2162,7 @@ static struct phy_driver realtek_drvs[] = {
.suspend = genphy_c45_pma_suspend,
.resume = rtlgen_c45_resume,
}, {
- .match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
+ .match_phy_device = rtl8221b_vm_cg_c22_match_phy_device,
.name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
.probe = rtl822x_probe,
.get_features = rtl822x_get_features,
@@ -1892,8 +2175,10 @@ static struct phy_driver realtek_drvs[] = {
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
- .match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
- .name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
+ .match_phy_device = rtl8221b_vm_cg_c45_match_phy_device,
+ .name = "RTL8221B-VM-CG 2.5Gbps PHY (C45)",
+ .config_intr = rtl8221b_config_intr,
+ .handle_interrupt = rtl8221b_handle_interrupt,
.probe = rtl822x_probe,
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
@@ -1930,11 +2215,14 @@ static struct phy_driver realtek_drvs[] = {
}, {
PHY_ID_MATCH_EXACT(0x001ccad0),
.name = "RTL8224 2.5Gbps PHY",
+ .flags = PHY_POLL_CABLE_TEST,
.get_features = rtl822x_c45_get_features,
.config_aneg = rtl822x_c45_config_aneg,
.read_status = rtl822x_c45_read_status,
.suspend = genphy_c45_pma_suspend,
.resume = rtlgen_c45_resume,
+ .cable_test_start = rtl8224_cable_test_start,
+ .cable_test_get_status = rtl8224_cable_test_get_status,
}, {
PHY_ID_MATCH_EXACT(0x001cc961),
.name = "RTL8366RB Gigabit Ethernet",
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index 4ac6afce267b..4275b393a454 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -608,8 +608,8 @@ static int pppoe_release(struct socket *sock)
return 0;
}
-static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len, int flags)
+static int pppoe_connect(struct socket *sock, struct sockaddr_unsized *uservaddr,
+ int sockaddr_len, int flags)
{
struct sock *sk = sock->sk;
struct sockaddr_pppox *sp = (struct sockaddr_pppox *)uservaddr;
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 90737cb71892..b18acd810561 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -382,8 +382,8 @@ drop:
return NET_RX_DROP;
}
-static int pptp_bind(struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len)
+static int pptp_bind(struct socket *sock, struct sockaddr_unsized *uservaddr,
+ int sockaddr_len)
{
struct sock *sk = sock->sk;
struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
@@ -415,8 +415,8 @@ out:
return error;
}
-static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr,
- int sockaddr_len, int flags)
+static int pptp_connect(struct socket *sock, struct sockaddr_unsized *uservaddr,
+ int sockaddr_len, int flags)
{
struct sock *sk = sock->sk;
struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr;
diff --git a/drivers/net/pse-pd/pd692x0.c b/drivers/net/pse-pd/pd692x0.c
index f4e91ba64a66..134435e90073 100644
--- a/drivers/net/pse-pd/pd692x0.c
+++ b/drivers/net/pse-pd/pd692x0.c
@@ -30,6 +30,8 @@
#define PD692X0_FW_MIN_VER 5
#define PD692X0_FW_PATCH_VER 5
+#define PD692X0_USER_BYTE 42
+
enum pd692x0_fw_state {
PD692X0_FW_UNKNOWN,
PD692X0_FW_OK,
@@ -80,11 +82,17 @@ enum {
PD692X0_MSG_GET_PORT_PARAM,
PD692X0_MSG_GET_POWER_BANK,
PD692X0_MSG_SET_POWER_BANK,
+ PD692X0_MSG_SET_USER_BYTE,
/* add new message above here */
PD692X0_MSG_CNT
};
+struct pd692x0_matrix {
+ u8 hw_port_a;
+ u8 hw_port_b;
+};
+
struct pd692x0_priv {
struct i2c_client *client;
struct pse_controller_dev pcdev;
@@ -98,9 +106,12 @@ struct pd692x0_priv {
bool last_cmd_key;
unsigned long last_cmd_key_time;
+ bool cfg_saved;
enum ethtool_c33_pse_admin_state admin_state[PD692X0_MAX_PIS];
struct regulator_dev *manager_reg[PD692X0_MAX_MANAGERS];
int manager_pw_budget[PD692X0_MAX_MANAGERS];
+ int nmanagers;
+ struct pd692x0_matrix *port_matrix;
};
/* Template list of communication messages. The non-null bytes defined here
@@ -186,6 +197,12 @@ static const struct pd692x0_msg pd692x0_msg_template_list[PD692X0_MSG_CNT] = {
.key = PD692X0_KEY_CMD,
.sub = {0x07, 0x0b, 0x57},
},
+ [PD692X0_MSG_SET_USER_BYTE] = {
+ .key = PD692X0_KEY_PRG,
+ .sub = {0x41, PD692X0_USER_BYTE},
+ .data = {0x4e, 0x4e, 0x4e, 0x4e,
+ 0x4e, 0x4e, 0x4e, 0x4e},
+ },
};
static u8 pd692x0_build_msg(struct pd692x0_msg *msg, u8 echo)
@@ -809,11 +826,6 @@ struct pd692x0_manager {
int nports;
};
-struct pd692x0_matrix {
- u8 hw_port_a;
- u8 hw_port_b;
-};
-
static int
pd692x0_of_get_ports_manager(struct pd692x0_priv *priv,
struct pd692x0_manager *manager,
@@ -903,7 +915,8 @@ pd692x0_of_get_managers(struct pd692x0_priv *priv,
}
of_node_put(managers_node);
- return nmanagers;
+ priv->nmanagers = nmanagers;
+ return 0;
out:
for (i = 0; i < nmanagers; i++) {
@@ -963,8 +976,7 @@ pd692x0_register_manager_regulator(struct device *dev, char *reg_name,
static int
pd692x0_register_managers_regulator(struct pd692x0_priv *priv,
- const struct pd692x0_manager *manager,
- int nmanagers)
+ const struct pd692x0_manager *manager)
{
struct device *dev = &priv->client->dev;
size_t reg_name_len;
@@ -975,7 +987,7 @@ pd692x0_register_managers_regulator(struct pd692x0_priv *priv,
*/
reg_name_len = strlen(dev_name(dev)) + 23;
- for (i = 0; i < nmanagers; i++) {
+ for (i = 0; i < priv->nmanagers; i++) {
static const char * const regulators[] = { "vaux5", "vaux3p3" };
struct regulator_dev *rdev;
char *reg_name;
@@ -1008,10 +1020,14 @@ pd692x0_register_managers_regulator(struct pd692x0_priv *priv,
}
static int
-pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id, int pw)
+pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id)
{
struct pd692x0_msg msg, buf;
- int ret, pw_mW = pw / 1000;
+ int ret, pw_mW;
+
+ pw_mW = priv->manager_pw_budget[id] / 1000;
+ if (!pw_mW)
+ return 0;
msg = pd692x0_msg_template_list[PD692X0_MSG_GET_POWER_BANK];
msg.data[0] = id;
@@ -1032,11 +1048,11 @@ pd692x0_conf_manager_power_budget(struct pd692x0_priv *priv, int id, int pw)
}
static int
-pd692x0_configure_managers(struct pd692x0_priv *priv, int nmanagers)
+pd692x0_req_managers_pw_budget(struct pd692x0_priv *priv)
{
int i, ret;
- for (i = 0; i < nmanagers; i++) {
+ for (i = 0; i < priv->nmanagers; i++) {
struct regulator *supply = priv->manager_reg[i]->supply;
int pw_budget;
@@ -1053,7 +1069,18 @@ pd692x0_configure_managers(struct pd692x0_priv *priv, int nmanagers)
return ret;
priv->manager_pw_budget[i] = pw_budget;
- ret = pd692x0_conf_manager_power_budget(priv, i, pw_budget);
+ }
+
+ return 0;
+}
+
+static int
+pd692x0_configure_managers(struct pd692x0_priv *priv)
+{
+ int i, ret;
+
+ for (i = 0; i < priv->nmanagers; i++) {
+ ret = pd692x0_conf_manager_power_budget(priv, i);
if (ret < 0)
return ret;
}
@@ -1101,10 +1128,9 @@ pd692x0_set_port_matrix(const struct pse_pi_pairset *pairset,
static int
pd692x0_set_ports_matrix(struct pd692x0_priv *priv,
- const struct pd692x0_manager *manager,
- int nmanagers,
- struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS])
+ const struct pd692x0_manager *manager)
{
+ struct pd692x0_matrix *port_matrix = priv->port_matrix;
struct pse_controller_dev *pcdev = &priv->pcdev;
int i, ret;
@@ -1117,7 +1143,7 @@ pd692x0_set_ports_matrix(struct pd692x0_priv *priv,
/* Update with values for every PSE PIs */
for (i = 0; i < pcdev->nr_lines; i++) {
ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[0],
- manager, nmanagers,
+ manager, priv->nmanagers,
&port_matrix[i]);
if (ret) {
dev_err(&priv->client->dev,
@@ -1126,7 +1152,7 @@ pd692x0_set_ports_matrix(struct pd692x0_priv *priv,
}
ret = pd692x0_set_port_matrix(&pcdev->pi[i].pairset[1],
- manager, nmanagers,
+ manager, priv->nmanagers,
&port_matrix[i]);
if (ret) {
dev_err(&priv->client->dev,
@@ -1139,9 +1165,9 @@ pd692x0_set_ports_matrix(struct pd692x0_priv *priv,
}
static int
-pd692x0_write_ports_matrix(struct pd692x0_priv *priv,
- const struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS])
+pd692x0_write_ports_matrix(struct pd692x0_priv *priv)
{
+ struct pd692x0_matrix *port_matrix = priv->port_matrix;
struct pd692x0_msg msg, buf;
int ret, i;
@@ -1166,13 +1192,32 @@ pd692x0_write_ports_matrix(struct pd692x0_priv *priv,
return 0;
}
+static int pd692x0_hw_conf_init(struct pd692x0_priv *priv)
+{
+ int ret;
+
+ /* Is PD692x0 ready to be configured? */
+ if (priv->fw_state != PD692X0_FW_OK &&
+ priv->fw_state != PD692X0_FW_COMPLETE)
+ return 0;
+
+ ret = pd692x0_configure_managers(priv);
+ if (ret)
+ return ret;
+
+ ret = pd692x0_write_ports_matrix(priv);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static void pd692x0_of_put_managers(struct pd692x0_priv *priv,
- struct pd692x0_manager *manager,
- int nmanagers)
+ struct pd692x0_manager *manager)
{
int i, j;
- for (i = 0; i < nmanagers; i++) {
+ for (i = 0; i < priv->nmanagers; i++) {
for (j = 0; j < manager[i].nports; j++)
of_node_put(manager[i].port_node[j]);
of_node_put(manager[i].node);
@@ -1198,50 +1243,71 @@ static void pd692x0_managers_free_pw_budget(struct pd692x0_priv *priv)
}
}
+static int
+pd692x0_save_user_byte(struct pd692x0_priv *priv)
+{
+ struct pd692x0_msg msg, buf;
+
+ msg = pd692x0_msg_template_list[PD692X0_MSG_SET_USER_BYTE];
+ return pd692x0_sendrecv_msg(priv, &msg, &buf);
+}
+
static int pd692x0_setup_pi_matrix(struct pse_controller_dev *pcdev)
{
- struct pd692x0_manager *manager __free(kfree) = NULL;
struct pd692x0_priv *priv = to_pd692x0_priv(pcdev);
- struct pd692x0_matrix port_matrix[PD692X0_MAX_PIS];
- int ret, nmanagers;
-
- /* Should we flash the port matrix */
- if (priv->fw_state != PD692X0_FW_OK &&
- priv->fw_state != PD692X0_FW_COMPLETE)
- return 0;
+ struct pd692x0_matrix *port_matrix;
+ struct pd692x0_manager *manager;
+ int ret;
manager = kcalloc(PD692X0_MAX_MANAGERS, sizeof(*manager), GFP_KERNEL);
if (!manager)
return -ENOMEM;
+ port_matrix = devm_kcalloc(&priv->client->dev, PD692X0_MAX_PIS,
+ sizeof(*port_matrix), GFP_KERNEL);
+ if (!port_matrix) {
+ ret = -ENOMEM;
+ goto err_free_manager;
+ }
+ priv->port_matrix = port_matrix;
+
ret = pd692x0_of_get_managers(priv, manager);
if (ret < 0)
- return ret;
+ goto err_free_manager;
- nmanagers = ret;
- ret = pd692x0_register_managers_regulator(priv, manager, nmanagers);
+ ret = pd692x0_register_managers_regulator(priv, manager);
if (ret)
goto err_of_managers;
- ret = pd692x0_configure_managers(priv, nmanagers);
+ ret = pd692x0_req_managers_pw_budget(priv);
if (ret)
goto err_of_managers;
- ret = pd692x0_set_ports_matrix(priv, manager, nmanagers, port_matrix);
+ ret = pd692x0_set_ports_matrix(priv, manager);
if (ret)
goto err_managers_req_pw;
- ret = pd692x0_write_ports_matrix(priv, port_matrix);
- if (ret)
- goto err_managers_req_pw;
+ /* Do not init the conf if it is already saved */
+ if (!priv->cfg_saved) {
+ ret = pd692x0_hw_conf_init(priv);
+ if (ret)
+ goto err_managers_req_pw;
- pd692x0_of_put_managers(priv, manager, nmanagers);
+ ret = pd692x0_save_user_byte(priv);
+ if (ret)
+ goto err_managers_req_pw;
+ }
+
+ pd692x0_of_put_managers(priv, manager);
+ kfree(manager);
return 0;
err_managers_req_pw:
pd692x0_managers_free_pw_budget(priv);
err_of_managers:
- pd692x0_of_put_managers(priv, manager, nmanagers);
+ pd692x0_of_put_managers(priv, manager);
+err_free_manager:
+ kfree(manager);
return ret;
}
@@ -1644,7 +1710,7 @@ static enum fw_upload_err pd692x0_fw_poll_complete(struct fw_upload *fwl)
return FW_UPLOAD_ERR_FW_INVALID;
}
- ret = pd692x0_setup_pi_matrix(&priv->pcdev);
+ ret = pd692x0_hw_conf_init(priv);
if (ret < 0) {
dev_err(&client->dev, "Error configuring ports matrix (%pe)\n",
ERR_PTR(ret));
@@ -1753,6 +1819,9 @@ static int pd692x0_i2c_probe(struct i2c_client *client)
}
}
+ if (buf.data[2] == PD692X0_USER_BYTE)
+ priv->cfg_saved = true;
+
priv->np = dev->of_node;
priv->pcdev.nr_lines = PD692X0_MAX_PIS;
priv->pcdev.owner = THIS_MODULE;
diff --git a/drivers/net/pse-pd/tps23881.c b/drivers/net/pse-pd/tps23881.c
index b724b222ab44..76ec1555d60d 100644
--- a/drivers/net/pse-pd/tps23881.c
+++ b/drivers/net/pse-pd/tps23881.c
@@ -55,8 +55,6 @@
#define TPS23881_REG_TPON BIT(0)
#define TPS23881_REG_FWREV 0x41
#define TPS23881_REG_DEVID 0x43
-#define TPS23881_REG_DEVID_MASK 0xF0
-#define TPS23881_DEVICE_ID 0x02
#define TPS23881_REG_CHAN1_CLASS 0x4c
#define TPS23881_REG_SRAM_CTRL 0x60
#define TPS23881_REG_SRAM_DATA 0x61
@@ -1012,8 +1010,28 @@ static const struct pse_controller_ops tps23881_ops = {
.pi_get_pw_req = tps23881_pi_get_pw_req,
};
-static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin";
-static const char fw_sram_name[] = "ti/tps23881/tps23881-sram-14.bin";
+struct tps23881_info {
+ u8 dev_id; /* device ID and silicon revision */
+ const char *fw_parity_name; /* parity code firmware file name */
+ const char *fw_sram_name; /* SRAM code firmware file name */
+};
+
+enum tps23881_model {
+ TPS23881,
+ TPS23881B,
+};
+
+static const struct tps23881_info tps23881_info[] = {
+ [TPS23881] = {
+ .dev_id = 0x22,
+ .fw_parity_name = "ti/tps23881/tps23881-parity-14.bin",
+ .fw_sram_name = "ti/tps23881/tps23881-sram-14.bin",
+ },
+ [TPS23881B] = {
+ .dev_id = 0x24,
+ /* skip SRAM load, ROM provides Clause 145 hardware-level support */
+ },
+};
struct tps23881_fw_conf {
u8 reg;
@@ -1085,16 +1103,17 @@ out:
return ret;
}
-static int tps23881_flash_sram_fw(struct i2c_client *client)
+static int tps23881_flash_sram_fw(struct i2c_client *client,
+ const struct tps23881_info *info)
{
int ret;
- ret = tps23881_flash_sram_fw_part(client, fw_parity_name,
+ ret = tps23881_flash_sram_fw_part(client, info->fw_parity_name,
tps23881_fw_parity_conf);
if (ret)
return ret;
- ret = tps23881_flash_sram_fw_part(client, fw_sram_name,
+ ret = tps23881_flash_sram_fw_part(client, info->fw_sram_name,
tps23881_fw_sram_conf);
if (ret)
return ret;
@@ -1412,6 +1431,7 @@ static int tps23881_setup_irq(struct tps23881_priv *priv, int irq)
static int tps23881_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
+ const struct tps23881_info *info;
struct tps23881_priv *priv;
struct gpio_desc *reset;
int ret;
@@ -1422,6 +1442,10 @@ static int tps23881_i2c_probe(struct i2c_client *client)
return -ENXIO;
}
+ info = i2c_get_match_data(client);
+ if (!info)
+ return -EINVAL;
+
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@@ -1440,7 +1464,7 @@ static int tps23881_i2c_probe(struct i2c_client *client)
* to Load TPS2388x SRAM and Parity Code over I2C" (Rev E))
* indicates we should delay that programming by at least 50ms. So
* we'll wait the entire 50ms here to ensure we're safe to go to the
- * SRAM loading proceedure.
+ * SRAM loading procedure.
*/
msleep(50);
}
@@ -1449,20 +1473,27 @@ static int tps23881_i2c_probe(struct i2c_client *client)
if (ret < 0)
return ret;
- if (FIELD_GET(TPS23881_REG_DEVID_MASK, ret) != TPS23881_DEVICE_ID) {
+ if (ret != info->dev_id) {
dev_err(dev, "Wrong device ID\n");
return -ENXIO;
}
- ret = tps23881_flash_sram_fw(client);
- if (ret < 0)
- return ret;
+ if (info->fw_sram_name) {
+ ret = tps23881_flash_sram_fw(client, info);
+ if (ret < 0)
+ return ret;
+ }
ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FWREV);
if (ret < 0)
return ret;
- dev_info(&client->dev, "Firmware revision 0x%x\n", ret);
+ if (ret == 0xFF) {
+ dev_err(&client->dev, "Device entered safe mode\n");
+ return -ENXIO;
+ }
+ dev_info(&client->dev, "Firmware revision 0x%x%s\n", ret,
+ ret == 0x00 ? " (ROM firmware)" : "");
/* Set configuration B, 16 bit access on a single device address */
ret = i2c_smbus_read_byte_data(client, TPS23881_REG_GEN_MASK);
@@ -1498,13 +1529,21 @@ static int tps23881_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id tps23881_id[] = {
- { "tps23881" },
+ { "tps23881", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881] },
+ { "tps23881b", .driver_data = (kernel_ulong_t)&tps23881_info[TPS23881B] },
{ }
};
MODULE_DEVICE_TABLE(i2c, tps23881_id);
static const struct of_device_id tps23881_of_match[] = {
- { .compatible = "ti,tps23881", },
+ {
+ .compatible = "ti,tps23881",
+ .data = &tps23881_info[TPS23881]
+ },
+ {
+ .compatible = "ti,tps23881b",
+ .data = &tps23881_info[TPS23881B]
+ },
{ },
};
MODULE_DEVICE_TABLE(of, tps23881_of_match);
diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c
index 55aa8d0c8e1f..c10198d44576 100644
--- a/drivers/net/sungem_phy.c
+++ b/drivers/net/sungem_phy.c
@@ -1165,7 +1165,7 @@ int sungem_phy_probe(struct mii_phy *phy, int mii_id)
int i;
/* We do not reset the mii_phy structure as the driver
- * may re-probe the PHY regulary
+ * may re-probe the PHY regularly
*/
phy->mii_id = mii_id;
diff --git a/drivers/net/team/team_core.c b/drivers/net/team/team_core.c
index 17f07eb0ee52..4d5c9ae8f221 100644
--- a/drivers/net/team/team_core.c
+++ b/drivers/net/team/team_core.c
@@ -982,63 +982,6 @@ static void team_port_disable(struct team *team,
team_lower_state_changed(port);
}
-#define TEAM_VLAN_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE | \
- NETIF_F_HIGHDMA | NETIF_F_LRO | \
- NETIF_F_GSO_ENCAP_ALL)
-
-#define TEAM_ENC_FEATURES (NETIF_F_HW_CSUM | NETIF_F_SG | \
- NETIF_F_RXCSUM | NETIF_F_GSO_SOFTWARE)
-
-static void __team_compute_features(struct team *team)
-{
- struct team_port *port;
- netdev_features_t vlan_features = TEAM_VLAN_FEATURES;
- netdev_features_t enc_features = TEAM_ENC_FEATURES;
- unsigned short max_hard_header_len = ETH_HLEN;
- unsigned int dst_release_flag = IFF_XMIT_DST_RELEASE |
- IFF_XMIT_DST_RELEASE_PERM;
-
- rcu_read_lock();
- if (list_empty(&team->port_list))
- goto done;
-
- vlan_features = netdev_base_features(vlan_features);
- enc_features = netdev_base_features(enc_features);
-
- list_for_each_entry_rcu(port, &team->port_list, list) {
- vlan_features = netdev_increment_features(vlan_features,
- port->dev->vlan_features,
- TEAM_VLAN_FEATURES);
- enc_features =
- netdev_increment_features(enc_features,
- port->dev->hw_enc_features,
- TEAM_ENC_FEATURES);
-
- dst_release_flag &= port->dev->priv_flags;
- if (port->dev->hard_header_len > max_hard_header_len)
- max_hard_header_len = port->dev->hard_header_len;
- }
-done:
- rcu_read_unlock();
-
- team->dev->vlan_features = vlan_features;
- team->dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL |
- NETIF_F_HW_VLAN_CTAG_TX |
- NETIF_F_HW_VLAN_STAG_TX;
- team->dev->hard_header_len = max_hard_header_len;
-
- team->dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
- if (dst_release_flag == (IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM))
- team->dev->priv_flags |= IFF_XMIT_DST_RELEASE;
-}
-
-static void team_compute_features(struct team *team)
-{
- __team_compute_features(team);
- netdev_change_features(team->dev);
-}
-
static int team_port_enter(struct team *team, struct team_port *port)
{
int err = 0;
@@ -1191,10 +1134,6 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
return -EPERM;
}
- err = team_dev_type_check_change(dev, port_dev);
- if (err)
- return err;
-
if (port_dev->flags & IFF_UP) {
NL_SET_ERR_MSG(extack, "Device is up. Set it down before adding it as a team port");
netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
@@ -1212,10 +1151,16 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
INIT_LIST_HEAD(&port->qom_list);
port->orig.mtu = port_dev->mtu;
- err = dev_set_mtu(port_dev, dev->mtu);
- if (err) {
- netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
- goto err_set_mtu;
+ /*
+ * MTU assignment will be handled in team_dev_type_check_change
+ * if dev and port_dev are of different types
+ */
+ if (dev->type == port_dev->type) {
+ err = dev_set_mtu(port_dev, dev->mtu);
+ if (err) {
+ netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
+ goto err_set_mtu;
+ }
}
memcpy(port->orig.dev_addr, port_dev->dev_addr, port_dev->addr_len);
@@ -1286,10 +1231,14 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
if (err) {
if (dev->flags & IFF_PROMISC)
dev_set_promiscuity(port_dev, -1);
- goto err_set_slave_promisc;
+ goto err_set_slave_allmulti;
}
}
+ err = team_dev_type_check_change(dev, port_dev);
+ if (err)
+ goto err_set_dev_type;
+
if (dev->flags & IFF_UP) {
netif_addr_lock_bh(dev);
dev_uc_sync_multiple(port_dev, dev);
@@ -1300,7 +1249,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
port->index = -1;
list_add_tail_rcu(&port->list, &team->port_list);
team_port_enable(team, port);
- __team_compute_features(team);
+ netdev_compute_master_upper_features(team->dev, true);
__team_port_change_port_added(port, !!netif_oper_up(port_dev));
__team_options_change_check(team);
@@ -1308,6 +1257,8 @@ static int team_port_add(struct team *team, struct net_device *port_dev,
return 0;
+err_set_dev_type:
+err_set_slave_allmulti:
err_set_slave_promisc:
__team_option_inst_del_port(team, port);
@@ -1382,7 +1333,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
dev_set_mtu(port_dev, port->orig.mtu);
kfree_rcu(port, rcu);
netdev_info(dev, "Port device %s removed\n", portname);
- __team_compute_features(team);
+ netdev_compute_master_upper_features(team->dev, true);
return 0;
}
@@ -1970,33 +1921,19 @@ static int team_add_slave(struct net_device *dev, struct net_device *port_dev,
struct netlink_ext_ack *extack)
{
struct team *team = netdev_priv(dev);
- int err;
ASSERT_RTNL();
- err = team_port_add(team, port_dev, extack);
-
- if (!err)
- netdev_change_features(dev);
-
- return err;
+ return team_port_add(team, port_dev, extack);
}
static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
{
struct team *team = netdev_priv(dev);
- int err;
ASSERT_RTNL();
- err = team_port_del(team, port_dev);
-
- if (err)
- return err;
-
- netdev_change_features(dev);
-
- return err;
+ return team_port_del(team, port_dev);
}
static netdev_features_t team_fix_features(struct net_device *dev,
@@ -2190,7 +2127,7 @@ static void team_setup(struct net_device *dev)
dev->features |= NETIF_F_GRO;
- dev->hw_features = TEAM_VLAN_FEATURES |
+ dev->hw_features = MASTER_UPPER_DEV_VLAN_FEATURES |
NETIF_F_HW_VLAN_CTAG_RX |
NETIF_F_HW_VLAN_CTAG_FILTER |
NETIF_F_HW_VLAN_STAG_RX |
@@ -2994,7 +2931,7 @@ static int team_device_event(struct notifier_block *unused,
case NETDEV_FEAT_CHANGE:
if (!port->team->notifier_ctx) {
port->team->notifier_ctx = true;
- team_compute_features(port->team);
+ netdev_compute_master_upper_features(port->team->dev, true);
port->team->notifier_ctx = false;
}
break;
diff --git a/drivers/net/team/team_nl.c b/drivers/net/team/team_nl.c
index 208424ab78f5..6db21725f9cc 100644
--- a/drivers/net/team/team_nl.c
+++ b/drivers/net/team/team_nl.c
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/team.yaml */
/* YNL-GEN kernel source */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#include <net/netlink.h>
#include <net/genetlink.h>
diff --git a/drivers/net/team/team_nl.h b/drivers/net/team/team_nl.h
index c9ec1b22ac4d..74816b193475 100644
--- a/drivers/net/team/team_nl.h
+++ b/drivers/net/team/team_nl.h
@@ -2,6 +2,7 @@
/* Do not edit directly, auto-generated from: */
/* Documentation/netlink/specs/team.yaml */
/* YNL-GEN kernel header */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
#ifndef _LINUX_TEAM_GEN_H
#define _LINUX_TEAM_GEN_H
diff --git a/drivers/net/tun_vnet.h b/drivers/net/tun_vnet.h
index 81662328b2c7..a5f93b6c4482 100644
--- a/drivers/net/tun_vnet.h
+++ b/drivers/net/tun_vnet.h
@@ -244,7 +244,7 @@ tun_vnet_hdr_tnl_from_skb(unsigned int flags,
if (virtio_net_hdr_tnl_from_skb(skb, tnl_hdr, has_tnl_offload,
tun_vnet_is_little_endian(flags),
- vlan_hlen)) {
+ vlan_hlen, true)) {
struct virtio_net_hdr_v1 *hdr = &tnl_hdr->hash_hdr.hdr;
struct skb_shared_info *sinfo = skb_shinfo(skb);
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 85bd5d845409..232bbd79a4de 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -230,7 +230,9 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
int i;
unsigned long gpio_bits = dev->driver_info->data;
- usbnet_get_endpoints(dev,intf);
+ ret = usbnet_get_endpoints(dev, intf);
+ if (ret)
+ goto out;
/* Toggle the GPIOs in a manufacturer/model specific way */
for (i = 2; i >= 0; i--) {
@@ -848,7 +850,9 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
dev->driver_priv = priv;
- usbnet_get_endpoints(dev, intf);
+ ret = usbnet_get_endpoints(dev, intf);
+ if (ret)
+ return ret;
/* Maybe the boot loader passed the MAC address via device tree */
if (!eth_platform_get_mac_address(&dev->udev->dev, buf)) {
@@ -1281,7 +1285,9 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
int ret;
u8 buf[ETH_ALEN] = {0};
- usbnet_get_endpoints(dev,intf);
+ ret = usbnet_get_endpoints(dev, intf);
+ if (ret)
+ return ret;
/* Get the MAC address */
ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 11352d85475a..3a4985b582cb 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -192,6 +192,12 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
if (!skbn)
return 0;
+ /* Raw IP packets don't have a MAC header, but other subsystems
+ * (like xfrm) may still access MAC header offsets, so they must
+ * be initialized.
+ */
+ skb_reset_mac_header(skbn);
+
switch (skb->data[offset + qmimux_hdr_sz] & 0xf0) {
case 0x40:
skbn->protocol = htons(ETH_P_IP);
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index a22d4bb2cf3b..fa5192583860 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -9311,6 +9311,7 @@ static const struct ethtool_ops ops = {
.set_ringparam = rtl8152_set_ringparam,
.get_pauseparam = rtl8152_get_pauseparam,
.set_pauseparam = rtl8152_set_pauseparam,
+ .get_ts_info = ethtool_op_get_ts_info,
};
static int rtl8152_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd)
diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
index 92add3daadbb..278e6cb6f4d9 100644
--- a/drivers/net/usb/rtl8150.c
+++ b/drivers/net/usb/rtl8150.c
@@ -685,9 +685,16 @@ static netdev_tx_t rtl8150_start_xmit(struct sk_buff *skb,
rtl8150_t *dev = netdev_priv(netdev);
int count, res;
+ /* pad the frame and ensure terminating USB packet, datasheet 9.2.3 */
+ count = max(skb->len, ETH_ZLEN);
+ if (count % 64 == 0)
+ count++;
+ if (skb_padto(skb, count)) {
+ netdev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+ }
+
netif_stop_queue(netdev);
- count = (skb->len < 60) ? 60 : skb->len;
- count = (count & 0x3f) ? count : count + 1;
dev->tx_skb = skb;
usb_fill_bulk_urb(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
skb->data, count, write_bulk_callback, dev);
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index bf01f2728531..1d9faa70ba3b 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -142,16 +142,16 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
if (alt->desc.bAlternateSetting != 0 ||
!(dev->driver_info->flags & FLAG_NO_SETINT)) {
- tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
- alt->desc.bAlternateSetting);
+ tmp = usb_set_interface(dev->udev, alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
if (tmp < 0)
return tmp;
}
- dev->in = usb_rcvbulkpipe (dev->udev,
- in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
- dev->out = usb_sndbulkpipe (dev->udev,
- out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->in = usb_rcvbulkpipe(dev->udev,
+ in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->out = usb_sndbulkpipe(dev->udev,
+ out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
dev->status = status;
return 0;
}
@@ -163,7 +163,7 @@ int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress)
int tmp = -1, ret;
unsigned char buf [13];
- ret = usb_string(dev->udev, iMACAddress, buf, sizeof buf);
+ ret = usb_string(dev->udev, iMACAddress, buf, sizeof(buf));
if (ret == 12)
tmp = hex2bin(addr, buf, 6);
if (tmp < 0) {
@@ -189,7 +189,7 @@ static bool usbnet_needs_usb_name_format(struct usbnet *dev, struct net_device *
is_local_ether_addr(net->dev_addr));
}
-static void intr_complete (struct urb *urb)
+static void intr_complete(struct urb *urb)
{
struct usbnet *dev = urb->context;
int status = urb->status;
@@ -215,13 +215,13 @@ static void intr_complete (struct urb *urb)
break;
}
- status = usb_submit_urb (urb, GFP_ATOMIC);
+ status = usb_submit_urb(urb, GFP_ATOMIC);
if (status != 0)
netif_err(dev, timer, dev->net,
"intr resubmit --> %d\n", status);
}
-static int init_status (struct usbnet *dev, struct usb_interface *intf)
+static int init_status(struct usbnet *dev, struct usb_interface *intf)
{
char *buf = NULL;
unsigned pipe = 0;
@@ -231,24 +231,24 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)
if (!dev->driver_info->status)
return 0;
- pipe = usb_rcvintpipe (dev->udev,
- dev->status->desc.bEndpointAddress
- & USB_ENDPOINT_NUMBER_MASK);
+ pipe = usb_rcvintpipe(dev->udev,
+ dev->status->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
maxp = usb_maxpacket(dev->udev, pipe);
/* avoid 1 msec chatter: min 8 msec poll rate */
period = max ((int) dev->status->desc.bInterval,
(dev->udev->speed == USB_SPEED_HIGH) ? 7 : 3);
- buf = kmalloc (maxp, GFP_KERNEL);
+ buf = kmalloc(maxp, GFP_KERNEL);
if (buf) {
- dev->interrupt = usb_alloc_urb (0, GFP_KERNEL);
+ dev->interrupt = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->interrupt) {
- kfree (buf);
+ kfree(buf);
return -ENOMEM;
} else {
usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
- buf, maxp, intr_complete, dev, period);
+ buf, maxp, intr_complete, dev, period);
dev->interrupt->transfer_flags |= URB_FREE_BUFFER;
dev_dbg(&intf->dev,
"status ep%din, %d bytes period %d\n",
@@ -326,7 +326,7 @@ static void __usbnet_status_stop_force(struct usbnet *dev)
* Some link protocols batch packets, so their rx_fixup paths
* can return clones as well as just modify the original skb.
*/
-void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
+void usbnet_skb_return(struct usbnet *dev, struct sk_buff *skb)
{
struct pcpu_sw_netstats *stats64 = this_cpu_ptr(dev->net->tstats);
unsigned long flags;
@@ -339,7 +339,7 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
/* only update if unset to allow minidriver rx_fixup override */
if (skb->protocol == 0)
- skb->protocol = eth_type_trans (skb, dev->net);
+ skb->protocol = eth_type_trans(skb, dev->net);
flags = u64_stats_update_begin_irqsave(&stats64->syncp);
u64_stats_inc(&stats64->rx_packets);
@@ -347,8 +347,8 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
u64_stats_update_end_irqrestore(&stats64->syncp, flags);
netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
- skb->len + sizeof (struct ethhdr), skb->protocol);
- memset (skb->cb, 0, sizeof (struct skb_data));
+ skb->len + sizeof(struct ethhdr), skb->protocol);
+ memset(skb->cb, 0, sizeof(struct skb_data));
if (skb_defer_rx_timestamp(skb))
return;
@@ -396,7 +396,7 @@ EXPORT_SYMBOL_GPL(usbnet_update_max_qlen);
*
*-------------------------------------------------------------------------*/
-int usbnet_change_mtu (struct net_device *net, int new_mtu)
+int usbnet_change_mtu(struct net_device *net, int new_mtu)
{
struct usbnet *dev = netdev_priv(net);
int ll_mtu = new_mtu + net->hard_header_len;
@@ -472,7 +472,7 @@ static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
* NOTE: annoying asymmetry: if it's active, schedule_work() fails,
* but tasklet_schedule() doesn't. hope the failure is rare.
*/
-void usbnet_defer_kevent (struct usbnet *dev, int work)
+void usbnet_defer_kevent(struct usbnet *dev, int work)
{
set_bit (work, &dev->flags);
if (!usbnet_going_away(dev)) {
@@ -489,9 +489,9 @@ EXPORT_SYMBOL_GPL(usbnet_defer_kevent);
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb);
+static void rx_complete(struct urb *urb);
-static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
+static int rx_submit(struct usbnet *dev, struct urb *urb, gfp_t flags)
{
struct sk_buff *skb;
struct skb_data *entry;
@@ -511,8 +511,8 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
skb = __netdev_alloc_skb_ip_align(dev->net, size, flags);
if (!skb) {
netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
- usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
- usb_free_urb (urb);
+ usbnet_defer_kevent(dev, EVENT_RX_MEMORY);
+ usb_free_urb(urb);
return -ENOMEM;
}
@@ -521,27 +521,27 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
entry->dev = dev;
entry->length = 0;
- usb_fill_bulk_urb (urb, dev->udev, dev->in,
- skb->data, size, rx_complete, skb);
+ usb_fill_bulk_urb(urb, dev->udev, dev->in,
+ skb->data, size, rx_complete, skb);
- spin_lock_irqsave (&dev->rxq.lock, lockflags);
+ spin_lock_irqsave(&dev->rxq.lock, lockflags);
- if (netif_running (dev->net) &&
- netif_device_present (dev->net) &&
+ if (netif_running(dev->net) &&
+ netif_device_present(dev->net) &&
test_bit(EVENT_DEV_OPEN, &dev->flags) &&
- !test_bit (EVENT_RX_HALT, &dev->flags) &&
- !test_bit (EVENT_DEV_ASLEEP, &dev->flags) &&
+ !test_bit(EVENT_RX_HALT, &dev->flags) &&
+ !test_bit(EVENT_DEV_ASLEEP, &dev->flags) &&
!usbnet_going_away(dev)) {
- switch (retval = usb_submit_urb (urb, GFP_ATOMIC)) {
+ switch (retval = usb_submit_urb(urb, GFP_ATOMIC)) {
case -EPIPE:
- usbnet_defer_kevent (dev, EVENT_RX_HALT);
+ usbnet_defer_kevent(dev, EVENT_RX_HALT);
break;
case -ENOMEM:
- usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+ usbnet_defer_kevent(dev, EVENT_RX_MEMORY);
break;
case -ENODEV:
netif_dbg(dev, ifdown, dev->net, "device gone\n");
- netif_device_detach (dev->net);
+ netif_device_detach(dev->net);
break;
case -EHOSTUNREACH:
retval = -ENOLINK;
@@ -558,10 +558,10 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
retval = -ENOLINK;
}
- spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
+ spin_unlock_irqrestore(&dev->rxq.lock, lockflags);
if (retval) {
- dev_kfree_skb_any (skb);
- usb_free_urb (urb);
+ dev_kfree_skb_any(skb);
+ usb_free_urb(urb);
}
return retval;
}
@@ -572,7 +572,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
static inline int rx_process(struct usbnet *dev, struct sk_buff *skb)
{
if (dev->driver_info->rx_fixup &&
- !dev->driver_info->rx_fixup (dev, skb)) {
+ !dev->driver_info->rx_fixup(dev, skb)) {
/* With RX_ASSEMBLE, rx_fixup() must update counters */
if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE))
dev->net->stats.rx_errors++;
@@ -597,7 +597,7 @@ static inline int rx_process(struct usbnet *dev, struct sk_buff *skb)
/*-------------------------------------------------------------------------*/
-static void rx_complete (struct urb *urb)
+static void rx_complete(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
struct skb_data *entry = (struct skb_data *) skb->cb;
@@ -605,7 +605,7 @@ static void rx_complete (struct urb *urb)
int urb_status = urb->status;
enum skb_state state;
- skb_put (skb, urb->actual_length);
+ skb_put(skb, urb->actual_length);
state = rx_done;
entry->urb = NULL;
@@ -621,7 +621,7 @@ static void rx_complete (struct urb *urb)
*/
case -EPIPE:
dev->net->stats.rx_errors++;
- usbnet_defer_kevent (dev, EVENT_RX_HALT);
+ usbnet_defer_kevent(dev, EVENT_RX_HALT);
fallthrough;
/* software-driven interface shutdown */
@@ -639,8 +639,8 @@ static void rx_complete (struct urb *urb)
case -ETIME:
case -EILSEQ:
dev->net->stats.rx_errors++;
- if (!timer_pending (&dev->delay)) {
- mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
+ if (!timer_pending(&dev->delay)) {
+ mod_timer(&dev->delay, jiffies + THROTTLE_JIFFIES);
netif_dbg(dev, link, dev->net,
"rx throttle %d\n", urb_status);
}
@@ -676,14 +676,14 @@ block:
state = defer_bh(dev, skb, &dev->rxq, state);
if (urb) {
- if (netif_running (dev->net) &&
- !test_bit (EVENT_RX_HALT, &dev->flags) &&
+ if (netif_running(dev->net) &&
+ !test_bit(EVENT_RX_HALT, &dev->flags) &&
state != unlink_start) {
- rx_submit (dev, urb, GFP_ATOMIC);
+ rx_submit(dev, urb, GFP_ATOMIC);
usb_mark_last_busy(dev->udev);
return;
}
- usb_free_urb (urb);
+ usb_free_urb(urb);
}
netif_dbg(dev, rx_err, dev->net, "no read resubmitted\n");
}
@@ -728,7 +728,7 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);
// unlink pending rx/tx; completion handlers do all other cleanup
-static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
+static int unlink_urbs(struct usbnet *dev, struct sk_buff_head *q)
{
unsigned long flags;
struct sk_buff *skb;
@@ -761,7 +761,7 @@ found:
spin_unlock_irqrestore(&q->lock, flags);
// during some PM-driven resume scenarios,
// these (async) unlinks complete immediately
- retval = usb_unlink_urb (urb);
+ retval = usb_unlink_urb(urb);
if (retval != -EINPROGRESS && retval != 0)
netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
else
@@ -769,7 +769,7 @@ found:
usb_put_urb(urb);
spin_lock_irqsave(&q->lock, flags);
}
- spin_unlock_irqrestore (&q->lock, flags);
+ spin_unlock_irqrestore(&q->lock, flags);
return count;
}
@@ -823,14 +823,15 @@ static void usbnet_terminate_urbs(struct usbnet *dev)
remove_wait_queue(&dev->wait, &wait);
}
-int usbnet_stop (struct net_device *net)
+int usbnet_stop(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
const struct driver_info *info = dev->driver_info;
int retval, pm, mpn;
clear_bit(EVENT_DEV_OPEN, &dev->flags);
- netif_stop_queue (net);
+ netif_stop_queue(net);
+ netdev_reset_queue(net);
netif_info(dev, ifdown, dev->net,
"stop stats: rx/tx %lu/%lu, errs %lu/%lu\n",
@@ -892,7 +893,7 @@ EXPORT_SYMBOL_GPL(usbnet_stop);
// precondition: never called in_interrupt
-int usbnet_open (struct net_device *net)
+int usbnet_open(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
int retval;
@@ -909,23 +910,29 @@ int usbnet_open (struct net_device *net)
}
// put into "known safe" state
- if (info->reset && (retval = info->reset (dev)) < 0) {
- netif_info(dev, ifup, dev->net,
- "open reset fail (%d) usbnet usb-%s-%s, %s\n",
- retval,
- dev->udev->bus->bus_name,
- dev->udev->devpath,
- info->description);
- goto done;
+ if (info->reset) {
+ retval = info->reset(dev);
+ if (retval < 0) {
+ netif_info(dev, ifup, dev->net,
+ "open reset fail (%d) usbnet usb-%s-%s, %s\n",
+ retval,
+ dev->udev->bus->bus_name,
+ dev->udev->devpath,
+ info->description);
+ goto done;
+ }
}
/* hard_mtu or rx_urb_size may change in reset() */
usbnet_update_max_qlen(dev);
// insist peer be connected
- if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
- netif_err(dev, ifup, dev->net, "can't open; %d\n", retval);
- goto done;
+ if (info->check_connect) {
+ retval = info->check_connect(dev);
+ if (retval < 0) {
+ netif_err(dev, ifup, dev->net, "can't open; %d\n", retval);
+ goto done;
+ }
}
/* start any status interrupt transfer */
@@ -939,6 +946,7 @@ int usbnet_open (struct net_device *net)
}
set_bit(EVENT_DEV_OPEN, &dev->flags);
+ netdev_reset_queue(net);
netif_start_queue (net);
netif_info(dev, ifup, dev->net,
"open: enable queueing (rx %d, tx %d) mtu %d %s framing\n",
@@ -1048,13 +1056,13 @@ int usbnet_set_link_ksettings_mii(struct net_device *net,
}
EXPORT_SYMBOL_GPL(usbnet_set_link_ksettings_mii);
-u32 usbnet_get_link (struct net_device *net)
+u32 usbnet_get_link(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
/* If a check_connect is defined, return its result */
if (dev->driver_info->check_connect)
- return dev->driver_info->check_connect (dev) == 0;
+ return dev->driver_info->check_connect(dev) == 0;
/* if the device has mii operations, use those */
if (dev->mii.mdio_read)
@@ -1076,18 +1084,18 @@ int usbnet_nway_reset(struct net_device *net)
}
EXPORT_SYMBOL_GPL(usbnet_nway_reset);
-void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
+void usbnet_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
{
struct usbnet *dev = netdev_priv(net);
strscpy(info->driver, dev->driver_name, sizeof(info->driver));
strscpy(info->fw_version, dev->driver_info->description,
sizeof(info->fw_version));
- usb_make_path (dev->udev, info->bus_info, sizeof info->bus_info);
+ usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info));
}
EXPORT_SYMBOL_GPL(usbnet_get_drvinfo);
-u32 usbnet_get_msglevel (struct net_device *net)
+u32 usbnet_get_msglevel(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
@@ -1095,7 +1103,7 @@ u32 usbnet_get_msglevel (struct net_device *net)
}
EXPORT_SYMBOL_GPL(usbnet_get_msglevel);
-void usbnet_set_msglevel (struct net_device *net, u32 level)
+void usbnet_set_msglevel(struct net_device *net, u32 level)
{
struct usbnet *dev = netdev_priv(net);
@@ -1166,71 +1174,71 @@ static void __handle_set_rx_mode(struct usbnet *dev)
* especially now that control transfers can be queued.
*/
static void
-usbnet_deferred_kevent (struct work_struct *work)
+usbnet_deferred_kevent(struct work_struct *work)
{
struct usbnet *dev =
container_of(work, struct usbnet, kevent);
int status;
/* usb_clear_halt() needs a thread context */
- if (test_bit (EVENT_TX_HALT, &dev->flags)) {
- unlink_urbs (dev, &dev->txq);
+ if (test_bit(EVENT_TX_HALT, &dev->flags)) {
+ unlink_urbs(dev, &dev->txq);
status = usb_autopm_get_interface(dev->intf);
if (status < 0)
goto fail_pipe;
- status = usb_clear_halt (dev->udev, dev->out);
+ status = usb_clear_halt(dev->udev, dev->out);
usb_autopm_put_interface(dev->intf);
if (status < 0 &&
status != -EPIPE &&
status != -ESHUTDOWN) {
- if (netif_msg_tx_err (dev))
+ if (netif_msg_tx_err(dev))
fail_pipe:
netdev_err(dev->net, "can't clear tx halt, status %d\n",
status);
} else {
- clear_bit (EVENT_TX_HALT, &dev->flags);
+ clear_bit(EVENT_TX_HALT, &dev->flags);
if (status != -ESHUTDOWN)
- netif_wake_queue (dev->net);
+ netif_wake_queue(dev->net);
}
}
- if (test_bit (EVENT_RX_HALT, &dev->flags)) {
- unlink_urbs (dev, &dev->rxq);
+ if (test_bit(EVENT_RX_HALT, &dev->flags)) {
+ unlink_urbs(dev, &dev->rxq);
status = usb_autopm_get_interface(dev->intf);
if (status < 0)
goto fail_halt;
- status = usb_clear_halt (dev->udev, dev->in);
+ status = usb_clear_halt(dev->udev, dev->in);
usb_autopm_put_interface(dev->intf);
if (status < 0 &&
status != -EPIPE &&
status != -ESHUTDOWN) {
- if (netif_msg_rx_err (dev))
+ if (netif_msg_rx_err(dev))
fail_halt:
netdev_err(dev->net, "can't clear rx halt, status %d\n",
status);
} else {
- clear_bit (EVENT_RX_HALT, &dev->flags);
+ clear_bit(EVENT_RX_HALT, &dev->flags);
if (!usbnet_going_away(dev))
queue_work(system_bh_wq, &dev->bh_work);
}
}
/* work could resubmit itself forever if memory is tight */
- if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
+ if (test_bit(EVENT_RX_MEMORY, &dev->flags)) {
struct urb *urb = NULL;
int resched = 1;
- if (netif_running (dev->net))
- urb = usb_alloc_urb (0, GFP_KERNEL);
+ if (netif_running(dev->net))
+ urb = usb_alloc_urb(0, GFP_KERNEL);
else
- clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ clear_bit(EVENT_RX_MEMORY, &dev->flags);
if (urb != NULL) {
- clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ clear_bit(EVENT_RX_MEMORY, &dev->flags);
status = usb_autopm_get_interface(dev->intf);
if (status < 0) {
usb_free_urb(urb);
goto fail_lowmem;
}
- if (rx_submit (dev, urb, GFP_KERNEL) == -ENOLINK)
+ if (rx_submit(dev, urb, GFP_KERNEL) == -ENOLINK)
resched = 0;
usb_autopm_put_interface(dev->intf);
fail_lowmem:
@@ -1244,7 +1252,7 @@ fail_lowmem:
const struct driver_info *info = dev->driver_info;
int retval = 0;
- clear_bit (EVENT_LINK_RESET, &dev->flags);
+ clear_bit(EVENT_LINK_RESET, &dev->flags);
status = usb_autopm_get_interface(dev->intf);
if (status < 0)
goto skip_reset;
@@ -1264,10 +1272,10 @@ skip_reset:
__handle_link_change(dev);
}
- if (test_bit (EVENT_LINK_CHANGE, &dev->flags))
+ if (test_bit(EVENT_LINK_CHANGE, &dev->flags))
__handle_link_change(dev);
- if (test_bit (EVENT_SET_RX_MODE, &dev->flags))
+ if (test_bit(EVENT_SET_RX_MODE, &dev->flags))
__handle_set_rx_mode(dev);
@@ -1277,7 +1285,7 @@ skip_reset:
/*-------------------------------------------------------------------------*/
-static void tx_complete (struct urb *urb)
+static void tx_complete(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
struct skb_data *entry = (struct skb_data *) skb->cb;
@@ -1296,7 +1304,7 @@ static void tx_complete (struct urb *urb)
switch (urb->status) {
case -EPIPE:
- usbnet_defer_kevent (dev, EVENT_TX_HALT);
+ usbnet_defer_kevent(dev, EVENT_TX_HALT);
break;
/* software-driven interface shutdown */
@@ -1311,13 +1319,13 @@ static void tx_complete (struct urb *urb)
case -ETIME:
case -EILSEQ:
usb_mark_last_busy(dev->udev);
- if (!timer_pending (&dev->delay)) {
- mod_timer (&dev->delay,
- jiffies + THROTTLE_JIFFIES);
+ if (!timer_pending(&dev->delay)) {
+ mod_timer(&dev->delay,
+ jiffies + THROTTLE_JIFFIES);
netif_dbg(dev, link, dev->net,
"tx throttle %d\n", urb->status);
}
- netif_stop_queue (dev->net);
+ netif_stop_queue(dev->net);
break;
default:
netif_dbg(dev, tx_err, dev->net,
@@ -1332,11 +1340,11 @@ static void tx_complete (struct urb *urb)
/*-------------------------------------------------------------------------*/
-void usbnet_tx_timeout (struct net_device *net, unsigned int txqueue)
+void usbnet_tx_timeout(struct net_device *net, unsigned int txqueue)
{
struct usbnet *dev = netdev_priv(net);
- unlink_urbs (dev, &dev->txq);
+ unlink_urbs(dev, &dev->txq);
queue_work(system_bh_wq, &dev->bh_work);
/* this needs to be handled individually because the generic layer
* doesn't know what is sufficient and could not restore private
@@ -1382,8 +1390,7 @@ static int build_dma_sg(const struct sk_buff *skb, struct urb *urb)
return 1;
}
-netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
- struct net_device *net)
+netdev_tx_t usbnet_start_xmit(struct sk_buff *skb, struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
unsigned int length;
@@ -1399,7 +1406,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
// some devices want funky USB-level framing, for
// win32 driver (usually) and/or hardware quirks
if (info->tx_fixup) {
- skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
+ skb = info->tx_fixup(dev, skb, GFP_ATOMIC);
if (!skb) {
/* packet collected; minidriver waiting for more */
if (info->flags & FLAG_MULTI_PACKET)
@@ -1409,7 +1416,8 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
}
}
- if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
netif_dbg(dev, tx_err, dev->net, "no urb\n");
goto drop;
}
@@ -1418,8 +1426,8 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
entry->urb = urb;
entry->dev = dev;
- usb_fill_bulk_urb (urb, dev->udev, dev->out,
- skb->data, skb->len, tx_complete, skb);
+ usb_fill_bulk_urb(urb, dev->udev, dev->out,
+ skb->data, skb->len, tx_complete, skb);
if (dev->can_dma_sg) {
if (build_dma_sg(skb, urb) < 0)
goto drop;
@@ -1489,8 +1497,8 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
case -EPIPE:
- netif_stop_queue (net);
- usbnet_defer_kevent (dev, EVENT_TX_HALT);
+ netif_stop_queue(net);
+ usbnet_defer_kevent(dev, EVENT_TX_HALT);
usb_autopm_put_interface_async(dev->intf);
break;
default:
@@ -1501,10 +1509,11 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
case 0:
netif_trans_update(net);
__usbnet_queue_skb(&dev->txq, skb, tx_start);
+ netdev_sent_queue(net, skb->len);
if (dev->txq.qlen >= TX_QLEN (dev))
netif_stop_queue (net);
}
- spin_unlock_irqrestore (&dev->txq.lock, flags);
+ spin_unlock_irqrestore(&dev->txq.lock, flags);
if (retval) {
netif_dbg(dev, tx_err, dev->net, "drop, code %d\n", retval);
@@ -1512,7 +1521,7 @@ drop:
dev->net->stats.tx_dropped++;
not_drop:
if (skb)
- dev_kfree_skb_any (skb);
+ dev_kfree_skb_any(skb);
if (urb) {
kfree(urb->sg);
usb_free_urb(urb);
@@ -1561,9 +1570,10 @@ static inline void usb_free_skb(struct sk_buff *skb)
// work (work deferred from completions, in_irq) or timer
-static void usbnet_bh (struct timer_list *t)
+static void usbnet_bh(struct timer_list *t)
{
struct usbnet *dev = timer_container_of(dev, t, delay);
+ unsigned int bytes_compl = 0, pkts_compl = 0;
struct sk_buff *skb;
struct skb_data *entry;
@@ -1575,6 +1585,8 @@ static void usbnet_bh (struct timer_list *t)
usb_free_skb(skb);
continue;
case tx_done:
+ bytes_compl += skb->len;
+ pkts_compl++;
kfree(entry->urb->sg);
fallthrough;
case rx_cleanup:
@@ -1585,6 +1597,10 @@ static void usbnet_bh (struct timer_list *t)
}
}
+ spin_lock_bh(&dev->bql_spinlock);
+ netdev_completed_queue(dev->net, pkts_compl, bytes_compl);
+ spin_unlock_bh(&dev->bql_spinlock);
+
/* restart RX again after disabling due to high error rate */
clear_bit(EVENT_RX_KILL, &dev->flags);
@@ -1616,7 +1632,7 @@ static void usbnet_bh (struct timer_list *t)
queue_work(system_bh_wq, &dev->bh_work);
}
if (dev->txq.qlen < TX_QLEN (dev))
- netif_wake_queue (dev->net);
+ netif_wake_queue(dev->net);
}
}
@@ -1636,7 +1652,7 @@ static void usbnet_bh_work(struct work_struct *work)
// precondition: never called in_interrupt
-void usbnet_disconnect (struct usb_interface *intf)
+void usbnet_disconnect(struct usb_interface *intf)
{
struct usbnet *dev;
struct usb_device *xdev;
@@ -1649,7 +1665,7 @@ void usbnet_disconnect (struct usb_interface *intf)
return;
usbnet_mark_going_away(dev);
- xdev = interface_to_usbdev (intf);
+ xdev = interface_to_usbdev(intf);
netif_info(dev, probe, dev->net, "unregister '%s' usb-%s-%s, %s\n",
intf->dev.driver->name,
@@ -1657,7 +1673,9 @@ void usbnet_disconnect (struct usb_interface *intf)
dev->driver_info->description);
net = dev->net;
- unregister_netdev (net);
+ unregister_netdev(net);
+
+ cancel_work_sync(&dev->kevent);
while ((urb = usb_get_from_anchor(&dev->deferred))) {
dev_kfree_skb(urb->context);
@@ -1700,7 +1718,7 @@ static const struct device_type wwan_type = {
};
int
-usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
+usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod)
{
struct usbnet *dev;
struct net_device *net;
@@ -1726,7 +1744,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
dev_dbg (&udev->dev, "blacklisted by %s\n", name);
return -ENODEV;
}
- xdev = interface_to_usbdev (udev);
+ xdev = interface_to_usbdev(udev);
interface = udev->cur_altsetting;
status = -ENOMEM;
@@ -1754,11 +1772,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
skb_queue_head_init (&dev->txq);
skb_queue_head_init (&dev->done);
skb_queue_head_init(&dev->rxq_pause);
+ spin_lock_init(&dev->bql_spinlock);
INIT_WORK(&dev->bh_work, usbnet_bh_work);
- INIT_WORK (&dev->kevent, usbnet_deferred_kevent);
+ INIT_WORK(&dev->kevent, usbnet_deferred_kevent);
init_usb_anchor(&dev->deferred);
timer_setup(&dev->delay, usbnet_bh, 0);
- mutex_init (&dev->phy_mutex);
+ mutex_init(&dev->phy_mutex);
mutex_init(&dev->interrupt_mutex);
dev->interrupt_count = 0;
@@ -1780,7 +1799,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
// allow device-specific bind/init procedures
// NOTE net->name still not usable ...
if (info->bind) {
- status = info->bind (dev, udev);
+ status = info->bind(dev, udev);
if (status < 0)
goto out1;
@@ -1805,18 +1824,18 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
if (net->mtu > (dev->hard_mtu - net->hard_header_len))
net->mtu = dev->hard_mtu - net->hard_header_len;
} else if (!info->in || !info->out)
- status = usbnet_get_endpoints (dev, udev);
+ status = usbnet_get_endpoints(dev, udev);
else {
u8 ep_addrs[3] = {
info->in + USB_DIR_IN, info->out + USB_DIR_OUT, 0
};
- dev->in = usb_rcvbulkpipe (xdev, info->in);
- dev->out = usb_sndbulkpipe (xdev, info->out);
+ dev->in = usb_rcvbulkpipe(xdev, info->in);
+ dev->out = usb_sndbulkpipe(xdev, info->out);
if (!(info->flags & FLAG_NO_SETINT))
- status = usb_set_interface (xdev,
- interface->desc.bInterfaceNumber,
- interface->desc.bAlternateSetting);
+ status = usb_set_interface(xdev,
+ interface->desc.bInterfaceNumber,
+ interface->desc.bAlternateSetting);
else
status = 0;
@@ -1824,7 +1843,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
status = -EINVAL;
}
if (status >= 0 && dev->status)
- status = init_status (dev, udev);
+ status = init_status(dev, udev);
if (status < 0)
goto out3;
@@ -1858,7 +1877,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
}
}
- status = register_netdev (net);
+ status = register_netdev(net);
if (status)
goto out5;
netif_info(dev, probe, dev->net,
@@ -1869,9 +1888,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
net->dev_addr);
// ok, it's ready to go.
- usb_set_intfdata (udev, dev);
+ usb_set_intfdata(udev, dev);
- netif_device_attach (net);
+ netif_device_attach(net);
if (dev->driver_info->flags & FLAG_LINK_INTR)
usbnet_link_change(dev, 0, 0);
@@ -1884,7 +1903,7 @@ out4:
usb_free_urb(dev->interrupt);
out3:
if (info->unbind)
- info->unbind (dev, udev);
+ info->unbind(dev, udev);
out1:
/* subdrivers must undo all they did in bind() if they
* fail it, but we may fail later and a deferred kevent
@@ -1907,7 +1926,7 @@ EXPORT_SYMBOL_GPL(usbnet_probe);
* resume only when the last interface is resumed
*/
-int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
+int usbnet_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
@@ -1926,7 +1945,7 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
* accelerate emptying of the rx and queues, to avoid
* having everything error out.
*/
- netif_device_detach (dev->net);
+ netif_device_detach(dev->net);
usbnet_terminate_urbs(dev);
__usbnet_status_stop_force(dev);
@@ -1934,13 +1953,13 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
* reattach so runtime management can use and
* wake the device
*/
- netif_device_attach (dev->net);
+ netif_device_attach(dev->net);
}
return 0;
}
EXPORT_SYMBOL_GPL(usbnet_suspend);
-int usbnet_resume (struct usb_interface *intf)
+int usbnet_resume(struct usb_interface *intf)
{
struct usbnet *dev = usb_get_intfdata(intf);
struct sk_buff *skb;
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index a3046142cb8e..14e6f2a2fb77 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -392,14 +392,12 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev)
}
/* Restore Eth hdr pulled by dev_forward_skb/eth_type_trans */
__skb_push(skb, ETH_HLEN);
- /* Depend on prior success packets started NAPI consumer via
- * __veth_xdp_flush(). Cancel TXQ stop if consumer stopped,
- * paired with empty check in veth_poll().
- */
netif_tx_stop_queue(txq);
- smp_mb__after_atomic();
- if (unlikely(__ptr_ring_empty(&rq->xdp_ring)))
- netif_tx_wake_queue(txq);
+ /* Makes sure NAPI peer consumer runs. Consumer is responsible
+ * for starting txq again, until then ndo_start_xmit (this
+ * function) will not be invoked by the netstack again.
+ */
+ __veth_xdp_flush(rq);
break;
case NET_RX_DROP: /* same as NET_XMIT_DROP */
drop:
@@ -900,17 +898,9 @@ static int veth_xdp_rcv(struct veth_rq *rq, int budget,
struct veth_xdp_tx_bq *bq,
struct veth_stats *stats)
{
- struct veth_priv *priv = netdev_priv(rq->dev);
- int queue_idx = rq->xdp_rxq.queue_index;
- struct netdev_queue *peer_txq;
- struct net_device *peer_dev;
int i, done = 0, n_xdpf = 0;
void *xdpf[VETH_XDP_BATCH];
- /* NAPI functions as RCU section */
- peer_dev = rcu_dereference_check(priv->peer, rcu_read_lock_bh_held());
- peer_txq = peer_dev ? netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
-
for (i = 0; i < budget; i++) {
void *ptr = __ptr_ring_consume(&rq->xdp_ring);
@@ -959,9 +949,6 @@ static int veth_xdp_rcv(struct veth_rq *rq, int budget,
rq->stats.vs.xdp_packets += done;
u64_stats_update_end(&rq->stats.syncp);
- if (peer_txq && unlikely(netif_tx_queue_stopped(peer_txq)))
- netif_tx_wake_queue(peer_txq);
-
return done;
}
@@ -969,17 +956,28 @@ static int veth_poll(struct napi_struct *napi, int budget)
{
struct veth_rq *rq =
container_of(napi, struct veth_rq, xdp_napi);
+ struct veth_priv *priv = netdev_priv(rq->dev);
+ int queue_idx = rq->xdp_rxq.queue_index;
+ struct netdev_queue *peer_txq;
struct veth_stats stats = {};
+ struct net_device *peer_dev;
struct veth_xdp_tx_bq bq;
int done;
bq.count = 0;
+ /* NAPI functions as RCU section */
+ peer_dev = rcu_dereference_check(priv->peer, rcu_read_lock_bh_held());
+ peer_txq = peer_dev ? netdev_get_tx_queue(peer_dev, queue_idx) : NULL;
+
xdp_set_return_frame_no_direct();
done = veth_xdp_rcv(rq, budget, &bq, &stats);
if (stats.xdp_redirect > 0)
xdp_do_flush();
+ if (stats.xdp_tx > 0)
+ veth_xdp_flush(rq, &bq);
+ xdp_clear_return_frame_no_direct();
if (done < budget && napi_complete_done(napi, done)) {
/* Write rx_notify_masked before reading ptr_ring */
@@ -992,9 +990,12 @@ static int veth_poll(struct napi_struct *napi, int budget)
}
}
- if (stats.xdp_tx > 0)
- veth_xdp_flush(rq, &bq);
- xdp_clear_return_frame_no_direct();
+ /* Release backpressure per NAPI poll */
+ smp_rmb(); /* Paired with netif_tx_stop_queue set_bit */
+ if (peer_txq && netif_tx_queue_stopped(peer_txq)) {
+ txq_trans_cond_update(peer_txq);
+ netif_tx_wake_queue(peer_txq);
+ }
return done;
}
@@ -1323,7 +1324,7 @@ static int veth_set_channels(struct net_device *dev,
if (peer)
netif_carrier_off(peer);
- /* try to allocate new resurces, as needed*/
+ /* try to allocate new resources, as needed*/
err = veth_enable_range_safe(dev, old_rx_count, new_rx_count);
if (err)
goto out;
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index a757cbcab87f..1bb3aeca66c6 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -775,10 +775,26 @@ static bool virtqueue_napi_complete(struct napi_struct *napi,
return false;
}
+static void virtnet_tx_wake_queue(struct virtnet_info *vi,
+ struct send_queue *sq)
+{
+ unsigned int index = vq2txq(sq->vq);
+ struct netdev_queue *txq = netdev_get_tx_queue(vi->dev, index);
+
+ if (netif_tx_queue_stopped(txq)) {
+ u64_stats_update_begin(&sq->stats.syncp);
+ u64_stats_inc(&sq->stats.wake);
+ u64_stats_update_end(&sq->stats.syncp);
+ netif_tx_wake_queue(txq);
+ }
+}
+
static void skb_xmit_done(struct virtqueue *vq)
{
struct virtnet_info *vi = vq->vdev->priv;
- struct napi_struct *napi = &vi->sq[vq2txq(vq)].napi;
+ unsigned int index = vq2txq(vq);
+ struct send_queue *sq = &vi->sq[index];
+ struct napi_struct *napi = &sq->napi;
/* Suppress further interrupts. */
virtqueue_disable_cb(vq);
@@ -786,8 +802,7 @@ static void skb_xmit_done(struct virtqueue *vq)
if (napi->weight)
virtqueue_napi_schedule(napi, vq);
else
- /* We were probably waiting for more output buffers. */
- netif_wake_subqueue(vi->dev, vq2txq(vq));
+ virtnet_tx_wake_queue(vi, sq);
}
#define MRG_CTX_HEADER_SHIFT 22
@@ -910,17 +925,6 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi,
goto ok;
}
- /*
- * Verify that we can indeed put this data into a skb.
- * This is here to handle cases when the device erroneously
- * tries to receive more than is possible. This is usually
- * the case of a broken device.
- */
- if (unlikely(len > MAX_SKB_FRAGS * PAGE_SIZE)) {
- net_dbg_ratelimited("%s: too much data\n", skb->dev->name);
- dev_kfree_skb(skb);
- return NULL;
- }
BUG_ON(offset >= PAGE_SIZE);
while (len) {
unsigned int frag_size = min((unsigned)PAGE_SIZE - offset, len);
@@ -1379,9 +1383,14 @@ static struct sk_buff *virtnet_receive_xsk_merge(struct net_device *dev, struct
ret = XDP_PASS;
rcu_read_lock();
prog = rcu_dereference(rq->xdp_prog);
- /* TODO: support multi buffer. */
- if (prog && num_buf == 1)
- ret = virtnet_xdp_handler(prog, xdp, dev, xdp_xmit, stats);
+ if (prog) {
+ /* TODO: support multi buffer. */
+ if (num_buf == 1)
+ ret = virtnet_xdp_handler(prog, xdp, dev, xdp_xmit,
+ stats);
+ else
+ ret = XDP_ABORTED;
+ }
rcu_read_unlock();
switch (ret) {
@@ -2107,9 +2116,19 @@ static struct sk_buff *receive_big(struct net_device *dev,
struct virtnet_rq_stats *stats)
{
struct page *page = buf;
- struct sk_buff *skb =
- page_to_skb(vi, rq, page, 0, len, PAGE_SIZE, 0);
+ struct sk_buff *skb;
+ /* Make sure that len does not exceed the size allocated in
+ * add_recvbuf_big.
+ */
+ if (unlikely(len > (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE)) {
+ pr_debug("%s: rx error: len %u exceeds allocated size %lu\n",
+ dev->name, len,
+ (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE);
+ goto err;
+ }
+
+ skb = page_to_skb(vi, rq, page, 0, len, PAGE_SIZE, 0);
u64_stats_add(&stats->bytes, len - vi->hdr_len);
if (unlikely(!skb))
goto err;
@@ -2534,6 +2553,13 @@ err_buf:
return NULL;
}
+static inline u32
+virtio_net_hash_value(const struct virtio_net_hdr_v1_hash *hdr_hash)
+{
+ return __le16_to_cpu(hdr_hash->hash_value_lo) |
+ (__le16_to_cpu(hdr_hash->hash_value_hi) << 16);
+}
+
static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash,
struct sk_buff *skb)
{
@@ -2560,7 +2586,7 @@ static void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash,
default:
rss_hash_type = PKT_HASH_TYPE_NONE;
}
- skb_set_hash(skb, __le32_to_cpu(hdr_hash->hash_value), rss_hash_type);
+ skb_set_hash(skb, virtio_net_hash_value(hdr_hash), rss_hash_type);
}
static void virtnet_receive_done(struct virtnet_info *vi, struct receive_queue *rq,
@@ -2620,22 +2646,28 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq,
return;
}
- /* 1. Save the flags early, as the XDP program might overwrite them.
+ /* About the flags below:
+ * 1. Save the flags early, as the XDP program might overwrite them.
* These flags ensure packets marked as VIRTIO_NET_HDR_F_DATA_VALID
* stay valid after XDP processing.
* 2. XDP doesn't work with partially checksummed packets (refer to
* virtnet_xdp_set()), so packets marked as
* VIRTIO_NET_HDR_F_NEEDS_CSUM get dropped during XDP processing.
*/
- flags = ((struct virtio_net_common_hdr *)buf)->hdr.flags;
- if (vi->mergeable_rx_bufs)
+ if (vi->mergeable_rx_bufs) {
+ flags = ((struct virtio_net_common_hdr *)buf)->hdr.flags;
skb = receive_mergeable(dev, vi, rq, buf, ctx, len, xdp_xmit,
stats);
- else if (vi->big_packets)
+ } else if (vi->big_packets) {
+ void *p = page_address((struct page *)buf);
+
+ flags = ((struct virtio_net_common_hdr *)p)->hdr.flags;
skb = receive_big(dev, vi, rq, buf, len, stats);
- else
+ } else {
+ flags = ((struct virtio_net_common_hdr *)buf)->hdr.flags;
skb = receive_small(dev, vi, rq, buf, ctx, len, xdp_xmit, stats);
+ }
if (unlikely(!skb))
return;
@@ -3063,13 +3095,8 @@ static void virtnet_poll_cleantx(struct receive_queue *rq, int budget)
free_old_xmit(sq, txq, !!budget);
} while (unlikely(!virtqueue_enable_cb_delayed(sq->vq)));
- if (sq->vq->num_free >= MAX_SKB_FRAGS + 2 &&
- netif_tx_queue_stopped(txq)) {
- u64_stats_update_begin(&sq->stats.syncp);
- u64_stats_inc(&sq->stats.wake);
- u64_stats_update_end(&sq->stats.syncp);
- netif_tx_wake_queue(txq);
- }
+ if (sq->vq->num_free >= MAX_SKB_FRAGS + 2)
+ virtnet_tx_wake_queue(vi, sq);
__netif_tx_unlock(txq);
}
@@ -3259,13 +3286,8 @@ static int virtnet_poll_tx(struct napi_struct *napi, int budget)
else
free_old_xmit(sq, txq, !!budget);
- if (sq->vq->num_free >= MAX_SKB_FRAGS + 2 &&
- netif_tx_queue_stopped(txq)) {
- u64_stats_update_begin(&sq->stats.syncp);
- u64_stats_inc(&sq->stats.wake);
- u64_stats_update_end(&sq->stats.syncp);
- netif_tx_wake_queue(txq);
- }
+ if (sq->vq->num_free >= MAX_SKB_FRAGS + 2)
+ virtnet_tx_wake_queue(vi, sq);
if (xsk_done >= budget) {
__netif_tx_unlock(txq);
@@ -3306,6 +3328,10 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest);
+ /* Make sure it's safe to cast between formats */
+ BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr));
+ BUILD_BUG_ON(__alignof__(*hdr) != __alignof__(hdr->hash_hdr.hdr));
+
can_push = vi->any_header_sg &&
!((unsigned long)skb->data & (__alignof__(*hdr) - 1)) &&
!skb_header_cloned(skb) && skb_headroom(skb) >= hdr_len;
@@ -3318,7 +3344,8 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb, bool orphan)
hdr = &skb_vnet_common_hdr(skb)->tnl_hdr;
if (virtio_net_hdr_tnl_from_skb(skb, hdr, vi->tx_tnl,
- virtio_is_little_endian(vi->vdev), 0))
+ virtio_is_little_endian(vi->vdev), 0,
+ false))
return -EPROTO;
if (vi->mergeable_rx_bufs)
@@ -3516,6 +3543,9 @@ static void virtnet_tx_pause(struct virtnet_info *vi, struct send_queue *sq)
/* Prevent the upper layer from trying to send packets. */
netif_stop_subqueue(vi->dev, qindex);
+ u64_stats_update_begin(&sq->stats.syncp);
+ u64_stats_inc(&sq->stats.stop);
+ u64_stats_update_end(&sq->stats.syncp);
__netif_tx_unlock_bh(txq);
}
@@ -3532,7 +3562,7 @@ static void virtnet_tx_resume(struct virtnet_info *vi, struct send_queue *sq)
__netif_tx_lock_bh(txq);
sq->reset = false;
- netif_tx_wake_queue(txq);
+ virtnet_tx_wake_queue(vi, sq);
__netif_tx_unlock_bh(txq);
if (running)
@@ -3755,7 +3785,7 @@ static int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs)
* (2) no user configuration.
*
* During rss command processing, device updates queue_pairs using rss.max_tx_vq. That is,
- * the device updates queue_pairs together with rss, so we can skip the sperate queue_pairs
+ * the device updates queue_pairs together with rss, so we can skip the separate queue_pairs
* update (VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET below) and return directly.
*/
if (vi->has_rss && !netif_is_rxfh_configured(dev)) {
@@ -6745,7 +6775,7 @@ static int virtnet_xdp_rx_hash(const struct xdp_md *_ctx, u32 *hash,
hash_report = VIRTIO_NET_HASH_REPORT_NONE;
*rss_type = virtnet_xdp_rss_type[hash_report];
- *hash = __le32_to_cpu(hdr_hash->hash_value);
+ *hash = virtio_net_hash_value(hdr_hash);
return 0;
}
diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c
index cc4d7573839d..a14d0ad978e1 100644
--- a/drivers/net/vmxnet3/vmxnet3_ethtool.c
+++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c
@@ -1081,23 +1081,11 @@ vmxnet3_set_rss_hash_opt(struct net_device *netdev,
return 0;
}
-static int
-vmxnet3_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *info,
- u32 *rules)
+static u32 vmxnet3_get_rx_ring_count(struct net_device *netdev)
{
struct vmxnet3_adapter *adapter = netdev_priv(netdev);
- int err = 0;
-
- switch (info->cmd) {
- case ETHTOOL_GRXRINGS:
- info->data = adapter->num_rx_queues;
- break;
- default:
- err = -EOPNOTSUPP;
- break;
- }
- return err;
+ return adapter->num_rx_queues;
}
#ifdef VMXNET3_RSS
@@ -1335,7 +1323,7 @@ static const struct ethtool_ops vmxnet3_ethtool_ops = {
.get_ethtool_stats = vmxnet3_get_ethtool_stats,
.get_ringparam = vmxnet3_get_ringparam,
.set_ringparam = vmxnet3_set_ringparam,
- .get_rxnfc = vmxnet3_get_rxnfc,
+ .get_rx_ring_count = vmxnet3_get_rx_ring_count,
#ifdef VMXNET3_RSS
.get_rxfh_indir_size = vmxnet3_get_rss_indir_size,
.get_rxfh = vmxnet3_get_rss,
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index a5c55e7e4d79..e957aa12a8a4 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2349,7 +2349,7 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
int addr_family;
__u8 tos, ttl;
int ifindex;
- int err;
+ int err = 0;
u32 flags = vxlan->cfg.flags;
bool use_cache;
bool udp_sum = false;
@@ -2454,12 +2454,18 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
rcu_read_lock();
if (addr_family == AF_INET) {
- struct vxlan_sock *sock4 = rcu_dereference(vxlan->vn4_sock);
+ struct vxlan_sock *sock4;
u16 ipcb_flags = 0;
struct rtable *rt;
__be16 df = 0;
__be32 saddr;
+ sock4 = rcu_dereference(vxlan->vn4_sock);
+ if (unlikely(!sock4)) {
+ reason = SKB_DROP_REASON_DEV_READY;
+ goto tx_error;
+ }
+
if (!ifindex)
ifindex = sock4->sock->sk->sk_bound_dev_if;
@@ -2534,10 +2540,16 @@ void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
ipcb_flags);
#if IS_ENABLED(CONFIG_IPV6)
} else {
- struct vxlan_sock *sock6 = rcu_dereference(vxlan->vn6_sock);
+ struct vxlan_sock *sock6;
struct in6_addr saddr;
u16 ip6cb_flags = 0;
+ sock6 = rcu_dereference(vxlan->vn6_sock);
+ if (unlikely(!sock6)) {
+ reason = SKB_DROP_REASON_DEV_READY;
+ goto tx_error;
+ }
+
if (!ifindex)
ifindex = sock6->sock->sk->sk_bound_dev_if;
diff --git a/drivers/net/vxlan/vxlan_private.h b/drivers/net/vxlan/vxlan_private.h
index 99fe772ad679..b1eec2216360 100644
--- a/drivers/net/vxlan/vxlan_private.h
+++ b/drivers/net/vxlan/vxlan_private.h
@@ -188,8 +188,6 @@ int __vxlan_fdb_delete(struct vxlan_dev *vxlan,
const unsigned char *addr, union vxlan_addr ip,
__be16 port, __be32 src_vni, __be32 vni,
u32 ifindex, bool swdev_notify);
-u32 eth_vni_hash(const unsigned char *addr, __be32 vni);
-u32 fdb_head_index(struct vxlan_dev *vxlan, const u8 *mac, __be32 vni);
int vxlan_fdb_update(struct vxlan_dev *vxlan,
const u8 *mac, union vxlan_addr *ip,
__u16 state, __u16 flags,
diff --git a/drivers/net/wan/framer/pef2256/pef2256.c b/drivers/net/wan/framer/pef2256/pef2256.c
index c5501826db1e..c058cc79137d 100644
--- a/drivers/net/wan/framer/pef2256/pef2256.c
+++ b/drivers/net/wan/framer/pef2256/pef2256.c
@@ -648,7 +648,8 @@ static int pef2256_add_audio_devices(struct pef2256 *pef2256)
audio_devs[i].id = i;
}
- ret = mfd_add_devices(pef2256->dev, 0, audio_devs, count, NULL, 0, NULL);
+ ret = devm_mfd_add_devices(pef2256->dev, 0, audio_devs, count,
+ NULL, 0, NULL);
kfree(audio_devs);
return ret;
}
@@ -822,8 +823,8 @@ static int pef2256_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pef2256);
- ret = mfd_add_devices(pef2256->dev, 0, pef2256_devs,
- ARRAY_SIZE(pef2256_devs), NULL, 0, NULL);
+ ret = devm_mfd_add_devices(pef2256->dev, 0, pef2256_devs,
+ ARRAY_SIZE(pef2256_devs), NULL, 0, NULL);
if (ret) {
dev_err(pef2256->dev, "add devices failed (%d)\n", ret);
return ret;
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 7496a2e9a282..159295c4bd6d 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -126,14 +126,12 @@ static inline struct proto *get_proto(struct net_device *dev, u16 pid)
static inline const char *proto_name(u16 pid)
{
switch (pid) {
- case PID_LCP:
- return "LCP";
case PID_IPCP:
return "IPCP";
case PID_IPV6CP:
return "IPV6CP";
default:
- return NULL;
+ return "LCP";
}
}
diff --git a/drivers/net/wireguard/Makefile b/drivers/net/wireguard/Makefile
index dbe1f8514efc..00cbcc9ab69d 100644
--- a/drivers/net/wireguard/Makefile
+++ b/drivers/net/wireguard/Makefile
@@ -13,5 +13,5 @@ wireguard-y += peerlookup.o
wireguard-y += allowedips.o
wireguard-y += ratelimiter.o
wireguard-y += cookie.o
-wireguard-y += netlink.o
+wireguard-y += netlink.o generated/netlink.o
obj-$(CONFIG_WIREGUARD) := wireguard.o
diff --git a/drivers/net/wireguard/cookie.c b/drivers/net/wireguard/cookie.c
index 94d0a7206084..08731b3fa32b 100644
--- a/drivers/net/wireguard/cookie.c
+++ b/drivers/net/wireguard/cookie.c
@@ -33,7 +33,7 @@ static void precompute_key(u8 key[NOISE_SYMMETRIC_KEY_LEN],
const u8 pubkey[NOISE_PUBLIC_KEY_LEN],
const u8 label[COOKIE_KEY_LABEL_LEN])
{
- struct blake2s_state blake;
+ struct blake2s_ctx blake;
blake2s_init(&blake, NOISE_SYMMETRIC_KEY_LEN);
blake2s_update(&blake, label, COOKIE_KEY_LABEL_LEN);
@@ -77,7 +77,7 @@ static void compute_mac1(u8 mac1[COOKIE_LEN], const void *message, size_t len,
{
len = len - sizeof(struct message_macs) +
offsetof(struct message_macs, mac1);
- blake2s(mac1, message, key, COOKIE_LEN, len, NOISE_SYMMETRIC_KEY_LEN);
+ blake2s(key, NOISE_SYMMETRIC_KEY_LEN, message, len, mac1, COOKIE_LEN);
}
static void compute_mac2(u8 mac2[COOKIE_LEN], const void *message, size_t len,
@@ -85,13 +85,13 @@ static void compute_mac2(u8 mac2[COOKIE_LEN], const void *message, size_t len,
{
len = len - sizeof(struct message_macs) +
offsetof(struct message_macs, mac2);
- blake2s(mac2, message, cookie, COOKIE_LEN, len, COOKIE_LEN);
+ blake2s(cookie, COOKIE_LEN, message, len, mac2, COOKIE_LEN);
}
static void make_cookie(u8 cookie[COOKIE_LEN], struct sk_buff *skb,
struct cookie_checker *checker)
{
- struct blake2s_state state;
+ struct blake2s_ctx blake;
if (wg_birthdate_has_expired(checker->secret_birthdate,
COOKIE_SECRET_MAX_AGE)) {
@@ -103,15 +103,15 @@ static void make_cookie(u8 cookie[COOKIE_LEN], struct sk_buff *skb,
down_read(&checker->secret_lock);
- blake2s_init_key(&state, COOKIE_LEN, checker->secret, NOISE_HASH_LEN);
+ blake2s_init_key(&blake, COOKIE_LEN, checker->secret, NOISE_HASH_LEN);
if (skb->protocol == htons(ETH_P_IP))
- blake2s_update(&state, (u8 *)&ip_hdr(skb)->saddr,
+ blake2s_update(&blake, (u8 *)&ip_hdr(skb)->saddr,
sizeof(struct in_addr));
else if (skb->protocol == htons(ETH_P_IPV6))
- blake2s_update(&state, (u8 *)&ipv6_hdr(skb)->saddr,
+ blake2s_update(&blake, (u8 *)&ipv6_hdr(skb)->saddr,
sizeof(struct in6_addr));
- blake2s_update(&state, (u8 *)&udp_hdr(skb)->source, sizeof(__be16));
- blake2s_final(&state, cookie);
+ blake2s_update(&blake, (u8 *)&udp_hdr(skb)->source, sizeof(__be16));
+ blake2s_final(&blake, cookie);
up_read(&checker->secret_lock);
}
diff --git a/drivers/net/wireguard/generated/netlink.c b/drivers/net/wireguard/generated/netlink.c
new file mode 100644
index 000000000000..3ef8c29908c2
--- /dev/null
+++ b/drivers/net/wireguard/generated/netlink.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/wireguard.yaml */
+/* YNL-GEN kernel source */
+/* YNL-ARG --function-prefix wg */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include "netlink.h"
+
+#include <uapi/linux/wireguard.h>
+#include <linux/time_types.h>
+
+/* Common nested types */
+const struct nla_policy wireguard_wgallowedip_nl_policy[WGALLOWEDIP_A_FLAGS + 1] = {
+ [WGALLOWEDIP_A_FAMILY] = { .type = NLA_U16, },
+ [WGALLOWEDIP_A_IPADDR] = NLA_POLICY_MIN_LEN(4),
+ [WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8, },
+ [WGALLOWEDIP_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, 0x1),
+};
+
+const struct nla_policy wireguard_wgpeer_nl_policy[WGPEER_A_PROTOCOL_VERSION + 1] = {
+ [WGPEER_A_PUBLIC_KEY] = NLA_POLICY_EXACT_LEN(WG_KEY_LEN),
+ [WGPEER_A_PRESHARED_KEY] = NLA_POLICY_EXACT_LEN(WG_KEY_LEN),
+ [WGPEER_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, 0x7),
+ [WGPEER_A_ENDPOINT] = NLA_POLICY_MIN_LEN(16),
+ [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NLA_U16, },
+ [WGPEER_A_LAST_HANDSHAKE_TIME] = NLA_POLICY_EXACT_LEN(16),
+ [WGPEER_A_RX_BYTES] = { .type = NLA_U64, },
+ [WGPEER_A_TX_BYTES] = { .type = NLA_U64, },
+ [WGPEER_A_ALLOWEDIPS] = NLA_POLICY_NESTED_ARRAY(wireguard_wgallowedip_nl_policy),
+ [WGPEER_A_PROTOCOL_VERSION] = { .type = NLA_U32, },
+};
+
+/* WG_CMD_GET_DEVICE - dump */
+static const struct nla_policy wireguard_get_device_nl_policy[WGDEVICE_A_IFNAME + 1] = {
+ [WGDEVICE_A_IFINDEX] = { .type = NLA_U32, },
+ [WGDEVICE_A_IFNAME] = { .type = NLA_NUL_STRING, .len = 15, },
+};
+
+/* WG_CMD_SET_DEVICE - do */
+static const struct nla_policy wireguard_set_device_nl_policy[WGDEVICE_A_PEERS + 1] = {
+ [WGDEVICE_A_IFINDEX] = { .type = NLA_U32, },
+ [WGDEVICE_A_IFNAME] = { .type = NLA_NUL_STRING, .len = 15, },
+ [WGDEVICE_A_PRIVATE_KEY] = NLA_POLICY_EXACT_LEN(WG_KEY_LEN),
+ [WGDEVICE_A_PUBLIC_KEY] = NLA_POLICY_EXACT_LEN(WG_KEY_LEN),
+ [WGDEVICE_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, 0x1),
+ [WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16, },
+ [WGDEVICE_A_FWMARK] = { .type = NLA_U32, },
+ [WGDEVICE_A_PEERS] = NLA_POLICY_NESTED_ARRAY(wireguard_wgpeer_nl_policy),
+};
+
+/* Ops table for wireguard */
+const struct genl_split_ops wireguard_nl_ops[2] = {
+ {
+ .cmd = WG_CMD_GET_DEVICE,
+ .start = wg_get_device_start,
+ .dumpit = wg_get_device_dumpit,
+ .done = wg_get_device_done,
+ .policy = wireguard_get_device_nl_policy,
+ .maxattr = WGDEVICE_A_IFNAME,
+ .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DUMP,
+ },
+ {
+ .cmd = WG_CMD_SET_DEVICE,
+ .doit = wg_set_device_doit,
+ .policy = wireguard_set_device_nl_policy,
+ .maxattr = WGDEVICE_A_PEERS,
+ .flags = GENL_UNS_ADMIN_PERM | GENL_CMD_CAP_DO,
+ },
+};
diff --git a/drivers/net/wireguard/generated/netlink.h b/drivers/net/wireguard/generated/netlink.h
new file mode 100644
index 000000000000..5dc977ee9e7c
--- /dev/null
+++ b/drivers/net/wireguard/generated/netlink.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
+/* Do not edit directly, auto-generated from: */
+/* Documentation/netlink/specs/wireguard.yaml */
+/* YNL-GEN kernel header */
+/* YNL-ARG --function-prefix wg */
+/* To regenerate run: tools/net/ynl/ynl-regen.sh */
+
+#ifndef _LINUX_WIREGUARD_GEN_H
+#define _LINUX_WIREGUARD_GEN_H
+
+#include <net/netlink.h>
+#include <net/genetlink.h>
+
+#include <uapi/linux/wireguard.h>
+#include <linux/time_types.h>
+
+/* Common nested types */
+extern const struct nla_policy wireguard_wgallowedip_nl_policy[WGALLOWEDIP_A_FLAGS + 1];
+extern const struct nla_policy wireguard_wgpeer_nl_policy[WGPEER_A_PROTOCOL_VERSION + 1];
+
+/* Ops table for wireguard */
+extern const struct genl_split_ops wireguard_nl_ops[2];
+
+int wg_get_device_start(struct netlink_callback *cb);
+int wg_get_device_done(struct netlink_callback *cb);
+
+int wg_get_device_dumpit(struct sk_buff *skb, struct netlink_callback *cb);
+int wg_set_device_doit(struct sk_buff *skb, struct genl_info *info);
+
+#endif /* _LINUX_WIREGUARD_GEN_H */
diff --git a/drivers/net/wireguard/netlink.c b/drivers/net/wireguard/netlink.c
index 67f962eb8b46..1da7e98d0d50 100644
--- a/drivers/net/wireguard/netlink.c
+++ b/drivers/net/wireguard/netlink.c
@@ -9,6 +9,7 @@
#include "socket.h"
#include "queueing.h"
#include "messages.h"
+#include "generated/netlink.h"
#include <uapi/linux/wireguard.h>
@@ -19,37 +20,6 @@
static struct genl_family genl_family;
-static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = {
- [WGDEVICE_A_IFINDEX] = { .type = NLA_U32 },
- [WGDEVICE_A_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
- [WGDEVICE_A_PRIVATE_KEY] = NLA_POLICY_EXACT_LEN(NOISE_PUBLIC_KEY_LEN),
- [WGDEVICE_A_PUBLIC_KEY] = NLA_POLICY_EXACT_LEN(NOISE_PUBLIC_KEY_LEN),
- [WGDEVICE_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, __WGDEVICE_F_ALL),
- [WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16 },
- [WGDEVICE_A_FWMARK] = { .type = NLA_U32 },
- [WGDEVICE_A_PEERS] = { .type = NLA_NESTED }
-};
-
-static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = {
- [WGPEER_A_PUBLIC_KEY] = NLA_POLICY_EXACT_LEN(NOISE_PUBLIC_KEY_LEN),
- [WGPEER_A_PRESHARED_KEY] = NLA_POLICY_EXACT_LEN(NOISE_SYMMETRIC_KEY_LEN),
- [WGPEER_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, __WGPEER_F_ALL),
- [WGPEER_A_ENDPOINT] = NLA_POLICY_MIN_LEN(sizeof(struct sockaddr)),
- [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NLA_U16 },
- [WGPEER_A_LAST_HANDSHAKE_TIME] = NLA_POLICY_EXACT_LEN(sizeof(struct __kernel_timespec)),
- [WGPEER_A_RX_BYTES] = { .type = NLA_U64 },
- [WGPEER_A_TX_BYTES] = { .type = NLA_U64 },
- [WGPEER_A_ALLOWEDIPS] = { .type = NLA_NESTED },
- [WGPEER_A_PROTOCOL_VERSION] = { .type = NLA_U32 }
-};
-
-static const struct nla_policy allowedip_policy[WGALLOWEDIP_A_MAX + 1] = {
- [WGALLOWEDIP_A_FAMILY] = { .type = NLA_U16 },
- [WGALLOWEDIP_A_IPADDR] = NLA_POLICY_MIN_LEN(sizeof(struct in_addr)),
- [WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8 },
- [WGALLOWEDIP_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, __WGALLOWEDIP_F_ALL),
-};
-
static struct wg_device *lookup_interface(struct nlattr **attrs,
struct sk_buff *skb)
{
@@ -197,7 +167,7 @@ err:
return -EMSGSIZE;
}
-static int wg_get_device_start(struct netlink_callback *cb)
+int wg_get_device_start(struct netlink_callback *cb)
{
struct wg_device *wg;
@@ -208,7 +178,7 @@ static int wg_get_device_start(struct netlink_callback *cb)
return 0;
}
-static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
+int wg_get_device_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
{
struct wg_peer *peer, *next_peer_cursor;
struct dump_ctx *ctx = DUMP_CTX(cb);
@@ -302,7 +272,7 @@ out:
*/
}
-static int wg_get_device_done(struct netlink_callback *cb)
+int wg_get_device_done(struct netlink_callback *cb)
{
struct dump_ctx *ctx = DUMP_CTX(cb);
@@ -467,7 +437,7 @@ static int set_peer(struct wg_device *wg, struct nlattr **attrs)
nla_for_each_nested(attr, attrs[WGPEER_A_ALLOWEDIPS], rem) {
ret = nla_parse_nested(allowedip, WGALLOWEDIP_A_MAX,
- attr, allowedip_policy, NULL);
+ attr, NULL, NULL);
if (ret < 0)
goto out;
ret = set_allowedip(peer, allowedip);
@@ -500,7 +470,7 @@ out:
return ret;
}
-static int wg_set_device(struct sk_buff *skb, struct genl_info *info)
+int wg_set_device_doit(struct sk_buff *skb, struct genl_info *info)
{
struct wg_device *wg = lookup_interface(info->attrs, skb);
u32 flags = 0;
@@ -593,7 +563,7 @@ skip_set_private_key:
nla_for_each_nested(attr, info->attrs[WGDEVICE_A_PEERS], rem) {
ret = nla_parse_nested(peer, WGPEER_A_MAX, attr,
- peer_policy, NULL);
+ NULL, NULL);
if (ret < 0)
goto out;
ret = set_peer(wg, peer);
@@ -614,34 +584,20 @@ out_nodev:
return ret;
}
-static const struct genl_ops genl_ops[] = {
- {
- .cmd = WG_CMD_GET_DEVICE,
- .start = wg_get_device_start,
- .dumpit = wg_get_device_dump,
- .done = wg_get_device_done,
- .flags = GENL_UNS_ADMIN_PERM
- }, {
- .cmd = WG_CMD_SET_DEVICE,
- .doit = wg_set_device,
- .flags = GENL_UNS_ADMIN_PERM
- }
-};
-
static struct genl_family genl_family __ro_after_init = {
- .ops = genl_ops,
- .n_ops = ARRAY_SIZE(genl_ops),
- .resv_start_op = WG_CMD_SET_DEVICE + 1,
+ .split_ops = wireguard_nl_ops,
+ .n_split_ops = ARRAY_SIZE(wireguard_nl_ops),
.name = WG_GENL_NAME,
.version = WG_GENL_VERSION,
- .maxattr = WGDEVICE_A_MAX,
.module = THIS_MODULE,
- .policy = device_policy,
.netnsok = true
};
int __init wg_genetlink_init(void)
{
+ BUILD_BUG_ON(WG_KEY_LEN != NOISE_PUBLIC_KEY_LEN);
+ BUILD_BUG_ON(WG_KEY_LEN != NOISE_SYMMETRIC_KEY_LEN);
+
return genl_register_family(&genl_family);
}
diff --git a/drivers/net/wireguard/noise.c b/drivers/net/wireguard/noise.c
index 7eb9a23a3d4d..1fe8468f0bef 100644
--- a/drivers/net/wireguard/noise.c
+++ b/drivers/net/wireguard/noise.c
@@ -33,10 +33,10 @@ static atomic64_t keypair_counter = ATOMIC64_INIT(0);
void __init wg_noise_init(void)
{
- struct blake2s_state blake;
+ struct blake2s_ctx blake;
- blake2s(handshake_init_chaining_key, handshake_name, NULL,
- NOISE_HASH_LEN, sizeof(handshake_name), 0);
+ blake2s(NULL, 0, handshake_name, sizeof(handshake_name),
+ handshake_init_chaining_key, NOISE_HASH_LEN);
blake2s_init(&blake, NOISE_HASH_LEN);
blake2s_update(&blake, handshake_init_chaining_key, NOISE_HASH_LEN);
blake2s_update(&blake, identifier_name, sizeof(identifier_name));
@@ -304,33 +304,33 @@ void wg_noise_set_static_identity_private_key(
static void hmac(u8 *out, const u8 *in, const u8 *key, const size_t inlen, const size_t keylen)
{
- struct blake2s_state state;
+ struct blake2s_ctx blake;
u8 x_key[BLAKE2S_BLOCK_SIZE] __aligned(__alignof__(u32)) = { 0 };
u8 i_hash[BLAKE2S_HASH_SIZE] __aligned(__alignof__(u32));
int i;
if (keylen > BLAKE2S_BLOCK_SIZE) {
- blake2s_init(&state, BLAKE2S_HASH_SIZE);
- blake2s_update(&state, key, keylen);
- blake2s_final(&state, x_key);
+ blake2s_init(&blake, BLAKE2S_HASH_SIZE);
+ blake2s_update(&blake, key, keylen);
+ blake2s_final(&blake, x_key);
} else
memcpy(x_key, key, keylen);
for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i)
x_key[i] ^= 0x36;
- blake2s_init(&state, BLAKE2S_HASH_SIZE);
- blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE);
- blake2s_update(&state, in, inlen);
- blake2s_final(&state, i_hash);
+ blake2s_init(&blake, BLAKE2S_HASH_SIZE);
+ blake2s_update(&blake, x_key, BLAKE2S_BLOCK_SIZE);
+ blake2s_update(&blake, in, inlen);
+ blake2s_final(&blake, i_hash);
for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i)
x_key[i] ^= 0x5c ^ 0x36;
- blake2s_init(&state, BLAKE2S_HASH_SIZE);
- blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE);
- blake2s_update(&state, i_hash, BLAKE2S_HASH_SIZE);
- blake2s_final(&state, i_hash);
+ blake2s_init(&blake, BLAKE2S_HASH_SIZE);
+ blake2s_update(&blake, x_key, BLAKE2S_BLOCK_SIZE);
+ blake2s_update(&blake, i_hash, BLAKE2S_HASH_SIZE);
+ blake2s_final(&blake, i_hash);
memcpy(out, i_hash, BLAKE2S_HASH_SIZE);
memzero_explicit(x_key, BLAKE2S_BLOCK_SIZE);
@@ -431,7 +431,7 @@ static bool __must_check mix_precomputed_dh(u8 chaining_key[NOISE_HASH_LEN],
static void mix_hash(u8 hash[NOISE_HASH_LEN], const u8 *src, size_t src_len)
{
- struct blake2s_state blake;
+ struct blake2s_ctx blake;
blake2s_init(&blake, NOISE_HASH_LEN);
blake2s_update(&blake, hash, NOISE_HASH_LEN);
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 6f78f1752cd6..7c2939cbde5f 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@@ -1187,7 +1186,7 @@ static int ath10k_download_fw(struct ath10k *ar)
u32 address, data_len;
const void *data;
int ret;
- struct pm_qos_request latency_qos;
+ struct pm_qos_request latency_qos = {};
address = ar->hw_params.patch_load_addr;
@@ -1221,7 +1220,6 @@ static int ath10k_download_fw(struct ath10k *ar)
ret);
}
- memset(&latency_qos, 0, sizeof(latency_qos));
cpu_latency_qos_add_request(&latency_qos, 0);
ret = ath10k_bmi_fast_download(ar, address, data, data_len);
@@ -2493,8 +2491,9 @@ static int ath10k_init_hw_params(struct ath10k *ar)
return 0;
}
-static bool ath10k_core_needs_recovery(struct ath10k *ar)
+static void ath10k_core_recovery_check_work(struct work_struct *work)
{
+ struct ath10k *ar = container_of(work, struct ath10k, recovery_check_work);
long time_left;
/* Sometimes the recovery will fail and then the next all recovery fail,
@@ -2504,7 +2503,7 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
ath10k_err(ar, "consecutive fail %d times, will shutdown driver!",
atomic_read(&ar->fail_cont_count));
ar->state = ATH10K_STATE_WEDGED;
- return false;
+ return;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "total recovery count: %d", ++ar->recovery_count);
@@ -2518,27 +2517,24 @@ static bool ath10k_core_needs_recovery(struct ath10k *ar)
ATH10K_RECOVERY_TIMEOUT_HZ);
if (time_left) {
ath10k_warn(ar, "previous recovery succeeded, skip this!\n");
- return false;
+ return;
}
/* Record the continuous recovery fail count when recovery failed. */
atomic_inc(&ar->fail_cont_count);
/* Avoid having multiple recoveries at the same time. */
- return false;
+ return;
}
atomic_inc(&ar->pending_recovery);
-
- return true;
+ queue_work(ar->workqueue, &ar->restart_work);
}
void ath10k_core_start_recovery(struct ath10k *ar)
{
- if (!ath10k_core_needs_recovery(ar))
- return;
-
- queue_work(ar->workqueue, &ar->restart_work);
+ /* Use workqueue_aux to avoid blocking recovery tracking */
+ queue_work(ar->workqueue_aux, &ar->recovery_check_work);
}
EXPORT_SYMBOL(ath10k_core_start_recovery);
@@ -3356,7 +3352,7 @@ EXPORT_SYMBOL(ath10k_core_stop);
*/
static int ath10k_core_probe_fw(struct ath10k *ar)
{
- struct bmi_target_info target_info;
+ struct bmi_target_info target_info = {};
int ret = 0;
ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_NORMAL);
@@ -3367,7 +3363,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
switch (ar->hif.bus) {
case ATH10K_BUS_SDIO:
- memset(&target_info, 0, sizeof(target_info));
ret = ath10k_bmi_get_target_info_sdio(ar, &target_info);
if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3379,7 +3374,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
case ATH10K_BUS_PCI:
case ATH10K_BUS_AHB:
case ATH10K_BUS_USB:
- memset(&target_info, 0, sizeof(target_info));
ret = ath10k_bmi_get_target_info(ar, &target_info);
if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3389,7 +3383,6 @@ static int ath10k_core_probe_fw(struct ath10k *ar)
ar->hw->wiphy->hw_version = target_info.version;
break;
case ATH10K_BUS_SNOC:
- memset(&target_info, 0, sizeof(target_info));
ret = ath10k_hif_get_target_info(ar, &target_info);
if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret);
@@ -3734,6 +3727,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
INIT_WORK(&ar->register_work, ath10k_core_register_work);
INIT_WORK(&ar->restart_work, ath10k_core_restart);
+ INIT_WORK(&ar->recovery_check_work, ath10k_core_recovery_check_work);
INIT_WORK(&ar->set_coverage_class_work,
ath10k_core_set_coverage_class_work);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 8c72ed386edb..73a9db302245 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@@ -1208,6 +1207,7 @@ struct ath10k {
struct work_struct register_work;
struct work_struct restart_work;
+ struct work_struct recovery_check_work;
struct work_struct bundle_tx_work;
struct work_struct tx_complete_work;
@@ -1259,9 +1259,13 @@ struct ath10k {
struct {
/* protected by conf_mutex */
struct ath10k_fw_components utf_mode_fw;
+ u8 ftm_msgref;
/* protected by data_lock */
bool utf_monitor;
+ u32 data_pos;
+ u32 expected_seq;
+ u8 *eventdata;
} testmode;
struct {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 154ac7a70982..da6f7957a0ae 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@@ -5428,6 +5427,7 @@ static void ath10k_stop(struct ieee80211_hw *hw, bool suspend)
cancel_work_sync(&ar->set_coverage_class_work);
cancel_delayed_work_sync(&ar->scan.timeout);
cancel_work_sync(&ar->restart_work);
+ cancel_work_sync(&ar->recovery_check_work);
}
static int ath10k_config_ps(struct ath10k *ar)
diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c
index f1f33af0170a..8275345631a0 100644
--- a/drivers/net/wireless/ath/ath10k/qmi.c
+++ b/drivers/net/wireless/ath/ath10k/qmi.c
@@ -986,7 +986,7 @@ static int ath10k_qmi_new_server(struct qmi_handle *qmi_hdl,
ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service found\n");
- ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)&qmi->sq,
+ ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)&qmi->sq,
sizeof(qmi->sq), 0);
if (ret) {
ath10k_err(ar, "failed to connect to a remote QMI service port\n");
diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c
index 3fcefc55b74f..d3bd385694d6 100644
--- a/drivers/net/wireless/ath/ath10k/testmode.c
+++ b/drivers/net/wireless/ath/ath10k/testmode.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: ISC
/*
* Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "testmode.h"
@@ -10,12 +11,17 @@
#include "debug.h"
#include "wmi.h"
+#include "wmi-tlv.h"
#include "hif.h"
#include "hw.h"
#include "core.h"
#include "testmode_i.h"
+#define ATH10K_FTM_SEG_NONE ((u32)-1)
+#define ATH10K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0)
+#define ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4)
+
static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
[ATH10K_TM_ATTR_CMD] = { .type = NLA_U32 },
[ATH10K_TM_ATTR_DATA] = { .type = NLA_BINARY,
@@ -25,41 +31,19 @@ static const struct nla_policy ath10k_tm_policy[ATH10K_TM_ATTR_MAX + 1] = {
[ATH10K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 },
};
-/* Returns true if callee consumes the skb and the skb should be discarded.
- * Returns false if skb is not used. Does not sleep.
- */
-bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+static void ath10k_tm_event_unsegmented(struct ath10k *ar, u32 cmd_id,
+ struct sk_buff *skb)
{
struct sk_buff *nl_skb;
- bool consumed;
int ret;
- ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
- "testmode event wmi cmd_id %d skb %p skb->len %d\n",
- cmd_id, skb, skb->len);
-
- ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
-
- spin_lock_bh(&ar->data_lock);
-
- if (!ar->testmode.utf_monitor) {
- consumed = false;
- goto out;
- }
-
- /* Only testmode.c should be handling events from utf firmware,
- * otherwise all sort of problems will arise as mac80211 operations
- * are not initialised.
- */
- consumed = true;
-
nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
2 * sizeof(u32) + skb->len,
GFP_ATOMIC);
if (!nl_skb) {
ath10k_warn(ar,
"failed to allocate skb for testmode wmi event\n");
- goto out;
+ return;
}
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_WMI);
@@ -68,7 +52,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
"failed to put testmode wmi event cmd attribute: %d\n",
ret);
kfree_skb(nl_skb);
- goto out;
+ return;
}
ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
@@ -77,7 +61,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
"failed to put testmode wmi event cmd_id: %d\n",
ret);
kfree_skb(nl_skb);
- goto out;
+ return;
}
ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, skb->len, skb->data);
@@ -86,10 +70,122 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
"failed to copy skb to testmode wmi event: %d\n",
ret);
kfree_skb(nl_skb);
- goto out;
+ return;
+ }
+
+ cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+static void ath10k_tm_event_segmented(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+{
+ struct wmi_ftm_cmd *ftm = (struct wmi_ftm_cmd *)skb->data;
+ u8 total_segments, current_seq;
+ struct sk_buff *nl_skb;
+ u8 const *buf_pos;
+ u16 datalen;
+ u32 data_pos;
+ int ret;
+
+ if (skb->len < sizeof(*ftm)) {
+ ath10k_warn(ar, "Invalid ftm event length: %d\n", skb->len);
+ return;
+ }
+
+ current_seq = FIELD_GET(ATH10K_FTM_SEGHDR_CURRENT_SEQ,
+ __le32_to_cpu(ftm->seg_hdr.segmentinfo));
+ total_segments = FIELD_GET(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS,
+ __le32_to_cpu(ftm->seg_hdr.segmentinfo));
+ datalen = skb->len - sizeof(*ftm);
+ buf_pos = ftm->data;
+
+ if (current_seq == 0) {
+ ar->testmode.expected_seq = 0;
+ ar->testmode.data_pos = 0;
+ }
+
+ data_pos = ar->testmode.data_pos;
+
+ if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) {
+ ath10k_warn(ar, "Invalid ftm event length at %u: %u\n",
+ data_pos, datalen);
+ ret = -EINVAL;
+ return;
+ }
+
+ memcpy(&ar->testmode.eventdata[data_pos], buf_pos, datalen);
+ data_pos += datalen;
+
+ if (++ar->testmode.expected_seq != total_segments) {
+ ar->testmode.data_pos = data_pos;
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "partial data received %u/%u\n",
+ current_seq + 1, total_segments);
+ return;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE, "total data length %u\n", data_pos);
+
+ nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy,
+ 2 * sizeof(u32) + data_pos,
+ GFP_ATOMIC);
+ if (!nl_skb) {
+ ath10k_warn(ar, "failed to allocate skb for testmode wmi event\n");
+ return;
+ }
+
+ ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_CMD, ATH10K_TM_CMD_TLV);
+ if (ret) {
+ ath10k_warn(ar, "failed to put testmode wmi event attribute: %d\n", ret);
+ kfree_skb(nl_skb);
+ return;
+ }
+
+ ret = nla_put_u32(nl_skb, ATH10K_TM_ATTR_WMI_CMDID, cmd_id);
+ if (ret) {
+ ath10k_warn(ar, "failed to put testmode wmi event cmd_id: %d\n", ret);
+ kfree_skb(nl_skb);
+ return;
+ }
+
+ ret = nla_put(nl_skb, ATH10K_TM_ATTR_DATA, data_pos, &ar->testmode.eventdata[0]);
+ if (ret) {
+ ath10k_warn(ar, "failed to copy skb to testmode wmi event: %d\n", ret);
+ kfree_skb(nl_skb);
+ return;
}
cfg80211_testmode_event(nl_skb, GFP_ATOMIC);
+}
+
+/* Returns true if callee consumes the skb and the skb should be discarded.
+ * Returns false if skb is not used. Does not sleep.
+ */
+bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb)
+{
+ bool consumed;
+
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+ "testmode event wmi cmd_id %d skb %p skb->len %d\n",
+ cmd_id, skb, skb->len);
+
+ ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len);
+
+ spin_lock_bh(&ar->data_lock);
+
+ if (!ar->testmode.utf_monitor) {
+ consumed = false;
+ goto out;
+ }
+
+ /* Only testmode.c should be handling events from utf firmware,
+ * otherwise all sort of problems will arise as mac80211 operations
+ * are not initialised.
+ */
+ consumed = true;
+
+ if (ar->testmode.expected_seq != ATH10K_FTM_SEG_NONE)
+ ath10k_tm_event_segmented(ar, cmd_id, skb);
+ else
+ ath10k_tm_event_unsegmented(ar, cmd_id, skb);
out:
spin_unlock_bh(&ar->data_lock);
@@ -281,12 +377,18 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
goto err_release_utf_mode_fw;
}
+ ar->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL);
+ if (!ar->testmode.eventdata) {
+ ret = -ENOMEM;
+ goto err_power_down;
+ }
+
ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_UTF,
&ar->testmode.utf_mode_fw);
if (ret) {
ath10k_err(ar, "failed to start core (testmode): %d\n", ret);
ar->state = ATH10K_STATE_OFF;
- goto err_power_down;
+ goto err_release_eventdata;
}
ar->state = ATH10K_STATE_UTF;
@@ -302,6 +404,10 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[])
return 0;
+err_release_eventdata:
+ kfree(ar->testmode.eventdata);
+ ar->testmode.eventdata = NULL;
+
err_power_down:
ath10k_hif_power_down(ar);
@@ -341,6 +447,9 @@ static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar)
release_firmware(ar->testmode.utf_mode_fw.fw_file.firmware);
ar->testmode.utf_mode_fw.fw_file.firmware = NULL;
+ kfree(ar->testmode.eventdata);
+ ar->testmode.eventdata = NULL;
+
ar->state = ATH10K_STATE_OFF;
}
@@ -424,6 +533,85 @@ out:
return ret;
}
+static int ath10k_tm_cmd_tlv(struct ath10k *ar, struct nlattr *tb[])
+{
+ u16 total_bytes, num_segments;
+ u32 cmd_id, buf_len;
+ u8 segnumber = 0;
+ u8 *bufpos;
+ void *buf;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_UTF) {
+ ret = -ENETDOWN;
+ goto out;
+ }
+
+ buf = nla_data(tb[ATH10K_TM_ATTR_DATA]);
+ buf_len = nla_len(tb[ATH10K_TM_ATTR_DATA]);
+ cmd_id = WMI_PDEV_UTF_CMDID;
+
+ ath10k_dbg(ar, ATH10K_DBG_TESTMODE,
+ "cmd wmi ftm cmd_id %d buffer length %d\n",
+ cmd_id, buf_len);
+ ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len);
+
+ bufpos = buf;
+ total_bytes = buf_len;
+ num_segments = total_bytes / MAX_WMI_UTF_LEN;
+ ar->testmode.expected_seq = 0;
+
+ if (buf_len - (num_segments * MAX_WMI_UTF_LEN))
+ num_segments++;
+
+ while (buf_len) {
+ u16 chunk_len = min_t(u16, buf_len, MAX_WMI_UTF_LEN);
+ struct wmi_ftm_cmd *ftm_cmd;
+ struct sk_buff *skb;
+ u32 hdr_info;
+ u8 seginfo;
+
+ skb = ath10k_wmi_alloc_skb(ar, (chunk_len +
+ sizeof(struct wmi_ftm_cmd)));
+ if (!skb) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ftm_cmd = (struct wmi_ftm_cmd *)skb->data;
+ hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TLV_TAG_ARRAY_BYTE) |
+ FIELD_PREP(WMI_TLV_LEN, (chunk_len +
+ sizeof(struct wmi_ftm_seg_hdr)));
+ ftm_cmd->tlv_header = __cpu_to_le32(hdr_info);
+ ftm_cmd->seg_hdr.len = __cpu_to_le32(total_bytes);
+ ftm_cmd->seg_hdr.msgref = __cpu_to_le32(ar->testmode.ftm_msgref);
+ seginfo = FIELD_PREP(ATH10K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) |
+ FIELD_PREP(ATH10K_FTM_SEGHDR_CURRENT_SEQ, segnumber);
+ ftm_cmd->seg_hdr.segmentinfo = __cpu_to_le32(seginfo);
+ segnumber++;
+
+ memcpy(&ftm_cmd->data, bufpos, chunk_len);
+
+ ret = ath10k_wmi_cmd_send(ar, skb, cmd_id);
+ if (ret) {
+ ath10k_warn(ar, "failed to send wmi ftm command: %d\n", ret);
+ goto out;
+ }
+
+ buf_len -= chunk_len;
+ bufpos += chunk_len;
+ }
+
+ ar->testmode.ftm_msgref++;
+ ret = 0;
+
+out:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
@@ -439,9 +627,14 @@ int ath10k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (!tb[ATH10K_TM_ATTR_CMD])
return -EINVAL;
+ ar->testmode.expected_seq = ATH10K_FTM_SEG_NONE;
+
switch (nla_get_u32(tb[ATH10K_TM_ATTR_CMD])) {
case ATH10K_TM_CMD_GET_VERSION:
- return ath10k_tm_cmd_get_version(ar, tb);
+ if (!tb[ATH10K_TM_ATTR_DATA])
+ return ath10k_tm_cmd_get_version(ar, tb);
+ else /* ATH10K_TM_CMD_TLV */
+ return ath10k_tm_cmd_tlv(ar, tb);
case ATH10K_TM_CMD_UTF_START:
return ath10k_tm_cmd_utf_start(ar, tb);
case ATH10K_TM_CMD_UTF_STOP:
diff --git a/drivers/net/wireless/ath/ath10k/testmode_i.h b/drivers/net/wireless/ath/ath10k/testmode_i.h
index ee1cb27c1d60..1603f5276682 100644
--- a/drivers/net/wireless/ath/ath10k/testmode_i.h
+++ b/drivers/net/wireless/ath/ath10k/testmode_i.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: ISC */
/*
* Copyright (c) 2014,2017 Qualcomm Atheros, Inc.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
/* "API" level of the ath10k testmode interface. Bump it after every
@@ -14,6 +15,7 @@
#define ATH10K_TESTMODE_VERSION_MINOR 0
#define ATH10K_TM_DATA_MAX_LEN 5000
+#define ATH_FTM_EVENT_MAX_BUF_LENGTH 2048
enum ath10k_tm_attr {
__ATH10K_TM_ATTR_INVALID = 0,
@@ -57,4 +59,17 @@ enum ath10k_tm_cmd {
* ATH10K_TM_ATTR_DATA.
*/
ATH10K_TM_CMD_WMI = 3,
+
+ /* The command used to transmit a test command to the firmware
+ * and the event to receive test events from the firmware. The data
+ * received only contain the TLV payload, need to add the tlv header
+ * and send the cmd to firmware with command id WMI_PDEV_UTF_CMDID.
+ * The data payload size could be large and the driver needs to
+ * send segmented data to firmware.
+ *
+ * This legacy testmode command shares the same value as the get-version
+ * command. To distinguish between them, we check whether the data attribute
+ * is present.
+ */
+ ATH10K_TM_CMD_TLV = ATH10K_TM_CMD_GET_VERSION,
};
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index e595b0979a56..b4aad6604d6d 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -1764,32 +1764,33 @@ void ath10k_wmi_put_wmi_channel(struct ath10k *ar, struct wmi_channel *ch,
int ath10k_wmi_wait_for_service_ready(struct ath10k *ar)
{
- unsigned long timeout = jiffies + WMI_SERVICE_READY_TIMEOUT_HZ;
unsigned long time_left, i;
- /* Sometimes the PCI HIF doesn't receive interrupt
- * for the service ready message even if the buffer
- * was completed. PCIe sniffer shows that it's
- * because the corresponding CE ring doesn't fires
- * it. Workaround here by polling CE rings. Since
- * the message could arrive at any time, continue
- * polling until timeout.
- */
- do {
+ time_left = wait_for_completion_timeout(&ar->wmi.service_ready,
+ WMI_SERVICE_READY_TIMEOUT_HZ);
+ if (!time_left) {
+ /* Sometimes the PCI HIF doesn't receive interrupt
+ * for the service ready message even if the buffer
+ * was completed. PCIe sniffer shows that it's
+ * because the corresponding CE ring doesn't fires
+ * it. Workaround here by polling CE rings once.
+ */
+ ath10k_warn(ar, "failed to receive service ready completion, polling..\n");
+
for (i = 0; i < CE_COUNT; i++)
ath10k_hif_send_complete_check(ar, i, 1);
- /* The 100 ms granularity is a tradeoff considering scheduler
- * overhead and response latency
- */
time_left = wait_for_completion_timeout(&ar->wmi.service_ready,
- msecs_to_jiffies(100));
- if (time_left)
- return 0;
- } while (time_before(jiffies, timeout));
+ WMI_SERVICE_READY_TIMEOUT_HZ);
+ if (!time_left) {
+ ath10k_warn(ar, "polling timed out\n");
+ return -ETIMEDOUT;
+ }
- ath10k_warn(ar, "failed to receive service ready completion\n");
- return -ETIMEDOUT;
+ ath10k_warn(ar, "service ready completion received, continuing normally\n");
+ }
+
+ return 0;
}
int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar)
@@ -1937,6 +1938,7 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
if (cmd_id == WMI_CMD_UNSUPPORTED) {
ath10k_warn(ar, "wmi command %d is not supported by firmware\n",
cmd_id);
+ dev_kfree_skb_any(skb);
return ret;
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 0faefc0a9a40..7f50a1de6b97 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -3,7 +3,7 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _WMI_H_
@@ -7418,6 +7418,23 @@ struct wmi_pdev_bb_timing_cfg_cmd {
__le32 bb_xpa_timing;
} __packed;
+struct wmi_ftm_seg_hdr {
+ __le32 len;
+ __le32 msgref;
+ __le32 segmentinfo;
+ __le32 pdev_id;
+} __packed;
+
+struct wmi_ftm_cmd {
+ __le32 tlv_header;
+ struct wmi_ftm_seg_hdr seg_hdr;
+ u8 data[];
+} __packed;
+
+#define WMI_TLV_LEN GENMASK(15, 0)
+#define WMI_TLV_TAG GENMASK(31, 16)
+#define MAX_WMI_UTF_LEN 252
+
struct ath10k;
struct ath10k_vif;
struct ath10k_fw_stats_pdev;
diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
index 2810752260f2..812686173ac8 100644
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -912,42 +912,84 @@ static const struct ath11k_hw_params ath11k_hw_params[] = {
static const struct dmi_system_id ath11k_pm_quirk_table[] = {
{
.driver_data = (void *)ATH11K_PM_WOW,
- .matches = {
+ .matches = { /* X13 G4 AMD #1 */
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21J3"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = { /* X13 G4 AMD #2 */
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21J4"),
},
},
{
.driver_data = (void *)ATH11K_PM_WOW,
- .matches = {
+ .matches = { /* T14 G4 AMD #1 */
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21K3"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = { /* T14 G4 AMD #2 */
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21K4"),
},
},
{
.driver_data = (void *)ATH11K_PM_WOW,
- .matches = {
+ .matches = { /* P14s G4 AMD #1 */
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21K5"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = { /* P14s G4 AMD #2 */
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21K6"),
},
},
{
.driver_data = (void *)ATH11K_PM_WOW,
- .matches = {
+ .matches = { /* T16 G2 AMD #1 */
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21K7"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = { /* T16 G2 AMD #2 */
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21K8"),
},
},
{
.driver_data = (void *)ATH11K_PM_WOW,
- .matches = {
+ .matches = { /* P16s G2 AMD #1 */
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21K9"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = { /* P16s G2 AMD #2 */
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21KA"),
},
},
{
.driver_data = (void *)ATH11K_PM_WOW,
- .matches = {
+ .matches = { /* T14s G4 AMD #1 */
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "21F8"),
+ },
+ },
+ {
+ .driver_data = (void *)ATH11K_PM_WOW,
+ .matches = { /* T14s G4 AMD #2 */
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "21F9"),
},
diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h
index 839095af9267..82603a389bb9 100644
--- a/drivers/net/wireless/ath/ath11k/hal.h
+++ b/drivers/net/wireless/ath/ath11k/hal.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_HAL_H
@@ -43,14 +43,14 @@ struct ath11k_base;
#define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000
#define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000
#define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000
-#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce0_src_reg)
-#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce0_dst_reg)
-#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce1_src_reg)
-#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(x) \
- (ab->hw_params.regs->hal_seq_wcss_umac_ce1_dst_reg)
+#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce0_src_reg)
+#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce0_dst_reg)
+#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce1_src_reg)
+#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) \
+ ((ab)->hw_params.regs->hal_seq_wcss_umac_ce1_dst_reg)
#define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000
#define HAL_CE_WFSS_CE_REG_BASE 0x01b80000
@@ -209,10 +209,10 @@ struct ath11k_base;
#define HAL_REO_STATUS_HP(ab) ab->hw_params.regs->hal_reo_status_hp
/* WBM Idle R0 address */
-#define HAL_WBM_IDLE_LINK_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm_idle_link_ring_base_lsb)
-#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR(x) \
- (ab->hw_params.regs->hal_wbm_idle_link_ring_misc)
+#define HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm_idle_link_ring_base_lsb)
+#define HAL_WBM_IDLE_LINK_RING_MISC_ADDR(ab) \
+ ((ab)->hw_params.regs->hal_wbm_idle_link_ring_misc)
#define HAL_WBM_R0_IDLE_LIST_CONTROL_ADDR 0x00000048
#define HAL_WBM_R0_IDLE_LIST_SIZE_ADDR 0x0000004c
#define HAL_WBM_SCATTERED_RING_BASE_LSB 0x00000058
@@ -227,17 +227,17 @@ struct ath11k_base;
#define HAL_WBM_IDLE_LINK_RING_HP 0x000030b0
/* SW2WBM R0 release address */
-#define HAL_WBM_RELEASE_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm_release_ring_base_lsb)
+#define HAL_WBM_RELEASE_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm_release_ring_base_lsb)
/* SW2WBM R2 release address */
#define HAL_WBM_RELEASE_RING_HP 0x00003018
/* WBM2SW R0 release address */
-#define HAL_WBM0_RELEASE_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm0_release_ring_base_lsb)
-#define HAL_WBM1_RELEASE_RING_BASE_LSB(x) \
- (ab->hw_params.regs->hal_wbm1_release_ring_base_lsb)
+#define HAL_WBM0_RELEASE_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm0_release_ring_base_lsb)
+#define HAL_WBM1_RELEASE_RING_BASE_LSB(ab) \
+ ((ab)->hw_params.regs->hal_wbm1_release_ring_base_lsb)
/* WBM2SW R2 release address */
#define HAL_WBM0_RELEASE_RING_HP 0x000030c0
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 106e2530b64e..3276fe443502 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <net/mac80211.h>
@@ -2235,9 +2235,9 @@ static void ath11k_peer_assoc_h_vht(struct ath11k *ar,
arg->peer_nss = min(sta->deflink.rx_nss, max_nss);
arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
+ arg->rx_mcs_set = ath11k_peer_assoc_h_vht_limit(arg->rx_mcs_set, vht_mcs_mask);
arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
- arg->tx_mcs_set = ath11k_peer_assoc_h_vht_limit(
- __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map), vht_mcs_mask);
+ arg->tx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
/* In IPQ8074 platform, VHT mcs rate 10 and 11 is enabled by default.
* VHT mcs rate 10 and 11 is not supported in 11ac standard.
@@ -2522,10 +2522,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar,
he_tx_mcs = v;
}
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160);
- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
arg->peer_he_mcs_count++;
@@ -2535,10 +2535,10 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar,
default:
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
+ v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80);
- v = ath11k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
arg->peer_he_mcs_count++;
@@ -4028,6 +4028,150 @@ static int ath11k_start_scan(struct ath11k *ar,
return 0;
}
+static void ath11k_mac_fw_stats_reset(struct ath11k *ar)
+{
+ spin_lock_bh(&ar->data_lock);
+ ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
+ ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
+ ar->fw_stats.num_vdev_recvd = 0;
+ ar->fw_stats.num_bcn_recvd = 0;
+ spin_unlock_bh(&ar->data_lock);
+}
+
+int ath11k_mac_fw_stats_request(struct ath11k *ar,
+ struct stats_request_params *req_param)
+{
+ struct ath11k_base *ab = ar->ab;
+ unsigned long time_left;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ ath11k_mac_fw_stats_reset(ar);
+
+ reinit_completion(&ar->fw_stats_complete);
+ reinit_completion(&ar->fw_stats_done);
+
+ ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
+
+ if (ret) {
+ ath11k_warn(ab, "could not request fw stats (%d)\n",
+ ret);
+ return ret;
+ }
+
+ time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ /* FW stats can get split when exceeding the stats data buffer limit.
+ * In that case, since there is no end marking for the back-to-back
+ * received 'update stats' event, we keep a 3 seconds timeout in case,
+ * fw_stats_done is not marked yet
+ */
+ time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int ath11k_mac_get_fw_stats(struct ath11k *ar, u32 pdev_id,
+ u32 vdev_id, u32 stats_id)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct stats_request_params req_param;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (ar->state != ATH11K_STATE_ON)
+ return -ENETDOWN;
+
+ req_param.pdev_id = pdev_id;
+ req_param.vdev_id = vdev_id;
+ req_param.stats_id = stats_id;
+
+ ret = ath11k_mac_fw_stats_request(ar, &req_param);
+ if (ret)
+ ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
+ pdev_id, vdev_id, stats_id);
+
+ return ret;
+}
+
+static int ath11k_mac_handle_get_txpower(struct ath11k *ar,
+ struct ieee80211_vif *vif,
+ int *dbm)
+{
+ struct ath11k_base *ab = ar->ab;
+ struct ath11k_fw_stats_pdev *pdev;
+ int ret;
+
+ /* Final Tx power is minimum of Target Power, CTL power, Regulatory
+ * Power, PSD EIRP Power. We just know the Regulatory power from the
+ * regulatory rules obtained. FW knows all these power and sets the min
+ * of these. Hence, we request the FW pdev stats in which FW reports
+ * the minimum of all vdev's channel Tx power.
+ */
+ lockdep_assert_held(&ar->conf_mutex);
+
+ /* Firmware doesn't provide Tx power during CAC hence no need to fetch
+ * the stats.
+ */
+ if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags))
+ return -EAGAIN;
+
+ ret = ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
+ WMI_REQUEST_PDEV_STAT);
+ if (ret) {
+ ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
+ goto err_fallback;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+ pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
+ struct ath11k_fw_stats_pdev, list);
+ if (!pdev) {
+ spin_unlock_bh(&ar->data_lock);
+ goto err_fallback;
+ }
+
+ /* tx power is set as 2 units per dBm in FW. */
+ *dbm = pdev->chan_tx_power / 2;
+
+ spin_unlock_bh(&ar->data_lock);
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware %d, reported %d dBm\n",
+ pdev->chan_tx_power, *dbm);
+ return 0;
+
+err_fallback:
+ /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
+ *dbm = vif->bss_conf.txpower;
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
+ *dbm);
+ return 0;
+}
+
+static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ unsigned int link_id,
+ int *dbm)
+{
+ struct ath11k *ar = hw->priv;
+ int ret;
+
+ mutex_lock(&ar->conf_mutex);
+ ret = ath11k_mac_handle_get_txpower(ar, vif, dbm);
+ mutex_unlock(&ar->conf_mutex);
+
+ return ret;
+}
+
static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req)
@@ -4417,9 +4561,9 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
- flags |= WMI_KEY_PAIRWISE;
+ flags = WMI_KEY_PAIRWISE;
else
- flags |= WMI_KEY_GROUP;
+ flags = WMI_KEY_GROUP;
ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
"%s for peer %pM on vdev %d flags 0x%X, type = %d, num_sta %d\n",
@@ -4456,7 +4600,7 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
is_ap_with_no_sta = (vif->type == NL80211_IFTYPE_AP &&
!arvif->num_stations);
- if ((flags & WMI_KEY_PAIRWISE) || cmd == SET_KEY || is_ap_with_no_sta) {
+ if (flags == WMI_KEY_PAIRWISE || cmd == SET_KEY || is_ap_with_no_sta) {
ret = ath11k_install_key(arvif, key, cmd, peer_addr, flags);
if (ret) {
ath11k_warn(ab, "ath11k_install_key failed (%d)\n", ret);
@@ -4470,7 +4614,7 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
goto exit;
}
- if ((flags & WMI_KEY_GROUP) && cmd == SET_KEY && is_ap_with_no_sta)
+ if (flags == WMI_KEY_GROUP && cmd == SET_KEY && is_ap_with_no_sta)
arvif->reinstall_group_keys = true;
}
@@ -6107,6 +6251,159 @@ static void ath11k_mgmt_over_wmi_tx_purge(struct ath11k *ar)
ath11k_mgmt_over_wmi_tx_drop(ar, skb);
}
+static int ath11k_mac_mgmt_action_frame_fill_elem_data(struct ath11k_vif *arvif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ u8 category, *buf, iv_len, action_code, dialog_token;
+ int cur_tx_power, max_tx_power;
+ struct ath11k *ar = arvif->ar;
+ struct cfg80211_chan_def def;
+ struct ath11k_skb_cb *skb_cb;
+ struct ieee80211_mgmt *mgmt;
+ unsigned int remaining_len;
+ bool has_protected;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ /* make sure category field is present */
+ if (skb->len < IEEE80211_MIN_ACTION_SIZE)
+ return -EINVAL;
+
+ remaining_len = skb->len - IEEE80211_MIN_ACTION_SIZE;
+ has_protected = ieee80211_has_protected(hdr->frame_control);
+
+ /* In case of SW crypto and hdr protected (PMF), packet will already be encrypted,
+ * we can't put in data in this case
+ */
+ if (test_bit(ATH11K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) &&
+ has_protected)
+ return 0;
+
+ mgmt = (struct ieee80211_mgmt *)hdr;
+ buf = (u8 *)&mgmt->u.action;
+
+ /* FCTL_PROTECTED frame might have extra space added for HDR_LEN. Offset that
+ * many bytes if it is there
+ */
+ if (has_protected) {
+ skb_cb = ATH11K_SKB_CB(skb);
+
+ switch (skb_cb->cipher) {
+ /* Cipher suite having flag %IEEE80211_KEY_FLAG_GENERATE_IV_MGMT set in
+ * key needs to be processed. See ath11k_install_key()
+ */
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP_256:
+ case WLAN_CIPHER_SUITE_GCMP:
+ case WLAN_CIPHER_SUITE_GCMP_256:
+ iv_len = IEEE80211_CCMP_HDR_LEN;
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ iv_len = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (remaining_len < iv_len)
+ return -EINVAL;
+
+ buf += iv_len;
+ remaining_len -= iv_len;
+ }
+
+ category = *buf++;
+ /* category code is already taken care in %IEEE80211_MIN_ACTION_SIZE hence
+ * no need to adjust remaining_len
+ */
+
+ switch (category) {
+ case WLAN_CATEGORY_RADIO_MEASUREMENT:
+ /* need action code and dialog token */
+ if (remaining_len < 2)
+ return -EINVAL;
+
+ /* Packet Format:
+ * Action Code | Dialog Token | Variable Len (based on Action Code)
+ */
+ action_code = *buf++;
+ dialog_token = *buf++;
+ remaining_len -= 2;
+
+ if (ath11k_mac_vif_chan(arvif->vif, &def))
+ return -ENOENT;
+
+ cur_tx_power = arvif->vif->bss_conf.txpower;
+ max_tx_power = min(def.chan->max_reg_power, (int)ar->max_tx_power / 2);
+ ath11k_mac_handle_get_txpower(ar, arvif->vif, &cur_tx_power);
+
+ switch (action_code) {
+ case WLAN_RM_ACTION_LINK_MEASUREMENT_REQUEST:
+ /* need variable fields to be present in len */
+ if (remaining_len < 2)
+ return -EINVAL;
+
+ /* Variable length format as defined in IEEE 802.11-2024,
+ * Figure 9-1187-Link Measurement Request frame Action field
+ * format.
+ * Transmit Power | Max Tx Power
+ * We fill both of these.
+ */
+ *buf++ = cur_tx_power;
+ *buf = max_tx_power;
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "RRM: Link Measurement Req dialog_token %u cur_tx_power %d max_tx_power %d\n",
+ dialog_token, cur_tx_power, max_tx_power);
+ break;
+ case WLAN_RM_ACTION_LINK_MEASUREMENT_REPORT:
+ /* need variable fields to be present in len */
+ if (remaining_len < 3)
+ return -EINVAL;
+
+ /* Variable length format as defined in IEEE 802.11-2024,
+ * Figure 9-1188-Link Measurement Report frame Action field format
+ * TPC Report | Variable Fields
+ *
+ * TPC Report Format:
+ * Element ID | Len | Tx Power | Link Margin
+ *
+ * We fill Tx power in the TPC Report (2nd index)
+ */
+ buf[2] = cur_tx_power;
+
+ /* TODO: At present, Link margin data is not present so can't
+ * really fill it now. Once it is available, it can be added
+ * here
+ */
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "RRM: Link Measurement Report dialog_token %u cur_tx_power %d\n",
+ dialog_token, cur_tx_power);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ /* nothing to fill */
+ return 0;
+ }
+
+ return 0;
+}
+
+static int ath11k_mac_mgmt_frame_fill_elem_data(struct ath11k_vif *arvif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+
+ if (!ieee80211_is_action(hdr->frame_control))
+ return 0;
+
+ return ath11k_mac_mgmt_action_frame_fill_elem_data(arvif, skb);
+}
+
static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work)
{
struct ath11k *ar = container_of(work, struct ath11k, wmi_mgmt_tx_work);
@@ -6126,6 +6423,19 @@ static void ath11k_mgmt_over_wmi_tx_work(struct work_struct *work)
arvif = ath11k_vif_to_arvif(skb_cb->vif);
mutex_lock(&ar->conf_mutex);
if (ar->allocated_vdev_map & (1LL << arvif->vdev_id)) {
+ /* Fill in the data which is required to be filled by the driver
+ * For example: Max Tx power in Link Measurement Request/Report
+ */
+ ret = ath11k_mac_mgmt_frame_fill_elem_data(arvif, skb);
+ if (ret) {
+ /* If we couldn't fill the data due to any reason,
+ * let's not discard transmitting the packet.
+ */
+ ath11k_dbg(ar->ab, ATH11K_DBG_MAC,
+ "Failed to fill the required data for the mgmt packet err %d\n",
+ ret);
+ }
+
ret = ath11k_mac_mgmt_tx_wmi(ar, arvif, skb);
if (ret) {
ath11k_warn(ar->ab, "failed to tx mgmt frame, vdev_id %d :%d\n",
@@ -9079,81 +9389,6 @@ static void ath11k_mac_put_chain_rssi(struct station_info *sinfo,
}
}
-static void ath11k_mac_fw_stats_reset(struct ath11k *ar)
-{
- spin_lock_bh(&ar->data_lock);
- ath11k_fw_stats_pdevs_free(&ar->fw_stats.pdevs);
- ath11k_fw_stats_vdevs_free(&ar->fw_stats.vdevs);
- ar->fw_stats.num_vdev_recvd = 0;
- ar->fw_stats.num_bcn_recvd = 0;
- spin_unlock_bh(&ar->data_lock);
-}
-
-int ath11k_mac_fw_stats_request(struct ath11k *ar,
- struct stats_request_params *req_param)
-{
- struct ath11k_base *ab = ar->ab;
- unsigned long time_left;
- int ret;
-
- lockdep_assert_held(&ar->conf_mutex);
-
- ath11k_mac_fw_stats_reset(ar);
-
- reinit_completion(&ar->fw_stats_complete);
- reinit_completion(&ar->fw_stats_done);
-
- ret = ath11k_wmi_send_stats_request_cmd(ar, req_param);
-
- if (ret) {
- ath11k_warn(ab, "could not request fw stats (%d)\n",
- ret);
- return ret;
- }
-
- time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ);
- if (!time_left)
- return -ETIMEDOUT;
-
- /* FW stats can get split when exceeding the stats data buffer limit.
- * In that case, since there is no end marking for the back-to-back
- * received 'update stats' event, we keep a 3 seconds timeout in case,
- * fw_stats_done is not marked yet
- */
- time_left = wait_for_completion_timeout(&ar->fw_stats_done, 3 * HZ);
- if (!time_left)
- return -ETIMEDOUT;
-
- return 0;
-}
-
-static int ath11k_mac_get_fw_stats(struct ath11k *ar, u32 pdev_id,
- u32 vdev_id, u32 stats_id)
-{
- struct ath11k_base *ab = ar->ab;
- struct stats_request_params req_param;
- int ret;
-
- lockdep_assert_held(&ar->conf_mutex);
-
- if (ar->state != ATH11K_STATE_ON)
- return -ENETDOWN;
-
- req_param.pdev_id = pdev_id;
- req_param.vdev_id = vdev_id;
- req_param.stats_id = stats_id;
-
- ret = ath11k_mac_fw_stats_request(ar, &req_param);
- if (ret)
- ath11k_warn(ab, "failed to request fw stats: %d\n", ret);
-
- ath11k_dbg(ab, ATH11K_DBG_WMI,
- "debug get fw stat pdev id %d vdev id %d stats id 0x%x\n",
- pdev_id, vdev_id, stats_id);
-
- return ret;
-}
-
static void ath11k_mac_op_sta_statistics(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -9539,66 +9774,6 @@ exit:
return ret;
}
-static int ath11k_mac_op_get_txpower(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- unsigned int link_id,
- int *dbm)
-{
- struct ath11k *ar = hw->priv;
- struct ath11k_base *ab = ar->ab;
- struct ath11k_fw_stats_pdev *pdev;
- int ret;
-
- /* Final Tx power is minimum of Target Power, CTL power, Regulatory
- * Power, PSD EIRP Power. We just know the Regulatory power from the
- * regulatory rules obtained. FW knows all these power and sets the min
- * of these. Hence, we request the FW pdev stats in which FW reports
- * the minimum of all vdev's channel Tx power.
- */
- mutex_lock(&ar->conf_mutex);
-
- /* Firmware doesn't provide Tx power during CAC hence no need to fetch
- * the stats.
- */
- if (test_bit(ATH11K_CAC_RUNNING, &ar->dev_flags)) {
- mutex_unlock(&ar->conf_mutex);
- return -EAGAIN;
- }
-
- ret = ath11k_mac_get_fw_stats(ar, ar->pdev->pdev_id, 0,
- WMI_REQUEST_PDEV_STAT);
- if (ret) {
- ath11k_warn(ab, "failed to request fw pdev stats: %d\n", ret);
- goto err_fallback;
- }
-
- spin_lock_bh(&ar->data_lock);
- pdev = list_first_entry_or_null(&ar->fw_stats.pdevs,
- struct ath11k_fw_stats_pdev, list);
- if (!pdev) {
- spin_unlock_bh(&ar->data_lock);
- goto err_fallback;
- }
-
- /* tx power is set as 2 units per dBm in FW. */
- *dbm = pdev->chan_tx_power / 2;
-
- spin_unlock_bh(&ar->data_lock);
- mutex_unlock(&ar->conf_mutex);
-
- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware %d, reported %d dBm\n",
- pdev->chan_tx_power, *dbm);
- return 0;
-
-err_fallback:
- mutex_unlock(&ar->conf_mutex);
- /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */
- *dbm = vif->bss_conf.txpower;
- ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n",
- *dbm);
- return 0;
-}
-
static int ath11k_mac_station_add(struct ath11k *ar,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
@@ -10368,6 +10543,8 @@ static int __ath11k_mac_register(struct ath11k *ar)
ar->hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
NL80211_FEATURE_AP_SCAN;
+ ar->hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
+
ar->max_num_stations = TARGET_NUM_STATIONS(ab);
ar->max_num_peers = TARGET_NUM_PEERS_PDEV(ab);
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index d8655badd96d..7114eca8810d 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/module.h>
@@ -177,6 +177,19 @@ static inline void ath11k_pci_select_static_window(struct ath11k_pci *ab_pci)
ab_pci->ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
}
+static void ath11k_pci_restore_window(struct ath11k_base *ab)
+{
+ struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
+
+ spin_lock_bh(&ab_pci->window_lock);
+
+ iowrite32(ATH11K_PCI_WINDOW_ENABLE_BIT | ab_pci->register_window,
+ ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
+ ioread32(ab->mem + ATH11K_PCI_WINDOW_REG_ADDRESS);
+
+ spin_unlock_bh(&ab_pci->window_lock);
+}
+
static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
{
u32 val, delay;
@@ -201,6 +214,11 @@ static void ath11k_pci_soc_global_reset(struct ath11k_base *ab)
val = ath11k_pcic_read32(ab, PCIE_SOC_GLOBAL_RESET);
if (val == 0xffffffff)
ath11k_warn(ab, "link down error during global reset\n");
+
+ /* Restore window register as its content is cleared during
+ * hardware global reset, such that it aligns with host cache.
+ */
+ ath11k_pci_restore_window(ab);
}
static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab)
diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h
index c33c7865145c..1e3005a4b64c 100644
--- a/drivers/net/wireless/ath/ath11k/pci.h
+++ b/drivers/net/wireless/ath/ath11k/pci.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022,2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef _ATH11K_PCI_H
#define _ATH11K_PCI_H
@@ -35,18 +35,18 @@
#define PCIE_SMLH_REQ_RST_LINK_DOWN 0x2
#define PCIE_INT_CLEAR_ALL 0xffffffff
-#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(x) \
- (ab->hw_params.regs->pcie_qserdes_sysclk_en_sel)
+#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG(ab) \
+ ((ab)->hw_params.regs->pcie_qserdes_sysclk_en_sel)
#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL 0x10
#define PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK 0xffffffff
-#define PCIE_PCS_OSC_DTCT_CONFIG1_REG(x) \
- (ab->hw_params.regs->pcie_pcs_osc_dtct_config_base)
+#define PCIE_PCS_OSC_DTCT_CONFIG1_REG(ab) \
+ ((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base)
#define PCIE_PCS_OSC_DTCT_CONFIG1_VAL 0x02
-#define PCIE_PCS_OSC_DTCT_CONFIG2_REG(x) \
- (ab->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0x4)
+#define PCIE_PCS_OSC_DTCT_CONFIG2_REG(ab) \
+ ((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0x4)
#define PCIE_PCS_OSC_DTCT_CONFIG2_VAL 0x52
-#define PCIE_PCS_OSC_DTCT_CONFIG4_REG(x) \
- (ab->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0xc)
+#define PCIE_PCS_OSC_DTCT_CONFIG4_REG(ab) \
+ ((ab)->hw_params.regs->pcie_pcs_osc_dtct_config_base + 0xc)
#define PCIE_PCS_OSC_DTCT_CONFIG4_VAL 0xff
#define PCIE_PCS_OSC_DTCT_CONFIG_MSK 0x000000ff
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index aea56c38bf8f..ff6a97e328b8 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -3177,7 +3177,7 @@ static int ath11k_qmi_ops_new_server(struct qmi_handle *qmi_hdl,
sq->sq_node = service->node;
sq->sq_port = service->port;
- ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)sq,
+ ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)sq,
sizeof(*sq), 0);
if (ret) {
ath11k_warn(ab, "failed to connect to qmi remote service: %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 0491e3fd6b5e..110035dae8a6 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/skbuff.h>
#include <linux/ctype.h>
@@ -2061,10 +2061,13 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
cmd->peer_bw_rxnss_override |= param->peer_bw_rxnss_override;
if (param->vht_capable) {
- mcs->rx_max_rate = param->rx_max_rate;
- mcs->rx_mcs_set = param->rx_mcs_set;
- mcs->tx_max_rate = param->tx_max_rate;
- mcs->tx_mcs_set = param->tx_mcs_set;
+ /* firmware interprets mcs->tx_mcs_set field as peer's
+ * RX capability
+ */
+ mcs->tx_max_rate = param->rx_max_rate;
+ mcs->tx_mcs_set = param->rx_mcs_set;
+ mcs->rx_max_rate = param->tx_max_rate;
+ mcs->rx_mcs_set = param->tx_mcs_set;
}
/* HE Rates */
@@ -2088,8 +2091,11 @@ int ath11k_wmi_send_peer_assoc_cmd(struct ath11k *ar,
FIELD_PREP(WMI_TLV_LEN,
sizeof(*he_mcs) - TLV_HDR_SIZE);
- he_mcs->rx_mcs_set = param->peer_he_tx_mcs_set[i];
- he_mcs->tx_mcs_set = param->peer_he_rx_mcs_set[i];
+ /* firmware interprets mcs->rx_mcs_set field as peer's
+ * RX capability
+ */
+ he_mcs->rx_mcs_set = param->peer_he_rx_mcs_set[i];
+ he_mcs->tx_mcs_set = param->peer_he_tx_mcs_set[i];
ptr += sizeof(*he_mcs);
}
@@ -5961,6 +5967,9 @@ static int wmi_process_mgmt_tx_comp(struct ath11k *ar,
dma_unmap_single(ar->ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
info = IEEE80211_SKB_CB(msdu);
+ memset(&info->status, 0, sizeof(info->status));
+ info->status.rates[0].idx = -1;
+
if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) &&
!tx_compl_param->status) {
info->flags |= IEEE80211_TX_STAT_ACK;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 9fcffaa2f383..0f0de24a3840 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH11K_WMI_H
@@ -3463,20 +3463,6 @@ struct scan_cancel_param {
u32 pdev_id;
};
-struct wmi_bcn_send_from_host_cmd {
- u32 tlv_header;
- u32 vdev_id;
- u32 data_len;
- union {
- u32 frag_ptr;
- u32 frag_ptr_lo;
- };
- u32 frame_ctrl;
- u32 dtim_flag;
- u32 bcn_antenna;
- u32 frag_ptr_hi;
-};
-
#define WMI_CHAN_INFO_MODE GENMASK(5, 0)
#define WMI_CHAN_INFO_HT40_PLUS BIT(6)
#define WMI_CHAN_INFO_PASSIVE BIT(7)
@@ -4133,8 +4119,10 @@ struct wmi_rate_set {
struct wmi_vht_rate_set {
u32 tlv_header;
u32 rx_max_rate;
+ /* MCS at which the peer can transmit */
u32 rx_mcs_set;
u32 tx_max_rate;
+ /* MCS at which the peer can receive */
u32 tx_mcs_set;
u32 tx_max_mcs_nss;
} __packed;
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 5d494c5cdc0d..cc352eef1939 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@@ -1250,7 +1249,6 @@ void ath12k_fw_stats_reset(struct ath12k *ar)
spin_lock_bh(&ar->data_lock);
ath12k_fw_stats_free(&ar->fw_stats);
ar->fw_stats.num_vdev_recvd = 0;
- ar->fw_stats.num_bcn_recvd = 0;
spin_unlock_bh(&ar->data_lock);
}
@@ -2106,14 +2104,27 @@ static int ath12k_core_hw_group_create(struct ath12k_hw_group *ag)
ret = ath12k_core_soc_create(ab);
if (ret) {
mutex_unlock(&ab->core_lock);
- ath12k_err(ab, "failed to create soc core: %d\n", ret);
- return ret;
+ ath12k_err(ab, "failed to create soc %d core: %d\n", i, ret);
+ goto destroy;
}
mutex_unlock(&ab->core_lock);
}
return 0;
+
+destroy:
+ for (i--; i >= 0; i--) {
+ ab = ag->ab[i];
+ if (!ab)
+ continue;
+
+ mutex_lock(&ab->core_lock);
+ ath12k_core_soc_destroy(ab);
+ mutex_unlock(&ab->core_lock);
+ }
+
+ return ret;
}
void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag)
@@ -2188,7 +2199,7 @@ int ath12k_core_init(struct ath12k_base *ab)
if (ret) {
mutex_unlock(&ag->mutex);
ath12k_warn(ab, "unable to create hw group\n");
- goto err_destroy_hw_group;
+ goto err_unassign_hw_group;
}
}
@@ -2196,8 +2207,7 @@ int ath12k_core_init(struct ath12k_base *ab)
return 0;
-err_destroy_hw_group:
- ath12k_core_hw_group_destroy(ab->ag);
+err_unassign_hw_group:
ath12k_core_hw_group_unassign(ab);
err_unregister_notifier:
ath12k_core_panic_notifier_unregister(ab);
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 3d1956966a48..3c1e0069be1e 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -355,6 +355,8 @@ struct ath12k_link_vif {
struct wmi_vdev_install_key_arg group_key;
bool pairwise_key_done;
u16 num_stations;
+ bool is_csa_in_progress;
+ struct wiphy_work bcn_tx_work;
};
struct ath12k_vif {
@@ -644,7 +646,6 @@ struct ath12k_fw_stats {
struct list_head vdevs;
struct list_head bcn;
u32 num_vdev_recvd;
- u32 num_bcn_recvd;
};
struct ath12k_dbg_htt_stats {
@@ -963,6 +964,7 @@ struct ath12k_device_dp_stats {
u32 tx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX];
u32 tx_enqueued[DP_TCL_NUM_RING_MAX];
u32 tx_completed[DP_TCL_NUM_RING_MAX];
+ u32 reo_excep_msdu_buf_type;
};
struct ath12k_reg_freq {
diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c
index 16601a8c3644..d6a86f075d73 100644
--- a/drivers/net/wireless/ath/ath12k/debugfs.c
+++ b/drivers/net/wireless/ath/ath12k/debugfs.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "core.h"
@@ -1178,6 +1178,9 @@ static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file,
len += scnprintf(buf + len, size - len, "\n");
}
+ len += scnprintf(buf + len, size - len, "\nREO excep MSDU buf type:%u\n",
+ device_stats->reo_excep_msdu_buf_type);
+
len += scnprintf(buf + len, size - len, "\nRx WBM REL SRC Errors:\n");
for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) {
@@ -1283,6 +1286,7 @@ static int ath12k_open_vdev_stats(struct inode *inode, struct file *file)
ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
buf);
+ ath12k_fw_stats_reset(ar);
file->private_data = no_free_ptr(buf);
@@ -1349,12 +1353,7 @@ static int ath12k_open_bcn_stats(struct inode *inode, struct file *file)
ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
buf);
- /* since beacon stats request is looped for all active VDEVs, saved fw
- * stats is not freed for each request until done for all active VDEVs
- */
- spin_lock_bh(&ar->data_lock);
- ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn);
- spin_unlock_bh(&ar->data_lock);
+ ath12k_fw_stats_reset(ar);
file->private_data = no_free_ptr(buf);
@@ -1415,6 +1414,7 @@ static int ath12k_open_pdev_stats(struct inode *inode, struct file *file)
ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id,
buf);
+ ath12k_fw_stats_reset(ar);
file->private_data = no_free_ptr(buf);
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 009c49502148..39d1967584db 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "dp_mon.h"
@@ -105,7 +105,7 @@ static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vh
if (ppdu_info->is_stbc && nsts > 0)
nsts = ((nsts + 1) >> 1) - 1;
- ppdu_info->nss = u32_get_bits(nsts, VHT_SIG_SU_NSS_MASK);
+ ppdu_info->nss = u32_get_bits(nsts, VHT_SIG_SU_NSS_MASK) + 1;
ppdu_info->bw = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_BW);
ppdu_info->beamformed = u32_get_bits(info1,
HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED);
@@ -129,7 +129,7 @@ static void ath12k_dp_mon_parse_ht_sig(const struct hal_rx_ht_sig_info *ht_sig,
ppdu_info->is_stbc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_STBC);
ppdu_info->ldpc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING);
ppdu_info->gi = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_GI);
- ppdu_info->nss = (ppdu_info->mcs >> 3);
+ ppdu_info->nss = (ppdu_info->mcs >> 3) + 1;
}
static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb,
@@ -233,7 +233,9 @@ ath12k_dp_mon_parse_he_sig_b2_ofdma(const struct hal_rx_he_sig_b2_ofdma_info *of
value = value << HE_STA_ID_SHIFT;
ppdu_info->he_data4 |= value;
- ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS);
+ ppdu_info->nss =
+ u32_get_bits(info0,
+ HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS) + 1;
ppdu_info->beamformed = u32_get_bits(info0,
HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF);
}
@@ -261,7 +263,9 @@ ath12k_dp_mon_parse_he_sig_b2_mu(const struct hal_rx_he_sig_b2_mu_info *he_sig_b
value = value << HE_STA_ID_SHIFT;
ppdu_info->he_data4 |= value;
- ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS);
+ ppdu_info->nss =
+ u32_get_bits(info0,
+ HAL_RX_HE_SIG_B2_MU_INFO_INFO0_STA_NSTS) + 1;
}
static void
@@ -553,7 +557,8 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info *
ppdu_info->is_stbc = u32_get_bits(info1, HAL_RX_HE_SIG_A_SU_INFO_INFO1_STBC);
ppdu_info->beamformed = u32_get_bits(info1, HAL_RX_HE_SIG_A_SU_INFO_INFO1_TXBF);
dcm = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM);
- ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS);
+ ppdu_info->nss = u32_get_bits(info0,
+ HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS) + 1;
ppdu_info->dcm = dcm;
}
@@ -2179,7 +2184,7 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar,
spin_unlock_bh(&ar->data_lock);
rxs->flag |= RX_FLAG_MACTIME_START;
- rxs->nss = ppduinfo->nss + 1;
+ rxs->nss = ppduinfo->nss;
if (test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT,
ar->ab->wmi_ab.svc_map))
rxs->signal = ppduinfo->rssi_comb;
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index 5e5c14a70316..d28d8ffec0f8 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/ieee80211.h>
@@ -1089,6 +1089,8 @@ static int ath12k_dp_prepare_reo_update_elem(struct ath12k_dp *dp,
{
struct dp_reo_update_rx_queue_elem *elem;
+ lockdep_assert_held(&dp->ab->base_lock);
+
elem = kzalloc(sizeof(*elem), GFP_ATOMIC);
if (!elem)
return -ENOMEM;
@@ -3781,6 +3783,50 @@ exit:
return 0;
}
+static int ath12k_dp_h_msdu_buffer_type(struct ath12k_base *ab,
+ struct list_head *list,
+ struct hal_reo_dest_ring *desc)
+{
+ struct ath12k_rx_desc_info *desc_info;
+ struct ath12k_skb_rxcb *rxcb;
+ struct sk_buff *msdu;
+ u64 desc_va;
+
+ ab->device_stats.reo_excep_msdu_buf_type++;
+
+ desc_va = (u64)le32_to_cpu(desc->buf_va_hi) << 32 |
+ le32_to_cpu(desc->buf_va_lo);
+ desc_info = (struct ath12k_rx_desc_info *)(uintptr_t)desc_va;
+ if (!desc_info) {
+ u32 cookie;
+
+ cookie = le32_get_bits(desc->buf_addr_info.info1,
+ BUFFER_ADDR_INFO1_SW_COOKIE);
+ desc_info = ath12k_dp_get_rx_desc(ab, cookie);
+ if (!desc_info) {
+ ath12k_warn(ab, "Invalid cookie in manual descriptor retrieval: 0x%x\n",
+ cookie);
+ return -EINVAL;
+ }
+ }
+
+ if (desc_info->magic != ATH12K_DP_RX_DESC_MAGIC) {
+ ath12k_warn(ab, "rx exception, magic check failed with value: %u\n",
+ desc_info->magic);
+ return -EINVAL;
+ }
+
+ msdu = desc_info->skb;
+ desc_info->skb = NULL;
+ list_add_tail(&desc_info->list, list);
+ rxcb = ATH12K_SKB_RXCB(msdu);
+ dma_unmap_single(ab->dev, rxcb->paddr, msdu->len + skb_tailroom(msdu),
+ DMA_FROM_DEVICE);
+ dev_kfree_skb_any(msdu);
+
+ return 0;
+}
+
int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
int budget)
{
@@ -3825,6 +3871,26 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
drop = false;
ab->device_stats.err_ring_pkts++;
+ hw_link_id = le32_get_bits(reo_desc->info0,
+ HAL_REO_DEST_RING_INFO0_SRC_LINK_ID);
+ device_id = hw_links[hw_link_id].device_id;
+ partner_ab = ath12k_ag_to_ab(ag, device_id);
+
+ /* Below case is added to handle data packet from un-associated clients.
+ * As it is expected that AST lookup will fail for
+ * un-associated station's data packets.
+ */
+ if (le32_get_bits(reo_desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE) ==
+ HAL_REO_DEST_RING_BUFFER_TYPE_MSDU) {
+ if (!ath12k_dp_h_msdu_buffer_type(partner_ab,
+ &rx_desc_used_list[device_id],
+ reo_desc)) {
+ num_buffs_reaped[device_id]++;
+ tot_n_bufs_reaped++;
+ }
+ goto next_desc;
+ }
+
ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr,
&desc_bank);
if (ret) {
@@ -3833,11 +3899,6 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
continue;
}
- hw_link_id = le32_get_bits(reo_desc->info0,
- HAL_REO_DEST_RING_INFO0_SRC_LINK_ID);
- device_id = hw_links[hw_link_id].device_id;
- partner_ab = ath12k_ag_to_ab(ag, device_id);
-
pdev_id = ath12k_hw_mac_id_to_pdev_id(partner_ab->hw_params,
hw_links[hw_link_id].pdev_idx);
ar = partner_ab->pdevs[pdev_id].ar;
@@ -3886,6 +3947,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi,
}
}
+next_desc:
if (tot_n_bufs_reaped >= quota) {
tot_n_bufs_reaped = quota;
goto exit;
diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c
index 669096278fdd..c4443ca05cd6 100644
--- a/drivers/net/wireless/ath/ath12k/hal_rx.c
+++ b/drivers/net/wireless/ath/ath12k/hal_rx.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include "debug.h"
@@ -323,7 +323,7 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab,
{
enum hal_reo_dest_ring_push_reason push_reason;
enum hal_reo_dest_ring_error_code err_code;
- u32 cookie, val;
+ u32 cookie;
push_reason = le32_get_bits(desc->info0,
HAL_REO_DEST_RING_INFO0_PUSH_REASON);
@@ -338,12 +338,6 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab,
return -EINVAL;
}
- val = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_BUFFER_TYPE);
- if (val != HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) {
- ath12k_warn(ab, "expected buffer type link_desc");
- return -EINVAL;
- }
-
ath12k_hal_rx_reo_ent_paddr_get(ab, &desc->buf_addr_info, paddr, &cookie);
*desc_bank = u32_get_bits(cookie, DP_LINK_DESC_BANK_MASK);
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 1d7b60aa5cb0..f7a2a544bef2 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -533,6 +533,30 @@ ath12k_mac_max_he_nss(const u16 he_mcs_mask[NL80211_HE_NSS_MAX])
return 1;
}
+static u32
+ath12k_mac_max_eht_nss(const u16 eht_mcs_mask[NL80211_EHT_NSS_MAX])
+{
+ int nss;
+
+ for (nss = NL80211_EHT_NSS_MAX - 1; nss >= 0; nss--)
+ if (eht_mcs_mask[nss])
+ return nss + 1;
+
+ return 1;
+}
+
+static u32
+ath12k_mac_max_eht_mcs_nss(const u8 *eht_mcs, int eht_mcs_set_size)
+{
+ int i;
+ u8 nss = 0;
+
+ for (i = 0; i < eht_mcs_set_size; i++)
+ nss = max(nss, u8_get_bits(eht_mcs[i], IEEE80211_EHT_MCS_NSS_RX));
+
+ return nss;
+}
+
static u8 ath12k_parse_mpdudensity(u8 mpdudensity)
{
/* From IEEE Std 802.11-2020 defined values for "Minimum MPDU Start Spacing":
@@ -2249,7 +2273,6 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
struct cfg80211_chan_def def;
enum nl80211_band band;
u16 *vht_mcs_mask;
- u16 tx_mcs_map;
u8 ampdu_factor;
u8 max_nss, vht_mcs;
int i, vht_nss, nss_idx;
@@ -2340,10 +2363,10 @@ static void ath12k_peer_assoc_h_vht(struct ath12k *ar,
arg->peer_nss = min(link_sta->rx_nss, max_nss);
arg->rx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.rx_highest);
arg->rx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
- arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+ arg->rx_mcs_set = ath12k_peer_assoc_h_vht_limit(arg->rx_mcs_set, vht_mcs_mask);
- tx_mcs_map = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
- arg->tx_mcs_set = ath12k_peer_assoc_h_vht_limit(tx_mcs_map, vht_mcs_mask);
+ arg->tx_max_rate = __le16_to_cpu(vht_cap->vht_mcs.tx_highest);
+ arg->tx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map);
/* In QCN9274 platform, VHT MCS rate 10 and 11 is enabled by default.
* VHT MCS rate 10 and 11 is not supported in 11ac standard.
@@ -2625,9 +2648,10 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar,
switch (link_sta->bandwidth) {
case IEEE80211_STA_RX_BW_160:
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
- v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
+ v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_160);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v;
arg->peer_he_mcs_count++;
@@ -2637,10 +2661,10 @@ static void ath12k_peer_assoc_h_he(struct ath12k *ar,
default:
v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
+ v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_rx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
v = le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80);
- v = ath12k_peer_assoc_h_he_limit(v, he_mcs_mask);
arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_80] = v;
arg->peer_he_mcs_count++;
@@ -3004,6 +3028,18 @@ static enum wmi_phy_mode ath12k_mac_get_phymode_eht(struct ath12k *ar,
return MODE_UNKNOWN;
}
+static bool
+ath12k_peer_assoc_h_eht_masked(const u16 eht_mcs_mask[NL80211_EHT_NSS_MAX])
+{
+ int nss;
+
+ for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++)
+ if (eht_mcs_mask[nss])
+ return false;
+
+ return true;
+}
+
static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ath12k_link_sta *arsta,
@@ -3015,6 +3051,7 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask;
const u16 *he_mcs_mask;
+ const u16 *eht_mcs_mask;
enum wmi_phy_mode phymode = MODE_UNKNOWN;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -3029,6 +3066,7 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
+ eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs;
link_sta = ath12k_mac_get_link_sta(arsta);
if (!link_sta) {
@@ -3039,7 +3077,8 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
switch (band) {
case NL80211_BAND_2GHZ:
- if (link_sta->eht_cap.has_eht) {
+ if (link_sta->eht_cap.has_eht &&
+ !ath12k_peer_assoc_h_eht_masked(eht_mcs_mask)) {
if (link_sta->bandwidth == IEEE80211_STA_RX_BW_40)
phymode = MODE_11BE_EHT40_2G;
else
@@ -3102,37 +3141,50 @@ static void ath12k_peer_assoc_h_phymode(struct ath12k *ar,
WARN_ON(phymode == MODE_UNKNOWN);
}
+#define ATH12K_EHT_MCS_7_ENABLED 0x00FF
+#define ATH12K_EHT_MCS_9_ENABLED 0x0300
+#define ATH12K_EHT_MCS_11_ENABLED 0x0C00
+#define ATH12K_EHT_MCS_13_ENABLED 0x3000
+
static void ath12k_mac_set_eht_mcs(u8 rx_tx_mcs7, u8 rx_tx_mcs9,
u8 rx_tx_mcs11, u8 rx_tx_mcs13,
- u32 *rx_mcs, u32 *tx_mcs)
-{
- *rx_mcs = 0;
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_0_7);
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_8_9);
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_10_11);
- u32p_replace_bits(rx_mcs,
- u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_RX),
- WMI_EHT_MCS_NSS_12_13);
-
- *tx_mcs = 0;
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_0_7);
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_8_9);
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_10_11);
- u32p_replace_bits(tx_mcs,
- u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_TX),
- WMI_EHT_MCS_NSS_12_13);
+ u32 *rx_mcs, u32 *tx_mcs,
+ const u16 eht_mcs_limit[NL80211_EHT_NSS_MAX])
+{
+ int nss;
+ u8 mcs_7 = 0, mcs_9 = 0, mcs_11 = 0, mcs_13 = 0;
+ u8 peer_mcs_7, peer_mcs_9, peer_mcs_11, peer_mcs_13;
+
+ for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++) {
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_7_ENABLED)
+ mcs_7++;
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_9_ENABLED)
+ mcs_9++;
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_11_ENABLED)
+ mcs_11++;
+ if (eht_mcs_limit[nss] & ATH12K_EHT_MCS_13_ENABLED)
+ mcs_13++;
+ }
+
+ peer_mcs_7 = u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_RX);
+ peer_mcs_9 = u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_RX);
+ peer_mcs_11 = u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_RX);
+ peer_mcs_13 = u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_RX);
+
+ *rx_mcs = u32_encode_bits(min(peer_mcs_7, mcs_7), WMI_EHT_MCS_NSS_0_7) |
+ u32_encode_bits(min(peer_mcs_9, mcs_9), WMI_EHT_MCS_NSS_8_9) |
+ u32_encode_bits(min(peer_mcs_11, mcs_11), WMI_EHT_MCS_NSS_10_11) |
+ u32_encode_bits(min(peer_mcs_13, mcs_13), WMI_EHT_MCS_NSS_12_13);
+
+ peer_mcs_7 = u8_get_bits(rx_tx_mcs7, IEEE80211_EHT_MCS_NSS_TX);
+ peer_mcs_9 = u8_get_bits(rx_tx_mcs9, IEEE80211_EHT_MCS_NSS_TX);
+ peer_mcs_11 = u8_get_bits(rx_tx_mcs11, IEEE80211_EHT_MCS_NSS_TX);
+ peer_mcs_13 = u8_get_bits(rx_tx_mcs13, IEEE80211_EHT_MCS_NSS_TX);
+
+ *tx_mcs = u32_encode_bits(min(peer_mcs_7, mcs_7), WMI_EHT_MCS_NSS_0_7) |
+ u32_encode_bits(min(peer_mcs_9, mcs_9), WMI_EHT_MCS_NSS_8_9) |
+ u32_encode_bits(min(peer_mcs_11, mcs_11), WMI_EHT_MCS_NSS_10_11) |
+ u32_encode_bits(min(peer_mcs_13, mcs_13), WMI_EHT_MCS_NSS_12_13);
}
static void ath12k_mac_set_eht_ppe_threshold(const u8 *ppe_thres,
@@ -3171,13 +3223,22 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
struct ath12k_wmi_peer_assoc_arg *arg)
{
struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
+ struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+ const struct ieee80211_eht_mcs_nss_supp *own_eht_mcs_nss_supp;
const struct ieee80211_eht_mcs_nss_supp_20mhz_only *bw_20;
+ const struct ieee80211_sta_eht_cap *eht_cap, *own_eht_cap;
+ const struct ieee80211_sband_iftype_data *iftd;
const struct ieee80211_eht_mcs_nss_supp_bw *bw;
- const struct ieee80211_sta_eht_cap *eht_cap;
const struct ieee80211_sta_he_cap *he_cap;
struct ieee80211_link_sta *link_sta;
struct ieee80211_bss_conf *link_conf;
+ struct cfg80211_chan_def def;
+ bool user_rate_valid = true;
+ enum nl80211_band band;
+ int eht_nss, nss_idx;
u32 *rx_mcs, *tx_mcs;
+ u16 *eht_mcs_mask;
+ u8 max_nss = 0;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -3199,6 +3260,22 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
if (!he_cap->has_he || !eht_cap->has_eht)
return;
+ if (WARN_ON(ath12k_mac_vif_link_chan(vif, arvif->link_id, &def)))
+ return;
+
+ band = def.chan->band;
+ eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs;
+
+ iftd = ieee80211_get_sband_iftype_data(&ar->mac.sbands[band], vif->type);
+ if (!iftd) {
+ ath12k_warn(ar->ab,
+ "unable to access iftype_data in struct ieee80211_supported_band\n");
+ return;
+ }
+
+ own_eht_cap = &iftd->eht_cap;
+ own_eht_mcs_nss_supp = &own_eht_cap->eht_mcs_nss_supp;
+
arg->eht_flag = true;
if ((eht_cap->eht_cap_elem.phy_cap_info[5] &
@@ -3215,6 +3292,28 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
rx_mcs = arg->peer_eht_rx_mcs_set;
tx_mcs = arg->peer_eht_tx_mcs_set;
+ eht_nss = ath12k_mac_max_eht_mcs_nss((void *)own_eht_mcs_nss_supp,
+ sizeof(*own_eht_mcs_nss_supp));
+ if (eht_nss > link_sta->rx_nss) {
+ user_rate_valid = false;
+ for (nss_idx = (link_sta->rx_nss - 1); nss_idx >= 0; nss_idx--) {
+ if (eht_mcs_mask[nss_idx]) {
+ user_rate_valid = true;
+ break;
+ }
+ }
+ }
+
+ if (!user_rate_valid) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "Setting eht range MCS value to peer supported nss %d for peer %pM\n",
+ link_sta->rx_nss, arsta->addr);
+ eht_mcs_mask[link_sta->rx_nss - 1] = eht_mcs_mask[eht_nss - 1];
+ }
+
+ bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz;
+ bw = &eht_cap->eht_mcs_nss_supp.bw._80;
+
switch (link_sta->bandwidth) {
case IEEE80211_STA_RX_BW_320:
bw = &eht_cap->eht_mcs_nss_supp.bw._320;
@@ -3223,7 +3322,8 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw->rx_tx_mcs11_max_nss,
bw->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_320],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_320]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_320],
+ eht_mcs_mask);
arg->peer_eht_mcs_count++;
fallthrough;
case IEEE80211_STA_RX_BW_160:
@@ -3233,15 +3333,13 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw->rx_tx_mcs11_max_nss,
bw->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_160],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_160]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_160],
+ eht_mcs_mask);
arg->peer_eht_mcs_count++;
fallthrough;
default:
- if ((he_cap->he_cap_elem.phy_cap_info[0] &
- (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)) == 0) {
+ if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz;
ath12k_mac_set_eht_mcs(bw_20->rx_tx_mcs7_max_nss,
@@ -3249,7 +3347,8 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw_20->rx_tx_mcs11_max_nss,
bw_20->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
+ eht_mcs_mask);
} else {
bw = &eht_cap->eht_mcs_nss_supp.bw._80;
ath12k_mac_set_eht_mcs(bw->rx_tx_mcs9_max_nss,
@@ -3257,7 +3356,8 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
bw->rx_tx_mcs11_max_nss,
bw->rx_tx_mcs13_max_nss,
&rx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
- &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80]);
+ &tx_mcs[WMI_EHTCAP_TXRX_MCS_NSS_IDX_80],
+ eht_mcs_mask);
}
arg->peer_eht_mcs_count++;
@@ -3266,6 +3366,41 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
arg->punct_bitmap = ~arvif->punct_bitmap;
arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15;
+
+ if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
+ if (bw_20->rx_tx_mcs13_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs13_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw_20->rx_tx_mcs11_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs11_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw_20->rx_tx_mcs9_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs9_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw_20->rx_tx_mcs7_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs7_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ } else {
+ if (bw->rx_tx_mcs13_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs13_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw->rx_tx_mcs11_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs11_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ if (bw->rx_tx_mcs9_max_nss)
+ max_nss = max(max_nss, u8_get_bits(bw->rx_tx_mcs9_max_nss,
+ IEEE80211_EHT_MCS_NSS_RX));
+ }
+
+ max_nss = min(max_nss, (uint8_t)eht_nss);
+
+ arg->peer_nss = min(link_sta->rx_nss, max_nss);
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "mac eht peer %pM nss %d mcs cnt %d ru_punct_bitmap 0x%x\n",
+ arsta->addr, arg->peer_nss, arg->peer_eht_mcs_count,
+ arg->punct_bitmap);
}
static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
@@ -3834,6 +3969,38 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar,
ath12k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret);
}
+static void ath12k_mac_bcn_tx_event(struct ath12k_link_vif *arvif)
+{
+ struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif);
+ struct ieee80211_bss_conf *link_conf;
+
+ link_conf = ath12k_mac_get_link_bss_conf(arvif);
+ if (!link_conf) {
+ ath12k_warn(arvif->ar->ab, "failed to get link conf for vdev %u\n",
+ arvif->vdev_id);
+ return;
+ }
+
+ if (link_conf->color_change_active) {
+ if (ieee80211_beacon_cntdwn_is_complete(vif, arvif->link_id)) {
+ ieee80211_color_change_finish(vif, arvif->link_id);
+ return;
+ }
+
+ ieee80211_beacon_update_cntdwn(vif, arvif->link_id);
+ ath12k_mac_setup_bcn_tmpl(arvif);
+ }
+}
+
+static void ath12k_mac_bcn_tx_work(struct wiphy *wiphy, struct wiphy_work *work)
+{
+ struct ath12k_link_vif *arvif = container_of(work, struct ath12k_link_vif,
+ bcn_tx_work);
+
+ lockdep_assert_wiphy(wiphy);
+ ath12k_mac_bcn_tx_event(arvif);
+}
+
static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif,
struct ath12k_link_vif *arvif, int link_id)
{
@@ -3863,6 +4030,7 @@ static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif,
INIT_LIST_HEAD(&arvif->list);
INIT_DELAYED_WORK(&arvif->connection_loss_work,
ath12k_mac_vif_sta_connection_loss_work);
+ wiphy_work_init(&arvif->bcn_tx_work, ath12k_mac_bcn_tx_work);
arvif->num_stations = 0;
@@ -3875,6 +4043,8 @@ static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif,
sizeof(arvif->bitrate_mask.control[i].vht_mcs));
memset(arvif->bitrate_mask.control[i].he_mcs, 0xff,
sizeof(arvif->bitrate_mask.control[i].he_mcs));
+ memset(arvif->bitrate_mask.control[i].eht_mcs, 0xff,
+ sizeof(arvif->bitrate_mask.control[i].eht_mcs));
}
/* Handle MLO related assignments */
@@ -3900,6 +4070,7 @@ static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw,
lockdep_assert_wiphy(ah->hw->wiphy);
cancel_delayed_work_sync(&arvif->connection_loss_work);
+ wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &arvif->bcn_tx_work);
ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)",
arvif->vdev_id, arvif->link_id);
@@ -4064,68 +4235,12 @@ static int ath12k_mac_fils_discovery(struct ath12k_link_vif *arvif,
return ret;
}
-static void ath12k_mac_vif_setup_ps(struct ath12k_link_vif *arvif)
-{
- struct ath12k *ar = arvif->ar;
- struct ieee80211_vif *vif = arvif->ahvif->vif;
- struct ieee80211_conf *conf = &ath12k_ar_to_hw(ar)->conf;
- enum wmi_sta_powersave_param param;
- struct ieee80211_bss_conf *info;
- enum wmi_sta_ps_mode psmode;
- int ret;
- int timeout;
- bool enable_ps;
-
- lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
-
- if (vif->type != NL80211_IFTYPE_STATION)
- return;
-
- enable_ps = arvif->ahvif->ps;
- if (enable_ps) {
- psmode = WMI_STA_PS_MODE_ENABLED;
- param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
-
- timeout = conf->dynamic_ps_timeout;
- if (timeout == 0) {
- info = ath12k_mac_get_link_bss_conf(arvif);
- if (!info) {
- ath12k_warn(ar->ab, "unable to access bss link conf in setup ps for vif %pM link %u\n",
- vif->addr, arvif->link_id);
- return;
- }
-
- /* firmware doesn't like 0 */
- timeout = ieee80211_tu_to_usec(info->beacon_int) / 1000;
- }
-
- ret = ath12k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
- timeout);
- if (ret) {
- ath12k_warn(ar->ab, "failed to set inactivity time for vdev %d: %i\n",
- arvif->vdev_id, ret);
- return;
- }
- } else {
- psmode = WMI_STA_PS_MODE_DISABLED;
- }
-
- ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %d psmode %s\n",
- arvif->vdev_id, psmode ? "enable" : "disable");
-
- ret = ath12k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, psmode);
- if (ret)
- ath12k_warn(ar->ab, "failed to set sta power save mode %d for vdev %d: %d\n",
- psmode, arvif->vdev_id, ret);
-}
-
static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u64 changed)
{
struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
unsigned long links = ahvif->links_map;
- struct ieee80211_vif_cfg *vif_cfg;
struct ieee80211_bss_conf *info;
struct ath12k_link_vif *arvif;
struct ieee80211_sta *sta;
@@ -4189,24 +4304,61 @@ static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw,
}
}
}
+}
- if (changed & BSS_CHANGED_PS) {
- links = ahvif->links_map;
- vif_cfg = &vif->cfg;
+static void ath12k_mac_vif_setup_ps(struct ath12k_link_vif *arvif)
+{
+ struct ath12k *ar = arvif->ar;
+ struct ieee80211_vif *vif = arvif->ahvif->vif;
+ struct ieee80211_conf *conf = &ath12k_ar_to_hw(ar)->conf;
+ enum wmi_sta_powersave_param param;
+ struct ieee80211_bss_conf *info;
+ enum wmi_sta_ps_mode psmode;
+ int ret;
+ int timeout;
+ bool enable_ps;
- for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
- arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]);
- if (!arvif || !arvif->ar)
- continue;
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
- ar = arvif->ar;
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
- if (ar->ab->hw_params->supports_sta_ps) {
- ahvif->ps = vif_cfg->ps;
- ath12k_mac_vif_setup_ps(arvif);
+ enable_ps = arvif->ahvif->ps;
+ if (enable_ps) {
+ psmode = WMI_STA_PS_MODE_ENABLED;
+ param = WMI_STA_PS_PARAM_INACTIVITY_TIME;
+
+ timeout = conf->dynamic_ps_timeout;
+ if (timeout == 0) {
+ info = ath12k_mac_get_link_bss_conf(arvif);
+ if (!info) {
+ ath12k_warn(ar->ab, "unable to access bss link conf in setup ps for vif %pM link %u\n",
+ vif->addr, arvif->link_id);
+ return;
}
+
+ /* firmware doesn't like 0 */
+ timeout = ieee80211_tu_to_usec(info->beacon_int) / 1000;
+ }
+
+ ret = ath12k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param,
+ timeout);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to set inactivity time for vdev %d: %i\n",
+ arvif->vdev_id, ret);
+ return;
}
+ } else {
+ psmode = WMI_STA_PS_MODE_DISABLED;
}
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev %d psmode %s\n",
+ arvif->vdev_id, psmode ? "enable" : "disable");
+
+ ret = ath12k_wmi_pdev_set_ps_mode(ar, arvif->vdev_id, psmode);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to set sta power save mode %d for vdev %d: %d\n",
+ psmode, arvif->vdev_id, ret);
}
static bool ath12k_mac_supports_tpc(struct ath12k *ar, struct ath12k_vif *ahvif,
@@ -4221,6 +4373,30 @@ static bool ath12k_mac_supports_tpc(struct ath12k *ar, struct ath12k_vif *ahvif,
chandef->chan->band == NL80211_BAND_6GHZ;
}
+static void ath12k_wmi_vdev_params_up(struct ath12k *ar,
+ struct ath12k_link_vif *arvif,
+ struct ath12k_link_vif *tx_arvif,
+ struct ieee80211_bss_conf *info, u16 aid)
+{
+ struct ath12k_wmi_vdev_up_params params = {
+ .vdev_id = arvif->vdev_id,
+ .aid = aid,
+ .bssid = arvif->bssid
+ };
+ int ret;
+
+ if (tx_arvif) {
+ params.tx_bssid = tx_arvif->bssid;
+ params.nontx_profile_idx = info->bssid_index;
+ params.nontx_profile_cnt = 1 << info->bssid_indicator;
+ }
+
+ ret = ath12k_wmi_vdev_up(arvif->ar, &params);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to bring vdev up %d: %d\n",
+ arvif->vdev_id, ret);
+}
+
static void ath12k_mac_bss_info_changed(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ieee80211_bss_conf *info,
@@ -4228,6 +4404,8 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
{
struct ath12k_vif *ahvif = arvif->ahvif;
struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif);
+ struct ieee80211_vif_cfg *vif_cfg = &vif->cfg;
+ struct ath12k_link_vif *tx_arvif;
struct cfg80211_chan_def def;
u32 param_id, param_value;
enum nl80211_band band;
@@ -4236,9 +4414,9 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
u32 preamble;
u16 hw_value;
u16 bitrate;
- int ret;
u8 rateidx;
u32 rate;
+ int ret;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -4271,12 +4449,41 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
"Set burst beacon mode for VDEV: %d\n",
arvif->vdev_id);
+ /* In MBSSID case, need to install transmitting VIF's template first */
+
ret = ath12k_mac_setup_bcn_tmpl(arvif);
if (ret)
ath12k_warn(ar->ab, "failed to update bcn template: %d\n",
ret);
+
+ if (!arvif->is_csa_in_progress)
+ goto skip_vdev_up;
+
+ tx_arvif = ath12k_mac_get_tx_arvif(arvif, info);
+ if (tx_arvif && arvif != tx_arvif && tx_arvif->is_csa_in_progress)
+ /* skip non tx vif's */
+ goto skip_vdev_up;
+
+ ath12k_wmi_vdev_params_up(ar, arvif, tx_arvif, info, ahvif->aid);
+
+ arvif->is_csa_in_progress = false;
+
+ if (tx_arvif && arvif == tx_arvif) {
+ struct ath12k_link_vif *arvif_itr;
+
+ list_for_each_entry(arvif_itr, &ar->arvifs, list) {
+ if (!arvif_itr->is_csa_in_progress)
+ continue;
+
+ ath12k_wmi_vdev_params_up(ar, arvif, tx_arvif,
+ info, ahvif->aid);
+ arvif_itr->is_csa_in_progress = false;
+ }
+ }
}
+skip_vdev_up:
+
if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) {
arvif->dtim_period = info->dtim_period;
@@ -4493,8 +4700,25 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
ATH12K_BSS_COLOR_AP_PERIODS,
info->he_bss_color.enabled);
if (ret)
- ath12k_warn(ar->ab, "failed to set bss color collision on vdev %i: %d\n",
+ ath12k_warn(ar->ab, "failed to set bss color collision on vdev %u: %d\n",
arvif->vdev_id, ret);
+
+ param_id = WMI_VDEV_PARAM_BSS_COLOR;
+ if (info->he_bss_color.enabled)
+ param_value = info->he_bss_color.color <<
+ IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET;
+ else
+ param_value = IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED;
+
+ ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
+ param_id,
+ param_value);
+ if (ret)
+ ath12k_warn(ar->ab, "failed to set bss color param on vdev %u: %d\n",
+ arvif->vdev_id, ret);
+ else
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "bss color param 0x%x set on vdev %u\n",
+ param_value, arvif->vdev_id);
} else if (vif->type == NL80211_IFTYPE_STATION) {
ret = ath12k_wmi_send_bss_color_change_enable_cmd(ar,
arvif->vdev_id,
@@ -4514,6 +4738,12 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar,
}
ath12k_mac_fils_discovery(arvif, info);
+
+ if (changed & BSS_CHANGED_PS &&
+ ar->ab->hw_params->supports_sta_ps) {
+ ahvif->ps = vif_cfg->ps;
+ ath12k_mac_vif_setup_ps(arvif);
+ }
}
static struct ath12k_vif_cache *ath12k_ahvif_get_link_cache(struct ath12k_vif *ahvif,
@@ -4849,8 +5079,6 @@ int ath12k_mac_get_fw_stats(struct ath12k *ar,
if (ah->state != ATH12K_HW_STATE_ON)
return -ENETDOWN;
- ath12k_fw_stats_reset(ar);
-
reinit_completion(&ar->fw_stats_complete);
reinit_completion(&ar->fw_stats_done);
@@ -4948,6 +5176,7 @@ static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
ar->chan_tx_pwr = pdev->chan_tx_power / 2;
spin_unlock_bh(&ar->data_lock);
ar->last_tx_power_update = jiffies;
+ ath12k_fw_stats_reset(ar);
send_tx_power:
*dbm = ar->chan_tx_pwr;
@@ -5071,7 +5300,8 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
ret = ath12k_mac_vdev_create(ar, arvif);
if (ret) {
ath12k_warn(ar->ab, "unable to create scan vdev %d\n", ret);
- return -EINVAL;
+ ath12k_mac_unassign_link_vif(arvif);
+ return ret;
}
}
@@ -5731,6 +5961,20 @@ ath12k_mac_bitrate_mask_num_he_rates(struct ath12k *ar,
}
static int
+ath12k_mac_bitrate_mask_num_eht_rates(struct ath12k *ar,
+ enum nl80211_band band,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ int num_rates = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].eht_mcs); i++)
+ num_rates += hweight16(mask->control[band].eht_mcs[i]);
+
+ return num_rates;
+}
+
+static int
ath12k_mac_set_peer_vht_fixed_rate(struct ath12k_link_vif *arvif,
struct ath12k_link_sta *arsta,
const struct cfg80211_bitrate_mask *mask,
@@ -5830,6 +6074,65 @@ ath12k_mac_set_peer_he_fixed_rate(struct ath12k_link_vif *arvif,
return ret;
}
+static int
+ath12k_mac_set_peer_eht_fixed_rate(struct ath12k_link_vif *arvif,
+ struct ath12k_link_sta *arsta,
+ const struct cfg80211_bitrate_mask *mask,
+ enum nl80211_band band)
+{
+ struct ath12k_sta *ahsta = arsta->ahsta;
+ struct ath12k *ar = arvif->ar;
+ struct ieee80211_sta *sta;
+ struct ieee80211_link_sta *link_sta;
+ u8 eht_rate, nss = 0;
+ u32 rate_code;
+ int ret, i;
+
+ lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
+
+ sta = ath12k_ahsta_to_sta(ahsta);
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].eht_mcs); i++) {
+ if (hweight16(mask->control[band].eht_mcs[i]) == 1) {
+ nss = i + 1;
+ eht_rate = ffs(mask->control[band].eht_mcs[i]) - 1;
+ }
+ }
+
+ if (!nss) {
+ ath12k_warn(ar->ab, "No single EHT Fixed rate found to set for %pM\n",
+ arsta->addr);
+ return -EINVAL;
+ }
+
+ /* Avoid updating invalid nss as fixed rate*/
+ link_sta = ath12k_mac_get_link_sta(arsta);
+ if (!link_sta || nss > link_sta->rx_nss) {
+ ath12k_warn(ar->ab,
+ "unable to access link sta for sta %pM link %u or fixed nss of %u is not supported by sta\n",
+ sta->addr, arsta->link_id, nss);
+ return -EINVAL;
+ }
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "Setting Fixed EHT Rate for peer %pM. Device will not switch to any other selected rates\n",
+ arsta->addr);
+
+ rate_code = ATH12K_HW_RATE_CODE(eht_rate, nss - 1,
+ WMI_RATE_PREAMBLE_EHT);
+
+ ret = ath12k_wmi_set_peer_param(ar, arsta->addr,
+ arvif->vdev_id,
+ WMI_PEER_PARAM_FIXED_RATE,
+ rate_code);
+ if (ret)
+ ath12k_warn(ar->ab,
+ "failed to update STA %pM Fixed Rate %d: %d\n",
+ arsta->addr, rate_code, ret);
+
+ return ret;
+}
+
static int ath12k_mac_station_assoc(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ath12k_link_sta *arsta,
@@ -5842,7 +6145,7 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
struct cfg80211_chan_def def;
enum nl80211_band band;
struct cfg80211_bitrate_mask *mask;
- u8 num_vht_rates, num_he_rates;
+ u8 num_vht_rates, num_he_rates, num_eht_rates;
u8 link_id = arvif->link_id;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -5885,10 +6188,11 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
num_vht_rates = ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask);
num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask);
+ num_eht_rates = ath12k_mac_bitrate_mask_num_eht_rates(ar, band, mask);
- /* If single VHT/HE rate is configured (by set_bitrate_mask()),
- * peer_assoc will disable VHT/HE. This is now enabled by a peer specific
- * fixed param.
+ /* If single VHT/HE/EHT rate is configured (by set_bitrate_mask()),
+ * peer_assoc will disable VHT/HE/EHT. This is now enabled by a peer
+ * specific fixed param.
* Note that all other rates and NSS will be disabled for this peer.
*/
link_sta = ath12k_mac_get_link_sta(arsta);
@@ -5908,6 +6212,10 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
ret = ath12k_mac_set_peer_he_fixed_rate(arvif, arsta, mask, band);
if (ret)
return ret;
+ } else if (link_sta->eht_cap.has_eht && num_eht_rates == 1) {
+ ret = ath12k_mac_set_peer_eht_fixed_rate(arvif, arsta, mask, band);
+ if (ret)
+ return ret;
}
/* Re-assoc is run only to update supported rates for given station. It
@@ -5970,8 +6278,9 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask;
const u16 *he_mcs_mask;
+ const u16 *eht_mcs_mask;
u32 changed, bw, nss, mac_nss, smps, bw_prev;
- int err, num_vht_rates, num_he_rates;
+ int err, num_vht_rates, num_he_rates, num_eht_rates;
const struct cfg80211_bitrate_mask *mask;
enum wmi_phy_mode peer_phymode;
struct ath12k_link_sta *arsta;
@@ -5992,6 +6301,7 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
ht_mcs_mask = arvif->bitrate_mask.control[band].ht_mcs;
vht_mcs_mask = arvif->bitrate_mask.control[band].vht_mcs;
he_mcs_mask = arvif->bitrate_mask.control[band].he_mcs;
+ eht_mcs_mask = arvif->bitrate_mask.control[band].eht_mcs;
spin_lock_bh(&ar->data_lock);
@@ -6009,6 +6319,7 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
mac_nss = max3(ath12k_mac_max_ht_nss(ht_mcs_mask),
ath12k_mac_max_vht_nss(vht_mcs_mask),
ath12k_mac_max_he_nss(he_mcs_mask));
+ mac_nss = max(mac_nss, ath12k_mac_max_eht_nss(eht_mcs_mask));
nss = min(nss, mac_nss);
struct ath12k_wmi_peer_assoc_arg *peer_arg __free(kfree) =
@@ -6094,6 +6405,8 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
mask);
num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band,
mask);
+ num_eht_rates = ath12k_mac_bitrate_mask_num_eht_rates(ar, band,
+ mask);
/* Peer_assoc_prepare will reject vht rates in
* bitrate_mask if its not available in range format and
@@ -6118,9 +6431,18 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
band);
} else if (link_sta->he_cap.has_he && num_he_rates == 1) {
ath12k_mac_set_peer_he_fixed_rate(arvif, arsta, mask, band);
+ } else if (link_sta->eht_cap.has_eht && num_eht_rates == 1) {
+ err = ath12k_mac_set_peer_eht_fixed_rate(arvif, arsta,
+ mask, band);
+ if (err) {
+ ath12k_warn(ar->ab,
+ "failed to set peer EHT fixed rate for STA %pM ret %d\n",
+ arsta->addr, err);
+ return;
+ }
} else {
- /* If the peer is non-VHT/HE or no fixed VHT/HE rate
- * is provided in the new bitrate mask we set the
+ /* If the peer is non-VHT/HE/EHT or no fixed VHT/HE/EHT
+ * rate is provided in the new bitrate mask we set the
* other rates using peer_assoc command. Also clear
* the peer fixed rate settings as it has higher proprity
* than peer assoc
@@ -8290,23 +8612,32 @@ static void ath12k_mgmt_over_wmi_tx_drop(struct ath12k *ar, struct sk_buff *skb)
wake_up(&ar->txmgmt_empty_waitq);
}
-int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx)
+static void ath12k_mac_tx_mgmt_free(struct ath12k *ar, int buf_id)
{
- struct sk_buff *msdu = skb;
+ struct sk_buff *msdu;
struct ieee80211_tx_info *info;
- struct ath12k *ar = ctx;
- struct ath12k_base *ab = ar->ab;
spin_lock_bh(&ar->txmgmt_idr_lock);
- idr_remove(&ar->txmgmt_idr, buf_id);
+ msdu = idr_remove(&ar->txmgmt_idr, buf_id);
spin_unlock_bh(&ar->txmgmt_idr_lock);
- dma_unmap_single(ab->dev, ATH12K_SKB_CB(msdu)->paddr, msdu->len,
+
+ if (!msdu)
+ return;
+
+ dma_unmap_single(ar->ab->dev, ATH12K_SKB_CB(msdu)->paddr, msdu->len,
DMA_TO_DEVICE);
info = IEEE80211_SKB_CB(msdu);
memset(&info->status, 0, sizeof(info->status));
- ath12k_mgmt_over_wmi_tx_drop(ar, skb);
+ ath12k_mgmt_over_wmi_tx_drop(ar, msdu);
+}
+
+int ath12k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx)
+{
+ struct ath12k *ar = ctx;
+
+ ath12k_mac_tx_mgmt_free(ar, buf_id);
return 0;
}
@@ -8315,17 +8646,10 @@ static int ath12k_mac_vif_txmgmt_idr_remove(int buf_id, void *skb, void *ctx)
{
struct ieee80211_vif *vif = ctx;
struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb);
- struct sk_buff *msdu = skb;
struct ath12k *ar = skb_cb->ar;
- struct ath12k_base *ab = ar->ab;
- if (skb_cb->vif == vif) {
- spin_lock_bh(&ar->txmgmt_idr_lock);
- idr_remove(&ar->txmgmt_idr, buf_id);
- spin_unlock_bh(&ar->txmgmt_idr_lock);
- dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len,
- DMA_TO_DEVICE);
- }
+ if (skb_cb->vif == vif)
+ ath12k_mac_tx_mgmt_free(ar, buf_id);
return 0;
}
@@ -9697,6 +10021,12 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif)
if (vif->type == NL80211_IFTYPE_MONITOR && ar->monitor_vdev_created)
return -EINVAL;
+ if (ar->num_created_vdevs >= TARGET_NUM_VDEVS(ab)) {
+ ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n",
+ TARGET_NUM_VDEVS(ab));
+ return -ENOSPC;
+ }
+
link_id = arvif->link_id;
if (link_id < IEEE80211_MLD_MAX_NUM_LINKS) {
@@ -10056,12 +10386,6 @@ static struct ath12k *ath12k_mac_assign_vif_to_vdev(struct ieee80211_hw *hw,
if (arvif->is_created)
goto flush;
- if (ar->num_created_vdevs > (TARGET_NUM_VDEVS(ab) - 1)) {
- ath12k_warn(ab, "failed to create vdev, reached max vdev limit %d\n",
- TARGET_NUM_VDEVS(ab));
- goto unlock;
- }
-
ret = ath12k_mac_vdev_create(ar, arvif);
if (ret) {
ath12k_warn(ab, "failed to create vdev %pM ret %d", vif->addr, ret);
@@ -10862,9 +11186,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
int n_vifs)
{
struct ath12k_wmi_vdev_up_params params = {};
- struct ath12k_link_vif *arvif;
struct ieee80211_bss_conf *link_conf;
struct ath12k_base *ab = ar->ab;
+ struct ath12k_link_vif *arvif;
struct ieee80211_vif *vif;
struct ath12k_vif *ahvif;
u8 link_id;
@@ -10925,6 +11249,28 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
continue;
}
+ ret = ath12k_mac_update_peer_puncturing_width(arvif->ar, arvif,
+ vifs[i].new_ctx->def);
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to update puncturing bitmap %02x and width %d: %d\n",
+ vifs[i].new_ctx->def.punctured,
+ vifs[i].new_ctx->def.width, ret);
+ continue;
+ }
+
+ /* Defer VDEV bring-up during CSA to avoid installing stale
+ * beacon templates. The beacon content is updated only
+ * after CSA finalize, so we mark CSA in progress and skip
+ * VDEV_UP for now. It will be handled later in
+ * bss_info_changed().
+ */
+ if (link_conf->csa_active &&
+ arvif->ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
+ arvif->is_csa_in_progress = true;
+ continue;
+ }
+
ret = ath12k_mac_setup_bcn_tmpl(arvif);
if (ret)
ath12k_warn(ab, "failed to update bcn tmpl during csa: %d\n",
@@ -10945,16 +11291,6 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
arvif->vdev_id, ret);
continue;
}
-
- ret = ath12k_mac_update_peer_puncturing_width(arvif->ar, arvif,
- vifs[i].new_ctx->def);
- if (ret) {
- ath12k_warn(ar->ab,
- "failed to update puncturing bitmap %02x and width %d: %d\n",
- vifs[i].new_ctx->def.punctured,
- vifs[i].new_ctx->def.width, ret);
- continue;
- }
}
/* Restart the internal monitor vdev on new channel */
@@ -11859,6 +12195,9 @@ ath12k_mac_has_single_legacy_rate(struct ath12k *ar,
if (ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask))
return false;
+ if (ath12k_mac_bitrate_mask_num_eht_rates(ar, band, mask))
+ return false;
+
return num_rates == 1;
}
@@ -11881,11 +12220,15 @@ ath12k_mac_bitrate_mask_get_single_nss(struct ath12k *ar,
{
struct ieee80211_supported_band *sband = &ar->mac.sbands[band];
u16 vht_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
+ const struct ieee80211_sband_iftype_data *data;
const struct ieee80211_sta_he_cap *he_cap;
u16 he_mcs_map = 0;
+ u16 eht_mcs_map = 0;
u8 ht_nss_mask = 0;
u8 vht_nss_mask = 0;
u8 he_nss_mask = 0;
+ u8 eht_nss_mask = 0;
+ u8 mcs_nss_len;
int i;
/* No need to consider legacy here. Basic rates are always present
@@ -11929,7 +12272,60 @@ ath12k_mac_bitrate_mask_get_single_nss(struct ath12k *ar,
return false;
}
- if (ht_nss_mask != vht_nss_mask || ht_nss_mask != he_nss_mask)
+ data = ieee80211_get_sband_iftype_data(sband, vif->type);
+
+ mcs_nss_len = ieee80211_eht_mcs_nss_size(&data->he_cap.he_cap_elem,
+ &data->eht_cap.eht_cap_elem,
+ false);
+ if (mcs_nss_len == 4) {
+ /* 20 MHz only STA case */
+ const struct ieee80211_eht_mcs_nss_supp_20mhz_only *eht_mcs_nss =
+ &data->eht_cap.eht_mcs_nss_supp.only_20mhz;
+ if (eht_mcs_nss->rx_tx_mcs13_max_nss)
+ eht_mcs_map = 0x1fff;
+ else if (eht_mcs_nss->rx_tx_mcs11_max_nss)
+ eht_mcs_map = 0x07ff;
+ else if (eht_mcs_nss->rx_tx_mcs9_max_nss)
+ eht_mcs_map = 0x01ff;
+ else
+ eht_mcs_map = 0x007f;
+ } else {
+ const struct ieee80211_eht_mcs_nss_supp_bw *eht_mcs_nss;
+
+ switch (mcs_nss_len) {
+ case 9:
+ eht_mcs_nss = &data->eht_cap.eht_mcs_nss_supp.bw._320;
+ break;
+ case 6:
+ eht_mcs_nss = &data->eht_cap.eht_mcs_nss_supp.bw._160;
+ break;
+ case 3:
+ eht_mcs_nss = &data->eht_cap.eht_mcs_nss_supp.bw._80;
+ break;
+ default:
+ return false;
+ }
+
+ if (eht_mcs_nss->rx_tx_mcs13_max_nss)
+ eht_mcs_map = 0x1fff;
+ else if (eht_mcs_nss->rx_tx_mcs11_max_nss)
+ eht_mcs_map = 0x7ff;
+ else
+ eht_mcs_map = 0x1ff;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(mask->control[band].eht_mcs); i++) {
+ if (mask->control[band].eht_mcs[i] == 0)
+ continue;
+
+ if (mask->control[band].eht_mcs[i] < eht_mcs_map)
+ eht_nss_mask |= BIT(i);
+ else
+ return false;
+ }
+
+ if (ht_nss_mask != vht_nss_mask || ht_nss_mask != he_nss_mask ||
+ ht_nss_mask != eht_nss_mask)
return false;
if (ht_nss_mask == 0)
@@ -11977,7 +12373,8 @@ ath12k_mac_get_single_legacy_rate(struct ath12k *ar,
}
static int
-ath12k_mac_set_fixed_rate_gi_ltf(struct ath12k_link_vif *arvif, u8 he_gi, u8 he_ltf)
+ath12k_mac_set_fixed_rate_gi_ltf(struct ath12k_link_vif *arvif, u8 gi, u8 ltf,
+ u32 param)
{
struct ath12k *ar = arvif->ar;
int ret;
@@ -11985,47 +12382,54 @@ ath12k_mac_set_fixed_rate_gi_ltf(struct ath12k_link_vif *arvif, u8 he_gi, u8 he_
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
/* 0.8 = 0, 1.6 = 2 and 3.2 = 3. */
- if (he_gi && he_gi != 0xFF)
- he_gi += 1;
+ if (gi && gi != 0xFF)
+ gi += 1;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
- WMI_VDEV_PARAM_SGI, he_gi);
+ WMI_VDEV_PARAM_SGI, gi);
if (ret) {
- ath12k_warn(ar->ab, "failed to set HE GI:%d, error:%d\n",
- he_gi, ret);
+ ath12k_warn(ar->ab, "failed to set GI:%d, error:%d\n",
+ gi, ret);
return ret;
}
- /* start from 1 */
- if (he_ltf != 0xFF)
- he_ltf += 1;
+
+ if (param == WMI_VDEV_PARAM_HE_LTF) {
+ /* HE values start from 1 */
+ if (ltf != 0xFF)
+ ltf += 1;
+ } else {
+ /* EHT values start from 5 */
+ if (ltf != 0xFF)
+ ltf += 4;
+ }
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
- WMI_VDEV_PARAM_HE_LTF, he_ltf);
+ param, ltf);
if (ret) {
- ath12k_warn(ar->ab, "failed to set HE LTF:%d, error:%d\n",
- he_ltf, ret);
+ ath12k_warn(ar->ab, "failed to set LTF:%d, error:%d\n",
+ ltf, ret);
return ret;
}
return 0;
}
static int
-ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 he_gi, u8 he_ltf)
+ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 gi, u8 ltf)
{
struct ath12k *ar = arvif->ar;
int ret;
- u32 he_ar_gi_ltf;
+ u32 ar_gi_ltf;
- if (he_gi != 0xFF) {
- switch (he_gi) {
- case NL80211_RATE_INFO_HE_GI_0_8:
- he_gi = WMI_AUTORATE_800NS_GI;
+ if (gi != 0xFF) {
+ switch (gi) {
+ case ATH12K_RATE_INFO_GI_0_8:
+ gi = WMI_AUTORATE_800NS_GI;
break;
- case NL80211_RATE_INFO_HE_GI_1_6:
- he_gi = WMI_AUTORATE_1600NS_GI;
+ case ATH12K_RATE_INFO_GI_1_6:
+ gi = WMI_AUTORATE_1600NS_GI;
break;
- case NL80211_RATE_INFO_HE_GI_3_2:
- he_gi = WMI_AUTORATE_3200NS_GI;
+ case ATH12K_RATE_INFO_GI_3_2:
+ gi = WMI_AUTORATE_3200NS_GI;
break;
default:
ath12k_warn(ar->ab, "Invalid GI\n");
@@ -12033,16 +12437,16 @@ ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 he_gi, u8 he_
}
}
- if (he_ltf != 0xFF) {
- switch (he_ltf) {
- case NL80211_RATE_INFO_HE_1XLTF:
- he_ltf = WMI_HE_AUTORATE_LTF_1X;
+ if (ltf != 0xFF) {
+ switch (ltf) {
+ case ATH12K_RATE_INFO_1XLTF:
+ ltf = WMI_AUTORATE_LTF_1X;
break;
- case NL80211_RATE_INFO_HE_2XLTF:
- he_ltf = WMI_HE_AUTORATE_LTF_2X;
+ case ATH12K_RATE_INFO_2XLTF:
+ ltf = WMI_AUTORATE_LTF_2X;
break;
- case NL80211_RATE_INFO_HE_4XLTF:
- he_ltf = WMI_HE_AUTORATE_LTF_4X;
+ case ATH12K_RATE_INFO_4XLTF:
+ ltf = WMI_AUTORATE_LTF_4X;
break;
default:
ath12k_warn(ar->ab, "Invalid LTF\n");
@@ -12050,15 +12454,15 @@ ath12k_mac_set_auto_rate_gi_ltf(struct ath12k_link_vif *arvif, u16 he_gi, u8 he_
}
}
- he_ar_gi_ltf = he_gi | he_ltf;
+ ar_gi_ltf = gi | ltf;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
WMI_VDEV_PARAM_AUTORATE_MISC_CFG,
- he_ar_gi_ltf);
+ ar_gi_ltf);
if (ret) {
ath12k_warn(ar->ab,
- "failed to set HE autorate GI:%u, LTF:%u params, error:%d\n",
- he_gi, he_ltf, ret);
+ "failed to set autorate GI:%u, LTF:%u params, error:%d\n",
+ gi, ltf, ret);
return ret;
}
@@ -12079,14 +12483,16 @@ static u32 ath12k_mac_nlgi_to_wmigi(enum nl80211_txrate_gi gi)
static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
u32 rate, u8 nss, u8 sgi, u8 ldpc,
- u8 he_gi, u8 he_ltf, bool he_fixed_rate)
+ u8 he_gi, u8 he_ltf, bool he_fixed_rate,
+ u8 eht_gi, u8 eht_ltf,
+ bool eht_fixed_rate)
{
struct ieee80211_bss_conf *link_conf;
struct ath12k *ar = arvif->ar;
+ bool he_support, eht_support, gi_ltf_set = false;
u32 vdev_param;
u32 param_value;
int ret;
- bool he_support;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@@ -12095,6 +12501,7 @@ static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
return -EINVAL;
he_support = link_conf->he_support;
+ eht_support = link_conf->eht_support;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
"mac set rate params vdev %i rate 0x%02x nss 0x%02x sgi 0x%02x ldpc 0x%02x\n",
@@ -12104,7 +12511,11 @@ static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
"he_gi 0x%02x he_ltf 0x%02x he_fixed_rate %d\n", he_gi,
he_ltf, he_fixed_rate);
- if (!he_support) {
+ ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
+ "eht_gi 0x%02x eht_ltf 0x%02x eht_fixed_rate %d\n",
+ eht_gi, eht_ltf, eht_fixed_rate);
+
+ if (!he_support && !eht_support) {
vdev_param = WMI_VDEV_PARAM_FIXED_RATE;
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
vdev_param, rate);
@@ -12133,14 +12544,34 @@ static int ath12k_mac_set_rate_params(struct ath12k_link_vif *arvif,
return ret;
}
+ if (eht_support) {
+ if (eht_fixed_rate)
+ ret = ath12k_mac_set_fixed_rate_gi_ltf(arvif, eht_gi, eht_ltf,
+ WMI_VDEV_PARAM_EHT_LTF);
+ else
+ ret = ath12k_mac_set_auto_rate_gi_ltf(arvif, eht_gi, eht_ltf);
+
+ if (ret) {
+ ath12k_warn(ar->ab,
+ "failed to set EHT LTF/GI params %d/%d: %d\n",
+ eht_gi, eht_ltf, ret);
+ return ret;
+ }
+ gi_ltf_set = true;
+ }
+
if (he_support) {
if (he_fixed_rate)
- ret = ath12k_mac_set_fixed_rate_gi_ltf(arvif, he_gi, he_ltf);
+ ret = ath12k_mac_set_fixed_rate_gi_ltf(arvif, he_gi, he_ltf,
+ WMI_VDEV_PARAM_HE_LTF);
else
ret = ath12k_mac_set_auto_rate_gi_ltf(arvif, he_gi, he_ltf);
if (ret)
return ret;
- } else {
+ gi_ltf_set = true;
+ }
+
+ if (!gi_ltf_set) {
vdev_param = WMI_VDEV_PARAM_SGI;
param_value = ath12k_mac_nlgi_to_wmigi(sgi);
ret = ath12k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id,
@@ -12205,6 +12636,38 @@ ath12k_mac_he_mcs_range_present(struct ath12k *ar,
return true;
}
+static bool
+ath12k_mac_eht_mcs_range_present(struct ath12k *ar,
+ enum nl80211_band band,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ u16 eht_mcs;
+ int i;
+
+ for (i = 0; i < NL80211_EHT_NSS_MAX; i++) {
+ eht_mcs = mask->control[band].eht_mcs[i];
+
+ switch (eht_mcs) {
+ case 0:
+ case BIT(8) - 1:
+ case BIT(10) - 1:
+ case BIT(12) - 1:
+ case BIT(14) - 1:
+ break;
+ case BIT(15) - 1:
+ case BIT(16) - 1:
+ case BIT(16) - BIT(14) - 1:
+ if (i != 0)
+ return false;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
static void ath12k_mac_set_bitrate_mask_iter(void *data,
struct ieee80211_sta *sta)
{
@@ -12259,15 +12722,16 @@ ath12k_mac_validate_fixed_rate_settings(struct ath12k *ar, enum nl80211_band ban
const struct cfg80211_bitrate_mask *mask,
unsigned int link_id)
{
- bool he_fixed_rate = false, vht_fixed_rate = false;
- const u16 *vht_mcs_mask, *he_mcs_mask;
+ bool eht_fixed_rate = false, he_fixed_rate = false, vht_fixed_rate = false;
+ const u16 *vht_mcs_mask, *he_mcs_mask, *eht_mcs_mask;
struct ieee80211_link_sta *link_sta;
struct ath12k_peer *peer, *tmp;
- u8 vht_nss, he_nss;
+ u8 vht_nss, he_nss, eht_nss;
int ret = true;
vht_mcs_mask = mask->control[band].vht_mcs;
he_mcs_mask = mask->control[band].he_mcs;
+ eht_mcs_mask = mask->control[band].eht_mcs;
if (ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask) == 1)
vht_fixed_rate = true;
@@ -12275,11 +12739,15 @@ ath12k_mac_validate_fixed_rate_settings(struct ath12k *ar, enum nl80211_band ban
if (ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask) == 1)
he_fixed_rate = true;
- if (!vht_fixed_rate && !he_fixed_rate)
+ if (ath12k_mac_bitrate_mask_num_eht_rates(ar, band, mask) == 1)
+ eht_fixed_rate = true;
+
+ if (!vht_fixed_rate && !he_fixed_rate && !eht_fixed_rate)
return true;
vht_nss = ath12k_mac_max_vht_nss(vht_mcs_mask);
he_nss = ath12k_mac_max_he_nss(he_mcs_mask);
+ eht_nss = ath12k_mac_max_eht_nss(eht_mcs_mask);
rcu_read_lock();
spin_lock_bh(&ar->ab->base_lock);
@@ -12301,6 +12769,11 @@ ath12k_mac_validate_fixed_rate_settings(struct ath12k *ar, enum nl80211_band ban
ret = false;
goto exit;
}
+ if (eht_fixed_rate && (!link_sta->eht_cap.has_eht ||
+ link_sta->rx_nss < eht_nss)) {
+ ret = false;
+ goto exit;
+ }
}
}
exit:
@@ -12322,8 +12795,10 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
const u8 *ht_mcs_mask;
const u16 *vht_mcs_mask;
const u16 *he_mcs_mask;
+ const u16 *eht_mcs_mask;
u8 he_ltf = 0;
u8 he_gi = 0;
+ u8 eht_ltf = 0, eht_gi = 0;
u32 rate;
u8 nss, mac_nss;
u8 sgi;
@@ -12332,6 +12807,7 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
int ret;
int num_rates;
bool he_fixed_rate = false;
+ bool eht_fixed_rate = false;
lockdep_assert_wiphy(hw->wiphy);
@@ -12347,6 +12823,7 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
ht_mcs_mask = mask->control[band].ht_mcs;
vht_mcs_mask = mask->control[band].vht_mcs;
he_mcs_mask = mask->control[band].he_mcs;
+ eht_mcs_mask = mask->control[band].eht_mcs;
ldpc = !!(ar->ht_cap_info & WMI_HT_CAP_LDPC);
sgi = mask->control[band].gi;
@@ -12358,6 +12835,9 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
he_gi = mask->control[band].he_gi;
he_ltf = mask->control[band].he_ltf;
+ eht_gi = mask->control[band].eht_gi;
+ eht_ltf = mask->control[band].eht_ltf;
+
/* mac80211 doesn't support sending a fixed HT/VHT MCS alone, rather it
* requires passing at least one of used basic rates along with them.
* Fixed rate setting across different preambles(legacy, HT, VHT) is
@@ -12395,9 +12875,10 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
ath12k_warn(ar->ab,
"failed to update fixed rate settings due to mcs/nss incompatibility\n");
- mac_nss = max3(ath12k_mac_max_ht_nss(ht_mcs_mask),
- ath12k_mac_max_vht_nss(vht_mcs_mask),
- ath12k_mac_max_he_nss(he_mcs_mask));
+ mac_nss = max(max3(ath12k_mac_max_ht_nss(ht_mcs_mask),
+ ath12k_mac_max_vht_nss(vht_mcs_mask),
+ ath12k_mac_max_he_nss(he_mcs_mask)),
+ ath12k_mac_max_eht_nss(eht_mcs_mask));
nss = min_t(u32, ar->num_tx_chains, mac_nss);
/* If multiple rates across different preambles are given
@@ -12445,6 +12926,20 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
ret = -EINVAL;
goto out;
}
+
+ num_rates = ath12k_mac_bitrate_mask_num_eht_rates(ar, band,
+ mask);
+ if (num_rates == 1)
+ eht_fixed_rate = true;
+
+ if (!ath12k_mac_eht_mcs_range_present(ar, band, mask) &&
+ num_rates > 1) {
+ ath12k_warn(ar->ab,
+ "Setting more than one EHT MCS Value in bitrate mask not supported\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
ieee80211_iterate_stations_mtx(hw,
ath12k_mac_disable_peer_fixed_rate,
arvif);
@@ -12456,7 +12951,8 @@ ath12k_mac_op_set_bitrate_mask(struct ieee80211_hw *hw,
}
ret = ath12k_mac_set_rate_params(arvif, rate, nss, sgi, ldpc, he_gi,
- he_ltf, he_fixed_rate);
+ he_ltf, he_fixed_rate, eht_gi, eht_ltf,
+ eht_fixed_rate);
if (ret) {
ath12k_warn(ar->ab, "failed to set rate params on vdev %i: %d\n",
arvif->vdev_id, ret);
@@ -12711,14 +13207,18 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw,
if (!signal &&
ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
- !(ath12k_mac_get_fw_stats(ar, &params)))
+ !(ath12k_mac_get_fw_stats(ar, &params))) {
signal = arsta->rssi_beacon;
+ ath12k_fw_stats_reset(ar);
+ }
params.stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT;
if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) &&
ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
- !(ath12k_mac_get_fw_stats(ar, &params)))
+ !(ath12k_mac_get_fw_stats(ar, &params))) {
ath12k_mac_put_chain_rssi(sinfo, arsta);
+ ath12k_fw_stats_reset(ar);
+ }
spin_lock_bh(&ar->data_lock);
noise_floor = ath12k_pdev_get_noise_floor(ar);
@@ -12802,8 +13302,10 @@ static void ath12k_mac_op_link_sta_statistics(struct ieee80211_hw *hw,
if (!signal &&
ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA &&
- !(ath12k_mac_get_fw_stats(ar, &params)))
+ !(ath12k_mac_get_fw_stats(ar, &params))) {
signal = arsta->rssi_beacon;
+ ath12k_fw_stats_reset(ar);
+ }
if (signal) {
link_sinfo->signal =
@@ -12905,6 +13407,7 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
if (ret) {
ath12k_warn(ar->ab, "unable to create scan vdev for roc: %d\n",
ret);
+ ath12k_mac_unassign_link_vif(arvif);
return ret;
}
}
@@ -13904,6 +14407,11 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT);
+ if (test_bit(WMI_TLV_SERVICE_BSS_COLOR_OFFLOAD,
+ ab->wmi_ab.svc_map)) {
+ wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR);
+ ieee80211_hw_set(hw, DETECTS_COLOR_COLLISION);
+ }
wiphy->cipher_suites = cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
index c05af40bd7a2..1f689e367c8a 100644
--- a/drivers/net/wireless/ath/ath12k/mac.h
+++ b/drivers/net/wireless/ath/ath12k/mac.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH12K_MAC_H
@@ -84,6 +84,18 @@ enum ath12k_supported_bw {
ATH12K_BW_320 = 4,
};
+enum ath12k_gi {
+ ATH12K_RATE_INFO_GI_0_8,
+ ATH12K_RATE_INFO_GI_1_6,
+ ATH12K_RATE_INFO_GI_3_2,
+};
+
+enum ath12k_ltf {
+ ATH12K_RATE_INFO_1XLTF,
+ ATH12K_RATE_INFO_2XLTF,
+ ATH12K_RATE_INFO_4XLTF,
+};
+
struct ath12k_mac_get_any_chanctx_conf_arg {
struct ath12k *ar;
struct ieee80211_chanctx_conf *chanctx_conf;
diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c
index c729d5526c75..a12c8379cb46 100644
--- a/drivers/net/wireless/ath/ath12k/pci.c
+++ b/drivers/net/wireless/ath/ath12k/pci.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/module.h>
@@ -218,6 +218,19 @@ static inline bool ath12k_pci_is_offset_within_mhi_region(u32 offset)
return (offset >= PCI_MHIREGLEN_REG && offset <= PCI_MHI_REGION_END);
}
+static void ath12k_pci_restore_window(struct ath12k_base *ab)
+{
+ struct ath12k_pci *ab_pci = ath12k_pci_priv(ab);
+
+ spin_lock_bh(&ab_pci->window_lock);
+
+ iowrite32(WINDOW_ENABLE_BIT | ab_pci->register_window,
+ ab->mem + WINDOW_REG_ADDRESS);
+ ioread32(ab->mem + WINDOW_REG_ADDRESS);
+
+ spin_unlock_bh(&ab_pci->window_lock);
+}
+
static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
{
u32 val, delay;
@@ -242,6 +255,11 @@ static void ath12k_pci_soc_global_reset(struct ath12k_base *ab)
val = ath12k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET);
if (val == 0xffffffff)
ath12k_warn(ab, "link down error during global reset\n");
+
+ /* Restore window register as its content is cleared during
+ * hardware global reset, such that it aligns with host cache.
+ */
+ ath12k_pci_restore_window(ab);
}
static void ath12k_pci_clear_dbg_registers(struct ath12k_base *ab)
@@ -1871,3 +1889,7 @@ void ath12k_pci_exit(void)
{
pci_unregister_driver(&ath12k_pci_driver);
}
+
+/* firmware files */
+MODULE_FIRMWARE(ATH12K_FW_DIR "/QCN9274/hw2.0/*");
+MODULE_FIRMWARE(ATH12K_FW_DIR "/WCN7850/hw2.0/*");
diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c
index 36325e62aa24..b7c48b6706df 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.c
+++ b/drivers/net/wireless/ath/ath12k/qmi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/elf.h>
@@ -3114,9 +3114,10 @@ static void ath12k_qmi_m3_free(struct ath12k_base *ab)
if (!m3_mem->vaddr)
return;
- dma_free_coherent(ab->dev, m3_mem->size,
+ dma_free_coherent(ab->dev, m3_mem->total_size,
m3_mem->vaddr, m3_mem->paddr);
m3_mem->vaddr = NULL;
+ m3_mem->total_size = 0;
m3_mem->size = 0;
}
@@ -3152,7 +3153,7 @@ static int ath12k_qmi_m3_load(struct ath12k_base *ab)
/* In recovery/resume cases, M3 buffer is not freed, try to reuse that */
if (m3_mem->vaddr) {
- if (m3_mem->size >= m3_len)
+ if (m3_mem->total_size >= m3_len)
goto skip_m3_alloc;
/* Old buffer is too small, free and reallocate */
@@ -3164,11 +3165,13 @@ static int ath12k_qmi_m3_load(struct ath12k_base *ab)
GFP_KERNEL);
if (!m3_mem->vaddr) {
ath12k_err(ab, "failed to allocate memory for M3 with size %zu\n",
- fw->size);
+ m3_len);
ret = -ENOMEM;
goto out;
}
+ m3_mem->total_size = m3_len;
+
skip_m3_alloc:
memcpy(m3_mem->vaddr, m3_data, m3_len);
m3_mem->size = m3_len;
@@ -3740,7 +3743,7 @@ static int ath12k_qmi_ops_new_server(struct qmi_handle *qmi_hdl,
sq->sq_node = service->node;
sq->sq_port = service->port;
- ret = kernel_connect(qmi_hdl->sock, (struct sockaddr *)sq,
+ ret = kernel_connect(qmi_hdl->sock, (struct sockaddr_unsized *)sq,
sizeof(*sq), 0);
if (ret) {
ath12k_warn(ab, "qmi failed to connect to remote service %d\n", ret);
diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h
index 4767d9a2e309..7a88268aa1e9 100644
--- a/drivers/net/wireless/ath/ath12k/qmi.h
+++ b/drivers/net/wireless/ath/ath12k/qmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH12K_QMI_H
@@ -120,6 +120,9 @@ struct target_info {
};
struct m3_mem_region {
+ /* total memory allocated */
+ u32 total_size;
+ /* actual memory being used */
u32 size;
dma_addr_t paddr;
void *vaddr;
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index ff6b3d4ea820..be8b2943094f 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/skbuff.h>
#include <linux/ctype.h>
@@ -14,6 +14,7 @@
#include <linux/uuid.h>
#include <linux/time.h>
#include <linux/of.h>
+#include <linux/cleanup.h>
#include "core.h"
#include "debugfs.h"
#include "debug.h"
@@ -190,6 +191,8 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = {
.min_len = sizeof(struct wmi_11d_new_cc_event) },
[WMI_TAG_PER_CHAIN_RSSI_STATS] = {
.min_len = sizeof(struct wmi_per_chain_rssi_stat_params) },
+ [WMI_TAG_OBSS_COLOR_COLLISION_EVT] = {
+ .min_len = sizeof(struct wmi_obss_color_collision_event) },
};
__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len)
@@ -2367,10 +2370,13 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar,
cmd->peer_bw_rxnss_override |= cpu_to_le32(arg->peer_bw_rxnss_override);
if (arg->vht_capable) {
- mcs->rx_max_rate = cpu_to_le32(arg->rx_max_rate);
- mcs->rx_mcs_set = cpu_to_le32(arg->rx_mcs_set);
- mcs->tx_max_rate = cpu_to_le32(arg->tx_max_rate);
- mcs->tx_mcs_set = cpu_to_le32(arg->tx_mcs_set);
+ /* Firmware interprets mcs->tx_mcs_set field as peer's
+ * RX capability
+ */
+ mcs->rx_max_rate = cpu_to_le32(arg->tx_max_rate);
+ mcs->rx_mcs_set = cpu_to_le32(arg->tx_mcs_set);
+ mcs->tx_max_rate = cpu_to_le32(arg->rx_max_rate);
+ mcs->tx_mcs_set = cpu_to_le32(arg->rx_mcs_set);
}
/* HE Rates */
@@ -3848,6 +3854,58 @@ int ath12k_wmi_fils_discovery(struct ath12k *ar, u32 vdev_id, u32 interval,
}
static void
+ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *skb)
+{
+ const struct wmi_obss_color_collision_event *ev;
+ struct ath12k_link_vif *arvif;
+ u32 vdev_id, evt_type;
+ u64 bitmap;
+
+ const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n",
+ PTR_ERR(tb));
+ return;
+ }
+
+ ev = tb[WMI_TAG_OBSS_COLOR_COLLISION_EVT];
+ if (!ev) {
+ ath12k_warn(ab, "failed to fetch OBSS color collision event\n");
+ return;
+ }
+
+ vdev_id = le32_to_cpu(ev->vdev_id);
+ evt_type = le32_to_cpu(ev->evt_type);
+ bitmap = le64_to_cpu(ev->obss_color_bitmap);
+
+ guard(rcu)();
+
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_id);
+ if (!arvif) {
+ ath12k_warn(ab, "no arvif found for vdev %u in OBSS color collision event\n",
+ vdev_id);
+ return;
+ }
+
+ switch (evt_type) {
+ case WMI_BSS_COLOR_COLLISION_DETECTION:
+ ieee80211_obss_color_collision_notify(arvif->ahvif->vif,
+ bitmap,
+ arvif->link_id);
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "obss color collision detected vdev %u event %d bitmap %016llx\n",
+ vdev_id, evt_type, bitmap);
+ break;
+ case WMI_BSS_COLOR_COLLISION_DISABLE:
+ case WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY:
+ case WMI_BSS_COLOR_FREE_SLOT_AVAILABLE:
+ break;
+ default:
+ ath12k_warn(ab, "unknown OBSS color collision event type %d\n", evt_type);
+ }
+}
+
+static void
ath12k_fill_band_to_mac_param(struct ath12k_base *soc,
struct ath12k_wmi_pdev_band_arg *arg)
{
@@ -7011,12 +7069,26 @@ static void ath12k_vdev_start_resp_event(struct ath12k_base *ab, struct sk_buff
static void ath12k_bcn_tx_status_event(struct ath12k_base *ab, struct sk_buff *skb)
{
+ struct ath12k_link_vif *arvif;
+ struct ath12k *ar;
u32 vdev_id, tx_status;
if (ath12k_pull_bcn_tx_status_ev(ab, skb, &vdev_id, &tx_status) != 0) {
ath12k_warn(ab, "failed to extract bcn tx status");
return;
}
+
+ guard(rcu)();
+
+ arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_id);
+ if (!arvif) {
+ ath12k_warn(ab, "invalid vdev %u in bcn tx status\n",
+ vdev_id);
+ return;
+ }
+
+ ar = arvif->ar;
+ wiphy_work_queue(ath12k_ar_to_hw(ar)->wiphy, &arvif->bcn_tx_work);
}
static void ath12k_vdev_stopped_event(struct ath12k_base *ab, struct sk_buff *skb)
@@ -8017,8 +8089,6 @@ void ath12k_wmi_fw_stats_dump(struct ath12k *ar,
buf[len - 1] = 0;
else
buf[len] = 0;
-
- ath12k_fw_stats_reset(ar);
}
static void
@@ -8415,18 +8485,10 @@ static void ath12k_wmi_fw_stats_process(struct ath12k *ar,
ath12k_warn(ab, "empty beacon stats");
return;
}
- /* Mark end until we reached the count of all started VDEVs
- * within the PDEV
- */
- if (ar->num_started_vdevs)
- is_end = ((++ar->fw_stats.num_bcn_recvd) ==
- ar->num_started_vdevs);
list_splice_tail_init(&stats->bcn,
&ar->fw_stats.bcn);
-
- if (is_end)
- complete(&ar->fw_stats_done);
+ complete(&ar->fw_stats_done);
}
}
@@ -9874,6 +9936,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
case WMI_PDEV_RSSI_DBM_CONVERSION_PARAMS_INFO_EVENTID:
ath12k_wmi_rssi_dbm_conversion_params_info_event(ab, skb);
break;
+ case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
+ ath12k_wmi_obss_color_collision_event(ab, skb);
+ break;
/* add Unsupported events (rare) here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
@@ -9884,7 +9949,6 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
/* add Unsupported events (frequent) here */
case WMI_PDEV_GET_HALPHY_CAL_STATUS_EVENTID:
case WMI_MGMT_RX_FW_CONSUMED_EVENTID:
- case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
/* debug might flood hence silently ignore (no-op) */
break;
case WMI_PDEV_UTF_EVENTID:
diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h
index a8c3190e8ad9..f99fced1610e 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#ifndef ATH12K_WMI_H
@@ -223,15 +223,15 @@ enum WMI_HOST_WLAN_BAND {
};
/* Parameters used for WMI_VDEV_PARAM_AUTORATE_MISC_CFG command.
- * Used only for HE auto rate mode.
+ * Used for HE and EHT auto rate mode.
*/
enum {
- /* HE LTF related configuration */
- WMI_HE_AUTORATE_LTF_1X = BIT(0),
- WMI_HE_AUTORATE_LTF_2X = BIT(1),
- WMI_HE_AUTORATE_LTF_4X = BIT(2),
+ /* LTF related configuration */
+ WMI_AUTORATE_LTF_1X = BIT(0),
+ WMI_AUTORATE_LTF_2X = BIT(1),
+ WMI_AUTORATE_LTF_4X = BIT(2),
- /* HE GI related configuration */
+ /* GI related configuration */
WMI_AUTORATE_400NS_GI = BIT(8),
WMI_AUTORATE_800NS_GI = BIT(9),
WMI_AUTORATE_1600NS_GI = BIT(10),
@@ -1197,6 +1197,7 @@ enum wmi_tlv_vdev_param {
WMI_VDEV_PARAM_SET_HEMU_MODE,
WMI_VDEV_PARAM_HEOPS_0_31 = 0x8003,
WMI_VDEV_PARAM_SET_EHT_MU_MODE = 0x8005,
+ WMI_VDEV_PARAM_EHT_LTF,
};
enum wmi_tlv_peer_flags {
@@ -3609,20 +3610,6 @@ struct ath12k_wmi_scan_cancel_arg {
u32 pdev_id;
};
-struct wmi_bcn_send_from_host_cmd {
- __le32 tlv_header;
- __le32 vdev_id;
- __le32 data_len;
- union {
- __le32 frag_ptr;
- __le32 frag_ptr_lo;
- };
- __le32 frame_ctrl;
- __le32 dtim_flag;
- __le32 bcn_antenna;
- __le32 frag_ptr_hi;
-};
-
#define WMI_CHAN_INFO_MODE GENMASK(5, 0)
#define WMI_CHAN_INFO_HT40_PLUS BIT(6)
#define WMI_CHAN_INFO_PASSIVE BIT(7)
@@ -4218,8 +4205,10 @@ struct wmi_unit_test_cmd {
struct ath12k_wmi_vht_rate_set_params {
__le32 tlv_header;
__le32 rx_max_rate;
+ /* MCS at which the peer can transmit */
__le32 rx_mcs_set;
__le32 tx_max_rate;
+ /* MCS at which the peer can receive */
__le32 tx_mcs_set;
__le32 tx_max_mcs_nss;
} __packed;
@@ -4940,6 +4929,24 @@ struct wmi_obss_spatial_reuse_params_cmd {
#define ATH12K_BSS_COLOR_STA_PERIODS 10000
#define ATH12K_BSS_COLOR_AP_PERIODS 5000
+/**
+ * enum wmi_bss_color_collision - Event types for BSS color collision handling
+ * @WMI_BSS_COLOR_COLLISION_DISABLE: Indicates that BSS color collision detection
+ * is disabled.
+ * @WMI_BSS_COLOR_COLLISION_DETECTION: Event triggered when a BSS color collision
+ * is detected.
+ * @WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY: Event indicating that the timer for waiting
+ * on a free BSS color slot has expired.
+ * @WMI_BSS_COLOR_FREE_SLOT_AVAILABLE: Event indicating that a free BSS color slot
+ * has become available.
+ */
+enum wmi_bss_color_collision {
+ WMI_BSS_COLOR_COLLISION_DISABLE = 0,
+ WMI_BSS_COLOR_COLLISION_DETECTION,
+ WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY,
+ WMI_BSS_COLOR_FREE_SLOT_AVAILABLE,
+};
+
struct wmi_obss_color_collision_cfg_params_cmd {
__le32 tlv_header;
__le32 vdev_id;
@@ -4957,6 +4964,12 @@ struct wmi_bss_color_change_enable_params_cmd {
__le32 enable;
} __packed;
+struct wmi_obss_color_collision_event {
+ __le32 vdev_id;
+ __le32 evt_type;
+ __le64 obss_color_bitmap;
+} __packed;
+
#define ATH12K_IPV4_TH_SEED_SIZE 5
#define ATH12K_IPV6_TH_SEED_SIZE 11
diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c
index dce9bd0bcaef..e8481626f194 100644
--- a/drivers/net/wireless/ath/ath12k/wow.c
+++ b/drivers/net/wireless/ath/ath12k/wow.c
@@ -758,6 +758,7 @@ static int ath12k_wow_arp_ns_offload(struct ath12k *ar, bool enable)
if (ret) {
ath12k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n",
arvif->vdev_id, enable, ret);
+ kfree(offload);
return ret;
}
}
diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h
index d3a9d00e65e1..ef9ea4ff891b 100644
--- a/drivers/net/wireless/ath/wcn36xx/hal.h
+++ b/drivers/net/wireless/ath/wcn36xx/hal.h
@@ -4484,80 +4484,6 @@ struct set_rssi_filter_resp {
u32 status;
};
-/* Update scan params - sent from host to PNO to be used during PNO
- * scanningx */
-struct wcn36xx_hal_update_scan_params_req {
-
- struct wcn36xx_hal_msg_header header;
-
- /* Host setting for 11d */
- u8 dot11d_enabled;
-
- /* Lets PNO know that host has determined the regulatory domain */
- u8 dot11d_resolved;
-
- /* Channels on which PNO is allowed to scan */
- u8 channel_count;
- u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS];
-
- /* Minimum channel time */
- u16 active_min_ch_time;
-
- /* Maximum channel time */
- u16 active_max_ch_time;
-
- /* Minimum channel time */
- u16 passive_min_ch_time;
-
- /* Maximum channel time */
- u16 passive_max_ch_time;
-
- /* Cb State */
- enum phy_chan_bond_state state;
-} __packed;
-
-/* Update scan params - sent from host to PNO to be used during PNO
- * scanningx */
-struct wcn36xx_hal_update_scan_params_req_ex {
-
- struct wcn36xx_hal_msg_header header;
-
- /* Host setting for 11d */
- u8 dot11d_enabled;
-
- /* Lets PNO know that host has determined the regulatory domain */
- u8 dot11d_resolved;
-
- /* Channels on which PNO is allowed to scan */
- u8 channel_count;
- u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX];
-
- /* Minimum channel time */
- u16 active_min_ch_time;
-
- /* Maximum channel time */
- u16 active_max_ch_time;
-
- /* Minimum channel time */
- u16 passive_min_ch_time;
-
- /* Maximum channel time */
- u16 passive_max_ch_time;
-
- /* Cb State */
- enum phy_chan_bond_state state;
-} __packed;
-
-/* Update scan params - sent from host to PNO to be used during PNO
- * scanningx */
-struct wcn36xx_hal_update_scan_params_resp {
-
- struct wcn36xx_hal_msg_header header;
-
- /* status of the request */
- u32 status;
-} __packed;
-
struct wcn36xx_hal_set_tx_per_tracking_req_msg {
struct wcn36xx_hal_msg_header header;
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c
index 2cf86fc3f8fe..136acc414714 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.c
+++ b/drivers/net/wireless/ath/wcn36xx/smd.c
@@ -1127,66 +1127,6 @@ out_nomem:
return ret;
}
-static int wcn36xx_smd_update_scan_params_rsp(void *buf, size_t len)
-{
- struct wcn36xx_hal_update_scan_params_resp *rsp;
-
- rsp = buf;
-
- /* Remove the PNO version bit */
- rsp->status &= (~(WCN36XX_FW_MSG_PNO_VERSION_MASK));
-
- if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) {
- wcn36xx_warn("error response from update scan\n");
- return rsp->status;
- }
-
- return 0;
-}
-
-int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn,
- u8 *channels, size_t channel_count)
-{
- struct wcn36xx_hal_update_scan_params_req_ex msg_body;
- int ret;
-
- mutex_lock(&wcn->hal_mutex);
- INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ);
-
- msg_body.dot11d_enabled = false;
- msg_body.dot11d_resolved = true;
-
- msg_body.channel_count = channel_count;
- memcpy(msg_body.channels, channels, channel_count);
- msg_body.active_min_ch_time = 60;
- msg_body.active_max_ch_time = 120;
- msg_body.passive_min_ch_time = 60;
- msg_body.passive_max_ch_time = 110;
- msg_body.state = PHY_SINGLE_CHANNEL_CENTERED;
-
- PREPARE_HAL_BUF(wcn->hal_buf, msg_body);
-
- wcn36xx_dbg(WCN36XX_DBG_HAL,
- "hal update scan params channel_count %d\n",
- msg_body.channel_count);
-
- ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
- if (ret) {
- wcn36xx_err("Sending hal_update_scan_params failed\n");
- goto out;
- }
- ret = wcn36xx_smd_update_scan_params_rsp(wcn->hal_buf,
- wcn->hal_rsp_len);
- if (ret) {
- wcn36xx_err("hal_update_scan_params response failed err=%d\n",
- ret);
- goto out;
- }
-out:
- mutex_unlock(&wcn->hal_mutex);
- return ret;
-}
-
static int wcn36xx_smd_add_sta_self_rsp(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
void *buf,
diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h
index 2c1ed9e570bf..4e39df5589b3 100644
--- a/drivers/net/wireless/ath/wcn36xx/smd.h
+++ b/drivers/net/wireless/ath/wcn36xx/smd.h
@@ -66,7 +66,6 @@ int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode,
int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode,
struct ieee80211_vif *vif);
-int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn, u8 *channels, size_t channel_count);
int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
int wcn36xx_smd_stop_hw_scan(struct wcn36xx *wcn);
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index f521af575e9b..c866cfd144c7 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -458,6 +458,5 @@ void wil_pm_runtime_put(struct wil6210_priv *wil)
{
struct device *dev = wil_to_dev(wil);
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 8afaffe31031..bb96b87b2a6e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -5627,8 +5627,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
*cookie, le16_to_cpu(action_frame->len),
le32_to_cpu(af_params->channel));
- ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
- af_params);
+ ack = brcmf_p2p_send_action_frame(vif->ifp, af_params);
cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
GFP_KERNEL);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c
index c3a602197662..abe7f6501e5e 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c
@@ -24,6 +24,10 @@ static const struct brcmf_dmi_data acepc_t8_data = {
BRCM_CC_4345_CHIP_ID, 6, "acepc-t8"
};
+static const struct brcmf_dmi_data acer_a1_840_data = {
+ BRCM_CC_43340_CHIP_ID, 2, "acer-a1-840"
+};
+
/* The Chuwi Hi8 Pro uses the same Ampak AP6212 module as the Chuwi Vi8 Plus
* and the nvram for the Vi8 Plus is already in linux-firmware, so use that.
*/
@@ -92,6 +96,16 @@ static const struct dmi_system_id dmi_platform_data[] = {
.driver_data = (void *)&acepc_t8_data,
},
{
+ /* Acer Iconia One 8 A1-840 (non FHD version) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "BayTrail"),
+ /* Above strings are too generic also match BIOS date */
+ DMI_MATCH(DMI_BIOS_DATE, "04/01/2014"),
+ },
+ .driver_data = (void *)&acer_a1_840_data,
+ },
+ {
/* Chuwi Hi8 Pro with D2D3_Hi8Pro.233 BIOS */
.matches = {
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Hampoo"),
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
index 0dc9d28cd77b..e1752a513c73 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
@@ -1529,6 +1529,7 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
/**
* brcmf_p2p_tx_action_frame() - send action frame over fil.
*
+ * @ifp: interface to transmit on.
* @p2p: p2p info struct for vif.
* @af_params: action frame data/info.
*
@@ -1538,12 +1539,11 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
* The WLC_E_ACTION_FRAME_COMPLETE event will be received when the action
* frame is transmitted.
*/
-static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
+static s32 brcmf_p2p_tx_action_frame(struct brcmf_if *ifp,
+ struct brcmf_p2p_info *p2p,
struct brcmf_fil_af_params_le *af_params)
{
struct brcmf_pub *drvr = p2p->cfg->pub;
- struct brcmf_cfg80211_vif *vif;
- struct brcmf_p2p_action_frame *p2p_af;
s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
@@ -1552,14 +1552,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p,
clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status);
clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status);
- /* check if it is a p2p_presence response */
- p2p_af = (struct brcmf_p2p_action_frame *)af_params->action_frame.data;
- if (p2p_af->subtype == P2P_AF_PRESENCE_RSP)
- vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif;
- else
- vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
-
- err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params,
+ err = brcmf_fil_bsscfg_data_set(ifp, "actframe", af_params,
sizeof(*af_params));
if (err) {
bphy_err(drvr, " sending action frame has failed\n");
@@ -1711,16 +1704,14 @@ static bool brcmf_p2p_check_dwell_overflow(u32 requested_dwell,
/**
* brcmf_p2p_send_action_frame() - send action frame .
*
- * @cfg: driver private data for cfg80211 interface.
- * @ndev: net device to transmit on.
+ * @ifp: interface to transmit on.
* @af_params: configuration data for action frame.
*/
-bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev,
+bool brcmf_p2p_send_action_frame(struct brcmf_if *ifp,
struct brcmf_fil_af_params_le *af_params)
{
+ struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
struct brcmf_p2p_info *p2p = &cfg->p2p;
- struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_fil_action_frame_le *action_frame;
struct brcmf_config_af_params config_af_params;
struct afx_hdl *afx_hdl = &p2p->afx_hdl;
@@ -1857,7 +1848,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
if (af_params->channel)
msleep(P2P_AF_RETRY_DELAY_TIME);
- ack = !brcmf_p2p_tx_action_frame(p2p, af_params);
+ ack = !brcmf_p2p_tx_action_frame(ifp, p2p, af_params);
tx_retry++;
dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell,
dwell_jiffies);
@@ -2217,7 +2208,6 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
WARN_ON(p2p_ifp->bsscfgidx != bsscfgidx);
- init_completion(&p2p->send_af_done);
INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
init_completion(&p2p->afx_hdl.act_frm_scan);
init_completion(&p2p->wait_next_af);
@@ -2513,6 +2503,8 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced)
pri_ifp = brcmf_get_ifp(cfg->pub, 0);
p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
+ init_completion(&p2p->send_af_done);
+
if (p2pdev_forced) {
err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL);
if (IS_ERR(err_ptr)) {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
index d2ecee565bf2..d3137ebd7158 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h
@@ -168,8 +168,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp,
const struct brcmf_event_msg *e,
void *data);
-bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
- struct net_device *ndev,
+bool brcmf_p2p_send_action_frame(struct brcmf_if *ifp,
struct brcmf_fil_af_params_le *af_params);
bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg,
struct brcmf_bss_info_le *bi);
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
index 215814861cbd..c7c5bc0f1650 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c
@@ -2143,7 +2143,7 @@ static void isr_indicate_rf_kill(struct ipw2100_priv *priv, u32 status)
/* Make sure the RF Kill check timer is running */
priv->stop_rf_kill = 0;
- mod_delayed_work(system_wq, &priv->rf_kill, round_jiffies_relative(HZ));
+ mod_delayed_work(system_percpu_wq, &priv->rf_kill, round_jiffies_relative(HZ));
}
static void ipw2100_scan_event(struct work_struct *work)
@@ -2170,7 +2170,7 @@ static void isr_scan_complete(struct ipw2100_priv *priv, u32 status)
round_jiffies_relative(msecs_to_jiffies(4000)));
} else {
priv->user_requested_scan = 0;
- mod_delayed_work(system_wq, &priv->scan_event, 0);
+ mod_delayed_work(system_percpu_wq, &priv->scan_event, 0);
}
}
@@ -4252,7 +4252,7 @@ static int ipw_radio_kill_sw(struct ipw2100_priv *priv, int disable_radio)
"disabled by HW switch\n");
/* Make sure the RF_KILL check timer is running */
priv->stop_rf_kill = 0;
- mod_delayed_work(system_wq, &priv->rf_kill,
+ mod_delayed_work(system_percpu_wq, &priv->rf_kill,
round_jiffies_relative(HZ));
} else
schedule_reset(priv);
diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
index 24a5624ef207..09035a77e775 100644
--- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c
+++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c
@@ -4415,7 +4415,7 @@ static void handle_scan_event(struct ipw_priv *priv)
round_jiffies_relative(msecs_to_jiffies(4000)));
} else {
priv->user_requested_scan = 0;
- mod_delayed_work(system_wq, &priv->scan_event, 0);
+ mod_delayed_work(system_percpu_wq, &priv->scan_event, 0);
}
}
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
index ca488931a33c..f0453f3f6ba6 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c
@@ -38,7 +38,6 @@ static const struct iwl_family_base_params iwl_22000_base = {
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
.apmg_not_supported = true,
.mac_addr_from_csr = 0x380,
- .min_umac_error_event_table = 0x400000,
.d3_debug_data_base_addr = 0x401000,
.d3_debug_data_length = 60 * 1024,
.mon_smem_regs = {
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c
index b56574006ee0..3c844cd419e8 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c
@@ -50,7 +50,6 @@ static const struct iwl_family_base_params iwl8000_base = {
.smem_offset = IWL8260_SMEM_OFFSET,
.smem_len = IWL8260_SMEM_LEN,
.apmg_not_supported = true,
- .min_umac_error_event_table = 0x800000,
};
static const struct iwl_tt_params iwl8000_tt_params = {
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
index ac1fa291cf2f..5872fc9b8caf 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c
@@ -41,7 +41,6 @@ static const struct iwl_family_base_params iwl9000_base = {
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
.apmg_not_supported = true,
.mac_addr_from_csr = 0x380,
- .min_umac_error_event_table = 0x800000,
.d3_debug_data_base_addr = 0x401000,
.d3_debug_data_length = 92 * 1024,
.nvm_hw_section_num = 10,
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c
index ddf3d313da5a..582f61661062 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c
@@ -33,7 +33,6 @@ static const struct iwl_family_base_params iwl_ax210_base = {
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
.apmg_not_supported = true,
.mac_addr_from_csr = 0x380,
- .min_umac_error_event_table = 0x400000,
.d3_debug_data_base_addr = 0x401000,
.d3_debug_data_length = 60 * 1024,
.mon_smem_regs = {
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
index 3e6206e739f6..d25445bd1e5c 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_BZ_UCODE_CORE_MAX 99
+#define IWL_BZ_UCODE_CORE_MAX 101
/* Lowest firmware API version supported */
#define IWL_BZ_UCODE_API_MIN 100
@@ -38,7 +38,6 @@ static const struct iwl_family_base_params iwl_bz_base = {
.smem_len = IWL_BZ_SMEM_LEN,
.apmg_not_supported = true,
.mac_addr_from_csr = 0x30,
- .min_umac_error_event_table = 0xD0000,
.d3_debug_data_base_addr = 0x401000,
.d3_debug_data_length = 60 * 1024,
.mon_smem_regs = {
@@ -90,6 +89,7 @@ const struct iwl_mac_cfg iwl_bz_mac_cfg = {
.low_latency_xtal = true,
.ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US,
};
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_bz_mac_cfg);
const struct iwl_mac_cfg iwl_gl_mac_cfg = {
.device_family = IWL_DEVICE_FAMILY_BZ,
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
index e53a785686c8..a279dcfd3083 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
@@ -9,7 +9,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_DR_UCODE_CORE_MAX 99
+#define IWL_DR_UCODE_CORE_MAX 101
/* Lowest firmware API version supported */
#define IWL_DR_UCODE_API_MIN 100
@@ -33,7 +33,6 @@ static const struct iwl_family_base_params iwl_dr_base = {
.smem_len = IWL_DR_SMEM_LEN,
.apmg_not_supported = true,
.mac_addr_from_csr = 0x30,
- .min_umac_error_event_table = 0xD0000,
.d3_debug_data_base_addr = 0x401000,
.d3_debug_data_length = 60 * 1024,
.mon_smem_regs = {
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c
index 456a666c8dfd..fd82050e33a3 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c
@@ -19,6 +19,7 @@
.non_shared_ant = ANT_B, \
.vht_mu_mimo_supported = true, \
.uhb_supported = true, \
+ .eht_supported = true, \
.num_rbds = IWL_NUM_RBDS_EHT, \
.nvm_ver = IWL_FM_NVM_VERSION, \
.nvm_type = IWL_NVM_EXT
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
index 483f21659eff..408b9850bd10 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c
@@ -12,5 +12,6 @@ const char iwl_killer_bn1850i_name[] =
"Killer(R) Wi-Fi 8 BN1850i 320MHz Wireless Network Adapter (BN201.NGW)";
const char iwl_bn201_name[] = "Intel(R) Wi-Fi 8 BN201";
+const char iwl_bn203_name[] = "Intel(R) Wi-Fi 8 BN203";
const char iwl_be221_name[] = "Intel(R) Wi-Fi 7 BE221";
const char iwl_be223_name[] = "Intel(R) Wi-Fi 7 BE223";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c
index 97735175cb0e..b5803ea1eb78 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c
@@ -4,8 +4,31 @@
*/
#include "iwl-config.h"
+/* NVM versions */
+#define IWL_WH_NVM_VERSION 0x0a1d
+
+#define IWL_DEVICE_WH \
+ .ht_params = { \
+ .stbc = true, \
+ .ldpc = true, \
+ .ht40_bands = BIT(NL80211_BAND_2GHZ) | \
+ BIT(NL80211_BAND_5GHZ), \
+ }, \
+ .led_mode = IWL_LED_RF_STATE, \
+ .non_shared_ant = ANT_B, \
+ .vht_mu_mimo_supported = true, \
+ .uhb_supported = true, \
+ .num_rbds = IWL_NUM_RBDS_EHT, \
+ .nvm_ver = IWL_WH_NVM_VERSION, \
+ .nvm_type = IWL_NVM_EXT
+
/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
+const struct iwl_rf_cfg iwl_rf_wh_non_eht = {
+ IWL_DEVICE_WH,
+ .eht_supported = false,
+};
+
const char iwl_killer_be1775s_name[] =
"Killer(R) Wi-Fi 7 BE1775s 320MHz Wireless Network Adapter (BE211D2W)";
const char iwl_killer_be1775i_name[] =
@@ -13,3 +36,4 @@ const char iwl_killer_be1775i_name[] =
const char iwl_be211_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz";
const char iwl_be213_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz";
+const char iwl_ax221_name[] = "Intel(R) Wi-Fi 6E AX221 160MHz";
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
index e9449b59114a..ee00b2af7a1d 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
@@ -10,7 +10,7 @@
#include "fw/api/txq.h"
/* Highest firmware core release supported */
-#define IWL_SC_UCODE_CORE_MAX 99
+#define IWL_SC_UCODE_CORE_MAX 101
/* Lowest firmware API version supported */
#define IWL_SC_UCODE_API_MIN 100
@@ -41,7 +41,6 @@ static const struct iwl_family_base_params iwl_sc_base = {
.smem_len = IWL_SC_SMEM_LEN,
.apmg_not_supported = true,
.mac_addr_from_csr = 0x30,
- .min_umac_error_event_table = 0xD0000,
.d3_debug_data_base_addr = 0x401000,
.d3_debug_data_length = 60 * 1024,
.mon_smem_regs = {
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
index 20bc6671f4eb..06cece4ea6d9 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h
@@ -151,6 +151,7 @@ union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev,
* @mcc: output buffer (3 bytes) that will get the MCC
*
* This function tries to read the current MCC from ACPI if available.
+ * Return: 0 on success, or a negative error code
*/
int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc);
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
index ad5b95cad0bf..ea2ba4b4cb7b 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h
@@ -88,7 +88,7 @@ struct iwl_imr_alive_info {
__le32 enabled;
} __packed; /* IMR_ALIVE_INFO_API_S_VER_1 */
-struct iwl_alive_ntf_v6 {
+struct iwl_alive_ntf_v7 {
__le16 status;
__le16 flags;
struct iwl_lmac_alive lmac_data[2];
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h b/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h
index d130d4f85444..073f003bdc5d 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/cmdhdr.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014 Intel Corporation
+ * Copyright (C) 2005-2014, 2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -98,7 +98,7 @@ struct iwl_cmd_header {
} __packed;
/**
- * struct iwl_cmd_header_wide
+ * struct iwl_cmd_header_wide - wide command header
*
* This header format appears in the beginning of each command sent from the
* driver, and each response/notification received from uCode.
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h
index ddc84430d895..616f00a8b603 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2023-2024 Intel Corporation
+ * Copyright (C) 2023-2025 Intel Corporation
* Copyright (C) 2013-2014, 2018-2019 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2017 Intel Deutschland GmbH
@@ -52,7 +52,7 @@ struct iwl_bt_coex_cmd {
} __packed; /* BT_COEX_CMD_API_S_VER_6 */
/**
- * struct iwl_bt_coex_reduced_txp_update_cmd
+ * struct iwl_bt_coex_reduced_txp_update_cmd - reduced TX power command
* @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
* bits are the sta_id (value)
*/
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
index 997b0c9ce984..8d64a271bb94 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h
@@ -60,7 +60,7 @@ enum iwl_legacy_cmds {
* @UCODE_ALIVE_NTFY:
* Alive data from the firmware, as described in
* &struct iwl_alive_ntf_v3 or &struct iwl_alive_ntf_v4 or
- * &struct iwl_alive_ntf_v5 or &struct iwl_alive_ntf_v6.
+ * &struct iwl_alive_ntf_v5 or &struct iwl_alive_ntf_v7.
*/
UCODE_ALIVE_NTFY = 0x1,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
index b1c6ee8ae2df..6a6e11a57dbf 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
@@ -124,6 +124,11 @@ enum iwl_data_path_subcmd_ids {
BEACON_FILTER_IN_NOTIF = 0xF8,
/**
+ * @PHY_AIR_SNIFFER_NOTIF: &struct iwl_rx_phy_air_sniffer_ntfy
+ */
+ PHY_AIR_SNIFFER_NOTIF = 0xF9,
+
+ /**
* @STA_PM_NOTIF: &struct iwl_mvm_pm_state_notification
*/
STA_PM_NOTIF = 0xFD,
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
index 3173fa96cb48..b62f0687327a 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
@@ -16,7 +16,7 @@
#define IWL_FW_INI_PRESET_DISABLE 0xff
/**
- * struct iwl_fw_ini_hcmd
+ * struct iwl_fw_ini_hcmd - debug configuration host command
*
* @id: the debug configuration command type for instance: 0xf6 / 0xf5 / DHC
* @group: the desired cmd group
@@ -199,7 +199,7 @@ struct iwl_fw_ini_region_tlv {
} __packed; /* FW_TLV_DEBUG_REGION_API_S_VER_1 */
/**
- * struct iwl_fw_ini_debug_info_tlv
+ * struct iwl_fw_ini_debug_info_tlv - debug info TLV
*
* debug configuration name for a specific image
*
@@ -311,7 +311,7 @@ struct iwl_fw_ini_conf_set_tlv {
} __packed; /* FW_TLV_DEBUG_CONFIG_SET_API_S_VER_1 */
/**
- * enum iwl_fw_ini_config_set_type
+ * enum iwl_fw_ini_config_set_type - configuration set type
*
* @IWL_FW_INI_CONFIG_SET_TYPE_INVALID: invalid config set
* @IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_PERIPHERY_MAC: for PERIPHERY MAC configuration
@@ -337,7 +337,7 @@ enum iwl_fw_ini_config_set_type {
} __packed;
/**
- * enum iwl_fw_ini_allocation_id
+ * enum iwl_fw_ini_allocation_id - allocation ID
*
* @IWL_FW_INI_ALLOCATION_INVALID: invalid
* @IWL_FW_INI_ALLOCATION_ID_DBGC1: allocation meant for DBGC1 configuration
@@ -356,7 +356,7 @@ enum iwl_fw_ini_allocation_id {
}; /* FW_DEBUG_TLV_ALLOCATION_ID_E_VER_1 */
/**
- * enum iwl_fw_ini_buffer_location
+ * enum iwl_fw_ini_buffer_location - buffer location
*
* @IWL_FW_INI_LOCATION_INVALID: invalid
* @IWL_FW_INI_LOCATION_SRAM_PATH: SRAM location
@@ -373,7 +373,7 @@ enum iwl_fw_ini_buffer_location {
}; /* FW_DEBUG_TLV_BUFFER_LOCATION_E_VER_1 */
/**
- * enum iwl_fw_ini_region_type
+ * enum iwl_fw_ini_region_type - region type
*
* @IWL_FW_INI_REGION_INVALID: invalid
* @IWL_FW_INI_REGION_TLV: uCode and debug TLVs
@@ -437,7 +437,7 @@ enum iwl_fw_ini_region_device_memory_subtype {
}; /* FW_TLV_DEBUG_REGION_DEVICE_MEMORY_SUBTYPE_API_E */
/**
- * enum iwl_fw_ini_time_point
+ * enum iwl_fw_ini_time_point - time point type
*
* Hard coded time points in which the driver can send hcmd or perform dump
* collection
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h
index 0cf1e5124fba..61a850de26fc 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h
@@ -421,7 +421,7 @@ struct iwl_dbgc1_info {
} __packed; /* INIT_DRAM_FRAGS_ALLOCATIONS_S_VER_1 */
/**
- * struct iwl_dbg_host_event_cfg_cmd
+ * struct iwl_dbg_host_event_cfg_cmd - host event config command
* @enabled_severities: enabled severities
*/
struct iwl_dbg_host_event_cfg_cmd {
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h
index 33541f92c7c7..2ee3a48aa5df 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h
@@ -1092,7 +1092,7 @@ struct iwl_tof_range_req_ap_entry {
} __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_9 */
/**
- * enum iwl_tof_response_mode
+ * enum iwl_tof_response_mode - TOF response mode
* @IWL_MVM_TOF_RESPONSE_ASAP: report each AP measurement separately as soon as
* possible (not supported for this release)
* @IWL_MVM_TOF_RESPONSE_TIMEOUT: report all AP measurements as a batch upon
@@ -1108,7 +1108,7 @@ enum iwl_tof_response_mode {
};
/**
- * enum iwl_tof_initiator_flags
+ * enum iwl_tof_initiator_flags - TOF initiator flags
*
* @IWL_TOF_INITIATOR_FLAGS_FAST_ALGO_DISABLED: disable fast algo, meaning run
* the algo on ant A+B, instead of only one of them.
@@ -1409,7 +1409,7 @@ enum iwl_tof_range_request_status {
};
/**
- * enum iwl_tof_entry_status
+ * enum iwl_tof_entry_status - TOF entry status
*
* @IWL_TOF_ENTRY_SUCCESS: successful measurement.
* @IWL_TOF_ENTRY_GENERAL_FAILURE: General failure.
@@ -1856,7 +1856,7 @@ struct iwl_tof_mcsi_notif {
} __packed;
/**
- * struct iwl_tof_range_abort_cmd
+ * struct iwl_tof_range_abort_cmd - TOF range abort command
* @request_id: corresponds to a range request
* @reserved: reserved
*/
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
index e90f3187e55c..4644fc1aa1ec 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h
@@ -18,13 +18,8 @@ enum iwl_regulatory_and_nvm_subcmd_ids {
/**
* @LARI_CONFIG_CHANGE: &struct iwl_lari_config_change_cmd_v1,
- * &struct iwl_lari_config_change_cmd_v2,
- * &struct iwl_lari_config_change_cmd_v3,
- * &struct iwl_lari_config_change_cmd_v4,
- * &struct iwl_lari_config_change_cmd_v5,
* &struct iwl_lari_config_change_cmd_v6,
- * &struct iwl_lari_config_change_cmd_v7,
- * &struct iwl_lari_config_change_cmd_v10 or
+ * &struct iwl_lari_config_change_cmd_v8,
* &struct iwl_lari_config_change_cmd
*/
LARI_CONFIG_CHANGE = 0x1,
@@ -565,74 +560,6 @@ struct iwl_lari_config_change_cmd_v1 {
} __packed; /* LARI_CHANGE_CONF_CMD_S_VER_1 */
/**
- * struct iwl_lari_config_change_cmd_v2 - change LARI configuration
- * @config_bitmap: bit map of the config commands. each bit will trigger a
- * different predefined FW config operation
- * @oem_uhb_allow_bitmap: bitmap of UHB enabled MCC sets
- */
-struct iwl_lari_config_change_cmd_v2 {
- __le32 config_bitmap;
- __le32 oem_uhb_allow_bitmap;
-} __packed; /* LARI_CHANGE_CONF_CMD_S_VER_2 */
-
-/**
- * struct iwl_lari_config_change_cmd_v3 - change LARI configuration
- * @config_bitmap: bit map of the config commands. each bit will trigger a
- * different predefined FW config operation
- * @oem_uhb_allow_bitmap: bitmap of UHB enabled MCC sets
- * @oem_11ax_allow_bitmap: bitmap of 11ax allowed MCCs.
- * For each supported country, a pair of regulatory override bit and 11ax mode exist
- * in the bit field.
- */
-struct iwl_lari_config_change_cmd_v3 {
- __le32 config_bitmap;
- __le32 oem_uhb_allow_bitmap;
- __le32 oem_11ax_allow_bitmap;
-} __packed; /* LARI_CHANGE_CONF_CMD_S_VER_3 */
-
-/**
- * struct iwl_lari_config_change_cmd_v4 - change LARI configuration
- * @config_bitmap: Bitmap of the config commands. Each bit will trigger a
- * different predefined FW config operation.
- * @oem_uhb_allow_bitmap: Bitmap of UHB enabled MCC sets.
- * @oem_11ax_allow_bitmap: Bitmap of 11ax allowed MCCs. There are two bits
- * per country, one to indicate whether to override and the other to
- * indicate the value to use.
- * @oem_unii4_allow_bitmap: Bitmap of unii4 allowed MCCs.There are two bits
- * per country, one to indicate whether to override and the other to
- * indicate allow/disallow unii4 channels.
- */
-struct iwl_lari_config_change_cmd_v4 {
- __le32 config_bitmap;
- __le32 oem_uhb_allow_bitmap;
- __le32 oem_11ax_allow_bitmap;
- __le32 oem_unii4_allow_bitmap;
-} __packed; /* LARI_CHANGE_CONF_CMD_S_VER_4 */
-
-/**
- * struct iwl_lari_config_change_cmd_v5 - change LARI configuration
- * @config_bitmap: Bitmap of the config commands. Each bit will trigger a
- * different predefined FW config operation.
- * @oem_uhb_allow_bitmap: Bitmap of UHB enabled MCC sets.
- * @oem_11ax_allow_bitmap: Bitmap of 11ax allowed MCCs. There are two bits
- * per country, one to indicate whether to override and the other to
- * indicate the value to use.
- * @oem_unii4_allow_bitmap: Bitmap of unii4 allowed MCCs.There are two bits
- * per country, one to indicate whether to override and the other to
- * indicate allow/disallow unii4 channels.
- * @chan_state_active_bitmap: Bitmap for overriding channel state to active.
- * Each bit represents a country or region to activate, according to the BIOS
- * definitions.
- */
-struct iwl_lari_config_change_cmd_v5 {
- __le32 config_bitmap;
- __le32 oem_uhb_allow_bitmap;
- __le32 oem_11ax_allow_bitmap;
- __le32 oem_unii4_allow_bitmap;
- __le32 chan_state_active_bitmap;
-} __packed; /* LARI_CHANGE_CONF_CMD_S_VER_5 */
-
-/**
* struct iwl_lari_config_change_cmd_v6 - change LARI configuration
* @config_bitmap: Bitmap of the config commands. Each bit will trigger a
* different predefined FW config operation.
@@ -659,8 +586,7 @@ struct iwl_lari_config_change_cmd_v6 {
} __packed; /* LARI_CHANGE_CONF_CMD_S_VER_6 */
/**
- * struct iwl_lari_config_change_cmd_v7 - change LARI configuration
- * This structure is used also for lari cmd version 8 and 9.
+ * struct iwl_lari_config_change_cmd_v8 - change LARI configuration
* @config_bitmap: Bitmap of the config commands. Each bit will trigger a
* different predefined FW config operation.
* @oem_uhb_allow_bitmap: Bitmap of UHB enabled MCC sets.
@@ -670,21 +596,19 @@ struct iwl_lari_config_change_cmd_v6 {
* @oem_unii4_allow_bitmap: Bitmap of unii4 allowed MCCs.There are two bits
* per country, one to indicate whether to override and the other to
* indicate allow/disallow unii4 channels.
- * For LARI cmd version 4 to 8 - bits 0:3 are supported.
- * For LARI cmd version 9 - bits 0:5 are supported.
+ * bit 0 - 3: supported.
* @chan_state_active_bitmap: Bitmap to enable different bands per country
* or region.
* Each bit represents a country or region, and a band to activate
* according to the BIOS definitions.
- * For LARI cmd version 7 - bits 0:3 are supported.
- * For LARI cmd version 8 - bits 0:4 are supported.
+ * bit 0 - 4: supported.
* @force_disable_channels_bitmap: Bitmap of disabled bands/channels.
* Each bit represents a set of channels in a specific band that should be
* disabled
* @edt_bitmap: Bitmap of energy detection threshold table.
* Disable/enable the EDT optimization method for different band.
*/
-struct iwl_lari_config_change_cmd_v7 {
+struct iwl_lari_config_change_cmd_v8 {
__le32 config_bitmap;
__le32 oem_uhb_allow_bitmap;
__le32 oem_11ax_allow_bitmap;
@@ -693,48 +617,8 @@ struct iwl_lari_config_change_cmd_v7 {
__le32 force_disable_channels_bitmap;
__le32 edt_bitmap;
} __packed;
-/* LARI_CHANGE_CONF_CMD_S_VER_7 */
/* LARI_CHANGE_CONF_CMD_S_VER_8 */
-/* LARI_CHANGE_CONF_CMD_S_VER_9 */
-/**
- * struct iwl_lari_config_change_cmd_v10 - change LARI configuration
- * @config_bitmap: Bitmap of the config commands. Each bit will trigger a
- * different predefined FW config operation.
- * @oem_uhb_allow_bitmap: Bitmap of UHB enabled MCC sets.
- * @oem_11ax_allow_bitmap: Bitmap of 11ax allowed MCCs. There are two bits
- * per country, one to indicate whether to override and the other to
- * indicate the value to use.
- * @oem_unii4_allow_bitmap: Bitmap of unii4 allowed MCCs.There are two bits
- * per country, one to indicate whether to override and the other to
- * indicate allow/disallow unii4 channels.
- * For LARI cmd version 10 - bits 0:5 are supported.
- * @chan_state_active_bitmap: Bitmap to enable different bands per country
- * or region.
- * Each bit represents a country or region, and a band to activate
- * according to the BIOS definitions.
- * For LARI cmd version 10 - bits 0:4 are supported.
- * @force_disable_channels_bitmap: Bitmap of disabled bands/channels.
- * Each bit represents a set of channels in a specific band that should be
- * disabled
- * @edt_bitmap: Bitmap of energy detection threshold table.
- * Disable/enable the EDT optimization method for different band.
- * @oem_320mhz_allow_bitmap: 320Mhz bandwidth enablement bitmap per MCC.
- * bit0: enable 320Mhz in Japan.
- * bit1: enable 320Mhz in South Korea.
- * bit 2 - 31: reserved.
- */
-struct iwl_lari_config_change_cmd_v10 {
- __le32 config_bitmap;
- __le32 oem_uhb_allow_bitmap;
- __le32 oem_11ax_allow_bitmap;
- __le32 oem_unii4_allow_bitmap;
- __le32 chan_state_active_bitmap;
- __le32 force_disable_channels_bitmap;
- __le32 edt_bitmap;
- __le32 oem_320mhz_allow_bitmap;
-} __packed;
-/* LARI_CHANGE_CONF_CMD_S_VER_10 */
/**
* struct iwl_lari_config_change_cmd - change LARI configuration
@@ -747,14 +631,11 @@ struct iwl_lari_config_change_cmd_v10 {
* @oem_unii4_allow_bitmap: Bitmap of unii4 allowed MCCs.There are two bits
* per country, one to indicate whether to override and the other to
* indicate allow/disallow unii4 channels.
- * For LARI cmd version 11 - bits 0:5 are supported.
* @chan_state_active_bitmap: Bitmap to enable different bands per country
* or region.
* Each bit represents a country or region, and a band to activate
* according to the BIOS definitions.
- * For LARI cmd version 11 - bits 0:4 are supported.
- * For LARI cmd version 12 - bits 0:6 are supported and bits 7:31 are
- * reserved.
+ * bit 0 - 6: supported.
* @force_disable_channels_bitmap: Bitmap of disabled bands/channels.
* Each bit represents a set of channels in a specific band that should be
* disabled
@@ -781,12 +662,11 @@ struct iwl_lari_config_change_cmd {
__le32 oem_320mhz_allow_bitmap;
__le32 oem_11be_allow_bitmap;
} __packed;
-/* LARI_CHANGE_CONF_CMD_S_VER_11 */
/* LARI_CHANGE_CONF_CMD_S_VER_12 */
/* Activate UNII-1 (5.2GHz) for World Wide */
#define ACTIVATE_5G2_IN_WW_MASK BIT(4)
-#define CHAN_STATE_ACTIVE_BITMAP_CMD_V11 0x1F
+#define CHAN_STATE_ACTIVE_BITMAP_CMD_V8 0x1F
#define CHAN_STATE_ACTIVE_BITMAP_CMD_V12 0x7F
/**
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
index 5eb8d10678fd..535864e22626 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
@@ -620,7 +620,7 @@ struct iwl_sar_offset_mapping_cmd {
} __packed; /*SAR_OFFSET_MAPPING_TABLE_CMD_API_S*/
/**
- * struct iwl_beacon_filter_cmd
+ * struct iwl_beacon_filter_cmd - beacon filter command
* REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
* @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon
* to driver if delta in Energy values calculated for this and last
@@ -762,7 +762,7 @@ enum iwl_6ghz_ap_type {
}; /* PHY_AP_TYPE_API_E_VER_1 */
/**
- * struct iwl_txpower_constraints_cmd
+ * struct iwl_txpower_constraints_cmd - TX power constraints command
* AP_TX_POWER_CONSTRAINTS_CMD
* Used for VLP/LPI/AFC Access Point power constraints for 6GHz channels
* @link_id: linkId
@@ -786,4 +786,5 @@ struct iwl_txpower_constraints_cmd {
__s8 psd_pwr[IWL_MAX_TX_EIRP_PSD_PWR_MAX_SIZE];
u8 reserved[3];
} __packed; /* PHY_AP_TX_POWER_CONSTRAINTS_CMD_API_S_VER_1 */
+
#endif /* __iwl_fw_api_power_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
index d751789998ac..3ed7e0807b90 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h
@@ -262,6 +262,7 @@ enum iwl_rx_mpdu_reorder_data {
};
enum iwl_rx_mpdu_phy_info {
+ IWL_RX_MPDU_PHY_EOF_INDICATION = BIT(0),
IWL_RX_MPDU_PHY_AMPDU = BIT(5),
IWL_RX_MPDU_PHY_AMPDU_TOGGLE = BIT(6),
IWL_RX_MPDU_PHY_SHORT_PREAMBLE = BIT(7),
@@ -1041,4 +1042,289 @@ struct iwl_beacon_filter_notif {
__le32 link_id;
} __packed; /* BEACON_FILTER_IN_NTFY_API_S_VER_2 */
+union iwl_legacy_sig {
+#define OFDM_RX_LEGACY_LENGTH 0x00000fff
+#define OFDM_RX_RATE 0x0000f000
+ __le32 ofdm;
+#define CCK_CRFR_SHORT_PREAMBLE 0x00000040
+ __le32 cck;
+};
+
+struct iwl_ht_sigs {
+#define OFDM_RX_FRAME_HT_MCS 0x0000007f
+#define OFDM_RX_FRAME_HT_BANDWIDTH 0x00000080
+#define OFDM_RX_FRAME_HT_LENGTH 0x03ffff00
+ __le32 a1;
+ __le32 a2;
+};
+
+struct iwl_vht_sigs {
+#define OFDM_RX_FRAME_VHT_NUM_OF_DATA_SYM 0x000007ff
+#define OFDM_RX_FRAME_VHT_NUM_OF_DATA_SYM_VALID 0x80000000
+ __le32 a0;
+ __le32 a1, a2;
+};
+
+struct iwl_he_sigs {
+#define OFDM_RX_FRAME_HE_BEAM_CHANGE 0x00000001
+#define OFDM_RX_FRAME_HE_UL_FLAG 0x00000002
+#define OFDM_RX_FRAME_HE_MCS 0x0000003c
+#define OFDM_RX_FRAME_HE_DCM 0x00000040
+#define OFDM_RX_FRAME_HE_BSS_COLOR 0x00001f80
+#define OFDM_RX_FRAME_HE_SPATIAL_REUSE 0x0001e000
+#define OFDM_RX_FRAME_HE_BANDWIDTH 0x00060000
+#define OFDM_RX_FRAME_HE_SU_EXT_BW10 0x00080000
+#define OFDM_RX_FRAME_HE_GI_LTF_TYPE 0x00700000
+#define OFDM_RX_FRAME_HE_NSTS 0x03800000
+#define OFDM_RX_FRAME_HE_PRMBL_PUNC_TYPE 0x0c000000
+ __le32 a1;
+#define OFDM_RX_FRAME_HE_TXOP_DURATION 0x0000007f
+#define OFDM_RX_FRAME_HE_CODING 0x00000080
+#define OFDM_RX_FRAME_HE_CODING_EXTRA_SYM 0x00000100
+#define OFDM_RX_FRAME_HE_STBC 0x00000200
+#define OFDM_RX_FRAME_HE_BF 0x00000400
+#define OFDM_RX_FRAME_HE_PRE_FEC_PAD_FACTOR 0x00001800
+#define OFDM_RX_FRAME_HE_PE_DISAMBIG 0x00002000
+#define OFDM_RX_FRAME_HE_DOPPLER 0x00004000
+#define OFDM_RX_FRAME_HE_TYPE 0x00038000
+#define OFDM_RX_FRAME_HE_MU_NUM_OF_SIGB_SYM_OR_USER_NUM 0x003c0000
+#define OFDM_RX_FRAME_HE_MU_SIGB_COMP 0x00400000
+#define OFDM_RX_FRAME_HE_MU_NUM_OF_LTF_SYM 0x03800000
+ __le32 a2;
+#define OFDM_RX_FRAME_HE_NUM_OF_DATA_SYM 0x000007ff
+#define OFDM_RX_FRAME_HE_PE_DURATION 0x00003800
+#define OFDM_RX_FRAME_HE_NUM_OF_DATA_SYM_VALID 0x80000000
+ __le32 a3;
+#define OFDM_RX_FRAME_HE_SIGB_STA_ID_FOUND 0x00000001
+#define OFDM_RX_FRAME_HE_SIGB_STA_ID_INDX 0x0000000e
+#define OFDM_RX_FRAME_HE_SIGB_NSTS 0x00000070
+#define OFDM_RX_FRAME_HE_SIGB_BF 0x00000080
+#define OFDM_RX_FRAME_HE_SIGB_MCS 0x00000f00
+#define OFDM_RX_FRAME_HE_SIGB_DCM 0x00001000
+#define OFDM_RX_FRAME_HE_SIGB_CODING 0x00002000
+#define OFDM_RX_FRAME_HE_SIGB_SPATIAL_CONFIG 0x0003c000
+#define OFDM_RX_FRAME_HE_SIGB_STA_RU 0x03fc0000
+#define OFDM_RX_FRAME_HE_SIGB_NUM_OF_SYM 0x3c000000
+#define OFDM_RX_FRAME_HE_SIGB_CRC_OK 0x40000000
+ __le32 b;
+/* index 0 */
+#define OFDM_RX_FRAME_HE_RU_ALLOC_0_A1 0x000000ff
+#define OFDM_RX_FRAME_HE_RU_ALLOC_0_A2 0x0000ff00
+#define OFDM_RX_FRAME_HE_RU_ALLOC_0_B1 0x00ff0000
+#define OFDM_RX_FRAME_HE_RU_ALLOC_0_B2 0xff000000
+/* index 1 */
+#define OFDM_RX_FRAME_HE_RU_ALLOC_1_C1 0x000000ff
+#define OFDM_RX_FRAME_HE_RU_ALLOC_1_C2 0x0000ff00
+#define OFDM_RX_FRAME_HE_RU_ALLOC_1_D1 0x00ff0000
+#define OFDM_RX_FRAME_HE_RU_ALLOC_1_D2 0xff000000
+/* index 2 */
+#define OFDM_RX_FRAME_HE_CENTER_RU_CC1 0x00000001
+#define OFDM_RX_FRAME_HE_CENTER_RU_CC2 0x00000002
+#define OFDM_RX_FRAME_HE_COMMON_CC1_CRC_OK 0x00000004
+#define OFDM_RX_FRAME_HE_COMMON_CC2_CRC_OK 0x00000008
+ __le32 cmn[3];
+};
+
+struct iwl_he_tb_sigs {
+#define OFDM_RX_HE_TRIG_FORMAT 0x00000001
+#define OFDM_RX_HE_TRIG_BSS_COLOR 0x0000007e
+#define OFDM_RX_HE_TRIG_SPATIAL_REUSE_1 0x00000780
+#define OFDM_RX_HE_TRIG_SPATIAL_REUSE_2 0x00007800
+#define OFDM_RX_HE_TRIG_SPATIAL_REUSE_3 0x00078000
+#define OFDM_RX_HE_TRIG_SPATIAL_REUSE_4 0x00780000
+#define OFDM_RX_HE_TRIG_BANDWIDTH 0x03000000
+ __le32 a1;
+#define OFDM_RX_HE_TRIG_TXOP_DURATION 0x0000007f
+#define OFDM_RX_HE_TRIG_SIG2_RESERVED 0x0000ff80
+#define OFDM_RX_HE_TRIG_FORMAT_ERR 0x08000000
+#define OFDM_RX_HE_TRIG_BW_ERR 0x10000000
+#define OFDM_RX_HE_TRIG_LEGACY_LENGTH_ERR 0x20000000
+#define OFDM_RX_HE_TRIG_CRC_OK 0x40000000
+ __le32 a2;
+#define OFDM_UCODE_TRIG_BASE_RX_LGCY_LENGTH 0x00000fff
+#define OFDM_UCODE_TRIG_BASE_RX_BANDWIDTH 0x00007000
+#define OFDM_UCODE_TRIG_BASE_PS160 0x00008000
+#define OFDM_UCODE_EHT_TRIG_CONTROL_CHANNEL 0x000f0000
+ __le32 tb_rx0;
+#define OFDM_UCODE_TRIG_BASE_RX_MCS 0x0000000f
+#define OFDM_UCODE_TRIG_BASE_RX_DCM 0x00000010
+#define OFDM_UCODE_TRIG_BASE_RX_GI_LTF_TYPE 0x00000060
+#define OFDM_UCODE_TRIG_BASE_RX_NSTS 0x00000380
+#define OFDM_UCODE_TRIG_BASE_RX_CODING 0x00000400
+#define OFDM_UCODE_TRIG_BASE_RX_CODING_EXTRA_SYM 0x00000800
+#define OFDM_UCODE_TRIG_BASE_RX_STBC 0x00001000
+#define OFDM_UCODE_TRIG_BASE_RX_PRE_FEC_PAD_FACTOR 0x00006000
+#define OFDM_UCODE_TRIG_BASE_RX_PE_DISAMBIG 0x00008000
+#define OFDM_UCODE_TRIG_BASE_RX_DOPPLER 0x00010000
+#define OFDM_UCODE_TRIG_BASE_RX_RU 0x01fe0000
+#define OFDM_UCODE_TRIG_BASE_RX_RU_P80 0x00020000
+#define OFDM_UCODE_TRIG_BASE_RX_NUM_OF_LTF_SYM 0x0e000000
+#define OFDM_UCODE_TRIG_BASE_RX_LTF_PILOT_TYPE 0x10000000
+#define OFDM_UCODE_TRIG_BASE_RX_LOWEST_SS_ALLOCATION 0xe0000000
+ __le32 tb_rx1;
+};
+
+struct iwl_eht_sigs {
+#define OFDM_RX_FRAME_ENHANCED_WIFI_VER_ID 0x00000007
+#define OFDM_RX_FRAME_ENHANCED_WIFI_BANDWIDTH 0x00000038
+#define OFDM_RX_FRAME_ENHANCED_WIFI_UL_FLAG 0x00000040
+#define OFDM_RX_FRAME_ENHANCED_WIFI_BSS_COLOR 0x00001f80
+#define OFDM_RX_FRAME_ENHANCED_WIFI_TXOP_DURATION 0x000fe000
+#define OFDM_RX_FRAME_EHT_USIG1_DISREGARD 0x01f00000
+#define OFDM_RX_FRAME_EHT_USIG1_VALIDATE 0x02000000
+#define OFDM_RX_FRAME_EHT_BW320_SLOT 0x04000000
+#define OFDM_RX_FRAME_EHT_TYPE 0x18000000
+#define OFDM_RX_FRAME_ENHANCED_ER_NO_STREAMS 0x20000000
+ __le32 usig_a1;
+#define OFDM_RX_FRAME_EHT_PPDU_TYPE 0x00000003
+#define OFDM_RX_FRAME_EHT_USIG2_VALIDATE_B2 0x00000004
+#define OFDM_RX_FRAME_EHT_PUNC_CHANNEL 0x000000f8
+#define OFDM_RX_FRAME_EHT_USIG2_VALIDATE_B8 0x00000100
+#define OFDM_RX_FRAME_EHT_SIG_MCS 0x00000600
+#define OFDM_RX_FRAME_EHT_SIG_SYM_NUM 0x0000f800
+#define OFDM_RX_FRAME_EHT_TRIG_SPATIAL_REUSE_1 0x000f0000
+#define OFDM_RX_FRAME_EHT_TRIG_SPATIAL_REUSE_2 0x00f00000
+#define OFDM_RX_FRAME_EHT_TRIG_USIG2_DISREGARD 0x1f000000
+#define OFDM_RX_FRAME_EHT_TRIG_NO_STREAMS 0x20000000
+#define OFDM_RX_USIG_CRC_OK 0x40000000
+ __le32 usig_a2_eht;
+#define OFDM_RX_FRAME_EHT_SPATIAL_REUSE 0x0000000f
+#define OFDM_RX_FRAME_EHT_GI_LTF_TYPE 0x00000030
+#define OFDM_RX_FRAME_EHT_NUM_OF_LTF_SYM 0x000001c0
+#define OFDM_RX_FRAME_EHT_CODING_EXTRA_SYM 0x00000200
+#define OFDM_RX_FRAME_EHT_PRE_FEC_PAD_FACTOR 0x00000c00
+#define OFDM_RX_FRAME_EHT_PE_DISAMBIG 0x00001000
+#define OFDM_RX_FRAME_EHT_USIG_OVF_DISREGARD 0x0001e000
+#define OFDM_RX_FRAME_EHT_NUM_OF_USERS 0x000e0000
+#define OFDM_RX_FRAME_EHT_NSTS 0x00f00000
+#define OFDM_RX_FRAME_EHT_BF 0x01000000
+#define OFDM_RX_FRAME_EHT_USIG_OVF_NDP_DISREGARD 0x06000000
+#define OFDM_RX_FRAME_EHTSIG_COMM_CC1_CRC_OK 0x08000000
+#define OFDM_RX_FRAME_EHTSIG_COMM_CC2_CRC_OK 0x10000000
+#define OFDM_RX_FRAME_EHT_NON_VALID_RU_ALLOC 0x20000000
+#define OFDM_RX_FRAME_EHT_NO_STREAMS 0x40000000
+ __le32 b1;
+#define OFDM_RX_FRAME_EHT_MATCH_ID_FOUND 0x00000001
+#define OFDM_RX_FRAME_EHT_ID_INDX 0x0000000e
+#define OFDM_RX_FRAME_EHT_MCS 0x000000f0
+#define OFDM_RX_FRAME_EHT_CODING 0x00000100
+#define OFDM_RX_FRAME_EHT_SPATIAL_CONFIG 0x00007e00
+#define OFDM_RX_FRAME_EHT_STA_RU 0x007f8000
+#define OFDM_RX_FRAME_EHT_STA_RU_P80 0x00008000
+#define OFDM_RX_FRAME_EHT_STA_RU_PS160 0x00800000
+#define OFDM_RX_FRAME_EHT_USER_FIELD_CRC_OK 0x40000000
+ __le32 b2;
+#define OFDM_RX_FRAME_EHT_NUM_OF_DATA_SYM 0x000007ff
+#define OFDM_RX_FRAME_EHT_PE_DURATION 0x00003800
+#define OFDM_RX_FRAME_EHT_NUM_OF_DATA_SYM_VALID 0x80000000
+ __le32 sig2;
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_0_A1 0x000001ff
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_0_A2 0x0003fe00
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_0_A3 0x07fc0000
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_1_B1 0x000001ff
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_1_B2 0x0003fe00
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_1_B3 0x07fc0000
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_2_C1 0x000001ff
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_2_C2 0x0003fe00
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_2_C3 0x07fc0000
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_3_D1 0x000001ff
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_3_D2 0x0003fe00
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_3_D3 0x07fc0000
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_4_A4 0x000001ff
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_4_B4 0x0003fe00
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_5_C4 0x000001ff
+#define OFDM_RX_FRAME_EHT_RU_ALLOC_5_D4 0x0003fe00
+ __le32 cmn[6];
+#define OFDM_RX_FRAME_EHT_USER_FIELD_ID 0x000007ff
+ __le32 user_id;
+};
+
+struct iwl_eht_tb_sigs {
+ /* same as non-TB above */
+ __le32 usig_a1, usig_a2_eht;
+ /* same as HE TB above */
+ __le32 tb_rx0, tb_rx1;
+};
+
+struct iwl_uhr_sigs {
+ __le32 usig_a1, usig_a1_uhr, usig_a2_uhr, b1, b2;
+ __le32 sig2;
+ __le32 cmn[6];
+ __le32 user_id;
+};
+
+struct iwl_uhr_tb_sigs {
+ __le32 usig_a1, usig_a2_uhr, tb_rx0, tb_rx1;
+};
+
+struct iwl_uhr_elr_sigs {
+ __le32 usig_a1, usig_a2_uhr;
+ __le32 uhr_sig_elr1, uhr_sig_elr2;
+};
+
+union iwl_sigs {
+ struct iwl_ht_sigs ht;
+ struct iwl_vht_sigs vht;
+ struct iwl_he_sigs he;
+ struct iwl_he_tb_sigs he_tb;
+ struct iwl_eht_sigs eht;
+ struct iwl_eht_tb_sigs eht_tb;
+ struct iwl_uhr_sigs uhr;
+ struct iwl_uhr_tb_sigs uhr_tb;
+ struct iwl_uhr_elr_sigs uhr_elr;
+};
+
+enum iwl_sniffer_status {
+ IWL_SNIF_STAT_PLCP_RX_OK = 0,
+ IWL_SNIF_STAT_AID_NOT_FOR_US = 1,
+ IWL_SNIF_STAT_PLCP_RX_LSIG_ERR = 2,
+ IWL_SNIF_STAT_PLCP_RX_SIGA_ERR = 3,
+ IWL_SNIF_STAT_PLCP_RX_SIGB_ERR = 4,
+ IWL_SNIF_STAT_UNEXPECTED_TB = 5,
+ IWL_SNIF_STAT_UNSUPPORTED_RATE = 6,
+ IWL_SNIF_STAT_UNKNOWN_ERROR = 7,
+}; /* AIR_SNIFFER_STATUS_E_VER_1 */
+
+enum iwl_sniffer_flags {
+ IWL_SNIF_FLAG_VALID_TB_RX = BIT(0),
+ IWL_SNIF_FLAG_VALID_RU = BIT(1),
+}; /* AIR_SNIFFER_FLAGS_E_VER_1 */
+
+/**
+ * struct iwl_rx_phy_air_sniffer_ntfy - air sniffer notification
+ *
+ * @status: &enum iwl_sniffer_status
+ * @flags: &enum iwl_sniffer_flags
+ * @reserved1: reserved
+ * @rssi_a: energy chain-A in negative dBm, measured at FINA time
+ * @rssi_b: energy chain-B in negative dBm, measured at FINA time
+ * @channel: channel number
+ * @band: band information, PHY_BAND_*
+ * @on_air_rise_time: GP2 at on air rise
+ * @frame_time: frame time in us
+ * @rate: RATE_MCS_*
+ * @bytecount: byte count for legay and HT, otherwise number of symbols
+ * @legacy_sig: CCK signal information if %RATE_MCS_MOD_TYPE_MSK in @rate is
+ * %RATE_MCS_MOD_TYPE_CCK, otherwise OFDM signal information
+ * @sigs: PHY signal information, depending on %RATE_MCS_MOD_TYPE_MSK in @rate
+ * @reserved2: reserved
+ *
+ * Sent for every frame and before the normal RX command if data is included.
+ */
+struct iwl_rx_phy_air_sniffer_ntfy {
+ u8 status;
+ u8 flags;
+ u8 reserved1[2];
+ u8 rssi_a, rssi_b;
+ u8 channel, band;
+ __le32 on_air_rise_time;
+ __le32 frame_time;
+ /* note: MCS in rate is not valid for MU-VHT */
+ __le32 rate;
+ __le32 bytecount;
+ union iwl_legacy_sig legacy_sig;
+ union iwl_sigs sigs;
+ __le32 reserved2;
+}; /* RX_PHY_AIR_SNIFFER_NTFY_API_S_VER_1 */
+
#endif /* __iwl_fw_api_rx_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index f486d624500b..60f0a4924ddf 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -129,7 +129,7 @@ struct iwl_scan_offload_profile {
} __packed;
/**
- * struct iwl_scan_offload_profile_cfg_data
+ * struct iwl_scan_offload_profile_cfg_data - scan offload profile configs
* @blocklist_len: length of blocklist
* @num_profiles: num of profiles in the list
* @match_notify: clients waiting for match found notification
@@ -159,7 +159,7 @@ struct iwl_scan_offload_profile_cfg_v1 {
} __packed; /* SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1-2*/
/**
- * struct iwl_scan_offload_profile_cfg
+ * struct iwl_scan_offload_profile_cfg - scan offload profile config
* @profiles: profiles to search for match
* @data: the rest of the data for profile_cfg
*/
@@ -507,7 +507,7 @@ enum iwl_uhb_chan_cfg_flags {
IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE = BIT(26),
};
/**
- * struct iwl_scan_dwell
+ * struct iwl_scan_dwell - scan dwell configuration
* @active: default dwell time for active scan
* @passive: default dwell time for passive scan
* @fragmented: default dwell time for fragmented scan
@@ -728,7 +728,7 @@ enum iwl_umac_scan_general_params_flags2 {
};
/**
- * struct iwl_scan_channel_cfg_umac
+ * struct iwl_scan_channel_cfg_umac - scan channel config
* @flags: bitmap - 0-19: directed scan to i'th ssid.
* @channel_num: channel number 1-13 etc.
* @v1: command version 1
@@ -774,7 +774,7 @@ struct iwl_scan_channel_cfg_umac {
} __packed;
/**
- * struct iwl_scan_umac_schedule
+ * struct iwl_scan_umac_schedule - scan schedule parameters
* @interval: interval in seconds between scan iterations
* @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop
* @reserved: for alignment and future use
@@ -815,7 +815,7 @@ struct iwl_scan_req_umac_tail_v2 {
} __packed;
/**
- * struct iwl_scan_umac_chan_param
+ * struct iwl_scan_umac_chan_param - scan channel parameters
* @flags: channel flags &enum iwl_scan_channel_flags
* @count: num of channels in scan request
* @reserved: for future use and alignment
@@ -827,33 +827,37 @@ struct iwl_scan_umac_chan_param {
} __packed; /*SCAN_CHANNEL_PARAMS_API_S_VER_1 */
/**
- * struct iwl_scan_req_umac
+ * struct iwl_scan_req_umac - scan request command
* @flags: &enum iwl_umac_scan_flags
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @ooc_priority: out of channel priority - &enum iwl_scan_priority
* @general_flags: &enum iwl_umac_scan_general_flags
+ * @reserved: reserved
* @scan_start_mac_id: report the scan start TSF time according to this mac TSF
- * @extended_dwell: dwell time for channels 1, 6 and 11
- * @active_dwell: dwell time for active scan per LMAC
- * @passive_dwell: dwell time for passive scan per LMAC
- * @fragmented_dwell: dwell time for fragmented passive scan
- * @adwell_default_n_aps: for adaptive dwell the default number of APs
+ * @v1: version 1 command data
+ * @v6: version 6 command data
+ * @v7: version 7 command data
+ * @v8: version 8 command data
+ * @v9: version 9 command data
+ * @v1.extended_dwell: dwell time for channels 1, 6 and 11
+ * @v1.active_dwell: dwell time for active scan per LMAC
+ * @v1.passive_dwell: dwell time for passive scan per LMAC
+ * @v1.fragmented_dwell: dwell time for fragmented passive scan
+ * @v7.adwell_default_n_aps: for adaptive dwell the default number of APs
* per channel
- * @adwell_default_n_aps_social: for adaptive dwell the default
+ * @v7.adwell_default_n_aps_social: for adaptive dwell the default
* number of APs per social (1,6,11) channel
- * @general_flags2: &enum iwl_umac_scan_general_flags2
- * @adwell_max_budget: for adaptive dwell the maximal budget of TU to be added
- * to total scan time
- * @max_out_time: max out of serving channel time, per LMAC - for CDB there
- * are 2 LMACs
- * @suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs
- * @scan_priority: scan internal prioritization &enum iwl_scan_priority
- * @num_of_fragments: Number of fragments needed for full coverage per band.
+ * @v8.general_flags2: &enum iwl_umac_scan_general_flags2
+ * @v7.adwell_max_budget: for adaptive dwell the maximal budget of TU to be
+ * added to total scan time
+ * @v1.max_out_time: max out of serving channel time, per LMAC - for CDB
+ * there are 2 LMACs
+ * @v1.suspend_time: max suspend time, per LMAC - for CDB there are 2 LMACs
+ * @v1.scan_priority: scan internal prioritization &enum iwl_scan_priority
+ * @v8.num_of_fragments: Number of fragments needed for full coverage per band.
* Relevant only for fragmented scan.
- * @channel: &struct iwl_scan_umac_chan_param
- * @reserved: for future use and alignment
- * @reserved3: for future use and alignment
- * @data: &struct iwl_scan_channel_cfg_umac and
+ * @v1.channel: &struct iwl_scan_umac_chan_param
+ * @v1.data: &struct iwl_scan_channel_cfg_umac and
* &struct iwl_scan_req_umac_tail
*/
struct iwl_scan_req_umac {
@@ -939,7 +943,7 @@ struct iwl_scan_req_umac {
#define IWL_SCAN_REQ_UMAC_SIZE_V1 36
/**
- * struct iwl_scan_probe_params_v3
+ * struct iwl_scan_probe_params_v3 - scan probe parameters
* @preq: scan probe request params
* @ssid_num: number of valid SSIDs in direct scan array
* @short_ssid_num: number of valid short SSIDs in short ssid array
@@ -961,7 +965,7 @@ struct iwl_scan_probe_params_v3 {
} __packed; /* SCAN_PROBE_PARAMS_API_S_VER_3 */
/**
- * struct iwl_scan_probe_params_v4
+ * struct iwl_scan_probe_params_v4 - scan probe parameters
* @preq: scan probe request params
* @short_ssid_num: number of valid short SSIDs in short ssid array
* @bssid_num: number of valid bssid in bssids array
@@ -983,7 +987,7 @@ struct iwl_scan_probe_params_v4 {
#define SCAN_MAX_NUM_CHANS_V3 67
/**
- * struct iwl_scan_channel_params_v4
+ * struct iwl_scan_channel_params_v4 - channel params
* @flags: channel flags &enum iwl_scan_channel_flags
* @count: num of channels in scan request
* @num_of_aps_override: override the number of APs the FW uses to calculate
@@ -1006,7 +1010,7 @@ struct iwl_scan_channel_params_v4 {
SCAN_CHANNEL_PARAMS_API_S_VER_5 */
/**
- * struct iwl_scan_channel_params_v7
+ * struct iwl_scan_channel_params_v7 - channel params
* @flags: channel flags &enum iwl_scan_channel_flags
* @count: num of channels in scan request
* @n_aps_override: override the number of APs the FW uses to calculate dwell
@@ -1024,7 +1028,7 @@ struct iwl_scan_channel_params_v7 {
} __packed; /* SCAN_CHANNEL_PARAMS_API_S_VER_6 */
/**
- * struct iwl_scan_general_params_v11
+ * struct iwl_scan_general_params_v11 - channel params
* @flags: &enum iwl_umac_scan_general_flags_v2
* @reserved: reserved for future
* @scan_start_mac_or_link_id: report the scan start TSF time according to this
@@ -1066,7 +1070,7 @@ struct iwl_scan_general_params_v11 {
} __packed; /* SCAN_GENERAL_PARAMS_API_S_VER_12, *_VER_11 and *_VER_10 */
/**
- * struct iwl_scan_periodic_parms_v1
+ * struct iwl_scan_periodic_parms_v1 - periodicity parameters
* @schedule: can scheduling parameter
* @delay: initial delay of the periodic scan in seconds
* @reserved: reserved for future
@@ -1078,7 +1082,7 @@ struct iwl_scan_periodic_parms_v1 {
} __packed; /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */
/**
- * struct iwl_scan_req_params_v12
+ * struct iwl_scan_req_params_v12 - scan request parameters (v12)
* @general_params: &struct iwl_scan_general_params_v11
* @channel_params: &struct iwl_scan_channel_params_v4
* @periodic_params: &struct iwl_scan_periodic_parms_v1
@@ -1106,7 +1110,7 @@ struct iwl_scan_req_params_v17 {
} __packed; /* SCAN_REQUEST_PARAMS_API_S_VER_17 - 14 */
/**
- * struct iwl_scan_req_umac_v12
+ * struct iwl_scan_req_umac_v12 - scan request command (v12)
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @ooc_priority: out of channel priority - &enum iwl_scan_priority
* @scan_params: scan parameters
@@ -1130,7 +1134,7 @@ struct iwl_scan_req_umac_v17 {
} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_17 - 14 */
/**
- * struct iwl_umac_scan_abort
+ * struct iwl_umac_scan_abort - scan abort command
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @flags: reserved
*/
@@ -1140,7 +1144,7 @@ struct iwl_umac_scan_abort {
} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */
/**
- * enum iwl_umac_scan_abort_status
+ * enum iwl_umac_scan_abort_status - scan abort status
*
* @IWL_UMAC_SCAN_ABORT_STATUS_SUCCESS: scan was successfully aborted
* @IWL_UMAC_SCAN_ABORT_STATUS_IN_PROGRESS: scan abort is in progress
@@ -1153,7 +1157,7 @@ enum iwl_umac_scan_abort_status {
};
/**
- * struct iwl_umac_scan_complete
+ * struct iwl_umac_scan_complete - scan complete notification
* @uid: scan id, &enum iwl_umac_scan_uid_offsets
* @last_schedule: last scheduling line
* @last_iter: last scan iteration number
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h
index ecbcd5084cd8..e6f9abdfa546 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2021, 2023 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2021, 2023, 2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -428,7 +428,7 @@ struct iwl_mvm_rm_sta_cmd {
} __packed; /* REMOVE_STA_CMD_API_S_VER_2 */
/**
- * struct iwl_mvm_mgmt_mcast_key_cmd_v1
+ * struct iwl_mvm_mgmt_mcast_key_cmd_v1 - IGTK command
* ( MGMT_MCAST_KEY = 0x1f )
* @ctrl_flags: &enum iwl_sta_key_flag
* @igtk: IGTK key material
@@ -449,7 +449,7 @@ struct iwl_mvm_mgmt_mcast_key_cmd_v1 {
} __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */
/**
- * struct iwl_mvm_mgmt_mcast_key_cmd
+ * struct iwl_mvm_mgmt_mcast_key_cmd - IGTK command
* ( MGMT_MCAST_KEY = 0x1f )
* @ctrl_flags: &enum iwl_sta_key_flag
* @igtk: IGTK master key
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
index 00713a991879..8d9a5058d5a5 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
@@ -26,7 +26,7 @@ struct mvm_statistics_div {
} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */
/**
- * struct mvm_statistics_rx_non_phy
+ * struct mvm_statistics_rx_non_phy - non-PHY RX statistics
* @bogus_cts: CTS received when not expecting CTS
* @bogus_ack: ACK received when not expecting ACK
* @non_channel_beacons: beacons with our bss id but not on our serving channel
@@ -456,7 +456,7 @@ struct iwl_system_statistics_cmd {
} __packed; /* STATISTICS_FW_CMD_API_S_VER_1 */
/**
- * enum iwl_fw_statistics_type
+ * enum iwl_fw_statistics_type - statistics type
*
* @FW_STATISTICS_OPERATIONAL: operational statistics
* @FW_STATISTICS_PHY: phy statistics
@@ -478,7 +478,7 @@ enum iwl_fw_statistics_type {
#define IWL_STATISTICS_TYPE_MSK 0x7f
/**
- * struct iwl_statistics_ntfy_hdr
+ * struct iwl_statistics_ntfy_hdr - statistics notification header
*
* @type: struct type
* @version: version of the struct
@@ -491,7 +491,7 @@ struct iwl_statistics_ntfy_hdr {
}; /* STATISTICS_NTFY_HDR_API_S_VER_1 */
/**
- * struct iwl_stats_ntfy_per_link
+ * struct iwl_stats_ntfy_per_link - per-link statistics
*
* @beacon_filter_average_energy: Average energy [-dBm] of the 2
* antennas.
@@ -514,7 +514,7 @@ struct iwl_stats_ntfy_per_link {
} __packed; /* STATISTICS_NTFY_PER_LINK_API_S_VER_1 */
/**
- * struct iwl_stats_ntfy_part1_per_link
+ * struct iwl_stats_ntfy_part1_per_link - part1 per link statistics
*
* @rx_time: rx time
* @tx_time: tx time
@@ -533,7 +533,7 @@ struct iwl_stats_ntfy_part1_per_link {
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_PER_LINK_API_S_VER_1 */
/**
- * struct iwl_stats_ntfy_per_mac
+ * struct iwl_stats_ntfy_per_mac - per MAC statistics
*
* @beacon_filter_average_energy: Average energy [-dBm] of the 2
* antennas.
@@ -556,7 +556,8 @@ struct iwl_stats_ntfy_per_mac {
} __packed; /* STATISTICS_NTFY_PER_MAC_API_S_VER_1 */
#define IWL_STATS_MAX_BW_INDEX 5
-/** struct iwl_stats_ntfy_per_phy
+/**
+ * struct iwl_stats_ntfy_per_phy - per PHY statistics
* @channel_load: channel load
* @channel_load_by_us: device contribution to MCLM
* @channel_load_not_by_us: other devices' contribution to MCLM
@@ -588,7 +589,7 @@ struct iwl_stats_ntfy_per_phy {
#define IWL_STATS_UNKNOWN_CHANNEL_LOAD 0xffffffff
/**
- * struct iwl_stats_ntfy_per_sta
+ * struct iwl_stats_ntfy_per_sta - per STA statistics
*
* @average_energy: in fact it is minus the energy..
*/
@@ -600,7 +601,7 @@ struct iwl_stats_ntfy_per_sta {
#define IWL_STATS_MAX_FW_LINKS (IWL_FW_MAX_LINK_ID + 1)
/**
- * struct iwl_system_statistics_notif_oper
+ * struct iwl_system_statistics_notif_oper - statistics notification
*
* @time_stamp: time when the notification is sent from firmware
* @per_link: per link statistics, &struct iwl_stats_ntfy_per_link
@@ -615,7 +616,7 @@ struct iwl_system_statistics_notif_oper {
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
/**
- * struct iwl_system_statistics_part1_notif_oper
+ * struct iwl_system_statistics_part1_notif_oper - part1 stats notification
*
* @time_stamp: time when the notification is sent from firmware
* @per_link: per link statistics &struct iwl_stats_ntfy_part1_per_link
@@ -628,7 +629,7 @@ struct iwl_system_statistics_part1_notif_oper {
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_API_S_VER_4 */
/**
- * struct iwl_system_statistics_end_notif
+ * struct iwl_system_statistics_end_notif - statistics end notification
*
* @time_stamp: time when the notification is sent from firmware
*/
@@ -637,7 +638,7 @@ struct iwl_system_statistics_end_notif {
} __packed; /* STATISTICS_FW_NTFY_END_API_S_VER_1 */
/**
- * struct iwl_statistics_operational_ntfy
+ * struct iwl_statistics_operational_ntfy - operational stats notification
*
* @hdr: general statistics header
* @flags: bitmap of possible notification structures
@@ -662,7 +663,7 @@ struct iwl_statistics_operational_ntfy {
} __packed; /* STATISTICS_OPERATIONAL_NTFY_API_S_VER_15 */
/**
- * struct iwl_statistics_operational_ntfy_ver_14
+ * struct iwl_statistics_operational_ntfy_ver_14 - operational stats notification
*
* @hdr: general statistics header
* @flags: bitmap of possible notification structures
@@ -707,7 +708,7 @@ struct iwl_statistics_operational_ntfy_ver_14 {
} __packed; /* STATISTICS_OPERATIONAL_NTFY_API_S_VER_14 */
/**
- * struct iwl_statistics_phy_ntfy
+ * struct iwl_statistics_phy_ntfy - PHY statistics notification
*
* @hdr: general statistics header
* RX PHY related statistics
@@ -808,7 +809,7 @@ struct iwl_statistics_phy_ntfy {
} __packed; /* STATISTICS_PHY_NTFY_API_S_VER_1 */
/**
- * struct iwl_statistics_mac_ntfy
+ * struct iwl_statistics_mac_ntfy - MAC statistics notification
*
* @hdr: general statistics header
* @bcast_filter_passed_per_mac: bcast filter passed per mac
@@ -827,7 +828,7 @@ struct iwl_statistics_mac_ntfy {
} __packed; /* STATISTICS_MAC_NTFY_API_S_VER_1 */
/**
- * struct iwl_statistics_rx_ntfy
+ * struct iwl_statistics_rx_ntfy - RX statistics notification
*
* @hdr: general statistics header
* @rx_agg_mpdu_cnt: aggregation frame count (number of
@@ -867,7 +868,7 @@ struct iwl_statistics_rx_ntfy {
} __packed; /* STATISTICS_RX_NTFY_API_S_VER_1 */
/**
- * struct iwl_statistics_tx_ntfy
+ * struct iwl_statistics_tx_ntfy - TX statistics notification
*
* @hdr: general statistics header
* @cts_timeout: timeout when waiting for CTS
@@ -976,7 +977,7 @@ struct iwl_statistics_tx_ntfy {
} __packed; /* STATISTICS_TX_NTFY_API_S_VER_1 */
/**
- * struct iwl_statistics_duration_ntfy
+ * struct iwl_statistics_duration_ntfy - burst/duration statistics
*
* @hdr: general statistics header
* @cont_burst_chk_cnt: number of times continuation or
@@ -995,7 +996,7 @@ struct iwl_statistics_duration_ntfy {
} __packed; /* STATISTICS_DURATION_NTFY_API_S_VER_1 */
/**
- * struct iwl_statistics_he_ntfy
+ * struct iwl_statistics_he_ntfy - HE statistics
*
* @hdr: general statistics header
* received HE frames
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
index 26d2013905ed..31d3336726b4 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h
@@ -963,7 +963,7 @@ struct iwl_scd_txq_cfg_cmd {
} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */
/**
- * struct iwl_scd_txq_cfg_rsp
+ * struct iwl_scd_txq_cfg_rsp - scheduler TXQ configuration response
* @token: taken from the command
* @sta_id: station id from the command
* @tid: tid from the command
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h
index c2a73cc85eff..525a82030daa 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h
@@ -266,7 +266,7 @@ struct iwl_fw_ini_error_dump_data {
} __packed;
/**
- * struct iwl_fw_ini_dump_entry
+ * struct iwl_fw_ini_dump_entry - dump entry descriptor
* @list: list of dump entries
* @size: size of the data
* @data: entry data
@@ -305,7 +305,7 @@ struct iwl_fw_ini_fifo_hdr {
* @dram_base_addr: base address of dram monitor range
* @page_num: page number of memory range
* @fifo_hdr: fifo header of memory range
- * @fw_pkt: FW packet header of memory range
+ * @fw_pkt_hdr: FW packet header of memory range
* @data: the actual memory
*/
struct iwl_fw_ini_error_dump_range {
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h
index b7c1ab7a3006..b9e0b69c6680 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/file.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h
@@ -222,7 +222,10 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t;
* @IWL_UCODE_TLV_API_STA_TYPE: This ucode supports station type assignement.
* @IWL_UCODE_TLV_API_NAN2_VER2: This ucode supports NAN API version 2
* @IWL_UCODE_TLV_API_ADAPTIVE_DWELL: support for adaptive dwell in scanning
+ * @IWL_UCODE_TLV_API_OCE: support for OCE
+ * @IWL_UCODE_TLV_API_NEW_BEACON_TEMPLATE: new beacon template
* @IWL_UCODE_TLV_API_NEW_RX_STATS: should new RX STATISTICS API be used
+ * @IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL: WoWLAN key material support
* @IWL_UCODE_TLV_API_QUOTA_LOW_LATENCY: Quota command includes a field
* indicating low latency direction.
* @IWL_UCODE_TLV_API_DEPRECATE_TTAK: RX status flag TTAK ok (bit 7) is
@@ -245,6 +248,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t;
* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S.
* @IWL_UCODE_TLV_API_MBSSID_HE: This ucode supports v2 of
* STA_CONTEXT_DOT11AX_API_S
+ * @IWL_UCODE_TLV_API_WOWLAN_TCP_SYN_WAKE: WoWLAN TCP-SYN wake support
* @IWL_UCODE_TLV_API_FTM_RTT_ACCURACY: version 7 of the range response API
* is supported by FW, this indicates the RTT confidence value
* @IWL_UCODE_TLV_API_SAR_TABLE_VER: This ucode supports different sar
@@ -253,6 +257,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t;
* SCAN_CONFIG_DB_CMD_API_S.
* @IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP: support for setting adaptive dwell
* number of APs in the 5 GHz band
+ * @IWL_UCODE_TLV_API_SCAN_EXT_CHAN_VER: extended channel config in scan
* @IWL_UCODE_TLV_API_BAND_IN_RX_DATA: FW reports band number in RX notification
* @IWL_UCODE_TLV_API_NO_HOST_DISABLE_TX: Firmware offloaded the station disable tx
* logic.
@@ -352,16 +357,24 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
* @IWL_UCODE_TLV_CAPA_SOC_LATENCY_SUPPORT: the firmware supports setting
* stabilization latency for SoCs.
* @IWL_UCODE_TLV_CAPA_STA_PM_NOTIF: firmware will send STA PM notification
+ * @IWL_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT: binding CDB support
+ * @IWL_UCODE_TLV_CAPA_CDB_SUPPORT: CDB support
+ * @IWL_UCODE_TLV_CAPA_D0I3_END_FIRST: D0I3 end command comes first
* @IWL_UCODE_TLV_CAPA_TLC_OFFLOAD: firmware implements rate scaling algorithm
* @IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA: firmware implements quota related
* @IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2: firmware implements Coex Schema 2
- * IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD: firmware supports CSA command
+ * @IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD: firmware supports CSA command
* @IWL_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS: firmware supports ultra high band
* (6 GHz).
* @IWL_UCODE_TLV_CAPA_CS_MODIFY: firmware supports modify action CSA command
+ * @IWL_UCODE_TLV_CAPA_SET_LTR_GEN2: LTR gen2 support
+ * @IWL_UCODE_TLV_CAPA_TAS_CFG: TAS configuration support
+ * @IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD: session protection command
+ * @IWL_UCODE_TLV_CAPA_SET_PPAG: PPAG support
* @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement
* @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts
* @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT
+ * @IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT: MQ RX support
* @IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD: the firmware supports CSA
* countdown offloading. Beacon notifications are not sent to the host.
* The fw also offloads TBTT alignment.
@@ -383,23 +396,46 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
* command size (command version 4) that supports toggling ACK TX
* power reduction.
* @IWL_UCODE_TLV_CAPA_D3_DEBUG: supports debug recording during D3
+ * @IWL_UCODE_TLV_CAPA_LED_CMD_SUPPORT: LED command support
* @IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT: MCC response support 11ax
* capability.
* @IWL_UCODE_TLV_CAPA_CSI_REPORTING: firmware is capable of being configured
* to report the CSI information with (certain) RX frames
+ * @IWL_UCODE_TLV_CAPA_DBG_SUSPEND_RESUME_CMD_SUPP: suspend/resume command
+ * @IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP: support for DBGC
+ * buffer allocation command
* @IWL_UCODE_TLV_CAPA_FTM_CALIBRATED: has FTM calibrated and thus supports both
* initiator and responder
* @IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_UNII4_US_CA: supports (de)activating UNII-4
* for US/CA/WW from BIOS
+ * @IWL_UCODE_TLV_CAPA_PSC_CHAN_SUPPORT: supports PSC channels
+ * @IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT: BIGTK support
* @IWL_UCODE_TLV_CAPA_PROTECTED_TWT: Supports protection of TWT action frames
* @IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE: Supports the firmware handshake in
* reset flow
* @IWL_UCODE_TLV_CAPA_PASSIVE_6GHZ_SCAN: Support for passive scan on 6GHz PSC
* channels even when these are not enabled.
+ * @IWL_UCODE_TLV_CAPA_HIDDEN_6GHZ_SCAN: hidden SSID 6 GHz scan support
+ * @IWL_UCODE_TLV_CAPA_BROADCAST_TWT: broadcast TWT support
+ * @IWL_UCODE_TLV_CAPA_COEX_HIGH_PRIO: support for BT-coex high
+ * priority for 802.1X/4-way-HS
+ * @IWL_UCODE_TLV_CAPA_BAID_ML_SUPPORT: multi-link BAID support
+ * @IWL_UCODE_TLV_CAPA_SYNCED_TIME: synced time command support
+ * @IWL_UCODE_TLV_CAPA_TIME_SYNC_BOTH_FTM_TM: time sync support
+ * @IWL_UCODE_TLV_CAPA_BIGTK_TX_SUPPORT: BIGTK TX support
+ * @IWL_UCODE_TLV_CAPA_MLD_API_SUPPORT: MLD API support
+ * @IWL_UCODE_TLV_CAPA_SCAN_DONT_TOGGLE_ANT: fixed antenna scan support
+ * @IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT: PPAG China BIOS support
+ * @IWL_UCODE_TLV_CAPA_OFFLOAD_BTM_SUPPORT: BTM protocol offload support
+ * @IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT: STA command MFP support
+ * @IWL_UCODE_TLV_CAPA_SNIFF_VALIDATE_SUPPORT: sniffer validate bits support
+ * @IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT: China 2022 regulator support
* @IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT: Support for indicating dump collection
* complete to FW.
* @IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT: Support SPP (signaling and payload
* protected) A-MSDU.
+ * @IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT: support for DBGC fragmented
+ * DRAM buffers
* @IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT: Support secure LTF measurement.
* @IWL_UCODE_TLV_CAPA_MONITOR_PASSIVE_CHANS: Support monitor mode on otherwise
* passive channels
@@ -407,6 +443,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t;
* for CA from BIOS.
* @IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT: supports %TAS_UHB_ALLOWED_CANADA
* @IWL_UCODE_TLV_CAPA_EXT_FSEQ_IMAGE_SUPPORT: external FSEQ image support
+ * @IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT: FW reset handshake is needed
+ * during assert handling even if the dump isn't split
* @IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE: Firmware has capability of
* handling raw DSM table data.
*
@@ -487,12 +525,7 @@ enum iwl_ucode_tlv_capa {
/* set 3 */
IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_UNII4_US_CA = (__force iwl_ucode_tlv_capa_t)96,
-
- /*
- * @IWL_UCODE_TLV_CAPA_PSC_CHAN_SUPPORT: supports PSC channels
- */
IWL_UCODE_TLV_CAPA_PSC_CHAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)98,
-
IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT = (__force iwl_ucode_tlv_capa_t)100,
IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT = (__force iwl_ucode_tlv_capa_t)103,
IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT = (__force iwl_ucode_tlv_capa_t)104,
@@ -514,11 +547,8 @@ enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_EXT_FSEQ_IMAGE_SUPPORT = (__force iwl_ucode_tlv_capa_t)125,
/* set 4 */
- /**
- * @IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT: FW reset handshake is needed
- * during assert handling even if the dump isn't split
- */
- IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT = (__force iwl_ucode_tlv_capa_t)(4 * 32 + 0),
+
+ IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT = (__force iwl_ucode_tlv_capa_t)(4 * 32 + 0),
IWL_UCODE_TLV_CAPA_FW_ACCEPTS_RAW_DSM_TABLE = (__force iwl_ucode_tlv_capa_t)(4 * 32 + 1),
NUM_IWL_UCODE_TLV_CAPA
/*
@@ -852,6 +882,8 @@ struct iwl_fw_dbg_trigger_low_rssi {
* @start_assoc_denied: number of denied association to start recording
* @start_assoc_timeout: number of association timeout to start recording
* @start_connection_loss: number of connection loss to start recording
+ * @reserved: reserved
+ * @reserved2: reserved
*/
struct iwl_fw_dbg_trigger_mlme {
u8 stop_auth_denied;
@@ -885,6 +917,7 @@ struct iwl_fw_dbg_trigger_mlme {
* @p2p_device: timeout for the queues of a P2P device in ms
* @ibss: timeout for the queues of an IBSS in ms
* @tdls: timeout for the queues of a TDLS station in ms
+ * @reserved: reserved
*/
struct iwl_fw_dbg_trigger_txq_timer {
__le32 command_queue;
@@ -900,7 +933,7 @@ struct iwl_fw_dbg_trigger_txq_timer {
/**
* struct iwl_fw_dbg_trigger_time_event - configures a time event trigger
- * time_Events: a list of tuples <id, action_bitmap>. The driver will issue a
+ * @time_events: a list of tuples <id, action_bitmap>. The driver will issue a
* trigger each time a time event notification that relates to time event
* id with one of the actions in the bitmap is received and
* BIT(notif->status) is set in status_bitmap.
@@ -916,19 +949,19 @@ struct iwl_fw_dbg_trigger_time_event {
/**
* struct iwl_fw_dbg_trigger_ba - configures BlockAck related trigger
- * rx_ba_start: tid bitmap to configure on what tid the trigger should occur
+ * @rx_ba_start: tid bitmap to configure on what tid the trigger should occur
* when an Rx BlockAck session is started.
- * rx_ba_stop: tid bitmap to configure on what tid the trigger should occur
+ * @rx_ba_stop: tid bitmap to configure on what tid the trigger should occur
* when an Rx BlockAck session is stopped.
- * tx_ba_start: tid bitmap to configure on what tid the trigger should occur
+ * @tx_ba_start: tid bitmap to configure on what tid the trigger should occur
* when a Tx BlockAck session is started.
- * tx_ba_stop: tid bitmap to configure on what tid the trigger should occur
+ * @tx_ba_stop: tid bitmap to configure on what tid the trigger should occur
* when a Tx BlockAck session is stopped.
- * rx_bar: tid bitmap to configure on what tid the trigger should occur
+ * @rx_bar: tid bitmap to configure on what tid the trigger should occur
* when a BAR is received (for a Tx BlockAck session).
- * tx_bar: tid bitmap to configure on what tid the trigger should occur
+ * @tx_bar: tid bitmap to configure on what tid the trigger should occur
* when a BAR is send (for an Rx BlocAck session).
- * frame_timeout: tid bitmap to configure on what tid the trigger should occur
+ * @frame_timeout: tid bitmap to configure on what tid the trigger should occur
* when a frame times out in the reordering buffer.
*/
struct iwl_fw_dbg_trigger_ba {
@@ -946,6 +979,7 @@ struct iwl_fw_dbg_trigger_ba {
* @action_bitmap: the TDLS action to trigger the collection upon
* @peer_mode: trigger on specific peer or all
* @peer: the TDLS peer to trigger the collection on
+ * @reserved: reserved
*/
struct iwl_fw_dbg_trigger_tdls {
u8 action_bitmap;
@@ -958,6 +992,7 @@ struct iwl_fw_dbg_trigger_tdls {
* struct iwl_fw_dbg_trigger_tx_status - configures trigger for tx response
* status.
* @statuses: the list of statuses to trigger the collection on
+ * @reserved: reserved
*/
struct iwl_fw_dbg_trigger_tx_status {
struct tx_status {
@@ -971,6 +1006,7 @@ struct iwl_fw_dbg_trigger_tx_status {
* struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
* @id: conf id
* @usniffer: should the uSniffer image be used
+ * @reserved: reserved
* @num_of_hcmds: how many HCMDs to send are present here
* @hcmd: a variable length host command to be sent to apply the configuration.
* If there is more than one HCMD to send, they will appear one after the
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h
index 5256f20623e9..045a3e009429 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/img.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h
@@ -14,14 +14,13 @@
#include "error-dump.h"
/**
- * enum iwl_ucode_type
- *
- * The type of ucode.
+ * enum iwl_ucode_type - type of ucode
*
* @IWL_UCODE_REGULAR: Normal runtime ucode
* @IWL_UCODE_INIT: Initial ucode
* @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode
* @IWL_UCODE_REGULAR_USNIFFER: Normal runtime ucode when using usniffer image
+ * @IWL_UCODE_TYPE_MAX: (internal value)
*/
enum iwl_ucode_type {
IWL_UCODE_REGULAR,
@@ -122,7 +121,7 @@ struct fw_img {
#define FW_ADDR_CACHE_CONTROL 0xC0000000UL
/**
- * struct iwl_fw_paging
+ * struct iwl_fw_paging - FW paging descriptor
* @fw_paging_phys: page phy pointer
* @fw_paging_block: pointer to the allocated block
* @fw_paging_size: page size
@@ -197,6 +196,11 @@ struct iwl_dump_exclude {
* @dump_excl_wowlan: image dump exclusion areas for WoWLAN image
* @pnvm_data: PNVM data embedded in the .ucode file, if any
* @pnvm_size: size of the embedded PNVM data
+ * @dbg: debug data, see &struct iwl_fw_dbg
+ * @default_calib: default calibration data
+ * @phy_config: PHY configuration flags
+ * @valid_rx_ant: valid RX antenna bitmap
+ * @valid_tx_ant: valid TX antenna bitmap
*/
struct iwl_fw {
u32 ucode_ver;
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
index e1f28b053253..d2ad169ae880 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c
@@ -543,32 +543,14 @@ static size_t iwl_get_lari_config_cmd_size(u8 cmd_ver)
switch (cmd_ver) {
case 12:
- case 11:
cmd_size = sizeof(struct iwl_lari_config_change_cmd);
break;
- case 10:
- cmd_size = sizeof(struct iwl_lari_config_change_cmd_v10);
- break;
- case 9:
case 8:
- case 7:
- cmd_size = sizeof(struct iwl_lari_config_change_cmd_v7);
+ cmd_size = sizeof(struct iwl_lari_config_change_cmd_v8);
break;
case 6:
cmd_size = sizeof(struct iwl_lari_config_change_cmd_v6);
break;
- case 5:
- cmd_size = sizeof(struct iwl_lari_config_change_cmd_v5);
- break;
- case 4:
- cmd_size = sizeof(struct iwl_lari_config_change_cmd_v4);
- break;
- case 3:
- cmd_size = sizeof(struct iwl_lari_config_change_cmd_v3);
- break;
- case 2:
- cmd_size = sizeof(struct iwl_lari_config_change_cmd_v2);
- break;
default:
cmd_size = sizeof(struct iwl_lari_config_change_cmd_v1);
break;
@@ -609,11 +591,11 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt,
if (!has_raw_dsm_capa)
value &= DSM_UNII4_ALLOW_BITMAP;
- /* Since version 9, bits 4 and 5 are supported
+ /* Since version 12, bits 4 and 5 are supported
* regardless of this capability, By pass this masking
* if firmware has capability of accepting raw DSM table.
*/
- if (!has_raw_dsm_capa && cmd_ver < 9 &&
+ if (!has_raw_dsm_capa && cmd_ver < 12 &&
!fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA))
value &= ~(DSM_VALUE_UNII4_CANADA_OVERRIDE_MSK |
@@ -637,7 +619,7 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt,
if (!has_raw_dsm_capa && cmd_ver < 12 &&
!fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_UNII4_US_CA))
- value &= CHAN_STATE_ACTIVE_BITMAP_CMD_V11;
+ value &= CHAN_STATE_ACTIVE_BITMAP_CMD_V8;
cmd->chan_state_active_bitmap = cpu_to_le32(value);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
index 806f9bcdf4f5..57570ff15622 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h
@@ -45,6 +45,8 @@ struct iwl_fwrt_shared_mem_cfg {
* struct iwl_fwrt_dump_data - dump data
* @trig: trigger the worker was scheduled upon
* @fw_pkt: packet received from FW
+ * @desc: dump descriptor
+ * @monitor_only: only dump for monitor
*
* Note that the decision which part of the union is used
* is based on iwl_trans_dbg_ini_valid(): the 'trig' part
@@ -68,6 +70,7 @@ struct iwl_fwrt_dump_data {
* struct iwl_fwrt_wk_data - dump worker data struct
* @idx: index of the worker
* @wk: worker
+ * @dump_data: dump data
*/
struct iwl_fwrt_wk_data {
u8 idx;
@@ -91,8 +94,8 @@ struct iwl_txf_iter_data {
/**
* struct iwl_fw_runtime - runtime data for firmware
+ * @trans: transport pointer
* @fw: firmware image
- * @cfg: NIC configuration
* @dev: device pointer
* @ops: user ops
* @ops_ctx: user ops context
@@ -117,6 +120,23 @@ struct iwl_txf_iter_data {
* zero (default initialization) means it hasn't been read yet,
* and BIT(0) is set when it has since function 0 also has this
* bitmap and is always supported
+ * @geo_enabled: WGDS table is present
+ * @geo_num_profiles: number of geo profiles
+ * @geo_rev: geo profiles table revision
+ * @ppag_chains: PPAG table data
+ * @ppag_flags: PPAG flags
+ * @reduced_power_flags: reduced power flags
+ * @sanitize_ctx: context for dump sanitizer
+ * @sanitize_ops: dump sanitizer ops
+ * @sar_chain_a_profile: SAR chain A profile
+ * @sar_chain_b_profile: SAR chain B profile
+ * @sgom_enabled: SGOM enabled
+ * @sgom_table: SGOM table
+ * @timestamp: timestamp marker data
+ * @timestamp.wk: timestamp marking worker
+ * @timestamp.seq: timestamp marking sequence
+ * @timestamp.delay: timestamp marking worker delay
+ * @tpc_enabled: TPC enabled
*/
struct iwl_fw_runtime {
struct iwl_trans *trans;
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
index a607e7ab914b..076810ee5d34 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h
@@ -170,7 +170,6 @@ struct iwl_fw_mon_regs {
* for aggregation
* @min_txq_size: minimum number of slots required in a TX queue
* @gp2_reg_addr: GP2 (timer) register address
- * @min_umac_error_event_table: minimum SMEM location of UMAC error table
* @mon_dbgi_regs: monitor DBGI registers
* @mon_dram_regs: monitor DRAM registers
* @mon_smem_regs: monitor SMEM registers
@@ -203,7 +202,6 @@ struct iwl_family_base_params {
netdev_features_t features;
u32 smem_offset;
u32 smem_len;
- u32 min_umac_error_event_table;
u32 d3_debug_data_base_addr;
u32 d3_debug_data_length;
u32 min_txq_size;
@@ -385,7 +383,7 @@ struct iwl_mac_cfg {
#define IWL_NUM_RBDS_EHT (512 * 8)
/**
- * struct iwl_rf_cfg
+ * struct iwl_rf_cfg - RF/CRF configuration data
* @fw_name_pre: Firmware filename prefix. The api version and extension
* (.ucode) will be added to filename before loading from disk. The
* filename is constructed as <fw_name_pre>-<api>.ucode.
@@ -418,6 +416,7 @@ struct iwl_mac_cfg {
* @vht_mu_mimo_supported: VHT MU-MIMO support
* @nvm_type: see &enum iwl_nvm_type
* @uhb_supported: ultra high band channels supported
+ * @eht_supported: EHT supported
* @num_rbds: number of receive buffer descriptors to use
* (only used for multi-queue capable devices)
*
@@ -450,7 +449,8 @@ struct iwl_rf_cfg {
host_interrupt_operation_mode:1,
lp_xtal_workaround:1,
vht_mu_mimo_supported:1,
- uhb_supported:1;
+ uhb_supported:1,
+ eht_supported:1;
u8 valid_tx_ant;
u8 valid_rx_ant;
u8 non_shared_ant;
@@ -686,8 +686,10 @@ extern const char iwl_be211_name[];
extern const char iwl_killer_bn1850w2_name[];
extern const char iwl_killer_bn1850i_name[];
extern const char iwl_bn201_name[];
+extern const char iwl_bn203_name[];
extern const char iwl_be221_name[];
extern const char iwl_be223_name[];
+extern const char iwl_ax221_name[];
#if IS_ENABLED(CONFIG_IWLDVM)
extern const struct iwl_rf_cfg iwl5300_agn_cfg;
extern const struct iwl_rf_cfg iwl5350_agn_cfg;
@@ -743,6 +745,7 @@ extern const struct iwl_rf_cfg iwl_rf_fm;
extern const struct iwl_rf_cfg iwl_rf_fm_160mhz;
#define iwl_rf_wh iwl_rf_fm
#define iwl_rf_wh_160mhz iwl_rf_fm_160mhz
+extern const struct iwl_rf_cfg iwl_rf_wh_non_eht;
#define iwl_rf_pe iwl_rf_fm
#endif /* CONFIG_IWLMLD */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h
index 7ed6329fd8ca..fe4e46a0edbd 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2018-2023 Intel Corporation
+ * Copyright (C) 2018-2023, 2025 Intel Corporation
*/
#ifndef __iwl_dbg_tlv_h__
#define __iwl_dbg_tlv_h__
@@ -32,7 +32,7 @@ union iwl_dbg_tlv_tp_data {
};
/**
- * struct iwl_dbg_tlv_time_point_data
+ * struct iwl_dbg_tlv_time_point_data - debug time point data
* @trig_list: list of triggers
* @active_trig_list: list of active triggers
* @hcmd_list: list of host commands
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 607fcea6f4ef..3391f07b01de 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -177,9 +177,10 @@ static inline char iwl_drv_get_step(int step)
return 'a' + step;
}
-static bool iwl_drv_is_wifi7_supported(struct iwl_trans *trans)
+bool iwl_drv_is_wifi7_supported(struct iwl_trans *trans)
{
- return CSR_HW_RFID_TYPE(trans->info.hw_rf_id) >= IWL_CFG_RF_TYPE_FM;
+ return trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ &&
+ CSR_HW_RFID_TYPE(trans->info.hw_rf_id) >= IWL_CFG_RF_TYPE_FM;
}
const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf)
@@ -347,8 +348,8 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first)
if (first)
drv->fw_index = ucode_api_max;
- else if (drv->fw_index == ENCODE_CORE_AS_API(99))
- drv->fw_index = 101; /* last API-scheme number below core 99 */
+ else if (drv->fw_index == ENCODE_CORE_AS_API(100))
+ drv->fw_index = 102; /* last API-scheme number below core 100 */
else
drv->fw_index--;
@@ -427,7 +428,6 @@ struct iwl_firmware_pieces {
size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
struct iwl_fw_dbg_mem_seg_tlv *dbg_mem_tlv;
size_t n_mem_tlv;
- u32 major;
};
static void alloc_sec_data(struct iwl_firmware_pieces *pieces,
@@ -1069,19 +1069,19 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
break;
case IWL_UCODE_TLV_FW_VERSION: {
const __le32 *ptr = (const void *)tlv_data;
- u32 minor;
+ u32 major, minor;
u8 local_comp;
if (tlv_len != sizeof(u32) * 3)
goto invalid_tlv_len;
- pieces->major = le32_to_cpup(ptr++);
+ major = le32_to_cpup(ptr++);
minor = le32_to_cpup(ptr++);
local_comp = le32_to_cpup(ptr);
snprintf(drv->fw.fw_version,
sizeof(drv->fw.fw_version),
- "%u.%08x.%u %s", pieces->major, minor,
+ "%u.%08x.%u %s", major, minor,
local_comp, iwl_reduced_fw_name(drv));
break;
}
@@ -1589,8 +1589,6 @@ static void _iwl_op_mode_stop(struct iwl_drv *drv)
}
}
-#define IWL_MLD_SUPPORTED_FW_VERSION 97
-
/*
* iwl_req_fw_callback - callback when firmware was loaded
*
@@ -1859,17 +1857,8 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
}
#if IS_ENABLED(CONFIG_IWLMLD)
- if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION &&
- iwl_drv_is_wifi7_supported(drv->trans))
+ if (iwl_drv_is_wifi7_supported(drv->trans))
op = &iwlwifi_opmode_table[MLD_OP_MODE];
-#else
- if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION &&
- iwl_drv_is_wifi7_supported(drv->trans)) {
- IWL_ERR(drv,
- "IWLMLD needs to be compiled to support this firmware\n");
- mutex_unlock(&iwlwifi_opmode_table_mtx);
- goto out_unbind;
- }
#endif
IWL_INFO(drv, "loaded firmware version %s op_mode %s\n",
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
index 595300a14639..6e60953de2ec 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h
@@ -62,7 +62,8 @@ struct iwl_rf_cfg;
* starts the driver: fetches the firmware. This should be called by bus
* specific system flows implementations. For example, the bus specific probe
* function should do bus related operations only, and then call to this
- * function. It returns the driver object or %NULL if an error occurred.
+ * function.
+ * Return: the driver object or %NULL if an error occurred.
*/
struct iwl_drv *iwl_drv_start(struct iwl_trans *trans);
@@ -78,6 +79,12 @@ struct iwl_drv *iwl_drv_start(struct iwl_trans *trans);
void iwl_drv_stop(struct iwl_drv *drv);
/*
+ * iwl_drv_is_wifi7_supported - returns if wifi7 is supported
+ * If yes, iwlmld needs to be used to drive the device.
+ */
+bool iwl_drv_is_wifi7_supported(struct iwl_trans *trans);
+
+/*
* exported symbol management
*
* The driver can be split into multiple modules, in which case some symbols
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
index 21eabfc3ffc8..0476df7b7f17 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2005-2014, 2018-2022, 2024 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2022, 2024-2025 Intel Corporation
*/
#ifndef __iwl_modparams_h__
#define __iwl_modparams_h__
@@ -42,7 +42,7 @@ enum iwl_uapsd_disable {
};
/**
- * struct iwl_mod_params
+ * struct iwl_mod_params - module parameters for iwlwifi
*
* Holds the module parameters
*
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 23465e4c4b39..e021fc57d85d 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -2080,7 +2080,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
!!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED);
nvm->sku_cap_mimo_disabled =
!!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
- if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) >= IWL_CFG_RF_TYPE_FM)
+ if (trans->cfg->eht_supported)
nvm->sku_cap_11be_enable = true;
/* Initialize PHY sku data */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
index cbc92abf9f87..12f28bb0e859 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h
@@ -115,11 +115,12 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
* iwl_parse_nvm_mcc_info - parse MCC (mobile country code) info coming from FW
*
* This function parses the regulatory channel data received as a
- * MCC_UPDATE_CMD command. It returns a newly allocation regulatory domain,
- * to be fed into the regulatory core. In case the geo_info is set handle
- * accordingly. An ERR_PTR is returned on error.
- * If not given to the regulatory core, the user is responsible for freeing
- * the regdomain returned here with kfree.
+ * MCC_UPDATE_CMD command.
+ *
+ * Return: a newly allocation regulatory domain, to be given to the regulatory
+ * core. In case the geo_info is set handle accordingly. An ERR_PTR is
+ * returned on error. If not given to the regulatory core, the user is
+ * responsible for freeing the regdomain returned here with kfree().
*
* @trans: the transport
* @num_of_ch: the number of channels
@@ -140,6 +141,8 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
* This struct holds an NVM section read from the NIC using NVM_ACCESS_CMD,
* and saved for later use by the driver. Not all NVM sections are saved
* this way, only the needed ones.
+ * @length: length of the section
+ * @data: section data
*/
struct iwl_nvm_section {
u16 length;
@@ -148,6 +151,10 @@ struct iwl_nvm_section {
/**
* iwl_read_external_nvm - Reads external NVM from a file into nvm_sections
+ * @trans: the transport
+ * @nvm_file_name: the filename to request
+ * @nvm_sections: sections data to fill
+ * Return: 0 on success or an error code
*/
int iwl_read_external_nvm(struct iwl_trans *trans,
const char *nvm_file_name,
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
index a146d0e399f2..df6341dfc4a1 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h
@@ -185,6 +185,7 @@ void iwl_opmode_deregister(const char *name);
/**
* struct iwl_op_mode - operational mode
* @ops: pointer to its own ops
+ * @op_mode_specific: per-opmode data
*
* This holds an implementation of the mac80211 / fw API.
*/
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
index 5232f66c2d52..cc8a84018f70 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -129,7 +129,7 @@ static enum iwl_reset_mode
iwl_trans_determine_restart_mode(struct iwl_trans *trans)
{
struct iwl_trans_dev_restart_data *data;
- enum iwl_reset_mode at_least = 0;
+ enum iwl_reset_mode min_mode = 0;
unsigned int index;
static const enum iwl_reset_mode escalation_list_old[] = {
IWL_RESET_MODE_SW_RESET,
@@ -173,11 +173,11 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
}
if (trans->restart.during_reset)
- at_least = IWL_RESET_MODE_REPROBE;
+ min_mode = IWL_RESET_MODE_REPROBE;
data = iwl_trans_get_restart_data(trans->dev);
if (!data)
- return at_least;
+ return min_mode;
if (!data->backoff &&
ktime_get_boottime_seconds() - data->last_error >=
@@ -194,7 +194,7 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
data->backoff = false;
}
- return max(at_least, escalation_list[index]);
+ return max(min_mode, escalation_list[index]);
}
#define IWL_TRANS_TOP_FOLLOWER_WAIT 180 /* ms */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index a0cc5d7745e8..a552669db6e2 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -121,7 +121,7 @@ enum CMD_MODE {
#define DEF_CMD_PAYLOAD_SIZE 320
/**
- * struct iwl_device_cmd
+ * struct iwl_device_cmd - device command structure
*
* For allocation of the command and tx queues, this establishes the overall
* size of the largest command we send to uCode, except for commands that
@@ -516,7 +516,7 @@ enum iwl_trans_state {
*/
/**
- * enum iwl_ini_cfg_state
+ * enum iwl_ini_cfg_state - debug config state
* @IWL_INI_CFG_STATE_NOT_LOADED: no debug cfg was given
* @IWL_INI_CFG_STATE_LOADED: debug cfg was found and loaded
* @IWL_INI_CFG_STATE_CORRUPTED: debug cfg was found and some of the TLVs
@@ -532,7 +532,7 @@ enum iwl_ini_cfg_state {
#define IWL_TRANS_NMI_TIMEOUT (HZ / 4)
/**
- * struct iwl_dram_data
+ * struct iwl_dram_data - DRAM data descriptor
* @physical: page phy pointer
* @block: pointer to the allocated block/page
* @size: size of the block/page
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
index 49accf96f44b..5d23a618ae3c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/constants.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h
@@ -75,5 +75,7 @@
#define IWL_MLD_FTM_RESP_LMR_FEEDBACK_SUPPORT true
#define IWL_MLD_FTM_NON_TB_MIN_TIME_BETWEEN_MSR 7
#define IWL_MLD_FTM_NON_TB_MAX_TIME_BETWEEN_MSR 1000
+#define IWL_MLD_STA_EXT_CAPA_SIZE 9
+#define IWL_MLD_EXT_CAPA_NUM_IFTYPES 1
#endif /* __iwl_mld_constants_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
index 1d4282a21f09..dd85be94433c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
@@ -1794,6 +1794,10 @@ iwl_mld_send_proto_offload(struct iwl_mld *mld,
u32 enabled = 0;
cmd = kzalloc(hcmd.len[0], GFP_KERNEL);
+ if (!cmd) {
+ IWL_DEBUG_WOWLAN(mld, "Failed to allocate proto offload cmd\n");
+ return -ENOMEM;
+ }
#if IS_ENABLED(CONFIG_IPV6)
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
index b372173c4a79..19da521a4bab 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c
@@ -124,9 +124,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
u16 status;
switch (version) {
- case 6:
case 7:
- expected_sz = sizeof(struct iwl_alive_ntf_v6);
+ expected_sz = sizeof(struct iwl_alive_ntf_v7);
break;
case 8:
expected_sz = sizeof(struct iwl_alive_ntf);
@@ -168,11 +167,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) &
~FW_ADDR_CACHE_CONTROL;
- if (umac_error_table >= trans->mac_cfg->base->min_umac_error_event_table)
- iwl_fw_umac_set_alive_err_table(trans, umac_error_table);
- else
- IWL_ERR(mld, "Not valid error log pointer 0x%08X\n",
- umac_error_table);
+ iwl_fw_umac_set_alive_err_table(trans, umac_error_table);
alive_data->valid = status == IWL_ALIVE_STATUS_OK;
@@ -188,9 +183,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
le32_to_cpu(umac->umac_major),
le32_to_cpu(umac->umac_minor));
- if (version >= 7)
- IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n",
- le16_to_cpu(palive->flags));
+ IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n",
+ le16_to_cpu(palive->flags));
if (version >= 8)
IWL_DEBUG_FW(mld, "platform_id 0x%llx\n",
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index ed379825a923..a5ececfc13e4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -528,6 +528,19 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
mld_link = &iwl_mld_vif_from_mac80211(vif)->deflink;
+ /* len_low should be 2 + n*13 (where n is the number of descriptors.
+ * 13 is the size of a NoA descriptor). We can have either one or two
+ * descriptors.
+ */
+ if (IWL_FW_CHECK(mld, notif->noa_active &&
+ notif->noa_attr.len_low != 2 +
+ sizeof(struct ieee80211_p2p_noa_desc) &&
+ notif->noa_attr.len_low != 2 +
+ sizeof(struct ieee80211_p2p_noa_desc) * 2,
+ "Invalid noa_attr.len_low (%d)\n",
+ notif->noa_attr.len_low))
+ return;
+
new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
if (!new_data)
return;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index 738f80fe0c50..d89840a1152b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -465,10 +465,13 @@ int iwl_mld_add_link(struct iwl_mld *mld,
int ret;
if (!link) {
- if (is_deflink)
+ if (is_deflink) {
link = &mld_vif->deflink;
- else
+ } else {
link = kzalloc(sizeof(*link), GFP_KERNEL);
+ if (!link)
+ return -ENOMEM;
+ }
} else {
WARN_ON(!mld->fw_status.in_hw_restart);
}
@@ -501,6 +504,7 @@ void iwl_mld_remove_link(struct iwl_mld *mld,
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
bool is_deflink = link == &mld_vif->deflink;
+ u8 fw_id = link->fw_id;
if (WARN_ON(!link || link->active))
return;
@@ -513,10 +517,10 @@ void iwl_mld_remove_link(struct iwl_mld *mld,
RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
- if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links))
+ if (WARN_ON(fw_id >= mld->fw->ucode_capa.num_links))
return;
- RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
+ RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL);
}
void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
@@ -571,8 +575,12 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
/* Not in EMLSR and we can't hear the link.
* Try to switch to a better link. EMLSR case is handled below.
*/
- if (!iwl_mld_emlsr_active(vif))
+ if (!iwl_mld_emlsr_active(vif)) {
+ IWL_DEBUG_EHT(mld,
+ "missed beacons exceeds threshold. link_id=%u. Try to switch to a better link.\n",
+ link_id);
iwl_mld_int_mlo_scan(mld, vif);
+ }
}
/* no more logic if we're not in EMLSR */
@@ -591,7 +599,8 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
return;
IWL_DEBUG_EHT(mld,
- "missed bcn on the other link (link_id=%u): %u\n",
+ "missed bcn link_id=%u: %u consecutive=%u, other link_id=%u: %u\n",
+ link_id, missed_bcon, missed_bcon_since_rx,
other_link->link_id, scnd_lnk_bcn_lost);
/* Exit EMLSR if we lost more than
@@ -707,18 +716,13 @@ static int
iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
struct ieee80211_bss_conf *link_conf)
{
- struct ieee80211_vif *vif = link_conf->vif;
const struct cfg80211_bss_ies *ies;
const struct element *bss_load_elem = NULL;
const struct ieee80211_bss_load_elem *bss_load;
guard(rcu)();
- if (ieee80211_vif_link_active(vif, link_conf->link_id))
- ies = rcu_dereference(link_conf->bss->beacon_ies);
- else
- ies = rcu_dereference(link_conf->bss->ies);
-
+ ies = rcu_dereference(link_conf->bss->beacon_ies);
if (ies)
bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD,
ies->data, ies->len);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 5725104a53bf..55b484c16280 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -23,6 +23,7 @@
#include "roc.h"
#include "mlo.h"
#include "stats.h"
+#include "iwl-nvm-parse.h"
#include "ftm-initiator.h"
#include "low_latency.h"
#include "fw/api/scan.h"
@@ -75,13 +76,12 @@ iwl_mld_iface_combinations[] = {
},
};
-static const u8 if_types_ext_capa_sta[] = {
- [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
- [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
- [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF |
- WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB,
- [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,
- [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT,
+static const u8 ext_capa_base[IWL_MLD_STA_EXT_CAPA_SIZE] = {
+ [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING,
+ [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT,
+ [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF |
+ WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB,
+ [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB,
};
#define IWL_MLD_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \
@@ -94,18 +94,6 @@ static const u8 if_types_ext_capa_sta[] = {
IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \
IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT)
-static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = {
- {
- .iftype = NL80211_IFTYPE_STATION,
- .extended_capabilities = if_types_ext_capa_sta,
- .extended_capabilities_mask = if_types_ext_capa_sta,
- .extended_capabilities_len = sizeof(if_types_ext_capa_sta),
- /* relevant only if EHT is supported */
- .eml_capabilities = IWL_MLD_EMLSR_CAPA,
- .mld_capa_and_ops = IWL_MLD_CAPA_OPS,
- },
-};
-
static void iwl_mld_hw_set_addresses(struct iwl_mld *mld)
{
struct wiphy *wiphy = mld->wiphy;
@@ -335,21 +323,37 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
if (fw_has_capa(ucode_capa, IWL_UCODE_TLV_CAPA_PROTECTED_TWT))
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PROTECTED_TWT);
- wiphy->iftype_ext_capab = NULL;
- wiphy->num_iftype_ext_capab = 0;
-
- if (!iwlwifi_mod_params.disable_11ax) {
- wiphy->iftype_ext_capab = iftypes_ext_capa;
- wiphy->num_iftype_ext_capab = ARRAY_SIZE(iftypes_ext_capa);
-
- ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
- ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);
- }
-
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
else
wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
+ /* We are done for non-HE */
+ if (iwlwifi_mod_params.disable_11ax)
+ return;
+
+ ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
+ ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);
+
+ wiphy->iftype_ext_capab = mld->ext_capab;
+ wiphy->num_iftype_ext_capab = ARRAY_SIZE(mld->ext_capab);
+
+ BUILD_BUG_ON(sizeof(mld->sta_ext_capab) < sizeof(ext_capa_base));
+
+ memcpy(mld->sta_ext_capab, ext_capa_base, sizeof(ext_capa_base));
+
+ mld->ext_capab[0].iftype = NL80211_IFTYPE_STATION;
+ mld->ext_capab[0].extended_capabilities = mld->sta_ext_capab;
+ mld->ext_capab[0].extended_capabilities_mask = mld->sta_ext_capab;
+ mld->ext_capab[0].extended_capabilities_len = sizeof(mld->sta_ext_capab);
+
+ if (!mld->nvm_data->sku_cap_11be_enable ||
+ iwlwifi_mod_params.disable_11be)
+ return;
+
+ mld->ext_capab[0].eml_capabilities = IWL_MLD_EMLSR_CAPA;
+ mld->ext_capab[0].mld_capa_and_ops = IWL_MLD_CAPA_OPS;
+
}
static void iwl_mac_hw_set_misc(struct iwl_mld *mld)
@@ -393,11 +397,9 @@ static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld)
TLC_MNG_UPDATE_NOTIF, 0) >= 4) +
(iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP,
REPLY_RX_MPDU_CMD, 0) >= 6) +
- (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP,
- RX_NO_DATA_NOTIF, 0) >= 4) +
(iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, TX_CMD, 0) >= 9);
- if (ratecheck != 0 && ratecheck != 5) {
+ if (ratecheck != 0 && ratecheck != 4) {
IWL_ERR(mld, "Firmware has inconsistent rates\n");
return -EINVAL;
}
@@ -680,6 +682,8 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw,
#endif
iwl_mld_rm_vif(mld, vif);
+
+ mld->monitor.phy.valid = false;
}
struct iwl_mld_mc_iter_data {
@@ -2591,11 +2595,44 @@ iwl_mld_can_neg_ttlm(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return NEG_TTLM_RES_ACCEPT;
}
+static int iwl_mld_get_antenna(struct ieee80211_hw *hw, int radio_idx,
+ u32 *tx_ant, u32 *rx_ant)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ *tx_ant = iwl_mld_get_valid_tx_ant(mld);
+ *rx_ant = iwl_mld_get_valid_rx_ant(mld);
+
+ return 0;
+}
+
+static int iwl_mld_set_antenna(struct ieee80211_hw *hw, int radio_idx,
+ u32 tx_ant, u32 rx_ant)
+{
+ struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
+
+ if (WARN_ON(!mld->nvm_data))
+ return -EBUSY;
+
+ /* mac80211 ensures the device is not started,
+ * so the firmware cannot be running
+ */
+
+ mld->set_tx_ant = tx_ant;
+ mld->set_rx_ant = rx_ant;
+
+ iwl_reinit_cab(mld->trans, mld->nvm_data, tx_ant, rx_ant, mld->fw);
+
+ return 0;
+}
+
const struct ieee80211_ops iwl_mld_hw_ops = {
.tx = iwl_mld_mac80211_tx,
.start = iwl_mld_mac80211_start,
.stop = iwl_mld_mac80211_stop,
.config = iwl_mld_mac80211_config,
+ .get_antenna = iwl_mld_get_antenna,
+ .set_antenna = iwl_mld_set_antenna,
.add_interface = iwl_mld_mac80211_add_interface,
.remove_interface = iwl_mld_mac80211_remove_interface,
.conf_tx = iwl_mld_mac80211_conf_tx,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
index a6962256bdd1..8a4c96385640 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -259,6 +259,7 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {
HCMD_NAME(MONITOR_NOTIF),
HCMD_NAME(TLC_MNG_UPDATE_NOTIF),
HCMD_NAME(BEACON_FILTER_IN_NOTIF),
+ HCMD_NAME(PHY_AIR_SNIFFER_NOTIF),
HCMD_NAME(MU_GROUP_MGMT_NOTIF),
};
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
index 94dc9da6360d..22efe8e10f53 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -118,7 +118,11 @@
* @monitor.cur_bssid: current bssid tracked by the sniffer
* @monitor.ptp_time: set the Rx mactime using the device's PTP clock time
* @monitor.p80: primary channel position relative to he whole bandwidth, in
- * steps of 80 MHz
+ * steps of 80 MHz
+ * @monitor.phy: PHY data information
+ * @monitor.phy.data: PHY data (&struct iwl_rx_phy_air_sniffer_ntfy) received
+ * @monitor.phy.valid: PHY data is valid (was received)
+ * @monitor.phy.used: PHY data was used by an RX
* @fw_id_to_link_sta: maps a fw id of a sta to the corresponding
* ieee80211_link_sta. This is not cleaned up on restart since we want to
* preserve the fw sta ids during a restart (for SN/PN restoring).
@@ -134,6 +138,8 @@
* @fw: a pointer to the fw object
* @hw: pointer to the hw object.
* @wiphy: a pointer to the wiphy struct, for easier access to it.
+ * @ext_capab: extended capabilities that will be set to wiphy on registration.
+ * @sta_ext_capab: extended capabilities for the station interface.
* @nvm_data: pointer to the nvm_data that includes all our capabilities
* @fwrt: fw runtime data
* @debugfs_dir: debugfs directory
@@ -180,6 +186,8 @@
* @mcast_filter_cmd: pointer to the multicast filter command.
* @mgmt_tx_ant: stores the last TX antenna index; used for setting
* TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure).
+ * @set_tx_ant: stores the last TX antenna bitmask set by user space (if any)
+ * @set_rx_ant: stores the last RX antenna bitmask set by user space (if any)
* @fw_rates_ver_3: FW rates are in version 3
* @low_latency: low-latency manager.
* @tzone: thermal zone device's data
@@ -205,6 +213,10 @@ struct iwl_mld {
u32 ampdu_ref;
bool ampdu_toggle;
u8 p80;
+ struct {
+ struct iwl_rx_phy_air_sniffer_ntfy data;
+ u8 valid:1, used:1;
+ } phy;
#ifdef CONFIG_IWLWIFI_DEBUGFS
__le16 cur_aid;
u8 cur_bssid[ETH_ALEN];
@@ -225,6 +237,8 @@ struct iwl_mld {
const struct iwl_fw *fw;
struct ieee80211_hw *hw;
struct wiphy *wiphy;
+ struct wiphy_iftype_ext_capab ext_capab[IWL_MLD_EXT_CAPA_NUM_IFTYPES];
+ u8 sta_ext_capab[IWL_MLD_STA_EXT_CAPA_SIZE];
struct iwl_nvm_data *nvm_data;
struct iwl_fw_runtime fwrt;
struct dentry *debugfs_dir;
@@ -279,6 +293,9 @@ struct iwl_mld {
u8 mgmt_tx_ant;
+ u8 set_tx_ant;
+ u8 set_rx_ant;
+
bool fw_rates_ver_3;
struct iwl_mld_low_latency low_latency;
@@ -374,6 +391,9 @@ static inline u8 iwl_mld_get_valid_tx_ant(const struct iwl_mld *mld)
if (mld->nvm_data && mld->nvm_data->valid_tx_ant)
tx_ant &= mld->nvm_data->valid_tx_ant;
+ if (mld->set_tx_ant)
+ tx_ant &= mld->set_tx_ant;
+
return tx_ant;
}
@@ -384,6 +404,9 @@ static inline u8 iwl_mld_get_valid_rx_ant(const struct iwl_mld *mld)
if (mld->nvm_data && mld->nvm_data->valid_rx_ant)
rx_ant &= mld->nvm_data->valid_rx_ant;
+ if (mld->set_rx_ant)
+ rx_ant &= mld->set_rx_ant;
+
return rx_ant;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index 241a6271d13d..c6b151f26921 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -31,11 +31,9 @@ static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask)
{
#define NAME_FMT(x) "%s"
#define NAME_PR(x) (mask & IWL_MLD_EMLSR_BLOCKED_##x) ? "[" #x "]" : "",
- IWL_DEBUG_INFO(mld,
- "EMLSR blocked = " HANDLE_EMLSR_BLOCKED_REASONS(NAME_FMT)
- " (0x%x)\n",
- HANDLE_EMLSR_BLOCKED_REASONS(NAME_PR)
- mask);
+ IWL_DEBUG_EHT(mld,
+ "EMLSR blocked = " HANDLE_EMLSR_BLOCKED_REASONS(NAME_FMT)
+ " (0x%x)\n", HANDLE_EMLSR_BLOCKED_REASONS(NAME_PR) mask);
#undef NAME_FMT
#undef NAME_PR
}
@@ -72,11 +70,9 @@ static void iwl_mld_print_emlsr_exit(struct iwl_mld *mld, u32 mask)
{
#define NAME_FMT(x) "%s"
#define NAME_PR(x) (mask & IWL_MLD_EMLSR_EXIT_##x) ? "[" #x "]" : "",
- IWL_DEBUG_INFO(mld,
- "EMLSR exit = " HANDLE_EMLSR_EXIT_REASONS(NAME_FMT)
- " (0x%x)\n",
- HANDLE_EMLSR_EXIT_REASONS(NAME_PR)
- mask);
+ IWL_DEBUG_EHT(mld,
+ "EMLSR exit = " HANDLE_EMLSR_EXIT_REASONS(NAME_FMT)
+ " (0x%x)\n", HANDLE_EMLSR_EXIT_REASONS(NAME_PR) mask);
#undef NAME_FMT
#undef NAME_PR
}
@@ -170,10 +166,10 @@ static void iwl_mld_check_emlsr_prevention(struct iwl_mld *mld,
WARN_ON(mld_vif->emlsr.exit_repeat_count > 3);
}
- IWL_DEBUG_INFO(mld,
- "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
- delay / HZ, mld_vif->emlsr.exit_repeat_count,
- iwl_mld_get_emlsr_exit_string(reason), reason);
+ IWL_DEBUG_EHT(mld,
+ "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n",
+ delay / HZ, mld_vif->emlsr.exit_repeat_count,
+ iwl_mld_get_emlsr_exit_string(reason), reason);
wiphy_delayed_work_queue(mld->wiphy,
&mld_vif->emlsr.prevent_done_wk, delay);
@@ -217,10 +213,10 @@ static int _iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
link_to_keep = __ffs(vif->active_links);
new_active_links = BIT(link_to_keep);
- IWL_DEBUG_INFO(mld,
- "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
- iwl_mld_get_emlsr_exit_string(exit), exit,
- vif->active_links, new_active_links);
+ IWL_DEBUG_EHT(mld,
+ "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n",
+ iwl_mld_get_emlsr_exit_string(exit), exit,
+ vif->active_links, new_active_links);
if (sync)
ret = ieee80211_set_active_links(vif, new_active_links);
@@ -262,9 +258,8 @@ static int _iwl_mld_emlsr_block(struct iwl_mld *mld, struct ieee80211_vif *vif,
mld_vif->emlsr.blocked_reasons |= reason;
- IWL_DEBUG_INFO(mld,
- "Blocking EMLSR mode. reason = %s (0x%x)\n",
- iwl_mld_get_emlsr_blocked_string(reason), reason);
+ IWL_DEBUG_EHT(mld, "Blocking EMLSR mode. reason = %s (0x%x)\n",
+ iwl_mld_get_emlsr_blocked_string(reason), reason);
iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons);
if (reason == IWL_MLD_EMLSR_BLOCKED_TPT)
@@ -335,9 +330,8 @@ void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
mld_vif->emlsr.blocked_reasons &= ~reason;
- IWL_DEBUG_INFO(mld,
- "Unblocking EMLSR mode. reason = %s (0x%x)\n",
- iwl_mld_get_emlsr_blocked_string(reason), reason);
+ IWL_DEBUG_EHT(mld, "Unblocking EMLSR mode. reason = %s (0x%x)\n",
+ iwl_mld_get_emlsr_blocked_string(reason), reason);
iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons);
if (reason == IWL_MLD_EMLSR_BLOCKED_TPT)
@@ -348,7 +342,7 @@ void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif,
if (mld_vif->emlsr.blocked_reasons)
return;
- IWL_DEBUG_INFO(mld, "EMLSR is unblocked\n");
+ IWL_DEBUG_EHT(mld, "EMLSR is unblocked\n");
iwl_mld_int_mlo_scan(mld, vif);
}
@@ -365,18 +359,17 @@ iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac,
switch (action) {
case ESR_RECOMMEND_LEAVE:
- IWL_DEBUG_INFO(mld_vif->mld,
- "FW recommend leave reason = 0x%x\n",
- le32_to_cpu(notif->leave_reason_mask));
+ IWL_DEBUG_EHT(mld_vif->mld,
+ "FW recommend leave reason = 0x%x\n",
+ le32_to_cpu(notif->leave_reason_mask));
iwl_mld_exit_emlsr(mld_vif->mld, vif,
IWL_MLD_EMLSR_EXIT_FW_REQUEST,
iwl_mld_get_primary_link(vif));
break;
case ESR_FORCE_LEAVE:
- IWL_DEBUG_INFO(mld_vif->mld,
- "FW force leave reason = 0x%x\n",
- le32_to_cpu(notif->leave_reason_mask));
+ IWL_DEBUG_EHT(mld_vif->mld, "FW force leave reason = 0x%x\n",
+ le32_to_cpu(notif->leave_reason_mask));
fallthrough;
case ESR_RECOMMEND_ENTER:
default:
@@ -412,11 +405,12 @@ void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld,
struct ieee80211_bss_conf *bss_conf =
iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
- IWL_DEBUG_INFO(mld, "Failed to %s EMLSR on link %d (FW: %d), reason %d\n",
- le32_to_cpu(notif->activation) ? "enter" : "exit",
- bss_conf ? bss_conf->link_id : -1,
- le32_to_cpu(notif->link_id),
- le32_to_cpu(notif->err_code));
+ IWL_DEBUG_EHT(mld,
+ "Failed to %s EMLSR on link %d (FW: %d), reason %d\n",
+ le32_to_cpu(notif->activation) ? "enter" : "exit",
+ bss_conf ? bss_conf->link_id : -1,
+ le32_to_cpu(notif->link_id),
+ le32_to_cpu(notif->err_code));
if (IWL_FW_CHECK(mld, !bss_conf,
"FW reported failure to %sactivate EMLSR on a non-existing link: %d\n",
@@ -590,8 +584,8 @@ void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk)
spin_unlock_bh(&queue_counter->lock);
}
- IWL_DEBUG_INFO(mld, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n",
- total_tx, total_rx);
+ IWL_DEBUG_EHT(mld, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n",
+ total_tx, total_rx);
/* If we don't have enough MPDUs - exit EMLSR */
if (total_tx < IWL_MLD_ENTER_EMLSR_TPT_THRESH &&
@@ -603,10 +597,10 @@ void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk)
/* EMLSR is not active */
if (sec_link_id == -1)
- return;
+ goto schedule;
- IWL_DEBUG_INFO(mld, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n",
- sec_link_id, sec_link_tx, sec_link_rx);
+ IWL_DEBUG_EHT(mld, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n",
+ sec_link_id, sec_link_tx, sec_link_rx);
/* Calculate the percentage of the secondary link TX/RX */
sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0;
@@ -625,6 +619,7 @@ void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk)
return;
}
+schedule:
/* Check again when the next window ends */
wiphy_delayed_work_queue(mld_vif->mld->wiphy,
&mld_vif->emlsr.check_tpt_wk,
@@ -702,10 +697,8 @@ iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld,
ret |= IWL_MLD_EMLSR_EXIT_CSA;
if (ret) {
- IWL_DEBUG_INFO(mld,
- "Link %d is not allowed for EMLSR as %s\n",
- link->link_id,
- primary ? "primary" : "secondary");
+ IWL_DEBUG_EHT(mld, "Link %d is not allowed for EMLSR as %s\n",
+ link->link_id, primary ? "primary" : "secondary");
iwl_mld_print_emlsr_exit(mld, ret);
}
@@ -869,13 +862,12 @@ iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif,
reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD;
if (reason_mask) {
- IWL_DEBUG_INFO(mld,
- "Links %d and %d are not a valid pair for EMLSR\n",
- a->link_id, b->link_id);
- IWL_DEBUG_INFO(mld,
- "Links bandwidth are: %d and %d\n",
- nl80211_chan_width_to_mhz(a->chandef->width),
- nl80211_chan_width_to_mhz(b->chandef->width));
+ IWL_DEBUG_EHT(mld,
+ "Links %d and %d are not a valid pair for EMLSR\n",
+ a->link_id, b->link_id);
+ IWL_DEBUG_EHT(mld, "Links bandwidth are: %d and %d\n",
+ nl80211_chan_width_to_mhz(a->chandef->width),
+ nl80211_chan_width_to_mhz(b->chandef->width));
iwl_mld_print_emlsr_exit(mld, reason_mask);
}
@@ -993,8 +985,8 @@ static void _iwl_mld_select_links(struct iwl_mld *mld,
}
set_active:
- IWL_DEBUG_INFO(mld, "Link selection result: 0x%x. Primary = %d\n",
- new_active, new_primary);
+ IWL_DEBUG_EHT(mld, "Link selection result: 0x%x. Primary = %d\n",
+ new_active, new_primary);
mld_vif->emlsr.selected_primary = new_primary;
mld_vif->emlsr.selected_links = new_active;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
index 884973d0b344..4cf3920b005f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -589,8 +589,8 @@ void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
RX_QUEUES_NOTIFICATION)))
iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0);
- else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF))
- iwl_mld_rx_monitor_no_data(mld, napi, pkt, 0);
+ else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, PHY_AIR_SNIFFER_NOTIF))
+ iwl_mld_handle_phy_air_sniffer_notif(mld, napi, pkt);
else
iwl_mld_rx_notif(mld, rxb, pkt);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c
index 4136c98030d0..4e37a288471e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/roc.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c
@@ -231,7 +231,9 @@ void iwl_mld_handle_roc_notif(struct iwl_mld *mld,
struct ieee80211_vif *vif;
vif = iwl_mld_find_roc_vif(mld, activity);
- if (WARN_ON(!vif))
+ if (IWL_FW_CHECK(mld, !vif,
+ "unexpected ROC notif from FW for activity %d\n",
+ activity))
return;
mld_vif = iwl_mld_vif_from_mac80211(vif);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 20d866dd92c2..6a76e3fcb581 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -18,41 +18,32 @@
/* stores relevant PHY data fields extracted from iwl_rx_mpdu_desc */
struct iwl_mld_rx_phy_data {
- enum iwl_rx_phy_info_type info_type;
- __le32 data0;
- __le32 data1;
- __le32 data2;
- __le32 data3;
- __le32 eht_data4;
- __le32 data5;
- __le16 data4;
+ struct iwl_rx_phy_air_sniffer_ntfy *ntfy;
bool first_subframe;
bool with_data;
- __le32 rx_vec[4];
u32 rate_n_flags;
u32 gp2_on_air_rise;
+ /* phy_info is only valid when we have a frame, i.e. with_data=true */
u16 phy_info;
u8 energy_a, energy_b;
};
static void
-iwl_mld_fill_phy_data(struct iwl_mld *mld,
- struct iwl_rx_mpdu_desc *desc,
- struct iwl_mld_rx_phy_data *phy_data)
+iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld,
+ struct iwl_rx_mpdu_desc *desc,
+ struct iwl_mld_rx_phy_data *phy_data)
{
+ if (unlikely(mld->monitor.phy.valid)) {
+ mld->monitor.phy.used = true;
+ phy_data->ntfy = &mld->monitor.phy.data;
+ }
+
phy_data->phy_info = le16_to_cpu(desc->phy_info);
phy_data->rate_n_flags = iwl_v3_rate_from_v2_v3(desc->v3.rate_n_flags,
mld->fw_rates_ver_3);
phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise);
phy_data->energy_a = desc->v3.energy_a;
phy_data->energy_b = desc->v3.energy_b;
- phy_data->data0 = desc->v3.phy_data0;
- phy_data->data1 = desc->v3.phy_data1;
- phy_data->data2 = desc->v3.phy_data2;
- phy_data->data3 = desc->v3.phy_data3;
- phy_data->data4 = desc->phy_data4;
- phy_data->eht_data4 = desc->phy_eht_data4;
- phy_data->data5 = desc->v3.phy_data5;
phy_data->with_data = true;
}
@@ -217,26 +208,19 @@ static void iwl_mld_fill_signal(struct iwl_mld *mld, int link_id,
}
static void
-iwl_mld_decode_he_phy_ru_alloc(struct iwl_mld_rx_phy_data *phy_data,
- struct ieee80211_radiotap_he *he,
- struct ieee80211_radiotap_he_mu *he_mu,
- struct ieee80211_rx_status *rx_status)
+iwl_mld_he_set_ru_alloc(struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_he *he,
+ u8 ru_with_p80)
{
- /* Unfortunately, we have to leave the mac80211 data
- * incorrect for the case that we receive an HE-MU
- * transmission and *don't* have the HE phy data (due
- * to the bits being used for TSF). This shouldn't
- * happen though as management frames where we need
- * the TSF/timers are not be transmitted in HE-MU.
- */
- u8 ru = le32_get_bits(phy_data->data1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK);
- u32 rate_n_flags = phy_data->rate_n_flags;
- u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
+ u8 ru = ru_with_p80 >> 1;
+ u8 p80 = ru_with_p80 & 1;
u8 offs = 0;
rx_status->bw = RATE_INFO_BW_HE_RU;
he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN);
+ he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN);
switch (ru) {
case 0 ... 36:
@@ -266,227 +250,262 @@ iwl_mld_decode_he_phy_ru_alloc(struct iwl_mld_rx_phy_data *phy_data,
rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996;
break;
}
+
he->data2 |= le16_encode_bits(offs,
IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET);
- he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN);
- if (phy_data->data1 & cpu_to_le32(IWL_RX_PHY_DATA1_HE_RU_ALLOC_SEC80))
- he->data2 |=
- cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC);
-#define CHECK_BW(bw) \
- BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_ ## bw ## MHZ != \
- RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS); \
- BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_ ## bw ## MHZ != \
- RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS)
- CHECK_BW(20);
- CHECK_BW(40);
- CHECK_BW(80);
- CHECK_BW(160);
-
- if (he_mu)
- he_mu->flags2 |=
- le16_encode_bits(u32_get_bits(rate_n_flags,
- RATE_MCS_CHAN_WIDTH_MSK),
- IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW);
- else if (he_type == RATE_MCS_HE_TYPE_TRIG)
- he->data6 |=
- cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) |
- le16_encode_bits(u32_get_bits(rate_n_flags,
- RATE_MCS_CHAN_WIDTH_MSK),
- IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW);
+ he->data2 |= le16_encode_bits(p80, IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC);
}
+#define RTAP_ENC_HE(src, src_msk, dst_msk) \
+ le16_encode_bits(le32_get_bits(src, src_msk), dst_msk)
+
static void
-iwl_mld_decode_he_mu_ext(struct iwl_mld_rx_phy_data *phy_data,
- struct ieee80211_radiotap_he_mu *he_mu)
+iwl_mld_decode_he_mu(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_he *he,
+ struct ieee80211_radiotap_he_mu *he_mu,
+ struct ieee80211_rx_status *rx_status)
{
- u32 phy_data2 = le32_to_cpu(phy_data->data2);
- u32 phy_data3 = le32_to_cpu(phy_data->data3);
- u16 phy_data4 = le16_to_cpu(phy_data->data4);
u32 rate_n_flags = phy_data->rate_n_flags;
- if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CRC_OK)) {
+ he_mu->flags1 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.b,
+ OFDM_RX_FRAME_HE_SIGB_DCM,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM);
+ he_mu->flags1 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.b,
+ OFDM_RX_FRAME_HE_SIGB_MCS,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS);
+ he_mu->flags2 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a1,
+ OFDM_RX_FRAME_HE_PRMBL_PUNC_TYPE,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW);
+ he_mu->flags2 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a2,
+ OFDM_RX_FRAME_HE_MU_NUM_OF_SIGB_SYM_OR_USER_NUM,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS);
+ he_mu->flags2 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.b,
+ OFDM_RX_FRAME_HE_MU_SIGB_COMP,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP);
+
+ if (phy_data->ntfy->flags & IWL_SNIF_FLAG_VALID_RU &&
+ le32_get_bits(phy_data->ntfy->sigs.he.cmn[2],
+ OFDM_RX_FRAME_HE_COMMON_CC1_CRC_OK)) {
he_mu->flags1 |=
cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN |
IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN);
he_mu->flags1 |=
- le16_encode_bits(u32_get_bits(phy_data4,
- IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CTR_RU),
- IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU);
-
- he_mu->ru_ch1[0] = u32_get_bits(phy_data2,
- IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU0);
- he_mu->ru_ch1[1] = u32_get_bits(phy_data3,
- IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU1);
- he_mu->ru_ch1[2] = u32_get_bits(phy_data2,
- IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU2);
- he_mu->ru_ch1[3] = u32_get_bits(phy_data3,
- IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU3);
+ RTAP_ENC_HE(phy_data->ntfy->sigs.he.cmn[2],
+ OFDM_RX_FRAME_HE_CENTER_RU_CC1,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU);
+
+ he_mu->ru_ch1[0] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[0],
+ OFDM_RX_FRAME_HE_RU_ALLOC_0_A1);
+ he_mu->ru_ch1[1] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[1],
+ OFDM_RX_FRAME_HE_RU_ALLOC_1_C1);
+ he_mu->ru_ch1[2] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[0],
+ OFDM_RX_FRAME_HE_RU_ALLOC_0_A2);
+ he_mu->ru_ch1[3] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[1],
+ OFDM_RX_FRAME_HE_RU_ALLOC_1_C2);
}
- if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CRC_OK) &&
+ if (phy_data->ntfy->flags & IWL_SNIF_FLAG_VALID_RU &&
+ le32_get_bits(phy_data->ntfy->sigs.he.cmn[2],
+ OFDM_RX_FRAME_HE_COMMON_CC2_CRC_OK) &&
(rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) != RATE_MCS_CHAN_WIDTH_20) {
he_mu->flags1 |=
cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN |
IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN);
he_mu->flags2 |=
- le16_encode_bits(u32_get_bits(phy_data4,
- IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CTR_RU),
- IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU);
-
- he_mu->ru_ch2[0] = u32_get_bits(phy_data2,
- IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU0);
- he_mu->ru_ch2[1] = u32_get_bits(phy_data3,
- IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU1);
- he_mu->ru_ch2[2] = u32_get_bits(phy_data2,
- IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU2);
- he_mu->ru_ch2[3] = u32_get_bits(phy_data3,
- IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU3);
+ RTAP_ENC_HE(phy_data->ntfy->sigs.he.cmn[2],
+ OFDM_RX_FRAME_HE_CENTER_RU_CC2,
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU);
+
+ he_mu->ru_ch2[0] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[0],
+ OFDM_RX_FRAME_HE_RU_ALLOC_0_B1);
+ he_mu->ru_ch2[1] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[1],
+ OFDM_RX_FRAME_HE_RU_ALLOC_1_D1);
+ he_mu->ru_ch2[2] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[0],
+ OFDM_RX_FRAME_HE_RU_ALLOC_0_B2);
+ he_mu->ru_ch2[3] = le32_get_bits(phy_data->ntfy->sigs.he.cmn[1],
+ OFDM_RX_FRAME_HE_RU_ALLOC_1_D2);
}
+
+#define CHECK_BW(bw) \
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_ ## bw ## MHZ != \
+ RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS)
+ CHECK_BW(20);
+ CHECK_BW(40);
+ CHECK_BW(80);
+ CHECK_BW(160);
+#undef CHECK_BW
+
+ he_mu->flags2 |=
+ le16_encode_bits(u32_get_bits(rate_n_flags, RATE_MCS_CHAN_WIDTH_MSK),
+ IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW);
+
+ iwl_mld_he_set_ru_alloc(rx_status, he,
+ le32_get_bits(phy_data->ntfy->sigs.he.b,
+ OFDM_RX_FRAME_HE_SIGB_STA_RU));
+}
+
+static void
+iwl_mld_decode_he_tb_phy_data(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_he *he,
+ struct ieee80211_rx_status *rx_status)
+{
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u32 nsts;
+
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN);
+
+ he->data4 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.a1,
+ OFDM_RX_HE_TRIG_SPATIAL_REUSE_1,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1);
+ he->data4 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.a1,
+ OFDM_RX_HE_TRIG_SPATIAL_REUSE_2,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2);
+ he->data4 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.a1,
+ OFDM_RX_HE_TRIG_SPATIAL_REUSE_3,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3);
+ he->data4 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.a1,
+ OFDM_RX_HE_TRIG_SPATIAL_REUSE_4,
+ IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4);
+ he->data3 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.a1,
+ OFDM_RX_HE_TRIG_BSS_COLOR,
+ IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
+
+#define CHECK_BW(bw) \
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_ ## bw ## MHZ != \
+ RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS)
+ CHECK_BW(20);
+ CHECK_BW(40);
+ CHECK_BW(80);
+ CHECK_BW(160);
+#undef CHECK_BW
+
+ he->data6 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) |
+ le16_encode_bits(u32_get_bits(rate_n_flags, RATE_MCS_CHAN_WIDTH_MSK),
+ IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW);
+
+ if (!(phy_data->ntfy->flags & IWL_SNIF_FLAG_VALID_TB_RX))
+ return;
+
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN);
+ he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN);
+
+ he->data3 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_CODING_EXTRA_SYM,
+ IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
+ he->data6 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_DOPPLER,
+ IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
+ he->data5 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_PRE_FEC_PAD_FACTOR,
+ IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
+ he->data5 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_PE_DISAMBIG,
+ IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
+ he->data5 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_NUM_OF_LTF_SYM,
+ IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS);
+ he->data6 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he_tb.a2,
+ OFDM_RX_HE_TRIG_TXOP_DURATION,
+ IEEE80211_RADIOTAP_HE_DATA6_TXOP);
+
+ iwl_mld_he_set_ru_alloc(rx_status, he,
+ le32_get_bits(phy_data->ntfy->sigs.he_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_RU));
+
+ nsts = le32_get_bits(phy_data->ntfy->sigs.he_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_NSTS) + 1;
+ rx_status->nss = nsts >> !!(rate_n_flags & RATE_MCS_STBC_MSK);
}
static void
iwl_mld_decode_he_phy_data(struct iwl_mld_rx_phy_data *phy_data,
struct ieee80211_radiotap_he *he,
struct ieee80211_radiotap_he_mu *he_mu,
- struct ieee80211_rx_status *rx_status,
- int queue)
+ struct ieee80211_rx_status *rx_status)
{
- switch (phy_data->info_type) {
- case IWL_RX_PHY_INFO_TYPE_NONE:
- case IWL_RX_PHY_INFO_TYPE_CCK:
- case IWL_RX_PHY_INFO_TYPE_OFDM_LGCY:
- case IWL_RX_PHY_INFO_TYPE_HT:
- case IWL_RX_PHY_INFO_TYPE_VHT_SU:
- case IWL_RX_PHY_INFO_TYPE_VHT_MU:
- case IWL_RX_PHY_INFO_TYPE_EHT_MU:
- case IWL_RX_PHY_INFO_TYPE_EHT_TB:
- case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT:
- case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT:
- return;
- case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
- he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN);
- he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
- IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE1),
- IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1);
- he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
- IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE2),
- IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2);
- he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
- IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE3),
- IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3);
- he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2,
- IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE4),
- IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4);
- fallthrough;
- case IWL_RX_PHY_INFO_TYPE_HE_SU:
- case IWL_RX_PHY_INFO_TYPE_HE_MU:
- case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
- case IWL_RX_PHY_INFO_TYPE_HE_TB:
- /* HE common */
- he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN);
- he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN |
- IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN);
- he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_BSS_COLOR_MASK),
- IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
- if (phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB &&
- phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB_EXT) {
- he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN);
- he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_UPLINK),
- IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
- }
- he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_LDPC_EXT_SYM),
- IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
- he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_PRE_FEC_PAD_MASK),
- IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
- he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_PE_DISAMBIG),
- IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
- he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data1,
- IWL_RX_PHY_DATA1_HE_LTF_NUM_MASK),
- IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS);
- he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_TXOP_DUR_MASK),
- IEEE80211_RADIOTAP_HE_DATA6_TXOP);
- he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_DOPPLER),
- IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
- break;
- }
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
+ u32 nsts;
- switch (phy_data->info_type) {
- case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
- case IWL_RX_PHY_INFO_TYPE_HE_MU:
- case IWL_RX_PHY_INFO_TYPE_HE_SU:
- he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN);
- he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_SPATIAL_REUSE_MASK),
- IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE);
- break;
- default:
- /* nothing here */
- break;
- }
+ switch (he_type) {
+ case RATE_MCS_HE_TYPE_TRIG:
+ iwl_mld_decode_he_tb_phy_data(phy_data, he, rx_status);
+ /* that's it, below is only for SU/MU */
+ return;
+ case RATE_MCS_HE_TYPE_MU:
+ iwl_mld_decode_he_mu(phy_data, he, he_mu, rx_status);
- switch (phy_data->info_type) {
- case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
- he_mu->flags1 |=
- le16_encode_bits(le16_get_bits(phy_data->data4,
- IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_DCM),
- IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM);
- he_mu->flags1 |=
- le16_encode_bits(le16_get_bits(phy_data->data4,
- IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_MCS_MASK),
- IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS);
- he_mu->flags2 |=
- le16_encode_bits(le16_get_bits(phy_data->data4,
- IWL_RX_PHY_DATA4_HE_MU_EXT_PREAMBLE_PUNC_TYPE_MASK),
- IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW);
- iwl_mld_decode_he_mu_ext(phy_data, he_mu);
- fallthrough;
- case IWL_RX_PHY_INFO_TYPE_HE_MU:
- he_mu->flags2 |=
- le16_encode_bits(le32_get_bits(phy_data->data1,
- IWL_RX_PHY_DATA1_HE_MU_SIBG_SYM_OR_USER_NUM_MASK),
- IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS);
- he_mu->flags2 |=
- le16_encode_bits(le32_get_bits(phy_data->data1,
- IWL_RX_PHY_DATA1_HE_MU_SIGB_COMPRESSION),
- IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP);
- fallthrough;
- case IWL_RX_PHY_INFO_TYPE_HE_TB:
- case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
- iwl_mld_decode_he_phy_ru_alloc(phy_data, he, he_mu, rx_status);
+ nsts = le32_get_bits(phy_data->ntfy->sigs.he.b,
+ OFDM_RX_FRAME_HE_SIGB_NSTS) + 1;
break;
- case IWL_RX_PHY_INFO_TYPE_HE_SU:
+ case RATE_MCS_HE_TYPE_SU:
+ case RATE_MCS_HE_TYPE_EXT_SU:
he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN);
- he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_HE_BEAM_CHNG),
- IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE);
- break;
- default:
- /* nothing */
+ he->data3 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a1,
+ OFDM_RX_FRAME_HE_BEAM_CHANGE,
+ IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE);
+
+ nsts = le32_get_bits(phy_data->ntfy->sigs.he.a1,
+ OFDM_RX_FRAME_HE_NSTS) + 1;
break;
}
+
+ rx_status->nss = nsts >> !!(rate_n_flags & RATE_MCS_STBC_MSK);
+
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN);
+ he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN);
+
+ he->data3 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a2,
+ OFDM_RX_FRAME_HE_CODING_EXTRA_SYM,
+ IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG);
+ he->data5 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a2,
+ OFDM_RX_FRAME_HE_PRE_FEC_PAD_FACTOR,
+ IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD);
+ he->data5 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a2,
+ OFDM_RX_FRAME_HE_PE_DISAMBIG,
+ IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG);
+ he->data5 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a2,
+ OFDM_RX_FRAME_HE_MU_NUM_OF_LTF_SYM,
+ IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS);
+ he->data6 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a2,
+ OFDM_RX_FRAME_HE_TXOP_DURATION,
+ IEEE80211_RADIOTAP_HE_DATA6_TXOP);
+ he->data6 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a2,
+ OFDM_RX_FRAME_HE_DOPPLER,
+ IEEE80211_RADIOTAP_HE_DATA6_DOPPLER);
+
+ he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN |
+ IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN);
+
+ he->data3 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a1,
+ OFDM_RX_FRAME_HE_BSS_COLOR,
+ IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR);
+ he->data3 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a1,
+ OFDM_RX_FRAME_HE_UL_FLAG,
+ IEEE80211_RADIOTAP_HE_DATA3_UL_DL);
+ he->data4 |= RTAP_ENC_HE(phy_data->ntfy->sigs.he.a1,
+ OFDM_RX_FRAME_HE_SPATIAL_REUSE,
+ IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE);
}
-static void iwl_mld_rx_he(struct iwl_mld *mld, struct sk_buff *skb,
- struct iwl_mld_rx_phy_data *phy_data,
- int queue)
+static void iwl_mld_rx_he(struct sk_buff *skb,
+ struct iwl_mld_rx_phy_data *phy_data)
{
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_he *he = NULL;
@@ -510,48 +529,28 @@ static void iwl_mld_rx_he(struct iwl_mld *mld, struct sk_buff *skb,
.flags2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN |
IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN),
};
- u16 phy_info = phy_data->phy_info;
he = skb_put_data(skb, &known, sizeof(known));
rx_status->flag |= RX_FLAG_RADIOTAP_HE;
- if (phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU ||
- phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU_EXT) {
- he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known));
- rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU;
- }
-
- /* report the AMPDU-EOF bit on single frames */
- if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
- rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
- if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF))
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
- }
-
- if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
- iwl_mld_decode_he_phy_data(phy_data, he, he_mu, rx_status,
- queue);
-
- /* update aggregation data for monitor sake on default queue */
- if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) &&
- (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) {
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
- if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
- }
-
- if (he_type == RATE_MCS_HE_TYPE_EXT_SU &&
- rate_n_flags & RATE_MCS_HE_106T_MSK) {
- rx_status->bw = RATE_INFO_BW_HE_RU;
- rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106;
- }
-
- /* actually data is filled in mac80211 */
- if (he_type == RATE_MCS_HE_TYPE_SU ||
- he_type == RATE_MCS_HE_TYPE_EXT_SU)
+ switch (he_type) {
+ case RATE_MCS_HE_TYPE_EXT_SU:
+ /*
+ * Except for this special case we won't have
+ * HE RU allocation info outside of monitor mode
+ * since we don't get the PHY notif.
+ */
+ if (rate_n_flags & RATE_MCS_HE_106T_MSK) {
+ rx_status->bw = RATE_INFO_BW_HE_RU;
+ rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106;
+ }
+ fallthrough;
+ case RATE_MCS_HE_TYPE_SU:
+ /* actual data is filled in mac80211 */
he->data1 |=
cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN);
+ break;
+ }
#define CHECK_TYPE(F) \
BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \
@@ -567,8 +566,7 @@ static void iwl_mld_rx_he(struct iwl_mld *mld, struct sk_buff *skb,
if (rate_n_flags & RATE_MCS_BF_MSK)
he->data5 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA5_TXBF);
- switch ((rate_n_flags & RATE_MCS_HE_GI_LTF_MSK) >>
- RATE_MCS_HE_GI_LTF_POS) {
+ switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) {
case 0:
if (he_type == RATE_MCS_HE_TYPE_TRIG)
rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6;
@@ -609,37 +607,52 @@ static void iwl_mld_rx_he(struct iwl_mld *mld, struct sk_buff *skb,
he->data5 |= le16_encode_bits(ltf,
IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE);
+
+ if (likely(!phy_data->ntfy))
+ return;
+
+ if (he_type == RATE_MCS_HE_TYPE_MU) {
+ he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known));
+ rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU;
+ }
+
+ iwl_mld_decode_he_phy_data(phy_data, he, he_mu, rx_status);
}
static void iwl_mld_decode_lsig(struct sk_buff *skb,
struct iwl_mld_rx_phy_data *phy_data)
{
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ u32 format = phy_data->rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
struct ieee80211_radiotap_lsig *lsig;
+ u32 lsig_len, rate;
- switch (phy_data->info_type) {
- case IWL_RX_PHY_INFO_TYPE_HT:
- case IWL_RX_PHY_INFO_TYPE_VHT_SU:
- case IWL_RX_PHY_INFO_TYPE_VHT_MU:
- case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT:
- case IWL_RX_PHY_INFO_TYPE_HE_SU:
- case IWL_RX_PHY_INFO_TYPE_HE_MU:
- case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT:
- case IWL_RX_PHY_INFO_TYPE_HE_TB:
- case IWL_RX_PHY_INFO_TYPE_EHT_MU:
- case IWL_RX_PHY_INFO_TYPE_EHT_TB:
- case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT:
- case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT:
- lsig = skb_put(skb, sizeof(*lsig));
- lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN);
- lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->data1,
- IWL_RX_PHY_DATA1_LSIG_LEN_MASK),
- IEEE80211_RADIOTAP_LSIG_DATA2_LENGTH);
- rx_status->flag |= RX_FLAG_RADIOTAP_LSIG;
- break;
- default:
- break;
- }
+ if (likely(!phy_data->ntfy))
+ return;
+
+ /*
+ * Technically legacy CCK/OFDM frames don't have an L-SIG
+ * since that's the compat format for HT (non-greenfield)
+ * and up. However, it's meant to be compatible with the
+ * LENGTH and RATE fields in Clause 17 and 18 OFDM frames
+ * so include the field for any non-CCK frame. For CCK it
+ * cannot work, since the LENGTH field for them is 16-bit
+ * and the radiotap field only has 12 bits.
+ */
+ if (format == RATE_MCS_MOD_TYPE_CCK)
+ return;
+
+ lsig_len = le32_get_bits(phy_data->ntfy->legacy_sig.ofdm,
+ OFDM_RX_LEGACY_LENGTH);
+ rate = le32_get_bits(phy_data->ntfy->legacy_sig.ofdm, OFDM_RX_RATE);
+
+ lsig = skb_put(skb, sizeof(*lsig));
+ lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN) |
+ cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_RATE_KNOWN);
+ lsig->data2 = le16_encode_bits(lsig_len,
+ IEEE80211_RADIOTAP_LSIG_DATA2_LENGTH) |
+ le16_encode_bits(rate, IEEE80211_RADIOTAP_LSIG_DATA2_RATE);
+ rx_status->flag |= RX_FLAG_RADIOTAP_LSIG;
}
/* Put a TLV on the skb and return data pointer
@@ -667,209 +680,144 @@ iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len)
(_usig)->value |= LE32_DEC_ENC(in_value, dec_bits, _enc_bits); \
} while (0)
-#define __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \
- eht->data[(rt_data)] |= \
- (cpu_to_le32 \
- (IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru ## _KNOWN) | \
- LE32_DEC_ENC(data ## fw_data, \
- IWL_RX_PHY_DATA ## fw_data ## _EHT_MU_EXT_RU_ALLOC_ ## fw_ru, \
- IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru))
+static void iwl_mld_decode_eht_usig_tb(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_eht_usig *usig)
+{
+ __le32 usig_a1 = phy_data->ntfy->sigs.eht_tb.usig_a1;
+ __le32 usig_a2 = phy_data->ntfy->sigs.eht_tb.usig_a2_eht;
+
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
+ OFDM_RX_FRAME_EHT_USIG1_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_PPDU_TYPE,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_USIG2_VALIDATE_B2,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_TRIG_SPATIAL_REUSE_1,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_TRIG_SPATIAL_REUSE_2,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_TRIG_USIG2_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD);
+}
-#define _IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \
- __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru)
+static void iwl_mld_decode_eht_usig_non_tb(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_eht_usig *usig)
+{
+ __le32 usig_a1 = phy_data->ntfy->sigs.eht.usig_a1;
+ __le32 usig_a2 = phy_data->ntfy->sigs.eht.usig_a2_eht;
+
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
+ OFDM_RX_FRAME_EHT_USIG1_DISREGARD,
+ IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
+ OFDM_RX_FRAME_EHT_USIG1_VALIDATE,
+ IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_PPDU_TYPE,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_USIG2_VALIDATE_B2,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_PUNC_CHANNEL,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_USIG2_VALIDATE_B8,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_SIG_MCS,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS);
+ IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
+ OFDM_RX_FRAME_EHT_SIG_SYM_NUM,
+ IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS);
+}
-#define IEEE80211_RADIOTAP_RU_DATA_1_1_1 1
-#define IEEE80211_RADIOTAP_RU_DATA_2_1_1 2
-#define IEEE80211_RADIOTAP_RU_DATA_1_1_2 2
-#define IEEE80211_RADIOTAP_RU_DATA_2_1_2 2
-#define IEEE80211_RADIOTAP_RU_DATA_1_2_1 3
-#define IEEE80211_RADIOTAP_RU_DATA_2_2_1 3
-#define IEEE80211_RADIOTAP_RU_DATA_1_2_2 3
-#define IEEE80211_RADIOTAP_RU_DATA_2_2_2 4
+static void iwl_mld_decode_eht_usig(struct iwl_mld_rx_phy_data *phy_data,
+ struct sk_buff *skb)
+{
+ u32 he_type = phy_data->rate_n_flags & RATE_MCS_HE_TYPE_MSK;
+ __le32 usig_a1 = phy_data->ntfy->sigs.eht.usig_a1;
+ __le32 usig_a2 = phy_data->ntfy->sigs.eht.usig_a2_eht;
+ struct ieee80211_radiotap_eht_usig *usig;
+ u32 bw;
-#define IWL_RX_RU_DATA_A1 2
-#define IWL_RX_RU_DATA_A2 2
-#define IWL_RX_RU_DATA_B1 2
-#define IWL_RX_RU_DATA_B2 4
-#define IWL_RX_RU_DATA_C1 3
-#define IWL_RX_RU_DATA_C2 3
-#define IWL_RX_RU_DATA_D1 4
-#define IWL_RX_RU_DATA_D2 4
+ usig = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG,
+ sizeof(*usig));
-#define IWL_MLD_ENC_EHT_RU(rt_ru, fw_ru) \
- _IWL_MLD_ENC_EHT_RU(IEEE80211_RADIOTAP_RU_DATA_ ## rt_ru, \
- rt_ru, \
- IWL_RX_RU_DATA_ ## fw_ru, \
- fw_ru)
+ BUILD_BUG_ON(offsetof(union iwl_sigs, eht.usig_a1) !=
+ offsetof(union iwl_sigs, eht_tb.usig_a1));
+ BUILD_BUG_ON(offsetof(union iwl_sigs, eht.usig_a2_eht) !=
+ offsetof(union iwl_sigs, eht_tb.usig_a2_eht));
-static void iwl_mld_decode_eht_ext_mu(struct iwl_mld *mld,
- struct iwl_mld_rx_phy_data *phy_data,
- struct ieee80211_rx_status *rx_status,
- struct ieee80211_radiotap_eht *eht,
- struct ieee80211_radiotap_eht_usig *usig)
-{
- if (phy_data->with_data) {
- __le32 data1 = phy_data->data1;
- __le32 data2 = phy_data->data2;
- __le32 data3 = phy_data->data3;
- __le32 data4 = phy_data->eht_data4;
- __le32 data5 = phy_data->data5;
- u32 phy_bw = phy_data->rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK;
-
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
- IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
- IWL_RX_PHY_DATA5_EHT_MU_PUNC_CH_CODE,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, data4,
- IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS);
- IWL_MLD_ENC_USIG_VALUE_MASK
- (usig, data1, IWL_RX_PHY_DATA1_EHT_MU_NUM_SIG_SYM_USIGA2,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS);
+ usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_CHECKED |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN |
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN);
- eht->user_info[0] |=
- cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) |
- LE32_DEC_ENC(data5, IWL_RX_PHY_DATA5_EHT_MU_STA_ID_USR,
- IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID);
+#define CHECK_BW(bw) \
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_ ## bw ## MHZ != \
+ RATE_MCS_CHAN_WIDTH_ ## bw ## _VAL)
+ CHECK_BW(20);
+ CHECK_BW(40);
+ CHECK_BW(80);
+ CHECK_BW(160);
+#undef CHECK_BW
+ BUILD_BUG_ON(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_320MHZ_1 !=
+ RATE_MCS_CHAN_WIDTH_320_VAL);
+ bw = u32_get_bits(phy_data->rate_n_flags, RATE_MCS_CHAN_WIDTH_MSK);
+ /* specific handling for 320MHz-1/320MHz-2 */
+ if (bw == RATE_MCS_CHAN_WIDTH_320_VAL)
+ bw += le32_get_bits(usig_a1, OFDM_RX_FRAME_EHT_BW320_SLOT);
+ usig->common |= le32_encode_bits(bw,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW);
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M);
- eht->data[7] |= LE32_DEC_ENC
- (data5, IWL_RX_PHY_DATA5_EHT_MU_NUM_USR_NON_OFDMA,
- IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS);
+ usig->common |= LE32_DEC_ENC(usig_a1, OFDM_RX_FRAME_ENHANCED_WIFI_UL_FLAG,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL);
+ usig->common |= LE32_DEC_ENC(usig_a1, OFDM_RX_FRAME_ENHANCED_WIFI_BSS_COLOR,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR);
- /*
- * Hardware labels the content channels/RU allocation values
- * as follows:
- * Content Channel 1 Content Channel 2
- * 20 MHz: A1
- * 40 MHz: A1 B1
- * 80 MHz: A1 C1 B1 D1
- * 160 MHz: A1 C1 A2 C2 B1 D1 B2 D2
- * 320 MHz: A1 C1 A2 C2 A3 C3 A4 C4 B1 D1 B2 D2 B3 D3 B4 D4
- *
- * However firmware can only give us A1-D2, so the higher
- * frequencies are missing.
- */
+ if (le32_get_bits(usig_a1, OFDM_RX_FRAME_EHT_USIG1_VALIDATE) &&
+ le32_get_bits(usig_a2, OFDM_RX_FRAME_EHT_USIG2_VALIDATE_B2) &&
+ le32_get_bits(usig_a2, OFDM_RX_FRAME_EHT_USIG2_VALIDATE_B8))
+ usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_OK);
- switch (phy_bw) {
- case RATE_MCS_CHAN_WIDTH_320:
- /* additional values are missing in RX metadata */
- fallthrough;
- case RATE_MCS_CHAN_WIDTH_160:
- /* content channel 1 */
- IWL_MLD_ENC_EHT_RU(1_2_1, A2);
- IWL_MLD_ENC_EHT_RU(1_2_2, C2);
- /* content channel 2 */
- IWL_MLD_ENC_EHT_RU(2_2_1, B2);
- IWL_MLD_ENC_EHT_RU(2_2_2, D2);
- fallthrough;
- case RATE_MCS_CHAN_WIDTH_80:
- /* content channel 1 */
- IWL_MLD_ENC_EHT_RU(1_1_2, C1);
- /* content channel 2 */
- IWL_MLD_ENC_EHT_RU(2_1_2, D1);
- fallthrough;
- case RATE_MCS_CHAN_WIDTH_40:
- /* content channel 2 */
- IWL_MLD_ENC_EHT_RU(2_1_1, B1);
- fallthrough;
- case RATE_MCS_CHAN_WIDTH_20:
- IWL_MLD_ENC_EHT_RU(1_1_1, A1);
- break;
- }
- } else {
- __le32 usig_a1 = phy_data->rx_vec[0];
- __le32 usig_a2 = phy_data->rx_vec[1];
-
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
- IWL_RX_USIG_A1_DISREGARD,
- IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
- IWL_RX_USIG_A1_VALIDATE,
- IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_PPDU_TYPE,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_PUNC_CHANNEL,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_SIG_MCS,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS);
- IWL_MLD_ENC_USIG_VALUE_MASK
- (usig, usig_a2, IWL_RX_USIG_A2_EHT_SIG_SYM_NUM,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_CRC_OK,
- IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC);
- }
-}
+ usig->common |= LE32_DEC_ENC(usig_a1,
+ OFDM_RX_FRAME_ENHANCED_WIFI_TXOP_DURATION,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP);
-static void iwl_mld_decode_eht_ext_tb(struct iwl_mld *mld,
- struct iwl_mld_rx_phy_data *phy_data,
- struct ieee80211_rx_status *rx_status,
- struct ieee80211_radiotap_eht *eht,
- struct ieee80211_radiotap_eht_usig *usig)
-{
- if (phy_data->with_data) {
- __le32 data5 = phy_data->data5;
-
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
- IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
- IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE1,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1);
-
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5,
- IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE2,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2);
- } else {
- __le32 usig_a1 = phy_data->rx_vec[0];
- __le32 usig_a2 = phy_data->rx_vec[1];
-
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1,
- IWL_RX_USIG_A1_DISREGARD,
- IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_PPDU_TYPE,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD);
- IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2,
- IWL_RX_USIG_A2_EHT_CRC_OK,
- IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC);
- }
+ if (!le32_get_bits(usig_a2, OFDM_RX_USIG_CRC_OK))
+ usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC);
+
+ usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN);
+ usig->common |= LE32_DEC_ENC(usig_a1,
+ OFDM_RX_FRAME_ENHANCED_WIFI_VER_ID,
+ IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER);
+
+ if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ iwl_mld_decode_eht_usig_tb(phy_data, usig);
+ else
+ iwl_mld_decode_eht_usig_non_tb(phy_data, usig);
}
-static void iwl_mld_decode_eht_ru(struct iwl_mld *mld,
- struct ieee80211_rx_status *rx_status,
- struct ieee80211_radiotap_eht *eht)
+static void
+iwl_mld_eht_set_ru_alloc(struct ieee80211_rx_status *rx_status,
+ u32 ru_with_p80)
{
- u32 ru = le32_get_bits(eht->data[8],
- IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1);
enum nl80211_eht_ru_alloc nl_ru;
+ u32 ru = ru_with_p80 >> 1;
- /* Using D1.5 Table 9-53a - Encoding of PS160 and RU Allocation subfields
- * in an EHT variant User Info field
+ /*
+ * HW always uses trigger frame format:
+ *
+ * Draft PIEEE802.11be D7.0 Table 9-46l - Encoding of the PS160 and
+ * RU Allocation subfields in an EHT variant User Info field
*/
switch (ru) {
@@ -929,135 +877,228 @@ static void iwl_mld_decode_eht_ru(struct iwl_mld *mld,
rx_status->eht.ru = nl_ru;
}
-static void iwl_mld_decode_eht_phy_data(struct iwl_mld *mld,
- struct iwl_mld_rx_phy_data *phy_data,
- struct ieee80211_rx_status *rx_status,
- struct ieee80211_radiotap_eht *eht,
- struct ieee80211_radiotap_eht_usig *usig)
-
+static void iwl_mld_decode_eht_tb(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_eht *eht)
{
- __le32 data0 = phy_data->data0;
- __le32 data1 = phy_data->data1;
- __le32 usig_a1 = phy_data->rx_vec[0];
- u8 info_type = phy_data->info_type;
-
- /* Not in EHT range */
- if (info_type < IWL_RX_PHY_INFO_TYPE_EHT_MU ||
- info_type > IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT)
+ if (!(phy_data->ntfy->flags & IWL_SNIF_FLAG_VALID_TB_RX))
return;
- usig->common |= cpu_to_le32
- (IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN |
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN);
- if (phy_data->with_data) {
- usig->common |= LE32_DEC_ENC(data0,
- IWL_RX_PHY_DATA0_EHT_UPLINK,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL);
- usig->common |= LE32_DEC_ENC(data0,
- IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR);
- } else {
- usig->common |= LE32_DEC_ENC(usig_a1,
- IWL_RX_USIG_A1_UL_FLAG,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL);
- usig->common |= LE32_DEC_ENC(usig_a1,
- IWL_RX_USIG_A1_BSS_COLOR,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR);
- }
-
- usig->common |=
- cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_CHECKED);
- usig->common |=
- LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_VALIDATE,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_OK);
-
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE);
- eht->data[0] |= LE32_DEC_ENC(data0,
- IWL_RX_PHY_DATA0_ETH_SPATIAL_REUSE_MASK,
- IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE);
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT |
+ IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80);
- /* All RU allocating size/index is in TB format */
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT);
- eht->data[8] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PS160,
+ eht->data[8] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht_tb.tb_rx0,
+ OFDM_UCODE_TRIG_BASE_PS160,
IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160);
- eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B0,
- IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0);
- eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B1_B7,
+ eht->data[8] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_RU,
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0 |
IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_CODING_EXTRA_SYM,
+ IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_PRE_FEC_PAD_FACTOR,
+ IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_PE_DISAMBIG,
+ IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_NUM_OF_LTF_SYM,
+ IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF);
+ eht->data[1] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht_tb.tb_rx0,
+ OFDM_UCODE_TRIG_BASE_RX_RU_P80,
+ IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80);
- iwl_mld_decode_eht_ru(mld, rx_status, eht);
-
- /* We only get here in case of IWL_RX_MPDU_PHY_TSF_OVERLOAD is set
- * which is on only in case of monitor mode so no need to check monitor
- * mode
- */
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80);
- eht->data[1] |=
- le32_encode_bits(mld->monitor.p80,
- IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80);
-
- usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN);
- if (phy_data->with_data)
- usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP);
- else
- usig->common |= LE32_DEC_ENC(usig_a1, IWL_RX_USIG_A1_TXOP_DURATION,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP);
+ iwl_mld_eht_set_ru_alloc(rx_status,
+ le32_get_bits(phy_data->ntfy->sigs.eht_tb.tb_rx1,
+ OFDM_UCODE_TRIG_BASE_RX_RU));
+}
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM);
- eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_LDPC_EXT_SYM,
- IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM);
+static void iwl_mld_eht_decode_user_ru(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_radiotap_eht *eht)
+{
+ u32 phy_bw = phy_data->rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK;
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM);
- eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PRE_FEC_PAD_MASK,
- IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM);
+ if (!(phy_data->ntfy->flags & IWL_SNIF_FLAG_VALID_RU))
+ return;
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM);
- eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PE_DISAMBIG,
- IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM);
+#define __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \
+ eht->data[(rt_data)] |= \
+ (cpu_to_le32(IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru ## _KNOWN) | \
+ LE32_DEC_ENC(phy_data->ntfy->sigs.eht.cmn[fw_data], \
+ OFDM_RX_FRAME_EHT_RU_ALLOC_ ## fw_data ## _ ## fw_ru, \
+ IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru))
- /* TODO: what about IWL_RX_PHY_DATA0_EHT_BW320_SLOT */
+#define _IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \
+ __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru)
- if (!le32_get_bits(data0, IWL_RX_PHY_DATA0_EHT_SIGA_CRC_OK))
- usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC);
+#define IEEE80211_RADIOTAP_RU_DATA_1_1_1 1
+#define IEEE80211_RADIOTAP_RU_DATA_2_1_1 2
+#define IEEE80211_RADIOTAP_RU_DATA_1_1_2 2
+#define IEEE80211_RADIOTAP_RU_DATA_2_1_2 2
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_1 3
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_1 3
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_2 3
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_2 4
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_3 4
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_3 4
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_4 5
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_4 5
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_5 5
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_5 6
+#define IEEE80211_RADIOTAP_RU_DATA_1_2_6 6
+#define IEEE80211_RADIOTAP_RU_DATA_2_2_6 6
+
+#define IWL_RX_RU_DATA_A1 0
+#define IWL_RX_RU_DATA_A2 0
+#define IWL_RX_RU_DATA_A3 0
+#define IWL_RX_RU_DATA_A4 4
+#define IWL_RX_RU_DATA_B1 1
+#define IWL_RX_RU_DATA_B2 1
+#define IWL_RX_RU_DATA_B3 1
+#define IWL_RX_RU_DATA_B4 4
+#define IWL_RX_RU_DATA_C1 2
+#define IWL_RX_RU_DATA_C2 2
+#define IWL_RX_RU_DATA_C3 2
+#define IWL_RX_RU_DATA_C4 5
+#define IWL_RX_RU_DATA_D1 3
+#define IWL_RX_RU_DATA_D2 3
+#define IWL_RX_RU_DATA_D3 3
+#define IWL_RX_RU_DATA_D4 5
- usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN);
- usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PHY_VER,
- IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER);
+#define IWL_MLD_ENC_EHT_RU(rt_ru, fw_ru) \
+ _IWL_MLD_ENC_EHT_RU(IEEE80211_RADIOTAP_RU_DATA_ ## rt_ru, \
+ rt_ru, \
+ IWL_RX_RU_DATA_ ## fw_ru, \
+ fw_ru)
/*
- * TODO: what about TB - IWL_RX_PHY_DATA1_EHT_TB_PILOT_TYPE,
- * IWL_RX_PHY_DATA1_EHT_TB_LOW_SS
+ * Hardware labels the content channels/RU allocation values
+ * as follows:
+ *
+ * Content Channel 1 Content Channel 2
+ * 20 MHz: A1
+ * 40 MHz: A1 B1
+ * 80 MHz: A1 C1 B1 D1
+ * 160 MHz: A1 C1 A2 C2 B1 D1 B2 D2
+ * 320 MHz: A1 C1 A2 C2 A3 C3 A4 C4 B1 D1 B2 D2 B3 D3 B4 D4
*/
- eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF);
- eht->data[0] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM,
+ switch (phy_bw) {
+ case RATE_MCS_CHAN_WIDTH_320:
+ /* content channel 1 */
+ IWL_MLD_ENC_EHT_RU(1_2_3, A3);
+ IWL_MLD_ENC_EHT_RU(1_2_4, C3);
+ IWL_MLD_ENC_EHT_RU(1_2_5, A4);
+ IWL_MLD_ENC_EHT_RU(1_2_6, C4);
+ /* content channel 2 */
+ IWL_MLD_ENC_EHT_RU(2_2_3, B3);
+ IWL_MLD_ENC_EHT_RU(2_2_4, D3);
+ IWL_MLD_ENC_EHT_RU(2_2_5, B4);
+ IWL_MLD_ENC_EHT_RU(2_2_6, D4);
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_160:
+ /* content channel 1 */
+ IWL_MLD_ENC_EHT_RU(1_2_1, A2);
+ IWL_MLD_ENC_EHT_RU(1_2_2, C2);
+ /* content channel 2 */
+ IWL_MLD_ENC_EHT_RU(2_2_1, B2);
+ IWL_MLD_ENC_EHT_RU(2_2_2, D2);
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_80:
+ /* content channel 1 */
+ IWL_MLD_ENC_EHT_RU(1_1_2, C1);
+ /* content channel 2 */
+ IWL_MLD_ENC_EHT_RU(2_1_2, D1);
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_40:
+ /* content channel 2 */
+ IWL_MLD_ENC_EHT_RU(2_1_1, B1);
+ fallthrough;
+ case RATE_MCS_CHAN_WIDTH_20:
+ /* content channel 1 */
+ IWL_MLD_ENC_EHT_RU(1_1_1, A1);
+ break;
+ }
+}
+
+static void iwl_mld_decode_eht_non_tb(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_eht *eht)
+{
+ eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE |
+ /* All RU allocating size/index is in TB format */
+ IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT |
+ IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM |
+ IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF |
+ IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80 |
+ IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M);
+
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b1,
+ OFDM_RX_FRAME_EHT_SPATIAL_REUSE,
+ IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE);
+ eht->data[8] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b2,
+ OFDM_RX_FRAME_EHT_STA_RU_PS160,
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160);
+ eht->data[8] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b2,
+ OFDM_RX_FRAME_EHT_STA_RU,
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0 |
+ IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b1,
+ OFDM_RX_FRAME_EHT_CODING_EXTRA_SYM,
+ IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b1,
+ OFDM_RX_FRAME_EHT_PRE_FEC_PAD_FACTOR,
+ IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b1,
+ OFDM_RX_FRAME_EHT_PE_DISAMBIG,
+ IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM);
+ eht->data[0] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b1,
+ OFDM_RX_FRAME_EHT_NUM_OF_LTF_SYM,
IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF);
+ eht->data[1] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b2,
+ OFDM_RX_FRAME_EHT_STA_RU_P80,
+ IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80);
+ eht->data[7] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b1,
+ OFDM_RX_FRAME_EHT_NUM_OF_USERS,
+ IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS);
+
+ iwl_mld_eht_decode_user_ru(phy_data, eht);
+
+ iwl_mld_eht_set_ru_alloc(rx_status,
+ le32_get_bits(phy_data->ntfy->sigs.eht.b2,
+ OFDM_RX_FRAME_EHT_STA_RU));
+}
- if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT ||
- info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB)
- iwl_mld_decode_eht_ext_tb(mld, phy_data, rx_status, eht, usig);
+static void iwl_mld_decode_eht_phy_data(struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status,
+ struct ieee80211_radiotap_eht *eht)
+{
+ u32 he_type = phy_data->rate_n_flags & RATE_MCS_HE_TYPE_MSK;
- if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT ||
- info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU)
- iwl_mld_decode_eht_ext_mu(mld, phy_data, rx_status, eht, usig);
+ if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ iwl_mld_decode_eht_tb(phy_data, rx_status, eht);
+ else
+ iwl_mld_decode_eht_non_tb(phy_data, rx_status, eht);
}
static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
- struct iwl_mld_rx_phy_data *phy_data,
- int queue)
+ struct iwl_mld_rx_phy_data *phy_data)
{
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_eht *eht;
- struct ieee80211_radiotap_eht_usig *usig;
size_t eht_len = sizeof(*eht);
-
u32 rate_n_flags = phy_data->rate_n_flags;
u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK;
/* EHT and HE have the same values for LTF */
u8 ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN;
- u16 phy_info = phy_data->phy_info;
- u32 bw;
/* u32 for 1 user_info */
if (phy_data->with_data)
@@ -1065,50 +1106,7 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len);
- usig = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG,
- sizeof(*usig));
rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
- usig->common |=
- cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN);
-
- /* specific handling for 320MHz */
- bw = u32_get_bits(rate_n_flags, RATE_MCS_CHAN_WIDTH_MSK);
- if (bw == RATE_MCS_CHAN_WIDTH_320_VAL)
- bw += le32_get_bits(phy_data->data0,
- IWL_RX_PHY_DATA0_EHT_BW320_SLOT);
-
- usig->common |= cpu_to_le32
- (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW, bw));
-
- /* report the AMPDU-EOF bit on single frames */
- if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
- rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
- if (phy_data->data0 &
- cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
- }
-
- /* update aggregation data for monitor sake on default queue */
- if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) &&
- (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) {
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
- if (phy_data->data0 &
- cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF))
- rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
- }
-
- if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
- iwl_mld_decode_eht_phy_data(mld, phy_data, rx_status, eht, usig);
-
-#define CHECK_TYPE(F) \
- BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \
- (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS))
-
- CHECK_TYPE(SU);
- CHECK_TYPE(EXT_SU);
- CHECK_TYPE(MU);
- CHECK_TYPE(TRIG);
switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) {
case 0:
@@ -1144,20 +1142,18 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
if (ltf != IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN) {
eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_GI);
- eht->data[0] |= cpu_to_le32
- (FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_LTF,
- ltf) |
- FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_GI,
- rx_status->eht.gi));
+ eht->data[0] |= le32_encode_bits(ltf,
+ IEEE80211_RADIOTAP_EHT_DATA0_LTF) |
+ le32_encode_bits(rx_status->eht.gi,
+ IEEE80211_RADIOTAP_EHT_DATA0_GI);
}
if (!phy_data->with_data) {
eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S |
IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S);
- eht->data[7] |=
- le32_encode_bits(le32_get_bits(phy_data->rx_vec[2],
- RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK),
- IEEE80211_RADIOTAP_EHT_DATA7_NSS_S);
+ eht->data[7] |= LE32_DEC_ENC(phy_data->ntfy->sigs.eht.b1,
+ OFDM_RX_FRAME_EHT_NSTS,
+ IEEE80211_RADIOTAP_EHT_DATA7_NSS_S);
if (rate_n_flags & RATE_MCS_BF_MSK)
eht->data[7] |=
cpu_to_le32(IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S);
@@ -1177,14 +1173,28 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
eht->user_info[0] |=
cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING);
- eht->user_info[0] |= cpu_to_le32
- (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS,
- u32_get_bits(rate_n_flags,
- RATE_VHT_MCS_RATE_CODE_MSK)) |
- FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O,
- u32_get_bits(rate_n_flags,
- RATE_MCS_NSS_MSK)));
+ eht->user_info[0] |=
+ le32_encode_bits(u32_get_bits(rate_n_flags,
+ RATE_VHT_MCS_RATE_CODE_MSK),
+ IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) |
+ le32_encode_bits(u32_get_bits(rate_n_flags,
+ RATE_MCS_NSS_MSK),
+ IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O);
}
+
+ if (likely(!phy_data->ntfy))
+ return;
+
+ if (phy_data->with_data) {
+ eht->user_info[0] |=
+ cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) |
+ LE32_DEC_ENC(phy_data->ntfy->sigs.eht.user_id,
+ OFDM_RX_FRAME_EHT_USER_FIELD_ID,
+ IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID);
+ }
+
+ iwl_mld_decode_eht_usig(phy_data, skb);
+ iwl_mld_decode_eht_phy_data(phy_data, rx_status, eht);
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -1207,8 +1217,9 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
radiotap->oui[0] = 0xf6;
radiotap->oui[1] = 0x54;
radiotap->oui[2] = 0x25;
- /* radiotap sniffer config sub-namespace */
+ /* Intel OUI default radiotap subtype */
radiotap->oui_subtype = 1;
+ /* Sniffer config element type */
radiotap->vendor_type = 0;
/* fill the data now */
@@ -1219,34 +1230,58 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
}
#endif
-/* Note: hdr can be NULL */
-static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,
- struct ieee80211_hdr *hdr,
- struct sk_buff *skb,
- struct iwl_mld_rx_phy_data *phy_data,
- int queue)
+static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
+ struct sk_buff *skb,
+ struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
{
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
- u32 format = phy_data->rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
- u32 rate_n_flags = phy_data->rate_n_flags;
- u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK);
- bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK;
+ struct ieee80211_radiotap_vendor_content *radiotap;
+ const u16 vendor_data_len = sizeof(*ntfy);
- phy_data->info_type = IWL_RX_PHY_INFO_TYPE_NONE;
+ radiotap =
+ iwl_mld_radiotap_put_tlv(skb,
+ IEEE80211_RADIOTAP_VENDOR_NAMESPACE,
+ sizeof(*radiotap) + vendor_data_len);
- if (phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD)
- phy_data->info_type =
- le32_get_bits(phy_data->data1,
- IWL_RX_PHY_DATA1_INFO_TYPE_MASK);
+ /* Intel OUI */
+ radiotap->oui[0] = 0xf6;
+ radiotap->oui[1] = 0x54;
+ radiotap->oui[2] = 0x25;
+ /* Intel OUI default radiotap subtype */
+ radiotap->oui_subtype = 1;
+ /* PHY data element type */
+ radiotap->vendor_type = cpu_to_le16(1);
- /* set the preamble flag if appropriate */
- if (format == RATE_MCS_MOD_TYPE_CCK &&
- phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
- rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
+ /* fill the data now */
+ memcpy(radiotap->data, ntfy, vendor_data_len);
- iwl_mld_fill_signal(mld, link_id, hdr, rx_status, phy_data);
+ rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
+}
+
+static void
+iwl_mld_set_rx_nonlegacy_rate_info(u32 rate_n_flags,
+ struct ieee80211_rx_status *rx_status)
+{
+ u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK);
+
+ /* NSS may be overridden by PHY ntfy with full value */
+ rx_status->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1;
+ rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK;
+ rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
+ if (rate_n_flags & RATE_MCS_LDPC_MSK)
+ rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
+}
+
+static void iwl_mld_set_rx_rate(struct iwl_mld *mld,
+ struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status)
+{
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK);
+ u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+ bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK;
- /* This may be overridden by iwl_mld_rx_he() to HE_RU */
+ /* bandwidth may be overridden to RU by PHY ntfy */
switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) {
case RATE_MCS_CHAN_WIDTH_20:
break;
@@ -1264,17 +1299,93 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,
break;
}
- /* must be before L-SIG data */
- if (format == RATE_MCS_MOD_TYPE_HE)
- iwl_mld_rx_he(mld, skb, phy_data, queue);
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_CCK:
+ if (phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
+ fallthrough;
+ case RATE_MCS_MOD_TYPE_LEGACY_OFDM: {
+ int rate =
+ iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags,
+ rx_status->band);
- iwl_mld_decode_lsig(skb, phy_data);
+ /* override BW - it could be DUP and indicate the wrong BW */
+ rx_status->bw = RATE_INFO_BW_20;
+
+ /* valid rate */
+ if (rate >= 0 && rate <= 0xFF) {
+ rx_status->rate_idx = rate;
+ break;
+ }
+
+ /* invalid rate */
+ rx_status->rate_idx = 0;
+
+ /*
+ * In monitor mode we can see CCK frames on 5 or 6 GHz, usually
+ * just the (possibly malformed) PHY header by accident, since
+ * the decoder doesn't seem to turn off CCK. We cannot correctly
+ * encode the rate to mac80211 (and therefore not in radiotap)
+ * since we give the per-band index which doesn't cover those
+ * rates.
+ */
+ if (!mld->monitor.on && net_ratelimit())
+ IWL_ERR(mld, "invalid rate_n_flags=0x%x, band=%d\n",
+ rate_n_flags, rx_status->band);
+ break;
+ }
+ case RATE_MCS_MOD_TYPE_HT:
+ rx_status->encoding = RX_ENC_HT;
+ rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags);
+ rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
+ break;
+ case RATE_MCS_MOD_TYPE_VHT:
+ rx_status->encoding = RX_ENC_VHT;
+ iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
+ break;
+ case RATE_MCS_MOD_TYPE_HE:
+ rx_status->encoding = RX_ENC_HE;
+ rx_status->he_dcm =
+ !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK);
+ iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
+ break;
+ case RATE_MCS_MOD_TYPE_EHT:
+ rx_status->encoding = RX_ENC_EHT;
+ iwl_mld_set_rx_nonlegacy_rate_info(rate_n_flags, rx_status);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ if (format != RATE_MCS_MOD_TYPE_CCK && is_sgi)
+ rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+}
+
+/* Note: hdr can be NULL */
+static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,
+ struct ieee80211_hdr *hdr,
+ struct sk_buff *skb,
+ struct iwl_mld_rx_phy_data *phy_data)
+{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ u32 rate_n_flags = phy_data->rate_n_flags;
+ u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
+
+ iwl_mld_fill_signal(mld, link_id, hdr, rx_status, phy_data);
rx_status->device_timestamp = phy_data->gp2_on_air_rise;
- /* using TLV format and must be after all fixed len fields */
+ iwl_mld_set_rx_rate(mld, phy_data, rx_status);
+
+ /* must be before L-SIG data (radiotap field order) */
+ if (format == RATE_MCS_MOD_TYPE_HE)
+ iwl_mld_rx_he(skb, phy_data);
+
+ iwl_mld_decode_lsig(skb, phy_data);
+
+ /* TLVs - must be after radiotap fixed fields */
if (format == RATE_MCS_MOD_TYPE_EHT)
- iwl_mld_rx_eht(mld, skb, phy_data, queue);
+ iwl_mld_rx_eht(mld, skb, phy_data);
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (unlikely(mld->monitor.on)) {
@@ -1282,9 +1393,9 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,
if (mld->monitor.ptp_time) {
u64 adj_time =
- iwl_mld_ptp_get_adj_time(mld,
- phy_data->gp2_on_air_rise *
- NSEC_PER_USEC);
+ iwl_mld_ptp_get_adj_time(mld,
+ phy_data->gp2_on_air_rise *
+ NSEC_PER_USEC);
rx_status->mactime = div64_u64(adj_time, NSEC_PER_USEC);
rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64;
@@ -1293,56 +1404,8 @@ static void iwl_mld_rx_fill_status(struct iwl_mld *mld, int link_id,
}
#endif
- if (format != RATE_MCS_MOD_TYPE_CCK && is_sgi)
- rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
-
- if (rate_n_flags & RATE_MCS_LDPC_MSK)
- rx_status->enc_flags |= RX_ENC_FLAG_LDPC;
-
- switch (format) {
- case RATE_MCS_MOD_TYPE_HT:
- rx_status->encoding = RX_ENC_HT;
- rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags);
- rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
- break;
- case RATE_MCS_MOD_TYPE_VHT:
- case RATE_MCS_MOD_TYPE_HE:
- case RATE_MCS_MOD_TYPE_EHT:
- if (format == RATE_MCS_MOD_TYPE_VHT) {
- rx_status->encoding = RX_ENC_VHT;
- } else if (format == RATE_MCS_MOD_TYPE_HE) {
- rx_status->encoding = RX_ENC_HE;
- rx_status->he_dcm =
- !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK);
- } else if (format == RATE_MCS_MOD_TYPE_EHT) {
- rx_status->encoding = RX_ENC_EHT;
- }
-
- rx_status->nss = u32_get_bits(rate_n_flags,
- RATE_MCS_NSS_MSK) + 1;
- rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK;
- rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT;
- break;
- default: {
- int rate =
- iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags,
- rx_status->band);
-
- /* valid rate */
- if (rate >= 0 && rate <= 0xFF) {
- rx_status->rate_idx = rate;
- break;
- }
-
- /* invalid rate */
- rx_status->rate_idx = 0;
-
- if (net_ratelimit())
- IWL_ERR(mld, "invalid rate_n_flags=0x%x, band=%d\n",
- rate_n_flags, rx_status->band);
- break;
- }
- }
+ if (phy_data->ntfy)
+ iwl_mld_add_rtap_sniffer_phy_data(mld, skb, phy_data->ntfy);
}
/* iwl_mld_create_skb adds the rxb to a new skb */
@@ -1763,13 +1826,36 @@ static int iwl_mld_rx_crypto(struct iwl_mld *mld,
return 0;
}
-static void iwl_mld_rx_update_ampdu_ref(struct iwl_mld *mld,
- struct iwl_mld_rx_phy_data *phy_data,
- struct ieee80211_rx_status *rx_status)
+static void iwl_mld_rx_update_ampdu_data(struct iwl_mld *mld,
+ struct iwl_mld_rx_phy_data *phy_data,
+ struct ieee80211_rx_status *rx_status)
{
+ u32 format = phy_data->rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
bool toggle_bit =
phy_data->phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE;
+ switch (format) {
+ case RATE_MCS_MOD_TYPE_CCK:
+ case RATE_MCS_MOD_TYPE_LEGACY_OFDM:
+ /* no aggregation possible */
+ return;
+ case RATE_MCS_MOD_TYPE_HT:
+ case RATE_MCS_MOD_TYPE_VHT:
+ /* single frames are not A-MPDU format */
+ if (!(phy_data->phy_info & IWL_RX_MPDU_PHY_AMPDU))
+ return;
+ break;
+ default:
+ /* HE/EHT/UHR have A-MPDU format for single frames */
+ if (!(phy_data->phy_info & IWL_RX_MPDU_PHY_AMPDU)) {
+ rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
+ if (phy_data->phy_info & IWL_RX_MPDU_PHY_EOF_INDICATION)
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
+ return;
+ }
+ }
+
rx_status->flag |= RX_FLAG_AMPDU_DETAILS;
/* Toggle is switched whenever new aggregation starts. Make
* sure ampdu_reference is never 0 so we can later use it to
@@ -1781,6 +1867,11 @@ static void iwl_mld_rx_update_ampdu_ref(struct iwl_mld *mld,
mld->monitor.ampdu_ref++;
mld->monitor.ampdu_toggle = toggle_bit;
phy_data->first_subframe = true;
+
+ /* report EOF bit on the first subframe */
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN;
+ if (phy_data->phy_info & IWL_RX_MPDU_PHY_EOF_INDICATION)
+ rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT;
}
rx_status->ampdu_reference = mld->monitor.ampdu_ref;
}
@@ -1810,6 +1901,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
u32 mpdu_len;
enum iwl_mld_reorder_result reorder_res;
struct ieee80211_rx_status *rx_status;
+ unsigned int alloc_size = 128;
if (unlikely(mld->fw_status.in_hw_restart))
return;
@@ -1824,10 +1916,17 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
"FW lied about packet len (%d)\n", pkt_len))
return;
+ iwl_mld_fill_phy_data_from_mpdu(mld, mpdu_desc, &phy_data);
+
/* Don't use dev_alloc_skb(), we'll have enough headroom once
* ieee80211_hdr pulled.
+ *
+ * For monitor mode we need more space to include the full PHY
+ * notification data.
*/
- skb = alloc_skb(128, GFP_ATOMIC);
+ if (unlikely(mld->monitor.on) && phy_data.ntfy)
+ alloc_size += sizeof(struct iwl_rx_phy_air_sniffer_ntfy);
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
if (!skb) {
IWL_ERR(mld, "alloc_skb failed\n");
return;
@@ -1835,8 +1934,6 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
hdr = (void *)(pkt->data + mpdu_desc_size);
- iwl_mld_fill_phy_data(mld, mpdu_desc, &phy_data);
-
if (mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) {
/* If the device inserted padding it means that (it thought)
* the 802.11 header wasn't a multiple of 4 bytes long. In
@@ -1861,9 +1958,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
if (drop)
goto drop;
- /* update aggregation data for monitor sake on default queue */
- if (!queue && (phy_data.phy_info & IWL_RX_MPDU_PHY_AMPDU))
- iwl_mld_rx_update_ampdu_ref(mld, &phy_data, rx_status);
+ if (unlikely(mld->monitor.on))
+ iwl_mld_rx_update_ampdu_data(mld, &phy_data, rx_status);
/* Keep packets with CRC errors (and with overrun) for monitor mode
* (otherwise the firmware discards them) but mark them as bad.
@@ -1897,7 +1993,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
link_id = u8_get_bits(mpdu_desc->mac_phy_band,
IWL_RX_MPDU_MAC_PHY_BAND_LINK_MASK);
- iwl_mld_rx_fill_status(mld, link_id, hdr, skb, &phy_data, queue);
+ iwl_mld_rx_fill_status(mld, link_id, hdr, skb, &phy_data);
if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue,
le32_to_cpu(pkt->len_n_flags), &crypto_len))
@@ -2031,87 +2127,65 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
wake_up(&mld->rxq_sync.waitq);
}
-void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi,
- struct iwl_rx_packet *pkt, int queue)
+static void iwl_mld_no_data_rx(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
{
- struct iwl_rx_no_data_ver_3 *desc;
- struct iwl_mld_rx_phy_data phy_data;
struct ieee80211_rx_status *rx_status;
+ struct iwl_mld_rx_phy_data phy_data = {
+ .ntfy = ntfy,
+ .phy_info = 0, /* short preamble set below */
+ .rate_n_flags = le32_to_cpu(ntfy->rate),
+ .gp2_on_air_rise = le32_to_cpu(ntfy->on_air_rise_time),
+ .energy_a = ntfy->rssi_a,
+ .energy_b = ntfy->rssi_b,
+ };
+ u32 format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
struct sk_buff *skb;
- u32 format, rssi;
- u8 channel;
-
- if (unlikely(mld->fw_status.in_hw_restart))
- return;
-
- if (IWL_FW_CHECK(mld, iwl_rx_packet_payload_len(pkt) < sizeof(*desc),
- "Bad RX_NO_DATA_NOTIF size (%d)\n",
- iwl_rx_packet_payload_len(pkt)))
- return;
-
- desc = (void *)pkt->data;
-
- rssi = le32_to_cpu(desc->rssi);
- channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK);
-
- phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK);
- phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK);
- phy_data.data0 = desc->phy_info[0];
- phy_data.data1 = desc->phy_info[1];
- phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD;
- phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time);
- phy_data.rate_n_flags = iwl_v3_rate_from_v2_v3(desc->rate,
- mld->fw_rates_ver_3);
- phy_data.with_data = false;
-
- BUILD_BUG_ON(sizeof(phy_data.rx_vec) != sizeof(desc->rx_vec));
- memcpy(phy_data.rx_vec, desc->rx_vec, sizeof(phy_data.rx_vec));
- format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK;
-
- /* Don't use dev_alloc_skb(), we'll have enough headroom once
- * ieee80211_hdr pulled.
- */
- skb = alloc_skb(128, GFP_ATOMIC);
- if (!skb) {
- IWL_ERR(mld, "alloc_skb failed\n");
+ skb = alloc_skb(128 + sizeof(struct iwl_rx_phy_air_sniffer_ntfy),
+ GFP_ATOMIC);
+ if (!skb)
return;
- }
rx_status = IEEE80211_SKB_RXCB(skb);
/* 0-length PSDU */
rx_status->flag |= RX_FLAG_NO_PSDU;
- /* mark as failed PLCP on any errors to skip checks in mac80211 */
- if (le32_get_bits(desc->info, RX_NO_DATA_INFO_ERR_MSK) !=
- RX_NO_DATA_INFO_ERR_NONE)
- rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC;
-
- switch (le32_get_bits(desc->info, RX_NO_DATA_INFO_TYPE_MSK)) {
- case RX_NO_DATA_INFO_TYPE_NDP:
+ switch (ntfy->status) {
+ case IWL_SNIF_STAT_PLCP_RX_OK:
+ /* we only get here with sounding PPDUs */
rx_status->zero_length_psdu_type =
IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING;
break;
- case RX_NO_DATA_INFO_TYPE_MU_UNMATCHED:
- case RX_NO_DATA_INFO_TYPE_TB_UNMATCHED:
+ case IWL_SNIF_STAT_AID_NOT_FOR_US:
rx_status->zero_length_psdu_type =
IEEE80211_RADIOTAP_ZERO_LEN_PSDU_NOT_CAPTURED;
break;
+ case IWL_SNIF_STAT_PLCP_RX_LSIG_ERR:
+ case IWL_SNIF_STAT_PLCP_RX_SIGA_ERR:
+ case IWL_SNIF_STAT_PLCP_RX_SIGB_ERR:
+ case IWL_SNIF_STAT_UNKNOWN_ERROR:
default:
+ rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+ fallthrough;
+ case IWL_SNIF_STAT_UNEXPECTED_TB:
+ case IWL_SNIF_STAT_UNSUPPORTED_RATE:
rx_status->zero_length_psdu_type =
IEEE80211_RADIOTAP_ZERO_LEN_PSDU_VENDOR;
- break;
+ /* we could include the real reason in a vendor TLV */
}
- rx_status->band = channel > 14 ? NL80211_BAND_5GHZ :
- NL80211_BAND_2GHZ;
+ if (format == RATE_MCS_MOD_TYPE_CCK &&
+ ntfy->legacy_sig.cck & cpu_to_le32(CCK_CRFR_SHORT_PREAMBLE))
+ phy_data.phy_info |= IWL_RX_MPDU_PHY_SHORT_PREAMBLE;
- rx_status->freq = ieee80211_channel_to_frequency(channel,
- rx_status->band);
+ iwl_mld_fill_rx_status_band_freq(IEEE80211_SKB_RXCB(skb),
+ ntfy->band, ntfy->channel);
/* link ID is ignored for NULL header */
- iwl_mld_rx_fill_status(mld, -1, NULL, skb, &phy_data, queue);
+ iwl_mld_rx_fill_status(mld, -1, NULL, skb, &phy_data);
/* No more radiotap info should be added after this point.
* Mark it as mac header for upper layers to know where
@@ -2119,29 +2193,72 @@ void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi,
*/
skb_set_mac_header(skb, skb->len);
- /* Override the nss from the rx_vec since the rate_n_flags has
- * only 1 bit for the nss which gives a max of 2 ss but there
- * may be up to 8 spatial streams.
- */
- switch (format) {
+ /* pass the packet to mac80211 */
+ rcu_read_lock();
+ ieee80211_rx_napi(mld->hw, NULL, skb, napi);
+ rcu_read_unlock();
+}
+
+void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt)
+{
+ struct iwl_rx_phy_air_sniffer_ntfy *ntfy = (void *)pkt->data;
+ bool is_ndp = false;
+ u32 he_type;
+
+ if (IWL_FW_CHECK(mld, iwl_rx_packet_payload_len(pkt) < sizeof(*ntfy),
+ "invalid air sniffer notification size\n"))
+ return;
+
+ /* check if there's an old one to release as errored */
+ if (mld->monitor.phy.valid && !mld->monitor.phy.used) {
+ /* didn't capture data, so override status */
+ mld->monitor.phy.data.status = IWL_SNIF_STAT_AID_NOT_FOR_US;
+ iwl_mld_no_data_rx(mld, napi, &mld->monitor.phy.data);
+ }
+
+ /* old data is no longer valid now */
+ mld->monitor.phy.valid = false;
+
+ he_type = le32_to_cpu(ntfy->rate) & RATE_MCS_HE_TYPE_MSK;
+
+ switch (le32_to_cpu(ntfy->rate) & RATE_MCS_MOD_TYPE_MSK) {
+ case RATE_MCS_MOD_TYPE_HT:
+ is_ndp = !le32_get_bits(ntfy->sigs.ht.a1,
+ OFDM_RX_FRAME_HT_LENGTH);
+ break;
case RATE_MCS_MOD_TYPE_VHT:
- rx_status->nss =
- le32_get_bits(desc->rx_vec[0],
- RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1;
+ is_ndp = le32_get_bits(ntfy->sigs.vht.a0,
+ OFDM_RX_FRAME_VHT_NUM_OF_DATA_SYM_VALID) &&
+ !le32_get_bits(ntfy->sigs.vht.a0,
+ OFDM_RX_FRAME_VHT_NUM_OF_DATA_SYM);
break;
case RATE_MCS_MOD_TYPE_HE:
- rx_status->nss =
- le32_get_bits(desc->rx_vec[0],
- RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1;
+ if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ break;
+ is_ndp = le32_get_bits(ntfy->sigs.he.a3,
+ OFDM_RX_FRAME_HE_NUM_OF_DATA_SYM_VALID) &&
+ !le32_get_bits(ntfy->sigs.he.a3,
+ OFDM_RX_FRAME_HE_NUM_OF_DATA_SYM);
break;
case RATE_MCS_MOD_TYPE_EHT:
- rx_status->nss =
- le32_get_bits(desc->rx_vec[2],
- RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1;
+ if (he_type == RATE_MCS_HE_TYPE_TRIG)
+ break;
+ is_ndp = le32_get_bits(ntfy->sigs.eht.sig2,
+ OFDM_RX_FRAME_EHT_NUM_OF_DATA_SYM_VALID) &&
+ !le32_get_bits(ntfy->sigs.eht.sig2,
+ OFDM_RX_FRAME_EHT_NUM_OF_DATA_SYM);
+ break;
}
- /* pass the packet to mac80211 */
- rcu_read_lock();
- ieee80211_rx_napi(mld->hw, NULL, skb, napi);
- rcu_read_unlock();
+ if (ntfy->status != IWL_SNIF_STAT_PLCP_RX_OK || is_ndp) {
+ iwl_mld_no_data_rx(mld, napi, ntfy);
+ return;
+ }
+
+ /* hang on to it for the RX_MPDU data packet(s) */
+ mld->monitor.phy.data = *ntfy;
+ mld->monitor.phy.valid = true;
+ mld->monitor.phy.used = false;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
index 2beabd7e70b1..09dddbd40f55 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
@@ -66,7 +66,8 @@ void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
struct sk_buff *skb, int queue,
struct ieee80211_sta *sta);
-void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi,
- struct iwl_rx_packet *pkt, int queue);
+void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld,
+ struct napi_struct *napi,
+ struct iwl_rx_packet *pkt);
#endif /* __iwl_mld_agg_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index 5cdbfa29a202..61ecc33116cf 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -890,7 +890,7 @@ static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue,
sizeof(queue_counter->per_link));
queue_counter->window_start_time = jiffies;
- IWL_DEBUG_INFO(mld, "MPDU counters are cleared\n");
+ IWL_DEBUG_EHT(mld, "MPDU counters are cleared\n");
}
link_counter = &queue_counter->per_link[mld_link->fw_id];
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
index 865f973f677d..edae13755ee6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c
@@ -115,7 +115,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
if (version >= 6) {
- struct iwl_alive_ntf_v6 *palive;
+ struct iwl_alive_ntf_v7 *palive;
if (pkt_len < sizeof(*palive))
return false;
@@ -214,17 +214,8 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
~FW_ADDR_CACHE_CONTROL;
if (umac_error_table) {
- if (umac_error_table >=
- mvm->trans->mac_cfg->base->min_umac_error_event_table) {
- iwl_fw_umac_set_alive_err_table(mvm->trans,
- umac_error_table);
- } else {
- IWL_ERR(mvm,
- "Not valid error log pointer 0x%08X for %s uCode\n",
- umac_error_table,
- (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) ?
- "Init" : "RT");
- }
+ iwl_fw_umac_set_alive_err_table(mvm->trans,
+ umac_error_table);
}
alive_data->valid = status == IWL_ALIVE_STATUS_OK;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
index 9c9e0e1c6e1d..867807abde66 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
@@ -938,19 +938,12 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm,
u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx)
{
+ u16 flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, BEACON_TEMPLATE_CMD, 0) > 10;
- u16 flags, cck_flag;
-
- if (is_new_rate) {
- flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
- cck_flag = IWL_MAC_BEACON_CCK;
- } else {
- cck_flag = IWL_MAC_BEACON_CCK_V1;
- flags = iwl_fw_rate_idx_to_plcp(rate_idx);
- }
if (rate_idx <= IWL_LAST_CCK_RATE)
- flags |= cck_flag;
+ flags |= is_new_rate ? IWL_MAC_BEACON_CCK
+ : IWL_MAC_BEACON_CCK_V1;
return flags;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
index b1dca76b7141..380b6f8a53fd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
@@ -102,9 +102,6 @@ static int iwl_mvm_mld_mac_add_interface(struct ieee80211_hw *hw,
mvm->csme_vif = vif;
}
- if (vif->p2p || iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1) < 5)
- vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW;
-
return 0;
out_free_bf:
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index b515028adc8f..301d590fe0bd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -2894,4 +2894,9 @@ iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm,
void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool update);
+
+/* rate_n_flags conversion */
+u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver);
+__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver);
+
#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
index 5e7e2926be0c..4f4111055ddd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2017 Intel Deutschland GmbH
*/
@@ -202,17 +202,13 @@ int iwl_mvm_phy_send_rlc(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt,
const struct cfg80211_chan_def *chandef,
- const struct cfg80211_chan_def *ap,
u8 chains_static, u8 chains_dynamic,
u32 action)
{
int ret;
int ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_CONTEXT_CMD, 1);
- if (ver < 5 || !ap || !ap->chan)
- ap = NULL;
-
- if (ver >= 3 && ver <= 6) {
+ if (ver >= 3 && ver <= 4) {
struct iwl_phy_context_cmd cmd = {};
/* Set the command header fields */
@@ -223,14 +219,6 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm,
chains_static,
chains_dynamic);
- if (ap) {
- cmd.sbb_bandwidth = iwl_mvm_get_channel_width(ap);
- cmd.sbb_ctrl_channel_loc = iwl_mvm_get_ctrl_pos(ap);
- }
-
- if (ver == 6)
- cmd.puncture_mask = cpu_to_le16(chandef->punctured);
-
ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD,
0, sizeof(cmd), &cmd);
} else if (ver < 3) {
@@ -284,7 +272,7 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
ctxt->width = chandef->width;
ctxt->center_freq1 = chandef->center_freq1;
- ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, ap,
+ ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
chains_static, chains_dynamic,
FW_CTXT_ACTION_ADD);
@@ -342,7 +330,7 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
int ret;
/* ... remove it here ...*/
- ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, NULL,
+ ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
chains_static, chains_dynamic,
FW_CTXT_ACTION_REMOVE);
if (ret)
@@ -356,7 +344,7 @@ int iwl_mvm_phy_ctxt_changed(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
ctxt->width = chandef->width;
ctxt->center_freq1 = chandef->center_freq1;
- return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, ap,
+ return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef,
chains_static, chains_dynamic,
action);
}
@@ -376,7 +364,7 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
cfg80211_chandef_create(&chandef, ctxt->channel, NL80211_CHAN_NO_HT);
- iwl_mvm_phy_ctxt_apply(mvm, ctxt, &chandef, NULL, 1, 1,
+ iwl_mvm_phy_ctxt_apply(mvm, ctxt, &chandef, 1, 1,
FW_CTXT_ACTION_REMOVE);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index 5802ed80a9ca..d1619a229d8f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -4178,167 +4178,3 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
else
return rs_drv_tx_protection(mvm, mvmsta, enable);
}
-
-static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags)
-{
- int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1;
- int idx;
- bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1);
- int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0;
- int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE;
-
- for (idx = offset; idx < last; idx++)
- if (iwl_fw_rate_idx_to_plcp(idx) == rate)
- return idx - offset;
- return IWL_RATE_INVALID;
-}
-
-u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver)
-{
- u32 rate_v3 = 0, rate_v1;
- u32 dup = 0;
-
- if (rate_ver > 1)
- return iwl_v3_rate_from_v2_v3(rate, rate_ver >= 3);
-
- rate_v1 = le32_to_cpu(rate);
- if (rate_v1 == 0)
- return rate_v1;
- /* convert rate */
- if (rate_v1 & RATE_MCS_HT_MSK_V1) {
- u32 nss;
-
- rate_v3 |= RATE_MCS_MOD_TYPE_HT;
- rate_v3 |=
- rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1;
- nss = u32_get_bits(rate_v1, RATE_HT_MCS_MIMO2_MSK);
- rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
- } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 ||
- rate_v1 & RATE_MCS_HE_MSK_V1) {
- u32 nss = u32_get_bits(rate_v1, RATE_VHT_MCS_NSS_MSK);
-
- rate_v3 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK;
-
- rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
-
- if (rate_v1 & RATE_MCS_HE_MSK_V1) {
- u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1;
- u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1;
- u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >>
- RATE_MCS_HE_106T_POS_V1;
- u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >>
- RATE_MCS_HE_GI_LTF_POS;
-
- if ((he_type_bits == RATE_MCS_HE_TYPE_SU ||
- he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) &&
- he_gi_ltf == RATE_MCS_HE_SU_4_LTF)
- /* the new rate have an additional bit to
- * represent the value 4 rather then using SGI
- * bit for this purpose - as it was done in the
- * old rate
- */
- he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >>
- RATE_MCS_SGI_POS_V1;
-
- rate_v3 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS;
- rate_v3 |= he_type << RATE_MCS_HE_TYPE_POS;
- rate_v3 |= he_106t << RATE_MCS_HE_106T_POS;
- rate_v3 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK;
- rate_v3 |= RATE_MCS_MOD_TYPE_HE;
- } else {
- rate_v3 |= RATE_MCS_MOD_TYPE_VHT;
- }
- /* if legacy format */
- } else {
- u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1);
-
- if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID))
- legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ?
- IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE;
-
- rate_v3 |= legacy_rate;
- if (!(rate_v1 & RATE_MCS_CCK_MSK_V1))
- rate_v3 |= RATE_MCS_MOD_TYPE_LEGACY_OFDM;
- }
-
- /* convert flags */
- if (rate_v1 & RATE_MCS_LDPC_MSK_V1)
- rate_v3 |= RATE_MCS_LDPC_MSK;
- rate_v3 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) |
- (rate_v1 & RATE_MCS_ANT_AB_MSK) |
- (rate_v1 & RATE_MCS_STBC_MSK) |
- (rate_v1 & RATE_MCS_BF_MSK);
-
- dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1;
- if (dup) {
- rate_v3 |= RATE_MCS_DUP_MSK;
- rate_v3 |= dup << RATE_MCS_CHAN_WIDTH_POS;
- }
-
- if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) &&
- (rate_v1 & RATE_MCS_SGI_MSK_V1))
- rate_v3 |= RATE_MCS_SGI_MSK;
-
- return rate_v3;
-}
-
-__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver)
-{
- u32 result = 0;
- int rate_idx;
-
- if (rate_ver > 1)
- return iwl_v3_rate_to_v2_v3(rate, rate_ver > 2);
-
- switch (rate & RATE_MCS_MOD_TYPE_MSK) {
- case RATE_MCS_MOD_TYPE_CCK:
- result = RATE_MCS_CCK_MSK_V1;
- fallthrough;
- case RATE_MCS_MOD_TYPE_LEGACY_OFDM:
- rate_idx = u32_get_bits(rate, RATE_LEGACY_RATE_MSK);
- if (!(result & RATE_MCS_CCK_MSK_V1))
- rate_idx += IWL_FIRST_OFDM_RATE;
- result |= u32_encode_bits(iwl_fw_rate_idx_to_plcp(rate_idx),
- RATE_LEGACY_RATE_MSK_V1);
- break;
- case RATE_MCS_MOD_TYPE_HT:
- result = RATE_MCS_HT_MSK_V1;
- result |= u32_encode_bits(u32_get_bits(rate,
- RATE_HT_MCS_CODE_MSK),
- RATE_HT_MCS_RATE_CODE_MSK_V1);
- result |= u32_encode_bits(u32_get_bits(rate,
- RATE_MCS_NSS_MSK),
- RATE_HT_MCS_MIMO2_MSK);
- break;
- case RATE_MCS_MOD_TYPE_VHT:
- result = RATE_MCS_VHT_MSK_V1;
- result |= u32_encode_bits(u32_get_bits(rate,
- RATE_VHT_MCS_NSS_MSK),
- RATE_MCS_CODE_MSK);
- result |= u32_encode_bits(u32_get_bits(rate, RATE_MCS_NSS_MSK),
- RATE_VHT_MCS_NSS_MSK);
- break;
- case RATE_MCS_MOD_TYPE_HE: /* not generated */
- default:
- WARN_ONCE(1, "bad modulation type %d\n",
- u32_get_bits(rate, RATE_MCS_MOD_TYPE_MSK));
- return 0;
- }
-
- if (rate & RATE_MCS_LDPC_MSK)
- result |= RATE_MCS_LDPC_MSK_V1;
- WARN_ON_ONCE(u32_get_bits(rate, RATE_MCS_CHAN_WIDTH_MSK) >
- RATE_MCS_CHAN_WIDTH_160_VAL);
- result |= (rate & RATE_MCS_CHAN_WIDTH_MSK_V1) |
- (rate & RATE_MCS_ANT_AB_MSK) |
- (rate & RATE_MCS_STBC_MSK) |
- (rate & RATE_MCS_BF_MSK);
-
- /* not handling DUP since we don't use it */
- WARN_ON_ONCE(rate & RATE_MCS_DUP_MSK);
-
- if (rate & RATE_MCS_SGI_MSK)
- result |= RATE_MCS_SGI_MSK_V1;
-
- return cpu_to_le32(result);
-}
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
index dfb062b7c5c2..34c957bef6f8 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h
@@ -425,9 +425,6 @@ void iwl_mvm_rate_control_unregister(void);
struct iwl_mvm_sta;
-u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver);
-__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver);
-
int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool enable);
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index 8c1bb3a7ffca..d0c0faae0122 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -519,6 +519,8 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi,
return;
}
rx_status->rate_idx = rate;
+ /* override BW - it could be DUP and indicate the wrong BW */
+ rx_status->bw = RATE_INFO_BW_20;
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
index 0c9c2492d8a7..0b12ee8ad618 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c
@@ -463,7 +463,7 @@ static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
if (!aux_roc_te) /* Not a Aux ROC time event */
return -EINVAL;
- iwl_mvm_te_check_trigger(mvm, notif, te_data);
+ iwl_mvm_te_check_trigger(mvm, notif, aux_roc_te);
IWL_DEBUG_TE(mvm,
"Aux ROC time event notification - UID = 0x%x action %d (error = %d)\n",
@@ -475,14 +475,14 @@ static int iwl_mvm_aux_roc_te_handle_notif(struct iwl_mvm *mvm,
/* End TE, notify mac80211 */
ieee80211_remain_on_channel_expired(mvm->hw);
iwl_mvm_roc_finished(mvm); /* flush aux queue */
- list_del(&te_data->list); /* remove from list */
- te_data->running = false;
- te_data->vif = NULL;
- te_data->uid = 0;
- te_data->id = TE_MAX;
+ list_del(&aux_roc_te->list); /* remove from list */
+ aux_roc_te->running = false;
+ aux_roc_te->vif = NULL;
+ aux_roc_te->uid = 0;
+ aux_roc_te->id = TE_MAX;
} else if (le32_to_cpu(notif->action) == TE_V2_NOTIF_HOST_EVENT_START) {
set_bit(IWL_MVM_STATUS_ROC_AUX_RUNNING, &mvm->status);
- te_data->running = true;
+ aux_roc_te->running = true;
ieee80211_ready_on_channel(mvm->hw); /* Start TE */
} else {
IWL_DEBUG_TE(mvm,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index 22602c32faa5..1a6c1f8706e1 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -159,9 +159,15 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx)
{
- return (rate_idx >= IWL_FIRST_OFDM_RATE ?
- rate_idx - IWL_FIRST_OFDM_RATE :
- rate_idx);
+ if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8)
+ /* In the new rate legacy rates are indexed:
+ * 0 - 3 for CCK and 0 - 7 for OFDM.
+ */
+ return (rate_idx >= IWL_FIRST_OFDM_RATE ?
+ rate_idx - IWL_FIRST_OFDM_RATE :
+ rate_idx);
+
+ return iwl_fw_rate_idx_to_plcp(rate_idx);
}
u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac)
@@ -1237,3 +1243,167 @@ bool iwl_mvm_vif_is_active(struct iwl_mvm_vif *mvmvif)
return false;
}
+
+static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags)
+{
+ int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1;
+ int idx;
+ bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1);
+ int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0;
+ int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE;
+
+ for (idx = offset; idx < last; idx++)
+ if (iwl_fw_rate_idx_to_plcp(idx) == rate)
+ return idx - offset;
+ return IWL_RATE_INVALID;
+}
+
+u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver)
+{
+ u32 rate_v3 = 0, rate_v1;
+ u32 dup = 0;
+
+ if (rate_ver > 1)
+ return iwl_v3_rate_from_v2_v3(rate, rate_ver >= 3);
+
+ rate_v1 = le32_to_cpu(rate);
+ if (rate_v1 == 0)
+ return rate_v1;
+ /* convert rate */
+ if (rate_v1 & RATE_MCS_HT_MSK_V1) {
+ u32 nss;
+
+ rate_v3 |= RATE_MCS_MOD_TYPE_HT;
+ rate_v3 |=
+ rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1;
+ nss = u32_get_bits(rate_v1, RATE_HT_MCS_MIMO2_MSK);
+ rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
+ } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 ||
+ rate_v1 & RATE_MCS_HE_MSK_V1) {
+ u32 nss = u32_get_bits(rate_v1, RATE_VHT_MCS_NSS_MSK);
+
+ rate_v3 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK;
+
+ rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK);
+
+ if (rate_v1 & RATE_MCS_HE_MSK_V1) {
+ u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1;
+ u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1;
+ u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >>
+ RATE_MCS_HE_106T_POS_V1;
+ u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >>
+ RATE_MCS_HE_GI_LTF_POS;
+
+ if ((he_type_bits == RATE_MCS_HE_TYPE_SU ||
+ he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) &&
+ he_gi_ltf == RATE_MCS_HE_SU_4_LTF)
+ /* the new rate have an additional bit to
+ * represent the value 4 rather then using SGI
+ * bit for this purpose - as it was done in the
+ * old rate
+ */
+ he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >>
+ RATE_MCS_SGI_POS_V1;
+
+ rate_v3 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS;
+ rate_v3 |= he_type << RATE_MCS_HE_TYPE_POS;
+ rate_v3 |= he_106t << RATE_MCS_HE_106T_POS;
+ rate_v3 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK;
+ rate_v3 |= RATE_MCS_MOD_TYPE_HE;
+ } else {
+ rate_v3 |= RATE_MCS_MOD_TYPE_VHT;
+ }
+ /* if legacy format */
+ } else {
+ u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1);
+
+ if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID))
+ legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ?
+ IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE;
+
+ rate_v3 |= legacy_rate;
+ if (!(rate_v1 & RATE_MCS_CCK_MSK_V1))
+ rate_v3 |= RATE_MCS_MOD_TYPE_LEGACY_OFDM;
+ }
+
+ /* convert flags */
+ if (rate_v1 & RATE_MCS_LDPC_MSK_V1)
+ rate_v3 |= RATE_MCS_LDPC_MSK;
+ rate_v3 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) |
+ (rate_v1 & RATE_MCS_ANT_AB_MSK) |
+ (rate_v1 & RATE_MCS_STBC_MSK) |
+ (rate_v1 & RATE_MCS_BF_MSK);
+
+ dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1;
+ if (dup) {
+ rate_v3 |= RATE_MCS_DUP_MSK;
+ rate_v3 |= dup << RATE_MCS_CHAN_WIDTH_POS;
+ }
+
+ if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) &&
+ (rate_v1 & RATE_MCS_SGI_MSK_V1))
+ rate_v3 |= RATE_MCS_SGI_MSK;
+
+ return rate_v3;
+}
+
+__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver)
+{
+ u32 result = 0;
+ int rate_idx;
+
+ if (rate_ver > 1)
+ return iwl_v3_rate_to_v2_v3(rate, rate_ver > 2);
+
+ switch (rate & RATE_MCS_MOD_TYPE_MSK) {
+ case RATE_MCS_MOD_TYPE_CCK:
+ result = RATE_MCS_CCK_MSK_V1;
+ fallthrough;
+ case RATE_MCS_MOD_TYPE_LEGACY_OFDM:
+ rate_idx = u32_get_bits(rate, RATE_LEGACY_RATE_MSK);
+ if (!(result & RATE_MCS_CCK_MSK_V1))
+ rate_idx += IWL_FIRST_OFDM_RATE;
+ result |= u32_encode_bits(iwl_fw_rate_idx_to_plcp(rate_idx),
+ RATE_LEGACY_RATE_MSK_V1);
+ break;
+ case RATE_MCS_MOD_TYPE_HT:
+ result = RATE_MCS_HT_MSK_V1;
+ result |= u32_encode_bits(u32_get_bits(rate,
+ RATE_HT_MCS_CODE_MSK),
+ RATE_HT_MCS_RATE_CODE_MSK_V1);
+ result |= u32_encode_bits(u32_get_bits(rate,
+ RATE_MCS_NSS_MSK),
+ RATE_HT_MCS_MIMO2_MSK);
+ break;
+ case RATE_MCS_MOD_TYPE_VHT:
+ result = RATE_MCS_VHT_MSK_V1;
+ result |= u32_encode_bits(u32_get_bits(rate,
+ RATE_VHT_MCS_NSS_MSK),
+ RATE_MCS_CODE_MSK);
+ result |= u32_encode_bits(u32_get_bits(rate, RATE_MCS_NSS_MSK),
+ RATE_VHT_MCS_NSS_MSK);
+ break;
+ case RATE_MCS_MOD_TYPE_HE: /* not generated */
+ default:
+ WARN_ONCE(1, "bad modulation type %d\n",
+ u32_get_bits(rate, RATE_MCS_MOD_TYPE_MSK));
+ return 0;
+ }
+
+ if (rate & RATE_MCS_LDPC_MSK)
+ result |= RATE_MCS_LDPC_MSK_V1;
+ WARN_ON_ONCE(u32_get_bits(rate, RATE_MCS_CHAN_WIDTH_MSK) >
+ RATE_MCS_CHAN_WIDTH_160_VAL);
+ result |= (rate & RATE_MCS_CHAN_WIDTH_MSK_V1) |
+ (rate & RATE_MCS_ANT_AB_MSK) |
+ (rate & RATE_MCS_STBC_MSK) |
+ (rate & RATE_MCS_BF_MSK);
+
+ /* not handling DUP since we don't use it */
+ WARN_ON_ONCE(rate & RATE_MCS_DUP_MSK);
+
+ if (rate & RATE_MCS_SGI_MSK)
+ result |= RATE_MCS_SGI_MSK_V1;
+
+ return cpu_to_le32(result);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index b21a4d8eb105..dc99e7ac4726 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -1061,12 +1061,18 @@ VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = {
/* WH RF */
IWL_DEV_INFO(iwl_rf_wh, iwl_be211_name, RF_TYPE(WH)),
+ IWL_DEV_INFO(iwl_rf_wh_non_eht, iwl_ax221_name, RF_TYPE(WH),
+ SUBDEV(0x0514)),
+ IWL_DEV_INFO(iwl_rf_wh_non_eht, iwl_ax221_name, RF_TYPE(WH),
+ SUBDEV(0x4514)),
IWL_DEV_INFO(iwl_rf_wh_160mhz, iwl_be213_name, RF_TYPE(WH), BW_LIMITED),
/* PE RF */
IWL_DEV_INFO(iwl_rf_pe, iwl_bn201_name, RF_TYPE(PE)),
- IWL_DEV_INFO(iwl_rf_pe, iwl_be223_name, RF_TYPE(PE), SUBDEV(0x0524)),
- IWL_DEV_INFO(iwl_rf_pe, iwl_be221_name, RF_TYPE(PE), SUBDEV(0x0324)),
+ IWL_DEV_INFO(iwl_rf_pe, iwl_be223_name, RF_TYPE(PE),
+ SUBDEV_MASKED(0x0524, 0xFFF)),
+ IWL_DEV_INFO(iwl_rf_pe, iwl_bn203_name, RF_TYPE(PE),
+ SUBDEV_MASKED(0x0324, 0xFFF)),
/* Killer */
IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775s_name, SUBDEV(0x1776)),
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
index 59307b5df441..164d060ec617 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c
@@ -4218,6 +4218,15 @@ int iwl_pci_gen1_2_probe(struct pci_dev *pdev,
pdev->device, pdev->subsystem_device,
info.hw_rev, info.hw_rf_id);
+#if !IS_ENABLED(CONFIG_IWLMLD)
+ if (iwl_drv_is_wifi7_supported(iwl_trans)) {
+ IWL_ERR(iwl_trans,
+ "IWLMLD needs to be compiled to support this device\n");
+ ret = -EOPNOTSUPP;
+ goto out_free_trans;
+ }
+#endif
+
dev_info = iwl_pci_find_dev_info(pdev->device, pdev->subsystem_device,
CSR_HW_RFID_TYPE(info.hw_rf_id),
CSR_HW_RFID_IS_CDB(info.hw_rf_id),
diff --git a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c
index c31bbd4e7a4a..6bf2ad18b009 100644
--- a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c
+++ b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c
@@ -265,6 +265,34 @@ static void devinfo_api_range(struct kunit *test)
}
}
+static void devinfo_pci_ids_config(struct kunit *test)
+{
+ for (int i = 0; iwl_hw_card_ids[i].vendor; i++) {
+ const struct pci_device_id *s = &iwl_hw_card_ids[i];
+ const struct iwl_dev_info *di;
+
+ if (s->device == PCI_ANY_ID || s->subdevice == PCI_ANY_ID)
+ continue;
+
+#if IS_ENABLED(CONFIG_IWLMVM) || IS_ENABLED(CONFIG_IWLMLD)
+ /*
+ * The check below only works for old (pre-CNVI) devices. Most
+ * new have subdevice==ANY, so are already skipped, but for some
+ * Bz platform(s) we list all the RF PCI IDs. Skip those too.
+ */
+ if (s->driver_data == (kernel_ulong_t)&iwl_bz_mac_cfg)
+ continue;
+#endif
+
+ di = iwl_pci_find_dev_info(s->device, s->subdevice,
+ 0, 0, 0, 0, true);
+
+ KUNIT_EXPECT_PTR_NE_MSG(test, di, NULL,
+ "PCI ID %04x:%04x not found\n",
+ s->device, s->subdevice);
+ }
+}
+
static struct kunit_case devinfo_test_cases[] = {
KUNIT_CASE(devinfo_table_order),
KUNIT_CASE(devinfo_discrete_match),
@@ -276,6 +304,7 @@ static struct kunit_case devinfo_test_cases[] = {
KUNIT_CASE(devinfo_pci_ids),
KUNIT_CASE(devinfo_no_mac_cfg_dups),
KUNIT_CASE(devinfo_api_range),
+ KUNIT_CASE(devinfo_pci_ids_config),
{}
};
diff --git a/drivers/net/wireless/marvell/mwl8k.c b/drivers/net/wireless/marvell/mwl8k.c
index 891e125ad30b..54d6d00ecdf1 100644
--- a/drivers/net/wireless/marvell/mwl8k.c
+++ b/drivers/net/wireless/marvell/mwl8k.c
@@ -2966,6 +2966,51 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask)
/*
* CMD_SET_BEACON.
*/
+
+static bool mwl8k_beacon_has_ds_params(const u8 *buf, int len)
+{
+ const struct ieee80211_mgmt *mgmt = (const void *)buf;
+ int ies_len;
+
+ if (len <= offsetof(struct ieee80211_mgmt, u.beacon.variable))
+ return false;
+
+ ies_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
+
+ return cfg80211_find_ie(WLAN_EID_DS_PARAMS, mgmt->u.beacon.variable,
+ ies_len) != NULL;
+}
+
+static void mwl8k_beacon_copy_inject_ds_params(struct ieee80211_hw *hw,
+ u8 *buf_dst, const u8 *buf_src,
+ int src_len)
+{
+ const struct ieee80211_mgmt *mgmt = (const void *)buf_src;
+ static const u8 before_ds_params[] = {
+ WLAN_EID_SSID,
+ WLAN_EID_SUPP_RATES,
+ };
+ const u8 *ies;
+ int hdr_len, left, offs, pos;
+
+ ies = mgmt->u.beacon.variable;
+ hdr_len = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+
+ offs = ieee80211_ie_split(ies, src_len - hdr_len, before_ds_params,
+ ARRAY_SIZE(before_ds_params), 0);
+
+ pos = hdr_len + offs;
+ left = src_len - pos;
+
+ memcpy(buf_dst, buf_src, pos);
+
+ /* Inject a DSSS Parameter Set after SSID + Supp Rates */
+ buf_dst[pos + 0] = WLAN_EID_DS_PARAMS;
+ buf_dst[pos + 1] = 1;
+ buf_dst[pos + 2] = hw->conf.chandef.chan->hw_value;
+
+ memcpy(buf_dst + pos + 3, buf_src + pos, left);
+}
struct mwl8k_cmd_set_beacon {
struct mwl8k_cmd_pkt_hdr header;
__le16 beacon_len;
@@ -2975,17 +3020,33 @@ struct mwl8k_cmd_set_beacon {
static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u8 *beacon, int len)
{
+ bool ds_params_present = mwl8k_beacon_has_ds_params(beacon, len);
struct mwl8k_cmd_set_beacon *cmd;
- int rc;
+ int rc, final_len = len;
- cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL);
+ if (!ds_params_present) {
+ /*
+ * mwl8k firmware requires a DS Params IE with the current
+ * channel in AP beacons. If mac80211/hostapd does not
+ * include it, inject one here. IE ID + length + channel
+ * number = 3 bytes.
+ */
+ final_len += 3;
+ }
+
+ cmd = kzalloc(sizeof(*cmd) + final_len, GFP_KERNEL);
if (cmd == NULL)
return -ENOMEM;
cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON);
- cmd->header.length = cpu_to_le16(sizeof(*cmd) + len);
- cmd->beacon_len = cpu_to_le16(len);
- memcpy(cmd->beacon, beacon, len);
+ cmd->header.length = cpu_to_le16(sizeof(*cmd) + final_len);
+ cmd->beacon_len = cpu_to_le16(final_len);
+
+ if (ds_params_present)
+ memcpy(cmd->beacon, beacon, len);
+ else
+ mwl8k_beacon_copy_inject_ds_params(hw, cmd->beacon, beacon,
+ len);
rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);
kfree(cmd);
diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig
index a86f800b8bf5..502303622a53 100644
--- a/drivers/net/wireless/mediatek/mt76/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT76_CORE
tristate
select PAGE_POOL
@@ -37,6 +37,10 @@ config MT792x_USB
tristate
select MT76_USB
+config MT76_NPU
+ bool
+ depends on MT76_CORE
+
source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig"
source "drivers/net/wireless/mediatek/mt76/mt7603/Kconfig"
diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile
index 87512d101a91..1d42adfe8030 100644
--- a/drivers/net/wireless/mediatek/mt76/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT76_CORE) += mt76.o
obj-$(CONFIG_MT76_USB) += mt76-usb.o
obj-$(CONFIG_MT76_SDIO) += mt76-sdio.o
@@ -12,6 +12,7 @@ mt76-y := \
mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \
tx.o agg-rx.o mcu.o wed.o scan.o channel.o
+mt76-$(CONFIG_MT76_NPU) += npu.o
mt76-$(CONFIG_PCI) += pci.o
mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o
diff --git a/drivers/net/wireless/mediatek/mt76/agg-rx.c b/drivers/net/wireless/mediatek/mt76/agg-rx.c
index 936ab1ca9246..3d34caf7e4f7 100644
--- a/drivers/net/wireless/mediatek/mt76/agg-rx.c
+++ b/drivers/net/wireless/mediatek/mt76/agg-rx.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c
index 130af1b254db..2b705bdb7993 100644
--- a/drivers/net/wireless/mediatek/mt76/channel.c
+++ b/drivers/net/wireless/mediatek/mt76/channel.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c
index b6a2746c187d..a5ac6ca86735 100644
--- a/drivers/net/wireless/mediatek/mt76/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -93,9 +93,9 @@ void mt76_seq_puts_array(struct seq_file *file, const char *str,
{
int i;
- seq_printf(file, "%10s:", str);
+ seq_printf(file, "%16s:", str);
for (i = 0; i < len; i++)
- seq_printf(file, " %2d", val[i]);
+ seq_printf(file, " %4d", val[i]);
seq_puts(file, "\n");
}
EXPORT_SYMBOL_GPL(mt76_seq_puts_array);
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index 1fa7de1d2c45..f240016ed9f0 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -7,37 +7,6 @@
#include "mt76.h"
#include "dma.h"
-#if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)
-
-#define Q_READ(_q, _field) ({ \
- u32 _offset = offsetof(struct mt76_queue_regs, _field); \
- u32 _val; \
- if ((_q)->flags & MT_QFLAG_WED) \
- _val = mtk_wed_device_reg_read((_q)->wed, \
- ((_q)->wed_regs + \
- _offset)); \
- else \
- _val = readl(&(_q)->regs->_field); \
- _val; \
-})
-
-#define Q_WRITE(_q, _field, _val) do { \
- u32 _offset = offsetof(struct mt76_queue_regs, _field); \
- if ((_q)->flags & MT_QFLAG_WED) \
- mtk_wed_device_reg_write((_q)->wed, \
- ((_q)->wed_regs + _offset), \
- _val); \
- else \
- writel(_val, &(_q)->regs->_field); \
-} while (0)
-
-#else
-
-#define Q_READ(_q, _field) readl(&(_q)->regs->_field)
-#define Q_WRITE(_q, _field, _val) writel(_val, &(_q)->regs->_field)
-
-#endif
-
static struct mt76_txwi_cache *
mt76_alloc_txwi(struct mt76_dev *dev)
{
@@ -220,10 +189,15 @@ static void
mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q)
{
Q_WRITE(q, desc_base, q->desc_dma);
- if (q->flags & MT_QFLAG_WED_RRO_EN)
+ if ((q->flags & MT_QFLAG_WED_RRO_EN) && !mt76_npu_device_active(dev))
Q_WRITE(q, ring_size, MT_DMA_RRO_EN | q->ndesc);
else
Q_WRITE(q, ring_size, q->ndesc);
+
+ if (mt76_queue_is_npu_tx(q)) {
+ writel(q->desc_dma, &q->regs->desc_base);
+ writel(q->ndesc, &q->regs->ring_size);
+ }
q->head = Q_READ(q, dma_idx);
q->tail = q->head;
}
@@ -235,7 +209,7 @@ void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q,
return;
if (!mt76_queue_is_wed_rro_ind(q) &&
- !mt76_queue_is_wed_rro_rxdmad_c(q)) {
+ !mt76_queue_is_wed_rro_rxdmad_c(q) && !mt76_queue_is_npu(q)) {
int i;
/* clear descriptors */
@@ -446,6 +420,7 @@ mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush)
while (q->queued > 0 && q->tail != last) {
mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry);
+ mt76_npu_txdesc_cleanup(q, q->tail);
mt76_queue_tx_complete(dev, q, &entry);
if (entry.txwi) {
@@ -680,6 +655,10 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
if (test_bit(MT76_RESET, &phy->state))
goto free_skb;
+ /* TODO: Take into account unlinear skbs */
+ if (mt76_npu_device_active(dev) && skb_linearize(skb))
+ goto free_skb;
+
t = mt76_get_txwi(dev);
if (!t)
goto free_skb;
@@ -727,6 +706,9 @@ mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q,
if (ret < 0)
goto unmap;
+ if (mt76_npu_device_active(dev))
+ return mt76_npu_dma_add_buf(phy, q, skb, &tx_info.buf[1], txwi);
+
return mt76_dma_add_buf(dev, q, tx_info.buf, tx_info.nbuf,
tx_info.info, tx_info.skb, t);
@@ -825,9 +807,17 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
q->ndesc = n_desc;
q->buf_size = bufsize;
q->hw_idx = idx;
+ q->dev = dev;
+
+ if (mt76_queue_is_wed_rro_ind(q))
+ size = sizeof(struct mt76_wed_rro_desc);
+ else if (mt76_queue_is_npu_tx(q))
+ size = sizeof(struct airoha_npu_tx_dma_desc);
+ else if (mt76_queue_is_npu_rx(q))
+ size = sizeof(struct airoha_npu_rx_dma_desc);
+ else
+ size = sizeof(struct mt76_desc);
- size = mt76_queue_is_wed_rro_ind(q) ? sizeof(struct mt76_wed_rro_desc)
- : sizeof(struct mt76_desc);
q->desc = dmam_alloc_coherent(dev->dma_dev, q->ndesc * size,
&q->desc_dma, GFP_KERNEL);
if (!q->desc)
@@ -843,6 +833,7 @@ mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q,
if (ret)
return ret;
+ mt76_npu_queue_setup(dev, q);
ret = mt76_wed_dma_setup(dev, q, false);
if (ret)
return ret;
@@ -870,6 +861,11 @@ mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
if (!q->ndesc)
return;
+ if (mt76_queue_is_npu(q)) {
+ mt76_npu_queue_cleanup(dev, q);
+ return;
+ }
+
do {
spin_lock_bh(&q->lock);
buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL);
@@ -900,7 +896,7 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
return;
if (!mt76_queue_is_wed_rro_ind(q) &&
- !mt76_queue_is_wed_rro_rxdmad_c(q)) {
+ !mt76_queue_is_wed_rro_rxdmad_c(q) && !mt76_queue_is_npu(q)) {
int i;
for (i = 0; i < q->ndesc; i++)
@@ -920,7 +916,10 @@ mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid)
return;
mt76_dma_sync_idx(dev, q);
- mt76_dma_rx_fill_buf(dev, q, false);
+ if (mt76_queue_is_npu(q))
+ mt76_npu_fill_rx_queue(dev, q);
+ else
+ mt76_dma_rx_fill(dev, q, false);
}
static void
diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h
index 17a80e1757fc..4a63de6c5bf5 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.h
+++ b/drivers/net/wireless/mediatek/mt76/dma.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -46,6 +46,73 @@
#define MT_FCE_INFO_LEN 4
#define MT_RX_RXWI_LEN 32
+#if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED)
+
+#define Q_READ(_q, _field) ({ \
+ u32 _offset = offsetof(struct mt76_queue_regs, _field); \
+ u32 _val; \
+ if ((_q)->flags & MT_QFLAG_WED) \
+ _val = mtk_wed_device_reg_read((_q)->wed, \
+ ((_q)->wed_regs + \
+ _offset)); \
+ else \
+ _val = readl(&(_q)->regs->_field); \
+ _val; \
+})
+
+#define Q_WRITE(_q, _field, _val) do { \
+ u32 _offset = offsetof(struct mt76_queue_regs, _field); \
+ if ((_q)->flags & MT_QFLAG_WED) \
+ mtk_wed_device_reg_write((_q)->wed, \
+ ((_q)->wed_regs + _offset), \
+ _val); \
+ else \
+ writel(_val, &(_q)->regs->_field); \
+} while (0)
+
+#elif IS_ENABLED(CONFIG_MT76_NPU)
+
+#define Q_READ(_q, _field) ({ \
+ u32 _offset = offsetof(struct mt76_queue_regs, _field); \
+ u32 _val = 0; \
+ if ((_q)->flags & MT_QFLAG_NPU) { \
+ struct airoha_npu *npu; \
+ \
+ rcu_read_lock(); \
+ npu = rcu_dereference(q->dev->mmio.npu); \
+ if (npu) \
+ regmap_read(npu->regmap, \
+ ((_q)->wed_regs + _offset), &_val); \
+ rcu_read_unlock(); \
+ } else { \
+ _val = readl(&(_q)->regs->_field); \
+ } \
+ _val; \
+})
+
+#define Q_WRITE(_q, _field, _val) do { \
+ u32 _offset = offsetof(struct mt76_queue_regs, _field); \
+ if ((_q)->flags & MT_QFLAG_NPU) { \
+ struct airoha_npu *npu; \
+ \
+ rcu_read_lock(); \
+ npu = rcu_dereference(q->dev->mmio.npu); \
+ if (npu) \
+ regmap_write(npu->regmap, \
+ ((_q)->wed_regs + _offset), _val); \
+ rcu_read_unlock(); \
+ } else { \
+ writel(_val, &(_q)->regs->_field); \
+ } \
+} while (0)
+
+#else
+
+#define Q_READ(_q, _field) readl(&(_q)->regs->_field)
+#define Q_WRITE(_q, _field, _val) writel(_val, &(_q)->regs->_field)
+
+#endif
+
struct mt76_desc {
__le32 buf0;
__le32 ctrl;
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index a987c5e4eff6..573400d57ce7 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -253,6 +253,19 @@ mt76_get_of_array(struct device_node *np, char *name, size_t *len, int min)
return prop->value;
}
+static const s8 *
+mt76_get_of_array_s8(struct device_node *np, char *name, size_t *len, int min)
+{
+ struct property *prop = of_find_property(np, name, NULL);
+
+ if (!prop || !prop->value || prop->length < min)
+ return NULL;
+
+ *len = prop->length;
+
+ return prop->value;
+}
+
struct device_node *
mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
{
@@ -294,7 +307,7 @@ mt76_get_txs_delta(struct device_node *np, u8 nss)
}
static void
-mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
+mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data,
s8 target_power, s8 nss_delta, s8 *max_power)
{
int i;
@@ -303,30 +316,29 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
return;
for (i = 0; i < pwr_len; i++) {
- pwr[i] = min_t(s8, target_power,
- be32_to_cpu(data[i]) + nss_delta);
+ pwr[i] = min_t(s8, target_power, data[i] + nss_delta);
*max_power = max(*max_power, pwr[i]);
}
}
static void
mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- const __be32 *data, size_t len, s8 target_power,
- s8 nss_delta, s8 *max_power)
+ const s8 *data, size_t len, s8 target_power,
+ s8 nss_delta)
{
int i, cur;
+ s8 max_power = -128;
if (!data)
return;
- len /= 4;
- cur = be32_to_cpu(data[0]);
+ cur = data[0];
for (i = 0; i < pwr_num; i++) {
if (len < pwr_len + 1)
break;
mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
- target_power, nss_delta, max_power);
+ target_power, nss_delta, &max_power);
if (--cur > 0)
continue;
@@ -335,7 +347,7 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
if (!len)
break;
- cur = be32_to_cpu(data[0]);
+ cur = data[0];
}
}
@@ -346,19 +358,23 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
{
struct mt76_dev *dev = phy->dev;
struct device_node *np;
- const __be32 *val;
+ const s8 *val;
char name[16];
u32 mcs_rates = dev->drv->mcs_rates;
u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
char band;
size_t len;
s8 max_power = 0;
+ s8 max_power_backoff = -127;
s8 txs_delta;
+ int n_chains = hweight16(phy->chainmask);
+ s8 target_power_combine = target_power + mt76_tx_power_path_delta(n_chains);
if (!mcs_rates)
mcs_rates = 10;
- memset(dest, target_power, sizeof(*dest));
+ memset(dest, target_power, sizeof(*dest) - sizeof(dest->path));
+ memset(&dest->path, 0, sizeof(dest->path));
if (!IS_ENABLED(CONFIG_OF))
return target_power;
@@ -392,24 +408,47 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
txs_delta = mt76_get_txs_delta(np, hweight16(phy->chainmask));
- val = mt76_get_of_array(np, "rates-cck", &len, ARRAY_SIZE(dest->cck));
+ val = mt76_get_of_array_s8(np, "rates-cck", &len, ARRAY_SIZE(dest->cck));
mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val,
target_power, txs_delta, &max_power);
- val = mt76_get_of_array(np, "rates-ofdm",
- &len, ARRAY_SIZE(dest->ofdm));
+ val = mt76_get_of_array_s8(np, "rates-ofdm",
+ &len, ARRAY_SIZE(dest->ofdm));
mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
target_power, txs_delta, &max_power);
- val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
+ val = mt76_get_of_array_s8(np, "rates-mcs", &len, mcs_rates + 1);
mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
ARRAY_SIZE(dest->mcs), val, len,
- target_power, txs_delta, &max_power);
+ target_power, txs_delta);
- val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
+ val = mt76_get_of_array_s8(np, "rates-ru", &len, ru_rates + 1);
mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
ARRAY_SIZE(dest->ru), val, len,
- target_power, txs_delta, &max_power);
+ target_power, txs_delta);
+
+ max_power_backoff = max_power;
+ val = mt76_get_of_array_s8(np, "paths-cck", &len, ARRAY_SIZE(dest->path.cck));
+ mt76_apply_array_limit(dest->path.cck, ARRAY_SIZE(dest->path.cck), val,
+ target_power_combine, txs_delta, &max_power_backoff);
+
+ val = mt76_get_of_array_s8(np, "paths-ofdm", &len, ARRAY_SIZE(dest->path.ofdm));
+ mt76_apply_array_limit(dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val,
+ target_power_combine, txs_delta, &max_power_backoff);
+
+ val = mt76_get_of_array_s8(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest->path.ofdm_bf));
+ mt76_apply_array_limit(dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val,
+ target_power_combine, txs_delta, &max_power_backoff);
+
+ val = mt76_get_of_array_s8(np, "paths-ru", &len, ARRAY_SIZE(dest->path.ru[0]) + 1);
+ mt76_apply_multi_array_limit(dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]),
+ ARRAY_SIZE(dest->path.ru), val, len,
+ target_power_combine, txs_delta);
+
+ val = mt76_get_of_array_s8(np, "paths-ru-bf", &len, ARRAY_SIZE(dest->path.ru_bf[0]) + 1);
+ mt76_apply_multi_array_limit(dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]),
+ ARRAY_SIZE(dest->path.ru_bf), val, len,
+ target_power_combine, txs_delta);
return max_power;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 5ceaf78c9ea0..75772979f438 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -630,6 +630,8 @@ int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q)
case MT_RXQ_MAIN:
case MT_RXQ_BAND1:
case MT_RXQ_BAND2:
+ case MT_RXQ_NPU0:
+ case MT_RXQ_NPU1:
pp_params.pool_size = 256;
break;
default:
@@ -814,6 +816,7 @@ void mt76_free_device(struct mt76_dev *dev)
destroy_workqueue(dev->wq);
dev->wq = NULL;
}
+ mt76_npu_deinit(dev);
ieee80211_free_hw(dev->hw);
}
EXPORT_SYMBOL_GPL(mt76_free_device);
@@ -847,8 +850,6 @@ void mt76_reset_device(struct mt76_dev *dev)
}
rcu_read_unlock();
- mt76_abort_scan(dev);
-
INIT_LIST_HEAD(&dev->wcid_list);
INIT_LIST_HEAD(&dev->sta_poll_list);
dev->vif_mask = 0;
@@ -1553,7 +1554,8 @@ void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q,
while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
mt76_check_sta(dev, skb);
- if (mtk_wed_device_active(&dev->mmio.wed))
+ if (mtk_wed_device_active(&dev->mmio.wed) ||
+ mt76_npu_device_active(dev))
__skb_queue_tail(&frames, skb);
else
mt76_rx_aggr_reorder(skb, &frames);
diff --git a/drivers/net/wireless/mediatek/mt76/mcu.c b/drivers/net/wireless/mediatek/mt76/mcu.c
index 65d4c2adb538..535c3d8a9cc0 100644
--- a/drivers/net/wireless/mediatek/mt76/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2019 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mmio.c b/drivers/net/wireless/mediatek/mt76/mmio.c
index cd2e9737c3bf..05d74cd7248e 100644
--- a/drivers/net/wireless/mediatek/mt76/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mmio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -33,13 +33,21 @@ static u32 mt76_mmio_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val)
static void mt76_mmio_write_copy(struct mt76_dev *dev, u32 offset,
const void *data, int len)
{
- __iowrite32_copy(dev->mmio.regs + offset, data, DIV_ROUND_UP(len, 4));
+ int i;
+
+ for (i = 0; i < ALIGN(len, 4); i += 4)
+ writel(get_unaligned_le32(data + i),
+ dev->mmio.regs + offset + i);
}
static void mt76_mmio_read_copy(struct mt76_dev *dev, u32 offset,
void *data, int len)
{
- __ioread32_copy(data, dev->mmio.regs + offset, DIV_ROUND_UP(len, 4));
+ int i;
+
+ for (i = 0; i < ALIGN(len, 4); i += 4)
+ put_unaligned_le32(readl(dev->mmio.regs + offset + i),
+ data + i);
}
static int mt76_mmio_wr_rp(struct mt76_dev *dev, u32 base,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index e0d50b58cd01..d05e83ea1cac 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -13,6 +13,7 @@
#include <linux/leds.h>
#include <linux/usb.h>
#include <linux/average.h>
+#include <linux/soc/airoha/airoha_offload.h>
#include <linux/soc/mediatek/mtk_wed.h>
#include <net/mac80211.h>
#include <net/page_pool/helpers.h>
@@ -34,6 +35,7 @@
#define MT_QFLAG_WED_RRO BIT(6)
#define MT_QFLAG_WED_RRO_EN BIT(7)
#define MT_QFLAG_EMI_EN BIT(8)
+#define MT_QFLAG_NPU BIT(9)
#define __MT_WED_Q(_type, _n) (MT_QFLAG_WED | \
FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \
@@ -48,6 +50,12 @@
#define MT_WED_RRO_Q_IND __MT_WED_RRO_Q(MT76_WED_RRO_Q_IND, 0)
#define MT_WED_RRO_Q_RXDMAD_C __MT_WED_RRO_Q(MT76_WED_RRO_Q_RXDMAD_C, 0)
+#define __MT_NPU_Q(_type, _n) (MT_QFLAG_NPU | \
+ FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \
+ FIELD_PREP(MT_QFLAG_WED_RING, _n))
+#define MT_NPU_Q_TX(_n) __MT_NPU_Q(MT76_WED_Q_TX, _n)
+#define MT_NPU_Q_RX(_n) __MT_NPU_Q(MT76_WED_Q_RX, _n)
+
struct mt76_dev;
struct mt76_phy;
struct mt76_wcid;
@@ -139,6 +147,8 @@ enum mt76_rxq_id {
MT_RXQ_TXFREE_BAND2,
MT_RXQ_RRO_IND,
MT_RXQ_RRO_RXDMAD_C,
+ MT_RXQ_NPU0,
+ MT_RXQ_NPU1,
__MT_RXQ_MAX
};
@@ -247,6 +257,7 @@ struct mt76_queue {
__le16 *emi_cpu_idx;
struct mtk_wed_device *wed;
+ struct mt76_dev *dev;
u32 wed_regs;
dma_addr_t desc_dma;
@@ -706,6 +717,11 @@ struct mt76_mmio {
struct mtk_wed_device wed_hif2;
struct completion wed_reset;
struct completion wed_reset_complete;
+
+ struct airoha_ppe_dev __rcu *ppe_dev;
+ struct airoha_npu __rcu *npu;
+ phys_addr_t phy_addr;
+ int npu_type;
};
struct mt76_rx_status {
@@ -943,6 +959,7 @@ struct mt76_dev {
struct idr token;
u16 wed_token_count;
u16 token_count;
+ u16 token_start;
u16 token_size;
spinlock_t rx_token_lock;
@@ -1113,6 +1130,14 @@ struct mt76_power_limits {
s8 mcs[4][10];
s8 ru[7][12];
s8 eht[16][16];
+
+ struct {
+ s8 cck[4];
+ s8 ofdm[4];
+ s8 ofdm_bf[4];
+ s8 ru[7][10];
+ s8 ru_bf[7][10];
+ } path;
};
struct mt76_ethtool_worker_info {
@@ -1252,6 +1277,15 @@ static inline int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q,
#define mt76_dereference(p, dev) \
rcu_dereference_protected(p, lockdep_is_held(&(dev)->mutex))
+static inline struct mt76_dev *mt76_wed_to_dev(struct mtk_wed_device *wed)
+{
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ if (wed->wlan.hif2)
+ return container_of(wed, struct mt76_dev, mmio.wed_hif2);
+#endif /* CONFIG_NET_MEDIATEK_SOC_WED */
+ return container_of(wed, struct mt76_dev, mmio.wed);
+}
+
static inline struct mt76_wcid *
__mt76_wcid_ptr(struct mt76_dev *dev, u16 idx)
{
@@ -1598,6 +1632,109 @@ int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb,
int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state);
int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len);
+#ifdef CONFIG_MT76_NPU
+void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb,
+ u32 info);
+int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q,
+ struct sk_buff *skb, struct mt76_queue_buf *buf,
+ void *txwi_ptr);
+int mt76_npu_rx_queue_init(struct mt76_dev *dev, struct mt76_queue *q);
+int mt76_npu_fill_rx_queue(struct mt76_dev *dev, struct mt76_queue *q);
+void mt76_npu_queue_cleanup(struct mt76_dev *dev, struct mt76_queue *q);
+void mt76_npu_disable_irqs(struct mt76_dev *dev);
+int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type);
+void mt76_npu_deinit(struct mt76_dev *dev);
+void mt76_npu_queue_setup(struct mt76_dev *dev, struct mt76_queue *q);
+void mt76_npu_txdesc_cleanup(struct mt76_queue *q, int index);
+int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct net_device *dev, enum tc_setup_type type,
+ void *type_data);
+#else
+static inline void mt76_npu_check_ppe(struct mt76_dev *dev,
+ struct sk_buff *skb, u32 info)
+{
+}
+
+static inline int mt76_npu_dma_add_buf(struct mt76_phy *phy,
+ struct mt76_queue *q,
+ struct sk_buff *skb,
+ struct mt76_queue_buf *buf,
+ void *txwi_ptr)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int mt76_npu_fill_rx_queue(struct mt76_dev *dev,
+ struct mt76_queue *q)
+{
+ return 0;
+}
+
+static inline void mt76_npu_queue_cleanup(struct mt76_dev *dev,
+ struct mt76_queue *q)
+{
+}
+
+static inline void mt76_npu_disable_irqs(struct mt76_dev *dev)
+{
+}
+
+static inline int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr,
+ int type)
+{
+ return 0;
+}
+
+static inline void mt76_npu_deinit(struct mt76_dev *dev)
+{
+}
+
+static inline void mt76_npu_queue_setup(struct mt76_dev *dev,
+ struct mt76_queue *q)
+{
+}
+
+static inline void mt76_npu_txdesc_cleanup(struct mt76_queue *q,
+ int index)
+{
+}
+
+static inline int mt76_npu_net_setup_tc(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct net_device *dev,
+ enum tc_setup_type type,
+ void *type_data)
+{
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_MT76_NPU */
+
+static inline bool mt76_npu_device_active(struct mt76_dev *dev)
+{
+ return !!rcu_access_pointer(dev->mmio.npu);
+}
+
+static inline bool mt76_ppe_device_active(struct mt76_dev *dev)
+{
+ return !!rcu_access_pointer(dev->mmio.ppe_dev);
+}
+
+static inline int mt76_npu_send_msg(struct airoha_npu *npu, int ifindex,
+ enum airoha_npu_wlan_set_cmd cmd,
+ u32 val, gfp_t gfp)
+{
+ return airoha_npu_wlan_send_msg(npu, ifindex, cmd, &val, sizeof(val),
+ gfp);
+}
+
+static inline int mt76_npu_get_msg(struct airoha_npu *npu, int ifindex,
+ enum airoha_npu_wlan_get_cmd cmd,
+ u32 *val, gfp_t gfp)
+{
+ return airoha_npu_wlan_get_msg(npu, ifindex, cmd, val, sizeof(*val),
+ gfp);
+}
+
static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable)
{
#ifdef CONFIG_NL80211_TESTMODE
@@ -1839,6 +1976,23 @@ static inline bool mt76_queue_is_emi(struct mt76_queue *q)
return q->flags & MT_QFLAG_EMI_EN;
}
+static inline bool mt76_queue_is_npu(struct mt76_queue *q)
+{
+ return q->flags & MT_QFLAG_NPU;
+}
+
+static inline bool mt76_queue_is_npu_tx(struct mt76_queue *q)
+{
+ return mt76_queue_is_npu(q) &&
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TX;
+}
+
+static inline bool mt76_queue_is_npu_rx(struct mt76_queue *q)
+{
+ return mt76_queue_is_npu(q) &&
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX;
+}
+
struct mt76_txwi_cache *
mt76_token_release(struct mt76_dev *dev, int token, bool *wake);
int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi);
@@ -1860,7 +2014,8 @@ mt76_get_page_pool_buf(struct mt76_queue *q, u32 *offset, u32 size)
{
struct page *page;
- page = page_pool_dev_alloc_frag(q->page_pool, offset, size);
+ page = page_pool_alloc_frag(q->page_pool, offset, size,
+ GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
if (!page)
return NULL;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig
index dd16acfd9735..ae40a596e49c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7603E
tristate "MediaTek MT7603E (PCIe) and MT76x8 WLAN support"
select MT76_CORE
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/Makefile b/drivers/net/wireless/mediatek/mt76/mt7603/Makefile
index 6878e305c24d..e954165ee133 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7603E) += mt7603e.o
mt7603e-y := \
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
index 6457ee06bb5a..300a7f9c2ef1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7603.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/core.c b/drivers/net/wireless/mediatek/mt76/mt7603/core.c
index 915b8349146a..9c2943fd904e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/core.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7603.h"
#include "../trace.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c
index 3967f2f05774..c891ad5498e6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7603.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
index e26cc78fff94..3a16851524a0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7603.h"
#include "mac.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c
index 88382b537a33..b89db2db6573 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include <linux/of.h>
#include "mt7603.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h
index 4687d6dc00dc..b6b746d1e56f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
#ifndef __MT7603_EEPROM_H
#define __MT7603_EEPROM_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/init.c b/drivers/net/wireless/mediatek/mt76/mt7603/init.c
index 86617a3e4328..10f2ec70c792 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include <linux/etherdevice.h>
#include "mt7603.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
index 6387f9e61060..d3110eeb45d7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include <linux/etherdevice.h>
#include <linux/timekeeping.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.h b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h
index 17e34ecf2bfb..9f5fab51ff83 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
#ifndef __MT7603_MAC_H
#define __MT7603_MAC_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c
index 0d7c84941cd0..0f3a7508996c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include <linux/etherdevice.h>
#include <linux/platform_device.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c
index 301668c3cc92..e432cce97640 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include <linux/firmware.h>
#include "mt7603.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h
index 30df8a3fd11a..7debe76cd092 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
#ifndef __MT7603_MCU_H
#define __MT7603_MCU_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
index 55a034ccbacd..071bfab3af7c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
#ifndef __MT7603_H
#define __MT7603_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/pci.c b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c
index 3d94cdb4314a..5fee610597a4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/regs.h b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h
index 524bceb8e958..97942f5ebdb4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
#ifndef __MT7603_REGS_H
#define __MT7603_REGS_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
index 1dd372372048..b74256efba55 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7615/Kconfig
index 1ab1439143f4..8cc0c1b5c24e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7615_COMMON
tristate
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/Makefile b/drivers/net/wireless/mediatek/mt76/mt7615/Makefile
index 2b97b9dde477..4def3b13eae1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7615_COMMON) += mt7615-common.o
obj-$(CONFIG_MT7615E) += mt7615e.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
index 2a6d317db5e0..0f7b20152279 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7615.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/dma.c b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c
index bcf7864312d7..59d2b3e8696b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c
index d4bc7e11e772..d6828e1cda19 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h
index a67fbb90f5b3..6aed52e14181 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2019 MediaTek Inc. */
#ifndef __MT7615_EEPROM_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
index 3e7af3e58736..42e11ba1206e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Roy Luo <royluo@google.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
index f8d2cc94b742..bd56cdb022a2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.h b/drivers/net/wireless/mediatek/mt76/mt7615/mac.h
index d08fbe64c262..336ebce5db5f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2019 MediaTek Inc. */
#ifndef __MT7615_MAC_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
index 15fe155ac3f3..727266892c3d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Roy Luo <royluo@google.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
index 4064e193d4de..fc0054f8bd60 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Roy Luo <royluo@google.com>
@@ -874,8 +874,10 @@ mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif,
wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid,
WTBL_RESET_AND_SET, NULL,
&wskb);
- if (IS_ERR(wtbl_hdr))
+ if (IS_ERR(wtbl_hdr)) {
+ dev_kfree_skb(sskb);
return PTR_ERR(wtbl_hdr);
+ }
if (enable) {
mt76_connac_mcu_wtbl_generic_tlv(&dev->mt76, wskb, vif, sta,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h
index 8e9604be0792..851b0e4839b5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2019 MediaTek Inc. */
#ifndef __MT7615_MCU_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
index dbb2c82407df..da7edd48ce2c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/kernel.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
index 9bdd29e8d25e..e16865dd8e52 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2019 MediaTek Inc. */
#ifndef __MT7615_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h
index 9be5a58a4e6d..697fc5d225de 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2019 Lorenzo Bianconi <lorenzo@kernel.org>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci.c
index 68010e27f065..f5018bfa317a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
index f607eee3fb47..a9178e077fd6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Roy Luo <royluo@google.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
index b795d11d943d..53cb1eed1e4f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
index 806b3887c541..eb3c24d51987 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2019 MediaTek Inc. */
#ifndef __MT7615_REGS_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c
index f56038cd4d3a..46188951ad19 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
* Author: Felix Fietkau <nbd@nbd.name>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/soc.c b/drivers/net/wireless/mediatek/mt76/mt7615/soc.c
index 06a0f2a141e8..4bd189dd67e3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/soc.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/soc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c
index 03f5af84424b..6eb97b7eba2d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
#include "mt7615.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/trace.c b/drivers/net/wireless/mediatek/mt76/mt7615/trace.c
index 6c02d5aff68f..7ec39e0b3fb2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/trace.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/trace.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2019 Lorenzo Bianconi <lorenzo@kernel.org>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
index d96e06b4fee1..d91feffadda9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2019 MediaTek Inc.
*
* Author: Felix Fietkau <nbd@nbd.name>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c
index 820b39590027..f4169de939c4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
* Author: Lorenzo Bianconi <lorenzo@kernel.org>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
index 192dcc374a64..813d61bffc2c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT76_CONNAC_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h
index eb4765365b8c..d4e2c3140441 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2022 MediaTek Inc. */
#ifndef __MT76_CONNAC2_MAC_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c
index 2d300948308d..651fcd4169f4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt76_connac.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
index c5eaedca11e0..247e2e7a47d8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT76_CONNAC3_MAC_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
index 0db00efe88b0..3304b5971be0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include "mt76_connac.h"
@@ -297,16 +297,18 @@ u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy,
struct ieee80211_bss_conf *conf,
bool beacon, bool mcast)
{
- struct mt76_vif_link *mvif = mt76_vif_conf_link(mphy->dev, conf->vif, conf);
- struct cfg80211_chan_def *chandef = mvif->ctx ?
- &mvif->ctx->def : &mphy->chandef;
- u8 nss = 0, mode = 0, band = chandef->chan->band;
- int rateidx = 0, mcast_rate;
- int offset = 0;
+ u8 nss = 0, mode = 0, band = NL80211_BAND_2GHZ;
+ int rateidx = 0, offset = 0, mcast_rate;
+ struct cfg80211_chan_def *chandef;
+ struct mt76_vif_link *mvif;
if (!conf)
goto legacy;
+ mvif = mt76_vif_conf_link(mphy->dev, conf->vif, conf);
+ chandef = mvif->ctx ? &mvif->ctx->def : &mphy->chandef;
+ band = chandef->chan->band;
+
if (is_mt7921(mphy->dev)) {
rateidx = ffs(conf->basic_rates) - 1;
goto legacy;
@@ -584,8 +586,9 @@ void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
bool multicast = ieee80211_is_data(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1);
- u16 rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon,
- multicast);
+ u16 rate = mt76_connac2_mac_tx_rate_val(mphy,
+ vif ? &vif->bss_conf : NULL,
+ beacon, multicast);
u32 val = MT_TXD6_FIXED_BW;
/* hardware won't add HTC for mgmt/ctrl frame */
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index fc3e6728fcfb..ea99167765b0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/firmware.h>
@@ -1974,7 +1974,7 @@ int mt76_connac_mcu_chip_config(struct mt76_dev *dev)
.resp_type = 0,
};
- memcpy(req.data, "assert", 7);
+ strscpy(req.data, "assert");
return mt76_mcu_send_msg(dev, MCU_CE_CMD(CHIP_CONFIG),
&req, sizeof(req), false);
@@ -3101,6 +3101,7 @@ int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name)
int i, ret, sem, max_len = mt76_is_sdio(dev) ? 2048 : 4096;
const struct mt76_connac2_patch_hdr *hdr;
const struct firmware *fw = NULL;
+ char build_date[17];
sem = mt76_connac_mcu_patch_sem_ctrl(dev, true);
switch (sem) {
@@ -3124,8 +3125,11 @@ int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name)
}
hdr = (const void *)fw->data;
+ strscpy(build_date, hdr->build_date, sizeof(build_date));
+ build_date[16] = '\0';
+ strim(build_date);
dev_info(dev->dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
- be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
+ be32_to_cpu(hdr->hw_sw_ver), build_date);
for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
struct mt76_connac2_patch_sec *sec;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 27daf419560a..8d59cf43f0e2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT76_CONNAC_MCU_H
@@ -1062,6 +1062,7 @@ enum {
MCU_UNI_EVENT_ROC = 0x27,
MCU_UNI_EVENT_TX_DONE = 0x2d,
MCU_UNI_EVENT_THERMAL = 0x35,
+ MCU_UNI_EVENT_RSSI_MONITOR = 0x41,
MCU_UNI_EVENT_NIC_CAPAB = 0x43,
MCU_UNI_EVENT_WED_RRO = 0x57,
MCU_UNI_EVENT_PER_STA_INFO = 0x6d,
@@ -1300,6 +1301,7 @@ enum {
MCU_UNI_CMD_THERMAL = 0x35,
MCU_UNI_CMD_VOW = 0x37,
MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
+ MCU_UNI_CMD_RSSI_MONITOR = 0x41,
MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
MCU_UNI_CMD_RRO = 0x57,
MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
index 11c16d1fc70f..f8d206a07f99 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c
index f0962507f72f..efa549dc68ec 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c
index 6dc1f51f5658..20a8f3659490 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h
index 8d06ef8c7c62..3c98808ccf26 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
index d570b99bccb9..7c9b16942275 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
index 8ce4bf44733d..d81f696b32c7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
index 7a07636d09c6..21c99ad7ef59 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h
index 491010a32247..d40051992586 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h b/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h
index 23b0e7d10d57..2f6ba8cf51e8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c
index a5e3392c0b48..d16be0cb0dc7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h
index 13fa70853b0d..3cbb2977f375 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
index 83488b2d6efb..14ee5b3b94d3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
index 5dc6c834111e..778454ac8974 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c
index 75978820a260..e16f06a284ca 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h
index e187ed52968e..a422cdc520cb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
index a683d53c7ceb..dd71c1c95cc9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
index cbe7e6f0c29a..557380c9bfab 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h
index 84d8a6138b3e..09e8edee2195 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h
index fe0c5e3298bc..e87d3f8a1de9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_trace.c b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.c
index a812c3a1e258..a92b2b7391ff 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_trace.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_trace.h b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.h
index 11d119cd0f6f..56eea2f68983 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_trace.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
index d8bc4ae185f5..301b43180006 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h b/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h
index b5be884b3549..49ab05c1fe73 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
index 4840d0b500b3..3a28a8cc1338 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
index b2cc44914294..968c73e06a5f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
index 7dfcb20c692c..e5d9d1bc9415 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig b/drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig
index 482a32b70ddf..d820510cb4bb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT76x2_COMMON
tristate
select MT76x02_LIB
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/Makefile b/drivers/net/wireless/mediatek/mt76/mt76x2/Makefile
index caf089538c11..cbc90a9616a6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0-only
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT76x2_COMMON) += mt76x2-common.o
obj-$(CONFIG_MT76x2E) += mt76x2e.o
obj-$(CONFIG_MT76x2U) += mt76x2u.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c
index 221805deb42f..782813aadc0a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h
index 43430ef98b11..1ee8be389b24 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
index 19c139290adb..408dc08b6457 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.c
index e08740ca3d0c..2fa34ca69095 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mac.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.h
index d5c3d26b94c1..f8ea70074c41 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c
index ac83ce5f3e8b..769d924220e3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h
index 41fd66563e82..16a4386aa754 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
index f051721bb00e..984756c81bdc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h
index f9d37c6cf1f0..27e478ab5b15 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c
index 2303019670e2..491a32921a06 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
index e38e8e5685c2..bec84f932311 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
index c5dfb06d81e8..550644676201 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
index e5b6282d1a6c..daba163802b6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c
index 8831337df23e..dcf4328c1cac 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
index e2b4cf30dc44..a5efa13a892f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
index 96cecc576a98..01cb3b2830f3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
index 3b5562811511..41778a8ef02c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c
index eaa622833f85..d0cb511ac6a2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
index 6671c53faf9f..66b06a493d95 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c
index dd22d8af0901..9102be1803b7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c
index a04a98f5ce1e..b63dd7f3ee80 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig
index 193112c49bd1..c24be8227f11 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7915E
tristate "MediaTek MT7915E (PCIe) support"
select MT76_CONNAC_LIB
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
index e0ca638c91a5..963fb3109af3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7915E) += mt7915e.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
index 5daf2258dfe6..6c7273e5b0bc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2022 MediaTek Inc. */
#include <linux/devcoredump.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h
index 709f8e9c795c..eb3991eda2e5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2022 MediaTek Inc. */
#ifndef _COREDUMP_H_
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
index b287b7d9394e..26ed3745af43 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/relay.h>
@@ -1008,7 +1008,7 @@ mt7915_rate_txpower_get(struct file *file, char __user *user_buf,
if (!buf)
return -ENOMEM;
- ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr));
+ ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr), TX_POWER_INFO_RATE);
if (ret)
goto out;
@@ -1118,7 +1118,7 @@ mt7915_rate_txpower_set(struct file *file, const char __user *user_buf,
mutex_lock(&dev->mt76.mutex);
ret = mt7915_mcu_get_txpower_sku(phy, req.txpower_sku,
- sizeof(req.txpower_sku));
+ sizeof(req.txpower_sku), TX_POWER_INFO_RATE);
if (ret)
goto out;
@@ -1160,7 +1160,7 @@ out:
return ret ? ret : count;
}
-static const struct file_operations mt7915_rate_txpower_fops = {
+static const struct file_operations mt7915_txpower_fops = {
.write = mt7915_rate_txpower_set,
.read = mt7915_rate_txpower_get,
.open = simple_open,
@@ -1169,6 +1169,70 @@ static const struct file_operations mt7915_rate_txpower_fops = {
};
static int
+mt7915_path_txpower_show(struct seq_file *file)
+{
+ struct mt7915_phy *phy = file->private;
+ s8 txpower[MT7915_SKU_PATH_NUM], *buf = txpower;
+ int ret;
+
+#define PATH_POWER_SHOW(_name, _len, _skip) do { \
+ size_t __len = (_len); \
+ if (_skip) { \
+ buf -= 1; \
+ *buf = 0; \
+ } \
+ mt76_seq_puts_array(file, _name, buf, __len); \
+ buf += __len; \
+ } while (0)
+
+ seq_printf(file, "\n%*c", 18, ' ');
+ seq_puts(file, "1T1S/2T1S/3T1S/4T1S/2T2S/3T2S/4T2S/3T3S/4T3S/4T4S\n");
+ ret = mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower),
+ TX_POWER_INFO_PATH);
+ if (ret)
+ return ret;
+
+ PATH_POWER_SHOW("CCK", 4, 0);
+ PATH_POWER_SHOW("OFDM", 4, 0);
+ PATH_POWER_SHOW("BF-OFDM", 4, 1);
+
+ PATH_POWER_SHOW("HT/VHT20", 10, 0);
+ PATH_POWER_SHOW("BF-HT/VHT20", 10, 1);
+ PATH_POWER_SHOW("HT/VHT40", 10, 0);
+ PATH_POWER_SHOW("BF-HT/VHT40", 10, 1);
+
+ PATH_POWER_SHOW("BW20/RU242", 10, 0);
+ PATH_POWER_SHOW("BF-BW20/RU242", 10, 1);
+ PATH_POWER_SHOW("BW40/RU484", 10, 0);
+ PATH_POWER_SHOW("BF-BW40/RU484", 10, 1);
+ PATH_POWER_SHOW("BW80/RU996", 10, 0);
+ PATH_POWER_SHOW("BF-BW80/RU996", 10, 1);
+ PATH_POWER_SHOW("BW160/RU2x996", 10, 0);
+ PATH_POWER_SHOW("BF-BW160/RU2x996", 10, 1);
+ PATH_POWER_SHOW("RU26", 10, 0);
+ PATH_POWER_SHOW("BF-RU26", 10, 0);
+ PATH_POWER_SHOW("RU52", 10, 0);
+ PATH_POWER_SHOW("BF-RU52", 10, 0);
+ PATH_POWER_SHOW("RU106", 10, 0);
+ PATH_POWER_SHOW("BF-RU106", 10, 0);
+#undef PATH_POWER_SHOW
+
+ return 0;
+}
+
+static int
+mt7915_txpower_path_show(struct seq_file *file, void *data)
+{
+ struct mt7915_phy *phy = file->private;
+
+ seq_printf(file, "\nBand %d\n", phy != &phy->dev->phy);
+
+ return mt7915_path_txpower_show(file);
+}
+
+DEFINE_SHOW_ATTRIBUTE(mt7915_txpower_path);
+
+static int
mt7915_twt_stats(struct seq_file *s, void *data)
{
struct mt7915_dev *dev = dev_get_drvdata(s->private);
@@ -1254,7 +1318,9 @@ int mt7915_init_debugfs(struct mt7915_phy *phy)
debugfs_create_file("implicit_txbf", 0600, dir, dev,
&fops_implicit_txbf);
debugfs_create_file("txpower_sku", 0400, dir, phy,
- &mt7915_rate_txpower_fops);
+ &mt7915_txpower_fops);
+ debugfs_create_file("txpower_path", 0400, dir, phy,
+ &mt7915_txpower_path_fops);
debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir,
mt7915_twt_stats);
debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
index 009ef713f437..aabd37366e86 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include "mt7915.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
index 38dfd5de365c..eb92cbf1a284 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/firmware.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
index 73611c9d26e1..1dc285c72991 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7915_EEPROM_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
index 5ea8b46e092e..22443cbc74ad 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/etherdevice.h>
@@ -289,6 +289,8 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band);
struct mt76_power_limits limits;
+ phy->sku_limit_en = true;
+ phy->sku_path_en = true;
for (i = 0; i < sband->n_channels; i++) {
struct ieee80211_channel *chan = &sband->channels[i];
u32 target_power = 0;
@@ -305,6 +307,11 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy,
target_power = mt76_get_rate_power_limits(phy->mt76, chan,
&limits,
target_power);
+
+ /* MT7915N can not enable Backoff table without setting value in dts */
+ if (!limits.path.ofdm[0])
+ phy->sku_path_en = false;
+
target_power += path_delta;
target_power = DIV_ROUND_UP(target_power, 2);
chan->max_power = min_t(int, chan->max_reg_power,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
index 1c0d310146d6..cefe56c05731 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/etherdevice.h>
@@ -1451,6 +1451,8 @@ mt7915_mac_full_reset(struct mt7915_dev *dev)
if (ext_phy)
cancel_delayed_work_sync(&ext_phy->mac_work);
+ mt76_abort_scan(&dev->mt76);
+
mutex_lock(&dev->mt76.mutex);
for (i = 0; i < 10; i++) {
if (!mt7915_mac_restart(dev))
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
index 448b1b380190..e39f96e00ba4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7915_MAC_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index fe0639c14bf9..90d5e79fbf74 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/etherdevice.h>
@@ -73,7 +73,7 @@ int mt7915_run(struct ieee80211_hw *hw)
if (ret)
goto out;
- ret = mt7915_mcu_set_sku_en(phy, true);
+ ret = mt7915_mcu_set_sku_en(phy);
if (ret)
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index c1fdd3c4f1ba..00bff4d3aab8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/fs.h>
@@ -3336,7 +3336,8 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
int ret;
s8 txpower_sku[MT7915_SKU_RATE_NUM];
- ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku));
+ ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku),
+ TX_POWER_INFO_RATE);
if (ret)
return ret;
@@ -3376,51 +3377,136 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
sizeof(req), true);
}
+static void
+mt7915_update_txpower(struct mt7915_phy *phy, int tx_power)
+{
+ struct mt76_phy *mphy = phy->mt76;
+ struct ieee80211_channel *chan = mphy->main_chandef.chan;
+ int chain_idx, val, e2p_power_limit = 0;
+
+ if (!chan) {
+ mphy->txpower_cur = tx_power;
+ return;
+ }
+
+ for (chain_idx = 0; chain_idx < hweight16(mphy->chainmask); chain_idx++) {
+ val = mt7915_eeprom_get_target_power(phy->dev, chan, chain_idx);
+ val += mt7915_eeprom_get_power_delta(phy->dev, chan->band);
+
+ e2p_power_limit = max_t(int, e2p_power_limit, val);
+ }
+
+ if (phy->sku_limit_en)
+ mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power);
+ else
+ mphy->txpower_cur = e2p_power_limit;
+}
+
int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy)
{
+#define TX_POWER_LIMIT_TABLE_RATE 0
+#define TX_POWER_LIMIT_TABLE_PATH 1
struct mt7915_dev *dev = phy->dev;
struct mt76_phy *mphy = phy->mt76;
struct ieee80211_hw *hw = mphy->hw;
- struct mt7915_mcu_txpower_sku req = {
+ struct mt7915_sku_val {
+ u8 format_id;
+ u8 limit_type;
+ u8 band_idx;
+ } __packed hdr = {
.format_id = TX_POWER_LIMIT_TABLE,
+ .limit_type = TX_POWER_LIMIT_TABLE_RATE,
.band_idx = phy->mt76->band_idx,
};
- struct mt76_power_limits limits_array;
- s8 *la = (s8 *)&limits_array;
- int i, idx;
- int tx_power;
+ int i, ret, tx_power;
+ const u8 *len = mt7915_sku_group_len;
+ struct mt76_power_limits la = {};
+ struct sk_buff *skb;
tx_power = mt76_get_power_bound(mphy, hw->conf.power_level);
- tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
- &limits_array, tx_power);
- mphy->txpower_cur = tx_power;
+ if (phy->sku_limit_en) {
+ tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan,
+ &la, tx_power);
+ mt7915_update_txpower(phy, tx_power);
+ } else {
+ mt7915_update_txpower(phy, tx_power);
+ return 0;
+ }
- for (i = 0, idx = 0; i < ARRAY_SIZE(mt7915_sku_group_len); i++) {
- u8 mcs_num, len = mt7915_sku_group_len[i];
- int j;
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
+ sizeof(hdr) + MT7915_SKU_RATE_NUM);
+ if (!skb)
+ return -ENOMEM;
- if (i >= SKU_HT_BW20 && i <= SKU_VHT_BW160) {
- mcs_num = 10;
+ skb_put_data(skb, &hdr, sizeof(hdr));
+ skb_put_data(skb, &la.cck, len[SKU_CCK] + len[SKU_OFDM]);
+ skb_put_data(skb, &la.mcs[0], len[SKU_HT_BW20]);
+ skb_put_data(skb, &la.mcs[1], len[SKU_HT_BW40]);
- if (i == SKU_HT_BW20 || i == SKU_VHT_BW20)
- la = (s8 *)&limits_array + 12;
- } else {
- mcs_num = len;
- }
+ /* vht */
+ for (i = 0; i < 4; i++) {
+ skb_put_data(skb, &la.mcs[i], sizeof(la.mcs[i]));
+ skb_put_zero(skb, 2); /* padding */
+ }
- for (j = 0; j < min_t(u8, mcs_num, len); j++)
- req.txpower_sku[idx + j] = la[j];
+ /* he */
+ skb_put_data(skb, &la.ru[0], sizeof(la.ru));
+ ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), true);
+ if (ret)
+ return ret;
+
+ /* only set per-path power table when it's configured */
+ if (!phy->sku_path_en)
+ return 0;
- la += mcs_num;
- idx += len;
+ skb = mt76_mcu_msg_alloc(&dev->mt76, NULL,
+ sizeof(hdr) + MT7915_SKU_PATH_NUM);
+ if (!skb)
+ return -ENOMEM;
+
+ hdr.limit_type = TX_POWER_LIMIT_TABLE_PATH;
+ skb_put_data(skb, &hdr, sizeof(hdr));
+ skb_put_data(skb, &la.path.cck, sizeof(la.path.cck));
+ skb_put_data(skb, &la.path.ofdm, sizeof(la.path.ofdm));
+ skb_put_data(skb, &la.path.ofdm_bf[1], sizeof(la.path.ofdm_bf) - 1);
+
+ /* HT20 and HT40 */
+ skb_put_data(skb, &la.path.ru[3], sizeof(la.path.ru[3]));
+ skb_put_data(skb, &la.path.ru_bf[3][1], sizeof(la.path.ru_bf[3]) - 1);
+ skb_put_data(skb, &la.path.ru[4], sizeof(la.path.ru[4]));
+ skb_put_data(skb, &la.path.ru_bf[4][1], sizeof(la.path.ru_bf[4]) - 1);
+
+ /* start from non-bf and bf fields of
+ * BW20/RU242, BW40/RU484, BW80/RU996, BW160/RU2x996,
+ * RU26, RU52, and RU106
+ */
+
+ for (i = 0; i < 8; i++) {
+ bool bf = i % 2;
+ u8 idx = (i + 6) / 2;
+ s8 *buf = bf ? la.path.ru_bf[idx] : la.path.ru[idx];
+ /* The non-bf fields of RU26 to RU106 are special cases */
+ if (bf)
+ skb_put_data(skb, buf + 1, 9);
+ else
+ skb_put_data(skb, buf, 10);
}
- return mt76_mcu_send_msg(&dev->mt76,
- MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
- sizeof(req), true);
+ for (i = 0; i < 6; i++) {
+ bool bf = i % 2;
+ u8 idx = i / 2;
+ s8 *buf = bf ? la.path.ru_bf[idx] : la.path.ru[idx];
+
+ skb_put_data(skb, buf, 10);
+ }
+
+ return mt76_mcu_skb_send_msg(&dev->mt76, skb,
+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), true);
}
-int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
+int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len,
+ u8 category)
{
#define RATE_POWER_INFO 2
struct mt7915_dev *dev = phy->dev;
@@ -3431,10 +3517,9 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
u8 _rsv;
} __packed req = {
.format_id = TX_POWER_LIMIT_INFO,
- .category = RATE_POWER_INFO,
+ .category = category,
.band_idx = phy->mt76->band_idx,
};
- s8 txpower_sku[MT7915_SKU_RATE_NUM][2];
struct sk_buff *skb;
int ret, i;
@@ -3444,9 +3529,15 @@ int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len)
if (ret)
return ret;
- memcpy(txpower_sku, skb->data + 4, sizeof(txpower_sku));
- for (i = 0; i < len; i++)
- txpower[i] = txpower_sku[i][req.band_idx];
+ if (category == TX_POWER_INFO_RATE) {
+ s8 res[MT7915_SKU_RATE_NUM][2];
+
+ memcpy(res, skb->data + 4, sizeof(res));
+ for (i = 0; i < len; i++)
+ txpower[i] = res[i][req.band_idx];
+ } else if (category == TX_POWER_INFO_PATH) {
+ memcpy(txpower, skb->data + 4, len);
+ }
dev_kfree_skb(skb);
@@ -3475,7 +3566,7 @@ int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode,
sizeof(req), false);
}
-int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
+int mt7915_mcu_set_sku_en(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
struct mt7915_sku {
@@ -3484,10 +3575,21 @@ int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable)
u8 band_idx;
u8 rsv;
} __packed req = {
- .format_id = TX_POWER_LIMIT_ENABLE,
.band_idx = phy->mt76->band_idx,
- .sku_enable = enable,
};
+ int ret;
+
+ req.sku_enable = phy->sku_limit_en;
+ req.format_id = TX_POWER_LIMIT_ENABLE;
+
+ ret = mt76_mcu_send_msg(&dev->mt76,
+ MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
+ sizeof(req), true);
+ if (ret)
+ return ret;
+
+ req.sku_enable = phy->sku_path_en;
+ req.format_id = TX_POWER_LIMIT_PATH_ENABLE;
return mt76_mcu_send_msg(&dev->mt76,
MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
index 086ad89ecd91..3af11a075a2f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7915_MCU_H
@@ -429,6 +429,7 @@ enum {
enum {
TX_POWER_LIMIT_ENABLE,
+ TX_POWER_LIMIT_PATH_ENABLE = 0x3,
TX_POWER_LIMIT_TABLE = 0x4,
TX_POWER_LIMIT_INFO = 0x7,
TX_POWER_LIMIT_FRAME = 0x11,
@@ -436,6 +437,11 @@ enum {
};
enum {
+ TX_POWER_INFO_PATH = 1,
+ TX_POWER_INFO_RATE,
+};
+
+enum {
SPR_ENABLE = 0x1,
SPR_ENABLE_SD = 0x3,
SPR_ENABLE_MODE = 0x5,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
index 36488aa6cc20..2708b1556f40 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/kernel.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
index 2e94347c46d6..b5c06201b707 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7915_H
@@ -70,6 +70,7 @@
#define MT7915_CDEV_THROTTLE_MAX 99
#define MT7915_SKU_RATE_NUM 161
+#define MT7915_SKU_PATH_NUM 185
#define MT7915_MAX_TWT_AGRT 16
#define MT7915_MAX_STA_TWT_AGRT 8
@@ -223,6 +224,9 @@ struct mt7915_phy {
struct mt76_mib_stats mib;
struct mt76_channel_state state_ts;
+ bool sku_limit_en:1;
+ bool sku_path_en:1;
+
#ifdef CONFIG_NL80211_TESTMODE
struct {
u32 *reg_backup;
@@ -491,9 +495,10 @@ int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable,
int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode,
u8 en);
int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band);
-int mt7915_mcu_set_sku_en(struct mt7915_phy *phy, bool enable);
+int mt7915_mcu_set_sku_en(struct mt7915_phy *phy);
int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy);
-int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len);
+int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len,
+ u8 category);
int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower);
int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy,
struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
index 07b0a5766eab..f6b03211a879 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
* Author: Ryder Lee <ryder.lee@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
index c5ec63a25a42..307bf6a75674 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7915_REGS_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
index c823a7554a3a..54ff6de96f3e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2022 MediaTek Inc. */
#include <linux/kernel.h>
@@ -284,20 +284,15 @@ static int mt798x_wmac_coninfra_check(struct mt7915_dev *dev)
static int mt798x_wmac_coninfra_setup(struct mt7915_dev *dev)
{
struct device *pdev = dev->mt76.dev;
- struct reserved_mem *rmem;
- struct device_node *np;
+ struct resource res;
u32 val;
+ int ret;
- np = of_parse_phandle(pdev->of_node, "memory-region", 0);
- if (!np)
- return -EINVAL;
-
- rmem = of_reserved_mem_lookup(np);
- of_node_put(np);
- if (!rmem)
- return -EINVAL;
+ ret = of_reserved_mem_region_to_resource(pdev->of_node, 0, &res);
+ if (ret)
+ return ret;
- val = (rmem->base >> 16) & MT_TOP_MCU_EMI_BASE_MASK;
+ val = (res.start >> 16) & MT_TOP_MCU_EMI_BASE_MASK;
if (is_mt7986(&dev->mt76)) {
/* Set conninfra subsys PLL check */
@@ -318,8 +313,8 @@ static int mt798x_wmac_coninfra_setup(struct mt7915_dev *dev)
MT_TOP_EFUSE_BASE_MASK, 0x11f20000 >> 16);
}
- mt76_wr(dev, MT_INFRA_BUS_EMI_START, rmem->base);
- mt76_wr(dev, MT_INFRA_BUS_EMI_END, rmem->size);
+ mt76_wr(dev, MT_INFRA_BUS_EMI_START, res.start);
+ mt76_wr(dev, MT_INFRA_BUS_EMI_END, resource_size(&res));
mt76_rr(dev, MT_CONN_INFRA_EFUSE);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
index d534fff5c952..618a5c2bdd29 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include "mt7915.h"
@@ -409,7 +409,7 @@ mt7915_tm_init(struct mt7915_phy *phy, bool en)
if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
return;
- mt7915_mcu_set_sku_en(phy, !en);
+ mt7915_mcu_set_sku_en(phy);
mt7915_tm_mode_ctrl(dev, en);
mt7915_tm_reg_backup_restore(phy);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
index 5573ac309363..bb1bc568751b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7915_TESTMODE_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig
index 7ed51e057857..37b5f46e76f4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7921_COMMON
tristate
select MT792x_LIB
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
index 849be9e848e0..2ad3c1cc3779 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7921_COMMON) += mt7921-common.o
obj-$(CONFIG_MT7921E) += mt7921e.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
index 616b66a3fde2..4333005b3ad9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include "mt7921.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
index b9098a7331b1..29732315af1c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/etherdevice.h>
@@ -343,7 +343,7 @@ int mt7921_register_device(struct mt792x_dev *dev)
dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask;
dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask;
- queue_work(system_wq, &dev->init_work);
+ queue_work(system_percpu_wq, &dev->init_work);
return 0;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index bce26389ab18..03b4960db73f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/devcoredump.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 67383c41a319..5fae9a6e273c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/etherdevice.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 86bd33b916a9..833d0ab64230 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/fs.h>
@@ -646,10 +646,10 @@ int mt7921_run_firmware(struct mt792x_dev *dev)
if (err)
return err;
- set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
err = mt7921_load_clc(dev, mt792x_ram_name(dev));
if (err)
return err;
+ set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
return mt7921_mcu_fw_log_2_host(dev, 1);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h
index 2834c6c53e58..de676b83b89c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7921_MCU_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
index c88793fcec64..83fc7f49ff84 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7921_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index a0c9df3c2cc7..ec9686183251 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
index 881812ba03ff..5ec084432ae3 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include "mt7921.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c
index 4cf1f2f0f968..8439c849a7a6 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include "mt7921.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h
index 43427a3a48af..4d9eaf1e0692 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc. */
#ifndef __MT7921_REGS_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
index d8d36b3c3068..3421e53dc948 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc.
*
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c
index a9eb6252a904..416d49e53499 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include <linux/iopoll.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c
index 5e4501d7f1c0..14e66f3f5aad 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2021 MediaTek Inc. */
#include <linux/kernel.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7921/testmode.c
index e838d93477c1..e60ee992edf8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/testmode.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7921.h"
#include "mcu.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
index 100bdba32ba5..17057e68bf21 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2022 MediaTek Inc.
*
* Author: Lorenzo Bianconi <lorenzo@kernel.org>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig
index 5854e95e68a5..f4f7c93c2ea7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7925_COMMON
tristate
select MT792x_LIB
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
index ade5e647c941..8f1078ce3231 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile
@@ -1,10 +1,10 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o
obj-$(CONFIG_MT7925E) += mt7925e.o
obj-$(CONFIG_MT7925U) += mt7925u.o
-mt7925-common-y := mac.o mcu.o main.o init.o debugfs.o
+mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o
mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
mt7925e-y := pci.o pci_mac.o pci_mcu.o
mt7925u-y := usb.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
index 1e2fc6577e78..e2498659c884 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt7925.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index d7d5afe365ed..3ce5d6fcc69d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/etherdevice.h>
@@ -7,6 +7,7 @@
#include <linux/thermal.h>
#include <linux/firmware.h>
#include "mt7925.h"
+#include "regd.h"
#include "mac.h"
#include "mcu.h"
@@ -60,151 +61,6 @@ static int mt7925_thermal_init(struct mt792x_phy *phy)
return PTR_ERR_OR_ZERO(hwmon);
}
-void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2)
-{
- struct mt792x_phy *phy = &dev->phy;
- struct mt7925_clc_rule_v2 *rule;
- struct mt7925_clc *clc;
- bool old = dev->has_eht, new = true;
- u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2);
- u8 *pos;
-
- if (mtcl_conf != MT792X_ACPI_MTCL_INVALID &&
- (((mtcl_conf >> 4) & 0x3) == 0)) {
- new = false;
- goto out;
- }
-
- if (!phy->clc[MT792x_CLC_BE_CTRL])
- goto out;
-
- clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL];
- pos = clc->data;
-
- while (1) {
- rule = (struct mt7925_clc_rule_v2 *)pos;
-
- if (rule->alpha2[0] == alpha2[0] &&
- rule->alpha2[1] == alpha2[1]) {
- new = false;
- break;
- }
-
- /* Check the last one */
- if (rule->flag & BIT(0))
- break;
-
- pos += sizeof(*rule);
- }
-
-out:
- if (old == new)
- return;
-
- dev->has_eht = new;
- mt7925_set_stream_he_eht_caps(phy);
-}
-
-static void
-mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
-{
-#define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \
- (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
-#define MT7925_UNII_59G_IS_VALID 0x1
-#define MT7925_UNII_6G_IS_VALID 0x1e
- struct ieee80211_supported_band *sband;
- struct mt76_dev *mdev = &dev->mt76;
- struct ieee80211_channel *ch;
- u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2);
- int i;
-
- if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) {
- if ((mtcl_conf & 0x3) == 0)
- dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID;
- if (((mtcl_conf >> 2) & 0x3) == 0)
- dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID;
- }
-
- sband = wiphy->bands[NL80211_BAND_5GHZ];
- if (!sband)
- return;
-
- for (i = 0; i < sband->n_channels; i++) {
- ch = &sband->channels[i];
-
- /* UNII-4 */
- if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq))
- ch->flags |= IEEE80211_CHAN_DISABLED;
- }
-
- sband = wiphy->bands[NL80211_BAND_6GHZ];
- if (!sband)
- return;
-
- for (i = 0; i < sband->n_channels; i++) {
- ch = &sband->channels[i];
-
- /* UNII-5/6/7/8 */
- if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) ||
- IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) ||
- IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) ||
- IS_UNII_INVALID(4, 6875, 7125, ch->center_freq))
- ch->flags |= IEEE80211_CHAN_DISABLED;
- }
-}
-
-void mt7925_regd_update(struct mt792x_dev *dev)
-{
- struct mt76_dev *mdev = &dev->mt76;
- struct ieee80211_hw *hw = mdev->hw;
- struct wiphy *wiphy = hw->wiphy;
-
- if (!dev->regd_change)
- return;
-
- mt7925_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
- mt7925_regd_channel_update(wiphy, dev);
- mt7925_mcu_set_channel_domain(hw->priv);
- mt7925_set_tx_sar_pwr(hw, NULL);
- dev->regd_change = false;
-}
-EXPORT_SYMBOL_GPL(mt7925_regd_update);
-
-static void
-mt7925_regd_notifier(struct wiphy *wiphy,
- struct regulatory_request *req)
-{
- struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
- struct mt792x_dev *dev = mt792x_hw_dev(hw);
- struct mt76_dev *mdev = &dev->mt76;
- struct mt76_connac_pm *pm = &dev->pm;
-
- /* allow world regdom at the first boot only */
- if (!memcmp(req->alpha2, "00", 2) &&
- mdev->alpha2[0] && mdev->alpha2[1])
- return;
-
- /* do not need to update the same country twice */
- if (!memcmp(req->alpha2, mdev->alpha2, 2) &&
- dev->country_ie_env == req->country_ie_env)
- return;
-
- memcpy(mdev->alpha2, req->alpha2, 2);
- mdev->region = req->dfs_region;
- dev->country_ie_env = req->country_ie_env;
- dev->regd_change = true;
-
- if (pm->suspended)
- return;
-
- dev->regd_in_progress = true;
- mt792x_mutex_acquire(dev);
- mt7925_regd_update(dev);
- mt792x_mutex_release(dev);
- dev->regd_in_progress = false;
- wake_up(&dev->wait);
-}
-
static void mt7925_mac_init_basic_rates(struct mt792x_dev *dev)
{
int i;
@@ -235,8 +91,6 @@ int mt7925_mac_init(struct mt792x_dev *dev)
mt7925_mac_init_basic_rates(dev);
- memzero_explicit(&dev->mt76.alpha2, sizeof(dev->mt76.alpha2));
-
return 0;
}
EXPORT_SYMBOL_GPL(mt7925_mac_init);
@@ -420,7 +274,7 @@ int mt7925_register_device(struct mt792x_dev *dev)
dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask;
dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask;
- queue_work(system_wq, &dev->init_work);
+ queue_work(system_percpu_wq, &dev->init_work);
return 0;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index 1e44e96f034e..871b67101976 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/devcoredump.h>
@@ -6,6 +6,7 @@
#include <linux/timekeeping.h>
#include "mt7925.h"
#include "../dma.h"
+#include "regd.h"
#include "mac.h"
#include "mcu.h"
@@ -1329,9 +1330,7 @@ void mt7925_mac_reset_work(struct work_struct *work)
mt7925_vif_connect_iter, NULL);
mt76_connac_power_save_sched(&dev->mt76.phy, pm);
- mt792x_mutex_acquire(dev);
- mt7925_mcu_set_clc(dev, "00", ENVIRON_INDOOR);
- mt792x_mutex_release(dev);
+ mt7925_regd_change(&dev->phy, "00");
}
void mt7925_coredump_work(struct work_struct *work)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h
index b10a993326b9..83ea9021daea 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_MAC_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index ac3d485a2f78..2d358a96640c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/etherdevice.h>
@@ -8,6 +8,7 @@
#include <linux/ctype.h>
#include <net/ipv6.h>
#include "mt7925.h"
+#include "regd.h"
#include "mcu.h"
#include "mac.h"
@@ -138,10 +139,14 @@ mt7925_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band,
}
if (band == NL80211_BAND_6GHZ) {
+ struct ieee80211_supported_band *sband =
+ &phy->mt76->sband_5g.sband;
+ struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
+
u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS |
IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS;
- cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_0_5,
+ cap |= u16_encode_bits(ht_cap->ampdu_density,
IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) |
u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K,
IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) |
@@ -430,6 +435,9 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
goto out;
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+ if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN)
+ vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI;
+
out:
mt792x_mutex_release(dev);
@@ -1312,20 +1320,6 @@ void mt7925_mlo_pm_work(struct work_struct *work)
mt7925_mlo_pm_iter, dev);
}
-static bool is_valid_alpha2(const char *alpha2)
-{
- if (!alpha2)
- return false;
-
- if (alpha2[0] == '0' && alpha2[1] == '0')
- return true;
-
- if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
- return true;
-
- return false;
-}
-
void mt7925_scan_work(struct work_struct *work)
{
struct mt792x_phy *phy;
@@ -1334,7 +1328,6 @@ void mt7925_scan_work(struct work_struct *work)
scan_work.work);
while (true) {
- struct mt76_dev *mdev = &phy->dev->mt76;
struct sk_buff *skb;
struct tlv *tlv;
int tlv_len;
@@ -1365,15 +1358,7 @@ void mt7925_scan_work(struct work_struct *work)
case UNI_EVENT_SCAN_DONE_CHNLINFO:
evt = (struct mt7925_mcu_scan_chinfo_event *)tlv->data;
- if (!is_valid_alpha2(evt->alpha2))
- break;
-
- mt7925_regd_be_ctrl(phy->dev, evt->alpha2);
-
- if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
- break;
-
- mt7925_mcu_set_clc(phy->dev, evt->alpha2, ENVIRON_INDOOR);
+ mt7925_regd_change(phy, evt->alpha2);
break;
case UNI_EVENT_SCAN_DONE_NLO:
@@ -1958,6 +1943,9 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw,
mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76,
link_conf, NULL);
+ if (changed & BSS_CHANGED_CQM)
+ mt7925_mcu_set_rssimonitor(dev, vif);
+
mt792x_mutex_release(dev);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 8eda407e4135..cf0fdea45cf7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1,19 +1,16 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/fs.h>
#include <linux/firmware.h>
#include "mt7925.h"
+#include "regd.h"
#include "mcu.h"
#include "mac.h"
#define MT_STA_BFER BIT(0)
#define MT_STA_BFEE BIT(1)
-static bool mt7925_disable_clc;
-module_param_named(disable_clc, mt7925_disable_clc, bool, 0644);
-MODULE_PARM_DESC(disable_clc, "disable CLC support");
-
int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd,
struct sk_buff *skb, int seq)
{
@@ -451,6 +448,56 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb)
}
static void
+mt7925_mcu_rssi_monitor_iter(void *priv, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mt7925_uni_rssi_monitor_event *event = priv;
+ enum nl80211_cqm_rssi_threshold_event nl_event;
+ s32 rssi = le32_to_cpu(event->rssi);
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
+ return;
+
+ if (rssi > vif->bss_conf.cqm_rssi_thold)
+ nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+ else
+ nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+
+ ieee80211_cqm_rssi_notify(vif, nl_event, rssi, GFP_KERNEL);
+}
+
+static void
+mt7925_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+ struct tlv *tlv;
+ u32 tlv_len;
+ struct mt7925_uni_rssi_monitor_event *event;
+
+ skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
+ tlv = (struct tlv *)skb->data;
+ tlv_len = skb->len;
+
+ while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
+ switch (le16_to_cpu(tlv->tag)) {
+ case UNI_EVENT_RSSI_MONITOR_INFO:
+ event = (struct mt7925_uni_rssi_monitor_event *)skb->data;
+ ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_mcu_rssi_monitor_iter,
+ event);
+ break;
+ default:
+ break;
+ }
+ tlv_len -= le16_to_cpu(tlv->len);
+ tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
+ }
+}
+
+static void
mt7925_mcu_uni_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb)
{
struct mt7925_uni_debug_msg {
@@ -546,6 +593,9 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev,
case MCU_UNI_EVENT_BSS_BEACON_LOSS:
mt7925_mcu_connection_loss_event(dev, skb);
break;
+ case MCU_UNI_EVENT_RSSI_MONITOR:
+ mt7925_mcu_rssi_monitor_event(dev, skb);
+ break;
case MCU_UNI_EVENT_COREDUMP:
dev->fw_assert = true;
mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump);
@@ -688,8 +738,8 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name)
int ret, i, len, offset = 0;
dev->phy.clc_chan_conf = 0xff;
- if (mt7925_disable_clc ||
- mt76_is_usb(&dev->mt76))
+ dev->regd_user = false;
+ if (!mt7925_regd_clc_supported(dev))
return 0;
if (mt76_is_mmio(&dev->mt76)) {
@@ -759,6 +809,7 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name)
}
}
+ ret = mt7925_regd_init(phy);
out:
release_firmware(fw);
@@ -1003,10 +1054,10 @@ int mt7925_run_firmware(struct mt792x_dev *dev)
if (err)
return err;
- set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
err = mt7925_load_clc(dev, mt792x_ram_name(dev));
if (err)
return err;
+ set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
return mt7925_mcu_fw_log_2_host(dev, 1);
}
@@ -3383,6 +3434,9 @@ int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy;
int i, ret;
+ if (!ARRAY_SIZE(phy->clc))
+ return -ESRCH;
+
/* submit all clc config */
for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
if (i == MT792x_CLC_BE_CTRL)
@@ -3818,3 +3872,32 @@ int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG),
&req, sizeof(req), true);
}
+
+int mt7925_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif)
+{
+ struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(&vif->bss_conf);
+ struct {
+ struct {
+ u8 bss_idx;
+ u8 pad[3];
+ } __packed hdr;
+ __le16 tag;
+ __le16 len;
+ u8 enable;
+ s8 cqm_rssi_high;
+ s8 cqm_rssi_low;
+ u8 rsv;
+ } req = {
+ .hdr = {
+ .bss_idx = mconf->mt76.idx,
+ },
+ .tag = cpu_to_le16(UNI_CMD_RSSI_MONITOR_SET),
+ .len = cpu_to_le16(sizeof(req) - 4),
+ .enable = vif->cfg.assoc,
+ .cqm_rssi_high = (s8)(vif->bss_conf.cqm_rssi_thold + vif->bss_conf.cqm_rssi_hyst),
+ .cqm_rssi_low = (s8)(vif->bss_conf.cqm_rssi_thold - vif->bss_conf.cqm_rssi_hyst),
+ };
+
+ return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(RSSI_MONITOR), &req,
+ sizeof(req), false);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
index a40764d89a1f..e09e0600534a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_MCU_H
@@ -152,6 +152,14 @@ enum {
UNI_EVENT_SCAN_DONE_NLO = 3,
};
+enum {
+ UNI_CMD_RSSI_MONITOR_SET = 0,
+};
+
+enum {
+ UNI_EVENT_RSSI_MONITOR_INFO = 0,
+};
+
enum connac3_mcu_cipher_type {
CONNAC3_CIPHER_NONE = 0,
CONNAC3_CIPHER_WEP40 = 1,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 1b165d0d8bd3..6b9bf1b89032 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_H
@@ -103,6 +103,12 @@ struct mt7925_uni_beacon_loss_event {
struct mt7925_beacon_loss_tlv beacon_loss;
} __packed;
+struct mt7925_uni_rssi_monitor_event {
+ __le16 tag;
+ __le16 len;
+ __le32 rssi;
+} __packed;
+
#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2)
#define to_rcpi(rssi) (2 * (rssi) + 220)
@@ -257,8 +263,6 @@ int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd);
int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif,
u8 bit_op, u32 bit_map);
-void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2);
-void mt7925_regd_update(struct mt792x_dev *dev);
int mt7925_mac_init(struct mt792x_dev *dev);
int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
@@ -372,4 +376,5 @@ int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg,
struct netlink_callback *cb, void *data, int len);
+int mt7925_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif);
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 8eb1fe1082d1..c4161754c01d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/kernel.h>
@@ -8,6 +8,7 @@
#include "mt7925.h"
#include "mac.h"
#include "mcu.h"
+#include "regd.h"
#include "../dma.h"
static const struct pci_device_id mt7925_pci_device_table[] = {
@@ -584,7 +585,7 @@ static int _mt7925_pci_resume(struct device *device, bool restore)
if (!pm->ds_enable)
mt7925_mcu_set_deep_sleep(dev, false);
- mt7925_regd_update(dev);
+ mt7925_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
failed:
pm->suspended = false;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
index 4578d16bf456..3072850c2752 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt7925.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
index f95bc5dcd830..6cceff88c656 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt7925.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
new file mode 100644
index 000000000000..292087e882d1
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/* Copyright (C) 2025 MediaTek Inc. */
+
+#include "mt7925.h"
+#include "regd.h"
+#include "mcu.h"
+
+static bool mt7925_disable_clc;
+module_param_named(disable_clc, mt7925_disable_clc, bool, 0644);
+MODULE_PARM_DESC(disable_clc, "disable CLC support");
+
+bool mt7925_regd_clc_supported(struct mt792x_dev *dev)
+{
+ if (mt7925_disable_clc ||
+ mt76_is_usb(&dev->mt76))
+ return false;
+
+ return true;
+}
+
+void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2)
+{
+ struct mt792x_phy *phy = &dev->phy;
+ struct mt7925_clc_rule_v2 *rule;
+ struct mt7925_clc *clc;
+ bool old = dev->has_eht, new = true;
+ u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2);
+ u8 *pos;
+
+ if (mtcl_conf != MT792X_ACPI_MTCL_INVALID &&
+ (((mtcl_conf >> 4) & 0x3) == 0)) {
+ new = false;
+ goto out;
+ }
+
+ if (!phy->clc[MT792x_CLC_BE_CTRL])
+ goto out;
+
+ clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL];
+ pos = clc->data;
+
+ while (1) {
+ rule = (struct mt7925_clc_rule_v2 *)pos;
+
+ if (rule->alpha2[0] == alpha2[0] &&
+ rule->alpha2[1] == alpha2[1]) {
+ new = false;
+ break;
+ }
+
+ /* Check the last one */
+ if (rule->flag & BIT(0))
+ break;
+
+ pos += sizeof(*rule);
+ }
+
+out:
+ if (old == new)
+ return;
+
+ dev->has_eht = new;
+ mt7925_set_stream_he_eht_caps(phy);
+}
+
+static void
+mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
+{
+#define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \
+ (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
+#define MT7925_UNII_59G_IS_VALID 0x1
+#define MT7925_UNII_6G_IS_VALID 0x1e
+ struct ieee80211_supported_band *sband;
+ struct mt76_dev *mdev = &dev->mt76;
+ struct ieee80211_channel *ch;
+ u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2);
+ int i;
+
+ if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) {
+ if ((mtcl_conf & 0x3) == 0)
+ dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID;
+ if (((mtcl_conf >> 2) & 0x3) == 0)
+ dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID;
+ }
+
+ sband = wiphy->bands[NL80211_BAND_2GHZ];
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ if (!dev->has_eht)
+ ch->flags |= IEEE80211_CHAN_NO_EHT;
+ }
+
+ sband = wiphy->bands[NL80211_BAND_5GHZ];
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ /* UNII-4 */
+ if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq))
+ ch->flags |= IEEE80211_CHAN_DISABLED;
+
+ if (!dev->has_eht)
+ ch->flags |= IEEE80211_CHAN_NO_EHT;
+ }
+
+ sband = wiphy->bands[NL80211_BAND_6GHZ];
+ if (!sband)
+ return;
+
+ for (i = 0; i < sband->n_channels; i++) {
+ ch = &sband->channels[i];
+
+ /* UNII-5/6/7/8 */
+ if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) ||
+ IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) ||
+ IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) ||
+ IS_UNII_INVALID(4, 6875, 7125, ch->center_freq))
+ ch->flags |= IEEE80211_CHAN_DISABLED;
+
+ if (!dev->has_eht)
+ ch->flags |= IEEE80211_CHAN_NO_EHT;
+ }
+}
+
+int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
+ enum environment_cap country_ie_env)
+{
+ struct ieee80211_hw *hw = mt76_hw(dev);
+ struct wiphy *wiphy = hw->wiphy;
+ int ret = 0;
+
+ dev->regd_in_progress = true;
+
+ mt792x_mutex_acquire(dev);
+ if (!dev->regd_change)
+ goto err;
+
+ ret = mt7925_mcu_set_clc(dev, alpha2, country_ie_env);
+ if (ret < 0)
+ goto err;
+
+ mt7925_regd_be_ctrl(dev, alpha2);
+ mt7925_regd_channel_update(wiphy, dev);
+
+ ret = mt7925_mcu_set_channel_domain(hw->priv);
+ if (ret < 0)
+ goto err;
+
+ ret = mt7925_set_tx_sar_pwr(hw, NULL);
+ if (ret < 0)
+ goto err;
+
+err:
+ mt792x_mutex_release(dev);
+ dev->regd_change = false;
+ dev->regd_in_progress = false;
+ wake_up(&dev->wait);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mt7925_mcu_regd_update);
+
+void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt76_connac_pm *pm = &dev->pm;
+ struct mt76_dev *mdev = &dev->mt76;
+
+ if (req->initiator == NL80211_REGDOM_SET_BY_USER &&
+ !dev->regd_user)
+ dev->regd_user = true;
+
+ /* allow world regdom at the first boot only */
+ if (!memcmp(req->alpha2, "00", 2) &&
+ mdev->alpha2[0] && mdev->alpha2[1])
+ return;
+
+ /* do not need to update the same country twice */
+ if (!memcmp(req->alpha2, mdev->alpha2, 2) &&
+ dev->country_ie_env == req->country_ie_env)
+ return;
+
+ memcpy(mdev->alpha2, req->alpha2, 2);
+ mdev->region = req->dfs_region;
+ dev->country_ie_env = req->country_ie_env;
+
+ dev->regd_change = true;
+
+ if (pm->suspended)
+ /* postpone the mcu update to resume */
+ return;
+
+ mt7925_mcu_regd_update(dev, req->alpha2,
+ req->country_ie_env);
+ return;
+}
+
+static bool
+mt7925_regd_is_valid_alpha2(const char *alpha2)
+{
+ if (!alpha2)
+ return false;
+
+ if (alpha2[0] == '0' && alpha2[1] == '0')
+ return true;
+
+ if (isalpha(alpha2[0]) && isalpha(alpha2[1]))
+ return true;
+
+ return false;
+}
+
+int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2)
+{
+ struct wiphy *wiphy = phy->mt76->hw->wiphy;
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt76_dev *mdev = &dev->mt76;
+
+ if (dev->hw_full_reset)
+ return 0;
+
+ if (!mt7925_regd_is_valid_alpha2(alpha2) ||
+ !mt7925_regd_clc_supported(dev) ||
+ dev->regd_user)
+ return -EINVAL;
+
+ if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0')
+ return 0;
+
+ /* do not need to update the same country twice */
+ if (!memcmp(alpha2, mdev->alpha2, 2))
+ return 0;
+
+ if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
+ return regulatory_hint(wiphy, alpha2);
+ } else {
+ return mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
+ }
+}
+EXPORT_SYMBOL_GPL(mt7925_regd_change);
+
+int mt7925_regd_init(struct mt792x_phy *phy)
+{
+ struct wiphy *wiphy = phy->mt76->hw->wiphy;
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct mt792x_dev *dev = mt792x_hw_dev(hw);
+ struct mt76_dev *mdev = &dev->mt76;
+
+ if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
+ wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
+ REGULATORY_DISABLE_BEACON_HINTS;
+ } else {
+ memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2));
+ }
+
+ return 0;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
new file mode 100644
index 000000000000..0767f078862e
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/* Copyright (C) 2025 MediaTek Inc. */
+
+#ifndef __MT7925_REGD_H
+#define __MT7925_REGD_H
+
+#include "mt7925.h"
+
+int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
+ enum environment_cap country_ie_env);
+
+void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2);
+void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req);
+bool mt7925_regd_clc_supported(struct mt792x_dev *dev);
+int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2);
+int mt7925_regd_init(struct mt792x_phy *phy);
+
+#endif
+
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
index 341987e47f67..24985bba1b90 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7925_REGS_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
index a3c97164ba21..3d40aacfc011 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
#include "mt7925.h"
#include "mcu.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
index bf040f34e4b9..d9968f03856d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/kernel.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index f2c8b9e4aa0f..8388638ed550 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT792X_H
@@ -28,6 +28,7 @@
#define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0)
#define MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN BIT(1)
#define MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN BIT(3)
+#define MT792x_CHIP_CAP_11D_EN BIT(4)
#define MT792x_CHIP_CAP_MLO_EN BIT(8)
#define MT792x_CHIP_CAP_MLO_EML_EN BIT(9)
@@ -230,6 +231,7 @@ struct mt792x_dev {
bool hw_init_done:1;
bool fw_assert:1;
bool has_eht:1;
+ bool regd_user:1;
bool regd_in_progress:1;
bool aspm_supported:1;
bool hif_idle:1;
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c
index d1aebadd50aa..946dd7956e4a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/acpi.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h
index e45dcd7fbdb1..474033073831 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT7921_ACPI_SAR_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index c0e56541a954..f2ed16feb6c1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/module.h>
@@ -688,7 +688,6 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw)
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW);
ieee80211_hw_set(hw, CONNECTION_MONITOR);
- ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR);
ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID);
ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c
index 9858d9a93851..65c37e0cef8f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include "mt792x.h"
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
index 69217ce91130..1ddec7788b66 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/module.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
index 3f1d9ba49076..71dec93094eb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/module.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
index 458cfd0260b1..acf627aed609 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef __MT792X_REGS_H
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_trace.c b/drivers/net/wireless/mediatek/mt76/mt792x_trace.c
index b6f284fb929d..ffc77d3944bd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_trace.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_trace.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2023 Lorenzo Bianconi <lorenzo@kernel.org>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_trace.h b/drivers/net/wireless/mediatek/mt76/mt792x_trace.h
index 61f2aa260656..7b0e3f00b194 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_trace.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_trace.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2023 Lorenzo Bianconi <lorenzo@kernel.org>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
index 76272a03b22e..552808458138 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc.
*
* Author: Lorenzo Bianconi <lorenzo@kernel.org>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
index bb44d4a5e2dc..5503d03bf62c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
config MT7996E
tristate "MediaTek MT7996 (PCIe) support"
select MT76_CONNAC_LIB
@@ -12,3 +12,10 @@ config MT7996E
and 2.4GHz IEEE 802.11be 4x4:4SS 4096-QAM, 320MHz channels.
To compile this driver as a module, choose M here.
+
+config MT7996_NPU
+ bool "MT7996 (PCIe) NPU support"
+ depends on MT7996E
+ depends on NET_AIROHA_NPU=y || MT7996E=NET_AIROHA_NPU
+ select MT76_NPU
+ default n
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Makefile b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile
index 07c8b555c1ac..69d2d4bb9e69 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/Makefile
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile
@@ -1,8 +1,9 @@
-# SPDX-License-Identifier: ISC
+# SPDX-License-Identifier: BSD-3-Clause-Clear
obj-$(CONFIG_MT7996E) += mt7996e.o
mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \
debugfs.o mmio.o
+mt7996e-$(CONFIG_MT7996_NPU) += npu.o
mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
index 303d6e80a666..5c293ae965cd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2023 MediaTek Inc. */
#include <linux/devcoredump.h>
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.h
index af2ba219b1b5..baa2f6f50832 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2023 MediaTek Inc. */
#ifndef _COREDUMP_H_
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
index 0ab827f52fd7..76d623b2cafb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -953,16 +953,34 @@ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len)
#ifdef CONFIG_MAC80211_DEBUGFS
/** per-station debugfs **/
-static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
+static int
+mt7996_queues_show(struct seq_file *s, void *data)
+{
+ struct ieee80211_sta *sta = s->private;
+
+ mt7996_sta_hw_queue_read(s, sta);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mt7996_queues);
+
+void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct dentry *dir)
+{
+ debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
+}
+
+static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
{
#define SHORT_PREAMBLE 0
#define LONG_PREAMBLE 1
- struct ieee80211_sta *sta = file->private_data;
- struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct ieee80211_link_sta *link_sta = file->private_data;
+ struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv;
struct mt7996_dev *dev = msta->vif->deflink.phy->dev;
- struct mt7996_sta_link *msta_link = &msta->deflink;
+ struct mt7996_sta_link *msta_link;
struct ra_rate phy = {};
char buf[100];
int ret;
@@ -981,12 +999,13 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
/* mode - cck: 0, ofdm: 1, ht: 2, gf: 3, vht: 4, he_su: 8, he_er: 9 EHT: 15
* bw - bw20: 0, bw40: 1, bw80: 2, bw160: 3, BW320: 4
- * nss - vht: 1~4, he: 1~4, eht: 1~4, others: ignore
* mcs - cck: 0~4, ofdm: 0~7, ht: 0~32, vht: 0~9, he_su: 0~11, he_er: 0~2, eht: 0~13
+ * nss - vht: 1~4, he: 1~4, eht: 1~4, others: ignore
* gi - (ht/vht) lgi: 0, sgi: 1; (he) 0.8us: 0, 1.6us: 1, 3.2us: 2
* preamble - short: 1, long: 0
- * ldpc - off: 0, on: 1
* stbc - off: 0, on: 1
+ * ldpc - off: 0, on: 1
+ * spe - off: 0, on: 1
* ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2
*/
if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu",
@@ -994,9 +1013,16 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
&phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, &ltf) != 10) {
dev_warn(dev->mt76.dev,
"format: Mode BW MCS NSS GI Preamble STBC LDPC SPE ltf\n");
- goto out;
+ return -EINVAL;
}
+ mutex_lock(&dev->mt76.mutex);
+
+ msta_link = mt76_dereference(msta->link[link_sta->link_id], &dev->mt76);
+ if (!msta_link) {
+ ret = -EINVAL;
+ goto out;
+ }
phy.wlan_idx = cpu_to_le16(msta_link->wcid.idx);
phy.gi = cpu_to_le16(gi);
phy.ltf = cpu_to_le16(ltf);
@@ -1005,36 +1031,26 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file,
ret = mt7996_mcu_set_fixed_rate_ctrl(dev, &phy, 0);
if (ret)
- return -EFAULT;
+ goto out;
+ ret = count;
out:
- return count;
+ mutex_unlock(&dev->mt76.mutex);
+ return ret;
}
static const struct file_operations fops_fixed_rate = {
- .write = mt7996_sta_fixed_rate_set,
+ .write = mt7996_link_sta_fixed_rate_set,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
-static int
-mt7996_queues_show(struct seq_file *s, void *data)
-{
- struct ieee80211_sta *sta = s->private;
-
- mt7996_sta_hw_queue_read(s, sta);
-
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(mt7996_queues);
-
-void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct ieee80211_sta *sta, struct dentry *dir)
+void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct dentry *dir)
{
- debugfs_create_file("fixed_rate", 0600, dir, sta, &fops_fixed_rate);
- debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops);
+ debugfs_create_file("fixed_rate", 0600, dir, link_sta, &fops_fixed_rate);
}
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
index 659015f93d32..274b273df1ee 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -23,6 +23,9 @@ int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc,
flags = MT_WED_Q_TX(idx);
}
+ if (mt76_npu_device_active(&dev->mt76))
+ flags = MT_NPU_Q_TX(phy->mt76->band_idx);
+
return mt76_connac_init_tx_queues(phy->mt76, idx, n_desc,
ring_base, wed, flags);
}
@@ -344,7 +347,7 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset)
mtk_wed_device_start(wed, wed_irq_mask);
}
- if (!mt7996_has_wa(dev))
+ if (!mt7996_has_wa(dev) || mt76_npu_device_active(&dev->mt76))
irq_mask &= ~(MT_INT_RX(MT_RXQ_MAIN_WA) |
MT_INT_RX(MT_RXQ_BAND1_WA));
irq_mask = reset ? MT_INT_MCU_CMD : irq_mask;
@@ -502,7 +505,7 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].flags = MT_WED_RRO_Q_RXDMAD_C;
if (mtk_wed_device_active(&mdev->mmio.wed))
mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].wed = &mdev->mmio.wed;
- else
+ else if (!mt76_npu_device_active(&dev->mt76))
mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].flags |= MT_QFLAG_EMI_EN;
ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_RRO_RXDMAD_C],
MT_RXQ_ID(MT_RXQ_RRO_RXDMAD_C),
@@ -512,12 +515,15 @@ int mt7996_dma_rro_init(struct mt7996_dev *dev)
if (ret)
return ret;
- /* We need to set cpu idx pointer before resetting the EMI
- * queues.
- */
- mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].emi_cpu_idx =
- &dev->wed_rro.emi_rings_cpu.ptr->ring[0].idx;
- mt76_queue_reset(dev, &mdev->q_rx[MT_RXQ_RRO_RXDMAD_C], true);
+ if (!mtk_wed_device_active(&mdev->mmio.wed)) {
+ /* We need to set cpu idx pointer before resetting the
+ * EMI queues.
+ */
+ mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].emi_cpu_idx =
+ &dev->wed_rro.emi_rings_cpu.ptr->ring[0].idx;
+ mt76_queue_reset(dev, &mdev->q_rx[MT_RXQ_RRO_RXDMAD_C],
+ true);
+ }
goto start_hw_rro;
}
@@ -610,7 +616,9 @@ start_hw_rro:
mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0,
mt76_dma_rx_poll);
}
- mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE);
+
+ if (!mt76_npu_device_active(&dev->mt76))
+ mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE);
}
return 0;
@@ -884,6 +892,10 @@ int mt7996_dma_init(struct mt7996_dev *dev)
if (ret < 0)
return ret;
+ ret = mt7996_npu_rx_queues_init(dev);
+ if (ret)
+ return ret;
+
netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi,
mt7996_poll_tx);
napi_enable(&dev->mt76.tx_napi);
@@ -941,6 +953,7 @@ void mt7996_dma_reset(struct mt7996_dev *dev, bool force)
if (mtk_wed_device_active(&dev->mt76.mmio.wed))
mtk_wed_device_dma_reset(&dev->mt76.mmio.wed);
+ mt76_npu_disable_irqs(&dev->mt76);
mt7996_dma_disable(dev, force);
mt76_wed_dma_reset(&dev->mt76);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
index da3231c9aa11..8f60772913b4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h
index 7a771ca2434c..9e6f0e04caf9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2022 MediaTek Inc.
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
index 5e95a36b42d1..00a8286bd136 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -475,7 +475,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed)
hw->max_tx_aggregation_subframes = 512;
hw->netdev_features = NETIF_F_RXCSUM;
- if (mtk_wed_device_active(wed))
+ if (mtk_wed_device_active(wed) || mt76_npu_device_active(mdev))
hw->netdev_features |= NETIF_F_HW_TC;
hw->radiotap_timestamp.units_pos =
@@ -830,7 +830,8 @@ void mt7996_rro_hw_init(struct mt7996_dev *dev)
MT_RRO_3_0_EMU_CONF_EN_MASK);
mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG,
MT_RRO_3_1_GLOBAL_CONFIG_RXDMAD_SEL);
- if (!mtk_wed_device_active(&dev->mt76.mmio.wed)) {
+ if (!mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+ !mt76_npu_device_active(&dev->mt76)) {
mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG,
MT_RRO_3_1_GLOBAL_CONFIG_RX_DIDX_WR_EN |
MT_RRO_3_1_GLOBAL_CONFIG_RX_CIDX_RD_EN);
@@ -959,9 +960,10 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
MT7996_RRO_MSDU_PG_SIZE_PER_CR);
}
- if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) {
+ if (!mtk_wed_device_active(&dev->mt76.mmio.wed) &&
+ dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) {
ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
- sizeof(dev->wed_rro.emi_rings_cpu.ptr),
+ sizeof(*dev->wed_rro.emi_rings_cpu.ptr),
&dev->wed_rro.emi_rings_cpu.phy_addr,
GFP_KERNEL);
if (!ptr)
@@ -970,7 +972,7 @@ static int mt7996_wed_rro_init(struct mt7996_dev *dev)
dev->wed_rro.emi_rings_cpu.ptr = ptr;
ptr = dmam_alloc_coherent(dev->mt76.dma_dev,
- sizeof(dev->wed_rro.emi_rings_dma.ptr),
+ sizeof(*dev->wed_rro.emi_rings_dma.ptr),
&dev->wed_rro.emi_rings_dma.phy_addr,
GFP_KERNEL);
if (!ptr)
@@ -1036,6 +1038,18 @@ static void mt7996_wed_rro_free(struct mt7996_dev *dev)
dev->wed_rro.msdu_pg[i].phy_addr);
}
+ if (dev->wed_rro.emi_rings_cpu.ptr)
+ dmam_free_coherent(dev->mt76.dma_dev,
+ sizeof(*dev->wed_rro.emi_rings_cpu.ptr),
+ dev->wed_rro.emi_rings_cpu.ptr,
+ dev->wed_rro.emi_rings_cpu.phy_addr);
+
+ if (dev->wed_rro.emi_rings_dma.ptr)
+ dmam_free_coherent(dev->mt76.dma_dev,
+ sizeof(*dev->wed_rro.emi_rings_dma.ptr),
+ dev->wed_rro.emi_rings_dma.ptr,
+ dev->wed_rro.emi_rings_dma.phy_addr);
+
if (!dev->wed_rro.session.ptr)
return;
@@ -1067,6 +1081,9 @@ static void mt7996_wed_rro_work(struct work_struct *work)
list);
list_del_init(&e->list);
+ if (mt76_npu_device_active(&dev->mt76))
+ goto reset_session;
+
for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) {
void *ptr = dev->wed_rro.session.ptr;
struct mt7996_wed_rro_addr *elem;
@@ -1087,6 +1104,7 @@ reset:
elem = ptr + elem_id * sizeof(*elem);
elem->data |= cpu_to_le32(val);
}
+reset_session:
mt7996_mcu_wed_rro_reset_sessions(dev, e->id);
out:
kfree(e);
@@ -1674,6 +1692,10 @@ int mt7996_register_device(struct mt7996_dev *dev)
if (ret)
return ret;
+ ret = mt7996_npu_hw_init(dev);
+ if (ret)
+ return ret;
+
ret = mt76_register_device(&dev->mt76, true, mt76_rates,
ARRAY_SIZE(mt76_rates));
if (ret)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index 9501def3e0e3..2560e2f46e89 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -718,6 +718,7 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q,
status->flag |= RX_FLAG_8023;
mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb,
*info);
+ mt76_npu_check_ppe(&dev->mt76, skb, *info);
}
if (rxv && !(status->flag & RX_FLAG_8023)) {
@@ -794,6 +795,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
__le16 fc = hdr->frame_control, sc = hdr->seq_ctrl;
u16 seqno = le16_to_cpu(sc);
+ bool hw_bigtk = false;
u8 fc_type, fc_stype;
u32 val;
@@ -819,7 +821,11 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi,
info->flags & IEEE80211_TX_CTL_USE_MINRATE)
val |= MT_TXD1_FIXED_RATE;
- if (key && multicast && ieee80211_is_robust_mgmt_frame(skb)) {
+ if (is_mt7990(&dev->mt76) && ieee80211_is_beacon(fc) &&
+ (wcid->hw_key_idx2 == 6 || wcid->hw_key_idx2 == 7))
+ hw_bigtk = true;
+
+ if ((key && multicast && ieee80211_is_robust_mgmt_frame(skb)) || hw_bigtk) {
val |= MT_TXD1_BIP;
txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME);
}
@@ -1034,15 +1040,20 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
struct ieee80211_sta *sta,
struct mt76_tx_info *tx_info)
{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data;
struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb);
struct ieee80211_key_conf *key = info->control.hw_key;
struct ieee80211_vif *vif = info->control.vif;
+ struct mt7996_vif *mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL;
+ struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL;
+ struct mt76_vif_link *mlink = NULL;
struct mt76_txwi_cache *t;
int id, i, pid, nbuf = tx_info->nbuf - 1;
bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP;
__le32 *ptr = (__le32 *)txwi_ptr;
u8 *txwi = (u8 *)txwi_ptr;
+ u8 link_id;
if (unlikely(tx_info->skb->len <= ETH_HLEN))
return -EINVAL;
@@ -1050,6 +1061,30 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
if (!wcid)
wcid = &dev->mt76.global_wcid;
+ if ((is_8023 || ieee80211_is_data_qos(hdr->frame_control)) && sta->mlo &&
+ likely(tx_info->skb->protocol != cpu_to_be16(ETH_P_PAE))) {
+ u8 tid = tx_info->skb->priority & IEEE80211_QOS_CTL_TID_MASK;
+
+ link_id = (tid % 2) ? msta->seclink_id : msta->deflink_id;
+ } else {
+ link_id = u32_get_bits(info->control.flags,
+ IEEE80211_TX_CTRL_MLO_LINK);
+ }
+
+ if (link_id != wcid->link_id && link_id != IEEE80211_LINK_UNSPECIFIED) {
+ if (msta) {
+ struct mt7996_sta_link *msta_link =
+ rcu_dereference(msta->link[link_id]);
+
+ if (msta_link)
+ wcid = &msta_link->wcid;
+ } else if (mvif) {
+ mlink = rcu_dereference(mvif->mt76.link[link_id]);
+ if (mlink && mlink->wcid)
+ wcid = mlink->wcid;
+ }
+ }
+
t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size);
t->skb = tx_info->skb;
@@ -1154,10 +1189,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
if (!is_8023 && mt7996_tx_use_mgmt(dev, tx_info->skb))
txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME);
- if (vif) {
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
- struct mt76_vif_link *mlink = NULL;
-
+ if (mvif) {
if (wcid->offchannel)
mlink = rcu_dereference(mvif->mt76.offchannel_link);
if (!mlink)
@@ -1681,8 +1713,7 @@ mt7996_msdu_page_get_from_cache(struct mt7996_dev *dev)
if (!list_empty(&dev->wed_rro.page_cache)) {
p = list_first_entry(&dev->wed_rro.page_cache,
struct mt7996_msdu_page, list);
- if (p)
- list_del(&p->list);
+ list_del(&p->list);
}
spin_unlock(&dev->wed_rro.lock);
@@ -2337,7 +2368,7 @@ mt7996_mac_restart(struct mt7996_dev *dev)
if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state))
continue;
- ret = mt7996_run(&dev->phy);
+ ret = mt7996_run(phy);
if (ret)
goto out;
}
@@ -2420,6 +2451,8 @@ mt7996_mac_full_reset(struct mt7996_dev *dev)
mt7996_for_each_phy(dev, phy)
cancel_delayed_work_sync(&phy->mt76->mac_work);
+ mt76_abort_scan(&dev->mt76);
+
mutex_lock(&dev->mt76.mutex);
for (i = 0; i < 10; i++) {
if (!mt7996_mac_restart(dev))
@@ -2536,6 +2569,8 @@ void mt7996_mac_reset_work(struct work_struct *work)
mutex_lock(&dev->mt76.mutex);
+ mt7996_npu_hw_stop(dev);
+
mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED);
if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) {
@@ -2551,7 +2586,7 @@ void mt7996_mac_reset_work(struct work_struct *work)
mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE);
mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE);
- /* enable DMA Tx/Tx and interrupt */
+ /* enable DMA Rx/Tx and interrupt */
mt7996_dma_start(dev, false, false);
if (!is_mt7996(&dev->mt76) && dev->mt76.hwrro_mode == MT76_HWRRO_V3)
@@ -2599,10 +2634,11 @@ void mt7996_mac_reset_work(struct work_struct *work)
local_bh_enable();
ieee80211_wake_queues(hw);
+ mt7996_update_beacons(dev);
mutex_unlock(&dev->mt76.mutex);
- mt7996_update_beacons(dev);
+ mt7996_npu_hw_init(dev);
mt7996_for_each_phy(dev, phy)
ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work,
@@ -2854,6 +2890,8 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
LIST_HEAD(list);
u32 changed;
+ mutex_lock(&dev->mt76.mutex);
+
spin_lock_bh(&dev->mt76.sta_poll_lock);
list_splice_init(&dev->sta_rc_list, &list);
@@ -2886,6 +2924,8 @@ void mt7996_mac_sta_rc_work(struct work_struct *work)
}
spin_unlock_bh(&dev->mt76.sta_poll_lock);
+
+ mutex_unlock(&dev->mt76.mutex);
}
void mt7996_mac_work(struct work_struct *work)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
index e629324a5617..4eca37b013fc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2022 MediaTek Inc.
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index 581314368c5b..beed795edb24 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -90,9 +90,11 @@ static void mt7996_stop(struct ieee80211_hw *hw, bool suspend)
{
}
-static inline int get_free_idx(u32 mask, u8 start, u8 end)
+static inline int get_free_idx(u64 mask, u8 start, u8 end)
{
- return ffs(~mask & GENMASK(end, start));
+ if (~mask & GENMASK_ULL(end, start))
+ return __ffs64(~mask & GENMASK_ULL(end, start)) + 1;
+ return 0;
}
static int get_omac_idx(enum nl80211_iftype type, u64 mask)
@@ -247,12 +249,13 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
else if (idx == *wcid_keyidx)
*wcid_keyidx = -1;
- if (cmd != SET_KEY && sta)
+ /* only do remove key for BIGTK */
+ if (cmd != SET_KEY && !is_bigtk)
return 0;
mt76_wcid_key_setup(&dev->mt76, &msta_link->wcid, key);
- err = mt7996_mcu_add_key(&dev->mt76, vif, key,
+ err = mt7996_mcu_add_key(&dev->mt76, link, key,
MCU_WMWA_UNI_CMD(STA_REC_UPDATE),
&msta_link->wcid, cmd);
@@ -308,12 +311,6 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
if (idx < 0)
return -ENOSPC;
- if (!dev->mld_idx_mask) { /* first link in the group */
- mvif->mld_group_idx = get_own_mld_idx(dev->mld_idx_mask, true);
- mvif->mld_remap_idx = get_free_idx(dev->mld_remap_idx_mask,
- 0, 15);
- }
-
mld_idx = get_own_mld_idx(dev->mld_idx_mask, false);
if (mld_idx < 0)
return -ENOSPC;
@@ -331,10 +328,6 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
return ret;
dev->mt76.vif_mask |= BIT_ULL(mlink->idx);
- if (!dev->mld_idx_mask) {
- dev->mld_idx_mask |= BIT_ULL(mvif->mld_group_idx);
- dev->mld_remap_idx_mask |= BIT_ULL(mvif->mld_remap_idx);
- }
dev->mld_idx_mask |= BIT_ULL(link->mld_idx);
phy->omac_mask |= BIT_ULL(mlink->omac_idx);
@@ -343,6 +336,7 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
INIT_LIST_HEAD(&msta_link->rc_list);
msta_link->wcid.idx = idx;
msta_link->wcid.link_id = link_conf->link_id;
+ msta_link->wcid.link_valid = ieee80211_vif_is_mld(vif);
msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET;
mt76_wcid_init(&msta_link->wcid, band_idx);
@@ -376,7 +370,8 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif,
ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it);
- if (mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED)
+ if (!mlink->wcid->offchannel &&
+ mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED)
mvif->mt76.deflink_id = link_conf->link_id;
return 0;
@@ -397,7 +392,8 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
};
int idx = msta_link->wcid.idx;
- ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it);
+ if (!mlink->wcid->offchannel)
+ ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it);
mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL,
CONN_STATE_DISCONNECT, false);
@@ -407,7 +403,8 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
- if (mvif->mt76.deflink_id == link_conf->link_id) {
+ if (!mlink->wcid->offchannel &&
+ mvif->mt76.deflink_id == link_conf->link_id) {
struct ieee80211_bss_conf *iter;
unsigned int link_id;
@@ -423,11 +420,6 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif,
dev->mt76.vif_mask &= ~BIT_ULL(mlink->idx);
dev->mld_idx_mask &= ~BIT_ULL(link->mld_idx);
phy->omac_mask &= ~BIT_ULL(mlink->omac_idx);
- if (!(dev->mld_idx_mask & ~BIT_ULL(mvif->mld_group_idx))) {
- /* last link */
- dev->mld_idx_mask &= ~BIT_ULL(mvif->mld_group_idx);
- dev->mld_remap_idx_mask &= ~BIT_ULL(mvif->mld_remap_idx);
- }
spin_lock_bh(&dev->mt76.sta_poll_lock);
if (!list_empty(&msta_link->wcid.poll_list))
@@ -665,8 +657,8 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
unsigned int link_id, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
- struct mt7996_dev *dev = mt7996_hw_dev(hw);
- struct mt7996_vif_link *mlink = mt7996_vif_link(dev, vif, link_id);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ struct mt7996_vif_link_info *link_info = &mvif->link_info[link_id];
static const u8 mq_to_aci[] = {
[IEEE80211_AC_VO] = 3,
[IEEE80211_AC_VI] = 2,
@@ -675,7 +667,7 @@ mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
};
/* firmware uses access class index */
- mlink->queue_params[mq_to_aci[queue]] = *params;
+ link_info->queue_params[mq_to_aci[queue]] = *params;
/* no need to update right away, we'll get BSS_CHANGED_QOS */
return 0;
@@ -962,6 +954,7 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
msta_link = &msta->deflink;
msta->deflink_id = link_id;
+ msta->seclink_id = msta->deflink_id;
for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
struct mt76_txq *mtxq;
@@ -976,6 +969,11 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
msta_link = kzalloc(sizeof(*msta_link), GFP_KERNEL);
if (!msta_link)
return -ENOMEM;
+
+ if (msta->seclink_id == msta->deflink_id &&
+ (sta->valid_links & ~BIT(msta->deflink_id)))
+ msta->seclink_id = __ffs(sta->valid_links &
+ ~BIT(msta->deflink_id));
}
INIT_LIST_HEAD(&msta_link->rc_list);
@@ -984,6 +982,7 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
msta_link->wcid.sta = 1;
msta_link->wcid.idx = idx;
msta_link->wcid.link_id = link_id;
+ msta_link->wcid.link_valid = !!sta->valid_links;
msta_link->wcid.def_wcid = &msta->deflink.wcid;
ewma_avg_signal_init(&msta_link->avg_ack_signal);
@@ -1049,6 +1048,8 @@ mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif,
if (msta->deflink_id == link_id) {
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
continue;
+ } else if (msta->seclink_id == link_id) {
+ msta->seclink_id = IEEE80211_LINK_UNSPECIFIED;
}
kfree_rcu(msta_link, rcu_head);
@@ -1144,6 +1145,7 @@ mt7996_mac_sta_add(struct mt7996_dev *dev, struct ieee80211_vif *vif,
mutex_lock(&dev->mt76.mutex);
msta->deflink_id = IEEE80211_LINK_UNSPECIFIED;
+ msta->seclink_id = IEEE80211_LINK_UNSPECIFIED;
msta->vif = mvif;
err = mt7996_mac_sta_add_links(dev, vif, sta, links);
@@ -1160,12 +1162,15 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
unsigned long links = sta->valid_links;
struct ieee80211_link_sta *link_sta;
unsigned int link_id;
+ int err = 0;
+
+ mutex_lock(&dev->mt76.mutex);
for_each_sta_active_link(vif, sta, link_sta, link_id) {
struct ieee80211_bss_conf *link_conf;
struct mt7996_sta_link *msta_link;
struct mt7996_vif_link *link;
- int i, err;
+ int i;
link_conf = link_conf_dereference_protected(vif, link_id);
if (!link_conf)
@@ -1185,12 +1190,12 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
link, msta_link,
CONN_STATE_CONNECT, true);
if (err)
- return err;
+ goto unlock;
err = mt7996_mcu_add_rate_ctrl(dev, msta_link->sta, vif,
link_id, false);
if (err)
- return err;
+ goto unlock;
msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET;
break;
@@ -1199,28 +1204,30 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
link, msta_link,
CONN_STATE_PORT_SECURE, false);
if (err)
- return err;
+ goto unlock;
break;
case MT76_STA_EVENT_DISASSOC:
for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++)
mt7996_mac_twt_teardown_flow(dev, link,
msta_link, i);
- if (sta->mlo && links == BIT(link_id)) /* last link */
- mt7996_mcu_teardown_mld_sta(dev, link,
- msta_link);
- else
+ if (!sta->mlo)
mt7996_mcu_add_sta(dev, link_conf, link_sta,
link, msta_link,
CONN_STATE_DISCONNECT, false);
+ else if (sta->mlo && links == BIT(link_id)) /* last link */
+ mt7996_mcu_teardown_mld_sta(dev, link,
+ msta_link);
msta_link->wcid.sta_disabled = 1;
msta_link->wcid.sta = 0;
links = links & ~BIT(link_id);
break;
}
}
+unlock:
+ mutex_unlock(&dev->mt76.mutex);
- return 0;
+ return err;
}
static void
@@ -1339,12 +1346,10 @@ static void mt7996_tx(struct ieee80211_hw *hw,
}
if (mvif) {
- struct mt76_vif_link *mlink = &mvif->deflink.mt76;
+ struct mt76_vif_link *mlink;
- if (link_id < IEEE80211_LINK_UNSPECIFIED)
- mlink = rcu_dereference(mvif->mt76.link[link_id]);
-
- if (mlink->wcid)
+ mlink = rcu_dereference(mvif->mt76.link[link_id]);
+ if (mlink && mlink->wcid)
wcid = mlink->wcid;
if (mvif->mt76.roc_phy &&
@@ -1352,7 +1357,7 @@ static void mt7996_tx(struct ieee80211_hw *hw,
mphy = mvif->mt76.roc_phy;
if (mphy->roc_link)
wcid = mphy->roc_link->wcid;
- } else {
+ } else if (mlink) {
mphy = mt76_vif_link_phy(mlink);
}
}
@@ -1362,7 +1367,7 @@ static void mt7996_tx(struct ieee80211_hw *hw,
goto unlock;
}
- if (msta && link_id < IEEE80211_LINK_UNSPECIFIED) {
+ if (msta) {
struct mt7996_sta_link *msta_link;
msta_link = rcu_dereference(msta->link[link_id]);
@@ -2159,7 +2164,6 @@ out:
return ret;
}
-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
static int
mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -2167,15 +2171,14 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
struct net_device_path_ctx *ctx,
struct net_device_path *path)
{
- struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
struct mt7996_dev *dev = mt7996_hw_dev(hw);
struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
struct mt7996_sta_link *msta_link;
- struct mt76_vif_link *mlink;
+ struct mt7996_vif_link *link;
- mlink = rcu_dereference(mvif->mt76.link[msta->deflink_id]);
- if (!mlink)
+ link = mt7996_vif_link(dev, vif, msta->deflink_id);
+ if (!link)
return -EIO;
msta_link = rcu_dereference(msta->link[msta->deflink_id]);
@@ -2190,13 +2193,19 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
(is_mt7992(&dev->mt76) && msta_link->wcid.phy_idx == MT_BAND1)))
wed = &dev->mt76.mmio.wed_hif2;
- if (!mtk_wed_device_active(wed))
+ if (!mtk_wed_device_active(wed) &&
+ !mt76_npu_device_active(&dev->mt76))
return -ENODEV;
path->type = DEV_PATH_MTK_WDMA;
path->dev = ctx->dev;
- path->mtk_wdma.wdma_idx = wed->wdma_idx;
- path->mtk_wdma.bss = mlink->idx;
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
+ if (mtk_wed_device_active(wed))
+ path->mtk_wdma.wdma_idx = wed->wdma_idx;
+ else
+#endif
+ path->mtk_wdma.wdma_idx = link->mt76.band_idx;
+ path->mtk_wdma.bss = link->mt76.idx;
path->mtk_wdma.queue = 0;
path->mtk_wdma.wcid = msta_link->wcid.idx;
@@ -2210,14 +2219,47 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
return 0;
}
-#endif
-
static int
mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u16 old_links, u16 new_links,
struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS])
{
- return 0;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ int ret = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ if (!old_links) {
+ int idx;
+
+ idx = get_own_mld_idx(dev->mld_idx_mask, true);
+ if (idx < 0) {
+ ret = -ENOSPC;
+ goto out;
+ }
+ mvif->mld_group_idx = idx;
+ dev->mld_idx_mask |= BIT_ULL(mvif->mld_group_idx);
+
+ idx = get_free_idx(dev->mld_remap_idx_mask, 0, 15) - 1;
+ if (idx < 0) {
+ ret = -ENOSPC;
+ goto out;
+ }
+ mvif->mld_remap_idx = idx;
+ dev->mld_remap_idx_mask |= BIT_ULL(mvif->mld_remap_idx);
+ }
+
+ if (new_links)
+ goto out;
+
+ dev->mld_idx_mask &= ~BIT_ULL(mvif->mld_group_idx);
+ dev->mld_remap_idx_mask &= ~BIT_ULL(mvif->mld_remap_idx);
+
+out:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return ret;
}
static void
@@ -2283,11 +2325,14 @@ const struct ieee80211_ops mt7996_ops = {
.twt_teardown_request = mt7996_twt_teardown_request,
#ifdef CONFIG_MAC80211_DEBUGFS
.sta_add_debugfs = mt7996_sta_add_debugfs,
+ .link_sta_add_debugfs = mt7996_link_sta_add_debugfs,
#endif
.set_radar_background = mt7996_set_radar_background,
-#ifdef CONFIG_NET_MEDIATEK_SOC_WED
.net_fill_forward_path = mt7996_net_fill_forward_path,
+#ifdef CONFIG_NET_MEDIATEK_SOC_WED
.net_setup_tc = mt76_wed_net_setup_tc,
+#elif defined(CONFIG_MT7996_NPU)
+ .net_setup_tc = mt76_npu_net_setup_tc,
#endif
.change_vif_links = mt7996_change_vif_links,
.change_sta_links = mt7996_mac_sta_change_links,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 0347ee0c2dd7..14a88ef79b6c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -318,6 +318,9 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
else
uni_txd->option = MCU_CMD_UNI_EXT_ACK;
+ if (mcu_cmd == MCU_UNI_CMD_SDO)
+ uni_txd->option &= ~MCU_CMD_ACK;
+
if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM))
uni_txd->s2d_index = MCU_S2D_H2CN;
else if (cmd & __MCU_CMD_FIELD_WA)
@@ -1034,7 +1037,6 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
struct mt76_connac_bss_basic_tlv *bss;
u32 type = CONNECTION_INFRA_AP;
u16 sta_wlan_idx = wlan_idx;
- struct ieee80211_sta *sta;
struct tlv *tlv;
int idx;
@@ -1045,14 +1047,18 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
break;
case NL80211_IFTYPE_STATION:
if (enable) {
+ struct ieee80211_sta *sta;
+
rcu_read_lock();
- sta = ieee80211_find_sta(vif, vif->bss_conf.bssid);
- /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */
+ sta = ieee80211_find_sta(vif, link_conf->bssid);
if (sta) {
- struct mt76_wcid *wcid;
+ struct mt7996_sta *msta = (void *)sta->drv_priv;
+ struct mt7996_sta_link *msta_link;
+ int link_id = link_conf->link_id;
- wcid = (struct mt76_wcid *)sta->drv_priv;
- sta_wlan_idx = wcid->idx;
+ msta_link = rcu_dereference(msta->link[link_id]);
+ if (msta_link)
+ sta_wlan_idx = msta_link->wcid.idx;
}
rcu_read_unlock();
}
@@ -1069,8 +1075,6 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss));
bss = (struct mt76_connac_bss_basic_tlv *)tlv;
- bss->bcn_interval = cpu_to_le16(link_conf->beacon_int);
- bss->dtim_period = link_conf->dtim_period;
bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx);
bss->sta_idx = cpu_to_le16(sta_wlan_idx);
bss->conn_type = cpu_to_le32(type);
@@ -1090,10 +1094,10 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
memcpy(bss->bssid, link_conf->bssid, ETH_ALEN);
bss->bcn_interval = cpu_to_le16(link_conf->beacon_int);
- bss->dtim_period = vif->bss_conf.dtim_period;
+ bss->dtim_period = link_conf->dtim_period;
bss->phymode = mt76_connac_get_phy_mode(phy, vif,
chandef->chan->band, NULL);
- bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, &vif->bss_conf,
+ bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, link_conf,
chandef->chan->band);
return 0;
@@ -1822,8 +1826,8 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
bf->ibf_nrow = tx_ant;
if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he)
- bf->ibf_timeout = is_mt7996(&dev->mt76) ? MT7996_IBF_TIMEOUT :
- MT7992_IBF_TIMEOUT;
+ bf->ibf_timeout = is_mt7992(&dev->mt76) ? MT7992_IBF_TIMEOUT :
+ MT7996_IBF_TIMEOUT;
else if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
bf->ibf_timeout = MT7996_IBF_TIMEOUT_LEGACY;
else
@@ -2390,8 +2394,8 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx);
if (nlinks > 1) {
- link_id = __ffs(sta->valid_links & ~BIT(msta->deflink_id));
- msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+ msta_link = mt76_dereference(msta->link[msta->seclink_id],
+ &dev->mt76);
if (!msta_link)
return;
}
@@ -2526,7 +2530,7 @@ int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev,
}
static int
-mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
+mt7996_mcu_sta_key_tlv(struct mt76_dev *dev, struct mt76_wcid *wcid,
struct sk_buff *skb,
struct ieee80211_key_conf *key,
enum set_key_cmd cmd)
@@ -2538,7 +2542,10 @@ mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec));
sec = (struct sta_rec_sec_uni *)tlv;
- sec->add = 0;
+ /* due to connac3 FW design, we only do remove key for BIGTK; even for
+ * removal, the field should be filled with SET_KEY
+ */
+ sec->add = SET_KEY;
sec->n_cipher = 1;
sec_key = &sec->key[0];
sec_key->wlan_idx = cpu_to_le16(wcid->idx);
@@ -2578,29 +2585,33 @@ mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid,
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
sec_key->cipher_id = MCU_CIPHER_BCN_PROT_GMAC_256;
break;
+ case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+ if (!is_mt7990(dev))
+ return -EOPNOTSUPP;
+ sec_key->cipher_id = MCU_CIPHER_BCN_PROT_CMAC_256;
+ break;
default:
return -EOPNOTSUPP;
}
- sec_key->bcn_mode = BP_SW_MODE;
+ sec_key->bcn_mode = is_mt7990(dev) ? BP_HW_MODE : BP_SW_MODE;
return 0;
}
-int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_vif_link *link,
struct ieee80211_key_conf *key, int mcu_cmd,
struct mt76_wcid *wcid, enum set_key_cmd cmd)
{
- struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv;
struct sk_buff *skb;
int ret;
- skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid,
- MT7996_STA_UPDATE_MAX_SIZE);
+ skb = __mt76_connac_mcu_alloc_sta_req(dev, (struct mt76_vif_link *)link,
+ wcid, MT7996_STA_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
- ret = mt7996_mcu_sta_key_tlv(wcid, skb, key, cmd);
+ ret = mt7996_mcu_sta_key_tlv(dev, wcid, skb, key, cmd);
if (ret) {
dev_kfree_skb(skb);
return ret;
@@ -2720,12 +2731,18 @@ mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
static void
mt7996_mcu_beacon_cont(struct mt7996_dev *dev,
struct ieee80211_bss_conf *link_conf,
+ struct mt7996_vif_link *link,
struct sk_buff *rskb, struct sk_buff *skb,
struct bss_bcn_content_tlv *bcn,
struct ieee80211_mutable_offsets *offs)
{
- struct mt76_wcid *wcid = &dev->mt76.global_wcid;
- u8 *buf;
+ u8 *buf, keyidx = link->msta_link.wcid.hw_key_idx2;
+ struct mt76_wcid *wcid;
+
+ if (is_mt7990(&dev->mt76) && (keyidx == 6 || keyidx == 7))
+ wcid = &link->msta_link.wcid;
+ else
+ wcid = &dev->mt76.global_wcid;
bcn->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
bcn->tim_ie_pos = cpu_to_le16(offs->tim_offset);
@@ -2800,7 +2817,7 @@ int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
info = IEEE80211_SKB_CB(skb);
info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, mlink->band_idx);
- mt7996_mcu_beacon_cont(dev, link_conf, rskb, skb, bcn, &offs);
+ mt7996_mcu_beacon_cont(dev, link_conf, link, rskb, skb, bcn, &offs);
if (link_conf->bssid_indicator)
mt7996_mcu_beacon_mbss(rskb, skb, bcn, &offs);
mt7996_mcu_beacon_cntdwn(rskb, skb, &offs, link_conf->csa_active);
@@ -3414,6 +3431,9 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif,
#define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \
WMM_CW_MAX_SET | WMM_TXOP_SET)
struct mt7996_vif_link *link = mt7996_vif_conf_link(dev, vif, link_conf);
+ struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv;
+ unsigned int link_id = link_conf->link_id;
+ struct mt7996_vif_link_info *link_info = &mvif->link_info[link_id];
struct {
u8 bss_idx;
u8 __rsv[3];
@@ -3431,7 +3451,7 @@ int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif,
skb_put_data(skb, &hdr, sizeof(hdr));
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
- struct ieee80211_tx_queue_params *q = &link->queue_params[ac];
+ struct ieee80211_tx_queue_params *q = &link_info->queue_params[ac];
struct edca *e;
struct tlv *tlv;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
index c841da1c60e5..e0b83ac9f5e2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2022 MediaTek Inc.
*/
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
index d14b626ee511..d9780bb425a7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -595,6 +595,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE;
wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf;
+ wed->wlan.hif2 = hif2;
wed->wlan.amsdu_max_subframes = 8;
wed->wlan.amsdu_max_len = 1536;
@@ -706,9 +707,18 @@ void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg,
static void mt7996_rx_poll_complete(struct mt76_dev *mdev,
enum mt76_rxq_id q)
{
- struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76);
+ if (q == MT_RXQ_NPU0 || q == MT_RXQ_NPU1) {
+ struct airoha_npu *npu;
+
+ npu = rcu_dereference(mdev->mmio.npu);
+ if (npu)
+ airoha_npu_wlan_enable_irq(npu, q - MT_RXQ_NPU0);
+ } else {
+ struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev,
+ mt76);
- mt7996_irq_enable(dev, MT_INT_RX(q));
+ mt7996_irq_enable(dev, MT_INT_RX(q));
+ }
}
/* TODO: support 2/4/6/8 MSI-X vectors */
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
index 8ec2acdb3319..7a884311800e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -243,6 +243,7 @@ struct mt7996_sta {
struct mt7996_sta_link deflink; /* must be first */
struct mt7996_sta_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
u8 deflink_id;
+ u8 seclink_id;
struct mt7996_vif *vif;
};
@@ -253,16 +254,21 @@ struct mt7996_vif_link {
struct mt7996_sta_link msta_link;
struct mt7996_phy *phy;
- struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
struct cfg80211_bitrate_mask bitrate_mask;
u8 mld_idx;
};
+struct mt7996_vif_link_info {
+ struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
+};
+
struct mt7996_vif {
struct mt7996_vif_link deflink; /* must be first */
struct mt76_vif_data mt76;
+ struct mt7996_vif_link_info link_info[IEEE80211_MLD_MAX_NUM_LINKS];
+
u8 mld_group_idx;
u8 mld_remap_idx;
};
@@ -781,7 +787,7 @@ void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset,
static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy)
{
- int max_nss = hweight8(phy->mt76->hw->wiphy->available_antennas_tx);
+ int max_nss = hweight16(phy->orig_antenna_mask);
int cur_nss = hweight8(phy->mt76->antenna_mask);
u16 tx_chainmask = phy->mt76->chainmask;
@@ -843,7 +849,7 @@ void mt7996_update_channel(struct mt76_phy *mphy);
int mt7996_init_debugfs(struct mt7996_dev *dev);
void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len);
bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len);
-int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif,
+int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_vif_link *link,
struct ieee80211_key_conf *key, int mcu_cmd,
struct mt76_wcid *wcid, enum set_key_cmd cmd);
int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev,
@@ -858,6 +864,9 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode);
#ifdef CONFIG_MAC80211_DEBUGFS
void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct dentry *dir);
+void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_link_sta *link_sta,
+ struct dentry *dir);
#endif
int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr,
bool hif2, int *irq);
@@ -869,4 +878,25 @@ int mt7996_mtk_init_debugfs(struct mt7996_phy *phy, struct dentry *dir);
int mt7996_dma_rro_init(struct mt7996_dev *dev);
+#ifdef CONFIG_MT7996_NPU
+int mt7996_npu_hw_init(struct mt7996_dev *dev);
+int mt7996_npu_hw_stop(struct mt7996_dev *dev);
+int mt7996_npu_rx_queues_init(struct mt7996_dev *dev);
+#else
+static inline int mt7996_npu_hw_init(struct mt7996_dev *dev)
+{
+ return 0;
+}
+
+static inline int mt7996_npu_hw_stop(struct mt7996_dev *dev)
+{
+ return 0;
+}
+
+static inline int mt7996_npu_rx_queues_init(struct mt7996_dev *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_MT7996_NPU */
+
#endif
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
new file mode 100644
index 000000000000..29bb735da4cb
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 AIROHA Inc
+ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
+ */
+#include <linux/kernel.h>
+#include <linux/soc/airoha/airoha_offload.h>
+
+#include "mt7996.h"
+
+static int mt7996_npu_offload_init(struct mt7996_dev *dev,
+ struct airoha_npu *npu)
+{
+ phys_addr_t phy_addr = dev->mt76.mmio.phy_addr;
+ u32 val, hif1_ofs = 0, dma_addr;
+ int i, err;
+
+ err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev, "failed getting NPU fw version\n");
+ return err;
+ }
+
+ dev_info(dev->mt76.dev, "NPU version: %0d.%d\n",
+ (val >> 16) & 0xffff, val & 0xffff);
+
+ err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE,
+ dev->mt76.mmio.npu_type, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe port type\n");
+ return err;
+ }
+
+ if (dev->hif2)
+ hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0);
+
+ for (i = MT_BAND0; i < MT_BAND2; i++) {
+ dma_addr = phy_addr;
+ if (i)
+ dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + 0x90 +
+ hif1_ofs;
+ else
+ dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0) + 0x80;
+
+ err = mt76_npu_send_msg(npu, i, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc addr\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, i, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_RX_RING_SIZE, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan PCIe desc size\n");
+ return err;
+ }
+
+ dma_addr = phy_addr;
+ if (i)
+ dma_addr += MT_TXQ_RING_BASE(0) + 0x150 + hif1_ofs;
+ else
+ dma_addr += MT_TXQ_RING_BASE(0) + 0x120;
+
+ err = mt76_npu_send_msg(npu, i,
+ WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan tx desc addr\n");
+ return err;
+ }
+ }
+
+ err = mt76_npu_send_msg(npu, 9, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ phy_addr + MT_RXQ_RRO_AP_RING_BASE,
+ GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rxdmad_c addr\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 9, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_RX_RING_SIZE, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rxdmad_c desc size\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR,
+ phy_addr + MT_RRO_ACK_SN_CTRL, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rro_ack_sn desc addr\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE,
+ MT7996_HW_TOKEN_SIZE, GFP_KERNEL);
+ if (err)
+ return err;
+
+ dev->mt76.token_start = MT7996_HW_TOKEN_SIZE;
+
+ return 0;
+}
+
+static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
+{
+ u32 val;
+ int err;
+
+ err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan rx ring0 addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base);
+
+ err = mt76_npu_get_msg(npu, 1, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan rx ring1 addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1].regs->desc_base);
+
+ err = mt76_npu_get_msg(npu, 9, WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan rxdmad_c ring addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_RXDMAD_C].regs->desc_base);
+
+ return 0;
+}
+
+static int mt7996_npu_txd_init(struct mt7996_dev *dev, struct airoha_npu *npu)
+{
+ int i, err;
+
+ for (i = MT_BAND0; i < MT_BAND2; i++) {
+ dma_addr_t dma_addr;
+ u32 val;
+
+ err = mt76_npu_get_msg(npu, i + 5,
+ WLAN_FUNC_GET_WAIT_RXDESC_BASE,
+ &val, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed retriving NPU wlan tx ring addr\n");
+ return err;
+ }
+ writel(val, &dev->mt76.phys[i]->q_tx[0]->regs->desc_base);
+
+ if (!dmam_alloc_coherent(dev->mt76.dma_dev,
+ 256 * MT7996_TX_RING_SIZE,
+ &dma_addr, GFP_KERNEL))
+ return -ENOMEM;
+
+ err = mt76_npu_send_msg(npu, i,
+ WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan queue buf addr\n");
+ return err;
+ }
+
+ if (!dmam_alloc_coherent(dev->mt76.dma_dev,
+ 256 * MT7996_TX_RING_SIZE,
+ &dma_addr, GFP_KERNEL))
+ return -ENOMEM;
+
+ err = mt76_npu_send_msg(npu, i + 5,
+ WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan tx buf addr\n");
+ return err;
+ }
+
+ if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * 1024,
+ &dma_addr, GFP_KERNEL))
+ return -ENOMEM;
+
+ err = mt76_npu_send_msg(npu, i + 10,
+ WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE,
+ dma_addr, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan tx buf base\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int mt7996_npu_rx_event_init(struct mt7996_dev *dev,
+ struct airoha_npu *npu)
+{
+ struct mt76_queue *q = &dev->mt76.q_rx[MT_RXQ_MAIN_WA];
+ phys_addr_t phy_addr = dev->mt76.mmio.phy_addr;
+ int err;
+
+ err = mt76_npu_send_msg(npu, 0,
+ WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE,
+ q->desc_dma, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan tx-done ring\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 10, WLAN_FUNC_SET_WAIT_DESC,
+ MT7996_RX_MCU_RING_SIZE, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan descriptors\n");
+ return err;
+ }
+
+ phy_addr += MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA) + 0x20;
+ err = mt76_npu_send_msg(npu, 10, WLAN_FUNC_SET_WAIT_PCIE_ADDR,
+ phy_addr, GFP_KERNEL);
+ if (err)
+ dev_warn(dev->mt76.dev,
+ "failed setting NPU wlan rx pcie address\n");
+ return err;
+}
+
+static int mt7996_npu_tx_done_init(struct mt7996_dev *dev,
+ struct airoha_npu *npu)
+{
+ int err;
+
+ err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
+ 0, GFP_KERNEL);
+ if (err) {
+ dev_warn(dev->mt76.dev, "failed setting NPU wlan txrx addr2\n");
+ return err;
+ }
+
+ err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
+ 0, GFP_KERNEL);
+ if (err)
+ dev_warn(dev->mt76.dev, "failed setting NPU wlan txrx addr7\n");
+
+ return err;
+}
+
+int mt7996_npu_rx_queues_init(struct mt7996_dev *dev)
+{
+ int err;
+
+ if (!mt76_npu_device_active(&dev->mt76))
+ return 0;
+
+ err = mt76_npu_rx_queue_init(&dev->mt76,
+ &dev->mt76.q_rx[MT_RXQ_NPU0]);
+ if (err)
+ return err;
+
+ return mt76_npu_rx_queue_init(&dev->mt76,
+ &dev->mt76.q_rx[MT_RXQ_NPU1]);
+}
+
+int mt7996_npu_hw_init(struct mt7996_dev *dev)
+{
+ struct airoha_npu *npu;
+ int i, err = 0;
+
+ mutex_lock(&dev->mt76.mutex);
+
+ npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex);
+ if (!npu)
+ goto unlock;
+
+ err = mt7996_npu_offload_init(dev, npu);
+ if (err)
+ goto unlock;
+
+ err = mt7996_npu_rxd_init(dev, npu);
+ if (err)
+ goto unlock;
+
+ err = mt7996_npu_txd_init(dev, npu);
+ if (err)
+ goto unlock;
+
+ err = mt7996_npu_rx_event_init(dev, npu);
+ if (err)
+ goto unlock;
+
+ err = mt7996_npu_tx_done_init(dev, npu);
+ if (err)
+ goto unlock;
+
+ for (i = MT_RXQ_NPU0; i <= MT_RXQ_NPU1; i++)
+ airoha_npu_wlan_enable_irq(npu, i - MT_RXQ_NPU0);
+unlock:
+ mutex_unlock(&dev->mt76.mutex);
+
+ return err;
+}
+
+int mt7996_npu_hw_stop(struct mt7996_dev *dev)
+{
+ struct airoha_npu *npu;
+ int i, err;
+ u32 info;
+
+ npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex);
+ if (!npu)
+ return 0;
+
+ err = mt76_npu_send_msg(npu, 4, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
+ 0, GFP_KERNEL);
+ if (err)
+ return err;
+
+ for (i = 0; i < 10; i++) {
+ err = mt76_npu_get_msg(npu, 3, WLAN_FUNC_GET_WAIT_NPU_INFO,
+ &info, GFP_KERNEL);
+ if (err)
+ continue;
+
+ if (info) {
+ err = -ETIMEDOUT;
+ continue;
+ }
+ }
+
+ if (!err)
+ err = mt76_npu_send_msg(npu, 6,
+ WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR,
+ 0, GFP_KERNEL);
+ return err;
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
index 3f49bbbba3b9..12523ddba630 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2022 MediaTek Inc.
*/
@@ -140,6 +140,9 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
hif2 = mt7996_pci_init_hif2(pdev);
dev->hif2 = hif2;
+ mt76_npu_init(mdev, pci_resource_start(pdev, 0),
+ pdev->bus && pci_domain_nr(pdev->bus) ? 3 : 2);
+
ret = mt7996_mmio_wed_init(dev, pdev, false, &irq);
if (ret < 0)
goto free_wed_or_irq_vector;
@@ -158,7 +161,7 @@ static int mt7996_pci_probe(struct pci_dev *pdev,
goto free_wed_or_irq_vector;
mt76_wr(dev, MT_INT_MASK_CSR, 0);
- /* master switch of PCIe tnterrupt enable */
+ /* master switch of PCIe interrupt enable */
mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff);
if (hif2) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
index 0fa325f87fcd..e48e0e575b64 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2022 MediaTek Inc.
*/
diff --git a/drivers/net/wireless/mediatek/mt76/npu.c b/drivers/net/wireless/mediatek/mt76/npu.c
new file mode 100644
index 000000000000..ec36975f6dc9
--- /dev/null
+++ b/drivers/net/wireless/mediatek/mt76/npu.c
@@ -0,0 +1,501 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 AIROHA Inc
+ * Author: Lorenzo Bianconi <lorenzo@kernel.org>
+ */
+#include <linux/kernel.h>
+#include <net/flow_offload.h>
+#include <net/pkt_cls.h>
+
+#include "mt76.h"
+#include "dma.h"
+#include "mt76_connac.h"
+
+#define MT76_NPU_RX_BUF_SIZE (1800 + \
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
+
+int mt76_npu_fill_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ int nframes = 0;
+
+ while (q->queued < q->ndesc - 1) {
+ struct airoha_npu_rx_dma_desc *desc = (void *)q->desc;
+ struct mt76_queue_entry *e = &q->entry[q->head];
+ struct page *page;
+ int offset;
+
+ e->buf = mt76_get_page_pool_buf(q, &offset, q->buf_size);
+ if (!e->buf)
+ break;
+
+ e->dma_len[0] = SKB_WITH_OVERHEAD(q->buf_size);
+ page = virt_to_head_page(e->buf);
+ e->dma_addr[0] = page_pool_get_dma_addr(page) + offset;
+
+ memset(&desc[q->head], 0, sizeof(*desc));
+ desc[q->head].addr = e->dma_addr[0];
+
+ q->head = (q->head + 1) % q->ndesc;
+ q->queued++;
+ nframes++;
+ }
+
+ return nframes;
+}
+
+void mt76_npu_queue_cleanup(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ spin_lock_bh(&q->lock);
+ while (q->queued > 0) {
+ struct mt76_queue_entry *e = &q->entry[q->tail];
+
+ dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
+ e->dma_len[0],
+ page_pool_get_dma_dir(q->page_pool));
+ mt76_put_page_pool_buf(e->buf, false);
+ q->tail = (q->tail + 1) % q->ndesc;
+ q->queued--;
+ }
+ spin_unlock_bh(&q->lock);
+}
+
+static struct sk_buff *mt76_npu_dequeue(struct mt76_dev *dev,
+ struct mt76_queue *q,
+ u32 *info)
+{
+ struct airoha_npu_rx_dma_desc *desc = (void *)q->desc;
+ int i, nframes, index = q->tail;
+ struct sk_buff *skb = NULL;
+
+ nframes = FIELD_GET(NPU_RX_DMA_PKT_COUNT_MASK, desc[index].info);
+ nframes = max_t(int, nframes, 1);
+
+ for (i = 0; i < nframes; i++) {
+ struct mt76_queue_entry *e = &q->entry[index];
+ int len = FIELD_GET(NPU_RX_DMA_DESC_CUR_LEN_MASK,
+ desc[index].ctrl);
+
+ if (!FIELD_GET(NPU_RX_DMA_DESC_DONE_MASK, desc[index].ctrl)) {
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+
+ dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0],
+ e->dma_len[0],
+ page_pool_get_dma_dir(q->page_pool));
+
+ if (!skb) {
+ skb = napi_build_skb(e->buf, q->buf_size);
+ if (!skb)
+ return NULL;
+
+ __skb_put(skb, len);
+ skb_reset_mac_header(skb);
+ skb_mark_for_recycle(skb);
+ } else {
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+ struct page *page = virt_to_head_page(e->buf);
+ int nr_frags = shinfo->nr_frags;
+
+ if (nr_frags < ARRAY_SIZE(shinfo->frags))
+ skb_add_rx_frag(skb, nr_frags, page,
+ e->buf - page_address(page),
+ len, q->buf_size);
+ }
+
+ *info = desc[index].info;
+ index = (index + 1) % q->ndesc;
+ }
+ q->tail = index;
+ q->queued -= i;
+ Q_WRITE(q, dma_idx, q->tail);
+
+ return skb;
+}
+
+void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb,
+ u32 info)
+{
+ struct airoha_ppe_dev *ppe_dev;
+ u16 reason, hash;
+
+ if (!mt76_npu_device_active(dev))
+ return;
+
+ rcu_read_lock();
+
+ ppe_dev = rcu_dereference(dev->mmio.ppe_dev);
+ if (!ppe_dev)
+ goto out;
+
+ hash = FIELD_GET(NPU_RX_DMA_FOE_ID_MASK, info);
+ skb_set_hash(skb, hash, PKT_HASH_TYPE_L4);
+
+ reason = FIELD_GET(NPU_RX_DMA_CRSN_MASK, info);
+ if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) {
+ skb_set_mac_header(skb, 0);
+ airoha_ppe_dev_check_skb(ppe_dev, skb, hash, true);
+ }
+out:
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(mt76_npu_check_ppe);
+
+static int mt76_npu_rx_poll(struct napi_struct *napi, int budget)
+{
+ struct mt76_dev *dev = mt76_priv(napi->dev);
+ enum mt76_rxq_id qid = napi - dev->napi;
+ struct airoha_npu *npu;
+ int done = 0;
+
+ rcu_read_lock();
+
+ npu = rcu_dereference(dev->mmio.npu);
+ if (!npu)
+ goto out;
+
+ while (done < budget) {
+ struct sk_buff *skb;
+ u32 info = 0;
+
+ skb = mt76_npu_dequeue(dev, &dev->q_rx[qid], &info);
+ if (!skb)
+ break;
+
+ dev->drv->rx_skb(dev, qid, skb, &info);
+ mt76_rx_poll_complete(dev, qid, napi);
+ done++;
+ }
+
+ mt76_npu_fill_rx_queue(dev, &dev->q_rx[qid]);
+out:
+ if (done < budget && napi_complete(napi))
+ dev->drv->rx_poll_complete(dev, qid);
+
+ rcu_read_unlock();
+
+ return done;
+}
+
+static irqreturn_t mt76_npu_irq_handler(int irq, void *q_instance)
+{
+ struct mt76_queue *q = q_instance;
+ struct mt76_dev *dev = q->dev;
+ int qid = q - &dev->q_rx[0];
+ int index = qid - MT_RXQ_NPU0;
+ struct airoha_npu *npu;
+ u32 status;
+
+ rcu_read_lock();
+
+ npu = rcu_dereference(dev->mmio.npu);
+ if (!npu)
+ goto out;
+
+ status = airoha_npu_wlan_get_irq_status(npu, index);
+ airoha_npu_wlan_set_irq_status(npu, status);
+
+ airoha_npu_wlan_disable_irq(npu, index);
+ napi_schedule(&dev->napi[qid]);
+out:
+ rcu_read_unlock();
+
+ return IRQ_HANDLED;
+}
+
+int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q,
+ struct sk_buff *skb, struct mt76_queue_buf *buf,
+ void *txwi_ptr)
+{
+ u16 txwi_len = min_t(u16, phy->dev->drv->txwi_size, NPU_TXWI_LEN);
+ struct airoha_npu_tx_dma_desc *desc = (void *)q->desc;
+ int ret;
+
+ /* TODO: Take into account unlinear skbs */
+ memcpy(desc[q->head].txwi, txwi_ptr, txwi_len);
+ desc[q->head].addr = buf->addr;
+ desc[q->head].ctrl = FIELD_PREP(NPU_TX_DMA_DESC_VEND_LEN_MASK, txwi_len) |
+ FIELD_PREP(NPU_TX_DMA_DESC_LEN_MASK, skb->len) |
+ NPU_TX_DMA_DESC_DONE_MASK;
+
+ ret = q->head;
+ q->entry[q->head].skip_buf0 = true;
+ q->entry[q->head].skip_buf1 = true;
+ q->entry[q->head].txwi = NULL;
+ q->entry[q->head].skb = NULL;
+ q->entry[q->head].wcid = 0xffff;
+
+ q->head = (q->head + 1) % q->ndesc;
+ q->queued++;
+
+ return ret;
+}
+
+void mt76_npu_txdesc_cleanup(struct mt76_queue *q, int index)
+{
+ struct airoha_npu_tx_dma_desc *desc = (void *)q->desc;
+
+ if (!mt76_queue_is_npu_tx(q))
+ return;
+
+ desc[index].ctrl &= ~NPU_TX_DMA_DESC_DONE_MASK;
+}
+
+void mt76_npu_queue_setup(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ int qid = FIELD_GET(MT_QFLAG_WED_RING, q->flags);
+ bool xmit = mt76_queue_is_npu_tx(q);
+ struct airoha_npu *npu;
+
+ if (!mt76_queue_is_npu(q))
+ return;
+
+ npu = rcu_dereference_protected(dev->mmio.npu, &dev->mutex);
+ if (npu)
+ q->wed_regs = airoha_npu_wlan_get_queue_addr(npu, qid, xmit);
+}
+
+int mt76_npu_rx_queue_init(struct mt76_dev *dev, struct mt76_queue *q)
+{
+ int err, irq, qid = q - &dev->q_rx[0];
+ int size, index = qid - MT_RXQ_NPU0;
+ struct airoha_npu *npu;
+ const char *name;
+
+ mutex_lock(&dev->mutex);
+
+ npu = rcu_dereference_protected(dev->mmio.npu, &dev->mutex);
+ irq = npu && index < ARRAY_SIZE(npu->irqs) ? npu->irqs[index]
+ : -EINVAL;
+ if (irq < 0) {
+ err = irq;
+ goto out;
+ }
+
+ q->flags = MT_NPU_Q_RX(index);
+ size = qid == MT_RXQ_NPU1 ? NPU_RX1_DESC_NUM : NPU_RX0_DESC_NUM;
+ err = dev->queue_ops->alloc(dev, q, 0, size,
+ MT76_NPU_RX_BUF_SIZE, 0);
+ if (err)
+ goto out;
+
+ name = devm_kasprintf(dev->dev, GFP_KERNEL, "mt76-npu.%d", index);
+ if (!name) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = devm_request_irq(dev->dev, irq, mt76_npu_irq_handler,
+ IRQF_SHARED, name, q);
+ if (err)
+ goto out;
+
+ netif_napi_add(dev->napi_dev, &dev->napi[qid], mt76_npu_rx_poll);
+ mt76_npu_fill_rx_queue(dev, q);
+ napi_enable(&dev->napi[qid]);
+out:
+ mutex_unlock(&dev->mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mt76_npu_rx_queue_init);
+
+static int mt76_npu_setup_tc_block_cb(enum tc_setup_type type,
+ void *type_data, void *cb_priv)
+{
+ struct mt76_phy *phy = cb_priv;
+ struct mt76_dev *dev = phy->dev;
+ struct airoha_ppe_dev *ppe_dev;
+ int err = -EOPNOTSUPP;
+
+ if (type != TC_SETUP_CLSFLOWER)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&dev->mutex);
+
+ ppe_dev = rcu_dereference_protected(dev->mmio.ppe_dev, &dev->mutex);
+ if (ppe_dev)
+ err = airoha_ppe_dev_setup_tc_block_cb(ppe_dev, type_data);
+
+ mutex_unlock(&dev->mutex);
+
+ return err;
+}
+
+static int mt76_npu_setup_tc_block(struct mt76_phy *phy,
+ struct net_device *dev,
+ struct flow_block_offload *f)
+{
+ flow_setup_cb_t *cb = mt76_npu_setup_tc_block_cb;
+ static LIST_HEAD(block_cb_list);
+ struct flow_block_cb *block_cb;
+
+ if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
+ return -EOPNOTSUPP;
+
+ if (!tc_can_offload(dev))
+ return -EOPNOTSUPP;
+
+ f->driver_block_list = &block_cb_list;
+ switch (f->command) {
+ case FLOW_BLOCK_BIND:
+ block_cb = flow_block_cb_lookup(f->block, cb, dev);
+ if (block_cb) {
+ flow_block_cb_incref(block_cb);
+ return 0;
+ }
+
+ block_cb = flow_block_cb_alloc(cb, dev, phy, NULL);
+ if (IS_ERR(block_cb))
+ return PTR_ERR(block_cb);
+
+ flow_block_cb_incref(block_cb);
+ flow_block_cb_add(block_cb, f);
+ list_add_tail(&block_cb->driver_list, &block_cb_list);
+ return 0;
+ case FLOW_BLOCK_UNBIND:
+ block_cb = flow_block_cb_lookup(f->block, cb, dev);
+ if (!block_cb)
+ return -ENOENT;
+
+ if (!flow_block_cb_decref(block_cb)) {
+ flow_block_cb_remove(block_cb, f);
+ list_del(&block_cb->driver_list);
+ }
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct net_device *dev, enum tc_setup_type type,
+ void *type_data)
+{
+ struct mt76_phy *phy = hw->priv;
+
+ if (!tc_can_offload(dev))
+ return -EOPNOTSUPP;
+
+ if (!mt76_npu_device_active(phy->dev))
+ return -EOPNOTSUPP;
+
+ switch (type) {
+ case TC_SETUP_BLOCK:
+ case TC_SETUP_FT:
+ return mt76_npu_setup_tc_block(phy, dev, type_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+EXPORT_SYMBOL_GPL(mt76_npu_net_setup_tc);
+
+void mt76_npu_disable_irqs(struct mt76_dev *dev)
+{
+ struct airoha_npu *npu;
+ int i;
+
+ rcu_read_lock();
+
+ npu = rcu_dereference(dev->mmio.npu);
+ if (!npu)
+ goto unlock;
+
+ for (i = MT_RXQ_NPU0; i <= MT_RXQ_NPU1; i++) {
+ int qid = i - MT_RXQ_NPU0;
+ u32 status;
+
+ status = airoha_npu_wlan_get_irq_status(npu, qid);
+ airoha_npu_wlan_set_irq_status(npu, status);
+ airoha_npu_wlan_disable_irq(npu, qid);
+ }
+unlock:
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(mt76_npu_disable_irqs);
+
+int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type)
+{
+ struct airoha_ppe_dev *ppe_dev;
+ struct airoha_npu *npu;
+ int err = 0;
+
+ /* NPU offloading is only supported by MT7992 */
+ if (!is_mt7992(dev))
+ return 0;
+
+ mutex_lock(&dev->mutex);
+
+ npu = airoha_npu_get(dev->dev);
+ if (IS_ERR(npu)) {
+ request_module("airoha-npu");
+ npu = airoha_npu_get(dev->dev);
+ }
+
+ if (IS_ERR(npu)) {
+ err = PTR_ERR(npu);
+ goto error_unlock;
+ }
+
+ ppe_dev = airoha_ppe_get_dev(dev->dev);
+ if (IS_ERR(ppe_dev)) {
+ request_module("airoha-eth");
+ ppe_dev = airoha_ppe_get_dev(dev->dev);
+ }
+
+ if (IS_ERR(ppe_dev)) {
+ err = PTR_ERR(ppe_dev);
+ goto error_npu_put;
+ }
+
+ err = airoha_npu_wlan_init_reserved_memory(npu);
+ if (err)
+ goto error_ppe_put;
+
+ dev->dma_dev = npu->dev;
+ dev->mmio.phy_addr = phy_addr;
+ dev->mmio.npu_type = type;
+ /* NPU offloading requires HW-RRO for RX packet reordering. */
+ dev->hwrro_mode = MT76_HWRRO_V3_1;
+
+ rcu_assign_pointer(dev->mmio.npu, npu);
+ rcu_assign_pointer(dev->mmio.ppe_dev, ppe_dev);
+ synchronize_rcu();
+
+ mutex_unlock(&dev->mutex);
+
+ return 0;
+
+error_ppe_put:
+ airoha_ppe_put_dev(ppe_dev);
+error_npu_put:
+ airoha_npu_put(npu);
+error_unlock:
+ mutex_unlock(&dev->mutex);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(mt76_npu_init);
+
+void mt76_npu_deinit(struct mt76_dev *dev)
+{
+ struct airoha_ppe_dev *ppe_dev;
+ struct airoha_npu *npu;
+
+ mutex_lock(&dev->mutex);
+
+ npu = rcu_replace_pointer(dev->mmio.npu, NULL,
+ lockdep_is_held(&dev->mutex));
+ if (npu)
+ airoha_npu_put(npu);
+
+ ppe_dev = rcu_replace_pointer(dev->mmio.ppe_dev, NULL,
+ lockdep_is_held(&dev->mutex));
+ if (ppe_dev)
+ airoha_ppe_put_dev(ppe_dev);
+
+ mutex_unlock(&dev->mutex);
+
+ mt76_npu_queue_cleanup(dev, &dev->q_rx[MT_RXQ_NPU0]);
+ mt76_npu_queue_cleanup(dev, &dev->q_rx[MT_RXQ_NPU1]);
+}
diff --git a/drivers/net/wireless/mediatek/mt76/pci.c b/drivers/net/wireless/mediatek/mt76/pci.c
index b5031ca7f73f..833923ab2483 100644
--- a/drivers/net/wireless/mediatek/mt76/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/pci.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2019 Lorenzo Bianconi <lorenzo@kernel.org>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c
index 5a875aac410f..ff9176cdee3d 100644
--- a/drivers/net/wireless/mediatek/mt76/scan.c
+++ b/drivers/net/wireless/mediatek/mt76/scan.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c
index 8e9576747052..8bae77c761be 100644
--- a/drivers/net/wireless/mediatek/mt76/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/sdio.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
* This file is written based on mt76/usb.c.
diff --git a/drivers/net/wireless/mediatek/mt76/sdio.h b/drivers/net/wireless/mediatek/mt76/sdio.h
index 27d5d2077eba..41b89f3de86b 100644
--- a/drivers/net/wireless/mediatek/mt76/sdio.h
+++ b/drivers/net/wireless/mediatek/mt76/sdio.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/* Copyright (C) 2020 MediaTek Inc.
*
* Author: Sean Wang <sean.wang@mediatek.com>
diff --git a/drivers/net/wireless/mediatek/mt76/sdio_txrx.c b/drivers/net/wireless/mediatek/mt76/sdio_txrx.c
index f882d21c9f63..3f314e8e1e69 100644
--- a/drivers/net/wireless/mediatek/mt76/sdio_txrx.c
+++ b/drivers/net/wireless/mediatek/mt76/sdio_txrx.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 MediaTek Inc.
*
* Author: Felix Fietkau <nbd@nbd.name>
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.c b/drivers/net/wireless/mediatek/mt76/testmode.c
index ca4feccf38ca..6ee160bda882 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.c
+++ b/drivers/net/wireless/mediatek/mt76/testmode.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name> */
#include <linux/random.h>
diff --git a/drivers/net/wireless/mediatek/mt76/testmode.h b/drivers/net/wireless/mediatek/mt76/testmode.h
index 0590c35c7126..bed1ba40ba94 100644
--- a/drivers/net/wireless/mediatek/mt76/testmode.h
+++ b/drivers/net/wireless/mediatek/mt76/testmode.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2020 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/trace.c b/drivers/net/wireless/mediatek/mt76/trace.c
index f199fcd2a63d..f17cc01017f3 100644
--- a/drivers/net/wireless/mediatek/mt76/trace.c
+++ b/drivers/net/wireless/mediatek/mt76/trace.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/trace.h b/drivers/net/wireless/mediatek/mt76/trace.h
index 109a07f9733a..794b957ac79d 100644
--- a/drivers/net/wireless/mediatek/mt76/trace.h
+++ b/drivers/net/wireless/mediatek/mt76/trace.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index b78ae6a34b65..9ec6d0b53a84 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
@@ -847,8 +847,10 @@ int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi)
spin_lock_bh(&dev->token_lock);
- token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC);
- if (token >= 0)
+ token = idr_alloc(&dev->token, *ptxwi, dev->token_start,
+ dev->token_start + dev->token_size,
+ GFP_ATOMIC);
+ if (token >= dev->token_start)
dev->token_count++;
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index f9e67b8c3b3c..632ae755c7a6 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/usb_trace.c b/drivers/net/wireless/mediatek/mt76/usb_trace.c
index 9942bdd6177b..a04585b4b778 100644
--- a/drivers/net/wireless/mediatek/mt76/usb_trace.c
+++ b/drivers/net/wireless/mediatek/mt76/usb_trace.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/usb_trace.h b/drivers/net/wireless/mediatek/mt76/usb_trace.h
index 7b261ddb2ac6..93bb69c65a4f 100644
--- a/drivers/net/wireless/mediatek/mt76/usb_trace.h
+++ b/drivers/net/wireless/mediatek/mt76/usb_trace.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: ISC */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/util.c b/drivers/net/wireless/mediatek/mt76/util.c
index 97249ebb4bc8..83d3dc42e534 100644
--- a/drivers/net/wireless/mediatek/mt76/util.c
+++ b/drivers/net/wireless/mediatek/mt76/util.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
*/
diff --git a/drivers/net/wireless/mediatek/mt76/util.h b/drivers/net/wireless/mediatek/mt76/util.h
index 260965dde94c..617966e8de76 100644
--- a/drivers/net/wireless/mediatek/mt76/util.h
+++ b/drivers/net/wireless/mediatek/mt76/util.h
@@ -1,7 +1,6 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
/*
* Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
- * Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
*/
#ifndef __MT76_UTIL_H
diff --git a/drivers/net/wireless/mediatek/mt76/wed.c b/drivers/net/wireless/mediatek/mt76/wed.c
index 907a8e43e72a..ed657d952de2 100644
--- a/drivers/net/wireless/mediatek/mt76/wed.c
+++ b/drivers/net/wireless/mediatek/mt76/wed.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: ISC
+// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (C) 2023 Lorenzo Bianconi <lorenzo@kernel.org>
*/
@@ -8,7 +8,7 @@
void mt76_wed_release_rx_buf(struct mtk_wed_device *wed)
{
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ struct mt76_dev *dev = mt76_wed_to_dev(wed);
int i;
for (i = 0; i < dev->rx_token_size; i++) {
@@ -31,8 +31,8 @@ EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf);
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
{
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc;
+ struct mt76_dev *dev = mt76_wed_to_dev(wed);
struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
struct mt76_txwi_cache *t = NULL;
int i;
@@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_init_rx_buf);
int mt76_wed_offload_enable(struct mtk_wed_device *wed)
{
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ struct mt76_dev *dev = mt76_wed_to_dev(wed);
spin_lock_bh(&dev->token_lock);
dev->token_size = wed->wlan.token_start;
@@ -164,7 +164,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_dma_setup);
void mt76_wed_offload_disable(struct mtk_wed_device *wed)
{
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ struct mt76_dev *dev = mt76_wed_to_dev(wed);
spin_lock_bh(&dev->token_lock);
dev->token_size = dev->drv->token_size;
@@ -174,7 +174,7 @@ EXPORT_SYMBOL_GPL(mt76_wed_offload_disable);
void mt76_wed_reset_complete(struct mtk_wed_device *wed)
{
- struct mt76_dev *dev = container_of(wed, struct mt76_dev, mmio.wed);
+ struct mt76_dev *dev = mt76_wed_to_dev(wed);
complete(&dev->mmio.wed_reset_complete);
}
diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index 825b05dd3271..38af6cdc2843 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -714,7 +714,8 @@ int qtnf_core_attach(struct qtnf_bus *bus)
goto error;
}
- bus->hprio_workqueue = alloc_workqueue("QTNF_HPRI", WQ_HIGHPRI, 0);
+ bus->hprio_workqueue = alloc_workqueue("QTNF_HPRI",
+ WQ_HIGHPRI | WQ_PERCPU, 0);
if (!bus->hprio_workqueue) {
pr_err("failed to alloc high prio workqueue\n");
ret = -ENOMEM;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index b264ed0af923..65d0f805459c 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -24,6 +24,7 @@
#include <linux/crc-ccitt.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/slab.h>
#include "rt2x00.h"
@@ -10962,6 +10963,36 @@ int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev)
}
EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);
+int rt2800_read_eeprom_nvmem(struct rt2x00_dev *rt2x00dev)
+{
+ struct device_node *np = rt2x00dev->dev->of_node;
+ unsigned int len = rt2x00dev->ops->eeprom_size;
+ struct nvmem_cell *cell;
+ const void *data;
+ size_t retlen;
+
+ cell = of_nvmem_cell_get(np, "eeprom");
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ data = nvmem_cell_read(cell, &retlen);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ if (retlen != len) {
+ dev_err(rt2x00dev->dev, "invalid eeprom size, required: 0x%04x\n", len);
+ kfree(data);
+ return -EINVAL;
+ }
+
+ memcpy(rt2x00dev->eeprom, data, len);
+ kfree(data);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_read_eeprom_nvmem);
+
static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev)
{
u16 word;
@@ -11011,7 +11042,9 @@ static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
* Start validation of the data that has been read.
*/
mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
- rt2x00lib_set_mac_address(rt2x00dev, mac);
+ retval = rt2x00lib_set_mac_address(rt2x00dev, mac);
+ if (retval)
+ return retval;
word = rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0);
if (word == 0xffff) {
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
index 620a3d9872ce..a3c3a751f57e 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.h
@@ -248,6 +248,8 @@ void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev);
int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev);
int rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev);
+int rt2800_read_eeprom_nvmem(struct rt2x00_dev *rt2x00dev);
+
int rt2800_probe_hw(struct rt2x00_dev *rt2x00dev);
void rt2800_get_key_seq(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
index 14c45aba836f..4fa14bb573ad 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800pci.c
@@ -278,6 +278,9 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev)
{
int retval;
+ if (!rt2800_read_eeprom_nvmem(rt2x00dev))
+ return 0;
+
if (rt2800pci_efuse_detect(rt2x00dev))
retval = rt2800pci_read_eeprom_efuse(rt2x00dev);
else
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
index 8f510a84e7f1..5c29201b34c8 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800soc.c
@@ -92,8 +92,12 @@ static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev,
static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev)
{
- void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE);
+ void __iomem *base_addr;
+ if (!rt2800_read_eeprom_nvmem(rt2x00dev))
+ return 0;
+
+ base_addr = ioremap(0x1F040000, EEPROM_SIZE);
if (!base_addr)
return -ENOMEM;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
index 09b9d1f9f793..665887e9b118 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h
@@ -1427,7 +1427,7 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
*/
u32 rt2x00lib_get_bssidx(struct rt2x00_dev *rt2x00dev,
struct ieee80211_vif *vif);
-void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr);
+int rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr);
/*
* Interrupt context handlers.
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
index f8a6f9c968a1..778a478ab53a 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c
@@ -988,14 +988,20 @@ static void rt2x00lib_rate(struct ieee80211_rate *entry,
entry->flags |= IEEE80211_RATE_SHORT_PREAMBLE;
}
-void rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
+int rt2x00lib_set_mac_address(struct rt2x00_dev *rt2x00dev, u8 *eeprom_mac_addr)
{
- of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
+ int ret;
+
+ ret = of_get_mac_address(rt2x00dev->dev->of_node, eeprom_mac_addr);
+ if (ret == -EPROBE_DEFER)
+ return ret;
if (!is_valid_ether_addr(eeprom_mac_addr)) {
eth_random_addr(eeprom_mac_addr);
rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", eeprom_mac_addr);
}
+
+ return 0;
}
EXPORT_SYMBOL_GPL(rt2x00lib_set_mac_address);
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
index 2905baea6239..070c0431c482 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8180/dev.c
@@ -1023,9 +1023,6 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev)
dma_addr_t *mapping;
entry = priv->rx_ring + priv->rx_ring_sz*i;
if (!skb) {
- dma_free_coherent(&priv->pdev->dev,
- priv->rx_ring_sz * 32,
- priv->rx_ring, priv->rx_ring_dma);
wiphy_err(dev->wiphy, "Cannot allocate RX skb\n");
return -ENOMEM;
}
@@ -1037,9 +1034,7 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev)
if (dma_mapping_error(&priv->pdev->dev, *mapping)) {
kfree_skb(skb);
- dma_free_coherent(&priv->pdev->dev,
- priv->rx_ring_sz * 32,
- priv->rx_ring, priv->rx_ring_dma);
+ priv->rx_buf[i] = NULL;
wiphy_err(dev->wiphy, "Cannot map DMA for RX skb\n");
return -ENOMEM;
}
@@ -1130,7 +1125,7 @@ static int rtl8180_start(struct ieee80211_hw *dev)
ret = rtl8180_init_rx_ring(dev);
if (ret)
- return ret;
+ goto err_free_rings;
for (i = 0; i < (dev->queues + 1); i++)
if ((ret = rtl8180_init_tx_ring(dev, i, 16)))
diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
index 0c5c66401daa..7aa2da0cd63c 100644
--- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
@@ -338,14 +338,16 @@ static void rtl8187_rx_cb(struct urb *urb)
spin_unlock_irqrestore(&priv->rx_queue.lock, f);
skb_put(skb, urb->actual_length);
- if (unlikely(urb->status)) {
- dev_kfree_skb_irq(skb);
- return;
- }
+ if (unlikely(urb->status))
+ goto free_skb;
if (!priv->is_rtl8187b) {
- struct rtl8187_rx_hdr *hdr =
- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
+ struct rtl8187_rx_hdr *hdr;
+
+ if (skb->len < sizeof(struct rtl8187_rx_hdr))
+ goto free_skb;
+
+ hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
flags = le32_to_cpu(hdr->flags);
/* As with the RTL8187B below, the AGC is used to calculate
* signal strength. In this case, the scaling
@@ -355,8 +357,12 @@ static void rtl8187_rx_cb(struct urb *urb)
rx_status.antenna = (hdr->signal >> 7) & 1;
rx_status.mactime = le64_to_cpu(hdr->mac_time);
} else {
- struct rtl8187b_rx_hdr *hdr =
- (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
+ struct rtl8187b_rx_hdr *hdr;
+
+ if (skb->len < sizeof(struct rtl8187b_rx_hdr))
+ goto free_skb;
+
+ hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr));
/* The Realtek datasheet for the RTL8187B shows that the RX
* header contains the following quantities: signal quality,
* RSSI, AGC, the received power in dB, and the measured SNR.
@@ -409,6 +415,11 @@ static void rtl8187_rx_cb(struct urb *urb)
skb_unlink(skb, &priv->rx_queue);
dev_kfree_skb_irq(skb);
}
+ return;
+
+free_skb:
+ dev_kfree_skb_irq(skb);
+ return;
}
static int rtl8187_init_urbs(struct ieee80211_hw *dev)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
index 73034e7e41d1..444872131c66 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c
@@ -593,6 +593,84 @@ static int rtl8192cu_power_on(struct rtl8xxxu_priv *priv)
return 0;
}
+static void rtl8192cu_power_off(struct rtl8xxxu_priv *priv)
+{
+ u32 val32;
+ u16 val16;
+ u8 val8;
+ int i;
+
+ /*
+ * Workaround for 8188RU LNA power leakage problem.
+ */
+ if (priv->rtl_chip == RTL8188R) {
+ val32 = rtl8xxxu_read32(priv, REG_FPGA0_XCD_RF_PARM);
+ val32 |= BIT(1);
+ rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32);
+ }
+
+ /* _DisableRFAFEAndResetBB */
+ rtl8xxxu_write8(priv, REG_TXPAUSE, 0xff);
+ rtl8xxxu_write_rfreg_mask(priv, RF_A, RF6052_REG_AC, 0xff, 0);
+
+ rtl8xxxu_write8_set(priv, REG_APSD_CTRL, APSD_CTRL_OFF);
+ rtl8xxxu_write32_set(priv, REG_FPGA0_XCD_RF_PARM, FPGA0_RF_PARM_CLK_GATE);
+
+ rtl8xxxu_write8(priv, REG_SYS_FUNC,
+ SYS_FUNC_USBA | SYS_FUNC_USBD | SYS_FUNC_BB_GLB_RSTN);
+ rtl8xxxu_write8(priv, REG_SYS_FUNC, SYS_FUNC_USBA | SYS_FUNC_USBD);
+
+ /* _ResetDigitalProcedure1 */
+ if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_DL_READY) {
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
+
+ rtl8xxxu_write8(priv, REG_FWIMR, 0x20);
+
+ rtl8xxxu_write8(priv, REG_HMTFR + 3, 0x20);
+
+ for (i = 0; i < 100; i++) {
+ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ if (!(val16 & SYS_FUNC_CPU_ENABLE))
+ break;
+
+ fsleep(50);
+ }
+
+ if (i == 100) {
+ rtl8xxxu_write8(priv, REG_SYS_FUNC + 1,
+ (SYS_FUNC_HWPDN | SYS_FUNC_ELDR) >> 8);
+ msleep(10);
+ }
+ }
+
+ val8 = (SYS_FUNC_HWPDN | SYS_FUNC_ELDR | SYS_FUNC_CPU_ENABLE) >> 8;
+ rtl8xxxu_write8(priv, REG_SYS_FUNC + 1, val8);
+
+ /* _DisableGPIO */
+ rtl8xxxu_write16(priv, REG_GPIO_PIN_CTRL + 2, 0);
+ val32 = rtl8xxxu_read32(priv, REG_GPIO_PIN_CTRL) & 0xffff00ff;
+ val32 |= (val32 & 0xff) << 8;
+ val32 |= 0x00ff0000;
+ rtl8xxxu_write32(priv, REG_GPIO_PIN_CTRL, val32);
+
+ rtl8xxxu_write8(priv, REG_GPIO_MUXCFG + 3, 0);
+ val16 = rtl8xxxu_read16(priv, REG_GPIO_MUXCFG + 2) & 0xff0f;
+ val16 |= (val16 & 0xf) << 4;
+ val16 |= 0x0780;
+ rtl8xxxu_write16(priv, REG_GPIO_MUXCFG + 2, val16);
+
+ /* _DisableAnalog */
+ val8 = 0x23;
+ if (priv->vendor_umc && priv->chip_cut == 1)
+ val8 |= BIT(3);
+ rtl8xxxu_write8(priv, REG_SPS0_CTRL, val8);
+
+ val16 = APS_FSMCO_HOST | APS_FSMCO_HW_SUSPEND | APS_FSMCO_PFM_ALDN;
+ rtl8xxxu_write16(priv, REG_APS_FSMCO, val16);
+
+ rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e);
+}
+
static int rtl8192cu_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
@@ -618,7 +696,7 @@ struct rtl8xxxu_fileops rtl8192cu_fops = {
.parse_efuse = rtl8192cu_parse_efuse,
.load_firmware = rtl8192cu_load_firmware,
.power_on = rtl8192cu_power_on,
- .power_off = rtl8xxxu_power_off,
+ .power_off = rtl8192cu_power_off,
.read_efuse = rtl8xxxu_read_efuse,
.reset_8051 = rtl8xxxu_reset_8051,
.llt_init = rtl8xxxu_init_llt_table,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
index ecbc324e4609..4f4493d0bfc2 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8723a.c
@@ -411,6 +411,119 @@ exit:
return ret;
}
+static int rtl8723au_active_to_emu(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ int count, ret = 0;
+
+ /* Start of rtl8723AU_card_enable_flow */
+ /* Act to Cardemu sequence*/
+ /* Turn off RF */
+ rtl8xxxu_write8(priv, REG_RF_CTRL, 0);
+
+ /* 0x004E[7] = 0, switch DPDT_SEL_P output from register 0x0065[2] */
+ val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
+ val8 &= ~LEDCFG2_DPDT_SELECT;
+ rtl8xxxu_write8(priv, REG_LEDCFG2, val8);
+
+ /* 0x0005[1] = 1 turn off MAC by HW state machine*/
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 |= BIT(1);
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+ for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ if ((val8 & BIT(1)) == 0)
+ break;
+ udelay(10);
+ }
+
+ if (!count) {
+ dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n",
+ __func__);
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */
+ val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
+ val8 |= SYS_ISO_ANALOG_IPS;
+ rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
+
+ /* 0x0020[0] = 0 disable LDOA12 MACRO block*/
+ val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL);
+ val8 &= ~LDOA15_ENABLE;
+ rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8);
+
+exit:
+ return ret;
+}
+
+static int rtl8723au_emu_to_disabled(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+
+ /* 0x0007[7:0] = 0x20 SOP option to disable BG/MB */
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 3, 0x20);
+
+ /* 0x04[12:11] = 01 enable WL suspend */
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 &= ~BIT(4);
+ val8 |= BIT(3);
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+ val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
+ val8 |= BIT(7);
+ rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
+
+ /* 0x48[16] = 1 to enable GPIO9 as EXT wakeup */
+ val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 2);
+ val8 |= BIT(0);
+ rtl8xxxu_write8(priv, REG_GPIO_INTM + 2, val8);
+
+ return 0;
+}
+
+static void rtl8723au_power_off(struct rtl8xxxu_priv *priv)
+{
+ u8 val8;
+ u16 val16;
+
+ rtl8xxxu_flush_fifo(priv);
+
+ rtl8xxxu_active_to_lps(priv);
+
+ /* Turn off RF */
+ rtl8xxxu_write8(priv, REG_RF_CTRL, 0x00);
+
+ /* Reset Firmware if running in RAM */
+ if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL)
+ rtl8xxxu_firmware_self_reset(priv);
+
+ /* Reset MCU */
+ val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
+ val16 &= ~SYS_FUNC_CPU_ENABLE;
+ rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
+
+ /* Reset MCU ready status */
+ rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
+
+ rtl8723au_active_to_emu(priv);
+ rtl8723au_emu_to_disabled(priv);
+
+ /* Reset MCU IO Wrapper */
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+ val8 &= ~BIT(0);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+ val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
+ val8 |= BIT(0);
+ rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
+
+ /* RSV_CTRL 0x1C[7:0] = 0x0e lock ISO/CLK/Power control register */
+ rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e);
+}
+
#define XTAL1 GENMASK(23, 18)
#define XTAL0 GENMASK(17, 12)
@@ -492,7 +605,7 @@ struct rtl8xxxu_fileops rtl8723au_fops = {
.parse_efuse = rtl8723au_parse_efuse,
.load_firmware = rtl8723au_load_firmware,
.power_on = rtl8723au_power_on,
- .power_off = rtl8xxxu_power_off,
+ .power_off = rtl8723au_power_off,
.read_efuse = rtl8xxxu_read_efuse,
.reset_8051 = rtl8xxxu_reset_8051,
.llt_init = rtl8xxxu_init_llt_table,
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 3ded5952729f..c06ad064f37c 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -20,7 +20,6 @@
#define DRIVER_NAME "rtl8xxxu"
int rtl8xxxu_debug;
-static bool rtl8xxxu_ht40_2g;
static bool rtl8xxxu_dma_aggregation;
static int rtl8xxxu_dma_agg_timeout = -1;
static int rtl8xxxu_dma_agg_pages = -1;
@@ -45,8 +44,6 @@ MODULE_FIRMWARE("rtlwifi/rtl8192fufw.bin");
module_param_named(debug, rtl8xxxu_debug, int, 0600);
MODULE_PARM_DESC(debug, "Set debug mask");
-module_param_named(ht40_2g, rtl8xxxu_ht40_2g, bool, 0600);
-MODULE_PARM_DESC(ht40_2g, "Enable HT40 support on the 2.4GHz band");
module_param_named(dma_aggregation, rtl8xxxu_dma_aggregation, bool, 0600);
MODULE_PARM_DESC(dma_aggregation, "Enable DMA packet aggregation");
module_param_named(dma_agg_timeout, rtl8xxxu_dma_agg_timeout, int, 0600);
@@ -1252,7 +1249,7 @@ void rtl8xxxu_gen1_config_channel(struct ieee80211_hw *hw)
opmode &= ~BW_OPMODE_20MHZ;
rtl8xxxu_write8(priv, REG_BW_OPMODE, opmode);
rsr &= ~RSR_RSC_BANDWIDTH_40M;
- if (sec_ch_above)
+ if (!sec_ch_above)
rsr |= RSR_RSC_UPPER_SUB_CHANNEL;
else
rsr |= RSR_RSC_LOWER_SUB_CHANNEL;
@@ -1321,9 +1318,8 @@ void rtl8xxxu_gen1_config_channel(struct ieee80211_hw *hw)
for (i = RF_A; i < priv->rf_paths; i++) {
val32 = rtl8xxxu_read_rfreg(priv, i, RF6052_REG_MODE_AG);
- if (hw->conf.chandef.width == NL80211_CHAN_WIDTH_40)
- val32 &= ~MODE_AG_CHANNEL_20MHZ;
- else
+ val32 &= ~MODE_AG_BW_MASK;
+ if (hw->conf.chandef.width != NL80211_CHAN_WIDTH_40)
val32 |= MODE_AG_CHANNEL_20MHZ;
rtl8xxxu_write_rfreg(priv, i, RF6052_REG_MODE_AG, val32);
}
@@ -1374,9 +1370,11 @@ void rtl8xxxu_gen2_config_channel(struct ieee80211_hw *hw)
hw->conf.chandef.chan->center_freq) {
sec_ch_above = 1;
channel += 2;
+ subchannel = 2;
} else {
sec_ch_above = 0;
channel -= 2;
+ subchannel = 1;
}
val32 = rtl8xxxu_read32(priv, REG_FPGA0_RF_MODE);
@@ -3637,54 +3635,6 @@ static void rtl8xxxu_set_ampdu_min_space(struct rtl8xxxu_priv *priv, u8 density)
rtl8xxxu_write8(priv, REG_AMPDU_MIN_SPACE, val8);
}
-static int rtl8xxxu_active_to_emu(struct rtl8xxxu_priv *priv)
-{
- u8 val8;
- int count, ret = 0;
-
- /* Start of rtl8723AU_card_enable_flow */
- /* Act to Cardemu sequence*/
- /* Turn off RF */
- rtl8xxxu_write8(priv, REG_RF_CTRL, 0);
-
- /* 0x004E[7] = 0, switch DPDT_SEL_P output from register 0x0065[2] */
- val8 = rtl8xxxu_read8(priv, REG_LEDCFG2);
- val8 &= ~LEDCFG2_DPDT_SELECT;
- rtl8xxxu_write8(priv, REG_LEDCFG2, val8);
-
- /* 0x0005[1] = 1 turn off MAC by HW state machine*/
- val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
- val8 |= BIT(1);
- rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
-
- for (count = RTL8XXXU_MAX_REG_POLL; count; count--) {
- val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
- if ((val8 & BIT(1)) == 0)
- break;
- udelay(10);
- }
-
- if (!count) {
- dev_warn(&priv->udev->dev, "%s: Disabling MAC timed out\n",
- __func__);
- ret = -EBUSY;
- goto exit;
- }
-
- /* 0x0000[5] = 1 analog Ips to digital, 1:isolation */
- val8 = rtl8xxxu_read8(priv, REG_SYS_ISO_CTRL);
- val8 |= SYS_ISO_ANALOG_IPS;
- rtl8xxxu_write8(priv, REG_SYS_ISO_CTRL, val8);
-
- /* 0x0020[0] = 0 disable LDOA12 MACRO block*/
- val8 = rtl8xxxu_read8(priv, REG_LDOA15_CTRL);
- val8 &= ~LDOA15_ENABLE;
- rtl8xxxu_write8(priv, REG_LDOA15_CTRL, val8);
-
-exit:
- return ret;
-}
-
int rtl8xxxu_active_to_lps(struct rtl8xxxu_priv *priv)
{
u8 val8;
@@ -3761,31 +3711,6 @@ void rtl8xxxu_disabled_to_emu(struct rtl8xxxu_priv *priv)
rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
}
-static int rtl8xxxu_emu_to_disabled(struct rtl8xxxu_priv *priv)
-{
- u8 val8;
-
- /* 0x0007[7:0] = 0x20 SOP option to disable BG/MB */
- rtl8xxxu_write8(priv, REG_APS_FSMCO + 3, 0x20);
-
- /* 0x04[12:11] = 01 enable WL suspend */
- val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
- val8 &= ~BIT(4);
- val8 |= BIT(3);
- rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
-
- val8 = rtl8xxxu_read8(priv, REG_APS_FSMCO + 1);
- val8 |= BIT(7);
- rtl8xxxu_write8(priv, REG_APS_FSMCO + 1, val8);
-
- /* 0x48[16] = 1 to enable GPIO9 as EXT wakeup */
- val8 = rtl8xxxu_read8(priv, REG_GPIO_INTM + 2);
- val8 |= BIT(0);
- rtl8xxxu_write8(priv, REG_GPIO_INTM + 2, val8);
-
- return 0;
-}
-
int rtl8xxxu_flush_fifo(struct rtl8xxxu_priv *priv)
{
struct device *dev = &priv->udev->dev;
@@ -3863,56 +3788,6 @@ void rtl8xxxu_gen2_usb_quirks(struct rtl8xxxu_priv *priv)
rtl8xxxu_write32(priv, REG_TXDMA_OFFSET_CHK, val32);
}
-void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv)
-{
- u8 val8;
- u16 val16;
- u32 val32;
-
- /*
- * Workaround for 8188RU LNA power leakage problem.
- */
- if (priv->rtl_chip == RTL8188R) {
- val32 = rtl8xxxu_read32(priv, REG_FPGA0_XCD_RF_PARM);
- val32 |= BIT(1);
- rtl8xxxu_write32(priv, REG_FPGA0_XCD_RF_PARM, val32);
- }
-
- rtl8xxxu_flush_fifo(priv);
-
- rtl8xxxu_active_to_lps(priv);
-
- /* Turn off RF */
- rtl8xxxu_write8(priv, REG_RF_CTRL, 0x00);
-
- /* Reset Firmware if running in RAM */
- if (rtl8xxxu_read8(priv, REG_MCU_FW_DL) & MCU_FW_RAM_SEL)
- rtl8xxxu_firmware_self_reset(priv);
-
- /* Reset MCU */
- val16 = rtl8xxxu_read16(priv, REG_SYS_FUNC);
- val16 &= ~SYS_FUNC_CPU_ENABLE;
- rtl8xxxu_write16(priv, REG_SYS_FUNC, val16);
-
- /* Reset MCU ready status */
- rtl8xxxu_write8(priv, REG_MCU_FW_DL, 0x00);
-
- rtl8xxxu_active_to_emu(priv);
- rtl8xxxu_emu_to_disabled(priv);
-
- /* Reset MCU IO Wrapper */
- val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
- val8 &= ~BIT(0);
- rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
-
- val8 = rtl8xxxu_read8(priv, REG_RSV_CTRL + 1);
- val8 |= BIT(0);
- rtl8xxxu_write8(priv, REG_RSV_CTRL + 1, val8);
-
- /* RSV_CTRL 0x1C[7:0] = 0x0e lock ISO/CLK/Power control register */
- rtl8xxxu_write8(priv, REG_RSV_CTRL, 0x0e);
-}
-
void rtl8723bu_set_ps_tdma(struct rtl8xxxu_priv *priv,
u8 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5)
{
@@ -5018,8 +4893,7 @@ rtl8xxxu_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
sgi = 1;
highest_rate = fls(ramask) - 1;
- if (rtl8xxxu_ht40_2g &&
- (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)
bw = RATE_INFO_BW_40;
else
bw = RATE_INFO_BW_20;
@@ -5345,9 +5219,19 @@ rtl8xxxu_fill_txdesc_v1(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
tx_desc->txdw5 |= cpu_to_le32(TXDESC32_RETRY_LIMIT_ENABLE);
}
- if (ieee80211_is_data_qos(hdr->frame_control))
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_QOS);
+ if (conf_is_ht40(&hw->conf)) {
+ tx_desc->txdw4 |= cpu_to_le32(TXDESC_DATA_BW);
+
+ if (conf_is_ht40_minus(&hw->conf))
+ tx_desc->txdw4 |= cpu_to_le32(TXDESC_PRIME_CH_OFF_UPPER);
+ else
+ tx_desc->txdw4 |= cpu_to_le32(TXDESC_PRIME_CH_OFF_LOWER);
+ }
+ }
+
if (short_preamble)
tx_desc->txdw4 |= cpu_to_le32(TXDESC32_SHORT_PREAMBLE);
@@ -5813,7 +5697,7 @@ static void jaguar2_rx_parse_phystats_type1(struct rtl8xxxu_priv *priv,
!rtl8xxxu_is_sta_sta(priv) &&
(rtl8xxxu_is_packet_match_bssid(priv, hdr, 0) ||
rtl8xxxu_is_packet_match_bssid(priv, hdr, 1));
- u8 pwdb_max = 0;
+ u8 pwdb_max = 0, rxsc;
int rx_path;
if (parse_cfo) {
@@ -5828,6 +5712,16 @@ static void jaguar2_rx_parse_phystats_type1(struct rtl8xxxu_priv *priv,
pwdb_max = max(pwdb_max, phy_stats1->pwdb[rx_path]);
rx_status->signal = pwdb_max - 110;
+
+ if (rxmcs >= DESC_RATE_6M && rxmcs <= DESC_RATE_54M)
+ rxsc = phy_stats1->l_rxsc;
+ else
+ rxsc = phy_stats1->ht_rxsc;
+
+ if (phy_stats1->rf_mode == 0 || rxsc == 1 || rxsc == 2)
+ rx_status->bw = RATE_INFO_BW_20;
+ else
+ rx_status->bw = RATE_INFO_BW_40;
}
static void jaguar2_rx_parse_phystats_type2(struct rtl8xxxu_priv *priv,
@@ -6454,6 +6348,8 @@ int rtl8xxxu_parse_rxdesc16(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
rtl8xxxu_rx_update_rssi(priv,
rx_status,
hdr);
+ } else {
+ rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
}
rx_status->mactime = rx_desc->tsfl;
@@ -6560,6 +6456,8 @@ int rtl8xxxu_parse_rxdesc24(struct rtl8xxxu_priv *priv, struct sk_buff *skb)
rtl8xxxu_rx_update_rssi(priv,
rx_status,
hdr);
+ } else {
+ rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
}
rx_status->mactime = rx_desc->tsfl;
@@ -7906,15 +7804,15 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
goto err_set_intfdata;
}
+ if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE)
+ rtl8xxxu_dump_efuse(priv);
+
ret = priv->fops->parse_efuse(priv);
if (ret) {
dev_err(&udev->dev, "Fatal - failed to parse EFuse\n");
goto err_set_intfdata;
}
- if (rtl8xxxu_debug & RTL8XXXU_DEBUG_EFUSE)
- rtl8xxxu_dump_efuse(priv);
-
rtl8xxxu_print_chipinfo(priv);
ret = priv->fops->load_firmware(priv);
@@ -7949,7 +7847,8 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
sband->ht_cap.ht_supported = true;
sband->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
sband->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
- sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40;
+ sband->ht_cap.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40;
memset(&sband->ht_cap.mcs, 0, sizeof(sband->ht_cap.mcs));
sband->ht_cap.mcs.rx_mask[0] = 0xff;
sband->ht_cap.mcs.rx_mask[4] = 0x01;
@@ -7958,15 +7857,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
sband->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
}
sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
- /*
- * Some APs will negotiate HT20_40 in a noisy environment leading
- * to miserable performance. Rather than defaulting to this, only
- * enable it if explicitly requested at module load time.
- */
- if (rtl8xxxu_ht40_2g) {
- dev_info(&udev->dev, "Enabling HT_20_40 on the 2.4GHz band\n");
- sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- }
+
hw->wiphy->bands[NL80211_BAND_2GHZ] = sband;
hw->wiphy->rts_threshold = 2347;
@@ -8136,6 +8027,9 @@ static const struct usb_device_id dev_table[] = {
/* TP-Link TL-WN823N V2 */
{USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0135, 0xff, 0xff, 0xff),
.driver_info = (unsigned long)&rtl8192fu_fops},
+/* D-Link AN3U rev. A1 */
+{USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3328, 0xff, 0xff, 0xff),
+ .driver_info = (unsigned long)&rtl8192fu_fops},
#ifdef CONFIG_RTL8XXXU_UNTESTED
/* Still supported by rtlwifi */
{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x8176, 0xff, 0xff, 0xff),
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/regs.h b/drivers/net/wireless/realtek/rtl8xxxu/regs.h
index 61c0c0ec07b3..0741db8d08bf 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/regs.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/regs.h
@@ -40,6 +40,7 @@
#define APS_FSMCO_SW_LPS BIT(10)
#define APS_FSMCO_HW_SUSPEND BIT(11)
#define APS_FSMCO_PCIE BIT(12)
+#define APS_FSMCO_HOST BIT(14)
#define APS_FSMCO_HW_POWERDOWN BIT(15)
#define APS_FSMCO_WLON_RESET BIT(16)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index f42463e595cc..9fb2583ffffc 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -2078,7 +2078,6 @@ int rtl8xxxu_init_phy_regs(struct rtl8xxxu_priv *priv,
const struct rtl8xxxu_reg32val *array);
int rtl8xxxu_load_firmware(struct rtl8xxxu_priv *priv, const char *fw_name);
void rtl8xxxu_firmware_self_reset(struct rtl8xxxu_priv *priv);
-void rtl8xxxu_power_off(struct rtl8xxxu_priv *priv);
void rtl8xxxu_identify_vendor_1bit(struct rtl8xxxu_priv *priv, u32 vendor);
void rtl8xxxu_identify_vendor_2bits(struct rtl8xxxu_priv *priv, u32 vendor);
void rtl8xxxu_config_endpoints_sie(struct rtl8xxxu_priv *priv);
diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c
index e26feb8de658..09e5a16d7252 100644
--- a/drivers/net/wireless/realtek/rtlwifi/base.c
+++ b/drivers/net/wireless/realtek/rtlwifi/base.c
@@ -445,7 +445,7 @@ static int _rtl_init_deferred_work(struct ieee80211_hw *hw)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct workqueue_struct *wq;
- wq = alloc_workqueue("%s", 0, 0, rtlpriv->cfg->name);
+ wq = alloc_workqueue("%s", WQ_UNBOUND, 0, rtlpriv->cfg->name);
if (!wq)
return -ENOMEM;
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c
index 7252bc621211..9a9f9e14f472 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/fw.c
@@ -694,7 +694,7 @@ void rtl88e_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state)
if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) {
p2p_ps_offload->role = 1;
- p2p_ps_offload->allstasleep = -1;
+ p2p_ps_offload->allstasleep = 0;
} else {
p2p_ps_offload->role = 0;
}
diff --git a/drivers/net/wireless/realtek/rtw88/bf.c b/drivers/net/wireless/realtek/rtw88/bf.c
index c827c4a2814b..0d0ccbc7d00c 100644
--- a/drivers/net/wireless/realtek/rtw88/bf.c
+++ b/drivers/net/wireless/realtek/rtw88/bf.c
@@ -124,8 +124,11 @@ void rtw_bf_init_bfer_entry_mu(struct rtw_dev *rtwdev,
void rtw_bf_cfg_sounding(struct rtw_dev *rtwdev, struct rtw_vif *vif,
enum rtw_trx_desc_rate rate)
{
+ u8 csi_rsc = CSI_RSC_FOLLOW_RX_PACKET_BW;
u32 psf_ctl = 0;
- u8 csi_rsc = 0x1;
+
+ if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C)
+ csi_rsc = CSI_RSC_PRIMARY_20M_BW;
psf_ctl = rtw_read32(rtwdev, REG_BBPSF_CTRL) |
BIT_WMAC_USE_NDPARATE |
@@ -387,6 +390,9 @@ void rtw_bf_cfg_csi_rate(struct rtw_dev *rtwdev, u8 rssi, u8 cur_rate,
csi_cfg = rtw_read32(rtwdev, REG_BBPSF_CTRL) & ~BIT_MASK_CSI_RATE;
cur_rrsr = rtw_read16(rtwdev, REG_RRSR);
+ if (rtwdev->chip->id == RTW_CHIP_TYPE_8822C)
+ csi_cfg |= BIT_CSI_FORCE_RATE;
+
if (rssi >= 40) {
if (cur_rate != DESC_RATE54M) {
cur_rrsr |= BIT(DESC_RATE54M);
diff --git a/drivers/net/wireless/realtek/rtw88/bf.h b/drivers/net/wireless/realtek/rtw88/bf.h
index 7b40c2c03856..a5d3010e6be6 100644
--- a/drivers/net/wireless/realtek/rtw88/bf.h
+++ b/drivers/net/wireless/realtek/rtw88/bf.h
@@ -33,6 +33,7 @@
#define BIT_SHIFT_R_MU_RL 12
#define BIT_SHIFT_WMAC_TXMU_ACKPOLICY 4
#define BIT_SHIFT_CSI_RATE 24
+#define BIT_CSI_FORCE_RATE BIT(15)
#define BIT_MASK_R_MU_RL (R_MU_RL << BIT_SHIFT_R_MU_RL)
#define BIT_MASK_R_MU_TABLE_VALID 0x3f
@@ -48,6 +49,12 @@
#define RTW_SND_CTRL_REMOVE 0x98
#define RTW_SND_CTRL_SOUNDING 0x9B
+enum csi_rsc {
+ CSI_RSC_PRIMARY_20M_BW = 0,
+ CSI_RSC_FOLLOW_RX_PACKET_BW = 1,
+ CSI_RSC_DUPLICATE_MODE = 2,
+};
+
enum csi_seg_len {
HAL_CSI_SEG_4K = 0,
HAL_CSI_SEG_8K = 1,
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c
index 44e28e583964..2769b86ce1b2 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c
@@ -79,6 +79,8 @@ static const struct usb_device_id rtw_8822bu_id_table[] = {
.driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* D-Link DWA-T185 rev. A1 */
{ USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03d1, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* BUFFALO WI-U2-866DM */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03d0, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* BUFFALO WI-U3-866DHP */
{},
};
MODULE_DEVICE_TABLE(usb, rtw_8822bu_id_table);
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c
index 324fd5c8bfd4..755f76840b12 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c
@@ -21,6 +21,8 @@ static const struct usb_device_id rtw_8822cu_id_table[] = {
.driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x13b1, 0x0043, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, /* Alpha - Alpha */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3329, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&(rtw8822c_hw_spec) }, /* D-Link AC13U rev. A1 */
{},
};
MODULE_DEVICE_TABLE(usb, rtw_8822cu_id_table);
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index 3b5126ffc81a..009202c627d2 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -965,7 +965,8 @@ static int rtw_usb_init_rx(struct rtw_dev *rtwdev)
struct sk_buff *rx_skb;
int i;
- rtwusb->rxwq = alloc_workqueue("rtw88_usb: rx wq", WQ_BH, 0);
+ rtwusb->rxwq = alloc_workqueue("rtw88_usb: rx wq", WQ_BH | WQ_UNBOUND,
+ 0);
if (!rtwusb->rxwq) {
rtw_err(rtwdev, "failed to create RX work queue\n");
return -ENOMEM;
diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig
index 4288c30b400a..44d8a7f32bf2 100644
--- a/drivers/net/wireless/realtek/rtw89/Kconfig
+++ b/drivers/net/wireless/realtek/rtw89/Kconfig
@@ -74,6 +74,17 @@ config RTW89_8852AE
802.11ax PCIe wireless network (Wi-Fi 6) adapter
+config RTW89_8852AU
+ tristate "Realtek 8852AU USB wireless network (Wi-Fi 6) adapter"
+ depends on USB
+ select RTW89_CORE
+ select RTW89_USB
+ select RTW89_8852A
+ help
+ Select this option will enable support for 8852AU chipset
+
+ 802.11ax USB wireless network (Wi-Fi 6) adapter
+
config RTW89_8852BE
tristate "Realtek 8852BE PCI wireless network (Wi-Fi 6) adapter"
depends on PCI
@@ -121,6 +132,17 @@ config RTW89_8852CE
802.11ax PCIe wireless network (Wi-Fi 6E) adapter
+config RTW89_8852CU
+ tristate "Realtek 8852CU USB wireless network (Wi-Fi 6E) adapter"
+ depends on USB
+ select RTW89_CORE
+ select RTW89_USB
+ select RTW89_8852C
+ help
+ Select this option will enable support for 8852CU chipset
+
+ 802.11ax USB wireless network (Wi-Fi 6E) adapter
+
config RTW89_8922AE
tristate "Realtek 8922AE/8922AE-VS PCI wireless network (Wi-Fi 7) adapter"
depends on PCI
diff --git a/drivers/net/wireless/realtek/rtw89/Makefile b/drivers/net/wireless/realtek/rtw89/Makefile
index 23e43c444f69..1be81f254fca 100644
--- a/drivers/net/wireless/realtek/rtw89/Makefile
+++ b/drivers/net/wireless/realtek/rtw89/Makefile
@@ -43,6 +43,9 @@ rtw89_8852a-objs := rtw8852a.o \
obj-$(CONFIG_RTW89_8852AE) += rtw89_8852ae.o
rtw89_8852ae-objs := rtw8852ae.o
+obj-$(CONFIG_RTW89_8852AU) += rtw89_8852au.o
+rtw89_8852au-objs := rtw8852au.o
+
obj-$(CONFIG_RTW89_8852B_COMMON) += rtw89_8852b_common.o
rtw89_8852b_common-objs := rtw8852b_common.o
@@ -75,6 +78,9 @@ rtw89_8852c-objs := rtw8852c.o \
obj-$(CONFIG_RTW89_8852CE) += rtw89_8852ce.o
rtw89_8852ce-objs := rtw8852ce.o
+obj-$(CONFIG_RTW89_8852CU) += rtw89_8852cu.o
+rtw89_8852cu-objs := rtw8852cu.o
+
obj-$(CONFIG_RTW89_8922A) += rtw89_8922a.o
rtw89_8922a-objs := rtw8922a.o \
rtw8922a_rfk.o
diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c
index 385a238fe5cc..9370cbda945c 100644
--- a/drivers/net/wireless/realtek/rtw89/cam.c
+++ b/drivers/net/wireless/realtek/rtw89/cam.c
@@ -236,7 +236,8 @@ static int __rtw89_cam_detach_sec_cam(struct rtw89_dev *rtwdev,
if (ret)
rtw89_err(rtwdev,
"failed to update dctl cam del key: %d\n", ret);
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL,
+ RTW89_ROLE_INFO_CHANGE);
if (ret)
rtw89_err(rtwdev, "failed to update cam del key: %d\n", ret);
}
@@ -276,7 +277,8 @@ static int __rtw89_cam_attach_sec_cam(struct rtw89_dev *rtwdev,
ret);
return ret;
}
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL,
+ RTW89_ROLE_INFO_CHANGE);
if (ret) {
rtw89_err(rtwdev, "failed to update addr cam sec entry: %d\n",
ret);
@@ -760,7 +762,8 @@ int rtw89_cam_init(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link)
int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
- struct rtw89_sta_link *rtwsta_link, u8 *cmd)
+ struct rtw89_sta_link *rtwsta_link,
+ struct rtw89_h2c_addr_cam_v0 *h2c)
{
struct rtw89_bssid_cam_entry *bssid_cam = rtw89_get_bssid_cam_of(rtwvif_link,
rtwsta_link);
@@ -780,20 +783,19 @@ int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev,
rcu_read_unlock();
- FWCMD_SET_ADDR_BSSID_IDX(cmd, bssid_cam->bssid_cam_idx);
- FWCMD_SET_ADDR_BSSID_OFFSET(cmd, bssid_cam->offset);
- FWCMD_SET_ADDR_BSSID_LEN(cmd, bssid_cam->len);
- FWCMD_SET_ADDR_BSSID_VALID(cmd, bssid_cam->valid);
- FWCMD_SET_ADDR_BSSID_MASK(cmd, bss_mask);
- FWCMD_SET_ADDR_BSSID_BB_SEL(cmd, bssid_cam->phy_idx);
- FWCMD_SET_ADDR_BSSID_BSS_COLOR(cmd, bss_color);
-
- FWCMD_SET_ADDR_BSSID_BSSID0(cmd, bssid_cam->bssid[0]);
- FWCMD_SET_ADDR_BSSID_BSSID1(cmd, bssid_cam->bssid[1]);
- FWCMD_SET_ADDR_BSSID_BSSID2(cmd, bssid_cam->bssid[2]);
- FWCMD_SET_ADDR_BSSID_BSSID3(cmd, bssid_cam->bssid[3]);
- FWCMD_SET_ADDR_BSSID_BSSID4(cmd, bssid_cam->bssid[4]);
- FWCMD_SET_ADDR_BSSID_BSSID5(cmd, bssid_cam->bssid[5]);
+ h2c->w12 = le32_encode_bits(bssid_cam->bssid_cam_idx, ADDR_CAM_W12_BSSID_IDX) |
+ le32_encode_bits(bssid_cam->offset, ADDR_CAM_W12_BSSID_OFFSET) |
+ le32_encode_bits(bssid_cam->len, ADDR_CAM_W12_BSSID_LEN);
+ h2c->w13 = le32_encode_bits(bssid_cam->valid, ADDR_CAM_W13_BSSID_VALID) |
+ le32_encode_bits(bss_mask, ADDR_CAM_W13_BSSID_MASK) |
+ le32_encode_bits(bssid_cam->phy_idx, ADDR_CAM_W13_BSSID_BB_SEL) |
+ le32_encode_bits(bss_color, ADDR_CAM_W13_BSSID_BSS_COLOR) |
+ le32_encode_bits(bssid_cam->bssid[0], ADDR_CAM_W13_BSSID_BSSID0) |
+ le32_encode_bits(bssid_cam->bssid[1], ADDR_CAM_W13_BSSID_BSSID1);
+ h2c->w14 = le32_encode_bits(bssid_cam->bssid[2], ADDR_CAM_W14_BSSID_BSSID2) |
+ le32_encode_bits(bssid_cam->bssid[3], ADDR_CAM_W14_BSSID_BSSID3) |
+ le32_encode_bits(bssid_cam->bssid[4], ADDR_CAM_W14_BSSID_BSSID4) |
+ le32_encode_bits(bssid_cam->bssid[5], ADDR_CAM_W14_BSSID_BSSID5);
return 0;
}
@@ -813,18 +815,21 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link,
const u8 *scan_mac_addr,
- u8 *cmd)
+ struct rtw89_h2c_addr_cam_v0 *h2c)
{
struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link);
struct rtw89_addr_cam_entry *addr_cam =
rtw89_get_addr_cam_of(rtwvif_link, rtwsta_link);
struct ieee80211_sta *sta = rtwsta_link_to_sta_safe(rtwsta_link);
+ const struct rtw89_chip_info *chip = rtwdev->chip;
struct ieee80211_link_sta *link_sta;
const u8 *sma = scan_mac_addr ? scan_mac_addr : rtwvif_link->mac_addr;
u8 sma_hash, tma_hash, addr_msk_start;
+ u8 ver = chip->addrcam_ver;
u8 sma_start = 0;
u8 tma_start = 0;
const u8 *tma;
+ u8 mac_id;
rcu_read_lock();
@@ -845,69 +850,79 @@ void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev,
sma_hash = rtw89_cam_addr_hash(sma_start, sma);
tma_hash = rtw89_cam_addr_hash(tma_start, tma);
- FWCMD_SET_ADDR_IDX(cmd, addr_cam->addr_cam_idx);
- FWCMD_SET_ADDR_OFFSET(cmd, addr_cam->offset);
- FWCMD_SET_ADDR_LEN(cmd, addr_cam->len);
-
- FWCMD_SET_ADDR_VALID(cmd, addr_cam->valid);
- FWCMD_SET_ADDR_NET_TYPE(cmd, rtwvif_link->net_type);
- FWCMD_SET_ADDR_BCN_HIT_COND(cmd, rtwvif_link->bcn_hit_cond);
- FWCMD_SET_ADDR_HIT_RULE(cmd, rtwvif_link->hit_rule);
- FWCMD_SET_ADDR_BB_SEL(cmd, rtwvif_link->phy_idx);
- FWCMD_SET_ADDR_ADDR_MASK(cmd, addr_cam->addr_mask);
- FWCMD_SET_ADDR_MASK_SEL(cmd, addr_cam->mask_sel);
- FWCMD_SET_ADDR_SMA_HASH(cmd, sma_hash);
- FWCMD_SET_ADDR_TMA_HASH(cmd, tma_hash);
-
- FWCMD_SET_ADDR_BSSID_CAM_IDX(cmd, addr_cam->bssid_cam_idx);
-
- FWCMD_SET_ADDR_SMA0(cmd, sma[0]);
- FWCMD_SET_ADDR_SMA1(cmd, sma[1]);
- FWCMD_SET_ADDR_SMA2(cmd, sma[2]);
- FWCMD_SET_ADDR_SMA3(cmd, sma[3]);
- FWCMD_SET_ADDR_SMA4(cmd, sma[4]);
- FWCMD_SET_ADDR_SMA5(cmd, sma[5]);
-
- FWCMD_SET_ADDR_TMA0(cmd, tma[0]);
- FWCMD_SET_ADDR_TMA1(cmd, tma[1]);
- FWCMD_SET_ADDR_TMA2(cmd, tma[2]);
- FWCMD_SET_ADDR_TMA3(cmd, tma[3]);
- FWCMD_SET_ADDR_TMA4(cmd, tma[4]);
- FWCMD_SET_ADDR_TMA5(cmd, tma[5]);
-
- FWCMD_SET_ADDR_PORT_INT(cmd, rtwvif_link->port);
- FWCMD_SET_ADDR_TSF_SYNC(cmd, rtwvif_link->port);
- FWCMD_SET_ADDR_TF_TRS(cmd, rtwvif_link->trigger);
- FWCMD_SET_ADDR_LSIG_TXOP(cmd, rtwvif_link->lsig_txop);
- FWCMD_SET_ADDR_TGT_IND(cmd, rtwvif_link->tgt_ind);
- FWCMD_SET_ADDR_FRM_TGT_IND(cmd, rtwvif_link->frm_tgt_ind);
- FWCMD_SET_ADDR_MACID(cmd, rtwsta_link ? rtwsta_link->mac_id :
- rtwvif_link->mac_id);
+ mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id;
+
+ if (ver == 0)
+ h2c->w1 = le32_encode_bits(addr_cam->addr_cam_idx, ADDR_CAM_W1_IDX) |
+ le32_encode_bits(addr_cam->offset, ADDR_CAM_W1_OFFSET) |
+ le32_encode_bits(addr_cam->len, ADDR_CAM_W1_LEN);
+ else
+ h2c->w1 = le32_encode_bits(addr_cam->addr_cam_idx, ADDR_CAM_W1_V1_IDX) |
+ le32_encode_bits(addr_cam->offset, ADDR_CAM_W1_V1_OFFSET) |
+ le32_encode_bits(addr_cam->len, ADDR_CAM_W1_V1_LEN);
+
+ h2c->w2 = le32_encode_bits(addr_cam->valid, ADDR_CAM_W2_VALID) |
+ le32_encode_bits(rtwvif_link->net_type, ADDR_CAM_W2_NET_TYPE) |
+ le32_encode_bits(rtwvif_link->bcn_hit_cond, ADDR_CAM_W2_BCN_HIT_COND) |
+ le32_encode_bits(rtwvif_link->hit_rule, ADDR_CAM_W2_HIT_RULE) |
+ le32_encode_bits(rtwvif_link->phy_idx, ADDR_CAM_W2_BB_SEL) |
+ le32_encode_bits(addr_cam->addr_mask, ADDR_CAM_W2_ADDR_MASK) |
+ le32_encode_bits(addr_cam->mask_sel, ADDR_CAM_W2_MASK_SEL) |
+ le32_encode_bits(sma_hash, ADDR_CAM_W2_SMA_HASH) |
+ le32_encode_bits(tma_hash, ADDR_CAM_W2_TMA_HASH);
+ h2c->w3 = le32_encode_bits(addr_cam->bssid_cam_idx, ADDR_CAM_W3_BSSID_CAM_IDX);
+ h2c->w4 = le32_encode_bits(sma[0], ADDR_CAM_W4_SMA0) |
+ le32_encode_bits(sma[1], ADDR_CAM_W4_SMA1) |
+ le32_encode_bits(sma[2], ADDR_CAM_W4_SMA2) |
+ le32_encode_bits(sma[3], ADDR_CAM_W4_SMA3);
+ h2c->w5 = le32_encode_bits(sma[4], ADDR_CAM_W5_SMA4) |
+ le32_encode_bits(sma[5], ADDR_CAM_W5_SMA5) |
+ le32_encode_bits(tma[0], ADDR_CAM_W5_TMA0) |
+ le32_encode_bits(tma[1], ADDR_CAM_W5_TMA1);
+ h2c->w6 = le32_encode_bits(tma[2], ADDR_CAM_W6_TMA2) |
+ le32_encode_bits(tma[3], ADDR_CAM_W6_TMA3) |
+ le32_encode_bits(tma[4], ADDR_CAM_W6_TMA4) |
+ le32_encode_bits(tma[5], ADDR_CAM_W6_TMA5);
+ if (ver == 0)
+ h2c->w8 = le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_PORT_INT) |
+ le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_TSF_SYNC) |
+ le32_encode_bits(rtwvif_link->trigger, ADDR_CAM_W8_TF_TRS) |
+ le32_encode_bits(rtwvif_link->lsig_txop, ADDR_CAM_W8_LSIG_TXOP) |
+ le32_encode_bits(rtwvif_link->tgt_ind, ADDR_CAM_W8_TGT_IND) |
+ le32_encode_bits(rtwvif_link->frm_tgt_ind, ADDR_CAM_W8_FRM_TGT_IND) |
+ le32_encode_bits(mac_id, ADDR_CAM_W8_MACID);
+ else
+ h2c->w8 = le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_V1_PORT_INT) |
+ le32_encode_bits(rtwvif_link->port, ADDR_CAM_W8_V1_TSF_SYNC) |
+ le32_encode_bits(rtwvif_link->trigger, ADDR_CAM_W8_V1_TF_TRS) |
+ le32_encode_bits(rtwvif_link->lsig_txop, ADDR_CAM_W8_V1_LSIG_TXOP) |
+ le32_encode_bits(mac_id, ADDR_CAM_W8_V1_MACID);
+
if (rtwvif_link->net_type == RTW89_NET_TYPE_INFRA)
- FWCMD_SET_ADDR_AID12(cmd, vif->cfg.aid & 0xfff);
+ h2c->w9 = le32_encode_bits(vif->cfg.aid & 0xfff, ADDR_CAM_W9_AID12);
else if (rtwvif_link->net_type == RTW89_NET_TYPE_AP_MODE)
- FWCMD_SET_ADDR_AID12(cmd, sta ? sta->aid & 0xfff : 0);
- FWCMD_SET_ADDR_WOL_PATTERN(cmd, rtwvif_link->wowlan_pattern);
- FWCMD_SET_ADDR_WOL_UC(cmd, rtwvif_link->wowlan_uc);
- FWCMD_SET_ADDR_WOL_MAGIC(cmd, rtwvif_link->wowlan_magic);
- FWCMD_SET_ADDR_WAPI(cmd, addr_cam->wapi);
- FWCMD_SET_ADDR_SEC_ENT_MODE(cmd, addr_cam->sec_ent_mode);
- FWCMD_SET_ADDR_SEC_ENT0_KEYID(cmd, addr_cam->sec_ent_keyid[0]);
- FWCMD_SET_ADDR_SEC_ENT1_KEYID(cmd, addr_cam->sec_ent_keyid[1]);
- FWCMD_SET_ADDR_SEC_ENT2_KEYID(cmd, addr_cam->sec_ent_keyid[2]);
- FWCMD_SET_ADDR_SEC_ENT3_KEYID(cmd, addr_cam->sec_ent_keyid[3]);
- FWCMD_SET_ADDR_SEC_ENT4_KEYID(cmd, addr_cam->sec_ent_keyid[4]);
- FWCMD_SET_ADDR_SEC_ENT5_KEYID(cmd, addr_cam->sec_ent_keyid[5]);
- FWCMD_SET_ADDR_SEC_ENT6_KEYID(cmd, addr_cam->sec_ent_keyid[6]);
-
- FWCMD_SET_ADDR_SEC_ENT_VALID(cmd, addr_cam->sec_cam_map[0] & 0xff);
- FWCMD_SET_ADDR_SEC_ENT0(cmd, addr_cam->sec_ent[0]);
- FWCMD_SET_ADDR_SEC_ENT1(cmd, addr_cam->sec_ent[1]);
- FWCMD_SET_ADDR_SEC_ENT2(cmd, addr_cam->sec_ent[2]);
- FWCMD_SET_ADDR_SEC_ENT3(cmd, addr_cam->sec_ent[3]);
- FWCMD_SET_ADDR_SEC_ENT4(cmd, addr_cam->sec_ent[4]);
- FWCMD_SET_ADDR_SEC_ENT5(cmd, addr_cam->sec_ent[5]);
- FWCMD_SET_ADDR_SEC_ENT6(cmd, addr_cam->sec_ent[6]);
+ h2c->w9 = le32_encode_bits(sta ? sta->aid & 0xfff : 0, ADDR_CAM_W9_AID12);
+
+ h2c->w9 |= le32_encode_bits(rtwvif_link->wowlan_pattern, ADDR_CAM_W9_WOL_PATTERN) |
+ le32_encode_bits(rtwvif_link->wowlan_uc, ADDR_CAM_W9_WOL_UC) |
+ le32_encode_bits(rtwvif_link->wowlan_magic, ADDR_CAM_W9_WOL_MAGIC) |
+ le32_encode_bits(addr_cam->wapi, ADDR_CAM_W9_WAPI) |
+ le32_encode_bits(addr_cam->sec_ent_mode, ADDR_CAM_W9_SEC_ENT_MODE) |
+ le32_encode_bits(addr_cam->sec_ent_keyid[0], ADDR_CAM_W9_SEC_ENT0_KEYID) |
+ le32_encode_bits(addr_cam->sec_ent_keyid[1], ADDR_CAM_W9_SEC_ENT1_KEYID) |
+ le32_encode_bits(addr_cam->sec_ent_keyid[2], ADDR_CAM_W9_SEC_ENT2_KEYID) |
+ le32_encode_bits(addr_cam->sec_ent_keyid[3], ADDR_CAM_W9_SEC_ENT3_KEYID) |
+ le32_encode_bits(addr_cam->sec_ent_keyid[4], ADDR_CAM_W9_SEC_ENT4_KEYID) |
+ le32_encode_bits(addr_cam->sec_ent_keyid[5], ADDR_CAM_W9_SEC_ENT5_KEYID) |
+ le32_encode_bits(addr_cam->sec_ent_keyid[6], ADDR_CAM_W9_SEC_ENT6_KEYID);
+ h2c->w10 = le32_encode_bits(addr_cam->sec_cam_map[0] & 0xff, ADDR_CAM_W10_SEC_ENT_VALID) |
+ le32_encode_bits(addr_cam->sec_ent[0], ADDR_CAM_W10_SEC_ENT0) |
+ le32_encode_bits(addr_cam->sec_ent[1], ADDR_CAM_W10_SEC_ENT1) |
+ le32_encode_bits(addr_cam->sec_ent[2], ADDR_CAM_W10_SEC_ENT2);
+ h2c->w11 = le32_encode_bits(addr_cam->sec_ent[3], ADDR_CAM_W11_SEC_ENT3) |
+ le32_encode_bits(addr_cam->sec_ent[4], ADDR_CAM_W11_SEC_ENT4) |
+ le32_encode_bits(addr_cam->sec_ent[5], ADDR_CAM_W11_SEC_ENT5) |
+ le32_encode_bits(addr_cam->sec_ent[6], ADDR_CAM_W11_SEC_ENT6);
rcu_read_unlock();
}
diff --git a/drivers/net/wireless/realtek/rtw89/cam.h b/drivers/net/wireless/realtek/rtw89/cam.h
index 8fd2d776408e..c46b6f91bbdb 100644
--- a/drivers/net/wireless/realtek/rtw89/cam.h
+++ b/drivers/net/wireless/realtek/rtw89/cam.h
@@ -12,345 +12,109 @@
#define RTW89_BSSID_MATCH_ALL GENMASK(5, 0)
#define RTW89_BSSID_MATCH_5_BYTES GENMASK(4, 0)
-static inline void FWCMD_SET_ADDR_IDX(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_OFFSET(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(15, 8));
-}
-
-static inline void FWCMD_SET_ADDR_LEN(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 1, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_VALID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, BIT(0));
-}
-
-static inline void FWCMD_SET_ADDR_NET_TYPE(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(2, 1));
-}
-
-static inline void FWCMD_SET_ADDR_BCN_HIT_COND(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(4, 3));
-}
-
-static inline void FWCMD_SET_ADDR_HIT_RULE(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(6, 5));
-}
-
-static inline void FWCMD_SET_ADDR_BB_SEL(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, BIT(7));
-}
-
-static inline void FWCMD_SET_ADDR_ADDR_MASK(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(13, 8));
-}
-
-static inline void FWCMD_SET_ADDR_MASK_SEL(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(15, 14));
-}
-
-static inline void FWCMD_SET_ADDR_SMA_HASH(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_TMA_HASH(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 2, value, GENMASK(31, 24));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_CAM_IDX(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 3, value, GENMASK(5, 0));
-}
-
-static inline void FWCMD_SET_ADDR_SMA0(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_SMA1(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(15, 8));
-}
-
-static inline void FWCMD_SET_ADDR_SMA2(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_SMA3(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 4, value, GENMASK(31, 24));
-}
-
-static inline void FWCMD_SET_ADDR_SMA4(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_SMA5(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(15, 8));
-}
-
-static inline void FWCMD_SET_ADDR_TMA0(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_TMA1(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 5, value, GENMASK(31, 24));
-}
-
-static inline void FWCMD_SET_ADDR_TMA2(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_TMA3(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(15, 8));
-}
-
-static inline void FWCMD_SET_ADDR_TMA4(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_TMA5(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 6, value, GENMASK(31, 24));
-}
-
-static inline void FWCMD_SET_ADDR_MACID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_PORT_INT(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(10, 8));
-}
-
-static inline void FWCMD_SET_ADDR_TSF_SYNC(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(13, 11));
-}
-
-static inline void FWCMD_SET_ADDR_TF_TRS(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 8, value, BIT(14));
-}
-
-static inline void FWCMD_SET_ADDR_LSIG_TXOP(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 8, value, BIT(15));
-}
-
-static inline void FWCMD_SET_ADDR_TGT_IND(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(26, 24));
-}
-
-static inline void FWCMD_SET_ADDR_FRM_TGT_IND(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 8, value, GENMASK(29, 27));
-}
-
-static inline void FWCMD_SET_ADDR_AID12(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(11, 0));
-}
-
-static inline void FWCMD_SET_ADDR_AID12_0(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_AID12_1(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(11, 8));
-}
-
-static inline void FWCMD_SET_ADDR_WOL_PATTERN(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(12));
-}
-
-static inline void FWCMD_SET_ADDR_WOL_UC(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(13));
-}
-
-static inline void FWCMD_SET_ADDR_WOL_MAGIC(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(14));
-}
-
-static inline void FWCMD_SET_ADDR_WAPI(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, BIT(15));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT_MODE(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(17, 16));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT0_KEYID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(19, 18));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT1_KEYID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(21, 20));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT2_KEYID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(23, 22));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT3_KEYID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(25, 24));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT4_KEYID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(27, 26));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT5_KEYID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(29, 28));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT6_KEYID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 9, value, GENMASK(31, 30));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT_VALID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT0(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(15, 8));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT1(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT2(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 10, value, GENMASK(31, 24));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT3(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT4(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(15, 8));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT5(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_SEC_ENT6(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 11, value, GENMASK(31, 24));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_IDX(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_OFFSET(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(15, 8));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_LEN(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 12, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_VALID(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 13, value, BIT(0));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_BB_SEL(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 13, value, BIT(1));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_MASK(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(7, 2));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_BSS_COLOR(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(13, 8));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_BSSID0(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(23, 16));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_BSSID1(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 13, value, GENMASK(31, 24));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_BSSID2(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(7, 0));
-}
-
-static inline void FWCMD_SET_ADDR_BSSID_BSSID3(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(15, 8));
-}
+struct rtw89_h2c_addr_cam_v0 {
+ __le32 w0;
+ __le32 w1;
+ __le32 w2;
+ __le32 w3;
+ __le32 w4;
+ __le32 w5;
+ __le32 w6;
+ __le32 w7;
+ __le32 w8;
+ __le32 w9;
+ __le32 w10;
+ __le32 w11;
+ __le32 w12;
+ __le32 w13;
+ __le32 w14;
+} __packed;
-static inline void FWCMD_SET_ADDR_BSSID_BSSID4(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(23, 16));
-}
+struct rtw89_h2c_addr_cam {
+ struct rtw89_h2c_addr_cam_v0 v0;
+ __le32 w15;
+} __packed;
-static inline void FWCMD_SET_ADDR_BSSID_BSSID5(void *cmd, u32 value)
-{
- le32p_replace_bits((__le32 *)(cmd) + 14, value, GENMASK(31, 24));
-}
+#define ADDR_CAM_W1_IDX GENMASK(7, 0)
+#define ADDR_CAM_W1_OFFSET GENMASK(15, 8)
+#define ADDR_CAM_W1_LEN GENMASK(23, 16)
+#define ADDR_CAM_W1_V1_IDX GENMASK(9, 0)
+#define ADDR_CAM_W1_V1_OFFSET GENMASK(23, 16)
+#define ADDR_CAM_W1_V1_LEN GENMASK(31, 24)
+#define ADDR_CAM_W2_VALID BIT(0)
+#define ADDR_CAM_W2_NET_TYPE GENMASK(2, 1)
+#define ADDR_CAM_W2_BCN_HIT_COND GENMASK(4, 3)
+#define ADDR_CAM_W2_HIT_RULE GENMASK(6, 5)
+#define ADDR_CAM_W2_BB_SEL BIT(7)
+#define ADDR_CAM_W2_ADDR_MASK GENMASK(13, 8)
+#define ADDR_CAM_W2_MASK_SEL GENMASK(15, 14)
+#define ADDR_CAM_W2_SMA_HASH GENMASK(23, 16)
+#define ADDR_CAM_W2_TMA_HASH GENMASK(31, 24)
+#define ADDR_CAM_W3_BSSID_CAM_IDX GENMASK(5, 0)
+#define ADDR_CAM_W4_SMA0 GENMASK(7, 0)
+#define ADDR_CAM_W4_SMA1 GENMASK(15, 8)
+#define ADDR_CAM_W4_SMA2 GENMASK(23, 16)
+#define ADDR_CAM_W4_SMA3 GENMASK(31, 24)
+#define ADDR_CAM_W5_SMA4 GENMASK(7, 0)
+#define ADDR_CAM_W5_SMA5 GENMASK(15, 8)
+#define ADDR_CAM_W5_TMA0 GENMASK(23, 16)
+#define ADDR_CAM_W5_TMA1 GENMASK(31, 24)
+#define ADDR_CAM_W6_TMA2 GENMASK(7, 0)
+#define ADDR_CAM_W6_TMA3 GENMASK(15, 8)
+#define ADDR_CAM_W6_TMA4 GENMASK(23, 16)
+#define ADDR_CAM_W6_TMA5 GENMASK(31, 24)
+#define ADDR_CAM_W8_MACID GENMASK(7, 0)
+#define ADDR_CAM_W8_PORT_INT GENMASK(10, 8)
+#define ADDR_CAM_W8_TSF_SYNC GENMASK(13, 11)
+#define ADDR_CAM_W8_TF_TRS BIT(14)
+#define ADDR_CAM_W8_LSIG_TXOP BIT(15)
+#define ADDR_CAM_W8_TGT_IND GENMASK(26, 24)
+#define ADDR_CAM_W8_FRM_TGT_IND GENMASK(29, 27)
+#define ADDR_CAM_W8_V1_MACID GENMASK(9, 0)
+#define ADDR_CAM_W8_V1_PORT_INT GENMASK(18, 16)
+#define ADDR_CAM_W8_V1_TSF_SYNC GENMASK(21, 19)
+#define ADDR_CAM_W8_V1_TF_TRS BIT(22)
+#define ADDR_CAM_W8_V1_LSIG_TXOP BIT(23)
+#define ADDR_CAM_W8_V1_TB_RANGING BIT(24)
+#define ADDR_CAM_W8_V1_TB_SENSING BIT(25)
+#define ADDR_CAM_W8_V1_SENS_EN BIT(26)
+#define ADDR_CAM_W9_AID12 GENMASK(11, 0)
+#define ADDR_CAM_W9_AID12_0 GENMASK(7, 0)
+#define ADDR_CAM_W9_AID12_1 GENMASK(11, 8)
+#define ADDR_CAM_W9_WOL_PATTERN BIT(12)
+#define ADDR_CAM_W9_WOL_UC BIT(13)
+#define ADDR_CAM_W9_WOL_MAGIC BIT(14)
+#define ADDR_CAM_W9_WAPI BIT(15)
+#define ADDR_CAM_W9_SEC_ENT_MODE GENMASK(17, 16)
+#define ADDR_CAM_W9_SEC_ENT0_KEYID GENMASK(19, 18)
+#define ADDR_CAM_W9_SEC_ENT1_KEYID GENMASK(21, 20)
+#define ADDR_CAM_W9_SEC_ENT2_KEYID GENMASK(23, 22)
+#define ADDR_CAM_W9_SEC_ENT3_KEYID GENMASK(25, 24)
+#define ADDR_CAM_W9_SEC_ENT4_KEYID GENMASK(27, 26)
+#define ADDR_CAM_W9_SEC_ENT5_KEYID GENMASK(29, 28)
+#define ADDR_CAM_W9_SEC_ENT6_KEYID GENMASK(31, 30)
+#define ADDR_CAM_W10_SEC_ENT_VALID GENMASK(7, 0)
+#define ADDR_CAM_W10_SEC_ENT0 GENMASK(15, 8)
+#define ADDR_CAM_W10_SEC_ENT1 GENMASK(23, 16)
+#define ADDR_CAM_W10_SEC_ENT2 GENMASK(31, 24)
+#define ADDR_CAM_W11_SEC_ENT3 GENMASK(7, 0)
+#define ADDR_CAM_W11_SEC_ENT4 GENMASK(15, 8)
+#define ADDR_CAM_W11_SEC_ENT5 GENMASK(23, 16)
+#define ADDR_CAM_W11_SEC_ENT6 GENMASK(31, 24)
+#define ADDR_CAM_W12_BSSID_IDX GENMASK(7, 0)
+#define ADDR_CAM_W12_BSSID_OFFSET GENMASK(15, 8)
+#define ADDR_CAM_W12_BSSID_LEN GENMASK(23, 16)
+#define ADDR_CAM_W13_BSSID_VALID BIT(0)
+#define ADDR_CAM_W13_BSSID_BB_SEL BIT(1)
+#define ADDR_CAM_W13_BSSID_MASK GENMASK(7, 2)
+#define ADDR_CAM_W13_BSSID_BSS_COLOR GENMASK(13, 8)
+#define ADDR_CAM_W13_BSSID_BSSID0 GENMASK(23, 16)
+#define ADDR_CAM_W13_BSSID_BSSID1 GENMASK(31, 24)
+#define ADDR_CAM_W14_BSSID_BSSID2 GENMASK(7, 0)
+#define ADDR_CAM_W14_BSSID_BSSID3 GENMASK(15, 8)
+#define ADDR_CAM_W14_BSSID_BSSID4 GENMASK(23, 16)
+#define ADDR_CAM_W14_BSSID_BSSID5 GENMASK(31, 24)
+#define ADDR_CAM_W15_UPD_MODE GENMASK(2, 0)
struct rtw89_h2c_dctlinfo_ud_v1 {
__le32 c0;
@@ -552,9 +316,10 @@ int rtw89_cam_init_bssid_cam(struct rtw89_dev *rtwdev,
void rtw89_cam_deinit_bssid_cam(struct rtw89_dev *rtwdev,
struct rtw89_bssid_cam_entry *bssid_cam);
void rtw89_cam_fill_addr_cam_info(struct rtw89_dev *rtwdev,
- struct rtw89_vif_link *vif,
+ struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link,
- const u8 *scan_mac_addr, u8 *cmd);
+ const u8 *scan_mac_addr,
+ struct rtw89_h2c_addr_cam_v0 *h2c);
void rtw89_cam_fill_dctl_sec_cam_info_v1(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link,
@@ -565,7 +330,8 @@ void rtw89_cam_fill_dctl_sec_cam_info_v2(struct rtw89_dev *rtwdev,
struct rtw89_h2c_dctlinfo_ud_v2 *h2c);
int rtw89_cam_fill_bssid_cam_info(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
- struct rtw89_sta_link *rtwsta_link, u8 *cmd);
+ struct rtw89_sta_link *rtwsta_link,
+ struct rtw89_h2c_addr_cam_v0 *h2c);
int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 917b2adede61..0824940c91ae 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -321,6 +321,26 @@ static const struct ieee80211_supported_band rtw89_sband_6ghz = {
.n_bitrates = ARRAY_SIZE(rtw89_bitrates) - 4,
};
+static const struct rtw89_hw_rate_def {
+ enum rtw89_hw_rate ht;
+ enum rtw89_hw_rate vht[RTW89_NSS_NUM];
+} rtw89_hw_rate[RTW89_CHIP_GEN_NUM] = {
+ [RTW89_CHIP_AX] = {
+ .ht = RTW89_HW_RATE_MCS0,
+ .vht = {RTW89_HW_RATE_VHT_NSS1_MCS0,
+ RTW89_HW_RATE_VHT_NSS2_MCS0,
+ RTW89_HW_RATE_VHT_NSS3_MCS0,
+ RTW89_HW_RATE_VHT_NSS4_MCS0},
+ },
+ [RTW89_CHIP_BE] = {
+ .ht = RTW89_HW_RATE_V1_MCS0,
+ .vht = {RTW89_HW_RATE_V1_VHT_NSS1_MCS0,
+ RTW89_HW_RATE_V1_VHT_NSS2_MCS0,
+ RTW89_HW_RATE_V1_VHT_NSS3_MCS0,
+ RTW89_HW_RATE_V1_VHT_NSS4_MCS0},
+ },
+};
+
static void __rtw89_traffic_stats_accu(struct rtw89_traffic_stats *stats,
struct sk_buff *skb, bool tx)
{
@@ -450,6 +470,22 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev)
__rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_1);
}
+static void rtw89_chip_rfk_channel_for_pure_mon_vif(struct rtw89_dev *rtwdev,
+ enum rtw89_phy_idx phy_idx)
+{
+ struct rtw89_vif *rtwvif = rtwdev->pure_monitor_mode_vif;
+ struct rtw89_vif_link *rtwvif_link;
+
+ if (!rtwvif)
+ return;
+
+ rtwvif_link = rtw89_vif_get_link_inst(rtwvif, phy_idx);
+ if (!rtwvif_link)
+ return;
+
+ rtw89_chip_rfk_channel(rtwdev, rtwvif_link);
+}
+
static void __rtw89_set_channel(struct rtw89_dev *rtwdev,
const struct rtw89_chan *chan,
enum rtw89_mac_idx mac_idx,
@@ -478,6 +514,8 @@ static void __rtw89_set_channel(struct rtw89_dev *rtwdev,
}
rtw89_set_entity_state(rtwdev, phy_idx, true);
+
+ rtw89_chip_rfk_channel_for_pure_mon_vif(rtwdev, phy_idx);
}
int rtw89_set_channel(struct rtw89_dev *rtwdev)
@@ -759,6 +797,25 @@ u8 rtw89_core_get_ch_dma_v1(struct rtw89_dev *rtwdev, u8 qsel)
}
EXPORT_SYMBOL(rtw89_core_get_ch_dma_v1);
+u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel)
+{
+ switch (qsel) {
+ default:
+ rtw89_warn(rtwdev, "Cannot map qsel to dma v2: %d\n", qsel);
+ fallthrough;
+ case RTW89_TX_QSEL_BE_0:
+ case RTW89_TX_QSEL_VO_0:
+ return RTW89_TXCH_ACH0;
+ case RTW89_TX_QSEL_BK_0:
+ case RTW89_TX_QSEL_VI_0:
+ return RTW89_TXCH_ACH2;
+ case RTW89_TX_QSEL_B0_MGMT:
+ case RTW89_TX_QSEL_B0_HI:
+ return RTW89_TXCH_CH8;
+ }
+}
+EXPORT_SYMBOL(rtw89_core_get_ch_dma_v2);
+
static void
rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev,
struct rtw89_core_tx_request *tx_req)
@@ -1078,6 +1135,44 @@ notify:
rtw89_mac_notify_wake(rtwdev);
}
+static void rtw89_core_tx_update_injection(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req,
+ struct ieee80211_tx_info *info)
+{
+ const struct rtw89_hw_rate_def *hw_rate = &rtw89_hw_rate[rtwdev->chip->chip_gen];
+ enum mac80211_rate_control_flags flags = info->control.rates[0].flags;
+ struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
+ const struct rtw89_chan *chan;
+ u8 idx = info->control.rates[0].idx;
+ u8 nss, mcs;
+
+ desc_info->use_rate = true;
+ desc_info->dis_data_fb = true;
+
+ if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+ desc_info->data_bw = 3;
+ else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ desc_info->data_bw = 2;
+ else if (flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ desc_info->data_bw = 1;
+
+ if (flags & IEEE80211_TX_RC_SHORT_GI)
+ desc_info->gi_ltf = 1;
+
+ if (flags & IEEE80211_TX_RC_VHT_MCS) {
+ nss = umin(idx >> 4, ARRAY_SIZE(hw_rate->vht) - 1);
+ mcs = idx & 0xf;
+ desc_info->data_rate = hw_rate->vht[nss] + mcs;
+ } else if (flags & IEEE80211_TX_RC_MCS) {
+ desc_info->data_rate = hw_rate->ht + idx;
+ } else {
+ chan = rtw89_chan_get(rtwdev, tx_req->rtwvif_link->chanctx_idx);
+
+ desc_info->data_rate = idx + (chan->band_type == RTW89_BAND_2G ?
+ RTW89_HW_RATE_CCK1 : RTW89_HW_RATE_OFDM6);
+ }
+}
+
static void
rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
struct rtw89_core_tx_request *tx_req)
@@ -1087,32 +1182,38 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (void *)skb->data;
struct rtw89_addr_cam_entry *addr_cam;
- enum rtw89_core_tx_type tx_type;
enum btc_pkt_type pkt_type;
bool upd_wlan_hdr = false;
bool is_bmc;
u16 seq;
+ desc_info->pkt_size = skb->len;
+
+ if (unlikely(tx_req->tx_type == RTW89_CORE_TX_TYPE_FWCMD)) {
+ rtw89_core_tx_update_h2c_info(rtwdev, tx_req);
+ return;
+ }
+
+ tx_req->tx_type = rtw89_core_get_tx_type(rtwdev, skb);
+
if (tx_req->sta)
desc_info->mlo = tx_req->sta->mlo;
else if (tx_req->vif)
desc_info->mlo = ieee80211_vif_is_mld(tx_req->vif);
seq = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
- if (tx_req->tx_type != RTW89_CORE_TX_TYPE_FWCMD) {
- tx_type = rtw89_core_get_tx_type(rtwdev, skb);
- tx_req->tx_type = tx_type;
+ addr_cam = rtw89_get_addr_cam_of(tx_req->rtwvif_link,
+ tx_req->rtwsta_link);
+ if (addr_cam->valid && desc_info->mlo)
+ upd_wlan_hdr = true;
+
+ if (rtw89_is_tx_rpt_skb(rtwdev, tx_req->skb))
+ rtw89_tx_rpt_init(rtwdev, tx_req);
- addr_cam = rtw89_get_addr_cam_of(tx_req->rtwvif_link,
- tx_req->rtwsta_link);
- if (addr_cam->valid && desc_info->mlo)
- upd_wlan_hdr = true;
- }
is_bmc = (is_broadcast_ether_addr(hdr->addr1) ||
is_multicast_ether_addr(hdr->addr1));
desc_info->seq = seq;
- desc_info->pkt_size = skb->len;
desc_info->is_bmc = is_bmc;
desc_info->wd_page = true;
desc_info->hiq = info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM;
@@ -1129,10 +1230,12 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
rtw89_core_tx_update_ampdu_info(rtwdev, tx_req, pkt_type);
rtw89_core_tx_update_llc_hdr(rtwdev, desc_info, skb);
break;
- case RTW89_CORE_TX_TYPE_FWCMD:
- rtw89_core_tx_update_h2c_info(rtwdev, tx_req);
+ default:
break;
}
+
+ if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
+ rtw89_core_tx_update_injection(rtwdev, tx_req, info);
}
static void rtw89_tx_wait_work(struct wiphy *wiphy, struct wiphy_work *work)
@@ -1239,14 +1342,13 @@ static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev,
tx_req.rtwvif_link = rtwvif_link;
tx_req.rtwsta_link = rtwsta_link;
tx_req.desc_info.sw_mld = sw_mld;
+ rcu_assign_pointer(skb_data->wait, wait);
rtw89_traffic_stats_accu(rtwdev, rtwvif, skb, true, true);
rtw89_wow_parse_akm(rtwdev, skb);
rtw89_core_tx_update_desc_info(rtwdev, &tx_req);
rtw89_core_tx_wake(rtwdev, &tx_req);
- rcu_assign_pointer(skb_data->wait, wait);
-
ret = rtw89_hci_tx_write(rtwdev, &tx_req);
if (ret) {
rtw89_err(rtwdev, "failed to transmit skb to HCI\n");
@@ -1362,6 +1464,8 @@ static __le32 rtw89_build_txwd_body5(struct rtw89_tx_desc_info *desc_info)
static __le32 rtw89_build_txwd_body7_v1(struct rtw89_tx_desc_info *desc_info)
{
u32 dword = FIELD_PREP(RTW89_TXWD_BODY7_USE_RATE_V1, desc_info->use_rate) |
+ FIELD_PREP(RTW89_TXWD_BODY7_DATA_BW, desc_info->data_bw) |
+ FIELD_PREP(RTW89_TXWD_BODY7_GI_LTF, desc_info->gi_ltf) |
FIELD_PREP(RTW89_TXWD_BODY7_DATA_RATE, desc_info->data_rate);
return cpu_to_le32(dword);
@@ -1370,6 +1474,8 @@ static __le32 rtw89_build_txwd_body7_v1(struct rtw89_tx_desc_info *desc_info)
static __le32 rtw89_build_txwd_info0(struct rtw89_tx_desc_info *desc_info)
{
u32 dword = FIELD_PREP(RTW89_TXWD_INFO0_USE_RATE, desc_info->use_rate) |
+ FIELD_PREP(RTW89_TXWD_INFO0_DATA_BW, desc_info->data_bw) |
+ FIELD_PREP(RTW89_TXWD_INFO0_GI_LTF, desc_info->gi_ltf) |
FIELD_PREP(RTW89_TXWD_INFO0_DATA_RATE, desc_info->data_rate) |
FIELD_PREP(RTW89_TXWD_INFO0_DATA_STBC, desc_info->stbc) |
FIELD_PREP(RTW89_TXWD_INFO0_DATA_LDPC, desc_info->ldpc) |
@@ -1396,7 +1502,10 @@ static __le32 rtw89_build_txwd_info1(struct rtw89_tx_desc_info *desc_info)
u32 dword = FIELD_PREP(RTW89_TXWD_INFO1_MAX_AGGNUM, desc_info->ampdu_num) |
FIELD_PREP(RTW89_TXWD_INFO1_A_CTRL_BSR, desc_info->a_ctrl_bsr) |
FIELD_PREP(RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE,
- desc_info->data_retry_lowest_rate);
+ desc_info->data_retry_lowest_rate) |
+ FIELD_PREP(RTW89_TXWD_INFO1_DATA_TXCNT_LMT_SEL,
+ desc_info->tx_cnt_lmt_en) |
+ FIELD_PREP(RTW89_TXWD_INFO1_DATA_TXCNT_LMT, desc_info->tx_cnt_lmt);
return cpu_to_le32(dword);
}
@@ -1420,11 +1529,19 @@ static __le32 rtw89_build_txwd_info2_v1(struct rtw89_tx_desc_info *desc_info)
return cpu_to_le32(dword);
}
+static __le32 rtw89_build_txwd_info3(struct rtw89_tx_desc_info *desc_info)
+{
+ u32 dword = FIELD_PREP(RTW89_TXWD_INFO3_SPE_RPT, desc_info->report);
+
+ return cpu_to_le32(dword);
+}
+
static __le32 rtw89_build_txwd_info4(struct rtw89_tx_desc_info *desc_info)
{
bool rts_en = !desc_info->is_bmc;
u32 dword = FIELD_PREP(RTW89_TXWD_INFO4_RTS_EN, rts_en) |
- FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1);
+ FIELD_PREP(RTW89_TXWD_INFO4_HW_RTS_EN, 1) |
+ FIELD_PREP(RTW89_TXWD_INFO4_SW_DEFINE, desc_info->sn);
return cpu_to_le32(dword);
}
@@ -1447,6 +1564,7 @@ void rtw89_core_fill_txdesc(struct rtw89_dev *rtwdev,
txwd_info->dword0 = rtw89_build_txwd_info0(desc_info);
txwd_info->dword1 = rtw89_build_txwd_info1(desc_info);
txwd_info->dword2 = rtw89_build_txwd_info2(desc_info);
+ txwd_info->dword3 = rtw89_build_txwd_info3(desc_info);
txwd_info->dword4 = rtw89_build_txwd_info4(desc_info);
}
@@ -1476,6 +1594,7 @@ void rtw89_core_fill_txdesc_v1(struct rtw89_dev *rtwdev,
txwd_info->dword0 = rtw89_build_txwd_info0_v1(desc_info);
txwd_info->dword1 = rtw89_build_txwd_info1(desc_info);
txwd_info->dword2 = rtw89_build_txwd_info2_v1(desc_info);
+ txwd_info->dword3 = rtw89_build_txwd_info3(desc_info);
txwd_info->dword4 = rtw89_build_txwd_info4(desc_info);
}
EXPORT_SYMBOL(rtw89_core_fill_txdesc_v1);
@@ -1549,6 +1668,8 @@ static __le32 rtw89_build_txwd_body6_v2(struct rtw89_tx_desc_info *desc_info)
static __le32 rtw89_build_txwd_body7_v2(struct rtw89_tx_desc_info *desc_info)
{
u32 dword = FIELD_PREP(BE_TXD_BODY7_USERATE_SEL, desc_info->use_rate) |
+ FIELD_PREP(BE_TXD_BODY7_DATA_BW, desc_info->data_bw) |
+ FIELD_PREP(BE_TXD_BODY7_GI_LTF, desc_info->gi_ltf) |
FIELD_PREP(BE_TXD_BODY7_DATA_ER, desc_info->er_cap) |
FIELD_PREP(BE_TXD_BODY7_DATA_BW_ER, 0) |
FIELD_PREP(BE_TXD_BODY7_DATARATE, desc_info->data_rate);
@@ -1561,7 +1682,10 @@ static __le32 rtw89_build_txwd_info0_v2(struct rtw89_tx_desc_info *desc_info)
u32 dword = FIELD_PREP(BE_TXD_INFO0_DATA_STBC, desc_info->stbc) |
FIELD_PREP(BE_TXD_INFO0_DATA_LDPC, desc_info->ldpc) |
FIELD_PREP(BE_TXD_INFO0_DISDATAFB, desc_info->dis_data_fb) |
- FIELD_PREP(BE_TXD_INFO0_MULTIPORT_ID, desc_info->port);
+ FIELD_PREP(BE_TXD_INFO0_MULTIPORT_ID, desc_info->port) |
+ FIELD_PREP(BE_TXD_INFO0_DATA_TXCNT_LMT_SEL,
+ desc_info->tx_cnt_lmt_en) |
+ FIELD_PREP(BE_TXD_INFO0_DATA_TXCNT_LMT, desc_info->tx_cnt_lmt);
return cpu_to_le32(dword);
}
@@ -1571,7 +1695,8 @@ static __le32 rtw89_build_txwd_info1_v2(struct rtw89_tx_desc_info *desc_info)
u32 dword = FIELD_PREP(BE_TXD_INFO1_MAX_AGG_NUM, desc_info->ampdu_num) |
FIELD_PREP(BE_TXD_INFO1_A_CTRL_BSR, desc_info->a_ctrl_bsr) |
FIELD_PREP(BE_TXD_INFO1_DATA_RTY_LOWEST_RATE,
- desc_info->data_retry_lowest_rate);
+ desc_info->data_retry_lowest_rate) |
+ FIELD_PREP(BE_TXD_INFO1_SW_DEFINE, desc_info->sn);
return cpu_to_le32(dword);
}
@@ -1580,7 +1705,8 @@ static __le32 rtw89_build_txwd_info2_v2(struct rtw89_tx_desc_info *desc_info)
{
u32 dword = FIELD_PREP(BE_TXD_INFO2_AMPDU_DENSITY, desc_info->ampdu_density) |
FIELD_PREP(BE_TXD_INFO2_FORCE_KEY_EN, desc_info->sec_en) |
- FIELD_PREP(BE_TXD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx);
+ FIELD_PREP(BE_TXD_INFO2_SEC_CAM_IDX, desc_info->sec_cam_idx) |
+ FIELD_PREP(BE_TXD_INFO2_SPE_RPT_V1, desc_info->report);
return cpu_to_le32(dword);
}
@@ -1708,9 +1834,13 @@ static int rtw89_core_rx_process_mac_ppdu(struct rtw89_dev *rtwdev,
/* For WiFi 7 chips, RXWD.mac_id of PPDU status is not set
* by hardware, so update mac_id by rxinfo_user[].mac_id.
*/
- if (chip_gen == RTW89_CHIP_BE)
+ if (chip->chip_id == RTL8922A)
phy_ppdu->mac_id =
le32_get_bits(user->w0, RTW89_RXINFO_USER_MACID);
+ else if (chip->chip_id == RTL8922D)
+ phy_ppdu->mac_id =
+ le32_get_bits(user->w0, RTW89_RXINFO_USER_MACID_V1);
+
phy_ppdu->has_data =
le32_get_bits(user->w0, RTW89_RXINFO_USER_DATA);
phy_ppdu->has_bcn =
@@ -3632,12 +3762,10 @@ void rtw89_core_free_sta_pending_roc_tx(struct rtw89_dev *rtwdev,
struct ieee80211_sta *sta)
{
struct rtw89_sta *rtwsta = sta_to_rtwsta(sta);
- struct sk_buff *skb, *tmp;
+ struct sk_buff *skb;
- skb_queue_walk_safe(&rtwsta->roc_queue, skb, tmp) {
- skb_unlink(skb, &rtwsta->roc_queue);
+ while ((skb = skb_dequeue(&rtwsta->roc_queue)))
dev_kfree_skb_any(skb);
- }
}
static void rtw89_core_stop_tx_ba_session(struct rtw89_dev *rtwdev,
@@ -3881,8 +4009,8 @@ static void rtw89_core_sta_pending_tx_iter(void *data,
struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
struct rtw89_vif_link *target = data;
struct rtw89_vif_link *rtwvif_link;
- struct sk_buff *skb, *tmp;
unsigned int link_id;
+ struct sk_buff *skb;
int qsel, ret;
rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
@@ -3895,9 +4023,7 @@ bottom:
if (skb_queue_len(&rtwsta->roc_queue) == 0)
return;
- skb_queue_walk_safe(&rtwsta->roc_queue, skb, tmp) {
- skb_unlink(skb, &rtwsta->roc_queue);
-
+ while ((skb = skb_dequeue(&rtwsta->roc_queue))) {
ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel);
if (ret) {
rtw89_warn(rtwdev, "pending tx failed with %d\n", ret);
@@ -4047,12 +4173,10 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
{
- const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
struct ieee80211_hw *hw = rtwdev->hw;
struct rtw89_roc *roc = &rtwvif->roc;
struct rtw89_vif_link *rtwvif_link;
struct rtw89_vif *tmp_vif;
- u32 reg;
int ret;
lockdep_assert_wiphy(hw->wiphy);
@@ -4069,8 +4193,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
return;
}
- reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);
- rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr);
+ rtw89_mac_set_rx_fltr(rtwdev, rtwvif_link->mac_idx, rtwdev->hal.rx_fltr);
roc->state = RTW89_ROC_IDLE;
rtw89_config_roc_chandef(rtwdev, rtwvif_link, NULL);
@@ -4701,7 +4824,8 @@ int rtw89_core_sta_link_disconnect(struct rtw89_dev *rtwdev,
}
/* update cam aid mac_id net_type */
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL,
+ RTW89_ROLE_CON_DISCONN);
if (ret) {
rtw89_warn(rtwdev, "failed to send h2c cam\n");
return ret;
@@ -4775,7 +4899,8 @@ int rtw89_core_sta_link_assoc(struct rtw89_dev *rtwdev,
}
/* update cam aid mac_id net_type */
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL,
+ RTW89_ROLE_CON_DISCONN);
if (ret) {
rtw89_warn(rtwdev, "failed to send h2c cam\n");
return ret;
@@ -5493,10 +5618,22 @@ EXPORT_SYMBOL(rtw89_check_quirks);
int rtw89_core_start(struct rtw89_dev *rtwdev)
{
+ bool no_bbmcu = !rtwdev->chip->bbmcu_nr;
int ret;
+ ret = rtw89_mac_preinit(rtwdev);
+ if (ret) {
+ rtw89_err(rtwdev, "mac preinit fail, ret: %d\n", ret);
+ return ret;
+ }
+
+ if (no_bbmcu)
+ rtw89_chip_bb_preinit(rtwdev);
+
rtw89_phy_init_bb_afe(rtwdev);
+ /* above do preinit before downloading firmware */
+
ret = rtw89_mac_init(rtwdev);
if (ret) {
rtw89_err(rtwdev, "mac init fail, ret:%d\n", ret);
@@ -5542,6 +5679,7 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
rtw89_fw_h2c_fw_log(rtwdev, rtwdev->fw.log.enable);
rtw89_fw_h2c_init_ba_cam(rtwdev);
rtw89_tas_fw_timer_enable(rtwdev, true);
+ rtwdev->ps_hang_cnt = 0;
return 0;
}
@@ -5829,6 +5967,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
wiphy_work_init(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work);
INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work);
+ spin_lock_init(&rtwdev->tx_rpt.skb_lock);
skb_queue_head_init(&rtwdev->c2h_queue);
rtw89_core_ppdu_sts_init(rtwdev);
rtw89_traffic_stats_init(rtwdev, &rtwdev->stats);
@@ -5893,7 +6032,8 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
rtw89_phy_config_edcca(rtwdev, bb, true);
rtw89_tas_scan(rtwdev, true);
- rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr);
+ rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr,
+ RTW89_ROLE_INFO_CHANGE);
}
void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,
@@ -5913,7 +6053,8 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,
rcu_read_unlock();
- rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL);
+ rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL,
+ RTW89_ROLE_INFO_CHANGE);
rtw89_chip_rfk_scan(rtwdev, rtwvif_link, false);
rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx);
@@ -6014,7 +6155,7 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
u16 usable_links = ieee80211_vif_usable_links(vif);
u16 active_links = vif->active_links;
- struct rtw89_vif_link *target, *cur;
+ struct rtw89_vif_link *target;
int ret;
lockdep_assert_wiphy(rtwdev->hw->wiphy);
@@ -6040,11 +6181,9 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
ieee80211_stop_queues(rtwdev->hw);
flush_work(&rtwdev->txq_work);
- cur = rtw89_get_designated_link(rtwvif);
-
- ret = ieee80211_set_active_links(vif, active_links | BIT(link_id));
+ ret = ieee80211_set_active_links(vif, BIT(link_id));
if (ret) {
- rtw89_err(rtwdev, "%s: failed to activate link id %u\n",
+ rtw89_err(rtwdev, "%s: failed to work on link id %u\n",
__func__, link_id);
goto wake_queue;
}
@@ -6059,16 +6198,6 @@ int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
goto wake_queue;
}
- if (likely(cur))
- rtw89_fw_h2c_mlo_link_cfg(rtwdev, cur, false);
-
- rtw89_fw_h2c_mlo_link_cfg(rtwdev, target, true);
-
- ret = ieee80211_set_active_links(vif, BIT(link_id));
- if (ret)
- rtw89_err(rtwdev, "%s: failed to inactivate links 0x%x\n",
- __func__, active_links);
-
rtw89_chip_rfk_channel(rtwdev, target);
rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 928c8c84c964..a9cb47ea0b93 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -15,6 +15,7 @@
struct rtw89_dev;
struct rtw89_pci_info;
+struct rtw89_usb_info;
struct rtw89_mac_gen_def;
struct rtw89_phy_gen_def;
struct rtw89_fw_blacklist;
@@ -38,10 +39,13 @@ extern const struct ieee80211_ops rtw89_ops;
#define RFREG_MASK 0xfffff
#define INV_RF_DATA 0xffffffff
#define BYPASS_CR_DATA 0xbabecafe
+#define RTW89_R32_EA 0xEAEAEAEA
+#define RTW89_R32_DEAD 0xDEADBEEF
#define RTW89_TRACK_WORK_PERIOD round_jiffies_relative(HZ * 2)
#define RTW89_TRACK_PS_WORK_PERIOD msecs_to_jiffies(100)
#define RTW89_FORBID_BA_TIMER round_jiffies_relative(HZ * 4)
+#define RTW89_PS_HANG_MAX_CNT 3
#define CFO_TRACK_MAX_USER 64
#define MAX_RSSI 110
#define RSSI_FACTOR 1
@@ -151,6 +155,7 @@ enum rtw89_core_chip_id {
RTL8852C,
RTL8851B,
RTL8922A,
+ RTL8922D,
};
enum rtw89_chip_gen {
@@ -1167,6 +1172,10 @@ struct rtw89_tx_desc_info {
u8 ampdu_density;
u8 ampdu_num;
bool sec_en;
+ bool report;
+ bool tx_cnt_lmt_en;
+ u8 sn: 4;
+ u8 tx_cnt_lmt: 6;
u8 addr_info_nr;
u8 sec_keyid;
u8 sec_type;
@@ -1174,6 +1183,8 @@ struct rtw89_tx_desc_info {
u8 sec_seq[6];
u16 data_rate;
u16 data_retry_lowest_rate;
+ u8 data_bw;
+ u8 gi_ltf;
bool fw_dl;
u16 seq;
bool a_ctrl_bsr;
@@ -3374,11 +3385,18 @@ struct rtw89_ra_info {
u8 cr_tbl_sel:1;
u8 fix_giltf_en:1;
u8 fix_giltf:3;
- u8 rsvd2:1;
+ u8 partial_bw_er:1;
u8 csi_mcs_ss_idx;
u8 csi_mode:2;
u8 csi_gi_ltf:3;
u8 csi_bw:3;
+ /* after v1 */
+ u8 is_noisy:1;
+ u8 psra_en:1;
+ u8 rsvd0:1;
+ u8 macid_msb:2;
+ u8 band:2; /* enum rtw89_band */
+ u8 is_new_dbgreg:1;
};
#define RTW89_PPDU_MAC_INFO_USR_SIZE 4
@@ -3507,6 +3525,20 @@ struct rtw89_phy_rate_pattern {
bool enable;
};
+#define RTW89_TX_DONE 0x0
+#define RTW89_TX_RETRY_LIMIT 0x1
+#define RTW89_TX_LIFE_TIME 0x2
+#define RTW89_TX_MACID_DROP 0x3
+
+#define RTW89_MAX_TX_RPTS 16
+#define RTW89_MAX_TX_RPTS_MASK (RTW89_MAX_TX_RPTS - 1)
+struct rtw89_tx_rpt {
+ struct sk_buff *skbs[RTW89_MAX_TX_RPTS];
+ /* protect skbs array access/modification */
+ spinlock_t skb_lock;
+ atomic_t sn;
+};
+
#define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500)
struct rtw89_tx_wait_info {
struct rcu_head rcu_head;
@@ -3518,6 +3550,8 @@ struct rtw89_tx_wait_info {
struct rtw89_tx_skb_data {
struct rtw89_tx_wait_info __rcu *wait;
+ u8 tx_rpt_sn;
+ u8 tx_pkt_cnt_lmt;
u8 hci_priv[];
};
@@ -3652,6 +3686,8 @@ struct rtw89_hci_ops {
void (*write16)(struct rtw89_dev *rtwdev, u32 addr, u16 data);
void (*write32)(struct rtw89_dev *rtwdev, u32 addr, u32 data);
+ u32 (*read32_pci_cfg)(struct rtw89_dev *rtwdev, u32 addr);
+
int (*mac_pre_init)(struct rtw89_dev *rtwdev);
int (*mac_pre_deinit)(struct rtw89_dev *rtwdev);
int (*mac_post_init)(struct rtw89_dev *rtwdev);
@@ -3687,6 +3723,7 @@ struct rtw89_hci_info {
u32 rpwm_addr;
u32 cpwm_addr;
bool paused;
+ bool tx_rpt_enabled;
};
struct rtw89_chip_ops {
@@ -3763,7 +3800,7 @@ struct rtw89_chip_ops {
void (*fill_txdesc_fwcmd)(struct rtw89_dev *rtwdev,
struct rtw89_tx_desc_info *desc_info,
void *txdesc);
- u8 (*get_ch_dma)(struct rtw89_dev *rtwdev, u8 qsel);
+ u8 (*get_ch_dma[RTW89_HCI_TYPE_NUM])(struct rtw89_dev *rtwdev, u8 qsel);
int (*cfg_ctrl_path)(struct rtw89_dev *rtwdev, bool wl);
int (*mac_cfg_gnt)(struct rtw89_dev *rtwdev,
const struct rtw89_mac_ax_coex_gnt *gnt_cfg);
@@ -4422,6 +4459,7 @@ struct rtw89_chip_info {
u8 bacam_num;
u8 bacam_dynamic_num;
enum rtw89_bacam_ver bacam_ver;
+ u8 addrcam_ver;
u8 ppdu_max_usr;
u8 sec_ctrl_efuse_size;
@@ -4513,6 +4551,7 @@ struct rtw89_chip_variant {
union rtw89_bus_info {
const struct rtw89_pci_info *pci;
+ const struct rtw89_usb_info *usb;
};
struct rtw89_driver_info {
@@ -4640,6 +4679,7 @@ enum rtw89_fw_feature {
RTW89_FW_FEATURE_RFK_NTFY_MCC_V0,
RTW89_FW_FEATURE_LPS_DACK_BY_C2H_REG,
RTW89_FW_FEATURE_BEACON_TRACKING,
+ RTW89_FW_FEATURE_ADDR_CAM_V0,
};
struct rtw89_fw_suit {
@@ -4700,6 +4740,7 @@ struct rtw89_fw_elm_info {
struct rtw89_phy_rfk_log_fmt *rfk_log_fmt;
const struct rtw89_regd_data *regd;
const struct rtw89_fw_element_hdr *afe;
+ const struct rtw89_fw_element_hdr *diag_mac;
};
enum rtw89_fw_mss_dev_type {
@@ -5449,6 +5490,8 @@ struct rtw89_regd_ctrl {
struct rtw89_regulatory_info {
struct rtw89_regd_ctrl ctrl;
const struct rtw89_regd *regd;
+ bool programmed;
+
enum rtw89_reg_6ghz_power reg_6ghz_power;
struct rtw89_reg_6ghz_tpe reg_6ghz_tpe;
bool txpwr_uk_follow_etsi;
@@ -5933,6 +5976,7 @@ struct rtw89_mcc_info {
enum rtw89_mlo_mode {
RTW89_MLO_MODE_MLSR = 0,
+ RTW89_MLO_MODE_EMLSR = 1,
NUM_OF_RTW89_MLO_MODE,
};
@@ -6006,6 +6050,8 @@ struct rtw89_dev {
struct list_head tx_waits;
struct wiphy_delayed_work tx_wait_work;
+ struct rtw89_tx_rpt tx_rpt;
+
struct rtw89_cam_info cam_info;
struct sk_buff_head c2h_queue;
@@ -6079,6 +6125,7 @@ struct rtw89_dev {
struct rtw89_btc btc;
enum rtw89_ps_mode ps_mode;
bool lps_enabled;
+ u8 ps_hang_cnt;
struct rtw89_wow_param wow;
@@ -6088,6 +6135,7 @@ struct rtw89_dev {
int napi_budget_countdown;
struct rtw89_debugfs *debugfs;
+ struct rtw89_vif *pure_monitor_mode_vif;
/* HCI related data, keep last */
u8 priv[] __aligned(sizeof(void *));
@@ -6097,6 +6145,12 @@ struct rtw89_link_conf_container {
struct ieee80211_bss_conf *link_conf[IEEE80211_MLD_MAX_NUM_LINKS];
};
+struct rtw89_vif_ml_trans {
+ u16 mediate_links;
+ u16 links_to_del;
+ u16 links_to_add;
+};
+
#define RTW89_VIF_IDLE_LINK_ID 0
struct rtw89_vif {
@@ -6119,6 +6173,7 @@ struct rtw89_vif {
bool offchan;
enum rtw89_mlo_mode mlo_mode;
+ struct rtw89_vif_ml_trans ml_trans;
struct list_head dlink_pool;
u8 links_inst_valid_num;
@@ -6291,6 +6346,7 @@ static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,
static inline void rtw89_hci_reset(struct rtw89_dev *rtwdev)
{
rtwdev->hci.ops->reset(rtwdev);
+ /* hci.ops->reset must complete all pending TX wait SKBs */
rtw89_tx_wait_list_clear(rtwdev);
}
@@ -6620,6 +6676,15 @@ rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
mutex_unlock(&rtwdev->rf_mutex);
}
+static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr)
+{
+ if (rtwdev->hci.type != RTW89_HCI_TYPE_PCIE ||
+ !rtwdev->hci.ops->read32_pci_cfg)
+ return RTW89_R32_EA;
+
+ return rtwdev->hci.ops->read32_pci_cfg(rtwdev, addr);
+}
+
static inline struct ieee80211_txq *rtw89_txq_to_txq(struct rtw89_txq *rtwtxq)
{
void *p = rtwtxq;
@@ -6984,12 +7049,17 @@ static inline void rtw89_chip_rfk_hw_init(struct rtw89_dev *rtwdev)
}
static inline
-void rtw89_chip_bb_preinit(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx)
+void rtw89_chip_bb_preinit(struct rtw89_dev *rtwdev)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
- if (chip->ops->bb_preinit)
- chip->ops->bb_preinit(rtwdev, phy_idx);
+ if (!chip->ops->bb_preinit)
+ return;
+
+ chip->ops->bb_preinit(rtwdev, RTW89_PHY_0);
+
+ if (rtwdev->dbcc_en)
+ chip->ops->bb_preinit(rtwdev, RTW89_PHY_1);
}
static inline
@@ -7241,7 +7311,7 @@ u8 rtw89_chip_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
- return chip->ops->get_ch_dma(rtwdev, qsel);
+ return chip->ops->get_ch_dma[rtwdev->hci.type](rtwdev, qsel);
}
static inline
@@ -7372,27 +7442,28 @@ static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,
return dev_alloc_skb(length);
}
+static inline bool rtw89_core_is_tx_wait(struct rtw89_dev *rtwdev,
+ struct rtw89_tx_skb_data *skb_data)
+{
+ return rcu_access_pointer(skb_data->wait);
+}
+
static inline bool rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev,
struct rtw89_tx_skb_data *skb_data,
- bool tx_done)
+ u8 tx_status)
{
struct rtw89_tx_wait_info *wait;
- bool ret = false;
- rcu_read_lock();
+ guard(rcu)();
wait = rcu_dereference(skb_data->wait);
if (!wait)
- goto out;
+ return false;
- ret = true;
- wait->tx_done = tx_done;
+ wait->tx_done = tx_status == RTW89_TX_DONE;
/* Don't access skb anymore after completion */
complete_all(&wait->completion);
-
-out:
- rcu_read_unlock();
- return ret;
+ return true;
}
static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev)
@@ -7495,6 +7566,7 @@ void rtw89_core_fill_txdesc_fwcmd_v2(struct rtw89_dev *rtwdev,
void *txdesc);
u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel);
u8 rtw89_core_get_ch_dma_v1(struct rtw89_dev *rtwdev, u8 qsel);
+u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel);
void rtw89_core_rx(struct rtw89_dev *rtwdev,
struct rtw89_rx_desc_info *desc_info,
struct sk_buff *skb);
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index 3dc7981c510f..1264c2f82600 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -87,6 +87,7 @@ struct rtw89_debugfs {
struct rtw89_debugfs_priv disable_dm;
struct rtw89_debugfs_priv mlo_mode;
struct rtw89_debugfs_priv beacon_info;
+ struct rtw89_debugfs_priv diag_mac;
};
struct rtw89_debugfs_iter_data {
@@ -4361,6 +4362,302 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev,
return count;
}
+enum __diag_mac_cmd {
+ __CMD_EQUALV,
+ __CMD_EQUALO,
+ __CMD_NEQUALV,
+ __CMD_NEQUALO,
+ __CMD_SETEQUALV,
+ __CMD_SETEQUALO,
+ __CMD_CMPWCR,
+ __CMD_CMPWWD,
+ __CMD_NEQ_CMPWCR,
+ __CMD_NEQ_CMPWWD,
+ __CMD_INCREMENT,
+ __CMD_MESSAGE,
+};
+
+enum __diag_mac_io {
+ __IO_NORMAL,
+ __IO_NORMAL_PCIE,
+ __IO_NORMAL_USB,
+ __IO_NORMAL_SDIO,
+ __IO_PCIE_CFG,
+ __IO_SDIO_CCCR,
+};
+
+struct __diag_mac_rule_header {
+ u8 sheet;
+ u8 cmd;
+ u8 seq_major;
+ u8 seq_minor;
+ u8 io_band;
+ #define __DIAG_MAC_IO GENMASK(3, 0)
+ #define __DIAG_MAC_N_BAND BIT(4)
+ #define __DIAG_MAC_HAS_BAND BIT(5)
+ u8 len; /* include header. Unit: 4 bytes */
+ u8 rsvd[2];
+} __packed;
+
+struct __diag_mac_rule_equal {
+ struct __diag_mac_rule_header header;
+ __le32 addr;
+ __le32 addr_name_offset;
+ __le32 mask;
+ __le32 val;
+ __le32 msg_offset;
+ u8 rsvd[4];
+} __packed;
+
+struct __diag_mac_rule_increment {
+ struct __diag_mac_rule_header header;
+ __le32 addr;
+ __le32 addr_name_offset;
+ __le32 mask;
+ __le16 sel;
+ __le16 delay;
+ __le32 msg_offset;
+ u8 rsvd[4];
+} __packed;
+
+struct __diag_mac_msg_buf {
+ __le16 len;
+ char string[];
+} __packed;
+
+static ssize_t rtw89_mac_diag_do_equalv(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ const struct __diag_mac_rule_equal *r,
+ const void *msg_start,
+ u64 *positive_bmp)
+{
+ const struct __diag_mac_msg_buf *name = msg_start +
+ le32_to_cpu(r->addr_name_offset);
+ const struct __diag_mac_msg_buf *msg = msg_start +
+ le32_to_cpu(r->msg_offset);
+ bool want_eq = r->header.cmd == __CMD_EQUALV;
+ char *p = buf, *end = buf + bufsz;
+ bool equal = false;
+ u32 val;
+
+ *positive_bmp <<= 1;
+
+ if (u8_get_bits(r->header.io_band, __DIAG_MAC_IO) == __IO_PCIE_CFG)
+ val = rtw89_read32_pci_cfg(rtwdev, le32_to_cpu(r->addr));
+ else
+ val = rtw89_read32(rtwdev, le32_to_cpu(r->addr));
+
+ if ((val & le32_to_cpu(r->mask)) == le32_to_cpu(r->val))
+ equal = true;
+
+ if (want_eq == equal) {
+ *positive_bmp |= BIT(0);
+ return p - buf;
+ }
+
+ p += scnprintf(p, end - p, "sheet: %d, cmd: %d, Reg: %.*s => %x, %.*s\n",
+ r->header.sheet, r->header.cmd, le16_to_cpu(name->len),
+ name->string, val, le16_to_cpu(msg->len), msg->string);
+
+ return p - buf;
+}
+
+static ssize_t rtw89_mac_diag_do_increment(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz,
+ const struct __diag_mac_rule_increment *r,
+ const void *msg_start,
+ u64 *positive_bmp)
+{
+ const struct __diag_mac_msg_buf *name = msg_start +
+ le32_to_cpu(r->addr_name_offset);
+ const struct __diag_mac_msg_buf *msg = msg_start +
+ le32_to_cpu(r->msg_offset);
+ char *p = buf, *end = buf + bufsz;
+ u32 addr = le32_to_cpu(r->addr);
+ u32 mask = le32_to_cpu(r->mask);
+ u16 sel = le16_to_cpu(r->sel);
+ u32 val1, val2;
+
+ *positive_bmp <<= 1;
+
+ rtw89_write32(rtwdev, addr, sel);
+
+ if (u8_get_bits(r->header.io_band, __DIAG_MAC_IO) == __IO_PCIE_CFG)
+ val1 = rtw89_read32_pci_cfg(rtwdev, addr);
+ else
+ val1 = rtw89_read32(rtwdev, addr);
+
+ mdelay(le16_to_cpu(r->delay));
+
+ if (u8_get_bits(r->header.io_band, __DIAG_MAC_IO) == __IO_PCIE_CFG)
+ val2 = rtw89_read32_pci_cfg(rtwdev, addr);
+ else
+ val2 = rtw89_read32(rtwdev, addr);
+
+ if ((val2 & mask) > (val1 & mask)) {
+ *positive_bmp |= BIT(0);
+ return p - buf;
+ }
+
+ p += scnprintf(p, end - p, "sheet: %d, cmd: %d, Reg: %.*s [%d]=> %x, %.*s\n",
+ r->header.sheet, r->header.cmd, le16_to_cpu(name->len),
+ name->string, le16_to_cpu(r->sel), val1,
+ le16_to_cpu(msg->len), msg->string);
+
+ return p - buf;
+}
+
+static bool rtw89_mac_diag_match_hci(struct rtw89_dev *rtwdev,
+ const struct __diag_mac_rule_header *rh)
+{
+ switch (u8_get_bits(rh->io_band, __DIAG_MAC_IO)) {
+ case __IO_NORMAL:
+ default:
+ return true;
+ case __IO_NORMAL_PCIE:
+ case __IO_PCIE_CFG:
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE)
+ return true;
+ break;
+ case __IO_NORMAL_USB:
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_USB)
+ return true;
+ break;
+ case __IO_NORMAL_SDIO:
+ case __IO_SDIO_CCCR:
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_SDIO)
+ return true;
+ break;
+ }
+
+ return false;
+}
+
+static bool rtw89_mac_diag_match_band(struct rtw89_dev *rtwdev,
+ const struct __diag_mac_rule_header *rh)
+{
+ u8 active_bands;
+ bool has_band;
+ u8 band;
+
+ has_band = u8_get_bits(rh->io_band, __DIAG_MAC_HAS_BAND);
+ if (!has_band)
+ return true;
+
+ band = u8_get_bits(rh->io_band, __DIAG_MAC_N_BAND);
+ active_bands = rtw89_get_active_phy_bitmap(rtwdev);
+
+ if (active_bands & BIT(band))
+ return true;
+
+ return false;
+}
+
+static ssize_t rtw89_mac_diag_iter_all(struct rtw89_dev *rtwdev,
+ char *buf, size_t bufsz)
+{
+ const struct rtw89_fw_element_hdr *elm = rtwdev->fw.elm_info.diag_mac;
+ u32 n_plains = 0, n_rules = 0, n_positive = 0, n_ignore = 0;
+ char *p = buf, *end = buf + bufsz, *p_rewind;
+ const void *rule, *rule_end;
+ u32 elm_size, rule_size;
+ const void *msg_start;
+ u64 positive_bmp = 0;
+ u8 prev_sheet = 0;
+ u8 prev_seq = 0;
+ int limit;
+
+ if (!elm) {
+ p += scnprintf(p, end - p, "No diag_mac entry\n");
+ goto out;
+ }
+
+ rule_size = le32_to_cpu(elm->u.diag_mac.rule_size);
+ elm_size = le32_to_cpu(elm->size);
+
+ if (ALIGN(rule_size, 16) > elm_size) {
+ p += scnprintf(p, end - p, "rule size (%u) exceed elm_size (%u)\n",
+ ALIGN(rule_size, 16), elm_size);
+ goto out;
+ }
+
+ rule = &elm->u.diag_mac.rules_and_msgs[0];
+ rule_end = &elm->u.diag_mac.rules_and_msgs[rule_size];
+ msg_start = &elm->u.diag_mac.rules_and_msgs[ALIGN(rule_size, 16)];
+
+ for (limit = 0; limit < 5000 && rule < rule_end; limit++) {
+ const struct __diag_mac_rule_header *rh = rule;
+ u8 sheet = rh->sheet;
+ u8 seq = rh->seq_major;
+
+ if (!rtw89_mac_diag_match_hci(rtwdev, rh) ||
+ !rtw89_mac_diag_match_band(rtwdev, rh)) {
+ n_ignore++;
+ goto next;
+ }
+
+ if (!seq || prev_sheet != sheet || prev_seq != seq) {
+ if (positive_bmp) {
+ n_positive++;
+ /*
+ * discard output for negative results if one in
+ * a sequence set is positive.
+ */
+ if (p_rewind)
+ p = p_rewind;
+ }
+ p_rewind = seq ? p : NULL;
+ positive_bmp = 0;
+ n_rules++;
+ }
+
+ switch (rh->cmd) {
+ case __CMD_EQUALV:
+ case __CMD_NEQUALV:
+ p += rtw89_mac_diag_do_equalv(rtwdev, p, end - p, rule,
+ msg_start, &positive_bmp);
+ break;
+ case __CMD_INCREMENT:
+ p += rtw89_mac_diag_do_increment(rtwdev, p, end - p, rule,
+ msg_start, &positive_bmp);
+ break;
+ default:
+ p += scnprintf(p, end - p, "unknown rule cmd %u\n", rh->cmd);
+ break;
+ }
+
+next:
+ n_plains++;
+ rule += rh->len * 4;
+ prev_seq = seq;
+ prev_sheet = sheet;
+ }
+
+ if (positive_bmp) {
+ n_positive++;
+ if (p_rewind)
+ p = p_rewind;
+ }
+
+ p += scnprintf(p, end - p, "\nPlain(Ignore)/Rules/Positive: %u(%u)/%u/%u\n",
+ n_plains, n_ignore, n_rules, n_positive);
+
+out:
+ return p - buf;
+}
+
+static ssize_t
+rtw89_debug_priv_diag_mac_get(struct rtw89_dev *rtwdev,
+ struct rtw89_debugfs_priv *debugfs_priv,
+ char *buf, size_t bufsz)
+{
+ lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
+ rtw89_leave_lps(rtwdev);
+
+ return rtw89_mac_diag_iter_all(rtwdev, buf, bufsz);
+}
+
static ssize_t
rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev,
struct rtw89_debugfs_priv *debugfs_priv,
@@ -4478,6 +4775,7 @@ static const struct rtw89_debugfs rtw89_debugfs_templ = {
.disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK),
.mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK),
.beacon_info = rtw89_debug_priv_get(beacon_info),
+ .diag_mac = rtw89_debug_priv_get(diag_mac, RSIZE_16K, RLOCK),
};
#define rtw89_debugfs_add(name, mode, fopname, parent) \
@@ -4524,6 +4822,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top
rtw89_debugfs_add_rw(disable_dm);
rtw89_debugfs_add_rw(mlo_mode);
rtw89_debugfs_add_r(beacon_info);
+ rtw89_debugfs_add_r(diag_mac);
}
void rtw89_debugfs_init(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index ab904a7def1b..7b9d9989e517 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -161,6 +161,11 @@ static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 le
info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_W7_DYN_HDR);
info->idmem_share_mode = le32_get_bits(fw_hdr->w7, FW_HDR_W7_IDMEM_SHARE_MODE);
+ if (chip->chip_gen == RTW89_CHIP_AX)
+ info->part_size = FWDL_SECTION_PER_PKT_LEN;
+ else
+ info->part_size = le32_get_bits(fw_hdr->w7, FW_HDR_W7_PART_SIZE);
+
if (info->dynamic_hdr_en) {
info->hdr_len = le32_get_bits(fw_hdr->w3, FW_HDR_W3_LEN);
info->dynamic_hdr_len = info->hdr_len - base_hdr_len;
@@ -439,6 +444,7 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le
struct rtw89_fw_bin_info *info)
{
const struct rtw89_fw_hdr_v1 *fw_hdr = (const struct rtw89_fw_hdr_v1 *)fw;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_fw_hdr_section_info *section_info;
const struct rtw89_fw_dynhdr_hdr *fwdynhdr;
const struct rtw89_fw_hdr_section_v1 *section;
@@ -455,6 +461,11 @@ static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 le
info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_DYN_HDR);
info->idmem_share_mode = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_IDMEM_SHARE_MODE);
+ if (chip->chip_gen == RTW89_CHIP_AX)
+ info->part_size = FWDL_SECTION_PER_PKT_LEN;
+ else
+ info->part_size = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_PART_SIZE);
+
if (info->dynamic_hdr_en) {
info->hdr_len = le32_get_bits(fw_hdr->w5, FW_HDR_V1_W5_HDR_SIZE);
info->dynamic_hdr_len = info->hdr_len - base_hdr_len;
@@ -870,6 +881,7 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = {
__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 76, 0, LPS_DACK_BY_C2H_REG),
__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 79, 0, CRASH_TRIGGER_TYPE_1),
__CFG_FW_FEAT(RTL8922A, ge, 0, 35, 80, 0, BEACON_TRACKING),
+ __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 84, 0, ADDR_CAM_V0),
};
static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw,
@@ -1298,6 +1310,18 @@ int rtw89_build_afe_pwr_seq_from_elm(struct rtw89_dev *rtwdev,
return 0;
}
+static
+int rtw89_recognize_diag_mac_from_elm(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm,
+ const union rtw89_fw_element_arg arg)
+{
+ struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
+
+ elm_info->diag_mac = elm;
+
+ return 0;
+}
+
static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
[RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,
{ .fw_type = RTW89_FW_BBMCU0 }, NULL},
@@ -1386,6 +1410,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
[RTW89_FW_ELEMENT_ID_AFE_PWR_SEQ] = {
rtw89_build_afe_pwr_seq_from_elm, {}, "AFE",
},
+ [RTW89_FW_ELEMENT_ID_DIAG_MAC] = {
+ rtw89_recognize_diag_mac_from_elm, {}, NULL,
+ },
};
int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
@@ -1501,8 +1528,7 @@ static u32 __rtw89_fw_download_tweak_hdr_v0(struct rtw89_dev *rtwdev,
struct rtw89_fw_hdr_section *section;
int i;
- le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN,
- FW_HDR_W7_PART_SIZE);
+ le32p_replace_bits(&fw_hdr->w7, info->part_size, FW_HDR_W7_PART_SIZE);
for (i = 0; i < info->section_num; i++) {
section_info = &info->section_info[i];
@@ -1527,8 +1553,7 @@ static u32 __rtw89_fw_download_tweak_hdr_v1(struct rtw89_dev *rtwdev,
u8 dst_sec_idx = 0;
u8 sec_idx;
- le32p_replace_bits(&fw_hdr->w7, FWDL_SECTION_PER_PKT_LEN,
- FW_HDR_V1_W7_PART_SIZE);
+ le32p_replace_bits(&fw_hdr->w7, info->part_size, FW_HDR_V1_W7_PART_SIZE);
for (sec_idx = 0; sec_idx < info->section_num; sec_idx++) {
section_info = &info->section_info[sec_idx];
@@ -1630,7 +1655,8 @@ static int rtw89_fw_download_hdr(struct rtw89_dev *rtwdev,
}
static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev,
- struct rtw89_fw_hdr_section_info *info)
+ struct rtw89_fw_hdr_section_info *info,
+ u32 part_size)
{
struct sk_buff *skb;
const u8 *section = info->addr;
@@ -1651,20 +1677,17 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev,
}
if (info->key_addr && info->key_len) {
- if (residue_len > FWDL_SECTION_PER_PKT_LEN || info->len < info->key_len)
+ if (residue_len > part_size || info->len < info->key_len)
rtw89_warn(rtwdev,
"ignore to copy key data because of len %d, %d, %d, %d\n",
- info->len, FWDL_SECTION_PER_PKT_LEN,
+ info->len, part_size,
info->key_len, residue_len);
else
copy_key = true;
}
while (residue_len) {
- if (residue_len >= FWDL_SECTION_PER_PKT_LEN)
- pkt_len = FWDL_SECTION_PER_PKT_LEN;
- else
- pkt_len = residue_len;
+ pkt_len = min(residue_len, part_size);
skb = rtw89_fw_h2c_alloc_skb_no_hdr(rtwdev, pkt_len);
if (!skb) {
@@ -1719,7 +1742,7 @@ static int rtw89_fw_download_main(struct rtw89_dev *rtwdev,
int ret;
while (section_num--) {
- ret = __rtw89_fw_download_main(rtwdev, section_info);
+ ret = __rtw89_fw_download_main(rtwdev, section_info, info->part_size);
if (ret)
return ret;
section_info++;
@@ -2110,28 +2133,48 @@ plain_log:
}
-#define H2C_CAM_LEN 60
int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link,
- struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr)
+ struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr,
+ enum rtw89_upd_mode upd_mode)
{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_h2c_addr_cam_v0 *h2c_v0;
+ struct rtw89_h2c_addr_cam *h2c;
+ u32 len = sizeof(*h2c);
struct sk_buff *skb;
+ u8 ver = U8_MAX;
int ret;
- skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_CAM_LEN);
+ if (RTW89_CHK_FW_FEATURE(ADDR_CAM_V0, &rtwdev->fw) ||
+ chip->chip_gen == RTW89_CHIP_AX) {
+ len = sizeof(*h2c_v0);
+ ver = 0;
+ }
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
if (!skb) {
rtw89_err(rtwdev, "failed to alloc skb for fw dl\n");
return -ENOMEM;
}
- skb_put(skb, H2C_CAM_LEN);
- rtw89_cam_fill_addr_cam_info(rtwdev, rtwvif_link, rtwsta_link, scan_mac_addr,
- skb->data);
- rtw89_cam_fill_bssid_cam_info(rtwdev, rtwvif_link, rtwsta_link, skb->data);
+ skb_put(skb, len);
+ h2c_v0 = (struct rtw89_h2c_addr_cam_v0 *)skb->data;
+
+ rtw89_cam_fill_addr_cam_info(rtwdev, rtwvif_link, rtwsta_link,
+ scan_mac_addr, h2c_v0);
+ rtw89_cam_fill_bssid_cam_info(rtwdev, rtwvif_link, rtwsta_link, h2c_v0);
+
+ if (ver == 0)
+ goto hdr;
+
+ h2c = (struct rtw89_h2c_addr_cam *)skb->data;
+ h2c->w15 = le32_encode_bits(upd_mode, ADDR_CAM_W15_UPD_MODE);
+hdr:
rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
H2C_CAT_MAC,
H2C_CL_MAC_ADDR_CAM_UPDATE,
H2C_FUNC_MAC_ADDR_CAM_UPD, 0, 1,
- H2C_CAM_LEN);
+ len);
ret = rtw89_h2c_tx(rtwdev, skb, false);
if (ret) {
@@ -3165,6 +3208,7 @@ int rtw89_fw_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev,
SET_CMC_TBL_ANTSEL_C(skb->data, 0);
SET_CMC_TBL_ANTSEL_D(skb->data, 0);
}
+ SET_CMC_TBL_MGQ_RPT_EN(skb->data, rtwdev->hci.tx_rpt_enabled);
SET_CMC_TBL_DOPPLER_CTRL(skb->data, 0);
SET_CMC_TBL_TXPWR_TOLERENCE(skb->data, 0);
if (rtwvif_link->net_type == RTW89_NET_TYPE_AP_MODE)
@@ -3210,7 +3254,8 @@ int rtw89_fw_h2c_default_cmac_tbl_g7(struct rtw89_dev *rtwdev,
h2c->c0 = le32_encode_bits(mac_id, CCTLINFO_G7_C0_MACID) |
le32_encode_bits(1, CCTLINFO_G7_C0_OP);
- h2c->w0 = le32_encode_bits(4, CCTLINFO_G7_W0_DATARATE);
+ h2c->w0 = le32_encode_bits(4, CCTLINFO_G7_W0_DATARATE) |
+ le32_encode_bits(rtwdev->hci.tx_rpt_enabled, CCTLINFO_G7_W0_MGQ_RPT_EN);
h2c->m0 = cpu_to_le32(CCTLINFO_G7_W0_ALL);
h2c->w1 = le32_encode_bits(4, CCTLINFO_G7_W1_DATA_RTY_LOWEST_RATE) |
@@ -4715,13 +4760,16 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi
struct rtw89_h2c_ra_v1 *h2c_v1;
struct rtw89_h2c_ra *h2c;
u32 len = sizeof(*h2c);
- bool format_v1 = false;
struct sk_buff *skb;
+ u8 ver = U8_MAX;
int ret;
- if (chip->chip_gen == RTW89_CHIP_BE) {
+ if (chip->chip_gen == RTW89_CHIP_AX) {
+ len = sizeof(*h2c);
+ ver = 0;
+ } else {
len = sizeof(*h2c_v1);
- format_v1 = true;
+ ver = 1;
}
skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
@@ -4753,16 +4801,8 @@ int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi
h2c->w3 = le32_encode_bits(ra->fix_giltf_en, RTW89_H2C_RA_W3_FIX_GILTF_EN) |
le32_encode_bits(ra->fix_giltf, RTW89_H2C_RA_W3_FIX_GILTF);
- if (!format_v1)
- goto csi;
-
- h2c_v1 = (struct rtw89_h2c_ra_v1 *)h2c;
- h2c_v1->w4 = le32_encode_bits(ra->mode_ctrl, RTW89_H2C_RA_V1_W4_MODE_EHT) |
- le32_encode_bits(ra->bw_cap, RTW89_H2C_RA_V1_W4_BW_EHT);
-
-csi:
- if (!csi)
- goto done;
+ if (!csi || ver >= 1)
+ goto next_v1;
h2c->w2 |= le32_encode_bits(1, RTW89_H2C_RA_W2_BFEE_CSI_CTL);
h2c->w3 |= le32_encode_bits(ra->band_num, RTW89_H2C_RA_W3_BAND_NUM) |
@@ -4774,6 +4814,18 @@ csi:
le32_encode_bits(ra->csi_gi_ltf, RTW89_H2C_RA_W3_FIXED_CSI_GI_LTF) |
le32_encode_bits(ra->csi_bw, RTW89_H2C_RA_W3_FIXED_CSI_BW);
+next_v1:
+ if (ver < 1)
+ goto done;
+
+ h2c->w3 |= le32_encode_bits(ra->partial_bw_er,
+ RTW89_H2C_RA_V1_W3_PARTIAL_BW_SU_ER) |
+ le32_encode_bits(ra->band, RTW89_H2C_RA_V1_W3_BAND);
+
+ h2c_v1 = (struct rtw89_h2c_ra_v1 *)h2c;
+ h2c_v1->w4 = le32_encode_bits(ra->mode_ctrl, RTW89_H2C_RA_V1_W4_MODE_EHT) |
+ le32_encode_bits(ra->bw_cap, RTW89_H2C_RA_V1_W4_BW_EHT);
+
done:
rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA,
@@ -6891,11 +6943,18 @@ void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work)
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
c2h_work);
struct sk_buff *skb, *tmp;
+ struct sk_buff_head c2hq;
+ unsigned long flags;
lockdep_assert_wiphy(rtwdev->hw->wiphy);
- skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) {
- skb_unlink(skb, &rtwdev->c2h_queue);
+ __skb_queue_head_init(&c2hq);
+
+ spin_lock_irqsave(&rtwdev->c2h_queue.lock, flags);
+ skb_queue_splice_init(&rtwdev->c2h_queue, &c2hq);
+ spin_unlock_irqrestore(&rtwdev->c2h_queue.lock, flags);
+
+ skb_queue_walk_safe(&c2hq, skb, tmp) {
rtw89_fw_c2h_cmd_handle(rtwdev, skb);
dev_kfree_skb_any(skb);
}
@@ -6905,17 +6964,19 @@ void rtw89_fw_c2h_purge_obsoleted_scan_events(struct rtw89_dev *rtwdev)
{
struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
struct sk_buff *skb, *tmp;
- int limit;
+ struct sk_buff_head c2hq;
+ unsigned long flags;
lockdep_assert_wiphy(rtwdev->hw->wiphy);
- limit = skb_queue_len(&rtwdev->c2h_queue);
+ __skb_queue_head_init(&c2hq);
- skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) {
- struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb);
+ spin_lock_irqsave(&rtwdev->c2h_queue.lock, flags);
+ skb_queue_splice_init(&rtwdev->c2h_queue, &c2hq);
+ spin_unlock_irqrestore(&rtwdev->c2h_queue.lock, flags);
- if (--limit < 0)
- return;
+ skb_queue_walk_safe(&c2hq, skb, tmp) {
+ struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb);
if (!attr->is_scan_event || attr->scan_seq == scan_info->seq)
continue;
@@ -6924,9 +6985,13 @@ void rtw89_fw_c2h_purge_obsoleted_scan_events(struct rtw89_dev *rtwdev)
"purge obsoleted scan event with seq=%d (cur=%d)\n",
attr->scan_seq, scan_info->seq);
- skb_unlink(skb, &rtwdev->c2h_queue);
+ __skb_unlink(skb, &c2hq);
dev_kfree_skb_any(skb);
}
+
+ spin_lock_irqsave(&rtwdev->c2h_queue.lock, flags);
+ skb_queue_splice(&c2hq, &rtwdev->c2h_queue);
+ spin_unlock_irqrestore(&rtwdev->c2h_queue.lock, flags);
}
static int rtw89_fw_write_h2c_reg(struct rtw89_dev *rtwdev,
@@ -7694,6 +7759,13 @@ int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev,
INIT_LIST_HEAD(&list);
list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) {
+ /* The operating channel (tx_null == true) should
+ * not be last in the list, to avoid breaking
+ * RTL8851BU and RTL8832BU.
+ */
+ if (list_len + 1 == RTW89_SCAN_LIST_LIMIT_AX && ch_info->tx_null)
+ break;
+
list_move_tail(&ch_info->list, &list);
list_len++;
@@ -7774,15 +7846,23 @@ int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev,
struct ieee80211_channel *channel;
struct list_head chan_list;
enum rtw89_chan_type type;
+ bool chan_by_rnr;
bool random_seq;
int ret;
u32 idx;
random_seq = !!(req->flags & NL80211_SCAN_FLAG_RANDOM_SN);
+ chan_by_rnr = rtwdev->chip->support_rnr &&
+ (req->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ);
INIT_LIST_HEAD(&chan_list);
for (idx = 0; idx < req->n_channels; idx++) {
channel = req->channels[idx];
+
+ if (channel->band == NL80211_BAND_6GHZ &&
+ !cfg80211_channel_is_psc(channel) && chan_by_rnr)
+ continue;
+
ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
if (!ch_info) {
ret = -ENOMEM;
@@ -8030,7 +8110,6 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct ieee80211_scan_request *scan_req)
{
- const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev);
struct cfg80211_scan_request *req = &scan_req->req;
const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,
@@ -8042,7 +8121,6 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
};
u32 rx_fltr = rtwdev->hal.rx_fltr;
u8 mac_addr[ETH_ALEN];
- u32 reg;
int ret;
/* clone op and keep it during scan */
@@ -8082,8 +8160,7 @@ int rtw89_hw_scan_start(struct rtw89_dev *rtwdev,
rx_fltr &= ~B_AX_A_BC;
rx_fltr &= ~B_AX_A_A1_MATCH;
- reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);
- rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rx_fltr);
+ rtw89_mac_set_rx_fltr(rtwdev, rtwvif_link->mac_idx, rx_fltr);
rtw89_chanctx_pause(rtwdev, &pause_parm);
rtw89_phy_dig_suspend(rtwdev);
@@ -8101,20 +8178,17 @@ struct rtw89_hw_scan_complete_cb_data {
static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data)
{
- const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev);
struct rtw89_hw_scan_complete_cb_data *cb_data = data;
struct rtw89_vif_link *rtwvif_link = cb_data->rtwvif_link;
struct cfg80211_scan_info info = {
.aborted = cb_data->aborted,
};
- u32 reg;
if (!rtwvif_link)
return -EINVAL;
- reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx);
- rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr);
+ rtw89_mac_set_rx_fltr(rtwdev, rtwvif_link->mac_idx, rtwdev->hal.rx_fltr);
rtw89_core_scan_complete(rtwdev, rtwvif_link, true);
ieee80211_scan_completed(rtwdev->hw, &info);
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index ddebf7972068..cedb4a47a769 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -297,6 +297,7 @@ struct rtw89_fw_hdr_section_info {
struct rtw89_fw_bin_info {
u8 section_num;
+ u32 part_size;
u32 hdr_len;
bool dynamic_hdr_en;
u32 dynamic_hdr_len;
@@ -446,6 +447,13 @@ struct rtw89_h2c_ra {
#define RTW89_H2C_RA_W3_FIXED_CSI_MODE GENMASK(25, 24)
#define RTW89_H2C_RA_W3_FIXED_CSI_GI_LTF GENMASK(28, 26)
#define RTW89_H2C_RA_W3_FIXED_CSI_BW GENMASK(31, 29)
+#define RTW89_H2C_RA_V1_W3_PARTIAL_BW_SU_ER BIT(15)
+#define RTW89_H2C_RA_V1_W3_FIXED_CSI_RATE_L GENMASK(23, 16)
+#define RTW89_H2C_RA_V1_W3_IS_NOISY BIT(24)
+#define RTW89_H2C_RA_V1_W3_PSRA_EN BIT(25)
+#define RTW89_H2C_RA_V1_W3_MACID_MSB GENMASK(28, 27)
+#define RTW89_H2C_RA_V1_W3_BAND GENMASK(30, 29)
+#define RTW89_H2C_RA_V1_W3_NEW_DBGREG BIT(31)
struct rtw89_h2c_ra_v1 {
struct rtw89_h2c_ra v0;
@@ -3647,6 +3655,15 @@ struct rtw89_fw_c2h_log_fmt {
#define RTW89_C2H_FW_LOG_SIGNATURE 0xA5A5
#define RTW89_C2H_FW_LOG_STR_BUF_SIZE 512
+struct rtw89_c2h_bcn_upd_done {
+ struct rtw89_c2h_hdr hdr;
+ __le32 w2;
+} __packed;
+
+#define RTW89_C2H_BCN_UPD_DONE_W2_PORT GENMASK(2, 0)
+#define RTW89_C2H_BCN_UPD_DONE_W2_MBSSID GENMASK(6, 3)
+#define RTW89_C2H_BCN_UPD_DONE_W2_BAND_IDX BIT(7)
+
struct rtw89_c2h_mac_bcnfltr_rpt {
__le32 w0;
__le32 w1;
@@ -3747,6 +3764,47 @@ struct rtw89_c2h_scanofld {
#define RTW89_GET_MAC_C2H_MCC_REQ_ACK_H2C_FUNC(c2h) \
le32_get_bits(*((const __le32 *)(c2h) + 2), GENMASK(15, 8))
+struct rtw89_c2h_mac_tx_rpt {
+ struct rtw89_c2h_hdr hdr;
+ __le32 w2;
+ __le32 w3;
+ __le32 w4;
+ __le32 w5;
+ __le32 w6;
+ __le32 w7;
+} __packed;
+
+#define RTW89_C2H_MAC_TX_RPT_W2_TX_STATE GENMASK(7, 6)
+#define RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE GENMASK(11, 8)
+#define RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT GENMASK(13, 8)
+#define RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT_V1 GENMASK(15, 10)
+
+struct rtw89_c2h_mac_tx_rpt_v2 {
+ struct rtw89_c2h_hdr hdr;
+ __le32 w2;
+ __le32 w3;
+ __le32 w4;
+ __le32 w5;
+ __le32 w6;
+ __le32 w7;
+ __le32 w8;
+ __le32 w9;
+ __le32 w10;
+ __le32 w11;
+ __le32 w12;
+ __le32 w13;
+ __le32 w14;
+ __le32 w15;
+ __le32 w16;
+ __le32 w17;
+ __le32 w18;
+ __le32 w19;
+} __packed;
+
+#define RTW89_C2H_MAC_TX_RPT_W12_TX_STATE_V2 GENMASK(9, 8)
+#define RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2 GENMASK(15, 12)
+#define RTW89_C2H_MAC_TX_RPT_W14_DATA_TX_CNT_V2 GENMASK(15, 10)
+
struct rtw89_mac_mcc_tsf_rpt {
u32 macid_x;
u32 macid_y;
@@ -3985,6 +4043,7 @@ enum rtw89_fw_element_id {
RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ = 25,
RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ = 26,
RTW89_FW_ELEMENT_ID_AFE_PWR_SEQ = 27,
+ RTW89_FW_ELEMENT_ID_DIAG_MAC = 28,
RTW89_FW_ELEMENT_ID_NUM,
};
@@ -4162,6 +4221,11 @@ struct rtw89_fw_element_hdr {
__le32 val;
} __packed infos[];
} __packed afe;
+ struct {
+ __le32 rule_size;
+ u8 rsvd[4];
+ u8 rules_and_msgs[];
+ } __packed diag_mac;
struct __rtw89_fw_txpwr_element txpwr;
struct __rtw89_fw_regd_element regd;
} __packed u;
@@ -4823,7 +4887,8 @@ int rtw89_fw_h2c_tbtt_tuning(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link, u32 offset);
int rtw89_fw_h2c_pwr_lvl(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link);
int rtw89_fw_h2c_cam(struct rtw89_dev *rtwdev, struct rtw89_vif_link *vif,
- struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr);
+ struct rtw89_sta_link *rtwsta_link, const u8 *scan_mac_addr,
+ enum rtw89_upd_mode upd_mode);
int rtw89_fw_h2c_dctl_sec_cam_v1(struct rtw89_dev *rtwdev,
struct rtw89_vif_link *rtwvif_link,
struct rtw89_sta_link *rtwsta_link);
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index fd11b8fb3c89..d78fbe73e365 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -12,6 +12,7 @@
#include "phy.h"
#include "ps.h"
#include "reg.h"
+#include "ser.h"
#include "util.h"
static const u32 rtw89_mac_mem_base_addrs_ax[RTW89_MAC_MEM_NUM] = {
@@ -1294,11 +1295,26 @@ static int rtw89_mac_sub_pwr_seq(struct rtw89_dev *rtwdev, u8 cv_msk,
static int rtw89_mac_pwr_seq(struct rtw89_dev *rtwdev,
const struct rtw89_pwr_cfg * const *cfg_seq)
{
+ u8 intf_msk;
int ret;
+ switch (rtwdev->hci.type) {
+ case RTW89_HCI_TYPE_PCIE:
+ intf_msk = PWR_INTF_MSK_PCIE;
+ break;
+ case RTW89_HCI_TYPE_USB:
+ intf_msk = PWR_INTF_MSK_USB;
+ break;
+ case RTW89_HCI_TYPE_SDIO:
+ intf_msk = PWR_INTF_MSK_SDIO;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
for (; *cfg_seq; cfg_seq++) {
ret = rtw89_mac_sub_pwr_seq(rtwdev, BIT(rtwdev->hal.cv),
- PWR_INTF_MSK_PCIE, *cfg_seq);
+ intf_msk, *cfg_seq);
if (ret)
return -EBUSY;
}
@@ -1423,13 +1439,15 @@ void rtw89_mac_power_mode_change(struct rtw89_dev *rtwdev, bool enter)
if (!ret)
break;
- if (i == RPWM_TRY_CNT - 1)
+ if (i == RPWM_TRY_CNT - 1) {
rtw89_err(rtwdev, "firmware failed to ack for %s ps mode\n",
enter ? "entering" : "leaving");
- else
+ rtw89_ser_notify(rtwdev, MAC_AX_ERR_ASSERTION);
+ } else {
rtw89_debug(rtwdev, RTW89_DBG_UNEXP,
"%d time firmware failed to ack for %s ps mode\n",
i + 1, enter ? "entering" : "leaving");
+ }
}
}
@@ -1651,6 +1669,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
/* PCIE 64 */
.wde_size0 = {RTW89_WDE_PG_64, 4095, 1,},
.wde_size0_v1 = {RTW89_WDE_PG_64, 3328, 0, 0,},
+ /* 8852A USB */
+ .wde_size1 = {RTW89_WDE_PG_64, 768, 0,},
/* DLFW */
.wde_size4 = {RTW89_WDE_PG_64, 0, 4096,},
.wde_size4_v1 = {RTW89_WDE_PG_64, 0, 3328, 0,},
@@ -1660,6 +1680,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
.wde_size7 = {RTW89_WDE_PG_64, 510, 2,},
/* DLFW */
.wde_size9 = {RTW89_WDE_PG_64, 0, 1024,},
+ /* 8852C USB3.0 */
+ .wde_size17 = {RTW89_WDE_PG_64, 354, 30,},
/* 8852C DLFW */
.wde_size18 = {RTW89_WDE_PG_64, 0, 2048,},
/* 8852C PCIE SCC */
@@ -1667,9 +1689,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
.wde_size23 = {RTW89_WDE_PG_64, 1022, 2,},
/* 8852B USB2.0/USB3.0 SCC */
.wde_size25 = {RTW89_WDE_PG_64, 162, 94,},
+ /* 8852C USB2.0 */
+ .wde_size31 = {RTW89_WDE_PG_64, 384, 0,},
/* PCIE */
.ple_size0 = {RTW89_PLE_PG_128, 1520, 16,},
.ple_size0_v1 = {RTW89_PLE_PG_128, 2688, 240, 212992,},
+ /* 8852A USB */
+ .ple_size1 = {RTW89_PLE_PG_128, 3184, 16,},
.ple_size3_v1 = {RTW89_PLE_PG_128, 2928, 0, 212992,},
/* DLFW */
.ple_size4 = {RTW89_PLE_PG_128, 64, 1472,},
@@ -1678,6 +1704,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
/* DLFW */
.ple_size8 = {RTW89_PLE_PG_128, 64, 960,},
.ple_size9 = {RTW89_PLE_PG_128, 2288, 16,},
+ /* 8852C USB */
+ .ple_size17 = {RTW89_PLE_PG_128, 3368, 24,},
/* 8852C DLFW */
.ple_size18 = {RTW89_PLE_PG_128, 2544, 16,},
/* 8852C PCIE SCC */
@@ -1686,15 +1714,21 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
.ple_size32 = {RTW89_PLE_PG_128, 620, 20,},
/* 8852B USB3.0 SCC */
.ple_size33 = {RTW89_PLE_PG_128, 632, 8,},
+ /* 8852C USB2.0 */
+ .ple_size34 = {RTW89_PLE_PG_128, 3374, 18,},
/* PCIE 64 */
.wde_qt0 = {3792, 196, 0, 107,},
.wde_qt0_v1 = {3302, 6, 0, 20,},
+ /* 8852A USB */
+ .wde_qt1 = {512, 196, 0, 60,},
/* DLFW */
.wde_qt4 = {0, 0, 0, 0,},
/* PCIE 64 */
.wde_qt6 = {448, 48, 0, 16,},
/* 8852B PCIE SCC */
.wde_qt7 = {446, 48, 0, 16,},
+ /* 8852C USB3.0 */
+ .wde_qt16 = {344, 2, 0, 8,},
/* 8852C DLFW */
.wde_qt17 = {0, 0, 0, 0,},
/* 8852C PCIE SCC */
@@ -1702,6 +1736,8 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
.wde_qt23 = {958, 48, 0, 16,},
/* 8852B USB2.0/USB3.0 SCC */
.wde_qt25 = {152, 2, 0, 8,},
+ /* 8852C USB2.0 */
+ .wde_qt31 = {338, 6, 0, 40,},
.ple_qt0 = {320, 320, 32, 16, 13, 13, 292, 292, 64, 18, 1, 4, 0,},
.ple_qt1 = {320, 320, 32, 16, 1316, 1316, 1595, 1595, 1367, 1321, 1, 1307, 0,},
/* PCIE SCC */
@@ -1713,6 +1749,13 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
.ple_qt13 = {0, 0, 16, 48, 0, 0, 0, 0, 0, 0, 0,},
/* PCIE 64 */
.ple_qt18 = {147, 0, 16, 20, 17, 13, 89, 0, 32, 14, 8, 0,},
+ /* 8852A USB SCC */
+ .ple_qt25 = {1536, 0, 16, 48, 13, 13, 360, 0, 32, 40, 8, 0,},
+ .ple_qt26 = {2654, 0, 1134, 48, 64, 13, 1478, 0, 64, 128, 120, 0,},
+ /* USB 52C USB3.0 */
+ .ple_qt42 = {1068, 0, 16, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,},
+ /* USB 52C USB3.0 */
+ .ple_qt43 = {3068, 0, 32, 48, 4, 13, 178, 0, 16, 1, 8, 16, 0,},
/* DLFW 52C */
.ple_qt44 = {0, 0, 16, 256, 0, 0, 0, 0, 0, 0, 0, 0,},
/* DLFW 52C */
@@ -1732,6 +1775,10 @@ const struct rtw89_mac_size_set rtw89_mac_size = {
/* USB3.0 52B 92K */
.ple_qt74 = {286, 0, 16, 48, 4, 13, 178, 0, 32, 14, 8, 0, 0,},
.ple_qt75 = {286, 0, 32, 48, 37, 13, 211, 0, 65, 14, 24, 0, 0,},
+ /* USB2.0 52C */
+ .ple_qt78 = {1560, 0, 16, 48, 13, 13, 390, 0, 32, 38, 8, 16, 0,},
+ /* USB2.0 52C */
+ .ple_qt79 = {1560, 0, 32, 48, 1253, 13, 1630, 0, 1272, 38, 120, 1256, 0,},
/* 8852A PCIE WOW */
.ple_qt_52a_wow = {264, 0, 32, 20, 64, 13, 1005, 0, 64, 128, 120,},
/* 8852B PCIE WOW */
@@ -2324,7 +2371,8 @@ static int sec_eng_init_ax(struct rtw89_dev *rtwdev)
if (chip->chip_id == RTL8852C)
val |= B_AX_UC_MGNT_DEC;
if (chip->chip_id == RTL8852A || chip->chip_id == RTL8852B ||
- chip->chip_id == RTL8851B)
+ chip->chip_id == RTL8851B ||
+ (chip->chip_id == RTL8852C && rtwdev->hci.type == RTW89_HCI_TYPE_USB))
val &= ~B_AX_TX_PARTIAL_MODE;
rtw89_write32(rtwdev, R_AX_SEC_ENG_CTRL, val);
@@ -2495,6 +2543,20 @@ static int rtw89_mac_typ_fltr_opt_ax(struct rtw89_dev *rtwdev,
return 0;
}
+void rtw89_mac_set_rx_fltr(struct rtw89_dev *rtwdev, u8 mac_idx, u32 rx_fltr)
+{
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+ u32 reg;
+ u32 val;
+
+ reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, mac_idx);
+
+ val = rtw89_read32(rtwdev, reg);
+ /* B_AX_RX_FLTR_CFG_MASK is not a consecutive bit mask */
+ val = (val & ~B_AX_RX_FLTR_CFG_MASK) | (rx_fltr & B_AX_RX_FLTR_CFG_MASK);
+ rtw89_write32(rtwdev, reg, val);
+}
+
static int rx_fltr_init_ax(struct rtw89_dev *rtwdev, u8 mac_idx)
{
int ret, i;
@@ -3980,8 +4042,15 @@ static void rtw89_mac_dmac_func_pre_en_ax(struct rtw89_dev *rtwdev)
val = rtw89_read32(rtwdev, R_AX_HAXI_INIT_CFG1);
val &= ~(B_AX_DMA_MODE_MASK | B_AX_STOP_AXI_MST);
- val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_PCIE_1B) |
- B_AX_TXHCI_EN_V1 | B_AX_RXHCI_EN_V1;
+ val |= B_AX_TXHCI_EN_V1 | B_AX_RXHCI_EN_V1;
+
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE)
+ val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_PCIE_1B);
+ else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB)
+ val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_USB);
+ else
+ val |= FIELD_PREP(B_AX_DMA_MODE_MASK, DMA_MOD_SDIO);
+
rtw89_write32(rtwdev, R_AX_HAXI_INIT_CFG1, val);
rtw89_write32_clr(rtwdev, R_AX_HAXI_DMA_STOP1,
@@ -4049,9 +4118,12 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb)
rtw89_mac_ctrl_hci_dma_trx(rtwdev, true);
if (include_bb) {
- rtw89_chip_bb_preinit(rtwdev, RTW89_PHY_0);
- if (rtwdev->dbcc_en)
- rtw89_chip_bb_preinit(rtwdev, RTW89_PHY_1);
+ /* Only call BB preinit including configuration of BB MCU for
+ * the chips which need to download BB MCU firmware. Otherwise,
+ * calling preinit later to prevent touching registers affecting
+ * download firmware.
+ */
+ rtw89_chip_bb_preinit(rtwdev);
}
ret = rtw89_mac_dmac_pre_init(rtwdev);
@@ -4071,17 +4143,24 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb)
return 0;
}
-int rtw89_mac_init(struct rtw89_dev *rtwdev)
+int rtw89_mac_preinit(struct rtw89_dev *rtwdev)
{
- const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
- const struct rtw89_chip_info *chip = rtwdev->chip;
- bool include_bb = !!chip->bbmcu_nr;
int ret;
ret = rtw89_mac_pwr_on(rtwdev);
if (ret)
return ret;
+ return 0;
+}
+
+int rtw89_mac_init(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ bool include_bb = !!chip->bbmcu_nr;
+ int ret;
+
ret = rtw89_mac_partial_init(rtwdev, include_bb);
if (ret)
goto fail;
@@ -4770,7 +4849,7 @@ int rtw89_mac_vif_init(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l
if (ret)
return ret;
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_CREATE);
if (ret)
return ret;
@@ -4795,7 +4874,7 @@ int rtw89_mac_vif_deinit(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif
rtw89_cam_deinit(rtwdev, rtwvif_link);
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_REMOVE);
if (ret)
return ret;
@@ -5244,8 +5323,19 @@ rtw89_mac_c2h_bcn_cnt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
}
static void
-rtw89_mac_c2h_bcn_upd_done(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+rtw89_mac_c2h_bcn_upd_done(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 len)
{
+ const struct rtw89_c2h_bcn_upd_done *c2h =
+ (const struct rtw89_c2h_bcn_upd_done *)skb_c2h->data;
+ u8 band, port, mbssid;
+
+ port = le32_get_bits(c2h->w2, RTW89_C2H_BCN_UPD_DONE_W2_PORT);
+ mbssid = le32_get_bits(c2h->w2, RTW89_C2H_BCN_UPD_DONE_W2_MBSSID);
+ band = le32_get_bits(c2h->w2, RTW89_C2H_BCN_UPD_DONE_W2_BAND_IDX);
+
+ rtw89_debug(rtwdev, RTW89_DBG_FW,
+ "BCN update done on port:%d mbssid:%d band:%d\n",
+ port, mbssid, band);
}
static void
@@ -5458,6 +5548,72 @@ rtw89_mac_c2h_mcc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32
}
static void
+rtw89_mac_c2h_tx_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+ struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt;
+ struct rtw89_tx_skb_data *skb_data;
+ u8 sw_define, tx_status, txcnt;
+ struct sk_buff *skb;
+
+ if (rtwdev->chip->chip_id == RTL8922A) {
+ const struct rtw89_c2h_mac_tx_rpt_v2 *rpt_v2;
+
+ rpt_v2 = (const struct rtw89_c2h_mac_tx_rpt_v2 *)c2h->data;
+ sw_define = le32_get_bits(rpt_v2->w12,
+ RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2);
+ tx_status = le32_get_bits(rpt_v2->w12,
+ RTW89_C2H_MAC_TX_RPT_W12_TX_STATE_V2);
+ txcnt = le32_get_bits(rpt_v2->w14,
+ RTW89_C2H_MAC_TX_RPT_W14_DATA_TX_CNT_V2);
+ } else {
+ const struct rtw89_c2h_mac_tx_rpt *rpt;
+
+ rpt = (const struct rtw89_c2h_mac_tx_rpt *)c2h->data;
+ sw_define = le32_get_bits(rpt->w2, RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE);
+ tx_status = le32_get_bits(rpt->w2, RTW89_C2H_MAC_TX_RPT_W2_TX_STATE);
+ if (rtwdev->chip->chip_id == RTL8852C)
+ txcnt = le32_get_bits(rpt->w5,
+ RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT_V1);
+ else
+ txcnt = le32_get_bits(rpt->w5,
+ RTW89_C2H_MAC_TX_RPT_W5_DATA_TX_CNT);
+ }
+
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+ "C2H TX RPT: sn %d, tx_status %d, txcnt %d\n",
+ sw_define, tx_status, txcnt);
+
+ /* claim sw_define is not over size of tx_rpt->skbs[] */
+ static_assert(hweight32(RTW89_MAX_TX_RPTS_MASK) ==
+ hweight32(RTW89_C2H_MAC_TX_RPT_W12_SW_DEFINE_V2) &&
+ hweight32(RTW89_MAX_TX_RPTS_MASK) ==
+ hweight32(RTW89_C2H_MAC_TX_RPT_W2_SW_DEFINE));
+
+ scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) {
+ skb = tx_rpt->skbs[sw_define];
+
+ /* skip if no skb (normally shouldn't happen) */
+ if (!skb) {
+ rtw89_debug(rtwdev, RTW89_DBG_TXRX,
+ "C2H TX RPT: no skb found in queue\n");
+ return;
+ }
+
+ skb_data = RTW89_TX_SKB_CB(skb);
+
+ /* skip if TX attempt has failed and retry limit has not been
+ * reached yet
+ */
+ if (tx_status != RTW89_TX_DONE &&
+ txcnt != skb_data->tx_pkt_cnt_lmt)
+ return;
+
+ tx_rpt->skbs[sw_define] = NULL;
+ rtw89_tx_rpt_tx_status(rtwdev, skb, tx_status);
+ }
+}
+
+static void
rtw89_mac_c2h_mrc_tsf_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
{
struct rtw89_wait_info *wait = &rtwdev->mcc.wait;
@@ -5692,6 +5848,12 @@ void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev,
};
static
+void (* const rtw89_mac_c2h_misc_handler[])(struct rtw89_dev *rtwdev,
+ struct sk_buff *c2h, u32 len) = {
+ [RTW89_MAC_C2H_FUNC_TX_REPORT] = rtw89_mac_c2h_tx_rpt,
+};
+
+static
void (* const rtw89_mac_c2h_mlo_handler[])(struct rtw89_dev *rtwdev,
struct sk_buff *c2h, u32 len) = {
[RTW89_MAC_C2H_FUNC_MLO_GET_TBL] = NULL,
@@ -5777,6 +5939,8 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
}
case RTW89_MAC_C2H_CLASS_MCC:
return true;
+ case RTW89_MAC_C2H_CLASS_MISC:
+ return true;
case RTW89_MAC_C2H_CLASS_MLO:
return true;
case RTW89_MAC_C2H_CLASS_MRC:
@@ -5812,6 +5976,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb,
if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC)
handler = rtw89_mac_c2h_mcc_handler[func];
break;
+ case RTW89_MAC_C2H_CLASS_MISC:
+ if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MISC)
+ handler = rtw89_mac_c2h_misc_handler[func];
+ break;
case RTW89_MAC_C2H_CLASS_MLO:
if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MLO)
handler = rtw89_mac_c2h_mlo_handler[func];
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index 25fe5e5c8a97..0007229d6753 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -432,6 +432,12 @@ enum rtw89_mac_c2h_mcc_func {
NUM_OF_RTW89_MAC_C2H_FUNC_MCC,
};
+enum rtw89_mac_c2h_misc_func {
+ RTW89_MAC_C2H_FUNC_TX_REPORT = 1,
+
+ NUM_OF_RTW89_MAC_C2H_FUNC_MISC,
+};
+
enum rtw89_mac_c2h_mlo_func {
RTW89_MAC_C2H_FUNC_MLO_GET_TBL = 0x0,
RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE = 0x1,
@@ -470,6 +476,7 @@ enum rtw89_mac_c2h_class {
RTW89_MAC_C2H_CLASS_WOW = 0x3,
RTW89_MAC_C2H_CLASS_MCC = 0x4,
RTW89_MAC_C2H_CLASS_FWDBG = 0x5,
+ RTW89_MAC_C2H_CLASS_MISC = 0x9,
RTW89_MAC_C2H_CLASS_MLO = 0xc,
RTW89_MAC_C2H_CLASS_MRC = 0xe,
RTW89_MAC_C2H_CLASS_AP = 0x18,
@@ -574,8 +581,6 @@ enum rtw89_mac_bf_rrsc_rate {
RTW89_MAC_BF_RRSC_MAX = 32
};
-#define RTW89_R32_EA 0xEAEAEAEA
-#define RTW89_R32_DEAD 0xDEADBEEF
#define MAC_REG_POOL_COUNT 10
#define ACCESS_CMAC(_addr) \
({typeof(_addr) __addr = (_addr); \
@@ -917,36 +922,45 @@ struct rtw89_mac_size_set {
const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c0;
const struct rtw89_hfc_prec_cfg hfc_prec_cfg_c2;
const struct rtw89_dle_size wde_size0;
+ const struct rtw89_dle_size wde_size1;
const struct rtw89_dle_size wde_size0_v1;
const struct rtw89_dle_size wde_size4;
const struct rtw89_dle_size wde_size4_v1;
const struct rtw89_dle_size wde_size6;
const struct rtw89_dle_size wde_size7;
const struct rtw89_dle_size wde_size9;
+ const struct rtw89_dle_size wde_size17;
const struct rtw89_dle_size wde_size18;
const struct rtw89_dle_size wde_size19;
const struct rtw89_dle_size wde_size23;
const struct rtw89_dle_size wde_size25;
+ const struct rtw89_dle_size wde_size31;
const struct rtw89_dle_size ple_size0;
+ const struct rtw89_dle_size ple_size1;
const struct rtw89_dle_size ple_size0_v1;
const struct rtw89_dle_size ple_size3_v1;
const struct rtw89_dle_size ple_size4;
const struct rtw89_dle_size ple_size6;
const struct rtw89_dle_size ple_size8;
const struct rtw89_dle_size ple_size9;
+ const struct rtw89_dle_size ple_size17;
const struct rtw89_dle_size ple_size18;
const struct rtw89_dle_size ple_size19;
const struct rtw89_dle_size ple_size32;
const struct rtw89_dle_size ple_size33;
+ const struct rtw89_dle_size ple_size34;
const struct rtw89_wde_quota wde_qt0;
+ const struct rtw89_wde_quota wde_qt1;
const struct rtw89_wde_quota wde_qt0_v1;
const struct rtw89_wde_quota wde_qt4;
const struct rtw89_wde_quota wde_qt6;
const struct rtw89_wde_quota wde_qt7;
+ const struct rtw89_wde_quota wde_qt16;
const struct rtw89_wde_quota wde_qt17;
const struct rtw89_wde_quota wde_qt18;
const struct rtw89_wde_quota wde_qt23;
const struct rtw89_wde_quota wde_qt25;
+ const struct rtw89_wde_quota wde_qt31;
const struct rtw89_ple_quota ple_qt0;
const struct rtw89_ple_quota ple_qt1;
const struct rtw89_ple_quota ple_qt4;
@@ -954,6 +968,10 @@ struct rtw89_mac_size_set {
const struct rtw89_ple_quota ple_qt9;
const struct rtw89_ple_quota ple_qt13;
const struct rtw89_ple_quota ple_qt18;
+ const struct rtw89_ple_quota ple_qt25;
+ const struct rtw89_ple_quota ple_qt26;
+ const struct rtw89_ple_quota ple_qt42;
+ const struct rtw89_ple_quota ple_qt43;
const struct rtw89_ple_quota ple_qt44;
const struct rtw89_ple_quota ple_qt45;
const struct rtw89_ple_quota ple_qt46;
@@ -965,6 +983,8 @@ struct rtw89_mac_size_set {
const struct rtw89_ple_quota ple_qt73;
const struct rtw89_ple_quota ple_qt74;
const struct rtw89_ple_quota ple_qt75;
+ const struct rtw89_ple_quota ple_qt78;
+ const struct rtw89_ple_quota ple_qt79;
const struct rtw89_ple_quota ple_qt_52a_wow;
const struct rtw89_ple_quota ple_qt_52b_wow;
const struct rtw89_ple_quota ple_qt_52bt_wow;
@@ -1181,6 +1201,7 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l
int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev);
void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev);
int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb);
+int rtw89_mac_preinit(struct rtw89_dev *rtwdev);
int rtw89_mac_init(struct rtw89_dev *rtwdev);
int rtw89_mac_dle_init(struct rtw89_dev *rtwdev, enum rtw89_qta_mode mode,
enum rtw89_qta_mode ext_mode);
@@ -1319,6 +1340,7 @@ int rtw89_mac_cfg_ppdu_status_bands(struct rtw89_dev *rtwdev, bool enable)
return rtw89_mac_cfg_ppdu_status(rtwdev, RTW89_MAC_1, enable);
}
+void rtw89_mac_set_rx_fltr(struct rtw89_dev *rtwdev, u8 mac_idx, u32 rx_fltr);
void rtw89_mac_update_rts_threshold(struct rtw89_dev *rtwdev);
void rtw89_mac_flush_txq(struct rtw89_dev *rtwdev, u32 queues, bool drop);
int rtw89_mac_coex_init(struct rtw89_dev *rtwdev, const struct rtw89_mac_ax_coex *coex);
@@ -1609,4 +1631,92 @@ int rtw89_mac_scan_offload(struct rtw89_dev *rtwdev,
return ret;
}
+
+static inline
+void rtw89_tx_rpt_init(struct rtw89_dev *rtwdev,
+ struct rtw89_core_tx_request *tx_req)
+{
+ struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt;
+
+ if (!rtwdev->hci.tx_rpt_enabled)
+ return;
+
+ tx_req->desc_info.report = true;
+ /* firmware maintains a 4-bit sequence number */
+ tx_req->desc_info.sn = atomic_inc_return(&tx_rpt->sn) &
+ RTW89_MAX_TX_RPTS_MASK;
+ tx_req->desc_info.tx_cnt_lmt_en = true;
+ tx_req->desc_info.tx_cnt_lmt = 8;
+}
+
+static inline
+bool rtw89_is_tx_rpt_skb(struct rtw89_dev *rtwdev, struct sk_buff *skb)
+{
+ struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ return rtw89_core_is_tx_wait(rtwdev, skb_data) ||
+ (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS);
+}
+
+static inline
+void rtw89_tx_rpt_tx_status(struct rtw89_dev *rtwdev, struct sk_buff *skb,
+ u8 tx_status)
+{
+ struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb);
+ struct ieee80211_tx_info *info;
+
+ if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status))
+ return;
+
+ info = IEEE80211_SKB_CB(skb);
+ ieee80211_tx_info_clear_status(info);
+
+ if (tx_status == RTW89_TX_DONE)
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ else
+ info->flags &= ~IEEE80211_TX_STAT_ACK;
+
+ ieee80211_tx_status_irqsafe(rtwdev->hw, skb);
+}
+
+static inline
+void rtw89_tx_rpt_skb_add(struct rtw89_dev *rtwdev, struct sk_buff *skb)
+{
+ struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt;
+ struct rtw89_tx_skb_data *skb_data;
+ u8 idx;
+
+ skb_data = RTW89_TX_SKB_CB(skb);
+ idx = skb_data->tx_rpt_sn;
+
+ scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) {
+ /* if skb having the similar seq number is still in the queue,
+ * this means the queue is overflowed - it isn't normal and
+ * should indicate firmware doesn't provide TX reports in time;
+ * report the old skb as dropped, we can't do much more here
+ */
+ if (tx_rpt->skbs[idx])
+ rtw89_tx_rpt_tx_status(rtwdev, tx_rpt->skbs[idx],
+ RTW89_TX_MACID_DROP);
+ tx_rpt->skbs[idx] = skb;
+ }
+}
+
+static inline
+void rtw89_tx_rpt_skbs_purge(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_tx_rpt *tx_rpt = &rtwdev->tx_rpt;
+ struct sk_buff *skbs[RTW89_MAX_TX_RPTS];
+
+ scoped_guard(spinlock_irqsave, &tx_rpt->skb_lock) {
+ memcpy(skbs, tx_rpt->skbs, sizeof(tx_rpt->skbs));
+ memset(tx_rpt->skbs, 0, sizeof(tx_rpt->skbs));
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(skbs); i++)
+ if (skbs[i])
+ rtw89_tx_rpt_tx_status(rtwdev, skbs[i],
+ RTW89_TX_MACID_DROP);
+}
#endif
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 7b04183a3a5d..f39ca1c2ed10 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -220,6 +220,8 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw,
if (ret)
goto unset_link;
+ rtwdev->pure_monitor_mode_vif = vif->type == NL80211_IFTYPE_MONITOR ?
+ rtwvif : NULL;
rtw89_recalc_lps(rtwdev);
return 0;
@@ -267,6 +269,8 @@ bottom:
rtw89_core_release_bit_map(rtwdev->hw_port, port);
rtw89_release_mac_id(rtwdev, macid);
+ rtwdev->pure_monitor_mode_vif = NULL;
+
rtw89_recalc_lps(rtwdev);
rtw89_enter_ips_by_hwflags(rtwdev);
}
@@ -303,7 +307,6 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
u64 multicast)
{
struct rtw89_dev *rtwdev = hw->priv;
- const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
u32 rx_fltr;
lockdep_assert_wiphy(hw->wiphy);
@@ -365,16 +368,10 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw,
rx_fltr &= ~B_AX_A_A1_MATCH;
}
- rtw89_write32_mask(rtwdev,
- rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_0),
- B_AX_RX_FLTR_CFG_MASK,
- rx_fltr);
+ rtw89_mac_set_rx_fltr(rtwdev, RTW89_MAC_0, rx_fltr);
if (!rtwdev->dbcc_en)
return;
- rtw89_write32_mask(rtwdev,
- rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_1),
- B_AX_RX_FLTR_CFG_MASK,
- rx_fltr);
+ rtw89_mac_set_rx_fltr(rtwdev, RTW89_MAC_1, rx_fltr);
}
static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS] = {
@@ -718,6 +715,17 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ARP_FILTER)
rtwvif->ip_addr = vif->cfg.arp_addr_list[0];
+
+ if (changed & BSS_CHANGED_MLD_VALID_LINKS) {
+ struct rtw89_vif_link *cur = rtw89_get_designated_link(rtwvif);
+
+ rtw89_chip_rfk_channel(rtwdev, cur);
+
+ if (hweight16(vif->active_links) == 1)
+ rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR;
+ else
+ rtwvif->mlo_mode = RTW89_MLO_MODE_EMLSR;
+ }
}
static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,
@@ -744,7 +752,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_BSSID) {
ether_addr_copy(rtwvif_link->bssid, conf->bssid);
rtw89_cam_bssid_changed(rtwdev, rtwvif_link);
- rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL);
+ rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_INFO_CHANGE);
WRITE_ONCE(rtwvif_link->sync_bcn_tsf, 0);
}
@@ -803,7 +811,7 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw,
rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL);
rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, NULL, RTW89_ROLE_TYPE_CHANGE);
rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true);
- rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL);
+ rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_TYPE_CHANGE);
rtw89_chip_rfk_channel(rtwdev, rtwvif_link);
if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) {
@@ -954,6 +962,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
}
break;
case DISABLE_KEY:
+ flush_work(&rtwdev->txq_work);
rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1,
false);
rtw89_mac_flush_txq(rtwdev, BIT(rtwdev->hw->queues) - 1, false);
@@ -1132,12 +1141,17 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant, u3
{
struct rtw89_dev *rtwdev = hw->priv;
struct rtw89_hal *hal = &rtwdev->hal;
+ const struct rtw89_chip_info *chip;
lockdep_assert_wiphy(hw->wiphy);
+ chip = rtwdev->chip;
+
if (hal->ant_diversity) {
if (tx_ant != rx_ant || hweight32(tx_ant) != 1)
return -EINVAL;
+ } else if (chip->ops->cfg_txrx_path) {
+ /* With cfg_txrx_path ops, chips can configure rx_ant */
} else if (rx_ant != hw->wiphy->available_antennas_rx && rx_ant != hal->antenna_rx) {
return -EINVAL;
}
@@ -1531,10 +1545,29 @@ static bool rtw89_ops_can_activate_links(struct ieee80211_hw *hw,
u16 active_links)
{
struct rtw89_dev *rtwdev = hw->priv;
+ struct rtw89_vif *rtwvif = vif_to_rtwvif(vif);
+ u16 current_links = vif->active_links;
+ struct rtw89_vif_ml_trans trans = {
+ .mediate_links = current_links | active_links,
+ .links_to_del = current_links & ~active_links,
+ .links_to_add = active_links & ~current_links,
+ };
lockdep_assert_wiphy(hw->wiphy);
- return rtw89_can_work_on_links(rtwdev, vif, active_links);
+ if (!rtw89_can_work_on_links(rtwdev, vif, active_links))
+ return false;
+
+ /*
+ * Leave LPS at the beginning of ieee80211_set_active_links().
+ * Because the entire process takes the same lock as our track
+ * work, LPS will not enter during ieee80211_set_active_links().
+ */
+ rtw89_leave_lps(rtwdev);
+
+ rtwvif->ml_trans = trans;
+
+ return true;
}
static void __rtw89_ops_clr_vif_links(struct rtw89_dev *rtwdev,
@@ -1579,6 +1612,36 @@ static int __rtw89_ops_set_vif_links(struct rtw89_dev *rtwdev,
return 0;
}
+static void rtw89_vif_cfg_fw_links(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ unsigned long links, bool en)
+{
+ struct rtw89_vif_link *rtwvif_link;
+ unsigned int link_id;
+
+ for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) {
+ rtwvif_link = rtwvif->links[link_id];
+ if (unlikely(!rtwvif_link))
+ continue;
+
+ rtw89_fw_h2c_mlo_link_cfg(rtwdev, rtwvif_link, en);
+ }
+}
+
+static void rtw89_vif_update_fw_links(struct rtw89_dev *rtwdev,
+ struct rtw89_vif *rtwvif,
+ u16 current_links)
+{
+ struct rtw89_vif_ml_trans *trans = &rtwvif->ml_trans;
+
+ /* Do follow-up when all updating links exist. */
+ if (current_links != trans->mediate_links)
+ return;
+
+ rtw89_vif_cfg_fw_links(rtwdev, rtwvif, trans->links_to_del, false);
+ rtw89_vif_cfg_fw_links(rtwdev, rtwvif, trans->links_to_add, true);
+}
+
static
int rtw89_ops_change_vif_links(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -1620,6 +1683,8 @@ int rtw89_ops_change_vif_links(struct ieee80211_hw *hw,
if (rtwdev->scanning)
rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif);
+ rtw89_vif_update_fw_links(rtwdev, rtwvif, old_links);
+
if (!old_links)
__rtw89_ops_clr_vif_links(rtwdev, rtwvif,
BIT(RTW89_VIF_IDLE_LINK_ID));
diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index ef69672b6862..556e5f98e8d4 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -458,6 +458,7 @@ static void set_cpu_en(struct rtw89_dev *rtwdev, bool include_bb)
static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw)
{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
u32 val32;
int ret;
@@ -479,6 +480,7 @@ static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw)
rtw89_write32(rtwdev, R_BE_UDM1, 0);
rtw89_write32(rtwdev, R_BE_UDM2, 0);
+ rtw89_write32(rtwdev, R_BE_BOOT_DBG, 0x0);
rtw89_write32(rtwdev, R_BE_HALT_H2C, 0);
rtw89_write32(rtwdev, R_BE_HALT_C2H, 0);
rtw89_write32(rtwdev, R_BE_HALT_H2C_CTRL, 0);
@@ -493,6 +495,11 @@ static int wcpu_on(struct rtw89_dev *rtwdev, u8 boot_reason, bool dlfw)
B_BE_WDT_WAKE_PCIE_EN | B_BE_WDT_WAKE_USB_EN);
rtw89_write32_clr(rtwdev, R_BE_WCPU_FW_CTRL,
B_BE_WDT_PLT_RST_EN | B_BE_WCPU_ROM_CUT_GET);
+ rtw89_write32(rtwdev, R_BE_SECURE_BOOT_MALLOC_INFO, 0);
+ rtw89_write32_clr(rtwdev, R_BE_GPIO_MUXCFG, B_BE_BOOT_MODE);
+
+ if (chip->chip_id != RTL8922A)
+ rtw89_write32_set(rtwdev, R_BE_WCPU_FW_CTRL, B_BE_HOST_EXIST);
rtw89_write16_mask(rtwdev, R_BE_BOOT_REASON, B_BE_BOOT_REASON_MASK, boot_reason);
rtw89_write32_clr(rtwdev, R_BE_PLATFORM_ENABLE, B_BE_WCPU_EN);
@@ -2020,7 +2027,7 @@ int rtw89_mac_cfg_ppdu_status_be(struct rtw89_dev *rtwdev, u8 mac_idx, bool enab
}
rtw89_write32_mask(rtwdev, R_BE_HW_PPDU_STATUS, B_BE_FWD_PPDU_STAT_MASK, 3);
- rtw89_write32(rtwdev, reg, B_BE_PPDU_STAT_RPT_EN | B_BE_PPDU_MAC_INFO |
+ rtw89_write32(rtwdev, reg, B_BE_PPDU_STAT_RPT_EN |
B_BE_APP_RX_CNT_RPT | B_BE_APP_PLCP_HDR_RPT |
B_BE_PPDU_STAT_RPT_CRC32 | B_BE_PPDU_STAT_RPT_DMA);
diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c
index 0ee5f8579447..a66fcdb0293b 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.c
+++ b/drivers/net/wireless/realtek/rtw89/pci.c
@@ -464,7 +464,7 @@ static void rtw89_pci_tx_status(struct rtw89_dev *rtwdev,
struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb);
struct ieee80211_tx_info *info;
- if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status == RTW89_TX_DONE))
+ if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status))
return;
info = IEEE80211_SKB_CB(skb);
@@ -2064,6 +2064,20 @@ static void rtw89_pci_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 data)
writel(data, rtwpci->mmap + addr);
}
+static u32 rtw89_pci_ops_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr)
+{
+ struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv;
+ struct pci_dev *pdev = rtwpci->pdev;
+ u32 value;
+ int ret;
+
+ ret = pci_read_config_dword(pdev, addr, &value);
+ if (ret)
+ return RTW89_R32_EA;
+
+ return value;
+}
+
static void rtw89_pci_ctrl_dma_trx(struct rtw89_dev *rtwdev, bool enable)
{
const struct rtw89_pci_info *info = rtwdev->pci_info;
@@ -4683,6 +4697,8 @@ static const struct rtw89_hci_ops rtw89_pci_ops = {
.write16 = rtw89_pci_ops_write16,
.write32 = rtw89_pci_ops_write32,
+ .read32_pci_cfg = rtw89_pci_ops_read32_pci_cfg,
+
.mac_pre_init = rtw89_pci_ops_mac_pre_init,
.mac_pre_deinit = rtw89_pci_ops_mac_pre_deinit,
.mac_post_init = rtw89_pci_ops_mac_post_init,
diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h
index cb05c83dfd56..16dfb0e79d77 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.h
+++ b/drivers/net/wireless/realtek/rtw89/pci.h
@@ -1487,10 +1487,6 @@ struct rtw89_pci_tx_addr_info_32_v1 {
#define RTW89_PCI_RPP_POLLUTED BIT(31)
#define RTW89_PCI_RPP_SEQ GENMASK(30, 16)
#define RTW89_PCI_RPP_TX_STATUS GENMASK(15, 13)
-#define RTW89_TX_DONE 0x0
-#define RTW89_TX_RETRY_LIMIT 0x1
-#define RTW89_TX_LIFE_TIME 0x2
-#define RTW89_TX_MACID_DROP 0x3
#define RTW89_PCI_RPP_QSEL GENMASK(12, 8)
#define RTW89_PCI_RPP_MACID GENMASK(7, 0)
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index ba7feadd7582..9f418b1fb7ed 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -231,7 +231,12 @@ static u64 rtw89_phy_ra_mask_cfg(struct rtw89_dev *rtwdev,
return -1;
}
- if (link_sta->he_cap.has_he) {
+ if (link_sta->eht_cap.has_eht) {
+ cfg_mask |= u64_encode_bits(mask->control[band].eht_mcs[0],
+ RA_MASK_EHT_1SS_RATES);
+ cfg_mask |= u64_encode_bits(mask->control[band].eht_mcs[1],
+ RA_MASK_EHT_2SS_RATES);
+ } else if (link_sta->he_cap.has_he) {
cfg_mask |= u64_encode_bits(mask->control[band].he_mcs[0],
RA_MASK_HE_1SS_RATES);
cfg_mask |= u64_encode_bits(mask->control[band].he_mcs[1],
@@ -471,6 +476,10 @@ static void rtw89_phy_ra_sta_update(struct rtw89_dev *rtwdev,
ra->ra_mask = ra_mask;
ra->fix_giltf_en = fix_giltf_en;
ra->fix_giltf = fix_giltf;
+ ra->partial_bw_er = link_sta->he_cap.has_he ?
+ !!(link_sta->he_cap.he_cap_elem.phy_cap_info[6] &
+ IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE) : 0;
+ ra->band = chan->band_type;
if (!csi)
return;
@@ -557,6 +566,14 @@ static bool __check_rate_pattern(struct rtw89_phy_rate_pattern *next,
return true;
}
+enum __rtw89_hw_rate_invalid_bases {
+ /* no EHT rate for ax chip */
+ RTW89_HW_RATE_EHT_NSS1_MCS0 = RTW89_HW_RATE_INVAL,
+ RTW89_HW_RATE_EHT_NSS2_MCS0 = RTW89_HW_RATE_INVAL,
+ RTW89_HW_RATE_EHT_NSS3_MCS0 = RTW89_HW_RATE_INVAL,
+ RTW89_HW_RATE_EHT_NSS4_MCS0 = RTW89_HW_RATE_INVAL,
+};
+
#define RTW89_HW_RATE_BY_CHIP_GEN(rate) \
{ \
[RTW89_CHIP_AX] = RTW89_HW_RATE_ ## rate, \
@@ -572,6 +589,12 @@ void __rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev,
struct rtw89_phy_rate_pattern next_pattern = {0};
const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,
rtwvif_link->chanctx_idx);
+ static const u16 hw_rate_eht[][RTW89_CHIP_GEN_NUM] = {
+ RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS1_MCS0),
+ RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS2_MCS0),
+ RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS3_MCS0),
+ RTW89_HW_RATE_BY_CHIP_GEN(EHT_NSS4_MCS0),
+ };
static const u16 hw_rate_he[][RTW89_CHIP_GEN_NUM] = {
RTW89_HW_RATE_BY_CHIP_GEN(HE_NSS1_MCS0),
RTW89_HW_RATE_BY_CHIP_GEN(HE_NSS2_MCS0),
@@ -596,6 +619,17 @@ void __rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev,
u8 tx_nss = rtwdev->hal.tx_nss;
u8 i;
+ if (chip_gen == RTW89_CHIP_AX)
+ goto rs_11ax;
+
+ for (i = 0; i < tx_nss; i++)
+ if (!__check_rate_pattern(&next_pattern, hw_rate_eht[i][chip_gen],
+ RA_MASK_EHT_RATES, RTW89_RA_MODE_EHT,
+ mask->control[nl_band].eht_mcs[i],
+ 0, true))
+ goto out;
+
+rs_11ax:
for (i = 0; i < tx_nss; i++)
if (!__check_rate_pattern(&next_pattern, hw_rate_he[i][chip_gen],
RA_MASK_HE_RATES, RTW89_RA_MODE_HE,
@@ -640,6 +674,13 @@ void __rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev,
if (!next_pattern.enable)
goto out;
+ if (unlikely(next_pattern.rate >= RTW89_HW_RATE_INVAL)) {
+ rtw89_debug(rtwdev, RTW89_DBG_RA,
+ "pattern invalid target: chip_gen %d, mode 0x%x\n",
+ chip_gen, next_pattern.ra_mode);
+ goto out;
+ }
+
rtwvif_link->rate_pattern = next_pattern;
rtw89_debug(rtwdev, RTW89_DBG_RA,
"configure pattern: rate 0x%x, mask 0x%llx, mode 0x%x\n",
@@ -2339,6 +2380,21 @@ static u8 rtw89_channel_to_idx(struct rtw89_dev *rtwdev, u8 band, u8 channel)
}
}
+static bool rtw89_phy_validate_txpwr_limit_bw(struct rtw89_dev *rtwdev,
+ u8 band, u8 bw)
+{
+ switch (band) {
+ case RTW89_BAND_2G:
+ return bw < RTW89_2G_BW_NUM;
+ case RTW89_BAND_5G:
+ return bw < RTW89_5G_BW_NUM;
+ case RTW89_BAND_6G:
+ return bw < RTW89_6G_BW_NUM;
+ default:
+ return false;
+ }
+}
+
s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch)
{
@@ -2363,6 +2419,11 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band,
};
s8 cstr;
+ if (!rtw89_phy_validate_txpwr_limit_bw(rtwdev, band, bw)) {
+ rtw89_warn(rtwdev, "invalid band %u bandwidth %u\n", band, bw);
+ return 0;
+ }
+
switch (band) {
case RTW89_BAND_2G:
if (has_ant_gain)
@@ -4551,7 +4612,7 @@ static void rtw89_dcfo_comp(struct rtw89_dev *rtwdev, s32 curr_cfo)
s32 dcfo_comp_val;
int sign;
- if (rtwdev->chip->chip_id == RTL8922A)
+ if (!dcfo_comp)
return;
if (!is_linked) {
diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c
index 3316a38a62d0..bd17714f13d1 100644
--- a/drivers/net/wireless/realtek/rtw89/phy_be.c
+++ b/drivers/net/wireless/realtek/rtw89/phy_be.c
@@ -266,6 +266,10 @@ static void rtw89_phy_config_bb_gain_be(struct rtw89_dev *rtwdev,
case 3:
rtw89_phy_cfg_bb_gain_op1db_be(rtwdev, arg, reg->data);
break;
+ case 15:
+ rtw89_phy_write32_idx(rtwdev, reg->addr & 0xFFFFF, MASKHWORD,
+ reg->data, RTW89_PHY_0);
+ break;
case 4:
/* This cfg_type is only used by rfe_type >= 50 with eFEM */
if (efuse->rfe_type < 50)
diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c
index cf58121eb541..3f69dd4361c3 100644
--- a/drivers/net/wireless/realtek/rtw89/ps.c
+++ b/drivers/net/wireless/realtek/rtw89/ps.c
@@ -11,6 +11,7 @@
#include "phy.h"
#include "ps.h"
#include "reg.h"
+#include "ser.h"
#include "util.h"
static int rtw89_fw_receive_lps_h2c_check(struct rtw89_dev *rtwdev, u8 macid)
@@ -26,16 +27,27 @@ static int rtw89_fw_receive_lps_h2c_check(struct rtw89_dev *rtwdev, u8 macid)
c2h_info.id = RTW89_FWCMD_C2HREG_FUNC_PS_LEAVE_ACK;
ret = rtw89_fw_msg_reg(rtwdev, NULL, &c2h_info);
if (ret)
- return ret;
+ goto fw_fail;
c2hreg_macid = u32_get_bits(c2h_info.u.c2hreg[0],
RTW89_C2HREG_PS_LEAVE_ACK_MACID);
c2hreg_ret = u32_get_bits(c2h_info.u.c2hreg[1], RTW89_C2HREG_PS_LEAVE_ACK_RET);
- if (macid != c2hreg_macid || c2hreg_ret)
+ if (macid != c2hreg_macid || c2hreg_ret) {
rtw89_warn(rtwdev, "rtw89: check lps h2c received by firmware fail\n");
+ ret = -EINVAL;
+ goto fw_fail;
+ }
+ rtwdev->ps_hang_cnt = 0;
return 0;
+
+fw_fail:
+ rtwdev->ps_hang_cnt++;
+ if (rtwdev->ps_hang_cnt >= RTW89_PS_HANG_MAX_CNT)
+ rtw89_ser_notify(rtwdev, MAC_AX_ERR_ASSERTION);
+
+ return ret;
}
static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid)
@@ -51,9 +63,16 @@ static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid)
mac->ps_status, chk_msk);
if (ret) {
rtw89_info(rtwdev, "rtw89: failed to leave lps state\n");
+
+ rtwdev->ps_hang_cnt++;
+ if (rtwdev->ps_hang_cnt >= RTW89_PS_HANG_MAX_CNT)
+ rtw89_ser_notify(rtwdev, MAC_AX_ERR_ASSERTION);
+
return -EBUSY;
}
+ rtwdev->ps_hang_cnt = 0;
+
return 0;
}
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index ed1d958bc49e..5b4a459cf29c 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -3963,6 +3963,24 @@
#define R_BE_EFUSE_CTRL_1_V1 0x0034
#define B_BE_EF_DATA_MASK GENMASK(31, 0)
+#define R_BE_GPIO_MUXCFG 0x0040
+#define B_BE_WCPU_AUTO_EN BIT(26)
+#define B_BE_WCPU_JTAG_EN BIT(24)
+#define B_BE_WCPU_DBG_EN BIT(23)
+#define B_BE_JTAG_CHAIN_EN BIT(20)
+#define B_BE_BOOT_MODE BIT(19)
+#define B_BE_WL_EECS_EXT_32K_SEL BIT(18)
+#define B_BE_WL_SEC_BONDING_OPT_STS BIT(17)
+#define B_BE_SECSIC_SEL BIT(16)
+#define B_BE_ENHTP BIT(14)
+#define B_BE_ENSIC BIT(12)
+#define B_BE_SIC_SWRST BIT(11)
+#define B_BE_PINMUX_PTA_EN BIT(10)
+#define B_BE_WL_BT_PTA_SEC BIT(9)
+#define B_BE_ENUARTTX BIT(8)
+#define B_BE_DBG_GNT_BT_S1_POLARITY BIT(4)
+#define B_BE_ENUARTRX BIT(2)
+
#define R_BE_GPIO_EXT_CTRL 0x0060
#define B_BE_GPIO_MOD_15_TO_8_MASK GENMASK(31, 24)
#define B_BE_GPIO_MOD_9 BIT(25)
@@ -4323,6 +4341,7 @@
#define B_BE_RUN_ENV_MASK GENMASK(31, 30)
#define B_BE_WCPU_FWDL_STATUS_MASK GENMASK(29, 26)
#define B_BE_WDT_PLT_RST_EN BIT(17)
+#define B_BE_HOST_EXIST BIT(16)
#define B_BE_FW_SEC_AUTH_DONE BIT(14)
#define B_BE_FW_CPU_UTIL_STS_EN BIT(13)
#define B_BE_BBMCU1_FWDL_EN BIT(12)
@@ -4599,6 +4618,10 @@
#define B_BE_HCI_RXDMA_EN BIT(1)
#define B_BE_HCI_TXDMA_EN BIT(0)
+#define R_BE_BOOT_DBG 0x78F0
+#define B_BE_BOOT_STATUS_MASK GENMASK(31, 16)
+#define B_BE_SECUREBOOT_STATUS_MASK GENMASK(15, 0)
+
#define R_BE_DBG_WOW_READY 0x815E
#define B_BE_DBG_WOW_READY GENMASK(7, 0)
@@ -7476,7 +7499,6 @@
#define B_BE_PPDU_STAT_RPT_ADDR BIT(4)
#define B_BE_APP_PLCP_HDR_RPT BIT(3)
#define B_BE_APP_RX_CNT_RPT BIT(2)
-#define B_BE_PPDU_MAC_INFO BIT(1)
#define B_BE_PPDU_STAT_RPT_EN BIT(0)
#define R_BE_RX_SR_CTRL 0x1144A
diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c
index 58582f8d2b74..209d84909f88 100644
--- a/drivers/net/wireless/realtek/rtw89/regd.c
+++ b/drivers/net/wireless/realtek/rtw89/regd.c
@@ -723,6 +723,8 @@ int rtw89_regd_init_hint(struct rtw89_dev *rtwdev)
chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code);
if (!rtw89_regd_is_ww(chip_regd)) {
rtwdev->regulatory.regd = chip_regd;
+ rtwdev->regulatory.programmed = true;
+
/* Ignore country ie if there is a country domain programmed in chip */
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
@@ -867,11 +869,6 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
else
wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
-
- rtw89_regd_apply_policy_unii4(rtwdev, wiphy);
- rtw89_regd_apply_policy_6ghz(rtwdev, wiphy);
- rtw89_regd_apply_policy_tas(rtwdev);
- rtw89_regd_apply_policy_ant_gain(rtwdev);
}
static
@@ -883,19 +880,22 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request
wiphy_lock(wiphy);
rtw89_leave_ps_mode(rtwdev);
- if (wiphy->regd) {
- rtw89_debug(rtwdev, RTW89_DBG_REGD,
- "There is a country domain programmed in chip, ignore notifications\n");
- goto exit;
- }
+ if (rtwdev->regulatory.programmed)
+ goto policy;
+
rtw89_regd_notifier_apply(rtwdev, wiphy, request);
rtw89_debug_regd(rtwdev, rtwdev->regulatory.regd,
"get from initiator %d, alpha2",
request->initiator);
+policy:
+ rtw89_regd_apply_policy_unii4(rtwdev, wiphy);
+ rtw89_regd_apply_policy_6ghz(rtwdev, wiphy);
+ rtw89_regd_apply_policy_tas(rtwdev);
+ rtw89_regd_apply_policy_ant_gain(rtwdev);
+
rtw89_core_set_chip_txpwr(rtwdev);
-exit:
wiphy_unlock(wiphy);
}
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
index edcbda124916..84b628d23882 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c
@@ -2537,7 +2537,9 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = {
.query_rxdesc = rtw89_core_query_rxdesc,
.fill_txdesc = rtw89_core_fill_txdesc,
.fill_txdesc_fwcmd = rtw89_core_fill_txdesc,
- .get_ch_dma = rtw89_core_get_ch_dma,
+ .get_ch_dma = {rtw89_core_get_ch_dma,
+ rtw89_core_get_ch_dma,
+ NULL,},
.cfg_ctrl_path = rtw89_mac_cfg_ctrl_path,
.mac_cfg_gnt = rtw89_mac_cfg_gnt,
.stop_sch_tx = rtw89_mac_stop_sch_tx,
@@ -2646,6 +2648,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = {
.bacam_num = 2,
.bacam_dynamic_num = 4,
.bacam_ver = RTW89_BACAM_V0,
+ .addrcam_ver = 0,
.ppdu_max_usr = 4,
.sec_ctrl_efuse_size = 4,
.physical_efuse_size = 1216,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c
index 84c46d2f4d85..e574a9950a3b 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851b_rfk.c
@@ -1626,7 +1626,7 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
iqk_info->iqk_table_idx[path] = idx;
rtw89_debug(rtwdev, RTW89_DBG_RFK, "[IQK]S%d (PHY%d): / DBCC %s/ %s/ CH%d/ %s\n",
- path, phy, rtwdev->dbcc_en ? "on" : "off",
+ path, phy, str_on_off(rtwdev->dbcc_en),
iqk_info->iqk_band[path] == 0 ? "2G" :
iqk_info->iqk_band[path] == 1 ? "5G" : "6G",
iqk_info->iqk_ch[path],
@@ -1901,8 +1901,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
rtw89_debug(rtwdev, RTW89_DBG_RFK,
"[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n",
path, dpk->cur_idx[path], phy,
- rtwdev->is_tssi_mode[path] ? "on" : "off",
- rtwdev->dbcc_en ? "on" : "off",
+ str_on_off(rtwdev->is_tssi_mode[path]),
+ str_on_off(rtwdev->dbcc_en),
dpk->bp[path][kidx].band == 0 ? "2G" :
dpk->bp[path][kidx].band == 1 ? "5G" : "6G",
dpk->bp[path][kidx].ch,
@@ -2016,7 +2016,7 @@ static void _dpk_txpwr_bb_force(struct rtw89_dev *rtwdev,
rtw89_phy_write32_mask(rtwdev, R_TXPWRB_H + (path << 13), B_TXPWRB_RDY, force);
rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d txpwr_bb_force %s\n",
- path, force ? "on" : "off");
+ path, str_on_off(force));
}
static void _dpk_kip_pwr_clk_onoff(struct rtw89_dev *rtwdev, bool turn_on)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c
index 04e1ab13b753..959d62aefdd8 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8851bu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8851bu.c
@@ -5,15 +5,39 @@
#include <linux/module.h>
#include <linux/usb.h>
#include "rtw8851b.h"
+#include "reg.h"
#include "usb.h"
+static const struct rtw89_usb_info rtw8851b_usb_info = {
+ .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2,
+ .usb_wlan0_1 = R_AX_USB_WLAN0_1,
+ .hci_func_en = R_AX_HCI_FUNC_EN,
+ .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0,
+ .usb_endpoint_0 = R_AX_USB_ENDPOINT_0,
+ .usb_endpoint_2 = R_AX_USB_ENDPOINT_2,
+ .bulkout_id = {
+ [RTW89_DMA_ACH0] = 3,
+ [RTW89_DMA_ACH1] = 4,
+ [RTW89_DMA_ACH2] = 5,
+ [RTW89_DMA_ACH3] = 6,
+ [RTW89_DMA_B0MG] = 0,
+ [RTW89_DMA_B0HI] = 1,
+ [RTW89_DMA_H2C] = 2,
+ },
+};
+
static const struct rtw89_driver_info rtw89_8851bu_info = {
.chip = &rtw8851b_chip_info,
.variant = NULL,
.quirks = NULL,
+ .bus = {
+ .usb = &rtw8851b_usb_info,
+ }
};
static const struct usb_device_id rtw_8851bu_id_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb831, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8851bu_info },
{ USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xb851, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&rtw89_8851bu_info },
/* D-Link AX9U rev. A1 */
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
index 232f4c1bee1b..8677723e3561 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c
@@ -48,6 +48,48 @@ static const struct rtw89_hfc_param_ini rtw8852a_hfc_param_ini_pcie[] = {
[RTW89_QTA_INVALID] = {NULL},
};
+static const struct rtw89_hfc_ch_cfg rtw8852a_hfc_chcfg_usb[] = {
+ {22, 402, grp_0}, /* ACH 0 */
+ {0, 0, grp_0}, /* ACH 1 */
+ {22, 402, grp_0}, /* ACH 2 */
+ {0, 0, grp_0}, /* ACH 3 */
+ {22, 402, grp_0}, /* ACH 4 */
+ {0, 0, grp_0}, /* ACH 5 */
+ {22, 402, grp_0}, /* ACH 6 */
+ {0, 0, grp_0}, /* ACH 7 */
+ {22, 402, grp_0}, /* B0MGQ */
+ {0, 0, grp_0}, /* B0HIQ */
+ {22, 402, grp_0}, /* B1MGQ */
+ {0, 0, grp_0}, /* B1HIQ */
+ {0, 0, 0} /* FWCMDQ */
+};
+
+static const struct rtw89_hfc_pub_cfg rtw8852a_hfc_pubcfg_usb = {
+ 512, /* Group 0 */
+ 0, /* Group 1 */
+ 512, /* Public Max */
+ 104 /* WP threshold */
+};
+
+static const struct rtw89_hfc_prec_cfg rtw8852a_hfc_preccfg_usb = {
+ 11, /* CH 0-11 pre-cost */
+ 32, /* H2C pre-cost */
+ 76, /* WP CH 0-7 pre-cost */
+ 25, /* WP CH 8-11 pre-cost */
+ 1, /* CH 0-11 full condition */
+ 1, /* H2C full condition */
+ 1, /* WP CH 0-7 full condition */
+ 1, /* WP CH 8-11 full condition */
+};
+
+static const struct rtw89_hfc_param_ini rtw8852a_hfc_param_ini_usb[] = {
+ [RTW89_QTA_SCC] = {rtw8852a_hfc_chcfg_usb, &rtw8852a_hfc_pubcfg_usb,
+ &rtw8852a_hfc_preccfg_usb, RTW89_HCIFC_STF},
+ [RTW89_QTA_DLFW] = {NULL, NULL,
+ &rtw8852a_hfc_preccfg_usb, RTW89_HCIFC_STF},
+ [RTW89_QTA_INVALID] = {NULL},
+};
+
static const struct rtw89_dle_mem rtw8852a_dle_mem_pcie[] = {
[RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size0,
&rtw89_mac_size.ple_size0, &rtw89_mac_size.wde_qt0,
@@ -65,6 +107,19 @@ static const struct rtw89_dle_mem rtw8852a_dle_mem_pcie[] = {
NULL},
};
+static const struct rtw89_dle_mem rtw8852a_dle_mem_usb[] = {
+ [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size1,
+ &rtw89_mac_size.ple_size1, &rtw89_mac_size.wde_qt1,
+ &rtw89_mac_size.wde_qt1, &rtw89_mac_size.ple_qt25,
+ &rtw89_mac_size.ple_qt26},
+ [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size4,
+ &rtw89_mac_size.ple_size4, &rtw89_mac_size.wde_qt4,
+ &rtw89_mac_size.wde_qt4, &rtw89_mac_size.ple_qt13,
+ &rtw89_mac_size.ple_qt13},
+ [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL,
+ NULL},
+};
+
static const struct rtw89_reg2_def rtw8852a_pmac_ht20_mcs7_tbl[] = {
{0x44AC, 0x00000000},
{0x44B0, 0x00000000},
@@ -566,14 +621,6 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = {
.tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M,
};
-static void rtw8852ae_efuse_parsing(struct rtw89_efuse *efuse,
- struct rtw8852a_efuse *map)
-{
- ether_addr_copy(efuse->addr, map->e.mac_addr);
- efuse->rfe_type = map->rfe_type;
- efuse->xtal_cap = map->xtal_k;
-}
-
static void rtw8852a_efuse_parsing_tssi(struct rtw89_dev *rtwdev,
struct rtw8852a_efuse *map)
{
@@ -619,12 +666,18 @@ static int rtw8852a_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map,
switch (rtwdev->hci.type) {
case RTW89_HCI_TYPE_PCIE:
- rtw8852ae_efuse_parsing(efuse, map);
+ ether_addr_copy(efuse->addr, map->e.mac_addr);
+ break;
+ case RTW89_HCI_TYPE_USB:
+ ether_addr_copy(efuse->addr, map->u.mac_addr);
break;
default:
return -ENOTSUPP;
}
+ efuse->rfe_type = map->rfe_type;
+ efuse->xtal_cap = map->xtal_k;
+
rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type);
return 0;
@@ -2178,7 +2231,9 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = {
.query_rxdesc = rtw89_core_query_rxdesc,
.fill_txdesc = rtw89_core_fill_txdesc,
.fill_txdesc_fwcmd = rtw89_core_fill_txdesc,
- .get_ch_dma = rtw89_core_get_ch_dma,
+ .get_ch_dma = {rtw89_core_get_ch_dma,
+ rtw89_core_get_ch_dma_v2,
+ NULL,},
.cfg_ctrl_path = rtw89_mac_cfg_ctrl_path,
.mac_cfg_gnt = rtw89_mac_cfg_gnt,
.stop_sch_tx = rtw89_mac_stop_sch_tx,
@@ -2222,8 +2277,13 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
.max_amsdu_limit = 3500,
.dis_2g_40m_ul_ofdma = true,
.rsvd_ple_ofst = 0x6f800,
- .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie, NULL, NULL},
- .dle_mem = {rtw8852a_dle_mem_pcie, NULL, NULL, NULL},
+ .hfc_param_ini = {rtw8852a_hfc_param_ini_pcie,
+ rtw8852a_hfc_param_ini_usb,
+ NULL},
+ .dle_mem = {rtw8852a_dle_mem_pcie,
+ rtw8852a_dle_mem_usb,
+ rtw8852a_dle_mem_usb,
+ NULL},
.wde_qempty_acq_grpnum = 16,
.wde_qempty_mgq_grpsel = 16,
.rf_base_addr = {0xc000, 0xd000},
@@ -2274,6 +2334,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
.bacam_num = 2,
.bacam_dynamic_num = 4,
.bacam_ver = RTW89_BACAM_V0,
+ .addrcam_ver = 0,
.ppdu_max_usr = 4,
.sec_ctrl_efuse_size = 4,
.physical_efuse_size = 1216,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c
index 9db8713ac99b..463399413318 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852a_rfk.c
@@ -756,8 +756,8 @@ static void _iqk_rxk_setting(struct rtw89_dev *rtwdev, u8 path)
rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_FLTRST, 0x1);
rtw89_phy_write32_mask(rtwdev, R_ANAPAR_PW15, B_ANAPAR_PW15_H2, 0x0);
udelay(1);
- rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0303);
- rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0000);
+ rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0303);
+ rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0000);
switch (iqk_info->iqk_band[path]) {
case RTW89_BAND_2G:
@@ -1239,8 +1239,8 @@ static void _iqk_txk_setting(struct rtw89_dev *rtwdev, u8 path)
udelay(1);
rtw89_phy_write32_mask(rtwdev, R_ANAPAR, B_ANAPAR_15, 0x0041);
udelay(1);
- rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0303);
- rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RST, 0x0000);
+ rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0303);
+ rtw89_phy_write32_mask(rtwdev, R_ADC_FIFO, B_ADC_FIFO_RXK, 0x0000);
switch (iqk_info->iqk_band[path]) {
case RTW89_BAND_2G:
rtw89_write_rf(rtwdev, path, RR_XALNA2, RR_XALNA2_SW, 0x00);
@@ -1403,7 +1403,7 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev,
path, iqk_info->iqk_ch[path]);
rtw89_debug(rtwdev, RTW89_DBG_RFK,
"[IQK]S%d (PHY%d): / DBCC %s/ %s/ CH%d/ %s\n", path, phy,
- rtwdev->dbcc_en ? "on" : "off",
+ str_on_off(rtwdev->dbcc_en),
iqk_info->iqk_band[path] == 0 ? "2G" :
iqk_info->iqk_band[path] == 1 ? "5G" : "6G",
iqk_info->iqk_ch[path],
@@ -1881,8 +1881,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev,
rtw89_debug(rtwdev, RTW89_DBG_RFK,
"[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n",
path, dpk->cur_idx[path], phy,
- rtwdev->is_tssi_mode[path] ? "on" : "off",
- rtwdev->dbcc_en ? "on" : "off",
+ str_on_off(rtwdev->is_tssi_mode[path]),
+ str_on_off(rtwdev->dbcc_en),
dpk->bp[path][kidx].band == 0 ? "2G" :
dpk->bp[path][kidx].band == 1 ? "5G" : "6G",
dpk->bp[path][kidx].ch,
@@ -2736,7 +2736,7 @@ static void _dpk_onoff(struct rtw89_dev *rtwdev,
MASKBYTE3, 0x6 | val);
rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path,
- kidx, dpk->is_dpk_enable && !off ? "enable" : "disable");
+ kidx, str_enable_disable(dpk->is_dpk_enable && !off));
}
static void _dpk_track(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852au.c b/drivers/net/wireless/realtek/rtw89/rtw8852au.c
new file mode 100644
index 000000000000..ca782469c455
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852au.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "rtw8852a.h"
+#include "reg.h"
+#include "usb.h"
+
+static const struct rtw89_usb_info rtw8852a_usb_info = {
+ .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2,
+ .usb_wlan0_1 = R_AX_USB_WLAN0_1,
+ .hci_func_en = R_AX_HCI_FUNC_EN,
+ .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0,
+ .usb_endpoint_0 = R_AX_USB_ENDPOINT_0,
+ .usb_endpoint_2 = R_AX_USB_ENDPOINT_2,
+ .bulkout_id = {
+ [RTW89_DMA_ACH0] = 3,
+ [RTW89_DMA_ACH2] = 5,
+ [RTW89_DMA_ACH4] = 4,
+ [RTW89_DMA_ACH6] = 6,
+ [RTW89_DMA_B0MG] = 0,
+ [RTW89_DMA_B0HI] = 0,
+ [RTW89_DMA_B1MG] = 1,
+ [RTW89_DMA_B1HI] = 1,
+ [RTW89_DMA_H2C] = 2,
+ },
+};
+
+static const struct rtw89_driver_info rtw89_8852au_info = {
+ .chip = &rtw8852a_chip_info,
+ .variant = NULL,
+ .quirks = NULL,
+ .bus = {
+ .usb = &rtw8852a_usb_info,
+ }
+};
+
+static const struct usb_device_id rtw_8852au_id_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x0312, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x4020, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1997, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x8832, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x885a, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0x885c, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3321, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x332c, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x013f, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0140, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0141, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x3625, 0x010f, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852au_info },
+ {},
+};
+MODULE_DEVICE_TABLE(usb, rtw_8852au_id_table);
+
+static struct usb_driver rtw_8852au_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtw_8852au_id_table,
+ .probe = rtw89_usb_probe,
+ .disconnect = rtw89_usb_disconnect,
+};
+module_usb_driver(rtw_8852au_driver);
+
+MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>");
+MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852AU driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
index 0777e336aaa1..70fb05bc5e98 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c
@@ -842,7 +842,9 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = {
.query_rxdesc = rtw89_core_query_rxdesc,
.fill_txdesc = rtw89_core_fill_txdesc,
.fill_txdesc_fwcmd = rtw89_core_fill_txdesc,
- .get_ch_dma = rtw89_core_get_ch_dma,
+ .get_ch_dma = {rtw89_core_get_ch_dma,
+ rtw89_core_get_ch_dma,
+ NULL,},
.cfg_ctrl_path = rtw89_mac_cfg_ctrl_path,
.mac_cfg_gnt = rtw89_mac_cfg_gnt,
.stop_sch_tx = rtw89_mac_stop_sch_tx,
@@ -957,6 +959,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
.bacam_num = 2,
.bacam_dynamic_num = 4,
.bacam_ver = RTW89_BACAM_V0,
+ .addrcam_ver = 0,
.ppdu_max_usr = 4,
.sec_ctrl_efuse_size = 4,
.physical_efuse_size = 1216,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
index 3fb2972ae6f6..4e72f4961837 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c
@@ -1747,11 +1747,15 @@ static void __rtw8852bx_bb_cfg_txrx_path(struct rtw89_dev *rtwdev)
struct rtw89_hal *hal = &rtwdev->hal;
const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
enum rtw89_rf_path_bit rx_path = hal->antenna_rx ? hal->antenna_rx : RF_AB;
+ u8 rx_nss = rtwdev->hal.rx_nss;
+
+ if (rx_path != RF_AB)
+ rx_nss = 1;
rtw8852bx_bb_ctrl_rx_path(rtwdev, rx_path, chan);
rtw8852bx_bb_ctrl_rf_mode_rx_path(rtwdev, rx_path);
- if (rtwdev->hal.rx_nss == 1) {
+ if (rx_nss == 1) {
rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 0);
rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 0);
rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 0);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
index 4796588c0256..70b1515c00fa 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c
@@ -1696,7 +1696,7 @@ static void _dpk_onoff(struct rtw89_dev *rtwdev, enum rtw89_rf_path path, bool o
MASKBYTE3, _dpk_order_convert(rtwdev) << 1 | val);
rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path,
- kidx, dpk->is_dpk_enable && !off ? "enable" : "disable");
+ kidx, str_enable_disable(dpk->is_dpk_enable && !off));
}
static void _dpk_one_shot(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
@@ -1763,8 +1763,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
rtw89_debug(rtwdev, RTW89_DBG_RFK,
"[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n",
path, dpk->cur_idx[path], phy,
- rtwdev->is_tssi_mode[path] ? "on" : "off",
- rtwdev->dbcc_en ? "on" : "off",
+ str_on_off(rtwdev->is_tssi_mode[path]),
+ str_on_off(rtwdev->dbcc_en),
dpk->bp[path][kidx].band == 0 ? "2G" :
dpk->bp[path][kidx].band == 1 ? "5G" : "6G",
dpk->bp[path][kidx].ch,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
index b3a79ebc7e75..f956474c3b72 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c
@@ -708,7 +708,9 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = {
.query_rxdesc = rtw89_core_query_rxdesc,
.fill_txdesc = rtw89_core_fill_txdesc,
.fill_txdesc_fwcmd = rtw89_core_fill_txdesc,
- .get_ch_dma = rtw89_core_get_ch_dma,
+ .get_ch_dma = {rtw89_core_get_ch_dma,
+ NULL,
+ NULL,},
.cfg_ctrl_path = rtw89_mac_cfg_ctrl_path,
.mac_cfg_gnt = rtw89_mac_cfg_gnt,
.stop_sch_tx = rtw89_mac_stop_sch_tx,
@@ -816,6 +818,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
.bacam_num = 2,
.bacam_dynamic_num = 4,
.bacam_ver = RTW89_BACAM_V0,
+ .addrcam_ver = 0,
.ppdu_max_usr = 4,
.sec_ctrl_efuse_size = 4,
.physical_efuse_size = 1216,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c
index 0694272f7ffa..980d17ef68d0 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852bu.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852bu.c
@@ -5,12 +5,34 @@
#include <linux/module.h>
#include <linux/usb.h>
#include "rtw8852b.h"
+#include "reg.h"
#include "usb.h"
+static const struct rtw89_usb_info rtw8852b_usb_info = {
+ .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2,
+ .usb_wlan0_1 = R_AX_USB_WLAN0_1,
+ .hci_func_en = R_AX_HCI_FUNC_EN,
+ .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0,
+ .usb_endpoint_0 = R_AX_USB_ENDPOINT_0,
+ .usb_endpoint_2 = R_AX_USB_ENDPOINT_2,
+ .bulkout_id = {
+ [RTW89_DMA_ACH0] = 3,
+ [RTW89_DMA_ACH1] = 4,
+ [RTW89_DMA_ACH2] = 5,
+ [RTW89_DMA_ACH3] = 6,
+ [RTW89_DMA_B0MG] = 0,
+ [RTW89_DMA_B0HI] = 1,
+ [RTW89_DMA_H2C] = 2,
+ },
+};
+
static const struct rtw89_driver_info rtw89_8852bu_info = {
.chip = &rtw8852b_chip_info,
.variant = NULL,
.quirks = NULL,
+ .bus = {
+ .usb = &rtw8852b_usb_info,
+ }
};
static const struct usb_device_id rtw_8852bu_id_table[] = {
@@ -28,6 +50,8 @@ static const struct usb_device_id rtw_8852bu_id_table[] = {
.driver_info = (kernel_ulong_t)&rtw89_8852bu_info },
{ USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1a62, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&rtw89_8852bu_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1cb6, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852bu_info },
{ USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x6931, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&rtw89_8852bu_info },
{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3327, 0xff, 0xff, 0xff),
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index 440801d63343..db99450e9158 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -51,6 +51,48 @@ static const struct rtw89_hfc_param_ini rtw8852c_hfc_param_ini_pcie[] = {
[RTW89_QTA_INVALID] = {NULL},
};
+static const struct rtw89_hfc_ch_cfg rtw8852c_hfc_chcfg_usb[] = {
+ {18, 344, grp_0}, /* ACH 0 */
+ {0, 0, grp_0}, /* ACH 1 */
+ {18, 344, grp_0}, /* ACH 2 */
+ {0, 0, grp_0}, /* ACH 3 */
+ {18, 344, grp_0}, /* ACH 4 */
+ {0, 0, grp_0}, /* ACH 5 */
+ {18, 344, grp_0}, /* ACH 6 */
+ {0, 0, grp_0}, /* ACH 7 */
+ {18, 344, grp_0}, /* B0MGQ */
+ {0, 0, grp_0}, /* B0HIQ */
+ {18, 344, grp_0}, /* B1MGQ */
+ {0, 0, grp_0}, /* B1HIQ */
+ {0, 0, 0} /* FWCMDQ */
+};
+
+static const struct rtw89_hfc_pub_cfg rtw8852c_hfc_pubcfg_usb = {
+ 344, /* Group 0 */
+ 0, /* Group 1 */
+ 344, /* Public Max */
+ 0 /* WP threshold */
+};
+
+static const struct rtw89_hfc_prec_cfg rtw8852c_hfc_preccfg_usb = {
+ 9, /* CH 0-11 pre-cost */
+ 32, /* H2C pre-cost */
+ 146, /* WP CH 0-7 pre-cost */
+ 146, /* WP CH 8-11 pre-cost */
+ 1, /* CH 0-11 full condition */
+ 1, /* H2C full condition */
+ 1, /* WP CH 0-7 full condition */
+ 1, /* WP CH 8-11 full condition */
+};
+
+static const struct rtw89_hfc_param_ini rtw8852c_hfc_param_ini_usb[] = {
+ [RTW89_QTA_SCC] = {rtw8852c_hfc_chcfg_usb, &rtw8852c_hfc_pubcfg_usb,
+ &rtw8852c_hfc_preccfg_usb, RTW89_HCIFC_STF},
+ [RTW89_QTA_DLFW] = {NULL, NULL,
+ &rtw8852c_hfc_preccfg_usb, RTW89_HCIFC_STF},
+ [RTW89_QTA_INVALID] = {NULL},
+};
+
static const struct rtw89_dle_mem rtw8852c_dle_mem_pcie[] = {
[RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size19,
&rtw89_mac_size.ple_size19, &rtw89_mac_size.wde_qt18,
@@ -64,6 +106,32 @@ static const struct rtw89_dle_mem rtw8852c_dle_mem_pcie[] = {
NULL},
};
+static const struct rtw89_dle_mem rtw8852c_dle_mem_usb2[] = {
+ [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size31,
+ &rtw89_mac_size.ple_size34, &rtw89_mac_size.wde_qt31,
+ &rtw89_mac_size.wde_qt31, &rtw89_mac_size.ple_qt78,
+ &rtw89_mac_size.ple_qt79},
+ [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18,
+ &rtw89_mac_size.ple_size18, &rtw89_mac_size.wde_qt17,
+ &rtw89_mac_size.wde_qt17, &rtw89_mac_size.ple_qt44,
+ &rtw89_mac_size.ple_qt45},
+ [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL,
+ NULL},
+};
+
+static const struct rtw89_dle_mem rtw8852c_dle_mem_usb3[] = {
+ [RTW89_QTA_SCC] = {RTW89_QTA_SCC, &rtw89_mac_size.wde_size17,
+ &rtw89_mac_size.ple_size17, &rtw89_mac_size.wde_qt16,
+ &rtw89_mac_size.wde_qt16, &rtw89_mac_size.ple_qt42,
+ &rtw89_mac_size.ple_qt43},
+ [RTW89_QTA_DLFW] = {RTW89_QTA_DLFW, &rtw89_mac_size.wde_size18,
+ &rtw89_mac_size.ple_size18, &rtw89_mac_size.wde_qt17,
+ &rtw89_mac_size.wde_qt17, &rtw89_mac_size.ple_qt44,
+ &rtw89_mac_size.ple_qt45},
+ [RTW89_QTA_INVALID] = {RTW89_QTA_INVALID, NULL, NULL, NULL, NULL, NULL,
+ NULL},
+};
+
static const u32 rtw8852c_h2c_regs[RTW89_H2CREG_MAX] = {
R_AX_H2CREG_DATA0_V1, R_AX_H2CREG_DATA1_V1, R_AX_H2CREG_DATA2_V1,
R_AX_H2CREG_DATA3_V1
@@ -214,7 +282,8 @@ static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev)
int ret;
val32 = rtw89_read32_mask(rtwdev, R_AX_SYS_STATUS1, B_AX_PAD_HCI_SEL_V2_MASK);
- if (val32 == MAC_AX_HCI_SEL_PCIE_USB)
+ if (val32 == MAC_AX_HCI_SEL_PCIE_USB ||
+ rtwdev->hci.type == RTW89_HCI_TYPE_USB)
rtw89_write32_set(rtwdev, R_AX_LDO_AON_CTRL0, B_AX_PD_REGU_L);
rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_AFSM_WLSUS_EN |
@@ -246,7 +315,9 @@ static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev)
rtw89_write8_clr(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN);
rtw89_write8_set(rtwdev, R_AX_PLATFORM_ENABLE, B_AX_PLATFORM_EN);
- rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1);
+
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE)
+ rtw89_write32_clr(rtwdev, R_AX_SYS_SDIO_CTRL, B_AX_PCIE_CALIB_EN_V1);
rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND, B_AX_CMAC1_FEN);
rtw89_write32_set(rtwdev, R_AX_SYS_ISO_CTRL_EXTEND, B_AX_R_SYM_ISO_CMAC12PP);
@@ -305,9 +376,11 @@ static int rtw8852c_pwr_on_func(struct rtw89_dev *rtwdev)
rtw89_write32_clr(rtwdev, R_AX_SYS_ISO_CTRL, B_AX_PWC_EV2EF_B14);
rtw89_write32_clr(rtwdev, R_AX_PMC_DBG_CTRL2, B_AX_SYSON_DIS_PMCR_AX_WRMSK);
- rtw89_write32_set(rtwdev, R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN,
- B_AX_EECS_PULL_LOW_EN | B_AX_EESK_PULL_LOW_EN |
- B_AX_LED1_PULL_LOW_EN);
+
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE)
+ rtw89_write32_set(rtwdev, R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN,
+ B_AX_EECS_PULL_LOW_EN | B_AX_EESK_PULL_LOW_EN |
+ B_AX_LED1_PULL_LOW_EN);
rtw89_write32_set(rtwdev, R_AX_DMAC_FUNC_EN,
B_AX_MAC_FUNC_EN | B_AX_DMAC_FUNC_EN | B_AX_MPDU_PROC_EN |
@@ -385,22 +458,26 @@ static int rtw8852c_pwr_off_func(struct rtw89_dev *rtwdev)
if (ret)
return ret;
- rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION);
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE)
+ rtw89_write32(rtwdev, R_AX_WLLPS_CTRL, SW_LPS_OPTION);
+ else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB)
+ rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_SOP_EDSWR);
+
rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_XTAL_OFF_A_DIE);
rtw89_write32_set(rtwdev, R_AX_SYS_SWR_CTRL1, B_AX_SYM_CTRL_SPS_PWMFREQ);
rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0,
B_AX_REG_ZCDC_H_MASK, 0x3);
- rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS);
- return 0;
-}
+ if (rtwdev->hci.type == RTW89_HCI_TYPE_PCIE) {
+ rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_APFM_SWLPS);
+ } else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) {
+ val32 = rtw89_read32(rtwdev, R_AX_SYS_PW_CTRL);
+ val32 &= ~B_AX_AFSM_PCIE_SUS_EN;
+ val32 |= B_AX_AFSM_WLSUS_EN;
+ rtw89_write32(rtwdev, R_AX_SYS_PW_CTRL, val32);
+ }
-static void rtw8852c_e_efuse_parsing(struct rtw89_efuse *efuse,
- struct rtw8852c_efuse *map)
-{
- ether_addr_copy(efuse->addr, map->e.mac_addr);
- efuse->rfe_type = map->rfe_type;
- efuse->xtal_cap = map->xtal_k;
+ return 0;
}
static void rtw8852c_efuse_parsing_tssi(struct rtw89_dev *rtwdev,
@@ -511,12 +588,18 @@ static int rtw8852c_read_efuse(struct rtw89_dev *rtwdev, u8 *log_map,
switch (rtwdev->hci.type) {
case RTW89_HCI_TYPE_PCIE:
- rtw8852c_e_efuse_parsing(efuse, map);
+ ether_addr_copy(efuse->addr, map->e.mac_addr);
+ break;
+ case RTW89_HCI_TYPE_USB:
+ ether_addr_copy(efuse->addr, map->u.mac_addr);
break;
default:
return -ENOTSUPP;
}
+ efuse->rfe_type = map->rfe_type;
+ efuse->xtal_cap = map->xtal_k;
+
rtw89_info(rtwdev, "chip rfe_type is %d\n", efuse->rfe_type);
return 0;
@@ -587,12 +670,16 @@ static void rtw8852c_phycap_parsing_thermal_trim(struct rtw89_dev *rtwdev,
}
}
+#define __THM_MASK_SIGN BIT(0)
+#define __THM_MASK_3BITS GENMASK(3, 1)
+#define __THM_MASK_VAL8 BIT(4)
+
static void rtw8852c_thermal_trim(struct rtw89_dev *rtwdev)
{
-#define __thm_setting(raw) \
-({ \
- u8 __v = (raw); \
- ((__v & 0x1) << 3) | ((__v & 0x1f) >> 1); \
+#define __thm_setting(raw) \
+({ \
+ u8 __v = (raw); \
+ ((__v & __THM_MASK_SIGN) << 3) | ((__v & __THM_MASK_3BITS) >> 1); \
})
struct rtw89_power_trim_info *info = &rtwdev->pwr_trim;
u8 i, val;
@@ -2415,10 +2502,20 @@ static void rtw8852c_ctrl_nbtg_bt_tx(struct rtw89_dev *rtwdev, bool en,
static void rtw8852c_bb_cfg_txrx_path(struct rtw89_dev *rtwdev)
{
struct rtw89_hal *hal = &rtwdev->hal;
+ u8 nrx_path = RF_PATH_AB;
+ u8 rx_nss = hal->rx_nss;
+
+ if (hal->antenna_rx == RF_A)
+ nrx_path = RF_PATH_A;
+ else if (hal->antenna_rx == RF_B)
+ nrx_path = RF_PATH_B;
+
+ if (nrx_path != RF_PATH_AB)
+ rx_nss = 1;
- rtw8852c_bb_cfg_rx_path(rtwdev, RF_PATH_AB);
+ rtw8852c_bb_cfg_rx_path(rtwdev, nrx_path);
- if (hal->rx_nss == 1) {
+ if (rx_nss == 1) {
rtw89_phy_write32_mask(rtwdev, R_RXHT_MCS_LIMIT, B_RXHT_MCS_LIMIT, 0);
rtw89_phy_write32_mask(rtwdev, R_RXVHT_MCS_LIMIT, B_RXVHT_MCS_LIMIT, 0);
rtw89_phy_write32_mask(rtwdev, R_RXHE, B_RXHE_MAX_NSS, 0);
@@ -2433,13 +2530,26 @@ static void rtw8852c_bb_cfg_txrx_path(struct rtw89_dev *rtwdev)
static u8 rtw8852c_get_thermal(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path)
{
+ struct rtw89_power_trim_info *info = &rtwdev->pwr_trim;
+ s8 comp = 0;
+ u8 val;
+
rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1);
rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x0);
rtw89_write_rf(rtwdev, rf_path, RR_TM, RR_TM_TRI, 0x1);
fsleep(200);
- return rtw89_read_rf(rtwdev, rf_path, RR_TM, RR_TM_VAL);
+ val = rtw89_read_rf(rtwdev, rf_path, RR_TM, RR_TM_VAL);
+
+ if (info->pg_thermal_trim) {
+ u8 trim = info->thermal_trim[rf_path];
+
+ if (trim & __THM_MASK_VAL8)
+ comp = 8 * (trim & __THM_MASK_SIGN ? -1 : 1);
+ }
+
+ return val + comp;
}
static void rtw8852c_btc_set_rfe(struct rtw89_dev *rtwdev)
@@ -2962,7 +3072,9 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = {
.query_rxdesc = rtw89_core_query_rxdesc,
.fill_txdesc = rtw89_core_fill_txdesc_v1,
.fill_txdesc_fwcmd = rtw89_core_fill_txdesc_fwcmd_v1,
- .get_ch_dma = rtw89_core_get_ch_dma,
+ .get_ch_dma = {rtw89_core_get_ch_dma,
+ rtw89_core_get_ch_dma_v2,
+ NULL,},
.cfg_ctrl_path = rtw89_mac_cfg_ctrl_path_v1,
.mac_cfg_gnt = rtw89_mac_cfg_gnt_v1,
.stop_sch_tx = rtw89_mac_stop_sch_tx_v1,
@@ -3006,8 +3118,13 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
.max_amsdu_limit = 8000,
.dis_2g_40m_ul_ofdma = false,
.rsvd_ple_ofst = 0x6f800,
- .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie, NULL, NULL},
- .dle_mem = {rtw8852c_dle_mem_pcie, NULL, NULL, NULL},
+ .hfc_param_ini = {rtw8852c_hfc_param_ini_pcie,
+ rtw8852c_hfc_param_ini_usb,
+ NULL},
+ .dle_mem = {rtw8852c_dle_mem_pcie,
+ rtw8852c_dle_mem_usb2,
+ rtw8852c_dle_mem_usb3,
+ NULL},
.wde_qempty_acq_grpnum = 16,
.wde_qempty_mgq_grpsel = 16,
.rf_base_addr = {0xe000, 0xf000},
@@ -3061,6 +3178,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
.bacam_num = 8,
.bacam_dynamic_num = 8,
.bacam_ver = RTW89_BACAM_V0_EXT,
+ .addrcam_ver = 0,
.ppdu_max_usr = 8,
.sec_ctrl_efuse_size = 4,
.physical_efuse_size = 1216,
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.h b/drivers/net/wireless/realtek/rtw89/rtw8852c.h
index 77b05daedd10..8585921ac6c4 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.h
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.h
@@ -11,7 +11,7 @@
#define BB_PATH_NUM_8852C 2
struct rtw8852c_u_efuse {
- u8 rsvd[0x38];
+ u8 rsvd[0x88];
u8 mac_addr[ETH_ALEN];
};
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c
index b92e2ce4f4ad..cbee484dee30 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c_rfk.c
@@ -1344,7 +1344,7 @@ static void _iqk_get_ch_info(struct rtw89_dev *rtwdev,
path, iqk_info->iqk_ch[path]);
rtw89_debug(rtwdev, RTW89_DBG_RFK,
"[IQK]S%d (PHY%d): / DBCC %s/ %s/ CH%d/ %s\n", path, phy,
- rtwdev->dbcc_en ? "on" : "off",
+ str_on_off(rtwdev->dbcc_en),
iqk_info->iqk_band[path] == 0 ? "2G" :
iqk_info->iqk_band[path] == 1 ? "5G" : "6G",
iqk_info->iqk_ch[path],
@@ -1920,8 +1920,8 @@ static void _dpk_information(struct rtw89_dev *rtwdev,
rtw89_debug(rtwdev, RTW89_DBG_RFK,
"[DPK] S%d[%d] (PHY%d): TSSI %s/ DBCC %s/ %s/ CH%d/ %s\n",
path, dpk->cur_idx[path], phy,
- rtwdev->is_tssi_mode[path] ? "on" : "off",
- rtwdev->dbcc_en ? "on" : "off",
+ str_on_off(rtwdev->is_tssi_mode[path]),
+ str_on_off(rtwdev->dbcc_en),
dpk->bp[path][kidx].band == 0 ? "2G" :
dpk->bp[path][kidx].band == 1 ? "5G" : "6G",
dpk->bp[path][kidx].ch,
@@ -2000,7 +2000,7 @@ static void _dpk_txpwr_bb_force(struct rtw89_dev *rtwdev, u8 path, bool force)
rtw89_phy_write32_mask(rtwdev, R_TXPWRB_H + (path << 13), B_TXPWRB_RDY, force);
rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d txpwr_bb_force %s\n",
- path, force ? "on" : "off");
+ path, str_on_off(force));
}
static void _dpk_kip_restore(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
@@ -2828,7 +2828,7 @@ static void _dpk_onoff(struct rtw89_dev *rtwdev,
B_DPD_MEN, val);
rtw89_debug(rtwdev, RTW89_DBG_RFK, "[DPK] S%d[%d] DPK %s !!!\n", path,
- kidx, dpk->is_dpk_enable && !off ? "enable" : "disable");
+ kidx, str_enable_disable(dpk->is_dpk_enable && !off));
}
static void _dpk_track(struct rtw89_dev *rtwdev)
@@ -3987,37 +3987,56 @@ static void _ctrl_ch(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
}
}
+static void _set_rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_rf_path path,
+ enum rtw89_bandwidth bw)
+{
+ u32 val;
+
+ rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x1);
+ rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M2, 0xa);
+
+ switch (bw) {
+ case RTW89_CHANNEL_WIDTH_20:
+ val = 0x1b;
+ break;
+ case RTW89_CHANNEL_WIDTH_40:
+ val = 0x13;
+ break;
+ case RTW89_CHANNEL_WIDTH_80:
+ val = 0xb;
+ break;
+ case RTW89_CHANNEL_WIDTH_160:
+ default:
+ val = 0x3;
+ break;
+ }
+
+ rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, val);
+ rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x0);
+}
+
+static void _set_tia_bw(struct rtw89_dev *rtwdev, enum rtw89_rf_path path,
+ enum rtw89_bandwidth bw)
+{
+ if (bw == RTW89_CHANNEL_WIDTH_160)
+ rtw89_write_rf(rtwdev, path, RR_RXBB2, RR_RXBB2_EBW, 0x0);
+ else
+ rtw89_write_rf(rtwdev, path, RR_RXBB2, RR_RXBB2_EBW, 0x2);
+}
+
static void _rxbb_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy,
enum rtw89_bandwidth bw)
{
u8 kpath;
u8 path;
- u32 val;
kpath = _kpath(rtwdev, phy);
for (path = 0; path < 2; path++) {
if (!(kpath & BIT(path)))
continue;
- rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x1);
- rtw89_write_rf(rtwdev, path, RR_LUTWA, RR_LUTWA_M2, 0xa);
- switch (bw) {
- case RTW89_CHANNEL_WIDTH_20:
- val = 0x1b;
- break;
- case RTW89_CHANNEL_WIDTH_40:
- val = 0x13;
- break;
- case RTW89_CHANNEL_WIDTH_80:
- val = 0xb;
- break;
- case RTW89_CHANNEL_WIDTH_160:
- default:
- val = 0x3;
- break;
- }
- rtw89_write_rf(rtwdev, path, RR_LUTWD0, RR_LUTWD0_LB, val);
- rtw89_write_rf(rtwdev, path, RR_LUTWE2, RR_LUTWE2_RTXBW, 0x0);
+ _set_rxbb_bw(rtwdev, path, bw);
+ _set_tia_bw(rtwdev, path, bw);
}
}
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852cu.c b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
new file mode 100644
index 000000000000..2708b523ca14
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852cu.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2025 Realtek Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "rtw8852c.h"
+#include "reg.h"
+#include "usb.h"
+
+static const struct rtw89_usb_info rtw8852c_usb_info = {
+ .usb_host_request_2 = R_AX_USB_HOST_REQUEST_2_V1,
+ .usb_wlan0_1 = R_AX_USB_WLAN0_1_V1,
+ .hci_func_en = R_AX_HCI_FUNC_EN_V1,
+ .usb3_mac_npi_config_intf_0 = R_AX_USB3_MAC_NPI_CONFIG_INTF_0_V1,
+ .usb_endpoint_0 = R_AX_USB_ENDPOINT_0_V1,
+ .usb_endpoint_2 = R_AX_USB_ENDPOINT_2_V1,
+ .bulkout_id = {
+ [RTW89_DMA_ACH0] = 3,
+ [RTW89_DMA_ACH2] = 5,
+ [RTW89_DMA_ACH4] = 4,
+ [RTW89_DMA_ACH6] = 6,
+ [RTW89_DMA_B0MG] = 0,
+ [RTW89_DMA_B0HI] = 0,
+ [RTW89_DMA_B1MG] = 1,
+ [RTW89_DMA_B1HI] = 1,
+ [RTW89_DMA_H2C] = 2,
+ },
+};
+
+static const struct rtw89_driver_info rtw89_8852cu_info = {
+ .chip = &rtw8852c_chip_info,
+ .variant = NULL,
+ .quirks = NULL,
+ .bus = {
+ .usb = &rtw8852c_usb_info,
+ },
+};
+
+static const struct usb_device_id rtw_8852cu_id_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc832, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85a, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0bda, 0xc85d, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0db0, 0x991d, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x35b2, 0x0502, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0101, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0102, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&rtw89_8852cu_info },
+ {},
+};
+MODULE_DEVICE_TABLE(usb, rtw_8852cu_id_table);
+
+static struct usb_driver rtw_8852cu_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = rtw_8852cu_id_table,
+ .probe = rtw89_usb_probe,
+ .disconnect = rtw89_usb_disconnect,
+};
+module_usb_driver(rtw_8852cu_driver);
+
+MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>");
+MODULE_DESCRIPTION("Realtek 802.11ax wireless 8852CU driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index 6aa19ad259ac..4437279c554b 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -2347,19 +2347,29 @@ static void rtw8922a_bb_cfg_txrx_path(struct rtw89_dev *rtwdev)
enum rtw89_band band = chan->band_type;
struct rtw89_hal *hal = &rtwdev->hal;
u8 ntx_path = RF_PATH_AB;
+ u8 nrx_path = RF_PATH_AB;
u32 tx_en0, tx_en1;
+ u8 rx_nss = 2;
if (hal->antenna_tx == RF_A)
ntx_path = RF_PATH_A;
else if (hal->antenna_tx == RF_B)
ntx_path = RF_PATH_B;
+ if (hal->antenna_rx == RF_A)
+ nrx_path = RF_PATH_A;
+ else if (hal->antenna_rx == RF_B)
+ nrx_path = RF_PATH_B;
+
+ if (nrx_path != RF_PATH_AB)
+ rx_nss = 1;
+
rtw8922a_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, true);
if (rtwdev->dbcc_en)
rtw8922a_hal_reset(rtwdev, RTW89_PHY_1, RTW89_MAC_1, band,
&tx_en1, true);
- rtw8922a_ctrl_trx_path(rtwdev, ntx_path, 2, RF_PATH_AB, 2);
+ rtw8922a_ctrl_trx_path(rtwdev, ntx_path, 2, nrx_path, rx_nss);
rtw8922a_hal_reset(rtwdev, RTW89_PHY_0, RTW89_MAC_0, band, &tx_en0, false);
if (rtwdev->dbcc_en)
@@ -2821,7 +2831,9 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = {
.query_rxdesc = rtw89_core_query_rxdesc_v2,
.fill_txdesc = rtw89_core_fill_txdesc_v2,
.fill_txdesc_fwcmd = rtw89_core_fill_txdesc_fwcmd_v2,
- .get_ch_dma = rtw89_core_get_ch_dma,
+ .get_ch_dma = {rtw89_core_get_ch_dma,
+ rtw89_core_get_ch_dma_v2,
+ NULL,},
.cfg_ctrl_path = rtw89_mac_cfg_ctrl_path_v2,
.mac_cfg_gnt = rtw89_mac_cfg_gnt_v2,
.stop_sch_tx = rtw89_mac_stop_sch_tx_v2,
@@ -2919,6 +2931,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
.bacam_num = 24,
.bacam_dynamic_num = 8,
.bacam_ver = RTW89_BACAM_V1,
+ .addrcam_ver = 0,
.ppdu_max_usr = 16,
.sec_ctrl_efuse_size = 4,
.physical_efuse_size = 0x1300,
diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h
index 984c9fdbb018..fa324b4a1dde 100644
--- a/drivers/net/wireless/realtek/rtw89/txrx.h
+++ b/drivers/net/wireless/realtek/rtw89/txrx.h
@@ -127,6 +127,8 @@ static inline u8 rtw89_get_data_nss(struct rtw89_dev *rtwdev, u16 hw_rate)
#define RTW89_TXWD_INFO0_MULTIPORT_ID GENMASK(6, 4)
/* TX WD INFO DWORD 1 */
+#define RTW89_TXWD_INFO1_DATA_TXCNT_LMT_SEL BIT(31)
+#define RTW89_TXWD_INFO1_DATA_TXCNT_LMT GENMASK(30, 25)
#define RTW89_TXWD_INFO1_DATA_RTY_LOWEST_RATE GENMASK(24, 16)
#define RTW89_TXWD_INFO1_A_CTRL_BSR BIT(14)
#define RTW89_TXWD_INFO1_MAX_AGGNUM GENMASK(7, 0)
@@ -139,10 +141,12 @@ static inline u8 rtw89_get_data_nss(struct rtw89_dev *rtwdev, u16 hw_rate)
#define RTW89_TXWD_INFO2_SEC_CAM_IDX GENMASK(7, 0)
/* TX WD INFO DWORD 3 */
+#define RTW89_TXWD_INFO3_SPE_RPT BIT(10)
/* TX WD INFO DWORD 4 */
-#define RTW89_TXWD_INFO4_RTS_EN BIT(27)
#define RTW89_TXWD_INFO4_HW_RTS_EN BIT(31)
+#define RTW89_TXWD_INFO4_RTS_EN BIT(27)
+#define RTW89_TXWD_INFO4_SW_DEFINE GENMASK(3, 0)
/* TX WD INFO DWORD 5 */
@@ -417,6 +421,7 @@ struct rtw89_rxinfo_user {
#define RTW89_RXINFO_USER_MGMT BIT(3)
#define RTW89_RXINFO_USER_BCN BIT(4)
#define RTW89_RXINFO_USER_MACID GENMASK(15, 8)
+#define RTW89_RXINFO_USER_MACID_V1 GENMASK(31, 20)
struct rtw89_rxinfo {
__le32 w0;
diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c
index 6cf89aee252e..d7d968207a39 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.c
+++ b/drivers/net/wireless/realtek/rtw89/usb.c
@@ -55,7 +55,7 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr,
else if (ret < 0)
rtw89_warn(rtwdev,
"usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n",
- reqtype == RTW89_USB_VENQT_READ ? "read" : "write",
+ str_read_write(reqtype == RTW89_USB_VENQT_READ),
len * 8, addr, ret,
le32_to_cpup(rtwusb->vendor_req_buf),
attempt);
@@ -167,27 +167,6 @@ rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev,
return 42; /* TODO some kind of calculation? */
}
-static u8 rtw89_usb_get_bulkout_id(u8 ch_dma)
-{
- switch (ch_dma) {
- case RTW89_DMA_ACH0:
- return 3;
- case RTW89_DMA_ACH1:
- return 4;
- case RTW89_DMA_ACH2:
- return 5;
- case RTW89_DMA_ACH3:
- return 6;
- default:
- case RTW89_DMA_B0MG:
- return 0;
- case RTW89_DMA_B0HI:
- return 1;
- case RTW89_DMA_H2C:
- return 2;
- }
-}
-
static void rtw89_usb_write_port_complete(struct urb *urb)
{
struct rtw89_usb_tx_ctrl_block *txcb = urb->context;
@@ -215,6 +194,15 @@ static void rtw89_usb_write_port_complete(struct urb *urb)
skb_pull(skb, txdesc_size);
+ if (rtw89_is_tx_rpt_skb(rtwdev, skb)) {
+ if (urb->status == 0)
+ rtw89_tx_rpt_skb_add(rtwdev, skb);
+ else
+ rtw89_tx_rpt_tx_status(rtwdev, skb,
+ RTW89_TX_MACID_DROP);
+ continue;
+ }
+
info = IEEE80211_SKB_CB(skb);
ieee80211_tx_info_clear_status(info);
@@ -242,21 +230,21 @@ static void rtw89_usb_write_port_complete(struct urb *urb)
}
kfree(txcb);
- usb_free_urb(urb);
}
static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma,
void *data, int len, void *context)
{
struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+ const struct rtw89_usb_info *info = rtwusb->info;
struct usb_device *usbd = rtwusb->udev;
struct urb *urb;
- u8 bulkout_id = rtw89_usb_get_bulkout_id(ch_dma);
+ u8 bulkout_id = info->bulkout_id[ch_dma];
unsigned int pipe;
int ret;
if (test_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags))
- return 0;
+ return -ENODEV;
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
@@ -267,10 +255,17 @@ static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma,
usb_fill_bulk_urb(urb, usbd, pipe, data, len,
rtw89_usb_write_port_complete, context);
urb->transfer_flags |= URB_ZERO_PACKET;
- ret = usb_submit_urb(urb, GFP_ATOMIC);
+ usb_anchor_urb(urb, &rtwusb->tx_submitted);
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret)
- usb_free_urb(urb);
+ usb_unanchor_urb(urb);
+
+ /* release our reference to this URB, USB core will eventually free it
+ * on its own after the completion callback finishes (or URB is
+ * immediately freed here if its submission has failed)
+ */
+ usb_free_urb(urb);
if (ret == -ENODEV)
set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
@@ -278,6 +273,15 @@ static int rtw89_usb_write_port(struct rtw89_dev *rtwdev, u8 ch_dma,
return ret;
}
+static void rtw89_usb_tx_free_skb(struct rtw89_dev *rtwdev, u8 txch,
+ struct sk_buff *skb)
+{
+ if (txch == RTW89_TXCH_CH12)
+ dev_kfree_skb_any(skb);
+ else
+ ieee80211_free_txskb(rtwdev->hw, skb);
+}
+
static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
{
struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
@@ -292,7 +296,7 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
txcb = kmalloc(sizeof(*txcb), GFP_ATOMIC);
if (!txcb) {
- dev_kfree_skb_any(skb);
+ rtw89_usb_tx_free_skb(rtwdev, txch, skb);
continue;
}
@@ -305,12 +309,13 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)
ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len,
txcb);
if (ret) {
- rtw89_err(rtwdev, "write port txch %d failed: %d\n",
- txch, ret);
+ if (ret != -ENODEV)
+ rtw89_err(rtwdev, "write port txch %d failed: %d\n",
+ txch, ret);
skb_dequeue(&txcb->tx_ack_queue);
kfree(txcb);
- dev_kfree_skb_any(skb);
+ rtw89_usb_tx_free_skb(rtwdev, txch, skb);
}
}
}
@@ -362,6 +367,7 @@ static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev,
{
struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info;
struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+ struct rtw89_tx_skb_data *skb_data;
struct sk_buff *skb = tx_req->skb;
struct rtw89_txwd_body *txdesc;
u32 txdesc_size;
@@ -388,6 +394,12 @@ static int rtw89_usb_ops_tx_write(struct rtw89_dev *rtwdev,
le32p_replace_bits(&txdesc->dword0, 1, RTW89_TXWD_BODY0_STF_MODE);
+ skb_data = RTW89_TX_SKB_CB(skb);
+ if (tx_req->desc_info.sn)
+ skb_data->tx_rpt_sn = tx_req->desc_info.sn;
+ if (tx_req->desc_info.tx_cnt_lmt)
+ skb_data->tx_pkt_cnt_lmt = tx_req->desc_info.tx_cnt_lmt;
+
skb_queue_tail(&rtwusb->tx_queue[desc_info->ch_dma], skb);
return 0;
@@ -410,8 +422,7 @@ static void rtw89_usb_rx_handler(struct work_struct *work)
if (skb_queue_len(&rtwusb->rx_queue) >= RTW89_USB_MAX_RXQ_LEN) {
rtw89_warn(rtwdev, "rx_queue overflow\n");
- dev_kfree_skb_any(rx_skb);
- continue;
+ goto free_or_reuse;
}
memset(&desc_info, 0, sizeof(desc_info));
@@ -422,7 +433,7 @@ static void rtw89_usb_rx_handler(struct work_struct *work)
rtw89_debug(rtwdev, RTW89_DBG_HCI,
"failed to allocate RX skb of size %u\n",
desc_info.pkt_size);
- continue;
+ goto free_or_reuse;
}
pkt_offset = desc_info.offset + desc_info.rxd_len;
@@ -432,6 +443,7 @@ static void rtw89_usb_rx_handler(struct work_struct *work)
rtw89_core_rx(rtwdev, &desc_info, skb);
+free_or_reuse:
if (skb_queue_len(&rtwusb->rx_free_queue) >= RTW89_USB_RX_SKB_NUM)
dev_kfree_skb_any(rx_skb);
else
@@ -567,6 +579,11 @@ static void rtw89_usb_cancel_rx_bufs(struct rtw89_usb *rtwusb)
}
}
+static void rtw89_usb_cancel_tx_bufs(struct rtw89_usb *rtwusb)
+{
+ usb_kill_anchored_urbs(&rtwusb->tx_submitted);
+}
+
static void rtw89_usb_free_rx_bufs(struct rtw89_usb *rtwusb)
{
struct rtw89_usb_rx_ctrl_block *rxcb;
@@ -668,7 +685,10 @@ static void rtw89_usb_deinit_tx(struct rtw89_dev *rtwdev)
static void rtw89_usb_ops_reset(struct rtw89_dev *rtwdev)
{
- /* TODO: anything to do here? */
+ struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+
+ rtw89_usb_cancel_tx_bufs(rtwusb);
+ rtw89_tx_rpt_skbs_purge(rtwdev);
}
static int rtw89_usb_ops_start(struct rtw89_dev *rtwdev)
@@ -698,20 +718,23 @@ static int rtw89_usb_ops_deinit(struct rtw89_dev *rtwdev)
static int rtw89_usb_ops_mac_pre_init(struct rtw89_dev *rtwdev)
{
+ struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+ const struct rtw89_usb_info *info = rtwusb->info;
u32 val32;
- rtw89_write32_set(rtwdev, R_AX_USB_HOST_REQUEST_2, B_AX_R_USBIO_MODE);
+ rtw89_write32_set(rtwdev, info->usb_host_request_2,
+ B_AX_R_USBIO_MODE);
/* fix USB IO hang suggest by chihhanli@realtek.com */
- rtw89_write32_clr(rtwdev, R_AX_USB_WLAN0_1,
+ rtw89_write32_clr(rtwdev, info->usb_wlan0_1,
B_AX_USBRX_RST | B_AX_USBTX_RST);
- val32 = rtw89_read32(rtwdev, R_AX_HCI_FUNC_EN);
+ val32 = rtw89_read32(rtwdev, info->hci_func_en);
val32 &= ~(B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN);
- rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32);
+ rtw89_write32(rtwdev, info->hci_func_en, val32);
val32 |= B_AX_HCI_RXDMA_EN | B_AX_HCI_TXDMA_EN;
- rtw89_write32(rtwdev, R_AX_HCI_FUNC_EN, val32);
+ rtw89_write32(rtwdev, info->hci_func_en, val32);
/* fix USB TRX hang suggest by chihhanli@realtek.com */
return 0;
@@ -725,10 +748,11 @@ static int rtw89_usb_ops_mac_pre_deinit(struct rtw89_dev *rtwdev)
static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev)
{
struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+ const struct rtw89_usb_info *info = rtwusb->info;
enum usb_device_speed speed;
u32 ep;
- rtw89_write32_clr(rtwdev, R_AX_USB3_MAC_NPI_CONFIG_INTF_0,
+ rtw89_write32_clr(rtwdev, info->usb3_mac_npi_config_intf_0,
B_AX_SSPHY_LFPS_FILTER);
speed = rtwusb->udev->speed;
@@ -744,9 +768,9 @@ static int rtw89_usb_ops_mac_post_init(struct rtw89_dev *rtwdev)
if (ep == 8)
continue;
- rtw89_write8_mask(rtwdev, R_AX_USB_ENDPOINT_0,
+ rtw89_write8_mask(rtwdev, info->usb_endpoint_0,
B_AX_EP_IDX, ep);
- rtw89_write8(rtwdev, R_AX_USB_ENDPOINT_2 + 1, NUMP);
+ rtw89_write8(rtwdev, info->usb_endpoint_2 + 1, NUMP);
}
return 0;
@@ -901,6 +925,8 @@ static int rtw89_usb_intf_init(struct rtw89_dev *rtwdev,
struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
int ret;
+ init_usb_anchor(&rtwusb->tx_submitted);
+
ret = rtw89_usb_parse(rtwdev, intf);
if (ret)
return ret;
@@ -949,9 +975,11 @@ int rtw89_usb_probe(struct usb_interface *intf,
rtwusb = rtw89_usb_priv(rtwdev);
rtwusb->rtwdev = rtwdev;
+ rtwusb->info = info->bus.usb;
rtwdev->hci.ops = &rtw89_usb_ops;
rtwdev->hci.type = RTW89_HCI_TYPE_USB;
+ rtwdev->hci.tx_rpt_enabled = true;
ret = rtw89_usb_intf_init(rtwdev, intf);
if (ret) {
@@ -1026,6 +1054,7 @@ void rtw89_usb_disconnect(struct usb_interface *intf)
rtwusb = rtw89_usb_priv(rtwdev);
rtw89_usb_cancel_rx_bufs(rtwusb);
+ rtw89_usb_cancel_tx_bufs(rtwusb);
rtw89_core_unregister(rtwdev);
rtw89_core_deinit(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h
index c1b4bfa20979..203ec8e993e9 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.h
+++ b/drivers/net/wireless/realtek/rtw89/usb.h
@@ -20,6 +20,16 @@
#define RTW89_MAX_ENDPOINT_NUM 9
#define RTW89_MAX_BULKOUT_NUM 7
+struct rtw89_usb_info {
+ u32 usb_host_request_2;
+ u32 usb_wlan0_1;
+ u32 hci_func_en;
+ u32 usb3_mac_npi_config_intf_0;
+ u32 usb_endpoint_0;
+ u32 usb_endpoint_2;
+ u8 bulkout_id[RTW89_DMA_CH_NUM];
+};
+
struct rtw89_usb_rx_ctrl_block {
struct rtw89_dev *rtwdev;
struct urb *rx_urb;
@@ -35,6 +45,7 @@ struct rtw89_usb_tx_ctrl_block {
struct rtw89_usb {
struct rtw89_dev *rtwdev;
struct usb_device *udev;
+ const struct rtw89_usb_info *info;
__le32 *vendor_req_buf;
@@ -49,6 +60,7 @@ struct rtw89_usb {
struct sk_buff_head rx_free_queue;
struct work_struct rx_work;
struct work_struct rx_urb_work;
+ struct usb_anchor tx_submitted;
struct sk_buff_head tx_queue[RTW89_TXCH_NUM];
};
diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c
index 5faa51ad896a..46aba4cb2ee9 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.c
+++ b/drivers/net/wireless/realtek/rtw89/wow.c
@@ -1221,7 +1221,8 @@ static int rtw89_wow_cfg_wake(struct rtw89_dev *rtwdev, bool wow)
}
}
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL,
+ RTW89_ROLE_INFO_CHANGE);
if (ret) {
rtw89_warn(rtwdev, "failed to send h2c cam\n");
return ret;
@@ -1248,7 +1249,7 @@ static int rtw89_wow_check_fw_status(struct rtw89_dev *rtwdev, bool wow_enable)
mac->wow_ctrl.addr, mac->wow_ctrl.mask);
if (ret)
rtw89_err(rtwdev, "failed to check wow status %s\n",
- wow_enable ? "enabled" : "disabled");
+ str_enabled_disabled(wow_enable));
return ret;
}
@@ -1318,7 +1319,8 @@ static int rtw89_wow_swap_fw(struct rtw89_dev *rtwdev, bool wow)
return ret;
}
- ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL);
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, rtwsta_link, NULL,
+ RTW89_ROLE_FW_RESTORE);
if (ret) {
rtw89_warn(rtwdev, "failed to send h2c cam\n");
return ret;
diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c
index a61128debbad..dda36e41eed1 100644
--- a/drivers/net/wireless/silabs/wfx/main.c
+++ b/drivers/net/wireless/silabs/wfx/main.c
@@ -364,7 +364,7 @@ int wfx_probe(struct wfx_dev *wdev)
wdev->pdata.gpio_wakeup = NULL;
wdev->poll_irq = true;
- wdev->bh_wq = alloc_workqueue("wfx_bh_wq", WQ_HIGHPRI, 0);
+ wdev->bh_wq = alloc_workqueue("wfx_bh_wq", WQ_HIGHPRI | WQ_PERCPU, 0);
if (!wdev->bh_wq)
return -ENOMEM;
diff --git a/drivers/net/wireless/st/cw1200/bh.c b/drivers/net/wireless/st/cw1200/bh.c
index 3b4ded2ac801..b034bab4b489 100644
--- a/drivers/net/wireless/st/cw1200/bh.c
+++ b/drivers/net/wireless/st/cw1200/bh.c
@@ -54,8 +54,9 @@ int cw1200_register_bh(struct cw1200_common *priv)
int err = 0;
/* Realtime workqueue */
priv->bh_workqueue = alloc_workqueue("cw1200_bh",
- WQ_MEM_RECLAIM | WQ_HIGHPRI
- | WQ_CPU_INTENSIVE, 1);
+ WQ_MEM_RECLAIM | WQ_HIGHPRI |
+ WQ_CPU_INTENSIVE | WQ_PERCPU,
+ 1);
if (!priv->bh_workqueue)
return -ENOMEM;
@@ -317,10 +318,12 @@ static int cw1200_bh_rx_helper(struct cw1200_common *priv,
if (wsm_id & 0x0400) {
int rc = wsm_release_tx_buffer(priv, 1);
- if (WARN_ON(rc < 0))
+ if (WARN_ON(rc < 0)) {
+ dev_kfree_skb(skb_rx);
return rc;
- else if (rc > 0)
+ } else if (rc > 0) {
*tx = 1;
+ }
}
/* cw1200_wsm_rx takes care on SKB livetime */
diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c
index 80fbf740fe6d..ac756318e8ea 100644
--- a/drivers/net/wireless/ti/wl18xx/debugfs.c
+++ b/drivers/net/wireless/ti/wl18xx/debugfs.c
@@ -272,7 +272,6 @@ static ssize_t radar_detection_write(struct file *file,
if (ret < 0)
count = ret;
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -312,7 +311,6 @@ static ssize_t dynamic_fw_traces_write(struct file *file,
if (ret < 0)
count = ret;
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -374,7 +372,6 @@ static ssize_t radar_debug_mode_write(struct file *file,
wl->radar_debug_mode, 0);
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index fa3a3f71dd15..9d73ba933a16 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -213,7 +213,6 @@ int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
} while (!event);
out:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
free_vector:
kfree(events_vector);
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index eb3d3f0e0b4d..bbfd2725215b 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -63,7 +63,6 @@ void wl1271_debugfs_update_stats(struct wl1271 *wl)
wl->stats.fw_stats_update = jiffies;
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -113,7 +112,6 @@ static void chip_op_handler(struct wl1271 *wl, unsigned long value,
chip_op = arg;
chip_op(wl);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
}
@@ -287,7 +285,6 @@ static ssize_t dynamic_ps_timeout_write(struct file *file,
wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE);
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -357,7 +354,6 @@ static ssize_t forced_ps_write(struct file *file,
wl1271_ps_set_mode(wl, wlvif, ps_mode);
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -830,7 +826,6 @@ static ssize_t rx_streaming_interval_write(struct file *file,
wl1271_recalc_rx_streaming(wl, wlvif);
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -886,7 +881,6 @@ static ssize_t rx_streaming_always_write(struct file *file,
wl1271_recalc_rx_streaming(wl, wlvif);
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -934,7 +928,6 @@ static ssize_t beacon_filtering_write(struct file *file,
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, !!value);
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -1015,7 +1008,6 @@ static ssize_t sleep_auth_write(struct file *file,
goto out_sleep;
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -1090,7 +1082,6 @@ read_err:
goto part_err;
part_err:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
skip_read:
@@ -1172,7 +1163,6 @@ write_err:
goto part_err;
part_err:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
skip_write:
@@ -1247,7 +1237,6 @@ static ssize_t fw_logger_write(struct file *file,
ret = wl12xx_cmd_config_fwlog(wl);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 6116a8522d96..12f0167d7380 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -154,7 +154,6 @@ static void wl1271_rx_streaming_enable_work(struct work_struct *work)
jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration));
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -181,7 +180,6 @@ static void wl1271_rx_streaming_disable_work(struct work_struct *work)
goto out_sleep;
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -234,7 +232,6 @@ static void wlcore_rc_update_work(struct work_struct *work)
}
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -711,7 +708,6 @@ static int wlcore_irq_locked(struct wl1271 *wl)
}
err_ret:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -1047,7 +1043,6 @@ static void wl1271_recovery_work(struct work_struct *work)
}
wlcore_op_stop_locked(wl);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
ieee80211_restart_hw(wl->hw);
@@ -1943,7 +1938,6 @@ static int __maybe_unused wl1271_op_resume(struct ieee80211_hw *hw)
goto out_sleep;
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -2131,7 +2125,6 @@ static void wlcore_channel_switch_work(struct work_struct *work)
wl12xx_cmd_stop_channel_switch(wl, wlvif);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -2201,7 +2194,6 @@ static void wlcore_pending_auth_complete_work(struct work_struct *work)
/* cancel the ROC if active */
wlcore_update_inconn_sta(wl, wlvif, NULL, false);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -2694,7 +2686,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
else
wl->sta_count++;
out:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out_unlock:
mutex_unlock(&wl->mutex);
@@ -2774,7 +2765,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
}
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
}
deinit:
@@ -3200,7 +3190,6 @@ static int wl1271_op_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)
}
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -3315,7 +3304,6 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
*/
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -3531,7 +3519,6 @@ static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out_wake_queues:
@@ -3695,7 +3682,6 @@ static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
}
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out_unlock:
@@ -3724,7 +3710,6 @@ void wlcore_regdomain_config(struct wl1271 *wl)
goto out;
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -3772,7 +3757,6 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
ret = wlcore_scan(hw->priv, vif, ssid, len, req);
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -3823,7 +3807,6 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
ieee80211_scan_completed(wl->hw, &info);
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -3860,7 +3843,6 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
wl->sched_vif = wlvif;
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -3887,7 +3869,6 @@ static int wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
wl->ops->sched_scan_stop(wl, wlvif);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -3916,7 +3897,6 @@ static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw,
if (ret < 0)
wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -3948,7 +3928,6 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx,
if (ret < 0)
wl1271_warning("set rts threshold failed: %d", ret);
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -4714,7 +4693,6 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
else
wl1271_bss_info_changed_sta(wl, vif, bss_conf, changed);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -4779,7 +4757,6 @@ static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
}
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -4828,7 +4805,6 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
wlvif->radar_enabled = true;
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -4871,7 +4847,6 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
wlvif->radar_enabled = false;
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -4941,7 +4916,6 @@ wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw,
goto out_sleep;
}
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -4995,7 +4969,6 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
0, 0);
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -5029,7 +5002,6 @@ static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
goto out_sleep;
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -5342,7 +5314,6 @@ static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
ret = wl12xx_update_sta_state(wl, wlvif, sta, old_state, new_state);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -5467,7 +5438,6 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
ret = -EINVAL;
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -5511,7 +5481,6 @@ static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
}
out:
@@ -5566,7 +5535,6 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
}
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
@@ -5645,7 +5613,6 @@ static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw,
set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags);
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -5699,7 +5666,6 @@ static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
msecs_to_jiffies(duration));
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -5748,7 +5714,6 @@ static int wlcore_roc_completed(struct wl1271 *wl)
ret = __wlcore_roc_completed(wl);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -5839,7 +5804,6 @@ static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
sinfo->signal = rssi_dbm;
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c
index b414305acc32..f6dc54c1dbad 100644
--- a/drivers/net/wireless/ti/wlcore/scan.c
+++ b/drivers/net/wireless/ti/wlcore/scan.c
@@ -69,7 +69,6 @@ void wl1271_scan_complete_work(struct work_struct *work)
wlcore_cmd_regdomain_config_locked(wl);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
ieee80211_scan_completed(wl->hw, &info);
diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c
index 65ca5dc569a0..5ab6c1683675 100644
--- a/drivers/net/wireless/ti/wlcore/sysfs.c
+++ b/drivers/net/wireless/ti/wlcore/sysfs.c
@@ -58,7 +58,6 @@ static ssize_t bt_coex_state_store(struct device *dev,
goto out;
wl1271_acx_sg_enable(wl, wl->sg_enabled);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c
index fc8ea58bc165..7c0cb1b7fef0 100644
--- a/drivers/net/wireless/ti/wlcore/testmode.c
+++ b/drivers/net/wireless/ti/wlcore/testmode.c
@@ -127,7 +127,6 @@ static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
}
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -192,7 +191,6 @@ static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
out_free:
kfree(cmd);
out_sleep:
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 464587d16ab2..f76087be2f75 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -863,7 +863,6 @@ void wl1271_tx_work(struct work_struct *work)
goto out;
}
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
index e4269e2b0098..5bb9eb300f97 100644
--- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c
+++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c
@@ -60,7 +60,6 @@ wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
ret = wlcore_smart_config_start(wl,
nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -92,7 +91,6 @@ wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
ret = wlcore_smart_config_stop(wl);
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
@@ -140,7 +138,6 @@ wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
- pm_runtime_mark_last_busy(wl->dev);
pm_runtime_put_autosuspend(wl->dev);
out:
mutex_unlock(&wl->mutex);
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 9f856042a67a..551f5eb4e747 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -2003,8 +2003,14 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
struct ieee80211_sta *sta = control->sta;
struct ieee80211_bss_conf *bss_conf;
+ /* This can happen in case of monitor injection */
+ if (!vif) {
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+
if (link != IEEE80211_LINK_UNSPECIFIED) {
- bss_conf = rcu_dereference(txi->control.vif->link_conf[link]);
+ bss_conf = rcu_dereference(vif->link_conf[link]);
if (sta)
link_sta = rcu_dereference(sta->link[link]);
} else {
@@ -2065,13 +2071,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
return;
}
- if (txi->control.vif)
- hwsim_check_magic(txi->control.vif);
+ if (vif)
+ hwsim_check_magic(vif);
if (control->sta)
hwsim_check_sta_magic(control->sta);
if (ieee80211_hw_check(hw, SUPPORTS_RC_TABLE))
- ieee80211_get_tx_rates(txi->control.vif, control->sta, skb,
+ ieee80211_get_tx_rates(vif, control->sta, skb,
txi->control.rates,
ARRAY_SIZE(txi->control.rates));
@@ -5793,6 +5799,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
ieee80211_hw_set(hw, NO_AUTO_VIF);
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
+ wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_PUNCT);
for (i = 0; i < ARRAY_SIZE(data->link_data); i++) {
hrtimer_setup(&data->link_data[i].beacon_timer, mac80211_hwsim_beacon,
@@ -6698,14 +6705,15 @@ static struct genl_family hwsim_genl_family __ro_after_init = {
.n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
};
-static void remove_user_radios(u32 portid)
+static void remove_user_radios(u32 portid, int netgroup)
{
struct mac80211_hwsim_data *entry, *tmp;
LIST_HEAD(list);
spin_lock_bh(&hwsim_radio_lock);
list_for_each_entry_safe(entry, tmp, &hwsim_radios, list) {
- if (entry->destroy_on_close && entry->portid == portid) {
+ if (entry->destroy_on_close && entry->portid == portid &&
+ entry->netgroup == netgroup) {
list_move(&entry->list, &list);
rhashtable_remove_fast(&hwsim_radios_rht, &entry->rht,
hwsim_rht_params);
@@ -6730,7 +6738,7 @@ static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
if (state != NETLINK_URELEASE)
return NOTIFY_DONE;
- remove_user_radios(notify->portid);
+ remove_user_radios(notify->portid, hwsim_net_get_netgroup(notify->net));
if (notify->portid == hwsim_net_get_wmediumd(notify->net)) {
printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
index 2faa0de2a36e..8ee15a15f4ca 100644
--- a/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zydas/zd1211rw/zd_usb.c
@@ -791,6 +791,7 @@ error:
if (urbs) {
for (i = 0; i < RX_URBS_COUNT; i++)
free_rx_urb(urbs[i]);
+ kfree(urbs);
}
return r;
}
diff --git a/drivers/net/wwan/iosm/iosm_ipc_devlink.c b/drivers/net/wwan/iosm/iosm_ipc_devlink.c
index 33d6342124bc..301a9d294d30 100644
--- a/drivers/net/wwan/iosm/iosm_ipc_devlink.c
+++ b/drivers/net/wwan/iosm/iosm_ipc_devlink.c
@@ -21,7 +21,8 @@ static struct iosm_coredump_file_info list[IOSM_NOF_CD_REGION] = {
/* Get the param values for the specific param ID's */
static int ipc_devlink_get_param(struct devlink *dl, u32 id,
- struct devlink_param_gset_ctx *ctx)
+ struct devlink_param_gset_ctx *ctx,
+ struct netlink_ext_ack *extack)
{
struct iosm_devlink *ipc_devlink = devlink_priv(dl);
diff --git a/drivers/net/wwan/mhi_wwan_mbim.c b/drivers/net/wwan/mhi_wwan_mbim.c
index c814fbd756a1..0dace12f5ad0 100644
--- a/drivers/net/wwan/mhi_wwan_mbim.c
+++ b/drivers/net/wwan/mhi_wwan_mbim.c
@@ -78,8 +78,9 @@ struct mhi_mbim_context {
struct mbim_tx_hdr {
struct usb_cdc_ncm_nth16 nth16;
+
+ /* Must be last as it ends in a flexible-array member. */
struct usb_cdc_ncm_ndp16 ndp16;
- struct usb_cdc_ncm_dpe16 dpe16[2];
} __packed;
static struct mhi_mbim_link *mhi_mbim_get_link_rcu(struct mhi_mbim_context *mbim,
@@ -98,7 +99,7 @@ static struct mhi_mbim_link *mhi_mbim_get_link_rcu(struct mhi_mbim_context *mbim
static int mhi_mbim_get_link_mux_id(struct mhi_controller *cntrl)
{
if (strcmp(cntrl->name, "foxconn-dw5934e") == 0 ||
- strcmp(cntrl->name, "foxconn-t99w515") == 0)
+ strcmp(cntrl->name, "foxconn-t99w640") == 0)
return WDS_BIND_MUX_DATA_PORT_MUX_ID;
return 0;
@@ -107,20 +108,20 @@ static int mhi_mbim_get_link_mux_id(struct mhi_controller *cntrl)
static struct sk_buff *mbim_tx_fixup(struct sk_buff *skb, unsigned int session,
u16 tx_seq)
{
+ DEFINE_RAW_FLEX(struct mbim_tx_hdr, mbim_hdr, ndp16.dpe16, 2);
unsigned int dgram_size = skb->len;
struct usb_cdc_ncm_nth16 *nth16;
struct usb_cdc_ncm_ndp16 *ndp16;
- struct mbim_tx_hdr *mbim_hdr;
/* Only one NDP is sent, containing the IP packet (no aggregation) */
/* Ensure we have enough headroom for crafting MBIM header */
- if (skb_cow_head(skb, sizeof(struct mbim_tx_hdr))) {
+ if (skb_cow_head(skb, __struct_size(mbim_hdr))) {
dev_kfree_skb_any(skb);
return NULL;
}
- mbim_hdr = skb_push(skb, sizeof(struct mbim_tx_hdr));
+ mbim_hdr = skb_push(skb, __struct_size(mbim_hdr));
/* Fill NTB header */
nth16 = &mbim_hdr->nth16;
@@ -133,12 +134,11 @@ static struct sk_buff *mbim_tx_fixup(struct sk_buff *skb, unsigned int session,
/* Fill the unique NDP */
ndp16 = &mbim_hdr->ndp16;
ndp16->dwSignature = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN | (session << 24));
- ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16)
- + sizeof(struct usb_cdc_ncm_dpe16) * 2);
+ ndp16->wLength = cpu_to_le16(struct_size(ndp16, dpe16, 2));
ndp16->wNextNdpIndex = 0;
/* Datagram follows the mbim header */
- ndp16->dpe16[0].wDatagramIndex = cpu_to_le16(sizeof(struct mbim_tx_hdr));
+ ndp16->dpe16[0].wDatagramIndex = cpu_to_le16(__struct_size(mbim_hdr));
ndp16->dpe16[0].wDatagramLength = cpu_to_le16(dgram_size);
/* null termination */
@@ -584,7 +584,8 @@ static void mhi_mbim_setup(struct net_device *ndev)
{
ndev->header_ops = NULL; /* No header */
ndev->type = ARPHRD_RAWIP;
- ndev->needed_headroom = sizeof(struct mbim_tx_hdr);
+ ndev->needed_headroom =
+ struct_size_t(struct mbim_tx_hdr, ndp16.dpe16, 2);
ndev->hard_header_len = 0;
ndev->addr_len = 0;
ndev->flags = IFF_POINTOPOINT | IFF_NOARP;
diff --git a/drivers/net/wwan/qcom_bam_dmux.c b/drivers/net/wwan/qcom_bam_dmux.c
index 64dab8b57611..6a5b22589af4 100644
--- a/drivers/net/wwan/qcom_bam_dmux.c
+++ b/drivers/net/wwan/qcom_bam_dmux.c
@@ -162,7 +162,6 @@ static void bam_dmux_tx_done(struct bam_dmux_skb_dma *skb_dma)
struct bam_dmux *dmux = skb_dma->dmux;
unsigned long flags;
- pm_runtime_mark_last_busy(dmux->dev);
pm_runtime_put_autosuspend(dmux->dev);
if (skb_dma->addr)
@@ -397,7 +396,6 @@ static void bam_dmux_tx_wakeup_work(struct work_struct *work)
dma_async_issue_pending(dmux->tx);
out:
- pm_runtime_mark_last_busy(dmux->dev);
pm_runtime_put_autosuspend(dmux->dev);
}
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
index 97163e1e5783..43ac1c3f1ad0 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
@@ -250,7 +250,6 @@ static void t7xx_cldma_rx_done(struct work_struct *work)
t7xx_cldma_clear_ip_busy(&md_ctrl->hw_info);
t7xx_cldma_hw_irq_en_txrx(&md_ctrl->hw_info, queue->index, MTK_RX);
t7xx_cldma_hw_irq_en_eq(&md_ctrl->hw_info, queue->index, MTK_RX);
- pm_runtime_mark_last_busy(md_ctrl->dev);
pm_runtime_put_autosuspend(md_ctrl->dev);
}
@@ -362,7 +361,6 @@ static void t7xx_cldma_tx_done(struct work_struct *work)
}
spin_unlock_irqrestore(&md_ctrl->cldma_lock, flags);
- pm_runtime_mark_last_busy(md_ctrl->dev);
pm_runtime_put_autosuspend(md_ctrl->dev);
}
@@ -899,7 +897,7 @@ static void t7xx_cldma_hw_start_send(struct cldma_ctrl *md_ctrl, int qno,
* @queue: CLDMA queue.
* @recv_skb: Receiving skb callback.
*/
-void t7xx_cldma_set_recv_skb(struct cldma_queue *queue,
+static void t7xx_cldma_set_recv_skb(struct cldma_queue *queue,
int (*recv_skb)(struct cldma_queue *queue, struct sk_buff *skb))
{
queue->recv_skb = recv_skb;
@@ -987,7 +985,6 @@ int t7xx_cldma_send_skb(struct cldma_ctrl *md_ctrl, int qno, struct sk_buff *skb
allow_sleep:
t7xx_pci_enable_sleep(md_ctrl->t7xx_dev);
- pm_runtime_mark_last_busy(md_ctrl->dev);
pm_runtime_put_autosuspend(md_ctrl->dev);
return ret;
}
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_cldma.h b/drivers/net/wwan/t7xx/t7xx_hif_cldma.h
index f2d9941be9c8..9d0107e18a7b 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.h
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.h
@@ -126,8 +126,6 @@ void t7xx_cldma_switch_cfg(struct cldma_ctrl *md_ctrl, enum cldma_cfg cfg_id);
void t7xx_cldma_start(struct cldma_ctrl *md_ctrl);
int t7xx_cldma_stop(struct cldma_ctrl *md_ctrl);
void t7xx_cldma_reset(struct cldma_ctrl *md_ctrl);
-void t7xx_cldma_set_recv_skb(struct cldma_queue *queue,
- int (*recv_skb)(struct cldma_queue *queue, struct sk_buff *skb));
int t7xx_cldma_send_skb(struct cldma_ctrl *md_ctrl, int qno, struct sk_buff *skb);
void t7xx_cldma_stop_all_qs(struct cldma_ctrl *md_ctrl, enum mtk_txrx tx_rx);
void t7xx_cldma_clear_all_qs(struct cldma_ctrl *md_ctrl, enum mtk_txrx tx_rx);
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
index 2310493203d3..b76bea6ab2d7 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
@@ -877,7 +877,6 @@ int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget)
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 {
@@ -1078,7 +1077,6 @@ static void t7xx_dpmaif_bat_release_work(struct work_struct *work)
}
t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
- pm_runtime_mark_last_busy(dpmaif_ctrl->dev);
pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
}
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
index 8dab025a088a..236d632cf591 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
@@ -185,7 +185,6 @@ static void t7xx_dpmaif_tx_done(struct work_struct *work)
}
t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
- pm_runtime_mark_last_busy(dpmaif_ctrl->dev);
pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
}
@@ -468,7 +467,6 @@ static int t7xx_dpmaif_tx_hw_push_thread(void *arg)
t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev);
t7xx_do_tx_hw_push(dpmaif_ctrl);
t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
- pm_runtime_mark_last_busy(dpmaif_ctrl->dev);
pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
}
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index a11a0e949400..7c2220366623 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -2696,8 +2696,9 @@ static int __init netif_init(void)
pr_info("Initialising Xen virtual ethernet driver\n");
- /* Allow as many queues as there are CPUs inut max. 8 if user has not
- * specified a value.
+ /* Allow the number of queues to match the number of CPUs, but not exceed
+ * the maximum limit. If the user has not specified a value, the default
+ * maximum limit is 8.
*/
if (xennet_max_queues == 0)
xennet_max_queues = min_t(unsigned int, MAX_QUEUES_DEFAULT,
diff --git a/drivers/nfc/mei_phy.h b/drivers/nfc/mei_phy.h
index 2b1edb3eba15..9b9c5eb54e07 100644
--- a/drivers/nfc/mei_phy.h
+++ b/drivers/nfc/mei_phy.h
@@ -12,11 +12,11 @@
#define MEI_NFC_MAX_HCI_PAYLOAD 300
/**
- * struct nfc_mei_phy
+ * struct nfc_mei_phy - NFC description of the MEI PHY and interface functions
*
* @cldev: mei client device
* @hdev: nfc hci device
-
+ *
* @send_wq: send completion wait queue
* @fw_ivn: NFC Interface Version Number
* @vendor_id: NFC manufacturer ID
diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
index f35d3f71d14f..15b3d07f8ccd 100644
--- a/drivers/nvme/host/apple.c
+++ b/drivers/nvme/host/apple.c
@@ -1283,6 +1283,7 @@ static const struct nvme_ctrl_ops nvme_ctrl_ops = {
.reg_read64 = apple_nvme_reg_read64,
.free_ctrl = apple_nvme_free_ctrl,
.get_address = apple_nvme_get_address,
+ .get_virt_boundary = nvme_get_virt_boundary,
};
static void apple_nvme_async_probe(void *data, async_cookie_t cookie)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index fa4181d7de73..7bf228df6001 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -2069,13 +2069,13 @@ static u32 nvme_max_drv_segments(struct nvme_ctrl *ctrl)
}
static void nvme_set_ctrl_limits(struct nvme_ctrl *ctrl,
- struct queue_limits *lim)
+ struct queue_limits *lim, bool is_admin)
{
lim->max_hw_sectors = ctrl->max_hw_sectors;
lim->max_segments = min_t(u32, USHRT_MAX,
min_not_zero(nvme_max_drv_segments(ctrl), ctrl->max_segments));
lim->max_integrity_segments = ctrl->max_integrity_segments;
- lim->virt_boundary_mask = NVME_CTRL_PAGE_SIZE - 1;
+ lim->virt_boundary_mask = ctrl->ops->get_virt_boundary(ctrl, is_admin);
lim->max_segment_size = UINT_MAX;
lim->dma_alignment = 3;
}
@@ -2177,7 +2177,7 @@ static int nvme_update_ns_info_generic(struct nvme_ns *ns,
int ret;
lim = queue_limits_start_update(ns->disk->queue);
- nvme_set_ctrl_limits(ns->ctrl, &lim);
+ nvme_set_ctrl_limits(ns->ctrl, &lim, false);
memflags = blk_mq_freeze_queue(ns->disk->queue);
ret = queue_limits_commit_update(ns->disk->queue, &lim);
@@ -2381,7 +2381,7 @@ static int nvme_update_ns_info_block(struct nvme_ns *ns,
ns->head->lba_shift = id->lbaf[lbaf].ds;
ns->head->nuse = le64_to_cpu(id->nuse);
capacity = nvme_lba_to_sect(ns->head, le64_to_cpu(id->nsze));
- nvme_set_ctrl_limits(ns->ctrl, &lim);
+ nvme_set_ctrl_limits(ns->ctrl, &lim, false);
nvme_configure_metadata(ns->ctrl, ns->head, id, nvm, info);
nvme_set_chunk_sectors(ns, id, &lim);
if (!nvme_update_disk_info(ns, id, &lim))
@@ -2599,10 +2599,9 @@ static void nvme_configure_opal(struct nvme_ctrl *ctrl, bool was_suspended)
#ifdef CONFIG_BLK_DEV_ZONED
static int nvme_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones, struct blk_report_zones_args *args)
{
- return nvme_ns_report_zones(disk->private_data, sector, nr_zones, cb,
- data);
+ return nvme_ns_report_zones(disk->private_data, sector, nr_zones, args);
}
#else
#define nvme_report_zones NULL
@@ -3589,7 +3588,7 @@ static int nvme_init_identify(struct nvme_ctrl *ctrl)
min_not_zero(ctrl->max_hw_sectors, max_hw_sectors);
lim = queue_limits_start_update(ctrl->admin_q);
- nvme_set_ctrl_limits(ctrl, &lim);
+ nvme_set_ctrl_limits(ctrl, &lim, true);
ret = queue_limits_commit_update(ctrl->admin_q, &lim);
if (ret)
goto out_free;
@@ -4901,7 +4900,6 @@ void nvme_remove_admin_tag_set(struct nvme_ctrl *ctrl)
*/
nvme_stop_keep_alive(ctrl);
blk_mq_destroy_queue(ctrl->admin_q);
- blk_put_queue(ctrl->admin_q);
if (ctrl->ops->flags & NVME_F_FABRICS) {
blk_mq_destroy_queue(ctrl->fabrics_q);
blk_put_queue(ctrl->fabrics_q);
@@ -5045,6 +5043,8 @@ static void nvme_free_ctrl(struct device *dev)
container_of(dev, struct nvme_ctrl, ctrl_device);
struct nvme_subsystem *subsys = ctrl->subsys;
+ if (ctrl->admin_q)
+ blk_put_queue(ctrl->admin_q);
if (!subsys || ctrl->instance != subsys->instance)
ida_free(&nvme_instance_ida, ctrl->instance);
nvme_free_cels(ctrl);
diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h
index 1b58ee7d0dce..caf5503d0833 100644
--- a/drivers/nvme/host/fabrics.h
+++ b/drivers/nvme/host/fabrics.h
@@ -217,6 +217,12 @@ static inline unsigned int nvmf_nr_io_queues(struct nvmf_ctrl_options *opts)
min(opts->nr_poll_queues, num_online_cpus());
}
+static inline unsigned long nvmf_get_virt_boundary(struct nvme_ctrl *ctrl,
+ bool is_admin)
+{
+ return 0;
+}
+
int nvmf_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val);
int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val);
int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val);
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 03987f497a5b..873954d43b18 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -2355,17 +2355,11 @@ nvme_fc_ctrl_free(struct kref *ref)
container_of(ref, struct nvme_fc_ctrl, ref);
unsigned long flags;
- if (ctrl->ctrl.tagset)
- nvme_remove_io_tag_set(&ctrl->ctrl);
-
/* remove from rport list */
spin_lock_irqsave(&ctrl->rport->lock, flags);
list_del(&ctrl->ctrl_list);
spin_unlock_irqrestore(&ctrl->rport->lock, flags);
- nvme_unquiesce_admin_queue(&ctrl->ctrl);
- nvme_remove_admin_tag_set(&ctrl->ctrl);
-
kfree(ctrl->queues);
put_device(ctrl->dev);
@@ -3259,13 +3253,20 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
- cancel_work_sync(&ctrl->ioerr_work);
cancel_delayed_work_sync(&ctrl->connect_work);
+
/*
* kill the association on the link side. this will block
* waiting for io to terminate
*/
nvme_fc_delete_association(ctrl);
+ cancel_work_sync(&ctrl->ioerr_work);
+
+ if (ctrl->ctrl.tagset)
+ nvme_remove_io_tag_set(&ctrl->ctrl);
+
+ nvme_unquiesce_admin_queue(&ctrl->ctrl);
+ nvme_remove_admin_tag_set(&ctrl->ctrl);
}
static void
@@ -3360,6 +3361,7 @@ static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = {
.submit_async_event = nvme_fc_submit_async_event,
.delete_ctrl = nvme_fc_delete_ctrl,
.get_address = nvmf_get_address,
+ .get_virt_boundary = nvmf_get_virt_boundary,
};
static void
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index c212fa952c0f..4fa8400a5627 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -398,14 +398,15 @@ static inline struct nvme_uring_cmd_pdu *nvme_uring_cmd_pdu(
return io_uring_cmd_to_pdu(ioucmd, struct nvme_uring_cmd_pdu);
}
-static void nvme_uring_task_cb(struct io_uring_cmd *ioucmd,
- unsigned issue_flags)
+static void nvme_uring_task_cb(struct io_tw_req tw_req, io_tw_token_t tw)
{
+ struct io_uring_cmd *ioucmd = io_uring_cmd_from_tw(tw_req);
struct nvme_uring_cmd_pdu *pdu = nvme_uring_cmd_pdu(ioucmd);
if (pdu->bio)
blk_rq_unmap_user(pdu->bio);
- io_uring_cmd_done32(ioucmd, pdu->status, pdu->result, issue_flags);
+ io_uring_cmd_done32(ioucmd, pdu->status, pdu->result,
+ IO_URING_CMD_TASK_WORK_ISSUE_FLAGS);
}
static enum rq_end_io_ret nvme_uring_cmd_end_io(struct request *req,
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index 543e17aead12..174027d1cc19 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -576,7 +576,7 @@ static int nvme_ns_head_get_unique_id(struct gendisk *disk, u8 id[16],
#ifdef CONFIG_BLK_DEV_ZONED
static int nvme_ns_head_report_zones(struct gendisk *disk, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones, struct blk_report_zones_args *args)
{
struct nvme_ns_head *head = disk->private_data;
struct nvme_ns *ns;
@@ -585,7 +585,7 @@ static int nvme_ns_head_report_zones(struct gendisk *disk, sector_t sector,
srcu_idx = srcu_read_lock(&head->srcu);
ns = nvme_find_path(head);
if (ns)
- ret = nvme_ns_report_zones(ns, sector, nr_zones, cb, data);
+ ret = nvme_ns_report_zones(ns, sector, nr_zones, args);
srcu_read_unlock(&head->srcu, srcu_idx);
return ret;
}
@@ -793,7 +793,7 @@ static void nvme_mpath_set_live(struct nvme_ns *ns)
return;
}
nvme_add_ns_head_cdev(head);
- kblockd_schedule_work(&head->partition_scan_work);
+ queue_work(nvme_wq, &head->partition_scan_work);
}
nvme_mpath_add_sysfs_link(ns->head);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 102fae6a231c..9a5f28c5103c 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -558,6 +558,12 @@ static inline bool nvme_ns_has_pi(struct nvme_ns_head *head)
return head->pi_type && head->ms == head->pi_size;
}
+static inline unsigned long nvme_get_virt_boundary(struct nvme_ctrl *ctrl,
+ bool is_admin)
+{
+ return NVME_CTRL_PAGE_SIZE - 1;
+}
+
struct nvme_ctrl_ops {
const char *name;
struct module *module;
@@ -578,6 +584,7 @@ struct nvme_ctrl_ops {
int (*get_address)(struct nvme_ctrl *ctrl, char *buf, int size);
void (*print_device_info)(struct nvme_ctrl *ctrl);
bool (*supports_pci_p2pdma)(struct nvme_ctrl *ctrl);
+ unsigned long (*get_virt_boundary)(struct nvme_ctrl *ctrl, bool is_admin);
};
/*
@@ -1108,7 +1115,7 @@ struct nvme_zone_info {
};
int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data);
+ unsigned int nr_zones, struct blk_report_zones_args *args);
int nvme_query_zone_info(struct nvme_ns *ns, unsigned lbaf,
struct nvme_zone_info *zi);
void nvme_update_zone_info(struct nvme_ns *ns, struct queue_limits *lim,
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index c916176bd9f0..e5ca8301bb8b 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -260,8 +260,20 @@ enum nvme_iod_flags {
/* single segment dma mapping */
IOD_SINGLE_SEGMENT = 1U << 2,
+ /* Data payload contains p2p memory */
+ IOD_DATA_P2P = 1U << 3,
+
+ /* Metadata contains p2p memory */
+ IOD_META_P2P = 1U << 4,
+
+ /* Data payload contains MMIO memory */
+ IOD_DATA_MMIO = 1U << 5,
+
+ /* Metadata contains MMIO memory */
+ IOD_META_MMIO = 1U << 6,
+
/* Metadata using non-coalesced MPTR */
- IOD_SINGLE_META_SEGMENT = 1U << 5,
+ IOD_SINGLE_META_SEGMENT = 1U << 7,
};
struct nvme_dma_vec {
@@ -613,9 +625,22 @@ static inline enum nvme_use_sgl nvme_pci_use_sgls(struct nvme_dev *dev,
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
if (nvmeq->qid && nvme_ctrl_sgl_supported(&dev->ctrl)) {
- if (nvme_req(req)->flags & NVME_REQ_USERCMD)
- return SGL_FORCED;
- if (req->nr_integrity_segments > 1)
+ /*
+ * When the controller is capable of using SGL, there are
+ * several conditions that we force to use it:
+ *
+ * 1. A request containing page gaps within the controller's
+ * mask can not use the PRP format.
+ *
+ * 2. User commands use SGL because that lets the device
+ * validate the requested transfer lengths.
+ *
+ * 3. Multiple integrity segments must use SGL as that's the
+ * only way to describe such a command in NVMe.
+ */
+ if (req_phys_gap_mask(req) & (NVME_CTRL_PAGE_SIZE - 1) ||
+ nvme_req(req)->flags & NVME_REQ_USERCMD ||
+ req->nr_integrity_segments > 1)
return SGL_FORCED;
return SGL_SUPPORTED;
}
@@ -685,20 +710,20 @@ static void nvme_free_descriptors(struct request *req)
}
}
-static void nvme_free_prps(struct request *req)
+static void nvme_free_prps(struct request *req, unsigned int attrs)
{
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
unsigned int i;
for (i = 0; i < iod->nr_dma_vecs; i++)
- dma_unmap_page(nvmeq->dev->dev, iod->dma_vecs[i].addr,
- iod->dma_vecs[i].len, rq_dma_dir(req));
+ dma_unmap_phys(nvmeq->dev->dev, iod->dma_vecs[i].addr,
+ iod->dma_vecs[i].len, rq_dma_dir(req), attrs);
mempool_free(iod->dma_vecs, nvmeq->dev->dmavec_mempool);
}
static void nvme_free_sgls(struct request *req, struct nvme_sgl_desc *sge,
- struct nvme_sgl_desc *sg_list)
+ struct nvme_sgl_desc *sg_list, unsigned int attrs)
{
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
enum dma_data_direction dir = rq_dma_dir(req);
@@ -707,22 +732,25 @@ static void nvme_free_sgls(struct request *req, struct nvme_sgl_desc *sge,
unsigned int i;
if (sge->type == (NVME_SGL_FMT_DATA_DESC << 4)) {
- dma_unmap_page(dma_dev, le64_to_cpu(sge->addr), len, dir);
+ dma_unmap_phys(dma_dev, le64_to_cpu(sge->addr), len, dir,
+ attrs);
return;
}
for (i = 0; i < len / sizeof(*sg_list); i++)
- dma_unmap_page(dma_dev, le64_to_cpu(sg_list[i].addr),
- le32_to_cpu(sg_list[i].length), dir);
+ dma_unmap_phys(dma_dev, le64_to_cpu(sg_list[i].addr),
+ le32_to_cpu(sg_list[i].length), dir, attrs);
}
static void nvme_unmap_metadata(struct request *req)
{
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
+ enum pci_p2pdma_map_type map = PCI_P2PDMA_MAP_NONE;
enum dma_data_direction dir = rq_dma_dir(req);
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
struct device *dma_dev = nvmeq->dev->dev;
struct nvme_sgl_desc *sge = iod->meta_descriptor;
+ unsigned int attrs = 0;
if (iod->flags & IOD_SINGLE_META_SEGMENT) {
dma_unmap_page(dma_dev, iod->meta_dma,
@@ -731,13 +759,20 @@ static void nvme_unmap_metadata(struct request *req)
return;
}
- if (!blk_rq_integrity_dma_unmap(req, dma_dev, &iod->meta_dma_state,
- iod->meta_total_len)) {
+ if (iod->flags & IOD_META_P2P)
+ map = PCI_P2PDMA_MAP_BUS_ADDR;
+ else if (iod->flags & IOD_META_MMIO) {
+ map = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
+ attrs |= DMA_ATTR_MMIO;
+ }
+
+ if (!blk_rq_dma_unmap(req, dma_dev, &iod->meta_dma_state,
+ iod->meta_total_len, map)) {
if (nvme_pci_cmd_use_meta_sgl(&iod->cmd))
- nvme_free_sgls(req, sge, &sge[1]);
+ nvme_free_sgls(req, sge, &sge[1], attrs);
else
- dma_unmap_page(dma_dev, iod->meta_dma,
- iod->meta_total_len, dir);
+ dma_unmap_phys(dma_dev, iod->meta_dma,
+ iod->meta_total_len, dir, attrs);
}
if (iod->meta_descriptor)
@@ -747,9 +782,11 @@ static void nvme_unmap_metadata(struct request *req)
static void nvme_unmap_data(struct request *req)
{
+ enum pci_p2pdma_map_type map = PCI_P2PDMA_MAP_NONE;
struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
struct device *dma_dev = nvmeq->dev->dev;
+ unsigned int attrs = 0;
if (iod->flags & IOD_SINGLE_SEGMENT) {
static_assert(offsetof(union nvme_data_ptr, prp1) ==
@@ -759,12 +796,20 @@ static void nvme_unmap_data(struct request *req)
return;
}
- if (!blk_rq_dma_unmap(req, dma_dev, &iod->dma_state, iod->total_len)) {
+ if (iod->flags & IOD_DATA_P2P)
+ map = PCI_P2PDMA_MAP_BUS_ADDR;
+ else if (iod->flags & IOD_DATA_MMIO) {
+ map = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
+ attrs |= DMA_ATTR_MMIO;
+ }
+
+ if (!blk_rq_dma_unmap(req, dma_dev, &iod->dma_state, iod->total_len,
+ map)) {
if (nvme_pci_cmd_use_sgl(&iod->cmd))
nvme_free_sgls(req, iod->descriptors[0],
- &iod->cmd.common.dptr.sgl);
+ &iod->cmd.common.dptr.sgl, attrs);
else
- nvme_free_prps(req);
+ nvme_free_prps(req, attrs);
}
if (iod->nr_descriptors)
@@ -1035,6 +1080,19 @@ static blk_status_t nvme_map_data(struct request *req)
if (!blk_rq_dma_map_iter_start(req, dev->dev, &iod->dma_state, &iter))
return iter.status;
+ switch (iter.p2pdma.map) {
+ case PCI_P2PDMA_MAP_BUS_ADDR:
+ iod->flags |= IOD_DATA_P2P;
+ break;
+ case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
+ iod->flags |= IOD_DATA_MMIO;
+ break;
+ case PCI_P2PDMA_MAP_NONE:
+ break;
+ default:
+ return BLK_STS_RESOURCE;
+ }
+
if (use_sgl == SGL_FORCED ||
(use_sgl == SGL_SUPPORTED &&
(sgl_threshold && nvme_pci_avg_seg_size(req) >= sgl_threshold)))
@@ -1042,7 +1100,7 @@ static blk_status_t nvme_map_data(struct request *req)
return nvme_pci_setup_data_prp(req, &iter);
}
-static blk_status_t nvme_pci_setup_meta_sgls(struct request *req)
+static blk_status_t nvme_pci_setup_meta_iter(struct request *req)
{
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
unsigned int entries = req->nr_integrity_segments;
@@ -1057,6 +1115,19 @@ static blk_status_t nvme_pci_setup_meta_sgls(struct request *req)
&iod->meta_dma_state, &iter))
return iter.status;
+ switch (iter.p2pdma.map) {
+ case PCI_P2PDMA_MAP_BUS_ADDR:
+ iod->flags |= IOD_META_P2P;
+ break;
+ case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
+ iod->flags |= IOD_META_MMIO;
+ break;
+ case PCI_P2PDMA_MAP_NONE:
+ break;
+ default:
+ return BLK_STS_RESOURCE;
+ }
+
if (blk_rq_dma_map_coalesce(&iod->meta_dma_state))
entries = 1;
@@ -1072,8 +1143,12 @@ static blk_status_t nvme_pci_setup_meta_sgls(struct request *req)
* descriptor provides an explicit length, so we're relying on that
* mechanism to catch any misunderstandings between the application and
* device.
+ *
+ * P2P DMA also needs to use the blk_dma_iter method, so mptr setup
+ * leverages this routine when that happens.
*/
- if (entries == 1 && !(nvme_req(req)->flags & NVME_REQ_USERCMD)) {
+ if (!nvme_ctrl_meta_sgl_supported(&dev->ctrl) ||
+ (entries == 1 && !(nvme_req(req)->flags & NVME_REQ_USERCMD))) {
iod->cmd.common.metadata = cpu_to_le64(iter.addr);
iod->meta_total_len = iter.len;
iod->meta_dma = iter.addr;
@@ -1114,6 +1189,9 @@ static blk_status_t nvme_pci_setup_meta_mptr(struct request *req)
struct nvme_queue *nvmeq = req->mq_hctx->driver_data;
struct bio_vec bv = rq_integrity_vec(req);
+ if (is_pci_p2pdma_page(bv.bv_page))
+ return nvme_pci_setup_meta_iter(req);
+
iod->meta_dma = dma_map_bvec(nvmeq->dev->dev, &bv, rq_dma_dir(req), 0);
if (dma_mapping_error(nvmeq->dev->dev, iod->meta_dma))
return BLK_STS_IOERR;
@@ -1128,7 +1206,7 @@ static blk_status_t nvme_map_metadata(struct request *req)
if ((iod->cmd.common.flags & NVME_CMD_SGL_METABUF) &&
nvme_pci_metadata_use_sgls(req))
- return nvme_pci_setup_meta_sgls(req);
+ return nvme_pci_setup_meta_iter(req);
return nvme_pci_setup_meta_mptr(req);
}
@@ -3243,6 +3321,14 @@ static bool nvme_pci_supports_pci_p2pdma(struct nvme_ctrl *ctrl)
return dma_pci_p2pdma_supported(dev->dev);
}
+static unsigned long nvme_pci_get_virt_boundary(struct nvme_ctrl *ctrl,
+ bool is_admin)
+{
+ if (!nvme_ctrl_sgl_supported(ctrl) || is_admin)
+ return NVME_CTRL_PAGE_SIZE - 1;
+ return 0;
+}
+
static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.name = "pcie",
.module = THIS_MODULE,
@@ -3257,6 +3343,7 @@ static const struct nvme_ctrl_ops nvme_pci_ctrl_ops = {
.get_address = nvme_pci_get_address,
.print_device_info = nvme_pci_print_device_info,
.supports_pci_p2pdma = nvme_pci_supports_pci_p2pdma,
+ .get_virt_boundary = nvme_pci_get_virt_boundary,
};
static int nvme_dev_map(struct nvme_dev *dev)
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 190a4cfa8a5e..35c0822edb2d 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -2202,6 +2202,7 @@ static const struct nvme_ctrl_ops nvme_rdma_ctrl_ops = {
.delete_ctrl = nvme_rdma_delete_ctrl,
.get_address = nvmf_get_address,
.stop_ctrl = nvme_rdma_stop_ctrl,
+ .get_virt_boundary = nvme_get_virt_boundary,
};
/*
diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 9a96df1a511c..69cb04406b47 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -1834,7 +1834,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
sk_set_memalloc(queue->sock->sk);
if (nctrl->opts->mask & NVMF_OPT_HOST_TRADDR) {
- ret = kernel_bind(queue->sock, (struct sockaddr *)&ctrl->src_addr,
+ ret = kernel_bind(queue->sock, (struct sockaddr_unsized *)&ctrl->src_addr,
sizeof(ctrl->src_addr));
if (ret) {
dev_err(nctrl->device,
@@ -1872,7 +1872,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl, int qid,
dev_dbg(nctrl->device, "connecting queue %d\n",
nvme_tcp_queue_id(queue));
- ret = kernel_connect(queue->sock, (struct sockaddr *)&ctrl->addr,
+ ret = kernel_connect(queue->sock, (struct sockaddr_unsized *)&ctrl->addr,
sizeof(ctrl->addr), 0);
if (ret) {
dev_err(nctrl->device,
@@ -2865,6 +2865,7 @@ static const struct nvme_ctrl_ops nvme_tcp_ctrl_ops = {
.delete_ctrl = nvme_tcp_delete_ctrl,
.get_address = nvme_tcp_get_address,
.stop_ctrl = nvme_tcp_stop_ctrl,
+ .get_virt_boundary = nvmf_get_virt_boundary,
};
static bool
diff --git a/drivers/nvme/host/zns.c b/drivers/nvme/host/zns.c
index cce4c5b55aa9..deea2dbef5b8 100644
--- a/drivers/nvme/host/zns.c
+++ b/drivers/nvme/host/zns.c
@@ -148,8 +148,8 @@ static void *nvme_zns_alloc_report_buffer(struct nvme_ns *ns,
static int nvme_zone_parse_entry(struct nvme_ns *ns,
struct nvme_zone_descriptor *entry,
- unsigned int idx, report_zones_cb cb,
- void *data)
+ unsigned int idx,
+ struct blk_report_zones_args *args)
{
struct nvme_ns_head *head = ns->head;
struct blk_zone zone = { };
@@ -169,11 +169,11 @@ static int nvme_zone_parse_entry(struct nvme_ns *ns,
else
zone.wp = nvme_lba_to_sect(head, le64_to_cpu(entry->wp));
- return cb(&zone, idx, data);
+ return disk_report_zone(ns->disk, &zone, idx, args);
}
int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector,
- unsigned int nr_zones, report_zones_cb cb, void *data)
+ unsigned int nr_zones, struct blk_report_zones_args *args)
{
struct nvme_zone_report *report;
struct nvme_command c = { };
@@ -213,7 +213,7 @@ int nvme_ns_report_zones(struct nvme_ns *ns, sector_t sector,
for (i = 0; i < nz && zone_idx < nr_zones; i++) {
ret = nvme_zone_parse_entry(ns, &report->entries[i],
- zone_idx, cb, data);
+ zone_idx, args);
if (ret)
goto out_free;
zone_idx++;
diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c
index b340380f3892..300d5e032f6d 100644
--- a/drivers/nvme/target/auth.c
+++ b/drivers/nvme/target/auth.c
@@ -367,13 +367,14 @@ int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response,
ret = crypto_shash_update(shash, buf, 2);
if (ret)
goto out;
- memset(buf, 0, 4);
+ *buf = req->sq->sc_c;
ret = crypto_shash_update(shash, buf, 1);
if (ret)
goto out;
ret = crypto_shash_update(shash, "HostHost", 8);
if (ret)
goto out;
+ memset(buf, 0, 4);
ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn));
if (ret)
goto out;
diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c
index bf01ec414c55..5946681cb0e3 100644
--- a/drivers/nvme/target/fabrics-cmd-auth.c
+++ b/drivers/nvme/target/fabrics-cmd-auth.c
@@ -43,6 +43,7 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d)
data->auth_protocol[0].dhchap.halen,
data->auth_protocol[0].dhchap.dhlen);
req->sq->dhchap_tid = le16_to_cpu(data->t_id);
+ req->sq->sc_c = data->sc_c;
if (data->sc_c != NVME_AUTH_SECP_NOSC) {
if (!IS_ENABLED(CONFIG_NVME_TARGET_TCP_TLS))
return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH;
diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c
index f85a8441bcc6..fc8e7c9ad858 100644
--- a/drivers/nvme/target/loop.c
+++ b/drivers/nvme/target/loop.c
@@ -511,6 +511,7 @@ static const struct nvme_ctrl_ops nvme_loop_ctrl_ops = {
.submit_async_event = nvme_loop_submit_async_event,
.delete_ctrl = nvme_loop_delete_ctrl_host,
.get_address = nvmf_get_address,
+ .get_virt_boundary = nvme_get_virt_boundary,
};
static int nvme_loop_create_io_queues(struct nvme_loop_ctrl *ctrl)
diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h
index 51df72f5e89b..f3b09f4099f0 100644
--- a/drivers/nvme/target/nvmet.h
+++ b/drivers/nvme/target/nvmet.h
@@ -159,6 +159,7 @@ struct nvmet_sq {
bool authenticated;
struct delayed_work auth_expired_work;
u16 dhchap_tid;
+ u8 sc_c;
u8 dhchap_status;
u8 dhchap_step;
u8 *dhchap_c1;
diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index 470bf37e5a63..d543da09ef8e 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -2055,7 +2055,7 @@ static int nvmet_tcp_add_port(struct nvmet_port *nport)
if (so_priority > 0)
sock_set_priority(port->sock->sk, so_priority);
- ret = kernel_bind(port->sock, (struct sockaddr *)&port->addr,
+ ret = kernel_bind(port->sock, (struct sockaddr_unsized *)&port->addr,
sizeof(port->addr));
if (ret) {
pr_err("failed to bind port socket %d\n", ret);
diff --git a/drivers/nvmem/layouts.c b/drivers/nvmem/layouts.c
index f381ce1e84bd..7ebe53249035 100644
--- a/drivers/nvmem/layouts.c
+++ b/drivers/nvmem/layouts.c
@@ -51,7 +51,7 @@ static int nvmem_layout_bus_uevent(const struct device *dev,
int ret;
ret = of_device_uevent_modalias(dev, env);
- if (ret != ENODEV)
+ if (ret != -ENODEV)
return ret;
return 0;
diff --git a/drivers/nvmem/rcar-efuse.c b/drivers/nvmem/rcar-efuse.c
index f24bdb9cb5a7..d9a96a1d59c8 100644
--- a/drivers/nvmem/rcar-efuse.c
+++ b/drivers/nvmem/rcar-efuse.c
@@ -127,6 +127,7 @@ static const struct of_device_id rcar_fuse_match[] = {
{ .compatible = "renesas,r8a779h0-otp", .data = &rcar_fuse_v4m },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, rcar_fuse_match);
static struct platform_driver rcar_fuse_driver = {
.probe = rcar_fuse_probe,
diff --git a/drivers/of/address.c b/drivers/of/address.c
index f0f8f0dd191c..4034d798c55a 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -328,10 +328,6 @@ static int of_bus_default_flags_match(struct device_node *np)
static int of_bus_default_match(struct device_node *np)
{
- /*
- * Check for presence first since of_bus_n_addr_cells() will warn when
- * walking parent nodes.
- */
return of_property_present(np, "#address-cells");
}
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 7043acd971a0..0b65039ece53 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -434,6 +434,53 @@ bool of_machine_compatible_match(const char *const *compats)
}
EXPORT_SYMBOL(of_machine_compatible_match);
+/**
+ * of_machine_device_match - Test root of device tree against a of_device_id array
+ * @matches: NULL terminated array of of_device_id match structures to search in
+ *
+ * Returns true if the root node has any of the given compatible values in its
+ * compatible property.
+ */
+bool of_machine_device_match(const struct of_device_id *matches)
+{
+ struct device_node *root;
+ const struct of_device_id *match = NULL;
+
+ root = of_find_node_by_path("/");
+ if (root) {
+ match = of_match_node(matches, root);
+ of_node_put(root);
+ }
+
+ return match != NULL;
+}
+EXPORT_SYMBOL(of_machine_device_match);
+
+/**
+ * of_machine_get_match_data - Tell if root of device tree has a matching of_match structure
+ * @matches: NULL terminated array of of_device_id match structures to search in
+ *
+ * Returns data associated with matched entry or NULL
+ */
+const void *of_machine_get_match_data(const struct of_device_id *matches)
+{
+ const struct of_device_id *match;
+ struct device_node *root;
+
+ root = of_find_node_by_path("/");
+ if (!root)
+ return NULL;
+
+ match = of_match_node(matches, root);
+ of_node_put(root);
+
+ if (!match)
+ return NULL;
+
+ return match->data;
+}
+EXPORT_SYMBOL(of_machine_get_match_data);
+
static bool __of_device_is_status(const struct device_node *device,
const char * const*strings)
{
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 0edd639898a6..d378d4b4109f 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -625,6 +625,47 @@ const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
return fdt_getprop(initial_boot_params, node, name, size);
}
+const __be32 *__init of_flat_dt_get_addr_size_prop(unsigned long node,
+ const char *name,
+ int *entries)
+{
+ const __be32 *prop;
+ int len, elen = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+
+ prop = of_get_flat_dt_prop(node, name, &len);
+ if (!prop || len % elen) {
+ *entries = 0;
+ return NULL;
+ }
+
+ *entries = len / elen;
+ return prop;
+}
+
+bool __init of_flat_dt_get_addr_size(unsigned long node, const char *name,
+ u64 *addr, u64 *size)
+{
+ const __be32 *prop;
+ int entries;
+
+ prop = of_flat_dt_get_addr_size_prop(node, name, &entries);
+ if (!prop || entries != 1)
+ return false;
+
+ of_flat_dt_read_addr_size(prop, 0, addr, size);
+ return true;
+}
+
+void __init of_flat_dt_read_addr_size(const __be32 *prop, int entry_index,
+ u64 *addr, u64 *size)
+{
+ int entry_cells = dt_root_addr_cells + dt_root_size_cells;
+ prop += entry_cells * entry_index;
+
+ *addr = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ *size = dt_mem_next_cell(dt_root_size_cells, &prop);
+}
+
/**
* of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list
@@ -812,21 +853,15 @@ static void __init early_init_dt_check_for_initrd(unsigned long node)
*/
static void __init early_init_dt_check_for_elfcorehdr(unsigned long node)
{
- const __be32 *prop;
- int len;
-
if (!IS_ENABLED(CONFIG_CRASH_DUMP))
return;
pr_debug("Looking for elfcorehdr property... ");
- prop = of_get_flat_dt_prop(node, "linux,elfcorehdr", &len);
- if (!prop || (len < (dt_root_addr_cells + dt_root_size_cells)))
+ if (!of_flat_dt_get_addr_size(node, "linux,elfcorehdr",
+ &elfcorehdr_addr, &elfcorehdr_size))
return;
- elfcorehdr_addr = dt_mem_next_cell(dt_root_addr_cells, &prop);
- elfcorehdr_size = dt_mem_next_cell(dt_root_size_cells, &prop);
-
pr_debug("elfcorehdr_start=0x%llx elfcorehdr_size=0x%llx\n",
elfcorehdr_addr, elfcorehdr_size);
}
@@ -849,8 +884,9 @@ static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND;
void __init early_init_dt_check_for_usable_mem_range(void)
{
struct memblock_region rgn[MAX_USABLE_RANGES] = {0};
- const __be32 *prop, *endp;
+ const __be32 *prop;
int len, i;
+ u64 base, size;
unsigned long node = chosen_node_offset;
if ((long)node < 0)
@@ -858,14 +894,17 @@ void __init early_init_dt_check_for_usable_mem_range(void)
pr_debug("Looking for usable-memory-range property... ");
- prop = of_get_flat_dt_prop(node, "linux,usable-memory-range", &len);
- if (!prop || (len % (dt_root_addr_cells + dt_root_size_cells)))
+ prop = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory-range",
+ &len);
+ if (!prop)
return;
- endp = prop + (len / sizeof(__be32));
- for (i = 0; i < MAX_USABLE_RANGES && prop < endp; i++) {
- rgn[i].base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- rgn[i].size = dt_mem_next_cell(dt_root_size_cells, &prop);
+ len = min(len, MAX_USABLE_RANGES);
+
+ for (i = 0; i < len; i++) {
+ of_flat_dt_read_addr_size(prop, i, &base, &size);
+ rgn[i].base = base;
+ rgn[i].size = size;
pr_debug("cap_mem_regions[%d]: base=%pa, size=%pa\n",
i, &rgn[i].base, &rgn[i].size);
@@ -883,26 +922,18 @@ static void __init early_init_dt_check_kho(void)
{
unsigned long node = chosen_node_offset;
u64 fdt_start, fdt_size, scratch_start, scratch_size;
- const __be32 *p;
- int l;
if (!IS_ENABLED(CONFIG_KEXEC_HANDOVER) || (long)node < 0)
return;
- p = of_get_flat_dt_prop(node, "linux,kho-fdt", &l);
- if (l != (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32))
+ if (!of_flat_dt_get_addr_size(node, "linux,kho-fdt",
+ &fdt_start, &fdt_size))
return;
- fdt_start = dt_mem_next_cell(dt_root_addr_cells, &p);
- fdt_size = dt_mem_next_cell(dt_root_addr_cells, &p);
-
- p = of_get_flat_dt_prop(node, "linux,kho-scratch", &l);
- if (l != (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32))
+ if (!of_flat_dt_get_addr_size(node, "linux,kho-scratch",
+ &scratch_start, &scratch_size))
return;
- scratch_start = dt_mem_next_cell(dt_root_addr_cells, &p);
- scratch_size = dt_mem_next_cell(dt_root_addr_cells, &p);
-
kho_populate(fdt_start, fdt_size, scratch_start, scratch_size);
}
@@ -1002,8 +1033,8 @@ int __init early_init_dt_scan_memory(void)
fdt_for_each_subnode(node, fdt, 0) {
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
- const __be32 *reg, *endp;
- int l;
+ const __be32 *reg;
+ int i, l;
bool hotpluggable;
/* We are scanning "memory" nodes only */
@@ -1013,23 +1044,21 @@ int __init early_init_dt_scan_memory(void)
if (!of_fdt_device_is_available(fdt, node))
continue;
- reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
+ reg = of_flat_dt_get_addr_size_prop(node, "linux,usable-memory", &l);
if (reg == NULL)
- reg = of_get_flat_dt_prop(node, "reg", &l);
+ reg = of_flat_dt_get_addr_size_prop(node, "reg", &l);
if (reg == NULL)
continue;
- endp = reg + (l / sizeof(__be32));
hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);
- pr_debug("memory scan node %s, reg size %d,\n",
+ pr_debug("memory scan node %s, reg {addr,size} entries %d,\n",
fdt_get_name(fdt, node, NULL), l);
- while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
+ for (i = 0; i < l; i++) {
u64 base, size;
- base = dt_mem_next_cell(dt_root_addr_cells, &reg);
- size = dt_mem_next_cell(dt_root_size_cells, &reg);
+ of_flat_dt_read_addr_size(reg, i, &base, &size);
if (size == 0)
continue;
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 65c3c23255b7..e3816819dbfe 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -479,6 +479,26 @@ out:
}
EXPORT_SYMBOL_GPL(of_irq_get);
+const struct cpumask *of_irq_get_affinity(struct device_node *dev, int index)
+{
+ struct of_phandle_args oirq;
+ struct irq_fwspec_info info;
+ struct irq_fwspec fwspec;
+ int rc;
+
+ rc = of_irq_parse_one(dev, index, &oirq);
+ if (rc)
+ return NULL;
+
+ of_phandle_args_to_fwspec(oirq.np, oirq.args, oirq.args_count,
+ &fwspec);
+
+ if (irq_populate_fwspec_info(&fwspec, &info))
+ return NULL;
+
+ return info.affinity;
+}
+
/**
* of_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
* @dev: pointer to device tree node
@@ -593,8 +613,10 @@ void __init of_irq_init(const struct of_device_id *matches)
* are the same distance away from the root irq controller.
*/
desc->interrupt_parent = of_parse_phandle(np, "interrupts-extended", 0);
- if (!desc->interrupt_parent)
+ if (!desc->interrupt_parent && of_property_present(np, "interrupts"))
desc->interrupt_parent = of_irq_find_parent(np);
+ else if (!desc->interrupt_parent)
+ desc->interrupt_parent = of_parse_phandle(np, "interrupt-parent", 0);
if (desc->interrupt_parent == np) {
of_node_put(desc->interrupt_parent);
desc->interrupt_parent = NULL;
@@ -671,6 +693,36 @@ err:
}
}
+static int of_check_msi_parent(struct device_node *dev_node, struct device_node **msi_node)
+{
+ struct of_phandle_args msi_spec;
+ int ret;
+
+ /*
+ * An msi-parent phandle with a missing or == 0 #msi-cells
+ * property identifies a 1:1 ID translation mapping.
+ *
+ * Set the msi controller node if the firmware matches this
+ * condition.
+ */
+ ret = of_parse_phandle_with_optional_args(dev_node, "msi-parent", "#msi-cells",
+ 0, &msi_spec);
+ if (ret)
+ return ret;
+
+ if ((*msi_node && *msi_node != msi_spec.np) || msi_spec.args_count != 0)
+ ret = -EINVAL;
+
+ if (!ret) {
+ /* Return with a node reference held */
+ *msi_node = msi_spec.np;
+ return 0;
+ }
+ of_node_put(msi_spec.np);
+
+ return ret;
+}
+
/**
* of_msi_xlate - map a MSI ID and find relevant MSI controller node
* @dev: device for which the mapping is to be done.
@@ -678,7 +730,7 @@ err:
* @id_in: Device ID.
*
* Walk up the device hierarchy looking for devices with a "msi-map"
- * property. If found, apply the mapping to @id_in.
+ * or "msi-parent" property. If found, apply the mapping to @id_in.
* If @msi_np points to a non-NULL device node pointer, only entries targeting
* that node will be matched; if it points to a NULL value, it will receive the
* device node of the first matching target phandle, with a reference held.
@@ -692,14 +744,18 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in)
/*
* Walk up the device parent links looking for one with a
- * "msi-map" property.
+ * "msi-map" or an "msi-parent" property.
*/
- for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent)
+ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) {
if (!of_map_id(parent_dev->of_node, id_in, "msi-map",
"msi-map-mask", msi_np, &id_out))
break;
+ if (!of_check_msi_parent(parent_dev->of_node, msi_np))
+ break;
+ }
return id_out;
}
+EXPORT_SYMBOL_GPL(of_msi_xlate);
/**
* of_msi_map_get_device_domain - Use msi-map to find the relevant MSI domain
@@ -741,8 +797,10 @@ struct irq_domain *of_msi_get_domain(struct device *dev,
of_for_each_phandle(&it, err, np, "msi-parent", "#msi-cells", 0) {
d = irq_find_matching_host(it.node, token);
- if (d)
+ if (d) {
+ of_node_put(it.node);
return d;
+ }
}
return NULL;
diff --git a/drivers/of/of_kunit_helpers.c b/drivers/of/of_kunit_helpers.c
index 7b3ed5a382aa..f6ed1af8b62a 100644
--- a/drivers/of/of_kunit_helpers.c
+++ b/drivers/of/of_kunit_helpers.c
@@ -18,8 +18,9 @@
*/
void of_root_kunit_skip(struct kunit *test)
{
- if (IS_ENABLED(CONFIG_ARM64) && IS_ENABLED(CONFIG_ACPI) && !of_root)
- kunit_skip(test, "arm64+acpi doesn't populate a root node");
+ if ((IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_RISCV)) &&
+ IS_ENABLED(CONFIG_ACPI) && !of_root)
+ kunit_skip(test, "arm64/riscv+acpi doesn't populate a root node");
}
EXPORT_SYMBOL_GPL(of_root_kunit_skip);
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 2e9ea751ed2d..5619ec917858 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -154,27 +154,24 @@ static int __init early_init_dt_reserve_memory(phys_addr_t base,
static int __init __reserved_mem_reserve_reg(unsigned long node,
const char *uname)
{
- int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
phys_addr_t base, size;
- int len;
+ int i, len;
const __be32 *prop;
bool nomap;
- prop = of_get_flat_dt_prop(node, "reg", &len);
+ prop = of_flat_dt_get_addr_size_prop(node, "reg", &len);
if (!prop)
return -ENOENT;
- if (len && len % t_len != 0) {
- pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
- uname);
- return -EINVAL;
- }
-
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
- while (len >= t_len) {
- base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- size = dt_mem_next_cell(dt_root_size_cells, &prop);
+ for (i = 0; i < len; i++) {
+ u64 b, s;
+
+ of_flat_dt_read_addr_size(prop, i, &b, &s);
+
+ base = b;
+ size = s;
if (size && early_init_dt_reserve_memory(base, size, nomap) == 0) {
/* Architecture specific contiguous memory fixup. */
@@ -187,8 +184,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n",
uname, &base, (unsigned long)(size / SZ_1M));
}
-
- len -= t_len;
}
return 0;
}
@@ -230,12 +225,9 @@ static void __init __rmem_check_for_overlap(void);
*/
void __init fdt_scan_reserved_mem_reg_nodes(void)
{
- int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
const void *fdt = initial_boot_params;
phys_addr_t base, size;
- const __be32 *prop;
int node, child;
- int len;
if (!fdt)
return;
@@ -256,29 +248,21 @@ void __init fdt_scan_reserved_mem_reg_nodes(void)
fdt_for_each_subnode(child, fdt, node) {
const char *uname;
+ u64 b, s;
- prop = of_get_flat_dt_prop(child, "reg", &len);
- if (!prop)
- continue;
if (!of_fdt_device_is_available(fdt, child))
continue;
- uname = fdt_get_name(fdt, child, NULL);
- if (len && len % t_len != 0) {
- pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
- uname);
+ if (!of_flat_dt_get_addr_size(child, "reg", &b, &s))
continue;
- }
- if (len > t_len)
- pr_warn("%s() ignores %d regions in node '%s'\n",
- __func__, len / t_len - 1, uname);
+ base = b;
+ size = s;
- base = dt_mem_next_cell(dt_root_addr_cells, &prop);
- size = dt_mem_next_cell(dt_root_size_cells, &prop);
-
- if (size)
+ if (size) {
+ uname = fdt_get_name(fdt, child, NULL);
fdt_reserved_mem_save_node(child, uname, base, size);
+ }
}
/* check for overlapping reserved regions */
@@ -401,10 +385,9 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
*/
static int __init __reserved_mem_alloc_size(unsigned long node, const char *uname)
{
- int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
phys_addr_t start = 0, end = 0;
phys_addr_t base = 0, align = 0, size;
- int len;
+ int i, len;
const __be32 *prop;
bool nomap;
int ret;
@@ -438,19 +421,15 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
&& !nomap)
align = max_t(phys_addr_t, align, CMA_MIN_ALIGNMENT_BYTES);
- prop = of_get_flat_dt_prop(node, "alloc-ranges", &len);
+ prop = of_flat_dt_get_addr_size_prop(node, "alloc-ranges", &len);
if (prop) {
+ for (i = 0; i < len; i++) {
+ u64 b, s;
- if (len % t_len != 0) {
- pr_err("invalid alloc-ranges property in '%s', skipping node.\n",
- uname);
- return -EINVAL;
- }
+ of_flat_dt_read_addr_size(prop, i, &b, &s);
- while (len > 0) {
- start = dt_mem_next_cell(dt_root_addr_cells, &prop);
- end = start + dt_mem_next_cell(dt_root_size_cells,
- &prop);
+ start = b;
+ end = b + s;
base = 0;
ret = __reserved_mem_alloc_in_range(size, align,
@@ -461,9 +440,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node, const char *unam
(unsigned long)(size / SZ_1M));
break;
}
- len -= t_len;
}
-
} else {
ret = early_init_dt_alloc_reserved_memory_arch(size, align,
0, 0, nomap, &base);
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 255e8362f600..5b4f42230e6c 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -1190,6 +1190,9 @@ int of_overlay_remove(int *ovcs_id)
struct overlay_changeset *ovcs;
int ret, ret_apply, ret_tmp;
+ if (*ovcs_id == 0)
+ return 0;
+
if (devicetree_corrupt()) {
pr_err("suspect devicetree state, refuse to remove overlay\n");
ret = -EBUSY;
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index bba4f7daff8c..dbebb8c829bc 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -309,9 +309,9 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_is_turbo);
*/
unsigned long dev_pm_opp_get_max_clock_latency(struct device *dev)
{
- struct opp_table *opp_table __free(put_opp_table);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
- opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
return 0;
@@ -327,7 +327,6 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_clock_latency);
*/
unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
{
- struct opp_table *opp_table __free(put_opp_table);
struct dev_pm_opp *opp;
struct regulator *reg;
unsigned long latency_ns = 0;
@@ -337,7 +336,9 @@ unsigned long dev_pm_opp_get_max_volt_latency(struct device *dev)
unsigned long max;
} *uV;
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table))
return 0;
@@ -409,10 +410,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_max_transition_latency);
*/
unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev)
{
- struct opp_table *opp_table __free(put_opp_table);
unsigned long freq = 0;
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table))
return 0;
@@ -447,9 +449,9 @@ int _get_opp_count(struct opp_table *opp_table)
*/
int dev_pm_opp_get_opp_count(struct device *dev)
{
- struct opp_table *opp_table __free(put_opp_table);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
- opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_dbg(dev, "%s: OPP table not found (%ld)\n",
__func__, PTR_ERR(opp_table));
@@ -605,9 +607,9 @@ _find_key(struct device *dev, unsigned long *key, int index, bool available,
unsigned long opp_key, unsigned long key),
bool (*assert)(struct opp_table *opp_table, unsigned int index))
{
- struct opp_table *opp_table __free(put_opp_table);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
- opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_err(dev, "%s: OPP table not found (%ld)\n", __func__,
PTR_ERR(opp_table));
@@ -1410,12 +1412,13 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table,
*/
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
{
- struct opp_table *opp_table __free(put_opp_table);
struct dev_pm_opp *opp __free(put_opp) = NULL;
unsigned long freq = 0, temp_freq;
bool forced = false;
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table)) {
dev_err(dev, "%s: device's opp table doesn't exist\n", __func__);
return PTR_ERR(opp_table);
@@ -1477,9 +1480,9 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_rate);
*/
int dev_pm_opp_set_opp(struct device *dev, struct dev_pm_opp *opp)
{
- struct opp_table *opp_table __free(put_opp_table);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
- opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
return PTR_ERR(opp_table);
@@ -1794,10 +1797,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_put);
*/
void dev_pm_opp_remove(struct device *dev, unsigned long freq)
{
- struct opp_table *opp_table __free(put_opp_table);
struct dev_pm_opp *opp = NULL, *iter;
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table))
return;
@@ -1885,9 +1889,9 @@ bool _opp_remove_all_static(struct opp_table *opp_table)
*/
void dev_pm_opp_remove_all_dynamic(struct device *dev)
{
- struct opp_table *opp_table __free(put_opp_table);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
- opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
return;
@@ -2871,10 +2875,11 @@ static int _opp_set_availability(struct device *dev, unsigned long freq,
bool availability_req)
{
struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp;
- struct opp_table *opp_table __free(put_opp_table);
/* Find the opp_table */
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table)) {
dev_warn(dev, "%s: Device OPP not found (%ld)\n", __func__,
PTR_ERR(opp_table));
@@ -2932,11 +2937,12 @@ int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq,
{
struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp;
- struct opp_table *opp_table __free(put_opp_table);
int r;
/* Find the opp_table */
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table)) {
r = PTR_ERR(opp_table);
dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r);
@@ -2986,12 +2992,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_adjust_voltage);
*/
int dev_pm_opp_sync_regulators(struct device *dev)
{
- struct opp_table *opp_table __free(put_opp_table);
struct regulator *reg;
int ret, i;
/* Device may not have OPP table */
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table))
return 0;
@@ -3062,9 +3069,9 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_disable);
*/
int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb)
{
- struct opp_table *opp_table __free(put_opp_table);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
- opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
@@ -3082,9 +3089,9 @@ EXPORT_SYMBOL(dev_pm_opp_register_notifier);
int dev_pm_opp_unregister_notifier(struct device *dev,
struct notifier_block *nb)
{
- struct opp_table *opp_table __free(put_opp_table);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
- opp_table = _find_opp_table(dev);
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
@@ -3101,10 +3108,10 @@ EXPORT_SYMBOL(dev_pm_opp_unregister_notifier);
*/
void dev_pm_opp_remove_table(struct device *dev)
{
- struct opp_table *opp_table __free(put_opp_table);
-
/* Check for existing table for 'dev' */
- opp_table = _find_opp_table(dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(dev);
+
if (IS_ERR(opp_table)) {
int error = PTR_ERR(opp_table);
diff --git a/drivers/opp/cpu.c b/drivers/opp/cpu.c
index 97989d4fe336..a6da7ee3ec76 100644
--- a/drivers/opp/cpu.c
+++ b/drivers/opp/cpu.c
@@ -56,10 +56,10 @@ int dev_pm_opp_init_cpufreq_table(struct device *dev,
return -ENOMEM;
for (i = 0, rate = 0; i < max_opps; i++, rate++) {
- struct dev_pm_opp *opp __free(put_opp);
-
/* find next rate */
- opp = dev_pm_opp_find_freq_ceil(dev, &rate);
+ struct dev_pm_opp *opp __free(put_opp) =
+ dev_pm_opp_find_freq_ceil(dev, &rate);
+
if (IS_ERR(opp)) {
ret = PTR_ERR(opp);
goto out;
@@ -154,12 +154,13 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_cpumask_remove_table);
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev,
const struct cpumask *cpumask)
{
- struct opp_table *opp_table __free(put_opp_table);
struct opp_device *opp_dev;
struct device *dev;
int cpu;
- opp_table = _find_opp_table(cpu_dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(cpu_dev);
+
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
@@ -201,10 +202,11 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_set_sharing_cpus);
*/
int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask)
{
- struct opp_table *opp_table __free(put_opp_table);
struct opp_device *opp_dev;
- opp_table = _find_opp_table(cpu_dev);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_opp_table(cpu_dev);
+
if (IS_ERR(opp_table))
return PTR_ERR(opp_table);
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 505d79821584..1e0d0adb18e1 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -45,9 +45,10 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node);
struct opp_table *_managed_opp(struct device *dev, int index)
{
struct opp_table *opp_table, *managed_table = NULL;
- struct device_node *np __free(device_node);
- np = _opp_of_get_opp_desc_node(dev->of_node, index);
+ struct device_node *np __free(device_node) =
+ _opp_of_get_opp_desc_node(dev->of_node, index);
+
if (!np)
return NULL;
@@ -95,10 +96,11 @@ static struct device_node *of_parse_required_opp(struct device_node *np,
/* The caller must call dev_pm_opp_put_opp_table() after the table is used */
static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np)
{
- struct device_node *opp_table_np __free(device_node);
struct opp_table *opp_table;
- opp_table_np = of_get_parent(opp_np);
+ struct device_node *opp_table_np __free(device_node) =
+ of_get_parent(opp_np);
+
if (!opp_table_np)
return ERR_PTR(-ENODEV);
@@ -146,12 +148,13 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
struct device_node *opp_np)
{
struct opp_table **required_opp_tables;
- struct device_node *np __free(device_node);
bool lazy = false;
int count, i, size;
/* Traversing the first OPP node is all we need */
- np = of_get_next_available_child(opp_np, NULL);
+ struct device_node *np __free(device_node) =
+ of_get_next_available_child(opp_np, NULL);
+
if (!np) {
dev_warn(dev, "Empty OPP table\n");
return;
@@ -171,9 +174,9 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
opp_table->required_opp_count = count;
for (i = 0; i < count; i++) {
- struct device_node *required_np __free(device_node);
+ struct device_node *required_np __free(device_node) =
+ of_parse_required_opp(np, i);
- required_np = of_parse_required_opp(np, i);
if (!required_np) {
_opp_table_free_required_tables(opp_table);
return;
@@ -199,14 +202,15 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
void _of_init_opp_table(struct opp_table *opp_table, struct device *dev,
int index)
{
- struct device_node *np __free(device_node), *opp_np;
+ struct device_node *opp_np;
u32 val;
/*
* Only required for backward compatibility with v1 bindings, but isn't
* harmful for other cases. And so we do it unconditionally.
*/
- np = of_node_get(dev->of_node);
+ struct device_node *np __free(device_node) = of_node_get(dev->of_node);
+
if (!np)
return;
@@ -273,9 +277,9 @@ void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp)
static int _link_required_opps(struct dev_pm_opp *opp,
struct opp_table *required_table, int index)
{
- struct device_node *np __free(device_node);
+ struct device_node *np __free(device_node) =
+ of_parse_required_opp(opp->np, index);
- np = of_parse_required_opp(opp->np, index);
if (unlikely(!np))
return -ENODEV;
@@ -349,16 +353,13 @@ static void lazy_link_required_opp_table(struct opp_table *new_table)
guard(mutex)(&opp_table_lock);
list_for_each_entry_safe(opp_table, temp, &lazy_opp_tables, lazy) {
- struct device_node *opp_np __free(device_node);
bool lazy = false;
/* opp_np can't be invalid here */
- opp_np = of_get_next_available_child(opp_table->np, NULL);
+ struct device_node *opp_np __free(device_node) =
+ of_get_next_available_child(opp_table->np, NULL);
for (i = 0; i < opp_table->required_opp_count; i++) {
- struct device_node *required_np __free(device_node) = NULL;
- struct device_node *required_table_np __free(device_node) = NULL;
-
required_opp_tables = opp_table->required_opp_tables;
/* Required opp-table is already parsed */
@@ -366,8 +367,10 @@ static void lazy_link_required_opp_table(struct opp_table *new_table)
continue;
/* required_np can't be invalid here */
- required_np = of_parse_required_opp(opp_np, i);
- required_table_np = of_get_parent(required_np);
+ struct device_node *required_np __free(device_node) =
+ of_parse_required_opp(opp_np, i);
+ struct device_node *required_table_np __free(device_node) =
+ of_get_parent(required_np);
/*
* Newly added table isn't the required opp-table for
@@ -402,13 +405,12 @@ static void lazy_link_required_opp_table(struct opp_table *new_table)
static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table)
{
struct device_node *opp_np __free(device_node) = NULL;
- struct device_node *np __free(device_node) = NULL;
struct property *prop;
if (!opp_table) {
- struct device_node *np __free(device_node);
+ struct device_node *np __free(device_node) =
+ of_node_get(dev->of_node);
- np = of_node_get(dev->of_node);
if (!np)
return -ENODEV;
@@ -422,7 +424,9 @@ static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table)
return 0;
/* Checking only first OPP is sufficient */
- np = of_get_next_available_child(opp_np, NULL);
+ struct device_node *np __free(device_node) =
+ of_get_next_available_child(opp_np, NULL);
+
if (!np) {
dev_err(dev, "OPP table empty\n");
return -EINVAL;
@@ -1269,11 +1273,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_add_table);
int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
struct cpumask *cpumask)
{
- struct device_node *np __free(device_node);
int cpu;
/* Get OPP descriptor node */
- np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
+ struct device_node *np __free(device_node) =
+ dev_pm_opp_of_get_opp_desc_node(cpu_dev);
+
if (!np) {
dev_dbg(cpu_dev, "%s: Couldn't find opp node.\n", __func__);
return -ENOENT;
@@ -1286,13 +1291,12 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
return 0;
for_each_possible_cpu(cpu) {
- struct device_node *cpu_np __free(device_node) = NULL;
- struct device_node *tmp_np __free(device_node) = NULL;
-
if (cpu == cpu_dev->id)
continue;
- cpu_np = of_cpu_device_node_get(cpu);
+ struct device_node *cpu_np __free(device_node) =
+ of_cpu_device_node_get(cpu);
+
if (!cpu_np) {
dev_err(cpu_dev, "%s: failed to get cpu%d node\n",
__func__, cpu);
@@ -1300,7 +1304,9 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev,
}
/* Get OPP descriptor node */
- tmp_np = _opp_of_get_opp_desc_node(cpu_np, 0);
+ struct device_node *tmp_np __free(device_node) =
+ _opp_of_get_opp_desc_node(cpu_np, 0);
+
if (!tmp_np) {
pr_err("%pOF: Couldn't find opp node\n", cpu_np);
return -ENOENT;
@@ -1328,16 +1334,17 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus);
*/
int of_get_required_opp_performance_state(struct device_node *np, int index)
{
- struct device_node *required_np __free(device_node);
- struct opp_table *opp_table __free(put_opp_table) = NULL;
- struct dev_pm_opp *opp __free(put_opp) = NULL;
int pstate = -EINVAL;
- required_np = of_parse_required_opp(np, index);
+ struct device_node *required_np __free(device_node) =
+ of_parse_required_opp(np, index);
+
if (!required_np)
return -ENODEV;
- opp_table = _find_table_of_opp_np(required_np);
+ struct opp_table *opp_table __free(put_opp_table) =
+ _find_table_of_opp_np(required_np);
+
if (IS_ERR(opp_table)) {
pr_err("%s: Failed to find required OPP table %pOF: %ld\n",
__func__, np, PTR_ERR(opp_table));
@@ -1350,7 +1357,9 @@ int of_get_required_opp_performance_state(struct device_node *np, int index)
return -EINVAL;
}
- opp = _find_opp_of_np(opp_table, required_np);
+ struct dev_pm_opp *opp __free(put_opp) =
+ _find_opp_of_np(opp_table, required_np);
+
if (opp) {
if (opp->level == OPP_LEVEL_UNSET) {
pr_err("%s: OPP levels aren't available for %pOF\n",
@@ -1376,14 +1385,17 @@ EXPORT_SYMBOL_GPL(of_get_required_opp_performance_state);
*/
bool dev_pm_opp_of_has_required_opp(struct device *dev)
{
- struct device_node *np __free(device_node) = NULL, *opp_np __free(device_node);
int count;
- opp_np = _opp_of_get_opp_desc_node(dev->of_node, 0);
+ struct device_node *opp_np __free(device_node) =
+ _opp_of_get_opp_desc_node(dev->of_node, 0);
+
if (!opp_np)
return false;
- np = of_get_next_available_child(opp_np, NULL);
+ struct device_node *np __free(device_node) =
+ of_get_next_available_child(opp_np, NULL);
+
if (!np) {
dev_warn(dev, "Empty OPP table\n");
return false;
@@ -1425,12 +1437,14 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node);
static int __maybe_unused
_get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz)
{
- struct dev_pm_opp *opp __free(put_opp);
unsigned long opp_freq, opp_power;
/* Find the right frequency and related OPP */
opp_freq = *kHz * 1000;
- opp = dev_pm_opp_find_freq_ceil(dev, &opp_freq);
+
+ struct dev_pm_opp *opp __free(put_opp) =
+ dev_pm_opp_find_freq_ceil(dev, &opp_freq);
+
if (IS_ERR(opp))
return -EINVAL;
@@ -1465,14 +1479,13 @@ _get_dt_power(struct device *dev, unsigned long *uW, unsigned long *kHz)
int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW,
unsigned long *kHz)
{
- struct dev_pm_opp *opp __free(put_opp) = NULL;
- struct device_node *np __free(device_node);
unsigned long mV, Hz;
u32 cap;
u64 tmp;
int ret;
- np = of_node_get(dev->of_node);
+ struct device_node *np __free(device_node) = of_node_get(dev->of_node);
+
if (!np)
return -EINVAL;
@@ -1481,7 +1494,10 @@ int dev_pm_opp_calc_power(struct device *dev, unsigned long *uW,
return -EINVAL;
Hz = *kHz * 1000;
- opp = dev_pm_opp_find_freq_ceil(dev, &Hz);
+
+ struct dev_pm_opp *opp __free(put_opp) =
+ dev_pm_opp_find_freq_ceil(dev, &Hz);
+
if (IS_ERR(opp))
return -EINVAL;
@@ -1502,11 +1518,12 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_calc_power);
static bool _of_has_opp_microwatt_property(struct device *dev)
{
- struct dev_pm_opp *opp __free(put_opp);
unsigned long freq = 0;
/* Check if at least one OPP has needed property */
- opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ struct dev_pm_opp *opp __free(put_opp) =
+ dev_pm_opp_find_freq_ceil(dev, &freq);
+
if (IS_ERR(opp))
return false;
@@ -1526,12 +1543,16 @@ static bool _of_has_opp_microwatt_property(struct device *dev)
*/
int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
{
- struct device_node *np __free(device_node) = NULL;
struct em_data_callback em_cb;
int ret, nr_opp;
u32 cap;
- if (IS_ERR_OR_NULL(dev)) {
+ if (IS_ERR_OR_NULL(dev))
+ return -EINVAL;
+
+ struct device_node *np __free(device_node) = of_node_get(dev->of_node);
+
+ if (!np) {
ret = -EINVAL;
goto failed;
}
@@ -1548,12 +1569,6 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto register_em;
}
- np = of_node_get(dev->of_node);
- if (!np) {
- ret = -EINVAL;
- goto failed;
- }
-
/*
* Register an EM only if the 'dynamic-power-coefficient' property is
* set in devicetree. It is assumed the voltage values are known if that
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 67647f1880fb..f3c81c892786 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_PCI) += access.o bus.o probe.o host-bridge.o \
remove.o pci.o pci-driver.o search.o \
- rom.o setup-res.o irq.o vpd.o \
+ rebar.o rom.o setup-res.o irq.o vpd.o \
setup-bus.o vc.o mmap.o devres.o
obj-$(CONFIG_PCI) += msi/
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index f26aec6ff588..9daf13ed3714 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -357,6 +357,9 @@ void pci_bus_add_device(struct pci_dev *dev)
pci_proc_attach_device(dev);
pci_bridge_d3_update(dev);
+ /* Save config space for error recoverability */
+ pci_save_state(dev);
+
/*
* If the PCI device is associated with a pwrctrl device with a
* power supply, create a device link between the PCI device and
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index 41748d083b93..c254d2b8bf17 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -146,7 +146,7 @@ config PCIE_HISI_ERR
config PCI_IXP4XX
bool "Intel IXP4xx PCI controller"
- depends on ARM && OF
+ depends on OF
depends on ARCH_IXP4XX || COMPILE_TEST
default ARCH_IXP4XX
help
@@ -259,12 +259,20 @@ config PCIE_RCAR_EP
config PCI_RCAR_GEN2
bool "Renesas R-Car Gen2 Internal PCI controller"
- depends on ARCH_RENESAS || COMPILE_TEST
- depends on ARM
+ depends on (ARCH_RENESAS && ARM) || COMPILE_TEST
help
Say Y here if you want internal PCI support on R-Car Gen2 SoC.
- There are 3 internal PCI controllers available with a single
- built-in EHCI/OHCI host controller present on each one.
+ Each internal PCI controller contains a single built-in EHCI/OHCI
+ host controller.
+
+config PCIE_RENESAS_RZG3S_HOST
+ bool "Renesas RZ/G3S PCIe host controller"
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select MFD_SYSCON
+ select IRQ_MSI_LIB
+ help
+ Say Y here if you want PCIe host controller support on Renesas RZ/G3S
+ SoC.
config PCIE_ROCKCHIP
bool
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index 038ccbd9e3ba..229929a945c2 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar.o pcie-rcar-host.o
obj-$(CONFIG_PCIE_RCAR_EP) += pcie-rcar.o pcie-rcar-ep.o
+obj-$(CONFIG_PCIE_RENESAS_RZG3S_HOST) += pcie-rzg3s-host.o
obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig
index 02a639e55fd8..9e651d545973 100644
--- a/drivers/pci/controller/cadence/Kconfig
+++ b/drivers/pci/controller/cadence/Kconfig
@@ -19,10 +19,10 @@ config PCIE_CADENCE_EP
select PCIE_CADENCE
config PCIE_CADENCE_PLAT
- bool
+ tristate
config PCIE_CADENCE_PLAT_HOST
- bool "Cadence platform PCIe controller (host mode)"
+ tristate "Cadence platform PCIe controller (host mode)"
depends on OF
select PCIE_CADENCE_HOST
select PCIE_CADENCE_PLAT
@@ -32,7 +32,7 @@ config PCIE_CADENCE_PLAT_HOST
vendors SoCs.
config PCIE_CADENCE_PLAT_EP
- bool "Cadence platform PCIe controller (endpoint mode)"
+ tristate "Cadence platform PCIe controller (endpoint mode)"
depends on OF
depends on PCI_ENDPOINT
select PCIE_CADENCE_EP
@@ -42,6 +42,21 @@ config PCIE_CADENCE_PLAT_EP
endpoint mode. This PCIe controller may be embedded into many
different vendors SoCs.
+config PCI_SKY1_HOST
+ tristate "CIX SKY1 PCIe controller (host mode)"
+ depends on OF && (ARCH_CIX || COMPILE_TEST)
+ select PCIE_CADENCE_HOST
+ select PCI_ECAM
+ help
+ Say Y here if you want to support the CIX SKY1 PCIe platform
+ controller in host mode. CIX SKY1 PCIe controller uses Cadence
+ HPA (High Performance Architecture IP [Second generation of
+ Cadence PCIe IP])
+
+ This driver requires Cadence PCIe core infrastructure
+ (PCIE_CADENCE_HOST) and hardware platform adaptation layer
+ to function.
+
config PCIE_SG2042_HOST
tristate "Sophgo SG2042 PCIe controller (host mode)"
depends on OF && (ARCH_SOPHGO || COMPILE_TEST)
diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile
index 5e23f8539ecc..b8ec1cecfaa8 100644
--- a/drivers/pci/controller/cadence/Makefile
+++ b/drivers/pci/controller/cadence/Makefile
@@ -1,7 +1,12 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_PCIE_CADENCE) += pcie-cadence.o
-obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o
-obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o
+pcie-cadence-mod-y := pcie-cadence-hpa.o pcie-cadence.o
+pcie-cadence-host-mod-y := pcie-cadence-host-common.o pcie-cadence-host.o pcie-cadence-host-hpa.o
+pcie-cadence-ep-mod-y := pcie-cadence-ep.o
+
+obj-$(CONFIG_PCIE_CADENCE) = pcie-cadence-mod.o
+obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host-mod.o
+obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep-mod.o
obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o
obj-$(CONFIG_PCI_J721E) += pci-j721e.o
obj-$(CONFIG_PCIE_SG2042_HOST) += pcie-sg2042.o
+obj-$(CONFIG_PCI_SKY1_HOST) += pci-sky1.o
diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c
index 5bc5ab20aa6d..ecd1b0312400 100644
--- a/drivers/pci/controller/cadence/pci-j721e.c
+++ b/drivers/pci/controller/cadence/pci-j721e.c
@@ -477,9 +477,7 @@ static int j721e_pcie_probe(struct platform_device *pdev)
struct j721e_pcie *pcie;
struct cdns_pcie_rc *rc = NULL;
struct cdns_pcie_ep *ep = NULL;
- struct gpio_desc *gpiod;
void __iomem *base;
- struct clk *clk;
u32 num_lanes;
u32 mode;
int ret;
@@ -590,12 +588,12 @@ static int j721e_pcie_probe(struct platform_device *pdev)
switch (mode) {
case PCI_MODE_RC:
- gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
- if (IS_ERR(gpiod)) {
- ret = dev_err_probe(dev, PTR_ERR(gpiod), "Failed to get reset GPIO\n");
+ pcie->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(pcie->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(pcie->reset_gpio),
+ "Failed to get reset GPIO\n");
goto err_get_sync;
}
- pcie->reset_gpio = gpiod;
ret = cdns_pcie_init_phy(dev, cdns_pcie);
if (ret) {
@@ -603,19 +601,13 @@ static int j721e_pcie_probe(struct platform_device *pdev)
goto err_get_sync;
}
- clk = devm_clk_get_optional(dev, "pcie_refclk");
- if (IS_ERR(clk)) {
- ret = dev_err_probe(dev, PTR_ERR(clk), "failed to get pcie_refclk\n");
+ pcie->refclk = devm_clk_get_optional_enabled(dev, "pcie_refclk");
+ if (IS_ERR(pcie->refclk)) {
+ ret = dev_err_probe(dev, PTR_ERR(pcie->refclk),
+ "failed to enable pcie_refclk\n");
goto err_pcie_setup;
}
- ret = clk_prepare_enable(clk);
- if (ret) {
- dev_err_probe(dev, ret, "failed to enable pcie_refclk\n");
- goto err_pcie_setup;
- }
- pcie->refclk = clk;
-
/*
* Section 2.2 of the PCI Express Card Electromechanical
* Specification (Revision 5.1) mandates that the deassertion
@@ -623,16 +615,14 @@ static int j721e_pcie_probe(struct platform_device *pdev)
* This shall ensure that the power and the reference clock
* are stable.
*/
- if (gpiod) {
+ if (pcie->reset_gpio) {
msleep(PCIE_T_PVPERL_MS);
- gpiod_set_value_cansleep(gpiod, 1);
+ gpiod_set_value_cansleep(pcie->reset_gpio, 1);
}
ret = cdns_pcie_host_setup(rc);
- if (ret < 0) {
- clk_disable_unprepare(pcie->refclk);
+ if (ret < 0)
goto err_pcie_setup;
- }
break;
case PCI_MODE_EP:
@@ -679,7 +669,6 @@ static void j721e_pcie_remove(struct platform_device *pdev)
gpiod_set_value_cansleep(pcie->reset_gpio, 0);
- clk_disable_unprepare(pcie->refclk);
cdns_pcie_disable_phy(cdns_pcie);
j721e_pcie_disable_link_irq(pcie);
pm_runtime_put(dev);
diff --git a/drivers/pci/controller/cadence/pci-sky1.c b/drivers/pci/controller/cadence/pci-sky1.c
new file mode 100644
index 000000000000..d8c216dc120d
--- /dev/null
+++ b/drivers/pci/controller/cadence/pci-sky1.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe controller driver for CIX's sky1 SoCs
+ *
+ * Copyright 2025 Cix Technology Group Co., Ltd.
+ * Author: Hans Zhang <hans.zhang@cixtech.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pci.h>
+#include <linux/pci-ecam.h>
+#include <linux/pci_ids.h>
+
+#include "pcie-cadence.h"
+#include "pcie-cadence-host-common.h"
+
+#define PCI_VENDOR_ID_CIX 0x1f6c
+#define PCI_DEVICE_ID_CIX_SKY1 0x0001
+
+#define STRAP_REG(n) ((n) * 0x04)
+#define STATUS_REG(n) ((n) * 0x04)
+#define LINK_TRAINING_ENABLE BIT(0)
+#define LINK_COMPLETE BIT(0)
+
+#define SKY1_IP_REG_BANK 0x1000
+#define SKY1_IP_CFG_CTRL_REG_BANK 0x4c00
+#define SKY1_IP_AXI_MASTER_COMMON 0xf000
+#define SKY1_AXI_SLAVE 0x9000
+#define SKY1_AXI_MASTER 0xb000
+#define SKY1_AXI_HLS_REGISTERS 0xc000
+#define SKY1_AXI_RAS_REGISTERS 0xe000
+#define SKY1_DTI_REGISTERS 0xd000
+
+#define IP_REG_I_DBG_STS_0 0x420
+
+struct sky1_pcie {
+ struct cdns_pcie *cdns_pcie;
+ struct cdns_pcie_rc *cdns_pcie_rc;
+
+ struct resource *cfg_res;
+ struct resource *msg_res;
+ struct pci_config_window *cfg;
+ void __iomem *strap_base;
+ void __iomem *status_base;
+ void __iomem *reg_base;
+ void __iomem *cfg_base;
+ void __iomem *msg_base;
+};
+
+static int sky1_pcie_resource_get(struct platform_device *pdev,
+ struct sky1_pcie *pcie)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource_byname(pdev, "reg");
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base),
+ "unable to find \"reg\" registers\n");
+ pcie->reg_base = base;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+ if (!res)
+ return dev_err_probe(dev, -ENODEV, "unable to get \"cfg\" resource\n");
+ pcie->cfg_res = res;
+
+ base = devm_platform_ioremap_resource_byname(pdev, "rcsu_strap");
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base),
+ "unable to find \"rcsu_strap\" registers\n");
+ pcie->strap_base = base;
+
+ base = devm_platform_ioremap_resource_byname(pdev, "rcsu_status");
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base),
+ "unable to find \"rcsu_status\" registers\n");
+ pcie->status_base = base;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msg");
+ if (!res)
+ return dev_err_probe(dev, -ENODEV, "unable to get \"msg\" resource\n");
+ pcie->msg_res = res;
+ pcie->msg_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(pcie->msg_base)) {
+ return dev_err_probe(dev, PTR_ERR(pcie->msg_base),
+ "unable to ioremap msg resource\n");
+ }
+
+ return 0;
+}
+
+static int sky1_pcie_start_link(struct cdns_pcie *cdns_pcie)
+{
+ struct sky1_pcie *pcie = dev_get_drvdata(cdns_pcie->dev);
+ u32 val;
+
+ val = readl(pcie->strap_base + STRAP_REG(1));
+ val |= LINK_TRAINING_ENABLE;
+ writel(val, pcie->strap_base + STRAP_REG(1));
+
+ return 0;
+}
+
+static void sky1_pcie_stop_link(struct cdns_pcie *cdns_pcie)
+{
+ struct sky1_pcie *pcie = dev_get_drvdata(cdns_pcie->dev);
+ u32 val;
+
+ val = readl(pcie->strap_base + STRAP_REG(1));
+ val &= ~LINK_TRAINING_ENABLE;
+ writel(val, pcie->strap_base + STRAP_REG(1));
+}
+
+static bool sky1_pcie_link_up(struct cdns_pcie *cdns_pcie)
+{
+ u32 val;
+
+ val = cdns_pcie_hpa_readl(cdns_pcie, REG_BANK_IP_REG,
+ IP_REG_I_DBG_STS_0);
+ return val & LINK_COMPLETE;
+}
+
+static const struct cdns_pcie_ops sky1_pcie_ops = {
+ .start_link = sky1_pcie_start_link,
+ .stop_link = sky1_pcie_stop_link,
+ .link_up = sky1_pcie_link_up,
+};
+
+static int sky1_pcie_probe(struct platform_device *pdev)
+{
+ struct cdns_plat_pcie_of_data *reg_off;
+ struct device *dev = &pdev->dev;
+ struct pci_host_bridge *bridge;
+ struct cdns_pcie *cdns_pcie;
+ struct resource_entry *bus;
+ struct cdns_pcie_rc *rc;
+ struct sky1_pcie *pcie;
+ int ret;
+
+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc));
+ if (!bridge)
+ return -ENOMEM;
+
+ ret = sky1_pcie_resource_get(pdev, pcie);
+ if (ret < 0)
+ return ret;
+
+ bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
+ if (!bus)
+ return -ENODEV;
+
+ pcie->cfg = pci_ecam_create(dev, pcie->cfg_res, bus->res,
+ &pci_generic_ecam_ops);
+ if (IS_ERR(pcie->cfg))
+ return PTR_ERR(pcie->cfg);
+
+ bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops;
+ rc = pci_host_bridge_priv(bridge);
+ rc->ecam_supported = 1;
+ rc->cfg_base = pcie->cfg->win;
+ rc->cfg_res = &pcie->cfg->res;
+
+ cdns_pcie = &rc->pcie;
+ cdns_pcie->dev = dev;
+ cdns_pcie->ops = &sky1_pcie_ops;
+ cdns_pcie->reg_base = pcie->reg_base;
+ cdns_pcie->msg_res = pcie->msg_res;
+ cdns_pcie->is_rc = 1;
+
+ reg_off = devm_kzalloc(dev, sizeof(*reg_off), GFP_KERNEL);
+ if (!reg_off)
+ return -ENOMEM;
+
+ reg_off->ip_reg_bank_offset = SKY1_IP_REG_BANK;
+ reg_off->ip_cfg_ctrl_reg_offset = SKY1_IP_CFG_CTRL_REG_BANK;
+ reg_off->axi_mstr_common_offset = SKY1_IP_AXI_MASTER_COMMON;
+ reg_off->axi_slave_offset = SKY1_AXI_SLAVE;
+ reg_off->axi_master_offset = SKY1_AXI_MASTER;
+ reg_off->axi_hls_offset = SKY1_AXI_HLS_REGISTERS;
+ reg_off->axi_ras_offset = SKY1_AXI_RAS_REGISTERS;
+ reg_off->axi_dti_offset = SKY1_DTI_REGISTERS;
+ cdns_pcie->cdns_pcie_reg_offsets = reg_off;
+
+ pcie->cdns_pcie = cdns_pcie;
+ pcie->cdns_pcie_rc = rc;
+ pcie->cfg_base = rc->cfg_base;
+ bridge->sysdata = pcie->cfg;
+
+ rc->vendor_id = PCI_VENDOR_ID_CIX;
+ rc->device_id = PCI_DEVICE_ID_CIX_SKY1;
+ rc->no_inbound_map = 1;
+
+ dev_set_drvdata(dev, pcie);
+
+ ret = cdns_pcie_hpa_host_setup(rc);
+ if (ret < 0) {
+ pci_ecam_free(pcie->cfg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id of_sky1_pcie_match[] = {
+ { .compatible = "cix,sky1-pcie-host", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_sky1_pcie_match);
+
+static void sky1_pcie_remove(struct platform_device *pdev)
+{
+ struct sky1_pcie *pcie = platform_get_drvdata(pdev);
+
+ pci_ecam_free(pcie->cfg);
+}
+
+static struct platform_driver sky1_pcie_driver = {
+ .probe = sky1_pcie_probe,
+ .remove = sky1_pcie_remove,
+ .driver = {
+ .name = "sky1-pcie",
+ .of_match_table = of_sky1_pcie_match,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+module_platform_driver(sky1_pcie_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PCIe controller driver for CIX's sky1 SoCs");
+MODULE_AUTHOR("Hans Zhang <hans.zhang@cixtech.com>");
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c
new file mode 100644
index 000000000000..15415d7f35ee
--- /dev/null
+++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence PCIe host controller library.
+ *
+ * Copyright (c) 2017 Cadence
+ * Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/list_sort.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/platform_device.h>
+
+#include "pcie-cadence.h"
+#include "pcie-cadence-host-common.h"
+
+#define LINK_RETRAIN_TIMEOUT HZ
+
+u64 bar_max_size[] = {
+ [RP_BAR0] = _ULL(128 * SZ_2G),
+ [RP_BAR1] = SZ_2G,
+ [RP_NO_BAR] = _BITULL(63),
+};
+EXPORT_SYMBOL_GPL(bar_max_size);
+
+int cdns_pcie_host_training_complete(struct cdns_pcie *pcie)
+{
+ u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
+ unsigned long end_jiffies;
+ u16 lnk_stat;
+
+ /* Wait for link training to complete. Exit after timeout. */
+ end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT;
+ do {
+ lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
+ if (!(lnk_stat & PCI_EXP_LNKSTA_LT))
+ break;
+ usleep_range(0, 1000);
+ } while (time_before(jiffies, end_jiffies));
+
+ if (!(lnk_stat & PCI_EXP_LNKSTA_LT))
+ return 0;
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_host_training_complete);
+
+int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie,
+ cdns_pcie_linkup_func pcie_link_up)
+{
+ struct device *dev = pcie->dev;
+ int retries;
+
+ /* Check if the link is up or not */
+ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ if (pcie_link_up(pcie)) {
+ dev_info(dev, "Link up\n");
+ return 0;
+ }
+ usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+ }
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_host_wait_for_link);
+
+int cdns_pcie_retrain(struct cdns_pcie *pcie,
+ cdns_pcie_linkup_func pcie_link_up)
+{
+ u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
+ u16 lnk_stat, lnk_ctl;
+ int ret = 0;
+
+ /*
+ * Set retrain bit if current speed is 2.5 GB/s,
+ * but the PCIe root port support is > 2.5 GB/s.
+ */
+
+ lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off +
+ PCI_EXP_LNKCAP));
+ if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
+ return ret;
+
+ lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
+ if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
+ lnk_ctl = cdns_pcie_rp_readw(pcie,
+ pcie_cap_off + PCI_EXP_LNKCTL);
+ lnk_ctl |= PCI_EXP_LNKCTL_RL;
+ cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL,
+ lnk_ctl);
+
+ ret = cdns_pcie_host_training_complete(pcie);
+ if (ret)
+ return ret;
+
+ ret = cdns_pcie_host_wait_for_link(pcie, pcie_link_up);
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_retrain);
+
+int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc,
+ cdns_pcie_linkup_func pcie_link_up)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ int ret;
+
+ ret = cdns_pcie_host_wait_for_link(pcie, pcie_link_up);
+
+ /*
+ * Retrain link for Gen2 training defect
+ * if quirk flag is set.
+ */
+ if (!ret && rc->quirk_retrain_flag)
+ ret = cdns_pcie_retrain(pcie, pcie_link_up);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_host_start_link);
+
+enum cdns_pcie_rp_bar
+cdns_pcie_host_find_min_bar(struct cdns_pcie_rc *rc, u64 size)
+{
+ enum cdns_pcie_rp_bar bar, sel_bar;
+
+ sel_bar = RP_BAR_UNDEFINED;
+ for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) {
+ if (!rc->avail_ib_bar[bar])
+ continue;
+
+ if (size <= bar_max_size[bar]) {
+ if (sel_bar == RP_BAR_UNDEFINED) {
+ sel_bar = bar;
+ continue;
+ }
+
+ if (bar_max_size[bar] < bar_max_size[sel_bar])
+ sel_bar = bar;
+ }
+ }
+
+ return sel_bar;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_host_find_min_bar);
+
+enum cdns_pcie_rp_bar
+cdns_pcie_host_find_max_bar(struct cdns_pcie_rc *rc, u64 size)
+{
+ enum cdns_pcie_rp_bar bar, sel_bar;
+
+ sel_bar = RP_BAR_UNDEFINED;
+ for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) {
+ if (!rc->avail_ib_bar[bar])
+ continue;
+
+ if (size >= bar_max_size[bar]) {
+ if (sel_bar == RP_BAR_UNDEFINED) {
+ sel_bar = bar;
+ continue;
+ }
+
+ if (bar_max_size[bar] > bar_max_size[sel_bar])
+ sel_bar = bar;
+ }
+ }
+
+ return sel_bar;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_host_find_max_bar);
+
+int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a,
+ const struct list_head *b)
+{
+ struct resource_entry *entry1, *entry2;
+
+ entry1 = container_of(a, struct resource_entry, node);
+ entry2 = container_of(b, struct resource_entry, node);
+
+ return resource_size(entry2->res) - resource_size(entry1->res);
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_host_dma_ranges_cmp);
+
+int cdns_pcie_host_bar_config(struct cdns_pcie_rc *rc,
+ struct resource_entry *entry,
+ cdns_pcie_host_bar_ib_cfg pci_host_ib_config)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ struct device *dev = pcie->dev;
+ u64 cpu_addr, size, winsize;
+ enum cdns_pcie_rp_bar bar;
+ unsigned long flags;
+ int ret;
+
+ cpu_addr = entry->res->start;
+ flags = entry->res->flags;
+ size = resource_size(entry->res);
+
+ while (size > 0) {
+ /*
+ * Try to find a minimum BAR whose size is greater than
+ * or equal to the remaining resource_entry size. This will
+ * fail if the size of each of the available BARs is less than
+ * the remaining resource_entry size.
+ *
+ * If a minimum BAR is found, IB ATU will be configured and
+ * exited.
+ */
+ bar = cdns_pcie_host_find_min_bar(rc, size);
+ if (bar != RP_BAR_UNDEFINED) {
+ ret = pci_host_ib_config(rc, bar, cpu_addr, size, flags);
+ if (ret)
+ dev_err(dev, "IB BAR: %d config failed\n", bar);
+ return ret;
+ }
+
+ /*
+ * If the control reaches here, it would mean the remaining
+ * resource_entry size cannot be fitted in a single BAR. So we
+ * find a maximum BAR whose size is less than or equal to the
+ * remaining resource_entry size and split the resource entry
+ * so that part of resource entry is fitted inside the maximum
+ * BAR. The remaining size would be fitted during the next
+ * iteration of the loop.
+ *
+ * If a maximum BAR is not found, there is no way we can fit
+ * this resource_entry, so we error out.
+ */
+ bar = cdns_pcie_host_find_max_bar(rc, size);
+ if (bar == RP_BAR_UNDEFINED) {
+ dev_err(dev, "No free BAR to map cpu_addr %llx\n",
+ cpu_addr);
+ return -EINVAL;
+ }
+
+ winsize = bar_max_size[bar];
+ ret = pci_host_ib_config(rc, bar, cpu_addr, winsize, flags);
+ if (ret) {
+ dev_err(dev, "IB BAR: %d config failed\n", bar);
+ return ret;
+ }
+
+ size -= winsize;
+ cpu_addr += winsize;
+ }
+
+ return 0;
+}
+
+int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc,
+ cdns_pcie_host_bar_ib_cfg pci_host_ib_config)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ struct device *dev = pcie->dev;
+ struct device_node *np = dev->of_node;
+ struct pci_host_bridge *bridge;
+ struct resource_entry *entry;
+ u32 no_bar_nbits = 32;
+ int err;
+
+ bridge = pci_host_bridge_from_priv(rc);
+ if (!bridge)
+ return -ENOMEM;
+
+ if (list_empty(&bridge->dma_ranges)) {
+ of_property_read_u32(np, "cdns,no-bar-match-nbits",
+ &no_bar_nbits);
+ err = pci_host_ib_config(rc, RP_NO_BAR, 0x0, (u64)1 << no_bar_nbits, 0);
+ if (err)
+ dev_err(dev, "IB BAR: %d config failed\n", RP_NO_BAR);
+ return err;
+ }
+
+ list_sort(NULL, &bridge->dma_ranges, cdns_pcie_host_dma_ranges_cmp);
+
+ resource_list_for_each_entry(entry, &bridge->dma_ranges) {
+ err = cdns_pcie_host_bar_config(rc, entry, pci_host_ib_config);
+ if (err) {
+ dev_err(dev, "Fail to configure IB using dma-ranges\n");
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cadence PCIe host controller driver");
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.h b/drivers/pci/controller/cadence/pcie-cadence-host-common.h
new file mode 100644
index 000000000000..fe7d4202a8b6
--- /dev/null
+++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence PCIe Host controller driver.
+ *
+ * Copyright (c) 2017 Cadence
+ * Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
+ */
+#ifndef _PCIE_CADENCE_HOST_COMMON_H
+#define _PCIE_CADENCE_HOST_COMMON_H
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+
+extern u64 bar_max_size[];
+
+typedef int (*cdns_pcie_host_bar_ib_cfg)(struct cdns_pcie_rc *,
+ enum cdns_pcie_rp_bar,
+ u64,
+ u64,
+ unsigned long);
+typedef bool (*cdns_pcie_linkup_func)(struct cdns_pcie *);
+
+int cdns_pcie_host_training_complete(struct cdns_pcie *pcie);
+int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie,
+ cdns_pcie_linkup_func pcie_link_up);
+int cdns_pcie_retrain(struct cdns_pcie *pcie, cdns_pcie_linkup_func pcie_linkup_func);
+int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc,
+ cdns_pcie_linkup_func pcie_link_up);
+enum cdns_pcie_rp_bar
+cdns_pcie_host_find_min_bar(struct cdns_pcie_rc *rc, u64 size);
+enum cdns_pcie_rp_bar
+cdns_pcie_host_find_max_bar(struct cdns_pcie_rc *rc, u64 size);
+int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a,
+ const struct list_head *b);
+int cdns_pcie_host_bar_ib_config(struct cdns_pcie_rc *rc,
+ enum cdns_pcie_rp_bar bar,
+ u64 cpu_addr,
+ u64 size,
+ unsigned long flags);
+int cdns_pcie_host_bar_config(struct cdns_pcie_rc *rc,
+ struct resource_entry *entry,
+ cdns_pcie_host_bar_ib_cfg pci_host_ib_config);
+int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc,
+ cdns_pcie_host_bar_ib_cfg pci_host_ib_config);
+
+#endif /* _PCIE_CADENCE_HOST_COMMON_H */
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
new file mode 100644
index 000000000000..0f540bed58e8
--- /dev/null
+++ b/drivers/pci/controller/cadence/pcie-cadence-host-hpa.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence PCIe host controller driver.
+ *
+ * Copyright (c) 2024, Cadence Design Systems
+ * Author: Manikandan K Pillai <mpillai@cadence.com>
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/list_sort.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "pcie-cadence.h"
+#include "pcie-cadence-host-common.h"
+
+static u8 bar_aperture_mask[] = {
+ [RP_BAR0] = 0x3F,
+ [RP_BAR1] = 0x3F,
+};
+
+void __iomem *cdns_pci_hpa_map_bus(struct pci_bus *bus, unsigned int devfn,
+ int where)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
+ struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge);
+ struct cdns_pcie *pcie = &rc->pcie;
+ unsigned int busn = bus->number;
+ u32 addr0, desc0, desc1, ctrl0;
+ u32 regval;
+
+ if (pci_is_root_bus(bus)) {
+ /*
+ * Only the root port (devfn == 0) is connected to this bus.
+ * All other PCI devices are behind some bridge hence on another
+ * bus.
+ */
+ if (devfn)
+ return NULL;
+
+ return pcie->reg_base + (where & 0xfff);
+ }
+
+ /* Clear AXI link-down status */
+ regval = cdns_pcie_hpa_readl(pcie, REG_BANK_AXI_SLAVE, CDNS_PCIE_HPA_AT_LINKDOWN);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE, CDNS_PCIE_HPA_AT_LINKDOWN,
+ (regval & ~GENMASK(0, 0)));
+
+ /* Update Output registers for AXI region 0 */
+ addr0 = CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS(12) |
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) |
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_BUS(busn);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(0), addr0);
+
+ desc1 = cdns_pcie_hpa_readl(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0));
+ desc1 &= ~CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN_MASK;
+ desc1 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(0);
+ ctrl0 = CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS |
+ CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN;
+
+ if (busn == bridge->busnr + 1)
+ desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0;
+ else
+ desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1;
+
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC0(0), desc0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0), desc1);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(0), ctrl0);
+
+ return rc->cfg_base + (where & 0xfff);
+}
+
+static struct pci_ops cdns_pcie_hpa_host_ops = {
+ .map_bus = cdns_pci_hpa_map_bus,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
+};
+
+static void cdns_pcie_hpa_host_enable_ptm_response(struct cdns_pcie *pcie)
+{
+ u32 val;
+
+ val = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_LM_PTM_CTRL);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_LM_PTM_CTRL,
+ val | CDNS_PCIE_HPA_LM_PTM_CTRL_PTMRSEN);
+}
+
+static int cdns_pcie_hpa_host_bar_ib_config(struct cdns_pcie_rc *rc,
+ enum cdns_pcie_rp_bar bar,
+ u64 cpu_addr, u64 size,
+ unsigned long flags)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ u32 addr0, addr1, aperture, value;
+
+ if (!rc->avail_ib_bar[bar])
+ return -ENODEV;
+
+ rc->avail_ib_bar[bar] = false;
+
+ aperture = ilog2(size);
+ if (bar == RP_NO_BAR) {
+ addr0 = CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0_NBITS(aperture) |
+ (lower_32_bits(cpu_addr) & GENMASK(31, 8));
+ addr1 = upper_32_bits(cpu_addr);
+ } else {
+ addr0 = lower_32_bits(cpu_addr);
+ addr1 = upper_32_bits(cpu_addr);
+ }
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER,
+ CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0(bar), addr0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER,
+ CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR1(bar), addr1);
+
+ if (bar == RP_NO_BAR)
+ bar = (enum cdns_pcie_rp_bar)BAR_0;
+
+ value = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_HPA_LM_RC_BAR_CFG);
+ value &= ~(HPA_LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) |
+ HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) |
+ HPA_LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) |
+ HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) |
+ HPA_LM_RC_BAR_CFG_APERTURE(bar, bar_aperture_mask[bar] + 7));
+ if (size + cpu_addr >= SZ_4G) {
+ value |= HPA_LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar);
+ if ((flags & IORESOURCE_PREFETCH))
+ value |= HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar);
+ } else {
+ value |= HPA_LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar);
+ if ((flags & IORESOURCE_PREFETCH))
+ value |= HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar);
+ }
+
+ value |= HPA_LM_RC_BAR_CFG_APERTURE(bar, aperture);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_HPA_LM_RC_BAR_CFG, value);
+
+ return 0;
+}
+
+static int cdns_pcie_hpa_host_init_root_port(struct cdns_pcie_rc *rc)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ u32 value, ctrl;
+
+ /*
+ * Set the root port BAR configuration register:
+ * - disable both BAR0 and BAR1
+ * - enable Prefetchable Memory Base and Limit registers in type 1
+ * config space (64 bits)
+ * - enable IO Base and Limit registers in type 1 config
+ * space (32 bits)
+ */
+
+ ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_DISABLED;
+ value = CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) |
+ CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) |
+ CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE |
+ CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS |
+ CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_ENABLE |
+ CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_32BITS;
+ cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG,
+ CDNS_PCIE_HPA_LM_RC_BAR_CFG, value);
+
+ if (rc->vendor_id != 0xffff)
+ cdns_pcie_hpa_rp_writew(pcie, PCI_VENDOR_ID, rc->vendor_id);
+
+ if (rc->device_id != 0xffff)
+ cdns_pcie_hpa_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id);
+
+ cdns_pcie_hpa_rp_writeb(pcie, PCI_CLASS_REVISION, 0);
+ cdns_pcie_hpa_rp_writeb(pcie, PCI_CLASS_PROG, 0);
+ cdns_pcie_hpa_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
+
+ /* Enable bus mastering */
+ value = cdns_pcie_hpa_readl(pcie, REG_BANK_RP, PCI_COMMAND);
+ value |= (PCI_COMMAND_MEMORY | PCI_COMMAND_IO | PCI_COMMAND_MASTER);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_RP, PCI_COMMAND, value);
+ return 0;
+}
+
+static void cdns_pcie_hpa_create_region_for_cfg(struct cdns_pcie_rc *rc)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc);
+ struct resource *cfg_res = rc->cfg_res;
+ struct resource_entry *entry;
+ u64 cpu_addr = cfg_res->start;
+ u32 addr0, addr1, desc1;
+ int busnr = 0;
+
+ entry = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
+ if (entry)
+ busnr = entry->res->start;
+
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_TAG_MANAGEMENT, 0x01000000);
+ /*
+ * Reserve region 0 for PCI configure space accesses:
+ * OB_REGION_PCI_ADDR0 and OB_REGION_DESC0 are updated dynamically by
+ * cdns_pci_map_bus(), other region registers are set here once for all
+ */
+ desc1 = CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(busnr);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(0), 0x0);
+ /* Type-1 CFG */
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC0(0), 0x05000000);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0), desc1);
+
+ addr0 = CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(12) |
+ (lower_32_bits(cpu_addr) & GENMASK(31, 8));
+ addr1 = upper_32_bits(cpu_addr);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(0), addr0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(0), addr1);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(0), 0x06000000);
+}
+
+static int cdns_pcie_hpa_host_init_address_translation(struct cdns_pcie_rc *rc)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc);
+ struct resource_entry *entry;
+ int r = 0, busnr = 0;
+
+ if (!rc->ecam_supported)
+ cdns_pcie_hpa_create_region_for_cfg(rc);
+
+ entry = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
+ if (entry)
+ busnr = entry->res->start;
+
+ r++;
+ if (pcie->msg_res) {
+ cdns_pcie_hpa_set_outbound_region_for_normal_msg(pcie, busnr, 0, r,
+ pcie->msg_res->start);
+
+ r++;
+ }
+ resource_list_for_each_entry(entry, &bridge->windows) {
+ struct resource *res = entry->res;
+ u64 pci_addr = res->start - entry->offset;
+
+ if (resource_type(res) == IORESOURCE_IO)
+ cdns_pcie_hpa_set_outbound_region(pcie, busnr, 0, r,
+ true,
+ pci_pio_to_address(res->start),
+ pci_addr,
+ resource_size(res));
+ else
+ cdns_pcie_hpa_set_outbound_region(pcie, busnr, 0, r,
+ false,
+ res->start,
+ pci_addr,
+ resource_size(res));
+
+ r++;
+ }
+
+ if (rc->no_inbound_map)
+ return 0;
+ else
+ return cdns_pcie_host_map_dma_ranges(rc, cdns_pcie_hpa_host_bar_ib_config);
+}
+
+static int cdns_pcie_hpa_host_init(struct cdns_pcie_rc *rc)
+{
+ int err;
+
+ err = cdns_pcie_hpa_host_init_root_port(rc);
+ if (err)
+ return err;
+
+ return cdns_pcie_hpa_host_init_address_translation(rc);
+}
+
+int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc)
+{
+ struct cdns_pcie *pcie = &rc->pcie;
+ struct device *dev = rc->pcie.dev;
+ int ret;
+
+ if (rc->quirk_detect_quiet_flag)
+ cdns_pcie_hpa_detect_quiet_min_delay_set(&rc->pcie);
+
+ cdns_pcie_hpa_host_enable_ptm_response(pcie);
+
+ ret = cdns_pcie_start_link(pcie);
+ if (ret) {
+ dev_err(dev, "Failed to start link\n");
+ return ret;
+ }
+
+ ret = cdns_pcie_host_wait_for_link(pcie, cdns_pcie_hpa_link_up);
+ if (ret)
+ dev_dbg(dev, "PCIe link never came up\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_link_setup);
+
+int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
+{
+ struct device *dev = rc->pcie.dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pci_host_bridge *bridge;
+ enum cdns_pcie_rp_bar bar;
+ struct cdns_pcie *pcie;
+ struct resource *res;
+ int ret;
+
+ bridge = pci_host_bridge_from_priv(rc);
+ if (!bridge)
+ return -ENOMEM;
+
+ pcie = &rc->pcie;
+ pcie->is_rc = true;
+
+ if (!pcie->reg_base) {
+ pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg");
+ if (IS_ERR(pcie->reg_base)) {
+ dev_err(dev, "missing \"reg\"\n");
+ return PTR_ERR(pcie->reg_base);
+ }
+ }
+
+ /* ECAM config space is remapped at glue layer */
+ if (!rc->cfg_base) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+ rc->cfg_base = devm_pci_remap_cfg_resource(dev, res);
+ if (IS_ERR(rc->cfg_base))
+ return PTR_ERR(rc->cfg_base);
+ rc->cfg_res = res;
+ }
+
+ /* Put EROM Bar aperture to 0 */
+ cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG, CDNS_PCIE_EROM, 0x0);
+
+ ret = cdns_pcie_hpa_host_link_setup(rc);
+ if (ret)
+ return ret;
+
+ for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++)
+ rc->avail_ib_bar[bar] = true;
+
+ ret = cdns_pcie_hpa_host_init(rc);
+ if (ret)
+ return ret;
+
+ if (!bridge->ops)
+ bridge->ops = &cdns_pcie_hpa_host_ops;
+
+ return pci_host_probe(bridge);
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_hpa_host_setup);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cadence PCIe host controller driver");
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c
index fffd63d6665e..db3154c1eccb 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host.c
@@ -12,14 +12,7 @@
#include <linux/platform_device.h>
#include "pcie-cadence.h"
-
-#define LINK_RETRAIN_TIMEOUT HZ
-
-static u64 bar_max_size[] = {
- [RP_BAR0] = _ULL(128 * SZ_2G),
- [RP_BAR1] = SZ_2G,
- [RP_NO_BAR] = _BITULL(63),
-};
+#include "pcie-cadence-host-common.h"
static u8 bar_aperture_mask[] = {
[RP_BAR0] = 0x1F,
@@ -81,77 +74,6 @@ static struct pci_ops cdns_pcie_host_ops = {
.write = pci_generic_config_write,
};
-static int cdns_pcie_host_training_complete(struct cdns_pcie *pcie)
-{
- u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
- unsigned long end_jiffies;
- u16 lnk_stat;
-
- /* Wait for link training to complete. Exit after timeout. */
- end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT;
- do {
- lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
- if (!(lnk_stat & PCI_EXP_LNKSTA_LT))
- break;
- usleep_range(0, 1000);
- } while (time_before(jiffies, end_jiffies));
-
- if (!(lnk_stat & PCI_EXP_LNKSTA_LT))
- return 0;
-
- return -ETIMEDOUT;
-}
-
-static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
-{
- struct device *dev = pcie->dev;
- int retries;
-
- /* Check if the link is up or not */
- for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
- if (cdns_pcie_link_up(pcie)) {
- dev_info(dev, "Link up\n");
- return 0;
- }
- usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
- }
-
- return -ETIMEDOUT;
-}
-
-static int cdns_pcie_retrain(struct cdns_pcie *pcie)
-{
- u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
- u16 lnk_stat, lnk_ctl;
- int ret = 0;
-
- /*
- * Set retrain bit if current speed is 2.5 GB/s,
- * but the PCIe root port support is > 2.5 GB/s.
- */
-
- lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off +
- PCI_EXP_LNKCAP));
- if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
- return ret;
-
- lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
- if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
- lnk_ctl = cdns_pcie_rp_readw(pcie,
- pcie_cap_off + PCI_EXP_LNKCTL);
- lnk_ctl |= PCI_EXP_LNKCTL_RL;
- cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL,
- lnk_ctl);
-
- ret = cdns_pcie_host_training_complete(pcie);
- if (ret)
- return ret;
-
- ret = cdns_pcie_host_wait_for_link(pcie);
- }
- return ret;
-}
-
static void cdns_pcie_host_disable_ptm_response(struct cdns_pcie *pcie)
{
u32 val;
@@ -168,23 +90,6 @@ static void cdns_pcie_host_enable_ptm_response(struct cdns_pcie *pcie)
cdns_pcie_writel(pcie, CDNS_PCIE_LM_PTM_CTRL, val | CDNS_PCIE_LM_TPM_CTRL_PTMRSEN);
}
-static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc)
-{
- struct cdns_pcie *pcie = &rc->pcie;
- int ret;
-
- ret = cdns_pcie_host_wait_for_link(pcie);
-
- /*
- * Retrain link for Gen2 training defect
- * if quirk flag is set.
- */
- if (!ret && rc->quirk_retrain_flag)
- ret = cdns_pcie_retrain(pcie);
-
- return ret;
-}
-
static void cdns_pcie_host_deinit_root_port(struct cdns_pcie_rc *rc)
{
struct cdns_pcie *pcie = &rc->pcie;
@@ -245,10 +150,11 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
return 0;
}
-static int cdns_pcie_host_bar_ib_config(struct cdns_pcie_rc *rc,
- enum cdns_pcie_rp_bar bar,
- u64 cpu_addr, u64 size,
- unsigned long flags)
+int cdns_pcie_host_bar_ib_config(struct cdns_pcie_rc *rc,
+ enum cdns_pcie_rp_bar bar,
+ u64 cpu_addr,
+ u64 size,
+ unsigned long flags)
{
struct cdns_pcie *pcie = &rc->pcie;
u32 addr0, addr1, aperture, value;
@@ -290,137 +196,6 @@ static int cdns_pcie_host_bar_ib_config(struct cdns_pcie_rc *rc,
return 0;
}
-static enum cdns_pcie_rp_bar
-cdns_pcie_host_find_min_bar(struct cdns_pcie_rc *rc, u64 size)
-{
- enum cdns_pcie_rp_bar bar, sel_bar;
-
- sel_bar = RP_BAR_UNDEFINED;
- for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) {
- if (!rc->avail_ib_bar[bar])
- continue;
-
- if (size <= bar_max_size[bar]) {
- if (sel_bar == RP_BAR_UNDEFINED) {
- sel_bar = bar;
- continue;
- }
-
- if (bar_max_size[bar] < bar_max_size[sel_bar])
- sel_bar = bar;
- }
- }
-
- return sel_bar;
-}
-
-static enum cdns_pcie_rp_bar
-cdns_pcie_host_find_max_bar(struct cdns_pcie_rc *rc, u64 size)
-{
- enum cdns_pcie_rp_bar bar, sel_bar;
-
- sel_bar = RP_BAR_UNDEFINED;
- for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) {
- if (!rc->avail_ib_bar[bar])
- continue;
-
- if (size >= bar_max_size[bar]) {
- if (sel_bar == RP_BAR_UNDEFINED) {
- sel_bar = bar;
- continue;
- }
-
- if (bar_max_size[bar] > bar_max_size[sel_bar])
- sel_bar = bar;
- }
- }
-
- return sel_bar;
-}
-
-static int cdns_pcie_host_bar_config(struct cdns_pcie_rc *rc,
- struct resource_entry *entry)
-{
- u64 cpu_addr, pci_addr, size, winsize;
- struct cdns_pcie *pcie = &rc->pcie;
- struct device *dev = pcie->dev;
- enum cdns_pcie_rp_bar bar;
- unsigned long flags;
- int ret;
-
- cpu_addr = entry->res->start;
- pci_addr = entry->res->start - entry->offset;
- flags = entry->res->flags;
- size = resource_size(entry->res);
-
- if (entry->offset) {
- dev_err(dev, "PCI addr: %llx must be equal to CPU addr: %llx\n",
- pci_addr, cpu_addr);
- return -EINVAL;
- }
-
- while (size > 0) {
- /*
- * Try to find a minimum BAR whose size is greater than
- * or equal to the remaining resource_entry size. This will
- * fail if the size of each of the available BARs is less than
- * the remaining resource_entry size.
- * If a minimum BAR is found, IB ATU will be configured and
- * exited.
- */
- bar = cdns_pcie_host_find_min_bar(rc, size);
- if (bar != RP_BAR_UNDEFINED) {
- ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr,
- size, flags);
- if (ret)
- dev_err(dev, "IB BAR: %d config failed\n", bar);
- return ret;
- }
-
- /*
- * If the control reaches here, it would mean the remaining
- * resource_entry size cannot be fitted in a single BAR. So we
- * find a maximum BAR whose size is less than or equal to the
- * remaining resource_entry size and split the resource entry
- * so that part of resource entry is fitted inside the maximum
- * BAR. The remaining size would be fitted during the next
- * iteration of the loop.
- * If a maximum BAR is not found, there is no way we can fit
- * this resource_entry, so we error out.
- */
- bar = cdns_pcie_host_find_max_bar(rc, size);
- if (bar == RP_BAR_UNDEFINED) {
- dev_err(dev, "No free BAR to map cpu_addr %llx\n",
- cpu_addr);
- return -EINVAL;
- }
-
- winsize = bar_max_size[bar];
- ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr, winsize,
- flags);
- if (ret) {
- dev_err(dev, "IB BAR: %d config failed\n", bar);
- return ret;
- }
-
- size -= winsize;
- cpu_addr += winsize;
- }
-
- return 0;
-}
-
-static int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a,
- const struct list_head *b)
-{
- struct resource_entry *entry1, *entry2;
-
- entry1 = container_of(a, struct resource_entry, node);
- entry2 = container_of(b, struct resource_entry, node);
-
- return resource_size(entry2->res) - resource_size(entry1->res);
-}
-
static void cdns_pcie_host_unmap_dma_ranges(struct cdns_pcie_rc *rc)
{
struct cdns_pcie *pcie = &rc->pcie;
@@ -447,43 +222,6 @@ static void cdns_pcie_host_unmap_dma_ranges(struct cdns_pcie_rc *rc)
}
}
-static int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc)
-{
- struct cdns_pcie *pcie = &rc->pcie;
- struct device *dev = pcie->dev;
- struct device_node *np = dev->of_node;
- struct pci_host_bridge *bridge;
- struct resource_entry *entry;
- u32 no_bar_nbits = 32;
- int err;
-
- bridge = pci_host_bridge_from_priv(rc);
- if (!bridge)
- return -ENOMEM;
-
- if (list_empty(&bridge->dma_ranges)) {
- of_property_read_u32(np, "cdns,no-bar-match-nbits",
- &no_bar_nbits);
- err = cdns_pcie_host_bar_ib_config(rc, RP_NO_BAR, 0x0,
- (u64)1 << no_bar_nbits, 0);
- if (err)
- dev_err(dev, "IB BAR: %d config failed\n", RP_NO_BAR);
- return err;
- }
-
- list_sort(NULL, &bridge->dma_ranges, cdns_pcie_host_dma_ranges_cmp);
-
- resource_list_for_each_entry(entry, &bridge->dma_ranges) {
- err = cdns_pcie_host_bar_config(rc, entry);
- if (err) {
- dev_err(dev, "Fail to configure IB using dma-ranges\n");
- return err;
- }
- }
-
- return 0;
-}
-
static void cdns_pcie_host_deinit_address_translation(struct cdns_pcie_rc *rc)
{
struct cdns_pcie *pcie = &rc->pcie;
@@ -561,7 +299,7 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc)
r++;
}
- return cdns_pcie_host_map_dma_ranges(rc);
+ return cdns_pcie_host_map_dma_ranges(rc, cdns_pcie_host_bar_ib_config);
}
static void cdns_pcie_host_deinit(struct cdns_pcie_rc *rc)
@@ -607,7 +345,7 @@ int cdns_pcie_host_link_setup(struct cdns_pcie_rc *rc)
return ret;
}
- ret = cdns_pcie_host_start_link(rc);
+ ret = cdns_pcie_host_start_link(rc, cdns_pcie_link_up);
if (ret)
dev_dbg(dev, "PCIe link never came up\n");
diff --git a/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h b/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h
new file mode 100644
index 000000000000..026e131600de
--- /dev/null
+++ b/drivers/pci/controller/cadence/pcie-cadence-hpa-regs.h
@@ -0,0 +1,193 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence PCIe controller driver.
+ *
+ * Copyright (c) 2024, Cadence Design Systems
+ * Author: Manikandan K Pillai <mpillai@cadence.com>
+ */
+#ifndef _PCIE_CADENCE_HPA_REGS_H
+#define _PCIE_CADENCE_HPA_REGS_H
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/pci-epf.h>
+#include <linux/phy/phy.h>
+#include <linux/bitfield.h>
+
+/* High Performance Architecture (HPA) PCIe controller registers */
+#define CDNS_PCIE_HPA_IP_REG_BANK 0x01000000
+#define CDNS_PCIE_HPA_IP_CFG_CTRL_REG_BANK 0x01003C00
+#define CDNS_PCIE_HPA_IP_AXI_MASTER_COMMON 0x02020000
+
+/* Address Translation Registers */
+#define CDNS_PCIE_HPA_AXI_SLAVE 0x03000000
+#define CDNS_PCIE_HPA_AXI_MASTER 0x03002000
+
+/* Root Port register base address */
+#define CDNS_PCIE_HPA_RP_BASE 0x0
+
+#define CDNS_PCIE_HPA_LM_ID 0x1420
+
+/* Endpoint Function BARs */
+#define CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG(bar, fn) \
+ (((bar) < BAR_3) ? CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG0(fn) : \
+ CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG1(fn))
+#define CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG0(pfn) (0x4000 * (pfn))
+#define CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG1(pfn) ((0x4000 * (pfn)) + 0x04)
+#define CDNS_PCIE_HPA_LM_EP_VFUNC_BAR_CFG(bar, fn) \
+ (((bar) < BAR_3) ? CDNS_PCIE_HPA_LM_EP_VFUNC_BAR_CFG0(fn) : \
+ CDNS_PCIE_HPA_LM_EP_VFUNC_BAR_CFG1(fn))
+#define CDNS_PCIE_HPA_LM_EP_VFUNC_BAR_CFG0(vfn) ((0x4000 * (vfn)) + 0x08)
+#define CDNS_PCIE_HPA_LM_EP_VFUNC_BAR_CFG1(vfn) ((0x4000 * (vfn)) + 0x0C)
+#define CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(f) \
+ (GENMASK(5, 0) << (0x4 + (f) * 10))
+#define CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \
+ (((a) << (4 + ((b) * 10))) & (CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b)))
+#define CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(f) \
+ (GENMASK(3, 0) << ((f) * 10))
+#define CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \
+ (((c) << ((b) * 10)) & (CDNS_PCIE_HPA_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b)))
+
+/* Endpoint Function Configuration Register */
+#define CDNS_PCIE_HPA_LM_EP_FUNC_CFG 0x02C0
+
+/* Root Complex BAR Configuration Register */
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG 0x14
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_APERTURE_MASK GENMASK(9, 4)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_APERTURE(a) \
+ FIELD_PREP(CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_APERTURE_MASK, a)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_CTRL_MASK GENMASK(3, 0)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_CTRL(c) \
+ FIELD_PREP(CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_CTRL_MASK, c)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_APERTURE_MASK GENMASK(19, 14)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_APERTURE(a) \
+ FIELD_PREP(CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_APERTURE_MASK, a)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_CTRL_MASK GENMASK(13, 10)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_CTRL(c) \
+ FIELD_PREP(CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_CTRL_MASK, c)
+
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE BIT(20)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS BIT(21)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_ENABLE BIT(22)
+#define CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_32BITS BIT(23)
+
+/* BAR control values applicable to both Endpoint Function and Root Complex */
+#define CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_DISABLED 0x0
+#define CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_IO_32BITS 0x3
+#define CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_MEM_32BITS 0x1
+#define CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x9
+#define CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_MEM_64BITS 0x5
+#define CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0xD
+
+#define HPA_LM_RC_BAR_CFG_CTRL_DISABLED(bar) \
+ (CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_DISABLED << ((bar) * 10))
+#define HPA_LM_RC_BAR_CFG_CTRL_IO_32BITS(bar) \
+ (CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_IO_32BITS << ((bar) * 10))
+#define HPA_LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) \
+ (CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_MEM_32BITS << ((bar) * 10))
+#define HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) \
+ (CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS << ((bar) * 10))
+#define HPA_LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) \
+ (CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_MEM_64BITS << ((bar) * 10))
+#define HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) \
+ (CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS << ((bar) * 10))
+#define HPA_LM_RC_BAR_CFG_APERTURE(bar, aperture) \
+ (((aperture) - 7) << (((bar) * 10) + 4))
+
+#define CDNS_PCIE_HPA_LM_PTM_CTRL 0x0520
+#define CDNS_PCIE_HPA_LM_PTM_CTRL_PTMRSEN BIT(17)
+
+/* Root Port Registers PCI config space for root port function */
+#define CDNS_PCIE_HPA_RP_CAP_OFFSET 0xC0
+
+/* Region r Outbound AXI to PCIe Address Translation Register 0 */
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(r) (0x1010 + ((r) & 0x1F) * 0x0080)
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS_MASK GENMASK(5, 0)
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) \
+ (((nbits) - 1) & CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS_MASK)
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(23, 16)
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK, devfn)
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(31, 24)
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_BUS(bus) \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_BUS_MASK, bus)
+
+/* Region r Outbound AXI to PCIe Address Translation Register 1 */
+#define CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(r) (0x1014 + ((r) & 0x1F) * 0x0080)
+
+/* Region r Outbound PCIe Descriptor Register */
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r) (0x1008 + ((r) & 0x1F) * 0x0080)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(28, 24)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MEM \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x0)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_IO \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x2)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x4)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x5)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MASK, 0x10)
+
+/* Region r Outbound PCIe Descriptor Register */
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r) (0x100C + ((r) & 0x1F) * 0x0080)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS_MASK GENMASK(31, 24)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(bus) \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS_MASK, bus)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN_MASK GENMASK(23, 16)
+#define CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(devfn) \
+ FIELD_PREP(CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN_MASK, devfn)
+
+#define CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(r) (0x1018 + ((r) & 0x1F) * 0x0080)
+#define CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS BIT(26)
+#define CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN BIT(25)
+
+/* Region r AXI Region Base Address Register 0 */
+#define CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(r) (0x1000 + ((r) & 0x1F) * 0x0080)
+#define CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS_MASK GENMASK(5, 0)
+#define CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) \
+ (((nbits) - 1) & CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS_MASK)
+
+/* Region r AXI Region Base Address Register 1 */
+#define CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(r) (0x1004 + ((r) & 0x1F) * 0x0080)
+
+/* Root Port BAR Inbound PCIe to AXI Address Translation Register */
+#define CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0(bar) (((bar) * 0x0008))
+#define CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0_NBITS_MASK GENMASK(5, 0)
+#define CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0_NBITS(nbits) \
+ (((nbits) - 1) & CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0_NBITS_MASK)
+#define CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR1(bar) (0x04 + ((bar) * 0x0008))
+
+/* AXI link down register */
+#define CDNS_PCIE_HPA_AT_LINKDOWN 0x04
+
+/*
+ * Physical Layer Configuration Register 0
+ * This register contains the parameters required for functional setup
+ * of Physical Layer.
+ */
+#define CDNS_PCIE_HPA_PHY_LAYER_CFG0 0x0400
+#define CDNS_PCIE_HPA_DETECT_QUIET_MIN_DELAY_MASK GENMASK(26, 24)
+#define CDNS_PCIE_HPA_DETECT_QUIET_MIN_DELAY(delay) \
+ FIELD_PREP(CDNS_PCIE_HPA_DETECT_QUIET_MIN_DELAY_MASK, delay)
+#define CDNS_PCIE_HPA_LINK_TRNG_EN_MASK GENMASK(27, 27)
+
+#define CDNS_PCIE_HPA_PHY_DBG_STS_REG0 0x0420
+
+#define CDNS_PCIE_HPA_RP_MAX_IB 0x3
+#define CDNS_PCIE_HPA_MAX_OB 15
+
+/* Endpoint Function BAR Inbound PCIe to AXI Address Translation Register */
+#define CDNS_PCIE_HPA_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) (((fn) * 0x0080) + ((bar) * 0x0008))
+#define CDNS_PCIE_HPA_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) (0x4 + ((fn) * 0x0080) + ((bar) * 0x0008))
+
+/* Miscellaneous offsets definitions */
+#define CDNS_PCIE_HPA_TAG_MANAGEMENT 0x0
+#define CDNS_PCIE_HPA_SLAVE_RESP 0x100
+
+#define I_ROOT_PORT_REQ_ID_REG 0x141c
+#define LM_HAL_SBSA_CTRL 0x1170
+
+#define I_PCIE_BUS_NUMBERS (CDNS_PCIE_HPA_RP_BASE + 0x18)
+#define CDNS_PCIE_EROM 0x18
+#endif /* _PCIE_CADENCE_HPA_REGS_H */
diff --git a/drivers/pci/controller/cadence/pcie-cadence-hpa.c b/drivers/pci/controller/cadence/pcie-cadence-hpa.c
new file mode 100644
index 000000000000..f60a16938265
--- /dev/null
+++ b/drivers/pci/controller/cadence/pcie-cadence-hpa.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence PCIe controller driver.
+ *
+ * Copyright (c) 2024, Cadence Design Systems
+ * Author: Manikandan K Pillai <mpillai@cadence.com>
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include "pcie-cadence.h"
+
+bool cdns_pcie_hpa_link_up(struct cdns_pcie *pcie)
+{
+ u32 pl_reg_val;
+
+ pl_reg_val = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG, CDNS_PCIE_HPA_PHY_DBG_STS_REG0);
+ if (pl_reg_val & GENMASK(0, 0))
+ return true;
+ return false;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_hpa_link_up);
+
+void cdns_pcie_hpa_detect_quiet_min_delay_set(struct cdns_pcie *pcie)
+{
+ u32 delay = 0x3;
+ u32 ltssm_control_cap;
+
+ /* Set the LTSSM Detect Quiet state min. delay to 2ms */
+ ltssm_control_cap = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG,
+ CDNS_PCIE_HPA_PHY_LAYER_CFG0);
+ ltssm_control_cap = ((ltssm_control_cap &
+ ~CDNS_PCIE_HPA_DETECT_QUIET_MIN_DELAY_MASK) |
+ CDNS_PCIE_HPA_DETECT_QUIET_MIN_DELAY(delay));
+
+ cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG,
+ CDNS_PCIE_HPA_PHY_LAYER_CFG0, ltssm_control_cap);
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_hpa_detect_quiet_min_delay_set);
+
+void cdns_pcie_hpa_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
+ u32 r, bool is_io,
+ u64 cpu_addr, u64 pci_addr, size_t size)
+{
+ /*
+ * roundup_pow_of_two() returns an unsigned long, which is not suited
+ * for 64bit values
+ */
+ u64 sz = 1ULL << fls64(size - 1);
+ int nbits = ilog2(sz);
+ u32 addr0, addr1, desc0, desc1, ctrl0;
+
+ if (nbits < 8)
+ nbits = 8;
+
+ /* Set the PCI address */
+ addr0 = CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) |
+ (lower_32_bits(pci_addr) & GENMASK(31, 8));
+ addr1 = upper_32_bits(pci_addr);
+
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(r), addr0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(r), addr1);
+
+ /* Set the PCIe header descriptor */
+ if (is_io)
+ desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_IO;
+ else
+ desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_MEM;
+ desc1 = 0;
+ ctrl0 = 0;
+
+ /*
+ * Whether Bit [26] is set or not inside DESC0 register of the outbound
+ * PCIe descriptor, the PCI function number must be set into
+ * Bits [31:24] of DESC1 anyway.
+ *
+ * In Root Complex mode, the function number is always 0 but in Endpoint
+ * mode, the PCIe controller may support more than one function. This
+ * function number needs to be set properly into the outbound PCIe
+ * descriptor.
+ *
+ * Besides, setting Bit [26] is mandatory when in Root Complex mode:
+ * then the driver must provide the bus, resp. device, number in
+ * Bits [31:24] of DESC1, resp. Bits[23:16] of DESC0. Like the function
+ * number, the device number is always 0 in Root Complex mode.
+ *
+ * However when in Endpoint mode, we can clear Bit [26] of DESC0, hence
+ * the PCIe controller will use the captured values for the bus and
+ * device numbers.
+ */
+ if (pcie->is_rc) {
+ /* The device and function numbers are always 0 */
+ desc1 = CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(busnr) |
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(0);
+ ctrl0 = CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS |
+ CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN;
+ } else {
+ /*
+ * Use captured values for bus and device numbers but still
+ * need to set the function number
+ */
+ desc1 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(fn);
+ }
+
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r), desc0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r), desc1);
+
+ addr0 = CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) |
+ (lower_32_bits(cpu_addr) & GENMASK(31, 8));
+ addr1 = upper_32_bits(cpu_addr);
+
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(r), addr0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(r), addr1);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(r), ctrl0);
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_hpa_set_outbound_region);
+
+void cdns_pcie_hpa_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie,
+ u8 busnr, u8 fn,
+ u32 r, u64 cpu_addr)
+{
+ u32 addr0, addr1, desc0, desc1, ctrl0;
+
+ desc0 = CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG;
+ desc1 = 0;
+ ctrl0 = 0;
+
+ /* See cdns_pcie_set_outbound_region() comments above */
+ if (pcie->is_rc) {
+ desc1 = CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(busnr) |
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(0);
+ ctrl0 = CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS |
+ CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN;
+ } else {
+ desc1 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(fn);
+ }
+
+ addr0 = CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(17) |
+ (lower_32_bits(cpu_addr) & GENMASK(31, 8));
+ addr1 = upper_32_bits(cpu_addr);
+
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(r), 0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(r), 0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC0(r), desc0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_DESC1(r), desc1);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(r), addr0);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(r), addr1);
+ cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
+ CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(r), ctrl0);
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_hpa_set_outbound_region_for_normal_msg);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cadence PCIe controller driver");
diff --git a/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h b/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h
new file mode 100644
index 000000000000..857b2140c5d2
--- /dev/null
+++ b/drivers/pci/controller/cadence/pcie-cadence-lga-regs.h
@@ -0,0 +1,230 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence PCIe controller driver.
+ *
+ * Copyright (c) 2017 Cadence
+ * Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
+ */
+#ifndef _PCIE_CADENCE_LGA_REGS_H
+#define _PCIE_CADENCE_LGA_REGS_H
+
+#include <linux/bitfield.h>
+
+/* Parameters for the waiting for link up routine */
+#define LINK_WAIT_MAX_RETRIES 10
+#define LINK_WAIT_USLEEP_MIN 90000
+#define LINK_WAIT_USLEEP_MAX 100000
+
+/* Local Management Registers */
+#define CDNS_PCIE_LM_BASE 0x00100000
+
+/* Vendor ID Register */
+#define CDNS_PCIE_LM_ID (CDNS_PCIE_LM_BASE + 0x0044)
+#define CDNS_PCIE_LM_ID_VENDOR_MASK GENMASK(15, 0)
+#define CDNS_PCIE_LM_ID_VENDOR_SHIFT 0
+#define CDNS_PCIE_LM_ID_VENDOR(vid) \
+ (((vid) << CDNS_PCIE_LM_ID_VENDOR_SHIFT) & CDNS_PCIE_LM_ID_VENDOR_MASK)
+#define CDNS_PCIE_LM_ID_SUBSYS_MASK GENMASK(31, 16)
+#define CDNS_PCIE_LM_ID_SUBSYS_SHIFT 16
+#define CDNS_PCIE_LM_ID_SUBSYS(sub) \
+ (((sub) << CDNS_PCIE_LM_ID_SUBSYS_SHIFT) & CDNS_PCIE_LM_ID_SUBSYS_MASK)
+
+/* Root Port Requester ID Register */
+#define CDNS_PCIE_LM_RP_RID (CDNS_PCIE_LM_BASE + 0x0228)
+#define CDNS_PCIE_LM_RP_RID_MASK GENMASK(15, 0)
+#define CDNS_PCIE_LM_RP_RID_SHIFT 0
+#define CDNS_PCIE_LM_RP_RID_(rid) \
+ (((rid) << CDNS_PCIE_LM_RP_RID_SHIFT) & CDNS_PCIE_LM_RP_RID_MASK)
+
+/* Endpoint Bus and Device Number Register */
+#define CDNS_PCIE_LM_EP_ID (CDNS_PCIE_LM_BASE + 0x022C)
+#define CDNS_PCIE_LM_EP_ID_DEV_MASK GENMASK(4, 0)
+#define CDNS_PCIE_LM_EP_ID_DEV_SHIFT 0
+#define CDNS_PCIE_LM_EP_ID_BUS_MASK GENMASK(15, 8)
+#define CDNS_PCIE_LM_EP_ID_BUS_SHIFT 8
+
+/* Endpoint Function f BAR b Configuration Registers */
+#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG(bar, fn) \
+ (((bar) < BAR_4) ? CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn) : CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn))
+#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn) \
+ (CDNS_PCIE_LM_BASE + 0x0240 + (fn) * 0x0008)
+#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn) \
+ (CDNS_PCIE_LM_BASE + 0x0244 + (fn) * 0x0008)
+#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG(bar, fn) \
+ (((bar) < BAR_4) ? CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn) : CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn))
+#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn) \
+ (CDNS_PCIE_LM_BASE + 0x0280 + (fn) * 0x0008)
+#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn) \
+ (CDNS_PCIE_LM_BASE + 0x0284 + (fn) * 0x0008)
+#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \
+ (GENMASK(4, 0) << ((b) * 8))
+#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \
+ (((a) << ((b) * 8)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b))
+#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b) \
+ (GENMASK(7, 5) << ((b) * 8))
+#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \
+ (((c) << ((b) * 8 + 5)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b))
+
+/* Endpoint Function Configuration Register */
+#define CDNS_PCIE_LM_EP_FUNC_CFG (CDNS_PCIE_LM_BASE + 0x02C0)
+
+/* Root Complex BAR Configuration Register */
+#define CDNS_PCIE_LM_RC_BAR_CFG (CDNS_PCIE_LM_BASE + 0x0300)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK GENMASK(5, 0)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE(a) \
+ (((a) << 0) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK GENMASK(8, 6)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(c) \
+ (((c) << 6) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK GENMASK(13, 9)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE(a) \
+ (((a) << 9) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK GENMASK(16, 14)
+#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(c) \
+ (((c) << 14) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK)
+#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE BIT(17)
+#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_32BITS 0
+#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS BIT(18)
+#define CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE BIT(19)
+#define CDNS_PCIE_LM_RC_BAR_CFG_IO_16BITS 0
+#define CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS BIT(20)
+#define CDNS_PCIE_LM_RC_BAR_CFG_CHECK_ENABLE BIT(31)
+
+/* BAR control values applicable to both Endpoint Function and Root Complex */
+#define CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED 0x0
+#define CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS 0x1
+#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS 0x4
+#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5
+#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS 0x6
+#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7
+
+#define LM_RC_BAR_CFG_CTRL_DISABLED(bar) \
+ (CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED << (((bar) * 8) + 6))
+#define LM_RC_BAR_CFG_CTRL_IO_32BITS(bar) \
+ (CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS << (((bar) * 8) + 6))
+#define LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) \
+ (CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS << (((bar) * 8) + 6))
+#define LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) \
+ (CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS << (((bar) * 8) + 6))
+#define LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) \
+ (CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS << (((bar) * 8) + 6))
+#define LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) \
+ (CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS << (((bar) * 8) + 6))
+#define LM_RC_BAR_CFG_APERTURE(bar, aperture) \
+ (((aperture) - 2) << ((bar) * 8))
+
+/* PTM Control Register */
+#define CDNS_PCIE_LM_PTM_CTRL (CDNS_PCIE_LM_BASE + 0x0DA8)
+#define CDNS_PCIE_LM_TPM_CTRL_PTMRSEN BIT(17)
+
+/*
+ * Endpoint Function Registers (PCI configuration space for endpoint functions)
+ */
+#define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12))
+
+#define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90
+#define CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET 0xB0
+#define CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET 0xC0
+#define CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET 0x200
+
+/* Endpoint PF Registers */
+#define CDNS_PCIE_CORE_PF_I_ARI_CAP_AND_CTRL(fn) (0x144 + (fn) * 0x1000)
+#define CDNS_PCIE_ARI_CAP_NFN_MASK GENMASK(15, 8)
+
+/* Root Port Registers (PCI configuration space for the root port function) */
+#define CDNS_PCIE_RP_BASE 0x00200000
+#define CDNS_PCIE_RP_CAP_OFFSET 0xC0
+
+/* Address Translation Registers */
+#define CDNS_PCIE_AT_BASE 0x00400000
+
+/* Region r Outbound AXI to PCIe Address Translation Register 0 */
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r) \
+ (CDNS_PCIE_AT_BASE + 0x0000 + ((r) & 0x1F) * 0x0020)
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK GENMASK(5, 0)
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) \
+ (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK)
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12)
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \
+ (((devfn) << 12) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK)
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20)
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \
+ (((bus) << 20) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK)
+
+/* Region r Outbound AXI to PCIe Address Translation Register 1 */
+#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r) \
+ (CDNS_PCIE_AT_BASE + 0x0004 + ((r) & 0x1F) * 0x0020)
+
+/* Region r Outbound PCIe Descriptor Register 0 */
+#define CDNS_PCIE_AT_OB_REGION_DESC0(r) \
+ (CDNS_PCIE_AT_BASE + 0x0008 + ((r) & 0x1F) * 0x0020)
+#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(3, 0)
+#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM 0x2
+#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO 0x6
+#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 0xA
+#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 0xB
+#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG 0xC
+#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_VENDOR_MSG 0xD
+/* Bit 23 MUST be set in RC mode. */
+#define CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23)
+#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24)
+#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \
+ (((devfn) << 24) & CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK)
+
+/* Region r Outbound PCIe Descriptor Register 1 */
+#define CDNS_PCIE_AT_OB_REGION_DESC1(r) \
+ (CDNS_PCIE_AT_BASE + 0x000C + ((r) & 0x1F) * 0x0020)
+#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK GENMASK(7, 0)
+#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus) \
+ ((bus) & CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK)
+
+/* Region r AXI Region Base Address Register 0 */
+#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r) \
+ (CDNS_PCIE_AT_BASE + 0x0018 + ((r) & 0x1F) * 0x0020)
+#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK GENMASK(5, 0)
+#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) \
+ (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK)
+
+/* Region r AXI Region Base Address Register 1 */
+#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r) \
+ (CDNS_PCIE_AT_BASE + 0x001C + ((r) & 0x1F) * 0x0020)
+
+/* Root Port BAR Inbound PCIe to AXI Address Translation Register */
+#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0(bar) \
+ (CDNS_PCIE_AT_BASE + 0x0800 + (bar) * 0x0008)
+#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK GENMASK(5, 0)
+#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(nbits) \
+ (((nbits) - 1) & CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK)
+#define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \
+ (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008)
+
+/* AXI link down register */
+#define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824)
+
+/* LTSSM Capabilities register */
+#define CDNS_PCIE_LTSSM_CONTROL_CAP (CDNS_PCIE_LM_BASE + 0x0054)
+#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK GENMASK(2, 1)
+#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT 1
+#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY(delay) \
+ (((delay) << CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT) & \
+ CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK)
+
+#define CDNS_PCIE_RP_MAX_IB 0x3
+#define CDNS_PCIE_MAX_OB 32
+
+/* Endpoint Function BAR Inbound PCIe to AXI Address Translation Register */
+#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \
+ (CDNS_PCIE_AT_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008)
+#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \
+ (CDNS_PCIE_AT_BASE + 0x0844 + (fn) * 0x0040 + (bar) * 0x0008)
+
+/* Normal/Vendor specific message access: offset inside some outbound region */
+#define CDNS_PCIE_NORMAL_MSG_ROUTING_MASK GENMASK(7, 5)
+#define CDNS_PCIE_NORMAL_MSG_ROUTING(route) \
+ (((route) << 5) & CDNS_PCIE_NORMAL_MSG_ROUTING_MASK)
+#define CDNS_PCIE_NORMAL_MSG_CODE_MASK GENMASK(15, 8)
+#define CDNS_PCIE_NORMAL_MSG_CODE(code) \
+ (((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK)
+#define CDNS_PCIE_MSG_NO_DATA BIT(16)
+
+#endif /* _PCIE_CADENCE_LGA_REGS_H */
diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c
index 0456845dabb9..b067a3296dd3 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-plat.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c
@@ -22,10 +22,6 @@ struct cdns_plat_pcie {
struct cdns_pcie *pcie;
};
-struct cdns_plat_pcie_of_data {
- bool is_rc;
-};
-
static const struct of_device_id cdns_plat_pcie_of_match[];
static u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr)
@@ -177,4 +173,7 @@ static struct platform_driver cdns_plat_pcie_driver = {
.probe = cdns_plat_pcie_probe,
.shutdown = cdns_plat_pcie_shutdown,
};
-builtin_platform_driver(cdns_plat_pcie_driver);
+module_platform_driver(cdns_plat_pcie_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cadence PCIe controller platform driver");
diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c
index bd683d0fecb2..e6f1a4ac0fb7 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.c
+++ b/drivers/pci/controller/cadence/pcie-cadence.c
@@ -23,6 +23,17 @@ u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap)
}
EXPORT_SYMBOL_GPL(cdns_pcie_find_ext_capability);
+bool cdns_pcie_linkup(struct cdns_pcie *pcie)
+{
+ u32 pl_reg_val;
+
+ pl_reg_val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_BASE);
+ if (pl_reg_val & GENMASK(0, 0))
+ return true;
+ return false;
+}
+EXPORT_SYMBOL_GPL(cdns_pcie_linkup);
+
void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie)
{
u32 delay = 0x3;
@@ -293,6 +304,7 @@ const struct dev_pm_ops cdns_pcie_pm_ops = {
NOIRQ_SYSTEM_SLEEP_PM_OPS(cdns_pcie_suspend_noirq,
cdns_pcie_resume_noirq)
};
+EXPORT_SYMBOL_GPL(cdns_pcie_pm_ops);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cadence PCIe controller driver");
diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
index e2a853d2c0ab..443033c607d7 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.h
+++ b/drivers/pci/controller/cadence/pcie-cadence.h
@@ -7,211 +7,12 @@
#define _PCIE_CADENCE_H
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci-epf.h>
#include <linux/phy/phy.h>
-
-/* Parameters for the waiting for link up routine */
-#define LINK_WAIT_MAX_RETRIES 10
-#define LINK_WAIT_USLEEP_MIN 90000
-#define LINK_WAIT_USLEEP_MAX 100000
-
-/*
- * Local Management Registers
- */
-#define CDNS_PCIE_LM_BASE 0x00100000
-
-/* Vendor ID Register */
-#define CDNS_PCIE_LM_ID (CDNS_PCIE_LM_BASE + 0x0044)
-#define CDNS_PCIE_LM_ID_VENDOR_MASK GENMASK(15, 0)
-#define CDNS_PCIE_LM_ID_VENDOR_SHIFT 0
-#define CDNS_PCIE_LM_ID_VENDOR(vid) \
- (((vid) << CDNS_PCIE_LM_ID_VENDOR_SHIFT) & CDNS_PCIE_LM_ID_VENDOR_MASK)
-#define CDNS_PCIE_LM_ID_SUBSYS_MASK GENMASK(31, 16)
-#define CDNS_PCIE_LM_ID_SUBSYS_SHIFT 16
-#define CDNS_PCIE_LM_ID_SUBSYS(sub) \
- (((sub) << CDNS_PCIE_LM_ID_SUBSYS_SHIFT) & CDNS_PCIE_LM_ID_SUBSYS_MASK)
-
-/* Root Port Requester ID Register */
-#define CDNS_PCIE_LM_RP_RID (CDNS_PCIE_LM_BASE + 0x0228)
-#define CDNS_PCIE_LM_RP_RID_MASK GENMASK(15, 0)
-#define CDNS_PCIE_LM_RP_RID_SHIFT 0
-#define CDNS_PCIE_LM_RP_RID_(rid) \
- (((rid) << CDNS_PCIE_LM_RP_RID_SHIFT) & CDNS_PCIE_LM_RP_RID_MASK)
-
-/* Endpoint Bus and Device Number Register */
-#define CDNS_PCIE_LM_EP_ID (CDNS_PCIE_LM_BASE + 0x022c)
-#define CDNS_PCIE_LM_EP_ID_DEV_MASK GENMASK(4, 0)
-#define CDNS_PCIE_LM_EP_ID_DEV_SHIFT 0
-#define CDNS_PCIE_LM_EP_ID_BUS_MASK GENMASK(15, 8)
-#define CDNS_PCIE_LM_EP_ID_BUS_SHIFT 8
-
-/* Endpoint Function f BAR b Configuration Registers */
-#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG(bar, fn) \
- (((bar) < BAR_4) ? CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn) : CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn))
-#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG0(fn) \
- (CDNS_PCIE_LM_BASE + 0x0240 + (fn) * 0x0008)
-#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG1(fn) \
- (CDNS_PCIE_LM_BASE + 0x0244 + (fn) * 0x0008)
-#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG(bar, fn) \
- (((bar) < BAR_4) ? CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn) : CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn))
-#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG0(fn) \
- (CDNS_PCIE_LM_BASE + 0x0280 + (fn) * 0x0008)
-#define CDNS_PCIE_LM_EP_VFUNC_BAR_CFG1(fn) \
- (CDNS_PCIE_LM_BASE + 0x0284 + (fn) * 0x0008)
-#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b) \
- (GENMASK(4, 0) << ((b) * 8))
-#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE(b, a) \
- (((a) << ((b) * 8)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_APERTURE_MASK(b))
-#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b) \
- (GENMASK(7, 5) << ((b) * 8))
-#define CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL(b, c) \
- (((c) << ((b) * 8 + 5)) & CDNS_PCIE_LM_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b))
-
-/* Endpoint Function Configuration Register */
-#define CDNS_PCIE_LM_EP_FUNC_CFG (CDNS_PCIE_LM_BASE + 0x02c0)
-
-/* Root Complex BAR Configuration Register */
-#define CDNS_PCIE_LM_RC_BAR_CFG (CDNS_PCIE_LM_BASE + 0x0300)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK GENMASK(5, 0)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE(a) \
- (((a) << 0) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_APERTURE_MASK)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK GENMASK(8, 6)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(c) \
- (((c) << 6) & CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL_MASK)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK GENMASK(13, 9)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE(a) \
- (((a) << 9) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_APERTURE_MASK)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK GENMASK(16, 14)
-#define CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(c) \
- (((c) << 14) & CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL_MASK)
-#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE BIT(17)
-#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_32BITS 0
-#define CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS BIT(18)
-#define CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE BIT(19)
-#define CDNS_PCIE_LM_RC_BAR_CFG_IO_16BITS 0
-#define CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS BIT(20)
-#define CDNS_PCIE_LM_RC_BAR_CFG_CHECK_ENABLE BIT(31)
-
-/* BAR control values applicable to both Endpoint Function and Root Complex */
-#define CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED 0x0
-#define CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS 0x1
-#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS 0x4
-#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5
-#define CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS 0x6
-#define CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7
-
-#define LM_RC_BAR_CFG_CTRL_DISABLED(bar) \
- (CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED << (((bar) * 8) + 6))
-#define LM_RC_BAR_CFG_CTRL_IO_32BITS(bar) \
- (CDNS_PCIE_LM_BAR_CFG_CTRL_IO_32BITS << (((bar) * 8) + 6))
-#define LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) \
- (CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_32BITS << (((bar) * 8) + 6))
-#define LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) \
- (CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_32BITS << (((bar) * 8) + 6))
-#define LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) \
- (CDNS_PCIE_LM_BAR_CFG_CTRL_MEM_64BITS << (((bar) * 8) + 6))
-#define LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) \
- (CDNS_PCIE_LM_BAR_CFG_CTRL_PREFETCH_MEM_64BITS << (((bar) * 8) + 6))
-#define LM_RC_BAR_CFG_APERTURE(bar, aperture) \
- (((aperture) - 2) << ((bar) * 8))
-
-/* PTM Control Register */
-#define CDNS_PCIE_LM_PTM_CTRL (CDNS_PCIE_LM_BASE + 0x0da8)
-#define CDNS_PCIE_LM_TPM_CTRL_PTMRSEN BIT(17)
-
-/*
- * Endpoint Function Registers (PCI configuration space for endpoint functions)
- */
-#define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12))
-
-/*
- * Endpoint PF Registers
- */
-#define CDNS_PCIE_CORE_PF_I_ARI_CAP_AND_CTRL(fn) (0x144 + (fn) * 0x1000)
-#define CDNS_PCIE_ARI_CAP_NFN_MASK GENMASK(15, 8)
-
-/*
- * Root Port Registers (PCI configuration space for the root port function)
- */
-#define CDNS_PCIE_RP_BASE 0x00200000
-#define CDNS_PCIE_RP_CAP_OFFSET 0xc0
-
-/*
- * Address Translation Registers
- */
-#define CDNS_PCIE_AT_BASE 0x00400000
-
-/* Region r Outbound AXI to PCIe Address Translation Register 0 */
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r) \
- (CDNS_PCIE_AT_BASE + 0x0000 + ((r) & 0x1f) * 0x0020)
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK GENMASK(5, 0)
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) \
- (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS_MASK)
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK GENMASK(19, 12)
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) \
- (((devfn) << 12) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN_MASK)
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK GENMASK(27, 20)
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(bus) \
- (((bus) << 20) & CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS_MASK)
-
-/* Region r Outbound AXI to PCIe Address Translation Register 1 */
-#define CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r) \
- (CDNS_PCIE_AT_BASE + 0x0004 + ((r) & 0x1f) * 0x0020)
-
-/* Region r Outbound PCIe Descriptor Register 0 */
-#define CDNS_PCIE_AT_OB_REGION_DESC0(r) \
- (CDNS_PCIE_AT_BASE + 0x0008 + ((r) & 0x1f) * 0x0020)
-#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MASK GENMASK(3, 0)
-#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM 0x2
-#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO 0x6
-#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0 0xa
-#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1 0xb
-#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG 0xc
-#define CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_VENDOR_MSG 0xd
-/* Bit 23 MUST be set in RC mode. */
-#define CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID BIT(23)
-#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK GENMASK(31, 24)
-#define CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(devfn) \
- (((devfn) << 24) & CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN_MASK)
-
-/* Region r Outbound PCIe Descriptor Register 1 */
-#define CDNS_PCIE_AT_OB_REGION_DESC1(r) \
- (CDNS_PCIE_AT_BASE + 0x000c + ((r) & 0x1f) * 0x0020)
-#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK GENMASK(7, 0)
-#define CDNS_PCIE_AT_OB_REGION_DESC1_BUS(bus) \
- ((bus) & CDNS_PCIE_AT_OB_REGION_DESC1_BUS_MASK)
-
-/* Region r AXI Region Base Address Register 0 */
-#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r) \
- (CDNS_PCIE_AT_BASE + 0x0018 + ((r) & 0x1f) * 0x0020)
-#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK GENMASK(5, 0)
-#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) \
- (((nbits) - 1) & CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS_MASK)
-
-/* Region r AXI Region Base Address Register 1 */
-#define CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r) \
- (CDNS_PCIE_AT_BASE + 0x001c + ((r) & 0x1f) * 0x0020)
-
-/* Root Port BAR Inbound PCIe to AXI Address Translation Register */
-#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0(bar) \
- (CDNS_PCIE_AT_BASE + 0x0800 + (bar) * 0x0008)
-#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK GENMASK(5, 0)
-#define CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(nbits) \
- (((nbits) - 1) & CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS_MASK)
-#define CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar) \
- (CDNS_PCIE_AT_BASE + 0x0804 + (bar) * 0x0008)
-
-/* AXI link down register */
-#define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824)
-
-/* LTSSM Capabilities register */
-#define CDNS_PCIE_LTSSM_CONTROL_CAP (CDNS_PCIE_LM_BASE + 0x0054)
-#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK GENMASK(2, 1)
-#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT 1
-#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY(delay) \
- (((delay) << CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT) & \
- CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK)
+#include "pcie-cadence-lga-regs.h"
+#include "pcie-cadence-hpa-regs.h"
enum cdns_pcie_rp_bar {
RP_BAR_UNDEFINED = -1,
@@ -220,42 +21,63 @@ enum cdns_pcie_rp_bar {
RP_NO_BAR
};
-#define CDNS_PCIE_RP_MAX_IB 0x3
-#define CDNS_PCIE_MAX_OB 32
-
struct cdns_pcie_rp_ib_bar {
u64 size;
bool free;
};
-/* Endpoint Function BAR Inbound PCIe to AXI Address Translation Register */
-#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR0(fn, bar) \
- (CDNS_PCIE_AT_BASE + 0x0840 + (fn) * 0x0040 + (bar) * 0x0008)
-#define CDNS_PCIE_AT_IB_EP_FUNC_BAR_ADDR1(fn, bar) \
- (CDNS_PCIE_AT_BASE + 0x0844 + (fn) * 0x0040 + (bar) * 0x0008)
-
-/* Normal/Vendor specific message access: offset inside some outbound region */
-#define CDNS_PCIE_NORMAL_MSG_ROUTING_MASK GENMASK(7, 5)
-#define CDNS_PCIE_NORMAL_MSG_ROUTING(route) \
- (((route) << 5) & CDNS_PCIE_NORMAL_MSG_ROUTING_MASK)
-#define CDNS_PCIE_NORMAL_MSG_CODE_MASK GENMASK(15, 8)
-#define CDNS_PCIE_NORMAL_MSG_CODE(code) \
- (((code) << 8) & CDNS_PCIE_NORMAL_MSG_CODE_MASK)
-#define CDNS_PCIE_MSG_DATA BIT(16)
-
struct cdns_pcie;
+struct cdns_pcie_rc;
+
+enum cdns_pcie_reg_bank {
+ REG_BANK_RP,
+ REG_BANK_IP_REG,
+ REG_BANK_IP_CFG_CTRL_REG,
+ REG_BANK_AXI_MASTER_COMMON,
+ REG_BANK_AXI_MASTER,
+ REG_BANK_AXI_SLAVE,
+ REG_BANK_AXI_HLS,
+ REG_BANK_AXI_RAS,
+ REG_BANK_AXI_DTI,
+ REG_BANKS_MAX,
+};
struct cdns_pcie_ops {
- int (*start_link)(struct cdns_pcie *pcie);
- void (*stop_link)(struct cdns_pcie *pcie);
- bool (*link_up)(struct cdns_pcie *pcie);
+ int (*start_link)(struct cdns_pcie *pcie);
+ void (*stop_link)(struct cdns_pcie *pcie);
+ bool (*link_up)(struct cdns_pcie *pcie);
u64 (*cpu_addr_fixup)(struct cdns_pcie *pcie, u64 cpu_addr);
};
/**
+ * struct cdns_plat_pcie_of_data - Register bank offset for a platform
+ * @is_rc: controller is a RC
+ * @ip_reg_bank_offset: ip register bank start offset
+ * @ip_cfg_ctrl_reg_offset: ip config control register start offset
+ * @axi_mstr_common_offset: AXI master common register start offset
+ * @axi_slave_offset: AXI slave start offset
+ * @axi_master_offset: AXI master start offset
+ * @axi_hls_offset: AXI HLS offset start
+ * @axi_ras_offset: AXI RAS offset
+ * @axi_dti_offset: AXI DTI offset
+ */
+struct cdns_plat_pcie_of_data {
+ u32 is_rc:1;
+ u32 ip_reg_bank_offset;
+ u32 ip_cfg_ctrl_reg_offset;
+ u32 axi_mstr_common_offset;
+ u32 axi_slave_offset;
+ u32 axi_master_offset;
+ u32 axi_hls_offset;
+ u32 axi_ras_offset;
+ u32 axi_dti_offset;
+};
+
+/**
* struct cdns_pcie - private data for Cadence PCIe controller drivers
* @reg_base: IO mapped register base
* @mem_res: start/end offsets in the physical system memory to map PCI accesses
+ * @msg_res: Region for send message to map PCI accesses
* @dev: PCIe controller
* @is_rc: tell whether the PCIe controller mode is Root Complex or Endpoint.
* @phy_count: number of supported PHY devices
@@ -263,16 +85,19 @@ struct cdns_pcie_ops {
* @link: list of pointers to corresponding device link representations
* @ops: Platform-specific ops to control various inputs from Cadence PCIe
* wrapper
+ * @cdns_pcie_reg_offsets: Register bank offsets for different SoC
*/
struct cdns_pcie {
- void __iomem *reg_base;
- struct resource *mem_res;
- struct device *dev;
- bool is_rc;
- int phy_count;
- struct phy **phy;
- struct device_link **link;
- const struct cdns_pcie_ops *ops;
+ void __iomem *reg_base;
+ struct resource *mem_res;
+ struct resource *msg_res;
+ struct device *dev;
+ bool is_rc;
+ int phy_count;
+ struct phy **phy;
+ struct device_link **link;
+ const struct cdns_pcie_ops *ops;
+ const struct cdns_plat_pcie_of_data *cdns_pcie_reg_offsets;
};
/**
@@ -288,6 +113,8 @@ struct cdns_pcie {
* available
* @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2
* @quirk_detect_quiet_flag: LTSSM Detect Quiet min delay set as quirk
+ * @ecam_supported: Whether the ECAM is supported
+ * @no_inbound_map: Whether inbound mapping is supported
*/
struct cdns_pcie_rc {
struct cdns_pcie pcie;
@@ -298,6 +125,8 @@ struct cdns_pcie_rc {
bool avail_ib_bar[CDNS_PCIE_RP_MAX_IB];
unsigned int quirk_retrain_flag:1;
unsigned int quirk_detect_quiet_flag:1;
+ unsigned int ecam_supported:1;
+ unsigned int no_inbound_map:1;
};
/**
@@ -350,6 +179,43 @@ struct cdns_pcie_ep {
unsigned int quirk_disable_flr:1;
};
+static inline u32 cdns_reg_bank_to_off(struct cdns_pcie *pcie, enum cdns_pcie_reg_bank bank)
+{
+ u32 offset = 0x0;
+
+ switch (bank) {
+ case REG_BANK_RP:
+ offset = 0;
+ break;
+ case REG_BANK_IP_REG:
+ offset = pcie->cdns_pcie_reg_offsets->ip_reg_bank_offset;
+ break;
+ case REG_BANK_IP_CFG_CTRL_REG:
+ offset = pcie->cdns_pcie_reg_offsets->ip_cfg_ctrl_reg_offset;
+ break;
+ case REG_BANK_AXI_MASTER_COMMON:
+ offset = pcie->cdns_pcie_reg_offsets->axi_mstr_common_offset;
+ break;
+ case REG_BANK_AXI_MASTER:
+ offset = pcie->cdns_pcie_reg_offsets->axi_master_offset;
+ break;
+ case REG_BANK_AXI_SLAVE:
+ offset = pcie->cdns_pcie_reg_offsets->axi_slave_offset;
+ break;
+ case REG_BANK_AXI_HLS:
+ offset = pcie->cdns_pcie_reg_offsets->axi_hls_offset;
+ break;
+ case REG_BANK_AXI_RAS:
+ offset = pcie->cdns_pcie_reg_offsets->axi_ras_offset;
+ break;
+ case REG_BANK_AXI_DTI:
+ offset = pcie->cdns_pcie_reg_offsets->axi_dti_offset;
+ break;
+ default:
+ break;
+ }
+ return offset;
+}
/* Register access */
static inline void cdns_pcie_writel(struct cdns_pcie *pcie, u32 reg, u32 value)
@@ -362,6 +228,27 @@ static inline u32 cdns_pcie_readl(struct cdns_pcie *pcie, u32 reg)
return readl(pcie->reg_base + reg);
}
+static inline void cdns_pcie_hpa_writel(struct cdns_pcie *pcie,
+ enum cdns_pcie_reg_bank bank,
+ u32 reg,
+ u32 value)
+{
+ u32 offset = cdns_reg_bank_to_off(pcie, bank);
+
+ reg += offset;
+ writel(value, pcie->reg_base + reg);
+}
+
+static inline u32 cdns_pcie_hpa_readl(struct cdns_pcie *pcie,
+ enum cdns_pcie_reg_bank bank,
+ u32 reg)
+{
+ u32 offset = cdns_reg_bank_to_off(pcie, bank);
+
+ reg += offset;
+ return readl(pcie->reg_base + reg);
+}
+
static inline u16 cdns_pcie_readw(struct cdns_pcie *pcie, u32 reg)
{
return readw(pcie->reg_base + reg);
@@ -457,6 +344,29 @@ static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg)
return cdns_pcie_read_sz(addr, 0x2);
}
+static inline void cdns_pcie_hpa_rp_writeb(struct cdns_pcie *pcie,
+ u32 reg, u8 value)
+{
+ void __iomem *addr = pcie->reg_base + CDNS_PCIE_HPA_RP_BASE + reg;
+
+ cdns_pcie_write_sz(addr, 0x1, value);
+}
+
+static inline void cdns_pcie_hpa_rp_writew(struct cdns_pcie *pcie,
+ u32 reg, u16 value)
+{
+ void __iomem *addr = pcie->reg_base + CDNS_PCIE_HPA_RP_BASE + reg;
+
+ cdns_pcie_write_sz(addr, 0x2, value);
+}
+
+static inline u16 cdns_pcie_hpa_rp_readw(struct cdns_pcie *pcie, u32 reg)
+{
+ void __iomem *addr = pcie->reg_base + CDNS_PCIE_HPA_RP_BASE + reg;
+
+ return cdns_pcie_read_sz(addr, 0x2);
+}
+
/* Endpoint Function register access */
static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn,
u32 reg, u8 value)
@@ -521,6 +431,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc);
void cdns_pcie_host_disable(struct cdns_pcie_rc *rc);
void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
int where);
+int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc);
#else
static inline int cdns_pcie_host_link_setup(struct cdns_pcie_rc *rc)
{
@@ -537,6 +448,11 @@ static inline int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
return 0;
}
+static inline int cdns_pcie_hpa_host_setup(struct cdns_pcie_rc *rc)
+{
+ return 0;
+}
+
static inline void cdns_pcie_host_disable(struct cdns_pcie_rc *rc)
{
}
@@ -551,6 +467,7 @@ static inline void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int d
#if IS_ENABLED(CONFIG_PCIE_CADENCE_EP)
int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep);
void cdns_pcie_ep_disable(struct cdns_pcie_ep *ep);
+int cdns_pcie_hpa_ep_setup(struct cdns_pcie_ep *ep);
#else
static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
{
@@ -560,10 +477,17 @@ static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
static inline void cdns_pcie_ep_disable(struct cdns_pcie_ep *ep)
{
}
+
+static inline int cdns_pcie_hpa_ep_setup(struct cdns_pcie_ep *ep)
+{
+ return 0;
+}
+
#endif
-u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap);
-u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap);
+u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap);
+u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap);
+bool cdns_pcie_linkup(struct cdns_pcie *pcie);
void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie);
@@ -577,8 +501,23 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie,
void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r);
void cdns_pcie_disable_phy(struct cdns_pcie *pcie);
-int cdns_pcie_enable_phy(struct cdns_pcie *pcie);
-int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie);
+int cdns_pcie_enable_phy(struct cdns_pcie *pcie);
+int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie);
+void cdns_pcie_hpa_detect_quiet_min_delay_set(struct cdns_pcie *pcie);
+void cdns_pcie_hpa_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
+ u32 r, bool is_io,
+ u64 cpu_addr, u64 pci_addr, size_t size);
+void cdns_pcie_hpa_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie,
+ u8 busnr, u8 fn,
+ u32 r, u64 cpu_addr);
+int cdns_pcie_hpa_host_link_setup(struct cdns_pcie_rc *rc);
+void __iomem *cdns_pci_hpa_map_bus(struct pci_bus *bus, unsigned int devfn,
+ int where);
+int cdns_pcie_hpa_host_start_link(struct cdns_pcie_rc *rc);
+int cdns_pcie_hpa_start_link(struct cdns_pcie *pcie);
+void cdns_pcie_hpa_stop_link(struct cdns_pcie *pcie);
+bool cdns_pcie_hpa_link_up(struct cdns_pcie *pcie);
+
extern const struct dev_pm_ops cdns_pcie_pm_ops;
#endif /* _PCIE_CADENCE_H */
diff --git a/drivers/pci/controller/cadence/pcie-sg2042.c b/drivers/pci/controller/cadence/pcie-sg2042.c
index a077b28d4894..0c50c74d03ee 100644
--- a/drivers/pci/controller/cadence/pcie-sg2042.c
+++ b/drivers/pci/controller/cadence/pcie-sg2042.c
@@ -74,15 +74,12 @@ static int sg2042_pcie_probe(struct platform_device *pdev)
static void sg2042_pcie_remove(struct platform_device *pdev)
{
struct cdns_pcie *pcie = platform_get_drvdata(pdev);
- struct device *dev = &pdev->dev;
struct cdns_pcie_rc *rc;
rc = container_of(pcie, struct cdns_pcie_rc, pcie);
cdns_pcie_host_disable(rc);
cdns_pcie_disable_phy(pcie);
-
- pm_runtime_disable(dev);
}
static int sg2042_pcie_suspend_noirq(struct device *dev)
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 349d4657393c..519b59422b47 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -256,6 +256,16 @@ config PCIE_TEGRA194_EP
in order to enable device-specific features PCIE_TEGRA194_EP must be
selected. This uses the DesignWare core.
+config PCIE_NXP_S32G
+ bool "NXP S32G PCIe controller (host mode)"
+ depends on ARCH_S32 || COMPILE_TEST
+ select PCIE_DW_HOST
+ help
+ Enable support for the PCIe controller in NXP S32G based boards to
+ work in Host mode. The controller is based on DesignWare IP and
+ can work either as RC or EP. In order to enable host-specific
+ features PCIE_NXP_S32G must be selected.
+
config PCIE_DW_PLAT
bool
@@ -416,6 +426,19 @@ config PCIE_SOPHGO_DW
Say Y here if you want PCIe host controller support on
Sophgo SoCs.
+config PCIE_SPACEMIT_K1
+ tristate "SpacemiT K1 PCIe controller (host mode)"
+ depends on ARCH_SPACEMIT || COMPILE_TEST
+ depends on HAS_IOMEM
+ select PCIE_DW_HOST
+ select PCI_PWRCTRL_SLOT
+ default ARCH_SPACEMIT
+ help
+ Enables support for the DesignWare based PCIe controller in
+ the SpacemiT K1 SoC operating in host mode. Three controllers
+ are available on the K1 SoC; the first of these shares a PHY
+ with a USB 3.0 host controller (one or the other can be used).
+
config PCIE_SPEAR13XX
bool "STMicroelectronics SPEAr PCIe controller"
depends on ARCH_SPEAR13XX || COMPILE_TEST
@@ -482,15 +505,21 @@ config PCI_DRA7XX_EP
to enable device-specific features PCI_DRA7XX_EP must be selected.
This uses the DesignWare core.
+# ARM32 platforms use hook_fault_code() and cannot support loadable module.
config PCI_KEYSTONE
bool
+# On non-ARM32 platforms, loadable module can be supported.
+config PCI_KEYSTONE_TRISTATE
+ tristate
+
config PCI_KEYSTONE_HOST
- bool "TI Keystone PCIe controller (host mode)"
+ tristate "TI Keystone PCIe controller (host mode)"
depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
depends on PCI_MSI
select PCIE_DW_HOST
- select PCI_KEYSTONE
+ select PCI_KEYSTONE if ARM
+ select PCI_KEYSTONE_TRISTATE if !ARM
help
Enables support for the PCIe controller in the Keystone SoC to
work in host mode. The PCI controller on Keystone is based on
@@ -498,11 +527,12 @@ config PCI_KEYSTONE_HOST
DesignWare core functions to implement the driver.
config PCI_KEYSTONE_EP
- bool "TI Keystone PCIe controller (endpoint mode)"
+ tristate "TI Keystone PCIe controller (endpoint mode)"
depends on ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
depends on PCI_ENDPOINT
select PCIE_DW_EP
- select PCI_KEYSTONE
+ select PCI_KEYSTONE if ARM
+ select PCI_KEYSTONE_TRISTATE if !ARM
help
Enables support for the PCIe controller in the Keystone SoC to
work in endpoint mode. The PCI controller on Keystone is based
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index 7ae28f3b0fb3..67ba59c02038 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -10,8 +10,12 @@ obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCIE_FU740) += pcie-fu740.o
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
+obj-$(CONFIG_PCIE_NXP_S32G) += pcie-nxp-s32g.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+# ARM32 platforms use hook_fault_code() and cannot support loadable module.
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
+# On non-ARM32 platforms, loadable module can be supported.
+obj-$(CONFIG_PCI_KEYSTONE_TRISTATE) += pci-keystone.o
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
obj-$(CONFIG_PCI_LAYERSCAPE_EP) += pci-layerscape-ep.o
obj-$(CONFIG_PCIE_QCOM_COMMON) += pcie-qcom-common.o
@@ -31,6 +35,7 @@ obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o
+obj-$(CONFIG_PCIE_SPACEMIT_K1) += pcie-spacemit-k1.o
obj-$(CONFIG_PCIE_STM32_HOST) += pcie-stm32.o
obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.o
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index eb00aa380722..f86d9111f863 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -17,6 +17,7 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/mfd/syscon.h>
+#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -777,29 +778,7 @@ err:
return ret;
}
-#ifdef CONFIG_ARM
-/*
- * When a PCI device does not exist during config cycles, keystone host
- * gets a bus error instead of returning 0xffffffff (PCI_ERROR_RESPONSE).
- * This handler always returns 0 for this kind of fault.
- */
-static int ks_pcie_fault(unsigned long addr, unsigned int fsr,
- struct pt_regs *regs)
-{
- unsigned long instr = *(unsigned long *) instruction_pointer(regs);
-
- if ((instr & 0x0e100090) == 0x00100090) {
- int reg = (instr >> 12) & 15;
-
- regs->uregs[reg] = -1;
- regs->ARM_pc += 4;
- }
-
- return 0;
-}
-#endif
-
-static int __init ks_pcie_init_id(struct keystone_pcie *ks_pcie)
+static int ks_pcie_init_id(struct keystone_pcie *ks_pcie)
{
int ret;
unsigned int id;
@@ -831,7 +810,7 @@ static int __init ks_pcie_init_id(struct keystone_pcie *ks_pcie)
return 0;
}
-static int __init ks_pcie_host_init(struct dw_pcie_rp *pp)
+static int ks_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct keystone_pcie *ks_pcie = to_keystone_pcie(pci);
@@ -861,15 +840,6 @@ static int __init ks_pcie_host_init(struct dw_pcie_rp *pp)
if (ret < 0)
return ret;
-#ifdef CONFIG_ARM
- /*
- * PCIe access errors that result into OCP errors are caught by ARM as
- * "External aborts"
- */
- hook_fault_code(17, ks_pcie_fault, SIGBUS, 0,
- "Asynchronous external abort");
-#endif
-
return 0;
}
@@ -1134,6 +1104,7 @@ static const struct of_device_id ks_pcie_of_match[] = {
},
{ },
};
+MODULE_DEVICE_TABLE(of, ks_pcie_of_match);
static int ks_pcie_probe(struct platform_device *pdev)
{
@@ -1337,6 +1308,8 @@ static int ks_pcie_probe(struct platform_device *pdev)
break;
default:
dev_err(dev, "INVALID device type %d\n", mode);
+ ret = -EINVAL;
+ goto err_get_sync;
}
ks_pcie_enable_error_irq(ks_pcie);
@@ -1379,4 +1352,45 @@ static struct platform_driver ks_pcie_driver = {
.of_match_table = ks_pcie_of_match,
},
};
+
+#ifdef CONFIG_ARM
+/*
+ * When a PCI device does not exist during config cycles, keystone host
+ * gets a bus error instead of returning 0xffffffff (PCI_ERROR_RESPONSE).
+ * This handler always returns 0 for this kind of fault.
+ */
+static int ks_pcie_fault(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
+{
+ unsigned long instr = *(unsigned long *)instruction_pointer(regs);
+
+ if ((instr & 0x0e100090) == 0x00100090) {
+ int reg = (instr >> 12) & 15;
+
+ regs->uregs[reg] = -1;
+ regs->ARM_pc += 4;
+ }
+
+ return 0;
+}
+
+static int __init ks_pcie_init(void)
+{
+ /*
+ * PCIe access errors that result into OCP errors are caught by ARM as
+ * "External aborts"
+ */
+ if (of_find_matching_node(NULL, ks_pcie_of_match))
+ hook_fault_code(17, ks_pcie_fault, SIGBUS, 0,
+ "Asynchronous external abort");
+
+ return platform_driver_register(&ks_pcie_driver);
+}
+device_initcall(ks_pcie_init);
+#else
builtin_platform_driver(ks_pcie_driver);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("PCIe controller driver for Texas Instruments Keystone SoCs");
+MODULE_AUTHOR("Murali Karicheri <m-karicheri2@ti.com>");
diff --git a/drivers/pci/controller/dwc/pci-meson.c b/drivers/pci/controller/dwc/pci-meson.c
index 787469d1b396..54b6a4196f17 100644
--- a/drivers/pci/controller/dwc/pci-meson.c
+++ b/drivers/pci/controller/dwc/pci-meson.c
@@ -108,10 +108,22 @@ static int meson_pcie_get_mems(struct platform_device *pdev,
struct meson_pcie *mp)
{
struct dw_pcie *pci = &mp->pci;
+ struct resource *res;
- pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "elbi");
- if (IS_ERR(pci->dbi_base))
- return PTR_ERR(pci->dbi_base);
+ /*
+ * For the broken DTs that supply 'dbi' as 'elbi', parse the 'elbi'
+ * region and assign it to both 'pci->elbi_base' and 'pci->dbi_space' so
+ * that the DWC core can skip parsing both regions.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
+ if (res) {
+ pci->elbi_base = devm_pci_remap_cfg_resource(pci->dev, res);
+ if (IS_ERR(pci->elbi_base))
+ return PTR_ERR(pci->elbi_base);
+
+ pci->dbi_base = pci->elbi_base;
+ pci->dbi_phys_addr = res->start;
+ }
mp->cfg_base = devm_platform_ioremap_resource_byname(pdev, "cfg");
if (IS_ERR(mp->cfg_base))
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7f2112c2fb21..19571ac2b961 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -797,6 +797,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0;
}
+EXPORT_SYMBOL_GPL(dw_pcie_ep_raise_msix_irq);
/**
* dw_pcie_ep_cleanup - Cleanup DWC EP resources after fundamental reset
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 20c9333bcb1c..372207c33a85 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -23,6 +23,7 @@
#include "pcie-designware.h"
static struct pci_ops dw_pcie_ops;
+static struct pci_ops dw_pcie_ecam_ops;
static struct pci_ops dw_child_pcie_ops;
#define DW_PCIE_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
@@ -232,6 +233,7 @@ int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
return 0;
}
+EXPORT_SYMBOL_GPL(dw_pcie_allocate_domains);
void dw_pcie_free_msi(struct dw_pcie_rp *pp)
{
@@ -471,9 +473,6 @@ static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *re
if (IS_ERR(pp->cfg))
return PTR_ERR(pp->cfg);
- pci->dbi_base = pp->cfg->win;
- pci->dbi_phys_addr = res->start;
-
return 0;
}
@@ -529,7 +528,7 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
if (ret)
return ret;
- pp->bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops;
+ pp->bridge->ops = &dw_pcie_ecam_ops;
pp->bridge->sysdata = pp->cfg;
pp->cfg->priv = pp;
} else {
@@ -842,10 +841,41 @@ void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn,
}
EXPORT_SYMBOL_GPL(dw_pcie_own_conf_map_bus);
+static void __iomem *dw_pcie_ecam_conf_map_bus(struct pci_bus *bus, unsigned int devfn, int where)
+{
+ struct pci_config_window *cfg = bus->sysdata;
+ struct dw_pcie_rp *pp = cfg->priv;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ unsigned int busn = bus->number;
+
+ if (busn > 0)
+ return pci_ecam_map_bus(bus, devfn, where);
+
+ if (PCI_SLOT(devfn) > 0)
+ return NULL;
+
+ return pci->dbi_base + where;
+}
+
+static int dw_pcie_op_assert_perst(struct pci_bus *bus, bool assert)
+{
+ struct dw_pcie_rp *pp = bus->sysdata;
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+
+ return dw_pcie_assert_perst(pci, assert);
+}
+
static struct pci_ops dw_pcie_ops = {
.map_bus = dw_pcie_own_conf_map_bus,
.read = pci_generic_config_read,
.write = pci_generic_config_write,
+ .assert_perst = dw_pcie_op_assert_perst,
+};
+
+static struct pci_ops dw_pcie_ecam_ops = {
+ .map_bus = dw_pcie_ecam_conf_map_bus,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
};
static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
@@ -1060,6 +1090,8 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+ dw_pcie_hide_unsupported_l1ss(pci);
+
dw_pcie_config_presets(pp);
/*
* If the platform provides its own child bus config accesses, it means
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index c644216995f6..75fc8b767fcc 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -168,11 +168,13 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
}
/* ELBI is an optional resource */
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
- if (res) {
- pci->elbi_base = devm_ioremap_resource(pci->dev, res);
- if (IS_ERR(pci->elbi_base))
- return PTR_ERR(pci->elbi_base);
+ if (!pci->elbi_base) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
+ if (res) {
+ pci->elbi_base = devm_ioremap_resource(pci->dev, res);
+ if (IS_ERR(pci->elbi_base))
+ return PTR_ERR(pci->elbi_base);
+ }
}
/* LLDD is supposed to manually switch the clocks and resets state */
@@ -1081,6 +1083,30 @@ void dw_pcie_edma_remove(struct dw_pcie *pci)
dw_edma_remove(&pci->edma);
}
+void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
+{
+ u16 l1ss;
+ u32 l1ss_cap;
+
+ if (pci->l1ss_support)
+ return;
+
+ l1ss = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
+ if (!l1ss)
+ return;
+
+ /*
+ * Unless the driver claims "l1ss_support", don't advertise L1 PM
+ * Substates because they require CLKREQ# and possibly other
+ * device-specific configuration.
+ */
+ l1ss_cap = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
+ l1ss_cap &= ~(PCI_L1SS_CAP_PCIPM_L1_1 | PCI_L1SS_CAP_ASPM_L1_1 |
+ PCI_L1SS_CAP_PCIPM_L1_2 | PCI_L1SS_CAP_ASPM_L1_2 |
+ PCI_L1SS_CAP_L1_PM_SS);
+ dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
+}
+
void dw_pcie_setup(struct dw_pcie *pci)
{
u32 val;
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index e995f692a1ec..31685951a080 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -97,7 +97,7 @@
#define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0)
#define PCIE_PORT_DEBUG0 0x728
-#define PORT_LOGIC_LTSSM_STATE_MASK 0x1f
+#define PORT_LOGIC_LTSSM_STATE_MASK 0x3f
#define PORT_LOGIC_LTSSM_STATE_L0 0x11
#define PCIE_PORT_DEBUG1 0x72C
#define PCIE_PORT_DEBUG1_LINK_UP BIT(4)
@@ -121,6 +121,7 @@
#define GEN3_RELATED_OFF 0x890
#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0)
+#define GEN3_RELATED_OFF_EQ_PHASE_2_3 BIT(9)
#define GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS BIT(13)
#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16)
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24
@@ -138,6 +139,13 @@
#define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10)
#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14)
+#define COHERENCY_CONTROL_1_OFF 0x8E0
+#define CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK GENMASK(31, 2)
+#define CFG_MEMTYPE_VALUE BIT(0)
+
+#define COHERENCY_CONTROL_2_OFF 0x8E4
+#define COHERENCY_CONTROL_3_OFF 0x8E8
+
#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0
#define PORT_MLTI_UPCFG_SUPPORT BIT(7)
@@ -485,6 +493,7 @@ struct dw_pcie_ops {
enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie);
int (*start_link)(struct dw_pcie *pcie);
void (*stop_link)(struct dw_pcie *pcie);
+ int (*assert_perst)(struct dw_pcie *pcie, bool assert);
};
struct debugfs_info {
@@ -516,6 +525,7 @@ struct dw_pcie {
int max_link_speed;
u8 n_fts[2];
struct dw_edma_chip edma;
+ bool l1ss_support; /* L1 PM Substates support */
struct clk_bulk_data app_clks[DW_PCIE_NUM_APP_CLKS];
struct clk_bulk_data core_clks[DW_PCIE_NUM_CORE_CLKS];
struct reset_control_bulk_data app_rsts[DW_PCIE_NUM_APP_RSTS];
@@ -573,6 +583,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
int type, u64 parent_bus_addr,
u8 bar, size_t size);
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
+void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
void dw_pcie_setup(struct dw_pcie *pci);
void dw_pcie_iatu_detect(struct dw_pcie *pci);
int dw_pcie_edma_detect(struct dw_pcie *pci);
@@ -787,6 +798,14 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci)
pci->ops->stop_link(pci);
}
+static inline int dw_pcie_assert_perst(struct dw_pcie *pci, bool assert)
+{
+ if (pci->ops && pci->ops->assert_perst)
+ return pci->ops->assert_perst(pci, assert);
+
+ return 0;
+}
+
static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
{
u32 val;
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 3e2752c7dd09..f8605fe61a41 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -62,6 +62,12 @@
/* Interrupt Mask Register Related to Miscellaneous Operation */
#define PCIE_CLIENT_INTR_MASK_MISC 0x24
+/* Power Management Control Register */
+#define PCIE_CLIENT_POWER_CON 0x2c
+#define PCIE_CLKREQ_READY FIELD_PREP_WM16(BIT(0), 1)
+#define PCIE_CLKREQ_NOT_READY FIELD_PREP_WM16(BIT(0), 0)
+#define PCIE_CLKREQ_PULL_DOWN FIELD_PREP_WM16(GENMASK(13, 12), 1)
+
/* Hot Reset Control Register */
#define PCIE_CLIENT_HOT_RESET_CTRL 0x180
#define PCIE_LTSSM_APP_DLY2_EN BIT(1)
@@ -82,9 +88,9 @@ struct rockchip_pcie {
unsigned int clk_cnt;
struct reset_control *rst;
struct gpio_desc *rst_gpio;
- struct regulator *vpcie3v3;
struct irq_domain *irq_domain;
const struct rockchip_pcie_of_data *data;
+ bool supports_clkreq;
};
struct rockchip_pcie_of_data {
@@ -200,6 +206,35 @@ static bool rockchip_pcie_link_up(struct dw_pcie *pci)
return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP;
}
+/*
+ * See e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0 for the steps
+ * needed to support L1 substates. Currently, just enable L1 substates for RC
+ * mode if CLKREQ# is properly connected and supports-clkreq is present in DT.
+ * For EP mode, there are more things should be done to actually save power in
+ * L1 substates, so disable L1 substates until there is proper support.
+ */
+static void rockchip_pcie_configure_l1ss(struct dw_pcie *pci)
+{
+ struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
+
+ /* Enable L1 substates if CLKREQ# is properly connected */
+ if (rockchip->supports_clkreq) {
+ rockchip_pcie_writel_apb(rockchip, PCIE_CLKREQ_READY,
+ PCIE_CLIENT_POWER_CON);
+ pci->l1ss_support = true;
+ return;
+ }
+
+ /*
+ * Otherwise, assert CLKREQ# unconditionally. Since
+ * pci->l1ss_support is not set, the DWC core will prevent L1
+ * Substates support from being advertised.
+ */
+ rockchip_pcie_writel_apb(rockchip,
+ PCIE_CLKREQ_PULL_DOWN | PCIE_CLKREQ_NOT_READY,
+ PCIE_CLIENT_POWER_CON);
+}
+
static void rockchip_pcie_enable_l0s(struct dw_pcie *pci)
{
u32 cap, lnkcap;
@@ -264,6 +299,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler,
rockchip);
+ rockchip_pcie_configure_l1ss(pci);
rockchip_pcie_enable_l0s(pci);
return 0;
@@ -412,6 +448,9 @@ static int rockchip_pcie_resource_get(struct platform_device *pdev,
return dev_err_probe(&pdev->dev, PTR_ERR(rockchip->rst),
"failed to get reset lines\n");
+ rockchip->supports_clkreq = of_property_read_bool(pdev->dev.of_node,
+ "supports-clkreq");
+
return 0;
}
@@ -652,22 +691,15 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
return ret;
/* DON'T MOVE ME: must be enable before PHY init */
- rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
- if (IS_ERR(rockchip->vpcie3v3)) {
- if (PTR_ERR(rockchip->vpcie3v3) != -ENODEV)
- return dev_err_probe(dev, PTR_ERR(rockchip->vpcie3v3),
- "failed to get vpcie3v3 regulator\n");
- rockchip->vpcie3v3 = NULL;
- } else {
- ret = regulator_enable(rockchip->vpcie3v3);
- if (ret)
- return dev_err_probe(dev, ret,
- "failed to enable vpcie3v3 regulator\n");
- }
+ ret = devm_regulator_get_enable_optional(dev, "vpcie3v3");
+ if (ret < 0 && ret != -ENODEV)
+ return dev_err_probe(dev, ret,
+ "failed to enable vpcie3v3 regulator\n");
ret = rockchip_pcie_phy_init(rockchip);
if (ret)
- goto disable_regulator;
+ return dev_err_probe(dev, ret,
+ "failed to initialize the phy\n");
ret = reset_control_deassert(rockchip->rst);
if (ret)
@@ -700,9 +732,6 @@ deinit_clk:
clk_bulk_disable_unprepare(rockchip->clk_cnt, rockchip->clks);
deinit_phy:
rockchip_pcie_phy_deinit(rockchip);
-disable_regulator:
- if (rockchip->vpcie3v3)
- regulator_disable(rockchip->vpcie3v3);
return ret;
}
diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c
new file mode 100644
index 000000000000..47745749f75c
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe host controller driver for NXP S32G SoCs
+ *
+ * Copyright 2019-2025 NXP
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+/* PCIe controller Sub-System */
+
+/* PCIe controller 0 General Control 1 */
+#define PCIE_S32G_PE0_GEN_CTRL_1 0x50
+#define DEVICE_TYPE_MASK GENMASK(3, 0)
+#define SRIS_MODE BIT(8)
+
+/* PCIe controller 0 General Control 3 */
+#define PCIE_S32G_PE0_GEN_CTRL_3 0x58
+#define LTSSM_EN BIT(0)
+
+/* PCIe Controller 0 Interrupt Status */
+#define PCIE_S32G_PE0_INT_STS 0xE8
+#define HP_INT_STS BIT(6)
+
+/* Boundary between peripheral space and physical memory space */
+#define S32G_MEMORY_BOUNDARY_ADDR 0x80000000
+
+struct s32g_pcie_port {
+ struct list_head list;
+ struct phy *phy;
+};
+
+struct s32g_pcie {
+ struct dw_pcie pci;
+ void __iomem *ctrl_base;
+ struct list_head ports;
+};
+
+#define to_s32g_from_dw_pcie(x) \
+ container_of(x, struct s32g_pcie, pci)
+
+static void s32g_pcie_writel_ctrl(struct s32g_pcie *s32g_pp, u32 reg, u32 val)
+{
+ writel(val, s32g_pp->ctrl_base + reg);
+}
+
+static u32 s32g_pcie_readl_ctrl(struct s32g_pcie *s32g_pp, u32 reg)
+{
+ return readl(s32g_pp->ctrl_base + reg);
+}
+
+static void s32g_pcie_enable_ltssm(struct s32g_pcie *s32g_pp)
+{
+ u32 reg;
+
+ reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3);
+ reg |= LTSSM_EN;
+ s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg);
+}
+
+static void s32g_pcie_disable_ltssm(struct s32g_pcie *s32g_pp)
+{
+ u32 reg;
+
+ reg = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3);
+ reg &= ~LTSSM_EN;
+ s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_3, reg);
+}
+
+static int s32g_pcie_start_link(struct dw_pcie *pci)
+{
+ struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci);
+
+ s32g_pcie_enable_ltssm(s32g_pp);
+
+ return 0;
+}
+
+static void s32g_pcie_stop_link(struct dw_pcie *pci)
+{
+ struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci);
+
+ s32g_pcie_disable_ltssm(s32g_pp);
+}
+
+static struct dw_pcie_ops s32g_pcie_ops = {
+ .start_link = s32g_pcie_start_link,
+ .stop_link = s32g_pcie_stop_link,
+};
+
+/* Configure the AMBA AXI Coherency Extensions (ACE) interface */
+static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci)
+{
+ u32 ddr_base_low = lower_32_bits(S32G_MEMORY_BOUNDARY_ADDR);
+ u32 ddr_base_high = upper_32_bits(S32G_MEMORY_BOUNDARY_ADDR);
+
+ dw_pcie_dbi_ro_wr_en(pci);
+ dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0);
+
+ /*
+ * Ncore is a cache-coherent interconnect module that enables the
+ * integration of heterogeneous coherent and non-coherent agents in
+ * the chip. Ncore transactions to peripheral should be non-coherent
+ * or it might drop them.
+ *
+ * One example where this is needed are PCIe MSIs, which use NoSnoop=0
+ * and might end up routed to Ncore. PCIe coherent traffic (e.g. MSIs)
+ * that targets peripheral space will be dropped by Ncore because
+ * peripherals on S32G are not coherent as slaves. We add a hard
+ * boundary in the PCIe controller coherency control registers to
+ * separate physical memory space from peripheral space.
+ *
+ * Define the start of DDR as seen by Linux as this boundary between
+ * "memory" and "peripherals", with peripherals being below.
+ */
+ dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF,
+ (ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK));
+ dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high);
+ dw_pcie_dbi_ro_wr_dis(pci);
+}
+
+static int s32g_init_pcie_controller(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct s32g_pcie *s32g_pp = to_s32g_from_dw_pcie(pci);
+ u32 val;
+
+ /* Set RP mode */
+ val = s32g_pcie_readl_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1);
+ val &= ~DEVICE_TYPE_MASK;
+ val |= FIELD_PREP(DEVICE_TYPE_MASK, PCI_EXP_TYPE_ROOT_PORT);
+
+ /* Use default CRNS */
+ val &= ~SRIS_MODE;
+
+ s32g_pcie_writel_ctrl(s32g_pp, PCIE_S32G_PE0_GEN_CTRL_1, val);
+
+ /*
+ * Make sure we use the coherency defaults (just in case the settings
+ * have been changed from their reset values)
+ */
+ s32g_pcie_reset_mstr_ace(pci);
+
+ dw_pcie_dbi_ro_wr_en(pci);
+
+ val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE);
+ val |= PORT_FORCE_DO_DESKEW_FOR_SRIS;
+ dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val);
+
+ val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
+ val |= GEN3_RELATED_OFF_EQ_PHASE_2_3;
+ dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
+
+ dw_pcie_dbi_ro_wr_dis(pci);
+
+ return 0;
+}
+
+static const struct dw_pcie_host_ops s32g_pcie_host_ops = {
+ .init = s32g_init_pcie_controller,
+};
+
+static int s32g_init_pcie_phy(struct s32g_pcie *s32g_pp)
+{
+ struct dw_pcie *pci = &s32g_pp->pci;
+ struct device *dev = pci->dev;
+ struct s32g_pcie_port *port, *tmp;
+ int ret;
+
+ list_for_each_entry(port, &s32g_pp->ports, list) {
+ ret = phy_init(port->phy);
+ if (ret) {
+ dev_err(dev, "Failed to init serdes PHY\n");
+ goto err_phy_revert;
+ }
+
+ ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, 0);
+ if (ret) {
+ dev_err(dev, "Failed to set mode on serdes PHY\n");
+ goto err_phy_exit;
+ }
+
+ ret = phy_power_on(port->phy);
+ if (ret) {
+ dev_err(dev, "Failed to power on serdes PHY\n");
+ goto err_phy_exit;
+ }
+ }
+
+ return 0;
+
+err_phy_exit:
+ phy_exit(port->phy);
+
+err_phy_revert:
+ list_for_each_entry_continue_reverse(port, &s32g_pp->ports, list) {
+ phy_power_off(port->phy);
+ phy_exit(port->phy);
+ }
+
+ list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list)
+ list_del(&port->list);
+
+ return ret;
+}
+
+static void s32g_deinit_pcie_phy(struct s32g_pcie *s32g_pp)
+{
+ struct s32g_pcie_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list) {
+ phy_power_off(port->phy);
+ phy_exit(port->phy);
+ list_del(&port->list);
+ }
+}
+
+static int s32g_pcie_init(struct device *dev, struct s32g_pcie *s32g_pp)
+{
+ s32g_pcie_disable_ltssm(s32g_pp);
+
+ return s32g_init_pcie_phy(s32g_pp);
+}
+
+static void s32g_pcie_deinit(struct s32g_pcie *s32g_pp)
+{
+ s32g_pcie_disable_ltssm(s32g_pp);
+
+ s32g_deinit_pcie_phy(s32g_pp);
+}
+
+static int s32g_pcie_parse_port(struct s32g_pcie *s32g_pp, struct device_node *node)
+{
+ struct device *dev = s32g_pp->pci.dev;
+ struct s32g_pcie_port *port;
+ int num_lanes;
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->phy = devm_of_phy_get(dev, node, NULL);
+ if (IS_ERR(port->phy))
+ return dev_err_probe(dev, PTR_ERR(port->phy),
+ "Failed to get serdes PHY\n");
+
+ INIT_LIST_HEAD(&port->list);
+ list_add_tail(&port->list, &s32g_pp->ports);
+
+ /*
+ * The DWC core initialization code cannot yet parse the num-lanes
+ * attribute in the Root Port node. The S32G only supports one Root
+ * Port for now so its driver can parse the node and set the num_lanes
+ * field of struct dwc_pcie before calling dw_pcie_host_init().
+ */
+ if (!of_property_read_u32(node, "num-lanes", &num_lanes))
+ s32g_pp->pci.num_lanes = num_lanes;
+
+ return 0;
+}
+
+static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp)
+{
+ struct s32g_pcie_port *port, *tmp;
+ int ret = -ENOENT;
+
+ for_each_available_child_of_node_scoped(dev->of_node, of_port) {
+ if (!of_node_is_type(of_port, "pci"))
+ continue;
+
+ ret = s32g_pcie_parse_port(s32g_pp, of_port);
+ if (ret)
+ goto err_port;
+ }
+
+err_port:
+ list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list)
+ list_del(&port->list);
+
+ return ret;
+}
+
+static int s32g_pcie_get_resources(struct platform_device *pdev,
+ struct s32g_pcie *s32g_pp)
+{
+ struct dw_pcie *pci = &s32g_pp->pci;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ pci->dev = dev;
+ pci->ops = &s32g_pcie_ops;
+
+ s32g_pp->ctrl_base = devm_platform_ioremap_resource_byname(pdev, "ctrl");
+ if (IS_ERR(s32g_pp->ctrl_base))
+ return PTR_ERR(s32g_pp->ctrl_base);
+
+ INIT_LIST_HEAD(&s32g_pp->ports);
+
+ ret = s32g_pcie_parse_ports(dev, s32g_pp);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to parse Root Port: %d\n", ret);
+
+ platform_set_drvdata(pdev, s32g_pp);
+
+ return 0;
+}
+
+static int s32g_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct s32g_pcie *s32g_pp;
+ struct dw_pcie_rp *pp;
+ int ret;
+
+ s32g_pp = devm_kzalloc(dev, sizeof(*s32g_pp), GFP_KERNEL);
+ if (!s32g_pp)
+ return -ENOMEM;
+
+ ret = s32g_pcie_get_resources(pdev, s32g_pp);
+ if (ret)
+ return ret;
+
+ pm_runtime_no_callbacks(dev);
+ devm_pm_runtime_enable(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ goto err_pm_runtime_put;
+
+ ret = s32g_pcie_init(dev, s32g_pp);
+ if (ret)
+ goto err_pm_runtime_put;
+
+ pp = &s32g_pp->pci.pp;
+ pp->ops = &s32g_pcie_host_ops;
+ pp->use_atu_msg = true;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret)
+ goto err_pcie_deinit;
+
+ return 0;
+
+err_pcie_deinit:
+ s32g_pcie_deinit(s32g_pp);
+err_pm_runtime_put:
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static int s32g_pcie_suspend_noirq(struct device *dev)
+{
+ struct s32g_pcie *s32g_pp = dev_get_drvdata(dev);
+ struct dw_pcie *pci = &s32g_pp->pci;
+
+ return dw_pcie_suspend_noirq(pci);
+}
+
+static int s32g_pcie_resume_noirq(struct device *dev)
+{
+ struct s32g_pcie *s32g_pp = dev_get_drvdata(dev);
+ struct dw_pcie *pci = &s32g_pp->pci;
+
+ return dw_pcie_resume_noirq(pci);
+}
+
+static const struct dev_pm_ops s32g_pcie_pm_ops = {
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(s32g_pcie_suspend_noirq,
+ s32g_pcie_resume_noirq)
+};
+
+static const struct of_device_id s32g_pcie_of_match[] = {
+ { .compatible = "nxp,s32g2-pcie" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, s32g_pcie_of_match);
+
+static struct platform_driver s32g_pcie_driver = {
+ .driver = {
+ .name = "s32g-pcie",
+ .of_match_table = s32g_pcie_of_match,
+ .suppress_bind_attrs = true,
+ .pm = pm_sleep_ptr(&s32g_pcie_pm_ops),
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = s32g_pcie_probe,
+};
+
+builtin_platform_driver(s32g_pcie_driver);
+
+MODULE_AUTHOR("Ionut Vicovan <Ionut.Vicovan@nxp.com>");
+MODULE_DESCRIPTION("NXP S32G PCIe Host controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 805edbbfe7eb..7b92e7a1c0d9 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -55,7 +55,6 @@
#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8
#define PARF_Q2A_FLUSH 0x1ac
#define PARF_LTSSM 0x1b0
-#define PARF_SLV_DBI_ELBI 0x1b4
#define PARF_INT_ALL_STATUS 0x224
#define PARF_INT_ALL_CLEAR 0x228
#define PARF_INT_ALL_MASK 0x22c
@@ -65,16 +64,6 @@
#define PARF_DBI_BASE_ADDR_V2_HI 0x354
#define PARF_SLV_ADDR_SPACE_SIZE_V2 0x358
#define PARF_SLV_ADDR_SPACE_SIZE_V2_HI 0x35c
-#define PARF_BLOCK_SLV_AXI_WR_BASE 0x360
-#define PARF_BLOCK_SLV_AXI_WR_BASE_HI 0x364
-#define PARF_BLOCK_SLV_AXI_WR_LIMIT 0x368
-#define PARF_BLOCK_SLV_AXI_WR_LIMIT_HI 0x36c
-#define PARF_BLOCK_SLV_AXI_RD_BASE 0x370
-#define PARF_BLOCK_SLV_AXI_RD_BASE_HI 0x374
-#define PARF_BLOCK_SLV_AXI_RD_LIMIT 0x378
-#define PARF_BLOCK_SLV_AXI_RD_LIMIT_HI 0x37c
-#define PARF_ECAM_BASE 0x380
-#define PARF_ECAM_BASE_HI 0x384
#define PARF_NO_SNOOP_OVERRIDE 0x3d4
#define PARF_ATU_BASE_ADDR 0x634
#define PARF_ATU_BASE_ADDR_HI 0x638
@@ -98,7 +87,6 @@
/* PARF_SYS_CTRL register fields */
#define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN BIT(29)
-#define PCIE_ECAM_BLOCKER_EN BIT(26)
#define MST_WAKEUP_EN BIT(13)
#define SLV_WAKEUP_EN BIT(12)
#define MSTR_ACLK_CGC_DIS BIT(10)
@@ -146,9 +134,6 @@
/* PARF_LTSSM register fields */
#define LTSSM_EN BIT(8)
-/* PARF_SLV_DBI_ELBI */
-#define SLV_DBI_ELBI_ADDR_BASE GENMASK(11, 0)
-
/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */
#define PARF_INT_ALL_LINK_UP BIT(13)
#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23)
@@ -262,6 +247,7 @@ struct qcom_pcie_ops {
int (*get_resources)(struct qcom_pcie *pcie);
int (*init)(struct qcom_pcie *pcie);
int (*post_init)(struct qcom_pcie *pcie);
+ void (*host_post_init)(struct qcom_pcie *pcie);
void (*deinit)(struct qcom_pcie *pcie);
void (*ltssm_enable)(struct qcom_pcie *pcie);
int (*config_sid)(struct qcom_pcie *pcie);
@@ -326,47 +312,6 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
qcom_perst_assert(pcie, false);
}
-static void qcom_pci_config_ecam(struct dw_pcie_rp *pp)
-{
- struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
- struct qcom_pcie *pcie = to_qcom_pcie(pci);
- u64 addr, addr_end;
- u32 val;
-
- writel_relaxed(lower_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE);
- writel_relaxed(upper_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE_HI);
-
- /*
- * The only device on the root bus is a single Root Port. If we try to
- * access any devices other than Device/Function 00.0 on Bus 0, the TLP
- * will go outside of the controller to the PCI bus. But with CFG Shift
- * Feature (ECAM) enabled in iATU, there is no guarantee that the
- * response is going to be all F's. Hence, to make sure that the
- * requester gets all F's response for accesses other than the Root
- * Port, configure iATU to block the transactions starting from
- * function 1 of the root bus to the end of the root bus (i.e., from
- * dbi_base + 4KB to dbi_base + 1MB).
- */
- addr = pci->dbi_phys_addr + SZ_4K;
- writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE);
- writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE_HI);
-
- writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE);
- writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE_HI);
-
- addr_end = pci->dbi_phys_addr + SZ_1M - 1;
-
- writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT);
- writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT_HI);
-
- writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT);
- writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT_HI);
-
- val = readl_relaxed(pcie->parf + PARF_SYS_CTRL);
- val |= PCIE_ECAM_BLOCKER_EN;
- writel_relaxed(val, pcie->parf + PARF_SYS_CTRL);
-}
-
static int qcom_pcie_start_link(struct dw_pcie *pci)
{
struct qcom_pcie *pcie = to_qcom_pcie(pci);
@@ -696,6 +641,18 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie)
return 0;
}
+static int qcom_pcie_assert_perst(struct dw_pcie *pci, bool assert)
+{
+ struct qcom_pcie *pcie = to_qcom_pcie(pci);
+
+ if (assert)
+ qcom_ep_reset_assert(pcie);
+ else
+ qcom_ep_reset_deassert(pcie);
+
+ return 0;
+}
+
static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie)
{
u32 val;
@@ -1067,6 +1024,8 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
val &= ~REQ_NOT_ENTR_L1;
writel(val, pcie->parf + PARF_PM_CTRL);
+ pci->l1ss_support = true;
+
val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2);
val |= EN;
writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2);
@@ -1094,6 +1053,25 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
return 0;
}
+static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata)
+{
+ /*
+ * Downstream devices need to be in D0 state before enabling PCI PM
+ * substates.
+ */
+ pci_set_power_state_locked(pdev, PCI_D0);
+ pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
+
+ return 0;
+}
+
+static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie)
+{
+ struct dw_pcie_rp *pp = &pcie->pci->pp;
+
+ pci_walk_bus(pp->bridge->bus, qcom_pcie_enable_aspm, NULL);
+}
+
static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
{
struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0;
@@ -1320,7 +1298,6 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qcom_pcie *pcie = to_qcom_pcie(pci);
- u16 offset;
int ret;
qcom_ep_reset_assert(pcie);
@@ -1329,17 +1306,6 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
return ret;
- if (pp->ecam_enabled) {
- /*
- * Override ELBI when ECAM is enabled, as when ECAM is enabled,
- * ELBI moves under the 'config' space.
- */
- offset = FIELD_GET(SLV_DBI_ELBI_ADDR_BASE, readl(pcie->parf + PARF_SLV_DBI_ELBI));
- pci->elbi_base = pci->dbi_base + offset;
-
- qcom_pci_config_ecam(pp);
- }
-
ret = qcom_pcie_phy_power_on(pcie);
if (ret)
goto err_deinit;
@@ -1380,9 +1346,19 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
pcie->cfg->ops->deinit(pcie);
}
+static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct qcom_pcie *pcie = to_qcom_pcie(pci);
+
+ if (pcie->cfg->ops->host_post_init)
+ pcie->cfg->ops->host_post_init(pcie);
+}
+
static const struct dw_pcie_host_ops qcom_pcie_dw_ops = {
.init = qcom_pcie_host_init,
.deinit = qcom_pcie_host_deinit,
+ .post_init = qcom_pcie_host_post_init,
};
/* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */
@@ -1444,6 +1420,7 @@ static const struct qcom_pcie_ops ops_1_9_0 = {
.get_resources = qcom_pcie_get_resources_2_7_0,
.init = qcom_pcie_init_2_7_0,
.post_init = qcom_pcie_post_init_2_7_0,
+ .host_post_init = qcom_pcie_host_post_init_2_7_0,
.deinit = qcom_pcie_deinit_2_7_0,
.ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
.config_sid = qcom_pcie_config_sid_1_9_0,
@@ -1454,6 +1431,7 @@ static const struct qcom_pcie_ops ops_1_21_0 = {
.get_resources = qcom_pcie_get_resources_2_7_0,
.init = qcom_pcie_init_2_7_0,
.post_init = qcom_pcie_post_init_2_7_0,
+ .host_post_init = qcom_pcie_host_post_init_2_7_0,
.deinit = qcom_pcie_deinit_2_7_0,
.ltssm_enable = qcom_pcie_2_3_2_ltssm_enable,
};
@@ -1516,6 +1494,7 @@ static const struct qcom_pcie_cfg cfg_fw_managed = {
static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = qcom_pcie_link_up,
.start_link = qcom_pcie_start_link,
+ .assert_perst = qcom_pcie_assert_perst,
};
static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
@@ -1565,6 +1544,7 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie)
{
u32 offset, status, width, speed;
struct dw_pcie *pci = pcie->pci;
+ struct dev_pm_opp_key key = {};
unsigned long freq_kbps;
struct dev_pm_opp *opp;
int ret, freq_mbps;
@@ -1592,8 +1572,20 @@ static void qcom_pcie_icc_opp_update(struct qcom_pcie *pcie)
return;
freq_kbps = freq_mbps * KILO;
- opp = dev_pm_opp_find_freq_exact(pci->dev, freq_kbps * width,
- true);
+ opp = dev_pm_opp_find_level_exact(pci->dev, speed);
+ if (IS_ERR(opp)) {
+ /* opp-level is not defined use only frequency */
+ opp = dev_pm_opp_find_freq_exact(pci->dev, freq_kbps * width,
+ true);
+ } else {
+ /* put opp-level OPP */
+ dev_pm_opp_put(opp);
+
+ key.freq = freq_kbps * width;
+ key.level = speed;
+ key.bw = 0;
+ opp = dev_pm_opp_find_key_exact(pci->dev, &key, true);
+ }
if (!IS_ERR(opp)) {
ret = dev_pm_opp_set_opp(pci->dev, opp);
if (ret)
diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
new file mode 100644
index 000000000000..be20a520255b
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
@@ -0,0 +1,357 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SpacemiT K1 PCIe host driver
+ *
+ * Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved.
+ * Copyright (c) 2023, spacemit Corporation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+#define PCI_VENDOR_ID_SPACEMIT 0x201f
+#define PCI_DEVICE_ID_SPACEMIT_K1 0x0001
+
+/* Offsets and field definitions for link management registers */
+#define K1_PHY_AHB_IRQ_EN 0x0000
+#define PCIE_INTERRUPT_EN BIT(0)
+
+#define K1_PHY_AHB_LINK_STS 0x0004
+#define SMLH_LINK_UP BIT(1)
+#define RDLH_LINK_UP BIT(12)
+
+#define INTR_ENABLE 0x0014
+#define MSI_CTRL_INT BIT(11)
+
+/* Some controls require APMU regmap access */
+#define SYSCON_APMU "spacemit,apmu"
+
+/* Offsets and field definitions for APMU registers */
+#define PCIE_CLK_RESET_CONTROL 0x0000
+#define LTSSM_EN BIT(6)
+#define PCIE_AUX_PWR_DET BIT(9)
+#define PCIE_RC_PERST BIT(12) /* 1: assert PERST# */
+#define APP_HOLD_PHY_RST BIT(30)
+#define DEVICE_TYPE_RC BIT(31) /* 0: endpoint; 1: RC */
+
+#define PCIE_CONTROL_LOGIC 0x0004
+#define PCIE_SOFT_RESET BIT(0)
+
+struct k1_pcie {
+ struct dw_pcie pci;
+ struct phy *phy;
+ void __iomem *link;
+ struct regmap *pmu; /* Errors ignored; MMIO-backed regmap */
+ u32 pmu_off;
+};
+
+#define to_k1_pcie(dw_pcie) \
+ platform_get_drvdata(to_platform_device((dw_pcie)->dev))
+
+static void k1_pcie_toggle_soft_reset(struct k1_pcie *k1)
+{
+ u32 offset;
+ u32 val;
+
+ /*
+ * Write, then read back to guarantee it has reached the device
+ * before we start the delay.
+ */
+ offset = k1->pmu_off + PCIE_CONTROL_LOGIC;
+ regmap_set_bits(k1->pmu, offset, PCIE_SOFT_RESET);
+ regmap_read(k1->pmu, offset, &val);
+
+ mdelay(2);
+
+ regmap_clear_bits(k1->pmu, offset, PCIE_SOFT_RESET);
+}
+
+/* Enable app clocks, deassert resets */
+static int k1_pcie_enable_resources(struct k1_pcie *k1)
+{
+ struct dw_pcie *pci = &k1->pci;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(ARRAY_SIZE(pci->app_clks), pci->app_clks);
+ if (ret)
+ return ret;
+
+ ret = reset_control_bulk_deassert(ARRAY_SIZE(pci->app_rsts),
+ pci->app_rsts);
+ if (ret)
+ goto err_disable_clks;
+
+ return 0;
+
+err_disable_clks:
+ clk_bulk_disable_unprepare(ARRAY_SIZE(pci->app_clks), pci->app_clks);
+
+ return ret;
+}
+
+/* Assert resets, disable app clocks */
+static void k1_pcie_disable_resources(struct k1_pcie *k1)
+{
+ struct dw_pcie *pci = &k1->pci;
+
+ reset_control_bulk_assert(ARRAY_SIZE(pci->app_rsts), pci->app_rsts);
+ clk_bulk_disable_unprepare(ARRAY_SIZE(pci->app_clks), pci->app_clks);
+}
+
+/* FIXME: Disable ASPM L1 to avoid errors reported on some NVMe drives */
+static void k1_pcie_disable_aspm_l1(struct k1_pcie *k1)
+{
+ struct dw_pcie *pci = &k1->pci;
+ u8 offset;
+ u32 val;
+
+ offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ offset += PCI_EXP_LNKCAP;
+
+ dw_pcie_dbi_ro_wr_en(pci);
+ val = dw_pcie_readl_dbi(pci, offset);
+ val &= ~PCI_EXP_LNKCAP_ASPM_L1;
+ dw_pcie_writel_dbi(pci, offset, val);
+ dw_pcie_dbi_ro_wr_dis(pci);
+}
+
+static int k1_pcie_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct k1_pcie *k1 = to_k1_pcie(pci);
+ u32 reset_ctrl;
+ u32 val;
+ int ret;
+
+ k1_pcie_toggle_soft_reset(k1);
+
+ ret = k1_pcie_enable_resources(k1);
+ if (ret)
+ return ret;
+
+ /* Set the PCI vendor and device ID */
+ dw_pcie_dbi_ro_wr_en(pci);
+ dw_pcie_writew_dbi(pci, PCI_VENDOR_ID, PCI_VENDOR_ID_SPACEMIT);
+ dw_pcie_writew_dbi(pci, PCI_DEVICE_ID, PCI_DEVICE_ID_SPACEMIT_K1);
+ dw_pcie_dbi_ro_wr_dis(pci);
+
+ /*
+ * Start by asserting fundamental reset (drive PERST# low). The
+ * PCI CEM spec says that PERST# should be deasserted at least
+ * 100ms after the power becomes stable, so we'll insert that
+ * delay first. Write, then read it back to guarantee the write
+ * reaches the device before we start the delay.
+ */
+ reset_ctrl = k1->pmu_off + PCIE_CLK_RESET_CONTROL;
+ regmap_set_bits(k1->pmu, reset_ctrl, PCIE_RC_PERST);
+ regmap_read(k1->pmu, reset_ctrl, &val);
+ mdelay(PCIE_T_PVPERL_MS);
+
+ /*
+ * Put the controller in root complex mode, and indicate that
+ * Vaux (3.3v) is present.
+ */
+ regmap_set_bits(k1->pmu, reset_ctrl, DEVICE_TYPE_RC | PCIE_AUX_PWR_DET);
+
+ ret = phy_init(k1->phy);
+ if (ret) {
+ k1_pcie_disable_resources(k1);
+
+ return ret;
+ }
+
+ /* Deassert fundamental reset (drive PERST# high) */
+ regmap_clear_bits(k1->pmu, reset_ctrl, PCIE_RC_PERST);
+
+ /* Finally, as a workaround, disable ASPM L1 */
+ k1_pcie_disable_aspm_l1(k1);
+
+ return 0;
+}
+
+static void k1_pcie_deinit(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct k1_pcie *k1 = to_k1_pcie(pci);
+
+ /* Assert fundamental reset (drive PERST# low) */
+ regmap_set_bits(k1->pmu, k1->pmu_off + PCIE_CLK_RESET_CONTROL,
+ PCIE_RC_PERST);
+
+ phy_exit(k1->phy);
+
+ k1_pcie_disable_resources(k1);
+}
+
+static const struct dw_pcie_host_ops k1_pcie_host_ops = {
+ .init = k1_pcie_init,
+ .deinit = k1_pcie_deinit,
+};
+
+static bool k1_pcie_link_up(struct dw_pcie *pci)
+{
+ struct k1_pcie *k1 = to_k1_pcie(pci);
+ u32 val;
+
+ val = readl_relaxed(k1->link + K1_PHY_AHB_LINK_STS);
+
+ return (val & RDLH_LINK_UP) && (val & SMLH_LINK_UP);
+}
+
+static int k1_pcie_start_link(struct dw_pcie *pci)
+{
+ struct k1_pcie *k1 = to_k1_pcie(pci);
+ u32 val;
+
+ /* Stop holding the PHY in reset, and enable link training */
+ regmap_update_bits(k1->pmu, k1->pmu_off + PCIE_CLK_RESET_CONTROL,
+ APP_HOLD_PHY_RST | LTSSM_EN, LTSSM_EN);
+
+ /* Enable the MSI interrupt */
+ writel_relaxed(MSI_CTRL_INT, k1->link + INTR_ENABLE);
+
+ /* Top-level interrupt enable */
+ val = readl_relaxed(k1->link + K1_PHY_AHB_IRQ_EN);
+ val |= PCIE_INTERRUPT_EN;
+ writel_relaxed(val, k1->link + K1_PHY_AHB_IRQ_EN);
+
+ return 0;
+}
+
+static void k1_pcie_stop_link(struct dw_pcie *pci)
+{
+ struct k1_pcie *k1 = to_k1_pcie(pci);
+ u32 val;
+
+ /* Disable interrupts */
+ val = readl_relaxed(k1->link + K1_PHY_AHB_IRQ_EN);
+ val &= ~PCIE_INTERRUPT_EN;
+ writel_relaxed(val, k1->link + K1_PHY_AHB_IRQ_EN);
+
+ writel_relaxed(0, k1->link + INTR_ENABLE);
+
+ /* Disable the link and hold the PHY in reset */
+ regmap_update_bits(k1->pmu, k1->pmu_off + PCIE_CLK_RESET_CONTROL,
+ APP_HOLD_PHY_RST | LTSSM_EN, APP_HOLD_PHY_RST);
+}
+
+static const struct dw_pcie_ops k1_pcie_ops = {
+ .link_up = k1_pcie_link_up,
+ .start_link = k1_pcie_start_link,
+ .stop_link = k1_pcie_stop_link,
+};
+
+static int k1_pcie_parse_port(struct k1_pcie *k1)
+{
+ struct device *dev = k1->pci.dev;
+ struct device_node *root_port;
+ struct phy *phy;
+
+ /* We assume only one root port */
+ root_port = of_get_next_available_child(dev_of_node(dev), NULL);
+ if (!root_port)
+ return -EINVAL;
+
+ phy = devm_of_phy_get(dev, root_port, NULL);
+
+ of_node_put(root_port);
+
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ k1->phy = phy;
+
+ return 0;
+}
+
+static int k1_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct k1_pcie *k1;
+ int ret;
+
+ k1 = devm_kzalloc(dev, sizeof(*k1), GFP_KERNEL);
+ if (!k1)
+ return -ENOMEM;
+
+ k1->pmu = syscon_regmap_lookup_by_phandle_args(dev_of_node(dev),
+ SYSCON_APMU, 1,
+ &k1->pmu_off);
+ if (IS_ERR(k1->pmu))
+ return dev_err_probe(dev, PTR_ERR(k1->pmu),
+ "failed to lookup PMU registers\n");
+
+ k1->link = devm_platform_ioremap_resource_byname(pdev, "link");
+ if (IS_ERR(k1->link))
+ return dev_err_probe(dev, PTR_ERR(k1->link),
+ "failed to map \"link\" registers\n");
+
+ k1->pci.dev = dev;
+ k1->pci.ops = &k1_pcie_ops;
+ k1->pci.pp.num_vectors = MAX_MSI_IRQS;
+ dw_pcie_cap_set(&k1->pci, REQ_RES);
+
+ k1->pci.pp.ops = &k1_pcie_host_ops;
+
+ /* Hold the PHY in reset until we start the link */
+ regmap_set_bits(k1->pmu, k1->pmu_off + PCIE_CLK_RESET_CONTROL,
+ APP_HOLD_PHY_RST);
+
+ ret = devm_regulator_get_enable(dev, "vpcie3v3");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to get \"vpcie3v3\" supply\n");
+
+ pm_runtime_set_active(dev);
+ pm_runtime_no_callbacks(dev);
+ devm_pm_runtime_enable(dev);
+
+ platform_set_drvdata(pdev, k1);
+
+ ret = k1_pcie_parse_port(k1);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to parse root port\n");
+
+ ret = dw_pcie_host_init(&k1->pci.pp);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize host\n");
+
+ return 0;
+}
+
+static void k1_pcie_remove(struct platform_device *pdev)
+{
+ struct k1_pcie *k1 = platform_get_drvdata(pdev);
+
+ dw_pcie_host_deinit(&k1->pci.pp);
+}
+
+static const struct of_device_id k1_pcie_of_match_table[] = {
+ { .compatible = "spacemit,k1-pcie", },
+ { }
+};
+
+static struct platform_driver k1_pcie_driver = {
+ .probe = k1_pcie_probe,
+ .remove = k1_pcie_remove,
+ .driver = {
+ .name = "spacemit-k1-pcie",
+ .of_match_table = k1_pcie_of_match_table,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+};
+module_platform_driver(k1_pcie_driver);
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SpacemiT K1 PCIe host driver");
diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
index 3400c7cd2d88..2cecf32d2b0f 100644
--- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
+++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
@@ -7,9 +7,9 @@
*/
#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
#include <linux/mfd/syscon.h>
#include <linux/of_platform.h>
-#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@@ -37,36 +37,9 @@ static void stm32_pcie_ep_init(struct dw_pcie_ep *ep)
dw_pcie_ep_reset_bar(pci, bar);
}
-static int stm32_pcie_enable_link(struct dw_pcie *pci)
-{
- struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
-
- regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
- STM32MP25_PCIECR_LTSSM_EN,
- STM32MP25_PCIECR_LTSSM_EN);
-
- return dw_pcie_wait_for_link(pci);
-}
-
-static void stm32_pcie_disable_link(struct dw_pcie *pci)
-{
- struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
-
- regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, STM32MP25_PCIECR_LTSSM_EN, 0);
-}
-
static int stm32_pcie_start_link(struct dw_pcie *pci)
{
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
- int ret;
-
- dev_dbg(pci->dev, "Enable link\n");
-
- ret = stm32_pcie_enable_link(pci);
- if (ret) {
- dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret);
- return ret;
- }
enable_irq(stm32_pcie->perst_irq);
@@ -77,11 +50,7 @@ static void stm32_pcie_stop_link(struct dw_pcie *pci)
{
struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci);
- dev_dbg(pci->dev, "Disable link\n");
-
disable_irq(stm32_pcie->perst_irq);
-
- stm32_pcie_disable_link(pci);
}
static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
@@ -152,6 +121,9 @@ static void stm32_pcie_perst_assert(struct dw_pcie *pci)
dev_dbg(dev, "PERST asserted by host\n");
+ regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
+ STM32MP25_PCIECR_LTSSM_EN, 0);
+
pci_epc_deinit_notify(ep->epc);
stm32_pcie_disable_resources(stm32_pcie);
@@ -192,6 +164,11 @@ static void stm32_pcie_perst_deassert(struct dw_pcie *pci)
pci_epc_init_notify(ep->epc);
+ /* Enable link training */
+ regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR,
+ STM32MP25_PCIECR_LTSSM_EN,
+ STM32MP25_PCIECR_LTSSM_EN);
+
return;
err_disable_resources:
@@ -237,6 +214,8 @@ static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie,
ep->ops = &stm32_pcie_ep_ops;
+ ep->page_size = stm32_pcie_epc_features.align;
+
ret = dw_pcie_ep_init(ep);
if (ret) {
dev_err(dev, "Failed to initialize ep: %d\n", ret);
diff --git a/drivers/pci/controller/dwc/pcie-stm32.c b/drivers/pci/controller/dwc/pcie-stm32.c
index 96a5fb893af4..a9e77478443b 100644
--- a/drivers/pci/controller/dwc/pcie-stm32.c
+++ b/drivers/pci/controller/dwc/pcie-stm32.c
@@ -7,18 +7,30 @@
*/
#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/irq.h>
#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <linux/stddef.h>
+
+#include "../../pci.h"
+
#include "pcie-designware.h"
#include "pcie-stm32.h"
-#include "../../pci.h"
struct stm32_pcie {
struct dw_pcie pci;
diff --git a/drivers/pci/controller/dwc/pcie-stm32.h b/drivers/pci/controller/dwc/pcie-stm32.h
index 09d39f04e469..419cf1ff669d 100644
--- a/drivers/pci/controller/dwc/pcie-stm32.h
+++ b/drivers/pci/controller/dwc/pcie-stm32.h
@@ -6,6 +6,9 @@
* Author: Christian Bruel <christian.bruel@foss.st.com>
*/
+#include <linux/bits.h>
+#include <linux/device.h>
+
#define to_stm32_pcie(x) dev_get_drvdata((x)->dev)
#define STM32MP25_PCIECR_TYPE_MASK GENMASK(11, 8)
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 10e74458e667..0ddeef70726d 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -260,7 +260,6 @@ struct tegra_pcie_dw {
u32 msi_ctrl_int;
u32 num_lanes;
u32 cid;
- u32 cfg_link_cap_l1sub;
u32 ras_des_cap;
u32 pcie_cap_base;
u32 aspm_cmrt;
@@ -475,8 +474,7 @@ static irqreturn_t tegra_pcie_ep_irq_thread(int irq, void *arg)
return IRQ_HANDLED;
/* If EP doesn't advertise L1SS, just return */
- val = dw_pcie_readl_dbi(pci, pcie->cfg_link_cap_l1sub);
- if (!(val & (PCI_L1SS_CAP_ASPM_L1_1 | PCI_L1SS_CAP_ASPM_L1_2)))
+ if (!pci->l1ss_support)
return IRQ_HANDLED;
/* Check if BME is set to '1' */
@@ -608,24 +606,6 @@ static struct pci_ops tegra_pci_ops = {
};
#if defined(CONFIG_PCIEASPM)
-static void disable_aspm_l11(struct tegra_pcie_dw *pcie)
-{
- u32 val;
-
- val = dw_pcie_readl_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub);
- val &= ~PCI_L1SS_CAP_ASPM_L1_1;
- dw_pcie_writel_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub, val);
-}
-
-static void disable_aspm_l12(struct tegra_pcie_dw *pcie)
-{
- u32 val;
-
- val = dw_pcie_readl_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub);
- val &= ~PCI_L1SS_CAP_ASPM_L1_2;
- dw_pcie_writel_dbi(&pcie->pci, pcie->cfg_link_cap_l1sub, val);
-}
-
static inline u32 event_counter_prog(struct tegra_pcie_dw *pcie, u32 event)
{
u32 val;
@@ -682,10 +662,9 @@ static int aspm_state_cnt(struct seq_file *s, void *data)
static void init_host_aspm(struct tegra_pcie_dw *pcie)
{
struct dw_pcie *pci = &pcie->pci;
- u32 val;
+ u32 l1ss, val;
- val = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
- pcie->cfg_link_cap_l1sub = val + PCI_L1SS_CAP;
+ l1ss = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
pcie->ras_des_cap = dw_pcie_find_ext_capability(&pcie->pci,
PCI_EXT_CAP_ID_VNDR);
@@ -697,11 +676,14 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
/* Program T_cmrt and T_pwr_on values */
- val = dw_pcie_readl_dbi(pci, pcie->cfg_link_cap_l1sub);
+ val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
val &= ~(PCI_L1SS_CAP_CM_RESTORE_TIME | PCI_L1SS_CAP_P_PWR_ON_VALUE);
val |= (pcie->aspm_cmrt << 8);
val |= (pcie->aspm_pwr_on_t << 19);
- dw_pcie_writel_dbi(pci, pcie->cfg_link_cap_l1sub, val);
+ dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, val);
+
+ if (pcie->supports_clkreq)
+ pci->l1ss_support = true;
/* Program L0s and L1 entrance latencies */
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
@@ -726,8 +708,6 @@ static void init_debugfs(struct tegra_pcie_dw *pcie)
aspm_state_cnt);
}
#else
-static inline void disable_aspm_l12(struct tegra_pcie_dw *pcie) { return; }
-static inline void disable_aspm_l11(struct tegra_pcie_dw *pcie) { return; }
static inline void init_host_aspm(struct tegra_pcie_dw *pcie) { return; }
static inline void init_debugfs(struct tegra_pcie_dw *pcie) { return; }
#endif
@@ -931,12 +911,6 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
init_host_aspm(pcie);
- /* Disable ASPM-L1SS advertisement if there is no CLKREQ routing */
- if (!pcie->supports_clkreq) {
- disable_aspm_l11(pcie);
- disable_aspm_l12(pcie);
- }
-
if (!pcie->of_data->has_l1ss_exit_fix) {
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
@@ -1871,12 +1845,6 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
init_host_aspm(pcie);
- /* Disable ASPM-L1SS advertisement if there is no CLKREQ routing */
- if (!pcie->supports_clkreq) {
- disable_aspm_l11(pcie);
- disable_aspm_l12(pcie);
- }
-
if (!pcie->of_data->has_l1ss_exit_fix) {
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index 810d1c8de24e..c473e7c03bac 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -53,16 +53,12 @@ struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
EXPORT_SYMBOL_GPL(pci_host_common_ecam_create);
int pci_host_common_init(struct platform_device *pdev,
+ struct pci_host_bridge *bridge,
const struct pci_ecam_ops *ops)
{
struct device *dev = &pdev->dev;
- struct pci_host_bridge *bridge;
struct pci_config_window *cfg;
- bridge = devm_pci_alloc_host_bridge(dev, 0);
- if (!bridge)
- return -ENOMEM;
-
of_pci_check_probe_only();
platform_set_drvdata(pdev, bridge);
@@ -85,12 +81,17 @@ EXPORT_SYMBOL_GPL(pci_host_common_init);
int pci_host_common_probe(struct platform_device *pdev)
{
const struct pci_ecam_ops *ops;
+ struct pci_host_bridge *bridge;
ops = of_device_get_match_data(&pdev->dev);
if (!ops)
return -ENODEV;
- return pci_host_common_init(pdev, ops);
+ bridge = devm_pci_alloc_host_bridge(&pdev->dev, 0);
+ if (!bridge)
+ return -ENOMEM;
+
+ return pci_host_common_init(pdev, bridge, ops);
}
EXPORT_SYMBOL_GPL(pci_host_common_probe);
diff --git a/drivers/pci/controller/pci-host-common.h b/drivers/pci/controller/pci-host-common.h
index 51c35ec0cf37..b5075d4bd7eb 100644
--- a/drivers/pci/controller/pci-host-common.h
+++ b/drivers/pci/controller/pci-host-common.h
@@ -14,6 +14,7 @@ struct pci_ecam_ops;
int pci_host_common_probe(struct platform_device *pdev);
int pci_host_common_init(struct platform_device *pdev,
+ struct pci_host_bridge *bridge,
const struct pci_ecam_ops *ops);
void pci_host_common_remove(struct platform_device *pdev);
diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c
index 146b43981b27..1e237d3538f9 100644
--- a/drivers/pci/controller/pci-hyperv.c
+++ b/drivers/pci/controller/pci-hyperv.c
@@ -3696,48 +3696,6 @@ static int hv_send_resources_released(struct hv_device *hdev)
return 0;
}
-#define HVPCI_DOM_MAP_SIZE (64 * 1024)
-static DECLARE_BITMAP(hvpci_dom_map, HVPCI_DOM_MAP_SIZE);
-
-/*
- * PCI domain number 0 is used by emulated devices on Gen1 VMs, so define 0
- * as invalid for passthrough PCI devices of this driver.
- */
-#define HVPCI_DOM_INVALID 0
-
-/**
- * hv_get_dom_num() - Get a valid PCI domain number
- * Check if the PCI domain number is in use, and return another number if
- * it is in use.
- *
- * @dom: Requested domain number
- *
- * return: domain number on success, HVPCI_DOM_INVALID on failure
- */
-static u16 hv_get_dom_num(u16 dom)
-{
- unsigned int i;
-
- if (test_and_set_bit(dom, hvpci_dom_map) == 0)
- return dom;
-
- for_each_clear_bit(i, hvpci_dom_map, HVPCI_DOM_MAP_SIZE) {
- if (test_and_set_bit(i, hvpci_dom_map) == 0)
- return i;
- }
-
- return HVPCI_DOM_INVALID;
-}
-
-/**
- * hv_put_dom_num() - Mark the PCI domain number as free
- * @dom: Domain number to be freed
- */
-static void hv_put_dom_num(u16 dom)
-{
- clear_bit(dom, hvpci_dom_map);
-}
-
/**
* hv_pci_probe() - New VMBus channel probe, for a root PCI bus
* @hdev: VMBus's tracking struct for this root PCI bus
@@ -3750,9 +3708,9 @@ static int hv_pci_probe(struct hv_device *hdev,
{
struct pci_host_bridge *bridge;
struct hv_pcibus_device *hbus;
- u16 dom_req, dom;
+ int ret, dom;
+ u16 dom_req;
char *name;
- int ret;
bridge = devm_pci_alloc_host_bridge(&hdev->device, 0);
if (!bridge)
@@ -3779,11 +3737,14 @@ static int hv_pci_probe(struct hv_device *hdev,
* PCI bus (which is actually emulated by the hypervisor) is domain 0.
* (2) There will be no overlap between domains (after fixing possible
* collisions) in the same VM.
+ *
+ * Because Gen1 VMs use domain 0, don't allow picking domain 0 here,
+ * even if bytes 4 and 5 of the instance GUID are both zero. For wider
+ * userspace compatibility, limit the domain ID to a 16-bit value.
*/
dom_req = hdev->dev_instance.b[5] << 8 | hdev->dev_instance.b[4];
- dom = hv_get_dom_num(dom_req);
-
- if (dom == HVPCI_DOM_INVALID) {
+ dom = pci_bus_find_emul_domain_nr(dom_req, 1, U16_MAX);
+ if (dom < 0) {
dev_err(&hdev->device,
"Unable to use dom# 0x%x or other numbers", dom_req);
ret = -EINVAL;
@@ -3917,7 +3878,7 @@ close:
destroy_wq:
destroy_workqueue(hbus->wq);
free_dom:
- hv_put_dom_num(hbus->bridge->domain_nr);
+ pci_bus_release_emul_domain_nr(hbus->bridge->domain_nr);
free_bus:
kfree(hbus);
return ret;
@@ -4042,8 +4003,6 @@ static void hv_pci_remove(struct hv_device *hdev)
irq_domain_remove(hbus->irq_domain);
irq_domain_free_fwnode(hbus->fwnode);
- hv_put_dom_num(hbus->bridge->domain_nr);
-
kfree(hbus);
}
@@ -4217,9 +4176,6 @@ static int __init init_hv_pci_drv(void)
if (ret)
return ret;
- /* Set the invalid domain number's bit, so it will not be used */
- set_bit(HVPCI_DOM_INVALID, hvpci_dom_map);
-
/* Initialize PCI block r/w interface */
hvpci_block_ops.read_block = hv_read_config_block;
hvpci_block_ops.write_block = hv_write_config_block;
diff --git a/drivers/pci/controller/pci-ixp4xx.c b/drivers/pci/controller/pci-ixp4xx.c
index acb85e0d5675..9fd401838bad 100644
--- a/drivers/pci/controller/pci-ixp4xx.c
+++ b/drivers/pci/controller/pci-ixp4xx.c
@@ -214,6 +214,7 @@ static u32 ixp4xx_crp_byte_lane_enable_bits(u32 n, int size)
return 0xffffffff;
}
+#ifdef CONFIG_ARM
static int ixp4xx_crp_read_config(struct ixp4xx_pci *p, int where, int size,
u32 *value)
{
@@ -251,6 +252,7 @@ static int ixp4xx_crp_read_config(struct ixp4xx_pci *p, int where, int size,
return PCIBIOS_SUCCESSFUL;
}
+#endif
static int ixp4xx_crp_write_config(struct ixp4xx_pci *p, int where, int size,
u32 value)
@@ -470,6 +472,7 @@ static int ixp4xx_pci_parse_map_dma_ranges(struct ixp4xx_pci *p)
return 0;
}
+#ifdef CONFIG_ARM
/* Only used to get context for abort handling */
static struct ixp4xx_pci *ixp4xx_pci_abort_singleton;
@@ -509,6 +512,7 @@ static int ixp4xx_pci_abort_handler(unsigned long addr, unsigned int fsr,
return 0;
}
+#endif
static int __init ixp4xx_pci_probe(struct platform_device *pdev)
{
@@ -555,10 +559,12 @@ static int __init ixp4xx_pci_probe(struct platform_device *pdev)
dev_info(dev, "controller is in %s mode\n",
p->host_mode ? "host" : "option");
+#ifdef CONFIG_ARM
/* Hook in our fault handler for PCI errors */
ixp4xx_pci_abort_singleton = p;
hook_fault_code(16+6, ixp4xx_pci_abort_handler, SIGBUS, 0,
"imprecise external abort");
+#endif
ret = ixp4xx_pci_parse_map_ranges(p);
if (ret)
diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
index 0380d300adca..2d92fc79f6dd 100644
--- a/drivers/pci/controller/pcie-apple.c
+++ b/drivers/pci/controller/pcie-apple.c
@@ -187,7 +187,6 @@ struct apple_pcie {
const struct hw_info *hw;
unsigned long *bitmap;
struct list_head ports;
- struct list_head entry;
struct completion event;
struct irq_fwspec fwspec;
u32 nvecs;
@@ -206,9 +205,6 @@ struct apple_pcie_port {
int idx;
};
-static LIST_HEAD(pcie_list);
-static DEFINE_MUTEX(pcie_list_lock);
-
static void rmw_set(u32 set, void __iomem *addr)
{
writel_relaxed(readl_relaxed(addr) | set, addr);
@@ -724,32 +720,9 @@ static int apple_msi_init(struct apple_pcie *pcie)
return 0;
}
-static void apple_pcie_register(struct apple_pcie *pcie)
-{
- guard(mutex)(&pcie_list_lock);
-
- list_add_tail(&pcie->entry, &pcie_list);
-}
-
-static void apple_pcie_unregister(struct apple_pcie *pcie)
-{
- guard(mutex)(&pcie_list_lock);
-
- list_del(&pcie->entry);
-}
-
static struct apple_pcie *apple_pcie_lookup(struct device *dev)
{
- struct apple_pcie *pcie;
-
- guard(mutex)(&pcie_list_lock);
-
- list_for_each_entry(pcie, &pcie_list, entry) {
- if (pcie->dev == dev)
- return pcie;
- }
-
- return NULL;
+ return pci_host_bridge_priv(dev_get_drvdata(dev));
}
static struct apple_pcie_port *apple_pcie_get_port(struct pci_dev *pdev)
@@ -875,13 +848,15 @@ static const struct pci_ecam_ops apple_pcie_cfg_ecam_ops = {
static int apple_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct pci_host_bridge *bridge;
struct apple_pcie *pcie;
int ret;
- pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
- if (!pcie)
+ bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
+ if (!bridge)
return -ENOMEM;
+ pcie = pci_host_bridge_priv(bridge);
pcie->dev = dev;
pcie->hw = of_device_get_match_data(dev);
if (!pcie->hw)
@@ -897,13 +872,7 @@ static int apple_pcie_probe(struct platform_device *pdev)
if (ret)
return ret;
- apple_pcie_register(pcie);
-
- ret = pci_host_common_init(pdev, &apple_pcie_cfg_ecam_ops);
- if (ret)
- apple_pcie_unregister(pcie);
-
- return ret;
+ return pci_host_common_init(pdev, bridge, &apple_pcie_cfg_ecam_ops);
}
static const struct of_device_id apple_pcie_of_match[] = {
diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c
index 9afbd02ded35..062f55690012 100644
--- a/drivers/pci/controller/pcie-brcmstb.c
+++ b/drivers/pci/controller/pcie-brcmstb.c
@@ -14,15 +14,18 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-msi-lib.h>
#include <linux/irqdomain.h>
+#include <linux/kdebug.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/msi.h>
+#include <linux/notifier.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/of_platform.h>
+#include <linux/panic_notifier.h>
#include <linux/pci.h>
#include <linux/pci-ecam.h>
#include <linux/printk.h>
@@ -30,7 +33,9 @@
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/string.h>
+#include <linux/string_choices.h>
#include <linux/types.h>
#include "../pci.h"
@@ -48,7 +53,6 @@
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_MAX_LINK_WIDTH_MASK 0x1f0
-#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00
#define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8
#define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8
@@ -155,8 +159,40 @@
#define MSI_INT_MASK_SET 0x10
#define MSI_INT_MASK_CLR 0x14
+/* Error report registers */
+#define PCIE_OUTB_ERR_TREAT 0x6000
+#define PCIE_OUTB_ERR_TREAT_CONFIG 0x1
+#define PCIE_OUTB_ERR_TREAT_MEM 0x2
+#define PCIE_OUTB_ERR_VALID 0x6004
+#define PCIE_OUTB_ERR_CLEAR 0x6008
+#define PCIE_OUTB_ERR_ACC_INFO 0x600c
+#define PCIE_OUTB_ERR_ACC_INFO_CFG_ERR BIT(0)
+#define PCIE_OUTB_ERR_ACC_INFO_MEM_ERR BIT(1)
+#define PCIE_OUTB_ERR_ACC_INFO_TYPE_64 BIT(2)
+#define PCIE_OUTB_ERR_ACC_INFO_DIR_WRITE BIT(4)
+#define PCIE_OUTB_ERR_ACC_INFO_BYTE_LANES 0xff00
+#define PCIE_OUTB_ERR_ACC_ADDR 0x6010
+#define PCIE_OUTB_ERR_ACC_ADDR_BUS 0xff00000
+#define PCIE_OUTB_ERR_ACC_ADDR_DEV 0xf8000
+#define PCIE_OUTB_ERR_ACC_ADDR_FUNC 0x7000
+#define PCIE_OUTB_ERR_ACC_ADDR_REG 0xfff
+#define PCIE_OUTB_ERR_CFG_CAUSE 0x6014
+#define PCIE_OUTB_ERR_CFG_CAUSE_TIMEOUT BIT(6)
+#define PCIE_OUTB_ERR_CFG_CAUSE_ABORT BIT(5)
+#define PCIE_OUTB_ERR_CFG_CAUSE_UNSUPP_REQ BIT(4)
+#define PCIE_OUTB_ERR_CFG_CAUSE_ACC_TIMEOUT BIT(2)
+#define PCIE_OUTB_ERR_CFG_CAUSE_ACC_DISABLED BIT(1)
+#define PCIE_OUTB_ERR_CFG_CAUSE_ACC_64BIT BIT(0)
+#define PCIE_OUTB_ERR_MEM_ADDR_LO 0x6018
+#define PCIE_OUTB_ERR_MEM_ADDR_HI 0x601c
+#define PCIE_OUTB_ERR_MEM_CAUSE 0x6020
+#define PCIE_OUTB_ERR_MEM_CAUSE_TIMEOUT BIT(6)
+#define PCIE_OUTB_ERR_MEM_CAUSE_ABORT BIT(5)
+#define PCIE_OUTB_ERR_MEM_CAUSE_UNSUPP_REQ BIT(4)
+#define PCIE_OUTB_ERR_MEM_CAUSE_ACC_DISABLED BIT(1)
+#define PCIE_OUTB_ERR_MEM_CAUSE_BAD_ADDR BIT(0)
+
#define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1
-#define PCIE_RGR1_SW_INIT_1_PERST_SHIFT 0x0
#define RGR1_SW_INIT_1_INIT_GENERIC_MASK 0x2
#define RGR1_SW_INIT_1_INIT_GENERIC_SHIFT 0x1
@@ -259,6 +295,7 @@ struct pcie_cfg_data {
int (*perst_set)(struct brcm_pcie *pcie, u32 val);
int (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
int (*post_setup)(struct brcm_pcie *pcie);
+ bool has_err_report;
};
struct subdev_regulators {
@@ -303,6 +340,10 @@ struct brcm_pcie {
struct subdev_regulators *sr;
bool ep_wakeup_capable;
const struct pcie_cfg_data *cfg;
+ bool bridge_in_reset;
+ struct notifier_block die_notifier;
+ struct notifier_block panic_notifier;
+ spinlock_t bridge_lock;
};
static inline bool is_bmips(const struct brcm_pcie *pcie)
@@ -310,6 +351,24 @@ static inline bool is_bmips(const struct brcm_pcie *pcie)
return pcie->cfg->soc_base == BCM7435 || pcie->cfg->soc_base == BCM7425;
}
+static int brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
+{
+ unsigned long flags;
+ int ret;
+
+ if (pcie->cfg->has_err_report)
+ spin_lock_irqsave(&pcie->bridge_lock, flags);
+
+ ret = pcie->cfg->bridge_sw_init_set(pcie, val);
+ /* If we fail, assume the bridge is in reset (off) */
+ pcie->bridge_in_reset = ret ? true : val;
+
+ if (pcie->cfg->has_err_report)
+ spin_unlock_irqrestore(&pcie->bridge_lock, flags);
+
+ return ret;
+}
+
/*
* This is to convert the size of the inbound "BAR" region to the
* non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE
@@ -1075,13 +1134,13 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
void __iomem *base = pcie->base;
struct pci_host_bridge *bridge;
struct resource_entry *entry;
- u32 tmp, burst, aspm_support, num_lanes, num_lanes_cap;
+ u32 tmp, burst, num_lanes, num_lanes_cap;
u8 num_out_wins = 0;
int num_inbound_wins = 0;
int memc, ret;
/* Reset the bridge */
- ret = pcie->cfg->bridge_sw_init_set(pcie, 1);
+ ret = brcm_pcie_bridge_sw_init_set(pcie, 1);
if (ret)
return ret;
@@ -1097,7 +1156,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
usleep_range(100, 200);
/* Take the bridge out of reset */
- ret = pcie->cfg->bridge_sw_init_set(pcie, 0);
+ ret = brcm_pcie_bridge_sw_init_set(pcie, 0);
if (ret)
return ret;
@@ -1175,12 +1234,9 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
/* Don't advertise L0s capability if 'aspm-no-l0s' */
- aspm_support = PCIE_LINK_STATE_L1;
- if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
- aspm_support |= PCIE_LINK_STATE_L0S;
tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
- u32p_replace_bits(&tmp, aspm_support,
- PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
+ if (of_property_read_bool(pcie->np, "aspm-no-l0s"))
+ tmp &= ~PCI_EXP_LNKCAP_ASPM_L0S;
writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
/* 'tmp' still holds the contents of PRIV1_LINK_CAPABILITY */
@@ -1565,7 +1621,7 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie)
if (!(pcie->cfg->quirks & CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN))
/* Shutdown PCIe bridge */
- ret = pcie->cfg->bridge_sw_init_set(pcie, 1);
+ ret = brcm_pcie_bridge_sw_init_set(pcie, 1);
return ret;
}
@@ -1653,7 +1709,9 @@ static int brcm_pcie_resume_noirq(struct device *dev)
goto err_reset;
/* Take bridge out of reset so we can access the SERDES reg */
- pcie->cfg->bridge_sw_init_set(pcie, 0);
+ ret = brcm_pcie_bridge_sw_init_set(pcie, 0);
+ if (ret)
+ goto err_reset;
/* SERDES_IDDQ = 0 */
tmp = readl(base + HARD_DEBUG(pcie));
@@ -1707,6 +1765,119 @@ err_disable_clk:
return ret;
}
+/* Dump out PCIe errors on die or panic */
+static int brcm_pcie_dump_err(struct brcm_pcie *pcie,
+ const char *type)
+{
+ void __iomem *base = pcie->base;
+ int i, is_cfg_err, is_mem_err, lanes;
+ const char *width_str, *direction_str;
+ u32 info, cfg_addr, cfg_cause, mem_cause, lo, hi;
+ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
+ unsigned long flags;
+ char lanes_str[9];
+
+ spin_lock_irqsave(&pcie->bridge_lock, flags);
+ /* Don't access registers when the bridge is off */
+ if (pcie->bridge_in_reset || readl(base + PCIE_OUTB_ERR_VALID) == 0) {
+ spin_unlock_irqrestore(&pcie->bridge_lock, flags);
+ return NOTIFY_DONE;
+ }
+
+ /* Read all necessary registers so we can release the spinlock ASAP */
+ info = readl(base + PCIE_OUTB_ERR_ACC_INFO);
+ is_cfg_err = !!(info & PCIE_OUTB_ERR_ACC_INFO_CFG_ERR);
+ is_mem_err = !!(info & PCIE_OUTB_ERR_ACC_INFO_MEM_ERR);
+ if (is_cfg_err) {
+ cfg_addr = readl(base + PCIE_OUTB_ERR_ACC_ADDR);
+ cfg_cause = readl(base + PCIE_OUTB_ERR_CFG_CAUSE);
+ }
+ if (is_mem_err) {
+ mem_cause = readl(base + PCIE_OUTB_ERR_MEM_CAUSE);
+ lo = readl(base + PCIE_OUTB_ERR_MEM_ADDR_LO);
+ hi = readl(base + PCIE_OUTB_ERR_MEM_ADDR_HI);
+ }
+ /* We've got all of the info, clear the error */
+ writel(1, base + PCIE_OUTB_ERR_CLEAR);
+ spin_unlock_irqrestore(&pcie->bridge_lock, flags);
+
+ dev_err(pcie->dev, "reporting PCIe info which may be related to %s error\n",
+ type);
+ width_str = (info & PCIE_OUTB_ERR_ACC_INFO_TYPE_64) ? "64bit" : "32bit";
+ direction_str = str_read_write(!(info & PCIE_OUTB_ERR_ACC_INFO_DIR_WRITE));
+ lanes = FIELD_GET(PCIE_OUTB_ERR_ACC_INFO_BYTE_LANES, info);
+ for (i = 0, lanes_str[8] = 0; i < 8; i++)
+ lanes_str[i] = (lanes & (1 << i)) ? '1' : '0';
+
+ if (is_cfg_err) {
+ int bus = FIELD_GET(PCIE_OUTB_ERR_ACC_ADDR_BUS, cfg_addr);
+ int dev = FIELD_GET(PCIE_OUTB_ERR_ACC_ADDR_DEV, cfg_addr);
+ int func = FIELD_GET(PCIE_OUTB_ERR_ACC_ADDR_FUNC, cfg_addr);
+ int reg = FIELD_GET(PCIE_OUTB_ERR_ACC_ADDR_REG, cfg_addr);
+
+ dev_err(pcie->dev, "Error: CFG Acc, %s, %s (%04x:%02x:%02x.%d) reg=0x%x, lanes=%s\n",
+ width_str, direction_str, bridge->domain_nr, bus, dev,
+ func, reg, lanes_str);
+ dev_err(pcie->dev, " Type: TO=%d Abt=%d UnsupReq=%d AccTO=%d AccDsbld=%d Acc64bit=%d\n",
+ !!(cfg_cause & PCIE_OUTB_ERR_CFG_CAUSE_TIMEOUT),
+ !!(cfg_cause & PCIE_OUTB_ERR_CFG_CAUSE_ABORT),
+ !!(cfg_cause & PCIE_OUTB_ERR_CFG_CAUSE_UNSUPP_REQ),
+ !!(cfg_cause & PCIE_OUTB_ERR_CFG_CAUSE_ACC_TIMEOUT),
+ !!(cfg_cause & PCIE_OUTB_ERR_CFG_CAUSE_ACC_DISABLED),
+ !!(cfg_cause & PCIE_OUTB_ERR_CFG_CAUSE_ACC_64BIT));
+ }
+
+ if (is_mem_err) {
+ u64 addr = ((u64)hi << 32) | (u64)lo;
+
+ dev_err(pcie->dev, "Error: Mem Acc, %s, %s, @0x%llx, lanes=%s\n",
+ width_str, direction_str, addr, lanes_str);
+ dev_err(pcie->dev, " Type: TO=%d Abt=%d UnsupReq=%d AccDsble=%d BadAddr=%d\n",
+ !!(mem_cause & PCIE_OUTB_ERR_MEM_CAUSE_TIMEOUT),
+ !!(mem_cause & PCIE_OUTB_ERR_MEM_CAUSE_ABORT),
+ !!(mem_cause & PCIE_OUTB_ERR_MEM_CAUSE_UNSUPP_REQ),
+ !!(mem_cause & PCIE_OUTB_ERR_MEM_CAUSE_ACC_DISABLED),
+ !!(mem_cause & PCIE_OUTB_ERR_MEM_CAUSE_BAD_ADDR));
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int brcm_pcie_die_notify_cb(struct notifier_block *self,
+ unsigned long v, void *p)
+{
+ struct brcm_pcie *pcie =
+ container_of(self, struct brcm_pcie, die_notifier);
+
+ return brcm_pcie_dump_err(pcie, "Die");
+}
+
+static int brcm_pcie_panic_notify_cb(struct notifier_block *self,
+ unsigned long v, void *p)
+{
+ struct brcm_pcie *pcie =
+ container_of(self, struct brcm_pcie, panic_notifier);
+
+ return brcm_pcie_dump_err(pcie, "Panic");
+}
+
+static void brcm_register_die_notifiers(struct brcm_pcie *pcie)
+{
+ pcie->panic_notifier.notifier_call = brcm_pcie_panic_notify_cb;
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &pcie->panic_notifier);
+
+ pcie->die_notifier.notifier_call = brcm_pcie_die_notify_cb;
+ register_die_notifier(&pcie->die_notifier);
+}
+
+static void brcm_unregister_die_notifiers(struct brcm_pcie *pcie)
+{
+ unregister_die_notifier(&pcie->die_notifier);
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &pcie->panic_notifier);
+}
+
static void __brcm_pcie_remove(struct brcm_pcie *pcie)
{
brcm_msi_remove(pcie);
@@ -1725,6 +1896,9 @@ static void brcm_pcie_remove(struct platform_device *pdev)
pci_stop_root_bus(bridge->bus);
pci_remove_root_bus(bridge->bus);
+ if (pcie->cfg->has_err_report)
+ brcm_unregister_die_notifiers(pcie);
+
__brcm_pcie_remove(pcie);
}
@@ -1825,6 +1999,7 @@ static const struct pcie_cfg_data bcm7216_cfg = {
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278,
.has_phy = true,
.num_inbound_wins = 3,
+ .has_err_report = true,
};
static const struct pcie_cfg_data bcm7712_cfg = {
@@ -1921,7 +2096,10 @@ static int brcm_pcie_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(&pdev->dev, ret, "could not enable clock\n");
- pcie->cfg->bridge_sw_init_set(pcie, 0);
+ ret = brcm_pcie_bridge_sw_init_set(pcie, 0);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "could not de-assert bridge reset\n");
if (pcie->swinit_reset) {
ret = reset_control_assert(pcie->swinit_reset);
@@ -1996,6 +2174,11 @@ static int brcm_pcie_probe(struct platform_device *pdev)
return ret;
}
+ if (pcie->cfg->has_err_report) {
+ spin_lock_init(&pcie->bridge_lock);
+ brcm_register_die_notifiers(pcie);
+ }
+
return 0;
fail:
diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c
index 22134e95574b..ccf71993ea35 100644
--- a/drivers/pci/controller/pcie-iproc.c
+++ b/drivers/pci/controller/pcie-iproc.c
@@ -17,6 +17,7 @@
#include <linux/irqchip/arm-gic-v3.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
+#include <linux/of_irq.h>
#include <linux/of_pci.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
@@ -1337,29 +1338,16 @@ static int iproc_pcie_msi_steer(struct iproc_pcie *pcie,
static int iproc_pcie_msi_enable(struct iproc_pcie *pcie)
{
- struct device_node *msi_node;
+ struct device_node *msi_node = NULL;
int ret;
/*
* Either the "msi-parent" or the "msi-map" phandle needs to exist
* for us to obtain the MSI node.
*/
-
- msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0);
- if (!msi_node) {
- const __be32 *msi_map = NULL;
- int len;
- u32 phandle;
-
- msi_map = of_get_property(pcie->dev->of_node, "msi-map", &len);
- if (!msi_map)
- return -ENODEV;
-
- phandle = be32_to_cpup(msi_map + 1);
- msi_node = of_find_node_by_phandle(phandle);
- if (!msi_node)
- return -ENODEV;
- }
+ of_msi_xlate(pcie->dev, &msi_node, 0);
+ if (!msi_node)
+ return -ENODEV;
/*
* Certain revisions of the iProc PCIe controller require additional
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 24cc30a2ab6c..4b78b6528f9f 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -143,23 +143,33 @@
struct mtk_pcie_port;
/**
+ * enum mtk_pcie_quirks - MTK PCIe quirks
+ * @MTK_PCIE_FIX_CLASS_ID: host's class ID needed to be fixed
+ * @MTK_PCIE_FIX_DEVICE_ID: host's device ID needed to be fixed
+ * @MTK_PCIE_NO_MSI: Bridge has no MSI support, and relies on an external block
+ * @MTK_PCIE_SKIP_RSTB: Skip calling RSTB bits on PCIe probe
+ */
+enum mtk_pcie_quirks {
+ MTK_PCIE_FIX_CLASS_ID = BIT(0),
+ MTK_PCIE_FIX_DEVICE_ID = BIT(1),
+ MTK_PCIE_NO_MSI = BIT(2),
+ MTK_PCIE_SKIP_RSTB = BIT(3),
+};
+
+/**
* struct mtk_pcie_soc - differentiate between host generations
- * @need_fix_class_id: whether this host's class ID needed to be fixed or not
- * @need_fix_device_id: whether this host's device ID needed to be fixed or not
- * @no_msi: Bridge has no MSI support, and relies on an external block
* @device_id: device ID which this host need to be fixed
* @ops: pointer to configuration access functions
* @startup: pointer to controller setting functions
* @setup_irq: pointer to initialize IRQ functions
+ * @quirks: PCIe device quirks.
*/
struct mtk_pcie_soc {
- bool need_fix_class_id;
- bool need_fix_device_id;
- bool no_msi;
unsigned int device_id;
struct pci_ops *ops;
int (*startup)(struct mtk_pcie_port *port);
int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node);
+ enum mtk_pcie_quirks quirks;
};
/**
@@ -679,31 +689,28 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
regmap_update_bits(pcie->cfg, PCIE_SYS_CFG_V2, val, val);
}
- /* Assert all reset signals */
- writel(0, port->base + PCIE_RST_CTRL);
+ if (!(soc->quirks & MTK_PCIE_SKIP_RSTB)) {
+ /* Assert all reset signals */
+ writel(0, port->base + PCIE_RST_CTRL);
- /*
- * Enable PCIe link down reset, if link status changed from link up to
- * link down, this will reset MAC control registers and configuration
- * space.
- */
- writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
+ /*
+ * Enable PCIe link down reset, if link status changed from
+ * link up to link down, this will reset MAC control registers
+ * and configuration space.
+ */
+ writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
- /*
- * Described in PCIe CEM specification sections 2.2 (PERST# Signal) and
- * 2.2.1 (Initial Power-Up (G3 to S0)). The deassertion of PERST# should
- * be delayed 100ms (TPVPERL) for the power and clock to become stable.
- */
- msleep(100);
+ msleep(PCIE_T_PVPERL_MS);
- /* De-assert PHY, PE, PIPE, MAC and configuration reset */
- val = readl(port->base + PCIE_RST_CTRL);
- val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
- PCIE_MAC_SRSTB | PCIE_CRSTB;
- writel(val, port->base + PCIE_RST_CTRL);
+ /* De-assert PHY, PE, PIPE, MAC and configuration reset */
+ val = readl(port->base + PCIE_RST_CTRL);
+ val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
+ PCIE_MAC_SRSTB | PCIE_CRSTB;
+ writel(val, port->base + PCIE_RST_CTRL);
+ }
/* Set up vendor ID and class code */
- if (soc->need_fix_class_id) {
+ if (soc->quirks & MTK_PCIE_FIX_CLASS_ID) {
val = PCI_VENDOR_ID_MEDIATEK;
writew(val, port->base + PCIE_CONF_VEND_ID);
@@ -711,7 +718,7 @@ static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
writew(val, port->base + PCIE_CONF_CLASS_ID);
}
- if (soc->need_fix_device_id)
+ if (soc->quirks & MTK_PCIE_FIX_DEVICE_ID)
writew(soc->device_id, port->base + PCIE_CONF_DEVICE_ID);
/* 100ms timeout value should be enough for Gen1/2 training */
@@ -821,6 +828,41 @@ static int mtk_pcie_startup_port(struct mtk_pcie_port *port)
return 0;
}
+static int mtk_pcie_startup_port_an7583(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ struct device *dev = pcie->dev;
+ struct pci_host_bridge *host;
+ struct resource_entry *entry;
+ struct regmap *pbus_regmap;
+ resource_size_t addr;
+ u32 args[2], size;
+
+ /*
+ * Configure PBus base address and base address mask to allow
+ * the hw to detect if a given address is accessible on PCIe
+ * controller.
+ */
+ pbus_regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "mediatek,pbus-csr",
+ ARRAY_SIZE(args),
+ args);
+ if (IS_ERR(pbus_regmap))
+ return PTR_ERR(pbus_regmap);
+
+ host = pci_host_bridge_from_priv(pcie);
+ entry = resource_list_first_type(&host->windows, IORESOURCE_MEM);
+ if (!entry)
+ return -ENODEV;
+
+ addr = entry->res->start - entry->offset;
+ regmap_write(pbus_regmap, args[0], lower_32_bits(addr));
+ size = lower_32_bits(resource_size(entry->res));
+ regmap_write(pbus_regmap, args[1], GENMASK(31, __fls(size)));
+
+ return mtk_pcie_startup_port_v2(port);
+}
+
static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
{
struct mtk_pcie *pcie = port->pcie;
@@ -1099,7 +1141,7 @@ static int mtk_pcie_probe(struct platform_device *pdev)
host->ops = pcie->soc->ops;
host->sysdata = pcie;
- host->msi_domain = pcie->soc->no_msi;
+ host->msi_domain = !!(pcie->soc->quirks & MTK_PCIE_NO_MSI);
err = pci_host_probe(host);
if (err)
@@ -1187,9 +1229,9 @@ static const struct dev_pm_ops mtk_pcie_pm_ops = {
};
static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
- .no_msi = true,
.ops = &mtk_pcie_ops,
.startup = mtk_pcie_startup_port,
+ .quirks = MTK_PCIE_NO_MSI,
};
static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = {
@@ -1199,22 +1241,29 @@ static const struct mtk_pcie_soc mtk_pcie_soc_mt2712 = {
};
static const struct mtk_pcie_soc mtk_pcie_soc_mt7622 = {
- .need_fix_class_id = true,
.ops = &mtk_pcie_ops_v2,
.startup = mtk_pcie_startup_port_v2,
.setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_FIX_CLASS_ID,
+};
+
+static const struct mtk_pcie_soc mtk_pcie_soc_an7583 = {
+ .ops = &mtk_pcie_ops_v2,
+ .startup = mtk_pcie_startup_port_an7583,
+ .setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_SKIP_RSTB,
};
static const struct mtk_pcie_soc mtk_pcie_soc_mt7629 = {
- .need_fix_class_id = true,
- .need_fix_device_id = true,
.device_id = PCI_DEVICE_ID_MEDIATEK_7629,
.ops = &mtk_pcie_ops_v2,
.startup = mtk_pcie_startup_port_v2,
.setup_irq = mtk_pcie_setup_irq,
+ .quirks = MTK_PCIE_FIX_CLASS_ID | MTK_PCIE_FIX_DEVICE_ID,
};
static const struct of_device_id mtk_pcie_ids[] = {
+ { .compatible = "airoha,an7583-pcie", .data = &mtk_pcie_soc_an7583 },
{ .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_mt2712 },
diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
new file mode 100644
index 000000000000..667e6d629474
--- /dev/null
+++ b/drivers/pci/controller/pcie-rzg3s-host.c
@@ -0,0 +1,1761 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe driver for Renesas RZ/G3S SoCs
+ *
+ * Copyright (C) 2025 Renesas Electronics Corp.
+ *
+ * Based on:
+ * drivers/pci/controller/pcie-rcar-host.c
+ * Copyright (C) 2009 - 2011 Paul Mundt
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/irq-msi-lib.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
+#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+
+#include "../pci.h"
+
+/* AXI registers */
+#define RZG3S_PCI_REQDATA(id) (0x80 + (id) * 0x4)
+#define RZG3S_PCI_REQRCVDAT 0x8c
+
+#define RZG3S_PCI_REQADR1 0x90
+#define RZG3S_PCI_REQADR1_BUS GENMASK(31, 24)
+#define RZG3S_PCI_REQADR1_DEV GENMASK(23, 19)
+#define RZG3S_PCI_REQADR1_FUNC GENMASK(18, 16)
+#define RZG3S_PCI_REQADR1_REG GENMASK(11, 0)
+
+#define RZG3S_PCI_REQBE 0x98
+#define RZG3S_PCI_REQBE_BYTE_EN GENMASK(3, 0)
+
+#define RZG3S_PCI_REQISS 0x9c
+#define RZG3S_PCI_REQISS_MOR_STATUS GENMASK(18, 16)
+#define RZG3S_PCI_REQISS_TR_TYPE GENMASK(11, 8)
+#define RZG3S_PCI_REQISS_TR_TP0_RD FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x4)
+#define RZG3S_PCI_REQISS_TR_TP0_WR FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x5)
+#define RZG3S_PCI_REQISS_TR_TP1_RD FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x6)
+#define RZG3S_PCI_REQISS_TR_TP1_WR FIELD_PREP(RZG3S_PCI_REQISS_TR_TYPE, 0x7)
+#define RZG3S_PCI_REQISS_REQ_ISSUE BIT(0)
+
+#define RZG3S_PCI_MSIRCVWADRL 0x100
+#define RZG3S_PCI_MSIRCVWADRL_MASK GENMASK(31, 3)
+#define RZG3S_PCI_MSIRCVWADRL_MSG_DATA_ENA BIT(1)
+#define RZG3S_PCI_MSIRCVWADRL_ENA BIT(0)
+
+#define RZG3S_PCI_MSIRCVWADRU 0x104
+
+#define RZG3S_PCI_MSIRCVWMSKL 0x108
+#define RZG3S_PCI_MSIRCVWMSKL_MASK GENMASK(31, 2)
+
+#define RZG3S_PCI_PINTRCVIE 0x110
+#define RZG3S_PCI_PINTRCVIE_INTX(i) BIT(i)
+#define RZG3S_PCI_PINTRCVIE_MSI BIT(4)
+
+#define RZG3S_PCI_PINTRCVIS 0x114
+#define RZG3S_PCI_PINTRCVIS_INTX(i) BIT(i)
+#define RZG3S_PCI_PINTRCVIS_MSI BIT(4)
+
+#define RZG3S_PCI_MSGRCVIE 0x120
+#define RZG3S_PCI_MSGRCVIE_MSG_RCV BIT(24)
+
+#define RZG3S_PCI_MSGRCVIS 0x124
+#define RZG3S_PCI_MSGRCVIS_MRI BIT(24)
+
+#define RZG3S_PCI_PEIE0 0x200
+
+#define RZG3S_PCI_PEIS0 0x204
+#define RZG3S_PCI_PEIS0_RX_DLLP_PM_ENTER BIT(12)
+#define RZG3S_PCI_PEIS0_DL_UPDOWN BIT(9)
+
+#define RZG3S_PCI_PEIE1 0x208
+#define RZG3S_PCI_PEIS1 0x20c
+#define RZG3S_PCI_AMEIS 0x214
+#define RZG3S_PCI_ASEIS1 0x224
+
+#define RZG3S_PCI_PCSTAT1 0x408
+#define RZG3S_PCI_PCSTAT1_LTSSM_STATE GENMASK(14, 10)
+#define RZG3S_PCI_PCSTAT1_DL_DOWN_STS BIT(0)
+
+#define RZG3S_PCI_PCCTRL2 0x410
+#define RZG3S_PCI_PCCTRL2_LS_CHG GENMASK(9, 8)
+#define RZG3S_PCI_PCCTRL2_LS_CHG_REQ BIT(0)
+
+#define RZG3S_PCI_PCSTAT2 0x414
+#define RZG3S_PCI_PCSTAT2_LS_CHG_DONE BIT(28)
+#define RZG3S_PCI_PCSTAT2_SDRIRE GENMASK(7, 1)
+
+#define RZG3S_PCI_PERM 0x300
+#define RZG3S_PCI_PERM_CFG_HWINIT_EN BIT(2)
+#define RZG3S_PCI_PERM_PIPE_PHY_REG_EN BIT(1)
+
+#define RZG3S_PCI_MSIRE(id) (0x600 + (id) * 0x10)
+#define RZG3S_PCI_MSIRE_ENA BIT(0)
+
+#define RZG3S_PCI_MSIRM(id) (0x608 + (id) * 0x10)
+#define RZG3S_PCI_MSIRS(id) (0x60c + (id) * 0x10)
+
+#define RZG3S_PCI_AWBASEL(id) (0x1000 + (id) * 0x20)
+#define RZG3S_PCI_AWBASEL_WIN_ENA BIT(0)
+
+#define RZG3S_PCI_AWBASEU(id) (0x1004 + (id) * 0x20)
+#define RZG3S_PCI_AWMASKL(id) (0x1008 + (id) * 0x20)
+#define RZG3S_PCI_AWMASKU(id) (0x100c + (id) * 0x20)
+#define RZG3S_PCI_ADESTL(id) (0x1010 + (id) * 0x20)
+#define RZG3S_PCI_ADESTU(id) (0x1014 + (id) * 0x20)
+
+#define RZG3S_PCI_PWBASEL(id) (0x1100 + (id) * 0x20)
+#define RZG3S_PCI_PWBASEL_ENA BIT(0)
+
+#define RZG3S_PCI_PWBASEU(id) (0x1104 + (id) * 0x20)
+#define RZG3S_PCI_PDESTL(id) (0x1110 + (id) * 0x20)
+#define RZG3S_PCI_PDESTU(id) (0x1114 + (id) * 0x20)
+#define RZG3S_PCI_PWMASKL(id) (0x1108 + (id) * 0x20)
+#define RZG3S_PCI_PWMASKU(id) (0x110c + (id) * 0x20)
+
+/* PHY control registers */
+#define RZG3S_PCI_PHY_XCFGD(id) (0x2000 + (id) * 0x10)
+#define RZG3S_PCI_PHY_XCFGD_NUM 39
+
+#define RZG3S_PCI_PHY_XCFGA_CMN(id) (0x2400 + (id) * 0x10)
+#define RZG3S_PCI_PHY_XCFGA_CMN_NUM 16
+
+#define RZG3S_PCI_PHY_XCFGA_RX(id) (0x2500 + (id) * 0x10)
+#define RZG3S_PCI_PHY_XCFGA_RX_NUM 13
+
+#define RZG3S_PCI_PHY_XCFGA_TX 0x25d0
+
+#define RZG3S_PCI_PHY_XCFG_CTRL 0x2a20
+#define RZG3S_PCI_PHY_XCFG_CTRL_PHYREG_SEL BIT(0)
+
+/* PCIe registers */
+#define RZG3S_PCI_CFG_BASE 0x6000
+#define RZG3S_PCI_CFG_BARMSK00L 0xa0
+#define RZG3S_PCI_CFG_BARMSK00U 0xa4
+
+#define RZG3S_PCI_CFG_PCIEC 0x60
+
+/* System controller registers */
+#define RZG3S_SYS_PCIE_RST_RSM_B 0xd74
+#define RZG3S_SYS_PCIE_RST_RSM_B_MASK BIT(0)
+
+/* Maximum number of windows */
+#define RZG3S_MAX_WINDOWS 8
+
+/* Number of MSI interrupts per register */
+#define RZG3S_PCI_MSI_INT_PER_REG 32
+/* The number of MSI interrupts */
+#define RZG3S_PCI_MSI_INT_NR RZG3S_PCI_MSI_INT_PER_REG
+
+/* Timeouts experimentally determined */
+#define RZG3S_REQ_ISSUE_TIMEOUT_US 2500
+
+/**
+ * struct rzg3s_pcie_msi - RZ/G3S PCIe MSI data structure
+ * @domain: IRQ domain
+ * @map: bitmap with the allocated MSIs
+ * @dma_addr: address of the allocated MSI window
+ * @window_base: base address of the MSI window
+ * @pages: allocated pages for MSI window mapping
+ * @map_lock: lock for bitmap with the allocated MSIs
+ * @irq: MSI interrupt
+ */
+struct rzg3s_pcie_msi {
+ struct irq_domain *domain;
+ DECLARE_BITMAP(map, RZG3S_PCI_MSI_INT_NR);
+ dma_addr_t dma_addr;
+ dma_addr_t window_base;
+ unsigned long pages;
+ struct mutex map_lock;
+ int irq;
+};
+
+struct rzg3s_pcie_host;
+
+/**
+ * struct rzg3s_pcie_soc_data - SoC specific data
+ * @init_phy: PHY initialization function
+ * @power_resets: array with the resets that need to be de-asserted after
+ * power-on
+ * @cfg_resets: array with the resets that need to be de-asserted after
+ * configuration
+ * @num_power_resets: number of power resets
+ * @num_cfg_resets: number of configuration resets
+ */
+struct rzg3s_pcie_soc_data {
+ int (*init_phy)(struct rzg3s_pcie_host *host);
+ const char * const *power_resets;
+ const char * const *cfg_resets;
+ u8 num_power_resets;
+ u8 num_cfg_resets;
+};
+
+/**
+ * struct rzg3s_pcie_port - RZ/G3S PCIe Root Port data structure
+ * @refclk: PCIe reference clock
+ * @vendor_id: Vendor ID
+ * @device_id: Device ID
+ */
+struct rzg3s_pcie_port {
+ struct clk *refclk;
+ u32 vendor_id;
+ u32 device_id;
+};
+
+/**
+ * struct rzg3s_pcie_host - RZ/G3S PCIe data structure
+ * @axi: base address for AXI registers
+ * @pcie: base address for PCIe registers
+ * @dev: struct device
+ * @power_resets: reset control signals that should be set after power up
+ * @cfg_resets: reset control signals that should be set after configuration
+ * @sysc: SYSC regmap
+ * @intx_domain: INTx IRQ domain
+ * @data: SoC specific data
+ * @msi: MSI data structure
+ * @port: PCIe Root Port
+ * @hw_lock: lock for access to the HW resources
+ * @intx_irqs: INTx interrupts
+ * @max_link_speed: maximum supported link speed
+ */
+struct rzg3s_pcie_host {
+ void __iomem *axi;
+ void __iomem *pcie;
+ struct device *dev;
+ struct reset_control_bulk_data *power_resets;
+ struct reset_control_bulk_data *cfg_resets;
+ struct regmap *sysc;
+ struct irq_domain *intx_domain;
+ const struct rzg3s_pcie_soc_data *data;
+ struct rzg3s_pcie_msi msi;
+ struct rzg3s_pcie_port port;
+ raw_spinlock_t hw_lock;
+ int intx_irqs[PCI_NUM_INTX];
+ int max_link_speed;
+};
+
+#define rzg3s_msi_to_host(_msi) container_of(_msi, struct rzg3s_pcie_host, msi)
+
+static void rzg3s_pcie_update_bits(void __iomem *base, u32 offset, u32 mask,
+ u32 val)
+{
+ u32 tmp;
+
+ tmp = readl_relaxed(base + offset);
+ tmp &= ~mask;
+ tmp |= val & mask;
+ writel_relaxed(tmp, base + offset);
+}
+
+static int rzg3s_pcie_child_issue_request(struct rzg3s_pcie_host *host)
+{
+ u32 val;
+ int ret;
+
+ rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_REQISS,
+ RZG3S_PCI_REQISS_REQ_ISSUE,
+ RZG3S_PCI_REQISS_REQ_ISSUE);
+ ret = readl_poll_timeout_atomic(host->axi + RZG3S_PCI_REQISS, val,
+ !(val & RZG3S_PCI_REQISS_REQ_ISSUE),
+ 5, RZG3S_REQ_ISSUE_TIMEOUT_US);
+
+ if (val & RZG3S_PCI_REQISS_MOR_STATUS)
+ return -EIO;
+
+ return ret;
+}
+
+static void rzg3s_pcie_child_prepare_bus(struct pci_bus *bus,
+ unsigned int devfn, int where)
+{
+ struct rzg3s_pcie_host *host = bus->sysdata;
+ unsigned int dev, func, reg;
+
+ dev = PCI_SLOT(devfn);
+ func = PCI_FUNC(devfn);
+ reg = where & ~0x3;
+
+ /* Set the destination */
+ writel_relaxed(FIELD_PREP(RZG3S_PCI_REQADR1_BUS, bus->number) |
+ FIELD_PREP(RZG3S_PCI_REQADR1_DEV, dev) |
+ FIELD_PREP(RZG3S_PCI_REQADR1_FUNC, func) |
+ FIELD_PREP(RZG3S_PCI_REQADR1_REG, reg),
+ host->axi + RZG3S_PCI_REQADR1);
+
+ /* Set byte enable */
+ writel_relaxed(RZG3S_PCI_REQBE_BYTE_EN, host->axi + RZG3S_PCI_REQBE);
+}
+
+static int rzg3s_pcie_child_read_conf(struct rzg3s_pcie_host *host,
+ struct pci_bus *bus, unsigned int devfn,
+ int where, u32 *data)
+{
+ bool type0 = pci_is_root_bus(bus->parent) ? true : false;
+ int ret;
+
+ rzg3s_pcie_child_prepare_bus(bus, devfn, where);
+
+ /* Set the type of request */
+ writel_relaxed(type0 ? RZG3S_PCI_REQISS_TR_TP0_RD :
+ RZG3S_PCI_REQISS_TR_TP1_RD,
+ host->axi + RZG3S_PCI_REQISS);
+
+ /* Issue the request and wait to finish */
+ ret = rzg3s_pcie_child_issue_request(host);